From b22da3d8526a935aa31e086e63f60ff3246cb61c Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Sat, 09 Dec 2023 07:24:11 +0000 Subject: [PATCH] add stmac read mac form eeprom --- kernel/drivers/of/address.c | 461 +++++++++++++++++++++++++++++++++++---------------------- 1 files changed, 281 insertions(+), 180 deletions(-) diff --git a/kernel/drivers/of/address.c b/kernel/drivers/of/address.c index 30806dd..73ddf25 100644 --- a/kernel/drivers/of/address.c +++ b/kernel/drivers/of/address.c @@ -13,6 +13,9 @@ #include <linux/sizes.h> #include <linux/slab.h> #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 @@ -47,6 +50,7 @@ u64 (*map)(__be32 *addr, const __be32 *range, int na, int ns, int pna); int (*translate)(__be32 *addr, u64 offset, int na); + bool has_flags; unsigned int (*get_flags)(const __be32 *addr); }; @@ -99,34 +103,13 @@ } #ifdef CONFIG_PCI -/* - * PCI bus specific translator - */ - -static int of_bus_pci_match(struct device_node *np) -{ - /* - * "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, - int *addrc, int *sizec) -{ - if (addrc) - *addrc = 3; - if (sizec) - *sizec = 2; -} - static unsigned int of_bus_pci_get_flags(const __be32 *addr) { unsigned int flags = 0; u32 w = be32_to_cpup(addr); + + if (!IS_ENABLED(CONFIG_PCI)) + return 0; switch((w >> 24) & 0x03) { case 0x01: @@ -140,6 +123,44 @@ if (w & 0x40000000) flags |= IORESOURCE_PREFETCH; return flags; +} + +/* + * PCI bus specific translator + */ + +static bool of_node_is_pcie(struct device_node *np) +{ + bool is_pcie = of_node_name_eq(np, "pcie"); + + if (is_pcie) + pr_warn_once("%pOF: Missing device_type\n", np); + + return is_pcie; +} + +static int of_bus_pci_match(struct device_node *np) +{ + /* + * "pciex" is PCI Express + * "vci" is for the /chaos bridge on 1st-gen PCI powermacs + * "ht" is hypertransport + * + * If none of the device_type match, and that the node name is + * "pcie", accept the device as PCI (with a warning). + */ + return of_node_is_type(np, "pci") || of_node_is_type(np, "pciex") || + of_node_is_type(np, "vci") || of_node_is_type(np, "ht") || + of_node_is_pcie(np); +} + +static void of_bus_pci_count_cells(struct device_node *np, + int *addrc, int *sizec) +{ + if (addrc) + *addrc = 3; + if (sizec) + *sizec = 2; } static u64 of_bus_pci_map(__be32 *addr, const __be32 *range, int na, int ns, @@ -232,84 +253,6 @@ } EXPORT_SYMBOL_GPL(of_pci_address_to_resource); -static int parser_init(struct of_pci_range_parser *parser, - struct device_node *node, const char *name) -{ - 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, name, &rlen); - if (parser->range == NULL) - return -ENOENT; - - parser->end = parser->range + rlen / sizeof(__be32); - - return 0; -} - -int of_pci_range_parser_init(struct of_pci_range_parser *parser, - struct device_node *node) -{ - return parser_init(parser, node, "ranges"); -} -EXPORT_SYMBOL_GPL(of_pci_range_parser_init); - -int of_pci_dma_range_parser_init(struct of_pci_range_parser *parser, - struct device_node *node) -{ - return parser_init(parser, node, "dma-ranges"); -} -EXPORT_SYMBOL_GPL(of_pci_dma_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 = be32_to_cpup(parser->range); - 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; - u64 pci_addr, cpu_addr, size; - - 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); - /* * of_pci_range_to_resource - Create a resource from an of_pci_range * @range: the PCI range that describes the resource @@ -371,7 +314,7 @@ static int of_bus_isa_match(struct device_node *np) { - return !strcmp(np->name, "isa"); + return of_node_name_eq(np, "isa"); } static void of_bus_isa_count_cells(struct device_node *child, @@ -437,6 +380,7 @@ .count_cells = of_bus_pci_count_cells, .map = of_bus_pci_map, .translate = of_bus_pci_translate, + .has_flags = true, .get_flags = of_bus_pci_get_flags, }, #endif /* CONFIG_PCI */ @@ -448,6 +392,7 @@ .count_cells = of_bus_isa_count_cells, .map = of_bus_isa_map, .translate = of_bus_isa_translate, + .has_flags = true, .get_flags = of_bus_isa_get_flags, }, /* Default */ @@ -517,9 +462,13 @@ * * As far as we know, this damage only exists on Apple machines, so * This code is only enabled on powerpc. --gcl + * + * This quirk also applies for 'dma-ranges' which frequently exist in + * child nodes without 'dma-ranges' in the parent nodes. --RobH */ ranges = of_get_property(parent, rprop, &rlen); - if (ranges == NULL && !of_empty_ranges_quirk(parent)) { + if (ranges == NULL && !of_empty_ranges_quirk(parent) && + strcmp(rprop, "dma-ranges")) { pr_debug("no ranges; cannot translate\n"); return 1; } @@ -569,6 +518,7 @@ * relative to that node. */ static u64 __of_translate_address(struct device_node *dev, + struct device_node *(*get_parent)(const struct device_node *), const __be32 *in_addr, const char *rprop, struct device_node **host) { @@ -585,7 +535,7 @@ *host = NULL; /* Get parent & match bus type */ - parent = of_get_parent(dev); + parent = get_parent(dev); if (parent == NULL) goto bail; bus = of_match_bus(parent); @@ -609,7 +559,7 @@ /* Switch to parent bus */ of_node_put(dev); dev = parent; - parent = of_get_parent(dev); + parent = get_parent(dev); /* If root, we have finished */ if (parent == NULL) { @@ -665,7 +615,8 @@ struct device_node *host; u64 ret; - ret = __of_translate_address(dev, in_addr, "ranges", &host); + ret = __of_translate_address(dev, of_get_parent, + in_addr, "ranges", &host); if (host) { of_node_put(host); return OF_BAD_ADDR; @@ -675,12 +626,41 @@ } EXPORT_SYMBOL(of_translate_address); +static struct device_node *__of_get_dma_parent(const struct device_node *np) +{ + struct of_phandle_args args; + int ret, index; + + index = of_property_match_string(np, "interconnect-names", "dma-mem"); + if (index < 0) + return of_get_parent(np); + + ret = of_parse_phandle_with_args(np, "interconnects", + "#interconnect-cells", + index, &args); + if (ret < 0) + return of_get_parent(np); + + return of_node_get(args.np); +} + +static struct device_node *of_get_next_dma_parent(struct device_node *np) +{ + struct device_node *parent; + + parent = __of_get_dma_parent(np); + of_node_put(np); + + return parent; +} + u64 of_translate_dma_address(struct device_node *dev, const __be32 *in_addr) { struct device_node *host; u64 ret; - ret = __of_translate_address(dev, in_addr, "dma-ranges", &host); + ret = __of_translate_address(dev, __of_get_dma_parent, + in_addr, "dma-ranges", &host); if (host) { of_node_put(host); @@ -729,6 +709,103 @@ } EXPORT_SYMBOL(of_get_address); +static int parser_init(struct of_pci_range_parser *parser, + struct device_node *node, const char *name) +{ + int rlen; + + parser->node = node; + parser->pna = of_n_addr_cells(node); + parser->na = of_bus_n_addr_cells(node); + parser->ns = of_bus_n_size_cells(node); + parser->dma = !strcmp(name, "dma-ranges"); + parser->bus = of_match_bus(node); + + parser->range = of_get_property(node, name, &rlen); + if (parser->range == NULL) + return -ENOENT; + + parser->end = parser->range + rlen / sizeof(__be32); + + return 0; +} + +int of_pci_range_parser_init(struct of_pci_range_parser *parser, + struct device_node *node) +{ + return parser_init(parser, node, "ranges"); +} +EXPORT_SYMBOL_GPL(of_pci_range_parser_init); + +int of_pci_dma_range_parser_init(struct of_pci_range_parser *parser, + struct device_node *node) +{ + return parser_init(parser, node, "dma-ranges"); +} +EXPORT_SYMBOL_GPL(of_pci_dma_range_parser_init); +#define of_dma_range_parser_init of_pci_dma_range_parser_init + +struct of_pci_range *of_pci_range_parser_one(struct of_pci_range_parser *parser, + struct of_pci_range *range) +{ + int na = parser->na; + int ns = parser->ns; + int np = parser->pna + na + ns; + int busflag_na = 0; + + if (!range) + return NULL; + + if (!parser->range || parser->range + np > parser->end) + return NULL; + + 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) + range->cpu_addr = of_translate_dma_address(parser->node, + parser->range + na); + else + range->cpu_addr = of_translate_address(parser->node, + parser->range + na); + range->size = of_read_number(parser->range + parser->pna + na, ns); + + parser->range += np; + + /* Now consume following elements while they are contiguous */ + while (parser->range + np <= parser->end) { + u32 flags = 0; + u64 bus_addr, cpu_addr, size; + + flags = parser->bus->get_flags(parser->range); + bus_addr = of_read_number(parser->range + busflag_na, na - busflag_na); + if (parser->dma) + cpu_addr = of_translate_dma_address(parser->node, + parser->range + na); + else + 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 (bus_addr != range->bus_addr + range->size || + cpu_addr != range->cpu_addr + range->size) + break; + + range->size += size; + parser->range += np; + } + + return range; +} +EXPORT_SYMBOL_GPL(of_pci_range_parser_one); + static u64 of_translate_ioport(struct device_node *dev, const __be32 *in_addr, u64 size) { @@ -736,7 +813,8 @@ unsigned long port; struct device_node *host; - taddr = __of_translate_address(dev, in_addr, "ranges", &host); + taddr = __of_translate_address(dev, of_get_parent, + in_addr, "ranges", &host); if (host) { /* host-specific port access */ port = logic_pio_trans_hwaddr(&host->fwnode, taddr, size); @@ -804,28 +882,9 @@ } EXPORT_SYMBOL_GPL(of_address_to_resource); -struct device_node *of_find_matching_node_by_address(struct device_node *from, - const struct of_device_id *matches, - u64 base_address) -{ - struct device_node *dn = of_find_matching_node(from, matches); - struct resource res; - - while (dn) { - if (!of_address_to_resource(dn, 0, &res) && - res.start == base_address) - return dn; - - dn = of_find_matching_node(dn, matches); - } - - return NULL; -} - - /** * of_iomap - Maps the memory mapped IO for a given device_node - * @device: the device whose io range will be mapped + * @np: the device whose io range will be mapped * @index: index of the io range * * Returns a pointer to the mapped memory @@ -879,91 +938,133 @@ } EXPORT_SYMBOL(of_io_request_and_map); +#ifdef CONFIG_HAS_DMA /** - * of_dma_get_range - Get DMA range info + * of_dma_get_range - Get DMA range info and put it into a map array * @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 + * @map: dma range structure to return * * Look in bottom up direction for the first "dma-ranges" property - * and parse it. - * dma-ranges format: + * and parse it. Put the information into a DMA offset map array. + * + * 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. + * It returns -ENODEV if "dma-ranges" property was not found for this + * device in the DT. */ -int of_dma_get_range(struct device_node *np, u64 *dma_addr, u64 *paddr, u64 *size) +int of_dma_get_range(struct device_node *np, const struct bus_dma_region **map) { struct device_node *node = of_node_get(np); const __be32 *ranges = NULL; - int len, naddr, nsize, pna; + 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; - 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; - + while (node) { 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; + /* Once we find 'dma-ranges', then a missing one is an error */ + if (found_dma_ranges && !ranges) { + ret = -ENODEV; + goto out; + } + found_dma_ranges = true; + + node = of_get_next_dma_parent(node); } - if (!ranges) { + if (!node || !ranges) { pr_debug("no dma-ranges found for node(%pOF)\n", np); ret = -ENODEV; goto out; } - len /= sizeof(u32); + of_dma_range_parser_init(&parser, node); + for_each_of_range(&parser, &range) + num_ranges++; - 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("translation of DMA address(%pad) to CPU address failed node(%pOF)\n", - dma_addr, np); - ret = -EINVAL; + r = kcalloc(num_ranges + 1, sizeof(*r), GFP_KERNEL); + if (!r) { + ret = -ENOMEM; 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); - + /* + * Record all info in the generic DMA ranges array for struct device. + */ + *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); + 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; } -EXPORT_SYMBOL_GPL(of_dma_get_range); +#endif /* CONFIG_HAS_DMA */ + +/** + * of_dma_get_max_cpu_address - Gets highest CPU address suitable for DMA + * @np: The node to start searching from or NULL to start from the root + * + * Gets the highest CPU physical address that is addressable by all DMA masters + * in the sub-tree pointed by np, or the whole tree if NULL is passed. If no + * DMA constrained device is found, it returns PHYS_ADDR_MAX. + */ +phys_addr_t __init of_dma_get_max_cpu_address(struct device_node *np) +{ + phys_addr_t max_cpu_addr = PHYS_ADDR_MAX; + struct of_range_parser parser; + phys_addr_t subtree_max_addr; + struct device_node *child; + struct of_range range; + const __be32 *ranges; + u64 cpu_end = 0; + int len; + + if (!np) + np = of_root; + + ranges = of_get_property(np, "dma-ranges", &len); + if (ranges && len) { + of_dma_range_parser_init(&parser, np); + for_each_of_range(&parser, &range) + if (range.cpu_addr + range.size > cpu_end) + cpu_end = range.cpu_addr + range.size - 1; + + if (max_cpu_addr > cpu_end) + max_cpu_addr = cpu_end; + } + + for_each_available_child_of_node(np, child) { + subtree_max_addr = of_dma_get_max_cpu_address(child); + if (max_cpu_addr > subtree_max_addr) + max_cpu_addr = subtree_max_addr; + } + + return max_cpu_addr; +} /** * of_dma_is_coherent - Check if device is coherent @@ -987,7 +1088,7 @@ of_node_put(node); return true; } - node = of_get_next_parent(node); + node = of_get_next_dma_parent(node); } of_node_put(node); return false; -- Gitblit v1.6.2