1 /* $OpenBSD: if_athn_pci.c,v 1.5 2010/02/02 17:16:47 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_AR2427 }, 91 { PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR9227 }, 92 { PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR9287 } 93 }; 94 95 int 96 athn_pci_match(struct device *parent, void *match, void *aux) 97 { 98 return (pci_matchbyid(aux, athn_pci_devices, 99 nitems(athn_pci_devices))); 100 } 101 102 void 103 athn_pci_attach(struct device *parent, struct device *self, void *aux) 104 { 105 struct athn_pci_softc *psc = (struct athn_pci_softc *)self; 106 struct athn_softc *sc = &psc->sc_sc; 107 struct pci_attach_args *pa = aux; 108 const char *intrstr; 109 pci_intr_handle_t ih; 110 pcireg_t memtype, reg; 111 pci_product_id_t subsysid; 112 int error; 113 114 sc->sc_dmat = pa->pa_dmat; 115 psc->sc_pc = pa->pa_pc; 116 psc->sc_tag = pa->pa_tag; 117 118 /* 119 * Get the offset of the PCI Express Capability Structure in PCI 120 * Configuration Space (Linux hardcodes it as 0x60.) 121 */ 122 error = pci_get_capability(pa->pa_pc, pa->pa_tag, PCI_CAP_PCIEXPRESS, 123 &psc->sc_cap_off, NULL); 124 if (error != 0) { /* Found. */ 125 sc->sc_disable_aspm = athn_pci_disable_aspm; 126 sc->flags |= ATHN_FLAG_PCIE; 127 } 128 /* 129 * Noone knows why this shit is necessary but there are claims that 130 * not doing this may cause very frequent PCI FATAL interrupts from 131 * the card: http://bugzilla.kernel.org/show_bug.cgi?id=13483 132 */ 133 reg = pci_conf_read(pa->pa_pc, pa->pa_tag, 0x40); 134 reg &= ~0xff00; 135 pci_conf_write(pa->pa_pc, pa->pa_tag, 0x40, reg); 136 137 /* Change latency timer; default value yields poor results. */ 138 reg = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_BHLC_REG); 139 reg &= ~(PCI_LATTIMER_MASK << PCI_LATTIMER_SHIFT); 140 reg |= 168 << PCI_LATTIMER_SHIFT; 141 pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_BHLC_REG, reg); 142 143 /* Determine if bluetooth is also supported (combo chip.) */ 144 reg = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_SUBSYS_ID_REG); 145 subsysid = PCI_PRODUCT(reg); 146 if (subsysid == PCI_SUBSYSID_ATHEROS_COEX3WIRE_SA || 147 subsysid == PCI_SUBSYSID_ATHEROS_COEX3WIRE_DA) 148 sc->flags |= ATHN_FLAG_BTCOEX3WIRE; 149 else if (subsysid == PCI_SUBSYSID_ATHEROS_COEX2WIRE) 150 sc->flags |= ATHN_FLAG_BTCOEX2WIRE; 151 152 /* Map control/status registers. */ 153 memtype = pci_mapreg_type(pa->pa_pc, pa->pa_tag, PCI_MAPREG_START); 154 error = pci_mapreg_map(pa, PCI_MAPREG_START, memtype, 0, &sc->sc_st, 155 &sc->sc_sh, NULL, &psc->sc_mapsize, 0); 156 if (error != 0) { 157 printf(": can't map mem space\n"); 158 return; 159 } 160 161 if (pci_intr_map(pa, &ih) != 0) { 162 printf(": can't map interrupt\n"); 163 return; 164 } 165 intrstr = pci_intr_string(psc->sc_pc, ih); 166 psc->sc_ih = pci_intr_establish(psc->sc_pc, ih, IPL_NET, 167 athn_intr, sc, sc->sc_dev.dv_xname); 168 if (psc->sc_ih == NULL) { 169 printf(": can't establish interrupt"); 170 if (intrstr != NULL) 171 printf(" at %s", intrstr); 172 printf("\n"); 173 return; 174 } 175 printf(": %s", intrstr); 176 177 athn_attach(sc); 178 } 179 180 int 181 athn_pci_detach(struct device *self, int flags) 182 { 183 struct athn_pci_softc *psc = (struct athn_pci_softc *)self; 184 struct athn_softc *sc = &psc->sc_sc; 185 186 if (psc->sc_ih != NULL) { 187 athn_detach(sc); 188 pci_intr_disestablish(psc->sc_pc, psc->sc_ih); 189 } 190 if (psc->sc_mapsize > 0) 191 bus_space_unmap(sc->sc_st, sc->sc_sh, psc->sc_mapsize); 192 193 return (0); 194 } 195 196 void 197 athn_pci_disable_aspm(struct athn_softc *sc) 198 { 199 struct athn_pci_softc *psc = (struct athn_pci_softc *)sc; 200 pcireg_t reg; 201 202 /* Disable PCIe Active State Power Management (ASPM). */ 203 reg = pci_conf_read(psc->sc_pc, psc->sc_tag, 204 psc->sc_cap_off + PCI_PCIE_LCSR); 205 reg &= ~(PCI_PCIE_LCSR_ASPM_L0S | PCI_PCIE_LCSR_ASPM_L1); 206 pci_conf_write(psc->sc_pc, psc->sc_tag, 207 psc->sc_cap_off + PCI_PCIE_LCSR, reg); 208 } 209