diff options
Diffstat (limited to 'drivers/of/device.c')
| -rw-r--r-- | drivers/of/device.c | 180 |
1 files changed, 135 insertions, 45 deletions
diff --git a/drivers/of/device.c b/drivers/of/device.c index 29681c4b700..dafb9736ab9 100644 --- a/drivers/of/device.c +++ b/drivers/of/device.c @@ -8,26 +8,26 @@ #include <linux/slab.h> #include <asm/errno.h> +#include "of_private.h" /** - * of_match_device - Tell if an of_device structure has a matching - * of_match structure + * 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 + * Used by a driver to check whether an platform_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 of_device *dev) + const struct device *dev) { - if (!dev->node) + if ((!matches) || (!dev->of_node)) return NULL; - return of_match_node(matches, dev->node); + return of_match_node(matches, dev->of_node); } EXPORT_SYMBOL(of_match_device); -struct of_device *of_dev_get(struct of_device *dev) +struct platform_device *of_dev_get(struct platform_device *dev) { struct device *tmp; @@ -35,68 +35,158 @@ struct of_device *of_dev_get(struct of_device *dev) return NULL; tmp = get_device(&dev->dev); if (tmp) - return to_of_device(tmp); + return to_platform_device(tmp); else return NULL; } EXPORT_SYMBOL(of_dev_get); -void of_dev_put(struct of_device *dev) +void of_dev_put(struct platform_device *dev) { if (dev) put_device(&dev->dev); } EXPORT_SYMBOL(of_dev_put); -static ssize_t dev_show_devspec(struct device *dev, - struct device_attribute *attr, char *buf) +int of_device_add(struct platform_device *ofdev) { - struct of_device *ofdev; + BUG_ON(ofdev->dev.of_node == NULL); - ofdev = to_of_device(dev); - return sprintf(buf, "%s", ofdev->node->full_name); + /* 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); } -static DEVICE_ATTR(devspec, S_IRUGO, dev_show_devspec, NULL); +int of_device_register(struct platform_device *pdev) +{ + device_initialize(&pdev->dev); + return of_device_add(pdev); +} +EXPORT_SYMBOL(of_device_register); -/** - * 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) +void of_device_unregister(struct platform_device *ofdev) { - struct of_device *ofdev; + device_unregister(&ofdev->dev); +} +EXPORT_SYMBOL(of_device_unregister); - ofdev = to_of_device(dev); - of_node_put(ofdev->node); - kfree(ofdev); +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; + + if ((!dev) || (!dev->of_node)) + return -ENODEV; + + /* 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; } -EXPORT_SYMBOL(of_release_dev); -int of_device_register(struct of_device *ofdev) +/** + * of_device_uevent - Display OF related uevent information + */ +void of_device_uevent(struct device *dev, struct kobj_uevent_env *env) { - int rc; + const char *compat; + struct alias_prop *app; + int seen = 0, cplen, sl; + + if ((!dev) || (!dev->of_node)) + return; + + add_uevent_var(env, "OF_NAME=%s", dev->of_node->name); + add_uevent_var(env, "OF_FULLNAME=%s", dev->of_node->full_name); + if (dev->of_node->type && strcmp("<NULL>", dev->of_node->type) != 0) + add_uevent_var(env, "OF_TYPE=%s", dev->of_node->type); + + /* 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) { + add_uevent_var(env, "OF_COMPATIBLE_%d=%s", seen, compat); + sl = strlen(compat) + 1; + compat += sl; + cplen -= sl; + seen++; + } + add_uevent_var(env, "OF_COMPATIBLE_N=%d", seen); + + seen = 0; + mutex_lock(&of_aliases_mutex); + list_for_each_entry(app, &aliases_lookup, link) { + if (dev->of_node == app->np) { + add_uevent_var(env, "OF_ALIAS_%d=%s", seen, + app->alias); + seen++; + } + } + mutex_unlock(&of_aliases_mutex); +} - BUG_ON(ofdev->node == NULL); +int of_device_uevent_modalias(struct device *dev, struct kobj_uevent_env *env) +{ + int sl; - rc = device_register(&ofdev->dev); - if (rc) - return rc; + if ((!dev) || (!dev->of_node)) + return -ENODEV; - rc = device_create_file(&ofdev->dev, &dev_attr_devspec); - if (rc) - device_unregister(&ofdev->dev); + /* Devicetree modalias is tricky, we add it in 2 steps */ + if (add_uevent_var(env, "MODALIAS=")) + return -ENOMEM; - return rc; -} -EXPORT_SYMBOL(of_device_register); + 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; -void of_device_unregister(struct of_device *ofdev) -{ - device_remove_file(&ofdev->dev, &dev_attr_devspec); - device_unregister(&ofdev->dev); + return 0; } -EXPORT_SYMBOL(of_device_unregister); |
