diff options
-rw-r--r-- | arch/arm/mach-omap2/Makefile | 3 | ||||
-rw-r--r-- | arch/arm/mach-omap2/mux.c | 89 | ||||
-rw-r--r-- | arch/arm/mach-omap2/omap_hwmod.c | 102 | ||||
-rw-r--r-- | arch/arm/mach-omap2/pm34xx.c | 115 | ||||
-rw-r--r-- | arch/arm/mach-omap2/prcm-common.h | 75 | ||||
-rw-r--r-- | arch/arm/mach-omap2/prm2xxx_3xxx.c | 97 | ||||
-rw-r--r-- | arch/arm/mach-omap2/prm2xxx_3xxx.h | 9 | ||||
-rw-r--r-- | arch/arm/mach-omap2/prm44xx.c | 116 | ||||
-rw-r--r-- | arch/arm/mach-omap2/prm44xx.h | 8 | ||||
-rw-r--r-- | arch/arm/mach-omap2/prm_common.c | 320 | ||||
-rw-r--r-- | arch/arm/plat-omap/include/plat/omap_hwmod.h | 3 |
11 files changed, 859 insertions, 78 deletions
diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile index 9a6da52661ce..63a5efad70e8 100644 --- a/arch/arm/mach-omap2/Makefile +++ b/arch/arm/mach-omap2/Makefile | |||
@@ -81,6 +81,7 @@ endif | |||
81 | endif | 81 | endif |
82 | 82 | ||
83 | # PRCM | 83 | # PRCM |
84 | obj-y += prm_common.o | ||
84 | obj-$(CONFIG_ARCH_OMAP2) += prcm.o cm2xxx_3xxx.o prm2xxx_3xxx.o | 85 | obj-$(CONFIG_ARCH_OMAP2) += prcm.o cm2xxx_3xxx.o prm2xxx_3xxx.o |
85 | obj-$(CONFIG_ARCH_OMAP3) += prcm.o cm2xxx_3xxx.o prm2xxx_3xxx.o \ | 86 | obj-$(CONFIG_ARCH_OMAP3) += prcm.o cm2xxx_3xxx.o prm2xxx_3xxx.o \ |
86 | vc3xxx_data.o vp3xxx_data.o | 87 | vc3xxx_data.o vp3xxx_data.o |
@@ -90,7 +91,7 @@ obj-$(CONFIG_ARCH_OMAP3) += prcm.o cm2xxx_3xxx.o prm2xxx_3xxx.o \ | |||
90 | obj-$(CONFIG_ARCH_OMAP4) += prcm.o cm2xxx_3xxx.o cminst44xx.o \ | 91 | obj-$(CONFIG_ARCH_OMAP4) += prcm.o cm2xxx_3xxx.o cminst44xx.o \ |
91 | cm44xx.o prcm_mpu44xx.o \ | 92 | cm44xx.o prcm_mpu44xx.o \ |
92 | prminst44xx.o vc44xx_data.o \ | 93 | prminst44xx.o vc44xx_data.o \ |
93 | vp44xx_data.o | 94 | vp44xx_data.o prm44xx.o |
94 | 95 | ||
95 | # OMAP voltage domains | 96 | # OMAP voltage domains |
96 | voltagedomain-common := voltage.o vc.o vp.o | 97 | voltagedomain-common := voltage.o vc.o vp.o |
diff --git a/arch/arm/mach-omap2/mux.c b/arch/arm/mach-omap2/mux.c index 655e9480eb98..e1cc75d1a57a 100644 --- a/arch/arm/mach-omap2/mux.c +++ b/arch/arm/mach-omap2/mux.c | |||
@@ -32,6 +32,8 @@ | |||
32 | #include <linux/debugfs.h> | 32 | #include <linux/debugfs.h> |
33 | #include <linux/seq_file.h> | 33 | #include <linux/seq_file.h> |
34 | #include <linux/uaccess.h> | 34 | #include <linux/uaccess.h> |
35 | #include <linux/irq.h> | ||
36 | #include <linux/interrupt.h> | ||
35 | 37 | ||
36 | #include <asm/system.h> | 38 | #include <asm/system.h> |
37 | 39 | ||
@@ -39,6 +41,7 @@ | |||
39 | 41 | ||
40 | #include "control.h" | 42 | #include "control.h" |
41 | #include "mux.h" | 43 | #include "mux.h" |
44 | #include "prm.h" | ||
42 | 45 | ||
43 | #define OMAP_MUX_BASE_OFFSET 0x30 /* Offset from CTRL_BASE */ | 46 | #define OMAP_MUX_BASE_OFFSET 0x30 /* Offset from CTRL_BASE */ |
44 | #define OMAP_MUX_BASE_SZ 0x5ca | 47 | #define OMAP_MUX_BASE_SZ 0x5ca |
@@ -306,7 +309,8 @@ omap_hwmod_mux_init(struct omap_device_pad *bpads, int nr_pads) | |||
306 | pad->idle = bpad->idle; | 309 | pad->idle = bpad->idle; |
307 | pad->off = bpad->off; | 310 | pad->off = bpad->off; |
308 | 311 | ||
309 | if (pad->flags & OMAP_DEVICE_PAD_REMUX) | 312 | if (pad->flags & |
313 | (OMAP_DEVICE_PAD_REMUX | OMAP_DEVICE_PAD_WAKEUP)) | ||
310 | nr_pads_dynamic++; | 314 | nr_pads_dynamic++; |
311 | 315 | ||
312 | pr_debug("%s: Initialized %s\n", __func__, pad->name); | 316 | pr_debug("%s: Initialized %s\n", __func__, pad->name); |
@@ -331,7 +335,8 @@ omap_hwmod_mux_init(struct omap_device_pad *bpads, int nr_pads) | |||
331 | for (i = 0; i < hmux->nr_pads; i++) { | 335 | for (i = 0; i < hmux->nr_pads; i++) { |
332 | struct omap_device_pad *pad = &hmux->pads[i]; | 336 | struct omap_device_pad *pad = &hmux->pads[i]; |
333 | 337 | ||
334 | if (pad->flags & OMAP_DEVICE_PAD_REMUX) { | 338 | if (pad->flags & |
339 | (OMAP_DEVICE_PAD_REMUX | OMAP_DEVICE_PAD_WAKEUP)) { | ||
335 | pr_debug("%s: pad %s tagged dynamic\n", | 340 | pr_debug("%s: pad %s tagged dynamic\n", |
336 | __func__, pad->name); | 341 | __func__, pad->name); |
337 | hmux->pads_dynamic[nr_pads_dynamic] = pad; | 342 | hmux->pads_dynamic[nr_pads_dynamic] = pad; |
@@ -351,6 +356,78 @@ err1: | |||
351 | return NULL; | 356 | return NULL; |
352 | } | 357 | } |
353 | 358 | ||
359 | /** | ||
360 | * omap_hwmod_mux_scan_wakeups - omap hwmod scan wakeup pads | ||
361 | * @hmux: Pads for a hwmod | ||
362 | * @mpu_irqs: MPU irq array for a hwmod | ||
363 | * | ||
364 | * Scans the wakeup status of pads for a single hwmod. If an irq | ||
365 | * array is defined for this mux, the parser will call the registered | ||
366 | * ISRs for corresponding pads, otherwise the parser will stop at the | ||
367 | * first wakeup active pad and return. Returns true if there is a | ||
368 | * pending and non-served wakeup event for the mux, otherwise false. | ||
369 | */ | ||
370 | static bool omap_hwmod_mux_scan_wakeups(struct omap_hwmod_mux_info *hmux, | ||
371 | struct omap_hwmod_irq_info *mpu_irqs) | ||
372 | { | ||
373 | int i, irq; | ||
374 | unsigned int val; | ||
375 | u32 handled_irqs = 0; | ||
376 | |||
377 | for (i = 0; i < hmux->nr_pads_dynamic; i++) { | ||
378 | struct omap_device_pad *pad = hmux->pads_dynamic[i]; | ||
379 | |||
380 | if (!(pad->flags & OMAP_DEVICE_PAD_WAKEUP) || | ||
381 | !(pad->idle & OMAP_WAKEUP_EN)) | ||
382 | continue; | ||
383 | |||
384 | val = omap_mux_read(pad->partition, pad->mux->reg_offset); | ||
385 | if (!(val & OMAP_WAKEUP_EVENT)) | ||
386 | continue; | ||
387 | |||
388 | if (!hmux->irqs) | ||
389 | return true; | ||
390 | |||
391 | irq = hmux->irqs[i]; | ||
392 | /* make sure we only handle each irq once */ | ||
393 | if (handled_irqs & 1 << irq) | ||
394 | continue; | ||
395 | |||
396 | handled_irqs |= 1 << irq; | ||
397 | |||
398 | generic_handle_irq(mpu_irqs[irq].irq); | ||
399 | } | ||
400 | |||
401 | return false; | ||
402 | } | ||
403 | |||
404 | /** | ||
405 | * _omap_hwmod_mux_handle_irq - Process wakeup events for a single hwmod | ||
406 | * | ||
407 | * Checks a single hwmod for every wakeup capable pad to see if there is an | ||
408 | * active wakeup event. If this is the case, call the corresponding ISR. | ||
409 | */ | ||
410 | static int _omap_hwmod_mux_handle_irq(struct omap_hwmod *oh, void *data) | ||
411 | { | ||
412 | if (!oh->mux || !oh->mux->enabled) | ||
413 | return 0; | ||
414 | if (omap_hwmod_mux_scan_wakeups(oh->mux, oh->mpu_irqs)) | ||
415 | generic_handle_irq(oh->mpu_irqs[0].irq); | ||
416 | return 0; | ||
417 | } | ||
418 | |||
419 | /** | ||
420 | * omap_hwmod_mux_handle_irq - Process pad wakeup irqs. | ||
421 | * | ||
422 | * Calls a function for each registered omap_hwmod to check | ||
423 | * pad wakeup statuses. | ||
424 | */ | ||
425 | static irqreturn_t omap_hwmod_mux_handle_irq(int irq, void *unused) | ||
426 | { | ||
427 | omap_hwmod_for_each(_omap_hwmod_mux_handle_irq, NULL); | ||
428 | return IRQ_HANDLED; | ||
429 | } | ||
430 | |||
354 | /* Assumes the calling function takes care of locking */ | 431 | /* Assumes the calling function takes care of locking */ |
355 | void omap_hwmod_mux(struct omap_hwmod_mux_info *hmux, u8 state) | 432 | void omap_hwmod_mux(struct omap_hwmod_mux_info *hmux, u8 state) |
356 | { | 433 | { |
@@ -715,6 +792,7 @@ static void __init omap_mux_free_names(struct omap_mux *m) | |||
715 | static int __init omap_mux_late_init(void) | 792 | static int __init omap_mux_late_init(void) |
716 | { | 793 | { |
717 | struct omap_mux_partition *partition; | 794 | struct omap_mux_partition *partition; |
795 | int ret; | ||
718 | 796 | ||
719 | list_for_each_entry(partition, &mux_partitions, node) { | 797 | list_for_each_entry(partition, &mux_partitions, node) { |
720 | struct omap_mux_entry *e, *tmp; | 798 | struct omap_mux_entry *e, *tmp; |
@@ -735,6 +813,13 @@ static int __init omap_mux_late_init(void) | |||
735 | } | 813 | } |
736 | } | 814 | } |
737 | 815 | ||
816 | ret = request_irq(omap_prcm_event_to_irq("io"), | ||
817 | omap_hwmod_mux_handle_irq, IRQF_SHARED | IRQF_NO_SUSPEND, | ||
818 | "hwmod_io", omap_mux_late_init); | ||
819 | |||
820 | if (ret) | ||
821 | pr_warning("mux: Failed to setup hwmod io irq %d\n", ret); | ||
822 | |||
738 | omap_mux_dbg_init(); | 823 | omap_mux_dbg_init(); |
739 | 824 | ||
740 | return 0; | 825 | return 0; |
diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c index f673f808725f..ee9416bcc3e6 100644 --- a/arch/arm/mach-omap2/omap_hwmod.c +++ b/arch/arm/mach-omap2/omap_hwmod.c | |||
@@ -136,6 +136,7 @@ | |||
136 | #include <linux/list.h> | 136 | #include <linux/list.h> |
137 | #include <linux/mutex.h> | 137 | #include <linux/mutex.h> |
138 | #include <linux/spinlock.h> | 138 | #include <linux/spinlock.h> |
139 | #include <linux/slab.h> | ||
139 | 140 | ||
140 | #include "common.h" | 141 | #include "common.h" |
141 | #include <plat/cpu.h> | 142 | #include <plat/cpu.h> |
@@ -381,6 +382,51 @@ static int _set_module_autoidle(struct omap_hwmod *oh, u8 autoidle, | |||
381 | } | 382 | } |
382 | 383 | ||
383 | /** | 384 | /** |
385 | * _set_idle_ioring_wakeup - enable/disable IO pad wakeup on hwmod idle for mux | ||
386 | * @oh: struct omap_hwmod * | ||
387 | * @set_wake: bool value indicating to set (true) or clear (false) wakeup enable | ||
388 | * | ||
389 | * Set or clear the I/O pad wakeup flag in the mux entries for the | ||
390 | * hwmod @oh. This function changes the @oh->mux->pads_dynamic array | ||
391 | * in memory. If the hwmod is currently idled, and the new idle | ||
392 | * values don't match the previous ones, this function will also | ||
393 | * update the SCM PADCTRL registers. Otherwise, if the hwmod is not | ||
394 | * currently idled, this function won't touch the hardware: the new | ||
395 | * mux settings are written to the SCM PADCTRL registers when the | ||
396 | * hwmod is idled. No return value. | ||
397 | */ | ||
398 | static void _set_idle_ioring_wakeup(struct omap_hwmod *oh, bool set_wake) | ||
399 | { | ||
400 | struct omap_device_pad *pad; | ||
401 | bool change = false; | ||
402 | u16 prev_idle; | ||
403 | int j; | ||
404 | |||
405 | if (!oh->mux || !oh->mux->enabled) | ||
406 | return; | ||
407 | |||
408 | for (j = 0; j < oh->mux->nr_pads_dynamic; j++) { | ||
409 | pad = oh->mux->pads_dynamic[j]; | ||
410 | |||
411 | if (!(pad->flags & OMAP_DEVICE_PAD_WAKEUP)) | ||
412 | continue; | ||
413 | |||
414 | prev_idle = pad->idle; | ||
415 | |||
416 | if (set_wake) | ||
417 | pad->idle |= OMAP_WAKEUP_EN; | ||
418 | else | ||
419 | pad->idle &= ~OMAP_WAKEUP_EN; | ||
420 | |||
421 | if (prev_idle != pad->idle) | ||
422 | change = true; | ||
423 | } | ||
424 | |||
425 | if (change && oh->_state == _HWMOD_STATE_IDLE) | ||
426 | omap_hwmod_mux(oh->mux, _HWMOD_STATE_IDLE); | ||
427 | } | ||
428 | |||
429 | /** | ||
384 | * _enable_wakeup: set OCP_SYSCONFIG.ENAWAKEUP bit in the hardware | 430 | * _enable_wakeup: set OCP_SYSCONFIG.ENAWAKEUP bit in the hardware |
385 | * @oh: struct omap_hwmod * | 431 | * @oh: struct omap_hwmod * |
386 | * | 432 | * |
@@ -2437,6 +2483,7 @@ int omap_hwmod_enable_wakeup(struct omap_hwmod *oh) | |||
2437 | v = oh->_sysc_cache; | 2483 | v = oh->_sysc_cache; |
2438 | _enable_wakeup(oh, &v); | 2484 | _enable_wakeup(oh, &v); |
2439 | _write_sysconfig(v, oh); | 2485 | _write_sysconfig(v, oh); |
2486 | _set_idle_ioring_wakeup(oh, true); | ||
2440 | spin_unlock_irqrestore(&oh->_lock, flags); | 2487 | spin_unlock_irqrestore(&oh->_lock, flags); |
2441 | 2488 | ||
2442 | return 0; | 2489 | return 0; |
@@ -2467,6 +2514,7 @@ int omap_hwmod_disable_wakeup(struct omap_hwmod *oh) | |||
2467 | v = oh->_sysc_cache; | 2514 | v = oh->_sysc_cache; |
2468 | _disable_wakeup(oh, &v); | 2515 | _disable_wakeup(oh, &v); |
2469 | _write_sysconfig(v, oh); | 2516 | _write_sysconfig(v, oh); |
2517 | _set_idle_ioring_wakeup(oh, false); | ||
2470 | spin_unlock_irqrestore(&oh->_lock, flags); | 2518 | spin_unlock_irqrestore(&oh->_lock, flags); |
2471 | 2519 | ||
2472 | return 0; | 2520 | return 0; |
@@ -2683,3 +2731,57 @@ int omap_hwmod_no_setup_reset(struct omap_hwmod *oh) | |||
2683 | 2731 | ||
2684 | return 0; | 2732 | return 0; |
2685 | } | 2733 | } |
2734 | |||
2735 | /** | ||
2736 | * omap_hwmod_pad_route_irq - route an I/O pad wakeup to a particular MPU IRQ | ||
2737 | * @oh: struct omap_hwmod * containing hwmod mux entries | ||
2738 | * @pad_idx: array index in oh->mux of the hwmod mux entry to route wakeup | ||
2739 | * @irq_idx: the hwmod mpu_irqs array index of the IRQ to trigger on wakeup | ||
2740 | * | ||
2741 | * When an I/O pad wakeup arrives for the dynamic or wakeup hwmod mux | ||
2742 | * entry number @pad_idx for the hwmod @oh, trigger the interrupt | ||
2743 | * service routine for the hwmod's mpu_irqs array index @irq_idx. If | ||
2744 | * this function is not called for a given pad_idx, then the ISR | ||
2745 | * associated with @oh's first MPU IRQ will be triggered when an I/O | ||
2746 | * pad wakeup occurs on that pad. Note that @pad_idx is the index of | ||
2747 | * the _dynamic or wakeup_ entry: if there are other entries not | ||
2748 | * marked with OMAP_DEVICE_PAD_WAKEUP or OMAP_DEVICE_PAD_REMUX, these | ||
2749 | * entries are NOT COUNTED in the dynamic pad index. This function | ||
2750 | * must be called separately for each pad that requires its interrupt | ||
2751 | * to be re-routed this way. Returns -EINVAL if there is an argument | ||
2752 | * problem or if @oh does not have hwmod mux entries or MPU IRQs; | ||
2753 | * returns -ENOMEM if memory cannot be allocated; or 0 upon success. | ||
2754 | * | ||
2755 | * XXX This function interface is fragile. Rather than using array | ||
2756 | * indexes, which are subject to unpredictable change, it should be | ||
2757 | * using hwmod IRQ names, and some other stable key for the hwmod mux | ||
2758 | * pad records. | ||
2759 | */ | ||
2760 | int omap_hwmod_pad_route_irq(struct omap_hwmod *oh, int pad_idx, int irq_idx) | ||
2761 | { | ||
2762 | int nr_irqs; | ||
2763 | |||
2764 | might_sleep(); | ||
2765 | |||
2766 | if (!oh || !oh->mux || !oh->mpu_irqs || pad_idx < 0 || | ||
2767 | pad_idx >= oh->mux->nr_pads_dynamic) | ||
2768 | return -EINVAL; | ||
2769 | |||
2770 | /* Check the number of available mpu_irqs */ | ||
2771 | for (nr_irqs = 0; oh->mpu_irqs[nr_irqs].irq >= 0; nr_irqs++) | ||
2772 | ; | ||
2773 | |||
2774 | if (irq_idx >= nr_irqs) | ||
2775 | return -EINVAL; | ||
2776 | |||
2777 | if (!oh->mux->irqs) { | ||
2778 | /* XXX What frees this? */ | ||
2779 | oh->mux->irqs = kzalloc(sizeof(int) * oh->mux->nr_pads_dynamic, | ||
2780 | GFP_KERNEL); | ||
2781 | if (!oh->mux->irqs) | ||
2782 | return -ENOMEM; | ||
2783 | } | ||
2784 | oh->mux->irqs[pad_idx] = irq_idx; | ||
2785 | |||
2786 | return 0; | ||
2787 | } | ||
diff --git a/arch/arm/mach-omap2/pm34xx.c b/arch/arm/mach-omap2/pm34xx.c index fa637dfdda53..53b5b1a71e0e 100644 --- a/arch/arm/mach-omap2/pm34xx.c +++ b/arch/arm/mach-omap2/pm34xx.c | |||
@@ -195,7 +195,7 @@ static void omap3_save_secure_ram_context(void) | |||
195 | * that any peripheral wake-up events occurring while attempting to | 195 | * that any peripheral wake-up events occurring while attempting to |
196 | * clear the PM_WKST_x are detected and cleared. | 196 | * clear the PM_WKST_x are detected and cleared. |
197 | */ | 197 | */ |
198 | static int prcm_clear_mod_irqs(s16 module, u8 regs) | 198 | static int prcm_clear_mod_irqs(s16 module, u8 regs, u32 ignore_bits) |
199 | { | 199 | { |
200 | u32 wkst, fclk, iclk, clken; | 200 | u32 wkst, fclk, iclk, clken; |
201 | u16 wkst_off = (regs == 3) ? OMAP3430ES2_PM_WKST3 : PM_WKST1; | 201 | u16 wkst_off = (regs == 3) ? OMAP3430ES2_PM_WKST3 : PM_WKST1; |
@@ -207,6 +207,7 @@ static int prcm_clear_mod_irqs(s16 module, u8 regs) | |||
207 | 207 | ||
208 | wkst = omap2_prm_read_mod_reg(module, wkst_off); | 208 | wkst = omap2_prm_read_mod_reg(module, wkst_off); |
209 | wkst &= omap2_prm_read_mod_reg(module, grpsel_off); | 209 | wkst &= omap2_prm_read_mod_reg(module, grpsel_off); |
210 | wkst &= ~ignore_bits; | ||
210 | if (wkst) { | 211 | if (wkst) { |
211 | iclk = omap2_cm_read_mod_reg(module, iclk_off); | 212 | iclk = omap2_cm_read_mod_reg(module, iclk_off); |
212 | fclk = omap2_cm_read_mod_reg(module, fclk_off); | 213 | fclk = omap2_cm_read_mod_reg(module, fclk_off); |
@@ -222,6 +223,7 @@ static int prcm_clear_mod_irqs(s16 module, u8 regs) | |||
222 | omap2_cm_set_mod_reg_bits(clken, module, fclk_off); | 223 | omap2_cm_set_mod_reg_bits(clken, module, fclk_off); |
223 | omap2_prm_write_mod_reg(wkst, module, wkst_off); | 224 | omap2_prm_write_mod_reg(wkst, module, wkst_off); |
224 | wkst = omap2_prm_read_mod_reg(module, wkst_off); | 225 | wkst = omap2_prm_read_mod_reg(module, wkst_off); |
226 | wkst &= ~ignore_bits; | ||
225 | c++; | 227 | c++; |
226 | } | 228 | } |
227 | omap2_cm_write_mod_reg(iclk, module, iclk_off); | 229 | omap2_cm_write_mod_reg(iclk, module, iclk_off); |
@@ -231,76 +233,35 @@ static int prcm_clear_mod_irqs(s16 module, u8 regs) | |||
231 | return c; | 233 | return c; |
232 | } | 234 | } |
233 | 235 | ||
234 | static int _prcm_int_handle_wakeup(void) | 236 | static irqreturn_t _prcm_int_handle_io(int irq, void *unused) |
235 | { | 237 | { |
236 | int c; | 238 | int c; |
237 | 239 | ||
238 | c = prcm_clear_mod_irqs(WKUP_MOD, 1); | 240 | c = prcm_clear_mod_irqs(WKUP_MOD, 1, |
239 | c += prcm_clear_mod_irqs(CORE_MOD, 1); | 241 | ~(OMAP3430_ST_IO_MASK | OMAP3430_ST_IO_CHAIN_MASK)); |
240 | c += prcm_clear_mod_irqs(OMAP3430_PER_MOD, 1); | ||
241 | if (omap_rev() > OMAP3430_REV_ES1_0) { | ||
242 | c += prcm_clear_mod_irqs(CORE_MOD, 3); | ||
243 | c += prcm_clear_mod_irqs(OMAP3430ES2_USBHOST_MOD, 1); | ||
244 | } | ||
245 | 242 | ||
246 | return c; | 243 | return c ? IRQ_HANDLED : IRQ_NONE; |
247 | } | 244 | } |
248 | 245 | ||
249 | /* | 246 | static irqreturn_t _prcm_int_handle_wakeup(int irq, void *unused) |
250 | * PRCM Interrupt Handler | ||
251 | * | ||
252 | * The PRM_IRQSTATUS_MPU register indicates if there are any pending | ||
253 | * interrupts from the PRCM for the MPU. These bits must be cleared in | ||
254 | * order to clear the PRCM interrupt. The PRCM interrupt handler is | ||
255 | * implemented to simply clear the PRM_IRQSTATUS_MPU in order to clear | ||
256 | * the PRCM interrupt. Please note that bit 0 of the PRM_IRQSTATUS_MPU | ||
257 | * register indicates that a wake-up event is pending for the MPU and | ||
258 | * this bit can only be cleared if the all the wake-up events latched | ||
259 | * in the various PM_WKST_x registers have been cleared. The interrupt | ||
260 | * handler is implemented using a do-while loop so that if a wake-up | ||
261 | * event occurred during the processing of the prcm interrupt handler | ||
262 | * (setting a bit in the corresponding PM_WKST_x register and thus | ||
263 | * preventing us from clearing bit 0 of the PRM_IRQSTATUS_MPU register) | ||
264 | * this would be handled. | ||
265 | */ | ||
266 | static irqreturn_t prcm_interrupt_handler (int irq, void *dev_id) | ||
267 | { | 247 | { |
268 | u32 irqenable_mpu, irqstatus_mpu; | 248 | int c; |
269 | int c = 0; | ||
270 | |||
271 | irqenable_mpu = omap2_prm_read_mod_reg(OCP_MOD, | ||
272 | OMAP3_PRM_IRQENABLE_MPU_OFFSET); | ||
273 | irqstatus_mpu = omap2_prm_read_mod_reg(OCP_MOD, | ||
274 | OMAP3_PRM_IRQSTATUS_MPU_OFFSET); | ||
275 | irqstatus_mpu &= irqenable_mpu; | ||
276 | |||
277 | do { | ||
278 | if (irqstatus_mpu & (OMAP3430_WKUP_ST_MASK | | ||
279 | OMAP3430_IO_ST_MASK)) { | ||
280 | c = _prcm_int_handle_wakeup(); | ||
281 | |||
282 | /* | ||
283 | * Is the MPU PRCM interrupt handler racing with the | ||
284 | * IVA2 PRCM interrupt handler ? | ||
285 | */ | ||
286 | WARN(c == 0, "prcm: WARNING: PRCM indicated MPU wakeup " | ||
287 | "but no wakeup sources are marked\n"); | ||
288 | } else { | ||
289 | /* XXX we need to expand our PRCM interrupt handler */ | ||
290 | WARN(1, "prcm: WARNING: PRCM interrupt received, but " | ||
291 | "no code to handle it (%08x)\n", irqstatus_mpu); | ||
292 | } | ||
293 | |||
294 | omap2_prm_write_mod_reg(irqstatus_mpu, OCP_MOD, | ||
295 | OMAP3_PRM_IRQSTATUS_MPU_OFFSET); | ||
296 | |||
297 | irqstatus_mpu = omap2_prm_read_mod_reg(OCP_MOD, | ||
298 | OMAP3_PRM_IRQSTATUS_MPU_OFFSET); | ||
299 | irqstatus_mpu &= irqenable_mpu; | ||
300 | 249 | ||
301 | } while (irqstatus_mpu); | 250 | /* |
251 | * Clear all except ST_IO and ST_IO_CHAIN for wkup module, | ||
252 | * these are handled in a separate handler to avoid acking | ||
253 | * IO events before parsing in mux code | ||
254 | */ | ||
255 | c = prcm_clear_mod_irqs(WKUP_MOD, 1, | ||
256 | OMAP3430_ST_IO_MASK | OMAP3430_ST_IO_CHAIN_MASK); | ||
257 | c += prcm_clear_mod_irqs(CORE_MOD, 1, 0); | ||
258 | c += prcm_clear_mod_irqs(OMAP3430_PER_MOD, 1, 0); | ||
259 | if (omap_rev() > OMAP3430_REV_ES1_0) { | ||
260 | c += prcm_clear_mod_irqs(CORE_MOD, 3, 0); | ||
261 | c += prcm_clear_mod_irqs(OMAP3430ES2_USBHOST_MOD, 1, 0); | ||
262 | } | ||
302 | 263 | ||
303 | return IRQ_HANDLED; | 264 | return c ? IRQ_HANDLED : IRQ_NONE; |
304 | } | 265 | } |
305 | 266 | ||
306 | static void omap34xx_save_context(u32 *save) | 267 | static void omap34xx_save_context(u32 *save) |
@@ -581,6 +542,7 @@ static int omap3_pm_begin(suspend_state_t state) | |||
581 | disable_hlt(); | 542 | disable_hlt(); |
582 | suspend_state = state; | 543 | suspend_state = state; |
583 | omap_uart_enable_irqs(0); | 544 | omap_uart_enable_irqs(0); |
545 | omap_prcm_irq_prepare(); | ||
584 | return 0; | 546 | return 0; |
585 | } | 547 | } |
586 | 548 | ||
@@ -592,10 +554,16 @@ static void omap3_pm_end(void) | |||
592 | return; | 554 | return; |
593 | } | 555 | } |
594 | 556 | ||
557 | static void omap3_pm_finish(void) | ||
558 | { | ||
559 | omap_prcm_irq_complete(); | ||
560 | } | ||
561 | |||
595 | static const struct platform_suspend_ops omap_pm_ops = { | 562 | static const struct platform_suspend_ops omap_pm_ops = { |
596 | .begin = omap3_pm_begin, | 563 | .begin = omap3_pm_begin, |
597 | .end = omap3_pm_end, | 564 | .end = omap3_pm_end, |
598 | .enter = omap3_pm_enter, | 565 | .enter = omap3_pm_enter, |
566 | .finish = omap3_pm_finish, | ||
599 | .valid = suspend_valid_only_mem, | 567 | .valid = suspend_valid_only_mem, |
600 | }; | 568 | }; |
601 | #endif /* CONFIG_SUSPEND */ | 569 | #endif /* CONFIG_SUSPEND */ |
@@ -701,10 +669,6 @@ static void __init prcm_setup_regs(void) | |||
701 | OMAP3430_GRPSEL_GPT1_MASK | | 669 | OMAP3430_GRPSEL_GPT1_MASK | |
702 | OMAP3430_GRPSEL_GPT12_MASK, | 670 | OMAP3430_GRPSEL_GPT12_MASK, |
703 | WKUP_MOD, OMAP3430_PM_MPUGRPSEL); | 671 | WKUP_MOD, OMAP3430_PM_MPUGRPSEL); |
704 | /* For some reason IO doesn't generate wakeup event even if | ||
705 | * it is selected to mpu wakeup goup */ | ||
706 | omap2_prm_write_mod_reg(OMAP3430_IO_EN_MASK | OMAP3430_WKUP_EN_MASK, | ||
707 | OCP_MOD, OMAP3_PRM_IRQENABLE_MPU_OFFSET); | ||
708 | 672 | ||
709 | /* Enable PM_WKEN to support DSS LPR */ | 673 | /* Enable PM_WKEN to support DSS LPR */ |
710 | omap2_prm_write_mod_reg(OMAP3430_PM_WKEN_DSS_EN_DSS_MASK, | 674 | omap2_prm_write_mod_reg(OMAP3430_PM_WKEN_DSS_EN_DSS_MASK, |
@@ -881,12 +845,21 @@ static int __init omap3_pm_init(void) | |||
881 | * supervised mode for powerdomains */ | 845 | * supervised mode for powerdomains */ |
882 | prcm_setup_regs(); | 846 | prcm_setup_regs(); |
883 | 847 | ||
884 | ret = request_irq(INT_34XX_PRCM_MPU_IRQ, | 848 | ret = request_irq(omap_prcm_event_to_irq("wkup"), |
885 | (irq_handler_t)prcm_interrupt_handler, | 849 | _prcm_int_handle_wakeup, IRQF_NO_SUSPEND, "pm_wkup", NULL); |
886 | IRQF_DISABLED, "prcm", NULL); | 850 | |
851 | if (ret) { | ||
852 | pr_err("pm: Failed to request pm_wkup irq\n"); | ||
853 | goto err1; | ||
854 | } | ||
855 | |||
856 | /* IO interrupt is shared with mux code */ | ||
857 | ret = request_irq(omap_prcm_event_to_irq("io"), | ||
858 | _prcm_int_handle_io, IRQF_SHARED | IRQF_NO_SUSPEND, "pm_io", | ||
859 | omap3_pm_init); | ||
860 | |||
887 | if (ret) { | 861 | if (ret) { |
888 | printk(KERN_ERR "request_irq failed to register for 0x%x\n", | 862 | pr_err("pm: Failed to request pm_io irq\n"); |
889 | INT_34XX_PRCM_MPU_IRQ); | ||
890 | goto err1; | 863 | goto err1; |
891 | } | 864 | } |
892 | 865 | ||
diff --git a/arch/arm/mach-omap2/prcm-common.h b/arch/arm/mach-omap2/prcm-common.h index 0363dcb0ef93..0f69d8fc5628 100644 --- a/arch/arm/mach-omap2/prcm-common.h +++ b/arch/arm/mach-omap2/prcm-common.h | |||
@@ -4,7 +4,7 @@ | |||
4 | /* | 4 | /* |
5 | * OMAP2/3 PRCM base and module definitions | 5 | * OMAP2/3 PRCM base and module definitions |
6 | * | 6 | * |
7 | * Copyright (C) 2007-2009 Texas Instruments, Inc. | 7 | * Copyright (C) 2007-2009, 2011 Texas Instruments, Inc. |
8 | * Copyright (C) 2007-2009 Nokia Corporation | 8 | * Copyright (C) 2007-2009 Nokia Corporation |
9 | * | 9 | * |
10 | * Written by Paul Walmsley | 10 | * Written by Paul Walmsley |
@@ -408,6 +408,79 @@ | |||
408 | extern void __iomem *prm_base; | 408 | extern void __iomem *prm_base; |
409 | extern void __iomem *cm_base; | 409 | extern void __iomem *cm_base; |
410 | extern void __iomem *cm2_base; | 410 | extern void __iomem *cm2_base; |
411 | |||
412 | /** | ||
413 | * struct omap_prcm_irq - describes a PRCM interrupt bit | ||
414 | * @name: a short name describing the interrupt type, e.g. "wkup" or "io" | ||
415 | * @offset: the bit shift of the interrupt inside the IRQ{ENABLE,STATUS} regs | ||
416 | * @priority: should this interrupt be handled before @priority=false IRQs? | ||
417 | * | ||
418 | * Describes interrupt bits inside the PRM_IRQ{ENABLE,STATUS}_MPU* registers. | ||
419 | * On systems with multiple PRM MPU IRQ registers, the bitfields read from | ||
420 | * the registers are concatenated, so @offset could be > 31 on these systems - | ||
421 | * see omap_prm_irq_handler() for more details. I/O ring interrupts should | ||
422 | * have @priority set to true. | ||
423 | */ | ||
424 | struct omap_prcm_irq { | ||
425 | const char *name; | ||
426 | unsigned int offset; | ||
427 | bool priority; | ||
428 | }; | ||
429 | |||
430 | /** | ||
431 | * struct omap_prcm_irq_setup - PRCM interrupt controller details | ||
432 | * @ack: PRM register offset for the first PRM_IRQSTATUS_MPU register | ||
433 | * @mask: PRM register offset for the first PRM_IRQENABLE_MPU register | ||
434 | * @nr_regs: number of PRM_IRQ{STATUS,ENABLE}_MPU* registers | ||
435 | * @nr_irqs: number of entries in the @irqs array | ||
436 | * @irqs: ptr to an array of PRCM interrupt bits (see @nr_irqs) | ||
437 | * @irq: MPU IRQ asserted when a PRCM interrupt arrives | ||
438 | * @read_pending_irqs: fn ptr to determine if any PRCM IRQs are pending | ||
439 | * @ocp_barrier: fn ptr to force buffered PRM writes to complete | ||
440 | * @save_and_clear_irqen: fn ptr to save and clear IRQENABLE regs | ||
441 | * @restore_irqen: fn ptr to save and clear IRQENABLE regs | ||
442 | * @saved_mask: IRQENABLE regs are saved here during suspend | ||
443 | * @priority_mask: 1 bit per IRQ, set to 1 if omap_prcm_irq.priority = true | ||
444 | * @base_irq: base dynamic IRQ number, returned from irq_alloc_descs() in init | ||
445 | * @suspended: set to true after Linux suspend code has called our ->prepare() | ||
446 | * @suspend_save_flag: set to true after IRQ masks have been saved and disabled | ||
447 | * | ||
448 | * @saved_mask, @priority_mask, @base_irq, @suspended, and | ||
449 | * @suspend_save_flag are populated dynamically, and are not to be | ||
450 | * specified in static initializers. | ||
451 | */ | ||
452 | struct omap_prcm_irq_setup { | ||
453 | u16 ack; | ||
454 | u16 mask; | ||
455 | u8 nr_regs; | ||
456 | u8 nr_irqs; | ||
457 | const struct omap_prcm_irq *irqs; | ||
458 | int irq; | ||
459 | void (*read_pending_irqs)(unsigned long *events); | ||
460 | void (*ocp_barrier)(void); | ||
461 | void (*save_and_clear_irqen)(u32 *saved_mask); | ||
462 | void (*restore_irqen)(u32 *saved_mask); | ||
463 | u32 *saved_mask; | ||
464 | u32 *priority_mask; | ||
465 | int base_irq; | ||
466 | bool suspended; | ||
467 | bool suspend_save_flag; | ||
468 | }; | ||
469 | |||
470 | /* OMAP_PRCM_IRQ: convenience macro for creating struct omap_prcm_irq records */ | ||
471 | #define OMAP_PRCM_IRQ(_name, _offset, _priority) { \ | ||
472 | .name = _name, \ | ||
473 | .offset = _offset, \ | ||
474 | .priority = _priority \ | ||
475 | } | ||
476 | |||
477 | extern void omap_prcm_irq_cleanup(void); | ||
478 | extern int omap_prcm_register_chain_handler( | ||
479 | struct omap_prcm_irq_setup *irq_setup); | ||
480 | extern int omap_prcm_event_to_irq(const char *event); | ||
481 | extern void omap_prcm_irq_prepare(void); | ||
482 | extern void omap_prcm_irq_complete(void); | ||
483 | |||
411 | # endif | 484 | # endif |
412 | 485 | ||
413 | #endif | 486 | #endif |
diff --git a/arch/arm/mach-omap2/prm2xxx_3xxx.c b/arch/arm/mach-omap2/prm2xxx_3xxx.c index 9a08ba397327..c1c4d86a79a8 100644 --- a/arch/arm/mach-omap2/prm2xxx_3xxx.c +++ b/arch/arm/mach-omap2/prm2xxx_3xxx.c | |||
@@ -1,7 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * OMAP2/3 PRM module functions | 2 | * OMAP2/3 PRM module functions |
3 | * | 3 | * |
4 | * Copyright (C) 2010 Texas Instruments, Inc. | 4 | * Copyright (C) 2010-2011 Texas Instruments, Inc. |
5 | * Copyright (C) 2010 Nokia Corporation | 5 | * Copyright (C) 2010 Nokia Corporation |
6 | * Benoît Cousson | 6 | * Benoît Cousson |
7 | * Paul Walmsley | 7 | * Paul Walmsley |
@@ -27,6 +27,24 @@ | |||
27 | #include "prm-regbits-24xx.h" | 27 | #include "prm-regbits-24xx.h" |
28 | #include "prm-regbits-34xx.h" | 28 | #include "prm-regbits-34xx.h" |
29 | 29 | ||
30 | static const struct omap_prcm_irq omap3_prcm_irqs[] = { | ||
31 | OMAP_PRCM_IRQ("wkup", 0, 0), | ||
32 | OMAP_PRCM_IRQ("io", 9, 1), | ||
33 | }; | ||
34 | |||
35 | static struct omap_prcm_irq_setup omap3_prcm_irq_setup = { | ||
36 | .ack = OMAP3_PRM_IRQSTATUS_MPU_OFFSET, | ||
37 | .mask = OMAP3_PRM_IRQENABLE_MPU_OFFSET, | ||
38 | .nr_regs = 1, | ||
39 | .irqs = omap3_prcm_irqs, | ||
40 | .nr_irqs = ARRAY_SIZE(omap3_prcm_irqs), | ||
41 | .irq = INT_34XX_PRCM_MPU_IRQ, | ||
42 | .read_pending_irqs = &omap3xxx_prm_read_pending_irqs, | ||
43 | .ocp_barrier = &omap3xxx_prm_ocp_barrier, | ||
44 | .save_and_clear_irqen = &omap3xxx_prm_save_and_clear_irqen, | ||
45 | .restore_irqen = &omap3xxx_prm_restore_irqen, | ||
46 | }; | ||
47 | |||
30 | u32 omap2_prm_read_mod_reg(s16 module, u16 idx) | 48 | u32 omap2_prm_read_mod_reg(s16 module, u16 idx) |
31 | { | 49 | { |
32 | return __raw_readl(prm_base + module + idx); | 50 | return __raw_readl(prm_base + module + idx); |
@@ -212,3 +230,80 @@ u32 omap3_prm_vcvp_rmw(u32 mask, u32 bits, u8 offset) | |||
212 | { | 230 | { |
213 | return omap2_prm_rmw_mod_reg_bits(mask, bits, OMAP3430_GR_MOD, offset); | 231 | return omap2_prm_rmw_mod_reg_bits(mask, bits, OMAP3430_GR_MOD, offset); |
214 | } | 232 | } |
233 | |||
234 | /** | ||
235 | * omap3xxx_prm_read_pending_irqs - read pending PRM MPU IRQs into @events | ||
236 | * @events: ptr to a u32, preallocated by caller | ||
237 | * | ||
238 | * Read PRM_IRQSTATUS_MPU bits, AND'ed with the currently-enabled PRM | ||
239 | * MPU IRQs, and store the result into the u32 pointed to by @events. | ||
240 | * No return value. | ||
241 | */ | ||
242 | void omap3xxx_prm_read_pending_irqs(unsigned long *events) | ||
243 | { | ||
244 | u32 mask, st; | ||
245 | |||
246 | /* XXX Can the mask read be avoided (e.g., can it come from RAM?) */ | ||
247 | mask = omap2_prm_read_mod_reg(OCP_MOD, OMAP3_PRM_IRQENABLE_MPU_OFFSET); | ||
248 | st = omap2_prm_read_mod_reg(OCP_MOD, OMAP3_PRM_IRQSTATUS_MPU_OFFSET); | ||
249 | |||
250 | events[0] = mask & st; | ||
251 | } | ||
252 | |||
253 | /** | ||
254 | * omap3xxx_prm_ocp_barrier - force buffered MPU writes to the PRM to complete | ||
255 | * | ||
256 | * Force any buffered writes to the PRM IP block to complete. Needed | ||
257 | * by the PRM IRQ handler, which reads and writes directly to the IP | ||
258 | * block, to avoid race conditions after acknowledging or clearing IRQ | ||
259 | * bits. No return value. | ||
260 | */ | ||
261 | void omap3xxx_prm_ocp_barrier(void) | ||
262 | { | ||
263 | omap2_prm_read_mod_reg(OCP_MOD, OMAP3_PRM_REVISION_OFFSET); | ||
264 | } | ||
265 | |||
266 | /** | ||
267 | * omap3xxx_prm_save_and_clear_irqen - save/clear PRM_IRQENABLE_MPU reg | ||
268 | * @saved_mask: ptr to a u32 array to save IRQENABLE bits | ||
269 | * | ||
270 | * Save the PRM_IRQENABLE_MPU register to @saved_mask. @saved_mask | ||
271 | * must be allocated by the caller. Intended to be used in the PRM | ||
272 | * interrupt handler suspend callback. The OCP barrier is needed to | ||
273 | * ensure the write to disable PRM interrupts reaches the PRM before | ||
274 | * returning; otherwise, spurious interrupts might occur. No return | ||
275 | * value. | ||
276 | */ | ||
277 | void omap3xxx_prm_save_and_clear_irqen(u32 *saved_mask) | ||
278 | { | ||
279 | saved_mask[0] = omap2_prm_read_mod_reg(OCP_MOD, | ||
280 | OMAP3_PRM_IRQENABLE_MPU_OFFSET); | ||
281 | omap2_prm_write_mod_reg(0, OCP_MOD, OMAP3_PRM_IRQENABLE_MPU_OFFSET); | ||
282 | |||
283 | /* OCP barrier */ | ||
284 | omap2_prm_read_mod_reg(OCP_MOD, OMAP3_PRM_REVISION_OFFSET); | ||
285 | } | ||
286 | |||
287 | /** | ||
288 | * omap3xxx_prm_restore_irqen - set PRM_IRQENABLE_MPU register from args | ||
289 | * @saved_mask: ptr to a u32 array of IRQENABLE bits saved previously | ||
290 | * | ||
291 | * Restore the PRM_IRQENABLE_MPU register from @saved_mask. Intended | ||
292 | * to be used in the PRM interrupt handler resume callback to restore | ||
293 | * values saved by omap3xxx_prm_save_and_clear_irqen(). No OCP | ||
294 | * barrier should be needed here; any pending PRM interrupts will fire | ||
295 | * once the writes reach the PRM. No return value. | ||
296 | */ | ||
297 | void omap3xxx_prm_restore_irqen(u32 *saved_mask) | ||
298 | { | ||
299 | omap2_prm_write_mod_reg(saved_mask[0], OCP_MOD, | ||
300 | OMAP3_PRM_IRQENABLE_MPU_OFFSET); | ||
301 | } | ||
302 | |||
303 | static int __init omap3xxx_prcm_init(void) | ||
304 | { | ||
305 | if (cpu_is_omap34xx()) | ||
306 | return omap_prcm_register_chain_handler(&omap3_prcm_irq_setup); | ||
307 | return 0; | ||
308 | } | ||
309 | subsys_initcall(omap3xxx_prcm_init); | ||
diff --git a/arch/arm/mach-omap2/prm2xxx_3xxx.h b/arch/arm/mach-omap2/prm2xxx_3xxx.h index cef533df0861..70ac2a19dc5f 100644 --- a/arch/arm/mach-omap2/prm2xxx_3xxx.h +++ b/arch/arm/mach-omap2/prm2xxx_3xxx.h | |||
@@ -1,7 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * OMAP2/3 Power/Reset Management (PRM) register definitions | 2 | * OMAP2/3 Power/Reset Management (PRM) register definitions |
3 | * | 3 | * |
4 | * Copyright (C) 2007-2009 Texas Instruments, Inc. | 4 | * Copyright (C) 2007-2009, 2011 Texas Instruments, Inc. |
5 | * Copyright (C) 2008-2010 Nokia Corporation | 5 | * Copyright (C) 2008-2010 Nokia Corporation |
6 | * Paul Walmsley | 6 | * Paul Walmsley |
7 | * | 7 | * |
@@ -314,6 +314,13 @@ void omap3_prm_vp_clear_txdone(u8 vp_id); | |||
314 | extern u32 omap3_prm_vcvp_read(u8 offset); | 314 | extern u32 omap3_prm_vcvp_read(u8 offset); |
315 | extern void omap3_prm_vcvp_write(u32 val, u8 offset); | 315 | extern void omap3_prm_vcvp_write(u32 val, u8 offset); |
316 | extern u32 omap3_prm_vcvp_rmw(u32 mask, u32 bits, u8 offset); | 316 | extern u32 omap3_prm_vcvp_rmw(u32 mask, u32 bits, u8 offset); |
317 | |||
318 | /* PRM interrupt-related functions */ | ||
319 | extern void omap3xxx_prm_read_pending_irqs(unsigned long *events); | ||
320 | extern void omap3xxx_prm_ocp_barrier(void); | ||
321 | extern void omap3xxx_prm_save_and_clear_irqen(u32 *saved_mask); | ||
322 | extern void omap3xxx_prm_restore_irqen(u32 *saved_mask); | ||
323 | |||
317 | #endif /* CONFIG_ARCH_OMAP4 */ | 324 | #endif /* CONFIG_ARCH_OMAP4 */ |
318 | 325 | ||
319 | #endif | 326 | #endif |
diff --git a/arch/arm/mach-omap2/prm44xx.c b/arch/arm/mach-omap2/prm44xx.c index dd885eecf22a..33dd655e6aab 100644 --- a/arch/arm/mach-omap2/prm44xx.c +++ b/arch/arm/mach-omap2/prm44xx.c | |||
@@ -27,6 +27,24 @@ | |||
27 | #include "prcm44xx.h" | 27 | #include "prcm44xx.h" |
28 | #include "prminst44xx.h" | 28 | #include "prminst44xx.h" |
29 | 29 | ||
30 | static const struct omap_prcm_irq omap4_prcm_irqs[] = { | ||
31 | OMAP_PRCM_IRQ("wkup", 0, 0), | ||
32 | OMAP_PRCM_IRQ("io", 9, 1), | ||
33 | }; | ||
34 | |||
35 | static struct omap_prcm_irq_setup omap4_prcm_irq_setup = { | ||
36 | .ack = OMAP4_PRM_IRQSTATUS_MPU_OFFSET, | ||
37 | .mask = OMAP4_PRM_IRQENABLE_MPU_OFFSET, | ||
38 | .nr_regs = 2, | ||
39 | .irqs = omap4_prcm_irqs, | ||
40 | .nr_irqs = ARRAY_SIZE(omap4_prcm_irqs), | ||
41 | .irq = OMAP44XX_IRQ_PRCM, | ||
42 | .read_pending_irqs = &omap44xx_prm_read_pending_irqs, | ||
43 | .ocp_barrier = &omap44xx_prm_ocp_barrier, | ||
44 | .save_and_clear_irqen = &omap44xx_prm_save_and_clear_irqen, | ||
45 | .restore_irqen = &omap44xx_prm_restore_irqen, | ||
46 | }; | ||
47 | |||
30 | /* PRM low-level functions */ | 48 | /* PRM low-level functions */ |
31 | 49 | ||
32 | /* Read a register in a CM/PRM instance in the PRM module */ | 50 | /* Read a register in a CM/PRM instance in the PRM module */ |
@@ -121,3 +139,101 @@ u32 omap4_prm_vcvp_rmw(u32 mask, u32 bits, u8 offset) | |||
121 | OMAP4430_PRM_DEVICE_INST, | 139 | OMAP4430_PRM_DEVICE_INST, |
122 | offset); | 140 | offset); |
123 | } | 141 | } |
142 | |||
143 | static inline u32 _read_pending_irq_reg(u16 irqen_offs, u16 irqst_offs) | ||
144 | { | ||
145 | u32 mask, st; | ||
146 | |||
147 | /* XXX read mask from RAM? */ | ||
148 | mask = omap4_prm_read_inst_reg(OMAP4430_PRM_DEVICE_INST, irqen_offs); | ||
149 | st = omap4_prm_read_inst_reg(OMAP4430_PRM_DEVICE_INST, irqst_offs); | ||
150 | |||
151 | return mask & st; | ||
152 | } | ||
153 | |||
154 | /** | ||
155 | * omap44xx_prm_read_pending_irqs - read pending PRM MPU IRQs into @events | ||
156 | * @events: ptr to two consecutive u32s, preallocated by caller | ||
157 | * | ||
158 | * Read PRM_IRQSTATUS_MPU* bits, AND'ed with the currently-enabled PRM | ||
159 | * MPU IRQs, and store the result into the two u32s pointed to by @events. | ||
160 | * No return value. | ||
161 | */ | ||
162 | void omap44xx_prm_read_pending_irqs(unsigned long *events) | ||
163 | { | ||
164 | events[0] = _read_pending_irq_reg(OMAP4_PRM_IRQENABLE_MPU_OFFSET, | ||
165 | OMAP4_PRM_IRQSTATUS_MPU_OFFSET); | ||
166 | |||
167 | events[1] = _read_pending_irq_reg(OMAP4_PRM_IRQENABLE_MPU_2_OFFSET, | ||
168 | OMAP4_PRM_IRQSTATUS_MPU_2_OFFSET); | ||
169 | } | ||
170 | |||
171 | /** | ||
172 | * omap44xx_prm_ocp_barrier - force buffered MPU writes to the PRM to complete | ||
173 | * | ||
174 | * Force any buffered writes to the PRM IP block to complete. Needed | ||
175 | * by the PRM IRQ handler, which reads and writes directly to the IP | ||
176 | * block, to avoid race conditions after acknowledging or clearing IRQ | ||
177 | * bits. No return value. | ||
178 | */ | ||
179 | void omap44xx_prm_ocp_barrier(void) | ||
180 | { | ||
181 | omap4_prm_read_inst_reg(OMAP4430_PRM_DEVICE_INST, | ||
182 | OMAP4_REVISION_PRM_OFFSET); | ||
183 | } | ||
184 | |||
185 | /** | ||
186 | * omap44xx_prm_save_and_clear_irqen - save/clear PRM_IRQENABLE_MPU* regs | ||
187 | * @saved_mask: ptr to a u32 array to save IRQENABLE bits | ||
188 | * | ||
189 | * Save the PRM_IRQENABLE_MPU and PRM_IRQENABLE_MPU_2 registers to | ||
190 | * @saved_mask. @saved_mask must be allocated by the caller. | ||
191 | * Intended to be used in the PRM interrupt handler suspend callback. | ||
192 | * The OCP barrier is needed to ensure the write to disable PRM | ||
193 | * interrupts reaches the PRM before returning; otherwise, spurious | ||
194 | * interrupts might occur. No return value. | ||
195 | */ | ||
196 | void omap44xx_prm_save_and_clear_irqen(u32 *saved_mask) | ||
197 | { | ||
198 | saved_mask[0] = | ||
199 | omap4_prm_read_inst_reg(OMAP4430_PRM_DEVICE_INST, | ||
200 | OMAP4_PRM_IRQSTATUS_MPU_OFFSET); | ||
201 | saved_mask[1] = | ||
202 | omap4_prm_read_inst_reg(OMAP4430_PRM_DEVICE_INST, | ||
203 | OMAP4_PRM_IRQSTATUS_MPU_2_OFFSET); | ||
204 | |||
205 | omap4_prm_write_inst_reg(0, OMAP4430_PRM_DEVICE_INST, | ||
206 | OMAP4_PRM_IRQENABLE_MPU_OFFSET); | ||
207 | omap4_prm_write_inst_reg(0, OMAP4430_PRM_DEVICE_INST, | ||
208 | OMAP4_PRM_IRQENABLE_MPU_2_OFFSET); | ||
209 | |||
210 | /* OCP barrier */ | ||
211 | omap4_prm_read_inst_reg(OMAP4430_PRM_DEVICE_INST, | ||
212 | OMAP4_REVISION_PRM_OFFSET); | ||
213 | } | ||
214 | |||
215 | /** | ||
216 | * omap44xx_prm_restore_irqen - set PRM_IRQENABLE_MPU* registers from args | ||
217 | * @saved_mask: ptr to a u32 array of IRQENABLE bits saved previously | ||
218 | * | ||
219 | * Restore the PRM_IRQENABLE_MPU and PRM_IRQENABLE_MPU_2 registers from | ||
220 | * @saved_mask. Intended to be used in the PRM interrupt handler resume | ||
221 | * callback to restore values saved by omap44xx_prm_save_and_clear_irqen(). | ||
222 | * No OCP barrier should be needed here; any pending PRM interrupts will fire | ||
223 | * once the writes reach the PRM. No return value. | ||
224 | */ | ||
225 | void omap44xx_prm_restore_irqen(u32 *saved_mask) | ||
226 | { | ||
227 | omap4_prm_write_inst_reg(saved_mask[0], OMAP4430_PRM_DEVICE_INST, | ||
228 | OMAP4_PRM_IRQENABLE_MPU_OFFSET); | ||
229 | omap4_prm_write_inst_reg(saved_mask[1], OMAP4430_PRM_DEVICE_INST, | ||
230 | OMAP4_PRM_IRQENABLE_MPU_2_OFFSET); | ||
231 | } | ||
232 | |||
233 | static int __init omap4xxx_prcm_init(void) | ||
234 | { | ||
235 | if (cpu_is_omap44xx()) | ||
236 | return omap_prcm_register_chain_handler(&omap4_prcm_irq_setup); | ||
237 | return 0; | ||
238 | } | ||
239 | subsys_initcall(omap4xxx_prcm_init); | ||
diff --git a/arch/arm/mach-omap2/prm44xx.h b/arch/arm/mach-omap2/prm44xx.h index 3d66ccd849d2..7978092946db 100644 --- a/arch/arm/mach-omap2/prm44xx.h +++ b/arch/arm/mach-omap2/prm44xx.h | |||
@@ -1,7 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * OMAP44xx PRM instance offset macros | 2 | * OMAP44xx PRM instance offset macros |
3 | * | 3 | * |
4 | * Copyright (C) 2009-2010 Texas Instruments, Inc. | 4 | * Copyright (C) 2009-2011 Texas Instruments, Inc. |
5 | * Copyright (C) 2009-2010 Nokia Corporation | 5 | * Copyright (C) 2009-2010 Nokia Corporation |
6 | * | 6 | * |
7 | * Paul Walmsley (paul@pwsan.com) | 7 | * Paul Walmsley (paul@pwsan.com) |
@@ -763,6 +763,12 @@ extern u32 omap4_prm_vcvp_read(u8 offset); | |||
763 | extern void omap4_prm_vcvp_write(u32 val, u8 offset); | 763 | extern void omap4_prm_vcvp_write(u32 val, u8 offset); |
764 | extern u32 omap4_prm_vcvp_rmw(u32 mask, u32 bits, u8 offset); | 764 | extern u32 omap4_prm_vcvp_rmw(u32 mask, u32 bits, u8 offset); |
765 | 765 | ||
766 | /* PRM interrupt-related functions */ | ||
767 | extern void omap44xx_prm_read_pending_irqs(unsigned long *events); | ||
768 | extern void omap44xx_prm_ocp_barrier(void); | ||
769 | extern void omap44xx_prm_save_and_clear_irqen(u32 *saved_mask); | ||
770 | extern void omap44xx_prm_restore_irqen(u32 *saved_mask); | ||
771 | |||
766 | # endif | 772 | # endif |
767 | 773 | ||
768 | #endif | 774 | #endif |
diff --git a/arch/arm/mach-omap2/prm_common.c b/arch/arm/mach-omap2/prm_common.c new file mode 100644 index 000000000000..860118ab43e2 --- /dev/null +++ b/arch/arm/mach-omap2/prm_common.c | |||
@@ -0,0 +1,320 @@ | |||
1 | /* | ||
2 | * OMAP2+ common Power & Reset Management (PRM) IP block functions | ||
3 | * | ||
4 | * Copyright (C) 2011 Texas Instruments, Inc. | ||
5 | * Tero Kristo <t-kristo@ti.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | * | ||
11 | * | ||
12 | * For historical purposes, the API used to configure the PRM | ||
13 | * interrupt handler refers to it as the "PRCM interrupt." The | ||
14 | * underlying registers are located in the PRM on OMAP3/4. | ||
15 | * | ||
16 | * XXX This code should eventually be moved to a PRM driver. | ||
17 | */ | ||
18 | |||
19 | #include <linux/kernel.h> | ||
20 | #include <linux/module.h> | ||
21 | #include <linux/init.h> | ||
22 | #include <linux/io.h> | ||
23 | #include <linux/irq.h> | ||
24 | #include <linux/interrupt.h> | ||
25 | #include <linux/slab.h> | ||
26 | |||
27 | #include <mach/system.h> | ||
28 | #include <plat/common.h> | ||
29 | #include <plat/prcm.h> | ||
30 | #include <plat/irqs.h> | ||
31 | |||
32 | #include "prm2xxx_3xxx.h" | ||
33 | #include "prm44xx.h" | ||
34 | |||
35 | /* | ||
36 | * OMAP_PRCM_MAX_NR_PENDING_REG: maximum number of PRM_IRQ*_MPU regs | ||
37 | * XXX this is technically not needed, since | ||
38 | * omap_prcm_register_chain_handler() could allocate this based on the | ||
39 | * actual amount of memory needed for the SoC | ||
40 | */ | ||
41 | #define OMAP_PRCM_MAX_NR_PENDING_REG 2 | ||
42 | |||
43 | /* | ||
44 | * prcm_irq_chips: an array of all of the "generic IRQ chips" in use | ||
45 | * by the PRCM interrupt handler code. There will be one 'chip' per | ||
46 | * PRM_{IRQSTATUS,IRQENABLE}_MPU register pair. (So OMAP3 will have | ||
47 | * one "chip" and OMAP4 will have two.) | ||
48 | */ | ||
49 | static struct irq_chip_generic **prcm_irq_chips; | ||
50 | |||
51 | /* | ||
52 | * prcm_irq_setup: the PRCM IRQ parameters for the hardware the code | ||
53 | * is currently running on. Defined and passed by initialization code | ||
54 | * that calls omap_prcm_register_chain_handler(). | ||
55 | */ | ||
56 | static struct omap_prcm_irq_setup *prcm_irq_setup; | ||
57 | |||
58 | /* Private functions */ | ||
59 | |||
60 | /* | ||
61 | * Move priority events from events to priority_events array | ||
62 | */ | ||
63 | static void omap_prcm_events_filter_priority(unsigned long *events, | ||
64 | unsigned long *priority_events) | ||
65 | { | ||
66 | int i; | ||
67 | |||
68 | for (i = 0; i < prcm_irq_setup->nr_regs; i++) { | ||
69 | priority_events[i] = | ||
70 | events[i] & prcm_irq_setup->priority_mask[i]; | ||
71 | events[i] ^= priority_events[i]; | ||
72 | } | ||
73 | } | ||
74 | |||
75 | /* | ||
76 | * PRCM Interrupt Handler | ||
77 | * | ||
78 | * This is a common handler for the OMAP PRCM interrupts. Pending | ||
79 | * interrupts are detected by a call to prcm_pending_events and | ||
80 | * dispatched accordingly. Clearing of the wakeup events should be | ||
81 | * done by the SoC specific individual handlers. | ||
82 | */ | ||
83 | static void omap_prcm_irq_handler(unsigned int irq, struct irq_desc *desc) | ||
84 | { | ||
85 | unsigned long pending[OMAP_PRCM_MAX_NR_PENDING_REG]; | ||
86 | unsigned long priority_pending[OMAP_PRCM_MAX_NR_PENDING_REG]; | ||
87 | struct irq_chip *chip = irq_desc_get_chip(desc); | ||
88 | unsigned int virtirq; | ||
89 | int nr_irqs = prcm_irq_setup->nr_regs * 32; | ||
90 | |||
91 | /* | ||
92 | * If we are suspended, mask all interrupts from PRCM level, | ||
93 | * this does not ack them, and they will be pending until we | ||
94 | * re-enable the interrupts, at which point the | ||
95 | * omap_prcm_irq_handler will be executed again. The | ||
96 | * _save_and_clear_irqen() function must ensure that the PRM | ||
97 | * write to disable all IRQs has reached the PRM before | ||
98 | * returning, or spurious PRCM interrupts may occur during | ||
99 | * suspend. | ||
100 | */ | ||
101 | if (prcm_irq_setup->suspended) { | ||
102 | prcm_irq_setup->save_and_clear_irqen(prcm_irq_setup->saved_mask); | ||
103 | prcm_irq_setup->suspend_save_flag = true; | ||
104 | } | ||
105 | |||
106 | /* | ||
107 | * Loop until all pending irqs are handled, since | ||
108 | * generic_handle_irq() can cause new irqs to come | ||
109 | */ | ||
110 | while (!prcm_irq_setup->suspended) { | ||
111 | prcm_irq_setup->read_pending_irqs(pending); | ||
112 | |||
113 | /* No bit set, then all IRQs are handled */ | ||
114 | if (find_first_bit(pending, nr_irqs) >= nr_irqs) | ||
115 | break; | ||
116 | |||
117 | omap_prcm_events_filter_priority(pending, priority_pending); | ||
118 | |||
119 | /* | ||
120 | * Loop on all currently pending irqs so that new irqs | ||
121 | * cannot starve previously pending irqs | ||
122 | */ | ||
123 | |||
124 | /* Serve priority events first */ | ||
125 | for_each_set_bit(virtirq, priority_pending, nr_irqs) | ||
126 | generic_handle_irq(prcm_irq_setup->base_irq + virtirq); | ||
127 | |||
128 | /* Serve normal events next */ | ||
129 | for_each_set_bit(virtirq, pending, nr_irqs) | ||
130 | generic_handle_irq(prcm_irq_setup->base_irq + virtirq); | ||
131 | } | ||
132 | if (chip->irq_ack) | ||
133 | chip->irq_ack(&desc->irq_data); | ||
134 | if (chip->irq_eoi) | ||
135 | chip->irq_eoi(&desc->irq_data); | ||
136 | chip->irq_unmask(&desc->irq_data); | ||
137 | |||
138 | prcm_irq_setup->ocp_barrier(); /* avoid spurious IRQs */ | ||
139 | } | ||
140 | |||
141 | /* Public functions */ | ||
142 | |||
143 | /** | ||
144 | * omap_prcm_event_to_irq - given a PRCM event name, returns the | ||
145 | * corresponding IRQ on which the handler should be registered | ||
146 | * @name: name of the PRCM interrupt bit to look up - see struct omap_prcm_irq | ||
147 | * | ||
148 | * Returns the Linux internal IRQ ID corresponding to @name upon success, | ||
149 | * or -ENOENT upon failure. | ||
150 | */ | ||
151 | int omap_prcm_event_to_irq(const char *name) | ||
152 | { | ||
153 | int i; | ||
154 | |||
155 | if (!prcm_irq_setup || !name) | ||
156 | return -ENOENT; | ||
157 | |||
158 | for (i = 0; i < prcm_irq_setup->nr_irqs; i++) | ||
159 | if (!strcmp(prcm_irq_setup->irqs[i].name, name)) | ||
160 | return prcm_irq_setup->base_irq + | ||
161 | prcm_irq_setup->irqs[i].offset; | ||
162 | |||
163 | return -ENOENT; | ||
164 | } | ||
165 | |||
166 | /** | ||
167 | * omap_prcm_irq_cleanup - reverses memory allocated and other steps | ||
168 | * done by omap_prcm_register_chain_handler() | ||
169 | * | ||
170 | * No return value. | ||
171 | */ | ||
172 | void omap_prcm_irq_cleanup(void) | ||
173 | { | ||
174 | int i; | ||
175 | |||
176 | if (!prcm_irq_setup) { | ||
177 | pr_err("PRCM: IRQ handler not initialized; cannot cleanup\n"); | ||
178 | return; | ||
179 | } | ||
180 | |||
181 | if (prcm_irq_chips) { | ||
182 | for (i = 0; i < prcm_irq_setup->nr_regs; i++) { | ||
183 | if (prcm_irq_chips[i]) | ||
184 | irq_remove_generic_chip(prcm_irq_chips[i], | ||
185 | 0xffffffff, 0, 0); | ||
186 | prcm_irq_chips[i] = NULL; | ||
187 | } | ||
188 | kfree(prcm_irq_chips); | ||
189 | prcm_irq_chips = NULL; | ||
190 | } | ||
191 | |||
192 | kfree(prcm_irq_setup->saved_mask); | ||
193 | prcm_irq_setup->saved_mask = NULL; | ||
194 | |||
195 | kfree(prcm_irq_setup->priority_mask); | ||
196 | prcm_irq_setup->priority_mask = NULL; | ||
197 | |||
198 | irq_set_chained_handler(prcm_irq_setup->irq, NULL); | ||
199 | |||
200 | if (prcm_irq_setup->base_irq > 0) | ||
201 | irq_free_descs(prcm_irq_setup->base_irq, | ||
202 | prcm_irq_setup->nr_regs * 32); | ||
203 | prcm_irq_setup->base_irq = 0; | ||
204 | } | ||
205 | |||
206 | void omap_prcm_irq_prepare(void) | ||
207 | { | ||
208 | prcm_irq_setup->suspended = true; | ||
209 | } | ||
210 | |||
211 | void omap_prcm_irq_complete(void) | ||
212 | { | ||
213 | prcm_irq_setup->suspended = false; | ||
214 | |||
215 | /* If we have not saved the masks, do not attempt to restore */ | ||
216 | if (!prcm_irq_setup->suspend_save_flag) | ||
217 | return; | ||
218 | |||
219 | prcm_irq_setup->suspend_save_flag = false; | ||
220 | |||
221 | /* | ||
222 | * Re-enable all masked PRCM irq sources, this causes the PRCM | ||
223 | * interrupt to fire immediately if the events were masked | ||
224 | * previously in the chain handler | ||
225 | */ | ||
226 | prcm_irq_setup->restore_irqen(prcm_irq_setup->saved_mask); | ||
227 | } | ||
228 | |||
229 | /** | ||
230 | * omap_prcm_register_chain_handler - initializes the prcm chained interrupt | ||
231 | * handler based on provided parameters | ||
232 | * @irq_setup: hardware data about the underlying PRM/PRCM | ||
233 | * | ||
234 | * Set up the PRCM chained interrupt handler on the PRCM IRQ. Sets up | ||
235 | * one generic IRQ chip per PRM interrupt status/enable register pair. | ||
236 | * Returns 0 upon success, -EINVAL if called twice or if invalid | ||
237 | * arguments are passed, or -ENOMEM on any other error. | ||
238 | */ | ||
239 | int omap_prcm_register_chain_handler(struct omap_prcm_irq_setup *irq_setup) | ||
240 | { | ||
241 | int nr_regs = irq_setup->nr_regs; | ||
242 | u32 mask[OMAP_PRCM_MAX_NR_PENDING_REG]; | ||
243 | int offset, i; | ||
244 | struct irq_chip_generic *gc; | ||
245 | struct irq_chip_type *ct; | ||
246 | |||
247 | if (!irq_setup) | ||
248 | return -EINVAL; | ||
249 | |||
250 | if (prcm_irq_setup) { | ||
251 | pr_err("PRCM: already initialized; won't reinitialize\n"); | ||
252 | return -EINVAL; | ||
253 | } | ||
254 | |||
255 | if (nr_regs > OMAP_PRCM_MAX_NR_PENDING_REG) { | ||
256 | pr_err("PRCM: nr_regs too large\n"); | ||
257 | return -EINVAL; | ||
258 | } | ||
259 | |||
260 | prcm_irq_setup = irq_setup; | ||
261 | |||
262 | prcm_irq_chips = kzalloc(sizeof(void *) * nr_regs, GFP_KERNEL); | ||
263 | prcm_irq_setup->saved_mask = kzalloc(sizeof(u32) * nr_regs, GFP_KERNEL); | ||
264 | prcm_irq_setup->priority_mask = kzalloc(sizeof(u32) * nr_regs, | ||
265 | GFP_KERNEL); | ||
266 | |||
267 | if (!prcm_irq_chips || !prcm_irq_setup->saved_mask || | ||
268 | !prcm_irq_setup->priority_mask) { | ||
269 | pr_err("PRCM: kzalloc failed\n"); | ||
270 | goto err; | ||
271 | } | ||
272 | |||
273 | memset(mask, 0, sizeof(mask)); | ||
274 | |||
275 | for (i = 0; i < irq_setup->nr_irqs; i++) { | ||
276 | offset = irq_setup->irqs[i].offset; | ||
277 | mask[offset >> 5] |= 1 << (offset & 0x1f); | ||
278 | if (irq_setup->irqs[i].priority) | ||
279 | irq_setup->priority_mask[offset >> 5] |= | ||
280 | 1 << (offset & 0x1f); | ||
281 | } | ||
282 | |||
283 | irq_set_chained_handler(irq_setup->irq, omap_prcm_irq_handler); | ||
284 | |||
285 | irq_setup->base_irq = irq_alloc_descs(-1, 0, irq_setup->nr_regs * 32, | ||
286 | 0); | ||
287 | |||
288 | if (irq_setup->base_irq < 0) { | ||
289 | pr_err("PRCM: failed to allocate irq descs: %d\n", | ||
290 | irq_setup->base_irq); | ||
291 | goto err; | ||
292 | } | ||
293 | |||
294 | for (i = 0; i <= irq_setup->nr_regs; i++) { | ||
295 | gc = irq_alloc_generic_chip("PRCM", 1, | ||
296 | irq_setup->base_irq + i * 32, prm_base, | ||
297 | handle_level_irq); | ||
298 | |||
299 | if (!gc) { | ||
300 | pr_err("PRCM: failed to allocate generic chip\n"); | ||
301 | goto err; | ||
302 | } | ||
303 | ct = gc->chip_types; | ||
304 | ct->chip.irq_ack = irq_gc_ack_set_bit; | ||
305 | ct->chip.irq_mask = irq_gc_mask_clr_bit; | ||
306 | ct->chip.irq_unmask = irq_gc_mask_set_bit; | ||
307 | |||
308 | ct->regs.ack = irq_setup->ack + i * 4; | ||
309 | ct->regs.mask = irq_setup->mask + i * 4; | ||
310 | |||
311 | irq_setup_generic_chip(gc, mask[i], 0, IRQ_NOREQUEST, 0); | ||
312 | prcm_irq_chips[i] = gc; | ||
313 | } | ||
314 | |||
315 | return 0; | ||
316 | |||
317 | err: | ||
318 | omap_prcm_irq_cleanup(); | ||
319 | return -ENOMEM; | ||
320 | } | ||
diff --git a/arch/arm/plat-omap/include/plat/omap_hwmod.h b/arch/arm/plat-omap/include/plat/omap_hwmod.h index 1a13c02118df..647010109afa 100644 --- a/arch/arm/plat-omap/include/plat/omap_hwmod.h +++ b/arch/arm/plat-omap/include/plat/omap_hwmod.h | |||
@@ -97,6 +97,7 @@ struct omap_hwmod_mux_info { | |||
97 | struct omap_device_pad *pads; | 97 | struct omap_device_pad *pads; |
98 | int nr_pads_dynamic; | 98 | int nr_pads_dynamic; |
99 | struct omap_device_pad **pads_dynamic; | 99 | struct omap_device_pad **pads_dynamic; |
100 | int *irqs; | ||
100 | bool enabled; | 101 | bool enabled; |
101 | }; | 102 | }; |
102 | 103 | ||
@@ -607,6 +608,8 @@ int omap_hwmod_get_context_loss_count(struct omap_hwmod *oh); | |||
607 | 608 | ||
608 | int omap_hwmod_no_setup_reset(struct omap_hwmod *oh); | 609 | int omap_hwmod_no_setup_reset(struct omap_hwmod *oh); |
609 | 610 | ||
611 | int omap_hwmod_pad_route_irq(struct omap_hwmod *oh, int pad_idx, int irq_idx); | ||
612 | |||
610 | /* | 613 | /* |
611 | * Chip variant-specific hwmod init routines - XXX should be converted | 614 | * Chip variant-specific hwmod init routines - XXX should be converted |
612 | * to use initcalls once the initial boot ordering is straightened out | 615 | * to use initcalls once the initial boot ordering is straightened out |