blob: a67af45557139743c2c8e78ddb7304391e183e93 [file] [log] [blame]
Duy Truong790f06d2013-02-13 16:38:12 -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>
Michael Bohane7204222012-09-07 10:21:18 -070027#include <linux/qpnp/power-on.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070028
29#include <asm/mach-types.h>
Subbaraman Narayanamurthy0c7a2872012-09-14 13:32:08 -070030#include <asm/cacheflush.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070031
32#include <mach/msm_iomap.h>
33#include <mach/restart.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070034#include <mach/socinfo.h>
Abhijeet Dharmapurikar6d565fd2011-09-15 18:49:56 -070035#include <mach/irqs.h>
Abhijeet Dharmapurikar9259fef2011-09-24 19:07:48 -070036#include <mach/scm.h>
37#include "msm_watchdog.h"
Rohit Vaswanif688fa62011-10-13 18:13:10 -070038#include "timer.h"
Pushkar Joshi892bcfc2012-09-27 12:33:31 -070039#include "wdog_debug.h"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070040
Rohit Vaswanif688fa62011-10-13 18:13:10 -070041#define WDT0_RST 0x38
42#define WDT0_EN 0x40
43#define WDT0_BARK_TIME 0x4C
44#define WDT0_BITE_TIME 0x5C
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070045
46#define PSHOLD_CTL_SU (MSM_TLMM_BASE + 0x820)
47
48#define RESTART_REASON_ADDR 0x65C
49#define DLOAD_MODE_ADDR 0x0
50
Abhijeet Dharmapurikar9259fef2011-09-24 19:07:48 -070051#define SCM_IO_DISABLE_PMIC_ARBITER 1
52
Stepan Moskovchenkoa000d132012-08-14 21:05:14 -070053#ifdef CONFIG_MSM_RESTART_V2
54#define use_restart_v2() 1
55#else
56#define use_restart_v2() 0
57#endif
58
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070059static int restart_mode;
60void *restart_reason;
61
Abhijeet Dharmapurikar6d565fd2011-09-15 18:49:56 -070062int pmic_reset_irq;
Rohit Vaswanif688fa62011-10-13 18:13:10 -070063static void __iomem *msm_tmr0_base;
Abhijeet Dharmapurikar6d565fd2011-09-15 18:49:56 -070064
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070065#ifdef CONFIG_MSM_DLOAD_MODE
66static int in_panic;
67static void *dload_mode_addr;
68
69/* Download mode master kill-switch */
70static int dload_set(const char *val, struct kernel_param *kp);
71static int download_mode = 1;
72module_param_call(download_mode, dload_set, param_get_int,
73 &download_mode, 0644);
74
75static int panic_prep_restart(struct notifier_block *this,
76 unsigned long event, void *ptr)
77{
78 in_panic = 1;
79 return NOTIFY_DONE;
80}
81
82static struct notifier_block panic_blk = {
83 .notifier_call = panic_prep_restart,
84};
85
86static void set_dload_mode(int on)
87{
88 if (dload_mode_addr) {
89 __raw_writel(on ? 0xE47B337D : 0, dload_mode_addr);
90 __raw_writel(on ? 0xCE14091A : 0,
91 dload_mode_addr + sizeof(unsigned int));
92 mb();
93 }
94}
95
96static int dload_set(const char *val, struct kernel_param *kp)
97{
98 int ret;
99 int old_val = download_mode;
100
101 ret = param_set_int(val, kp);
102
103 if (ret)
104 return ret;
105
106 /* If download_mode is not zero or one, ignore. */
107 if (download_mode >> 1) {
108 download_mode = old_val;
109 return -EINVAL;
110 }
111
112 set_dload_mode(download_mode);
113
114 return 0;
115}
116#else
117#define set_dload_mode(x) do {} while (0)
118#endif
119
120void msm_set_restart_mode(int mode)
121{
122 restart_mode = mode;
123}
124EXPORT_SYMBOL(msm_set_restart_mode);
125
Abhijeet Dharmapurikar6d565fd2011-09-15 18:49:56 -0700126static void __msm_power_off(int lower_pshold)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700127{
Abhijeet Dharmapurikar6d565fd2011-09-15 18:49:56 -0700128 printk(KERN_CRIT "Powering off the SoC\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700129#ifdef CONFIG_MSM_DLOAD_MODE
130 set_dload_mode(0);
131#endif
Jeff Ohlstein28009a82011-07-25 19:21:26 -0700132 pm8xxx_reset_pwr_off(0);
Michael Bohane7204222012-09-07 10:21:18 -0700133 qpnp_pon_system_pwr_off(0);
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530134
Abhijeet Dharmapurikar9259fef2011-09-24 19:07:48 -0700135 if (lower_pshold) {
Shashank Mittale77ce162012-08-28 18:19:20 -0700136 if (!use_restart_v2())
137 __raw_writel(0, PSHOLD_CTL_SU);
138 else
139 __raw_writel(0, MSM_MPM2_PSHOLD_BASE);
140
Abhijeet Dharmapurikar9259fef2011-09-24 19:07:48 -0700141 mdelay(10000);
142 printk(KERN_ERR "Powering off has failed\n");
143 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700144 return;
145}
146
Abhijeet Dharmapurikar6d565fd2011-09-15 18:49:56 -0700147static void msm_power_off(void)
148{
149 /* MSM initiated power off, lower ps_hold */
150 __msm_power_off(1);
151}
152
153static void cpu_power_off(void *data)
154{
Abhijeet Dharmapurikar9259fef2011-09-24 19:07:48 -0700155 int rc;
156
Abhijeet Dharmapurikar6d565fd2011-09-15 18:49:56 -0700157 pr_err("PMIC Initiated shutdown %s cpu=%d\n", __func__,
158 smp_processor_id());
Abhijeet Dharmapurikar9259fef2011-09-24 19:07:48 -0700159 if (smp_processor_id() == 0) {
Abhijeet Dharmapurikar6d565fd2011-09-15 18:49:56 -0700160 /*
161 * PMIC initiated power off, do not lower ps_hold, pmic will
162 * shut msm down
163 */
164 __msm_power_off(0);
165
Abhijeet Dharmapurikar9259fef2011-09-24 19:07:48 -0700166 pet_watchdog();
167 pr_err("Calling scm to disable arbiter\n");
168 /* call secure manager to disable arbiter and never return */
169 rc = scm_call_atomic1(SCM_SVC_PWR,
170 SCM_IO_DISABLE_PMIC_ARBITER, 1);
171
172 pr_err("SCM returned even when asked to busy loop rc=%d\n", rc);
173 pr_err("waiting on pmic to shut msm down\n");
174 }
175
Abhijeet Dharmapurikar6d565fd2011-09-15 18:49:56 -0700176 preempt_disable();
177 while (1)
178 ;
179}
180
181static irqreturn_t resout_irq_handler(int irq, void *dev_id)
182{
183 pr_warn("%s PMIC Initiated shutdown\n", __func__);
184 oops_in_progress = 1;
185 smp_call_function_many(cpu_online_mask, cpu_power_off, NULL, 0);
186 if (smp_processor_id() == 0)
187 cpu_power_off(NULL);
188 preempt_disable();
189 while (1)
190 ;
191 return IRQ_HANDLED;
192}
193
Stepan Moskovchenkoa000d132012-08-14 21:05:14 -0700194static void msm_restart_prepare(const char *cmd)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700195{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700196#ifdef CONFIG_MSM_DLOAD_MODE
197
198 /* This looks like a normal reboot at this point. */
199 set_dload_mode(0);
200
201 /* Write download mode flags if we're panic'ing */
202 set_dload_mode(in_panic);
203
204 /* Write download mode flags if restart_mode says so */
205 if (restart_mode == RESTART_DLOAD)
206 set_dload_mode(1);
207
208 /* Kill download mode if master-kill switch is set */
209 if (!download_mode)
210 set_dload_mode(0);
211#endif
212
Jeff Ohlstein28009a82011-07-25 19:21:26 -0700213 pm8xxx_reset_pwr_off(1);
Michael Bohane7204222012-09-07 10:21:18 -0700214 qpnp_pon_system_pwr_off(1);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700215
216 if (cmd != NULL) {
217 if (!strncmp(cmd, "bootloader", 10)) {
218 __raw_writel(0x77665500, restart_reason);
219 } else if (!strncmp(cmd, "recovery", 8)) {
220 __raw_writel(0x77665502, restart_reason);
221 } else if (!strncmp(cmd, "oem-", 4)) {
222 unsigned long code;
223 code = simple_strtoul(cmd + 4, NULL, 16) & 0xff;
224 __raw_writel(0x6f656d00 | code, restart_reason);
225 } else {
226 __raw_writel(0x77665501, restart_reason);
227 }
228 }
Subbaraman Narayanamurthy0c7a2872012-09-14 13:32:08 -0700229
230 flush_cache_all();
Abhimanyu Kapurf12bcca2012-10-22 18:51:01 -0700231 outer_flush_all();
Stepan Moskovchenkoa000d132012-08-14 21:05:14 -0700232}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700233
Stepan Moskovchenkoa000d132012-08-14 21:05:14 -0700234void msm_restart(char mode, const char *cmd)
235{
236 printk(KERN_NOTICE "Going down for restart now\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700237
Subbaraman Narayanamurthye31f2792012-08-22 21:07:22 -0700238 msm_restart_prepare(cmd);
239
Stepan Moskovchenkoa000d132012-08-14 21:05:14 -0700240 if (!use_restart_v2()) {
Stepan Moskovchenkoa000d132012-08-14 21:05:14 -0700241 __raw_writel(0, msm_tmr0_base + WDT0_EN);
242 if (!(machine_is_msm8x60_fusion() ||
243 machine_is_msm8x60_fusn_ffa())) {
244 mb();
245 /* Actually reset the chip */
246 __raw_writel(0, PSHOLD_CTL_SU);
247 mdelay(5000);
248 pr_notice("PS_HOLD didn't work, falling back to watchdog\n");
249 }
250
251 __raw_writel(1, msm_tmr0_base + WDT0_RST);
252 __raw_writel(5*0x31F3, msm_tmr0_base + WDT0_BARK_TIME);
253 __raw_writel(0x31F3, msm_tmr0_base + WDT0_BITE_TIME);
254 __raw_writel(1, msm_tmr0_base + WDT0_EN);
Pushkar Joshi892bcfc2012-09-27 12:33:31 -0700255 } else {
256 /* Needed for 8974: Reset GCC_WDOG_DEBUG register */
257 msm_disable_wdog_debug();
Stepan Moskovchenkoa000d132012-08-14 21:05:14 -0700258 __raw_writel(0, MSM_MPM2_PSHOLD_BASE);
Pushkar Joshi892bcfc2012-09-27 12:33:31 -0700259 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700260
261 mdelay(10000);
262 printk(KERN_ERR "Restarting has failed\n");
263}
264
Jeff Ohlsteina238a9852012-05-14 15:45:54 -0700265static int __init msm_pmic_restart_init(void)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700266{
Abhijeet Dharmapurikar6d565fd2011-09-15 18:49:56 -0700267 int rc;
268
Michael Bohan0e4e6b32012-12-20 12:22:59 -0800269 if (use_restart_v2())
270 return 0;
271
Abhijeet Dharmapurikar6d565fd2011-09-15 18:49:56 -0700272 if (pmic_reset_irq != 0) {
273 rc = request_any_context_irq(pmic_reset_irq,
274 resout_irq_handler, IRQF_TRIGGER_HIGH,
275 "restart_from_pmic", NULL);
276 if (rc < 0)
277 pr_err("pmic restart irq fail rc = %d\n", rc);
278 } else {
279 pr_warn("no pmic restart interrupt specified\n");
280 }
281
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700282 return 0;
283}
284
Jeff Ohlsteina238a9852012-05-14 15:45:54 -0700285late_initcall(msm_pmic_restart_init);
286
287static int __init msm_restart_init(void)
288{
289#ifdef CONFIG_MSM_DLOAD_MODE
290 atomic_notifier_chain_register(&panic_notifier_list, &panic_blk);
291 dload_mode_addr = MSM_IMEM_BASE + DLOAD_MODE_ADDR;
292 set_dload_mode(download_mode);
293#endif
294 msm_tmr0_base = msm_timer_get_timer0_base();
295 restart_reason = MSM_IMEM_BASE + RESTART_REASON_ADDR;
296 pm_power_off = msm_power_off;
297
298 return 0;
299}
300early_initcall(msm_restart_init);