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/module.h> 44 #include <sys/socket.h> 45 #include <sys/sysctl.h> 46 47 #include <machine/bus.h> 48 #include <sys/bus.h> 49 #include <sys/rman.h> 50 51 #include <net/if.h> 52 #include <net/if_media.h> 53 54 #include <net80211/ieee80211_var.h> 55 56 #include <dev/malo/if_malo.h> 57 58 #include <dev/pci/pcivar.h> 59 #include <dev/pci/pcireg.h> 60 61 /* 62 * PCI glue. 63 */ 64 65 #define MALO_RESOURCE_MAX 2 66 #define MALO_MSI_MESSAGES 1 67 68 struct malo_pci_softc { 69 struct malo_softc malo_sc; 70 struct resource_spec *malo_mem_spec; 71 struct resource *malo_res_mem[MALO_RESOURCE_MAX]; 72 struct resource_spec *malo_irq_spec; 73 struct resource *malo_res_irq[MALO_MSI_MESSAGES]; 74 void *malo_intrhand[MALO_MSI_MESSAGES]; 75 int malo_msi; 76 }; 77 78 /* 79 * Tunable variables. 80 */ 81 SYSCTL_DECL(_hw_malo); 82 SYSCTL_NODE(_hw_malo, OID_AUTO, pci, CTLFLAG_RD, 0, 83 "Marvell 88W8335 driver PCI parameters"); 84 85 static int msi_disable = 0; /* MSI disabled */ 86 SYSCTL_INT(_hw_malo_pci, OID_AUTO, msi_disable, CTLFLAG_RW, &msi_disable, 87 0, "MSI disabled"); 88 TUNABLE_INT("hw.malo.pci.msi_disable", &msi_disable); 89 90 /* 91 * Devices supported by this driver. 92 */ 93 #define VENDORID_MARVELL 0X11AB 94 #define DEVICEID_MRVL_88W8310 0X1FA7 95 #define DEVICEID_MRVL_88W8335R1 0X1FAA 96 #define DEVICEID_MRVL_88W8335R2 0X1FAB 97 98 static struct malo_product { 99 uint16_t mp_vendorid; 100 uint16_t mp_deviceid; 101 const char *mp_name; 102 } malo_products[] = { 103 { VENDORID_MARVELL, DEVICEID_MRVL_88W8310, 104 "Marvell Libertas 88W8310 802.11g Wireless Adapter" }, 105 { VENDORID_MARVELL, DEVICEID_MRVL_88W8335R1, 106 "Marvell Libertas 88W8335 802.11g Wireless Adapter" }, 107 { VENDORID_MARVELL, DEVICEID_MRVL_88W8335R2, 108 "Marvell Libertas 88W8335 802.11g Wireless Adapter" } 109 }; 110 111 static struct resource_spec malo_res_spec_mem[] = { 112 { SYS_RES_MEMORY, PCIR_BAR(0), RF_ACTIVE }, 113 { SYS_RES_MEMORY, PCIR_BAR(1), RF_ACTIVE }, 114 { -1, 0, 0 } 115 }; 116 117 static struct resource_spec malo_res_spec_legacy[] = { 118 { SYS_RES_IRQ, 0, RF_ACTIVE | RF_SHAREABLE }, 119 { -1, 0, 0 } 120 }; 121 122 static struct resource_spec malo_res_spec_msi[] = { 123 { SYS_RES_IRQ, 1, RF_ACTIVE }, 124 { -1, 0, 0 } 125 }; 126 127 static int malo_pci_detach(device_t); 128 129 static int 130 malo_pci_probe(device_t dev) 131 { 132 #define N(a) (sizeof(a) / sizeof((a)[0])) 133 struct malo_product *mp; 134 uint16_t vendor, devid; 135 int i; 136 137 vendor = pci_get_vendor(dev); 138 devid = pci_get_device(dev); 139 mp = malo_products; 140 141 for (i = 0; i < N(malo_products); i++, mp++) { 142 if (vendor == mp->mp_vendorid && devid == mp->mp_deviceid) { 143 device_set_desc(dev, mp->mp_name); 144 return (BUS_PROBE_DEFAULT); 145 } 146 } 147 148 return (ENXIO); 149 #undef N 150 } 151 152 static int 153 malo_pci_setup(device_t dev) 154 { 155 156 /* 157 * Enable memory mapping and bus mastering. 158 */ 159 if (pci_enable_busmaster(dev) != 0) 160 return -1; 161 if (pci_enable_io(dev, SYS_RES_MEMORY) != 0) 162 return -1; 163 164 return 0; 165 } 166 167 static int 168 malo_pci_attach(device_t dev) 169 { 170 int error = ENXIO, i, msic, reg; 171 struct malo_pci_softc *psc = device_get_softc(dev); 172 struct malo_softc *sc = &psc->malo_sc; 173 174 sc->malo_dev = dev; 175 176 /* 177 * Enable memory mapping and bus mastering. 178 */ 179 if (malo_pci_setup(dev)) 180 return (ENXIO); 181 182 /* 183 * Setup memory-mapping of PCI registers. 184 */ 185 psc->malo_mem_spec = malo_res_spec_mem; 186 error = bus_alloc_resources(dev, psc->malo_mem_spec, psc->malo_res_mem); 187 if (error) { 188 device_printf(dev, "couldn't allocate memory resources\n"); 189 return (ENXIO); 190 } 191 192 /* 193 * Arrange and allocate interrupt line. 194 */ 195 sc->malo_invalid = 1; 196 197 if (pci_find_extcap(dev, PCIY_EXPRESS, ®) == 0) { 198 msic = pci_msi_count(dev); 199 if (bootverbose) 200 device_printf(dev, "MSI count : %d\n", msic); 201 } else 202 msic = 0; 203 204 psc->malo_irq_spec = malo_res_spec_legacy; 205 if (msic == MALO_MSI_MESSAGES && msi_disable == 0) { 206 if (pci_alloc_msi(dev, &msic) == 0) { 207 if (msic == MALO_MSI_MESSAGES) { 208 device_printf(dev, "Using %d MSI messages\n", 209 msic); 210 psc->malo_irq_spec = malo_res_spec_msi; 211 psc->malo_msi = 1; 212 } else 213 pci_release_msi(dev); 214 } 215 } 216 217 error = bus_alloc_resources(dev, psc->malo_irq_spec, psc->malo_res_irq); 218 if (error) { 219 device_printf(dev, "couldn't allocate IRQ resources\n"); 220 goto bad; 221 } 222 223 if (psc->malo_msi == 0) 224 error = bus_setup_intr(dev, psc->malo_res_irq[0], 225 INTR_TYPE_NET | INTR_MPSAFE, malo_intr, NULL, sc, 226 &psc->malo_intrhand[0]); 227 else { 228 for (i = 0; i < MALO_MSI_MESSAGES; i++) { 229 error = bus_setup_intr(dev, psc->malo_res_irq[i], 230 INTR_TYPE_NET | INTR_MPSAFE, malo_intr, NULL, sc, 231 &psc->malo_intrhand[i]); 232 if (error != 0) 233 break; 234 } 235 } 236 237 /* 238 * Setup DMA descriptor area. 239 */ 240 if (bus_dma_tag_create(bus_get_dma_tag(dev), /* parent */ 241 1, 0, /* alignment, bounds */ 242 BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 243 BUS_SPACE_MAXADDR, /* highaddr */ 244 NULL, NULL, /* filter, filterarg */ 245 BUS_SPACE_MAXADDR, /* maxsize */ 246 0, /* nsegments */ 247 BUS_SPACE_MAXADDR, /* maxsegsize */ 248 BUS_DMA_ALLOCNOW, /* flags */ 249 NULL, /* lockfunc */ 250 NULL, /* lockarg */ 251 &sc->malo_dmat)) { 252 device_printf(dev, "cannot allocate DMA tag\n"); 253 goto bad1; 254 } 255 256 sc->malo_io0t = rman_get_bustag(psc->malo_res_mem[0]); 257 sc->malo_io0h = rman_get_bushandle(psc->malo_res_mem[0]); 258 sc->malo_io1t = rman_get_bustag(psc->malo_res_mem[1]); 259 sc->malo_io1h = rman_get_bushandle(psc->malo_res_mem[1]); 260 261 error = malo_attach(pci_get_device(dev), sc); 262 263 if (error != 0) { 264 malo_pci_detach(dev); 265 return (error); 266 } 267 268 return (error); 269 bad1: 270 if (psc->malo_msi == 0) 271 bus_teardown_intr(dev, psc->malo_res_irq[0], 272 psc->malo_intrhand[0]); 273 else { 274 for (i = 0; i < MALO_MSI_MESSAGES; i++) 275 bus_teardown_intr(dev, psc->malo_res_irq[i], 276 psc->malo_intrhand[i]); 277 } 278 279 bad: 280 if (psc->malo_msi != 0) 281 pci_release_msi(dev); 282 283 return (error); 284 } 285 286 static int 287 malo_pci_detach(device_t dev) 288 { 289 int i; 290 struct malo_pci_softc *psc = device_get_softc(dev); 291 struct malo_softc *sc = &psc->malo_sc; 292 293 /* check if device was removed */ 294 sc->malo_invalid = !bus_child_present(dev); 295 296 malo_detach(sc); 297 298 bus_generic_detach(dev); 299 300 if (psc->malo_msi == 0) 301 bus_teardown_intr(dev, psc->malo_res_irq[0], 302 psc->malo_intrhand[0]); 303 else { 304 for (i = 0; i < MALO_MSI_MESSAGES; i++) 305 bus_teardown_intr(dev, psc->malo_res_irq[i], 306 psc->malo_intrhand[i]); 307 308 pci_release_msi(dev); 309 } 310 311 bus_release_resources(dev, psc->malo_irq_spec, psc->malo_res_irq); 312 bus_dma_tag_destroy(sc->malo_dmat); 313 bus_release_resources(dev, psc->malo_mem_spec, psc->malo_res_mem); 314 315 return (0); 316 } 317 318 static int 319 malo_pci_shutdown(device_t dev) 320 { 321 struct malo_pci_softc *psc = device_get_softc(dev); 322 323 malo_shutdown(&psc->malo_sc); 324 325 return (0); 326 } 327 328 static int 329 malo_pci_suspend(device_t dev) 330 { 331 struct malo_pci_softc *psc = device_get_softc(dev); 332 333 malo_suspend(&psc->malo_sc); 334 335 return (0); 336 } 337 338 static int 339 malo_pci_resume(device_t dev) 340 { 341 struct malo_pci_softc *psc = device_get_softc(dev); 342 343 if (!malo_pci_setup(dev)) 344 return ENXIO; 345 346 malo_resume(&psc->malo_sc); 347 348 return (0); 349 } 350 351 static device_method_t malo_pci_methods[] = { 352 /* Device interface */ 353 DEVMETHOD(device_probe, malo_pci_probe), 354 DEVMETHOD(device_attach, malo_pci_attach), 355 DEVMETHOD(device_detach, malo_pci_detach), 356 DEVMETHOD(device_shutdown, malo_pci_shutdown), 357 DEVMETHOD(device_suspend, malo_pci_suspend), 358 DEVMETHOD(device_resume, malo_pci_resume), 359 { 0,0 } 360 }; 361 362 static driver_t malo_pci_driver = { 363 "malo", 364 malo_pci_methods, 365 sizeof(struct malo_pci_softc) 366 }; 367 368 static devclass_t malo_devclass; 369 DRIVER_MODULE(if_malo, pci, malo_pci_driver, malo_devclass, 0, 0); 370 DRIVER_MODULE(if_malo, cardbus, malo_pci_driver, malo_devclass, 0, 0); 371 MODULE_VERSION(if_malo, 1); 372 MODULE_DEPEND(if_malo, wlan, 1, 1, 1); /* 802.11 media layer */ 373 MODULE_DEPEND(if_malo, malofw_fw, 1, 1, 1); 374