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