Commit 5f766a95 authored by Paul Mackerras's avatar Paul Mackerras Committed by Linus Torvalds

[PATCH] ppc64: Mode 2 PCI-X config space size fix

This patch is from Brian King <brking@us.ibm.com>.

When working with a PCI-X Mode 2 adapter on a PCI-X Mode 1 PPC64 system, the
current code used to determine the config space size of a device results in a
PCI Master abort and an EEH error, resulting in the device being taken
offline.  This patch checks OF to see if the PCI bridge supports PCI-X Mode 2
and fails config accesses beyond 256 bytes if it does not.
Signed-off-by: default avatarBrian King <brking@us.ibm.com>
Signed-off-by: default avatarPaul Mackerras <paulus@samba.org>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent fd2d5b6a
......@@ -610,6 +610,10 @@ static int iSeries_pci_read_config(struct pci_bus *bus, unsigned int devfn,
if (node == NULL)
return PCIBIOS_DEVICE_NOT_FOUND;
if (offset > 255) {
*val = ~0;
return PCIBIOS_BAD_REGISTER_NUMBER;
}
fn = hv_cfg_read_func[(size - 1) & 3];
HvCall3Ret16(fn, &ret, node->DsaAddr.DsaAddr, offset, 0);
......@@ -636,6 +640,8 @@ static int iSeries_pci_write_config(struct pci_bus *bus, unsigned int devfn,
if (node == NULL)
return PCIBIOS_DEVICE_NOT_FOUND;
if (offset > 255)
return PCIBIOS_BAD_REGISTER_NUMBER;
fn = hv_cfg_write_func[(size - 1) & 3];
ret = HvCall4(fn, node->DsaAddr.DsaAddr, offset, val, 0);
......
......@@ -52,6 +52,16 @@ static int s7a_workaround;
extern struct mpic *pSeries_mpic;
static int config_access_valid(struct device_node *dn, int where)
{
if (where < 256)
return 1;
if (where < 4096 && dn->pci_ext_config_space)
return 1;
return 0;
}
static int rtas_read_config(struct device_node *dn, int where, int size, u32 *val)
{
int returnval = -1;
......@@ -60,10 +70,11 @@ static int rtas_read_config(struct device_node *dn, int where, int size, u32 *va
if (!dn)
return PCIBIOS_DEVICE_NOT_FOUND;
if (where & (size - 1))
if (!config_access_valid(dn, where))
return PCIBIOS_BAD_REGISTER_NUMBER;
addr = (dn->busno << 16) | (dn->devfn << 8) | where;
addr = ((where & 0xf00) << 20) | (dn->busno << 16) |
(dn->devfn << 8) | (where & 0xff);
buid = dn->phb->buid;
if (buid) {
ret = rtas_call(ibm_read_pci_config, 4, 2, &returnval,
......@@ -108,10 +119,11 @@ static int rtas_write_config(struct device_node *dn, int where, int size, u32 va
if (!dn)
return PCIBIOS_DEVICE_NOT_FOUND;
if (where & (size - 1))
if (!config_access_valid(dn, where))
return PCIBIOS_BAD_REGISTER_NUMBER;
addr = (dn->busno << 16) | (dn->devfn << 8) | where;
addr = ((where & 0xf00) << 20) | (dn->busno << 16) |
(dn->devfn << 8) | (where & 0xff);
buid = dn->phb->buid;
if (buid) {
ret = rtas_call(ibm_write_pci_config, 5, 1, NULL, addr, buid >> 32, buid & 0xffffffff, size, (ulong) val);
......
......@@ -37,6 +37,7 @@
static void * __devinit update_dn_pci_info(struct device_node *dn, void *data)
{
struct pci_controller *phb = data;
int *type = (int *)get_property(dn, "ibm,pci-config-space-type", NULL);
u32 *regs;
dn->phb = phb;
......@@ -46,6 +47,8 @@ static void * __devinit update_dn_pci_info(struct device_node *dn, void *data)
dn->busno = (regs[0] >> 16) & 0xff;
dn->devfn = (regs[0] >> 8) & 0xff;
}
dn->pci_ext_config_space = (type && *type == 1);
return NULL;
}
......
......@@ -137,6 +137,7 @@ struct device_node {
int devfn; /* for pci devices */
int eeh_mode; /* See eeh.h for possible EEH_MODEs */
int eeh_config_addr;
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 */
......
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