diff options
Diffstat (limited to 'drivers/amba')
-rw-r--r-- | drivers/amba/bus.c | 313 |
1 files changed, 145 insertions, 168 deletions
diff --git a/drivers/amba/bus.c b/drivers/amba/bus.c index 0cb20324da16..32b0e0b930c1 100644 --- a/drivers/amba/bus.c +++ b/drivers/amba/bus.c @@ -130,11 +130,100 @@ static struct attribute *amba_dev_attrs[] = { }; ATTRIBUTE_GROUPS(amba_dev); +static int amba_read_periphid(struct amba_device *dev) +{ + struct reset_control *rstc; + u32 size, pid, cid; + void __iomem *tmp; + int i, ret; + + ret = dev_pm_domain_attach(&dev->dev, true); + if (ret) { + dev_dbg(&dev->dev, "can't get PM domain: %d\n", ret); + goto err_out; + } + + ret = amba_get_enable_pclk(dev); + if (ret) { + dev_dbg(&dev->dev, "can't get pclk: %d\n", ret); + goto err_pm; + } + + /* + * Find reset control(s) of the amba bus and de-assert them. + */ + rstc = of_reset_control_array_get_optional_shared(dev->dev.of_node); + if (IS_ERR(rstc)) { + ret = PTR_ERR(rstc); + if (ret != -EPROBE_DEFER) + dev_err(&dev->dev, "can't get reset: %d\n", ret); + goto err_clk; + } + reset_control_deassert(rstc); + reset_control_put(rstc); + + size = resource_size(&dev->res); + tmp = ioremap(dev->res.start, size); + if (!tmp) { + ret = -ENOMEM; + goto err_clk; + } + + /* + * Read pid and cid based on size of resource + * they are located at end of region + */ + for (pid = 0, i = 0; i < 4; i++) + pid |= (readl(tmp + size - 0x20 + 4 * i) & 255) << (i * 8); + for (cid = 0, i = 0; i < 4; i++) + cid |= (readl(tmp + size - 0x10 + 4 * i) & 255) << (i * 8); + + if (cid == CORESIGHT_CID) { + /* set the base to the start of the last 4k block */ + void __iomem *csbase = tmp + size - 4096; + + dev->uci.devarch = readl(csbase + UCI_REG_DEVARCH_OFFSET); + dev->uci.devtype = readl(csbase + UCI_REG_DEVTYPE_OFFSET) & 0xff; + } + + if (cid == AMBA_CID || cid == CORESIGHT_CID) { + dev->periphid = pid; + dev->cid = cid; + } + + if (!dev->periphid) + ret = -ENODEV; + + iounmap(tmp); + +err_clk: + amba_put_disable_pclk(dev); +err_pm: + dev_pm_domain_detach(&dev->dev, true); +err_out: + return ret; +} + static int amba_match(struct device *dev, struct device_driver *drv) { struct amba_device *pcdev = to_amba_device(dev); struct amba_driver *pcdrv = to_amba_driver(drv); + if (!pcdev->periphid) { + int ret = amba_read_periphid(pcdev); + + /* + * Returning any error other than -EPROBE_DEFER from bus match + * can cause driver registration failure. So, if there's a + * permanent failure in reading pid and cid, simply map it to + * -EPROBE_DEFER. + */ + if (ret) + return -EPROBE_DEFER; + dev_set_uevent_suppress(dev, false); + kobject_uevent(&dev->kobj, KOBJ_ADD); + } + /* When driver_override is set, only bind to the matching driver */ if (pcdev->driver_override) return !strcmp(pcdev->driver_override, drv->name); @@ -368,6 +457,42 @@ static int __init amba_init(void) postcore_initcall(amba_init); +static int amba_proxy_probe(struct amba_device *adev, + const struct amba_id *id) +{ + WARN(1, "Stub driver should never match any device.\n"); + return -ENODEV; +} + +static const struct amba_id amba_stub_drv_ids[] = { + { 0, 0 }, +}; + +static struct amba_driver amba_proxy_drv = { + .drv = { + .name = "amba-proxy", + }, + .probe = amba_proxy_probe, + .id_table = amba_stub_drv_ids, +}; + +static int __init amba_stub_drv_init(void) +{ + if (!IS_ENABLED(CONFIG_MODULES)) + return 0; + + /* + * The amba_match() function will get called only if there is at least + * one amba driver registered. If all amba drivers are modules and are + * only loaded based on uevents, then we'll hit a chicken-and-egg + * situation where amba_match() is waiting on drivers and drivers are + * waiting on amba_match(). So, register a stub driver to make sure + * amba_match() is called even if no amba driver has been registered. + */ + return amba_driver_register(&amba_proxy_drv); +} +late_initcall_sync(amba_stub_drv_init); + /** * amba_driver_register - register an AMBA device driver * @drv: amba device driver structure @@ -410,156 +535,6 @@ static void amba_device_release(struct device *dev) kfree(d); } -static int amba_read_periphid(struct amba_device *dev) -{ - struct reset_control *rstc; - u32 size, pid, cid; - void __iomem *tmp; - int i, ret; - - ret = dev_pm_domain_attach(&dev->dev, true); - if (ret) - goto err_out; - - ret = amba_get_enable_pclk(dev); - if (ret) - goto err_pm; - - /* - * Find reset control(s) of the amba bus and de-assert them. - */ - rstc = of_reset_control_array_get_optional_shared(dev->dev.of_node); - if (IS_ERR(rstc)) { - ret = PTR_ERR(rstc); - if (ret != -EPROBE_DEFER) - dev_err(&dev->dev, "can't get reset: %d\n", ret); - goto err_clk; - } - reset_control_deassert(rstc); - reset_control_put(rstc); - - size = resource_size(&dev->res); - tmp = ioremap(dev->res.start, size); - if (!tmp) { - ret = -ENOMEM; - goto err_clk; - } - - /* - * Read pid and cid based on size of resource - * they are located at end of region - */ - for (pid = 0, i = 0; i < 4; i++) - pid |= (readl(tmp + size - 0x20 + 4 * i) & 255) << (i * 8); - for (cid = 0, i = 0; i < 4; i++) - cid |= (readl(tmp + size - 0x10 + 4 * i) & 255) << (i * 8); - - if (cid == CORESIGHT_CID) { - /* set the base to the start of the last 4k block */ - void __iomem *csbase = tmp + size - 4096; - - dev->uci.devarch = readl(csbase + UCI_REG_DEVARCH_OFFSET); - dev->uci.devtype = readl(csbase + UCI_REG_DEVTYPE_OFFSET) & 0xff; - } - - if (cid == AMBA_CID || cid == CORESIGHT_CID) { - dev->periphid = pid; - dev->cid = cid; - } - - if (!dev->periphid) - ret = -ENODEV; - - iounmap(tmp); - -err_clk: - amba_put_disable_pclk(dev); -err_pm: - dev_pm_domain_detach(&dev->dev, true); -err_out: - return ret; -} - -static int amba_device_try_add(struct amba_device *dev, struct resource *parent) -{ - int ret; - - ret = request_resource(parent, &dev->res); - if (ret) - goto err_out; - - /* Hard-coded primecell ID instead of plug-n-play */ - if (dev->periphid != 0) - goto skip_probe; - - ret = amba_read_periphid(dev); - if (ret) - goto err_release; - -skip_probe: - ret = device_add(&dev->dev); -err_release: - if (ret) - release_resource(&dev->res); -err_out: - return ret; -} - -/* - * Registration of AMBA device require reading its pid and cid registers. - * To do this, the device must be turned on (if it is a part of power domain) - * and have clocks enabled. However in some cases those resources might not be - * yet available. Returning EPROBE_DEFER is not a solution in such case, - * because callers don't handle this special error code. Instead such devices - * are added to the special list and their registration is retried from - * periodic worker, until all resources are available and registration succeeds. - */ -struct deferred_device { - struct amba_device *dev; - struct resource *parent; - struct list_head node; -}; - -static LIST_HEAD(deferred_devices); -static DEFINE_MUTEX(deferred_devices_lock); - -static void amba_deferred_retry_func(struct work_struct *dummy); -static DECLARE_DELAYED_WORK(deferred_retry_work, amba_deferred_retry_func); - -#define DEFERRED_DEVICE_TIMEOUT (msecs_to_jiffies(5 * 1000)) - -static int amba_deferred_retry(void) -{ - struct deferred_device *ddev, *tmp; - - mutex_lock(&deferred_devices_lock); - - list_for_each_entry_safe(ddev, tmp, &deferred_devices, node) { - int ret = amba_device_try_add(ddev->dev, ddev->parent); - - if (ret == -EPROBE_DEFER) - continue; - - list_del_init(&ddev->node); - amba_device_put(ddev->dev); - kfree(ddev); - } - - mutex_unlock(&deferred_devices_lock); - - return 0; -} -late_initcall(amba_deferred_retry); - -static void amba_deferred_retry_func(struct work_struct *dummy) -{ - amba_deferred_retry(); - - if (!list_empty(&deferred_devices)) - schedule_delayed_work(&deferred_retry_work, - DEFERRED_DEVICE_TIMEOUT); -} - /** * amba_device_add - add a previously allocated AMBA device structure * @dev: AMBA device allocated by amba_device_alloc @@ -571,28 +546,30 @@ static void amba_deferred_retry_func(struct work_struct *dummy) */ int amba_device_add(struct amba_device *dev, struct resource *parent) { - int ret = amba_device_try_add(dev, parent); - - if (ret == -EPROBE_DEFER) { - struct deferred_device *ddev; - - ddev = kmalloc(sizeof(*ddev), GFP_KERNEL); - if (!ddev) - return -ENOMEM; + int ret; - ddev->dev = dev; - ddev->parent = parent; - ret = 0; + ret = request_resource(parent, &dev->res); + if (ret) + return ret; - mutex_lock(&deferred_devices_lock); + /* If primecell ID isn't hard-coded, figure it out */ + if (!dev->periphid) { + /* + * AMBA device uevents require reading its pid and cid + * registers. To do this, the device must be on, clocked and + * out of reset. However in some cases those resources might + * not yet be available. If that's the case, we suppress the + * generation of uevents until we can read the pid and cid + * registers. See also amba_match(). + */ + if (amba_read_periphid(dev)) + dev_set_uevent_suppress(&dev->dev, true); + } - if (list_empty(&deferred_devices)) - schedule_delayed_work(&deferred_retry_work, - DEFERRED_DEVICE_TIMEOUT); - list_add_tail(&ddev->node, &deferred_devices); + ret = device_add(&dev->dev); + if (ret) + release_resource(&dev->res); - mutex_unlock(&deferred_devices_lock); - } return ret; } EXPORT_SYMBOL_GPL(amba_device_add); |