Commit 0ab147bb authored by Srinivas Pandruvada's avatar Srinivas Pandruvada Committed by Hans de Goede

platform/x86: ISST: Parse SST MMIO and update instance

SST registers are presented to OS in multi-layer structures starting
with a SST header showing version information freezing current
definition.

For details on SST terminology refer to
Documentation/admin-guide/pm/intel-speed-select.rst
under the kernel documentation

SST TPMI details are published in the following document:
https://github.com/intel/tpmi_power_management/blob/main/SST_TPMI_public_disclosure_FINAL.docx

SST MMIO structure layout follows:
SST-HEADER
	SST-CP Header
		SST-CP CONTROL
		SST-CP STATUS
		SST-CP CONFIG0
		SST-CP CONFIG1
		...
		...
	SST-PP Header
		SST-PP OFFSET_0
		SST-PP OFFSET_1
					SST_PP_0_INFO
					SST_PP_1_INFO
					SST_PP_2_INFO
					SST_PP_3_INFO
		SST-PP CONTROL
		SST-PP STATUS

Each register bank contains information to get to next lower level
information. This information is parsed and stored in the struct
tpmi_per_power_domain_info for each domain. This information is
used to process each SST requests.
Signed-off-by: default avatarSrinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
Reviewed-by: default avatarZhang Rui <rui.zhang@intel.com>
Tested-by: default avatarPragya Tanwar <pragya.tanwar@intel.com>
Link: https://lore.kernel.org/r/20230308070642.1727167-4-srinivas.pandruvada@linux.intel.comSigned-off-by: default avatarHans de Goede <hdegoede@redhat.com>
parent d805456c
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include <linux/auxiliary_bus.h> #include <linux/auxiliary_bus.h>
#include <linux/intel_tpmi.h> #include <linux/intel_tpmi.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/io.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <uapi/linux/isst_if.h> #include <uapi/linux/isst_if.h>
...@@ -27,10 +28,192 @@ ...@@ -27,10 +28,192 @@
#include "isst_tpmi_core.h" #include "isst_tpmi_core.h"
#include "isst_if_common.h" #include "isst_if_common.h"
/* Supported SST hardware version by this driver */
#define ISST_HEADER_VERSION 1
/**
* struct sst_header - SST main header
* @interface_version: Version number for this interface
* @cap_mask: Bitmask of the supported sub features. 1=the sub feature is enabled.
* 0=disabled.
* Bit[8]= SST_CP enable (1), disable (0)
* bit[9]= SST_PP enable (1), disable (0)
* other bits are reserved for future use
* @cp_offset: Qword (8 bytes) offset to the SST_CP register bank
* @pp_offset: Qword (8 bytes) offset to the SST_PP register bank
* @reserved: Reserved for future use
*
* This register allows SW to discover SST capability and the offsets to SST-CP
* and SST-PP register banks.
*/
struct sst_header {
u8 interface_version;
u8 cap_mask;
u8 cp_offset;
u8 pp_offset;
u32 reserved;
} __packed;
/**
* struct cp_header - SST-CP (core-power) header
* @feature_id: 0=SST-CP, 1=SST-PP, 2=SST-BF, 3=SST-TF
* @feature_rev: Interface Version number for this SST feature
* @ratio_unit: Frequency ratio unit. 00: 100MHz. All others are reserved
* @reserved: Reserved for future use
*
* This structure is used store SST-CP header. This is packed to the same
* format as defined in the specifications.
*/
struct cp_header {
u64 feature_id :4;
u64 feature_rev :8;
u64 ratio_unit :2;
u64 reserved :50;
} __packed;
/**
* struct pp_header - SST-PP (Perf profile) header
* @feature_id: 0=SST-CP, 1=SST-PP, 2=SST-BF, 3=SST-TF
* @feature_rev: Interface Version number for this SST feature
* @level_en_mask: SST-PP level enable/disable fuse mask
* @allowed_level_mask: Allowed level mask used for dynamic config level switching
* @reserved0: Reserved for future use
* @ratio_unit: Frequency ratio unit. 00: 100MHz. All others are reserved
* @block_size: Size of PP block in Qword unit (8 bytes)
* @dynamic_switch: If set (1), dynamic switching of SST PP is supported
* @memory_ratio_unit: Memory Controller frequency ratio unit. 00: 100MHz, others reserved
* @reserved1: Reserved for future use
*
* This structure is used store SST-PP header. This is packed to the same
* format as defined in the specifications.
*/
struct pp_header {
u64 feature_id :4;
u64 feature_rev :8;
u64 level_en_mask :8;
u64 allowed_level_mask :8;
u64 reserved0 :4;
u64 ratio_unit :2;
u64 block_size :8;
u64 dynamic_switch :1;
u64 memory_ratio_unit :2;
u64 reserved1 :19;
} __packed;
/**
* struct feature_offset - Offsets to SST-PP features
* @pp_offset: Qword offset within PP level for the SST_PP register bank
* @bf_offset: Qword offset within PP level for the SST_BF register bank
* @tf_offset: Qword offset within PP level for the SST_TF register bank
* @reserved: Reserved for future use
*
* This structure is used store offsets for SST features in the register bank.
* This is packed to the same format as defined in the specifications.
*/
struct feature_offset {
u64 pp_offset :8;
u64 bf_offset :8;
u64 tf_offset :8;
u64 reserved :40;
} __packed;
/**
* struct levels_offset - Offsets to each SST PP level
* @sst_pp_level0_offset: Qword offset to the register block of PP level 0
* @sst_pp_level1_offset: Qword offset to the register block of PP level 1
* @sst_pp_level2_offset: Qword offset to the register block of PP level 2
* @sst_pp_level3_offset: Qword offset to the register block of PP level 3
* @sst_pp_level4_offset: Qword offset to the register block of PP level 4
* @reserved: Reserved for future use
*
* This structure is used store offsets of SST PP levels in the register bank.
* This is packed to the same format as defined in the specifications.
*/
struct levels_offset {
u64 sst_pp_level0_offset :8;
u64 sst_pp_level1_offset :8;
u64 sst_pp_level2_offset :8;
u64 sst_pp_level3_offset :8;
u64 sst_pp_level4_offset :8;
u64 reserved :24;
} __packed;
/**
* struct pp_control_offset - Offsets for SST PP controls
* @perf_level: A SST-PP level that SW intends to switch to
* @perf_level_lock: SST-PP level select lock. 0 - unlocked. 1 - locked till next reset
* @resvd0: Reserved for future use
* @current_state: Bit mask to control the enable(1)/disable(0) state of each feature
* of the current PP level, bit 0 = BF, bit 1 = TF, bit 2-7 = reserved
* @reserved: Reserved for future use
*
* This structure is used store offsets of SST PP controls in the register bank.
* This is packed to the same format as defined in the specifications.
*/
struct pp_control_offset {
u64 perf_level :3;
u64 perf_level_lock :1;
u64 resvd0 :4;
u64 current_state :8;
u64 reserved :48;
} __packed;
/**
* struct pp_status_offset - Offsets for SST PP status fields
* @sst_pp_level: Returns the current SST-PP level
* @sst_pp_lock: Returns the lock bit setting of perf_level_lock in pp_control_offset
* @error_type: Returns last error of SST-PP level change request. 0: no error,
* 1: level change not allowed, others: reserved
* @feature_state: Bit mask to indicate the enable(1)/disable(0) state of each feature of the
* current PP level. bit 0 = BF, bit 1 = TF, bit 2-7 reserved
* @reserved0: Reserved for future use
* @feature_error_type: Returns last error of the specific feature. Three error_type bits per
* feature. i.e. ERROR_TYPE[2:0] for BF, ERROR_TYPE[5:3] for TF, etc.
* 0x0: no error, 0x1: The specific feature is not supported by the hardware.
* 0x2-0x6: Reserved. 0x7: feature state change is not allowed.
* @reserved1: Reserved for future use
*
* This structure is used store offsets of SST PP status in the register bank.
* This is packed to the same format as defined in the specifications.
*/
struct pp_status_offset {
u64 sst_pp_level :3;
u64 sst_pp_lock :1;
u64 error_type :4;
u64 feature_state :8;
u64 reserved0 :16;
u64 feature_error_type : 24;
u64 reserved1 :8;
} __packed;
/**
* struct perf_level - Used to store perf level and mmio offset
* @mmio_offset: mmio offset for a perf level
* @level: perf level for this offset
*
* This structure is used store final mmio offset of each perf level from the
* SST base mmio offset.
*/
struct perf_level {
int mmio_offset;
int level;
};
/** /**
* struct tpmi_per_power_domain_info - Store per power_domain SST info * struct tpmi_per_power_domain_info - Store per power_domain SST info
* @package_id: Package id for this power_domain * @package_id: Package id for this power_domain
* @power_domain_id: Power domain id, Each entry from the SST-TPMI instance is a power_domain. * @power_domain_id: Power domain id, Each entry from the SST-TPMI instance is a power_domain.
* @max_level: Max possible PP level possible for this power_domain
* @ratio_unit: Ratio unit for converting to MHz
* @avx_levels: Number of AVX levels
* @pp_block_size: Block size from PP header
* @sst_header: Store SST header for this power_domain
* @cp_header: Store SST-CP header for this power_domain
* @pp_header: Store SST-PP header for this power_domain
* @perf_levels: Pointer to each perf level to map level to mmio offset
* @feature_offsets: Store feature offsets for each PP-level
* @control_offset: Store the control offset for each PP-level
* @status_offset: Store the status offset for each PP-level
* @sst_base: Mapped SST base IO memory * @sst_base: Mapped SST base IO memory
* @auxdev: Auxiliary device instance enumerated this instance * @auxdev: Auxiliary device instance enumerated this instance
* *
...@@ -41,6 +224,17 @@ ...@@ -41,6 +224,17 @@
struct tpmi_per_power_domain_info { struct tpmi_per_power_domain_info {
int package_id; int package_id;
int power_domain_id; int power_domain_id;
int max_level;
int ratio_unit;
int avx_levels;
int pp_block_size;
struct sst_header sst_header;
struct cp_header cp_header;
struct pp_header pp_header;
struct perf_level *perf_levels;
struct feature_offset feature_offsets;
struct pp_control_offset control_offset;
struct pp_status_offset status_offset;
void __iomem *sst_base; void __iomem *sst_base;
struct auxiliary_device *auxdev; struct auxiliary_device *auxdev;
}; };
...@@ -85,6 +279,86 @@ static int isst_core_usage_count; ...@@ -85,6 +279,86 @@ static int isst_core_usage_count;
/* Stores complete SST information for every package and power_domain */ /* Stores complete SST information for every package and power_domain */
static struct tpmi_sst_common_struct isst_common; static struct tpmi_sst_common_struct isst_common;
#define SST_MAX_AVX_LEVELS 3
#define SST_PP_OFFSET_0 8
#define SST_PP_OFFSET_1 16
#define SST_PP_OFFSET_SIZE 8
static int sst_add_perf_profiles(struct auxiliary_device *auxdev,
struct tpmi_per_power_domain_info *pd_info,
int levels)
{
u64 perf_level_offsets;
int i;
pd_info->perf_levels = devm_kcalloc(&auxdev->dev, levels,
sizeof(struct perf_level),
GFP_KERNEL);
if (!pd_info->perf_levels)
return 0;
pd_info->ratio_unit = pd_info->pp_header.ratio_unit;
pd_info->avx_levels = SST_MAX_AVX_LEVELS;
pd_info->pp_block_size = pd_info->pp_header.block_size;
/* Read PP Offset 0: Get feature offset with PP level */
*((u64 *)&pd_info->feature_offsets) = readq(pd_info->sst_base +
pd_info->sst_header.pp_offset +
SST_PP_OFFSET_0);
perf_level_offsets = readq(pd_info->sst_base + pd_info->sst_header.pp_offset +
SST_PP_OFFSET_1);
for (i = 0; i < levels; ++i) {
u64 offset;
offset = perf_level_offsets & (0xff << (i * SST_PP_OFFSET_SIZE));
offset >>= (i * 8);
offset &= 0xff;
offset *= 8; /* Convert to byte from QWORD offset */
pd_info->perf_levels[i].mmio_offset = pd_info->sst_header.pp_offset + offset;
}
return 0;
}
static int sst_main(struct auxiliary_device *auxdev, struct tpmi_per_power_domain_info *pd_info)
{
int i, mask, levels;
*((u64 *)&pd_info->sst_header) = readq(pd_info->sst_base);
pd_info->sst_header.cp_offset *= 8;
pd_info->sst_header.pp_offset *= 8;
if (pd_info->sst_header.interface_version != ISST_HEADER_VERSION) {
dev_err(&auxdev->dev, "SST: Unsupported version:%x\n",
pd_info->sst_header.interface_version);
return -ENODEV;
}
/* Read SST CP Header */
*((u64 *)&pd_info->cp_header) = readq(pd_info->sst_base + pd_info->sst_header.cp_offset);
/* Read PP header */
*((u64 *)&pd_info->pp_header) = readq(pd_info->sst_base + pd_info->sst_header.pp_offset);
/* Force level_en_mask level 0 */
pd_info->pp_header.level_en_mask |= 0x01;
mask = 0x01;
levels = 0;
for (i = 0; i < 8; ++i) {
if (pd_info->pp_header.level_en_mask & mask)
levels = i;
mask <<= 1;
}
pd_info->max_level = levels;
sst_add_perf_profiles(auxdev, pd_info, levels + 1);
return 0;
}
static int isst_if_get_tpmi_instance_count(void __user *argp) static int isst_if_get_tpmi_instance_count(void __user *argp)
{ {
struct isst_tpmi_instance_count tpmi_inst; struct isst_tpmi_instance_count tpmi_inst;
...@@ -102,10 +376,10 @@ static int isst_if_get_tpmi_instance_count(void __user *argp) ...@@ -102,10 +376,10 @@ static int isst_if_get_tpmi_instance_count(void __user *argp)
sst_inst = isst_common.sst_inst[tpmi_inst.socket_id]; sst_inst = isst_common.sst_inst[tpmi_inst.socket_id];
tpmi_inst.valid_mask = 0; tpmi_inst.valid_mask = 0;
for (i = 0; i < sst_inst->number_of_power_domains; ++i) { for (i = 0; i < sst_inst->number_of_power_domains; ++i) {
struct tpmi_per_power_domain_info *power_domain_info; struct tpmi_per_power_domain_info *pd_info;
power_domain_info = &sst_inst->power_domain_info[i]; pd_info = &sst_inst->power_domain_info[i];
if (power_domain_info->sst_base) if (pd_info->sst_base)
tpmi_inst.valid_mask |= BIT(i); tpmi_inst.valid_mask |= BIT(i);
} }
...@@ -134,11 +408,13 @@ static long isst_if_def_ioctl(struct file *file, unsigned int cmd, ...@@ -134,11 +408,13 @@ static long isst_if_def_ioctl(struct file *file, unsigned int cmd,
return ret; return ret;
} }
#define TPMI_SST_AUTO_SUSPEND_DELAY_MS 2000
int tpmi_sst_dev_add(struct auxiliary_device *auxdev) int tpmi_sst_dev_add(struct auxiliary_device *auxdev)
{ {
struct intel_tpmi_plat_info *plat_info; struct intel_tpmi_plat_info *plat_info;
struct tpmi_sst_struct *tpmi_sst; struct tpmi_sst_struct *tpmi_sst;
int i, pkg = 0, inst = 0; int i, ret, pkg = 0, inst = 0;
int num_resources; int num_resources;
plat_info = tpmi_get_platform_data(auxdev); plat_info = tpmi_get_platform_data(auxdev);
...@@ -189,6 +465,13 @@ int tpmi_sst_dev_add(struct auxiliary_device *auxdev) ...@@ -189,6 +465,13 @@ int tpmi_sst_dev_add(struct auxiliary_device *auxdev)
if (IS_ERR(tpmi_sst->power_domain_info[i].sst_base)) if (IS_ERR(tpmi_sst->power_domain_info[i].sst_base))
return PTR_ERR(tpmi_sst->power_domain_info[i].sst_base); return PTR_ERR(tpmi_sst->power_domain_info[i].sst_base);
ret = sst_main(auxdev, &tpmi_sst->power_domain_info[i]);
if (ret) {
devm_iounmap(&auxdev->dev, tpmi_sst->power_domain_info[i].sst_base);
tpmi_sst->power_domain_info[i].sst_base = NULL;
continue;
}
++inst; ++inst;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment