X-Git-Url: http://pilppa.org/gitweb/gitweb.cgi?a=blobdiff_plain;f=arch%2Fpowerpc%2Fkernel%2Fof_device.c;h=5748ddb47d9f82fc332b844e56613d3a6e84a07b;hb=a19214430d27a3af6f1672ec26f3c893ef899ede;hp=397c83eda20ee3ac907c929e526bf59510b7b5f1;hpb=fecf3404f4aba6d0edeba31eeb018cbb6326dff2;p=linux-2.6-omap-h63xx.git diff --git a/arch/powerpc/kernel/of_device.c b/arch/powerpc/kernel/of_device.c index 397c83eda20..5748ddb47d9 100644 --- a/arch/powerpc/kernel/of_device.c +++ b/arch/powerpc/kernel/of_device.c @@ -1,248 +1,187 @@ #include #include +#include #include #include #include #include +#include #include -#include - -/** - * of_match_device - Tell if an of_device structure has a matching - * of_match structure - * @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 of_device *dev) -{ - if (!dev->node) - return NULL; - while (matches->name[0] || matches->type[0] || matches->compatible[0]) { - int match = 1; - if (matches->name[0]) - match &= dev->node->name - && !strcmp(matches->name, dev->node->name); - if (matches->type[0]) - match &= dev->node->type - && !strcmp(matches->type, dev->node->type); - if (matches->compatible[0]) - match &= device_is_compatible(dev->node, - matches->compatible); - if (match) - return matches; - matches++; +#include + +static void of_device_make_bus_id(struct of_device *dev) +{ + static atomic_t bus_no_reg_magic; + struct device_node *node = dev->node; + char *name = dev->dev.bus_id; + const u32 *reg; + u64 addr; + int magic; + + /* + * If it's a DCR based device, use 'd' for native DCRs + * and 'D' for MMIO DCRs. + */ +#ifdef CONFIG_PPC_DCR + reg = of_get_property(node, "dcr-reg", NULL); + if (reg) { +#ifdef CONFIG_PPC_DCR_NATIVE + snprintf(name, BUS_ID_SIZE, "d%x.%s", + *reg, node->name); +#else /* CONFIG_PPC_DCR_NATIVE */ + addr = of_translate_dcr_address(node, *reg, NULL); + if (addr != OF_BAD_ADDR) { + snprintf(name, BUS_ID_SIZE, + "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) { + snprintf(name, BUS_ID_SIZE, + "%llx.%s", (unsigned long long)addr, + node->name); + return; + } } - return NULL; -} - -static int of_platform_bus_match(struct device *dev, struct device_driver *drv) -{ - struct of_device * of_dev = to_of_device(dev); - struct of_platform_driver * of_drv = to_of_platform_driver(drv); - const struct of_device_id * matches = of_drv->match_table; - - if (!matches) - return 0; - return of_match_device(matches, of_dev) != NULL; + /* + * No BusID, use the node name and add a globally incremented + * counter (and pray...) + */ + magic = atomic_add_return(1, &bus_no_reg_magic); + snprintf(name, BUS_ID_SIZE, "%s.%d", node->name, magic - 1); } -struct of_device *of_dev_get(struct of_device *dev) +struct of_device *of_device_alloc(struct device_node *np, + const char *bus_id, + struct device *parent) { - struct device *tmp; + struct of_device *dev; + dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) return NULL; - tmp = get_device(&dev->dev); - if (tmp) - return to_of_device(tmp); - else - return NULL; -} - -void of_dev_put(struct of_device *dev) -{ - if (dev) - put_device(&dev->dev); -} - - -static int of_device_probe(struct device *dev) -{ - int error = -ENODEV; - struct of_platform_driver *drv; - struct of_device *of_dev; - const struct of_device_id *match; - - drv = to_of_platform_driver(dev->driver); - of_dev = to_of_device(dev); - - if (!drv->probe) - return error; - of_dev_get(of_dev); - - match = of_match_device(drv->match_table, of_dev); - if (match) - error = drv->probe(of_dev, match); - if (error) - of_dev_put(of_dev); - - return error; -} + dev->node = of_node_get(np); + dev->dev.dma_mask = &dev->dma_mask; + dev->dev.parent = parent; + dev->dev.release = of_release_dev; + dev->dev.archdata.of_node = np; + dev->dev.archdata.numa_node = of_node_to_nid(np); -static int of_device_remove(struct device *dev) -{ - struct of_device * of_dev = to_of_device(dev); - struct of_platform_driver * drv = to_of_platform_driver(dev->driver); + if (bus_id) + strlcpy(dev->dev.bus_id, bus_id, BUS_ID_SIZE); + else + of_device_make_bus_id(dev); - if (dev->driver && drv->remove) - drv->remove(of_dev); - return 0; + return dev; } +EXPORT_SYMBOL(of_device_alloc); -static int of_device_suspend(struct device *dev, pm_message_t state) +ssize_t of_device_get_modalias(struct of_device *ofdev, + char *str, ssize_t len) { - struct of_device * of_dev = to_of_device(dev); - struct of_platform_driver * drv = to_of_platform_driver(dev->driver); - int error = 0; + const char *compat; + int cplen, i; + ssize_t tsize, csize, repend; - if (dev->driver && drv->suspend) - error = drv->suspend(of_dev, state); - return error; -} + /* Name & Type */ + csize = snprintf(str, len, "of:N%sT%s", + ofdev->node->name, ofdev->node->type); -static int of_device_resume(struct device * dev) -{ - struct of_device * of_dev = to_of_device(dev); - struct of_platform_driver * drv = to_of_platform_driver(dev->driver); - int error = 0; + /* Get compatible property if any */ + compat = of_get_property(ofdev->node, "compatible", &cplen); + if (!compat) + return csize; - if (dev->driver && drv->resume) - error = drv->resume(of_dev); - return error; -} + /* 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++; -struct bus_type of_platform_bus_type = { - .name = "of_platform", - .match = of_platform_bus_match, - .probe = of_device_probe, - .remove = of_device_remove, - .suspend = of_device_suspend, - .resume = of_device_resume, -}; + /* Check space (need cplen+1 chars including final \0) */ + tsize = csize + cplen; + repend = tsize; -static int __init of_bus_driver_init(void) -{ - return bus_register(&of_platform_bus_type); -} + if (csize>=len) /* @ the limit, all is already filled */ + return tsize; -postcore_initcall(of_bus_driver_init); - -int of_register_driver(struct of_platform_driver *drv) -{ - /* initialize common driver fields */ - drv->driver.name = drv->name; - drv->driver.bus = &of_platform_bus_type; + if (tsize>=len) { /* limit compat list */ + cplen = len-csize-1; + repend = len; + } - /* register with core */ - return driver_register(&drv->driver); -} + /* Copy and do char replacement */ + memcpy(&str[csize+1], compat, cplen); + for (i=csize; idriver); + return tsize; } - -static ssize_t dev_show_devspec(struct device *dev, struct device_attribute *attr, char *buf) +int of_device_uevent(struct device *dev, struct kobj_uevent_env *env) { struct of_device *ofdev; + const char *compat; + int seen = 0, cplen, sl; + + if (!dev) + return -ENODEV; ofdev = to_of_device(dev); - return sprintf(buf, "%s", ofdev->node->full_name); -} -static DEVICE_ATTR(devspec, S_IRUGO, dev_show_devspec, NULL); + if (add_uevent_var(env, "OF_NAME=%s", ofdev->node->name)) + return -ENOMEM; -/** - * 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; + if (add_uevent_var(env, "OF_TYPE=%s", ofdev->node->type)) + return -ENOMEM; - ofdev = to_of_device(dev); - of_node_put(ofdev->node); - kfree(ofdev); -} + /* 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. */ -int of_device_register(struct of_device *ofdev) -{ - int rc; + compat = of_get_property(ofdev->node, "compatible", &cplen); + while (compat && *compat && cplen > 0) { + if (add_uevent_var(env, "OF_COMPATIBLE_%d=%s", seen, compat)) + return -ENOMEM; - BUG_ON(ofdev->node == NULL); + sl = strlen (compat) + 1; + compat += sl; + cplen -= sl; + seen++; + } - rc = device_register(&ofdev->dev); - if (rc) - return rc; + if (add_uevent_var(env, "OF_COMPATIBLE_N=%d", seen)) + return -ENOMEM; - device_create_file(&ofdev->dev, &dev_attr_devspec); + /* modalias is trickier, we add it in 2 steps */ + if (add_uevent_var(env, "MODALIAS=")) + return -ENOMEM; + sl = of_device_get_modalias(ofdev, &env->buf[env->buflen-1], + sizeof(env->buf) - env->buflen); + if (sl >= (sizeof(env->buf) - env->buflen)) + return -ENOMEM; + env->buflen += sl; return 0; } - -void of_device_unregister(struct of_device *ofdev) -{ - device_remove_file(&ofdev->dev, &dev_attr_devspec); - - device_unregister(&ofdev->dev); -} - -struct of_device* of_platform_device_create(struct device_node *np, - const char *bus_id, - struct device *parent) -{ - struct of_device *dev; - - dev = kmalloc(sizeof(*dev), GFP_KERNEL); - if (!dev) - return NULL; - memset(dev, 0, sizeof(*dev)); - - dev->node = of_node_get(np); - dev->dma_mask = 0xffffffffUL; - dev->dev.dma_mask = &dev->dma_mask; - dev->dev.parent = parent; - dev->dev.bus = &of_platform_bus_type; - dev->dev.release = of_release_dev; - - strlcpy(dev->dev.bus_id, bus_id, BUS_ID_SIZE); - - if (of_device_register(dev) != 0) { - kfree(dev); - return NULL; - } - - return dev; -} - -EXPORT_SYMBOL(of_match_device); -EXPORT_SYMBOL(of_platform_bus_type); -EXPORT_SYMBOL(of_register_driver); -EXPORT_SYMBOL(of_unregister_driver); -EXPORT_SYMBOL(of_device_register); -EXPORT_SYMBOL(of_device_unregister); -EXPORT_SYMBOL(of_dev_get); -EXPORT_SYMBOL(of_dev_put); -EXPORT_SYMBOL(of_platform_device_create); -EXPORT_SYMBOL(of_release_dev); +EXPORT_SYMBOL(of_device_uevent); +EXPORT_SYMBOL(of_device_get_modalias);