diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/block/xsysace.c | 98 | ||||
-rw-r--r-- | drivers/of/address.c | 18 | ||||
-rw-r--r-- | drivers/of/base.c | 65 | ||||
-rw-r--r-- | drivers/of/platform.c | 196 | ||||
-rw-r--r-- | drivers/tty/serial/of_serial.c | 43 |
5 files changed, 313 insertions, 107 deletions
diff --git a/drivers/block/xsysace.c b/drivers/block/xsysace.c index 6c7fd7db6dff..fb1975d82a73 100644 --- a/drivers/block/xsysace.c +++ b/drivers/block/xsysace.c @@ -1155,12 +1155,19 @@ static int __devinit ace_probe(struct platform_device *dev) { resource_size_t physaddr = 0; int bus_width = ACE_BUS_WIDTH_16; /* FIXME: should not be hard coded */ - int id = dev->id; + u32 id = dev->id; int irq = NO_IRQ; int i; dev_dbg(&dev->dev, "ace_probe(%p)\n", dev); + /* device id and bus width */ + of_property_read_u32(dev->dev.of_node, "port-number", &id); + if (id < 0) + id = 0; + if (of_find_property(dev->dev.of_node, "8-bit", NULL)) + bus_width = ACE_BUS_WIDTH_8; + for (i = 0; i < dev->num_resources; i++) { if (dev->resource[i].flags & IORESOURCE_MEM) physaddr = dev->resource[i].start; @@ -1181,57 +1188,7 @@ static int __devexit ace_remove(struct platform_device *dev) return 0; } -static struct platform_driver ace_platform_driver = { - .probe = ace_probe, - .remove = __devexit_p(ace_remove), - .driver = { - .owner = THIS_MODULE, - .name = "xsysace", - }, -}; - -/* --------------------------------------------------------------------- - * OF_Platform Bus Support - */ - #if defined(CONFIG_OF) -static int __devinit ace_of_probe(struct platform_device *op) -{ - struct resource res; - resource_size_t physaddr; - const u32 *id; - int irq, bus_width, rc; - - /* device id */ - id = of_get_property(op->dev.of_node, "port-number", NULL); - - /* physaddr */ - rc = of_address_to_resource(op->dev.of_node, 0, &res); - if (rc) { - dev_err(&op->dev, "invalid address\n"); - return rc; - } - physaddr = res.start; - - /* irq */ - irq = irq_of_parse_and_map(op->dev.of_node, 0); - - /* bus width */ - bus_width = ACE_BUS_WIDTH_16; - if (of_find_property(op->dev.of_node, "8-bit", NULL)) - bus_width = ACE_BUS_WIDTH_8; - - /* Call the bus-independent setup code */ - return ace_alloc(&op->dev, id ? be32_to_cpup(id) : 0, - physaddr, irq, bus_width); -} - -static int __devexit ace_of_remove(struct platform_device *op) -{ - ace_free(&op->dev); - return 0; -} - /* Match table for of_platform binding */ static const struct of_device_id ace_of_match[] __devinitconst = { { .compatible = "xlnx,opb-sysace-1.00.b", }, @@ -1241,34 +1198,20 @@ static const struct of_device_id ace_of_match[] __devinitconst = { {}, }; MODULE_DEVICE_TABLE(of, ace_of_match); +#else /* CONFIG_OF */ +#define ace_of_match NULL +#endif /* CONFIG_OF */ -static struct platform_driver ace_of_driver = { - .probe = ace_of_probe, - .remove = __devexit_p(ace_of_remove), +static struct platform_driver ace_platform_driver = { + .probe = ace_probe, + .remove = __devexit_p(ace_remove), .driver = { - .name = "xsysace", .owner = THIS_MODULE, + .name = "xsysace", .of_match_table = ace_of_match, }, }; -/* Registration helpers to keep the number of #ifdefs to a minimum */ -static inline int __init ace_of_register(void) -{ - pr_debug("xsysace: registering OF binding\n"); - return platform_driver_register(&ace_of_driver); -} - -static inline void __exit ace_of_unregister(void) -{ - platform_driver_unregister(&ace_of_driver); -} -#else /* CONFIG_OF */ -/* CONFIG_OF not enabled; do nothing helpers */ -static inline int __init ace_of_register(void) { return 0; } -static inline void __exit ace_of_unregister(void) { } -#endif /* CONFIG_OF */ - /* --------------------------------------------------------------------- * Module init/exit routines */ @@ -1282,11 +1225,6 @@ static int __init ace_init(void) goto err_blk; } - rc = ace_of_register(); - if (rc) - goto err_of; - - pr_debug("xsysace: registering platform binding\n"); rc = platform_driver_register(&ace_platform_driver); if (rc) goto err_plat; @@ -1295,21 +1233,17 @@ static int __init ace_init(void) return 0; err_plat: - ace_of_unregister(); -err_of: unregister_blkdev(ace_major, "xsysace"); err_blk: printk(KERN_ERR "xsysace: registration failed; err=%i\n", rc); return rc; } +module_init(ace_init); static void __exit ace_exit(void) { pr_debug("Unregistering Xilinx SystemACE driver\n"); platform_driver_unregister(&ace_platform_driver); - ace_of_unregister(); unregister_blkdev(ace_major, "xsysace"); } - -module_init(ace_init); module_exit(ace_exit); diff --git a/drivers/of/address.c b/drivers/of/address.c index b4559c58c095..da1f4b9605df 100644 --- a/drivers/of/address.c +++ b/drivers/of/address.c @@ -577,6 +577,24 @@ int of_address_to_resource(struct device_node *dev, int index, } EXPORT_SYMBOL_GPL(of_address_to_resource); +struct device_node *of_find_matching_node_by_address(struct device_node *from, + const struct of_device_id *matches, + u64 base_address) +{ + struct device_node *dn = of_find_matching_node(from, matches); + struct resource res; + + while (dn) { + if (of_address_to_resource(dn, 0, &res)) + continue; + if (res.start == base_address) + return dn; + dn = of_find_matching_node(dn, matches); + } + + return NULL; +} + /** * of_iomap - Maps the memory mapped IO for a given device_node diff --git a/drivers/of/base.c b/drivers/of/base.c index 632ebae7f17a..02ed36719def 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -596,6 +596,71 @@ struct device_node *of_find_node_by_phandle(phandle handle) EXPORT_SYMBOL(of_find_node_by_phandle); /** + * of_property_read_u32_array - Find and read an array of 32 bit integers + * from a property. + * + * @np: device node from which the property value is to be read. + * @propname: name of the property to be searched. + * @out_value: pointer to return value, modified only if return value is 0. + * + * Search for a property in a device node and read 32-bit value(s) from + * it. Returns 0 on success, -EINVAL if the property does not exist, + * -ENODATA if property does not have a value, and -EOVERFLOW if the + * property data isn't large enough. + * + * The out_value is modified only if a valid u32 value can be decoded. + */ +int of_property_read_u32_array(const struct device_node *np, char *propname, + u32 *out_values, size_t sz) +{ + struct property *prop = of_find_property(np, propname, NULL); + const __be32 *val; + + if (!prop) + return -EINVAL; + if (!prop->value) + return -ENODATA; + if ((sz * sizeof(*out_values)) > prop->length) + return -EOVERFLOW; + + val = prop->value; + while (sz--) + *out_values++ = be32_to_cpup(val++); + return 0; +} +EXPORT_SYMBOL_GPL(of_property_read_u32_array); + +/** + * of_property_read_string - Find and read a string from a property + * @np: device node from which the property value is to be read. + * @propname: name of the property to be searched. + * @out_string: pointer to null terminated return string, modified only if + * return value is 0. + * + * Search for a property in a device tree node and retrieve a null + * terminated string value (pointer to data, not a copy). Returns 0 on + * success, -EINVAL if the property does not exist, -ENODATA if property + * does not have a value, and -EILSEQ if the string is not null-terminated + * within the length of the property data. + * + * The out_string pointer is modified only if a valid string can be decoded. + */ +int of_property_read_string(struct device_node *np, char *propname, + const char **out_string) +{ + struct property *prop = of_find_property(np, propname, NULL); + if (!prop) + return -EINVAL; + if (!prop->value) + return -ENODATA; + if (strnlen(prop->value, prop->length) >= prop->length) + return -EILSEQ; + *out_string = prop->value; + return 0; +} +EXPORT_SYMBOL_GPL(of_property_read_string); + +/** * of_parse_phandle - Resolve a phandle property to a device_node pointer * @np: Pointer to device node holding phandle property * @phandle_name: Name of property holding a phandle value diff --git a/drivers/of/platform.c b/drivers/of/platform.c index 63d3cb73bdb9..e75af391e286 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -13,6 +13,7 @@ */ #include <linux/errno.h> #include <linux/module.h> +#include <linux/amba/bus.h> #include <linux/device.h> #include <linux/dma-mapping.h> #include <linux/slab.h> @@ -22,6 +23,14 @@ #include <linux/of_platform.h> #include <linux/platform_device.h> +const struct of_device_id of_default_bus_match_table[] = { + { .compatible = "simple-bus", }, +#ifdef CONFIG_ARM_AMBA + { .compatible = "arm,amba-bus", }, +#endif /* CONFIG_ARM_AMBA */ + {} /* Empty terminated list */ +}; + static int of_dev_node_match(struct device *dev, void *data) { return dev->of_node == data; @@ -168,17 +177,20 @@ struct platform_device *of_device_alloc(struct device_node *np, EXPORT_SYMBOL(of_device_alloc); /** - * of_platform_device_create - Alloc, initialize and register an of_device + * of_platform_device_create_pdata - Alloc, initialize and register an of_device * @np: pointer to node to create device for * @bus_id: name to assign device + * @platform_data: pointer to populate platform_data pointer with * @parent: Linux device model parent device. * * Returns pointer to created platform device, or NULL if a device was not * registered. Unavailable devices will not get registered. */ -struct platform_device *of_platform_device_create(struct device_node *np, - const char *bus_id, - struct device *parent) +struct platform_device *of_platform_device_create_pdata( + struct device_node *np, + const char *bus_id, + void *platform_data, + struct device *parent) { struct platform_device *dev; @@ -194,6 +206,7 @@ struct platform_device *of_platform_device_create(struct device_node *np, #endif dev->dev.coherent_dma_mask = DMA_BIT_MASK(32); dev->dev.bus = &platform_bus_type; + dev->dev.platform_data = platform_data; /* We do not fill the DMA ops for platform devices by default. * This is currently the responsibility of the platform code @@ -207,8 +220,111 @@ struct platform_device *of_platform_device_create(struct device_node *np, return dev; } + +/** + * of_platform_device_create - Alloc, initialize and register an of_device + * @np: pointer to node to create device for + * @bus_id: name to assign device + * @parent: Linux device model parent device. + * + * Returns pointer to created platform device, or NULL if a device was not + * registered. Unavailable devices will not get registered. + */ +struct platform_device *of_platform_device_create(struct device_node *np, + const char *bus_id, + struct device *parent) +{ + return of_platform_device_create_pdata(np, bus_id, NULL, parent); +} EXPORT_SYMBOL(of_platform_device_create); +#ifdef CONFIG_ARM_AMBA +static struct amba_device *of_amba_device_create(struct device_node *node, + const char *bus_id, + void *platform_data, + struct device *parent) +{ + struct amba_device *dev; + const void *prop; + int i, ret; + + pr_debug("Creating amba device %s\n", node->full_name); + + if (!of_device_is_available(node)) + return NULL; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return NULL; + + /* setup generic device info */ + dev->dev.coherent_dma_mask = ~0; + dev->dev.of_node = of_node_get(node); + dev->dev.parent = parent; + dev->dev.platform_data = platform_data; + if (bus_id) + dev_set_name(&dev->dev, "%s", bus_id); + else + of_device_make_bus_id(&dev->dev); + + /* setup amba-specific device info */ + dev->dma_mask = ~0; + + /* Allow the HW Peripheral ID to be overridden */ + prop = of_get_property(node, "arm,primecell-periphid", NULL); + if (prop) + dev->periphid = of_read_ulong(prop, 1); + + /* Decode the IRQs and address ranges */ + for (i = 0; i < AMBA_NR_IRQS; i++) + dev->irq[i] = irq_of_parse_and_map(node, i); + + ret = of_address_to_resource(node, 0, &dev->res); + if (ret) + goto err_free; + + ret = amba_device_register(dev, &iomem_resource); + if (ret) + goto err_free; + + return dev; + +err_free: + kfree(dev); + return NULL; +} +#else /* CONFIG_ARM_AMBA */ +static struct amba_device *of_amba_device_create(struct device_node *node, + const char *bus_id, + void *platform_data, + struct device *parent) +{ + return NULL; +} +#endif /* CONFIG_ARM_AMBA */ + +/** + * of_devname_lookup() - Given a device node, lookup the preferred Linux name + */ +static const struct of_dev_auxdata *of_dev_lookup(const struct of_dev_auxdata *lookup, + struct device_node *np) +{ + struct resource res; + if (lookup) { + for(; lookup->name != NULL; lookup++) { + if (!of_device_is_compatible(np, lookup->compatible)) + continue; + if (of_address_to_resource(np, 0, &res)) + continue; + if (res.start != lookup->phys_addr) + continue; + pr_debug("%s: devname=%s\n", np->full_name, lookup->name); + return lookup; + } + } + return NULL; +} + /** * of_platform_bus_create() - Create a device for a node and its children. * @bus: device node of the bus to instantiate @@ -221,19 +337,41 @@ EXPORT_SYMBOL(of_platform_device_create); */ static int of_platform_bus_create(struct device_node *bus, const struct of_device_id *matches, - struct device *parent) + const struct of_dev_auxdata *lookup, + struct device *parent, bool strict) { + const struct of_dev_auxdata *auxdata; struct device_node *child; struct platform_device *dev; + const char *bus_id = NULL; + void *platform_data = NULL; int rc = 0; - dev = of_platform_device_create(bus, NULL, parent); + /* Make sure it has a compatible property */ + if (strict && (!of_get_property(bus, "compatible", NULL))) { + pr_debug("%s() - skipping %s, no compatible prop\n", + __func__, bus->full_name); + return 0; + } + + auxdata = of_dev_lookup(lookup, bus); + if (auxdata) { + bus_id = auxdata->name; + platform_data = auxdata->platform_data; + } + + if (of_device_is_compatible(bus, "arm,primecell")) { + of_amba_device_create(bus, bus_id, platform_data, parent); + return 0; + } + + dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent); if (!dev || !of_match_node(matches, bus)) return 0; for_each_child_of_node(bus, child) { pr_debug(" create child: %s\n", child->full_name); - rc = of_platform_bus_create(child, matches, &dev->dev); + rc = of_platform_bus_create(child, matches, lookup, &dev->dev, strict); if (rc) { of_node_put(child); break; @@ -267,11 +405,11 @@ int of_platform_bus_probe(struct device_node *root, /* Do a self check of bus type, if there's a match, create children */ if (of_match_node(matches, root)) { - rc = of_platform_bus_create(root, matches, parent); + rc = of_platform_bus_create(root, matches, NULL, parent, false); } else for_each_child_of_node(root, child) { if (!of_match_node(matches, child)) continue; - rc = of_platform_bus_create(child, matches, parent); + rc = of_platform_bus_create(child, matches, NULL, parent, false); if (rc) break; } @@ -280,4 +418,44 @@ int of_platform_bus_probe(struct device_node *root, return rc; } EXPORT_SYMBOL(of_platform_bus_probe); + +/** + * of_platform_populate() - Populate platform_devices from device tree data + * @root: parent of the first level to probe or NULL for the root of the tree + * @matches: match table, NULL to use the default + * @parent: parent to hook devices from, NULL for toplevel + * + * Similar to of_platform_bus_probe(), this function walks the device tree + * and creates devices from nodes. It differs in that it follows the modern + * convention of requiring all device nodes to have a 'compatible' property, + * and it is suitable for creating devices which are children of the root + * node (of_platform_bus_probe will only create children of the root which + * are selected by the @matches argument). + * + * New board support should be using this function instead of + * of_platform_bus_probe(). + * + * Returns 0 on success, < 0 on failure. + */ +int of_platform_populate(struct device_node *root, + const struct of_device_id *matches, + const struct of_dev_auxdata *lookup, + struct device *parent) +{ + struct device_node *child; + int rc = 0; + + root = root ? of_node_get(root) : of_find_node_by_path("/"); + if (!root) + return -EINVAL; + + for_each_child_of_node(root, child) { + rc = of_platform_bus_create(child, matches, lookup, parent, true); + if (rc) + break; + } + + of_node_put(root); + return rc; +} #endif /* !CONFIG_SPARC */ diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/of_serial.c index c911b2419abb..e58cece6f443 100644 --- a/drivers/tty/serial/of_serial.c +++ b/drivers/tty/serial/of_serial.c @@ -32,17 +32,17 @@ static int __devinit of_platform_serial_setup(struct platform_device *ofdev, { struct resource resource; struct device_node *np = ofdev->dev.of_node; - const __be32 *clk, *spd; - const __be32 *prop; - int ret, prop_size; + u32 clk, spd, prop; + int ret; memset(port, 0, sizeof *port); - spd = of_get_property(np, "current-speed", NULL); - clk = of_get_property(np, "clock-frequency", NULL); - if (!clk) { + if (of_property_read_u32(np, "clock-frequency", &clk)) { dev_warn(&ofdev->dev, "no clock-frequency property set\n"); return -ENODEV; } + /* If current-speed was set, then try not to change it. */ + if (of_property_read_u32(np, "current-speed", &spd) == 0) + port->custom_divisor = clk / (16 * spd); ret = of_address_to_resource(np, 0, &resource); if (ret) { @@ -54,25 +54,35 @@ static int __devinit of_platform_serial_setup(struct platform_device *ofdev, port->mapbase = resource.start; /* Check for shifted address mapping */ - prop = of_get_property(np, "reg-offset", &prop_size); - if (prop && (prop_size == sizeof(u32))) - port->mapbase += be32_to_cpup(prop); + if (of_property_read_u32(np, "reg-offset", &prop) == 0) + port->mapbase += prop; /* Check for registers offset within the devices address range */ - prop = of_get_property(np, "reg-shift", &prop_size); - if (prop && (prop_size == sizeof(u32))) - port->regshift = be32_to_cpup(prop); + if (of_property_read_u32(np, "reg-shift", &prop) == 0) + port->regshift = prop; port->irq = irq_of_parse_and_map(np, 0); port->iotype = UPIO_MEM; + if (of_property_read_u32(np, "reg-io-width", &prop) == 0) { + switch (prop) { + case 1: + port->iotype = UPIO_MEM; + break; + case 4: + port->iotype = UPIO_MEM32; + break; + default: + dev_warn(&ofdev->dev, "unsupported reg-io-width (%d)\n", + prop); + return -EINVAL; + } + } + port->type = type; - port->uartclk = be32_to_cpup(clk); + port->uartclk = clk; port->flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_IOREMAP | UPF_FIXED_PORT | UPF_FIXED_TYPE; port->dev = &ofdev->dev; - /* If current-speed was set, then try not to change it. */ - if (spd) - port->custom_divisor = be32_to_cpup(clk) / (16 * (be32_to_cpup(spd))); return 0; } @@ -171,6 +181,7 @@ static struct of_device_id __devinitdata of_platform_serial_table[] = { { .compatible = "ns16550", .data = (void *)PORT_16550, }, { .compatible = "ns16750", .data = (void *)PORT_16750, }, { .compatible = "ns16850", .data = (void *)PORT_16850, }, + { .compatible = "nvidia,tegra20-uart", .data = (void *)PORT_TEGRA, }, #ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL { .compatible = "ibm,qpace-nwp-serial", .data = (void *)PORT_NWPSERIAL, }, |