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