Home | History | Annotate | Line # | Download | only in sdmmc
sdmmc_cis.c revision 1.9
      1 /*	$NetBSD: sdmmc_cis.c,v 1.9 2024/10/13 18:21:20 jmcneill Exp $	*/
      2 /*	$OpenBSD: sdmmc_cis.c,v 1.1 2006/06/01 21:53:41 uwe Exp $	*/
      3 
      4 /*
      5  * Copyright (c) 2006 Uwe Stuehler <uwe (at) openbsd.org>
      6  *
      7  * Permission to use, copy, modify, and distribute this software for any
      8  * purpose with or without fee is hereby granted, provided that the above
      9  * copyright notice and this permission notice appear in all copies.
     10  *
     11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     18  */
     19 
     20 /* Routines to decode the Card Information Structure of SD I/O cards */
     21 
     22 #include <sys/cdefs.h>
     23 __KERNEL_RCSID(0, "$NetBSD: sdmmc_cis.c,v 1.9 2024/10/13 18:21:20 jmcneill Exp $");
     24 
     25 #ifdef _KERNEL_OPT
     26 #include "opt_sdmmc.h"
     27 #endif
     28 
     29 #include <sys/param.h>
     30 #include <sys/systm.h>
     31 
     32 #include <dev/sdmmc/sdmmc_ioreg.h>
     33 #include <dev/sdmmc/sdmmcdevs.h>
     34 #include <dev/sdmmc/sdmmcvar.h>
     35 
     36 #include <dev/pcmcia/pcmciareg.h>
     37 
     38 #ifdef SDMMCCISDEBUG
     39 #define DPRINTF(s)	printf s
     40 #else
     41 #define DPRINTF(s)	/**/
     42 #endif
     43 
     44 static uint32_t sdmmc_cisptr(struct sdmmc_function *);
     45 static void decode_funce_common(struct sdmmc_function *, struct sdmmc_cis *,
     46 				int, uint32_t);
     47 static void decode_funce_function(struct sdmmc_function *, struct sdmmc_cis *,
     48 				  int, uint32_t);
     49 static void decode_vers_1(struct sdmmc_function *, struct sdmmc_cis *, int,
     50 			  uint32_t);
     51 
     52 static uint32_t
     53 sdmmc_cisptr(struct sdmmc_function *sf)
     54 {
     55 	uint32_t cisptr = 0;
     56 
     57 	/* CIS pointer stored in little-endian format. */
     58 	if (sf->number == 0) {
     59 		cisptr |= sdmmc_io_read_1(sf, SD_IO_CCCR_CISPTR + 0) << 0;
     60 		cisptr |= sdmmc_io_read_1(sf, SD_IO_CCCR_CISPTR + 1) << 8;
     61 		cisptr |= sdmmc_io_read_1(sf, SD_IO_CCCR_CISPTR + 2) << 16;
     62 	} else {
     63 		struct sdmmc_function *sf0 = sf->sc->sc_fn0;
     64 		int num = sf->number;
     65 
     66 		cisptr |= sdmmc_io_read_1(sf0, SD_IO_FBR(num) + 9) << 0;
     67 		cisptr |= sdmmc_io_read_1(sf0, SD_IO_FBR(num) + 10) << 8;
     68 		cisptr |= sdmmc_io_read_1(sf0, SD_IO_FBR(num) + 11) << 16;
     69 	}
     70 	return cisptr;
     71 }
     72 
     73 static void
     74 decode_funce_common(struct sdmmc_function *sf, struct sdmmc_cis *cis,
     75 		    int tpllen, uint32_t reg)
     76 {
     77 	static const int speed_val[] =
     78 	    { 0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80 };
     79 	static const int speed_unit[] = { 10, 100, 1000, 10000, };
     80 	struct sdmmc_function *sf0 = sf->sc->sc_fn0;
     81 	device_t dev = sf->sc->sc_dev;
     82 	int fn0_blk_size, max_tran_speed;
     83 
     84 	if (sf->number != 0) {
     85 		aprint_error_dev(dev,
     86 		    "CISTPL_FUNCE(common) found in function\n");
     87 		return;
     88 	}
     89 	if (tpllen < 4) {
     90 		aprint_error_dev(dev, "CISTPL_FUNCE(common) too short\n");
     91 		return;
     92 	}
     93 
     94 	fn0_blk_size = sdmmc_io_read_1(sf0, reg++);
     95 	fn0_blk_size |= sdmmc_io_read_1(sf0, reg++) << 8;
     96 	max_tran_speed = sdmmc_io_read_1(sf0, reg++);
     97 	sf->csd.tran_speed =
     98 	    speed_val[max_tran_speed >> 3] * speed_unit[max_tran_speed & 7];
     99 
    100 	DPRINTF(
    101 	    ("CISTPL_FUNCE: FN0_BLK_SIZE=0x%x, MAX_TRAN_SPEED=0x%x(%dkHz)\n",
    102 	    fn0_blk_size, max_tran_speed, sf->csd.tran_speed));
    103 }
    104 
    105 static void
    106 decode_funce_lan_nid(struct sdmmc_function *sf, struct sdmmc_cis *cis,
    107 		     int tpllen, uint32_t reg)
    108 {
    109 	struct sdmmc_function *sf0 = sf->sc->sc_fn0;
    110 	device_t dev = sf->sc->sc_dev;
    111 	uint8_t mac[6] __unused;
    112 	int i;
    113 
    114 	if (tpllen != 8) {
    115 		aprint_error_dev(dev,
    116 		    "CISTPL_FUNCE(lan_nid) too short\n");
    117 		return;
    118 	}
    119 
    120 	for (i = 0; i < 6; i++) {
    121 		mac[i] = sdmmc_io_read_1(sf0, reg++);
    122 	}
    123 
    124 	DPRINTF(
    125 	    ("CISTPL_FUNCE: LAN_NID=%02x:%02x:%02x:%02x:%02x:%02x\n",
    126 	    mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]));
    127 }
    128 
    129 static void
    130 decode_funce_function(struct sdmmc_function *sf, struct sdmmc_cis *cis,
    131 		      int tpllen, uint32_t reg)
    132 {
    133 	struct sdmmc_function *sf0 = sf->sc->sc_fn0;
    134 	device_t dev = sf->sc->sc_dev;
    135 	int sdiox_cccrx, sdiox, max_blk_size;
    136 
    137 	sdiox_cccrx = sdmmc_io_read_1(sf0, SD_IO_CCCR_CCCR_SDIO_REV);
    138 	sdiox = SD_IO_CCCR_SDIO_REV(sdiox_cccrx);
    139 
    140 	if (sf->number == 0) {
    141 		aprint_error_dev(dev,
    142 		    "CISTPL_FUNCE(function) found in common\n");
    143 		return;
    144 	}
    145 	if (sdiox == CCCR_SDIO_REV_1_00 && tpllen < 0x1c) {
    146 		aprint_error_dev(dev,
    147 		    "CISTPL_FUNCE(function) too short (v1.00)\n");
    148 		return;
    149 	} else if (sdiox != CCCR_SDIO_REV_1_00 && tpllen < 0x2a) {
    150 		aprint_error_dev(dev, "CISTPL_FUNCE(function) too short\n");
    151 		return;
    152 	}
    153 
    154 	max_blk_size = sdmmc_io_read_1(sf0, reg + 11);
    155 	max_blk_size |= sdmmc_io_read_1(sf0, reg + 12) << 8;
    156 
    157 	DPRINTF(("CISTPL_FUNCE: MAX_BLK_SIZE=0x%x\n", max_blk_size));
    158 }
    159 
    160 static void
    161 decode_vers_1(struct sdmmc_function *sf, struct sdmmc_cis *cis, int tpllen,
    162 	      uint32_t reg)
    163 {
    164 	struct sdmmc_function *sf0 = sf->sc->sc_fn0;
    165 	device_t dev = sf->sc->sc_dev;
    166 	int start, ch, count, i;
    167 
    168 	if (tpllen < 2) {
    169 		aprint_error_dev(dev, "CISTPL_VERS_1 too short\n");
    170 		return;
    171 	}
    172 
    173 	cis->cis1_major = sdmmc_io_read_1(sf0, reg++);
    174 	cis->cis1_minor = sdmmc_io_read_1(sf0, reg++);
    175 
    176 	for (count = 0, start = 0, i = 0; (count < 4) && ((i + 4) < 256); i++) {
    177 		ch = sdmmc_io_read_1(sf0, reg + i);
    178 		if (ch == 0xff)
    179 			break;
    180 		cis->cis1_info_buf[i] = ch;
    181 		if (ch == 0) {
    182 			cis->cis1_info[count] = cis->cis1_info_buf + start;
    183 			start = i + 1;
    184 			count++;
    185 		}
    186 	}
    187 
    188 	DPRINTF(("CISTPL_VERS_1\n"));
    189 }
    190 
    191 int
    192 sdmmc_read_cis(struct sdmmc_function *sf, struct sdmmc_cis *cis)
    193 {
    194 	struct sdmmc_function *sf0 = sf->sc->sc_fn0;
    195 	device_t dev = sf->sc->sc_dev;
    196 	uint32_t reg;
    197 	uint8_t tplcode, tpllen;
    198 
    199 	memset(cis, 0, sizeof *cis);
    200 
    201 	reg = sdmmc_cisptr(sf);
    202 	if (reg < SD_IO_CIS_START ||
    203 	    reg >= (SD_IO_CIS_START + SD_IO_CIS_SIZE - 16)) {
    204 		aprint_error_dev(dev, "bad CIS ptr %#x\n", reg);
    205 		return 1;
    206 	}
    207 
    208 	for (;;) {
    209 		tplcode = sdmmc_io_read_1(sf0, reg++);
    210 
    211 		if (tplcode == PCMCIA_CISTPL_NULL) {
    212 			DPRINTF((" 00\nCISTPL_NONE\n"));
    213 			continue;
    214 		}
    215 
    216 		tpllen = sdmmc_io_read_1(sf0, reg++);
    217 		if (tplcode == PCMCIA_CISTPL_END || tpllen == 0) {
    218 			if (tplcode != 0xff)
    219 				aprint_error_dev(dev, "CIS parse error at %d, "
    220 				    "tuple code %#x, length %d\n",
    221 				    reg, tplcode, tpllen);
    222 			else {
    223 				DPRINTF((" ff\nCISTPL_END\n"));
    224 			}
    225 			break;
    226 		}
    227 
    228 #ifdef SDMMCCISDEBUG
    229 		{
    230 			int i;
    231 
    232 			/* print the tuple */
    233 			DPRINTF((" %02x %02x", tplcode, tpllen));
    234 
    235 			for (i = 0; i < tpllen; i++) {
    236 				DPRINTF((" %02x",
    237 				    sdmmc_io_read_1(sf0, reg + i)));
    238 				if ((i % 16) == 13)
    239 					DPRINTF(("\n"));
    240 			}
    241 			if ((i % 16) != 14)
    242 				DPRINTF(("\n"));
    243 		}
    244 #endif
    245 
    246 		switch (tplcode) {
    247 		case PCMCIA_CISTPL_FUNCE:
    248 			switch (sdmmc_io_read_1(sf0, reg++)) {
    249 			case 0:
    250 				decode_funce_common(sf, cis, tpllen, reg);
    251 				break;
    252 			case PCMCIA_TPLFE_TYPE_LAN_NID:
    253 				decode_funce_lan_nid(sf, cis, tpllen, reg);
    254 				break;
    255 			default:
    256 				decode_funce_function(sf, cis, tpllen, reg);
    257 			}
    258 			reg += (tpllen - 1);
    259 			break;
    260 
    261 		case PCMCIA_CISTPL_FUNCID:
    262 			if (tpllen < 2) {
    263 				aprint_error_dev(dev,
    264 				    "bad CISTPL_FUNCID length\n");
    265 				reg += tpllen;
    266 				break;
    267 			}
    268 			cis->function = sdmmc_io_read_1(sf0, reg);
    269 			DPRINTF(("CISTPL_FUNCID\n"));
    270 			reg += tpllen;
    271 			break;
    272 
    273 		case PCMCIA_CISTPL_MANFID:
    274 			if (tpllen < 4) {
    275 				aprint_error_dev(dev,
    276 				    "bad CISTPL_MANFID length\n");
    277 				reg += tpllen;
    278 				break;
    279 			}
    280 			cis->manufacturer = sdmmc_io_read_1(sf0, reg++);
    281 			cis->manufacturer |= sdmmc_io_read_1(sf0, reg++) << 8;
    282 			cis->product = sdmmc_io_read_1(sf0, reg++);
    283 			cis->product |= sdmmc_io_read_1(sf0, reg++) << 8;
    284 			DPRINTF(("CISTPL_MANFID\n"));
    285 			break;
    286 
    287 		case PCMCIA_CISTPL_VERS_1:
    288 			decode_vers_1(sf, cis, tpllen, reg);
    289 			reg += tpllen;
    290 			break;
    291 
    292 		case PCMCIA_CISTPL_SDIO:
    293 			aprint_normal_dev(dev, "SDIO function\n");
    294 			reg += tpllen;
    295 			break;
    296 
    297 		default:
    298 			/*
    299 			 * Tuple codes between 80h-8Fh are vendor unique.
    300 			 * Print a warning about all other codes.
    301 			 */
    302 			if ((tplcode & 0xf0) != 0x80)
    303 				aprint_error_dev(dev,
    304 				    "unknown tuple code %#x, length %d\n",
    305 				    tplcode, tpllen);
    306 			reg += tpllen;
    307 			break;
    308 		}
    309 	}
    310 
    311 	return 0;
    312 }
    313 
    314 void
    315 sdmmc_print_cis(struct sdmmc_function *sf)
    316 {
    317 	device_t dev = sf->sc->sc_dev;
    318 	struct sdmmc_cis *cis = &sf->cis;
    319 	int i;
    320 
    321 	printf("%s: CIS version %u.%u\n", device_xname(dev), cis->cis1_major,
    322 	    cis->cis1_minor);
    323 
    324 	printf("%s: CIS info: ", device_xname(dev));
    325 	for (i = 0; i < 4; i++) {
    326 		if (cis->cis1_info[i] == NULL)
    327 			break;
    328 		if (i != 0)
    329 			aprint_verbose(", ");
    330 		printf("%s", cis->cis1_info[i]);
    331 	}
    332 	printf("\n");
    333 
    334 	printf("%s: Manufacturer code 0x%x, product 0x%x\n", device_xname(dev),
    335 	    cis->manufacturer, cis->product);
    336 
    337 	printf("%s: function %d: ", device_xname(dev), sf->number);
    338 	printf("\n");
    339 }
    340 
    341 void
    342 sdmmc_check_cis_quirks(struct sdmmc_function *sf)
    343 {
    344 	char *p;
    345 	int i;
    346 
    347 	if (sf->cis.manufacturer == SDMMC_VENDOR_SPECTEC &&
    348 	    sf->cis.product == SDMMC_PRODUCT_SPECTEC_SDW820) {
    349 		/* This card lacks the VERS_1 tuple. */
    350 		static const char cis1_info[] =
    351 		    "Spectec\0SDIO WLAN Card\0SDW-820\0\0";
    352 
    353 		sf->cis.cis1_major = 0x01;
    354 		sf->cis.cis1_minor = 0x00;
    355 
    356 		p = sf->cis.cis1_info_buf;
    357 		strlcpy(p, cis1_info, sizeof(sf->cis.cis1_info_buf));
    358 		for (i = 0; i < 4; i++) {
    359 			sf->cis.cis1_info[i] = p;
    360 			p += strlen(p) + 1;
    361 		}
    362 	}
    363 }
    364