Hi Tero, Tero Kristo <t-kristo@xxxxxx> writes: > Introduce a chained interrupt handler mechanism for the PRCM > interrupt, so that individual PRCM event can cleanly be handled by > handlers in separate drivers. We do this by introducing PRCM event > names, which are then matched to the particular PRCM interrupt bit > depending on the specific OMAP SoC being used. > arch/arm/mach-omap2/prcm.c implements the chained interrupt mechanism > itself, with individual PRCM events for OMAP3 and OMAP4 being > described in arch/arm/mach-omap2/prcm3xxx.c and > arch/arm/mach-omap2/prcm4xxx.c respectively. At initialization time, > the set of PRCM events is filtered against the SoC on which we are > running, keeping only the ones that are actually useful. All the logic > is written to be generic with regard to OMAP3/OMAP4, even though OMAP3 > has single PRCM event registers and OMAP4 has two PRCM event > registers. > > The wakeup and I/O PRCM events are now handled as two separate > interrupts, and their handler is registered with IRQF_NO_SUSPEND, > otherwise the IRQ gets disabled during suspend, which prevents resume. > > Patch tested on OMAP4 blaze board, no testing done on OMAP3. Is this still true? > Signed-off-by: Tero Kristo <t-kristo@xxxxxx> > Cc: Thomas Petazzoni <thomas.petazzoni@xxxxxxxxxxxxxxxxxx> > Cc: Avinash.H.M <avinashhm@xxxxxx> > Cc: Kevin Hilman <khilman@xxxxxx> > Cc: Cousson, Benoit <b-cousson@xxxxxx> Thanks for working on this. So far, I've only had time for a cosmetic review of this code. I should have some more time later this week for a more detailed review. First some general comments: This series introduces a few section mismatch warnings. Please build with CONFIG_DEBUG_SECTION_MISMATCH=y for details and fixup. > --- > arch/arm/mach-omap2/Makefile | 4 + > arch/arm/mach-omap2/pm34xx.c | 104 ++++++------------ > arch/arm/mach-omap2/prcm.c | 187 ++++++++++++++++++++++++++++++++ > arch/arm/mach-omap2/prcm3xxx.c | 117 ++++++++++++++++++++ > arch/arm/mach-omap2/prcm4xxx.c | 146 +++++++++++++++++++++++++ > arch/arm/plat-omap/include/plat/irqs.h | 9 ++- > arch/arm/plat-omap/include/plat/prcm.h | 45 ++++++++ > 7 files changed, 541 insertions(+), 71 deletions(-) > create mode 100644 arch/arm/mach-omap2/prcm3xxx.c > create mode 100644 arch/arm/mach-omap2/prcm4xxx.c > > diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile > index 5024064..339d2d4 100644 > --- a/arch/arm/mach-omap2/Makefile > +++ b/arch/arm/mach-omap2/Makefile > @@ -39,6 +39,10 @@ AFLAGS_sram242x.o :=-Wa,-march=armv6 > AFLAGS_sram243x.o :=-Wa,-march=armv6 > AFLAGS_sram34xx.o :=-Wa,-march=armv7-a > > +# PRCM > +obj-$(CONFIG_ARCH_OMAP3) += prcm3xxx.o > +obj-$(CONFIG_ARCH_OMAP4) += prcm4xxx.o > + > # Pin multiplexing > obj-$(CONFIG_SOC_OMAP2420) += mux2420.o > obj-$(CONFIG_SOC_OMAP2430) += mux2430.o > diff --git a/arch/arm/mach-omap2/pm34xx.c b/arch/arm/mach-omap2/pm34xx.c > index 96a7624..adab4d5 100644 > --- a/arch/arm/mach-omap2/pm34xx.c > +++ b/arch/arm/mach-omap2/pm34xx.c > @@ -240,7 +240,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; > > @@ -252,64 +252,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 = omap2_prm_read_mod_reg(OCP_MOD, > - OMAP3_PRM_IRQENABLE_MPU_OFFSET); > - irqstatus_mpu = omap2_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); > - } > - > - omap2_prm_write_mod_reg(irqstatus_mpu, OCP_MOD, > - OMAP3_PRM_IRQSTATUS_MPU_OFFSET); > - > - irqstatus_mpu = omap2_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; > } > > /* Function to restore the table entry that was modified for enabling MMU */ > @@ -880,20 +826,32 @@ static int __init omap3_pm_init(void) > /* XXX prcm_setup_regs needs to be before enabling hw > * supervised mode for powerdomains */ > prcm_setup_regs(); > + ret = omap_prcm_irq_init(); > + if (ret) { > + pr_err("omap_prcm_irq_init() failed with %d\n", ret); > + goto err_prcm_irq_init; > + } > + > + ret = request_irq(omap_prcm_event_to_irq("wkup"), > + _prcm_int_handle_wakeup, > + IRQF_NO_SUSPEND, "prcm_wkup", NULL); Do you need to register a handler for this if all the handler does is 'return IRQ_HANDLED' ? Since you're now clearing all the events in every idle path, this doesn't seem to be necessary. > + if (ret) { > + pr_err("request_irq failed to register for PRCM wakeup\n"); > + goto err_prcm_irq_wkup; > + } > > - ret = request_irq(INT_34XX_PRCM_MPU_IRQ, > - (irq_handler_t)prcm_interrupt_handler, > - IRQF_DISABLED, "prcm", NULL); > + ret = request_irq(omap_prcm_event_to_irq("io"), > + _prcm_int_handle_wakeup, > + IRQF_NO_SUSPEND, "prcm_io", NULL); ditto > if (ret) { > - printk(KERN_ERR "request_irq failed to register for 0x%x\n", > - INT_34XX_PRCM_MPU_IRQ); > - goto err1; > + pr_err("request_irq failed to register for PRCM io\n"); > + goto err_prcm_irq_io; > } > > ret = pwrdm_for_each(pwrdms_setup, NULL); > if (ret) { > printk(KERN_ERR "Failed to setup powerdomains\n"); > - goto err2; > + goto err_pwrdms_setup; > } > > (void) clkdm_for_each(clkdms_setup, NULL); > @@ -901,7 +859,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 err_pwrdms_setup; > } > > neon_pwrdm = pwrdm_lookup("neon_pwrdm"); > @@ -950,14 +908,20 @@ static int __init omap3_pm_init(void) > } > > omap3_save_scratchpad_contents(); > -err1: > + > return ret; > -err2: > - free_irq(INT_34XX_PRCM_MPU_IRQ, NULL); > + > + err_pwrdms_setup: > + free_irq(omap_prcm_event_to_irq("io"), NULL); > list_for_each_entry_safe(pwrst, tmp, &pwrst_list, node) { > list_del(&pwrst->node); > kfree(pwrst); > } > + err_prcm_irq_io: > + free_irq(omap_prcm_event_to_irq("wkup"), NULL); > + err_prcm_irq_wkup: > + omap_prcm_irq_cleanup(); > + err_prcm_irq_init: > return ret; > } > > diff --git a/arch/arm/mach-omap2/prcm.c b/arch/arm/mach-omap2/prcm.c > index 6be1438..362c59c 100644 > --- a/arch/arm/mach-omap2/prcm.c > +++ b/arch/arm/mach-omap2/prcm.c > @@ -23,6 +23,8 @@ > #include <linux/clk.h> > #include <linux/io.h> > #include <linux/delay.h> > +#include <linux/irq.h> > +#include <linux/slab.h> > > #include <mach/system.h> > #include <plat/common.h> > @@ -45,6 +47,191 @@ void __iomem *cm2_base; > > #define MAX_MODULE_ENABLE_WAIT 100000 > > +/* Array of valid PRCM events for the current OMAP */ > +static struct omap_prcm_irq *omap_prcm_irqs; > + > +/* Number of entries in omap_prcm_irqs */ > +static int omap_prcm_irqs_nr; > + > +/* Pointers to either OMAP3 or OMAP4 specific functions */ > +static void (*omap_prcm_mask_event)(unsigned event); > +static void (*omap_prcm_unmask_event)(unsigned event); > +static void (*omap_prcm_ack_event)(unsigned event); > +static void (*omap_prcm_pending_events)(unsigned long *pending); > + > +static void prcm_irq_ack(struct irq_data *data) > +{ > + unsigned int prcm_irq = data->irq - OMAP_PRCM_IRQ_BASE; > + omap_prcm_ack_event(prcm_irq); > +} > + > +static void prcm_irq_mask(struct irq_data *data) > +{ > + unsigned int prcm_irq = data->irq - OMAP_PRCM_IRQ_BASE; > + omap_prcm_mask_event(prcm_irq); > +} > + > +static void prcm_irq_unmask(struct irq_data *data) > +{ > + unsigned int prcm_irq = data->irq - OMAP_PRCM_IRQ_BASE; > + omap_prcm_unmask_event(prcm_irq); > +} > + > +static struct irq_chip prcm_irq_chip = { > + .name = "PRCM", > + .irq_ack = prcm_irq_ack, > + .irq_mask = prcm_irq_mask, > + .irq_unmask = prcm_irq_unmask, > +}; You can probably use the new generic IRQ chip framework to handle this (c.f. kernel/irq/generic-chip.c and usage in mach-omap2/irq.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 void prcm_irq_handler(unsigned int irq, struct irq_desc *desc) > +{ > + unsigned long pending[OMAP_PRCM_MAX_NR_PENDING_REG]; > + struct irq_chip *chip = irq_desc_get_chip(desc); > + > + /* > + * 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) { > + unsigned int virtirq; > + > + chip->irq_ack(&desc->irq_data); > + > + memset(pending, 0, sizeof(pending)); > + omap_prcm_pending_events(pending); > + > + /* No bit set, then all IRQs are handled */ > + if (find_first_bit(pending, OMAP_PRCM_NR_IRQS) > + >= OMAP_PRCM_NR_IRQS) { > + chip->irq_unmask(&desc->irq_data); > + break; > + } > + > + /* > + * Loop on all currently pending irqs so that new irqs > + * cannot starve previously pending irqs > + */ > + for_each_set_bit(virtirq, pending, OMAP_PRCM_NR_IRQS) > + generic_handle_irq(OMAP_PRCM_IRQ_BASE + virtirq); > + > + chip->irq_unmask(&desc->irq_data); > + } > +} > + > +/* > + * Given a PRCM event name, returns the corresponding IRQ on which the > + * handler should be registered. > + */ > +int omap_prcm_event_to_irq(const char *name) > +{ > + int i; > + > + for (i = 0; i < omap_prcm_irqs_nr; i++) > + if (!strcmp(omap_prcm_irqs[i].name, name)) > + return OMAP_PRCM_IRQ_BASE + omap_prcm_irqs[i].offset; > + > + return -ENOENT; > +} > + > +/* > + * Prepare the array of PRCM events corresponding to the current SoC, > + * and set-up the chained interrupt handler mechanism. > + */ > +int omap_prcm_irq_init(void) > +{ > + int i, j; > + struct omap_prcm_irq *unfiltered_irqs; > + unsigned unfiltered_irqs_nr; > + > + if (cpu_is_omap34xx() || cpu_is_omap3630()) { > + unfiltered_irqs = omap_prcm_3xxx_irqs; > + unfiltered_irqs_nr = omap_prcm_3xxx_irqs_nr; > + omap_prcm_mask_event = omap3_prcm_mask_event; > + omap_prcm_unmask_event = omap3_prcm_unmask_event; > + omap_prcm_ack_event = omap3_prcm_ack_event; > + omap_prcm_pending_events = omap3_prcm_pending_events; > + irq_set_chained_handler(INT_34XX_PRCM_MPU_IRQ, > + prcm_irq_handler); > + } else if (cpu_is_omap44xx()) { > + unfiltered_irqs = omap_prcm_4xxx_irqs; > + unfiltered_irqs_nr = omap_prcm_4xxx_irqs_nr; > + omap_prcm_mask_event = omap4_prcm_mask_event; > + omap_prcm_unmask_event = omap4_prcm_unmask_event; > + omap_prcm_ack_event = omap4_prcm_ack_event; > + omap_prcm_pending_events = omap4_prcm_pending_events; > + irq_set_chained_handler(OMAP44XX_IRQ_PRCM, prcm_irq_handler); > + } else { > + return -ENODEV; > + } Minor nit: rather than use cpu_is_* here, some sort of struct of func ptrs should be defined that is filled out by the prcm[34]xxx.c code and registered with the common code. > + for (i = 0; i < unfiltered_irqs_nr; i++) > + if (omap_chip_is(unfiltered_irqs[i].omap_chip)) > + omap_prcm_irqs_nr++; > + > + omap_prcm_irqs = kmalloc(omap_prcm_irqs_nr * > + sizeof(struct omap_prcm_irq), > + GFP_KERNEL); > + if (!omap_prcm_irqs) > + return -ENOMEM; > + > + for (i = 0, j = 0; i < unfiltered_irqs_nr; i++) > + if (omap_chip_is(unfiltered_irqs[i].omap_chip)) { > + memcpy(&omap_prcm_irqs[j], &unfiltered_irqs[i], > + sizeof(struct omap_prcm_irq)); > + j++; > + } > + > + for (i = OMAP_PRCM_IRQ_BASE; i < OMAP_PRCM_IRQ_END; i++) { > + irq_set_chip(i, &prcm_irq_chip); > + irq_set_handler(i, handle_level_irq); > + set_irq_flags(i, IRQF_VALID); > + } > + > + return 0; > +} > + > +/* > + * Reverses memory allocated and other setups done by > + * omap_prcm_irq_init(). > + */ > +void omap_prcm_irq_cleanup(void) > +{ > + int i; > + > + for (i = OMAP_PRCM_IRQ_BASE; i < OMAP_PRCM_IRQ_END; i++) { > + irq_set_chip(i, NULL); > + irq_set_handler(i, NULL); > + set_irq_flags(i, 0); > + } > + > + kfree(omap_prcm_irqs); > + > + if (cpu_is_omap34xx() || cpu_is_omap3630()) { > + irq_set_chained_handler(INT_34XX_PRCM_MPU_IRQ, NULL); > + } else { > + irq_set_chained_handler(OMAP44XX_IRQ_PRCM, NULL); > + } > +} > + > u32 omap_prcm_get_reset_sources(void) > { > /* XXX This presumably needs modification for 34XX */ > diff --git a/arch/arm/mach-omap2/prcm3xxx.c b/arch/arm/mach-omap2/prcm3xxx.c > new file mode 100644 > index 0000000..a57fe69 > --- /dev/null > +++ b/arch/arm/mach-omap2/prcm3xxx.c > @@ -0,0 +1,117 @@ > +/* > + * linux/arch/arm/mach-omap2/prcm3xxx.c > + * > + * OMAP 3xxx Power Reset and Clock Management (PRCM) interrupt > + * definitions > + * > + * Written by Thomas Petazzoni <t-petazzoni@xxxxxx> > + * Copyright (C) 2010 Texas Instruments, Inc. > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License version 2 as > + * published by the Free Software Foundation. > + */ > + > +#include <linux/kernel.h> > +#include <linux/init.h> > + > +#include <plat/prcm.h> > + > +#include "prm-regbits-24xx.h" > + > +struct omap_prcm_irq __initdata omap_prcm_3xxx_irqs[] = { > + OMAP_PRCM_IRQ("wkup", 0, > + CHIP_IS_OMAP3430 | CHIP_GE_OMAP3630ES1_1), > + OMAP_PRCM_IRQ("evgenon", 2, > + CHIP_IS_OMAP3430 | CHIP_GE_OMAP3630ES1_1), > + OMAP_PRCM_IRQ("evgenoff", 3, > + CHIP_IS_OMAP3430 | CHIP_GE_OMAP3630ES1_1), > + OMAP_PRCM_IRQ("transition", 4, > + CHIP_IS_OMAP3430 | CHIP_GE_OMAP3630ES1_1), > + OMAP_PRCM_IRQ("core_dpll_recal", 5, > + CHIP_IS_OMAP3430 | CHIP_GE_OMAP3630ES1_1), > + OMAP_PRCM_IRQ("periph_dpll_recal", 6, > + CHIP_IS_OMAP3430 | CHIP_GE_OMAP3630ES1_1), > + OMAP_PRCM_IRQ("mpu_dpll_recal", 7, > + CHIP_IS_OMAP3430 | CHIP_GE_OMAP3630ES1_1), > + OMAP_PRCM_IRQ("iva2_dpll_recal", 8, > + CHIP_IS_OMAP3430 | CHIP_GE_OMAP3630ES1_1), > + OMAP_PRCM_IRQ("io", 9, > + CHIP_IS_OMAP3430 | CHIP_GE_OMAP3630ES1_1), > + OMAP_PRCM_IRQ("vp1_oppchangedone", 10, > + CHIP_IS_OMAP3430 | CHIP_GE_OMAP3630ES1_1), > + OMAP_PRCM_IRQ("vp1_minvdd", 11, > + CHIP_IS_OMAP3430 | CHIP_GE_OMAP3630ES1_1), > + OMAP_PRCM_IRQ("vp1_maxvdd", 12, > + CHIP_IS_OMAP3430 | CHIP_GE_OMAP3630ES1_1), > + OMAP_PRCM_IRQ("vp1_nosmpsack", 13, > + CHIP_IS_OMAP3430 | CHIP_GE_OMAP3630ES1_1), > + OMAP_PRCM_IRQ("vp1_eqvalue", 14, > + CHIP_IS_OMAP3430 | CHIP_GE_OMAP3630ES1_1), > + OMAP_PRCM_IRQ("vp1_tranxdone", 15, > + CHIP_IS_OMAP3430 | CHIP_GE_OMAP3630ES1_1), > + OMAP_PRCM_IRQ("vp2_oppchangedone", 16, > + CHIP_IS_OMAP3430 | CHIP_GE_OMAP3630ES1_1), > + OMAP_PRCM_IRQ("vp2_minvdd", 17, > + CHIP_IS_OMAP3430 | CHIP_GE_OMAP3630ES1_1), > + OMAP_PRCM_IRQ("vp2_maxvdd", 18, > + CHIP_IS_OMAP3430 | CHIP_GE_OMAP3630ES1_1), > + OMAP_PRCM_IRQ("vp2_nosmpsack", 19, > + CHIP_IS_OMAP3430 | CHIP_GE_OMAP3630ES1_1), > + OMAP_PRCM_IRQ("vp2_eqvalue", 20, > + CHIP_IS_OMAP3430 | CHIP_GE_OMAP3630ES1_1), > + OMAP_PRCM_IRQ("vp2_tranxdone", 21, > + CHIP_IS_OMAP3430 | CHIP_GE_OMAP3630ES1_1), > + OMAP_PRCM_IRQ("vc_saerr", 22, > + CHIP_IS_OMAP3430 | CHIP_GE_OMAP3630ES1_1), > + OMAP_PRCM_IRQ("vc_raerr", 23, > + CHIP_IS_OMAP3430 | CHIP_GE_OMAP3630ES1_1), > + OMAP_PRCM_IRQ("vc_timeout_err", 24, > + CHIP_IS_OMAP3430 | CHIP_GE_OMAP3630ES1_1), > + OMAP_PRCM_IRQ("snd_periph_recal", 25, > + CHIP_IS_OMAP3430 | CHIP_GE_OMAP3630ES1_1), > + OMAP_PRCM_IRQ("abb_ldo_tranxdone", 26, > + CHIP_GE_OMAP3630ES1_1), > + OMAP_PRCM_IRQ("vc_vp1_ack", 27, > + CHIP_GE_OMAP3630ES1_1), > + OMAP_PRCM_IRQ("vc_bypass_ack", 28, > + CHIP_GE_OMAP3630ES1_1), > +}; > + > +unsigned int __initdata > +omap_prcm_3xxx_irqs_nr = ARRAY_SIZE(omap_prcm_3xxx_irqs); > + > +void omap3_prcm_mask_event(unsigned event) > +{ > + unsigned int bit = BIT(event); > + > + omap2_prm_rmw_mod_reg_bits(bit, 0, OCP_MOD, > + OMAP3_PRM_IRQENABLE_MPU_OFFSET); > +} > + > +void omap3_prcm_unmask_event(unsigned event) > +{ > + unsigned int bit = BIT(event); > + > + omap2_prm_rmw_mod_reg_bits(0, bit, OCP_MOD, > + OMAP3_PRM_IRQENABLE_MPU_OFFSET); > +} > + > +void omap3_prcm_ack_event(unsigned event) > +{ > + unsigned int bit = BIT(event); > + > + omap2_prm_write_mod_reg(bit, OCP_MOD, > + OMAP3_PRM_IRQSTATUS_MPU_OFFSET); > +} > + > +void omap3_prcm_pending_events(unsigned long *events) > +{ > + u32 irqenable_mpu = > + omap2_prm_read_mod_reg(OCP_MOD, > + OMAP3_PRM_IRQENABLE_MPU_OFFSET); > + u32 irqstatus_mpu = > + omap2_prm_read_mod_reg(OCP_MOD, > + OMAP3_PRM_IRQSTATUS_MPU_OFFSET); > + events[0] = irqenable_mpu & irqstatus_mpu; > +} > diff --git a/arch/arm/mach-omap2/prcm4xxx.c b/arch/arm/mach-omap2/prcm4xxx.c > new file mode 100644 > index 0000000..e70f267 > --- /dev/null > +++ b/arch/arm/mach-omap2/prcm4xxx.c > @@ -0,0 +1,146 @@ > +/* > + * linux/arch/arm/mach-omap2/prcm4xxx.c Minor: filenames are not needed in headers. Files tend to move around and these comments don't get updated. > + * OMAP 4xxx Power Reset and Clock Management (PRCM) interrupt > + * definitions > + * > + * Written by Thomas Petazzoni <t-petazzoni@xxxxxx> > + * Copyright (C) 2010 Texas Instruments, Inc. > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License version 2 as > + * published by the Free Software Foundation. > + */ > + > +#include <linux/kernel.h> > +#include <linux/init.h> > + > +#include <plat/prcm.h> > + > +#include "prcm44xx.h" > +#include "prm44xx.h" > + > +struct omap_prcm_irq __initdata omap_prcm_4xxx_irqs[] = { > + OMAP_PRCM_IRQ("dpll_core_recal", 0, > + CHIP_IS_OMAP4430), > + OMAP_PRCM_IRQ("dpll_mpu_recal", 1, > + CHIP_IS_OMAP4430), > + OMAP_PRCM_IRQ("dpll_iva_recal", 2, > + CHIP_IS_OMAP4430), > + OMAP_PRCM_IRQ("dpll_per_recal", 3, > + CHIP_IS_OMAP4430), > + OMAP_PRCM_IRQ("dpll_abe_recal", 4, > + CHIP_IS_OMAP4430), > + OMAP_PRCM_IRQ("dpll_usb_recal", 5, > + CHIP_IS_OMAP4430), > + OMAP_PRCM_IRQ("dpll_unipro_recal", 7, > + CHIP_IS_OMAP4430), > + OMAP_PRCM_IRQ("transition", 8, > + CHIP_IS_OMAP4430), > + OMAP_PRCM_IRQ("io", 9, > + CHIP_IS_OMAP4430), > + OMAP_PRCM_IRQ("vc_saerr", 11, > + CHIP_IS_OMAP4430), > + OMAP_PRCM_IRQ("vc_raerr", 12, > + CHIP_IS_OMAP4430), > + OMAP_PRCM_IRQ("vc_toerr", 13, > + CHIP_IS_OMAP4430), > + OMAP_PRCM_IRQ("vc_bypassack", 14, > + CHIP_IS_OMAP4430), > + OMAP_PRCM_IRQ("vp_core_oppchangedone", 16, > + CHIP_IS_OMAP4430), > + OMAP_PRCM_IRQ("vp_core_minvdd", 17, > + CHIP_IS_OMAP4430), > + OMAP_PRCM_IRQ("vp_core_maxvdd", 18, > + CHIP_IS_OMAP4430), > + OMAP_PRCM_IRQ("vp_core_nosmpsack", 19, > + CHIP_IS_OMAP4430), > + OMAP_PRCM_IRQ("vp_core_eqvalue", 20, > + CHIP_IS_OMAP4430), > + OMAP_PRCM_IRQ("vp_core_tranxdone", 21, > + CHIP_IS_OMAP4430), > + OMAP_PRCM_IRQ("vp_iva_oppchangedone", 24, > + CHIP_IS_OMAP4430), > + OMAP_PRCM_IRQ("vp_iva_minvdd", 25, > + CHIP_IS_OMAP4430), > + OMAP_PRCM_IRQ("vp_iva_maxvdd", 26, > + CHIP_IS_OMAP4430), > + OMAP_PRCM_IRQ("vp_iva_nosmpsack", 27, > + CHIP_IS_OMAP4430), > + OMAP_PRCM_IRQ("vp_iva_eqvalue", 28, > + CHIP_IS_OMAP4430), > + OMAP_PRCM_IRQ("vp_iva_tranxdone", 29, > + CHIP_IS_OMAP4430), > + OMAP_PRCM_IRQ("vp_iva_vpack", 30, > + CHIP_IS_OMAP4430), > + OMAP_PRCM_IRQ("abb_iva_done", 31, > + CHIP_IS_OMAP4430), > + OMAP_PRCM_IRQ("vp_mpu_oppchangedone", 32, > + CHIP_IS_OMAP4430), > + OMAP_PRCM_IRQ("vp_mpu_minvdd", 33, > + CHIP_IS_OMAP4430), > + OMAP_PRCM_IRQ("vp_mpu_maxvdd", 34, > + CHIP_IS_OMAP4430), > + OMAP_PRCM_IRQ("vp_mpu_nosmpsack", 35, > + CHIP_IS_OMAP4430), > + OMAP_PRCM_IRQ("vp_mpu_eqvalue", 36, > + CHIP_IS_OMAP4430), > + OMAP_PRCM_IRQ("vp_mpu_tranxdone", 37, > + CHIP_IS_OMAP4430), > + OMAP_PRCM_IRQ("vp_mpu_vpack", 38, > + CHIP_IS_OMAP4430), > + OMAP_PRCM_IRQ("abb_mpu_done", 39, > + CHIP_IS_OMAP4430), > +}; > + > +unsigned int __initdata > +omap_prcm_4xxx_irqs_nr = ARRAY_SIZE(omap_prcm_4xxx_irqs); > + > +void omap4_prcm_mask_event(unsigned event) > +{ > + unsigned int bit = BIT(event % 32); > + unsigned int off = (event / 32) * 4; > + > + omap4_prm_rmw_inst_reg_bits(bit, 0, > + OMAP4430_PRM_OCP_SOCKET_INST, > + OMAP4_PRM_IRQENABLE_MPU_OFFSET + off); > +} > + > +void omap4_prcm_unmask_event(unsigned event) > +{ > + unsigned int bit = BIT(event % 32); > + unsigned int off = (event / 32) * 4; > + > + omap4_prm_rmw_inst_reg_bits(0, bit, > + OMAP4430_PRM_OCP_SOCKET_INST, > + OMAP4_PRM_IRQENABLE_MPU_OFFSET + off); > +} > + > +void omap4_prcm_ack_event(unsigned event) > +{ > + unsigned int bit = BIT(event % 32); > + unsigned int off = (event / 32) * 4; > + > + omap4_prm_write_inst_reg(bit, > + OMAP4430_PRM_OCP_SOCKET_INST, > + OMAP4_PRM_IRQSTATUS_MPU_OFFSET + off); > +} > + > +void omap4_prcm_pending_events(unsigned long *events) > +{ > + u32 irqenable_mpu, irqstatus_mpu; > + int i; > + > + /* OMAP4 has two enable/status registers for the PRCM */ > + for (i = 0; i < 2; i++) { > + irqenable_mpu = > + omap4_prm_read_inst_reg(OMAP4430_PRM_OCP_SOCKET_INST, > + OMAP4_PRM_IRQENABLE_MPU_OFFSET > + + i * 4); > + irqstatus_mpu = > + omap4_prm_read_inst_reg(OMAP4430_PRM_OCP_SOCKET_INST, > + OMAP4_PRM_IRQSTATUS_MPU_OFFSET > + + i * 4); > + events[i] = irqenable_mpu & irqstatus_mpu; > + } > +} > diff --git a/arch/arm/plat-omap/include/plat/irqs.h b/arch/arm/plat-omap/include/plat/irqs.h > index 5a25098..23b9680 100644 > --- a/arch/arm/plat-omap/include/plat/irqs.h > +++ b/arch/arm/plat-omap/include/plat/irqs.h > @@ -366,7 +366,14 @@ > #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) > + > +/* 64 IRQs for the PRCM (32 are needed on OMAP3, 64 on OMAP4) */ > +#define OMAP_PRCM_IRQ_BASE (OMAP_MPUIO_IRQ_END) > +#define OMAP_PRCM_NR_IRQS 64 > +#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) > diff --git a/arch/arm/plat-omap/include/plat/prcm.h b/arch/arm/plat-omap/include/plat/prcm.h > index 267f43b..5785555 100644 > --- a/arch/arm/plat-omap/include/plat/prcm.h > +++ b/arch/arm/plat-omap/include/plat/prcm.h > @@ -27,6 +27,51 @@ > #ifndef __ASM_ARM_ARCH_OMAP_PRCM_H > #define __ASM_ARM_ARCH_OMAP_PRCM_H > > +#include <plat/cpu.h> > + > +/* > + * Structure describing the interrupt corresponding to each PRCM event > + */ > +struct omap_prcm_irq { > + /* Logical name for the interrupt */ > + const char *name; > + > + /* > + * Corresponding offset in the status/enable register. The > + * offset can be greater than 32, in which case it spans over > + * the second status register > + */ > + unsigned int offset; > + > + /* OMAP chip for which this PRCM event exists */ > + const struct omap_chip_id omap_chip; > +}; > + > +#define OMAP_PRCM_IRQ(_name, _offset, _chip) \ > + { .name = _name, \ > + .offset = _offset, \ > + .omap_chip = OMAP_CHIP_INIT(_chip) } > + > +/* Maximum number of PRCM interrupt status registers */ > +#define OMAP_PRCM_MAX_NR_PENDING_REG 2 > + > +extern struct omap_prcm_irq omap_prcm_3xxx_irqs[]; > +extern unsigned int omap_prcm_3xxx_irqs_nr; > +void omap3_prcm_mask_event(unsigned event); > +void omap3_prcm_unmask_event(unsigned event); > +void omap3_prcm_ack_event(unsigned event); > +void omap3_prcm_pending_events(unsigned long *pending); > + > +extern struct omap_prcm_irq omap_prcm_4xxx_irqs[]; > +extern unsigned int omap_prcm_4xxx_irqs_nr; > +void omap4_prcm_mask_event(unsigned event); > +void omap4_prcm_unmask_event(unsigned event); > +void omap4_prcm_ack_event(unsigned event); > +void omap4_prcm_pending_events(unsigned long *pending); Defining a struct of func ptrs and filling it out in the prcm[34]xxx.c file would also mean you wouldn't need all the omap3_ and omap4_ functions in the header. > +int omap_prcm_event_to_irq(const char *name); > +int omap_prcm_irq_init(void); > +void omap_prcm_irq_cleanup(void); > u32 omap_prcm_get_reset_sources(void); > int omap2_cm_wait_idlest(void __iomem *reg, u32 mask, u8 idlest, > const char *name); Kevin -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html