diff options
author | Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com> | 2023-10-09 22:05:35 +0300 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2023-10-12 22:09:48 +0300 |
commit | b473d6a9d68f5bc27c7e35f5c98172d5f2206039 (patch) | |
tree | 0f97382405451355c61989e1fc0071ea84f5500a /drivers/thermal/intel/int340x_thermal/processor_thermal_device.c | |
parent | 6ebc25d8b053a208786295bab58abbb66b39c318 (diff) | |
download | linux-b473d6a9d68f5bc27c7e35f5c98172d5f2206039.tar.xz |
thermal: int340x: processor_thermal: Support power floor notifications
When the hardware reduces the power to the minimum possible, the power
floor is notified via an interrupt.
This can happen when user space requests a power limit via powercap RAPL
interface, which forces the system to enter to the lowest power. This
power floor indication can be used as a hint to resort to other methods
of reducing power than via RAPL power limit.
Before power floor status can be read or the firmware can trigger
notifications regarding it, it needs to be configured via a mailbox
command. The actual power floor status is read via bit 39 of MMIO
offset 0x5B18 of the processor thermal PCI device.
To show the current power floor status and get notification
on a sysfs attribute, add 2 new attributes to
/sys/bus/pci/devices/0000\:00\:04.0/power_limits/
power_floor_enable : This attribute is present when power floor
notifications are supported. This attribute allows to enable/disable
power floor notifications.
power_floor_status : This attribute is present when power floor
notifications are supported. When enabled via power_floor_enable, this
attribute shows the current power floor status.
The power floor implementation provides interfaces which are called
from the sysfs callbacks to enable/disable and read power floor
status. It also provides two additional interfaces to check if the
current processor thermal device interrupt is for power floor status
and to send notifications to user space.
Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
[ rjw: Changelog and documentation changes edits ]
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'drivers/thermal/intel/int340x_thermal/processor_thermal_device.c')
-rw-r--r-- | drivers/thermal/intel/int340x_thermal/processor_thermal_device.c | 68 |
1 files changed, 67 insertions, 1 deletions
diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c index 29ed7d0f7022..649f67fdf345 100644 --- a/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c +++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c @@ -26,6 +26,48 @@ static ssize_t power_limit_##index##_##suffix##_show(struct device *dev, \ (unsigned long)proc_dev->power_limits[index].suffix * 1000); \ } +static ssize_t power_floor_status_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct proc_thermal_device *proc_dev = dev_get_drvdata(dev); + int ret; + + ret = proc_thermal_read_power_floor_status(proc_dev); + + return sysfs_emit(buf, "%d\n", ret); +} + +static ssize_t power_floor_enable_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct proc_thermal_device *proc_dev = dev_get_drvdata(dev); + bool ret; + + ret = proc_thermal_power_floor_get_state(proc_dev); + + return sysfs_emit(buf, "%d\n", ret); +} + +static ssize_t power_floor_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct proc_thermal_device *proc_dev = dev_get_drvdata(dev); + u8 state; + int ret; + + if (kstrtou8(buf, 0, &state)) + return -EINVAL; + + ret = proc_thermal_power_floor_set_state(proc_dev, !!state); + if (ret) + return ret; + + return count; +} + POWER_LIMIT_SHOW(0, min_uw) POWER_LIMIT_SHOW(0, max_uw) POWER_LIMIT_SHOW(0, step_uw) @@ -50,6 +92,9 @@ static DEVICE_ATTR_RO(power_limit_1_step_uw); static DEVICE_ATTR_RO(power_limit_1_tmin_us); static DEVICE_ATTR_RO(power_limit_1_tmax_us); +static DEVICE_ATTR_RO(power_floor_status); +static DEVICE_ATTR_RW(power_floor_enable); + static struct attribute *power_limit_attrs[] = { &dev_attr_power_limit_0_min_uw.attr, &dev_attr_power_limit_1_min_uw.attr, @@ -61,12 +106,30 @@ static struct attribute *power_limit_attrs[] = { &dev_attr_power_limit_1_tmin_us.attr, &dev_attr_power_limit_0_tmax_us.attr, &dev_attr_power_limit_1_tmax_us.attr, + &dev_attr_power_floor_status.attr, + &dev_attr_power_floor_enable.attr, NULL }; +static umode_t power_limit_attr_visible(struct kobject *kobj, struct attribute *attr, int unused) +{ + struct device *dev = kobj_to_dev(kobj); + struct proc_thermal_device *proc_dev; + + if (attr != &dev_attr_power_floor_status.attr && attr != &dev_attr_power_floor_enable.attr) + return attr->mode; + + proc_dev = dev_get_drvdata(dev); + if (!proc_dev || !(proc_dev->mmio_feature_mask & PROC_THERMAL_FEATURE_POWER_FLOOR)) + return 0; + + return attr->mode; +} + static const struct attribute_group power_limit_attribute_group = { .attrs = power_limit_attrs, - .name = "power_limits" + .name = "power_limits", + .is_visible = power_limit_attr_visible, }; static ssize_t tcc_offset_degree_celsius_show(struct device *dev, @@ -380,6 +443,9 @@ void proc_thermal_mmio_remove(struct pci_dev *pdev, struct proc_thermal_device * proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_DVFS) proc_thermal_rfim_remove(pdev); + if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_POWER_FLOOR) + proc_thermal_power_floor_set_state(proc_priv, false); + if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_WT_REQ) proc_thermal_wt_req_remove(pdev); else if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_WT_HINT) |