aboutsummaryrefslogtreecommitdiff
path: root/drivers/of
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/of')
-rw-r--r--drivers/of/Kconfig40
-rw-r--r--drivers/of/Makefile2
-rw-r--r--drivers/of/address.c595
-rw-r--r--drivers/of/base.c76
-rw-r--r--drivers/of/device.c91
-rw-r--r--drivers/of/fdt.c26
-rw-r--r--drivers/of/gpio.c93
-rw-r--r--drivers/of/irq.c349
-rw-r--r--drivers/of/of_i2c.c50
-rw-r--r--drivers/of/of_mdio.c1
-rw-r--r--drivers/of/of_spi.c11
-rw-r--r--drivers/of/platform.c384
12 files changed, 1548 insertions, 170 deletions
diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
index 7cecc8fea9b..6acbff389ab 100644
--- a/drivers/of/Kconfig
+++ b/drivers/of/Kconfig
@@ -1,35 +1,61 @@
-config OF_FLATTREE
+config DTC
+ bool
+
+config OF
bool
+
+menu "Flattened Device Tree and Open Firmware support"
depends on OF
+config PROC_DEVICETREE
+ bool "Support for device tree in /proc"
+ depends on PROC_FS && !SPARC
+ help
+ This option adds a device-tree directory under /proc which contains
+ an image of the device tree that the kernel copies from Open
+ Firmware or other boot firmware. If unsure, say Y here.
+
+config OF_FLATTREE
+ bool
+ select DTC
+
config OF_DYNAMIC
def_bool y
- depends on OF && PPC_OF
+ depends on PPC_OF
+
+config OF_ADDRESS
+ def_bool y
+ depends on !SPARC
+
+config OF_IRQ
+ def_bool y
+ depends on !SPARC
config OF_DEVICE
def_bool y
- depends on OF && (SPARC || PPC_OF || MICROBLAZE)
config OF_GPIO
def_bool y
- depends on OF && (PPC_OF || MICROBLAZE) && GPIOLIB
+ depends on GPIOLIB && !SPARC
help
OpenFirmware GPIO accessors
config OF_I2C
def_tristate I2C
- depends on (PPC_OF || MICROBLAZE) && I2C
+ depends on I2C && !SPARC
help
OpenFirmware I2C accessors
config OF_SPI
def_tristate SPI
- depends on OF && (PPC_OF || MICROBLAZE) && SPI
+ depends on SPI && !SPARC
help
OpenFirmware SPI accessors
config OF_MDIO
def_tristate PHYLIB
- depends on OF && PHYLIB
+ depends on PHYLIB
help
OpenFirmware MDIO bus (Ethernet PHY) accessors
+
+endmenu # OF
diff --git a/drivers/of/Makefile b/drivers/of/Makefile
index f232cc98ce0..0052c405463 100644
--- a/drivers/of/Makefile
+++ b/drivers/of/Makefile
@@ -1,5 +1,7 @@
obj-y = base.o
obj-$(CONFIG_OF_FLATTREE) += fdt.o
+obj-$(CONFIG_OF_ADDRESS) += address.o
+obj-$(CONFIG_OF_IRQ) += irq.o
obj-$(CONFIG_OF_DEVICE) += device.o platform.o
obj-$(CONFIG_OF_GPIO) += gpio.o
obj-$(CONFIG_OF_I2C) += of_i2c.o
diff --git a/drivers/of/address.c b/drivers/of/address.c
new file mode 100644
index 00000000000..fcadb726d4f
--- /dev/null
+++ b/drivers/of/address.c
@@ -0,0 +1,595 @@
+
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/pci_regs.h>
+#include <linux/string.h>
+
+/* Max address size we deal with */
+#define OF_MAX_ADDR_CELLS 4
+#define OF_CHECK_COUNTS(na, ns) ((na) > 0 && (na) <= OF_MAX_ADDR_CELLS && \
+ (ns) > 0)
+
+static struct of_bus *of_match_bus(struct device_node *np);
+static int __of_address_to_resource(struct device_node *dev, const u32 *addrp,
+ u64 size, unsigned int flags,
+ struct resource *r);
+
+/* Debug utility */
+#ifdef DEBUG
+static void of_dump_addr(const char *s, const u32 *addr, int na)
+{
+ printk(KERN_DEBUG "%s", s);
+ while (na--)
+ printk(" %08x", be32_to_cpu(*(addr++)));
+ printk("\n");
+}
+#else
+static void of_dump_addr(const char *s, const u32 *addr, int na) { }
+#endif
+
+/* Callbacks for bus specific translators */
+struct of_bus {
+ const char *name;
+ const char *addresses;
+ int (*match)(struct device_node *parent);
+ void (*count_cells)(struct device_node *child,
+ int *addrc, int *sizec);
+ u64 (*map)(u32 *addr, const u32 *range,
+ int na, int ns, int pna);
+ int (*translate)(u32 *addr, u64 offset, int na);
+ unsigned int (*get_flags)(const u32 *addr);
+};
+
+/*
+ * Default translator (generic bus)
+ */
+
+static void of_bus_default_count_cells(struct device_node *dev,
+ int *addrc, int *sizec)
+{
+ if (addrc)
+ *addrc = of_n_addr_cells(dev);
+ if (sizec)
+ *sizec = of_n_size_cells(dev);
+}
+
+static u64 of_bus_default_map(u32 *addr, const u32 *range,
+ int na, int ns, int pna)
+{
+ u64 cp, s, da;
+
+ cp = of_read_number(range, na);
+ s = of_read_number(range + na + pna, ns);
+ da = of_read_number(addr, na);
+
+ pr_debug("OF: default map, cp=%llx, s=%llx, da=%llx\n",
+ (unsigned long long)cp, (unsigned long long)s,
+ (unsigned long long)da);
+
+ if (da < cp || da >= (cp + s))
+ return OF_BAD_ADDR;
+ return da - cp;
+}
+
+static int of_bus_default_translate(u32 *addr, u64 offset, int na)
+{
+ u64 a = of_read_number(addr, na);
+ memset(addr, 0, na * 4);
+ a += offset;
+ if (na > 1)
+ addr[na - 2] = cpu_to_be32(a >> 32);
+ addr[na - 1] = cpu_to_be32(a & 0xffffffffu);
+
+ return 0;
+}
+
+static unsigned int of_bus_default_get_flags(const u32 *addr)
+{
+ return IORESOURCE_MEM;
+}
+
+#ifdef CONFIG_PCI
+/*
+ * PCI bus specific translator
+ */
+
+static int of_bus_pci_match(struct device_node *np)
+{
+ /* "vci" is for the /chaos bridge on 1st-gen PCI powermacs */
+ return !strcmp(np->type, "pci") || !strcmp(np->type, "vci");
+}
+
+static void of_bus_pci_count_cells(struct device_node *np,
+ int *addrc, int *sizec)
+{
+ if (addrc)
+ *addrc = 3;
+ if (sizec)
+ *sizec = 2;
+}
+
+static unsigned int of_bus_pci_get_flags(const u32 *addr)
+{
+ unsigned int flags = 0;
+ u32 w = addr[0];
+
+ switch((w >> 24) & 0x03) {
+ case 0x01:
+ flags |= IORESOURCE_IO;
+ break;
+ case 0x02: /* 32 bits */
+ case 0x03: /* 64 bits */
+ flags |= IORESOURCE_MEM;
+ break;
+ }
+ if (w & 0x40000000)
+ flags |= IORESOURCE_PREFETCH;
+ return flags;
+}
+
+static u64 of_bus_pci_map(u32 *addr, const u32 *range, int na, int ns, int pna)
+{
+ u64 cp, s, da;
+ unsigned int af, rf;
+
+ af = of_bus_pci_get_flags(addr);
+ rf = of_bus_pci_get_flags(range);
+
+ /* Check address type match */
+ if ((af ^ rf) & (IORESOURCE_MEM | IORESOURCE_IO))
+ return OF_BAD_ADDR;
+
+ /* Read address values, skipping high cell */
+ cp = of_read_number(range + 1, na - 1);
+ s = of_read_number(range + na + pna, ns);
+ da = of_read_number(addr + 1, na - 1);
+
+ pr_debug("OF: PCI map, cp=%llx, s=%llx, da=%llx\n",
+ (unsigned long long)cp, (unsigned long long)s,
+ (unsigned long long)da);
+
+ if (da < cp || da >= (cp + s))
+ return OF_BAD_ADDR;
+ return da - cp;
+}
+
+static int of_bus_pci_translate(u32 *addr, u64 offset, int na)
+{
+ return of_bus_default_translate(addr + 1, offset, na - 1);
+}
+
+const u32 *of_get_pci_address(struct device_node *dev, int bar_no, u64 *size,
+ unsigned int *flags)
+{
+ const u32 *prop;
+ unsigned int psize;
+ struct device_node *parent;
+ struct of_bus *bus;
+ int onesize, i, na, ns;
+
+ /* Get parent & match bus type */
+ parent = of_get_parent(dev);
+ if (parent == NULL)
+ return NULL;
+ bus = of_match_bus(parent);
+ if (strcmp(bus->name, "pci")) {
+ of_node_put(parent);
+ return NULL;
+ }
+ bus->count_cells(dev, &na, &ns);
+ of_node_put(parent);
+ if (!OF_CHECK_COUNTS(na, ns))
+ return NULL;
+
+ /* Get "reg" or "assigned-addresses" property */
+ prop = of_get_property(dev, bus->addresses, &psize);
+ if (prop == NULL)
+ return NULL;
+ psize /= 4;
+
+ onesize = na + ns;
+ for (i = 0; psize >= onesize; psize -= onesize, prop += onesize, i++) {
+ u32 val = be32_to_cpu(prop[0]);
+ if ((val & 0xff) == ((bar_no * 4) + PCI_BASE_ADDRESS_0)) {
+ if (size)
+ *size = of_read_number(prop + na, ns);
+ if (flags)
+ *flags = bus->get_flags(prop);
+ return prop;
+ }
+ }
+ return NULL;
+}
+EXPORT_SYMBOL(of_get_pci_address);
+
+int of_pci_address_to_resource(struct device_node *dev, int bar,
+ struct resource *r)
+{
+ const u32 *addrp;
+ u64 size;
+ unsigned int flags;
+
+ addrp = of_get_pci_address(dev, bar, &size, &flags);
+ if (addrp == NULL)
+ return -EINVAL;
+ return __of_address_to_resource(dev, addrp, size, flags, r);
+}
+EXPORT_SYMBOL_GPL(of_pci_address_to_resource);
+#endif /* CONFIG_PCI */
+
+/*
+ * ISA bus specific translator
+ */
+
+static int of_bus_isa_match(struct device_node *np)
+{
+ return !strcmp(np->name, "isa");
+}
+
+static void of_bus_isa_count_cells(struct device_node *child,
+ int *addrc, int *sizec)
+{
+ if (addrc)
+ *addrc = 2;
+ if (sizec)
+ *sizec = 1;
+}
+
+static u64 of_bus_isa_map(u32 *addr, const u32 *range, int na, int ns, int pna)
+{
+ u64 cp, s, da;
+
+ /* Check address type match */
+ if ((addr[0] ^ range[0]) & 0x00000001)
+ return OF_BAD_ADDR;
+
+ /* Read address values, skipping high cell */
+ cp = of_read_number(range + 1, na - 1);
+ s = of_read_number(range + na + pna, ns);
+ da = of_read_number(addr + 1, na - 1);
+
+ pr_debug("OF: ISA map, cp=%llx, s=%llx, da=%llx\n",
+ (unsigned long long)cp, (unsigned long long)s,
+ (unsigned long long)da);
+
+ if (da < cp || da >= (cp + s))
+ return OF_BAD_ADDR;
+ return da - cp;
+}
+
+static int of_bus_isa_translate(u32 *addr, u64 offset, int na)
+{
+ return of_bus_default_translate(addr + 1, offset, na - 1);
+}
+
+static unsigned int of_bus_isa_get_flags(const u32 *addr)
+{
+ unsigned int flags = 0;
+ u32 w = addr[0];
+
+ if (w & 1)
+ flags |= IORESOURCE_IO;
+ else
+ flags |= IORESOURCE_MEM;
+ return flags;
+}
+
+/*
+ * Array of bus specific translators
+ */
+
+static struct of_bus of_busses[] = {
+#ifdef CONFIG_PCI
+ /* PCI */
+ {
+ .name = "pci",
+ .addresses = "assigned-addresses",
+ .match = of_bus_pci_match,
+ .count_cells = of_bus_pci_count_cells,
+ .map = of_bus_pci_map,
+ .translate = of_bus_pci_translate,
+ .get_flags = of_bus_pci_get_flags,
+ },
+#endif /* CONFIG_PCI */
+ /* ISA */
+ {
+ .name = "isa",
+ .addresses = "reg",
+ .match = of_bus_isa_match,
+ .count_cells = of_bus_isa_count_cells,
+ .map = of_bus_isa_map,
+ .translate = of_bus_isa_translate,
+ .get_flags = of_bus_isa_get_flags,
+ },
+ /* Default */
+ {
+ .name = "default",
+ .addresses = "reg",
+ .match = NULL,
+ .count_cells = of_bus_default_count_cells,
+ .map = of_bus_default_map,
+ .translate = of_bus_default_translate,
+ .get_flags = of_bus_default_get_flags,
+ },
+};
+
+static struct of_bus *of_match_bus(struct device_node *np)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(of_busses); i++)
+ if (!of_busses[i].match || of_busses[i].match(np))
+ return &of_busses[i];
+ BUG();
+ return NULL;
+}
+
+static int of_translate_one(struct device_node *parent, struct of_bus *bus,
+ struct of_bus *pbus, u32 *addr,
+ int na, int ns, int pna, const char *rprop)
+{
+ const u32 *ranges;
+ unsigned int rlen;
+ int rone;
+ u64 offset = OF_BAD_ADDR;
+
+ /* Normally, an absence of a "ranges" property means we are
+ * crossing a non-translatable boundary, and thus the addresses
+ * below the current not cannot be converted to CPU physical ones.
+ * Unfortunately, while this is very clear in the spec, it's not
+ * what Apple understood, and they do have things like /uni-n or
+ * /ht nodes with no "ranges" property and a lot of perfectly
+ * useable mapped devices below them. Thus we treat the absence of
+ * "ranges" as equivalent to an empty "ranges" property which means
+ * a 1:1 translation at that level. It's up to the caller not to try
+ * to translate addresses that aren't supposed to be translated in
+ * the first place. --BenH.
+ *
+ * As far as we know, this damage only exists on Apple machines, so
+ * This code is only enabled on powerpc. --gcl
+ */
+ ranges = of_get_property(parent, rprop, &rlen);
+#if !defined(CONFIG_PPC)
+ if (ranges == NULL) {
+ pr_err("OF: no ranges; cannot translate\n");
+ return 1;
+ }
+#endif /* !defined(CONFIG_PPC) */
+ if (ranges == NULL || rlen == 0) {
+ offset = of_read_number(addr, na);
+ memset(addr, 0, pna * 4);
+ pr_debug("OF: empty ranges; 1:1 translation\n");
+ goto finish;
+ }
+
+ pr_debug("OF: walking ranges...\n");
+
+ /* Now walk through the ranges */
+ rlen /= 4;
+ rone = na + pna + ns;
+ for (; rlen >= rone; rlen -= rone, ranges += rone) {
+ offset = bus->map(addr, ranges, na, ns, pna);
+ if (offset != OF_BAD_ADDR)
+ break;
+ }
+ if (offset == OF_BAD_ADDR) {
+ pr_debug("OF: not found !\n");
+ return 1;
+ }
+ memcpy(addr, ranges + na, 4 * pna);
+
+ finish:
+ of_dump_addr("OF: parent translation for:", addr, pna);
+ pr_debug("OF: with offset: %llx\n", (unsigned long long)offset);
+
+ /* Translate it into parent bus space */
+ return pbus->translate(addr, offset, pna);
+}
+
+/*
+ * Translate an address from the device-tree into a CPU physical address,
+ * this walks up the tree and applies the various bus mappings on the
+ * way.
+ *
+ * Note: We consider that crossing any level with #size-cells == 0 to mean
+ * that translation is impossible (that is we are not dealing with a value
+ * that can be mapped to a cpu physical address). This is not really specified
+ * that way, but this is traditionally the way IBM at least do things
+ */
+u64 __of_translate_address(struct device_node *dev, const u32 *in_addr,
+ const char *rprop)
+{
+ struct device_node *parent = NULL;
+ struct of_bus *bus, *pbus;
+ u32 addr[OF_MAX_ADDR_CELLS];
+ int na, ns, pna, pns;
+ u64 result = OF_BAD_ADDR;
+
+ pr_debug("OF: ** translation for device %s **\n", dev->full_name);
+
+ /* Increase refcount at current level */
+ of_node_get(dev);
+
+ /* Get parent & match bus type */
+ parent = of_get_parent(dev);
+ if (parent == NULL)
+ goto bail;
+ bus = of_match_bus(parent);
+
+ /* Cound address cells & copy address locally */
+ bus->count_cells(dev, &na, &ns);
+ if (!OF_CHECK_COUNTS(na, ns)) {
+ printk(KERN_ERR "prom_parse: Bad cell count for %s\n",
+ dev->full_name);
+ goto bail;
+ }
+ memcpy(addr, in_addr, na * 4);
+
+ pr_debug("OF: bus is %s (na=%d, ns=%d) on %s\n",
+ bus->name, na, ns, parent->full_name);
+ of_dump_addr("OF: translating address:", addr, na);
+
+ /* Translate */
+ for (;;) {
+ /* Switch to parent bus */
+ of_node_put(dev);
+ dev = parent;
+ parent = of_get_parent(dev);
+
+ /* If root, we have finished */
+ if (parent == NULL) {
+ pr_debug("OF: reached root node\n");
+ result = of_read_number(addr, na);
+ break;
+ }
+
+ /* Get new parent bus and counts */
+ pbus = of_match_bus(parent);
+ pbus->count_cells(dev, &pna, &pns);
+ if (!OF_CHECK_COUNTS(pna, pns)) {
+ printk(KERN_ERR "prom_parse: Bad cell count for %s\n",
+ dev->full_name);
+ break;
+ }
+
+ pr_debug("OF: parent bus is %s (na=%d, ns=%d) on %s\n",
+ pbus->name, pna, pns, parent->full_name);
+
+ /* Apply bus translation */
+ if (of_translate_one(dev, bus, pbus, addr, na, ns, pna, rprop))
+ break;
+
+ /* Complete the move up one level */
+ na = pna;
+ ns = pns;
+ bus = pbus;
+
+ of_dump_addr("OF: one level translation:", addr, na);
+ }
+ bail:
+ of_node_put(parent);
+ of_node_put(dev);
+
+ return result;
+}
+
+u64 of_translate_address(struct device_node *dev, const u32 *in_addr)
+{
+ return __of_translate_address(dev, in_addr, "ranges");
+}
+EXPORT_SYMBOL(of_translate_address);
+
+u64 of_translate_dma_address(struct device_node *dev, const u32 *in_addr)
+{
+ return __of_translate_address(dev, in_addr, "dma-ranges");
+}
+EXPORT_SYMBOL(of_translate_dma_address);
+
+const u32 *of_get_address(struct device_node *dev, int index, u64 *size,
+ unsigned int *flags)
+{
+ const u32 *prop;
+ unsigned int psize;
+ struct device_node *parent;
+ struct of_bus *bus;
+ int onesize, i, na, ns;
+
+ /* Get parent & match bus type */
+ parent = of_get_parent(dev);
+ if (parent == NULL)
+ return NULL;
+ bus = of_match_bus(parent);
+ bus->count_cells(dev, &na, &ns);
+ of_node_put(parent);
+ if (!OF_CHECK_COUNTS(na, ns))
+ return NULL;
+
+ /* Get "reg" or "assigned-addresses" property */
+ prop = of_get_property(dev, bus->addresses, &psize);
+ if (prop == NULL)
+ return NULL;
+ psize /= 4;
+
+ onesize = na + ns;
+ for (i = 0; psize >= onesize; psize -= onesize, prop += onesize, i++)
+ if (i == index) {
+ if (size)
+ *size = of_read_number(prop + na, ns);
+ if (flags)
+ *flags = bus->get_flags(prop);
+ return prop;
+ }
+ return NULL;
+}
+EXPORT_SYMBOL(of_get_address);
+
+static int __of_address_to_resource(struct device_node *dev, const u32 *addrp,
+ u64 size, unsigned int flags,
+ struct resource *r)
+{
+ u64 taddr;
+
+ if ((flags & (IORESOURCE_IO | IORESOURCE_MEM)) == 0)
+ return -EINVAL;
+ taddr = of_translate_address(dev, addrp);
+ if (taddr == OF_BAD_ADDR)
+ return -EINVAL;
+ memset(r, 0, sizeof(struct resource));
+ if (flags & IORESOURCE_IO) {
+ unsigned long port;
+ port = pci_address_to_pio(taddr);
+ if (port == (unsigned long)-1)
+ return -EINVAL;
+ r->start = port;
+ r->end = port + size - 1;
+ } else {
+ r->start = taddr;
+ r->end = taddr + size - 1;
+ }
+ r->flags = flags;
+ r->name = dev->full_name;
+ return 0;
+}
+
+/**
+ * of_address_to_resource - Translate device tree address and return as resource
+ *
+ * Note that if your address is a PIO address, the conversion will fail if
+ * the physical address can't be internally converted to an IO token with
+ * pci_address_to_pio(), that is because it's either called to early or it
+ * can't be matched to any host bridge IO space
+ */
+int of_address_to_resource(struct device_node *dev, int index,
+ struct resource *r)
+{
+ const u32 *addrp;
+ u64 size;
+ unsigned int flags;
+
+ addrp = of_get_address(dev, index, &size, &flags);
+ if (addrp == NULL)
+ return -EINVAL;
+ return __of_address_to_resource(dev, addrp, size, flags, r);
+}
+EXPORT_SYMBOL_GPL(of_address_to_resource);
+
+
+/**
+ * of_iomap - Maps the memory mapped IO for a given device_node
+ * @device: the device whose io range will be mapped
+ * @index: index of the io range
+ *
+ * Returns a pointer to the mapped memory
+ */
+void __iomem *of_iomap(struct device_node *np, int index)
+{
+ struct resource res;
+
+ if (of_address_to_resource(np, index, &res))
+ return NULL;
+
+ return ioremap(res.start, 1 + res.end - res.start);
+}
+EXPORT_SYMBOL(of_iomap);
diff --git a/drivers/of/base.c b/drivers/of/base.c
index b5ad9740d8b..aa805250de7 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -545,74 +545,28 @@ struct device_node *of_find_matching_node(struct device_node *from,
EXPORT_SYMBOL(of_find_matching_node);
/**
- * of_modalias_table: Table of explicit compatible ==> modalias mappings
- *
- * This table allows particulare compatible property values to be mapped
- * to modalias strings. This is useful for busses which do not directly
- * understand the OF device tree but are populated based on data contained
- * within the device tree. SPI and I2C are the two current users of this
- * table.
- *
- * In most cases, devices do not need to be listed in this table because
- * the modalias value can be derived directly from the compatible table.
- * However, if for any reason a value cannot be derived, then this table
- * provides a method to override the implicit derivation.
- *
- * At the moment, a single table is used for all bus types because it is
- * assumed that the data size is small and that the compatible values
- * should already be distinct enough to differentiate between SPI, I2C
- * and other devices.
- */
-struct of_modalias_table {
- char *of_device;
- char *modalias;
-};
-static struct of_modalias_table of_modalias_table[] = {
- { "fsl,mcu-mpc8349emitx", "mcu-mpc8349emitx" },
- { "mmc-spi-slot", "mmc_spi" },
-};
-
-/**
* of_modalias_node - Lookup appropriate modalias for a device node
* @node: pointer to a device tree node
* @modalias: Pointer to buffer that modalias value will be copied into
* @len: Length of modalias value
*
- * Based on the value of the compatible property, this routine will determine
- * an appropriate modalias value for a particular device tree node. Two
- * separate methods are attempted to derive a modalias value.
+ * Based on the value of the compatible property, this routine will attempt
+ * to choose an appropriate modalias value for a particular device tree node.
+ * It does this by stripping the manufacturer prefix (as delimited by a ',')
+ * from the first entry in the compatible list property.
*
- * First method is to lookup the compatible value in of_modalias_table.
- * Second is to strip off the manufacturer prefix from the first
- * compatible entry and use the remainder as modalias
- *
- * This routine returns 0 on success
+ * This routine returns 0 on success, <0 on failure.
*/
int of_modalias_node(struct device_node *node, char *modalias, int len)
{
- int i, cplen;
- const char *compatible;
- const char *p;
-
- /* 1. search for exception list entry */
- for (i = 0; i < ARRAY_SIZE(of_modalias_table); i++) {
- compatible = of_modalias_table[i].of_device;
- if (!of_device_is_compatible(node, compatible))
- continue;
- strlcpy(modalias, of_modalias_table[i].modalias, len);
- return 0;
- }
+ const char *compatible, *p;
+ int cplen;
compatible = of_get_property(node, "compatible", &cplen);
- if (!compatible)
+ if (!compatible || strlen(compatible) > cplen)
return -ENODEV;
-
- /* 2. take first compatible entry and strip manufacturer */
p = strchr(compatible, ',');
- if (!p)
- return -ENODEV;
- p++;
- strlcpy(modalias, p, len);
+ strlcpy(modalias, p ? p + 1 : compatible, len);
return 0;
}
EXPORT_SYMBOL_GPL(of_modalias_node);
@@ -651,14 +605,14 @@ EXPORT_SYMBOL(of_find_node_by_phandle);
struct device_node *
of_parse_phandle(struct device_node *np, const char *phandle_name, int index)
{
- const phandle *phandle;
+ const __be32 *phandle;
int size;
phandle = of_get_property(np, phandle_name, &size);
if ((!phandle) || (size < sizeof(*phandle) * (index + 1)))
return NULL;
- return of_find_node_by_phandle(phandle[index]);
+ return of_find_node_by_phandle(be32_to_cpup(phandle + index));
}
EXPORT_SYMBOL(of_parse_phandle);
@@ -714,16 +668,16 @@ int of_parse_phandles_with_args(struct device_node *np, const char *list_name,
while (list < list_end) {
const __be32 *cells;
- const phandle *phandle;
+ phandle phandle;
- phandle = list++;
+ phandle = be32_to_cpup(list++);
args = list;
/* one cell hole in the list = <>; */
- if (!*phandle)
+ if (!phandle)
goto next;
- node = of_find_node_by_phandle(*phandle);
+ node = of_find_node_by_phandle(phandle);
if (!node) {
pr_debug("%s: could not find phandle\n",
np->full_name);
diff --git a/drivers/of/device.c b/drivers/of/device.c
index 7d18f8e0b01..0d8a0644f54 100644
--- a/drivers/of/device.c
+++ b/drivers/of/device.c
@@ -20,13 +20,13 @@
const struct of_device_id *of_match_device(const struct of_device_id *matches,
const struct device *dev)
{
- if (!dev->of_node)
+ if ((!matches) || (!dev->of_node))
return NULL;
return of_match_node(matches, dev->of_node);
}
EXPORT_SYMBOL(of_match_device);
-struct of_device *of_dev_get(struct of_device *dev)
+struct platform_device *of_dev_get(struct platform_device *dev)
{
struct device *tmp;
@@ -34,13 +34,13 @@ struct of_device *of_dev_get(struct of_device *dev)
return NULL;
tmp = get_device(&dev->dev);
if (tmp)
- return to_of_device(tmp);
+ return to_platform_device(tmp);
else
return NULL;
}
EXPORT_SYMBOL(of_dev_get);
-void of_dev_put(struct of_device *dev)
+void of_dev_put(struct platform_device *dev)
{
if (dev)
put_device(&dev->dev);
@@ -50,28 +50,25 @@ EXPORT_SYMBOL(of_dev_put);
static ssize_t devspec_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct of_device *ofdev;
+ struct platform_device *ofdev;
- ofdev = to_of_device(dev);
+ ofdev = to_platform_device(dev);
return sprintf(buf, "%s\n", ofdev->dev.of_node->full_name);
}
static ssize_t name_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct of_device *ofdev;
+ struct platform_device *ofdev;
- ofdev = to_of_device(dev);
+ ofdev = to_platform_device(dev);
return sprintf(buf, "%s\n", ofdev->dev.of_node->name);
}
static ssize_t modalias_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct of_device *ofdev = to_of_device(dev);
- ssize_t len = 0;
-
- len = of_device_get_modalias(ofdev, buf, PAGE_SIZE - 2);
+ ssize_t len = of_device_get_modalias(dev, buf, PAGE_SIZE - 2);
buf[len] = '\n';
buf[len+1] = 0;
return len+1;
@@ -93,20 +90,25 @@ struct device_attribute of_platform_device_attrs[] = {
*/
void of_release_dev(struct device *dev)
{
- struct of_device *ofdev;
+ struct platform_device *ofdev;
- ofdev = to_of_device(dev);
+ ofdev = to_platform_device(dev);
of_node_put(ofdev->dev.of_node);
kfree(ofdev);
}
EXPORT_SYMBOL(of_release_dev);
-int of_device_register(struct of_device *ofdev)
+int of_device_register(struct platform_device *ofdev)
{
BUG_ON(ofdev->dev.of_node == NULL);
device_initialize(&ofdev->dev);
+ /* name and id have to be set so that the platform bus doesn't get
+ * confused on matching */
+ ofdev->name = dev_name(&ofdev->dev);
+ ofdev->id = -1;
+
/* device_add will assume that this device is on the same node as
* the parent. If there is no parent defined, set the node
* explicitly */
@@ -117,25 +119,24 @@ int of_device_register(struct of_device *ofdev)
}
EXPORT_SYMBOL(of_device_register);
-void of_device_unregister(struct of_device *ofdev)
+void of_device_unregister(struct platform_device *ofdev)
{
device_unregister(&ofdev->dev);
}
EXPORT_SYMBOL(of_device_unregister);
-ssize_t of_device_get_modalias(struct of_device *ofdev,
- char *str, ssize_t len)
+ssize_t of_device_get_modalias(struct device *dev, char *str, ssize_t len)
{
const char *compat;
int cplen, i;
ssize_t tsize, csize, repend;
/* Name & Type */
- csize = snprintf(str, len, "of:N%sT%s", ofdev->dev.of_node->name,
- ofdev->dev.of_node->type);
+ csize = snprintf(str, len, "of:N%sT%s", dev->of_node->name,
+ dev->of_node->type);
/* Get compatible property if any */
- compat = of_get_property(ofdev->dev.of_node, "compatible", &cplen);
+ compat = of_get_property(dev->of_node, "compatible", &cplen);
if (!compat)
return csize;
@@ -170,3 +171,51 @@ ssize_t of_device_get_modalias(struct of_device *ofdev,
return tsize;
}
+
+/**
+ * of_device_uevent - Display OF related uevent information
+ */
+int of_device_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+ const char *compat;
+ int seen = 0, cplen, sl;
+
+ if ((!dev) || (!dev->of_node))
+ return -ENODEV;
+
+ if (add_uevent_var(env, "OF_NAME=%s", dev->of_node->name))
+ return -ENOMEM;
+
+ if (add_uevent_var(env, "OF_TYPE=%s", dev->of_node->type))
+ return -ENOMEM;
+
+ /* Since the compatible field can contain pretty much anything
+ * it's not really legal to split it out with commas. We split it
+ * up using a number of environment variables instead. */
+
+ compat = of_get_property(dev->of_node, "compatible", &cplen);
+ while (compat && *compat && cplen > 0) {
+ if (add_uevent_var(env, "OF_COMPATIBLE_%d=%s", seen, compat))
+ return -ENOMEM;
+
+ sl = strlen(compat) + 1;
+ compat += sl;
+ cplen -= sl;
+ seen++;
+ }
+
+ if (add_uevent_var(env, "OF_COMPATIBLE_N=%d", seen))
+ return -ENOMEM;
+
+ /* modalias is trickier, we add it in 2 steps */
+ if (add_uevent_var(env, "MODALIAS="))
+ return -ENOMEM;
+
+ sl = of_device_get_modalias(dev, &env->buf[env->buflen-1],
+ sizeof(env->buf) - env->buflen);
+ if (sl >= (sizeof(env->buf) - env->buflen))
+ return -ENOMEM;
+ env->buflen += sl;
+
+ return 0;
+}
diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c
index b6987bba855..65da5aec755 100644
--- a/drivers/of/fdt.c
+++ b/drivers/of/fdt.c
@@ -69,9 +69,9 @@ int __init of_scan_flat_dt(int (*it)(unsigned long node,
u32 sz = be32_to_cpup((__be32 *)p);
p += 8;
if (be32_to_cpu(initial_boot_params->version) < 0x10)
- p = _ALIGN(p, sz >= 8 ? 8 : 4);
+ p = ALIGN(p, sz >= 8 ? 8 : 4);
p += sz;
- p = _ALIGN(p, 4);
+ p = ALIGN(p, 4);
continue;
}
if (tag != OF_DT_BEGIN_NODE) {
@@ -80,7 +80,7 @@ int __init of_scan_flat_dt(int (*it)(unsigned long node,
}
depth++;
pathp = (char *)p;
- p = _ALIGN(p + strlen(pathp) + 1, 4);
+ p = ALIGN(p + strlen(pathp) + 1, 4);
if ((*pathp) == '/') {
char *lp, *np;
for (lp = NULL, np = pathp; *np; np++)
@@ -109,7 +109,7 @@ unsigned long __init of_get_flat_dt_root(void)
p += 4;
BUG_ON(be32_to_cpup((__be32 *)p) != OF_DT_BEGIN_NODE);
p += 4;
- return _ALIGN(p + strlen((char *)p) + 1, 4);
+ return ALIGN(p + strlen((char *)p) + 1, 4);
}
/**
@@ -138,7 +138,7 @@ void *__init of_get_flat_dt_prop(unsigned long node, const char *name,
noff = be32_to_cpup((__be32 *)(p + 4));
p += 8;
if (be32_to_cpu(initial_boot_params->version) < 0x10)
- p = _ALIGN(p, sz >= 8 ? 8 : 4);
+ p = ALIGN(p, sz >= 8 ? 8 : 4);
nstr = find_flat_dt_string(noff);
if (nstr == NULL) {
@@ -151,7 +151,7 @@ void *__init of_get_flat_dt_prop(unsigned long node, const char *name,
return (void *)p;
}
p += sz;
- p = _ALIGN(p, 4);
+ p = ALIGN(p, 4);
} while (1);
}
@@ -169,7 +169,7 @@ int __init of_flat_dt_is_compatible(unsigned long node, const char *compat)
if (cp == NULL)
return 0;
while (cplen > 0) {
- if (strncasecmp(cp, compat, strlen(compat)) == 0)
+ if (of_compat_cmp(cp, compat, strlen(compat)) == 0)
return 1;
l = strlen(cp) + 1;
cp += l;
@@ -184,7 +184,7 @@ static void *__init unflatten_dt_alloc(unsigned long *mem, unsigned long size,
{
void *res;
- *mem = _ALIGN(*mem, align);
+ *mem = ALIGN(*mem, align);
res = (void *)*mem;
*mem += size;
@@ -220,7 +220,7 @@ unsigned long __init unflatten_dt_node(unsigned long mem,
*p += 4;
pathp = (char *)*p;
l = allocl = strlen(pathp) + 1;
- *p = _ALIGN(*p + l, 4);
+ *p = ALIGN(*p + l, 4);
/* version 0x10 has a more compact unit name here instead of the full
* path. we accumulate the full path size using "fpsize", we'll rebuild
@@ -299,7 +299,7 @@ unsigned long __init unflatten_dt_node(unsigned long mem,
noff = be32_to_cpup((__be32 *)((*p) + 4));
*p += 8;
if (be32_to_cpu(initial_boot_params->version) < 0x10)
- *p = _ALIGN(*p, sz >= 8 ? 8 : 4);
+ *p = ALIGN(*p, sz >= 8 ? 8 : 4);
pname = find_flat_dt_string(noff);
if (pname == NULL) {
@@ -320,20 +320,20 @@ unsigned long __init unflatten_dt_node(unsigned long mem,
if ((strcmp(pname, "phandle") == 0) ||
(strcmp(pname, "linux,phandle") == 0)) {
if (np->phandle == 0)
- np->phandle = *((u32 *)*p);
+ np->phandle = be32_to_cpup((__be32*)*p);
}
/* And we process the "ibm,phandle" property
* used in pSeries dynamic device tree
* stuff */
if (strcmp(pname, "ibm,phandle") == 0)
- np->phandle = *((u32 *)*p);
+ np->phandle = be32_to_cpup((__be32 *)*p);
pp->name = pname;
pp->length = sz;
pp->value = (void *)*p;
*prev_pp = pp;
prev_pp = &pp->next;
}
- *p = _ALIGN((*p) +