1/* $NetBSD: if_athn_pci.c,v 1.14 2022/09/25 17:52:25 thorpej 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.14 2022/09/25 17:52:25 thorpej 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/callout.h> 36#include <sys/device.h> 37 38#include <sys/bus.h> 39#include <sys/intr.h> 40 41#include <net/if.h> 42#include <net/if_ether.h> 43#include <net/if_media.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#define ATHN_PCI_MMBA PCI_BAR(0) /* memory mapped base */ 61 62struct athn_pci_softc { 63 struct athn_softc psc_sc; 64 65 /* PCI specific goo. */ 66 pci_chipset_tag_t psc_pc; 67 pcitag_t psc_tag; 68 pci_intr_handle_t psc_pih; 69 void *psc_ih; 70 bus_space_tag_t psc_iot; 71 bus_space_handle_t psc_ioh; 72 bus_size_t psc_mapsz; 73 int psc_cap_off; 74}; 75 76#define Static static 77 78Static int athn_pci_match(device_t, cfdata_t, void *); 79Static void athn_pci_attach(device_t, device_t, void *); 80Static int athn_pci_detach(device_t, int); 81Static int athn_pci_activate(device_t, enum devact); 82 83CFATTACH_DECL_NEW(athn_pci, sizeof(struct athn_pci_softc), athn_pci_match, 84 athn_pci_attach, athn_pci_detach, athn_pci_activate); 85 86Static bool athn_pci_resume(device_t, const pmf_qual_t *); 87Static bool athn_pci_suspend(device_t, const pmf_qual_t *); 88Static uint32_t athn_pci_read(struct athn_softc *, uint32_t); 89Static void athn_pci_write(struct athn_softc *, uint32_t, uint32_t); 90Static void athn_pci_write_barrier(struct athn_softc *); 91Static void athn_pci_disable_aspm(struct athn_softc *); 92 93Static int 94athn_pci_match(device_t parent, cfdata_t match, void *aux) 95{ 96 static const struct { 97 pci_vendor_id_t apd_vendor; 98 pci_product_id_t apd_product; 99 } athn_pci_devices[] = { 100 { PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR5416 }, 101 { PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR5418 }, 102 { PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR9160 }, 103 { PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR9280 }, 104 { PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR9281 }, 105 { PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR9285 }, 106 { PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR2427 }, 107 { PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR9227 }, 108 { PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR9287 }, 109 { PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR9300 } 110 }; 111 struct pci_attach_args *pa = aux; 112 size_t i; 113 114 for (i = 0; i < __arraycount(athn_pci_devices); i++) { 115 if (PCI_VENDOR(pa->pa_id) == athn_pci_devices[i].apd_vendor && 116 PCI_PRODUCT(pa->pa_id) == athn_pci_devices[i].apd_product) 117 /* 118 * Match better than 1, we prefer this driver 119 * over ath(4) 120 */ 121 return 10; 122 } 123 return 0; 124} 125 126Static void 127athn_pci_attach(device_t parent, device_t self, void *aux) 128{ 129 struct athn_pci_softc *psc = device_private(self); 130 struct athn_softc *sc = &psc->psc_sc; 131 struct ieee80211com *ic = &sc->sc_ic; 132 struct pci_attach_args *pa = aux; 133 const char *intrstr; 134 pcireg_t memtype, reg; 135 pci_product_id_t subsysid; 136 int error; 137 char intrbuf[PCI_INTRSTR_LEN]; 138 139 sc->sc_dev = self; 140 sc->sc_dmat = pa->pa_dmat; 141 psc->psc_pc = pa->pa_pc; 142 psc->psc_tag = pa->pa_tag; 143 144 sc->sc_ops.read = athn_pci_read; 145 sc->sc_ops.write = athn_pci_write; 146 sc->sc_ops.write_barrier = athn_pci_write_barrier; 147 148 /* 149 * Get the offset of the PCI Express Capability Structure in PCI 150 * Configuration Space (Linux hardcodes it as 0x60.) 151 */ 152 error = pci_get_capability(pa->pa_pc, pa->pa_tag, PCI_CAP_PCIEXPRESS, 153 &psc->psc_cap_off, NULL); 154 if (error != 0) { /* Found. */ 155 sc->sc_disable_aspm = athn_pci_disable_aspm; 156 sc->sc_flags |= ATHN_FLAG_PCIE; 157 } 158 /* 159 * Noone knows why this shit is necessary but there are claims that 160 * not doing this may cause very frequent PCI FATAL interrupts from 161 * the card: http://bugzilla.kernel.org/show_bug.cgi?id=13483 162 */ 163 reg = pci_conf_read(pa->pa_pc, pa->pa_tag, 0x40); 164 if (reg & 0xff00) 165 pci_conf_write(pa->pa_pc, pa->pa_tag, 0x40, reg & ~0xff00); 166 167 /* Change latency timer; default value yields poor results. */ 168 reg = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_BHLC_REG); 169 reg &= ~(PCI_LATTIMER_MASK << PCI_LATTIMER_SHIFT); 170 reg |= 168 << PCI_LATTIMER_SHIFT; 171 pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_BHLC_REG, reg); 172 173 /* Determine if bluetooth is also supported (combo chip.) */ 174 reg = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_SUBSYS_ID_REG); 175 subsysid = PCI_PRODUCT(reg); 176 if (subsysid == PCI_SUBSYSID_ATHEROS_COEX3WIRE_SA || 177 subsysid == PCI_SUBSYSID_ATHEROS_COEX3WIRE_DA) 178 sc->sc_flags |= ATHN_FLAG_BTCOEX3WIRE; 179 else if (subsysid == PCI_SUBSYSID_ATHEROS_COEX2WIRE) 180 sc->sc_flags |= ATHN_FLAG_BTCOEX2WIRE; 181 182 /* 183 * Setup memory-mapping of PCI registers. 184 */ 185 memtype = pci_mapreg_type(pa->pa_pc, pa->pa_tag, ATHN_PCI_MMBA); 186 if (memtype != PCI_MAPREG_TYPE_MEM && 187 memtype != PCI_MAPREG_MEM_TYPE_64BIT) { 188 aprint_error_dev(self, "bad pci register type %d\n", 189 (int)memtype); 190 goto fail; 191 } 192 error = pci_mapreg_map(pa, ATHN_PCI_MMBA, memtype, 0, &psc->psc_iot, 193 &psc->psc_ioh, NULL, &psc->psc_mapsz); 194 if (error != 0) { 195 aprint_error_dev(self, "cannot map register space\n"); 196 goto fail; 197 } 198 199 /* 200 * Arrange interrupt line. 201 */ 202 if (pci_intr_map(pa, &psc->psc_pih) != 0) { 203 aprint_error_dev(self, "couldn't map interrupt\n"); 204 goto fail1; 205 } 206 207 intrstr = pci_intr_string(psc->psc_pc, psc->psc_pih, intrbuf, sizeof(intrbuf)); 208 psc->psc_ih = pci_intr_establish_xname(psc->psc_pc, psc->psc_pih, 209 IPL_NET, athn_intr, sc, device_xname(self)); 210 if (psc->psc_ih == NULL) { 211 aprint_error_dev(self, "couldn't map interrupt\n"); 212 goto fail1; 213 } 214 215 ic->ic_ifp = &sc->sc_if; 216 if (athn_attach(sc) != 0) 217 goto fail2; 218 219 aprint_verbose_dev(self, "interrupting at %s\n", intrstr); 220 221 if (pmf_device_register(self, athn_pci_suspend, athn_pci_resume)) { 222 pmf_class_network_register(self, &sc->sc_if); 223 pmf_device_suspend(self, &sc->sc_qual); 224 } 225 else 226 aprint_error_dev(self, "couldn't establish power handler\n"); 227 228 ieee80211_announce(ic); 229 return; 230 231 fail2: 232 pci_intr_disestablish(psc->psc_pc, psc->psc_ih); 233 psc->psc_ih = NULL; 234 fail1: 235 bus_space_unmap(psc->psc_iot, psc->psc_ioh, psc->psc_mapsz); 236 psc->psc_mapsz = 0; 237 fail: 238 return; 239} 240 241Static int 242athn_pci_detach(device_t self, int flags) 243{ 244 struct athn_pci_softc *psc = device_private(self); 245 struct athn_softc *sc = &psc->psc_sc; 246 247 if (psc->psc_ih != NULL) { 248 athn_detach(sc); 249 pci_intr_disestablish(psc->psc_pc, psc->psc_ih); 250 psc->psc_ih = NULL; 251 } 252 if (psc->psc_mapsz > 0) { 253 bus_space_unmap(psc->psc_iot, psc->psc_ioh, psc->psc_mapsz); 254 psc->psc_mapsz = 0; 255 } 256 return 0; 257} 258 259Static int 260athn_pci_activate(device_t self, enum devact act) 261{ 262 struct athn_pci_softc *psc = device_private(self); 263 struct athn_softc *sc = &psc->psc_sc; 264 265 switch (act) { 266 case DVACT_DEACTIVATE: 267 if_deactivate(sc->sc_ic.ic_ifp); 268 break; 269 } 270 return 0; 271} 272 273Static bool 274athn_pci_suspend(device_t self, const pmf_qual_t *qual) 275{ 276 struct athn_pci_softc *psc = device_private(self); 277 struct athn_softc *sc = &psc->psc_sc; 278 279 athn_suspend(sc); 280 if (psc->psc_ih != NULL) { 281 pci_intr_disestablish(psc->psc_pc, psc->psc_ih); 282 psc->psc_ih = NULL; 283 } 284 return true; 285} 286 287Static bool 288athn_pci_resume(device_t self, const pmf_qual_t *qual) 289{ 290 struct athn_pci_softc *psc = device_private(self); 291 struct athn_softc *sc = &psc->psc_sc; 292 pcireg_t reg; 293 294 /* 295 * XXX: see comment in athn_attach(). 296 */ 297 reg = pci_conf_read(psc->psc_pc, psc->psc_tag, 0x40); 298 if (reg & 0xff00) 299 pci_conf_write(psc->psc_pc, psc->psc_tag, 0x40, reg & ~0xff00); 300 301 /* XXX re-establishing interrupt shouldn't be needed */ 302 psc->psc_ih = pci_intr_establish_xname(psc->psc_pc, psc->psc_pih, 303 IPL_NET, athn_intr, sc, device_xname(self)); 304 if (psc->psc_ih == NULL) { 305 aprint_error_dev(self, "couldn't map interrupt\n"); 306 return false; 307 } 308 return athn_resume(sc); 309} 310 311Static uint32_t 312athn_pci_read(struct athn_softc *sc, uint32_t addr) 313{ 314 struct athn_pci_softc *psc = (struct athn_pci_softc *)sc; 315 316 return bus_space_read_4(psc->psc_iot, psc->psc_ioh, addr); 317} 318 319Static void 320athn_pci_write(struct athn_softc *sc, uint32_t addr, uint32_t val) 321{ 322 struct athn_pci_softc *psc = (struct athn_pci_softc *)sc; 323 324 bus_space_write_4(psc->psc_iot, psc->psc_ioh, addr, val); 325} 326 327Static void 328athn_pci_write_barrier(struct athn_softc *sc) 329{ 330 struct athn_pci_softc *psc = (struct athn_pci_softc *)sc; 331 332 bus_space_barrier(psc->psc_iot, psc->psc_ioh, 0, psc->psc_mapsz, 333 BUS_SPACE_BARRIER_WRITE); 334} 335 336Static void 337athn_pci_disable_aspm(struct athn_softc *sc) 338{ 339 struct athn_pci_softc *psc = (struct athn_pci_softc *)sc; 340 pcireg_t reg; 341 342 /* Disable PCIe Active State Power Management (ASPM). */ 343 reg = pci_conf_read(psc->psc_pc, psc->psc_tag, 344 psc->psc_cap_off + PCIE_LCSR); 345 reg &= ~(PCIE_LCSR_ASPM_L0S | PCIE_LCSR_ASPM_L1); 346 pci_conf_write(psc->psc_pc, psc->psc_tag, 347 psc->psc_cap_off + PCIE_LCSR, reg); 348} 349