* [PATCHv3 0/6] PRCM chain handler
@ 2011-06-22 16:42 Tero Kristo
2011-06-22 16:42 ` [PATCHv3 1/6] omap: prcm: switch to a chained IRQ handler mechanism Tero Kristo
` (6 more replies)
0 siblings, 7 replies; 29+ messages in thread
From: Tero Kristo @ 2011-06-22 16:42 UTC (permalink / raw)
To: linux-omap
I was able to test this on OMAP3 beagleboard and got it working properly
after some sweating.
Version 3 changes:
- Changed omap3 PRCM wakeup interrupt handler to be a dummy, wakeup irq
clearing is now done when we are entering idle. This prevents PRCM
interrupt hanging in case UART is disabled, and gives probably slightly
better latencies during wakeup.
- Added console lock protection during uart clock enable / disable
* prevents nastiness to happen when omap_device wants to print out something
- A few new resume_idle calls added to tty omap-serial
-Tero
Texas Instruments Oy, Tekniikantie 12, 02150 Espoo. Y-tunnus: 0115040-6. Kotipaikka: Helsinki
^ permalink raw reply [flat|nested] 29+ messages in thread
* [PATCHv3 1/6] omap: prcm: switch to a chained IRQ handler mechanism
2011-06-22 16:42 [PATCHv3 0/6] PRCM chain handler Tero Kristo
@ 2011-06-22 16:42 ` Tero Kristo
2011-06-22 23:53 ` Kevin Hilman
` (2 more replies)
2011-06-22 16:42 ` [PATCHv3 2/6] PRCM: Add support for PAD wakeup interrupts Tero Kristo
` (5 subsequent siblings)
6 siblings, 3 replies; 29+ messages in thread
From: Tero Kristo @ 2011-06-22 16:42 UTC (permalink / raw)
To: linux-omap; +Cc: Thomas Petazzoni, Avinash.H.M, Kevin Hilman, Cousson, Benoit
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.
Signed-off-by: Tero Kristo <t-kristo@ti.com>
Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
Cc: Avinash.H.M <avinashhm@ti.com>
Cc: Kevin Hilman <khilman@ti.com>
Cc: Cousson, Benoit <b-cousson@ti.com>
---
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);
+ 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);
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,
+};
+
+/*
+ * 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;
+ }
+
+ 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@ti.com>
+ * 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
+ *
+ * OMAP 4xxx Power Reset and Clock Management (PRCM) interrupt
+ * definitions
+ *
+ * Written by Thomas Petazzoni <t-petazzoni@ti.com>
+ * 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);
+
+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);
--
1.7.4.1
Texas Instruments Oy, Tekniikantie 12, 02150 Espoo. Y-tunnus: 0115040-6. Kotipaikka: Helsinki
^ permalink raw reply related [flat|nested] 29+ messages in thread
* [PATCHv3 2/6] PRCM: Add support for PAD wakeup interrupts
2011-06-22 16:42 [PATCHv3 0/6] PRCM chain handler Tero Kristo
2011-06-22 16:42 ` [PATCHv3 1/6] omap: prcm: switch to a chained IRQ handler mechanism Tero Kristo
@ 2011-06-22 16:42 ` Tero Kristo
2011-06-23 10:23 ` Govindraj
2011-06-24 21:21 ` Kevin Hilman
2011-06-22 16:42 ` [PATCHv3 3/6] OMAP: PRCM: Added an api to get id for a PRCM event Tero Kristo
` (4 subsequent siblings)
6 siblings, 2 replies; 29+ messages in thread
From: Tero Kristo @ 2011-06-22 16:42 UTC (permalink / raw)
To: linux-omap
PRCM interrupt handler will now parse registered pads to see whether there
is an active wakeup event. If this is the case, the corresponding interrupt
will be triggered. This can be used for example with UART driver to register
PAD wakeup event for the UART RX pin, and when this happens, UART interrupt
will be triggered.
Signed-off-by: Tero Kristo <t-kristo@ti.com>
---
arch/arm/mach-omap2/prcm.c | 50 ++++++++++++++++++++++++++++++++
arch/arm/plat-omap/include/plat/prcm.h | 1 +
2 files changed, 51 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-omap2/prcm.c b/arch/arm/mach-omap2/prcm.c
index 362c59c..cc92064 100644
--- a/arch/arm/mach-omap2/prcm.c
+++ b/arch/arm/mach-omap2/prcm.c
@@ -53,6 +53,15 @@ static struct omap_prcm_irq *omap_prcm_irqs;
/* Number of entries in omap_prcm_irqs */
static int omap_prcm_irqs_nr;
+/* PAD handlers list */
+struct pad_def {
+ u32 pad;
+ unsigned int irq;
+ struct list_head node;
+};
+
+static LIST_HEAD(pad_handler_list);
+
/* Pointers to either OMAP3 or OMAP4 specific functions */
static void (*omap_prcm_mask_event)(unsigned event);
static void (*omap_prcm_unmask_event)(unsigned event);
@@ -84,6 +93,25 @@ static struct irq_chip prcm_irq_chip = {
.irq_unmask = prcm_irq_unmask,
};
+
+/*
+ * Handler for PAD irqs, called from PRCM interrupt handler
+ */
+static void omap_prcm_handle_pad_irqs(void)
+{
+ struct pad_def *def;
+ u16 val = 0;
+ list_for_each_entry(def, &pad_handler_list, node) {
+ /* Read padconf value based on cpu type */
+ if (cpu_is_omap34xx())
+ val = omap_ctrl_readw(def->pad);
+
+ /* if pad wakeupevent is active, fire registered IRQ */
+ if (val & OMAP3_PADCONF_WAKEUPEVENT0)
+ generic_handle_irq(def->irq);
+ }
+}
+
/*
* PRCM Interrupt Handler
*
@@ -106,6 +134,9 @@ 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);
+ /* Handle PAD events first, we don't want to ack them before parse */
+ omap_prcm_handle_pad_irqs();
+
/*
* Loop until all pending irqs are handled, since
* generic_handle_irq(), called by prcm_irq_handle_virtirqs()
@@ -153,6 +184,25 @@ int omap_prcm_event_to_irq(const char *name)
}
/*
+ * Register interrupt handler for a given pad. When the PRCM interrupt
+ * handler detects wakeupevent on the corresponding pad, the IRQ will
+ * be triggered.
+ */
+int omap_prcm_register_pad_irq(u32 pad, unsigned int irq)
+{
+ struct pad_def *def;
+
+ def = kmalloc(sizeof(struct pad_def), GFP_ATOMIC);
+ if (!def)
+ return -ENOMEM;
+
+ def->pad = pad;
+ def->irq = irq;
+ list_add(&def->node, &pad_handler_list);
+ return 0;
+}
+
+/*
* Prepare the array of PRCM events corresponding to the current SoC,
* and set-up the chained interrupt handler mechanism.
*/
diff --git a/arch/arm/plat-omap/include/plat/prcm.h b/arch/arm/plat-omap/include/plat/prcm.h
index 5785555..789eb17 100644
--- a/arch/arm/plat-omap/include/plat/prcm.h
+++ b/arch/arm/plat-omap/include/plat/prcm.h
@@ -72,6 +72,7 @@ void omap4_prcm_pending_events(unsigned long *pending);
int omap_prcm_event_to_irq(const char *name);
int omap_prcm_irq_init(void);
void omap_prcm_irq_cleanup(void);
+int omap_prcm_register_pad_irq(u32 pad, unsigned int irq);
u32 omap_prcm_get_reset_sources(void);
int omap2_cm_wait_idlest(void __iomem *reg, u32 mask, u8 idlest,
const char *name);
--
1.7.4.1
Texas Instruments Oy, Tekniikantie 12, 02150 Espoo. Y-tunnus: 0115040-6. Kotipaikka: Helsinki
^ permalink raw reply related [flat|nested] 29+ messages in thread
* [PATCHv3 3/6] OMAP: PRCM: Added an api to get id for a PRCM event
2011-06-22 16:42 [PATCHv3 0/6] PRCM chain handler Tero Kristo
2011-06-22 16:42 ` [PATCHv3 1/6] omap: prcm: switch to a chained IRQ handler mechanism Tero Kristo
2011-06-22 16:42 ` [PATCHv3 2/6] PRCM: Add support for PAD wakeup interrupts Tero Kristo
@ 2011-06-22 16:42 ` Tero Kristo
2011-06-24 21:58 ` Kevin Hilman
2011-06-22 16:42 ` [PATCHv3 4/6] OMAP3: PM: Use PRCM chain handler Tero Kristo
` (3 subsequent siblings)
6 siblings, 1 reply; 29+ messages in thread
From: Tero Kristo @ 2011-06-22 16:42 UTC (permalink / raw)
To: linux-omap
This is required by OMAP3 as it needs to dynamically unmask events during
idle cycle.
Signed-off-by: Tero Kristo <t-kristo@ti.com>
---
arch/arm/mach-omap2/prcm.c | 17 ++++++++++++++---
arch/arm/plat-omap/include/plat/prcm.h | 1 +
2 files changed, 15 insertions(+), 3 deletions(-)
diff --git a/arch/arm/mach-omap2/prcm.c b/arch/arm/mach-omap2/prcm.c
index cc92064..f6cde6f 100644
--- a/arch/arm/mach-omap2/prcm.c
+++ b/arch/arm/mach-omap2/prcm.c
@@ -174,12 +174,23 @@ static void prcm_irq_handler(unsigned int irq, struct irq_desc *desc)
*/
int omap_prcm_event_to_irq(const char *name)
{
- int i;
+ int id;
+
+ id = omap_prcm_event_to_id(name);
+ if (id < 0)
+ return id;
+ return OMAP_PRCM_IRQ_BASE + id;
+}
+/*
+ * Given a PRCM event name, returns the corresponding event id.
+ */
+int omap_prcm_event_to_id(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 omap_prcm_irqs[i].offset;
return -ENOENT;
}
diff --git a/arch/arm/plat-omap/include/plat/prcm.h b/arch/arm/plat-omap/include/plat/prcm.h
index 789eb17..b5413ea 100644
--- a/arch/arm/plat-omap/include/plat/prcm.h
+++ b/arch/arm/plat-omap/include/plat/prcm.h
@@ -70,6 +70,7 @@ void omap4_prcm_ack_event(unsigned event);
void omap4_prcm_pending_events(unsigned long *pending);
int omap_prcm_event_to_irq(const char *name);
+int omap_prcm_event_to_id(const char *name);
int omap_prcm_irq_init(void);
void omap_prcm_irq_cleanup(void);
int omap_prcm_register_pad_irq(u32 pad, unsigned int irq);
--
1.7.4.1
Texas Instruments Oy, Tekniikantie 12, 02150 Espoo. Y-tunnus: 0115040-6. Kotipaikka: Helsinki
^ permalink raw reply related [flat|nested] 29+ messages in thread
* [PATCHv3 4/6] OMAP3: PM: Use PRCM chain handler
2011-06-22 16:42 [PATCHv3 0/6] PRCM chain handler Tero Kristo
` (2 preceding siblings ...)
2011-06-22 16:42 ` [PATCHv3 3/6] OMAP: PRCM: Added an api to get id for a PRCM event Tero Kristo
@ 2011-06-22 16:42 ` Tero Kristo
2011-06-24 21:57 ` Kevin Hilman
2011-06-22 16:42 ` [PATCHv3 5/6] OMAP3: Serial: Made serial to work properly with " Tero Kristo
` (2 subsequent siblings)
6 siblings, 1 reply; 29+ messages in thread
From: Tero Kristo @ 2011-06-22 16:42 UTC (permalink / raw)
To: linux-omap
PRCM interrupts are now handled with the chained handler mechanism. This patch
also changes the PRCM interrupts to be of one-shot type, and the interrupt
does not clear the wakeup statuses anymore. Clearing of the wakeup interrupts
is done just before entering idle, as we probably have more time to do this
during this time. Changing the wakeup handling logic also fixes an issue
with the chained PRCM serial interrupts, that prevents clearing of the
UART wakeup status and hangs the device.
Signed-off-by: Tero Kristo <t-kristo@ti.com>
---
arch/arm/mach-omap2/pm34xx.c | 31 ++++++++++++++++++-------------
1 files changed, 18 insertions(+), 13 deletions(-)
diff --git a/arch/arm/mach-omap2/pm34xx.c b/arch/arm/mach-omap2/pm34xx.c
index adab4d5..ff0811a 100644
--- a/arch/arm/mach-omap2/pm34xx.c
+++ b/arch/arm/mach-omap2/pm34xx.c
@@ -90,6 +90,7 @@ static int (*_omap_save_secure_sram)(u32 *addr);
static struct powerdomain *mpu_pwrdm, *neon_pwrdm;
static struct powerdomain *core_pwrdm, *per_pwrdm;
static struct powerdomain *cam_pwrdm;
+static int wkup_event, io_event;
static inline void omap3_per_save_context(void)
{
@@ -242,20 +243,18 @@ static int prcm_clear_mod_irqs(s16 module, u8 regs)
static irqreturn_t _prcm_int_handle_wakeup(int irq, void *unused)
{
- int c;
+ return IRQ_HANDLED;
+}
- c = prcm_clear_mod_irqs(WKUP_MOD, 1);
- c += prcm_clear_mod_irqs(CORE_MOD, 1);
- c += prcm_clear_mod_irqs(OMAP3430_PER_MOD, 1);
+static void prcm_clear_wakeups(void)
+{
+ prcm_clear_mod_irqs(WKUP_MOD, 1);
+ prcm_clear_mod_irqs(CORE_MOD, 1);
+ prcm_clear_mod_irqs(OMAP3430_PER_MOD, 1);
if (omap_rev() > OMAP3430_REV_ES1_0) {
- c += prcm_clear_mod_irqs(CORE_MOD, 3);
- c += prcm_clear_mod_irqs(OMAP3430ES2_USBHOST_MOD, 1);
+ prcm_clear_mod_irqs(CORE_MOD, 3);
+ prcm_clear_mod_irqs(OMAP3430ES2_USBHOST_MOD, 1);
}
-
- if (c)
- return IRQ_HANDLED;
- else
- return IRQ_NONE;
}
/* Function to restore the table entry that was modified for enabling MMU */
@@ -301,6 +300,10 @@ void omap_sram_idle(void)
if (!_omap_sram_idle)
return;
+ prcm_clear_wakeups();
+ omap3_prcm_unmask_event(wkup_event);
+ omap3_prcm_unmask_event(io_event);
+
pwrdm_clear_all_prev_pwrst(mpu_pwrdm);
pwrdm_clear_all_prev_pwrst(neon_pwrdm);
pwrdm_clear_all_prev_pwrst(core_pwrdm);
@@ -832,17 +835,19 @@ static int __init omap3_pm_init(void)
goto err_prcm_irq_init;
}
+ wkup_event = omap_prcm_event_to_id("wkup");
ret = request_irq(omap_prcm_event_to_irq("wkup"),
_prcm_int_handle_wakeup,
- IRQF_NO_SUSPEND, "prcm_wkup", NULL);
+ IRQF_NO_SUSPEND | IRQF_ONESHOT, "prcm_wkup", NULL);
if (ret) {
pr_err("request_irq failed to register for PRCM wakeup\n");
goto err_prcm_irq_wkup;
}
+ io_event = omap_prcm_event_to_id("io");
ret = request_irq(omap_prcm_event_to_irq("io"),
_prcm_int_handle_wakeup,
- IRQF_NO_SUSPEND, "prcm_io", NULL);
+ IRQF_NO_SUSPEND | IRQF_ONESHOT, "prcm_io", NULL);
if (ret) {
pr_err("request_irq failed to register for PRCM io\n");
goto err_prcm_irq_io;
--
1.7.4.1
Texas Instruments Oy, Tekniikantie 12, 02150 Espoo. Y-tunnus: 0115040-6. Kotipaikka: Helsinki
^ permalink raw reply related [flat|nested] 29+ messages in thread
* [PATCHv3 5/6] OMAP3: Serial: Made serial to work properly with PRCM chain handler
2011-06-22 16:42 [PATCHv3 0/6] PRCM chain handler Tero Kristo
` (3 preceding siblings ...)
2011-06-22 16:42 ` [PATCHv3 4/6] OMAP3: PM: Use PRCM chain handler Tero Kristo
@ 2011-06-22 16:42 ` Tero Kristo
2011-06-22 17:09 ` Tero Kristo
` (2 more replies)
2011-06-22 16:42 ` [PATCHv3 6/6] OMAP3: Serial tty: Added resume_idle calls to critical points Tero Kristo
2011-06-27 15:02 ` [PATCHv3 0/6] PRCM chain handler Tero Kristo
6 siblings, 3 replies; 29+ messages in thread
From: Tero Kristo @ 2011-06-22 16:42 UTC (permalink / raw)
To: linux-omap
Signed-off-by: Tero Kristo <t-kristo@ti.com>
---
arch/arm/mach-omap2/pm34xx.c | 19 -------------------
arch/arm/mach-omap2/serial.c | 40 +++++++++++++++++++++-------------------
2 files changed, 21 insertions(+), 38 deletions(-)
diff --git a/arch/arm/mach-omap2/pm34xx.c b/arch/arm/mach-omap2/pm34xx.c
index ff0811a..6fff894 100644
--- a/arch/arm/mach-omap2/pm34xx.c
+++ b/arch/arm/mach-omap2/pm34xx.c
@@ -340,18 +340,9 @@ void omap_sram_idle(void)
omap3_enable_io_chain();
}
- /* Block console output in case it is on one of the OMAP UARTs */
- if (!is_suspending())
- if (per_next_state < PWRDM_POWER_ON ||
- core_next_state < PWRDM_POWER_ON)
- if (!console_trylock())
- goto console_still_active;
-
/* PER */
if (per_next_state < PWRDM_POWER_ON) {
per_going_off = (per_next_state == PWRDM_POWER_OFF) ? 1 : 0;
- omap_uart_prepare_idle(2);
- omap_uart_prepare_idle(3);
omap2_gpio_prepare_for_idle(per_going_off);
if (per_next_state == PWRDM_POWER_OFF)
omap3_per_save_context();
@@ -359,8 +350,6 @@ void omap_sram_idle(void)
/* CORE */
if (core_next_state < PWRDM_POWER_ON) {
- omap_uart_prepare_idle(0);
- omap_uart_prepare_idle(1);
if (core_next_state == PWRDM_POWER_OFF) {
omap3_core_save_context();
omap3_cm_save_context();
@@ -407,8 +396,6 @@ void omap_sram_idle(void)
omap3_sram_restore_context();
omap2_sms_restore_context();
}
- omap_uart_resume_idle(0);
- omap_uart_resume_idle(1);
if (core_next_state == PWRDM_POWER_OFF)
omap2_prm_clear_mod_reg_bits(OMAP3430_AUTO_OFF_MASK,
OMAP3430_GR_MOD,
@@ -422,14 +409,8 @@ void omap_sram_idle(void)
omap2_gpio_resume_after_idle();
if (per_prev_state == PWRDM_POWER_OFF)
omap3_per_restore_context();
- omap_uart_resume_idle(2);
- omap_uart_resume_idle(3);
}
- if (!is_suspending())
- console_unlock();
-
-console_still_active:
/* Disable IO-PAD and IO-CHAIN wakeup */
if (omap3_has_io_wakeup() &&
(per_next_state < PWRDM_POWER_ON ||
diff --git a/arch/arm/mach-omap2/serial.c b/arch/arm/mach-omap2/serial.c
index 1ac361b..e4a5a8c 100644
--- a/arch/arm/mach-omap2/serial.c
+++ b/arch/arm/mach-omap2/serial.c
@@ -39,6 +39,7 @@
#include <plat/dma.h>
#include <plat/omap_hwmod.h>
#include <plat/omap_device.h>
+#include <plat/prcm.h>
#include "prm2xxx_3xxx.h"
#include "pm.h"
@@ -280,24 +281,34 @@ static inline void omap_uart_restore_context(struct omap_uart_state *uart) {}
static inline void omap_uart_enable_clocks(struct omap_uart_state *uart)
{
+ int locked;
+
if (uart->clocked)
return;
+ locked = console_trylock();
omap_device_enable(uart->pdev);
uart->clocked = 1;
omap_uart_restore_context(uart);
+ if (locked)
+ console_unlock();
}
#ifdef CONFIG_PM
static inline void omap_uart_disable_clocks(struct omap_uart_state *uart)
{
+ int locked;
+
if (!uart->clocked)
return;
+ locked = console_trylock();
omap_uart_save_context(uart);
uart->clocked = 0;
omap_device_idle(uart->pdev);
+ if (locked)
+ console_unlock();
}
static void omap_uart_enable_wakeup(struct omap_uart_state *uart)
@@ -380,6 +391,7 @@ static void omap_uart_allow_sleep(struct omap_uart_state *uart)
omap_uart_smart_idle_enable(uart, 1);
uart->can_sleep = 1;
del_timer(&uart->timer);
+ omap_uart_disable_clocks(uart);
}
static void omap_uart_idle_timer(unsigned long data)
@@ -391,35 +403,23 @@ static void omap_uart_idle_timer(unsigned long data)
void omap_uart_prepare_idle(int num)
{
- struct omap_uart_state *uart;
-
- list_for_each_entry(uart, &uart_list, node) {
- if (num == uart->num && uart->can_sleep) {
- omap_uart_disable_clocks(uart);
- return;
- }
- }
}
void omap_uart_resume_idle(int num)
{
struct omap_uart_state *uart;
+ u32 wkst;
list_for_each_entry(uart, &uart_list, node) {
if (num == uart->num && uart->can_sleep) {
- omap_uart_enable_clocks(uart);
+ omap_uart_block_sleep(uart);
- /* Check for IO pad wakeup */
- if (cpu_is_omap34xx() && uart->padconf) {
- u16 p = omap_ctrl_readw(uart->padconf);
-
- if (p & OMAP3_PADCONF_WAKEUPEVENT0)
- omap_uart_block_sleep(uart);
+ /* Check for normal UART wakeup (and clear it) */
+ if (uart->wk_st && uart->wk_mask) {
+ wkst = __raw_readl(uart->wk_st) & uart->wk_mask;
+ if (wkst)
+ __raw_writel(wkst, uart->wk_st);
}
-
- /* Check for normal UART wakeup */
- if (__raw_readl(uart->wk_st) & uart->wk_mask)
- omap_uart_block_sleep(uart);
return;
}
}
@@ -550,6 +550,8 @@ static void omap_uart_idle_init(struct omap_uart_state *uart)
ret = request_threaded_irq(uart->irq, NULL, omap_uart_interrupt,
IRQF_SHARED, "serial idle", (void *)uart);
WARN_ON(ret);
+ ret = omap_prcm_register_pad_irq(uart->padconf, uart->irq);
+ WARN_ON(ret);
}
void omap_uart_enable_irqs(int enable)
--
1.7.4.1
Texas Instruments Oy, Tekniikantie 12, 02150 Espoo. Y-tunnus: 0115040-6. Kotipaikka: Helsinki
^ permalink raw reply related [flat|nested] 29+ messages in thread
* [PATCHv3 6/6] OMAP3: Serial tty: Added resume_idle calls to critical points
2011-06-22 16:42 [PATCHv3 0/6] PRCM chain handler Tero Kristo
` (4 preceding siblings ...)
2011-06-22 16:42 ` [PATCHv3 5/6] OMAP3: Serial: Made serial to work properly with " Tero Kristo
@ 2011-06-22 16:42 ` Tero Kristo
2011-06-27 15:02 ` [PATCHv3 0/6] PRCM chain handler Tero Kristo
6 siblings, 0 replies; 29+ messages in thread
From: Tero Kristo @ 2011-06-22 16:42 UTC (permalink / raw)
To: linux-omap
Any tty access should enable UART port first if it is idle.
Signed-off-by: Tero Kristo <t-kristo@ti.com>
---
drivers/tty/serial/omap-serial.c | 8 ++++++++
1 files changed, 8 insertions(+), 0 deletions(-)
diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c
index 47cadf4..bd6ae02 100644
--- a/drivers/tty/serial/omap-serial.c
+++ b/drivers/tty/serial/omap-serial.c
@@ -261,6 +261,8 @@ static void serial_omap_start_tx(struct uart_port *port)
unsigned int start;
int ret = 0;
+ omap_uart_resume_idle(up->pdev->id);
+
if (!up->use_dma) {
serial_omap_enable_ier_thri(up);
return;
@@ -354,6 +356,8 @@ static inline irqreturn_t serial_omap_irq(int irq, void *dev_id)
unsigned int iir, lsr;
unsigned long flags;
+ omap_uart_resume_idle(up->pdev->id);
+
iir = serial_in(up, UART_IIR);
if (iir & UART_IIR_NO_INT)
return IRQ_NONE;
@@ -641,6 +645,8 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios,
unsigned long flags = 0;
unsigned int baud, quot;
+ omap_uart_resume_idle(up->pdev->id);
+
switch (termios->c_cflag & CSIZE) {
case CS5:
cval = UART_LCR_WLEN5;
@@ -947,6 +953,8 @@ serial_omap_console_write(struct console *co, const char *s,
unsigned int ier;
int locked = 1;
+ omap_uart_resume_idle(up->pdev->id);
+
local_irq_save(flags);
if (up->port.sysrq)
locked = 0;
--
1.7.4.1
Texas Instruments Oy, Tekniikantie 12, 02150 Espoo. Y-tunnus: 0115040-6. Kotipaikka: Helsinki
^ permalink raw reply related [flat|nested] 29+ messages in thread
* Re: [PATCHv3 5/6] OMAP3: Serial: Made serial to work properly with PRCM chain handler
2011-06-22 16:42 ` [PATCHv3 5/6] OMAP3: Serial: Made serial to work properly with " Tero Kristo
@ 2011-06-22 17:09 ` Tero Kristo
2011-06-23 10:30 ` Govindraj
2011-06-23 8:21 ` Tony Lindgren
2011-06-24 22:00 ` Kevin Hilman
2 siblings, 1 reply; 29+ messages in thread
From: Tero Kristo @ 2011-06-22 17:09 UTC (permalink / raw)
To: linux-omap
Hi,
There seems to be still one issue with this patch. It fails to protect
against pr_warning from omap_device.c in some cases, console_trylock()
console_unlock() hacks inside UART clock enable / disable are not enough
to protect against this. This bug can be prevented by removing the
pr_warning() calls from omap_device.c.
-Tero
On Wed, 2011-06-22 at 18:42 +0200, Kristo, Tero wrote:
> Signed-off-by: Tero Kristo <t-kristo@ti.com>
> ---
> arch/arm/mach-omap2/pm34xx.c | 19 -------------------
> arch/arm/mach-omap2/serial.c | 40 +++++++++++++++++++++-------------------
> 2 files changed, 21 insertions(+), 38 deletions(-)
>
> diff --git a/arch/arm/mach-omap2/pm34xx.c b/arch/arm/mach-omap2/pm34xx.c
> index ff0811a..6fff894 100644
> --- a/arch/arm/mach-omap2/pm34xx.c
> +++ b/arch/arm/mach-omap2/pm34xx.c
> @@ -340,18 +340,9 @@ void omap_sram_idle(void)
> omap3_enable_io_chain();
> }
>
> - /* Block console output in case it is on one of the OMAP UARTs */
> - if (!is_suspending())
> - if (per_next_state < PWRDM_POWER_ON ||
> - core_next_state < PWRDM_POWER_ON)
> - if (!console_trylock())
> - goto console_still_active;
> -
> /* PER */
> if (per_next_state < PWRDM_POWER_ON) {
> per_going_off = (per_next_state == PWRDM_POWER_OFF) ? 1 : 0;
> - omap_uart_prepare_idle(2);
> - omap_uart_prepare_idle(3);
> omap2_gpio_prepare_for_idle(per_going_off);
> if (per_next_state == PWRDM_POWER_OFF)
> omap3_per_save_context();
> @@ -359,8 +350,6 @@ void omap_sram_idle(void)
>
> /* CORE */
> if (core_next_state < PWRDM_POWER_ON) {
> - omap_uart_prepare_idle(0);
> - omap_uart_prepare_idle(1);
> if (core_next_state == PWRDM_POWER_OFF) {
> omap3_core_save_context();
> omap3_cm_save_context();
> @@ -407,8 +396,6 @@ void omap_sram_idle(void)
> omap3_sram_restore_context();
> omap2_sms_restore_context();
> }
> - omap_uart_resume_idle(0);
> - omap_uart_resume_idle(1);
> if (core_next_state == PWRDM_POWER_OFF)
> omap2_prm_clear_mod_reg_bits(OMAP3430_AUTO_OFF_MASK,
> OMAP3430_GR_MOD,
> @@ -422,14 +409,8 @@ void omap_sram_idle(void)
> omap2_gpio_resume_after_idle();
> if (per_prev_state == PWRDM_POWER_OFF)
> omap3_per_restore_context();
> - omap_uart_resume_idle(2);
> - omap_uart_resume_idle(3);
> }
>
> - if (!is_suspending())
> - console_unlock();
> -
> -console_still_active:
> /* Disable IO-PAD and IO-CHAIN wakeup */
> if (omap3_has_io_wakeup() &&
> (per_next_state < PWRDM_POWER_ON ||
> diff --git a/arch/arm/mach-omap2/serial.c b/arch/arm/mach-omap2/serial.c
> index 1ac361b..e4a5a8c 100644
> --- a/arch/arm/mach-omap2/serial.c
> +++ b/arch/arm/mach-omap2/serial.c
> @@ -39,6 +39,7 @@
> #include <plat/dma.h>
> #include <plat/omap_hwmod.h>
> #include <plat/omap_device.h>
> +#include <plat/prcm.h>
>
> #include "prm2xxx_3xxx.h"
> #include "pm.h"
> @@ -280,24 +281,34 @@ static inline void omap_uart_restore_context(struct omap_uart_state *uart) {}
>
> static inline void omap_uart_enable_clocks(struct omap_uart_state *uart)
> {
> + int locked;
> +
> if (uart->clocked)
> return;
>
> + locked = console_trylock();
> omap_device_enable(uart->pdev);
> uart->clocked = 1;
> omap_uart_restore_context(uart);
> + if (locked)
> + console_unlock();
> }
>
> #ifdef CONFIG_PM
>
> static inline void omap_uart_disable_clocks(struct omap_uart_state *uart)
> {
> + int locked;
> +
> if (!uart->clocked)
> return;
>
> + locked = console_trylock();
> omap_uart_save_context(uart);
> uart->clocked = 0;
> omap_device_idle(uart->pdev);
> + if (locked)
> + console_unlock();
> }
>
> static void omap_uart_enable_wakeup(struct omap_uart_state *uart)
> @@ -380,6 +391,7 @@ static void omap_uart_allow_sleep(struct omap_uart_state *uart)
> omap_uart_smart_idle_enable(uart, 1);
> uart->can_sleep = 1;
> del_timer(&uart->timer);
> + omap_uart_disable_clocks(uart);
> }
>
> static void omap_uart_idle_timer(unsigned long data)
> @@ -391,35 +403,23 @@ static void omap_uart_idle_timer(unsigned long data)
>
> void omap_uart_prepare_idle(int num)
> {
> - struct omap_uart_state *uart;
> -
> - list_for_each_entry(uart, &uart_list, node) {
> - if (num == uart->num && uart->can_sleep) {
> - omap_uart_disable_clocks(uart);
> - return;
> - }
> - }
> }
>
> void omap_uart_resume_idle(int num)
> {
> struct omap_uart_state *uart;
> + u32 wkst;
>
> list_for_each_entry(uart, &uart_list, node) {
> if (num == uart->num && uart->can_sleep) {
> - omap_uart_enable_clocks(uart);
> + omap_uart_block_sleep(uart);
>
> - /* Check for IO pad wakeup */
> - if (cpu_is_omap34xx() && uart->padconf) {
> - u16 p = omap_ctrl_readw(uart->padconf);
> -
> - if (p & OMAP3_PADCONF_WAKEUPEVENT0)
> - omap_uart_block_sleep(uart);
> + /* Check for normal UART wakeup (and clear it) */
> + if (uart->wk_st && uart->wk_mask) {
> + wkst = __raw_readl(uart->wk_st) & uart->wk_mask;
> + if (wkst)
> + __raw_writel(wkst, uart->wk_st);
> }
> -
> - /* Check for normal UART wakeup */
> - if (__raw_readl(uart->wk_st) & uart->wk_mask)
> - omap_uart_block_sleep(uart);
> return;
> }
> }
> @@ -550,6 +550,8 @@ static void omap_uart_idle_init(struct omap_uart_state *uart)
> ret = request_threaded_irq(uart->irq, NULL, omap_uart_interrupt,
> IRQF_SHARED, "serial idle", (void *)uart);
> WARN_ON(ret);
> + ret = omap_prcm_register_pad_irq(uart->padconf, uart->irq);
> + WARN_ON(ret);
> }
>
> void omap_uart_enable_irqs(int enable)
Texas Instruments Oy, Tekniikantie 12, 02150 Espoo. Y-tunnus: 0115040-6. Kotipaikka: Helsinki
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [PATCHv3 1/6] omap: prcm: switch to a chained IRQ handler mechanism
2011-06-22 16:42 ` [PATCHv3 1/6] omap: prcm: switch to a chained IRQ handler mechanism Tero Kristo
@ 2011-06-22 23:53 ` Kevin Hilman
2011-06-23 7:24 ` Tero Kristo
2011-06-23 8:19 ` Tony Lindgren
2011-06-24 21:02 ` Kevin Hilman
2 siblings, 1 reply; 29+ messages in thread
From: Kevin Hilman @ 2011-06-22 23:53 UTC (permalink / raw)
To: Tero Kristo; +Cc: linux-omap, Thomas Petazzoni, Avinash.H.M, Cousson, Benoit
Hi Tero,
Tero Kristo <t-kristo@ti.com> 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@ti.com>
> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
> Cc: Avinash.H.M <avinashhm@ti.com>
> Cc: Kevin Hilman <khilman@ti.com>
> Cc: Cousson, Benoit <b-cousson@ti.com>
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@ti.com>
> + * 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@ti.com>
> + * 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
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [PATCHv3 1/6] omap: prcm: switch to a chained IRQ handler mechanism
2011-06-22 23:53 ` Kevin Hilman
@ 2011-06-23 7:24 ` Tero Kristo
0 siblings, 0 replies; 29+ messages in thread
From: Tero Kristo @ 2011-06-23 7:24 UTC (permalink / raw)
To: Hilman, Kevin
Cc: linux-omap, Thomas Petazzoni, Mahadeva, Avinash, Cousson, Benoit
On Thu, 2011-06-23 at 01:53 +0200, Hilman, Kevin wrote:
> Hi Tero,
>
> Tero Kristo <t-kristo@ti.com> 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?
Actually no, I just sent this patch out as is, forgot to update this
comment. I'll update this also as I need to update this patch anyway. :)
>
> > Signed-off-by: Tero Kristo <t-kristo@ti.com>
> > Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
> > Cc: Avinash.H.M <avinashhm@ti.com>
> > Cc: Kevin Hilman <khilman@ti.com>
> > Cc: Cousson, Benoit <b-cousson@ti.com>
>
> 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.
Ok, easy one to fix.
>
> > ---
> > 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.
I tried this out and yea, you are right. It is not needed anymore. 1st
level chain handler + level_handler for them is enough to handle PRCM
interrupts properly. We still need the actual interrupt to wake up from
WFI.
>
> > + 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.)
I'll take a look at this if it is possible.
>
> > +/*
> > + * 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.
I was actually thinking something like this myself, but stayed with the
original implementation here. I'll change this part.
>
> > + 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@ti.com>
> > + * 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.
I'll remove those.
>
> > + * OMAP 4xxx Power Reset and Clock Management (PRCM) interrupt
> > + * definitions
> > + *
> > + * Written by Thomas Petazzoni <t-petazzoni@ti.com>
> > + * 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.
omap3 specific unmask functions are still needed by the omap_sram_idle
to enable wakeup sources. Or, I could just enable those manually from
the PRCM registers. I'll look up a solution for this.
>
> > +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
Texas Instruments Oy, Tekniikantie 12, 02150 Espoo. Y-tunnus: 0115040-6. Kotipaikka: Helsinki
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [PATCHv3 1/6] omap: prcm: switch to a chained IRQ handler mechanism
2011-06-22 16:42 ` [PATCHv3 1/6] omap: prcm: switch to a chained IRQ handler mechanism Tero Kristo
2011-06-22 23:53 ` Kevin Hilman
@ 2011-06-23 8:19 ` Tony Lindgren
2011-06-23 9:08 ` Tero Kristo
2011-06-24 16:00 ` Kevin Hilman
2011-06-24 21:02 ` Kevin Hilman
2 siblings, 2 replies; 29+ messages in thread
From: Tony Lindgren @ 2011-06-23 8:19 UTC (permalink / raw)
To: Tero Kristo
Cc: linux-omap, Thomas Petazzoni, Avinash.H.M, Kevin Hilman,
Cousson, Benoit
* Tero Kristo <t-kristo@ti.com> [110622 09:38]:
> 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.
Nice, this makes things more generic. Some comments below.
> +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;
> + }
> +
> + 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;
> +}
Please make omap_prcm_irq_init generic so you pass it the configuration.
Otherwise you have to add more else if cpu_is_omap code for each new omap.
Then you can add just an arch_initcall for each new omap to call
omap_prcm_irq_init. This will also make it easier to add support for
initializing things from device tree for omap_prcm_irq_init.
> +/*
> + * 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);
> + }
> +}
Please get rid of the cpu_is_omap tests here too so prcm.c is
generic for the new code added.
> +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),
...
Please note consider that this data will be coming from device
tree and will disappear from here. We won't be merging any new
data after v3.1 unless it comes from device tree. So this too
will need to be converted because we won't be able to add support
for new omaps otherwise.
Also, please Cc linux-arm-kernel too.
Regards,
Tony
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [PATCHv3 5/6] OMAP3: Serial: Made serial to work properly with PRCM chain handler
2011-06-22 16:42 ` [PATCHv3 5/6] OMAP3: Serial: Made serial to work properly with " Tero Kristo
2011-06-22 17:09 ` Tero Kristo
@ 2011-06-23 8:21 ` Tony Lindgren
2011-06-23 9:11 ` Tero Kristo
2011-06-24 22:00 ` Kevin Hilman
2 siblings, 1 reply; 29+ messages in thread
From: Tony Lindgren @ 2011-06-23 8:21 UTC (permalink / raw)
To: Tero Kristo; +Cc: linux-omap
* Tero Kristo <t-kristo@ti.com> [110622 09:38]:
> @@ -550,6 +550,8 @@ static void omap_uart_idle_init(struct omap_uart_state *uart)
> ret = request_threaded_irq(uart->irq, NULL, omap_uart_interrupt,
> IRQF_SHARED, "serial idle", (void *)uart);
> WARN_ON(ret);
> + ret = omap_prcm_register_pad_irq(uart->padconf, uart->irq);
> + WARN_ON(ret);
> }
Argh, looks like we still have direct mux register tinkering in serial.c:
$ grep "padconf = 0x" serial.c
padconf = 0x182;
padconf = 0x17a;
padconf = 0x19e;
padconf = 0x0d2;
By deducting 0x30 from the values above, these map into the following mux defines:
$ grep RX_OFFSET mux34xx.h
#define OMAP3_CONTROL_PADCONF_UART2_RX_OFFSET 0x14a
#define OMAP3_CONTROL_PADCONF_UART1_RX_OFFSET 0x152
#define OMAP3_CONTROL_PADCONF_UART3_RX_IRRX_OFFSET 0x16e
So you can make things more generic by getting rid of those and using
struct omap_mux_partition instead for omap_prcm_register_pad_irq.
The pins used are set already in serial.c with omap_hwmod_mux_init.
Otherwise we have to patch the hardcoded padconf values for every new omap.
And looks like we're already missing them for 44xx in serial.c.
Then access to the padconf registers should be done with
omap_mux_read/write instead. If you need to do something more complex
with them maybe consider adding some new functions to mux.c as needed.
Regards,
Tony
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [PATCHv3 1/6] omap: prcm: switch to a chained IRQ handler mechanism
2011-06-23 8:19 ` Tony Lindgren
@ 2011-06-23 9:08 ` Tero Kristo
2011-06-23 9:51 ` Tony Lindgren
2011-06-24 16:00 ` Kevin Hilman
1 sibling, 1 reply; 29+ messages in thread
From: Tero Kristo @ 2011-06-23 9:08 UTC (permalink / raw)
To: Tony Lindgren
Cc: linux-omap, Thomas Petazzoni, Mahadeva, Avinash, Hilman, Kevin,
Cousson, Benoit
On Thu, 2011-06-23 at 10:19 +0200, Tony Lindgren wrote:
> * Tero Kristo <t-kristo@ti.com> [110622 09:38]:
> > 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.
>
> Nice, this makes things more generic. Some comments below.
>
> > +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;
> > + }
> > +
> > + 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;
> > +}
>
> Please make omap_prcm_irq_init generic so you pass it the configuration.
> Otherwise you have to add more else if cpu_is_omap code for each new omap.
> Then you can add just an arch_initcall for each new omap to call
> omap_prcm_irq_init. This will also make it easier to add support for
> initializing things from device tree for omap_prcm_irq_init.
Yea, can do this.
>
> > +/*
> > + * 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);
> > + }
> > +}
>
> Please get rid of the cpu_is_omap tests here too so prcm.c is
> generic for the new code added.
Same.
>
> > +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),
> ...
>
> Please note consider that this data will be coming from device
> tree and will disappear from here. We won't be merging any new
> data after v3.1 unless it comes from device tree. So this too
> will need to be converted because we won't be able to add support
> for new omaps otherwise.
This part I am not too sure what you mean with this. Do you have some
info / examples about the device tree somewhere and how this data should
be converted?
>
> Also, please Cc linux-arm-kernel too.
>
> Regards,
>
> Tony
Texas Instruments Oy, Tekniikantie 12, 02150 Espoo. Y-tunnus: 0115040-6. Kotipaikka: Helsinki
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [PATCHv3 5/6] OMAP3: Serial: Made serial to work properly with PRCM chain handler
2011-06-23 8:21 ` Tony Lindgren
@ 2011-06-23 9:11 ` Tero Kristo
2011-06-23 10:00 ` Tony Lindgren
0 siblings, 1 reply; 29+ messages in thread
From: Tero Kristo @ 2011-06-23 9:11 UTC (permalink / raw)
To: Tony Lindgren; +Cc: linux-omap
On Thu, 2011-06-23 at 10:21 +0200, Tony Lindgren wrote:
> * Tero Kristo <t-kristo@ti.com> [110622 09:38]:
> > @@ -550,6 +550,8 @@ static void omap_uart_idle_init(struct omap_uart_state *uart)
> > ret = request_threaded_irq(uart->irq, NULL, omap_uart_interrupt,
> > IRQF_SHARED, "serial idle", (void *)uart);
> > WARN_ON(ret);
> > + ret = omap_prcm_register_pad_irq(uart->padconf, uart->irq);
> > + WARN_ON(ret);
> > }
>
> Argh, looks like we still have direct mux register tinkering in serial.c:
>
> $ grep "padconf = 0x" serial.c
> padconf = 0x182;
> padconf = 0x17a;
> padconf = 0x19e;
> padconf = 0x0d2;
>
> By deducting 0x30 from the values above, these map into the following mux defines:
>
> $ grep RX_OFFSET mux34xx.h
> #define OMAP3_CONTROL_PADCONF_UART2_RX_OFFSET 0x14a
> #define OMAP3_CONTROL_PADCONF_UART1_RX_OFFSET 0x152
> #define OMAP3_CONTROL_PADCONF_UART3_RX_IRRX_OFFSET 0x16e
>
> So you can make things more generic by getting rid of those and using
> struct omap_mux_partition instead for omap_prcm_register_pad_irq.
> The pins used are set already in serial.c with omap_hwmod_mux_init.
>
> Otherwise we have to patch the hardcoded padconf values for every new omap.
> And looks like we're already missing them for 44xx in serial.c.
>
> Then access to the padconf registers should be done with
> omap_mux_read/write instead. If you need to do something more complex
> with them maybe consider adding some new functions to mux.c as needed.
>
Padconf stuff is not that much related to this patch set yet (I am not
touching the serial padconf code), but I can probably take a look at
this also later on, unless you want this fixed with this set already?
> Regards,
>
> Tony
Texas Instruments Oy, Tekniikantie 12, 02150 Espoo. Y-tunnus: 0115040-6. Kotipaikka: Helsinki
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [PATCHv3 1/6] omap: prcm: switch to a chained IRQ handler mechanism
2011-06-23 9:08 ` Tero Kristo
@ 2011-06-23 9:51 ` Tony Lindgren
0 siblings, 0 replies; 29+ messages in thread
From: Tony Lindgren @ 2011-06-23 9:51 UTC (permalink / raw)
To: Tero Kristo
Cc: linux-omap, Thomas Petazzoni, Mahadeva, Avinash, Hilman, Kevin,
Cousson, Benoit
* Tero Kristo <t-kristo@ti.com> [110623 02:04]:
> On Thu, 2011-06-23 at 10:19 +0200, Tony Lindgren wrote:
> >
> > Please note consider that this data will be coming from device
> > tree and will disappear from here. We won't be merging any new
> > data after v3.1 unless it comes from device tree. So this too
> > will need to be converted because we won't be able to add support
> > for new omaps otherwise.
>
> This part I am not too sure what you mean with this. Do you have some
> info / examples about the device tree somewhere and how this data should
> be converted?
Well there's some minimal data in the devel-devicetree branch currently.
That allows you to play with it if, the last commit contains some
instructions for the devicetree append support.
Then there are tons of examples in arch/powerpc/boot/dts. Basically
we'll have something like omap34xx.dts included into the board .dts
file. So your prcm interrupt data would live in the omap34xx.dts as
would 34xx specific mux data. Then the board specific .dts contains
the configured pins etc.
Just something to consider for organizing the code so you don't have
to change things around much after the device tree support is merged :)
Regards,
Tony
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [PATCHv3 5/6] OMAP3: Serial: Made serial to work properly with PRCM chain handler
2011-06-23 9:11 ` Tero Kristo
@ 2011-06-23 10:00 ` Tony Lindgren
2011-06-23 10:35 ` Govindraj
2011-06-24 15:15 ` Kevin Hilman
0 siblings, 2 replies; 29+ messages in thread
From: Tony Lindgren @ 2011-06-23 10:00 UTC (permalink / raw)
To: Tero Kristo; +Cc: linux-omap
* Tero Kristo <t-kristo@ti.com> [110623 02:06]:
> On Thu, 2011-06-23 at 10:21 +0200, Tony Lindgren wrote:
> > * Tero Kristo <t-kristo@ti.com> [110622 09:38]:
> > > @@ -550,6 +550,8 @@ static void omap_uart_idle_init(struct omap_uart_state *uart)
> > > ret = request_threaded_irq(uart->irq, NULL, omap_uart_interrupt,
> > > IRQF_SHARED, "serial idle", (void *)uart);
> > > WARN_ON(ret);
> > > + ret = omap_prcm_register_pad_irq(uart->padconf, uart->irq);
> > > + WARN_ON(ret);
> > > }
> >
> > Argh, looks like we still have direct mux register tinkering in serial.c:
> >
> > $ grep "padconf = 0x" serial.c
> > padconf = 0x182;
> > padconf = 0x17a;
> > padconf = 0x19e;
> > padconf = 0x0d2;
> >
> > By deducting 0x30 from the values above, these map into the following mux defines:
> >
> > $ grep RX_OFFSET mux34xx.h
> > #define OMAP3_CONTROL_PADCONF_UART2_RX_OFFSET 0x14a
> > #define OMAP3_CONTROL_PADCONF_UART1_RX_OFFSET 0x152
> > #define OMAP3_CONTROL_PADCONF_UART3_RX_IRRX_OFFSET 0x16e
> >
> > So you can make things more generic by getting rid of those and using
> > struct omap_mux_partition instead for omap_prcm_register_pad_irq.
> > The pins used are set already in serial.c with omap_hwmod_mux_init.
> >
> > Otherwise we have to patch the hardcoded padconf values for every new omap.
> > And looks like we're already missing them for 44xx in serial.c.
> >
> > Then access to the padconf registers should be done with
> > omap_mux_read/write instead. If you need to do something more complex
> > with them maybe consider adding some new functions to mux.c as needed.
> >
>
> Padconf stuff is not that much related to this patch set yet (I am not
> touching the serial padconf code), but I can probably take a look at
> this also later on, unless you want this fixed with this set already?
Yes please, maybe make it a separate patch before your series to
prepare things?
Otherwise we will have to change the omap_prcm_register_pad_irq
later on in each driver using it.
Tony
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [PATCHv3 2/6] PRCM: Add support for PAD wakeup interrupts
2011-06-22 16:42 ` [PATCHv3 2/6] PRCM: Add support for PAD wakeup interrupts Tero Kristo
@ 2011-06-23 10:23 ` Govindraj
2011-06-24 21:34 ` Kevin Hilman
2011-06-24 21:21 ` Kevin Hilman
1 sibling, 1 reply; 29+ messages in thread
From: Govindraj @ 2011-06-23 10:23 UTC (permalink / raw)
To: Tero Kristo; +Cc: linux-omap
On Wed, Jun 22, 2011 at 10:12 PM, Tero Kristo <t-kristo@ti.com> wrote:
> PRCM interrupt handler will now parse registered pads to see whether there
> is an active wakeup event. If this is the case, the corresponding interrupt
> will be triggered. This can be used for example with UART driver to register
> PAD wakeup event for the UART RX pin, and when this happens, UART interrupt
> will be triggered.
>
> Signed-off-by: Tero Kristo <t-kristo@ti.com>
> ---
> arch/arm/mach-omap2/prcm.c | 50 ++++++++++++++++++++++++++++++++
> arch/arm/plat-omap/include/plat/prcm.h | 1 +
> 2 files changed, 51 insertions(+), 0 deletions(-)
>
> diff --git a/arch/arm/mach-omap2/prcm.c b/arch/arm/mach-omap2/prcm.c
> index 362c59c..cc92064 100644
> --- a/arch/arm/mach-omap2/prcm.c
> +++ b/arch/arm/mach-omap2/prcm.c
> @@ -53,6 +53,15 @@ static struct omap_prcm_irq *omap_prcm_irqs;
> /* Number of entries in omap_prcm_irqs */
> static int omap_prcm_irqs_nr;
>
> +/* PAD handlers list */
> +struct pad_def {
> + u32 pad;
> + unsigned int irq;
> + struct list_head node;
> +};
> +
> +static LIST_HEAD(pad_handler_list);
> +
> /* Pointers to either OMAP3 or OMAP4 specific functions */
> static void (*omap_prcm_mask_event)(unsigned event);
> static void (*omap_prcm_unmask_event)(unsigned event);
> @@ -84,6 +93,25 @@ static struct irq_chip prcm_irq_chip = {
> .irq_unmask = prcm_irq_unmask,
> };
>
> +
> +/*
> + * Handler for PAD irqs, called from PRCM interrupt handler
> + */
> +static void omap_prcm_handle_pad_irqs(void)
> +{
> + struct pad_def *def;
> + u16 val = 0;
> + list_for_each_entry(def, &pad_handler_list, node) {
> + /* Read padconf value based on cpu type */
> + if (cpu_is_omap34xx())
> + val = omap_ctrl_readw(def->pad);
> +
> + /* if pad wakeupevent is active, fire registered IRQ */
> + if (val & OMAP3_PADCONF_WAKEUPEVENT0)
> + generic_handle_irq(def->irq);
> + }
> +}
> +
> /*
> * PRCM Interrupt Handler
> *
> @@ -106,6 +134,9 @@ 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);
>
> + /* Handle PAD events first, we don't want to ack them before parse */
> + omap_prcm_handle_pad_irqs();
> +
> /*
> * Loop until all pending irqs are handled, since
> * generic_handle_irq(), called by prcm_irq_handle_virtirqs()
> @@ -153,6 +184,25 @@ int omap_prcm_event_to_irq(const char *name)
> }
>
> /*
> + * Register interrupt handler for a given pad. When the PRCM interrupt
> + * handler detects wakeupevent on the corresponding pad, the IRQ will
> + * be triggered.
> + */
> +int omap_prcm_register_pad_irq(u32 pad, unsigned int irq)
I tested this v3 series seems to work fine.
Minor comments.
Can we make the prcm_register interface params with mux_name
or
even omap_hmwod and with hwmod api's check
wake-up event through a mux api as here [1]
rather than passing pad offset.
since I planning to test uart-runtime changes with irq_chaining,
pad offsets are no more available with uart-runtime
and uses hmwod_mux api's.
--
Thanks,
Govindraj.R
[1] https://patchwork.kernel.org/patch/773932/
> +{
> + struct pad_def *def;
> +
> + def = kmalloc(sizeof(struct pad_def), GFP_ATOMIC);
> + if (!def)
> + return -ENOMEM;
> +
> + def->pad = pad;
> + def->irq = irq;
> + list_add(&def->node, &pad_handler_list);
> + return 0;
> +}
> +
> +/*
> * Prepare the array of PRCM events corresponding to the current SoC,
> * and set-up the chained interrupt handler mechanism.
> */
> diff --git a/arch/arm/plat-omap/include/plat/prcm.h b/arch/arm/plat-omap/include/plat/prcm.h
> index 5785555..789eb17 100644
> --- a/arch/arm/plat-omap/include/plat/prcm.h
> +++ b/arch/arm/plat-omap/include/plat/prcm.h
> @@ -72,6 +72,7 @@ void omap4_prcm_pending_events(unsigned long *pending);
> int omap_prcm_event_to_irq(const char *name);
> int omap_prcm_irq_init(void);
> void omap_prcm_irq_cleanup(void);
> +int omap_prcm_register_pad_irq(u32 pad, unsigned int irq);
> u32 omap_prcm_get_reset_sources(void);
> int omap2_cm_wait_idlest(void __iomem *reg, u32 mask, u8 idlest,
> const char *name);
> --
> 1.7.4.1
>
>
> Texas Instruments Oy, Tekniikantie 12, 02150 Espoo. Y-tunnus: 0115040-6. Kotipaikka: Helsinki
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-omap" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [PATCHv3 5/6] OMAP3: Serial: Made serial to work properly with PRCM chain handler
2011-06-22 17:09 ` Tero Kristo
@ 2011-06-23 10:30 ` Govindraj
0 siblings, 0 replies; 29+ messages in thread
From: Govindraj @ 2011-06-23 10:30 UTC (permalink / raw)
To: t-kristo; +Cc: linux-omap
On Wed, Jun 22, 2011 at 10:39 PM, Tero Kristo <t-kristo@ti.com> wrote:
> Hi,
>
> There seems to be still one issue with this patch. It fails to protect
> against pr_warning from omap_device.c in some cases, console_trylock()
> console_unlock() hacks inside UART clock enable / disable are not enough
> to protect against this. This bug can be prevented by removing the
> pr_warning() calls from omap_device.c.
>
This is handled with uart time patches in serial_omap_console_write
with console_lock which is the right place.
--
Thanks,
Govindraj.R
> -Tero
>
> On Wed, 2011-06-22 at 18:42 +0200, Kristo, Tero wrote:
>> Signed-off-by: Tero Kristo <t-kristo@ti.com>
>> ---
>> arch/arm/mach-omap2/pm34xx.c | 19 -------------------
>> arch/arm/mach-omap2/serial.c | 40 +++++++++++++++++++++-------------------
>> 2 files changed, 21 insertions(+), 38 deletions(-)
>>
>> diff --git a/arch/arm/mach-omap2/pm34xx.c b/arch/arm/mach-omap2/pm34xx.c
>> index ff0811a..6fff894 100644
>> --- a/arch/arm/mach-omap2/pm34xx.c
>> +++ b/arch/arm/mach-omap2/pm34xx.c
>> @@ -340,18 +340,9 @@ void omap_sram_idle(void)
>> omap3_enable_io_chain();
>> }
>>
>> - /* Block console output in case it is on one of the OMAP UARTs */
>> - if (!is_suspending())
>> - if (per_next_state < PWRDM_POWER_ON ||
>> - core_next_state < PWRDM_POWER_ON)
>> - if (!console_trylock())
>> - goto console_still_active;
>> -
>> /* PER */
>> if (per_next_state < PWRDM_POWER_ON) {
>> per_going_off = (per_next_state == PWRDM_POWER_OFF) ? 1 : 0;
>> - omap_uart_prepare_idle(2);
>> - omap_uart_prepare_idle(3);
>> omap2_gpio_prepare_for_idle(per_going_off);
>> if (per_next_state == PWRDM_POWER_OFF)
>> omap3_per_save_context();
>> @@ -359,8 +350,6 @@ void omap_sram_idle(void)
>>
>> /* CORE */
>> if (core_next_state < PWRDM_POWER_ON) {
>> - omap_uart_prepare_idle(0);
>> - omap_uart_prepare_idle(1);
>> if (core_next_state == PWRDM_POWER_OFF) {
>> omap3_core_save_context();
>> omap3_cm_save_context();
>> @@ -407,8 +396,6 @@ void omap_sram_idle(void)
>> omap3_sram_restore_context();
>> omap2_sms_restore_context();
>> }
>> - omap_uart_resume_idle(0);
>> - omap_uart_resume_idle(1);
>> if (core_next_state == PWRDM_POWER_OFF)
>> omap2_prm_clear_mod_reg_bits(OMAP3430_AUTO_OFF_MASK,
>> OMAP3430_GR_MOD,
>> @@ -422,14 +409,8 @@ void omap_sram_idle(void)
>> omap2_gpio_resume_after_idle();
>> if (per_prev_state == PWRDM_POWER_OFF)
>> omap3_per_restore_context();
>> - omap_uart_resume_idle(2);
>> - omap_uart_resume_idle(3);
>> }
>>
>> - if (!is_suspending())
>> - console_unlock();
>> -
>> -console_still_active:
>> /* Disable IO-PAD and IO-CHAIN wakeup */
>> if (omap3_has_io_wakeup() &&
>> (per_next_state < PWRDM_POWER_ON ||
>> diff --git a/arch/arm/mach-omap2/serial.c b/arch/arm/mach-omap2/serial.c
>> index 1ac361b..e4a5a8c 100644
>> --- a/arch/arm/mach-omap2/serial.c
>> +++ b/arch/arm/mach-omap2/serial.c
>> @@ -39,6 +39,7 @@
>> #include <plat/dma.h>
>> #include <plat/omap_hwmod.h>
>> #include <plat/omap_device.h>
>> +#include <plat/prcm.h>
>>
>> #include "prm2xxx_3xxx.h"
>> #include "pm.h"
>> @@ -280,24 +281,34 @@ static inline void omap_uart_restore_context(struct omap_uart_state *uart) {}
>>
>> static inline void omap_uart_enable_clocks(struct omap_uart_state *uart)
>> {
>> + int locked;
>> +
>> if (uart->clocked)
>> return;
>>
>> + locked = console_trylock();
>> omap_device_enable(uart->pdev);
>> uart->clocked = 1;
>> omap_uart_restore_context(uart);
>> + if (locked)
>> + console_unlock();
>> }
>>
>> #ifdef CONFIG_PM
>>
>> static inline void omap_uart_disable_clocks(struct omap_uart_state *uart)
>> {
>> + int locked;
>> +
>> if (!uart->clocked)
>> return;
>>
>> + locked = console_trylock();
>> omap_uart_save_context(uart);
>> uart->clocked = 0;
>> omap_device_idle(uart->pdev);
>> + if (locked)
>> + console_unlock();
>> }
>>
>> static void omap_uart_enable_wakeup(struct omap_uart_state *uart)
>> @@ -380,6 +391,7 @@ static void omap_uart_allow_sleep(struct omap_uart_state *uart)
>> omap_uart_smart_idle_enable(uart, 1);
>> uart->can_sleep = 1;
>> del_timer(&uart->timer);
>> + omap_uart_disable_clocks(uart);
>> }
>>
>> static void omap_uart_idle_timer(unsigned long data)
>> @@ -391,35 +403,23 @@ static void omap_uart_idle_timer(unsigned long data)
>>
>> void omap_uart_prepare_idle(int num)
>> {
>> - struct omap_uart_state *uart;
>> -
>> - list_for_each_entry(uart, &uart_list, node) {
>> - if (num == uart->num && uart->can_sleep) {
>> - omap_uart_disable_clocks(uart);
>> - return;
>> - }
>> - }
>> }
>>
>> void omap_uart_resume_idle(int num)
>> {
>> struct omap_uart_state *uart;
>> + u32 wkst;
>>
>> list_for_each_entry(uart, &uart_list, node) {
>> if (num == uart->num && uart->can_sleep) {
>> - omap_uart_enable_clocks(uart);
>> + omap_uart_block_sleep(uart);
>>
>> - /* Check for IO pad wakeup */
>> - if (cpu_is_omap34xx() && uart->padconf) {
>> - u16 p = omap_ctrl_readw(uart->padconf);
>> -
>> - if (p & OMAP3_PADCONF_WAKEUPEVENT0)
>> - omap_uart_block_sleep(uart);
>> + /* Check for normal UART wakeup (and clear it) */
>> + if (uart->wk_st && uart->wk_mask) {
>> + wkst = __raw_readl(uart->wk_st) & uart->wk_mask;
>> + if (wkst)
>> + __raw_writel(wkst, uart->wk_st);
>> }
>> -
>> - /* Check for normal UART wakeup */
>> - if (__raw_readl(uart->wk_st) & uart->wk_mask)
>> - omap_uart_block_sleep(uart);
>> return;
>> }
>> }
>> @@ -550,6 +550,8 @@ static void omap_uart_idle_init(struct omap_uart_state *uart)
>> ret = request_threaded_irq(uart->irq, NULL, omap_uart_interrupt,
>> IRQF_SHARED, "serial idle", (void *)uart);
>> WARN_ON(ret);
>> + ret = omap_prcm_register_pad_irq(uart->padconf, uart->irq);
>> + WARN_ON(ret);
>> }
>>
>> void omap_uart_enable_irqs(int enable)
>
>
>
> Texas Instruments Oy, Tekniikantie 12, 02150 Espoo. Y-tunnus: 0115040-6. Kotipaikka: Helsinki
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-omap" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [PATCHv3 5/6] OMAP3: Serial: Made serial to work properly with PRCM chain handler
2011-06-23 10:00 ` Tony Lindgren
@ 2011-06-23 10:35 ` Govindraj
2011-06-23 11:12 ` Tony Lindgren
2011-06-24 15:15 ` Kevin Hilman
1 sibling, 1 reply; 29+ messages in thread
From: Govindraj @ 2011-06-23 10:35 UTC (permalink / raw)
To: Tony Lindgren; +Cc: Tero Kristo, linux-omap
On Thu, Jun 23, 2011 at 3:30 PM, Tony Lindgren <tony@atomide.com> wrote:
> * Tero Kristo <t-kristo@ti.com> [110623 02:06]:
>> On Thu, 2011-06-23 at 10:21 +0200, Tony Lindgren wrote:
>> > * Tero Kristo <t-kristo@ti.com> [110622 09:38]:
>> > > @@ -550,6 +550,8 @@ static void omap_uart_idle_init(struct omap_uart_state *uart)
>> > > ret = request_threaded_irq(uart->irq, NULL, omap_uart_interrupt,
>> > > IRQF_SHARED, "serial idle", (void *)uart);
>> > > WARN_ON(ret);
>> > > + ret = omap_prcm_register_pad_irq(uart->padconf, uart->irq);
>> > > + WARN_ON(ret);
>> > > }
>> >
>> > Argh, looks like we still have direct mux register tinkering in serial.c:
>> >
>> > $ grep "padconf = 0x" serial.c
>> > padconf = 0x182;
>> > padconf = 0x17a;
>> > padconf = 0x19e;
>> > padconf = 0x0d2;
>> >
>> > By deducting 0x30 from the values above, these map into the following mux defines:
>> >
>> > $ grep RX_OFFSET mux34xx.h
>> > #define OMAP3_CONTROL_PADCONF_UART2_RX_OFFSET 0x14a
>> > #define OMAP3_CONTROL_PADCONF_UART1_RX_OFFSET 0x152
>> > #define OMAP3_CONTROL_PADCONF_UART3_RX_IRRX_OFFSET 0x16e
>> >
>> > So you can make things more generic by getting rid of those and using
>> > struct omap_mux_partition instead for omap_prcm_register_pad_irq.
>> > The pins used are set already in serial.c with omap_hwmod_mux_init.
>> >
>> > Otherwise we have to patch the hardcoded padconf values for every new omap.
>> > And looks like we're already missing them for 44xx in serial.c.
>> >
>> > Then access to the padconf registers should be done with
>> > omap_mux_read/write instead. If you need to do something more complex
>> > with them maybe consider adding some new functions to mux.c as needed.
>> >
>>
>> Padconf stuff is not that much related to this patch set yet (I am not
>> touching the serial padconf code), but I can probably take a look at
>> this also later on, unless you want this fixed with this set already?
>
> Yes please, maybe make it a separate patch before your series to
> prepare things?
>
> Otherwise we will have to change the omap_prcm_register_pad_irq
> later on in each driver using it.
>
Agree,
I am planning of use this call from omap-serial driver. :)
How about using omap_hwmod_mux_get_wake_status proposed earlier?
https://patchwork.kernel.org/patch/773932/
How about register with omap_hwmod data and irq_number from driver
after requesting irq ?
I suppose even using omap_hwmod data from driver file not allowed.
or use pdev ?
--
Thanks,
Govindraj.R
> Tony
> --
> To unsubscribe from this list: send the line "unsubscribe linux-omap" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [PATCHv3 5/6] OMAP3: Serial: Made serial to work properly with PRCM chain handler
2011-06-23 10:35 ` Govindraj
@ 2011-06-23 11:12 ` Tony Lindgren
0 siblings, 0 replies; 29+ messages in thread
From: Tony Lindgren @ 2011-06-23 11:12 UTC (permalink / raw)
To: Govindraj; +Cc: Tero Kristo, linux-omap
* Govindraj <govindraj.ti@gmail.com> [110623 03:30]:
> On Thu, Jun 23, 2011 at 3:30 PM, Tony Lindgren <tony@atomide.com> wrote:
> > * Tero Kristo <t-kristo@ti.com> [110623 02:06]:
> >> On Thu, 2011-06-23 at 10:21 +0200, Tony Lindgren wrote:
> >> > * Tero Kristo <t-kristo@ti.com> [110622 09:38]:
> >> > > @@ -550,6 +550,8 @@ static void omap_uart_idle_init(struct omap_uart_state *uart)
> >> > > ret = request_threaded_irq(uart->irq, NULL, omap_uart_interrupt,
> >> > > IRQF_SHARED, "serial idle", (void *)uart);
> >> > > WARN_ON(ret);
> >> > > + ret = omap_prcm_register_pad_irq(uart->padconf, uart->irq);
> >> > > + WARN_ON(ret);
> >> > > }
> >> >
> >> > Argh, looks like we still have direct mux register tinkering in serial.c:
> >> >
> >> > $ grep "padconf = 0x" serial.c
> >> > padconf = 0x182;
> >> > padconf = 0x17a;
> >> > padconf = 0x19e;
> >> > padconf = 0x0d2;
> >> >
> >> > By deducting 0x30 from the values above, these map into the following mux defines:
> >> >
> >> > $ grep RX_OFFSET mux34xx.h
> >> > #define OMAP3_CONTROL_PADCONF_UART2_RX_OFFSET 0x14a
> >> > #define OMAP3_CONTROL_PADCONF_UART1_RX_OFFSET 0x152
> >> > #define OMAP3_CONTROL_PADCONF_UART3_RX_IRRX_OFFSET 0x16e
> >> >
> >> > So you can make things more generic by getting rid of those and using
> >> > struct omap_mux_partition instead for omap_prcm_register_pad_irq.
> >> > The pins used are set already in serial.c with omap_hwmod_mux_init.
> >> >
> >> > Otherwise we have to patch the hardcoded padconf values for every new omap.
> >> > And looks like we're already missing them for 44xx in serial.c.
> >> >
> >> > Then access to the padconf registers should be done with
> >> > omap_mux_read/write instead. If you need to do something more complex
> >> > with them maybe consider adding some new functions to mux.c as needed.
> >> >
> >>
> >> Padconf stuff is not that much related to this patch set yet (I am not
> >> touching the serial padconf code), but I can probably take a look at
> >> this also later on, unless you want this fixed with this set already?
> >
> > Yes please, maybe make it a separate patch before your series to
> > prepare things?
> >
> > Otherwise we will have to change the omap_prcm_register_pad_irq
> > later on in each driver using it.
> >
>
> Agree,
> I am planning of use this call from omap-serial driver. :)
>
> How about using omap_hwmod_mux_get_wake_status proposed earlier?
>
> https://patchwork.kernel.org/patch/773932/
Hmm, usually there's only one wake-up enabled pin though. Do we
ever have more than one per device? If not, there's no need to
iterate over the list every time.
> How about register with omap_hwmod data and irq_number from driver
> after requesting irq ?
>
> I suppose even using omap_hwmod data from driver file not allowed.
> or use pdev ?
Eventually the configured pins come from device tree and then hwmod
code can set up things automagically for the device. I think drivers
should just use device_set_wakeup_enable to set it and then the wake-up
capability should change automatically somehow.. Or maybe I did not
quite understand what you're after.
Tony
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [PATCHv3 5/6] OMAP3: Serial: Made serial to work properly with PRCM chain handler
2011-06-23 10:00 ` Tony Lindgren
2011-06-23 10:35 ` Govindraj
@ 2011-06-24 15:15 ` Kevin Hilman
1 sibling, 0 replies; 29+ messages in thread
From: Kevin Hilman @ 2011-06-24 15:15 UTC (permalink / raw)
To: Tony Lindgren; +Cc: Tero Kristo, linux-omap
Tony Lindgren <tony@atomide.com> writes:
> * Tero Kristo <t-kristo@ti.com> [110623 02:06]:
>> On Thu, 2011-06-23 at 10:21 +0200, Tony Lindgren wrote:
>> > * Tero Kristo <t-kristo@ti.com> [110622 09:38]:
>> > > @@ -550,6 +550,8 @@ static void omap_uart_idle_init(struct omap_uart_state *uart)
>> > > ret = request_threaded_irq(uart->irq, NULL, omap_uart_interrupt,
>> > > IRQF_SHARED, "serial idle", (void *)uart);
>> > > WARN_ON(ret);
>> > > + ret = omap_prcm_register_pad_irq(uart->padconf, uart->irq);
>> > > + WARN_ON(ret);
>> > > }
>> >
>> > Argh, looks like we still have direct mux register tinkering in serial.c:
>> >
>> > $ grep "padconf = 0x" serial.c
>> > padconf = 0x182;
>> > padconf = 0x17a;
>> > padconf = 0x19e;
>> > padconf = 0x0d2;
>> >
>> > By deducting 0x30 from the values above, these map into the following mux defines:
>> >
>> > $ grep RX_OFFSET mux34xx.h
>> > #define OMAP3_CONTROL_PADCONF_UART2_RX_OFFSET 0x14a
>> > #define OMAP3_CONTROL_PADCONF_UART1_RX_OFFSET 0x152
>> > #define OMAP3_CONTROL_PADCONF_UART3_RX_IRRX_OFFSET 0x16e
>> >
>> > So you can make things more generic by getting rid of those and using
>> > struct omap_mux_partition instead for omap_prcm_register_pad_irq.
>> > The pins used are set already in serial.c with omap_hwmod_mux_init.
>> >
>> > Otherwise we have to patch the hardcoded padconf values for every new omap.
>> > And looks like we're already missing them for 44xx in serial.c.
>> >
>> > Then access to the padconf registers should be done with
>> > omap_mux_read/write instead. If you need to do something more complex
>> > with them maybe consider adding some new functions to mux.c as needed.
>> >
>>
>> Padconf stuff is not that much related to this patch set yet (I am not
>> touching the serial padconf code), but I can probably take a look at
>> this also later on, unless you want this fixed with this set already?
>
> Yes please, maybe make it a separate patch before your series to
> prepare things?
>
> Otherwise we will have to change the omap_prcm_register_pad_irq
> later on in each driver using it.
Govindraj's UART runtime PM series is addressing this so Tero shouldn't
need to make those changes as part of this series.
IOW, $SUBJECT patch is only temporary until the UART runtime PM series
is ready.
Govindraj, are you able to use this series with your runtime PM
conversion?
Kevin
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [PATCHv3 1/6] omap: prcm: switch to a chained IRQ handler mechanism
2011-06-23 8:19 ` Tony Lindgren
2011-06-23 9:08 ` Tero Kristo
@ 2011-06-24 16:00 ` Kevin Hilman
1 sibling, 0 replies; 29+ messages in thread
From: Kevin Hilman @ 2011-06-24 16:00 UTC (permalink / raw)
To: Tony Lindgren
Cc: Tero Kristo, linux-omap, Thomas Petazzoni, Avinash.H.M,
Cousson, Benoit
Tony Lindgren <tony@atomide.com> writes:
> * Tero Kristo <t-kristo@ti.com> [110622 09:38]:
[...]
>> +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),
> ...
>
> Please note consider that this data will be coming from device
> tree and will disappear from here. We won't be merging any new
> data after v3.1 unless it comes from device tree.
Personally, I don't think this qualifies as device-tree data. At least
not yet, since the device tree does not currently handle this type of
thing since it's not a real interrupt controller.
Even for the newly proposed (but not merged) irq_domain stuff in the
device tree to work, there needs to be an irq_desc for each of the
"linux IRQs" (to use the term from the irq_domain stuff), so the above
mapping of names needs to exist in order to create the irq_descs so that
irq_domains can even work.
Also, until the rest of our IRQ infrastructure is converted to device
tree (which will also need irq_domains), and until there is a way to
describe the PRCM in the device tree, I don't think it makes sense to
use device tree to describe this small amount of data (which is also
__initdata.)
> So this too will need to be converted because we won't be able to add
> support for new omaps otherwise.
For small amounts of data like this, I think we can.
As long as we're working on the device tree for the big items, I think
we can make a case that items like this belong in the kernel. In my
opinion, just because it's data, doesn't mean it always belongs in the
device tree. Some kinds of data will always be needed to create
infrastructure even for the device tree to work. I think interrupts are
one of those items.
Kevin
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [PATCHv3 1/6] omap: prcm: switch to a chained IRQ handler mechanism
2011-06-22 16:42 ` [PATCHv3 1/6] omap: prcm: switch to a chained IRQ handler mechanism Tero Kristo
2011-06-22 23:53 ` Kevin Hilman
2011-06-23 8:19 ` Tony Lindgren
@ 2011-06-24 21:02 ` Kevin Hilman
2 siblings, 0 replies; 29+ messages in thread
From: Kevin Hilman @ 2011-06-24 21:02 UTC (permalink / raw)
To: Tero Kristo; +Cc: linux-omap, Thomas Petazzoni, Avinash.H.M, Cousson, Benoit
Tero Kristo <t-kristo@ti.com> 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.
>
> Signed-off-by: Tero Kristo <t-kristo@ti.com>
> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
> Cc: Avinash.H.M <avinashhm@ti.com>
> Cc: Kevin Hilman <khilman@ti.com>
> Cc: Cousson, Benoit <b-cousson@ti.com>
Another minor comment/request:
Can you see if you can keep the chip_is checking out of the generic code
and only in the SoC specific code. Using the register approach I
mentioned earlier, it should be easy to do the filtering in SoC specific
code before registering so the generic driver doesn't have to care.
IOW, all SoC-specific stuff is done in SoC-specific init and all that is
registered with the generic code is name/offset pairs.
Kevin
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [PATCHv3 2/6] PRCM: Add support for PAD wakeup interrupts
2011-06-22 16:42 ` [PATCHv3 2/6] PRCM: Add support for PAD wakeup interrupts Tero Kristo
2011-06-23 10:23 ` Govindraj
@ 2011-06-24 21:21 ` Kevin Hilman
1 sibling, 0 replies; 29+ messages in thread
From: Kevin Hilman @ 2011-06-24 21:21 UTC (permalink / raw)
To: Tero Kristo; +Cc: linux-omap
Tero Kristo <t-kristo@ti.com> writes:
> PRCM interrupt handler will now parse registered pads to see whether there
> is an active wakeup event. If this is the case, the corresponding interrupt
> will be triggered. This can be used for example with UART driver to register
> PAD wakeup event for the UART RX pin, and when this happens, UART interrupt
> will be triggered.
>
> Signed-off-by: Tero Kristo <t-kristo@ti.com>
> ---
> arch/arm/mach-omap2/prcm.c | 50 ++++++++++++++++++++++++++++++++
> arch/arm/plat-omap/include/plat/prcm.h | 1 +
> 2 files changed, 51 insertions(+), 0 deletions(-)
>
> diff --git a/arch/arm/mach-omap2/prcm.c b/arch/arm/mach-omap2/prcm.c
> index 362c59c..cc92064 100644
> --- a/arch/arm/mach-omap2/prcm.c
> +++ b/arch/arm/mach-omap2/prcm.c
> @@ -53,6 +53,15 @@ static struct omap_prcm_irq *omap_prcm_irqs;
> /* Number of entries in omap_prcm_irqs */
> static int omap_prcm_irqs_nr;
>
> +/* PAD handlers list */
> +struct pad_def {
> + u32 pad;
> + unsigned int irq;
> + struct list_head node;
> +};
> +
> +static LIST_HEAD(pad_handler_list);
> +
> /* Pointers to either OMAP3 or OMAP4 specific functions */
> static void (*omap_prcm_mask_event)(unsigned event);
> static void (*omap_prcm_unmask_event)(unsigned event);
> @@ -84,6 +93,25 @@ static struct irq_chip prcm_irq_chip = {
> .irq_unmask = prcm_irq_unmask,
> };
>
> +
> +/*
> + * Handler for PAD irqs, called from PRCM interrupt handler
> + */
> +static void omap_prcm_handle_pad_irqs(void)
> +{
> + struct pad_def *def;
> + u16 val = 0;
minor: insert blank line between variables and code
> + list_for_each_entry(def, &pad_handler_list, node) {
> + /* Read padconf value based on cpu type */
> + if (cpu_is_omap34xx())
> + val = omap_ctrl_readw(def->pad);
We need to avoid cpu_is_* checks in driver code.
What we'll need is another control-module API for reading a padconf, and
the SoC-specific implementation can be managed by the control module code.
> + /* if pad wakeupevent is active, fire registered IRQ */
> + if (val & OMAP3_PADCONF_WAKEUPEVENT0)
Does this need to be masked with the wakeup enable? IOW, is it possible
to find an pad with a masked wakeup event? Might be worth double
checking the TRM on that one, but masking with the enable would not hurt
here.
> + generic_handle_irq(def->irq);
Nice, I wish I thought of that! I was trying to figure out how to SW
trigger a hard IRQ, but this is much better and doesn't rely on any
hardware feature. Sometimes the simplest solution is the best. :)
> + }
> +}
> +
> /*
> * PRCM Interrupt Handler
> *
> @@ -106,6 +134,9 @@ 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);
>
> + /* Handle PAD events first, we don't want to ack them before parse */
> + omap_prcm_handle_pad_irqs();
> +
Handling here is fine, but it's not terribly clear on first read where
the events are eventually cleared.
I see now that they are cleared upon entry into the idle path, but I
think it would help readability if they were cleared after this handler
runs.
> /*
> * Loop until all pending irqs are handled, since
> * generic_handle_irq(), called by prcm_irq_handle_virtirqs()
> @@ -153,6 +184,25 @@ int omap_prcm_event_to_irq(const char *name)
> }
>
> /*
> + * Register interrupt handler for a given pad. When the PRCM interrupt
> + * handler detects wakeupevent on the corresponding pad, the IRQ will
> + * be triggered.
This comment needs a minor change: technically, it's not the IRQ that is
triggered but the ISR for that IRQ will be called.
The same change should be made in the changelog.
> + */
> +int omap_prcm_register_pad_irq(u32 pad, unsigned int irq)
> +{
> + struct pad_def *def;
> +
> + def = kmalloc(sizeof(struct pad_def), GFP_ATOMIC);
> + if (!def)
> + return -ENOMEM;
> +
> + def->pad = pad;
> + def->irq = irq;
> + list_add(&def->node, &pad_handler_list);
> + return 0;
> +}
This wants an unregister counterpart.
> +/*
> * Prepare the array of PRCM events corresponding to the current SoC,
> * and set-up the chained interrupt handler mechanism.
> */
> diff --git a/arch/arm/plat-omap/include/plat/prcm.h b/arch/arm/plat-omap/include/plat/prcm.h
> index 5785555..789eb17 100644
> --- a/arch/arm/plat-omap/include/plat/prcm.h
> +++ b/arch/arm/plat-omap/include/plat/prcm.h
> @@ -72,6 +72,7 @@ void omap4_prcm_pending_events(unsigned long *pending);
> int omap_prcm_event_to_irq(const char *name);
> int omap_prcm_irq_init(void);
> void omap_prcm_irq_cleanup(void);
> +int omap_prcm_register_pad_irq(u32 pad, unsigned int irq);
> u32 omap_prcm_get_reset_sources(void);
> int omap2_cm_wait_idlest(void __iomem *reg, u32 mask, u8 idlest,
> const char *name);
Kevin
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [PATCHv3 2/6] PRCM: Add support for PAD wakeup interrupts
2011-06-23 10:23 ` Govindraj
@ 2011-06-24 21:34 ` Kevin Hilman
0 siblings, 0 replies; 29+ messages in thread
From: Kevin Hilman @ 2011-06-24 21:34 UTC (permalink / raw)
To: Govindraj; +Cc: Tero Kristo, linux-omap
Govindraj <govindraj.ti@gmail.com> writes:
> On Wed, Jun 22, 2011 at 10:12 PM, Tero Kristo <t-kristo@ti.com> wrote:
>> PRCM interrupt handler will now parse registered pads to see whether there
>> is an active wakeup event. If this is the case, the corresponding interrupt
>> will be triggered. This can be used for example with UART driver to register
>> PAD wakeup event for the UART RX pin, and when this happens, UART interrupt
>> will be triggered.
>>
>> Signed-off-by: Tero Kristo <t-kristo@ti.com>
[...]
>> @@ -153,6 +184,25 @@ int omap_prcm_event_to_irq(const char *name)
>> }
>>
>> /*
>> + * Register interrupt handler for a given pad. When the PRCM interrupt
>> + * handler detects wakeupevent on the corresponding pad, the IRQ will
>> + * be triggered.
>> + */
>> +int omap_prcm_register_pad_irq(u32 pad, unsigned int irq)
>
> I tested this v3 series seems to work fine.
>
> Minor comments.
>
> Can we make the prcm_register interface params with mux_name
> or
> even omap_hmwod and with hwmod api's check
> wake-up event through a mux api as here [1]
> rather than passing pad offset.
Hmm, good point.
In fact, each omap_hwmod already knows about the pads and the IRQ for
each IP block, so another API to create that mapping isn't really
needed.
Instead, what we need is simply a way register an omap_hwmod with the
PRCM layer to indicate that it should trigger the IRQ whenever a wakup
is detected. Then, when a wakeup event is detected, it could call
a new omap_hwmod_mux_* function which would use the IRQ in the hwmod and
call generic_handle_irq().
I think adding to Govindraj's patch below[1] with a new API to handle
the irq: omap_hwmod_mux_handle_irq() or something similar would be the
right way to handle this.
> since I planning to test uart-runtime changes with irq_chaining,
> pad offsets are no more available with uart-runtime
> and uses hmwod_mux api's.
>
> --
> Thanks,
> Govindraj.R
>
> [1] https://patchwork.kernel.org/patch/773932/
Kevin
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [PATCHv3 4/6] OMAP3: PM: Use PRCM chain handler
2011-06-22 16:42 ` [PATCHv3 4/6] OMAP3: PM: Use PRCM chain handler Tero Kristo
@ 2011-06-24 21:57 ` Kevin Hilman
0 siblings, 0 replies; 29+ messages in thread
From: Kevin Hilman @ 2011-06-24 21:57 UTC (permalink / raw)
To: Tero Kristo; +Cc: linux-omap
Tero Kristo <t-kristo@ti.com> writes:
> PRCM interrupts are now handled with the chained handler mechanism. This patch
> also changes the PRCM interrupts to be of one-shot type,
Not sure I understand the change to one-shot. But since I don't think
you need the handlers anymore, I don't think this change matters.
> and the interrupt
> does not clear the wakeup statuses anymore. Clearing of the wakeup interrupts
> is done just before entering idle, as we probably have more time to do this
> during this time.
Possibly, but if events are not cleared and another PRCM IRQ fires
before the idle path, won't the ISR for that event be triggered again
unncessarily?
Personally, I would much rather see the PRCM IRQ handling code be
responsible for clearning the events (as it is today) instead having it
delegated to the idle task. It is difficult to follow when reading
and...
> Changing the wakeup handling logic also fixes an issue with the
> chained PRCM serial interrupts, that prevents clearing of the UART
> wakeup status and hangs the device.
...and seems to be masking a bug in the UART code someplace.
>
> Signed-off-by: Tero Kristo <t-kristo@ti.com>
> ---
> arch/arm/mach-omap2/pm34xx.c | 31 ++++++++++++++++++-------------
> 1 files changed, 18 insertions(+), 13 deletions(-)
>
> diff --git a/arch/arm/mach-omap2/pm34xx.c b/arch/arm/mach-omap2/pm34xx.c
> index adab4d5..ff0811a 100644
> --- a/arch/arm/mach-omap2/pm34xx.c
> +++ b/arch/arm/mach-omap2/pm34xx.c
> @@ -90,6 +90,7 @@ static int (*_omap_save_secure_sram)(u32 *addr);
> static struct powerdomain *mpu_pwrdm, *neon_pwrdm;
> static struct powerdomain *core_pwrdm, *per_pwrdm;
> static struct powerdomain *cam_pwrdm;
> +static int wkup_event, io_event;
>
> static inline void omap3_per_save_context(void)
> {
> @@ -242,20 +243,18 @@ static int prcm_clear_mod_irqs(s16 module, u8 regs)
>
> static irqreturn_t _prcm_int_handle_wakeup(int irq, void *unused)
> {
> - int c;
> + return IRQ_HANDLED;
> +}
>
> - c = prcm_clear_mod_irqs(WKUP_MOD, 1);
> - c += prcm_clear_mod_irqs(CORE_MOD, 1);
> - c += prcm_clear_mod_irqs(OMAP3430_PER_MOD, 1);
> +static void prcm_clear_wakeups(void)
> +{
> + prcm_clear_mod_irqs(WKUP_MOD, 1);
> + prcm_clear_mod_irqs(CORE_MOD, 1);
> + prcm_clear_mod_irqs(OMAP3430_PER_MOD, 1);
> if (omap_rev() > OMAP3430_REV_ES1_0) {
> - c += prcm_clear_mod_irqs(CORE_MOD, 3);
> - c += prcm_clear_mod_irqs(OMAP3430ES2_USBHOST_MOD, 1);
> + prcm_clear_mod_irqs(CORE_MOD, 3);
> + prcm_clear_mod_irqs(OMAP3430ES2_USBHOST_MOD, 1);
> }
> -
> - if (c)
> - return IRQ_HANDLED;
> - else
> - return IRQ_NONE;
> }
>
> /* Function to restore the table entry that was modified for enabling MMU */
> @@ -301,6 +300,10 @@ void omap_sram_idle(void)
> if (!_omap_sram_idle)
> return;
>
> + prcm_clear_wakeups();
> + omap3_prcm_unmask_event(wkup_event);
> + omap3_prcm_unmask_event(io_event);
Hmm, Also, if you move the event clearing into the actual IRQ handling,
you shouldn't need these unmasks here either since this event will be
handled by handle_level_irq(), which should mask/ack on entry, then
unmask after it's handled.
Kevin
> +
> pwrdm_clear_all_prev_pwrst(mpu_pwrdm);
> pwrdm_clear_all_prev_pwrst(neon_pwrdm);
> pwrdm_clear_all_prev_pwrst(core_pwrdm);
> @@ -832,17 +835,19 @@ static int __init omap3_pm_init(void)
> goto err_prcm_irq_init;
> }
>
> + wkup_event = omap_prcm_event_to_id("wkup");
> ret = request_irq(omap_prcm_event_to_irq("wkup"),
> _prcm_int_handle_wakeup,
> - IRQF_NO_SUSPEND, "prcm_wkup", NULL);
> + IRQF_NO_SUSPEND | IRQF_ONESHOT, "prcm_wkup", NULL);
> if (ret) {
> pr_err("request_irq failed to register for PRCM wakeup\n");
> goto err_prcm_irq_wkup;
> }
>
> + io_event = omap_prcm_event_to_id("io");
> ret = request_irq(omap_prcm_event_to_irq("io"),
> _prcm_int_handle_wakeup,
> - IRQF_NO_SUSPEND, "prcm_io", NULL);
> + IRQF_NO_SUSPEND | IRQF_ONESHOT, "prcm_io", NULL);
> if (ret) {
> pr_err("request_irq failed to register for PRCM io\n");
> goto err_prcm_irq_io;
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [PATCHv3 3/6] OMAP: PRCM: Added an api to get id for a PRCM event
2011-06-22 16:42 ` [PATCHv3 3/6] OMAP: PRCM: Added an api to get id for a PRCM event Tero Kristo
@ 2011-06-24 21:58 ` Kevin Hilman
0 siblings, 0 replies; 29+ messages in thread
From: Kevin Hilman @ 2011-06-24 21:58 UTC (permalink / raw)
To: Tero Kristo; +Cc: linux-omap
Tero Kristo <t-kristo@ti.com> writes:
> This is required by OMAP3 as it needs to dynamically unmask events during
> idle cycle.
This shouldn't be needed if you move the clearing etc. back into the
PRCM IRQ handler (more on this in [4/6])
Kevin
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [PATCHv3 5/6] OMAP3: Serial: Made serial to work properly with PRCM chain handler
2011-06-22 16:42 ` [PATCHv3 5/6] OMAP3: Serial: Made serial to work properly with " Tero Kristo
2011-06-22 17:09 ` Tero Kristo
2011-06-23 8:21 ` Tony Lindgren
@ 2011-06-24 22:00 ` Kevin Hilman
2 siblings, 0 replies; 29+ messages in thread
From: Kevin Hilman @ 2011-06-24 22:00 UTC (permalink / raw)
To: Tero Kristo; +Cc: linux-omap
Tero Kristo <t-kristo@ti.com> writes:
> Signed-off-by: Tero Kristo <t-kristo@ti.com>
You should put 'TEMP' or something similar in the subject of 5/6 and 6/6
since those are only needed without the full conversion from Govindraj.
If Govindraj is able to validate the rest of this series with his
runtime PM conversion, I'd prefer to merge his conversion instead of
your patches 5 & 6.
Kevin
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [PATCHv3 0/6] PRCM chain handler
2011-06-22 16:42 [PATCHv3 0/6] PRCM chain handler Tero Kristo
` (5 preceding siblings ...)
2011-06-22 16:42 ` [PATCHv3 6/6] OMAP3: Serial tty: Added resume_idle calls to critical points Tero Kristo
@ 2011-06-27 15:02 ` Tero Kristo
6 siblings, 0 replies; 29+ messages in thread
From: Tero Kristo @ 2011-06-27 15:02 UTC (permalink / raw)
To: linux-omap; +Cc: Hilman, Kevin, Tony Lindgren, Govindraj
Just a heads up on this, to let you know I have not ignored your
comments. :)
I have been doing some re-work / testing on this set based on your
comments, and will hopefully post a new set tomorrow. One of the bigger
changes is that I have changed pad wakeups to use omap_mux and they are
now enabled automatically when omap_hwmod_enable_wakeup /
omap_hwmod_disable_wakeup are called. I am trying to get other changes
in also.
-Tero
On Wed, 2011-06-22 at 18:42 +0200, Kristo, Tero wrote:
> I was able to test this on OMAP3 beagleboard and got it working properly
> after some sweating.
>
> Version 3 changes:
> - Changed omap3 PRCM wakeup interrupt handler to be a dummy, wakeup irq
> clearing is now done when we are entering idle. This prevents PRCM
> interrupt hanging in case UART is disabled, and gives probably slightly
> better latencies during wakeup.
> - Added console lock protection during uart clock enable / disable
> * prevents nastiness to happen when omap_device wants to print out something
> - A few new resume_idle calls added to tty omap-serial
>
> -Tero
>
>
> Texas Instruments Oy, Tekniikantie 12, 02150 Espoo. Y-tunnus: 0115040-6. Kotipaikka: Helsinki
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-omap" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
Texas Instruments Oy, Tekniikantie 12, 02150 Espoo. Y-tunnus: 0115040-6. Kotipaikka: Helsinki
^ permalink raw reply [flat|nested] 29+ messages in thread
end of thread, other threads:[~2011-06-27 15:02 UTC | newest]
Thread overview: 29+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-06-22 16:42 [PATCHv3 0/6] PRCM chain handler Tero Kristo
2011-06-22 16:42 ` [PATCHv3 1/6] omap: prcm: switch to a chained IRQ handler mechanism Tero Kristo
2011-06-22 23:53 ` Kevin Hilman
2011-06-23 7:24 ` Tero Kristo
2011-06-23 8:19 ` Tony Lindgren
2011-06-23 9:08 ` Tero Kristo
2011-06-23 9:51 ` Tony Lindgren
2011-06-24 16:00 ` Kevin Hilman
2011-06-24 21:02 ` Kevin Hilman
2011-06-22 16:42 ` [PATCHv3 2/6] PRCM: Add support for PAD wakeup interrupts Tero Kristo
2011-06-23 10:23 ` Govindraj
2011-06-24 21:34 ` Kevin Hilman
2011-06-24 21:21 ` Kevin Hilman
2011-06-22 16:42 ` [PATCHv3 3/6] OMAP: PRCM: Added an api to get id for a PRCM event Tero Kristo
2011-06-24 21:58 ` Kevin Hilman
2011-06-22 16:42 ` [PATCHv3 4/6] OMAP3: PM: Use PRCM chain handler Tero Kristo
2011-06-24 21:57 ` Kevin Hilman
2011-06-22 16:42 ` [PATCHv3 5/6] OMAP3: Serial: Made serial to work properly with " Tero Kristo
2011-06-22 17:09 ` Tero Kristo
2011-06-23 10:30 ` Govindraj
2011-06-23 8:21 ` Tony Lindgren
2011-06-23 9:11 ` Tero Kristo
2011-06-23 10:00 ` Tony Lindgren
2011-06-23 10:35 ` Govindraj
2011-06-23 11:12 ` Tony Lindgren
2011-06-24 15:15 ` Kevin Hilman
2011-06-24 22:00 ` Kevin Hilman
2011-06-22 16:42 ` [PATCHv3 6/6] OMAP3: Serial tty: Added resume_idle calls to critical points Tero Kristo
2011-06-27 15:02 ` [PATCHv3 0/6] PRCM chain handler Tero Kristo
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox