blob: 7288c1d67d6d6f3e12ea24247833d661df9c90e4 [file] [log] [blame]
Jeff Ohlstein2b9c21c2012-04-30 18:57:53 -07001/* Copyright (c) 2010-2012, Code Aurora Forum. 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
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070050static int restart_mode;
51void *restart_reason;
52
Abhijeet Dharmapurikar6d565fd2011-09-15 18:49:56 -070053int pmic_reset_irq;
Rohit Vaswanif688fa62011-10-13 18:13:10 -070054static void __iomem *msm_tmr0_base;
Abhijeet Dharmapurikar6d565fd2011-09-15 18:49:56 -070055
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070056#ifdef CONFIG_MSM_DLOAD_MODE
57static int in_panic;
58static void *dload_mode_addr;
59
60/* Download mode master kill-switch */
61static int dload_set(const char *val, struct kernel_param *kp);
62static int download_mode = 1;
63module_param_call(download_mode, dload_set, param_get_int,
64 &download_mode, 0644);
65
66static int panic_prep_restart(struct notifier_block *this,
67 unsigned long event, void *ptr)
68{
69 in_panic = 1;
70 return NOTIFY_DONE;
71}
72
73static struct notifier_block panic_blk = {
74 .notifier_call = panic_prep_restart,
75};
76
77static void set_dload_mode(int on)
78{
79 if (dload_mode_addr) {
80 __raw_writel(on ? 0xE47B337D : 0, dload_mode_addr);
81 __raw_writel(on ? 0xCE14091A : 0,
82 dload_mode_addr + sizeof(unsigned int));
83 mb();
84 }
85}
86
87static int dload_set(const char *val, struct kernel_param *kp)
88{
89 int ret;
90 int old_val = download_mode;
91
92 ret = param_set_int(val, kp);
93
94 if (ret)
95 return ret;
96
97 /* If download_mode is not zero or one, ignore. */
98 if (download_mode >> 1) {
99 download_mode = old_val;
100 return -EINVAL;
101 }
102
103 set_dload_mode(download_mode);
104
105 return 0;
106}
107#else
108#define set_dload_mode(x) do {} while (0)
109#endif
110
111void msm_set_restart_mode(int mode)
112{
113 restart_mode = mode;
114}
115EXPORT_SYMBOL(msm_set_restart_mode);
116
Abhijeet Dharmapurikar6d565fd2011-09-15 18:49:56 -0700117static void __msm_power_off(int lower_pshold)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700118{
Abhijeet Dharmapurikar6d565fd2011-09-15 18:49:56 -0700119 printk(KERN_CRIT "Powering off the SoC\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700120#ifdef CONFIG_MSM_DLOAD_MODE
121 set_dload_mode(0);
122#endif
Jeff Ohlstein28009a82011-07-25 19:21:26 -0700123 pm8xxx_reset_pwr_off(0);
Anirudh Ghayalc2019332011-11-12 06:29:10 +0530124
Abhijeet Dharmapurikar9259fef2011-09-24 19:07:48 -0700125 if (lower_pshold) {
Abhijeet Dharmapurikar6d565fd2011-09-15 18:49:56 -0700126 __raw_writel(0, PSHOLD_CTL_SU);
Abhijeet Dharmapurikar9259fef2011-09-24 19:07:48 -0700127 mdelay(10000);
128 printk(KERN_ERR "Powering off has failed\n");
129 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700130 return;
131}
132
Abhijeet Dharmapurikar6d565fd2011-09-15 18:49:56 -0700133static void msm_power_off(void)
134{
135 /* MSM initiated power off, lower ps_hold */
136 __msm_power_off(1);
137}
138
139static void cpu_power_off(void *data)
140{
Abhijeet Dharmapurikar9259fef2011-09-24 19:07:48 -0700141 int rc;
142
Abhijeet Dharmapurikar6d565fd2011-09-15 18:49:56 -0700143 pr_err("PMIC Initiated shutdown %s cpu=%d\n", __func__,
144 smp_processor_id());
Abhijeet Dharmapurikar9259fef2011-09-24 19:07:48 -0700145 if (smp_processor_id() == 0) {
Abhijeet Dharmapurikar6d565fd2011-09-15 18:49:56 -0700146 /*
147 * PMIC initiated power off, do not lower ps_hold, pmic will
148 * shut msm down
149 */
150 __msm_power_off(0);
151
Abhijeet Dharmapurikar9259fef2011-09-24 19:07:48 -0700152 pet_watchdog();
153 pr_err("Calling scm to disable arbiter\n");
154 /* call secure manager to disable arbiter and never return */
155 rc = scm_call_atomic1(SCM_SVC_PWR,
156 SCM_IO_DISABLE_PMIC_ARBITER, 1);
157
158 pr_err("SCM returned even when asked to busy loop rc=%d\n", rc);
159 pr_err("waiting on pmic to shut msm down\n");
160 }
161
Abhijeet Dharmapurikar6d565fd2011-09-15 18:49:56 -0700162 preempt_disable();
163 while (1)
164 ;
165}
166
167static irqreturn_t resout_irq_handler(int irq, void *dev_id)
168{
169 pr_warn("%s PMIC Initiated shutdown\n", __func__);
170 oops_in_progress = 1;
171 smp_call_function_many(cpu_online_mask, cpu_power_off, NULL, 0);
172 if (smp_processor_id() == 0)
173 cpu_power_off(NULL);
174 preempt_disable();
175 while (1)
176 ;
177 return IRQ_HANDLED;
178}
179
Jeff Ohlsteindd0dd9b2012-05-29 17:47:21 -0700180void msm_restart(char mode, const char *cmd)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700181{
182
183#ifdef CONFIG_MSM_DLOAD_MODE
184
185 /* This looks like a normal reboot at this point. */
186 set_dload_mode(0);
187
188 /* Write download mode flags if we're panic'ing */
189 set_dload_mode(in_panic);
190
191 /* Write download mode flags if restart_mode says so */
192 if (restart_mode == RESTART_DLOAD)
193 set_dload_mode(1);
194
195 /* Kill download mode if master-kill switch is set */
196 if (!download_mode)
197 set_dload_mode(0);
198#endif
199
200 printk(KERN_NOTICE "Going down for restart now\n");
201
Jeff Ohlstein28009a82011-07-25 19:21:26 -0700202 pm8xxx_reset_pwr_off(1);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700203
204 if (cmd != NULL) {
205 if (!strncmp(cmd, "bootloader", 10)) {
206 __raw_writel(0x77665500, restart_reason);
207 } else if (!strncmp(cmd, "recovery", 8)) {
208 __raw_writel(0x77665502, restart_reason);
209 } else if (!strncmp(cmd, "oem-", 4)) {
210 unsigned long code;
211 code = simple_strtoul(cmd + 4, NULL, 16) & 0xff;
212 __raw_writel(0x6f656d00 | code, restart_reason);
213 } else {
214 __raw_writel(0x77665501, restart_reason);
215 }
216 }
217
Rohit Vaswanif688fa62011-10-13 18:13:10 -0700218 __raw_writel(0, msm_tmr0_base + WDT0_EN);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700219 if (!(machine_is_msm8x60_fusion() || machine_is_msm8x60_fusn_ffa())) {
220 mb();
221 __raw_writel(0, PSHOLD_CTL_SU); /* Actually reset the chip */
222 mdelay(5000);
223 pr_notice("PS_HOLD didn't work, falling back to watchdog\n");
224 }
225
Rohit Vaswanif688fa62011-10-13 18:13:10 -0700226 __raw_writel(1, msm_tmr0_base + WDT0_RST);
227 __raw_writel(5*0x31F3, msm_tmr0_base + WDT0_BARK_TIME);
228 __raw_writel(0x31F3, msm_tmr0_base + WDT0_BITE_TIME);
229 __raw_writel(1, msm_tmr0_base + WDT0_EN);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700230
231 mdelay(10000);
232 printk(KERN_ERR "Restarting has failed\n");
233}
234
Jeff Ohlsteina238a9852012-05-14 15:45:54 -0700235static int __init msm_pmic_restart_init(void)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700236{
Abhijeet Dharmapurikar6d565fd2011-09-15 18:49:56 -0700237 int rc;
238
Abhijeet Dharmapurikar6d565fd2011-09-15 18:49:56 -0700239 if (pmic_reset_irq != 0) {
240 rc = request_any_context_irq(pmic_reset_irq,
241 resout_irq_handler, IRQF_TRIGGER_HIGH,
242 "restart_from_pmic", NULL);
243 if (rc < 0)
244 pr_err("pmic restart irq fail rc = %d\n", rc);
245 } else {
246 pr_warn("no pmic restart interrupt specified\n");
247 }
248
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700249 return 0;
250}
251
Jeff Ohlsteina238a9852012-05-14 15:45:54 -0700252late_initcall(msm_pmic_restart_init);
253
254static int __init msm_restart_init(void)
255{
256#ifdef CONFIG_MSM_DLOAD_MODE
257 atomic_notifier_chain_register(&panic_notifier_list, &panic_blk);
258 dload_mode_addr = MSM_IMEM_BASE + DLOAD_MODE_ADDR;
259 set_dload_mode(download_mode);
260#endif
261 msm_tmr0_base = msm_timer_get_timer0_base();
262 restart_reason = MSM_IMEM_BASE + RESTART_REASON_ADDR;
263 pm_power_off = msm_power_off;
264
265 return 0;
266}
267early_initcall(msm_restart_init);