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, &reg_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, &reg_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, &reg_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