summaryrefslogtreecommitdiff
path: root/arch/arm/mach-omap2
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-omap2')
-rw-r--r--arch/arm/mach-omap2/board-rx51-peripherals.c78
-rw-r--r--arch/arm/mach-omap2/clock34xx.c35
-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
5 files changed, 209 insertions, 134 deletions
diff --git a/arch/arm/mach-omap2/board-rx51-peripherals.c b/arch/arm/mach-omap2/board-rx51-peripherals.c
index b45ad312c587..c1af5326e92f 100644
--- a/arch/arm/mach-omap2/board-rx51-peripherals.c
+++ b/arch/arm/mach-omap2/board-rx51-peripherals.c
@@ -38,49 +38,49 @@
static int board_keymap[] = {
KEY(0, 0, KEY_Q),
- KEY(0, 1, KEY_W),
- KEY(0, 2, KEY_E),
- KEY(0, 3, KEY_R),
- KEY(0, 4, KEY_T),
- KEY(0, 5, KEY_Y),
- KEY(0, 6, KEY_U),
- KEY(0, 7, KEY_I),
- KEY(1, 0, KEY_O),
+ KEY(0, 1, KEY_O),
+ KEY(0, 2, KEY_P),
+ KEY(0, 3, KEY_COMMA),
+ KEY(0, 4, KEY_BACKSPACE),
+ KEY(0, 6, KEY_A),
+ KEY(0, 7, KEY_S),
+ KEY(1, 0, KEY_W),
KEY(1, 1, KEY_D),
- KEY(1, 2, KEY_DOT),
- KEY(1, 3, KEY_V),
- KEY(1, 4, KEY_DOWN),
- KEY(2, 0, KEY_P),
- KEY(2, 1, KEY_F),
+ KEY(1, 2, KEY_F),
+ KEY(1, 3, KEY_G),
+ KEY(1, 4, KEY_H),
+ KEY(1, 5, KEY_J),
+ KEY(1, 6, KEY_K),
+ KEY(1, 7, KEY_L),
+ KEY(2, 0, KEY_E),
+ KEY(2, 1, KEY_DOT),
KEY(2, 2, KEY_UP),
- KEY(2, 3, KEY_B),
- KEY(2, 4, KEY_RIGHT),
- KEY(3, 0, KEY_COMMA),
- KEY(3, 1, KEY_G),
- KEY(3, 2, KEY_ENTER),
+ KEY(2, 3, KEY_ENTER),
+ KEY(2, 5, KEY_Z),
+ KEY(2, 6, KEY_X),
+ KEY(2, 7, KEY_C),
+ KEY(3, 0, KEY_R),
+ KEY(3, 1, KEY_V),
+ KEY(3, 2, KEY_B),
KEY(3, 3, KEY_N),
- KEY(4, 0, KEY_BACKSPACE),
- KEY(4, 1, KEY_H),
- KEY(4, 3, KEY_M),
+ KEY(3, 4, KEY_M),
+ KEY(3, 5, KEY_SPACE),
+ KEY(3, 6, KEY_SPACE),
+ KEY(3, 7, KEY_LEFT),
+ KEY(4, 0, KEY_T),
+ KEY(4, 1, KEY_DOWN),
+ KEY(4, 2, KEY_RIGHT),
KEY(4, 4, KEY_LEFTCTRL),
- KEY(5, 1, KEY_J),
- KEY(5, 2, KEY_Z),
- KEY(5, 3, KEY_SPACE),
- KEY(5, 4, KEY_LEFTSHIFT),
- KEY(6, 0, KEY_A),
- KEY(6, 1, KEY_K),
- KEY(6, 2, KEY_X),
- KEY(6, 3, KEY_SPACE),
- KEY(6, 4, KEY_FN),
- KEY(7, 0, KEY_S),
- KEY(7, 1, KEY_L),
- KEY(7, 2, KEY_C),
- KEY(7, 3, KEY_LEFT),
- KEY(0xff, 0, KEY_F6),
- KEY(0xff, 1, KEY_F7),
- KEY(0xff, 2, KEY_F8),
- KEY(0xff, 4, KEY_F9),
- KEY(0xff, 5, KEY_F10),
+ KEY(4, 5, KEY_RIGHTALT),
+ KEY(4, 6, KEY_LEFTSHIFT),
+ KEY(5, 0, KEY_Y),
+ KEY(6, 0, KEY_U),
+ KEY(7, 0, KEY_I),
+ KEY(7, 1, KEY_F7),
+ KEY(7, 2, KEY_F8),
+ KEY(0xff, 2, KEY_F9),
+ KEY(0xff, 4, KEY_F10),
+ KEY(0xff, 5, KEY_F11),
};
static struct matrix_keymap_data board_map_data = {
diff --git a/arch/arm/mach-omap2/clock34xx.c b/arch/arm/mach-omap2/clock34xx.c
index fafcd32e6907..489556eecbd1 100644
--- a/arch/arm/mach-omap2/clock34xx.c
+++ b/arch/arm/mach-omap2/clock34xx.c
@@ -338,6 +338,13 @@ static struct omap_clk omap34xx_clks[] = {
*/
#define SDRC_MPURATE_LOOPS 96
+/*
+ * DPLL5_FREQ_FOR_USBHOST: USBHOST and USBTLL are the only clocks
+ * that are sourced by DPLL5, and both of these require this clock
+ * to be at 120 MHz for proper operation.
+ */
+#define DPLL5_FREQ_FOR_USBHOST 120000000
+
/**
* omap3430es2_clk_ssi_find_idlest - return CM_IDLEST info for SSI
* @clk: struct clk * being enabled
@@ -1056,6 +1063,28 @@ void omap2_clk_prepare_for_reboot(void)
#endif
}
+static void omap3_clk_lock_dpll5(void)
+{
+ struct clk *dpll5_clk;
+ struct clk *dpll5_m2_clk;
+
+ dpll5_clk = clk_get(NULL, "dpll5_ck");
+ clk_set_rate(dpll5_clk, DPLL5_FREQ_FOR_USBHOST);
+ clk_enable(dpll5_clk);
+
+ /* Enable autoidle to allow it to enter low power bypass */
+ omap3_dpll_allow_idle(dpll5_clk);
+
+ /* Program dpll5_m2_clk divider for no division */
+ dpll5_m2_clk = clk_get(NULL, "dpll5_m2_ck");
+ clk_enable(dpll5_m2_clk);
+ clk_set_rate(dpll5_m2_clk, DPLL5_FREQ_FOR_USBHOST);
+
+ clk_disable(dpll5_m2_clk);
+ clk_disable(dpll5_clk);
+ return;
+}
+
/* REVISIT: Move this init stuff out into clock.c */
/*
@@ -1148,6 +1177,12 @@ int __init omap2_clk_init(void)
*/
clk_enable_init_clocks();
+ /*
+ * Lock DPLL5 and put it in autoidle.
+ */
+ if (omap_rev() >= OMAP3430_REV_ES2_0)
+ omap3_clk_lock_dpll5();
+
/* Avoid sleeping during omap2_clk_prepare_for_reboot() */
/* REVISIT: not yet ready for 343x */
#if 0
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)
printk(KERN_ERR "%s: only OMAP3 supported\n", __func__);
return -ENODEV;
}
-
+
d = debugfs_create_dir("pm_debug", NULL);
if (IS_ERR(d))
return PTR_ERR(d);
@@ -551,7 +551,7 @@ static int __init pm_dbg_init(void)
(void) debugfs_create_file("time", S_IRUGO,
d, (void *)DEBUG_FILE_TIMERS, &debug_fops);
- pwrdm_for_each(pwrdms_setup, (void *)d);
+ pwrdm_for_each_nolock(pwrdms_setup, (void *)d);
pm_dbg_dir = debugfs_create_dir("registers", d);
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);
static struct powerdomain *mpu_pwrdm;
-/* PRCM Interrupt Handler for wakeups */
-static irqreturn_t prcm_interrupt_handler (int irq, void *dev_id)
+/*
+ * PRCM Interrupt Handler Helper Function
+ *
+ * The purpose of this function is to clear any wake-up events latched
+ * in the PRCM PM_WKST_x registers. It is possible that a wake-up event
+ * may occur whilst attempting to clear a PM_WKST_x register and thus
+ * set another bit in this register. A while loop is used to ensure
+ * that any peripheral wake-up events occurring while attempting to
+ * clear the PM_WKST_x are detected and cleared.
+ */
+static int prcm_clear_mod_irqs(s16 module, u8 regs)
{
- u32 wkst, irqstatus_mpu;
- u32 fclk, iclk;
-
- /* WKUP */
- wkst = prm_read_mod_reg(WKUP_MOD, PM_WKST);
+ u32 wkst, fclk, iclk, clken;
+ u16 wkst_off = (regs == 3) ? OMAP3430ES2_PM_WKST3 : PM_WKST1;
+ u16 fclk_off = (regs == 3) ? OMAP3430ES2_CM_FCLKEN3 : CM_FCLKEN1;
+ u16 iclk_off = (regs == 3) ? CM_ICLKEN3 : CM_ICLKEN1;
+ u16 grpsel_off = (regs == 3) ?
+ OMAP3430ES2_PM_MPUGRPSEL3 : OMAP3430_PM_MPUGRPSEL;
+ int c = 0;
+
+ wkst = prm_read_mod_reg(module, wkst_off);
+ wkst &= prm_read_mod_reg(module, grpsel_off);
if (wkst) {
- iclk = cm_read_mod_reg(WKUP_MOD, CM_ICLKEN);
- fclk = cm_read_mod_reg(WKUP_MOD, CM_FCLKEN);
- cm_set_mod_reg_bits(wkst, WKUP_MOD, CM_ICLKEN);
- cm_set_mod_reg_bits(wkst, WKUP_MOD, CM_FCLKEN);
- prm_write_mod_reg(wkst, WKUP_MOD, PM_WKST);
- while (prm_read_mod_reg(WKUP_MOD, PM_WKST))
- cpu_relax();
- cm_write_mod_reg(iclk, WKUP_MOD, CM_ICLKEN);
- cm_write_mod_reg(fclk, WKUP_MOD, CM_FCLKEN);
+ iclk = cm_read_mod_reg(module, iclk_off);
+ fclk = cm_read_mod_reg(module, fclk_off);
+ while (wkst) {
+ clken = wkst;
+ cm_set_mod_reg_bits(clken, module, iclk_off);
+ /*
+ * For USBHOST, we don't know whether HOST1 or
+ * HOST2 woke us up, so enable both f-clocks
+ */
+ if (module == OMAP3430ES2_USBHOST_MOD)
+ clken |= 1 << OMAP3430ES2_EN_USBHOST2_SHIFT;
+ cm_set_mod_reg_bits(clken, module, fclk_off);
+ prm_write_mod_reg(wkst, module, wkst_off);
+ wkst = prm_read_mod_reg(module, wkst_off);
+ c++;
+ }
+ cm_write_mod_reg(iclk, module, iclk_off);
+ cm_write_mod_reg(fclk, module, fclk_off);
}
- /* CORE */
- wkst = prm_read_mod_reg(CORE_MOD, PM_WKST1);
- if (wkst) {
- iclk = cm_read_mod_reg(CORE_MOD, CM_ICLKEN1);
- fclk = cm_read_mod_reg(CORE_MOD, CM_FCLKEN1);
- cm_set_mod_reg_bits(wkst, CORE_MOD, CM_ICLKEN1);
- cm_set_mod_reg_bits(wkst, CORE_MOD, CM_FCLKEN1);
- prm_write_mod_reg(wkst, CORE_MOD, PM_WKST1);
- while (prm_read_mod_reg(CORE_MOD, PM_WKST1))
- cpu_relax();
- cm_write_mod_reg(iclk, CORE_MOD, CM_ICLKEN1);
- cm_write_mod_reg(fclk, CORE_MOD, CM_FCLKEN1);
- }
- wkst = prm_read_mod_reg(CORE_MOD, OMAP3430ES2_PM_WKST3);
- if (wkst) {
- iclk = cm_read_mod_reg(CORE_MOD, CM_ICLKEN3);
- fclk = cm_read_mod_reg(CORE_MOD, OMAP3430ES2_CM_FCLKEN3);
- cm_set_mod_reg_bits(wkst, CORE_MOD, CM_ICLKEN3);
- cm_set_mod_reg_bits(wkst, CORE_MOD, OMAP3430ES2_CM_FCLKEN3);
- prm_write_mod_reg(wkst, CORE_MOD, OMAP3430ES2_PM_WKST3);
- while (prm_read_mod_reg(CORE_MOD, OMAP3430ES2_PM_WKST3))
- cpu_relax();
- cm_write_mod_reg(iclk, CORE_MOD, CM_ICLKEN3);
- cm_write_mod_reg(fclk, CORE_MOD, OMAP3430ES2_CM_FCLKEN3);
- }
+ return c;
+}
- /* PER */
- wkst = prm_read_mod_reg(OMAP3430_PER_MOD, PM_WKST);
- if (wkst) {
- iclk = cm_read_mod_reg(OMAP3430_PER_MOD, CM_ICLKEN);
- fclk = cm_read_mod_reg(OMAP3430_PER_MOD, CM_FCLKEN);
- cm_set_mod_reg_bits(wkst, OMAP3430_PER_MOD, CM_ICLKEN);
- cm_set_mod_reg_bits(wkst, OMAP3430_PER_MOD, CM_FCLKEN);
- prm_write_mod_reg(wkst, OMAP3430_PER_MOD, PM_WKST);
- while (prm_read_mod_reg(OMAP3430_PER_MOD, PM_WKST))
- cpu_relax();
- cm_write_mod_reg(iclk, OMAP3430_PER_MOD, CM_ICLKEN);
- cm_write_mod_reg(fclk, OMAP3430_PER_MOD, CM_FCLKEN);
- }
+static int _prcm_int_handle_wakeup(void)
+{
+ int c;
+ c = prcm_clear_mod_irqs(WKUP_MOD, 1);
+ c += prcm_clear_mod_irqs(CORE_MOD, 1);
+ c += prcm_clear_mod_irqs(OMAP3430_PER_MOD, 1);
if (omap_rev() > OMAP3430_REV_ES1_0) {
- /* USBHOST */
- wkst = prm_read_mod_reg(OMAP3430ES2_USBHOST_MOD, PM_WKST);
- if (wkst) {
- iclk = cm_read_mod_reg(OMAP3430ES2_USBHOST_MOD,
- CM_ICLKEN);
- fclk = cm_read_mod_reg(OMAP3430ES2_USBHOST_MOD,
- CM_FCLKEN);
- cm_set_mod_reg_bits(wkst, OMAP3430ES2_USBHOST_MOD,
- CM_ICLKEN);
- cm_set_mod_reg_bits(wkst, OMAP3430ES2_USBHOST_MOD,
- CM_FCLKEN);
- prm_write_mod_reg(wkst, OMAP3430ES2_USBHOST_MOD,
- PM_WKST);
- while (prm_read_mod_reg(OMAP3430ES2_USBHOST_MOD,
- PM_WKST))
- cpu_relax();
- cm_write_mod_reg(iclk, OMAP3430ES2_USBHOST_MOD,
- CM_ICLKEN);
- cm_write_mod_reg(fclk, OMAP3430ES2_USBHOST_MOD,
- CM_FCLKEN);
- }
+ c += prcm_clear_mod_irqs(CORE_MOD, 3);
+ c += prcm_clear_mod_irqs(OMAP3430ES2_USBHOST_MOD, 1);
}
- irqstatus_mpu = prm_read_mod_reg(OCP_MOD,
- OMAP3_PRM_IRQSTATUS_MPU_OFFSET);
- prm_write_mod_reg(irqstatus_mpu, OCP_MOD,
- OMAP3_PRM_IRQSTATUS_MPU_OFFSET);
+ return c;
+}
+
+/*
+ * PRCM Interrupt Handler
+ *
+ * The PRM_IRQSTATUS_MPU register indicates if there are any pending
+ * interrupts from the PRCM for the MPU. These bits must be cleared in
+ * order to clear the PRCM interrupt. The PRCM interrupt handler is
+ * implemented to simply clear the PRM_IRQSTATUS_MPU in order to clear
+ * the PRCM interrupt. Please note that bit 0 of the PRM_IRQSTATUS_MPU
+ * register indicates that a wake-up event is pending for the MPU and
+ * this bit can only be cleared if the all the wake-up events latched
+ * in the various PM_WKST_x registers have been cleared. The interrupt
+ * handler is implemented using a do-while loop so that if a wake-up
+ * event occurred during the processing of the prcm interrupt handler
+ * (setting a bit in the corresponding PM_WKST_x register and thus
+ * preventing us from clearing bit 0 of the PRM_IRQSTATUS_MPU register)
+ * this would be handled.
+ */
+static irqreturn_t prcm_interrupt_handler (int irq, void *dev_id)
+{
+ u32 irqstatus_mpu;
+ int c = 0;
+
+ do {
+ irqstatus_mpu = prm_read_mod_reg(OCP_MOD,
+ OMAP3_PRM_IRQSTATUS_MPU_OFFSET);
+
+ if (irqstatus_mpu & (OMAP3430_WKUP_ST | OMAP3430_IO_ST)) {
+ c = _prcm_int_handle_wakeup();
+
+ /*
+ * Is the MPU PRCM interrupt handler racing with the
+ * IVA2 PRCM interrupt handler ?
+ */
+ WARN(c == 0, "prcm: WARNING: PRCM indicated MPU wakeup "
+ "but no wakeup sources are marked\n");
+ } else {
+ /* XXX we need to expand our PRCM interrupt handler */
+ WARN(1, "prcm: WARNING: PRCM interrupt received, but "
+ "no code to handle it (%08x)\n", irqstatus_mpu);
+ }
+
+ prm_write_mod_reg(irqstatus_mpu, OCP_MOD,
+ OMAP3_PRM_IRQSTATUS_MPU_OFFSET);
- while (prm_read_mod_reg(OCP_MOD, OMAP3_PRM_IRQSTATUS_MPU_OFFSET))
- cpu_relax();
+ } while (prm_read_mod_reg(OCP_MOD, OMAP3_PRM_IRQSTATUS_MPU_OFFSET));
return IRQ_HANDLED;
}
@@ -624,6 +639,16 @@ static void __init prcm_setup_regs(void)
prm_write_mod_reg(OMAP3430_IO_EN | OMAP3430_WKUP_EN,
OCP_MOD, OMAP3_PRM_IRQENABLE_MPU_OFFSET);
+ /* Enable GPIO wakeups in PER */
+ prm_write_mod_reg(OMAP3430_EN_GPIO2 | OMAP3430_EN_GPIO3 |
+ OMAP3430_EN_GPIO4 | OMAP3430_EN_GPIO5 |
+ OMAP3430_EN_GPIO6, OMAP3430_PER_MOD, PM_WKEN);
+ /* and allow them to wake up MPU */
+ prm_write_mod_reg(OMAP3430_GRPSEL_GPIO2 | OMAP3430_EN_GPIO3 |
+ OMAP3430_GRPSEL_GPIO4 | OMAP3430_EN_GPIO5 |
+ OMAP3430_GRPSEL_GPIO6,
+ OMAP3430_PER_MOD, OMAP3430_PM_MPUGRPSEL);
+
/* Don't attach IVA interrupts */
prm_write_mod_reg(0, WKUP_MOD, OMAP3430_PM_IVAGRPSEL);
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)
}
/**
- * pwrdm_for_each - call function on each registered clockdomain
+ * pwrdm_for_each_nolock - call function on each registered clockdomain
* @fn: callback function *
*
* Call the supplied function for each registered powerdomain. The
* callback function can return anything but 0 to bail out early from
- * the iterator. The callback function is called with the pwrdm_rwlock
- * held for reading, so no powerdomain structure manipulation
- * functions should be called from the callback, although hardware
- * powerdomain control functions are fine. Returns the last return
- * value of the callback function, which should be 0 for success or
- * anything else to indicate failure; or -EINVAL if the function
- * pointer is null.
+ * the iterator. Returns the last return value of the callback function, which
+ * should be 0 for success or anything else to indicate failure; or -EINVAL if
+ * the function pointer is null.
*/
-int pwrdm_for_each(int (*fn)(struct powerdomain *pwrdm, void *user),
- void *user)
+int pwrdm_for_each_nolock(int (*fn)(struct powerdomain *pwrdm, void *user),
+ void *user)
{
struct powerdomain *temp_pwrdm;
- unsigned long flags;
int ret = 0;
if (!fn)
return -EINVAL;
- read_lock_irqsave(&pwrdm_rwlock, flags);
list_for_each_entry(temp_pwrdm, &pwrdm_list, node) {
ret = (*fn)(temp_pwrdm, user);
if (ret)
break;
}
+
+ return ret;
+}
+
+/**
+ * pwrdm_for_each - call function on each registered clockdomain
+ * @fn: callback function *
+ *
+ * This function is the same as 'pwrdm_for_each_nolock()', but keeps the
+ * &pwrdm_rwlock locked for reading, so no powerdomain structure manipulation
+ * functions should be called from the callback, although hardware powerdomain
+ * control functions are fine.
+ */
+int pwrdm_for_each(int (*fn)(struct powerdomain *pwrdm, void *user),
+ void *user)
+{
+ unsigned long flags;
+ int ret;
+
+ read_lock_irqsave(&pwrdm_rwlock, flags);
+ ret = pwrdm_for_each_nolock(fn, user);
read_unlock_irqrestore(&pwrdm_rwlock, flags);
return ret;