patch-2.1.87 linux/arch/m68k/kernel/bios32.c

Next file: linux/arch/m68k/kernel/console.c
Previous file: linux/arch/m68k/kernel/Makefile
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.86/linux/arch/m68k/kernel/bios32.c linux/arch/m68k/kernel/bios32.c
@@ -0,0 +1,580 @@
+/*
+ * bios32.c - PCI BIOS functions for Alpha systems not using BIOS
+ *	      emulation code.
+ *
+ * Written by Wout Klaren.
+ *
+ * Based on the DEC Alpha bios32.c by Dave Rusling and David Mosberger.
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+
+#if 0
+# define DBG_DEVS(args)		printk args
+#else
+# define DBG_DEVS(args)
+#endif
+
+#ifdef CONFIG_PCI
+
+/*
+ * PCI support for Linux/m68k. Currently only the Hades is supported.
+ *
+ * Notes:
+ *
+ * 1. The PCI memory area starts at address 0x80000000 and the
+ *    I/O area starts at 0xB0000000. Therefore these offsets
+ *    are added to the base addresses when they are read and
+ *    substracted when they are written.
+ *
+ * 2. The support for PCI bridges in the DEC Alpha version has
+ *    been removed in this version.
+ */
+
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+
+#include <asm/atarihw.h>
+#include <asm/atariints.h>
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+#define KB		1024
+#define MB		(1024*KB)
+#define GB		(1024*MB)
+
+#define MAJOR_REV	0
+#define MINOR_REV	0
+
+/*
+ * Base addresses of the PCI memory and I/O areas on the Hades.
+ */
+
+static unsigned long pci_mem_base = 0;
+static unsigned long pci_io_base = 0;
+
+/*
+ * Align VAL to ALIGN, which must be a power of two.
+ */
+
+#define ALIGN(val,align)	(((val) + ((align) - 1)) & ~((align) - 1))
+
+/*
+ * Calculate the address of the PCI configuration area of the given
+ * device.
+ *
+ * BUG: boards with multiple functions are probably not correctly
+ * supported.
+ */
+
+static int mk_conf_addr(unsigned char bus, unsigned char device_fn,
+			unsigned char where, unsigned long *pci_addr)
+{
+	static const unsigned long pci_conf_base[] = { 0xA0080000, 0xA0040000,
+						       0xA0020000, 0xA0010000 };
+	int device = device_fn >> 3;
+
+	DBG_DEVS(("mk_conf_addr(bus=%d ,device_fn=0x%x, where=0x%x, pci_addr=0x%p)\n",
+		  bus, device_fn, where, pci_addr));
+
+	if (device > 3) {
+		DBG_DEVS(("mk_conf_addr: device (%d) > 3, returning -1\n", device));
+		return -1;
+	}
+
+	*pci_addr = pci_conf_base[device] | (where);
+	DBG_DEVS(("mk_conf_addr: returning pci_addr 0x%lx\n", *pci_addr));
+	return 0;
+}
+
+int pcibios_read_config_byte(unsigned char bus, unsigned char device_fn,
+			     unsigned char where, unsigned char *value)
+{
+	unsigned long pci_addr;
+
+	*value = 0xff;
+
+	if (mk_conf_addr(bus, device_fn, where, &pci_addr) < 0)
+		return PCIBIOS_DEVICE_NOT_FOUND;
+
+	*value = *((unsigned char *)pci_addr);
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+int pcibios_read_config_word(unsigned char bus, unsigned char device_fn,
+			     unsigned char where, unsigned short *value)
+{
+	unsigned long pci_addr;
+
+	*value = 0xffff;
+
+	if (where & 0x1)
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+
+	if (mk_conf_addr(bus, device_fn, where, &pci_addr))
+		return PCIBIOS_DEVICE_NOT_FOUND;
+
+	*value = le16_to_cpu(*((unsigned short *)pci_addr));
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+int pcibios_read_config_dword(unsigned char bus, unsigned char device_fn,
+			      unsigned char where, unsigned int *value)
+{
+	unsigned long pci_addr;
+
+	*value = 0xffffffff;
+
+	if (where & 0x3)
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+
+	if (mk_conf_addr(bus, device_fn, where, &pci_addr))
+		return PCIBIOS_DEVICE_NOT_FOUND;
+
+	*value = le32_to_cpu(*((unsigned int *)pci_addr));
+
+	if ((where >= PCI_BASE_ADDRESS_0) && (where <= PCI_BASE_ADDRESS_5))
+	{
+		if ((*value & PCI_BASE_ADDRESS_SPACE) ==
+		    PCI_BASE_ADDRESS_SPACE_IO)
+			*value += pci_io_base;
+		else
+		{
+			if (*value == 0)
+			{
+				/*
+				 * Base address is 0. Test if this base
+				 * address register is used.
+				 */
+
+				*((unsigned long *)pci_addr) = 0xffffffff;
+				if (*((unsigned long *)pci_addr) != 0)
+					*value += pci_mem_base;
+			}
+			else
+				*value += pci_mem_base;
+		}
+	}
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+int pcibios_write_config_byte(unsigned char bus, unsigned char device_fn,
+			      unsigned char where, unsigned char value)
+{
+	unsigned long pci_addr;
+
+	if (mk_conf_addr(bus, device_fn, where, &pci_addr) < 0)
+		return PCIBIOS_DEVICE_NOT_FOUND;
+
+	*((unsigned char *)pci_addr) = value;
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+int pcibios_write_config_word(unsigned char bus, unsigned char device_fn,
+			      unsigned char where, unsigned short value)
+{
+	unsigned long pci_addr;
+
+	if (mk_conf_addr(bus, device_fn, where, &pci_addr) < 0)
+		return PCIBIOS_DEVICE_NOT_FOUND;
+
+	*((unsigned short *)pci_addr) = cpu_to_le16(value);
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+int pcibios_write_config_dword(unsigned char bus, unsigned char device_fn,
+			       unsigned char where, unsigned int value)
+{
+	unsigned long pci_addr;
+
+	if (mk_conf_addr(bus, device_fn, where, &pci_addr) < 0)
+		return PCIBIOS_DEVICE_NOT_FOUND;
+
+	if ((where >= PCI_BASE_ADDRESS_0) && (where <= PCI_BASE_ADDRESS_5))
+	{
+		if ((value & PCI_BASE_ADDRESS_SPACE) ==
+		    PCI_BASE_ADDRESS_SPACE_IO)
+			value -= pci_io_base;
+		else
+			value -= pci_mem_base;
+	}
+
+	*((unsigned int *)pci_addr) = cpu_to_le32(value);
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+/*
+ * Macro to enable programming of the PCI devices. On the Hades this
+ * define should be true, because the Hades has no PCI BIOS.
+ */
+
+#define PCI_MODIFY		1
+
+#if PCI_MODIFY
+
+/*
+ * Leave some room for a VGA card. We assume that the VGA card is
+ * always in the first 32M of PCI memory. For the time being we do
+ * not program the VGA card, because to make this work we also
+ * need to change the frame buffer device.
+ */
+
+#define FIRST_IO_ADDR	0x10000
+#define FIRST_MEM_ADDR	0x02000000
+
+static unsigned int io_base = FIRST_IO_ADDR;	/* Skip first 64K. */
+static unsigned int mem_base = FIRST_MEM_ADDR;	/* Skip first 32M. */
+
+/*
+ * Disable PCI device DEV so that it does not respond to I/O or memory
+ * accesses.
+ */
+
+__initfunc(static void disable_dev(struct pci_dev *dev))
+{
+	struct pci_bus *bus;
+	unsigned short cmd;
+
+	if (dev->class >> 8 == PCI_CLASS_NOT_DEFINED_VGA ||
+	    dev->class >> 8 == PCI_CLASS_DISPLAY_VGA ||
+	    dev->class >> 8 == PCI_CLASS_DISPLAY_XGA)
+		return;
+
+	bus = dev->bus;
+	pcibios_read_config_word(bus->number, dev->devfn, PCI_COMMAND, &cmd);
+
+	cmd &= (~PCI_COMMAND_IO & ~PCI_COMMAND_MEMORY & ~PCI_COMMAND_MASTER);
+	pcibios_write_config_word(bus->number, dev->devfn, PCI_COMMAND, cmd);
+}
+
+/*
+ * Layout memory and I/O for a device:
+ */
+
+#define MAX(val1, val2) ( ((val1) > (val2)) ? val1 : val2)
+
+__initfunc(static void layout_dev(struct pci_dev *dev))
+{
+	struct pci_bus *bus;
+	unsigned short cmd;
+	unsigned int base, mask, size, reg;
+	unsigned int alignto;
+
+	/*
+	 * Skip video cards for the time being.
+	 */
+
+	if (dev->class >> 8 == PCI_CLASS_NOT_DEFINED_VGA ||
+	    dev->class >> 8 == PCI_CLASS_DISPLAY_VGA ||
+	    dev->class >> 8 == PCI_CLASS_DISPLAY_XGA)
+		return;
+
+	bus = dev->bus;
+	pcibios_read_config_word(bus->number, dev->devfn, PCI_COMMAND, &cmd);
+
+	for (reg = PCI_BASE_ADDRESS_0; reg <= PCI_BASE_ADDRESS_5; reg += 4)
+	{
+		/*
+		 * Figure out how much space and of what type this
+		 * device wants.
+		 */
+
+		pcibios_write_config_dword(bus->number, dev->devfn, reg,
+					   0xffffffff);
+		pcibios_read_config_dword(bus->number, dev->devfn, reg, &base);
+
+		if (!base)
+		{
+			/* this base-address register is unused */
+			continue;
+		}
+
+		/*
+		 * We've read the base address register back after
+		 * writing all ones and so now we must decode it.
+		 */
+
+		if (base & PCI_BASE_ADDRESS_SPACE_IO)
+		{
+			/*
+			 * I/O space base address register.
+			 */
+
+			cmd |= PCI_COMMAND_IO;
+
+			base &= PCI_BASE_ADDRESS_IO_MASK;
+			mask = (~base << 1) | 0x1;
+			size = (mask & base) & 0xffffffff;
+			/* align to multiple of size of minimum base */
+			alignto = MAX(0x400, size) ;
+			base = ALIGN(io_base, alignto);
+			io_base = base + size;
+			pcibios_write_config_dword(bus->number, dev->devfn,
+						   reg, base | 0x1);
+			DBG_DEVS(("layout_dev: IO address: %lX\n", base));
+		}
+		else
+		{
+			unsigned int type;
+
+			/*
+			 * Memory space base address register.
+			 */
+
+			cmd |= PCI_COMMAND_MEMORY;
+			type = base & PCI_BASE_ADDRESS_MEM_TYPE_MASK;
+			base &= PCI_BASE_ADDRESS_MEM_MASK;
+			mask = (~base << 1) | 0x1;
+			size = (mask & base) & 0xffffffff;
+			switch (type)
+			{
+			case PCI_BASE_ADDRESS_MEM_TYPE_32:
+				break;
+
+			case PCI_BASE_ADDRESS_MEM_TYPE_64:
+				printk("bios32 WARNING: "
+				       "ignoring 64-bit device in "
+				       "slot %d, function %d: \n",
+				       PCI_SLOT(dev->devfn),
+				       PCI_FUNC(dev->devfn));
+				reg += 4;	/* skip extra 4 bytes */
+				continue;
+
+			case PCI_BASE_ADDRESS_MEM_TYPE_1M:
+				printk("bios32 WARNING: slot %d, function %d "
+				       "requests memory below 1MB---don't "
+				       "know how to do that.\n",
+				       PCI_SLOT(dev->devfn),
+				       PCI_FUNC(dev->devfn));
+				continue;
+			}
+
+			/*
+			 * Align to multiple of size of minimum base
+			 */
+
+			alignto = MAX(0x1000, size) ;
+			base = ALIGN(mem_base, alignto);
+			mem_base = base + size;
+			pcibios_write_config_dword(bus->number, dev->devfn,
+						   reg, base);
+		}
+	}
+
+	/*
+	 * Enable device:
+	 */
+
+	if (dev->class >> 8 == PCI_CLASS_NOT_DEFINED ||
+	    dev->class >> 8 == PCI_CLASS_NOT_DEFINED_VGA ||
+	    dev->class >> 8 == PCI_CLASS_DISPLAY_VGA ||
+	    dev->class >> 8 == PCI_CLASS_DISPLAY_XGA)
+	{
+		/*
+		 * All of these (may) have I/O scattered all around
+		 * and may not use i/o-base address registers at all.
+		 * So we just have to always enable I/O to these
+		 * devices.
+		 */
+		cmd |= PCI_COMMAND_IO;
+	}
+
+	pcibios_write_config_word(bus->number, dev->devfn, PCI_COMMAND,
+				  cmd | PCI_COMMAND_MASTER);
+	DBG_DEVS(("layout_dev: bus %d  slot 0x%x  VID 0x%x  DID 0x%x  class 0x%x\n",
+		  bus->number, PCI_SLOT(dev->devfn), dev->vendor, dev->device, dev->class));
+}
+
+__initfunc(static void layout_bus(struct pci_bus *bus))
+{
+	struct pci_dev *dev;
+
+	DBG_DEVS(("layout_bus: starting bus %d\n", bus->number));
+
+	if (!bus->devices && !bus->children)
+		return;
+
+	/*
+	 * Align the current bases on appropriate boundaries (4K for
+	 * IO and 1MB for memory).
+	 */
+
+	io_base = ALIGN(io_base, 4*KB);
+	mem_base = ALIGN(mem_base, 1*MB);
+
+	/*
+	 * PCI devices might have been setup by a PCI BIOS emulation
+	 * running under TOS. In these cases there is a
+	 * window during which two devices may have an overlapping
+	 * address range.  To avoid this causing trouble, we first
+	 * turn off the I/O and memory address decoders for all PCI
+	 * devices.  They'll be re-enabled only once all address
+	 * decoders are programmed consistently.
+	 */
+
+	for (dev = bus->devices; dev; dev = dev->sibling)
+	{
+		if (dev->class >> 16 != PCI_BASE_CLASS_BRIDGE)
+			disable_dev(dev);
+	}
+
+	/*
+	 * Allocate space to each device:
+	 */
+
+	DBG_DEVS(("layout_bus: starting bus %d devices\n", bus->number));
+
+	for (dev = bus->devices; dev; dev = dev->sibling)
+	{
+		if (dev->class >> 16 != PCI_BASE_CLASS_BRIDGE)
+			layout_dev(dev);
+	}
+}
+
+#endif /* !PCI_MODIFY */
+
+/*
+ * Given the vendor and device ids, find the n'th instance of that device
+ * in the system.
+ */
+
+int pcibios_find_device(unsigned short vendor, unsigned short device_id,
+			unsigned short index, unsigned char *bus,
+			unsigned char *devfn)
+{
+	unsigned int curr = 0;
+	struct pci_dev *dev;
+
+	for (dev = pci_devices; dev; dev = dev->next)
+	{
+		if (dev->vendor == vendor && dev->device == device_id)
+		{
+			if (curr == index)
+			{
+				*devfn = dev->devfn;
+				*bus = dev->bus->number;
+				return PCIBIOS_SUCCESSFUL;
+			}
+			++curr;
+		}
+	}
+	return PCIBIOS_DEVICE_NOT_FOUND;
+}
+
+/*
+ * Given the class, find the n'th instance of that device
+ * in the system.
+ */
+
+int pcibios_find_class(unsigned int class_code, unsigned short index,
+		       unsigned char *bus, unsigned char *devfn)
+{
+	unsigned int curr = 0;
+	struct pci_dev *dev;
+
+	for (dev = pci_devices; dev; dev = dev->next)
+	{
+		if (dev->class == class_code)
+		{
+			if (curr == index)
+			{
+				*devfn = dev->devfn;
+				*bus = dev->bus->number;
+				return PCIBIOS_SUCCESSFUL;
+			}
+			++curr;
+		}
+	}
+	return PCIBIOS_DEVICE_NOT_FOUND;
+}
+
+int pcibios_present(void)
+{
+	if (MACH_IS_HADES)
+		return 1;
+	else
+		return 0;
+}
+
+__initfunc(unsigned long pcibios_init(unsigned long mem_start,
+				      unsigned long mem_end))
+{
+	printk("Linux/m68k PCI BIOS32 revision %x.%02x\n", MAJOR_REV, MINOR_REV);
+
+#if !PCI_MODIFY
+	printk("...NOT modifying existing PCI configuration\n");
+#endif
+
+	return mem_start;
+}
+
+/*
+ * static inline void hades_fixup(void)
+ *
+ * Assign IRQ numbers as used by Linux to the interrupt pins
+ * of the PCI cards.
+ */
+
+__initfunc(static inline void hades_fixup(void))
+{
+	char irq_tab[4] = {
+			    IRQ_TT_MFP_IO0,	/* Slot 0. */
+			    IRQ_TT_MFP_IO1,	/* Slot 1. */
+			    IRQ_TT_MFP_SCC,	/* Slot 2. */
+			    IRQ_TT_MFP_SCSIDMA	/* Slot 3. */
+			  };
+	struct pci_dev *dev;
+	unsigned char slot;
+
+	/*
+	 * Go through all devices, fixing up irqs as we see fit:
+	 */
+
+	for (dev = pci_devices; dev; dev = dev->next)
+	{
+		if (dev->class >> 16 != PCI_BASE_CLASS_BRIDGE)
+		{
+			slot = PCI_SLOT(dev->devfn);	/* Determine slot number. */
+			dev->irq = irq_tab[slot];
+#if PCI_MODIFY
+			pcibios_write_config_byte(dev->bus->number, dev->devfn,
+						  PCI_INTERRUPT_LINE, dev->irq);
+#endif
+		}
+	}
+}
+
+__initfunc(unsigned long pcibios_fixup(unsigned long mem_start,
+				       unsigned long mem_end))
+{
+#if PCI_MODIFY
+	/*
+	 * Scan the tree, allocating PCI memory and I/O space.
+	 */
+
+	layout_bus(&pci_root);
+#endif
+
+	pci_mem_base = 0x80000000;
+	pci_io_base = 0xB0000000;
+
+	/*
+	 * Now is the time to do all those dirty little deeds...
+	 */
+
+	hades_fixup();
+
+	return mem_start;
+}
+#endif /* CONFIG_PCI */

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov