Skip to content

Commit

Permalink
[PATCH] powerpc: pci_64 fixes & cleanups
Browse files Browse the repository at this point in the history
I discovered that in some cases (PowerMac for example) we wouldn't
properly map the PCI IO space on recent kernels. In addition, the code
for initializing PCI host bridges was scattered all over the place with
some duplication between platforms.

This patch fixes the problem and does a small cleanup by creating a
pcibios_alloc_controller() in pci_64.c that is similar to the one in
pci_32.c (just takes an additional device node argument) that takes care
of all the grunt allocation and initialisation work. It should work for
both boot time and dynamically allocated PHBs.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Paul Mackerras <paulus@samba.org>
  • Loading branch information
Benjamin Herrenschmidt authored and Paul Mackerras committed Nov 16, 2005
1 parent f9e4ec5 commit b5166cc
Show file tree
Hide file tree
Showing 7 changed files with 114 additions and 118 deletions.
68 changes: 65 additions & 3 deletions arch/powerpc/kernel/pci_64.c
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ static DEFINE_SPINLOCK(hose_spinlock);
/*
* pci_controller(phb) initialized common variables.
*/
void __devinit pci_setup_pci_controller(struct pci_controller *hose)
static void __devinit pci_setup_pci_controller(struct pci_controller *hose)
{
memset(hose, 0, sizeof(struct pci_controller));

Expand All @@ -197,6 +197,65 @@ void __devinit pci_setup_pci_controller(struct pci_controller *hose)
spin_unlock(&hose_spinlock);
}

static void add_linux_pci_domain(struct device_node *dev,
struct pci_controller *phb)
{
struct property *of_prop;
unsigned int size;

of_prop = (struct property *)
get_property(dev, "linux,pci-domain", &size);
if (of_prop != NULL)
return;
WARN_ON(of_prop && size < sizeof(int));
if (of_prop && size < sizeof(int))
of_prop = NULL;
size = sizeof(struct property) + sizeof(int);
if (of_prop == NULL) {
if (mem_init_done)
of_prop = kmalloc(size, GFP_KERNEL);
else
of_prop = alloc_bootmem(size);
}
memset(of_prop, 0, sizeof(struct property));
of_prop->name = "linux,pci-domain";
of_prop->length = sizeof(int);
of_prop->value = (unsigned char *)&of_prop[1];
*((int *)of_prop->value) = phb->global_number;
prom_add_property(dev, of_prop);
}

struct pci_controller * pcibios_alloc_controller(struct device_node *dev)
{
struct pci_controller *phb;

if (mem_init_done)
phb = kmalloc(sizeof(struct pci_controller), GFP_KERNEL);
else
phb = alloc_bootmem(sizeof (struct pci_controller));
if (phb == NULL)
return NULL;
pci_setup_pci_controller(phb);
phb->arch_data = dev;
phb->is_dynamic = mem_init_done;
if (dev)
add_linux_pci_domain(dev, phb);
return phb;
}

void pcibios_free_controller(struct pci_controller *phb)
{
if (phb->arch_data) {
struct device_node *np = phb->arch_data;
int *domain = (int *)get_property(np,
"linux,pci-domain", NULL);
if (domain)
*domain = -1;
}
if (phb->is_dynamic)
kfree(phb);
}

static void __init pcibios_claim_one_bus(struct pci_bus *b)
{
struct pci_dev *dev;
Expand Down Expand Up @@ -907,9 +966,10 @@ void __devinit pci_process_bridge_OF_ranges(struct pci_controller *hose,
* (size depending on dev->n_addr_cells)
* cells 4+5 or 5+6: the size of the range
*/
rlen = 0;
hose->io_base_phys = 0;
ranges = (unsigned int *) get_property(dev, "ranges", &rlen);
if (ranges == NULL)
return;
hose->io_base_phys = 0;
while ((rlen -= np * sizeof(unsigned int)) >= 0) {
res = NULL;
pci_space = ranges[0];
Expand Down Expand Up @@ -1107,6 +1167,8 @@ int remap_bus_range(struct pci_bus *bus)

if (get_bus_io_range(bus, &start_phys, &start_virt, &size))
return 1;
if (start_phys == 0)
return 1;
printk("mapping IO %lx -> %lx, size: %lx\n", start_phys, start_virt, size);
if (__ioremap_explicit(start_phys, start_virt, size,
_PAGE_NO_CACHE | _PAGE_GUARDED))
Expand Down
68 changes: 5 additions & 63 deletions arch/powerpc/kernel/rtas_pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -304,75 +304,18 @@ static int __devinit setup_phb(struct device_node *dev,
struct pci_controller *phb,
unsigned int addr_size_words)
{
pci_setup_pci_controller(phb);

if (is_python(dev))
python_countermeasures(dev, addr_size_words);

if (phb_set_bus_ranges(dev, phb))
return 1;

phb->arch_data = dev;
phb->ops = &rtas_pci_ops;
phb->buid = get_phb_buid(dev);

return 0;
}

static void __devinit add_linux_pci_domain(struct device_node *dev,
struct pci_controller *phb,
struct property *of_prop)
{
memset(of_prop, 0, sizeof(struct property));
of_prop->name = "linux,pci-domain";
of_prop->length = sizeof(phb->global_number);
of_prop->value = (unsigned char *)&of_prop[1];
memcpy(of_prop->value, &phb->global_number, sizeof(phb->global_number));
prom_add_property(dev, of_prop);
}

static struct pci_controller * __init alloc_phb(struct device_node *dev,
unsigned int addr_size_words)
{
struct pci_controller *phb;
struct property *of_prop;

phb = alloc_bootmem(sizeof(struct pci_controller));
if (phb == NULL)
return NULL;

of_prop = alloc_bootmem(sizeof(struct property) +
sizeof(phb->global_number));
if (!of_prop)
return NULL;

if (setup_phb(dev, phb, addr_size_words))
return NULL;

add_linux_pci_domain(dev, phb, of_prop);

return phb;
}

static struct pci_controller * __devinit alloc_phb_dynamic(struct device_node *dev, unsigned int addr_size_words)
{
struct pci_controller *phb;

phb = (struct pci_controller *)kmalloc(sizeof(struct pci_controller),
GFP_KERNEL);
if (phb == NULL)
return NULL;

if (setup_phb(dev, phb, addr_size_words))
return NULL;

phb->is_dynamic = 1;

/* TODO: linux,pci-domain? */

return phb;
}

unsigned long __init find_and_init_phbs(void)
{
struct device_node *node;
Expand All @@ -397,10 +340,10 @@ unsigned long __init find_and_init_phbs(void)
if (node->type == NULL || strcmp(node->type, "pci") != 0)
continue;

phb = alloc_phb(node, root_size_cells);
phb = pcibios_alloc_controller(node);
if (!phb)
continue;

setup_phb(node, phb, root_size_cells);
pci_process_bridge_OF_ranges(phb, node, 0);
pci_setup_phb_io(phb, index == 0);
#ifdef CONFIG_PPC_PSERIES
Expand Down Expand Up @@ -446,10 +389,10 @@ struct pci_controller * __devinit init_phb_dynamic(struct device_node *dn)
root_size_cells = prom_n_size_cells(root);

primary = list_empty(&hose_list);
phb = alloc_phb_dynamic(dn, root_size_cells);
phb = pcibios_alloc_controller(dn);
if (!phb)
return NULL;

setup_phb(dn, phb, root_size_cells);
pci_process_bridge_OF_ranges(phb, dn, primary);

pci_setup_phb_io_dynamic(phb, primary);
Expand Down Expand Up @@ -505,8 +448,7 @@ int pcibios_remove_root_bus(struct pci_controller *phb)
}

list_del(&phb->list_node);
if (phb->is_dynamic)
kfree(phb);
pcibios_free_controller(phb);

return 0;
}
Expand Down
3 changes: 1 addition & 2 deletions arch/powerpc/platforms/iseries/pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -244,10 +244,9 @@ unsigned long __init find_and_init_phbs(void)
if (ret == 0) {
printk("bus %d appears to exist\n", bus);

phb = (struct pci_controller *)kmalloc(sizeof(struct pci_controller), GFP_KERNEL);
phb = pcibios_alloc_controller(NULL);
if (phb == NULL)
return -ENOMEM;
pci_setup_pci_controller(phb);

phb->pci_mem_offset = phb->local_number = bus;
phb->first_busno = bus;
Expand Down
16 changes: 1 addition & 15 deletions arch/powerpc/platforms/maple/pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -326,26 +326,12 @@ static int __init add_bridge(struct device_node *dev)
dev->full_name);
}

hose = alloc_bootmem(sizeof(struct pci_controller));
hose = pcibios_alloc_controller(dev);
if (hose == NULL)
return -ENOMEM;
pci_setup_pci_controller(hose);

hose->arch_data = dev;
hose->first_busno = bus_range ? bus_range[0] : 0;
hose->last_busno = bus_range ? bus_range[1] : 0xff;

of_prop = alloc_bootmem(sizeof(struct property) +
sizeof(hose->global_number));
if (of_prop) {
memset(of_prop, 0, sizeof(struct property));
of_prop->name = "linux,pci-domain";
of_prop->length = sizeof(hose->global_number);
of_prop->value = (unsigned char *)&of_prop[1];
memcpy(of_prop->value, &hose->global_number, sizeof(hose->global_number));
prom_add_property(dev, of_prop);
}

disp_name = NULL;
if (device_is_compatible(dev, "u3-agp")) {
setup_u3_agp(hose);
Expand Down
62 changes: 32 additions & 30 deletions arch/powerpc/platforms/powermac/pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -640,15 +640,16 @@ static void __init setup_u3_ht(struct pci_controller* hose)
* the reg address cell, we shall fix that by killing struct
* reg_property and using some accessor functions instead
*/
hose->cfg_data = (volatile unsigned char *)ioremap(0xf2000000, 0x02000000);
hose->cfg_data = (volatile unsigned char *)ioremap(0xf2000000,
0x02000000);

/*
* /ht node doesn't expose a "ranges" property, so we "remove" regions that
* have been allocated to AGP. So far, this version of the code doesn't assign
* any of the 0xfxxxxxxx "fine" memory regions to /ht.
* We need to fix that sooner or later by either parsing all child "ranges"
* properties or figuring out the U3 address space decoding logic and
* then read its configuration register (if any).
* /ht node doesn't expose a "ranges" property, so we "remove"
* regions that have been allocated to AGP. So far, this version of
* the code doesn't assign any of the 0xfxxxxxxx "fine" memory regions
* to /ht. We need to fix that sooner or later by either parsing all
* child "ranges" properties or figuring out the U3 address space
* decoding logic and then read its configuration register (if any).
*/
hose->io_base_phys = 0xf4000000;
hose->pci_io_size = 0x00400000;
Expand All @@ -671,10 +672,10 @@ static void __init setup_u3_ht(struct pci_controller* hose)
return;
}

/* We "remove" the AGP resources from the resources allocated to HT, that
* is we create "holes". However, that code does assumptions that so far
* happen to be true (cross fingers...), typically that resources in the
* AGP node are properly ordered
/* We "remove" the AGP resources from the resources allocated to HT,
* that is we create "holes". However, that code does assumptions
* that so far happen to be true (cross fingers...), typically that
* resources in the AGP node are properly ordered
*/
cur = 0;
for (i=0; i<3; i++) {
Expand All @@ -684,23 +685,30 @@ static void __init setup_u3_ht(struct pci_controller* hose)
/* We don't care about "fine" resources */
if (res->start >= 0xf0000000)
continue;
/* Check if it's just a matter of "shrinking" us in one direction */
/* Check if it's just a matter of "shrinking" us in one
* direction
*/
if (hose->mem_resources[cur].start == res->start) {
DBG("U3/HT: shrink start of %d, %08lx -> %08lx\n",
cur, hose->mem_resources[cur].start, res->end + 1);
cur, hose->mem_resources[cur].start,
res->end + 1);
hose->mem_resources[cur].start = res->end + 1;
continue;
}
if (hose->mem_resources[cur].end == res->end) {
DBG("U3/HT: shrink end of %d, %08lx -> %08lx\n",
cur, hose->mem_resources[cur].end, res->start - 1);
cur, hose->mem_resources[cur].end,
res->start - 1);
hose->mem_resources[cur].end = res->start - 1;
continue;
}
/* No, it's not the case, we need a hole */
if (cur == 2) {
/* not enough resources for a hole, we drop part of the range */
printk(KERN_WARNING "Running out of resources for /ht host !\n");
/* not enough resources for a hole, we drop part
* of the range
*/
printk(KERN_WARNING "Running out of resources"
" for /ht host !\n");
hose->mem_resources[cur].end = res->start - 1;
continue;
}
Expand All @@ -714,17 +722,6 @@ static void __init setup_u3_ht(struct pci_controller* hose)
hose->mem_resources[cur-1].end = res->start - 1;
}
}

/* XXX this needs to be converged between ppc32 and ppc64... */
static struct pci_controller * __init pcibios_alloc_controller(void)
{
struct pci_controller *hose;

hose = alloc_bootmem(sizeof(struct pci_controller));
if (hose)
pci_setup_pci_controller(hose);
return hose;
}
#endif

/*
Expand Down Expand Up @@ -756,19 +753,24 @@ static int __init add_bridge(struct device_node *dev)
#endif
bus_range = (int *) get_property(dev, "bus-range", &len);
if (bus_range == NULL || len < 2 * sizeof(int)) {
printk(KERN_WARNING "Can't get bus-range for %s, assume bus 0\n",
dev->full_name);
printk(KERN_WARNING "Can't get bus-range for %s, assume"
" bus 0\n", dev->full_name);
}

/* XXX Different prototypes, to be merged */
#ifdef CONFIG_PPC64
hose = pcibios_alloc_controller(dev);
#else
hose = pcibios_alloc_controller();
#endif
if (!hose)
return -ENOMEM;
hose->arch_data = dev;
hose->first_busno = bus_range ? bus_range[0] : 0;
hose->last_busno = bus_range ? bus_range[1] : 0xff;

disp_name = NULL;
#ifdef CONFIG_POWER4
#ifdef CONFIG_PPC64
if (device_is_compatible(dev, "u3-agp")) {
setup_u3_agp(hose);
disp_name = "U3-AGP";
Expand Down
1 change: 0 additions & 1 deletion include/asm-powerpc/ppc-pci.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@

extern unsigned long isa_io_base;

extern void pci_setup_pci_controller(struct pci_controller *hose);
extern void pci_setup_phb_io(struct pci_controller *hose, int primary);
extern void pci_setup_phb_io_dynamic(struct pci_controller *hose, int primary);

Expand Down
Loading

0 comments on commit b5166cc

Please sign in to comment.