summaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-bcm
diff options
context:
space:
mode:
authorBrian Norris <computersforpeace@gmail.com>2014-12-16 19:13:50 -0800
committerFlorian Fainelli <f.fainelli@gmail.com>2015-01-12 14:59:29 -0800
commita1ad3b94a7661b643fef2efbc6fc217bd148f462 (patch)
treea4521ca95d0be701ab8ee4c92d5e0bcbaa0e1414 /arch/arm/mach-bcm
parent97bf6af1f928216fd6c5a66e8a57bfa95a659672 (diff)
downloadlinux-a1ad3b94a7661b643fef2efbc6fc217bd148f462.tar.gz
linux-a1ad3b94a7661b643fef2efbc6fc217bd148f462.tar.bz2
linux-a1ad3b94a7661b643fef2efbc6fc217bd148f462.zip
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: 62639c2f5332 ("ARM: brcmstb: reintroduce SMP support") Acked-by: Gregory Fong <gregory.0xf0@gmail.com> Signed-off-by: Brian Norris <computersforpeace@gmail.com> Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
Diffstat (limited to 'arch/arm/mach-bcm')
-rw-r--r--arch/arm/mach-bcm/platsmp-brcmstb.c85
1 files changed, 63 insertions, 22 deletions
diff --git a/arch/arm/mach-bcm/platsmp-brcmstb.c b/arch/arm/mach-bcm/platsmp-brcmstb.c
index 31c87a284a34..e209e6fc7caf 100644
--- a/arch/arm/mach-bcm/platsmp-brcmstb.c
+++ b/arch/arm/mach-bcm/platsmp-brcmstb.c
@@ -17,6 +17,7 @@
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/io.h>
+#include <linux/jiffies.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/printk.h>
@@ -94,10 +95,35 @@ static u32 pwr_ctrl_rd(u32 cpu)
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);
- 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)
@@ -139,15 +165,22 @@ static void brcmstb_cpu_power_on(u32 cpu)
* The secondary cores power was cut, so we must go through
* 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_wr(cpu, ZONE_PWR_UP_REQ_MASK);
+ pwr_ctrl_set(cpu, ZONE_MAN_MEM_PWR_MASK, -1);
- /* Wait for the power up FSM to complete */
- do {
- tmp = pwr_ctrl_rd(cpu);
- } while (!(tmp & ZONE_PWR_ON_STATE_MASK));
+ if (pwr_ctrl_wait_tmout(cpu, 1, ZONE_MEM_PWR_STATE_MASK))
+ panic("ZONE_MEM_PWR_STATE_MASK set timeout");
+
+ 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)
@@ -174,25 +207,33 @@ static void brcmstb_cpu_die(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))
;
- /* Program zone reset */
- pwr_ctrl_wr(cpu, ZONE_RESET_STATE_MASK | ZONE_BLK_RST_ASSERT_MASK |
- ZONE_PWR_DN_REQ_MASK);
+ pwr_ctrl_set(cpu, ZONE_MANUAL_CONTROL_MASK, -1);
+ pwr_ctrl_clr(cpu, ZONE_MAN_RESET_CNTL_MASK, -1);
+ 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 */
- tmp = pwr_ctrl_rd(cpu);
- if (!(tmp & ZONE_RESET_STATE_MASK))
- pr_err("%s: Zone reset bit for CPU %d not asserted!\n",
- __func__, cpu);
+ if (pwr_ctrl_wait_tmout(cpu, 0, ZONE_MEM_PWR_STATE_MASK))
+ panic("ZONE_MEM_PWR_STATE_MASK clear timeout");
- /* Wait for power down */
- do {
- tmp = pwr_ctrl_rd(cpu);
- } while (!(tmp & ZONE_PWR_OFF_STATE_MASK));
+ pwr_ctrl_clr(cpu, ZONE_RESERVED_1_MASK, -1);
+
+ if (pwr_ctrl_wait_tmout(cpu, 0, ZONE_DPG_PWR_STATE_MASK))
+ panic("ZONE_DPG_PWR_STATE_MASK clear timeout");
/* Flush pipeline before resetting CPU */
mb();