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