summaryrefslogtreecommitdiffstats
path: root/drivers/of/platform.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/of/platform.c')
-rw-r--r--drivers/of/platform.c243
1 files changed, 180 insertions, 63 deletions
diff --git a/drivers/of/platform.c b/drivers/of/platform.c
index 92b6caef5a..918607a518 100644
--- a/drivers/of/platform.c
+++ b/drivers/of/platform.c
@@ -1,18 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* platform.c - bus/device related devicetree functions
*
* Copyright (c) 2012 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
*
* based on Linux devicetree support
- *
- * 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.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <common.h>
#include <deep-probe.h>
@@ -20,6 +12,7 @@
#include <of.h>
#include <of_address.h>
#include <linux/amba/bus.h>
+#include <mmu.h>
/**
* of_find_device_by_node - Find the platform_device associated with a node
@@ -27,17 +20,18 @@
*
* Returns platform_device pointer, or NULL if not found
*/
-struct device_d *of_find_device_by_node(struct device_node *np)
+struct device *of_find_device_by_node(struct device_node *np)
{
- struct device_d *dev;
- int ret;
+ struct device *dev;
- ret = of_device_ensure_probed(np);
- if (ret)
- return NULL;
+ /* Not having a driver is not an error here */
+ (void)of_device_ensure_probed(np);
+
+ if (deep_probe_is_supported())
+ return np->dev;
for_each_device(dev)
- if (dev->device_node == np)
+ if (dev->of_node == np)
return dev;
return NULL;
}
@@ -51,9 +45,9 @@ EXPORT_SYMBOL(of_find_device_by_node);
* derive a unique name. If it cannot, then it will prepend names from
* parent nodes until a unique name can be derived.
*/
-static void of_device_make_bus_id(struct device_d *dev)
+static void of_device_make_bus_id(struct device *dev)
{
- struct device_node *node = dev->device_node;
+ struct device_node *node = dev->of_node;
const __be32 *reg;
u64 addr;
@@ -78,7 +72,64 @@ static void of_device_make_bus_id(struct device_d *dev)
}
}
-static void of_dma_configure(struct device_d *dev, struct device_node *np)
+static struct device_node *of_get_next_dma_parent(const struct device_node *np)
+{
+ struct of_phandle_args args;
+ int ret, index;
+
+ index = of_property_match_string(np, "interconnect-names", "dma-mem");
+ if (index < 0)
+ return of_get_parent(np);
+
+ ret = of_parse_phandle_with_args(np, "interconnects",
+ "#interconnect-cells",
+ index, &args);
+ if (ret < 0)
+ return of_get_parent(np);
+
+ return args.np;
+}
+
+static enum dev_dma_coherence of_dma_get_coherence(struct device_node *node)
+{
+ if (IS_ENABLED(CONFIG_OF_DMA_COHERENCY)) {
+ while (node) {
+ if (of_property_read_bool(node, "dma-coherent"))
+ return DEV_DMA_COHERENT;
+ if (of_property_read_bool(node, "dma-noncoherent"))
+ return DEV_DMA_NON_COHERENT;
+ node = of_get_next_dma_parent(node);
+ }
+ }
+
+ return DEV_DMA_COHERENCE_DEFAULT;
+}
+
+/**
+ * of_dma_is_coherent - Check if device is coherent
+ * @np: device node
+ *
+ * It returns true if "dma-coherent" property was found
+ * for this device in the DT, or if DMA is coherent by
+ * default for OF devices on the current platform and no
+ * "dma-noncoherent" property was found for this device.
+ */
+bool of_dma_is_coherent(struct device_node *node)
+{
+ switch (of_dma_get_coherence(node)) {
+ case DEV_DMA_COHERENT:
+ return true;
+ case DEV_DMA_NON_COHERENT:
+ return false;
+ case DEV_DMA_COHERENCE_DEFAULT:
+ return IS_ENABLED(CONFIG_ARCH_DMA_DEFAULT_COHERENT);
+ }
+
+ BUG();
+}
+EXPORT_SYMBOL_GPL(of_dma_is_coherent);
+
+static void of_dma_configure(struct device *dev, struct device_node *np)
{
u64 dma_addr, paddr, size = 0;
unsigned long offset;
@@ -92,6 +143,7 @@ static void of_dma_configure(struct device_d *dev, struct device_node *np)
}
dev->dma_offset = offset;
+ dev->dma_coherent = of_dma_get_coherence(np);
}
/**
@@ -102,13 +154,14 @@ static void of_dma_configure(struct device_d *dev, struct device_node *np)
* Returns pointer to created platform device, or NULL if a device was not
* registered. Unavailable devices will not get registered.
*/
-struct device_d *of_platform_device_create(struct device_node *np,
- struct device_d *parent)
+struct device *of_platform_device_create(struct device_node *np,
+ struct device *parent)
{
- struct device_d *dev;
+ struct device *dev;
struct resource *res = NULL, temp_res;
resource_size_t resinval;
int i, ret, num_reg = 0;
+ u32 virt;
if (!of_device_is_available(np))
return NULL;
@@ -117,8 +170,10 @@ struct device_d *of_platform_device_create(struct device_node *np,
* Linux uses the OF_POPULATED flag to skip already populated/created
* devices.
*/
- if (np->dev)
+ if (np->dev) {
+ device_rescan(np->dev);
return np->dev;
+ }
/* count the io resources */
if (of_can_translate_address(np))
@@ -140,7 +195,7 @@ struct device_d *of_platform_device_create(struct device_node *np,
/* setup generic device info */
dev = xzalloc(sizeof(*dev));
dev->id = DEVICE_ID_SINGLE;
- dev->device_node = np;
+ dev->of_node = np;
dev->parent = parent;
dev->resource = res;
dev->num_resources = num_reg;
@@ -148,6 +203,24 @@ struct device_d *of_platform_device_create(struct device_node *np,
of_dma_configure(dev, np);
+ if (num_reg && !of_property_read_u32(np, "virtual-reg", &virt)) {
+ resource_size_t remap_offset = virt - res[0].start;
+
+ for (i = 0; i < num_reg; i++) {
+ void *new_virt = (void *)res[i].start + remap_offset;
+ resource_size_t size = resource_size(&res[i]);
+
+ ret = arch_remap_range(new_virt, res[i].start, size, MAP_UNCACHED);
+ if (!ret) {
+ debug("%s: remap device %s resource %d: %pa -> 0x%p\n",
+ __func__, dev_name(dev), i, &res[i].start, new_virt);
+
+ res[i].start = (resource_size_t)new_virt;
+ res[i].end = res[i].start + size - 1;
+ }
+ }
+ }
+
resinval = (-1);
debug("%s: register device %s, io=%pa\n",
@@ -163,17 +236,17 @@ struct device_d *of_platform_device_create(struct device_node *np,
np->dev = NULL;
- free(dev);
+ free_device(dev);
if (num_reg)
free(res);
return NULL;
}
-struct driver_d dummy_driver = {
+struct driver dummy_driver = {
.name = "dummy-driver",
};
-void of_platform_device_dummy_drv(struct device_d *dev)
+void of_platform_device_dummy_drv(struct device *dev)
{
dev->driver = &dummy_driver;
}
@@ -185,9 +258,9 @@ void of_platform_device_dummy_drv(struct device_d *dev)
* Returns pointer to created platform device, or NULL if a device was not
* registered. Unavailable devices will not get registered.
*/
-struct device_d *of_device_enable_and_register(struct device_node *np)
+struct device *of_device_enable_and_register(struct device_node *np)
{
- struct device_d *dev;
+ struct device *dev;
of_device_enable(np);
@@ -206,11 +279,11 @@ EXPORT_SYMBOL(of_device_enable_and_register);
* Returns pointer to created platform device, or NULL if a device was not
* registered. Unavailable devices will not get registered.
*/
-struct device_d *of_device_enable_and_register_by_name(const char *name)
+struct device *of_device_enable_and_register_by_name(const char *name)
{
struct device_node *node;
- node = of_find_node_by_name(NULL, name);
+ node = of_find_node_by_name_address(NULL, name);
if (!node)
node = of_find_node_by_path(name);
@@ -228,7 +301,7 @@ EXPORT_SYMBOL(of_device_enable_and_register_by_name);
* Returns pointer to created platform device, or NULL if a device was not
* registered. Unavailable devices will not get registered.
*/
-struct device_d *of_device_enable_and_register_by_alias(const char *alias)
+struct device *of_device_enable_and_register_by_alias(const char *alias)
{
struct device_node *node;
@@ -241,12 +314,12 @@ struct device_d *of_device_enable_and_register_by_alias(const char *alias)
EXPORT_SYMBOL(of_device_enable_and_register_by_alias);
#ifdef CONFIG_ARM_AMBA
-static struct device_d *of_amba_device_create(struct device_node *np)
+static struct device *of_amba_device_create(struct device_node *np)
{
struct amba_device *dev;
int ret;
- debug("Creating amba device %s\n", np->full_name);
+ debug("Creating amba device %pOF\n", np);
if (!of_device_is_available(np))
return NULL;
@@ -262,7 +335,7 @@ static struct device_d *of_amba_device_create(struct device_node *np)
/* setup generic device info */
dev->dev.id = DEVICE_ID_SINGLE;
- dev->dev.device_node = np;
+ dev->dev.of_node = np;
of_device_make_bus_id(&dev->dev);
ret = of_address_to_resource(np, 0, &dev->res);
@@ -286,11 +359,12 @@ static struct device_d *of_amba_device_create(struct device_node *np)
return &dev->dev;
amba_err_free:
+ free_device_res(&dev->dev);
free(dev);
return NULL;
}
#else /* CONFIG_ARM_AMBA */
-static inline struct device_d *of_amba_device_create(struct device_node *np)
+static inline struct device *of_amba_device_create(struct device_node *np)
{
return NULL;
}
@@ -307,16 +381,16 @@ static inline struct device_d *of_amba_device_create(struct device_node *np)
*/
static int of_platform_bus_create(struct device_node *bus,
const struct of_device_id *matches,
- struct device_d *parent)
+ struct device *parent)
{
struct device_node *child;
- struct device_d *dev;
+ struct device *dev;
int rc = 0;
/* Make sure it has a compatible property */
if (!of_get_property(bus, "compatible", NULL)) {
- pr_debug("%s() - skipping %s, no compatible prop\n",
- __func__, bus->full_name);
+ pr_debug("%s() - skipping %pOF, no compatible prop\n",
+ __func__, bus);
return 0;
}
@@ -330,7 +404,7 @@ static int of_platform_bus_create(struct device_node *bus,
return 0;
for_each_child_of_node(bus, child) {
- pr_debug(" create child: %s\n", child->full_name);
+ pr_debug(" create child: %pOF\n", child);
rc = of_platform_bus_create(child, matches, dev);
if (rc)
break;
@@ -353,7 +427,7 @@ static int of_platform_bus_create(struct device_node *bus,
*/
int of_platform_populate(struct device_node *root,
const struct of_device_id *matches,
- struct device_d *parent)
+ struct device *parent)
{
struct device_node *child;
int rc = 0;
@@ -373,17 +447,17 @@ int of_platform_populate(struct device_node *root,
}
EXPORT_SYMBOL_GPL(of_platform_populate);
-static struct device_d *of_device_create_on_demand(struct device_node *np)
+static struct device *of_device_create_on_demand(struct device_node *np)
{
struct device_node *parent;
- struct device_d *parent_dev, *dev;
+ struct device *parent_dev, *dev;
parent = of_get_parent(np);
if (!parent)
return NULL;
- if (!np->dev)
- pr_debug("Creating device for %s\n", np->full_name);
+ if (!np->dev && parent->dev)
+ device_rescan(parent->dev);
/* Create all parent devices needed for the requested device */
parent_dev = parent->dev ? : of_device_create_on_demand(parent);
@@ -398,12 +472,17 @@ static struct device_d *of_device_create_on_demand(struct device_node *np)
if (np->dev)
return np->dev;
+ if (!of_property_present(np, "compatible"))
+ return NULL;
+
+ pr_debug("Creating device for %pOF\n", np);
+
if (of_device_is_compatible(np, "arm,primecell"))
dev = of_amba_device_create(np);
else
dev = of_platform_device_create(np, parent_dev);
- return dev ? : ERR_PTR(-ENODEV);
+ return dev ? : ERR_PTR(-EPROBE_DEFER);
}
/**
@@ -415,21 +494,19 @@ static struct device_d *of_device_create_on_demand(struct device_node *np)
* it.
*
* Return: %0 on success
- * %-ENODEV if either the device can't be populated, the driver is
+ * %-EPROBE_DEFER if either the device can't be populated, the driver is
* missing or the driver probe returns an error.
*/
int of_device_ensure_probed(struct device_node *np)
{
- struct device_d *dev;
+ struct device *dev;
- if (!deep_probe_is_supported())
+ if (!np || !deep_probe_is_supported())
return 0;
dev = of_device_create_on_demand(np);
- if (IS_ERR(dev))
- return PTR_ERR(dev);
-
- BUG_ON(!dev);
+ if (IS_ERR_OR_NULL(dev))
+ return -EPROBE_DEFER;
/*
* The deep-probe mechanism relies on the fact that all necessary
@@ -439,7 +516,7 @@ int of_device_ensure_probed(struct device_node *np)
* requirements are fulfilled if 'dev->driver' is not NULL.
*/
if (!dev->driver)
- return -ENODEV;
+ return -EPROBE_DEFER;
return 0;
}
@@ -454,7 +531,7 @@ EXPORT_SYMBOL_GPL(of_device_ensure_probed);
* populated and probed if found.
*
* Return: %0 on success
- * %-ENODEV if either the device can't be populated, the driver is
+ * %-EPROBE_DEFER if either the device can't be populated, the driver is
* missing or the driver probe returns an error
* %-EINVAL if alias can't be found
*/
@@ -462,6 +539,9 @@ int of_device_ensure_probed_by_alias(const char *alias)
{
struct device_node *dev_node;
+ if (!deep_probe_is_supported())
+ return 0;
+
dev_node = of_find_node_by_alias(NULL, alias);
if (!dev_node)
return -EINVAL;
@@ -479,7 +559,7 @@ EXPORT_SYMBOL_GPL(of_device_ensure_probed_by_alias);
* probes devices which match @ids.
*
* Return: %0 on success
- * %-ENODEV if either the device wasn't found, can't be populated,
+ * %-EPROBE_DEFER if either the device wasn't found, can't be populated,
* the driver is missing or the driver probe returns an error
*/
int of_devices_ensure_probed_by_dev_id(const struct of_device_id *ids)
@@ -487,6 +567,9 @@ int of_devices_ensure_probed_by_dev_id(const struct of_device_id *ids)
struct device_node *np;
int err, ret = 0;
+ if (!deep_probe_is_supported())
+ return 0;
+
for_each_matching_node(np, ids) {
if (!of_device_is_available(np))
continue;
@@ -509,25 +592,51 @@ EXPORT_SYMBOL_GPL(of_devices_ensure_probed_by_dev_id);
* devices which matches @property_name.
*
* Return: %0 on success
- * %-ENODEV if either the device wasn't found, can't be populated,
+ * %-EPROBE_DEFER if either the device wasn't found, can't be populated,
* the driver is missing or the driver probe returns an error
*/
int of_devices_ensure_probed_by_property(const char *property_name)
{
struct device_node *node;
+ int err, ret = 0;
+
+ if (!deep_probe_is_supported())
+ return 0;
for_each_node_with_property(node, property_name) {
- int ret;
+ if (!of_device_is_available(node))
+ continue;
- ret = of_device_ensure_probed(node);
- if (ret)
- return ret;
+ err = of_device_ensure_probed(node);
+ if (err)
+ ret = err;
}
- return 0;
+ return ret;
}
EXPORT_SYMBOL_GPL(of_devices_ensure_probed_by_property);
+int of_devices_ensure_probed_by_name(const char *name)
+{
+ struct device_node *node;
+ int err, ret = 0;
+
+ if (!deep_probe_is_supported())
+ return 0;
+
+ for_each_node_by_name(node, name) {
+ if (!of_device_is_available(node))
+ continue;
+
+ err = of_device_ensure_probed(node);
+ if (err)
+ ret = err;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(of_devices_ensure_probed_by_name);
+
static int of_stdoutpath_init(void)
{
struct device_node *np;
@@ -544,3 +653,11 @@ static int of_stdoutpath_init(void)
return of_device_ensure_probed(np);
}
postconsole_initcall(of_stdoutpath_init);
+
+static int of_timer_init(void)
+{
+ of_devices_ensure_probed_by_name("timer");
+
+ return 0;
+}
+postcore_initcall(of_timer_init);