blob: ed15a0cce4647a007dd2ea6efa74da89094b8397 [file] [log] [blame]
Sravan Kumar Ambapuramb22cf4d2012-01-02 21:45:04 +05301/* Copyright (c) 2011-2012, Code Aurora Forum. 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:
Murali Nalajala41786ab2012-03-06 10:47:32 +0530140 if (!cpu_is_msm8625()) {
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 Nalajalafeedeae2012-03-28 16:15:32 +0530161 warm_boot_ptr = ioremap_nocache(
162 MSM8625_WARM_BOOT_PHYS, SZ_64);
163 ret = msm_pm_boot_reset_vector_init(warm_boot_ptr);
164
Murali Nalajala41786ab2012-03-06 10:47:32 +0530165 entry = virt_to_phys(msm_pm_boot_entry);
166
Murali Nalajalab99ce122012-03-21 00:40:40 +0530167 /* Below sequence is a work around for cores
168 * to come out of GDFS properly on 8625 target.
169 * On 8625 while cores coming out of GDFS observed
170 * the memory corruption at very first memory read.
171 */
172 msm_pm_reset_vector[0] = 0xE59F000C; /* ldr r0, 0x14 */
173 msm_pm_reset_vector[1] = 0xE59F1008; /* ldr r1, 0x14 */
174 msm_pm_reset_vector[2] = 0xE1500001; /* cmp r0, r1 */
175 msm_pm_reset_vector[3] = 0x1AFFFFFB; /* bne 0x0 */
176 msm_pm_reset_vector[4] = 0xE12FFF10; /* bx r0 */
177 msm_pm_reset_vector[5] = entry; /* 0x14 */
Murali Nalajala41786ab2012-03-06 10:47:32 +0530178
179 /* Here upper 16bits[16:31] used by CORE1
180 * lower 16bits[0:15] used by CORE0
181 */
Murali Nalajalafeedeae2012-03-28 16:15:32 +0530182 entry = (MSM8625_WARM_BOOT_PHYS |
183 ((MSM8625_WARM_BOOT_PHYS & 0xFFFF0000) >> 16));
Murali Nalajala41786ab2012-03-06 10:47:32 +0530184
185 /* write 'entry' to boot remapper register */
186 __raw_writel(entry, (pdata->v_addr +
187 MPA5_BOOT_REMAP_ADDR));
188
189 /* Enable boot remapper for C0 [bit:25th] */
190 __raw_writel(readl_relaxed(pdata->v_addr +
191 MPA5_CFG_CTL_REG) | BIT(25),
192 pdata->v_addr + MPA5_CFG_CTL_REG);
193
194 /* Enable boot remapper for C1 [bit:26th] */
195 __raw_writel(readl_relaxed(pdata->v_addr +
196 MPA5_CFG_CTL_REG) | BIT(26),
197 pdata->v_addr + MPA5_CFG_CTL_REG);
Murali Nalajala867f8482012-04-02 20:57:35 +0530198 msm_pm_boot_before_pc = msm_pm_write_boot_vector;
Murali Nalajala41786ab2012-03-06 10:47:32 +0530199 }
Maheshkumar Sivasubramanian8ccc16e2011-10-25 15:59:57 -0600200 break;
201 default:
202 __WARN();
203 }
204
205 return ret;
206}
Praveen Chidambaram7d4167b2012-04-30 17:37:48 -0600207
208static int __devinit msm_pm_boot_probe(struct platform_device *pdev)
209{
210 struct msm_pm_boot_platform_data pdata;
211 char *key = NULL;
212 uint32_t val = 0;
213 int ret = 0;
214 int flag = 0;
215
216 key = "qcom,mode";
217 ret = of_property_read_u32(pdev->dev.of_node, key, &val);
218 if (ret) {
219 pr_err("Unable to read boot mode Err(%d).\n", ret);
220 return -ENODEV;
221 }
222 pdata.mode = val;
223
224 key = "qcom,phy-addr";
225 ret = of_property_read_u32(pdev->dev.of_node, key, &val);
226 if (ret && pdata.mode == MSM_PM_BOOT_CONFIG_RESET_VECTOR_PHYS)
227 goto fail;
228 if (!ret) {
229 pdata.p_addr = val;
230 flag++;
231 }
232
233 key = "qcom,virt-addr";
234 ret = of_property_read_u32(pdev->dev.of_node, key, &val);
235 if (ret && pdata.mode == MSM_PM_BOOT_CONFIG_RESET_VECTOR_VIRT)
236 goto fail;
237 if (!ret) {
238 pdata.v_addr = (void *)val;
239 flag++;
240 }
241
242 if (pdata.mode == MSM_PM_BOOT_CONFIG_REMAP_BOOT_ADDR && (flag != 2)) {
243 key = "addresses for boot remap";
244 goto fail;
245 }
246
247 return msm_pm_boot_init(&pdata);
248
249fail:
250 pr_err("Error reading %s\n", key);
251 return -EFAULT;
252}
253
254static struct of_device_id msm_pm_match_table[] = {
255 {.compatible = "qcom,pm-boot"},
256 {},
257};
258
259static struct platform_driver msm_pm_boot_driver = {
260 .probe = msm_pm_boot_probe,
261 .driver = {
262 .name = "pm-boot",
263 .owner = THIS_MODULE,
264 .of_match_table = msm_pm_match_table,
265 },
266};
267
268static int __init msm_pm_boot_module_init(void)
269{
270 return platform_driver_register(&msm_pm_boot_driver);
271}
272module_init(msm_pm_boot_module_init);