diff options
author | Gary King <gking@nvidia.com> | 2010-03-21 18:42:53 -0700 |
---|---|---|
committer | Gary King <gking@nvidia.com> | 2010-03-23 08:38:26 -0800 |
commit | 5c1af346ca8d2d9e69fc55cf68455265d36d75a3 (patch) | |
tree | 35f41afe471c41fcd19b4ae7cec5d403d734ad4a /arch/arm/mach-tegra/irq.c | |
parent | 80a7ccc42463585796202053e0d591d137094045 (diff) |
ARM tegra: clean up interrupt handling
reimplement interrupt controllers following the kernel coding
conventions
propogate set_irq_wake signal from gpio chip to the primary chip
use IRQ_WAKEUP status to control masking and unmasking of interrupts
when entering low-power modes; non-wakeup interrupts are disabled
using with disable_irq, and are re-enabled after wakeup with enable_irq
ODM kit wakeup pads are configured with enable_irq_wake during the
board initialization
move context save & restore for the GPIO and interrupt controllers out
of power-context-t2.c and into their respective drivers; no distinction
is made between LP0 and LP1 context currently; if there is enough of a
performance difference to warrant reintroducing it, this can be done at
a later time.
delete now-deprecated NVIDIA GPIO IRQ code
bug 656008
Change-Id: I68f98f2442c50a93a7ad9cdfef87b630e8c132a9
Reviewed-on: http://git-master/r/931
Tested-by: Gary King <gking@nvidia.com>
Reviewed-by: Venkata (Muni) Anda <vanda@nvidia.com>
Reviewed-by: Gary King <gking@nvidia.com>
Diffstat (limited to 'arch/arm/mach-tegra/irq.c')
-rw-r--r-- | arch/arm/mach-tegra/irq.c | 436 |
1 files changed, 197 insertions, 239 deletions
diff --git a/arch/arm/mach-tegra/irq.c b/arch/arm/mach-tegra/irq.c index d7bd6dc8f05a..b07f01c41b6d 100644 --- a/arch/arm/mach-tegra/irq.c +++ b/arch/arm/mach-tegra/irq.c @@ -21,301 +21,259 @@ */ #include <linux/init.h> -#include <linux/interrupt.h> +#include <linux/kernel.h> #include <linux/irq.h> +#include <linux/smp.h> #include <linux/io.h> -#include <asm/hardware/gic.h> - -#include "nvcommon.h" -#include "nvrm_init.h" -#include "nvrm_drf.h" -#include "nvrm_hardware_access.h" -#include "nvrm_module.h" -#include "nvrm_interrupt.h" -#include "mach/nvrm_linux.h" -#include "ap15/arictlr.h" -#include "ap15/arapb_misc.h" -#include "ap20/arfic_proc_if.h" -#include "ap20/arfic_dist.h" -#include "mach/board.h" - -#ifdef CONFIG_TEGRA_SYSTEM_DMA -extern void __init tegra_init_dma(void); /* irq_dma.c */ -#endif -extern void __init tegra_init_gpio(void); /* irq_gpio.c */ - -/* Exported symbol shared with NvOs defining the number of non-GPIO interrupts - * present on the system */ -NvU32 g_NvNumSocIrqs = 0; - -/* Causes the interrupt decoder to use the legacy portal player decoder */ -NvU32 g_NvUsePpiDecoder = 0; -void __iomem *g_NvIctlrBase = NULL; - -#ifdef CONFIG_CPU_AP15 -#define NV_MAX_IRQ_INSTANCES 3 -static volatile NvU8 *s_Controllers[NV_MAX_IRQ_INSTANCES] = {NULL}; - -static struct irq_chip s_NvIrqDispatch = { - .name = "tegra", -}; +#include <linux/interrupt.h> -static void NvPrivAckIrq(unsigned int irq) -{ - /* nothing to do */ -} +#include <asm/irq.h> +#include <asm/bitops.h> +#include <mach/platform.h> -static void NvPrivAp15MaskIrq(unsigned int irq) -{ - NV_WRITE32(s_Controllers[irq>>5] + ICTLR_CPU_IER_CLR_0, - 1 << (irq & 31)); -} +#ifdef CONFIG_ARCH_TEGRA_2x_SOC +#define INT_PPI_ADDRESS(_inst) (0x60004000ul + 0x100*(_inst)) +#define INT_APBDMA_ADDRESS 0x6000a000ul +#else +#error "interrupt controller addresses not defined" +#endif -static void NvPrivAp15UnmaskIrq(unsigned int irq) -{ - NV_WRITE32(s_Controllers[irq>>5] + ICTLR_CPU_IER_SET_0, - 1 << (irq & 31)); -} +#ifdef CONFIG_ARM_GIC +#ifdef CONFIG_ARCH_TEGRA_2x_SOC +#define ARM_PERIF_BASE 0x50040000ul +#define GIC_PROC_IF_BASE (ARM_PERIF_BASE + 0x100ul) +#define GIC_DIST_BASE (ARM_PERIF_BASE + 0x1000ul) +#else +#error "interrupt distributor address not defined" +#endif +#endif -static struct irq_chip* __init NvPrivAp15InitIrq(void) -{ - NvU32 i; - NvRmPhysAddr Phys; - NvU32 Len; - NvU32 Num; - - Num = NvRmModuleGetNumInstances(s_hRmGlobal,NvRmPrivModuleID_Interrupt); - if (Num > NV_MAX_IRQ_INSTANCES) { - printk("More interrupt controllers than static array size\n"); - while (1) { } - } - g_NvNumSocIrqs = Num*32; - - for (i=0; i<Num; i++) { - NvRmModuleGetBaseAddress(s_hRmGlobal, - NVRM_MODULE_ID(NvRmPrivModuleID_Interrupt,i), - &Phys, &Len); - if (NvRmPhysicalMemMap(Phys, Len, NVOS_MEM_READ_WRITE, - NvOsMemAttribute_Uncached, - (void **)&s_Controllers[i])!=NvSuccess) { - printk("Failed to get IRQ controller base address\n"); - while (1) { } - } - NV_WRITE32(s_Controllers[i] + ICTLR_CPU_IER_CLR_0, ~0UL); - NV_WRITE32(s_Controllers[i] + ICTLR_CPU_IEP_CLASS_0, 0); - } +#define ICTLR_CPU_IER_0 0x20 +#define ICTLR_CPU_IER_SET_0 0x24 +#define ICTLR_CPU_IER_CLR_0 0x28 +#define ICTLR_CPU_IEP_CLASS_0 0x2c +#define ICTLR_COP_IER_0 0x30 +#define ICTLR_COP_IER_SET_0 0x34 +#define ICTLR_COP_IER_CLR_0 0x38 +#define ICTLR_COP_IEP_CLASS_0 0x3c - s_NvIrqDispatch.mask = NvPrivAp15MaskIrq; - s_NvIrqDispatch.unmask = NvPrivAp15UnmaskIrq; - s_NvIrqDispatch.ack = NvPrivAckIrq; - s_NvIrqDispatch.set_type = NULL; - g_NvIctlrBase = (void __iomem*)s_Controllers[0]; - g_NvUsePpiDecoder = 1; - return &s_NvIrqDispatch; -} -#endif +#define APBDMA_IRQ_STA_CPU_0 0x14 +#define APBDMA_IRQ_MASK_SET_0 0x20 +#define APBDMA_IRQ_MASK_CLR_0 0x24 #ifdef CONFIG_ARM_GIC -#define NV_MAX_IRQ_INSTANCES 4 -static volatile NvU8 *s_Controllers[NV_MAX_IRQ_INSTANCES] = {NULL}; extern void gic_mask_irq(unsigned int); extern void gic_unmask_irq(unsigned int); extern void gic_ack_irq(unsigned int); extern void gic_set_cpu(unsigned int, const struct cpumask*); +extern void gic_dist_init(unsigned int, void __iomem *, unsigned int); +extern void gic_cpu_init(unsigned int, void __iomem *); +#else +#define gic_mask_irq(i) do { } while (0) +#define gic_unmask_irq(i) do { } while (0) +#define gic_ack_irq(i) do { } while (0) +#define gic_set_cpu NULL +#define gic_dist_init(i, p, s) do { } while (0) +#define gic_cpu_init(i, p) do { } while (0) +#endif -static struct irq_chip s_NvIrqDispatch = -{ - .name = "tegra2", +struct tegra_irq_chip { + unsigned int irq_start; + void __iomem *mmio; + /* context save/restore data for interrupts */ +#ifdef CONFIG_PM + u32 cpu_ier; + u32 cop_ier; +#endif }; -void NvPrivAp20MaskIrq(unsigned int irq) +static struct tegra_irq_chip tegra_chip[(INT_SYS_NR+INT_SYS_SZ-1)/INT_SYS_SZ]; + +static void tegra_mask(unsigned int irq) { + struct tegra_irq_chip *chip; gic_mask_irq(irq); - - irq -= 32; - - NV_WRITE32(s_Controllers[irq>>5] + ICTLR_CPU_IER_CLR_0, - 1 << (irq & 31)); - + irq -= INT_PRI_BASE; + chip = &tegra_chip[irq/INT_SYS_SZ]; + writel(1<<(irq&31), chip->mmio + ICTLR_CPU_IER_CLR_0); } -void NvPrivAp20UnmaskIrq(unsigned int irq) +static void tegra_unmask(unsigned int irq) { + struct tegra_irq_chip *chip; gic_unmask_irq(irq); - - irq -= 32; - - NV_WRITE32(s_Controllers[irq>>5] + ICTLR_CPU_IER_SET_0, - 1 << (irq & 31)); + irq -= INT_PRI_BASE; + chip = &tegra_chip[irq/INT_SYS_SZ]; + writel(1<<(irq&31), chip->mmio + ICTLR_CPU_IER_SET_0); } -static void NvPrivAp20AckIrq(unsigned int irq) +static void tegra_ack(unsigned int irq) { gic_ack_irq(irq); } -#ifdef CONFIG_SMP -static void NvPrivAp20SetCpu(unsigned int irq, const struct cpumask *mask_val) +#ifdef CONFIG_PM + +static int tegra_set_wake(unsigned int irq, unsigned int on) { - gic_set_cpu(irq, mask_val); + return 0; } -#endif -static void tegra_irq_register_module(NvRmModuleID Module, - struct irq_chip* pIrq) +void tegra_irq_resume(void) { - NvU32 i, Num; - Num = NvRmModuleGetNumInstances(s_hRmGlobal, Module); - for (i=0; i<Num; i++) { - NvU32 Ints; - Ints = NvRmGetIrqCountForLogicalInterrupt(s_hRmGlobal, - NVRM_MODULE_ID(Module,i)); - while (Ints) { - NvU32 Irq; - --Ints; - if (Module == NvRmPrivModuleID_Gpio) { - /* GPIOs are handled differently. Here we set the - * handler for the entire GPIO controller. Each GPIO - * controller has a set of GPIO ports which can - * programmed by the GPIO APIs. Check the NvRmGpio* - * APIs for more information - * - * To get the main IRQ line connected to the GPIO line, - * use an index value of 0xff. - */ - Irq = NvRmGetIrqForLogicalInterrupt(s_hRmGlobal, - NVRM_MODULE_ID(Module, i), 0xff); - } else { - Irq = NvRmGetIrqForLogicalInterrupt(s_hRmGlobal, - NVRM_MODULE_ID(Module, i), Ints); - } - - if (pIrq) { - if (set_irq_chip(Irq, pIrq)) { - panic("set_irq_chip %d failed\n", Irq); - } - set_irq_handler(Irq, handle_level_irq); - } - - set_irq_flags(Irq, IRQF_VALID); - } + unsigned long flags; + int i; + + local_irq_save(flags); + for (i=0; i<ARRAY_SIZE(tegra_chip); i++) { + struct tegra_irq_chip *chip = &tegra_chip[i]; + writel(chip->cpu_ier, chip->mmio + ICTLR_CPU_IER_SET_0); + writel(0, chip->mmio + ICTLR_CPU_IEP_CLASS_0); + writel(chip->cop_ier, chip->mmio + ICTLR_COP_IER_SET_0); + writel(0, chip->mmio + ICTLR_COP_IEP_CLASS_0); + } + local_irq_restore(flags); + + for (i=INT_PRI_BASE; i<INT_GPIO_BASE; i++) { + struct irq_desc *desc = irq_to_desc(i); + if (!desc || (desc->status & IRQ_WAKEUP)) continue; + enable_irq(i); } + + for (i=INT_APBDMA_BASE; i<INT_APBDMA_BASE+INT_APBDMA_NR; i++) + enable_irq(i); } -static struct irq_chip* __init NvPrivGicInitIrq(void) +void tegra_irq_suspend(void) { - NvRmPhysAddr Phys; - NvU32 Len; - NvU32 Num; - NvU32 i; - volatile NvU8 *pArm = NULL; - - Num = NvRmModuleGetNumInstances(s_hRmGlobal, - NvRmPrivModuleID_Interrupt); - g_NvNumSocIrqs = (Num+1)*32; - - NvRmModuleGetBaseAddress(s_hRmGlobal, - NVRM_MODULE_ID(NvRmPrivModuleID_ArmPerif,0), &Phys, &Len); - - if (NvRmPhysicalMemMap(Phys, Len, NVOS_MEM_READ_WRITE, - NvOsMemAttribute_Uncached, (void **)&pArm)!=NvSuccess) { - panic("Unable to map interrupt controller aperture\n"); - } + unsigned long flags; + int i; + + for (i=INT_APBDMA_BASE; i<INT_APBDMA_BASE+INT_APBDMA_NR; i++) + disable_irq(i); - gic_dist_init(0, - (void __iomem*)(pArm + FIC_DIST_DISTRIBUTOR_ENABLE_0), 29); - gic_cpu_init(0, (void __iomem*)(pArm + FIC_PROC_IF_CONTROL_0)); - - g_NvIctlrBase = (void __iomem*)(pArm + FIC_PROC_IF_CONTROL_0); - - /* gic_dist_init sets the IRQF_PROBE and IRQF_VALID flags for the entire - * range of distributor IRQs. The Tegra code undoes this, and then - * respecifies the desired flags for only valid IRQ numbers */ - for (i=32; i<g_NvNumSocIrqs; i++) - set_irq_flags(i, 0); - - /* Set up the legacy controller as well. Needed for flow controller */ - for (i=0; i<Num; i++) { - NvRmModuleGetBaseAddress(s_hRmGlobal, - NVRM_MODULE_ID(NvRmPrivModuleID_Interrupt,i), - &Phys, &Len); - if (NvRmPhysicalMemMap(Phys, Len, NVOS_MEM_READ_WRITE, - NvOsMemAttribute_Uncached, - (void **)&s_Controllers[i])!=NvSuccess) { - printk("failed to get IRQ controller base address\n"); - while (1) { } + for (i=INT_PRI_BASE; i<INT_GPIO_BASE; i++) { + struct irq_desc *desc= irq_to_desc(i); + if (!desc) continue; + if (desc->status & IRQ_WAKEUP) { + pr_debug("irq %d is wakeup\n", i); + continue; } - NV_WRITE32(s_Controllers[i] + ICTLR_CPU_IER_CLR_0, ~0UL); - NV_WRITE32(s_Controllers[i] + ICTLR_CPU_IEP_CLASS_0, 0); + disable_irq(i); } - //Set up the irq_chip for AP20 - s_NvIrqDispatch.mask = NvPrivAp20MaskIrq; - s_NvIrqDispatch.unmask = NvPrivAp20UnmaskIrq; - s_NvIrqDispatch.ack = NvPrivAp20AckIrq; -#ifdef CONFIG_SMP - s_NvIrqDispatch.set_affinity = NvPrivAp20SetCpu; + local_irq_save(flags); + for (i=0; i<ARRAY_SIZE(tegra_chip); i++) { + struct tegra_irq_chip *chip = &tegra_chip[i]; + chip->cpu_ier = readl(chip->mmio + ICTLR_CPU_IER_0); + chip->cop_ier = readl(chip->mmio + ICTLR_COP_IER_0); + writel(~0ul, chip->mmio + ICTLR_COP_IER_CLR_0); + } + local_irq_restore(flags); +} + #endif - return &s_NvIrqDispatch; + +#ifdef CONFIG_TEGRA_SYSTEM_DMA +struct apbdma_irq_chip { + unsigned int irq_start; + void __iomem *mmio; + spinlock_t lock; +}; + +static struct apbdma_irq_chip apbdma_chip; + +static void apbdma_ack(unsigned int irq) { } + +static void apbdma_mask(unsigned int irq) +{ + struct apbdma_irq_chip *chip = get_irq_chip_data(irq); + irq -= chip->irq_start; + writel(1<<irq, chip->mmio + APBDMA_IRQ_MASK_CLR_0); } -#endif +static void apbdma_unmask(unsigned int irq) +{ + struct apbdma_irq_chip *chip = get_irq_chip_data(irq); + irq -= chip->irq_start; + writel(1<<irq, chip->mmio + APBDMA_IRQ_MASK_SET_0); +} -void __init tegra_init_irq(void) +static void apbdma_cascade(unsigned int irq, struct irq_desc *desc) { - NvRmModuleID Module; - NvU32 ChipId; - volatile NvU8 *MiscRegionVirtual; - NvRmPhysAddr Phys; - NvU32 Len; - struct irq_chip* pIrq = NULL; - - NvRmModuleGetBaseAddress(s_hRmGlobal, - NVRM_MODULE_ID(NvRmModuleID_Misc,0), - &Phys, &Len); - - if (NvRmPhysicalMemMap(Phys, Len, NVOS_MEM_READ_WRITE, - NvOsMemAttribute_Uncached, - (void **)&MiscRegionVirtual)!=NvSuccess) { - printk("ERROR: Failed to get Misc controller base address\n"); - while (1) { }; + struct irq_chip *pri = get_irq_chip(irq); + struct apbdma_irq_chip *chip = get_irq_data(irq); + u32 reg, ch=0; + + pri->ack(irq); + spin_lock(&chip->lock); + reg = readl(chip->mmio + APBDMA_IRQ_STA_CPU_0); + if (reg) { + reg = __fls(reg); + writel(1<<reg, chip->mmio + APBDMA_IRQ_STA_CPU_0); + ch = chip->irq_start + reg; } + spin_unlock(&chip->lock); + if (ch) generic_handle_irq(ch); + pri->unmask(irq); +} - ChipId = NV_READ32(MiscRegionVirtual + APB_MISC_GP_HIDREV_0); - ChipId = NV_DRF_VAL(APB_MISC_GP, HIDREV, CHIPID, ChipId); +static struct irq_chip apbdma_irq = { + .name = "APBDMA", + .ack = apbdma_ack, + .mask = apbdma_mask, + .unmask = apbdma_unmask, +}; +#endif - if (ChipId==0x15 || ChipId==0x16) { -#ifdef CONFIG_CPU_AP15 - pIrq = NvPrivAp15InitIrq(); -#else - panic("Kernel built without APX 2600 support\n"); +static struct irq_chip tegra_irq = { + .name = "PPI", + .mask = tegra_mask, + .unmask = tegra_unmask, + .ack = tegra_ack, +#ifdef CONFIG_PM + .set_wake = tegra_set_wake, #endif - } - else if (ChipId==0x20) { -#ifdef CONFIG_ARM_GIC - pIrq = NvPrivGicInitIrq(); -#else - panic("Kernel built without AP2x support\n"); +#ifdef CONFIG_SMP + .set_affinity = gic_set_cpu, #endif - } - else { - panic("Unsupported chip ID: 0x%x\n", ChipId); - } - for (Module = NvRmModuleID_Cpu; Module<NvRmPrivModuleID_Num; Module++) { +}; - /* Skip interrupt registration for interrupt controllers */ - if ((Module == NvRmPrivModuleID_Interrupt) - || (Module == NvRmPrivModuleID_ArmInterruptctrl)) { - continue; - } +void __init tegra_init_irq(void) +{ + unsigned int i; - tegra_irq_register_module(Module, pIrq); + for (i=0; i<ARRAY_SIZE(tegra_chip); i++) { + tegra_chip[i].irq_start = INT_PRI_BASE + INT_SYS_SZ*i; + tegra_chip[i].mmio = IO_ADDRESS(INT_PPI_ADDRESS(i)); + writel(~0ul, tegra_chip[i].mmio + ICTLR_CPU_IER_CLR_0); + writel(0, tegra_chip[i].mmio + ICTLR_CPU_IEP_CLASS_0); + } + + gic_dist_init(0, IO_ADDRESS(GIC_DIST_BASE), 29); + gic_cpu_init(0, IO_ADDRESS(GIC_PROC_IF_BASE)); + + for (i=INT_PRI_BASE; i<INT_GPIO_BASE; i++) { + set_irq_chip(i, &tegra_irq); + set_irq_handler(i, handle_level_irq); + set_irq_flags(i, IRQF_VALID); } - #ifdef CONFIG_TEGRA_SYSTEM_DMA - tegra_init_dma(); + apbdma_chip.mmio = IO_ADDRESS(INT_APBDMA_ADDRESS); + spin_lock_init(&apbdma_chip.lock); + apbdma_chip.irq_start = INT_APBDMA_BASE; + + for (i=INT_APBDMA_BASE; i<INT_APBDMA_NR+INT_APBDMA_BASE; i++) { + set_irq_chip(i, &apbdma_irq); + set_irq_chip_data(i, &apbdma_chip); + set_irq_handler(i, handle_level_irq); + set_irq_flags(i, IRQF_VALID); + } + if (set_irq_data(INT_APB_DMA, &apbdma_chip)) + BUG(); + set_irq_chained_handler(INT_APB_DMA, apbdma_cascade); #endif } |