1 /*- 2 * Copyright (c) 2007 Marvell Semiconductor, Inc. 3 * Copyright (c) 2007 Sam Leffler, Errno Consulting 4 * Copyright (c) 2008 Weongyo Jeong <weongyo@freebsd.org> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer, 12 * without modification. 13 * 2. Redistributions in binary form must reproduce at minimum a disclaimer 14 * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any 15 * redistribution must be conditioned upon including a substantially 16 * similar Disclaimer requirement for further binary redistribution. 17 * 18 * NO WARRANTY 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY 22 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 23 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, 24 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 27 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 29 * THE POSSIBILITY OF SUCH DAMAGES. 30 */ 31 32 #include <sys/cdefs.h> 33 #ifdef __FreeBSD__ 34 __FBSDID("$FreeBSD$"); 35 #endif 36 37 /* 38 * PCI front-end for the Marvell 88W8335 Wireless LAN controller driver. 39 */ 40 41 #include <sys/param.h> 42 #include <sys/kernel.h> 43 #include <sys/malloc.h> 44 #include <sys/module.h> 45 #include <sys/socket.h> 46 #include <sys/sysctl.h> 47 48 #include <machine/bus.h> 49 #include <sys/bus.h> 50 #include <sys/rman.h> 51 52 #include <net/if.h> 53 #include <net/if_var.h> 54 #include <net/if_media.h> 55 #include <net/ethernet.h> 56 57 #include <net80211/ieee80211_var.h> 58 59 #include <dev/malo/if_malo.h> 60 61 #include <dev/pci/pcivar.h> 62 #include <dev/pci/pcireg.h> 63 64 /* 65 * PCI glue. 66 */ 67 68 #define MALO_RESOURCE_MAX 2 69 #define MALO_MSI_MESSAGES 1 70 71 struct malo_pci_softc { 72 struct malo_softc malo_sc; 73 struct resource_spec *malo_mem_spec; 74 struct resource *malo_res_mem[MALO_RESOURCE_MAX]; 75 struct resource_spec *malo_irq_spec; 76 struct resource *malo_res_irq[MALO_MSI_MESSAGES]; 77 void *malo_intrhand[MALO_MSI_MESSAGES]; 78 int malo_msi; 79 }; 80 81 /* 82 * Tunable variables. 83 */ 84 SYSCTL_DECL(_hw_malo); 85 static SYSCTL_NODE(_hw_malo, OID_AUTO, pci, CTLFLAG_RD, 0, 86 "Marvell 88W8335 driver PCI parameters"); 87 88 static int msi_disable = 0; /* MSI disabled */ 89 SYSCTL_INT(_hw_malo_pci, OID_AUTO, msi_disable, CTLFLAG_RW, &msi_disable, 90 0, "MSI disabled"); 91 TUNABLE_INT("hw.malo.pci.msi_disable", &msi_disable); 92 93 /* 94 * Devices supported by this driver. 95 */ 96 #define VENDORID_MARVELL 0X11AB 97 #define DEVICEID_MRVL_88W8310 0X1FA7 98 #define DEVICEID_MRVL_88W8335R1 0X1FAA 99 #define DEVICEID_MRVL_88W8335R2 0X1FAB 100 101 static struct malo_product { 102 uint16_t mp_vendorid; 103 uint16_t mp_deviceid; 104 const char *mp_name; 105 } malo_products[] = { 106 { VENDORID_MARVELL, DEVICEID_MRVL_88W8310, 107 "Marvell Libertas 88W8310 802.11g Wireless Adapter" }, 108 { VENDORID_MARVELL, DEVICEID_MRVL_88W8335R1, 109 "Marvell Libertas 88W8335 802.11g Wireless Adapter" }, 110 { VENDORID_MARVELL, DEVICEID_MRVL_88W8335R2, 111 "Marvell Libertas 88W8335 802.11g Wireless Adapter" } 112 }; 113 114 static struct resource_spec malo_res_spec_mem[] = { 115 { SYS_RES_MEMORY, PCIR_BAR(0), RF_ACTIVE }, 116 { SYS_RES_MEMORY, PCIR_BAR(1), RF_ACTIVE }, 117 { -1, 0, 0 } 118 }; 119 120 static struct resource_spec malo_res_spec_legacy[] = { 121 { SYS_RES_IRQ, 0, RF_ACTIVE | RF_SHAREABLE }, 122 { -1, 0, 0 } 123 }; 124 125 static struct resource_spec malo_res_spec_msi[] = { 126 { SYS_RES_IRQ, 1, RF_ACTIVE }, 127 { -1, 0, 0 } 128 }; 129 130 static int malo_pci_detach(device_t); 131 132 static int 133 malo_pci_probe(device_t dev) 134 { 135 #define N(a) (sizeof(a) / sizeof((a)[0])) 136 struct malo_product *mp; 137 uint16_t vendor, devid; 138 int i; 139 140 vendor = pci_get_vendor(dev); 141 devid = pci_get_device(dev); 142 mp = malo_products; 143 144 for (i = 0; i < N(malo_products); i++, mp++) { 145 if (vendor == mp->mp_vendorid && devid == mp->mp_deviceid) { 146 device_set_desc(dev, mp->mp_name); 147 return (BUS_PROBE_DEFAULT); 148 } 149 } 150 151 return (ENXIO); 152 #undef N 153 } 154 155 static int 156 malo_pci_attach(device_t dev) 157 { 158 int error = ENXIO, i, msic, reg; 159 struct malo_pci_softc *psc = device_get_softc(dev); 160 struct malo_softc *sc = &psc->malo_sc; 161 162 sc->malo_dev = dev; 163 164 pci_enable_busmaster(dev); 165 166 /* 167 * Setup memory-mapping of PCI registers. 168 */ 169 psc->malo_mem_spec = malo_res_spec_mem; 170 error = bus_alloc_resources(dev, psc->malo_mem_spec, psc->malo_res_mem); 171 if (error) { 172 device_printf(dev, "couldn't allocate memory resources\n"); 173 return (ENXIO); 174 } 175 176 /* 177 * Arrange and allocate interrupt line. 178 */ 179 sc->malo_invalid = 1; 180 181 if (pci_find_cap(dev, PCIY_EXPRESS, ®) == 0) { 182 msic = pci_msi_count(dev); 183 if (bootverbose) 184 device_printf(dev, "MSI count : %d\n", msic); 185 } else 186 msic = 0; 187 188 psc->malo_irq_spec = malo_res_spec_legacy; 189 if (msic == MALO_MSI_MESSAGES && msi_disable == 0) { 190 if (pci_alloc_msi(dev, &msic) == 0) { 191 if (msic == MALO_MSI_MESSAGES) { 192 device_printf(dev, "Using %d MSI messages\n", 193 msic); 194 psc->malo_irq_spec = malo_res_spec_msi; 195 psc->malo_msi = 1; 196 } else 197 pci_release_msi(dev); 198 } 199 } 200 201 error = bus_alloc_resources(dev, psc->malo_irq_spec, psc->malo_res_irq); 202 if (error) { 203 device_printf(dev, "couldn't allocate IRQ resources\n"); 204 goto bad; 205 } 206 207 if (psc->malo_msi == 0) 208 error = bus_setup_intr(dev, psc->malo_res_irq[0], 209 INTR_TYPE_NET | INTR_MPSAFE, malo_intr, NULL, sc, 210 &psc->malo_intrhand[0]); 211 else { 212 for (i = 0; i < MALO_MSI_MESSAGES; i++) { 213 error = bus_setup_intr(dev, psc->malo_res_irq[i], 214 INTR_TYPE_NET | INTR_MPSAFE, malo_intr, NULL, sc, 215 &psc->malo_intrhand[i]); 216 if (error != 0) 217 break; 218 } 219 } 220 221 /* 222 * Setup DMA descriptor area. 223 */ 224 if (bus_dma_tag_create(bus_get_dma_tag(dev), /* parent */ 225 1, 0, /* alignment, bounds */ 226 BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 227 BUS_SPACE_MAXADDR, /* highaddr */ 228 NULL, NULL, /* filter, filterarg */ 229 BUS_SPACE_MAXADDR, /* maxsize */ 230 0, /* nsegments */ 231 BUS_SPACE_MAXADDR, /* maxsegsize */ 232 0, /* flags */ 233 NULL, /* lockfunc */ 234 NULL, /* lockarg */ 235 &sc->malo_dmat)) { 236 device_printf(dev, "cannot allocate DMA tag\n"); 237 goto bad1; 238 } 239 240 sc->malo_io0t = rman_get_bustag(psc->malo_res_mem[0]); 241 sc->malo_io0h = rman_get_bushandle(psc->malo_res_mem[0]); 242 sc->malo_io1t = rman_get_bustag(psc->malo_res_mem[1]); 243 sc->malo_io1h = rman_get_bushandle(psc->malo_res_mem[1]); 244 245 error = malo_attach(pci_get_device(dev), sc); 246 247 if (error != 0) 248 goto bad2; 249 250 return (error); 251 252 bad2: 253 bus_dma_tag_destroy(sc->malo_dmat); 254 bad1: 255 if (psc->malo_msi == 0) 256 bus_teardown_intr(dev, psc->malo_res_irq[0], 257 psc->malo_intrhand[0]); 258 else { 259 for (i = 0; i < MALO_MSI_MESSAGES; i++) 260 bus_teardown_intr(dev, psc->malo_res_irq[i], 261 psc->malo_intrhand[i]); 262 } 263 bus_release_resources(dev, psc->malo_irq_spec, psc->malo_res_irq); 264 bad: 265 if (psc->malo_msi != 0) 266 pci_release_msi(dev); 267 bus_release_resources(dev, psc->malo_mem_spec, psc->malo_res_mem); 268 269 return (error); 270 } 271 272 static int 273 malo_pci_detach(device_t dev) 274 { 275 int i; 276 struct malo_pci_softc *psc = device_get_softc(dev); 277 struct malo_softc *sc = &psc->malo_sc; 278 279 /* check if device was removed */ 280 sc->malo_invalid = !bus_child_present(dev); 281 282 malo_detach(sc); 283 284 bus_generic_detach(dev); 285 286 if (psc->malo_msi == 0) 287 bus_teardown_intr(dev, psc->malo_res_irq[0], 288 psc->malo_intrhand[0]); 289 else { 290 for (i = 0; i < MALO_MSI_MESSAGES; i++) 291 bus_teardown_intr(dev, psc->malo_res_irq[i], 292 psc->malo_intrhand[i]); 293 294 pci_release_msi(dev); 295 } 296 297 bus_release_resources(dev, psc->malo_irq_spec, psc->malo_res_irq); 298 bus_dma_tag_destroy(sc->malo_dmat); 299 bus_release_resources(dev, psc->malo_mem_spec, psc->malo_res_mem); 300 301 return (0); 302 } 303 304 static int 305 malo_pci_shutdown(device_t dev) 306 { 307 struct malo_pci_softc *psc = device_get_softc(dev); 308 309 malo_shutdown(&psc->malo_sc); 310 311 return (0); 312 } 313 314 static int 315 malo_pci_suspend(device_t dev) 316 { 317 struct malo_pci_softc *psc = device_get_softc(dev); 318 319 malo_suspend(&psc->malo_sc); 320 321 return (0); 322 } 323 324 static int 325 malo_pci_resume(device_t dev) 326 { 327 struct malo_pci_softc *psc = device_get_softc(dev); 328 329 malo_resume(&psc->malo_sc); 330 331 return (0); 332 } 333 334 static device_method_t malo_pci_methods[] = { 335 /* Device interface */ 336 DEVMETHOD(device_probe, malo_pci_probe), 337 DEVMETHOD(device_attach, malo_pci_attach), 338 DEVMETHOD(device_detach, malo_pci_detach), 339 DEVMETHOD(device_shutdown, malo_pci_shutdown), 340 DEVMETHOD(device_suspend, malo_pci_suspend), 341 DEVMETHOD(device_resume, malo_pci_resume), 342 { 0,0 } 343 }; 344 345 static driver_t malo_pci_driver = { 346 "malo", 347 malo_pci_methods, 348 sizeof(struct malo_pci_softc) 349 }; 350 351 static devclass_t malo_devclass; 352 DRIVER_MODULE(malo, pci, malo_pci_driver, malo_devclass, 0, 0); 353 MODULE_VERSION(malo, 1); 354 MODULE_DEPEND(malo, wlan, 1, 1, 1); /* 802.11 media layer */ 355 MODULE_DEPEND(malo, firmware, 1, 1, 1); 356