18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Broadcom specific AMBA 38c2ecf20Sopenharmony_ci * Bus subsystem 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Licensed under the GNU/GPL. See COPYING for details. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include "bcma_private.h" 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/mmc/sdio_func.h> 118c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 128c2ecf20Sopenharmony_ci#include <linux/pci.h> 138c2ecf20Sopenharmony_ci#include <linux/bcma/bcma.h> 148c2ecf20Sopenharmony_ci#include <linux/slab.h> 158c2ecf20Sopenharmony_ci#include <linux/of_address.h> 168c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 178c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Broadcom's specific AMBA driver"); 208c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/* contains the number the next bus should get. */ 238c2ecf20Sopenharmony_cistatic unsigned int bcma_bus_next_num = 0; 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/* bcma_buses_mutex locks the bcma_bus_next_num */ 268c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(bcma_buses_mutex); 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic int bcma_bus_match(struct device *dev, struct device_driver *drv); 298c2ecf20Sopenharmony_cistatic int bcma_device_probe(struct device *dev); 308c2ecf20Sopenharmony_cistatic int bcma_device_remove(struct device *dev); 318c2ecf20Sopenharmony_cistatic int bcma_device_uevent(struct device *dev, struct kobj_uevent_env *env); 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic ssize_t manuf_show(struct device *dev, struct device_attribute *attr, char *buf) 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci struct bcma_device *core = container_of(dev, struct bcma_device, dev); 368c2ecf20Sopenharmony_ci return sprintf(buf, "0x%03X\n", core->id.manuf); 378c2ecf20Sopenharmony_ci} 388c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(manuf); 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic ssize_t id_show(struct device *dev, struct device_attribute *attr, char *buf) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci struct bcma_device *core = container_of(dev, struct bcma_device, dev); 438c2ecf20Sopenharmony_ci return sprintf(buf, "0x%03X\n", core->id.id); 448c2ecf20Sopenharmony_ci} 458c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(id); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic ssize_t rev_show(struct device *dev, struct device_attribute *attr, char *buf) 488c2ecf20Sopenharmony_ci{ 498c2ecf20Sopenharmony_ci struct bcma_device *core = container_of(dev, struct bcma_device, dev); 508c2ecf20Sopenharmony_ci return sprintf(buf, "0x%02X\n", core->id.rev); 518c2ecf20Sopenharmony_ci} 528c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(rev); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic ssize_t class_show(struct device *dev, struct device_attribute *attr, char *buf) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci struct bcma_device *core = container_of(dev, struct bcma_device, dev); 578c2ecf20Sopenharmony_ci return sprintf(buf, "0x%X\n", core->id.class); 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(class); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic struct attribute *bcma_device_attrs[] = { 628c2ecf20Sopenharmony_ci &dev_attr_manuf.attr, 638c2ecf20Sopenharmony_ci &dev_attr_id.attr, 648c2ecf20Sopenharmony_ci &dev_attr_rev.attr, 658c2ecf20Sopenharmony_ci &dev_attr_class.attr, 668c2ecf20Sopenharmony_ci NULL, 678c2ecf20Sopenharmony_ci}; 688c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(bcma_device); 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic struct bus_type bcma_bus_type = { 718c2ecf20Sopenharmony_ci .name = "bcma", 728c2ecf20Sopenharmony_ci .match = bcma_bus_match, 738c2ecf20Sopenharmony_ci .probe = bcma_device_probe, 748c2ecf20Sopenharmony_ci .remove = bcma_device_remove, 758c2ecf20Sopenharmony_ci .uevent = bcma_device_uevent, 768c2ecf20Sopenharmony_ci .dev_groups = bcma_device_groups, 778c2ecf20Sopenharmony_ci}; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic u16 bcma_cc_core_id(struct bcma_bus *bus) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci if (bus->chipinfo.id == BCMA_CHIP_ID_BCM4706) 828c2ecf20Sopenharmony_ci return BCMA_CORE_4706_CHIPCOMMON; 838c2ecf20Sopenharmony_ci return BCMA_CORE_CHIPCOMMON; 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistruct bcma_device *bcma_find_core_unit(struct bcma_bus *bus, u16 coreid, 878c2ecf20Sopenharmony_ci u8 unit) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci struct bcma_device *core; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci list_for_each_entry(core, &bus->cores, list) { 928c2ecf20Sopenharmony_ci if (core->id.id == coreid && core->core_unit == unit) 938c2ecf20Sopenharmony_ci return core; 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci return NULL; 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(bcma_find_core_unit); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cibool bcma_wait_value(struct bcma_device *core, u16 reg, u32 mask, u32 value, 1008c2ecf20Sopenharmony_ci int timeout) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci unsigned long deadline = jiffies + timeout; 1038c2ecf20Sopenharmony_ci u32 val; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci do { 1068c2ecf20Sopenharmony_ci val = bcma_read32(core, reg); 1078c2ecf20Sopenharmony_ci if ((val & mask) == value) 1088c2ecf20Sopenharmony_ci return true; 1098c2ecf20Sopenharmony_ci cpu_relax(); 1108c2ecf20Sopenharmony_ci udelay(10); 1118c2ecf20Sopenharmony_ci } while (!time_after_eq(jiffies, deadline)); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci bcma_warn(core->bus, "Timeout waiting for register 0x%04X!\n", reg); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci return false; 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic void bcma_release_core_dev(struct device *dev) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci struct bcma_device *core = container_of(dev, struct bcma_device, dev); 1218c2ecf20Sopenharmony_ci if (core->io_addr) 1228c2ecf20Sopenharmony_ci iounmap(core->io_addr); 1238c2ecf20Sopenharmony_ci if (core->io_wrap) 1248c2ecf20Sopenharmony_ci iounmap(core->io_wrap); 1258c2ecf20Sopenharmony_ci kfree(core); 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic bool bcma_is_core_needed_early(u16 core_id) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci switch (core_id) { 1318c2ecf20Sopenharmony_ci case BCMA_CORE_NS_NAND: 1328c2ecf20Sopenharmony_ci case BCMA_CORE_NS_QSPI: 1338c2ecf20Sopenharmony_ci return true; 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci return false; 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cistatic struct device_node *bcma_of_find_child_device(struct device *parent, 1408c2ecf20Sopenharmony_ci struct bcma_device *core) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci struct device_node *node; 1438c2ecf20Sopenharmony_ci u64 size; 1448c2ecf20Sopenharmony_ci const __be32 *reg; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci if (!parent->of_node) 1478c2ecf20Sopenharmony_ci return NULL; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci for_each_child_of_node(parent->of_node, node) { 1508c2ecf20Sopenharmony_ci reg = of_get_address(node, 0, &size, NULL); 1518c2ecf20Sopenharmony_ci if (!reg) 1528c2ecf20Sopenharmony_ci continue; 1538c2ecf20Sopenharmony_ci if (of_translate_address(node, reg) == core->addr) 1548c2ecf20Sopenharmony_ci return node; 1558c2ecf20Sopenharmony_ci } 1568c2ecf20Sopenharmony_ci return NULL; 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic int bcma_of_irq_parse(struct device *parent, 1608c2ecf20Sopenharmony_ci struct bcma_device *core, 1618c2ecf20Sopenharmony_ci struct of_phandle_args *out_irq, int num) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci __be32 laddr[1]; 1648c2ecf20Sopenharmony_ci int rc; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci if (core->dev.of_node) { 1678c2ecf20Sopenharmony_ci rc = of_irq_parse_one(core->dev.of_node, num, out_irq); 1688c2ecf20Sopenharmony_ci if (!rc) 1698c2ecf20Sopenharmony_ci return rc; 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci out_irq->np = parent->of_node; 1738c2ecf20Sopenharmony_ci out_irq->args_count = 1; 1748c2ecf20Sopenharmony_ci out_irq->args[0] = num; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci laddr[0] = cpu_to_be32(core->addr); 1778c2ecf20Sopenharmony_ci return of_irq_parse_raw(laddr, out_irq); 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cistatic unsigned int bcma_of_get_irq(struct device *parent, 1818c2ecf20Sopenharmony_ci struct bcma_device *core, int num) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci struct of_phandle_args out_irq; 1848c2ecf20Sopenharmony_ci int ret; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci if (!IS_ENABLED(CONFIG_OF_IRQ) || !parent->of_node) 1878c2ecf20Sopenharmony_ci return 0; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci ret = bcma_of_irq_parse(parent, core, &out_irq, num); 1908c2ecf20Sopenharmony_ci if (ret) { 1918c2ecf20Sopenharmony_ci bcma_debug(core->bus, "bcma_of_get_irq() failed with rc=%d\n", 1928c2ecf20Sopenharmony_ci ret); 1938c2ecf20Sopenharmony_ci return 0; 1948c2ecf20Sopenharmony_ci } 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci return irq_create_of_mapping(&out_irq); 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_cistatic void bcma_of_fill_device(struct device *parent, 2008c2ecf20Sopenharmony_ci struct bcma_device *core) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci struct device_node *node; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci node = bcma_of_find_child_device(parent, core); 2058c2ecf20Sopenharmony_ci if (node) 2068c2ecf20Sopenharmony_ci core->dev.of_node = node; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci core->irq = bcma_of_get_irq(parent, core, 0); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci of_dma_configure(&core->dev, node, false); 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ciunsigned int bcma_core_irq(struct bcma_device *core, int num) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci struct bcma_bus *bus = core->bus; 2168c2ecf20Sopenharmony_ci unsigned int mips_irq; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci switch (bus->hosttype) { 2198c2ecf20Sopenharmony_ci case BCMA_HOSTTYPE_PCI: 2208c2ecf20Sopenharmony_ci return bus->host_pci->irq; 2218c2ecf20Sopenharmony_ci case BCMA_HOSTTYPE_SOC: 2228c2ecf20Sopenharmony_ci if (bus->drv_mips.core && num == 0) { 2238c2ecf20Sopenharmony_ci mips_irq = bcma_core_mips_irq(core); 2248c2ecf20Sopenharmony_ci return mips_irq <= 4 ? mips_irq + 2 : 0; 2258c2ecf20Sopenharmony_ci } 2268c2ecf20Sopenharmony_ci if (bus->dev) 2278c2ecf20Sopenharmony_ci return bcma_of_get_irq(bus->dev, core, num); 2288c2ecf20Sopenharmony_ci return 0; 2298c2ecf20Sopenharmony_ci case BCMA_HOSTTYPE_SDIO: 2308c2ecf20Sopenharmony_ci return 0; 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci return 0; 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ciEXPORT_SYMBOL(bcma_core_irq); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_civoid bcma_prepare_core(struct bcma_bus *bus, struct bcma_device *core) 2388c2ecf20Sopenharmony_ci{ 2398c2ecf20Sopenharmony_ci device_initialize(&core->dev); 2408c2ecf20Sopenharmony_ci core->dev.release = bcma_release_core_dev; 2418c2ecf20Sopenharmony_ci core->dev.bus = &bcma_bus_type; 2428c2ecf20Sopenharmony_ci dev_set_name(&core->dev, "bcma%d:%d", bus->num, core->core_index); 2438c2ecf20Sopenharmony_ci core->dev.parent = bus->dev; 2448c2ecf20Sopenharmony_ci if (bus->dev) 2458c2ecf20Sopenharmony_ci bcma_of_fill_device(bus->dev, core); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci switch (bus->hosttype) { 2488c2ecf20Sopenharmony_ci case BCMA_HOSTTYPE_PCI: 2498c2ecf20Sopenharmony_ci core->dma_dev = bus->dev; 2508c2ecf20Sopenharmony_ci core->irq = bus->host_pci->irq; 2518c2ecf20Sopenharmony_ci break; 2528c2ecf20Sopenharmony_ci case BCMA_HOSTTYPE_SOC: 2538c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_OF) && bus->dev) { 2548c2ecf20Sopenharmony_ci core->dma_dev = bus->dev; 2558c2ecf20Sopenharmony_ci } else { 2568c2ecf20Sopenharmony_ci core->dev.dma_mask = &core->dev.coherent_dma_mask; 2578c2ecf20Sopenharmony_ci core->dma_dev = &core->dev; 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci break; 2608c2ecf20Sopenharmony_ci case BCMA_HOSTTYPE_SDIO: 2618c2ecf20Sopenharmony_ci break; 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_civoid bcma_init_bus(struct bcma_bus *bus) 2668c2ecf20Sopenharmony_ci{ 2678c2ecf20Sopenharmony_ci mutex_lock(&bcma_buses_mutex); 2688c2ecf20Sopenharmony_ci bus->num = bcma_bus_next_num++; 2698c2ecf20Sopenharmony_ci mutex_unlock(&bcma_buses_mutex); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&bus->cores); 2728c2ecf20Sopenharmony_ci bus->nr_cores = 0; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci bcma_detect_chip(bus); 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_cistatic void bcma_register_core(struct bcma_bus *bus, struct bcma_device *core) 2788c2ecf20Sopenharmony_ci{ 2798c2ecf20Sopenharmony_ci int err; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci err = device_add(&core->dev); 2828c2ecf20Sopenharmony_ci if (err) { 2838c2ecf20Sopenharmony_ci bcma_err(bus, "Could not register dev for core 0x%03X\n", 2848c2ecf20Sopenharmony_ci core->id.id); 2858c2ecf20Sopenharmony_ci return; 2868c2ecf20Sopenharmony_ci } 2878c2ecf20Sopenharmony_ci core->dev_registered = true; 2888c2ecf20Sopenharmony_ci} 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_cistatic int bcma_register_devices(struct bcma_bus *bus) 2918c2ecf20Sopenharmony_ci{ 2928c2ecf20Sopenharmony_ci struct bcma_device *core; 2938c2ecf20Sopenharmony_ci int err; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci list_for_each_entry(core, &bus->cores, list) { 2968c2ecf20Sopenharmony_ci /* We support that cores ourself */ 2978c2ecf20Sopenharmony_ci switch (core->id.id) { 2988c2ecf20Sopenharmony_ci case BCMA_CORE_4706_CHIPCOMMON: 2998c2ecf20Sopenharmony_ci case BCMA_CORE_CHIPCOMMON: 3008c2ecf20Sopenharmony_ci case BCMA_CORE_NS_CHIPCOMMON_B: 3018c2ecf20Sopenharmony_ci case BCMA_CORE_PCI: 3028c2ecf20Sopenharmony_ci case BCMA_CORE_PCIE: 3038c2ecf20Sopenharmony_ci case BCMA_CORE_PCIE2: 3048c2ecf20Sopenharmony_ci case BCMA_CORE_MIPS_74K: 3058c2ecf20Sopenharmony_ci case BCMA_CORE_4706_MAC_GBIT_COMMON: 3068c2ecf20Sopenharmony_ci continue; 3078c2ecf20Sopenharmony_ci } 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci /* Early cores were already registered */ 3108c2ecf20Sopenharmony_ci if (bcma_is_core_needed_early(core->id.id)) 3118c2ecf20Sopenharmony_ci continue; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci /* Only first GMAC core on BCM4706 is connected and working */ 3148c2ecf20Sopenharmony_ci if (core->id.id == BCMA_CORE_4706_MAC_GBIT && 3158c2ecf20Sopenharmony_ci core->core_unit > 0) 3168c2ecf20Sopenharmony_ci continue; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci bcma_register_core(bus, core); 3198c2ecf20Sopenharmony_ci } 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci#ifdef CONFIG_BCMA_PFLASH 3228c2ecf20Sopenharmony_ci if (bus->drv_cc.pflash.present) { 3238c2ecf20Sopenharmony_ci err = platform_device_register(&bcma_pflash_dev); 3248c2ecf20Sopenharmony_ci if (err) 3258c2ecf20Sopenharmony_ci bcma_err(bus, "Error registering parallel flash\n"); 3268c2ecf20Sopenharmony_ci } 3278c2ecf20Sopenharmony_ci#endif 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci#ifdef CONFIG_BCMA_SFLASH 3308c2ecf20Sopenharmony_ci if (bus->drv_cc.sflash.present) { 3318c2ecf20Sopenharmony_ci err = platform_device_register(&bcma_sflash_dev); 3328c2ecf20Sopenharmony_ci if (err) 3338c2ecf20Sopenharmony_ci bcma_err(bus, "Error registering serial flash\n"); 3348c2ecf20Sopenharmony_ci } 3358c2ecf20Sopenharmony_ci#endif 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci#ifdef CONFIG_BCMA_NFLASH 3388c2ecf20Sopenharmony_ci if (bus->drv_cc.nflash.present) { 3398c2ecf20Sopenharmony_ci err = platform_device_register(&bcma_nflash_dev); 3408c2ecf20Sopenharmony_ci if (err) 3418c2ecf20Sopenharmony_ci bcma_err(bus, "Error registering NAND flash\n"); 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci#endif 3448c2ecf20Sopenharmony_ci err = bcma_gpio_init(&bus->drv_cc); 3458c2ecf20Sopenharmony_ci if (err == -ENOTSUPP) 3468c2ecf20Sopenharmony_ci bcma_debug(bus, "GPIO driver not activated\n"); 3478c2ecf20Sopenharmony_ci else if (err) 3488c2ecf20Sopenharmony_ci bcma_err(bus, "Error registering GPIO driver: %i\n", err); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci if (bus->hosttype == BCMA_HOSTTYPE_SOC) { 3518c2ecf20Sopenharmony_ci err = bcma_chipco_watchdog_register(&bus->drv_cc); 3528c2ecf20Sopenharmony_ci if (err) 3538c2ecf20Sopenharmony_ci bcma_err(bus, "Error registering watchdog driver\n"); 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci return 0; 3578c2ecf20Sopenharmony_ci} 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_civoid bcma_unregister_cores(struct bcma_bus *bus) 3608c2ecf20Sopenharmony_ci{ 3618c2ecf20Sopenharmony_ci struct bcma_device *core, *tmp; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci list_for_each_entry_safe(core, tmp, &bus->cores, list) { 3648c2ecf20Sopenharmony_ci if (!core->dev_registered) 3658c2ecf20Sopenharmony_ci continue; 3668c2ecf20Sopenharmony_ci list_del(&core->list); 3678c2ecf20Sopenharmony_ci device_unregister(&core->dev); 3688c2ecf20Sopenharmony_ci } 3698c2ecf20Sopenharmony_ci if (bus->hosttype == BCMA_HOSTTYPE_SOC) 3708c2ecf20Sopenharmony_ci platform_device_unregister(bus->drv_cc.watchdog); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci /* Now noone uses internally-handled cores, we can free them */ 3738c2ecf20Sopenharmony_ci list_for_each_entry_safe(core, tmp, &bus->cores, list) { 3748c2ecf20Sopenharmony_ci list_del(&core->list); 3758c2ecf20Sopenharmony_ci put_device(&core->dev); 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci} 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ciint bcma_bus_register(struct bcma_bus *bus) 3808c2ecf20Sopenharmony_ci{ 3818c2ecf20Sopenharmony_ci int err; 3828c2ecf20Sopenharmony_ci struct bcma_device *core; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci /* Scan for devices (cores) */ 3858c2ecf20Sopenharmony_ci err = bcma_bus_scan(bus); 3868c2ecf20Sopenharmony_ci if (err) { 3878c2ecf20Sopenharmony_ci bcma_err(bus, "Failed to scan: %d\n", err); 3888c2ecf20Sopenharmony_ci return err; 3898c2ecf20Sopenharmony_ci } 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci /* Early init CC core */ 3928c2ecf20Sopenharmony_ci core = bcma_find_core(bus, bcma_cc_core_id(bus)); 3938c2ecf20Sopenharmony_ci if (core) { 3948c2ecf20Sopenharmony_ci bus->drv_cc.core = core; 3958c2ecf20Sopenharmony_ci bcma_core_chipcommon_early_init(&bus->drv_cc); 3968c2ecf20Sopenharmony_ci } 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci /* Early init PCIE core */ 3998c2ecf20Sopenharmony_ci core = bcma_find_core(bus, BCMA_CORE_PCIE); 4008c2ecf20Sopenharmony_ci if (core) { 4018c2ecf20Sopenharmony_ci bus->drv_pci[0].core = core; 4028c2ecf20Sopenharmony_ci bcma_core_pci_early_init(&bus->drv_pci[0]); 4038c2ecf20Sopenharmony_ci } 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci if (bus->dev) 4068c2ecf20Sopenharmony_ci of_platform_default_populate(bus->dev->of_node, NULL, bus->dev); 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci /* Cores providing flash access go before SPROM init */ 4098c2ecf20Sopenharmony_ci list_for_each_entry(core, &bus->cores, list) { 4108c2ecf20Sopenharmony_ci if (bcma_is_core_needed_early(core->id.id)) 4118c2ecf20Sopenharmony_ci bcma_register_core(bus, core); 4128c2ecf20Sopenharmony_ci } 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci /* Try to get SPROM */ 4158c2ecf20Sopenharmony_ci err = bcma_sprom_get(bus); 4168c2ecf20Sopenharmony_ci if (err == -ENOENT) { 4178c2ecf20Sopenharmony_ci bcma_err(bus, "No SPROM available\n"); 4188c2ecf20Sopenharmony_ci } else if (err) 4198c2ecf20Sopenharmony_ci bcma_err(bus, "Failed to get SPROM: %d\n", err); 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci /* Init CC core */ 4228c2ecf20Sopenharmony_ci core = bcma_find_core(bus, bcma_cc_core_id(bus)); 4238c2ecf20Sopenharmony_ci if (core) { 4248c2ecf20Sopenharmony_ci bus->drv_cc.core = core; 4258c2ecf20Sopenharmony_ci bcma_core_chipcommon_init(&bus->drv_cc); 4268c2ecf20Sopenharmony_ci } 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci /* Init CC core */ 4298c2ecf20Sopenharmony_ci core = bcma_find_core(bus, BCMA_CORE_NS_CHIPCOMMON_B); 4308c2ecf20Sopenharmony_ci if (core) { 4318c2ecf20Sopenharmony_ci bus->drv_cc_b.core = core; 4328c2ecf20Sopenharmony_ci bcma_core_chipcommon_b_init(&bus->drv_cc_b); 4338c2ecf20Sopenharmony_ci } 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci /* Init MIPS core */ 4368c2ecf20Sopenharmony_ci core = bcma_find_core(bus, BCMA_CORE_MIPS_74K); 4378c2ecf20Sopenharmony_ci if (core) { 4388c2ecf20Sopenharmony_ci bus->drv_mips.core = core; 4398c2ecf20Sopenharmony_ci bcma_core_mips_init(&bus->drv_mips); 4408c2ecf20Sopenharmony_ci } 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci /* Init PCIE core */ 4438c2ecf20Sopenharmony_ci core = bcma_find_core_unit(bus, BCMA_CORE_PCIE, 0); 4448c2ecf20Sopenharmony_ci if (core) { 4458c2ecf20Sopenharmony_ci bus->drv_pci[0].core = core; 4468c2ecf20Sopenharmony_ci bcma_core_pci_init(&bus->drv_pci[0]); 4478c2ecf20Sopenharmony_ci } 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci /* Init PCIE core */ 4508c2ecf20Sopenharmony_ci core = bcma_find_core_unit(bus, BCMA_CORE_PCIE, 1); 4518c2ecf20Sopenharmony_ci if (core) { 4528c2ecf20Sopenharmony_ci bus->drv_pci[1].core = core; 4538c2ecf20Sopenharmony_ci bcma_core_pci_init(&bus->drv_pci[1]); 4548c2ecf20Sopenharmony_ci } 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci /* Init PCIe Gen 2 core */ 4578c2ecf20Sopenharmony_ci core = bcma_find_core_unit(bus, BCMA_CORE_PCIE2, 0); 4588c2ecf20Sopenharmony_ci if (core) { 4598c2ecf20Sopenharmony_ci bus->drv_pcie2.core = core; 4608c2ecf20Sopenharmony_ci bcma_core_pcie2_init(&bus->drv_pcie2); 4618c2ecf20Sopenharmony_ci } 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci /* Init GBIT MAC COMMON core */ 4648c2ecf20Sopenharmony_ci core = bcma_find_core(bus, BCMA_CORE_4706_MAC_GBIT_COMMON); 4658c2ecf20Sopenharmony_ci if (core) { 4668c2ecf20Sopenharmony_ci bus->drv_gmac_cmn.core = core; 4678c2ecf20Sopenharmony_ci bcma_core_gmac_cmn_init(&bus->drv_gmac_cmn); 4688c2ecf20Sopenharmony_ci } 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci /* Register found cores */ 4718c2ecf20Sopenharmony_ci bcma_register_devices(bus); 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci bcma_info(bus, "Bus registered\n"); 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci return 0; 4768c2ecf20Sopenharmony_ci} 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_civoid bcma_bus_unregister(struct bcma_bus *bus) 4798c2ecf20Sopenharmony_ci{ 4808c2ecf20Sopenharmony_ci int err; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci err = bcma_gpio_unregister(&bus->drv_cc); 4838c2ecf20Sopenharmony_ci if (err == -EBUSY) 4848c2ecf20Sopenharmony_ci bcma_err(bus, "Some GPIOs are still in use.\n"); 4858c2ecf20Sopenharmony_ci else if (err) 4868c2ecf20Sopenharmony_ci bcma_err(bus, "Can not unregister GPIO driver: %i\n", err); 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci bcma_core_chipcommon_b_free(&bus->drv_cc_b); 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci bcma_unregister_cores(bus); 4918c2ecf20Sopenharmony_ci} 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci/* 4948c2ecf20Sopenharmony_ci * This is a special version of bus registration function designed for SoCs. 4958c2ecf20Sopenharmony_ci * It scans bus and performs basic initialization of main cores only. 4968c2ecf20Sopenharmony_ci * Please note it requires memory allocation, however it won't try to sleep. 4978c2ecf20Sopenharmony_ci */ 4988c2ecf20Sopenharmony_ciint __init bcma_bus_early_register(struct bcma_bus *bus) 4998c2ecf20Sopenharmony_ci{ 5008c2ecf20Sopenharmony_ci int err; 5018c2ecf20Sopenharmony_ci struct bcma_device *core; 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci /* Scan for devices (cores) */ 5048c2ecf20Sopenharmony_ci err = bcma_bus_scan(bus); 5058c2ecf20Sopenharmony_ci if (err) { 5068c2ecf20Sopenharmony_ci bcma_err(bus, "Failed to scan bus: %d\n", err); 5078c2ecf20Sopenharmony_ci return -1; 5088c2ecf20Sopenharmony_ci } 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci /* Early init CC core */ 5118c2ecf20Sopenharmony_ci core = bcma_find_core(bus, bcma_cc_core_id(bus)); 5128c2ecf20Sopenharmony_ci if (core) { 5138c2ecf20Sopenharmony_ci bus->drv_cc.core = core; 5148c2ecf20Sopenharmony_ci bcma_core_chipcommon_early_init(&bus->drv_cc); 5158c2ecf20Sopenharmony_ci } 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci /* Early init MIPS core */ 5188c2ecf20Sopenharmony_ci core = bcma_find_core(bus, BCMA_CORE_MIPS_74K); 5198c2ecf20Sopenharmony_ci if (core) { 5208c2ecf20Sopenharmony_ci bus->drv_mips.core = core; 5218c2ecf20Sopenharmony_ci bcma_core_mips_early_init(&bus->drv_mips); 5228c2ecf20Sopenharmony_ci } 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci bcma_info(bus, "Early bus registered\n"); 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci return 0; 5278c2ecf20Sopenharmony_ci} 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 5308c2ecf20Sopenharmony_ciint bcma_bus_suspend(struct bcma_bus *bus) 5318c2ecf20Sopenharmony_ci{ 5328c2ecf20Sopenharmony_ci struct bcma_device *core; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci list_for_each_entry(core, &bus->cores, list) { 5358c2ecf20Sopenharmony_ci struct device_driver *drv = core->dev.driver; 5368c2ecf20Sopenharmony_ci if (drv) { 5378c2ecf20Sopenharmony_ci struct bcma_driver *adrv = container_of(drv, struct bcma_driver, drv); 5388c2ecf20Sopenharmony_ci if (adrv->suspend) 5398c2ecf20Sopenharmony_ci adrv->suspend(core); 5408c2ecf20Sopenharmony_ci } 5418c2ecf20Sopenharmony_ci } 5428c2ecf20Sopenharmony_ci return 0; 5438c2ecf20Sopenharmony_ci} 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ciint bcma_bus_resume(struct bcma_bus *bus) 5468c2ecf20Sopenharmony_ci{ 5478c2ecf20Sopenharmony_ci struct bcma_device *core; 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci /* Init CC core */ 5508c2ecf20Sopenharmony_ci if (bus->drv_cc.core) { 5518c2ecf20Sopenharmony_ci bus->drv_cc.setup_done = false; 5528c2ecf20Sopenharmony_ci bcma_core_chipcommon_init(&bus->drv_cc); 5538c2ecf20Sopenharmony_ci } 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci list_for_each_entry(core, &bus->cores, list) { 5568c2ecf20Sopenharmony_ci struct device_driver *drv = core->dev.driver; 5578c2ecf20Sopenharmony_ci if (drv) { 5588c2ecf20Sopenharmony_ci struct bcma_driver *adrv = container_of(drv, struct bcma_driver, drv); 5598c2ecf20Sopenharmony_ci if (adrv->resume) 5608c2ecf20Sopenharmony_ci adrv->resume(core); 5618c2ecf20Sopenharmony_ci } 5628c2ecf20Sopenharmony_ci } 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci return 0; 5658c2ecf20Sopenharmony_ci} 5668c2ecf20Sopenharmony_ci#endif 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ciint __bcma_driver_register(struct bcma_driver *drv, struct module *owner) 5698c2ecf20Sopenharmony_ci{ 5708c2ecf20Sopenharmony_ci drv->drv.name = drv->name; 5718c2ecf20Sopenharmony_ci drv->drv.bus = &bcma_bus_type; 5728c2ecf20Sopenharmony_ci drv->drv.owner = owner; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci return driver_register(&drv->drv); 5758c2ecf20Sopenharmony_ci} 5768c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__bcma_driver_register); 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_civoid bcma_driver_unregister(struct bcma_driver *drv) 5798c2ecf20Sopenharmony_ci{ 5808c2ecf20Sopenharmony_ci driver_unregister(&drv->drv); 5818c2ecf20Sopenharmony_ci} 5828c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(bcma_driver_unregister); 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_cistatic int bcma_bus_match(struct device *dev, struct device_driver *drv) 5858c2ecf20Sopenharmony_ci{ 5868c2ecf20Sopenharmony_ci struct bcma_device *core = container_of(dev, struct bcma_device, dev); 5878c2ecf20Sopenharmony_ci struct bcma_driver *adrv = container_of(drv, struct bcma_driver, drv); 5888c2ecf20Sopenharmony_ci const struct bcma_device_id *cid = &core->id; 5898c2ecf20Sopenharmony_ci const struct bcma_device_id *did; 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci for (did = adrv->id_table; did->manuf || did->id || did->rev; did++) { 5928c2ecf20Sopenharmony_ci if ((did->manuf == cid->manuf || did->manuf == BCMA_ANY_MANUF) && 5938c2ecf20Sopenharmony_ci (did->id == cid->id || did->id == BCMA_ANY_ID) && 5948c2ecf20Sopenharmony_ci (did->rev == cid->rev || did->rev == BCMA_ANY_REV) && 5958c2ecf20Sopenharmony_ci (did->class == cid->class || did->class == BCMA_ANY_CLASS)) 5968c2ecf20Sopenharmony_ci return 1; 5978c2ecf20Sopenharmony_ci } 5988c2ecf20Sopenharmony_ci return 0; 5998c2ecf20Sopenharmony_ci} 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_cistatic int bcma_device_probe(struct device *dev) 6028c2ecf20Sopenharmony_ci{ 6038c2ecf20Sopenharmony_ci struct bcma_device *core = container_of(dev, struct bcma_device, dev); 6048c2ecf20Sopenharmony_ci struct bcma_driver *adrv = container_of(dev->driver, struct bcma_driver, 6058c2ecf20Sopenharmony_ci drv); 6068c2ecf20Sopenharmony_ci int err = 0; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci get_device(dev); 6098c2ecf20Sopenharmony_ci if (adrv->probe) 6108c2ecf20Sopenharmony_ci err = adrv->probe(core); 6118c2ecf20Sopenharmony_ci if (err) 6128c2ecf20Sopenharmony_ci put_device(dev); 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci return err; 6158c2ecf20Sopenharmony_ci} 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_cistatic int bcma_device_remove(struct device *dev) 6188c2ecf20Sopenharmony_ci{ 6198c2ecf20Sopenharmony_ci struct bcma_device *core = container_of(dev, struct bcma_device, dev); 6208c2ecf20Sopenharmony_ci struct bcma_driver *adrv = container_of(dev->driver, struct bcma_driver, 6218c2ecf20Sopenharmony_ci drv); 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci if (adrv->remove) 6248c2ecf20Sopenharmony_ci adrv->remove(core); 6258c2ecf20Sopenharmony_ci put_device(dev); 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci return 0; 6288c2ecf20Sopenharmony_ci} 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_cistatic int bcma_device_uevent(struct device *dev, struct kobj_uevent_env *env) 6318c2ecf20Sopenharmony_ci{ 6328c2ecf20Sopenharmony_ci struct bcma_device *core = container_of(dev, struct bcma_device, dev); 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci return add_uevent_var(env, 6358c2ecf20Sopenharmony_ci "MODALIAS=bcma:m%04Xid%04Xrev%02Xcl%02X", 6368c2ecf20Sopenharmony_ci core->id.manuf, core->id.id, 6378c2ecf20Sopenharmony_ci core->id.rev, core->id.class); 6388c2ecf20Sopenharmony_ci} 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_cistatic unsigned int bcma_bus_registered; 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci/* 6438c2ecf20Sopenharmony_ci * If built-in, bus has to be registered early, before any driver calls 6448c2ecf20Sopenharmony_ci * bcma_driver_register. 6458c2ecf20Sopenharmony_ci * Otherwise registering driver would trigger BUG in driver_register. 6468c2ecf20Sopenharmony_ci */ 6478c2ecf20Sopenharmony_cistatic int __init bcma_init_bus_register(void) 6488c2ecf20Sopenharmony_ci{ 6498c2ecf20Sopenharmony_ci int err; 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci if (bcma_bus_registered) 6528c2ecf20Sopenharmony_ci return 0; 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci err = bus_register(&bcma_bus_type); 6558c2ecf20Sopenharmony_ci if (!err) 6568c2ecf20Sopenharmony_ci bcma_bus_registered = 1; 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci return err; 6598c2ecf20Sopenharmony_ci} 6608c2ecf20Sopenharmony_ci#ifndef MODULE 6618c2ecf20Sopenharmony_cifs_initcall(bcma_init_bus_register); 6628c2ecf20Sopenharmony_ci#endif 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci/* Main initialization has to be done with SPI/mtd/NAND/SPROM available */ 6658c2ecf20Sopenharmony_cistatic int __init bcma_modinit(void) 6668c2ecf20Sopenharmony_ci{ 6678c2ecf20Sopenharmony_ci int err; 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci err = bcma_init_bus_register(); 6708c2ecf20Sopenharmony_ci if (err) 6718c2ecf20Sopenharmony_ci return err; 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci err = bcma_host_soc_register_driver(); 6748c2ecf20Sopenharmony_ci if (err) { 6758c2ecf20Sopenharmony_ci pr_err("SoC host initialization failed\n"); 6768c2ecf20Sopenharmony_ci err = 0; 6778c2ecf20Sopenharmony_ci } 6788c2ecf20Sopenharmony_ci#ifdef CONFIG_BCMA_HOST_PCI 6798c2ecf20Sopenharmony_ci err = bcma_host_pci_init(); 6808c2ecf20Sopenharmony_ci if (err) { 6818c2ecf20Sopenharmony_ci pr_err("PCI host initialization failed\n"); 6828c2ecf20Sopenharmony_ci err = 0; 6838c2ecf20Sopenharmony_ci } 6848c2ecf20Sopenharmony_ci#endif 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci return err; 6878c2ecf20Sopenharmony_ci} 6888c2ecf20Sopenharmony_cimodule_init(bcma_modinit); 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_cistatic void __exit bcma_modexit(void) 6918c2ecf20Sopenharmony_ci{ 6928c2ecf20Sopenharmony_ci#ifdef CONFIG_BCMA_HOST_PCI 6938c2ecf20Sopenharmony_ci bcma_host_pci_exit(); 6948c2ecf20Sopenharmony_ci#endif 6958c2ecf20Sopenharmony_ci bcma_host_soc_unregister_driver(); 6968c2ecf20Sopenharmony_ci bus_unregister(&bcma_bus_type); 6978c2ecf20Sopenharmony_ci} 6988c2ecf20Sopenharmony_cimodule_exit(bcma_modexit) 699