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 | 125 | ||||
| -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 | 6 |
11 files changed, 884 insertions, 79 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 529142aff766..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 | * |
| @@ -1441,6 +1487,25 @@ static int _enable(struct omap_hwmod *oh) | |||
| 1441 | 1487 | ||
| 1442 | pr_debug("omap_hwmod: %s: enabling\n", oh->name); | 1488 | pr_debug("omap_hwmod: %s: enabling\n", oh->name); |
| 1443 | 1489 | ||
| 1490 | /* | ||
| 1491 | * hwmods with HWMOD_INIT_NO_IDLE flag set are left | ||
| 1492 | * in enabled state at init. | ||
| 1493 | * Now that someone is really trying to enable them, | ||
| 1494 | * just ensure that the hwmod mux is set. | ||
| 1495 | */ | ||
| 1496 | if (oh->_int_flags & _HWMOD_SKIP_ENABLE) { | ||
| 1497 | /* | ||
| 1498 | * If the caller has mux data populated, do the mux'ing | ||
| 1499 | * which wouldn't have been done as part of the _enable() | ||
| 1500 | * done during setup. | ||
| 1501 | */ | ||
| 1502 | if (oh->mux) | ||
| 1503 | omap_hwmod_mux(oh->mux, _HWMOD_STATE_ENABLED); | ||
| 1504 | |||
| 1505 | oh->_int_flags &= ~_HWMOD_SKIP_ENABLE; | ||
| 1506 | return 0; | ||
| 1507 | } | ||
| 1508 | |||
| 1444 | if (oh->_state != _HWMOD_STATE_INITIALIZED && | 1509 | if (oh->_state != _HWMOD_STATE_INITIALIZED && |
| 1445 | oh->_state != _HWMOD_STATE_IDLE && | 1510 | oh->_state != _HWMOD_STATE_IDLE && |
| 1446 | oh->_state != _HWMOD_STATE_DISABLED) { | 1511 | oh->_state != _HWMOD_STATE_DISABLED) { |
| @@ -1744,8 +1809,10 @@ static int _setup(struct omap_hwmod *oh, void *data) | |||
| 1744 | * it should be set by the core code as a runtime flag during startup | 1809 | * it should be set by the core code as a runtime flag during startup |
| 1745 | */ | 1810 | */ |
| 1746 | if ((oh->flags & HWMOD_INIT_NO_IDLE) && | 1811 | if ((oh->flags & HWMOD_INIT_NO_IDLE) && |
| 1747 | (postsetup_state == _HWMOD_STATE_IDLE)) | 1812 | (postsetup_state == _HWMOD_STATE_IDLE)) { |
| 1813 | oh->_int_flags |= _HWMOD_SKIP_ENABLE; | ||
| 1748 | postsetup_state = _HWMOD_STATE_ENABLED; | 1814 | postsetup_state = _HWMOD_STATE_ENABLED; |
| 1815 | } | ||
| 1749 | 1816 | ||
| 1750 | if (postsetup_state == _HWMOD_STATE_IDLE) | 1817 | if (postsetup_state == _HWMOD_STATE_IDLE) |
| 1751 | _idle(oh); | 1818 | _idle(oh); |
| @@ -2416,6 +2483,7 @@ int omap_hwmod_enable_wakeup(struct omap_hwmod *oh) | |||
| 2416 | v = oh->_sysc_cache; | 2483 | v = oh->_sysc_cache; |
| 2417 | _enable_wakeup(oh, &v); | 2484 | _enable_wakeup(oh, &v); |
| 2418 | _write_sysconfig(v, oh); | 2485 | _write_sysconfig(v, oh); |
| 2486 | _set_idle_ioring_wakeup(oh, true); | ||
| 2419 | spin_unlock_irqrestore(&oh->_lock, flags); | 2487 | spin_unlock_irqrestore(&oh->_lock, flags); |
| 2420 | 2488 | ||
| 2421 | return 0; | 2489 | return 0; |
| @@ -2446,6 +2514,7 @@ int omap_hwmod_disable_wakeup(struct omap_hwmod *oh) | |||
| 2446 | v = oh->_sysc_cache; | 2514 | v = oh->_sysc_cache; |
| 2447 | _disable_wakeup(oh, &v); | 2515 | _disable_wakeup(oh, &v); |
| 2448 | _write_sysconfig(v, oh); | 2516 | _write_sysconfig(v, oh); |
| 2517 | _set_idle_ioring_wakeup(oh, false); | ||
| 2449 | spin_unlock_irqrestore(&oh->_lock, flags); | 2518 | spin_unlock_irqrestore(&oh->_lock, flags); |
| 2450 | 2519 | ||
| 2451 | return 0; | 2520 | return 0; |
| @@ -2662,3 +2731,57 @@ int omap_hwmod_no_setup_reset(struct omap_hwmod *oh) | |||
| 2662 | 2731 | ||
| 2663 | return 0; | 2732 | return 0; |
| 2664 | } | 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 8b372ede17c1..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 | ||
| @@ -416,10 +417,13 @@ struct omap_hwmod_omap4_prcm { | |||
| 416 | * _HWMOD_NO_MPU_PORT: no path exists for the MPU to write to this module | 417 | * _HWMOD_NO_MPU_PORT: no path exists for the MPU to write to this module |
| 417 | * _HWMOD_WAKEUP_ENABLED: set when the omap_hwmod code has enabled ENAWAKEUP | 418 | * _HWMOD_WAKEUP_ENABLED: set when the omap_hwmod code has enabled ENAWAKEUP |
| 418 | * _HWMOD_SYSCONFIG_LOADED: set when the OCP_SYSCONFIG value has been cached | 419 | * _HWMOD_SYSCONFIG_LOADED: set when the OCP_SYSCONFIG value has been cached |
| 420 | * _HWMOD_SKIP_ENABLE: set if hwmod enabled during init (HWMOD_INIT_NO_IDLE) - | ||
| 421 | * causes the first call to _enable() to only update the pinmux | ||
| 419 | */ | 422 | */ |
| 420 | #define _HWMOD_NO_MPU_PORT (1 << 0) | 423 | #define _HWMOD_NO_MPU_PORT (1 << 0) |
| 421 | #define _HWMOD_WAKEUP_ENABLED (1 << 1) | 424 | #define _HWMOD_WAKEUP_ENABLED (1 << 1) |
| 422 | #define _HWMOD_SYSCONFIG_LOADED (1 << 2) | 425 | #define _HWMOD_SYSCONFIG_LOADED (1 << 2) |
| 426 | #define _HWMOD_SKIP_ENABLE (1 << 3) | ||
| 423 | 427 | ||
| 424 | /* | 428 | /* |
| 425 | * omap_hwmod._state definitions | 429 | * omap_hwmod._state definitions |
| @@ -604,6 +608,8 @@ int omap_hwmod_get_context_loss_count(struct omap_hwmod *oh); | |||
| 604 | 608 | ||
| 605 | int omap_hwmod_no_setup_reset(struct omap_hwmod *oh); | 609 | int omap_hwmod_no_setup_reset(struct omap_hwmod *oh); |
| 606 | 610 | ||
| 611 | int omap_hwmod_pad_route_irq(struct omap_hwmod *oh, int pad_idx, int irq_idx); | ||
| 612 | |||
| 607 | /* | 613 | /* |
| 608 | * Chip variant-specific hwmod init routines - XXX should be converted | 614 | * Chip variant-specific hwmod init routines - XXX should be converted |
| 609 | * to use initcalls once the initial boot ordering is straightened out | 615 | * to use initcalls once the initial boot ordering is straightened out |
