diff options
Diffstat (limited to 'arch/arm/plat-mxc/irq.c')
-rw-r--r-- | arch/arm/plat-mxc/irq.c | 144 |
1 files changed, 143 insertions, 1 deletions
diff --git a/arch/arm/plat-mxc/irq.c b/arch/arm/plat-mxc/irq.c index 778ddfe57d89..91a862933541 100644 --- a/arch/arm/plat-mxc/irq.c +++ b/arch/arm/plat-mxc/irq.c @@ -1,5 +1,5 @@ /* - * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved. * Copyright 2008 Juergen Beisert, kernel@pengutronix.de * * This program is free software; you can redistribute it and/or @@ -20,6 +20,7 @@ #include <linux/module.h> #include <linux/irq.h> #include <linux/io.h> +#include <linux/sysdev.h> #include <mach/common.h> #include <asm/mach/irq.h> #include <mach/hardware.h> @@ -46,6 +47,11 @@ void __iomem *avic_base; +#define IRQ_BIT(irq) (1 << (irq)) + +static uint32_t saved_wakeup_low, saved_wakeup_high; +static uint32_t suspend_wakeup_low, suspend_wakeup_high; + int imx_irq_set_priority(unsigned char irq, unsigned char prio) { #ifdef CONFIG_MXC_IRQ_PRIOR @@ -102,13 +108,122 @@ static void mxc_unmask_irq(unsigned int irq) __raw_writel(irq, avic_base + AVIC_INTENNUM); } +/*! + * Set interrupt number "irq" in the AVIC as a wake-up source. + * + * @param irq interrupt source number + * @param enable enable as wake-up if equal to non-zero + * disble as wake-up if equal to zero + * + * @return This function returns 0 on success. + */ +static int mxc_set_wake_irq(unsigned int irq, unsigned int enable) +{ + uint32_t *wakeup_intr; + uint32_t irq_bit; + + if (irq < 32) { + wakeup_intr = &suspend_wakeup_low; + irq_bit = IRQ_BIT(irq); + } else { + wakeup_intr = &suspend_wakeup_high; + irq_bit = IRQ_BIT(irq - 32); + } + + if (enable) { + *wakeup_intr |= irq_bit; + } else { + *wakeup_intr &= ~irq_bit; + } + + return 0; +} + static struct irq_chip mxc_avic_chip = { .ack = mxc_mask_irq, .mask = mxc_mask_irq, .unmask = mxc_unmask_irq, + .set_wake = mxc_set_wake_irq, +}; + +#ifdef CONFIG_PM +/*! + * This function puts the AVIC in low-power mode/state. + * All the interrupts that are enabled are first saved. + * Only those interrupts which registers as a wake source by calling + * enable_irq_wake are enabled. All other interrupts are disabled. + * + * @param dev the system device structure used to give information + * on AVIC to suspend + * @param mesg the power state the device is entering + * + * @return The function always returns 0. + */ +static int mxc_avic_suspend(struct sys_device *dev, pm_message_t mesg) +{ + saved_wakeup_high = __raw_readl(avic_base + AVIC_INTENABLEH); + saved_wakeup_low = __raw_readl(avic_base + AVIC_INTENABLEL); + + __raw_writel(suspend_wakeup_high, avic_base + AVIC_INTENABLEH); + __raw_writel(suspend_wakeup_low, avic_base + AVIC_INTENABLEL); + + return 0; +} + +/*! + * This function brings the AVIC back from low-power state. + * All the interrupts enabled before suspension are re-enabled from + * the saved information. + * + * @param dev the system device structure used to give information + * on AVIC to resume + * + * @return The function always returns 0. + */ +static int mxc_avic_resume(struct sys_device *dev) +{ + __raw_writel(saved_wakeup_high, avic_base + AVIC_INTENABLEH); + __raw_writel(saved_wakeup_low, avic_base + AVIC_INTENABLEL); + + return 0; +} + +#else +#define mxc_avic_suspend NULL +#define mxc_avic_resume NULL +#endif /* CONFIG_PM */ +/*! + * This structure contains pointers to the power management callback functions. + */ +static struct sysdev_class mxc_avic_sysclass = { + .name = "mxc_irq", + .suspend = mxc_avic_suspend, + .resume = mxc_avic_resume, +}; + +/*! + * This structure represents AVIC as a system device. + * System devices follow a slightly different driver model. + * They don't need to do dynammic driver binding, can't be probed, + * and don't reside on any type of peripheral bus. + * So, it is represented and treated a little differently. + */ +static struct sys_device mxc_avic_device = { + .id = 0, + .cls = &mxc_avic_sysclass, }; /* + * This function is used to get the AVIC Lo and Hi interrupts + * that are enabled as wake up sources to wake up the core from suspend + */ +void mxc_get_wake_irq(u32 *wake_src[]) +{ + *wake_src[0] = __raw_readl(avic_base + AVIC_INTENABLEL); + *wake_src[1] = __raw_readl(avic_base + AVIC_INTENABLEH); +} + +/* * This function initializes the AVIC hardware and disables all the * interrupts. It registers the interrupt enable and disable functions * to the kernel for each interrupt source. @@ -150,6 +265,33 @@ void __init mxc_init_irq(void __iomem *irqbase) init_FIQ(); #endif + if (MXC_INT_FORCE >= 32) + __raw_writel(1 << (MXC_INT_FORCE & 31), avic_base + AVIC_INTFRCH); + else if (MXC_INT_FORCE >= 0) + __raw_writel(1 << MXC_INT_FORCE, avic_base + AVIC_INTFRCL); + printk(KERN_INFO "MXC IRQ initialized\n"); } +/*! + * This function registers AVIC hardware as a system device. + * System devices will only be suspended with interrupts disabled, and + * after all other devices have been suspended. On resume, they will be + * resumed before any other devices, and also with interrupts disabled. + * + * @return This function returns 0 on success. + */ +static int __init mxc_avic_sysinit(void) +{ + int ret = 0; + + ret = sysdev_class_register(&mxc_avic_sysclass); + if (ret == 0) { + ret = sysdev_register(&mxc_avic_device); + } + + return ret; +} + +arch_initcall(mxc_avic_sysinit); + |