|
@@ -462,3 +462,180 @@ void pci_resource_to_user(const struct pci_dev *dev, int bar,
|
|
|
*start = rsrc->start - offset;
|
|
|
*end = rsrc->end - offset;
|
|
|
}
|
|
|
+
|
|
|
+/**
|
|
|
+ * pci_process_bridge_OF_ranges - Parse PCI bridge resources from device tree
|
|
|
+ * @hose: newly allocated pci_controller to be setup
|
|
|
+ * @dev: device node of the host bridge
|
|
|
+ * @primary: set if primary bus (32 bits only, soon to be deprecated)
|
|
|
+ *
|
|
|
+ * This function will parse the "ranges" property of a PCI host bridge device
|
|
|
+ * node and setup the resource mapping of a pci controller based on its
|
|
|
+ * content.
|
|
|
+ *
|
|
|
+ * Life would be boring if it wasn't for a few issues that we have to deal
|
|
|
+ * with here:
|
|
|
+ *
|
|
|
+ * - We can only cope with one IO space range and up to 3 Memory space
|
|
|
+ * ranges. However, some machines (thanks Apple !) tend to split their
|
|
|
+ * space into lots of small contiguous ranges. So we have to coalesce.
|
|
|
+ *
|
|
|
+ * - We can only cope with all memory ranges having the same offset
|
|
|
+ * between CPU addresses and PCI addresses. Unfortunately, some bridges
|
|
|
+ * are setup for a large 1:1 mapping along with a small "window" which
|
|
|
+ * maps PCI address 0 to some arbitrary high address of the CPU space in
|
|
|
+ * order to give access to the ISA memory hole.
|
|
|
+ * The way out of here that I've chosen for now is to always set the
|
|
|
+ * offset based on the first resource found, then override it if we
|
|
|
+ * have a different offset and the previous was set by an ISA hole.
|
|
|
+ *
|
|
|
+ * - Some busses have IO space not starting at 0, which causes trouble with
|
|
|
+ * the way we do our IO resource renumbering. The code somewhat deals with
|
|
|
+ * it for 64 bits but I would expect problems on 32 bits.
|
|
|
+ *
|
|
|
+ * - Some 32 bits platforms such as 4xx can have physical space larger than
|
|
|
+ * 32 bits so we need to use 64 bits values for the parsing
|
|
|
+ */
|
|
|
+void __devinit pci_process_bridge_OF_ranges(struct pci_controller *hose,
|
|
|
+ struct device_node *dev,
|
|
|
+ int primary)
|
|
|
+{
|
|
|
+ const u32 *ranges;
|
|
|
+ int rlen;
|
|
|
+ int pna = of_n_addr_cells(dev);
|
|
|
+ int np = pna + 5;
|
|
|
+ int memno = 0, isa_hole = -1;
|
|
|
+ u32 pci_space;
|
|
|
+ unsigned long long pci_addr, cpu_addr, pci_next, cpu_next, size;
|
|
|
+ unsigned long long isa_mb = 0;
|
|
|
+ struct resource *res;
|
|
|
+
|
|
|
+ printk(KERN_INFO "PCI host bridge %s %s ranges:\n",
|
|
|
+ dev->full_name, primary ? "(primary)" : "");
|
|
|
+
|
|
|
+ /* Get ranges property */
|
|
|
+ ranges = of_get_property(dev, "ranges", &rlen);
|
|
|
+ if (ranges == NULL)
|
|
|
+ return;
|
|
|
+
|
|
|
+ /* Parse it */
|
|
|
+ while ((rlen -= np * 4) >= 0) {
|
|
|
+ /* Read next ranges element */
|
|
|
+ pci_space = ranges[0];
|
|
|
+ pci_addr = of_read_number(ranges + 1, 2);
|
|
|
+ cpu_addr = of_translate_address(dev, ranges + 3);
|
|
|
+ size = of_read_number(ranges + pna + 3, 2);
|
|
|
+ ranges += np;
|
|
|
+ if (cpu_addr == OF_BAD_ADDR || size == 0)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ /* Now consume following elements while they are contiguous */
|
|
|
+ for (; rlen >= np * sizeof(u32);
|
|
|
+ ranges += np, rlen -= np * 4) {
|
|
|
+ if (ranges[0] != pci_space)
|
|
|
+ break;
|
|
|
+ pci_next = of_read_number(ranges + 1, 2);
|
|
|
+ cpu_next = of_translate_address(dev, ranges + 3);
|
|
|
+ if (pci_next != pci_addr + size ||
|
|
|
+ cpu_next != cpu_addr + size)
|
|
|
+ break;
|
|
|
+ size += of_read_number(ranges + pna + 3, 2);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Act based on address space type */
|
|
|
+ res = NULL;
|
|
|
+ switch ((pci_space >> 24) & 0x3) {
|
|
|
+ case 1: /* PCI IO space */
|
|
|
+ printk(KERN_INFO
|
|
|
+ " IO 0x%016llx..0x%016llx -> 0x%016llx\n",
|
|
|
+ cpu_addr, cpu_addr + size - 1, pci_addr);
|
|
|
+
|
|
|
+ /* We support only one IO range */
|
|
|
+ if (hose->pci_io_size) {
|
|
|
+ printk(KERN_INFO
|
|
|
+ " \\--> Skipped (too many) !\n");
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+#ifdef CONFIG_PPC32
|
|
|
+ /* On 32 bits, limit I/O space to 16MB */
|
|
|
+ if (size > 0x01000000)
|
|
|
+ size = 0x01000000;
|
|
|
+
|
|
|
+ /* 32 bits needs to map IOs here */
|
|
|
+ hose->io_base_virt = ioremap(cpu_addr, size);
|
|
|
+
|
|
|
+ /* Expect trouble if pci_addr is not 0 */
|
|
|
+ if (primary)
|
|
|
+ isa_io_base =
|
|
|
+ (unsigned long)hose->io_base_virt;
|
|
|
+#endif /* CONFIG_PPC32 */
|
|
|
+ /* pci_io_size and io_base_phys always represent IO
|
|
|
+ * space starting at 0 so we factor in pci_addr
|
|
|
+ */
|
|
|
+ hose->pci_io_size = pci_addr + size;
|
|
|
+ hose->io_base_phys = cpu_addr - pci_addr;
|
|
|
+
|
|
|
+ /* Build resource */
|
|
|
+ res = &hose->io_resource;
|
|
|
+ res->flags = IORESOURCE_IO;
|
|
|
+ res->start = pci_addr;
|
|
|
+ break;
|
|
|
+ case 2: /* PCI Memory space */
|
|
|
+ printk(KERN_INFO
|
|
|
+ " MEM 0x%016llx..0x%016llx -> 0x%016llx %s\n",
|
|
|
+ cpu_addr, cpu_addr + size - 1, pci_addr,
|
|
|
+ (pci_space & 0x40000000) ? "Prefetch" : "");
|
|
|
+
|
|
|
+ /* We support only 3 memory ranges */
|
|
|
+ if (memno >= 3) {
|
|
|
+ printk(KERN_INFO
|
|
|
+ " \\--> Skipped (too many) !\n");
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ /* Handles ISA memory hole space here */
|
|
|
+ if (pci_addr == 0) {
|
|
|
+ isa_mb = cpu_addr;
|
|
|
+ isa_hole = memno;
|
|
|
+ if (primary || isa_mem_base == 0)
|
|
|
+ isa_mem_base = cpu_addr;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* We get the PCI/Mem offset from the first range or
|
|
|
+ * the, current one if the offset came from an ISA
|
|
|
+ * hole. If they don't match, bugger.
|
|
|
+ */
|
|
|
+ if (memno == 0 ||
|
|
|
+ (isa_hole >= 0 && pci_addr != 0 &&
|
|
|
+ hose->pci_mem_offset == isa_mb))
|
|
|
+ hose->pci_mem_offset = cpu_addr - pci_addr;
|
|
|
+ else if (pci_addr != 0 &&
|
|
|
+ hose->pci_mem_offset != cpu_addr - pci_addr) {
|
|
|
+ printk(KERN_INFO
|
|
|
+ " \\--> Skipped (offset mismatch) !\n");
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Build resource */
|
|
|
+ res = &hose->mem_resources[memno++];
|
|
|
+ res->flags = IORESOURCE_MEM;
|
|
|
+ if (pci_space & 0x40000000)
|
|
|
+ res->flags |= IORESOURCE_PREFETCH;
|
|
|
+ res->start = cpu_addr;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if (res != NULL) {
|
|
|
+ res->name = dev->full_name;
|
|
|
+ res->end = res->start + size - 1;
|
|
|
+ res->parent = NULL;
|
|
|
+ res->sibling = NULL;
|
|
|
+ res->child = NULL;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Out of paranoia, let's put the ISA hole last if any */
|
|
|
+ if (isa_hole >= 0 && memno > 0 && isa_hole != (memno-1)) {
|
|
|
+ struct resource tmp = hose->mem_resources[isa_hole];
|
|
|
+ hose->mem_resources[isa_hole] = hose->mem_resources[memno-1];
|
|
|
+ hose->mem_resources[memno-1] = tmp;
|
|
|
+ }
|
|
|
+}
|