14ad7e9b0SAdrian Chadd /*- 24ad7e9b0SAdrian Chadd * Copyright (c) 2015 Landon Fuller <landon@landonf.org> 34ad7e9b0SAdrian Chadd * All rights reserved. 44ad7e9b0SAdrian Chadd * 54ad7e9b0SAdrian Chadd * Redistribution and use in source and binary forms, with or without 64ad7e9b0SAdrian Chadd * modification, are permitted provided that the following conditions 74ad7e9b0SAdrian Chadd * are met: 84ad7e9b0SAdrian Chadd * 1. Redistributions of source code must retain the above copyright 94ad7e9b0SAdrian Chadd * notice, this list of conditions and the following disclaimer, 104ad7e9b0SAdrian Chadd * without modification. 114ad7e9b0SAdrian Chadd * 2. Redistributions in binary form must reproduce at minimum a disclaimer 124ad7e9b0SAdrian Chadd * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any 134ad7e9b0SAdrian Chadd * redistribution must be conditioned upon including a substantially 144ad7e9b0SAdrian Chadd * similar Disclaimer requirement for further binary redistribution. 154ad7e9b0SAdrian Chadd * 164ad7e9b0SAdrian Chadd * NO WARRANTY 174ad7e9b0SAdrian Chadd * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 184ad7e9b0SAdrian Chadd * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 194ad7e9b0SAdrian Chadd * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY 204ad7e9b0SAdrian Chadd * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 214ad7e9b0SAdrian Chadd * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, 224ad7e9b0SAdrian Chadd * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 234ad7e9b0SAdrian Chadd * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 244ad7e9b0SAdrian Chadd * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 254ad7e9b0SAdrian Chadd * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 264ad7e9b0SAdrian Chadd * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 274ad7e9b0SAdrian Chadd * THE POSSIBILITY OF SUCH DAMAGES. 284ad7e9b0SAdrian Chadd */ 294ad7e9b0SAdrian Chadd 304ad7e9b0SAdrian Chadd #include <sys/cdefs.h> 314ad7e9b0SAdrian Chadd __FBSDID("$FreeBSD$"); 324ad7e9b0SAdrian Chadd 334ad7e9b0SAdrian Chadd /* 344ad7e9b0SAdrian Chadd * Broadcom ChipCommon driver. 354ad7e9b0SAdrian Chadd * 364ad7e9b0SAdrian Chadd * With the exception of some very early chipsets, the ChipCommon core 374ad7e9b0SAdrian Chadd * has been included in all HND SoCs and chipsets based on the siba(4) 384ad7e9b0SAdrian Chadd * and bcma(4) interconnects, providing a common interface to chipset 394ad7e9b0SAdrian Chadd * identification, bus enumeration, UARTs, clocks, watchdog interrupts, GPIO, 404ad7e9b0SAdrian Chadd * flash, etc. 414ad7e9b0SAdrian Chadd */ 424ad7e9b0SAdrian Chadd 434ad7e9b0SAdrian Chadd #include <sys/param.h> 444ad7e9b0SAdrian Chadd #include <sys/kernel.h> 454ad7e9b0SAdrian Chadd #include <sys/bus.h> 464ad7e9b0SAdrian Chadd #include <sys/module.h> 474ad7e9b0SAdrian Chadd #include <sys/systm.h> 484ad7e9b0SAdrian Chadd 494ad7e9b0SAdrian Chadd #include <machine/bus.h> 504ad7e9b0SAdrian Chadd #include <sys/rman.h> 514ad7e9b0SAdrian Chadd #include <machine/resource.h> 524ad7e9b0SAdrian Chadd 534ad7e9b0SAdrian Chadd #include <dev/bhnd/bhnd.h> 544ad7e9b0SAdrian Chadd 554ad7e9b0SAdrian Chadd #include "chipcreg.h" 564ad7e9b0SAdrian Chadd #include "chipcvar.h" 574ad7e9b0SAdrian Chadd 584ad7e9b0SAdrian Chadd devclass_t bhnd_chipc_devclass; /**< bhnd(4) chipcommon device class */ 594ad7e9b0SAdrian Chadd 604ad7e9b0SAdrian Chadd static const struct resource_spec chipc_rspec[CHIPC_MAX_RSPEC] = { 614ad7e9b0SAdrian Chadd { SYS_RES_MEMORY, 0, RF_ACTIVE }, 624ad7e9b0SAdrian Chadd { -1, -1, 0 } 634ad7e9b0SAdrian Chadd }; 644ad7e9b0SAdrian Chadd 6536e4410aSAdrian Chadd static struct bhnd_device_quirk chipc_quirks[]; 6636e4410aSAdrian Chadd 674ad7e9b0SAdrian Chadd /* Supported device identifiers */ 6836e4410aSAdrian Chadd static const struct bhnd_device chipc_devices[] = { 698a3fcfa7SAdrian Chadd BHND_DEVICE(CC, "CC", chipc_quirks), 7036e4410aSAdrian Chadd BHND_DEVICE_END 714ad7e9b0SAdrian Chadd }; 724ad7e9b0SAdrian Chadd 7336e4410aSAdrian Chadd 744ad7e9b0SAdrian Chadd /* Device quirks table */ 754ad7e9b0SAdrian Chadd static struct bhnd_device_quirk chipc_quirks[] = { 7636e4410aSAdrian Chadd { BHND_HWREV_RANGE (0, 21), CHIPC_QUIRK_ALWAYS_HAS_SPROM }, 7736e4410aSAdrian Chadd { BHND_HWREV_EQ (22), CHIPC_QUIRK_SPROM_CHECK_CST_R22 }, 7836e4410aSAdrian Chadd { BHND_HWREV_RANGE (23, 31), CHIPC_QUIRK_SPROM_CHECK_CST_R23 }, 7936e4410aSAdrian Chadd { BHND_HWREV_GTE (35), CHIPC_QUIRK_SUPPORTS_NFLASH }, 8036e4410aSAdrian Chadd BHND_DEVICE_QUIRK_END 814ad7e9b0SAdrian Chadd }; 824ad7e9b0SAdrian Chadd 834ad7e9b0SAdrian Chadd /* quirk and capability flag convenience macros */ 844ad7e9b0SAdrian Chadd #define CHIPC_QUIRK(_sc, _name) \ 854ad7e9b0SAdrian Chadd ((_sc)->quirks & CHIPC_QUIRK_ ## _name) 864ad7e9b0SAdrian Chadd 874ad7e9b0SAdrian Chadd #define CHIPC_CAP(_sc, _name) \ 884ad7e9b0SAdrian Chadd ((_sc)->caps & CHIPC_ ## _name) 894ad7e9b0SAdrian Chadd 904ad7e9b0SAdrian Chadd #define CHIPC_ASSERT_QUIRK(_sc, name) \ 914ad7e9b0SAdrian Chadd KASSERT(CHIPC_QUIRK((_sc), name), ("quirk " __STRING(_name) " not set")) 924ad7e9b0SAdrian Chadd 934ad7e9b0SAdrian Chadd #define CHIPC_ASSERT_CAP(_sc, name) \ 944ad7e9b0SAdrian Chadd KASSERT(CHIPC_CAP((_sc), name), ("capability " __STRING(_name) " not set")) 954ad7e9b0SAdrian Chadd 964ad7e9b0SAdrian Chadd static int 974ad7e9b0SAdrian Chadd chipc_probe(device_t dev) 984ad7e9b0SAdrian Chadd { 9936e4410aSAdrian Chadd const struct bhnd_device *id; 1004ad7e9b0SAdrian Chadd 10136e4410aSAdrian Chadd id = bhnd_device_lookup(dev, chipc_devices, sizeof(chipc_devices[0])); 10236e4410aSAdrian Chadd if (id == NULL) 1034ad7e9b0SAdrian Chadd return (ENXIO); 10436e4410aSAdrian Chadd 10536e4410aSAdrian Chadd bhnd_set_default_core_desc(dev); 10636e4410aSAdrian Chadd return (BUS_PROBE_DEFAULT); 1074ad7e9b0SAdrian Chadd } 1084ad7e9b0SAdrian Chadd 1094ad7e9b0SAdrian Chadd static int 1104ad7e9b0SAdrian Chadd chipc_attach(device_t dev) 1114ad7e9b0SAdrian Chadd { 1124ad7e9b0SAdrian Chadd struct chipc_softc *sc; 1134ad7e9b0SAdrian Chadd bhnd_addr_t enum_addr; 1144ad7e9b0SAdrian Chadd uint32_t ccid_reg; 1154ad7e9b0SAdrian Chadd uint8_t chip_type; 1164ad7e9b0SAdrian Chadd int error; 1174ad7e9b0SAdrian Chadd 1184ad7e9b0SAdrian Chadd sc = device_get_softc(dev); 1194ad7e9b0SAdrian Chadd sc->dev = dev; 12036e4410aSAdrian Chadd sc->quirks = bhnd_device_quirks(dev, chipc_devices, 12136e4410aSAdrian Chadd sizeof(chipc_devices[0])); 1224ad7e9b0SAdrian Chadd 1234ad7e9b0SAdrian Chadd /* Allocate bus resources */ 1244ad7e9b0SAdrian Chadd memcpy(sc->rspec, chipc_rspec, sizeof(sc->rspec)); 1254ad7e9b0SAdrian Chadd if ((error = bhnd_alloc_resources(dev, sc->rspec, sc->res))) 1264ad7e9b0SAdrian Chadd return (error); 1274ad7e9b0SAdrian Chadd 1284ad7e9b0SAdrian Chadd sc->core = sc->res[0]; 1294ad7e9b0SAdrian Chadd 1304ad7e9b0SAdrian Chadd /* Fetch our chipset identification data */ 1314ad7e9b0SAdrian Chadd ccid_reg = bhnd_bus_read_4(sc->core, CHIPC_ID); 1324ad7e9b0SAdrian Chadd chip_type = CHIPC_GET_ATTR(ccid_reg, ID_BUS); 1334ad7e9b0SAdrian Chadd 1344ad7e9b0SAdrian Chadd switch (chip_type) { 1354ad7e9b0SAdrian Chadd case BHND_CHIPTYPE_SIBA: 1364ad7e9b0SAdrian Chadd /* enumeration space starts at the ChipCommon register base. */ 1374ad7e9b0SAdrian Chadd enum_addr = rman_get_start(sc->core->res); 1384ad7e9b0SAdrian Chadd break; 1394ad7e9b0SAdrian Chadd case BHND_CHIPTYPE_BCMA: 1404ad7e9b0SAdrian Chadd case BHND_CHIPTYPE_BCMA_ALT: 1414ad7e9b0SAdrian Chadd enum_addr = bhnd_bus_read_4(sc->core, CHIPC_EROMPTR); 1424ad7e9b0SAdrian Chadd break; 1434ad7e9b0SAdrian Chadd default: 1444ad7e9b0SAdrian Chadd device_printf(dev, "unsupported chip type %hhu\n", chip_type); 1454ad7e9b0SAdrian Chadd error = ENODEV; 1464ad7e9b0SAdrian Chadd goto cleanup; 1474ad7e9b0SAdrian Chadd } 1484ad7e9b0SAdrian Chadd 1494ad7e9b0SAdrian Chadd sc->ccid = bhnd_parse_chipid(ccid_reg, enum_addr); 1504ad7e9b0SAdrian Chadd 1514ad7e9b0SAdrian Chadd /* Fetch capability and status register values */ 1524ad7e9b0SAdrian Chadd sc->caps = bhnd_bus_read_4(sc->core, CHIPC_CAPABILITIES); 1534ad7e9b0SAdrian Chadd sc->cst = bhnd_bus_read_4(sc->core, CHIPC_CHIPST); 1544ad7e9b0SAdrian Chadd 1554ad7e9b0SAdrian Chadd // TODO 1564ad7e9b0SAdrian Chadd switch (bhnd_chipc_nvram_src(dev)) { 1574ad7e9b0SAdrian Chadd case BHND_NVRAM_SRC_CIS: 1584ad7e9b0SAdrian Chadd device_printf(dev, "NVRAM source: CIS\n"); 1594ad7e9b0SAdrian Chadd break; 1604ad7e9b0SAdrian Chadd case BHND_NVRAM_SRC_SPROM: 1614ad7e9b0SAdrian Chadd device_printf(dev, "NVRAM source: SPROM\n"); 1624ad7e9b0SAdrian Chadd break; 1634ad7e9b0SAdrian Chadd case BHND_NVRAM_SRC_OTP: 1644ad7e9b0SAdrian Chadd device_printf(dev, "NVRAM source: OTP\n"); 1654ad7e9b0SAdrian Chadd break; 1664ad7e9b0SAdrian Chadd case BHND_NVRAM_SRC_NFLASH: 1674ad7e9b0SAdrian Chadd device_printf(dev, "NVRAM source: NFLASH\n"); 1684ad7e9b0SAdrian Chadd break; 1694ad7e9b0SAdrian Chadd case BHND_NVRAM_SRC_NONE: 1704ad7e9b0SAdrian Chadd device_printf(dev, "NVRAM source: NONE\n"); 1714ad7e9b0SAdrian Chadd break; 1724ad7e9b0SAdrian Chadd } 1734ad7e9b0SAdrian Chadd 1744ad7e9b0SAdrian Chadd return (0); 1754ad7e9b0SAdrian Chadd 1764ad7e9b0SAdrian Chadd cleanup: 1774ad7e9b0SAdrian Chadd bhnd_release_resources(dev, sc->rspec, sc->res); 1784ad7e9b0SAdrian Chadd return (error); 1794ad7e9b0SAdrian Chadd } 1804ad7e9b0SAdrian Chadd 1814ad7e9b0SAdrian Chadd static int 1824ad7e9b0SAdrian Chadd chipc_detach(device_t dev) 1834ad7e9b0SAdrian Chadd { 1844ad7e9b0SAdrian Chadd struct chipc_softc *sc; 1854ad7e9b0SAdrian Chadd 1864ad7e9b0SAdrian Chadd sc = device_get_softc(dev); 1874ad7e9b0SAdrian Chadd bhnd_release_resources(dev, sc->rspec, sc->res); 1884ad7e9b0SAdrian Chadd 1894ad7e9b0SAdrian Chadd return (0); 1904ad7e9b0SAdrian Chadd } 1914ad7e9b0SAdrian Chadd 1924ad7e9b0SAdrian Chadd static int 1934ad7e9b0SAdrian Chadd chipc_suspend(device_t dev) 1944ad7e9b0SAdrian Chadd { 1954ad7e9b0SAdrian Chadd return (0); 1964ad7e9b0SAdrian Chadd } 1974ad7e9b0SAdrian Chadd 1984ad7e9b0SAdrian Chadd static int 1994ad7e9b0SAdrian Chadd chipc_resume(device_t dev) 2004ad7e9b0SAdrian Chadd { 2014ad7e9b0SAdrian Chadd return (0); 2024ad7e9b0SAdrian Chadd } 2034ad7e9b0SAdrian Chadd 2044ad7e9b0SAdrian Chadd /** 2054ad7e9b0SAdrian Chadd * Use device-specific ChipStatus flags to determine the preferred NVRAM 2064ad7e9b0SAdrian Chadd * data source. 2074ad7e9b0SAdrian Chadd */ 2084ad7e9b0SAdrian Chadd static bhnd_nvram_src_t 2094ad7e9b0SAdrian Chadd chipc_nvram_src_chipst(struct chipc_softc *sc) 2104ad7e9b0SAdrian Chadd { 2114ad7e9b0SAdrian Chadd uint8_t nvram_sel; 2124ad7e9b0SAdrian Chadd 2134ad7e9b0SAdrian Chadd CHIPC_ASSERT_QUIRK(sc, SPROM_CHECK_CHIPST); 2144ad7e9b0SAdrian Chadd 2154ad7e9b0SAdrian Chadd if (CHIPC_QUIRK(sc, SPROM_CHECK_CST_R22)) { 2164ad7e9b0SAdrian Chadd // TODO: On these devices, the official driver code always 2174ad7e9b0SAdrian Chadd // assumes SPROM availability if CHIPC_CST_OTP_SEL is not 2184ad7e9b0SAdrian Chadd // set; we must review against the actual behavior of our 2194ad7e9b0SAdrian Chadd // BCM4312 hardware 2204ad7e9b0SAdrian Chadd nvram_sel = CHIPC_GET_ATTR(sc->cst, CST_SPROM_OTP_SEL_R22); 2214ad7e9b0SAdrian Chadd } else if (CHIPC_QUIRK(sc, SPROM_CHECK_CST_R23)) { 2224ad7e9b0SAdrian Chadd nvram_sel = CHIPC_GET_ATTR(sc->cst, CST_SPROM_OTP_SEL_R23); 2234ad7e9b0SAdrian Chadd } else { 2244ad7e9b0SAdrian Chadd panic("invalid CST OTP/SPROM chipc quirk flags"); 2254ad7e9b0SAdrian Chadd } 2264ad7e9b0SAdrian Chadd device_printf(sc->dev, "querying chipst for 0x%x, 0x%x\n", sc->ccid.chip_id, sc->cst); 2274ad7e9b0SAdrian Chadd 2284ad7e9b0SAdrian Chadd switch (nvram_sel) { 2294ad7e9b0SAdrian Chadd case CHIPC_CST_DEFCIS_SEL: 2304ad7e9b0SAdrian Chadd return (BHND_NVRAM_SRC_CIS); 2314ad7e9b0SAdrian Chadd 2324ad7e9b0SAdrian Chadd case CHIPC_CST_SPROM_SEL: 2334ad7e9b0SAdrian Chadd case CHIPC_CST_OTP_PWRDN: 2344ad7e9b0SAdrian Chadd return (BHND_NVRAM_SRC_SPROM); 2354ad7e9b0SAdrian Chadd 2364ad7e9b0SAdrian Chadd case CHIPC_CST_OTP_SEL: 2374ad7e9b0SAdrian Chadd return (BHND_NVRAM_SRC_OTP); 2384ad7e9b0SAdrian Chadd 2394ad7e9b0SAdrian Chadd default: 2404ad7e9b0SAdrian Chadd device_printf(sc->dev, "unrecognized OTP/SPROM type 0x%hhx", 2414ad7e9b0SAdrian Chadd nvram_sel); 2424ad7e9b0SAdrian Chadd return (BHND_NVRAM_SRC_NONE); 2434ad7e9b0SAdrian Chadd } 2444ad7e9b0SAdrian Chadd } 2454ad7e9b0SAdrian Chadd 2464ad7e9b0SAdrian Chadd /** 2474ad7e9b0SAdrian Chadd * Determine the preferred NVRAM data source. 2484ad7e9b0SAdrian Chadd */ 2494ad7e9b0SAdrian Chadd static bhnd_nvram_src_t 2504ad7e9b0SAdrian Chadd chipc_nvram_src(device_t dev) 2514ad7e9b0SAdrian Chadd { 2524ad7e9b0SAdrian Chadd struct chipc_softc *sc; 2534ad7e9b0SAdrian Chadd uint32_t srom_ctrl; 2544ad7e9b0SAdrian Chadd 2554ad7e9b0SAdrian Chadd sc = device_get_softc(dev); 2564ad7e9b0SAdrian Chadd 2574ad7e9b0SAdrian Chadd /* Very early devices always included a SPROM */ 2584ad7e9b0SAdrian Chadd if (CHIPC_QUIRK(sc, ALWAYS_HAS_SPROM)) 2594ad7e9b0SAdrian Chadd return (BHND_NVRAM_SRC_SPROM); 2604ad7e9b0SAdrian Chadd 2614ad7e9b0SAdrian Chadd /* Most other early devices require checking ChipStatus flags */ 2624ad7e9b0SAdrian Chadd if (CHIPC_QUIRK(sc, SPROM_CHECK_CHIPST)) 2634ad7e9b0SAdrian Chadd return (chipc_nvram_src_chipst(sc)); 2644ad7e9b0SAdrian Chadd 2654ad7e9b0SAdrian Chadd /* 2664ad7e9b0SAdrian Chadd * Later chipset revisions standardized the NVRAM capability flags and 2674ad7e9b0SAdrian Chadd * register interfaces. 2684ad7e9b0SAdrian Chadd * 2694ad7e9b0SAdrian Chadd * We check for hardware presence in order of precedence. For example, 2704ad7e9b0SAdrian Chadd * SPROM is is always used in preference to internal OTP if found. 2714ad7e9b0SAdrian Chadd */ 2724ad7e9b0SAdrian Chadd if (CHIPC_CAP(sc, CAP_SPROM)) { 2734ad7e9b0SAdrian Chadd srom_ctrl = bhnd_bus_read_4(sc->core, CHIPC_SPROM_CTRL); 2744ad7e9b0SAdrian Chadd if (srom_ctrl & CHIPC_SRC_PRESENT) 2754ad7e9b0SAdrian Chadd return (BHND_NVRAM_SRC_SPROM); 2764ad7e9b0SAdrian Chadd } 2774ad7e9b0SAdrian Chadd 2784ad7e9b0SAdrian Chadd /* Check for OTP */ 2794ad7e9b0SAdrian Chadd if (CHIPC_CAP(sc, CAP_OTP_SIZE)) 2804ad7e9b0SAdrian Chadd return (BHND_NVRAM_SRC_OTP); 2814ad7e9b0SAdrian Chadd 2824ad7e9b0SAdrian Chadd /* 2834ad7e9b0SAdrian Chadd * Finally, Northstar chipsets (and possibly other chipsets?) support 2844ad7e9b0SAdrian Chadd * external NAND flash. 2854ad7e9b0SAdrian Chadd */ 2864ad7e9b0SAdrian Chadd if (CHIPC_QUIRK(sc, SUPPORTS_NFLASH) && CHIPC_CAP(sc, CAP_NFLASH)) 2874ad7e9b0SAdrian Chadd return (BHND_NVRAM_SRC_NFLASH); 2884ad7e9b0SAdrian Chadd 2894ad7e9b0SAdrian Chadd /* No NVRAM hardware capability declared */ 2904ad7e9b0SAdrian Chadd return (BHND_NVRAM_SRC_NONE); 2914ad7e9b0SAdrian Chadd } 2924ad7e9b0SAdrian Chadd 2934ad7e9b0SAdrian Chadd static device_method_t chipc_methods[] = { 2944ad7e9b0SAdrian Chadd /* Device interface */ 2954ad7e9b0SAdrian Chadd DEVMETHOD(device_probe, chipc_probe), 2964ad7e9b0SAdrian Chadd DEVMETHOD(device_attach, chipc_attach), 2974ad7e9b0SAdrian Chadd DEVMETHOD(device_detach, chipc_detach), 2984ad7e9b0SAdrian Chadd DEVMETHOD(device_suspend, chipc_suspend), 2994ad7e9b0SAdrian Chadd DEVMETHOD(device_resume, chipc_resume), 3004ad7e9b0SAdrian Chadd 3014ad7e9b0SAdrian Chadd /* ChipCommon interface */ 3024ad7e9b0SAdrian Chadd DEVMETHOD(bhnd_chipc_nvram_src, chipc_nvram_src), 3034ad7e9b0SAdrian Chadd 3044ad7e9b0SAdrian Chadd DEVMETHOD_END 3054ad7e9b0SAdrian Chadd }; 3064ad7e9b0SAdrian Chadd 3074ad7e9b0SAdrian Chadd DEFINE_CLASS_0(bhnd_chipc, chipc_driver, chipc_methods, sizeof(struct chipc_softc)); 3084ad7e9b0SAdrian Chadd DRIVER_MODULE(bhnd_chipc, bhnd, chipc_driver, bhnd_chipc_devclass, 0, 0); 30996546b75SAdrian Chadd MODULE_DEPEND(bhnd_chipc, bhnd, 1, 1, 1); 3104ad7e9b0SAdrian Chadd MODULE_VERSION(bhnd_chipc, 1); 311