1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * intel_tcc.c - Library for Intel TCC (thermal control circuitry) MSR access
4  * Copyright (c) 2022, Intel Corporation.
5  */
6 
7 #include <linux/errno.h>
8 #include <linux/intel_tcc.h>
9 #include <asm/msr.h>
10 
11 /**
12  * intel_tcc_get_tjmax() - returns the default TCC activation Temperature
13  * @cpu: cpu that the MSR should be run on, nagative value means any cpu.
14  *
15  * Get the TjMax value, which is the default thermal throttling or TCC
16  * activation temperature in degrees C.
17  *
18  * Return: Tjmax value in degrees C on success, negative error code otherwise.
19  */
20 int intel_tcc_get_tjmax(int cpu)
21 {
22 	u32 low, high;
23 	int val, err;
24 
25 	if (cpu < 0)
26 		err = rdmsr_safe(MSR_IA32_TEMPERATURE_TARGET, &low, &high);
27 	else
28 		err = rdmsr_safe_on_cpu(cpu, MSR_IA32_TEMPERATURE_TARGET, &low, &high);
29 	if (err)
30 		return err;
31 
32 	val = (low >> 16) & 0xff;
33 
34 	return val ? val : -ENODATA;
35 }
36 EXPORT_SYMBOL_NS_GPL(intel_tcc_get_tjmax, INTEL_TCC);
37 
38 /**
39  * intel_tcc_get_offset() - returns the TCC Offset value to Tjmax
40  * @cpu: cpu that the MSR should be run on, nagative value means any cpu.
41  *
42  * Get the TCC offset value to Tjmax. The effective thermal throttling or TCC
43  * activation temperature equals "Tjmax" - "TCC Offset", in degrees C.
44  *
45  * Return: Tcc offset value in degrees C on success, negative error code otherwise.
46  */
47 int intel_tcc_get_offset(int cpu)
48 {
49 	u32 low, high;
50 	int err;
51 
52 	if (cpu < 0)
53 		err = rdmsr_safe(MSR_IA32_TEMPERATURE_TARGET, &low, &high);
54 	else
55 		err = rdmsr_safe_on_cpu(cpu, MSR_IA32_TEMPERATURE_TARGET, &low, &high);
56 	if (err)
57 		return err;
58 
59 	return (low >> 24) & 0x3f;
60 }
61 EXPORT_SYMBOL_NS_GPL(intel_tcc_get_offset, INTEL_TCC);
62 
63 /**
64  * intel_tcc_set_offset() - set the TCC offset value to Tjmax
65  * @cpu: cpu that the MSR should be run on, nagative value means any cpu.
66  * @offset: TCC offset value in degree C
67  *
68  * Set the TCC Offset value to Tjmax. The effective thermal throttling or TCC
69  * activation temperature equals "Tjmax" - "TCC Offset", in degree C.
70  *
71  * Return: On success returns 0, negative error code otherwise.
72  */
73 
74 int intel_tcc_set_offset(int cpu, int offset)
75 {
76 	u32 low, high;
77 	int err;
78 
79 	if (offset < 0 || offset > 0x3f)
80 		return -EINVAL;
81 
82 	if (cpu < 0)
83 		err = rdmsr_safe(MSR_IA32_TEMPERATURE_TARGET, &low, &high);
84 	else
85 		err = rdmsr_safe_on_cpu(cpu, MSR_IA32_TEMPERATURE_TARGET, &low, &high);
86 	if (err)
87 		return err;
88 
89 	/* MSR Locked */
90 	if (low & BIT(31))
91 		return -EPERM;
92 
93 	low &= ~(0x3f << 24);
94 	low |= offset << 24;
95 
96 	if (cpu < 0)
97 		return wrmsr_safe(MSR_IA32_TEMPERATURE_TARGET, low, high);
98 	else
99 		return wrmsr_safe_on_cpu(cpu, MSR_IA32_TEMPERATURE_TARGET, low, high);
100 }
101 EXPORT_SYMBOL_NS_GPL(intel_tcc_set_offset, INTEL_TCC);
102 
103 /**
104  * intel_tcc_get_temp() - returns the current temperature
105  * @cpu: cpu that the MSR should be run on, nagative value means any cpu.
106  * @temp: pointer to the memory for saving cpu temperature.
107  * @pkg: true: Package Thermal Sensor. false: Core Thermal Sensor.
108  *
109  * Get the current temperature returned by the CPU core/package level
110  * thermal sensor, in degrees C.
111  *
112  * Return: 0 on success, negative error code otherwise.
113  */
114 int intel_tcc_get_temp(int cpu, int *temp, bool pkg)
115 {
116 	u32 low, high;
117 	u32 msr = pkg ? MSR_IA32_PACKAGE_THERM_STATUS : MSR_IA32_THERM_STATUS;
118 	int tjmax, err;
119 
120 	tjmax = intel_tcc_get_tjmax(cpu);
121 	if (tjmax < 0)
122 		return tjmax;
123 
124 	if (cpu < 0)
125 		err = rdmsr_safe(msr, &low, &high);
126 	else
127 		err = rdmsr_safe_on_cpu(cpu, msr, &low, &high);
128 	if (err)
129 		return err;
130 
131 	/* Temperature is beyond the valid thermal sensor range */
132 	if (!(low & BIT(31)))
133 		return -ENODATA;
134 
135 	*temp = tjmax - ((low >> 16) & 0x7f);
136 
137 	return 0;
138 }
139 EXPORT_SYMBOL_NS_GPL(intel_tcc_get_temp, INTEL_TCC);
140