1 /* $NetBSD: if_athn_cardbus.c,v 1.2 2013/04/03 14:20:02 christos Exp $ */ 2 /* $OpenBSD: if_athn_cardbus.c,v 1.13 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 * CardBus front-end for Atheros 802.11a/g/n chipsets. 22 */ 23 24 #include <sys/cdefs.h> 25 __KERNEL_RCSID(0, "$NetBSD: if_athn_cardbus.c,v 1.2 2013/04/03 14:20:02 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 #include <dev/cardbus/cardbusvar.h> 58 59 /* 60 * PCI configuration space registers 61 */ 62 #define ATHN_PCI_MMBA PCI_BAR(0) /* memory mapped base */ 63 64 struct athn_cardbus_softc { 65 struct athn_softc csc_sc; 66 67 /* CardBus specific goo. */ 68 cardbus_devfunc_t csc_ct; 69 pcitag_t csc_tag; 70 void *csc_ih; 71 bus_space_tag_t csc_iot; 72 bus_space_handle_t csc_ioh; 73 bus_size_t csc_mapsz; 74 pcireg_t csc_bar_val; 75 }; 76 77 #define Static static 78 79 Static int athn_cardbus_match(device_t, cfdata_t, void *); 80 Static void athn_cardbus_attach(device_t, device_t, void *); 81 Static int athn_cardbus_detach(device_t, int); 82 83 CFATTACH_DECL_NEW(athn_cardbus, sizeof(struct athn_cardbus_softc), 84 athn_cardbus_match, athn_cardbus_attach, athn_cardbus_detach, NULL); 85 86 Static uint32_t athn_cardbus_read(struct athn_softc *, uint32_t); 87 Static bool athn_cardbus_resume(device_t, const pmf_qual_t *l); 88 Static void athn_cardbus_setup(struct athn_cardbus_softc *); 89 Static bool athn_cardbus_suspend(device_t, const pmf_qual_t *); 90 Static void athn_cardbus_write(struct athn_softc *, uint32_t, uint32_t); 91 Static void athn_cardbus_write_barrier(struct athn_softc *); 92 93 #ifdef openbsd_power_management 94 Static int athn_cardbus_enable(struct athn_softc *); 95 Static void athn_cardbus_disable(struct athn_softc *); 96 Static void athn_cardbus_power(struct athn_softc *, int); 97 #endif /* openbsd_power_management */ 98 99 Static int 100 athn_cardbus_match(device_t parent, cfdata_t match, void *aux) 101 { 102 static const struct { 103 pci_vendor_id_t vendor; 104 pci_product_id_t product; 105 } athn_cardbus_devices[] = { 106 { PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR5416 }, 107 { PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR5418 }, 108 { PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR9160 }, 109 { PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR9280 }, 110 { PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR9281 }, 111 { PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR9285 }, 112 { PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR2427 }, 113 { PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR9227 }, 114 { PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR9287 }, 115 { PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR9300 } 116 }; 117 struct cardbus_attach_args *ca = aux; 118 size_t i; 119 120 for (i = 0; i < __arraycount(athn_cardbus_devices); i++) { 121 if (PCI_VENDOR(ca->ca_id) == athn_cardbus_devices[i].vendor && 122 PCI_VENDOR(ca->ca_id) == athn_cardbus_devices[i].product) 123 return 1; 124 } 125 return 0; 126 } 127 128 Static void 129 athn_cardbus_attach(device_t parent, device_t self, void *aux) 130 { 131 struct athn_cardbus_softc *csc = device_private(self); 132 struct athn_softc *sc = &csc->csc_sc; 133 struct ieee80211com *ic = &sc->sc_ic; 134 struct cardbus_attach_args *ca = aux; 135 cardbus_devfunc_t ct = ca->ca_ct; 136 bus_addr_t base; 137 int error; 138 139 sc->sc_dmat = ca->ca_dmat; 140 csc->csc_ct = ct; 141 csc->csc_tag = ca->ca_tag; 142 143 aprint_normal("\n"); 144 aprint_naive("\n"); 145 146 #ifdef openbsd_power_management 147 /* Power management hooks. */ 148 sc->sc_enable = athn_cardbus_enable; 149 sc->sc_disable = athn_cardbus_disable; 150 sc->sc_power = athn_cardbus_power; 151 #endif 152 sc->sc_ops.read = athn_cardbus_read; 153 sc->sc_ops.write = athn_cardbus_write; 154 sc->sc_ops.write_barrier = athn_cardbus_write_barrier; 155 156 /* 157 * Map control/status registers. 158 */ 159 error = Cardbus_mapreg_map(ct, ATHN_PCI_MMBA, PCI_MAPREG_TYPE_MEM, 0, 160 &csc->csc_iot, &csc->csc_ioh, &base, &csc->csc_mapsz); 161 if (error != 0) { 162 aprint_error_dev(self, "unable to map device (%d)\n", error); 163 return; 164 } 165 csc->csc_bar_val = base | PCI_MAPREG_TYPE_MEM; 166 167 /* 168 * Set up the PCI configuration registers. 169 */ 170 athn_cardbus_setup(csc); 171 172 if (athn_attach(sc) != 0) { 173 Cardbus_mapreg_unmap(ct, ATHN_PCI_MMBA, csc->csc_iot, 174 csc->csc_ioh, csc->csc_mapsz); 175 return; 176 } 177 178 if (pmf_device_register(self, 179 athn_cardbus_suspend, athn_cardbus_resume)) { 180 pmf_class_network_register(self, &sc->sc_if); 181 pmf_device_suspend(self, &sc->sc_qual); 182 } else 183 aprint_error_dev(self, "couldn't establish power handler\n"); 184 185 // Cardbus_function_disable(ct); 186 187 ieee80211_announce(ic); 188 } 189 190 Static int 191 athn_cardbus_detach(device_t self, int flags) 192 { 193 struct athn_cardbus_softc *csc = device_private(self); 194 struct athn_softc *sc = &csc->csc_sc; 195 cardbus_devfunc_t ct = csc->csc_ct; 196 cardbus_chipset_tag_t cc = ct->ct_cc; 197 cardbus_function_tag_t cf = ct->ct_cf; 198 199 athn_detach(sc); 200 201 pmf_device_deregister(self); 202 203 /* Unhook the interrupt handler. */ 204 if (csc->csc_ih != NULL) 205 cardbus_intr_disestablish(cc, cf, csc->csc_ih); 206 207 /* Release bus space and close window. */ 208 Cardbus_mapreg_unmap(ct, ATHN_PCI_MMBA, csc->csc_iot, csc->csc_ioh, 209 csc->csc_mapsz); 210 211 return 0; 212 } 213 214 Static void 215 athn_cardbus_setup(struct athn_cardbus_softc *csc) 216 { 217 cardbus_devfunc_t ct = csc->csc_ct; 218 #ifdef unneeded /* XXX */ 219 pci_chipset_tag_t pc = csc->csc_pc; 220 cardbus_chipset_tag_t cc = ct->ct_cc; 221 cardbus_function_tag_t cf = ct->ct_cf; 222 #endif 223 pcireg_t reg; 224 int rc; 225 226 if ((rc = cardbus_set_powerstate(ct, csc->csc_tag, PCI_PWR_D0)) != 0) 227 aprint_debug("%s: cardbus_set_powerstate %d\n", __func__, rc); 228 229 /* Program the BAR. */ 230 Cardbus_conf_write(ct, csc->csc_tag, ATHN_PCI_MMBA, csc->csc_bar_val); 231 232 #ifdef unneeded /* XXX */ 233 /* Make sure the right access type is on the cardbus bridge. */ 234 (*cf->cardbus_ctrl)(cc, CARDBUS_MEM_ENABLE); 235 (*cf->cardbus_ctrl)(cc, CARDBUS_BM_ENABLE); 236 #endif 237 /* Enable the appropriate bits in the PCI CSR. */ 238 reg = Cardbus_conf_read(ct, csc->csc_tag, PCI_COMMAND_STATUS_REG); 239 reg |= PCI_COMMAND_MASTER_ENABLE | PCI_COMMAND_MEM_ENABLE; 240 Cardbus_conf_write(ct, csc->csc_tag, PCI_COMMAND_STATUS_REG, reg); 241 242 /* 243 * Noone knows why this shit is necessary but there are claims that 244 * not doing this may cause very frequent PCI FATAL interrupts from 245 * the card: http://bugzilla.kernel.org/show_bug.cgi?id=13483 246 */ 247 reg = Cardbus_conf_read(ct, csc->csc_tag, 0x40); 248 if (reg & 0xff00) 249 Cardbus_conf_write(ct, csc->csc_tag, 0x40, reg & ~0xff00); 250 251 /* Change latency timer; default value yields poor results. */ 252 reg = Cardbus_conf_read(ct, csc->csc_tag, PCI_BHLC_REG); 253 reg &= ~(PCI_LATTIMER_MASK << PCI_LATTIMER_SHIFT); 254 reg |= 168 << PCI_LATTIMER_SHIFT; 255 Cardbus_conf_write(ct, csc->csc_tag, PCI_BHLC_REG, reg); 256 } 257 258 Static uint32_t 259 athn_cardbus_read(struct athn_softc *sc, uint32_t addr) 260 { 261 struct athn_cardbus_softc *csc = (struct athn_cardbus_softc *)sc; 262 263 return bus_space_read_4(csc->csc_iot, csc->csc_ioh, addr); 264 } 265 266 Static void 267 athn_cardbus_write(struct athn_softc *sc, uint32_t addr, uint32_t val) 268 { 269 struct athn_cardbus_softc *csc = (struct athn_cardbus_softc *)sc; 270 271 bus_space_write_4(csc->csc_iot, csc->csc_ioh, addr, val); 272 } 273 274 Static void 275 athn_cardbus_write_barrier(struct athn_softc *sc) 276 { 277 struct athn_cardbus_softc *csc = (struct athn_cardbus_softc *)sc; 278 279 bus_space_barrier(csc->csc_iot, csc->csc_ioh, 0, csc->csc_mapsz, 280 BUS_SPACE_BARRIER_WRITE); 281 } 282 283 Static bool 284 athn_cardbus_suspend(device_t self, const pmf_qual_t *qual) 285 { 286 struct athn_cardbus_softc *csc = device_private(self); 287 288 athn_suspend(&csc->csc_sc); 289 if (csc->csc_ih != NULL) { 290 Cardbus_intr_disestablish(csc->csc_ct, csc->csc_ih); 291 csc->csc_ih = NULL; 292 } 293 return true; 294 } 295 296 Static bool 297 athn_cardbus_resume(device_t self, const pmf_qual_t *qual) 298 { 299 struct athn_cardbus_softc *csc = device_private(self); 300 301 csc->csc_ih = Cardbus_intr_establish(csc->csc_ct, IPL_NET, athn_intr, 302 &csc->csc_sc); 303 304 if (csc->csc_ih == NULL) { 305 aprint_error_dev(self, 306 "unable to establish interrupt\n"); 307 return false; 308 } 309 return athn_resume(&csc->csc_sc); 310 } 311 312 /************************************************************************ 313 * XXX: presumably the pmf_* stuff handles this. 314 */ 315 #ifdef openbsd_power_management 316 Static int 317 athn_cardbus_enable(struct athn_softc *sc) 318 { 319 struct athn_cardbus_softc *csc = (struct athn_cardbus_softc *)sc; 320 cardbus_devfunc_t ct = csc->csc_ct; 321 cardbus_chipset_tag_t cc = ct->ct_cc; 322 cardbus_function_tag_t cf = ct->ct_cf; 323 324 /* Power on the socket. */ 325 Cardbus_function_enable(ct); 326 327 /* Setup the PCI configuration registers. */ 328 athn_cardbus_setup(csc); 329 330 /* Map and establish the interrupt handler. */ 331 csc->csc_ih = cardbus_intr_establish(cc, cf, IPL_NET, athn_intr, sc); 332 if (csc->csc_ih == NULL) { 333 printf("%s: could not establish interrupt at %d\n", 334 device_xname(sc->sc_dev), csc->csc_intrline); 335 Cardbus_function_disable(ct); 336 return 1; 337 } 338 return 0; 339 } 340 341 Static void 342 athn_cardbus_disable(struct athn_softc *sc) 343 { 344 struct athn_cardbus_softc *csc = (struct athn_cardbus_softc *)sc; 345 cardbus_devfunc_t ct = csc->csc_ct; 346 cardbus_chipset_tag_t cc = ct->ct_cc; 347 cardbus_function_tag_t cf = ct->ct_cf; 348 349 /* Unhook the interrupt handler. */ 350 cardbus_intr_disestablish(cc, cf, csc->csc_ih); 351 csc->csc_ih = NULL; 352 353 /* Power down the socket. */ 354 Cardbus_function_disable(ct); 355 } 356 357 Static void 358 athn_cardbus_power(struct athn_softc *sc, int why) 359 { 360 struct athn_cardbus_softc *csc = (struct athn_cardbus_softc *)sc; 361 362 if (why == DVACT_RESUME) { 363 /* Restore the PCI configuration registers. */ 364 athn_cardbus_setup(csc); 365 } 366 } 367 #endif /* openbsd_power_management */ 368