diff options
Diffstat (limited to 'drivers/of')
-rw-r--r-- | drivers/of/Kconfig | 5 | ||||
-rw-r--r-- | drivers/of/Makefile | 1 | ||||
-rw-r--r-- | drivers/of/address.c | 2 | ||||
-rw-r--r-- | drivers/of/base.c | 4 | ||||
-rw-r--r-- | drivers/of/device.c | 27 | ||||
-rw-r--r-- | drivers/of/fdt.c | 2 | ||||
-rw-r--r-- | drivers/of/irq.c | 39 | ||||
-rw-r--r-- | drivers/of/of_i2c.c | 1 | ||||
-rw-r--r-- | drivers/of/pdt.c | 276 | ||||
-rw-r--r-- | drivers/of/platform.c | 34 |
10 files changed, 347 insertions, 44 deletions
diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig index 6acbff389ab..aa675ebd8eb 100644 --- a/drivers/of/Kconfig +++ b/drivers/of/Kconfig @@ -4,7 +4,7 @@ config DTC config OF bool -menu "Flattened Device Tree and Open Firmware support" +menu "Device Tree and Open Firmware support" depends on OF config PROC_DEVICETREE @@ -19,6 +19,9 @@ config OF_FLATTREE bool select DTC +config OF_PROMTREE + bool + config OF_DYNAMIC def_bool y depends on PPC_OF diff --git a/drivers/of/Makefile b/drivers/of/Makefile index 0052c405463..7888155bea0 100644 --- a/drivers/of/Makefile +++ b/drivers/of/Makefile @@ -1,5 +1,6 @@ obj-y = base.o 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_DEVICE) += device.o platform.o diff --git a/drivers/of/address.c b/drivers/of/address.c index fcadb726d4f..3a1c7e70b19 100644 --- a/drivers/of/address.c +++ b/drivers/of/address.c @@ -163,7 +163,7 @@ static int of_bus_pci_translate(u32 *addr, u64 offset, int na) const u32 *of_get_pci_address(struct device_node *dev, int bar_no, u64 *size, unsigned int *flags) { - const u32 *prop; + const __be32 *prop; unsigned int psize; struct device_node *parent; struct of_bus *bus; diff --git a/drivers/of/base.c b/drivers/of/base.c index aa805250de7..710b53bfac6 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -33,7 +33,7 @@ DEFINE_RWLOCK(devtree_lock); int of_n_addr_cells(struct device_node *np) { - const int *ip; + const __be32 *ip; do { if (np->parent) @@ -49,7 +49,7 @@ EXPORT_SYMBOL(of_n_addr_cells); int of_n_size_cells(struct device_node *np) { - const int *ip; + const __be32 *ip; do { if (np->parent) diff --git a/drivers/of/device.c b/drivers/of/device.c index 92de0eb74ae..45d86530799 100644 --- a/drivers/of/device.c +++ b/drivers/of/device.c @@ -81,29 +81,10 @@ struct device_attribute of_platform_device_attrs[] = { __ATTR_NULL }; -/** - * of_release_dev - free an of device structure when all users of it are finished. - * @dev: device that's been disconnected - * - * Will be called only by the device core when all users of this of device are - * done. - */ -void of_release_dev(struct device *dev) -{ - struct platform_device *ofdev; - - ofdev = to_platform_device(dev); - of_node_put(ofdev->dev.of_node); - kfree(ofdev); -} -EXPORT_SYMBOL(of_release_dev); - -int of_device_register(struct platform_device *ofdev) +int of_device_add(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); @@ -117,6 +98,12 @@ int of_device_register(struct platform_device *ofdev) return device_add(&ofdev->dev); } + +int of_device_register(struct platform_device *pdev) +{ + device_initialize(&pdev->dev); + return of_device_add(pdev); +} EXPORT_SYMBOL(of_device_register); void of_device_unregister(struct platform_device *ofdev) diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 65da5aec755..c1360e02f92 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -533,8 +533,6 @@ int __init early_init_dt_scan_chosen(unsigned long node, const char *uname, strlcpy(cmd_line, CONFIG_CMDLINE, COMMAND_LINE_SIZE); #endif /* CONFIG_CMDLINE */ - early_init_dt_scan_chosen_arch(node); - pr_debug("Command line is: %s\n", cmd_line); /* break now */ diff --git a/drivers/of/irq.c b/drivers/of/irq.c index 6e595e5a397..75b0d3cb767 100644 --- a/drivers/of/irq.c +++ b/drivers/of/irq.c @@ -24,6 +24,11 @@ #include <linux/of_irq.h> #include <linux/string.h> +/* For archs that don't support NO_IRQ (such as x86), provide a dummy value */ +#ifndef NO_IRQ +#define NO_IRQ 0 +#endif + /** * irq_of_parse_and_map - Parse and map an interrupt into linux virq space * @device: Device node of the device whose interrupt is to be mapped @@ -347,3 +352,37 @@ int of_irq_to_resource(struct device_node *dev, int index, struct resource *r) return irq; } EXPORT_SYMBOL_GPL(of_irq_to_resource); + +/** + * of_irq_count - Count the number of IRQs a node uses + * @dev: pointer to device tree node + */ +int of_irq_count(struct device_node *dev) +{ + int nr = 0; + + while (of_irq_to_resource(dev, nr, NULL) != NO_IRQ) + nr++; + + return nr; +} + +/** + * of_irq_to_resource_table - Fill in resource table with node's IRQ info + * @dev: pointer to device tree node + * @res: array of resources to fill in + * @nr_irqs: the number of IRQs (and upper bound for num of @res elements) + * + * Returns the size of the filled in table (up to @nr_irqs). + */ +int of_irq_to_resource_table(struct device_node *dev, struct resource *res, + int nr_irqs) +{ + int i; + + for (i = 0; i < nr_irqs; i++, res++) + if (of_irq_to_resource(dev, i, res) == NO_IRQ) + break; + + return i; +} diff --git a/drivers/of/of_i2c.c b/drivers/of/of_i2c.c index 0a694debd22..c85d3c7421f 100644 --- a/drivers/of/of_i2c.c +++ b/drivers/of/of_i2c.c @@ -12,6 +12,7 @@ */ #include <linux/i2c.h> +#include <linux/irq.h> #include <linux/of.h> #include <linux/of_i2c.h> #include <linux/of_irq.h> diff --git a/drivers/of/pdt.c b/drivers/of/pdt.c new file mode 100644 index 00000000000..28295d0a50f --- /dev/null +++ b/drivers/of/pdt.c @@ -0,0 +1,276 @@ +/* pdt.c: OF PROM device tree support code. + * + * Paul Mackerras August 1996. + * Copyright (C) 1996-2005 Paul Mackerras. + * + * Adapted for 64bit PowerPC by Dave Engebretsen and Peter Bergner. + * {engebret|bergner}@us.ibm.com + * + * Adapted for sparc by David S. Miller davem@davemloft.net + * Adapted for multiple architectures by Andres Salomon <dilinger@queued.net> + * + * 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/kernel.h> +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/mutex.h> +#include <linux/slab.h> +#include <linux/of.h> +#include <linux/of_pdt.h> +#include <asm/prom.h> + +static struct of_pdt_ops *of_pdt_prom_ops __initdata; + +void __initdata (*of_pdt_build_more)(struct device_node *dp, + struct device_node ***nextp); + +#if defined(CONFIG_SPARC) +unsigned int of_pdt_unique_id __initdata; + +#define of_pdt_incr_unique_id(p) do { \ + (p)->unique_id = of_pdt_unique_id++; \ +} while (0) + +static inline const char *of_pdt_node_name(struct device_node *dp) +{ + return dp->path_component_name; +} + +#else + +static inline void of_pdt_incr_unique_id(void *p) { } +static inline void irq_trans_init(struct device_node *dp) { } + +static inline const char *of_pdt_node_name(struct device_node *dp) +{ + return dp->name; +} + +#endif /* !CONFIG_SPARC */ + +static struct property * __init of_pdt_build_one_prop(phandle node, char *prev, + char *special_name, + void *special_val, + int special_len) +{ + static struct property *tmp = NULL; + struct property *p; + int err; + + if (tmp) { + p = tmp; + memset(p, 0, sizeof(*p) + 32); + tmp = NULL; + } else { + p = prom_early_alloc(sizeof(struct property) + 32); + of_pdt_incr_unique_id(p); + } + + p->name = (char *) (p + 1); + if (special_name) { + strcpy(p->name, special_name); + p->length = special_len; + p->value = prom_early_alloc(special_len); + memcpy(p->value, special_val, special_len); + } else { + err = of_pdt_prom_ops->nextprop(node, prev, p->name); + if (err) { + tmp = p; + return NULL; + } + p->length = of_pdt_prom_ops->getproplen(node, p->name); + if (p->length <= 0) { + p->length = 0; + } else { + int len; + + p->value = prom_early_alloc(p->length + 1); + len = of_pdt_prom_ops->getproperty(node, p->name, + p->value, p->length); + if (len <= 0) + p->length = 0; + ((unsigned char *)p->value)[p->length] = '\0'; + } + } + return p; +} + +static struct property * __init of_pdt_build_prop_list(phandle node) +{ + struct property *head, *tail; + + head = tail = of_pdt_build_one_prop(node, NULL, + ".node", &node, sizeof(node)); + + tail->next = of_pdt_build_one_prop(node, NULL, NULL, NULL, 0); + tail = tail->next; + while(tail) { + tail->next = of_pdt_build_one_prop(node, tail->name, + NULL, NULL, 0); + tail = tail->next; + } + + return head; +} + +static char * __init of_pdt_get_one_property(phandle node, const char *name) +{ + char *buf = "<NULL>"; + int len; + + len = of_pdt_prom_ops->getproplen(node, name); + if (len > 0) { + buf = prom_early_alloc(len); + len = of_pdt_prom_ops->getproperty(node, name, buf, len); + } + + return buf; +} + +static char * __init of_pdt_try_pkg2path(phandle node) +{ + char *res, *buf = NULL; + int len; + + if (!of_pdt_prom_ops->pkg2path) + return NULL; + + if (of_pdt_prom_ops->pkg2path(node, buf, 0, &len)) + return NULL; + buf = prom_early_alloc(len + 1); + if (of_pdt_prom_ops->pkg2path(node, buf, len, &len)) { + pr_err("%s: package-to-path failed\n", __func__); + return NULL; + } + + res = strrchr(buf, '/'); + if (!res) { + pr_err("%s: couldn't find / in %s\n", __func__, buf); + return NULL; + } + return res+1; +} + +/* + * When fetching the node's name, first try using package-to-path; if + * that fails (either because the arch hasn't supplied a PROM callback, + * or some other random failure), fall back to just looking at the node's + * 'name' property. + */ +static char * __init of_pdt_build_name(phandle node) +{ + char *buf; + + buf = of_pdt_try_pkg2path(node); + if (!buf) + buf = of_pdt_get_one_property(node, "name"); + + return buf; +} + +static struct device_node * __init of_pdt_create_node(phandle node, + struct device_node *parent) +{ + struct device_node *dp; + + if (!node) + return NULL; + + dp = prom_early_alloc(sizeof(*dp)); + of_pdt_incr_unique_id(dp); + dp->parent = parent; + + kref_init(&dp->kref); + + dp->name = of_pdt_build_name(node); + dp->type = of_pdt_get_one_property(node, "device_type"); + dp->phandle = node; + + dp->properties = of_pdt_build_prop_list(node); + + irq_trans_init(dp); + + return dp; +} + +static char * __init of_pdt_build_full_name(struct device_node *dp) +{ + int len, ourlen, plen; + char *n; + + plen = strlen(dp->parent->full_name); + ourlen = strlen(of_pdt_node_name(dp)); + len = ourlen + plen + 2; + + n = prom_early_alloc(len); + strcpy(n, dp->parent->full_name); + if (!of_node_is_root(dp->parent)) { + strcpy(n + plen, "/"); + plen++; + } + strcpy(n + plen, of_pdt_node_name(dp)); + + return n; +} + +static struct device_node * __init of_pdt_build_tree(struct device_node *parent, + phandle node, + struct device_node ***nextp) +{ + struct device_node *ret = NULL, *prev_sibling = NULL; + struct device_node *dp; + + while (1) { + dp = of_pdt_create_node(node, parent); + if (!dp) + break; + + if (prev_sibling) + prev_sibling->sibling = dp; + + if (!ret) + ret = dp; + prev_sibling = dp; + + *(*nextp) = dp; + *nextp = &dp->allnext; + +#if defined(CONFIG_SPARC) + dp->path_component_name = build_path_component(dp); +#endif + dp->full_name = of_pdt_build_full_name(dp); + + dp->child = of_pdt_build_tree(dp, + of_pdt_prom_ops->getchild(node), nextp); + + if (of_pdt_build_more) + of_pdt_build_more(dp, nextp); + + node = of_pdt_prom_ops->getsibling(node); + } + + return ret; +} + +void __init of_pdt_build_devicetree(phandle root_node, struct of_pdt_ops *ops) +{ + struct device_node **nextp; + + BUG_ON(!ops); + of_pdt_prom_ops = ops; + + allnodes = of_pdt_create_node(root_node, NULL); +#if defined(CONFIG_SPARC) + allnodes->path_component_name = ""; +#endif + allnodes->full_name = "/"; + + nextp = &allnodes->allnext; + allnodes->child = of_pdt_build_tree(allnodes, + of_pdt_prom_ops->getchild(allnodes->phandle), &nextp); +} diff --git a/drivers/of/platform.c b/drivers/of/platform.c index bb72223c22a..5b4a07f1220 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -584,34 +584,33 @@ struct platform_device *of_device_alloc(struct device_node *np, struct device *parent) { struct platform_device *dev; - int rc, i, num_reg = 0, num_irq = 0; + int rc, i, num_reg = 0, num_irq; struct resource *res, temp_res; - /* First count how many resources are needed */ - while (of_address_to_resource(np, num_reg, &temp_res) == 0) - num_reg++; - while (of_irq_to_resource(np, num_irq, &temp_res) != NO_IRQ) - num_irq++; - - /* Allocate memory for both the struct device and the resource table */ - dev = kzalloc(sizeof(*dev) + (sizeof(*res) * (num_reg + num_irq)), - GFP_KERNEL); + dev = platform_device_alloc("", -1); if (!dev) return NULL; - res = (struct resource *) &dev[1]; + + /* count the io and irq resources */ + while (of_address_to_resource(np, num_reg, &temp_res) == 0) + num_reg++; + num_irq = of_irq_count(np); /* Populate the resource table */ if (num_irq || num_reg) { + res = kzalloc(sizeof(*res) * (num_irq + num_reg), GFP_KERNEL); + if (!res) { + platform_device_put(dev); + return NULL; + } + dev->num_resources = num_reg + num_irq; dev->resource = res; for (i = 0; i < num_reg; i++, res++) { rc = of_address_to_resource(np, i, res); WARN_ON(rc); } - for (i = 0; i < num_irq; i++, res++) { - rc = of_irq_to_resource(np, i, res); - WARN_ON(rc == NO_IRQ); - } + WARN_ON(of_irq_to_resource_table(np, res, num_irq) != num_irq); } dev->dev.of_node = of_node_get(np); @@ -619,7 +618,6 @@ struct platform_device *of_device_alloc(struct device_node *np, dev->dev.dma_mask = &dev->archdata.dma_mask; #endif dev->dev.parent = parent; - dev->dev.release = of_release_dev; if (bus_id) dev_set_name(&dev->dev, "%s", bus_id); @@ -657,8 +655,8 @@ struct platform_device *of_platform_device_create(struct device_node *np, * to do such, possibly using a device notifier */ - if (of_device_register(dev) != 0) { - of_device_free(dev); + if (of_device_add(dev) != 0) { + platform_device_put(dev); return NULL; } |