diff --git a/arch/arm/mach-orion5x/Kconfig b/arch/arm/mach-orion5x/Kconfig index 6604fc6..40053b2 100644 --- a/arch/arm/mach-orion5x/Kconfig +++ b/arch/arm/mach-orion5x/Kconfig @@ -86,7 +86,7 @@ config MACH_WRT350N_V2 config MACH_TS78XX bool "Technologic Systems TS-78xx" - select PM + select SPARSE_IRQ help Say 'Y' here if you want your kernel to support the Technologic Systems TS-78xx platform. diff --git a/arch/arm/mach-orion5x/ts78xx-fpga.h b/arch/arm/mach-orion5x/ts78xx-fpga.h index 97c393d..e5b3762 100644 --- a/arch/arm/mach-orion5x/ts78xx-fpga.h +++ b/arch/arm/mach-orion5x/ts78xx-fpga.h @@ -27,6 +27,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 190673e..97cf180 100644 --- a/arch/arm/mach-orion5x/ts78xx-setup.c +++ b/arch/arm/mach-orion5x/ts78xx-setup.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -37,6 +38,9 @@ #define TS78XX_FPGA_REGS_VIRT_BASE 0xff900000 #define TS78XX_FPGA_REGS_SIZE SZ_1M +#define NR_TS78XX_FPGA_IRQS 32 +#define FPGA_IRQ(irq) (NR_IRQS + irq) + static struct ts78xx_fpga_data ts78xx_fpga = { .id = 0, .state = 1, @@ -62,6 +66,112 @@ void __init ts78xx_map_io(void) } /***************************************************************************** + * CPU Doorbell IRQ + ****************************************************************************/ +#define H2C_DOORBELL_REG (ORION5X_BRIDGE_VIRT_BASE | 0x400) +#define H2C_DOORBELL_MASK_REG (ORION5X_BRIDGE_VIRT_BASE | 0x404) +#define C2H_DOORBELL_REG (ORION5X_BRIDGE_VIRT_BASE | 0x408) +#define C2H_DOORBELL_MASK_REG (ORION5X_BRIDGE_VIRT_BASE | 0x40c) + +static void ts78xx_irq_ack(struct irq_data *data) +{ + unsigned int irq = data->irq; + u32 addr; + + addr = readl(H2C_DOORBELL_REG); + addr &= ~(1 << (irq - FPGA_IRQ(0))); + writel(addr, H2C_DOORBELL_REG); +} + +static void ts78xx_irq_mask(struct irq_data *data) +{ + void __iomem *fpgaaddr = irq_data_get_irq_chip_data(data); + unsigned int irq = 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(struct irq_data *data) +{ + void __iomem *fpgaaddr = irq_data_get_irq_chip_data(data); + unsigned int irq = 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", + .irq_ack = ts78xx_irq_ack, + .irq_mask = ts78xx_irq_mask, + .irq_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 >> 8) & 0xffffff) { + case TS7800_FPGA_MAGIC: + 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++) { + irq_set_chip(irq, &ts78xx_irq_chip); + irq_set_chip_data(irq, fpgaaddr); + irq_set_handler(irq, handle_level_irq); + irq_set_status_flags(irq, IRQ_LEVEL); + set_irq_flags(irq, IRQF_VALID); + } + + irq_set_chained_handler(IRQ_ORION5X_DOORBELL_H2C, ts78xx_irq); + + return 0; +} + +static void ts78xx_doorbell_irq_unload(void) +{ + unsigned int irq; + + irq_set_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); + irq_set_chip(irq, NULL); + irq_set_chip_data(irq, NULL); + } +} + +/***************************************************************************** * Ethernet ****************************************************************************/ static struct mv643xx_eth_platform_data ts78xx_eth_data = { @@ -376,6 +486,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,6 +505,7 @@ static void ts78xx_fpga_supports(void) case TS7800_REV_7: case TS7800_REV_8: case TS7800_REV_9: + ts78xx_fpga.supports.doorbell_irq.init = 1; ts78xx_fpga.supports.ts_rtc.present = 1; ts78xx_fpga.supports.ts_nand.present = 1; ts78xx_fpga.supports.ts_rng.present = 1; @@ -404,11 +516,13 @@ static void ts78xx_fpga_supports(void) case TS7800_FPGA_MAGIC: pr_warning("TS-7800 FPGA: unrecognized revision 0x%.2x\n", ts78xx_fpga.id & 0xff); + ts78xx_fpga.supports.doorbell_irq.init = 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.init = 0; ts78xx_fpga.supports.ts_rtc.present = 0; ts78xx_fpga.supports.ts_nand.present = 0; ts78xx_fpga.supports.ts_rng.present = 0; @@ -420,6 +534,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) { + pr_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) { @@ -452,6 +574,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) @@ -624,6 +748,7 @@ MACHINE_START(TS78XX, "Technologic Systems TS-78xx SBC") .atag_offset = 0x100, .init_machine = ts78xx_init, .map_io = ts78xx_map_io, + .nr_irqs = NR_IRQS + NR_TS78XX_FPGA_IRQS, .init_early = orion5x_init_early, .init_irq = orion5x_init_irq, .timer = &orion5x_timer,