1 /* $OpenBSD: if_athn_pci.c,v 1.1 2009/11/14 16:55:11 damien Exp $ */ 2 3 /*- 4 * Copyright (c) 2009 Damien Bergamini <damien.bergamini@free.fr> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 /* 20 * PCI front-end for Atheros 802.11a/g/n chipsets. 21 */ 22 23 #include "bpfilter.h" 24 25 #include <sys/param.h> 26 #include <sys/sockio.h> 27 #include <sys/mbuf.h> 28 #include <sys/kernel.h> 29 #include <sys/socket.h> 30 #include <sys/systm.h> 31 #include <sys/malloc.h> 32 #include <sys/timeout.h> 33 #include <sys/device.h> 34 35 #include <machine/bus.h> 36 #include <machine/intr.h> 37 38 #include <net/if.h> 39 #include <net/if_dl.h> 40 #include <net/if_media.h> 41 42 #include <netinet/in.h> 43 #include <netinet/if_ether.h> 44 45 #include <net80211/ieee80211_var.h> 46 #include <net80211/ieee80211_amrr.h> 47 #include <net80211/ieee80211_radiotap.h> 48 49 #include <dev/ic/athnreg.h> 50 #include <dev/ic/athnvar.h> 51 52 #include <dev/pci/pcireg.h> 53 #include <dev/pci/pcivar.h> 54 #include <dev/pci/pcidevs.h> 55 56 #define PCI_SUBSYSID_ATHEROS_COEX2WIRE 0x309b 57 #define PCI_SUBSYSID_ATHEROS_COEX3WIRE_SA 0x30aa 58 #define PCI_SUBSYSID_ATHEROS_COEX3WIRE_DA 0x30ab 59 60 struct athn_pci_softc { 61 struct athn_softc sc_sc; 62 63 /* PCI specific goo. */ 64 pci_chipset_tag_t sc_pc; 65 pcitag_t sc_tag; 66 void *sc_ih; 67 bus_size_t sc_mapsize; 68 int sc_cap_off; 69 }; 70 71 int athn_pci_match(struct device *, void *, void *); 72 void athn_pci_attach(struct device *, struct device *, void *); 73 int athn_pci_detach(struct device *, int); 74 void athn_pci_disable_aspm(struct athn_softc *); 75 76 struct cfattach athn_pci_ca = { 77 sizeof (struct athn_pci_softc), 78 athn_pci_match, 79 athn_pci_attach, 80 athn_pci_detach 81 }; 82 83 static const struct pci_matchid athn_pci_devices[] = { 84 { PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR5416 }, 85 { PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR5418 }, 86 { PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR9160 }, 87 { PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR9280 }, 88 { PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR9281 }, 89 { PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR9285 }, 90 { PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR9287 } 91 }; 92 93 int 94 athn_pci_match(struct device *parent, void *match, void *aux) 95 { 96 return (pci_matchbyid(aux, athn_pci_devices, 97 nitems(athn_pci_devices))); 98 } 99 100 void 101 athn_pci_attach(struct device *parent, struct device *self, void *aux) 102 { 103 struct athn_pci_softc *psc = (struct athn_pci_softc *)self; 104 struct athn_softc *sc = &psc->sc_sc; 105 struct pci_attach_args *pa = aux; 106 const char *intrstr; 107 pci_intr_handle_t ih; 108 pcireg_t memtype, reg; 109 pci_product_id_t subsysid; 110 int error; 111 112 sc->sc_dmat = pa->pa_dmat; 113 psc->sc_pc = pa->pa_pc; 114 psc->sc_tag = pa->pa_tag; 115 116 /* 117 * Get the offset of the PCI Express Capability Structure in PCI 118 * Configuration Space (Linux hardcodes it as 0x60.) 119 */ 120 error = pci_get_capability(pa->pa_pc, pa->pa_tag, PCI_CAP_PCIEXPRESS, 121 &psc->sc_cap_off, NULL); 122 if (error != 0) { /* Found. */ 123 sc->sc_disable_aspm = athn_pci_disable_aspm; 124 sc->flags |= ATHN_FLAG_PCIE; 125 } 126 /* 127 * Noone knows why this shit is necessary but there are claims that 128 * not doing this may cause very frequent PCI FATAL interrupts from 129 * the card: http://bugzilla.kernel.org/show_bug.cgi?id=13483 130 */ 131 reg = pci_conf_read(pa->pa_pc, pa->pa_tag, 0x40); 132 reg &= ~0xff00; 133 pci_conf_write(pa->pa_pc, pa->pa_tag, 0x40, reg); 134 135 /* Change latency timer; default value yields poor results. */ 136 reg = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_BHLC_REG); 137 reg &= ~(PCI_LATTIMER_MASK << PCI_LATTIMER_SHIFT); 138 reg |= 168 << PCI_LATTIMER_SHIFT; 139 pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_BHLC_REG, reg); 140 141 /* Determine if bluetooth is also supported (combo chip.) */ 142 reg = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_SUBSYS_ID_REG); 143 subsysid = PCI_PRODUCT(reg); 144 if (subsysid == PCI_SUBSYSID_ATHEROS_COEX3WIRE_SA || 145 subsysid == PCI_SUBSYSID_ATHEROS_COEX3WIRE_DA) 146 sc->flags |= ATHN_FLAG_BTCOEX3WIRE; 147 else if (subsysid == PCI_SUBSYSID_ATHEROS_COEX2WIRE) 148 sc->flags |= ATHN_FLAG_BTCOEX2WIRE; 149 150 /* Map control/status registers. */ 151 memtype = pci_mapreg_type(pa->pa_pc, pa->pa_tag, PCI_MAPREG_START); 152 error = pci_mapreg_map(pa, PCI_MAPREG_START, memtype, 0, &sc->sc_st, 153 &sc->sc_sh, NULL, &psc->sc_mapsize, 0); 154 if (error != 0) { 155 printf(": can't map mem space\n"); 156 return; 157 } 158 159 if (pci_intr_map(pa, &ih) != 0) { 160 printf(": can't map interrupt\n"); 161 return; 162 } 163 intrstr = pci_intr_string(psc->sc_pc, ih); 164 psc->sc_ih = pci_intr_establish(psc->sc_pc, ih, IPL_NET, 165 athn_intr, sc, sc->sc_dev.dv_xname); 166 if (psc->sc_ih == NULL) { 167 printf(": can't establish interrupt"); 168 if (intrstr != NULL) 169 printf(" at %s", intrstr); 170 printf("\n"); 171 return; 172 } 173 printf(": %s", intrstr); 174 175 athn_attach(sc); 176 } 177 178 int 179 athn_pci_detach(struct device *self, int flags) 180 { 181 struct athn_pci_softc *psc = (struct athn_pci_softc *)self; 182 struct athn_softc *sc = &psc->sc_sc; 183 184 if (psc->sc_ih != NULL) { 185 athn_detach(sc); 186 pci_intr_disestablish(psc->sc_pc, psc->sc_ih); 187 } 188 if (psc->sc_mapsize > 0) 189 bus_space_unmap(sc->sc_st, sc->sc_sh, psc->sc_mapsize); 190 191 return (0); 192 } 193 194 void 195 athn_pci_disable_aspm(struct athn_softc *sc) 196 { 197 struct athn_pci_softc *psc = (struct athn_pci_softc *)sc; 198 pcireg_t reg; 199 200 /* Disable PCIe Active State Power Management (ASPM). */ 201 reg = pci_conf_read(psc->sc_pc, psc->sc_tag, 202 psc->sc_cap_off + PCI_PCIE_LCSR); 203 reg &= ~(PCI_PCIE_LCSR_ASPM_L0S | PCI_PCIE_LCSR_ASPM_L1); 204 pci_conf_write(psc->sc_pc, psc->sc_tag, 205 psc->sc_cap_off + PCI_PCIE_LCSR, reg); 206 } 207