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