aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
Diffstat (limited to 'arch')
-rw-r--r--arch/arm/mach-omap2/pm-debug.c4
-rw-r--r--arch/arm/mach-omap2/pm34xx.c187
-rw-r--r--arch/arm/mach-omap2/powerdomain.c39
-rw-r--r--arch/arm/plat-omap/include/mach/powerdomain.h2
4 files changed, 137 insertions, 95 deletions
diff --git a/arch/arm/mach-omap2/pm-debug.c b/arch/arm/mach-omap2/pm-debug.c
index 1b4c1600f8d8..2fc4d6abbd0a 100644
--- a/arch/arm/mach-omap2/pm-debug.c
+++ b/arch/arm/mach-omap2/pm-debug.c
@@ -541,7 +541,7 @@ static int __init pm_dbg_init(void)
541 printk(KERN_ERR "%s: only OMAP3 supported\n", __func__); 541 printk(KERN_ERR "%s: only OMAP3 supported\n", __func__);
542 return -ENODEV; 542 return -ENODEV;
543 } 543 }
544 544
545 d = debugfs_create_dir("pm_debug", NULL); 545 d = debugfs_create_dir("pm_debug", NULL);
546 if (IS_ERR(d)) 546 if (IS_ERR(d))
547 return PTR_ERR(d); 547 return PTR_ERR(d);
@@ -551,7 +551,7 @@ static int __init pm_dbg_init(void)
551 (void) debugfs_create_file("time", S_IRUGO, 551 (void) debugfs_create_file("time", S_IRUGO,
552 d, (void *)DEBUG_FILE_TIMERS, &debug_fops); 552 d, (void *)DEBUG_FILE_TIMERS, &debug_fops);
553 553
554 pwrdm_for_each(pwrdms_setup, (void *)d); 554 pwrdm_for_each_nolock(pwrdms_setup, (void *)d);
555 555
556 pm_dbg_dir = debugfs_create_dir("registers", d); 556 pm_dbg_dir = debugfs_create_dir("registers", d);
557 if (IS_ERR(pm_dbg_dir)) 557 if (IS_ERR(pm_dbg_dir))
diff --git a/arch/arm/mach-omap2/pm34xx.c b/arch/arm/mach-omap2/pm34xx.c
index 0ff5a6c53aa0..378c2f618358 100644
--- a/arch/arm/mach-omap2/pm34xx.c
+++ b/arch/arm/mach-omap2/pm34xx.c
@@ -51,97 +51,112 @@ static void (*_omap_sram_idle)(u32 *addr, int save_state);
51 51
52static struct powerdomain *mpu_pwrdm; 52static struct powerdomain *mpu_pwrdm;
53 53
54/* PRCM Interrupt Handler for wakeups */ 54/*
55static irqreturn_t prcm_interrupt_handler (int irq, void *dev_id) 55 * PRCM Interrupt Handler Helper Function
56 *
57 * The purpose of this function is to clear any wake-up events latched
58 * in the PRCM PM_WKST_x registers. It is possible that a wake-up event
59 * may occur whilst attempting to clear a PM_WKST_x register and thus
60 * set another bit in this register. A while loop is used to ensure
61 * that any peripheral wake-up events occurring while attempting to
62 * clear the PM_WKST_x are detected and cleared.
63 */
64static int prcm_clear_mod_irqs(s16 module, u8 regs)
56{ 65{
57 u32 wkst, irqstatus_mpu; 66 u32 wkst, fclk, iclk, clken;
58 u32 fclk, iclk; 67 u16 wkst_off = (regs == 3) ? OMAP3430ES2_PM_WKST3 : PM_WKST1;
59 68 u16 fclk_off = (regs == 3) ? OMAP3430ES2_CM_FCLKEN3 : CM_FCLKEN1;
60 /* WKUP */ 69 u16 iclk_off = (regs == 3) ? CM_ICLKEN3 : CM_ICLKEN1;
61 wkst = prm_read_mod_reg(WKUP_MOD, PM_WKST); 70 u16 grpsel_off = (regs == 3) ?
71 OMAP3430ES2_PM_MPUGRPSEL3 : OMAP3430_PM_MPUGRPSEL;
72 int c = 0;
73
74 wkst = prm_read_mod_reg(module, wkst_off);
75 wkst &= prm_read_mod_reg(module, grpsel_off);
62 if (wkst) { 76 if (wkst) {
63 iclk = cm_read_mod_reg(WKUP_MOD, CM_ICLKEN); 77 iclk = cm_read_mod_reg(module, iclk_off);
64 fclk = cm_read_mod_reg(WKUP_MOD, CM_FCLKEN); 78 fclk = cm_read_mod_reg(module, fclk_off);
65 cm_set_mod_reg_bits(wkst, WKUP_MOD, CM_ICLKEN); 79 while (wkst) {
66 cm_set_mod_reg_bits(wkst, WKUP_MOD, CM_FCLKEN); 80 clken = wkst;
67 prm_write_mod_reg(wkst, WKUP_MOD, PM_WKST); 81 cm_set_mod_reg_bits(clken, module, iclk_off);
68 while (prm_read_mod_reg(WKUP_MOD, PM_WKST)) 82 /*
69 cpu_relax(); 83 * For USBHOST, we don't know whether HOST1 or
70 cm_write_mod_reg(iclk, WKUP_MOD, CM_ICLKEN); 84 * HOST2 woke us up, so enable both f-clocks
71 cm_write_mod_reg(fclk, WKUP_MOD, CM_FCLKEN); 85 */
86 if (module == OMAP3430ES2_USBHOST_MOD)
87 clken |= 1 << OMAP3430ES2_EN_USBHOST2_SHIFT;
88 cm_set_mod_reg_bits(clken, module, fclk_off);
89 prm_write_mod_reg(wkst, module, wkst_off);
90 wkst = prm_read_mod_reg(module, wkst_off);
91 c++;
92 }
93 cm_write_mod_reg(iclk, module, iclk_off);
94 cm_write_mod_reg(fclk, module, fclk_off);
72 } 95 }
73 96
74 /* CORE */ 97 return c;
75 wkst = prm_read_mod_reg(CORE_MOD, PM_WKST1); 98}
76 if (wkst) {
77 iclk = cm_read_mod_reg(CORE_MOD, CM_ICLKEN1);
78 fclk = cm_read_mod_reg(CORE_MOD, CM_FCLKEN1);
79 cm_set_mod_reg_bits(wkst, CORE_MOD, CM_ICLKEN1);
80 cm_set_mod_reg_bits(wkst, CORE_MOD, CM_FCLKEN1);
81 prm_write_mod_reg(wkst, CORE_MOD, PM_WKST1);
82 while (prm_read_mod_reg(CORE_MOD, PM_WKST1))
83 cpu_relax();
84 cm_write_mod_reg(iclk, CORE_MOD, CM_ICLKEN1);
85 cm_write_mod_reg(fclk, CORE_MOD, CM_FCLKEN1);
86 }
87 wkst = prm_read_mod_reg(CORE_MOD, OMAP3430ES2_PM_WKST3);
88 if (wkst) {
89 iclk = cm_read_mod_reg(CORE_MOD, CM_ICLKEN3);
90 fclk = cm_read_mod_reg(CORE_MOD, OMAP3430ES2_CM_FCLKEN3);
91 cm_set_mod_reg_bits(wkst, CORE_MOD, CM_ICLKEN3);
92 cm_set_mod_reg_bits(wkst, CORE_MOD, OMAP3430ES2_CM_FCLKEN3);
93 prm_write_mod_reg(wkst, CORE_MOD, OMAP3430ES2_PM_WKST3);
94 while (prm_read_mod_reg(CORE_MOD, OMAP3430ES2_PM_WKST3))
95 cpu_relax();
96 cm_write_mod_reg(iclk, CORE_MOD, CM_ICLKEN3);
97 cm_write_mod_reg(fclk, CORE_MOD, OMAP3430ES2_CM_FCLKEN3);
98 }
99 99
100 /* PER */ 100static int _prcm_int_handle_wakeup(void)
101 wkst = prm_read_mod_reg(OMAP3430_PER_MOD, PM_WKST); 101{
102 if (wkst) { 102 int c;
103 iclk = cm_read_mod_reg(OMAP3430_PER_MOD, CM_ICLKEN);
104 fclk = cm_read_mod_reg(OMAP3430_PER_MOD, CM_FCLKEN);
105 cm_set_mod_reg_bits(wkst, OMAP3430_PER_MOD, CM_ICLKEN);
106 cm_set_mod_reg_bits(wkst, OMAP3430_PER_MOD, CM_FCLKEN);
107 prm_write_mod_reg(wkst, OMAP3430_PER_MOD, PM_WKST);
108 while (prm_read_mod_reg(OMAP3430_PER_MOD, PM_WKST))
109 cpu_relax();
110 cm_write_mod_reg(iclk, OMAP3430_PER_MOD, CM_ICLKEN);
111 cm_write_mod_reg(fclk, OMAP3430_PER_MOD, CM_FCLKEN);
112 }
113 103
104 c = prcm_clear_mod_irqs(WKUP_MOD, 1);
105 c += prcm_clear_mod_irqs(CORE_MOD, 1);
106 c += prcm_clear_mod_irqs(OMAP3430_PER_MOD, 1);
114 if (omap_rev() > OMAP3430_REV_ES1_0) { 107 if (omap_rev() > OMAP3430_REV_ES1_0) {
115 /* USBHOST */ 108 c += prcm_clear_mod_irqs(CORE_MOD, 3);
116 wkst = prm_read_mod_reg(OMAP3430ES2_USBHOST_MOD, PM_WKST); 109 c += prcm_clear_mod_irqs(OMAP3430ES2_USBHOST_MOD, 1);
117 if (wkst) {
118 iclk = cm_read_mod_reg(OMAP3430ES2_USBHOST_MOD,
119 CM_ICLKEN);
120 fclk = cm_read_mod_reg(OMAP3430ES2_USBHOST_MOD,
121 CM_FCLKEN);
122 cm_set_mod_reg_bits(wkst, OMAP3430ES2_USBHOST_MOD,
123 CM_ICLKEN);
124 cm_set_mod_reg_bits(wkst, OMAP3430ES2_USBHOST_MOD,
125 CM_FCLKEN);
126 prm_write_mod_reg(wkst, OMAP3430ES2_USBHOST_MOD,
127 PM_WKST);
128 while (prm_read_mod_reg(OMAP3430ES2_USBHOST_MOD,
129 PM_WKST))
130 cpu_relax();
131 cm_write_mod_reg(iclk, OMAP3430ES2_USBHOST_MOD,
132 CM_ICLKEN);
133 cm_write_mod_reg(fclk, OMAP3430ES2_USBHOST_MOD,
134 CM_FCLKEN);
135 }
136 } 110 }
137 111
138 irqstatus_mpu = prm_read_mod_reg(OCP_MOD, 112 return c;
139 OMAP3_PRM_IRQSTATUS_MPU_OFFSET); 113}
140 prm_write_mod_reg(irqstatus_mpu, OCP_MOD, 114
141 OMAP3_PRM_IRQSTATUS_MPU_OFFSET); 115/*
116 * PRCM Interrupt Handler
117 *
118 * The PRM_IRQSTATUS_MPU register indicates if there are any pending
119 * interrupts from the PRCM for the MPU. These bits must be cleared in
120 * order to clear the PRCM interrupt. The PRCM interrupt handler is
121 * implemented to simply clear the PRM_IRQSTATUS_MPU in order to clear
122 * the PRCM interrupt. Please note that bit 0 of the PRM_IRQSTATUS_MPU
123 * register indicates that a wake-up event is pending for the MPU and
124 * this bit can only be cleared if the all the wake-up events latched
125 * in the various PM_WKST_x registers have been cleared. The interrupt
126 * handler is implemented using a do-while loop so that if a wake-up
127 * event occurred during the processing of the prcm interrupt handler
128 * (setting a bit in the corresponding PM_WKST_x register and thus
129 * preventing us from clearing bit 0 of the PRM_IRQSTATUS_MPU register)
130 * this would be handled.
131 */
132static irqreturn_t prcm_interrupt_handler (int irq, void *dev_id)
133{
134 u32 irqstatus_mpu;
135 int c = 0;
136
137 do {
138 irqstatus_mpu = prm_read_mod_reg(OCP_MOD,
139 OMAP3_PRM_IRQSTATUS_MPU_OFFSET);
140
141 if (irqstatus_mpu & (OMAP3430_WKUP_ST | OMAP3430_IO_ST)) {
142 c = _prcm_int_handle_wakeup();
143
144 /*
145 * Is the MPU PRCM interrupt handler racing with the
146 * IVA2 PRCM interrupt handler ?
147 */
148 WARN(c == 0, "prcm: WARNING: PRCM indicated MPU wakeup "
149 "but no wakeup sources are marked\n");
150 } else {
151 /* XXX we need to expand our PRCM interrupt handler */
152 WARN(1, "prcm: WARNING: PRCM interrupt received, but "
153 "no code to handle it (%08x)\n", irqstatus_mpu);
154 }
155
156 prm_write_mod_reg(irqstatus_mpu, OCP_MOD,
157 OMAP3_PRM_IRQSTATUS_MPU_OFFSET);
142 158
143 while (prm_read_mod_reg(OCP_MOD, OMAP3_PRM_IRQSTATUS_MPU_OFFSET)) 159 } while (prm_read_mod_reg(OCP_MOD, OMAP3_PRM_IRQSTATUS_MPU_OFFSET));
144 cpu_relax();
145 160
146 return IRQ_HANDLED; 161 return IRQ_HANDLED;
147} 162}
@@ -624,6 +639,16 @@ static void __init prcm_setup_regs(void)
624 prm_write_mod_reg(OMAP3430_IO_EN | OMAP3430_WKUP_EN, 639 prm_write_mod_reg(OMAP3430_IO_EN | OMAP3430_WKUP_EN,
625 OCP_MOD, OMAP3_PRM_IRQENABLE_MPU_OFFSET); 640 OCP_MOD, OMAP3_PRM_IRQENABLE_MPU_OFFSET);
626 641
642 /* Enable GPIO wakeups in PER */
643 prm_write_mod_reg(OMAP3430_EN_GPIO2 | OMAP3430_EN_GPIO3 |
644 OMAP3430_EN_GPIO4 | OMAP3430_EN_GPIO5 |
645 OMAP3430_EN_GPIO6, OMAP3430_PER_MOD, PM_WKEN);
646 /* and allow them to wake up MPU */
647 prm_write_mod_reg(OMAP3430_GRPSEL_GPIO2 | OMAP3430_EN_GPIO3 |
648 OMAP3430_GRPSEL_GPIO4 | OMAP3430_EN_GPIO5 |
649 OMAP3430_GRPSEL_GPIO6,
650 OMAP3430_PER_MOD, OMAP3430_PM_MPUGRPSEL);
651
627 /* Don't attach IVA interrupts */ 652 /* Don't attach IVA interrupts */
628 prm_write_mod_reg(0, WKUP_MOD, OMAP3430_PM_IVAGRPSEL); 653 prm_write_mod_reg(0, WKUP_MOD, OMAP3430_PM_IVAGRPSEL);
629 prm_write_mod_reg(0, CORE_MOD, OMAP3430_PM_IVAGRPSEL1); 654 prm_write_mod_reg(0, CORE_MOD, OMAP3430_PM_IVAGRPSEL1);
diff --git a/arch/arm/mach-omap2/powerdomain.c b/arch/arm/mach-omap2/powerdomain.c
index 2594cbff3947..f00289abd30f 100644
--- a/arch/arm/mach-omap2/powerdomain.c
+++ b/arch/arm/mach-omap2/powerdomain.c
@@ -273,35 +273,50 @@ struct powerdomain *pwrdm_lookup(const char *name)
273} 273}
274 274
275/** 275/**
276 * pwrdm_for_each - call function on each registered clockdomain 276 * pwrdm_for_each_nolock - call function on each registered clockdomain
277 * @fn: callback function * 277 * @fn: callback function *
278 * 278 *
279 * Call the supplied function for each registered powerdomain. The 279 * Call the supplied function for each registered powerdomain. The
280 * callback function can return anything but 0 to bail out early from 280 * callback function can return anything but 0 to bail out early from
281 * the iterator. The callback function is called with the pwrdm_rwlock 281 * the iterator. Returns the last return value of the callback function, which
282 * held for reading, so no powerdomain structure manipulation 282 * should be 0 for success or anything else to indicate failure; or -EINVAL if
283 * functions should be called from the callback, although hardware 283 * the function pointer is null.
284 * powerdomain control functions are fine. Returns the last return
285 * value of the callback function, which should be 0 for success or
286 * anything else to indicate failure; or -EINVAL if the function
287 * pointer is null.
288 */ 284 */
289int pwrdm_for_each(int (*fn)(struct powerdomain *pwrdm, void *user), 285int pwrdm_for_each_nolock(int (*fn)(struct powerdomain *pwrdm, void *user),
290 void *user) 286 void *user)
291{ 287{
292 struct powerdomain *temp_pwrdm; 288 struct powerdomain *temp_pwrdm;
293 unsigned long flags;
294 int ret = 0; 289 int ret = 0;
295 290
296 if (!fn) 291 if (!fn)
297 return -EINVAL; 292 return -EINVAL;
298 293
299 read_lock_irqsave(&pwrdm_rwlock, flags);
300 list_for_each_entry(temp_pwrdm, &pwrdm_list, node) { 294 list_for_each_entry(temp_pwrdm, &pwrdm_list, node) {
301 ret = (*fn)(temp_pwrdm, user); 295 ret = (*fn)(temp_pwrdm, user);
302 if (ret) 296 if (ret)
303 break; 297 break;
304 } 298 }
299
300 return ret;
301}
302
303/**
304 * pwrdm_for_each - call function on each registered clockdomain
305 * @fn: callback function *
306 *
307 * This function is the same as 'pwrdm_for_each_nolock()', but keeps the
308 * &pwrdm_rwlock locked for reading, so no powerdomain structure manipulation
309 * functions should be called from the callback, although hardware powerdomain
310 * control functions are fine.
311 */
312int pwrdm_for_each(int (*fn)(struct powerdomain *pwrdm, void *user),
313 void *user)
314{
315 unsigned long flags;
316 int ret;
317
318 read_lock_irqsave(&pwrdm_rwlock, flags);
319 ret = pwrdm_for_each_nolock(fn, user);
305 read_unlock_irqrestore(&pwrdm_rwlock, flags); 320 read_unlock_irqrestore(&pwrdm_rwlock, flags);
306 321
307 return ret; 322 return ret;
diff --git a/arch/arm/plat-omap/include/mach/powerdomain.h b/arch/arm/plat-omap/include/mach/powerdomain.h
index 6271d8556a40..fa6461423bd0 100644
--- a/arch/arm/plat-omap/include/mach/powerdomain.h
+++ b/arch/arm/plat-omap/include/mach/powerdomain.h
@@ -135,6 +135,8 @@ struct powerdomain *pwrdm_lookup(const char *name);
135 135
136int pwrdm_for_each(int (*fn)(struct powerdomain *pwrdm, void *user), 136int pwrdm_for_each(int (*fn)(struct powerdomain *pwrdm, void *user),
137 void *user); 137 void *user);
138int pwrdm_for_each_nolock(int (*fn)(struct powerdomain *pwrdm, void *user),
139 void *user);
138 140
139int pwrdm_add_clkdm(struct powerdomain *pwrdm, struct clockdomain *clkdm); 141int pwrdm_add_clkdm(struct powerdomain *pwrdm, struct clockdomain *clkdm);
140int pwrdm_del_clkdm(struct powerdomain *pwrdm, struct clockdomain *clkdm); 142int pwrdm_del_clkdm(struct powerdomain *pwrdm, struct clockdomain *clkdm);