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