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

Commit 1d9a5425 authored by Tero Kristo's avatar Tero Kristo Committed by Tony Lindgren
Browse files

ARM: OMAP2+: clockdomain: add usecounting support to autoidle APIs



The previous implementation was racy in many locations, where the current
status of the clockdomain was read out, some operations were executed,
and the previous status info was used afterwards to decide next state
for the clockdomain. Instead, fix the implementation of the allow_idle /
deny_idle APIs to properly have usecounting support. This allows clean
handling internally within the clockdomain core, and simplifies the
usage also within hwmod.

Signed-off-by: default avatarTero Kristo <t-kristo@ti.com>
Signed-off-by: default avatarTony Lindgren <tony@atomide.com>
parent e98580e8
Loading
Loading
Loading
Loading
+24 −12
Original line number Diff line number Diff line
@@ -465,9 +465,6 @@ int clkdm_complete_init(void)
		return -EACCES;

	list_for_each_entry(clkdm, &clkdm_list, node) {
		if (clkdm->flags & CLKDM_CAN_FORCE_WAKEUP)
			clkdm_wakeup(clkdm);
		else if (clkdm->flags & CLKDM_CAN_DISABLE_AUTO)
		clkdm_deny_idle(clkdm);

		_resolve_clkdm_deps(clkdm, clkdm->wkdep_srcs);
@@ -925,11 +922,20 @@ void clkdm_allow_idle_nolock(struct clockdomain *clkdm)
	if (!clkdm)
		return;

	if (!(clkdm->flags & CLKDM_CAN_ENABLE_AUTO)) {
		pr_debug("clock: %s: automatic idle transitions cannot be enabled\n",
			 clkdm->name);
	if (!WARN_ON(!clkdm->forcewake_count))
		clkdm->forcewake_count--;

	if (clkdm->forcewake_count)
		return;

	if (!clkdm->usecount && (clkdm->flags & CLKDM_CAN_FORCE_SLEEP))
		clkdm_sleep_nolock(clkdm);

	if (!(clkdm->flags & CLKDM_CAN_ENABLE_AUTO))
		return;

	if (clkdm->flags & CLKDM_MISSING_IDLE_REPORTING)
		return;
	}

	if (!arch_clkdm || !arch_clkdm->clkdm_allow_idle)
		return;
@@ -974,11 +980,17 @@ void clkdm_deny_idle_nolock(struct clockdomain *clkdm)
	if (!clkdm)
		return;

	if (!(clkdm->flags & CLKDM_CAN_DISABLE_AUTO)) {
		pr_debug("clockdomain: %s: automatic idle transitions cannot be disabled\n",
			 clkdm->name);
	if (clkdm->forcewake_count++)
		return;

	if (clkdm->flags & CLKDM_CAN_FORCE_WAKEUP)
		clkdm_wakeup_nolock(clkdm);

	if (!(clkdm->flags & CLKDM_CAN_DISABLE_AUTO))
		return;

	if (clkdm->flags & CLKDM_MISSING_IDLE_REPORTING)
		return;
	}

	if (!arch_clkdm || !arch_clkdm->clkdm_deny_idle)
		return;
+2 −0
Original line number Diff line number Diff line
@@ -114,6 +114,7 @@ struct omap_hwmod;
 * @wkdep_srcs: Clockdomains that can be told to wake this powerdomain up
 * @sleepdep_srcs: Clockdomains that can be told to keep this clkdm from inact
 * @usecount: Usecount tracking
 * @forcewake_count: Usecount for forcing the domain active
 * @node: list_head to link all clockdomains together
 *
 * @prcm_partition should be a macro from mach-omap2/prcm44xx.h (OMAP4 only)
@@ -138,6 +139,7 @@ struct clockdomain {
	struct clkdm_dep *wkdep_srcs;
	struct clkdm_dep *sleepdep_srcs;
	int usecount;
	int forcewake_count;
	struct list_head node;
};

+1 −1
Original line number Diff line number Diff line
@@ -140,7 +140,7 @@ static int omap_enter_idle_coupled(struct cpuidle_device *dev,
		    mpuss_can_lose_context)
			gic_dist_disable();

		clkdm_wakeup(cpu_clkdm[1]);
		clkdm_deny_idle(cpu_clkdm[1]);
		omap_set_pwrdm_state(cpu_pd[1], PWRDM_POWER_ON);
		clkdm_allow_idle(cpu_clkdm[1]);

+1 −1
Original line number Diff line number Diff line
@@ -200,7 +200,7 @@ static int omap4_boot_secondary(unsigned int cpu, struct task_struct *idle)
		 * Ensure that CPU power state is set to ON to avoid CPU
		 * powerdomain transition on wfi
		 */
		clkdm_wakeup_nolock(cpu1_clkdm);
		clkdm_deny_idle_nolock(cpu1_clkdm);
		pwrdm_set_next_pwrst(cpu1_pwrdm, PWRDM_POWER_ON);
		clkdm_allow_idle_nolock(cpu1_clkdm);

+12 −15
Original line number Diff line number Diff line
@@ -1702,7 +1702,6 @@ static int _deassert_hardreset(struct omap_hwmod *oh, const char *name)
{
	struct omap_hwmod_rst_info ohri;
	int ret = -EINVAL;
	int hwsup = 0;

	if (!oh)
		return -EINVAL;
@@ -1720,7 +1719,7 @@ static int _deassert_hardreset(struct omap_hwmod *oh, const char *name)
		 * might not be completed. The clockdomain can be set
		 * in HW_AUTO only when the module become ready.
		 */
		hwsup = clkdm_in_hwsup(oh->clkdm);
		clkdm_deny_idle(oh->clkdm);
		ret = clkdm_hwmod_enable(oh->clkdm, oh);
		if (ret) {
			WARN(1, "omap_hwmod: %s: could not enable clockdomain %s: %d\n",
@@ -1747,7 +1746,6 @@ static int _deassert_hardreset(struct omap_hwmod *oh, const char *name)
		 * Set the clockdomain to HW_AUTO, assuming that the
		 * previous state was HW_AUTO.
		 */
		if (hwsup)
		clkdm_allow_idle(oh->clkdm);

		clkdm_hwmod_disable(oh->clkdm, oh);
@@ -2102,7 +2100,6 @@ static int _enable_preprogram(struct omap_hwmod *oh)
static int _enable(struct omap_hwmod *oh)
{
	int r;
	int hwsup = 0;

	pr_debug("omap_hwmod: %s: enabling\n", oh->name);

@@ -2162,8 +2159,7 @@ static int _enable(struct omap_hwmod *oh)
		 * completely the module. The clockdomain can be set
		 * in HW_AUTO only when the module become ready.
		 */
		hwsup = clkdm_in_hwsup(oh->clkdm) &&
			!clkdm_missing_idle_reporting(oh->clkdm);
		clkdm_deny_idle(oh->clkdm);
		r = clkdm_hwmod_enable(oh->clkdm, oh);
		if (r) {
			WARN(1, "omap_hwmod: %s: could not enable clockdomain %s: %d\n",
@@ -2183,14 +2179,10 @@ static int _enable(struct omap_hwmod *oh)

	r = (soc_ops.wait_target_ready) ? soc_ops.wait_target_ready(oh) :
		-EINVAL;
	if (!r) {
		/*
		 * Set the clockdomain to HW_AUTO only if the target is ready,
		 * assuming that the previous state was HW_AUTO
		 */
		if (oh->clkdm && hwsup)
	if (oh->clkdm)
		clkdm_allow_idle(oh->clkdm);

	if (!r) {
		oh->_state = _HWMOD_STATE_ENABLED;

		/* Access the sysconfig only if the target is ready */
@@ -2244,6 +2236,9 @@ static int _idle(struct omap_hwmod *oh)
		_idle_sysc(oh);
	_del_initiator_dep(oh, mpu_oh);

	if (oh->clkdm)
		clkdm_deny_idle(oh->clkdm);

	if (oh->flags & HWMOD_BLOCK_WFI)
		cpu_idle_poll_ctrl(false);
	if (soc_ops.disable_module)
@@ -2256,8 +2251,10 @@ static int _idle(struct omap_hwmod *oh)
	 * transition to complete properly.
	 */
	_disable_clocks(oh);
	if (oh->clkdm)
	if (oh->clkdm) {
		clkdm_allow_idle(oh->clkdm);
		clkdm_hwmod_disable(oh->clkdm, oh);
	}

	/* Mux pins for device idle if populated */
	if (oh->mux && oh->mux->pads_dynamic) {
Loading