diff options
Diffstat (limited to 'drivers/thermal/intel/int340x_thermal/processor_thermal_mbox.c')
| -rw-r--r-- | drivers/thermal/intel/int340x_thermal/processor_thermal_mbox.c | 159 |
1 files changed, 159 insertions, 0 deletions
diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_mbox.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_mbox.c new file mode 100644 index 000000000000..b1d531ef440f --- /dev/null +++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_mbox.c @@ -0,0 +1,159 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * processor thermal device mailbox driver for Workload type hints + * Copyright (c) 2020, Intel Corporation. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/io-64-nonatomic-lo-hi.h> +#include "processor_thermal_device.h" + +#define MBOX_OFFSET_DATA 0x5810 +#define MBOX_OFFSET_INTERFACE 0x5818 + +#define MBOX_BUSY_BIT 31 +#define MBOX_RETRY_COUNT 100 + +static DEFINE_MUTEX(mbox_lock); + +static int wait_for_mbox_ready(struct proc_thermal_device *proc_priv) +{ + u32 retries, data; + int ret; + + /* Poll for rb bit == 0 */ + retries = MBOX_RETRY_COUNT; + do { + data = readl(proc_priv->mmio_base + MBOX_OFFSET_INTERFACE); + if (data & BIT_ULL(MBOX_BUSY_BIT)) { + ret = -EBUSY; + continue; + } + ret = 0; + break; + } while (--retries); + + return ret; +} + +static int send_mbox_write_cmd(struct pci_dev *pdev, u16 id, u32 data) +{ + struct proc_thermal_device *proc_priv; + u32 reg_data; + int ret; + + proc_priv = pci_get_drvdata(pdev); + ret = wait_for_mbox_ready(proc_priv); + if (ret) + 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)); + + return wait_for_mbox_ready(proc_priv); +} + +static int send_mbox_read_cmd(struct pci_dev *pdev, u16 id, u64 *resp) +{ + struct proc_thermal_device *proc_priv; + u32 reg_data; + int ret; + + proc_priv = pci_get_drvdata(pdev); + ret = wait_for_mbox_ready(proc_priv); + if (ret) + return ret; + + /* 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); + if (ret) + 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); + + return 0; +} + +int processor_thermal_send_mbox_read_cmd(struct pci_dev *pdev, u16 id, u64 *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) +{ + int ret; + + 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"); + +#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 + +int processor_thermal_mbox_interrupt_config(struct pci_dev *pdev, bool enable, + int enable_bit, int time_window) +{ + u64 data; + int ret; + + if (!pdev) + return -ENODEV; + + mutex_lock(&mbox_lock); + + /* Do read modify write for MBOX_CAMARILLO_RD_INTR_CONFIG */ + + 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; + } + + if (time_window >= 0) { + data &= ~WLT_TW_MASK; + + /* Program notification delay */ + data |= ((u64)time_window << SOC_PREDICTION_TW_SHIFT) & WLT_TW_MASK; + } + + if (enable) + data |= BIT(enable_bit); + else + data &= ~BIT(enable_bit); + + ret = send_mbox_write_cmd(pdev, MBOX_CAMARILLO_WR_INTR_CONFIG, data); + if (ret) + dev_err(&pdev->dev, "MBOX_CAMARILLO_WR_INTR_CONFIG failed\n"); + +unlock: + mutex_unlock(&mbox_lock); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(processor_thermal_mbox_interrupt_config, "INT340X_THERMAL"); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Processor Thermal Mail Box Interface"); |
