]> nv-tegra.nvidia Code Review - linux-3.10.git/blobdiff - arch/arm/mach-tegra/platsmp.c
ARM: tegra11x: CPUID virtualization support
[linux-3.10.git] / arch / arm / mach-tegra / platsmp.c
index 9fe381004f1a19210b2e17dcd11c83d5ecd07f13..d459b5d21c4a1b22e6b17f8a81b1e0872ce6210b 100644 (file)
@@ -23,6 +23,8 @@
 #include <linux/clk/tegra.h>
 #include <linux/cpumask.h>
 
+#include <asm/cputype.h>
+#include <asm/smp_plat.h>
 #include <asm/smp_scu.h>
 
 #include <mach/powergate.h>
@@ -33,6 +35,7 @@
 #include "pm.h"
 #include "clock.h"
 #include "sleep.h"
+#include "cpu-tegra.h"
 
 #include "common.h"
 #include "iomap.h"
@@ -43,46 +46,67 @@ static DECLARE_BITMAP(tegra_cpu_init_bits, CONFIG_NR_CPUS) __read_mostly;
 const struct cpumask *const tegra_cpu_init_mask = to_cpumask(tegra_cpu_init_bits);
 #define tegra_cpu_init_map     (*(cpumask_t *)tegra_cpu_init_mask)
 
+static DECLARE_BITMAP(tegra_cpu_power_up_by_fc, CONFIG_NR_CPUS) __read_mostly;
+struct cpumask *tegra_cpu_power_mask =
+                               to_cpumask(tegra_cpu_power_up_by_fc);
+#define tegra_cpu_power_map    (*(cpumask_t *)tegra_cpu_power_mask)
+
 #ifndef CONFIG_ARCH_TEGRA_2x_SOC
-#define CLK_RST_CONTROLLER_CLK_CPU_CMPLX_CLR \
-       (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x34c)
 #define CAR_BOND_OUT_V \
        (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x390)
 #define CAR_BOND_OUT_V_CPU_G   (1<<0)
 #endif
 
+#define CLAMP_STATUS   0x2c
+#define PWRGATE_TOGGLE 0x30
+
+#define PMC_TOGGLE_START       0x100
+
 #ifdef CONFIG_HAVE_ARM_SCU
 static void __iomem *scu_base = IO_ADDRESS(TEGRA_ARM_PERIF_BASE);
-static inline unsigned int get_core_count(void)
-{
-       return scu_get_core_count(scu_base);
-}
-#else
-static inline unsigned int get_core_count(void)
+#endif
+
+static unsigned int number_of_cores;
+
+static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE);
+#define pmc_writel(value, reg) writel(value, pmc + (reg))
+#define pmc_readl(reg)         readl(pmc + (reg))
+
+static void __init setup_core_count(void)
 {
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
        u32 l2ctlr;
 
-       __asm__("mrc p15, 1, %0, c9, c0, 2\n" : "=r" (l2ctlr));
+       unsigned int cpuid = (read_cpuid_id() >> 4) & 0xFFF;
 
-       return ((l2ctlr >> 24) & 3) + 1;
-}
+       /* Cortex-A15? */
+       if (cpuid == 0xC0F) {
+               __asm__("mrc p15, 1, %0, c9, c0, 2\n" : "=r" (l2ctlr));
+               number_of_cores = ((l2ctlr >> 24) & 3) + 1;
+       }
+       else {
+#endif
+#ifdef CONFIG_HAVE_ARM_SCU
+               number_of_cores = scu_get_core_count(scu_base);
+#else
+               number_of_cores = 1;
+#endif
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+       }
+       if (number_of_cores > 1) {
+               u32 fuse_sku = readl(FUSE_SKU_DIRECT_CONFIG);
+               number_of_cores -= FUSE_SKU_NUM_DISABLED_CPUS(fuse_sku);
+               BUG_ON((int)number_of_cores <= 0);
+       }
 #endif
+}
 
 static unsigned int available_cpus(void)
 {
-       static unsigned int ncores;
 
-       if (ncores == 0) {
-               ncores = get_core_count();
-#ifndef CONFIG_ARCH_TEGRA_2x_SOC
-               if (ncores > 1) {
-                       u32 fuse_sku = readl(FUSE_SKU_DIRECT_CONFIG);
-                       ncores -= FUSE_SKU_NUM_DISABLED_CPUS(fuse_sku);
-                       BUG_ON((int)ncores <= 0);
-               }
-#endif
-       }
-       return ncores;
+       BUG_ON((int)number_of_cores <= 0);
+
+       return number_of_cores;
 }
 
 static int is_g_cluster_available(unsigned int cpu)
@@ -116,6 +140,15 @@ static bool is_cpu_powered(unsigned int cpu)
                return tegra_powergate_is_powered(TEGRA_CPU_POWERGATE_ID(cpu));
 }
 
+static bool is_clamp_removed(unsigned int cpu)
+{
+       u32 reg;
+
+       reg = pmc_readl(CLAMP_STATUS);
+
+       return !((reg >> TEGRA_CPU_POWERGATE_ID(cpu)) & 1);
+}
+
 static void __cpuinit tegra_secondary_init(unsigned int cpu)
 {
        cpumask_set_cpu(cpu, to_cpumask(tegra_cpu_init_bits));
@@ -140,16 +173,22 @@ static int tegra30_power_up_cpu(unsigned int cpu)
 {
        int ret;
        unsigned long timeout;
+       bool booted = false;
 
        BUG_ON(cpu == smp_processor_id());
        BUG_ON(is_lp_cluster());
 
+       if (cpu_isset(cpu, tegra_cpu_init_map))
+               booted = true;
+
+       cpu = cpu_logical_map(cpu);
+
        /* If this cpu has booted this function is entered after
         * CPU has been already un-gated by flow controller. Wait
         * for confirmation that cpu is powered and remove clamps.
         * On first boot entry do not wait - go to direct ungate.
         */
-       if (cpu_isset(cpu, tegra_cpu_init_map)) {
+       if (booted) {
                timeout = jiffies + 5;
                do {
                        if (is_cpu_powered(cpu))
@@ -196,10 +235,55 @@ fail:
        return 0;
 }
 
+static int tegra11x_power_up_cpu(unsigned int cpu)
+{
+       int ret;
+       unsigned long timeout;
+
+       BUG_ON(cpu == smp_processor_id());
+       BUG_ON(is_lp_cluster());
+
+       cpu = cpu_logical_map(cpu);
+
+       if (cpu_isset(cpu, tegra_cpu_power_map)) {
+
+               /* set SCLK as event trigger for flow conroller */
+               flowctrl_write_cpu_csr(cpu, 0x1);
+               flowctrl_write_cpu_halt(cpu, 0x48000000);
+
+       } else {
+               u32 reg;
+
+               reg = PMC_TOGGLE_START | TEGRA_CPU_POWERGATE_ID(cpu);
+               pmc_writel(reg, PWRGATE_TOGGLE);
+       }
+
+       ret = -ETIMEDOUT;
+
+       /* Wait for the power to come up. */
+       timeout = jiffies + 10;
+       do {
+               if (is_cpu_powered(cpu) && is_clamp_removed(cpu)) {
+                       cpumask_set_cpu(cpu, tegra_cpu_power_mask);
+                       wmb();
+                       ret = 0;
+                       break;
+               }
+               udelay(10);
+       } while (time_before(jiffies, timeout));
+
+       /* Clear flow controller CSR. */
+       flowctrl_write_cpu_csr(cpu, 0);
+
+       return ret;
+}
+
 static int __cpuinit tegra_boot_secondary(unsigned int cpu, struct task_struct *idle)
 {
        int status;
 
+       cpu = cpu_logical_map(cpu);
+
        /* Avoid timer calibration on slave cpus. Use the value calibrated
         * on master cpu. This reduces the bringup time for each slave cpu
         * by around 260ms.
@@ -221,8 +305,16 @@ static int __cpuinit tegra_boot_secondary(unsigned int cpu, struct task_struct *
                        /* Early boot, clock infrastructure is not initialized
                           - CPU mode switch is not allowed */
                        status = -EINVAL;
-               } else
+               } else {
+#ifdef CONFIG_CPU_FREQ
+                       /* set cpu rate is within g-mode range before switch */
+                       unsigned int speed = max(
+                               (unsigned long)tegra_getspeed(0),
+                               clk_get_min_rate(cpu_g_clk) / 1000);
+                       tegra_update_cpu_speed(speed);
+#endif
                        status = clk_set_parent(cpu_clk, cpu_g_clk);
+               }
 
                if (status)
                        goto done;
@@ -230,6 +322,7 @@ static int __cpuinit tegra_boot_secondary(unsigned int cpu, struct task_struct *
 
        smp_wmb();
 
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC) || defined(CONFIG_ARCH_TEGRA_3x_SOC)
        /*
         * Force the CPU into reset. The CPU must remain in reset when the
         * flow controller state is cleared (which will cause the flow
@@ -238,32 +331,44 @@ static int __cpuinit tegra_boot_secondary(unsigned int cpu, struct task_struct *
         * of the CPU since it should already be in reset.
         */
        tegra_put_cpu_in_reset(cpu);
-
-       /*
-        * Unhalt the CPU. If the flow controller was used to power-gate the
-        * CPU this will cause the flow controller to stop driving reset.
-        * The CPU will remain in reset because the clock and reset block
-        * is now driving reset.
-        */
-       flowctrl_write_cpu_halt(cpu, 0);
+#endif
 
        switch (tegra_chip_id) {
        case TEGRA20:
+               /*
+                * Unhalt the CPU. If the flow controller was used to power-gate
+                * the CPU this will cause the flow controller to stop driving
+                * reset. The CPU will remain in reset because the clock and
+                * reset block is now driving reset.
+                */
+               flowctrl_write_cpu_halt(cpu, 0);
                status = tegra20_power_up_cpu(cpu);
                break;
        case TEGRA30:
+               /*
+                * Unhalt the CPU. If the flow controller was used to power-gate
+                * the CPU this will cause the flow controller to stop driving
+                * reset. The CPU will remain in reset because the clock and
+                * reset block is now driving reset.
+                */
+               flowctrl_write_cpu_halt(cpu, 0);
                status = tegra30_power_up_cpu(cpu);
                break;
+       case TEGRA11X:
+               status = tegra11x_power_up_cpu(cpu);
+               break;
        default:
                status = -EINVAL;
                break;
        }
 
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC) || defined(CONFIG_ARCH_TEGRA_3x_SOC)
        if (status)
                goto done;
 
        /* Take the CPU out of reset. */
        tegra_cpu_out_of_reset(cpu);
+#endif
 done:
        return status;
 }
@@ -274,9 +379,13 @@ done:
  */
 static void __init tegra_smp_init_cpus(void)
 {
-       unsigned int ncores = available_cpus();
+       unsigned int ncores;
        unsigned int i;
 
+       setup_core_count();
+
+       ncores = available_cpus();
+
        if (ncores > nr_cpu_ids) {
                pr_warn("SMP: %u cores greater than maximum (%u), clipping\n",
                        ncores, nr_cpu_ids);
@@ -302,6 +411,9 @@ static void __init tegra_smp_prepare_cpus(unsigned int max_cpus)
        /* Always mark the boot CPU as initialized. */
        cpumask_set_cpu(0, to_cpumask(tegra_cpu_init_bits));
 
+       /* Always mark the boot CPU as initially powered up */
+       cpumask_set_cpu(0, tegra_cpu_power_mask);
+
        if (max_cpus == 1)
                tegra_all_cpus_booted = true;
 
@@ -310,22 +422,26 @@ static void __init tegra_smp_prepare_cpus(unsigned int max_cpus)
           reset handler. Do it now before the secondary CPUs are started. */
        tegra_cpu_reset_handler_init();
 
-#if defined(CONFIG_HAVE_ARM_SCU)
-       {
-               u32 scu_ctrl = __raw_readl(scu_base) |
-                               1 << 3 | /* Enable speculative line fill*/
-                               1 << 5 | /* Enable IC standby */
-                               1 << 6; /* Enable SCU standby */
-               if (!(scu_ctrl & 1))
-                       __raw_writel(scu_ctrl, scu_base);
-       }
-#endif
-
 #ifdef CONFIG_HAVE_ARM_SCU
        scu_enable(scu_base);
 #endif
 }
 
+#if !defined(CONFIG_ARCH_TEGRA_2x_SOC) && !defined(CONFIG_ARCH_TEGRA_3x_SOC)
+void tegra_smp_clear_power_mask()
+{
+       cpumask_clear(tegra_cpu_power_mask);
+       cpumask_set_cpu(0, tegra_cpu_power_mask);
+}
+#endif
+
+#ifdef CONFIG_TEGRA_VIRTUAL_CPUID
+static int tegra_cpu_disable(unsigned int cpu)
+{
+       return 0;
+}
+#endif
+
 struct smp_operations tegra_smp_ops __initdata = {
        .smp_init_cpus          = tegra_smp_init_cpus,
        .smp_prepare_cpus       = tegra_smp_prepare_cpus,
@@ -334,5 +450,8 @@ struct smp_operations tegra_smp_ops __initdata = {
 #ifdef CONFIG_HOTPLUG_CPU
        .cpu_kill               = tegra_cpu_kill,
        .cpu_die                = tegra_cpu_die,
+#ifdef CONFIG_TEGRA_VIRTUAL_CPUID
+       .cpu_disable            = tegra_cpu_disable,
+#endif
 #endif
 };