Loading drivers/thermal/cpu_cooling.c +3 −13 Original line number Diff line number Diff line Loading @@ -26,7 +26,6 @@ #include <linux/thermal.h> #include <linux/cpufreq.h> #include <linux/err.h> #include <linux/idr.h> #include <linux/pm_opp.h> #include <linux/slab.h> #include <linux/cpu.h> Loading Loading @@ -113,7 +112,6 @@ struct cpufreq_cooling_device { struct cpu_cooling_ops *plat_ops; }; static DEFINE_IDA(cpufreq_ida); static DEFINE_MUTEX(cooling_list_lock); static LIST_HEAD(cpufreq_cdev_list); Loading Loading @@ -760,12 +758,7 @@ __cpufreq_cooling_register(struct device_node *np, goto free_idle_time; } ret = ida_simple_get(&cpufreq_ida, 0, 0, GFP_KERNEL); if (ret < 0) { cdev = ERR_PTR(ret); goto free_table; } cpufreq_cdev->id = ret; cpufreq_cdev->id = policy->cpu; snprintf(dev_name, sizeof(dev_name), "thermal-cpufreq-%d", cpufreq_cdev->id); Loading @@ -786,7 +779,7 @@ __cpufreq_cooling_register(struct device_node *np, ret = update_freq_table(cpufreq_cdev, capacitance); if (ret) { cdev = ERR_PTR(ret); goto remove_ida; goto free_table; } cooling_ops = &cpufreq_power_cooling_ops; Loading @@ -799,7 +792,7 @@ __cpufreq_cooling_register(struct device_node *np, cdev = thermal_of_cooling_device_register(np, dev_name, cpufreq_cdev, cooling_ops); if (IS_ERR(cdev)) goto remove_ida; goto free_table; cpufreq_cdev->clipped_freq = cpufreq_cdev->freq_table[0].frequency; cpufreq_cdev->floor_freq = Loading @@ -819,8 +812,6 @@ __cpufreq_cooling_register(struct device_node *np, return cdev; remove_ida: ida_simple_remove(&cpufreq_ida, cpufreq_cdev->id); free_table: kfree(cpufreq_cdev->freq_table); free_idle_time: Loading Loading @@ -969,7 +960,6 @@ void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev) } thermal_cooling_device_unregister(cpufreq_cdev->cdev); ida_simple_remove(&cpufreq_ida, cpufreq_cdev->id); kfree(cpufreq_cdev->idle_time); kfree(cpufreq_cdev->freq_table); kfree(cpufreq_cdev); Loading drivers/thermal/qcom/Kconfig +11 −0 Original line number Diff line number Diff line Loading @@ -101,3 +101,14 @@ config REGULATOR_COOLING_DEVICE voltage. If you want this support, you should say Y here. config QTI_CPU_ISOLATE_COOLING_DEVICE bool "QTI CPU Isolate cooling devices" depends on THERMAL_OF help This enables the QTI CPU Isolation cooling devices. These cooling devices will be used by QTI chipset to isolate a CPU from being scheduled and hence will let the CPU to power collapse. Isolating a CPU will be used when the CPU frequency mitigation is not good enough to achieve the necessary cooling. drivers/thermal/qcom/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -9,3 +9,4 @@ obj-$(CONFIG_QTI_QMI_COOLING_DEVICE) += thermal_mitigation_device_service_v01.o obj-$(CONFIG_QTI_THERMAL_LIMITS_DCVS) += msm_lmh_dcvs.o lmh_dbg.o obj-$(CONFIG_QTI_AOP_REG_COOLING_DEVICE) += regulator_aop_cdev.o obj-$(CONFIG_REGULATOR_COOLING_DEVICE) += regulator_cdev.o obj-$(CONFIG_QTI_CPU_ISOLATE_COOLING_DEVICE) += cpu_isolate.o drivers/thermal/qcom/cpu_isolate.c 0 → 100644 +314 −0 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2019, 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> #include <linux/suspend.h> #define CPU_ISOLATE_LEVEL 1 struct cpu_isolate_cdev { struct list_head node; int cpu_id; bool cpu_isolate_state; struct thermal_cooling_device *cdev; struct device_node *np; struct work_struct reg_work; }; static DEFINE_MUTEX(cpu_isolate_lock); static LIST_HEAD(cpu_isolate_cdev_list); static atomic_t in_suspend; static struct cpumask cpus_pending_online; static struct cpumask cpus_isolated_by_thermal; static int cpu_isolate_pm_notify(struct notifier_block *nb, unsigned long mode, void *_unused) { struct cpu_isolate_cdev *cpu_isolate_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(&cpu_isolate_lock); list_for_each_entry(cpu_isolate_cdev, &cpu_isolate_cdev_list, node) { if (cpu_isolate_cdev->cpu_id == -1) continue; if (cpu_isolate_cdev->cpu_isolate_state) { cpu = cpu_isolate_cdev->cpu_id; 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(&cpu_isolate_lock); atomic_set(&in_suspend, 0); break; default: break; } return 0; } static struct notifier_block cpu_isolate_pm_nb = { .notifier_call = cpu_isolate_pm_notify, }; static int cpu_isolate_hp_offline(unsigned int offline_cpu) { struct cpu_isolate_cdev *cpu_isolate_cdev; mutex_lock(&cpu_isolate_lock); list_for_each_entry(cpu_isolate_cdev, &cpu_isolate_cdev_list, node) { if (offline_cpu != cpu_isolate_cdev->cpu_id) continue; if (!cpu_isolate_cdev->cdev) break; if ((cpu_isolate_cdev->cpu_isolate_state) && (cpumask_test_and_clear_cpu(offline_cpu, &cpus_isolated_by_thermal))) sched_unisolate_cpu_unlocked(offline_cpu); break; } mutex_unlock(&cpu_isolate_lock); return 0; } static int cpu_isolate_hp_online(unsigned int online_cpu) { struct cpu_isolate_cdev *cpu_isolate_cdev; int ret = 0; if (atomic_read(&in_suspend)) return 0; mutex_lock(&cpu_isolate_lock); list_for_each_entry(cpu_isolate_cdev, &cpu_isolate_cdev_list, node) { if (online_cpu != cpu_isolate_cdev->cpu_id) continue; if (cpu_isolate_cdev->cdev) { if (cpu_isolate_cdev->cpu_isolate_state) { cpumask_set_cpu(online_cpu, &cpus_pending_online); ret = NOTIFY_BAD; } } else { queue_work(system_highpri_wq, &cpu_isolate_cdev->reg_work); } break; } mutex_unlock(&cpu_isolate_lock); return ret; } /** * cpu_isolate_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 isolation * current cooling state. * * Return: 0 on success, an error code otherwise. */ static int cpu_isolate_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state) { struct cpu_isolate_cdev *cpu_isolate_cdev = cdev->devdata; struct device *cpu_dev; int ret = 0; int cpu = 0; if (cpu_isolate_cdev->cpu_id == -1) return -ENODEV; /* Request state should be less than max_level */ if (state > CPU_ISOLATE_LEVEL) state = CPU_ISOLATE_LEVEL; state = !!state; /* Check if the old cooling action is same as new cooling action */ if (cpu_isolate_cdev->cpu_isolate_state == state) return 0; mutex_lock(&cpu_isolate_lock); cpu = cpu_isolate_cdev->cpu_id; cpu_isolate_cdev->cpu_isolate_state = state; if (state == CPU_ISOLATE_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); } } else { if (cpumask_test_and_clear_cpu(cpu, &cpus_pending_online)) { cpu_dev = get_cpu_device(cpu); mutex_unlock(&cpu_isolate_lock); ret = device_online(cpu_dev); if (ret) pr_err("CPU:%d online error:%d\n", cpu, ret); return ret; } else if (cpumask_test_and_clear_cpu(cpu, &cpus_isolated_by_thermal)) { sched_unisolate_cpu(cpu); } } mutex_unlock(&cpu_isolate_lock); return 0; } /** * cpu_isolate_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 isolation * current cooling state. * * Return: 0 on success, an error code otherwise. */ static int cpu_isolate_get_cur_state(struct thermal_cooling_device *cdev, unsigned long *state) { struct cpu_isolate_cdev *cpu_isolate_cdev = cdev->devdata; *state = (cpu_isolate_cdev->cpu_isolate_state) ? CPU_ISOLATE_LEVEL : 0; return 0; } /** * cpu_isolate_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 * isolation max cooling state. * * Return: 0 on success, an error code otherwise. */ static int cpu_isolate_get_max_state(struct thermal_cooling_device *cdev, unsigned long *state) { *state = CPU_ISOLATE_LEVEL; return 0; } static struct thermal_cooling_device_ops cpu_isolate_cooling_ops = { .get_max_state = cpu_isolate_get_max_state, .get_cur_state = cpu_isolate_get_cur_state, .set_cur_state = cpu_isolate_set_cur_state, }; static void cpu_isolate_register_cdev(struct work_struct *work) { struct cpu_isolate_cdev *cpu_isolate_cdev = container_of(work, struct cpu_isolate_cdev, reg_work); char cdev_name[THERMAL_NAME_LENGTH] = ""; int ret = 0; snprintf(cdev_name, THERMAL_NAME_LENGTH, "cpu-isolate%d", cpu_isolate_cdev->cpu_id); cpu_isolate_cdev->cdev = thermal_of_cooling_device_register( cpu_isolate_cdev->np, cdev_name, cpu_isolate_cdev, &cpu_isolate_cooling_ops); if (IS_ERR(cpu_isolate_cdev->cdev)) { ret = PTR_ERR(cpu_isolate_cdev->cdev); pr_err("Cooling register failed for %s, ret:%ld\n", cdev_name, ret); cpu_isolate_cdev->cdev = NULL; return; } pr_debug("Cooling device [%s] registered.\n", cdev_name); } static int cpu_isolate_probe(struct platform_device *pdev) { int ret = 0, cpu = 0; struct device_node *dev_phandle, *subsys_np; struct device *cpu_dev; struct cpu_isolate_cdev *cpu_isolate_cdev = NULL; struct device_node *np = pdev->dev.of_node; INIT_LIST_HEAD(&cpu_isolate_cdev_list); for_each_available_child_of_node(np, subsys_np) { cpu_isolate_cdev = devm_kzalloc(&pdev->dev, sizeof(*cpu_isolate_cdev), GFP_KERNEL); if (!cpu_isolate_cdev) return -ENOMEM; cpu_isolate_cdev->cpu_id = -1; cpu_isolate_cdev->cpu_isolate_state = false; cpu_isolate_cdev->cdev = NULL; cpu_isolate_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_isolate_cdev->cpu_id = cpu; break; } } INIT_WORK(&cpu_isolate_cdev->reg_work, cpu_isolate_register_cdev); list_add(&cpu_isolate_cdev->node, &cpu_isolate_cdev_list); } atomic_set(&in_suspend, 0); ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "cpu-isolate/cdev:online", cpu_isolate_hp_online, cpu_isolate_hp_offline); if (ret < 0) return ret; register_pm_notifier(&cpu_isolate_pm_nb); ret = 0; return ret; } static const struct of_device_id cpu_isolate_match[] = { { .compatible = "qcom,cpu-isolate", }, {}, }; static struct platform_driver cpu_isolate_driver = { .probe = cpu_isolate_probe, .driver = { .name = KBUILD_MODNAME, .of_match_table = cpu_isolate_match, }, }; builtin_platform_driver(cpu_isolate_driver); Loading
drivers/thermal/cpu_cooling.c +3 −13 Original line number Diff line number Diff line Loading @@ -26,7 +26,6 @@ #include <linux/thermal.h> #include <linux/cpufreq.h> #include <linux/err.h> #include <linux/idr.h> #include <linux/pm_opp.h> #include <linux/slab.h> #include <linux/cpu.h> Loading Loading @@ -113,7 +112,6 @@ struct cpufreq_cooling_device { struct cpu_cooling_ops *plat_ops; }; static DEFINE_IDA(cpufreq_ida); static DEFINE_MUTEX(cooling_list_lock); static LIST_HEAD(cpufreq_cdev_list); Loading Loading @@ -760,12 +758,7 @@ __cpufreq_cooling_register(struct device_node *np, goto free_idle_time; } ret = ida_simple_get(&cpufreq_ida, 0, 0, GFP_KERNEL); if (ret < 0) { cdev = ERR_PTR(ret); goto free_table; } cpufreq_cdev->id = ret; cpufreq_cdev->id = policy->cpu; snprintf(dev_name, sizeof(dev_name), "thermal-cpufreq-%d", cpufreq_cdev->id); Loading @@ -786,7 +779,7 @@ __cpufreq_cooling_register(struct device_node *np, ret = update_freq_table(cpufreq_cdev, capacitance); if (ret) { cdev = ERR_PTR(ret); goto remove_ida; goto free_table; } cooling_ops = &cpufreq_power_cooling_ops; Loading @@ -799,7 +792,7 @@ __cpufreq_cooling_register(struct device_node *np, cdev = thermal_of_cooling_device_register(np, dev_name, cpufreq_cdev, cooling_ops); if (IS_ERR(cdev)) goto remove_ida; goto free_table; cpufreq_cdev->clipped_freq = cpufreq_cdev->freq_table[0].frequency; cpufreq_cdev->floor_freq = Loading @@ -819,8 +812,6 @@ __cpufreq_cooling_register(struct device_node *np, return cdev; remove_ida: ida_simple_remove(&cpufreq_ida, cpufreq_cdev->id); free_table: kfree(cpufreq_cdev->freq_table); free_idle_time: Loading Loading @@ -969,7 +960,6 @@ void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev) } thermal_cooling_device_unregister(cpufreq_cdev->cdev); ida_simple_remove(&cpufreq_ida, cpufreq_cdev->id); kfree(cpufreq_cdev->idle_time); kfree(cpufreq_cdev->freq_table); kfree(cpufreq_cdev); Loading
drivers/thermal/qcom/Kconfig +11 −0 Original line number Diff line number Diff line Loading @@ -101,3 +101,14 @@ config REGULATOR_COOLING_DEVICE voltage. If you want this support, you should say Y here. config QTI_CPU_ISOLATE_COOLING_DEVICE bool "QTI CPU Isolate cooling devices" depends on THERMAL_OF help This enables the QTI CPU Isolation cooling devices. These cooling devices will be used by QTI chipset to isolate a CPU from being scheduled and hence will let the CPU to power collapse. Isolating a CPU will be used when the CPU frequency mitigation is not good enough to achieve the necessary cooling.
drivers/thermal/qcom/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -9,3 +9,4 @@ obj-$(CONFIG_QTI_QMI_COOLING_DEVICE) += thermal_mitigation_device_service_v01.o obj-$(CONFIG_QTI_THERMAL_LIMITS_DCVS) += msm_lmh_dcvs.o lmh_dbg.o obj-$(CONFIG_QTI_AOP_REG_COOLING_DEVICE) += regulator_aop_cdev.o obj-$(CONFIG_REGULATOR_COOLING_DEVICE) += regulator_cdev.o obj-$(CONFIG_QTI_CPU_ISOLATE_COOLING_DEVICE) += cpu_isolate.o
drivers/thermal/qcom/cpu_isolate.c 0 → 100644 +314 −0 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2019, 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> #include <linux/suspend.h> #define CPU_ISOLATE_LEVEL 1 struct cpu_isolate_cdev { struct list_head node; int cpu_id; bool cpu_isolate_state; struct thermal_cooling_device *cdev; struct device_node *np; struct work_struct reg_work; }; static DEFINE_MUTEX(cpu_isolate_lock); static LIST_HEAD(cpu_isolate_cdev_list); static atomic_t in_suspend; static struct cpumask cpus_pending_online; static struct cpumask cpus_isolated_by_thermal; static int cpu_isolate_pm_notify(struct notifier_block *nb, unsigned long mode, void *_unused) { struct cpu_isolate_cdev *cpu_isolate_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(&cpu_isolate_lock); list_for_each_entry(cpu_isolate_cdev, &cpu_isolate_cdev_list, node) { if (cpu_isolate_cdev->cpu_id == -1) continue; if (cpu_isolate_cdev->cpu_isolate_state) { cpu = cpu_isolate_cdev->cpu_id; 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(&cpu_isolate_lock); atomic_set(&in_suspend, 0); break; default: break; } return 0; } static struct notifier_block cpu_isolate_pm_nb = { .notifier_call = cpu_isolate_pm_notify, }; static int cpu_isolate_hp_offline(unsigned int offline_cpu) { struct cpu_isolate_cdev *cpu_isolate_cdev; mutex_lock(&cpu_isolate_lock); list_for_each_entry(cpu_isolate_cdev, &cpu_isolate_cdev_list, node) { if (offline_cpu != cpu_isolate_cdev->cpu_id) continue; if (!cpu_isolate_cdev->cdev) break; if ((cpu_isolate_cdev->cpu_isolate_state) && (cpumask_test_and_clear_cpu(offline_cpu, &cpus_isolated_by_thermal))) sched_unisolate_cpu_unlocked(offline_cpu); break; } mutex_unlock(&cpu_isolate_lock); return 0; } static int cpu_isolate_hp_online(unsigned int online_cpu) { struct cpu_isolate_cdev *cpu_isolate_cdev; int ret = 0; if (atomic_read(&in_suspend)) return 0; mutex_lock(&cpu_isolate_lock); list_for_each_entry(cpu_isolate_cdev, &cpu_isolate_cdev_list, node) { if (online_cpu != cpu_isolate_cdev->cpu_id) continue; if (cpu_isolate_cdev->cdev) { if (cpu_isolate_cdev->cpu_isolate_state) { cpumask_set_cpu(online_cpu, &cpus_pending_online); ret = NOTIFY_BAD; } } else { queue_work(system_highpri_wq, &cpu_isolate_cdev->reg_work); } break; } mutex_unlock(&cpu_isolate_lock); return ret; } /** * cpu_isolate_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 isolation * current cooling state. * * Return: 0 on success, an error code otherwise. */ static int cpu_isolate_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state) { struct cpu_isolate_cdev *cpu_isolate_cdev = cdev->devdata; struct device *cpu_dev; int ret = 0; int cpu = 0; if (cpu_isolate_cdev->cpu_id == -1) return -ENODEV; /* Request state should be less than max_level */ if (state > CPU_ISOLATE_LEVEL) state = CPU_ISOLATE_LEVEL; state = !!state; /* Check if the old cooling action is same as new cooling action */ if (cpu_isolate_cdev->cpu_isolate_state == state) return 0; mutex_lock(&cpu_isolate_lock); cpu = cpu_isolate_cdev->cpu_id; cpu_isolate_cdev->cpu_isolate_state = state; if (state == CPU_ISOLATE_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); } } else { if (cpumask_test_and_clear_cpu(cpu, &cpus_pending_online)) { cpu_dev = get_cpu_device(cpu); mutex_unlock(&cpu_isolate_lock); ret = device_online(cpu_dev); if (ret) pr_err("CPU:%d online error:%d\n", cpu, ret); return ret; } else if (cpumask_test_and_clear_cpu(cpu, &cpus_isolated_by_thermal)) { sched_unisolate_cpu(cpu); } } mutex_unlock(&cpu_isolate_lock); return 0; } /** * cpu_isolate_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 isolation * current cooling state. * * Return: 0 on success, an error code otherwise. */ static int cpu_isolate_get_cur_state(struct thermal_cooling_device *cdev, unsigned long *state) { struct cpu_isolate_cdev *cpu_isolate_cdev = cdev->devdata; *state = (cpu_isolate_cdev->cpu_isolate_state) ? CPU_ISOLATE_LEVEL : 0; return 0; } /** * cpu_isolate_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 * isolation max cooling state. * * Return: 0 on success, an error code otherwise. */ static int cpu_isolate_get_max_state(struct thermal_cooling_device *cdev, unsigned long *state) { *state = CPU_ISOLATE_LEVEL; return 0; } static struct thermal_cooling_device_ops cpu_isolate_cooling_ops = { .get_max_state = cpu_isolate_get_max_state, .get_cur_state = cpu_isolate_get_cur_state, .set_cur_state = cpu_isolate_set_cur_state, }; static void cpu_isolate_register_cdev(struct work_struct *work) { struct cpu_isolate_cdev *cpu_isolate_cdev = container_of(work, struct cpu_isolate_cdev, reg_work); char cdev_name[THERMAL_NAME_LENGTH] = ""; int ret = 0; snprintf(cdev_name, THERMAL_NAME_LENGTH, "cpu-isolate%d", cpu_isolate_cdev->cpu_id); cpu_isolate_cdev->cdev = thermal_of_cooling_device_register( cpu_isolate_cdev->np, cdev_name, cpu_isolate_cdev, &cpu_isolate_cooling_ops); if (IS_ERR(cpu_isolate_cdev->cdev)) { ret = PTR_ERR(cpu_isolate_cdev->cdev); pr_err("Cooling register failed for %s, ret:%ld\n", cdev_name, ret); cpu_isolate_cdev->cdev = NULL; return; } pr_debug("Cooling device [%s] registered.\n", cdev_name); } static int cpu_isolate_probe(struct platform_device *pdev) { int ret = 0, cpu = 0; struct device_node *dev_phandle, *subsys_np; struct device *cpu_dev; struct cpu_isolate_cdev *cpu_isolate_cdev = NULL; struct device_node *np = pdev->dev.of_node; INIT_LIST_HEAD(&cpu_isolate_cdev_list); for_each_available_child_of_node(np, subsys_np) { cpu_isolate_cdev = devm_kzalloc(&pdev->dev, sizeof(*cpu_isolate_cdev), GFP_KERNEL); if (!cpu_isolate_cdev) return -ENOMEM; cpu_isolate_cdev->cpu_id = -1; cpu_isolate_cdev->cpu_isolate_state = false; cpu_isolate_cdev->cdev = NULL; cpu_isolate_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_isolate_cdev->cpu_id = cpu; break; } } INIT_WORK(&cpu_isolate_cdev->reg_work, cpu_isolate_register_cdev); list_add(&cpu_isolate_cdev->node, &cpu_isolate_cdev_list); } atomic_set(&in_suspend, 0); ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "cpu-isolate/cdev:online", cpu_isolate_hp_online, cpu_isolate_hp_offline); if (ret < 0) return ret; register_pm_notifier(&cpu_isolate_pm_nb); ret = 0; return ret; } static const struct of_device_id cpu_isolate_match[] = { { .compatible = "qcom,cpu-isolate", }, {}, }; static struct platform_driver cpu_isolate_driver = { .probe = cpu_isolate_probe, .driver = { .name = KBUILD_MODNAME, .of_match_table = cpu_isolate_match, }, }; builtin_platform_driver(cpu_isolate_driver);