mirror of
https://github.com/torvalds/linux.git
synced 2026-05-28 00:53:34 +02:00
Merge branch 'thermal-intel'
Merge an update of the Intel int340x thermal driver adding Platform Temperature Control (PTC) support to it (Srinivas Pandruvada). * thermal-intel: thermal: int340x: processor_thermal: Platform temperature control documentation thermal: intel: int340x: Enable platform temperature control thermal: intel: int340x: Add platform temperature control interface
This commit is contained in:
commit
2a8aadb108
|
|
@ -191,6 +191,27 @@ ABI.
|
|||
User space can specify any one of the available workload type using
|
||||
this interface.
|
||||
|
||||
:file:`/sys/bus/pci/devices/0000\:00\:04.0/ptc_0_control`
|
||||
:file:`/sys/bus/pci/devices/0000\:00\:04.0/ptc_1_control`
|
||||
:file:`/sys/bus/pci/devices/0000\:00\:04.0/ptc_2_control`
|
||||
|
||||
All these controls needs admin privilege to update.
|
||||
|
||||
``enable`` (RW)
|
||||
1 for enable, 0 for disable. Shows the current enable status of
|
||||
platform temperature control feature. User space can enable/disable
|
||||
hardware controls.
|
||||
|
||||
``temperature_target`` (RW)
|
||||
Update a new temperature target in milli degree celsius for hardware to
|
||||
use for the temperature control.
|
||||
|
||||
Given that this is platform temperature control, it is expected that a
|
||||
single user-level manager owns and manages the controls. If multiple
|
||||
user-level software applications attempt to write different targets, it
|
||||
can lead to unexpected behavior.
|
||||
|
||||
|
||||
DPTF Processor thermal RFIM interface
|
||||
--------------------------------------------
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_device_pci_legacy.o
|
|||
obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_device_pci.o
|
||||
obj-$(CONFIG_PROC_THERMAL_MMIO_RAPL) += processor_thermal_rapl.o
|
||||
obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_rfim.o
|
||||
obj-$(CONFIG_INT340X_THERMAL) += platform_temperature_control.o
|
||||
obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_mbox.o
|
||||
obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_wt_req.o
|
||||
obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_wt_hint.o
|
||||
|
|
|
|||
|
|
@ -0,0 +1,243 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* processor thermal device platform temperature controls
|
||||
* Copyright (c) 2025, Intel Corporation.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Platform temperature controls hardware interface
|
||||
*
|
||||
* The hardware control interface is via MMIO offsets in the processor
|
||||
* thermal device MMIO space. There are three instances of MMIO registers.
|
||||
* All registers are 64 bit wide with RW access.
|
||||
*
|
||||
* Name: PLATFORM_TEMPERATURE_CONTROL
|
||||
* Offsets: 0x5B20, 0x5B28, 0x5B30
|
||||
*
|
||||
* Bits Description
|
||||
* 7:0 TARGET_TEMP : Target temperature limit to which the control
|
||||
* mechanism is regulating. Units: 0.5C.
|
||||
* 8:8 ENABLE: Read current enable status of the feature or enable
|
||||
* feature.
|
||||
* 11:9 GAIN: Sets the aggressiveness of control loop from 0 to 7
|
||||
* 7 graceful, favors performance at the expense of temperature
|
||||
* overshoots
|
||||
* 0 aggressive, favors tight regulation over performance
|
||||
* 12:12 TEMPERATURE_OVERRIDE_EN
|
||||
* When set, hardware will use TEMPERATURE_OVERRIDE values instead
|
||||
* of reading from corresponding sensor.
|
||||
* 15:13 RESERVED
|
||||
* 23:16 MIN_PERFORMANCE_LEVEL: Minimum Performance level below which the
|
||||
* there will be no throttling. 0 - all levels of throttling allowed
|
||||
* including survivability actions. 255 - no throttling allowed.
|
||||
* 31:24 TEMPERATURE_OVERRIDE: Allows SW to override the input temperature.
|
||||
* hardware will use this value instead of the sensor temperature.
|
||||
* Units: 0.5C.
|
||||
* 63:32 RESERVED
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include "processor_thermal_device.h"
|
||||
|
||||
struct mmio_reg {
|
||||
int bits;
|
||||
u16 mask;
|
||||
u16 shift;
|
||||
u16 units;
|
||||
};
|
||||
|
||||
#define MAX_ATTR_GROUP_NAME_LEN 32
|
||||
#define PTC_MAX_ATTRS 3
|
||||
|
||||
struct ptc_data {
|
||||
u32 offset;
|
||||
struct attribute_group ptc_attr_group;
|
||||
struct attribute *ptc_attrs[PTC_MAX_ATTRS];
|
||||
struct device_attribute temperature_target_attr;
|
||||
struct device_attribute enable_attr;
|
||||
char group_name[MAX_ATTR_GROUP_NAME_LEN];
|
||||
};
|
||||
|
||||
static const struct mmio_reg ptc_mmio_regs[] = {
|
||||
{ 8, 0xFF, 0, 500}, /* temperature_target, units 0.5C*/
|
||||
{ 1, 0x01, 8, 0}, /* enable */
|
||||
{ 3, 0x7, 9, 0}, /* gain */
|
||||
{ 8, 0xFF, 16, 0}, /* min_performance_level */
|
||||
{ 1, 0x1, 12, 0}, /* temperature_override_enable */
|
||||
{ 8, 0xFF, 24, 500}, /* temperature_override, units 0.5C */
|
||||
};
|
||||
|
||||
#define PTC_MAX_INSTANCES 3
|
||||
|
||||
/* Unique offset for each PTC instance */
|
||||
static u32 ptc_offsets[PTC_MAX_INSTANCES] = {0x5B20, 0x5B28, 0x5B30};
|
||||
|
||||
/* These will represent sysfs attribute names */
|
||||
static const char * const ptc_strings[] = {
|
||||
"temperature_target",
|
||||
"enable",
|
||||
NULL
|
||||
};
|
||||
|
||||
/* Lock to protect concurrent read/write and read-modify-write */
|
||||
static DEFINE_MUTEX(ptc_lock);
|
||||
|
||||
static ssize_t ptc_mmio_show(struct ptc_data *data, struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(dev);
|
||||
struct proc_thermal_device *proc_priv;
|
||||
const struct mmio_reg *mmio_regs;
|
||||
int ret, units;
|
||||
u64 reg_val;
|
||||
|
||||
proc_priv = pci_get_drvdata(pdev);
|
||||
mmio_regs = ptc_mmio_regs;
|
||||
ret = match_string(ptc_strings, -1, attr->attr.name);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
units = mmio_regs[ret].units;
|
||||
|
||||
guard(mutex)(&ptc_lock);
|
||||
|
||||
reg_val = readq((void __iomem *) (proc_priv->mmio_base + data->offset));
|
||||
ret = (reg_val >> mmio_regs[ret].shift) & mmio_regs[ret].mask;
|
||||
if (units)
|
||||
ret *= units;
|
||||
|
||||
return sysfs_emit(buf, "%d\n", ret);
|
||||
}
|
||||
|
||||
#define PTC_SHOW(suffix)\
|
||||
static ssize_t suffix##_show(struct device *dev,\
|
||||
struct device_attribute *attr,\
|
||||
char *buf)\
|
||||
{\
|
||||
struct ptc_data *data = container_of(attr, struct ptc_data, suffix##_attr);\
|
||||
return ptc_mmio_show(data, dev, attr, buf);\
|
||||
}
|
||||
|
||||
static void ptc_mmio_write(struct pci_dev *pdev, u32 offset, int index, u32 value)
|
||||
{
|
||||
struct proc_thermal_device *proc_priv;
|
||||
u64 mask, reg_val;
|
||||
|
||||
proc_priv = pci_get_drvdata(pdev);
|
||||
|
||||
mask = GENMASK_ULL(ptc_mmio_regs[index].shift + ptc_mmio_regs[index].bits - 1,
|
||||
ptc_mmio_regs[index].shift);
|
||||
|
||||
guard(mutex)(&ptc_lock);
|
||||
|
||||
reg_val = readq((void __iomem *) (proc_priv->mmio_base + offset));
|
||||
reg_val &= ~mask;
|
||||
reg_val |= (value << ptc_mmio_regs[index].shift);
|
||||
writeq(reg_val, (void __iomem *) (proc_priv->mmio_base + offset));
|
||||
}
|
||||
|
||||
static int ptc_store(struct ptc_data *data, struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(dev);
|
||||
unsigned int input;
|
||||
int ret;
|
||||
|
||||
ret = kstrtouint(buf, 10, &input);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = match_string(ptc_strings, -1, attr->attr.name);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (ptc_mmio_regs[ret].units)
|
||||
input /= ptc_mmio_regs[ret].units;
|
||||
|
||||
if (input > ptc_mmio_regs[ret].mask)
|
||||
return -EINVAL;
|
||||
|
||||
ptc_mmio_write(pdev, data->offset, ret, input);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
#define PTC_STORE(suffix)\
|
||||
static ssize_t suffix##_store(struct device *dev,\
|
||||
struct device_attribute *attr,\
|
||||
const char *buf, size_t count)\
|
||||
{\
|
||||
struct ptc_data *data = container_of(attr, struct ptc_data, suffix##_attr);\
|
||||
return ptc_store(data, dev, attr, buf, count);\
|
||||
}
|
||||
|
||||
PTC_SHOW(temperature_target);
|
||||
PTC_STORE(temperature_target);
|
||||
PTC_SHOW(enable);
|
||||
PTC_STORE(enable);
|
||||
|
||||
#define ptc_init_attribute(_name)\
|
||||
do {\
|
||||
sysfs_attr_init(&data->_name##_attr.attr);\
|
||||
data->_name##_attr.show = _name##_show;\
|
||||
data->_name##_attr.store = _name##_store;\
|
||||
data->_name##_attr.attr.name = #_name;\
|
||||
data->_name##_attr.attr.mode = 0644;\
|
||||
} while (0)
|
||||
|
||||
static int ptc_create_groups(struct pci_dev *pdev, int instance, struct ptc_data *data)
|
||||
{
|
||||
int ret, index = 0;
|
||||
|
||||
ptc_init_attribute(temperature_target);
|
||||
ptc_init_attribute(enable);
|
||||
|
||||
data->ptc_attrs[index++] = &data->temperature_target_attr.attr;
|
||||
data->ptc_attrs[index++] = &data->enable_attr.attr;
|
||||
data->ptc_attrs[index] = NULL;
|
||||
|
||||
snprintf(data->group_name, MAX_ATTR_GROUP_NAME_LEN,
|
||||
"ptc_%d_control", instance);
|
||||
data->ptc_attr_group.name = data->group_name;
|
||||
data->ptc_attr_group.attrs = data->ptc_attrs;
|
||||
|
||||
ret = sysfs_create_group(&pdev->dev.kobj, &data->ptc_attr_group);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct ptc_data ptc_instance[PTC_MAX_INSTANCES];
|
||||
|
||||
int proc_thermal_ptc_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv)
|
||||
{
|
||||
if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_PTC) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < PTC_MAX_INSTANCES; i++) {
|
||||
ptc_instance[i].offset = ptc_offsets[i];
|
||||
ptc_create_groups(pdev, i, &ptc_instance[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(proc_thermal_ptc_add);
|
||||
|
||||
void proc_thermal_ptc_remove(struct pci_dev *pdev)
|
||||
{
|
||||
struct proc_thermal_device *proc_priv = pci_get_drvdata(pdev);
|
||||
|
||||
if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_PTC) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < PTC_MAX_INSTANCES; i++)
|
||||
sysfs_remove_group(&pdev->dev.kobj, &ptc_instance[i].ptc_attr_group);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(proc_thermal_ptc_remove);
|
||||
|
||||
MODULE_IMPORT_NS("INT340X_THERMAL");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Processor Thermal PTC Interface");
|
||||
|
|
@ -399,13 +399,21 @@ int proc_thermal_mmio_add(struct pci_dev *pdev,
|
|||
}
|
||||
}
|
||||
|
||||
if (feature_mask & PROC_THERMAL_FEATURE_PTC) {
|
||||
ret = proc_thermal_ptc_add(pdev, proc_priv);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to add PTC MMIO interface\n");
|
||||
goto err_rem_rapl;
|
||||
}
|
||||
}
|
||||
|
||||
if (feature_mask & PROC_THERMAL_FEATURE_FIVR ||
|
||||
feature_mask & PROC_THERMAL_FEATURE_DVFS ||
|
||||
feature_mask & PROC_THERMAL_FEATURE_DLVR) {
|
||||
ret = proc_thermal_rfim_add(pdev, proc_priv);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to add RFIM interface\n");
|
||||
goto err_rem_rapl;
|
||||
goto err_rem_ptc;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -427,6 +435,8 @@ int proc_thermal_mmio_add(struct pci_dev *pdev,
|
|||
|
||||
err_rem_rfim:
|
||||
proc_thermal_rfim_remove(pdev);
|
||||
err_rem_ptc:
|
||||
proc_thermal_ptc_remove(pdev);
|
||||
err_rem_rapl:
|
||||
proc_thermal_rapl_remove();
|
||||
|
||||
|
|
@ -439,6 +449,9 @@ void proc_thermal_mmio_remove(struct pci_dev *pdev, struct proc_thermal_device *
|
|||
if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_RAPL)
|
||||
proc_thermal_rapl_remove();
|
||||
|
||||
if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_PTC)
|
||||
proc_thermal_ptc_remove(pdev);
|
||||
|
||||
if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_FIVR ||
|
||||
proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_DVFS ||
|
||||
proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_DLVR)
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@ struct rapl_mmio_regs {
|
|||
#define PROC_THERMAL_FEATURE_WT_HINT 0x20
|
||||
#define PROC_THERMAL_FEATURE_POWER_FLOOR 0x40
|
||||
#define PROC_THERMAL_FEATURE_MSI_SUPPORT 0x80
|
||||
#define PROC_THERMAL_FEATURE_PTC 0x100
|
||||
|
||||
#if IS_ENABLED(CONFIG_PROC_THERMAL_MMIO_RAPL)
|
||||
int proc_thermal_rapl_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv);
|
||||
|
|
@ -123,4 +124,6 @@ int proc_thermal_mmio_add(struct pci_dev *pdev,
|
|||
struct proc_thermal_device *proc_priv,
|
||||
kernel_ulong_t feature_mask);
|
||||
void proc_thermal_mmio_remove(struct pci_dev *pdev, struct proc_thermal_device *proc_priv);
|
||||
int proc_thermal_ptc_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv);
|
||||
void proc_thermal_ptc_remove(struct pci_dev *pdev);
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -486,7 +486,8 @@ static const struct pci_device_id proc_thermal_pci_ids[] = {
|
|||
PROC_THERMAL_FEATURE_FIVR | PROC_THERMAL_FEATURE_DVFS | PROC_THERMAL_FEATURE_WT_REQ) },
|
||||
{ PCI_DEVICE_DATA(INTEL, LNLM_THERMAL, PROC_THERMAL_FEATURE_MSI_SUPPORT |
|
||||
PROC_THERMAL_FEATURE_RAPL | PROC_THERMAL_FEATURE_DLVR | PROC_THERMAL_FEATURE_DVFS |
|
||||
PROC_THERMAL_FEATURE_WT_HINT | PROC_THERMAL_FEATURE_POWER_FLOOR) },
|
||||
PROC_THERMAL_FEATURE_WT_HINT | PROC_THERMAL_FEATURE_POWER_FLOOR |
|
||||
PROC_THERMAL_FEATURE_PTC) },
|
||||
{ PCI_DEVICE_DATA(INTEL, MTLP_THERMAL, PROC_THERMAL_FEATURE_RAPL |
|
||||
PROC_THERMAL_FEATURE_FIVR | PROC_THERMAL_FEATURE_DVFS | PROC_THERMAL_FEATURE_DLVR |
|
||||
PROC_THERMAL_FEATURE_WT_HINT | PROC_THERMAL_FEATURE_POWER_FLOOR) },
|
||||
|
|
@ -497,7 +498,7 @@ static const struct pci_device_id proc_thermal_pci_ids[] = {
|
|||
{ PCI_DEVICE_DATA(INTEL, PTL_THERMAL, PROC_THERMAL_FEATURE_RAPL |
|
||||
PROC_THERMAL_FEATURE_DLVR | PROC_THERMAL_FEATURE_DVFS |
|
||||
PROC_THERMAL_FEATURE_MSI_SUPPORT | PROC_THERMAL_FEATURE_WT_HINT |
|
||||
PROC_THERMAL_FEATURE_POWER_FLOOR) },
|
||||
PROC_THERMAL_FEATURE_POWER_FLOOR | PROC_THERMAL_FEATURE_PTC) },
|
||||
{ },
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user