diff options
Diffstat (limited to 'drivers/amba/bus.c')
| -rw-r--r-- | drivers/amba/bus.c | 801 |
1 files changed, 367 insertions, 434 deletions
diff --git a/drivers/amba/bus.c b/drivers/amba/bus.c index 41b706403ef7..952c45ca6e48 100644 --- a/drivers/amba/bus.c +++ b/drivers/amba/bus.c @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * linux/arch/arm/common/amba.c * * Copyright (C) 2003 Deep Blue Solutions Ltd, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #include <linux/module.h> #include <linux/init.h> @@ -21,51 +18,70 @@ #include <linux/limits.h> #include <linux/clk/clk-conf.h> #include <linux/platform_device.h> +#include <linux/property.h> +#include <linux/reset.h> +#include <linux/of_irq.h> +#include <linux/of_device.h> +#include <linux/acpi.h> +#include <linux/iommu.h> +#include <linux/dma-map-ops.h> + +#define to_amba_driver(d) container_of_const(d, struct amba_driver, drv) -#include <asm/irq.h> +/* called on periphid match and class 0x9 coresight device. */ +static int +amba_cs_uci_id_match(const struct amba_id *table, struct amba_device *dev) +{ + int ret = 0; + struct amba_cs_uci_id *uci; -#define to_amba_driver(d) container_of(d, struct amba_driver, drv) + uci = table->data; + + /* no table data or zero mask - return match on periphid */ + if (!uci || (uci->devarch_mask == 0)) + return 1; + + /* test against read devtype and masked devarch value */ + ret = (dev->uci.devtype == uci->devtype) && + ((dev->uci.devarch & uci->devarch_mask) == uci->devarch); + return ret; +} static const struct amba_id * amba_lookup(const struct amba_id *table, struct amba_device *dev) { - int ret = 0; - while (table->mask) { - ret = (dev->periphid & table->mask) == table->id; - if (ret) - break; + if (((dev->periphid & table->mask) == table->id) && + ((dev->cid != CORESIGHT_CID) || + (amba_cs_uci_id_match(table, dev)))) + return table; table++; } - - return ret ? table : NULL; + return NULL; } -static int amba_match(struct device *dev, struct device_driver *drv) +static int amba_get_enable_pclk(struct amba_device *pcdev) { - struct amba_device *pcdev = to_amba_device(dev); - struct amba_driver *pcdrv = to_amba_driver(drv); + int ret; - /* When driver_override is set, only bind to the matching driver */ - if (pcdev->driver_override) - return !strcmp(pcdev->driver_override, drv->name); + pcdev->pclk = clk_get(&pcdev->dev, "apb_pclk"); + if (IS_ERR(pcdev->pclk)) + return PTR_ERR(pcdev->pclk); - return amba_lookup(pcdrv->id_table, pcdev) != NULL; + ret = clk_prepare_enable(pcdev->pclk); + if (ret) + clk_put(pcdev->pclk); + + return ret; } -static int amba_uevent(struct device *dev, struct kobj_uevent_env *env) +static void amba_put_disable_pclk(struct amba_device *pcdev) { - struct amba_device *pcdev = to_amba_device(dev); - int retval = 0; - - retval = add_uevent_var(env, "AMBA_ID=%08x", pcdev->periphid); - if (retval) - return retval; - - retval = add_uevent_var(env, "MODALIAS=amba:d%08X", pcdev->periphid); - return retval; + clk_disable_unprepare(pcdev->pclk); + clk_put(pcdev->pclk); } + static ssize_t driver_override_show(struct device *_dev, struct device_attribute *attr, char *buf) { @@ -83,31 +99,11 @@ static ssize_t driver_override_store(struct device *_dev, const char *buf, size_t count) { struct amba_device *dev = to_amba_device(_dev); - char *driver_override, *old, *cp; - - /* We need to keep extra room for a newline */ - if (count >= (PAGE_SIZE - 1)) - return -EINVAL; - - driver_override = kstrndup(buf, count, GFP_KERNEL); - if (!driver_override) - return -ENOMEM; - - cp = strchr(driver_override, '\n'); - if (cp) - *cp = '\0'; - - device_lock(_dev); - old = dev->driver_override; - if (strlen(driver_override)) { - dev->driver_override = driver_override; - } else { - kfree(driver_override); - dev->driver_override = NULL; - } - device_unlock(_dev); + int ret; - kfree(old); + ret = driver_set_override(_dev, &dev->driver_override, buf, count); + if (ret) + return ret; return count; } @@ -123,8 +119,6 @@ static ssize_t name##_show(struct device *_dev, \ static DEVICE_ATTR_RO(name) amba_attr_func(id, "%08x\n", dev->periphid); -amba_attr_func(irq0, "%u\n", dev->irq[0]); -amba_attr_func(irq1, "%u\n", dev->irq[1]); amba_attr_func(resource, "\t%016llx\t%016llx\t%016lx\n", (unsigned long long)dev->res.start, (unsigned long long)dev->res.end, dev->res.flags); @@ -137,102 +131,144 @@ static struct attribute *amba_dev_attrs[] = { }; ATTRIBUTE_GROUPS(amba_dev); -#ifdef CONFIG_PM -/* - * Hooks to provide runtime PM of the pclk (bus clock). It is safe to - * enable/disable the bus clock at runtime PM suspend/resume as this - * does not result in loss of context. - */ -static int amba_pm_runtime_suspend(struct device *dev) +static int amba_read_periphid(struct amba_device *dev) { - struct amba_device *pcdev = to_amba_device(dev); - int ret = pm_generic_runtime_suspend(dev); + struct reset_control *rstc; + u32 size, pid, cid; + void __iomem *tmp; + int i, ret; - if (ret == 0 && dev->driver) { - if (pm_runtime_is_irq_safe(dev)) - clk_disable(pcdev->pclk); - else - clk_disable_unprepare(pcdev->pclk); + ret = dev_pm_domain_attach(&dev->dev, PD_FLAG_ATTACH_POWER_ON); + 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_pm_runtime_resume(struct device *dev) +static int amba_match(struct device *dev, const struct device_driver *drv) { struct amba_device *pcdev = to_amba_device(dev); - int ret; + const struct amba_driver *pcdrv = to_amba_driver(drv); - if (dev->driver) { - if (pm_runtime_is_irq_safe(dev)) - ret = clk_enable(pcdev->pclk); - else - ret = clk_prepare_enable(pcdev->pclk); - /* Failure is probably fatal to the system, but... */ - if (ret) - return ret; + mutex_lock(&pcdev->periphid_lock); + 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) { + mutex_unlock(&pcdev->periphid_lock); + return -EPROBE_DEFER; + } + dev_set_uevent_suppress(dev, false); + kobject_uevent(&dev->kobj, KOBJ_ADD); } + mutex_unlock(&pcdev->periphid_lock); - return pm_generic_runtime_resume(dev); + /* When driver_override is set, only bind to the matching driver */ + if (pcdev->driver_override) + return !strcmp(pcdev->driver_override, drv->name); + + return amba_lookup(pcdrv->id_table, pcdev) != NULL; } -#endif /* CONFIG_PM */ -static const struct dev_pm_ops amba_pm = { - .suspend = pm_generic_suspend, - .resume = pm_generic_resume, - .freeze = pm_generic_freeze, - .thaw = pm_generic_thaw, - .poweroff = pm_generic_poweroff, - .restore = pm_generic_restore, - SET_RUNTIME_PM_OPS( - amba_pm_runtime_suspend, - amba_pm_runtime_resume, - NULL - ) -}; +static int amba_uevent(const struct device *dev, struct kobj_uevent_env *env) +{ + const struct amba_device *pcdev = to_amba_device(dev); + int retval = 0; -/* - * Primecells are part of the Advanced Microcontroller Bus Architecture, - * so we call the bus "amba". - * DMA configuration for platform and AMBA bus is same. So here we reuse - * platform's DMA config routine. - */ -struct bus_type amba_bustype = { - .name = "amba", - .dev_groups = amba_dev_groups, - .match = amba_match, - .uevent = amba_uevent, - .dma_configure = platform_dma_configure, - .pm = &amba_pm, -}; -EXPORT_SYMBOL_GPL(amba_bustype); + retval = add_uevent_var(env, "AMBA_ID=%08x", pcdev->periphid); + if (retval) + return retval; -static int __init amba_init(void) -{ - return bus_register(&amba_bustype); + retval = add_uevent_var(env, "MODALIAS=amba:d%08X", pcdev->periphid); + return retval; } -postcore_initcall(amba_init); - -static int amba_get_enable_pclk(struct amba_device *pcdev) +static int of_amba_device_decode_irq(struct amba_device *dev) { - int ret; - - pcdev->pclk = clk_get(&pcdev->dev, "apb_pclk"); - if (IS_ERR(pcdev->pclk)) - return PTR_ERR(pcdev->pclk); + struct device_node *node = dev->dev.of_node; + int i, irq = 0; - ret = clk_prepare_enable(pcdev->pclk); - if (ret) - clk_put(pcdev->pclk); + if (IS_ENABLED(CONFIG_OF_IRQ) && node) { + /* Decode the IRQs and address ranges */ + for (i = 0; i < AMBA_NR_IRQS; i++) { + irq = of_irq_get(node, i); + if (irq < 0) { + if (irq == -EPROBE_DEFER) + return irq; + irq = 0; + } - return ret; -} + dev->irq[i] = irq; + } + } -static void amba_put_disable_pclk(struct amba_device *pcdev) -{ - clk_disable_unprepare(pcdev->pclk); - clk_put(pcdev->pclk); + return 0; } /* @@ -247,19 +283,22 @@ static int amba_probe(struct device *dev) int ret; do { + ret = of_amba_device_decode_irq(pcdev); + if (ret) + break; + ret = of_clk_set_defaults(dev->of_node, false); if (ret < 0) break; - ret = dev_pm_domain_attach(dev, true); + ret = dev_pm_domain_attach(dev, PD_FLAG_ATTACH_POWER_ON | + PD_FLAG_DETACH_POWER_OFF); if (ret) break; ret = amba_get_enable_pclk(pcdev); - if (ret) { - dev_pm_domain_detach(dev, true); + if (ret) break; - } pm_runtime_get_noresume(dev); pm_runtime_set_active(dev); @@ -274,20 +313,19 @@ static int amba_probe(struct device *dev) pm_runtime_put_noidle(dev); amba_put_disable_pclk(pcdev); - dev_pm_domain_detach(dev, true); } while (0); return ret; } -static int amba_remove(struct device *dev) +static void amba_remove(struct device *dev) { struct amba_device *pcdev = to_amba_device(dev); struct amba_driver *drv = to_amba_driver(dev->driver); - int ret; pm_runtime_get_sync(dev); - ret = drv->remove(pcdev); + if (drv->remove) + drv->remove(pcdev); pm_runtime_put_noidle(dev); /* Undo the runtime PM settings in amba_probe() */ @@ -296,188 +334,213 @@ static int amba_remove(struct device *dev) pm_runtime_put_noidle(dev); amba_put_disable_pclk(pcdev); - dev_pm_domain_detach(dev, true); - - return ret; } static void amba_shutdown(struct device *dev) { - struct amba_driver *drv = to_amba_driver(dev->driver); - drv->shutdown(to_amba_device(dev)); -} + struct amba_driver *drv; -/** - * amba_driver_register - register an AMBA device driver - * @drv: amba device driver structure - * - * Register an AMBA device driver with the Linux device model - * core. If devices pre-exist, the drivers probe function will - * be called. - */ -int amba_driver_register(struct amba_driver *drv) -{ - drv->drv.bus = &amba_bustype; + if (!dev->driver) + return; -#define SETFN(fn) if (drv->fn) drv->drv.fn = amba_##fn - SETFN(probe); - SETFN(remove); - SETFN(shutdown); - - return driver_register(&drv->drv); + drv = to_amba_driver(dev->driver); + if (drv->shutdown) + drv->shutdown(to_amba_device(dev)); } -/** - * amba_driver_unregister - remove an AMBA device driver - * @drv: AMBA device driver structure to remove - * - * Unregister an AMBA device driver from the Linux device - * model. The device model will call the drivers remove function - * for each device the device driver is currently handling. - */ -void amba_driver_unregister(struct amba_driver *drv) +static int amba_dma_configure(struct device *dev) { - driver_unregister(&drv->drv); -} + struct amba_driver *drv = to_amba_driver(dev->driver); + enum dev_dma_attr attr; + int ret = 0; + if (dev->of_node) { + ret = of_dma_configure(dev, dev->of_node, true); + } else if (has_acpi_companion(dev)) { + attr = acpi_get_dma_attr(to_acpi_device_node(dev->fwnode)); + ret = acpi_dma_configure(dev, attr); + } -static void amba_device_release(struct device *dev) -{ - struct amba_device *d = to_amba_device(dev); + /* @drv may not be valid when we're called from the IOMMU layer */ + if (!ret && dev->driver && !drv->driver_managed_dma) { + ret = iommu_device_use_default_domain(dev); + if (ret) + arch_teardown_dma_ops(dev); + } - if (d->res.parent) - release_resource(&d->res); - kfree(d); + return ret; } -static int amba_device_try_add(struct amba_device *dev, struct resource *parent) +static void amba_dma_cleanup(struct device *dev) { - u32 size; - void __iomem *tmp; - int i, ret; - - WARN_ON(dev->irq[0] == (unsigned int)-1); - WARN_ON(dev->irq[1] == (unsigned int)-1); - - ret = request_resource(parent, &dev->res); - if (ret) - goto err_out; + struct amba_driver *drv = to_amba_driver(dev->driver); - /* Hard-coded primecell ID instead of plug-n-play */ - if (dev->periphid != 0) - goto skip_probe; + if (!drv->driver_managed_dma) + iommu_device_unuse_default_domain(dev); +} - /* - * Dynamically calculate the size of the resource - * and use this for iomap - */ - size = resource_size(&dev->res); - tmp = ioremap(dev->res.start, size); - if (!tmp) { - ret = -ENOMEM; - goto err_release; - } +#ifdef CONFIG_PM +/* + * Hooks to provide runtime PM of the pclk (bus clock). It is safe to + * enable/disable the bus clock at runtime PM suspend/resume as this + * does not result in loss of context. + */ +static int amba_pm_runtime_suspend(struct device *dev) +{ + struct amba_device *pcdev = to_amba_device(dev); + int ret = pm_generic_runtime_suspend(dev); - ret = dev_pm_domain_attach(&dev->dev, true); - if (ret) { - iounmap(tmp); - goto err_release; + if (ret == 0 && dev->driver) { + if (pm_runtime_is_irq_safe(dev)) + clk_disable(pcdev->pclk); + else + clk_disable_unprepare(pcdev->pclk); } - ret = amba_get_enable_pclk(dev); - if (ret == 0) { - u32 pid, cid; - - /* - * 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); - - amba_put_disable_pclk(dev); + return ret; +} - if (cid == AMBA_CID || cid == CORESIGHT_CID) - dev->periphid = pid; +static int amba_pm_runtime_resume(struct device *dev) +{ + struct amba_device *pcdev = to_amba_device(dev); + int ret; - if (!dev->periphid) - ret = -ENODEV; + if (dev->driver) { + if (pm_runtime_is_irq_safe(dev)) + ret = clk_enable(pcdev->pclk); + else + ret = clk_prepare_enable(pcdev->pclk); + /* Failure is probably fatal to the system, but... */ + if (ret) + return ret; } - iounmap(tmp); - dev_pm_domain_detach(&dev->dev, true); + return pm_generic_runtime_resume(dev); +} +#endif /* CONFIG_PM */ - if (ret) - goto err_release; +static const struct dev_pm_ops amba_pm = { + SET_RUNTIME_PM_OPS( + amba_pm_runtime_suspend, + amba_pm_runtime_resume, + NULL + ) +}; - skip_probe: - ret = device_add(&dev->dev); - if (ret) - goto err_release; +/* + * Primecells are part of the Advanced Microcontroller Bus Architecture, + * so we call the bus "amba". + * DMA configuration for platform and AMBA bus is same. So here we reuse + * platform's DMA config routine. + */ +const struct bus_type amba_bustype = { + .name = "amba", + .dev_groups = amba_dev_groups, + .match = amba_match, + .uevent = amba_uevent, + .probe = amba_probe, + .remove = amba_remove, + .shutdown = amba_shutdown, + .dma_configure = amba_dma_configure, + .dma_cleanup = amba_dma_cleanup, + .pm = &amba_pm, +}; +EXPORT_SYMBOL_GPL(amba_bustype); - if (dev->irq[0]) - ret = device_create_file(&dev->dev, &dev_attr_irq0); - if (ret == 0 && dev->irq[1]) - ret = device_create_file(&dev->dev, &dev_attr_irq1); - if (ret == 0) - return ret; +bool dev_is_amba(const struct device *dev) +{ + return dev->bus == &amba_bustype; +} +EXPORT_SYMBOL_GPL(dev_is_amba); - device_unregister(&dev->dev); +static int __init amba_init(void) +{ + return bus_register(&amba_bustype); +} - err_release: - release_resource(&dev->res); - err_out: - return ret; +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; } -/* - * 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 const struct amba_id amba_stub_drv_ids[] = { + { 0, 0 }, }; -static LIST_HEAD(deferred_devices); -static DEFINE_MUTEX(deferred_devices_lock); +static struct amba_driver amba_proxy_drv = { + .drv = { + .name = "amba-proxy", + }, + .probe = amba_proxy_probe, + .id_table = amba_stub_drv_ids, +}; -static void amba_deferred_retry_func(struct work_struct *dummy); -static DECLARE_DELAYED_WORK(deferred_retry_work, amba_deferred_retry_func); +static int __init amba_stub_drv_init(void) +{ + if (!IS_ENABLED(CONFIG_MODULES)) + return 0; -#define DEFERRED_DEVICE_TIMEOUT (msecs_to_jiffies(5 * 1000)) + /* + * 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, NULL); +} +late_initcall_sync(amba_stub_drv_init); -static void amba_deferred_retry_func(struct work_struct *dummy) +/** + * __amba_driver_register - register an AMBA device driver + * @drv: amba device driver structure + * @owner: owning module/driver + * + * Register an AMBA device driver with the Linux device model + * core. If devices pre-exist, the drivers probe function will + * be called. + */ +int __amba_driver_register(struct amba_driver *drv, + struct module *owner) { - struct deferred_device *ddev, *tmp; - - mutex_lock(&deferred_devices_lock); + if (!drv->probe) + return -EINVAL; - list_for_each_entry_safe(ddev, tmp, &deferred_devices, node) { - int ret = amba_device_try_add(ddev->dev, ddev->parent); + drv->drv.owner = owner; + drv->drv.bus = &amba_bustype; - if (ret == -EPROBE_DEFER) - continue; + return driver_register(&drv->drv); +} +EXPORT_SYMBOL(__amba_driver_register); - list_del_init(&ddev->node); - kfree(ddev); - } +/** + * amba_driver_unregister - remove an AMBA device driver + * @drv: AMBA device driver structure to remove + * + * Unregister an AMBA device driver from the Linux device + * model. The device model will call the drivers remove function + * for each device the device driver is currently handling. + */ +void amba_driver_unregister(struct amba_driver *drv) +{ + driver_unregister(&drv->drv); +} +EXPORT_SYMBOL(amba_driver_unregister); - if (!list_empty(&deferred_devices)) - schedule_delayed_work(&deferred_retry_work, - DEFERRED_DEVICE_TIMEOUT); +static void amba_device_release(struct device *dev) +{ + struct amba_device *d = to_amba_device(dev); - mutex_unlock(&deferred_devices_lock); + fwnode_handle_put(dev_fwnode(&d->dev)); + if (d->res.parent) + release_resource(&d->res); + mutex_destroy(&d->periphid_lock); + kfree(d); } /** @@ -491,103 +554,35 @@ 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; - - ddev->dev = dev; - ddev->parent = parent; - ret = 0; - - mutex_lock(&deferred_devices_lock); - - if (list_empty(&deferred_devices)) - schedule_delayed_work(&deferred_retry_work, - DEFERRED_DEVICE_TIMEOUT); - list_add_tail(&ddev->node, &deferred_devices); - - mutex_unlock(&deferred_devices_lock); - } - return ret; -} -EXPORT_SYMBOL_GPL(amba_device_add); - -static struct amba_device * -amba_aphb_device_add(struct device *parent, const char *name, - resource_size_t base, size_t size, int irq1, int irq2, - void *pdata, unsigned int periphid, u64 dma_mask, - struct resource *resbase) -{ - struct amba_device *dev; int ret; - dev = amba_device_alloc(name, base, size); - if (!dev) - return ERR_PTR(-ENOMEM); + fwnode_handle_get(dev_fwnode(&dev->dev)); - dev->dev.coherent_dma_mask = dma_mask; - dev->irq[0] = irq1; - dev->irq[1] = irq2; - dev->periphid = periphid; - dev->dev.platform_data = pdata; - dev->dev.parent = parent; + ret = request_resource(parent, &dev->res); + if (ret) + return ret; - ret = amba_device_add(dev, resbase); - if (ret) { - amba_device_put(dev); - return ERR_PTR(ret); + /* 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); } - return dev; -} - -struct amba_device * -amba_apb_device_add(struct device *parent, const char *name, - resource_size_t base, size_t size, int irq1, int irq2, - void *pdata, unsigned int periphid) -{ - return amba_aphb_device_add(parent, name, base, size, irq1, irq2, pdata, - periphid, 0, &iomem_resource); -} -EXPORT_SYMBOL_GPL(amba_apb_device_add); - -struct amba_device * -amba_ahb_device_add(struct device *parent, const char *name, - resource_size_t base, size_t size, int irq1, int irq2, - void *pdata, unsigned int periphid) -{ - return amba_aphb_device_add(parent, name, base, size, irq1, irq2, pdata, - periphid, ~0ULL, &iomem_resource); -} -EXPORT_SYMBOL_GPL(amba_ahb_device_add); - -struct amba_device * -amba_apb_device_add_res(struct device *parent, const char *name, - resource_size_t base, size_t size, int irq1, - int irq2, void *pdata, unsigned int periphid, - struct resource *resbase) -{ - return amba_aphb_device_add(parent, name, base, size, irq1, irq2, pdata, - periphid, 0, resbase); -} -EXPORT_SYMBOL_GPL(amba_apb_device_add_res); + ret = device_add(&dev->dev); + if (ret) + release_resource(&dev->res); -struct amba_device * -amba_ahb_device_add_res(struct device *parent, const char *name, - resource_size_t base, size_t size, int irq1, - int irq2, void *pdata, unsigned int periphid, - struct resource *resbase) -{ - return amba_aphb_device_add(parent, name, base, size, irq1, irq2, pdata, - periphid, ~0ULL, resbase); + return ret; } -EXPORT_SYMBOL_GPL(amba_ahb_device_add_res); - +EXPORT_SYMBOL_GPL(amba_device_add); static void amba_device_initialize(struct amba_device *dev, const char *name) { @@ -597,7 +592,9 @@ static void amba_device_initialize(struct amba_device *dev, const char *name) dev->dev.release = amba_device_release; dev->dev.bus = &amba_bustype; dev->dev.dma_mask = &dev->dev.coherent_dma_mask; + dev->dev.dma_parms = &dev->dma_parms; dev->res.name = dev_name(&dev->dev); + mutex_init(&dev->periphid_lock); } /** @@ -642,6 +639,7 @@ int amba_device_register(struct amba_device *dev, struct resource *parent) return amba_device_add(dev, parent); } +EXPORT_SYMBOL(amba_device_register); /** * amba_device_put - put an AMBA device @@ -668,66 +666,7 @@ void amba_device_unregister(struct amba_device *dev) { device_unregister(&dev->dev); } - - -struct find_data { - struct amba_device *dev; - struct device *parent; - const char *busid; - unsigned int id; - unsigned int mask; -}; - -static int amba_find_match(struct device *dev, void *data) -{ - struct find_data *d = data; - struct amba_device *pcdev = to_amba_device(dev); - int r; - - r = (pcdev->periphid & d->mask) == d->id; - if (d->parent) - r &= d->parent == dev->parent; - if (d->busid) - r &= strcmp(dev_name(dev), d->busid) == 0; - - if (r) { - get_device(dev); - d->dev = pcdev; - } - - return r; -} - -/** - * amba_find_device - locate an AMBA device given a bus id - * @busid: bus id for device (or NULL) - * @parent: parent device (or NULL) - * @id: peripheral ID (or 0) - * @mask: peripheral ID mask (or 0) - * - * Return the AMBA device corresponding to the supplied parameters. - * If no device matches, returns NULL. - * - * NOTE: When a valid device is found, its refcount is - * incremented, and must be decremented before the returned - * reference. - */ -struct amba_device * -amba_find_device(const char *busid, struct device *parent, unsigned int id, - unsigned int mask) -{ - struct find_data data; - - data.dev = NULL; - data.parent = parent; - data.busid = busid; - data.id = id; - data.mask = mask; - - bus_for_each_dev(&amba_bustype, NULL, &data, amba_find_match); - - return data.dev; -} +EXPORT_SYMBOL(amba_device_unregister); /** * amba_request_regions - request all mem regions associated with device @@ -749,6 +688,7 @@ int amba_request_regions(struct amba_device *dev, const char *name) return ret; } +EXPORT_SYMBOL(amba_request_regions); /** * amba_release_regions - release mem regions associated with device @@ -763,11 +703,4 @@ void amba_release_regions(struct amba_device *dev) size = resource_size(&dev->res); release_mem_region(dev->res.start, size); } - -EXPORT_SYMBOL(amba_driver_register); -EXPORT_SYMBOL(amba_driver_unregister); -EXPORT_SYMBOL(amba_device_register); -EXPORT_SYMBOL(amba_device_unregister); -EXPORT_SYMBOL(amba_find_device); -EXPORT_SYMBOL(amba_request_regions); EXPORT_SYMBOL(amba_release_regions); |
