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

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

diff -u --recursive --new-file v2.2.16/arch/ppc/kernel/pmac_pci.c linux/arch/ppc/kernel/pmac_pci.c
@@ -28,6 +28,18 @@
 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, unsigned long *mem_ptr);
 
 /*
@@ -71,6 +83,161 @@
 	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)
@@ -360,6 +527,36 @@
 	       bp->io_base);
 }
 
+#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)
+{
+	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_PICR1_STG) :
+		(val & ~GRACKLE_PICR1_STG);
+	out_be32(bp->cfg_addr, GRACKLE_CFA(0, 0, 0xa8));
+	out_le32((volatile unsigned int *)bp->cfg_data, val);
+}
+
+static inline void grackle_set_loop_snoop(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_PICR1_LOOPSNOOP) :
+		(val & ~GRACKLE_PICR1_LOOPSNOOP);
+	out_be32(bp->cfg_addr, GRACKLE_CFA(0, 0, 0xa8));
+	out_le32((volatile unsigned int *)bp->cfg_data, val);
+}
+
+
 __initfunc(unsigned long pmac_find_bridges(unsigned long mem_start, unsigned long mem_end))
 {
 	int bus;
@@ -411,21 +608,49 @@
 			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 *) *mem_ptr;
 		*mem_ptr += sizeof(struct bridge_data);
-		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 (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 = (void *) ioremap(addr->address, 0x10000);
 		}
 		if (isa_io_base == 0)
 			isa_io_base = (unsigned long) bp->io_base;
@@ -468,7 +693,13 @@
 		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);
 	}
 }
 
@@ -476,9 +707,17 @@
 void
 pmac_setup_pci_ptrs(void))
 {
-	if (find_devices("pci") != 0) {
-		/* looks like a G3 powermac */
-		set_config_access_method(grackle);
+	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);
 	}

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