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