From patchwork Wed Nov 17 12:16:19 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thomas Petazzoni X-Patchwork-Id: 332621 X-Patchwork-Delegate: paul@pwsan.com Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter1.kernel.org (8.14.4/8.14.3) with ESMTP id oAHCGdkt023353 for ; Wed, 17 Nov 2010 12:16:41 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S934606Ab0KQMQi (ORCPT ); Wed, 17 Nov 2010 07:16:38 -0500 Received: from mail.free-electrons.com ([88.190.12.23]:41282 "EHLO mail.free-electrons.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932871Ab0KQMQh (ORCPT ); Wed, 17 Nov 2010 07:16:37 -0500 Received: by mail.free-electrons.com (Postfix, from userid 106) id 6A33318F; Wed, 17 Nov 2010 13:16:36 +0100 (CET) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.free-electrons.com X-Spam-Level: X-Spam-Status: No, score=-2.9 required=5.0 tests=ALL_TRUSTED,BAYES_00 autolearn=ham version=3.3.1 Received: from localhost (humanoidz.org [82.247.183.72]) by mail.free-electrons.com (Postfix) with ESMTPSA id 1ED03132; Wed, 17 Nov 2010 13:16:28 +0100 (CET) From: Thomas Petazzoni To: linux-omap@vger.kernel.org Cc: Thomas Petazzoni , Kevin Hilman , "Cousson, Benoit" Subject: [PATCH] omap: prcm: switch to a chained IRQ handler mechanism Date: Wed, 17 Nov 2010 13:16:19 +0100 Message-Id: <1289996179-17698-1-git-send-email-thomas.petazzoni@free-electrons.com> X-Mailer: git-send-email 1.7.0.4 Sender: linux-omap-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-omap@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.3 (demeter1.kernel.org [140.211.167.41]); Wed, 17 Nov 2010 12:16:41 +0000 (UTC) diff --git a/arch/arm/mach-omap2/pm34xx.c b/arch/arm/mach-omap2/pm34xx.c index d068348..77a9a49 100644 --- a/arch/arm/mach-omap2/pm34xx.c +++ b/arch/arm/mach-omap2/pm34xx.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -243,7 +244,7 @@ static int prcm_clear_mod_irqs(s16 module, u8 regs) return c; } -static int _prcm_int_handle_wakeup(void) +static irqreturn_t _prcm_int_handle_wakeup(int irq, void *unused) { int c; @@ -255,64 +256,10 @@ static int _prcm_int_handle_wakeup(void) c += prcm_clear_mod_irqs(OMAP3430ES2_USBHOST_MOD, 1); } - return c; -} - -/* - * PRCM Interrupt Handler - * - * The PRM_IRQSTATUS_MPU register indicates if there are any pending - * interrupts from the PRCM for the MPU. These bits must be cleared in - * order to clear the PRCM interrupt. The PRCM interrupt handler is - * implemented to simply clear the PRM_IRQSTATUS_MPU in order to clear - * the PRCM interrupt. Please note that bit 0 of the PRM_IRQSTATUS_MPU - * register indicates that a wake-up event is pending for the MPU and - * this bit can only be cleared if the all the wake-up events latched - * in the various PM_WKST_x registers have been cleared. The interrupt - * handler is implemented using a do-while loop so that if a wake-up - * event occurred during the processing of the prcm interrupt handler - * (setting a bit in the corresponding PM_WKST_x register and thus - * preventing us from clearing bit 0 of the PRM_IRQSTATUS_MPU register) - * this would be handled. - */ -static irqreturn_t prcm_interrupt_handler (int irq, void *dev_id) -{ - u32 irqenable_mpu, irqstatus_mpu; - int c = 0; - - irqenable_mpu = prm_read_mod_reg(OCP_MOD, - OMAP3_PRM_IRQENABLE_MPU_OFFSET); - irqstatus_mpu = prm_read_mod_reg(OCP_MOD, - OMAP3_PRM_IRQSTATUS_MPU_OFFSET); - irqstatus_mpu &= irqenable_mpu; - - do { - if (irqstatus_mpu & (OMAP3430_WKUP_ST_MASK | - OMAP3430_IO_ST_MASK)) { - c = _prcm_int_handle_wakeup(); - - /* - * Is the MPU PRCM interrupt handler racing with the - * IVA2 PRCM interrupt handler ? - */ - WARN(c == 0, "prcm: WARNING: PRCM indicated MPU wakeup " - "but no wakeup sources are marked\n"); - } else { - /* XXX we need to expand our PRCM interrupt handler */ - WARN(1, "prcm: WARNING: PRCM interrupt received, but " - "no code to handle it (%08x)\n", irqstatus_mpu); - } - - prm_write_mod_reg(irqstatus_mpu, OCP_MOD, - OMAP3_PRM_IRQSTATUS_MPU_OFFSET); - - irqstatus_mpu = prm_read_mod_reg(OCP_MOD, - OMAP3_PRM_IRQSTATUS_MPU_OFFSET); - irqstatus_mpu &= irqenable_mpu; - - } while (irqstatus_mpu); - - return IRQ_HANDLED; + if (c) + return IRQ_HANDLED; + else + return IRQ_NONE; } static void restore_control_register(u32 val) @@ -998,11 +945,104 @@ void omap_push_sram_idle(void) save_secure_ram_context_sz); } +static void prcm_irq_ack(unsigned irq) +{ + int prcm_irq = irq - OMAP_PRCM_IRQ_BASE; + + prm_write_mod_reg((1 << prcm_irq), OCP_MOD, + OMAP3_PRM_IRQSTATUS_MPU_OFFSET); +} + +static void prcm_irq_mask(unsigned irq) +{ + int prcm_irq = irq - OMAP_PRCM_IRQ_BASE; + + prm_rmw_mod_reg_bits((1 << prcm_irq), 0, OCP_MOD, + OMAP3_PRM_IRQENABLE_MPU_OFFSET); +} + +static void prcm_irq_unmask(unsigned irq) +{ + int prcm_irq = irq - OMAP_PRCM_IRQ_BASE; + + prm_rmw_mod_reg_bits(0, (1 << prcm_irq), OCP_MOD, + OMAP3_PRM_IRQENABLE_MPU_OFFSET); +} + +static struct irq_chip prcm_irq_chip = { + .name = "PRCM", + .ack = prcm_irq_ack, + .mask = prcm_irq_mask, + .unmask = prcm_irq_unmask, +}; + +static u32 prcm_irq_pending(void) +{ + u32 irqenable_mpu, irqstatus_mpu; + + irqenable_mpu = prm_read_mod_reg(OCP_MOD, + OMAP3_PRM_IRQENABLE_MPU_OFFSET); + irqstatus_mpu = prm_read_mod_reg(OCP_MOD, + OMAP3_PRM_IRQSTATUS_MPU_OFFSET); + return irqstatus_mpu & irqenable_mpu; +} + +static void prcm_irq_handle_virtirqs(u32 pending) +{ + int virtirq; + + /* Loop on all currently pending irqs so that new irqs cannot + * starve previously pending irqs + */ + for (virtirq = 0; virtirq < 32; virtirq++) + if (pending & (1 << virtirq)) + generic_handle_irq(OMAP_PRCM_IRQ_BASE + virtirq); +} + +/* + * PRCM Interrupt Handler + * + * The PRM_IRQSTATUS_MPU register indicates if there are any pending + * interrupts from the PRCM for the MPU. These bits must be cleared in + * order to clear the PRCM interrupt. The PRCM interrupt handler is + * implemented to simply clear the PRM_IRQSTATUS_MPU in order to clear + * the PRCM interrupt. Please note that bit 0 of the PRM_IRQSTATUS_MPU + * register indicates that a wake-up event is pending for the MPU and + * this bit can only be cleared if the all the wake-up events latched + * in the various PM_WKST_x registers have been cleared. The interrupt + * handler is implemented using a do-while loop so that if a wake-up + * event occurred during the processing of the prcm interrupt handler + * (setting a bit in the corresponding PM_WKST_x register and thus + * preventing us from clearing bit 0 of the PRM_IRQSTATUS_MPU register) + * this would be handled. + */ +static void prcm_irq_handler(unsigned int irq, struct irq_desc *desc) +{ + u32 pending; + + /* Loop until all pending irqs are handled, since + * generic_handle_irq(), called by prcm_irq_handle_virtirqs() + * can cause new irqs to come + */ + while (1) { + desc->chip->ack(irq); + + pending = prcm_irq_pending(); + if (!pending) { + desc->chip->unmask(irq); + break; + } + + prcm_irq_handle_virtirqs(pending); + desc->chip->unmask(irq); + } +} + static int __init omap3_pm_init(void) { struct power_state *pwrst, *tmp; struct clockdomain *neon_clkdm, *per_clkdm, *mpu_clkdm, *core_clkdm; - int ret; + int ret, i; if (!cpu_is_omap34xx()) return -ENODEV; @@ -1013,19 +1053,34 @@ static int __init omap3_pm_init(void) * supervised mode for powerdomains */ prcm_setup_regs(); - ret = request_irq(INT_34XX_PRCM_MPU_IRQ, - (irq_handler_t)prcm_interrupt_handler, - IRQF_DISABLED, "prcm", NULL); + for (i = OMAP_PRCM_IRQ_BASE; i < OMAP_PRCM_IRQ_END; i++) { + set_irq_chip(i, &prcm_irq_chip); + set_irq_handler(i, handle_level_irq); + set_irq_flags(i, IRQF_VALID); + } + + set_irq_chained_handler(INT_34XX_PRCM_MPU_IRQ, prcm_irq_handler); + + ret = request_irq(INT_34XX_PRCM_WKUP_EN, _prcm_int_handle_wakeup, + IRQF_NO_SUSPEND, "prcm_wkup", NULL); if (ret) { printk(KERN_ERR "request_irq failed to register for 0x%x\n", - INT_34XX_PRCM_MPU_IRQ); + INT_34XX_PRCM_WKUP_EN); goto err1; } + ret = request_irq(INT_34XX_PRCM_IO_EN, _prcm_int_handle_wakeup, + IRQF_NO_SUSPEND, "prcm_io", NULL); + if (ret) { + printk(KERN_ERR "request_irq failed to register for 0x%x\n", + INT_34XX_PRCM_IO_EN); + goto err2; + } + ret = pwrdm_for_each(pwrdms_setup, NULL); if (ret) { printk(KERN_ERR "Failed to setup powerdomains\n"); - goto err2; + goto err3; } (void) clkdm_for_each(clkdms_setup, NULL); @@ -1033,7 +1088,7 @@ static int __init omap3_pm_init(void) mpu_pwrdm = pwrdm_lookup("mpu_pwrdm"); if (mpu_pwrdm == NULL) { printk(KERN_ERR "Failed to get mpu_pwrdm\n"); - goto err2; + goto err3; } neon_pwrdm = pwrdm_lookup("neon_pwrdm"); @@ -1080,7 +1135,9 @@ static int __init omap3_pm_init(void) err1: return ret; err2: - free_irq(INT_34XX_PRCM_MPU_IRQ, NULL); + free_irq(INT_34XX_PRCM_WKUP_EN, NULL); +err3: + free_irq(INT_34XX_PRCM_IO_EN, NULL); list_for_each_entry_safe(pwrst, tmp, &pwrst_list, node) { list_del(&pwrst->node); kfree(pwrst); diff --git a/arch/arm/plat-omap/include/plat/irqs.h b/arch/arm/plat-omap/include/plat/irqs.h index 65e20a6..6c5eb1c 100644 --- a/arch/arm/plat-omap/include/plat/irqs.h +++ b/arch/arm/plat-omap/include/plat/irqs.h @@ -363,7 +363,43 @@ #define OMAP_MAX_GPIO_LINES 192 #define IH_GPIO_BASE (128 + IH2_BASE) #define IH_MPUIO_BASE (OMAP_MAX_GPIO_LINES + IH_GPIO_BASE) -#define OMAP_IRQ_END (IH_MPUIO_BASE + 16) +#define OMAP_MPUIO_IRQ_END (IH_MPUIO_BASE + 16) + +/* 32 IRQs for the PRCM */ +#define OMAP_PRCM_IRQ_BASE (OMAP_MPUIO_IRQ_END) +#define INT_34XX_PRCM_WKUP_EN (OMAP_PRCM_IRQ_BASE + 0) +#define INT_34XX_PRCM_EVGENON_EN (OMAP_PRCM_IRQ_BASE + 2) +#define INT_34XX_PRCM_EVGENOFF_EN (OMAP_PRCM_IRQ_BASE + 3) +#define INT_34XX_PRCM_TRANSITION_EN (OMAP_PRCM_IRQ_BASE + 4) +#define INT_34XX_PRCM_CORE_DPLL_RECAL_EN (OMAP_PRCM_IRQ_BASE + 5) +#define INT_34XX_PRCM_PERIPH_DPLL_RECAL_EN (OMAP_PRCM_IRQ_BASE + 6) +#define INT_34XX_PRCM_MPU_DPLL_RECAL_EN_EN (OMAP_PRCM_IRQ_BASE + 7) +#define INT_34XX_PRCM_IVA2_DPLL_RECAL_EN (OMAP_PRCM_IRQ_BASE + 8) +#define INT_34XX_PRCM_IO_EN (OMAP_PRCM_IRQ_BASE + 9) +#define INT_34XX_PRCM_VP1_OPPCHANGEDONE_EN (OMAP_PRCM_IRQ_BASE + 10) +#define INT_34XX_PRCM_VP1_MINVDD_EN (OMAP_PRCM_IRQ_BASE + 11) +#define INT_34XX_PRCM_VP1_MAXVDD_EN (OMAP_PRCM_IRQ_BASE + 12) +#define INT_34XX_PRCM_VP1_NOSMPSACK_EN (OMAP_PRCM_IRQ_BASE + 13) +#define INT_34XX_PRCM_VP1_EQVALUE_EN (OMAP_PRCM_IRQ_BASE + 14) +#define INT_34XX_PRCM_VP1_TRANXDONE_EN (OMAP_PRCM_IRQ_BASE + 15) +#define INT_34XX_PRCM_VP2_OPPCHANGEDONE_EN (OMAP_PRCM_IRQ_BASE + 16) +#define INT_34XX_PRCM_VP2_MINVDD_EN (OMAP_PRCM_IRQ_BASE + 17) +#define INT_34XX_PRCM_VP2_MAXVDD_EN (OMAP_PRCM_IRQ_BASE + 18) +#define INT_34XX_PRCM_VP2_NOSMPSACK_EN (OMAP_PRCM_IRQ_BASE + 19) +#define INT_34XX_PRCM_VP2_EQVALUE_EN (OMAP_PRCM_IRQ_BASE + 20) +#define INT_34XX_PRCM_VP2_TRANXDONE_EN (OMAP_PRCM_IRQ_BASE + 21) +#define INT_34XX_PRCM_VC_SAERR_EN (OMAP_PRCM_IRQ_BASE + 22) +#define INT_34XX_PRCM_VC_RAERR_EN (OMAP_PRCM_IRQ_BASE + 23) +#define INT_34XX_PRCM_VC_TIMEOUT_ERR_EN (OMAP_PRCM_IRQ_BASE + 24) +#define INT_34XX_PRCM_SND_PERIPH_RECAL_EN (OMAP_PRCM_IRQ_BASE + 25) +#define INT_36XX_PRCM_ABB_LDO_TRANXDONE_EN (OMAP_PRCM_IRQ_BASE + 26) +#define INT_36XX_PRCM_VC_VP1_ACK_EN (OMAP_PRCM_IRQ_BASE + 27) +#define INT_36XX_PRCM_VC_BYPASS_ACK_EN (OMAP_PRCM_IRQ_BASE + 28) +#define OMAP_PRCM_NR_IRQS 32 +#define OMAP_PRCM_IRQ_END (OMAP_PRCM_IRQ_BASE + \ + OMAP_PRCM_NR_IRQS) + +#define OMAP_IRQ_END (OMAP_PRCM_IRQ_END) /* External FPGA handles interrupts on Innovator boards */ #define OMAP_FPGA_IRQ_BASE (OMAP_IRQ_END)