summaryrefslogtreecommitdiff
path: root/drivers/amba/bus.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/amba/bus.c')
-rw-r--r--drivers/amba/bus.c819
1 files changed, 363 insertions, 456 deletions
diff --git a/drivers/amba/bus.c b/drivers/amba/bus.c
index c6707278a6bb..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>
@@ -15,48 +12,102 @@
#include <linux/io.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
+#include <linux/pm_domain.h>
#include <linux/amba/bus.h>
#include <linux/sizes.h>
+#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)
+
+/* 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;
-#include <asm/irq.h>
+ uci = table->data;
-#define to_amba_driver(d) container_of(d, struct amba_driver, drv)
+ /* 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 NULL;
+}
- return ret ? table : NULL;
+static int amba_get_enable_pclk(struct amba_device *pcdev)
+{
+ int ret;
+
+ pcdev->pclk = clk_get(&pcdev->dev, "apb_pclk");
+ if (IS_ERR(pcdev->pclk))
+ return PTR_ERR(pcdev->pclk);
+
+ ret = clk_prepare_enable(pcdev->pclk);
+ if (ret)
+ clk_put(pcdev->pclk);
+
+ return ret;
}
-static int amba_match(struct device *dev, struct device_driver *drv)
+static void amba_put_disable_pclk(struct amba_device *pcdev)
{
- struct amba_device *pcdev = to_amba_device(dev);
- struct amba_driver *pcdrv = to_amba_driver(drv);
+ clk_disable_unprepare(pcdev->pclk);
+ clk_put(pcdev->pclk);
+}
- return amba_lookup(pcdrv->id_table, pcdev) != NULL;
+
+static ssize_t driver_override_show(struct device *_dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct amba_device *dev = to_amba_device(_dev);
+ ssize_t len;
+
+ device_lock(_dev);
+ len = sprintf(buf, "%s\n", dev->driver_override);
+ device_unlock(_dev);
+ return len;
}
-static int amba_uevent(struct device *dev, struct kobj_uevent_env *env)
+static ssize_t driver_override_store(struct device *_dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
{
- struct amba_device *pcdev = to_amba_device(dev);
- int retval = 0;
+ struct amba_device *dev = to_amba_device(_dev);
+ int ret;
- retval = add_uevent_var(env, "AMBA_ID=%08x", pcdev->periphid);
- if (retval)
- return retval;
+ ret = driver_set_override(_dev, &dev->driver_override, buf, count);
+ if (ret)
+ return ret;
- retval = add_uevent_var(env, "MODALIAS=amba:d%08X", pcdev->periphid);
- return retval;
+ return count;
}
+static DEVICE_ATTR_RW(driver_override);
#define amba_attr_func(name,fmt,arg...) \
static ssize_t name##_show(struct device *_dev, \
@@ -64,182 +115,271 @@ static ssize_t name##_show(struct device *_dev, \
{ \
struct amba_device *dev = to_amba_device(_dev); \
return sprintf(buf, fmt, arg); \
-}
-
-#define amba_attr(name,fmt,arg...) \
-amba_attr_func(name,fmt,arg) \
-static DEVICE_ATTR(name, S_IRUGO, name##_show, NULL)
+} \
+static DEVICE_ATTR_RO(name)
amba_attr_func(id, "%08x\n", dev->periphid);
-amba_attr(irq0, "%u\n", dev->irq[0]);
-amba_attr(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);
-static struct device_attribute amba_dev_attrs[] = {
- __ATTR_RO(id),
- __ATTR_RO(resource),
- __ATTR_NULL,
+static struct attribute *amba_dev_attrs[] = {
+ &dev_attr_id.attr,
+ &dev_attr_resource.attr,
+ &dev_attr_driver_override.attr,
+ NULL,
};
+ATTRIBUTE_GROUPS(amba_dev);
-#ifdef CONFIG_PM_SLEEP
-
-static int amba_legacy_suspend(struct device *dev, pm_message_t mesg)
+static int amba_read_periphid(struct amba_device *dev)
{
- struct amba_driver *adrv = to_amba_driver(dev->driver);
- struct amba_device *adev = to_amba_device(dev);
- int ret = 0;
+ struct reset_control *rstc;
+ u32 size, pid, cid;
+ void __iomem *tmp;
+ int i, ret;
- if (dev->driver && adrv->suspend)
- ret = adrv->suspend(adev, mesg);
+ 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;
+ }
- return ret;
-}
+ ret = amba_get_enable_pclk(dev);
+ if (ret) {
+ dev_dbg(&dev->dev, "can't get pclk: %d\n", ret);
+ goto err_pm;
+ }
-static int amba_legacy_resume(struct device *dev)
-{
- struct amba_driver *adrv = to_amba_driver(dev->driver);
- struct amba_device *adev = to_amba_device(dev);
- int ret = 0;
+ /*
+ * 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);
- if (dev->driver && adrv->resume)
- ret = adrv->resume(adev);
+ size = resource_size(&dev->res);
+ tmp = ioremap(dev->res.start, size);
+ if (!tmp) {
+ ret = -ENOMEM;
+ goto err_clk;
+ }
- return ret;
-}
+ /*
+ * 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);
-#endif /* CONFIG_PM_SLEEP */
+ if (cid == CORESIGHT_CID) {
+ /* set the base to the start of the last 4k block */
+ void __iomem *csbase = tmp + size - 4096;
-#ifdef CONFIG_SUSPEND
+ dev->uci.devarch = readl(csbase + UCI_REG_DEVARCH_OFFSET);
+ dev->uci.devtype = readl(csbase + UCI_REG_DEVTYPE_OFFSET) & 0xff;
+ }
-static int amba_pm_suspend(struct device *dev)
-{
- struct device_driver *drv = dev->driver;
- int ret = 0;
+ if (cid == AMBA_CID || cid == CORESIGHT_CID) {
+ dev->periphid = pid;
+ dev->cid = cid;
+ }
- if (!drv)
- return 0;
+ if (!dev->periphid)
+ ret = -ENODEV;
- if (drv->pm) {
- if (drv->pm->suspend)
- ret = drv->pm->suspend(dev);
- } else {
- ret = amba_legacy_suspend(dev, PMSG_SUSPEND);
- }
+ 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_resume(struct device *dev)
+static int amba_match(struct device *dev, const struct device_driver *drv)
{
- struct device_driver *drv = dev->driver;
- int ret = 0;
+ struct amba_device *pcdev = to_amba_device(dev);
+ const struct amba_driver *pcdrv = to_amba_driver(drv);
- if (!drv)
- return 0;
+ mutex_lock(&pcdev->periphid_lock);
+ if (!pcdev->periphid) {
+ int ret = amba_read_periphid(pcdev);
- if (drv->pm) {
- if (drv->pm->resume)
- ret = drv->pm->resume(dev);
- } else {
- ret = amba_legacy_resume(dev);
+ /*
+ * 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 ret;
-}
+ /* When driver_override is set, only bind to the matching driver */
+ if (pcdev->driver_override)
+ return !strcmp(pcdev->driver_override, drv->name);
-#else /* !CONFIG_SUSPEND */
+ return amba_lookup(pcdrv->id_table, pcdev) != NULL;
+}
-#define amba_pm_suspend NULL
-#define amba_pm_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;
-#endif /* !CONFIG_SUSPEND */
+ retval = add_uevent_var(env, "AMBA_ID=%08x", pcdev->periphid);
+ if (retval)
+ return retval;
-#ifdef CONFIG_HIBERNATE_CALLBACKS
+ retval = add_uevent_var(env, "MODALIAS=amba:d%08X", pcdev->periphid);
+ return retval;
+}
-static int amba_pm_freeze(struct device *dev)
+static int of_amba_device_decode_irq(struct amba_device *dev)
{
- struct device_driver *drv = dev->driver;
- int ret = 0;
+ struct device_node *node = dev->dev.of_node;
+ int i, irq = 0;
- if (!drv)
- return 0;
+ 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;
+ }
- if (drv->pm) {
- if (drv->pm->freeze)
- ret = drv->pm->freeze(dev);
- } else {
- ret = amba_legacy_suspend(dev, PMSG_FREEZE);
+ dev->irq[i] = irq;
+ }
}
- return ret;
+ return 0;
}
-static int amba_pm_thaw(struct device *dev)
+/*
+ * These are the device model conversion veneers; they convert the
+ * device model structures to our more specific structures.
+ */
+static int amba_probe(struct device *dev)
{
- struct device_driver *drv = dev->driver;
- int ret = 0;
+ struct amba_device *pcdev = to_amba_device(dev);
+ struct amba_driver *pcdrv = to_amba_driver(dev->driver);
+ const struct amba_id *id = amba_lookup(pcdrv->id_table, pcdev);
+ int ret;
- if (!drv)
- return 0;
+ do {
+ ret = of_amba_device_decode_irq(pcdev);
+ if (ret)
+ break;
- if (drv->pm) {
- if (drv->pm->thaw)
- ret = drv->pm->thaw(dev);
- } else {
- ret = amba_legacy_resume(dev);
- }
+ ret = of_clk_set_defaults(dev->of_node, false);
+ if (ret < 0)
+ break;
+
+ 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)
+ break;
+
+ pm_runtime_get_noresume(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
+ ret = pcdrv->probe(pcdev, id);
+ if (ret == 0)
+ break;
+
+ pm_runtime_disable(dev);
+ pm_runtime_set_suspended(dev);
+ pm_runtime_put_noidle(dev);
+
+ amba_put_disable_pclk(pcdev);
+ } while (0);
return ret;
}
-static int amba_pm_poweroff(struct device *dev)
+static void amba_remove(struct device *dev)
{
- struct device_driver *drv = dev->driver;
- int ret = 0;
+ struct amba_device *pcdev = to_amba_device(dev);
+ struct amba_driver *drv = to_amba_driver(dev->driver);
- if (!drv)
- return 0;
+ pm_runtime_get_sync(dev);
+ if (drv->remove)
+ drv->remove(pcdev);
+ pm_runtime_put_noidle(dev);
- if (drv->pm) {
- if (drv->pm->poweroff)
- ret = drv->pm->poweroff(dev);
- } else {
- ret = amba_legacy_suspend(dev, PMSG_HIBERNATE);
- }
+ /* Undo the runtime PM settings in amba_probe() */
+ pm_runtime_disable(dev);
+ pm_runtime_set_suspended(dev);
+ pm_runtime_put_noidle(dev);
- return ret;
+ amba_put_disable_pclk(pcdev);
}
-static int amba_pm_restore(struct device *dev)
+static void amba_shutdown(struct device *dev)
{
- struct device_driver *drv = dev->driver;
+ struct amba_driver *drv;
+
+ if (!dev->driver)
+ return;
+
+ drv = to_amba_driver(dev->driver);
+ if (drv->shutdown)
+ drv->shutdown(to_amba_device(dev));
+}
+
+static int amba_dma_configure(struct device *dev)
+{
+ struct amba_driver *drv = to_amba_driver(dev->driver);
+ enum dev_dma_attr attr;
int ret = 0;
- if (!drv)
- return 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);
+ }
- if (drv->pm) {
- if (drv->pm->restore)
- ret = drv->pm->restore(dev);
- } else {
- ret = amba_legacy_resume(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);
}
return ret;
}
-#else /* !CONFIG_HIBERNATE_CALLBACKS */
-
-#define amba_pm_freeze NULL
-#define amba_pm_thaw NULL
-#define amba_pm_poweroff NULL
-#define amba_pm_restore NULL
+static void amba_dma_cleanup(struct device *dev)
+{
+ struct amba_driver *drv = to_amba_driver(dev->driver);
-#endif /* !CONFIG_HIBERNATE_CALLBACKS */
+ if (!drv->driver_managed_dma)
+ iommu_device_unuse_default_domain(dev);
+}
-#ifdef CONFIG_PM_RUNTIME
+#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
@@ -250,8 +390,12 @@ static int amba_pm_runtime_suspend(struct device *dev)
struct amba_device *pcdev = to_amba_device(dev);
int ret = pm_generic_runtime_suspend(dev);
- if (ret == 0 && dev->driver)
- clk_disable(pcdev->pclk);
+ if (ret == 0 && dev->driver) {
+ if (pm_runtime_is_irq_safe(dev))
+ clk_disable(pcdev->pclk);
+ else
+ clk_disable_unprepare(pcdev->pclk);
+ }
return ret;
}
@@ -262,7 +406,10 @@ static int amba_pm_runtime_resume(struct device *dev)
int ret;
if (dev->driver) {
- ret = clk_enable(pcdev->pclk);
+ 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;
@@ -270,17 +417,9 @@ static int amba_pm_runtime_resume(struct device *dev)
return pm_generic_runtime_resume(dev);
}
-#endif
-
-#ifdef CONFIG_PM
+#endif /* CONFIG_PM */
static const struct dev_pm_ops amba_pm = {
- .suspend = amba_pm_suspend,
- .resume = amba_pm_resume,
- .freeze = amba_pm_freeze,
- .thaw = amba_pm_thaw,
- .poweroff = amba_pm_poweroff,
- .restore = amba_pm_restore,
SET_RUNTIME_PM_OPS(
amba_pm_runtime_suspend,
amba_pm_runtime_resume,
@@ -288,25 +427,31 @@ static const struct dev_pm_ops amba_pm = {
)
};
-#define AMBA_PM (&amba_pm)
-
-#else /* !CONFIG_PM */
-
-#define AMBA_PM NULL
-
-#endif /* !CONFIG_PM */
-
/*
* 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 = {
+const struct bus_type amba_bustype = {
.name = "amba",
- .dev_attrs = amba_dev_attrs,
+ .dev_groups = amba_dev_groups,
.match = amba_match,
.uevent = amba_uevent,
- .pm = AMBA_PM,
+ .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);
+
+bool dev_is_amba(const struct device *dev)
+{
+ return dev->bus == &amba_bustype;
+}
+EXPORT_SYMBOL_GPL(dev_is_amba);
static int __init amba_init(void)
{
@@ -315,119 +460,63 @@ static int __init amba_init(void)
postcore_initcall(amba_init);
-static int amba_get_enable_pclk(struct amba_device *pcdev)
+static int amba_proxy_probe(struct amba_device *adev,
+ const struct amba_id *id)
{
- struct clk *pclk = clk_get(&pcdev->dev, "apb_pclk");
- int ret;
-
- pcdev->pclk = pclk;
-
- if (IS_ERR(pclk))
- return PTR_ERR(pclk);
-
- ret = clk_prepare(pclk);
- if (ret) {
- clk_put(pclk);
- return ret;
- }
-
- ret = clk_enable(pclk);
- if (ret) {
- clk_unprepare(pclk);
- clk_put(pclk);
- }
-
- return ret;
-}
-
-static void amba_put_disable_pclk(struct amba_device *pcdev)
-{
- struct clk *pclk = pcdev->pclk;
-
- clk_disable(pclk);
- clk_unprepare(pclk);
- clk_put(pclk);
+ WARN(1, "Stub driver should never match any device.\n");
+ return -ENODEV;
}
-/*
- * These are the device model conversion veneers; they convert the
- * device model structures to our more specific structures.
- */
-static int amba_probe(struct device *dev)
-{
- struct amba_device *pcdev = to_amba_device(dev);
- struct amba_driver *pcdrv = to_amba_driver(dev->driver);
- const struct amba_id *id = amba_lookup(pcdrv->id_table, pcdev);
- int ret;
-
- do {
- ret = amba_get_enable_pclk(pcdev);
- if (ret)
- break;
-
- pm_runtime_get_noresume(dev);
- pm_runtime_set_active(dev);
- pm_runtime_enable(dev);
-
- ret = pcdrv->probe(pcdev, id);
- if (ret == 0)
- break;
-
- pm_runtime_disable(dev);
- pm_runtime_set_suspended(dev);
- pm_runtime_put_noidle(dev);
-
- amba_put_disable_pclk(pcdev);
- } while (0);
+static const struct amba_id amba_stub_drv_ids[] = {
+ { 0, 0 },
+};
- return ret;
-}
+static struct amba_driver amba_proxy_drv = {
+ .drv = {
+ .name = "amba-proxy",
+ },
+ .probe = amba_proxy_probe,
+ .id_table = amba_stub_drv_ids,
+};
-static int amba_remove(struct device *dev)
+static int __init amba_stub_drv_init(void)
{
- 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);
- pm_runtime_put_noidle(dev);
-
- /* Undo the runtime PM settings in amba_probe() */
- pm_runtime_disable(dev);
- pm_runtime_set_suspended(dev);
- pm_runtime_put_noidle(dev);
-
- amba_put_disable_pclk(pcdev);
-
- return ret;
-}
+ if (!IS_ENABLED(CONFIG_MODULES))
+ return 0;
-static void amba_shutdown(struct device *dev)
-{
- struct amba_driver *drv = to_amba_driver(dev->driver);
- drv->shutdown(to_amba_device(dev));
+ /*
+ * 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);
/**
- * amba_driver_register - register an AMBA device driver
+ * __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)
+int __amba_driver_register(struct amba_driver *drv,
+ struct module *owner)
{
- drv->drv.bus = &amba_bustype;
+ if (!drv->probe)
+ return -EINVAL;
-#define SETFN(fn) if (drv->fn) drv->drv.fn = amba_##fn
- SETFN(probe);
- SETFN(remove);
- SETFN(shutdown);
+ drv->drv.owner = owner;
+ drv->drv.bus = &amba_bustype;
return driver_register(&drv->drv);
}
+EXPORT_SYMBOL(__amba_driver_register);
/**
* amba_driver_unregister - remove an AMBA device driver
@@ -441,14 +530,16 @@ void amba_driver_unregister(struct amba_driver *drv)
{
driver_unregister(&drv->drv);
}
-
+EXPORT_SYMBOL(amba_driver_unregister);
static void amba_device_release(struct device *dev)
{
struct amba_device *d = to_amba_device(dev);
+ fwnode_handle_put(dev_fwnode(&d->dev));
if (d->res.parent)
release_resource(&d->res);
+ mutex_destroy(&d->periphid_lock);
kfree(d);
}
@@ -463,155 +554,36 @@ static void amba_device_release(struct device *dev)
*/
int amba_device_add(struct amba_device *dev, struct resource *parent)
{
- u32 size;
- void __iomem *tmp;
- int i, ret;
+ int ret;
- WARN_ON(dev->irq[0] == (unsigned int)-1);
- WARN_ON(dev->irq[1] == (unsigned int)-1);
+ fwnode_handle_get(dev_fwnode(&dev->dev));
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;
-
- /*
- * 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;
- }
-
- ret = amba_get_enable_pclk(dev);
- if (ret == 0) {
- u32 pid, cid;
+ return ret;
+ /* If primecell ID isn't hard-coded, figure it out */
+ if (!dev->periphid) {
/*
- * Read pid and cid based on size of resource
- * they are located at end of region
+ * 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().
*/
- 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);
-
- if (cid == AMBA_CID)
- dev->periphid = pid;
-
- if (!dev->periphid)
- ret = -ENODEV;
+ if (amba_read_periphid(dev))
+ dev_set_uevent_suppress(&dev->dev, true);
}
- iounmap(tmp);
-
- if (ret)
- goto err_release;
-
- skip_probe:
ret = device_add(&dev->dev);
if (ret)
- goto err_release;
-
- 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;
-
- device_unregister(&dev->dev);
+ release_resource(&dev->res);
- err_release:
- release_resource(&dev->res);
- err_out:
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);
-
- dev->dma_mask = dma_mask;
- 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 = amba_device_add(dev, resbase);
- if (ret) {
- amba_device_put(dev);
- return ERR_PTR(ret);
- }
-
- 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);
-
-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);
-}
-EXPORT_SYMBOL_GPL(amba_ahb_device_add_res);
-
-
static void amba_device_initialize(struct amba_device *dev, const char *name)
{
device_initialize(&dev->dev);
@@ -619,8 +591,10 @@ static void amba_device_initialize(struct amba_device *dev, const char *name)
dev_set_name(&dev->dev, "%s", name);
dev->dev.release = amba_device_release;
dev->dev.bus = &amba_bustype;
- dev->dev.dma_mask = &dev->dma_mask;
+ 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);
}
/**
@@ -663,11 +637,9 @@ int amba_device_register(struct amba_device *dev, struct resource *parent)
amba_device_initialize(dev, dev->dev.init_name);
dev->dev.init_name = NULL;
- if (!dev->dev.coherent_dma_mask && dev->dma_mask)
- dev_warn(&dev->dev, "coherent dma mask is unset\n");
-
return amba_device_add(dev, parent);
}
+EXPORT_SYMBOL(amba_device_register);
/**
* amba_device_put - put an AMBA device
@@ -694,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
@@ -775,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
@@ -789,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);