1 /* $OpenBSD: sdmmc_cis.c,v 1.7 2016/04/23 14:15:59 kettenis Exp $ */ 2 3 /* 4 * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 /* Routines to decode the Card Information Structure of SD I/O cards */ 20 21 #include <sys/param.h> 22 #include <sys/device.h> 23 #include <sys/systm.h> 24 25 #include <dev/sdmmc/sdmmc_ioreg.h> 26 #include <dev/sdmmc/sdmmcdevs.h> 27 #include <dev/sdmmc/sdmmcvar.h> 28 29 u_int32_t sdmmc_cisptr(struct sdmmc_function *); 30 31 #ifdef SDMMC_DEBUG 32 #define DPRINTF(s) printf s 33 #else 34 #define DPRINTF(s) /**/ 35 #endif 36 37 u_int32_t 38 sdmmc_cisptr(struct sdmmc_function *sf) 39 { 40 struct sdmmc_function *sf0 = sf->sc->sc_fn0; 41 u_int32_t cisptr = 0; 42 int reg; 43 44 rw_assert_wrlock(&sf->sc->sc_lock); 45 46 reg = SD_IO_CCCR_CISPTR + (sf->number * SD_IO_CCCR_SIZE); 47 cisptr |= sdmmc_io_read_1(sf0, reg + 0) << 0; 48 cisptr |= sdmmc_io_read_1(sf0, reg + 1) << 8; 49 cisptr |= sdmmc_io_read_1(sf0, reg + 2) << 16; 50 51 return cisptr; 52 } 53 54 int 55 sdmmc_read_cis(struct sdmmc_function *sf, struct sdmmc_cis *cis) 56 { 57 struct sdmmc_function *sf0 = sf->sc->sc_fn0; 58 int reg; 59 u_int8_t tplcode; 60 u_int8_t tpllen; 61 62 rw_assert_wrlock(&sf->sc->sc_lock); 63 64 reg = (int)sdmmc_cisptr(sf); 65 if (reg < SD_IO_CIS_START || 66 reg >= (SD_IO_CIS_START+SD_IO_CIS_SIZE-16)) { 67 printf("%s: bad CIS ptr %#x\n", DEVNAME(sf->sc), reg); 68 return 1; 69 } 70 71 for (;;) { 72 tplcode = sdmmc_io_read_1(sf0, reg++); 73 if (tplcode == SD_IO_CISTPL_END) 74 break; 75 if (tplcode == SD_IO_CISTPL_NULL) 76 continue; 77 78 tpllen = sdmmc_io_read_1(sf0, reg++); 79 if (tpllen == 0) { 80 printf("%s: CIS parse error at %d, " 81 "tuple code %#x, length %d\n", 82 DEVNAME(sf->sc), reg, tplcode, tpllen); 83 break; 84 } 85 86 switch (tplcode) { 87 case SD_IO_CISTPL_FUNCID: 88 if (tpllen < 2) { 89 printf("%s: bad CISTPL_FUNCID length\n", 90 DEVNAME(sf->sc)); 91 reg += tpllen; 92 break; 93 } 94 cis->function = sdmmc_io_read_1(sf0, reg); 95 reg += tpllen; 96 break; 97 case SD_IO_CISTPL_MANFID: 98 if (tpllen < 4) { 99 printf("%s: bad CISTPL_MANFID length\n", 100 DEVNAME(sf->sc)); 101 reg += tpllen; 102 break; 103 } 104 cis->manufacturer = sdmmc_io_read_1(sf0, reg++); 105 cis->manufacturer |= sdmmc_io_read_1(sf0, reg++) << 8; 106 cis->product = sdmmc_io_read_1(sf0, reg++); 107 cis->product |= sdmmc_io_read_1(sf0, reg++) << 8; 108 break; 109 case SD_IO_CISTPL_VERS_1: 110 if (tpllen < 2) { 111 printf("%s: CISTPL_VERS_1 too short\n", 112 DEVNAME(sf->sc)); 113 reg += tpllen; 114 break; 115 } 116 { 117 int start, i, ch, count; 118 119 cis->cis1_major = sdmmc_io_read_1(sf0, reg++); 120 cis->cis1_minor = sdmmc_io_read_1(sf0, reg++); 121 122 for (count = 0, start = 0, i = 0; 123 (count < 4) && ((i + 4) < 256); i++) { 124 ch = sdmmc_io_read_1(sf0, reg + i); 125 if (ch == 0xff) 126 break; 127 cis->cis1_info_buf[i] = ch; 128 if (ch == 0) { 129 cis->cis1_info[count] = 130 cis->cis1_info_buf + start; 131 start = i + 1; 132 count++; 133 } 134 } 135 136 reg += tpllen - 2; 137 } 138 break; 139 default: 140 DPRINTF(("%s: unknown tuple code %#x, length %d\n", 141 DEVNAME(sf->sc), tplcode, tpllen)); 142 reg += tpllen; 143 break; 144 } 145 } 146 147 return 0; 148 } 149 150 void 151 sdmmc_print_cis(struct sdmmc_function *sf) 152 { 153 struct sdmmc_cis *cis = &sf->cis; 154 int i; 155 156 printf("%s: CIS version %d.%d\n", DEVNAME(sf->sc), 157 cis->cis1_major, cis->cis1_minor); 158 159 printf("%s: CIS info: ", DEVNAME(sf->sc)); 160 for (i = 0; i < 4; i++) { 161 if (cis->cis1_info[i] == NULL) 162 break; 163 if (i) 164 printf(", "); 165 printf("%s", cis->cis1_info[i]); 166 } 167 printf("\n"); 168 169 printf("%s: Manufacturer code 0x%x, product 0x%x\n", 170 DEVNAME(sf->sc), cis->manufacturer, cis->product); 171 172 printf("%s: function %d: ", DEVNAME(sf->sc), sf->number); 173 switch (sf->cis.function) { 174 case TPLFID_FUNCTION_SDIO: 175 printf("SDIO"); 176 break; 177 default: 178 printf("unknown (%d)", sf->cis.function); 179 break; 180 } 181 printf("\n"); 182 } 183 184 void 185 sdmmc_check_cis_quirks(struct sdmmc_function *sf) 186 { 187 if (sf->cis.manufacturer == SDMMC_VENDOR_SPECTEC && 188 sf->cis.product == SDMMC_PRODUCT_SPECTEC_SDW820) { 189 /* This card lacks the VERS_1 tuple. */ 190 sf->cis.cis1_major = 0x01; 191 sf->cis.cis1_minor = 0x00; 192 sf->cis.cis1_info[0] = "Spectec"; 193 sf->cis.cis1_info[1] = "SDIO WLAN Card"; 194 sf->cis.cis1_info[2] = "SDW-820"; 195 sf->cis.cis1_info[3] = ""; 196 } 197 } 198