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