1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Actions Semi Leopard
4  *
5  * This file is based on arm realview smp platform.
6  *
7  * Copyright 2012 Actions Semi Inc.
8  * Author: Actions Semi, Inc.
9  *
10  * Copyright (c) 2017 Andreas Färber
11  */
12 
13 #include <linux/delay.h>
14 #include <linux/io.h>
15 #include <linux/of.h>
16 #include <linux/of_address.h>
17 #include <linux/smp.h>
18 #include <linux/soc/actions/owl-sps.h>
19 #include <asm/cacheflush.h>
20 #include <asm/smp_plat.h>
21 #include <asm/smp_scu.h>
22 
23 #include <trace/events/ipi.h>
24 
25 #define OWL_CPU1_ADDR	0x50
26 #define OWL_CPU1_FLAG	0x5c
27 
28 #define OWL_CPUx_FLAG_BOOT	0x55aa
29 
30 #define OWL_SPS_PG_CTL_PWR_CPU2	BIT(5)
31 #define OWL_SPS_PG_CTL_PWR_CPU3	BIT(6)
32 #define OWL_SPS_PG_CTL_ACK_CPU2	BIT(21)
33 #define OWL_SPS_PG_CTL_ACK_CPU3	BIT(22)
34 
35 static void __iomem *scu_base_addr;
36 static void __iomem *sps_base_addr;
37 static void __iomem *timer_base_addr;
38 static int ncores;
39 
s500_wakeup_secondary(unsigned int cpu)40 static int s500_wakeup_secondary(unsigned int cpu)
41 {
42 	int ret;
43 
44 	if (cpu > 3)
45 		return -EINVAL;
46 
47 	/* The generic PM domain driver is not available this early. */
48 	switch (cpu) {
49 	case 2:
50 		ret = owl_sps_set_pg(sps_base_addr,
51 		                     OWL_SPS_PG_CTL_PWR_CPU2,
52 				     OWL_SPS_PG_CTL_ACK_CPU2, true);
53 		if (ret)
54 			return ret;
55 		break;
56 	case 3:
57 		ret = owl_sps_set_pg(sps_base_addr,
58 		                     OWL_SPS_PG_CTL_PWR_CPU3,
59 				     OWL_SPS_PG_CTL_ACK_CPU3, true);
60 		if (ret)
61 			return ret;
62 		break;
63 	}
64 
65 	/* wait for CPUx to run to WFE instruction */
66 	udelay(200);
67 
68 	writel(__pa_symbol(secondary_startup),
69 	       timer_base_addr + OWL_CPU1_ADDR + (cpu - 1) * 4);
70 	writel(OWL_CPUx_FLAG_BOOT,
71 	       timer_base_addr + OWL_CPU1_FLAG + (cpu - 1) * 4);
72 
73 	dsb_sev();
74 	mb();
75 
76 	return 0;
77 }
78 
s500_smp_boot_secondary(unsigned int cpu,struct task_struct * idle)79 static int s500_smp_boot_secondary(unsigned int cpu, struct task_struct *idle)
80 {
81 	int ret;
82 
83 	ret = s500_wakeup_secondary(cpu);
84 	if (ret)
85 		return ret;
86 
87 	udelay(10);
88 
89 	smp_send_reschedule(cpu);
90 
91 	writel(0, timer_base_addr + OWL_CPU1_ADDR + (cpu - 1) * 4);
92 	writel(0, timer_base_addr + OWL_CPU1_FLAG + (cpu - 1) * 4);
93 
94 	return 0;
95 }
96 
s500_smp_prepare_cpus(unsigned int max_cpus)97 static void __init s500_smp_prepare_cpus(unsigned int max_cpus)
98 {
99 	struct device_node *node;
100 
101 	node = of_find_compatible_node(NULL, NULL, "actions,s500-timer");
102 	if (!node) {
103 		pr_err("%s: missing timer\n", __func__);
104 		return;
105 	}
106 
107 	timer_base_addr = of_iomap(node, 0);
108 	if (!timer_base_addr) {
109 		pr_err("%s: could not map timer registers\n", __func__);
110 		return;
111 	}
112 
113 	node = of_find_compatible_node(NULL, NULL, "actions,s500-sps");
114 	if (!node) {
115 		pr_err("%s: missing sps\n", __func__);
116 		return;
117 	}
118 
119 	sps_base_addr = of_iomap(node, 0);
120 	if (!sps_base_addr) {
121 		pr_err("%s: could not map sps registers\n", __func__);
122 		return;
123 	}
124 
125 	if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9) {
126 		node = of_find_compatible_node(NULL, NULL, "arm,cortex-a9-scu");
127 		if (!node) {
128 			pr_err("%s: missing scu\n", __func__);
129 			return;
130 		}
131 
132 		scu_base_addr = of_iomap(node, 0);
133 		if (!scu_base_addr) {
134 			pr_err("%s: could not map scu registers\n", __func__);
135 			return;
136 		}
137 
138 		/*
139 		 * While the number of cpus is gathered from dt, also get the
140 		 * number of cores from the scu to verify this value when
141 		 * booting the cores.
142 		 */
143 		ncores = scu_get_core_count(scu_base_addr);
144 		pr_debug("%s: ncores %d\n", __func__, ncores);
145 
146 		scu_enable(scu_base_addr);
147 	}
148 }
149 
150 static const struct smp_operations s500_smp_ops __initconst = {
151 	.smp_prepare_cpus = s500_smp_prepare_cpus,
152 	.smp_boot_secondary = s500_smp_boot_secondary,
153 };
154 CPU_METHOD_OF_DECLARE(s500_smp, "actions,s500-smp", &s500_smp_ops);
155