Commit 473eb05a authored by Benjamin Herrenschmidt's avatar Benjamin Herrenschmidt Committed by Linus Torvalds

[PATCH] ppc64: Improve PCI config accessors

This improves the config space access routines on G5, by adding a
generic helper function to locate the pci_controller structure (to be
used by an upcoming new platform too) and cleaning up the pmac routines.

It includes the fix to skip devices that aren't present in the OF tree
that is necessary for newer G5 desktop models.
Signed-off-by: default avatarBenjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 00afcdcf
...@@ -18,7 +18,6 @@ extern void pmac_calibrate_decr(void); ...@@ -18,7 +18,6 @@ extern void pmac_calibrate_decr(void);
extern void pmac_pcibios_fixup(void); extern void pmac_pcibios_fixup(void);
extern void pmac_pci_init(void); extern void pmac_pci_init(void);
extern void pmac_setup_pci_dma(void); extern void pmac_setup_pci_dma(void);
extern void fixup_k2_sata(struct pci_dev* dev);
extern void pmac_check_ht_link(void); extern void pmac_check_ht_link(void);
extern void pmac_setup_smp(void); extern void pmac_setup_smp(void);
......
...@@ -46,7 +46,6 @@ extern int pci_read_irq_line(struct pci_dev *pci_dev); ...@@ -46,7 +46,6 @@ extern int pci_read_irq_line(struct pci_dev *pci_dev);
* assuming we won't have both UniNorth and Bandit */ * assuming we won't have both UniNorth and Bandit */
static int has_uninorth; static int has_uninorth;
static struct pci_controller *u3_agp; static struct pci_controller *u3_agp;
u8 pci_cache_line_size;
struct pci_dev *k2_skiplist[2]; struct pci_dev *k2_skiplist[2];
static int __init fixup_one_level_bus_range(struct device_node *node, int higher) static int __init fixup_one_level_bus_range(struct device_node *node, int higher)
...@@ -150,16 +149,9 @@ static int __pmac macrisc_read_config(struct pci_bus *bus, unsigned int devfn, ...@@ -150,16 +149,9 @@ static int __pmac macrisc_read_config(struct pci_bus *bus, unsigned int devfn,
int offset, int len, u32 *val) int offset, int len, u32 *val)
{ {
struct pci_controller *hose; struct pci_controller *hose;
struct device_node *busdn;
unsigned long addr; unsigned long addr;
if (bus->self) hose = pci_bus_to_host(bus);
busdn = pci_device_to_OF_node(bus->self);
else
busdn = bus->sysdata; /* must be a phb */
if (busdn == NULL)
return PCIBIOS_DEVICE_NOT_FOUND;
hose = busdn->phb;
if (hose == NULL) if (hose == NULL)
return PCIBIOS_DEVICE_NOT_FOUND; return PCIBIOS_DEVICE_NOT_FOUND;
...@@ -188,16 +180,9 @@ static int __pmac macrisc_write_config(struct pci_bus *bus, unsigned int devfn, ...@@ -188,16 +180,9 @@ static int __pmac macrisc_write_config(struct pci_bus *bus, unsigned int devfn,
int offset, int len, u32 val) int offset, int len, u32 val)
{ {
struct pci_controller *hose; struct pci_controller *hose;
struct device_node *busdn;
unsigned long addr; unsigned long addr;
if (bus->self) hose = pci_bus_to_host(bus);
busdn = pci_device_to_OF_node(bus->self);
else
busdn = bus->sysdata; /* must be a phb */
if (busdn == NULL)
return PCIBIOS_DEVICE_NOT_FOUND;
hose = busdn->phb;
if (hose == NULL) if (hose == NULL)
return PCIBIOS_DEVICE_NOT_FOUND; return PCIBIOS_DEVICE_NOT_FOUND;
...@@ -236,14 +221,44 @@ static struct pci_ops macrisc_pci_ops = ...@@ -236,14 +221,44 @@ static struct pci_ops macrisc_pci_ops =
* implement self-view of the HT host yet * implement self-view of the HT host yet
*/ */
static int skip_k2_device(struct pci_bus *bus, unsigned int devfn) /*
* This function deals with some "special cases" devices.
*
* 0 -> No special case
* 1 -> Skip the device but act as if the access was successfull
* (return 0xff's on reads, eventually, cache config space
* accesses in a later version)
* -1 -> Hide the device (unsuccessful acess)
*/
static int u3_ht_skip_device(struct pci_controller *hose,
struct pci_bus *bus, unsigned int devfn)
{ {
struct device_node *busdn, *dn;
int i; int i;
/*
* When a device in K2 is powered down, we die on config
* cycle accesses. Fix that here.
*/
for (i=0; i<2; i++) for (i=0; i<2; i++)
if (k2_skiplist[i] && k2_skiplist[i]->bus == bus && if (k2_skiplist[i] && k2_skiplist[i]->bus == bus &&
k2_skiplist[i]->devfn == devfn) k2_skiplist[i]->devfn == devfn)
return 1; return 1;
/* We only allow config cycles to devices that are in OF device-tree
* as we are apparently having some weird things going on with some
* revs of K2 on recent G5s
*/
if (bus->self)
busdn = pci_device_to_OF_node(bus->self);
else
busdn = hose->arch_data;
for (dn = busdn->child; dn; dn = dn->sibling)
if (dn->devfn == devfn)
break;
if (dn == NULL)
return -1;
return 0; return 0;
} }
...@@ -259,8 +274,7 @@ static unsigned long __pmac u3_ht_cfg_access(struct pci_controller* hose, ...@@ -259,8 +274,7 @@ static unsigned long __pmac u3_ht_cfg_access(struct pci_controller* hose,
{ {
if (bus == hose->first_busno) { if (bus == hose->first_busno) {
/* For now, we don't self probe U3 HT bridge */ /* For now, we don't self probe U3 HT bridge */
if (PCI_FUNC(devfn) != 0 || PCI_SLOT(devfn) > 7 || if (PCI_SLOT(devfn) == 0)
PCI_SLOT(devfn) < 1)
return 0; return 0;
return ((unsigned long)hose->cfg_data) + U3_HT_CFA0(devfn, offset); return ((unsigned long)hose->cfg_data) + U3_HT_CFA0(devfn, offset);
} else } else
...@@ -271,39 +285,21 @@ static int __pmac u3_ht_read_config(struct pci_bus *bus, unsigned int devfn, ...@@ -271,39 +285,21 @@ static int __pmac u3_ht_read_config(struct pci_bus *bus, unsigned int devfn,
int offset, int len, u32 *val) int offset, int len, u32 *val)
{ {
struct pci_controller *hose; struct pci_controller *hose;
struct device_node *busdn, *dn;
unsigned long addr; unsigned long addr;
if (bus->self)
busdn = pci_device_to_OF_node(bus->self);
else
busdn = bus->sysdata; /* must be a phb */
if (busdn == NULL)
return PCIBIOS_DEVICE_NOT_FOUND;
hose = busdn->phb;
if (hose == NULL)
return PCIBIOS_DEVICE_NOT_FOUND;
/* We only allow config cycles to devices that are in OF device-tree hose = pci_bus_to_host(bus);
* as we are apparently having some weird things going on with some if (hose == NULL)
* revs of K2 on recent G5s
*/
for (dn = busdn->child; dn; dn = dn->sibling)
if (dn->devfn == devfn)
break;
if (dn == NULL)
return PCIBIOS_DEVICE_NOT_FOUND; return PCIBIOS_DEVICE_NOT_FOUND;
addr = u3_ht_cfg_access(hose, bus->number, devfn, offset); addr = u3_ht_cfg_access(hose, bus->number, devfn, offset);
if (!addr) if (!addr)
return PCIBIOS_DEVICE_NOT_FOUND; return PCIBIOS_DEVICE_NOT_FOUND;
/*
* When a device in K2 is powered down, we die on config switch (u3_ht_skip_device(hose, bus, devfn)) {
* cycle accesses. Fix that here. We may ultimately want case 0:
* to cache the config space for those instead of returning break;
* 0xffffffff's to make life easier to HW detection tools case 1:
*/
if (skip_k2_device(bus, devfn)) {
switch (len) { switch (len) {
case 1: case 1:
*val = 0xff; break; *val = 0xff; break;
...@@ -313,6 +309,8 @@ static int __pmac u3_ht_read_config(struct pci_bus *bus, unsigned int devfn, ...@@ -313,6 +309,8 @@ static int __pmac u3_ht_read_config(struct pci_bus *bus, unsigned int devfn,
*val = 0xfffffffful; break; *val = 0xfffffffful; break;
} }
return PCIBIOS_SUCCESSFUL; return PCIBIOS_SUCCESSFUL;
default:
return PCIBIOS_DEVICE_NOT_FOUND;
} }
/* /*
...@@ -337,28 +335,24 @@ static int __pmac u3_ht_write_config(struct pci_bus *bus, unsigned int devfn, ...@@ -337,28 +335,24 @@ static int __pmac u3_ht_write_config(struct pci_bus *bus, unsigned int devfn,
int offset, int len, u32 val) int offset, int len, u32 val)
{ {
struct pci_controller *hose; struct pci_controller *hose;
struct device_node *busdn;
unsigned long addr; unsigned long addr;
if (bus->self) hose = pci_bus_to_host(bus);
busdn = pci_device_to_OF_node(bus->self);
else
busdn = bus->sysdata; /* must be a phb */
if (busdn == NULL)
return PCIBIOS_DEVICE_NOT_FOUND;
hose = busdn->phb;
if (hose == NULL) if (hose == NULL)
return PCIBIOS_DEVICE_NOT_FOUND; return PCIBIOS_DEVICE_NOT_FOUND;
addr = u3_ht_cfg_access(hose, bus->number, devfn, offset); addr = u3_ht_cfg_access(hose, bus->number, devfn, offset);
if (!addr) if (!addr)
return PCIBIOS_DEVICE_NOT_FOUND; return PCIBIOS_DEVICE_NOT_FOUND;
/*
* When a device in K2 is powered down, we die on config switch (u3_ht_skip_device(hose, bus, devfn)) {
* cycle accesses. Fix that here. case 0:
*/ break;
if (skip_k2_device(bus, devfn)) case 1:
return PCIBIOS_SUCCESSFUL; return PCIBIOS_SUCCESSFUL;
default:
return PCIBIOS_DEVICE_NOT_FOUND;
}
/* /*
* Note: the caller has already checked that offset is * Note: the caller has already checked that offset is
...@@ -675,7 +669,6 @@ void __init pmac_pcibios_fixup(void) ...@@ -675,7 +669,6 @@ void __init pmac_pcibios_fixup(void)
pci_fix_bus_sysdata(); pci_fix_bus_sysdata();
iommu_setup_u3(); iommu_setup_u3();
} }
static void __init pmac_fixup_phb_resources(void) static void __init pmac_fixup_phb_resources(void)
...@@ -750,11 +743,6 @@ void __init pmac_pci_init(void) ...@@ -750,11 +743,6 @@ void __init pmac_pci_init(void)
/* Tell pci.c to use the common resource allocation mecanism */ /* Tell pci.c to use the common resource allocation mecanism */
pci_probe_only = 0; pci_probe_only = 0;
/* HT don't do more than 64 bytes transfers. FIXME: Deal with
* the exception of U3/AGP (hook into pci_set_mwi)
*/
pci_cache_line_size = 16; /* 64 bytes */
/* Allow all IO */ /* Allow all IO */
io_page_mask = -1; io_page_mask = -1;
} }
...@@ -763,7 +751,7 @@ void __init pmac_pci_init(void) ...@@ -763,7 +751,7 @@ void __init pmac_pci_init(void)
* Disable second function on K2-SATA, it's broken * Disable second function on K2-SATA, it's broken
* and disable IO BARs on first one * and disable IO BARs on first one
*/ */
void fixup_k2_sata(struct pci_dev* dev) static void fixup_k2_sata(struct pci_dev* dev)
{ {
int i; int i;
u16 cmd; u16 cmd;
......
...@@ -101,5 +101,22 @@ extern int pcibios_remove_root_bus(struct pci_controller *phb); ...@@ -101,5 +101,22 @@ extern int pcibios_remove_root_bus(struct pci_controller *phb);
extern void phbs_remap_io(void); extern void phbs_remap_io(void);
static inline struct pci_controller *pci_bus_to_host(struct pci_bus *bus)
{
struct device_node *busdn;
busdn = bus->sysdata;
if (busdn == 0) {
struct pci_bus *b;
for (b = bus->parent; b && bus->sysdata == 0; b = b->parent)
;
busdn = b->sysdata;
}
if (busdn == NULL)
return NULL;
return busdn->phb;
}
#endif #endif
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment