diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-02-23 00:40:54 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-02-23 00:40:54 +0000 |
commit | 529c593ece216e4aaffd36bd940cb94f1fa63129 (patch) | |
tree | 78f1c0b805f5656aa7b0417a043c5346f700a2cf /arch/ppc/kernel/pmac_pci.c | |
parent | 0bd079751d25808d1972baee5c4eaa1db2227257 (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.c | 253 |
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; } |