aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/thermal
diff options
context:
space:
mode:
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>2023-10-05 13:24:25 +0200
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2023-10-05 13:24:25 +0200
commit2cbe1a333115b9d318a5066945a74f1d01e9ee19 (patch)
tree0b7e93b1f7a45821d34dcbb9adef6047587710c4 /drivers/thermal
parenta56cc0a8338523f709892696cc229527617c1316 (diff)
parent1ced5dce63d0a43eda3e81d2dd74e17f9c4a30b5 (diff)
downloadlinux-2cbe1a333115b9d318a5066945a74f1d01e9ee19.tar.gz
Merge earlier changes in Intel thermal drivers for v6.7.
Diffstat (limited to 'drivers/thermal')
-rw-r--r--drivers/thermal/intel/int340x_thermal/Makefile2
-rw-r--r--drivers/thermal/intel/int340x_thermal/processor_thermal_device.c17
-rw-r--r--drivers/thermal/intel/int340x_thermal/processor_thermal_device.h22
-rw-r--r--drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.c80
-rw-r--r--drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci_legacy.c3
-rw-r--r--drivers/thermal/intel/int340x_thermal/processor_thermal_mbox.c179
-rw-r--r--drivers/thermal/intel/int340x_thermal/processor_thermal_wt_hint.c258
-rw-r--r--drivers/thermal/intel/int340x_thermal/processor_thermal_wt_req.c136
8 files changed, 540 insertions, 157 deletions
diff --git a/drivers/thermal/intel/int340x_thermal/Makefile b/drivers/thermal/intel/int340x_thermal/Makefile
index 4e852ce4a5d5a..f33a3ad3bef3b 100644
--- a/drivers/thermal/intel/int340x_thermal/Makefile
+++ b/drivers/thermal/intel/int340x_thermal/Makefile
@@ -10,5 +10,7 @@ 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) += processor_thermal_mbox.o
+obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_wt_req.o
+obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_wt_hint.o
obj-$(CONFIG_INT3406_THERMAL) += int3406_thermal.o
obj-$(CONFIG_ACPI_THERMAL_REL) += acpi_thermal_rel.o
diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c
index 3ca0a2f5937f2..29ed7d0f70227 100644
--- a/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c
+++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c
@@ -346,12 +346,18 @@ int proc_thermal_mmio_add(struct pci_dev *pdev,
}
}
- if (feature_mask & PROC_THERMAL_FEATURE_MBOX) {
- ret = proc_thermal_mbox_add(pdev, proc_priv);
+ if (feature_mask & PROC_THERMAL_FEATURE_WT_REQ) {
+ ret = proc_thermal_wt_req_add(pdev, proc_priv);
if (ret) {
dev_err(&pdev->dev, "failed to add MBOX interface\n");
goto err_rem_rfim;
}
+ } else if (feature_mask & PROC_THERMAL_FEATURE_WT_HINT) {
+ ret = proc_thermal_wt_hint_add(pdev, proc_priv);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to add WT Hint\n");
+ goto err_rem_rfim;
+ }
}
return 0;
@@ -374,12 +380,15 @@ 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_MBOX)
- proc_thermal_mbox_remove(pdev);
+ 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)
+ proc_thermal_wt_hint_remove(pdev);
}
EXPORT_SYMBOL_GPL(proc_thermal_mmio_remove);
MODULE_IMPORT_NS(INTEL_TCC);
+MODULE_IMPORT_NS(INT340X_THERMAL);
MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
MODULE_DESCRIPTION("Processor Thermal Reporting Device Driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_device.h b/drivers/thermal/intel/int340x_thermal/processor_thermal_device.h
index 7acaa8f1b896f..dd025c8c2bac5 100644
--- a/drivers/thermal/intel/int340x_thermal/processor_thermal_device.h
+++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_device.h
@@ -10,6 +10,7 @@
#include <linux/intel_rapl.h>
#define PCI_DEVICE_ID_INTEL_ADL_THERMAL 0x461d
+#define PCI_DEVICE_ID_INTEL_ARL_S_THERMAL 0xAD03
#define PCI_DEVICE_ID_INTEL_BDW_THERMAL 0x1603
#define PCI_DEVICE_ID_INTEL_BSW_THERMAL 0x22DC
@@ -59,8 +60,9 @@ struct rapl_mmio_regs {
#define PROC_THERMAL_FEATURE_RAPL 0x01
#define PROC_THERMAL_FEATURE_FIVR 0x02
#define PROC_THERMAL_FEATURE_DVFS 0x04
-#define PROC_THERMAL_FEATURE_MBOX 0x08
+#define PROC_THERMAL_FEATURE_WT_REQ 0x08
#define PROC_THERMAL_FEATURE_DLVR 0x10
+#define PROC_THERMAL_FEATURE_WT_HINT 0x20
#if IS_ENABLED(CONFIG_PROC_THERMAL_MMIO_RAPL)
int proc_thermal_rapl_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv);
@@ -80,13 +82,27 @@ static void __maybe_unused proc_thermal_rapl_remove(void)
int proc_thermal_rfim_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv);
void proc_thermal_rfim_remove(struct pci_dev *pdev);
-int proc_thermal_mbox_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv);
-void proc_thermal_mbox_remove(struct pci_dev *pdev);
+int proc_thermal_wt_req_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv);
+void proc_thermal_wt_req_remove(struct pci_dev *pdev);
+
+#define MBOX_CMD_WORKLOAD_TYPE_READ 0x0E
+#define MBOX_CMD_WORKLOAD_TYPE_WRITE 0x0F
+
+#define MBOX_DATA_BIT_AC_DC 30
+#define MBOX_DATA_BIT_VALID 31
int processor_thermal_send_mbox_read_cmd(struct pci_dev *pdev, u16 id, u64 *resp);
int processor_thermal_send_mbox_write_cmd(struct pci_dev *pdev, u16 id, u32 data);
+int processor_thermal_mbox_interrupt_config(struct pci_dev *pdev, bool enable, int enable_bit,
+ int time_window);
int proc_thermal_add(struct device *dev, struct proc_thermal_device *priv);
void proc_thermal_remove(struct proc_thermal_device *proc_priv);
+
+int proc_thermal_wt_hint_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv);
+void proc_thermal_wt_hint_remove(struct pci_dev *pdev);
+void proc_thermal_wt_intr_callback(struct pci_dev *pdev, struct proc_thermal_device *proc_priv);
+bool proc_thermal_check_wt_intr(struct proc_thermal_device *proc_priv);
+
int proc_thermal_suspend(struct device *dev);
int proc_thermal_resume(struct device *dev);
int proc_thermal_mmio_add(struct pci_dev *pdev,
diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.c
index 0d1e980072704..3c5ced79ead06 100644
--- a/drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.c
+++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.c
@@ -15,6 +15,11 @@
#define DRV_NAME "proc_thermal_pci"
+static bool use_msi;
+module_param(use_msi, bool, 0644);
+MODULE_PARM_DESC(use_msi,
+ "Use PCI MSI based interrupts for processor thermal device.");
+
struct proc_thermal_pci {
struct pci_dev *pdev;
struct proc_thermal_device *proc_priv;
@@ -117,20 +122,44 @@ static void pkg_thermal_schedule_work(struct delayed_work *work)
schedule_delayed_work(work, ms);
}
+static irqreturn_t proc_thermal_irq_thread_handler(int irq, void *devid)
+{
+ struct proc_thermal_pci *pci_info = devid;
+
+ proc_thermal_wt_intr_callback(pci_info->pdev, pci_info->proc_priv);
+
+ return IRQ_HANDLED;
+}
+
static irqreturn_t proc_thermal_irq_handler(int irq, void *devid)
{
struct proc_thermal_pci *pci_info = devid;
+ struct proc_thermal_device *proc_priv;
+ int ret = IRQ_HANDLED;
u32 status;
+ proc_priv = pci_info->proc_priv;
+
+ if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_WT_HINT) {
+ if (proc_thermal_check_wt_intr(pci_info->proc_priv))
+ ret = IRQ_WAKE_THREAD;
+ }
+
+ /*
+ * Since now there are two sources of interrupts: one from thermal threshold
+ * and another from workload hint, add a check if there was really a threshold
+ * interrupt before scheduling work function for thermal threshold.
+ */
proc_thermal_mmio_read(pci_info, PROC_THERMAL_MMIO_INT_STATUS_0, &status);
+ if (status) {
+ /* Disable enable interrupt flag */
+ proc_thermal_mmio_write(pci_info, PROC_THERMAL_MMIO_INT_ENABLE_0, 0);
+ pkg_thermal_schedule_work(&pci_info->work);
+ }
- /* Disable enable interrupt flag */
- proc_thermal_mmio_write(pci_info, PROC_THERMAL_MMIO_INT_ENABLE_0, 0);
pci_write_config_byte(pci_info->pdev, 0xdc, 0x01);
- pkg_thermal_schedule_work(&pci_info->work);
-
- return IRQ_HANDLED;
+ return ret;
}
static int sys_get_curr_temp(struct thermal_zone_device *tzd, int *temp)
@@ -203,6 +232,7 @@ static int proc_thermal_pci_probe(struct pci_dev *pdev, const struct pci_device_
struct proc_thermal_device *proc_priv;
struct proc_thermal_pci *pci_info;
int irq_flag = 0, irq, ret;
+ bool msi_irq = false;
proc_priv = devm_kzalloc(&pdev->dev, sizeof(*proc_priv), GFP_KERNEL);
if (!proc_priv)
@@ -248,18 +278,23 @@ static int proc_thermal_pci_probe(struct pci_dev *pdev, const struct pci_device_
goto err_ret_mmio;
}
- /* request and enable interrupt */
- ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES);
- if (ret < 0) {
- dev_err(&pdev->dev, "Failed to allocate vectors!\n");
- goto err_ret_tzone;
- }
- if (!pdev->msi_enabled && !pdev->msix_enabled)
+ if (use_msi && (pdev->msi_enabled || pdev->msix_enabled)) {
+ /* request and enable interrupt */
+ ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to allocate vectors!\n");
+ goto err_ret_tzone;
+ }
+
+ irq = pci_irq_vector(pdev, 0);
+ msi_irq = true;
+ } else {
irq_flag = IRQF_SHARED;
+ irq = pdev->irq;
+ }
- irq = pci_irq_vector(pdev, 0);
ret = devm_request_threaded_irq(&pdev->dev, irq,
- proc_thermal_irq_handler, NULL,
+ proc_thermal_irq_handler, proc_thermal_irq_thread_handler,
irq_flag, KBUILD_MODNAME, pci_info);
if (ret) {
dev_err(&pdev->dev, "Request IRQ %d failed\n", pdev->irq);
@@ -273,7 +308,8 @@ static int proc_thermal_pci_probe(struct pci_dev *pdev, const struct pci_device_
return 0;
err_free_vectors:
- pci_free_irq_vectors(pdev);
+ if (msi_irq)
+ pci_free_irq_vectors(pdev);
err_ret_tzone:
thermal_zone_device_unregister(pci_info->tzone);
err_ret_mmio:
@@ -350,9 +386,15 @@ static SIMPLE_DEV_PM_OPS(proc_thermal_pci_pm, proc_thermal_pci_suspend,
proc_thermal_pci_resume);
static const struct pci_device_id proc_thermal_pci_ids[] = {
- { PCI_DEVICE_DATA(INTEL, ADL_THERMAL, PROC_THERMAL_FEATURE_RAPL | PROC_THERMAL_FEATURE_FIVR | PROC_THERMAL_FEATURE_DVFS | PROC_THERMAL_FEATURE_MBOX) },
- { PCI_DEVICE_DATA(INTEL, MTLP_THERMAL, PROC_THERMAL_FEATURE_RAPL | PROC_THERMAL_FEATURE_FIVR | PROC_THERMAL_FEATURE_DVFS | PROC_THERMAL_FEATURE_MBOX | PROC_THERMAL_FEATURE_DLVR) },
- { PCI_DEVICE_DATA(INTEL, RPL_THERMAL, PROC_THERMAL_FEATURE_RAPL | PROC_THERMAL_FEATURE_FIVR | PROC_THERMAL_FEATURE_DVFS | PROC_THERMAL_FEATURE_MBOX) },
+ { PCI_DEVICE_DATA(INTEL, ADL_THERMAL, PROC_THERMAL_FEATURE_RAPL |
+ PROC_THERMAL_FEATURE_FIVR | PROC_THERMAL_FEATURE_DVFS | PROC_THERMAL_FEATURE_WT_REQ) },
+ { 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) },
+ { PCI_DEVICE_DATA(INTEL, ARL_S_THERMAL, PROC_THERMAL_FEATURE_RAPL |
+ PROC_THERMAL_FEATURE_DVFS | PROC_THERMAL_FEATURE_DLVR | PROC_THERMAL_FEATURE_WT_HINT) },
+ { PCI_DEVICE_DATA(INTEL, RPL_THERMAL, PROC_THERMAL_FEATURE_RAPL |
+ PROC_THERMAL_FEATURE_FIVR | PROC_THERMAL_FEATURE_DVFS | PROC_THERMAL_FEATURE_WT_REQ) },
{ },
};
@@ -368,6 +410,8 @@ static struct pci_driver proc_thermal_pci_driver = {
module_pci_driver(proc_thermal_pci_driver);
+MODULE_IMPORT_NS(INT340X_THERMAL);
+
MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
MODULE_DESCRIPTION("Processor Thermal Reporting Device Driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci_legacy.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci_legacy.c
index 16fd9df5f36d0..ccfdd2f9d9737 100644
--- a/drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci_legacy.c
+++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci_legacy.c
@@ -137,7 +137,8 @@ static const struct pci_device_id proc_thermal_pci_ids[] = {
{ PCI_DEVICE_DATA(INTEL, ICL_THERMAL, PROC_THERMAL_FEATURE_RAPL) },
{ PCI_DEVICE_DATA(INTEL, JSL_THERMAL, 0) },
{ PCI_DEVICE_DATA(INTEL, SKL_THERMAL, PROC_THERMAL_FEATURE_RAPL) },
- { PCI_DEVICE_DATA(INTEL, TGL_THERMAL, PROC_THERMAL_FEATURE_RAPL | PROC_THERMAL_FEATURE_FIVR | PROC_THERMAL_FEATURE_MBOX) },
+ { PCI_DEVICE_DATA(INTEL, TGL_THERMAL, PROC_THERMAL_FEATURE_RAPL |
+ PROC_THERMAL_FEATURE_FIVR | PROC_THERMAL_FEATURE_WT_REQ) },
{ },
};
diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_mbox.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_mbox.c
index 0b89a4340ff4e..4d3bd32ff9eae 100644
--- a/drivers/thermal/intel/int340x_thermal/processor_thermal_mbox.c
+++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_mbox.c
@@ -10,18 +10,12 @@
#include <linux/io-64-nonatomic-lo-hi.h>
#include "processor_thermal_device.h"
-#define MBOX_CMD_WORKLOAD_TYPE_READ 0x0E
-#define MBOX_CMD_WORKLOAD_TYPE_WRITE 0x0F
-
#define MBOX_OFFSET_DATA 0x5810
#define MBOX_OFFSET_INTERFACE 0x5818
#define MBOX_BUSY_BIT 31
#define MBOX_RETRY_COUNT 100
-#define MBOX_DATA_BIT_VALID 31
-#define MBOX_DATA_BIT_AC_DC 30
-
static DEFINE_MUTEX(mbox_lock);
static int wait_for_mbox_ready(struct proc_thermal_device *proc_priv)
@@ -51,23 +45,16 @@ static int send_mbox_write_cmd(struct pci_dev *pdev, u16 id, u32 data)
int ret;
proc_priv = pci_get_drvdata(pdev);
-
- mutex_lock(&mbox_lock);
-
ret = wait_for_mbox_ready(proc_priv);
if (ret)
- goto unlock_mbox;
+ return ret;
writel(data, (proc_priv->mmio_base + MBOX_OFFSET_DATA));
/* Write command register */
reg_data = BIT_ULL(MBOX_BUSY_BIT) | id;
writel(reg_data, (proc_priv->mmio_base + MBOX_OFFSET_INTERFACE));
- ret = wait_for_mbox_ready(proc_priv);
-
-unlock_mbox:
- mutex_unlock(&mbox_lock);
- return ret;
+ return wait_for_mbox_ready(proc_priv);
}
static int send_mbox_read_cmd(struct pci_dev *pdev, u16 id, u64 *resp)
@@ -77,12 +64,9 @@ static int send_mbox_read_cmd(struct pci_dev *pdev, u16 id, u64 *resp)
int ret;
proc_priv = pci_get_drvdata(pdev);
-
- mutex_lock(&mbox_lock);
-
ret = wait_for_mbox_ready(proc_priv);
if (ret)
- goto unlock_mbox;
+ return ret;
/* Write command register */
reg_data = BIT_ULL(MBOX_BUSY_BIT) | id;
@@ -90,152 +74,85 @@ static int send_mbox_read_cmd(struct pci_dev *pdev, u16 id, u64 *resp)
ret = wait_for_mbox_ready(proc_priv);
if (ret)
- goto unlock_mbox;
+ return ret;
if (id == MBOX_CMD_WORKLOAD_TYPE_READ)
*resp = readl(proc_priv->mmio_base + MBOX_OFFSET_DATA);
else
*resp = readq(proc_priv->mmio_base + MBOX_OFFSET_DATA);
-unlock_mbox:
- mutex_unlock(&mbox_lock);
- return ret;
+ return 0;
}
int processor_thermal_send_mbox_read_cmd(struct pci_dev *pdev, u16 id, u64 *resp)
{
- return send_mbox_read_cmd(pdev, id, resp);
+ int ret;
+
+ mutex_lock(&mbox_lock);
+ ret = send_mbox_read_cmd(pdev, id, resp);
+ mutex_unlock(&mbox_lock);
+
+ return ret;
}
EXPORT_SYMBOL_NS_GPL(processor_thermal_send_mbox_read_cmd, INT340X_THERMAL);
int processor_thermal_send_mbox_write_cmd(struct pci_dev *pdev, u16 id, u32 data)
{
- return send_mbox_write_cmd(pdev, id, data);
-}
-EXPORT_SYMBOL_NS_GPL(processor_thermal_send_mbox_write_cmd, INT340X_THERMAL);
-
-/* List of workload types */
-static const char * const workload_types[] = {
- "none",
- "idle",
- "semi_active",
- "bursty",
- "sustained",
- "battery_life",
- NULL
-};
-
-static ssize_t workload_available_types_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- int i = 0;
- int ret = 0;
-
- while (workload_types[i] != NULL)
- ret += sprintf(&buf[ret], "%s ", workload_types[i++]);
+ int ret;
- ret += sprintf(&buf[ret], "\n");
+ mutex_lock(&mbox_lock);
+ ret = send_mbox_write_cmd(pdev, id, data);
+ mutex_unlock(&mbox_lock);
return ret;
}
+EXPORT_SYMBOL_NS_GPL(processor_thermal_send_mbox_write_cmd, INT340X_THERMAL);
-static DEVICE_ATTR_RO(workload_available_types);
-
-static ssize_t workload_type_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct pci_dev *pdev = to_pci_dev(dev);
- char str_preference[15];
- u32 data = 0;
- ssize_t ret;
-
- ret = sscanf(buf, "%14s", str_preference);
- if (ret != 1)
- return -EINVAL;
-
- ret = match_string(workload_types, -1, str_preference);
- if (ret < 0)
- return ret;
-
- ret &= 0xff;
-
- if (ret)
- data = BIT(MBOX_DATA_BIT_VALID) | BIT(MBOX_DATA_BIT_AC_DC);
-
- data |= ret;
-
- ret = send_mbox_write_cmd(pdev, MBOX_CMD_WORKLOAD_TYPE_WRITE, data);
- if (ret)
- return false;
-
- return count;
-}
+#define MBOX_CAMARILLO_RD_INTR_CONFIG 0x1E
+#define MBOX_CAMARILLO_WR_INTR_CONFIG 0x1F
+#define WLT_TW_MASK GENMASK_ULL(30, 24)
+#define SOC_PREDICTION_TW_SHIFT 24
-static ssize_t workload_type_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
+int processor_thermal_mbox_interrupt_config(struct pci_dev *pdev, bool enable,
+ int enable_bit, int time_window)
{
- struct pci_dev *pdev = to_pci_dev(dev);
- u64 cmd_resp;
+ u64 data;
int ret;
- ret = send_mbox_read_cmd(pdev, MBOX_CMD_WORKLOAD_TYPE_READ, &cmd_resp);
- if (ret)
- return false;
-
- cmd_resp &= 0xff;
-
- if (cmd_resp > ARRAY_SIZE(workload_types) - 1)
- return -EINVAL;
+ if (!pdev)
+ return -ENODEV;
- return sprintf(buf, "%s\n", workload_types[cmd_resp]);
-}
-
-static DEVICE_ATTR_RW(workload_type);
+ mutex_lock(&mbox_lock);
-static struct attribute *workload_req_attrs[] = {
- &dev_attr_workload_available_types.attr,
- &dev_attr_workload_type.attr,
- NULL
-};
+ /* Do read modify write for MBOX_CAMARILLO_RD_INTR_CONFIG */
-static const struct attribute_group workload_req_attribute_group = {
- .attrs = workload_req_attrs,
- .name = "workload_request"
-};
+ ret = send_mbox_read_cmd(pdev, MBOX_CAMARILLO_RD_INTR_CONFIG, &data);
+ if (ret) {
+ dev_err(&pdev->dev, "MBOX_CAMARILLO_RD_INTR_CONFIG failed\n");
+ goto unlock;
+ }
-static bool workload_req_created;
+ if (time_window >= 0) {
+ data &= ~WLT_TW_MASK;
-int proc_thermal_mbox_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv)
-{
- u64 cmd_resp;
- int ret;
+ /* Program notification delay */
+ data |= ((u64)time_window << SOC_PREDICTION_TW_SHIFT) & WLT_TW_MASK;
+ }
- /* Check if there is a mailbox support, if fails return success */
- ret = send_mbox_read_cmd(pdev, MBOX_CMD_WORKLOAD_TYPE_READ, &cmd_resp);
- if (ret)
- return 0;
+ if (enable)
+ data |= BIT(enable_bit);
+ else
+ data &= ~BIT(enable_bit);
- ret = sysfs_create_group(&pdev->dev.kobj, &workload_req_attribute_group);
+ ret = send_mbox_write_cmd(pdev, MBOX_CAMARILLO_WR_INTR_CONFIG, data);
if (ret)
- return ret;
+ dev_err(&pdev->dev, "MBOX_CAMARILLO_WR_INTR_CONFIG failed\n");
- workload_req_created = true;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(proc_thermal_mbox_add);
-
-void proc_thermal_mbox_remove(struct pci_dev *pdev)
-{
- if (workload_req_created)
- sysfs_remove_group(&pdev->dev.kobj, &workload_req_attribute_group);
-
- workload_req_created = false;
+unlock:
+ mutex_unlock(&mbox_lock);
+ return ret;
}
-EXPORT_SYMBOL_GPL(proc_thermal_mbox_remove);
+EXPORT_SYMBOL_NS_GPL(processor_thermal_mbox_interrupt_config, INT340X_THERMAL);
MODULE_LICENSE("GPL v2");
diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_wt_hint.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_wt_hint.c
new file mode 100644
index 0000000000000..fabd8a363abb1
--- /dev/null
+++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_wt_hint.c
@@ -0,0 +1,258 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * processor thermal device interface for reading workload type hints
+ * from the user space. The hints are provided by the firmware.
+ *
+ * Operation:
+ * When user space enables workload type prediction:
+ * - Use mailbox to:
+ * Configure notification delay
+ * Enable processor thermal device interrupt
+ *
+ * - The predicted workload type can be read from MMIO:
+ * Offset 0x5B18 shows if there was an interrupt
+ * active for change in workload type and also
+ * predicted workload type.
+ *
+ * Two interface functions are provided to call when there is a
+ * thermal device interrupt:
+ * - proc_thermal_check_wt_intr():
+ * Check if the interrupt is for change in workload type. Called from
+ * interrupt context.
+ *
+ * - proc_thermal_wt_intr_callback():
+ * Callback for interrupt processing in thread context. This involves
+ * sending notification to user space that there is a change in the
+ * workload type.
+ *
+ * Copyright (c) 2023, Intel Corporation.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/pci.h>
+#include "processor_thermal_device.h"
+
+#define SOC_WT_RES_INT_STATUS_OFFSET 0x5B18
+#define SOC_WT GENMASK_ULL(47, 40)
+
+#define SOC_WT_PREDICTION_INT_ENABLE_BIT 23
+
+#define SOC_WT_PREDICTION_INT_ACTIVE BIT(2)
+
+/*
+ * Closest possible to 1 Second is 1024 ms with programmed time delay
+ * of 0x0A.
+ */
+static u8 notify_delay = 0x0A;
+static u16 notify_delay_ms = 1024;
+
+static DEFINE_MUTEX(wt_lock);
+static u8 wt_enable;
+
+/* Show current predicted workload type index */
+static ssize_t workload_type_index_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct proc_thermal_device *proc_priv;
+ struct pci_dev *pdev = to_pci_dev(dev);
+ u64 status = 0;
+ int wt;
+
+ mutex_lock(&wt_lock);
+ if (!wt_enable) {
+ mutex_unlock(&wt_lock);
+ return -ENODATA;
+ }
+
+ proc_priv = pci_get_drvdata(pdev);
+
+ status = readq(proc_priv->mmio_base + SOC_WT_RES_INT_STATUS_OFFSET);
+
+ mutex_unlock(&wt_lock);
+
+ wt = FIELD_GET(SOC_WT, status);
+
+ return sysfs_emit(buf, "%d\n", wt);
+}
+
+static DEVICE_ATTR_RO(workload_type_index);
+
+static ssize_t workload_hint_enable_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return sysfs_emit(buf, "%d\n", wt_enable);
+}
+
+static ssize_t workload_hint_enable_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ u8 mode;
+ int ret;
+
+ if (kstrtou8(buf, 10, &mode) || mode > 1)
+ return -EINVAL;
+
+ mutex_lock(&wt_lock);
+
+ if (mode)
+ ret = processor_thermal_mbox_interrupt_config(pdev, true,
+ SOC_WT_PREDICTION_INT_ENABLE_BIT,
+ notify_delay);
+ else
+ ret = processor_thermal_mbox_interrupt_config(pdev, false,
+ SOC_WT_PREDICTION_INT_ENABLE_BIT, 0);
+
+ if (ret)
+ goto ret_enable_store;
+
+ ret = size;
+ wt_enable = mode;
+
+ret_enable_store:
+ mutex_unlock(&wt_lock);
+
+ return ret;
+}
+
+static DEVICE_ATTR_RW(workload_hint_enable);
+
+static ssize_t notification_delay_ms_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return sysfs_emit(buf, "%u\n", notify_delay_ms);
+}
+
+static ssize_t notification_delay_ms_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ u16 new_tw;
+ int ret;
+ u8 tm;
+
+ /*
+ * Time window register value:
+ * Formula: (1 + x/4) * power(2,y)
+ * x = 2 msbs, that is [30:29] y = 5 [28:24]
+ * in INTR_CONFIG register.
+ * The result will be in milli seconds.
+ * Here, just keep x = 0, and just change y.
+ * First round up the user value to power of 2 and
+ * then take log2, to get "y" value to program.
+ */
+ ret = kstrtou16(buf, 10, &new_tw);
+ if (ret)
+ return ret;
+
+ if (!new_tw)
+ return -EINVAL;
+
+ new_tw = roundup_pow_of_two(new_tw);
+ tm = ilog2(new_tw);
+ if (tm > 31)
+ return -EINVAL;
+
+ mutex_lock(&wt_lock);
+
+ /* If the workload hint was already enabled, then update with the new delay */
+ if (wt_enable)
+ ret = processor_thermal_mbox_interrupt_config(pdev, true,
+ SOC_WT_PREDICTION_INT_ENABLE_BIT,
+ tm);
+
+ if (!ret) {
+ ret = size;
+ notify_delay = tm;
+ notify_delay_ms = new_tw;
+ }
+
+ mutex_unlock(&wt_lock);
+
+ return ret;
+}
+
+static DEVICE_ATTR_RW(notification_delay_ms);
+
+static struct attribute *workload_hint_attrs[] = {
+ &dev_attr_workload_type_index.attr,
+ &dev_attr_workload_hint_enable.attr,
+ &dev_attr_notification_delay_ms.attr,
+ NULL
+};
+
+static const struct attribute_group workload_hint_attribute_group = {
+ .attrs = workload_hint_attrs,
+ .name = "workload_hint"
+};
+
+/*
+ * Callback to check if the interrupt for prediction is active.
+ * Caution: Called from the interrupt context.
+ */
+bool proc_thermal_check_wt_intr(struct proc_thermal_device *proc_priv)
+{
+ u64 int_status;
+
+ int_status = readq(proc_priv->mmio_base + SOC_WT_RES_INT_STATUS_OFFSET);
+ if (int_status & SOC_WT_PREDICTION_INT_ACTIVE)
+ return true;
+
+ return false;
+}
+EXPORT_SYMBOL_NS_GPL(proc_thermal_check_wt_intr, INT340X_THERMAL);
+
+/* Callback to notify user space */
+void proc_thermal_wt_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_WT_PREDICTION_INT_ACTIVE))
+ return;
+
+ writeq(status & ~SOC_WT_PREDICTION_INT_ACTIVE,
+ proc_priv->mmio_base + SOC_WT_RES_INT_STATUS_OFFSET);
+ sysfs_notify(&pdev->dev.kobj, "workload_hint", "workload_type_index");
+}
+EXPORT_SYMBOL_NS_GPL(proc_thermal_wt_intr_callback, INT340X_THERMAL);
+
+static bool workload_hint_created;
+
+int proc_thermal_wt_hint_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv)
+{
+ int ret;
+
+ ret = sysfs_create_group(&pdev->dev.kobj, &workload_hint_attribute_group);
+ if (ret)
+ return ret;
+
+ workload_hint_created = true;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(proc_thermal_wt_hint_add, INT340X_THERMAL);
+
+void proc_thermal_wt_hint_remove(struct pci_dev *pdev)
+{
+ mutex_lock(&wt_lock);
+ if (wt_enable)
+ processor_thermal_mbox_interrupt_config(pdev, false,
+ SOC_WT_PREDICTION_INT_ENABLE_BIT,
+ 0);
+ mutex_unlock(&wt_lock);
+
+ if (workload_hint_created)
+ sysfs_remove_group(&pdev->dev.kobj, &workload_hint_attribute_group);
+
+ workload_hint_created = false;
+}
+EXPORT_SYMBOL_NS_GPL(proc_thermal_wt_hint_remove, INT340X_THERMAL);
+
+MODULE_IMPORT_NS(INT340X_THERMAL);
+MODULE_LICENSE("GPL");
diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_wt_req.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_wt_req.c
new file mode 100644
index 0000000000000..711c4f761c9a4
--- /dev/null
+++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_wt_req.c
@@ -0,0 +1,136 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * processor thermal device for Workload type hints
+ * update from user space
+ *
+ * Copyright (c) 2020-2023, Intel Corporation.
+ */
+
+#include <linux/pci.h>
+#include "processor_thermal_device.h"
+
+/* List of workload types */
+static const char * const workload_types[] = {
+ "none",
+ "idle",
+ "semi_active",
+ "bursty",
+ "sustained",
+ "battery_life",
+ NULL
+};
+
+static ssize_t workload_available_types_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int i = 0;
+ int ret = 0;
+
+ while (workload_types[i] != NULL)
+ ret += sprintf(&buf[ret], "%s ", workload_types[i++]);
+
+ ret += sprintf(&buf[ret], "\n");
+
+ return ret;
+}
+
+static DEVICE_ATTR_RO(workload_available_types);
+
+static ssize_t workload_type_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ char str_preference[15];
+ u32 data = 0;
+ ssize_t ret;
+
+ ret = sscanf(buf, "%14s", str_preference);
+ if (ret != 1)
+ return -EINVAL;
+
+ ret = match_string(workload_types, -1, str_preference);
+ if (ret < 0)
+ return ret;
+
+ ret &= 0xff;
+
+ if (ret)
+ data = BIT(MBOX_DATA_BIT_VALID) | BIT(MBOX_DATA_BIT_AC_DC);
+
+ data |= ret;
+
+ ret = processor_thermal_send_mbox_write_cmd(pdev, MBOX_CMD_WORKLOAD_TYPE_WRITE, data);
+ if (ret)
+ return false;
+
+ return count;
+}
+
+static ssize_t workload_type_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ u64 cmd_resp;
+ int ret;
+
+ ret = processor_thermal_send_mbox_read_cmd(pdev, MBOX_CMD_WORKLOAD_TYPE_READ, &cmd_resp);
+ if (ret)
+ return false;
+
+ cmd_resp &= 0xff;
+
+ if (cmd_resp > ARRAY_SIZE(workload_types) - 1)
+ return -EINVAL;
+
+ return sprintf(buf, "%s\n", workload_types[cmd_resp]);
+}
+
+static DEVICE_ATTR_RW(workload_type);
+
+static struct attribute *workload_req_attrs[] = {
+ &dev_attr_workload_available_types.attr,
+ &dev_attr_workload_type.attr,
+ NULL
+};
+
+static const struct attribute_group workload_req_attribute_group = {
+ .attrs = workload_req_attrs,
+ .name = "workload_request"
+};
+
+static bool workload_req_created;
+
+int proc_thermal_wt_req_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv)
+{
+ u64 cmd_resp;
+ int ret;
+
+ /* Check if there is a mailbox support, if fails return success */
+ ret = processor_thermal_send_mbox_read_cmd(pdev, MBOX_CMD_WORKLOAD_TYPE_READ, &cmd_resp);
+ if (ret)
+ return 0;
+
+ ret = sysfs_create_group(&pdev->dev.kobj, &workload_req_attribute_group);
+ if (ret)
+ return ret;
+
+ workload_req_created = true;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(proc_thermal_wt_req_add);
+
+void proc_thermal_wt_req_remove(struct pci_dev *pdev)
+{
+ if (workload_req_created)
+ sysfs_remove_group(&pdev->dev.kobj, &workload_req_attribute_group);
+
+ workload_req_created = false;
+}
+EXPORT_SYMBOL_GPL(proc_thermal_wt_req_remove);
+
+MODULE_IMPORT_NS(INT340X_THERMAL);
+MODULE_LICENSE("GPL");