blob: 7ddcde74e7cfa8d899bf6f69cd331c98690b36a9 [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
2 *
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"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070036
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070037#define WDT0_RST (MSM_TMR0_BASE + 0x38)
38#define WDT0_EN (MSM_TMR0_BASE + 0x40)
39#define WDT0_BARK_TIME (MSM_TMR0_BASE + 0x4C)
40#define WDT0_BITE_TIME (MSM_TMR0_BASE + 0x5C)
41
42#define PSHOLD_CTL_SU (MSM_TLMM_BASE + 0x820)
43
44#define RESTART_REASON_ADDR 0x65C
45#define DLOAD_MODE_ADDR 0x0
46
Abhijeet Dharmapurikar9259fef2011-09-24 19:07:48 -070047#define SCM_IO_DISABLE_PMIC_ARBITER 1
48
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070049static int restart_mode;
50void *restart_reason;
51
Abhijeet Dharmapurikar6d565fd2011-09-15 18:49:56 -070052int pmic_reset_irq;
53
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070054#ifdef CONFIG_MSM_DLOAD_MODE
55static int in_panic;
56static void *dload_mode_addr;
57
58/* Download mode master kill-switch */
59static int dload_set(const char *val, struct kernel_param *kp);
60static int download_mode = 1;
61module_param_call(download_mode, dload_set, param_get_int,
62 &download_mode, 0644);
63
64static int panic_prep_restart(struct notifier_block *this,
65 unsigned long event, void *ptr)
66{
67 in_panic = 1;
68 return NOTIFY_DONE;
69}
70
71static struct notifier_block panic_blk = {
72 .notifier_call = panic_prep_restart,
73};
74
75static void set_dload_mode(int on)
76{
77 if (dload_mode_addr) {
78 __raw_writel(on ? 0xE47B337D : 0, dload_mode_addr);
79 __raw_writel(on ? 0xCE14091A : 0,
80 dload_mode_addr + sizeof(unsigned int));
81 mb();
82 }
83}
84
85static int dload_set(const char *val, struct kernel_param *kp)
86{
87 int ret;
88 int old_val = download_mode;
89
90 ret = param_set_int(val, kp);
91
92 if (ret)
93 return ret;
94
95 /* If download_mode is not zero or one, ignore. */
96 if (download_mode >> 1) {
97 download_mode = old_val;
98 return -EINVAL;
99 }
100
101 set_dload_mode(download_mode);
102
103 return 0;
104}
105#else
106#define set_dload_mode(x) do {} while (0)
107#endif
108
109void msm_set_restart_mode(int mode)
110{
111 restart_mode = mode;
112}
113EXPORT_SYMBOL(msm_set_restart_mode);
114
Abhijeet Dharmapurikar6d565fd2011-09-15 18:49:56 -0700115static void __msm_power_off(int lower_pshold)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700116{
Abhijeet Dharmapurikar6d565fd2011-09-15 18:49:56 -0700117 printk(KERN_CRIT "Powering off the SoC\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700118#ifdef CONFIG_MSM_DLOAD_MODE
119 set_dload_mode(0);
120#endif
121 if (cpu_is_msm8x60()) {
122 pm8058_reset_pwr_off(0);
123 pm8901_reset_pwr_off(0);
124 }
Jeff Ohlstein28009a82011-07-25 19:21:26 -0700125 pm8xxx_reset_pwr_off(0);
Abhijeet Dharmapurikar9259fef2011-09-24 19:07:48 -0700126 if (lower_pshold) {
Abhijeet Dharmapurikar6d565fd2011-09-15 18:49:56 -0700127 __raw_writel(0, PSHOLD_CTL_SU);
Abhijeet Dharmapurikar9259fef2011-09-24 19:07:48 -0700128 mdelay(10000);
129 printk(KERN_ERR "Powering off has failed\n");
130 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700131 return;
132}
133
Abhijeet Dharmapurikar6d565fd2011-09-15 18:49:56 -0700134static void msm_power_off(void)
135{
136 /* MSM initiated power off, lower ps_hold */
137 __msm_power_off(1);
138}
139
140static void cpu_power_off(void *data)
141{
Abhijeet Dharmapurikar9259fef2011-09-24 19:07:48 -0700142 int rc;
143
Abhijeet Dharmapurikar6d565fd2011-09-15 18:49:56 -0700144 pr_err("PMIC Initiated shutdown %s cpu=%d\n", __func__,
145 smp_processor_id());
Abhijeet Dharmapurikar9259fef2011-09-24 19:07:48 -0700146 if (smp_processor_id() == 0) {
Abhijeet Dharmapurikar6d565fd2011-09-15 18:49:56 -0700147 /*
148 * PMIC initiated power off, do not lower ps_hold, pmic will
149 * shut msm down
150 */
151 __msm_power_off(0);
152
Abhijeet Dharmapurikar9259fef2011-09-24 19:07:48 -0700153 pet_watchdog();
154 pr_err("Calling scm to disable arbiter\n");
155 /* call secure manager to disable arbiter and never return */
156 rc = scm_call_atomic1(SCM_SVC_PWR,
157 SCM_IO_DISABLE_PMIC_ARBITER, 1);
158
159 pr_err("SCM returned even when asked to busy loop rc=%d\n", rc);
160 pr_err("waiting on pmic to shut msm down\n");
161 }
162
Abhijeet Dharmapurikar6d565fd2011-09-15 18:49:56 -0700163 preempt_disable();
164 while (1)
165 ;
166}
167
168static irqreturn_t resout_irq_handler(int irq, void *dev_id)
169{
170 pr_warn("%s PMIC Initiated shutdown\n", __func__);
171 oops_in_progress = 1;
172 smp_call_function_many(cpu_online_mask, cpu_power_off, NULL, 0);
173 if (smp_processor_id() == 0)
174 cpu_power_off(NULL);
175 preempt_disable();
176 while (1)
177 ;
178 return IRQ_HANDLED;
179}
180
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700181void arch_reset(char mode, const char *cmd)
182{
183
184#ifdef CONFIG_MSM_DLOAD_MODE
185
186 /* This looks like a normal reboot at this point. */
187 set_dload_mode(0);
188
189 /* Write download mode flags if we're panic'ing */
190 set_dload_mode(in_panic);
191
192 /* Write download mode flags if restart_mode says so */
193 if (restart_mode == RESTART_DLOAD)
194 set_dload_mode(1);
195
196 /* Kill download mode if master-kill switch is set */
197 if (!download_mode)
198 set_dload_mode(0);
199#endif
200
201 printk(KERN_NOTICE "Going down for restart now\n");
202
203 if (cpu_is_msm8x60())
204 pm8058_reset_pwr_off(1);
Jeff Ohlstein28009a82011-07-25 19:21:26 -0700205 pm8xxx_reset_pwr_off(1);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700206
207 if (cmd != NULL) {
208 if (!strncmp(cmd, "bootloader", 10)) {
209 __raw_writel(0x77665500, restart_reason);
210 } else if (!strncmp(cmd, "recovery", 8)) {
211 __raw_writel(0x77665502, restart_reason);
212 } else if (!strncmp(cmd, "oem-", 4)) {
213 unsigned long code;
214 code = simple_strtoul(cmd + 4, NULL, 16) & 0xff;
215 __raw_writel(0x6f656d00 | code, restart_reason);
216 } else {
217 __raw_writel(0x77665501, restart_reason);
218 }
219 }
220
221 __raw_writel(0, WDT0_EN);
222 if (!(machine_is_msm8x60_fusion() || machine_is_msm8x60_fusn_ffa())) {
223 mb();
224 __raw_writel(0, PSHOLD_CTL_SU); /* Actually reset the chip */
225 mdelay(5000);
226 pr_notice("PS_HOLD didn't work, falling back to watchdog\n");
227 }
228
229 __raw_writel(1, WDT0_RST);
230 __raw_writel(5*0x31F3, WDT0_BARK_TIME);
231 __raw_writel(0x31F3, WDT0_BITE_TIME);
Jeff Ohlstein7cab7ff2011-07-14 18:03:57 -0700232 __raw_writel(1, WDT0_EN);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700233
234 mdelay(10000);
235 printk(KERN_ERR "Restarting has failed\n");
236}
237
238static int __init msm_restart_init(void)
239{
Abhijeet Dharmapurikar6d565fd2011-09-15 18:49:56 -0700240 int rc;
241
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700242#ifdef CONFIG_MSM_DLOAD_MODE
243 atomic_notifier_chain_register(&panic_notifier_list, &panic_blk);
244 dload_mode_addr = MSM_IMEM_BASE + DLOAD_MODE_ADDR;
245
246 /* Reset detection is switched on below.*/
247 set_dload_mode(1);
248#endif
249 restart_reason = MSM_IMEM_BASE + RESTART_REASON_ADDR;
250 pm_power_off = msm_power_off;
251
Abhijeet Dharmapurikar6d565fd2011-09-15 18:49:56 -0700252 if (pmic_reset_irq != 0) {
253 rc = request_any_context_irq(pmic_reset_irq,
254 resout_irq_handler, IRQF_TRIGGER_HIGH,
255 "restart_from_pmic", NULL);
256 if (rc < 0)
257 pr_err("pmic restart irq fail rc = %d\n", rc);
258 } else {
259 pr_warn("no pmic restart interrupt specified\n");
260 }
261
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700262 return 0;
263}
264
265late_initcall(msm_restart_init);