diff options
Diffstat (limited to 'drivers/of/device.c')
| -rw-r--r-- | drivers/of/device.c | 300 |
1 files changed, 138 insertions, 162 deletions
diff --git a/drivers/of/device.c b/drivers/of/device.c index 27203bfd0b22..f7e75e527667 100644 --- a/drivers/of/device.c +++ b/drivers/of/device.c @@ -1,13 +1,13 @@ // SPDX-License-Identifier: GPL-2.0 -#include <linux/string.h> #include <linux/kernel.h> #include <linux/of.h> #include <linux/of_device.h> #include <linux/of_address.h> #include <linux/of_iommu.h> -#include <linux/dma-mapping.h> +#include <linux/of_reserved_mem.h> +#include <linux/dma-direct.h> /* for bus_dma_region */ +#include <linux/dma-map-ops.h> #include <linux/init.h> -#include <linux/module.h> #include <linux/mod_devicetable.h> #include <linux/slab.h> #include <linux/platform_device.h> @@ -17,8 +17,8 @@ /** * of_match_device - Tell if a struct device matches an of_device_id list - * @matches: array of of device match structures to search in - * @dev: the of device structure to match against + * @matches: array of of_device_id match structures to search in + * @dev: the OF device structure to match against * * Used by a driver to check whether an platform_device present in the * system is in its list of supported devices. @@ -26,58 +26,53 @@ const struct of_device_id *of_match_device(const struct of_device_id *matches, const struct device *dev) { - if ((!matches) || (!dev->of_node)) + if (!matches || !dev->of_node || dev->of_node_reused) return NULL; return of_match_node(matches, dev->of_node); } EXPORT_SYMBOL(of_match_device); -struct platform_device *of_dev_get(struct platform_device *dev) +static void +of_dma_set_restricted_buffer(struct device *dev, struct device_node *np) { - struct device *tmp; + struct device_node *of_node = dev->of_node; + struct of_phandle_iterator it; + int rc, i = 0; - if (!dev) - return NULL; - tmp = get_device(&dev->dev); - if (tmp) - return to_platform_device(tmp); - else - return NULL; -} -EXPORT_SYMBOL(of_dev_get); - -void of_dev_put(struct platform_device *dev) -{ - if (dev) - put_device(&dev->dev); -} -EXPORT_SYMBOL(of_dev_put); - -int of_device_add(struct platform_device *ofdev) -{ - BUG_ON(ofdev->dev.of_node == NULL); - - /* 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 = PLATFORM_DEVID_NONE; + if (!IS_ENABLED(CONFIG_DMA_RESTRICTED_POOL)) + return; /* - * If this device has not binding numa node in devicetree, that is - * of_node_to_nid returns NUMA_NO_NODE. device_add will assume that this - * device is on the same node as the parent. + * If dev->of_node doesn't exist or doesn't contain memory-region, try + * the OF node having DMA configuration. */ - set_dev_node(&ofdev->dev, of_node_to_nid(ofdev->dev.of_node)); + if (!of_property_present(of_node, "memory-region")) + of_node = np; + + of_for_each_phandle(&it, rc, of_node, "memory-region", NULL, 0) { + /* + * There might be multiple memory regions, but only one + * restricted-dma-pool region is allowed. + */ + if (of_device_is_compatible(it.node, "restricted-dma-pool") && + of_device_is_available(it.node)) { + if (of_reserved_mem_device_init_by_idx(dev, of_node, i)) + dev_warn(dev, "failed to initialise \"restricted-dma-pool\" memory node\n"); + of_node_put(it.node); + break; + } + i++; + } - return device_add(&ofdev->dev); } /** - * of_dma_configure - Setup DMA configuration + * of_dma_configure_id - Setup DMA configuration * @dev: Device to apply DMA configuration * @np: Pointer to OF node having DMA configuration * @force_dma: Whether device is to be set up by of_dma_configure() even if * DMA capability is not explicitly described by firmware. + * @id: Optional const pointer value input id * * Try to get devices's DMA configuration from DT and update it * accordingly. @@ -86,16 +81,27 @@ int of_device_add(struct platform_device *ofdev) * can use a platform bus notifier and handle BUS_NOTIFY_ADD_DEVICE events * to fix up DMA configuration. */ -int of_dma_configure(struct device *dev, struct device_node *np, bool force_dma) +int of_dma_configure_id(struct device *dev, struct device_node *np, + bool force_dma, const u32 *id) { - u64 dma_addr, paddr, size = 0; + const struct bus_dma_region *map = NULL; + struct device_node *bus_np; + u64 mask, end = 0; + bool coherent, set_map = false; int ret; - bool coherent; - unsigned long offset; - const struct iommu_ops *iommu; - u64 mask, end; - ret = of_dma_get_range(np, &dma_addr, &paddr, &size); + if (dev->dma_range_map) { + dev_dbg(dev, "dma_range_map already set\n"); + goto skip_map; + } + + if (np == dev->of_node) + bus_np = __of_get_dma_parent(np); + else + bus_np = of_node_get(np); + + ret = of_dma_get_range(bus_np, &map); + of_node_put(bus_np); if (ret < 0) { /* * For legacy reasons, we have to assume some devices need @@ -104,28 +110,12 @@ int of_dma_configure(struct device *dev, struct device_node *np, bool force_dma) */ if (!force_dma) return ret == -ENODEV ? 0 : ret; - - dma_addr = offset = 0; } else { - offset = PFN_DOWN(paddr - dma_addr); - - /* - * Add a work around to treat the size as mask + 1 in case - * it is defined in DT as a mask. - */ - if (size & 1) { - dev_warn(dev, "Invalid size 0x%llx for dma-range\n", - size); - size = size + 1; - } - - if (!size) { - dev_err(dev, "Adjusted size 0x%llx invalid\n", size); - return -EINVAL; - } - dev_dbg(dev, "dma_pfn_offset(%#08lx)\n", offset); + /* Determine the overall bounds of all DMA regions */ + end = dma_range_map_max(map); + set_map = true; } - +skip_map: /* * If @dev is expected to be DMA-capable then the bus code that created * it should have initialised its dma_mask pointer by this point. For @@ -137,54 +127,48 @@ int of_dma_configure(struct device *dev, struct device_node *np, bool force_dma) dev->dma_mask = &dev->coherent_dma_mask; } - if (!size && dev->coherent_dma_mask) - size = max(dev->coherent_dma_mask, dev->coherent_dma_mask + 1); - else if (!size) - size = 1ULL << 32; - - dev->dma_pfn_offset = offset; + if (!end && dev->coherent_dma_mask) + end = dev->coherent_dma_mask; + else if (!end) + end = (1ULL << 32) - 1; /* * Limit coherent and dma mask based on size and default mask * set by the driver. */ - end = dma_addr + size - 1; mask = DMA_BIT_MASK(ilog2(end) + 1); dev->coherent_dma_mask &= mask; *dev->dma_mask &= mask; - /* ...but only set bus limit if we found valid dma-ranges earlier */ - if (!ret) + /* ...but only set bus limit and range map if we found valid dma-ranges earlier */ + if (set_map) { dev->bus_dma_limit = end; + dev->dma_range_map = map; + } coherent = of_dma_is_coherent(np); dev_dbg(dev, "device is%sdma coherent\n", coherent ? " " : " not "); - iommu = of_iommu_configure(dev, np); - if (PTR_ERR(iommu) == -EPROBE_DEFER) + ret = of_iommu_configure(dev, np, id); + if (ret == -EPROBE_DEFER) { + /* Don't touch range map if it wasn't set from a valid dma-ranges */ + if (set_map) + dev->dma_range_map = NULL; + kfree(map); return -EPROBE_DEFER; - + } + /* Take all other IOMMU errors to mean we'll just carry on without it */ dev_dbg(dev, "device is%sbehind an iommu\n", - iommu ? " " : " not "); - - arch_setup_dma_ops(dev, dma_addr, size, iommu, coherent); + !ret ? " " : " not "); - return 0; -} -EXPORT_SYMBOL_GPL(of_dma_configure); + arch_setup_dma_ops(dev, coherent); -int of_device_register(struct platform_device *pdev) -{ - device_initialize(&pdev->dev); - return of_device_add(pdev); -} -EXPORT_SYMBOL(of_device_register); + if (ret) + of_dma_set_restricted_buffer(dev, np); -void of_device_unregister(struct platform_device *ofdev) -{ - device_unregister(&ofdev->dev); + return 0; } -EXPORT_SYMBOL(of_device_unregister); +EXPORT_SYMBOL_GPL(of_dma_configure_id); const void *of_device_get_match_data(const struct device *dev) { @@ -198,74 +182,20 @@ const void *of_device_get_match_data(const struct device *dev) } EXPORT_SYMBOL(of_device_get_match_data); -static ssize_t of_device_get_modalias(struct device *dev, char *str, ssize_t len) -{ - const char *compat; - char *c; - struct property *p; - ssize_t csize; - ssize_t tsize; - - if ((!dev) || (!dev->of_node)) - return -ENODEV; - - /* Name & Type */ - /* %p eats all alphanum characters, so %c must be used here */ - csize = snprintf(str, len, "of:N%pOFn%c%s", dev->of_node, 'T', - of_node_get_device_type(dev->of_node)); - tsize = csize; - len -= csize; - if (str) - str += csize; - - of_property_for_each_string(dev->of_node, "compatible", p, compat) { - csize = strlen(compat) + 1; - tsize += csize; - if (csize > len) - continue; - - csize = snprintf(str, len, "C%s", compat); - for (c = str; c; ) { - c = strchr(c, ' '); - if (c) - *c++ = '_'; - } - len -= csize; - str += csize; - } - - return tsize; -} - -int of_device_request_module(struct device *dev) -{ - char *str; - ssize_t size; - int ret; - - size = of_device_get_modalias(dev, NULL, 0); - if (size < 0) - return size; - - str = kmalloc(size + 1, GFP_KERNEL); - if (!str) - return -ENOMEM; - - of_device_get_modalias(dev, str, size); - str[size] = '\0'; - ret = request_module(str); - kfree(str); - - return ret; -} -EXPORT_SYMBOL_GPL(of_device_request_module); - /** * of_device_modalias - Fill buffer with newline terminated modalias string + * @dev: Calling device + * @str: Modalias string + * @len: Size of @str */ ssize_t of_device_modalias(struct device *dev, char *str, ssize_t len) { - ssize_t sl = of_device_get_modalias(dev, str, len - 2); + ssize_t sl; + + if (!dev || !dev->of_node || dev->of_node_reused) + return -ENODEV; + + sl = of_modalias(dev->of_node, str, len - 2); if (sl < 0) return sl; if (sl > len - 2) @@ -279,8 +209,10 @@ EXPORT_SYMBOL_GPL(of_device_modalias); /** * of_device_uevent - Display OF related uevent information + * @dev: Device to display the uevent information for + * @env: Kernel object's userspace event reference to fill up */ -void of_device_uevent(struct device *dev, struct kobj_uevent_env *env) +void of_device_uevent(const struct device *dev, struct kobj_uevent_env *env) { const char *compat, *type; struct alias_prop *app; @@ -316,20 +248,23 @@ void of_device_uevent(struct device *dev, struct kobj_uevent_env *env) } mutex_unlock(&of_mutex); } +EXPORT_SYMBOL_GPL(of_device_uevent); -int of_device_uevent_modalias(struct device *dev, struct kobj_uevent_env *env) +int of_device_uevent_modalias(const struct device *dev, struct kobj_uevent_env *env) { int sl; - if ((!dev) || (!dev->of_node)) + if ((!dev) || (!dev->of_node) || dev->of_node_reused) return -ENODEV; /* Devicetree modalias is tricky, 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); + sl = of_modalias(dev->of_node, &env->buf[env->buflen-1], + sizeof(env->buf) - env->buflen); + if (sl < 0) + return sl; if (sl >= (sizeof(env->buf) - env->buflen)) return -ENOMEM; env->buflen += sl; @@ -337,3 +272,44 @@ int of_device_uevent_modalias(struct device *dev, struct kobj_uevent_env *env) return 0; } EXPORT_SYMBOL_GPL(of_device_uevent_modalias); + +/** + * of_device_make_bus_id - Use the device node data to assign a unique name + * @dev: pointer to device structure that is linked to a device tree node + * + * This routine will first try using the translated bus address to + * derive a unique name. If it cannot, then it will prepend names from + * parent nodes until a unique name can be derived. + */ +void of_device_make_bus_id(struct device *dev) +{ + struct device_node *node = dev->of_node; + const __be32 *reg; + u64 addr; + u32 mask; + + /* Construct the name, using parent nodes if necessary to ensure uniqueness */ + while (node->parent) { + /* + * If the address can be translated, then that is as much + * uniqueness as we need. Make it the first component and return + */ + reg = of_get_property(node, "reg", NULL); + if (reg && (addr = of_translate_address(node, reg)) != OF_BAD_ADDR) { + if (!of_property_read_u32(node, "mask", &mask)) + dev_set_name(dev, dev_name(dev) ? "%llx.%x.%pOFn:%s" : "%llx.%x.%pOFn", + addr, ffs(mask) - 1, node, dev_name(dev)); + + else + dev_set_name(dev, dev_name(dev) ? "%llx.%pOFn:%s" : "%llx.%pOFn", + addr, node, dev_name(dev)); + return; + } + + /* format arguments only used if dev_name() resolves to NULL */ + dev_set_name(dev, dev_name(dev) ? "%s:%s" : "%s", + kbasename(node->full_name), dev_name(dev)); + node = node->parent; + } +} +EXPORT_SYMBOL_GPL(of_device_make_bus_id); |
