Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 81039
b: refs/heads/master
c: 13dccb9
h: refs/heads/master
i:
  81037: 7af7e8b
  81035: 795165c
  81031: 937b1a3
  81023: 8ca99a4
v: v3
  • Loading branch information
Benjamin Herrenschmidt authored and Paul Mackerras committed Dec 11, 2007
1 parent 6a74387 commit ac1ff1c
Show file tree
Hide file tree
Showing 5 changed files with 181 additions and 208 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 25e81f925d4be0a0f60520e1c3c1b5af744404e1
refs/heads/master: 13dccb9e65dc0fa4de83e5bd5639f7a7f3f6fb9e
177 changes: 177 additions & 0 deletions trunk/arch/powerpc/kernel/pci-common.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
114 changes: 0 additions & 114 deletions trunk/arch/powerpc/kernel/pci_32.c
Original file line number Diff line number Diff line change
Expand Up @@ -842,120 +842,6 @@ pci_device_from_OF_node(struct device_node* node, u8* bus, u8* devfn)
}
EXPORT_SYMBOL(pci_device_from_OF_node);

void __init
pci_process_bridge_OF_ranges(struct pci_controller *hose,
struct device_node *dev, int primary)
{
static unsigned int static_lc_ranges[256] __initdata;
const unsigned int *dt_ranges;
unsigned int *lc_ranges, *ranges, *prev, size;
int rlen = 0, orig_rlen;
int memno = 0;
struct resource *res;
int np, na = of_n_addr_cells(dev);
np = na + 5;

/* First we try to merge ranges to fix a problem with some pmacs
* that can have more than 3 ranges, fortunately using contiguous
* addresses -- BenH
*/
dt_ranges = of_get_property(dev, "ranges", &rlen);
if (!dt_ranges)
return;
/* Sanity check, though hopefully that never happens */
if (rlen > sizeof(static_lc_ranges)) {
printk(KERN_WARNING "OF ranges property too large !\n");
rlen = sizeof(static_lc_ranges);
}
lc_ranges = static_lc_ranges;
memcpy(lc_ranges, dt_ranges, rlen);
orig_rlen = rlen;

/* Let's work on a copy of the "ranges" property instead of damaging
* the device-tree image in memory
*/
ranges = lc_ranges;
prev = NULL;
while ((rlen -= np * sizeof(unsigned int)) >= 0) {
if (prev) {
if (prev[0] == ranges[0] && prev[1] == ranges[1] &&
(prev[2] + prev[na+4]) == ranges[2] &&
(prev[na+2] + prev[na+4]) == ranges[na+2]) {
prev[na+4] += ranges[na+4];
ranges[0] = 0;
ranges += np;
continue;
}
}
prev = ranges;
ranges += np;
}

/*
* The ranges property is laid out as an array of elements,
* each of which comprises:
* cells 0 - 2: a PCI address
* cells 3 or 3+4: a CPU physical address
* (size depending on dev->n_addr_cells)
* cells 4+5 or 5+6: the size of the range
*/
ranges = lc_ranges;
rlen = orig_rlen;
while (ranges && (rlen -= np * sizeof(unsigned int)) >= 0) {
res = NULL;
size = ranges[na+4];
switch ((ranges[0] >> 24) & 0x3) {
case 1: /* I/O space */
if (ranges[2] != 0)
break;
hose->io_base_phys = ranges[na+2];
/* limit I/O space to 16MB */
if (size > 0x01000000)
size = 0x01000000;
hose->io_base_virt = ioremap(ranges[na+2], size);
if (primary)
isa_io_base = (unsigned long) hose->io_base_virt;
res = &hose->io_resource;
res->flags = IORESOURCE_IO;
res->start = ranges[2];
DBG("PCI: IO 0x%llx -> 0x%llx\n",
(u64)res->start, (u64)res->start + size - 1);
break;
case 2: /* memory space */
memno = 0;
if (ranges[1] == 0 && ranges[2] == 0
&& ranges[na+4] <= (16 << 20)) {
/* 1st 16MB, i.e. ISA memory area */
if (primary)
isa_mem_base = ranges[na+2];
memno = 1;
}
while (memno < 3 && hose->mem_resources[memno].flags)
++memno;
if (memno == 0)
hose->pci_mem_offset = ranges[na+2] - ranges[2];
if (memno < 3) {
res = &hose->mem_resources[memno];
res->flags = IORESOURCE_MEM;
if(ranges[0] & 0x40000000)
res->flags |= IORESOURCE_PREFETCH;
res->start = ranges[na+2];
DBG("PCI: MEM[%d] 0x%llx -> 0x%llx\n", memno,
(u64)res->start, (u64)res->start + size - 1);
}
break;
}
if (res != NULL) {
res->name = dev->full_name;
res->end = res->start + size - 1;
res->parent = NULL;
res->sibling = NULL;
res->child = NULL;
}
ranges += np;
}
}

/* We create the "pci-OF-bus-map" property now so it appears in the
* /proc device tree
*/
Expand Down
Loading

0 comments on commit ac1ff1c

Please sign in to comment.