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