162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci#define pr_fmt(fmt) "OF: " fmt 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/device.h> 562306a36Sopenharmony_ci#include <linux/fwnode.h> 662306a36Sopenharmony_ci#include <linux/io.h> 762306a36Sopenharmony_ci#include <linux/ioport.h> 862306a36Sopenharmony_ci#include <linux/logic_pio.h> 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/of_address.h> 1162306a36Sopenharmony_ci#include <linux/pci.h> 1262306a36Sopenharmony_ci#include <linux/pci_regs.h> 1362306a36Sopenharmony_ci#include <linux/sizes.h> 1462306a36Sopenharmony_ci#include <linux/slab.h> 1562306a36Sopenharmony_ci#include <linux/string.h> 1662306a36Sopenharmony_ci#include <linux/dma-direct.h> /* for bus_dma_region */ 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include "of_private.h" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci/* Max address size we deal with */ 2162306a36Sopenharmony_ci#define OF_MAX_ADDR_CELLS 4 2262306a36Sopenharmony_ci#define OF_CHECK_ADDR_COUNT(na) ((na) > 0 && (na) <= OF_MAX_ADDR_CELLS) 2362306a36Sopenharmony_ci#define OF_CHECK_COUNTS(na, ns) (OF_CHECK_ADDR_COUNT(na) && (ns) > 0) 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci/* Debug utility */ 2662306a36Sopenharmony_ci#ifdef DEBUG 2762306a36Sopenharmony_cistatic void of_dump_addr(const char *s, const __be32 *addr, int na) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci pr_debug("%s", s); 3062306a36Sopenharmony_ci while (na--) 3162306a36Sopenharmony_ci pr_cont(" %08x", be32_to_cpu(*(addr++))); 3262306a36Sopenharmony_ci pr_cont("\n"); 3362306a36Sopenharmony_ci} 3462306a36Sopenharmony_ci#else 3562306a36Sopenharmony_cistatic void of_dump_addr(const char *s, const __be32 *addr, int na) { } 3662306a36Sopenharmony_ci#endif 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci/* Callbacks for bus specific translators */ 3962306a36Sopenharmony_cistruct of_bus { 4062306a36Sopenharmony_ci const char *name; 4162306a36Sopenharmony_ci const char *addresses; 4262306a36Sopenharmony_ci int (*match)(struct device_node *parent); 4362306a36Sopenharmony_ci void (*count_cells)(struct device_node *child, 4462306a36Sopenharmony_ci int *addrc, int *sizec); 4562306a36Sopenharmony_ci u64 (*map)(__be32 *addr, const __be32 *range, 4662306a36Sopenharmony_ci int na, int ns, int pna); 4762306a36Sopenharmony_ci int (*translate)(__be32 *addr, u64 offset, int na); 4862306a36Sopenharmony_ci bool has_flags; 4962306a36Sopenharmony_ci unsigned int (*get_flags)(const __be32 *addr); 5062306a36Sopenharmony_ci}; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci/* 5362306a36Sopenharmony_ci * Default translator (generic bus) 5462306a36Sopenharmony_ci */ 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic void of_bus_default_count_cells(struct device_node *dev, 5762306a36Sopenharmony_ci int *addrc, int *sizec) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci if (addrc) 6062306a36Sopenharmony_ci *addrc = of_n_addr_cells(dev); 6162306a36Sopenharmony_ci if (sizec) 6262306a36Sopenharmony_ci *sizec = of_n_size_cells(dev); 6362306a36Sopenharmony_ci} 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistatic u64 of_bus_default_map(__be32 *addr, const __be32 *range, 6662306a36Sopenharmony_ci int na, int ns, int pna) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci u64 cp, s, da; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci cp = of_read_number(range, na); 7162306a36Sopenharmony_ci s = of_read_number(range + na + pna, ns); 7262306a36Sopenharmony_ci da = of_read_number(addr, na); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci pr_debug("default map, cp=%llx, s=%llx, da=%llx\n", cp, s, da); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci if (da < cp || da >= (cp + s)) 7762306a36Sopenharmony_ci return OF_BAD_ADDR; 7862306a36Sopenharmony_ci return da - cp; 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic int of_bus_default_translate(__be32 *addr, u64 offset, int na) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci u64 a = of_read_number(addr, na); 8462306a36Sopenharmony_ci memset(addr, 0, na * 4); 8562306a36Sopenharmony_ci a += offset; 8662306a36Sopenharmony_ci if (na > 1) 8762306a36Sopenharmony_ci addr[na - 2] = cpu_to_be32(a >> 32); 8862306a36Sopenharmony_ci addr[na - 1] = cpu_to_be32(a & 0xffffffffu); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci return 0; 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic unsigned int of_bus_default_flags_get_flags(const __be32 *addr) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci return of_read_number(addr, 1); 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic unsigned int of_bus_default_get_flags(const __be32 *addr) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci return IORESOURCE_MEM; 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic u64 of_bus_default_flags_map(__be32 *addr, const __be32 *range, int na, 10462306a36Sopenharmony_ci int ns, int pna) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci u64 cp, s, da; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci /* Check that flags match */ 10962306a36Sopenharmony_ci if (*addr != *range) 11062306a36Sopenharmony_ci return OF_BAD_ADDR; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci /* Read address values, skipping high cell */ 11362306a36Sopenharmony_ci cp = of_read_number(range + 1, na - 1); 11462306a36Sopenharmony_ci s = of_read_number(range + na + pna, ns); 11562306a36Sopenharmony_ci da = of_read_number(addr + 1, na - 1); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci pr_debug("default flags map, cp=%llx, s=%llx, da=%llx\n", cp, s, da); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci if (da < cp || da >= (cp + s)) 12062306a36Sopenharmony_ci return OF_BAD_ADDR; 12162306a36Sopenharmony_ci return da - cp; 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic int of_bus_default_flags_translate(__be32 *addr, u64 offset, int na) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci /* Keep "flags" part (high cell) in translated address */ 12762306a36Sopenharmony_ci return of_bus_default_translate(addr + 1, offset, na - 1); 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci#ifdef CONFIG_PCI 13162306a36Sopenharmony_cistatic unsigned int of_bus_pci_get_flags(const __be32 *addr) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci unsigned int flags = 0; 13462306a36Sopenharmony_ci u32 w = be32_to_cpup(addr); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_PCI)) 13762306a36Sopenharmony_ci return 0; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci switch((w >> 24) & 0x03) { 14062306a36Sopenharmony_ci case 0x01: 14162306a36Sopenharmony_ci flags |= IORESOURCE_IO; 14262306a36Sopenharmony_ci break; 14362306a36Sopenharmony_ci case 0x02: /* 32 bits */ 14462306a36Sopenharmony_ci flags |= IORESOURCE_MEM; 14562306a36Sopenharmony_ci break; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci case 0x03: /* 64 bits */ 14862306a36Sopenharmony_ci flags |= IORESOURCE_MEM | IORESOURCE_MEM_64; 14962306a36Sopenharmony_ci break; 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci if (w & 0x40000000) 15262306a36Sopenharmony_ci flags |= IORESOURCE_PREFETCH; 15362306a36Sopenharmony_ci return flags; 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci/* 15762306a36Sopenharmony_ci * PCI bus specific translator 15862306a36Sopenharmony_ci */ 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic bool of_node_is_pcie(struct device_node *np) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci bool is_pcie = of_node_name_eq(np, "pcie"); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci if (is_pcie) 16562306a36Sopenharmony_ci pr_warn_once("%pOF: Missing device_type\n", np); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci return is_pcie; 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_cistatic int of_bus_pci_match(struct device_node *np) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci /* 17362306a36Sopenharmony_ci * "pciex" is PCI Express 17462306a36Sopenharmony_ci * "vci" is for the /chaos bridge on 1st-gen PCI powermacs 17562306a36Sopenharmony_ci * "ht" is hypertransport 17662306a36Sopenharmony_ci * 17762306a36Sopenharmony_ci * If none of the device_type match, and that the node name is 17862306a36Sopenharmony_ci * "pcie", accept the device as PCI (with a warning). 17962306a36Sopenharmony_ci */ 18062306a36Sopenharmony_ci return of_node_is_type(np, "pci") || of_node_is_type(np, "pciex") || 18162306a36Sopenharmony_ci of_node_is_type(np, "vci") || of_node_is_type(np, "ht") || 18262306a36Sopenharmony_ci of_node_is_pcie(np); 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cistatic void of_bus_pci_count_cells(struct device_node *np, 18662306a36Sopenharmony_ci int *addrc, int *sizec) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci if (addrc) 18962306a36Sopenharmony_ci *addrc = 3; 19062306a36Sopenharmony_ci if (sizec) 19162306a36Sopenharmony_ci *sizec = 2; 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cistatic u64 of_bus_pci_map(__be32 *addr, const __be32 *range, int na, int ns, 19562306a36Sopenharmony_ci int pna) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci u64 cp, s, da; 19862306a36Sopenharmony_ci unsigned int af, rf; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci af = of_bus_pci_get_flags(addr); 20162306a36Sopenharmony_ci rf = of_bus_pci_get_flags(range); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci /* Check address type match */ 20462306a36Sopenharmony_ci if ((af ^ rf) & (IORESOURCE_MEM | IORESOURCE_IO)) 20562306a36Sopenharmony_ci return OF_BAD_ADDR; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci /* Read address values, skipping high cell */ 20862306a36Sopenharmony_ci cp = of_read_number(range + 1, na - 1); 20962306a36Sopenharmony_ci s = of_read_number(range + na + pna, ns); 21062306a36Sopenharmony_ci da = of_read_number(addr + 1, na - 1); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci pr_debug("PCI map, cp=%llx, s=%llx, da=%llx\n", cp, s, da); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci if (da < cp || da >= (cp + s)) 21562306a36Sopenharmony_ci return OF_BAD_ADDR; 21662306a36Sopenharmony_ci return da - cp; 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistatic int of_bus_pci_translate(__be32 *addr, u64 offset, int na) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci return of_bus_default_translate(addr + 1, offset, na - 1); 22262306a36Sopenharmony_ci} 22362306a36Sopenharmony_ci#endif /* CONFIG_PCI */ 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci/* 22662306a36Sopenharmony_ci * of_pci_range_to_resource - Create a resource from an of_pci_range 22762306a36Sopenharmony_ci * @range: the PCI range that describes the resource 22862306a36Sopenharmony_ci * @np: device node where the range belongs to 22962306a36Sopenharmony_ci * @res: pointer to a valid resource that will be updated to 23062306a36Sopenharmony_ci * reflect the values contained in the range. 23162306a36Sopenharmony_ci * 23262306a36Sopenharmony_ci * Returns -EINVAL if the range cannot be converted to resource. 23362306a36Sopenharmony_ci * 23462306a36Sopenharmony_ci * Note that if the range is an IO range, the resource will be converted 23562306a36Sopenharmony_ci * using pci_address_to_pio() which can fail if it is called too early or 23662306a36Sopenharmony_ci * if the range cannot be matched to any host bridge IO space (our case here). 23762306a36Sopenharmony_ci * To guard against that we try to register the IO range first. 23862306a36Sopenharmony_ci * If that fails we know that pci_address_to_pio() will do too. 23962306a36Sopenharmony_ci */ 24062306a36Sopenharmony_ciint of_pci_range_to_resource(struct of_pci_range *range, 24162306a36Sopenharmony_ci struct device_node *np, struct resource *res) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci int err; 24462306a36Sopenharmony_ci res->flags = range->flags; 24562306a36Sopenharmony_ci res->parent = res->child = res->sibling = NULL; 24662306a36Sopenharmony_ci res->name = np->full_name; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci if (res->flags & IORESOURCE_IO) { 24962306a36Sopenharmony_ci unsigned long port; 25062306a36Sopenharmony_ci err = pci_register_io_range(&np->fwnode, range->cpu_addr, 25162306a36Sopenharmony_ci range->size); 25262306a36Sopenharmony_ci if (err) 25362306a36Sopenharmony_ci goto invalid_range; 25462306a36Sopenharmony_ci port = pci_address_to_pio(range->cpu_addr); 25562306a36Sopenharmony_ci if (port == (unsigned long)-1) { 25662306a36Sopenharmony_ci err = -EINVAL; 25762306a36Sopenharmony_ci goto invalid_range; 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci res->start = port; 26062306a36Sopenharmony_ci } else { 26162306a36Sopenharmony_ci if ((sizeof(resource_size_t) < 8) && 26262306a36Sopenharmony_ci upper_32_bits(range->cpu_addr)) { 26362306a36Sopenharmony_ci err = -EINVAL; 26462306a36Sopenharmony_ci goto invalid_range; 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci res->start = range->cpu_addr; 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci res->end = res->start + range->size - 1; 27062306a36Sopenharmony_ci return 0; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ciinvalid_range: 27362306a36Sopenharmony_ci res->start = (resource_size_t)OF_BAD_ADDR; 27462306a36Sopenharmony_ci res->end = (resource_size_t)OF_BAD_ADDR; 27562306a36Sopenharmony_ci return err; 27662306a36Sopenharmony_ci} 27762306a36Sopenharmony_ciEXPORT_SYMBOL(of_pci_range_to_resource); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci/* 28062306a36Sopenharmony_ci * of_range_to_resource - Create a resource from a ranges entry 28162306a36Sopenharmony_ci * @np: device node where the range belongs to 28262306a36Sopenharmony_ci * @index: the 'ranges' index to convert to a resource 28362306a36Sopenharmony_ci * @res: pointer to a valid resource that will be updated to 28462306a36Sopenharmony_ci * reflect the values contained in the range. 28562306a36Sopenharmony_ci * 28662306a36Sopenharmony_ci * Returns ENOENT if the entry is not found or EINVAL if the range cannot be 28762306a36Sopenharmony_ci * converted to resource. 28862306a36Sopenharmony_ci */ 28962306a36Sopenharmony_ciint of_range_to_resource(struct device_node *np, int index, struct resource *res) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci int ret, i = 0; 29262306a36Sopenharmony_ci struct of_range_parser parser; 29362306a36Sopenharmony_ci struct of_range range; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci ret = of_range_parser_init(&parser, np); 29662306a36Sopenharmony_ci if (ret) 29762306a36Sopenharmony_ci return ret; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci for_each_of_range(&parser, &range) 30062306a36Sopenharmony_ci if (i++ == index) 30162306a36Sopenharmony_ci return of_pci_range_to_resource(&range, np, res); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci return -ENOENT; 30462306a36Sopenharmony_ci} 30562306a36Sopenharmony_ciEXPORT_SYMBOL(of_range_to_resource); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci/* 30862306a36Sopenharmony_ci * ISA bus specific translator 30962306a36Sopenharmony_ci */ 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_cistatic int of_bus_isa_match(struct device_node *np) 31262306a36Sopenharmony_ci{ 31362306a36Sopenharmony_ci return of_node_name_eq(np, "isa"); 31462306a36Sopenharmony_ci} 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_cistatic void of_bus_isa_count_cells(struct device_node *child, 31762306a36Sopenharmony_ci int *addrc, int *sizec) 31862306a36Sopenharmony_ci{ 31962306a36Sopenharmony_ci if (addrc) 32062306a36Sopenharmony_ci *addrc = 2; 32162306a36Sopenharmony_ci if (sizec) 32262306a36Sopenharmony_ci *sizec = 1; 32362306a36Sopenharmony_ci} 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_cistatic u64 of_bus_isa_map(__be32 *addr, const __be32 *range, int na, int ns, 32662306a36Sopenharmony_ci int pna) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci u64 cp, s, da; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci /* Check address type match */ 33162306a36Sopenharmony_ci if ((addr[0] ^ range[0]) & cpu_to_be32(1)) 33262306a36Sopenharmony_ci return OF_BAD_ADDR; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci /* Read address values, skipping high cell */ 33562306a36Sopenharmony_ci cp = of_read_number(range + 1, na - 1); 33662306a36Sopenharmony_ci s = of_read_number(range + na + pna, ns); 33762306a36Sopenharmony_ci da = of_read_number(addr + 1, na - 1); 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci pr_debug("ISA map, cp=%llx, s=%llx, da=%llx\n", cp, s, da); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci if (da < cp || da >= (cp + s)) 34262306a36Sopenharmony_ci return OF_BAD_ADDR; 34362306a36Sopenharmony_ci return da - cp; 34462306a36Sopenharmony_ci} 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_cistatic int of_bus_isa_translate(__be32 *addr, u64 offset, int na) 34762306a36Sopenharmony_ci{ 34862306a36Sopenharmony_ci return of_bus_default_translate(addr + 1, offset, na - 1); 34962306a36Sopenharmony_ci} 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_cistatic unsigned int of_bus_isa_get_flags(const __be32 *addr) 35262306a36Sopenharmony_ci{ 35362306a36Sopenharmony_ci unsigned int flags = 0; 35462306a36Sopenharmony_ci u32 w = be32_to_cpup(addr); 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci if (w & 1) 35762306a36Sopenharmony_ci flags |= IORESOURCE_IO; 35862306a36Sopenharmony_ci else 35962306a36Sopenharmony_ci flags |= IORESOURCE_MEM; 36062306a36Sopenharmony_ci return flags; 36162306a36Sopenharmony_ci} 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_cistatic int of_bus_default_flags_match(struct device_node *np) 36462306a36Sopenharmony_ci{ 36562306a36Sopenharmony_ci return of_bus_n_addr_cells(np) == 3; 36662306a36Sopenharmony_ci} 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci/* 36962306a36Sopenharmony_ci * Array of bus specific translators 37062306a36Sopenharmony_ci */ 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_cistatic struct of_bus of_busses[] = { 37362306a36Sopenharmony_ci#ifdef CONFIG_PCI 37462306a36Sopenharmony_ci /* PCI */ 37562306a36Sopenharmony_ci { 37662306a36Sopenharmony_ci .name = "pci", 37762306a36Sopenharmony_ci .addresses = "assigned-addresses", 37862306a36Sopenharmony_ci .match = of_bus_pci_match, 37962306a36Sopenharmony_ci .count_cells = of_bus_pci_count_cells, 38062306a36Sopenharmony_ci .map = of_bus_pci_map, 38162306a36Sopenharmony_ci .translate = of_bus_pci_translate, 38262306a36Sopenharmony_ci .has_flags = true, 38362306a36Sopenharmony_ci .get_flags = of_bus_pci_get_flags, 38462306a36Sopenharmony_ci }, 38562306a36Sopenharmony_ci#endif /* CONFIG_PCI */ 38662306a36Sopenharmony_ci /* ISA */ 38762306a36Sopenharmony_ci { 38862306a36Sopenharmony_ci .name = "isa", 38962306a36Sopenharmony_ci .addresses = "reg", 39062306a36Sopenharmony_ci .match = of_bus_isa_match, 39162306a36Sopenharmony_ci .count_cells = of_bus_isa_count_cells, 39262306a36Sopenharmony_ci .map = of_bus_isa_map, 39362306a36Sopenharmony_ci .translate = of_bus_isa_translate, 39462306a36Sopenharmony_ci .has_flags = true, 39562306a36Sopenharmony_ci .get_flags = of_bus_isa_get_flags, 39662306a36Sopenharmony_ci }, 39762306a36Sopenharmony_ci /* Default with flags cell */ 39862306a36Sopenharmony_ci { 39962306a36Sopenharmony_ci .name = "default-flags", 40062306a36Sopenharmony_ci .addresses = "reg", 40162306a36Sopenharmony_ci .match = of_bus_default_flags_match, 40262306a36Sopenharmony_ci .count_cells = of_bus_default_count_cells, 40362306a36Sopenharmony_ci .map = of_bus_default_flags_map, 40462306a36Sopenharmony_ci .translate = of_bus_default_flags_translate, 40562306a36Sopenharmony_ci .has_flags = true, 40662306a36Sopenharmony_ci .get_flags = of_bus_default_flags_get_flags, 40762306a36Sopenharmony_ci }, 40862306a36Sopenharmony_ci /* Default */ 40962306a36Sopenharmony_ci { 41062306a36Sopenharmony_ci .name = "default", 41162306a36Sopenharmony_ci .addresses = "reg", 41262306a36Sopenharmony_ci .match = NULL, 41362306a36Sopenharmony_ci .count_cells = of_bus_default_count_cells, 41462306a36Sopenharmony_ci .map = of_bus_default_map, 41562306a36Sopenharmony_ci .translate = of_bus_default_translate, 41662306a36Sopenharmony_ci .get_flags = of_bus_default_get_flags, 41762306a36Sopenharmony_ci }, 41862306a36Sopenharmony_ci}; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_cistatic struct of_bus *of_match_bus(struct device_node *np) 42162306a36Sopenharmony_ci{ 42262306a36Sopenharmony_ci int i; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(of_busses); i++) 42562306a36Sopenharmony_ci if (!of_busses[i].match || of_busses[i].match(np)) 42662306a36Sopenharmony_ci return &of_busses[i]; 42762306a36Sopenharmony_ci BUG(); 42862306a36Sopenharmony_ci return NULL; 42962306a36Sopenharmony_ci} 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_cistatic int of_empty_ranges_quirk(struct device_node *np) 43262306a36Sopenharmony_ci{ 43362306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_PPC)) { 43462306a36Sopenharmony_ci /* To save cycles, we cache the result for global "Mac" setting */ 43562306a36Sopenharmony_ci static int quirk_state = -1; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci /* PA-SEMI sdc DT bug */ 43862306a36Sopenharmony_ci if (of_device_is_compatible(np, "1682m-sdc")) 43962306a36Sopenharmony_ci return true; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci /* Make quirk cached */ 44262306a36Sopenharmony_ci if (quirk_state < 0) 44362306a36Sopenharmony_ci quirk_state = 44462306a36Sopenharmony_ci of_machine_is_compatible("Power Macintosh") || 44562306a36Sopenharmony_ci of_machine_is_compatible("MacRISC"); 44662306a36Sopenharmony_ci return quirk_state; 44762306a36Sopenharmony_ci } 44862306a36Sopenharmony_ci return false; 44962306a36Sopenharmony_ci} 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_cistatic int of_translate_one(struct device_node *parent, struct of_bus *bus, 45262306a36Sopenharmony_ci struct of_bus *pbus, __be32 *addr, 45362306a36Sopenharmony_ci int na, int ns, int pna, const char *rprop) 45462306a36Sopenharmony_ci{ 45562306a36Sopenharmony_ci const __be32 *ranges; 45662306a36Sopenharmony_ci unsigned int rlen; 45762306a36Sopenharmony_ci int rone; 45862306a36Sopenharmony_ci u64 offset = OF_BAD_ADDR; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci /* 46162306a36Sopenharmony_ci * Normally, an absence of a "ranges" property means we are 46262306a36Sopenharmony_ci * crossing a non-translatable boundary, and thus the addresses 46362306a36Sopenharmony_ci * below the current cannot be converted to CPU physical ones. 46462306a36Sopenharmony_ci * Unfortunately, while this is very clear in the spec, it's not 46562306a36Sopenharmony_ci * what Apple understood, and they do have things like /uni-n or 46662306a36Sopenharmony_ci * /ht nodes with no "ranges" property and a lot of perfectly 46762306a36Sopenharmony_ci * useable mapped devices below them. Thus we treat the absence of 46862306a36Sopenharmony_ci * "ranges" as equivalent to an empty "ranges" property which means 46962306a36Sopenharmony_ci * a 1:1 translation at that level. It's up to the caller not to try 47062306a36Sopenharmony_ci * to translate addresses that aren't supposed to be translated in 47162306a36Sopenharmony_ci * the first place. --BenH. 47262306a36Sopenharmony_ci * 47362306a36Sopenharmony_ci * As far as we know, this damage only exists on Apple machines, so 47462306a36Sopenharmony_ci * This code is only enabled on powerpc. --gcl 47562306a36Sopenharmony_ci * 47662306a36Sopenharmony_ci * This quirk also applies for 'dma-ranges' which frequently exist in 47762306a36Sopenharmony_ci * child nodes without 'dma-ranges' in the parent nodes. --RobH 47862306a36Sopenharmony_ci */ 47962306a36Sopenharmony_ci ranges = of_get_property(parent, rprop, &rlen); 48062306a36Sopenharmony_ci if (ranges == NULL && !of_empty_ranges_quirk(parent) && 48162306a36Sopenharmony_ci strcmp(rprop, "dma-ranges")) { 48262306a36Sopenharmony_ci pr_debug("no ranges; cannot translate\n"); 48362306a36Sopenharmony_ci return 1; 48462306a36Sopenharmony_ci } 48562306a36Sopenharmony_ci if (ranges == NULL || rlen == 0) { 48662306a36Sopenharmony_ci offset = of_read_number(addr, na); 48762306a36Sopenharmony_ci memset(addr, 0, pna * 4); 48862306a36Sopenharmony_ci pr_debug("empty ranges; 1:1 translation\n"); 48962306a36Sopenharmony_ci goto finish; 49062306a36Sopenharmony_ci } 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci pr_debug("walking ranges...\n"); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci /* Now walk through the ranges */ 49562306a36Sopenharmony_ci rlen /= 4; 49662306a36Sopenharmony_ci rone = na + pna + ns; 49762306a36Sopenharmony_ci for (; rlen >= rone; rlen -= rone, ranges += rone) { 49862306a36Sopenharmony_ci offset = bus->map(addr, ranges, na, ns, pna); 49962306a36Sopenharmony_ci if (offset != OF_BAD_ADDR) 50062306a36Sopenharmony_ci break; 50162306a36Sopenharmony_ci } 50262306a36Sopenharmony_ci if (offset == OF_BAD_ADDR) { 50362306a36Sopenharmony_ci pr_debug("not found !\n"); 50462306a36Sopenharmony_ci return 1; 50562306a36Sopenharmony_ci } 50662306a36Sopenharmony_ci memcpy(addr, ranges + na, 4 * pna); 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci finish: 50962306a36Sopenharmony_ci of_dump_addr("parent translation for:", addr, pna); 51062306a36Sopenharmony_ci pr_debug("with offset: %llx\n", offset); 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci /* Translate it into parent bus space */ 51362306a36Sopenharmony_ci return pbus->translate(addr, offset, pna); 51462306a36Sopenharmony_ci} 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci/* 51762306a36Sopenharmony_ci * Translate an address from the device-tree into a CPU physical address, 51862306a36Sopenharmony_ci * this walks up the tree and applies the various bus mappings on the 51962306a36Sopenharmony_ci * way. 52062306a36Sopenharmony_ci * 52162306a36Sopenharmony_ci * Note: We consider that crossing any level with #size-cells == 0 to mean 52262306a36Sopenharmony_ci * that translation is impossible (that is we are not dealing with a value 52362306a36Sopenharmony_ci * that can be mapped to a cpu physical address). This is not really specified 52462306a36Sopenharmony_ci * that way, but this is traditionally the way IBM at least do things 52562306a36Sopenharmony_ci * 52662306a36Sopenharmony_ci * Whenever the translation fails, the *host pointer will be set to the 52762306a36Sopenharmony_ci * device that had registered logical PIO mapping, and the return code is 52862306a36Sopenharmony_ci * relative to that node. 52962306a36Sopenharmony_ci */ 53062306a36Sopenharmony_cistatic u64 __of_translate_address(struct device_node *dev, 53162306a36Sopenharmony_ci struct device_node *(*get_parent)(const struct device_node *), 53262306a36Sopenharmony_ci const __be32 *in_addr, const char *rprop, 53362306a36Sopenharmony_ci struct device_node **host) 53462306a36Sopenharmony_ci{ 53562306a36Sopenharmony_ci struct device_node *parent = NULL; 53662306a36Sopenharmony_ci struct of_bus *bus, *pbus; 53762306a36Sopenharmony_ci __be32 addr[OF_MAX_ADDR_CELLS]; 53862306a36Sopenharmony_ci int na, ns, pna, pns; 53962306a36Sopenharmony_ci u64 result = OF_BAD_ADDR; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci pr_debug("** translation for device %pOF **\n", dev); 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci /* Increase refcount at current level */ 54462306a36Sopenharmony_ci of_node_get(dev); 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci *host = NULL; 54762306a36Sopenharmony_ci /* Get parent & match bus type */ 54862306a36Sopenharmony_ci parent = get_parent(dev); 54962306a36Sopenharmony_ci if (parent == NULL) 55062306a36Sopenharmony_ci goto bail; 55162306a36Sopenharmony_ci bus = of_match_bus(parent); 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci /* Count address cells & copy address locally */ 55462306a36Sopenharmony_ci bus->count_cells(dev, &na, &ns); 55562306a36Sopenharmony_ci if (!OF_CHECK_COUNTS(na, ns)) { 55662306a36Sopenharmony_ci pr_debug("Bad cell count for %pOF\n", dev); 55762306a36Sopenharmony_ci goto bail; 55862306a36Sopenharmony_ci } 55962306a36Sopenharmony_ci memcpy(addr, in_addr, na * 4); 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci pr_debug("bus is %s (na=%d, ns=%d) on %pOF\n", 56262306a36Sopenharmony_ci bus->name, na, ns, parent); 56362306a36Sopenharmony_ci of_dump_addr("translating address:", addr, na); 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci /* Translate */ 56662306a36Sopenharmony_ci for (;;) { 56762306a36Sopenharmony_ci struct logic_pio_hwaddr *iorange; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci /* Switch to parent bus */ 57062306a36Sopenharmony_ci of_node_put(dev); 57162306a36Sopenharmony_ci dev = parent; 57262306a36Sopenharmony_ci parent = get_parent(dev); 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci /* If root, we have finished */ 57562306a36Sopenharmony_ci if (parent == NULL) { 57662306a36Sopenharmony_ci pr_debug("reached root node\n"); 57762306a36Sopenharmony_ci result = of_read_number(addr, na); 57862306a36Sopenharmony_ci break; 57962306a36Sopenharmony_ci } 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci /* 58262306a36Sopenharmony_ci * For indirectIO device which has no ranges property, get 58362306a36Sopenharmony_ci * the address from reg directly. 58462306a36Sopenharmony_ci */ 58562306a36Sopenharmony_ci iorange = find_io_range_by_fwnode(&dev->fwnode); 58662306a36Sopenharmony_ci if (iorange && (iorange->flags != LOGIC_PIO_CPU_MMIO)) { 58762306a36Sopenharmony_ci result = of_read_number(addr + 1, na - 1); 58862306a36Sopenharmony_ci pr_debug("indirectIO matched(%pOF) 0x%llx\n", 58962306a36Sopenharmony_ci dev, result); 59062306a36Sopenharmony_ci *host = of_node_get(dev); 59162306a36Sopenharmony_ci break; 59262306a36Sopenharmony_ci } 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci /* Get new parent bus and counts */ 59562306a36Sopenharmony_ci pbus = of_match_bus(parent); 59662306a36Sopenharmony_ci pbus->count_cells(dev, &pna, &pns); 59762306a36Sopenharmony_ci if (!OF_CHECK_COUNTS(pna, pns)) { 59862306a36Sopenharmony_ci pr_err("Bad cell count for %pOF\n", dev); 59962306a36Sopenharmony_ci break; 60062306a36Sopenharmony_ci } 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci pr_debug("parent bus is %s (na=%d, ns=%d) on %pOF\n", 60362306a36Sopenharmony_ci pbus->name, pna, pns, parent); 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci /* Apply bus translation */ 60662306a36Sopenharmony_ci if (of_translate_one(dev, bus, pbus, addr, na, ns, pna, rprop)) 60762306a36Sopenharmony_ci break; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci /* Complete the move up one level */ 61062306a36Sopenharmony_ci na = pna; 61162306a36Sopenharmony_ci ns = pns; 61262306a36Sopenharmony_ci bus = pbus; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci of_dump_addr("one level translation:", addr, na); 61562306a36Sopenharmony_ci } 61662306a36Sopenharmony_ci bail: 61762306a36Sopenharmony_ci of_node_put(parent); 61862306a36Sopenharmony_ci of_node_put(dev); 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci return result; 62162306a36Sopenharmony_ci} 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ciu64 of_translate_address(struct device_node *dev, const __be32 *in_addr) 62462306a36Sopenharmony_ci{ 62562306a36Sopenharmony_ci struct device_node *host; 62662306a36Sopenharmony_ci u64 ret; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci ret = __of_translate_address(dev, of_get_parent, 62962306a36Sopenharmony_ci in_addr, "ranges", &host); 63062306a36Sopenharmony_ci if (host) { 63162306a36Sopenharmony_ci of_node_put(host); 63262306a36Sopenharmony_ci return OF_BAD_ADDR; 63362306a36Sopenharmony_ci } 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci return ret; 63662306a36Sopenharmony_ci} 63762306a36Sopenharmony_ciEXPORT_SYMBOL(of_translate_address); 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci#ifdef CONFIG_HAS_DMA 64062306a36Sopenharmony_cistruct device_node *__of_get_dma_parent(const struct device_node *np) 64162306a36Sopenharmony_ci{ 64262306a36Sopenharmony_ci struct of_phandle_args args; 64362306a36Sopenharmony_ci int ret, index; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci index = of_property_match_string(np, "interconnect-names", "dma-mem"); 64662306a36Sopenharmony_ci if (index < 0) 64762306a36Sopenharmony_ci return of_get_parent(np); 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci ret = of_parse_phandle_with_args(np, "interconnects", 65062306a36Sopenharmony_ci "#interconnect-cells", 65162306a36Sopenharmony_ci index, &args); 65262306a36Sopenharmony_ci if (ret < 0) 65362306a36Sopenharmony_ci return of_get_parent(np); 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci return of_node_get(args.np); 65662306a36Sopenharmony_ci} 65762306a36Sopenharmony_ci#endif 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_cistatic struct device_node *of_get_next_dma_parent(struct device_node *np) 66062306a36Sopenharmony_ci{ 66162306a36Sopenharmony_ci struct device_node *parent; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci parent = __of_get_dma_parent(np); 66462306a36Sopenharmony_ci of_node_put(np); 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci return parent; 66762306a36Sopenharmony_ci} 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ciu64 of_translate_dma_address(struct device_node *dev, const __be32 *in_addr) 67062306a36Sopenharmony_ci{ 67162306a36Sopenharmony_ci struct device_node *host; 67262306a36Sopenharmony_ci u64 ret; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci ret = __of_translate_address(dev, __of_get_dma_parent, 67562306a36Sopenharmony_ci in_addr, "dma-ranges", &host); 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci if (host) { 67862306a36Sopenharmony_ci of_node_put(host); 67962306a36Sopenharmony_ci return OF_BAD_ADDR; 68062306a36Sopenharmony_ci } 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci return ret; 68362306a36Sopenharmony_ci} 68462306a36Sopenharmony_ciEXPORT_SYMBOL(of_translate_dma_address); 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci/** 68762306a36Sopenharmony_ci * of_translate_dma_region - Translate device tree address and size tuple 68862306a36Sopenharmony_ci * @dev: device tree node for which to translate 68962306a36Sopenharmony_ci * @prop: pointer into array of cells 69062306a36Sopenharmony_ci * @start: return value for the start of the DMA range 69162306a36Sopenharmony_ci * @length: return value for the length of the DMA range 69262306a36Sopenharmony_ci * 69362306a36Sopenharmony_ci * Returns a pointer to the cell immediately following the translated DMA region. 69462306a36Sopenharmony_ci */ 69562306a36Sopenharmony_ciconst __be32 *of_translate_dma_region(struct device_node *dev, const __be32 *prop, 69662306a36Sopenharmony_ci phys_addr_t *start, size_t *length) 69762306a36Sopenharmony_ci{ 69862306a36Sopenharmony_ci struct device_node *parent; 69962306a36Sopenharmony_ci u64 address, size; 70062306a36Sopenharmony_ci int na, ns; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci parent = __of_get_dma_parent(dev); 70362306a36Sopenharmony_ci if (!parent) 70462306a36Sopenharmony_ci return NULL; 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci na = of_bus_n_addr_cells(parent); 70762306a36Sopenharmony_ci ns = of_bus_n_size_cells(parent); 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci of_node_put(parent); 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci address = of_translate_dma_address(dev, prop); 71262306a36Sopenharmony_ci if (address == OF_BAD_ADDR) 71362306a36Sopenharmony_ci return NULL; 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci size = of_read_number(prop + na, ns); 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci if (start) 71862306a36Sopenharmony_ci *start = address; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci if (length) 72162306a36Sopenharmony_ci *length = size; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci return prop + na + ns; 72462306a36Sopenharmony_ci} 72562306a36Sopenharmony_ciEXPORT_SYMBOL(of_translate_dma_region); 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ciconst __be32 *__of_get_address(struct device_node *dev, int index, int bar_no, 72862306a36Sopenharmony_ci u64 *size, unsigned int *flags) 72962306a36Sopenharmony_ci{ 73062306a36Sopenharmony_ci const __be32 *prop; 73162306a36Sopenharmony_ci unsigned int psize; 73262306a36Sopenharmony_ci struct device_node *parent; 73362306a36Sopenharmony_ci struct of_bus *bus; 73462306a36Sopenharmony_ci int onesize, i, na, ns; 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci /* Get parent & match bus type */ 73762306a36Sopenharmony_ci parent = of_get_parent(dev); 73862306a36Sopenharmony_ci if (parent == NULL) 73962306a36Sopenharmony_ci return NULL; 74062306a36Sopenharmony_ci bus = of_match_bus(parent); 74162306a36Sopenharmony_ci if (strcmp(bus->name, "pci") && (bar_no >= 0)) { 74262306a36Sopenharmony_ci of_node_put(parent); 74362306a36Sopenharmony_ci return NULL; 74462306a36Sopenharmony_ci } 74562306a36Sopenharmony_ci bus->count_cells(dev, &na, &ns); 74662306a36Sopenharmony_ci of_node_put(parent); 74762306a36Sopenharmony_ci if (!OF_CHECK_ADDR_COUNT(na)) 74862306a36Sopenharmony_ci return NULL; 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci /* Get "reg" or "assigned-addresses" property */ 75162306a36Sopenharmony_ci prop = of_get_property(dev, bus->addresses, &psize); 75262306a36Sopenharmony_ci if (prop == NULL) 75362306a36Sopenharmony_ci return NULL; 75462306a36Sopenharmony_ci psize /= 4; 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci onesize = na + ns; 75762306a36Sopenharmony_ci for (i = 0; psize >= onesize; psize -= onesize, prop += onesize, i++) { 75862306a36Sopenharmony_ci u32 val = be32_to_cpu(prop[0]); 75962306a36Sopenharmony_ci /* PCI bus matches on BAR number instead of index */ 76062306a36Sopenharmony_ci if (((bar_no >= 0) && ((val & 0xff) == ((bar_no * 4) + PCI_BASE_ADDRESS_0))) || 76162306a36Sopenharmony_ci ((index >= 0) && (i == index))) { 76262306a36Sopenharmony_ci if (size) 76362306a36Sopenharmony_ci *size = of_read_number(prop + na, ns); 76462306a36Sopenharmony_ci if (flags) 76562306a36Sopenharmony_ci *flags = bus->get_flags(prop); 76662306a36Sopenharmony_ci return prop; 76762306a36Sopenharmony_ci } 76862306a36Sopenharmony_ci } 76962306a36Sopenharmony_ci return NULL; 77062306a36Sopenharmony_ci} 77162306a36Sopenharmony_ciEXPORT_SYMBOL(__of_get_address); 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci/** 77462306a36Sopenharmony_ci * of_property_read_reg - Retrieve the specified "reg" entry index without translating 77562306a36Sopenharmony_ci * @np: device tree node for which to retrieve "reg" from 77662306a36Sopenharmony_ci * @idx: "reg" entry index to read 77762306a36Sopenharmony_ci * @addr: return value for the untranslated address 77862306a36Sopenharmony_ci * @size: return value for the entry size 77962306a36Sopenharmony_ci * 78062306a36Sopenharmony_ci * Returns -EINVAL if "reg" is not found. Returns 0 on success with addr and 78162306a36Sopenharmony_ci * size values filled in. 78262306a36Sopenharmony_ci */ 78362306a36Sopenharmony_ciint of_property_read_reg(struct device_node *np, int idx, u64 *addr, u64 *size) 78462306a36Sopenharmony_ci{ 78562306a36Sopenharmony_ci const __be32 *prop = of_get_address(np, idx, size, NULL); 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci if (!prop) 78862306a36Sopenharmony_ci return -EINVAL; 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci *addr = of_read_number(prop, of_n_addr_cells(np)); 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci return 0; 79362306a36Sopenharmony_ci} 79462306a36Sopenharmony_ciEXPORT_SYMBOL(of_property_read_reg); 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_cistatic int parser_init(struct of_pci_range_parser *parser, 79762306a36Sopenharmony_ci struct device_node *node, const char *name) 79862306a36Sopenharmony_ci{ 79962306a36Sopenharmony_ci int rlen; 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci parser->node = node; 80262306a36Sopenharmony_ci parser->pna = of_n_addr_cells(node); 80362306a36Sopenharmony_ci parser->na = of_bus_n_addr_cells(node); 80462306a36Sopenharmony_ci parser->ns = of_bus_n_size_cells(node); 80562306a36Sopenharmony_ci parser->dma = !strcmp(name, "dma-ranges"); 80662306a36Sopenharmony_ci parser->bus = of_match_bus(node); 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci parser->range = of_get_property(node, name, &rlen); 80962306a36Sopenharmony_ci if (parser->range == NULL) 81062306a36Sopenharmony_ci return -ENOENT; 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci parser->end = parser->range + rlen / sizeof(__be32); 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci return 0; 81562306a36Sopenharmony_ci} 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ciint of_pci_range_parser_init(struct of_pci_range_parser *parser, 81862306a36Sopenharmony_ci struct device_node *node) 81962306a36Sopenharmony_ci{ 82062306a36Sopenharmony_ci return parser_init(parser, node, "ranges"); 82162306a36Sopenharmony_ci} 82262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_pci_range_parser_init); 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ciint of_pci_dma_range_parser_init(struct of_pci_range_parser *parser, 82562306a36Sopenharmony_ci struct device_node *node) 82662306a36Sopenharmony_ci{ 82762306a36Sopenharmony_ci return parser_init(parser, node, "dma-ranges"); 82862306a36Sopenharmony_ci} 82962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_pci_dma_range_parser_init); 83062306a36Sopenharmony_ci#define of_dma_range_parser_init of_pci_dma_range_parser_init 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_cistruct of_pci_range *of_pci_range_parser_one(struct of_pci_range_parser *parser, 83362306a36Sopenharmony_ci struct of_pci_range *range) 83462306a36Sopenharmony_ci{ 83562306a36Sopenharmony_ci int na = parser->na; 83662306a36Sopenharmony_ci int ns = parser->ns; 83762306a36Sopenharmony_ci int np = parser->pna + na + ns; 83862306a36Sopenharmony_ci int busflag_na = 0; 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci if (!range) 84162306a36Sopenharmony_ci return NULL; 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci if (!parser->range || parser->range + np > parser->end) 84462306a36Sopenharmony_ci return NULL; 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci range->flags = parser->bus->get_flags(parser->range); 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci /* A extra cell for resource flags */ 84962306a36Sopenharmony_ci if (parser->bus->has_flags) 85062306a36Sopenharmony_ci busflag_na = 1; 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci range->bus_addr = of_read_number(parser->range + busflag_na, na - busflag_na); 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci if (parser->dma) 85562306a36Sopenharmony_ci range->cpu_addr = of_translate_dma_address(parser->node, 85662306a36Sopenharmony_ci parser->range + na); 85762306a36Sopenharmony_ci else 85862306a36Sopenharmony_ci range->cpu_addr = of_translate_address(parser->node, 85962306a36Sopenharmony_ci parser->range + na); 86062306a36Sopenharmony_ci range->size = of_read_number(parser->range + parser->pna + na, ns); 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci parser->range += np; 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci /* Now consume following elements while they are contiguous */ 86562306a36Sopenharmony_ci while (parser->range + np <= parser->end) { 86662306a36Sopenharmony_ci u32 flags = 0; 86762306a36Sopenharmony_ci u64 bus_addr, cpu_addr, size; 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci flags = parser->bus->get_flags(parser->range); 87062306a36Sopenharmony_ci bus_addr = of_read_number(parser->range + busflag_na, na - busflag_na); 87162306a36Sopenharmony_ci if (parser->dma) 87262306a36Sopenharmony_ci cpu_addr = of_translate_dma_address(parser->node, 87362306a36Sopenharmony_ci parser->range + na); 87462306a36Sopenharmony_ci else 87562306a36Sopenharmony_ci cpu_addr = of_translate_address(parser->node, 87662306a36Sopenharmony_ci parser->range + na); 87762306a36Sopenharmony_ci size = of_read_number(parser->range + parser->pna + na, ns); 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci if (flags != range->flags) 88062306a36Sopenharmony_ci break; 88162306a36Sopenharmony_ci if (bus_addr != range->bus_addr + range->size || 88262306a36Sopenharmony_ci cpu_addr != range->cpu_addr + range->size) 88362306a36Sopenharmony_ci break; 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci range->size += size; 88662306a36Sopenharmony_ci parser->range += np; 88762306a36Sopenharmony_ci } 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci return range; 89062306a36Sopenharmony_ci} 89162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_pci_range_parser_one); 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_cistatic u64 of_translate_ioport(struct device_node *dev, const __be32 *in_addr, 89462306a36Sopenharmony_ci u64 size) 89562306a36Sopenharmony_ci{ 89662306a36Sopenharmony_ci u64 taddr; 89762306a36Sopenharmony_ci unsigned long port; 89862306a36Sopenharmony_ci struct device_node *host; 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci taddr = __of_translate_address(dev, of_get_parent, 90162306a36Sopenharmony_ci in_addr, "ranges", &host); 90262306a36Sopenharmony_ci if (host) { 90362306a36Sopenharmony_ci /* host-specific port access */ 90462306a36Sopenharmony_ci port = logic_pio_trans_hwaddr(&host->fwnode, taddr, size); 90562306a36Sopenharmony_ci of_node_put(host); 90662306a36Sopenharmony_ci } else { 90762306a36Sopenharmony_ci /* memory-mapped I/O range */ 90862306a36Sopenharmony_ci port = pci_address_to_pio(taddr); 90962306a36Sopenharmony_ci } 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci if (port == (unsigned long)-1) 91262306a36Sopenharmony_ci return OF_BAD_ADDR; 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci return port; 91562306a36Sopenharmony_ci} 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci#ifdef CONFIG_HAS_DMA 91862306a36Sopenharmony_ci/** 91962306a36Sopenharmony_ci * of_dma_get_range - Get DMA range info and put it into a map array 92062306a36Sopenharmony_ci * @np: device node to get DMA range info 92162306a36Sopenharmony_ci * @map: dma range structure to return 92262306a36Sopenharmony_ci * 92362306a36Sopenharmony_ci * Look in bottom up direction for the first "dma-ranges" property 92462306a36Sopenharmony_ci * and parse it. Put the information into a DMA offset map array. 92562306a36Sopenharmony_ci * 92662306a36Sopenharmony_ci * dma-ranges format: 92762306a36Sopenharmony_ci * DMA addr (dma_addr) : naddr cells 92862306a36Sopenharmony_ci * CPU addr (phys_addr_t) : pna cells 92962306a36Sopenharmony_ci * size : nsize cells 93062306a36Sopenharmony_ci * 93162306a36Sopenharmony_ci * It returns -ENODEV if "dma-ranges" property was not found for this 93262306a36Sopenharmony_ci * device in the DT. 93362306a36Sopenharmony_ci */ 93462306a36Sopenharmony_ciint of_dma_get_range(struct device_node *np, const struct bus_dma_region **map) 93562306a36Sopenharmony_ci{ 93662306a36Sopenharmony_ci struct device_node *node = of_node_get(np); 93762306a36Sopenharmony_ci const __be32 *ranges = NULL; 93862306a36Sopenharmony_ci bool found_dma_ranges = false; 93962306a36Sopenharmony_ci struct of_range_parser parser; 94062306a36Sopenharmony_ci struct of_range range; 94162306a36Sopenharmony_ci struct bus_dma_region *r; 94262306a36Sopenharmony_ci int len, num_ranges = 0; 94362306a36Sopenharmony_ci int ret = 0; 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci while (node) { 94662306a36Sopenharmony_ci ranges = of_get_property(node, "dma-ranges", &len); 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci /* Ignore empty ranges, they imply no translation required */ 94962306a36Sopenharmony_ci if (ranges && len > 0) 95062306a36Sopenharmony_ci break; 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci /* Once we find 'dma-ranges', then a missing one is an error */ 95362306a36Sopenharmony_ci if (found_dma_ranges && !ranges) { 95462306a36Sopenharmony_ci ret = -ENODEV; 95562306a36Sopenharmony_ci goto out; 95662306a36Sopenharmony_ci } 95762306a36Sopenharmony_ci found_dma_ranges = true; 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci node = of_get_next_dma_parent(node); 96062306a36Sopenharmony_ci } 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci if (!node || !ranges) { 96362306a36Sopenharmony_ci pr_debug("no dma-ranges found for node(%pOF)\n", np); 96462306a36Sopenharmony_ci ret = -ENODEV; 96562306a36Sopenharmony_ci goto out; 96662306a36Sopenharmony_ci } 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci of_dma_range_parser_init(&parser, node); 96962306a36Sopenharmony_ci for_each_of_range(&parser, &range) { 97062306a36Sopenharmony_ci if (range.cpu_addr == OF_BAD_ADDR) { 97162306a36Sopenharmony_ci pr_err("translation of DMA address(%llx) to CPU address failed node(%pOF)\n", 97262306a36Sopenharmony_ci range.bus_addr, node); 97362306a36Sopenharmony_ci continue; 97462306a36Sopenharmony_ci } 97562306a36Sopenharmony_ci num_ranges++; 97662306a36Sopenharmony_ci } 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci if (!num_ranges) { 97962306a36Sopenharmony_ci ret = -EINVAL; 98062306a36Sopenharmony_ci goto out; 98162306a36Sopenharmony_ci } 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci r = kcalloc(num_ranges + 1, sizeof(*r), GFP_KERNEL); 98462306a36Sopenharmony_ci if (!r) { 98562306a36Sopenharmony_ci ret = -ENOMEM; 98662306a36Sopenharmony_ci goto out; 98762306a36Sopenharmony_ci } 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci /* 99062306a36Sopenharmony_ci * Record all info in the generic DMA ranges array for struct device, 99162306a36Sopenharmony_ci * returning an error if we don't find any parsable ranges. 99262306a36Sopenharmony_ci */ 99362306a36Sopenharmony_ci *map = r; 99462306a36Sopenharmony_ci of_dma_range_parser_init(&parser, node); 99562306a36Sopenharmony_ci for_each_of_range(&parser, &range) { 99662306a36Sopenharmony_ci pr_debug("dma_addr(%llx) cpu_addr(%llx) size(%llx)\n", 99762306a36Sopenharmony_ci range.bus_addr, range.cpu_addr, range.size); 99862306a36Sopenharmony_ci if (range.cpu_addr == OF_BAD_ADDR) 99962306a36Sopenharmony_ci continue; 100062306a36Sopenharmony_ci r->cpu_start = range.cpu_addr; 100162306a36Sopenharmony_ci r->dma_start = range.bus_addr; 100262306a36Sopenharmony_ci r->size = range.size; 100362306a36Sopenharmony_ci r->offset = range.cpu_addr - range.bus_addr; 100462306a36Sopenharmony_ci r++; 100562306a36Sopenharmony_ci } 100662306a36Sopenharmony_ciout: 100762306a36Sopenharmony_ci of_node_put(node); 100862306a36Sopenharmony_ci return ret; 100962306a36Sopenharmony_ci} 101062306a36Sopenharmony_ci#endif /* CONFIG_HAS_DMA */ 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci/** 101362306a36Sopenharmony_ci * of_dma_get_max_cpu_address - Gets highest CPU address suitable for DMA 101462306a36Sopenharmony_ci * @np: The node to start searching from or NULL to start from the root 101562306a36Sopenharmony_ci * 101662306a36Sopenharmony_ci * Gets the highest CPU physical address that is addressable by all DMA masters 101762306a36Sopenharmony_ci * in the sub-tree pointed by np, or the whole tree if NULL is passed. If no 101862306a36Sopenharmony_ci * DMA constrained device is found, it returns PHYS_ADDR_MAX. 101962306a36Sopenharmony_ci */ 102062306a36Sopenharmony_ciphys_addr_t __init of_dma_get_max_cpu_address(struct device_node *np) 102162306a36Sopenharmony_ci{ 102262306a36Sopenharmony_ci phys_addr_t max_cpu_addr = PHYS_ADDR_MAX; 102362306a36Sopenharmony_ci struct of_range_parser parser; 102462306a36Sopenharmony_ci phys_addr_t subtree_max_addr; 102562306a36Sopenharmony_ci struct device_node *child; 102662306a36Sopenharmony_ci struct of_range range; 102762306a36Sopenharmony_ci const __be32 *ranges; 102862306a36Sopenharmony_ci u64 cpu_end = 0; 102962306a36Sopenharmony_ci int len; 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci if (!np) 103262306a36Sopenharmony_ci np = of_root; 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci ranges = of_get_property(np, "dma-ranges", &len); 103562306a36Sopenharmony_ci if (ranges && len) { 103662306a36Sopenharmony_ci of_dma_range_parser_init(&parser, np); 103762306a36Sopenharmony_ci for_each_of_range(&parser, &range) 103862306a36Sopenharmony_ci if (range.cpu_addr + range.size > cpu_end) 103962306a36Sopenharmony_ci cpu_end = range.cpu_addr + range.size - 1; 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci if (max_cpu_addr > cpu_end) 104262306a36Sopenharmony_ci max_cpu_addr = cpu_end; 104362306a36Sopenharmony_ci } 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_ci for_each_available_child_of_node(np, child) { 104662306a36Sopenharmony_ci subtree_max_addr = of_dma_get_max_cpu_address(child); 104762306a36Sopenharmony_ci if (max_cpu_addr > subtree_max_addr) 104862306a36Sopenharmony_ci max_cpu_addr = subtree_max_addr; 104962306a36Sopenharmony_ci } 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci return max_cpu_addr; 105262306a36Sopenharmony_ci} 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci/** 105562306a36Sopenharmony_ci * of_dma_is_coherent - Check if device is coherent 105662306a36Sopenharmony_ci * @np: device node 105762306a36Sopenharmony_ci * 105862306a36Sopenharmony_ci * It returns true if "dma-coherent" property was found 105962306a36Sopenharmony_ci * for this device in the DT, or if DMA is coherent by 106062306a36Sopenharmony_ci * default for OF devices on the current platform and no 106162306a36Sopenharmony_ci * "dma-noncoherent" property was found for this device. 106262306a36Sopenharmony_ci */ 106362306a36Sopenharmony_cibool of_dma_is_coherent(struct device_node *np) 106462306a36Sopenharmony_ci{ 106562306a36Sopenharmony_ci struct device_node *node; 106662306a36Sopenharmony_ci bool is_coherent = dma_default_coherent; 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci node = of_node_get(np); 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci while (node) { 107162306a36Sopenharmony_ci if (of_property_read_bool(node, "dma-coherent")) { 107262306a36Sopenharmony_ci is_coherent = true; 107362306a36Sopenharmony_ci break; 107462306a36Sopenharmony_ci } 107562306a36Sopenharmony_ci if (of_property_read_bool(node, "dma-noncoherent")) { 107662306a36Sopenharmony_ci is_coherent = false; 107762306a36Sopenharmony_ci break; 107862306a36Sopenharmony_ci } 107962306a36Sopenharmony_ci node = of_get_next_dma_parent(node); 108062306a36Sopenharmony_ci } 108162306a36Sopenharmony_ci of_node_put(node); 108262306a36Sopenharmony_ci return is_coherent; 108362306a36Sopenharmony_ci} 108462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_dma_is_coherent); 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci/** 108762306a36Sopenharmony_ci * of_mmio_is_nonposted - Check if device uses non-posted MMIO 108862306a36Sopenharmony_ci * @np: device node 108962306a36Sopenharmony_ci * 109062306a36Sopenharmony_ci * Returns true if the "nonposted-mmio" property was found for 109162306a36Sopenharmony_ci * the device's bus. 109262306a36Sopenharmony_ci * 109362306a36Sopenharmony_ci * This is currently only enabled on builds that support Apple ARM devices, as 109462306a36Sopenharmony_ci * an optimization. 109562306a36Sopenharmony_ci */ 109662306a36Sopenharmony_cistatic bool of_mmio_is_nonposted(struct device_node *np) 109762306a36Sopenharmony_ci{ 109862306a36Sopenharmony_ci struct device_node *parent; 109962306a36Sopenharmony_ci bool nonposted; 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_ARCH_APPLE)) 110262306a36Sopenharmony_ci return false; 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci parent = of_get_parent(np); 110562306a36Sopenharmony_ci if (!parent) 110662306a36Sopenharmony_ci return false; 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci nonposted = of_property_read_bool(parent, "nonposted-mmio"); 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci of_node_put(parent); 111162306a36Sopenharmony_ci return nonposted; 111262306a36Sopenharmony_ci} 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_cistatic int __of_address_to_resource(struct device_node *dev, int index, int bar_no, 111562306a36Sopenharmony_ci struct resource *r) 111662306a36Sopenharmony_ci{ 111762306a36Sopenharmony_ci u64 taddr; 111862306a36Sopenharmony_ci const __be32 *addrp; 111962306a36Sopenharmony_ci u64 size; 112062306a36Sopenharmony_ci unsigned int flags; 112162306a36Sopenharmony_ci const char *name = NULL; 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci addrp = __of_get_address(dev, index, bar_no, &size, &flags); 112462306a36Sopenharmony_ci if (addrp == NULL) 112562306a36Sopenharmony_ci return -EINVAL; 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci /* Get optional "reg-names" property to add a name to a resource */ 112862306a36Sopenharmony_ci if (index >= 0) 112962306a36Sopenharmony_ci of_property_read_string_index(dev, "reg-names", index, &name); 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci if (flags & IORESOURCE_MEM) 113262306a36Sopenharmony_ci taddr = of_translate_address(dev, addrp); 113362306a36Sopenharmony_ci else if (flags & IORESOURCE_IO) 113462306a36Sopenharmony_ci taddr = of_translate_ioport(dev, addrp, size); 113562306a36Sopenharmony_ci else 113662306a36Sopenharmony_ci return -EINVAL; 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci if (taddr == OF_BAD_ADDR) 113962306a36Sopenharmony_ci return -EINVAL; 114062306a36Sopenharmony_ci memset(r, 0, sizeof(struct resource)); 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_ci if (of_mmio_is_nonposted(dev)) 114362306a36Sopenharmony_ci flags |= IORESOURCE_MEM_NONPOSTED; 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci r->start = taddr; 114662306a36Sopenharmony_ci r->end = taddr + size - 1; 114762306a36Sopenharmony_ci r->flags = flags; 114862306a36Sopenharmony_ci r->name = name ? name : dev->full_name; 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ci return 0; 115162306a36Sopenharmony_ci} 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ci/** 115462306a36Sopenharmony_ci * of_address_to_resource - Translate device tree address and return as resource 115562306a36Sopenharmony_ci * @dev: Caller's Device Node 115662306a36Sopenharmony_ci * @index: Index into the array 115762306a36Sopenharmony_ci * @r: Pointer to resource array 115862306a36Sopenharmony_ci * 115962306a36Sopenharmony_ci * Returns -EINVAL if the range cannot be converted to resource. 116062306a36Sopenharmony_ci * 116162306a36Sopenharmony_ci * Note that if your address is a PIO address, the conversion will fail if 116262306a36Sopenharmony_ci * the physical address can't be internally converted to an IO token with 116362306a36Sopenharmony_ci * pci_address_to_pio(), that is because it's either called too early or it 116462306a36Sopenharmony_ci * can't be matched to any host bridge IO space 116562306a36Sopenharmony_ci */ 116662306a36Sopenharmony_ciint of_address_to_resource(struct device_node *dev, int index, 116762306a36Sopenharmony_ci struct resource *r) 116862306a36Sopenharmony_ci{ 116962306a36Sopenharmony_ci return __of_address_to_resource(dev, index, -1, r); 117062306a36Sopenharmony_ci} 117162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_address_to_resource); 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ciint of_pci_address_to_resource(struct device_node *dev, int bar, 117462306a36Sopenharmony_ci struct resource *r) 117562306a36Sopenharmony_ci{ 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_PCI)) 117862306a36Sopenharmony_ci return -ENOSYS; 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci return __of_address_to_resource(dev, -1, bar, r); 118162306a36Sopenharmony_ci} 118262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_pci_address_to_resource); 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci/** 118562306a36Sopenharmony_ci * of_iomap - Maps the memory mapped IO for a given device_node 118662306a36Sopenharmony_ci * @np: the device whose io range will be mapped 118762306a36Sopenharmony_ci * @index: index of the io range 118862306a36Sopenharmony_ci * 118962306a36Sopenharmony_ci * Returns a pointer to the mapped memory 119062306a36Sopenharmony_ci */ 119162306a36Sopenharmony_civoid __iomem *of_iomap(struct device_node *np, int index) 119262306a36Sopenharmony_ci{ 119362306a36Sopenharmony_ci struct resource res; 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci if (of_address_to_resource(np, index, &res)) 119662306a36Sopenharmony_ci return NULL; 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_ci if (res.flags & IORESOURCE_MEM_NONPOSTED) 119962306a36Sopenharmony_ci return ioremap_np(res.start, resource_size(&res)); 120062306a36Sopenharmony_ci else 120162306a36Sopenharmony_ci return ioremap(res.start, resource_size(&res)); 120262306a36Sopenharmony_ci} 120362306a36Sopenharmony_ciEXPORT_SYMBOL(of_iomap); 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci/* 120662306a36Sopenharmony_ci * of_io_request_and_map - Requests a resource and maps the memory mapped IO 120762306a36Sopenharmony_ci * for a given device_node 120862306a36Sopenharmony_ci * @device: the device whose io range will be mapped 120962306a36Sopenharmony_ci * @index: index of the io range 121062306a36Sopenharmony_ci * @name: name "override" for the memory region request or NULL 121162306a36Sopenharmony_ci * 121262306a36Sopenharmony_ci * Returns a pointer to the requested and mapped memory or an ERR_PTR() encoded 121362306a36Sopenharmony_ci * error code on failure. Usage example: 121462306a36Sopenharmony_ci * 121562306a36Sopenharmony_ci * base = of_io_request_and_map(node, 0, "foo"); 121662306a36Sopenharmony_ci * if (IS_ERR(base)) 121762306a36Sopenharmony_ci * return PTR_ERR(base); 121862306a36Sopenharmony_ci */ 121962306a36Sopenharmony_civoid __iomem *of_io_request_and_map(struct device_node *np, int index, 122062306a36Sopenharmony_ci const char *name) 122162306a36Sopenharmony_ci{ 122262306a36Sopenharmony_ci struct resource res; 122362306a36Sopenharmony_ci void __iomem *mem; 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci if (of_address_to_resource(np, index, &res)) 122662306a36Sopenharmony_ci return IOMEM_ERR_PTR(-EINVAL); 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci if (!name) 122962306a36Sopenharmony_ci name = res.name; 123062306a36Sopenharmony_ci if (!request_mem_region(res.start, resource_size(&res), name)) 123162306a36Sopenharmony_ci return IOMEM_ERR_PTR(-EBUSY); 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_ci if (res.flags & IORESOURCE_MEM_NONPOSTED) 123462306a36Sopenharmony_ci mem = ioremap_np(res.start, resource_size(&res)); 123562306a36Sopenharmony_ci else 123662306a36Sopenharmony_ci mem = ioremap(res.start, resource_size(&res)); 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_ci if (!mem) { 123962306a36Sopenharmony_ci release_mem_region(res.start, resource_size(&res)); 124062306a36Sopenharmony_ci return IOMEM_ERR_PTR(-ENOMEM); 124162306a36Sopenharmony_ci } 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci return mem; 124462306a36Sopenharmony_ci} 124562306a36Sopenharmony_ciEXPORT_SYMBOL(of_io_request_and_map); 1246