summaryrefslogtreecommitdiff
path: root/drivers/thermal/intel/int340x_thermal/processor_thermal_power_floor.c
diff options
context:
space:
mode:
authorSrinivas Pandruvada <srinivas.pandruvada@linux.intel.com>2023-10-09 22:05:35 +0300
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2023-10-12 22:09:48 +0300
commitb473d6a9d68f5bc27c7e35f5c98172d5f2206039 (patch)
tree0f97382405451355c61989e1fc0071ea84f5500a /drivers/thermal/intel/int340x_thermal/processor_thermal_power_floor.c
parent6ebc25d8b053a208786295bab58abbb66b39c318 (diff)
downloadlinux-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_power_floor.c')
-rw-r--r--drivers/thermal/intel/int340x_thermal/processor_thermal_power_floor.c126
1 files changed, 126 insertions, 0 deletions
diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_power_floor.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_power_floor.c
new file mode 100644
index 000000000000..a1a108407f0f
--- /dev/null
+++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_power_floor.c
@@ -0,0 +1,126 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Processor thermal device module for registering and processing
+ * power floor. When the hardware reduces the power to the minimum
+ * possible, the power floor is notified via an interrupt.
+ *
+ * Operation:
+ * When user space enables power floor reporting:
+ * - Use mailbox to:
+ * Enable processor thermal device interrupt
+ *
+ * - Current status of power floor is read from offset 0x5B18
+ * bit 39.
+ *
+ * Two interface functions are provided to call when there is a
+ * thermal device interrupt:
+ * - proc_thermal_power_floor_intr():
+ * Check if the interrupt is for change in power floor.
+ * Called from interrupt context.
+ *
+ * - proc_thermal_power_floor_intr_callback():
+ * Callback for interrupt processing in thread context. This involves
+ * sending notification to user space that there is a change in the
+ * power floor status.
+ *
+ * Copyright (c) 2023, Intel Corporation.
+ */
+
+#include <linux/pci.h>
+#include "processor_thermal_device.h"
+
+#define SOC_POWER_FLOOR_STATUS BIT(39)
+#define SOC_POWER_FLOOR_SHIFT 39
+
+#define SOC_POWER_FLOOR_INT_ENABLE_BIT 31
+#define SOC_POWER_FLOOR_INT_ACTIVE BIT(3)
+
+int proc_thermal_read_power_floor_status(struct proc_thermal_device *proc_priv)
+{
+ u64 status = 0;
+
+ status = readq(proc_priv->mmio_base + SOC_WT_RES_INT_STATUS_OFFSET);
+ return (status & SOC_POWER_FLOOR_STATUS) >> SOC_POWER_FLOOR_SHIFT;
+}
+EXPORT_SYMBOL_NS_GPL(proc_thermal_read_power_floor_status, INT340X_THERMAL);
+
+static bool enable_state;
+static DEFINE_MUTEX(pf_lock);
+
+int proc_thermal_power_floor_set_state(struct proc_thermal_device *proc_priv, bool enable)
+{
+ int ret = 0;
+
+ mutex_lock(&pf_lock);
+ if (enable_state == enable)
+ goto pf_unlock;
+
+ /*
+ * Time window parameter is not applicable to power floor interrupt configuration.
+ * Hence use -1 for time window.
+ */
+ ret = processor_thermal_mbox_interrupt_config(to_pci_dev(proc_priv->dev), enable,
+ SOC_POWER_FLOOR_INT_ENABLE_BIT, -1);
+ if (!ret)
+ enable_state = enable;
+
+pf_unlock:
+ mutex_unlock(&pf_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(proc_thermal_power_floor_set_state, INT340X_THERMAL);
+
+bool proc_thermal_power_floor_get_state(struct proc_thermal_device *proc_priv)
+{
+ return enable_state;
+}
+EXPORT_SYMBOL_NS_GPL(proc_thermal_power_floor_get_state, INT340X_THERMAL);
+
+/**
+ * proc_thermal_check_power_floor_intr() - Check power floor interrupt.
+ * @proc_priv: Processor thermal device instance.
+ *
+ * Callback to check if the interrupt for power floor is active.
+ *
+ * Context: Called from interrupt context.
+ *
+ * Return: true if power floor is active, false when not active.
+ */
+bool proc_thermal_check_power_floor_intr(struct proc_thermal_device *proc_priv)
+{
+ u64 int_status;
+
+ int_status = readq(proc_priv->mmio_base + SOC_WT_RES_INT_STATUS_OFFSET);
+ return !!(int_status & SOC_POWER_FLOOR_INT_ACTIVE);
+}
+EXPORT_SYMBOL_NS_GPL(proc_thermal_check_power_floor_intr, INT340X_THERMAL);
+
+/**
+ * proc_thermal_power_floor_intr_callback() - Process power floor notification
+ * @pdev: PCI device instance
+ * @proc_priv: Processor thermal device instance.
+ *
+ * Check if the power floor interrupt is active, if active send notification to
+ * user space for the attribute "power_limits", so that user can read the attribute
+ * and take action.
+ *
+ * Context: Called from interrupt thread context.
+ *
+ * Return: None.
+ */
+void proc_thermal_power_floor_intr_callback(struct pci_dev *pdev,
+ struct proc_thermal_device *proc_priv)
+{
+ u64 status;
+
+ status = readq(proc_priv->mmio_base + SOC_WT_RES_INT_STATUS_OFFSET);
+ if (!(status & SOC_POWER_FLOOR_INT_ACTIVE))
+ return;
+
+ sysfs_notify(&pdev->dev.kobj, "power_limits", "power_floor_status");
+}
+EXPORT_SYMBOL_NS_GPL(proc_thermal_power_floor_intr_callback, INT340X_THERMAL);
+
+MODULE_IMPORT_NS(INT340X_THERMAL);
+MODULE_LICENSE("GPL");