arch/powerpc/kernel/ibmebus.c is the only remaining user of the of_bus_type support code for initializing the bus and registering drivers. All others have either been switched to the vanilla platform bus or already have their own infrastructure. This patch moves the functionality that ibmebus is using out of drivers/of/{platform,device}.c and into ibmebus.c where it is actually used. Also renames the moved symbols from of_platform_* to ibmebus_bus_* to reflect the actual usage. This patch is part of moving all of the of_platform_bus_type users over to the platform_bus_type. Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
378 lines
10 KiB
C
378 lines
10 KiB
C
/*
|
|
* Copyright (C) 2006 Benjamin Herrenschmidt, IBM Corp.
|
|
* <benh@kernel.crashing.org>
|
|
* and Arnd Bergmann, IBM Corp.
|
|
* Merged from powerpc/kernel/of_platform.c and
|
|
* sparc{,64}/kernel/of_device.c by Stephen Rothwell
|
|
*
|
|
* 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/errno.h>
|
|
#include <linux/module.h>
|
|
#include <linux/device.h>
|
|
#include <linux/dma-mapping.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/of_address.h>
|
|
#include <linux/of_device.h>
|
|
#include <linux/of_irq.h>
|
|
#include <linux/of_platform.h>
|
|
#include <linux/platform_device.h>
|
|
|
|
static int of_dev_node_match(struct device *dev, void *data)
|
|
{
|
|
return dev->of_node == data;
|
|
}
|
|
|
|
/**
|
|
* of_find_device_by_node - Find the platform_device associated with a node
|
|
* @np: Pointer to device tree node
|
|
*
|
|
* Returns platform_device pointer, or NULL if not found
|
|
*/
|
|
struct platform_device *of_find_device_by_node(struct device_node *np)
|
|
{
|
|
struct device *dev;
|
|
|
|
dev = bus_find_device(&platform_bus_type, NULL, np, of_dev_node_match);
|
|
return dev ? to_platform_device(dev) : NULL;
|
|
}
|
|
EXPORT_SYMBOL(of_find_device_by_node);
|
|
|
|
static int platform_driver_probe_shim(struct platform_device *pdev)
|
|
{
|
|
struct platform_driver *pdrv;
|
|
struct of_platform_driver *ofpdrv;
|
|
const struct of_device_id *match;
|
|
|
|
pdrv = container_of(pdev->dev.driver, struct platform_driver, driver);
|
|
ofpdrv = container_of(pdrv, struct of_platform_driver, platform_driver);
|
|
|
|
/* There is an unlikely chance that an of_platform driver might match
|
|
* on a non-OF platform device. If so, then of_match_device() will
|
|
* come up empty. Return -EINVAL in this case so other drivers get
|
|
* the chance to bind. */
|
|
match = of_match_device(pdev->dev.driver->of_match_table, &pdev->dev);
|
|
return match ? ofpdrv->probe(pdev, match) : -EINVAL;
|
|
}
|
|
|
|
static void platform_driver_shutdown_shim(struct platform_device *pdev)
|
|
{
|
|
struct platform_driver *pdrv;
|
|
struct of_platform_driver *ofpdrv;
|
|
|
|
pdrv = container_of(pdev->dev.driver, struct platform_driver, driver);
|
|
ofpdrv = container_of(pdrv, struct of_platform_driver, platform_driver);
|
|
ofpdrv->shutdown(pdev);
|
|
}
|
|
|
|
/**
|
|
* of_register_platform_driver
|
|
*/
|
|
int of_register_platform_driver(struct of_platform_driver *drv)
|
|
{
|
|
char *of_name;
|
|
|
|
/* setup of_platform_driver to platform_driver adaptors */
|
|
drv->platform_driver.driver = drv->driver;
|
|
|
|
/* Prefix the driver name with 'of:' to avoid namespace collisions
|
|
* and bogus matches. There are some drivers in the tree that
|
|
* register both an of_platform_driver and a platform_driver with
|
|
* the same name. This is a temporary measure until they are all
|
|
* cleaned up --gcl July 29, 2010 */
|
|
of_name = kmalloc(strlen(drv->driver.name) + 5, GFP_KERNEL);
|
|
if (!of_name)
|
|
return -ENOMEM;
|
|
sprintf(of_name, "of:%s", drv->driver.name);
|
|
drv->platform_driver.driver.name = of_name;
|
|
|
|
if (drv->probe)
|
|
drv->platform_driver.probe = platform_driver_probe_shim;
|
|
drv->platform_driver.remove = drv->remove;
|
|
if (drv->shutdown)
|
|
drv->platform_driver.shutdown = platform_driver_shutdown_shim;
|
|
drv->platform_driver.suspend = drv->suspend;
|
|
drv->platform_driver.resume = drv->resume;
|
|
|
|
return platform_driver_register(&drv->platform_driver);
|
|
}
|
|
EXPORT_SYMBOL(of_register_platform_driver);
|
|
|
|
void of_unregister_platform_driver(struct of_platform_driver *drv)
|
|
{
|
|
platform_driver_unregister(&drv->platform_driver);
|
|
kfree(drv->platform_driver.driver.name);
|
|
drv->platform_driver.driver.name = NULL;
|
|
}
|
|
EXPORT_SYMBOL(of_unregister_platform_driver);
|
|
|
|
#if defined(CONFIG_PPC_DCR)
|
|
#include <asm/dcr.h>
|
|
#endif
|
|
|
|
#if !defined(CONFIG_SPARC)
|
|
/*
|
|
* The following routines scan a subtree and registers a device for
|
|
* each applicable node.
|
|
*
|
|
* Note: sparc doesn't use these routines because it has a different
|
|
* mechanism for creating devices from device tree nodes.
|
|
*/
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
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;
|
|
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);
|
|
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;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 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);
|
|
}
|
|
|
|
/**
|
|
* of_device_alloc - Allocate and initialize an of_device
|
|
* @np: device node to assign to device
|
|
* @bus_id: Name to assign to the device. May be null to use default name.
|
|
* @parent: Parent device.
|
|
*/
|
|
struct platform_device *of_device_alloc(struct device_node *np,
|
|
const char *bus_id,
|
|
struct device *parent)
|
|
{
|
|
struct platform_device *dev;
|
|
int rc, i, num_reg = 0, num_irq;
|
|
struct resource *res, temp_res;
|
|
|
|
dev = platform_device_alloc("", -1);
|
|
if (!dev)
|
|
return NULL;
|
|
|
|
/* 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);
|
|
}
|
|
WARN_ON(of_irq_to_resource_table(np, res, num_irq) != num_irq);
|
|
}
|
|
|
|
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)
|
|
dev_set_name(&dev->dev, "%s", bus_id);
|
|
else
|
|
of_device_make_bus_id(&dev->dev);
|
|
|
|
return dev;
|
|
}
|
|
EXPORT_SYMBOL(of_device_alloc);
|
|
|
|
/**
|
|
* 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)
|
|
{
|
|
struct platform_device *dev;
|
|
|
|
if (!of_device_is_available(np))
|
|
return NULL;
|
|
|
|
dev = of_device_alloc(np, bus_id, parent);
|
|
if (!dev)
|
|
return NULL;
|
|
|
|
#if defined(CONFIG_PPC) || defined(CONFIG_MICROBLAZE)
|
|
dev->archdata.dma_mask = 0xffffffffUL;
|
|
#endif
|
|
dev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
|
|
dev->dev.bus = &platform_bus_type;
|
|
|
|
/* 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
|
|
*/
|
|
|
|
if (of_device_add(dev) != 0) {
|
|
platform_device_put(dev);
|
|
return NULL;
|
|
}
|
|
|
|
return dev;
|
|
}
|
|
EXPORT_SYMBOL(of_platform_device_create);
|
|
|
|
/**
|
|
* of_platform_bus_create - Create an OF device for a bus node and all its
|
|
* children. Optionally recursively instantiate matching busses.
|
|
* @bus: device node of the bus to instantiate
|
|
* @matches: match table, NULL to use the default, OF_NO_DEEP_PROBE to
|
|
* disallow recursive creation of child busses
|
|
*/
|
|
static int of_platform_bus_create(const struct device_node *bus,
|
|
const struct of_device_id *matches,
|
|
struct device *parent)
|
|
{
|
|
struct device_node *child;
|
|
struct platform_device *dev;
|
|
int rc = 0;
|
|
|
|
for_each_child_of_node(bus, child) {
|
|
pr_debug(" create child: %s\n", child->full_name);
|
|
dev = of_platform_device_create(child, NULL, parent);
|
|
if (dev == NULL)
|
|
continue;
|
|
|
|
if (!of_match_node(matches, child))
|
|
continue;
|
|
if (rc == 0) {
|
|
pr_debug(" and sub busses\n");
|
|
rc = of_platform_bus_create(child, matches, &dev->dev);
|
|
}
|
|
if (rc) {
|
|
of_node_put(child);
|
|
break;
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* of_platform_bus_probe - Probe the device-tree for platform busses
|
|
* @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
|
|
*
|
|
* Note that children of the provided root are not instantiated as devices
|
|
* unless the specified root itself matches the bus list and is not NULL.
|
|
*/
|
|
int of_platform_bus_probe(struct device_node *root,
|
|
const struct of_device_id *matches,
|
|
struct device *parent)
|
|
{
|
|
struct device_node *child;
|
|
struct platform_device *dev;
|
|
int rc = 0;
|
|
|
|
if (WARN_ON(!matches || matches == OF_NO_DEEP_PROBE))
|
|
return -EINVAL;
|
|
if (root == NULL)
|
|
root = of_find_node_by_path("/");
|
|
else
|
|
of_node_get(root);
|
|
if (root == NULL)
|
|
return -EINVAL;
|
|
|
|
pr_debug("of_platform_bus_probe()\n");
|
|
pr_debug(" starting at: %s\n", root->full_name);
|
|
|
|
/* Do a self check of bus type, if there's a match, create
|
|
* children
|
|
*/
|
|
if (of_match_node(matches, root)) {
|
|
pr_debug(" root match, create all sub devices\n");
|
|
dev = of_platform_device_create(root, NULL, parent);
|
|
if (dev == NULL)
|
|
goto bail;
|
|
|
|
pr_debug(" create all sub busses\n");
|
|
rc = of_platform_bus_create(root, matches, &dev->dev);
|
|
goto bail;
|
|
}
|
|
for_each_child_of_node(root, child) {
|
|
if (!of_match_node(matches, child))
|
|
continue;
|
|
|
|
pr_debug(" match: %s\n", child->full_name);
|
|
dev = of_platform_device_create(child, NULL, parent);
|
|
if (dev == NULL)
|
|
continue;
|
|
|
|
rc = of_platform_bus_create(child, matches, &dev->dev);
|
|
if (rc) {
|
|
of_node_put(child);
|
|
break;
|
|
}
|
|
}
|
|
bail:
|
|
of_node_put(root);
|
|
return rc;
|
|
}
|
|
EXPORT_SYMBOL(of_platform_bus_probe);
|
|
#endif /* !CONFIG_SPARC */
|