patch-2.1.53 linux/arch/ppc/kernel/pmac_pci.c
Next file: linux/arch/ppc/kernel/pmac_setup.c
Previous file: linux/arch/ppc/kernel/pci.c
Back to the patch index
Back to the overall index
- Lines: 424
- Date:
Thu Sep 4 12:54:48 1997
- Orig file:
v2.1.52/linux/arch/ppc/kernel/pmac_pci.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -u --recursive --new-file v2.1.52/linux/arch/ppc/kernel/pmac_pci.c linux/arch/ppc/kernel/pmac_pci.c
@@ -0,0 +1,423 @@
+/*
+ * Support for PCI bridges found on Power Macintoshes.
+ * At present the "bandit" and "chaos" bridges are supported.
+ * Fortunately you access configuration space in the same
+ * way with either bridge.
+ *
+ * Copyright (C) 1997 Paul Mackerras (paulus@cs.anu.edu.au)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/bios32.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#include <asm/prom.h>
+#include <asm/pci-bridge.h>
+
+struct bridge_data {
+ volatile unsigned int *cfg_addr;
+ volatile unsigned char *cfg_data;
+ void *io_base;
+ int bus_number;
+ int max_bus;
+ struct bridge_data *next;
+ struct device_node *node;
+};
+
+static struct bridge_data **bridges, *bridge_list;
+static int max_bus;
+
+static void add_bridges(struct device_node *dev, unsigned long *mem_ptr);
+
+/*
+ * Magic constants for enabling cache coherency in the bandit/PSX bridge.
+ */
+#define APPLE_VENDID 0x106b
+#define BANDIT_DEVID 1
+#define BANDIT_REVID 3
+
+#define BANDIT_DEVNUM 11
+#define BANDIT_MAGIC 0x50
+#define BANDIT_COHERENT 0x40
+
+/*
+ * 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.
+ */
+static void init_bandit(struct bridge_data *bp)
+{
+ unsigned int vendev, magic;
+ int rev;
+
+ /* read the word at offset 0 in config space for device 11 */
+ out_le32(bp->cfg_addr, (1UL << BANDIT_DEVNUM) + PCI_VENDOR_ID);
+ udelay(2);
+ vendev = in_le32((volatile unsigned int *)bp->cfg_data);
+ if (vendev != (BANDIT_DEVID << 16) + APPLE_VENDID) {
+ printk(KERN_WARNING "bandit isn't? (%x)\n", vendev);
+ return;
+ }
+
+ /* read the revision id */
+ out_le32(bp->cfg_addr, (1UL << BANDIT_DEVNUM) + PCI_REVISION_ID);
+ 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);
+
+ /* read the word at offset 0x50 */
+ out_le32(bp->cfg_addr, (1UL << BANDIT_DEVNUM) + BANDIT_MAGIC);
+ udelay(2);
+ magic = in_le32((volatile unsigned int *)bp->cfg_data);
+ if ((magic & BANDIT_COHERENT) != 0)
+ return;
+ 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);
+}
+
+unsigned long pmac_find_bridges(unsigned long mem_start, unsigned long mem_end)
+{
+ int bus;
+ struct bridge_data *bridge;
+
+ bridge_list = 0;
+ max_bus = 0;
+ add_bridges(find_devices("bandit"), &mem_start);
+ add_bridges(find_devices("chaos"), &mem_start);
+ bridges = (struct bridge_data **) mem_start;
+ mem_start += (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;
+
+ return mem_start;
+}
+
+static void add_bridges(struct device_node *dev, unsigned long *mem_ptr)
+{
+ int *bus_range;
+ int len;
+ struct bridge_data *bp;
+
+ for (; dev != NULL; dev = dev->next) {
+ if (dev->n_addrs < 1) {
+ printk(KERN_WARNING "Can't use %s: no address\n",
+ dev->full_name);
+ continue;
+ }
+ 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, dev->addrs[0].address);
+ bp = (struct bridge_data *) *mem_ptr;
+ *mem_ptr += sizeof(struct bridge_data);
+ bp->cfg_addr = (volatile unsigned int *)
+ (dev->addrs[0].address + 0x800000);
+ bp->cfg_data = (volatile unsigned char *)
+ (dev->addrs[0].address + 0xc00000);
+ bp->io_base = (void *) dev->addrs[0].address;
+ ioremap(dev->addrs[0].address, 0x800000);
+ 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;
+
+ if (strcmp(dev->name, "bandit") == 0)
+ init_bandit(bp);
+ }
+}
+
+void *pci_io_base(unsigned int bus)
+{
+ struct bridge_data *bp;
+
+ if (bus > max_bus || (bp = bridges[bus]) == 0)
+ return 0;
+ return bp->io_base;
+}
+
+int pci_device_loc(struct device_node *dev, unsigned char *bus_ptr,
+ unsigned char *devfn_ptr)
+{
+ unsigned int *reg;
+ 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;
+ }
+ *bus_ptr = reg[0] >> 16;
+ *devfn_ptr = reg[0] >> 8;
+ return 0;
+}
+
+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 {
+ out_le32(bp->cfg_addr, (dev_fn << 8) + (offset & ~3) + 1);
+ }
+ udelay(2);
+ *val = in_8(bp->cfg_data + (offset & 3));
+
+ if (offset == PCI_INTERRUPT_LINE) {
+ /*
+ * Open Firmware often doesn't initialize this
+ * register properly, so we find the node and see
+ * if it has an AAPL,interrupts property.
+ */
+ struct device_node *node;
+ unsigned int *reg;
+
+ for (node = bp->node->child; node != 0; node = node->sibling) {
+ reg = (unsigned int *) get_property(node, "reg", 0);
+ if (reg == 0 || ((reg[0] >> 8) & 0xff) != dev_fn)
+ continue;
+ /* this is the node, see if it has interrupts */
+ if (node->n_intrs > 0)
+ *val = node->intrs[0];
+ break;
+ }
+ }
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+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 || (offset & 1) != 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 {
+ out_le32(bp->cfg_addr, (dev_fn << 8) + (offset & ~3) + 1);
+ }
+ udelay(2);
+ *val = in_le16((volatile unsigned short *)(bp->cfg_data + (offset & 3)));
+ return PCIBIOS_SUCCESSFUL;
+}
+
+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 || (offset & 3) != 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);
+ } else {
+ out_le32(bp->cfg_addr, (dev_fn << 8) + offset + 1);
+ }
+ udelay(2);
+ *val = in_le32((volatile unsigned int *)bp->cfg_data);
+ return PCIBIOS_SUCCESSFUL;
+}
+
+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) {
+ 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 {
+ out_le32(bp->cfg_addr, (dev_fn << 8) + (offset & ~3) + 1);
+ }
+ udelay(2);
+ out_8(bp->cfg_data + (offset & 3), val);
+ return PCIBIOS_SUCCESSFUL;
+}
+
+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 || (offset & 1) != 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 {
+ out_le32(bp->cfg_addr, (dev_fn << 8) + (offset & ~3) + 1);
+ }
+ udelay(2);
+ out_le16((volatile unsigned short *)(bp->cfg_data + (offset & 3)), val);
+ return PCIBIOS_SUCCESSFUL;
+}
+
+int pmac_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn,
+ unsigned char offset, unsigned int val)
+{
+ struct bridge_data *bp;
+
+ if (bus > max_bus || (bp = bridges[bus]) == 0 || (offset & 3) != 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);
+ } else {
+ out_le32(bp->cfg_addr, (dev_fn << 8) + offset + 1);
+ }
+ udelay(2);
+ out_le32((volatile unsigned int *)bp->cfg_data, val);
+ return PCIBIOS_SUCCESSFUL;
+}
+
+int pmac_pcibios_find_device(unsigned short vendor, unsigned short dev_id,
+ unsigned short index, unsigned char *bus_ptr,
+ unsigned char *dev_fn_ptr)
+{
+ int bus, unit, fn, num, devfn;
+ unsigned int x, vendev;
+ unsigned char h;
+
+ if (vendor == 0xffff)
+ return PCIBIOS_BAD_VENDOR_ID;
+ vendev = (dev_id << 16) + vendor;
+ num = 0;
+ for (bus = 0; bus <= max_bus; ++bus) {
+ if (bridges[bus] == 0)
+ continue;
+ unit = fn = 0;
+ if (bus == bridges[bus]->bus_number)
+ unit = 11;
+ while (unit < 32) {
+ devfn = PCI_DEVFN(unit, fn);
+ if (pcibios_read_config_dword(bus, devfn,
+ PCI_VENDOR_ID, &x)
+ == PCIBIOS_SUCCESSFUL && x == vendev) {
+ if (index == num) {
+ *bus_ptr = bus;
+ *dev_fn_ptr = devfn;
+ return PCIBIOS_SUCCESSFUL;
+ }
+ ++num;
+ }
+ if (fn != 0) {
+ if (++fn >= 8) {
+ ++unit;
+ fn = 0;
+ }
+ continue;
+ }
+ if (pcibios_read_config_byte(bus, devfn,
+ PCI_HEADER_TYPE, &h)
+ == PCIBIOS_SUCCESSFUL && (h & 0x80) != 0)
+ ++fn;
+ else
+ ++unit;
+ }
+ }
+ return PCIBIOS_DEVICE_NOT_FOUND;
+}
+
+int pmac_pcibios_find_class(unsigned int class_code, unsigned short index,
+ unsigned char *bus_ptr, unsigned char *dev_fn_ptr)
+{
+ int bus, unit, fn, num, devfn;
+ unsigned int x;
+ unsigned char h;
+
+ num = 0;
+ for (bus = 0; bus <= max_bus; ++bus) {
+ if (bridges[bus] == 0)
+ continue;
+ unit = fn = 0;
+ if (bus == bridges[bus]->bus_number)
+ unit = 11;
+ while (unit < 32) {
+ devfn = PCI_DEVFN(unit, fn);
+ if (pcibios_read_config_dword(bus, devfn,
+ PCI_CLASS_REVISION, &x)
+ == PCIBIOS_SUCCESSFUL && (x >> 8) == class_code) {
+ if (index == num) {
+ *bus_ptr = bus;
+ *dev_fn_ptr = devfn;
+ return PCIBIOS_SUCCESSFUL;
+ }
+ ++num;
+ }
+ if (fn != 0) {
+ if (++fn >= 8) {
+ ++unit;
+ fn = 0;
+ }
+ continue;
+ }
+ if (pcibios_read_config_byte(bus, devfn,
+ PCI_HEADER_TYPE, &h)
+ == PCIBIOS_SUCCESSFUL && (h & 0x80) != 0)
+ ++fn;
+ else
+ ++unit;
+ }
+ }
+ return PCIBIOS_DEVICE_NOT_FOUND;
+}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov