diff options
Diffstat (limited to 'drivers/of/address.c')
| -rw-r--r-- | drivers/of/address.c | 680 |
1 files changed, 374 insertions, 306 deletions
diff --git a/drivers/of/address.c b/drivers/of/address.c index c34ac33b7338..4034d798c55a 100644 --- a/drivers/of/address.c +++ b/drivers/of/address.c @@ -8,6 +8,7 @@ #include <linux/logic_pio.h> #include <linux/module.h> #include <linux/of_address.h> +#include <linux/overflow.h> #include <linux/pci.h> #include <linux/pci_regs.h> #include <linux/sizes.h> @@ -15,30 +16,12 @@ #include <linux/string.h> #include <linux/dma-direct.h> /* for bus_dma_region */ -#include "of_private.h" - -/* Max address size we deal with */ -#define OF_MAX_ADDR_CELLS 4 -#define OF_CHECK_ADDR_COUNT(na) ((na) > 0 && (na) <= OF_MAX_ADDR_CELLS) -#define OF_CHECK_COUNTS(na, ns) (OF_CHECK_ADDR_COUNT(na) && (ns) > 0) +#include <kunit/visibility.h> -static struct of_bus *of_match_bus(struct device_node *np); -static int __of_address_to_resource(struct device_node *dev, int index, - int bar_no, struct resource *r); -static bool of_mmio_is_nonposted(struct device_node *np); +/* Uncomment me to enable of_dump_addr() debugging output */ +// #define DEBUG -/* Debug utility */ -#ifdef DEBUG -static void of_dump_addr(const char *s, const __be32 *addr, int na) -{ - pr_debug("%s", s); - while (na--) - pr_cont(" %08x", be32_to_cpu(*(addr++))); - pr_cont("\n"); -} -#else -static void of_dump_addr(const char *s, const __be32 *addr, int na) { } -#endif +#include "of_private.h" /* Callbacks for bus specific translators */ struct of_bus { @@ -48,9 +31,9 @@ struct of_bus { void (*count_cells)(struct device_node *child, int *addrc, int *sizec); u64 (*map)(__be32 *addr, const __be32 *range, - int na, int ns, int pna); + int na, int ns, int pna, int fna); int (*translate)(__be32 *addr, u64 offset, int na); - bool has_flags; + int flag_cells; unsigned int (*get_flags)(const __be32 *addr); }; @@ -68,13 +51,13 @@ static void of_bus_default_count_cells(struct device_node *dev, } static u64 of_bus_default_map(__be32 *addr, const __be32 *range, - int na, int ns, int pna) + int na, int ns, int pna, int fna) { u64 cp, s, da; - cp = of_read_number(range, na); + cp = of_read_number(range + fna, na - fna); s = of_read_number(range + na + pna, ns); - da = of_read_number(addr, na); + da = of_read_number(addr + fna, na - fna); pr_debug("default map, cp=%llx, s=%llx, da=%llx\n", cp, s, da); @@ -95,11 +78,32 @@ static int of_bus_default_translate(__be32 *addr, u64 offset, int na) return 0; } +static unsigned int of_bus_default_flags_get_flags(const __be32 *addr) +{ + return of_read_number(addr, 1); +} + static unsigned int of_bus_default_get_flags(const __be32 *addr) { return IORESOURCE_MEM; } +static u64 of_bus_default_flags_map(__be32 *addr, const __be32 *range, int na, + int ns, int pna, int fna) +{ + /* Check that flags match */ + if (*addr != *range) + return OF_BAD_ADDR; + + return of_bus_default_map(addr, range, na, ns, pna, fna); +} + +static int of_bus_default_flags_translate(__be32 *addr, u64 offset, int na) +{ + /* Keep "flags" part (high cell) in translated address */ + return of_bus_default_translate(addr + 1, offset, na - 1); +} + #ifdef CONFIG_PCI static unsigned int of_bus_pci_get_flags(const __be32 *addr) { @@ -130,7 +134,7 @@ static unsigned int of_bus_pci_get_flags(const __be32 *addr) * PCI bus specific translator */ -static bool of_node_is_pcie(struct device_node *np) +static bool of_node_is_pcie(const struct device_node *np) { bool is_pcie = of_node_name_eq(np, "pcie"); @@ -165,9 +169,8 @@ static void of_bus_pci_count_cells(struct device_node *np, } static u64 of_bus_pci_map(__be32 *addr, const __be32 *range, int na, int ns, - int pna) + int pna, int fna) { - u64 cp, s, da; unsigned int af, rf; af = of_bus_pci_get_flags(addr); @@ -177,34 +180,26 @@ static u64 of_bus_pci_map(__be32 *addr, const __be32 *range, int na, int ns, if ((af ^ rf) & (IORESOURCE_MEM | IORESOURCE_IO)) return OF_BAD_ADDR; - /* Read address values, skipping high cell */ - cp = of_read_number(range + 1, na - 1); - s = of_read_number(range + na + pna, ns); - da = of_read_number(addr + 1, na - 1); - - pr_debug("PCI map, cp=%llx, s=%llx, da=%llx\n", cp, s, da); - - if (da < cp || da >= (cp + s)) - return OF_BAD_ADDR; - return da - cp; + return of_bus_default_map(addr, range, na, ns, pna, fna); } -static int of_bus_pci_translate(__be32 *addr, u64 offset, int na) -{ - return of_bus_default_translate(addr + 1, offset, na - 1); -} #endif /* CONFIG_PCI */ -int of_pci_address_to_resource(struct device_node *dev, int bar, - struct resource *r) +VISIBLE_IF_KUNIT int __of_address_resource_bounds(struct resource *r, u64 start, u64 size) { + if (overflows_type(start, r->start)) + return -EOVERFLOW; - if (!IS_ENABLED(CONFIG_PCI)) - return -ENOSYS; + r->start = start; - return __of_address_to_resource(dev, -1, bar, r); + if (!size) + r->end = wrapping_sub(typeof(r->end), r->start, 1); + else if (size && check_add_overflow(r->start, size - 1, &r->end)) + return -EOVERFLOW; + + return 0; } -EXPORT_SYMBOL_GPL(of_pci_address_to_resource); +EXPORT_SYMBOL_IF_KUNIT(__of_address_resource_bounds); /* * of_pci_range_to_resource - Create a resource from an of_pci_range @@ -213,7 +208,7 @@ EXPORT_SYMBOL_GPL(of_pci_address_to_resource); * @res: pointer to a valid resource that will be updated to * reflect the values contained in the range. * - * Returns EINVAL if the range cannot be converted to resource. + * Returns -EINVAL if the range cannot be converted to resource. * * Note that if the range is an IO range, the resource will be converted * using pci_address_to_pio() which can fail if it is called too early or @@ -221,17 +216,15 @@ EXPORT_SYMBOL_GPL(of_pci_address_to_resource); * To guard against that we try to register the IO range first. * If that fails we know that pci_address_to_pio() will do too. */ -int of_pci_range_to_resource(struct of_pci_range *range, - struct device_node *np, struct resource *res) +int of_pci_range_to_resource(const struct of_pci_range *range, + const struct device_node *np, struct resource *res) { + u64 start; int err; res->flags = range->flags; res->parent = res->child = res->sibling = NULL; res->name = np->full_name; - if (!IS_ENABLED(CONFIG_PCI)) - return -ENOSYS; - if (res->flags & IORESOURCE_IO) { unsigned long port; err = pci_register_io_range(&np->fwnode, range->cpu_addr, @@ -243,18 +236,11 @@ int of_pci_range_to_resource(struct of_pci_range *range, err = -EINVAL; goto invalid_range; } - res->start = port; + start = port; } else { - if ((sizeof(resource_size_t) < 8) && - upper_32_bits(range->cpu_addr)) { - err = -EINVAL; - goto invalid_range; - } - - res->start = range->cpu_addr; + start = range->cpu_addr; } - res->end = res->start + range->size - 1; - return 0; + return __of_address_resource_bounds(res, start, range->size); invalid_range: res->start = (resource_size_t)OF_BAD_ADDR; @@ -264,6 +250,34 @@ invalid_range: EXPORT_SYMBOL(of_pci_range_to_resource); /* + * of_range_to_resource - Create a resource from a ranges entry + * @np: device node where the range belongs to + * @index: the 'ranges' index to convert to a resource + * @res: pointer to a valid resource that will be updated to + * reflect the values contained in the range. + * + * Returns -ENOENT if the entry is not found or -EOVERFLOW if the range + * cannot be converted to resource. + */ +int of_range_to_resource(struct device_node *np, int index, struct resource *res) +{ + int ret, i = 0; + struct of_range_parser parser; + struct of_range range; + + ret = of_range_parser_init(&parser, np); + if (ret) + return ret; + + for_each_of_range(&parser, &range) + if (i++ == index) + return of_pci_range_to_resource(&range, np, res); + + return -ENOENT; +} +EXPORT_SYMBOL(of_range_to_resource); + +/* * ISA bus specific translator */ @@ -282,29 +296,13 @@ static void of_bus_isa_count_cells(struct device_node *child, } static u64 of_bus_isa_map(__be32 *addr, const __be32 *range, int na, int ns, - int pna) + int pna, int fna) { - u64 cp, s, da; - /* Check address type match */ if ((addr[0] ^ range[0]) & cpu_to_be32(1)) return OF_BAD_ADDR; - /* Read address values, skipping high cell */ - cp = of_read_number(range + 1, na - 1); - s = of_read_number(range + na + pna, ns); - da = of_read_number(addr + 1, na - 1); - - pr_debug("ISA map, cp=%llx, s=%llx, da=%llx\n", cp, s, da); - - if (da < cp || da >= (cp + s)) - return OF_BAD_ADDR; - return da - cp; -} - -static int of_bus_isa_translate(__be32 *addr, u64 offset, int na) -{ - return of_bus_default_translate(addr + 1, offset, na - 1); + return of_bus_default_map(addr, range, na, ns, pna, fna); } static unsigned int of_bus_isa_get_flags(const __be32 *addr) @@ -319,11 +317,25 @@ static unsigned int of_bus_isa_get_flags(const __be32 *addr) return flags; } +static int of_bus_default_flags_match(struct device_node *np) +{ + /* + * Check for presence first since of_bus_n_addr_cells() will warn when + * walking parent nodes. + */ + return of_property_present(np, "#address-cells") && (of_bus_n_addr_cells(np) == 3); +} + +static int of_bus_default_match(struct device_node *np) +{ + return of_property_present(np, "#address-cells"); +} + /* * Array of bus specific translators */ -static struct of_bus of_busses[] = { +static const struct of_bus of_busses[] = { #ifdef CONFIG_PCI /* PCI */ { @@ -332,8 +344,8 @@ static struct of_bus of_busses[] = { .match = of_bus_pci_match, .count_cells = of_bus_pci_count_cells, .map = of_bus_pci_map, - .translate = of_bus_pci_translate, - .has_flags = true, + .translate = of_bus_default_flags_translate, + .flag_cells = 1, .get_flags = of_bus_pci_get_flags, }, #endif /* CONFIG_PCI */ @@ -344,15 +356,26 @@ static struct of_bus of_busses[] = { .match = of_bus_isa_match, .count_cells = of_bus_isa_count_cells, .map = of_bus_isa_map, - .translate = of_bus_isa_translate, - .has_flags = true, + .translate = of_bus_default_flags_translate, + .flag_cells = 1, .get_flags = of_bus_isa_get_flags, }, + /* Default with flags cell */ + { + .name = "default-flags", + .addresses = "reg", + .match = of_bus_default_flags_match, + .count_cells = of_bus_default_count_cells, + .map = of_bus_default_flags_map, + .translate = of_bus_default_flags_translate, + .flag_cells = 1, + .get_flags = of_bus_default_flags_get_flags, + }, /* Default */ { .name = "default", .addresses = "reg", - .match = NULL, + .match = of_bus_default_match, .count_cells = of_bus_default_count_cells, .map = of_bus_default_map, .translate = of_bus_default_translate, @@ -360,18 +383,17 @@ static struct of_bus of_busses[] = { }, }; -static struct of_bus *of_match_bus(struct device_node *np) +static const struct of_bus *of_match_bus(struct device_node *np) { int i; for (i = 0; i < ARRAY_SIZE(of_busses); i++) if (!of_busses[i].match || of_busses[i].match(np)) return &of_busses[i]; - BUG(); return NULL; } -static int of_empty_ranges_quirk(struct device_node *np) +static int of_empty_ranges_quirk(const struct device_node *np) { if (IS_ENABLED(CONFIG_PPC)) { /* To save cycles, we cache the result for global "Mac" setting */ @@ -391,8 +413,8 @@ static int of_empty_ranges_quirk(struct device_node *np) return false; } -static int of_translate_one(struct device_node *parent, struct of_bus *bus, - struct of_bus *pbus, __be32 *addr, +static int of_translate_one(const struct device_node *parent, const struct of_bus *bus, + const struct of_bus *pbus, __be32 *addr, int na, int ns, int pna, const char *rprop) { const __be32 *ranges; @@ -427,7 +449,8 @@ static int of_translate_one(struct device_node *parent, struct of_bus *bus, } if (ranges == NULL || rlen == 0) { offset = of_read_number(addr, na); - memset(addr, 0, pna * 4); + /* set address to zero, pass flags through */ + memset(addr + pbus->flag_cells, 0, (pna - pbus->flag_cells) * 4); pr_debug("empty ranges; 1:1 translation\n"); goto finish; } @@ -438,7 +461,7 @@ static int of_translate_one(struct device_node *parent, struct of_bus *bus, rlen /= 4; rone = na + pna + ns; for (; rlen >= rone; rlen -= rone, ranges += rone) { - offset = bus->map(addr, ranges, na, ns, pna); + offset = bus->map(addr, ranges, na, ns, pna, bus->flag_cells); if (offset != OF_BAD_ADDR) break; } @@ -470,34 +493,32 @@ static int of_translate_one(struct device_node *parent, struct of_bus *bus, * device that had registered logical PIO mapping, and the return code is * relative to that node. */ -static u64 __of_translate_address(struct device_node *dev, +static u64 __of_translate_address(struct device_node *node, struct device_node *(*get_parent)(const struct device_node *), const __be32 *in_addr, const char *rprop, struct device_node **host) { - struct device_node *parent = NULL; - struct of_bus *bus, *pbus; + struct device_node *dev __free(device_node) = of_node_get(node); + struct device_node *parent __free(device_node) = get_parent(dev); + const struct of_bus *bus, *pbus; __be32 addr[OF_MAX_ADDR_CELLS]; int na, ns, pna, pns; - u64 result = OF_BAD_ADDR; pr_debug("** translation for device %pOF **\n", dev); - /* Increase refcount at current level */ - of_node_get(dev); - *host = NULL; - /* Get parent & match bus type */ - parent = get_parent(dev); + if (parent == NULL) - goto bail; + return OF_BAD_ADDR; bus = of_match_bus(parent); + if (!bus) + return OF_BAD_ADDR; /* Count address cells & copy address locally */ bus->count_cells(dev, &na, &ns); if (!OF_CHECK_COUNTS(na, ns)) { pr_debug("Bad cell count for %pOF\n", dev); - goto bail; + return OF_BAD_ADDR; } memcpy(addr, in_addr, na * 4); @@ -517,8 +538,7 @@ static u64 __of_translate_address(struct device_node *dev, /* If root, we have finished */ if (parent == NULL) { pr_debug("reached root node\n"); - result = of_read_number(addr, na); - break; + return of_read_number(addr, na); } /* @@ -527,19 +547,21 @@ static u64 __of_translate_address(struct device_node *dev, */ iorange = find_io_range_by_fwnode(&dev->fwnode); if (iorange && (iorange->flags != LOGIC_PIO_CPU_MMIO)) { - result = of_read_number(addr + 1, na - 1); + u64 result = of_read_number(addr + 1, na - 1); pr_debug("indirectIO matched(%pOF) 0x%llx\n", dev, result); - *host = of_node_get(dev); - break; + *host = no_free_ptr(dev); + return result; } /* Get new parent bus and counts */ pbus = of_match_bus(parent); + if (!pbus) + return OF_BAD_ADDR; pbus->count_cells(dev, &pna, &pns); if (!OF_CHECK_COUNTS(pna, pns)) { pr_err("Bad cell count for %pOF\n", dev); - break; + return OF_BAD_ADDR; } pr_debug("parent bus is %s (na=%d, ns=%d) on %pOF\n", @@ -547,7 +569,7 @@ static u64 __of_translate_address(struct device_node *dev, /* Apply bus translation */ if (of_translate_one(dev, bus, pbus, addr, na, ns, pna, rprop)) - break; + return OF_BAD_ADDR; /* Complete the move up one level */ na = pna; @@ -556,11 +578,8 @@ static u64 __of_translate_address(struct device_node *dev, of_dump_addr("one level translation:", addr, na); } - bail: - of_node_put(parent); - of_node_put(dev); - return result; + unreachable(); } u64 of_translate_address(struct device_node *dev, const __be32 *in_addr) @@ -595,7 +614,7 @@ struct device_node *__of_get_dma_parent(const struct device_node *np) if (ret < 0) return of_get_parent(np); - return of_node_get(args.np); + return args.np; } #endif @@ -626,27 +645,59 @@ u64 of_translate_dma_address(struct device_node *dev, const __be32 *in_addr) } EXPORT_SYMBOL(of_translate_dma_address); +/** + * of_translate_dma_region - Translate device tree address and size tuple + * @dev: device tree node for which to translate + * @prop: pointer into array of cells + * @start: return value for the start of the DMA range + * @length: return value for the length of the DMA range + * + * Returns a pointer to the cell immediately following the translated DMA region. + */ +const __be32 *of_translate_dma_region(struct device_node *dev, const __be32 *prop, + phys_addr_t *start, size_t *length) +{ + struct device_node *parent __free(device_node) = __of_get_dma_parent(dev); + u64 address, size; + int na, ns; + + if (!parent) + return NULL; + + na = of_bus_n_addr_cells(parent); + ns = of_bus_n_size_cells(parent); + + address = of_translate_dma_address(dev, prop); + if (address == OF_BAD_ADDR) + return NULL; + + size = of_read_number(prop + na, ns); + + if (start) + *start = address; + + if (length) + *length = size; + + return prop + na + ns; +} +EXPORT_SYMBOL(of_translate_dma_region); + const __be32 *__of_get_address(struct device_node *dev, int index, int bar_no, u64 *size, unsigned int *flags) { const __be32 *prop; unsigned int psize; - struct device_node *parent; - struct of_bus *bus; + struct device_node *parent __free(device_node) = of_get_parent(dev); + const struct of_bus *bus; int onesize, i, na, ns; - /* Get parent & match bus type */ - parent = of_get_parent(dev); if (parent == NULL) return NULL; + + /* match the parent's bus type */ bus = of_match_bus(parent); - if (strcmp(bus->name, "pci") && (bar_no >= 0)) { - of_node_put(parent); - return NULL; - } - bus->count_cells(dev, &na, &ns); - of_node_put(parent); - if (!OF_CHECK_ADDR_COUNT(na)) + if (!bus || (strcmp(bus->name, "pci") && (bar_no >= 0))) return NULL; /* Get "reg" or "assigned-addresses" property */ @@ -655,6 +706,10 @@ const __be32 *__of_get_address(struct device_node *dev, int index, int bar_no, return NULL; psize /= 4; + bus->count_cells(dev, &na, &ns); + if (!OF_CHECK_ADDR_COUNT(na)) + return NULL; + onesize = na + ns; for (i = 0; psize >= onesize; psize -= onesize, prop += onesize, i++) { u32 val = be32_to_cpu(prop[0]); @@ -672,6 +727,29 @@ const __be32 *__of_get_address(struct device_node *dev, int index, int bar_no, } EXPORT_SYMBOL(__of_get_address); +/** + * of_property_read_reg - Retrieve the specified "reg" entry index without translating + * @np: device tree node for which to retrieve "reg" from + * @idx: "reg" entry index to read + * @addr: return value for the untranslated address + * @size: return value for the entry size + * + * Returns -EINVAL if "reg" is not found. Returns 0 on success with addr and + * size values filled in. + */ +int of_property_read_reg(struct device_node *np, int idx, u64 *addr, u64 *size) +{ + const __be32 *prop = of_get_address(np, idx, size, NULL); + + if (!prop) + return -EINVAL; + + *addr = of_read_number(prop, of_n_addr_cells(np)); + + return 0; +} +EXPORT_SYMBOL(of_property_read_reg); + static int parser_init(struct of_pci_range_parser *parser, struct device_node *node, const char *name) { @@ -714,7 +792,7 @@ struct of_pci_range *of_pci_range_parser_one(struct of_pci_range_parser *parser, int na = parser->na; int ns = parser->ns; int np = parser->pna + na + ns; - int busflag_na = 0; + int busflag_na = parser->bus->flag_cells; if (!range) return NULL; @@ -724,10 +802,6 @@ struct of_pci_range *of_pci_range_parser_one(struct of_pci_range_parser *parser, range->flags = parser->bus->get_flags(parser->range); - /* A extra cell for resource flags */ - if (parser->bus->has_flags) - busflag_na = 1; - range->bus_addr = of_read_number(parser->range + busflag_na, na - busflag_na); if (parser->dma) @@ -736,6 +810,8 @@ struct of_pci_range *of_pci_range_parser_one(struct of_pci_range_parser *parser, else range->cpu_addr = of_translate_address(parser->node, parser->range + na); + + range->parent_bus_addr = of_read_number(parser->range + na, parser->pna); range->size = of_read_number(parser->range + parser->pna + na, ns); parser->range += np; @@ -793,126 +869,6 @@ static u64 of_translate_ioport(struct device_node *dev, const __be32 *in_addr, return port; } -static int __of_address_to_resource(struct device_node *dev, int index, int bar_no, - struct resource *r) -{ - u64 taddr; - const __be32 *addrp; - u64 size; - unsigned int flags; - const char *name = NULL; - - addrp = __of_get_address(dev, index, bar_no, &size, &flags); - if (addrp == NULL) - return -EINVAL; - - /* Get optional "reg-names" property to add a name to a resource */ - if (index >= 0) - of_property_read_string_index(dev, "reg-names", index, &name); - - if (flags & IORESOURCE_MEM) - taddr = of_translate_address(dev, addrp); - else if (flags & IORESOURCE_IO) - taddr = of_translate_ioport(dev, addrp, size); - else - return -EINVAL; - - if (taddr == OF_BAD_ADDR) - return -EINVAL; - memset(r, 0, sizeof(struct resource)); - - if (of_mmio_is_nonposted(dev)) - flags |= IORESOURCE_MEM_NONPOSTED; - - r->start = taddr; - r->end = taddr + size - 1; - r->flags = flags; - r->name = name ? name : dev->full_name; - - return 0; -} - -/** - * of_address_to_resource - Translate device tree address and return as resource - * @dev: Caller's Device Node - * @index: Index into the array - * @r: Pointer to resource array - * - * Note that if your address is a PIO address, the conversion will fail if - * the physical address can't be internally converted to an IO token with - * pci_address_to_pio(), that is because it's either called too early or it - * can't be matched to any host bridge IO space - */ -int of_address_to_resource(struct device_node *dev, int index, - struct resource *r) -{ - return __of_address_to_resource(dev, index, -1, r); -} -EXPORT_SYMBOL_GPL(of_address_to_resource); - -/** - * of_iomap - Maps the memory mapped IO for a given device_node - * @np: the device whose io range will be mapped - * @index: index of the io range - * - * Returns a pointer to the mapped memory - */ -void __iomem *of_iomap(struct device_node *np, int index) -{ - struct resource res; - - if (of_address_to_resource(np, index, &res)) - return NULL; - - if (res.flags & IORESOURCE_MEM_NONPOSTED) - return ioremap_np(res.start, resource_size(&res)); - else - return ioremap(res.start, resource_size(&res)); -} -EXPORT_SYMBOL(of_iomap); - -/* - * of_io_request_and_map - Requests a resource and maps the memory mapped IO - * for a given device_node - * @device: the device whose io range will be mapped - * @index: index of the io range - * @name: name "override" for the memory region request or NULL - * - * Returns a pointer to the requested and mapped memory or an ERR_PTR() encoded - * error code on failure. Usage example: - * - * base = of_io_request_and_map(node, 0, "foo"); - * if (IS_ERR(base)) - * return PTR_ERR(base); - */ -void __iomem *of_io_request_and_map(struct device_node *np, int index, - const char *name) -{ - struct resource res; - void __iomem *mem; - - if (of_address_to_resource(np, index, &res)) - return IOMEM_ERR_PTR(-EINVAL); - - if (!name) - name = res.name; - if (!request_mem_region(res.start, resource_size(&res), name)) - return IOMEM_ERR_PTR(-EBUSY); - - if (res.flags & IORESOURCE_MEM_NONPOSTED) - mem = ioremap_np(res.start, resource_size(&res)); - else - mem = ioremap(res.start, resource_size(&res)); - - if (!mem) { - release_mem_region(res.start, resource_size(&res)); - return IOMEM_ERR_PTR(-ENOMEM); - } - - return mem; -} -EXPORT_SYMBOL(of_io_request_and_map); - #ifdef CONFIG_HAS_DMA /** * of_dma_get_range - Get DMA range info and put it into a map array @@ -932,14 +888,13 @@ EXPORT_SYMBOL(of_io_request_and_map); */ int of_dma_get_range(struct device_node *np, const struct bus_dma_region **map) { - struct device_node *node = of_node_get(np); + struct device_node *node __free(device_node) = of_node_get(np); const __be32 *ranges = NULL; bool found_dma_ranges = false; struct of_range_parser parser; struct of_range range; struct bus_dma_region *r; int len, num_ranges = 0; - int ret = 0; while (node) { ranges = of_get_property(node, "dma-ranges", &len); @@ -949,10 +904,9 @@ int of_dma_get_range(struct device_node *np, const struct bus_dma_region **map) break; /* Once we find 'dma-ranges', then a missing one is an error */ - if (found_dma_ranges && !ranges) { - ret = -ENODEV; - goto out; - } + if (found_dma_ranges && !ranges) + return -ENODEV; + found_dma_ranges = true; node = of_get_next_dma_parent(node); @@ -960,42 +914,42 @@ int of_dma_get_range(struct device_node *np, const struct bus_dma_region **map) if (!node || !ranges) { pr_debug("no dma-ranges found for node(%pOF)\n", np); - ret = -ENODEV; - goto out; + return -ENODEV; } - of_dma_range_parser_init(&parser, node); - for_each_of_range(&parser, &range) + for_each_of_range(&parser, &range) { + if (range.cpu_addr == OF_BAD_ADDR) { + pr_err("translation of DMA address(%llx) to CPU address failed node(%pOF)\n", + range.bus_addr, node); + continue; + } num_ranges++; + } + + if (!num_ranges) + return -EINVAL; r = kcalloc(num_ranges + 1, sizeof(*r), GFP_KERNEL); - if (!r) { - ret = -ENOMEM; - goto out; - } + if (!r) + return -ENOMEM; /* - * Record all info in the generic DMA ranges array for struct device. + * Record all info in the generic DMA ranges array for struct device, + * returning an error if we don't find any parsable ranges. */ *map = r; of_dma_range_parser_init(&parser, node); for_each_of_range(&parser, &range) { pr_debug("dma_addr(%llx) cpu_addr(%llx) size(%llx)\n", range.bus_addr, range.cpu_addr, range.size); - if (range.cpu_addr == OF_BAD_ADDR) { - pr_err("translation of DMA address(%llx) to CPU address failed node(%pOF)\n", - range.bus_addr, node); + if (range.cpu_addr == OF_BAD_ADDR) continue; - } r->cpu_start = range.cpu_addr; r->dma_start = range.bus_addr; r->size = range.size; - r->offset = range.cpu_addr - range.bus_addr; r++; } -out: - of_node_put(node); - return ret; + return 0; } #endif /* CONFIG_HAS_DMA */ @@ -1052,24 +1006,18 @@ phys_addr_t __init of_dma_get_max_cpu_address(struct device_node *np) */ bool of_dma_is_coherent(struct device_node *np) { - struct device_node *node; - bool is_coherent = IS_ENABLED(CONFIG_OF_DMA_DEFAULT_COHERENT); - - node = of_node_get(np); + struct device_node *node __free(device_node) = of_node_get(np); while (node) { - if (of_property_read_bool(node, "dma-coherent")) { - is_coherent = true; - break; - } - if (of_property_read_bool(node, "dma-noncoherent")) { - is_coherent = false; - break; - } + if (of_property_read_bool(node, "dma-coherent")) + return true; + + if (of_property_read_bool(node, "dma-noncoherent")) + return false; + node = of_get_next_dma_parent(node); } - of_node_put(node); - return is_coherent; + return dma_default_coherent; } EXPORT_SYMBOL_GPL(of_dma_is_coherent); @@ -1079,24 +1027,144 @@ EXPORT_SYMBOL_GPL(of_dma_is_coherent); * * Returns true if the "nonposted-mmio" property was found for * the device's bus. + */ +static bool of_mmio_is_nonposted(const struct device_node *np) +{ + struct device_node *parent __free(device_node) = of_get_parent(np); + + if (of_property_read_bool(np, "nonposted-mmio")) + return true; + + return parent && of_property_read_bool(parent, "nonposted-mmio"); +} + +static int __of_address_to_resource(struct device_node *dev, int index, int bar_no, + struct resource *r) +{ + u64 taddr; + const __be32 *addrp; + u64 size; + unsigned int flags; + const char *name = NULL; + + addrp = __of_get_address(dev, index, bar_no, &size, &flags); + if (addrp == NULL) + return -EINVAL; + + /* Get optional "reg-names" property to add a name to a resource */ + if (index >= 0) + of_property_read_string_index(dev, "reg-names", index, &name); + + if (flags & IORESOURCE_MEM) + taddr = of_translate_address(dev, addrp); + else if (flags & IORESOURCE_IO) + taddr = of_translate_ioport(dev, addrp, size); + else + return -EINVAL; + + if (taddr == OF_BAD_ADDR) + return -EINVAL; + memset(r, 0, sizeof(struct resource)); + + if (of_mmio_is_nonposted(dev)) + flags |= IORESOURCE_MEM_NONPOSTED; + + r->flags = flags; + r->name = name ? name : dev->full_name; + + return __of_address_resource_bounds(r, taddr, size); +} + +/** + * of_address_to_resource - Translate device tree address and return as resource + * @dev: Caller's Device Node + * @index: Index into the array + * @r: Pointer to resource array * - * This is currently only enabled on builds that support Apple ARM devices, as - * an optimization. + * Returns -EINVAL if the range cannot be converted to resource. + * + * Note that if your address is a PIO address, the conversion will fail if + * the physical address can't be internally converted to an IO token with + * pci_address_to_pio(), that is because it's either called too early or it + * can't be matched to any host bridge IO space */ -static bool of_mmio_is_nonposted(struct device_node *np) +int of_address_to_resource(struct device_node *dev, int index, + struct resource *r) { - struct device_node *parent; - bool nonposted; + return __of_address_to_resource(dev, index, -1, r); +} +EXPORT_SYMBOL_GPL(of_address_to_resource); - if (!IS_ENABLED(CONFIG_ARCH_APPLE)) - return false; +int of_pci_address_to_resource(struct device_node *dev, int bar, + struct resource *r) +{ - parent = of_get_parent(np); - if (!parent) - return false; + if (!IS_ENABLED(CONFIG_PCI)) + return -ENOSYS; - nonposted = of_property_read_bool(parent, "nonposted-mmio"); + return __of_address_to_resource(dev, -1, bar, r); +} +EXPORT_SYMBOL_GPL(of_pci_address_to_resource); + +/** + * of_iomap - Maps the memory mapped IO for a given device_node + * @np: the device whose io range will be mapped + * @index: index of the io range + * + * Returns a pointer to the mapped memory + */ +void __iomem *of_iomap(struct device_node *np, int index) +{ + struct resource res; - of_node_put(parent); - return nonposted; + if (of_address_to_resource(np, index, &res)) + return NULL; + + if (res.flags & IORESOURCE_MEM_NONPOSTED) + return ioremap_np(res.start, resource_size(&res)); + else + return ioremap(res.start, resource_size(&res)); +} +EXPORT_SYMBOL(of_iomap); + +/* + * of_io_request_and_map - Requests a resource and maps the memory mapped IO + * for a given device_node + * @device: the device whose io range will be mapped + * @index: index of the io range + * @name: name "override" for the memory region request or NULL + * + * Returns a pointer to the requested and mapped memory or an ERR_PTR() encoded + * error code on failure. Usage example: + * + * base = of_io_request_and_map(node, 0, "foo"); + * if (IS_ERR(base)) + * return PTR_ERR(base); + */ +void __iomem *of_io_request_and_map(struct device_node *np, int index, + const char *name) +{ + struct resource res; + void __iomem *mem; + + if (of_address_to_resource(np, index, &res)) + return IOMEM_ERR_PTR(-EINVAL); + + if (!name) + name = res.name; + if (!request_mem_region(res.start, resource_size(&res), name)) + return IOMEM_ERR_PTR(-EBUSY); + + if (res.flags & IORESOURCE_MEM_NONPOSTED) + mem = ioremap_np(res.start, resource_size(&res)); + else + mem = ioremap(res.start, resource_size(&res)); + + if (!mem) { + release_mem_region(res.start, resource_size(&res)); + return IOMEM_ERR_PTR(-ENOMEM); + } + + return mem; } +EXPORT_SYMBOL(of_io_request_and_map); |
