1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Broadcom BCM63138 PMB initialization for secondary CPU(s) |
4 | * |
5 | * Copyright (C) 2015 Broadcom Corporation |
6 | * Author: Florian Fainelli <f.fainelli@gmail.com> |
7 | */ |
8 | #include <linux/kernel.h> |
9 | #include <linux/io.h> |
10 | #include <linux/spinlock.h> |
11 | #include <linux/reset/bcm63xx_pmb.h> |
12 | #include <linux/of.h> |
13 | #include <linux/of_address.h> |
14 | |
15 | #include "bcm63xx_smp.h" |
16 | |
17 | /* ARM Control register definitions */ |
18 | #define CORE_PWR_CTRL_SHIFT 0 |
19 | #define CORE_PWR_CTRL_MASK 0x3 |
20 | #define PLL_PWR_ON BIT(8) |
21 | #define PLL_LDO_PWR_ON BIT(9) |
22 | #define PLL_CLAMP_ON BIT(10) |
23 | #define CPU_RESET_N(x) BIT(13 + (x)) |
24 | #define NEON_RESET_N BIT(15) |
25 | #define PWR_CTRL_STATUS_SHIFT 28 |
26 | #define PWR_CTRL_STATUS_MASK 0x3 |
27 | #define PWR_DOWN_SHIFT 30 |
28 | #define PWR_DOWN_MASK 0x3 |
29 | |
30 | /* CPU Power control register definitions */ |
31 | #define MEM_PWR_OK BIT(0) |
32 | #define MEM_PWR_ON BIT(1) |
33 | #define MEM_CLAMP_ON BIT(2) |
34 | #define MEM_PWR_OK_STATUS BIT(4) |
35 | #define MEM_PWR_ON_STATUS BIT(5) |
36 | #define MEM_PDA_SHIFT 8 |
37 | #define MEM_PDA_MASK 0xf |
38 | #define MEM_PDA_CPU_MASK 0x1 |
39 | #define MEM_PDA_NEON_MASK 0xf |
40 | #define CLAMP_ON BIT(15) |
41 | #define PWR_OK_SHIFT 16 |
42 | #define PWR_OK_MASK 0xf |
43 | #define PWR_ON_SHIFT 20 |
44 | #define PWR_CPU_MASK 0x03 |
45 | #define PWR_NEON_MASK 0x01 |
46 | #define PWR_ON_MASK 0xf |
47 | #define PWR_OK_STATUS_SHIFT 24 |
48 | #define PWR_OK_STATUS_MASK 0xf |
49 | #define PWR_ON_STATUS_SHIFT 28 |
50 | #define PWR_ON_STATUS_MASK 0xf |
51 | |
52 | #define ARM_CONTROL 0x30 |
53 | #define ARM_PWR_CONTROL_BASE 0x34 |
54 | #define ARM_PWR_CONTROL(x) (ARM_PWR_CONTROL_BASE + (x) * 0x4) |
55 | #define ARM_NEON_L2 0x3c |
56 | |
57 | /* Perform a value write, then spin until the value shifted by |
58 | * shift is seen, masked with mask and is different from cond. |
59 | */ |
60 | static int bpcm_wr_rd_mask(void __iomem *master, |
61 | unsigned int addr, u32 off, u32 *val, |
62 | u32 shift, u32 mask, u32 cond) |
63 | { |
64 | int ret; |
65 | |
66 | ret = bpcm_wr(master, addr, off, val: *val); |
67 | if (ret) |
68 | return ret; |
69 | |
70 | do { |
71 | ret = bpcm_rd(master, addr, off, val); |
72 | if (ret) |
73 | return ret; |
74 | |
75 | cpu_relax(); |
76 | } while (((*val >> shift) & mask) != cond); |
77 | |
78 | return ret; |
79 | } |
80 | |
81 | /* Global lock to serialize accesses to the PMB registers while we |
82 | * are bringing up the secondary CPU |
83 | */ |
84 | static DEFINE_SPINLOCK(pmb_lock); |
85 | |
86 | static int bcm63xx_pmb_get_resources(struct device_node *dn, |
87 | void __iomem **base, |
88 | unsigned int *cpu, |
89 | unsigned int *addr) |
90 | { |
91 | struct of_phandle_args args; |
92 | int ret; |
93 | |
94 | *cpu = of_get_cpu_hwid(cpun: dn, thread: 0); |
95 | if (*cpu == ~0U) { |
96 | pr_err("CPU is missing a reg node\n" ); |
97 | return -ENODEV; |
98 | } |
99 | |
100 | ret = of_parse_phandle_with_args(np: dn, list_name: "resets" , cells_name: "#reset-cells" , |
101 | index: 0, out_args: &args); |
102 | if (ret) { |
103 | pr_err("CPU is missing a resets phandle\n" ); |
104 | return ret; |
105 | } |
106 | |
107 | if (args.args_count != 2) { |
108 | pr_err("reset-controller does not conform to reset-cells\n" ); |
109 | return -EINVAL; |
110 | } |
111 | |
112 | *base = of_iomap(node: args.np, index: 0); |
113 | if (!*base) { |
114 | pr_err("failed remapping PMB register\n" ); |
115 | return -ENOMEM; |
116 | } |
117 | |
118 | /* We do not need the number of zones */ |
119 | *addr = args.args[0]; |
120 | |
121 | return 0; |
122 | } |
123 | |
124 | int bcm63xx_pmb_power_on_cpu(struct device_node *dn) |
125 | { |
126 | void __iomem *base; |
127 | unsigned int cpu, addr; |
128 | unsigned long flags; |
129 | u32 val, ctrl; |
130 | int ret; |
131 | |
132 | ret = bcm63xx_pmb_get_resources(dn, base: &base, cpu: &cpu, addr: &addr); |
133 | if (ret) |
134 | return ret; |
135 | |
136 | /* We would not know how to enable a third and greater CPU */ |
137 | WARN_ON(cpu > 1); |
138 | |
139 | spin_lock_irqsave(&pmb_lock, flags); |
140 | |
141 | /* Check if the CPU is already on and save the ARM_CONTROL register |
142 | * value since we will use it later for CPU de-assert once done with |
143 | * the CPU-specific power sequence |
144 | */ |
145 | ret = bpcm_rd(master: base, addr, ARM_CONTROL, val: &ctrl); |
146 | if (ret) |
147 | goto out; |
148 | |
149 | if (ctrl & CPU_RESET_N(cpu)) { |
150 | pr_info("PMB: CPU%d is already powered on\n" , cpu); |
151 | ret = 0; |
152 | goto out; |
153 | } |
154 | |
155 | /* Power on PLL */ |
156 | ret = bpcm_rd(master: base, addr, ARM_PWR_CONTROL(cpu), val: &val); |
157 | if (ret) |
158 | goto out; |
159 | |
160 | val |= (PWR_CPU_MASK << PWR_ON_SHIFT); |
161 | |
162 | ret = bpcm_wr_rd_mask(master: base, addr, ARM_PWR_CONTROL(cpu), val: &val, |
163 | PWR_ON_STATUS_SHIFT, PWR_CPU_MASK, PWR_CPU_MASK); |
164 | if (ret) |
165 | goto out; |
166 | |
167 | val |= (PWR_CPU_MASK << PWR_OK_SHIFT); |
168 | |
169 | ret = bpcm_wr_rd_mask(master: base, addr, ARM_PWR_CONTROL(cpu), val: &val, |
170 | PWR_OK_STATUS_SHIFT, PWR_CPU_MASK, PWR_CPU_MASK); |
171 | if (ret) |
172 | goto out; |
173 | |
174 | val &= ~CLAMP_ON; |
175 | |
176 | ret = bpcm_wr(master: base, addr, ARM_PWR_CONTROL(cpu), val); |
177 | if (ret) |
178 | goto out; |
179 | |
180 | /* Power on CPU<N> RAM */ |
181 | val &= ~(MEM_PDA_MASK << MEM_PDA_SHIFT); |
182 | |
183 | ret = bpcm_wr(master: base, addr, ARM_PWR_CONTROL(cpu), val); |
184 | if (ret) |
185 | goto out; |
186 | |
187 | val |= MEM_PWR_ON; |
188 | |
189 | ret = bpcm_wr_rd_mask(master: base, addr, ARM_PWR_CONTROL(cpu), val: &val, |
190 | shift: 0, MEM_PWR_ON_STATUS, MEM_PWR_ON_STATUS); |
191 | if (ret) |
192 | goto out; |
193 | |
194 | val |= MEM_PWR_OK; |
195 | |
196 | ret = bpcm_wr_rd_mask(master: base, addr, ARM_PWR_CONTROL(cpu), val: &val, |
197 | shift: 0, MEM_PWR_OK_STATUS, MEM_PWR_OK_STATUS); |
198 | if (ret) |
199 | goto out; |
200 | |
201 | val &= ~MEM_CLAMP_ON; |
202 | |
203 | ret = bpcm_wr(master: base, addr, ARM_PWR_CONTROL(cpu), val); |
204 | if (ret) |
205 | goto out; |
206 | |
207 | /* De-assert CPU reset */ |
208 | ctrl |= CPU_RESET_N(cpu); |
209 | |
210 | ret = bpcm_wr(master: base, addr, ARM_CONTROL, val: ctrl); |
211 | out: |
212 | spin_unlock_irqrestore(lock: &pmb_lock, flags); |
213 | iounmap(addr: base); |
214 | return ret; |
215 | } |
216 | |