cpuidle: lpm-levels: Add system_pm ops for system level LPMs

Add system_pm ops that are used by LPM driver to configure sleep
and wake votes as well as next wake up time. LPM driver uses them
during system level LPMs.

Change-Id: I72b89aeb3408b89a73d1f0c821649f4f998ab85c
Signed-off-by: Raju P.L.S.S.S.N <rplsssn@codeaurora.org>
This commit is contained in:
Raju P.L.S.S.S.N
2017-12-07 00:09:01 +05:30
committed by Lina Iyer
parent 687d4ad9f6
commit 8b6b8fd39a
5 changed files with 110 additions and 108 deletions

View File

@@ -39,8 +39,8 @@
#include <linux/sched/stat.h>
#include <soc/qcom/pm.h>
#include <soc/qcom/event_timer.h>
#include <soc/qcom/lpm_levels.h>
#include <soc/qcom/lpm-stats.h>
#include <soc/qcom/system_pm.h>
#include <soc/qcom/minidump.h>
#include <asm/arch_timer.h>
#include <asm/suspend.h>
@@ -79,6 +79,9 @@ struct lpm_debug {
uint32_t arg4;
};
static struct system_pm_ops *sys_pm_ops;
struct lpm_cluster *lpm_root_node;
#define MAXSAMPLES 5
@@ -123,11 +126,6 @@ static void cluster_prepare(struct lpm_cluster *cluster,
const struct cpumask *cpu, int child_idx, bool from_idle,
int64_t time);
static int msm_pm_sleep_time_override;
module_param_named(sleep_time_override,
msm_pm_sleep_time_override, int, 0664);
static uint64_t suspend_wake_time;
static bool print_parsed_dt;
module_param_named(print_parsed_dt, print_parsed_dt, bool, 0664);
@@ -145,20 +143,15 @@ s32 msm_cpuidle_get_deep_idle_latency(void)
}
EXPORT_SYMBOL(msm_cpuidle_get_deep_idle_latency);
void lpm_suspend_wake_time(uint64_t wakeup_time)
uint32_t register_system_pm_ops(struct system_pm_ops *pm_ops)
{
if (wakeup_time <= 0) {
suspend_wake_time = msm_pm_sleep_time_override;
return;
}
if (sys_pm_ops)
return -EUSERS;
if (msm_pm_sleep_time_override &&
(msm_pm_sleep_time_override < wakeup_time))
suspend_wake_time = msm_pm_sleep_time_override;
else
suspend_wake_time = wakeup_time;
sys_pm_ops = pm_ops;
return 0;
}
EXPORT_SYMBOL(lpm_suspend_wake_time);
static uint32_t least_cluster_latency(struct lpm_cluster *cluster,
struct latency_level *lat_level)
@@ -703,32 +696,16 @@ done_select:
return best_level;
}
static uint64_t get_cluster_sleep_time(struct lpm_cluster *cluster,
struct cpumask *mask, bool from_idle, uint32_t *pred_time)
static unsigned int get_next_online_cpu(bool from_idle)
{
int cpu;
int next_cpu = raw_smp_processor_id();
unsigned int cpu;
ktime_t next_event;
struct cpumask online_cpus_in_cluster;
struct lpm_history *history;
int64_t prediction = LONG_MAX;
unsigned int next_cpu = raw_smp_processor_id();
if (!from_idle)
return next_cpu;
next_event = KTIME_MAX;
if (!suspend_wake_time)
suspend_wake_time = msm_pm_sleep_time_override;
if (!from_idle) {
if (mask)
cpumask_copy(mask, cpumask_of(raw_smp_processor_id()));
if (!suspend_wake_time)
return ~0ULL;
else
return USEC_PER_SEC * suspend_wake_time;
}
cpumask_and(&online_cpus_in_cluster,
&cluster->num_children_in_sync, cpu_online_mask);
for_each_cpu(cpu, &online_cpus_in_cluster) {
for_each_online_cpu(cpu) {
ktime_t *next_event_c;
next_event_c = get_next_event_cpu(cpu);
@@ -736,6 +713,32 @@ static uint64_t get_cluster_sleep_time(struct lpm_cluster *cluster,
next_event = *next_event_c;
next_cpu = cpu;
}
}
return next_cpu;
}
static uint64_t get_cluster_sleep_time(struct lpm_cluster *cluster,
bool from_idle, uint32_t *pred_time)
{
int cpu;
ktime_t next_event;
struct cpumask online_cpus_in_cluster;
struct lpm_history *history;
int64_t prediction = LONG_MAX;
if (!from_idle)
return ~0ULL;
next_event = KTIME_MAX;
cpumask_and(&online_cpus_in_cluster,
&cluster->num_children_in_sync, cpu_online_mask);
for_each_cpu(cpu, &online_cpus_in_cluster) {
ktime_t *next_event_c;
next_event_c = get_next_event_cpu(cpu);
if (*next_event_c < next_event)
next_event = *next_event_c;
if (from_idle && lpm_prediction) {
history = &per_cpu(hist, cpu);
@@ -744,9 +747,6 @@ static uint64_t get_cluster_sleep_time(struct lpm_cluster *cluster,
}
}
if (mask)
cpumask_copy(mask, cpumask_of(next_cpu));
if (from_idle && lpm_prediction) {
if (prediction > ktime_to_us(ktime_get()))
*pred_time = prediction - ktime_to_us(ktime_get());
@@ -929,7 +929,7 @@ static int cluster_select(struct lpm_cluster *cluster, bool from_idle,
if (!cluster)
return -EINVAL;
sleep_us = (uint32_t)get_cluster_sleep_time(cluster, NULL,
sleep_us = (uint32_t)get_cluster_sleep_time(cluster,
from_idle, &cpupred_us);
if (from_idle) {
@@ -980,8 +980,12 @@ static int cluster_select(struct lpm_cluster *cluster, bool from_idle,
if (suspend_in_progress && from_idle && level->notify_rpm)
continue;
if (level->notify_rpm && !system_sleep_allowed())
continue;
if (level->notify_rpm) {
if (!(sys_pm_ops && sys_pm_ops->sleep_allowed))
continue;
if (!sys_pm_ops->sleep_allowed())
continue;
}
best_level = i;
@@ -1015,6 +1019,11 @@ static int cluster_configure(struct lpm_cluster *cluster, int idx,
bool from_idle, int predicted)
{
struct lpm_cluster_level *level = &cluster->levels[idx];
struct cpumask online_cpus, cpumask;
unsigned int cpu;
cpumask_and(&online_cpus, &cluster->num_children_in_sync,
cpu_online_mask);
if (!cpumask_equal(&cluster->num_children_in_sync, &cluster->child_cpus)
|| is_IPI_pending(&cluster->num_children_in_sync)) {
@@ -1036,10 +1045,13 @@ static int cluster_configure(struct lpm_cluster *cluster, int idx,
}
if (level->notify_rpm) {
cpu = get_next_online_cpu(from_idle);
cpumask_copy(&cpumask, cpumask_of(cpu));
clear_predict_history();
clear_cl_predict_history();
if (system_sleep_enter())
return -EBUSY;
if (sys_pm_ops && sys_pm_ops->enter)
if ((sys_pm_ops->enter(&cpumask)))
return -EBUSY;
}
/* Notify cluster enter event after successfully config completion */
cluster_notify(cluster, level, true);
@@ -1170,7 +1182,8 @@ static void cluster_unprepare(struct lpm_cluster *cluster,
level = &cluster->levels[cluster->last_level];
if (level->notify_rpm)
system_sleep_exit();
if (sys_pm_ops && sys_pm_ops->exit)
sys_pm_ops->exit();
update_debug_pc_event(CLUSTER_EXIT, cluster->last_level,
cluster->num_children_in_sync.bits[0],
@@ -1223,7 +1236,8 @@ static inline void cpu_unprepare(struct lpm_cpu *cpu, int cpu_index,
cpu_pm_exit();
}
int get_cluster_id(struct lpm_cluster *cluster, int *aff_lvl)
static int get_cluster_id(struct lpm_cluster *cluster, int *aff_lvl,
bool from_idle)
{
int state_id = 0;
@@ -1236,7 +1250,7 @@ int get_cluster_id(struct lpm_cluster *cluster, int *aff_lvl)
&cluster->child_cpus))
goto unlock_and_return;
state_id |= get_cluster_id(cluster->parent, aff_lvl);
state_id |= get_cluster_id(cluster->parent, aff_lvl, from_idle);
if (cluster->last_level != cluster->default_level) {
struct lpm_cluster_level *level
@@ -1250,7 +1264,8 @@ int get_cluster_id(struct lpm_cluster *cluster, int *aff_lvl)
* the wakeup value by reading the bc timer directly.
*/
if (level->notify_rpm)
system_sleep_update_wakeup();
if (sys_pm_ops && sys_pm_ops->update_wakeup)
sys_pm_ops->update_wakeup(from_idle);
if (level->psci_id)
(*aff_lvl)++;
}
@@ -1279,7 +1294,7 @@ static bool psci_enter_sleep(struct lpm_cpu *cpu, int idx, bool from_idle)
return success;
}
state_id = get_cluster_id(cpu->parent, &affinity_level);
state_id = get_cluster_id(cpu->parent, &affinity_level, from_idle);
power_state = PSCI_POWER_STATE(cpu->levels[idx].is_reset);
affinity_level = PSCI_AFFINITY_LEVEL(affinity_level);
state_id |= power_state | affinity_level | cpu->levels[idx].psci_id;

View File

@@ -108,8 +108,6 @@ struct lpm_cluster {
struct hrtimer histtimer;
};
void lpm_suspend_wake_time(uint64_t wakeup_time);
struct lpm_cluster *lpm_of_parse_cluster(struct platform_device *pdev);
void free_cluster_node(struct lpm_cluster *cluster);
void cluster_dt_walkthrough(struct lpm_cluster *cluster);

View File

@@ -13,11 +13,9 @@
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <soc/qcom/rpmh.h>
#include <soc/qcom/system_pm.h>
#include <clocksource/arm_arch_timer.h>
#include <soc/qcom/lpm_levels.h>
#define PDC_TIME_VALID_SHIFT 31
#define PDC_TIME_UPPER_MASK 0xFFFFFF
@@ -35,7 +33,7 @@ static int setup_wakeup(uint32_t lo, uint32_t hi)
return rpmh_write_control(rpmh_client, cmd, ARRAY_SIZE(cmd));
}
int system_sleep_update_wakeup(void)
static int system_sleep_update_wakeup(bool from_idle)
{
uint32_t lo = ~0U, hi = ~0U;
@@ -44,16 +42,14 @@ int system_sleep_update_wakeup(void)
return setup_wakeup(lo, hi);
}
EXPORT_SYMBOL(system_sleep_update_wakeup);
/**
* system_sleep_allowed() - Returns if its okay to enter system low power modes
*/
bool system_sleep_allowed(void)
static bool system_sleep_allowed(void)
{
return (rpmh_ctrlr_idle(rpmh_client) == 0);
}
EXPORT_SYMBOL(system_sleep_allowed);
/**
* system_sleep_enter() - Activties done when entering system low power modes
@@ -61,22 +57,24 @@ EXPORT_SYMBOL(system_sleep_allowed);
* Returns 0 for success or error values from writing the sleep/wake values to
* the hardware block.
*/
int system_sleep_enter(void)
static int system_sleep_enter(struct cpumask *mask)
{
if (IS_ERR_OR_NULL(rpmh_client))
return -EFAULT;
return rpmh_flush(rpmh_client);
}
EXPORT_SYMBOL(system_sleep_enter);
/**
* system_sleep_exit() - Activities done when exiting system low power modes
*/
void system_sleep_exit(void)
static void system_sleep_exit(void)
{
}
EXPORT_SYMBOL(system_sleep_exit);
static struct system_pm_ops pm_ops = {
.enter = system_sleep_enter,
.exit = system_sleep_exit,
.update_wakeup = system_sleep_update_wakeup,
.sleep_allowed = system_sleep_allowed,
};
static int sys_pm_probe(struct platform_device *pdev)
{
@@ -84,7 +82,7 @@ static int sys_pm_probe(struct platform_device *pdev)
if (IS_ERR_OR_NULL(rpmh_client))
return PTR_ERR(rpmh_client);
return 0;
return register_system_pm_ops(&pm_ops);
}
static const struct of_device_id sys_pm_drv_match[] = {

View File

@@ -0,0 +1,30 @@
/* Copyright (c) 2018, 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
* 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.
*
*/
#ifndef __SOC_QCOM_LPM_LEVEL_H__
#define __SOC_QCOM_LPM_LEVEL_H__
struct system_pm_ops {
int (*enter)(struct cpumask *mask);
void (*exit)(void);
int (*update_wakeup)(bool);
bool (*sleep_allowed)(void);
};
#ifdef CONFIG_MSM_PM
uint32_t register_system_pm_ops(struct system_pm_ops *pm_ops);
#else
static inline uint32_t register_system_pm_ops(struct system_pm_ops *pm_ops)
{ return -ENODEV; }
#endif
#endif

View File

@@ -1,39 +0,0 @@
/* Copyright (c) 2016-2017, 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
* 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.
*
*/
#ifndef __SOC_QCOM_SYS_PM_H__
#define __SOC_QCOM_SYS_PM_H__
#ifdef CONFIG_QTI_SYSTEM_PM
int system_sleep_enter(void);
void system_sleep_exit(void);
bool system_sleep_allowed(void);
int system_sleep_update_wakeup(void);
#else
static inline int system_sleep_enter(void)
{ return -ENODEV; }
static inline void system_sleep_exit(void)
{ }
static inline bool system_sleep_allowed(void)
{ return false; }
static inline int system_sleep_update_wakeup(void)
{ return -ENODEV; }
#endif /* CONFIG_QTI_SYSTEM_PM */
#endif /* __SOC_QCOM_SYS_PM_H__ */