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 | |
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(base: sps_base_addr, |
51 | OWL_SPS_PG_CTL_PWR_CPU2, |
52 | OWL_SPS_PG_CTL_ACK_CPU2, enable: true); |
53 | if (ret) |
54 | return ret; |
55 | break; |
56 | case 3: |
57 | ret = owl_sps_set_pg(base: sps_base_addr, |
58 | OWL_SPS_PG_CTL_PWR_CPU3, |
59 | OWL_SPS_PG_CTL_ACK_CPU3, enable: 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 | addr: timer_base_addr + OWL_CPU1_FLAG + (cpu - 1) * 4); |
72 | |
73 | dsb_sev(); |
74 | mb(); |
75 | |
76 | return 0; |
77 | } |
78 | |
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(val: 0, addr: timer_base_addr + OWL_CPU1_ADDR + (cpu - 1) * 4); |
92 | writel(val: 0, addr: timer_base_addr + OWL_CPU1_FLAG + (cpu - 1) * 4); |
93 | |
94 | return 0; |
95 | } |
96 | |
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, compat: "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, index: 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, compat: "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, index: 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, compat: "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, index: 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 | |