orion5x: TS-78XX support for FPGA generated doorbell IRQ's
Subsystems (4)
Alexander Clouter
maintainerRFC 1/1 orion5x: TS-78XX support for FPGA generated doorbell IRQ's
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(-)
arch/arm/mach-orion5x/addr-map.c +0 -2
@@ -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))
arch/arm/mach-orion5x/include/mach/irqs.h +8 -2
@@ -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
arch/arm/mach-orion5x/include/mach/orion5x.h +10 -0
@@ -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) */
arch/arm/mach-orion5x/irq.c +2 -1
@@ -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 ;
arch/arm/mach-orion5x/ts78xx-fpga.h +2 -0
@@ -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;
arch/arm/mach-orion5x/ts78xx-setup.c +121 -0
162 lines not on this page · raw message
Signature · 1 line
1.7.2.3
Quoted 9 lines · Alexander Clouter
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_IRQSfrom 64 to 96 for the TS-7800 and calls upon the rather niftyset_irq_chained_handler()framework (the vendor of the board in their tree do hairy things in assembler).
The set_irq_chained_handler() is certainly the way to go.
As to NR_IRQS, it is best to leave it unchanged and provide the number of IRQs you require in the machine description record (see commit 354e6f72d6 for some details).
Quoted 8 lines
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_addrcould 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.
Looks sane to me.
Quoted 291 lines
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(-)arch/arm/mach-orion5x/addr-map.c +0
-2@@ -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))arch/arm/mach-orion5x/include/mach/irqs.h +8
-2@@ -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 arch/arm/mach-orion5x/include/mach/orion5x.h +10
-0@@ -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) */ arch/arm/mach-orion5x/irq.c +2
-1@@ -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 ;arch/arm/mach-orion5x/ts78xx-fpga.h +2
-0@@ -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;arch/arm/mach-orion5x/ts78xx-setup.c +121
-0@@ -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, ®,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 )Signature · 1 line
1.7.2.3