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