patch-2.4.19 linux-2.4.19/arch/mips/cobalt/pci.c

Next file: linux-2.4.19/arch/mips/cobalt/promcon.c
Previous file: linux-2.4.19/arch/mips/cobalt/irq.c
Back to the patch index
Back to the overall index

diff -urN linux-2.4.18/arch/mips/cobalt/pci.c linux-2.4.19/arch/mips/cobalt/pci.c
@@ -0,0 +1,429 @@
+/*
+ * Cobalt Qube/Raq PCI support
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1995, 1996, 1997 by Ralf Baechle
+ * Copyright (C) 2001 by Liam Davies (ldavies@agile.tv)
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+
+#include <asm/cobalt/cobalt.h>
+#include <asm/pci.h>
+#include <asm/io.h>
+ 
+#ifdef CONFIG_PCI
+
+static void qube_expansion_slot_bist(struct pci_dev *dev)
+{
+	unsigned char ctrl;
+	int timeout = 100000;
+
+	pci_read_config_byte(dev, PCI_BIST, &ctrl);
+	if(!(ctrl & PCI_BIST_CAPABLE))
+		return;
+
+	pci_write_config_byte(dev, PCI_BIST, ctrl|PCI_BIST_START);
+	do {
+		pci_read_config_byte(dev, PCI_BIST, &ctrl);
+		if(!(ctrl & PCI_BIST_START))
+			break;
+	} while(--timeout > 0);
+	if((timeout <= 0) || (ctrl & PCI_BIST_CODE_MASK))
+		printk("PCI: Expansion slot card failed BIST with code %x\n",
+		       (ctrl & PCI_BIST_CODE_MASK));
+}
+
+static void qube_expansion_slot_fixup(struct pci_dev *dev)
+{
+	unsigned short pci_cmd;
+	unsigned long ioaddr_base = 0x10108000; /* It's magic, ask Doug. */
+	unsigned long memaddr_base = 0x12000000;
+	int i;
+
+	/* Enable bits in COMMAND so driver can talk to it. */
+	pci_read_config_word(dev, PCI_COMMAND, &pci_cmd);
+	pci_cmd |= (PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
+	pci_write_config_word(dev, PCI_COMMAND, pci_cmd);
+
+	/* Give it a working IRQ. */
+	pci_write_config_byte(dev, PCI_INTERRUPT_LINE, COBALT_QUBE_SLOT_IRQ);
+
+	/* Fixup base addresses, we only support I/O at the moment. */
+	for(i = 0; i <= 5; i++) {
+		unsigned int regaddr = (PCI_BASE_ADDRESS_0 + (i * 4));
+		unsigned int rval, mask, size, alignme, aspace;
+		unsigned long *basep = &ioaddr_base;
+
+		/* Check type first, punt if non-IO. */
+		pci_read_config_dword(dev, regaddr, &rval);
+		aspace = (rval & PCI_BASE_ADDRESS_SPACE);
+		if(aspace != PCI_BASE_ADDRESS_SPACE_IO)
+			basep = &memaddr_base;
+
+		/* Figure out how much it wants, if anything. */
+		pci_write_config_dword(dev, regaddr, 0xffffffff);
+		pci_read_config_dword(dev, regaddr, &rval);
+
+		/* Unused? */
+		if(rval == 0)
+			continue;
+
+		rval &= PCI_BASE_ADDRESS_IO_MASK;
+		mask = (~rval << 1) | 0x1;
+		size = (mask & rval) & 0xffffffff;
+		alignme = size;
+		if(alignme < 0x400)
+			alignme = 0x400;
+		rval = ((*basep + (alignme - 1)) & ~(alignme - 1));
+		*basep = (rval + size);
+		pci_write_config_dword(dev, regaddr, rval | aspace);
+		dev->resource[i].start = rval;
+		dev->resource[i].end = *basep - 1;
+		if(aspace == PCI_BASE_ADDRESS_SPACE_IO) {
+			dev->resource[i].start -= 0x10000000;
+			dev->resource[i].end -= 0x10000000;
+		}
+	}
+	qube_expansion_slot_bist(dev);
+}
+
+static void qube_raq_via_bmIDE_fixup(struct pci_dev *dev)
+{
+	unsigned short cfgword;
+	unsigned char lt;
+
+	/* Enable Bus Mastering and fast back to back. */
+	pci_read_config_word(dev, PCI_COMMAND, &cfgword);
+	cfgword |= (PCI_COMMAND_FAST_BACK | PCI_COMMAND_MASTER);
+	pci_write_config_word(dev, PCI_COMMAND, cfgword);
+
+	/* Enable both ide interfaces. ROM only enables primary one.  */
+	pci_write_config_byte(dev, 0x40, 0xb);
+
+	/* Set latency timer to reasonable value. */
+	pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lt);
+	if(lt < 64)
+		pci_write_config_byte(dev, PCI_LATENCY_TIMER, 64);
+	pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, 7);
+}
+
+static void qube_raq_tulip_fixup(struct pci_dev *dev)
+{
+	unsigned short pci_cmd;
+	extern int cobalt_is_raq;
+
+	/* Fixup the first tulip located at device PCICONF_ETH0 */
+	if (PCI_SLOT(dev->devfn) == COBALT_PCICONF_ETH0) {
+		/*
+		 * Now tell the Ethernet device that we expect an interrupt at
+		 * IRQ 13 and not the default 189.
+		 *
+		 * The IRQ of the first Tulip is different on Qube and RaQ
+		 */
+		if (!cobalt_is_raq) {
+			/* All Qube's route this the same way. */
+			pci_write_config_byte(dev, PCI_INTERRUPT_LINE,
+                                              COBALT_QUBE_ETH_IRQ);
+		} else {
+			/* Setup the first Tulip on the RAQ */
+			pci_write_config_byte(dev, PCI_INTERRUPT_LINE,
+                                              COBALT_RAQ_ETH0_IRQ);
+		}
+		dev->resource[0].start = 0x100000;
+		dev->resource[0].end = 0x10007f;
+		if (dev->resource[1].start < 0x10000000) {
+			dev->resource[1].start = 0xe9ffec00;
+			dev->resource[1].end = 0xe9ffefff;
+			pci_write_config_dword(dev, PCI_BASE_ADDRESS_1, 0xe9ffec00);
+		}
+	/* Fixup the second tulip located at device PCICONF_ETH1 */
+	} else if (PCI_SLOT(dev->devfn) == COBALT_PCICONF_ETH1) {
+
+		/* Enable the second Tulip device. */
+		pci_read_config_word(dev, PCI_COMMAND, &pci_cmd);
+		pci_cmd |= (PCI_COMMAND_IO | PCI_COMMAND_MASTER);
+		pci_write_config_word(dev, PCI_COMMAND, pci_cmd);
+
+		/* Give it it's IRQ. */
+		pci_write_config_byte(dev, PCI_INTERRUPT_LINE,
+                                      COBALT_RAQ_ETH1_IRQ);
+
+		/* And finally, a usable I/O space allocation, right after what
+		 * the first Tulip uses.
+		 */
+		dev->resource[0].start = 0x101000;
+		dev->resource[0].end = 0x10107f;
+	}
+}
+
+static void qube_raq_scsi_fixup(struct pci_dev *dev)
+{
+	unsigned short pci_cmd;
+	extern int cobalt_is_raq;
+
+        /*
+         * Tell the SCSI device that we expect an interrupt at
+         * IRQ 7 and not the default 0.
+         */
+        pci_write_config_byte(dev, PCI_INTERRUPT_LINE, COBALT_SCSI_IRQ);
+
+	if (cobalt_is_raq) {
+
+		/* Enable the device. */
+		pci_read_config_word(dev, PCI_COMMAND, &pci_cmd);
+
+		pci_cmd |= (PCI_COMMAND_IO | PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY
+			| PCI_COMMAND_INVALIDATE);
+		pci_write_config_word(dev, PCI_COMMAND, pci_cmd);
+
+		/* Give it it's RAQ IRQ. */
+		pci_write_config_byte(dev, PCI_INTERRUPT_LINE, 4);
+
+		/* And finally, a usable I/O space allocation, right after what
+		 * the second Tulip uses.
+		 */
+		pci_write_config_dword(dev, PCI_BASE_ADDRESS_0, 0x10102001);
+		pci_write_config_dword(dev, PCI_BASE_ADDRESS_1, 0x00002000);
+		pci_write_config_dword(dev, PCI_BASE_ADDRESS_2, 0x00100000);
+	}
+}
+
+static void qube_raq_galileo_fixup(struct pci_dev *dev)
+{
+	unsigned short galileo_id;
+
+	/* Fix PCI latency-timer and cache-line-size values in Galileo
+	 * host bridge.
+	 */
+	pci_write_config_byte(dev, PCI_LATENCY_TIMER, 64);
+	pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, 7);
+
+	/* On all machines prior to Q2, we had the STOP line disconnected
+	 * from Galileo to VIA on PCI.  The new Galileo does not function
+	 * correctly unless we have it connected.
+	 *
+	 * Therefore we must set the disconnect/retry cycle values to
+	 * something sensible when using the new Galileo.
+	 */
+	pci_read_config_word(dev, PCI_REVISION_ID, &galileo_id);
+	galileo_id &= 0xff;     /* mask off class info */
+	if (galileo_id == 0x10) {
+		/* New Galileo, assumes PCI stop line to VIA is connected. */
+		*((volatile unsigned int *)0xb4000c04) = 0x00004020;
+	} else if (galileo_id == 0x1 || galileo_id == 0x2) {
+		signed int timeo;
+		/* XXX WE MUST DO THIS ELSE GALILEO LOCKS UP! -DaveM */
+		timeo = *((volatile unsigned int *)0xb4000c04);
+		/* Old Galileo, assumes PCI STOP line to VIA is disconnected. */
+		*((volatile unsigned int *)0xb4000c04) = 0x0000ffff;
+	}
+}
+
+static void
+qube_pcibios_fixup(struct pci_dev *dev)
+{
+	if (PCI_SLOT(dev->devfn) == COBALT_PCICONF_PCISLOT) {
+		unsigned int tmp;
+
+		/* See if there is a device in the expansion slot, if so
+		 * discover its resources and fixup whatever we need to
+		 */
+		pci_read_config_dword(dev, PCI_VENDOR_ID, &tmp);
+		if(tmp != 0xffffffff && tmp != 0x00000000)
+			qube_expansion_slot_fixup(dev);
+	}
+}
+
+struct pci_fixup pcibios_fixups[] = {
+	{ PCI_FIXUP_HEADER, PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_1, qube_raq_via_bmIDE_fixup },
+	{ PCI_FIXUP_HEADER, PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_21142, qube_raq_tulip_fixup },
+	{ PCI_FIXUP_HEADER, PCI_VENDOR_ID_GALILEO, PCI_ANY_ID, qube_raq_galileo_fixup },
+	{ PCI_FIXUP_HEADER, PCI_VENDOR_ID_NCR, PCI_DEVICE_ID_NCR_53C860, qube_raq_scsi_fixup },
+	{ PCI_FIXUP_HEADER, PCI_ANY_ID, PCI_ANY_ID, qube_pcibios_fixup }
+};
+
+
+static __inline__ int pci_range_ck(struct pci_dev *dev) 
+{
+       if ((dev->bus->number == 0) 
+           && ((PCI_SLOT (dev->devfn) == 0) 
+               || ((PCI_SLOT (dev->devfn) > 6) 
+                   && (PCI_SLOT (dev->devfn) <= 12))))
+		return 0;  /* OK device number  */
+
+	return -1;  /* NOT ok device number */
+}
+
+#define PCI_CFG_DATA	((volatile unsigned long *)0xb4000cfc)
+#define PCI_CFG_CTRL	((volatile unsigned long *)0xb4000cf8)
+
+#define PCI_CFG_SET(dev,where) \
+       ((*PCI_CFG_CTRL) = (0x80000000 | (PCI_SLOT ((dev)->devfn) << 11) | \
+                           (PCI_FUNC ((dev)->devfn) << 8) | (where)))
+
+static int qube_pci_read_config_dword (struct pci_dev *dev,
+                                          int where,
+                                          u32 *val)
+{
+	if (where & 0x3)
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+	if (pci_range_ck (dev)) {
+		*val = 0xFFFFFFFF;
+		return PCIBIOS_DEVICE_NOT_FOUND;
+	}
+	PCI_CFG_SET(dev, where);
+	*val = *PCI_CFG_DATA;
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static int qube_pci_read_config_word (struct pci_dev *dev,
+                                         int where,
+                                         u16 *val)
+{
+        if (where & 0x1)
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+	if (pci_range_ck (dev)) {
+		*val = 0xffff;
+		return PCIBIOS_DEVICE_NOT_FOUND;
+	}
+	PCI_CFG_SET(dev, (where & ~0x3));
+	*val = *PCI_CFG_DATA >> ((where & 3) * 8);
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static int qube_pci_read_config_byte (struct pci_dev *dev,
+                                         int where,
+                                         u8 *val)
+{
+	if (pci_range_ck (dev)) {
+		*val = 0xff;
+		return PCIBIOS_DEVICE_NOT_FOUND;
+	}
+	PCI_CFG_SET(dev, (where & ~0x3));
+	*val = *PCI_CFG_DATA >> ((where & 3) * 8);
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static int qube_pci_write_config_dword (struct pci_dev *dev,
+                                           int where,
+                                           u32 val)
+{
+	if(where & 0x3)
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+	if (pci_range_ck (dev))
+		return PCIBIOS_DEVICE_NOT_FOUND;
+	PCI_CFG_SET(dev, where);
+	*PCI_CFG_DATA = val;
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static int
+qube_pci_write_config_word (struct pci_dev *dev,
+                                int where, 
+                               u16 val)
+{
+	unsigned long tmp;
+
+	if (where & 0x1)
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+	if (pci_range_ck (dev))
+		return PCIBIOS_DEVICE_NOT_FOUND;
+	PCI_CFG_SET(dev, (where & ~0x3));
+	tmp = *PCI_CFG_DATA;
+	tmp &= ~(0xffff << ((where & 0x3) * 8));
+	tmp |=  (val << ((where & 0x3) * 8));
+	*PCI_CFG_DATA = tmp;
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static int
+qube_pci_write_config_byte (struct pci_dev *dev,
+                                int where, 
+                               u8 val)
+{
+	unsigned long tmp;
+
+	if (pci_range_ck (dev))
+		return PCIBIOS_DEVICE_NOT_FOUND;
+	PCI_CFG_SET(dev, (where & ~0x3));
+	tmp = *PCI_CFG_DATA;
+	tmp &= ~(0xff << ((where & 0x3) * 8));
+	tmp |=  (val << ((where & 0x3) * 8));
+	*PCI_CFG_DATA = tmp;
+	return PCIBIOS_SUCCESSFUL;
+}
+
+
+struct pci_ops qube_pci_ops = {
+	qube_pci_read_config_byte,
+	qube_pci_read_config_word,
+	qube_pci_read_config_dword,
+	qube_pci_write_config_byte,
+	qube_pci_write_config_word,
+	qube_pci_write_config_dword
+};
+
+void __init pcibios_init(void)
+{
+	printk("PCI: Probing PCI hardware\n");
+
+	ioport_resource.start = 0x00000000;
+	ioport_resource.end = 0x0fffffff;
+
+	iomem_resource.start = 0x01000000;
+	iomem_resource.end = 0xffffffff;
+
+	pci_scan_bus(0, &qube_pci_ops, NULL);
+}
+
+char *pcibios_setup(char *str)
+{
+	return str;
+}
+
+int pcibios_enable_device(struct pci_dev *dev)
+{
+	u16 cmd, status;
+	
+	pci_read_config_word(dev, PCI_COMMAND, &cmd);
+	pci_read_config_word(dev, PCI_STATUS, &status);
+	printk("PCI: Enabling device %s (%04x  %04x)\n", dev->slot_name, cmd, status);
+	/* We'll sort this out when we know it isn't enabled ;) */
+
+	return 0;
+}
+
+void pcibios_align_resource(void *data, struct resource *res,
+		unsigned long size)
+{
+
+	panic("Uhhoh called pcibios_align_resource\n");
+}
+
+void pcibios_update_resource(struct pci_dev *dev, struct resource *root, 
+		struct resource *res, int resource)
+{
+
+	panic("Uhhoh called pcibios_update_resource\n");
+}
+
+void __init pcibios_fixup_bus(struct pci_bus *bus)
+{
+	/* We don't have sub-busses to fixup here */
+}
+
+unsigned int __init pcibios_assign_all_busses(void)
+{
+	return 1;
+}
+
+#endif /* CONFIG_PCI */

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