diff options
Diffstat (limited to 'drivers/of/address.c')
| -rw-r--r-- | drivers/of/address.c | 248 |
1 files changed, 220 insertions, 28 deletions
diff --git a/drivers/of/address.c b/drivers/of/address.c index 66d96f14c27..5edfcb0da37 100644 --- a/drivers/of/address.c +++ b/drivers/of/address.c @@ -1,4 +1,5 @@ +#include <linux/device.h> #include <linux/io.h> #include <linux/ioport.h> #include <linux/module.h> @@ -8,8 +9,8 @@ /* Max address size we deal with */ #define OF_MAX_ADDR_CELLS 4 -#define OF_CHECK_COUNTS(na, ns) ((na) > 0 && (na) <= OF_MAX_ADDR_CELLS && \ - (ns) > 0) +#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) static struct of_bus *of_match_bus(struct device_node *np); static int __of_address_to_resource(struct device_node *dev, @@ -36,9 +37,9 @@ struct of_bus { int (*match)(struct device_node *parent); void (*count_cells)(struct device_node *child, int *addrc, int *sizec); - u64 (*map)(u32 *addr, const __be32 *range, + u64 (*map)(__be32 *addr, const __be32 *range, int na, int ns, int pna); - int (*translate)(u32 *addr, u64 offset, int na); + int (*translate)(__be32 *addr, u64 offset, int na); unsigned int (*get_flags)(const __be32 *addr); }; @@ -55,7 +56,7 @@ static void of_bus_default_count_cells(struct device_node *dev, *sizec = of_n_size_cells(dev); } -static u64 of_bus_default_map(u32 *addr, const __be32 *range, +static u64 of_bus_default_map(__be32 *addr, const __be32 *range, int na, int ns, int pna) { u64 cp, s, da; @@ -73,7 +74,7 @@ static u64 of_bus_default_map(u32 *addr, const __be32 *range, return da - cp; } -static int of_bus_default_translate(u32 *addr, u64 offset, int na) +static int of_bus_default_translate(__be32 *addr, u64 offset, int na) { u64 a = of_read_number(addr, na); memset(addr, 0, na * 4); @@ -90,15 +91,20 @@ static unsigned int of_bus_default_get_flags(const __be32 *addr) return IORESOURCE_MEM; } -#ifdef CONFIG_PCI +#ifdef CONFIG_OF_ADDRESS_PCI /* * PCI bus specific translator */ static int of_bus_pci_match(struct device_node *np) { - /* "vci" is for the /chaos bridge on 1st-gen PCI powermacs */ - return !strcmp(np->type, "pci") || !strcmp(np->type, "vci"); + /* + * "pciex" is PCI Express + * "vci" is for the /chaos bridge on 1st-gen PCI powermacs + * "ht" is hypertransport + */ + return !strcmp(np->type, "pci") || !strcmp(np->type, "pciex") || + !strcmp(np->type, "vci") || !strcmp(np->type, "ht"); } static void of_bus_pci_count_cells(struct device_node *np, @@ -129,7 +135,7 @@ static unsigned int of_bus_pci_get_flags(const __be32 *addr) return flags; } -static u64 of_bus_pci_map(u32 *addr, const __be32 *range, int na, int ns, +static u64 of_bus_pci_map(__be32 *addr, const __be32 *range, int na, int ns, int pna) { u64 cp, s, da; @@ -156,11 +162,13 @@ static u64 of_bus_pci_map(u32 *addr, const __be32 *range, int na, int ns, return da - cp; } -static int of_bus_pci_translate(u32 *addr, u64 offset, int na) +static int of_bus_pci_translate(__be32 *addr, u64 offset, int na) { return of_bus_default_translate(addr + 1, offset, na - 1); } +#endif /* CONFIG_OF_ADDRESS_PCI */ +#ifdef CONFIG_PCI const __be32 *of_get_pci_address(struct device_node *dev, int bar_no, u64 *size, unsigned int *flags) { @@ -181,7 +189,7 @@ const __be32 *of_get_pci_address(struct device_node *dev, int bar_no, u64 *size, } bus->count_cells(dev, &na, &ns); of_node_put(parent); - if (!OF_CHECK_COUNTS(na, ns)) + if (!OF_CHECK_ADDR_COUNT(na)) return NULL; /* Get "reg" or "assigned-addresses" property */ @@ -218,6 +226,73 @@ int of_pci_address_to_resource(struct device_node *dev, int bar, return __of_address_to_resource(dev, addrp, size, flags, NULL, r); } EXPORT_SYMBOL_GPL(of_pci_address_to_resource); + +int of_pci_range_parser_init(struct of_pci_range_parser *parser, + struct device_node *node) +{ + const int na = 3, ns = 2; + int rlen; + + parser->node = node; + parser->pna = of_n_addr_cells(node); + parser->np = parser->pna + na + ns; + + parser->range = of_get_property(node, "ranges", &rlen); + if (parser->range == NULL) + return -ENOENT; + + parser->end = parser->range + rlen / sizeof(__be32); + + return 0; +} +EXPORT_SYMBOL_GPL(of_pci_range_parser_init); + +struct of_pci_range *of_pci_range_parser_one(struct of_pci_range_parser *parser, + struct of_pci_range *range) +{ + const int na = 3, ns = 2; + + if (!range) + return NULL; + + if (!parser->range || parser->range + parser->np > parser->end) + return NULL; + + range->pci_space = parser->range[0]; + range->flags = of_bus_pci_get_flags(parser->range); + range->pci_addr = of_read_number(parser->range + 1, ns); + range->cpu_addr = of_translate_address(parser->node, + parser->range + na); + range->size = of_read_number(parser->range + parser->pna + na, ns); + + parser->range += parser->np; + + /* Now consume following elements while they are contiguous */ + while (parser->range + parser->np <= parser->end) { + u32 flags, pci_space; + u64 pci_addr, cpu_addr, size; + + pci_space = be32_to_cpup(parser->range); + flags = of_bus_pci_get_flags(parser->range); + pci_addr = of_read_number(parser->range + 1, ns); + cpu_addr = of_translate_address(parser->node, + parser->range + na); + size = of_read_number(parser->range + parser->pna + na, ns); + + if (flags != range->flags) + break; + if (pci_addr != range->pci_addr + range->size || + cpu_addr != range->cpu_addr + range->size) + break; + + range->size += size; + parser->range += parser->np; + } + + return range; +} +EXPORT_SYMBOL_GPL(of_pci_range_parser_one); + #endif /* CONFIG_PCI */ /* @@ -238,7 +313,7 @@ static void of_bus_isa_count_cells(struct device_node *child, *sizec = 1; } -static u64 of_bus_isa_map(u32 *addr, const __be32 *range, int na, int ns, +static u64 of_bus_isa_map(__be32 *addr, const __be32 *range, int na, int ns, int pna) { u64 cp, s, da; @@ -261,7 +336,7 @@ static u64 of_bus_isa_map(u32 *addr, const __be32 *range, int na, int ns, return da - cp; } -static int of_bus_isa_translate(u32 *addr, u64 offset, int na) +static int of_bus_isa_translate(__be32 *addr, u64 offset, int na) { return of_bus_default_translate(addr + 1, offset, na - 1); } @@ -283,7 +358,7 @@ static unsigned int of_bus_isa_get_flags(const __be32 *addr) */ static struct of_bus of_busses[] = { -#ifdef CONFIG_PCI +#ifdef CONFIG_OF_ADDRESS_PCI /* PCI */ { .name = "pci", @@ -294,7 +369,7 @@ static struct of_bus of_busses[] = { .translate = of_bus_pci_translate, .get_flags = of_bus_pci_get_flags, }, -#endif /* CONFIG_PCI */ +#endif /* CONFIG_OF_ADDRESS_PCI */ /* ISA */ { .name = "isa", @@ -329,7 +404,7 @@ static struct of_bus *of_match_bus(struct device_node *np) } static int of_translate_one(struct device_node *parent, struct of_bus *bus, - struct of_bus *pbus, u32 *addr, + struct of_bus *pbus, __be32 *addr, int na, int ns, int pna, const char *rprop) { const __be32 *ranges; @@ -400,16 +475,16 @@ static int of_translate_one(struct device_node *parent, struct of_bus *bus, * that can be mapped to a cpu physical address). This is not really specified * that way, but this is traditionally the way IBM at least do things */ -u64 __of_translate_address(struct device_node *dev, const __be32 *in_addr, - const char *rprop) +static u64 __of_translate_address(struct device_node *dev, + const __be32 *in_addr, const char *rprop) { struct device_node *parent = NULL; struct of_bus *bus, *pbus; - u32 addr[OF_MAX_ADDR_CELLS]; + __be32 addr[OF_MAX_ADDR_CELLS]; int na, ns, pna, pns; u64 result = OF_BAD_ADDR; - pr_debug("OF: ** translation for device %s **\n", dev->full_name); + pr_debug("OF: ** translation for device %s **\n", of_node_full_name(dev)); /* Increase refcount at current level */ of_node_get(dev); @@ -420,17 +495,16 @@ u64 __of_translate_address(struct device_node *dev, const __be32 *in_addr, goto bail; bus = of_match_bus(parent); - /* Cound address cells & copy address locally */ + /* Count address cells & copy address locally */ bus->count_cells(dev, &na, &ns); if (!OF_CHECK_COUNTS(na, ns)) { - printk(KERN_ERR "prom_parse: Bad cell count for %s\n", - dev->full_name); + pr_debug("OF: Bad cell count for %s\n", of_node_full_name(dev)); goto bail; } memcpy(addr, in_addr, na * 4); pr_debug("OF: bus is %s (na=%d, ns=%d) on %s\n", - bus->name, na, ns, parent->full_name); + bus->name, na, ns, of_node_full_name(parent)); of_dump_addr("OF: translating address:", addr, na); /* Translate */ @@ -452,12 +526,12 @@ u64 __of_translate_address(struct device_node *dev, const __be32 *in_addr, pbus->count_cells(dev, &pna, &pns); if (!OF_CHECK_COUNTS(pna, pns)) { printk(KERN_ERR "prom_parse: Bad cell count for %s\n", - dev->full_name); + of_node_full_name(dev)); break; } pr_debug("OF: parent bus is %s (na=%d, ns=%d) on %s\n", - pbus->name, pna, pns, parent->full_name); + pbus->name, pna, pns, of_node_full_name(parent)); /* Apply bus translation */ if (of_translate_one(dev, bus, pbus, addr, na, ns, pna, rprop)) @@ -505,7 +579,7 @@ const __be32 *of_get_address(struct device_node *dev, int index, u64 *size, bus = of_match_bus(parent); bus->count_cells(dev, &na, &ns); of_node_put(parent); - if (!OF_CHECK_COUNTS(na, ns)) + if (!OF_CHECK_ADDR_COUNT(na)) return NULL; /* Get "reg" or "assigned-addresses" property */ @@ -527,6 +601,14 @@ const __be32 *of_get_address(struct device_node *dev, int index, u64 *size, } EXPORT_SYMBOL(of_get_address); +unsigned long __weak pci_address_to_pio(phys_addr_t address) +{ + if (address > IO_SPACE_LIMIT) + return (unsigned long)-1; + + return (unsigned long) address; +} + static int __of_address_to_resource(struct device_node *dev, const __be32 *addrp, u64 size, unsigned int flags, const char *name, struct resource *r) @@ -619,3 +701,113 @@ void __iomem *of_iomap(struct device_node *np, int index) return ioremap(res.start, resource_size(&res)); } EXPORT_SYMBOL(of_iomap); + +/** + * of_dma_get_range - Get DMA range info + * @np: device node to get DMA range info + * @dma_addr: pointer to store initial DMA address of DMA range + * @paddr: pointer to store initial CPU address of DMA range + * @size: pointer to store size of DMA range + * + * Look in bottom up direction for the first "dma-ranges" property + * and parse it. + * dma-ranges format: + * DMA addr (dma_addr) : naddr cells + * CPU addr (phys_addr_t) : pna cells + * size : nsize cells + * + * It returns -ENODEV if "dma-ranges" property was not found + * for this device in DT. + */ +int of_dma_get_range(struct device_node *np, u64 *dma_addr, u64 *paddr, u64 *size) +{ + struct device_node *node = of_node_get(np); + const __be32 *ranges = NULL; + int len, naddr, nsize, pna; + int ret = 0; + u64 dmaaddr; + + if (!node) + return -EINVAL; + + while (1) { + naddr = of_n_addr_cells(node); + nsize = of_n_size_cells(node); + node = of_get_next_parent(node); + if (!node) + break; + + ranges = of_get_property(node, "dma-ranges", &len); + + /* Ignore empty ranges, they imply no translation required */ + if (ranges && len > 0) + break; + + /* + * At least empty ranges has to be defined for parent node if + * DMA is supported + */ + if (!ranges) + break; + } + + if (!ranges) { + pr_debug("%s: no dma-ranges found for node(%s)\n", + __func__, np->full_name); + ret = -ENODEV; + goto out; + } + + len /= sizeof(u32); + + pna = of_n_addr_cells(node); + + /* dma-ranges format: + * DMA addr : naddr cells + * CPU addr : pna cells + * size : nsize cells + */ + dmaaddr = of_read_number(ranges, naddr); + *paddr = of_translate_dma_address(np, ranges); + if (*paddr == OF_BAD_ADDR) { + pr_err("%s: translation of DMA address(%pad) to CPU address failed node(%s)\n", + __func__, dma_addr, np->full_name); + ret = -EINVAL; + goto out; + } + *dma_addr = dmaaddr; + + *size = of_read_number(ranges + naddr + pna, nsize); + + pr_debug("dma_addr(%llx) cpu_addr(%llx) size(%llx)\n", + *dma_addr, *paddr, *size); + +out: + of_node_put(node); + + return ret; +} +EXPORT_SYMBOL_GPL(of_dma_get_range); + +/** + * of_dma_is_coherent - Check if device is coherent + * @np: device node + * + * It returns true if "dma-coherent" property was found + * for this device in DT. + */ +bool of_dma_is_coherent(struct device_node *np) +{ + struct device_node *node = of_node_get(np); + + while (node) { + if (of_property_read_bool(node, "dma-coherent")) { + of_node_put(node); + return true; + } + node = of_get_next_parent(node); + } + of_node_put(node); + return false; +} +EXPORT_SYMBOL_GPL(of_dma_is_coherent); |
