linux-stable/drivers/of/device.c
Grant Likely eca3930163 of: Merge of_platform_bus_type with platform_bus_type
of_platform_bus was being used in the same manner as the platform_bus.
The only difference being that of_platform_bus devices are generated
from data in the device tree, and platform_bus devices are usually
statically allocated in platform code.  Having them separate causes
the problem of device drivers having to be registered twice if it
was possible for the same device to appear on either bus.

This patch removes of_platform_bus_type and registers all of_platform
bus devices and drivers on the platform bus instead.  A previous patch
made the of_device structure an alias for the platform_device structure,
and a shim is used to adapt of_platform_drivers to the platform bus.

After all of of_platform_bus drivers are converted to be normal platform
drivers, the shim code can be removed.

Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
Acked-by: David S. Miller <davem@davemloft.net>
2010-07-24 09:57:51 -06:00

221 lines
5.1 KiB
C

#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/slab.h>
#include <asm/errno.h>
/**
* of_match_device - Tell if a struct device matches an of_device_id list
* @ids: array of of device match structures to search in
* @dev: the of device structure to match against
*
* Used by a driver to check whether an of_device present in the
* system is in its list of supported devices.
*/
const struct of_device_id *of_match_device(const struct of_device_id *matches,
const struct device *dev)
{
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 device *tmp;
if (!dev)
return NULL;
tmp = get_device(&dev->dev);
if (tmp)
return to_of_device(tmp);
else
return NULL;
}
EXPORT_SYMBOL(of_dev_get);
void of_dev_put(struct of_device *dev)
{
if (dev)
put_device(&dev->dev);
}
EXPORT_SYMBOL(of_dev_put);
static ssize_t devspec_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct of_device *ofdev;
ofdev = to_of_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;
ofdev = to_of_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)
{
ssize_t len = of_device_get_modalias(dev, buf, PAGE_SIZE - 2);
buf[len] = '\n';
buf[len+1] = 0;
return len+1;
}
struct device_attribute of_platform_device_attrs[] = {
__ATTR_RO(devspec),
__ATTR_RO(name),
__ATTR_RO(modalias),
__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 of_device *ofdev;
ofdev = to_of_device(dev);
of_node_put(ofdev->dev.of_node);
kfree(ofdev);
}
EXPORT_SYMBOL(of_release_dev);
int of_device_register(struct of_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 */
if (!ofdev->dev.parent)
set_dev_node(&ofdev->dev, of_node_to_nid(ofdev->dev.of_node));
return device_add(&ofdev->dev);
}
EXPORT_SYMBOL(of_device_register);
void of_device_unregister(struct of_device *ofdev)
{
device_unregister(&ofdev->dev);
}
EXPORT_SYMBOL(of_device_unregister);
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", dev->of_node->name,
dev->of_node->type);
/* Get compatible property if any */
compat = of_get_property(dev->of_node, "compatible", &cplen);
if (!compat)
return csize;
/* Find true end (we tolerate multiple \0 at the end */
for (i = (cplen - 1); i >= 0 && !compat[i]; i--)
cplen--;
if (!cplen)
return csize;
cplen++;
/* Check space (need cplen+1 chars including final \0) */
tsize = csize + cplen;
repend = tsize;
if (csize >= len) /* @ the limit, all is already filled */
return tsize;
if (tsize >= len) { /* limit compat list */
cplen = len - csize - 1;
repend = len;
}
/* Copy and do char replacement */
memcpy(&str[csize + 1], compat, cplen);
for (i = csize; i < repend; i++) {
char c = str[i];
if (c == '\0')
str[i] = 'C';
else if (c == ' ')
str[i] = '_';
}
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;
}