xref: /linux/arch/arm/mach-axxia/platsmp.c (revision 1d22924e1c4e299337e86e290c02c3e3eb43b608)
1*1d22924eSAnders Berg /*
2*1d22924eSAnders Berg  * linux/arch/arm/mach-axxia/platsmp.c
3*1d22924eSAnders Berg  *
4*1d22924eSAnders Berg  * Copyright (C) 2012 LSI Corporation
5*1d22924eSAnders Berg  *
6*1d22924eSAnders Berg  * This program is free software; you can redistribute it and/or modify
7*1d22924eSAnders Berg  * it under the terms of the GNU General Public License version 2 as
8*1d22924eSAnders Berg  * published by the Free Software Foundation.
9*1d22924eSAnders Berg  */
10*1d22924eSAnders Berg 
11*1d22924eSAnders Berg #include <linux/init.h>
12*1d22924eSAnders Berg #include <linux/io.h>
13*1d22924eSAnders Berg #include <linux/smp.h>
14*1d22924eSAnders Berg #include <linux/of.h>
15*1d22924eSAnders Berg #include <linux/of_address.h>
16*1d22924eSAnders Berg #include <asm/cacheflush.h>
17*1d22924eSAnders Berg 
18*1d22924eSAnders Berg /* Syscon register offsets for releasing cores from reset */
19*1d22924eSAnders Berg #define SC_CRIT_WRITE_KEY	0x1000
20*1d22924eSAnders Berg #define SC_RST_CPU_HOLD		0x1010
21*1d22924eSAnders Berg 
22*1d22924eSAnders Berg /*
23*1d22924eSAnders Berg  * Write the kernel entry point for secondary CPUs to the specified address
24*1d22924eSAnders Berg  */
25*1d22924eSAnders Berg static void write_release_addr(u32 release_phys)
26*1d22924eSAnders Berg {
27*1d22924eSAnders Berg 	u32 *virt = (u32 *) phys_to_virt(release_phys);
28*1d22924eSAnders Berg 	writel_relaxed(virt_to_phys(secondary_startup), virt);
29*1d22924eSAnders Berg 	/* Make sure this store is visible to other CPUs */
30*1d22924eSAnders Berg 	smp_wmb();
31*1d22924eSAnders Berg 	__cpuc_flush_dcache_area(virt, sizeof(u32));
32*1d22924eSAnders Berg }
33*1d22924eSAnders Berg 
34*1d22924eSAnders Berg static int axxia_boot_secondary(unsigned int cpu, struct task_struct *idle)
35*1d22924eSAnders Berg {
36*1d22924eSAnders Berg 	struct device_node *syscon_np;
37*1d22924eSAnders Berg 	void __iomem *syscon;
38*1d22924eSAnders Berg 	u32 tmp;
39*1d22924eSAnders Berg 
40*1d22924eSAnders Berg 	syscon_np = of_find_compatible_node(NULL, NULL, "lsi,axxia-syscon");
41*1d22924eSAnders Berg 	if (!syscon_np)
42*1d22924eSAnders Berg 		return -ENOENT;
43*1d22924eSAnders Berg 
44*1d22924eSAnders Berg 	syscon = of_iomap(syscon_np, 0);
45*1d22924eSAnders Berg 	if (!syscon)
46*1d22924eSAnders Berg 		return -ENOMEM;
47*1d22924eSAnders Berg 
48*1d22924eSAnders Berg 	tmp = readl(syscon + SC_RST_CPU_HOLD);
49*1d22924eSAnders Berg 	writel(0xab, syscon + SC_CRIT_WRITE_KEY);
50*1d22924eSAnders Berg 	tmp &= ~(1 << cpu);
51*1d22924eSAnders Berg 	writel(tmp, syscon + SC_RST_CPU_HOLD);
52*1d22924eSAnders Berg 
53*1d22924eSAnders Berg 	return 0;
54*1d22924eSAnders Berg }
55*1d22924eSAnders Berg 
56*1d22924eSAnders Berg static void __init axxia_smp_prepare_cpus(unsigned int max_cpus)
57*1d22924eSAnders Berg {
58*1d22924eSAnders Berg 	int cpu_count = 0;
59*1d22924eSAnders Berg 	int cpu;
60*1d22924eSAnders Berg 
61*1d22924eSAnders Berg 	/*
62*1d22924eSAnders Berg 	 * Initialise the present map, which describes the set of CPUs actually
63*1d22924eSAnders Berg 	 * populated at the present time.
64*1d22924eSAnders Berg 	 */
65*1d22924eSAnders Berg 	for_each_possible_cpu(cpu) {
66*1d22924eSAnders Berg 		struct device_node *np;
67*1d22924eSAnders Berg 		u32 release_phys;
68*1d22924eSAnders Berg 
69*1d22924eSAnders Berg 		np = of_get_cpu_node(cpu, NULL);
70*1d22924eSAnders Berg 		if (!np)
71*1d22924eSAnders Berg 			continue;
72*1d22924eSAnders Berg 		if (of_property_read_u32(np, "cpu-release-addr", &release_phys))
73*1d22924eSAnders Berg 			continue;
74*1d22924eSAnders Berg 
75*1d22924eSAnders Berg 		if (cpu_count < max_cpus) {
76*1d22924eSAnders Berg 			set_cpu_present(cpu, true);
77*1d22924eSAnders Berg 			cpu_count++;
78*1d22924eSAnders Berg 		}
79*1d22924eSAnders Berg 
80*1d22924eSAnders Berg 		if (release_phys != 0)
81*1d22924eSAnders Berg 			write_release_addr(release_phys);
82*1d22924eSAnders Berg 	}
83*1d22924eSAnders Berg }
84*1d22924eSAnders Berg 
85*1d22924eSAnders Berg static struct smp_operations axxia_smp_ops __initdata = {
86*1d22924eSAnders Berg 	.smp_prepare_cpus	= axxia_smp_prepare_cpus,
87*1d22924eSAnders Berg 	.smp_boot_secondary	= axxia_boot_secondary,
88*1d22924eSAnders Berg };
89*1d22924eSAnders Berg CPU_METHOD_OF_DECLARE(axxia_smp, "lsi,syscon-release", &axxia_smp_ops);
90