1 /* $NetBSD: sdmmc_cis.c,v 1.8 2019/10/28 06:31:39 mlelstv 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@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.8 2019/10/28 06:31:39 mlelstv 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_function(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 int sdiox_cccrx, sdiox, max_blk_size; 112 113 sdiox_cccrx = sdmmc_io_read_1(sf0, SD_IO_CCCR_CCCR_SDIO_REV); 114 sdiox = SD_IO_CCCR_SDIO_REV(sdiox_cccrx); 115 116 if (sf->number == 0) { 117 aprint_error_dev(dev, 118 "CISTPL_FUNCE(function) found in common\n"); 119 return; 120 } 121 if (sdiox == CCCR_SDIO_REV_1_00 && tpllen < 0x1c) { 122 aprint_error_dev(dev, 123 "CISTPL_FUNCE(function) too short (v1.00)\n"); 124 return; 125 } else if (sdiox != CCCR_SDIO_REV_1_00 && tpllen < 0x2a) { 126 aprint_error_dev(dev, "CISTPL_FUNCE(function) too short\n"); 127 return; 128 } 129 130 max_blk_size = sdmmc_io_read_1(sf0, reg + 11); 131 max_blk_size |= sdmmc_io_read_1(sf0, reg + 12) << 8; 132 133 DPRINTF(("CISTPL_FUNCE: MAX_BLK_SIZE=0x%x\n", max_blk_size)); 134 } 135 136 static void 137 decode_vers_1(struct sdmmc_function *sf, struct sdmmc_cis *cis, int tpllen, 138 uint32_t reg) 139 { 140 struct sdmmc_function *sf0 = sf->sc->sc_fn0; 141 device_t dev = sf->sc->sc_dev; 142 int start, ch, count, i; 143 144 if (tpllen < 2) { 145 aprint_error_dev(dev, "CISTPL_VERS_1 too short\n"); 146 return; 147 } 148 149 cis->cis1_major = sdmmc_io_read_1(sf0, reg++); 150 cis->cis1_minor = sdmmc_io_read_1(sf0, reg++); 151 152 for (count = 0, start = 0, i = 0; (count < 4) && ((i + 4) < 256); i++) { 153 ch = sdmmc_io_read_1(sf0, reg + i); 154 if (ch == 0xff) 155 break; 156 cis->cis1_info_buf[i] = ch; 157 if (ch == 0) { 158 cis->cis1_info[count] = cis->cis1_info_buf + start; 159 start = i + 1; 160 count++; 161 } 162 } 163 164 DPRINTF(("CISTPL_VERS_1\n")); 165 } 166 167 int 168 sdmmc_read_cis(struct sdmmc_function *sf, struct sdmmc_cis *cis) 169 { 170 struct sdmmc_function *sf0 = sf->sc->sc_fn0; 171 device_t dev = sf->sc->sc_dev; 172 uint32_t reg; 173 uint8_t tplcode, tpllen; 174 175 memset(cis, 0, sizeof *cis); 176 177 reg = sdmmc_cisptr(sf); 178 if (reg < SD_IO_CIS_START || 179 reg >= (SD_IO_CIS_START + SD_IO_CIS_SIZE - 16)) { 180 aprint_error_dev(dev, "bad CIS ptr %#x\n", reg); 181 return 1; 182 } 183 184 for (;;) { 185 tplcode = sdmmc_io_read_1(sf0, reg++); 186 187 if (tplcode == PCMCIA_CISTPL_NULL) { 188 DPRINTF((" 00\nCISTPL_NONE\n")); 189 continue; 190 } 191 192 tpllen = sdmmc_io_read_1(sf0, reg++); 193 if (tplcode == PCMCIA_CISTPL_END || tpllen == 0) { 194 if (tplcode != 0xff) 195 aprint_error_dev(dev, "CIS parse error at %d, " 196 "tuple code %#x, length %d\n", 197 reg, tplcode, tpllen); 198 else { 199 DPRINTF((" ff\nCISTPL_END\n")); 200 } 201 break; 202 } 203 204 #ifdef SDMMCCISDEBUG 205 { 206 int i; 207 208 /* print the tuple */ 209 DPRINTF((" %02x %02x", tplcode, tpllen)); 210 211 for (i = 0; i < tpllen; i++) { 212 DPRINTF((" %02x", 213 sdmmc_io_read_1(sf0, reg + i))); 214 if ((i % 16) == 13) 215 DPRINTF(("\n")); 216 } 217 if ((i % 16) != 14) 218 DPRINTF(("\n")); 219 } 220 #endif 221 222 switch (tplcode) { 223 case PCMCIA_CISTPL_FUNCE: 224 if (sdmmc_io_read_1(sf0, reg++) == 0) 225 decode_funce_common(sf, cis, tpllen, reg); 226 else 227 decode_funce_function(sf, cis, tpllen, reg); 228 reg += (tpllen - 1); 229 break; 230 231 case PCMCIA_CISTPL_FUNCID: 232 if (tpllen < 2) { 233 aprint_error_dev(dev, 234 "bad CISTPL_FUNCID length\n"); 235 reg += tpllen; 236 break; 237 } 238 cis->function = sdmmc_io_read_1(sf0, reg); 239 DPRINTF(("CISTPL_FUNCID\n")); 240 reg += tpllen; 241 break; 242 243 case PCMCIA_CISTPL_MANFID: 244 if (tpllen < 4) { 245 aprint_error_dev(dev, 246 "bad CISTPL_MANFID length\n"); 247 reg += tpllen; 248 break; 249 } 250 cis->manufacturer = sdmmc_io_read_1(sf0, reg++); 251 cis->manufacturer |= sdmmc_io_read_1(sf0, reg++) << 8; 252 cis->product = sdmmc_io_read_1(sf0, reg++); 253 cis->product |= sdmmc_io_read_1(sf0, reg++) << 8; 254 DPRINTF(("CISTPL_MANFID\n")); 255 break; 256 257 case PCMCIA_CISTPL_VERS_1: 258 decode_vers_1(sf, cis, tpllen, reg); 259 reg += tpllen; 260 break; 261 262 case PCMCIA_CISTPL_SDIO: 263 aprint_normal_dev(dev, "SDIO function\n"); 264 reg += tpllen; 265 break; 266 267 default: 268 /* 269 * Tuple codes between 80h-8Fh are vendor unique. 270 * Print a warning about all other codes. 271 */ 272 if ((tplcode & 0xf0) != 0x80) 273 aprint_error_dev(dev, 274 "unknown tuple code %#x, length %d\n", 275 tplcode, tpllen); 276 reg += tpllen; 277 break; 278 } 279 } 280 281 return 0; 282 } 283 284 void 285 sdmmc_print_cis(struct sdmmc_function *sf) 286 { 287 device_t dev = sf->sc->sc_dev; 288 struct sdmmc_cis *cis = &sf->cis; 289 int i; 290 291 printf("%s: CIS version %u.%u\n", device_xname(dev), cis->cis1_major, 292 cis->cis1_minor); 293 294 printf("%s: CIS info: ", device_xname(dev)); 295 for (i = 0; i < 4; i++) { 296 if (cis->cis1_info[i] == NULL) 297 break; 298 if (i != 0) 299 aprint_verbose(", "); 300 printf("%s", cis->cis1_info[i]); 301 } 302 printf("\n"); 303 304 printf("%s: Manufacturer code 0x%x, product 0x%x\n", device_xname(dev), 305 cis->manufacturer, cis->product); 306 307 printf("%s: function %d: ", device_xname(dev), sf->number); 308 printf("\n"); 309 } 310 311 void 312 sdmmc_check_cis_quirks(struct sdmmc_function *sf) 313 { 314 char *p; 315 int i; 316 317 if (sf->cis.manufacturer == SDMMC_VENDOR_SPECTEC && 318 sf->cis.product == SDMMC_PRODUCT_SPECTEC_SDW820) { 319 /* This card lacks the VERS_1 tuple. */ 320 static const char cis1_info[] = 321 "Spectec\0SDIO WLAN Card\0SDW-820\0\0"; 322 323 sf->cis.cis1_major = 0x01; 324 sf->cis.cis1_minor = 0x00; 325 326 p = sf->cis.cis1_info_buf; 327 strlcpy(p, cis1_info, sizeof(sf->cis.cis1_info_buf)); 328 for (i = 0; i < 4; i++) { 329 sf->cis.cis1_info[i] = p; 330 p += strlen(p) + 1; 331 } 332 } 333 } 334