mirror of
https://github.com/lkl/linux.git
synced 2025-12-18 23:53:03 +09:00
platform/x86/intel/tpmi: Read feature control status
Some of the PM features can be locked or disabled. In that case, write interface can be locked. This status is read via a mailbox. There is one TPMI ID which provides base address for interface and data register for mail box operation. The mailbox operations is defined in the TPMI specification. Refer to https://github.com/intel/tpmi_power_management/ for TPMI specifications. An API is exposed to feature drivers to read feature control status. Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com> Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Link: https://lore.kernel.org/r/20230712225950.171326-2-srinivas.pandruvada@linux.intel.com Signed-off-by: Hans de Goede <hdegoede@redhat.com>
This commit is contained in:
committed by
Hans de Goede
parent
2dd074c405
commit
6145794968
@@ -47,10 +47,14 @@
|
||||
*/
|
||||
|
||||
#include <linux/auxiliary_bus.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/intel_tpmi.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/sizes.h>
|
||||
|
||||
#include "vsec.h"
|
||||
|
||||
@@ -98,6 +102,7 @@ struct intel_tpmi_pm_feature {
|
||||
* @feature_count: Number of TPMI of TPMI instances pointed by tpmi_features
|
||||
* @pfs_start: Start of PFS offset for the TPMI instances in this device
|
||||
* @plat_info: Stores platform info which can be used by the client drivers
|
||||
* @tpmi_control_mem: Memory mapped IO for getting control information
|
||||
*
|
||||
* Stores the information for all TPMI devices enumerated from a single PCI device.
|
||||
*/
|
||||
@@ -107,6 +112,7 @@ struct intel_tpmi_info {
|
||||
int feature_count;
|
||||
u64 pfs_start;
|
||||
struct intel_tpmi_plat_info plat_info;
|
||||
void __iomem *tpmi_control_mem;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -139,9 +145,19 @@ enum intel_tpmi_id {
|
||||
TPMI_ID_PEM = 1, /* Power and Perf excursion Monitor */
|
||||
TPMI_ID_UNCORE = 2, /* Uncore Frequency Scaling */
|
||||
TPMI_ID_SST = 5, /* Speed Select Technology */
|
||||
TPMI_CONTROL_ID = 0x80, /* Special ID for getting feature status */
|
||||
TPMI_INFO_ID = 0x81, /* Special ID for PCI BDF and Package ID information */
|
||||
};
|
||||
|
||||
/*
|
||||
* The size from hardware is in u32 units. This size is from a trusted hardware,
|
||||
* but better to verify for pre silicon platforms. Set size to 0, when invalid.
|
||||
*/
|
||||
#define TPMI_GET_SINGLE_ENTRY_SIZE(pfs) \
|
||||
({ \
|
||||
pfs->pfs_header.entry_size > SZ_1K ? 0 : pfs->pfs_header.entry_size << 2; \
|
||||
})
|
||||
|
||||
/* Used during auxbus device creation */
|
||||
static DEFINE_IDA(intel_vsec_tpmi_ida);
|
||||
|
||||
@@ -175,6 +191,167 @@ struct resource *tpmi_get_resource_at_index(struct auxiliary_device *auxdev, int
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(tpmi_get_resource_at_index, INTEL_TPMI);
|
||||
|
||||
/* TPMI Control Interface */
|
||||
|
||||
#define TPMI_CONTROL_STATUS_OFFSET 0x00
|
||||
#define TPMI_COMMAND_OFFSET 0x08
|
||||
|
||||
/*
|
||||
* Spec is calling for max 1 seconds to get ownership at the worst
|
||||
* case. Read at 10 ms timeouts and repeat up to 1 second.
|
||||
*/
|
||||
#define TPMI_CONTROL_TIMEOUT_US (10 * USEC_PER_MSEC)
|
||||
#define TPMI_CONTROL_TIMEOUT_MAX_US (1 * USEC_PER_SEC)
|
||||
|
||||
#define TPMI_RB_TIMEOUT_US (10 * USEC_PER_MSEC)
|
||||
#define TPMI_RB_TIMEOUT_MAX_US USEC_PER_SEC
|
||||
|
||||
/* TPMI Control status register defines */
|
||||
|
||||
#define TPMI_CONTROL_STATUS_RB BIT_ULL(0)
|
||||
|
||||
#define TPMI_CONTROL_STATUS_OWNER GENMASK_ULL(5, 4)
|
||||
#define TPMI_OWNER_NONE 0
|
||||
#define TPMI_OWNER_IN_BAND 1
|
||||
|
||||
#define TPMI_CONTROL_STATUS_CPL BIT_ULL(6)
|
||||
#define TPMI_CONTROL_STATUS_RESULT GENMASK_ULL(15, 8)
|
||||
#define TPMI_CONTROL_STATUS_LEN GENMASK_ULL(31, 16)
|
||||
|
||||
#define TPMI_CMD_PKT_LEN 2
|
||||
#define TPMI_CMD_STATUS_SUCCESS 0x40
|
||||
|
||||
/* TPMI command data registers */
|
||||
#define TMPI_CONTROL_DATA_CMD GENMASK_ULL(7, 0)
|
||||
#define TMPI_CONTROL_DATA_VAL GENMASK_ULL(63, 32)
|
||||
#define TPMI_CONTROL_DATA_VAL_FEATURE GENMASK_ULL(48, 40)
|
||||
|
||||
/* Command to send via control interface */
|
||||
#define TPMI_CONTROL_GET_STATE_CMD 0x10
|
||||
|
||||
#define TPMI_CONTROL_CMD_MASK GENMASK_ULL(48, 40)
|
||||
|
||||
#define TPMI_CMD_LEN_MASK GENMASK_ULL(18, 16)
|
||||
|
||||
#define TPMI_STATE_DISABLED BIT_ULL(0)
|
||||
#define TPMI_STATE_LOCKED BIT_ULL(31)
|
||||
|
||||
/* Mutex to complete get feature status without interruption */
|
||||
static DEFINE_MUTEX(tpmi_dev_lock);
|
||||
|
||||
static int tpmi_wait_for_owner(struct intel_tpmi_info *tpmi_info, u8 owner)
|
||||
{
|
||||
u64 control;
|
||||
|
||||
return readq_poll_timeout(tpmi_info->tpmi_control_mem + TPMI_CONTROL_STATUS_OFFSET,
|
||||
control, owner == FIELD_GET(TPMI_CONTROL_STATUS_OWNER, control),
|
||||
TPMI_CONTROL_TIMEOUT_US, TPMI_CONTROL_TIMEOUT_MAX_US);
|
||||
}
|
||||
|
||||
static int tpmi_read_feature_status(struct intel_tpmi_info *tpmi_info, int feature_id,
|
||||
int *locked, int *disabled)
|
||||
{
|
||||
u64 control, data;
|
||||
int ret;
|
||||
|
||||
if (!tpmi_info->tpmi_control_mem)
|
||||
return -EFAULT;
|
||||
|
||||
mutex_lock(&tpmi_dev_lock);
|
||||
|
||||
/* Wait for owner bit set to 0 (none) */
|
||||
ret = tpmi_wait_for_owner(tpmi_info, TPMI_OWNER_NONE);
|
||||
if (ret)
|
||||
goto err_unlock;
|
||||
|
||||
/* set command id to 0x10 for TPMI_GET_STATE */
|
||||
data = FIELD_PREP(TMPI_CONTROL_DATA_CMD, TPMI_CONTROL_GET_STATE_CMD);
|
||||
|
||||
/* 32 bits for DATA offset and +8 for feature_id field */
|
||||
data |= FIELD_PREP(TPMI_CONTROL_DATA_VAL_FEATURE, feature_id);
|
||||
|
||||
/* Write at command offset for qword access */
|
||||
writeq(data, tpmi_info->tpmi_control_mem + TPMI_COMMAND_OFFSET);
|
||||
|
||||
/* Wait for owner bit set to in-band */
|
||||
ret = tpmi_wait_for_owner(tpmi_info, TPMI_OWNER_IN_BAND);
|
||||
if (ret)
|
||||
goto err_unlock;
|
||||
|
||||
/* Set Run Busy and packet length of 2 dwords */
|
||||
control = TPMI_CONTROL_STATUS_RB;
|
||||
control |= FIELD_PREP(TPMI_CONTROL_STATUS_LEN, TPMI_CMD_PKT_LEN);
|
||||
|
||||
/* Write at status offset for qword access */
|
||||
writeq(control, tpmi_info->tpmi_control_mem + TPMI_CONTROL_STATUS_OFFSET);
|
||||
|
||||
/* Wait for Run Busy clear */
|
||||
ret = readq_poll_timeout(tpmi_info->tpmi_control_mem + TPMI_CONTROL_STATUS_OFFSET,
|
||||
control, !(control & TPMI_CONTROL_STATUS_RB),
|
||||
TPMI_RB_TIMEOUT_US, TPMI_RB_TIMEOUT_MAX_US);
|
||||
if (ret)
|
||||
goto done_proc;
|
||||
|
||||
control = FIELD_GET(TPMI_CONTROL_STATUS_RESULT, control);
|
||||
if (control != TPMI_CMD_STATUS_SUCCESS) {
|
||||
ret = -EBUSY;
|
||||
goto done_proc;
|
||||
}
|
||||
|
||||
/* Response is ready */
|
||||
data = readq(tpmi_info->tpmi_control_mem + TPMI_COMMAND_OFFSET);
|
||||
data = FIELD_GET(TMPI_CONTROL_DATA_VAL, data);
|
||||
|
||||
*disabled = 0;
|
||||
*locked = 0;
|
||||
|
||||
if (!(data & TPMI_STATE_DISABLED))
|
||||
*disabled = 1;
|
||||
|
||||
if (data & TPMI_STATE_LOCKED)
|
||||
*locked = 1;
|
||||
|
||||
ret = 0;
|
||||
|
||||
done_proc:
|
||||
/* Set CPL "completion" bit */
|
||||
writeq(TPMI_CONTROL_STATUS_CPL, tpmi_info->tpmi_control_mem + TPMI_CONTROL_STATUS_OFFSET);
|
||||
|
||||
err_unlock:
|
||||
mutex_unlock(&tpmi_dev_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int tpmi_get_feature_status(struct auxiliary_device *auxdev, int feature_id,
|
||||
int *locked, int *disabled)
|
||||
{
|
||||
struct intel_vsec_device *intel_vsec_dev = dev_to_ivdev(auxdev->dev.parent);
|
||||
struct intel_tpmi_info *tpmi_info = auxiliary_get_drvdata(&intel_vsec_dev->auxdev);
|
||||
|
||||
return tpmi_read_feature_status(tpmi_info, feature_id, locked, disabled);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(tpmi_get_feature_status, INTEL_TPMI);
|
||||
|
||||
static void tpmi_set_control_base(struct auxiliary_device *auxdev,
|
||||
struct intel_tpmi_info *tpmi_info,
|
||||
struct intel_tpmi_pm_feature *pfs)
|
||||
{
|
||||
void __iomem *mem;
|
||||
u32 size;
|
||||
|
||||
size = TPMI_GET_SINGLE_ENTRY_SIZE(pfs);
|
||||
if (!size)
|
||||
return;
|
||||
|
||||
mem = devm_ioremap(&auxdev->dev, pfs->vsec_offset, size);
|
||||
if (!mem)
|
||||
return;
|
||||
|
||||
/* mem is pointing to TPMI CONTROL base */
|
||||
tpmi_info->tpmi_control_mem = mem;
|
||||
}
|
||||
|
||||
static const char *intel_tpmi_name(enum intel_tpmi_id id)
|
||||
{
|
||||
switch (id) {
|
||||
@@ -369,6 +546,9 @@ static int intel_vsec_tpmi_init(struct auxiliary_device *auxdev)
|
||||
*/
|
||||
if (pfs->pfs_header.tpmi_id == TPMI_INFO_ID)
|
||||
tpmi_process_info(tpmi_info, pfs);
|
||||
|
||||
if (pfs->pfs_header.tpmi_id == TPMI_CONTROL_ID)
|
||||
tpmi_set_control_base(auxdev, tpmi_info, pfs);
|
||||
}
|
||||
|
||||
tpmi_info->pfs_start = pfs_start;
|
||||
|
||||
@@ -27,4 +27,6 @@ struct intel_tpmi_plat_info *tpmi_get_platform_data(struct auxiliary_device *aux
|
||||
struct resource *tpmi_get_resource_at_index(struct auxiliary_device *auxdev, int index);
|
||||
int tpmi_get_resource_count(struct auxiliary_device *auxdev);
|
||||
|
||||
int tpmi_get_feature_status(struct auxiliary_device *auxdev, int feature_id, int *locked,
|
||||
int *disabled);
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user