blob: c77b19c5ff9995ef3cf1d9c51cd766573699b826 [file] [log] [blame]
Duy Truong790f06d2013-02-13 16:38:12 -08001/* Copyright (c) 2011-2012, 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
Steve Mucklec25a9362012-03-22 16:40:01 -070035static void msm_pm_write_boot_vector(unsigned int cpu, unsigned long address)
36{
37 msm_pm_boot_vector[cpu] = address;
38 clean_caches((unsigned long)&msm_pm_boot_vector[cpu],
39 sizeof(msm_pm_boot_vector[cpu]),
40 virt_to_phys(&msm_pm_boot_vector[cpu]));
41}
42
Murali Nalajala867f8482012-04-02 20:57:35 +053043#ifdef CONFIG_MSM_SCM
Mahesh Sivasubramanian073aea02012-05-29 16:52:45 -060044static int __devinit msm_pm_tz_boot_init(void)
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -060045{
Stephen Boydefadd5b2012-08-08 15:22:04 -070046 unsigned int flag = 0;
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -060047 if (num_possible_cpus() == 1)
48 flag = SCM_FLAG_WARMBOOT_CPU0;
49 else if (num_possible_cpus() == 2)
50 flag = SCM_FLAG_WARMBOOT_CPU0 | SCM_FLAG_WARMBOOT_CPU1;
Joel King274621c2011-12-05 06:18:20 -080051 else if (num_possible_cpus() == 4)
52 flag = SCM_FLAG_WARMBOOT_CPU0 | SCM_FLAG_WARMBOOT_CPU1 |
53 SCM_FLAG_WARMBOOT_CPU2 | SCM_FLAG_WARMBOOT_CPU3;
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -060054 else
55 __WARN();
56
Stephen Boydefadd5b2012-08-08 15:22:04 -070057 return scm_set_boot_addr(virt_to_phys(msm_pm_boot_entry), flag);
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -060058}
59
60static void msm_pm_config_tz_before_pc(unsigned int cpu,
61 unsigned long entry)
62{
63 msm_pm_write_boot_vector(cpu, entry);
64}
65#else
66static int __init msm_pm_tz_boot_init(void)
67{
68 return 0;
69};
70
71static inline void msm_pm_config_tz_before_pc(unsigned int cpu,
72 unsigned long entry) {}
73#endif
74
Mahesh Sivasubramanian073aea02012-05-29 16:52:45 -060075static int __devinit msm_pm_boot_reset_vector_init(uint32_t *reset_vector)
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -060076{
Maheshkumar Sivasubramanianc6c55032011-10-25 16:01:32 -060077 if (!reset_vector)
78 return -ENODEV;
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -060079 msm_pm_reset_vector = reset_vector;
80 mb();
81
82 return 0;
83}
84
85static void msm_pm_config_rst_vector_before_pc(unsigned int cpu,
86 unsigned long entry)
87{
88 saved_vector[0] = msm_pm_reset_vector[0];
89 saved_vector[1] = msm_pm_reset_vector[1];
90 msm_pm_reset_vector[0] = 0xE51FF004; /* ldr pc, 4 */
91 msm_pm_reset_vector[1] = entry;
92}
93
94static void msm_pm_config_rst_vector_after_pc(unsigned int cpu)
95{
96 msm_pm_reset_vector[0] = saved_vector[0];
97 msm_pm_reset_vector[1] = saved_vector[1];
98}
99
100void msm_pm_boot_config_before_pc(unsigned int cpu, unsigned long entry)
101{
102 if (msm_pm_boot_before_pc)
103 msm_pm_boot_before_pc(cpu, entry);
104}
105
106void msm_pm_boot_config_after_pc(unsigned int cpu)
107{
108 if (msm_pm_boot_after_pc)
109 msm_pm_boot_after_pc(cpu);
110}
Maheshkumar Sivasubramanianc6c55032011-10-25 16:01:32 -0600111#define BOOT_REMAP_ENABLE BIT(0)
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -0600112
Mahesh Sivasubramanian073aea02012-05-29 16:52:45 -0600113int __devinit msm_pm_boot_init(struct msm_pm_boot_platform_data *pdata)
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -0600114{
115 int ret = 0;
Murali Nalajala41786ab2012-03-06 10:47:32 +0530116 unsigned long entry;
Murali Nalajalafeedeae2012-03-28 16:15:32 +0530117 void __iomem *warm_boot_ptr;
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -0600118
Maheshkumar Sivasubramanianc6c55032011-10-25 16:01:32 -0600119 switch (pdata->mode) {
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -0600120 case MSM_PM_BOOT_CONFIG_TZ:
121 ret = msm_pm_tz_boot_init();
122 msm_pm_boot_before_pc = msm_pm_config_tz_before_pc;
123 msm_pm_boot_after_pc = NULL;
124 break;
Maheshkumar Sivasubramanianc6c55032011-10-25 16:01:32 -0600125 case MSM_PM_BOOT_CONFIG_RESET_VECTOR_PHYS:
Maheshkumar Sivasubramanianc6c55032011-10-25 16:01:32 -0600126 pdata->v_addr = ioremap(pdata->p_addr, PAGE_SIZE);
127 /* Fall through */
128 case MSM_PM_BOOT_CONFIG_RESET_VECTOR_VIRT:
129
130 if (!pdata->v_addr)
131 return -ENODEV;
132
133 ret = msm_pm_boot_reset_vector_init(pdata->v_addr);
134 msm_pm_boot_before_pc
135 = msm_pm_config_rst_vector_before_pc;
136 msm_pm_boot_after_pc
137 = msm_pm_config_rst_vector_after_pc;
138 break;
139 case MSM_PM_BOOT_CONFIG_REMAP_BOOT_ADDR:
Utsab Bose4ed4ba12012-11-08 18:52:38 +0530140 if (!cpu_is_msm8625() && !cpu_is_msm8625q()) {
Olav Haugane6a0acd2012-04-05 09:29:12 -0700141 void *remapped;
142
Murali Nalajalafeedeae2012-03-28 16:15:32 +0530143 /*
144 * Set the boot remap address and enable remapping of
145 * reset vector
146 */
147 if (!pdata->p_addr || !pdata->v_addr)
148 return -ENODEV;
149
Olav Haugane6a0acd2012-04-05 09:29:12 -0700150 remapped = ioremap_nocache(pdata->p_addr, SZ_8);
151 ret = msm_pm_boot_reset_vector_init(remapped);
Murali Nalajalafeedeae2012-03-28 16:15:32 +0530152
Murali Nalajala41786ab2012-03-06 10:47:32 +0530153 __raw_writel((pdata->p_addr | BOOT_REMAP_ENABLE),
154 pdata->v_addr);
155
156 msm_pm_boot_before_pc
157 = msm_pm_config_rst_vector_before_pc;
158 msm_pm_boot_after_pc
159 = msm_pm_config_rst_vector_after_pc;
160 } else {
Murali Nalajala40b48802012-10-19 18:18:42 +0530161 uint32_t mpa5_boot_remap_addr[2] = {0x34, 0x4C};
162 uint32_t mpa5_cfg_ctl[2] = {0x30, 0x48};
163
Murali Nalajalafeedeae2012-03-28 16:15:32 +0530164 warm_boot_ptr = ioremap_nocache(
165 MSM8625_WARM_BOOT_PHYS, SZ_64);
166 ret = msm_pm_boot_reset_vector_init(warm_boot_ptr);
167
Murali Nalajala41786ab2012-03-06 10:47:32 +0530168 entry = virt_to_phys(msm_pm_boot_entry);
169
Murali Nalajala40b48802012-10-19 18:18:42 +0530170 /*
171 * Below sequence is a work around for cores
Murali Nalajalab99ce122012-03-21 00:40:40 +0530172 * to come out of GDFS properly on 8625 target.
173 * On 8625 while cores coming out of GDFS observed
174 * the memory corruption at very first memory read.
175 */
176 msm_pm_reset_vector[0] = 0xE59F000C; /* ldr r0, 0x14 */
177 msm_pm_reset_vector[1] = 0xE59F1008; /* ldr r1, 0x14 */
178 msm_pm_reset_vector[2] = 0xE1500001; /* cmp r0, r1 */
179 msm_pm_reset_vector[3] = 0x1AFFFFFB; /* bne 0x0 */
180 msm_pm_reset_vector[4] = 0xE12FFF10; /* bx r0 */
181 msm_pm_reset_vector[5] = entry; /* 0x14 */
Murali Nalajala41786ab2012-03-06 10:47:32 +0530182
Murali Nalajala40b48802012-10-19 18:18:42 +0530183 /*
184 * Here upper 16bits[16:31] used by CORE1
Murali Nalajala41786ab2012-03-06 10:47:32 +0530185 * lower 16bits[0:15] used by CORE0
186 */
Murali Nalajalafeedeae2012-03-28 16:15:32 +0530187 entry = (MSM8625_WARM_BOOT_PHYS |
188 ((MSM8625_WARM_BOOT_PHYS & 0xFFFF0000) >> 16));
Murali Nalajala41786ab2012-03-06 10:47:32 +0530189
190 /* write 'entry' to boot remapper register */
191 __raw_writel(entry, (pdata->v_addr +
Murali Nalajala40b48802012-10-19 18:18:42 +0530192 mpa5_boot_remap_addr[0]));
Murali Nalajala41786ab2012-03-06 10:47:32 +0530193
Murali Nalajala40b48802012-10-19 18:18:42 +0530194 /*
195 * Enable boot remapper for C0 [bit:25th]
196 * Enable boot remapper for C1 [bit:26th]
197 */
Murali Nalajala41786ab2012-03-06 10:47:32 +0530198 __raw_writel(readl_relaxed(pdata->v_addr +
Murali Nalajala40b48802012-10-19 18:18:42 +0530199 mpa5_cfg_ctl[0]) | (0x3 << 25),
200 pdata->v_addr + mpa5_cfg_ctl[0]);
Murali Nalajala41786ab2012-03-06 10:47:32 +0530201
Murali Nalajala40b48802012-10-19 18:18:42 +0530202 /* 8x25Q changes */
Utsab Bose4ed4ba12012-11-08 18:52:38 +0530203 if (cpu_is_msm8625q()) {
Murali Nalajala40b48802012-10-19 18:18:42 +0530204 /* write 'entry' to boot remapper register */
205 __raw_writel(entry, (pdata->v_addr +
206 mpa5_boot_remap_addr[1]));
207
208 /*
209 * Enable boot remapper for C2 [bit:25th]
210 * Enable boot remapper for C3 [bit:26th]
211 */
212 __raw_writel(readl_relaxed(pdata->v_addr +
213 mpa5_cfg_ctl[1]) | (0x3 << 25),
214 pdata->v_addr + mpa5_cfg_ctl[1]);
215 }
Murali Nalajala867f8482012-04-02 20:57:35 +0530216 msm_pm_boot_before_pc = msm_pm_write_boot_vector;
Murali Nalajala41786ab2012-03-06 10:47:32 +0530217 }
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -0600218 break;
219 default:
220 __WARN();
221 }
222
223 return ret;
224}
Praveen Chidambaram7d4167b2012-04-30 17:37:48 -0600225
226static int __devinit msm_pm_boot_probe(struct platform_device *pdev)
227{
228 struct msm_pm_boot_platform_data pdata;
229 char *key = NULL;
230 uint32_t val = 0;
231 int ret = 0;
Girish Mahadevan47991972012-09-18 09:24:54 -0600232 uint32_t vaddr_val;
233
234 pdata.p_addr = 0;
235 vaddr_val = 0;
Praveen Chidambaram7d4167b2012-04-30 17:37:48 -0600236
237 key = "qcom,mode";
238 ret = of_property_read_u32(pdev->dev.of_node, key, &val);
239 if (ret) {
240 pr_err("Unable to read boot mode Err(%d).\n", ret);
241 return -ENODEV;
242 }
243 pdata.mode = val;
244
245 key = "qcom,phy-addr";
246 ret = of_property_read_u32(pdev->dev.of_node, key, &val);
Girish Mahadevan47991972012-09-18 09:24:54 -0600247 if (!ret)
Praveen Chidambaram7d4167b2012-04-30 17:37:48 -0600248 pdata.p_addr = val;
Girish Mahadevan47991972012-09-18 09:24:54 -0600249
Praveen Chidambaram7d4167b2012-04-30 17:37:48 -0600250
251 key = "qcom,virt-addr";
Girish Mahadevan47991972012-09-18 09:24:54 -0600252 ret = of_property_read_u32(pdev->dev.of_node, key, &vaddr_val);
Praveen Chidambaram7d4167b2012-04-30 17:37:48 -0600253
Girish Mahadevan47991972012-09-18 09:24:54 -0600254 switch (pdata.mode) {
255 case MSM_PM_BOOT_CONFIG_RESET_VECTOR_PHYS:
256 if (!pdata.p_addr) {
257 key = "qcom,phy-addr";
258 goto fail;
259 }
260 break;
261 case MSM_PM_BOOT_CONFIG_RESET_VECTOR_VIRT:
262 if (!vaddr_val)
263 goto fail;
264
265 pdata.v_addr = (void *)vaddr_val;
266 break;
267 case MSM_PM_BOOT_CONFIG_REMAP_BOOT_ADDR:
268 if (!vaddr_val)
269 goto fail;
270
271 pdata.v_addr = ioremap_nocache(vaddr_val, SZ_8);
272
273 pdata.p_addr = allocate_contiguous_ebi_nomap(SZ_8, SZ_64K);
274 if (!pdata.p_addr) {
275 key = "qcom,phy-addr";
276 goto fail;
277 }
278 break;
279 case MSM_PM_BOOT_CONFIG_TZ:
280 break;
281 default:
282 pr_err("%s: Unsupported boot mode %d",
283 __func__, pdata.mode);
Praveen Chidambaram7d4167b2012-04-30 17:37:48 -0600284 goto fail;
285 }
286
287 return msm_pm_boot_init(&pdata);
288
289fail:
290 pr_err("Error reading %s\n", key);
291 return -EFAULT;
292}
293
294static struct of_device_id msm_pm_match_table[] = {
295 {.compatible = "qcom,pm-boot"},
296 {},
297};
298
299static struct platform_driver msm_pm_boot_driver = {
300 .probe = msm_pm_boot_probe,
301 .driver = {
302 .name = "pm-boot",
303 .owner = THIS_MODULE,
304 .of_match_table = msm_pm_match_table,
305 },
306};
307
308static int __init msm_pm_boot_module_init(void)
309{
310 return platform_driver_register(&msm_pm_boot_driver);
311}
312module_init(msm_pm_boot_module_init);