From 6c3bc79a39ea9370ca7750a22705f93ef2f43e9d Mon Sep 17 00:00:00 2001 From: Stepan Moskovchenko Date: Thu, 22 Jan 2015 14:08:08 -0800 Subject: [PATCH] power: reset: msm: Deassert PS_HOLD via SCM if possible Support a new SCM call which disables the PMIC arbiter and deasserts PS_HOLD in a single operation. Fall back on the legacy behavior of disabling the PMIC abriter and directly deasserting PS_HOLD if this call is not available. This is necessary comply with atomicity requirements of the secure environment. Change-Id: Ia3b13d469932edf94d3249fa3e7234a1c12eee1b Signed-off-by: Stepan Moskovchenko --- drivers/power/reset/msm-poweroff.c | 36 ++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/drivers/power/reset/msm-poweroff.c b/drivers/power/reset/msm-poweroff.c index c1e80f813e69..1a4cc70ab5d3 100644 --- a/drivers/power/reset/msm-poweroff.c +++ b/drivers/power/reset/msm-poweroff.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2015, The Linux Foundation. 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 @@ -38,6 +38,7 @@ #define EMERGENCY_DLOAD_MAGIC3 0x77777777 #define SCM_IO_DISABLE_PMIC_ARBITER 1 +#define SCM_IO_DEASSERT_PS_HOLD 2 #define SCM_WDOG_DEBUG_BOOT_PART 0x9 #define SCM_DLOAD_MODE 0X10 #define SCM_EDLOAD_MODE 0X01 @@ -47,6 +48,7 @@ static int restart_mode; void *restart_reason; static bool scm_pmic_arbiter_disable_supported; +static bool scm_deassert_ps_hold_supported; /* Download mode master kill-switch */ static void __iomem *msm_ps_hold; @@ -255,6 +257,30 @@ static void msm_restart_prepare(const char *cmd) } +/* + * Deassert PS_HOLD to signal the PMIC that we are ready to power down or reset. + * Do this by calling into the secure environment, if available, or by directly + * writing to a hardware register. + * + * This function should never return. + */ +static void deassert_ps_hold(void) +{ + struct scm_desc desc = { + .args[0] = 0, + .arginfo = SCM_ARGS(1), + }; + + if (scm_deassert_ps_hold_supported) { + /* This call will be available on ARMv8 only */ + scm_call2_atomic(SCM_SIP_FNID(SCM_SVC_PWR, + SCM_IO_DEASSERT_PS_HOLD), &desc); + } + + /* Fall-through to the direct write in case the scm_call "returns" */ + __raw_writel(0, msm_ps_hold); +} + static void do_msm_restart(enum reboot_mode reboot_mode, const char *cmd) { int ret; @@ -289,7 +315,7 @@ static void do_msm_restart(enum reboot_mode reboot_mode, const char *cmd) pr_err("Failed to disable secure wdog debug: %d\n", ret); halt_spmi_pmic_arbiter(); - __raw_writel(0, msm_ps_hold); + deassert_ps_hold(); mdelay(10000); } @@ -319,8 +345,7 @@ static void do_msm_poweroff(void) pr_err("Failed to disable wdog debug: %d\n", ret); halt_spmi_pmic_arbiter(); - /* MSM initiated power off, lower ps_hold */ - __raw_writel(0, msm_ps_hold); + deassert_ps_hold(); mdelay(10000); pr_err("Powering off has failed\n"); @@ -383,6 +408,9 @@ static int msm_restart_probe(struct platform_device *pdev) if (scm_is_call_available(SCM_SVC_PWR, SCM_IO_DISABLE_PMIC_ARBITER) > 0) scm_pmic_arbiter_disable_supported = true; + if (scm_is_call_available(SCM_SVC_PWR, SCM_IO_DEASSERT_PS_HOLD) > 0) + scm_deassert_ps_hold_supported = true; + return 0; err_restart_reason: