xref: /openbmc/linux/arch/arm/mach-actions/platsmp.c (revision 4c8c3c7f)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2172067e0SAndreas Färber /*
3172067e0SAndreas Färber  * Actions Semi Leopard
4172067e0SAndreas Färber  *
5172067e0SAndreas Färber  * This file is based on arm realview smp platform.
6172067e0SAndreas Färber  *
7172067e0SAndreas Färber  * Copyright 2012 Actions Semi Inc.
8172067e0SAndreas Färber  * Author: Actions Semi, Inc.
9172067e0SAndreas Färber  *
10172067e0SAndreas Färber  * Copyright (c) 2017 Andreas Färber
11172067e0SAndreas Färber  */
12172067e0SAndreas Färber 
13172067e0SAndreas Färber #include <linux/delay.h>
14172067e0SAndreas Färber #include <linux/io.h>
15172067e0SAndreas Färber #include <linux/of.h>
16172067e0SAndreas Färber #include <linux/of_address.h>
17172067e0SAndreas Färber #include <linux/smp.h>
18b6a0e18cSAndreas Färber #include <linux/soc/actions/owl-sps.h>
19172067e0SAndreas Färber #include <asm/cacheflush.h>
20172067e0SAndreas Färber #include <asm/smp_plat.h>
21172067e0SAndreas Färber #include <asm/smp_scu.h>
22172067e0SAndreas Färber 
23*4c8c3c7fSValentin Schneider #include <trace/events/ipi.h>
24*4c8c3c7fSValentin Schneider 
25172067e0SAndreas Färber #define OWL_CPU1_ADDR	0x50
26172067e0SAndreas Färber #define OWL_CPU1_FLAG	0x5c
27172067e0SAndreas Färber 
28172067e0SAndreas Färber #define OWL_CPUx_FLAG_BOOT	0x55aa
29172067e0SAndreas Färber 
30b6a0e18cSAndreas Färber #define OWL_SPS_PG_CTL_PWR_CPU2	BIT(5)
31b6a0e18cSAndreas Färber #define OWL_SPS_PG_CTL_PWR_CPU3	BIT(6)
32b6a0e18cSAndreas Färber #define OWL_SPS_PG_CTL_ACK_CPU2	BIT(21)
33b6a0e18cSAndreas Färber #define OWL_SPS_PG_CTL_ACK_CPU3	BIT(22)
34b6a0e18cSAndreas Färber 
35172067e0SAndreas Färber static void __iomem *scu_base_addr;
36b6a0e18cSAndreas Färber static void __iomem *sps_base_addr;
37172067e0SAndreas Färber static void __iomem *timer_base_addr;
38172067e0SAndreas Färber static int ncores;
39172067e0SAndreas Färber 
s500_wakeup_secondary(unsigned int cpu)40172067e0SAndreas Färber static int s500_wakeup_secondary(unsigned int cpu)
41172067e0SAndreas Färber {
42b6a0e18cSAndreas Färber 	int ret;
43b6a0e18cSAndreas Färber 
44172067e0SAndreas Färber 	if (cpu > 3)
45172067e0SAndreas Färber 		return -EINVAL;
46172067e0SAndreas Färber 
47b6a0e18cSAndreas Färber 	/* The generic PM domain driver is not available this early. */
48172067e0SAndreas Färber 	switch (cpu) {
49172067e0SAndreas Färber 	case 2:
50b6a0e18cSAndreas Färber 		ret = owl_sps_set_pg(sps_base_addr,
51b6a0e18cSAndreas Färber 		                     OWL_SPS_PG_CTL_PWR_CPU2,
52b6a0e18cSAndreas Färber 				     OWL_SPS_PG_CTL_ACK_CPU2, true);
53b6a0e18cSAndreas Färber 		if (ret)
54b6a0e18cSAndreas Färber 			return ret;
55b6a0e18cSAndreas Färber 		break;
56172067e0SAndreas Färber 	case 3:
57b6a0e18cSAndreas Färber 		ret = owl_sps_set_pg(sps_base_addr,
58b6a0e18cSAndreas Färber 		                     OWL_SPS_PG_CTL_PWR_CPU3,
59b6a0e18cSAndreas Färber 				     OWL_SPS_PG_CTL_ACK_CPU3, true);
60b6a0e18cSAndreas Färber 		if (ret)
61b6a0e18cSAndreas Färber 			return ret;
62b6a0e18cSAndreas Färber 		break;
63172067e0SAndreas Färber 	}
64172067e0SAndreas Färber 
65172067e0SAndreas Färber 	/* wait for CPUx to run to WFE instruction */
66172067e0SAndreas Färber 	udelay(200);
67172067e0SAndreas Färber 
686c2eb3e7SAndreas Färber 	writel(__pa_symbol(secondary_startup),
69172067e0SAndreas Färber 	       timer_base_addr + OWL_CPU1_ADDR + (cpu - 1) * 4);
70172067e0SAndreas Färber 	writel(OWL_CPUx_FLAG_BOOT,
71172067e0SAndreas Färber 	       timer_base_addr + OWL_CPU1_FLAG + (cpu - 1) * 4);
72172067e0SAndreas Färber 
73172067e0SAndreas Färber 	dsb_sev();
74172067e0SAndreas Färber 	mb();
75172067e0SAndreas Färber 
76172067e0SAndreas Färber 	return 0;
77172067e0SAndreas Färber }
78172067e0SAndreas Färber 
s500_smp_boot_secondary(unsigned int cpu,struct task_struct * idle)79172067e0SAndreas Färber static int s500_smp_boot_secondary(unsigned int cpu, struct task_struct *idle)
80172067e0SAndreas Färber {
81172067e0SAndreas Färber 	int ret;
82172067e0SAndreas Färber 
83172067e0SAndreas Färber 	ret = s500_wakeup_secondary(cpu);
84172067e0SAndreas Färber 	if (ret)
85172067e0SAndreas Färber 		return ret;
86172067e0SAndreas Färber 
87172067e0SAndreas Färber 	udelay(10);
88172067e0SAndreas Färber 
89172067e0SAndreas Färber 	smp_send_reschedule(cpu);
90172067e0SAndreas Färber 
91172067e0SAndreas Färber 	writel(0, timer_base_addr + OWL_CPU1_ADDR + (cpu - 1) * 4);
92172067e0SAndreas Färber 	writel(0, timer_base_addr + OWL_CPU1_FLAG + (cpu - 1) * 4);
93172067e0SAndreas Färber 
9418cfd942SAndreas Färber 	return 0;
95172067e0SAndreas Färber }
96172067e0SAndreas Färber 
s500_smp_prepare_cpus(unsigned int max_cpus)97172067e0SAndreas Färber static void __init s500_smp_prepare_cpus(unsigned int max_cpus)
98172067e0SAndreas Färber {
99172067e0SAndreas Färber 	struct device_node *node;
100172067e0SAndreas Färber 
101172067e0SAndreas Färber 	node = of_find_compatible_node(NULL, NULL, "actions,s500-timer");
102172067e0SAndreas Färber 	if (!node) {
103172067e0SAndreas Färber 		pr_err("%s: missing timer\n", __func__);
104172067e0SAndreas Färber 		return;
105172067e0SAndreas Färber 	}
106172067e0SAndreas Färber 
107172067e0SAndreas Färber 	timer_base_addr = of_iomap(node, 0);
108172067e0SAndreas Färber 	if (!timer_base_addr) {
109172067e0SAndreas Färber 		pr_err("%s: could not map timer registers\n", __func__);
110172067e0SAndreas Färber 		return;
111172067e0SAndreas Färber 	}
112172067e0SAndreas Färber 
113b6a0e18cSAndreas Färber 	node = of_find_compatible_node(NULL, NULL, "actions,s500-sps");
114b6a0e18cSAndreas Färber 	if (!node) {
115b6a0e18cSAndreas Färber 		pr_err("%s: missing sps\n", __func__);
116b6a0e18cSAndreas Färber 		return;
117b6a0e18cSAndreas Färber 	}
118b6a0e18cSAndreas Färber 
119b6a0e18cSAndreas Färber 	sps_base_addr = of_iomap(node, 0);
120b6a0e18cSAndreas Färber 	if (!sps_base_addr) {
121b6a0e18cSAndreas Färber 		pr_err("%s: could not map sps registers\n", __func__);
122b6a0e18cSAndreas Färber 		return;
123b6a0e18cSAndreas Färber 	}
124b6a0e18cSAndreas Färber 
125172067e0SAndreas Färber 	if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9) {
126172067e0SAndreas Färber 		node = of_find_compatible_node(NULL, NULL, "arm,cortex-a9-scu");
127172067e0SAndreas Färber 		if (!node) {
128172067e0SAndreas Färber 			pr_err("%s: missing scu\n", __func__);
129172067e0SAndreas Färber 			return;
130172067e0SAndreas Färber 		}
131172067e0SAndreas Färber 
132172067e0SAndreas Färber 		scu_base_addr = of_iomap(node, 0);
133172067e0SAndreas Färber 		if (!scu_base_addr) {
134172067e0SAndreas Färber 			pr_err("%s: could not map scu registers\n", __func__);
135172067e0SAndreas Färber 			return;
136172067e0SAndreas Färber 		}
137172067e0SAndreas Färber 
138172067e0SAndreas Färber 		/*
139172067e0SAndreas Färber 		 * While the number of cpus is gathered from dt, also get the
140172067e0SAndreas Färber 		 * number of cores from the scu to verify this value when
141172067e0SAndreas Färber 		 * booting the cores.
142172067e0SAndreas Färber 		 */
143172067e0SAndreas Färber 		ncores = scu_get_core_count(scu_base_addr);
144172067e0SAndreas Färber 		pr_debug("%s: ncores %d\n", __func__, ncores);
145172067e0SAndreas Färber 
146172067e0SAndreas Färber 		scu_enable(scu_base_addr);
147172067e0SAndreas Färber 	}
148172067e0SAndreas Färber }
149172067e0SAndreas Färber 
150172067e0SAndreas Färber static const struct smp_operations s500_smp_ops __initconst = {
151172067e0SAndreas Färber 	.smp_prepare_cpus = s500_smp_prepare_cpus,
152172067e0SAndreas Färber 	.smp_boot_secondary = s500_smp_boot_secondary,
153172067e0SAndreas Färber };
154172067e0SAndreas Färber CPU_METHOD_OF_DECLARE(s500_smp, "actions,s500-smp", &s500_smp_ops);
155