[PATCH] powerpc: pci_64 fixes & cleanups
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>
This commit is contained in:
parent
f9e4ec57c6
commit
b5166cc252
|
@ -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));
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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];
|
||||
|
@ -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))
|
||||
|
|
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
@ -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++) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
/*
|
||||
|
@ -756,11 +753,16 @@ 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;
|
||||
|
@ -768,7 +770,7 @@ static int __init add_bridge(struct device_node *dev)
|
|||
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";
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -61,12 +61,14 @@ struct pci_dn {
|
|||
int busno; /* for pci devices */
|
||||
int bussubno; /* for pci devices */
|
||||
int devfn; /* for pci devices */
|
||||
|
||||
#ifdef CONFIG_PPC_PSERIES
|
||||
int eeh_mode; /* See eeh.h for possible EEH_MODEs */
|
||||
int eeh_config_addr;
|
||||
int eeh_check_count; /* # times driver ignored error */
|
||||
int eeh_freeze_count; /* # times this device froze up. */
|
||||
int eeh_is_bridge; /* device is pci-to-pci bridge */
|
||||
|
||||
#endif
|
||||
int pci_ext_config_space; /* for pci devices */
|
||||
struct pci_controller *phb; /* for pci devices */
|
||||
struct iommu_table *iommu_table; /* for phb's or bridges */
|
||||
|
@ -136,6 +138,10 @@ static inline struct pci_controller *pci_bus_to_host(struct pci_bus *bus)
|
|||
return PCI_DN(busdn)->phb;
|
||||
}
|
||||
|
||||
extern struct pci_controller *
|
||||
pcibios_alloc_controller(struct device_node *dev);
|
||||
extern void pcibios_free_controller(struct pci_controller *phb);
|
||||
|
||||
/* Return values for ppc_md.pci_probe_mode function */
|
||||
#define PCI_PROBE_NONE -1 /* Don't look at this bus at all */
|
||||
#define PCI_PROBE_NORMAL 0 /* Do normal PCI probing */
|
||||
|
|
Loading…
Reference in a new issue