[RFC] orion5x: TS-78XX support for FPGA generated doorbell IRQ's
Nicolas Pitre
nico at fluxnic.net
Sun Feb 20 16:44:39 EST 2011
On Wed, 16 Feb 2011, Alexander Clouter wrote:
> 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 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).
> 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.
Looks sane to me.
> 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, ®, 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