diff options
Diffstat (limited to 'arch/arm/mach-mvebu/platsmp.c')
| -rw-r--r-- | arch/arm/mach-mvebu/platsmp.c | 122 | 
1 files changed, 122 insertions, 0 deletions
diff --git a/arch/arm/mach-mvebu/platsmp.c b/arch/arm/mach-mvebu/platsmp.c new file mode 100644 index 00000000000..fe16aaf7c19 --- /dev/null +++ b/arch/arm/mach-mvebu/platsmp.c @@ -0,0 +1,122 @@ +/* + * Symmetric Multi Processing (SMP) support for Armada XP + * + * Copyright (C) 2012 Marvell + * + * Lior Amsalem <alior@marvell.com> + * Yehuda Yitschak <yehuday@marvell.com> + * Gregory CLEMENT <gregory.clement@free-electrons.com> + * Thomas Petazzoni <thomas.petazzoni@free-electrons.com> + * + * This file is licensed under the terms of the GNU General Public + * License version 2.  This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + * + * The Armada XP SoC has 4 ARMv7 PJ4B CPUs running in full HW coherency + * This file implements the routines for preparing the SMP infrastructure + * and waking up the secondary CPUs + */ + +#include <linux/init.h> +#include <linux/smp.h> +#include <linux/clk.h> +#include <linux/of.h> +#include <asm/cacheflush.h> +#include <asm/smp_plat.h> +#include "common.h" +#include "armada-370-xp.h" +#include "pmsu.h" +#include "coherency.h" + +void __init set_secondary_cpus_clock(void) +{ +	int thiscpu; +	unsigned long rate; +	struct clk *cpu_clk = NULL; +	struct device_node *np = NULL; + +	thiscpu = smp_processor_id(); +	for_each_node_by_type(np, "cpu") { +		int err; +		int cpu; + +		err = of_property_read_u32(np, "reg", &cpu); +		if (WARN_ON(err)) +			return; + +		if (cpu == thiscpu) { +			cpu_clk = of_clk_get(np, 0); +			break; +		} +	} +	if (WARN_ON(IS_ERR(cpu_clk))) +		return; +	clk_prepare_enable(cpu_clk); +	rate = clk_get_rate(cpu_clk); + +	/* set all the other CPU clk to the same rate than the boot CPU */ +	for_each_node_by_type(np, "cpu") { +		int err; +		int cpu; + +		err = of_property_read_u32(np, "reg", &cpu); +		if (WARN_ON(err)) +			return; + +		if (cpu != thiscpu) { +			cpu_clk = of_clk_get(np, 0); +			clk_set_rate(cpu_clk, rate); +		} +	} +} + +static void __cpuinit armada_xp_secondary_init(unsigned int cpu) +{ +	armada_xp_mpic_smp_cpu_init(); +} + +static int __cpuinit armada_xp_boot_secondary(unsigned int cpu, +					      struct task_struct *idle) +{ +	pr_info("Booting CPU %d\n", cpu); + +	armada_xp_boot_cpu(cpu, armada_xp_secondary_startup); + +	return 0; +} + +static void __init armada_xp_smp_init_cpus(void) +{ +	unsigned int i, ncores; +	ncores = coherency_get_cpu_count(); + +	/* Limit possible CPUs to defconfig */ +	if (ncores > nr_cpu_ids) { +		pr_warn("SMP: %d CPUs physically present. Only %d configured.", +			ncores, nr_cpu_ids); +		pr_warn("Clipping CPU count to %d\n", nr_cpu_ids); +		ncores = nr_cpu_ids; +	} + +	for (i = 0; i < ncores; i++) +		set_cpu_possible(i, true); + +	set_smp_cross_call(armada_mpic_send_doorbell); +} + +void __init armada_xp_smp_prepare_cpus(unsigned int max_cpus) +{ +	set_secondary_cpus_clock(); +	flush_cache_all(); +	set_cpu_coherent(cpu_logical_map(smp_processor_id()), 0); +} + +struct smp_operations armada_xp_smp_ops __initdata = { +	.smp_init_cpus		= armada_xp_smp_init_cpus, +	.smp_prepare_cpus	= armada_xp_smp_prepare_cpus, +	.smp_secondary_init	= armada_xp_secondary_init, +	.smp_boot_secondary	= armada_xp_boot_secondary, +#ifdef CONFIG_HOTPLUG_CPU +	.cpu_die		= armada_xp_cpu_die, +#endif +};  |