summaryrefslogtreecommitdiffstats
path: root/drivers/of
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/of')
-rw-r--r--drivers/of/Kconfig12
-rw-r--r--drivers/of/Makefile2
-rw-r--r--drivers/of/base.c121
-rw-r--r--drivers/of/fdt.c76
-rw-r--r--drivers/of/of_i2c.c114
-rw-r--r--drivers/of/of_pci.c45
-rw-r--r--drivers/of/of_reserved_mem.c175
-rw-r--r--drivers/of/platform.c4
8 files changed, 428 insertions, 121 deletions
diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
index 80e5c13..9d2009a 100644
--- a/drivers/of/Kconfig
+++ b/drivers/of/Kconfig
@@ -48,12 +48,6 @@ config OF_IRQ
def_bool y
depends on !SPARC
-config OF_I2C
- def_tristate I2C
- depends on I2C
- help
- OpenFirmware I2C accessors
-
config OF_NET
depends on NETDEVICES
def_bool y
@@ -80,4 +74,10 @@ config OF_MTD
depends on MTD
def_bool y
+config OF_RESERVED_MEM
+ depends on OF_FLATTREE && (DMA_CMA || (HAVE_GENERIC_DMA_COHERENT && HAVE_MEMBLOCK))
+ def_bool y
+ help
+ Initialization code for DMA reserved memory
+
endmenu # OF
diff --git a/drivers/of/Makefile b/drivers/of/Makefile
index 1f9c0c4..ed9660a 100644
--- a/drivers/of/Makefile
+++ b/drivers/of/Makefile
@@ -3,10 +3,10 @@ obj-$(CONFIG_OF_FLATTREE) += fdt.o
obj-$(CONFIG_OF_PROMTREE) += pdt.o
obj-$(CONFIG_OF_ADDRESS) += address.o
obj-$(CONFIG_OF_IRQ) += irq.o
-obj-$(CONFIG_OF_I2C) += of_i2c.o
obj-$(CONFIG_OF_NET) += of_net.o
obj-$(CONFIG_OF_SELFTEST) += selftest.o
obj-$(CONFIG_OF_MDIO) += of_mdio.o
obj-$(CONFIG_OF_PCI) += of_pci.o
obj-$(CONFIG_OF_PCI_IRQ) += of_pci_irq.o
obj-$(CONFIG_OF_MTD) += of_mtd.o
+obj-$(CONFIG_OF_RESERVED_MEM) += of_reserved_mem.o
diff --git a/drivers/of/base.c b/drivers/of/base.c
index 7c75d75..865d3f6 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -18,6 +18,7 @@
* 2 of the License, or (at your option) any later version.
*/
#include <linux/ctype.h>
+#include <linux/cpu.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/spinlock.h>
@@ -32,6 +33,7 @@ struct device_node *of_allnodes;
EXPORT_SYMBOL(of_allnodes);
struct device_node *of_chosen;
struct device_node *of_aliases;
+static struct device_node *of_stdout;
DEFINE_MUTEX(of_aliases_mutex);
@@ -230,6 +232,100 @@ const void *of_get_property(const struct device_node *np, const char *name,
}
EXPORT_SYMBOL(of_get_property);
+/*
+ * arch_match_cpu_phys_id - Match the given logical CPU and physical id
+ *
+ * @cpu: logical cpu index of a core/thread
+ * @phys_id: physical identifier of a core/thread
+ *
+ * CPU logical to physical index mapping is architecture specific.
+ * However this __weak function provides a default match of physical
+ * id to logical cpu index. phys_id provided here is usually values read
+ * from the device tree which must match the hardware internal registers.
+ *
+ * Returns true if the physical identifier and the logical cpu index
+ * correspond to the same core/thread, false otherwise.
+ */
+bool __weak arch_match_cpu_phys_id(int cpu, u64 phys_id)
+{
+ return (u32)phys_id == cpu;
+}
+
+/**
+ * Checks if the given "prop_name" property holds the physical id of the
+ * core/thread corresponding to the logical cpu 'cpu'. If 'thread' is not
+ * NULL, local thread number within the core is returned in it.
+ */
+static bool __of_find_n_match_cpu_property(struct device_node *cpun,
+ const char *prop_name, int cpu, unsigned int *thread)
+{
+ const __be32 *cell;
+ int ac, prop_len, tid;
+ u64 hwid;
+
+ ac = of_n_addr_cells(cpun);
+ cell = of_get_property(cpun, prop_name, &prop_len);
+ if (!cell)
+ return false;
+ prop_len /= sizeof(*cell);
+ for (tid = 0; tid < prop_len; tid++) {
+ hwid = of_read_number(cell, ac);
+ if (arch_match_cpu_phys_id(cpu, hwid)) {
+ if (thread)
+ *thread = tid;
+ return true;
+ }
+ cell += ac;
+ }
+ return false;
+}
+
+/**
+ * of_get_cpu_node - Get device node associated with the given logical CPU
+ *
+ * @cpu: CPU number(logical index) for which device node is required
+ * @thread: if not NULL, local thread number within the physical core is
+ * returned
+ *
+ * The main purpose of this function is to retrieve the device node for the
+ * given logical CPU index. It should be used to initialize the of_node in
+ * cpu device. Once of_node in cpu device is populated, all the further
+ * references can use that instead.
+ *
+ * CPU logical to physical index mapping is architecture specific and is built
+ * before booting secondary cores. This function uses arch_match_cpu_phys_id
+ * which can be overridden by architecture specific implementation.
+ *
+ * Returns a node pointer for the logical cpu if found, else NULL.
+ */
+struct device_node *of_get_cpu_node(int cpu, unsigned int *thread)
+{
+ struct device_node *cpun, *cpus;
+
+ cpus = of_find_node_by_path("/cpus");
+ if (!cpus) {
+ pr_warn("Missing cpus node, bailing out\n");
+ return NULL;
+ }
+
+ for_each_child_of_node(cpus, cpun) {
+ if (of_node_cmp(cpun->type, "cpu"))
+ continue;
+ /* Check for non-standard "ibm,ppc-interrupt-server#s" property
+ * for thread ids on PowerPC. If it doesn't exist fallback to
+ * standard "reg" property.
+ */
+ if (IS_ENABLED(CONFIG_PPC) &&
+ __of_find_n_match_cpu_property(cpun,
+ "ibm,ppc-interrupt-server#s", cpu, thread))
+ return cpun;
+ if (__of_find_n_match_cpu_property(cpun, "reg", cpu, thread))
+ return cpun;
+ }
+ return NULL;
+}
+EXPORT_SYMBOL(of_get_cpu_node);
+
/** Checks if the given "compat" string matches one of the strings in
* the device's "compatible" property
*/
@@ -1654,6 +1750,15 @@ void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align))
of_chosen = of_find_node_by_path("/chosen");
if (of_chosen == NULL)
of_chosen = of_find_node_by_path("/chosen@0");
+
+ if (of_chosen) {
+ const char *name;
+
+ name = of_get_property(of_chosen, "linux,stdout-path", NULL);
+ if (name)
+ of_stdout = of_find_node_by_path(name);
+ }
+
of_aliases = of_find_node_by_path("/aliases");
if (!of_aliases)
return;
@@ -1763,3 +1868,19 @@ const char *of_prop_next_string(struct property *prop, const char *cur)
return curv;
}
EXPORT_SYMBOL_GPL(of_prop_next_string);
+
+/**
+ * of_device_is_stdout_path - check if a device node matches the
+ * linux,stdout-path property
+ *
+ * Check if this device node matches the linux,stdout-path property
+ * in the chosen node. return true if yes, false otherwise.
+ */
+int of_device_is_stdout_path(struct device_node *dn)
+{
+ if (!of_stdout)
+ return false;
+
+ return of_stdout == dn;
+}
+EXPORT_SYMBOL_GPL(of_device_is_stdout_path);
diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c
index 543c500..229dd9d 100644
--- a/drivers/of/fdt.c
+++ b/drivers/of/fdt.c
@@ -543,6 +543,82 @@ int __init of_flat_dt_match(unsigned long node, const char *const *compat)
return of_fdt_match(initial_boot_params, node, compat);
}
+struct fdt_scan_status {
+ const char *name;
+ int namelen;
+ int depth;
+ int found;
+ int (*iterator)(unsigned long node, const char *uname, int depth, void *data);
+ void *data;
+};
+
+/**
+ * fdt_scan_node_by_path - iterator for of_scan_flat_dt_by_path function
+ */
+static int __init fdt_scan_node_by_path(unsigned long node, const char *uname,
+ int depth, void *data)
+{
+ struct fdt_scan_status *st = data;
+
+ /*
+ * if scan at the requested fdt node has been completed,
+ * return -ENXIO to abort further scanning
+ */
+ if (depth <= st->depth)
+ return -ENXIO;
+
+ /* requested fdt node has been found, so call iterator function */
+ if (st->found)
+ return st->iterator(node, uname, depth, st->data);
+
+ /* check if scanning automata is entering next level of fdt nodes */
+ if (depth == st->depth + 1 &&
+ strncmp(st->name, uname, st->namelen) == 0 &&
+ uname[st->namelen] == 0) {
+ st->depth += 1;
+ if (st->name[st->namelen] == 0) {
+ st->found = 1;
+ } else {
+ const char *next = st->name + st->namelen + 1;
+ st->name = next;
+ st->namelen = strcspn(next, "/");
+ }
+ return 0;
+ }
+
+ /* scan next fdt node */
+ return 0;
+}
+
+/**
+ * of_scan_flat_dt_by_path - scan flattened tree blob and call callback on each
+ * child of the given path.
+ * @path: path to start searching for children
+ * @it: callback function
+ * @data: context data pointer
+ *
+ * This function is used to scan the flattened device-tree starting from the
+ * node given by path. It is used to extract information (like reserved
+ * memory), which is required on ealy boot before we can unflatten the tree.
+ */
+int __init of_scan_flat_dt_by_path(const char *path,
+ int (*it)(unsigned long node, const char *name, int depth, void *data),
+ void *data)
+{
+ struct fdt_scan_status st = {path, 0, -1, 0, it, data};
+ int ret = 0;
+
+ if (initial_boot_params)
+ ret = of_scan_flat_dt(fdt_scan_node_by_path, &st);
+
+ if (!st.found)
+ return -ENOENT;
+ else if (ret == -ENXIO) /* scan has been completed */
+ return 0;
+ else
+ return ret;
+}
+
#ifdef CONFIG_BLK_DEV_INITRD
/**
* early_init_dt_check_for_initrd - Decode initrd location from flat tree
diff --git a/drivers/of/of_i2c.c b/drivers/of/of_i2c.c
deleted file mode 100644
index b667264..0000000
--- a/drivers/of/of_i2c.c
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * OF helpers for the I2C API
- *
- * Copyright (c) 2008 Jochen Friedrich <jochen@scram.de>
- *
- * Based on a previous patch from Jon Smirl <jonsmirl@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
-
-#include <linux/i2c.h>
-#include <linux/irq.h>
-#include <linux/of.h>
-#include <linux/of_i2c.h>
-#include <linux/of_irq.h>
-#include <linux/module.h>
-
-void of_i2c_register_devices(struct i2c_adapter *adap)
-{
- void *result;
- struct device_node *node;
-
- /* Only register child devices if the adapter has a node pointer set */
- if (!adap->dev.of_node)
- return;
-
- dev_dbg(&adap->dev, "of_i2c: walking child nodes\n");
-
- for_each_available_child_of_node(adap->dev.of_node, node) {
- struct i2c_board_info info = {};
- struct dev_archdata dev_ad = {};
- const __be32 *addr;
- int len;
-
- dev_dbg(&adap->dev, "of_i2c: register %s\n", node->full_name);
-
- if (of_modalias_node(node, info.type, sizeof(info.type)) < 0) {
- dev_err(&adap->dev, "of_i2c: modalias failure on %s\n",
- node->full_name);
- continue;
- }
-
- addr = of_get_property(node, "reg", &len);
- if (!addr || (len < sizeof(int))) {
- dev_err(&adap->dev, "of_i2c: invalid reg on %s\n",
- node->full_name);
- continue;
- }
-
- info.addr = be32_to_cpup(addr);
- if (info.addr > (1 << 10) - 1) {
- dev_err(&adap->dev, "of_i2c: invalid addr=%x on %s\n",
- info.addr, node->full_name);
- continue;
- }
-
- info.irq = irq_of_parse_and_map(node, 0);
- info.of_node = of_node_get(node);
- info.archdata = &dev_ad;
-
- if (of_get_property(node, "wakeup-source", NULL))
- info.flags |= I2C_CLIENT_WAKE;
-
- request_module("%s%s", I2C_MODULE_PREFIX, info.type);
-
- result = i2c_new_device(adap, &info);
- if (result == NULL) {
- dev_err(&adap->dev, "of_i2c: Failure registering %s\n",
- node->full_name);
- of_node_put(node);
- irq_dispose_mapping(info.irq);
- continue;
- }
- }
-}
-EXPORT_SYMBOL(of_i2c_register_devices);
-
-static int of_dev_node_match(struct device *dev, void *data)
-{
- return dev->of_node == data;
-}
-
-/* must call put_device() when done with returned i2c_client device */
-struct i2c_client *of_find_i2c_device_by_node(struct device_node *node)
-{
- struct device *dev;
-
- dev = bus_find_device(&i2c_bus_type, NULL, node,
- of_dev_node_match);
- if (!dev)
- return NULL;
-
- return i2c_verify_client(dev);
-}
-EXPORT_SYMBOL(of_find_i2c_device_by_node);
-
-/* must call put_device() when done with returned i2c_adapter device */
-struct i2c_adapter *of_find_i2c_adapter_by_node(struct device_node *node)
-{
- struct device *dev;
-
- dev = bus_find_device(&i2c_bus_type, NULL, node,
- of_dev_node_match);
- if (!dev)
- return NULL;
-
- return i2c_verify_adapter(dev);
-}
-EXPORT_SYMBOL(of_find_i2c_adapter_by_node);
-
-MODULE_LICENSE("GPL");
diff --git a/drivers/of/of_pci.c b/drivers/of/of_pci.c
index 42c687a..e5ca008 100644
--- a/drivers/of/of_pci.c
+++ b/drivers/of/of_pci.c
@@ -89,3 +89,48 @@ int of_pci_parse_bus_range(struct device_node *node, struct resource *res)
return 0;
}
EXPORT_SYMBOL_GPL(of_pci_parse_bus_range);
+
+#ifdef CONFIG_PCI_MSI
+
+static LIST_HEAD(of_pci_msi_chip_list);
+static DEFINE_MUTEX(of_pci_msi_chip_mutex);
+
+int of_pci_msi_chip_add(struct msi_chip *chip)
+{
+ if (!of_property_read_bool(chip->of_node, "msi-controller"))
+ return -EINVAL;
+
+ mutex_lock(&of_pci_msi_chip_mutex);
+ list_add(&chip->list, &of_pci_msi_chip_list);
+ mutex_unlock(&of_pci_msi_chip_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(of_pci_msi_chip_add);
+
+void of_pci_msi_chip_remove(struct msi_chip *chip)
+{
+ mutex_lock(&of_pci_msi_chip_mutex);
+ list_del(&chip->list);
+ mutex_unlock(&of_pci_msi_chip_mutex);
+}
+EXPORT_SYMBOL_GPL(of_pci_msi_chip_remove);
+
+struct msi_chip *of_pci_find_msi_chip_by_node(struct device_node *of_node)
+{
+ struct msi_chip *c;
+
+ mutex_lock(&of_pci_msi_chip_mutex);
+ list_for_each_entry(c, &of_pci_msi_chip_list, list) {
+ if (c->of_node == of_node) {
+ mutex_unlock(&of_pci_msi_chip_mutex);
+ return c;
+ }
+ }
+ mutex_unlock(&of_pci_msi_chip_mutex);
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(of_pci_find_msi_chip_by_node);
+
+#endif /* CONFIG_PCI_MSI */
diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c
new file mode 100644
index 0000000..a754b84
--- /dev/null
+++ b/drivers/of/of_reserved_mem.c
@@ -0,0 +1,175 @@
+/*
+ * Device tree based initialization code for reserved memory.
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ * Author: Marek Szyprowski <m.szyprowski@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License or (at your optional) any later version of the license.
+ */
+
+#include <asm/dma-contiguous.h>
+
+#include <linux/memblock.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/of_fdt.h>
+#include <linux/of_platform.h>
+#include <linux/mm.h>
+#include <linux/sizes.h>
+#include <linux/mm_types.h>
+#include <linux/dma-contiguous.h>
+#include <linux/dma-mapping.h>
+#include <linux/of_reserved_mem.h>
+
+#define MAX_RESERVED_REGIONS 16
+struct reserved_mem {
+ phys_addr_t base;
+ unsigned long size;
+ struct cma *cma;
+ char name[32];
+};
+static struct reserved_mem reserved_mem[MAX_RESERVED_REGIONS];
+static int reserved_mem_count;
+
+static int __init fdt_scan_reserved_mem(unsigned long node, const char *uname,
+ int depth, void *data)
+{
+ struct reserved_mem *rmem = &reserved_mem[reserved_mem_count];
+ phys_addr_t base, size;
+ int is_cma, is_reserved;
+ unsigned long len;
+ const char *status;
+ __be32 *prop;
+
+ is_cma = IS_ENABLED(CONFIG_DMA_CMA) &&
+ of_flat_dt_is_compatible(node, "linux,contiguous-memory-region");
+ is_reserved = of_flat_dt_is_compatible(node, "reserved-memory-region");
+
+ if (!is_reserved && !is_cma) {
+ /* ignore node and scan next one */
+ return 0;
+ }
+
+ status = of_get_flat_dt_prop(node, "status", &len);
+ if (status && strcmp(status, "okay") != 0) {
+ /* ignore disabled node nad scan next one */
+ return 0;
+ }
+
+ prop = of_get_flat_dt_prop(node, "reg", &len);
+ if (!prop || (len < (dt_root_size_cells + dt_root_addr_cells) *
+ sizeof(__be32))) {
+ pr_err("Reserved mem: node %s, incorrect \"reg\" property\n",
+ uname);
+ /* ignore node and scan next one */
+ return 0;
+ }
+ base = dt_mem_next_cell(dt_root_addr_cells, &prop);
+ size = dt_mem_next_cell(dt_root_size_cells, &prop);
+
+ if (!size) {
+ /* ignore node and scan next one */
+ return 0;
+ }
+
+ pr_info("Reserved mem: found %s, memory base %lx, size %ld MiB\n",
+ uname, (unsigned long)base, (unsigned long)size / SZ_1M);
+
+ if (reserved_mem_count == ARRAY_SIZE(reserved_mem))
+ return -ENOSPC;
+
+ rmem->base = base;
+ rmem->size = size;
+ strlcpy(rmem->name, uname, sizeof(rmem->name));
+
+ if (is_cma) {
+ struct cma *cma;
+ if (dma_contiguous_reserve_area(size, base, 0, &cma) == 0) {
+ rmem->cma = cma;
+ reserved_mem_count++;
+ if (of_get_flat_dt_prop(node,
+ "linux,default-contiguous-region",
+ NULL))
+ dma_contiguous_set_default(cma);
+ }
+ } else if (is_reserved) {
+ if (memblock_remove(base, size) == 0)
+ reserved_mem_count++;
+ else
+ pr_err("Failed to reserve memory for %s\n", uname);
+ }
+
+ return 0;
+}
+
+static struct reserved_mem *get_dma_memory_region(struct device *dev)
+{
+ struct device_node *node;
+ const char *name;
+ int i;
+
+ node = of_parse_phandle(dev->of_node, "memory-region", 0);
+ if (!node)
+ return NULL;
+
+ name = kbasename(node->full_name);
+ for (i = 0; i < reserved_mem_count; i++)
+ if (strcmp(name, reserved_mem[i].name) == 0)
+ return &reserved_mem[i];
+ return NULL;
+}
+
+/**
+ * of_reserved_mem_device_init() - assign reserved memory region to given device
+ *
+ * This function assign memory region pointed by "memory-region" device tree
+ * property to the given device.
+ */
+void of_reserved_mem_device_init(struct device *dev)
+{
+ struct reserved_mem *region = get_dma_memory_region(dev);
+ if (!region)
+ return;
+
+ if (region->cma) {
+ dev_set_cma_area(dev, region->cma);
+ pr_info("Assigned CMA %s to %s device\n", region->name,
+ dev_name(dev));
+ } else {
+ if (dma_declare_coherent_memory(dev, region->base, region->base,
+ region->size, DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE) != 0)
+ pr_info("Declared reserved memory %s to %s device\n",
+ region->name, dev_name(dev));
+ }
+}
+
+/**
+ * of_reserved_mem_device_release() - release reserved memory device structures
+ *
+ * This function releases structures allocated for memory region handling for
+ * the given device.
+ */
+void of_reserved_mem_device_release(struct device *dev)
+{
+ struct reserved_mem *region = get_dma_memory_region(dev);
+ if (!region && !region->cma)
+ dma_release_declared_memory(dev);
+}
+
+/**
+ * early_init_dt_scan_reserved_mem() - create reserved memory regions
+ *
+ * This function grabs memory from early allocator for device exclusive use
+ * defined in device tree structures. It should be called by arch specific code
+ * once the early allocator (memblock) has been activated and all other
+ * subsystems have already allocated/reserved memory.
+ */
+void __init early_init_dt_scan_reserved_mem(void)
+{
+ of_scan_flat_dt_by_path("/memory/reserved-memory",
+ fdt_scan_reserved_mem, NULL);
+}
diff --git a/drivers/of/platform.c b/drivers/of/platform.c
index f6dcde2..9b439ac 100644
--- a/drivers/of/platform.c
+++ b/drivers/of/platform.c
@@ -21,6 +21,7 @@
#include <linux/of_device.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
+#include <linux/of_reserved_mem.h>
#include <linux/platform_device.h>
const struct of_device_id of_default_bus_match_table[] = {
@@ -218,6 +219,8 @@ static struct platform_device *of_platform_device_create_pdata(
dev->dev.bus = &platform_bus_type;
dev->dev.platform_data = platform_data;
+ of_reserved_mem_device_init(&dev->dev);
+
/* We do not fill the DMA ops for platform devices by default.
* This is currently the responsibility of the platform code
* to do such, possibly using a device notifier
@@ -225,6 +228,7 @@ static struct platform_device *of_platform_device_create_pdata(
if (of_device_add(dev) != 0) {
platform_device_put(dev);
+ of_reserved_mem_device_release(&dev->dev);
return NULL;
}
OpenPOWER on IntegriCloud