blob: 0de65a5aa9cd561c7cd192aaeb14e40b3f6d74c1 [file] [log] [blame]
/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/reboot.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/mfd/pmic8058.h>
#include <linux/mfd/pmic8901.h>
#include <linux/mfd/pm8xxx/misc.h>
#include <asm/mach-types.h>
#include <mach/msm_iomap.h>
#include <mach/restart.h>
#include <mach/scm-io.h>
#include <mach/socinfo.h>
#define TCSR_WDT_CFG 0x30
#define WDT0_RST (MSM_TMR0_BASE + 0x38)
#define WDT0_EN (MSM_TMR0_BASE + 0x40)
#define WDT0_BARK_TIME (MSM_TMR0_BASE + 0x4C)
#define WDT0_BITE_TIME (MSM_TMR0_BASE + 0x5C)
#define PSHOLD_CTL_SU (MSM_TLMM_BASE + 0x820)
#define RESTART_REASON_ADDR 0x65C
#define DLOAD_MODE_ADDR 0x0
static int restart_mode;
void *restart_reason;
#ifdef CONFIG_MSM_DLOAD_MODE
static int in_panic;
static void *dload_mode_addr;
/* Download mode master kill-switch */
static int dload_set(const char *val, struct kernel_param *kp);
static int download_mode = 1;
module_param_call(download_mode, dload_set, param_get_int,
&download_mode, 0644);
static int panic_prep_restart(struct notifier_block *this,
unsigned long event, void *ptr)
{
in_panic = 1;
return NOTIFY_DONE;
}
static struct notifier_block panic_blk = {
.notifier_call = panic_prep_restart,
};
static void set_dload_mode(int on)
{
if (dload_mode_addr) {
__raw_writel(on ? 0xE47B337D : 0, dload_mode_addr);
__raw_writel(on ? 0xCE14091A : 0,
dload_mode_addr + sizeof(unsigned int));
mb();
}
}
static int dload_set(const char *val, struct kernel_param *kp)
{
int ret;
int old_val = download_mode;
ret = param_set_int(val, kp);
if (ret)
return ret;
/* If download_mode is not zero or one, ignore. */
if (download_mode >> 1) {
download_mode = old_val;
return -EINVAL;
}
set_dload_mode(download_mode);
return 0;
}
#else
#define set_dload_mode(x) do {} while (0)
#endif
void msm_set_restart_mode(int mode)
{
restart_mode = mode;
}
EXPORT_SYMBOL(msm_set_restart_mode);
static void msm_power_off(void)
{
printk(KERN_NOTICE "Powering off the SoC\n");
#ifdef CONFIG_MSM_DLOAD_MODE
set_dload_mode(0);
#endif
if (cpu_is_msm8x60()) {
pm8058_reset_pwr_off(0);
pm8901_reset_pwr_off(0);
}
pm8xxx_reset_pwr_off(0);
__raw_writel(0, PSHOLD_CTL_SU);
mdelay(10000);
printk(KERN_ERR "Powering off has failed\n");
return;
}
void arch_reset(char mode, const char *cmd)
{
#ifdef CONFIG_MSM_DLOAD_MODE
/* This looks like a normal reboot at this point. */
set_dload_mode(0);
/* Write download mode flags if we're panic'ing */
set_dload_mode(in_panic);
/* Write download mode flags if restart_mode says so */
if (restart_mode == RESTART_DLOAD)
set_dload_mode(1);
/* Kill download mode if master-kill switch is set */
if (!download_mode)
set_dload_mode(0);
#endif
printk(KERN_NOTICE "Going down for restart now\n");
if (cpu_is_msm8x60())
pm8058_reset_pwr_off(1);
pm8xxx_reset_pwr_off(1);
if (cmd != NULL) {
if (!strncmp(cmd, "bootloader", 10)) {
__raw_writel(0x77665500, restart_reason);
} else if (!strncmp(cmd, "recovery", 8)) {
__raw_writel(0x77665502, restart_reason);
} else if (!strncmp(cmd, "oem-", 4)) {
unsigned long code;
code = simple_strtoul(cmd + 4, NULL, 16) & 0xff;
__raw_writel(0x6f656d00 | code, restart_reason);
} else {
__raw_writel(0x77665501, restart_reason);
}
}
__raw_writel(0, WDT0_EN);
if (!(machine_is_msm8x60_fusion() || machine_is_msm8x60_fusn_ffa())) {
mb();
__raw_writel(0, PSHOLD_CTL_SU); /* Actually reset the chip */
mdelay(5000);
pr_notice("PS_HOLD didn't work, falling back to watchdog\n");
}
__raw_writel(1, WDT0_RST);
__raw_writel(5*0x31F3, WDT0_BARK_TIME);
__raw_writel(0x31F3, WDT0_BITE_TIME);
__raw_writel(1, WDT0_EN);
secure_writel(3, MSM_TCSR_BASE + TCSR_WDT_CFG);
mdelay(10000);
printk(KERN_ERR "Restarting has failed\n");
}
static int __init msm_restart_init(void)
{
#ifdef CONFIG_MSM_DLOAD_MODE
atomic_notifier_chain_register(&panic_notifier_list, &panic_blk);
dload_mode_addr = MSM_IMEM_BASE + DLOAD_MODE_ADDR;
/* Reset detection is switched on below.*/
set_dload_mode(1);
#endif
restart_reason = MSM_IMEM_BASE + RESTART_REASON_ADDR;
pm_power_off = msm_power_off;
return 0;
}
late_initcall(msm_restart_init);