1203319Sweongyo/*- 2203319Sweongyo * Copyright (c) 2009-2010 Weongyo Jeong <weongyo@freebsd.org> 3203319Sweongyo * All rights reserved. 4203319Sweongyo * 5203319Sweongyo * Redistribution and use in source and binary forms, with or without 6203319Sweongyo * modification, are permitted provided that the following conditions 7203319Sweongyo * are met: 8203319Sweongyo * 1. Redistributions of source code must retain the above copyright 9203319Sweongyo * notice, this list of conditions and the following disclaimer, 10203319Sweongyo * without modification. 11203319Sweongyo * 2. Redistributions in binary form must reproduce at minimum a disclaimer 12203319Sweongyo * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any 13203319Sweongyo * redistribution must be conditioned upon including a substantially 14203319Sweongyo * similar Disclaimer requirement for further binary redistribution. 15203319Sweongyo * 16203319Sweongyo * NO WARRANTY 17203319Sweongyo * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18203319Sweongyo * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19203319Sweongyo * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY 20203319Sweongyo * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 21203319Sweongyo * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, 22203319Sweongyo * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23203319Sweongyo * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24203319Sweongyo * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 25203319Sweongyo * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26203319Sweongyo * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 27203319Sweongyo * THE POSSIBILITY OF SUCH DAMAGES. 28203319Sweongyo */ 29203319Sweongyo 30203319Sweongyo#include <sys/cdefs.h> 31203319Sweongyo__FBSDID("$FreeBSD: releng/10.2/sys/dev/siba/siba_bwn.c 232472 2012-03-03 18:08:57Z jhb $"); 32203319Sweongyo 33203319Sweongyo/* 34203319Sweongyo * Sonics Silicon Backplane front-end for bwn(4). 35203319Sweongyo */ 36203319Sweongyo 37203319Sweongyo#include <sys/param.h> 38203319Sweongyo#include <sys/systm.h> 39203319Sweongyo#include <sys/module.h> 40203319Sweongyo#include <sys/kernel.h> 41203319Sweongyo#include <sys/lock.h> 42203319Sweongyo#include <sys/mutex.h> 43203319Sweongyo#include <sys/errno.h> 44203319Sweongyo#include <machine/bus.h> 45203319Sweongyo#include <machine/resource.h> 46203319Sweongyo#include <sys/bus.h> 47203319Sweongyo#include <sys/rman.h> 48203319Sweongyo#include <sys/socket.h> 49203319Sweongyo 50203319Sweongyo#include <net/if.h> 51203319Sweongyo#include <net/if_media.h> 52203319Sweongyo#include <net/if_arp.h> 53203319Sweongyo 54203319Sweongyo#include <dev/pci/pcivar.h> 55203319Sweongyo#include <dev/pci/pcireg.h> 56203319Sweongyo 57203319Sweongyo#include <dev/siba/siba_ids.h> 58203319Sweongyo#include <dev/siba/sibareg.h> 59203319Sweongyo#include <dev/siba/sibavar.h> 60203319Sweongyo 61203319Sweongyo/* 62203319Sweongyo * PCI glue. 63203319Sweongyo */ 64203319Sweongyo 65203319Sweongyostruct siba_bwn_softc { 66203319Sweongyo /* Child driver using MSI. */ 67203319Sweongyo device_t ssc_msi_child; 68203319Sweongyo struct siba_softc ssc_siba; 69203319Sweongyo}; 70203319Sweongyo 71203319Sweongyo#define BS_BAR 0x10 72203319Sweongyo#define PCI_VENDOR_BROADCOM 0x14e4 73203319Sweongyo#define N(a) (sizeof(a) / sizeof(a[0])) 74203319Sweongyo 75203319Sweongyostatic const struct siba_dev { 76203319Sweongyo uint16_t vid; 77203319Sweongyo uint16_t did; 78203319Sweongyo const char *desc; 79203319Sweongyo} siba_devices[] = { 80203319Sweongyo { PCI_VENDOR_BROADCOM, 0x4301, "Broadcom BCM4301 802.11b Wireless" }, 81203319Sweongyo { PCI_VENDOR_BROADCOM, 0x4306, "Unknown" }, 82203319Sweongyo { PCI_VENDOR_BROADCOM, 0x4307, "Broadcom BCM4307 802.11b Wireless" }, 83203319Sweongyo { PCI_VENDOR_BROADCOM, 0x4311, "Broadcom BCM4311 802.11b/g Wireless" }, 84203319Sweongyo { PCI_VENDOR_BROADCOM, 0x4312, 85203319Sweongyo "Broadcom BCM4312 802.11a/b/g Wireless" }, 86203319Sweongyo { PCI_VENDOR_BROADCOM, 0x4315, "Broadcom BCM4312 802.11b/g Wireless" }, 87203319Sweongyo { PCI_VENDOR_BROADCOM, 0x4318, "Broadcom BCM4318 802.11b/g Wireless" }, 88203319Sweongyo { PCI_VENDOR_BROADCOM, 0x4319, 89203319Sweongyo "Broadcom BCM4318 802.11a/b/g Wireless" }, 90203319Sweongyo { PCI_VENDOR_BROADCOM, 0x4320, "Broadcom BCM4306 802.11b/g Wireless" }, 91203319Sweongyo { PCI_VENDOR_BROADCOM, 0x4321, "Broadcom BCM4306 802.11a Wireless" }, 92203319Sweongyo { PCI_VENDOR_BROADCOM, 0x4324, 93203319Sweongyo "Broadcom BCM4309 802.11a/b/g Wireless" }, 94203319Sweongyo { PCI_VENDOR_BROADCOM, 0x4325, "Broadcom BCM4306 802.11b/g Wireless" }, 95203319Sweongyo { PCI_VENDOR_BROADCOM, 0x4328, "Unknown" }, 96203319Sweongyo { PCI_VENDOR_BROADCOM, 0x4329, "Unknown" }, 97203319Sweongyo { PCI_VENDOR_BROADCOM, 0x432b, "Unknown" } 98203319Sweongyo}; 99203319Sweongyo 100203319Sweongyoint siba_core_attach(struct siba_softc *); 101203319Sweongyoint siba_core_detach(struct siba_softc *); 102203319Sweongyoint siba_core_suspend(struct siba_softc *); 103203319Sweongyoint siba_core_resume(struct siba_softc *); 104203319Sweongyo 105203319Sweongyostatic int 106203319Sweongyosiba_bwn_probe(device_t dev) 107203319Sweongyo{ 108203319Sweongyo int i; 109203319Sweongyo uint16_t did, vid; 110203319Sweongyo 111203319Sweongyo did = pci_get_device(dev); 112203319Sweongyo vid = pci_get_vendor(dev); 113203319Sweongyo 114203319Sweongyo for (i = 0; i < N(siba_devices); i++) { 115203319Sweongyo if (siba_devices[i].did == did && siba_devices[i].vid == vid) { 116203319Sweongyo device_set_desc(dev, siba_devices[i].desc); 117203319Sweongyo return (BUS_PROBE_DEFAULT); 118203319Sweongyo } 119203319Sweongyo } 120203319Sweongyo return (ENXIO); 121203319Sweongyo} 122203319Sweongyo 123203319Sweongyostatic int 124203319Sweongyosiba_bwn_attach(device_t dev) 125203319Sweongyo{ 126203319Sweongyo struct siba_bwn_softc *ssc = device_get_softc(dev); 127203319Sweongyo struct siba_softc *siba = &ssc->ssc_siba; 128203319Sweongyo 129203319Sweongyo siba->siba_dev = dev; 130203319Sweongyo siba->siba_type = SIBA_TYPE_PCI; 131203319Sweongyo 132203319Sweongyo /* 133203319Sweongyo * Enable bus mastering. 134203319Sweongyo */ 135203319Sweongyo pci_enable_busmaster(dev); 136203319Sweongyo 137203319Sweongyo /* 138203319Sweongyo * Setup memory-mapping of PCI registers. 139203319Sweongyo */ 140203319Sweongyo siba->siba_mem_rid = SIBA_PCIR_BAR; 141203319Sweongyo siba->siba_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 142203319Sweongyo &siba->siba_mem_rid, RF_ACTIVE); 143203319Sweongyo if (siba->siba_mem_res == NULL) { 144203319Sweongyo device_printf(dev, "cannot map register space\n"); 145203319Sweongyo return (ENXIO); 146203319Sweongyo } 147203319Sweongyo siba->siba_mem_bt = rman_get_bustag(siba->siba_mem_res); 148203319Sweongyo siba->siba_mem_bh = rman_get_bushandle(siba->siba_mem_res); 149203319Sweongyo 150203319Sweongyo /* Get more PCI information */ 151203319Sweongyo siba->siba_pci_did = pci_get_device(dev); 152203319Sweongyo siba->siba_pci_vid = pci_get_vendor(dev); 153203319Sweongyo siba->siba_pci_subvid = pci_get_subvendor(dev); 154203319Sweongyo siba->siba_pci_subdid = pci_get_subdevice(dev); 155204922Sweongyo siba->siba_pci_revid = pci_get_revid(dev); 156203319Sweongyo 157203319Sweongyo return (siba_core_attach(siba)); 158203319Sweongyo} 159203319Sweongyo 160203319Sweongyostatic int 161203319Sweongyosiba_bwn_detach(device_t dev) 162203319Sweongyo{ 163203319Sweongyo struct siba_bwn_softc *ssc = device_get_softc(dev); 164203319Sweongyo struct siba_softc *siba = &ssc->ssc_siba; 165203319Sweongyo 166203319Sweongyo /* check if device was removed */ 167203319Sweongyo siba->siba_invalid = !bus_child_present(dev); 168203319Sweongyo 169203319Sweongyo pci_disable_busmaster(dev); 170203319Sweongyo bus_generic_detach(dev); 171203319Sweongyo siba_core_detach(siba); 172203319Sweongyo 173203319Sweongyo bus_release_resource(dev, SYS_RES_MEMORY, BS_BAR, siba->siba_mem_res); 174203319Sweongyo 175203319Sweongyo return (0); 176203319Sweongyo} 177203319Sweongyo 178203319Sweongyostatic int 179203319Sweongyosiba_bwn_shutdown(device_t dev) 180203319Sweongyo{ 181203319Sweongyo device_t *devlistp; 182203319Sweongyo int devcnt, error = 0, i; 183203319Sweongyo 184203319Sweongyo error = device_get_children(dev, &devlistp, &devcnt); 185203319Sweongyo if (error != 0) 186203319Sweongyo return (error); 187203319Sweongyo 188203319Sweongyo for (i = 0 ; i < devcnt ; i++) 189203319Sweongyo device_shutdown(devlistp[i]); 190203319Sweongyo free(devlistp, M_TEMP); 191203319Sweongyo return (0); 192203319Sweongyo} 193203319Sweongyo 194203319Sweongyostatic int 195203319Sweongyosiba_bwn_suspend(device_t dev) 196203319Sweongyo{ 197203319Sweongyo struct siba_bwn_softc *ssc = device_get_softc(dev); 198203319Sweongyo struct siba_softc *siba = &ssc->ssc_siba; 199203319Sweongyo device_t *devlistp; 200203319Sweongyo int devcnt, error = 0, i, j; 201203319Sweongyo 202203319Sweongyo error = device_get_children(dev, &devlistp, &devcnt); 203203319Sweongyo if (error != 0) 204203319Sweongyo return (error); 205203319Sweongyo 206203319Sweongyo for (i = 0 ; i < devcnt ; i++) { 207203319Sweongyo error = DEVICE_SUSPEND(devlistp[i]); 208203319Sweongyo if (error) { 209226149Sbrueffer for (j = 0; j < i; j++) 210203319Sweongyo DEVICE_RESUME(devlistp[j]); 211226406Sbrueffer free(devlistp, M_TEMP); 212203319Sweongyo return (error); 213203319Sweongyo } 214203319Sweongyo } 215203319Sweongyo free(devlistp, M_TEMP); 216203319Sweongyo return (siba_core_suspend(siba)); 217203319Sweongyo} 218203319Sweongyo 219203319Sweongyostatic int 220203319Sweongyosiba_bwn_resume(device_t dev) 221203319Sweongyo{ 222203319Sweongyo struct siba_bwn_softc *ssc = device_get_softc(dev); 223203319Sweongyo struct siba_softc *siba = &ssc->ssc_siba; 224203319Sweongyo device_t *devlistp; 225203319Sweongyo int devcnt, error = 0, i; 226203319Sweongyo 227203319Sweongyo error = siba_core_resume(siba); 228203319Sweongyo if (error != 0) 229203319Sweongyo return (error); 230203319Sweongyo 231203319Sweongyo error = device_get_children(dev, &devlistp, &devcnt); 232203319Sweongyo if (error != 0) 233203319Sweongyo return (error); 234203319Sweongyo 235203319Sweongyo for (i = 0 ; i < devcnt ; i++) 236203319Sweongyo DEVICE_RESUME(devlistp[i]); 237203319Sweongyo free(devlistp, M_TEMP); 238203319Sweongyo return (0); 239203319Sweongyo} 240203319Sweongyo 241203319Sweongyo/* proxying to the parent */ 242203319Sweongyostatic struct resource * 243203319Sweongyosiba_bwn_alloc_resource(device_t dev, device_t child, int type, int *rid, 244203319Sweongyo u_long start, u_long end, u_long count, u_int flags) 245203319Sweongyo{ 246203319Sweongyo 247203319Sweongyo return (BUS_ALLOC_RESOURCE(device_get_parent(dev), dev, 248203319Sweongyo type, rid, start, end, count, flags)); 249203319Sweongyo} 250203319Sweongyo 251203319Sweongyo/* proxying to the parent */ 252203319Sweongyostatic int 253203319Sweongyosiba_bwn_release_resource(device_t dev, device_t child, int type, 254203319Sweongyo int rid, struct resource *r) 255203319Sweongyo{ 256203319Sweongyo 257203319Sweongyo return (BUS_RELEASE_RESOURCE(device_get_parent(dev), dev, type, 258203319Sweongyo rid, r)); 259203319Sweongyo} 260203319Sweongyo 261203319Sweongyo/* proxying to the parent */ 262203319Sweongyostatic int 263203319Sweongyosiba_bwn_setup_intr(device_t dev, device_t child, struct resource *irq, 264203319Sweongyo int flags, driver_filter_t *filter, driver_intr_t *intr, void *arg, 265203319Sweongyo void **cookiep) 266203319Sweongyo{ 267203319Sweongyo 268203319Sweongyo return (BUS_SETUP_INTR(device_get_parent(dev), dev, irq, flags, 269203319Sweongyo filter, intr, arg, cookiep)); 270203319Sweongyo} 271203319Sweongyo 272203319Sweongyo/* proxying to the parent */ 273203319Sweongyostatic int 274203319Sweongyosiba_bwn_teardown_intr(device_t dev, device_t child, struct resource *irq, 275203319Sweongyo void *cookie) 276203319Sweongyo{ 277203319Sweongyo 278203319Sweongyo return (BUS_TEARDOWN_INTR(device_get_parent(dev), dev, irq, cookie)); 279203319Sweongyo} 280203319Sweongyo 281203319Sweongyostatic int 282232472Sjhbsiba_bwn_find_cap(device_t dev, device_t child, int capability, 283232472Sjhb int *capreg) 284232472Sjhb{ 285232472Sjhb 286232472Sjhb return (pci_find_cap(dev, capability, capreg)); 287232472Sjhb} 288232472Sjhb 289232472Sjhbstatic int 290203319Sweongyosiba_bwn_find_extcap(device_t dev, device_t child, int capability, 291203319Sweongyo int *capreg) 292203319Sweongyo{ 293203319Sweongyo 294203319Sweongyo return (pci_find_extcap(dev, capability, capreg)); 295203319Sweongyo} 296203319Sweongyo 297203319Sweongyostatic int 298232472Sjhbsiba_bwn_find_htcap(device_t dev, device_t child, int capability, 299232472Sjhb int *capreg) 300232472Sjhb{ 301232472Sjhb 302232472Sjhb return (pci_find_htcap(dev, capability, capreg)); 303232472Sjhb} 304232472Sjhb 305232472Sjhbstatic int 306203319Sweongyosiba_bwn_alloc_msi(device_t dev, device_t child, int *count) 307203319Sweongyo{ 308203319Sweongyo struct siba_bwn_softc *ssc; 309203319Sweongyo int error; 310203319Sweongyo 311203319Sweongyo ssc = device_get_softc(dev); 312203319Sweongyo if (ssc->ssc_msi_child != NULL) 313203319Sweongyo return (EBUSY); 314203319Sweongyo error = pci_alloc_msi(dev, count); 315203319Sweongyo if (error == 0) 316203319Sweongyo ssc->ssc_msi_child = child; 317203319Sweongyo return (error); 318203319Sweongyo} 319203319Sweongyo 320203319Sweongyostatic int 321203319Sweongyosiba_bwn_release_msi(device_t dev, device_t child) 322203319Sweongyo{ 323203319Sweongyo struct siba_bwn_softc *ssc; 324203319Sweongyo int error; 325203319Sweongyo 326203319Sweongyo ssc = device_get_softc(dev); 327203319Sweongyo if (ssc->ssc_msi_child != child) 328203319Sweongyo return (ENXIO); 329203319Sweongyo error = pci_release_msi(dev); 330203319Sweongyo if (error == 0) 331203319Sweongyo ssc->ssc_msi_child = NULL; 332203319Sweongyo return (error); 333203319Sweongyo} 334203319Sweongyo 335203319Sweongyostatic int 336203319Sweongyosiba_bwn_msi_count(device_t dev, device_t child) 337203319Sweongyo{ 338203319Sweongyo 339203319Sweongyo return (pci_msi_count(dev)); 340203319Sweongyo} 341203319Sweongyo 342204922Sweongyostatic int 343204922Sweongyosiba_bwn_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) 344204922Sweongyo{ 345204922Sweongyo struct siba_dev_softc *sd; 346216227Skevlo struct siba_softc *siba; 347204922Sweongyo 348204922Sweongyo sd = device_get_ivars(child); 349204922Sweongyo siba = sd->sd_bus; 350204922Sweongyo 351204922Sweongyo switch (which) { 352204922Sweongyo case SIBA_IVAR_VENDOR: 353204922Sweongyo *result = sd->sd_id.sd_vendor; 354204922Sweongyo break; 355204922Sweongyo case SIBA_IVAR_DEVICE: 356204922Sweongyo *result = sd->sd_id.sd_device; 357204922Sweongyo break; 358204922Sweongyo case SIBA_IVAR_REVID: 359204922Sweongyo *result = sd->sd_id.sd_rev; 360204922Sweongyo break; 361204922Sweongyo case SIBA_IVAR_PCI_VENDOR: 362204922Sweongyo *result = siba->siba_pci_vid; 363204922Sweongyo break; 364204922Sweongyo case SIBA_IVAR_PCI_DEVICE: 365204922Sweongyo *result = siba->siba_pci_did; 366204922Sweongyo break; 367204922Sweongyo case SIBA_IVAR_PCI_SUBVENDOR: 368204922Sweongyo *result = siba->siba_pci_subvid; 369204922Sweongyo break; 370204922Sweongyo case SIBA_IVAR_PCI_SUBDEVICE: 371204922Sweongyo *result = siba->siba_pci_subdid; 372204922Sweongyo break; 373204922Sweongyo case SIBA_IVAR_PCI_REVID: 374204922Sweongyo *result = siba->siba_pci_revid; 375204922Sweongyo break; 376204922Sweongyo case SIBA_IVAR_CHIPID: 377204922Sweongyo *result = siba->siba_chipid; 378204922Sweongyo break; 379204922Sweongyo case SIBA_IVAR_CHIPREV: 380204922Sweongyo *result = siba->siba_chiprev; 381204922Sweongyo break; 382204922Sweongyo case SIBA_IVAR_CHIPPKG: 383204922Sweongyo *result = siba->siba_chippkg; 384204922Sweongyo break; 385204922Sweongyo case SIBA_IVAR_TYPE: 386204922Sweongyo *result = siba->siba_type; 387204922Sweongyo break; 388204922Sweongyo case SIBA_IVAR_CC_PMUFREQ: 389204922Sweongyo *result = siba->siba_cc.scc_pmu.freq; 390204922Sweongyo break; 391204922Sweongyo case SIBA_IVAR_CC_CAPS: 392204922Sweongyo *result = siba->siba_cc.scc_caps; 393204922Sweongyo break; 394204922Sweongyo case SIBA_IVAR_CC_POWERDELAY: 395204922Sweongyo *result = siba->siba_cc.scc_powerup_delay; 396204922Sweongyo break; 397204922Sweongyo case SIBA_IVAR_PCICORE_REVID: 398204922Sweongyo *result = siba->siba_pci.spc_dev->sd_id.sd_rev; 399204922Sweongyo break; 400204922Sweongyo default: 401204922Sweongyo return (ENOENT); 402204922Sweongyo } 403204922Sweongyo 404204922Sweongyo return (0); 405204922Sweongyo} 406204922Sweongyo 407203319Sweongyostatic device_method_t siba_bwn_methods[] = { 408203319Sweongyo /* Device interface */ 409203319Sweongyo DEVMETHOD(device_probe, siba_bwn_probe), 410203319Sweongyo DEVMETHOD(device_attach, siba_bwn_attach), 411203319Sweongyo DEVMETHOD(device_detach, siba_bwn_detach), 412203319Sweongyo DEVMETHOD(device_shutdown, siba_bwn_shutdown), 413203319Sweongyo DEVMETHOD(device_suspend, siba_bwn_suspend), 414203319Sweongyo DEVMETHOD(device_resume, siba_bwn_resume), 415203319Sweongyo 416203319Sweongyo /* Bus interface */ 417203319Sweongyo DEVMETHOD(bus_alloc_resource, siba_bwn_alloc_resource), 418203319Sweongyo DEVMETHOD(bus_release_resource, siba_bwn_release_resource), 419204922Sweongyo DEVMETHOD(bus_read_ivar, siba_bwn_read_ivar), 420203319Sweongyo DEVMETHOD(bus_setup_intr, siba_bwn_setup_intr), 421203319Sweongyo DEVMETHOD(bus_teardown_intr, siba_bwn_teardown_intr), 422203319Sweongyo 423203319Sweongyo /* PCI interface */ 424232472Sjhb DEVMETHOD(pci_find_cap, siba_bwn_find_cap), 425203319Sweongyo DEVMETHOD(pci_find_extcap, siba_bwn_find_extcap), 426232472Sjhb DEVMETHOD(pci_find_htcap, siba_bwn_find_htcap), 427203319Sweongyo DEVMETHOD(pci_alloc_msi, siba_bwn_alloc_msi), 428203319Sweongyo DEVMETHOD(pci_release_msi, siba_bwn_release_msi), 429203319Sweongyo DEVMETHOD(pci_msi_count, siba_bwn_msi_count), 430203319Sweongyo 431227848Smarius DEVMETHOD_END 432203319Sweongyo}; 433203319Sweongyostatic driver_t siba_bwn_driver = { 434203319Sweongyo "siba_bwn", 435203319Sweongyo siba_bwn_methods, 436203319Sweongyo sizeof(struct siba_bwn_softc) 437203319Sweongyo}; 438203319Sweongyostatic devclass_t siba_bwn_devclass; 439203319SweongyoDRIVER_MODULE(siba_bwn, pci, siba_bwn_driver, siba_bwn_devclass, 0, 0); 440203319SweongyoMODULE_VERSION(siba_bwn, 1); 441