Commit df63a57d authored by Ranjani Vaidyanathan's avatar Ranjani Vaidyanathan

ENGR00221440 MX6x-Fix race-condition in checking bus_freq variables

Checking of the bus_freq variables and changing of the
bus/ddr frequency should be done under one mutex.
Else there is a race-condition that the variable changed
just after it was checked.
Also ensure that the bus freq is always increased before
the cpu freq is set to anything other than the lowest setpoint.
Else there is a possibility that the ARM is set to run from
PLL1 at higher frequency when bus/DDR are still at 24MHz.
This is dangerous since when system enters WAIT mode in
low bus freq state, PLL1 is set to bypass when ARM is being
sourced from it.
Signed-off-by: default avatarRanjani Vaidyanathan <ra5478@freescale.com>
parent 0b04f438
...@@ -69,6 +69,7 @@ int bus_freq_scaling_is_active; ...@@ -69,6 +69,7 @@ int bus_freq_scaling_is_active;
int lp_high_freq; int lp_high_freq;
int lp_med_freq; int lp_med_freq;
int lp_audio_freq; int lp_audio_freq;
int high_cpu_freq;
unsigned int ddr_low_rate; unsigned int ddr_low_rate;
unsigned int ddr_med_rate; unsigned int ddr_med_rate;
unsigned int ddr_normal_rate; unsigned int ddr_normal_rate;
...@@ -194,7 +195,6 @@ static void reduce_bus_freq_handler(struct work_struct *work) ...@@ -194,7 +195,6 @@ static void reduce_bus_freq_handler(struct work_struct *work)
spin_unlock_irqrestore(&freq_lock, flags); spin_unlock_irqrestore(&freq_lock, flags);
} }
high_bus_freq_mode = 0; high_bus_freq_mode = 0;
mutex_unlock(&bus_freq_mutex); mutex_unlock(&bus_freq_mutex);
} }
...@@ -225,40 +225,34 @@ int set_high_bus_freq(int high_bus_freq) ...@@ -225,40 +225,34 @@ int set_high_bus_freq(int high_bus_freq)
{ {
if (bus_freq_scaling_initialized && bus_freq_scaling_is_active) if (bus_freq_scaling_initialized && bus_freq_scaling_is_active)
cancel_delayed_work_sync(&low_bus_freq_handler); cancel_delayed_work_sync(&low_bus_freq_handler);
mutex_lock(&bus_freq_mutex);
if (busfreq_suspended) { if (busfreq_suspended)
mutex_unlock(&bus_freq_mutex);
return 0; return 0;
}
if (!bus_freq_scaling_initialized || !bus_freq_scaling_is_active) {
mutex_unlock(&bus_freq_mutex); if (!bus_freq_scaling_initialized || !bus_freq_scaling_is_active)
return 0; return 0;
}
if (high_bus_freq_mode && high_bus_freq) {
mutex_unlock(&bus_freq_mutex); if (high_bus_freq_mode && high_bus_freq)
return 0; return 0;
}
if (med_bus_freq_mode && !high_bus_freq) {
mutex_unlock(&bus_freq_mutex); /* medium bus freq is only supported for MX6DQ */
if (cpu_is_mx6q() && med_bus_freq_mode && !high_bus_freq)
return 0; return 0;
}
if (cpu_is_mx6dl() && high_bus_freq) if (cpu_is_mx6dl() && high_bus_freq)
high_bus_freq = 0; high_bus_freq = 0;
if (cpu_is_mx6dl() && med_bus_freq_mode) { if (cpu_is_mx6dl() && med_bus_freq_mode)
mutex_unlock(&bus_freq_mutex);
return 0; return 0;
}
if ((high_bus_freq_mode && (high_bus_freq || lp_high_freq)) || if ((high_bus_freq_mode && (high_bus_freq || lp_high_freq)) ||
(med_bus_freq_mode && !high_bus_freq && lp_med_freq && (med_bus_freq_mode && !high_bus_freq && lp_med_freq &&
!lp_high_freq)) { !lp_high_freq))
mutex_unlock(&bus_freq_mutex);
return 0; return 0;
}
if (cpu_is_mx6sl()) { if (cpu_is_mx6sl()) {
u32 reg; u32 reg;
unsigned long flags; unsigned long flags;
...@@ -321,8 +315,6 @@ int set_high_bus_freq(int high_bus_freq) ...@@ -321,8 +315,6 @@ int set_high_bus_freq(int high_bus_freq)
clk_disable(pll3); clk_disable(pll3);
} }
mutex_unlock(&bus_freq_mutex);
return 0; return 0;
} }
...@@ -334,11 +326,8 @@ int low_freq_bus_used(void) ...@@ -334,11 +326,8 @@ int low_freq_bus_used(void)
/* We only go the lowest setpoint if ARM is also /* We only go the lowest setpoint if ARM is also
* at the lowest setpoint. * at the lowest setpoint.
*/ */
if ((clk_get_rate(cpu_clk) > if (high_cpu_freq)
cpu_op_tbl[cpu_op_nr - 1].cpu_rate)
|| (cpu_op_nr == 1)) {
return 0; return 0;
}
if ((lp_high_freq == 0) if ((lp_high_freq == 0)
&& (lp_med_freq == 0)) && (lp_med_freq == 0))
...@@ -350,62 +339,80 @@ int low_freq_bus_used(void) ...@@ -350,62 +339,80 @@ int low_freq_bus_used(void)
void bus_freq_update(struct clk *clk, bool flag) void bus_freq_update(struct clk *clk, bool flag)
{ {
mutex_lock(&bus_freq_mutex); mutex_lock(&bus_freq_mutex);
if (flag) { if (flag) {
/* Update count */ if (clk == cpu_clk) {
if (clk->flags & AHB_HIGH_SET_POINT) /* The CPU freq is being increased.
lp_high_freq++; * check if we need to increase the bus freq
else if (clk->flags & AHB_MED_SET_POINT) */
lp_med_freq++; high_cpu_freq = 1;
else if (clk->flags & AHB_AUDIO_SET_POINT) if (low_bus_freq_mode || audio_bus_freq_mode)
lp_audio_freq++;
/* Update bus freq */
if ((clk->flags & CPU_FREQ_TRIG_UPDATE)
&& (clk_get_usecount(clk) == 0)) {
if (!(clk->flags &
(AHB_HIGH_SET_POINT | AHB_MED_SET_POINT))) {
if (low_freq_bus_used()) {
if ((clk->flags & AHB_AUDIO_SET_POINT) & !audio_bus_freq_mode)
set_low_bus_freq();
else if (!low_bus_freq_mode)
set_low_bus_freq();
}
} else {
if ((clk->flags & AHB_MED_SET_POINT)
&& !med_bus_freq_mode) {
/* Set to Medium setpoint */
mutex_unlock(&bus_freq_mutex);
set_high_bus_freq(0); set_high_bus_freq(0);
return; } else {
} /* Update count */
else if ((clk->flags & AHB_HIGH_SET_POINT) if (clk->flags & AHB_HIGH_SET_POINT)
&& !high_bus_freq_mode) { lp_high_freq++;
/* Currently at low or medium set point, else if (clk->flags & AHB_MED_SET_POINT)
* need to set to high setpoint lp_med_freq++;
*/ else if (clk->flags & AHB_AUDIO_SET_POINT)
mutex_unlock(&bus_freq_mutex); lp_audio_freq++;
set_high_bus_freq(1); /* Update bus freq */
return; if ((clk->flags & CPU_FREQ_TRIG_UPDATE)
} && (clk_get_usecount(clk) == 0)) {
if (!(clk->flags &
(AHB_HIGH_SET_POINT | AHB_MED_SET_POINT))) {
if (low_freq_bus_used()) {
if ((clk->flags & AHB_AUDIO_SET_POINT) &
!audio_bus_freq_mode)
set_low_bus_freq();
else if (!low_bus_freq_mode)
set_low_bus_freq();
}
} else {
if ((clk->flags & AHB_MED_SET_POINT)
&& !med_bus_freq_mode) {
/* Set to Medium setpoint */
set_high_bus_freq(0);
} else if ((clk->flags & AHB_HIGH_SET_POINT)
&& !high_bus_freq_mode) {
/* Currently at low or medium
* set point, need to set to
* high setpoint
*/
set_high_bus_freq(1);
}
}
} }
} }
} else { } else {
/* Update count */ if (clk == cpu_clk) {
if (clk->flags & AHB_HIGH_SET_POINT) /* CPU freq is dropped, check if we can
lp_high_freq--; * lower the bus freq.
else if (clk->flags & AHB_MED_SET_POINT) */
lp_med_freq--; high_cpu_freq = 0;
else if (clk->flags & AHB_AUDIO_SET_POINT)
lp_audio_freq--; if (low_freq_bus_used() &&
/* Update bus freq */ !(low_bus_freq_mode || audio_bus_freq_mode))
if ((clk->flags & CPU_FREQ_TRIG_UPDATE)
&& (clk_get_usecount(clk) == 0)) {
if (low_freq_bus_used() && !low_bus_freq_mode)
set_low_bus_freq(); set_low_bus_freq();
else { } else {
/* Set to either high or medium setpoint. */ /* Update count */
mutex_unlock(&bus_freq_mutex); if (clk->flags & AHB_HIGH_SET_POINT)
set_high_bus_freq(0); lp_high_freq--;
return; else if (clk->flags & AHB_MED_SET_POINT)
lp_med_freq--;
else if (clk->flags & AHB_AUDIO_SET_POINT)
lp_audio_freq--;
/* Update bus freq */
if ((clk->flags & CPU_FREQ_TRIG_UPDATE)
&& (clk_get_usecount(clk) == 0)) {
if (low_freq_bus_used())
set_low_bus_freq();
else {
/* Set to either high or
* medium setpoint.
*/
set_high_bus_freq(0);
}
} }
} }
} }
......
...@@ -472,7 +472,6 @@ static int _clk_pll_enable(struct clk *clk) ...@@ -472,7 +472,6 @@ static int _clk_pll_enable(struct clk *clk)
pllbase = _get_pll_base(clk); pllbase = _get_pll_base(clk);
reg = __raw_readl(pllbase); reg = __raw_readl(pllbase);
reg &= ~ANADIG_PLL_BYPASS;
reg &= ~ANADIG_PLL_POWER_DOWN; reg &= ~ANADIG_PLL_POWER_DOWN;
/* The 480MHz PLLs have the opposite definition for power bit. */ /* The 480MHz PLLs have the opposite definition for power bit. */
...@@ -492,6 +491,7 @@ static int _clk_pll_enable(struct clk *clk) ...@@ -492,6 +491,7 @@ static int _clk_pll_enable(struct clk *clk)
/* Enable the PLL output now*/ /* Enable the PLL output now*/
reg = __raw_readl(pllbase); reg = __raw_readl(pllbase);
reg &= ~ANADIG_PLL_BYPASS;
reg |= ANADIG_PLL_ENABLE; reg |= ANADIG_PLL_ENABLE;
__raw_writel(reg, pllbase); __raw_writel(reg, pllbase);
...@@ -1262,14 +1262,17 @@ static int _clk_arm_set_rate(struct clk *clk, unsigned long rate) ...@@ -1262,14 +1262,17 @@ static int _clk_arm_set_rate(struct clk *clk, unsigned long rate)
*/ */
if (pll1_sw_clk.parent != &pll2_pfd_400M) { if (pll1_sw_clk.parent != &pll2_pfd_400M) {
pll2_pfd_400M.enable(&pll2_pfd_400M); pll2_pfd_400M.enable(&pll2_pfd_400M);
pll2_pfd_400M.usecount++;
arm_needs_pll2_400 = true; arm_needs_pll2_400 = true;
pll1_sw_clk.set_parent(&pll1_sw_clk, &pll2_pfd_400M); pll1_sw_clk.set_parent(&pll1_sw_clk, &pll2_pfd_400M);
pll1_sw_clk.parent = &pll2_pfd_400M; pll1_sw_clk.parent = &pll2_pfd_400M;
} }
} else { } else {
/* Make sure PLL1 is enabled */ /* Make sure PLL1 is enabled */
if (!pll1_enabled) if (!pll1_enabled) {
pll1_sys_main_clk.enable(&pll1_sys_main_clk); pll1_sys_main_clk.enable(&pll1_sys_main_clk);
pll1_sys_main_clk.usecount = 1;
}
/* Make sure PLL1 rate is what we want */ /* Make sure PLL1 rate is what we want */
if (cpu_op_tbl[i].pll_rate != clk_get_rate(&pll1_sys_main_clk)) { if (cpu_op_tbl[i].pll_rate != clk_get_rate(&pll1_sys_main_clk)) {
/* If pll1_sw_clk is from pll1_sys_main_clk, switch it */ /* If pll1_sw_clk is from pll1_sys_main_clk, switch it */
...@@ -1285,6 +1288,8 @@ static int _clk_arm_set_rate(struct clk *clk, unsigned long rate) ...@@ -1285,6 +1288,8 @@ static int _clk_arm_set_rate(struct clk *clk, unsigned long rate)
/* Make sure pll1_sw_clk is from pll1_sys_main_clk */ /* Make sure pll1_sw_clk is from pll1_sys_main_clk */
pll1_sw_clk.set_parent(&pll1_sw_clk, &pll1_sys_main_clk); pll1_sw_clk.set_parent(&pll1_sw_clk, &pll1_sys_main_clk);
pll1_sw_clk.parent = &pll1_sys_main_clk; pll1_sw_clk.parent = &pll1_sys_main_clk;
if (arm_needs_pll2_400)
pll2_pfd_400M.usecount--;
arm_needs_pll2_400 = false; arm_needs_pll2_400 = false;
if (pll2_pfd_400M.usecount == 0) if (pll2_pfd_400M.usecount == 0)
pll2_pfd_400M.disable(&pll2_pfd_400M); pll2_pfd_400M.disable(&pll2_pfd_400M);
...@@ -1323,8 +1328,10 @@ static int _clk_arm_set_rate(struct clk *clk, unsigned long rate) ...@@ -1323,8 +1328,10 @@ static int _clk_arm_set_rate(struct clk *clk, unsigned long rate)
while (__raw_readl(MXC_CCM_CDHIPR)) while (__raw_readl(MXC_CCM_CDHIPR))
; ;
if (pll1_sys_main_clk.usecount == 1 && arm_needs_pll2_400) if (pll1_sys_main_clk.usecount == 1 && arm_needs_pll2_400) {
pll1_sys_main_clk.disable(&pll1_sys_main_clk); pll1_sys_main_clk.disable(&pll1_sys_main_clk);
pll1_sys_main_clk.usecount = 0;
}
spin_unlock_irqrestore(&clk_lock, flags); spin_unlock_irqrestore(&clk_lock, flags);
......
...@@ -67,6 +67,7 @@ static struct clk pll7_usb_host_main_clk; ...@@ -67,6 +67,7 @@ static struct clk pll7_usb_host_main_clk;
static struct clk usdhc3_clk; static struct clk usdhc3_clk;
static struct clk ipg_clk; static struct clk ipg_clk;
static struct clk gpt_clk[]; static struct clk gpt_clk[];
static struct clk ahb_clk;
static struct cpu_op *cpu_op_tbl; static struct cpu_op *cpu_op_tbl;
static int cpu_op_nr; static int cpu_op_nr;
...@@ -412,6 +413,7 @@ static int _clk_pfd_enable(struct clk *clk) ...@@ -412,6 +413,7 @@ static int _clk_pfd_enable(struct clk *clk)
__raw_writel((1 << (clk->enable_shift + 7)), __raw_writel((1 << (clk->enable_shift + 7)),
(int)clk->enable_reg + 8); (int)clk->enable_reg + 8);
udelay(3);
return 0; return 0;
} }
...@@ -430,7 +432,6 @@ static int _clk_pll_enable(struct clk *clk) ...@@ -430,7 +432,6 @@ static int _clk_pll_enable(struct clk *clk)
pllbase = _get_pll_base(clk); pllbase = _get_pll_base(clk);
reg = __raw_readl(pllbase); reg = __raw_readl(pllbase);
reg &= ~ANADIG_PLL_BYPASS;
reg &= ~ANADIG_PLL_POWER_DOWN; reg &= ~ANADIG_PLL_POWER_DOWN;
/* The 480MHz PLLs have the opposite definition for power bit. */ /* The 480MHz PLLs have the opposite definition for power bit. */
...@@ -450,6 +451,7 @@ static int _clk_pll_enable(struct clk *clk) ...@@ -450,6 +451,7 @@ static int _clk_pll_enable(struct clk *clk)
/* Enable the PLL output now*/ /* Enable the PLL output now*/
reg = __raw_readl(pllbase); reg = __raw_readl(pllbase);
reg &= ~ANADIG_PLL_BYPASS;
reg |= ANADIG_PLL_ENABLE; reg |= ANADIG_PLL_ENABLE;
__raw_writel(reg, pllbase); __raw_writel(reg, pllbase);
...@@ -1153,6 +1155,11 @@ static int _clk_arm_set_rate(struct clk *clk, unsigned long rate) ...@@ -1153,6 +1155,11 @@ static int _clk_arm_set_rate(struct clk *clk, unsigned long rate)
if (i >= cpu_op_nr) if (i >= cpu_op_nr)
return -EINVAL; return -EINVAL;
if (clk_get_rate(&ahb_clk) == 24000000) {
printk(KERN_INFO "we should not be here!!!!! AHB is at 24MHz....cpu_rate requested = %ld\n", rate);
dump_stack();
BUG();
}
spin_lock_irqsave(&mx6sl_clk_lock, flags); spin_lock_irqsave(&mx6sl_clk_lock, flags);
if (rate <= clk_get_rate(&pll2_pfd2_400M)) { if (rate <= clk_get_rate(&pll2_pfd2_400M)) {
...@@ -1161,14 +1168,17 @@ static int _clk_arm_set_rate(struct clk *clk, unsigned long rate) ...@@ -1161,14 +1168,17 @@ static int _clk_arm_set_rate(struct clk *clk, unsigned long rate)
*/ */
if (pll1_sw_clk.parent != &pll2_pfd2_400M) { if (pll1_sw_clk.parent != &pll2_pfd2_400M) {
pll2_pfd2_400M.enable(&pll2_pfd2_400M); pll2_pfd2_400M.enable(&pll2_pfd2_400M);
pll2_pfd2_400M.usecount++;
arm_needs_pll2_400 = true; arm_needs_pll2_400 = true;
pll1_sw_clk.set_parent(&pll1_sw_clk, &pll2_pfd2_400M); pll1_sw_clk.set_parent(&pll1_sw_clk, &pll2_pfd2_400M);
pll1_sw_clk.parent = &pll2_pfd2_400M; pll1_sw_clk.parent = &pll2_pfd2_400M;
} }
} else { } else {
/* Make sure PLL1 is enabled */ /* Make sure PLL1 is enabled */
if (!pll1_enabled) if (!pll1_enabled) {
pll1_sys_main_clk.enable(&pll1_sys_main_clk); pll1_sys_main_clk.enable(&pll1_sys_main_clk);
pll1_sys_main_clk.usecount = 1;
}
if (cpu_op_tbl[i].pll_rate != clk_get_rate(&pll1_sys_main_clk)) { if (cpu_op_tbl[i].pll_rate != clk_get_rate(&pll1_sys_main_clk)) {
if (pll1_sw_clk.parent == &pll1_sys_main_clk) { if (pll1_sw_clk.parent == &pll1_sys_main_clk) {
/* Change the PLL1 rate. */ /* Change the PLL1 rate. */
...@@ -1177,15 +1187,13 @@ static int _clk_arm_set_rate(struct clk *clk, unsigned long rate) ...@@ -1177,15 +1187,13 @@ static int _clk_arm_set_rate(struct clk *clk, unsigned long rate)
else else
pll1_sw_clk.set_parent(&pll1_sw_clk, &osc_clk); pll1_sw_clk.set_parent(&pll1_sw_clk, &osc_clk);
} }
if (cpu_op_tbl[i].cpu_podf) {
__raw_writel(cpu_op_tbl[i].cpu_podf, MXC_CCM_CACRR);
while (__raw_readl(MXC_CCM_CDHIPR))
;
}
pll1_sys_main_clk.set_rate(&pll1_sys_main_clk, cpu_op_tbl[i].pll_rate); pll1_sys_main_clk.set_rate(&pll1_sys_main_clk, cpu_op_tbl[i].pll_rate);
} }
pll1_sw_clk.set_parent(&pll1_sw_clk, &pll1_sys_main_clk); pll1_sw_clk.set_parent(&pll1_sw_clk, &pll1_sys_main_clk);
pll1_sw_clk.parent = &pll1_sys_main_clk; pll1_sw_clk.parent = &pll1_sys_main_clk;
if (arm_needs_pll2_400)
pll2_pfd2_400M.usecount--;
arm_needs_pll2_400 = false; arm_needs_pll2_400 = false;
if (pll2_pfd2_400M.usecount == 0) if (pll2_pfd2_400M.usecount == 0)
pll2_pfd2_400M.disable(&pll2_pfd2_400M); pll2_pfd2_400M.disable(&pll2_pfd2_400M);
...@@ -1224,9 +1232,10 @@ static int _clk_arm_set_rate(struct clk *clk, unsigned long rate) ...@@ -1224,9 +1232,10 @@ static int _clk_arm_set_rate(struct clk *clk, unsigned long rate)
while (__raw_readl(MXC_CCM_CDHIPR)) while (__raw_readl(MXC_CCM_CDHIPR))
; ;
if (pll1_sys_main_clk.usecount == 1 && arm_needs_pll2_400) if (pll1_sys_main_clk.usecount == 1 && arm_needs_pll2_400) {
pll1_sys_main_clk.disable(&pll1_sys_main_clk); pll1_sys_main_clk.disable(&pll1_sys_main_clk);
pll1_sys_main_clk.usecount = 0;
}
spin_unlock_irqrestore(&mx6sl_clk_lock, flags); spin_unlock_irqrestore(&mx6sl_clk_lock, flags);
return 0; return 0;
......
...@@ -58,13 +58,7 @@ extern struct regulator *soc_regulator; ...@@ -58,13 +58,7 @@ extern struct regulator *soc_regulator;
extern struct regulator *pu_regulator; extern struct regulator *pu_regulator;
extern int dvfs_core_is_active; extern int dvfs_core_is_active;
extern struct cpu_op *(*get_cpu_op)(int *op); extern struct cpu_op *(*get_cpu_op)(int *op);
extern int low_bus_freq_mode; extern void bus_freq_update(struct clk *clk, bool flag);
extern int audio_bus_freq_mode;
extern int high_bus_freq_mode;
extern int set_low_bus_freq(void);
extern int set_high_bus_freq(int high_bus_speed);
extern int low_freq_bus_used(void);
extern struct mutex bus_freq_mutex;
int set_cpu_freq(int freq) int set_cpu_freq(int freq)
{ {
...@@ -95,12 +89,9 @@ int set_cpu_freq(int freq) ...@@ -95,12 +89,9 @@ int set_cpu_freq(int freq)
#endif #endif
/*Set the voltage for the GP domain. */ /*Set the voltage for the GP domain. */
if (freq > org_cpu_rate) { if (freq > org_cpu_rate) {
mutex_lock(&bus_freq_mutex); /* Check if the bus freq needs to be increased first */
if (low_bus_freq_mode || audio_bus_freq_mode) { bus_freq_update(cpu_clk, true);
mutex_unlock(&bus_freq_mutex);
set_high_bus_freq(0);
} else
mutex_unlock(&bus_freq_mutex);
if (freq == cpu_op_tbl[0].cpu_rate && !IS_ERR(soc_regulator) && !IS_ERR(pu_regulator)) { if (freq == cpu_op_tbl[0].cpu_rate && !IS_ERR(soc_regulator) && !IS_ERR(pu_regulator)) {
ret = regulator_set_voltage(soc_regulator, soc_volt, ret = regulator_set_voltage(soc_regulator, soc_volt,
soc_volt); soc_volt);
...@@ -152,11 +143,8 @@ int set_cpu_freq(int freq) ...@@ -152,11 +143,8 @@ int set_cpu_freq(int freq)
} }
soc_regulator_set = 0; soc_regulator_set = 0;
} }
mutex_lock(&bus_freq_mutex); /* Check if the bus freq can be decreased.*/
if (low_freq_bus_used() && bus_freq_update(cpu_clk, false);
!(low_bus_freq_mode || audio_bus_freq_mode))
set_low_bus_freq();
mutex_unlock(&bus_freq_mutex);
} }
return ret; return ret;
......
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