[RFC] orion5x: TS-78XX support for FPGA generated doorbell IRQ's

Alexander Clouter alex at digriz.org.uk
Wed Feb 16 17:45:22 EST 2011


Hi,

The TS-7800 board[1] FPGA can generate interrupts for devices connected 
to the PC/104 header.  This works by triggering the doorbell IRQ, which 
seems typically to not be used on other orion5x boards seen in the wild.

The following patch bumps NR_IRQS from 64 to 96 for the TS-7800 and 
calls upon the rather nifty set_irq_chained_handler() framework (the 
vendor of the board in their tree do hairy things in assembler).

The code has been tested with an Ethernet card driven off ax88796.c, but 
I am looking for feedback if this is the Right Way(tm) to do this, and 
if there is a better way.

Bear in mind that fpga_addr could vary if people wish to put their own 
bitstream on the FPGA and generate IRQ's in a similar fashion; which is 
why there is a switch statement present[2] as the VHDL coder might want 
to use different addresses.

Cheers

[1] http://www.embeddedarm.com/products/board-detail.php?product=TS-7800
[2] I am planning at some stage to rework all the FPGA related bits to 
	use either MFD or even the new ARM Device Tree framework

---
 arch/arm/mach-orion5x/addr-map.c             |    2 -
 arch/arm/mach-orion5x/include/mach/irqs.h    |   10 ++-
 arch/arm/mach-orion5x/include/mach/orion5x.h |   10 ++
 arch/arm/mach-orion5x/irq.c                  |    3 +-
 arch/arm/mach-orion5x/ts78xx-fpga.h          |    2 +
 arch/arm/mach-orion5x/ts78xx-setup.c         |  121 ++++++++++++++++++++++++++
 6 files changed, 143 insertions(+), 5 deletions(-)

diff --git a/arch/arm/mach-orion5x/addr-map.c b/arch/arm/mach-orion5x/addr-map.c
index 1a5d6a0..1fa730c 100644
--- a/arch/arm/mach-orion5x/addr-map.c
+++ b/arch/arm/mach-orion5x/addr-map.c
@@ -60,14 +60,12 @@
 /*
  * Helpers to get DDR bank info
  */
-#define ORION5X_DDR_REG(x)	(ORION5X_DDR_VIRT_BASE | (x))
 #define DDR_BASE_CS(n)		ORION5X_DDR_REG(0x1500 + ((n) << 3))
 #define DDR_SIZE_CS(n)		ORION5X_DDR_REG(0x1504 + ((n) << 3))
 
 /*
  * CPU Address Decode Windows registers
  */
-#define ORION5X_BRIDGE_REG(x)	(ORION5X_BRIDGE_VIRT_BASE | (x))
 #define CPU_WIN_CTRL(n)		ORION5X_BRIDGE_REG(0x000 | ((n) << 4))
 #define CPU_WIN_BASE(n)		ORION5X_BRIDGE_REG(0x004 | ((n) << 4))
 #define CPU_WIN_REMAP_LO(n)	ORION5X_BRIDGE_REG(0x008 | ((n) << 4))
diff --git a/arch/arm/mach-orion5x/include/mach/irqs.h b/arch/arm/mach-orion5x/include/mach/irqs.h
index a6fa9d8..677b594 100644
--- a/arch/arm/mach-orion5x/include/mach/irqs.h
+++ b/arch/arm/mach-orion5x/include/mach/irqs.h
@@ -54,7 +54,13 @@
 #define IRQ_ORION5X_GPIO_START	32
 #define NR_GPIO_IRQS		32
 
-#define NR_IRQS			(IRQ_ORION5X_GPIO_START + NR_GPIO_IRQS)
-
+#ifdef CONFIG_MACH_TS78XX
+#	define NR_TS78XX_FPGA_IRQS	32
+#	define FPGA_IRQ(irq)		(IRQ_ORION5X_GPIO_START + NR_GPIO_IRQS + irq)
+#
+#	define NR_IRQS			(IRQ_ORION5X_GPIO_START + NR_GPIO_IRQS + NR_TS78XX_FPGA_IRQS)
+#else
+#	define NR_IRQS			(IRQ_ORION5X_GPIO_START + NR_GPIO_IRQS)
+#endif
 
 #endif
diff --git a/arch/arm/mach-orion5x/include/mach/orion5x.h b/arch/arm/mach-orion5x/include/mach/orion5x.h
index 2d87665..5a1711b 100644
--- a/arch/arm/mach-orion5x/include/mach/orion5x.h
+++ b/arch/arm/mach-orion5x/include/mach/orion5x.h
@@ -69,6 +69,7 @@
  ******************************************************************************/
 
 #define ORION5X_DDR_VIRT_BASE		(ORION5X_REGS_VIRT_BASE | 0x00000)
+#define ORION5X_DDR_REG(x)		(ORION5X_DDR_VIRT_BASE | (x))
 
 #define ORION5X_DEV_BUS_PHYS_BASE	(ORION5X_REGS_PHYS_BASE | 0x10000)
 #define ORION5X_DEV_BUS_VIRT_BASE	(ORION5X_REGS_VIRT_BASE | 0x10000)
@@ -81,6 +82,7 @@
 #define  UART1_VIRT_BASE		(ORION5X_DEV_BUS_VIRT_BASE | 0x2100)
 
 #define ORION5X_BRIDGE_VIRT_BASE	(ORION5X_REGS_VIRT_BASE | 0x20000)
+#define ORION5X_BRIDGE_REG(x)		(ORION5X_BRIDGE_VIRT_BASE | (x))
 
 #define ORION5X_PCI_VIRT_BASE		(ORION5X_REGS_VIRT_BASE | 0x30000)
 
@@ -120,6 +122,14 @@
 #define DEV_BUS_INT_MASK	ORION5X_DEV_BUS_REG(0x4d4)
 
 /*******************************************************************************
+ * CPU Doorbell Registers
+ ******************************************************************************/
+#define H2C_DOORBELL_REG	ORION5X_BRIDGE_REG(0x400)
+#define H2C_DOORBELL_MASK_REG	ORION5X_BRIDGE_REG(0x404)
+#define C2H_DOORBELL_REG	ORION5X_BRIDGE_REG(0x408)
+#define C2H_DOORBELL_MASK_REG	ORION5X_BRIDGE_REG(0x40c)
+
+/*******************************************************************************
  * Supported Devices & Revisions
  ******************************************************************************/
 /* Orion-1 (88F5181) and Orion-VoIP (88F5181L) */
diff --git a/arch/arm/mach-orion5x/irq.c b/arch/arm/mach-orion5x/irq.c
index d7512b9..d5ba5b4 100644
--- a/arch/arm/mach-orion5x/irq.c
+++ b/arch/arm/mach-orion5x/irq.c
@@ -43,7 +43,8 @@ void __init orion5x_init_irq(void)
 	 * Register chained level handlers for GPIO IRQs by default.
 	 * User can use set_type() if he wants to use edge types handlers.
 	 */
-	for (i = IRQ_ORION5X_GPIO_START; i < NR_IRQS; i++) {
+	for (i = IRQ_ORION5X_GPIO_START;
+			i < IRQ_ORION5X_GPIO_START + NR_GPIO_IRQS; i++) {
 		set_irq_chip(i, &orion_gpio_irq_chip);
 		set_irq_handler(i, handle_level_irq);
 		irq_desc[i].status |= IRQ_LEVEL;
diff --git a/arch/arm/mach-orion5x/ts78xx-fpga.h b/arch/arm/mach-orion5x/ts78xx-fpga.h
index 791f754..8bb0331 100644
--- a/arch/arm/mach-orion5x/ts78xx-fpga.h
+++ b/arch/arm/mach-orion5x/ts78xx-fpga.h
@@ -26,6 +26,8 @@ struct fpga_device {
 };
 
 struct fpga_devices {
+	struct fpga_device	doorbell_irq;
+
 	/* Technologic Systems */
 	struct fpga_device 	ts_rtc;
 	struct fpga_device 	ts_nand;
diff --git a/arch/arm/mach-orion5x/ts78xx-setup.c b/arch/arm/mach-orion5x/ts78xx-setup.c
index d3e29f8..592773a 100644
--- a/arch/arm/mach-orion5x/ts78xx-setup.c
+++ b/arch/arm/mach-orion5x/ts78xx-setup.c
@@ -22,6 +22,8 @@
 #include <asm/mach/arch.h>
 #include <asm/mach/map.h>
 #include <mach/orion5x.h>
+#include <linux/irq.h>
+#include <mach/bridge-regs.h>
 #include "common.h"
 #include "mpp.h"
 #include "ts78xx-fpga.h"
@@ -62,6 +64,112 @@ void __init ts78xx_map_io(void)
 }
 
 /*****************************************************************************
+ * IRQ
+ ****************************************************************************/
+static void ts78xx_irq_ack(u32 irq)
+{
+	u32 addr;
+
+	addr = readl(H2C_DOORBELL_REG);
+	addr &= ~(1 << (irq - FPGA_IRQ(0)));
+	writel(addr, H2C_DOORBELL_REG);
+}
+
+static void ts78xx_irq_mask(u32 irq)
+{
+	void __iomem *fpgaaddr = get_irq_chip_data(irq);
+	unsigned long reg;
+
+	reg = readl(H2C_DOORBELL_MASK_REG);
+	reg &= ~(1 << (irq - FPGA_IRQ(0)));
+	writel(reg, H2C_DOORBELL_MASK_REG);
+
+	reg = readl(fpgaaddr);
+	reg &= ~(1 << (irq - FPGA_IRQ(0)));
+	writel(reg, fpgaaddr);
+}
+
+static void ts78xx_irq_unmask(u32 irq)
+{
+	void __iomem *fpgaaddr = get_irq_chip_data(irq);
+	unsigned long reg;
+
+	reg = readl(H2C_DOORBELL_MASK_REG);
+	reg |= 1 << (irq - FPGA_IRQ(0));
+	writel(reg, H2C_DOORBELL_MASK_REG);
+
+	reg = readl(H2C_DOORBELL_REG);
+	reg &= ~(1 << (irq - FPGA_IRQ(0)));
+	writel(reg, H2C_DOORBELL_REG);
+
+	reg = readl(fpgaaddr);
+	reg |= 1 << (irq - FPGA_IRQ(0));
+	writel(reg, fpgaaddr);
+}
+
+static struct irq_chip ts78xx_irq_chip = {
+	.name		= "ts78xx_irq",
+	.ack		= ts78xx_irq_ack,
+	.mask		= ts78xx_irq_mask,
+	.unmask		= ts78xx_irq_unmask,
+};
+
+static void ts78xx_irq(unsigned int irq, struct irq_desc *desc)
+{
+	unsigned long reg = readl(H2C_DOORBELL_REG);
+
+	for_each_set_bit(irq, &reg, 32)
+		generic_handle_irq(FPGA_IRQ(irq));
+}
+
+static int ts78xx_doorbell_irq_load(void)
+{
+	void __iomem *fpgaaddr;
+	unsigned int irq;
+
+	switch (ts78xx_fpga.id) {
+	case TS7800_REV_1:
+	case TS7800_REV_2:
+	case TS7800_REV_3:
+	case TS7800_REV_4:
+	case TS7800_REV_5:
+	case TS7800_REV_6:
+	case TS7800_REV_7:
+	case TS7800_REV_8:
+	case TS7800_REV_9:
+		fpgaaddr = (void __iomem *)(TS78XX_FPGA_REGS_VIRT_BASE | 0x204);
+		break;
+	default:
+		return -ENODEV;
+	}
+
+	for (irq = FPGA_IRQ(0); irq < FPGA_IRQ(NR_TS78XX_FPGA_IRQS); irq++) {
+		set_irq_chip(irq, &ts78xx_irq_chip);
+		set_irq_chip_data(irq, fpgaaddr);
+		set_irq_handler(irq, handle_level_irq);
+		irq_desc[irq].status |= IRQ_LEVEL;
+		set_irq_flags(irq, IRQF_VALID);
+	}
+
+	set_irq_chained_handler(IRQ_ORION5X_DOORBELL_H2C, ts78xx_irq);
+
+	return 0;
+}
+
+static void ts78xx_doorbell_irq_unload(void)
+{
+	unsigned int irq;
+
+	set_irq_chained_handler(IRQ_ORION5X_DOORBELL_H2C, NULL);
+
+	for (irq = FPGA_IRQ(0); irq < FPGA_IRQ(NR_TS78XX_FPGA_IRQS); irq++) {
+		set_irq_flags(irq, 0);
+		set_irq_chip(irq, NULL);
+		set_irq_chip_data(irq, NULL);
+	}
+}
+
+/*****************************************************************************
  * Ethernet
  ****************************************************************************/
 static struct mv643xx_eth_platform_data ts78xx_eth_data = {
@@ -376,6 +484,7 @@ static void ts78xx_ts_rng_unload(void)
  ****************************************************************************/
 static void ts78xx_fpga_devices_zero_init(void)
 {
+	ts78xx_fpga.supports.doorbell_irq.init = 0;
 	ts78xx_fpga.supports.ts_rtc.init = 0;
 	ts78xx_fpga.supports.ts_nand.init = 0;
 	ts78xx_fpga.supports.ts_rng.init = 0;
@@ -394,11 +503,13 @@ static void ts78xx_fpga_supports(void)
 	case TS7800_REV_7:
 	case TS7800_REV_8:
 	case TS7800_REV_9:
+		ts78xx_fpga.supports.doorbell_irq.present = 1;
 		ts78xx_fpga.supports.ts_rtc.present = 1;
 		ts78xx_fpga.supports.ts_nand.present = 1;
 		ts78xx_fpga.supports.ts_rng.present = 1;
 		break;
 	default:
+		ts78xx_fpga.supports.doorbell_irq.present = 0;
 		ts78xx_fpga.supports.ts_rtc.present = 0;
 		ts78xx_fpga.supports.ts_nand.present = 0;
 		ts78xx_fpga.supports.ts_rng.present = 0;
@@ -409,6 +520,14 @@ static int ts78xx_fpga_load_devices(void)
 {
 	int tmp, ret = 0;
 
+	if (ts78xx_fpga.supports.doorbell_irq.present == 1) {
+		tmp = ts78xx_doorbell_irq_load();
+		if (tmp) {
+			printk(KERN_INFO "TS-78xx: Doorbell IRQs not registered\n");
+			ts78xx_fpga.supports.doorbell_irq.present = 0;
+		}
+		ret |= tmp;
+	}
 	if (ts78xx_fpga.supports.ts_rtc.present == 1) {
 		tmp = ts78xx_ts_rtc_load();
 		if (tmp) {
@@ -441,6 +560,8 @@ static int ts78xx_fpga_unload_devices(void)
 {
 	int ret = 0;
 
+	if (ts78xx_fpga.supports.doorbell_irq.present == 1)
+		ts78xx_doorbell_irq_unload();
 	if (ts78xx_fpga.supports.ts_rtc.present == 1)
 		ts78xx_ts_rtc_unload();
 	if (ts78xx_fpga.supports.ts_nand.present == 1)
-- 
1.7.2.3



More information about the linux-arm-kernel mailing list