1 /* $OpenBSD: if_athn_pci.c,v 1.11 2011/01/08 10:02:32 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 #include <sys/workq.h> 35 36 #include <machine/bus.h> 37 #include <machine/intr.h> 38 39 #include <net/if.h> 40 #include <net/if_dl.h> 41 #include <net/if_media.h> 42 43 #include <netinet/in.h> 44 #include <netinet/if_ether.h> 45 46 #include <net80211/ieee80211_var.h> 47 #include <net80211/ieee80211_amrr.h> 48 #include <net80211/ieee80211_radiotap.h> 49 50 #include <dev/ic/athnreg.h> 51 #include <dev/ic/athnvar.h> 52 53 #include <dev/pci/pcireg.h> 54 #include <dev/pci/pcivar.h> 55 #include <dev/pci/pcidevs.h> 56 57 #define PCI_SUBSYSID_ATHEROS_COEX2WIRE 0x309b 58 #define PCI_SUBSYSID_ATHEROS_COEX3WIRE_SA 0x30aa 59 #define PCI_SUBSYSID_ATHEROS_COEX3WIRE_DA 0x30ab 60 61 struct athn_pci_softc { 62 struct athn_softc sc_sc; 63 64 /* PCI specific goo. */ 65 pci_chipset_tag_t sc_pc; 66 pcitag_t sc_tag; 67 void *sc_ih; 68 bus_space_tag_t sc_st; 69 bus_space_handle_t sc_sh; 70 bus_size_t sc_mapsize; 71 int sc_cap_off; 72 struct workq_task sc_resume_wqt; 73 }; 74 75 int athn_pci_match(struct device *, void *, void *); 76 void athn_pci_attach(struct device *, struct device *, void *); 77 int athn_pci_detach(struct device *, int); 78 int athn_pci_activate(struct device *, int); 79 void athn_pci_resume(void *, void *); 80 uint32_t athn_pci_read(struct athn_softc *, uint32_t); 81 void athn_pci_write(struct athn_softc *, uint32_t, uint32_t); 82 void athn_pci_write_barrier(struct athn_softc *); 83 void athn_pci_disable_aspm(struct athn_softc *); 84 85 struct cfattach athn_pci_ca = { 86 sizeof (struct athn_pci_softc), 87 athn_pci_match, 88 athn_pci_attach, 89 athn_pci_detach, 90 athn_pci_activate 91 }; 92 93 static const struct pci_matchid athn_pci_devices[] = { 94 { PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR5416 }, 95 { PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR5418 }, 96 { PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR9160 }, 97 { PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR9280 }, 98 { PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR9281 }, 99 { PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR9285 }, 100 { PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR2427 }, 101 { PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR9227 }, 102 { PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR9287 }, 103 { PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR9300 } 104 }; 105 106 int 107 athn_pci_match(struct device *parent, void *match, void *aux) 108 { 109 return (pci_matchbyid(aux, athn_pci_devices, 110 nitems(athn_pci_devices))); 111 } 112 113 void 114 athn_pci_attach(struct device *parent, struct device *self, void *aux) 115 { 116 struct athn_pci_softc *psc = (struct athn_pci_softc *)self; 117 struct athn_softc *sc = &psc->sc_sc; 118 struct pci_attach_args *pa = aux; 119 const char *intrstr; 120 pci_intr_handle_t ih; 121 pcireg_t memtype, reg; 122 pci_product_id_t subsysid; 123 int error; 124 125 sc->sc_dmat = pa->pa_dmat; 126 psc->sc_pc = pa->pa_pc; 127 psc->sc_tag = pa->pa_tag; 128 129 sc->ops.read = athn_pci_read; 130 sc->ops.write = athn_pci_write; 131 sc->ops.write_barrier = athn_pci_write_barrier; 132 133 /* 134 * Get the offset of the PCI Express Capability Structure in PCI 135 * Configuration Space (Linux hardcodes it as 0x60.) 136 */ 137 error = pci_get_capability(pa->pa_pc, pa->pa_tag, PCI_CAP_PCIEXPRESS, 138 &psc->sc_cap_off, NULL); 139 if (error != 0) { /* Found. */ 140 sc->sc_disable_aspm = athn_pci_disable_aspm; 141 sc->flags |= ATHN_FLAG_PCIE; 142 } 143 /* 144 * Noone knows why this shit is necessary but there are claims that 145 * not doing this may cause very frequent PCI FATAL interrupts from 146 * the card: http://bugzilla.kernel.org/show_bug.cgi?id=13483 147 */ 148 reg = pci_conf_read(pa->pa_pc, pa->pa_tag, 0x40); 149 if (reg & 0xff00) 150 pci_conf_write(pa->pa_pc, pa->pa_tag, 0x40, reg & ~0xff00); 151 152 /* Change latency timer; default value yields poor results. */ 153 reg = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_BHLC_REG); 154 reg &= ~(PCI_LATTIMER_MASK << PCI_LATTIMER_SHIFT); 155 reg |= 168 << PCI_LATTIMER_SHIFT; 156 pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_BHLC_REG, reg); 157 158 /* Determine if bluetooth is also supported (combo chip.) */ 159 reg = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_SUBSYS_ID_REG); 160 subsysid = PCI_PRODUCT(reg); 161 if (subsysid == PCI_SUBSYSID_ATHEROS_COEX3WIRE_SA || 162 subsysid == PCI_SUBSYSID_ATHEROS_COEX3WIRE_DA) 163 sc->flags |= ATHN_FLAG_BTCOEX3WIRE; 164 else if (subsysid == PCI_SUBSYSID_ATHEROS_COEX2WIRE) 165 sc->flags |= ATHN_FLAG_BTCOEX2WIRE; 166 167 /* Map control/status registers. */ 168 memtype = pci_mapreg_type(pa->pa_pc, pa->pa_tag, PCI_MAPREG_START); 169 error = pci_mapreg_map(pa, PCI_MAPREG_START, memtype, 0, &psc->sc_st, 170 &psc->sc_sh, NULL, &psc->sc_mapsize, 0); 171 if (error != 0) { 172 printf(": can't map mem space\n"); 173 return; 174 } 175 176 if (pci_intr_map(pa, &ih) != 0) { 177 printf(": can't map interrupt\n"); 178 return; 179 } 180 intrstr = pci_intr_string(psc->sc_pc, ih); 181 psc->sc_ih = pci_intr_establish(psc->sc_pc, ih, IPL_NET, 182 athn_intr, sc, sc->sc_dev.dv_xname); 183 if (psc->sc_ih == NULL) { 184 printf(": can't establish interrupt"); 185 if (intrstr != NULL) 186 printf(" at %s", intrstr); 187 printf("\n"); 188 return; 189 } 190 printf(": %s\n", intrstr); 191 192 athn_attach(sc); 193 } 194 195 int 196 athn_pci_detach(struct device *self, int flags) 197 { 198 struct athn_pci_softc *psc = (struct athn_pci_softc *)self; 199 struct athn_softc *sc = &psc->sc_sc; 200 201 if (psc->sc_ih != NULL) { 202 athn_detach(sc); 203 pci_intr_disestablish(psc->sc_pc, psc->sc_ih); 204 } 205 if (psc->sc_mapsize > 0) 206 bus_space_unmap(psc->sc_st, psc->sc_sh, psc->sc_mapsize); 207 208 return (0); 209 } 210 211 int 212 athn_pci_activate(struct device *self, int act) 213 { 214 struct athn_pci_softc *psc = (struct athn_pci_softc *)self; 215 struct athn_softc *sc = &psc->sc_sc; 216 217 switch (act) { 218 case DVACT_SUSPEND: 219 athn_suspend(sc); 220 break; 221 case DVACT_RESUME: 222 workq_queue_task(NULL, &psc->sc_resume_wqt, 0, 223 athn_pci_resume, psc, NULL); 224 break; 225 } 226 227 return (0); 228 } 229 230 void 231 athn_pci_resume(void *arg1, void *arg2) 232 { 233 struct athn_pci_softc *psc = arg1; 234 struct athn_softc *sc = &psc->sc_sc; 235 pcireg_t reg; 236 int s; 237 238 reg = pci_conf_read(psc->sc_pc, psc->sc_tag, 0x40); 239 if (reg & 0xff00) 240 pci_conf_write(psc->sc_pc, psc->sc_tag, 0x40, reg & ~0xff00); 241 242 s = splnet(); 243 athn_resume(sc); 244 splx(s); 245 } 246 247 uint32_t 248 athn_pci_read(struct athn_softc *sc, uint32_t addr) 249 { 250 struct athn_pci_softc *psc = (struct athn_pci_softc *)sc; 251 252 return (bus_space_read_4(psc->sc_st, psc->sc_sh, addr)); 253 } 254 255 void 256 athn_pci_write(struct athn_softc *sc, uint32_t addr, uint32_t val) 257 { 258 struct athn_pci_softc *psc = (struct athn_pci_softc *)sc; 259 260 bus_space_write_4(psc->sc_st, psc->sc_sh, addr, val); 261 } 262 263 void 264 athn_pci_write_barrier(struct athn_softc *sc) 265 { 266 struct athn_pci_softc *psc = (struct athn_pci_softc *)sc; 267 268 bus_space_barrier(psc->sc_st, psc->sc_sh, 0, psc->sc_mapsize, 269 BUS_SPACE_BARRIER_WRITE); 270 } 271 272 void 273 athn_pci_disable_aspm(struct athn_softc *sc) 274 { 275 struct athn_pci_softc *psc = (struct athn_pci_softc *)sc; 276 pcireg_t reg; 277 278 /* Disable PCIe Active State Power Management (ASPM). */ 279 reg = pci_conf_read(psc->sc_pc, psc->sc_tag, 280 psc->sc_cap_off + PCI_PCIE_LCSR); 281 reg &= ~(PCI_PCIE_LCSR_ASPM_L0S | PCI_PCIE_LCSR_ASPM_L1); 282 pci_conf_write(psc->sc_pc, psc->sc_tag, 283 psc->sc_cap_off + PCI_PCIE_LCSR, reg); 284 } 285