162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  Copyright (C) 2019 Linaro Limited.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Author: Daniel Lezcano <daniel.lezcano@linaro.org>
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci#define pr_fmt(fmt) "cpuidle cooling: " fmt
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/cpu.h>
1162306a36Sopenharmony_ci#include <linux/cpu_cooling.h>
1262306a36Sopenharmony_ci#include <linux/cpuidle.h>
1362306a36Sopenharmony_ci#include <linux/device.h>
1462306a36Sopenharmony_ci#include <linux/err.h>
1562306a36Sopenharmony_ci#include <linux/idle_inject.h>
1662306a36Sopenharmony_ci#include <linux/of.h>
1762306a36Sopenharmony_ci#include <linux/slab.h>
1862306a36Sopenharmony_ci#include <linux/thermal.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci/**
2162306a36Sopenharmony_ci * struct cpuidle_cooling_device - data for the idle cooling device
2262306a36Sopenharmony_ci * @ii_dev: an atomic to keep track of the last task exiting the idle cycle
2362306a36Sopenharmony_ci * @state: a normalized integer giving the state of the cooling device
2462306a36Sopenharmony_ci */
2562306a36Sopenharmony_cistruct cpuidle_cooling_device {
2662306a36Sopenharmony_ci	struct idle_inject_device *ii_dev;
2762306a36Sopenharmony_ci	unsigned long state;
2862306a36Sopenharmony_ci};
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci/**
3162306a36Sopenharmony_ci * cpuidle_cooling_runtime - Running time computation
3262306a36Sopenharmony_ci * @idle_duration_us: CPU idle time to inject in microseconds
3362306a36Sopenharmony_ci * @state: a percentile based number
3462306a36Sopenharmony_ci *
3562306a36Sopenharmony_ci * The running duration is computed from the idle injection duration
3662306a36Sopenharmony_ci * which is fixed. If we reach 100% of idle injection ratio, that
3762306a36Sopenharmony_ci * means the running duration is zero. If we have a 50% ratio
3862306a36Sopenharmony_ci * injection, that means we have equal duration for idle and for
3962306a36Sopenharmony_ci * running duration.
4062306a36Sopenharmony_ci *
4162306a36Sopenharmony_ci * The formula is deduced as follows:
4262306a36Sopenharmony_ci *
4362306a36Sopenharmony_ci *  running = idle x ((100 / ratio) - 1)
4462306a36Sopenharmony_ci *
4562306a36Sopenharmony_ci * For precision purpose for integer math, we use the following:
4662306a36Sopenharmony_ci *
4762306a36Sopenharmony_ci *  running = (idle x 100) / ratio - idle
4862306a36Sopenharmony_ci *
4962306a36Sopenharmony_ci * For example, if we have an injected duration of 50%, then we end up
5062306a36Sopenharmony_ci * with 10ms of idle injection and 10ms of running duration.
5162306a36Sopenharmony_ci *
5262306a36Sopenharmony_ci * Return: An unsigned int for a usec based runtime duration.
5362306a36Sopenharmony_ci */
5462306a36Sopenharmony_cistatic unsigned int cpuidle_cooling_runtime(unsigned int idle_duration_us,
5562306a36Sopenharmony_ci					    unsigned long state)
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	if (!state)
5862306a36Sopenharmony_ci		return 0;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	return ((idle_duration_us * 100) / state) - idle_duration_us;
6162306a36Sopenharmony_ci}
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci/**
6462306a36Sopenharmony_ci * cpuidle_cooling_get_max_state - Get the maximum state
6562306a36Sopenharmony_ci * @cdev  : the thermal cooling device
6662306a36Sopenharmony_ci * @state : a pointer to the state variable to be filled
6762306a36Sopenharmony_ci *
6862306a36Sopenharmony_ci * The function always returns 100 as the injection ratio. It is
6962306a36Sopenharmony_ci * percentile based for consistency accross different platforms.
7062306a36Sopenharmony_ci *
7162306a36Sopenharmony_ci * Return: The function can not fail, it is always zero
7262306a36Sopenharmony_ci */
7362306a36Sopenharmony_cistatic int cpuidle_cooling_get_max_state(struct thermal_cooling_device *cdev,
7462306a36Sopenharmony_ci					 unsigned long *state)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	/*
7762306a36Sopenharmony_ci	 * Depending on the configuration or the hardware, the running
7862306a36Sopenharmony_ci	 * cycle and the idle cycle could be different. We want to
7962306a36Sopenharmony_ci	 * unify that to an 0..100 interval, so the set state
8062306a36Sopenharmony_ci	 * interface will be the same whatever the platform is.
8162306a36Sopenharmony_ci	 *
8262306a36Sopenharmony_ci	 * The state 100% will make the cluster 100% ... idle. A 0%
8362306a36Sopenharmony_ci	 * injection ratio means no idle injection at all and 50%
8462306a36Sopenharmony_ci	 * means for 10ms of idle injection, we have 10ms of running
8562306a36Sopenharmony_ci	 * time.
8662306a36Sopenharmony_ci	 */
8762306a36Sopenharmony_ci	*state = 100;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	return 0;
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci/**
9362306a36Sopenharmony_ci * cpuidle_cooling_get_cur_state - Get the current cooling state
9462306a36Sopenharmony_ci * @cdev: the thermal cooling device
9562306a36Sopenharmony_ci * @state: a pointer to the state
9662306a36Sopenharmony_ci *
9762306a36Sopenharmony_ci * The function just copies  the state value from the private thermal
9862306a36Sopenharmony_ci * cooling device structure, the mapping is 1 <-> 1.
9962306a36Sopenharmony_ci *
10062306a36Sopenharmony_ci * Return: The function can not fail, it is always zero
10162306a36Sopenharmony_ci */
10262306a36Sopenharmony_cistatic int cpuidle_cooling_get_cur_state(struct thermal_cooling_device *cdev,
10362306a36Sopenharmony_ci					 unsigned long *state)
10462306a36Sopenharmony_ci{
10562306a36Sopenharmony_ci	struct cpuidle_cooling_device *idle_cdev = cdev->devdata;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	*state = idle_cdev->state;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	return 0;
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci/**
11362306a36Sopenharmony_ci * cpuidle_cooling_set_cur_state - Set the current cooling state
11462306a36Sopenharmony_ci * @cdev: the thermal cooling device
11562306a36Sopenharmony_ci * @state: the target state
11662306a36Sopenharmony_ci *
11762306a36Sopenharmony_ci * The function checks first if we are initiating the mitigation which
11862306a36Sopenharmony_ci * in turn wakes up all the idle injection tasks belonging to the idle
11962306a36Sopenharmony_ci * cooling device. In any case, it updates the internal state for the
12062306a36Sopenharmony_ci * cooling device.
12162306a36Sopenharmony_ci *
12262306a36Sopenharmony_ci * Return: The function can not fail, it is always zero
12362306a36Sopenharmony_ci */
12462306a36Sopenharmony_cistatic int cpuidle_cooling_set_cur_state(struct thermal_cooling_device *cdev,
12562306a36Sopenharmony_ci					 unsigned long state)
12662306a36Sopenharmony_ci{
12762306a36Sopenharmony_ci	struct cpuidle_cooling_device *idle_cdev = cdev->devdata;
12862306a36Sopenharmony_ci	struct idle_inject_device *ii_dev = idle_cdev->ii_dev;
12962306a36Sopenharmony_ci	unsigned long current_state = idle_cdev->state;
13062306a36Sopenharmony_ci	unsigned int runtime_us, idle_duration_us;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	idle_cdev->state = state;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	idle_inject_get_duration(ii_dev, &runtime_us, &idle_duration_us);
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	runtime_us = cpuidle_cooling_runtime(idle_duration_us, state);
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	idle_inject_set_duration(ii_dev, runtime_us, idle_duration_us);
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	if (current_state == 0 && state > 0) {
14162306a36Sopenharmony_ci		idle_inject_start(ii_dev);
14262306a36Sopenharmony_ci	} else if (current_state > 0 && !state)  {
14362306a36Sopenharmony_ci		idle_inject_stop(ii_dev);
14462306a36Sopenharmony_ci	}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	return 0;
14762306a36Sopenharmony_ci}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci/**
15062306a36Sopenharmony_ci * cpuidle_cooling_ops - thermal cooling device ops
15162306a36Sopenharmony_ci */
15262306a36Sopenharmony_cistatic struct thermal_cooling_device_ops cpuidle_cooling_ops = {
15362306a36Sopenharmony_ci	.get_max_state = cpuidle_cooling_get_max_state,
15462306a36Sopenharmony_ci	.get_cur_state = cpuidle_cooling_get_cur_state,
15562306a36Sopenharmony_ci	.set_cur_state = cpuidle_cooling_set_cur_state,
15662306a36Sopenharmony_ci};
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci/**
15962306a36Sopenharmony_ci * __cpuidle_cooling_register: register the cooling device
16062306a36Sopenharmony_ci * @drv: a cpuidle driver structure pointer
16162306a36Sopenharmony_ci * @np: a device node structure pointer used for the thermal binding
16262306a36Sopenharmony_ci *
16362306a36Sopenharmony_ci * This function is in charge of allocating the cpuidle cooling device
16462306a36Sopenharmony_ci * structure, the idle injection, initialize them and register the
16562306a36Sopenharmony_ci * cooling device to the thermal framework.
16662306a36Sopenharmony_ci *
16762306a36Sopenharmony_ci * Return: zero on success, a negative value returned by one of the
16862306a36Sopenharmony_ci * underlying subsystem in case of error
16962306a36Sopenharmony_ci */
17062306a36Sopenharmony_cistatic int __cpuidle_cooling_register(struct device_node *np,
17162306a36Sopenharmony_ci				      struct cpuidle_driver *drv)
17262306a36Sopenharmony_ci{
17362306a36Sopenharmony_ci	struct idle_inject_device *ii_dev;
17462306a36Sopenharmony_ci	struct cpuidle_cooling_device *idle_cdev;
17562306a36Sopenharmony_ci	struct thermal_cooling_device *cdev;
17662306a36Sopenharmony_ci	struct device *dev;
17762306a36Sopenharmony_ci	unsigned int idle_duration_us = TICK_USEC;
17862306a36Sopenharmony_ci	unsigned int latency_us = UINT_MAX;
17962306a36Sopenharmony_ci	char *name;
18062306a36Sopenharmony_ci	int ret;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	idle_cdev = kzalloc(sizeof(*idle_cdev), GFP_KERNEL);
18362306a36Sopenharmony_ci	if (!idle_cdev) {
18462306a36Sopenharmony_ci		ret = -ENOMEM;
18562306a36Sopenharmony_ci		goto out;
18662306a36Sopenharmony_ci	}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	ii_dev = idle_inject_register(drv->cpumask);
18962306a36Sopenharmony_ci	if (!ii_dev) {
19062306a36Sopenharmony_ci		ret = -EINVAL;
19162306a36Sopenharmony_ci		goto out_kfree;
19262306a36Sopenharmony_ci	}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	of_property_read_u32(np, "duration-us", &idle_duration_us);
19562306a36Sopenharmony_ci	of_property_read_u32(np, "exit-latency-us", &latency_us);
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	idle_inject_set_duration(ii_dev, TICK_USEC, idle_duration_us);
19862306a36Sopenharmony_ci	idle_inject_set_latency(ii_dev, latency_us);
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	idle_cdev->ii_dev = ii_dev;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	dev = get_cpu_device(cpumask_first(drv->cpumask));
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	name = kasprintf(GFP_KERNEL, "idle-%s", dev_name(dev));
20562306a36Sopenharmony_ci	if (!name) {
20662306a36Sopenharmony_ci		ret = -ENOMEM;
20762306a36Sopenharmony_ci		goto out_unregister;
20862306a36Sopenharmony_ci	}
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	cdev = thermal_of_cooling_device_register(np, name, idle_cdev,
21162306a36Sopenharmony_ci						  &cpuidle_cooling_ops);
21262306a36Sopenharmony_ci	if (IS_ERR(cdev)) {
21362306a36Sopenharmony_ci		ret = PTR_ERR(cdev);
21462306a36Sopenharmony_ci		goto out_kfree_name;
21562306a36Sopenharmony_ci	}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	pr_debug("%s: Idle injection set with idle duration=%u, latency=%u\n",
21862306a36Sopenharmony_ci		 name, idle_duration_us, latency_us);
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	kfree(name);
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	return 0;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ciout_kfree_name:
22562306a36Sopenharmony_ci	kfree(name);
22662306a36Sopenharmony_ciout_unregister:
22762306a36Sopenharmony_ci	idle_inject_unregister(ii_dev);
22862306a36Sopenharmony_ciout_kfree:
22962306a36Sopenharmony_ci	kfree(idle_cdev);
23062306a36Sopenharmony_ciout:
23162306a36Sopenharmony_ci	return ret;
23262306a36Sopenharmony_ci}
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci/**
23562306a36Sopenharmony_ci * cpuidle_cooling_register - Idle cooling device initialization function
23662306a36Sopenharmony_ci * @drv: a cpuidle driver structure pointer
23762306a36Sopenharmony_ci *
23862306a36Sopenharmony_ci * This function is in charge of creating a cooling device per cpuidle
23962306a36Sopenharmony_ci * driver and register it to the thermal framework.
24062306a36Sopenharmony_ci */
24162306a36Sopenharmony_civoid cpuidle_cooling_register(struct cpuidle_driver *drv)
24262306a36Sopenharmony_ci{
24362306a36Sopenharmony_ci	struct device_node *cooling_node;
24462306a36Sopenharmony_ci	struct device_node *cpu_node;
24562306a36Sopenharmony_ci	int cpu, ret;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	for_each_cpu(cpu, drv->cpumask) {
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci		cpu_node = of_cpu_device_node_get(cpu);
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci		cooling_node = of_get_child_by_name(cpu_node, "thermal-idle");
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci		of_node_put(cpu_node);
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci		if (!cooling_node) {
25662306a36Sopenharmony_ci			pr_debug("'thermal-idle' node not found for cpu%d\n", cpu);
25762306a36Sopenharmony_ci			continue;
25862306a36Sopenharmony_ci		}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci		ret = __cpuidle_cooling_register(cooling_node, drv);
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci		of_node_put(cooling_node);
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci		if (ret) {
26562306a36Sopenharmony_ci			pr_err("Failed to register the cpuidle cooling device" \
26662306a36Sopenharmony_ci			       "for cpu%d: %d\n", cpu, ret);
26762306a36Sopenharmony_ci			break;
26862306a36Sopenharmony_ci		}
26962306a36Sopenharmony_ci	}
27062306a36Sopenharmony_ci}
271