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$"); 32 33/* 34 * Sonics Silicon Backplane front-end for bwn(4). 35 */ 36 37#include <sys/param.h> 38#include <sys/systm.h> 39#include <sys/module.h> 40#include <sys/kernel.h> 41#include <sys/lock.h> 42#include <sys/mutex.h> 43#include <sys/errno.h> 44#include <machine/bus.h> 45#include <machine/resource.h> 46#include <sys/bus.h> 47#include <sys/rman.h> 48#include <sys/socket.h> 49 50#include <net/if.h> 51#include <net/if_media.h> 52#include <net/if_arp.h> 53 54#include <dev/pci/pcivar.h> 55#include <dev/pci/pcireg.h> 56 57#include <dev/siba/siba_ids.h> 58#include <dev/siba/sibareg.h> 59#include <dev/siba/sibavar.h> 60 61/* 62 * PCI glue. 63 */ 64 65struct siba_bwn_softc { 66 /* Child driver using MSI. */ 67 device_t ssc_msi_child; 68 struct siba_softc ssc_siba; 69}; 70 71#define BS_BAR 0x10 72#define PCI_VENDOR_BROADCOM 0x14e4 73#define N(a) (sizeof(a) / sizeof(a[0])) 74 75static const struct siba_dev { 76 uint16_t vid; 77 uint16_t did; 78 const char *desc; 79} siba_devices[] = { 80 { PCI_VENDOR_BROADCOM, 0x4301, "Broadcom BCM4301 802.11b Wireless" }, 81 { PCI_VENDOR_BROADCOM, 0x4306, "Unknown" }, 82 { PCI_VENDOR_BROADCOM, 0x4307, "Broadcom BCM4307 802.11b Wireless" }, 83 { PCI_VENDOR_BROADCOM, 0x4311, "Broadcom BCM4311 802.11b/g Wireless" }, 84 { PCI_VENDOR_BROADCOM, 0x4312, 85 "Broadcom BCM4312 802.11a/b/g Wireless" }, 86 { PCI_VENDOR_BROADCOM, 0x4315, "Broadcom BCM4312 802.11b/g Wireless" }, 87 { PCI_VENDOR_BROADCOM, 0x4318, "Broadcom BCM4318 802.11b/g Wireless" }, 88 { PCI_VENDOR_BROADCOM, 0x4319, 89 "Broadcom BCM4318 802.11a/b/g Wireless" }, 90 { PCI_VENDOR_BROADCOM, 0x4320, "Broadcom BCM4306 802.11b/g Wireless" }, 91 { PCI_VENDOR_BROADCOM, 0x4321, "Broadcom BCM4306 802.11a Wireless" }, 92 { PCI_VENDOR_BROADCOM, 0x4324, 93 "Broadcom BCM4309 802.11a/b/g Wireless" }, 94 { PCI_VENDOR_BROADCOM, 0x4325, "Broadcom BCM4306 802.11b/g Wireless" }, 95 { PCI_VENDOR_BROADCOM, 0x4328, "Unknown" }, 96 { PCI_VENDOR_BROADCOM, 0x4329, "Unknown" }, 97 { PCI_VENDOR_BROADCOM, 0x432b, "Unknown" } 98}; 99 100int siba_core_attach(struct siba_softc *); 101int siba_core_detach(struct siba_softc *); 102int siba_core_suspend(struct siba_softc *); 103int siba_core_resume(struct siba_softc *); 104 105static int 106siba_bwn_probe(device_t dev) 107{ 108 int i; 109 uint16_t did, vid; 110 111 did = pci_get_device(dev); 112 vid = pci_get_vendor(dev); 113 114 for (i = 0; i < N(siba_devices); i++) { 115 if (siba_devices[i].did == did && siba_devices[i].vid == vid) { 116 device_set_desc(dev, siba_devices[i].desc); 117 return (BUS_PROBE_DEFAULT); 118 } 119 } 120 return (ENXIO); 121} 122 123static int 124siba_bwn_attach(device_t dev) 125{ 126 struct siba_bwn_softc *ssc = device_get_softc(dev); 127 struct siba_softc *siba = &ssc->ssc_siba; 128 129 siba->siba_dev = dev; 130 siba->siba_type = SIBA_TYPE_PCI; 131 132 /* 133 * Enable bus mastering. 134 */ 135 pci_enable_busmaster(dev); 136 137 /* 138 * Setup memory-mapping of PCI registers. 139 */ 140 siba->siba_mem_rid = SIBA_PCIR_BAR; 141 siba->siba_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 142 &siba->siba_mem_rid, RF_ACTIVE); 143 if (siba->siba_mem_res == NULL) { 144 device_printf(dev, "cannot map register space\n"); 145 return (ENXIO); 146 } 147 siba->siba_mem_bt = rman_get_bustag(siba->siba_mem_res); 148 siba->siba_mem_bh = rman_get_bushandle(siba->siba_mem_res); 149 150 /* Get more PCI information */ 151 siba->siba_pci_did = pci_get_device(dev); 152 siba->siba_pci_vid = pci_get_vendor(dev); 153 siba->siba_pci_subvid = pci_get_subvendor(dev); 154 siba->siba_pci_subdid = pci_get_subdevice(dev); 155 siba->siba_pci_revid = pci_get_revid(dev); 156 157 return (siba_core_attach(siba)); 158} 159 160static int 161siba_bwn_detach(device_t dev) 162{ 163 struct siba_bwn_softc *ssc = device_get_softc(dev); 164 struct siba_softc *siba = &ssc->ssc_siba; 165 166 /* check if device was removed */ 167 siba->siba_invalid = !bus_child_present(dev); 168 169 pci_disable_busmaster(dev); 170 bus_generic_detach(dev); 171 siba_core_detach(siba); 172 173 bus_release_resource(dev, SYS_RES_MEMORY, BS_BAR, siba->siba_mem_res); 174 175 return (0); 176} 177 178static int 179siba_bwn_shutdown(device_t dev) 180{ 181 device_t *devlistp; 182 int devcnt, error = 0, i; 183 184 error = device_get_children(dev, &devlistp, &devcnt); 185 if (error != 0) 186 return (error); 187 188 for (i = 0 ; i < devcnt ; i++) 189 device_shutdown(devlistp[i]); 190 free(devlistp, M_TEMP); 191 return (0); 192} 193 194static int 195siba_bwn_suspend(device_t dev) 196{ 197 struct siba_bwn_softc *ssc = device_get_softc(dev); 198 struct siba_softc *siba = &ssc->ssc_siba; 199 device_t *devlistp; 200 int devcnt, error = 0, i, j; 201 202 error = device_get_children(dev, &devlistp, &devcnt); 203 if (error != 0) 204 return (error); 205 206 for (i = 0 ; i < devcnt ; i++) { 207 error = DEVICE_SUSPEND(devlistp[i]); 208 if (error) { 209 for (j = 0; j < i; j++) 210 DEVICE_RESUME(devlistp[j]); 211 free(devlistp, M_TEMP); 212 return (error); 213 } 214 } 215 free(devlistp, M_TEMP); 216 return (siba_core_suspend(siba)); 217} 218 219static int 220siba_bwn_resume(device_t dev) 221{ 222 struct siba_bwn_softc *ssc = device_get_softc(dev); 223 struct siba_softc *siba = &ssc->ssc_siba; 224 device_t *devlistp; 225 int devcnt, error = 0, i; 226 227 error = siba_core_resume(siba); 228 if (error != 0) 229 return (error); 230 231 error = device_get_children(dev, &devlistp, &devcnt); 232 if (error != 0) 233 return (error); 234 235 for (i = 0 ; i < devcnt ; i++) 236 DEVICE_RESUME(devlistp[i]); 237 free(devlistp, M_TEMP); 238 return (0); 239} 240 241/* proxying to the parent */ 242static struct resource * 243siba_bwn_alloc_resource(device_t dev, device_t child, int type, int *rid, 244 u_long start, u_long end, u_long count, u_int flags) 245{ 246 247 return (BUS_ALLOC_RESOURCE(device_get_parent(dev), dev, 248 type, rid, start, end, count, flags)); 249} 250 251/* proxying to the parent */ 252static int 253siba_bwn_release_resource(device_t dev, device_t child, int type, 254 int rid, struct resource *r) 255{ 256 257 return (BUS_RELEASE_RESOURCE(device_get_parent(dev), dev, type, 258 rid, r)); 259} 260 261/* proxying to the parent */ 262static int 263siba_bwn_setup_intr(device_t dev, device_t child, struct resource *irq, 264 int flags, driver_filter_t *filter, driver_intr_t *intr, void *arg, 265 void **cookiep) 266{ 267 268 return (BUS_SETUP_INTR(device_get_parent(dev), dev, irq, flags, 269 filter, intr, arg, cookiep)); 270} 271 272/* proxying to the parent */ 273static int 274siba_bwn_teardown_intr(device_t dev, device_t child, struct resource *irq, 275 void *cookie) 276{ 277 278 return (BUS_TEARDOWN_INTR(device_get_parent(dev), dev, irq, cookie)); 279} 280 281static int 282siba_bwn_find_cap(device_t dev, device_t child, int capability, 283 int *capreg) 284{ 285 286 return (pci_find_cap(dev, capability, capreg)); 287} 288 289static int 290siba_bwn_find_extcap(device_t dev, device_t child, int capability, 291 int *capreg) 292{ 293 294 return (pci_find_extcap(dev, capability, capreg)); 295} 296 297static int 298siba_bwn_find_htcap(device_t dev, device_t child, int capability, 299 int *capreg) 300{ 301 302 return (pci_find_htcap(dev, capability, capreg)); 303} 304 305static int 306siba_bwn_alloc_msi(device_t dev, device_t child, int *count) 307{ 308 struct siba_bwn_softc *ssc; 309 int error; 310 311 ssc = device_get_softc(dev); 312 if (ssc->ssc_msi_child != NULL) 313 return (EBUSY); 314 error = pci_alloc_msi(dev, count); 315 if (error == 0) 316 ssc->ssc_msi_child = child; 317 return (error); 318} 319 320static int 321siba_bwn_release_msi(device_t dev, device_t child) 322{ 323 struct siba_bwn_softc *ssc; 324 int error; 325 326 ssc = device_get_softc(dev); 327 if (ssc->ssc_msi_child != child) 328 return (ENXIO); 329 error = pci_release_msi(dev); 330 if (error == 0) 331 ssc->ssc_msi_child = NULL; 332 return (error); 333} 334 335static int 336siba_bwn_msi_count(device_t dev, device_t child) 337{ 338 339 return (pci_msi_count(dev)); 340} 341 342static int 343siba_bwn_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) 344{ 345 struct siba_dev_softc *sd; 346 struct siba_softc *siba; 347 348 sd = device_get_ivars(child); 349 siba = sd->sd_bus; 350 351 switch (which) { 352 case SIBA_IVAR_VENDOR: 353 *result = sd->sd_id.sd_vendor; 354 break; 355 case SIBA_IVAR_DEVICE: 356 *result = sd->sd_id.sd_device; 357 break; 358 case SIBA_IVAR_REVID: 359 *result = sd->sd_id.sd_rev; 360 break; 361 case SIBA_IVAR_PCI_VENDOR: 362 *result = siba->siba_pci_vid; 363 break; 364 case SIBA_IVAR_PCI_DEVICE: 365 *result = siba->siba_pci_did; 366 break; 367 case SIBA_IVAR_PCI_SUBVENDOR: 368 *result = siba->siba_pci_subvid; 369 break; 370 case SIBA_IVAR_PCI_SUBDEVICE: 371 *result = siba->siba_pci_subdid; 372 break; 373 case SIBA_IVAR_PCI_REVID: 374 *result = siba->siba_pci_revid; 375 break; 376 case SIBA_IVAR_CHIPID: 377 *result = siba->siba_chipid; 378 break; 379 case SIBA_IVAR_CHIPREV: 380 *result = siba->siba_chiprev; 381 break; 382 case SIBA_IVAR_CHIPPKG: 383 *result = siba->siba_chippkg; 384 break; 385 case SIBA_IVAR_TYPE: 386 *result = siba->siba_type; 387 break; 388 case SIBA_IVAR_CC_PMUFREQ: 389 *result = siba->siba_cc.scc_pmu.freq; 390 break; 391 case SIBA_IVAR_CC_CAPS: 392 *result = siba->siba_cc.scc_caps; 393 break; 394 case SIBA_IVAR_CC_POWERDELAY: 395 *result = siba->siba_cc.scc_powerup_delay; 396 break; 397 case SIBA_IVAR_PCICORE_REVID: 398 *result = siba->siba_pci.spc_dev->sd_id.sd_rev; 399 break; 400 default: 401 return (ENOENT); 402 } 403 404 return (0); 405} 406 407static device_method_t siba_bwn_methods[] = { 408 /* Device interface */ 409 DEVMETHOD(device_probe, siba_bwn_probe), 410 DEVMETHOD(device_attach, siba_bwn_attach), 411 DEVMETHOD(device_detach, siba_bwn_detach), 412 DEVMETHOD(device_shutdown, siba_bwn_shutdown), 413 DEVMETHOD(device_suspend, siba_bwn_suspend), 414 DEVMETHOD(device_resume, siba_bwn_resume), 415 416 /* Bus interface */ 417 DEVMETHOD(bus_alloc_resource, siba_bwn_alloc_resource), 418 DEVMETHOD(bus_release_resource, siba_bwn_release_resource), 419 DEVMETHOD(bus_read_ivar, siba_bwn_read_ivar), 420 DEVMETHOD(bus_setup_intr, siba_bwn_setup_intr), 421 DEVMETHOD(bus_teardown_intr, siba_bwn_teardown_intr), 422 423 /* PCI interface */ 424 DEVMETHOD(pci_find_cap, siba_bwn_find_cap), 425 DEVMETHOD(pci_find_extcap, siba_bwn_find_extcap), 426 DEVMETHOD(pci_find_htcap, siba_bwn_find_htcap), 427 DEVMETHOD(pci_alloc_msi, siba_bwn_alloc_msi), 428 DEVMETHOD(pci_release_msi, siba_bwn_release_msi), 429 DEVMETHOD(pci_msi_count, siba_bwn_msi_count), 430 431 DEVMETHOD_END 432}; 433static driver_t siba_bwn_driver = { 434 "siba_bwn", 435 siba_bwn_methods, 436 sizeof(struct siba_bwn_softc) 437}; 438static devclass_t siba_bwn_devclass; 439DRIVER_MODULE(siba_bwn, pci, siba_bwn_driver, siba_bwn_devclass, 0, 0); 440MODULE_VERSION(siba_bwn, 1); 441