Commit a1ad3b94 authored by Brian Norris's avatar Brian Norris Committed by Florian Fainelli

ARM: brcmstb: update CPU power management sequence

The automatic CPU power state machine for B15 CPUs does not work
reliably as-is. This patch implements a manual sequence in software to
replace it.

This was tested successfully with over 10,000 hotplug cycles of
something like this:

  echo 0 > /sys/devices/system/cpu/cpu1/online
  echo 1 > /sys/devices/system/cpu/cpu1/online

whereas the existing sequence often locks up after a few hundred cycles.

Fixes: 62639c2f ("ARM: brcmstb: reintroduce SMP support")
Acked-by: default avatarGregory Fong <gregory.0xf0@gmail.com>
Signed-off-by: default avatarBrian Norris <computersforpeace@gmail.com>
Signed-off-by: default avatarFlorian Fainelli <f.fainelli@gmail.com>
parent 97bf6af1
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/jiffies.h>
#include <linux/of_address.h> #include <linux/of_address.h>
#include <linux/of_platform.h> #include <linux/of_platform.h>
#include <linux/printk.h> #include <linux/printk.h>
...@@ -94,10 +95,35 @@ static u32 pwr_ctrl_rd(u32 cpu) ...@@ -94,10 +95,35 @@ static u32 pwr_ctrl_rd(u32 cpu)
return readl_relaxed(base); return readl_relaxed(base);
} }
static void pwr_ctrl_wr(u32 cpu, u32 val) static void pwr_ctrl_set(unsigned int cpu, u32 val, u32 mask)
{ {
void __iomem *base = pwr_ctrl_get_base(cpu); void __iomem *base = pwr_ctrl_get_base(cpu);
writel(val, base); writel((readl(base) & mask) | val, base);
}
static void pwr_ctrl_clr(unsigned int cpu, u32 val, u32 mask)
{
void __iomem *base = pwr_ctrl_get_base(cpu);
writel((readl(base) & mask) & ~val, base);
}
#define POLL_TMOUT_MS 500
static int pwr_ctrl_wait_tmout(unsigned int cpu, u32 set, u32 mask)
{
const unsigned long timeo = jiffies + msecs_to_jiffies(POLL_TMOUT_MS);
u32 tmp;
do {
tmp = pwr_ctrl_rd(cpu) & mask;
if (!set == !tmp)
return 0;
} while (time_before(jiffies, timeo));
tmp = pwr_ctrl_rd(cpu) & mask;
if (!set == !tmp)
return 0;
return -ETIMEDOUT;
} }
static void cpu_rst_cfg_set(u32 cpu, int set) static void cpu_rst_cfg_set(u32 cpu, int set)
...@@ -139,15 +165,22 @@ static void brcmstb_cpu_power_on(u32 cpu) ...@@ -139,15 +165,22 @@ static void brcmstb_cpu_power_on(u32 cpu)
* The secondary cores power was cut, so we must go through * The secondary cores power was cut, so we must go through
* power-on initialization. * power-on initialization.
*/ */
u32 tmp; pwr_ctrl_set(cpu, ZONE_MAN_ISO_CNTL_MASK, 0xffffff00);
pwr_ctrl_set(cpu, ZONE_MANUAL_CONTROL_MASK, -1);
pwr_ctrl_set(cpu, ZONE_RESERVED_1_MASK, -1);
/* Request zone power up */ pwr_ctrl_set(cpu, ZONE_MAN_MEM_PWR_MASK, -1);
pwr_ctrl_wr(cpu, ZONE_PWR_UP_REQ_MASK);
/* Wait for the power up FSM to complete */ if (pwr_ctrl_wait_tmout(cpu, 1, ZONE_MEM_PWR_STATE_MASK))
do { panic("ZONE_MEM_PWR_STATE_MASK set timeout");
tmp = pwr_ctrl_rd(cpu);
} while (!(tmp & ZONE_PWR_ON_STATE_MASK)); pwr_ctrl_set(cpu, ZONE_MAN_CLKEN_MASK, -1);
if (pwr_ctrl_wait_tmout(cpu, 1, ZONE_DPG_PWR_STATE_MASK))
panic("ZONE_DPG_PWR_STATE_MASK set timeout");
pwr_ctrl_clr(cpu, ZONE_MAN_ISO_CNTL_MASK, -1);
pwr_ctrl_set(cpu, ZONE_MAN_RESET_CNTL_MASK, -1);
} }
static int brcmstb_cpu_get_power_state(u32 cpu) static int brcmstb_cpu_get_power_state(u32 cpu)
...@@ -174,25 +207,33 @@ static void brcmstb_cpu_die(u32 cpu) ...@@ -174,25 +207,33 @@ static void brcmstb_cpu_die(u32 cpu)
static int brcmstb_cpu_kill(u32 cpu) static int brcmstb_cpu_kill(u32 cpu)
{ {
u32 tmp; /*
* Ordinarily, the hardware forbids power-down of CPU0 (which is good
* because it is the boot CPU), but this is not true when using BPCM
* manual mode. Consequently, we must avoid turning off CPU0 here to
* ensure that TI2C master reset will work.
*/
if (cpu == 0) {
pr_warn("SMP: refusing to power off CPU0\n");
return 1;
}
while (per_cpu_sw_state_rd(cpu)) while (per_cpu_sw_state_rd(cpu))
; ;
/* Program zone reset */ pwr_ctrl_set(cpu, ZONE_MANUAL_CONTROL_MASK, -1);
pwr_ctrl_wr(cpu, ZONE_RESET_STATE_MASK | ZONE_BLK_RST_ASSERT_MASK | pwr_ctrl_clr(cpu, ZONE_MAN_RESET_CNTL_MASK, -1);
ZONE_PWR_DN_REQ_MASK); pwr_ctrl_clr(cpu, ZONE_MAN_CLKEN_MASK, -1);
pwr_ctrl_set(cpu, ZONE_MAN_ISO_CNTL_MASK, -1);
pwr_ctrl_clr(cpu, ZONE_MAN_MEM_PWR_MASK, -1);
/* Verify zone reset */ if (pwr_ctrl_wait_tmout(cpu, 0, ZONE_MEM_PWR_STATE_MASK))
tmp = pwr_ctrl_rd(cpu); panic("ZONE_MEM_PWR_STATE_MASK clear timeout");
if (!(tmp & ZONE_RESET_STATE_MASK))
pr_err("%s: Zone reset bit for CPU %d not asserted!\n",
__func__, cpu);
/* Wait for power down */ pwr_ctrl_clr(cpu, ZONE_RESERVED_1_MASK, -1);
do {
tmp = pwr_ctrl_rd(cpu); if (pwr_ctrl_wait_tmout(cpu, 0, ZONE_DPG_PWR_STATE_MASK))
} while (!(tmp & ZONE_PWR_OFF_STATE_MASK)); panic("ZONE_DPG_PWR_STATE_MASK clear timeout");
/* Flush pipeline before resetting CPU */ /* Flush pipeline before resetting CPU */
mb(); mb();
......
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