linux/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c

// SPDX-License-Identifier: GPL-2.0-only
/*
 * isst_tpmi.c: SST TPMI interface core
 *
 * Copyright (c) 2023, Intel Corporation.
 * All Rights Reserved.
 *
 * This information will be useful to understand flows:
 * In the current generation of platforms, TPMI is supported via OOB
 * PCI device. This PCI device has one instance per CPU package.
 * There is a unique TPMI ID for SST. Each TPMI ID also has multiple
 * entries, representing per power domain information.
 *
 * There is one dev file for complete SST information and control same as the
 * prior generation of hardware. User spaces don't need to know how the
 * information is presented by the hardware. The TPMI core module implements
 * the hardware mapping.
 */

#define dev_fmt(fmt)

#include <linux/auxiliary_bus.h>
#include <linux/delay.h>
#include <linux/intel_tpmi.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/minmax.h>
#include <linux/module.h>
#include <uapi/linux/isst_if.h>

#include "isst_tpmi_core.h"
#include "isst_if_common.h"

/* Supported SST hardware version by this driver */
#define ISST_MAJOR_VERSION
#define ISST_MINOR_VERSION

/*
 * Used to indicate if value read from MMIO needs to get multiplied
 * to get to a standard unit or not.
 */
#define SST_MUL_FACTOR_NONE

/* Define 100 as a scaling factor frequency ratio to frequency conversion */
#define SST_MUL_FACTOR_FREQ

/* All SST regs are 64 bit size */
#define SST_REG_SIZE

/**
 * 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 {} __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 {} __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 {} __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 {} __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 {} __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 {} __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 {} __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 {};

/**
 * struct tpmi_per_power_domain_info -	Store per power_domain SST info
 * @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.
 * @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
 * @auxdev:		Auxiliary device instance enumerated this instance
 * @saved_sst_cp_control: Save SST-CP control configuration to store restore for suspend/resume
 * @saved_clos_configs:	Save SST-CP CLOS configuration to store restore for suspend/resume
 * @saved_clos_assocs:	Save SST-CP CLOS association to store restore for suspend/resume
 * @saved_pp_control:	Save SST-PP control information to store restore for suspend/resume
 * @write_blocked:	Write operation is blocked, so can't change SST state
 *
 * This structure is used store complete SST information for a power_domain. This information
 * is used to read/write request for any SST IOCTL. Each physical CPU package can have multiple
 * power_domains. Each power domain describes its own SST information and has its own controls.
 */
struct tpmi_per_power_domain_info {};

/* Supported maximum partitions */
#define SST_MAX_PARTITIONS

/**
 * struct tpmi_sst_struct -	Store sst info for a package
 * @package_id:			Package id for this aux device instance
 * @number_of_power_domains:	Number of power_domains pointed by power_domain_info pointer
 * @power_domain_info:		Pointer to power domains information
 * @cdie_mask:			Mask of compute dies present in a partition from hardware.
 *				This mask is not present in the version 1 information header.
 * @io_dies:			Number of IO dies in a partition. This will be 0 for TPMI
 *				version 1 information header.
 * @partition_mask:		Mask of all partitions.
 * @partition_mask_current:	Current partition mask as some may have been unbound.
 *
 * This structure is used store full SST information for a package.
 * Each package has one or multiple OOB PCI devices. Each package can contain multiple
 * power domains.
 */
struct tpmi_sst_struct {};

/**
 * struct tpmi_sst_common_struct -	Store all SST instances
 * @max_index:		Maximum instances currently present
 * @sst_inst:		Pointer to per package instance
 *
 * Stores every SST Package instance.
 */
struct tpmi_sst_common_struct {};

/*
 * Each IOCTL request is processed under this lock. Also used to protect
 * registration functions and common data structures.
 */
static DEFINE_MUTEX(isst_tpmi_dev_lock);

/* Usage count to track, number of TPMI SST instances registered to this core. */
static int isst_core_usage_count;

/* Stores complete SST information for every package and power_domain */
static struct tpmi_sst_common_struct isst_common;

#define SST_MAX_AVX_LEVELS

#define SST_PP_OFFSET_0
#define SST_PP_OFFSET_1
#define SST_PP_OFFSET_SIZE

static int sst_add_perf_profiles(struct auxiliary_device *auxdev,
				 struct tpmi_per_power_domain_info *pd_info,
				 int levels)
{}

static int sst_main(struct auxiliary_device *auxdev, struct tpmi_per_power_domain_info *pd_info)
{}

static u8 isst_instance_count(struct tpmi_sst_struct *sst_inst)
{}

/**
 * map_cdies() - Map user domain ID to compute domain ID
 * @sst_inst: TPMI Instance
 * @id: User domain ID
 * @partition: Resolved partition
 *
 * Helper function to map_partition_power_domain_id() to resolve compute
 * domain ID and partition. Use hardware provided cdie_mask for a partition
 * as is to resolve a compute domain ID.
 *
 * Return: %-EINVAL on error, otherwise mapped domain ID >= 0.
 */
static int map_cdies(struct tpmi_sst_struct *sst_inst, u8 id, u8 *partition)
{}

/**
 * map_partition_power_domain_id() - Map user domain ID to partition domain ID
 * @sst_inst: TPMI Instance
 * @id: User domain ID
 * @partition: Resolved partition
 *
 * In a partitioned system a CPU package has two separate MMIO ranges (Under
 * two PCI devices). But the CPU package compute die/power domain IDs are
 * unique in a package. User space can get compute die/power domain ID from
 * CPUID and MSR 0x54 for a CPU. So, those IDs need to be preserved even if
 * they are present in two different partitions with its own order.
 *
 * For example for command ISST_IF_COUNT_TPMI_INSTANCES, the valid_mask
 * is 111111b for a 4 compute and 2 IO dies system. This is presented as
 * provided by the hardware in a non-partitioned system with the following
 * order:
 *	I1-I0-C3-C2-C1-C0
 * Here: "C": for compute and "I" for IO die.
 * Compute dies are always present first in TPMI instances, as they have
 * to map to the real power domain/die ID of a system. In a non-partitioned
 * system there is no way to identify compute and IO die boundaries from
 * this driver without reading each CPU's mapping.
 *
 * The same order needs to be preserved, even if those compute dies are
 * distributed among multiple partitions. For example:
 * Partition 1 can contain: I1-C1-C0
 * Partition 2 can contain: I2-C3-C2
 *
 * This will require a conversion of user space IDs to the actual index into
 * array of stored power domains for each partition. For the above example
 * this function will return partition and index as follows:
 *
 * =============	=========	=====	========
 * User space ID	Partition	Index	Die type
 * =============	=========	=====	========
 * 0			0		0	Compute
 * 1			0		1	Compute
 * 2			1		0	Compute
 * 3			1		1	Compute
 * 4			0		2	IO
 * 5			1		2	IO
 * =============	=========	=====	========
 *
 * Return: %-EINVAL on error, otherwise mapped domain ID >= 0.
 */
static int map_partition_power_domain_id(struct tpmi_sst_struct *sst_inst, u8 id, u8 *partition)
{}

/*
 * Map a package and power_domain id to SST information structure unique for a power_domain.
 * The caller should call under isst_tpmi_dev_lock.
 */
static struct tpmi_per_power_domain_info *get_instance(int pkg_id, int power_domain_id)
{}

static bool disable_dynamic_sst_features(void)
{}

#define _read_cp_info(name_str, name, offset, start, width, mult_factor)

#define _write_cp_info(name_str, name, offset, start, width, div_factor)

#define SST_CP_CONTROL_OFFSET
#define SST_CP_STATUS_OFFSET

#define SST_CP_ENABLE_START
#define SST_CP_ENABLE_WIDTH

#define SST_CP_PRIORITY_TYPE_START
#define SST_CP_PRIORITY_TYPE_WIDTH

static long isst_if_core_power_state(void __user *argp)
{}

#define SST_CLOS_CONFIG_0_OFFSET

#define SST_CLOS_CONFIG_PRIO_START
#define SST_CLOS_CONFIG_PRIO_WIDTH

#define SST_CLOS_CONFIG_MIN_START
#define SST_CLOS_CONFIG_MIN_WIDTH

#define SST_CLOS_CONFIG_MAX_START
#define SST_CLOS_CONFIG_MAX_WIDTH

static long isst_if_clos_param(void __user *argp)
{}

#define SST_CLOS_ASSOC_0_OFFSET
#define SST_CLOS_ASSOC_CPUS_PER_REG
#define SST_CLOS_ASSOC_BITS_PER_CPU

static long isst_if_clos_assoc(void __user *argp)
{}

#define _read_pp_info(name_str, name, offset, start, width, mult_factor)

#define _write_pp_info(name_str, name, offset, start, width, div_factor)

#define _read_bf_level_info(name_str, name, level, offset, start, width, mult_factor)

#define _read_tf_level_info(name_str, name, level, offset, start, width, mult_factor)

#define SST_PP_STATUS_OFFSET

#define SST_PP_LEVEL_START
#define SST_PP_LEVEL_WIDTH

#define SST_PP_LOCK_START
#define SST_PP_LOCK_WIDTH

#define SST_PP_FEATURE_STATE_START
#define SST_PP_FEATURE_STATE_WIDTH

#define SST_BF_FEATURE_SUPPORTED_START
#define SST_BF_FEATURE_SUPPORTED_WIDTH

#define SST_TF_FEATURE_SUPPORTED_START
#define SST_TF_FEATURE_SUPPORTED_WIDTH

static int isst_if_get_perf_level(void __user *argp)
{}

#define SST_PP_CONTROL_OFFSET
#define SST_PP_LEVEL_CHANGE_TIME_MS
#define SST_PP_LEVEL_CHANGE_RETRY_COUNT

static int isst_if_set_perf_level(void __user *argp)
{}

static int isst_if_set_perf_feature(void __user *argp)
{}

#define _read_pp_level_info(name_str, name, level, offset, start, width, mult_factor)

#define SST_PP_INFO_0_OFFSET
#define SST_PP_INFO_1_OFFSET
#define SST_PP_INFO_2_OFFSET
#define SST_PP_INFO_3_OFFSET

/* SST_PP_INFO_4_OFFSET to SST_PP_INFO_9_OFFSET are trl levels */
#define SST_PP_INFO_4_OFFSET

#define SST_PP_INFO_10_OFFSET
#define SST_PP_INFO_11_OFFSET

#define SST_PP_P1_SSE_START
#define SST_PP_P1_SSE_WIDTH

#define SST_PP_P1_AVX2_START
#define SST_PP_P1_AVX2_WIDTH

#define SST_PP_P1_AVX512_START
#define SST_PP_P1_AVX512_WIDTH

#define SST_PP_P1_AMX_START
#define SST_PP_P1_AMX_WIDTH

#define SST_PP_TDP_START
#define SST_PP_TDP_WIDTH

#define SST_PP_T_PROCHOT_START
#define SST_PP_T_PROCHOT_WIDTH

#define SST_PP_MAX_MEMORY_FREQ_START
#define SST_PP_MAX_MEMORY_FREQ_WIDTH

#define SST_PP_COOLING_TYPE_START
#define SST_PP_COOLING_TYPE_WIDTH

#define SST_PP_TRL_0_RATIO_0_START
#define SST_PP_TRL_0_RATIO_0_WIDTH

#define SST_PP_TRL_CORES_BUCKET_0_START
#define SST_PP_TRL_CORES_BUCKET_0_WIDTH

#define SST_PP_CORE_RATIO_P0_START
#define SST_PP_CORE_RATIO_P0_WIDTH

#define SST_PP_CORE_RATIO_P1_START
#define SST_PP_CORE_RATIO_P1_WIDTH

#define SST_PP_CORE_RATIO_PN_START
#define SST_PP_CORE_RATIO_PN_WIDTH

#define SST_PP_CORE_RATIO_PM_START
#define SST_PP_CORE_RATIO_PM_WIDTH

#define SST_PP_CORE_RATIO_P0_FABRIC_START
#define SST_PP_CORE_RATIO_P0_FABRIC_WIDTH

#define SST_PP_CORE_RATIO_P1_FABRIC_START
#define SST_PP_CORE_RATIO_P1_FABRIC_WIDTH

#define SST_PP_CORE_RATIO_PM_FABRIC_START
#define SST_PP_CORE_RATIO_PM_FABRIC_WIDTH

static int isst_if_get_perf_level_info(void __user *argp)
{}

#define SST_PP_FUSED_CORE_COUNT_START
#define SST_PP_FUSED_CORE_COUNT_WIDTH

#define SST_PP_RSLVD_CORE_COUNT_START
#define SST_PP_RSLVD_CORE_COUNT_WIDTH

#define SST_PP_RSLVD_CORE_MASK_START
#define SST_PP_RSLVD_CORE_MASK_WIDTH

static int isst_if_get_perf_level_mask(void __user *argp)
{}

#define SST_BF_INFO_0_OFFSET
#define SST_BF_INFO_1_OFFSET

#define SST_BF_P1_HIGH_START
#define SST_BF_P1_HIGH_WIDTH

#define SST_BF_P1_LOW_START
#define SST_BF_P1_LOW_WIDTH

#define SST_BF_T_PROHOT_START
#define SST_BF_T_PROHOT_WIDTH

#define SST_BF_TDP_START
#define SST_BF_TDP_WIDTH

static int isst_if_get_base_freq_info(void __user *argp)
{}

#define P1_HI_CORE_MASK_START
#define P1_HI_CORE_MASK_WIDTH

static int isst_if_get_base_freq_mask(void __user *argp)
{}

static int isst_if_get_tpmi_instance_count(void __user *argp)
{}

#define SST_TF_INFO_0_OFFSET
#define SST_TF_INFO_1_OFFSET
#define SST_TF_INFO_2_OFFSET

#define SST_TF_MAX_LP_CLIP_RATIOS

#define SST_TF_LP_CLIP_RATIO_0_START
#define SST_TF_LP_CLIP_RATIO_0_WIDTH

#define SST_TF_RATIO_0_START
#define SST_TF_RATIO_0_WIDTH

#define SST_TF_NUM_CORE_0_START
#define SST_TF_NUM_CORE_0_WIDTH

static int isst_if_get_turbo_freq_info(void __user *argp)
{}

static long isst_if_def_ioctl(struct file *file, unsigned int cmd,
			      unsigned long arg)
{}

#define TPMI_SST_AUTO_SUSPEND_DELAY_MS

int tpmi_sst_dev_add(struct auxiliary_device *auxdev)
{}
EXPORT_SYMBOL_NS_GPL();

void tpmi_sst_dev_remove(struct auxiliary_device *auxdev)
{}
EXPORT_SYMBOL_NS_GPL();

void tpmi_sst_dev_suspend(struct auxiliary_device *auxdev)
{}
EXPORT_SYMBOL_NS_GPL();

void tpmi_sst_dev_resume(struct auxiliary_device *auxdev)
{}
EXPORT_SYMBOL_NS_GPL();

#define ISST_TPMI_API_VERSION

int tpmi_sst_init(void)
{}
EXPORT_SYMBOL_NS_GPL();

void tpmi_sst_exit(void)
{}
EXPORT_SYMBOL_NS_GPL();

MODULE_IMPORT_NS();
MODULE_IMPORT_NS();

MODULE_DESCRIPTION();
MODULE_LICENSE();