Loading arch/arm/mach-omap2/Makefile +2 −1 Original line number Diff line number Diff line Loading @@ -81,6 +81,7 @@ endif endif # PRCM obj-y += prm_common.o obj-$(CONFIG_ARCH_OMAP2) += prcm.o cm2xxx_3xxx.o prm2xxx_3xxx.o obj-$(CONFIG_ARCH_OMAP3) += prcm.o cm2xxx_3xxx.o prm2xxx_3xxx.o \ vc3xxx_data.o vp3xxx_data.o Loading @@ -90,7 +91,7 @@ obj-$(CONFIG_ARCH_OMAP3) += prcm.o cm2xxx_3xxx.o prm2xxx_3xxx.o \ obj-$(CONFIG_ARCH_OMAP4) += prcm.o cm2xxx_3xxx.o cminst44xx.o \ cm44xx.o prcm_mpu44xx.o \ prminst44xx.o vc44xx_data.o \ vp44xx_data.o vp44xx_data.o prm44xx.o # OMAP voltage domains voltagedomain-common := voltage.o vc.o vp.o Loading arch/arm/mach-omap2/mux.c +87 −2 Original line number Diff line number Diff line Loading @@ -32,6 +32,8 @@ #include <linux/debugfs.h> #include <linux/seq_file.h> #include <linux/uaccess.h> #include <linux/irq.h> #include <linux/interrupt.h> #include <asm/system.h> Loading @@ -39,6 +41,7 @@ #include "control.h" #include "mux.h" #include "prm.h" #define OMAP_MUX_BASE_OFFSET 0x30 /* Offset from CTRL_BASE */ #define OMAP_MUX_BASE_SZ 0x5ca Loading Loading @@ -306,7 +309,8 @@ omap_hwmod_mux_init(struct omap_device_pad *bpads, int nr_pads) pad->idle = bpad->idle; pad->off = bpad->off; if (pad->flags & OMAP_DEVICE_PAD_REMUX) if (pad->flags & (OMAP_DEVICE_PAD_REMUX | OMAP_DEVICE_PAD_WAKEUP)) nr_pads_dynamic++; pr_debug("%s: Initialized %s\n", __func__, pad->name); Loading @@ -331,7 +335,8 @@ omap_hwmod_mux_init(struct omap_device_pad *bpads, int nr_pads) for (i = 0; i < hmux->nr_pads; i++) { struct omap_device_pad *pad = &hmux->pads[i]; if (pad->flags & OMAP_DEVICE_PAD_REMUX) { if (pad->flags & (OMAP_DEVICE_PAD_REMUX | OMAP_DEVICE_PAD_WAKEUP)) { pr_debug("%s: pad %s tagged dynamic\n", __func__, pad->name); hmux->pads_dynamic[nr_pads_dynamic] = pad; Loading @@ -351,6 +356,78 @@ omap_hwmod_mux_init(struct omap_device_pad *bpads, int nr_pads) return NULL; } /** * omap_hwmod_mux_scan_wakeups - omap hwmod scan wakeup pads * @hmux: Pads for a hwmod * @mpu_irqs: MPU irq array for a hwmod * * Scans the wakeup status of pads for a single hwmod. If an irq * array is defined for this mux, the parser will call the registered * ISRs for corresponding pads, otherwise the parser will stop at the * first wakeup active pad and return. Returns true if there is a * pending and non-served wakeup event for the mux, otherwise false. */ static bool omap_hwmod_mux_scan_wakeups(struct omap_hwmod_mux_info *hmux, struct omap_hwmod_irq_info *mpu_irqs) { int i, irq; unsigned int val; u32 handled_irqs = 0; for (i = 0; i < hmux->nr_pads_dynamic; i++) { struct omap_device_pad *pad = hmux->pads_dynamic[i]; if (!(pad->flags & OMAP_DEVICE_PAD_WAKEUP) || !(pad->idle & OMAP_WAKEUP_EN)) continue; val = omap_mux_read(pad->partition, pad->mux->reg_offset); if (!(val & OMAP_WAKEUP_EVENT)) continue; if (!hmux->irqs) return true; irq = hmux->irqs[i]; /* make sure we only handle each irq once */ if (handled_irqs & 1 << irq) continue; handled_irqs |= 1 << irq; generic_handle_irq(mpu_irqs[irq].irq); } return false; } /** * _omap_hwmod_mux_handle_irq - Process wakeup events for a single hwmod * * Checks a single hwmod for every wakeup capable pad to see if there is an * active wakeup event. If this is the case, call the corresponding ISR. */ static int _omap_hwmod_mux_handle_irq(struct omap_hwmod *oh, void *data) { if (!oh->mux || !oh->mux->enabled) return 0; if (omap_hwmod_mux_scan_wakeups(oh->mux, oh->mpu_irqs)) generic_handle_irq(oh->mpu_irqs[0].irq); return 0; } /** * omap_hwmod_mux_handle_irq - Process pad wakeup irqs. * * Calls a function for each registered omap_hwmod to check * pad wakeup statuses. */ static irqreturn_t omap_hwmod_mux_handle_irq(int irq, void *unused) { omap_hwmod_for_each(_omap_hwmod_mux_handle_irq, NULL); return IRQ_HANDLED; } /* Assumes the calling function takes care of locking */ void omap_hwmod_mux(struct omap_hwmod_mux_info *hmux, u8 state) { Loading Loading @@ -715,6 +792,7 @@ static void __init omap_mux_free_names(struct omap_mux *m) static int __init omap_mux_late_init(void) { struct omap_mux_partition *partition; int ret; list_for_each_entry(partition, &mux_partitions, node) { struct omap_mux_entry *e, *tmp; Loading @@ -735,6 +813,13 @@ static int __init omap_mux_late_init(void) } } ret = request_irq(omap_prcm_event_to_irq("io"), omap_hwmod_mux_handle_irq, IRQF_SHARED | IRQF_NO_SUSPEND, "hwmod_io", omap_mux_late_init); if (ret) pr_warning("mux: Failed to setup hwmod io irq %d\n", ret); omap_mux_dbg_init(); return 0; Loading arch/arm/mach-omap2/omap_hwmod.c +102 −0 Original line number Diff line number Diff line Loading @@ -136,6 +136,7 @@ #include <linux/list.h> #include <linux/mutex.h> #include <linux/spinlock.h> #include <linux/slab.h> #include "common.h" #include <plat/cpu.h> Loading Loading @@ -380,6 +381,51 @@ static int _set_module_autoidle(struct omap_hwmod *oh, u8 autoidle, return 0; } /** * _set_idle_ioring_wakeup - enable/disable IO pad wakeup on hwmod idle for mux * @oh: struct omap_hwmod * * @set_wake: bool value indicating to set (true) or clear (false) wakeup enable * * Set or clear the I/O pad wakeup flag in the mux entries for the * hwmod @oh. This function changes the @oh->mux->pads_dynamic array * in memory. If the hwmod is currently idled, and the new idle * values don't match the previous ones, this function will also * update the SCM PADCTRL registers. Otherwise, if the hwmod is not * currently idled, this function won't touch the hardware: the new * mux settings are written to the SCM PADCTRL registers when the * hwmod is idled. No return value. */ static void _set_idle_ioring_wakeup(struct omap_hwmod *oh, bool set_wake) { struct omap_device_pad *pad; bool change = false; u16 prev_idle; int j; if (!oh->mux || !oh->mux->enabled) return; for (j = 0; j < oh->mux->nr_pads_dynamic; j++) { pad = oh->mux->pads_dynamic[j]; if (!(pad->flags & OMAP_DEVICE_PAD_WAKEUP)) continue; prev_idle = pad->idle; if (set_wake) pad->idle |= OMAP_WAKEUP_EN; else pad->idle &= ~OMAP_WAKEUP_EN; if (prev_idle != pad->idle) change = true; } if (change && oh->_state == _HWMOD_STATE_IDLE) omap_hwmod_mux(oh->mux, _HWMOD_STATE_IDLE); } /** * _enable_wakeup: set OCP_SYSCONFIG.ENAWAKEUP bit in the hardware * @oh: struct omap_hwmod * Loading Loading @@ -2437,6 +2483,7 @@ int omap_hwmod_enable_wakeup(struct omap_hwmod *oh) v = oh->_sysc_cache; _enable_wakeup(oh, &v); _write_sysconfig(v, oh); _set_idle_ioring_wakeup(oh, true); spin_unlock_irqrestore(&oh->_lock, flags); return 0; Loading Loading @@ -2467,6 +2514,7 @@ int omap_hwmod_disable_wakeup(struct omap_hwmod *oh) v = oh->_sysc_cache; _disable_wakeup(oh, &v); _write_sysconfig(v, oh); _set_idle_ioring_wakeup(oh, false); spin_unlock_irqrestore(&oh->_lock, flags); return 0; Loading Loading @@ -2683,3 +2731,57 @@ int omap_hwmod_no_setup_reset(struct omap_hwmod *oh) return 0; } /** * omap_hwmod_pad_route_irq - route an I/O pad wakeup to a particular MPU IRQ * @oh: struct omap_hwmod * containing hwmod mux entries * @pad_idx: array index in oh->mux of the hwmod mux entry to route wakeup * @irq_idx: the hwmod mpu_irqs array index of the IRQ to trigger on wakeup * * When an I/O pad wakeup arrives for the dynamic or wakeup hwmod mux * entry number @pad_idx for the hwmod @oh, trigger the interrupt * service routine for the hwmod's mpu_irqs array index @irq_idx. If * this function is not called for a given pad_idx, then the ISR * associated with @oh's first MPU IRQ will be triggered when an I/O * pad wakeup occurs on that pad. Note that @pad_idx is the index of * the _dynamic or wakeup_ entry: if there are other entries not * marked with OMAP_DEVICE_PAD_WAKEUP or OMAP_DEVICE_PAD_REMUX, these * entries are NOT COUNTED in the dynamic pad index. This function * must be called separately for each pad that requires its interrupt * to be re-routed this way. Returns -EINVAL if there is an argument * problem or if @oh does not have hwmod mux entries or MPU IRQs; * returns -ENOMEM if memory cannot be allocated; or 0 upon success. * * XXX This function interface is fragile. Rather than using array * indexes, which are subject to unpredictable change, it should be * using hwmod IRQ names, and some other stable key for the hwmod mux * pad records. */ int omap_hwmod_pad_route_irq(struct omap_hwmod *oh, int pad_idx, int irq_idx) { int nr_irqs; might_sleep(); if (!oh || !oh->mux || !oh->mpu_irqs || pad_idx < 0 || pad_idx >= oh->mux->nr_pads_dynamic) return -EINVAL; /* Check the number of available mpu_irqs */ for (nr_irqs = 0; oh->mpu_irqs[nr_irqs].irq >= 0; nr_irqs++) ; if (irq_idx >= nr_irqs) return -EINVAL; if (!oh->mux->irqs) { /* XXX What frees this? */ oh->mux->irqs = kzalloc(sizeof(int) * oh->mux->nr_pads_dynamic, GFP_KERNEL); if (!oh->mux->irqs) return -ENOMEM; } oh->mux->irqs[pad_idx] = irq_idx; return 0; } arch/arm/mach-omap2/pm34xx.c +44 −71 Original line number Diff line number Diff line Loading @@ -195,7 +195,7 @@ static void omap3_save_secure_ram_context(void) * that any peripheral wake-up events occurring while attempting to * clear the PM_WKST_x are detected and cleared. */ static int prcm_clear_mod_irqs(s16 module, u8 regs) static int prcm_clear_mod_irqs(s16 module, u8 regs, u32 ignore_bits) { u32 wkst, fclk, iclk, clken; u16 wkst_off = (regs == 3) ? OMAP3430ES2_PM_WKST3 : PM_WKST1; Loading @@ -207,6 +207,7 @@ static int prcm_clear_mod_irqs(s16 module, u8 regs) wkst = omap2_prm_read_mod_reg(module, wkst_off); wkst &= omap2_prm_read_mod_reg(module, grpsel_off); wkst &= ~ignore_bits; if (wkst) { iclk = omap2_cm_read_mod_reg(module, iclk_off); fclk = omap2_cm_read_mod_reg(module, fclk_off); Loading @@ -222,6 +223,7 @@ static int prcm_clear_mod_irqs(s16 module, u8 regs) omap2_cm_set_mod_reg_bits(clken, module, fclk_off); omap2_prm_write_mod_reg(wkst, module, wkst_off); wkst = omap2_prm_read_mod_reg(module, wkst_off); wkst &= ~ignore_bits; c++; } omap2_cm_write_mod_reg(iclk, module, iclk_off); Loading @@ -231,76 +233,35 @@ 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_io(int irq, void *unused) { int c; 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); if (omap_rev() > OMAP3430_REV_ES1_0) { c += prcm_clear_mod_irqs(CORE_MOD, 3); c += prcm_clear_mod_irqs(OMAP3430ES2_USBHOST_MOD, 1); } c = prcm_clear_mod_irqs(WKUP_MOD, 1, ~(OMAP3430_ST_IO_MASK | OMAP3430_ST_IO_CHAIN_MASK)); return c; return c ? IRQ_HANDLED : IRQ_NONE; } /* * 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) static irqreturn_t _prcm_int_handle_wakeup(int irq, void *unused) { 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(); int c; /* * Is the MPU PRCM interrupt handler racing with the * IVA2 PRCM interrupt handler ? * Clear all except ST_IO and ST_IO_CHAIN for wkup module, * these are handled in a separate handler to avoid acking * IO events before parsing in mux code */ 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); c = prcm_clear_mod_irqs(WKUP_MOD, 1, OMAP3430_ST_IO_MASK | OMAP3430_ST_IO_CHAIN_MASK); c += prcm_clear_mod_irqs(CORE_MOD, 1, 0); c += prcm_clear_mod_irqs(OMAP3430_PER_MOD, 1, 0); if (omap_rev() > OMAP3430_REV_ES1_0) { c += prcm_clear_mod_irqs(CORE_MOD, 3, 0); c += prcm_clear_mod_irqs(OMAP3430ES2_USBHOST_MOD, 1, 0); } 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; return c ? IRQ_HANDLED : IRQ_NONE; } static void omap34xx_save_context(u32 *save) Loading Loading @@ -581,6 +542,7 @@ static int omap3_pm_begin(suspend_state_t state) disable_hlt(); suspend_state = state; omap_uart_enable_irqs(0); omap_prcm_irq_prepare(); return 0; } Loading @@ -592,10 +554,16 @@ static void omap3_pm_end(void) return; } static void omap3_pm_finish(void) { omap_prcm_irq_complete(); } static const struct platform_suspend_ops omap_pm_ops = { .begin = omap3_pm_begin, .end = omap3_pm_end, .enter = omap3_pm_enter, .finish = omap3_pm_finish, .valid = suspend_valid_only_mem, }; #endif /* CONFIG_SUSPEND */ Loading Loading @@ -701,10 +669,6 @@ static void __init prcm_setup_regs(void) OMAP3430_GRPSEL_GPT1_MASK | OMAP3430_GRPSEL_GPT12_MASK, WKUP_MOD, OMAP3430_PM_MPUGRPSEL); /* For some reason IO doesn't generate wakeup event even if * it is selected to mpu wakeup goup */ omap2_prm_write_mod_reg(OMAP3430_IO_EN_MASK | OMAP3430_WKUP_EN_MASK, OCP_MOD, OMAP3_PRM_IRQENABLE_MPU_OFFSET); /* Enable PM_WKEN to support DSS LPR */ omap2_prm_write_mod_reg(OMAP3430_PM_WKEN_DSS_EN_DSS_MASK, Loading Loading @@ -881,12 +845,21 @@ static int __init omap3_pm_init(void) * supervised mode for powerdomains */ prcm_setup_regs(); ret = request_irq(INT_34XX_PRCM_MPU_IRQ, (irq_handler_t)prcm_interrupt_handler, IRQF_DISABLED, "prcm", NULL); ret = request_irq(omap_prcm_event_to_irq("wkup"), _prcm_int_handle_wakeup, IRQF_NO_SUSPEND, "pm_wkup", NULL); if (ret) { pr_err("pm: Failed to request pm_wkup irq\n"); goto err1; } /* IO interrupt is shared with mux code */ ret = request_irq(omap_prcm_event_to_irq("io"), _prcm_int_handle_io, IRQF_SHARED | IRQF_NO_SUSPEND, "pm_io", omap3_pm_init); if (ret) { printk(KERN_ERR "request_irq failed to register for 0x%x\n", INT_34XX_PRCM_MPU_IRQ); pr_err("pm: Failed to request pm_io irq\n"); goto err1; } Loading arch/arm/mach-omap2/prcm-common.h +74 −1 Original line number Diff line number Diff line Loading @@ -4,7 +4,7 @@ /* * OMAP2/3 PRCM base and module definitions * * Copyright (C) 2007-2009 Texas Instruments, Inc. * Copyright (C) 2007-2009, 2011 Texas Instruments, Inc. * Copyright (C) 2007-2009 Nokia Corporation * * Written by Paul Walmsley Loading Loading @@ -408,6 +408,79 @@ extern void __iomem *prm_base; extern void __iomem *cm_base; extern void __iomem *cm2_base; /** * struct omap_prcm_irq - describes a PRCM interrupt bit * @name: a short name describing the interrupt type, e.g. "wkup" or "io" * @offset: the bit shift of the interrupt inside the IRQ{ENABLE,STATUS} regs * @priority: should this interrupt be handled before @priority=false IRQs? * * Describes interrupt bits inside the PRM_IRQ{ENABLE,STATUS}_MPU* registers. * On systems with multiple PRM MPU IRQ registers, the bitfields read from * the registers are concatenated, so @offset could be > 31 on these systems - * see omap_prm_irq_handler() for more details. I/O ring interrupts should * have @priority set to true. */ struct omap_prcm_irq { const char *name; unsigned int offset; bool priority; }; /** * struct omap_prcm_irq_setup - PRCM interrupt controller details * @ack: PRM register offset for the first PRM_IRQSTATUS_MPU register * @mask: PRM register offset for the first PRM_IRQENABLE_MPU register * @nr_regs: number of PRM_IRQ{STATUS,ENABLE}_MPU* registers * @nr_irqs: number of entries in the @irqs array * @irqs: ptr to an array of PRCM interrupt bits (see @nr_irqs) * @irq: MPU IRQ asserted when a PRCM interrupt arrives * @read_pending_irqs: fn ptr to determine if any PRCM IRQs are pending * @ocp_barrier: fn ptr to force buffered PRM writes to complete * @save_and_clear_irqen: fn ptr to save and clear IRQENABLE regs * @restore_irqen: fn ptr to save and clear IRQENABLE regs * @saved_mask: IRQENABLE regs are saved here during suspend * @priority_mask: 1 bit per IRQ, set to 1 if omap_prcm_irq.priority = true * @base_irq: base dynamic IRQ number, returned from irq_alloc_descs() in init * @suspended: set to true after Linux suspend code has called our ->prepare() * @suspend_save_flag: set to true after IRQ masks have been saved and disabled * * @saved_mask, @priority_mask, @base_irq, @suspended, and * @suspend_save_flag are populated dynamically, and are not to be * specified in static initializers. */ struct omap_prcm_irq_setup { u16 ack; u16 mask; u8 nr_regs; u8 nr_irqs; const struct omap_prcm_irq *irqs; int irq; void (*read_pending_irqs)(unsigned long *events); void (*ocp_barrier)(void); void (*save_and_clear_irqen)(u32 *saved_mask); void (*restore_irqen)(u32 *saved_mask); u32 *saved_mask; u32 *priority_mask; int base_irq; bool suspended; bool suspend_save_flag; }; /* OMAP_PRCM_IRQ: convenience macro for creating struct omap_prcm_irq records */ #define OMAP_PRCM_IRQ(_name, _offset, _priority) { \ .name = _name, \ .offset = _offset, \ .priority = _priority \ } extern void omap_prcm_irq_cleanup(void); extern int omap_prcm_register_chain_handler( struct omap_prcm_irq_setup *irq_setup); extern int omap_prcm_event_to_irq(const char *event); extern void omap_prcm_irq_prepare(void); extern void omap_prcm_irq_complete(void); # endif #endif Loading Loading
arch/arm/mach-omap2/Makefile +2 −1 Original line number Diff line number Diff line Loading @@ -81,6 +81,7 @@ endif endif # PRCM obj-y += prm_common.o obj-$(CONFIG_ARCH_OMAP2) += prcm.o cm2xxx_3xxx.o prm2xxx_3xxx.o obj-$(CONFIG_ARCH_OMAP3) += prcm.o cm2xxx_3xxx.o prm2xxx_3xxx.o \ vc3xxx_data.o vp3xxx_data.o Loading @@ -90,7 +91,7 @@ obj-$(CONFIG_ARCH_OMAP3) += prcm.o cm2xxx_3xxx.o prm2xxx_3xxx.o \ obj-$(CONFIG_ARCH_OMAP4) += prcm.o cm2xxx_3xxx.o cminst44xx.o \ cm44xx.o prcm_mpu44xx.o \ prminst44xx.o vc44xx_data.o \ vp44xx_data.o vp44xx_data.o prm44xx.o # OMAP voltage domains voltagedomain-common := voltage.o vc.o vp.o Loading
arch/arm/mach-omap2/mux.c +87 −2 Original line number Diff line number Diff line Loading @@ -32,6 +32,8 @@ #include <linux/debugfs.h> #include <linux/seq_file.h> #include <linux/uaccess.h> #include <linux/irq.h> #include <linux/interrupt.h> #include <asm/system.h> Loading @@ -39,6 +41,7 @@ #include "control.h" #include "mux.h" #include "prm.h" #define OMAP_MUX_BASE_OFFSET 0x30 /* Offset from CTRL_BASE */ #define OMAP_MUX_BASE_SZ 0x5ca Loading Loading @@ -306,7 +309,8 @@ omap_hwmod_mux_init(struct omap_device_pad *bpads, int nr_pads) pad->idle = bpad->idle; pad->off = bpad->off; if (pad->flags & OMAP_DEVICE_PAD_REMUX) if (pad->flags & (OMAP_DEVICE_PAD_REMUX | OMAP_DEVICE_PAD_WAKEUP)) nr_pads_dynamic++; pr_debug("%s: Initialized %s\n", __func__, pad->name); Loading @@ -331,7 +335,8 @@ omap_hwmod_mux_init(struct omap_device_pad *bpads, int nr_pads) for (i = 0; i < hmux->nr_pads; i++) { struct omap_device_pad *pad = &hmux->pads[i]; if (pad->flags & OMAP_DEVICE_PAD_REMUX) { if (pad->flags & (OMAP_DEVICE_PAD_REMUX | OMAP_DEVICE_PAD_WAKEUP)) { pr_debug("%s: pad %s tagged dynamic\n", __func__, pad->name); hmux->pads_dynamic[nr_pads_dynamic] = pad; Loading @@ -351,6 +356,78 @@ omap_hwmod_mux_init(struct omap_device_pad *bpads, int nr_pads) return NULL; } /** * omap_hwmod_mux_scan_wakeups - omap hwmod scan wakeup pads * @hmux: Pads for a hwmod * @mpu_irqs: MPU irq array for a hwmod * * Scans the wakeup status of pads for a single hwmod. If an irq * array is defined for this mux, the parser will call the registered * ISRs for corresponding pads, otherwise the parser will stop at the * first wakeup active pad and return. Returns true if there is a * pending and non-served wakeup event for the mux, otherwise false. */ static bool omap_hwmod_mux_scan_wakeups(struct omap_hwmod_mux_info *hmux, struct omap_hwmod_irq_info *mpu_irqs) { int i, irq; unsigned int val; u32 handled_irqs = 0; for (i = 0; i < hmux->nr_pads_dynamic; i++) { struct omap_device_pad *pad = hmux->pads_dynamic[i]; if (!(pad->flags & OMAP_DEVICE_PAD_WAKEUP) || !(pad->idle & OMAP_WAKEUP_EN)) continue; val = omap_mux_read(pad->partition, pad->mux->reg_offset); if (!(val & OMAP_WAKEUP_EVENT)) continue; if (!hmux->irqs) return true; irq = hmux->irqs[i]; /* make sure we only handle each irq once */ if (handled_irqs & 1 << irq) continue; handled_irqs |= 1 << irq; generic_handle_irq(mpu_irqs[irq].irq); } return false; } /** * _omap_hwmod_mux_handle_irq - Process wakeup events for a single hwmod * * Checks a single hwmod for every wakeup capable pad to see if there is an * active wakeup event. If this is the case, call the corresponding ISR. */ static int _omap_hwmod_mux_handle_irq(struct omap_hwmod *oh, void *data) { if (!oh->mux || !oh->mux->enabled) return 0; if (omap_hwmod_mux_scan_wakeups(oh->mux, oh->mpu_irqs)) generic_handle_irq(oh->mpu_irqs[0].irq); return 0; } /** * omap_hwmod_mux_handle_irq - Process pad wakeup irqs. * * Calls a function for each registered omap_hwmod to check * pad wakeup statuses. */ static irqreturn_t omap_hwmod_mux_handle_irq(int irq, void *unused) { omap_hwmod_for_each(_omap_hwmod_mux_handle_irq, NULL); return IRQ_HANDLED; } /* Assumes the calling function takes care of locking */ void omap_hwmod_mux(struct omap_hwmod_mux_info *hmux, u8 state) { Loading Loading @@ -715,6 +792,7 @@ static void __init omap_mux_free_names(struct omap_mux *m) static int __init omap_mux_late_init(void) { struct omap_mux_partition *partition; int ret; list_for_each_entry(partition, &mux_partitions, node) { struct omap_mux_entry *e, *tmp; Loading @@ -735,6 +813,13 @@ static int __init omap_mux_late_init(void) } } ret = request_irq(omap_prcm_event_to_irq("io"), omap_hwmod_mux_handle_irq, IRQF_SHARED | IRQF_NO_SUSPEND, "hwmod_io", omap_mux_late_init); if (ret) pr_warning("mux: Failed to setup hwmod io irq %d\n", ret); omap_mux_dbg_init(); return 0; Loading
arch/arm/mach-omap2/omap_hwmod.c +102 −0 Original line number Diff line number Diff line Loading @@ -136,6 +136,7 @@ #include <linux/list.h> #include <linux/mutex.h> #include <linux/spinlock.h> #include <linux/slab.h> #include "common.h" #include <plat/cpu.h> Loading Loading @@ -380,6 +381,51 @@ static int _set_module_autoidle(struct omap_hwmod *oh, u8 autoidle, return 0; } /** * _set_idle_ioring_wakeup - enable/disable IO pad wakeup on hwmod idle for mux * @oh: struct omap_hwmod * * @set_wake: bool value indicating to set (true) or clear (false) wakeup enable * * Set or clear the I/O pad wakeup flag in the mux entries for the * hwmod @oh. This function changes the @oh->mux->pads_dynamic array * in memory. If the hwmod is currently idled, and the new idle * values don't match the previous ones, this function will also * update the SCM PADCTRL registers. Otherwise, if the hwmod is not * currently idled, this function won't touch the hardware: the new * mux settings are written to the SCM PADCTRL registers when the * hwmod is idled. No return value. */ static void _set_idle_ioring_wakeup(struct omap_hwmod *oh, bool set_wake) { struct omap_device_pad *pad; bool change = false; u16 prev_idle; int j; if (!oh->mux || !oh->mux->enabled) return; for (j = 0; j < oh->mux->nr_pads_dynamic; j++) { pad = oh->mux->pads_dynamic[j]; if (!(pad->flags & OMAP_DEVICE_PAD_WAKEUP)) continue; prev_idle = pad->idle; if (set_wake) pad->idle |= OMAP_WAKEUP_EN; else pad->idle &= ~OMAP_WAKEUP_EN; if (prev_idle != pad->idle) change = true; } if (change && oh->_state == _HWMOD_STATE_IDLE) omap_hwmod_mux(oh->mux, _HWMOD_STATE_IDLE); } /** * _enable_wakeup: set OCP_SYSCONFIG.ENAWAKEUP bit in the hardware * @oh: struct omap_hwmod * Loading Loading @@ -2437,6 +2483,7 @@ int omap_hwmod_enable_wakeup(struct omap_hwmod *oh) v = oh->_sysc_cache; _enable_wakeup(oh, &v); _write_sysconfig(v, oh); _set_idle_ioring_wakeup(oh, true); spin_unlock_irqrestore(&oh->_lock, flags); return 0; Loading Loading @@ -2467,6 +2514,7 @@ int omap_hwmod_disable_wakeup(struct omap_hwmod *oh) v = oh->_sysc_cache; _disable_wakeup(oh, &v); _write_sysconfig(v, oh); _set_idle_ioring_wakeup(oh, false); spin_unlock_irqrestore(&oh->_lock, flags); return 0; Loading Loading @@ -2683,3 +2731,57 @@ int omap_hwmod_no_setup_reset(struct omap_hwmod *oh) return 0; } /** * omap_hwmod_pad_route_irq - route an I/O pad wakeup to a particular MPU IRQ * @oh: struct omap_hwmod * containing hwmod mux entries * @pad_idx: array index in oh->mux of the hwmod mux entry to route wakeup * @irq_idx: the hwmod mpu_irqs array index of the IRQ to trigger on wakeup * * When an I/O pad wakeup arrives for the dynamic or wakeup hwmod mux * entry number @pad_idx for the hwmod @oh, trigger the interrupt * service routine for the hwmod's mpu_irqs array index @irq_idx. If * this function is not called for a given pad_idx, then the ISR * associated with @oh's first MPU IRQ will be triggered when an I/O * pad wakeup occurs on that pad. Note that @pad_idx is the index of * the _dynamic or wakeup_ entry: if there are other entries not * marked with OMAP_DEVICE_PAD_WAKEUP or OMAP_DEVICE_PAD_REMUX, these * entries are NOT COUNTED in the dynamic pad index. This function * must be called separately for each pad that requires its interrupt * to be re-routed this way. Returns -EINVAL if there is an argument * problem or if @oh does not have hwmod mux entries or MPU IRQs; * returns -ENOMEM if memory cannot be allocated; or 0 upon success. * * XXX This function interface is fragile. Rather than using array * indexes, which are subject to unpredictable change, it should be * using hwmod IRQ names, and some other stable key for the hwmod mux * pad records. */ int omap_hwmod_pad_route_irq(struct omap_hwmod *oh, int pad_idx, int irq_idx) { int nr_irqs; might_sleep(); if (!oh || !oh->mux || !oh->mpu_irqs || pad_idx < 0 || pad_idx >= oh->mux->nr_pads_dynamic) return -EINVAL; /* Check the number of available mpu_irqs */ for (nr_irqs = 0; oh->mpu_irqs[nr_irqs].irq >= 0; nr_irqs++) ; if (irq_idx >= nr_irqs) return -EINVAL; if (!oh->mux->irqs) { /* XXX What frees this? */ oh->mux->irqs = kzalloc(sizeof(int) * oh->mux->nr_pads_dynamic, GFP_KERNEL); if (!oh->mux->irqs) return -ENOMEM; } oh->mux->irqs[pad_idx] = irq_idx; return 0; }
arch/arm/mach-omap2/pm34xx.c +44 −71 Original line number Diff line number Diff line Loading @@ -195,7 +195,7 @@ static void omap3_save_secure_ram_context(void) * that any peripheral wake-up events occurring while attempting to * clear the PM_WKST_x are detected and cleared. */ static int prcm_clear_mod_irqs(s16 module, u8 regs) static int prcm_clear_mod_irqs(s16 module, u8 regs, u32 ignore_bits) { u32 wkst, fclk, iclk, clken; u16 wkst_off = (regs == 3) ? OMAP3430ES2_PM_WKST3 : PM_WKST1; Loading @@ -207,6 +207,7 @@ static int prcm_clear_mod_irqs(s16 module, u8 regs) wkst = omap2_prm_read_mod_reg(module, wkst_off); wkst &= omap2_prm_read_mod_reg(module, grpsel_off); wkst &= ~ignore_bits; if (wkst) { iclk = omap2_cm_read_mod_reg(module, iclk_off); fclk = omap2_cm_read_mod_reg(module, fclk_off); Loading @@ -222,6 +223,7 @@ static int prcm_clear_mod_irqs(s16 module, u8 regs) omap2_cm_set_mod_reg_bits(clken, module, fclk_off); omap2_prm_write_mod_reg(wkst, module, wkst_off); wkst = omap2_prm_read_mod_reg(module, wkst_off); wkst &= ~ignore_bits; c++; } omap2_cm_write_mod_reg(iclk, module, iclk_off); Loading @@ -231,76 +233,35 @@ 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_io(int irq, void *unused) { int c; 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); if (omap_rev() > OMAP3430_REV_ES1_0) { c += prcm_clear_mod_irqs(CORE_MOD, 3); c += prcm_clear_mod_irqs(OMAP3430ES2_USBHOST_MOD, 1); } c = prcm_clear_mod_irqs(WKUP_MOD, 1, ~(OMAP3430_ST_IO_MASK | OMAP3430_ST_IO_CHAIN_MASK)); return c; return c ? IRQ_HANDLED : IRQ_NONE; } /* * 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) static irqreturn_t _prcm_int_handle_wakeup(int irq, void *unused) { 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(); int c; /* * Is the MPU PRCM interrupt handler racing with the * IVA2 PRCM interrupt handler ? * Clear all except ST_IO and ST_IO_CHAIN for wkup module, * these are handled in a separate handler to avoid acking * IO events before parsing in mux code */ 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); c = prcm_clear_mod_irqs(WKUP_MOD, 1, OMAP3430_ST_IO_MASK | OMAP3430_ST_IO_CHAIN_MASK); c += prcm_clear_mod_irqs(CORE_MOD, 1, 0); c += prcm_clear_mod_irqs(OMAP3430_PER_MOD, 1, 0); if (omap_rev() > OMAP3430_REV_ES1_0) { c += prcm_clear_mod_irqs(CORE_MOD, 3, 0); c += prcm_clear_mod_irqs(OMAP3430ES2_USBHOST_MOD, 1, 0); } 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; return c ? IRQ_HANDLED : IRQ_NONE; } static void omap34xx_save_context(u32 *save) Loading Loading @@ -581,6 +542,7 @@ static int omap3_pm_begin(suspend_state_t state) disable_hlt(); suspend_state = state; omap_uart_enable_irqs(0); omap_prcm_irq_prepare(); return 0; } Loading @@ -592,10 +554,16 @@ static void omap3_pm_end(void) return; } static void omap3_pm_finish(void) { omap_prcm_irq_complete(); } static const struct platform_suspend_ops omap_pm_ops = { .begin = omap3_pm_begin, .end = omap3_pm_end, .enter = omap3_pm_enter, .finish = omap3_pm_finish, .valid = suspend_valid_only_mem, }; #endif /* CONFIG_SUSPEND */ Loading Loading @@ -701,10 +669,6 @@ static void __init prcm_setup_regs(void) OMAP3430_GRPSEL_GPT1_MASK | OMAP3430_GRPSEL_GPT12_MASK, WKUP_MOD, OMAP3430_PM_MPUGRPSEL); /* For some reason IO doesn't generate wakeup event even if * it is selected to mpu wakeup goup */ omap2_prm_write_mod_reg(OMAP3430_IO_EN_MASK | OMAP3430_WKUP_EN_MASK, OCP_MOD, OMAP3_PRM_IRQENABLE_MPU_OFFSET); /* Enable PM_WKEN to support DSS LPR */ omap2_prm_write_mod_reg(OMAP3430_PM_WKEN_DSS_EN_DSS_MASK, Loading Loading @@ -881,12 +845,21 @@ static int __init omap3_pm_init(void) * supervised mode for powerdomains */ prcm_setup_regs(); ret = request_irq(INT_34XX_PRCM_MPU_IRQ, (irq_handler_t)prcm_interrupt_handler, IRQF_DISABLED, "prcm", NULL); ret = request_irq(omap_prcm_event_to_irq("wkup"), _prcm_int_handle_wakeup, IRQF_NO_SUSPEND, "pm_wkup", NULL); if (ret) { pr_err("pm: Failed to request pm_wkup irq\n"); goto err1; } /* IO interrupt is shared with mux code */ ret = request_irq(omap_prcm_event_to_irq("io"), _prcm_int_handle_io, IRQF_SHARED | IRQF_NO_SUSPEND, "pm_io", omap3_pm_init); if (ret) { printk(KERN_ERR "request_irq failed to register for 0x%x\n", INT_34XX_PRCM_MPU_IRQ); pr_err("pm: Failed to request pm_io irq\n"); goto err1; } Loading
arch/arm/mach-omap2/prcm-common.h +74 −1 Original line number Diff line number Diff line Loading @@ -4,7 +4,7 @@ /* * OMAP2/3 PRCM base and module definitions * * Copyright (C) 2007-2009 Texas Instruments, Inc. * Copyright (C) 2007-2009, 2011 Texas Instruments, Inc. * Copyright (C) 2007-2009 Nokia Corporation * * Written by Paul Walmsley Loading Loading @@ -408,6 +408,79 @@ extern void __iomem *prm_base; extern void __iomem *cm_base; extern void __iomem *cm2_base; /** * struct omap_prcm_irq - describes a PRCM interrupt bit * @name: a short name describing the interrupt type, e.g. "wkup" or "io" * @offset: the bit shift of the interrupt inside the IRQ{ENABLE,STATUS} regs * @priority: should this interrupt be handled before @priority=false IRQs? * * Describes interrupt bits inside the PRM_IRQ{ENABLE,STATUS}_MPU* registers. * On systems with multiple PRM MPU IRQ registers, the bitfields read from * the registers are concatenated, so @offset could be > 31 on these systems - * see omap_prm_irq_handler() for more details. I/O ring interrupts should * have @priority set to true. */ struct omap_prcm_irq { const char *name; unsigned int offset; bool priority; }; /** * struct omap_prcm_irq_setup - PRCM interrupt controller details * @ack: PRM register offset for the first PRM_IRQSTATUS_MPU register * @mask: PRM register offset for the first PRM_IRQENABLE_MPU register * @nr_regs: number of PRM_IRQ{STATUS,ENABLE}_MPU* registers * @nr_irqs: number of entries in the @irqs array * @irqs: ptr to an array of PRCM interrupt bits (see @nr_irqs) * @irq: MPU IRQ asserted when a PRCM interrupt arrives * @read_pending_irqs: fn ptr to determine if any PRCM IRQs are pending * @ocp_barrier: fn ptr to force buffered PRM writes to complete * @save_and_clear_irqen: fn ptr to save and clear IRQENABLE regs * @restore_irqen: fn ptr to save and clear IRQENABLE regs * @saved_mask: IRQENABLE regs are saved here during suspend * @priority_mask: 1 bit per IRQ, set to 1 if omap_prcm_irq.priority = true * @base_irq: base dynamic IRQ number, returned from irq_alloc_descs() in init * @suspended: set to true after Linux suspend code has called our ->prepare() * @suspend_save_flag: set to true after IRQ masks have been saved and disabled * * @saved_mask, @priority_mask, @base_irq, @suspended, and * @suspend_save_flag are populated dynamically, and are not to be * specified in static initializers. */ struct omap_prcm_irq_setup { u16 ack; u16 mask; u8 nr_regs; u8 nr_irqs; const struct omap_prcm_irq *irqs; int irq; void (*read_pending_irqs)(unsigned long *events); void (*ocp_barrier)(void); void (*save_and_clear_irqen)(u32 *saved_mask); void (*restore_irqen)(u32 *saved_mask); u32 *saved_mask; u32 *priority_mask; int base_irq; bool suspended; bool suspend_save_flag; }; /* OMAP_PRCM_IRQ: convenience macro for creating struct omap_prcm_irq records */ #define OMAP_PRCM_IRQ(_name, _offset, _priority) { \ .name = _name, \ .offset = _offset, \ .priority = _priority \ } extern void omap_prcm_irq_cleanup(void); extern int omap_prcm_register_chain_handler( struct omap_prcm_irq_setup *irq_setup); extern int omap_prcm_event_to_irq(const char *event); extern void omap_prcm_irq_prepare(void); extern void omap_prcm_irq_complete(void); # endif #endif Loading