blob: 7daf77d65f4236642bb726410ebfd26a131f5195 [file] [log] [blame]
Duy Truonge833aca2013-02-12 13:35:08 -08001/* Copyright (c) 2010-2012, The Linux Foundation. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002 *
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/moduleparam.h>
16#include <linux/kernel.h>
17#include <linux/init.h>
18#include <linux/reboot.h>
19#include <linux/io.h>
20#include <linux/delay.h>
21#include <linux/pm.h>
Abhijeet Dharmapurikar6d565fd2011-09-15 18:49:56 -070022#include <linux/cpu.h>
23#include <linux/interrupt.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070024#include <linux/mfd/pmic8058.h>
25#include <linux/mfd/pmic8901.h>
Jeff Ohlstein28009a82011-07-25 19:21:26 -070026#include <linux/mfd/pm8xxx/misc.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070027
28#include <asm/mach-types.h>
29
30#include <mach/msm_iomap.h>
31#include <mach/restart.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070032#include <mach/socinfo.h>
Abhijeet Dharmapurikar6d565fd2011-09-15 18:49:56 -070033#include <mach/irqs.h>
Abhijeet Dharmapurikar9259fef2011-09-24 19:07:48 -070034#include <mach/scm.h>
35#include "msm_watchdog.h"
Rohit Vaswanif688fa62011-10-13 18:13:10 -070036#include "timer.h"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070037
Rohit Vaswanif688fa62011-10-13 18:13:10 -070038#define WDT0_RST 0x38
39#define WDT0_EN 0x40
40#define WDT0_BARK_TIME 0x4C
41#define WDT0_BITE_TIME 0x5C
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070042
43#define PSHOLD_CTL_SU (MSM_TLMM_BASE + 0x820)
44
45#define RESTART_REASON_ADDR 0x65C
46#define DLOAD_MODE_ADDR 0x0
47
Abhijeet Dharmapurikar9259fef2011-09-24 19:07:48 -070048#define SCM_IO_DISABLE_PMIC_ARBITER 1
49
Jaeseong GIMb8499412012-06-26 10:36:57 -070050#ifdef CONFIG_LGE_CRASH_HANDLER
51#define LGE_ERROR_HANDLER_MAGIC_NUM 0xA97F2C46
52#define LGE_ERROR_HANDLER_MAGIC_ADDR 0x18
53void *lge_error_handler_cookie_addr;
Jaeseong GIM73ec1d02012-08-04 00:26:57 -070054static int ssr_magic_number = 0;
Jaeseong GIMb8499412012-06-26 10:36:57 -070055#endif
56
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070057static int restart_mode;
58void *restart_reason;
59
Abhijeet Dharmapurikar6d565fd2011-09-15 18:49:56 -070060int pmic_reset_irq;
Rohit Vaswanif688fa62011-10-13 18:13:10 -070061static void __iomem *msm_tmr0_base;
Abhijeet Dharmapurikar6d565fd2011-09-15 18:49:56 -070062
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070063#ifdef CONFIG_MSM_DLOAD_MODE
64static int in_panic;
65static void *dload_mode_addr;
66
67/* Download mode master kill-switch */
68static int dload_set(const char *val, struct kernel_param *kp);
Ameya Thakur7212b392012-12-11 17:56:03 -080069static int download_mode = 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070070module_param_call(download_mode, dload_set, param_get_int,
71 &download_mode, 0644);
72
73static int panic_prep_restart(struct notifier_block *this,
74 unsigned long event, void *ptr)
75{
76 in_panic = 1;
77 return NOTIFY_DONE;
78}
79
80static struct notifier_block panic_blk = {
81 .notifier_call = panic_prep_restart,
82};
83
84static void set_dload_mode(int on)
85{
86 if (dload_mode_addr) {
87 __raw_writel(on ? 0xE47B337D : 0, dload_mode_addr);
88 __raw_writel(on ? 0xCE14091A : 0,
89 dload_mode_addr + sizeof(unsigned int));
Jaeseong GIMb8499412012-06-26 10:36:57 -070090#ifdef CONFIG_LGE_CRASH_HANDLER
91 __raw_writel(on ? LGE_ERROR_HANDLER_MAGIC_NUM : 0,
92 lge_error_handler_cookie_addr);
93#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070094 mb();
95 }
96}
97
98static int dload_set(const char *val, struct kernel_param *kp)
99{
100 int ret;
101 int old_val = download_mode;
102
103 ret = param_set_int(val, kp);
104
105 if (ret)
106 return ret;
107
108 /* If download_mode is not zero or one, ignore. */
109 if (download_mode >> 1) {
110 download_mode = old_val;
111 return -EINVAL;
112 }
113
114 set_dload_mode(download_mode);
Jaeseong GIM73ec1d02012-08-04 00:26:57 -0700115#ifdef CONFIG_LGE_CRASH_HANDLER
116 ssr_magic_number = 0;
117#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700118
119 return 0;
120}
121#else
122#define set_dload_mode(x) do {} while (0)
123#endif
124
125void msm_set_restart_mode(int mode)
126{
127 restart_mode = mode;
Jaeseong GIMe7640062012-07-23 15:46:47 -0700128#ifdef CONFIG_LGE_CRASH_HANDLER
129 if (download_mode == 1 && (mode & 0xFFFF0000) == 0x6D630000)
130 panic("LGE crash handler detected panic");
131#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700132}
133EXPORT_SYMBOL(msm_set_restart_mode);
134
Abhijeet Dharmapurikar6d565fd2011-09-15 18:49:56 -0700135static void __msm_power_off(int lower_pshold)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700136{
Abhijeet Dharmapurikar6d565fd2011-09-15 18:49:56 -0700137 printk(KERN_CRIT "Powering off the SoC\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700138#ifdef CONFIG_MSM_DLOAD_MODE
139 set_dload_mode(0);
140#endif
Jeff Ohlstein28009a82011-07-25 19:21:26 -0700141 pm8xxx_reset_pwr_off(0);
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530142
Abhijeet Dharmapurikar9259fef2011-09-24 19:07:48 -0700143 if (lower_pshold) {
Abhijeet Dharmapurikar6d565fd2011-09-15 18:49:56 -0700144 __raw_writel(0, PSHOLD_CTL_SU);
Abhijeet Dharmapurikar9259fef2011-09-24 19:07:48 -0700145 mdelay(10000);
146 printk(KERN_ERR "Powering off has failed\n");
147 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700148 return;
149}
150
Abhijeet Dharmapurikar6d565fd2011-09-15 18:49:56 -0700151static void msm_power_off(void)
152{
153 /* MSM initiated power off, lower ps_hold */
154 __msm_power_off(1);
155}
156
157static void cpu_power_off(void *data)
158{
Abhijeet Dharmapurikar9259fef2011-09-24 19:07:48 -0700159 int rc;
160
Abhijeet Dharmapurikar6d565fd2011-09-15 18:49:56 -0700161 pr_err("PMIC Initiated shutdown %s cpu=%d\n", __func__,
162 smp_processor_id());
Abhijeet Dharmapurikar9259fef2011-09-24 19:07:48 -0700163 if (smp_processor_id() == 0) {
Abhijeet Dharmapurikar6d565fd2011-09-15 18:49:56 -0700164 /*
165 * PMIC initiated power off, do not lower ps_hold, pmic will
166 * shut msm down
167 */
168 __msm_power_off(0);
169
Abhijeet Dharmapurikar9259fef2011-09-24 19:07:48 -0700170 pet_watchdog();
171 pr_err("Calling scm to disable arbiter\n");
172 /* call secure manager to disable arbiter and never return */
173 rc = scm_call_atomic1(SCM_SVC_PWR,
174 SCM_IO_DISABLE_PMIC_ARBITER, 1);
175
176 pr_err("SCM returned even when asked to busy loop rc=%d\n", rc);
177 pr_err("waiting on pmic to shut msm down\n");
178 }
179
Abhijeet Dharmapurikar6d565fd2011-09-15 18:49:56 -0700180 preempt_disable();
181 while (1)
182 ;
183}
184
185static irqreturn_t resout_irq_handler(int irq, void *dev_id)
186{
187 pr_warn("%s PMIC Initiated shutdown\n", __func__);
188 oops_in_progress = 1;
189 smp_call_function_many(cpu_online_mask, cpu_power_off, NULL, 0);
190 if (smp_processor_id() == 0)
191 cpu_power_off(NULL);
192 preempt_disable();
193 while (1)
194 ;
195 return IRQ_HANDLED;
196}
197
Jaeseong GIMb8499412012-06-26 10:36:57 -0700198#ifdef CONFIG_LGE_CRASH_HANDLER
Jaeseong GIMb8499412012-06-26 10:36:57 -0700199#define SUBSYS_NAME_MAX_LENGTH 40
200
201int get_ssr_magic_number(void)
202{
203 return ssr_magic_number;
204}
205
206void set_ssr_magic_number(const char* subsys_name)
207{
208 int i;
209 const char *subsys_list[] = {
210 "modem", "riva", "dsps", "lpass",
211 "external_modem", "gss",
212 };
213
214 ssr_magic_number = (0x6d630000 | 0x0000f000);
215
216 for (i=0; i < ARRAY_SIZE(subsys_list); i++) {
217 if (!strncmp(subsys_list[i], subsys_name,
218 SUBSYS_NAME_MAX_LENGTH)) {
219 ssr_magic_number = (0x6d630000 | ((i+1)<<12));
220 break;
221 }
222 }
223}
224
225void set_kernel_crash_magic_number(void)
226{
Jaeseong GIMaaeabf32012-07-31 15:25:25 -0700227 pet_watchdog();
Jaeseong GIMb8499412012-06-26 10:36:57 -0700228 if (ssr_magic_number == 0)
229 __raw_writel(0x6d630100, restart_reason);
230 else
231 __raw_writel(restart_mode, restart_reason);
232}
233#endif /* CONFIG_LGE_CRASH_HANDLER */
234
Jeff Ohlsteindd0dd9b2012-05-29 17:47:21 -0700235void msm_restart(char mode, const char *cmd)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700236{
237
238#ifdef CONFIG_MSM_DLOAD_MODE
239
240 /* This looks like a normal reboot at this point. */
241 set_dload_mode(0);
242
243 /* Write download mode flags if we're panic'ing */
244 set_dload_mode(in_panic);
245
246 /* Write download mode flags if restart_mode says so */
Jaeseong GIMb8499412012-06-26 10:36:57 -0700247 if (restart_mode == RESTART_DLOAD) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700248 set_dload_mode(1);
Jaeseong GIMb8499412012-06-26 10:36:57 -0700249#ifdef CONFIG_LGE_CRASH_HANDLER
250 writel(0x6d63c421, restart_reason);
251 goto reset;
252#endif
253 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700254
255 /* Kill download mode if master-kill switch is set */
256 if (!download_mode)
257 set_dload_mode(0);
258#endif
259
260 printk(KERN_NOTICE "Going down for restart now\n");
261
Jeff Ohlstein28009a82011-07-25 19:21:26 -0700262 pm8xxx_reset_pwr_off(1);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700263
264 if (cmd != NULL) {
265 if (!strncmp(cmd, "bootloader", 10)) {
266 __raw_writel(0x77665500, restart_reason);
267 } else if (!strncmp(cmd, "recovery", 8)) {
268 __raw_writel(0x77665502, restart_reason);
269 } else if (!strncmp(cmd, "oem-", 4)) {
270 unsigned long code;
271 code = simple_strtoul(cmd + 4, NULL, 16) & 0xff;
272 __raw_writel(0x6f656d00 | code, restart_reason);
273 } else {
274 __raw_writel(0x77665501, restart_reason);
275 }
Devin Kim95904b82012-10-10 12:32:02 -0700276 } else {
277 __raw_writel(0x77665501, restart_reason);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700278 }
Jaeseong GIM88a0c172012-08-21 18:03:23 -0700279#ifdef CONFIG_LGE_CRASH_HANDLER
280 if (in_panic == 1)
281 set_kernel_crash_magic_number();
282reset:
Jaeseong GIMb8499412012-06-26 10:36:57 -0700283#endif /* CONFIG_LGE_CRASH_HANDLER */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700284
Rohit Vaswanif688fa62011-10-13 18:13:10 -0700285 __raw_writel(0, msm_tmr0_base + WDT0_EN);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700286 if (!(machine_is_msm8x60_fusion() || machine_is_msm8x60_fusn_ffa())) {
287 mb();
288 __raw_writel(0, PSHOLD_CTL_SU); /* Actually reset the chip */
289 mdelay(5000);
290 pr_notice("PS_HOLD didn't work, falling back to watchdog\n");
291 }
292
Rohit Vaswanif688fa62011-10-13 18:13:10 -0700293 __raw_writel(1, msm_tmr0_base + WDT0_RST);
294 __raw_writel(5*0x31F3, msm_tmr0_base + WDT0_BARK_TIME);
295 __raw_writel(0x31F3, msm_tmr0_base + WDT0_BITE_TIME);
296 __raw_writel(1, msm_tmr0_base + WDT0_EN);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700297
298 mdelay(10000);
299 printk(KERN_ERR "Restarting has failed\n");
300}
301
Jeff Ohlsteina238a982012-05-14 15:45:54 -0700302static int __init msm_pmic_restart_init(void)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700303{
Abhijeet Dharmapurikar6d565fd2011-09-15 18:49:56 -0700304 int rc;
305
Abhijeet Dharmapurikar6d565fd2011-09-15 18:49:56 -0700306 if (pmic_reset_irq != 0) {
307 rc = request_any_context_irq(pmic_reset_irq,
308 resout_irq_handler, IRQF_TRIGGER_HIGH,
309 "restart_from_pmic", NULL);
310 if (rc < 0)
311 pr_err("pmic restart irq fail rc = %d\n", rc);
312 } else {
313 pr_warn("no pmic restart interrupt specified\n");
314 }
315
Jaeseong GIMb8499412012-06-26 10:36:57 -0700316#ifdef CONFIG_LGE_CRASH_HANDLER
317 __raw_writel(0x6d63ad00, restart_reason);
318#endif
319
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700320 return 0;
321}
322
Jeff Ohlsteina238a982012-05-14 15:45:54 -0700323late_initcall(msm_pmic_restart_init);
324
325static int __init msm_restart_init(void)
326{
327#ifdef CONFIG_MSM_DLOAD_MODE
328 atomic_notifier_chain_register(&panic_notifier_list, &panic_blk);
329 dload_mode_addr = MSM_IMEM_BASE + DLOAD_MODE_ADDR;
Jaeseong GIMb8499412012-06-26 10:36:57 -0700330#ifdef CONFIG_LGE_CRASH_HANDLER
331 lge_error_handler_cookie_addr = MSM_IMEM_BASE +
332 LGE_ERROR_HANDLER_MAGIC_ADDR;
333#endif
Jeff Ohlsteina238a982012-05-14 15:45:54 -0700334 set_dload_mode(download_mode);
335#endif
336 msm_tmr0_base = msm_timer_get_timer0_base();
337 restart_reason = MSM_IMEM_BASE + RESTART_REASON_ADDR;
338 pm_power_off = msm_power_off;
339
340 return 0;
341}
342early_initcall(msm_restart_init);