blob: c3eed8443296e3b9864e7826eed2e8930612abb1 [file] [log] [blame]
/*
* linux/arch/arm/mach-tegra/platsmp.c
*
* Copyright (C) 2002 ARM Ltd.
* All Rights Reserved
*
* Copyright (C) 2009 Palm
* All Rights Reserved
*
* Copyright (C) 2010-2013, NVIDIA Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/smp.h>
#include <linux/delay.h>
#include <linux/clk.h>
#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 "fuse.h"
#include "flowctrl.h"
#include "reset.h"
#include "pm.h"
#include "clock.h"
#include "sleep.h"
#include "cpu-tegra.h"
#include "timer.h"
#include "common.h"
#include "iomap.h"
bool tegra_all_cpus_booted;
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)
#if defined(CONFIG_ARCH_TEGRA_14x_SOC)
static struct cpumask tegra_cpu_power_mask_saved;
#endif
#ifndef CONFIG_ARCH_TEGRA_2x_SOC
#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);
#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;
unsigned int cpuid;
/* Dual core SKU */
if (tegra_get_chipid() == TEGRA_CHIPID_TEGRA11) {
if (tegra_sku_id == 0x20) {
number_of_cores = 2;
return;
}
}
cpuid = (read_cpuid_id() >> 4) & 0xFFF;
/* 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)
{
BUG_ON((int)number_of_cores <= 0);
return number_of_cores;
}
static int is_g_cluster_available(unsigned int cpu)
{
#ifdef CONFIG_TEGRA_CLUSTER_CONTROL
u32 fuse_sku = readl(FUSE_SKU_DIRECT_CONFIG);
u32 bond_out = readl(CAR_BOND_OUT_V);
/* Does the G CPU complex exist at all? */
if ((fuse_sku & FUSE_SKU_DISABLE_ALL_CPUS) ||
(bond_out & CAR_BOND_OUT_V_CPU_G))
return -EPERM;
if (cpu >= available_cpus())
return -EPERM;
/* FIXME: The G CPU can be unavailable for a number of reasons
* (e.g., low battery, over temperature, etc.). Add checks for
* these conditions. */
return 0;
#else
return -EPERM;
#endif
}
static bool is_cpu_powered(unsigned int cpu)
{
if (is_lp_cluster())
return true;
else
return tegra_powergate_is_powered(TEGRA_CPU_POWERGATE_ID(cpu));
}
static void __cpuinit tegra_secondary_init(unsigned int cpu)
{
cpumask_set_cpu(cpu, to_cpumask(tegra_cpu_init_bits));
cpumask_set_cpu(cpu, tegra_cpu_power_mask);
if (!tegra_all_cpus_booted)
if (cpumask_equal(tegra_cpu_init_mask, cpu_present_mask))
tegra_all_cpus_booted = true;
}
static int tegra20_power_up_cpu(unsigned int cpu)
{
/* Enable the CPU clock. */
tegra_enable_cpu_clock(cpu);
/* Clear flow controller CSR. */
flowctrl_write_cpu_csr(cpu, 0);
return 0;
}
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 (booted) {
timeout = jiffies + msecs_to_jiffies(50);
do {
if (is_cpu_powered(cpu))
goto remove_clamps;
udelay(10);
} while (time_before(jiffies, timeout));
}
/* First boot or Flow controller did not work as expected. Try to
directly toggle power gates. Error if direct power on also fails. */
if (!is_cpu_powered(cpu)) {
ret = tegra_unpowergate_partition(TEGRA_CPU_POWERGATE_ID(cpu));
if (ret)
goto fail;
/* Wait for the power to come up. */
timeout = jiffies + 10*HZ;
do {
if (is_cpu_powered(cpu))
goto remove_clamps;
udelay(10);
} while (time_before(jiffies, timeout));
ret = -ETIMEDOUT;
goto fail;
}
remove_clamps:
/* CPU partition is powered. Enable the CPU clock. */
tegra_enable_cpu_clock(cpu);
udelay(10);
/* Remove I/O clamps. */
ret = tegra_powergate_remove_clamping(TEGRA_CPU_POWERGATE_ID(cpu));
if (ret)
return ret;
udelay(10);
fail:
/* Clear flow controller CSR. */
flowctrl_write_cpu_csr(cpu, 0);
return 0;
}
static int tegra11x_power_up_cpu(unsigned int cpu)
{
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);
}
return 0;
}
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.
*/
preset_lpj = loops_per_jiffy;
if (is_lp_cluster()) {
struct clk *cpu_clk, *cpu_g_clk;
/* The G CPU may not be available for a variety of reasons. */
status = is_g_cluster_available(cpu);
if (status)
goto done;
cpu_clk = tegra_get_clock_by_name("cpu");
cpu_g_clk = tegra_get_clock_by_name("cpu_g");
/* Switch to G CPU before continuing. */
if (!cpu_clk || !cpu_g_clk) {
/* Early boot, clock infrastructure is not initialized
- CPU mode switch is not allowed */
status = -EINVAL;
} else {
#if defined(CONFIG_CPU_FREQ) && !defined(CONFIG_COMMON_CLK)
/* 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 = tegra_cluster_switch(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
* controller to stop driving reset if the CPU has been power-gated
* via the flow controller). This will have no effect on first boot
* of the CPU since it should already be in reset.
*/
tegra_put_cpu_in_reset(cpu);
#endif
switch (tegra_chip_id) {
case TEGRA_CHIPID_TEGRA2:
/*
* 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 TEGRA_CHIPID_TEGRA3:
/*
* 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 TEGRA_CHIPID_TEGRA11:
case TEGRA_CHIPID_TEGRA14:
case TEGRA_CHIPID_TEGRA12:
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;
}
/*
* Initialise the CPU possible map early - this describes the CPUs
* which may be present or become present in the system.
*/
static void __init tegra_smp_init_cpus(void)
{
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);
ncores = nr_cpu_ids;
}
for (i = 0; i < ncores; i++)
set_cpu_possible(i, true);
/* If only one CPU is possible, platform_smp_prepare_cpus() will
never get called. We must therefore initialize the reset handler
here. If there is more than one CPU, we must wait until after
the cpu_present_mask has been updated with all present CPUs in
platform_smp_prepare_cpus() before initializing the reset handler. */
if (ncores == 1) {
tegra_cpu_reset_handler_init();
tegra_all_cpus_booted = true;
}
}
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;
/* If we're here, it means that more than one CPU was found by
smp_init_cpus() which also means that it did not initialize the
reset handler. Do it now before the secondary CPUs are started. */
tegra_cpu_reset_handler_init();
#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
#if defined(CONFIG_ARCH_TEGRA_14x_SOC)
void tegra_smp_save_power_mask()
{
tegra_cpu_power_mask_saved = *((struct cpumask *)tegra_cpu_power_mask);
}
void tegra_smp_restore_power_mask()
{
*tegra_cpu_power_mask = tegra_cpu_power_mask_saved;
}
#endif
#ifdef CONFIG_HOTPLUG_CPU
int tegra_cpu_disable(unsigned int cpu)
{
if (cpu == 0) return -EPERM;
/* only call this if the cputimer is in use */
#if !defined(CONFIG_ARM_ARCH_TIMER) && !defined(CONFIG_HAVE_ARM_TWD)
tegra_cputimer_reset_irq_affinity(cpu);
#endif
return 0;
}
#endif
struct smp_operations tegra_smp_ops __initdata = {
.smp_init_cpus = tegra_smp_init_cpus,
.smp_prepare_cpus = tegra_smp_prepare_cpus,
.smp_secondary_init = tegra_secondary_init,
.smp_boot_secondary = tegra_boot_secondary,
#ifdef CONFIG_HOTPLUG_CPU
.cpu_kill = tegra_cpu_kill,
.cpu_die = tegra_cpu_die,
.cpu_disable = tegra_cpu_disable,
#endif
};