mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-10-30 08:02:30 +00:00
platform/x86: ISST: Add SST-CP support via TPMI
Intel Speed Select Technology Core Power (SST-CP) is an interface that allows users to define per core priority. This defines a mechanism to distribute power among cores when there is a power constrained scenario. This defines a class of service (CLOS) configuration. Three new IOCTLs are added: ISST_IF_CORE_POWER_STATE : Enable/Disable SST-CP ISST_IF_CLOS_PARAM : Configure CLOS parameters ISST_IF_CLOS_ASSOC : Associate CPUs to a CLOS To associate CPUs to CLOS, either Linux CPU numbering or PUNIT numbering scheme can be used, using parameter punit_cpu_map (1: for PUNIT numbering 0 for Linux CPU number). There is no change to IOCTL to get PUNIT CPU number for a CPU. Introduce get_instance() function, which is used by majority of IOCTLs processing to convert a socket and power domain to tpmi_per_power_domain_info * instance. This instance has all the MMIO offsets stored to read a particular field. Once an instance is identified, read or write from correct MMIO offset for a given field as defined in the specification. For details on SST CP operations using intel-speed-selet utility, refer to: Documentation/admin-guide/pm/intel-speed-select.rst under the kernel documentation Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com> Reviewed-by: Zhang Rui <rui.zhang@intel.com> Tested-by: Pragya Tanwar <pragya.tanwar@intel.com> Link: https://lore.kernel.org/r/20230308070642.1727167-5-srinivas.pandruvada@linux.intel.com Signed-off-by: Hans de Goede <hdegoede@redhat.com>
This commit is contained in:
parent
0ab147bb84
commit
12a7d2cb81
2 changed files with 343 additions and 0 deletions
|
@ -31,6 +31,18 @@
|
|||
/* Supported SST hardware version by this driver */
|
||||
#define ISST_HEADER_VERSION 1
|
||||
|
||||
/*
|
||||
* 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 1
|
||||
|
||||
/* Define 100 as a scaling factor frequency ratio to frequency conversion */
|
||||
#define SST_MUL_FACTOR_FREQ 100
|
||||
|
||||
/* All SST regs are 64 bit size */
|
||||
#define SST_REG_SIZE 8
|
||||
|
||||
/**
|
||||
* struct sst_header - SST main header
|
||||
* @interface_version: Version number for this interface
|
||||
|
@ -359,6 +371,249 @@ static int sst_main(struct auxiliary_device *auxdev, struct tpmi_per_power_domai
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
struct tpmi_per_power_domain_info *power_domain_info;
|
||||
struct tpmi_sst_struct *sst_inst;
|
||||
|
||||
if (pkg_id < 0 || pkg_id > isst_common.max_index ||
|
||||
pkg_id >= topology_max_packages())
|
||||
return NULL;
|
||||
|
||||
sst_inst = isst_common.sst_inst[pkg_id];
|
||||
if (!sst_inst)
|
||||
return NULL;
|
||||
|
||||
if (power_domain_id < 0 || power_domain_id >= sst_inst->number_of_power_domains)
|
||||
return NULL;
|
||||
|
||||
power_domain_info = &sst_inst->power_domain_info[power_domain_id];
|
||||
|
||||
if (power_domain_info && !power_domain_info->sst_base)
|
||||
return NULL;
|
||||
|
||||
return power_domain_info;
|
||||
}
|
||||
|
||||
static bool disable_dynamic_sst_features(void)
|
||||
{
|
||||
u64 value;
|
||||
|
||||
rdmsrl(MSR_PM_ENABLE, value);
|
||||
return !(value & 0x1);
|
||||
}
|
||||
|
||||
#define _read_cp_info(name_str, name, offset, start, width, mult_factor)\
|
||||
{\
|
||||
u64 val, mask;\
|
||||
\
|
||||
val = readq(power_domain_info->sst_base + power_domain_info->sst_header.cp_offset +\
|
||||
(offset));\
|
||||
mask = GENMASK_ULL((start + width - 1), start);\
|
||||
val &= mask; \
|
||||
val >>= start;\
|
||||
name = (val * mult_factor);\
|
||||
}
|
||||
|
||||
#define _write_cp_info(name_str, name, offset, start, width, div_factor)\
|
||||
{\
|
||||
u64 val, mask;\
|
||||
\
|
||||
val = readq(power_domain_info->sst_base +\
|
||||
power_domain_info->sst_header.cp_offset + (offset));\
|
||||
mask = GENMASK_ULL((start + width - 1), start);\
|
||||
val &= ~mask;\
|
||||
val |= (name / div_factor) << start;\
|
||||
writeq(val, power_domain_info->sst_base + power_domain_info->sst_header.cp_offset +\
|
||||
(offset));\
|
||||
}
|
||||
|
||||
#define SST_CP_CONTROL_OFFSET 8
|
||||
#define SST_CP_STATUS_OFFSET 16
|
||||
|
||||
#define SST_CP_ENABLE_START 0
|
||||
#define SST_CP_ENABLE_WIDTH 1
|
||||
|
||||
#define SST_CP_PRIORITY_TYPE_START 1
|
||||
#define SST_CP_PRIORITY_TYPE_WIDTH 1
|
||||
|
||||
static long isst_if_core_power_state(void __user *argp)
|
||||
{
|
||||
struct tpmi_per_power_domain_info *power_domain_info;
|
||||
struct isst_core_power core_power;
|
||||
|
||||
if (disable_dynamic_sst_features())
|
||||
return -EFAULT;
|
||||
|
||||
if (copy_from_user(&core_power, argp, sizeof(core_power)))
|
||||
return -EFAULT;
|
||||
|
||||
power_domain_info = get_instance(core_power.socket_id, core_power.power_domain_id);
|
||||
if (!power_domain_info)
|
||||
return -EINVAL;
|
||||
|
||||
if (core_power.get_set) {
|
||||
_write_cp_info("cp_enable", core_power.enable, SST_CP_CONTROL_OFFSET,
|
||||
SST_CP_ENABLE_START, SST_CP_ENABLE_WIDTH, SST_MUL_FACTOR_NONE)
|
||||
_write_cp_info("cp_prio_type", core_power.priority_type, SST_CP_CONTROL_OFFSET,
|
||||
SST_CP_PRIORITY_TYPE_START, SST_CP_PRIORITY_TYPE_WIDTH,
|
||||
SST_MUL_FACTOR_NONE)
|
||||
} else {
|
||||
/* get */
|
||||
_read_cp_info("cp_enable", core_power.enable, SST_CP_STATUS_OFFSET,
|
||||
SST_CP_ENABLE_START, SST_CP_ENABLE_WIDTH, SST_MUL_FACTOR_NONE)
|
||||
_read_cp_info("cp_prio_type", core_power.priority_type, SST_CP_STATUS_OFFSET,
|
||||
SST_CP_PRIORITY_TYPE_START, SST_CP_PRIORITY_TYPE_WIDTH,
|
||||
SST_MUL_FACTOR_NONE)
|
||||
core_power.supported = !!(power_domain_info->sst_header.cap_mask & BIT(0));
|
||||
if (copy_to_user(argp, &core_power, sizeof(core_power)))
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define SST_CLOS_CONFIG_0_OFFSET 24
|
||||
|
||||
#define SST_CLOS_CONFIG_PRIO_START 4
|
||||
#define SST_CLOS_CONFIG_PRIO_WIDTH 4
|
||||
|
||||
#define SST_CLOS_CONFIG_MIN_START 8
|
||||
#define SST_CLOS_CONFIG_MIN_WIDTH 8
|
||||
|
||||
#define SST_CLOS_CONFIG_MAX_START 16
|
||||
#define SST_CLOS_CONFIG_MAX_WIDTH 8
|
||||
|
||||
static long isst_if_clos_param(void __user *argp)
|
||||
{
|
||||
struct tpmi_per_power_domain_info *power_domain_info;
|
||||
struct isst_clos_param clos_param;
|
||||
|
||||
if (copy_from_user(&clos_param, argp, sizeof(clos_param)))
|
||||
return -EFAULT;
|
||||
|
||||
power_domain_info = get_instance(clos_param.socket_id, clos_param.power_domain_id);
|
||||
if (!power_domain_info)
|
||||
return -EINVAL;
|
||||
|
||||
if (clos_param.get_set) {
|
||||
_write_cp_info("clos.min_freq", clos_param.min_freq_mhz,
|
||||
(SST_CLOS_CONFIG_0_OFFSET + clos_param.clos * SST_REG_SIZE),
|
||||
SST_CLOS_CONFIG_MIN_START, SST_CLOS_CONFIG_MIN_WIDTH,
|
||||
SST_MUL_FACTOR_FREQ);
|
||||
_write_cp_info("clos.max_freq", clos_param.max_freq_mhz,
|
||||
(SST_CLOS_CONFIG_0_OFFSET + clos_param.clos * SST_REG_SIZE),
|
||||
SST_CLOS_CONFIG_MAX_START, SST_CLOS_CONFIG_MAX_WIDTH,
|
||||
SST_MUL_FACTOR_FREQ);
|
||||
_write_cp_info("clos.prio", clos_param.prop_prio,
|
||||
(SST_CLOS_CONFIG_0_OFFSET + clos_param.clos * SST_REG_SIZE),
|
||||
SST_CLOS_CONFIG_PRIO_START, SST_CLOS_CONFIG_PRIO_WIDTH,
|
||||
SST_MUL_FACTOR_NONE);
|
||||
} else {
|
||||
/* get */
|
||||
_read_cp_info("clos.min_freq", clos_param.min_freq_mhz,
|
||||
(SST_CLOS_CONFIG_0_OFFSET + clos_param.clos * SST_REG_SIZE),
|
||||
SST_CLOS_CONFIG_MIN_START, SST_CLOS_CONFIG_MIN_WIDTH,
|
||||
SST_MUL_FACTOR_FREQ)
|
||||
_read_cp_info("clos.max_freq", clos_param.max_freq_mhz,
|
||||
(SST_CLOS_CONFIG_0_OFFSET + clos_param.clos * SST_REG_SIZE),
|
||||
SST_CLOS_CONFIG_MAX_START, SST_CLOS_CONFIG_MAX_WIDTH,
|
||||
SST_MUL_FACTOR_FREQ)
|
||||
_read_cp_info("clos.prio", clos_param.prop_prio,
|
||||
(SST_CLOS_CONFIG_0_OFFSET + clos_param.clos * SST_REG_SIZE),
|
||||
SST_CLOS_CONFIG_PRIO_START, SST_CLOS_CONFIG_PRIO_WIDTH,
|
||||
SST_MUL_FACTOR_NONE)
|
||||
|
||||
if (copy_to_user(argp, &clos_param, sizeof(clos_param)))
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define SST_CLOS_ASSOC_0_OFFSET 56
|
||||
#define SST_CLOS_ASSOC_CPUS_PER_REG 16
|
||||
#define SST_CLOS_ASSOC_BITS_PER_CPU 4
|
||||
|
||||
static long isst_if_clos_assoc(void __user *argp)
|
||||
{
|
||||
struct isst_if_clos_assoc_cmds assoc_cmds;
|
||||
unsigned char __user *ptr;
|
||||
int i;
|
||||
|
||||
/* Each multi command has u16 command count as the first field */
|
||||
if (copy_from_user(&assoc_cmds, argp, sizeof(assoc_cmds)))
|
||||
return -EFAULT;
|
||||
|
||||
if (!assoc_cmds.cmd_count || assoc_cmds.cmd_count > ISST_IF_CMD_LIMIT)
|
||||
return -EINVAL;
|
||||
|
||||
ptr = argp + offsetof(struct isst_if_clos_assoc_cmds, assoc_info);
|
||||
for (i = 0; i < assoc_cmds.cmd_count; ++i) {
|
||||
struct tpmi_per_power_domain_info *power_domain_info;
|
||||
struct isst_if_clos_assoc clos_assoc;
|
||||
int punit_id, punit_cpu_no, pkg_id;
|
||||
struct tpmi_sst_struct *sst_inst;
|
||||
int offset, shift, cpu;
|
||||
u64 val, mask, clos;
|
||||
|
||||
if (copy_from_user(&clos_assoc, ptr, sizeof(clos_assoc)))
|
||||
return -EFAULT;
|
||||
|
||||
if (clos_assoc.socket_id > topology_max_packages())
|
||||
return -EINVAL;
|
||||
|
||||
cpu = clos_assoc.logical_cpu;
|
||||
clos = clos_assoc.clos;
|
||||
|
||||
if (assoc_cmds.punit_cpu_map)
|
||||
punit_cpu_no = cpu;
|
||||
else
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (punit_cpu_no < 0)
|
||||
return -EINVAL;
|
||||
|
||||
punit_id = clos_assoc.power_domain_id;
|
||||
pkg_id = clos_assoc.socket_id;
|
||||
|
||||
sst_inst = isst_common.sst_inst[pkg_id];
|
||||
|
||||
if (clos_assoc.power_domain_id > sst_inst->number_of_power_domains)
|
||||
return -EINVAL;
|
||||
|
||||
power_domain_info = &sst_inst->power_domain_info[punit_id];
|
||||
|
||||
offset = SST_CLOS_ASSOC_0_OFFSET +
|
||||
(punit_cpu_no / SST_CLOS_ASSOC_CPUS_PER_REG) * SST_REG_SIZE;
|
||||
shift = punit_cpu_no % SST_CLOS_ASSOC_CPUS_PER_REG;
|
||||
shift *= SST_CLOS_ASSOC_BITS_PER_CPU;
|
||||
|
||||
val = readq(power_domain_info->sst_base +
|
||||
power_domain_info->sst_header.cp_offset + offset);
|
||||
if (assoc_cmds.get_set) {
|
||||
mask = GENMASK_ULL((shift + SST_CLOS_ASSOC_BITS_PER_CPU - 1), shift);
|
||||
val &= ~mask;
|
||||
val |= (clos << shift);
|
||||
writeq(val, power_domain_info->sst_base +
|
||||
power_domain_info->sst_header.cp_offset + offset);
|
||||
} else {
|
||||
val >>= shift;
|
||||
clos_assoc.clos = val & GENMASK(SST_CLOS_ASSOC_BITS_PER_CPU - 1, 0);
|
||||
if (copy_to_user(ptr, &clos_assoc, sizeof(clos_assoc)))
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
ptr += sizeof(clos_assoc);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int isst_if_get_tpmi_instance_count(void __user *argp)
|
||||
{
|
||||
struct isst_tpmi_instance_count tpmi_inst;
|
||||
|
@ -400,6 +655,15 @@ static long isst_if_def_ioctl(struct file *file, unsigned int cmd,
|
|||
case ISST_IF_COUNT_TPMI_INSTANCES:
|
||||
ret = isst_if_get_tpmi_instance_count(argp);
|
||||
break;
|
||||
case ISST_IF_CORE_POWER_STATE:
|
||||
ret = isst_if_core_power_state(argp);
|
||||
break;
|
||||
case ISST_IF_CLOS_PARAM:
|
||||
ret = isst_if_clos_param(argp);
|
||||
break;
|
||||
case ISST_IF_CLOS_ASSOC:
|
||||
ret = isst_if_clos_assoc(argp);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -163,6 +163,82 @@ struct isst_if_msr_cmds {
|
|||
struct isst_if_msr_cmd msr_cmd[1];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct isst_core_power - Structure to get/set core_power feature
|
||||
* @get_set: 0: Get, 1: Set
|
||||
* @socket_id: Socket/package id
|
||||
* @power_domain: Power Domain id
|
||||
* @enable: Feature enable status
|
||||
* @priority_type: Priority type for the feature (ordered/proportional)
|
||||
*
|
||||
* Structure to get/set core_power feature state using IOCTL
|
||||
* ISST_IF_CORE_POWER_STATE.
|
||||
*/
|
||||
struct isst_core_power {
|
||||
__u8 get_set;
|
||||
__u8 socket_id;
|
||||
__u8 power_domain_id;
|
||||
__u8 enable;
|
||||
__u8 supported;
|
||||
__u8 priority_type;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct isst_clos_param - Structure to get/set clos praram
|
||||
* @get_set: 0: Get, 1: Set
|
||||
* @socket_id: Socket/package id
|
||||
* @power_domain: Power Domain id
|
||||
* clos: Clos ID for the parameters
|
||||
* min_freq_mhz: Minimum frequency in MHz
|
||||
* max_freq_mhz: Maximum frequency in MHz
|
||||
* prop_prio: Proportional priority from 0-15
|
||||
*
|
||||
* Structure to get/set per clos property using IOCTL
|
||||
* ISST_IF_CLOS_PARAM.
|
||||
*/
|
||||
struct isst_clos_param {
|
||||
__u8 get_set;
|
||||
__u8 socket_id;
|
||||
__u8 power_domain_id;
|
||||
__u8 clos;
|
||||
__u16 min_freq_mhz;
|
||||
__u16 max_freq_mhz;
|
||||
__u8 prop_prio;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct isst_if_clos_assoc - Structure to assign clos to a CPU
|
||||
* @socket_id: Socket/package id
|
||||
* @power_domain: Power Domain id
|
||||
* @logical_cpu: CPU number
|
||||
* @clos: Clos ID to assign to the logical CPU
|
||||
*
|
||||
* Structure to get/set core_power feature.
|
||||
*/
|
||||
struct isst_if_clos_assoc {
|
||||
__u8 socket_id;
|
||||
__u8 power_domain_id;
|
||||
__u16 logical_cpu;
|
||||
__u16 clos;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct isst_if_clos_assoc_cmds - Structure to assign clos to CPUs
|
||||
* @cmd_count: Number of cmds (cpus) in this request
|
||||
* @get_set: Request is for get or set
|
||||
* @punit_cpu_map: Set to 1 if the CPU number is punit numbering not
|
||||
* Linux CPU number
|
||||
*
|
||||
* Structure used to get/set associate CPUs to clos using IOCTL
|
||||
* ISST_IF_CLOS_ASSOC.
|
||||
*/
|
||||
struct isst_if_clos_assoc_cmds {
|
||||
__u16 cmd_count;
|
||||
__u16 get_set;
|
||||
__u16 punit_cpu_map;
|
||||
struct isst_if_clos_assoc assoc_info[1];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct isst_tpmi_instance_count - Get number of TPMI instances per socket
|
||||
* @socket_id: Socket/package id
|
||||
|
@ -186,5 +262,8 @@ struct isst_tpmi_instance_count {
|
|||
#define ISST_IF_MSR_COMMAND _IOWR(ISST_IF_MAGIC, 4, struct isst_if_msr_cmds *)
|
||||
|
||||
#define ISST_IF_COUNT_TPMI_INSTANCES _IOR(ISST_IF_MAGIC, 5, struct isst_tpmi_instance_count *)
|
||||
#define ISST_IF_CORE_POWER_STATE _IOWR(ISST_IF_MAGIC, 6, struct isst_core_power *)
|
||||
#define ISST_IF_CLOS_PARAM _IOWR(ISST_IF_MAGIC, 7, struct isst_clos_param *)
|
||||
#define ISST_IF_CLOS_ASSOC _IOWR(ISST_IF_MAGIC, 8, struct isst_if_clos_assoc_cmds *)
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue