summaryrefslogtreecommitdiff
path: root/arch/arm/mach-imx
diff options
context:
space:
mode:
authorPeng Fan <peng.fan@nxp.com>2017-11-02 09:58:50 +0800
committerJason Liu <jason.hui.liu@nxp.com>2019-02-12 10:31:23 +0800
commitca7ccb4635c65be759aa15ca7c4c56083a18171c (patch)
tree13b39a9f38cd1d032fd5583a06ba65a561d20a5a /arch/arm/mach-imx
parenta34a5a0452e56c0e2c0295135cdda69893f245b8 (diff)
MLK-16750-5: arm: imx7d: support using psci to handle power stuff
Support using PSCI to handle Power stuff on imx7d. i.MX7 LPSR mode not implemented now. Signed-off-by: Peng Fan <peng.fan@nxp.com> Reviewed-by: Anson Huang <Anson.Huang@nxp.com>
Diffstat (limited to 'arch/arm/mach-imx')
-rw-r--r--arch/arm/mach-imx/cpuidle-imx7d.c123
1 files changed, 88 insertions, 35 deletions
diff --git a/arch/arm/mach-imx/cpuidle-imx7d.c b/arch/arm/mach-imx/cpuidle-imx7d.c
index 40dcdce257ff..8e1e61c5503b 100644
--- a/arch/arm/mach-imx/cpuidle-imx7d.c
+++ b/arch/arm/mach-imx/cpuidle-imx7d.c
@@ -16,6 +16,7 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
+#include <linux/psci.h>
#include <asm/cp15.h>
#include <asm/cpuidle.h>
#include <asm/fncpy.h>
@@ -24,6 +25,8 @@
#include <asm/suspend.h>
#include <asm/tlb.h>
+#include <uapi/linux/psci.h>
+
#include "common.h"
#include "cpuidle.h"
#include "hardware.h"
@@ -77,11 +80,22 @@ struct imx7_cpuidle_pm_info {
struct imx7_pm_base gic_dist_base;
} __aligned(8);
+static atomic_t master_lpi = ATOMIC_INIT(0);
static atomic_t master_wait = ATOMIC_INIT(0);
static void (*imx7d_wfi_in_iram_fn)(void __iomem *iram_vbase);
static struct imx7_cpuidle_pm_info *cpuidle_pm_info;
+#define MX7D_POWERDWN_IDLE_PARAM \
+ ((1 << PSCI_0_2_POWER_STATE_ID_SHIFT) | \
+ (1 << PSCI_0_2_POWER_STATE_AFFL_SHIFT) | \
+ (PSCI_POWER_STATE_TYPE_POWER_DOWN << PSCI_0_2_POWER_STATE_TYPE_SHIFT))
+
+#define MX7D_STANDBY_IDLE_PARAM \
+ ((1 << PSCI_0_2_POWER_STATE_ID_SHIFT) | \
+ (1 << PSCI_0_2_POWER_STATE_AFFL_SHIFT) | \
+ (PSCI_POWER_STATE_TYPE_STANDBY << PSCI_0_2_POWER_STATE_TYPE_SHIFT))
+
/* Mapped for the kernel, unlike cpuidle_pm_info->gic_dist_base.vbase */
static void __iomem *imx7d_cpuidle_gic_base;
@@ -119,7 +133,11 @@ static void imx_pen_unlock(int cpu)
static int imx7d_idle_finish(unsigned long val)
{
- imx7d_wfi_in_iram_fn(wfi_iram_base);
+ if (psci_ops.cpu_suspend)
+ psci_ops.cpu_suspend(MX7D_POWERDWN_IDLE_PARAM, __pa(cpu_resume));
+ else
+ imx7d_wfi_in_iram_fn(wfi_iram_base);
+
return 0;
}
@@ -133,6 +151,7 @@ static bool imx7d_gic_sgis_pending(void)
readl_relaxed(sgip_base + 0xc));
}
+static DEFINE_SPINLOCK(psci_lock);
static int imx7d_enter_low_power_idle(struct cpuidle_device *dev,
struct cpuidle_driver *drv, int index)
{
@@ -149,42 +168,74 @@ static int imx7d_enter_low_power_idle(struct cpuidle_device *dev,
atomic_dec(&master_wait);
imx_gpcv2_set_lpm_mode(WAIT_CLOCKED);
} else {
- imx_pen_lock(dev->cpu);
- cpuidle_pm_info->num_online_cpus = num_online_cpus();
- ++cpuidle_pm_info->num_lpi_cpus;
- cpu_pm_enter();
- if (cpuidle_pm_info->num_lpi_cpus ==
- cpuidle_pm_info->num_online_cpus) {
- /*
- * GPC will not wake on SGIs so check for them
- * manually here. At this point we know the other cpu
- * is in wfi or waiting for the lock and can't send
- * any additional IPIs.
- */
- if (imx7d_gic_sgis_pending()) {
- index = -1;
- goto skip_lpi_flow;
+ if (psci_ops.cpu_suspend) {
+ cpu_pm_enter();
+ spin_lock(&psci_lock);
+ if (atomic_inc_return(&master_lpi) == num_online_cpus()) {
+ if (imx7d_gic_sgis_pending()) {
+ atomic_dec(&master_lpi);
+ index = -1;
+ goto psci_skip_lpi_flow;
+ }
+
+ imx_gpcv2_set_lpm_mode(WAIT_UNCLOCKED);
+ imx_gpcv2_set_cpu_power_gate_in_idle(true);
+
+ cpu_cluster_pm_enter();
}
- imx_gpcv2_set_lpm_mode(WAIT_UNCLOCKED);
- imx_gpcv2_set_cpu_power_gate_in_idle(true);
- cpu_cluster_pm_enter();
+ spin_unlock(&psci_lock);
+
+ cpu_suspend(0, imx7d_idle_finish);
+
+ spin_lock(&psci_lock);
+ if (atomic_read(&master_lpi) == num_online_cpus()) {
+ cpu_cluster_pm_exit();
+ imx_gpcv2_set_cpu_power_gate_in_idle(false);
+ imx_gpcv2_set_lpm_mode(WAIT_CLOCKED);
+ }
+
+ atomic_dec(&master_lpi);
+psci_skip_lpi_flow:
+ spin_unlock(&psci_lock);
+ cpu_pm_exit();
} else {
- imx_set_cpu_jump(dev->cpu, ca7_cpu_resume);
- }
+ imx_pen_lock(dev->cpu);
+ cpuidle_pm_info->num_online_cpus = num_online_cpus();
+ ++cpuidle_pm_info->num_lpi_cpus;
+ cpu_pm_enter();
+ if (cpuidle_pm_info->num_lpi_cpus ==
+ cpuidle_pm_info->num_online_cpus) {
+ /*
+ * GPC will not wake on SGIs so check for them
+ * manually here. At this point we know the other cpu
+ * is in wfi or waiting for the lock and can't send
+ * any additional IPIs.
+ */
+ if (imx7d_gic_sgis_pending()) {
+ index = -1;
+ goto skip_lpi_flow;
+ }
+ imx_gpcv2_set_lpm_mode(WAIT_UNCLOCKED);
+ imx_gpcv2_set_cpu_power_gate_in_idle(true);
+ cpu_cluster_pm_enter();
+ } else {
+ imx_set_cpu_jump(dev->cpu, ca7_cpu_resume);
+ }
- cpu_suspend(0, imx7d_idle_finish);
+ cpu_suspend(0, imx7d_idle_finish);
- if (cpuidle_pm_info->num_lpi_cpus ==
- cpuidle_pm_info->num_online_cpus) {
- cpu_cluster_pm_exit();
- imx_gpcv2_set_cpu_power_gate_in_idle(false);
- imx_gpcv2_set_lpm_mode(WAIT_CLOCKED);
- }
+ if (cpuidle_pm_info->num_lpi_cpus ==
+ cpuidle_pm_info->num_online_cpus) {
+ cpu_cluster_pm_exit();
+ imx_gpcv2_set_cpu_power_gate_in_idle(false);
+ imx_gpcv2_set_lpm_mode(WAIT_CLOCKED);
+ }
skip_lpi_flow:
- cpu_pm_exit();
- --cpuidle_pm_info->num_lpi_cpus;
- imx_pen_unlock(dev->cpu);
+ cpu_pm_exit();
+ --cpuidle_pm_info->num_lpi_cpus;
+ imx_pen_unlock(dev->cpu);
+ }
}
return index;
@@ -328,10 +379,12 @@ int __init imx7d_cpuidle_init(void)
imx7d_enable_rcosc();
/* code size should include cpuidle_pm_info size */
- imx7d_wfi_in_iram_fn = (void *)fncpy(wfi_iram_base +
- sizeof(*cpuidle_pm_info),
- &imx7d_low_power_idle,
- MX7_CPUIDLE_OCRAM_SIZE - sizeof(*cpuidle_pm_info));
+ if (!psci_ops.cpu_suspend) {
+ imx7d_wfi_in_iram_fn = (void *)fncpy(wfi_iram_base +
+ sizeof(*cpuidle_pm_info),
+ &imx7d_low_power_idle,
+ MX7_CPUIDLE_OCRAM_SIZE - sizeof(*cpuidle_pm_info));
+ }
return cpuidle_register(&imx7d_cpuidle_driver, NULL);
}