mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
190 lines
5.0 KiB
190 lines
5.0 KiB
// SPDX-License-Identifier: GPL-2.0-only |
|
/* |
|
* linux/arch/arm/mach-tegra/platsmp.c |
|
* |
|
* Copyright (C) 2002 ARM Ltd. |
|
* All Rights Reserved |
|
* |
|
* Copyright (C) 2009 Palm |
|
* All Rights Reserved |
|
*/ |
|
|
|
#include <linux/clk/tegra.h> |
|
#include <linux/delay.h> |
|
#include <linux/device.h> |
|
#include <linux/errno.h> |
|
#include <linux/init.h> |
|
#include <linux/io.h> |
|
#include <linux/jiffies.h> |
|
#include <linux/smp.h> |
|
|
|
#include <soc/tegra/flowctrl.h> |
|
#include <soc/tegra/fuse.h> |
|
#include <soc/tegra/pmc.h> |
|
|
|
#include <asm/cacheflush.h> |
|
#include <asm/mach-types.h> |
|
#include <asm/smp_plat.h> |
|
#include <asm/smp_scu.h> |
|
|
|
#include "common.h" |
|
#include "iomap.h" |
|
#include "reset.h" |
|
|
|
static cpumask_t tegra_cpu_init_mask; |
|
|
|
static void tegra_secondary_init(unsigned int cpu) |
|
{ |
|
cpumask_set_cpu(cpu, &tegra_cpu_init_mask); |
|
} |
|
|
|
|
|
static int tegra20_boot_secondary(unsigned int cpu, struct task_struct *idle) |
|
{ |
|
cpu = cpu_logical_map(cpu); |
|
|
|
/* |
|
* 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); |
|
|
|
/* |
|
* 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); |
|
|
|
tegra_enable_cpu_clock(cpu); |
|
flowctrl_write_cpu_csr(cpu, 0); /* Clear flow controller CSR. */ |
|
tegra_cpu_out_of_reset(cpu); |
|
return 0; |
|
} |
|
|
|
static int tegra30_boot_secondary(unsigned int cpu, struct task_struct *idle) |
|
{ |
|
int ret; |
|
unsigned long timeout; |
|
|
|
cpu = cpu_logical_map(cpu); |
|
tegra_put_cpu_in_reset(cpu); |
|
flowctrl_write_cpu_halt(cpu, 0); |
|
|
|
/* |
|
* The power up sequence of cold boot CPU and warm boot CPU |
|
* was different. |
|
* |
|
* For warm boot CPU that was resumed from CPU hotplug, the |
|
* power will be resumed automatically after un-halting the |
|
* flow controller of the warm boot CPU. We need to wait for |
|
* the confirmaiton that the CPU is powered then removing |
|
* the IO clamps. |
|
* For cold boot CPU, do not wait. After the cold boot CPU be |
|
* booted, it will run to tegra_secondary_init() and set |
|
* tegra_cpu_init_mask which influences what tegra30_boot_secondary() |
|
* next time around. |
|
*/ |
|
if (cpumask_test_cpu(cpu, &tegra_cpu_init_mask)) { |
|
timeout = jiffies + msecs_to_jiffies(50); |
|
do { |
|
if (tegra_pmc_cpu_is_powered(cpu)) |
|
goto remove_clamps; |
|
udelay(10); |
|
} while (time_before(jiffies, timeout)); |
|
} |
|
|
|
/* |
|
* The power status of the cold boot CPU is power gated as |
|
* default. To power up the cold boot CPU, the power should |
|
* be un-gated by un-toggling the power gate register |
|
* manually. |
|
*/ |
|
ret = tegra_pmc_cpu_power_on(cpu); |
|
if (ret) |
|
return ret; |
|
|
|
remove_clamps: |
|
/* CPU partition is powered. Enable the CPU clock. */ |
|
tegra_enable_cpu_clock(cpu); |
|
udelay(10); |
|
|
|
/* Remove I/O clamps. */ |
|
ret = tegra_pmc_cpu_remove_clamping(cpu); |
|
if (ret) |
|
return ret; |
|
|
|
udelay(10); |
|
|
|
flowctrl_write_cpu_csr(cpu, 0); /* Clear flow controller CSR. */ |
|
tegra_cpu_out_of_reset(cpu); |
|
return 0; |
|
} |
|
|
|
static int tegra114_boot_secondary(unsigned int cpu, struct task_struct *idle) |
|
{ |
|
int ret = 0; |
|
|
|
cpu = cpu_logical_map(cpu); |
|
|
|
if (cpumask_test_cpu(cpu, &tegra_cpu_init_mask)) { |
|
/* |
|
* Warm boot flow |
|
* The flow controller in charge of the power state and |
|
* control for each CPU. |
|
*/ |
|
/* set SCLK as event trigger for flow controller */ |
|
flowctrl_write_cpu_csr(cpu, 1); |
|
flowctrl_write_cpu_halt(cpu, |
|
FLOW_CTRL_WAITEVENT | FLOW_CTRL_SCLK_RESUME); |
|
} else { |
|
/* |
|
* Cold boot flow |
|
* The CPU is powered up by toggling PMC directly. It will |
|
* also initial power state in flow controller. After that, |
|
* the CPU's power state is maintained by flow controller. |
|
*/ |
|
ret = tegra_pmc_cpu_power_on(cpu); |
|
} |
|
|
|
return ret; |
|
} |
|
|
|
static int tegra_boot_secondary(unsigned int cpu, |
|
struct task_struct *idle) |
|
{ |
|
if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC) && tegra_get_chip_id() == TEGRA20) |
|
return tegra20_boot_secondary(cpu, idle); |
|
if (IS_ENABLED(CONFIG_ARCH_TEGRA_3x_SOC) && tegra_get_chip_id() == TEGRA30) |
|
return tegra30_boot_secondary(cpu, idle); |
|
if (IS_ENABLED(CONFIG_ARCH_TEGRA_114_SOC) && tegra_get_chip_id() == TEGRA114) |
|
return tegra114_boot_secondary(cpu, idle); |
|
if (IS_ENABLED(CONFIG_ARCH_TEGRA_124_SOC) && tegra_get_chip_id() == TEGRA124) |
|
return tegra114_boot_secondary(cpu, idle); |
|
|
|
return -EINVAL; |
|
} |
|
|
|
static void __init tegra_smp_prepare_cpus(unsigned int max_cpus) |
|
{ |
|
/* Always mark the boot CPU (CPU0) as initialized. */ |
|
cpumask_set_cpu(0, &tegra_cpu_init_mask); |
|
|
|
if (scu_a9_has_base()) |
|
scu_enable(IO_ADDRESS(scu_a9_get_base())); |
|
} |
|
|
|
const struct smp_operations tegra_smp_ops __initconst = { |
|
.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, |
|
#endif |
|
};
|
|
|