[PATCH] ARM: Gemini: Add support for PCI BUS

Hans Ulli Kroll ulli.kroll at googlemail.com
Sat Nov 27 07:24:35 EST 2010


Add support for PCI Bux on Gemini Devices SL3516
-changed Paulius Zaleckas mail address
-changed #include <asm/gpio.h> to #include <linux/gpio.h>

Signed-off-by: Hans Ulli Kroll <ulli.kroll at googlemail.com>
---
 arch/arm/Kconfig                             |    1 +
 arch/arm/mach-gemini/Makefile                |    2 +
 arch/arm/mach-gemini/include/mach/hardware.h |    8 +
 arch/arm/mach-gemini/include/mach/irqs.h     |    7 +-
 arch/arm/mach-gemini/mm.c                    |    5 +
 arch/arm/mach-gemini/pci.c                   |  319 ++++++++++++++++++++++++++
 6 files changed, 340 insertions(+), 2 deletions(-)
 create mode 100644 arch/arm/mach-gemini/pci.c

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index a19a526..5d4b398 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -307,6 +307,7 @@ config ARCH_GEMINI
 	select CPU_FA526
 	select ARCH_REQUIRE_GPIOLIB
 	select ARCH_USES_GETTIMEOFFSET
+	select PCI
 	help
 	  Support for the Cortina Systems Gemini family SoCs
 
diff --git a/arch/arm/mach-gemini/Makefile b/arch/arm/mach-gemini/Makefile
index c5b24b9..c263f48 100644
--- a/arch/arm/mach-gemini/Makefile
+++ b/arch/arm/mach-gemini/Makefile
@@ -6,6 +6,8 @@
 
 obj-y			:= irq.o mm.o time.o devices.o gpio.o
 
+obj-$(CONFIG_PCI)	+= pci.o
+
 # Board-specific support
 obj-$(CONFIG_MACH_NAS4220B)	+= board-nas4220b.o
 obj-$(CONFIG_MACH_RUT100)	+= board-rut1xx.o
diff --git a/arch/arm/mach-gemini/include/mach/hardware.h b/arch/arm/mach-gemini/include/mach/hardware.h
index 213a4fc..38c530f 100644
--- a/arch/arm/mach-gemini/include/mach/hardware.h
+++ b/arch/arm/mach-gemini/include/mach/hardware.h
@@ -71,4 +71,12 @@
  */
 #define IO_ADDRESS(x)	((((x) & 0xFFF00000) >> 4) | ((x) & 0x000FFFFF) | 0xF0000000)
 
+/*
+ * PCI subsystem macros
+ */
+#define PCIBIOS_MIN_IO		0x00000100
+#define PCIBIOS_MIN_MEM		0x00000000
+
+#define pcibios_assign_all_busses()	1
+
 #endif
diff --git a/arch/arm/mach-gemini/include/mach/irqs.h b/arch/arm/mach-gemini/include/mach/irqs.h
index 06bc47e..c737673 100644
--- a/arch/arm/mach-gemini/include/mach/irqs.h
+++ b/arch/arm/mach-gemini/include/mach/irqs.h
@@ -43,11 +43,14 @@
 
 #define NORMAL_IRQ_NUM	32
 
-#define GPIO_IRQ_BASE	NORMAL_IRQ_NUM
+#define PCI_IRQ_BASE	NORMAL_IRQ_NUM
+#define PCI_IRQ_NUM	4
+
+#define GPIO_IRQ_BASE	(NORMAL_IRQ_NUM + PCI_IRQ_NUM)
 #define GPIO_IRQ_NUM	(3 * 32)
 
 #define ARCH_TIMER_IRQ	IRQ_TIMER2
 
-#define NR_IRQS		(NORMAL_IRQ_NUM + GPIO_IRQ_NUM)
+#define NR_IRQS		(NORMAL_IRQ_NUM + PCI_IRQ_NUM + GPIO_IRQ_NUM)
 
 #endif /* __MACH_IRQS_H__ */
diff --git a/arch/arm/mach-gemini/mm.c b/arch/arm/mach-gemini/mm.c
index 5194824..2bf20b2 100644
--- a/arch/arm/mach-gemini/mm.c
+++ b/arch/arm/mach-gemini/mm.c
@@ -59,6 +59,11 @@ static struct map_desc gemini_io_desc[] __initdata = {
 		.length		= SZ_512K,
 		.type 		= MT_DEVICE,
 	}, {
+		.virtual	= IO_ADDRESS(GEMINI_PCI_IO_BASE),
+		.pfn		= __phys_to_pfn(GEMINI_PCI_IO_BASE),
+		.length		= SZ_512K,
+		.type		= MT_DEVICE,
+	}, {
 		.virtual	= IO_ADDRESS(GEMINI_FLASH_CTRL_BASE),
 		.pfn		= __phys_to_pfn(GEMINI_FLASH_CTRL_BASE),
 		.length		= SZ_512K,
diff --git a/arch/arm/mach-gemini/pci.c b/arch/arm/mach-gemini/pci.c
new file mode 100644
index 0000000..1acdd07
--- /dev/null
+++ b/arch/arm/mach-gemini/pci.c
@@ -0,0 +1,319 @@
+/*
+ *  Support for Gemini PCI Controller
+ *
+ *  Copyright (C) 2009 Janos Laube <janos.dev at gmail.com>
+ *  Copyright (C) 2009 Paulius Zaleckas <paulius.zaleckas at gmail.com>
+ *
+ * based on SL2312 PCI controller code
+ *   Storlink (C) 2003
+ *
+ * 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/irq.h>
+
+#include <asm/mach/pci.h>
+#include <linux/gpio.h>
+
+#include <mach/irqs.h>
+
+#define GEMINI_PCI_IOSIZE_1M		0x0000
+
+#define GEMINI_PCI_PMC			0x40
+#define GEMINI_PCI_PMCSR		0x44
+#define GEMINI_PCI_CTRL1		0x48
+#define GEMINI_PCI_CTRL2		0x4C
+#define GEMINI_PCI_MEM1_BASE_SIZE	0x50
+#define GEMINI_PCI_MEM2_BASE_SIZE	0x54
+#define GEMINI_PCI_MEM3_BASE_SIZE	0x58
+
+#define PCI_CTRL2_INTSTS_OFFSET		28
+#define PCI_CTRL2_INTMASK_OFFSET	22
+
+#define GEMINI_PCI_DMA_MASK		0xFFF00000
+#define GEMINI_PCI_DMA_MEM1_BASE	0x00000000
+#define GEMINI_PCI_DMA_MEM2_BASE	0x00000000
+#define GEMINI_PCI_DMA_MEM3_BASE	0x00000000
+#define GEMINI_PCI_DMA_MEM1_SIZE	7
+#define GEMINI_PCI_DMA_MEM2_SIZE	6
+#define GEMINI_PCI_DMA_MEM3_SIZE	6
+
+#define PCI_CONF_ENABLE		(1 << 31)
+#define PCI_CONF_WHERE(r)	((r) & 0xFC)
+#define PCI_CONF_BUS(b)		(((b) & 0xFF) << 16)
+#define PCI_CONF_DEVICE(d)	(((d) & 0x1F) << 11)
+#define PCI_CONF_FUNCTION(f)	(((f) & 0x07) << 8)
+
+#define PCI_IOSIZE_REG		(IO_ADDRESS(GEMINI_PCI_IO_BASE))
+#define PCI_PROT_REG		(IO_ADDRESS(GEMINI_PCI_IO_BASE) + 0x04)
+#define PCI_CTRL_REG		(IO_ADDRESS(GEMINI_PCI_IO_BASE) + 0x08)
+#define PCI_SOFTRST_REG		(IO_ADDRESS(GEMINI_PCI_IO_BASE) + 0x10)
+#define PCI_CONFIG_REG		(IO_ADDRESS(GEMINI_PCI_IO_BASE) + 0x28)
+#define PCI_DATA_REG		(IO_ADDRESS(GEMINI_PCI_IO_BASE) + 0x2C)
+
+
+static DEFINE_SPINLOCK(gemini_pci_lock);
+
+static struct resource gemini_pci_resource_io = {
+	.name	= "PCI I/O Space",
+	.start	= IO_ADDRESS(GEMINI_PCI_IO_BASE),
+	.end	= IO_ADDRESS(GEMINI_PCI_IO_BASE) + SZ_1M - 1,
+	.flags	= IORESOURCE_IO,
+};
+
+static struct resource gemini_pci_resource_mem = {
+	.name	= "PCI Memory Space",
+	.start	= GEMINI_PCI_MEM_BASE,
+	.end	= GEMINI_PCI_MEM_BASE + SZ_128M - 1,
+	.flags	= IORESOURCE_MEM,
+};
+
+static int gemini_pci_read_config(struct pci_bus *bus, unsigned int fn,
+				  int config, int size, u32 *value)
+{
+	unsigned long irq_flags;
+
+	spin_lock_irqsave(&gemini_pci_lock, irq_flags);
+
+	__raw_writel(PCI_CONF_BUS(bus->number) |
+			PCI_CONF_DEVICE(PCI_SLOT(fn)) |
+			PCI_CONF_FUNCTION(PCI_FUNC(fn)) |
+			PCI_CONF_WHERE(config) |
+			PCI_CONF_ENABLE,
+			PCI_CONFIG_REG);
+
+	*value = __raw_readl(PCI_DATA_REG);
+
+	if (size == 1)
+		*value = (*value >> (8 * (config & 3))) & 0xFF;
+	else if (size == 2)
+		*value = (*value >> (8 * (config & 3))) & 0xFFFF;
+
+	spin_unlock_irqrestore(&gemini_pci_lock, irq_flags);
+
+	dev_dbg(&bus->dev,
+		"[read]  slt: %.2d, fnc: %d, cnf: 0x%.2X, val (%d bytes): 0x%.8X\n",
+		PCI_SLOT(fn), PCI_FUNC(fn), config, size, *value);
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static int gemini_pci_write_config(struct pci_bus *bus, unsigned int fn,
+				   int config, int size, u32 value)
+{
+	unsigned long irq_flags = 0;
+	int ret = PCIBIOS_SUCCESSFUL;
+
+	dev_dbg(&bus->dev,
+		"[write] slt: %.2d, fnc: %d, cnf: 0x%.2X, val (%d bytes): 0x%.8X\n",
+		PCI_SLOT(fn), PCI_FUNC(fn), config, size, value);
+
+	spin_lock_irqsave(&gemini_pci_lock, irq_flags);
+
+	__raw_writel(PCI_CONF_BUS(bus->number) |
+			PCI_CONF_DEVICE(PCI_SLOT(fn)) |
+			PCI_CONF_FUNCTION(PCI_FUNC(fn)) |
+			PCI_CONF_WHERE(config) |
+			PCI_CONF_ENABLE,
+			PCI_CONFIG_REG);
+
+	switch (size) {
+	case 4:
+		__raw_writel(value, PCI_DATA_REG);
+		break;
+	case 2:
+		__raw_writew(value, PCI_DATA_REG + (config & 3));
+		break;
+	case 1:
+		__raw_writeb(value, PCI_DATA_REG + (config & 3));
+		break;
+	default:
+		ret = PCIBIOS_BAD_REGISTER_NUMBER;
+	}
+
+	spin_unlock_irqrestore(&gemini_pci_lock, irq_flags);
+
+	return ret;
+}
+
+static struct pci_ops gemini_pci_ops = {
+	.read	= gemini_pci_read_config,
+	.write	= gemini_pci_write_config,
+};
+
+static int __init gemini_pci_request_resources(struct pci_sys_data *sys)
+{
+	if (request_resource(&ioport_resource, &gemini_pci_resource_io))
+		goto bad_resources;
+	if (request_resource(&iomem_resource, &gemini_pci_resource_mem))
+		goto bad_resources;
+
+	sys->resource[0] = &gemini_pci_resource_io;
+	sys->resource[1] = &gemini_pci_resource_mem;
+	sys->resource[2] = 0;
+
+	return 0;
+
+bad_resources:
+	pr_err("Gemini PCI: request_resource() failed. "
+			"Abort PCI bus enumeration.\n");
+	return -1;
+}
+
+static int __init gemini_pci_setup(int nr, struct pci_sys_data *sys)
+{
+	unsigned int cmd;
+
+	if ((nr > 0) || gemini_pci_request_resources(sys))
+		return 0;
+
+	/* setup I/O space to 1MB size */
+	__raw_writel(GEMINI_PCI_IOSIZE_1M, PCI_IOSIZE_REG);
+
+	/* setup hostbridge */
+	cmd = __raw_readl(PCI_CTRL_REG);
+	cmd |= PCI_COMMAND_IO;
+	cmd |= PCI_COMMAND_MEMORY;
+	cmd |= PCI_COMMAND_MASTER;
+	__raw_writel(cmd, PCI_CTRL_REG);
+
+	return 1;
+}
+
+static struct pci_bus __init *gemini_pci_scan_bus(int nr,
+	struct pci_sys_data *sys)
+{
+	unsigned int reg = 0;
+	struct pci_bus *bus = 0;
+
+	bus = pci_scan_bus(nr, &gemini_pci_ops, sys);
+	if (bus) {
+		dev_dbg(&bus->dev, "setting up PCI DMA\n");
+		reg = (GEMINI_PCI_DMA_MEM1_BASE & GEMINI_PCI_DMA_MASK)
+			| (GEMINI_PCI_DMA_MEM1_SIZE << 16);
+		gemini_pci_write_config(bus, 0, GEMINI_PCI_MEM1_BASE_SIZE,
+					4, reg);
+		reg =	(GEMINI_PCI_DMA_MEM2_BASE & GEMINI_PCI_DMA_MASK)
+			| (GEMINI_PCI_DMA_MEM2_SIZE << 16);
+		gemini_pci_write_config(bus, 0, GEMINI_PCI_MEM2_BASE_SIZE,
+					4, reg);
+		reg = (GEMINI_PCI_DMA_MEM3_BASE & GEMINI_PCI_DMA_MASK)
+			| (GEMINI_PCI_DMA_MEM3_SIZE << 16);
+		gemini_pci_write_config(bus, 0, GEMINI_PCI_MEM3_BASE_SIZE,
+					4, reg);
+	}
+
+	return bus;
+}
+
+/* Should work with all boards based on original Storlink EVB */
+static int __init gemini_pci_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+{
+	if (slot < 9 || slot > 12)
+		return -1;
+
+	return PCI_IRQ_BASE + (((slot - 9) + (pin - 1)) & 0x3);
+}
+
+static struct hw_pci gemini_hw_pci __initdata = {
+	.nr_controllers	= 1,
+	.setup		= gemini_pci_setup,
+	.scan           = gemini_pci_scan_bus,
+	.swizzle	= pci_std_swizzle,
+	.map_irq	= gemini_pci_map_irq,
+};
+
+/* we need this for muxed PCI interrupts handling */
+static struct pci_bus bogus_pci_bus;
+
+static void gemini_pci_ack_irq(unsigned int irq)
+{
+	unsigned int reg;
+
+	gemini_pci_read_config(&bogus_pci_bus, 0, GEMINI_PCI_CTRL2, 4, &reg);
+	reg &= ~(0xF << PCI_CTRL2_INTSTS_OFFSET);
+	reg |= 1 << (irq - PCI_IRQ_BASE + PCI_CTRL2_INTSTS_OFFSET);
+	gemini_pci_write_config(&bogus_pci_bus, 0, GEMINI_PCI_CTRL2, 4, reg);
+}
+
+static void gemini_pci_mask_irq(unsigned int irq)
+{
+	unsigned int reg;
+
+	gemini_pci_read_config(&bogus_pci_bus, 0, GEMINI_PCI_CTRL2, 4, &reg);
+	reg &= ~((0xF << PCI_CTRL2_INTSTS_OFFSET)
+		| (1 << (irq - PCI_IRQ_BASE + PCI_CTRL2_INTMASK_OFFSET)));
+	gemini_pci_write_config(&bogus_pci_bus, 0, GEMINI_PCI_CTRL2, 4, reg);
+}
+
+static void gemini_pci_unmask_irq(unsigned int irq)
+{
+	unsigned int reg;
+
+	gemini_pci_read_config(&bogus_pci_bus, 0, GEMINI_PCI_CTRL2, 4, &reg);
+	reg &= ~(0xF << PCI_CTRL2_INTSTS_OFFSET);
+	reg |= 1 << (irq - PCI_IRQ_BASE + PCI_CTRL2_INTMASK_OFFSET);
+	gemini_pci_write_config(&bogus_pci_bus, 0, GEMINI_PCI_CTRL2, 4, reg);
+}
+
+static void gemini_pci_irq_handler(unsigned int irq, struct irq_desc *desc)
+{
+	unsigned int pci_irq_no, irq_stat, reg, i;
+
+	gemini_pci_read_config(&bogus_pci_bus, 0, GEMINI_PCI_CTRL2, 4, &reg);
+	irq_stat = reg >> PCI_CTRL2_INTSTS_OFFSET;
+
+	for (i = 0; i < 4; i++) {
+
+		if ((irq_stat & (1 << i)) == 0)
+			continue;
+
+		pci_irq_no = PCI_IRQ_BASE + i;
+
+		BUG_ON(!(irq_desc[pci_irq_no].handle_irq));
+		irq_desc[pci_irq_no].handle_irq(pci_irq_no,
+				&irq_desc[pci_irq_no]);
+	}
+}
+
+static struct irq_chip gemini_pci_irq_chip = {
+	.name = "PCI",
+	.ack = gemini_pci_ack_irq,
+	.mask = gemini_pci_mask_irq,
+	.unmask = gemini_pci_unmask_irq,
+};
+
+static int __init gemini_pci_init(void)
+{
+	int i;
+
+	for (i = 72; i <= 95; i++)
+		gpio_request(i, "PCI");
+
+	/* initialize our bogus bus */
+	dev_set_name(&bogus_pci_bus.dev, "PCI IRQ handler");
+	bogus_pci_bus.number = 0;
+
+	/* mask and clear all interrupts */
+	gemini_pci_write_config(&bogus_pci_bus, 0, GEMINI_PCI_CTRL2 + 2, 2,
+				0xF000);
+
+	for (i = PCI_IRQ_BASE; i < PCI_IRQ_BASE + 4; i++) {
+		set_irq_chip(i, &gemini_pci_irq_chip);
+		set_irq_handler(i, handle_level_irq);
+		set_irq_flags(i, IRQF_VALID);
+	}
+
+	set_irq_chained_handler(IRQ_PCI, gemini_pci_irq_handler);
+
+	pci_common_init(&gemini_hw_pci);
+
+	return 0;
+}
+
+subsys_initcall(gemini_pci_init);
-- 
1.7.3.2




More information about the linux-arm-kernel mailing list