#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/bug.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/watchdog.h> /* for WDIOF_CARDRESET */
#include <mach/regs-clock.h>
#include <mach/pmu.h>
{ EXYNOS5_CMU_SYSCLK_MAU_SYS_PWR_REG, { 0x1, 0x1, 0x0} },
{ EXYNOS5_CMU_SYSCLK_GPS_SYS_PWR_REG, { 0x1, 0x0, 0x0} },
{ EXYNOS5_CMU_RESET_GSCL_SYS_PWR_REG, { 0x1, 0x0, 0x0} },
- { EXYNOS5_CMU_RESET_ISP_SYS_PWR_REG, { 0x1, 0x0, 0x0} },
+ /* CMU_RESET_ISP_SYS_PWR_REG handled in exynos5250_disable_isp() */
{ EXYNOS5_CMU_RESET_MFC_SYS_PWR_REG, { 0x1, 0x0, 0x0} },
{ EXYNOS5_CMU_RESET_G3D_SYS_PWR_REG, { 0x1, 0x0, 0x0} },
{ EXYNOS5_CMU_RESET_DISP1_SYS_PWR_REG, { 0x1, 0x0, 0x0} },
void __iomem *exynos5_list_diable_wfi_wfe[] = {
EXYNOS5_ARM_CORE1_OPTION,
EXYNOS5_FSYS_ARM_OPTION,
- EXYNOS5_ISP_ARM_OPTION,
};
+
+/*
+ * RST_STAT bits:
+ * power-on boot will set bit 16
+ * Watchdog reset will set bit 20
+ * "warm" reboot will set bit 29
+ */
+#define EXYNOS4_RST_STAT (S3C_ADDR(0x10020000) + 0x0404)
+#define EXYNOS5_RST_STAT (S3C_ADDR(0x02180000) + 0x0404)
+#define EXYNOS_WDTRESET (1 << 20)
+
+static void exynos5_power_off(void)
+{
+ unsigned int tmp;
+
+ pr_info("Power down.\n");
+ tmp = __raw_readl(EXYNOS5_PS_HOLD_CONTROL);
+ tmp &= ~(1 << 8);
+ __raw_writel(tmp, EXYNOS5_PS_HOLD_CONTROL);
+
+ /* Wait a little so we don't give a false warning below */
+ mdelay(100);
+
+ pr_err("Power down failed, please power off system manually.\n");
+ while (1)
+ ;
+}
+
+static void exynos5_debug_enable_uart_wakeup(void)
+{
+#ifdef CONFIG_SAMSUNG_PM_DEBUG
+ unsigned int tmp;
+
+ /* Enable UART automatic wakeup for resume console output */
+ tmp = __raw_readl(S5P_PAD_RET_UART_OPTION);
+ tmp |= EXYNOS5_PAD_RET_UART_AUTOMATIC_WAKEUP;
+ __raw_writel(tmp, S5P_PAD_RET_UART_OPTION);
+#endif
+}
+
static void exynos5_init_pmu(void)
{
unsigned int i;
EXYNOS5_OPTION_USE_STANDBYWFI);
__raw_writel(tmp, exynos5_list_diable_wfi_wfe[i]);
}
+
+ exynos5_debug_enable_uart_wakeup();
}
void exynos4_sys_powerdown_conf(enum sys_powerdown mode)
exynos4_pmu_config[i].reg);
}
+#define ISP_DISABLE_TRIES 10
+
+/*
+ * Disable the image signal processor.
+ *
+ * We currently have no code in the kernel to manage the state of the ISP.
+ *
+ * The ISP's power sequencing code needs to be run in a very specific order
+ * and shouldn't necessarily be intertwined with the power on/power off code
+ * of the main CPU core. Until there is kernel code to manage the ISP, we'll
+ * just hardcode powering off the ISP here.
+ */
+static void exynos5250_disable_isp(void)
+{
+ int i;
+
+ /* Make sure ISP ARM is disabled; don't use WFI or WFE */
+ __raw_writel(0, EXYNOS5_ISP_ARM_OPTION);
+
+ /* Put the ISP ARM in reset */
+ __raw_writel(0x0, EXYNOS5_ISP_ARM_CONFIGURATION);
+ for (i = 0; i < ISP_DISABLE_TRIES; i++) {
+ if (!(__raw_readl(EXYNOS5_ISP_ARM_STATUS) & 0x1))
+ break;
+ usleep_range(80, 100);
+ }
+ WARN_ON(i == ISP_DISABLE_TRIES);
+
+ /* Reset the ISP CMU block in power-off/low power state */
+ __raw_writel(0x0, EXYNOS5_CMU_RESET_ISP_SYS_PWR_REG);
+
+ /* Turn off power to the ISP in normal mode */
+ __raw_writel(0x0, EXYNOS5_ISP_CONFIGURATION);
+ for (i = 0; i < ISP_DISABLE_TRIES; i++) {
+ if (!(__raw_readl(EXYNOS5_ISP_STATUS) & 0x7))
+ break;
+ usleep_range(80, 100);
+ }
+ WARN_ON(i == ISP_DISABLE_TRIES);
+}
+
+
+/*
+ * exynos_get_bootstatus() supports generic WDIOC_GETBOOTSTATUS ioctl.
+ * See Documentation/watchdog/watchdog-api.txt for user API.
+ * See usage by drivers/watchdog/s3c2410_wdt.c
+ *
+ * Other subsystems might need to set bits too.
+ * (e.g. WDIOF_OVERHEAT or WDIOF_FANFAULT).
+ */
+unsigned int exynos_get_bootstatus(void)
+{
+ unsigned int rst_stat;
+
+ if (soc_is_exynos5250())
+ rst_stat = readl(EXYNOS5_RST_STAT);
+ else if (soc_is_exynos4210() || soc_is_exynos4212())
+ rst_stat = readl(EXYNOS4_RST_STAT);
+ else
+ return 0;
+
+ return (rst_stat & EXYNOS_WDTRESET) ? WDIOF_CARDRESET : 0;
+}
+
static int __init exynos4_pmu_init(void)
{
+ unsigned int bootstatus;
+
exynos4_pmu_config = exynos4210_pmu_config;
if (soc_is_exynos4210()) {
exynos4_pmu_config = exynos4212_pmu_config;
pr_info("EXYNOS4212 PMU Initialize\n");
} else if (soc_is_exynos5250()) {
+ exynos5250_disable_isp();
+
exynos4_pmu_config = exynos5250_pmu_config;
+ pm_power_off = exynos5_power_off;
pr_info("EXYNOS5250 PMU Initialize\n");
} else {
pr_info("EXYNOS4: PMU not supported\n");
}
+ bootstatus = exynos_get_bootstatus();
+ if (bootstatus & WDIOF_CARDRESET)
+ pr_warning("EXYNOS Watchdog timed out - caused last reboot!");
+
return 0;
}
arch_initcall(exynos4_pmu_init);