diff options
Diffstat (limited to 'drivers/of/platform.c')
| -rw-r--r-- | drivers/of/platform.c | 417 |
1 files changed, 349 insertions, 68 deletions
diff --git a/drivers/of/platform.c b/drivers/of/platform.c index 63d3cb73bdb..500436f9be7 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; @@ -42,11 +51,7 @@ struct platform_device *of_find_device_by_node(struct device_node *np) } EXPORT_SYMBOL(of_find_device_by_node); -#if defined(CONFIG_PPC_DCR) -#include <asm/dcr.h> -#endif - -#if !defined(CONFIG_SPARC) +#ifdef CONFIG_OF_ADDRESS /* * The following routines scan a subtree and registers a device for * each applicable node. @@ -59,57 +64,35 @@ EXPORT_SYMBOL(of_find_device_by_node); * of_device_make_bus_id - Use the device node data to assign a unique name * @dev: pointer to device structure that is linked to a device tree node * - * This routine will first try using either the dcr-reg or the reg property - * value to derive a unique name. As a last resort it will use the node - * name followed by a unique number. + * This routine will first try using the translated bus address to + * derive a unique name. If it cannot, then it will prepend names from + * parent nodes until a unique name can be derived. */ void of_device_make_bus_id(struct device *dev) { - static atomic_t bus_no_reg_magic; struct device_node *node = dev->of_node; - const u32 *reg; + const __be32 *reg; u64 addr; - int magic; -#ifdef CONFIG_PPC_DCR - /* - * If it's a DCR based device, use 'd' for native DCRs - * and 'D' for MMIO DCRs. - */ - reg = of_get_property(node, "dcr-reg", NULL); - if (reg) { -#ifdef CONFIG_PPC_DCR_NATIVE - dev_set_name(dev, "d%x.%s", *reg, node->name); -#else /* CONFIG_PPC_DCR_NATIVE */ - u64 addr = of_translate_dcr_address(node, *reg, NULL); - if (addr != OF_BAD_ADDR) { - dev_set_name(dev, "D%llx.%s", - (unsigned long long)addr, node->name); + /* Construct the name, using parent nodes if necessary to ensure uniqueness */ + while (node->parent) { + /* + * If the address can be translated, then that is as much + * uniqueness as we need. Make it the first component and return + */ + reg = of_get_property(node, "reg", NULL); + if (reg && (addr = of_translate_address(node, reg)) != OF_BAD_ADDR) { + dev_set_name(dev, dev_name(dev) ? "%llx.%s:%s" : "%llx.%s", + (unsigned long long)addr, node->name, + dev_name(dev)); return; } -#endif /* !CONFIG_PPC_DCR_NATIVE */ - } -#endif /* CONFIG_PPC_DCR */ - /* - * For MMIO, get the physical address - */ - reg = of_get_property(node, "reg", NULL); - if (reg) { - addr = of_translate_address(node, reg); - if (addr != OF_BAD_ADDR) { - dev_set_name(dev, "%llx.%s", - (unsigned long long)addr, node->name); - return; - } + /* format arguments only used if dev_name() resolves to NULL */ + dev_set_name(dev, dev_name(dev) ? "%s:%s" : "%s", + strrchr(node->full_name, '/') + 1, dev_name(dev)); + node = node->parent; } - - /* - * No BusID, use the node name and add a globally incremented - * counter (and pray...) - */ - magic = atomic_add_return(1, &bus_no_reg_magic); - dev_set_name(dev, "%s.%d", node->name, magic - 1); } /** @@ -149,13 +132,12 @@ struct platform_device *of_device_alloc(struct device_node *np, rc = of_address_to_resource(np, i, res); WARN_ON(rc); } - WARN_ON(of_irq_to_resource_table(np, res, num_irq) != num_irq); + if (of_irq_to_resource_table(np, res, num_irq) != num_irq) + pr_debug("not all legacy IRQ resources mapped for %s\n", + np->name); } dev->dev.of_node = of_node_get(np); -#if defined(CONFIG_PPC) || defined(CONFIG_MICROBLAZE) - dev->dev.dma_mask = &dev->archdata.dma_mask; -#endif dev->dev.parent = parent; if (bus_id) @@ -168,32 +150,88 @@ 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_dma_configure - Setup DMA configuration + * @dev: Device to apply DMA configuration + * + * Try to get devices's DMA configuration from DT and update it + * accordingly. + * + * In case if platform code need to use own special DMA configuration,it + * can use Platform bus notifier and handle BUS_NOTIFY_ADD_DEVICE event + * to fix up DMA configuration. + */ +static void of_dma_configure(struct platform_device *pdev) +{ + u64 dma_addr, paddr, size; + int ret; + struct device *dev = &pdev->dev; + + /* + * Set default dma-mask to 32 bit. Drivers are expected to setup + * the correct supported dma_mask. + */ + dev->coherent_dma_mask = DMA_BIT_MASK(32); + + /* + * Set it to coherent_dma_mask by default if the architecture + * code has not set it. + */ + if (!dev->dma_mask) + dev->dma_mask = &dev->coherent_dma_mask; + + /* + * if dma-coherent property exist, call arch hook to setup + * dma coherent operations. + */ + if (of_dma_is_coherent(dev->of_node)) { + set_arch_dma_coherent_ops(dev); + dev_dbg(dev, "device is dma coherent\n"); + } + + /* + * if dma-ranges property doesn't exist - just return else + * setup the dma offset + */ + ret = of_dma_get_range(dev->of_node, &dma_addr, &paddr, &size); + if (ret < 0) { + dev_dbg(dev, "no dma range information to setup\n"); + return; + } + + /* DMA ranges found. Calculate and set dma_pfn_offset */ + dev->dma_pfn_offset = PFN_DOWN(paddr - dma_addr); + dev_dbg(dev, "dma_pfn_offset(%#08lx)\n", dev->dma_pfn_offset); +} + +/** + * 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) +static 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; - if (!of_device_is_available(np)) + if (!of_device_is_available(np) || + of_node_test_and_set_flag(np, OF_POPULATED)) return NULL; dev = of_device_alloc(np, bus_id, parent); if (!dev) - return NULL; + goto err_clear_flag; -#if defined(CONFIG_PPC) || defined(CONFIG_MICROBLAZE) - dev->archdata.dma_mask = 0xffffffffUL; -#endif - dev->dev.coherent_dma_mask = DMA_BIT_MASK(32); + of_dma_configure(dev); 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 @@ -202,38 +240,183 @@ struct platform_device *of_platform_device_create(struct device_node *np, if (of_device_add(dev) != 0) { platform_device_put(dev); - return NULL; + goto err_clear_flag; } return dev; + +err_clear_flag: + of_node_clear_flag(np, OF_POPULATED); + return NULL; +} + +/** + * 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) || + of_node_test_and_set_flag(node, OF_POPULATED)) + return NULL; + + dev = amba_device_alloc(NULL, 0, 0); + if (!dev) { + pr_err("%s(): amba_device_alloc() failed for %s\n", + __func__, node->full_name); + goto err_clear_flag; + } + + /* 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); + + /* 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) { + pr_err("%s(): of_address_to_resource() failed (%d) for %s\n", + __func__, ret, node->full_name); + goto err_free; + } + + ret = amba_device_add(dev, &iomem_resource); + if (ret) { + pr_err("%s(): amba_device_add() failed (%d) for %s\n", + __func__, ret, node->full_name); + goto err_free; + } + + return dev; + +err_free: + amba_device_put(dev); +err_clear_flag: + of_node_clear_flag(node, OF_POPULATED); + 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) + return NULL; + + for(; lookup->compatible != NULL; lookup++) { + if (!of_device_is_compatible(np, lookup->compatible)) + continue; + if (!of_address_to_resource(np, 0, &res)) + 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 * @matches: match table for bus nodes - * disallow recursive creation of child buses + * @lookup: auxdata table for matching id and platform_data with device nodes * @parent: parent for new device, or NULL for top level. + * @strict: require compatible property * * Creates a platform_device for the provided device_node, and optionally * recursively create devices for all the child nodes. */ 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")) { + /* + * Don't return an error here to keep compatibility with older + * device tree files. + */ + 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 +450,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 +463,102 @@ int of_platform_bus_probe(struct device_node *root, return rc; } EXPORT_SYMBOL(of_platform_bus_probe); -#endif /* !CONFIG_SPARC */ + +/** + * 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 + * @lookup: auxdata table for matching id and platform_data with device nodes + * @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; +} +EXPORT_SYMBOL_GPL(of_platform_populate); + +static int of_platform_device_destroy(struct device *dev, void *data) +{ + bool *children_left = data; + + /* Do not touch devices not populated from the device tree */ + if (!dev->of_node || !of_node_check_flag(dev->of_node, OF_POPULATED)) { + *children_left = true; + return 0; + } + + /* Recurse, but don't touch this device if it has any children left */ + if (of_platform_depopulate(dev) != 0) { + *children_left = true; + return 0; + } + + if (dev->bus == &platform_bus_type) + platform_device_unregister(to_platform_device(dev)); +#ifdef CONFIG_ARM_AMBA + else if (dev->bus == &amba_bustype) + amba_device_unregister(to_amba_device(dev)); +#endif + else { + *children_left = true; + return 0; + } + + of_node_clear_flag(dev->of_node, OF_POPULATED); + + return 0; +} + +/** + * of_platform_depopulate() - Remove devices populated from device tree + * @parent: device which childred will be removed + * + * Complementary to of_platform_populate(), this function removes children + * of the given device (and, recurrently, their children) that have been + * created from their respective device tree nodes (and only those, + * leaving others - eg. manually created - unharmed). + * + * Returns 0 when all children devices have been removed or + * -EBUSY when some children remained. + */ +int of_platform_depopulate(struct device *parent) +{ + bool children_left = false; + + device_for_each_child(parent, &children_left, + of_platform_device_destroy); + + return children_left ? -EBUSY : 0; +} +EXPORT_SYMBOL_GPL(of_platform_depopulate); + +#endif /* CONFIG_OF_ADDRESS */ |
