summaryrefslogtreecommitdiff
path: root/arch/arm/mach-tegra/irq.c
diff options
context:
space:
mode:
authorGary King <gking@nvidia.com>2010-03-21 18:42:53 -0700
committerGary King <gking@nvidia.com>2010-03-23 08:38:26 -0800
commit5c1af346ca8d2d9e69fc55cf68455265d36d75a3 (patch)
tree35f41afe471c41fcd19b4ae7cec5d403d734ad4a /arch/arm/mach-tegra/irq.c
parent80a7ccc42463585796202053e0d591d137094045 (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.c436
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
}