[RFC V2 3/6] MIPS: add PCI support for GT64120-based malta

Antony Pavlov antonynpavlov at gmail.com
Thu Mar 8 14:50:29 EST 2012


Signed-off-by: Antony Pavlov <antonynpavlov at gmail.com>
---
 arch/mips/Kconfig             |   22 ++++
 arch/mips/mach-malta/Makefile |    1 +
 arch/mips/mach-malta/pci.c    |  241 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 264 insertions(+), 0 deletions(-)
 create mode 100644 arch/mips/mach-malta/pci.c

diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index 50d5c67..158cdce 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -36,6 +36,7 @@ config MACH_MIPS_MALTA
 	select SYS_SUPPORTS_32BIT_KERNEL
 	select SYS_SUPPORTS_BIG_ENDIAN
 	select HAS_DEBUG_LL
+	select HW_HAS_PCI
 
 config MACH_MIPS_BCM47XX
 	bool "Broadcom BCM47xx-based boards"
@@ -219,6 +220,27 @@ config CMD_MIPS_CPUINFO
 
 endmenu
 
+menu "Bus options (PCI)"
+
+config HW_HAS_PCI
+	bool
+
+config PCI
+	bool "Support for PCI controller"
+	depends on HW_HAS_PCI
+	select PCI_DOMAINS
+	help
+	  Find out whether you have a PCI motherboard. PCI is the name of a
+	  bus system, i.e. the way the CPU talks to the other stuff inside
+	  your box. If you have PCI, say Y, otherwise N.
+
+config PCI_DOMAINS
+	bool
+
+source "drivers/pci/Kconfig"
+
+endmenu
+
 source common/Kconfig
 source commands/Kconfig
 source net/Kconfig
diff --git a/arch/mips/mach-malta/Makefile b/arch/mips/mach-malta/Makefile
index f3cc668..0c5a701 100644
--- a/arch/mips/mach-malta/Makefile
+++ b/arch/mips/mach-malta/Makefile
@@ -1 +1,2 @@
 obj-y += reset.o
+obj-$(CONFIG_PCI) += pci.o
diff --git a/arch/mips/mach-malta/pci.c b/arch/mips/mach-malta/pci.c
new file mode 100644
index 0000000..5e679a7
--- /dev/null
+++ b/arch/mips/mach-malta/pci.c
@@ -0,0 +1,241 @@
+#include <common.h>
+#include <types.h>
+#include <driver.h>
+#include <init.h>
+#include <mach/hardware.h>
+#include <asm/io.h>
+
+#include <linux/pci.h>
+#include <asm/gt64120.h>
+
+#define GT64120_BASE    0xb4000000
+
+#define PCI_ACCESS_READ  0
+#define PCI_ACCESS_WRITE 1
+
+static struct resource gt64120_mem_resource = {
+	.name	= "GT-64120 PCI MEM",
+	.flags	= IORESOURCE_MEM,
+};
+
+static struct resource gt64120_io_resource = {
+	.name	= "GT-64120 PCI I/O",
+	.flags	= IORESOURCE_IO,
+};
+
+static int gt64xxx_pci0_pcibios_config_access(unsigned char access_type,
+		struct pci_bus *bus, unsigned int devfn, int where, u32 *data)
+{
+	unsigned char busnum = bus->number;
+	u32 intr;
+
+	if ((busnum == 0) && (devfn >= PCI_DEVFN(31, 0)))
+		return -1;	/* Because of a bug in the galileo (for slot 31). */
+
+	/* Clear cause register bits */
+	GT_WRITE(GT_INTRCAUSE_OFS, ~(GT_INTRCAUSE_MASABORT0_BIT |
+	                             GT_INTRCAUSE_TARABORT0_BIT));
+
+	/* Setup address */
+	GT_WRITE(GT_PCI0_CFGADDR_OFS,
+		 (busnum << GT_PCI0_CFGADDR_BUSNUM_SHF) |
+		 (devfn << GT_PCI0_CFGADDR_FUNCTNUM_SHF) |
+		 ((where / 4) << GT_PCI0_CFGADDR_REGNUM_SHF) |
+		 GT_PCI0_CFGADDR_CONFIGEN_BIT);
+
+	if (access_type == PCI_ACCESS_WRITE) {
+		if (busnum == 0 && PCI_SLOT(devfn) == 0) {
+			/*
+			 * The Galileo system controller is acting
+			 * differently than other devices.
+			 */
+			GT_WRITE(GT_PCI0_CFGDATA_OFS, *data);
+		} else
+			__GT_WRITE(GT_PCI0_CFGDATA_OFS, *data);
+	} else {
+		if (busnum == 0 && PCI_SLOT(devfn) == 0) {
+			/*
+			 * The Galileo system controller is acting
+			 * differently than other devices.
+			 */
+			*data = GT_READ(GT_PCI0_CFGDATA_OFS);
+		} else
+			*data = __GT_READ(GT_PCI0_CFGDATA_OFS);
+	}
+
+	/* Check for master or target abort */
+	intr = GT_READ(GT_INTRCAUSE_OFS);
+
+	if (intr & (GT_INTRCAUSE_MASABORT0_BIT | GT_INTRCAUSE_TARABORT0_BIT)) {
+		/* Error occurred */
+
+		/* Clear bits */
+		GT_WRITE(GT_INTRCAUSE_OFS, ~(GT_INTRCAUSE_MASABORT0_BIT |
+		                             GT_INTRCAUSE_TARABORT0_BIT));
+
+		return -1;
+	}
+
+	return 0;
+}
+
+/*
+ * We can't address 8 and 16 bit words directly.  Instead we have to
+ * read/write a 32bit word and mask/modify the data we actually want.
+ */
+static int gt64xxx_pci0_pcibios_read(struct pci_bus *bus, unsigned int devfn,
+		int where, int size, u32 * val)
+{
+	u32 data = 0;
+
+	if (gt64xxx_pci0_pcibios_config_access(PCI_ACCESS_READ, bus, devfn,
+	                                       where, &data))
+		return PCIBIOS_DEVICE_NOT_FOUND;
+
+	if (size == 1)
+		*val = (data >> ((where & 3) << 3)) & 0xff;
+	else if (size == 2)
+		*val = (data >> ((where & 3) << 3)) & 0xffff;
+	else
+		*val = data;
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static int gt64xxx_pci0_pcibios_write(struct pci_bus *bus, unsigned int devfn,
+		int where, int size, u32 val)
+{
+	u32 data = 0;
+
+	if (size == 4)
+		data = val;
+	else {
+		if (gt64xxx_pci0_pcibios_config_access(PCI_ACCESS_READ, bus,
+		                                       devfn, where, &data))
+			return PCIBIOS_DEVICE_NOT_FOUND;
+
+		if (size == 1)
+			data = (data & ~(0xff << ((where & 3) << 3))) |
+				(val << ((where & 3) << 3));
+		else if (size == 2)
+			data = (data & ~(0xffff << ((where & 3) << 3))) |
+				(val << ((where & 3) << 3));
+	}
+
+	if (gt64xxx_pci0_pcibios_config_access(PCI_ACCESS_WRITE, bus, devfn,
+	                                       where, &data))
+		return PCIBIOS_DEVICE_NOT_FOUND;
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+/* FIXME: it's HACK */
+/* function returns memory address for begin of pci resource */
+static int gt64xxx_res_start(struct pci_bus *bus, resource_size_t res_addr)
+{
+	return res_addr | 0xa0000000;
+}
+
+struct pci_ops gt64xxx_pci0_ops = {
+	.read	= gt64xxx_pci0_pcibios_read,
+	.write	= gt64xxx_pci0_pcibios_write,
+
+	.res_start = gt64xxx_res_start,
+};
+
+static struct pci_controller gt64120_controller = {
+	.pci_ops	= &gt64xxx_pci0_ops,
+	.io_resource	= &gt64120_io_resource,
+	.mem_resource	= &gt64120_mem_resource,
+};
+
+int pcibios_init()
+{
+	resource_size_t start, end, map, start1, end1, map1, mask, res_end;
+
+	/*
+	 * Due to a bug in the Galileo system controller, we need
+	 * to setup the PCI BAR for the Galileo internal registers.
+	 * This should be done in the bios/bootprom and will be
+	 * fixed in a later revision of YAMON (the MIPS boards
+	 * boot prom).
+	 */
+	GT_WRITE(GT_PCI0_CFGADDR_OFS,
+		 (0 << GT_PCI0_CFGADDR_BUSNUM_SHF) | /* Local bus */
+		 (0 << GT_PCI0_CFGADDR_DEVNUM_SHF) | /* GT64120 dev */
+		 (0 << GT_PCI0_CFGADDR_FUNCTNUM_SHF) | /* Function 0*/
+		 ((0x20/4) << GT_PCI0_CFGADDR_REGNUM_SHF) | /* BAR 4*/
+		 GT_PCI0_CFGADDR_CONFIGEN_BIT);
+
+	/* Perform the write */
+	//GT_WRITE(GT_PCI0_CFGDATA_OFS, CPHYSADDR(MIPS_GT_BASE));
+	//GT_WRITE(GT_PCI0_CFGDATA_OFS, GT64120_BASE);
+	GT_WRITE(GT_PCI0_CFGDATA_OFS, 0x14000000);
+
+#define PCI0_IO_SPACE_BASE	0x10000000
+#define PCI0_IO_SPACE_SIZE	0x01000000
+#define PCI0_MEM_SPACE_BASE	0x11000000
+#define PCI0_MEM_SPACE_SIZE	0x01000000
+
+#if 1
+	GT_WRITE(GT_PCI0IOLD_OFS, (PCI0_IO_SPACE_BASE >> 21) & 0x7ff);
+	GT_WRITE(GT_PCI0IOHD_OFS, ((PCI0_IO_SPACE_BASE + PCI0_IO_SPACE_SIZE - 1) >> 21) & 0x7f);
+	GT_WRITE(GT_PCI0M0LD_OFS, (PCI0_MEM_SPACE_BASE >> 21) & 0x7ff);
+	GT_WRITE(GT_PCI0M0HD_OFS, ((PCI0_MEM_SPACE_BASE + PCI0_MEM_SPACE_SIZE - 1) >> 21) & 0x7f);
+#endif
+
+	/* Here is linux code. It assumes, that firmware made the work... */
+
+	/* Set up resource ranges from the controller's registers.  */
+	start = GT_READ(GT_PCI0M0LD_OFS);
+	end = GT_READ(GT_PCI0M0HD_OFS);
+	map = GT_READ(GT_PCI0M0REMAP_OFS);
+	end = (end & GT_PCI_HD_MSK) | (start & ~GT_PCI_HD_MSK);
+	start1 = GT_READ(GT_PCI0M1LD_OFS);
+	end1 = GT_READ(GT_PCI0M1HD_OFS);
+	map1 = GT_READ(GT_PCI0M1REMAP_OFS);
+	end1 = (end1 & GT_PCI_HD_MSK) | (start1 & ~GT_PCI_HD_MSK);
+#if 0
+	/* Cannot support multiple windows, use the wider.  */
+	if (end1 - start1 > end - start) {
+		start = start1;
+		end = end1;
+		map = map1;
+	}
+#endif
+	mask = ~(start ^ end);
+        /* We don't support remapping with a discontiguous mask.  */
+	BUG_ON((start & GT_PCI_HD_MSK) != (map & GT_PCI_HD_MSK) &&
+	       mask != ~((mask & -mask) - 1));
+	gt64120_mem_resource.start = start;
+	res_end = end;
+	gt64120_controller.mem_offset = (start & mask) - (map & mask);
+	/* Addresses are 36-bit, so do shifts in the destinations.  */
+	gt64120_mem_resource.start <<= GT_PCI_DCRM_SHF;
+	res_end <<= GT_PCI_DCRM_SHF;
+	res_end |= (1 << GT_PCI_DCRM_SHF) - 1;
+	gt64120_mem_resource.size = res_end - gt64120_mem_resource.start + 1;
+	gt64120_controller.mem_offset <<= GT_PCI_DCRM_SHF;
+
+	start = GT_READ(GT_PCI0IOLD_OFS);
+	end = GT_READ(GT_PCI0IOHD_OFS);
+	map = GT_READ(GT_PCI0IOREMAP_OFS);
+	end = (end & GT_PCI_HD_MSK) | (start & ~GT_PCI_HD_MSK);
+	mask = ~(start ^ end);
+        /* We don't support remapping with a discontiguous mask.  */
+	BUG_ON((start & GT_PCI_HD_MSK) != (map & GT_PCI_HD_MSK) &&
+	       mask != ~((mask & -mask) - 1));
+	gt64120_io_resource.start = map & mask;
+	res_end = (map & mask) | ~mask;
+	gt64120_controller.io_offset = 0;
+	/* Addresses are 36-bit, so do shifts in the destinations.  */
+	gt64120_io_resource.start <<= GT_PCI_DCRM_SHF;
+	res_end <<= GT_PCI_DCRM_SHF;
+	res_end |= (1 << GT_PCI_DCRM_SHF) - 1;
+	gt64120_io_resource.size = res_end - gt64120_io_resource.start + 1;
+
+	register_pci_controller(&gt64120_controller);
+
+	return 0;
+}
+EXPORT_SYMBOL(pcibios_init);
-- 
1.7.8.3




More information about the barebox mailing list