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