| 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 | |