blob: c85f7a1381c91a8b827371d54882256edebf4791 [file] [log] [blame]
Pratik Patele08204d2013-03-12 17:43:56 -07001/* Copyright (c) 2010-2013, 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
Ameya Thakur92f29dc2013-06-28 14:34:47 -070050#define EMERGENCY_DLOAD_MODE_ADDR 0xFE0
51#define EMERGENCY_DLOAD_MAGIC1 0x322A4F99
52#define EMERGENCY_DLOAD_MAGIC2 0xC67E4350
53#define EMERGENCY_DLOAD_MAGIC3 0x77777777
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070054
Abhijeet Dharmapurikar9259fef2011-09-24 19:07:48 -070055#define SCM_IO_DISABLE_PMIC_ARBITER 1
56
Stepan Moskovchenkoa000d132012-08-14 21:05:14 -070057#ifdef CONFIG_MSM_RESTART_V2
58#define use_restart_v2() 1
59#else
60#define use_restart_v2() 0
61#endif
62
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070063static int restart_mode;
64void *restart_reason;
65
Abhijeet Dharmapurikar6d565fd2011-09-15 18:49:56 -070066int pmic_reset_irq;
Rohit Vaswanif688fa62011-10-13 18:13:10 -070067static void __iomem *msm_tmr0_base;
Abhijeet Dharmapurikar6d565fd2011-09-15 18:49:56 -070068
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070069#ifdef CONFIG_MSM_DLOAD_MODE
70static int in_panic;
71static void *dload_mode_addr;
David Collins0e50f3e2013-07-09 11:52:40 -070072static bool dload_mode_enabled;
Ameya Thakur92f29dc2013-06-28 14:34:47 -070073static void *emergency_dload_mode_addr;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070074
75/* Download mode master kill-switch */
76static int dload_set(const char *val, struct kernel_param *kp);
77static int download_mode = 1;
78module_param_call(download_mode, dload_set, param_get_int,
79 &download_mode, 0644);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070080static int panic_prep_restart(struct notifier_block *this,
81 unsigned long event, void *ptr)
82{
83 in_panic = 1;
84 return NOTIFY_DONE;
85}
86
87static struct notifier_block panic_blk = {
88 .notifier_call = panic_prep_restart,
89};
90
91static void set_dload_mode(int on)
92{
93 if (dload_mode_addr) {
94 __raw_writel(on ? 0xE47B337D : 0, dload_mode_addr);
95 __raw_writel(on ? 0xCE14091A : 0,
96 dload_mode_addr + sizeof(unsigned int));
97 mb();
David Collins0e50f3e2013-07-09 11:52:40 -070098 dload_mode_enabled = on;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070099 }
100}
101
David Collins0e50f3e2013-07-09 11:52:40 -0700102static bool get_dload_mode(void)
103{
104 return dload_mode_enabled;
105}
106
Ameya Thakur92f29dc2013-06-28 14:34:47 -0700107static void enable_emergency_dload_mode(void)
108{
109 if (emergency_dload_mode_addr) {
110 __raw_writel(EMERGENCY_DLOAD_MAGIC1,
111 emergency_dload_mode_addr);
112 __raw_writel(EMERGENCY_DLOAD_MAGIC2,
113 emergency_dload_mode_addr +
114 sizeof(unsigned int));
115 __raw_writel(EMERGENCY_DLOAD_MAGIC3,
116 emergency_dload_mode_addr +
117 (2 * sizeof(unsigned int)));
118 mb();
119 }
120}
121
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700122static int dload_set(const char *val, struct kernel_param *kp)
123{
124 int ret;
125 int old_val = download_mode;
126
127 ret = param_set_int(val, kp);
128
129 if (ret)
130 return ret;
131
132 /* If download_mode is not zero or one, ignore. */
133 if (download_mode >> 1) {
134 download_mode = old_val;
135 return -EINVAL;
136 }
137
138 set_dload_mode(download_mode);
139
140 return 0;
141}
142#else
143#define set_dload_mode(x) do {} while (0)
David Collins0e50f3e2013-07-09 11:52:40 -0700144
Ameya Thakur92f29dc2013-06-28 14:34:47 -0700145static void enable_emergency_dload_mode(void)
146{
147 printk(KERN_ERR "dload mode is not enabled on target\n");
148}
149
David Collins0e50f3e2013-07-09 11:52:40 -0700150static bool get_dload_mode(void)
151{
152 return false;
153}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700154#endif
155
156void msm_set_restart_mode(int mode)
157{
158 restart_mode = mode;
159}
160EXPORT_SYMBOL(msm_set_restart_mode);
161
Abhijeet Dharmapurikar6d565fd2011-09-15 18:49:56 -0700162static void __msm_power_off(int lower_pshold)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700163{
Abhijeet Dharmapurikar6d565fd2011-09-15 18:49:56 -0700164 printk(KERN_CRIT "Powering off the SoC\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700165#ifdef CONFIG_MSM_DLOAD_MODE
166 set_dload_mode(0);
167#endif
Jeff Ohlstein28009a82011-07-25 19:21:26 -0700168 pm8xxx_reset_pwr_off(0);
David Collins0e50f3e2013-07-09 11:52:40 -0700169 qpnp_pon_system_pwr_off(PON_POWER_OFF_SHUTDOWN);
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530170
Abhijeet Dharmapurikar9259fef2011-09-24 19:07:48 -0700171 if (lower_pshold) {
Shashank Mittale77ce162012-08-28 18:19:20 -0700172 if (!use_restart_v2())
173 __raw_writel(0, PSHOLD_CTL_SU);
174 else
175 __raw_writel(0, MSM_MPM2_PSHOLD_BASE);
176
Abhijeet Dharmapurikar9259fef2011-09-24 19:07:48 -0700177 mdelay(10000);
178 printk(KERN_ERR "Powering off has failed\n");
179 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700180 return;
181}
182
Abhijeet Dharmapurikar6d565fd2011-09-15 18:49:56 -0700183static void msm_power_off(void)
184{
185 /* MSM initiated power off, lower ps_hold */
186 __msm_power_off(1);
187}
188
189static void cpu_power_off(void *data)
190{
Abhijeet Dharmapurikar9259fef2011-09-24 19:07:48 -0700191 int rc;
192
Abhijeet Dharmapurikar6d565fd2011-09-15 18:49:56 -0700193 pr_err("PMIC Initiated shutdown %s cpu=%d\n", __func__,
194 smp_processor_id());
Abhijeet Dharmapurikar9259fef2011-09-24 19:07:48 -0700195 if (smp_processor_id() == 0) {
Abhijeet Dharmapurikar6d565fd2011-09-15 18:49:56 -0700196 /*
197 * PMIC initiated power off, do not lower ps_hold, pmic will
198 * shut msm down
199 */
200 __msm_power_off(0);
201
Abhijeet Dharmapurikar9259fef2011-09-24 19:07:48 -0700202 pet_watchdog();
203 pr_err("Calling scm to disable arbiter\n");
204 /* call secure manager to disable arbiter and never return */
205 rc = scm_call_atomic1(SCM_SVC_PWR,
206 SCM_IO_DISABLE_PMIC_ARBITER, 1);
207
208 pr_err("SCM returned even when asked to busy loop rc=%d\n", rc);
209 pr_err("waiting on pmic to shut msm down\n");
210 }
211
Abhijeet Dharmapurikar6d565fd2011-09-15 18:49:56 -0700212 preempt_disable();
213 while (1)
214 ;
215}
216
217static irqreturn_t resout_irq_handler(int irq, void *dev_id)
218{
219 pr_warn("%s PMIC Initiated shutdown\n", __func__);
220 oops_in_progress = 1;
221 smp_call_function_many(cpu_online_mask, cpu_power_off, NULL, 0);
222 if (smp_processor_id() == 0)
223 cpu_power_off(NULL);
224 preempt_disable();
225 while (1)
226 ;
227 return IRQ_HANDLED;
228}
229
Stepan Moskovchenkoa000d132012-08-14 21:05:14 -0700230static void msm_restart_prepare(const char *cmd)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700231{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700232#ifdef CONFIG_MSM_DLOAD_MODE
233
234 /* This looks like a normal reboot at this point. */
235 set_dload_mode(0);
236
237 /* Write download mode flags if we're panic'ing */
238 set_dload_mode(in_panic);
239
240 /* Write download mode flags if restart_mode says so */
241 if (restart_mode == RESTART_DLOAD)
242 set_dload_mode(1);
243
244 /* Kill download mode if master-kill switch is set */
245 if (!download_mode)
246 set_dload_mode(0);
247#endif
248
Jeff Ohlstein28009a82011-07-25 19:21:26 -0700249 pm8xxx_reset_pwr_off(1);
David Collins0e50f3e2013-07-09 11:52:40 -0700250
251 /* Hard reset the PMIC unless memory contents must be maintained. */
252 if (get_dload_mode() || (cmd != NULL && cmd[0] != '\0'))
253 qpnp_pon_system_pwr_off(PON_POWER_OFF_WARM_RESET);
254 else
255 qpnp_pon_system_pwr_off(PON_POWER_OFF_HARD_RESET);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700256
257 if (cmd != NULL) {
258 if (!strncmp(cmd, "bootloader", 10)) {
259 __raw_writel(0x77665500, restart_reason);
260 } else if (!strncmp(cmd, "recovery", 8)) {
261 __raw_writel(0x77665502, restart_reason);
262 } else if (!strncmp(cmd, "oem-", 4)) {
263 unsigned long code;
264 code = simple_strtoul(cmd + 4, NULL, 16) & 0xff;
265 __raw_writel(0x6f656d00 | code, restart_reason);
Ameya Thakur92f29dc2013-06-28 14:34:47 -0700266 } else if (!strncmp(cmd, "edl", 3)) {
267 enable_emergency_dload_mode();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700268 } else {
269 __raw_writel(0x77665501, restart_reason);
270 }
271 }
Subbaraman Narayanamurthy0c7a2872012-09-14 13:32:08 -0700272
273 flush_cache_all();
Abhimanyu Kapurf12bcca2012-10-22 18:51:01 -0700274 outer_flush_all();
Stepan Moskovchenkoa000d132012-08-14 21:05:14 -0700275}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700276
Stepan Moskovchenkoa000d132012-08-14 21:05:14 -0700277void msm_restart(char mode, const char *cmd)
278{
279 printk(KERN_NOTICE "Going down for restart now\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700280
Subbaraman Narayanamurthye31f2792012-08-22 21:07:22 -0700281 msm_restart_prepare(cmd);
282
Stepan Moskovchenkoa000d132012-08-14 21:05:14 -0700283 if (!use_restart_v2()) {
Stepan Moskovchenkoa000d132012-08-14 21:05:14 -0700284 __raw_writel(0, msm_tmr0_base + WDT0_EN);
285 if (!(machine_is_msm8x60_fusion() ||
286 machine_is_msm8x60_fusn_ffa())) {
287 mb();
288 /* Actually reset the chip */
289 __raw_writel(0, PSHOLD_CTL_SU);
290 mdelay(5000);
291 pr_notice("PS_HOLD didn't work, falling back to watchdog\n");
292 }
293
294 __raw_writel(1, msm_tmr0_base + WDT0_RST);
295 __raw_writel(5*0x31F3, msm_tmr0_base + WDT0_BARK_TIME);
296 __raw_writel(0x31F3, msm_tmr0_base + WDT0_BITE_TIME);
297 __raw_writel(1, msm_tmr0_base + WDT0_EN);
Pushkar Joshi892bcfc2012-09-27 12:33:31 -0700298 } else {
Pratik Patele08204d2013-03-12 17:43:56 -0700299 /* Needed to bypass debug image on some chips */
Pushkar Joshi892bcfc2012-09-27 12:33:31 -0700300 msm_disable_wdog_debug();
Stepan Moskovchenkoa000d132012-08-14 21:05:14 -0700301 __raw_writel(0, MSM_MPM2_PSHOLD_BASE);
Pushkar Joshi892bcfc2012-09-27 12:33:31 -0700302 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700303
304 mdelay(10000);
305 printk(KERN_ERR "Restarting has failed\n");
306}
307
Jeff Ohlsteina238a9852012-05-14 15:45:54 -0700308static int __init msm_pmic_restart_init(void)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700309{
Abhijeet Dharmapurikar6d565fd2011-09-15 18:49:56 -0700310 int rc;
311
Michael Bohan0e4e6b32012-12-20 12:22:59 -0800312 if (use_restart_v2())
313 return 0;
314
Abhijeet Dharmapurikar6d565fd2011-09-15 18:49:56 -0700315 if (pmic_reset_irq != 0) {
316 rc = request_any_context_irq(pmic_reset_irq,
317 resout_irq_handler, IRQF_TRIGGER_HIGH,
318 "restart_from_pmic", NULL);
319 if (rc < 0)
320 pr_err("pmic restart irq fail rc = %d\n", rc);
321 } else {
322 pr_warn("no pmic restart interrupt specified\n");
323 }
324
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700325 return 0;
326}
327
Jeff Ohlsteina238a9852012-05-14 15:45:54 -0700328late_initcall(msm_pmic_restart_init);
329
330static int __init msm_restart_init(void)
331{
332#ifdef CONFIG_MSM_DLOAD_MODE
333 atomic_notifier_chain_register(&panic_notifier_list, &panic_blk);
334 dload_mode_addr = MSM_IMEM_BASE + DLOAD_MODE_ADDR;
Ameya Thakur92f29dc2013-06-28 14:34:47 -0700335 emergency_dload_mode_addr = MSM_IMEM_BASE +
336 EMERGENCY_DLOAD_MODE_ADDR;
Jeff Ohlsteina238a9852012-05-14 15:45:54 -0700337 set_dload_mode(download_mode);
338#endif
339 msm_tmr0_base = msm_timer_get_timer0_base();
340 restart_reason = MSM_IMEM_BASE + RESTART_REASON_ADDR;
341 pm_power_off = msm_power_off;
342
343 return 0;
344}
345early_initcall(msm_restart_init);