blob: 7bc4fe028056baf7fa0e24702b1acbf76847c5fc [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;
Girish Mahadevan47991972012-09-18 09:24:54 -0600214 uint32_t vaddr_val;
215
216 pdata.p_addr = 0;
217 vaddr_val = 0;
Praveen Chidambaram7d4167b2012-04-30 17:37:48 -0600218
219 key = "qcom,mode";
220 ret = of_property_read_u32(pdev->dev.of_node, key, &val);
221 if (ret) {
222 pr_err("Unable to read boot mode Err(%d).\n", ret);
223 return -ENODEV;
224 }
225 pdata.mode = val;
226
227 key = "qcom,phy-addr";
228 ret = of_property_read_u32(pdev->dev.of_node, key, &val);
Girish Mahadevan47991972012-09-18 09:24:54 -0600229 if (!ret)
Praveen Chidambaram7d4167b2012-04-30 17:37:48 -0600230 pdata.p_addr = val;
Girish Mahadevan47991972012-09-18 09:24:54 -0600231
Praveen Chidambaram7d4167b2012-04-30 17:37:48 -0600232
233 key = "qcom,virt-addr";
Girish Mahadevan47991972012-09-18 09:24:54 -0600234 ret = of_property_read_u32(pdev->dev.of_node, key, &vaddr_val);
Praveen Chidambaram7d4167b2012-04-30 17:37:48 -0600235
Girish Mahadevan47991972012-09-18 09:24:54 -0600236 switch (pdata.mode) {
237 case MSM_PM_BOOT_CONFIG_RESET_VECTOR_PHYS:
238 if (!pdata.p_addr) {
239 key = "qcom,phy-addr";
240 goto fail;
241 }
242 break;
243 case MSM_PM_BOOT_CONFIG_RESET_VECTOR_VIRT:
244 if (!vaddr_val)
245 goto fail;
246
247 pdata.v_addr = (void *)vaddr_val;
248 break;
249 case MSM_PM_BOOT_CONFIG_REMAP_BOOT_ADDR:
250 if (!vaddr_val)
251 goto fail;
252
253 pdata.v_addr = ioremap_nocache(vaddr_val, SZ_8);
254
255 pdata.p_addr = allocate_contiguous_ebi_nomap(SZ_8, SZ_64K);
256 if (!pdata.p_addr) {
257 key = "qcom,phy-addr";
258 goto fail;
259 }
260 break;
261 case MSM_PM_BOOT_CONFIG_TZ:
262 break;
263 default:
264 pr_err("%s: Unsupported boot mode %d",
265 __func__, pdata.mode);
Praveen Chidambaram7d4167b2012-04-30 17:37:48 -0600266 goto fail;
267 }
268
269 return msm_pm_boot_init(&pdata);
270
271fail:
272 pr_err("Error reading %s\n", key);
273 return -EFAULT;
274}
275
276static struct of_device_id msm_pm_match_table[] = {
277 {.compatible = "qcom,pm-boot"},
278 {},
279};
280
281static struct platform_driver msm_pm_boot_driver = {
282 .probe = msm_pm_boot_probe,
283 .driver = {
284 .name = "pm-boot",
285 .owner = THIS_MODULE,
286 .of_match_table = msm_pm_match_table,
287 },
288};
289
290static int __init msm_pm_boot_module_init(void)
291{
292 return platform_driver_register(&msm_pm_boot_driver);
293}
294module_init(msm_pm_boot_module_init);