]> asedeno.scripts.mit.edu Git - linux.git/blob - arch/arm/mach-actions/platsmp.c
b4806ce0e9bbdba174e60eb6277d41a31042af74
[linux.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 static void write_pen_release(int val)
45 {
46         pen_release = val;
47         smp_wmb();
48         __cpuc_flush_dcache_area((void *)&pen_release, sizeof(pen_release));
49         outer_clean_range(__pa(&pen_release), __pa(&pen_release + 1));
50 }
51
52 static void s500_smp_secondary_init(unsigned int cpu)
53 {
54         /*
55          * let the primary processor know we're out of the
56          * pen, then head off into the C entry point
57          */
58         write_pen_release(-1);
59
60         spin_lock(&boot_lock);
61         spin_unlock(&boot_lock);
62 }
63
64 void owl_secondary_startup(void);
65
66 static int s500_wakeup_secondary(unsigned int cpu)
67 {
68         int ret;
69
70         if (cpu > 3)
71                 return -EINVAL;
72
73         /* The generic PM domain driver is not available this early. */
74         switch (cpu) {
75         case 2:
76                 ret = owl_sps_set_pg(sps_base_addr,
77                                      OWL_SPS_PG_CTL_PWR_CPU2,
78                                      OWL_SPS_PG_CTL_ACK_CPU2, true);
79                 if (ret)
80                         return ret;
81                 break;
82         case 3:
83                 ret = owl_sps_set_pg(sps_base_addr,
84                                      OWL_SPS_PG_CTL_PWR_CPU3,
85                                      OWL_SPS_PG_CTL_ACK_CPU3, true);
86                 if (ret)
87                         return ret;
88                 break;
89         }
90
91         /* wait for CPUx to run to WFE instruction */
92         udelay(200);
93
94         writel(virt_to_phys(owl_secondary_startup),
95                timer_base_addr + OWL_CPU1_ADDR + (cpu - 1) * 4);
96         writel(OWL_CPUx_FLAG_BOOT,
97                timer_base_addr + OWL_CPU1_FLAG + (cpu - 1) * 4);
98
99         dsb_sev();
100         mb();
101
102         return 0;
103 }
104
105 static int s500_smp_boot_secondary(unsigned int cpu, struct task_struct *idle)
106 {
107         unsigned long timeout;
108         int ret;
109
110         ret = s500_wakeup_secondary(cpu);
111         if (ret)
112                 return ret;
113
114         udelay(10);
115
116         spin_lock(&boot_lock);
117
118         /*
119          * The secondary processor is waiting to be released from
120          * the holding pen - release it, then wait for it to flag
121          * that it has been released by resetting pen_release.
122          */
123         write_pen_release(cpu_logical_map(cpu));
124         smp_send_reschedule(cpu);
125
126         timeout = jiffies + (1 * HZ);
127         while (time_before(jiffies, timeout)) {
128                 if (pen_release == -1)
129                         break;
130         }
131
132         writel(0, timer_base_addr + OWL_CPU1_ADDR + (cpu - 1) * 4);
133         writel(0, timer_base_addr + OWL_CPU1_FLAG + (cpu - 1) * 4);
134
135         spin_unlock(&boot_lock);
136
137         return pen_release != -1 ? -ENOSYS : 0;
138 }
139
140 static void __init s500_smp_prepare_cpus(unsigned int max_cpus)
141 {
142         struct device_node *node;
143
144         node = of_find_compatible_node(NULL, NULL, "actions,s500-timer");
145         if (!node) {
146                 pr_err("%s: missing timer\n", __func__);
147                 return;
148         }
149
150         timer_base_addr = of_iomap(node, 0);
151         if (!timer_base_addr) {
152                 pr_err("%s: could not map timer registers\n", __func__);
153                 return;
154         }
155
156         node = of_find_compatible_node(NULL, NULL, "actions,s500-sps");
157         if (!node) {
158                 pr_err("%s: missing sps\n", __func__);
159                 return;
160         }
161
162         sps_base_addr = of_iomap(node, 0);
163         if (!sps_base_addr) {
164                 pr_err("%s: could not map sps registers\n", __func__);
165                 return;
166         }
167
168         if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9) {
169                 node = of_find_compatible_node(NULL, NULL, "arm,cortex-a9-scu");
170                 if (!node) {
171                         pr_err("%s: missing scu\n", __func__);
172                         return;
173                 }
174
175                 scu_base_addr = of_iomap(node, 0);
176                 if (!scu_base_addr) {
177                         pr_err("%s: could not map scu registers\n", __func__);
178                         return;
179                 }
180
181                 /*
182                  * While the number of cpus is gathered from dt, also get the
183                  * number of cores from the scu to verify this value when
184                  * booting the cores.
185                  */
186                 ncores = scu_get_core_count(scu_base_addr);
187                 pr_debug("%s: ncores %d\n", __func__, ncores);
188
189                 scu_enable(scu_base_addr);
190         }
191 }
192
193 static const struct smp_operations s500_smp_ops __initconst = {
194         .smp_prepare_cpus = s500_smp_prepare_cpus,
195         .smp_secondary_init = s500_smp_secondary_init,
196         .smp_boot_secondary = s500_smp_boot_secondary,
197 };
198 CPU_METHOD_OF_DECLARE(s500_smp, "actions,s500-smp", &s500_smp_ops);