Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3c268d1d authored by Ram Chandrasekar's avatar Ram Chandrasekar
Browse files

drivers: thermal: cpu_cooling: Snapshot of cpu cooling driver



Add a snapshot of CPU cooling device driver changes from
msm-4.9 from 'commit <2d02e0aa148d> ("drivers: thermal: cpu-cooling:
Avoid deadlock").'

Change-Id: If3f61fcfdb78c68c794b37c58e632b75cd1ff467
Signed-off-by: default avatarRam Chandrasekar <rkumbako@codeaurora.org>
parent 5c5332ee
Loading
Loading
Loading
Loading
+340 −33
Original line number Diff line number Diff line
@@ -31,6 +31,9 @@
#include <linux/slab.h>
#include <linux/cpu.h>
#include <linux/cpu_cooling.h>
#include <linux/sched.h>
#include <linux/of_device.h>
#include <linux/suspend.h>

#include <trace/events/thermal.h>

@@ -46,6 +49,7 @@
 *	level 0 --> 1st Max Freq
 *	level 1 --> 2nd Max Freq
 *	...
 *	leven n --> core isolated
 */

/**
@@ -80,6 +84,10 @@ struct time_in_idle {
 *	cooling	devices.
 * @clipped_freq: integer value representing the absolute value of the clipped
 *	frequency.
 * @cpufreq_floor_state: integer value representing the frequency floor state
 *	of cpufreq cooling devices.
 * @floor_freq: integer value representing the absolute value of the floor
 *	frequency.
 * @max_level: maximum cooling level. One less than total number of valid
 *	cpufreq frequencies.
 * @freq_table: Freq table in descending order of frequencies
@@ -98,6 +106,8 @@ struct cpufreq_cooling_device {
	u32 last_load;
	unsigned int cpufreq_state;
	unsigned int clipped_freq;
	unsigned int cpufreq_floor_state;
	unsigned int floor_freq;
	unsigned int max_level;
	struct freq_table *freq_table;	/* In descending order */
	struct thermal_cooling_device *cdev;
@@ -105,8 +115,16 @@ struct cpufreq_cooling_device {
	struct list_head node;
	struct time_in_idle *idle_time;
	get_static_t plat_get_static_power;
	struct cpu_cooling_ops *plat_ops;
};

static atomic_t in_suspend;
static int8_t cpuhp_registered;
static struct work_struct cpuhp_register_work;
static struct cpumask cpus_pending_online;
static struct cpumask cpus_isolated_by_thermal;
static DEFINE_MUTEX(core_isolate_lock);

static DEFINE_IDA(cpufreq_ida);
static DEFINE_MUTEX(cooling_list_lock);
static LIST_HEAD(cpufreq_cdev_list);
@@ -133,6 +151,109 @@ static unsigned long get_level(struct cpufreq_cooling_device *cpufreq_cdev,
	return level - 1;
}

static int cpufreq_cooling_pm_notify(struct notifier_block *nb,
				unsigned long mode, void *_unused)
{
	struct cpufreq_cooling_device *cpufreq_cdev;
	unsigned int cpu;

	switch (mode) {
	case PM_HIBERNATION_PREPARE:
	case PM_RESTORE_PREPARE:
	case PM_SUSPEND_PREPARE:
		atomic_set(&in_suspend, 1);
		break;
	case PM_POST_HIBERNATION:
	case PM_POST_RESTORE:
	case PM_POST_SUSPEND:
		mutex_lock(&cooling_list_lock);
		list_for_each_entry(cpufreq_cdev, &cpufreq_cdev_list, node) {
			mutex_lock(&core_isolate_lock);
			if (cpufreq_cdev->cpufreq_state ==
				cpufreq_cdev->max_level) {
				cpu = cpufreq_cdev->policy->cpu;
				/*
				 * Unlock this lock before calling
				 * schedule_isolate. as this could lead to
				 * deadlock with hotplug path.
				 */
				mutex_unlock(&core_isolate_lock);
				if (cpu_online(cpu) &&
					!cpumask_test_and_set_cpu(cpu,
					&cpus_isolated_by_thermal)) {
					if (sched_isolate_cpu(cpu))
						cpumask_clear_cpu(cpu,
						&cpus_isolated_by_thermal);
				}
				continue;
			}
			mutex_unlock(&core_isolate_lock);
		}
		mutex_unlock(&cooling_list_lock);

		atomic_set(&in_suspend, 0);
		break;
	default:
		break;
	}
	return 0;
}

static struct notifier_block cpufreq_cooling_pm_nb = {
	.notifier_call = cpufreq_cooling_pm_notify,
};

static int cpufreq_hp_offline(unsigned int offline_cpu)
{
	struct cpufreq_cooling_device *cpufreq_cdev;

	mutex_lock(&cooling_list_lock);
	list_for_each_entry(cpufreq_cdev, &cpufreq_cdev_list, node) {
		if (!cpumask_test_cpu(offline_cpu,
					cpufreq_cdev->policy->related_cpus))
			continue;

		mutex_lock(&core_isolate_lock);
		if ((cpufreq_cdev->cpufreq_state == cpufreq_cdev->max_level) &&
			(cpumask_test_and_clear_cpu(offline_cpu,
			&cpus_isolated_by_thermal)))
			sched_unisolate_cpu_unlocked(offline_cpu);
		mutex_unlock(&core_isolate_lock);
		break;
	}
	mutex_unlock(&cooling_list_lock);

	return 0;
}

static int cpufreq_hp_online(unsigned int online_cpu)
{
	struct cpufreq_cooling_device *cpufreq_cdev;
	int ret = 0;

	if (atomic_read(&in_suspend))
		return 0;

	mutex_lock(&cooling_list_lock);
	list_for_each_entry(cpufreq_cdev, &cpufreq_cdev_list, node) {
		if (!cpumask_test_cpu(online_cpu,
					cpufreq_cdev->policy->related_cpus))
			continue;

		mutex_lock(&core_isolate_lock);
		if (cpufreq_cdev->cpufreq_state == cpufreq_cdev->max_level) {
			cpumask_set_cpu(online_cpu, &cpus_pending_online);
			ret = NOTIFY_BAD;
		}
		mutex_unlock(&core_isolate_lock);
		break;
	}
	mutex_unlock(&cooling_list_lock);

	return ret;
}


/**
 * cpufreq_thermal_notifier - notifier callback for cpufreq policy change.
 * @nb:	struct notifier_block * with callback info.
@@ -149,7 +270,7 @@ static int cpufreq_thermal_notifier(struct notifier_block *nb,
				    unsigned long event, void *data)
{
	struct cpufreq_policy *policy = data;
	unsigned long clipped_freq;
	unsigned long clipped_freq = ULONG_MAX, floor_freq = 0;
	struct cpufreq_cooling_device *cpufreq_cdev;

	if (event != CPUFREQ_ADJUST)
@@ -161,9 +282,14 @@ static int cpufreq_thermal_notifier(struct notifier_block *nb,
		 * A new copy of the policy is sent to the notifier and can't
		 * compare that directly.
		 */
		if (policy->cpu != cpufreq_cdev->policy->cpu)
		if (!cpumask_intersects(cpufreq_cdev->policy->related_cpus,
					policy->related_cpus))
			continue;

		if (cpufreq_cdev->clipped_freq < clipped_freq)
			clipped_freq = cpufreq_cdev->clipped_freq;
		if (cpufreq_cdev->floor_freq > floor_freq)
			floor_freq = cpufreq_cdev->floor_freq;
	}
	/*
	 * policy->max is the maximum allowed frequency defined by user
	 * and clipped_freq is the maximum that thermal constraints
@@ -174,13 +300,12 @@ static int cpufreq_thermal_notifier(struct notifier_block *nb,
	 *
	 * But, if clipped_freq is greater than policy->max, we don't
	 * need to do anything.
	 *
	 * Similarly, if policy minimum set by the user is less than
	 * the floor_frequency, then adjust the policy->min.
	 */
		clipped_freq = cpufreq_cdev->clipped_freq;

		if (policy->max > clipped_freq)
			cpufreq_verify_within_limits(policy, 0, clipped_freq);
		break;
	}
	if (policy->max > clipped_freq || policy->min < floor_freq)
		cpufreq_verify_within_limits(policy, floor_freq, clipped_freq);
	mutex_unlock(&cooling_list_lock);

	return NOTIFY_OK;
@@ -410,6 +535,76 @@ static int cpufreq_get_max_state(struct thermal_cooling_device *cdev,
	return 0;
}

/**
 * cpufreq_get_min_state - callback function to get the device floor state.
 * @cdev: thermal cooling device pointer.
 * @state: fill this variable with the cooling device floor.
 *
 * Callback for the thermal cooling device to return the cpufreq
 * floor state.
 *
 * Return: 0 on success, an error code otherwise.
 */
static int cpufreq_get_min_state(struct thermal_cooling_device *cdev,
				 unsigned long *state)
{
	struct cpufreq_cooling_device *cpufreq_cdev = cdev->devdata;

	*state = cpufreq_cdev->cpufreq_floor_state;

	return 0;
}

/**
 * cpufreq_set_min_state - callback function to set the device floor state.
 * @cdev: thermal cooling device pointer.
 * @state: set this variable to the current cooling state.
 *
 * Callback for the thermal cooling device to change the cpufreq
 * floor state.
 *
 * Return: 0 on success, an error code otherwise.
 */
static int cpufreq_set_min_state(struct thermal_cooling_device *cdev,
				 unsigned long state)
{
	struct cpufreq_cooling_device *cpufreq_cdev = cdev->devdata;
	unsigned int cpu = cpufreq_cdev->policy->cpu;
	unsigned int floor_freq;

	if (state > cpufreq_cdev->max_level)
		state = cpufreq_cdev->max_level;

	if (cpufreq_cdev->cpufreq_floor_state == state)
		return 0;

	cpufreq_cdev->cpufreq_floor_state = state;

	/*
	 * Check if the device has a platform mitigation function that
	 * can handle the CPU freq mitigation, if not, notify cpufreq
	 * framework.
	 */
	if (cpufreq_cdev->plat_ops &&
		cpufreq_cdev->plat_ops->floor_limit) {
		/*
		 * Last level is core isolation so use the frequency
		 * of previous state.
		 */
		if (state == cpufreq_cdev->max_level)
			state--;
		floor_freq = cpufreq_cdev->freq_table[state].frequency;
		cpufreq_cdev->floor_freq = floor_freq;
		cpufreq_cdev->plat_ops->floor_limit(cpu, floor_freq);
	} else {
		floor_freq = cpufreq_cdev->freq_table[state].frequency;
		cpufreq_cdev->floor_freq = floor_freq;
		cpufreq_update_policy(cpu);
	}

	return 0;
}

/**
 * cpufreq_get_cur_state - callback function to get the current cooling state.
 * @cdev: thermal cooling device pointer.
@@ -445,6 +640,10 @@ static int cpufreq_set_cur_state(struct thermal_cooling_device *cdev,
{
	struct cpufreq_cooling_device *cpufreq_cdev = cdev->devdata;
	unsigned int clip_freq;
	unsigned long prev_state;
	struct device *cpu_dev;
	int ret = 0;
	int cpu = 0;

	/* Request state should be less than max_level */
	if (WARN_ON(state > cpufreq_cdev->max_level))
@@ -454,11 +653,50 @@ static int cpufreq_set_cur_state(struct thermal_cooling_device *cdev,
	if (cpufreq_cdev->cpufreq_state == state)
		return 0;

	mutex_lock(&core_isolate_lock);
	cpu = cpufreq_cdev->policy->cpu;
	prev_state = cpufreq_cdev->cpufreq_state;
	cpufreq_cdev->cpufreq_state = state;
	mutex_unlock(&core_isolate_lock);
	/* If state is the last, isolate the CPU */
	if (state == cpufreq_cdev->max_level) {
		if (cpu_online(cpu) &&
			(!cpumask_test_and_set_cpu(cpu,
			&cpus_isolated_by_thermal))) {
			if (sched_isolate_cpu(cpu))
				cpumask_clear_cpu(cpu,
					&cpus_isolated_by_thermal);
		}
		return ret;
	} else if ((prev_state == cpufreq_cdev->max_level)
			&& (state < cpufreq_cdev->max_level)) {
		if (cpumask_test_and_clear_cpu(cpu, &cpus_pending_online)) {
			cpu_dev = get_cpu_device(cpu);
			ret = device_online(cpu_dev);
			if (ret)
				pr_err("CPU:%d online error:%d\n", cpu, ret);
			goto update_frequency;
		} else if (cpumask_test_and_clear_cpu(cpu,
			&cpus_isolated_by_thermal)) {
			sched_unisolate_cpu(cpu);
		}
	}
update_frequency:
	clip_freq = cpufreq_cdev->freq_table[state].frequency;
	cpufreq_cdev->cpufreq_state = state;
	cpufreq_cdev->clipped_freq = clip_freq;

	cpufreq_update_policy(cpufreq_cdev->policy->cpu);
	/* Check if the device has a platform mitigation function that
	 * can handle the CPU freq mitigation, if not, notify cpufreq
	 * framework.
	 */
	if (cpufreq_cdev->plat_ops) {
		if (cpufreq_cdev->plat_ops->ceil_limit)
			cpufreq_cdev->plat_ops->ceil_limit(cpu,
						clip_freq);
	} else {
		cpufreq_update_policy(cpu);
	}

	return 0;
}
@@ -635,6 +873,8 @@ static struct thermal_cooling_device_ops cpufreq_cooling_ops = {
	.get_max_state = cpufreq_get_max_state,
	.get_cur_state = cpufreq_get_cur_state,
	.set_cur_state = cpufreq_set_cur_state,
	.set_min_state = cpufreq_set_min_state,
	.get_min_state = cpufreq_get_min_state,
};

static struct thermal_cooling_device_ops cpufreq_power_cooling_ops = {
@@ -665,6 +905,16 @@ static unsigned int find_next_max(struct cpufreq_frequency_table *table,
	return max;
}

static void register_cdev(struct work_struct *work)
{
	int ret = 0;

	ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
				"cpu_cooling/no-sched",	cpufreq_hp_online,
				cpufreq_hp_offline);
	if (ret < 0)
		pr_err("Error registering for hotpug callback:%d\n", ret);
}
/**
 * __cpufreq_cooling_register - helper function to create cpufreq cooling device
 * @np: a valid struct device_node to the cooling device device tree node
@@ -673,6 +923,9 @@ static unsigned int find_next_max(struct cpufreq_frequency_table *table,
 * @capacitance: dynamic power coefficient for these cpus
 * @plat_static_func: function to calculate the static power consumed by these
 *                    cpus (optional)
 * @plat_mitig_func: function that does the mitigation by changing the
 *                   frequencies (Optional). By default, cpufreq framework will
 *                   be notified of the new limits.
 *
 * This interface function registers the cpufreq cooling device with the name
 * "thermal-cpufreq-%x". This api can support multiple instances of cpufreq
@@ -685,7 +938,8 @@ static unsigned int find_next_max(struct cpufreq_frequency_table *table,
static struct thermal_cooling_device *
__cpufreq_cooling_register(struct device_node *np,
			struct cpufreq_policy *policy, u32 capacitance,
			get_static_t plat_static_func)
			get_static_t plat_static_func,
			struct cpu_cooling_ops *plat_ops)
{
	struct thermal_cooling_device *cdev;
	struct cpufreq_cooling_device *cpufreq_cdev;
@@ -721,16 +975,18 @@ __cpufreq_cooling_register(struct device_node *np,
		goto free_cdev;
	}

	/* max_level is an index, not a counter */
	cpufreq_cdev->max_level = i - 1;
	/* Last level will indicate the core will be isolated. */
	cpufreq_cdev->max_level = i + 1;

	cpufreq_cdev->freq_table = kmalloc_array(i,
	cpufreq_cdev->freq_table = kmalloc_array(cpufreq_cdev->max_level,
					sizeof(*cpufreq_cdev->freq_table),
					GFP_KERNEL);
	if (!cpufreq_cdev->freq_table) {
		cdev = ERR_PTR(-ENOMEM);
		goto free_idle_time;
	}
	/* max_level is an index */
	cpufreq_cdev->max_level--;

	ret = ida_simple_get(&cpufreq_ida, 0, 0, GFP_KERNEL);
	if (ret < 0) {
@@ -743,7 +999,7 @@ __cpufreq_cooling_register(struct device_node *np,
		 cpufreq_cdev->id);

	/* Fill freq-table in descending order of frequencies */
	for (i = 0, freq = -1; i <= cpufreq_cdev->max_level; i++) {
	for (i = 0, freq = -1; i < cpufreq_cdev->max_level; i++) {
		freq = find_next_max(policy->freq_table, freq);
		cpufreq_cdev->freq_table[i].frequency = freq;

@@ -768,12 +1024,17 @@ __cpufreq_cooling_register(struct device_node *np,
		cooling_ops = &cpufreq_cooling_ops;
	}

	cpufreq_cdev->plat_ops = plat_ops;

	cdev = thermal_of_cooling_device_register(np, dev_name, cpufreq_cdev,
						  cooling_ops);
	if (IS_ERR(cdev))
		goto remove_ida;

	cpufreq_cdev->clipped_freq = cpufreq_cdev->freq_table[0].frequency;
	cpufreq_cdev->floor_freq =
		cpufreq_cdev->freq_table[cpufreq_cdev->max_level].frequency;
	cpufreq_cdev->cpufreq_floor_state = cpufreq_cdev->max_level;
	cpufreq_cdev->cdev = cdev;

	mutex_lock(&cooling_list_lock);
@@ -782,9 +1043,17 @@ __cpufreq_cooling_register(struct device_node *np,
	list_add(&cpufreq_cdev->node, &cpufreq_cdev_list);
	mutex_unlock(&cooling_list_lock);

	if (first)
	if (first && !cpufreq_cdev->plat_ops)
		cpufreq_register_notifier(&thermal_cpufreq_notifier_block,
					  CPUFREQ_POLICY_NOTIFIER);
	if (!cpuhp_registered) {
		cpuhp_registered = 1;
		register_pm_notifier(&cpufreq_cooling_pm_nb);
		cpumask_clear(&cpus_pending_online);
		cpumask_clear(&cpus_isolated_by_thermal);
		INIT_WORK(&cpuhp_register_work, register_cdev);
		queue_work(system_wq, &cpuhp_register_work);
	}

	return cdev;

@@ -813,7 +1082,7 @@ __cpufreq_cooling_register(struct device_node *np,
struct thermal_cooling_device *
cpufreq_cooling_register(struct cpufreq_policy *policy)
{
	return __cpufreq_cooling_register(NULL, policy, 0, NULL);
	return __cpufreq_cooling_register(NULL, policy, 0, NULL, NULL);
}
EXPORT_SYMBOL_GPL(cpufreq_cooling_register);

@@ -837,7 +1106,7 @@ of_cpufreq_cooling_register(struct device_node *np,
	if (!np)
		return ERR_PTR(-EINVAL);

	return __cpufreq_cooling_register(np, policy, 0, NULL);
	return __cpufreq_cooling_register(np, policy, 0, NULL, NULL);
}
EXPORT_SYMBOL_GPL(of_cpufreq_cooling_register);

@@ -867,10 +1136,44 @@ cpufreq_power_cooling_register(struct cpufreq_policy *policy, u32 capacitance,
			       get_static_t plat_static_func)
{
	return __cpufreq_cooling_register(NULL, policy, capacitance,
				plat_static_func);
				plat_static_func, NULL);
}
EXPORT_SYMBOL(cpufreq_power_cooling_register);

/**
 * cpufreq_platform_cooling_register() - create cpufreq cooling device with
 * additional platform specific mitigation function.
 *
 * @clip_cpus: cpumask of cpus where the frequency constraints will happen
 * @plat_ops: the platform mitigation functions that will be called insted of
 * cpufreq, if provided.
 *
 * Return: a valid struct thermal_cooling_device pointer on success,
 * on failure, it returns a corresponding ERR_PTR().
 */
struct thermal_cooling_device *
cpufreq_platform_cooling_register(const struct cpumask *clip_cpus,
				struct cpu_cooling_ops *plat_ops)
{
	struct device_node *cpu_node;
	struct cpufreq_policy *policy;

	cpu_node = of_cpu_device_node_get(cpumask_first(clip_cpus));
	if (!cpu_node) {
		pr_err("No cpu node\n");
		return ERR_PTR(-EINVAL);
	}
	policy = cpufreq_cpu_get(cpumask_first(clip_cpus));
	if (!policy) {
		pr_err("no policy for cpu%d\n", cpumask_first(clip_cpus));
		return ERR_PTR(-EINVAL);
	}

	return __cpufreq_cooling_register(cpu_node, policy, 0, NULL,
			plat_ops);
}
EXPORT_SYMBOL(cpufreq_platform_cooling_register);

/**
 * of_cpufreq_power_cooling_register() - create cpufreq cooling device with power extensions
 * @np:	a valid struct device_node to the cooling device device tree node
@@ -904,7 +1207,7 @@ of_cpufreq_power_cooling_register(struct device_node *np,
		return ERR_PTR(-EINVAL);

	return __cpufreq_cooling_register(np, policy, capacitance,
				plat_static_func);
				plat_static_func, NULL);
}
EXPORT_SYMBOL(of_cpufreq_power_cooling_register);

@@ -930,9 +1233,13 @@ void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev)
	last = list_empty(&cpufreq_cdev_list);
	mutex_unlock(&cooling_list_lock);

	if (last)
		cpufreq_unregister_notifier(&thermal_cpufreq_notifier_block,
	if (last) {
		unregister_pm_notifier(&cpufreq_cooling_pm_nb);
		if (!cpufreq_cdev->plat_ops)
			cpufreq_unregister_notifier(
					&thermal_cpufreq_notifier_block,
					CPUFREQ_POLICY_NOTIFIER);
	}

	thermal_cooling_device_unregister(cpufreq_cdev->cdev);
	ida_simple_remove(&cpufreq_ida, cpufreq_cdev->id);
+16 −0
Original line number Diff line number Diff line
@@ -32,6 +32,11 @@ struct cpufreq_policy;

typedef int (*get_static_t)(cpumask_t *cpumask, int interval,
			    unsigned long voltage, u32 *power);
typedef int (*plat_mitig_t)(int cpu, u32 clip_freq);

struct cpu_cooling_ops {
	plat_mitig_t ceil_limit, floor_limit;
};

#ifdef CONFIG_CPU_THERMAL
/**
@@ -45,6 +50,10 @@ struct thermal_cooling_device *
cpufreq_power_cooling_register(struct cpufreq_policy *policy,
			       u32 capacitance, get_static_t plat_static_func);

struct thermal_cooling_device *
cpufreq_platform_cooling_register(const struct cpumask *clip_cpus,
					struct cpu_cooling_ops *ops);

/**
 * of_cpufreq_cooling_register - create cpufreq cooling device based on DT.
 * @np: a valid struct device_node to the cooling device device tree node.
@@ -113,6 +122,13 @@ of_cpufreq_power_cooling_register(struct device_node *np,
	return NULL;
}

static inline struct thermal_cooling_device *
cpufreq_platform_cooling_register(const struct cpumask *clip_cpus,
					struct cpu_cooling_ops *ops)
{
	return NULL;
}

static inline
void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev)
{