xref: /openbsd-src/sys/dev/sdmmc/sdmmc_cis.c (revision c90a81c56dcebd6a1b73fe4aff9b03385b8e63b3)
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