1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 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 #endif 37 38 /* 39 * PCI front-end for the Marvell 88W8335 Wireless LAN controller driver. 40 */ 41 42 #include <sys/param.h> 43 #include <sys/kernel.h> 44 #include <sys/malloc.h> 45 #include <sys/module.h> 46 #include <sys/socket.h> 47 #include <sys/sysctl.h> 48 49 #include <machine/bus.h> 50 #include <sys/bus.h> 51 #include <sys/rman.h> 52 53 #include <net/if.h> 54 #include <net/if_var.h> 55 #include <net/if_media.h> 56 #include <net/ethernet.h> 57 58 #include <net80211/ieee80211_var.h> 59 60 #include <dev/malo/if_malo.h> 61 62 #include <dev/pci/pcivar.h> 63 #include <dev/pci/pcireg.h> 64 65 /* 66 * PCI glue. 67 */ 68 69 #define MALO_RESOURCE_MAX 2 70 #define MALO_MSI_MESSAGES 1 71 72 struct malo_pci_softc { 73 struct malo_softc malo_sc; 74 struct resource_spec *malo_mem_spec; 75 struct resource *malo_res_mem[MALO_RESOURCE_MAX]; 76 struct resource_spec *malo_irq_spec; 77 struct resource *malo_res_irq[MALO_MSI_MESSAGES]; 78 void *malo_intrhand[MALO_MSI_MESSAGES]; 79 int malo_msi; 80 }; 81 82 /* 83 * Tunable variables. 84 */ 85 SYSCTL_DECL(_hw_malo); 86 static SYSCTL_NODE(_hw_malo, OID_AUTO, pci, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, 87 "Marvell 88W8335 driver PCI parameters"); 88 89 static int msi_disable = 0; /* MSI disabled */ 90 SYSCTL_INT(_hw_malo_pci, OID_AUTO, msi_disable, CTLFLAG_RWTUN, &msi_disable, 91 0, "MSI disabled"); 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 struct malo_product *mp; 136 uint16_t vendor, devid; 137 int i; 138 139 vendor = pci_get_vendor(dev); 140 devid = pci_get_device(dev); 141 mp = malo_products; 142 143 for (i = 0; i < nitems(malo_products); i++, mp++) { 144 if (vendor == mp->mp_vendorid && devid == mp->mp_deviceid) { 145 device_set_desc(dev, mp->mp_name); 146 return (BUS_PROBE_DEFAULT); 147 } 148 } 149 150 return (ENXIO); 151 } 152 153 static int 154 malo_pci_attach(device_t dev) 155 { 156 int error = ENXIO, i, msic, reg; 157 struct malo_pci_softc *psc = device_get_softc(dev); 158 struct malo_softc *sc = &psc->malo_sc; 159 160 sc->malo_dev = dev; 161 162 pci_enable_busmaster(dev); 163 164 /* 165 * Setup memory-mapping of PCI registers. 166 */ 167 psc->malo_mem_spec = malo_res_spec_mem; 168 error = bus_alloc_resources(dev, psc->malo_mem_spec, psc->malo_res_mem); 169 if (error) { 170 device_printf(dev, "couldn't allocate memory resources\n"); 171 return (ENXIO); 172 } 173 174 /* 175 * Arrange and allocate interrupt line. 176 */ 177 sc->malo_invalid = 1; 178 179 if (pci_find_cap(dev, PCIY_EXPRESS, ®) == 0) { 180 msic = pci_msi_count(dev); 181 if (bootverbose) 182 device_printf(dev, "MSI count : %d\n", msic); 183 } else 184 msic = 0; 185 186 psc->malo_irq_spec = malo_res_spec_legacy; 187 if (msic == MALO_MSI_MESSAGES && msi_disable == 0) { 188 if (pci_alloc_msi(dev, &msic) == 0) { 189 if (msic == MALO_MSI_MESSAGES) { 190 device_printf(dev, "Using %d MSI messages\n", 191 msic); 192 psc->malo_irq_spec = malo_res_spec_msi; 193 psc->malo_msi = 1; 194 } else 195 pci_release_msi(dev); 196 } 197 } 198 199 error = bus_alloc_resources(dev, psc->malo_irq_spec, psc->malo_res_irq); 200 if (error) { 201 device_printf(dev, "couldn't allocate IRQ resources\n"); 202 goto bad; 203 } 204 205 if (psc->malo_msi == 0) 206 error = bus_setup_intr(dev, psc->malo_res_irq[0], 207 INTR_TYPE_NET | INTR_MPSAFE, malo_intr, NULL, sc, 208 &psc->malo_intrhand[0]); 209 else { 210 for (i = 0; i < MALO_MSI_MESSAGES; i++) { 211 error = bus_setup_intr(dev, psc->malo_res_irq[i], 212 INTR_TYPE_NET | INTR_MPSAFE, malo_intr, NULL, sc, 213 &psc->malo_intrhand[i]); 214 if (error != 0) 215 break; 216 } 217 } 218 219 /* 220 * Setup DMA descriptor area. 221 */ 222 if (bus_dma_tag_create(bus_get_dma_tag(dev), /* parent */ 223 1, 0, /* alignment, bounds */ 224 BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 225 BUS_SPACE_MAXADDR, /* highaddr */ 226 NULL, NULL, /* filter, filterarg */ 227 BUS_SPACE_MAXSIZE, /* maxsize */ 228 0, /* nsegments */ 229 BUS_SPACE_MAXSIZE, /* maxsegsize */ 230 0, /* flags */ 231 NULL, /* lockfunc */ 232 NULL, /* lockarg */ 233 &sc->malo_dmat)) { 234 device_printf(dev, "cannot allocate DMA tag\n"); 235 goto bad1; 236 } 237 238 sc->malo_io0t = rman_get_bustag(psc->malo_res_mem[0]); 239 sc->malo_io0h = rman_get_bushandle(psc->malo_res_mem[0]); 240 sc->malo_io1t = rman_get_bustag(psc->malo_res_mem[1]); 241 sc->malo_io1h = rman_get_bushandle(psc->malo_res_mem[1]); 242 243 error = malo_attach(pci_get_device(dev), sc); 244 245 if (error != 0) 246 goto bad2; 247 248 return (error); 249 250 bad2: 251 bus_dma_tag_destroy(sc->malo_dmat); 252 bad1: 253 if (psc->malo_msi == 0) 254 bus_teardown_intr(dev, psc->malo_res_irq[0], 255 psc->malo_intrhand[0]); 256 else { 257 for (i = 0; i < MALO_MSI_MESSAGES; i++) 258 bus_teardown_intr(dev, psc->malo_res_irq[i], 259 psc->malo_intrhand[i]); 260 } 261 bus_release_resources(dev, psc->malo_irq_spec, psc->malo_res_irq); 262 bad: 263 if (psc->malo_msi != 0) 264 pci_release_msi(dev); 265 bus_release_resources(dev, psc->malo_mem_spec, psc->malo_res_mem); 266 267 return (error); 268 } 269 270 static int 271 malo_pci_detach(device_t dev) 272 { 273 int i; 274 struct malo_pci_softc *psc = device_get_softc(dev); 275 struct malo_softc *sc = &psc->malo_sc; 276 277 /* check if device was removed */ 278 sc->malo_invalid = !bus_child_present(dev); 279 280 malo_detach(sc); 281 282 bus_generic_detach(dev); 283 284 if (psc->malo_msi == 0) 285 bus_teardown_intr(dev, psc->malo_res_irq[0], 286 psc->malo_intrhand[0]); 287 else { 288 for (i = 0; i < MALO_MSI_MESSAGES; i++) 289 bus_teardown_intr(dev, psc->malo_res_irq[i], 290 psc->malo_intrhand[i]); 291 292 pci_release_msi(dev); 293 } 294 295 bus_release_resources(dev, psc->malo_irq_spec, psc->malo_res_irq); 296 bus_dma_tag_destroy(sc->malo_dmat); 297 bus_release_resources(dev, psc->malo_mem_spec, psc->malo_res_mem); 298 299 return (0); 300 } 301 302 static int 303 malo_pci_shutdown(device_t dev) 304 { 305 struct malo_pci_softc *psc = device_get_softc(dev); 306 307 malo_shutdown(&psc->malo_sc); 308 309 return (0); 310 } 311 312 static int 313 malo_pci_suspend(device_t dev) 314 { 315 struct malo_pci_softc *psc = device_get_softc(dev); 316 317 malo_suspend(&psc->malo_sc); 318 319 return (0); 320 } 321 322 static int 323 malo_pci_resume(device_t dev) 324 { 325 struct malo_pci_softc *psc = device_get_softc(dev); 326 327 malo_resume(&psc->malo_sc); 328 329 return (0); 330 } 331 332 static device_method_t malo_pci_methods[] = { 333 /* Device interface */ 334 DEVMETHOD(device_probe, malo_pci_probe), 335 DEVMETHOD(device_attach, malo_pci_attach), 336 DEVMETHOD(device_detach, malo_pci_detach), 337 DEVMETHOD(device_shutdown, malo_pci_shutdown), 338 DEVMETHOD(device_suspend, malo_pci_suspend), 339 DEVMETHOD(device_resume, malo_pci_resume), 340 { 0,0 } 341 }; 342 343 static driver_t malo_pci_driver = { 344 "malo", 345 malo_pci_methods, 346 sizeof(struct malo_pci_softc) 347 }; 348 349 DRIVER_MODULE(malo, pci, malo_pci_driver, 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