Home | History | Annotate | Line # | Download | only in pci
      1  1.14   thorpej /*	$NetBSD: if_athn_pci.c,v 1.14 2022/09/25 17:52:25 thorpej Exp $	*/
      2   1.1  christos /*	$OpenBSD: if_athn_pci.c,v 1.11 2011/01/08 10:02:32 damien Exp $	*/
      3   1.1  christos 
      4   1.1  christos /*-
      5   1.1  christos  * Copyright (c) 2009 Damien Bergamini <damien.bergamini (at) free.fr>
      6   1.1  christos  *
      7   1.1  christos  * Permission to use, copy, modify, and distribute this software for any
      8   1.1  christos  * purpose with or without fee is hereby granted, provided that the above
      9   1.1  christos  * copyright notice and this permission notice appear in all copies.
     10   1.1  christos  *
     11   1.1  christos  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     12   1.1  christos  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     13   1.1  christos  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     14   1.1  christos  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     15   1.1  christos  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     16   1.1  christos  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     17   1.1  christos  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     18   1.1  christos  */
     19   1.1  christos 
     20   1.1  christos /*
     21   1.1  christos  * PCI front-end for Atheros 802.11a/g/n chipsets.
     22   1.1  christos  */
     23   1.1  christos 
     24   1.1  christos #include <sys/cdefs.h>
     25  1.14   thorpej __KERNEL_RCSID(0, "$NetBSD: if_athn_pci.c,v 1.14 2022/09/25 17:52:25 thorpej Exp $");
     26   1.1  christos 
     27   1.1  christos #include "opt_inet.h"
     28   1.1  christos 
     29   1.1  christos #include <sys/param.h>
     30   1.1  christos #include <sys/sockio.h>
     31   1.1  christos #include <sys/mbuf.h>
     32   1.1  christos #include <sys/kernel.h>
     33   1.1  christos #include <sys/socket.h>
     34   1.1  christos #include <sys/systm.h>
     35   1.1  christos #include <sys/callout.h>
     36   1.1  christos #include <sys/device.h>
     37   1.1  christos 
     38   1.1  christos #include <sys/bus.h>
     39   1.1  christos #include <sys/intr.h>
     40   1.1  christos 
     41   1.1  christos #include <net/if.h>
     42   1.5  christos #include <net/if_ether.h>
     43   1.1  christos #include <net/if_media.h>
     44   1.1  christos 
     45   1.1  christos #include <net80211/ieee80211_var.h>
     46   1.1  christos #include <net80211/ieee80211_amrr.h>
     47   1.1  christos #include <net80211/ieee80211_radiotap.h>
     48   1.1  christos 
     49   1.1  christos #include <dev/ic/athnreg.h>
     50   1.1  christos #include <dev/ic/athnvar.h>
     51   1.1  christos 
     52   1.1  christos #include <dev/pci/pcireg.h>
     53   1.1  christos #include <dev/pci/pcivar.h>
     54   1.1  christos #include <dev/pci/pcidevs.h>
     55   1.1  christos 
     56   1.1  christos #define PCI_SUBSYSID_ATHEROS_COEX2WIRE		0x309b
     57   1.1  christos #define PCI_SUBSYSID_ATHEROS_COEX3WIRE_SA	0x30aa
     58   1.1  christos #define PCI_SUBSYSID_ATHEROS_COEX3WIRE_DA	0x30ab
     59   1.1  christos 
     60   1.1  christos #define ATHN_PCI_MMBA	PCI_BAR(0)	/* memory mapped base */
     61   1.1  christos 
     62   1.1  christos struct athn_pci_softc {
     63   1.1  christos 	struct athn_softc	psc_sc;
     64   1.1  christos 
     65   1.1  christos 	/* PCI specific goo. */
     66   1.1  christos 	pci_chipset_tag_t	psc_pc;
     67   1.1  christos 	pcitag_t		psc_tag;
     68  1.12  jakllsch 	pci_intr_handle_t	psc_pih;
     69   1.1  christos 	void			*psc_ih;
     70   1.1  christos 	bus_space_tag_t		psc_iot;
     71   1.1  christos 	bus_space_handle_t	psc_ioh;
     72   1.1  christos 	bus_size_t		psc_mapsz;
     73   1.1  christos 	int			psc_cap_off;
     74   1.1  christos };
     75   1.1  christos 
     76   1.1  christos #define Static static
     77   1.1  christos 
     78   1.1  christos Static int	athn_pci_match(device_t, cfdata_t, void *);
     79   1.1  christos Static void	athn_pci_attach(device_t, device_t, void *);
     80   1.1  christos Static int	athn_pci_detach(device_t, int);
     81   1.1  christos Static int	athn_pci_activate(device_t, enum devact);
     82   1.1  christos 
     83   1.1  christos CFATTACH_DECL_NEW(athn_pci, sizeof(struct athn_pci_softc), athn_pci_match,
     84   1.1  christos     athn_pci_attach, athn_pci_detach, athn_pci_activate);
     85   1.1  christos 
     86   1.1  christos Static bool	athn_pci_resume(device_t, const pmf_qual_t *);
     87   1.1  christos Static bool	athn_pci_suspend(device_t, const pmf_qual_t *);
     88   1.1  christos Static uint32_t	athn_pci_read(struct athn_softc *, uint32_t);
     89   1.1  christos Static void	athn_pci_write(struct athn_softc *, uint32_t, uint32_t);
     90   1.1  christos Static void	athn_pci_write_barrier(struct athn_softc *);
     91   1.1  christos Static void	athn_pci_disable_aspm(struct athn_softc *);
     92   1.1  christos 
     93   1.1  christos Static int
     94   1.1  christos athn_pci_match(device_t parent, cfdata_t match, void *aux)
     95   1.1  christos {
     96   1.1  christos 	static const struct {
     97   1.1  christos 		pci_vendor_id_t		apd_vendor;
     98   1.1  christos 		pci_product_id_t	apd_product;
     99   1.1  christos 	} athn_pci_devices[] = {
    100   1.1  christos 		{ PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR5416 },
    101   1.1  christos 		{ PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR5418 },
    102   1.1  christos 		{ PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR9160 },
    103   1.1  christos 		{ PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR9280 },
    104   1.1  christos 		{ PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR9281 },
    105   1.1  christos 		{ PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR9285 },
    106   1.1  christos 		{ PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR2427 },
    107   1.1  christos 		{ PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR9227 },
    108   1.1  christos 		{ PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR9287 },
    109   1.1  christos 		{ PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR9300 }
    110   1.1  christos 	};
    111   1.1  christos 	struct pci_attach_args *pa = aux;
    112   1.1  christos 	size_t i;
    113   1.1  christos 
    114   1.1  christos 	for (i = 0; i < __arraycount(athn_pci_devices); i++) {
    115   1.1  christos 		if (PCI_VENDOR(pa->pa_id) == athn_pci_devices[i].apd_vendor &&
    116   1.2    martin 		    PCI_PRODUCT(pa->pa_id) == athn_pci_devices[i].apd_product)
    117   1.7    martin 			/*
    118   1.7    martin 			 * Match better than 1, we prefer this driver
    119   1.7    martin 			 * over ath(4)
    120   1.7    martin 			 */
    121   1.7    martin 			return 10;
    122   1.1  christos 	}
    123   1.1  christos 	return 0;
    124   1.1  christos }
    125   1.1  christos 
    126   1.1  christos Static void
    127   1.1  christos athn_pci_attach(device_t parent, device_t self, void *aux)
    128   1.1  christos {
    129   1.1  christos 	struct athn_pci_softc *psc = device_private(self);
    130   1.1  christos 	struct athn_softc *sc = &psc->psc_sc;
    131   1.1  christos 	struct ieee80211com *ic = &sc->sc_ic;
    132   1.1  christos 	struct pci_attach_args *pa = aux;
    133   1.1  christos 	const char *intrstr;
    134   1.1  christos 	pcireg_t memtype, reg;
    135   1.1  christos 	pci_product_id_t subsysid;
    136   1.1  christos 	int error;
    137  1.10  christos 	char intrbuf[PCI_INTRSTR_LEN];
    138   1.1  christos 
    139   1.3    martin 	sc->sc_dev = self;
    140   1.1  christos 	sc->sc_dmat = pa->pa_dmat;
    141   1.1  christos 	psc->psc_pc = pa->pa_pc;
    142   1.1  christos 	psc->psc_tag = pa->pa_tag;
    143   1.1  christos 
    144   1.1  christos 	sc->sc_ops.read = athn_pci_read;
    145   1.1  christos 	sc->sc_ops.write = athn_pci_write;
    146   1.1  christos 	sc->sc_ops.write_barrier = athn_pci_write_barrier;
    147   1.1  christos 
    148   1.1  christos 	/*
    149   1.1  christos 	 * Get the offset of the PCI Express Capability Structure in PCI
    150   1.1  christos 	 * Configuration Space (Linux hardcodes it as 0x60.)
    151   1.1  christos 	 */
    152   1.1  christos 	error = pci_get_capability(pa->pa_pc, pa->pa_tag, PCI_CAP_PCIEXPRESS,
    153   1.1  christos 	    &psc->psc_cap_off, NULL);
    154   1.1  christos 	if (error != 0) {	/* Found. */
    155   1.1  christos 		sc->sc_disable_aspm = athn_pci_disable_aspm;
    156   1.1  christos 		sc->sc_flags |= ATHN_FLAG_PCIE;
    157   1.1  christos 	}
    158   1.1  christos 	/*
    159   1.1  christos 	 * Noone knows why this shit is necessary but there are claims that
    160   1.1  christos 	 * not doing this may cause very frequent PCI FATAL interrupts from
    161   1.1  christos 	 * the card: http://bugzilla.kernel.org/show_bug.cgi?id=13483
    162   1.1  christos 	 */
    163   1.1  christos 	reg = pci_conf_read(pa->pa_pc, pa->pa_tag, 0x40);
    164   1.1  christos 	if (reg & 0xff00)
    165   1.1  christos 		pci_conf_write(pa->pa_pc, pa->pa_tag, 0x40, reg & ~0xff00);
    166   1.1  christos 
    167   1.1  christos 	/* Change latency timer; default value yields poor results. */
    168   1.1  christos 	reg = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_BHLC_REG);
    169   1.1  christos 	reg &= ~(PCI_LATTIMER_MASK << PCI_LATTIMER_SHIFT);
    170   1.1  christos 	reg |= 168 << PCI_LATTIMER_SHIFT;
    171   1.1  christos 	pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_BHLC_REG, reg);
    172   1.1  christos 
    173   1.1  christos 	/* Determine if bluetooth is also supported (combo chip.) */
    174   1.1  christos 	reg = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_SUBSYS_ID_REG);
    175   1.1  christos 	subsysid = PCI_PRODUCT(reg);
    176   1.1  christos 	if (subsysid == PCI_SUBSYSID_ATHEROS_COEX3WIRE_SA ||
    177   1.1  christos 	    subsysid == PCI_SUBSYSID_ATHEROS_COEX3WIRE_DA)
    178   1.1  christos 		sc->sc_flags |= ATHN_FLAG_BTCOEX3WIRE;
    179   1.1  christos 	else if (subsysid == PCI_SUBSYSID_ATHEROS_COEX2WIRE)
    180   1.1  christos 		sc->sc_flags |= ATHN_FLAG_BTCOEX2WIRE;
    181   1.1  christos 
    182   1.1  christos 	/*
    183   1.1  christos 	 * Setup memory-mapping of PCI registers.
    184   1.1  christos 	 */
    185   1.1  christos 	memtype = pci_mapreg_type(pa->pa_pc, pa->pa_tag, ATHN_PCI_MMBA);
    186   1.1  christos 	if (memtype != PCI_MAPREG_TYPE_MEM &&
    187   1.1  christos 	    memtype != PCI_MAPREG_MEM_TYPE_64BIT) {
    188   1.1  christos 		aprint_error_dev(self, "bad pci register type %d\n",
    189   1.1  christos 		    (int)memtype);
    190   1.1  christos 		goto fail;
    191   1.1  christos 	}
    192   1.9    martin 	error = pci_mapreg_map(pa, ATHN_PCI_MMBA, memtype, 0, &psc->psc_iot,
    193   1.1  christos 	    &psc->psc_ioh, NULL, &psc->psc_mapsz);
    194   1.1  christos 	if (error != 0) {
    195   1.1  christos 		aprint_error_dev(self, "cannot map register space\n");
    196   1.1  christos 		goto fail;
    197   1.1  christos 	}
    198   1.1  christos 
    199   1.1  christos 	/*
    200   1.1  christos 	 * Arrange interrupt line.
    201   1.1  christos 	 */
    202  1.12  jakllsch 	if (pci_intr_map(pa, &psc->psc_pih) != 0) {
    203   1.1  christos 		aprint_error_dev(self, "couldn't map interrupt\n");
    204   1.1  christos 		goto fail1;
    205   1.1  christos 	}
    206   1.1  christos 
    207  1.12  jakllsch 	intrstr = pci_intr_string(psc->psc_pc, psc->psc_pih, intrbuf, sizeof(intrbuf));
    208  1.13  jdolecek 	psc->psc_ih = pci_intr_establish_xname(psc->psc_pc, psc->psc_pih,
    209  1.13  jdolecek 	    IPL_NET, athn_intr, sc, device_xname(self));
    210   1.1  christos 	if (psc->psc_ih == NULL) {
    211   1.1  christos 		aprint_error_dev(self, "couldn't map interrupt\n");
    212   1.1  christos 		goto fail1;
    213   1.1  christos 	}
    214   1.1  christos 
    215   1.4    martin 	ic->ic_ifp = &sc->sc_if;
    216   1.1  christos 	if (athn_attach(sc) != 0)
    217   1.1  christos 		goto fail2;
    218   1.1  christos 
    219   1.6    martin 	aprint_verbose_dev(self, "interrupting at %s\n", intrstr);
    220   1.6    martin 
    221   1.1  christos 	if (pmf_device_register(self, athn_pci_suspend, athn_pci_resume)) {
    222   1.1  christos 		pmf_class_network_register(self, &sc->sc_if);
    223   1.1  christos 		pmf_device_suspend(self, &sc->sc_qual);
    224   1.1  christos 	}
    225   1.1  christos 	else
    226   1.1  christos 		aprint_error_dev(self, "couldn't establish power handler\n");
    227   1.1  christos 
    228   1.1  christos 	ieee80211_announce(ic);
    229   1.1  christos 	return;
    230   1.1  christos 
    231   1.1  christos  fail2:
    232   1.1  christos 	pci_intr_disestablish(psc->psc_pc, psc->psc_ih);
    233   1.1  christos 	psc->psc_ih = NULL;
    234   1.1  christos  fail1:
    235   1.1  christos 	bus_space_unmap(psc->psc_iot, psc->psc_ioh, psc->psc_mapsz);
    236   1.1  christos 	psc->psc_mapsz = 0;
    237   1.1  christos  fail:
    238   1.1  christos 	return;
    239   1.1  christos }
    240   1.1  christos 
    241   1.1  christos Static int
    242   1.1  christos athn_pci_detach(device_t self, int flags)
    243   1.1  christos {
    244   1.1  christos 	struct athn_pci_softc *psc = device_private(self);
    245   1.1  christos 	struct athn_softc *sc = &psc->psc_sc;
    246   1.1  christos 
    247   1.1  christos 	if (psc->psc_ih != NULL) {
    248   1.1  christos 		athn_detach(sc);
    249   1.1  christos 		pci_intr_disestablish(psc->psc_pc, psc->psc_ih);
    250   1.1  christos 		psc->psc_ih = NULL;
    251   1.1  christos 	}
    252   1.1  christos 	if (psc->psc_mapsz > 0) {
    253   1.1  christos 		bus_space_unmap(psc->psc_iot, psc->psc_ioh, psc->psc_mapsz);
    254   1.1  christos 		psc->psc_mapsz = 0;
    255   1.1  christos 	}
    256   1.1  christos 	return 0;
    257   1.1  christos }
    258   1.1  christos 
    259   1.1  christos Static int
    260   1.1  christos athn_pci_activate(device_t self, enum devact act)
    261   1.1  christos {
    262   1.1  christos 	struct athn_pci_softc *psc = device_private(self);
    263   1.1  christos 	struct athn_softc *sc = &psc->psc_sc;
    264   1.1  christos 
    265   1.1  christos 	switch (act) {
    266   1.1  christos 	case DVACT_DEACTIVATE:
    267   1.1  christos 		if_deactivate(sc->sc_ic.ic_ifp);
    268   1.1  christos 		break;
    269   1.1  christos 	}
    270   1.1  christos 	return 0;
    271   1.1  christos }
    272   1.1  christos 
    273   1.1  christos Static bool
    274   1.1  christos athn_pci_suspend(device_t self, const pmf_qual_t *qual)
    275   1.1  christos {
    276   1.1  christos 	struct athn_pci_softc *psc = device_private(self);
    277   1.1  christos 	struct athn_softc *sc = &psc->psc_sc;
    278   1.1  christos 
    279   1.1  christos 	athn_suspend(sc);
    280   1.1  christos 	if (psc->psc_ih != NULL) {
    281   1.1  christos 		pci_intr_disestablish(psc->psc_pc, psc->psc_ih);
    282   1.1  christos 		psc->psc_ih = NULL;
    283   1.1  christos 	}
    284   1.1  christos 	return true;
    285   1.1  christos }
    286   1.1  christos 
    287   1.1  christos Static bool
    288   1.1  christos athn_pci_resume(device_t self, const pmf_qual_t *qual)
    289   1.1  christos {
    290   1.1  christos 	struct athn_pci_softc *psc = device_private(self);
    291   1.1  christos 	struct athn_softc *sc = &psc->psc_sc;
    292   1.1  christos 	pcireg_t reg;
    293   1.1  christos 
    294   1.1  christos 	/*
    295   1.1  christos 	 * XXX: see comment in athn_attach().
    296   1.1  christos 	 */
    297   1.1  christos 	reg = pci_conf_read(psc->psc_pc, psc->psc_tag, 0x40);
    298   1.1  christos 	if (reg & 0xff00)
    299   1.1  christos 		pci_conf_write(psc->psc_pc, psc->psc_tag, 0x40, reg & ~0xff00);
    300   1.1  christos 
    301  1.13  jdolecek 	/* XXX re-establishing interrupt shouldn't be needed */
    302  1.13  jdolecek 	psc->psc_ih = pci_intr_establish_xname(psc->psc_pc, psc->psc_pih,
    303  1.13  jdolecek 	    IPL_NET, athn_intr, sc, device_xname(self));
    304   1.1  christos 	if (psc->psc_ih == NULL) {
    305   1.1  christos 		aprint_error_dev(self, "couldn't map interrupt\n");
    306   1.1  christos 		return false;
    307   1.1  christos 	}
    308   1.1  christos 	return athn_resume(sc);
    309   1.1  christos }
    310   1.1  christos 
    311   1.1  christos Static uint32_t
    312   1.1  christos athn_pci_read(struct athn_softc *sc, uint32_t addr)
    313   1.1  christos {
    314   1.1  christos 	struct athn_pci_softc *psc = (struct athn_pci_softc *)sc;
    315   1.1  christos 
    316   1.1  christos 	return bus_space_read_4(psc->psc_iot, psc->psc_ioh, addr);
    317   1.1  christos }
    318   1.1  christos 
    319   1.1  christos Static void
    320   1.1  christos athn_pci_write(struct athn_softc *sc, uint32_t addr, uint32_t val)
    321   1.1  christos {
    322   1.1  christos 	struct athn_pci_softc *psc = (struct athn_pci_softc *)sc;
    323   1.1  christos 
    324   1.1  christos 	bus_space_write_4(psc->psc_iot, psc->psc_ioh, addr, val);
    325   1.1  christos }
    326   1.1  christos 
    327   1.1  christos Static void
    328   1.1  christos athn_pci_write_barrier(struct athn_softc *sc)
    329   1.1  christos {
    330   1.1  christos 	struct athn_pci_softc *psc = (struct athn_pci_softc *)sc;
    331   1.1  christos 
    332   1.1  christos 	bus_space_barrier(psc->psc_iot, psc->psc_ioh, 0, psc->psc_mapsz,
    333   1.1  christos 	    BUS_SPACE_BARRIER_WRITE);
    334   1.1  christos }
    335   1.1  christos 
    336   1.1  christos Static void
    337   1.1  christos athn_pci_disable_aspm(struct athn_softc *sc)
    338   1.1  christos {
    339   1.1  christos 	struct athn_pci_softc *psc = (struct athn_pci_softc *)sc;
    340   1.1  christos 	pcireg_t reg;
    341   1.1  christos 
    342   1.1  christos 	/* Disable PCIe Active State Power Management (ASPM). */
    343   1.1  christos 	reg = pci_conf_read(psc->psc_pc, psc->psc_tag,
    344   1.8   msaitoh 	    psc->psc_cap_off + PCIE_LCSR);
    345   1.8   msaitoh 	reg &= ~(PCIE_LCSR_ASPM_L0S | PCIE_LCSR_ASPM_L1);
    346   1.1  christos 	pci_conf_write(psc->psc_pc, psc->psc_tag,
    347   1.8   msaitoh 	    psc->psc_cap_off + PCIE_LCSR, reg);
    348   1.1  christos }
    349