X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=drivers%2Fcore%2Fdevice.c;h=5463d1ffa508df95fb65f24f058ecbceb8387989;hb=1c7b5d0309c1da3952b1236d42c82c5ea8446476;hp=758f39064cca9a4b288733cce1a1d8c1c00ff3ee;hpb=e573bdb324c78fac56655a493bea843842c9d9f8;p=oweals%2Fu-boot.git diff --git a/drivers/core/device.c b/drivers/core/device.c index 758f39064c..5463d1ffa5 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -10,6 +10,7 @@ */ #include +#include #include #include #include @@ -18,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -26,9 +28,10 @@ DECLARE_GLOBAL_DATA_PTR; -int device_bind(struct udevice *parent, const struct driver *drv, - const char *name, void *platdata, int of_offset, - struct udevice **devp) +static int device_bind_common(struct udevice *parent, const struct driver *drv, + const char *name, void *platdata, + ulong driver_data, ofnode node, + uint of_platdata_size, struct udevice **devp) { struct udevice *dev; struct uclass *uc; @@ -56,37 +59,53 @@ int device_bind(struct udevice *parent, const struct driver *drv, INIT_LIST_HEAD(&dev->devres_head); #endif dev->platdata = platdata; + dev->driver_data = driver_data; dev->name = name; - dev->of_offset = of_offset; + dev->node = node; dev->parent = parent; dev->driver = drv; dev->uclass = uc; dev->seq = -1; dev->req_seq = -1; - if (CONFIG_IS_ENABLED(OF_CONTROL) && IS_ENABLED(CONFIG_DM_SEQ_ALIAS)) { + if (CONFIG_IS_ENABLED(OF_CONTROL) && CONFIG_IS_ENABLED(DM_SEQ_ALIAS)) { /* - * Some devices, such as a SPI bus, I2C bus and serial ports - * are numbered using aliases. - * - * This is just a 'requested' sequence, and will be - * resolved (and ->seq updated) when the device is probed. - */ + * Some devices, such as a SPI bus, I2C bus and serial ports + * are numbered using aliases. + * + * This is just a 'requested' sequence, and will be + * resolved (and ->seq updated) when the device is probed. + */ if (uc->uc_drv->flags & DM_UC_FLAG_SEQ_ALIAS) { - if (uc->uc_drv->name && of_offset != -1) { - fdtdec_get_alias_seq(gd->fdt_blob, - uc->uc_drv->name, of_offset, - &dev->req_seq); + if (uc->uc_drv->name && ofnode_valid(node)) { + dev_read_alias_seq(dev, &dev->req_seq); } } } - if (!dev->platdata && drv->platdata_auto_alloc_size) { - dev->flags |= DM_FLAG_ALLOC_PDATA; - dev->platdata = calloc(1, drv->platdata_auto_alloc_size); - if (!dev->platdata) { - ret = -ENOMEM; - goto fail_alloc1; + if (drv->platdata_auto_alloc_size) { + bool alloc = !platdata; + + if (CONFIG_IS_ENABLED(OF_PLATDATA)) { + if (of_platdata_size) { + dev->flags |= DM_FLAG_OF_PLATDATA; + if (of_platdata_size < + drv->platdata_auto_alloc_size) + alloc = true; + } + } + if (alloc) { + dev->flags |= DM_FLAG_ALLOC_PDATA; + dev->platdata = calloc(1, + drv->platdata_auto_alloc_size); + if (!dev->platdata) { + ret = -ENOMEM; + goto fail_alloc1; + } + if (CONFIG_IS_ENABLED(OF_PLATDATA) && platdata) { + memcpy(dev->platdata, platdata, + of_platdata_size); + } } } @@ -135,6 +154,11 @@ int device_bind(struct udevice *parent, const struct driver *drv, if (ret) goto fail_child_post_bind; } + if (uc->uc_drv->post_bind) { + ret = uc->uc_drv->post_bind(dev); + if (ret) + goto fail_uclass_post_bind; + } if (parent) dm_dbg("Bound device %s to %s\n", dev->name, parent->name); @@ -145,6 +169,8 @@ int device_bind(struct udevice *parent, const struct driver *drv, return 0; +fail_uclass_post_bind: + /* There is no child unbind() method, so no clean-up required */ fail_child_post_bind: if (CONFIG_IS_ENABLED(DM_DEVICE_REMOVE)) { if (drv->unbind && drv->unbind(dev)) { @@ -186,10 +212,28 @@ fail_alloc1: return ret; } +int device_bind_with_driver_data(struct udevice *parent, + const struct driver *drv, const char *name, + ulong driver_data, ofnode node, + struct udevice **devp) +{ + return device_bind_common(parent, drv, name, NULL, driver_data, node, + 0, devp); +} + +int device_bind(struct udevice *parent, const struct driver *drv, + const char *name, void *platdata, int of_offset, + struct udevice **devp) +{ + return device_bind_common(parent, drv, name, platdata, 0, + offset_to_ofnode(of_offset), 0, devp); +} + int device_bind_by_name(struct udevice *parent, bool pre_reloc_only, const struct driver_info *info, struct udevice **devp) { struct driver *drv; + uint platdata_size = 0; drv = lists_driver_lookup_name(info->name); if (!drv) @@ -197,8 +241,12 @@ int device_bind_by_name(struct udevice *parent, bool pre_reloc_only, if (pre_reloc_only && !(drv->flags & DM_FLAG_PRE_RELOC)) return -EPERM; - return device_bind(parent, drv, info->name, (void *)info->platdata, - -1, devp); +#if CONFIG_IS_ENABLED(OF_PLATDATA) + platdata_size = info->platdata_size; +#endif + return device_bind_common(parent, drv, info->name, + (void *)info->platdata, 0, ofnode_null(), platdata_size, + devp); } static void *alloc_priv(int size, uint flags) @@ -207,8 +255,36 @@ static void *alloc_priv(int size, uint flags) if (flags & DM_FLAG_ALLOC_PRIV_DMA) { priv = memalign(ARCH_DMA_MINALIGN, size); - if (priv) + if (priv) { memset(priv, '\0', size); + + /* + * Ensure that the zero bytes are flushed to memory. + * This prevents problems if the driver uses this as + * both an input and an output buffer: + * + * 1. Zeroes written to buffer (here) and sit in the + * cache + * 2. Driver issues a read command to DMA + * 3. CPU runs out of cache space and evicts some cache + * data in the buffer, writing zeroes to RAM from + * the memset() above + * 4. DMA completes + * 5. Buffer now has some DMA data and some zeroes + * 6. Data being read is now incorrect + * + * To prevent this, ensure that the cache is clean + * within this range at the start. The driver can then + * use normal flush-after-write, invalidate-before-read + * procedures. + * + * TODO(sjg@chromium.org): Drop this microblaze + * exception. + */ +#ifndef CONFIG_MICROBLAZE + flush_dcache_range((ulong)priv, (ulong)priv + size); +#endif + } } else { priv = calloc(1, size); } @@ -216,7 +292,7 @@ static void *alloc_priv(int size, uint flags) return priv; } -int device_probe_child(struct udevice *dev, void *parent_priv) +int device_probe(struct udevice *dev) { const struct driver *drv; int size = 0; @@ -263,8 +339,6 @@ int device_probe_child(struct udevice *dev, void *parent_priv) ret = -ENOMEM; goto fail; } - if (parent_priv) - memcpy(dev->parent_priv, parent_priv, size); } ret = device_probe(dev->parent); @@ -292,9 +366,11 @@ int device_probe_child(struct udevice *dev, void *parent_priv) /* * Process pinctrl for everything except the root device, and - * continue regardless of the result of pinctrl. + * continue regardless of the result of pinctrl. Don't process pinctrl + * settings for pinctrl devices since the device may not yet be + * probed. */ - if (dev->parent) + if (dev->parent && device_get_uclass_id(dev) != UCLASS_PINCTRL) pinctrl_select_state(dev, "default"); ret = uclass_pre_probe_device(dev); @@ -307,7 +383,7 @@ int device_probe_child(struct udevice *dev, void *parent_priv) goto fail; } - if (drv->ofdata_to_platdata && dev->of_offset >= 0) { + if (drv->ofdata_to_platdata && dev_has_of_node(dev)) { ret = drv->ofdata_to_platdata(dev); if (ret) goto fail; @@ -325,9 +401,12 @@ int device_probe_child(struct udevice *dev, void *parent_priv) if (ret) goto fail_uclass; + if (dev->parent && device_get_uclass_id(dev) == UCLASS_PINCTRL) + pinctrl_select_state(dev, "default"); + return 0; fail_uclass: - if (device_remove(dev)) { + if (device_remove(dev, DM_REMOVE_NORMAL)) { dm_warn("%s: Device '%s' failed to remove on error path\n", __func__, dev->name); } @@ -340,11 +419,6 @@ fail: return ret; } -int device_probe(struct udevice *dev) -{ - return device_probe_child(dev, NULL); -} - void *dev_get_platdata(struct udevice *dev) { if (!dev) { @@ -478,7 +552,7 @@ int device_find_child_by_of_offset(struct udevice *parent, int of_offset, *devp = NULL; list_for_each_entry(dev, &parent->child_head, sibling_node) { - if (dev->of_offset == of_offset) { + if (dev_of_offset(dev) == of_offset) { *devp = dev; return 0; } @@ -503,7 +577,7 @@ static struct udevice *_device_find_global_by_of_offset(struct udevice *parent, { struct udevice *dev, *found; - if (parent->of_offset == of_offset) + if (dev_of_offset(parent) == of_offset) return parent; list_for_each_entry(dev, &parent->child_head, sibling_node) { @@ -581,45 +655,6 @@ const char *dev_get_uclass_name(struct udevice *dev) return dev->uclass->uc_drv->name; } -fdt_addr_t dev_get_addr(struct udevice *dev) -{ -#if CONFIG_IS_ENABLED(OF_CONTROL) - fdt_addr_t addr; - - if (CONFIG_IS_ENABLED(OF_TRANSLATE)) { - const fdt32_t *reg; - - reg = fdt_getprop(gd->fdt_blob, dev->of_offset, "reg", NULL); - if (!reg) - return FDT_ADDR_T_NONE; - - /* - * Use the full-fledged translate function for complex - * bus setups. - */ - return fdt_translate_address((void *)gd->fdt_blob, - dev->of_offset, reg); - } - - /* - * Use the "simple" translate function for less complex - * bus setups. - */ - addr = fdtdec_get_addr_size_auto_parent(gd->fdt_blob, - dev->parent->of_offset, - dev->of_offset, "reg", - 0, NULL); - if (CONFIG_IS_ENABLED(SIMPLE_BUS) && addr != FDT_ADDR_T_NONE) { - if (device_get_uclass_id(dev->parent) == UCLASS_SIMPLE_BUS) - addr = simple_bus_translate(dev->parent, addr); - } - - return addr; -#else - return FDT_ADDR_T_NONE; -#endif -} - bool device_has_children(struct udevice *dev) { return !list_empty(&dev->child_head); @@ -648,12 +683,32 @@ bool device_is_last_sibling(struct udevice *dev) return list_is_last(&dev->sibling_node, &parent->child_head); } +void device_set_name_alloced(struct udevice *dev) +{ + dev->flags |= DM_FLAG_NAME_ALLOCED; +} + int device_set_name(struct udevice *dev, const char *name) { name = strdup(name); if (!name) return -ENOMEM; dev->name = name; + device_set_name_alloced(dev); return 0; } + +bool device_is_compatible(struct udevice *dev, const char *compat) +{ + const void *fdt = gd->fdt_blob; + + return !fdt_node_check_compatible(fdt, dev_of_offset(dev), compat); +} + +bool of_machine_is_compatible(const char *compat) +{ + const void *fdt = gd->fdt_blob; + + return !fdt_node_check_compatible(fdt, 0, compat); +}