12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2172067e0SAndreas Färber /*
3172067e0SAndreas Färber * Actions Semi Leopard
4172067e0SAndreas Färber *
5172067e0SAndreas Färber * This file is based on arm realview smp platform.
6172067e0SAndreas Färber *
7172067e0SAndreas Färber * Copyright 2012 Actions Semi Inc.
8172067e0SAndreas Färber * Author: Actions Semi, Inc.
9172067e0SAndreas Färber *
10172067e0SAndreas Färber * Copyright (c) 2017 Andreas Färber
11172067e0SAndreas Färber */
12172067e0SAndreas Färber
13172067e0SAndreas Färber #include <linux/delay.h>
14172067e0SAndreas Färber #include <linux/io.h>
15172067e0SAndreas Färber #include <linux/of.h>
16172067e0SAndreas Färber #include <linux/of_address.h>
17172067e0SAndreas Färber #include <linux/smp.h>
18b6a0e18cSAndreas Färber #include <linux/soc/actions/owl-sps.h>
19172067e0SAndreas Färber #include <asm/cacheflush.h>
20172067e0SAndreas Färber #include <asm/smp_plat.h>
21172067e0SAndreas Färber #include <asm/smp_scu.h>
22172067e0SAndreas Färber
23*4c8c3c7fSValentin Schneider #include <trace/events/ipi.h>
24*4c8c3c7fSValentin Schneider
25172067e0SAndreas Färber #define OWL_CPU1_ADDR 0x50
26172067e0SAndreas Färber #define OWL_CPU1_FLAG 0x5c
27172067e0SAndreas Färber
28172067e0SAndreas Färber #define OWL_CPUx_FLAG_BOOT 0x55aa
29172067e0SAndreas Färber
30b6a0e18cSAndreas Färber #define OWL_SPS_PG_CTL_PWR_CPU2 BIT(5)
31b6a0e18cSAndreas Färber #define OWL_SPS_PG_CTL_PWR_CPU3 BIT(6)
32b6a0e18cSAndreas Färber #define OWL_SPS_PG_CTL_ACK_CPU2 BIT(21)
33b6a0e18cSAndreas Färber #define OWL_SPS_PG_CTL_ACK_CPU3 BIT(22)
34b6a0e18cSAndreas Färber
35172067e0SAndreas Färber static void __iomem *scu_base_addr;
36b6a0e18cSAndreas Färber static void __iomem *sps_base_addr;
37172067e0SAndreas Färber static void __iomem *timer_base_addr;
38172067e0SAndreas Färber static int ncores;
39172067e0SAndreas Färber
s500_wakeup_secondary(unsigned int cpu)40172067e0SAndreas Färber static int s500_wakeup_secondary(unsigned int cpu)
41172067e0SAndreas Färber {
42b6a0e18cSAndreas Färber int ret;
43b6a0e18cSAndreas Färber
44172067e0SAndreas Färber if (cpu > 3)
45172067e0SAndreas Färber return -EINVAL;
46172067e0SAndreas Färber
47b6a0e18cSAndreas Färber /* The generic PM domain driver is not available this early. */
48172067e0SAndreas Färber switch (cpu) {
49172067e0SAndreas Färber case 2:
50b6a0e18cSAndreas Färber ret = owl_sps_set_pg(sps_base_addr,
51b6a0e18cSAndreas Färber OWL_SPS_PG_CTL_PWR_CPU2,
52b6a0e18cSAndreas Färber OWL_SPS_PG_CTL_ACK_CPU2, true);
53b6a0e18cSAndreas Färber if (ret)
54b6a0e18cSAndreas Färber return ret;
55b6a0e18cSAndreas Färber break;
56172067e0SAndreas Färber case 3:
57b6a0e18cSAndreas Färber ret = owl_sps_set_pg(sps_base_addr,
58b6a0e18cSAndreas Färber OWL_SPS_PG_CTL_PWR_CPU3,
59b6a0e18cSAndreas Färber OWL_SPS_PG_CTL_ACK_CPU3, true);
60b6a0e18cSAndreas Färber if (ret)
61b6a0e18cSAndreas Färber return ret;
62b6a0e18cSAndreas Färber break;
63172067e0SAndreas Färber }
64172067e0SAndreas Färber
65172067e0SAndreas Färber /* wait for CPUx to run to WFE instruction */
66172067e0SAndreas Färber udelay(200);
67172067e0SAndreas Färber
686c2eb3e7SAndreas Färber writel(__pa_symbol(secondary_startup),
69172067e0SAndreas Färber timer_base_addr + OWL_CPU1_ADDR + (cpu - 1) * 4);
70172067e0SAndreas Färber writel(OWL_CPUx_FLAG_BOOT,
71172067e0SAndreas Färber timer_base_addr + OWL_CPU1_FLAG + (cpu - 1) * 4);
72172067e0SAndreas Färber
73172067e0SAndreas Färber dsb_sev();
74172067e0SAndreas Färber mb();
75172067e0SAndreas Färber
76172067e0SAndreas Färber return 0;
77172067e0SAndreas Färber }
78172067e0SAndreas Färber
s500_smp_boot_secondary(unsigned int cpu,struct task_struct * idle)79172067e0SAndreas Färber static int s500_smp_boot_secondary(unsigned int cpu, struct task_struct *idle)
80172067e0SAndreas Färber {
81172067e0SAndreas Färber int ret;
82172067e0SAndreas Färber
83172067e0SAndreas Färber ret = s500_wakeup_secondary(cpu);
84172067e0SAndreas Färber if (ret)
85172067e0SAndreas Färber return ret;
86172067e0SAndreas Färber
87172067e0SAndreas Färber udelay(10);
88172067e0SAndreas Färber
89172067e0SAndreas Färber smp_send_reschedule(cpu);
90172067e0SAndreas Färber
91172067e0SAndreas Färber writel(0, timer_base_addr + OWL_CPU1_ADDR + (cpu - 1) * 4);
92172067e0SAndreas Färber writel(0, timer_base_addr + OWL_CPU1_FLAG + (cpu - 1) * 4);
93172067e0SAndreas Färber
9418cfd942SAndreas Färber return 0;
95172067e0SAndreas Färber }
96172067e0SAndreas Färber
s500_smp_prepare_cpus(unsigned int max_cpus)97172067e0SAndreas Färber static void __init s500_smp_prepare_cpus(unsigned int max_cpus)
98172067e0SAndreas Färber {
99172067e0SAndreas Färber struct device_node *node;
100172067e0SAndreas Färber
101172067e0SAndreas Färber node = of_find_compatible_node(NULL, NULL, "actions,s500-timer");
102172067e0SAndreas Färber if (!node) {
103172067e0SAndreas Färber pr_err("%s: missing timer\n", __func__);
104172067e0SAndreas Färber return;
105172067e0SAndreas Färber }
106172067e0SAndreas Färber
107172067e0SAndreas Färber timer_base_addr = of_iomap(node, 0);
108172067e0SAndreas Färber if (!timer_base_addr) {
109172067e0SAndreas Färber pr_err("%s: could not map timer registers\n", __func__);
110172067e0SAndreas Färber return;
111172067e0SAndreas Färber }
112172067e0SAndreas Färber
113b6a0e18cSAndreas Färber node = of_find_compatible_node(NULL, NULL, "actions,s500-sps");
114b6a0e18cSAndreas Färber if (!node) {
115b6a0e18cSAndreas Färber pr_err("%s: missing sps\n", __func__);
116b6a0e18cSAndreas Färber return;
117b6a0e18cSAndreas Färber }
118b6a0e18cSAndreas Färber
119b6a0e18cSAndreas Färber sps_base_addr = of_iomap(node, 0);
120b6a0e18cSAndreas Färber if (!sps_base_addr) {
121b6a0e18cSAndreas Färber pr_err("%s: could not map sps registers\n", __func__);
122b6a0e18cSAndreas Färber return;
123b6a0e18cSAndreas Färber }
124b6a0e18cSAndreas Färber
125172067e0SAndreas Färber if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9) {
126172067e0SAndreas Färber node = of_find_compatible_node(NULL, NULL, "arm,cortex-a9-scu");
127172067e0SAndreas Färber if (!node) {
128172067e0SAndreas Färber pr_err("%s: missing scu\n", __func__);
129172067e0SAndreas Färber return;
130172067e0SAndreas Färber }
131172067e0SAndreas Färber
132172067e0SAndreas Färber scu_base_addr = of_iomap(node, 0);
133172067e0SAndreas Färber if (!scu_base_addr) {
134172067e0SAndreas Färber pr_err("%s: could not map scu registers\n", __func__);
135172067e0SAndreas Färber return;
136172067e0SAndreas Färber }
137172067e0SAndreas Färber
138172067e0SAndreas Färber /*
139172067e0SAndreas Färber * While the number of cpus is gathered from dt, also get the
140172067e0SAndreas Färber * number of cores from the scu to verify this value when
141172067e0SAndreas Färber * booting the cores.
142172067e0SAndreas Färber */
143172067e0SAndreas Färber ncores = scu_get_core_count(scu_base_addr);
144172067e0SAndreas Färber pr_debug("%s: ncores %d\n", __func__, ncores);
145172067e0SAndreas Färber
146172067e0SAndreas Färber scu_enable(scu_base_addr);
147172067e0SAndreas Färber }
148172067e0SAndreas Färber }
149172067e0SAndreas Färber
150172067e0SAndreas Färber static const struct smp_operations s500_smp_ops __initconst = {
151172067e0SAndreas Färber .smp_prepare_cpus = s500_smp_prepare_cpus,
152172067e0SAndreas Färber .smp_boot_secondary = s500_smp_boot_secondary,
153172067e0SAndreas Färber };
154172067e0SAndreas Färber CPU_METHOD_OF_DECLARE(s500_smp, "actions,s500-smp", &s500_smp_ops);
155