of/pci: Provide support for parsing PCI DT ranges property
[sfrench/cifs-2.6.git] / drivers / of / address.c
index 04da786c84d2451b79be8bcbcb61c469030f977e..fdd0636a987d1fe9165c0d4917d86efc8cce40c3 100644 (file)
@@ -227,6 +227,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 */
 
 /*