Commit ab8d64d7 authored by Tony Lindgren's avatar Tony Lindgren

Merge branch 'pm-fixes-32' of...

Merge branch 'pm-fixes-32' of git://git.kernel.org/pub/scm/linux/kernel/git/khilman/linux-omap-pm into omap-fixes-for-linus
parents 82e86501 ee894b18
...@@ -541,7 +541,7 @@ static int __init pm_dbg_init(void) ...@@ -541,7 +541,7 @@ static int __init pm_dbg_init(void)
printk(KERN_ERR "%s: only OMAP3 supported\n", __func__); printk(KERN_ERR "%s: only OMAP3 supported\n", __func__);
return -ENODEV; return -ENODEV;
} }
d = debugfs_create_dir("pm_debug", NULL); d = debugfs_create_dir("pm_debug", NULL);
if (IS_ERR(d)) if (IS_ERR(d))
return PTR_ERR(d); return PTR_ERR(d);
...@@ -551,7 +551,7 @@ static int __init pm_dbg_init(void) ...@@ -551,7 +551,7 @@ static int __init pm_dbg_init(void)
(void) debugfs_create_file("time", S_IRUGO, (void) debugfs_create_file("time", S_IRUGO,
d, (void *)DEBUG_FILE_TIMERS, &debug_fops); 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); pm_dbg_dir = debugfs_create_dir("registers", d);
if (IS_ERR(pm_dbg_dir)) if (IS_ERR(pm_dbg_dir))
......
...@@ -51,97 +51,112 @@ static void (*_omap_sram_idle)(u32 *addr, int save_state); ...@@ -51,97 +51,112 @@ static void (*_omap_sram_idle)(u32 *addr, int save_state);
static struct powerdomain *mpu_pwrdm; 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 wkst, fclk, iclk, clken;
u32 fclk, iclk; u16 wkst_off = (regs == 3) ? OMAP3430ES2_PM_WKST3 : PM_WKST1;
u16 fclk_off = (regs == 3) ? OMAP3430ES2_CM_FCLKEN3 : CM_FCLKEN1;
/* WKUP */ u16 iclk_off = (regs == 3) ? CM_ICLKEN3 : CM_ICLKEN1;
wkst = prm_read_mod_reg(WKUP_MOD, PM_WKST); 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) { if (wkst) {
iclk = cm_read_mod_reg(WKUP_MOD, CM_ICLKEN); iclk = cm_read_mod_reg(module, iclk_off);
fclk = cm_read_mod_reg(WKUP_MOD, CM_FCLKEN); fclk = cm_read_mod_reg(module, fclk_off);
cm_set_mod_reg_bits(wkst, WKUP_MOD, CM_ICLKEN); while (wkst) {
cm_set_mod_reg_bits(wkst, WKUP_MOD, CM_FCLKEN); clken = wkst;
prm_write_mod_reg(wkst, WKUP_MOD, PM_WKST); cm_set_mod_reg_bits(clken, module, iclk_off);
while (prm_read_mod_reg(WKUP_MOD, PM_WKST)) /*
cpu_relax(); * For USBHOST, we don't know whether HOST1 or
cm_write_mod_reg(iclk, WKUP_MOD, CM_ICLKEN); * HOST2 woke us up, so enable both f-clocks
cm_write_mod_reg(fclk, WKUP_MOD, CM_FCLKEN); */
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 */ return c;
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);
}
/* PER */ static int _prcm_int_handle_wakeup(void)
wkst = prm_read_mod_reg(OMAP3430_PER_MOD, PM_WKST); {
if (wkst) { int c;
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);
}
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) { if (omap_rev() > OMAP3430_REV_ES1_0) {
/* USBHOST */ c += prcm_clear_mod_irqs(CORE_MOD, 3);
wkst = prm_read_mod_reg(OMAP3430ES2_USBHOST_MOD, PM_WKST); c += prcm_clear_mod_irqs(OMAP3430ES2_USBHOST_MOD, 1);
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);
}
} }
irqstatus_mpu = prm_read_mod_reg(OCP_MOD, return c;
OMAP3_PRM_IRQSTATUS_MPU_OFFSET); }
prm_write_mod_reg(irqstatus_mpu, OCP_MOD,
OMAP3_PRM_IRQSTATUS_MPU_OFFSET); /*
* 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)) } while (prm_read_mod_reg(OCP_MOD, OMAP3_PRM_IRQSTATUS_MPU_OFFSET));
cpu_relax();
return IRQ_HANDLED; return IRQ_HANDLED;
} }
...@@ -624,6 +639,16 @@ static void __init prcm_setup_regs(void) ...@@ -624,6 +639,16 @@ static void __init prcm_setup_regs(void)
prm_write_mod_reg(OMAP3430_IO_EN | OMAP3430_WKUP_EN, prm_write_mod_reg(OMAP3430_IO_EN | OMAP3430_WKUP_EN,
OCP_MOD, OMAP3_PRM_IRQENABLE_MPU_OFFSET); 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 */ /* Don't attach IVA interrupts */
prm_write_mod_reg(0, WKUP_MOD, OMAP3430_PM_IVAGRPSEL); prm_write_mod_reg(0, WKUP_MOD, OMAP3430_PM_IVAGRPSEL);
prm_write_mod_reg(0, CORE_MOD, OMAP3430_PM_IVAGRPSEL1); prm_write_mod_reg(0, CORE_MOD, OMAP3430_PM_IVAGRPSEL1);
......
...@@ -273,35 +273,50 @@ struct powerdomain *pwrdm_lookup(const char *name) ...@@ -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 * * @fn: callback function *
* *
* Call the supplied function for each registered powerdomain. The * Call the supplied function for each registered powerdomain. The
* callback function can return anything but 0 to bail out early from * callback function can return anything but 0 to bail out early from
* the iterator. The callback function is called with the pwrdm_rwlock * the iterator. Returns the last return value of the callback function, which
* held for reading, so no powerdomain structure manipulation * should be 0 for success or anything else to indicate failure; or -EINVAL if
* functions should be called from the callback, although hardware * the function pointer is null.
* 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.
*/ */
int pwrdm_for_each(int (*fn)(struct powerdomain *pwrdm, void *user), int pwrdm_for_each_nolock(int (*fn)(struct powerdomain *pwrdm, void *user),
void *user) void *user)
{ {
struct powerdomain *temp_pwrdm; struct powerdomain *temp_pwrdm;
unsigned long flags;
int ret = 0; int ret = 0;
if (!fn) if (!fn)
return -EINVAL; return -EINVAL;
read_lock_irqsave(&pwrdm_rwlock, flags);
list_for_each_entry(temp_pwrdm, &pwrdm_list, node) { list_for_each_entry(temp_pwrdm, &pwrdm_list, node) {
ret = (*fn)(temp_pwrdm, user); ret = (*fn)(temp_pwrdm, user);
if (ret) if (ret)
break; 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); read_unlock_irqrestore(&pwrdm_rwlock, flags);
return ret; return ret;
......
...@@ -135,6 +135,8 @@ struct powerdomain *pwrdm_lookup(const char *name); ...@@ -135,6 +135,8 @@ struct powerdomain *pwrdm_lookup(const char *name);
int pwrdm_for_each(int (*fn)(struct powerdomain *pwrdm, void *user), int pwrdm_for_each(int (*fn)(struct powerdomain *pwrdm, void *user),
void *user); void *user);
int pwrdm_for_each_nolock(int (*fn)(struct powerdomain *pwrdm, void *user),
void *user);
int pwrdm_add_clkdm(struct powerdomain *pwrdm, struct clockdomain *clkdm); int pwrdm_add_clkdm(struct powerdomain *pwrdm, struct clockdomain *clkdm);
int pwrdm_del_clkdm(struct powerdomain *pwrdm, struct clockdomain *clkdm); int pwrdm_del_clkdm(struct powerdomain *pwrdm, struct clockdomain *clkdm);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment