xref: /netbsd-src/sys/dev/pci/if_bwi_pci.c (revision cf80ca28cb9ef8022317ff1af66f5a815972f8ad)
1*cf80ca28Sthorpej /*	$NetBSD: if_bwi_pci.c,v 1.18 2023/12/20 05:08:34 thorpej Exp $	*/
2d6ded78bSmacallan /*	$OpenBSD: if_bwi_pci.c,v 1.6 2008/02/14 22:10:02 brad Exp $ */
3b7893fb6Smacallan 
4b7893fb6Smacallan /*
5b7893fb6Smacallan  * Copyright (c) 2007 Marcus Glocker <mglocker@openbsd.org>
6b7893fb6Smacallan  *
7b7893fb6Smacallan  * Permission to use, copy, modify, and distribute this software for any
8b7893fb6Smacallan  * purpose with or without fee is hereby granted, provided that the above
9b7893fb6Smacallan  * copyright notice and this permission notice appear in all copies.
10b7893fb6Smacallan  *
11b7893fb6Smacallan  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12b7893fb6Smacallan  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13b7893fb6Smacallan  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14b7893fb6Smacallan  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15b7893fb6Smacallan  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16b7893fb6Smacallan  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17b7893fb6Smacallan  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18b7893fb6Smacallan  */
19b7893fb6Smacallan 
20b7893fb6Smacallan /*
21d6ded78bSmacallan  * Broadcom AirForce BCM43xx IEEE 802.11b/g wireless network driver
22d6ded78bSmacallan  * PCI front end
23b7893fb6Smacallan  */
24b7893fb6Smacallan 
25b7893fb6Smacallan 
26d6ded78bSmacallan #include <sys/cdefs.h>
27*cf80ca28Sthorpej __KERNEL_RCSID(0, "$NetBSD: if_bwi_pci.c,v 1.18 2023/12/20 05:08:34 thorpej Exp $");
28d6ded78bSmacallan 
29b7893fb6Smacallan #include <sys/param.h>
30d6ded78bSmacallan #include <sys/callout.h>
31b7893fb6Smacallan #include <sys/device.h>
32d6ded78bSmacallan #include <sys/kernel.h>
33d6ded78bSmacallan #include <sys/mbuf.h>
34d6ded78bSmacallan #include <sys/socket.h>
35d6ded78bSmacallan #include <sys/sockio.h>
36d6ded78bSmacallan #include <sys/systm.h>
37b7893fb6Smacallan 
38b11777e5Sdyoung #include <sys/bus.h>
39b7893fb6Smacallan 
40b7893fb6Smacallan #include <net/if.h>
41b7893fb6Smacallan #include <net/if_dl.h>
42d6ded78bSmacallan #include <net/if_ether.h>
43b7893fb6Smacallan #include <net/if_media.h>
44b7893fb6Smacallan 
45b7893fb6Smacallan #include <net80211/ieee80211_var.h>
46b7893fb6Smacallan #include <net80211/ieee80211_amrr.h>
47b7893fb6Smacallan #include <net80211/ieee80211_radiotap.h>
48b7893fb6Smacallan 
49b7893fb6Smacallan #include <dev/ic/bwivar.h>
50b7893fb6Smacallan 
51b7893fb6Smacallan #include <dev/pci/pcireg.h>
52b7893fb6Smacallan #include <dev/pci/pcivar.h>
53b7893fb6Smacallan #include <dev/pci/pcidevs.h>
54b7893fb6Smacallan 
55b7893fb6Smacallan /* Base Address Register */
56c9e24ddaSdyoung #define BWI_PCI_BAR0 PCI_BAR(0)
57b7893fb6Smacallan 
58c6c48498Scegger static int	bwi_pci_match(device_t, cfdata_t, void *);
59c6c48498Scegger static void	bwi_pci_attach(device_t, device_t, void *);
60c6c48498Scegger static int	bwi_pci_detach(device_t, int);
61d6ded78bSmacallan static void	bwi_pci_conf_write(void *, uint32_t, uint32_t);
62d6ded78bSmacallan static uint32_t	bwi_pci_conf_read(void *, uint32_t);
63b7893fb6Smacallan 
64b7893fb6Smacallan struct bwi_pci_softc {
65b7893fb6Smacallan 	struct bwi_softc	 psc_bwi;
66b7893fb6Smacallan 
67b7893fb6Smacallan 	pci_chipset_tag_t        psc_pc;
68b7893fb6Smacallan 	pcitag_t		 psc_pcitag;
69b7893fb6Smacallan 
70b7893fb6Smacallan 	bus_size_t		 psc_mapsize;
71b7893fb6Smacallan };
72b7893fb6Smacallan 
7357fb22a8Scegger CFATTACH_DECL_NEW(bwi_pci, sizeof(struct bwi_pci_softc),
74d6ded78bSmacallan     bwi_pci_match, bwi_pci_attach, bwi_pci_detach, NULL);
75b7893fb6Smacallan 
76c06247ceSthorpej static const struct device_compatible_entry compat_data[] = {
77c06247ceSthorpej 	{ .id = PCI_ID_CODE(PCI_VENDOR_BROADCOM,
78c06247ceSthorpej 		PCI_PRODUCT_BROADCOM_BCM4303), },
79c06247ceSthorpej 
80c06247ceSthorpej 	{ .id = PCI_ID_CODE(PCI_VENDOR_BROADCOM,
81c06247ceSthorpej 		PCI_PRODUCT_BROADCOM_BCM4306), },
82c06247ceSthorpej 
83c06247ceSthorpej 	{ .id = PCI_ID_CODE(PCI_VENDOR_BROADCOM,
84c06247ceSthorpej 		PCI_PRODUCT_BROADCOM_BCM4306_2), },
85c06247ceSthorpej 
86c06247ceSthorpej 	{ .id = PCI_ID_CODE(PCI_VENDOR_BROADCOM,
87c06247ceSthorpej 		PCI_PRODUCT_BROADCOM_BCM4307), },
88c06247ceSthorpej 
89c06247ceSthorpej 	{ .id = PCI_ID_CODE(PCI_VENDOR_BROADCOM,
90c06247ceSthorpej 		PCI_PRODUCT_BROADCOM_BCM4309), },
91c06247ceSthorpej 
92c06247ceSthorpej 	{ .id = PCI_ID_CODE(PCI_VENDOR_BROADCOM,
93c06247ceSthorpej 		PCI_PRODUCT_BROADCOM_BCM4311), },
94c06247ceSthorpej 
95c06247ceSthorpej 	{ .id = PCI_ID_CODE(PCI_VENDOR_BROADCOM,
96c06247ceSthorpej 		PCI_PRODUCT_BROADCOM_BCM4312), },
97c06247ceSthorpej 
98c06247ceSthorpej 	{ .id = PCI_ID_CODE(PCI_VENDOR_BROADCOM,
99c06247ceSthorpej 		PCI_PRODUCT_BROADCOM_BCM4318), },
100c06247ceSthorpej 
101c06247ceSthorpej 	{ .id = PCI_ID_CODE(PCI_VENDOR_BROADCOM,
102c06247ceSthorpej 		PCI_PRODUCT_BROADCOM_BCM4319), },
103c06247ceSthorpej 
104c06247ceSthorpej 	{ .id = PCI_ID_CODE(PCI_VENDOR_BROADCOM,
105c06247ceSthorpej 		PCI_PRODUCT_BROADCOM_BCM4322), },
106c06247ceSthorpej 
107c06247ceSthorpej 	{ .id = PCI_ID_CODE(PCI_VENDOR_BROADCOM,
108c06247ceSthorpej 		PCI_PRODUCT_BROADCOM_BCM43XG), },
109c06247ceSthorpej 
110c06247ceSthorpej 	{ .id = PCI_ID_CODE(PCI_VENDOR_BROADCOM,
111c06247ceSthorpej 		PCI_PRODUCT_BROADCOM_BCM4328), },
112c06247ceSthorpej 
113c06247ceSthorpej 	PCI_COMPAT_EOL
114c06247ceSthorpej };
115c06247ceSthorpej 
116d6ded78bSmacallan static int
bwi_pci_match(device_t parent,cfdata_t match,void * aux)117c6c48498Scegger bwi_pci_match(device_t parent, cfdata_t match, void *aux)
118b7893fb6Smacallan {
119d6ded78bSmacallan 	struct pci_attach_args *pa = aux;
120d6ded78bSmacallan 
121c06247ceSthorpej 	return pci_compatible_match(pa, compat_data);
122d6ded78bSmacallan }
123d6ded78bSmacallan 
124d6ded78bSmacallan static void
bwi_pci_attach(device_t parent,device_t self,void * aux)125c6c48498Scegger bwi_pci_attach(device_t parent, device_t self, void *aux)
126b7893fb6Smacallan {
127c6c48498Scegger 	struct bwi_pci_softc *psc = device_private(self);
128b7893fb6Smacallan 	struct pci_attach_args *pa = aux;
129b7893fb6Smacallan 	struct bwi_softc *sc = &psc->psc_bwi;
130b7893fb6Smacallan 	const char *intrstr = NULL;
131b7893fb6Smacallan 	pci_intr_handle_t ih;
132b7893fb6Smacallan 	pcireg_t memtype, reg;
1332cae64a1Scegger 	int error = 0;
134e58a356cSchristos 	char intrbuf[PCI_INTRSTR_LEN];
135b7893fb6Smacallan 
136394d81abScegger 	aprint_naive("\n");
137e5dc6216Sphx 	aprint_normal(": Broadcom Wireless\n");
138394d81abScegger 
13957fb22a8Scegger 	sc->sc_dev = self;
140b7893fb6Smacallan 	sc->sc_dmat = pa->pa_dmat;
141b7893fb6Smacallan 	psc->psc_pc = pa->pa_pc;
142b7893fb6Smacallan 	psc->psc_pcitag = pa->pa_tag;
143b7893fb6Smacallan 
144b7893fb6Smacallan 	/* map control / status registers */
145b7893fb6Smacallan 	memtype = pci_mapreg_type(pa->pa_pc, pa->pa_tag, BWI_PCI_BAR0);
146d6ded78bSmacallan 	switch (memtype) {
147d6ded78bSmacallan 	case PCI_MAPREG_TYPE_MEM | PCI_MAPREG_MEM_TYPE_32BIT:
148d6ded78bSmacallan 	case PCI_MAPREG_TYPE_MEM | PCI_MAPREG_MEM_TYPE_64BIT:
149d6ded78bSmacallan 		break;
150d6ded78bSmacallan 	default:
151394d81abScegger 		aprint_error_dev(self, "invalid base address register\n");
152b7893fb6Smacallan 		return;
153b7893fb6Smacallan 	}
154b7893fb6Smacallan 
1558bc54e5bSmsaitoh 	if (pci_mapreg_map(pa, BWI_PCI_BAR0, memtype, 0, &sc->sc_mem_bt,
1568bc54e5bSmsaitoh 	    &sc->sc_mem_bh, NULL, &psc->psc_mapsize) != 0) {
157394d81abScegger 		aprint_error_dev(self, "could not map mem space\n");
158394d81abScegger 		return;
159394d81abScegger 	}
160d6ded78bSmacallan 
161b7893fb6Smacallan 	/* map interrupt */
162b7893fb6Smacallan 	if (pci_intr_map(pa, &ih) != 0) {
163d6ded78bSmacallan 		aprint_error_dev(self, "could not map interrupt\n");
164394d81abScegger 		goto fail;
165b7893fb6Smacallan 	}
166b7893fb6Smacallan 
167b7893fb6Smacallan 	/* establish interrupt */
168e58a356cSchristos 	intrstr = pci_intr_string(psc->psc_pc, ih, intrbuf, sizeof(intrbuf));
169d3cda613Sjdolecek 	sc->sc_ih = pci_intr_establish_xname(psc->psc_pc, ih, IPL_NET, bwi_intr,
170d3cda613Sjdolecek 	    sc, device_xname(self));
171d6ded78bSmacallan 	if (sc->sc_ih == NULL) {
172d6ded78bSmacallan 		aprint_error_dev(self, "could not establish interrupt");
173b7893fb6Smacallan 		if (intrstr != NULL)
174d6ded78bSmacallan 			aprint_error(" at %s", intrstr);
175d6ded78bSmacallan 		aprint_error("\n");
176394d81abScegger 		goto fail;
177b7893fb6Smacallan 	}
178d6ded78bSmacallan 	aprint_normal_dev(self, "interrupting at %s\n", intrstr);
179b7893fb6Smacallan 
180b7893fb6Smacallan 	/* we need to access PCI config space from the driver */
181b7893fb6Smacallan 	sc->sc_conf_write = bwi_pci_conf_write;
182b7893fb6Smacallan 	sc->sc_conf_read = bwi_pci_conf_read;
183b7893fb6Smacallan 
184b7893fb6Smacallan 	reg = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_SUBSYS_ID_REG);
185b7893fb6Smacallan 
186b7893fb6Smacallan 	sc->sc_pci_revid = PCI_REVISION(pa->pa_class);
187b7893fb6Smacallan 	sc->sc_pci_did = PCI_PRODUCT(pa->pa_id);
188b7893fb6Smacallan 	sc->sc_pci_subvid = PCI_VENDOR(reg);
189b7893fb6Smacallan 	sc->sc_pci_subdid = PCI_PRODUCT(reg);
190b7893fb6Smacallan 
19102190332Skefren 	if (!pmf_device_register(self, bwi_suspend, bwi_resume))
19202190332Skefren 		aprint_error_dev(self, "couldn't establish power handler\n");
19302190332Skefren 
1942cae64a1Scegger 	error = bwi_attach(sc);
1952cae64a1Scegger 	if (error)
1962cae64a1Scegger 		goto fail;
197394d81abScegger 	return;
198394d81abScegger 
199394d81abScegger fail:
200394d81abScegger 	if (sc->sc_ih) {
201394d81abScegger 		pci_intr_disestablish(psc->psc_pc, sc->sc_ih);
202394d81abScegger 		sc->sc_ih = NULL;
203394d81abScegger 	}
204394d81abScegger 	if (psc->psc_mapsize) {
205394d81abScegger 		bus_space_unmap(sc->sc_mem_bt, sc->sc_mem_bh, psc->psc_mapsize);
206394d81abScegger 		psc->psc_mapsize = 0;
207394d81abScegger 	}
208394d81abScegger 	return;
209b7893fb6Smacallan }
210b7893fb6Smacallan 
211b7893fb6Smacallan int
bwi_pci_detach(device_t self,int flags)212c6c48498Scegger bwi_pci_detach(device_t self, int flags)
213b7893fb6Smacallan {
214c6c48498Scegger 	struct bwi_pci_softc *psc = device_private(self);
215b7893fb6Smacallan 	struct bwi_softc *sc = &psc->psc_bwi;
216b7893fb6Smacallan 
21702190332Skefren 	pmf_device_deregister(self);
21802190332Skefren 
219b7893fb6Smacallan 	bwi_detach(sc);
220d6ded78bSmacallan 
221d6ded78bSmacallan 	if (sc->sc_ih != NULL) {
222d6ded78bSmacallan 		pci_intr_disestablish(psc->psc_pc, sc->sc_ih);
223d6ded78bSmacallan 		sc->sc_ih = NULL;
224d6ded78bSmacallan 	}
225b7893fb6Smacallan 
226b7893fb6Smacallan 	return (0);
227b7893fb6Smacallan }
228b7893fb6Smacallan 
229d6ded78bSmacallan static void
bwi_pci_conf_write(void * sc,uint32_t reg,uint32_t val)230c6c48498Scegger bwi_pci_conf_write(void *sc, uint32_t reg, uint32_t val)
231b7893fb6Smacallan {
232c6c48498Scegger 	struct bwi_pci_softc *psc = (struct bwi_pci_softc *)sc;
233b7893fb6Smacallan 
234b7893fb6Smacallan 	pci_conf_write(psc->psc_pc, psc->psc_pcitag, reg, val);
235b7893fb6Smacallan }
236b7893fb6Smacallan 
237d6ded78bSmacallan static uint32_t
bwi_pci_conf_read(void * sc,uint32_t reg)238c6c48498Scegger bwi_pci_conf_read(void *sc, uint32_t reg)
239b7893fb6Smacallan {
240c6c48498Scegger 	struct bwi_pci_softc *psc = (struct bwi_pci_softc *)sc;
241b7893fb6Smacallan 
242b7893fb6Smacallan 	return (pci_conf_read(psc->psc_pc, psc->psc_pcitag, reg));
243b7893fb6Smacallan }
244