siba_bwn.c revision 204922
1238106Sdes/*- 2238106Sdes * Copyright (c) 2009-2010 Weongyo Jeong <weongyo@freebsd.org> 3238106Sdes * All rights reserved. 4238106Sdes * 5238106Sdes * Redistribution and use in source and binary forms, with or without 6238106Sdes * modification, are permitted provided that the following conditions 7238106Sdes * are met: 8238106Sdes * 1. Redistributions of source code must retain the above copyright 9238106Sdes * notice, this list of conditions and the following disclaimer, 10238106Sdes * without modification. 11238106Sdes * 2. Redistributions in binary form must reproduce at minimum a disclaimer 12238106Sdes * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any 13238106Sdes * redistribution must be conditioned upon including a substantially 14238106Sdes * similar Disclaimer requirement for further binary redistribution. 15238106Sdes * 16238106Sdes * NO WARRANTY 17238106Sdes * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18238106Sdes * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19238106Sdes * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY 20238106Sdes * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 21238106Sdes * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, 22238106Sdes * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23238106Sdes * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24238106Sdes * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 25238106Sdes * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26238106Sdes * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 27238106Sdes * THE POSSIBILITY OF SUCH DAMAGES. 28238106Sdes */ 29238106Sdes 30238106Sdes#include <sys/cdefs.h> 31238106Sdes__FBSDID("$FreeBSD: head/sys/dev/siba/siba_bwn.c 204922 2010-03-09 19:58:00Z weongyo $"); 32238106Sdes 33238106Sdes/* 34238106Sdes * Sonics Silicon Backplane front-end for bwn(4). 35238106Sdes */ 36238106Sdes 37238106Sdes#include <sys/param.h> 38238106Sdes#include <sys/systm.h> 39238106Sdes#include <sys/module.h> 40238106Sdes#include <sys/kernel.h> 41238106Sdes#include <sys/lock.h> 42238106Sdes#include <sys/mutex.h> 43238106Sdes#include <sys/errno.h> 44238106Sdes#include <machine/bus.h> 45238106Sdes#include <machine/resource.h> 46238106Sdes#include <sys/bus.h> 47238106Sdes#include <sys/rman.h> 48238106Sdes#include <sys/socket.h> 49238106Sdes 50238106Sdes#include <net/if.h> 51238106Sdes#include <net/if_media.h> 52238106Sdes#include <net/if_arp.h> 53238106Sdes 54238106Sdes#include <dev/pci/pcivar.h> 55238106Sdes#include <dev/pci/pcireg.h> 56238106Sdes 57238106Sdes#include <dev/siba/siba_ids.h> 58238106Sdes#include <dev/siba/sibareg.h> 59238106Sdes#include <dev/siba/sibavar.h> 60238106Sdes 61238106Sdes/* 62238106Sdes * PCI glue. 63238106Sdes */ 64238106Sdes 65238106Sdesstruct siba_bwn_softc { 66238106Sdes /* Child driver using MSI. */ 67238106Sdes device_t ssc_msi_child; 68238106Sdes struct siba_softc ssc_siba; 69238106Sdes}; 70238106Sdes 71238106Sdes#define BS_BAR 0x10 72238106Sdes#define PCI_VENDOR_BROADCOM 0x14e4 73238106Sdes#define N(a) (sizeof(a) / sizeof(a[0])) 74238106Sdes 75238106Sdesstatic const struct siba_dev { 76238106Sdes uint16_t vid; 77238106Sdes uint16_t did; 78238106Sdes const char *desc; 79238106Sdes} siba_devices[] = { 80238106Sdes { PCI_VENDOR_BROADCOM, 0x4301, "Broadcom BCM4301 802.11b Wireless" }, 81238106Sdes { PCI_VENDOR_BROADCOM, 0x4306, "Unknown" }, 82238106Sdes { PCI_VENDOR_BROADCOM, 0x4307, "Broadcom BCM4307 802.11b Wireless" }, 83238106Sdes { PCI_VENDOR_BROADCOM, 0x4311, "Broadcom BCM4311 802.11b/g Wireless" }, 84238106Sdes { PCI_VENDOR_BROADCOM, 0x4312, 85238106Sdes "Broadcom BCM4312 802.11a/b/g Wireless" }, 86238106Sdes { PCI_VENDOR_BROADCOM, 0x4315, "Broadcom BCM4312 802.11b/g Wireless" }, 87238106Sdes { PCI_VENDOR_BROADCOM, 0x4318, "Broadcom BCM4318 802.11b/g Wireless" }, 88238106Sdes { PCI_VENDOR_BROADCOM, 0x4319, 89238106Sdes "Broadcom BCM4318 802.11a/b/g Wireless" }, 90238106Sdes { PCI_VENDOR_BROADCOM, 0x4320, "Broadcom BCM4306 802.11b/g Wireless" }, 91238106Sdes { PCI_VENDOR_BROADCOM, 0x4321, "Broadcom BCM4306 802.11a Wireless" }, 92238106Sdes { PCI_VENDOR_BROADCOM, 0x4324, 93238106Sdes "Broadcom BCM4309 802.11a/b/g Wireless" }, 94238106Sdes { PCI_VENDOR_BROADCOM, 0x4325, "Broadcom BCM4306 802.11b/g Wireless" }, 95238106Sdes { PCI_VENDOR_BROADCOM, 0x4328, "Unknown" }, 96238106Sdes { PCI_VENDOR_BROADCOM, 0x4329, "Unknown" }, 97238106Sdes { PCI_VENDOR_BROADCOM, 0x432b, "Unknown" } 98238106Sdes}; 99238106Sdes 100238106Sdesint siba_core_attach(struct siba_softc *); 101238106Sdesint siba_core_detach(struct siba_softc *); 102238106Sdesint siba_core_suspend(struct siba_softc *); 103238106Sdesint siba_core_resume(struct siba_softc *); 104238106Sdes 105238106Sdesstatic int 106238106Sdessiba_bwn_probe(device_t dev) 107238106Sdes{ 108238106Sdes int i; 109238106Sdes uint16_t did, vid; 110238106Sdes 111238106Sdes did = pci_get_device(dev); 112238106Sdes vid = pci_get_vendor(dev); 113238106Sdes 114238106Sdes for (i = 0; i < N(siba_devices); i++) { 115238106Sdes if (siba_devices[i].did == did && siba_devices[i].vid == vid) { 116238106Sdes device_set_desc(dev, siba_devices[i].desc); 117238106Sdes return (BUS_PROBE_DEFAULT); 118238106Sdes } 119238106Sdes } 120238106Sdes return (ENXIO); 121238106Sdes} 122238106Sdes 123238106Sdesstatic int 124238106Sdessiba_bwn_attach(device_t dev) 125238106Sdes{ 126238106Sdes struct siba_bwn_softc *ssc = device_get_softc(dev); 127238106Sdes struct siba_softc *siba = &ssc->ssc_siba; 128238106Sdes 129238106Sdes siba->siba_dev = dev; 130238106Sdes siba->siba_type = SIBA_TYPE_PCI; 131238106Sdes 132238106Sdes /* 133238106Sdes * Enable bus mastering. 134238106Sdes */ 135238106Sdes pci_enable_busmaster(dev); 136238106Sdes 137238106Sdes /* 138238106Sdes * Setup memory-mapping of PCI registers. 139238106Sdes */ 140238106Sdes siba->siba_mem_rid = SIBA_PCIR_BAR; 141238106Sdes siba->siba_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 142238106Sdes &siba->siba_mem_rid, RF_ACTIVE); 143238106Sdes if (siba->siba_mem_res == NULL) { 144238106Sdes device_printf(dev, "cannot map register space\n"); 145238106Sdes return (ENXIO); 146238106Sdes } 147238106Sdes siba->siba_mem_bt = rman_get_bustag(siba->siba_mem_res); 148238106Sdes siba->siba_mem_bh = rman_get_bushandle(siba->siba_mem_res); 149238106Sdes 150238106Sdes /* Get more PCI information */ 151238106Sdes siba->siba_pci_did = pci_get_device(dev); 152238106Sdes siba->siba_pci_vid = pci_get_vendor(dev); 153238106Sdes siba->siba_pci_subvid = pci_get_subvendor(dev); 154238106Sdes siba->siba_pci_subdid = pci_get_subdevice(dev); 155238106Sdes siba->siba_pci_revid = pci_get_revid(dev); 156238106Sdes 157238106Sdes return (siba_core_attach(siba)); 158238106Sdes} 159238106Sdes 160238106Sdesstatic int 161238106Sdessiba_bwn_detach(device_t dev) 162238106Sdes{ 163238106Sdes struct siba_bwn_softc *ssc = device_get_softc(dev); 164238106Sdes struct siba_softc *siba = &ssc->ssc_siba; 165238106Sdes 166238106Sdes /* check if device was removed */ 167238106Sdes siba->siba_invalid = !bus_child_present(dev); 168238106Sdes 169238106Sdes pci_disable_busmaster(dev); 170238106Sdes bus_generic_detach(dev); 171238106Sdes siba_core_detach(siba); 172238106Sdes 173238106Sdes bus_release_resource(dev, SYS_RES_MEMORY, BS_BAR, siba->siba_mem_res); 174238106Sdes 175238106Sdes return (0); 176238106Sdes} 177238106Sdes 178238106Sdesstatic int 179238106Sdessiba_bwn_shutdown(device_t dev) 180238106Sdes{ 181238106Sdes device_t *devlistp; 182238106Sdes int devcnt, error = 0, i; 183238106Sdes 184238106Sdes error = device_get_children(dev, &devlistp, &devcnt); 185238106Sdes if (error != 0) 186238106Sdes return (error); 187238106Sdes 188238106Sdes for (i = 0 ; i < devcnt ; i++) 189238106Sdes device_shutdown(devlistp[i]); 190238106Sdes free(devlistp, M_TEMP); 191238106Sdes return (0); 192238106Sdes} 193238106Sdes 194238106Sdesstatic int 195238106Sdessiba_bwn_suspend(device_t dev) 196238106Sdes{ 197238106Sdes struct siba_bwn_softc *ssc = device_get_softc(dev); 198238106Sdes struct siba_softc *siba = &ssc->ssc_siba; 199238106Sdes device_t *devlistp; 200238106Sdes int devcnt, error = 0, i, j; 201238106Sdes 202238106Sdes error = device_get_children(dev, &devlistp, &devcnt); 203238106Sdes if (error != 0) 204238106Sdes return (error); 205238106Sdes 206238106Sdes for (i = 0 ; i < devcnt ; i++) { 207238106Sdes error = DEVICE_SUSPEND(devlistp[i]); 208238106Sdes if (error) { 209238106Sdes for (j = 0; j < i; i++) 210238106Sdes DEVICE_RESUME(devlistp[j]); 211238106Sdes return (error); 212238106Sdes } 213238106Sdes } 214238106Sdes free(devlistp, M_TEMP); 215238106Sdes return (siba_core_suspend(siba)); 216238106Sdes} 217238106Sdes 218238106Sdesstatic int 219238106Sdessiba_bwn_resume(device_t dev) 220238106Sdes{ 221238106Sdes struct siba_bwn_softc *ssc = device_get_softc(dev); 222238106Sdes struct siba_softc *siba = &ssc->ssc_siba; 223238106Sdes device_t *devlistp; 224238106Sdes int devcnt, error = 0, i; 225238106Sdes 226238106Sdes error = siba_core_resume(siba); 227238106Sdes if (error != 0) 228238106Sdes return (error); 229238106Sdes 230238106Sdes error = device_get_children(dev, &devlistp, &devcnt); 231238106Sdes if (error != 0) 232238106Sdes return (error); 233238106Sdes 234238106Sdes for (i = 0 ; i < devcnt ; i++) 235238106Sdes DEVICE_RESUME(devlistp[i]); 236238106Sdes free(devlistp, M_TEMP); 237238106Sdes return (0); 238238106Sdes} 239238106Sdes 240238106Sdes/* proxying to the parent */ 241238106Sdesstatic struct resource * 242238106Sdessiba_bwn_alloc_resource(device_t dev, device_t child, int type, int *rid, 243238106Sdes u_long start, u_long end, u_long count, u_int flags) 244238106Sdes{ 245238106Sdes 246238106Sdes return (BUS_ALLOC_RESOURCE(device_get_parent(dev), dev, 247238106Sdes type, rid, start, end, count, flags)); 248238106Sdes} 249238106Sdes 250238106Sdes/* proxying to the parent */ 251238106Sdesstatic int 252238106Sdessiba_bwn_release_resource(device_t dev, device_t child, int type, 253238106Sdes int rid, struct resource *r) 254238106Sdes{ 255238106Sdes 256238106Sdes return (BUS_RELEASE_RESOURCE(device_get_parent(dev), dev, type, 257238106Sdes rid, r)); 258238106Sdes} 259238106Sdes 260238106Sdes/* proxying to the parent */ 261238106Sdesstatic int 262238106Sdessiba_bwn_setup_intr(device_t dev, device_t child, struct resource *irq, 263238106Sdes int flags, driver_filter_t *filter, driver_intr_t *intr, void *arg, 264238106Sdes void **cookiep) 265238106Sdes{ 266238106Sdes 267238106Sdes return (BUS_SETUP_INTR(device_get_parent(dev), dev, irq, flags, 268238106Sdes filter, intr, arg, cookiep)); 269238106Sdes} 270238106Sdes 271238106Sdes/* proxying to the parent */ 272238106Sdesstatic int 273238106Sdessiba_bwn_teardown_intr(device_t dev, device_t child, struct resource *irq, 274238106Sdes void *cookie) 275238106Sdes{ 276238106Sdes 277238106Sdes return (BUS_TEARDOWN_INTR(device_get_parent(dev), dev, irq, cookie)); 278238106Sdes} 279238106Sdes 280238106Sdesstatic int 281238106Sdessiba_bwn_find_extcap(device_t dev, device_t child, int capability, 282238106Sdes int *capreg) 283238106Sdes{ 284238106Sdes 285238106Sdes return (pci_find_extcap(dev, capability, capreg)); 286238106Sdes} 287238106Sdes 288238106Sdesstatic int 289238106Sdessiba_bwn_alloc_msi(device_t dev, device_t child, int *count) 290238106Sdes{ 291238106Sdes struct siba_bwn_softc *ssc; 292238106Sdes int error; 293238106Sdes 294238106Sdes ssc = device_get_softc(dev); 295238106Sdes if (ssc->ssc_msi_child != NULL) 296238106Sdes return (EBUSY); 297238106Sdes error = pci_alloc_msi(dev, count); 298238106Sdes if (error == 0) 299238106Sdes ssc->ssc_msi_child = child; 300238106Sdes return (error); 301238106Sdes} 302238106Sdes 303238106Sdesstatic int 304238106Sdessiba_bwn_release_msi(device_t dev, device_t child) 305238106Sdes{ 306238106Sdes struct siba_bwn_softc *ssc; 307238106Sdes int error; 308238106Sdes 309238106Sdes ssc = device_get_softc(dev); 310238106Sdes if (ssc->ssc_msi_child != child) 311238106Sdes return (ENXIO); 312238106Sdes error = pci_release_msi(dev); 313238106Sdes if (error == 0) 314238106Sdes ssc->ssc_msi_child = NULL; 315238106Sdes return (error); 316238106Sdes} 317238106Sdes 318238106Sdesstatic int 319238106Sdessiba_bwn_msi_count(device_t dev, device_t child) 320238106Sdes{ 321238106Sdes 322238106Sdes return (pci_msi_count(dev)); 323238106Sdes} 324238106Sdes 325238106Sdesstatic int 326238106Sdessiba_bwn_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) 327238106Sdes{ 328238106Sdes struct siba_dev_softc *sd; 329238106Sdes struct siba_softc *siba;; 330238106Sdes 331238106Sdes sd = device_get_ivars(child); 332238106Sdes siba = sd->sd_bus; 333238106Sdes 334238106Sdes switch (which) { 335238106Sdes case SIBA_IVAR_VENDOR: 336238106Sdes *result = sd->sd_id.sd_vendor; 337238106Sdes break; 338238106Sdes case SIBA_IVAR_DEVICE: 339238106Sdes *result = sd->sd_id.sd_device; 340238106Sdes break; 341238106Sdes case SIBA_IVAR_REVID: 342238106Sdes *result = sd->sd_id.sd_rev; 343238106Sdes break; 344238106Sdes case SIBA_IVAR_PCI_VENDOR: 345238106Sdes *result = siba->siba_pci_vid; 346238106Sdes break; 347238106Sdes case SIBA_IVAR_PCI_DEVICE: 348238106Sdes *result = siba->siba_pci_did; 349238106Sdes break; 350238106Sdes case SIBA_IVAR_PCI_SUBVENDOR: 351238106Sdes *result = siba->siba_pci_subvid; 352238106Sdes break; 353238106Sdes case SIBA_IVAR_PCI_SUBDEVICE: 354238106Sdes *result = siba->siba_pci_subdid; 355238106Sdes break; 356238106Sdes case SIBA_IVAR_PCI_REVID: 357238106Sdes *result = siba->siba_pci_revid; 358238106Sdes break; 359238106Sdes case SIBA_IVAR_CHIPID: 360238106Sdes *result = siba->siba_chipid; 361238106Sdes break; 362238106Sdes case SIBA_IVAR_CHIPREV: 363238106Sdes *result = siba->siba_chiprev; 364238106Sdes break; 365238106Sdes case SIBA_IVAR_CHIPPKG: 366238106Sdes *result = siba->siba_chippkg; 367238106Sdes break; 368238106Sdes case SIBA_IVAR_TYPE: 369238106Sdes *result = siba->siba_type; 370238106Sdes break; 371238106Sdes case SIBA_IVAR_CC_PMUFREQ: 372238106Sdes *result = siba->siba_cc.scc_pmu.freq; 373238106Sdes break; 374238106Sdes case SIBA_IVAR_CC_CAPS: 375238106Sdes *result = siba->siba_cc.scc_caps; 376238106Sdes break; 377238106Sdes case SIBA_IVAR_CC_POWERDELAY: 378238106Sdes *result = siba->siba_cc.scc_powerup_delay; 379238106Sdes break; 380238106Sdes case SIBA_IVAR_PCICORE_REVID: 381238106Sdes *result = siba->siba_pci.spc_dev->sd_id.sd_rev; 382238106Sdes break; 383238106Sdes default: 384238106Sdes return (ENOENT); 385238106Sdes } 386238106Sdes 387238106Sdes return (0); 388238106Sdes} 389238106Sdes 390238106Sdesstatic device_method_t siba_bwn_methods[] = { 391238106Sdes /* Device interface */ 392238106Sdes DEVMETHOD(device_probe, siba_bwn_probe), 393238106Sdes DEVMETHOD(device_attach, siba_bwn_attach), 394238106Sdes DEVMETHOD(device_detach, siba_bwn_detach), 395238106Sdes DEVMETHOD(device_shutdown, siba_bwn_shutdown), 396238106Sdes DEVMETHOD(device_suspend, siba_bwn_suspend), 397238106Sdes DEVMETHOD(device_resume, siba_bwn_resume), 398238106Sdes 399238106Sdes /* Bus interface */ 400238106Sdes DEVMETHOD(bus_alloc_resource, siba_bwn_alloc_resource), 401238106Sdes DEVMETHOD(bus_release_resource, siba_bwn_release_resource), 402238106Sdes DEVMETHOD(bus_read_ivar, siba_bwn_read_ivar), 403238106Sdes DEVMETHOD(bus_setup_intr, siba_bwn_setup_intr), 404238106Sdes DEVMETHOD(bus_teardown_intr, siba_bwn_teardown_intr), 405238106Sdes 406238106Sdes /* PCI interface */ 407238106Sdes DEVMETHOD(pci_find_extcap, siba_bwn_find_extcap), 408238106Sdes DEVMETHOD(pci_alloc_msi, siba_bwn_alloc_msi), 409238106Sdes DEVMETHOD(pci_release_msi, siba_bwn_release_msi), 410238106Sdes DEVMETHOD(pci_msi_count, siba_bwn_msi_count), 411238106Sdes 412238106Sdes { 0,0 } 413238106Sdes}; 414238106Sdesstatic driver_t siba_bwn_driver = { 415238106Sdes "siba_bwn", 416238106Sdes siba_bwn_methods, 417238106Sdes sizeof(struct siba_bwn_softc) 418238106Sdes}; 419238106Sdesstatic devclass_t siba_bwn_devclass; 420238106SdesDRIVER_MODULE(siba_bwn, pci, siba_bwn_driver, siba_bwn_devclass, 0, 0); 421238106SdesMODULE_VERSION(siba_bwn, 1); 422238106Sdes