patch-2.4.1 linux/arch/ppc/kernel/pmac_pci.c

Next file: linux/arch/ppc/kernel/pmac_pic.c
Previous file: linux/arch/ppc/kernel/pci.h
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.0/linux/arch/ppc/kernel/pmac_pci.c linux/arch/ppc/kernel/pmac_pci.c
@@ -27,23 +27,14 @@
 
 #include "pci.h"
 
-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;
-	void*			iobase;
-	unsigned long		iobase_phys;
-};
-
-static struct uninorth_data uninorth_bridges[3];
-static int uninorth_count;
-static int uninorth_default = -1;
+#undef DEBUG
 
 static void add_bridges(struct device_node *dev);
 
+/* XXX Could be per-controller, but I don't think we risk anything by
+ * assuming we won't have both UniNorth and Bandit */
+static int has_uninorth;
+
 /*
  * Magic constants for enabling cache coherency in the bandit/PSX bridge.
  */
@@ -56,477 +47,201 @@
 #define BANDIT_MAGIC	0x50
 #define BANDIT_COHERENT	0x40
 
-/* Obsolete, should be replaced by pmac_pci_dev_io_base() (below) */
-__pmac
-void *pci_io_base(unsigned int bus)
+static int __init
+fixup_one_level_bus_range(struct device_node *node, int higher)
 {
-	struct bridge_data *bp;
-
-	if (bus > max_bus || (bp = bridges[bus]) == 0)
-		return 0;
-	return bp->io_base;
-}
-
-__pmac
-int pci_device_loc(struct device_node *dev, unsigned char *bus_ptr,
-		   unsigned char *devfn_ptr)
-{
-	unsigned int *reg;
-	int len;
+	for (; node != 0;node = node->sibling) {
+		int * bus_range;
+		unsigned int *class_code;			
+		int len;
 
-	reg = (unsigned int *) get_property(dev, "reg", &len);
-	if (reg == 0 || len < 5 * sizeof(unsigned int)) {
-		/* doesn't look like a PCI device */
-		*bus_ptr = 0xff;
-		*devfn_ptr = 0xff;
-		return -1;
+		/* For PCI<->PCI bridges or CardBus bridges, we go down */
+		class_code = (unsigned int *) get_property(node, "class-code", 0);
+		if (!class_code || ((*class_code >> 8) != PCI_CLASS_BRIDGE_PCI &&
+			(*class_code >> 8) != PCI_CLASS_BRIDGE_CARDBUS))
+			continue;
+		bus_range = (int *) get_property(node, "bus-range", &len);
+		if (bus_range != NULL && len > 2 * sizeof(int)) {
+			if (bus_range[1] > higher)
+				higher = bus_range[1];
+		}
+		higher = fixup_one_level_bus_range(node->child, higher);
 	}
-	*bus_ptr = reg[0] >> 16;
-	*devfn_ptr = reg[0] >> 8;
-	return 0;
+	return higher;
 }
 
-/* This routines figures out on which root bridge a given PCI device
- * is attached.
+/* This routine fixes the "bus-range" property of all bridges in the
+ * system since they tend to have their "last" member wrong on macs
+ * 
+ * Note that the bus numbers manipulated here are OF bus numbers, they
+ * are not Linux bus numbers.
  */
-__pmac
-int
-pmac_pci_dev_root_bridge(unsigned char bus, unsigned char dev_fn)
+static void __init
+fixup_bus_range(struct device_node *bridge)
 {
-	struct device_node *node, *bridge_node;
-	int bridge = uninorth_default;
-
-	if (uninorth_count == 0)
-		return 0;
-	if (bus == 0 && PCI_SLOT(dev_fn) < 11)
-		return 0;
+	int * bus_range;
+	int len;
 	
-	/* 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 (node->type && !strcmp(node->type, "pci") 
-			&& device_is_compatible(node, "uni-north"))
-			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;
-		}
-	}
-
-	if (bridge == -1) {
-		printk(KERN_WARNING "pmac_pci: no default bridge !\n");
-		return 0;
-	}
-
-	return bridge;	
-}
-
-__pmac
-void *
-pmac_pci_dev_io_base(unsigned char bus, unsigned char devfn, int physical)
-{
-	int bridge = -1;
-	if (uninorth_count != 0)
-		bridge = pmac_pci_dev_root_bridge(bus, devfn);
-	if (bridge == -1) {
-		struct bridge_data *bp;
-
-		if (bus > max_bus || (bp = bridges[bus]) == 0)
-			return 0;
-		return physical ? (void *) bp->io_base_phys : bp->io_base;
+	/* Lookup the "bus-range" property for the hose */		
+	bus_range = (int *) get_property(bridge, "bus-range", &len);
+	if (bus_range == NULL || len < 2 * sizeof(int)) {
+		printk(KERN_WARNING "Can't get bus-range for %s\n",
+			       bridge->full_name);
+		return;
 	}
-	return physical ? (void *) uninorth_bridges[bridge].iobase_phys
-		: uninorth_bridges[bridge].iobase;
+	bus_range[1] = fixup_one_level_bus_range(bridge->child, bus_range[1]);
 }
 
-__pmac
-void *
-pmac_pci_dev_mem_base(unsigned char bus, unsigned char devfn)
-{
-	return 0;
-}
-
-/* This function only works for bus 0, uni-N uses a different mecanism for
- * other busses (see below)
+/*
+ * Apple MacRISC (UniNorth, Bandit) PCI controllers.
+ * 
+ * The "Bandit" version is present in all early PCI PowerMacs,
+ * and up to the first ones using Grackle. Some machines may
+ * have 2 bandit controllers (2 PCI busses).
+ * 
+ * The "UniNorth" version is present in all Core99 machines
+ * (iBook, G4, new IMacs, and all the recent Apple machines).
+ * It contains 3 controllers in one ASIC.
  */
-#define UNI_N_CFA0(devfn, off)	\
+
+#define MACRISC_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)	\
+#define MACRISC_CFA1(bus, devfn, off)	\
 	((((unsigned long)(bus)) << 16) \
 	|(((unsigned long)(devfn)) << 8) \
 	|(((unsigned long)(off)) & 0xFCUL) \
 	|1UL)
 	
-__pmac static
-unsigned int
-uni_north_access_data(unsigned char bus, unsigned char dev_fn,
-				unsigned char offset)
+static unsigned int __pmac
+macrisc_cfg_access(struct pci_controller* hose, u8 bus, u8 dev_fn, u8 offset)
 {
-	int bridge;
 	unsigned int caddr;
-
-	bridge = pmac_pci_dev_root_bridge(bus, dev_fn);
-	if (bus == 0)
-		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)
-{
-	struct bridge_data *bp;
-
-	*val = 0xff;
-	if (bus > max_bus || (bp = bridges[bus]) == 0)
-		return PCIBIOS_DEVICE_NOT_FOUND;
-	if (bus == bp->bus_number) {
-		if (dev_fn < (11 << 3))
-			return PCIBIOS_DEVICE_NOT_FOUND;
-		out_le32(bp->cfg_addr,
-			 (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8)
-			 + (offset & ~3));
-	} else {
-		/* Bus number once again taken into consideration.
-		 * Change applied from 2.1.24. This makes devices located
-		 * behind PCI-PCI bridges visible.
-		 * -Ranjit Deshpande, 01/20/99
-		 */
-		out_le32(bp->cfg_addr, (bus << 16) + (dev_fn << 8) + (offset & ~3) + 1);
-	}
-	udelay(2);
-	*val = in_8(bp->cfg_data + (offset & 3));
-	return PCIBIOS_SUCCESSFUL;
-}
-
-__pmac
-int pmac_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn,
-				  unsigned char offset, unsigned short *val)
-{
-	struct bridge_data *bp;
-
-	*val = 0xffff;
-	if (bus > max_bus || (bp = bridges[bus]) == 0)
-		return PCIBIOS_DEVICE_NOT_FOUND;
-	if ((offset & 1) != 0)
-		return PCIBIOS_BAD_REGISTER_NUMBER;
-	if (bus == bp->bus_number) {
-		if (dev_fn < (11 << 3))
-			return PCIBIOS_DEVICE_NOT_FOUND;
-		out_le32(bp->cfg_addr,
-			 (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8)
-			 + (offset & ~3));
-	} else {
-		/* See pci_read_config_byte */
-		out_le32(bp->cfg_addr, (bus << 16) + (dev_fn << 8) + (offset & ~3) + 1);
-	}
-	udelay(2);
-	*val = in_le16((volatile unsigned short *)(bp->cfg_data + (offset & 3)));
-	return PCIBIOS_SUCCESSFUL;
-}
-
-__pmac
-int pmac_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn,
-				   unsigned char offset, unsigned int *val)
-{
-	struct bridge_data *bp;
-
-	*val = 0xffffffff;
-	if (bus > max_bus || (bp = bridges[bus]) == 0)
-		return PCIBIOS_DEVICE_NOT_FOUND;
-	if ((offset & 3) != 0)
-		return PCIBIOS_BAD_REGISTER_NUMBER;
-	if (bus == bp->bus_number) {
-		if (dev_fn < (11 << 3))
-			return PCIBIOS_DEVICE_NOT_FOUND;
-		out_le32(bp->cfg_addr,
-			 (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8)
-			 + offset);
-	} else {
-		/* See pci_read_config_byte */
-		out_le32(bp->cfg_addr, (bus << 16) + (dev_fn << 8) + offset + 1);
-	}
-	udelay(2);
-	*val = in_le32((volatile unsigned int *)bp->cfg_data);
-	return PCIBIOS_SUCCESSFUL;
-}
-
-__pmac
-int pmac_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn,
-				   unsigned char offset, unsigned char val)
-{
-	struct bridge_data *bp;
-
-	if (bus > max_bus || (bp = bridges[bus]) == 0)
-		return PCIBIOS_DEVICE_NOT_FOUND;
-	if (bus == bp->bus_number) {
+#ifdef DEBUG
+//	printk("macrisc_config_access(hose: 0x%08lx, bus: 0x%x, devfb: 0x%x, offset: 0x%x)\n",
+//		hose, bus, dev_fn, offset);
+#endif
+	if (bus == hose->first_busno) {
 		if (dev_fn < (11 << 3))
-			return PCIBIOS_DEVICE_NOT_FOUND;
-		out_le32(bp->cfg_addr,
-			 (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8)
-			 + (offset & ~3));
-	} else {
-		/* See pci_read_config_byte */
-		out_le32(bp->cfg_addr, (bus << 16) + (dev_fn << 8) + (offset & ~3) + 1);
-	}
-	udelay(2);
-	out_8(bp->cfg_data + (offset & 3), val);
-	return PCIBIOS_SUCCESSFUL;
-}
+			return 0;
+		caddr = MACRISC_CFA0(dev_fn, offset);
+	} else
+		caddr = MACRISC_CFA1(bus, dev_fn, offset);
+	
+	/* Uninorth will return garbage if we don't read back the value ! */
+	do {
+		out_le32(hose->cfg_addr, caddr);
+	} while(in_le32(hose->cfg_addr) != caddr);
+
+	offset &= has_uninorth ? 0x07 : 0x03;
+	return (unsigned int)(hose->cfg_data) + (unsigned int)offset;
+}
+
+#define cfg_read(val, addr, type, op, op2)	\
+	*val = op((type)(addr))
+#define cfg_write(val, addr, type, op, op2)	\
+	op((type *)(addr), (val)); (void) op2((type *)(addr))
+
+#define cfg_read_bad(val, size)		*val = bad_##size;
+#define cfg_write_bad(val, size)
+
+#define bad_byte	0xff
+#define bad_word	0xffff
+#define bad_dword	0xffffffffU
+
+#define MACRISC_PCI_OP(rw, size, type, op, op2)				    \
+static int __pmac							    \
+macrisc_##rw##_config_##size(struct pci_dev *dev, int off, type val)	    \
+{									    \
+	struct pci_controller *hose = dev->sysdata;			    \
+	unsigned int addr;						    \
+									    \
+	addr = macrisc_cfg_access(hose, dev->bus->number, dev->devfn, off); \
+	if (!addr) {							    \
+		cfg_##rw##_bad(val, size)				    \
+		return PCIBIOS_DEVICE_NOT_FOUND;			    \
+	}								    \
+	cfg_##rw(val, addr, type, op, op2);				    \
+	return PCIBIOS_SUCCESSFUL;					    \
+}
+
+MACRISC_PCI_OP(read, byte, u8 *, in_8, x)
+MACRISC_PCI_OP(read, word, u16 *, in_le16, x)
+MACRISC_PCI_OP(read, dword, u32 *, in_le32, x)
+MACRISC_PCI_OP(write, byte, u8, out_8, in_8)
+MACRISC_PCI_OP(write, word, u16, out_le16, in_le16)
+MACRISC_PCI_OP(write, dword, u32, out_le32, in_le32)
+
+static struct pci_ops macrisc_pci_ops =
+{
+	macrisc_read_config_byte,
+	macrisc_read_config_word,
+	macrisc_read_config_dword,
+	macrisc_write_config_byte,
+	macrisc_write_config_word,
+	macrisc_write_config_dword
+};
 
-__pmac
-int pmac_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn,
-				   unsigned char offset, unsigned short val)
-{
-	struct bridge_data *bp;
 
-	if (bus > max_bus || (bp = bridges[bus]) == 0)
-		return PCIBIOS_DEVICE_NOT_FOUND;
-	if ((offset & 1) != 0)
-		return PCIBIOS_BAD_REGISTER_NUMBER;
-	if (bus == bp->bus_number) {
-		if (dev_fn < (11 << 3))
-			return PCIBIOS_DEVICE_NOT_FOUND;
-		out_le32(bp->cfg_addr,
-			 (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8)
-			 + (offset & ~3));
-	} else {
-		/* See pci_read_config_byte */
-		out_le32(bp->cfg_addr, (bus << 16) + (dev_fn << 8) + (offset & ~3) + 1);
-	}
-	udelay(2);
-	out_le16((volatile unsigned short *)(bp->cfg_data + (offset & 3)), val);
-	return PCIBIOS_SUCCESSFUL;
-}
-
-__pmac
-int pmac_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn,
-				    unsigned char offset, unsigned int val)
+/*
+ * Apple "Chaos" PCI controller.
+ * 
+ * This controller is present on some first generation "PowerSurge"
+ * machines (8500, 8600, ...). It's a very weird beast and will die
+ * in flames if we try to probe the config space.
+ * The long-term solution is to provide a config space "emulation"
+ * based on what we find in OF device tree
+ */
+ 
+static int chaos_config_read_byte(struct pci_dev *dev, int offset, u8 *val)
 {
-	struct bridge_data *bp;
-
-	if (bus > max_bus || (bp = bridges[bus]) == 0)
-		return PCIBIOS_DEVICE_NOT_FOUND;
-	if ((offset & 3) != 0)
-		return PCIBIOS_BAD_REGISTER_NUMBER;
-	if (bus == bp->bus_number) {
-		if (dev_fn < (11 << 3))
-			return PCIBIOS_DEVICE_NOT_FOUND;
-		out_le32(bp->cfg_addr,
-			 (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8)
-			 + offset);
-	} else {
-		/* See pci_read_config_byte */
-		out_le32(bp->cfg_addr, (bus << 16) + (dev_fn << 8) + (offset & ~3) + 1);
-	}
-	udelay(2);
-	out_le32((volatile unsigned int *)bp->cfg_data, val);
-	return PCIBIOS_SUCCESSFUL;
+	return PCIBIOS_DEVICE_NOT_FOUND;
 }
 
-#define GRACKLE_CFA(b, d, o)	(0x80 | ((b) << 8) | ((d) << 16) \
-				 | (((o) & ~3) << 24))
-
-int grackle_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn,
-				     unsigned char offset, unsigned char *val)
+static int chaos_config_read_word(struct pci_dev *dev, int offset, u16 *val)
 {
-	struct bridge_data *bp;
-
-	*val = 0xff;
-	if (bus > max_bus || (bp = bridges[bus]) == 0)
-		return PCIBIOS_DEVICE_NOT_FOUND;
-	out_be32(bp->cfg_addr, GRACKLE_CFA(bus, dev_fn, offset));
-	*val = in_8(bp->cfg_data + (offset & 3));
-	return PCIBIOS_SUCCESSFUL;
+	return PCIBIOS_DEVICE_NOT_FOUND;
 }
 
-int grackle_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn,
-				     unsigned char offset, unsigned short *val)
+static int chaos_config_read_dword(struct pci_dev *dev, int offset, u32 *val)
 {
-	struct bridge_data *bp;
-
-	*val = 0xffff;
-	if (bus > max_bus || (bp = bridges[bus]) == 0)
-		return PCIBIOS_DEVICE_NOT_FOUND;
-	if ((offset & 1) != 0)
-		return PCIBIOS_BAD_REGISTER_NUMBER;
-	out_be32(bp->cfg_addr, GRACKLE_CFA(bus, dev_fn, offset));
-	*val = in_le16((volatile unsigned short *)(bp->cfg_data + (offset&3)));
-	return PCIBIOS_SUCCESSFUL;
+	return PCIBIOS_DEVICE_NOT_FOUND;
 }
 
-int grackle_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn,
-				      unsigned char offset, unsigned int *val)
+static int chaos_config_write_byte(struct pci_dev *dev, int offset, u8 val)
 {
-	struct bridge_data *bp;
-
-	*val = 0xffffffff;
-	if (bus > max_bus || (bp = bridges[bus]) == 0)
-		return PCIBIOS_DEVICE_NOT_FOUND;
-	if ((offset & 3) != 0)
-		return PCIBIOS_BAD_REGISTER_NUMBER;
-	out_be32(bp->cfg_addr, GRACKLE_CFA(bus, dev_fn, offset));
-	*val = in_le32((volatile unsigned int *)bp->cfg_data);
-	return PCIBIOS_SUCCESSFUL;
+	return PCIBIOS_DEVICE_NOT_FOUND;
 }
 
-int grackle_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn,
-				      unsigned char offset, unsigned char val)
+static int chaos_config_write_word(struct pci_dev *dev, int offset, u16 val)
 {
-	struct bridge_data *bp;
-
-	if (bus > max_bus || (bp = bridges[bus]) == 0)
-		return PCIBIOS_DEVICE_NOT_FOUND;
-	out_be32(bp->cfg_addr, GRACKLE_CFA(bus, dev_fn, offset));
-	out_8(bp->cfg_data + (offset & 3), val);
-	return PCIBIOS_SUCCESSFUL;
+	return PCIBIOS_DEVICE_NOT_FOUND;
 }
 
-int grackle_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn,
-				      unsigned char offset, unsigned short val)
+static int chaos_config_write_dword(struct pci_dev *dev, int offset, u32 val)
 {
-	struct bridge_data *bp;
-
-	if (bus > max_bus || (bp = bridges[bus]) == 0)
-		return PCIBIOS_DEVICE_NOT_FOUND;
-	if ((offset & 1) != 0)
-		return PCIBIOS_BAD_REGISTER_NUMBER;
-	out_be32(bp->cfg_addr, GRACKLE_CFA(bus, dev_fn, offset));
-	out_le16((volatile unsigned short *)(bp->cfg_data + (offset&3)), val);
-	return PCIBIOS_SUCCESSFUL;
+	return PCIBIOS_DEVICE_NOT_FOUND;
 }
 
-int grackle_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn,
-				    unsigned char offset, unsigned int val)
+static struct pci_ops chaos_pci_ops =
 {
-	struct bridge_data *bp;
+	chaos_config_read_byte,
+	chaos_config_read_word,
+	chaos_config_read_dword,
+	chaos_config_write_byte,
+	chaos_config_write_word,
+	chaos_config_write_dword
+};
 
-	if (bus > max_bus || (bp = bridges[bus]) == 0)
-		return PCIBIOS_DEVICE_NOT_FOUND;
-	if ((offset & 1) != 0)
-		return PCIBIOS_BAD_REGISTER_NUMBER;
-	out_be32(bp->cfg_addr, GRACKLE_CFA(bus, dev_fn, offset));
-	out_le32((volatile unsigned int *)bp->cfg_data, val);
-	return PCIBIOS_SUCCESSFUL;
-}
 
 /*
  * For a bandit bridge, turn on cache coherency if necessary.
- * N.B. we can't use pcibios_*_config_* here because bridges[]
- * is not initialized yet.
+ * N.B. we could clean this up using the hose ops directly.
  */
-static void __init init_bandit(struct bridge_data *bp)
+static void __init init_bandit(struct pci_controller *bp)
 {
 	unsigned int vendev, magic;
 	int rev;
@@ -543,8 +258,8 @@
 		rev = in_8(bp->cfg_data);
 		if (rev != BANDIT_REVID)
 			printk(KERN_WARNING
-			       "Unknown revision %d for bandit at %p\n",
-			       rev, bp->io_base);
+			       "Unknown revision %d for bandit at %08lx\n",
+			       rev, bp->io_base_phys);
 	} else if (vendev != (BANDIT_DEVID_2 << 16) + APPLE_VENDID) {
 		printk(KERN_WARNING "bandit isn't? (%x)\n", vendev);
 		return;
@@ -555,8 +270,8 @@
 	udelay(2);
 	rev = in_8(bp->cfg_data);
 	if (rev != BANDIT_REVID)
-		printk(KERN_WARNING "Unknown revision %d for bandit at %p\n",
-		       rev, bp->io_base);
+		printk(KERN_WARNING "Unknown revision %d for bandit at %08lx\n",
+		       rev, bp->io_base_phys);
 
 	/* read the word at offset 0x50 */
 	out_le32(bp->cfg_addr, (1UL << BANDIT_DEVNUM) + BANDIT_MAGIC);
@@ -567,16 +282,71 @@
 	magic |= BANDIT_COHERENT;
 	udelay(2);
 	out_le32((volatile unsigned int *)bp->cfg_data, magic);
-	printk(KERN_INFO "Cache coherency enabled for bandit/PSX at %p\n",
-	       bp->io_base);
+	printk(KERN_INFO "Cache coherency enabled for bandit/PSX at %08lx\n",
+	       bp->io_base_phys);
+}
+
+
+/*
+ * Tweak the PCI-PCI bridge chip on the blue & white G3s.
+ */
+static void __init
+init_p2pbridge(void)
+{
+	struct device_node *p2pbridge;
+	struct pci_controller* hose;
+	u8 bus, devfn;
+	u16 val;
+
+	/* XXX it would be better here to identify the specific
+	   PCI-PCI bridge chip we have. */
+	if ((p2pbridge = find_devices("pci-bridge")) == 0
+	    || p2pbridge->parent == NULL
+	    || strcmp(p2pbridge->parent->name, "pci") != 0)
+		return;
+	if (pci_device_from_OF_node(p2pbridge, &bus, &devfn) < 0) {
+#ifdef DEBUG
+		printk("Can't find PCI infos for PCI<->PCI bridge\n");
+#endif		
+		return;
+	}
+	/* Warning: At this point, we have not yet renumbered all busses. 
+	 * So we must use OF walking to find out hose
+	 */
+	hose = pci_find_hose_for_OF_device(p2pbridge);
+	if (!hose) {
+#ifdef DEBUG
+		printk("Can't find hose for PCI<->PCI bridge\n");
+#endif		
+		return;
+	}
+	if (early_read_config_word(hose, bus, devfn,
+				   PCI_BRIDGE_CONTROL, &val) < 0) {
+		printk(KERN_ERR "init_p2pbridge: couldn't read bridge control\n");
+		return;
+	}
+	val &= ~PCI_BRIDGE_CTL_MASTER_ABORT;
+	early_write_config_word(hose, bus, devfn, PCI_BRIDGE_CONTROL, val);
 }
 
+void __init
+pmac_find_bridges(void)
+{
+	add_bridges(find_devices("bandit"));
+	add_bridges(find_devices("chaos"));
+	add_bridges(find_devices("pci"));
+	init_p2pbridge();
+}
+
+#define GRACKLE_CFA(b, d, o)	(0x80 | ((b) << 8) | ((d) << 16) \
+				 | (((o) & ~3) << 24))
+
 #define GRACKLE_PICR1_STG		0x00000040
 #define GRACKLE_PICR1_LOOPSNOOP		0x00000010
 
 /* 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)
+static inline void grackle_set_stg(struct pci_controller* bp, int enable)
 {
 	unsigned int val;
 
@@ -586,9 +356,10 @@
 		(val & ~GRACKLE_PICR1_STG);
 	out_be32(bp->cfg_addr, GRACKLE_CFA(0, 0, 0xa8));
 	out_le32((volatile unsigned int *)bp->cfg_data, val);
+	(void)in_le32((volatile unsigned int *)bp->cfg_data);
 }
 
-static inline void grackle_set_loop_snoop(struct bridge_data *bp, int enable)
+static inline void grackle_set_loop_snoop(struct pci_controller *bp, int enable)
 {
 	unsigned int val;
 
@@ -598,25 +369,68 @@
 		(val & ~GRACKLE_PICR1_LOOPSNOOP);
 	out_be32(bp->cfg_addr, GRACKLE_CFA(0, 0, 0xa8));
 	out_le32((volatile unsigned int *)bp->cfg_data, val);
+	(void)in_le32((volatile unsigned int *)bp->cfg_data);
 }
 
+static void __init
+setup_uninorth(struct pci_controller* hose, struct reg_property* addr)
+{
+	pci_assign_all_busses = 1;
+	has_uninorth = 1;
+	hose->ops = &macrisc_pci_ops;
+	hose->cfg_addr = ioremap(addr->address + 0x800000, 0x1000);
+	hose->cfg_data = ioremap(addr->address + 0xc00000, 0x1000);
+	hose->io_base_phys = addr->address;
+	/* is 0x10000 enough for io space ? */
+	hose->io_base_virt = (void *)ioremap(addr->address, 0x10000);
+
+	/* XXX This is the bridge with the PCI expansion bus. We route
+	 * legacy IOs to it.
+	 */
+	if (addr->address == 0xf2000000)
+		isa_io_base = (unsigned long)hose->io_base_virt;
+}
 
-void __init pmac_find_bridges(void)
+static void __init
+setup_bandit(struct pci_controller* hose, struct reg_property* addr)
 {
-	int bus;
-	struct bridge_data *bridge;
+	hose->ops = &macrisc_pci_ops;
+	hose->cfg_addr = (volatile unsigned int *)
+		ioremap(addr->address + 0x800000, 0x1000);
+	hose->cfg_data = (volatile unsigned char *)
+		ioremap(addr->address + 0xc00000, 0x1000);
+	hose->io_base_phys = addr->address;
+	hose->io_base_virt = (void *) ioremap(addr->address, 0x10000);
+	init_bandit(hose);
+}
+
+static void __init
+setup_chaos(struct pci_controller* hose, struct reg_property* addr)
+{
+	/* assume a `chaos' bridge */
+	hose->ops = &chaos_pci_ops;
+	hose->cfg_addr = (volatile unsigned int *)
+		ioremap(addr->address + 0x800000, 0x1000);
+	hose->cfg_data = (volatile unsigned char *)
+		ioremap(addr->address + 0xc00000, 0x1000);
+	hose->io_base_phys = addr->address;
+	hose->io_base_virt = (void *) ioremap(addr->address, 0x10000);
+}
 
-	bridge_list = 0;
-	max_bus = 0;
-	add_bridges(find_devices("bandit"));
-	add_bridges(find_devices("chaos"));
-	add_bridges(find_devices("pci"));
-	bridges = (struct bridge_data **)
-		alloc_bootmem((max_bus + 1) * sizeof(struct bridge_data *));
-	memset(bridges, 0, (max_bus + 1) * sizeof(struct bridge_data *));
-	for (bridge = bridge_list; bridge != NULL; bridge = bridge->next)
-		for (bus = bridge->bus_number; bus <= bridge->max_bus; ++bus)
-			bridges[bus] = bridge;
+void __init
+setup_grackle(struct pci_controller *hose, unsigned io_space_size)
+{
+	setup_indirect_pci(hose, 0xfec00000, 0xfee00000);
+	hose->io_base_phys = 0xfe000000;
+	hose->io_base_virt = (void *) ioremap(0xfe000000, io_space_size);
+	pci_dram_offset = 0;
+	isa_mem_base = 0xfd000000;
+	isa_io_base = (unsigned long) hose->io_base_virt;
+	if (machine_is_compatible("AAPL,PowerBook1998"))
+		grackle_set_loop_snoop(hose, 1);
+#if 0	/* Disabled for now, HW problems ??? */
+	grackle_set_stg(hose, 1);
+#endif
 }
 
 /*
@@ -626,11 +440,12 @@
  */
 static void __init add_bridges(struct device_node *dev)
 {
-	int *bus_range;
 	int len;
-	struct bridge_data *bp;
+	struct pci_controller *hose;
 	struct reg_property *addr;
-
+	char* disp_name;
+	int *bus_range;
+	
 	for (; dev != NULL; dev = dev->next) {
 		addr = (struct reg_property *) get_property(dev, "reg", &len);
 		if (addr == NULL || len < sizeof(*addr)) {
@@ -640,108 +455,52 @@
 		}
 		bus_range = (int *) get_property(dev, "bus-range", &len);
 		if (bus_range == NULL || len < 2 * sizeof(int)) {
-			printk(KERN_WARNING "Can't get bus-range for %s\n",
-			       dev->full_name);
-			continue;
-		}
-		if (bus_range[1] == bus_range[0])
-			printk(KERN_INFO "PCI bus %d", bus_range[0]);
-		else
-			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;
-			uninorth_bridges[i].iobase_phys = addr->address;
-			/* is 0x10000 enough for io space ? */
-			uninorth_bridges[i].iobase = (void *)ioremap(addr->address, 0x10000);
-			/* 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;
+			printk(KERN_WARNING "Can't get bus-range for %s, assume bus 0\n",
+				       dev->full_name);
 		}
 		
-		bp = (struct bridge_data *) alloc_bootmem(sizeof(*bp));
+		hose = pcibios_alloc_controller();
+		if (!hose)
+			continue;
+		hose->arch_data = dev;
+		hose->first_busno = bus_range ? bus_range[0] : 0;
+		hose->last_busno = bus_range ? bus_range[1] : 0xff;
+
+		disp_name = NULL;
 		if (device_is_compatible(dev, "uni-north")) {
-			bp->cfg_addr = 0;
-			bp->cfg_data = 0;
-			bp->io_base = uninorth_bridges[uninorth_count-1].iobase;
-			bp->io_base_phys = uninorth_bridges[uninorth_count-1].iobase_phys;
+			setup_uninorth(hose, addr);
+			disp_name = "UniNorth";
 		} 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_phys = 0xfe000000;
-                        bp->io_base = (void *) ioremap(0xfe000000, 0x20000);
-                        if (machine_is_compatible("AAPL,PowerBook1998"))
-                        	grackle_set_loop_snoop(bp, 1);
-#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_phys = addr->address;
-			bp->io_base = (void *) ioremap(addr->address, 0x10000);
+			setup_grackle(hose, 0x20000);
+			disp_name = "Grackle (MPC106)";
+		} else if (strcmp(dev->name, "bandit") == 0) {
+			setup_bandit(hose, addr);
+			disp_name = "Bandit";
+		} else if (strcmp(dev->name, "chaos") == 0) {
+			setup_chaos(hose, addr);
+			disp_name = "Chaos";
 		}
+		printk(KERN_INFO "Found %s PCI host bridge at 0x%08x. Firmware bus number: %d->%d\n",
+			disp_name, addr->address, hose->first_busno, hose->last_busno);
+#ifdef DEBUG
+		printk(" ->Hose at 0x%08lx, cfg_addr=0x%08lx,cfg_data=0x%08lx\n",
+			hose, hose->cfg_addr, hose->cfg_data);
+#endif		
+		
+		/* Setup a default isa_io_base */
 		if (isa_io_base == 0)
-			isa_io_base = (unsigned long) bp->io_base;
-		bp->bus_number = bus_range[0];
-		bp->max_bus = bus_range[1];
-		bp->next = bridge_list;
-		bp->node = dev;
-		bridge_list = bp;
-		if (bp->max_bus > max_bus)
-			max_bus = bp->max_bus;
+			isa_io_base = (unsigned long)hose->io_base_virt;
 
-		if (strcmp(dev->name, "bandit") == 0)
-			init_bandit(bp);
+		/* Fixup "bus-range" OF property */
+		fixup_bus_range(dev);
 	}
 }
 
-/* Recursively searches any node that is of type PCI-PCI bridge. Without
- * this, the old code would miss children of P2P bridges and hence not
- * fix IRQ's for cards located behind P2P bridges.
- * - Ranjit Deshpande, 01/20/99
- */
-void __init
-fix_intr(struct device_node *node, struct pci_dev *dev)
-{
-	unsigned int *reg, *class_code;
-
-	for (; node != 0;node = node->sibling) {
-		class_code = (unsigned int *) get_property(node, "class-code", 0);
-		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)
-			continue;
-		/* this is the node, see if it has interrupts */
-		if (node->n_intrs > 0) 
-			dev->irq = node->intrs[0].line;
-		break;
-	}
-}
-
-void __init
-pmac_pcibios_fixup(void)
-{
-	struct pci_dev *dev;
+static void
+pcibios_fixup_OF_interrupts(void)
+{	
+	struct pci_dev* dev;
 	
 	/*
 	 * FIXME: This is broken: We should not assign IRQ's to IRQless
@@ -757,44 +516,43 @@
 		 * should find the device node and se if it has an
 		 * AAPL,interrupts property.
 		 */
-		struct bridge_data *bp = bridges[dev->bus->number];
 		unsigned char pin;
+		struct device_node* node;
 			
-		if (pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin) ||
-		    !pin)
+		if (pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin) || !pin)
 			continue; /* No interrupt generated -> no fixup */
-		/* 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);
+		node = pci_device_to_OF_node(dev);
+		if (!node) {
+			printk("No OF node for device %x:%x\n", dev->bus->number, dev->devfn >> 3);
+			continue;
+		}
+		/* this is the node, see if it has interrupts */
+		if (node->n_intrs > 0) 
+			dev->irq = node->intrs[0].line;
+		pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq);
 	}
 }
 
 void __init
-pmac_setup_pci_ptrs(void)
+pmac_pcibios_fixup(void)
 {
-	struct device_node* np;
+	/* Fixup interrupts according to OF tree */
+	pcibios_fixup_OF_interrupts();
+}
 
-	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);
+/* We don't want to enable USB controllers absent from the OF tree
+ * (iBook second controller)
+ */
+int
+pmac_pci_enable_device_hook(struct pci_dev *dev, int initial)
+{
+	if (dev->vendor == PCI_VENDOR_ID_APPLE
+	    && dev->device == PCI_DEVICE_ID_APPLE_KL_USB) {
+		struct device_node* node;
+		node = pci_device_to_OF_node(dev);
+		if (!node)
+			return -EINVAL;
 	}
-	
-	ppc_md.pcibios_fixup = pmac_pcibios_fixup;
+	return 0;
 }
 

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)