ARM: EXYNOS: add exynos_get_boot_addr() helper
[deliverable/linux.git] / arch / arm / mach-exynos / platsmp.c
index d2e9f12d12f187e1e2c40f11748f75bbe0a283f2..449edd1121e4d85a15b5e5e8ff40804407b480f5 100644 (file)
 
 extern void exynos4_secondary_startup(void);
 
-/*
- * Set or clear the USE_DELAYED_RESET_ASSERTION option, set on Exynos4 SoCs
- * during hot-(un)plugging CPUx.
- *
- * The feature can be cleared safely during first boot of secondary CPU.
- *
- * Exynos4 SoCs require setting USE_DELAYED_RESET_ASSERTION during powering
- * down a CPU so the CPU idle clock down feature could properly detect global
- * idle state when CPUx is off.
- */
-static void exynos_set_delayed_reset_assertion(u32 core_id, bool enable)
-{
-       if (soc_is_exynos4()) {
-               unsigned int tmp;
-
-               tmp = pmu_raw_readl(EXYNOS_ARM_CORE_OPTION(core_id));
-               if (enable)
-                       tmp |= S5P_USE_DELAYED_RESET_ASSERTION;
-               else
-                       tmp &= ~(S5P_USE_DELAYED_RESET_ASSERTION);
-               pmu_raw_writel(tmp, EXYNOS_ARM_CORE_OPTION(core_id));
-       }
-}
-
 #ifdef CONFIG_HOTPLUG_CPU
 static inline void cpu_leave_lowpower(u32 core_id)
 {
@@ -73,8 +49,6 @@ static inline void cpu_leave_lowpower(u32 core_id)
          : "=&r" (v)
          : "Ir" (CR_C), "Ir" (0x40)
          : "cc");
-
-        exynos_set_delayed_reset_assertion(core_id, false);
 }
 
 static inline void platform_do_lowpower(unsigned int cpu, int *spurious)
@@ -87,14 +61,6 @@ static inline void platform_do_lowpower(unsigned int cpu, int *spurious)
                /* Turn the CPU off on next WFI instruction. */
                exynos_cpu_power_down(core_id);
 
-               /*
-                * Exynos4 SoCs require setting
-                * USE_DELAYED_RESET_ASSERTION so the CPU idle
-                * clock down feature could properly detect
-                * global idle state when CPUx is off.
-                */
-               exynos_set_delayed_reset_assertion(core_id, true);
-
                wfi();
 
                if (pen_release == core_id) {
@@ -126,6 +92,8 @@ static inline void platform_do_lowpower(unsigned int cpu, int *spurious)
  */
 void exynos_cpu_power_down(int cpu)
 {
+       u32 core_conf;
+
        if (cpu == 0 && (soc_is_exynos5420() || soc_is_exynos5800())) {
                /*
                 * Bypass power down for CPU0 during suspend. Check for
@@ -137,7 +105,10 @@ void exynos_cpu_power_down(int cpu)
                if (!(val & S5P_CORE_LOCAL_PWR_EN))
                        return;
        }
-       pmu_raw_writel(0, EXYNOS_ARM_CORE_CONFIGURATION(cpu));
+
+       core_conf = pmu_raw_readl(EXYNOS_ARM_CORE_CONFIGURATION(cpu));
+       core_conf &= ~S5P_CORE_LOCAL_PWR_EN;
+       pmu_raw_writel(core_conf, EXYNOS_ARM_CORE_CONFIGURATION(cpu));
 }
 
 /**
@@ -148,7 +119,12 @@ void exynos_cpu_power_down(int cpu)
  */
 void exynos_cpu_power_up(int cpu)
 {
-       pmu_raw_writel(S5P_CORE_LOCAL_PWR_EN,
+       u32 core_conf = S5P_CORE_LOCAL_PWR_EN;
+
+       if (soc_is_exynos3250())
+               core_conf |= S5P_CORE_AUTOWAKEUP_EN;
+
+       pmu_raw_writel(core_conf,
                        EXYNOS_ARM_CORE_CONFIGURATION(cpu));
 }
 
@@ -226,11 +202,14 @@ static void exynos_core_restart(u32 core_id)
        if (!of_machine_is_compatible("samsung,exynos3250"))
                return;
 
+       while (!pmu_raw_readl(S5P_PMU_SPARE2))
+               udelay(10);
+       udelay(10);
+
        val = pmu_raw_readl(EXYNOS_ARM_CORE_STATUS(core_id));
        val |= S5P_CORE_WAKEUP_FROM_LOCAL_CFG;
        pmu_raw_writel(val, EXYNOS_ARM_CORE_STATUS(core_id));
 
-       pr_info("CPU%u: Software reset\n", core_id);
        pmu_raw_writel(EXYNOS_CORE_PO_RESET(core_id), EXYNOS_SWRESET);
 }
 
@@ -268,6 +247,56 @@ static void exynos_secondary_init(unsigned int cpu)
        spin_unlock(&boot_lock);
 }
 
+static int exynos_set_boot_addr(u32 core_id, unsigned long boot_addr)
+{
+       int ret;
+
+       /*
+        * Try to set boot address using firmware first
+        * and fall back to boot register if it fails.
+        */
+       ret = call_firmware_op(set_cpu_boot_addr, core_id, boot_addr);
+       if (ret && ret != -ENOSYS)
+               goto fail;
+       if (ret == -ENOSYS) {
+               void __iomem *boot_reg = cpu_boot_reg(core_id);
+
+               if (IS_ERR(boot_reg)) {
+                       ret = PTR_ERR(boot_reg);
+                       goto fail;
+               }
+               __raw_writel(boot_addr, boot_reg);
+               ret = 0;
+       }
+fail:
+       return ret;
+}
+
+static int exynos_get_boot_addr(u32 core_id, unsigned long *boot_addr)
+{
+       int ret;
+
+       /*
+        * Try to get boot address using firmware first
+        * and fall back to boot register if it fails.
+        */
+       ret = call_firmware_op(get_cpu_boot_addr, core_id, boot_addr);
+       if (ret && ret != -ENOSYS)
+               goto fail;
+       if (ret == -ENOSYS) {
+               void __iomem *boot_reg = cpu_boot_reg(core_id);
+
+               if (IS_ERR(boot_reg)) {
+                       ret = PTR_ERR(boot_reg);
+                       goto fail;
+               }
+               *boot_addr = __raw_readl(boot_reg);
+               ret = 0;
+       }
+fail:
+       return ret;
+}
+
 static int exynos_boot_secondary(unsigned int cpu, struct task_struct *idle)
 {
        unsigned long timeout;
@@ -327,26 +356,16 @@ static int exynos_boot_secondary(unsigned int cpu, struct task_struct *idle)
 
                boot_addr = virt_to_phys(exynos4_secondary_startup);
 
-               /*
-                * Try to set boot address using firmware first
-                * and fall back to boot register if it fails.
-                */
-               ret = call_firmware_op(set_cpu_boot_addr, core_id, boot_addr);
-               if (ret && ret != -ENOSYS)
+               ret = exynos_set_boot_addr(core_id, boot_addr);
+               if (ret)
                        goto fail;
-               if (ret == -ENOSYS) {
-                       void __iomem *boot_reg = cpu_boot_reg(core_id);
-
-                       if (IS_ERR(boot_reg)) {
-                               ret = PTR_ERR(boot_reg);
-                               goto fail;
-                       }
-                       __raw_writel(boot_addr, boot_reg);
-               }
 
                call_firmware_op(cpu_boot, core_id);
 
-               arch_send_wakeup_ipi_mask(cpumask_of(cpu));
+               if (soc_is_exynos3250())
+                       dsb_sev();
+               else
+                       arch_send_wakeup_ipi_mask(cpumask_of(cpu));
 
                if (pen_release == -1)
                        break;
@@ -354,8 +373,8 @@ static int exynos_boot_secondary(unsigned int cpu, struct task_struct *idle)
                udelay(10);
        }
 
-       /* No harm if this is called during first boot of secondary CPU */
-       exynos_set_delayed_reset_assertion(core_id, false);
+       if (pen_release != -1)
+               ret = -ETIMEDOUT;
 
        /*
         * now the secondary core is starting up let it run its
@@ -403,6 +422,8 @@ static void __init exynos_smp_prepare_cpus(unsigned int max_cpus)
 
        exynos_sysram_init();
 
+       exynos_set_delayed_reset_assertion(true);
+
        if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9)
                scu_enable(scu_base_addr());
 
@@ -425,16 +446,9 @@ static void __init exynos_smp_prepare_cpus(unsigned int max_cpus)
                core_id = MPIDR_AFFINITY_LEVEL(mpidr, 0);
                boot_addr = virt_to_phys(exynos4_secondary_startup);
 
-               ret = call_firmware_op(set_cpu_boot_addr, core_id, boot_addr);
-               if (ret && ret != -ENOSYS)
+               ret = exynos_set_boot_addr(core_id, boot_addr);
+               if (ret)
                        break;
-               if (ret == -ENOSYS) {
-                       void __iomem *boot_reg = cpu_boot_reg(core_id);
-
-                       if (IS_ERR(boot_reg))
-                               break;
-                       __raw_writel(boot_addr, boot_reg);
-               }
        }
 }
 
This page took 0.088556 seconds and 5 git commands to generate.