|
@@ -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)
|
|
|
{
|
|
@@ -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;
|
|
@@ -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;
|
|
@@ -101,6 +162,53 @@ 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)
|
|
|
{
|
|
@@ -108,9 +216,10 @@ static int sun4u_write_pci_cfg(struct pci_bus *bus_dev, unsigned int devfn,
|
|
|
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;
|