| // SPDX-License-Identifier: GPL-2.0-only |
| /* |
| * intel_tcc.c - Library for Intel TCC (thermal control circuitry) MSR access |
| * Copyright (c) 2022, Intel Corporation. |
| */ |
| |
| #include <linux/errno.h> |
| #include <linux/intel_tcc.h> |
| #include <asm/msr.h> |
| |
| /** |
| * intel_tcc_get_tjmax() - returns the default TCC activation Temperature |
| * @cpu: cpu that the MSR should be run on, nagative value means any cpu. |
| * |
| * Get the TjMax value, which is the default thermal throttling or TCC |
| * activation temperature in degrees C. |
| * |
| * Return: Tjmax value in degrees C on success, negative error code otherwise. |
| */ |
| int intel_tcc_get_tjmax(int cpu) |
| { |
| u32 low, high; |
| int val, err; |
| |
| if (cpu < 0) |
| err = rdmsr_safe(MSR_IA32_TEMPERATURE_TARGET, &low, &high); |
| else |
| err = rdmsr_safe_on_cpu(cpu, MSR_IA32_TEMPERATURE_TARGET, &low, &high); |
| if (err) |
| return err; |
| |
| val = (low >> 16) & 0xff; |
| |
| return val ? val : -ENODATA; |
| } |
| EXPORT_SYMBOL_NS_GPL(intel_tcc_get_tjmax, INTEL_TCC); |
| |
| /** |
| * intel_tcc_get_offset() - returns the TCC Offset value to Tjmax |
| * @cpu: cpu that the MSR should be run on, nagative value means any cpu. |
| * |
| * Get the TCC offset value to Tjmax. The effective thermal throttling or TCC |
| * activation temperature equals "Tjmax" - "TCC Offset", in degrees C. |
| * |
| * Return: Tcc offset value in degrees C on success, negative error code otherwise. |
| */ |
| int intel_tcc_get_offset(int cpu) |
| { |
| u32 low, high; |
| int err; |
| |
| if (cpu < 0) |
| err = rdmsr_safe(MSR_IA32_TEMPERATURE_TARGET, &low, &high); |
| else |
| err = rdmsr_safe_on_cpu(cpu, MSR_IA32_TEMPERATURE_TARGET, &low, &high); |
| if (err) |
| return err; |
| |
| return (low >> 24) & 0x3f; |
| } |
| EXPORT_SYMBOL_NS_GPL(intel_tcc_get_offset, INTEL_TCC); |
| |
| /** |
| * intel_tcc_set_offset() - set the TCC offset value to Tjmax |
| * @cpu: cpu that the MSR should be run on, nagative value means any cpu. |
| * @offset: TCC offset value in degree C |
| * |
| * Set the TCC Offset value to Tjmax. The effective thermal throttling or TCC |
| * activation temperature equals "Tjmax" - "TCC Offset", in degree C. |
| * |
| * Return: On success returns 0, negative error code otherwise. |
| */ |
| |
| int intel_tcc_set_offset(int cpu, int offset) |
| { |
| u32 low, high; |
| int err; |
| |
| if (offset < 0 || offset > 0x3f) |
| return -EINVAL; |
| |
| if (cpu < 0) |
| err = rdmsr_safe(MSR_IA32_TEMPERATURE_TARGET, &low, &high); |
| else |
| err = rdmsr_safe_on_cpu(cpu, MSR_IA32_TEMPERATURE_TARGET, &low, &high); |
| if (err) |
| return err; |
| |
| /* MSR Locked */ |
| if (low & BIT(31)) |
| return -EPERM; |
| |
| low &= ~(0x3f << 24); |
| low |= offset << 24; |
| |
| if (cpu < 0) |
| return wrmsr_safe(MSR_IA32_TEMPERATURE_TARGET, low, high); |
| else |
| return wrmsr_safe_on_cpu(cpu, MSR_IA32_TEMPERATURE_TARGET, low, high); |
| } |
| EXPORT_SYMBOL_NS_GPL(intel_tcc_set_offset, INTEL_TCC); |
| |
| /** |
| * intel_tcc_get_temp() - returns the current temperature |
| * @cpu: cpu that the MSR should be run on, nagative value means any cpu. |
| * @temp: pointer to the memory for saving cpu temperature. |
| * @pkg: true: Package Thermal Sensor. false: Core Thermal Sensor. |
| * |
| * Get the current temperature returned by the CPU core/package level |
| * thermal sensor, in degrees C. |
| * |
| * Return: 0 on success, negative error code otherwise. |
| */ |
| int intel_tcc_get_temp(int cpu, int *temp, bool pkg) |
| { |
| u32 low, high; |
| u32 msr = pkg ? MSR_IA32_PACKAGE_THERM_STATUS : MSR_IA32_THERM_STATUS; |
| int tjmax, err; |
| |
| tjmax = intel_tcc_get_tjmax(cpu); |
| if (tjmax < 0) |
| return tjmax; |
| |
| if (cpu < 0) |
| err = rdmsr_safe(msr, &low, &high); |
| else |
| err = rdmsr_safe_on_cpu(cpu, msr, &low, &high); |
| if (err) |
| return err; |
| |
| /* Temperature is beyond the valid thermal sensor range */ |
| if (!(low & BIT(31))) |
| return -ENODATA; |
| |
| *temp = tjmax - ((low >> 16) & 0x7f); |
| |
| return 0; |
| } |
| EXPORT_SYMBOL_NS_GPL(intel_tcc_get_temp, INTEL_TCC); |