// 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(…) …;