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