xref: /openbmc/linux/arch/arm/mach-actions/platsmp.c (revision b6a0e18c)
1172067e0SAndreas Färber /*
2172067e0SAndreas Färber  * Actions Semi Leopard
3172067e0SAndreas Färber  *
4172067e0SAndreas Färber  * This file is based on arm realview smp platform.
5172067e0SAndreas Färber  *
6172067e0SAndreas Färber  * Copyright 2012 Actions Semi Inc.
7172067e0SAndreas Färber  * Author: Actions Semi, Inc.
8172067e0SAndreas Färber  *
9172067e0SAndreas Färber  * Copyright (c) 2017 Andreas Färber
10172067e0SAndreas Färber  *
11172067e0SAndreas Färber  * This program is free software; you can redistribute it and/or modify it
12172067e0SAndreas Färber  * under the terms of the GNU General Public License as published by the
13172067e0SAndreas Färber  * Free Software Foundation; either version 2 of the License, or (at your
14172067e0SAndreas Färber  * option) any later version.
15172067e0SAndreas Färber  */
16172067e0SAndreas Färber 
17172067e0SAndreas Färber #include <linux/delay.h>
18172067e0SAndreas Färber #include <linux/io.h>
19172067e0SAndreas Färber #include <linux/of.h>
20172067e0SAndreas Färber #include <linux/of_address.h>
21172067e0SAndreas Färber #include <linux/smp.h>
22b6a0e18cSAndreas Färber #include <linux/soc/actions/owl-sps.h>
23172067e0SAndreas Färber #include <asm/cacheflush.h>
24172067e0SAndreas Färber #include <asm/smp_plat.h>
25172067e0SAndreas Färber #include <asm/smp_scu.h>
26172067e0SAndreas Färber 
27172067e0SAndreas Färber #define OWL_CPU1_ADDR	0x50
28172067e0SAndreas Färber #define OWL_CPU1_FLAG	0x5c
29172067e0SAndreas Färber 
30172067e0SAndreas Färber #define OWL_CPUx_FLAG_BOOT	0x55aa
31172067e0SAndreas Färber 
32b6a0e18cSAndreas Färber #define OWL_SPS_PG_CTL_PWR_CPU2	BIT(5)
33b6a0e18cSAndreas Färber #define OWL_SPS_PG_CTL_PWR_CPU3	BIT(6)
34b6a0e18cSAndreas Färber #define OWL_SPS_PG_CTL_ACK_CPU2	BIT(21)
35b6a0e18cSAndreas Färber #define OWL_SPS_PG_CTL_ACK_CPU3	BIT(22)
36b6a0e18cSAndreas Färber 
37172067e0SAndreas Färber static void __iomem *scu_base_addr;
38b6a0e18cSAndreas Färber static void __iomem *sps_base_addr;
39172067e0SAndreas Färber static void __iomem *timer_base_addr;
40172067e0SAndreas Färber static int ncores;
41172067e0SAndreas Färber 
42172067e0SAndreas Färber static DEFINE_SPINLOCK(boot_lock);
43172067e0SAndreas Färber 
44172067e0SAndreas Färber static void write_pen_release(int val)
45172067e0SAndreas Färber {
46172067e0SAndreas Färber 	pen_release = val;
47172067e0SAndreas Färber 	smp_wmb();
48172067e0SAndreas Färber 	__cpuc_flush_dcache_area((void *)&pen_release, sizeof(pen_release));
49172067e0SAndreas Färber 	outer_clean_range(__pa(&pen_release), __pa(&pen_release + 1));
50172067e0SAndreas Färber }
51172067e0SAndreas Färber 
52172067e0SAndreas Färber static void s500_smp_secondary_init(unsigned int cpu)
53172067e0SAndreas Färber {
54172067e0SAndreas Färber 	/*
55172067e0SAndreas Färber 	 * let the primary processor know we're out of the
56172067e0SAndreas Färber 	 * pen, then head off into the C entry point
57172067e0SAndreas Färber 	 */
58172067e0SAndreas Färber 	write_pen_release(-1);
59172067e0SAndreas Färber 
60172067e0SAndreas Färber 	spin_lock(&boot_lock);
61172067e0SAndreas Färber 	spin_unlock(&boot_lock);
62172067e0SAndreas Färber }
63172067e0SAndreas Färber 
64172067e0SAndreas Färber void owl_secondary_startup(void);
65172067e0SAndreas Färber 
66172067e0SAndreas Färber static int s500_wakeup_secondary(unsigned int cpu)
67172067e0SAndreas Färber {
68b6a0e18cSAndreas Färber 	int ret;
69b6a0e18cSAndreas Färber 
70172067e0SAndreas Färber 	if (cpu > 3)
71172067e0SAndreas Färber 		return -EINVAL;
72172067e0SAndreas Färber 
73b6a0e18cSAndreas Färber 	/* The generic PM domain driver is not available this early. */
74172067e0SAndreas Färber 	switch (cpu) {
75172067e0SAndreas Färber 	case 2:
76b6a0e18cSAndreas Färber 		ret = owl_sps_set_pg(sps_base_addr,
77b6a0e18cSAndreas Färber 		                     OWL_SPS_PG_CTL_PWR_CPU2,
78b6a0e18cSAndreas Färber 				     OWL_SPS_PG_CTL_ACK_CPU2, true);
79b6a0e18cSAndreas Färber 		if (ret)
80b6a0e18cSAndreas Färber 			return ret;
81b6a0e18cSAndreas Färber 		break;
82172067e0SAndreas Färber 	case 3:
83b6a0e18cSAndreas Färber 		ret = owl_sps_set_pg(sps_base_addr,
84b6a0e18cSAndreas Färber 		                     OWL_SPS_PG_CTL_PWR_CPU3,
85b6a0e18cSAndreas Färber 				     OWL_SPS_PG_CTL_ACK_CPU3, true);
86b6a0e18cSAndreas Färber 		if (ret)
87b6a0e18cSAndreas Färber 			return ret;
88b6a0e18cSAndreas Färber 		break;
89172067e0SAndreas Färber 	}
90172067e0SAndreas Färber 
91172067e0SAndreas Färber 	/* wait for CPUx to run to WFE instruction */
92172067e0SAndreas Färber 	udelay(200);
93172067e0SAndreas Färber 
94172067e0SAndreas Färber 	writel(virt_to_phys(owl_secondary_startup),
95172067e0SAndreas Färber 	       timer_base_addr + OWL_CPU1_ADDR + (cpu - 1) * 4);
96172067e0SAndreas Färber 	writel(OWL_CPUx_FLAG_BOOT,
97172067e0SAndreas Färber 	       timer_base_addr + OWL_CPU1_FLAG + (cpu - 1) * 4);
98172067e0SAndreas Färber 
99172067e0SAndreas Färber 	dsb_sev();
100172067e0SAndreas Färber 	mb();
101172067e0SAndreas Färber 
102172067e0SAndreas Färber 	return 0;
103172067e0SAndreas Färber }
104172067e0SAndreas Färber 
105172067e0SAndreas Färber static int s500_smp_boot_secondary(unsigned int cpu, struct task_struct *idle)
106172067e0SAndreas Färber {
107172067e0SAndreas Färber 	unsigned long timeout;
108172067e0SAndreas Färber 	int ret;
109172067e0SAndreas Färber 
110172067e0SAndreas Färber 	ret = s500_wakeup_secondary(cpu);
111172067e0SAndreas Färber 	if (ret)
112172067e0SAndreas Färber 		return ret;
113172067e0SAndreas Färber 
114172067e0SAndreas Färber 	udelay(10);
115172067e0SAndreas Färber 
116172067e0SAndreas Färber 	spin_lock(&boot_lock);
117172067e0SAndreas Färber 
118172067e0SAndreas Färber 	/*
119172067e0SAndreas Färber 	 * The secondary processor is waiting to be released from
120172067e0SAndreas Färber 	 * the holding pen - release it, then wait for it to flag
121172067e0SAndreas Färber 	 * that it has been released by resetting pen_release.
122172067e0SAndreas Färber 	 */
123172067e0SAndreas Färber 	write_pen_release(cpu_logical_map(cpu));
124172067e0SAndreas Färber 	smp_send_reschedule(cpu);
125172067e0SAndreas Färber 
126172067e0SAndreas Färber 	timeout = jiffies + (1 * HZ);
127172067e0SAndreas Färber 	while (time_before(jiffies, timeout)) {
128172067e0SAndreas Färber 		if (pen_release == -1)
129172067e0SAndreas Färber 			break;
130172067e0SAndreas Färber 	}
131172067e0SAndreas Färber 
132172067e0SAndreas Färber 	writel(0, timer_base_addr + OWL_CPU1_ADDR + (cpu - 1) * 4);
133172067e0SAndreas Färber 	writel(0, timer_base_addr + OWL_CPU1_FLAG + (cpu - 1) * 4);
134172067e0SAndreas Färber 
135172067e0SAndreas Färber 	spin_unlock(&boot_lock);
136172067e0SAndreas Färber 
137172067e0SAndreas Färber 	return pen_release != -1 ? -ENOSYS : 0;
138172067e0SAndreas Färber }
139172067e0SAndreas Färber 
140172067e0SAndreas Färber static void __init s500_smp_prepare_cpus(unsigned int max_cpus)
141172067e0SAndreas Färber {
142172067e0SAndreas Färber 	struct device_node *node;
143172067e0SAndreas Färber 
144172067e0SAndreas Färber 	node = of_find_compatible_node(NULL, NULL, "actions,s500-timer");
145172067e0SAndreas Färber 	if (!node) {
146172067e0SAndreas Färber 		pr_err("%s: missing timer\n", __func__);
147172067e0SAndreas Färber 		return;
148172067e0SAndreas Färber 	}
149172067e0SAndreas Färber 
150172067e0SAndreas Färber 	timer_base_addr = of_iomap(node, 0);
151172067e0SAndreas Färber 	if (!timer_base_addr) {
152172067e0SAndreas Färber 		pr_err("%s: could not map timer registers\n", __func__);
153172067e0SAndreas Färber 		return;
154172067e0SAndreas Färber 	}
155172067e0SAndreas Färber 
156b6a0e18cSAndreas Färber 	node = of_find_compatible_node(NULL, NULL, "actions,s500-sps");
157b6a0e18cSAndreas Färber 	if (!node) {
158b6a0e18cSAndreas Färber 		pr_err("%s: missing sps\n", __func__);
159b6a0e18cSAndreas Färber 		return;
160b6a0e18cSAndreas Färber 	}
161b6a0e18cSAndreas Färber 
162b6a0e18cSAndreas Färber 	sps_base_addr = of_iomap(node, 0);
163b6a0e18cSAndreas Färber 	if (!sps_base_addr) {
164b6a0e18cSAndreas Färber 		pr_err("%s: could not map sps registers\n", __func__);
165b6a0e18cSAndreas Färber 		return;
166b6a0e18cSAndreas Färber 	}
167b6a0e18cSAndreas Färber 
168172067e0SAndreas Färber 	if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9) {
169172067e0SAndreas Färber 		node = of_find_compatible_node(NULL, NULL, "arm,cortex-a9-scu");
170172067e0SAndreas Färber 		if (!node) {
171172067e0SAndreas Färber 			pr_err("%s: missing scu\n", __func__);
172172067e0SAndreas Färber 			return;
173172067e0SAndreas Färber 		}
174172067e0SAndreas Färber 
175172067e0SAndreas Färber 		scu_base_addr = of_iomap(node, 0);
176172067e0SAndreas Färber 		if (!scu_base_addr) {
177172067e0SAndreas Färber 			pr_err("%s: could not map scu registers\n", __func__);
178172067e0SAndreas Färber 			return;
179172067e0SAndreas Färber 		}
180172067e0SAndreas Färber 
181172067e0SAndreas Färber 		/*
182172067e0SAndreas Färber 		 * While the number of cpus is gathered from dt, also get the
183172067e0SAndreas Färber 		 * number of cores from the scu to verify this value when
184172067e0SAndreas Färber 		 * booting the cores.
185172067e0SAndreas Färber 		 */
186172067e0SAndreas Färber 		ncores = scu_get_core_count(scu_base_addr);
187172067e0SAndreas Färber 		pr_debug("%s: ncores %d\n", __func__, ncores);
188172067e0SAndreas Färber 
189172067e0SAndreas Färber 		scu_enable(scu_base_addr);
190172067e0SAndreas Färber 	}
191172067e0SAndreas Färber }
192172067e0SAndreas Färber 
193172067e0SAndreas Färber static const struct smp_operations s500_smp_ops __initconst = {
194172067e0SAndreas Färber 	.smp_prepare_cpus = s500_smp_prepare_cpus,
195172067e0SAndreas Färber 	.smp_secondary_init = s500_smp_secondary_init,
196172067e0SAndreas Färber 	.smp_boot_secondary = s500_smp_boot_secondary,
197172067e0SAndreas Färber };
198172067e0SAndreas Färber CPU_METHOD_OF_DECLARE(s500_smp, "actions,s500-smp", &s500_smp_ops);
199