mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-25 02:09:42 +00:00
Merge branch 'pm-fixes-32' of git://git.kernel.org/pub/scm/linux/kernel/git/khilman/linux-omap-pm into omap-fixes-for-linus
This commit is contained in:
commit
ab8d64d7d4
4 changed files with 139 additions and 97 deletions
|
@ -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)
|
||||||
(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);
|
||||||
|
|
||||||
static struct powerdomain *mpu_pwrdm;
|
static struct powerdomain *mpu_pwrdm;
|
||||||
|
|
||||||
/* PRCM Interrupt Handler for wakeups */
|
/*
|
||||||
|
* 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, 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(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);
|
||||||
|
}
|
||||||
|
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
c += prcm_clear_mod_irqs(CORE_MOD, 3);
|
||||||
|
c += prcm_clear_mod_irqs(OMAP3430ES2_USBHOST_MOD, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
static irqreturn_t prcm_interrupt_handler (int irq, void *dev_id)
|
||||||
{
|
{
|
||||||
u32 wkst, irqstatus_mpu;
|
u32 irqstatus_mpu;
|
||||||
u32 fclk, iclk;
|
int c = 0;
|
||||||
|
|
||||||
/* WKUP */
|
do {
|
||||||
wkst = prm_read_mod_reg(WKUP_MOD, PM_WKST);
|
irqstatus_mpu = prm_read_mod_reg(OCP_MOD,
|
||||||
if (wkst) {
|
OMAP3_PRM_IRQSTATUS_MPU_OFFSET);
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* CORE */
|
if (irqstatus_mpu & (OMAP3430_WKUP_ST | OMAP3430_IO_ST)) {
|
||||||
wkst = prm_read_mod_reg(CORE_MOD, PM_WKST1);
|
c = _prcm_int_handle_wakeup();
|
||||||
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 */
|
/*
|
||||||
wkst = prm_read_mod_reg(OMAP3430_PER_MOD, PM_WKST);
|
* Is the MPU PRCM interrupt handler racing with the
|
||||||
if (wkst) {
|
* IVA2 PRCM interrupt handler ?
|
||||||
iclk = cm_read_mod_reg(OMAP3430_PER_MOD, CM_ICLKEN);
|
*/
|
||||||
fclk = cm_read_mod_reg(OMAP3430_PER_MOD, CM_FCLKEN);
|
WARN(c == 0, "prcm: WARNING: PRCM indicated MPU wakeup "
|
||||||
cm_set_mod_reg_bits(wkst, OMAP3430_PER_MOD, CM_ICLKEN);
|
"but no wakeup sources are marked\n");
|
||||||
cm_set_mod_reg_bits(wkst, OMAP3430_PER_MOD, CM_FCLKEN);
|
} else {
|
||||||
prm_write_mod_reg(wkst, OMAP3430_PER_MOD, PM_WKST);
|
/* XXX we need to expand our PRCM interrupt handler */
|
||||||
while (prm_read_mod_reg(OMAP3430_PER_MOD, PM_WKST))
|
WARN(1, "prcm: WARNING: PRCM interrupt received, but "
|
||||||
cpu_relax();
|
"no code to handle it (%08x)\n", irqstatus_mpu);
|
||||||
cm_write_mod_reg(iclk, OMAP3430_PER_MOD, CM_ICLKEN);
|
|
||||||
cm_write_mod_reg(fclk, OMAP3430_PER_MOD, CM_FCLKEN);
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
irqstatus_mpu = prm_read_mod_reg(OCP_MOD,
|
prm_write_mod_reg(irqstatus_mpu, OCP_MOD,
|
||||||
OMAP3_PRM_IRQSTATUS_MPU_OFFSET);
|
OMAP3_PRM_IRQSTATUS_MPU_OFFSET);
|
||||||
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)
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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);
|
||||||
|
|
||||||
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);
|
||||||
|
|
Loading…
Reference in a new issue