Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6b21a9ce authored by Arnd Bergmann's avatar Arnd Bergmann
Browse files

Merge tag 'omap-devel-pm-for-v3.6' of...

Merge tag 'omap-devel-pm-for-v3.6' of git://git.kernel.org/pub/scm/linux/kernel/git/tmlind/linux-omap into next/pm

From: Tony Lindgren <tony@atomide.com>:

Here are some omap PM changes that reimplement omap PRCM I/O chain
code for wake-ups, and improve idle latencies for cpuidle.

* tag 'omap-devel-pm-for-v3.6' of git://git.kernel.org/pub/scm/linux/kernel/git/tmlind/linux-omap

:
  ARM: OMAP2+: PM: fix IRQ_NOAUTOEN removal by mis-merge
  ARM: OMAP3: PM: cpuidle: optimize the clkdm idle latency in C1 state
  ARM: OMAP3: PM: cpuidle: optimize the PER latency in C1 state
  ARM: OMAP3: PM: cpuidle: default to C1 in next_valid_state
  ARM: OMAP3: PM: cleanup cam_pwrdm leftovers
  ARM: OMAP3: PM: call pre/post transition per powerdomain
  ARM: OMAP2+: powerdomain: allow pre/post transtion to be per pwrdm
  ARM: OMAP3: PM: Remove IO Daisychain control from cpuidle
  ARM: OMAP3PLUS: hwmod: reconfigure IO Daisychain during hwmod mux
  ARM: OMAP3+: PRM: Enable IO wake up
  ARM: OMAP4: PRM: Add IO Daisychain support
  ARM: OMAP3: PM: Move IO Daisychain function to omap3 prm file
  ARM: OMAP3: PM: correct enable/disable of daisy io chain
  ARM: OMAP2+: PRM: fix compile for OMAP4-only build

Signed-off-by: default avatarArnd Bergmann <arnd@arndb.de>
parents 0d1d76dd d660e9b9
Loading
Loading
Loading
Loading
+27 −44
Original line number Diff line number Diff line
@@ -77,20 +77,6 @@ static struct omap3_idle_statedata omap3_idle_data[] = {

static struct powerdomain *mpu_pd, *core_pd, *per_pd, *cam_pd;

static int _cpuidle_allow_idle(struct powerdomain *pwrdm,
				struct clockdomain *clkdm)
{
	clkdm_allow_idle(clkdm);
	return 0;
}

static int _cpuidle_deny_idle(struct powerdomain *pwrdm,
				struct clockdomain *clkdm)
{
	clkdm_deny_idle(clkdm);
	return 0;
}

static int __omap3_enter_idle(struct cpuidle_device *dev,
				struct cpuidle_driver *drv,
				int index)
@@ -108,8 +94,8 @@ static int __omap3_enter_idle(struct cpuidle_device *dev,

	/* Deny idle for C1 */
	if (index == 0) {
		pwrdm_for_each_clkdm(mpu_pd, _cpuidle_deny_idle);
		pwrdm_for_each_clkdm(core_pd, _cpuidle_deny_idle);
		clkdm_deny_idle(mpu_pd->pwrdm_clkdms[0]);
		clkdm_deny_idle(core_pd->pwrdm_clkdms[0]);
	}

	/*
@@ -131,8 +117,8 @@ static int __omap3_enter_idle(struct cpuidle_device *dev,

	/* Re-allow idle for C1 */
	if (index == 0) {
		pwrdm_for_each_clkdm(mpu_pd, _cpuidle_allow_idle);
		pwrdm_for_each_clkdm(core_pd, _cpuidle_allow_idle);
		clkdm_allow_idle(mpu_pd->pwrdm_clkdms[0]);
		clkdm_allow_idle(core_pd->pwrdm_clkdms[0]);
	}

return_sleep_time:
@@ -178,7 +164,7 @@ static int next_valid_state(struct cpuidle_device *dev,
	u32 mpu_deepest_state = PWRDM_POWER_RET;
	u32 core_deepest_state = PWRDM_POWER_RET;
	int idx;
	int next_index = -1;
	int next_index = 0; /* C1 is the default value */

	if (enable_off_mode) {
		mpu_deepest_state = PWRDM_POWER_OFF;
@@ -209,12 +195,6 @@ static int next_valid_state(struct cpuidle_device *dev,
		}
	}

	/*
	 * C1 is always valid.
	 * So, no need to check for 'next_index == -1' outside
	 * this loop.
	 */

	return next_index;
}

@@ -232,19 +212,18 @@ static int omap3_enter_idle_bm(struct cpuidle_device *dev,
			       int index)
{
	int new_state_idx;
	u32 core_next_state, per_next_state = 0, per_saved_state = 0, cam_state;
	u32 core_next_state, per_next_state = 0, per_saved_state = 0;
	struct omap3_idle_statedata *cx;
	int ret;

	/*
	 * Prevent idle completely if CAM is active.
	 * Use only C1 if CAM is active.
	 * CAM does not have wakeup capability in OMAP3.
	 */
	cam_state = pwrdm_read_pwrst(cam_pd);
	if (cam_state == PWRDM_POWER_ON) {
	if (pwrdm_read_pwrst(cam_pd) == PWRDM_POWER_ON)
		new_state_idx = drv->safe_state_index;
		goto select_state;
	}
	else
		new_state_idx = next_valid_state(dev, drv, index);

	/*
	 * FIXME: we currently manage device-specific idle states
@@ -254,24 +233,28 @@ static int omap3_enter_idle_bm(struct cpuidle_device *dev,
	 *        its own code.
	 */

	/* Program PER state */
	cx = &omap3_idle_data[new_state_idx];
	core_next_state = cx->core_state;
	per_next_state = per_saved_state = pwrdm_read_next_pwrst(per_pd);
	if (new_state_idx == 0) {
		/* In C1 do not allow PER state lower than CORE state */
		if (per_next_state < core_next_state)
			per_next_state = core_next_state;
	} else {
		/*
	 * Prevent PER off if CORE is not in retention or off as this
		 * Prevent PER OFF if CORE is not in RETention or OFF as this
		 * would disable PER wakeups completely.
		 */
	cx = &omap3_idle_data[index];
	core_next_state = cx->core_state;
	per_next_state = per_saved_state = pwrdm_read_next_pwrst(per_pd);
		if ((per_next_state == PWRDM_POWER_OFF) &&
		    (core_next_state > PWRDM_POWER_RET))
			per_next_state = PWRDM_POWER_RET;
	}

	/* Are we changing PER target state? */
	if (per_next_state != per_saved_state)
		pwrdm_set_next_pwrst(per_pd, per_next_state);

	new_state_idx = next_valid_state(dev, drv, index);

select_state:
	ret = omap3_enter_idle(dev, drv, new_state_idx);

	/* Restore original PER state if it was modified */
@@ -288,7 +271,7 @@ struct cpuidle_driver omap3_idle_driver = {
	.owner = 	THIS_MODULE,
	.states = {
		{
			.enter		  = omap3_enter_idle,
			.enter		  = omap3_enter_idle_bm,
			.exit_latency	  = 2 + 2,
			.target_residency = 5,
			.flags		  = CPUIDLE_FLAG_TIME_VALID,
+2 −2
Original line number Diff line number Diff line
@@ -255,7 +255,7 @@ int omap4_enter_lowpower(unsigned int cpu, unsigned int power_state)
		return -ENXIO;
	}

	pwrdm_pre_transition();
	pwrdm_pre_transition(NULL);

	/*
	 * Check MPUSS next state and save interrupt controller if needed.
@@ -287,7 +287,7 @@ int omap4_enter_lowpower(unsigned int cpu, unsigned int power_state)
	wakeup_cpu = smp_processor_id();
	set_cpu_next_pwrst(wakeup_cpu, PWRDM_POWER_ON);

	pwrdm_post_transition();
	pwrdm_post_transition(NULL);

	return 0;
}
+36 −2
Original line number Diff line number Diff line
@@ -153,6 +153,7 @@
#include "prm44xx.h"
#include "prminst44xx.h"
#include "mux.h"
#include "pm.h"

/* Maximum microseconds to wait for OMAP module to softreset */
#define MAX_MODULE_SOFTRESET_WAIT	10000
@@ -172,6 +173,9 @@ static LIST_HEAD(omap_hwmod_list);
/* mpu_oh: used to add/remove MPU initiator from sleepdep list */
static struct omap_hwmod *mpu_oh;

/* io_chain_lock: used to serialize reconfigurations of the I/O chain */
static DEFINE_SPINLOCK(io_chain_lock);

/*
 * linkspace: ptr to a buffer that struct omap_hwmod_link records are
 * allocated from - used to reduce the number of small memory
@@ -1737,6 +1741,32 @@ static int _reset(struct omap_hwmod *oh)
	return r;
}

/**
 * _reconfigure_io_chain - clear any I/O chain wakeups and reconfigure chain
 *
 * Call the appropriate PRM function to clear any logged I/O chain
 * wakeups and to reconfigure the chain.  This apparently needs to be
 * done upon every mux change.  Since hwmods can be concurrently
 * enabled and idled, hold a spinlock around the I/O chain
 * reconfiguration sequence.  No return value.
 *
 * XXX When the PRM code is moved to drivers, this function can be removed,
 * as the PRM infrastructure should abstract this.
 */
static void _reconfigure_io_chain(void)
{
	unsigned long flags;

	spin_lock_irqsave(&io_chain_lock, flags);

	if (cpu_is_omap34xx() && omap3_has_io_chain_ctrl())
		omap3xxx_prm_reconfigure_io_chain();
	else if (cpu_is_omap44xx())
		omap44xx_prm_reconfigure_io_chain();

	spin_unlock_irqrestore(&io_chain_lock, flags);
}

/**
 * _enable - enable an omap_hwmod
 * @oh: struct omap_hwmod *
@@ -1793,8 +1823,10 @@ static int _enable(struct omap_hwmod *oh)
	/* Mux pins for device runtime if populated */
	if (oh->mux && (!oh->mux->enabled ||
			((oh->_state == _HWMOD_STATE_IDLE) &&
			 oh->mux->pads_dynamic)))
			 oh->mux->pads_dynamic))) {
		omap_hwmod_mux(oh->mux, _HWMOD_STATE_ENABLED);
		_reconfigure_io_chain();
	}

	_add_initiator_dep(oh, mpu_oh);

@@ -1883,8 +1915,10 @@ static int _idle(struct omap_hwmod *oh)
		clkdm_hwmod_disable(oh->clkdm, oh);

	/* Mux pins for device idle if populated */
	if (oh->mux && oh->mux->pads_dynamic)
	if (oh->mux && oh->mux->pads_dynamic) {
		omap_hwmod_mux(oh->mux, _HWMOD_STATE_IDLE);
		_reconfigure_io_chain();
	}

	oh->_state = _HWMOD_STATE_IDLE;

+13 −50
Original line number Diff line number Diff line
@@ -70,34 +70,6 @@ void (*omap3_do_wfi_sram)(void);

static struct powerdomain *mpu_pwrdm, *neon_pwrdm;
static struct powerdomain *core_pwrdm, *per_pwrdm;
static struct powerdomain *cam_pwrdm;

static void omap3_enable_io_chain(void)
{
	int timeout = 0;

	omap2_prm_set_mod_reg_bits(OMAP3430_EN_IO_CHAIN_MASK, WKUP_MOD,
				   PM_WKEN);
	/* Do a readback to assure write has been done */
	omap2_prm_read_mod_reg(WKUP_MOD, PM_WKEN);

	while (!(omap2_prm_read_mod_reg(WKUP_MOD, PM_WKEN) &
		 OMAP3430_ST_IO_CHAIN_MASK)) {
		timeout++;
		if (timeout > 1000) {
			pr_err("Wake up daisy chain activation failed.\n");
			return;
		}
		omap2_prm_set_mod_reg_bits(OMAP3430_ST_IO_CHAIN_MASK,
					   WKUP_MOD, PM_WKEN);
	}
}

static void omap3_disable_io_chain(void)
{
	omap2_prm_clear_mod_reg_bits(OMAP3430_EN_IO_CHAIN_MASK, WKUP_MOD,
				     PM_WKEN);
}

static void omap3_core_save_context(void)
{
@@ -299,24 +271,22 @@ void omap_sram_idle(void)
	/* Enable IO-PAD and IO-CHAIN wakeups */
	per_next_state = pwrdm_read_next_pwrst(per_pwrdm);
	core_next_state = pwrdm_read_next_pwrst(core_pwrdm);
	if (omap3_has_io_wakeup() &&
	    (per_next_state < PWRDM_POWER_ON ||
	     core_next_state < PWRDM_POWER_ON)) {
		omap2_prm_set_mod_reg_bits(OMAP3430_EN_IO_MASK, WKUP_MOD, PM_WKEN);
		if (omap3_has_io_chain_ctrl())
			omap3_enable_io_chain();
	}

	pwrdm_pre_transition();
	if (mpu_next_state < PWRDM_POWER_ON) {
		pwrdm_pre_transition(mpu_pwrdm);
		pwrdm_pre_transition(neon_pwrdm);
	}

	/* PER */
	if (per_next_state < PWRDM_POWER_ON) {
		pwrdm_pre_transition(per_pwrdm);
		per_going_off = (per_next_state == PWRDM_POWER_OFF) ? 1 : 0;
		omap2_gpio_prepare_for_idle(per_going_off);
	}

	/* CORE */
	if (core_next_state < PWRDM_POWER_ON) {
		pwrdm_pre_transition(core_pwrdm);
		if (core_next_state == PWRDM_POWER_OFF) {
			omap3_core_save_context();
			omap3_cm_save_context();
@@ -369,26 +339,20 @@ void omap_sram_idle(void)
			omap2_prm_clear_mod_reg_bits(OMAP3430_AUTO_OFF_MASK,
					       OMAP3430_GR_MOD,
					       OMAP3_PRM_VOLTCTRL_OFFSET);
		pwrdm_post_transition(core_pwrdm);
	}
	omap3_intc_resume_idle();

	pwrdm_post_transition();

	/* PER */
	if (per_next_state < PWRDM_POWER_ON)
	if (per_next_state < PWRDM_POWER_ON) {
		omap2_gpio_resume_after_idle();

	/* Disable IO-PAD and IO-CHAIN wakeup */
	if (omap3_has_io_wakeup() &&
	    (per_next_state < PWRDM_POWER_ON ||
	     core_next_state < PWRDM_POWER_ON)) {
		omap2_prm_clear_mod_reg_bits(OMAP3430_EN_IO_MASK, WKUP_MOD,
					     PM_WKEN);
		if (omap3_has_io_chain_ctrl())
			omap3_disable_io_chain();
		pwrdm_post_transition(per_pwrdm);
	}

	clkdm_allow_idle(mpu_pwrdm->pwrdm_clkdms[0]);
	if (mpu_next_state < PWRDM_POWER_ON) {
		pwrdm_post_transition(mpu_pwrdm);
		pwrdm_post_transition(neon_pwrdm);
	}
}

static void omap3_pm_idle(void)
@@ -749,7 +713,6 @@ int __init omap3_pm_init(void)
	neon_pwrdm = pwrdm_lookup("neon_pwrdm");
	per_pwrdm = pwrdm_lookup("per_pwrdm");
	core_pwrdm = pwrdm_lookup("core_pwrdm");
	cam_pwrdm = pwrdm_lookup("cam_pwrdm");

	neon_clkdm = clkdm_lookup("neon_clkdm");
	mpu_clkdm = clkdm_lookup("mpu_clkdm");
+12 −4
Original line number Diff line number Diff line
@@ -981,15 +981,23 @@ int pwrdm_state_switch(struct powerdomain *pwrdm)
	return ret;
}

int pwrdm_pre_transition(void)
int pwrdm_pre_transition(struct powerdomain *pwrdm)
{
	if (pwrdm)
		_pwrdm_pre_transition_cb(pwrdm, NULL);
	else
		pwrdm_for_each(_pwrdm_pre_transition_cb, NULL);

	return 0;
}

int pwrdm_post_transition(void)
int pwrdm_post_transition(struct powerdomain *pwrdm)
{
	if (pwrdm)
		_pwrdm_post_transition_cb(pwrdm, NULL);
	else
		pwrdm_for_each(_pwrdm_post_transition_cb, NULL);

	return 0;
}

Loading