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
- Lines: 312
- Date:
Mon Sep 4 18:39:16 2000
- Orig file:
v2.2.16/arch/ppc/kernel/pmac_pci.c
- Orig date:
Mon Sep 4 18:37:47 2000
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)