summaryrefslogtreecommitdiffstats
path: root/arch/ppc/kernel/pmac_pci.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2000-02-23 00:40:54 +0000
committerRalf Baechle <ralf@linux-mips.org>2000-02-23 00:40:54 +0000
commit529c593ece216e4aaffd36bd940cb94f1fa63129 (patch)
tree78f1c0b805f5656aa7b0417a043c5346f700a2cf /arch/ppc/kernel/pmac_pci.c
parent0bd079751d25808d1972baee5c4eaa1db2227257 (diff)
Merge with 2.3.43. I did ignore all modifications to the qlogicisp.c
driver due to the Origin A64 hacks.
Diffstat (limited to 'arch/ppc/kernel/pmac_pci.c')
-rw-r--r--arch/ppc/kernel/pmac_pci.c253
1 files changed, 238 insertions, 15 deletions
diff --git a/arch/ppc/kernel/pmac_pci.c b/arch/ppc/kernel/pmac_pci.c
index 75f8097fd..b57d5aa28 100644
--- a/arch/ppc/kernel/pmac_pci.c
+++ b/arch/ppc/kernel/pmac_pci.c
@@ -30,6 +30,16 @@
struct bridge_data **bridges, *bridge_list;
static int max_bus;
+struct uninorth_data {
+ struct device_node* node;
+ volatile unsigned int* cfg_addr;
+ volatile unsigned int* cfg_data;
+};
+
+static struct uninorth_data uninorth_bridges[3];
+static int uninorth_count;
+static int uninorth_default = -1;
+
static void add_bridges(struct device_node *dev);
/*
@@ -73,6 +83,159 @@ int pci_device_loc(struct device_node *dev, unsigned char *bus_ptr,
return 0;
}
+/* This function only works for bus 0, uni-N uses a different mecanism for
+ * other busses (see below)
+ */
+#define UNI_N_CFA0(devfn, off) \
+ ((1 << (unsigned long)PCI_SLOT(dev_fn)) \
+ | (((unsigned long)PCI_FUNC(dev_fn)) << 8) \
+ | (((unsigned long)(off)) & 0xFCUL))
+
+/* This one is for type 1 config accesses */
+#define UNI_N_CFA1(bus, devfn, off) \
+ ((((unsigned long)(bus)) << 16) \
+ |(((unsigned long)(devfn)) << 8) \
+ |(((unsigned long)(off)) & 0xFCUL) \
+ |1UL)
+
+/* We should really use RTAS here, unfortunately, it's not available with BootX.
+ * (one more reason for writing a beautiful OF booter). I'll do the RTAS stuff
+ * later, once I have something that works enough with BootX.
+ */
+__pmac static
+unsigned int
+uni_north_access_data(unsigned char bus, unsigned char dev_fn,
+ unsigned char offset)
+{
+ struct device_node *node, *bridge_node;
+ int bridge = uninorth_default;
+ unsigned int caddr;
+
+ if (bus == 0) {
+ if (PCI_SLOT(dev_fn) < 11) {
+ return 0;
+ }
+ /* We look for the OF device corresponding to this bus/devfn pair. If we
+ * don't find it, we default to the external PCI */
+ bridge_node = NULL;
+ node = find_pci_device_OFnode(bus, dev_fn & 0xf8);
+ if (node) {
+ /* note: we don't stop on the first occurence since we need to go
+ * up to the root bridge */
+ do {
+ if (!strcmp(node->type, "pci"))
+ bridge_node = node;
+ node=node->parent;
+ } while (node);
+ }
+ if (bridge_node) {
+ int i;
+ for (i=0;i<uninorth_count;i++)
+ if (uninorth_bridges[i].node == bridge_node) {
+ bridge = i;
+ break;
+ }
+ }
+ caddr = UNI_N_CFA0(dev_fn, offset);
+ } else
+ caddr = UNI_N_CFA1(bus, dev_fn, offset);
+
+ if (bridge == -1) {
+ printk(KERN_WARNING "pmac_pci: no default bridge !\n");
+ return 0;
+ }
+
+ /* Uninorth will return garbage if we don't read back the value ! */
+ out_le32(uninorth_bridges[bridge].cfg_addr, caddr);
+ (void)in_le32(uninorth_bridges[bridge].cfg_addr);
+ /* Yes, offset is & 7, not & 3 ! */
+ return (unsigned int)(uninorth_bridges[bridge].cfg_data) + (offset & 0x07);
+}
+
+__pmac
+int uni_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn,
+ unsigned char offset, unsigned char *val)
+{
+ unsigned int addr;
+
+ *val = 0xff;
+ addr = uni_north_access_data(bus, dev_fn, offset);
+ if (!addr)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ *val = in_8((volatile unsigned char*)addr);
+ return PCIBIOS_SUCCESSFUL;
+}
+
+__pmac
+int uni_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn,
+ unsigned char offset, unsigned short *val)
+{
+ unsigned int addr;
+
+ *val = 0xffff;
+ addr = uni_north_access_data(bus, dev_fn, offset);
+ if (!addr)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ *val = in_le16((volatile unsigned short*)addr);
+ return PCIBIOS_SUCCESSFUL;
+}
+
+__pmac
+int uni_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn,
+ unsigned char offset, unsigned int *val)
+{
+ unsigned int addr;
+
+ *val = 0xffff;
+ addr = uni_north_access_data(bus, dev_fn, offset);
+ if (!addr)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ *val = in_le32((volatile unsigned int*)addr);
+ return PCIBIOS_SUCCESSFUL;
+}
+
+__pmac
+int uni_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn,
+ unsigned char offset, unsigned char val)
+{
+ unsigned int addr;
+
+ addr = uni_north_access_data(bus, dev_fn, offset);
+ if (!addr)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ out_8((volatile unsigned char *)addr, val);
+ (void)in_8((volatile unsigned char *)addr);
+ return PCIBIOS_SUCCESSFUL;
+}
+
+__pmac
+int uni_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn,
+ unsigned char offset, unsigned short val)
+{
+ unsigned int addr;
+
+ addr = uni_north_access_data(bus, dev_fn, offset);
+ if (!addr)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ out_le16((volatile unsigned short *)addr, val);
+ (void)in_le16((volatile unsigned short *)addr);
+ return PCIBIOS_SUCCESSFUL;
+}
+
+__pmac
+int uni_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn,
+ unsigned char offset, unsigned int val)
+{
+ unsigned int addr;
+
+ addr = uni_north_access_data(bus, dev_fn, offset);
+ if (!addr)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ out_le32((volatile unsigned int *)addr, val);
+ (void)in_le32((volatile unsigned int *)addr);
+ return PCIBIOS_SUCCESSFUL;
+}
+
__pmac
int pmac_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn,
unsigned char offset, unsigned char *val)
@@ -362,6 +525,21 @@ static void __init init_bandit(struct bridge_data *bp)
bp->io_base);
}
+#define GRACKLE_STG_ENABLE 0x00000040
+
+/* N.B. this is called before bridges is initialized, so we can't
+ use grackle_pcibios_{read,write}_config_dword. */
+static inline void grackle_set_stg(struct bridge_data *bp, int enable)
+{
+ unsigned int val;
+
+ out_be32(bp->cfg_addr, GRACKLE_CFA(0, 0, 0xa8));
+ val = in_le32((volatile unsigned int *)bp->cfg_data);
+ val = enable? (val | GRACKLE_STG_ENABLE): (val & ~GRACKLE_STG_ENABLE);
+ out_be32(bp->cfg_addr, GRACKLE_CFA(0, 0, 0xa8));
+ out_le32((volatile unsigned int *)bp->cfg_data, val);
+}
+
void __init pmac_find_bridges(void)
{
int bus;
@@ -411,20 +589,47 @@ static void __init add_bridges(struct device_node *dev)
printk(KERN_INFO "PCI buses %d..%d", bus_range[0],
bus_range[1]);
printk(" controlled by %s at %x\n", dev->name, addr->address);
+ if (device_is_compatible(dev, "uni-north")) {
+ int i = uninorth_count++;
+ uninorth_bridges[i].cfg_addr = ioremap(addr->address + 0x800000, 0x1000);
+ uninorth_bridges[i].cfg_data = ioremap(addr->address + 0xc00000, 0x1000);
+ uninorth_bridges[i].node = dev;
+ /* XXX This is the bridge with the PCI expansion bus. This is also the
+ * address of the bus that will receive type 1 config accesses and io
+ * accesses. Appears to be correct for iMac DV and G4 Sawtooth too.
+ * That means that we cannot do io cycles on the AGP bus nor the internal
+ * ethernet/fw bus. Fortunately, they appear not to be needed on iMac DV
+ * and G4 neither.
+ */
+ if (addr->address == 0xf2000000)
+ uninorth_default = i;
+ else
+ continue;
+ }
+
bp = (struct bridge_data *) alloc_bootmem(sizeof(*bp));
- if (strcmp(dev->name, "pci") != 0) {
- bp->cfg_addr = (volatile unsigned int *)
- ioremap(addr->address + 0x800000, 0x1000);
- bp->cfg_data = (volatile unsigned char *)
- ioremap(addr->address + 0xc00000, 0x1000);
- bp->io_base = (void *) ioremap(addr->address, 0x10000);
- } else {
- /* XXX */
+ if (device_is_compatible(dev, "uni-north")) {
+ bp->cfg_addr = 0;
+ bp->cfg_data = 0;
+ /* is 0x10000 enough for io space ? */
+ bp->io_base = (void *)ioremap(addr->address, 0x10000);
+ } else if (strcmp(dev->name, "pci") == 0) {
+ /* XXX assume this is a mpc106 (grackle) */
bp->cfg_addr = (volatile unsigned int *)
ioremap(0xfec00000, 0x1000);
bp->cfg_data = (volatile unsigned char *)
ioremap(0xfee00000, 0x1000);
bp->io_base = (void *) ioremap(0xfe000000, 0x20000);
+#if 0 /* Disabled for now, HW problems */
+ grackle_set_stg(bp, 1);
+#endif
+ } else {
+ /* a `bandit' or `chaos' bridge */
+ bp->cfg_addr = (volatile unsigned int *)
+ ioremap(addr->address + 0x800000, 0x1000);
+ bp->cfg_data = (volatile unsigned char *)
+ ioremap(addr->address + 0xc00000, 0x1000);
+ bp->io_base = (void *) ioremap(addr->address, 0x10000);
}
if (isa_io_base == 0)
isa_io_base = (unsigned long) bp->io_base;
@@ -453,7 +658,7 @@ fix_intr(struct device_node *node, struct pci_dev *dev)
for (; node != 0;node = node->sibling) {
class_code = (unsigned int *) get_property(node, "class-code", 0);
- if((*class_code >> 8) == PCI_CLASS_BRIDGE_PCI)
+ if(class_code && (*class_code >> 8) == PCI_CLASS_BRIDGE_PCI)
fix_intr(node->child, dev);
reg = (unsigned int *) get_property(node, "reg", 0);
if (reg == 0 || ((reg[0] >> 8) & 0xff) != dev->devfn)
@@ -490,20 +695,38 @@ pmac_pcibios_fixup(void)
if (pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin) ||
!pin)
continue; /* No interrupt generated -> no fixup */
- fix_intr(bp->node->child, dev);
+ /* We iterate all instances of uninorth for now */
+ if (uninorth_count && dev->bus->number == 0) {
+ int i;
+ for (i=0;i<uninorth_count;i++)
+ fix_intr(uninorth_bridges[i].node->child, dev);
+ } else
+ fix_intr(bp->node->child, dev);
}
}
void __init
pmac_setup_pci_ptrs(void)
{
- if (find_devices("pci") != 0) {
- /* looks like a G3 powermac */
- set_config_access_method(grackle);
- } else {
+ struct device_node* np;
+
+ np = find_devices("pci");
+ if (np != 0)
+ {
+ if (device_is_compatible(np, "uni-north"))
+ {
+ /* looks like an Core99 powermac */
+ set_config_access_method(uni);
+ } else
+ {
+ /* looks like a G3 powermac */
+ set_config_access_method(grackle);
+ }
+ } else
+ {
set_config_access_method(pmac);
}
-
+
ppc_md.pcibios_fixup = pmac_pcibios_fixup;
}