blob: 3559510ed67889cdec2ad567a5c8ec88a2a59173 [file] [log] [blame]
Archana Sathyakumar28441012013-02-01 17:45:38 -07001/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -06002 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 */
13
14#include <linux/module.h>
15#include <linux/kernel.h>
16#include <linux/init.h>
17#include <linux/io.h>
Praveen Chidambaram7d4167b2012-04-30 17:37:48 -060018#include <linux/platform_device.h>
19#include <linux/of.h>
20#include <linux/of_address.h>
21#include <linux/of_irq.h>
Maheshkumar Sivasubramanianc6c55032011-10-25 16:01:32 -060022#include <mach/msm_iomap.h>
23#include <mach/socinfo.h>
24#include <asm/mach-types.h>
25#include <asm/sizes.h>
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -060026#include "scm-boot.h"
27#include "idle.h"
28#include "pm-boot.h"
29
30static uint32_t *msm_pm_reset_vector;
31static uint32_t saved_vector[2];
32static void (*msm_pm_boot_before_pc)(unsigned int cpu, unsigned long entry);
33static void (*msm_pm_boot_after_pc)(unsigned int cpu);
34
Archana Sathyakumar28441012013-02-01 17:45:38 -070035
36static int msm_pm_get_boot_config_mode(struct device_node *dev,
37 const char *key, uint32_t *boot_config_mode)
38{
39 struct pm_lookup_table {
40 uint32_t boot_config_mode;
41 const char *boot_config_name;
42 };
43 const char *boot_config_str;
44 struct pm_lookup_table boot_config_lookup[] = {
45 {MSM_PM_BOOT_CONFIG_TZ, "tz"},
46 {MSM_PM_BOOT_CONFIG_RESET_VECTOR_PHYS, "reset_vector_phys"},
47 {MSM_PM_BOOT_CONFIG_RESET_VECTOR_VIRT, "reset_vector_virt"},
48 {MSM_PM_BOOT_CONFIG_REMAP_BOOT_ADDR, "remap_boot_addr"}
49 };
50 int ret;
51 int i;
52
53 ret = of_property_read_string(dev, key, &boot_config_str);
54 if (!ret) {
55 ret = -EINVAL;
56 for (i = 0; i < ARRAY_SIZE(boot_config_lookup); i++) {
57 if (!strncmp(boot_config_str,
58 boot_config_lookup[i].boot_config_name,
59 strlen(boot_config_lookup[i].boot_config_name))
60 ) {
61 *boot_config_mode =
62 boot_config_lookup[i].boot_config_mode;
63 ret = 0;
64 break;
65 }
66 }
67 }
68 return ret;
69}
70
Steve Mucklec25a9362012-03-22 16:40:01 -070071static void msm_pm_write_boot_vector(unsigned int cpu, unsigned long address)
72{
73 msm_pm_boot_vector[cpu] = address;
74 clean_caches((unsigned long)&msm_pm_boot_vector[cpu],
75 sizeof(msm_pm_boot_vector[cpu]),
76 virt_to_phys(&msm_pm_boot_vector[cpu]));
77}
78
Murali Nalajala867f8482012-04-02 20:57:35 +053079#ifdef CONFIG_MSM_SCM
Mahesh Sivasubramanian073aea02012-05-29 16:52:45 -060080static int __devinit msm_pm_tz_boot_init(void)
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -060081{
Stephen Boydefadd5b2012-08-08 15:22:04 -070082 unsigned int flag = 0;
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -060083 if (num_possible_cpus() == 1)
84 flag = SCM_FLAG_WARMBOOT_CPU0;
85 else if (num_possible_cpus() == 2)
86 flag = SCM_FLAG_WARMBOOT_CPU0 | SCM_FLAG_WARMBOOT_CPU1;
Joel King274621c2011-12-05 06:18:20 -080087 else if (num_possible_cpus() == 4)
88 flag = SCM_FLAG_WARMBOOT_CPU0 | SCM_FLAG_WARMBOOT_CPU1 |
89 SCM_FLAG_WARMBOOT_CPU2 | SCM_FLAG_WARMBOOT_CPU3;
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -060090 else
91 __WARN();
92
Stephen Boydefadd5b2012-08-08 15:22:04 -070093 return scm_set_boot_addr(virt_to_phys(msm_pm_boot_entry), flag);
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -060094}
95
96static void msm_pm_config_tz_before_pc(unsigned int cpu,
97 unsigned long entry)
98{
99 msm_pm_write_boot_vector(cpu, entry);
100}
101#else
102static int __init msm_pm_tz_boot_init(void)
103{
104 return 0;
105};
106
107static inline void msm_pm_config_tz_before_pc(unsigned int cpu,
108 unsigned long entry) {}
109#endif
110
Mahesh Sivasubramanian073aea02012-05-29 16:52:45 -0600111static int __devinit msm_pm_boot_reset_vector_init(uint32_t *reset_vector)
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -0600112{
Maheshkumar Sivasubramanianc6c55032011-10-25 16:01:32 -0600113 if (!reset_vector)
114 return -ENODEV;
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -0600115 msm_pm_reset_vector = reset_vector;
116 mb();
117
118 return 0;
119}
120
121static void msm_pm_config_rst_vector_before_pc(unsigned int cpu,
122 unsigned long entry)
123{
124 saved_vector[0] = msm_pm_reset_vector[0];
125 saved_vector[1] = msm_pm_reset_vector[1];
126 msm_pm_reset_vector[0] = 0xE51FF004; /* ldr pc, 4 */
127 msm_pm_reset_vector[1] = entry;
128}
129
130static void msm_pm_config_rst_vector_after_pc(unsigned int cpu)
131{
132 msm_pm_reset_vector[0] = saved_vector[0];
133 msm_pm_reset_vector[1] = saved_vector[1];
134}
135
136void msm_pm_boot_config_before_pc(unsigned int cpu, unsigned long entry)
137{
138 if (msm_pm_boot_before_pc)
139 msm_pm_boot_before_pc(cpu, entry);
140}
141
142void msm_pm_boot_config_after_pc(unsigned int cpu)
143{
144 if (msm_pm_boot_after_pc)
145 msm_pm_boot_after_pc(cpu);
146}
Maheshkumar Sivasubramanianc6c55032011-10-25 16:01:32 -0600147#define BOOT_REMAP_ENABLE BIT(0)
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -0600148
Mahesh Sivasubramanian073aea02012-05-29 16:52:45 -0600149int __devinit msm_pm_boot_init(struct msm_pm_boot_platform_data *pdata)
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -0600150{
151 int ret = 0;
Murali Nalajala41786ab2012-03-06 10:47:32 +0530152 unsigned long entry;
Murali Nalajalafeedeae2012-03-28 16:15:32 +0530153 void __iomem *warm_boot_ptr;
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -0600154
Maheshkumar Sivasubramanianc6c55032011-10-25 16:01:32 -0600155 switch (pdata->mode) {
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -0600156 case MSM_PM_BOOT_CONFIG_TZ:
157 ret = msm_pm_tz_boot_init();
158 msm_pm_boot_before_pc = msm_pm_config_tz_before_pc;
159 msm_pm_boot_after_pc = NULL;
160 break;
Maheshkumar Sivasubramanianc6c55032011-10-25 16:01:32 -0600161 case MSM_PM_BOOT_CONFIG_RESET_VECTOR_PHYS:
Maheshkumar Sivasubramanianc6c55032011-10-25 16:01:32 -0600162 pdata->v_addr = ioremap(pdata->p_addr, PAGE_SIZE);
163 /* Fall through */
164 case MSM_PM_BOOT_CONFIG_RESET_VECTOR_VIRT:
165
166 if (!pdata->v_addr)
167 return -ENODEV;
168
169 ret = msm_pm_boot_reset_vector_init(pdata->v_addr);
170 msm_pm_boot_before_pc
171 = msm_pm_config_rst_vector_before_pc;
172 msm_pm_boot_after_pc
173 = msm_pm_config_rst_vector_after_pc;
174 break;
175 case MSM_PM_BOOT_CONFIG_REMAP_BOOT_ADDR:
Utsab Bose4ed4ba12012-11-08 18:52:38 +0530176 if (!cpu_is_msm8625() && !cpu_is_msm8625q()) {
Olav Haugane6a0acd2012-04-05 09:29:12 -0700177 void *remapped;
178
Murali Nalajalafeedeae2012-03-28 16:15:32 +0530179 /*
180 * Set the boot remap address and enable remapping of
181 * reset vector
182 */
183 if (!pdata->p_addr || !pdata->v_addr)
184 return -ENODEV;
185
Olav Haugane6a0acd2012-04-05 09:29:12 -0700186 remapped = ioremap_nocache(pdata->p_addr, SZ_8);
187 ret = msm_pm_boot_reset_vector_init(remapped);
Murali Nalajalafeedeae2012-03-28 16:15:32 +0530188
Murali Nalajala41786ab2012-03-06 10:47:32 +0530189 __raw_writel((pdata->p_addr | BOOT_REMAP_ENABLE),
190 pdata->v_addr);
191
192 msm_pm_boot_before_pc
193 = msm_pm_config_rst_vector_before_pc;
194 msm_pm_boot_after_pc
195 = msm_pm_config_rst_vector_after_pc;
196 } else {
Murali Nalajala40b48802012-10-19 18:18:42 +0530197 uint32_t mpa5_boot_remap_addr[2] = {0x34, 0x4C};
198 uint32_t mpa5_cfg_ctl[2] = {0x30, 0x48};
199
Murali Nalajalafeedeae2012-03-28 16:15:32 +0530200 warm_boot_ptr = ioremap_nocache(
201 MSM8625_WARM_BOOT_PHYS, SZ_64);
202 ret = msm_pm_boot_reset_vector_init(warm_boot_ptr);
203
Murali Nalajala41786ab2012-03-06 10:47:32 +0530204 entry = virt_to_phys(msm_pm_boot_entry);
205
Murali Nalajala40b48802012-10-19 18:18:42 +0530206 /*
207 * Below sequence is a work around for cores
Murali Nalajalab99ce122012-03-21 00:40:40 +0530208 * to come out of GDFS properly on 8625 target.
209 * On 8625 while cores coming out of GDFS observed
210 * the memory corruption at very first memory read.
211 */
212 msm_pm_reset_vector[0] = 0xE59F000C; /* ldr r0, 0x14 */
213 msm_pm_reset_vector[1] = 0xE59F1008; /* ldr r1, 0x14 */
214 msm_pm_reset_vector[2] = 0xE1500001; /* cmp r0, r1 */
215 msm_pm_reset_vector[3] = 0x1AFFFFFB; /* bne 0x0 */
216 msm_pm_reset_vector[4] = 0xE12FFF10; /* bx r0 */
217 msm_pm_reset_vector[5] = entry; /* 0x14 */
Murali Nalajala41786ab2012-03-06 10:47:32 +0530218
Murali Nalajala40b48802012-10-19 18:18:42 +0530219 /*
220 * Here upper 16bits[16:31] used by CORE1
Murali Nalajala41786ab2012-03-06 10:47:32 +0530221 * lower 16bits[0:15] used by CORE0
222 */
Murali Nalajalafeedeae2012-03-28 16:15:32 +0530223 entry = (MSM8625_WARM_BOOT_PHYS |
224 ((MSM8625_WARM_BOOT_PHYS & 0xFFFF0000) >> 16));
Murali Nalajala41786ab2012-03-06 10:47:32 +0530225
226 /* write 'entry' to boot remapper register */
227 __raw_writel(entry, (pdata->v_addr +
Murali Nalajala40b48802012-10-19 18:18:42 +0530228 mpa5_boot_remap_addr[0]));
Murali Nalajala41786ab2012-03-06 10:47:32 +0530229
Murali Nalajala40b48802012-10-19 18:18:42 +0530230 /*
231 * Enable boot remapper for C0 [bit:25th]
232 * Enable boot remapper for C1 [bit:26th]
233 */
Murali Nalajala41786ab2012-03-06 10:47:32 +0530234 __raw_writel(readl_relaxed(pdata->v_addr +
Murali Nalajala40b48802012-10-19 18:18:42 +0530235 mpa5_cfg_ctl[0]) | (0x3 << 25),
236 pdata->v_addr + mpa5_cfg_ctl[0]);
Murali Nalajala41786ab2012-03-06 10:47:32 +0530237
Murali Nalajala40b48802012-10-19 18:18:42 +0530238 /* 8x25Q changes */
Utsab Bose4ed4ba12012-11-08 18:52:38 +0530239 if (cpu_is_msm8625q()) {
Murali Nalajala40b48802012-10-19 18:18:42 +0530240 /* write 'entry' to boot remapper register */
241 __raw_writel(entry, (pdata->v_addr +
242 mpa5_boot_remap_addr[1]));
243
244 /*
245 * Enable boot remapper for C2 [bit:25th]
246 * Enable boot remapper for C3 [bit:26th]
247 */
248 __raw_writel(readl_relaxed(pdata->v_addr +
249 mpa5_cfg_ctl[1]) | (0x3 << 25),
250 pdata->v_addr + mpa5_cfg_ctl[1]);
251 }
Murali Nalajala867f8482012-04-02 20:57:35 +0530252 msm_pm_boot_before_pc = msm_pm_write_boot_vector;
Murali Nalajala41786ab2012-03-06 10:47:32 +0530253 }
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -0600254 break;
255 default:
256 __WARN();
257 }
258
259 return ret;
260}
Praveen Chidambaram7d4167b2012-04-30 17:37:48 -0600261
262static int __devinit msm_pm_boot_probe(struct platform_device *pdev)
263{
264 struct msm_pm_boot_platform_data pdata;
265 char *key = NULL;
266 uint32_t val = 0;
267 int ret = 0;
Girish Mahadevan47991972012-09-18 09:24:54 -0600268 uint32_t vaddr_val;
269
270 pdata.p_addr = 0;
271 vaddr_val = 0;
Praveen Chidambaram7d4167b2012-04-30 17:37:48 -0600272
273 key = "qcom,mode";
Archana Sathyakumar28441012013-02-01 17:45:38 -0700274 ret = msm_pm_get_boot_config_mode(pdev->dev.of_node, key, &val);
275 if (ret)
276 goto fail;
Praveen Chidambaram7d4167b2012-04-30 17:37:48 -0600277 pdata.mode = val;
278
279 key = "qcom,phy-addr";
280 ret = of_property_read_u32(pdev->dev.of_node, key, &val);
Girish Mahadevan47991972012-09-18 09:24:54 -0600281 if (!ret)
Praveen Chidambaram7d4167b2012-04-30 17:37:48 -0600282 pdata.p_addr = val;
Girish Mahadevan47991972012-09-18 09:24:54 -0600283
Praveen Chidambaram7d4167b2012-04-30 17:37:48 -0600284
285 key = "qcom,virt-addr";
Girish Mahadevan47991972012-09-18 09:24:54 -0600286 ret = of_property_read_u32(pdev->dev.of_node, key, &vaddr_val);
Praveen Chidambaram7d4167b2012-04-30 17:37:48 -0600287
Girish Mahadevan47991972012-09-18 09:24:54 -0600288 switch (pdata.mode) {
289 case MSM_PM_BOOT_CONFIG_RESET_VECTOR_PHYS:
290 if (!pdata.p_addr) {
291 key = "qcom,phy-addr";
292 goto fail;
293 }
294 break;
295 case MSM_PM_BOOT_CONFIG_RESET_VECTOR_VIRT:
296 if (!vaddr_val)
297 goto fail;
298
299 pdata.v_addr = (void *)vaddr_val;
300 break;
301 case MSM_PM_BOOT_CONFIG_REMAP_BOOT_ADDR:
302 if (!vaddr_val)
303 goto fail;
304
305 pdata.v_addr = ioremap_nocache(vaddr_val, SZ_8);
306
307 pdata.p_addr = allocate_contiguous_ebi_nomap(SZ_8, SZ_64K);
308 if (!pdata.p_addr) {
309 key = "qcom,phy-addr";
310 goto fail;
311 }
312 break;
313 case MSM_PM_BOOT_CONFIG_TZ:
314 break;
315 default:
316 pr_err("%s: Unsupported boot mode %d",
317 __func__, pdata.mode);
Praveen Chidambaram7d4167b2012-04-30 17:37:48 -0600318 goto fail;
319 }
320
321 return msm_pm_boot_init(&pdata);
322
323fail:
324 pr_err("Error reading %s\n", key);
325 return -EFAULT;
326}
327
328static struct of_device_id msm_pm_match_table[] = {
329 {.compatible = "qcom,pm-boot"},
330 {},
331};
332
333static struct platform_driver msm_pm_boot_driver = {
334 .probe = msm_pm_boot_probe,
335 .driver = {
336 .name = "pm-boot",
337 .owner = THIS_MODULE,
338 .of_match_table = msm_pm_match_table,
339 },
340};
341
342static int __init msm_pm_boot_module_init(void)
343{
344 return platform_driver_register(&msm_pm_boot_driver);
345}
346module_init(msm_pm_boot_module_init);