xref: /netbsd-src/sys/dev/sdmmc/if_bwi_sdio.c (revision c2ec63e8e78bb6624e6ff097647c672261afdf87)
1*c2ec63e8Sjmcneill /* $NetBSD: if_bwi_sdio.c,v 1.1 2025/01/19 00:29:29 jmcneill Exp $ */
2*c2ec63e8Sjmcneill 
3*c2ec63e8Sjmcneill /*-
4*c2ec63e8Sjmcneill  * Copyright (c) 2025 Jared McNeill <jmcneill@invisible.ca>
5*c2ec63e8Sjmcneill  * All rights reserved.
6*c2ec63e8Sjmcneill  *
7*c2ec63e8Sjmcneill  * Redistribution and use in source and binary forms, with or without
8*c2ec63e8Sjmcneill  * modification, are permitted provided that the following conditions
9*c2ec63e8Sjmcneill  * are met:
10*c2ec63e8Sjmcneill  * 1. Redistributions of source code must retain the above copyright
11*c2ec63e8Sjmcneill  *    notice, this list of conditions and the following disclaimer.
12*c2ec63e8Sjmcneill  * 2. Redistributions in binary form must reproduce the above copyright
13*c2ec63e8Sjmcneill  *    notice, this list of conditions and the following disclaimer in the
14*c2ec63e8Sjmcneill  *    documentation and/or other materials provided with the distribution.
15*c2ec63e8Sjmcneill  *
16*c2ec63e8Sjmcneill  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17*c2ec63e8Sjmcneill  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18*c2ec63e8Sjmcneill  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19*c2ec63e8Sjmcneill  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20*c2ec63e8Sjmcneill  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21*c2ec63e8Sjmcneill  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22*c2ec63e8Sjmcneill  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23*c2ec63e8Sjmcneill  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24*c2ec63e8Sjmcneill  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25*c2ec63e8Sjmcneill  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26*c2ec63e8Sjmcneill  * POSSIBILITY OF SUCH DAMAGE.
27*c2ec63e8Sjmcneill  */
28*c2ec63e8Sjmcneill 
29*c2ec63e8Sjmcneill #include <sys/cdefs.h>
30*c2ec63e8Sjmcneill 
31*c2ec63e8Sjmcneill __KERNEL_RCSID(0, "$NetBSD: if_bwi_sdio.c,v 1.1 2025/01/19 00:29:29 jmcneill Exp $");
32*c2ec63e8Sjmcneill 
33*c2ec63e8Sjmcneill #include <sys/param.h>
34*c2ec63e8Sjmcneill #include <sys/bus.h>
35*c2ec63e8Sjmcneill #include <sys/device.h>
36*c2ec63e8Sjmcneill #include <sys/mutex.h>
37*c2ec63e8Sjmcneill #include <sys/systm.h>
38*c2ec63e8Sjmcneill 
39*c2ec63e8Sjmcneill #include <net/if.h>
40*c2ec63e8Sjmcneill #include <net/if_dl.h>
41*c2ec63e8Sjmcneill #include <net/if_ether.h>
42*c2ec63e8Sjmcneill #include <net/if_media.h>
43*c2ec63e8Sjmcneill 
44*c2ec63e8Sjmcneill #include <netinet/in.h>
45*c2ec63e8Sjmcneill 
46*c2ec63e8Sjmcneill #include <net80211/ieee80211_node.h>
47*c2ec63e8Sjmcneill #include <net80211/ieee80211_amrr.h>
48*c2ec63e8Sjmcneill #include <net80211/ieee80211_radiotap.h>
49*c2ec63e8Sjmcneill #include <net80211/ieee80211_var.h>
50*c2ec63e8Sjmcneill 
51*c2ec63e8Sjmcneill #include <dev/ic/bwireg.h>
52*c2ec63e8Sjmcneill #include <dev/ic/bwivar.h>
53*c2ec63e8Sjmcneill 
54*c2ec63e8Sjmcneill #include <dev/pcmcia/pcmciareg.h>
55*c2ec63e8Sjmcneill 
56*c2ec63e8Sjmcneill #include <dev/sdmmc/sdmmcdevs.h>
57*c2ec63e8Sjmcneill #include <dev/sdmmc/sdmmcvar.h>
58*c2ec63e8Sjmcneill 
59*c2ec63e8Sjmcneill #define BWI_SDIO_FUNC1_SBADDRLOW	0x1000a
60*c2ec63e8Sjmcneill #define BWI_SDIO_FUNC1_SBADDRMID	0x1000b
61*c2ec63e8Sjmcneill #define BWI_SDIO_FUNC1_SBADDRHI		0x1000c
62*c2ec63e8Sjmcneill 
63*c2ec63e8Sjmcneill #define BWI_CISTPL_VENDOR		0x80
64*c2ec63e8Sjmcneill #define BWI_VENDOR_SROMREV		0
65*c2ec63e8Sjmcneill #define BWI_VENDOR_ID			1
66*c2ec63e8Sjmcneill #define BWI_VENDOR_BOARDREV		2
67*c2ec63e8Sjmcneill #define BWI_VENDOR_PA			3
68*c2ec63e8Sjmcneill #define BWI_VENDOR_OEMNAME		4
69*c2ec63e8Sjmcneill #define BWI_VENDOR_CCODE		5
70*c2ec63e8Sjmcneill #define BWI_VENDOR_ANTENNA		6
71*c2ec63e8Sjmcneill #define BWI_VENDOR_ANTGAIN		7
72*c2ec63e8Sjmcneill #define BWI_VENDOR_BFLAGS		8
73*c2ec63e8Sjmcneill #define BWI_VENDOR_LEDS			9
74*c2ec63e8Sjmcneill 
75*c2ec63e8Sjmcneill #define BWI_SDIO_REG_OFFSET(ssc, reg)	\
76*c2ec63e8Sjmcneill 	((reg) | ((ssc)->sc_sel_regwin & 0x7000))
77*c2ec63e8Sjmcneill 
78*c2ec63e8Sjmcneill #define BWI_SDIO_REG_32BIT_ACCESS	0x8000
79*c2ec63e8Sjmcneill 
80*c2ec63e8Sjmcneill static const struct bwi_sdio_product {
81*c2ec63e8Sjmcneill 	uint16_t	vendor;
82*c2ec63e8Sjmcneill 	uint16_t	product;
83*c2ec63e8Sjmcneill } bwi_sdio_products[] = {
84*c2ec63e8Sjmcneill 	{ SDMMC_VENDOR_BROADCOM, SDMMC_PRODUCT_BROADCOM_NINTENDO_WII },
85*c2ec63e8Sjmcneill };
86*c2ec63e8Sjmcneill 
87*c2ec63e8Sjmcneill struct bwi_sdio_sprom {
88*c2ec63e8Sjmcneill 	uint16_t pa_params[3];
89*c2ec63e8Sjmcneill 	uint16_t board_vendor;
90*c2ec63e8Sjmcneill 	uint16_t card_flags;
91*c2ec63e8Sjmcneill 	uint8_t srom_rev;
92*c2ec63e8Sjmcneill 	uint8_t board_rev;
93*c2ec63e8Sjmcneill 	uint8_t idle_tssi;
94*c2ec63e8Sjmcneill 	uint8_t max_txpwr;
95*c2ec63e8Sjmcneill 	uint8_t country_code;
96*c2ec63e8Sjmcneill 	uint8_t ant_avail;
97*c2ec63e8Sjmcneill 	uint8_t ant_gain;
98*c2ec63e8Sjmcneill 	uint8_t gpio[4];
99*c2ec63e8Sjmcneill };
100*c2ec63e8Sjmcneill 
101*c2ec63e8Sjmcneill struct bwi_sdio_softc {
102*c2ec63e8Sjmcneill 	struct bwi_softc sc_base;
103*c2ec63e8Sjmcneill 
104*c2ec63e8Sjmcneill 	struct sdmmc_function *sc_sf;
105*c2ec63e8Sjmcneill 	struct bwi_sdio_sprom sc_sprom;
106*c2ec63e8Sjmcneill 	uint32_t sc_sel_regwin;
107*c2ec63e8Sjmcneill 	kmutex_t sc_lock;
108*c2ec63e8Sjmcneill };
109*c2ec63e8Sjmcneill 
110*c2ec63e8Sjmcneill static int bwi_sdio_match(device_t, cfdata_t, void *);
111*c2ec63e8Sjmcneill static void bwi_sdio_attach(device_t, device_t, void *);
112*c2ec63e8Sjmcneill 
113*c2ec63e8Sjmcneill static void bwi_sdio_parse_cis(struct bwi_sdio_softc *);
114*c2ec63e8Sjmcneill 
115*c2ec63e8Sjmcneill static int bwi_sdio_intr(void *);
116*c2ec63e8Sjmcneill 
117*c2ec63e8Sjmcneill static void bwi_sdio_conf_write(void *, uint32_t, uint32_t);
118*c2ec63e8Sjmcneill static uint32_t bwi_sdio_conf_read(void *, uint32_t);
119*c2ec63e8Sjmcneill static void bwi_sdio_reg_write_2(void *, uint32_t, uint16_t);
120*c2ec63e8Sjmcneill static uint16_t bwi_sdio_reg_read_2(void *, uint32_t);
121*c2ec63e8Sjmcneill static void bwi_sdio_reg_write_4(void *, uint32_t, uint32_t);
122*c2ec63e8Sjmcneill static uint32_t bwi_sdio_reg_read_4(void *, uint32_t);
123*c2ec63e8Sjmcneill static void bwi_sdio_reg_write_multi_4(void *, uint32_t, const uint32_t *,
124*c2ec63e8Sjmcneill     size_t);
125*c2ec63e8Sjmcneill static void bwi_sdio_reg_read_multi_4(void *, uint32_t, uint32_t *,
126*c2ec63e8Sjmcneill     size_t);
127*c2ec63e8Sjmcneill 
128*c2ec63e8Sjmcneill CFATTACH_DECL_NEW(bwi_sdio, sizeof(struct bwi_sdio_softc),
129*c2ec63e8Sjmcneill     bwi_sdio_match, bwi_sdio_attach, NULL, NULL);
130*c2ec63e8Sjmcneill 
131*c2ec63e8Sjmcneill static int
132*c2ec63e8Sjmcneill bwi_sdio_match(device_t parent, cfdata_t cf, void *aux)
133*c2ec63e8Sjmcneill {
134*c2ec63e8Sjmcneill 	struct sdmmc_attach_args * const saa = aux;
135*c2ec63e8Sjmcneill 	struct sdmmc_function *sf = saa->sf;
136*c2ec63e8Sjmcneill 	struct sdmmc_cis *cis;
137*c2ec63e8Sjmcneill 	u_int n;
138*c2ec63e8Sjmcneill 
139*c2ec63e8Sjmcneill 	if (sf == NULL) {
140*c2ec63e8Sjmcneill 		return 0;
141*c2ec63e8Sjmcneill 	}
142*c2ec63e8Sjmcneill 	cis = &sf->sc->sc_fn0->cis;
143*c2ec63e8Sjmcneill 
144*c2ec63e8Sjmcneill 	for (n = 0; n < __arraycount(bwi_sdio_products); n++) {
145*c2ec63e8Sjmcneill 		const struct bwi_sdio_product *bsp = &bwi_sdio_products[n];
146*c2ec63e8Sjmcneill 
147*c2ec63e8Sjmcneill 		if (bsp->vendor == cis->manufacturer &&
148*c2ec63e8Sjmcneill 		    bsp->product == cis->product) {
149*c2ec63e8Sjmcneill 			return 1;
150*c2ec63e8Sjmcneill 		}
151*c2ec63e8Sjmcneill 	}
152*c2ec63e8Sjmcneill 
153*c2ec63e8Sjmcneill 	return 0;
154*c2ec63e8Sjmcneill }
155*c2ec63e8Sjmcneill 
156*c2ec63e8Sjmcneill static void
157*c2ec63e8Sjmcneill bwi_sdio_attach(device_t parent, device_t self, void *aux)
158*c2ec63e8Sjmcneill {
159*c2ec63e8Sjmcneill 	struct bwi_sdio_softc * const ssc = device_private(self);
160*c2ec63e8Sjmcneill 	struct bwi_softc * const sc = &ssc->sc_base;
161*c2ec63e8Sjmcneill 	struct sdmmc_attach_args * const saa = aux;
162*c2ec63e8Sjmcneill 	struct sdmmc_function *sf = saa->sf;
163*c2ec63e8Sjmcneill 	struct sdmmc_cis *cis = &sf->sc->sc_fn0->cis;
164*c2ec63e8Sjmcneill 	int error;
165*c2ec63e8Sjmcneill 	void *ih;
166*c2ec63e8Sjmcneill 
167*c2ec63e8Sjmcneill 	aprint_naive("\n");
168*c2ec63e8Sjmcneill 	aprint_normal(": Broadcom Wireless\n");
169*c2ec63e8Sjmcneill 
170*c2ec63e8Sjmcneill 	sc->sc_dev = self;
171*c2ec63e8Sjmcneill 	sc->sc_flags = BWI_F_SDIO | BWI_F_PIO;
172*c2ec63e8Sjmcneill 	sc->sc_conf_write = bwi_sdio_conf_write;
173*c2ec63e8Sjmcneill 	sc->sc_conf_read = bwi_sdio_conf_read;
174*c2ec63e8Sjmcneill 	sc->sc_reg_write_multi_4 = bwi_sdio_reg_write_multi_4;
175*c2ec63e8Sjmcneill 	sc->sc_reg_read_multi_4 = bwi_sdio_reg_read_multi_4;
176*c2ec63e8Sjmcneill 	sc->sc_reg_write_2 = bwi_sdio_reg_write_2;
177*c2ec63e8Sjmcneill 	sc->sc_reg_read_2 = bwi_sdio_reg_read_2;
178*c2ec63e8Sjmcneill 	sc->sc_reg_write_4 = bwi_sdio_reg_write_4;
179*c2ec63e8Sjmcneill 	sc->sc_reg_read_4 = bwi_sdio_reg_read_4;
180*c2ec63e8Sjmcneill 	sc->sc_pci_revid = 0;	/* XXX can this come from CIS? */
181*c2ec63e8Sjmcneill 	sc->sc_pci_did = cis->product;
182*c2ec63e8Sjmcneill 	sc->sc_pci_subvid = cis->manufacturer;
183*c2ec63e8Sjmcneill 	sc->sc_pci_subdid = cis->product;
184*c2ec63e8Sjmcneill 
185*c2ec63e8Sjmcneill 	ssc->sc_sf = sf;
186*c2ec63e8Sjmcneill 	mutex_init(&ssc->sc_lock, MUTEX_DEFAULT, IPL_NONE);
187*c2ec63e8Sjmcneill 
188*c2ec63e8Sjmcneill 	sdmmc_io_set_blocklen(ssc->sc_sf, 64);
189*c2ec63e8Sjmcneill 	if (sdmmc_io_function_enable(ssc->sc_sf) != 0) {
190*c2ec63e8Sjmcneill 		aprint_error_dev(self, "couldn't enable function\n");
191*c2ec63e8Sjmcneill 		return;
192*c2ec63e8Sjmcneill 	}
193*c2ec63e8Sjmcneill 
194*c2ec63e8Sjmcneill 	bwi_sdio_parse_cis(ssc);
195*c2ec63e8Sjmcneill 
196*c2ec63e8Sjmcneill 	ih = sdmmc_intr_establish(parent, bwi_sdio_intr, ssc,
197*c2ec63e8Sjmcneill 	    device_xname(self));
198*c2ec63e8Sjmcneill 	if (ih == NULL) {
199*c2ec63e8Sjmcneill 		aprint_error_dev(self, "couldn't establish interrupt\n");
200*c2ec63e8Sjmcneill 		return;
201*c2ec63e8Sjmcneill 	}
202*c2ec63e8Sjmcneill 
203*c2ec63e8Sjmcneill 	error = bwi_attach(sc);
204*c2ec63e8Sjmcneill 	if (error != 0) {
205*c2ec63e8Sjmcneill 		sdmmc_intr_disestablish(ih);
206*c2ec63e8Sjmcneill 		return;
207*c2ec63e8Sjmcneill 	}
208*c2ec63e8Sjmcneill 
209*c2ec63e8Sjmcneill 	sdmmc_intr_enable(ssc->sc_sf);
210*c2ec63e8Sjmcneill }
211*c2ec63e8Sjmcneill 
212*c2ec63e8Sjmcneill static void
213*c2ec63e8Sjmcneill bwi_sdio_parse_cis(struct bwi_sdio_softc *ssc)
214*c2ec63e8Sjmcneill {
215*c2ec63e8Sjmcneill 	struct sdmmc_function *sf0 = ssc->sc_sf->sc->sc_fn0;
216*c2ec63e8Sjmcneill 	struct bwi_sdio_sprom *sprom = &ssc->sc_sprom;
217*c2ec63e8Sjmcneill 	uint32_t reg;
218*c2ec63e8Sjmcneill 	uint8_t tplcode, tpllen;
219*c2ec63e8Sjmcneill 
220*c2ec63e8Sjmcneill 	reg = sdmmc_cisptr(ssc->sc_sf);
221*c2ec63e8Sjmcneill 	for (;;) {
222*c2ec63e8Sjmcneill 		tplcode = sdmmc_io_read_1(sf0, reg++);
223*c2ec63e8Sjmcneill 		if (tplcode == PCMCIA_CISTPL_NULL) {
224*c2ec63e8Sjmcneill 			continue;
225*c2ec63e8Sjmcneill 		}
226*c2ec63e8Sjmcneill 		tpllen = sdmmc_io_read_1(sf0, reg++);
227*c2ec63e8Sjmcneill 		if (tplcode == PCMCIA_CISTPL_END || tpllen == 0) {
228*c2ec63e8Sjmcneill 			break;
229*c2ec63e8Sjmcneill 		}
230*c2ec63e8Sjmcneill 		if (tplcode != BWI_CISTPL_VENDOR) {
231*c2ec63e8Sjmcneill 			reg += tpllen;
232*c2ec63e8Sjmcneill 			continue;
233*c2ec63e8Sjmcneill 		}
234*c2ec63e8Sjmcneill 
235*c2ec63e8Sjmcneill 		switch (sdmmc_io_read_1(sf0, reg)) {
236*c2ec63e8Sjmcneill 		case BWI_VENDOR_SROMREV:
237*c2ec63e8Sjmcneill 			sprom->srom_rev = sdmmc_io_read_1(sf0, reg + 1);
238*c2ec63e8Sjmcneill 			break;
239*c2ec63e8Sjmcneill 		case BWI_VENDOR_ID:
240*c2ec63e8Sjmcneill 			sprom->board_vendor =
241*c2ec63e8Sjmcneill 			    sdmmc_io_read_1(sf0, reg + 1) |
242*c2ec63e8Sjmcneill 			    ((uint16_t)sdmmc_io_read_1(sf0, reg + 2) << 8);
243*c2ec63e8Sjmcneill 			break;
244*c2ec63e8Sjmcneill 		case BWI_VENDOR_BOARDREV:
245*c2ec63e8Sjmcneill 			sprom->board_rev =
246*c2ec63e8Sjmcneill 			    sdmmc_io_read_1(sf0, reg + 1);
247*c2ec63e8Sjmcneill 			break;
248*c2ec63e8Sjmcneill 		case BWI_VENDOR_PA:
249*c2ec63e8Sjmcneill 			sprom->pa_params[0] =
250*c2ec63e8Sjmcneill 			    sdmmc_io_read_1(sf0, reg + 1) |
251*c2ec63e8Sjmcneill 			    ((uint16_t)sdmmc_io_read_1(sf0, reg + 2) << 8);
252*c2ec63e8Sjmcneill 			sprom->pa_params[1] =
253*c2ec63e8Sjmcneill 			    sdmmc_io_read_1(sf0, reg + 3) |
254*c2ec63e8Sjmcneill 			    ((uint16_t)sdmmc_io_read_1(sf0, reg + 4) << 8);
255*c2ec63e8Sjmcneill 			sprom->pa_params[2] =
256*c2ec63e8Sjmcneill 			    sdmmc_io_read_1(sf0, reg + 5) |
257*c2ec63e8Sjmcneill 			    ((uint16_t)sdmmc_io_read_1(sf0, reg + 6) << 8);
258*c2ec63e8Sjmcneill 			sprom->idle_tssi =
259*c2ec63e8Sjmcneill 			    sdmmc_io_read_1(sf0, reg + 7);
260*c2ec63e8Sjmcneill 			sprom->max_txpwr =
261*c2ec63e8Sjmcneill 			    sdmmc_io_read_1(sf0, reg + 8);
262*c2ec63e8Sjmcneill 			break;
263*c2ec63e8Sjmcneill 		case BWI_VENDOR_CCODE:
264*c2ec63e8Sjmcneill 			sprom->country_code =
265*c2ec63e8Sjmcneill 			    sdmmc_io_read_1(sf0, reg + 1);
266*c2ec63e8Sjmcneill 			break;
267*c2ec63e8Sjmcneill 		case BWI_VENDOR_ANTGAIN:
268*c2ec63e8Sjmcneill 			sprom->ant_gain = sdmmc_io_read_1(sf0, reg + 1);
269*c2ec63e8Sjmcneill 			break;
270*c2ec63e8Sjmcneill 		case BWI_VENDOR_BFLAGS:
271*c2ec63e8Sjmcneill 			sprom->card_flags =
272*c2ec63e8Sjmcneill 			    sdmmc_io_read_1(sf0, reg + 1) |
273*c2ec63e8Sjmcneill 			    ((uint16_t)sdmmc_io_read_1(sf0, reg + 2) << 8);
274*c2ec63e8Sjmcneill 			break;
275*c2ec63e8Sjmcneill 		case BWI_VENDOR_LEDS:
276*c2ec63e8Sjmcneill 			sprom->gpio[0] = sdmmc_io_read_1(sf0, reg + 1);
277*c2ec63e8Sjmcneill 			sprom->gpio[1] = sdmmc_io_read_1(sf0, reg + 2);
278*c2ec63e8Sjmcneill 			sprom->gpio[2] = sdmmc_io_read_1(sf0, reg + 3);
279*c2ec63e8Sjmcneill 			sprom->gpio[3] = sdmmc_io_read_1(sf0, reg + 4);
280*c2ec63e8Sjmcneill 			break;
281*c2ec63e8Sjmcneill 		}
282*c2ec63e8Sjmcneill 
283*c2ec63e8Sjmcneill 		reg += tpllen;
284*c2ec63e8Sjmcneill 	}
285*c2ec63e8Sjmcneill }
286*c2ec63e8Sjmcneill 
287*c2ec63e8Sjmcneill static int
288*c2ec63e8Sjmcneill bwi_sdio_intr(void *priv)
289*c2ec63e8Sjmcneill {
290*c2ec63e8Sjmcneill 	struct bwi_sdio_softc * const ssc = priv;
291*c2ec63e8Sjmcneill 
292*c2ec63e8Sjmcneill 	bwi_intr(&ssc->sc_base);
293*c2ec63e8Sjmcneill 
294*c2ec63e8Sjmcneill 	return 1;
295*c2ec63e8Sjmcneill }
296*c2ec63e8Sjmcneill 
297*c2ec63e8Sjmcneill static void
298*c2ec63e8Sjmcneill bwi_sdio_conf_write(void *priv, uint32_t reg, uint32_t val)
299*c2ec63e8Sjmcneill {
300*c2ec63e8Sjmcneill 	struct bwi_sdio_softc * const ssc = priv;
301*c2ec63e8Sjmcneill 
302*c2ec63e8Sjmcneill 	KASSERT(reg == BWI_PCIR_SEL_REGWIN);
303*c2ec63e8Sjmcneill 
304*c2ec63e8Sjmcneill 	mutex_enter(&ssc->sc_lock);
305*c2ec63e8Sjmcneill 	if (reg == BWI_PCIR_SEL_REGWIN && ssc->sc_sel_regwin != val) {
306*c2ec63e8Sjmcneill 		sdmmc_io_write_1(ssc->sc_sf, BWI_SDIO_FUNC1_SBADDRLOW,
307*c2ec63e8Sjmcneill 		    (val >> 8) & 0x80);
308*c2ec63e8Sjmcneill 		sdmmc_io_write_1(ssc->sc_sf, BWI_SDIO_FUNC1_SBADDRMID,
309*c2ec63e8Sjmcneill 		    (val >> 16) & 0xff);
310*c2ec63e8Sjmcneill 		sdmmc_io_write_1(ssc->sc_sf, BWI_SDIO_FUNC1_SBADDRHI,
311*c2ec63e8Sjmcneill 		    (val >> 24) & 0xff);
312*c2ec63e8Sjmcneill 		ssc->sc_sel_regwin = val;
313*c2ec63e8Sjmcneill 	}
314*c2ec63e8Sjmcneill 	mutex_exit(&ssc->sc_lock);
315*c2ec63e8Sjmcneill }
316*c2ec63e8Sjmcneill 
317*c2ec63e8Sjmcneill static uint32_t
318*c2ec63e8Sjmcneill bwi_sdio_conf_read(void *priv, uint32_t reg)
319*c2ec63e8Sjmcneill {
320*c2ec63e8Sjmcneill 	struct bwi_sdio_softc * const ssc = priv;
321*c2ec63e8Sjmcneill 
322*c2ec63e8Sjmcneill 	KASSERT(reg == BWI_PCIR_SEL_REGWIN);
323*c2ec63e8Sjmcneill 
324*c2ec63e8Sjmcneill 	if (reg == BWI_PCIR_SEL_REGWIN) {
325*c2ec63e8Sjmcneill 		return ssc->sc_sel_regwin;
326*c2ec63e8Sjmcneill 	} else {
327*c2ec63e8Sjmcneill 		return 0;
328*c2ec63e8Sjmcneill 	}
329*c2ec63e8Sjmcneill }
330*c2ec63e8Sjmcneill 
331*c2ec63e8Sjmcneill static void
332*c2ec63e8Sjmcneill bwi_sdio_reg_write_multi_4(void *priv, uint32_t reg, const uint32_t *datap,
333*c2ec63e8Sjmcneill     size_t count)
334*c2ec63e8Sjmcneill {
335*c2ec63e8Sjmcneill 	struct bwi_sdio_softc * const ssc = priv;
336*c2ec63e8Sjmcneill 
337*c2ec63e8Sjmcneill 	mutex_enter(&ssc->sc_lock);
338*c2ec63e8Sjmcneill 	sdmmc_io_write_multi_1(ssc->sc_sf,
339*c2ec63e8Sjmcneill 	    BWI_SDIO_REG_OFFSET(ssc, reg) | BWI_SDIO_REG_32BIT_ACCESS,
340*c2ec63e8Sjmcneill 	    (uint8_t *)__UNCONST(datap), count * sizeof(uint32_t));
341*c2ec63e8Sjmcneill 	mutex_exit(&ssc->sc_lock);
342*c2ec63e8Sjmcneill }
343*c2ec63e8Sjmcneill 
344*c2ec63e8Sjmcneill static void
345*c2ec63e8Sjmcneill bwi_sdio_reg_read_multi_4(void *priv, uint32_t reg, uint32_t *datap,
346*c2ec63e8Sjmcneill     size_t count)
347*c2ec63e8Sjmcneill {
348*c2ec63e8Sjmcneill 	struct bwi_sdio_softc * const ssc = priv;
349*c2ec63e8Sjmcneill 
350*c2ec63e8Sjmcneill 	mutex_enter(&ssc->sc_lock);
351*c2ec63e8Sjmcneill 	sdmmc_io_read_multi_1(ssc->sc_sf,
352*c2ec63e8Sjmcneill 	    BWI_SDIO_REG_OFFSET(ssc, reg) | BWI_SDIO_REG_32BIT_ACCESS,
353*c2ec63e8Sjmcneill 	    (uint8_t *)datap, count * sizeof(uint32_t));
354*c2ec63e8Sjmcneill 	mutex_exit(&ssc->sc_lock);
355*c2ec63e8Sjmcneill }
356*c2ec63e8Sjmcneill 
357*c2ec63e8Sjmcneill static void
358*c2ec63e8Sjmcneill bwi_sdio_reg_write_2(void *priv, uint32_t reg, uint16_t val)
359*c2ec63e8Sjmcneill {
360*c2ec63e8Sjmcneill 	struct bwi_sdio_softc * const ssc = priv;
361*c2ec63e8Sjmcneill 
362*c2ec63e8Sjmcneill 	val = htole16(val);
363*c2ec63e8Sjmcneill 
364*c2ec63e8Sjmcneill 	mutex_enter(&ssc->sc_lock);
365*c2ec63e8Sjmcneill 	sdmmc_io_write_2(ssc->sc_sf, BWI_SDIO_REG_OFFSET(ssc, reg), val);
366*c2ec63e8Sjmcneill 	mutex_exit(&ssc->sc_lock);
367*c2ec63e8Sjmcneill }
368*c2ec63e8Sjmcneill 
369*c2ec63e8Sjmcneill static uint16_t
370*c2ec63e8Sjmcneill bwi_sdio_reg_read_sprom(struct bwi_sdio_softc *ssc, uint32_t reg)
371*c2ec63e8Sjmcneill {
372*c2ec63e8Sjmcneill 	struct bwi_sdio_sprom *sprom = &ssc->sc_sprom;
373*c2ec63e8Sjmcneill 	struct sdmmc_cis *cis = &ssc->sc_sf->cis;
374*c2ec63e8Sjmcneill 
375*c2ec63e8Sjmcneill 	switch (reg) {
376*c2ec63e8Sjmcneill 	case BWI_SPROM_11BG_EADDR ... BWI_SPROM_11BG_EADDR + 4:
377*c2ec63e8Sjmcneill 		return *(uint16_t *)&cis->lan_nid[reg - BWI_SPROM_11BG_EADDR];
378*c2ec63e8Sjmcneill 	case BWI_SPROM_11A_EADDR ... BWI_SPROM_11A_EADDR + 4:
379*c2ec63e8Sjmcneill 		return *(uint16_t *)&cis->lan_nid[reg - BWI_SPROM_11A_EADDR];
380*c2ec63e8Sjmcneill 	case BWI_SPROM_CARD_INFO:
381*c2ec63e8Sjmcneill 		return (uint16_t)sprom->country_code << 8;
382*c2ec63e8Sjmcneill 	case BWI_SPROM_PA_PARAM_11BG ... BWI_SPROM_PA_PARAM_11BG + 4:
383*c2ec63e8Sjmcneill 		return sprom->pa_params[(reg - BWI_SPROM_PA_PARAM_11BG) / 2];
384*c2ec63e8Sjmcneill 	case BWI_SPROM_PA_PARAM_11A ... BWI_SPROM_PA_PARAM_11A + 4:
385*c2ec63e8Sjmcneill 		return sprom->pa_params[(reg - BWI_SPROM_PA_PARAM_11A) / 2];
386*c2ec63e8Sjmcneill 	case BWI_SPROM_GPIO01:
387*c2ec63e8Sjmcneill 		return sprom->gpio[0] | ((uint16_t)sprom->gpio[1] << 8);
388*c2ec63e8Sjmcneill 	case BWI_SPROM_GPIO23:
389*c2ec63e8Sjmcneill 		return sprom->gpio[2] | ((uint16_t)sprom->gpio[3] << 8);
390*c2ec63e8Sjmcneill 	case BWI_SPROM_MAX_TXPWR:
391*c2ec63e8Sjmcneill 		return sprom->max_txpwr | ((uint16_t)sprom->max_txpwr << 8);
392*c2ec63e8Sjmcneill 	case BWI_SPROM_IDLE_TSSI:
393*c2ec63e8Sjmcneill 		return sprom->idle_tssi | ((uint16_t)sprom->idle_tssi << 8);
394*c2ec63e8Sjmcneill 	case BWI_SPROM_CARD_FLAGS:
395*c2ec63e8Sjmcneill 		return sprom->card_flags;
396*c2ec63e8Sjmcneill 	case BWI_SPROM_ANT_GAIN:
397*c2ec63e8Sjmcneill 		return sprom->ant_gain | ((uint16_t)sprom->ant_gain << 8);
398*c2ec63e8Sjmcneill 	default:
399*c2ec63e8Sjmcneill 		return 0xffff;
400*c2ec63e8Sjmcneill 	}
401*c2ec63e8Sjmcneill }
402*c2ec63e8Sjmcneill 
403*c2ec63e8Sjmcneill static uint16_t
404*c2ec63e8Sjmcneill bwi_sdio_reg_read_2(void *priv, uint32_t reg)
405*c2ec63e8Sjmcneill {
406*c2ec63e8Sjmcneill 	struct bwi_sdio_softc * const ssc = priv;
407*c2ec63e8Sjmcneill 	uint16_t val;
408*c2ec63e8Sjmcneill 
409*c2ec63e8Sjmcneill 	/* Emulate SPROM reads */
410*c2ec63e8Sjmcneill 	if (reg >= BWI_SPROM_START &&
411*c2ec63e8Sjmcneill 	    reg <= BWI_SPROM_START + BWI_SPROM_ANT_GAIN) {
412*c2ec63e8Sjmcneill 		return bwi_sdio_reg_read_sprom(ssc, reg - BWI_SPROM_START);
413*c2ec63e8Sjmcneill 	}
414*c2ec63e8Sjmcneill 
415*c2ec63e8Sjmcneill 	mutex_enter(&ssc->sc_lock);
416*c2ec63e8Sjmcneill 	val = sdmmc_io_read_2(ssc->sc_sf, BWI_SDIO_REG_OFFSET(ssc, reg));
417*c2ec63e8Sjmcneill 	mutex_exit(&ssc->sc_lock);
418*c2ec63e8Sjmcneill 
419*c2ec63e8Sjmcneill 	val = le16toh(val);
420*c2ec63e8Sjmcneill 
421*c2ec63e8Sjmcneill 	return val;
422*c2ec63e8Sjmcneill }
423*c2ec63e8Sjmcneill 
424*c2ec63e8Sjmcneill static void
425*c2ec63e8Sjmcneill bwi_sdio_reg_write_4(void *priv, uint32_t reg, uint32_t val)
426*c2ec63e8Sjmcneill {
427*c2ec63e8Sjmcneill 	struct bwi_sdio_softc * const ssc = priv;
428*c2ec63e8Sjmcneill 
429*c2ec63e8Sjmcneill 	val = htole32(val);
430*c2ec63e8Sjmcneill 
431*c2ec63e8Sjmcneill 	mutex_enter(&ssc->sc_lock);
432*c2ec63e8Sjmcneill 	sdmmc_io_write_4(ssc->sc_sf,
433*c2ec63e8Sjmcneill 	    BWI_SDIO_REG_OFFSET(ssc, reg) | BWI_SDIO_REG_32BIT_ACCESS, val);
434*c2ec63e8Sjmcneill 	/* SDIO cards require a read after a 32-bit write */
435*c2ec63e8Sjmcneill 	sdmmc_io_read_4(ssc->sc_sf, 0);
436*c2ec63e8Sjmcneill 	mutex_exit(&ssc->sc_lock);
437*c2ec63e8Sjmcneill }
438*c2ec63e8Sjmcneill 
439*c2ec63e8Sjmcneill static uint32_t
440*c2ec63e8Sjmcneill bwi_sdio_reg_read_4(void *priv, uint32_t reg)
441*c2ec63e8Sjmcneill {
442*c2ec63e8Sjmcneill 	struct bwi_sdio_softc * const ssc = priv;
443*c2ec63e8Sjmcneill 	uint32_t val;
444*c2ec63e8Sjmcneill 
445*c2ec63e8Sjmcneill 	mutex_enter(&ssc->sc_lock);
446*c2ec63e8Sjmcneill 	val = sdmmc_io_read_4(ssc->sc_sf,
447*c2ec63e8Sjmcneill 	    BWI_SDIO_REG_OFFSET(ssc, reg) | BWI_SDIO_REG_32BIT_ACCESS);
448*c2ec63e8Sjmcneill 	mutex_exit(&ssc->sc_lock);
449*c2ec63e8Sjmcneill 
450*c2ec63e8Sjmcneill 	val = le32toh(val);
451*c2ec63e8Sjmcneill 
452*c2ec63e8Sjmcneill 	return val;
453*c2ec63e8Sjmcneill }
454