ARM: zynq: Synchronise zynq_cpu_die/kill
authorSoren Brinkmann <soren.brinkmann@xilinx.com>
Tue, 2 Sep 2014 21:19:12 +0000 (14:19 -0700)
committerMichal Simek <michal.simek@xilinx.com>
Tue, 16 Sep 2014 10:55:09 +0000 (12:55 +0200)
Avoid races and add synchronisation between the arch specific
kill and die routines.

The same synchronisation issue was fixed on IMX platform
by this commit:
"ARM: imx: fix sync issue between imx_cpu_die and imx_cpu_kill"
(sha1: 2f3edfd7e27ad4206acbc2ae99c9df5f46353024)

Signed-off-by: Soren Brinkmann <soren.brinkmann@xilinx.com>
Signed-off-by: Michal Simek <michal.simek@xilinx.com>
arch/arm/mach-zynq/common.h
arch/arm/mach-zynq/hotplug.c
arch/arm/mach-zynq/platsmp.c
arch/arm/mach-zynq/slcr.c

index 0edbb6997b1cc2aa794cb47fe2275a785cf53315..24d6340d3fb6de4b415d9ee7c55e2c5bf019269b 100644 (file)
@@ -24,6 +24,8 @@ extern int zynq_early_slcr_init(void);
 extern void zynq_slcr_system_reset(void);
 extern void zynq_slcr_cpu_stop(int cpu);
 extern void zynq_slcr_cpu_start(int cpu);
+extern bool zynq_slcr_cpu_state_read(int cpu);
+extern void zynq_slcr_cpu_state_write(int cpu, bool die);
 extern u32 zynq_slcr_get_device_id(void);
 
 #ifdef CONFIG_SMP
index 366f46c913658f249bc93927b40b388489d8075c..fe44a05677e214d306b61cf145714ec8a8184b36 100644 (file)
@@ -19,6 +19,8 @@
  */
 void zynq_platform_cpu_die(unsigned int cpu)
 {
+       zynq_slcr_cpu_state_write(cpu, true);
+
        /*
         * there is no power-control hardware on this platform, so all
         * we can do is put the core into WFI; this is safe as the calling
index 6c7843108c7f58f202d974111de21628af1f2078..06415eeba7e6fb118cd1d868a5e45ad10af0dcba 100644 (file)
@@ -127,6 +127,12 @@ static void zynq_secondary_init(unsigned int cpu)
 #ifdef CONFIG_HOTPLUG_CPU
 static int zynq_cpu_kill(unsigned cpu)
 {
+       unsigned long timeout = jiffies + msecs_to_jiffies(50);
+
+       while (zynq_slcr_cpu_state_read(cpu))
+               if (time_after(jiffies, timeout))
+                       return 0;
+
        zynq_slcr_cpu_stop(cpu);
        return 1;
 }
index c43a2d16e223bcfd74eca29eb6200c3bc93ff94d..d4cb50cf97c027dd5aed93b7900ae9bc96a7eed8 100644 (file)
@@ -138,6 +138,8 @@ void zynq_slcr_cpu_start(int cpu)
        zynq_slcr_write(reg, SLCR_A9_CPU_RST_CTRL_OFFSET);
        reg &= ~(SLCR_A9_CPU_CLKSTOP << cpu);
        zynq_slcr_write(reg, SLCR_A9_CPU_RST_CTRL_OFFSET);
+
+       zynq_slcr_cpu_state_write(cpu, false);
 }
 
 /**
@@ -154,8 +156,47 @@ void zynq_slcr_cpu_stop(int cpu)
 }
 
 /**
- * zynq_slcr_init - Regular slcr driver init
+ * zynq_slcr_cpu_state - Read/write cpu state
+ * @cpu:       cpu number
  *
+ * SLCR_REBOOT_STATUS save upper 2 bits (31/30 cpu states for cpu0 and cpu1)
+ * 0 means cpu is running, 1 cpu is going to die.
+ *
+ * Return: true if cpu is running, false if cpu is going to die
+ */
+bool zynq_slcr_cpu_state_read(int cpu)
+{
+       u32 state;
+
+       state = readl(zynq_slcr_base + SLCR_REBOOT_STATUS_OFFSET);
+       state &= 1 << (31 - cpu);
+
+       return !state;
+}
+
+/**
+ * zynq_slcr_cpu_state - Read/write cpu state
+ * @cpu:       cpu number
+ * @die:       cpu state - true if cpu is going to die
+ *
+ * SLCR_REBOOT_STATUS save upper 2 bits (31/30 cpu states for cpu0 and cpu1)
+ * 0 means cpu is running, 1 cpu is going to die.
+ */
+void zynq_slcr_cpu_state_write(int cpu, bool die)
+{
+       u32 state, mask;
+
+       state = readl(zynq_slcr_base + SLCR_REBOOT_STATUS_OFFSET);
+       mask = 1 << (31 - cpu);
+       if (die)
+               state |= mask;
+       else
+               state &= ~mask;
+       writel(state, zynq_slcr_base + SLCR_REBOOT_STATUS_OFFSET);
+}
+
+/**
+ * zynq_slcr_init - Regular slcr driver init
  * Return:     0 on success, negative errno otherwise.
  *
  * Called early during boot from platform code to remap SLCR area.