1 /*- 2 * Copyright (c) 2009-2010 Weongyo Jeong <weongyo@freebsd.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer, 10 * without modification. 11 * 2. Redistributions in binary form must reproduce at minimum a disclaimer 12 * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any 13 * redistribution must be conditioned upon including a substantially 14 * similar Disclaimer requirement for further binary redistribution. 15 * 16 * NO WARRANTY 17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY 20 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 21 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, 22 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 25 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 27 * THE POSSIBILITY OF SUCH DAMAGES. 28 */ 29 30 #include <sys/cdefs.h> 31 __FBSDID("$FreeBSD: head/sys/dev/siba/siba_bwn.c 257241 2013-10-28 07:29:16Z glebius $"); 32 33 /* 34 * Sonics Silicon Backplane front-end for bwn(4). 35 */ 36 37 #include <opt_siba.h> 38 39 #include <sys/param.h> 40 #include <sys/systm.h> 41 #include <sys/malloc.h> 42 #include <sys/module.h> 43 #include <sys/kernel.h> 44 #include <sys/errno.h> 45 #include <sys/bus.h> 46 #include <sys/bus_resource.h> 47 #include <sys/bus.h> 48 #include <sys/rman.h> 49 #include <sys/socket.h> 50 51 #include <bus/pci/pcivar.h> 52 #include <bus/pci/pcireg.h> 53 54 #include "siba_ids.h" 55 #include "sibareg.h" 56 #include "sibavar.h" 57 58 /* 59 * PCI glue. 60 */ 61 62 struct siba_bwn_softc { 63 /* Child driver using MSI. */ 64 device_t ssc_msi_child; 65 struct siba_softc ssc_siba; 66 }; 67 68 #define BS_BAR 0x10 69 #define PCI_VENDOR_BROADCOM 0x14e4 70 #define N(a) (sizeof(a) / sizeof(a[0])) 71 72 static const struct siba_dev { 73 uint16_t vid; 74 uint16_t did; 75 const char *desc; 76 } siba_devices[] = { 77 { PCI_VENDOR_BROADCOM, 0x4301, "Broadcom BCM4301 802.11b Wireless" }, 78 { PCI_VENDOR_BROADCOM, 0x4306, "Unknown" }, 79 { PCI_VENDOR_BROADCOM, 0x4307, "Broadcom BCM4307 802.11b Wireless" }, 80 { PCI_VENDOR_BROADCOM, 0x4311, "Broadcom BCM4311 802.11b/g Wireless" }, 81 { PCI_VENDOR_BROADCOM, 0x4312, 82 "Broadcom BCM4312 802.11a/b/g Wireless" }, 83 { PCI_VENDOR_BROADCOM, 0x4315, "Broadcom BCM4312 802.11b/g Wireless" }, 84 { PCI_VENDOR_BROADCOM, 0x4318, "Broadcom BCM4318 802.11b/g Wireless" }, 85 { PCI_VENDOR_BROADCOM, 0x4319, 86 "Broadcom BCM4318 802.11a/b/g Wireless" }, 87 { PCI_VENDOR_BROADCOM, 0x4320, "Broadcom BCM4306 802.11b/g Wireless" }, 88 { PCI_VENDOR_BROADCOM, 0x4321, "Broadcom BCM4306 802.11a Wireless" }, 89 { PCI_VENDOR_BROADCOM, 0x4324, 90 "Broadcom BCM4309 802.11a/b/g Wireless" }, 91 { PCI_VENDOR_BROADCOM, 0x4325, "Broadcom BCM4306 802.11b/g Wireless" }, 92 { PCI_VENDOR_BROADCOM, 0x4328, "Unknown" }, 93 { PCI_VENDOR_BROADCOM, 0x4329, "Unknown" }, 94 { PCI_VENDOR_BROADCOM, 0x432b, "Unknown" } 95 }; 96 97 int siba_core_attach(struct siba_softc *); 98 int siba_core_detach(struct siba_softc *); 99 int siba_core_suspend(struct siba_softc *); 100 int siba_core_resume(struct siba_softc *); 101 102 static int 103 siba_bwn_probe(device_t dev) 104 { 105 int i; 106 uint16_t did, vid; 107 108 did = pci_get_device(dev); 109 vid = pci_get_vendor(dev); 110 111 for (i = 0; i < N(siba_devices); i++) { 112 if (siba_devices[i].did == did && siba_devices[i].vid == vid) { 113 device_set_desc(dev, siba_devices[i].desc); 114 return (BUS_PROBE_DEFAULT); 115 } 116 } 117 return (ENXIO); 118 } 119 120 static int 121 siba_bwn_attach(device_t dev) 122 { 123 struct siba_bwn_softc *ssc = device_get_softc(dev); 124 struct siba_softc *siba = &ssc->ssc_siba; 125 126 siba->siba_dev = dev; 127 siba->siba_type = SIBA_TYPE_PCI; 128 129 /* 130 * Enable bus mastering. 131 */ 132 pci_enable_busmaster(dev); 133 134 /* 135 * Setup memory-mapping of PCI registers. 136 */ 137 siba->siba_mem_rid = SIBA_PCIR_BAR; 138 siba->siba_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 139 &siba->siba_mem_rid, RF_ACTIVE); 140 if (siba->siba_mem_res == NULL) { 141 device_printf(dev, "cannot map register space\n"); 142 return (ENXIO); 143 } 144 siba->siba_mem_bt = rman_get_bustag(siba->siba_mem_res); 145 siba->siba_mem_bh = rman_get_bushandle(siba->siba_mem_res); 146 147 /* Get more PCI information */ 148 siba->siba_pci_did = pci_get_device(dev); 149 siba->siba_pci_vid = pci_get_vendor(dev); 150 siba->siba_pci_subvid = pci_get_subvendor(dev); 151 siba->siba_pci_subdid = pci_get_subdevice(dev); 152 siba->siba_pci_revid = pci_get_revid(dev); 153 154 return (siba_core_attach(siba)); 155 } 156 157 static int 158 siba_bwn_detach(device_t dev) 159 { 160 struct siba_bwn_softc *ssc = device_get_softc(dev); 161 struct siba_softc *siba = &ssc->ssc_siba; 162 163 /* check if device was removed */ 164 siba->siba_invalid = !bus_child_present(dev); 165 166 pci_disable_busmaster(dev); 167 bus_generic_detach(dev); 168 siba_core_detach(siba); 169 170 bus_release_resource(dev, SYS_RES_MEMORY, BS_BAR, siba->siba_mem_res); 171 172 return (0); 173 } 174 175 static int 176 siba_bwn_shutdown(device_t dev) 177 { 178 device_t *devlistp; 179 int devcnt, error = 0, i; 180 181 error = device_get_children(dev, &devlistp, &devcnt); 182 if (error != 0) 183 return (error); 184 185 for (i = 0 ; i < devcnt ; i++) 186 device_shutdown(devlistp[i]); 187 kfree(devlistp, M_TEMP); 188 return (0); 189 } 190 191 static int 192 siba_bwn_suspend(device_t dev) 193 { 194 struct siba_bwn_softc *ssc = device_get_softc(dev); 195 struct siba_softc *siba = &ssc->ssc_siba; 196 device_t *devlistp; 197 int devcnt, error = 0, i, j; 198 199 error = device_get_children(dev, &devlistp, &devcnt); 200 if (error != 0) 201 return (error); 202 203 for (i = 0 ; i < devcnt ; i++) { 204 error = DEVICE_SUSPEND(devlistp[i]); 205 if (error) { 206 for (j = 0; j < i; j++) 207 DEVICE_RESUME(devlistp[j]); 208 kfree(devlistp, M_TEMP); 209 return (error); 210 } 211 } 212 kfree(devlistp, M_TEMP); 213 return (siba_core_suspend(siba)); 214 } 215 216 static int 217 siba_bwn_resume(device_t dev) 218 { 219 struct siba_bwn_softc *ssc = device_get_softc(dev); 220 struct siba_softc *siba = &ssc->ssc_siba; 221 device_t *devlistp; 222 int devcnt, error = 0, i; 223 224 error = siba_core_resume(siba); 225 if (error != 0) 226 return (error); 227 228 error = device_get_children(dev, &devlistp, &devcnt); 229 if (error != 0) 230 return (error); 231 232 for (i = 0 ; i < devcnt ; i++) 233 DEVICE_RESUME(devlistp[i]); 234 kfree(devlistp, M_TEMP); 235 return (0); 236 } 237 238 /* proxying to the parent */ 239 static struct resource * 240 siba_bwn_alloc_resource(device_t dev, device_t child, int type, int *rid, 241 u_long start, u_long end, u_long count, u_int flags, int cpuid) 242 { 243 244 return (BUS_ALLOC_RESOURCE(device_get_parent(dev), dev, 245 type, rid, start, end, count, flags, cpuid)); 246 } 247 248 /* proxying to the parent */ 249 static int 250 siba_bwn_release_resource(device_t dev, device_t child, int type, 251 int rid, struct resource *r) 252 { 253 254 return (BUS_RELEASE_RESOURCE(device_get_parent(dev), dev, type, 255 rid, r)); 256 } 257 258 /* proxying to the parent */ 259 static int 260 siba_bwn_setup_intr(device_t dev, device_t child, struct resource *irq, 261 int flags, driver_intr_t *intr, void *arg, void **cookiep, 262 lwkt_serialize_t serializer) 263 { 264 265 return (BUS_SETUP_INTR(device_get_parent(dev), dev, irq, flags, 266 intr, arg, cookiep, serializer, NULL)); 267 } 268 269 /* proxying to the parent */ 270 static int 271 siba_bwn_teardown_intr(device_t dev, device_t child, struct resource *irq, 272 void *cookie) 273 { 274 275 return (BUS_TEARDOWN_INTR(device_get_parent(dev), dev, irq, cookie)); 276 } 277 278 static int 279 siba_bwn_find_extcap(device_t dev, device_t child, int capability, 280 int *capreg) 281 { 282 283 return (pci_find_extcap(dev, capability, capreg)); 284 } 285 286 static int 287 siba_bwn_alloc_msi(device_t dev, device_t child, int *rid, int count, 288 int cpuid) 289 { 290 struct siba_bwn_softc *ssc; 291 int error; 292 293 ssc = device_get_softc(dev); 294 if (ssc->ssc_msi_child != NULL) 295 return (EBUSY); 296 error = pci_alloc_msi(dev, rid, count, cpuid); 297 if (error == 0) 298 ssc->ssc_msi_child = child; 299 return (error); 300 } 301 302 static int 303 siba_bwn_release_msi(device_t dev, device_t child) 304 { 305 struct siba_bwn_softc *ssc; 306 int error; 307 308 ssc = device_get_softc(dev); 309 if (ssc->ssc_msi_child != child) 310 return (ENXIO); 311 error = pci_release_msi(dev); 312 if (error == 0) 313 ssc->ssc_msi_child = NULL; 314 return (error); 315 } 316 317 static int 318 siba_bwn_msi_count(device_t dev, device_t child) 319 { 320 321 return (pci_msi_count(dev)); 322 } 323 324 static int 325 siba_bwn_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) 326 { 327 struct siba_dev_softc *sd; 328 struct siba_softc *siba; 329 330 sd = device_get_ivars(child); 331 siba = sd->sd_bus; 332 333 switch (which) { 334 case SIBA_IVAR_VENDOR: 335 *result = sd->sd_id.sd_vendor; 336 break; 337 case SIBA_IVAR_DEVICE: 338 *result = sd->sd_id.sd_device; 339 break; 340 case SIBA_IVAR_REVID: 341 *result = sd->sd_id.sd_rev; 342 break; 343 case SIBA_IVAR_PCI_VENDOR: 344 *result = siba->siba_pci_vid; 345 break; 346 case SIBA_IVAR_PCI_DEVICE: 347 *result = siba->siba_pci_did; 348 break; 349 case SIBA_IVAR_PCI_SUBVENDOR: 350 *result = siba->siba_pci_subvid; 351 break; 352 case SIBA_IVAR_PCI_SUBDEVICE: 353 *result = siba->siba_pci_subdid; 354 break; 355 case SIBA_IVAR_PCI_REVID: 356 *result = siba->siba_pci_revid; 357 break; 358 case SIBA_IVAR_CHIPID: 359 *result = siba->siba_chipid; 360 break; 361 case SIBA_IVAR_CHIPREV: 362 *result = siba->siba_chiprev; 363 break; 364 case SIBA_IVAR_CHIPPKG: 365 *result = siba->siba_chippkg; 366 break; 367 case SIBA_IVAR_TYPE: 368 *result = siba->siba_type; 369 break; 370 case SIBA_IVAR_CC_PMUFREQ: 371 *result = siba->siba_cc.scc_pmu.freq; 372 break; 373 case SIBA_IVAR_CC_CAPS: 374 *result = siba->siba_cc.scc_caps; 375 break; 376 case SIBA_IVAR_CC_POWERDELAY: 377 *result = siba->siba_cc.scc_powerup_delay; 378 break; 379 case SIBA_IVAR_PCICORE_REVID: 380 *result = siba->siba_pci.spc_dev->sd_id.sd_rev; 381 break; 382 default: 383 return (ENOENT); 384 } 385 386 return (0); 387 } 388 389 static device_method_t siba_bwn_methods[] = { 390 /* Device interface */ 391 DEVMETHOD(device_probe, siba_bwn_probe), 392 DEVMETHOD(device_attach, siba_bwn_attach), 393 DEVMETHOD(device_detach, siba_bwn_detach), 394 DEVMETHOD(device_shutdown, siba_bwn_shutdown), 395 DEVMETHOD(device_suspend, siba_bwn_suspend), 396 DEVMETHOD(device_resume, siba_bwn_resume), 397 398 /* Bus interface */ 399 DEVMETHOD(bus_alloc_resource, siba_bwn_alloc_resource), 400 DEVMETHOD(bus_release_resource, siba_bwn_release_resource), 401 DEVMETHOD(bus_read_ivar, siba_bwn_read_ivar), 402 DEVMETHOD(bus_setup_intr, siba_bwn_setup_intr), 403 DEVMETHOD(bus_teardown_intr, siba_bwn_teardown_intr), 404 405 /* PCI interface */ 406 DEVMETHOD(pci_find_extcap, siba_bwn_find_extcap), 407 DEVMETHOD(pci_alloc_msi, siba_bwn_alloc_msi), 408 DEVMETHOD(pci_release_msi, siba_bwn_release_msi), 409 DEVMETHOD(pci_msi_count, siba_bwn_msi_count), 410 411 DEVMETHOD_END 412 }; 413 static driver_t siba_bwn_driver = { 414 "siba_bwn", 415 siba_bwn_methods, 416 sizeof(struct siba_bwn_softc) 417 }; 418 static devclass_t siba_bwn_devclass; 419 DRIVER_MODULE(siba_bwn, pci, siba_bwn_driver, siba_bwn_devclass, NULL, NULL); 420 MODULE_VERSION(siba_bwn, 1); 421