]>
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 | { | |
95 | struct device_node *pmb_dn; | |
96 | struct of_phandle_args args; | |
97 | int ret; | |
98 | ||
99 | ret = of_property_read_u32(dn, "reg", cpu); | |
100 | if (ret) { | |
101 | pr_err("CPU is missing a reg node\n"); | |
102 | return ret; | |
103 | } | |
104 | ||
105 | ret = of_parse_phandle_with_args(dn, "resets", "#reset-cells", | |
106 | 0, &args); | |
107 | if (ret) { | |
108 | pr_err("CPU is missing a resets phandle\n"); | |
109 | return ret; | |
110 | } | |
111 | ||
112 | pmb_dn = args.np; | |
113 | if (args.args_count != 2) { | |
114 | pr_err("reset-controller does not conform to reset-cells\n"); | |
115 | return -EINVAL; | |
116 | } | |
117 | ||
118 | *base = of_iomap(args.np, 0); | |
119 | if (!*base) { | |
120 | pr_err("failed remapping PMB register\n"); | |
121 | return -ENOMEM; | |
122 | } | |
123 | ||
124 | /* We do not need the number of zones */ | |
125 | *addr = args.args[0]; | |
126 | ||
127 | return 0; | |
128 | } | |
129 | ||
130 | int bcm63xx_pmb_power_on_cpu(struct device_node *dn) | |
131 | { | |
132 | void __iomem *base; | |
133 | unsigned int cpu, addr; | |
134 | unsigned long flags; | |
135 | u32 val, ctrl; | |
136 | int ret; | |
137 | ||
138 | ret = bcm63xx_pmb_get_resources(dn, &base, &cpu, &addr); | |
139 | if (ret) | |
140 | return ret; | |
141 | ||
142 | /* We would not know how to enable a third and greater CPU */ | |
143 | WARN_ON(cpu > 1); | |
144 | ||
145 | spin_lock_irqsave(&pmb_lock, flags); | |
146 | ||
147 | /* Check if the CPU is already on and save the ARM_CONTROL register | |
148 | * value since we will use it later for CPU de-assert once done with | |
149 | * the CPU-specific power sequence | |
150 | */ | |
151 | ret = bpcm_rd(base, addr, ARM_CONTROL, &ctrl); | |
152 | if (ret) | |
3cc63056 | 153 | goto out; |
3f2a43c9 FF |
154 | |
155 | if (ctrl & CPU_RESET_N(cpu)) { | |
156 | pr_info("PMB: CPU%d is already powered on\n", cpu); | |
157 | ret = 0; | |
158 | goto out; | |
159 | } | |
160 | ||
161 | /* Power on PLL */ | |
162 | ret = bpcm_rd(base, addr, ARM_PWR_CONTROL(cpu), &val); | |
163 | if (ret) | |
164 | goto out; | |
165 | ||
166 | val |= (PWR_CPU_MASK << PWR_ON_SHIFT); | |
167 | ||
168 | ret = bpcm_wr_rd_mask(base, addr, ARM_PWR_CONTROL(cpu), &val, | |
169 | PWR_ON_STATUS_SHIFT, PWR_CPU_MASK, PWR_CPU_MASK); | |
170 | if (ret) | |
171 | goto out; | |
172 | ||
173 | val |= (PWR_CPU_MASK << PWR_OK_SHIFT); | |
174 | ||
175 | ret = bpcm_wr_rd_mask(base, addr, ARM_PWR_CONTROL(cpu), &val, | |
176 | PWR_OK_STATUS_SHIFT, PWR_CPU_MASK, PWR_CPU_MASK); | |
177 | if (ret) | |
178 | goto out; | |
179 | ||
180 | val &= ~CLAMP_ON; | |
181 | ||
182 | ret = bpcm_wr(base, addr, ARM_PWR_CONTROL(cpu), val); | |
183 | if (ret) | |
184 | goto out; | |
185 | ||
186 | /* Power on CPU<N> RAM */ | |
187 | val &= ~(MEM_PDA_MASK << MEM_PDA_SHIFT); | |
188 | ||
189 | ret = bpcm_wr(base, addr, ARM_PWR_CONTROL(cpu), val); | |
190 | if (ret) | |
191 | goto out; | |
192 | ||
193 | val |= MEM_PWR_ON; | |
194 | ||
195 | ret = bpcm_wr_rd_mask(base, addr, ARM_PWR_CONTROL(cpu), &val, | |
196 | 0, MEM_PWR_ON_STATUS, MEM_PWR_ON_STATUS); | |
197 | if (ret) | |
198 | goto out; | |
199 | ||
200 | val |= MEM_PWR_OK; | |
201 | ||
202 | ret = bpcm_wr_rd_mask(base, addr, ARM_PWR_CONTROL(cpu), &val, | |
203 | 0, MEM_PWR_OK_STATUS, MEM_PWR_OK_STATUS); | |
204 | if (ret) | |
205 | goto out; | |
206 | ||
207 | val &= ~MEM_CLAMP_ON; | |
208 | ||
209 | ret = bpcm_wr(base, addr, ARM_PWR_CONTROL(cpu), val); | |
210 | if (ret) | |
211 | goto out; | |
212 | ||
213 | /* De-assert CPU reset */ | |
214 | ctrl |= CPU_RESET_N(cpu); | |
215 | ||
216 | ret = bpcm_wr(base, addr, ARM_CONTROL, ctrl); | |
217 | out: | |
218 | spin_unlock_irqrestore(&pmb_lock, flags); | |
219 | iounmap(base); | |
220 | return ret; | |
221 | } |