1 /*
2 * xen/arch/arm/platforms/brcm.c
3 *
4 * Broadcom Platform startup.
5 *
6 * Jon Fraser <jfraser@broadcom.com>
7 * Copyright (c) 2013-2014 Broadcom Corporation
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 */
19
20 #include <asm/platform.h>
21 #include <xen/mm.h>
22 #include <xen/vmap.h>
23 #include <asm/io.h>
24 #include <xen/delay.h>
25
26 struct brcm_plat_regs {
27 uint32_t hif_mask;
28 uint32_t hif_cpu_reset_config;
29 uint32_t hif_boot_continuation;
30 uint32_t cpu0_pwr_zone_ctrl;
31 uint32_t scratch_reg;
32 };
33
34 static u32 brcm_boot_continuation_pc;
35
36 static struct brcm_plat_regs regs;
37
brcm_get_dt_node(char * compat_str,const struct dt_device_node ** dn,u32 * reg_base)38 static __init int brcm_get_dt_node(char *compat_str,
39 const struct dt_device_node **dn,
40 u32 *reg_base)
41 {
42 const struct dt_device_node *node;
43 u64 reg_base_64;
44 int rc;
45
46 node = dt_find_compatible_node(NULL, NULL, compat_str);
47 if ( !node )
48 {
49 dprintk(XENLOG_ERR, "%s: missing \"%s\" node\n", __func__, compat_str);
50 return -ENOENT;
51 }
52
53 rc = dt_device_get_address(node, 0, ®_base_64, NULL);
54 if ( rc )
55 {
56 dprintk(XENLOG_ERR, "%s: missing \"reg\" prop\n", __func__);
57 return rc;
58 }
59
60 if ( dn )
61 *dn = node;
62
63 if ( reg_base )
64 *reg_base = reg_base_64;
65
66 return 0;
67 }
68
brcm_populate_plat_regs(void)69 static __init int brcm_populate_plat_regs(void)
70 {
71 int rc;
72 const struct dt_device_node *node;
73 u32 reg_base;
74 u32 val;
75
76 rc = brcm_get_dt_node("brcm,brcmstb-cpu-biu-ctrl", &node, ®_base);
77 if ( rc )
78 return rc;
79
80 if ( !dt_property_read_u32(node, "cpu-reset-config-reg", &val) )
81 {
82 dprintk(XENLOG_ERR, "Missing property \"cpu-reset-config-reg\"\n");
83 return -ENOENT;
84 }
85 regs.hif_cpu_reset_config = reg_base + val;
86
87 if ( !dt_property_read_u32(node, "cpu0-pwr-zone-ctrl-reg", &val) )
88 {
89 dprintk(XENLOG_ERR, "Missing property \"cpu0-pwr-zone-ctrl-reg\"\n");
90 return -ENOENT;
91 }
92 regs.cpu0_pwr_zone_ctrl = reg_base + val;
93
94 if ( !dt_property_read_u32(node, "scratch-reg", &val) )
95 {
96 dprintk(XENLOG_ERR, "Missing property \"scratch-reg\"\n");
97 return -ENOENT;
98 }
99 regs.scratch_reg = reg_base + val;
100
101 rc = brcm_get_dt_node("brcm,brcmstb-hif-continuation", NULL, ®_base);
102 if ( rc )
103 return rc;
104
105 regs.hif_boot_continuation = reg_base;
106
107 dprintk(XENLOG_INFO, "hif_cpu_reset_config : %08xh\n",
108 regs.hif_cpu_reset_config);
109 dprintk(XENLOG_INFO, "cpu0_pwr_zone_ctrl : %08xh\n",
110 regs.cpu0_pwr_zone_ctrl);
111 dprintk(XENLOG_INFO, "hif_boot_continuation : %08xh\n",
112 regs.hif_boot_continuation);
113 dprintk(XENLOG_INFO, "scratch_reg : %08xh\n",
114 regs.scratch_reg);
115
116 return 0;
117 }
118
119 #define ZONE_PWR_UP_REQ (1 << 10)
120 #define ZONE_PWR_ON_STATE (1 << 26)
121
brcm_cpu_power_on(int cpu)122 static int brcm_cpu_power_on(int cpu)
123 {
124 u32 tmp;
125 void __iomem *pwr_ctl;
126 unsigned int timeout;
127
128 dprintk(XENLOG_ERR, "%s: Power on cpu %d\n", __func__, cpu);
129
130 pwr_ctl = ioremap_nocache(regs.cpu0_pwr_zone_ctrl + (cpu * sizeof(u32)),
131 sizeof(u32));
132
133 if ( !pwr_ctl )
134 {
135 dprintk(XENLOG_ERR, "%s: Unable to map \"cpu0_pwr_zone_ctrl\"\n",
136 __func__);
137 return -EFAULT;
138 }
139
140 /* request core power on */
141 tmp = readl(pwr_ctl);
142 tmp |= ZONE_PWR_UP_REQ;
143 writel(tmp, pwr_ctl);
144
145 /*
146 * Wait for the cpu to power on.
147 * Wait a max of 10 msec.
148 */
149 timeout = 10;
150 tmp = readl(pwr_ctl);
151
152 while ( !(tmp & ZONE_PWR_ON_STATE) )
153 {
154 if ( timeout-- == 0 )
155 break;
156
157 mdelay(1);
158 tmp = readl(pwr_ctl);
159 }
160
161 iounmap(pwr_ctl);
162
163 if ( timeout == 0 )
164 {
165 dprintk(XENLOG_ERR, "CPU%d power enable failed\n", cpu);
166 return -ETIMEDOUT;
167 }
168
169 return 0;
170 }
171
brcm_cpu_release(u32 cpu)172 static int brcm_cpu_release(u32 cpu)
173 {
174 u32 tmp;
175 u32 __iomem *reg;
176
177 dprintk(XENLOG_INFO, "%s: Taking cpu %d out of reset \n", __func__, cpu);
178
179 reg = ioremap_nocache(regs.hif_cpu_reset_config, sizeof(u32));
180 if ( !reg )
181 {
182 dprintk(XENLOG_ERR, "%s: Unable to map \"hif_cpu_reset_config\"\n",
183 __func__);
184 return -EFAULT;
185 }
186
187 /* now take the cpu out of reset */
188 tmp = readl(reg);
189 tmp &= ~(1 << cpu);
190 writel(tmp, reg);
191
192 iounmap(reg);
193
194 return 0;
195 }
196
brcm_set_boot_continuation(u32 cpu,u32 pc)197 static int brcm_set_boot_continuation(u32 cpu, u32 pc)
198 {
199 u32 __iomem *reg;
200 dprintk(XENLOG_INFO, "%s: cpu %d pc 0x%x\n", __func__, cpu, pc);
201
202 reg = ioremap_nocache(regs.hif_boot_continuation + (cpu * 2 * sizeof(u32)),
203 2 * sizeof(u32));
204 if ( !reg )
205 {
206 dprintk(XENLOG_ERR, "%s: Unable to map \"hif_boot_continuation\"\n",
207 __func__);
208 return -EFAULT;
209 }
210
211 writel(0, reg);
212 writel(pc, reg + 1);
213
214 iounmap(reg);
215
216 return 0;
217 }
218
brcm_cpu_up(int cpu)219 static int brcm_cpu_up(int cpu)
220 {
221 int rc;
222
223 rc = brcm_cpu_power_on(cpu);
224 if ( rc )
225 return rc;
226
227 rc = brcm_set_boot_continuation(cpu, brcm_boot_continuation_pc);
228 if ( rc )
229 return rc;
230
231 return brcm_cpu_release(cpu);
232 }
233
brcm_smp_init(void)234 static int __init brcm_smp_init(void)
235 {
236 u32 __iomem *scratch;
237 u32 target_pc;
238
239 scratch = ioremap_nocache(regs.scratch_reg, sizeof(u32));
240
241 if ( !scratch )
242 {
243 dprintk(XENLOG_ERR, "%s: Unable to map \"scratch_reg\"\n", __func__);
244 return -EFAULT;
245 }
246 /*
247 * The HIF CPU BIU CTRL Scratch Register is used to pass
248 * addresses between this code in xen and the boot helper.
249 * The helper puts its own entry point in the scratch register.
250 * That address is written to the cpu boot continuation registers.
251 * The helper expects xen to put xen's entry point back in the register.
252 * The helper will jump to that address.
253 * The helper is in SRAM, which will always be a 32 bit address.
254 */
255
256 brcm_boot_continuation_pc = readl(scratch);
257
258 target_pc = __pa(init_secondary);
259 writel(target_pc, scratch);
260
261 iounmap(scratch);
262
263 dprintk(XENLOG_INFO, "%s: target_pc 0x%x boot continuation pc 0x%x\n",
264 __func__, target_pc, brcm_boot_continuation_pc);
265
266 return 0;
267 }
268
brcm_init(void)269 static __init int brcm_init(void)
270 {
271 return brcm_populate_plat_regs();
272 }
273
274 static const char *const brcm_dt_compat[] __initconst =
275 {
276 "brcm,bcm7445d0",
277 NULL
278 };
279
280 PLATFORM_START(brcm, "Broadcom B15")
281 .compatible = brcm_dt_compat,
282 .init = brcm_init,
283 .smp_init = brcm_smp_init,
284 .cpu_up = brcm_cpu_up,
285 PLATFORM_END
286
287 /*
288 * Local variables:
289 * mode: C
290 * c-file-style: "BSD"
291 * c-basic-offset: 4
292 * indent-tabs-mode: nil
293 * End:
294 */
295