]>
Commit | Line | Data |
---|---|---|
7b7dfdd2 AT |
1 | /* |
2 | * Copyright (C) 2014 Marvell Technology Group Ltd. | |
3 | * | |
4 | * Antoine Ténart <antoine.tenart@free-electrons.com> | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License version 2 as | |
8 | * published by the Free Software Foundation. | |
9 | */ | |
10 | ||
11 | #include <linux/io.h> | |
12 | #include <linux/delay.h> | |
13 | #include <linux/of.h> | |
14 | #include <linux/of_address.h> | |
15 | ||
16 | #include <asm/cacheflush.h> | |
a7b3d5a7 | 17 | #include <asm/cp15.h> |
7b7dfdd2 AT |
18 | #include <asm/smp_plat.h> |
19 | #include <asm/smp_scu.h> | |
20 | ||
ac7fc233 JZ |
21 | /* |
22 | * There are two reset registers, one with self-clearing (SC) | |
23 | * reset and one with non-self-clearing reset (NON_SC). | |
24 | */ | |
25 | #define CPU_RESET_SC 0x00 | |
26 | #define CPU_RESET_NON_SC 0x20 | |
7b7dfdd2 AT |
27 | |
28 | #define RESET_VECT 0x00 | |
29 | #define SW_RESET_ADDR 0x94 | |
30 | ||
7b7dfdd2 AT |
31 | extern u32 boot_inst; |
32 | ||
33 | static void __iomem *cpu_ctrl; | |
34 | ||
35 | static inline void berlin_perform_reset_cpu(unsigned int cpu) | |
36 | { | |
37 | u32 val; | |
38 | ||
ac7fc233 JZ |
39 | val = readl(cpu_ctrl + CPU_RESET_NON_SC); |
40 | val &= ~BIT(cpu_logical_map(cpu)); | |
41 | writel(val, cpu_ctrl + CPU_RESET_NON_SC); | |
7b7dfdd2 | 42 | val |= BIT(cpu_logical_map(cpu)); |
ac7fc233 | 43 | writel(val, cpu_ctrl + CPU_RESET_NON_SC); |
7b7dfdd2 AT |
44 | } |
45 | ||
46 | static int berlin_boot_secondary(unsigned int cpu, struct task_struct *idle) | |
47 | { | |
48 | if (!cpu_ctrl) | |
49 | return -EFAULT; | |
50 | ||
51 | /* | |
52 | * Reset the CPU, making it to execute the instruction in the reset | |
53 | * exception vector. | |
54 | */ | |
55 | berlin_perform_reset_cpu(cpu); | |
56 | ||
57 | return 0; | |
58 | } | |
59 | ||
60 | static void __init berlin_smp_prepare_cpus(unsigned int max_cpus) | |
61 | { | |
62 | struct device_node *np; | |
63 | void __iomem *scu_base; | |
64 | void __iomem *vectors_base; | |
65 | ||
66 | np = of_find_compatible_node(NULL, NULL, "arm,cortex-a9-scu"); | |
67 | scu_base = of_iomap(np, 0); | |
68 | of_node_put(np); | |
69 | if (!scu_base) | |
70 | return; | |
71 | ||
72 | np = of_find_compatible_node(NULL, NULL, "marvell,berlin-cpu-ctrl"); | |
73 | cpu_ctrl = of_iomap(np, 0); | |
74 | of_node_put(np); | |
75 | if (!cpu_ctrl) | |
76 | goto unmap_scu; | |
77 | ||
78 | vectors_base = ioremap(CONFIG_VECTORS_BASE, SZ_32K); | |
79 | if (!vectors_base) | |
80 | goto unmap_scu; | |
81 | ||
82 | scu_enable(scu_base); | |
83 | flush_cache_all(); | |
84 | ||
85 | /* | |
86 | * Write the first instruction the CPU will execute after being reset | |
87 | * in the reset exception vector. | |
88 | */ | |
89 | writel(boot_inst, vectors_base + RESET_VECT); | |
90 | ||
91 | /* | |
92 | * Write the secondary startup address into the SW reset address | |
93 | * vector. This is used by boot_inst. | |
94 | */ | |
02b4e275 | 95 | writel(virt_to_phys(secondary_startup), vectors_base + SW_RESET_ADDR); |
7b7dfdd2 AT |
96 | |
97 | iounmap(vectors_base); | |
98 | unmap_scu: | |
99 | iounmap(scu_base); | |
100 | } | |
101 | ||
a7b3d5a7 JZ |
102 | #ifdef CONFIG_HOTPLUG_CPU |
103 | static void berlin_cpu_die(unsigned int cpu) | |
104 | { | |
105 | v7_exit_coherency_flush(louis); | |
106 | while (1) | |
107 | cpu_do_idle(); | |
108 | } | |
109 | ||
110 | static int berlin_cpu_kill(unsigned int cpu) | |
111 | { | |
112 | u32 val; | |
113 | ||
114 | val = readl(cpu_ctrl + CPU_RESET_NON_SC); | |
115 | val &= ~BIT(cpu_logical_map(cpu)); | |
116 | writel(val, cpu_ctrl + CPU_RESET_NON_SC); | |
117 | ||
118 | return 1; | |
119 | } | |
120 | #endif | |
121 | ||
75305275 | 122 | static const struct smp_operations berlin_smp_ops __initconst = { |
7b7dfdd2 AT |
123 | .smp_prepare_cpus = berlin_smp_prepare_cpus, |
124 | .smp_boot_secondary = berlin_boot_secondary, | |
a7b3d5a7 JZ |
125 | #ifdef CONFIG_HOTPLUG_CPU |
126 | .cpu_die = berlin_cpu_die, | |
127 | .cpu_kill = berlin_cpu_kill, | |
128 | #endif | |
7b7dfdd2 AT |
129 | }; |
130 | CPU_METHOD_OF_DECLARE(berlin_smp, "marvell,berlin-smp", &berlin_smp_ops); |