From mboxrd@z Thu Jan 1 00:00:00 1970 From: Daniel Lezcano Subject: Re: [PATCH v2 07/10] qcom: msm-pm: Add cpu low power mode functions Date: Wed, 13 Aug 2014 13:18:01 +0200 Message-ID: <53EB4969.3030204@linaro.org> References: <1407872640-6732-1-git-send-email-lina.iyer@linaro.org> <1407872640-6732-8-git-send-email-lina.iyer@linaro.org> Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: QUOTED-PRINTABLE Return-path: Received: from mail-wi0-f180.google.com ([209.85.212.180]:38461 "EHLO mail-wi0-f180.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1750751AbaHMLSE (ORCPT ); Wed, 13 Aug 2014 07:18:04 -0400 Received: by mail-wi0-f180.google.com with SMTP id n3so615368wiv.13 for ; Wed, 13 Aug 2014 04:18:03 -0700 (PDT) In-Reply-To: <1407872640-6732-8-git-send-email-lina.iyer@linaro.org> Sender: linux-arm-msm-owner@vger.kernel.org List-Id: linux-arm-msm@vger.kernel.org To: Lina Iyer , khilman@linaro.org, amit.kucheria@linaro.org, sboyd@codeaurora.org, davidb@codeaurora.org, galak@codeaurora.org, linux-arm-msm@vger.kernel.org Cc: msivasub@codeaurora.org, Venkat Devarasetty , Nicolas Pitre On 08/12/2014 09:43 PM, Lina Iyer wrote: > Add interface layer to abstract and handle hardware specific > functionality for executing various cpu low power modes in QCOM > chipsets. > > Signed-off-by: Venkat Devarasetty > Signed-off-by: Mahesh Sivasubramanian > Signed-off-by: Lina Iyer > --- > drivers/soc/qcom/Makefile | 2 +- > drivers/soc/qcom/msm-pm.c | 219 +++++++++++++++++++++++++++++++++++= +++++++++++ > include/soc/qcom/pm.h | 39 +++++++++ > 3 files changed, 259 insertions(+), 1 deletion(-) > create mode 100644 drivers/soc/qcom/msm-pm.c > create mode 100644 include/soc/qcom/pm.h > > diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile > index d7ae93b..7925f83 100644 > --- a/drivers/soc/qcom/Makefile > +++ b/drivers/soc/qcom/Makefile > @@ -1,5 +1,5 @@ > obj-$(CONFIG_QCOM_GSBI) +=3D qcom_gsbi.o > -obj-$(CONFIG_QCOM_PM) +=3D spm-devices.o spm.o > +obj-$(CONFIG_QCOM_PM) +=3D spm-devices.o spm.o msm-pm.o > > CFLAGS_scm.o :=3D$(call as-instr,.arch_extension sec,-DREQUIRES_SEC= =3D1) > obj-$(CONFIG_QCOM_SCM) +=3D scm.o scm-boot.o > diff --git a/drivers/soc/qcom/msm-pm.c b/drivers/soc/qcom/msm-pm.c > new file mode 100644 > index 0000000..f2f15b8 > --- /dev/null > +++ b/drivers/soc/qcom/msm-pm.c > @@ -0,0 +1,219 @@ > +/* Copyright (c) 2010-2014, The Linux Foundation. All rights reserve= d. > + * > + * This program is free software; you can redistribute it and/or mod= ify > + * it under the terms of the GNU General Public License version 2 an= d > + * 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 > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include > +#include > +#include > +#include > + > +#include > +#include > +#include > +#include > + > +#define SCM_CMD_TERMINATE_PC (0x2) > +#define SCM_CMD_CORE_HOTPLUGGED (0x10) > +#define SCM_FLUSH_FLAG_MASK (0x3) > + > +static bool msm_pm_is_L1_writeback(void) > +{ > + u32 cache_id =3D 0; > + > +#if defined(CONFIG_CPU_V7) > + u32 sel =3D 0; > + > + asm volatile ("mcr p15, 2, %[ccselr], c0, c0, 0\n\t" > + "isb\n\t" > + "mrc p15, 1, %[ccsidr], c0, c0, 0\n\t" > + :[ccsidr]"=3Dr" (cache_id) > + :[ccselr]"r" (sel) > + ); > + return cache_id & BIT(30); > +#elif defined(CONFIG_ARM64) > + u32 sel =3D 0; > + asm volatile("msr csselr_el1, %[ccselr]\n\t" > + "isb\n\t" > + "mrs %[ccsidr],ccsidr_el1\n\t" > + :[ccsidr]"=3Dr" (cache_id) > + :[ccselr]"r" (sel) > + ); > + return cache_id & BIT(30); > +#else > +#error No valid CPU arch selected > +#endif > +} > + > +static inline void msm_arch_idle(void) > +{ > + /* Flush and clock-gate */ > + mb(); Why is needed this memory barrier ? > + wfi(); > +} > + > +static bool msm_pm_swfi(bool from_idle) > +{ > + msm_arch_idle(); > + return true; > +} > + > +static bool msm_pm_retention(bool from_idle) > +{ > + int ret =3D 0; > + > + ret =3D msm_spm_set_low_power_mode(MSM_SPM_MODE_RETENTION, false); > + WARN_ON(ret); > + > + msm_arch_idle(); > + > + ret =3D msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING, false= ); > + WARN_ON(ret); Why do you need to set the clock gating mode each time you exit the=20 retention mode ? > + return true; > +} > + > +static int msm_pm_collapse(unsigned long from_idle) > +{ > + enum msm_pm_l2_scm_flag flag =3D MSM_SCM_L2_ON; > + > + /** > + * Single core processors need to have L2 > + * flushed when powering down the core. > + * Notify SCM to flush secure L2 lines. > + */ > + if (num_possible_cpus() =3D=3D 1) > + flag =3D MSM_SCM_L2_OFF; I am wondering if this shouldn't be handle by a mcpm driver. Cc nico. > + if (flag =3D=3D MSM_SCM_L2_OFF) > + flush_cache_all(); > + else if (msm_pm_is_L1_writeback()) > + flush_cache_louis(); > + > + flag &=3D SCM_FLUSH_FLAG_MASK; > + if (!from_idle) > + flag |=3D SCM_CMD_CORE_HOTPLUGGED; > + > + scm_call_atomic1(SCM_SVC_BOOT, SCM_CMD_TERMINATE_PC, flag); > + > + return 0; > +} > + > +static void set_up_boot_address(void *entry, int cpu) > +{ > + static int flags[NR_CPUS] =3D { > + SCM_FLAG_WARMBOOT_CPU0, > + SCM_FLAG_WARMBOOT_CPU1, > + SCM_FLAG_WARMBOOT_CPU2, > + SCM_FLAG_WARMBOOT_CPU3, > + }; > + static DEFINE_PER_CPU(void *, last_known_entry); > + > + if (entry =3D=3D per_cpu(last_known_entry, cpu)) > + return; > + > + per_cpu(last_known_entry, cpu) =3D entry; > + scm_set_boot_addr(virt_to_phys(entry), flags[cpu]); > +} > + > +static bool __ref msm_pm_spm_power_collapse(unsigned int cpu, bool f= rom_idle) > +{ > + void *entry; > + bool collapsed =3D 0; > + int ret; > + bool save_cpu_regs =3D (cpu_online(cpu) || from_idle); > + > + if (from_idle) > + cpu_pm_enter(); > + > + ret =3D msm_spm_set_low_power_mode(MSM_SPM_MODE_POWER_COLLAPSE, fal= se); > + WARN_ON(ret); > + > + entry =3D save_cpu_regs ? cpu_resume : secondary_startup; > + set_up_boot_address(entry, cpu); > + > +#ifdef CONFIG_CPU_V7 > + collapsed =3D !cpu_suspend(from_idle, msm_pm_collapse); > +#else > + collapsed =3D !cpu_suspend(0); > +#endif > + > + if (collapsed) > + local_fiq_enable(); > + > + if (from_idle) > + cpu_pm_exit(); > + > + ret =3D msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING, false= ); > + WARN_ON(ret); > + > + return collapsed; > +} > + > +static bool msm_pm_power_collapse_standalone(bool from_idle) > +{ > + unsigned int cpu =3D smp_processor_id(); > + bool collapsed; > + > + collapsed =3D msm_pm_spm_power_collapse(cpu, from_idle); > + > + return collapsed; > +} > + > +static bool msm_pm_power_collapse(bool from_idle) > +{ > + unsigned int cpu =3D smp_processor_id(); > + bool collapsed; > + > + collapsed =3D msm_pm_spm_power_collapse(cpu, from_idle); > + > + return collapsed; > +} > + > +static bool (*execute[MSM_PM_SLEEP_MODE_NR])(bool idle) =3D { > + [MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT] =3D msm_pm_swfi, > + [MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE] =3D > + msm_pm_power_collapse_standalone, > + [MSM_PM_SLEEP_MODE_RETENTION] =3D msm_pm_retention, > + [MSM_PM_SLEEP_MODE_POWER_COLLAPSE] =3D msm_pm_power_collapse, > +}; > + > +/** > + * msm_cpu_pm_enter_sleep(): Enter a low power mode on current cpu > + * > + * @mode - sleep mode to enter > + * @from_idle - bool to indicate that the mode is exercised during i= dle/suspend > + * > + * The code should be with interrupts disabled and on the core on wh= ich the > + * low power is to be executed. > + * > + */ > +bool msm_cpu_pm_enter_sleep(enum msm_pm_sleep_mode mode, bool from_i= dle) > +{ > + bool exit_stat =3D false; > + > + if (execute[mode]) > + exit_stat =3D execute[mode](from_idle); > + > + local_irq_enable(); > + return exit_stat; > +} > +EXPORT_SYMBOL(msm_cpu_pm_enter_sleep); > diff --git a/include/soc/qcom/pm.h b/include/soc/qcom/pm.h > new file mode 100644 > index 0000000..01872ad > --- /dev/null > +++ b/include/soc/qcom/pm.h > @@ -0,0 +1,39 @@ > +/* > + * Copyright (c) 2009-2014, The Linux Foundation. All rights reserve= d. > + * > + * This software is licensed under the terms of the GNU General Publ= ic > + * License version 2, as published by the Free Software Foundation, = and > + * may be copied, distributed, and modified under those terms. > + * > + * 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. > + * > + */ > + > +#ifndef __QCOM_PM_H > +#define __QCOM_PM_H > + > +enum msm_pm_sleep_mode { > + MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT, > + MSM_PM_SLEEP_MODE_RETENTION, > + MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE, > + MSM_PM_SLEEP_MODE_POWER_COLLAPSE, > + MSM_PM_SLEEP_MODE_NR, > +}; > + > +enum msm_pm_l2_scm_flag { > + MSM_SCM_L2_ON =3D 0, > + MSM_SCM_L2_OFF =3D 1, > +}; > + > +#ifdef CONFIG_QCOM_PM > +bool msm_cpu_pm_enter_sleep(enum msm_pm_sleep_mode mode, bool from_i= dle); > +#else > +static inline bool msm_cpu_pm_enter_sleep(enum msm_pm_sleep_mode mod= e, > + bool from_idle) > +{ return true; } > +#endif > + > +#endif /* __QCOM_PM_H */ > --=20 Linaro.org =E2=94=82 Open source software fo= r ARM SoCs =46ollow Linaro: Facebook | Twitter | Blog