From 3e4b12bdc32d2771af435d4cf8fac732df2c0371 Mon Sep 17 00:00:00 2001 From: Ram Chandrasekar Date: Fri, 11 Sep 2020 16:51:09 -0700 Subject: [PATCH 1/2] drivers: thermal: Add hotplug cooling device Add hotplug cooling device. This cooling device can be used in thermal condition after core isolation is triggered. This will help any process from triggering a isolated core. Change-Id: I4e77a85d12951c839b513eb9a05462fcdf1ce6e2 Signed-off-by: Ram Chandrasekar --- drivers/thermal/qcom/Kconfig | 9 + drivers/thermal/qcom/Makefile | 1 + drivers/thermal/qcom/cpu_hotplug.c | 279 +++++++++++++++++++++++++++++ 3 files changed, 289 insertions(+) create mode 100644 drivers/thermal/qcom/cpu_hotplug.c diff --git a/drivers/thermal/qcom/Kconfig b/drivers/thermal/qcom/Kconfig index 1c0a96e441a9..cf3e5df109f8 100644 --- a/drivers/thermal/qcom/Kconfig +++ b/drivers/thermal/qcom/Kconfig @@ -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. diff --git a/drivers/thermal/qcom/Makefile b/drivers/thermal/qcom/Makefile index a37fe6040c6f..0d96be34dfaf 100644 --- a/drivers/thermal/qcom/Makefile +++ b/drivers/thermal/qcom/Makefile @@ -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 diff --git a/drivers/thermal/qcom/cpu_hotplug.c b/drivers/thermal/qcom/cpu_hotplug.c new file mode 100644 index 000000000000..6020a14a42f9 --- /dev/null +++ b/drivers/thermal/qcom/cpu_hotplug.c @@ -0,0 +1,279 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + */ +#include +#include +#include +#include +#include +#include + +#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"); From 0209bc8baa156fd2fe9b0c18bf68b767e64ea176 Mon Sep 17 00:00:00 2001 From: Ram Chandrasekar Date: Fri, 11 Sep 2020 16:52:28 -0700 Subject: [PATCH 2/2] defconfig: arm64: Enable hotplug cooling device for lahaina Enable hotplug cooling device for lahaina. Change-Id: I4837a9c3587db764ea8dd90d48421d9409b0e158 Signed-off-by: Ram Chandrasekar --- arch/arm64/configs/vendor/lahaina_QGKI.config | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/configs/vendor/lahaina_QGKI.config b/arch/arm64/configs/vendor/lahaina_QGKI.config index 685f084b240a..f39b02ddd457 100644 --- a/arch/arm64/configs/vendor/lahaina_QGKI.config +++ b/arch/arm64/configs/vendor/lahaina_QGKI.config @@ -121,6 +121,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