X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=drivers%2Fcore%2Fdevice.c;h=836bcadced508ddc0864827c0f0a5e8d1ca41bd3;hb=9450ab2ba8d720bd9f73bccc0af2e2b5a2c2aaf1;hp=f7fb0cc0fa7c60000153897316b1ef7ff5ae8e95;hpb=b8e599746cac1833328bc3a8e37eeefe346baf90;p=platform%2Fkernel%2Fu-boot.git diff --git a/drivers/core/device.c b/drivers/core/device.c index f7fb0cc..836bcad 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Device manager * @@ -5,32 +6,34 @@ * * (C) Copyright 2012 * Pavel Herrmann - * - * SPDX-License-Identifier: GPL-2.0+ */ #include #include +#include #include #include #include #include #include #include +#include #include #include +#include #include #include #include #include #include +#include DECLARE_GLOBAL_DATA_PTR; static int device_bind_common(struct udevice *parent, const struct driver *drv, const char *name, void *platdata, - ulong driver_data, int of_offset, - struct udevice **devp) + ulong driver_data, ofnode node, + uint of_platdata_size, struct udevice **devp) { struct udevice *dev; struct uclass *uc; @@ -60,7 +63,7 @@ static int device_bind_common(struct udevice *parent, const struct driver *drv, 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; @@ -76,20 +79,35 @@ static int device_bind_common(struct udevice *parent, const struct driver *drv, * 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); + } } } @@ -145,7 +163,7 @@ static int device_bind_common(struct udevice *parent, const struct driver *drv, } if (parent) - dm_dbg("Bound device %s to %s\n", dev->name, parent->name); + pr_debug("Bound device %s to %s\n", dev->name, parent->name); if (devp) *devp = dev; @@ -198,18 +216,26 @@ fail_alloc1: int device_bind_with_driver_data(struct udevice *parent, const struct driver *drv, const char *name, - ulong driver_data, int of_offset, + ulong driver_data, ofnode node, struct udevice **devp) { - return device_bind_common(parent, drv, name, NULL, driver_data, - of_offset, 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, of_offset, + return device_bind_common(parent, drv, name, platdata, 0, + offset_to_ofnode(of_offset), 0, devp); +} + +int device_bind_ofnode(struct udevice *parent, const struct driver *drv, + const char *name, void *platdata, ofnode node, + struct udevice **devp) +{ + return device_bind_common(parent, drv, name, platdata, 0, node, 0, devp); } @@ -217,6 +243,7 @@ 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) @@ -224,8 +251,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) @@ -233,9 +264,38 @@ static void *alloc_priv(int size, uint flags) void *priv; if (flags & DM_FLAG_ALLOC_PRIV_DMA) { + size = ROUND(size, ARCH_DMA_MINALIGN); 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); } @@ -245,6 +305,7 @@ static void *alloc_priv(int size, uint flags) int device_probe(struct udevice *dev) { + struct power_domain pd; const struct driver *drv; int size = 0; int ret; @@ -270,7 +331,8 @@ int device_probe(struct udevice *dev) /* Allocate private data if requested and not reentered */ size = dev->uclass->uc_drv->per_device_auto_alloc_size; if (size && !dev->uclass_priv) { - dev->uclass_priv = calloc(1, size); + dev->uclass_priv = alloc_priv(size, + dev->uclass->uc_drv->flags); if (!dev->uclass_priv) { ret = -ENOMEM; goto fail; @@ -324,6 +386,11 @@ int device_probe(struct udevice *dev) if (dev->parent && device_get_uclass_id(dev) != UCLASS_PINCTRL) pinctrl_select_state(dev, "default"); + if (dev->parent && device_get_uclass_id(dev) != UCLASS_POWER_DOMAIN) { + if (!power_domain_get(dev, &pd)) + power_domain_on(&pd); + } + ret = uclass_pre_probe_device(dev); if (ret) goto fail; @@ -334,12 +401,17 @@ int device_probe(struct udevice *dev) 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; } + /* Process 'assigned-{clocks/clock-parents/clock-rates}' properties */ + ret = clk_set_defaults(dev); + if (ret) + goto fail; + if (drv->probe) { ret = drv->probe(dev); if (ret) { @@ -357,7 +429,7 @@ int device_probe(struct udevice *dev) 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); } @@ -370,7 +442,7 @@ fail: return ret; } -void *dev_get_platdata(struct udevice *dev) +void *dev_get_platdata(const struct udevice *dev) { if (!dev) { dm_warn("%s: null device\n", __func__); @@ -380,7 +452,7 @@ void *dev_get_platdata(struct udevice *dev) return dev->platdata; } -void *dev_get_parent_platdata(struct udevice *dev) +void *dev_get_parent_platdata(const struct udevice *dev) { if (!dev) { dm_warn("%s: null device\n", __func__); @@ -390,7 +462,7 @@ void *dev_get_parent_platdata(struct udevice *dev) return dev->parent_platdata; } -void *dev_get_uclass_platdata(struct udevice *dev) +void *dev_get_uclass_platdata(const struct udevice *dev) { if (!dev) { dm_warn("%s: null device\n", __func__); @@ -400,7 +472,7 @@ void *dev_get_uclass_platdata(struct udevice *dev) return dev->uclass_platdata; } -void *dev_get_priv(struct udevice *dev) +void *dev_get_priv(const struct udevice *dev) { if (!dev) { dm_warn("%s: null device\n", __func__); @@ -410,7 +482,7 @@ void *dev_get_priv(struct udevice *dev) return dev->priv; } -void *dev_get_uclass_priv(struct udevice *dev) +void *dev_get_uclass_priv(const struct udevice *dev) { if (!dev) { dm_warn("%s: null device\n", __func__); @@ -420,7 +492,7 @@ void *dev_get_uclass_priv(struct udevice *dev) return dev->uclass_priv; } -void *dev_get_parent_priv(struct udevice *dev) +void *dev_get_parent_priv(const struct udevice *dev) { if (!dev) { dm_warn("%s: null device\n", __func__); @@ -445,6 +517,33 @@ static int device_get_device_tail(struct udevice *dev, int ret, return 0; } +/** + * device_find_by_ofnode() - Return device associated with given ofnode + * + * The returned device is *not* activated. + * + * @node: The ofnode for which a associated device should be looked up + * @devp: Pointer to structure to hold the found device + * Return: 0 if OK, -ve on error + */ +static int device_find_by_ofnode(ofnode node, struct udevice **devp) +{ + struct uclass *uc; + struct udevice *dev; + int ret; + + list_for_each_entry(uc, &gd->uclass_root, sibling_node) { + ret = uclass_find_device_by_ofnode(uc->uc_drv->id, node, + &dev); + if (!ret || dev) { + *devp = dev; + return 0; + } + } + + return -ENODEV; +} + int device_get_child(struct udevice *parent, int index, struct udevice **devp) { struct udevice *dev; @@ -503,7 +602,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; } @@ -523,16 +622,16 @@ int device_get_child_by_of_offset(struct udevice *parent, int node, return device_get_device_tail(dev, ret, devp); } -static struct udevice *_device_find_global_by_of_offset(struct udevice *parent, - int of_offset) +static struct udevice *_device_find_global_by_ofnode(struct udevice *parent, + ofnode ofnode) { struct udevice *dev, *found; - if (parent->of_offset == of_offset) + if (ofnode_equal(dev_ofnode(parent), ofnode)) return parent; list_for_each_entry(dev, &parent->child_head, sibling_node) { - found = _device_find_global_by_of_offset(dev, of_offset); + found = _device_find_global_by_ofnode(dev, ofnode); if (found) return found; } @@ -540,11 +639,18 @@ static struct udevice *_device_find_global_by_of_offset(struct udevice *parent, return NULL; } -int device_get_global_by_of_offset(int of_offset, struct udevice **devp) +int device_find_global_by_ofnode(ofnode ofnode, struct udevice **devp) +{ + *devp = _device_find_global_by_ofnode(gd->dm_root, ofnode); + + return *devp ? 0 : -ENOENT; +} + +int device_get_global_by_ofnode(ofnode ofnode, struct udevice **devp) { struct udevice *dev; - dev = _device_find_global_by_of_offset(gd->dm_root, of_offset); + dev = _device_find_global_by_ofnode(gd->dm_root, ofnode); return device_get_device_tail(dev, dev ? 0 : -ENOENT, devp); } @@ -575,140 +681,90 @@ int device_find_next_child(struct udevice **devp) return 0; } -struct udevice *dev_get_parent(struct udevice *child) -{ - return child->parent; -} - -ulong dev_get_driver_data(struct udevice *dev) +int device_find_first_inactive_child(struct udevice *parent, + enum uclass_id uclass_id, + struct udevice **devp) { - return dev->driver_data; -} + struct udevice *dev; -const void *dev_get_driver_ops(struct udevice *dev) -{ - if (!dev || !dev->driver->ops) - return NULL; + *devp = NULL; + list_for_each_entry(dev, &parent->child_head, sibling_node) { + if (!device_active(dev) && + device_get_uclass_id(dev) == uclass_id) { + *devp = dev; + return 0; + } + } - return dev->driver->ops; + return -ENODEV; } -enum uclass_id device_get_uclass_id(struct udevice *dev) +int device_find_first_child_by_uclass(struct udevice *parent, + enum uclass_id uclass_id, + struct udevice **devp) { - return dev->uclass->uc_drv->id; -} + struct udevice *dev; -const char *dev_get_uclass_name(struct udevice *dev) -{ - if (!dev) - return NULL; + *devp = NULL; + list_for_each_entry(dev, &parent->child_head, sibling_node) { + if (device_get_uclass_id(dev) == uclass_id) { + *devp = dev; + return 0; + } + } - return dev->uclass->uc_drv->name; + return -ENODEV; } -fdt_addr_t dev_get_addr_index(struct udevice *dev, int index) +int device_find_child_by_name(struct udevice *parent, const char *name, + struct udevice **devp) { -#if CONFIG_IS_ENABLED(OF_CONTROL) - fdt_addr_t addr; - - if (CONFIG_IS_ENABLED(OF_TRANSLATE)) { - const fdt32_t *reg; - int len = 0; - int na, ns; - - na = fdt_address_cells(gd->fdt_blob, dev->parent->of_offset); - if (na < 1) { - debug("bad #address-cells\n"); - return FDT_ADDR_T_NONE; - } - - ns = fdt_size_cells(gd->fdt_blob, dev->parent->of_offset); - if (ns < 0) { - debug("bad #size-cells\n"); - return FDT_ADDR_T_NONE; - } - - reg = fdt_getprop(gd->fdt_blob, dev->of_offset, "reg", &len); - if (!reg || (len <= (index * sizeof(fdt32_t) * (na + ns)))) { - debug("Req index out of range\n"); - return FDT_ADDR_T_NONE; - } + struct udevice *dev; - reg += index * (na + ns); + *devp = NULL; - /* - * Use the full-fledged translate function for complex - * bus setups. - */ - addr = fdt_translate_address((void *)gd->fdt_blob, - dev->of_offset, reg); - } else { - /* - * 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", - index, 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); + list_for_each_entry(dev, &parent->child_head, sibling_node) { + if (!strcmp(dev->name, name)) { + *devp = dev; + return 0; } } - /* - * Some platforms need a special address translation. Those - * platforms (e.g. mvebu in SPL) can configure a translation - * offset in the DM by calling dm_set_translation_offset() that - * will get added to all addresses returned by dev_get_addr(). - */ - addr += dm_get_translation_offset(); - - return addr; -#else - return FDT_ADDR_T_NONE; -#endif + return -ENODEV; } -fdt_addr_t dev_get_addr_name(struct udevice *dev, const char *name) +struct udevice *dev_get_parent(const struct udevice *child) { -#if CONFIG_IS_ENABLED(OF_CONTROL) - int index; - - index = fdt_find_string(gd->fdt_blob, dev->of_offset, "reg-names", - name); - if (index < 0) - return index; - - return dev_get_addr_index(dev, index); -#else - return FDT_ADDR_T_NONE; -#endif + return child->parent; } -fdt_addr_t dev_get_addr(struct udevice *dev) +ulong dev_get_driver_data(const struct udevice *dev) { - return dev_get_addr_index(dev, 0); + return dev->driver_data; } -void *dev_get_addr_ptr(struct udevice *dev) +const void *dev_get_driver_ops(const struct udevice *dev) { - return (void *)(uintptr_t)dev_get_addr_index(dev, 0); + if (!dev || !dev->driver->ops) + return NULL; + + return dev->driver->ops; } -void *dev_map_physmem(struct udevice *dev, unsigned long size) +enum uclass_id device_get_uclass_id(const struct udevice *dev) { - fdt_addr_t addr = dev_get_addr(dev); + return dev->uclass->uc_drv->id; +} - if (addr == FDT_ADDR_T_NONE) +const char *dev_get_uclass_name(const struct udevice *dev) +{ + if (!dev) return NULL; - return map_physmem(addr, size, MAP_NOCACHE); + return dev->uclass->uc_drv->name; } -bool device_has_children(struct udevice *dev) +bool device_has_children(const struct udevice *dev) { return !list_empty(&dev->child_head); } @@ -738,7 +794,7 @@ bool device_is_last_sibling(struct udevice *dev) void device_set_name_alloced(struct udevice *dev) { - dev->flags |= DM_NAME_ALLOCED; + dev->flags |= DM_FLAG_NAME_ALLOCED; } int device_set_name(struct udevice *dev, const char *name) @@ -752,11 +808,9 @@ int device_set_name(struct udevice *dev, const char *name) return 0; } -bool of_device_is_compatible(struct udevice *dev, const char *compat) +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, compat); + return ofnode_device_is_compatible(dev_ofnode(dev), compat); } bool of_machine_is_compatible(const char *compat) @@ -765,3 +819,54 @@ bool of_machine_is_compatible(const char *compat) return !fdt_node_check_compatible(fdt, 0, compat); } + +int dev_disable_by_path(const char *path) +{ + struct uclass *uc; + ofnode node = ofnode_path(path); + struct udevice *dev; + int ret = 1; + + if (!of_live_active()) + return -ENOSYS; + + list_for_each_entry(uc, &gd->uclass_root, sibling_node) { + ret = uclass_find_device_by_ofnode(uc->uc_drv->id, node, &dev); + if (!ret) + break; + } + + if (ret) + return ret; + + ret = device_remove(dev, DM_REMOVE_NORMAL); + if (ret) + return ret; + + ret = device_unbind(dev); + if (ret) + return ret; + + return ofnode_set_enabled(node, false); +} + +int dev_enable_by_path(const char *path) +{ + ofnode node = ofnode_path(path); + ofnode pnode = ofnode_get_parent(node); + struct udevice *parent; + int ret = 1; + + if (!of_live_active()) + return -ENOSYS; + + ret = device_find_by_ofnode(pnode, &parent); + if (ret) + return ret; + + ret = ofnode_set_enabled(node, true); + if (ret) + return ret; + + return lists_bind_fdt(parent, node, NULL, false); +}