Merge branch 'linus' of git://git.kernel.org/pub/scm/linux/kernel/git/herbert/crypto-2.6
[linux-2.6-block.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 int s500_wakeup_secondary(unsigned int cpu)
43 {
44         int ret;
45
46         if (cpu > 3)
47                 return -EINVAL;
48
49         /* The generic PM domain driver is not available this early. */
50         switch (cpu) {
51         case 2:
52                 ret = owl_sps_set_pg(sps_base_addr,
53                                      OWL_SPS_PG_CTL_PWR_CPU2,
54                                      OWL_SPS_PG_CTL_ACK_CPU2, true);
55                 if (ret)
56                         return ret;
57                 break;
58         case 3:
59                 ret = owl_sps_set_pg(sps_base_addr,
60                                      OWL_SPS_PG_CTL_PWR_CPU3,
61                                      OWL_SPS_PG_CTL_ACK_CPU3, true);
62                 if (ret)
63                         return ret;
64                 break;
65         }
66
67         /* wait for CPUx to run to WFE instruction */
68         udelay(200);
69
70         writel(__pa_symbol(secondary_startup),
71                timer_base_addr + OWL_CPU1_ADDR + (cpu - 1) * 4);
72         writel(OWL_CPUx_FLAG_BOOT,
73                timer_base_addr + OWL_CPU1_FLAG + (cpu - 1) * 4);
74
75         dsb_sev();
76         mb();
77
78         return 0;
79 }
80
81 static int s500_smp_boot_secondary(unsigned int cpu, struct task_struct *idle)
82 {
83         int ret;
84
85         ret = s500_wakeup_secondary(cpu);
86         if (ret)
87                 return ret;
88
89         udelay(10);
90
91         smp_send_reschedule(cpu);
92
93         writel(0, timer_base_addr + OWL_CPU1_ADDR + (cpu - 1) * 4);
94         writel(0, timer_base_addr + OWL_CPU1_FLAG + (cpu - 1) * 4);
95
96         return 0;
97 }
98
99 static void __init s500_smp_prepare_cpus(unsigned int max_cpus)
100 {
101         struct device_node *node;
102
103         node = of_find_compatible_node(NULL, NULL, "actions,s500-timer");
104         if (!node) {
105                 pr_err("%s: missing timer\n", __func__);
106                 return;
107         }
108
109         timer_base_addr = of_iomap(node, 0);
110         if (!timer_base_addr) {
111                 pr_err("%s: could not map timer registers\n", __func__);
112                 return;
113         }
114
115         node = of_find_compatible_node(NULL, NULL, "actions,s500-sps");
116         if (!node) {
117                 pr_err("%s: missing sps\n", __func__);
118                 return;
119         }
120
121         sps_base_addr = of_iomap(node, 0);
122         if (!sps_base_addr) {
123                 pr_err("%s: could not map sps registers\n", __func__);
124                 return;
125         }
126
127         if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9) {
128                 node = of_find_compatible_node(NULL, NULL, "arm,cortex-a9-scu");
129                 if (!node) {
130                         pr_err("%s: missing scu\n", __func__);
131                         return;
132                 }
133
134                 scu_base_addr = of_iomap(node, 0);
135                 if (!scu_base_addr) {
136                         pr_err("%s: could not map scu registers\n", __func__);
137                         return;
138                 }
139
140                 /*
141                  * While the number of cpus is gathered from dt, also get the
142                  * number of cores from the scu to verify this value when
143                  * booting the cores.
144                  */
145                 ncores = scu_get_core_count(scu_base_addr);
146                 pr_debug("%s: ncores %d\n", __func__, ncores);
147
148                 scu_enable(scu_base_addr);
149         }
150 }
151
152 static const struct smp_operations s500_smp_ops __initconst = {
153         .smp_prepare_cpus = s500_smp_prepare_cpus,
154         .smp_boot_secondary = s500_smp_boot_secondary,
155 };
156 CPU_METHOD_OF_DECLARE(s500_smp, "actions,s500-smp", &s500_smp_ops);