Skip to contentSkip to conversation

: ARM: OMAP: PM usecounting changes

PATCH New Tero Kristo 3 participants 15 messages Sep 25, 2012 – Oct 17, 2012

Subsystems (7)
lore
Share & tools
lore
#0 lore raw

v6 cover : ARM: OMAP: PM usecounting changes

Hi,

Changes compared to previous version:

- Added patch #1 for fixing a potential race condition within clockdomain code
- Fixed patch #3 (old patch #2) regarding a similar race condition within the voltagedomain code

Tested with OMAP3 beagle, OMAP4460 GP panda, OMAP4430 EMU blaze devices.

Branch available:

git://gitorious.org/~kristo/omap-pm/omap-pm-work.git
branch: mainline-3.6-rc5-pwrdm-changes-v6

-Tero

#1 lore raw

PATCH v6 1/11 ARM: OMAP: clockdomain: Fix locking on _clkdm_clk_hwmod_enable / disable

Previously the code only acquired spinlock after increasing / decreasing the usecount value, which is wrong. This leaves a small window where a task switch may occur between the check of the usecount and the actual wakeup / sleep of the domain. Fixed by moving the spinlock locking before the usecount access. Left the usecount as atomic_t if someone wants an easy access to the parameter through atomic_read.

---
 arch/arm/mach-omap2/clockdomain.c |   15 +++++++++++----
 1 files changed, 11 insertions(+), 4 deletions(-)
arch/arm/mach-omap2/clockdomain.c +11 -4
index 8664f5a..173905d 100644
@@ -914,15 +914,18 @@ static int _clkdm_clk_hwmod_enable(struct clockdomain *clkdm)
 	if (!clkdm || !arch_clkdm || !arch_clkdm->clkdm_clk_enable)
 		return -EINVAL;
 
+	spin_lock_irqsave(&clkdm->lock, flags);
+
 	/*
 	 * For arch's with no autodeps, clkcm_clk_enable
 	 * should be called for every clock instance or hwmod that is
 	 * enabled, so the clkdm can be force woken up.
 	 */
-	if ((atomic_inc_return(&clkdm->usecount) > 1) && autodeps)
+	if ((atomic_inc_return(&clkdm->usecount) > 1) && autodeps) {
+		spin_unlock_irqrestore(&clkdm->lock, flags);
 		return 0;
+	}
 
-	spin_lock_irqsave(&clkdm->lock, flags);
 	arch_clkdm->clkdm_clk_enable(clkdm);
 	pwrdm_state_switch(clkdm->pwrdm.ptr);
 	spin_unlock_irqrestore(&clkdm->lock, flags);
@@ -939,15 +942,19 @@ static int _clkdm_clk_hwmod_disable(struct clockdomain *clkdm)
 	if (!clkdm || !arch_clkdm || !arch_clkdm->clkdm_clk_disable)
 		return -EINVAL;
 
+	spin_lock_irqsave(&clkdm->lock, flags);
+
 	if (atomic_read(&clkdm->usecount) == 0) {
+		spin_unlock_irqrestore(&clkdm->lock, flags);
 		WARN_ON(1); /* underflow */
 		return -ERANGE;
 	}
 
-	if (atomic_dec_return(&clkdm->usecount) > 0)
+	if (atomic_dec_return(&clkdm->usecount) > 0) {
+		spin_unlock_irqrestore(&clkdm->lock, flags);
 		return 0;
+	}
 
-	spin_lock_irqsave(&clkdm->lock, flags);
 	arch_clkdm->clkdm_clk_disable(clkdm);
 	pwrdm_state_switch(clkdm->pwrdm.ptr);
 	spin_unlock_irqrestore(&clkdm->lock, flags);
Signature · 1 line
1.7.4.1
maintainer
#2 lore raw

PATCH v6 1/11 ARM: OMAP: clockdomain: Fix locking on _clkdm_clk_hwmod_enable / disable

Quoted 8 lines · Tero Kristo

Previously the code only acquired spinlock after increasing / decreasing the usecount value, which is wrong. This leaves a small window where a task switch may occur between the check of the usecount and the actual wakeup / sleep of the domain. Fixed by moving the spinlock locking before the usecount access. Left the usecount as atomic_t if someone wants an easy access to the parameter through atomic_read.

Thanks, queued for 3.7-rc fixes.

- Paul

maintainer
#3 lore raw

PATCH v6 1/11 ARM: OMAP: clockdomain: Fix locking on _clkdm_clk_hwmod_enable / disable

In reply to Tero Kristo
Quoted 8 lines · Tero Kristo

Previously the code only acquired spinlock after increasing / decreasing the usecount value, which is wrong. This leaves a small window where a task switch may occur between the check of the usecount and the actual wakeup / sleep of the domain. Fixed by moving the spinlock locking before the usecount access. Left the usecount as atomic_t if someone wants an easy access to the parameter through atomic_read.

- Paul

maintainer
#4 lore raw

PATCH v6 1/11 ARM: OMAP: clockdomain: Fix locking on _clkdm_clk_hwmod_enable / disable

Quoted 9 lines · Paul Walmsley
Quoted 7 lines · Tero Kristo

Previously the code only acquired spinlock after increasing / decreasing the usecount value, which is wrong. This leaves a small window where a task switch may occur between the check of the usecount and the actual wakeup / sleep of the domain. Fixed by moving the spinlock locking before the usecount access. Left the usecount as atomic_t if someone wants an easy access to the parameter through atomic_read.

Quoted 3 lines
Quoted 1 line

OK I'll apply this into omap-for-v3.7-rc1/fixes-take4.

Regards,

Tony

#5 lore raw

PATCH v6 2/11 ARM: OMAP3+: voltage/pwrdm/clkdm/clock add recursive usecount tracking

In reply to Tero Kristo

This patch fixes the usecount tracking for omap3+, previously the usecount numbers were rather bogus and were not really useful for any purpose. Now usecount numbers track the number of really active clients on each domain. This patch also adds support for usecount tracking on powerdomain level and autoidle flag for clocks that are hardware controlled and should be skipped in usecount calculations.

Cc Paul Walmsley <[email protected]>
Cc Kevin Hilman <[email protected]>
---
 arch/arm/mach-omap2/clkt_iclk.c         |   21 ++++++++++++++++++
 arch/arm/mach-omap2/clockdomain.c       |   16 +++++++++++++-
 arch/arm/mach-omap2/dpll3xxx.c          |   19 ++++++++++++++++
 arch/arm/mach-omap2/powerdomain.c       |   35 +++++++++++++++++++++++++++++++
 arch/arm/mach-omap2/powerdomain.h       |    4 +++
 arch/arm/plat-omap/clock.c              |    6 +++++
 arch/arm/plat-omap/include/plat/clock.h |    2 +
 7 files changed, 102 insertions(+), 1 deletions(-)
arch/arm/mach-omap2/clkt_iclk.c +21 -0
index 3d43fba..1afc599 100644
@@ -21,6 +21,7 @@
 #include "clock2xxx.h"
 #include "cm2xxx_3xxx.h"
 #include "cm-regbits-24xx.h"
+#include "clockdomain.h"
 
 /* Private functions */
 
@@ -34,6 +35,16 @@ void omap2_clkt_iclk_allow_idle(struct clk *clk)
 	v = __raw_readl((__force void __iomem *)r);
 	v |= (1 << clk->enable_bit);
 	__raw_writel(v, (__force void __iomem *)r);
+
+	/* Remove this clock from parent clockdomain usecounts */
+	if (clk->usecount && clk->clkdm)
+		clkdm_clk_disable(clk->clkdm, clk);
+
+	/*
+	 * Mark as autoidle, so we continue to ignore this clock in
+	 * parent clkdm usecount calculations
+	 */
+	clk->autoidle = true;
 }
 
 /* XXX */
@@ -46,6 +57,16 @@ void omap2_clkt_iclk_deny_idle(struct clk *clk)
 	v = __raw_readl((__force void __iomem *)r);
 	v &= ~(1 << clk->enable_bit);
 	__raw_writel(v, (__force void __iomem *)r);
+
+	/*
+	 * Disable autoidle flag so further clkdm usecounts take this
+	 * clock into account
+	 */
+	clk->autoidle = false;
+
+	/* Add clock back to parent clockdomain usecount */
+	if (clk->usecount && clk->clkdm)
+		clkdm_clk_enable(clk->clkdm, clk);
 }
 
 /* Public data */
arch/arm/mach-omap2/clockdomain.c +15 -1
index 173905d..7715353 100644
@@ -910,6 +910,7 @@ bool clkdm_in_hwsup(struct clockdomain *clkdm)
 static int _clkdm_clk_hwmod_enable(struct clockdomain *clkdm)
 {
 	unsigned long flags;
+	int usecount;
 
 	if (!clkdm || !arch_clkdm || !arch_clkdm->clkdm_clk_enable)
 		return -EINVAL;
@@ -921,12 +922,16 @@ static int _clkdm_clk_hwmod_enable(struct clockdomain *clkdm)
 	 * should be called for every clock instance or hwmod that is
 	 * enabled, so the clkdm can be force woken up.
 	 */
-	if ((atomic_inc_return(&clkdm->usecount) > 1) && autodeps) {
+	usecount = atomic_inc_return(&clkdm->usecount);
+
+	if (usecount > 1 && autodeps) {
 		spin_unlock_irqrestore(&clkdm->lock, flags);
 		return 0;
 	}
 
 	arch_clkdm->clkdm_clk_enable(clkdm);
+	if (usecount == 1)
+		pwrdm_clkdm_enable(clkdm->pwrdm.ptr);
 	pwrdm_state_switch(clkdm->pwrdm.ptr);
 	spin_unlock_irqrestore(&clkdm->lock, flags);
 
@@ -956,6 +961,7 @@ static int _clkdm_clk_hwmod_disable(struct clockdomain *clkdm)
 	}
 
 	arch_clkdm->clkdm_clk_disable(clkdm);
+	pwrdm_clkdm_disable(clkdm->pwrdm.ptr);
 	pwrdm_state_switch(clkdm->pwrdm.ptr);
 	spin_unlock_irqrestore(&clkdm->lock, flags);
 
@@ -988,6 +994,10 @@ int clkdm_clk_enable(struct clockdomain *clkdm, struct clk *clk)
 	if (!clk)
 		return -EINVAL;
 
+	/* If autoidle clock, do not update clkdm usecounts */
+	if (clk->autoidle)
+		return 0;
+
 	return _clkdm_clk_hwmod_enable(clkdm);
 }
 
@@ -1014,6 +1024,10 @@ int clkdm_clk_disable(struct clockdomain *clkdm, struct clk *clk)
 	if (!clk)
 		return -EINVAL;
 
+	/* If autoidle clock, do not update clkdm usecounts */
+	if (clk->autoidle)
+		return 0;
+
 	return _clkdm_clk_hwmod_disable(clkdm);
 }
 
arch/arm/mach-omap2/dpll3xxx.c +19 -0
index b9c8d2f..da660d2 100644
@@ -34,6 +34,7 @@
 #include "clock.h"
 #include "cm2xxx_3xxx.h"
 #include "cm-regbits-34xx.h"
+#include "clockdomain.h"
 
 /* CM_AUTOIDLE_PLL*.AUTO_* bit values */
 #define DPLL_AUTOIDLE_DISABLE			0x0
@@ -571,6 +572,15 @@ void omap3_dpll_allow_idle(struct clk *clk)
 	v |= DPLL_AUTOIDLE_LOW_POWER_STOP << __ffs(dd->autoidle_mask);
 	__raw_writel(v, dd->autoidle_reg);
 
+	/* Remove this clock from parent clockdomain usecounts */
+	if (clk->usecount && clk->clkdm)
+		clkdm_clk_disable(clk->clkdm, clk);
+
+	/*
+	 * Mark as autoidle, so we continue to ignore this clock in
+	 * parent clkdm usecount calculations
+	 */
+	clk->autoidle = true;
 }
 
 /**
@@ -600,6 +610,15 @@ void omap3_dpll_deny_idle(struct clk *clk)
 	v |= DPLL_AUTOIDLE_DISABLE << __ffs(dd->autoidle_mask);
 	__raw_writel(v, dd->autoidle_reg);
 
+	/*
+	 * Disable autoidle flag so further clkdm usecounts take this
+	 * clock into account
+	 */
+	clk->autoidle = false;
+
+	/* Add clock back to parent clockdomain usecount */
+	if (clk->usecount && clk->clkdm)
+		clkdm_clk_enable(clk->clkdm, clk);
 }
 
 /* Clock control for DPLL outputs */
arch/arm/mach-omap2/powerdomain.c +35 -0
index d6daa81..ba49029 100644
@@ -1466,6 +1466,41 @@ int pwrdm_state_switch(struct powerdomain *pwrdm)
 	return ret;
 }
 
+/**
+ * pwrdm_clkdm_enable - increment powerdomain usecount
+ * @pwrdm: struct powerdomain *
+ *
+ * Increases the usecount for a powerdomain. Called from clockdomain
+ * code once a clockdomain becomes active.
+ */
+void pwrdm_clkdm_enable(struct powerdomain *pwrdm)
+{
+	if (!pwrdm)
+		return;
+
+	atomic_inc(&pwrdm->usecount);
+}
+
+/**
+ * pwrdm_clkdm_disable - decrease powerdomain usecount
+ * @pwrdm: struct powerdomain *
+ *
+ * Decreases the usecount for a powerdomain. Called from clockdomain
+ * code once a clockdomain usecount reaches zero, i.e. it is ready to
+ * idle.
+ */
+void pwrdm_clkdm_disable(struct powerdomain *pwrdm)
+{
+	int val;
+
+	if (!pwrdm)
+		return;
+
+	val = atomic_dec_return(&pwrdm->usecount);
+
+	WARN_ON(val < 0);
+}
+
 int pwrdm_pre_transition(struct powerdomain *pwrdm)
 {
 	if (pwrdm)
arch/arm/mach-omap2/powerdomain.h +4 -0
index dcd2315..1bad255 100644
@@ -92,6 +92,9 @@ int pwrdm_state_switch(struct powerdomain *pwrdm);
 int pwrdm_pre_transition(struct powerdomain *pwrdm);
 int pwrdm_post_transition(struct powerdomain *pwrdm);
 
+void pwrdm_clkdm_enable(struct powerdomain *pwrdm);
+void pwrdm_clkdm_disable(struct powerdomain *pwrdm);
+
 int pwrdm_get_context_loss_count(struct powerdomain *pwrdm);
 bool pwrdm_can_ever_lose_context(struct powerdomain *pwrdm);
 
@@ -209,6 +212,7 @@ struct powerdomain {
 	unsigned state_counter[PWRDM_MAX_FUNC_PWRSTS];
 	unsigned ret_logic_off_counter;
 	unsigned ret_mem_off_counter[PWRDM_MAX_MEM_BANKS];
+	atomic_t usecount;
 
 	const u8 pwrstctrl_offs;
 	const u8 pwrstst_offs;
arch/arm/plat-omap/clock.c +6 -0
index 706b7e2..d657d39 100644
@@ -282,6 +282,12 @@ int clk_register(struct clk *clk)
 		list_add(&clk->sibling, &root_clks);
 
 	list_add(&clk->node, &clocks);
+	/*
+	 * If clock has no ops, it is handled by hardware and thus will
+	 * idle automatically
+	 */
+	if (clk->ops == &clkops_null)
+		clk->autoidle = true;
 	if (clk->init)
 		clk->init(clk);
 	mutex_unlock(&clocks_mutex);
arch/arm/plat-omap/include/plat/clock.h +2 -0
index 656b986..3b5acea 100644
@@ -206,6 +206,7 @@ struct dpll_data {
  * @init: fn ptr to do clock-specific initialization
  * @enable_bit: bitshift to write to enable/disable the clock (see @enable_reg)
  * @usecount: number of users that have requested this clock to be enabled
+ * @autoidle: indicates hardware controlled clock (not used in domain usecounts)
  * @fixed_div: when > 0, this clock's rate is its parent's rate / @fixed_div
  * @flags: see "struct clk.flags possibilities" above
  * @clksel_reg: for clksel clks, register va containing src/divisor select
@@ -252,6 +253,7 @@ struct clk {
 	void			(*init)(struct clk *);
 	u8			enable_bit;
 	s8			usecount;
+	bool			autoidle;
 	u8			fixed_div;
 	u8			flags;
 #ifdef CONFIG_ARCH_OMAP2PLUS
Signature · 1 line
1.7.4.1
#6 lore raw

PATCH v6 3/11 ARM: OMAP3+: voltage: add support for voltagedomain usecounts

In reply to Tero Kristo

These are updated based on powerdomain usecounts. Also added support for voltdm->sleep and voltdm->wakeup calls that will be invoked once voltagedomain enters sleep or wakes up based on usecount numbers. These will be used for controlling voltage scaling functionality.

Cc Paul Walmsley <[email protected]>
Cc Kevin Hilman <[email protected]>
---
 arch/arm/mach-omap2/powerdomain.c |   17 +++++++++-
 arch/arm/mach-omap2/voltage.c     |   65 +++++++++++++++++++++++++++++++++++++
 arch/arm/mach-omap2/voltage.h     |   13 +++++++
 3 files changed, 94 insertions(+), 1 deletions(-)
arch/arm/mach-omap2/powerdomain.c +16 -1
index ba49029..abc50c2 100644
@@ -1475,10 +1475,17 @@ int pwrdm_state_switch(struct powerdomain *pwrdm)
  */
 void pwrdm_clkdm_enable(struct powerdomain *pwrdm)
 {
+	unsigned long flags;
+
 	if (!pwrdm)
 		return;
 
-	atomic_inc(&pwrdm->usecount);
+	spin_lock_irqsave(&pwrdm->lock, flags);
+
+	if (atomic_inc_return(&pwrdm->usecount) == 1)
+		voltdm_pwrdm_enable(pwrdm->voltdm.ptr);
+
+	spin_unlock_irqrestore(&pwrdm->lock, flags);
 }
 
 /**
@@ -1492,12 +1499,20 @@ void pwrdm_clkdm_enable(struct powerdomain *pwrdm)
 void pwrdm_clkdm_disable(struct powerdomain *pwrdm)
 {
 	int val;
+	unsigned long flags;
 
 	if (!pwrdm)
 		return;
 
+	spin_lock_irqsave(&pwrdm->lock, flags);
+
 	val = atomic_dec_return(&pwrdm->usecount);
 
+	if (!val)
+		voltdm_pwrdm_disable(pwrdm->voltdm.ptr);
+
+	spin_unlock_irqrestore(&pwrdm->lock, flags);
+
 	WARN_ON(val < 0);
 }
 
arch/arm/mach-omap2/voltage.c +65 -0

77 lines not on this page · raw message

arch/arm/mach-omap2/voltage.h +13 -0
index 0ac2caf..7f4f99d 100644
@@ -15,6 +15,7 @@
 #define __ARCH_ARM_MACH_OMAP2_VOLTAGE_H
 
 #include <linux/err.h>
+#include <linux/spinlock.h>
 
 #include <plat/voltage.h>
 
@@ -56,10 +57,14 @@ struct omap_vfsm_instance {
  * @pwrdm_list: list_head linking all powerdomains in this voltagedomain
  * @vc: pointer to VC channel associated with this voltagedomain
  * @vp: pointer to VP associated with this voltagedomain
+ * @usecount: number of users for this voltagedomain
+ * @target_state: calculated target state for the children of this domain
  * @read: read a VC/VP register
  * @write: write a VC/VP register
  * @read: read-modify-write a VC/VP register
  * @sys_clk: system clock name/frequency, used for various timing calculations
+ * @sleep: function to call once the domain enters idle
+ * @wakeup: function to call once the domain wakes up from idle
  * @scale: function used to scale the voltage of the voltagedomain
  * @nominal_volt: current nominal voltage for this voltage domain
  * @volt_data: voltage table having the distinct voltages supported
@@ -75,6 +80,9 @@ struct voltagedomain {
 	struct omap_vp_instance *vp;
 	struct omap_voltdm_pmic *pmic;
 
+	atomic_t usecount;
+	u8 target_state;
+
 	/* VC/VP register access functions: SoC specific */
 	u32 (*read) (u8 offset);
 	void (*write) (u32 val, u8 offset);
@@ -85,11 +93,14 @@ struct voltagedomain {
 		u32 rate;
 	} sys_clk;
 
+	void (*sleep) (struct voltagedomain *voltdm);
+	void (*wakeup) (struct voltagedomain *voltdm);
 	int (*scale) (struct voltagedomain *voltdm,
 		      unsigned long target_volt);
 
 	u32 nominal_volt;
 	struct omap_volt_data *volt_data;
+	spinlock_t lock;
 };
 
 /**
@@ -145,6 +156,8 @@ extern void omap44xx_voltagedomains_init(void);
 struct voltagedomain *voltdm_lookup(const char *name);
 void voltdm_init(struct voltagedomain **voltdm_list);
 int voltdm_add_pwrdm(struct voltagedomain *voltdm, struct powerdomain *pwrdm);
+void voltdm_pwrdm_enable(struct voltagedomain *voltdm);
+void voltdm_pwrdm_disable(struct voltagedomain *voltdm);
 int voltdm_for_each(int (*fn)(struct voltagedomain *voltdm, void *user),
 		    void *user);
 int voltdm_for_each_pwrdm(struct voltagedomain *voltdm,
Signature · 1 line
1.7.4.1
#7 lore raw

PATCH v6 4/11 ARM: OMAP3: add manual control for mpu / core pwrdm usecounting

In reply to Tero Kristo

mpu / core powerdomain usecounts are now statically increased by 1 during MPU activity. This allows the domains to reflect actual usage, and will allow the usecount to reach 0 just before all CPUs are ready to idle. Proper powerdomain usecounts are propageted to voltagedomain level also, and will allow vc callbacks to be triggered at right point of time.

Cc Paul Walmsley <[email protected]>
Cc Kevin Hilman <[email protected]>
---
 arch/arm/mach-omap2/common.h              |    6 ++
 arch/arm/mach-omap2/omap-mpuss-lowpower.c |   72 ++++++++++++++++++++++++++++-
 arch/arm/mach-omap2/omap-smp.c            |    2 +
 arch/arm/mach-omap2/pm34xx.c              |   21 ++++++++
 4 files changed, 100 insertions(+), 1 deletions(-)
arch/arm/mach-omap2/common.h +6 -0
index 6ea837a..a445a02 100644
@@ -292,6 +292,8 @@ extern int omap4_finish_suspend(unsigned long cpu_state);
 extern void omap4_cpu_resume(void);
 extern int omap4_hotplug_cpu(unsigned int cpu, unsigned int power_state);
 extern u32 omap4_mpuss_read_prev_context_state(void);
+extern void omap4_pm_cpu_online(void);
+extern void omap4_pm_cpu_offline(void);
 #else
 static inline int omap4_enter_lowpower(unsigned int cpu,
 					unsigned int power_state)
@@ -323,6 +325,10 @@ static inline u32 omap4_mpuss_read_prev_context_state(void)
 {
 	return 0;
 }
+
+static inline void omap4_pm_cpu_online(void) { }
+
+static inline void omap4_pm_cpu_offline(void) { }
 #endif
 
 struct omap_sdrc_params;
arch/arm/mach-omap2/omap-mpuss-lowpower.c +71 -1

117 lines not on this page · raw message

arch/arm/mach-omap2/omap-smp.c +2 -0
index b9738e2..f296e35 100644
@@ -71,6 +71,8 @@ void __cpuinit platform_secondary_init(unsigned int cpu)
 	 */
 	gic_secondary_init(0);
 
+	omap4_pm_cpu_online();
+
 	/*
 	 * Synchronise with the boot thread.
 	 */
arch/arm/mach-omap2/pm34xx.c +21 -0
index 5b6505f..7a423f7 100644
@@ -294,6 +294,14 @@ void omap_sram_idle(void)
 	omap3_intc_prepare_idle();
 
 	/*
+	 * CPU going offline, update pwrdm counters. Core pwrdm
+	 * count is decreased as MPU is using SDRAM which resides
+	 * under core pwrdm.
+	 */
+	pwrdm_clkdm_disable(mpu_pwrdm);
+	pwrdm_clkdm_disable(core_pwrdm);
+
+	/*
 	 * On EMU/HS devices ROM code restores a SRDC value
 	 * from scratchpad which has automatic self refresh on timeout
 	 * of AUTO_CNT = 1 enabled. This takes care of erratum ID i443.
@@ -324,6 +332,12 @@ void omap_sram_idle(void)
 	    core_next_state == PWRDM_FUNC_PWRST_OFF)
 		sdrc_write_reg(sdrc_pwr, SDRC_POWER);
 
+	/*
+	 * CPU is coming back online, increase pwrdm usecounts
+	 */
+	pwrdm_clkdm_enable(mpu_pwrdm);
+	pwrdm_clkdm_enable(core_pwrdm);
+
 	/* CORE */
 	if (core_next_state < PWRDM_FUNC_PWRST_ON) {
 		core_prev_state = pwrdm_read_prev_fpwrst(core_pwrdm);
@@ -718,6 +732,13 @@ int __init omap3_pm_init(void)
 	omap_pm_suspend = omap3_pm_suspend;
 #endif
 
+	/*
+	 * CPU online, increase pwrdm usecounts for MPU and CORE domains.
+	 * MPU is using memory subsystem which resides in CORE domain.
+	 */
+	pwrdm_clkdm_enable(mpu_pwrdm);
+	pwrdm_clkdm_enable(core_pwrdm);
+
 	arm_pm_idle = omap3_pm_idle;
 	omap3_idle_init();
 
Signature · 1 line
1.7.4.1
#8 lore raw

PATCH v6 5/11 ARM: OMAP3: set autoidle flag for sdrc_ick

In reply to Tero Kristo

sdrc_ick doesn't have autoidle flag on HW, but is always automatically idled. Thus mark the autoidle flag statically as true for it to reflect hardware behavior. The clock will no longer show as active in usecount dumps and will allow the voltdm->sleep / wakeup calls to work properly.

---
 arch/arm/mach-omap2/clock3xxx_data.c |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)
arch/arm/mach-omap2/clock3xxx_data.c +1 -0
index 83bed9a..1ddc7fc 100644
@@ -1739,6 +1739,7 @@ static struct clk sdrc_ick = {
 	.flags		= ENABLE_ON_INIT,
 	.clkdm_name	= "core_l3_clkdm",
 	.recalc		= &followparent_recalc,
+	.autoidle	= true,
 };
 
 static struct clk gpmc_fck = {
Signature · 1 line
1.7.4.1
#9 lore raw

PATCH v6 6/11 ARM: OMAP: clockdomain: add support for preventing autodep delete

In reply to Tero Kristo

Some clockdomains bug out if their autodeps are deleted before idle. This happens namely with OMAP3 PER domain, it will bug out if it doesn't have wakedeps enabled when it enters off-mode. This patch adds support for new flag 'CLKDM_NO_AUTODEP_DISABLE' which does this.

---
 arch/arm/mach-omap2/clockdomain.c |    3 +++
 arch/arm/mach-omap2/clockdomain.h |    4 ++++
 2 files changed, 7 insertions(+), 0 deletions(-)
arch/arm/mach-omap2/clockdomain.c +3 -0
index 7715353..4f01c8f 100644
@@ -201,6 +201,9 @@ void _clkdm_del_autodeps(struct clockdomain *clkdm)
 	if (!autodeps || clkdm->flags & CLKDM_NO_AUTODEPS)
 		return;
 
+	if (clkdm->flags & CLKDM_NO_AUTODEP_DISABLE)
+		return;
+
 	for (autodep = autodeps; autodep->clkdm.ptr; autodep++) {
 		if (IS_ERR(autodep->clkdm.ptr))
 			continue;
arch/arm/mach-omap2/clockdomain.h +4 -0
index 5601dc1..9b8733e 100644
@@ -34,6 +34,9 @@
  * CLKDM_ACTIVE_WITH_MPU: The PRCM guarantees that this clockdomain is
  *     active whenever the MPU is active.  True for interconnects and
  *     the WKUP clockdomains.
+ * CLKDM_NO_AUTODEP_DISABLE: Prevent clockdomain code from deleting autodeps.
+ *     Needed for PER domain on omap3, as it will bug out with off-mode if
+ *     wakedeps are removed.
  */
 #define CLKDM_CAN_FORCE_SLEEP			(1 << 0)
 #define CLKDM_CAN_FORCE_WAKEUP			(1 << 1)
@@ -41,6 +44,7 @@
 #define CLKDM_CAN_DISABLE_AUTO			(1 << 3)
 #define CLKDM_NO_AUTODEPS			(1 << 4)
 #define CLKDM_ACTIVE_WITH_MPU			(1 << 5)
+#define CLKDM_NO_AUTODEP_DISABLE		(1 << 6)
 
 #define CLKDM_CAN_HWSUP		(CLKDM_CAN_ENABLE_AUTO | CLKDM_CAN_DISABLE_AUTO)
 #define CLKDM_CAN_SWSUP		(CLKDM_CAN_FORCE_SLEEP | CLKDM_CAN_FORCE_WAKEUP)
Signature · 1 line
1.7.4.1
#10 lore raw

PATCH v6 7/11 ARM: OMAP3: do not delete per_clkdm autodeps during idle

In reply to Tero Kristo

Previously, PER clock domain was always enabled, as the usecounts for this domain incorrectly always showed positive value. On HW level though, the domain enters idle as it is set in HW supervised mode. Now, when the usecounts reflect real values, PER domain will be put to HWSUP sleep mode, which means its autodeps are deleted. Removing wakedeps for PER domain will cause multiple problems. First of all, coming back from idle, PER domain remains idle as the wakedeps have been disabled for the domain, and this causes a crash with the GPIO code, as the resume code attempts to access domain which is not active. Just enabling the interface clocks for the GPIO does not help, as they are autoidled and don't bring the domain out of idle. Secondly, there are multiple erratas for omap3, which say that the wakedeps should be enabled for the PER domain, see e.g. errata i582 for omap3630.

---
 arch/arm/mach-omap2/clockdomains3xxx_data.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)
arch/arm/mach-omap2/clockdomains3xxx_data.c +1 -1
index 56089c4..3b3c524 100644
@@ -370,7 +370,7 @@ static struct clockdomain usbhost_am35x_clkdm = {
 static struct clockdomain per_clkdm = {
 	.name		= "per_clkdm",
 	.pwrdm		= { .name = "per_pwrdm" },
-	.flags		= CLKDM_CAN_HWSUP_SWSUP,
+	.flags		= CLKDM_CAN_HWSUP_SWSUP | CLKDM_NO_AUTODEP_DISABLE,
 	.dep_bit	= OMAP3430_EN_PER_SHIFT,
 	.wkdep_srcs	= per_wkdeps,
 	.sleepdep_srcs	= per_sleepdeps,
Signature · 1 line
1.7.4.1
#11 lore raw

PATCH v6 8/11 ARM: OMAP4: clock data: set autoidle flag for dss_fck

In reply to Tero Kristo

dss_fck is currently being used improperly within the hwmod database as an interface clock for DSS hwmods. This causes the dss_fck to be enabled even if the dss powerdomain itself is idle, resulting in wrong data within the powerdomain usecounts. Marked dss_fck as autoidle which makes the clock to disappear from usecounts for now. This patch can be reverted once the interface clocks for DSS hwmods have been properly fixed.

---
 arch/arm/mach-omap2/clock44xx_data.c |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)
arch/arm/mach-omap2/clock44xx_data.c +1 -0
index d7f55e4..95834d7 100644
@@ -1408,6 +1408,7 @@ static struct clk dss_fck = {
 	.clkdm_name	= "l3_dss_clkdm",
 	.parent		= &l3_div_ck,
 	.recalc		= &followparent_recalc,
+	.autoidle	= true,
 };
 
 static struct clk efuse_ctrl_cust_fck = {
Signature · 1 line
1.7.4.1
#12 lore raw

PATCH v6 9/11 ARM: OMAP4: hwmod: add support for hwmod autoidle flag

In reply to Tero Kristo

If a hwmod is in HWAUTO mode, it will idle automatically and should not be accounted for in the clkdm / pwrdm usecounts. Thus, flag modules in such mode as autoidle during init, and ignore these in subsequent usecount calculations.

---
 arch/arm/mach-omap2/clockdomain.c            |    6 ++++++
 arch/arm/mach-omap2/omap_hwmod.c             |    3 +++
 arch/arm/plat-omap/include/plat/omap_hwmod.h |    2 ++
 3 files changed, 11 insertions(+), 0 deletions(-)
arch/arm/mach-omap2/clockdomain.c +6 -0
index 4f01c8f..af88ae5 100644
@@ -1063,6 +1063,9 @@ int clkdm_hwmod_enable(struct clockdomain *clkdm, struct omap_hwmod *oh)
 	if (!oh)
 		return -EINVAL;
 
+	if (oh->flags & HWMOD_AUTOIDLE)
+		return 0;
+
 	return _clkdm_clk_hwmod_enable(clkdm);
 }
 
@@ -1094,6 +1097,9 @@ int clkdm_hwmod_disable(struct clockdomain *clkdm, struct omap_hwmod *oh)
 	if (!oh)
 		return -EINVAL;
 
+	if (oh->flags & HWMOD_AUTOIDLE)
+		return 0;
+
 	return _clkdm_clk_hwmod_disable(clkdm);
 }
 
arch/arm/mach-omap2/omap_hwmod.c +3 -0
index f826917..6807b02 100644
@@ -2377,6 +2377,9 @@ static int __init _register(struct omap_hwmod *oh)
 	INIT_LIST_HEAD(&oh->slave_ports);
 	spin_lock_init(&oh->_lock);
 
+	if (cpu_is_omap44xx() && oh->prcm.omap4.modulemode == MODULEMODE_HWCTRL)
+		oh->flags |= HWMOD_AUTOIDLE;
+
 	oh->_state = _HWMOD_STATE_REGISTERED;
 
 	/*
arch/arm/plat-omap/include/plat/omap_hwmod.h +2 -0
index 0f840a9..36130f9 100644
@@ -440,6 +440,7 @@ struct omap_hwmod_omap4_prcm {
  *     in order to complete the reset. Optional clocks will be disabled
  *     again after the reset.
  * HWMOD_16BIT_REG: Module has 16bit registers
+ * HWMOD_AUTOIDLE: Module is controlled automatically by hardware
  */
 #define HWMOD_SWSUP_SIDLE			(1 << 0)
 #define HWMOD_SWSUP_MSTANDBY			(1 << 1)
@@ -450,6 +451,7 @@ struct omap_hwmod_omap4_prcm {
 #define HWMOD_NO_IDLEST				(1 << 6)
 #define HWMOD_CONTROL_OPT_CLKS_IN_RESET		(1 << 7)
 #define HWMOD_16BIT_REG				(1 << 8)
+#define HWMOD_AUTOIDLE				(1 << 9)
 
 /*
  * omap_hwmod._int_flags definitions
Signature · 1 line
1.7.4.1
#13 lore raw

PATCH v6 10/11 ARM: OMAP4: hwmod data: set mpu hwmod modulemode to hwauto

In reply to Tero Kristo

This makes sure it is handled as autoidle type by the usecounting.

---
 arch/arm/mach-omap2/omap_hwmod_44xx_data.c |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)
arch/arm/mach-omap2/omap_hwmod_44xx_data.c +1 -0
index 2d13b33..367de25 100644
@@ -2492,6 +2492,7 @@ static struct omap_hwmod omap44xx_mpu_hwmod = {
 		.omap4 = {
 			.clkctrl_offs = OMAP4_CM_MPU_MPU_CLKCTRL_OFFSET,
 			.context_offs = OMAP4_RM_MPU_MPU_CONTEXT_OFFSET,
+			.modulemode   = MODULEMODE_HWCTRL,
 		},
 	},
 };
Signature · 1 line
1.7.4.1
#14 lore raw

PATCH v6 11/11 ARM: OMAP4: clock data: flag hw controlled clocks as autoidle

In reply to Tero Kristo

This makes sure these clocks are ignored by the clkdm / pwrdm usecounting.

---
 arch/arm/mach-omap2/clock44xx_data.c |    5 ++++-
 1 files changed, 4 insertions(+), 1 deletions(-)
arch/arm/mach-omap2/clock44xx_data.c +4 -1
index 95834d7..d2fb4e8 100644
@@ -3356,8 +3356,11 @@ int __init omap4xxx_clk_init(void)
 	 */
 
 	for (c = omap44xx_clks; c < omap44xx_clks + ARRAY_SIZE(omap44xx_clks);
-									  c++)
+									  c++) {
+		if (c->lk.clk->enable_bit == OMAP4430_MODULEMODE_HWCTRL)
+			c->lk.clk->autoidle = true;
 		clk_preinit(c->lk.clk);
+	}
 
 	for (c = omap44xx_clks; c < omap44xx_clks + ARRAY_SIZE(omap44xx_clks);
 									  c++)
Signature · 1 line
1.7.4.1