Skip to content

Commit

Permalink
[SPARC64]: Fix sun4u PCI config space accesses on sun4u.
Browse files Browse the repository at this point in the history
Don't provide fake PCI config space for sun4u.

Also, put back the funny host controller space handling that
at least Sabre needs.  You have to read PCI host controller
registers at their nature size otherwise you get zeros instead
of correct values.

Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Jul 30, 2007
1 parent b84d879 commit a2d6ea0
Show file tree
Hide file tree
Showing 2 changed files with 126 additions and 12 deletions.
15 changes: 10 additions & 5 deletions arch/sparc64/kernel/pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -422,10 +422,15 @@ struct pci_dev *of_create_pci_dev(struct pci_pbm_info *pbm,
dev->multifunction = 0; /* maybe a lie? */

if (host_controller) {
dev->vendor = 0x108e;
dev->device = 0x8000;
dev->subsystem_vendor = 0x0000;
dev->subsystem_device = 0x0000;
if (tlb_type != hypervisor) {
pci_read_config_word(dev, PCI_VENDOR_ID,
&dev->vendor);
pci_read_config_word(dev, PCI_DEVICE_ID,
&dev->device);
} else {
dev->vendor = PCI_VENDOR_ID_SUN;
dev->device = 0x80f0;
}
dev->cfg_size = 256;
dev->class = PCI_CLASS_BRIDGE_HOST << 8;
sprintf(pci_name(dev), "%04x:%02x:%02x.%d", pci_domain_nr(bus),
Expand Down Expand Up @@ -818,7 +823,7 @@ int pci_host_bridge_read_pci_cfg(struct pci_bus *bus_dev,
{
static u8 fake_pci_config[] = {
0x8e, 0x10, /* Vendor: 0x108e (Sun) */
0x00, 0x80, /* Device: 0x8000 (PBM) */
0xf0, 0x80, /* Device: 0x80f0 (Fire) */
0x46, 0x01, /* Command: 0x0146 (SERR, PARITY, MASTER, MEM) */
0xa0, 0x22, /* Status: 0x02a0 (DEVSEL_MED, FB2B, 66MHZ) */
0x00, 0x00, 0x00, 0x06, /* Class: 0x06000000 host bridge */
Expand Down
123 changes: 116 additions & 7 deletions arch/sparc64/kernel/pci_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,67 @@ static void *sun4u_config_mkaddr(struct pci_pbm_info *pbm,
return (void *) (pbm->config_space | bus | devfn | reg);
}

/* At least on Sabre, it is necessary to access all PCI host controller
* registers at their natural size, otherwise zeros are returned.
* Strange but true, and I see no language in the UltraSPARC-IIi
* programmer's manual that mentions this even indirectly.
*/
static int sun4u_read_pci_cfg_host(struct pci_pbm_info *pbm,
unsigned char bus, unsigned int devfn,
int where, int size, u32 *value)
{
u32 tmp32, *addr;
u16 tmp16;
u8 tmp8;

addr = sun4u_config_mkaddr(pbm, bus, devfn, where);
if (!addr)
return PCIBIOS_SUCCESSFUL;

switch (size) {
case 1:
if (where < 8) {
unsigned long align = (unsigned long) addr;

align &= ~1;
pci_config_read16((u16 *)align, &tmp16);
if (where & 1)
*value = tmp16 >> 8;
else
*value = tmp16 & 0xff;
} else {
pci_config_read8((u8 *)addr, &tmp8);
*value = (u32) tmp8;
}
break;

case 2:
if (where < 8) {
pci_config_read16((u16 *)addr, &tmp16);
*value = (u32) tmp16;
} else {
pci_config_read8((u8 *)addr, &tmp8);
*value = (u32) tmp8;
pci_config_read8(((u8 *)addr) + 1, &tmp8);
*value |= ((u32) tmp8) << 8;
}
break;

case 4:
tmp32 = 0xffffffff;
sun4u_read_pci_cfg_host(pbm, bus, devfn,
where, 2, &tmp32);
*value = tmp32;

tmp32 = 0xffffffff;
sun4u_read_pci_cfg_host(pbm, bus, devfn,
where + 2, 2, &tmp32);
*value |= tmp32 << 16;
break;
}
return PCIBIOS_SUCCESSFUL;
}

static int sun4u_read_pci_cfg(struct pci_bus *bus_dev, unsigned int devfn,
int where, int size, u32 *value)
{
Expand All @@ -53,10 +114,6 @@ static int sun4u_read_pci_cfg(struct pci_bus *bus_dev, unsigned int devfn,
u16 tmp16;
u8 tmp8;

if (bus_dev == pbm->pci_bus && devfn == 0x00)
return pci_host_bridge_read_pci_cfg(bus_dev, devfn, where,
size, value);

switch (size) {
case 1:
*value = 0xff;
Expand All @@ -69,6 +126,10 @@ static int sun4u_read_pci_cfg(struct pci_bus *bus_dev, unsigned int devfn,
break;
}

if (!bus_dev->number && !PCI_SLOT(devfn))
return sun4u_read_pci_cfg_host(pbm, bus, devfn, where,
size, value);

addr = sun4u_config_mkaddr(pbm, bus, devfn, where);
if (!addr)
return PCIBIOS_SUCCESSFUL;
Expand Down Expand Up @@ -101,16 +162,64 @@ static int sun4u_read_pci_cfg(struct pci_bus *bus_dev, unsigned int devfn,
return PCIBIOS_SUCCESSFUL;
}

static int sun4u_write_pci_cfg_host(struct pci_pbm_info *pbm,
unsigned char bus, unsigned int devfn,
int where, int size, u32 value)
{
u32 *addr;

addr = sun4u_config_mkaddr(pbm, bus, devfn, where);
if (!addr)
return PCIBIOS_SUCCESSFUL;

switch (size) {
case 1:
if (where < 8) {
unsigned long align = (unsigned long) addr;
u16 tmp16;

align &= ~1;
pci_config_read16((u16 *)align, &tmp16);
if (where & 1) {
tmp16 &= 0x00ff;
tmp16 |= value << 8;
} else {
tmp16 &= 0xff00;
tmp16 |= value;
}
pci_config_write16((u16 *)align, tmp16);
} else
pci_config_write8((u8 *)addr, value);
break;
case 2:
if (where < 8) {
pci_config_write16((u16 *)addr, value);
} else {
pci_config_write8((u8 *)addr, value & 0xff);
pci_config_write8(((u8 *)addr) + 1, value >> 8);
}
break;
case 4:
sun4u_write_pci_cfg_host(pbm, bus, devfn,
where, 2, value & 0xffff);
sun4u_write_pci_cfg_host(pbm, bus, devfn,
where + 2, 2, value >> 16);
break;
}
return PCIBIOS_SUCCESSFUL;
}

static int sun4u_write_pci_cfg(struct pci_bus *bus_dev, unsigned int devfn,
int where, int size, u32 value)
{
struct pci_pbm_info *pbm = bus_dev->sysdata;
unsigned char bus = bus_dev->number;
u32 *addr;

if (bus_dev == pbm->pci_bus && devfn == 0x00)
return pci_host_bridge_write_pci_cfg(bus_dev, devfn, where,
size, value);
if (!bus_dev->number && !PCI_SLOT(devfn))
return sun4u_write_pci_cfg_host(pbm, bus, devfn, where,
size, value);

addr = sun4u_config_mkaddr(pbm, bus, devfn, where);
if (!addr)
return PCIBIOS_SUCCESSFUL;
Expand Down

0 comments on commit a2d6ea0

Please sign in to comment.