18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Sonics Silicon Backplane 38c2ecf20Sopenharmony_ci * Subsystem core 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2005, Broadcom Corporation 68c2ecf20Sopenharmony_ci * Copyright 2006, 2007, Michael Buesch <m@bues.ch> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Licensed under the GNU/GPL. See COPYING for details. 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include "ssb_private.h" 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/delay.h> 148c2ecf20Sopenharmony_ci#include <linux/io.h> 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 178c2ecf20Sopenharmony_ci#include <linux/ssb/ssb.h> 188c2ecf20Sopenharmony_ci#include <linux/ssb/ssb_regs.h> 198c2ecf20Sopenharmony_ci#include <linux/ssb/ssb_driver_gige.h> 208c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 218c2ecf20Sopenharmony_ci#include <linux/pci.h> 228c2ecf20Sopenharmony_ci#include <linux/mmc/sdio_func.h> 238c2ecf20Sopenharmony_ci#include <linux/slab.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include <pcmcia/cistpl.h> 268c2ecf20Sopenharmony_ci#include <pcmcia/ds.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Sonics Silicon Backplane driver"); 308c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci/* Temporary list of yet-to-be-attached buses */ 348c2ecf20Sopenharmony_cistatic LIST_HEAD(attach_queue); 358c2ecf20Sopenharmony_ci/* List if running buses */ 368c2ecf20Sopenharmony_cistatic LIST_HEAD(buses); 378c2ecf20Sopenharmony_ci/* Software ID counter */ 388c2ecf20Sopenharmony_cistatic unsigned int next_busnumber; 398c2ecf20Sopenharmony_ci/* buses_mutes locks the two buslists and the next_busnumber. 408c2ecf20Sopenharmony_ci * Don't lock this directly, but use ssb_buses_[un]lock() below. */ 418c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(buses_mutex); 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci/* There are differences in the codeflow, if the bus is 448c2ecf20Sopenharmony_ci * initialized from early boot, as various needed services 458c2ecf20Sopenharmony_ci * are not available early. This is a mechanism to delay 468c2ecf20Sopenharmony_ci * these initializations to after early boot has finished. 478c2ecf20Sopenharmony_ci * It's also used to avoid mutex locking, as that's not 488c2ecf20Sopenharmony_ci * available and needed early. */ 498c2ecf20Sopenharmony_cistatic bool ssb_is_early_boot = 1; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic void ssb_buses_lock(void); 528c2ecf20Sopenharmony_cistatic void ssb_buses_unlock(void); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci#ifdef CONFIG_SSB_PCIHOST 568c2ecf20Sopenharmony_cistruct ssb_bus *ssb_pci_dev_to_bus(struct pci_dev *pdev) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci struct ssb_bus *bus; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci ssb_buses_lock(); 618c2ecf20Sopenharmony_ci list_for_each_entry(bus, &buses, list) { 628c2ecf20Sopenharmony_ci if (bus->bustype == SSB_BUSTYPE_PCI && 638c2ecf20Sopenharmony_ci bus->host_pci == pdev) 648c2ecf20Sopenharmony_ci goto found; 658c2ecf20Sopenharmony_ci } 668c2ecf20Sopenharmony_ci bus = NULL; 678c2ecf20Sopenharmony_cifound: 688c2ecf20Sopenharmony_ci ssb_buses_unlock(); 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci return bus; 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci#endif /* CONFIG_SSB_PCIHOST */ 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci#ifdef CONFIG_SSB_PCMCIAHOST 758c2ecf20Sopenharmony_cistruct ssb_bus *ssb_pcmcia_dev_to_bus(struct pcmcia_device *pdev) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci struct ssb_bus *bus; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci ssb_buses_lock(); 808c2ecf20Sopenharmony_ci list_for_each_entry(bus, &buses, list) { 818c2ecf20Sopenharmony_ci if (bus->bustype == SSB_BUSTYPE_PCMCIA && 828c2ecf20Sopenharmony_ci bus->host_pcmcia == pdev) 838c2ecf20Sopenharmony_ci goto found; 848c2ecf20Sopenharmony_ci } 858c2ecf20Sopenharmony_ci bus = NULL; 868c2ecf20Sopenharmony_cifound: 878c2ecf20Sopenharmony_ci ssb_buses_unlock(); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci return bus; 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ci#endif /* CONFIG_SSB_PCMCIAHOST */ 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ciint ssb_for_each_bus_call(unsigned long data, 948c2ecf20Sopenharmony_ci int (*func)(struct ssb_bus *bus, unsigned long data)) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci struct ssb_bus *bus; 978c2ecf20Sopenharmony_ci int res; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci ssb_buses_lock(); 1008c2ecf20Sopenharmony_ci list_for_each_entry(bus, &buses, list) { 1018c2ecf20Sopenharmony_ci res = func(bus, data); 1028c2ecf20Sopenharmony_ci if (res >= 0) { 1038c2ecf20Sopenharmony_ci ssb_buses_unlock(); 1048c2ecf20Sopenharmony_ci return res; 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci } 1078c2ecf20Sopenharmony_ci ssb_buses_unlock(); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci return -ENODEV; 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic struct ssb_device *ssb_device_get(struct ssb_device *dev) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci if (dev) 1158c2ecf20Sopenharmony_ci get_device(dev->dev); 1168c2ecf20Sopenharmony_ci return dev; 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic void ssb_device_put(struct ssb_device *dev) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci if (dev) 1228c2ecf20Sopenharmony_ci put_device(dev->dev); 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic int ssb_device_resume(struct device *dev) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci struct ssb_device *ssb_dev = dev_to_ssb_dev(dev); 1288c2ecf20Sopenharmony_ci struct ssb_driver *ssb_drv; 1298c2ecf20Sopenharmony_ci int err = 0; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci if (dev->driver) { 1328c2ecf20Sopenharmony_ci ssb_drv = drv_to_ssb_drv(dev->driver); 1338c2ecf20Sopenharmony_ci if (ssb_drv && ssb_drv->resume) 1348c2ecf20Sopenharmony_ci err = ssb_drv->resume(ssb_dev); 1358c2ecf20Sopenharmony_ci if (err) 1368c2ecf20Sopenharmony_ci goto out; 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ciout: 1398c2ecf20Sopenharmony_ci return err; 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic int ssb_device_suspend(struct device *dev, pm_message_t state) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci struct ssb_device *ssb_dev = dev_to_ssb_dev(dev); 1458c2ecf20Sopenharmony_ci struct ssb_driver *ssb_drv; 1468c2ecf20Sopenharmony_ci int err = 0; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci if (dev->driver) { 1498c2ecf20Sopenharmony_ci ssb_drv = drv_to_ssb_drv(dev->driver); 1508c2ecf20Sopenharmony_ci if (ssb_drv && ssb_drv->suspend) 1518c2ecf20Sopenharmony_ci err = ssb_drv->suspend(ssb_dev, state); 1528c2ecf20Sopenharmony_ci if (err) 1538c2ecf20Sopenharmony_ci goto out; 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ciout: 1568c2ecf20Sopenharmony_ci return err; 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ciint ssb_bus_resume(struct ssb_bus *bus) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci int err; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci /* Reset HW state information in memory, so that HW is 1648c2ecf20Sopenharmony_ci * completely reinitialized. */ 1658c2ecf20Sopenharmony_ci bus->mapped_device = NULL; 1668c2ecf20Sopenharmony_ci#ifdef CONFIG_SSB_DRIVER_PCICORE 1678c2ecf20Sopenharmony_ci bus->pcicore.setup_done = 0; 1688c2ecf20Sopenharmony_ci#endif 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci err = ssb_bus_powerup(bus, 0); 1718c2ecf20Sopenharmony_ci if (err) 1728c2ecf20Sopenharmony_ci return err; 1738c2ecf20Sopenharmony_ci err = ssb_pcmcia_hardware_setup(bus); 1748c2ecf20Sopenharmony_ci if (err) { 1758c2ecf20Sopenharmony_ci ssb_bus_may_powerdown(bus); 1768c2ecf20Sopenharmony_ci return err; 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci ssb_chipco_resume(&bus->chipco); 1798c2ecf20Sopenharmony_ci ssb_bus_may_powerdown(bus); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci return 0; 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ssb_bus_resume); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ciint ssb_bus_suspend(struct ssb_bus *bus) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci ssb_chipco_suspend(&bus->chipco); 1888c2ecf20Sopenharmony_ci ssb_pci_xtal(bus, SSB_GPIO_XTAL | SSB_GPIO_PLL, 0); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci return 0; 1918c2ecf20Sopenharmony_ci} 1928c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ssb_bus_suspend); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci#ifdef CONFIG_SSB_SPROM 1958c2ecf20Sopenharmony_ci/** ssb_devices_freeze - Freeze all devices on the bus. 1968c2ecf20Sopenharmony_ci * 1978c2ecf20Sopenharmony_ci * After freezing no device driver will be handling a device 1988c2ecf20Sopenharmony_ci * on this bus anymore. ssb_devices_thaw() must be called after 1998c2ecf20Sopenharmony_ci * a successful freeze to reactivate the devices. 2008c2ecf20Sopenharmony_ci * 2018c2ecf20Sopenharmony_ci * @bus: The bus. 2028c2ecf20Sopenharmony_ci * @ctx: Context structure. Pass this to ssb_devices_thaw(). 2038c2ecf20Sopenharmony_ci */ 2048c2ecf20Sopenharmony_ciint ssb_devices_freeze(struct ssb_bus *bus, struct ssb_freeze_context *ctx) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci struct ssb_device *sdev; 2078c2ecf20Sopenharmony_ci struct ssb_driver *sdrv; 2088c2ecf20Sopenharmony_ci unsigned int i; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci memset(ctx, 0, sizeof(*ctx)); 2118c2ecf20Sopenharmony_ci ctx->bus = bus; 2128c2ecf20Sopenharmony_ci WARN_ON(bus->nr_devices > ARRAY_SIZE(ctx->device_frozen)); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci for (i = 0; i < bus->nr_devices; i++) { 2158c2ecf20Sopenharmony_ci sdev = ssb_device_get(&bus->devices[i]); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci if (!sdev->dev || !sdev->dev->driver || 2188c2ecf20Sopenharmony_ci !device_is_registered(sdev->dev)) { 2198c2ecf20Sopenharmony_ci ssb_device_put(sdev); 2208c2ecf20Sopenharmony_ci continue; 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci sdrv = drv_to_ssb_drv(sdev->dev->driver); 2238c2ecf20Sopenharmony_ci if (WARN_ON(!sdrv->remove)) 2248c2ecf20Sopenharmony_ci continue; 2258c2ecf20Sopenharmony_ci sdrv->remove(sdev); 2268c2ecf20Sopenharmony_ci ctx->device_frozen[i] = 1; 2278c2ecf20Sopenharmony_ci } 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci return 0; 2308c2ecf20Sopenharmony_ci} 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci/** ssb_devices_thaw - Unfreeze all devices on the bus. 2338c2ecf20Sopenharmony_ci * 2348c2ecf20Sopenharmony_ci * This will re-attach the device drivers and re-init the devices. 2358c2ecf20Sopenharmony_ci * 2368c2ecf20Sopenharmony_ci * @ctx: The context structure from ssb_devices_freeze() 2378c2ecf20Sopenharmony_ci */ 2388c2ecf20Sopenharmony_ciint ssb_devices_thaw(struct ssb_freeze_context *ctx) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci struct ssb_bus *bus = ctx->bus; 2418c2ecf20Sopenharmony_ci struct ssb_device *sdev; 2428c2ecf20Sopenharmony_ci struct ssb_driver *sdrv; 2438c2ecf20Sopenharmony_ci unsigned int i; 2448c2ecf20Sopenharmony_ci int err, result = 0; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci for (i = 0; i < bus->nr_devices; i++) { 2478c2ecf20Sopenharmony_ci if (!ctx->device_frozen[i]) 2488c2ecf20Sopenharmony_ci continue; 2498c2ecf20Sopenharmony_ci sdev = &bus->devices[i]; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci if (WARN_ON(!sdev->dev || !sdev->dev->driver)) 2528c2ecf20Sopenharmony_ci continue; 2538c2ecf20Sopenharmony_ci sdrv = drv_to_ssb_drv(sdev->dev->driver); 2548c2ecf20Sopenharmony_ci if (WARN_ON(!sdrv || !sdrv->probe)) 2558c2ecf20Sopenharmony_ci continue; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci err = sdrv->probe(sdev, &sdev->id); 2588c2ecf20Sopenharmony_ci if (err) { 2598c2ecf20Sopenharmony_ci dev_err(sdev->dev, 2608c2ecf20Sopenharmony_ci "Failed to thaw device %s\n", 2618c2ecf20Sopenharmony_ci dev_name(sdev->dev)); 2628c2ecf20Sopenharmony_ci result = err; 2638c2ecf20Sopenharmony_ci } 2648c2ecf20Sopenharmony_ci ssb_device_put(sdev); 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci return result; 2688c2ecf20Sopenharmony_ci} 2698c2ecf20Sopenharmony_ci#endif /* CONFIG_SSB_SPROM */ 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_cistatic void ssb_device_shutdown(struct device *dev) 2728c2ecf20Sopenharmony_ci{ 2738c2ecf20Sopenharmony_ci struct ssb_device *ssb_dev = dev_to_ssb_dev(dev); 2748c2ecf20Sopenharmony_ci struct ssb_driver *ssb_drv; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci if (!dev->driver) 2778c2ecf20Sopenharmony_ci return; 2788c2ecf20Sopenharmony_ci ssb_drv = drv_to_ssb_drv(dev->driver); 2798c2ecf20Sopenharmony_ci if (ssb_drv && ssb_drv->shutdown) 2808c2ecf20Sopenharmony_ci ssb_drv->shutdown(ssb_dev); 2818c2ecf20Sopenharmony_ci} 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_cistatic int ssb_device_remove(struct device *dev) 2848c2ecf20Sopenharmony_ci{ 2858c2ecf20Sopenharmony_ci struct ssb_device *ssb_dev = dev_to_ssb_dev(dev); 2868c2ecf20Sopenharmony_ci struct ssb_driver *ssb_drv = drv_to_ssb_drv(dev->driver); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci if (ssb_drv && ssb_drv->remove) 2898c2ecf20Sopenharmony_ci ssb_drv->remove(ssb_dev); 2908c2ecf20Sopenharmony_ci ssb_device_put(ssb_dev); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci return 0; 2938c2ecf20Sopenharmony_ci} 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_cistatic int ssb_device_probe(struct device *dev) 2968c2ecf20Sopenharmony_ci{ 2978c2ecf20Sopenharmony_ci struct ssb_device *ssb_dev = dev_to_ssb_dev(dev); 2988c2ecf20Sopenharmony_ci struct ssb_driver *ssb_drv = drv_to_ssb_drv(dev->driver); 2998c2ecf20Sopenharmony_ci int err = 0; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci ssb_device_get(ssb_dev); 3028c2ecf20Sopenharmony_ci if (ssb_drv && ssb_drv->probe) 3038c2ecf20Sopenharmony_ci err = ssb_drv->probe(ssb_dev, &ssb_dev->id); 3048c2ecf20Sopenharmony_ci if (err) 3058c2ecf20Sopenharmony_ci ssb_device_put(ssb_dev); 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci return err; 3088c2ecf20Sopenharmony_ci} 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_cistatic int ssb_match_devid(const struct ssb_device_id *tabid, 3118c2ecf20Sopenharmony_ci const struct ssb_device_id *devid) 3128c2ecf20Sopenharmony_ci{ 3138c2ecf20Sopenharmony_ci if ((tabid->vendor != devid->vendor) && 3148c2ecf20Sopenharmony_ci tabid->vendor != SSB_ANY_VENDOR) 3158c2ecf20Sopenharmony_ci return 0; 3168c2ecf20Sopenharmony_ci if ((tabid->coreid != devid->coreid) && 3178c2ecf20Sopenharmony_ci tabid->coreid != SSB_ANY_ID) 3188c2ecf20Sopenharmony_ci return 0; 3198c2ecf20Sopenharmony_ci if ((tabid->revision != devid->revision) && 3208c2ecf20Sopenharmony_ci tabid->revision != SSB_ANY_REV) 3218c2ecf20Sopenharmony_ci return 0; 3228c2ecf20Sopenharmony_ci return 1; 3238c2ecf20Sopenharmony_ci} 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_cistatic int ssb_bus_match(struct device *dev, struct device_driver *drv) 3268c2ecf20Sopenharmony_ci{ 3278c2ecf20Sopenharmony_ci struct ssb_device *ssb_dev = dev_to_ssb_dev(dev); 3288c2ecf20Sopenharmony_ci struct ssb_driver *ssb_drv = drv_to_ssb_drv(drv); 3298c2ecf20Sopenharmony_ci const struct ssb_device_id *id; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci for (id = ssb_drv->id_table; 3328c2ecf20Sopenharmony_ci id->vendor || id->coreid || id->revision; 3338c2ecf20Sopenharmony_ci id++) { 3348c2ecf20Sopenharmony_ci if (ssb_match_devid(id, &ssb_dev->id)) 3358c2ecf20Sopenharmony_ci return 1; /* found */ 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci return 0; 3398c2ecf20Sopenharmony_ci} 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_cistatic int ssb_device_uevent(struct device *dev, struct kobj_uevent_env *env) 3428c2ecf20Sopenharmony_ci{ 3438c2ecf20Sopenharmony_ci struct ssb_device *ssb_dev = dev_to_ssb_dev(dev); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci if (!dev) 3468c2ecf20Sopenharmony_ci return -ENODEV; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci return add_uevent_var(env, 3498c2ecf20Sopenharmony_ci "MODALIAS=ssb:v%04Xid%04Xrev%02X", 3508c2ecf20Sopenharmony_ci ssb_dev->id.vendor, ssb_dev->id.coreid, 3518c2ecf20Sopenharmony_ci ssb_dev->id.revision); 3528c2ecf20Sopenharmony_ci} 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci#define ssb_config_attr(attrib, field, format_string) \ 3558c2ecf20Sopenharmony_cistatic ssize_t \ 3568c2ecf20Sopenharmony_ciattrib##_show(struct device *dev, struct device_attribute *attr, char *buf) \ 3578c2ecf20Sopenharmony_ci{ \ 3588c2ecf20Sopenharmony_ci return sprintf(buf, format_string, dev_to_ssb_dev(dev)->field); \ 3598c2ecf20Sopenharmony_ci} \ 3608c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(attrib); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_cissb_config_attr(core_num, core_index, "%u\n") 3638c2ecf20Sopenharmony_cissb_config_attr(coreid, id.coreid, "0x%04x\n") 3648c2ecf20Sopenharmony_cissb_config_attr(vendor, id.vendor, "0x%04x\n") 3658c2ecf20Sopenharmony_cissb_config_attr(revision, id.revision, "%u\n") 3668c2ecf20Sopenharmony_cissb_config_attr(irq, irq, "%u\n") 3678c2ecf20Sopenharmony_cistatic ssize_t 3688c2ecf20Sopenharmony_ciname_show(struct device *dev, struct device_attribute *attr, char *buf) 3698c2ecf20Sopenharmony_ci{ 3708c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", 3718c2ecf20Sopenharmony_ci ssb_core_name(dev_to_ssb_dev(dev)->id.coreid)); 3728c2ecf20Sopenharmony_ci} 3738c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(name); 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_cistatic struct attribute *ssb_device_attrs[] = { 3768c2ecf20Sopenharmony_ci &dev_attr_name.attr, 3778c2ecf20Sopenharmony_ci &dev_attr_core_num.attr, 3788c2ecf20Sopenharmony_ci &dev_attr_coreid.attr, 3798c2ecf20Sopenharmony_ci &dev_attr_vendor.attr, 3808c2ecf20Sopenharmony_ci &dev_attr_revision.attr, 3818c2ecf20Sopenharmony_ci &dev_attr_irq.attr, 3828c2ecf20Sopenharmony_ci NULL, 3838c2ecf20Sopenharmony_ci}; 3848c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(ssb_device); 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_cistatic struct bus_type ssb_bustype = { 3878c2ecf20Sopenharmony_ci .name = "ssb", 3888c2ecf20Sopenharmony_ci .match = ssb_bus_match, 3898c2ecf20Sopenharmony_ci .probe = ssb_device_probe, 3908c2ecf20Sopenharmony_ci .remove = ssb_device_remove, 3918c2ecf20Sopenharmony_ci .shutdown = ssb_device_shutdown, 3928c2ecf20Sopenharmony_ci .suspend = ssb_device_suspend, 3938c2ecf20Sopenharmony_ci .resume = ssb_device_resume, 3948c2ecf20Sopenharmony_ci .uevent = ssb_device_uevent, 3958c2ecf20Sopenharmony_ci .dev_groups = ssb_device_groups, 3968c2ecf20Sopenharmony_ci}; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_cistatic void ssb_buses_lock(void) 3998c2ecf20Sopenharmony_ci{ 4008c2ecf20Sopenharmony_ci /* See the comment at the ssb_is_early_boot definition */ 4018c2ecf20Sopenharmony_ci if (!ssb_is_early_boot) 4028c2ecf20Sopenharmony_ci mutex_lock(&buses_mutex); 4038c2ecf20Sopenharmony_ci} 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_cistatic void ssb_buses_unlock(void) 4068c2ecf20Sopenharmony_ci{ 4078c2ecf20Sopenharmony_ci /* See the comment at the ssb_is_early_boot definition */ 4088c2ecf20Sopenharmony_ci if (!ssb_is_early_boot) 4098c2ecf20Sopenharmony_ci mutex_unlock(&buses_mutex); 4108c2ecf20Sopenharmony_ci} 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_cistatic void ssb_devices_unregister(struct ssb_bus *bus) 4138c2ecf20Sopenharmony_ci{ 4148c2ecf20Sopenharmony_ci struct ssb_device *sdev; 4158c2ecf20Sopenharmony_ci int i; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci for (i = bus->nr_devices - 1; i >= 0; i--) { 4188c2ecf20Sopenharmony_ci sdev = &(bus->devices[i]); 4198c2ecf20Sopenharmony_ci if (sdev->dev) 4208c2ecf20Sopenharmony_ci device_unregister(sdev->dev); 4218c2ecf20Sopenharmony_ci } 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci#ifdef CONFIG_SSB_EMBEDDED 4248c2ecf20Sopenharmony_ci if (bus->bustype == SSB_BUSTYPE_SSB) 4258c2ecf20Sopenharmony_ci platform_device_unregister(bus->watchdog); 4268c2ecf20Sopenharmony_ci#endif 4278c2ecf20Sopenharmony_ci} 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_civoid ssb_bus_unregister(struct ssb_bus *bus) 4308c2ecf20Sopenharmony_ci{ 4318c2ecf20Sopenharmony_ci int err; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci err = ssb_gpio_unregister(bus); 4348c2ecf20Sopenharmony_ci if (err == -EBUSY) 4358c2ecf20Sopenharmony_ci pr_debug("Some GPIOs are still in use\n"); 4368c2ecf20Sopenharmony_ci else if (err) 4378c2ecf20Sopenharmony_ci pr_debug("Can not unregister GPIO driver: %i\n", err); 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci ssb_buses_lock(); 4408c2ecf20Sopenharmony_ci ssb_devices_unregister(bus); 4418c2ecf20Sopenharmony_ci list_del(&bus->list); 4428c2ecf20Sopenharmony_ci ssb_buses_unlock(); 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci ssb_pcmcia_exit(bus); 4458c2ecf20Sopenharmony_ci ssb_pci_exit(bus); 4468c2ecf20Sopenharmony_ci ssb_iounmap(bus); 4478c2ecf20Sopenharmony_ci} 4488c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ssb_bus_unregister); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_cistatic void ssb_release_dev(struct device *dev) 4518c2ecf20Sopenharmony_ci{ 4528c2ecf20Sopenharmony_ci struct __ssb_dev_wrapper *devwrap; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci devwrap = container_of(dev, struct __ssb_dev_wrapper, dev); 4558c2ecf20Sopenharmony_ci kfree(devwrap); 4568c2ecf20Sopenharmony_ci} 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_cistatic int ssb_devices_register(struct ssb_bus *bus) 4598c2ecf20Sopenharmony_ci{ 4608c2ecf20Sopenharmony_ci struct ssb_device *sdev; 4618c2ecf20Sopenharmony_ci struct device *dev; 4628c2ecf20Sopenharmony_ci struct __ssb_dev_wrapper *devwrap; 4638c2ecf20Sopenharmony_ci int i, err = 0; 4648c2ecf20Sopenharmony_ci int dev_idx = 0; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci for (i = 0; i < bus->nr_devices; i++) { 4678c2ecf20Sopenharmony_ci sdev = &(bus->devices[i]); 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci /* We don't register SSB-system devices to the kernel, 4708c2ecf20Sopenharmony_ci * as the drivers for them are built into SSB. */ 4718c2ecf20Sopenharmony_ci switch (sdev->id.coreid) { 4728c2ecf20Sopenharmony_ci case SSB_DEV_CHIPCOMMON: 4738c2ecf20Sopenharmony_ci case SSB_DEV_PCI: 4748c2ecf20Sopenharmony_ci case SSB_DEV_PCIE: 4758c2ecf20Sopenharmony_ci case SSB_DEV_PCMCIA: 4768c2ecf20Sopenharmony_ci case SSB_DEV_MIPS: 4778c2ecf20Sopenharmony_ci case SSB_DEV_MIPS_3302: 4788c2ecf20Sopenharmony_ci case SSB_DEV_EXTIF: 4798c2ecf20Sopenharmony_ci continue; 4808c2ecf20Sopenharmony_ci } 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci devwrap = kzalloc(sizeof(*devwrap), GFP_KERNEL); 4838c2ecf20Sopenharmony_ci if (!devwrap) { 4848c2ecf20Sopenharmony_ci err = -ENOMEM; 4858c2ecf20Sopenharmony_ci goto error; 4868c2ecf20Sopenharmony_ci } 4878c2ecf20Sopenharmony_ci dev = &devwrap->dev; 4888c2ecf20Sopenharmony_ci devwrap->sdev = sdev; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci dev->release = ssb_release_dev; 4918c2ecf20Sopenharmony_ci dev->bus = &ssb_bustype; 4928c2ecf20Sopenharmony_ci dev_set_name(dev, "ssb%u:%d", bus->busnumber, dev_idx); 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci switch (bus->bustype) { 4958c2ecf20Sopenharmony_ci case SSB_BUSTYPE_PCI: 4968c2ecf20Sopenharmony_ci#ifdef CONFIG_SSB_PCIHOST 4978c2ecf20Sopenharmony_ci sdev->irq = bus->host_pci->irq; 4988c2ecf20Sopenharmony_ci dev->parent = &bus->host_pci->dev; 4998c2ecf20Sopenharmony_ci sdev->dma_dev = dev->parent; 5008c2ecf20Sopenharmony_ci#endif 5018c2ecf20Sopenharmony_ci break; 5028c2ecf20Sopenharmony_ci case SSB_BUSTYPE_PCMCIA: 5038c2ecf20Sopenharmony_ci#ifdef CONFIG_SSB_PCMCIAHOST 5048c2ecf20Sopenharmony_ci sdev->irq = bus->host_pcmcia->irq; 5058c2ecf20Sopenharmony_ci dev->parent = &bus->host_pcmcia->dev; 5068c2ecf20Sopenharmony_ci#endif 5078c2ecf20Sopenharmony_ci break; 5088c2ecf20Sopenharmony_ci case SSB_BUSTYPE_SDIO: 5098c2ecf20Sopenharmony_ci#ifdef CONFIG_SSB_SDIOHOST 5108c2ecf20Sopenharmony_ci dev->parent = &bus->host_sdio->dev; 5118c2ecf20Sopenharmony_ci#endif 5128c2ecf20Sopenharmony_ci break; 5138c2ecf20Sopenharmony_ci case SSB_BUSTYPE_SSB: 5148c2ecf20Sopenharmony_ci dev->dma_mask = &dev->coherent_dma_mask; 5158c2ecf20Sopenharmony_ci sdev->dma_dev = dev; 5168c2ecf20Sopenharmony_ci break; 5178c2ecf20Sopenharmony_ci } 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci sdev->dev = dev; 5208c2ecf20Sopenharmony_ci err = device_register(dev); 5218c2ecf20Sopenharmony_ci if (err) { 5228c2ecf20Sopenharmony_ci pr_err("Could not register %s\n", dev_name(dev)); 5238c2ecf20Sopenharmony_ci /* Set dev to NULL to not unregister 5248c2ecf20Sopenharmony_ci * dev on error unwinding. */ 5258c2ecf20Sopenharmony_ci sdev->dev = NULL; 5268c2ecf20Sopenharmony_ci put_device(dev); 5278c2ecf20Sopenharmony_ci goto error; 5288c2ecf20Sopenharmony_ci } 5298c2ecf20Sopenharmony_ci dev_idx++; 5308c2ecf20Sopenharmony_ci } 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci#ifdef CONFIG_SSB_DRIVER_MIPS 5338c2ecf20Sopenharmony_ci if (bus->mipscore.pflash.present) { 5348c2ecf20Sopenharmony_ci err = platform_device_register(&ssb_pflash_dev); 5358c2ecf20Sopenharmony_ci if (err) 5368c2ecf20Sopenharmony_ci pr_err("Error registering parallel flash\n"); 5378c2ecf20Sopenharmony_ci } 5388c2ecf20Sopenharmony_ci#endif 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci#ifdef CONFIG_SSB_SFLASH 5418c2ecf20Sopenharmony_ci if (bus->mipscore.sflash.present) { 5428c2ecf20Sopenharmony_ci err = platform_device_register(&ssb_sflash_dev); 5438c2ecf20Sopenharmony_ci if (err) 5448c2ecf20Sopenharmony_ci pr_err("Error registering serial flash\n"); 5458c2ecf20Sopenharmony_ci } 5468c2ecf20Sopenharmony_ci#endif 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci return 0; 5498c2ecf20Sopenharmony_cierror: 5508c2ecf20Sopenharmony_ci /* Unwind the already registered devices. */ 5518c2ecf20Sopenharmony_ci ssb_devices_unregister(bus); 5528c2ecf20Sopenharmony_ci return err; 5538c2ecf20Sopenharmony_ci} 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci/* Needs ssb_buses_lock() */ 5568c2ecf20Sopenharmony_cistatic int ssb_attach_queued_buses(void) 5578c2ecf20Sopenharmony_ci{ 5588c2ecf20Sopenharmony_ci struct ssb_bus *bus, *n; 5598c2ecf20Sopenharmony_ci int err = 0; 5608c2ecf20Sopenharmony_ci int drop_them_all = 0; 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci list_for_each_entry_safe(bus, n, &attach_queue, list) { 5638c2ecf20Sopenharmony_ci if (drop_them_all) { 5648c2ecf20Sopenharmony_ci list_del(&bus->list); 5658c2ecf20Sopenharmony_ci continue; 5668c2ecf20Sopenharmony_ci } 5678c2ecf20Sopenharmony_ci /* Can't init the PCIcore in ssb_bus_register(), as that 5688c2ecf20Sopenharmony_ci * is too early in boot for embedded systems 5698c2ecf20Sopenharmony_ci * (no udelay() available). So do it here in attach stage. 5708c2ecf20Sopenharmony_ci */ 5718c2ecf20Sopenharmony_ci err = ssb_bus_powerup(bus, 0); 5728c2ecf20Sopenharmony_ci if (err) 5738c2ecf20Sopenharmony_ci goto error; 5748c2ecf20Sopenharmony_ci ssb_pcicore_init(&bus->pcicore); 5758c2ecf20Sopenharmony_ci if (bus->bustype == SSB_BUSTYPE_SSB) 5768c2ecf20Sopenharmony_ci ssb_watchdog_register(bus); 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci err = ssb_gpio_init(bus); 5798c2ecf20Sopenharmony_ci if (err == -ENOTSUPP) 5808c2ecf20Sopenharmony_ci pr_debug("GPIO driver not activated\n"); 5818c2ecf20Sopenharmony_ci else if (err) 5828c2ecf20Sopenharmony_ci pr_debug("Error registering GPIO driver: %i\n", err); 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci ssb_bus_may_powerdown(bus); 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci err = ssb_devices_register(bus); 5878c2ecf20Sopenharmony_cierror: 5888c2ecf20Sopenharmony_ci if (err) { 5898c2ecf20Sopenharmony_ci drop_them_all = 1; 5908c2ecf20Sopenharmony_ci list_del(&bus->list); 5918c2ecf20Sopenharmony_ci continue; 5928c2ecf20Sopenharmony_ci } 5938c2ecf20Sopenharmony_ci list_move_tail(&bus->list, &buses); 5948c2ecf20Sopenharmony_ci } 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci return err; 5978c2ecf20Sopenharmony_ci} 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_cistatic int ssb_fetch_invariants(struct ssb_bus *bus, 6008c2ecf20Sopenharmony_ci ssb_invariants_func_t get_invariants) 6018c2ecf20Sopenharmony_ci{ 6028c2ecf20Sopenharmony_ci struct ssb_init_invariants iv; 6038c2ecf20Sopenharmony_ci int err; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci memset(&iv, 0, sizeof(iv)); 6068c2ecf20Sopenharmony_ci err = get_invariants(bus, &iv); 6078c2ecf20Sopenharmony_ci if (err) 6088c2ecf20Sopenharmony_ci goto out; 6098c2ecf20Sopenharmony_ci memcpy(&bus->boardinfo, &iv.boardinfo, sizeof(iv.boardinfo)); 6108c2ecf20Sopenharmony_ci memcpy(&bus->sprom, &iv.sprom, sizeof(iv.sprom)); 6118c2ecf20Sopenharmony_ci bus->has_cardbus_slot = iv.has_cardbus_slot; 6128c2ecf20Sopenharmony_ciout: 6138c2ecf20Sopenharmony_ci return err; 6148c2ecf20Sopenharmony_ci} 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_cistatic int __maybe_unused 6178c2ecf20Sopenharmony_cissb_bus_register(struct ssb_bus *bus, 6188c2ecf20Sopenharmony_ci ssb_invariants_func_t get_invariants, 6198c2ecf20Sopenharmony_ci unsigned long baseaddr) 6208c2ecf20Sopenharmony_ci{ 6218c2ecf20Sopenharmony_ci int err; 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci spin_lock_init(&bus->bar_lock); 6248c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&bus->list); 6258c2ecf20Sopenharmony_ci#ifdef CONFIG_SSB_EMBEDDED 6268c2ecf20Sopenharmony_ci spin_lock_init(&bus->gpio_lock); 6278c2ecf20Sopenharmony_ci#endif 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci /* Powerup the bus */ 6308c2ecf20Sopenharmony_ci err = ssb_pci_xtal(bus, SSB_GPIO_XTAL | SSB_GPIO_PLL, 1); 6318c2ecf20Sopenharmony_ci if (err) 6328c2ecf20Sopenharmony_ci goto out; 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci /* Init SDIO-host device (if any), before the scan */ 6358c2ecf20Sopenharmony_ci err = ssb_sdio_init(bus); 6368c2ecf20Sopenharmony_ci if (err) 6378c2ecf20Sopenharmony_ci goto err_disable_xtal; 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci ssb_buses_lock(); 6408c2ecf20Sopenharmony_ci bus->busnumber = next_busnumber; 6418c2ecf20Sopenharmony_ci /* Scan for devices (cores) */ 6428c2ecf20Sopenharmony_ci err = ssb_bus_scan(bus, baseaddr); 6438c2ecf20Sopenharmony_ci if (err) 6448c2ecf20Sopenharmony_ci goto err_sdio_exit; 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci /* Init PCI-host device (if any) */ 6478c2ecf20Sopenharmony_ci err = ssb_pci_init(bus); 6488c2ecf20Sopenharmony_ci if (err) 6498c2ecf20Sopenharmony_ci goto err_unmap; 6508c2ecf20Sopenharmony_ci /* Init PCMCIA-host device (if any) */ 6518c2ecf20Sopenharmony_ci err = ssb_pcmcia_init(bus); 6528c2ecf20Sopenharmony_ci if (err) 6538c2ecf20Sopenharmony_ci goto err_pci_exit; 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci /* Initialize basic system devices (if available) */ 6568c2ecf20Sopenharmony_ci err = ssb_bus_powerup(bus, 0); 6578c2ecf20Sopenharmony_ci if (err) 6588c2ecf20Sopenharmony_ci goto err_pcmcia_exit; 6598c2ecf20Sopenharmony_ci ssb_chipcommon_init(&bus->chipco); 6608c2ecf20Sopenharmony_ci ssb_extif_init(&bus->extif); 6618c2ecf20Sopenharmony_ci ssb_mipscore_init(&bus->mipscore); 6628c2ecf20Sopenharmony_ci err = ssb_fetch_invariants(bus, get_invariants); 6638c2ecf20Sopenharmony_ci if (err) { 6648c2ecf20Sopenharmony_ci ssb_bus_may_powerdown(bus); 6658c2ecf20Sopenharmony_ci goto err_pcmcia_exit; 6668c2ecf20Sopenharmony_ci } 6678c2ecf20Sopenharmony_ci ssb_bus_may_powerdown(bus); 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci /* Queue it for attach. 6708c2ecf20Sopenharmony_ci * See the comment at the ssb_is_early_boot definition. */ 6718c2ecf20Sopenharmony_ci list_add_tail(&bus->list, &attach_queue); 6728c2ecf20Sopenharmony_ci if (!ssb_is_early_boot) { 6738c2ecf20Sopenharmony_ci /* This is not early boot, so we must attach the bus now */ 6748c2ecf20Sopenharmony_ci err = ssb_attach_queued_buses(); 6758c2ecf20Sopenharmony_ci if (err) 6768c2ecf20Sopenharmony_ci goto err_dequeue; 6778c2ecf20Sopenharmony_ci } 6788c2ecf20Sopenharmony_ci next_busnumber++; 6798c2ecf20Sopenharmony_ci ssb_buses_unlock(); 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ciout: 6828c2ecf20Sopenharmony_ci return err; 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_cierr_dequeue: 6858c2ecf20Sopenharmony_ci list_del(&bus->list); 6868c2ecf20Sopenharmony_cierr_pcmcia_exit: 6878c2ecf20Sopenharmony_ci ssb_pcmcia_exit(bus); 6888c2ecf20Sopenharmony_cierr_pci_exit: 6898c2ecf20Sopenharmony_ci ssb_pci_exit(bus); 6908c2ecf20Sopenharmony_cierr_unmap: 6918c2ecf20Sopenharmony_ci ssb_iounmap(bus); 6928c2ecf20Sopenharmony_cierr_sdio_exit: 6938c2ecf20Sopenharmony_ci ssb_sdio_exit(bus); 6948c2ecf20Sopenharmony_cierr_disable_xtal: 6958c2ecf20Sopenharmony_ci ssb_buses_unlock(); 6968c2ecf20Sopenharmony_ci ssb_pci_xtal(bus, SSB_GPIO_XTAL | SSB_GPIO_PLL, 0); 6978c2ecf20Sopenharmony_ci return err; 6988c2ecf20Sopenharmony_ci} 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci#ifdef CONFIG_SSB_PCIHOST 7018c2ecf20Sopenharmony_ciint ssb_bus_pcibus_register(struct ssb_bus *bus, struct pci_dev *host_pci) 7028c2ecf20Sopenharmony_ci{ 7038c2ecf20Sopenharmony_ci int err; 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci bus->bustype = SSB_BUSTYPE_PCI; 7068c2ecf20Sopenharmony_ci bus->host_pci = host_pci; 7078c2ecf20Sopenharmony_ci bus->ops = &ssb_pci_ops; 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci err = ssb_bus_register(bus, ssb_pci_get_invariants, 0); 7108c2ecf20Sopenharmony_ci if (!err) { 7118c2ecf20Sopenharmony_ci dev_info(&host_pci->dev, 7128c2ecf20Sopenharmony_ci "Sonics Silicon Backplane found on PCI device %s\n", 7138c2ecf20Sopenharmony_ci dev_name(&host_pci->dev)); 7148c2ecf20Sopenharmony_ci } else { 7158c2ecf20Sopenharmony_ci dev_err(&host_pci->dev, 7168c2ecf20Sopenharmony_ci "Failed to register PCI version of SSB with error %d\n", 7178c2ecf20Sopenharmony_ci err); 7188c2ecf20Sopenharmony_ci } 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci return err; 7218c2ecf20Sopenharmony_ci} 7228c2ecf20Sopenharmony_ci#endif /* CONFIG_SSB_PCIHOST */ 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci#ifdef CONFIG_SSB_PCMCIAHOST 7258c2ecf20Sopenharmony_ciint ssb_bus_pcmciabus_register(struct ssb_bus *bus, 7268c2ecf20Sopenharmony_ci struct pcmcia_device *pcmcia_dev, 7278c2ecf20Sopenharmony_ci unsigned long baseaddr) 7288c2ecf20Sopenharmony_ci{ 7298c2ecf20Sopenharmony_ci int err; 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci bus->bustype = SSB_BUSTYPE_PCMCIA; 7328c2ecf20Sopenharmony_ci bus->host_pcmcia = pcmcia_dev; 7338c2ecf20Sopenharmony_ci bus->ops = &ssb_pcmcia_ops; 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci err = ssb_bus_register(bus, ssb_pcmcia_get_invariants, baseaddr); 7368c2ecf20Sopenharmony_ci if (!err) { 7378c2ecf20Sopenharmony_ci dev_info(&pcmcia_dev->dev, 7388c2ecf20Sopenharmony_ci "Sonics Silicon Backplane found on PCMCIA device %s\n", 7398c2ecf20Sopenharmony_ci pcmcia_dev->devname); 7408c2ecf20Sopenharmony_ci } 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci return err; 7438c2ecf20Sopenharmony_ci} 7448c2ecf20Sopenharmony_ci#endif /* CONFIG_SSB_PCMCIAHOST */ 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci#ifdef CONFIG_SSB_SDIOHOST 7478c2ecf20Sopenharmony_ciint ssb_bus_sdiobus_register(struct ssb_bus *bus, struct sdio_func *func, 7488c2ecf20Sopenharmony_ci unsigned int quirks) 7498c2ecf20Sopenharmony_ci{ 7508c2ecf20Sopenharmony_ci int err; 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci bus->bustype = SSB_BUSTYPE_SDIO; 7538c2ecf20Sopenharmony_ci bus->host_sdio = func; 7548c2ecf20Sopenharmony_ci bus->ops = &ssb_sdio_ops; 7558c2ecf20Sopenharmony_ci bus->quirks = quirks; 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci err = ssb_bus_register(bus, ssb_sdio_get_invariants, ~0); 7588c2ecf20Sopenharmony_ci if (!err) { 7598c2ecf20Sopenharmony_ci dev_info(&func->dev, 7608c2ecf20Sopenharmony_ci "Sonics Silicon Backplane found on SDIO device %s\n", 7618c2ecf20Sopenharmony_ci sdio_func_id(func)); 7628c2ecf20Sopenharmony_ci } 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci return err; 7658c2ecf20Sopenharmony_ci} 7668c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ssb_bus_sdiobus_register); 7678c2ecf20Sopenharmony_ci#endif /* CONFIG_SSB_PCMCIAHOST */ 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci#ifdef CONFIG_SSB_HOST_SOC 7708c2ecf20Sopenharmony_ciint ssb_bus_host_soc_register(struct ssb_bus *bus, unsigned long baseaddr) 7718c2ecf20Sopenharmony_ci{ 7728c2ecf20Sopenharmony_ci int err; 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci bus->bustype = SSB_BUSTYPE_SSB; 7758c2ecf20Sopenharmony_ci bus->ops = &ssb_host_soc_ops; 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci err = ssb_bus_register(bus, ssb_host_soc_get_invariants, baseaddr); 7788c2ecf20Sopenharmony_ci if (!err) { 7798c2ecf20Sopenharmony_ci pr_info("Sonics Silicon Backplane found at address 0x%08lX\n", 7808c2ecf20Sopenharmony_ci baseaddr); 7818c2ecf20Sopenharmony_ci } 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci return err; 7848c2ecf20Sopenharmony_ci} 7858c2ecf20Sopenharmony_ci#endif 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ciint __ssb_driver_register(struct ssb_driver *drv, struct module *owner) 7888c2ecf20Sopenharmony_ci{ 7898c2ecf20Sopenharmony_ci drv->drv.name = drv->name; 7908c2ecf20Sopenharmony_ci drv->drv.bus = &ssb_bustype; 7918c2ecf20Sopenharmony_ci drv->drv.owner = owner; 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci return driver_register(&drv->drv); 7948c2ecf20Sopenharmony_ci} 7958c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__ssb_driver_register); 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_civoid ssb_driver_unregister(struct ssb_driver *drv) 7988c2ecf20Sopenharmony_ci{ 7998c2ecf20Sopenharmony_ci driver_unregister(&drv->drv); 8008c2ecf20Sopenharmony_ci} 8018c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ssb_driver_unregister); 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_civoid ssb_set_devtypedata(struct ssb_device *dev, void *data) 8048c2ecf20Sopenharmony_ci{ 8058c2ecf20Sopenharmony_ci struct ssb_bus *bus = dev->bus; 8068c2ecf20Sopenharmony_ci struct ssb_device *ent; 8078c2ecf20Sopenharmony_ci int i; 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci for (i = 0; i < bus->nr_devices; i++) { 8108c2ecf20Sopenharmony_ci ent = &(bus->devices[i]); 8118c2ecf20Sopenharmony_ci if (ent->id.vendor != dev->id.vendor) 8128c2ecf20Sopenharmony_ci continue; 8138c2ecf20Sopenharmony_ci if (ent->id.coreid != dev->id.coreid) 8148c2ecf20Sopenharmony_ci continue; 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci ent->devtypedata = data; 8178c2ecf20Sopenharmony_ci } 8188c2ecf20Sopenharmony_ci} 8198c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ssb_set_devtypedata); 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_cistatic u32 clkfactor_f6_resolve(u32 v) 8228c2ecf20Sopenharmony_ci{ 8238c2ecf20Sopenharmony_ci /* map the magic values */ 8248c2ecf20Sopenharmony_ci switch (v) { 8258c2ecf20Sopenharmony_ci case SSB_CHIPCO_CLK_F6_2: 8268c2ecf20Sopenharmony_ci return 2; 8278c2ecf20Sopenharmony_ci case SSB_CHIPCO_CLK_F6_3: 8288c2ecf20Sopenharmony_ci return 3; 8298c2ecf20Sopenharmony_ci case SSB_CHIPCO_CLK_F6_4: 8308c2ecf20Sopenharmony_ci return 4; 8318c2ecf20Sopenharmony_ci case SSB_CHIPCO_CLK_F6_5: 8328c2ecf20Sopenharmony_ci return 5; 8338c2ecf20Sopenharmony_ci case SSB_CHIPCO_CLK_F6_6: 8348c2ecf20Sopenharmony_ci return 6; 8358c2ecf20Sopenharmony_ci case SSB_CHIPCO_CLK_F6_7: 8368c2ecf20Sopenharmony_ci return 7; 8378c2ecf20Sopenharmony_ci } 8388c2ecf20Sopenharmony_ci return 0; 8398c2ecf20Sopenharmony_ci} 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci/* Calculate the speed the backplane would run at a given set of clockcontrol values */ 8428c2ecf20Sopenharmony_ciu32 ssb_calc_clock_rate(u32 plltype, u32 n, u32 m) 8438c2ecf20Sopenharmony_ci{ 8448c2ecf20Sopenharmony_ci u32 n1, n2, clock, m1, m2, m3, mc; 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci n1 = (n & SSB_CHIPCO_CLK_N1); 8478c2ecf20Sopenharmony_ci n2 = ((n & SSB_CHIPCO_CLK_N2) >> SSB_CHIPCO_CLK_N2_SHIFT); 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci switch (plltype) { 8508c2ecf20Sopenharmony_ci case SSB_PLLTYPE_6: /* 100/200 or 120/240 only */ 8518c2ecf20Sopenharmony_ci if (m & SSB_CHIPCO_CLK_T6_MMASK) 8528c2ecf20Sopenharmony_ci return SSB_CHIPCO_CLK_T6_M1; 8538c2ecf20Sopenharmony_ci return SSB_CHIPCO_CLK_T6_M0; 8548c2ecf20Sopenharmony_ci case SSB_PLLTYPE_1: /* 48Mhz base, 3 dividers */ 8558c2ecf20Sopenharmony_ci case SSB_PLLTYPE_3: /* 25Mhz, 2 dividers */ 8568c2ecf20Sopenharmony_ci case SSB_PLLTYPE_4: /* 48Mhz, 4 dividers */ 8578c2ecf20Sopenharmony_ci case SSB_PLLTYPE_7: /* 25Mhz, 4 dividers */ 8588c2ecf20Sopenharmony_ci n1 = clkfactor_f6_resolve(n1); 8598c2ecf20Sopenharmony_ci n2 += SSB_CHIPCO_CLK_F5_BIAS; 8608c2ecf20Sopenharmony_ci break; 8618c2ecf20Sopenharmony_ci case SSB_PLLTYPE_2: /* 48Mhz, 4 dividers */ 8628c2ecf20Sopenharmony_ci n1 += SSB_CHIPCO_CLK_T2_BIAS; 8638c2ecf20Sopenharmony_ci n2 += SSB_CHIPCO_CLK_T2_BIAS; 8648c2ecf20Sopenharmony_ci WARN_ON(!((n1 >= 2) && (n1 <= 7))); 8658c2ecf20Sopenharmony_ci WARN_ON(!((n2 >= 5) && (n2 <= 23))); 8668c2ecf20Sopenharmony_ci break; 8678c2ecf20Sopenharmony_ci case SSB_PLLTYPE_5: /* 25Mhz, 4 dividers */ 8688c2ecf20Sopenharmony_ci return 100000000; 8698c2ecf20Sopenharmony_ci default: 8708c2ecf20Sopenharmony_ci WARN_ON(1); 8718c2ecf20Sopenharmony_ci } 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci switch (plltype) { 8748c2ecf20Sopenharmony_ci case SSB_PLLTYPE_3: /* 25Mhz, 2 dividers */ 8758c2ecf20Sopenharmony_ci case SSB_PLLTYPE_7: /* 25Mhz, 4 dividers */ 8768c2ecf20Sopenharmony_ci clock = SSB_CHIPCO_CLK_BASE2 * n1 * n2; 8778c2ecf20Sopenharmony_ci break; 8788c2ecf20Sopenharmony_ci default: 8798c2ecf20Sopenharmony_ci clock = SSB_CHIPCO_CLK_BASE1 * n1 * n2; 8808c2ecf20Sopenharmony_ci } 8818c2ecf20Sopenharmony_ci if (!clock) 8828c2ecf20Sopenharmony_ci return 0; 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci m1 = (m & SSB_CHIPCO_CLK_M1); 8858c2ecf20Sopenharmony_ci m2 = ((m & SSB_CHIPCO_CLK_M2) >> SSB_CHIPCO_CLK_M2_SHIFT); 8868c2ecf20Sopenharmony_ci m3 = ((m & SSB_CHIPCO_CLK_M3) >> SSB_CHIPCO_CLK_M3_SHIFT); 8878c2ecf20Sopenharmony_ci mc = ((m & SSB_CHIPCO_CLK_MC) >> SSB_CHIPCO_CLK_MC_SHIFT); 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci switch (plltype) { 8908c2ecf20Sopenharmony_ci case SSB_PLLTYPE_1: /* 48Mhz base, 3 dividers */ 8918c2ecf20Sopenharmony_ci case SSB_PLLTYPE_3: /* 25Mhz, 2 dividers */ 8928c2ecf20Sopenharmony_ci case SSB_PLLTYPE_4: /* 48Mhz, 4 dividers */ 8938c2ecf20Sopenharmony_ci case SSB_PLLTYPE_7: /* 25Mhz, 4 dividers */ 8948c2ecf20Sopenharmony_ci m1 = clkfactor_f6_resolve(m1); 8958c2ecf20Sopenharmony_ci if ((plltype == SSB_PLLTYPE_1) || 8968c2ecf20Sopenharmony_ci (plltype == SSB_PLLTYPE_3)) 8978c2ecf20Sopenharmony_ci m2 += SSB_CHIPCO_CLK_F5_BIAS; 8988c2ecf20Sopenharmony_ci else 8998c2ecf20Sopenharmony_ci m2 = clkfactor_f6_resolve(m2); 9008c2ecf20Sopenharmony_ci m3 = clkfactor_f6_resolve(m3); 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci switch (mc) { 9038c2ecf20Sopenharmony_ci case SSB_CHIPCO_CLK_MC_BYPASS: 9048c2ecf20Sopenharmony_ci return clock; 9058c2ecf20Sopenharmony_ci case SSB_CHIPCO_CLK_MC_M1: 9068c2ecf20Sopenharmony_ci return (clock / m1); 9078c2ecf20Sopenharmony_ci case SSB_CHIPCO_CLK_MC_M1M2: 9088c2ecf20Sopenharmony_ci return (clock / (m1 * m2)); 9098c2ecf20Sopenharmony_ci case SSB_CHIPCO_CLK_MC_M1M2M3: 9108c2ecf20Sopenharmony_ci return (clock / (m1 * m2 * m3)); 9118c2ecf20Sopenharmony_ci case SSB_CHIPCO_CLK_MC_M1M3: 9128c2ecf20Sopenharmony_ci return (clock / (m1 * m3)); 9138c2ecf20Sopenharmony_ci } 9148c2ecf20Sopenharmony_ci return 0; 9158c2ecf20Sopenharmony_ci case SSB_PLLTYPE_2: 9168c2ecf20Sopenharmony_ci m1 += SSB_CHIPCO_CLK_T2_BIAS; 9178c2ecf20Sopenharmony_ci m2 += SSB_CHIPCO_CLK_T2M2_BIAS; 9188c2ecf20Sopenharmony_ci m3 += SSB_CHIPCO_CLK_T2_BIAS; 9198c2ecf20Sopenharmony_ci WARN_ON(!((m1 >= 2) && (m1 <= 7))); 9208c2ecf20Sopenharmony_ci WARN_ON(!((m2 >= 3) && (m2 <= 10))); 9218c2ecf20Sopenharmony_ci WARN_ON(!((m3 >= 2) && (m3 <= 7))); 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci if (!(mc & SSB_CHIPCO_CLK_T2MC_M1BYP)) 9248c2ecf20Sopenharmony_ci clock /= m1; 9258c2ecf20Sopenharmony_ci if (!(mc & SSB_CHIPCO_CLK_T2MC_M2BYP)) 9268c2ecf20Sopenharmony_ci clock /= m2; 9278c2ecf20Sopenharmony_ci if (!(mc & SSB_CHIPCO_CLK_T2MC_M3BYP)) 9288c2ecf20Sopenharmony_ci clock /= m3; 9298c2ecf20Sopenharmony_ci return clock; 9308c2ecf20Sopenharmony_ci default: 9318c2ecf20Sopenharmony_ci WARN_ON(1); 9328c2ecf20Sopenharmony_ci } 9338c2ecf20Sopenharmony_ci return 0; 9348c2ecf20Sopenharmony_ci} 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci/* Get the current speed the backplane is running at */ 9378c2ecf20Sopenharmony_ciu32 ssb_clockspeed(struct ssb_bus *bus) 9388c2ecf20Sopenharmony_ci{ 9398c2ecf20Sopenharmony_ci u32 rate; 9408c2ecf20Sopenharmony_ci u32 plltype; 9418c2ecf20Sopenharmony_ci u32 clkctl_n, clkctl_m; 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci if (bus->chipco.capabilities & SSB_CHIPCO_CAP_PMU) 9448c2ecf20Sopenharmony_ci return ssb_pmu_get_controlclock(&bus->chipco); 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci if (ssb_extif_available(&bus->extif)) 9478c2ecf20Sopenharmony_ci ssb_extif_get_clockcontrol(&bus->extif, &plltype, 9488c2ecf20Sopenharmony_ci &clkctl_n, &clkctl_m); 9498c2ecf20Sopenharmony_ci else if (bus->chipco.dev) 9508c2ecf20Sopenharmony_ci ssb_chipco_get_clockcontrol(&bus->chipco, &plltype, 9518c2ecf20Sopenharmony_ci &clkctl_n, &clkctl_m); 9528c2ecf20Sopenharmony_ci else 9538c2ecf20Sopenharmony_ci return 0; 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci if (bus->chip_id == 0x5365) { 9568c2ecf20Sopenharmony_ci rate = 100000000; 9578c2ecf20Sopenharmony_ci } else { 9588c2ecf20Sopenharmony_ci rate = ssb_calc_clock_rate(plltype, clkctl_n, clkctl_m); 9598c2ecf20Sopenharmony_ci if (plltype == SSB_PLLTYPE_3) /* 25Mhz, 2 dividers */ 9608c2ecf20Sopenharmony_ci rate /= 2; 9618c2ecf20Sopenharmony_ci } 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci return rate; 9648c2ecf20Sopenharmony_ci} 9658c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ssb_clockspeed); 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_cistatic u32 ssb_tmslow_reject_bitmask(struct ssb_device *dev) 9688c2ecf20Sopenharmony_ci{ 9698c2ecf20Sopenharmony_ci u32 rev = ssb_read32(dev, SSB_IDLOW) & SSB_IDLOW_SSBREV; 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci /* The REJECT bit seems to be different for Backplane rev 2.3 */ 9728c2ecf20Sopenharmony_ci switch (rev) { 9738c2ecf20Sopenharmony_ci case SSB_IDLOW_SSBREV_22: 9748c2ecf20Sopenharmony_ci case SSB_IDLOW_SSBREV_24: 9758c2ecf20Sopenharmony_ci case SSB_IDLOW_SSBREV_26: 9768c2ecf20Sopenharmony_ci return SSB_TMSLOW_REJECT; 9778c2ecf20Sopenharmony_ci case SSB_IDLOW_SSBREV_23: 9788c2ecf20Sopenharmony_ci return SSB_TMSLOW_REJECT_23; 9798c2ecf20Sopenharmony_ci case SSB_IDLOW_SSBREV_25: /* TODO - find the proper REJECT bit */ 9808c2ecf20Sopenharmony_ci case SSB_IDLOW_SSBREV_27: /* same here */ 9818c2ecf20Sopenharmony_ci return SSB_TMSLOW_REJECT; /* this is a guess */ 9828c2ecf20Sopenharmony_ci case SSB_IDLOW_SSBREV: 9838c2ecf20Sopenharmony_ci break; 9848c2ecf20Sopenharmony_ci default: 9858c2ecf20Sopenharmony_ci WARN(1, KERN_INFO "ssb: Backplane Revision 0x%.8X\n", rev); 9868c2ecf20Sopenharmony_ci } 9878c2ecf20Sopenharmony_ci return (SSB_TMSLOW_REJECT | SSB_TMSLOW_REJECT_23); 9888c2ecf20Sopenharmony_ci} 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ciint ssb_device_is_enabled(struct ssb_device *dev) 9918c2ecf20Sopenharmony_ci{ 9928c2ecf20Sopenharmony_ci u32 val; 9938c2ecf20Sopenharmony_ci u32 reject; 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci reject = ssb_tmslow_reject_bitmask(dev); 9968c2ecf20Sopenharmony_ci val = ssb_read32(dev, SSB_TMSLOW); 9978c2ecf20Sopenharmony_ci val &= SSB_TMSLOW_CLOCK | SSB_TMSLOW_RESET | reject; 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci return (val == SSB_TMSLOW_CLOCK); 10008c2ecf20Sopenharmony_ci} 10018c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ssb_device_is_enabled); 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_cistatic void ssb_flush_tmslow(struct ssb_device *dev) 10048c2ecf20Sopenharmony_ci{ 10058c2ecf20Sopenharmony_ci /* Make _really_ sure the device has finished the TMSLOW 10068c2ecf20Sopenharmony_ci * register write transaction, as we risk running into 10078c2ecf20Sopenharmony_ci * a machine check exception otherwise. 10088c2ecf20Sopenharmony_ci * Do this by reading the register back to commit the 10098c2ecf20Sopenharmony_ci * PCI write and delay an additional usec for the device 10108c2ecf20Sopenharmony_ci * to react to the change. */ 10118c2ecf20Sopenharmony_ci ssb_read32(dev, SSB_TMSLOW); 10128c2ecf20Sopenharmony_ci udelay(1); 10138c2ecf20Sopenharmony_ci} 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_civoid ssb_device_enable(struct ssb_device *dev, u32 core_specific_flags) 10168c2ecf20Sopenharmony_ci{ 10178c2ecf20Sopenharmony_ci u32 val; 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci ssb_device_disable(dev, core_specific_flags); 10208c2ecf20Sopenharmony_ci ssb_write32(dev, SSB_TMSLOW, 10218c2ecf20Sopenharmony_ci SSB_TMSLOW_RESET | SSB_TMSLOW_CLOCK | 10228c2ecf20Sopenharmony_ci SSB_TMSLOW_FGC | core_specific_flags); 10238c2ecf20Sopenharmony_ci ssb_flush_tmslow(dev); 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci /* Clear SERR if set. This is a hw bug workaround. */ 10268c2ecf20Sopenharmony_ci if (ssb_read32(dev, SSB_TMSHIGH) & SSB_TMSHIGH_SERR) 10278c2ecf20Sopenharmony_ci ssb_write32(dev, SSB_TMSHIGH, 0); 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci val = ssb_read32(dev, SSB_IMSTATE); 10308c2ecf20Sopenharmony_ci if (val & (SSB_IMSTATE_IBE | SSB_IMSTATE_TO)) { 10318c2ecf20Sopenharmony_ci val &= ~(SSB_IMSTATE_IBE | SSB_IMSTATE_TO); 10328c2ecf20Sopenharmony_ci ssb_write32(dev, SSB_IMSTATE, val); 10338c2ecf20Sopenharmony_ci } 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_ci ssb_write32(dev, SSB_TMSLOW, 10368c2ecf20Sopenharmony_ci SSB_TMSLOW_CLOCK | SSB_TMSLOW_FGC | 10378c2ecf20Sopenharmony_ci core_specific_flags); 10388c2ecf20Sopenharmony_ci ssb_flush_tmslow(dev); 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_ci ssb_write32(dev, SSB_TMSLOW, SSB_TMSLOW_CLOCK | 10418c2ecf20Sopenharmony_ci core_specific_flags); 10428c2ecf20Sopenharmony_ci ssb_flush_tmslow(dev); 10438c2ecf20Sopenharmony_ci} 10448c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ssb_device_enable); 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_ci/* Wait for bitmask in a register to get set or cleared. 10478c2ecf20Sopenharmony_ci * timeout is in units of ten-microseconds */ 10488c2ecf20Sopenharmony_cistatic int ssb_wait_bits(struct ssb_device *dev, u16 reg, u32 bitmask, 10498c2ecf20Sopenharmony_ci int timeout, int set) 10508c2ecf20Sopenharmony_ci{ 10518c2ecf20Sopenharmony_ci int i; 10528c2ecf20Sopenharmony_ci u32 val; 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_ci for (i = 0; i < timeout; i++) { 10558c2ecf20Sopenharmony_ci val = ssb_read32(dev, reg); 10568c2ecf20Sopenharmony_ci if (set) { 10578c2ecf20Sopenharmony_ci if ((val & bitmask) == bitmask) 10588c2ecf20Sopenharmony_ci return 0; 10598c2ecf20Sopenharmony_ci } else { 10608c2ecf20Sopenharmony_ci if (!(val & bitmask)) 10618c2ecf20Sopenharmony_ci return 0; 10628c2ecf20Sopenharmony_ci } 10638c2ecf20Sopenharmony_ci udelay(10); 10648c2ecf20Sopenharmony_ci } 10658c2ecf20Sopenharmony_ci dev_err(dev->dev, 10668c2ecf20Sopenharmony_ci "Timeout waiting for bitmask %08X on register %04X to %s\n", 10678c2ecf20Sopenharmony_ci bitmask, reg, set ? "set" : "clear"); 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci return -ETIMEDOUT; 10708c2ecf20Sopenharmony_ci} 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_civoid ssb_device_disable(struct ssb_device *dev, u32 core_specific_flags) 10738c2ecf20Sopenharmony_ci{ 10748c2ecf20Sopenharmony_ci u32 reject, val; 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_ci if (ssb_read32(dev, SSB_TMSLOW) & SSB_TMSLOW_RESET) 10778c2ecf20Sopenharmony_ci return; 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci reject = ssb_tmslow_reject_bitmask(dev); 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci if (ssb_read32(dev, SSB_TMSLOW) & SSB_TMSLOW_CLOCK) { 10828c2ecf20Sopenharmony_ci ssb_write32(dev, SSB_TMSLOW, reject | SSB_TMSLOW_CLOCK); 10838c2ecf20Sopenharmony_ci ssb_wait_bits(dev, SSB_TMSLOW, reject, 1000, 1); 10848c2ecf20Sopenharmony_ci ssb_wait_bits(dev, SSB_TMSHIGH, SSB_TMSHIGH_BUSY, 1000, 0); 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_ci if (ssb_read32(dev, SSB_IDLOW) & SSB_IDLOW_INITIATOR) { 10878c2ecf20Sopenharmony_ci val = ssb_read32(dev, SSB_IMSTATE); 10888c2ecf20Sopenharmony_ci val |= SSB_IMSTATE_REJECT; 10898c2ecf20Sopenharmony_ci ssb_write32(dev, SSB_IMSTATE, val); 10908c2ecf20Sopenharmony_ci ssb_wait_bits(dev, SSB_IMSTATE, SSB_IMSTATE_BUSY, 1000, 10918c2ecf20Sopenharmony_ci 0); 10928c2ecf20Sopenharmony_ci } 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci ssb_write32(dev, SSB_TMSLOW, 10958c2ecf20Sopenharmony_ci SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK | 10968c2ecf20Sopenharmony_ci reject | SSB_TMSLOW_RESET | 10978c2ecf20Sopenharmony_ci core_specific_flags); 10988c2ecf20Sopenharmony_ci ssb_flush_tmslow(dev); 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_ci if (ssb_read32(dev, SSB_IDLOW) & SSB_IDLOW_INITIATOR) { 11018c2ecf20Sopenharmony_ci val = ssb_read32(dev, SSB_IMSTATE); 11028c2ecf20Sopenharmony_ci val &= ~SSB_IMSTATE_REJECT; 11038c2ecf20Sopenharmony_ci ssb_write32(dev, SSB_IMSTATE, val); 11048c2ecf20Sopenharmony_ci } 11058c2ecf20Sopenharmony_ci } 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_ci ssb_write32(dev, SSB_TMSLOW, 11088c2ecf20Sopenharmony_ci reject | SSB_TMSLOW_RESET | 11098c2ecf20Sopenharmony_ci core_specific_flags); 11108c2ecf20Sopenharmony_ci ssb_flush_tmslow(dev); 11118c2ecf20Sopenharmony_ci} 11128c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ssb_device_disable); 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_ci/* Some chipsets need routing known for PCIe and 64-bit DMA */ 11158c2ecf20Sopenharmony_cistatic bool ssb_dma_translation_special_bit(struct ssb_device *dev) 11168c2ecf20Sopenharmony_ci{ 11178c2ecf20Sopenharmony_ci u16 chip_id = dev->bus->chip_id; 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci if (dev->id.coreid == SSB_DEV_80211) { 11208c2ecf20Sopenharmony_ci return (chip_id == 0x4322 || chip_id == 43221 || 11218c2ecf20Sopenharmony_ci chip_id == 43231 || chip_id == 43222); 11228c2ecf20Sopenharmony_ci } 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci return false; 11258c2ecf20Sopenharmony_ci} 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_ciu32 ssb_dma_translation(struct ssb_device *dev) 11288c2ecf20Sopenharmony_ci{ 11298c2ecf20Sopenharmony_ci switch (dev->bus->bustype) { 11308c2ecf20Sopenharmony_ci case SSB_BUSTYPE_SSB: 11318c2ecf20Sopenharmony_ci return 0; 11328c2ecf20Sopenharmony_ci case SSB_BUSTYPE_PCI: 11338c2ecf20Sopenharmony_ci if (pci_is_pcie(dev->bus->host_pci) && 11348c2ecf20Sopenharmony_ci ssb_read32(dev, SSB_TMSHIGH) & SSB_TMSHIGH_DMA64) { 11358c2ecf20Sopenharmony_ci return SSB_PCIE_DMA_H32; 11368c2ecf20Sopenharmony_ci } else { 11378c2ecf20Sopenharmony_ci if (ssb_dma_translation_special_bit(dev)) 11388c2ecf20Sopenharmony_ci return SSB_PCIE_DMA_H32; 11398c2ecf20Sopenharmony_ci else 11408c2ecf20Sopenharmony_ci return SSB_PCI_DMA; 11418c2ecf20Sopenharmony_ci } 11428c2ecf20Sopenharmony_ci default: 11438c2ecf20Sopenharmony_ci __ssb_dma_not_implemented(dev); 11448c2ecf20Sopenharmony_ci } 11458c2ecf20Sopenharmony_ci return 0; 11468c2ecf20Sopenharmony_ci} 11478c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ssb_dma_translation); 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ciint ssb_bus_may_powerdown(struct ssb_bus *bus) 11508c2ecf20Sopenharmony_ci{ 11518c2ecf20Sopenharmony_ci struct ssb_chipcommon *cc; 11528c2ecf20Sopenharmony_ci int err = 0; 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci /* On buses where more than one core may be working 11558c2ecf20Sopenharmony_ci * at a time, we must not powerdown stuff if there are 11568c2ecf20Sopenharmony_ci * still cores that may want to run. */ 11578c2ecf20Sopenharmony_ci if (bus->bustype == SSB_BUSTYPE_SSB) 11588c2ecf20Sopenharmony_ci goto out; 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_ci cc = &bus->chipco; 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci if (!cc->dev) 11638c2ecf20Sopenharmony_ci goto out; 11648c2ecf20Sopenharmony_ci if (cc->dev->id.revision < 5) 11658c2ecf20Sopenharmony_ci goto out; 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_ci ssb_chipco_set_clockmode(cc, SSB_CLKMODE_SLOW); 11688c2ecf20Sopenharmony_ci err = ssb_pci_xtal(bus, SSB_GPIO_XTAL | SSB_GPIO_PLL, 0); 11698c2ecf20Sopenharmony_ci if (err) 11708c2ecf20Sopenharmony_ci goto error; 11718c2ecf20Sopenharmony_ciout: 11728c2ecf20Sopenharmony_ci bus->powered_up = 0; 11738c2ecf20Sopenharmony_ci return err; 11748c2ecf20Sopenharmony_cierror: 11758c2ecf20Sopenharmony_ci pr_err("Bus powerdown failed\n"); 11768c2ecf20Sopenharmony_ci goto out; 11778c2ecf20Sopenharmony_ci} 11788c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ssb_bus_may_powerdown); 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ciint ssb_bus_powerup(struct ssb_bus *bus, bool dynamic_pctl) 11818c2ecf20Sopenharmony_ci{ 11828c2ecf20Sopenharmony_ci int err; 11838c2ecf20Sopenharmony_ci enum ssb_clkmode mode; 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci err = ssb_pci_xtal(bus, SSB_GPIO_XTAL | SSB_GPIO_PLL, 1); 11868c2ecf20Sopenharmony_ci if (err) 11878c2ecf20Sopenharmony_ci goto error; 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_ci bus->powered_up = 1; 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_ci mode = dynamic_pctl ? SSB_CLKMODE_DYNAMIC : SSB_CLKMODE_FAST; 11928c2ecf20Sopenharmony_ci ssb_chipco_set_clockmode(&bus->chipco, mode); 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_ci return 0; 11958c2ecf20Sopenharmony_cierror: 11968c2ecf20Sopenharmony_ci pr_err("Bus powerup failed\n"); 11978c2ecf20Sopenharmony_ci return err; 11988c2ecf20Sopenharmony_ci} 11998c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ssb_bus_powerup); 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_cistatic void ssb_broadcast_value(struct ssb_device *dev, 12028c2ecf20Sopenharmony_ci u32 address, u32 data) 12038c2ecf20Sopenharmony_ci{ 12048c2ecf20Sopenharmony_ci#ifdef CONFIG_SSB_DRIVER_PCICORE 12058c2ecf20Sopenharmony_ci /* This is used for both, PCI and ChipCommon core, so be careful. */ 12068c2ecf20Sopenharmony_ci BUILD_BUG_ON(SSB_PCICORE_BCAST_ADDR != SSB_CHIPCO_BCAST_ADDR); 12078c2ecf20Sopenharmony_ci BUILD_BUG_ON(SSB_PCICORE_BCAST_DATA != SSB_CHIPCO_BCAST_DATA); 12088c2ecf20Sopenharmony_ci#endif 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci ssb_write32(dev, SSB_CHIPCO_BCAST_ADDR, address); 12118c2ecf20Sopenharmony_ci ssb_read32(dev, SSB_CHIPCO_BCAST_ADDR); /* flush */ 12128c2ecf20Sopenharmony_ci ssb_write32(dev, SSB_CHIPCO_BCAST_DATA, data); 12138c2ecf20Sopenharmony_ci ssb_read32(dev, SSB_CHIPCO_BCAST_DATA); /* flush */ 12148c2ecf20Sopenharmony_ci} 12158c2ecf20Sopenharmony_ci 12168c2ecf20Sopenharmony_civoid ssb_commit_settings(struct ssb_bus *bus) 12178c2ecf20Sopenharmony_ci{ 12188c2ecf20Sopenharmony_ci struct ssb_device *dev; 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci#ifdef CONFIG_SSB_DRIVER_PCICORE 12218c2ecf20Sopenharmony_ci dev = bus->chipco.dev ? bus->chipco.dev : bus->pcicore.dev; 12228c2ecf20Sopenharmony_ci#else 12238c2ecf20Sopenharmony_ci dev = bus->chipco.dev; 12248c2ecf20Sopenharmony_ci#endif 12258c2ecf20Sopenharmony_ci if (WARN_ON(!dev)) 12268c2ecf20Sopenharmony_ci return; 12278c2ecf20Sopenharmony_ci /* This forces an update of the cached registers. */ 12288c2ecf20Sopenharmony_ci ssb_broadcast_value(dev, 0xFD8, 0); 12298c2ecf20Sopenharmony_ci} 12308c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ssb_commit_settings); 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ciu32 ssb_admatch_base(u32 adm) 12338c2ecf20Sopenharmony_ci{ 12348c2ecf20Sopenharmony_ci u32 base = 0; 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_ci switch (adm & SSB_ADM_TYPE) { 12378c2ecf20Sopenharmony_ci case SSB_ADM_TYPE0: 12388c2ecf20Sopenharmony_ci base = (adm & SSB_ADM_BASE0); 12398c2ecf20Sopenharmony_ci break; 12408c2ecf20Sopenharmony_ci case SSB_ADM_TYPE1: 12418c2ecf20Sopenharmony_ci WARN_ON(adm & SSB_ADM_NEG); /* unsupported */ 12428c2ecf20Sopenharmony_ci base = (adm & SSB_ADM_BASE1); 12438c2ecf20Sopenharmony_ci break; 12448c2ecf20Sopenharmony_ci case SSB_ADM_TYPE2: 12458c2ecf20Sopenharmony_ci WARN_ON(adm & SSB_ADM_NEG); /* unsupported */ 12468c2ecf20Sopenharmony_ci base = (adm & SSB_ADM_BASE2); 12478c2ecf20Sopenharmony_ci break; 12488c2ecf20Sopenharmony_ci default: 12498c2ecf20Sopenharmony_ci WARN_ON(1); 12508c2ecf20Sopenharmony_ci } 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_ci return base; 12538c2ecf20Sopenharmony_ci} 12548c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ssb_admatch_base); 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_ciu32 ssb_admatch_size(u32 adm) 12578c2ecf20Sopenharmony_ci{ 12588c2ecf20Sopenharmony_ci u32 size = 0; 12598c2ecf20Sopenharmony_ci 12608c2ecf20Sopenharmony_ci switch (adm & SSB_ADM_TYPE) { 12618c2ecf20Sopenharmony_ci case SSB_ADM_TYPE0: 12628c2ecf20Sopenharmony_ci size = ((adm & SSB_ADM_SZ0) >> SSB_ADM_SZ0_SHIFT); 12638c2ecf20Sopenharmony_ci break; 12648c2ecf20Sopenharmony_ci case SSB_ADM_TYPE1: 12658c2ecf20Sopenharmony_ci WARN_ON(adm & SSB_ADM_NEG); /* unsupported */ 12668c2ecf20Sopenharmony_ci size = ((adm & SSB_ADM_SZ1) >> SSB_ADM_SZ1_SHIFT); 12678c2ecf20Sopenharmony_ci break; 12688c2ecf20Sopenharmony_ci case SSB_ADM_TYPE2: 12698c2ecf20Sopenharmony_ci WARN_ON(adm & SSB_ADM_NEG); /* unsupported */ 12708c2ecf20Sopenharmony_ci size = ((adm & SSB_ADM_SZ2) >> SSB_ADM_SZ2_SHIFT); 12718c2ecf20Sopenharmony_ci break; 12728c2ecf20Sopenharmony_ci default: 12738c2ecf20Sopenharmony_ci WARN_ON(1); 12748c2ecf20Sopenharmony_ci } 12758c2ecf20Sopenharmony_ci size = (1 << (size + 1)); 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_ci return size; 12788c2ecf20Sopenharmony_ci} 12798c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ssb_admatch_size); 12808c2ecf20Sopenharmony_ci 12818c2ecf20Sopenharmony_cistatic int __init ssb_modinit(void) 12828c2ecf20Sopenharmony_ci{ 12838c2ecf20Sopenharmony_ci int err; 12848c2ecf20Sopenharmony_ci 12858c2ecf20Sopenharmony_ci /* See the comment at the ssb_is_early_boot definition */ 12868c2ecf20Sopenharmony_ci ssb_is_early_boot = 0; 12878c2ecf20Sopenharmony_ci err = bus_register(&ssb_bustype); 12888c2ecf20Sopenharmony_ci if (err) 12898c2ecf20Sopenharmony_ci return err; 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ci /* Maybe we already registered some buses at early boot. 12928c2ecf20Sopenharmony_ci * Check for this and attach them 12938c2ecf20Sopenharmony_ci */ 12948c2ecf20Sopenharmony_ci ssb_buses_lock(); 12958c2ecf20Sopenharmony_ci err = ssb_attach_queued_buses(); 12968c2ecf20Sopenharmony_ci ssb_buses_unlock(); 12978c2ecf20Sopenharmony_ci if (err) { 12988c2ecf20Sopenharmony_ci bus_unregister(&ssb_bustype); 12998c2ecf20Sopenharmony_ci goto out; 13008c2ecf20Sopenharmony_ci } 13018c2ecf20Sopenharmony_ci 13028c2ecf20Sopenharmony_ci err = b43_pci_ssb_bridge_init(); 13038c2ecf20Sopenharmony_ci if (err) { 13048c2ecf20Sopenharmony_ci pr_err("Broadcom 43xx PCI-SSB-bridge initialization failed\n"); 13058c2ecf20Sopenharmony_ci /* don't fail SSB init because of this */ 13068c2ecf20Sopenharmony_ci err = 0; 13078c2ecf20Sopenharmony_ci } 13088c2ecf20Sopenharmony_ci err = ssb_host_pcmcia_init(); 13098c2ecf20Sopenharmony_ci if (err) { 13108c2ecf20Sopenharmony_ci pr_err("PCMCIA host initialization failed\n"); 13118c2ecf20Sopenharmony_ci /* don't fail SSB init because of this */ 13128c2ecf20Sopenharmony_ci err = 0; 13138c2ecf20Sopenharmony_ci } 13148c2ecf20Sopenharmony_ci err = ssb_gige_init(); 13158c2ecf20Sopenharmony_ci if (err) { 13168c2ecf20Sopenharmony_ci pr_err("SSB Broadcom Gigabit Ethernet driver initialization failed\n"); 13178c2ecf20Sopenharmony_ci /* don't fail SSB init because of this */ 13188c2ecf20Sopenharmony_ci err = 0; 13198c2ecf20Sopenharmony_ci } 13208c2ecf20Sopenharmony_ciout: 13218c2ecf20Sopenharmony_ci return err; 13228c2ecf20Sopenharmony_ci} 13238c2ecf20Sopenharmony_ci/* ssb must be initialized after PCI but before the ssb drivers. 13248c2ecf20Sopenharmony_ci * That means we must use some initcall between subsys_initcall 13258c2ecf20Sopenharmony_ci * and device_initcall. */ 13268c2ecf20Sopenharmony_cifs_initcall(ssb_modinit); 13278c2ecf20Sopenharmony_ci 13288c2ecf20Sopenharmony_cistatic void __exit ssb_modexit(void) 13298c2ecf20Sopenharmony_ci{ 13308c2ecf20Sopenharmony_ci ssb_gige_exit(); 13318c2ecf20Sopenharmony_ci ssb_host_pcmcia_exit(); 13328c2ecf20Sopenharmony_ci b43_pci_ssb_bridge_exit(); 13338c2ecf20Sopenharmony_ci bus_unregister(&ssb_bustype); 13348c2ecf20Sopenharmony_ci} 13358c2ecf20Sopenharmony_cimodule_exit(ssb_modexit) 1336