#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>
#include "pm.h"
#include "clock.h"
#include "sleep.h"
+#include "cpu-tegra.h"
#include "common.h"
#include "iomap.h"
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)
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));
{
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))
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.
/* 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;
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
* 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;
}
*/
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);
/* 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;
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,
#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
};