1 /* $OpenBSD: sdmmc_cis.c,v 1.8 2020/04/29 09:44:49 patrick 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
sdmmc_cisptr(struct sdmmc_function * sf)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
sdmmc_read_cis(struct sdmmc_function * sf,struct sdmmc_cis * cis)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
80 switch (tplcode) {
81 case SD_IO_CISTPL_FUNCID:
82 if (tpllen < 2) {
83 printf("%s: bad CISTPL_FUNCID length\n",
84 DEVNAME(sf->sc));
85 reg += tpllen;
86 break;
87 }
88 cis->function = sdmmc_io_read_1(sf0, reg);
89 reg += tpllen;
90 break;
91 case SD_IO_CISTPL_MANFID:
92 if (tpllen < 4) {
93 printf("%s: bad CISTPL_MANFID length\n",
94 DEVNAME(sf->sc));
95 reg += tpllen;
96 break;
97 }
98 cis->manufacturer = sdmmc_io_read_1(sf0, reg++);
99 cis->manufacturer |= sdmmc_io_read_1(sf0, reg++) << 8;
100 cis->product = sdmmc_io_read_1(sf0, reg++);
101 cis->product |= sdmmc_io_read_1(sf0, reg++) << 8;
102 break;
103 case SD_IO_CISTPL_VERS_1:
104 if (tpllen < 2) {
105 printf("%s: CISTPL_VERS_1 too short\n",
106 DEVNAME(sf->sc));
107 reg += tpllen;
108 break;
109 }
110 {
111 int start, i, ch, count;
112
113 cis->cis1_major = sdmmc_io_read_1(sf0, reg++);
114 cis->cis1_minor = sdmmc_io_read_1(sf0, reg++);
115
116 for (count = 0, start = 0, i = 0;
117 (count < 4) && ((i + 4) < 256); i++) {
118 ch = sdmmc_io_read_1(sf0, reg + i);
119 if (ch == 0xff)
120 break;
121 cis->cis1_info_buf[i] = ch;
122 if (ch == 0) {
123 cis->cis1_info[count] =
124 cis->cis1_info_buf + start;
125 start = i + 1;
126 count++;
127 }
128 }
129
130 reg += tpllen - 2;
131 }
132 break;
133 default:
134 DPRINTF(("%s: unknown tuple code %#x, length %d\n",
135 DEVNAME(sf->sc), tplcode, tpllen));
136 reg += tpllen;
137 break;
138 }
139 }
140
141 return 0;
142 }
143
144 void
sdmmc_print_cis(struct sdmmc_function * sf)145 sdmmc_print_cis(struct sdmmc_function *sf)
146 {
147 struct sdmmc_cis *cis = &sf->cis;
148 int i;
149
150 printf("%s: CIS version %d.%d\n", DEVNAME(sf->sc),
151 cis->cis1_major, cis->cis1_minor);
152
153 printf("%s: CIS info: ", DEVNAME(sf->sc));
154 for (i = 0; i < 4; i++) {
155 if (cis->cis1_info[i] == NULL)
156 break;
157 if (i)
158 printf(", ");
159 printf("%s", cis->cis1_info[i]);
160 }
161 printf("\n");
162
163 printf("%s: Manufacturer code 0x%x, product 0x%x\n",
164 DEVNAME(sf->sc), cis->manufacturer, cis->product);
165
166 printf("%s: function %d: ", DEVNAME(sf->sc), sf->number);
167 switch (sf->cis.function) {
168 case TPLFID_FUNCTION_SDIO:
169 printf("SDIO");
170 break;
171 default:
172 printf("unknown (%d)", sf->cis.function);
173 break;
174 }
175 printf("\n");
176 }
177
178 void
sdmmc_check_cis_quirks(struct sdmmc_function * sf)179 sdmmc_check_cis_quirks(struct sdmmc_function *sf)
180 {
181 if (sf->cis.manufacturer == SDMMC_VENDOR_SPECTEC &&
182 sf->cis.product == SDMMC_PRODUCT_SPECTEC_SDW820) {
183 /* This card lacks the VERS_1 tuple. */
184 sf->cis.cis1_major = 0x01;
185 sf->cis.cis1_minor = 0x00;
186 sf->cis.cis1_info[0] = "Spectec";
187 sf->cis.cis1_info[1] = "SDIO WLAN Card";
188 sf->cis.cis1_info[2] = "SDW-820";
189 sf->cis.cis1_info[3] = "";
190 }
191 }
192