Merge "defconfig: arm64: Enable hotplug cooling device for lahaina"

This commit is contained in:
qctecmdr 2020-10-08 21:56:15 -07:00 • committed by Gerrit - the friendly Code Review server
commit 3fe3c763dc
4 changed files with 290 additions and 0 deletions

View file

@ -125,6 +125,7 @@ CONFIG_MSM_PERFORMANCE=y
CONFIG_QTI_QMI_COOLING_DEVICE=y
CONFIG_QTI_ADC_TM=y
CONFIG_QTI_CPU_ISOLATE_COOLING_DEVICE=y
CONFIG_QTI_CPU_HOTPLUG_COOLING_DEVICE=y
CONFIG_QTI_THERMAL_LIMITS_DCVS=y
CONFIG_QTI_CPU_VOLTAGE_COOLING_DEVICE=y
CONFIG_QTI_POLICY_ENGINE_SENSOR=y

View file

@ -145,3 +145,12 @@ config QTI_LIMITS_ISENSE_CDSP
shared memory and enable sysfs file support to access this data. This
driver is required for the chipsets where isense hardware is present
as part of cdsp subsystem.
config QTI_CPU_HOTPLUG_COOLING_DEVICE
tristate "QTI CPU Hotplug cooling devices"
depends on THERMAL_OF && QTI_THERMAL && HOTPLUG_CPU
help
This enables the QTI CPU Hotplug cooling devices. These cooling
devices will be used by QTI chipset to hotplug a CPU to achieve
thermal cooling. CPU Hotplug will be done after core isolation,
to prevent any process from waking the mitigated CPU.

View file

@ -13,6 +13,7 @@ obj-$(CONFIG_QTI_QMI_COOLING_DEVICE) += qti_qmi_cdev.o
qti_qmi_cdev-y += thermal_mitigation_device_service_v01.o qmi_cooling.o
obj-$(CONFIG_QTI_CPU_ISOLATE_COOLING_DEVICE) += cpu_isolate.o
obj-$(CONFIG_QTI_CPU_HOTPLUG_COOLING_DEVICE) += cpu_hotplug.o
obj-$(CONFIG_QTI_BCL_PMIC5) += bcl_pmic5.o
obj-$(CONFIG_QTI_BCL_SOC_DRIVER) += bcl_soc.o
obj-$(CONFIG_QTI_THERMAL_LIMITS_DCVS) += msm_lmh_dcvs.o

View file

@ -0,0 +1,279 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2020, The Linux Foundation. All rights reserved.
*/
#include <linux/module.h>
#include <linux/thermal.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/cpu.h>
#include <linux/of_device.h>
#define CPU_HOTPLUG_LEVEL 1
struct cpu_hot_cdev {
struct list_head node;
int cpu_id;
bool cpu_hot_state;
struct thermal_cooling_device *cdev;
struct device_node *np;
struct work_struct reg_work;
};
static enum cpuhp_state cpu_hp_online;
static DEFINE_MUTEX(cpu_hot_lock);
static LIST_HEAD(cpu_hot_cdev_list);
static int cpu_hot_hp_online(unsigned int online_cpu)
{
struct cpu_hot_cdev *cpu_hot_cdev;
int ret = 0;
mutex_lock(&cpu_hot_lock);
list_for_each_entry(cpu_hot_cdev, &cpu_hot_cdev_list, node) {
if (online_cpu != cpu_hot_cdev->cpu_id)
continue;
if (cpu_hot_cdev->cdev) {
if (cpu_hot_cdev->cpu_hot_state) {
pr_debug("Offline CPU:%d\n",
online_cpu);
ret = NOTIFY_BAD;
}
} else
queue_work(system_highpri_wq, &cpu_hot_cdev->reg_work);
break;
}
mutex_unlock(&cpu_hot_lock);
return ret;
}
/**
* cpu_hot_set_cur_state - callback function to set the current cooling
* state.
* @cdev: thermal cooling device pointer.
* @state: set this variable to the current cooling state.
*
* Callback for the thermal cooling device to change the cpu hotplug
* current cooling state.
*
* Return: 0 on success, an error code otherwise.
*/
static int cpu_hot_set_cur_state(struct thermal_cooling_device *cdev,
unsigned long state)
{
struct cpu_hot_cdev *cpu_hot_cdev = cdev->devdata;
struct device *cpu_dev;
int ret = 0;
int cpu = 0;
if (cpu_hot_cdev->cpu_id == -1)
return -ENODEV;
/* Request state should be less than max_level */
if (state > CPU_HOTPLUG_LEVEL)
return -EINVAL;
state = !!state;
/* Check if the old cooling action is same as new cooling action */
if (cpu_hot_cdev->cpu_hot_state == state)
return 0;
mutex_lock(&cpu_hot_lock);
cpu = cpu_hot_cdev->cpu_id;
cpu_hot_cdev->cpu_hot_state = state;
mutex_unlock(&cpu_hot_lock);
cpu_dev = get_cpu_device(cpu);
if (!cpu_dev) {
pr_err("CPU:%d cpu dev error\n", cpu);
return ret;
}
if (state == CPU_HOTPLUG_LEVEL) {
ret = device_offline(cpu_dev);
if (ret < 0)
pr_err("CPU:%d offline error:%d\n", cpu, ret);
} else {
ret = device_online(cpu_dev);
if (ret)
pr_err("CPU:%d online error:%d\n", cpu, ret);
}
if (ret == 1) {
/* Device is already online or offline. This is not an error */
ret = 0;
}
return ret;
}
/**
* cpu_hot_get_cur_state - callback function to get the current cooling
* state.
* @cdev: thermal cooling device pointer.
* @state: fill this variable with the current cooling state.
*
* Callback for the thermal cooling device to return the cpu hotplug
* current cooling state.
*
* Return: 0 on success, an error code otherwise.
*/
static int cpu_hot_get_cur_state(struct thermal_cooling_device *cdev,
unsigned long *state)
{
struct cpu_hot_cdev *cpu_hot_cdev = cdev->devdata;
mutex_lock(&cpu_hot_lock);
*state = (cpu_hot_cdev->cpu_hot_state) ?
CPU_HOTPLUG_LEVEL : 0;
mutex_unlock(&cpu_hot_lock);
return 0;
}
/**
* cpu_hot_get_max_state - callback function to get the max cooling state.
* @cdev: thermal cooling device pointer.
* @state: fill this variable with the max cooling state.
*
* Callback for the thermal cooling device to return the cpu
* hotplug max cooling state.
*
* Return: 0 on success, an error code otherwise.
*/
static int cpu_hot_get_max_state(struct thermal_cooling_device *cdev,
unsigned long *state)
{
*state = CPU_HOTPLUG_LEVEL;
return 0;
}
static struct thermal_cooling_device_ops cpu_hot_cooling_ops = {
.get_max_state = cpu_hot_get_max_state,
.get_cur_state = cpu_hot_get_cur_state,
.set_cur_state = cpu_hot_set_cur_state,
};
static void cpu_hot_register_cdev(struct work_struct *work)
{
struct cpu_hot_cdev *cpu_hot_cdev =
container_of(work, struct cpu_hot_cdev, reg_work);
char cdev_name[THERMAL_NAME_LENGTH] = "";
int ret = 0;
snprintf(cdev_name, THERMAL_NAME_LENGTH, "cpu-hotplug%d",
cpu_hot_cdev->cpu_id);
cpu_hot_cdev->cdev = thermal_of_cooling_device_register(
cpu_hot_cdev->np,
cdev_name,
cpu_hot_cdev,
&cpu_hot_cooling_ops);
if (IS_ERR(cpu_hot_cdev->cdev)) {
ret = PTR_ERR(cpu_hot_cdev->cdev);
pr_err("Cooling register failed for %s, ret:%d\n",
cdev_name, ret);
cpu_hot_cdev->cdev = NULL;
return;
}
pr_debug("Cooling device [%s] registered.\n", cdev_name);
}
static int cpu_hot_probe(struct platform_device *pdev)
{
int ret = 0, cpu = 0;
struct device_node *dev_phandle, *subsys_np = NULL;
struct device *cpu_dev;
struct cpu_hot_cdev *cpu_hot_cdev = NULL;
struct device_node *np = pdev->dev.of_node;
INIT_LIST_HEAD(&cpu_hot_cdev_list);
for_each_available_child_of_node(np, subsys_np) {
cpu_hot_cdev = devm_kzalloc(&pdev->dev,
sizeof(*cpu_hot_cdev), GFP_KERNEL);
if (!cpu_hot_cdev) {
of_node_put(subsys_np);
return -ENOMEM;
}
cpu_hot_cdev->cpu_id = -1;
cpu_hot_cdev->cpu_hot_state = false;
cpu_hot_cdev->cdev = NULL;
cpu_hot_cdev->np = subsys_np;
dev_phandle = of_parse_phandle(subsys_np, "qcom,cpu", 0);
for_each_possible_cpu(cpu) {
cpu_dev = get_cpu_device(cpu);
if (cpu_dev && cpu_dev->of_node == dev_phandle) {
cpu_hot_cdev->cpu_id = cpu;
break;
}
}
if (cpu_hot_cdev->cpu_id == -1) {
dev_err(&pdev->dev, "Invalid CPU phandle\n");
continue;
}
INIT_WORK(&cpu_hot_cdev->reg_work,
cpu_hot_register_cdev);
list_add(&cpu_hot_cdev->node, &cpu_hot_cdev_list);
}
ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "cpu-hotplug/cdev:online",
cpu_hot_hp_online, NULL);
if (ret < 0)
return ret;
cpu_hp_online = ret;
return 0;
}
static int cpu_hot_remove(struct platform_device *pdev)
{
struct cpu_hot_cdev *cpu_hot_cdev = NULL, *next = NULL;
struct device *cpu_dev = NULL;
int ret = 0;
if (cpu_hp_online) {
cpuhp_remove_state_nocalls(cpu_hp_online);
cpu_hp_online = 0;
}
mutex_lock(&cpu_hot_lock);
list_for_each_entry_safe(cpu_hot_cdev, next, &cpu_hot_cdev_list, node) {
if (!cpu_hot_cdev->cdev)
goto loop_skip;
thermal_cooling_device_unregister(cpu_hot_cdev->cdev);
if (cpu_hot_cdev->cpu_hot_state) {
cpu_hot_cdev->cpu_hot_state = false;
cpu_dev = get_cpu_device(cpu_hot_cdev->cpu_id);
if (!cpu_dev) {
pr_err("CPU:%d cpu dev error\n",
cpu_hot_cdev->cpu_id);
goto loop_skip;
}
ret = device_online(cpu_dev);
if (ret)
pr_err("CPU:%d online error:%d\n",
cpu_hot_cdev->cpu_id, ret);
}
loop_skip:
list_del(&cpu_hot_cdev->node);
}
mutex_unlock(&cpu_hot_lock);
return 0;
}
static const struct of_device_id cpu_hot_match[] = {
{ .compatible = "qcom,cpu-hotplug", },
{},
};
static struct platform_driver cpu_hot_driver = {
.probe = cpu_hot_probe,
.remove = cpu_hot_remove,
.driver = {
.name = KBUILD_MODNAME,
.of_match_table = cpu_hot_match,
},
};
module_platform_driver(cpu_hot_driver);
MODULE_LICENSE("GPL v2");