1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * cooling device driver that activates the processor throttling by |
4 | * programming the TCC Offset register. |
5 | * Copyright (c) 2021, Intel Corporation. |
6 | */ |
7 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
8 | |
9 | #include <linux/device.h> |
10 | #include <linux/intel_tcc.h> |
11 | #include <linux/module.h> |
12 | #include <linux/thermal.h> |
13 | #include <asm/cpu_device_id.h> |
14 | |
15 | #define TCC_PROGRAMMABLE BIT(30) |
16 | #define TCC_LOCKED BIT(31) |
17 | |
18 | static struct thermal_cooling_device *tcc_cdev; |
19 | |
20 | static int tcc_get_max_state(struct thermal_cooling_device *cdev, unsigned long |
21 | *state) |
22 | { |
23 | *state = 0x3f; |
24 | return 0; |
25 | } |
26 | |
27 | static int tcc_get_cur_state(struct thermal_cooling_device *cdev, unsigned long |
28 | *state) |
29 | { |
30 | int offset = intel_tcc_get_offset(cpu: -1); |
31 | |
32 | if (offset < 0) |
33 | return offset; |
34 | |
35 | *state = offset; |
36 | return 0; |
37 | } |
38 | |
39 | static int tcc_set_cur_state(struct thermal_cooling_device *cdev, unsigned long |
40 | state) |
41 | { |
42 | return intel_tcc_set_offset(cpu: -1, offset: (int)state); |
43 | } |
44 | |
45 | static const struct thermal_cooling_device_ops tcc_cooling_ops = { |
46 | .get_max_state = tcc_get_max_state, |
47 | .get_cur_state = tcc_get_cur_state, |
48 | .set_cur_state = tcc_set_cur_state, |
49 | }; |
50 | |
51 | static const struct x86_cpu_id tcc_ids[] __initconst = { |
52 | X86_MATCH_INTEL_FAM6_MODEL(SKYLAKE, NULL), |
53 | X86_MATCH_INTEL_FAM6_MODEL(SKYLAKE_L, NULL), |
54 | X86_MATCH_INTEL_FAM6_MODEL(KABYLAKE, NULL), |
55 | X86_MATCH_INTEL_FAM6_MODEL(KABYLAKE_L, NULL), |
56 | X86_MATCH_INTEL_FAM6_MODEL(ICELAKE, NULL), |
57 | X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_L, NULL), |
58 | X86_MATCH_INTEL_FAM6_MODEL(TIGERLAKE, NULL), |
59 | X86_MATCH_INTEL_FAM6_MODEL(TIGERLAKE_L, NULL), |
60 | X86_MATCH_INTEL_FAM6_MODEL(COMETLAKE, NULL), |
61 | X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE, NULL), |
62 | X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE_L, NULL), |
63 | X86_MATCH_INTEL_FAM6_MODEL(ATOM_GRACEMONT, NULL), |
64 | X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE, NULL), |
65 | X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE_P, NULL), |
66 | X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE_S, NULL), |
67 | {} |
68 | }; |
69 | |
70 | MODULE_DEVICE_TABLE(x86cpu, tcc_ids); |
71 | |
72 | static int __init tcc_cooling_init(void) |
73 | { |
74 | int ret; |
75 | u64 val; |
76 | const struct x86_cpu_id *id; |
77 | |
78 | int err; |
79 | |
80 | id = x86_match_cpu(match: tcc_ids); |
81 | if (!id) |
82 | return -ENODEV; |
83 | |
84 | err = rdmsrl_safe(MSR_PLATFORM_INFO, p: &val); |
85 | if (err) |
86 | return err; |
87 | |
88 | if (!(val & TCC_PROGRAMMABLE)) |
89 | return -ENODEV; |
90 | |
91 | err = rdmsrl_safe(MSR_IA32_TEMPERATURE_TARGET, p: &val); |
92 | if (err) |
93 | return err; |
94 | |
95 | if (val & TCC_LOCKED) { |
96 | pr_info("TCC Offset locked\n" ); |
97 | return -ENODEV; |
98 | } |
99 | |
100 | pr_info("Programmable TCC Offset detected\n" ); |
101 | |
102 | tcc_cdev = |
103 | thermal_cooling_device_register("TCC Offset" , NULL, |
104 | &tcc_cooling_ops); |
105 | if (IS_ERR(ptr: tcc_cdev)) { |
106 | ret = PTR_ERR(ptr: tcc_cdev); |
107 | return ret; |
108 | } |
109 | return 0; |
110 | } |
111 | |
112 | module_init(tcc_cooling_init) |
113 | |
114 | static void __exit tcc_cooling_exit(void) |
115 | { |
116 | thermal_cooling_device_unregister(tcc_cdev); |
117 | } |
118 | |
119 | module_exit(tcc_cooling_exit) |
120 | |
121 | MODULE_IMPORT_NS(INTEL_TCC); |
122 | MODULE_DESCRIPTION("TCC offset cooling device Driver" ); |
123 | MODULE_AUTHOR("Zhang Rui <rui.zhang@intel.com>" ); |
124 | MODULE_LICENSE("GPL v2" ); |
125 | |