1*13d4bb4cSthorpej /* $NetBSD: if_athn_pci.c,v 1.14 2022/09/25 17:52:25 thorpej Exp $ */
264f89611Schristos /* $OpenBSD: if_athn_pci.c,v 1.11 2011/01/08 10:02:32 damien Exp $ */
364f89611Schristos
464f89611Schristos /*-
564f89611Schristos * Copyright (c) 2009 Damien Bergamini <damien.bergamini@free.fr>
664f89611Schristos *
764f89611Schristos * Permission to use, copy, modify, and distribute this software for any
864f89611Schristos * purpose with or without fee is hereby granted, provided that the above
964f89611Schristos * copyright notice and this permission notice appear in all copies.
1064f89611Schristos *
1164f89611Schristos * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1264f89611Schristos * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1364f89611Schristos * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1464f89611Schristos * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1564f89611Schristos * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1664f89611Schristos * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1764f89611Schristos * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1864f89611Schristos */
1964f89611Schristos
2064f89611Schristos /*
2164f89611Schristos * PCI front-end for Atheros 802.11a/g/n chipsets.
2264f89611Schristos */
2364f89611Schristos
2464f89611Schristos #include <sys/cdefs.h>
25*13d4bb4cSthorpej __KERNEL_RCSID(0, "$NetBSD: if_athn_pci.c,v 1.14 2022/09/25 17:52:25 thorpej Exp $");
2664f89611Schristos
2764f89611Schristos #include "opt_inet.h"
2864f89611Schristos
2964f89611Schristos #include <sys/param.h>
3064f89611Schristos #include <sys/sockio.h>
3164f89611Schristos #include <sys/mbuf.h>
3264f89611Schristos #include <sys/kernel.h>
3364f89611Schristos #include <sys/socket.h>
3464f89611Schristos #include <sys/systm.h>
3564f89611Schristos #include <sys/callout.h>
3664f89611Schristos #include <sys/device.h>
3764f89611Schristos
3864f89611Schristos #include <sys/bus.h>
3964f89611Schristos #include <sys/intr.h>
4064f89611Schristos
4164f89611Schristos #include <net/if.h>
4202421171Schristos #include <net/if_ether.h>
4364f89611Schristos #include <net/if_media.h>
4464f89611Schristos
4564f89611Schristos #include <net80211/ieee80211_var.h>
4664f89611Schristos #include <net80211/ieee80211_amrr.h>
4764f89611Schristos #include <net80211/ieee80211_radiotap.h>
4864f89611Schristos
4964f89611Schristos #include <dev/ic/athnreg.h>
5064f89611Schristos #include <dev/ic/athnvar.h>
5164f89611Schristos
5264f89611Schristos #include <dev/pci/pcireg.h>
5364f89611Schristos #include <dev/pci/pcivar.h>
5464f89611Schristos #include <dev/pci/pcidevs.h>
5564f89611Schristos
5664f89611Schristos #define PCI_SUBSYSID_ATHEROS_COEX2WIRE 0x309b
5764f89611Schristos #define PCI_SUBSYSID_ATHEROS_COEX3WIRE_SA 0x30aa
5864f89611Schristos #define PCI_SUBSYSID_ATHEROS_COEX3WIRE_DA 0x30ab
5964f89611Schristos
6064f89611Schristos #define ATHN_PCI_MMBA PCI_BAR(0) /* memory mapped base */
6164f89611Schristos
6264f89611Schristos struct athn_pci_softc {
6364f89611Schristos struct athn_softc psc_sc;
6464f89611Schristos
6564f89611Schristos /* PCI specific goo. */
6664f89611Schristos pci_chipset_tag_t psc_pc;
6764f89611Schristos pcitag_t psc_tag;
68aabeaec8Sjakllsch pci_intr_handle_t psc_pih;
6964f89611Schristos void *psc_ih;
7064f89611Schristos bus_space_tag_t psc_iot;
7164f89611Schristos bus_space_handle_t psc_ioh;
7264f89611Schristos bus_size_t psc_mapsz;
7364f89611Schristos int psc_cap_off;
7464f89611Schristos };
7564f89611Schristos
7664f89611Schristos #define Static static
7764f89611Schristos
7864f89611Schristos Static int athn_pci_match(device_t, cfdata_t, void *);
7964f89611Schristos Static void athn_pci_attach(device_t, device_t, void *);
8064f89611Schristos Static int athn_pci_detach(device_t, int);
8164f89611Schristos Static int athn_pci_activate(device_t, enum devact);
8264f89611Schristos
8364f89611Schristos CFATTACH_DECL_NEW(athn_pci, sizeof(struct athn_pci_softc), athn_pci_match,
8464f89611Schristos athn_pci_attach, athn_pci_detach, athn_pci_activate);
8564f89611Schristos
8664f89611Schristos Static bool athn_pci_resume(device_t, const pmf_qual_t *);
8764f89611Schristos Static bool athn_pci_suspend(device_t, const pmf_qual_t *);
8864f89611Schristos Static uint32_t athn_pci_read(struct athn_softc *, uint32_t);
8964f89611Schristos Static void athn_pci_write(struct athn_softc *, uint32_t, uint32_t);
9064f89611Schristos Static void athn_pci_write_barrier(struct athn_softc *);
9164f89611Schristos Static void athn_pci_disable_aspm(struct athn_softc *);
9264f89611Schristos
9364f89611Schristos Static int
athn_pci_match(device_t parent,cfdata_t match,void * aux)9464f89611Schristos athn_pci_match(device_t parent, cfdata_t match, void *aux)
9564f89611Schristos {
9664f89611Schristos static const struct {
9764f89611Schristos pci_vendor_id_t apd_vendor;
9864f89611Schristos pci_product_id_t apd_product;
9964f89611Schristos } athn_pci_devices[] = {
10064f89611Schristos { PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR5416 },
10164f89611Schristos { PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR5418 },
10264f89611Schristos { PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR9160 },
10364f89611Schristos { PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR9280 },
10464f89611Schristos { PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR9281 },
10564f89611Schristos { PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR9285 },
10664f89611Schristos { PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR2427 },
10764f89611Schristos { PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR9227 },
10864f89611Schristos { PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR9287 },
10964f89611Schristos { PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR9300 }
11064f89611Schristos };
11164f89611Schristos struct pci_attach_args *pa = aux;
11264f89611Schristos size_t i;
11364f89611Schristos
11464f89611Schristos for (i = 0; i < __arraycount(athn_pci_devices); i++) {
11564f89611Schristos if (PCI_VENDOR(pa->pa_id) == athn_pci_devices[i].apd_vendor &&
1163fac26a7Smartin PCI_PRODUCT(pa->pa_id) == athn_pci_devices[i].apd_product)
117419d68e6Smartin /*
118419d68e6Smartin * Match better than 1, we prefer this driver
119419d68e6Smartin * over ath(4)
120419d68e6Smartin */
121419d68e6Smartin return 10;
12264f89611Schristos }
12364f89611Schristos return 0;
12464f89611Schristos }
12564f89611Schristos
12664f89611Schristos Static void
athn_pci_attach(device_t parent,device_t self,void * aux)12764f89611Schristos athn_pci_attach(device_t parent, device_t self, void *aux)
12864f89611Schristos {
12964f89611Schristos struct athn_pci_softc *psc = device_private(self);
13064f89611Schristos struct athn_softc *sc = &psc->psc_sc;
13164f89611Schristos struct ieee80211com *ic = &sc->sc_ic;
13264f89611Schristos struct pci_attach_args *pa = aux;
13364f89611Schristos const char *intrstr;
13464f89611Schristos pcireg_t memtype, reg;
13564f89611Schristos pci_product_id_t subsysid;
13664f89611Schristos int error;
137e58a356cSchristos char intrbuf[PCI_INTRSTR_LEN];
13864f89611Schristos
13934e0ebeaSmartin sc->sc_dev = self;
14064f89611Schristos sc->sc_dmat = pa->pa_dmat;
14164f89611Schristos psc->psc_pc = pa->pa_pc;
14264f89611Schristos psc->psc_tag = pa->pa_tag;
14364f89611Schristos
14464f89611Schristos sc->sc_ops.read = athn_pci_read;
14564f89611Schristos sc->sc_ops.write = athn_pci_write;
14664f89611Schristos sc->sc_ops.write_barrier = athn_pci_write_barrier;
14764f89611Schristos
14864f89611Schristos /*
14964f89611Schristos * Get the offset of the PCI Express Capability Structure in PCI
15064f89611Schristos * Configuration Space (Linux hardcodes it as 0x60.)
15164f89611Schristos */
15264f89611Schristos error = pci_get_capability(pa->pa_pc, pa->pa_tag, PCI_CAP_PCIEXPRESS,
15364f89611Schristos &psc->psc_cap_off, NULL);
15464f89611Schristos if (error != 0) { /* Found. */
15564f89611Schristos sc->sc_disable_aspm = athn_pci_disable_aspm;
15664f89611Schristos sc->sc_flags |= ATHN_FLAG_PCIE;
15764f89611Schristos }
15864f89611Schristos /*
15964f89611Schristos * Noone knows why this shit is necessary but there are claims that
16064f89611Schristos * not doing this may cause very frequent PCI FATAL interrupts from
16164f89611Schristos * the card: http://bugzilla.kernel.org/show_bug.cgi?id=13483
16264f89611Schristos */
16364f89611Schristos reg = pci_conf_read(pa->pa_pc, pa->pa_tag, 0x40);
16464f89611Schristos if (reg & 0xff00)
16564f89611Schristos pci_conf_write(pa->pa_pc, pa->pa_tag, 0x40, reg & ~0xff00);
16664f89611Schristos
16764f89611Schristos /* Change latency timer; default value yields poor results. */
16864f89611Schristos reg = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_BHLC_REG);
16964f89611Schristos reg &= ~(PCI_LATTIMER_MASK << PCI_LATTIMER_SHIFT);
17064f89611Schristos reg |= 168 << PCI_LATTIMER_SHIFT;
17164f89611Schristos pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_BHLC_REG, reg);
17264f89611Schristos
17364f89611Schristos /* Determine if bluetooth is also supported (combo chip.) */
17464f89611Schristos reg = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_SUBSYS_ID_REG);
17564f89611Schristos subsysid = PCI_PRODUCT(reg);
17664f89611Schristos if (subsysid == PCI_SUBSYSID_ATHEROS_COEX3WIRE_SA ||
17764f89611Schristos subsysid == PCI_SUBSYSID_ATHEROS_COEX3WIRE_DA)
17864f89611Schristos sc->sc_flags |= ATHN_FLAG_BTCOEX3WIRE;
17964f89611Schristos else if (subsysid == PCI_SUBSYSID_ATHEROS_COEX2WIRE)
18064f89611Schristos sc->sc_flags |= ATHN_FLAG_BTCOEX2WIRE;
18164f89611Schristos
18264f89611Schristos /*
18364f89611Schristos * Setup memory-mapping of PCI registers.
18464f89611Schristos */
18564f89611Schristos memtype = pci_mapreg_type(pa->pa_pc, pa->pa_tag, ATHN_PCI_MMBA);
18664f89611Schristos if (memtype != PCI_MAPREG_TYPE_MEM &&
18764f89611Schristos memtype != PCI_MAPREG_MEM_TYPE_64BIT) {
18864f89611Schristos aprint_error_dev(self, "bad pci register type %d\n",
18964f89611Schristos (int)memtype);
19064f89611Schristos goto fail;
19164f89611Schristos }
192b558379eSmartin error = pci_mapreg_map(pa, ATHN_PCI_MMBA, memtype, 0, &psc->psc_iot,
19364f89611Schristos &psc->psc_ioh, NULL, &psc->psc_mapsz);
19464f89611Schristos if (error != 0) {
19564f89611Schristos aprint_error_dev(self, "cannot map register space\n");
19664f89611Schristos goto fail;
19764f89611Schristos }
19864f89611Schristos
19964f89611Schristos /*
20064f89611Schristos * Arrange interrupt line.
20164f89611Schristos */
202aabeaec8Sjakllsch if (pci_intr_map(pa, &psc->psc_pih) != 0) {
20364f89611Schristos aprint_error_dev(self, "couldn't map interrupt\n");
20464f89611Schristos goto fail1;
20564f89611Schristos }
20664f89611Schristos
207aabeaec8Sjakllsch intrstr = pci_intr_string(psc->psc_pc, psc->psc_pih, intrbuf, sizeof(intrbuf));
208d3cda613Sjdolecek psc->psc_ih = pci_intr_establish_xname(psc->psc_pc, psc->psc_pih,
209d3cda613Sjdolecek IPL_NET, athn_intr, sc, device_xname(self));
21064f89611Schristos if (psc->psc_ih == NULL) {
21164f89611Schristos aprint_error_dev(self, "couldn't map interrupt\n");
21264f89611Schristos goto fail1;
21364f89611Schristos }
21464f89611Schristos
215013dc312Smartin ic->ic_ifp = &sc->sc_if;
21664f89611Schristos if (athn_attach(sc) != 0)
21764f89611Schristos goto fail2;
21864f89611Schristos
21942847337Smartin aprint_verbose_dev(self, "interrupting at %s\n", intrstr);
22042847337Smartin
22164f89611Schristos if (pmf_device_register(self, athn_pci_suspend, athn_pci_resume)) {
22264f89611Schristos pmf_class_network_register(self, &sc->sc_if);
22364f89611Schristos pmf_device_suspend(self, &sc->sc_qual);
22464f89611Schristos }
22564f89611Schristos else
22664f89611Schristos aprint_error_dev(self, "couldn't establish power handler\n");
22764f89611Schristos
22864f89611Schristos ieee80211_announce(ic);
22964f89611Schristos return;
23064f89611Schristos
23164f89611Schristos fail2:
23264f89611Schristos pci_intr_disestablish(psc->psc_pc, psc->psc_ih);
23364f89611Schristos psc->psc_ih = NULL;
23464f89611Schristos fail1:
23564f89611Schristos bus_space_unmap(psc->psc_iot, psc->psc_ioh, psc->psc_mapsz);
23664f89611Schristos psc->psc_mapsz = 0;
23764f89611Schristos fail:
23864f89611Schristos return;
23964f89611Schristos }
24064f89611Schristos
24164f89611Schristos Static int
athn_pci_detach(device_t self,int flags)24264f89611Schristos athn_pci_detach(device_t self, int flags)
24364f89611Schristos {
24464f89611Schristos struct athn_pci_softc *psc = device_private(self);
24564f89611Schristos struct athn_softc *sc = &psc->psc_sc;
24664f89611Schristos
24764f89611Schristos if (psc->psc_ih != NULL) {
24864f89611Schristos athn_detach(sc);
24964f89611Schristos pci_intr_disestablish(psc->psc_pc, psc->psc_ih);
25064f89611Schristos psc->psc_ih = NULL;
25164f89611Schristos }
25264f89611Schristos if (psc->psc_mapsz > 0) {
25364f89611Schristos bus_space_unmap(psc->psc_iot, psc->psc_ioh, psc->psc_mapsz);
25464f89611Schristos psc->psc_mapsz = 0;
25564f89611Schristos }
25664f89611Schristos return 0;
25764f89611Schristos }
25864f89611Schristos
25964f89611Schristos Static int
athn_pci_activate(device_t self,enum devact act)26064f89611Schristos athn_pci_activate(device_t self, enum devact act)
26164f89611Schristos {
26264f89611Schristos struct athn_pci_softc *psc = device_private(self);
26364f89611Schristos struct athn_softc *sc = &psc->psc_sc;
26464f89611Schristos
26564f89611Schristos switch (act) {
26664f89611Schristos case DVACT_DEACTIVATE:
26764f89611Schristos if_deactivate(sc->sc_ic.ic_ifp);
26864f89611Schristos break;
26964f89611Schristos }
27064f89611Schristos return 0;
27164f89611Schristos }
27264f89611Schristos
27364f89611Schristos Static bool
athn_pci_suspend(device_t self,const pmf_qual_t * qual)27464f89611Schristos athn_pci_suspend(device_t self, const pmf_qual_t *qual)
27564f89611Schristos {
27664f89611Schristos struct athn_pci_softc *psc = device_private(self);
27764f89611Schristos struct athn_softc *sc = &psc->psc_sc;
27864f89611Schristos
27964f89611Schristos athn_suspend(sc);
28064f89611Schristos if (psc->psc_ih != NULL) {
28164f89611Schristos pci_intr_disestablish(psc->psc_pc, psc->psc_ih);
28264f89611Schristos psc->psc_ih = NULL;
28364f89611Schristos }
28464f89611Schristos return true;
28564f89611Schristos }
28664f89611Schristos
28764f89611Schristos Static bool
athn_pci_resume(device_t self,const pmf_qual_t * qual)28864f89611Schristos athn_pci_resume(device_t self, const pmf_qual_t *qual)
28964f89611Schristos {
29064f89611Schristos struct athn_pci_softc *psc = device_private(self);
29164f89611Schristos struct athn_softc *sc = &psc->psc_sc;
29264f89611Schristos pcireg_t reg;
29364f89611Schristos
29464f89611Schristos /*
29564f89611Schristos * XXX: see comment in athn_attach().
29664f89611Schristos */
29764f89611Schristos reg = pci_conf_read(psc->psc_pc, psc->psc_tag, 0x40);
29864f89611Schristos if (reg & 0xff00)
29964f89611Schristos pci_conf_write(psc->psc_pc, psc->psc_tag, 0x40, reg & ~0xff00);
30064f89611Schristos
301d3cda613Sjdolecek /* XXX re-establishing interrupt shouldn't be needed */
302d3cda613Sjdolecek psc->psc_ih = pci_intr_establish_xname(psc->psc_pc, psc->psc_pih,
303d3cda613Sjdolecek IPL_NET, athn_intr, sc, device_xname(self));
30464f89611Schristos if (psc->psc_ih == NULL) {
30564f89611Schristos aprint_error_dev(self, "couldn't map interrupt\n");
30664f89611Schristos return false;
30764f89611Schristos }
30864f89611Schristos return athn_resume(sc);
30964f89611Schristos }
31064f89611Schristos
31164f89611Schristos Static uint32_t
athn_pci_read(struct athn_softc * sc,uint32_t addr)31264f89611Schristos athn_pci_read(struct athn_softc *sc, uint32_t addr)
31364f89611Schristos {
31464f89611Schristos struct athn_pci_softc *psc = (struct athn_pci_softc *)sc;
31564f89611Schristos
31664f89611Schristos return bus_space_read_4(psc->psc_iot, psc->psc_ioh, addr);
31764f89611Schristos }
31864f89611Schristos
31964f89611Schristos Static void
athn_pci_write(struct athn_softc * sc,uint32_t addr,uint32_t val)32064f89611Schristos athn_pci_write(struct athn_softc *sc, uint32_t addr, uint32_t val)
32164f89611Schristos {
32264f89611Schristos struct athn_pci_softc *psc = (struct athn_pci_softc *)sc;
32364f89611Schristos
32464f89611Schristos bus_space_write_4(psc->psc_iot, psc->psc_ioh, addr, val);
32564f89611Schristos }
32664f89611Schristos
32764f89611Schristos Static void
athn_pci_write_barrier(struct athn_softc * sc)32864f89611Schristos athn_pci_write_barrier(struct athn_softc *sc)
32964f89611Schristos {
33064f89611Schristos struct athn_pci_softc *psc = (struct athn_pci_softc *)sc;
33164f89611Schristos
33264f89611Schristos bus_space_barrier(psc->psc_iot, psc->psc_ioh, 0, psc->psc_mapsz,
33364f89611Schristos BUS_SPACE_BARRIER_WRITE);
33464f89611Schristos }
33564f89611Schristos
33664f89611Schristos Static void
athn_pci_disable_aspm(struct athn_softc * sc)33764f89611Schristos athn_pci_disable_aspm(struct athn_softc *sc)
33864f89611Schristos {
33964f89611Schristos struct athn_pci_softc *psc = (struct athn_pci_softc *)sc;
34064f89611Schristos pcireg_t reg;
34164f89611Schristos
34264f89611Schristos /* Disable PCIe Active State Power Management (ASPM). */
34364f89611Schristos reg = pci_conf_read(psc->psc_pc, psc->psc_tag,
3444184db8fSmsaitoh psc->psc_cap_off + PCIE_LCSR);
3454184db8fSmsaitoh reg &= ~(PCIE_LCSR_ASPM_L0S | PCIE_LCSR_ASPM_L1);
34664f89611Schristos pci_conf_write(psc->psc_pc, psc->psc_tag,
3474184db8fSmsaitoh psc->psc_cap_off + PCIE_LCSR, reg);
34864f89611Schristos }
349