xref: /openbmc/linux/drivers/acpi/processor_thermal.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * processor_thermal.c - Passive cooling submodule of the ACPI processor driver
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  *  Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
61da177e4SLinus Torvalds  *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
71da177e4SLinus Torvalds  *  Copyright (C) 2004       Dominik Brodowski <linux@brodo.de>
81da177e4SLinus Torvalds  *  Copyright (C) 2004  Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com>
91da177e4SLinus Torvalds  *  			- Added processor hotplug support
101da177e4SLinus Torvalds  */
111da177e4SLinus Torvalds 
121da177e4SLinus Torvalds #include <linux/kernel.h>
131da177e4SLinus Torvalds #include <linux/module.h>
141da177e4SLinus Torvalds #include <linux/init.h>
151da177e4SLinus Torvalds #include <linux/cpufreq.h>
168b48463fSLv Zheng #include <linux/acpi.h>
171da177e4SLinus Torvalds #include <acpi/processor.h>
187c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
191da177e4SLinus Torvalds 
201da177e4SLinus Torvalds #ifdef CONFIG_CPU_FREQ
211da177e4SLinus Torvalds 
221da177e4SLinus Torvalds /* If a passive cooling situation is detected, primarily CPUfreq is used, as it
231da177e4SLinus Torvalds  * offers (in most cases) voltage scaling in addition to frequency scaling, and
241da177e4SLinus Torvalds  * thus a cubic (instead of linear) reduction of energy. Also, we allow for
251da177e4SLinus Torvalds  * _any_ cpufreq driver and not only the acpi-cpufreq driver.
261da177e4SLinus Torvalds  */
271da177e4SLinus Torvalds 
28d9460fd2SZhang Rui #define CPUFREQ_THERMAL_MIN_STEP 0
29d9460fd2SZhang Rui #define CPUFREQ_THERMAL_MAX_STEP 3
30d9460fd2SZhang Rui 
31c938ac21SMike Travis static DEFINE_PER_CPU(unsigned int, cpufreq_thermal_reduction_pctg);
321da177e4SLinus Torvalds 
332815ab92SAndi Kleen #define reduction_pctg(cpu) \
342815ab92SAndi Kleen 	per_cpu(cpufreq_thermal_reduction_pctg, phys_package_first_cpu(cpu))
352815ab92SAndi Kleen 
362815ab92SAndi Kleen /*
372815ab92SAndi Kleen  * Emulate "per package data" using per cpu data (which should really be
382815ab92SAndi Kleen  * provided elsewhere)
392815ab92SAndi Kleen  *
402815ab92SAndi Kleen  * Note we can lose a CPU on cpu hotunplug, in this case we forget the state
412815ab92SAndi Kleen  * temporarily. Fortunately that's not a big issue here (I hope)
422815ab92SAndi Kleen  */
phys_package_first_cpu(int cpu)432815ab92SAndi Kleen static int phys_package_first_cpu(int cpu)
442815ab92SAndi Kleen {
452815ab92SAndi Kleen 	int i;
462815ab92SAndi Kleen 	int id = topology_physical_package_id(cpu);
472815ab92SAndi Kleen 
482815ab92SAndi Kleen 	for_each_online_cpu(i)
492815ab92SAndi Kleen 		if (topology_physical_package_id(i) == id)
502815ab92SAndi Kleen 			return i;
512815ab92SAndi Kleen 	return 0;
522815ab92SAndi Kleen }
532815ab92SAndi Kleen 
cpu_has_cpufreq(unsigned int cpu)541da177e4SLinus Torvalds static int cpu_has_cpufreq(unsigned int cpu)
551da177e4SLinus Torvalds {
568120832dSManfred Spraul 	struct cpufreq_policy *policy;
578120832dSManfred Spraul 
588120832dSManfred Spraul 	if (!acpi_processor_cpufreq_init)
591da177e4SLinus Torvalds 		return 0;
608120832dSManfred Spraul 
618120832dSManfred Spraul 	policy = cpufreq_cpu_get(cpu);
628120832dSManfred Spraul 	if (policy) {
638120832dSManfred Spraul 		cpufreq_cpu_put(policy);
6475b245b3SThomas Renninger 		return 1;
651da177e4SLinus Torvalds 	}
668120832dSManfred Spraul 	return 0;
678120832dSManfred Spraul }
681da177e4SLinus Torvalds 
cpufreq_get_max_state(unsigned int cpu)69d9460fd2SZhang Rui static int cpufreq_get_max_state(unsigned int cpu)
70d9460fd2SZhang Rui {
71d9460fd2SZhang Rui 	if (!cpu_has_cpufreq(cpu))
72d9460fd2SZhang Rui 		return 0;
73d9460fd2SZhang Rui 
74d9460fd2SZhang Rui 	return CPUFREQ_THERMAL_MAX_STEP;
75d9460fd2SZhang Rui }
76d9460fd2SZhang Rui 
cpufreq_get_cur_state(unsigned int cpu)77d9460fd2SZhang Rui static int cpufreq_get_cur_state(unsigned int cpu)
78d9460fd2SZhang Rui {
79d9460fd2SZhang Rui 	if (!cpu_has_cpufreq(cpu))
80d9460fd2SZhang Rui 		return 0;
81d9460fd2SZhang Rui 
822815ab92SAndi Kleen 	return reduction_pctg(cpu);
83d9460fd2SZhang Rui }
84d9460fd2SZhang Rui 
cpufreq_set_cur_state(unsigned int cpu,int state)85d9460fd2SZhang Rui static int cpufreq_set_cur_state(unsigned int cpu, int state)
86d9460fd2SZhang Rui {
87d15ce412SViresh Kumar 	struct cpufreq_policy *policy;
88d15ce412SViresh Kumar 	struct acpi_processor *pr;
89d15ce412SViresh Kumar 	unsigned long max_freq;
90d15ce412SViresh Kumar 	int i, ret;
912815ab92SAndi Kleen 
92d9460fd2SZhang Rui 	if (!cpu_has_cpufreq(cpu))
93d9460fd2SZhang Rui 		return 0;
94d9460fd2SZhang Rui 
952815ab92SAndi Kleen 	reduction_pctg(cpu) = state;
962815ab92SAndi Kleen 
972815ab92SAndi Kleen 	/*
982815ab92SAndi Kleen 	 * Update all the CPUs in the same package because they all
992815ab92SAndi Kleen 	 * contribute to the temperature and often share the same
1002815ab92SAndi Kleen 	 * frequency.
1012815ab92SAndi Kleen 	 */
1022815ab92SAndi Kleen 	for_each_online_cpu(i) {
103d15ce412SViresh Kumar 		if (topology_physical_package_id(i) !=
1042815ab92SAndi Kleen 		    topology_physical_package_id(cpu))
105d15ce412SViresh Kumar 			continue;
106d15ce412SViresh Kumar 
107d15ce412SViresh Kumar 		pr = per_cpu(processors, i);
108d15ce412SViresh Kumar 
1093000ce3cSRafael J. Wysocki 		if (unlikely(!freq_qos_request_active(&pr->thermal_req)))
110d15ce412SViresh Kumar 			continue;
111d15ce412SViresh Kumar 
112d15ce412SViresh Kumar 		policy = cpufreq_cpu_get(i);
113d15ce412SViresh Kumar 		if (!policy)
114d15ce412SViresh Kumar 			return -EINVAL;
115d15ce412SViresh Kumar 
116d15ce412SViresh Kumar 		max_freq = (policy->cpuinfo.max_freq * (100 - reduction_pctg(i) * 20)) / 100;
117d15ce412SViresh Kumar 
118d15ce412SViresh Kumar 		cpufreq_cpu_put(policy);
119d15ce412SViresh Kumar 
1203000ce3cSRafael J. Wysocki 		ret = freq_qos_update_request(&pr->thermal_req, max_freq);
121d15ce412SViresh Kumar 		if (ret < 0) {
122d15ce412SViresh Kumar 			pr_warn("Failed to update thermal freq constraint: CPU%d (%d)\n",
123d15ce412SViresh Kumar 				pr->id, ret);
124d15ce412SViresh Kumar 		}
1252815ab92SAndi Kleen 	}
126d9460fd2SZhang Rui 	return 0;
127d9460fd2SZhang Rui }
128d9460fd2SZhang Rui 
acpi_thermal_cpufreq_init(struct cpufreq_policy * policy)1293000ce3cSRafael J. Wysocki void acpi_thermal_cpufreq_init(struct cpufreq_policy *policy)
1304be44fcdSLen Brown {
131a1bb46c3SRafael J. Wysocki 	unsigned int cpu;
132a1bb46c3SRafael J. Wysocki 
133a1bb46c3SRafael J. Wysocki 	for_each_cpu(cpu, policy->related_cpus) {
134d15ce412SViresh Kumar 		struct acpi_processor *pr = per_cpu(processors, cpu);
135d15ce412SViresh Kumar 		int ret;
1361da177e4SLinus Torvalds 
1372d8b39a6SRafael J. Wysocki 		if (!pr)
138a1bb46c3SRafael J. Wysocki 			continue;
1392d8b39a6SRafael J. Wysocki 
140a1bb46c3SRafael J. Wysocki 		ret = freq_qos_add_request(&policy->constraints,
141a1bb46c3SRafael J. Wysocki 					   &pr->thermal_req,
1423000ce3cSRafael J. Wysocki 					   FREQ_QOS_MAX, INT_MAX);
143*22c52fa5SRafael J. Wysocki 		if (ret < 0) {
144a1bb46c3SRafael J. Wysocki 			pr_err("Failed to add freq constraint for CPU%d (%d)\n",
145a1bb46c3SRafael J. Wysocki 			       cpu, ret);
146*22c52fa5SRafael J. Wysocki 			continue;
147*22c52fa5SRafael J. Wysocki 		}
148*22c52fa5SRafael J. Wysocki 
149*22c52fa5SRafael J. Wysocki 		thermal_cooling_device_update(pr->cdev);
150a1bb46c3SRafael J. Wysocki 	}
1511da177e4SLinus Torvalds }
1521da177e4SLinus Torvalds 
acpi_thermal_cpufreq_exit(struct cpufreq_policy * policy)1533000ce3cSRafael J. Wysocki void acpi_thermal_cpufreq_exit(struct cpufreq_policy *policy)
1544be44fcdSLen Brown {
155a1bb46c3SRafael J. Wysocki 	unsigned int cpu;
156a1bb46c3SRafael J. Wysocki 
157a1bb46c3SRafael J. Wysocki 	for_each_cpu(cpu, policy->related_cpus) {
15836527b9dSRiwen Lu 		struct acpi_processor *pr = per_cpu(processors, cpu);
1591da177e4SLinus Torvalds 
160*22c52fa5SRafael J. Wysocki 		if (!pr)
161*22c52fa5SRafael J. Wysocki 			continue;
162*22c52fa5SRafael J. Wysocki 
1633000ce3cSRafael J. Wysocki 		freq_qos_remove_request(&pr->thermal_req);
164*22c52fa5SRafael J. Wysocki 
165*22c52fa5SRafael J. Wysocki 		thermal_cooling_device_update(pr->cdev);
1661da177e4SLinus Torvalds 	}
167a1bb46c3SRafael J. Wysocki }
1681da177e4SLinus Torvalds #else				/* ! CONFIG_CPU_FREQ */
cpufreq_get_max_state(unsigned int cpu)169d9460fd2SZhang Rui static int cpufreq_get_max_state(unsigned int cpu)
170d9460fd2SZhang Rui {
171d9460fd2SZhang Rui 	return 0;
172d9460fd2SZhang Rui }
173d9460fd2SZhang Rui 
cpufreq_get_cur_state(unsigned int cpu)174d9460fd2SZhang Rui static int cpufreq_get_cur_state(unsigned int cpu)
175d9460fd2SZhang Rui {
176d9460fd2SZhang Rui 	return 0;
177d9460fd2SZhang Rui }
178d9460fd2SZhang Rui 
cpufreq_set_cur_state(unsigned int cpu,int state)179d9460fd2SZhang Rui static int cpufreq_set_cur_state(unsigned int cpu, int state)
180d9460fd2SZhang Rui {
181d9460fd2SZhang Rui 	return 0;
182d9460fd2SZhang Rui }
1831da177e4SLinus Torvalds 
1841da177e4SLinus Torvalds #endif
1851da177e4SLinus Torvalds 
1866dd7aca8SAl Stone /* thermal cooling device callbacks */
acpi_processor_max_state(struct acpi_processor * pr)187d9460fd2SZhang Rui static int acpi_processor_max_state(struct acpi_processor *pr)
188d9460fd2SZhang Rui {
189d9460fd2SZhang Rui 	int max_state = 0;
190d9460fd2SZhang Rui 
191d9460fd2SZhang Rui 	/*
192d9460fd2SZhang Rui 	 * There exists four states according to
1936dd7aca8SAl Stone 	 * cpufreq_thermal_reduction_pctg. 0, 1, 2, 3
194d9460fd2SZhang Rui 	 */
195d9460fd2SZhang Rui 	max_state += cpufreq_get_max_state(pr->id);
196d9460fd2SZhang Rui 	if (pr->flags.throttling)
197d9460fd2SZhang Rui 		max_state += (pr->throttling.state_count -1);
198d9460fd2SZhang Rui 
199d9460fd2SZhang Rui 	return max_state;
200d9460fd2SZhang Rui }
201d9460fd2SZhang Rui static int
processor_get_max_state(struct thermal_cooling_device * cdev,unsigned long * state)2026503e5dfSMatthew Garrett processor_get_max_state(struct thermal_cooling_device *cdev,
2036503e5dfSMatthew Garrett 			unsigned long *state)
204d9460fd2SZhang Rui {
205d9460fd2SZhang Rui 	struct acpi_device *device = cdev->devdata;
20699aa3638SColin Ian King 	struct acpi_processor *pr;
207d9460fd2SZhang Rui 
20899aa3638SColin Ian King 	if (!device)
20999aa3638SColin Ian King 		return -EINVAL;
21099aa3638SColin Ian King 
21199aa3638SColin Ian King 	pr = acpi_driver_data(device);
21299aa3638SColin Ian King 	if (!pr)
213d9460fd2SZhang Rui 		return -EINVAL;
214d9460fd2SZhang Rui 
2156503e5dfSMatthew Garrett 	*state = acpi_processor_max_state(pr);
2166503e5dfSMatthew Garrett 	return 0;
217d9460fd2SZhang Rui }
218d9460fd2SZhang Rui 
219d9460fd2SZhang Rui static int
processor_get_cur_state(struct thermal_cooling_device * cdev,unsigned long * cur_state)2206503e5dfSMatthew Garrett processor_get_cur_state(struct thermal_cooling_device *cdev,
2216503e5dfSMatthew Garrett 			unsigned long *cur_state)
222d9460fd2SZhang Rui {
223d9460fd2SZhang Rui 	struct acpi_device *device = cdev->devdata;
22499aa3638SColin Ian King 	struct acpi_processor *pr;
225d9460fd2SZhang Rui 
22699aa3638SColin Ian King 	if (!device)
22799aa3638SColin Ian King 		return -EINVAL;
22899aa3638SColin Ian King 
22999aa3638SColin Ian King 	pr = acpi_driver_data(device);
23099aa3638SColin Ian King 	if (!pr)
231d9460fd2SZhang Rui 		return -EINVAL;
232d9460fd2SZhang Rui 
2336503e5dfSMatthew Garrett 	*cur_state = cpufreq_get_cur_state(pr->id);
234d9460fd2SZhang Rui 	if (pr->flags.throttling)
2356503e5dfSMatthew Garrett 		*cur_state += pr->throttling.state;
2366503e5dfSMatthew Garrett 	return 0;
237d9460fd2SZhang Rui }
238d9460fd2SZhang Rui 
239d9460fd2SZhang Rui static int
processor_set_cur_state(struct thermal_cooling_device * cdev,unsigned long state)2406503e5dfSMatthew Garrett processor_set_cur_state(struct thermal_cooling_device *cdev,
2416503e5dfSMatthew Garrett 			unsigned long state)
242d9460fd2SZhang Rui {
243d9460fd2SZhang Rui 	struct acpi_device *device = cdev->devdata;
24499aa3638SColin Ian King 	struct acpi_processor *pr;
245d9460fd2SZhang Rui 	int result = 0;
246d9460fd2SZhang Rui 	int max_pstate;
247d9460fd2SZhang Rui 
24899aa3638SColin Ian King 	if (!device)
24999aa3638SColin Ian King 		return -EINVAL;
25099aa3638SColin Ian King 
25199aa3638SColin Ian King 	pr = acpi_driver_data(device);
25299aa3638SColin Ian King 	if (!pr)
253d9460fd2SZhang Rui 		return -EINVAL;
254d9460fd2SZhang Rui 
255d9460fd2SZhang Rui 	max_pstate = cpufreq_get_max_state(pr->id);
256d9460fd2SZhang Rui 
257d9460fd2SZhang Rui 	if (state > acpi_processor_max_state(pr))
258d9460fd2SZhang Rui 		return -EINVAL;
259d9460fd2SZhang Rui 
260d9460fd2SZhang Rui 	if (state <= max_pstate) {
261d9460fd2SZhang Rui 		if (pr->flags.throttling && pr->throttling.state)
2622a908002SFrans Pop 			result = acpi_processor_set_throttling(pr, 0, false);
263d9460fd2SZhang Rui 		cpufreq_set_cur_state(pr->id, state);
264d9460fd2SZhang Rui 	} else {
265d9460fd2SZhang Rui 		cpufreq_set_cur_state(pr->id, max_pstate);
266d9460fd2SZhang Rui 		result = acpi_processor_set_throttling(pr,
2672a908002SFrans Pop 				state - max_pstate, false);
268d9460fd2SZhang Rui 	}
269d9460fd2SZhang Rui 	return result;
270d9460fd2SZhang Rui }
271d9460fd2SZhang Rui 
2729c8b04beSVasiliy Kulikov const struct thermal_cooling_device_ops processor_cooling_ops = {
273d9460fd2SZhang Rui 	.get_max_state = processor_get_max_state,
274d9460fd2SZhang Rui 	.get_cur_state = processor_get_cur_state,
275d9460fd2SZhang Rui 	.set_cur_state = processor_set_cur_state,
276d9460fd2SZhang Rui };
2777fdc74daSRiwen Lu 
acpi_processor_thermal_init(struct acpi_processor * pr,struct acpi_device * device)2787fdc74daSRiwen Lu int acpi_processor_thermal_init(struct acpi_processor *pr,
2797fdc74daSRiwen Lu 				struct acpi_device *device)
2807fdc74daSRiwen Lu {
2817fdc74daSRiwen Lu 	int result = 0;
2827fdc74daSRiwen Lu 
2837fdc74daSRiwen Lu 	pr->cdev = thermal_cooling_device_register("Processor", device,
2847fdc74daSRiwen Lu 						   &processor_cooling_ops);
2857fdc74daSRiwen Lu 	if (IS_ERR(pr->cdev)) {
2867fdc74daSRiwen Lu 		result = PTR_ERR(pr->cdev);
2877fdc74daSRiwen Lu 		return result;
2887fdc74daSRiwen Lu 	}
2897fdc74daSRiwen Lu 
2907fdc74daSRiwen Lu 	dev_dbg(&device->dev, "registered as cooling_device%d\n",
2917fdc74daSRiwen Lu 		pr->cdev->id);
2927fdc74daSRiwen Lu 
2937fdc74daSRiwen Lu 	result = sysfs_create_link(&device->dev.kobj,
2947fdc74daSRiwen Lu 				   &pr->cdev->device.kobj,
2957fdc74daSRiwen Lu 				   "thermal_cooling");
2967fdc74daSRiwen Lu 	if (result) {
2977fdc74daSRiwen Lu 		dev_err(&device->dev,
2987fdc74daSRiwen Lu 			"Failed to create sysfs link 'thermal_cooling'\n");
2997fdc74daSRiwen Lu 		goto err_thermal_unregister;
3007fdc74daSRiwen Lu 	}
3017fdc74daSRiwen Lu 
3027fdc74daSRiwen Lu 	result = sysfs_create_link(&pr->cdev->device.kobj,
3037fdc74daSRiwen Lu 				   &device->dev.kobj,
3047fdc74daSRiwen Lu 				   "device");
3057fdc74daSRiwen Lu 	if (result) {
3067fdc74daSRiwen Lu 		dev_err(&pr->cdev->device,
3077fdc74daSRiwen Lu 			"Failed to create sysfs link 'device'\n");
3087fdc74daSRiwen Lu 		goto err_remove_sysfs_thermal;
3097fdc74daSRiwen Lu 	}
3107fdc74daSRiwen Lu 
3117fdc74daSRiwen Lu 	return 0;
3127fdc74daSRiwen Lu 
3137fdc74daSRiwen Lu err_remove_sysfs_thermal:
3147fdc74daSRiwen Lu 	sysfs_remove_link(&device->dev.kobj, "thermal_cooling");
3157fdc74daSRiwen Lu err_thermal_unregister:
3167fdc74daSRiwen Lu 	thermal_cooling_device_unregister(pr->cdev);
3177fdc74daSRiwen Lu 
3187fdc74daSRiwen Lu 	return result;
3197fdc74daSRiwen Lu }
3207fdc74daSRiwen Lu 
acpi_processor_thermal_exit(struct acpi_processor * pr,struct acpi_device * device)3217fdc74daSRiwen Lu void acpi_processor_thermal_exit(struct acpi_processor *pr,
3227fdc74daSRiwen Lu 				 struct acpi_device *device)
3237fdc74daSRiwen Lu {
3247fdc74daSRiwen Lu 	if (pr->cdev) {
3257fdc74daSRiwen Lu 		sysfs_remove_link(&device->dev.kobj, "thermal_cooling");
3267fdc74daSRiwen Lu 		sysfs_remove_link(&pr->cdev->device.kobj, "device");
3277fdc74daSRiwen Lu 		thermal_cooling_device_unregister(pr->cdev);
3287fdc74daSRiwen Lu 		pr->cdev = NULL;
3297fdc74daSRiwen Lu 	}
3307fdc74daSRiwen Lu }
331