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 static 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_attach(device_t dev) 154 { 155 int error = ENXIO, i, msic, reg; 156 struct malo_pci_softc *psc = device_get_softc(dev); 157 struct malo_softc *sc = &psc->malo_sc; 158 159 sc->malo_dev = dev; 160 161 pci_enable_busmaster(dev); 162 163 /* 164 * Setup memory-mapping of PCI registers. 165 */ 166 psc->malo_mem_spec = malo_res_spec_mem; 167 error = bus_alloc_resources(dev, psc->malo_mem_spec, psc->malo_res_mem); 168 if (error) { 169 device_printf(dev, "couldn't allocate memory resources\n"); 170 return (ENXIO); 171 } 172 173 /* 174 * Arrange and allocate interrupt line. 175 */ 176 sc->malo_invalid = 1; 177 178 if (pci_find_cap(dev, PCIY_EXPRESS, ®) == 0) { 179 msic = pci_msi_count(dev); 180 if (bootverbose) 181 device_printf(dev, "MSI count : %d\n", msic); 182 } else 183 msic = 0; 184 185 psc->malo_irq_spec = malo_res_spec_legacy; 186 if (msic == MALO_MSI_MESSAGES && msi_disable == 0) { 187 if (pci_alloc_msi(dev, &msic) == 0) { 188 if (msic == MALO_MSI_MESSAGES) { 189 device_printf(dev, "Using %d MSI messages\n", 190 msic); 191 psc->malo_irq_spec = malo_res_spec_msi; 192 psc->malo_msi = 1; 193 } else 194 pci_release_msi(dev); 195 } 196 } 197 198 error = bus_alloc_resources(dev, psc->malo_irq_spec, psc->malo_res_irq); 199 if (error) { 200 device_printf(dev, "couldn't allocate IRQ resources\n"); 201 goto bad; 202 } 203 204 if (psc->malo_msi == 0) 205 error = bus_setup_intr(dev, psc->malo_res_irq[0], 206 INTR_TYPE_NET | INTR_MPSAFE, malo_intr, NULL, sc, 207 &psc->malo_intrhand[0]); 208 else { 209 for (i = 0; i < MALO_MSI_MESSAGES; i++) { 210 error = bus_setup_intr(dev, psc->malo_res_irq[i], 211 INTR_TYPE_NET | INTR_MPSAFE, malo_intr, NULL, sc, 212 &psc->malo_intrhand[i]); 213 if (error != 0) 214 break; 215 } 216 } 217 218 /* 219 * Setup DMA descriptor area. 220 */ 221 if (bus_dma_tag_create(bus_get_dma_tag(dev), /* parent */ 222 1, 0, /* alignment, bounds */ 223 BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 224 BUS_SPACE_MAXADDR, /* highaddr */ 225 NULL, NULL, /* filter, filterarg */ 226 BUS_SPACE_MAXADDR, /* maxsize */ 227 0, /* nsegments */ 228 BUS_SPACE_MAXADDR, /* maxsegsize */ 229 0, /* flags */ 230 NULL, /* lockfunc */ 231 NULL, /* lockarg */ 232 &sc->malo_dmat)) { 233 device_printf(dev, "cannot allocate DMA tag\n"); 234 goto bad1; 235 } 236 237 sc->malo_io0t = rman_get_bustag(psc->malo_res_mem[0]); 238 sc->malo_io0h = rman_get_bushandle(psc->malo_res_mem[0]); 239 sc->malo_io1t = rman_get_bustag(psc->malo_res_mem[1]); 240 sc->malo_io1h = rman_get_bushandle(psc->malo_res_mem[1]); 241 242 error = malo_attach(pci_get_device(dev), sc); 243 244 if (error != 0) 245 goto bad2; 246 247 return (error); 248 249 bad2: 250 bus_dma_tag_destroy(sc->malo_dmat); 251 bad1: 252 if (psc->malo_msi == 0) 253 bus_teardown_intr(dev, psc->malo_res_irq[0], 254 psc->malo_intrhand[0]); 255 else { 256 for (i = 0; i < MALO_MSI_MESSAGES; i++) 257 bus_teardown_intr(dev, psc->malo_res_irq[i], 258 psc->malo_intrhand[i]); 259 } 260 bus_release_resources(dev, psc->malo_irq_spec, psc->malo_res_irq); 261 bad: 262 if (psc->malo_msi != 0) 263 pci_release_msi(dev); 264 bus_release_resources(dev, psc->malo_mem_spec, psc->malo_res_mem); 265 266 return (error); 267 } 268 269 static int 270 malo_pci_detach(device_t dev) 271 { 272 int i; 273 struct malo_pci_softc *psc = device_get_softc(dev); 274 struct malo_softc *sc = &psc->malo_sc; 275 276 /* check if device was removed */ 277 sc->malo_invalid = !bus_child_present(dev); 278 279 malo_detach(sc); 280 281 bus_generic_detach(dev); 282 283 if (psc->malo_msi == 0) 284 bus_teardown_intr(dev, psc->malo_res_irq[0], 285 psc->malo_intrhand[0]); 286 else { 287 for (i = 0; i < MALO_MSI_MESSAGES; i++) 288 bus_teardown_intr(dev, psc->malo_res_irq[i], 289 psc->malo_intrhand[i]); 290 291 pci_release_msi(dev); 292 } 293 294 bus_release_resources(dev, psc->malo_irq_spec, psc->malo_res_irq); 295 bus_dma_tag_destroy(sc->malo_dmat); 296 bus_release_resources(dev, psc->malo_mem_spec, psc->malo_res_mem); 297 298 return (0); 299 } 300 301 static int 302 malo_pci_shutdown(device_t dev) 303 { 304 struct malo_pci_softc *psc = device_get_softc(dev); 305 306 malo_shutdown(&psc->malo_sc); 307 308 return (0); 309 } 310 311 static int 312 malo_pci_suspend(device_t dev) 313 { 314 struct malo_pci_softc *psc = device_get_softc(dev); 315 316 malo_suspend(&psc->malo_sc); 317 318 return (0); 319 } 320 321 static int 322 malo_pci_resume(device_t dev) 323 { 324 struct malo_pci_softc *psc = device_get_softc(dev); 325 326 malo_resume(&psc->malo_sc); 327 328 return (0); 329 } 330 331 static device_method_t malo_pci_methods[] = { 332 /* Device interface */ 333 DEVMETHOD(device_probe, malo_pci_probe), 334 DEVMETHOD(device_attach, malo_pci_attach), 335 DEVMETHOD(device_detach, malo_pci_detach), 336 DEVMETHOD(device_shutdown, malo_pci_shutdown), 337 DEVMETHOD(device_suspend, malo_pci_suspend), 338 DEVMETHOD(device_resume, malo_pci_resume), 339 { 0,0 } 340 }; 341 342 static driver_t malo_pci_driver = { 343 "malo", 344 malo_pci_methods, 345 sizeof(struct malo_pci_softc) 346 }; 347 348 static devclass_t malo_devclass; 349 DRIVER_MODULE(malo, pci, malo_pci_driver, malo_devclass, 0, 0); 350 MODULE_VERSION(malo, 1); 351 MODULE_DEPEND(malo, wlan, 1, 1, 1); /* 802.11 media layer */ 352 MODULE_DEPEND(malo, firmware, 1, 1, 1); 353