diff options
Diffstat (limited to 'tools/power/x86/intel-speed-select')
-rw-r--r-- | tools/power/x86/intel-speed-select/.gitignore | 3 | ||||
-rw-r--r-- | tools/power/x86/intel-speed-select/Build | 1 | ||||
-rw-r--r-- | tools/power/x86/intel-speed-select/Makefile | 60 | ||||
-rw-r--r-- | tools/power/x86/intel-speed-select/hfi-events.c | 308 | ||||
-rw-r--r-- | tools/power/x86/intel-speed-select/isst-config.c | 3187 | ||||
-rw-r--r-- | tools/power/x86/intel-speed-select/isst-core-mbox.c | 1066 | ||||
-rw-r--r-- | tools/power/x86/intel-speed-select/isst-core-tpmi.c | 814 | ||||
-rw-r--r-- | tools/power/x86/intel-speed-select/isst-core.c | 498 | ||||
-rw-r--r-- | tools/power/x86/intel-speed-select/isst-daemon.c | 255 | ||||
-rw-r--r-- | tools/power/x86/intel-speed-select/isst-display.c | 765 | ||||
-rw-r--r-- | tools/power/x86/intel-speed-select/isst.h | 324 |
11 files changed, 7281 insertions, 0 deletions
diff --git a/tools/power/x86/intel-speed-select/.gitignore b/tools/power/x86/intel-speed-select/.gitignore new file mode 100644 index 0000000000..a814f89fe7 --- /dev/null +++ b/tools/power/x86/intel-speed-select/.gitignore @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only +include/ +intel-speed-select diff --git a/tools/power/x86/intel-speed-select/Build b/tools/power/x86/intel-speed-select/Build new file mode 100644 index 0000000000..5a9637e167 --- /dev/null +++ b/tools/power/x86/intel-speed-select/Build @@ -0,0 +1 @@ +intel-speed-select-y += isst-config.o isst-core.o isst-display.o isst-daemon.o hfi-events.o isst-core-mbox.o isst-core-tpmi.o diff --git a/tools/power/x86/intel-speed-select/Makefile b/tools/power/x86/intel-speed-select/Makefile new file mode 100644 index 0000000000..7221f2f55e --- /dev/null +++ b/tools/power/x86/intel-speed-select/Makefile @@ -0,0 +1,60 @@ +# SPDX-License-Identifier: GPL-2.0 +include ../../../scripts/Makefile.include + +bindir ?= /usr/bin + +ifeq ($(srctree),) +srctree := $(patsubst %/,%,$(dir $(CURDIR))) +srctree := $(patsubst %/,%,$(dir $(srctree))) +srctree := $(patsubst %/,%,$(dir $(srctree))) +srctree := $(patsubst %/,%,$(dir $(srctree))) +endif + +# Do not use make's built-in rules +# (this improves performance and avoids hard-to-debug behaviour); +MAKEFLAGS += -r +override CFLAGS += -O2 -Wall -g -D_GNU_SOURCE -I$(OUTPUT)include -I/usr/include/libnl3 +override LDFLAGS += -lnl-genl-3 -lnl-3 + +ALL_TARGETS := intel-speed-select +ALL_PROGRAMS := $(patsubst %,$(OUTPUT)%,$(ALL_TARGETS)) + +all: $(ALL_PROGRAMS) + +export srctree OUTPUT CC LD CFLAGS +include $(srctree)/tools/build/Makefile.include + +# +# We need the following to be outside of kernel tree +# +$(OUTPUT)include/linux/isst_if.h: ../../../../include/uapi/linux/isst_if.h + mkdir -p $(OUTPUT)include/linux 2>&1 || true + ln -sf $(CURDIR)/../../../../include/uapi/linux/isst_if.h $@ + +$(OUTPUT)include/linux/thermal.h: ../../../../include/uapi/linux/thermal.h + mkdir -p $(OUTPUT)include/linux 2>&1 || true + ln -sf $(CURDIR)/../../../../include/uapi/linux/thermal.h $@ + +prepare: $(OUTPUT)include/linux/isst_if.h $(OUTPUT)include/linux/thermal.h + +ISST_IN := $(OUTPUT)intel-speed-select-in.o + +$(ISST_IN): prepare FORCE + $(Q)$(MAKE) $(build)=intel-speed-select +$(OUTPUT)intel-speed-select: $(ISST_IN) + $(QUIET_LINK)$(CC) $(CFLAGS) $< $(LDFLAGS) -o $@ + +clean: + rm -f $(ALL_PROGRAMS) + rm -rf $(OUTPUT)include/linux/isst_if.h + find $(or $(OUTPUT),.) -name '*.o' -delete -o -name '\.*.d' -delete + +install: $(ALL_PROGRAMS) + install -d -m 755 $(DESTDIR)$(bindir); \ + for program in $(ALL_PROGRAMS); do \ + install $$program $(DESTDIR)$(bindir); \ + done + +FORCE: + +.PHONY: all install clean FORCE prepare diff --git a/tools/power/x86/intel-speed-select/hfi-events.c b/tools/power/x86/intel-speed-select/hfi-events.c new file mode 100644 index 0000000000..174b99d9f8 --- /dev/null +++ b/tools/power/x86/intel-speed-select/hfi-events.c @@ -0,0 +1,308 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Intel Speed Select -- Read HFI events for OOB + * Copyright (c) 2022 Intel Corporation. + */ + +/* + * This file incorporates work covered by the following copyright and + * permission notice: + + * WPA Supplicant - driver interaction with Linux nl80211/cfg80211 + * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of + * BSD license. + * + * Requires + * libnl-genl-3-dev + * + * For Fedora/CenOS + * dnf install libnl3-devel + * For Ubuntu + * apt install libnl-3-dev libnl-genl-3-dev + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/file.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> +#include <getopt.h> +#include <signal.h> +#include <netlink/genl/genl.h> +#include <netlink/genl/family.h> +#include <netlink/genl/ctrl.h> + +#include <linux/thermal.h> +#include "isst.h" + +struct hfi_event_data { + struct nl_sock *nl_handle; + struct nl_cb *nl_cb; +}; + +struct hfi_event_data drv; + +static int ack_handler(struct nl_msg *msg, void *arg) +{ + int *err = arg; + *err = 0; + return NL_STOP; +} + +static int finish_handler(struct nl_msg *msg, void *arg) +{ + int *ret = arg; + *ret = 0; + return NL_SKIP; +} + +static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, + void *arg) +{ + int *ret = arg; + *ret = err->error; + return NL_SKIP; +} + +static int seq_check_handler(struct nl_msg *msg, void *arg) +{ + return NL_OK; +} + +static int send_and_recv_msgs(struct hfi_event_data *drv, + struct nl_msg *msg, + int (*valid_handler)(struct nl_msg *, void *), + void *valid_data) +{ + struct nl_cb *cb; + int err = -ENOMEM; + + cb = nl_cb_clone(drv->nl_cb); + if (!cb) + goto out; + + err = nl_send_auto_complete(drv->nl_handle, msg); + if (err < 0) + goto out; + + err = 1; + + nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err); + nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err); + nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &err); + + if (valid_handler) + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, + valid_handler, valid_data); + + while (err > 0) + nl_recvmsgs(drv->nl_handle, cb); + out: + nl_cb_put(cb); + nlmsg_free(msg); + return err; +} + +struct family_data { + const char *group; + int id; +}; + +static int family_handler(struct nl_msg *msg, void *arg) +{ + struct family_data *res = arg; + struct nlattr *tb[CTRL_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *mcgrp; + int i; + + nla_parse(tb, CTRL_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + if (!tb[CTRL_ATTR_MCAST_GROUPS]) + return NL_SKIP; + + nla_for_each_nested(mcgrp, tb[CTRL_ATTR_MCAST_GROUPS], i) { + struct nlattr *tb2[CTRL_ATTR_MCAST_GRP_MAX + 1]; + nla_parse(tb2, CTRL_ATTR_MCAST_GRP_MAX, nla_data(mcgrp), + nla_len(mcgrp), NULL); + if (!tb2[CTRL_ATTR_MCAST_GRP_NAME] || + !tb2[CTRL_ATTR_MCAST_GRP_ID] || + strncmp(nla_data(tb2[CTRL_ATTR_MCAST_GRP_NAME]), + res->group, + nla_len(tb2[CTRL_ATTR_MCAST_GRP_NAME])) != 0) + continue; + res->id = nla_get_u32(tb2[CTRL_ATTR_MCAST_GRP_ID]); + break; + } + + return 0; +} + +static int nl_get_multicast_id(struct hfi_event_data *drv, + const char *family, const char *group) +{ + struct nl_msg *msg; + int ret = -1; + struct family_data res = { group, -ENOENT }; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + genlmsg_put(msg, 0, 0, genl_ctrl_resolve(drv->nl_handle, "nlctrl"), + 0, 0, CTRL_CMD_GETFAMILY, 0); + NLA_PUT_STRING(msg, CTRL_ATTR_FAMILY_NAME, family); + + ret = send_and_recv_msgs(drv, msg, family_handler, &res); + msg = NULL; + if (ret == 0) + ret = res.id; + +nla_put_failure: + nlmsg_free(msg); + return ret; +} + +struct perf_cap { + int cpu; + int perf; + int eff; +}; + +static void process_hfi_event(struct perf_cap *perf_cap) +{ + struct isst_id id; + + set_isst_id(&id, perf_cap->cpu); + process_level_change(&id); +} + +static int handle_event(struct nl_msg *n, void *arg) +{ + struct nlmsghdr *nlh = nlmsg_hdr(n); + struct genlmsghdr *genlhdr = genlmsg_hdr(nlh); + struct nlattr *attrs[THERMAL_GENL_ATTR_MAX + 1]; + int ret; + struct perf_cap perf_cap = {0}; + + ret = genlmsg_parse(nlh, 0, attrs, THERMAL_GENL_ATTR_MAX, NULL); + + debug_printf("Received event %d parse_rer:%d\n", genlhdr->cmd, ret); + if (genlhdr->cmd == THERMAL_GENL_EVENT_CPU_CAPABILITY_CHANGE) { + struct nlattr *cap; + int j, index = 0; + + debug_printf("THERMAL_GENL_EVENT_CPU_CAPABILITY_CHANGE\n"); + nla_for_each_nested(cap, attrs[THERMAL_GENL_ATTR_CPU_CAPABILITY], j) { + switch (index) { + case 0: + perf_cap.cpu = nla_get_u32(cap); + break; + case 1: + perf_cap.perf = nla_get_u32(cap); + break; + case 2: + perf_cap.eff = nla_get_u32(cap); + break; + default: + break; + } + ++index; + if (index == 3) { + index = 0; + process_hfi_event(&perf_cap); + } + } + } + + return 0; +} + +static int _hfi_exit; + +static int check_hf_suport(void) +{ + unsigned int eax = 0, ebx = 0, ecx = 0, edx = 0; + + __cpuid(6, eax, ebx, ecx, edx); + if (eax & BIT(19)) + return 1; + + return 0; +} + +int hfi_main(void) +{ + struct nl_sock *sock; + struct nl_cb *cb; + int err = 0; + int mcast_id; + + if (!check_hf_suport()) { + fprintf(stderr, "CPU Doesn't support HFI\n"); + return -1; + } + + sock = nl_socket_alloc(); + if (!sock) { + fprintf(stderr, "nl_socket_alloc failed\n"); + return -1; + } + + if (genl_connect(sock)) { + fprintf(stderr, "genl_connect(sk_event) failed\n"); + goto free_sock; + } + + drv.nl_handle = sock; + drv.nl_cb = cb = nl_cb_alloc(NL_CB_DEFAULT); + if (drv.nl_cb == NULL) { + printf("Failed to allocate netlink callbacks"); + goto free_sock; + } + + mcast_id = nl_get_multicast_id(&drv, THERMAL_GENL_FAMILY_NAME, + THERMAL_GENL_EVENT_GROUP_NAME); + if (mcast_id < 0) { + fprintf(stderr, "nl_get_multicast_id failed\n"); + goto free_sock; + } + + if (nl_socket_add_membership(sock, mcast_id)) { + fprintf(stderr, "nl_socket_add_membership failed"); + goto free_sock; + } + + nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, seq_check_handler, 0); + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, handle_event, NULL); + + debug_printf("hfi is initialized\n"); + + while (!_hfi_exit && !err) { + err = nl_recvmsgs(sock, cb); + debug_printf("nl_recv_message err:%d\n", err); + } + + return 0; + + /* Netlink library doesn't have calls to dealloc cb or disconnect */ +free_sock: + nl_socket_free(sock); + + return -1; +} + +void hfi_exit(void) +{ + _hfi_exit = 1; +} diff --git a/tools/power/x86/intel-speed-select/isst-config.c b/tools/power/x86/intel-speed-select/isst-config.c new file mode 100644 index 0000000000..5fcc2a9295 --- /dev/null +++ b/tools/power/x86/intel-speed-select/isst-config.c @@ -0,0 +1,3187 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Intel Speed Select -- Enumerate and control features + * Copyright (c) 2019 Intel Corporation. + */ + +#include <linux/isst_if.h> +#include <sys/utsname.h> + +#include "isst.h" + +struct process_cmd_struct { + char *feature; + char *command; + void (*process_fn)(int arg); + int arg; +}; + +static const char *version_str = "v1.17"; + +static const int supported_api_ver = 2; +static struct isst_if_platform_info isst_platform_info; +static char *progname; +static int debug_flag; +static FILE *outf; + +static int cpu_model; +static int cpu_stepping; + +#define MAX_CPUS_IN_ONE_REQ 256 +static short max_target_cpus; +static unsigned short target_cpus[MAX_CPUS_IN_ONE_REQ]; + +static int topo_max_cpus; +static size_t present_cpumask_size; +static cpu_set_t *present_cpumask; +static size_t target_cpumask_size; +static cpu_set_t *target_cpumask; +static int tdp_level = 0xFF; +static int fact_bucket = 0xFF; +static int fact_avx = 0xFF; +static unsigned long long fact_trl; +static int out_format_json; +static int cmd_help; +static int force_online_offline; +static int auto_mode; +static int fact_enable_fail; +static int cgroupv2; + +/* clos related */ +static int current_clos = -1; +static int clos_epp = -1; +static int clos_prop_prio = -1; +static int clos_min = -1; +static int clos_max = -1; +static int clos_desired = -1; +static int clos_priority_type; + +struct _cpu_map { + unsigned short core_id; + unsigned short pkg_id; + unsigned short die_id; + unsigned short punit_id; + unsigned short punit_cpu; + unsigned short punit_cpu_core; + unsigned short initialized; +}; +struct _cpu_map *cpu_map; + +struct cpu_topology { + short cpu; + short core_id; + short pkg_id; + short die_id; +}; + +FILE *get_output_file(void) +{ + return outf; +} + +int is_debug_enabled(void) +{ + return debug_flag; +} + +void debug_printf(const char *format, ...) +{ + va_list args; + + va_start(args, format); + + if (debug_flag) + vprintf(format, args); + + va_end(args); +} + + +int is_clx_n_platform(void) +{ + if (cpu_model == 0x55) + if (cpu_stepping == 0x6 || cpu_stepping == 0x7) + return 1; + return 0; +} + +int is_skx_based_platform(void) +{ + if (cpu_model == 0x55) + return 1; + + return 0; +} + +int is_spr_platform(void) +{ + if (cpu_model == 0x8F) + return 1; + + return 0; +} + +int is_emr_platform(void) +{ + if (cpu_model == 0xCF) + return 1; + + return 0; +} + + +int is_icx_platform(void) +{ + if (cpu_model == 0x6A || cpu_model == 0x6C) + return 1; + + return 0; +} + +static int update_cpu_model(void) +{ + unsigned int ebx, ecx, edx; + unsigned int fms, family; + + __cpuid(1, fms, ebx, ecx, edx); + family = (fms >> 8) & 0xf; + cpu_model = (fms >> 4) & 0xf; + if (family == 6 || family == 0xf) + cpu_model += ((fms >> 16) & 0xf) << 4; + + cpu_stepping = fms & 0xf; + /* only three CascadeLake-N models are supported */ + if (is_clx_n_platform()) { + FILE *fp; + size_t n = 0; + char *line = NULL; + int ret = 1; + + fp = fopen("/proc/cpuinfo", "r"); + if (!fp) + err(-1, "cannot open /proc/cpuinfo\n"); + + while (getline(&line, &n, fp) > 0) { + if (strstr(line, "model name")) { + if (strstr(line, "6252N") || + strstr(line, "6230N") || + strstr(line, "5218N")) + ret = 0; + break; + } + } + free(line); + fclose(fp); + return ret; + } + return 0; +} + +int api_version(void) +{ + return isst_platform_info.api_version; +} + +/* Open a file, and exit on failure */ +static FILE *fopen_or_exit(const char *path, const char *mode) +{ + FILE *filep = fopen(path, mode); + + if (!filep) + err(1, "%s: open failed", path); + + return filep; +} + +/* Parse a file containing a single int */ +static int parse_int_file(int fatal, const char *fmt, ...) +{ + va_list args; + char path[PATH_MAX]; + FILE *filep; + int value; + + va_start(args, fmt); + vsnprintf(path, sizeof(path), fmt, args); + va_end(args); + if (fatal) { + filep = fopen_or_exit(path, "r"); + } else { + filep = fopen(path, "r"); + if (!filep) + return -1; + } + if (fscanf(filep, "%d", &value) != 1) + err(1, "%s: failed to parse number from file", path); + fclose(filep); + + return value; +} + +int cpufreq_sysfs_present(void) +{ + DIR *dir; + + dir = opendir("/sys/devices/system/cpu/cpu0/cpufreq"); + if (dir) { + closedir(dir); + return 1; + } + + return 0; +} + +int out_format_is_json(void) +{ + return out_format_json; +} + +static int get_stored_topology_info(int cpu, int *core_id, int *pkg_id, int *die_id) +{ + const char *pathname = "/var/run/isst_cpu_topology.dat"; + struct cpu_topology cpu_top; + FILE *fp; + int ret; + + fp = fopen(pathname, "rb"); + if (!fp) + return -1; + + ret = fseek(fp, cpu * sizeof(cpu_top), SEEK_SET); + if (ret) + goto err_ret; + + ret = fread(&cpu_top, sizeof(cpu_top), 1, fp); + if (ret != 1) { + ret = -1; + goto err_ret; + } + + *pkg_id = cpu_top.pkg_id; + *core_id = cpu_top.core_id; + *die_id = cpu_top.die_id; + ret = 0; + +err_ret: + fclose(fp); + + return ret; +} + +static void store_cpu_topology(void) +{ + const char *pathname = "/var/run/isst_cpu_topology.dat"; + FILE *fp; + int i; + + fp = fopen(pathname, "rb"); + if (fp) { + /* Mapping already exists */ + fclose(fp); + return; + } + + fp = fopen(pathname, "wb"); + if (!fp) { + fprintf(stderr, "Can't create file:%s\n", pathname); + return; + } + + fprintf(stderr, "Caching topology information\n"); + + for (i = 0; i < topo_max_cpus; ++i) { + struct cpu_topology cpu_top; + + cpu_top.core_id = parse_int_file(0, + "/sys/devices/system/cpu/cpu%d/topology/core_id", i); + if (cpu_top.core_id < 0) + cpu_top.core_id = -1; + + cpu_top.pkg_id = parse_int_file(0, + "/sys/devices/system/cpu/cpu%d/topology/physical_package_id", i); + if (cpu_top.pkg_id < 0) + cpu_top.pkg_id = -1; + + cpu_top.die_id = parse_int_file(0, + "/sys/devices/system/cpu/cpu%d/topology/die_id", i); + if (cpu_top.die_id < 0) + cpu_top.die_id = -1; + + cpu_top.cpu = i; + + if (fwrite(&cpu_top, sizeof(cpu_top), 1, fp) != 1) { + fprintf(stderr, "Can't write to:%s\n", pathname); + break; + } + } + + fclose(fp); +} + +static int get_physical_package_id(int cpu) +{ + int ret; + + if (cpu < 0) + return -1; + + if (cpu_map && cpu_map[cpu].initialized) + return cpu_map[cpu].pkg_id; + + ret = parse_int_file(0, + "/sys/devices/system/cpu/cpu%d/topology/physical_package_id", + cpu); + if (ret < 0) { + int core_id, pkg_id, die_id; + + ret = get_stored_topology_info(cpu, &core_id, &pkg_id, &die_id); + if (!ret) + return pkg_id; + } + + return ret; +} + +static int get_physical_core_id(int cpu) +{ + int ret; + + if (cpu < 0) + return -1; + + if (cpu_map && cpu_map[cpu].initialized) + return cpu_map[cpu].core_id; + + ret = parse_int_file(0, + "/sys/devices/system/cpu/cpu%d/topology/core_id", + cpu); + if (ret < 0) { + int core_id, pkg_id, die_id; + + ret = get_stored_topology_info(cpu, &core_id, &pkg_id, &die_id); + if (!ret) + return core_id; + } + + return ret; +} + +static int get_physical_die_id(int cpu) +{ + int ret; + + if (cpu < 0) + return -1; + + if (cpu_map && cpu_map[cpu].initialized) + return cpu_map[cpu].die_id; + + ret = parse_int_file(0, + "/sys/devices/system/cpu/cpu%d/topology/die_id", + cpu); + if (ret < 0) { + int core_id, pkg_id, die_id; + + ret = get_stored_topology_info(cpu, &core_id, &pkg_id, &die_id); + if (!ret) { + if (die_id < 0) + die_id = 0; + + return die_id; + } + } + + if (ret < 0) + ret = 0; + + return ret; +} + +static int get_physical_punit_id(int cpu) +{ + if (cpu < 0) + return -1; + + if (cpu_map && cpu_map[cpu].initialized) + return cpu_map[cpu].punit_id; + + return -1; +} + +void set_isst_id(struct isst_id *id, int cpu) +{ + id->cpu = cpu; + + id->pkg = get_physical_package_id(cpu); + if (id->pkg >= MAX_PACKAGE_COUNT) + id->pkg = -1; + + id->die = get_physical_die_id(cpu); + if (id->die >= MAX_DIE_PER_PACKAGE) + id->die = -1; + + id->punit = get_physical_punit_id(cpu); + if (id->punit >= MAX_PUNIT_PER_DIE) + id->punit = -1; +} + +int is_cpu_in_power_domain(int cpu, struct isst_id *id) +{ + struct isst_id tid; + + set_isst_id(&tid, cpu); + + if (id->pkg == tid.pkg && id->die == tid.die && id->punit == tid.punit) + return 1; + + return 0; +} + +int get_cpufreq_base_freq(int cpu) +{ + return parse_int_file(0, "/sys/devices/system/cpu/cpu%d/cpufreq/base_frequency", cpu); +} + +int get_topo_max_cpus(void) +{ + return topo_max_cpus; +} + +static unsigned int is_cpu_online(int cpu) +{ + char buffer[128]; + int fd, ret; + unsigned char online; + + snprintf(buffer, sizeof(buffer), + "/sys/devices/system/cpu/cpu%d/online", cpu); + + fd = open(buffer, O_RDONLY); + if (fd < 0) + return fd; + + ret = read(fd, &online, sizeof(online)); + close(fd); + + if (ret == -1) + return ret; + + if (online == '1') + online = 1; + else + online = 0; + + return online; +} + +static int get_kernel_version(int *major, int *minor) +{ + struct utsname buf; + int ret; + + ret = uname(&buf); + if (ret) + return ret; + + ret = sscanf(buf.release, "%d.%d", major, minor); + if (ret != 2) + return ret; + + return 0; +} + +#define CPU0_HOTPLUG_DEPRECATE_MAJOR_VER 6 +#define CPU0_HOTPLUG_DEPRECATE_MINOR_VER 5 + +void set_cpu_online_offline(int cpu, int state) +{ + char buffer[128]; + int fd, ret; + + if (!cpu) { + int major, minor; + + ret = get_kernel_version(&major, &minor); + if (!ret) { + if (major > CPU0_HOTPLUG_DEPRECATE_MAJOR_VER || (major == CPU0_HOTPLUG_DEPRECATE_MAJOR_VER && + minor >= CPU0_HOTPLUG_DEPRECATE_MINOR_VER)) { + debug_printf("Ignore CPU 0 offline/online for kernel version >= %d.%d\n", major, minor); + debug_printf("Use cgroups to isolate CPU 0\n"); + return; + } + } + } + + snprintf(buffer, sizeof(buffer), + "/sys/devices/system/cpu/cpu%d/online", cpu); + + fd = open(buffer, O_WRONLY); + if (fd < 0) { + if (!cpu && state) { + fprintf(stderr, "This system is not configured for CPU 0 online/offline\n"); + fprintf(stderr, "Ignoring online request for CPU 0 as this is already online\n"); + return; + } + err(-1, "%s open failed", buffer); + } + + if (state) + ret = write(fd, "1\n", 2); + else + ret = write(fd, "0\n", 2); + + if (ret == -1) + perror("Online/Offline: Operation failed\n"); + + close(fd); +} + +static void force_all_cpus_online(void) +{ + int i; + + fprintf(stderr, "Forcing all CPUs online\n"); + + for (i = 0; i < topo_max_cpus; ++i) + set_cpu_online_offline(i, 1); + + unlink("/var/run/isst_cpu_topology.dat"); +} + +void for_each_online_power_domain_in_set(void (*callback)(struct isst_id *, void *, void *, + void *, void *), + void *arg1, void *arg2, void *arg3, + void *arg4) +{ + struct isst_id id; + int cpus[MAX_PACKAGE_COUNT][MAX_DIE_PER_PACKAGE][MAX_PUNIT_PER_DIE]; + int valid_mask[MAX_PACKAGE_COUNT][MAX_DIE_PER_PACKAGE] = {0}; + int i, j, k; + + memset(cpus, -1, sizeof(cpus)); + + for (i = 0; i < topo_max_cpus; ++i) { + int online; + + if (!CPU_ISSET_S(i, present_cpumask_size, present_cpumask)) + continue; + + online = parse_int_file( + i != 0, "/sys/devices/system/cpu/cpu%d/online", i); + if (online < 0) + online = 1; /* online entry for CPU 0 needs some special configs */ + + if (!online) + continue; + + set_isst_id(&id, i); + + if (id.pkg < 0 || id.die < 0 || id.punit < 0) + continue; + + valid_mask[id.pkg][id.die] = 1; + + if (cpus[id.pkg][id.die][id.punit] == -1) + cpus[id.pkg][id.die][id.punit] = i; + } + + for (i = 0; i < MAX_PACKAGE_COUNT; i++) { + for (j = 0; j < MAX_DIE_PER_PACKAGE; j++) { + /* + * Fix me: + * How to check a non-cpu die for a package/die with all cpu offlined? + */ + if (!valid_mask[i][j]) + continue; + for (k = 0; k < MAX_PUNIT_PER_DIE; k++) { + id.cpu = cpus[i][j][k]; + id.pkg = i; + id.die = j; + id.punit = k; + if (isst_is_punit_valid(&id)) + callback(&id, arg1, arg2, arg3, arg4); + } + } + } +} + +static void for_each_online_target_cpu_in_set( + void (*callback)(struct isst_id *, void *, void *, void *, void *), void *arg1, + void *arg2, void *arg3, void *arg4) +{ + int i, found = 0; + struct isst_id id; + + for (i = 0; i < topo_max_cpus; ++i) { + int online; + + if (!CPU_ISSET_S(i, target_cpumask_size, target_cpumask)) + continue; + if (i) + online = parse_int_file( + 1, "/sys/devices/system/cpu/cpu%d/online", i); + else + online = + 1; /* online entry for CPU 0 needs some special configs */ + + set_isst_id(&id, i); + if (online && callback) { + callback(&id, arg1, arg2, arg3, arg4); + found = 1; + } + } + + if (!found) + fprintf(stderr, "No valid CPU in the list\n"); +} + +#define BITMASK_SIZE 32 +static void set_max_cpu_num(void) +{ + FILE *filep; + unsigned long dummy; + int i; + + topo_max_cpus = 0; + for (i = 0; i < 256; ++i) { + char path[256]; + + snprintf(path, sizeof(path), + "/sys/devices/system/cpu/cpu%d/topology/thread_siblings", i); + filep = fopen(path, "r"); + if (filep) + break; + } + + if (!filep) { + fprintf(stderr, "Can't get max cpu number\n"); + exit(0); + } + + while (fscanf(filep, "%lx,", &dummy) == 1) + topo_max_cpus += BITMASK_SIZE; + fclose(filep); + + debug_printf("max cpus %d\n", topo_max_cpus); +} + +size_t alloc_cpu_set(cpu_set_t **cpu_set) +{ + cpu_set_t *_cpu_set; + size_t size; + + _cpu_set = CPU_ALLOC((topo_max_cpus + 1)); + if (_cpu_set == NULL) + err(3, "CPU_ALLOC"); + size = CPU_ALLOC_SIZE((topo_max_cpus + 1)); + CPU_ZERO_S(size, _cpu_set); + + *cpu_set = _cpu_set; + return size; +} + +void free_cpu_set(cpu_set_t *cpu_set) +{ + CPU_FREE(cpu_set); +} + +static int cpu_cnt[MAX_PACKAGE_COUNT][MAX_DIE_PER_PACKAGE][MAX_PUNIT_PER_DIE]; + +int get_max_punit_core_id(struct isst_id *id) +{ + int max_id = 0; + int i; + + for (i = 0; i < topo_max_cpus; ++i) + { + if (!CPU_ISSET_S(i, present_cpumask_size, present_cpumask)) + continue; + + if (is_cpu_in_power_domain(i, id) && + cpu_map[i].punit_cpu_core > max_id) + max_id = cpu_map[i].punit_cpu_core; + } + + return max_id; +} + +int get_cpu_count(struct isst_id *id) +{ + if (id->pkg < 0 || id->die < 0 || id->punit < 0) + return 0; + + return cpu_cnt[id->pkg][id->die][id->punit]; +} + +static void update_punit_cpu_info(__u32 physical_cpu, struct _cpu_map *cpu_map) +{ + if (api_version() > 1) { + /* + * MSR 0x54 format + * [15:11] PM_DOMAIN_ID + * [10:3] MODULE_ID (aka IDI_AGENT_ID) + * [2:0] LP_ID (We don't care about these bits we only + * care die and core id + * For Atom: + * [2] Always 0 + * [1:0] core ID within module + * For Core + * [2:1] Always 0 + * [0] thread ID + */ + cpu_map->punit_id = (physical_cpu >> 11) & 0x1f; + cpu_map->punit_cpu_core = (physical_cpu >> 3) & 0xff; + cpu_map->punit_cpu = physical_cpu & 0x7ff; + } else { + int punit_id; + + /* + * MSR 0x53 format + * Format + * Bit 0 – thread ID + * Bit 8:1 – core ID + * Bit 13:9 – punit ID + */ + cpu_map->punit_cpu = physical_cpu & 0x1ff; + cpu_map->punit_cpu_core = (cpu_map->punit_cpu >> 1); // shift to get core id + punit_id = (physical_cpu >> 9) & 0x1f; + + if (punit_id >= MAX_PUNIT_PER_DIE) + punit_id = 0; + + cpu_map->punit_id = punit_id; + } +} + +static void create_cpu_map(void) +{ + const char *pathname = "/dev/isst_interface"; + size_t size; + DIR *dir; + int i, fd = 0; + struct isst_if_cpu_maps map; + + /* Use calloc to make sure the memory is initialized to Zero */ + cpu_map = calloc(topo_max_cpus, sizeof(*cpu_map)); + if (!cpu_map) + err(3, "cpumap"); + + fd = open(pathname, O_RDWR); + if (fd < 0 && !is_clx_n_platform()) + err(-1, "%s open failed", pathname); + + size = alloc_cpu_set(&present_cpumask); + present_cpumask_size = size; + + for (i = 0; i < topo_max_cpus; ++i) { + char buffer[256]; + int pkg_id, die_id, core_id, punit_id; + + /* check if CPU is online */ + snprintf(buffer, sizeof(buffer), + "/sys/devices/system/cpu/cpu%d", i); + dir = opendir(buffer); + if (!dir) + continue; + closedir(dir); + + CPU_SET_S(i, size, present_cpumask); + + pkg_id = get_physical_package_id(i); + die_id = get_physical_die_id(i); + core_id = get_physical_core_id(i); + + if (pkg_id < 0 || die_id < 0 || core_id < 0) + continue; + + cpu_map[i].pkg_id = pkg_id; + cpu_map[i].die_id = die_id; + cpu_map[i].core_id = core_id; + + + punit_id = 0; + + if (fd >= 0) { + map.cmd_count = 1; + map.cpu_map[0].logical_cpu = i; + debug_printf(" map logical_cpu:%d\n", + map.cpu_map[0].logical_cpu); + if (ioctl(fd, ISST_IF_GET_PHY_ID, &map) == -1) { + perror("ISST_IF_GET_PHY_ID"); + fprintf(outf, "Error: map logical_cpu:%d\n", + map.cpu_map[0].logical_cpu); + } else { + update_punit_cpu_info(map.cpu_map[0].physical_cpu, &cpu_map[i]); + punit_id = cpu_map[i].punit_id; + } + } + cpu_map[i].initialized = 1; + + cpu_cnt[pkg_id][die_id][punit_id]++; + + debug_printf( + "map logical_cpu:%d core: %d die:%d pkg:%d punit:%d punit_cpu:%d punit_core:%d\n", + i, cpu_map[i].core_id, cpu_map[i].die_id, + cpu_map[i].pkg_id, cpu_map[i].punit_id, + cpu_map[i].punit_cpu, cpu_map[i].punit_cpu_core); + } + if (fd >= 0) + close(fd); + + size = alloc_cpu_set(&target_cpumask); + target_cpumask_size = size; + for (i = 0; i < max_target_cpus; ++i) { + if (!CPU_ISSET_S(target_cpus[i], present_cpumask_size, + present_cpumask)) + continue; + + CPU_SET_S(target_cpus[i], size, target_cpumask); + } +} + +void set_cpu_mask_from_punit_coremask(struct isst_id *id, unsigned long long core_mask, + size_t core_cpumask_size, + cpu_set_t *core_cpumask, int *cpu_cnt) +{ + int i, cnt = 0; + + if (id->cpu < 0) + return; + + *cpu_cnt = 0; + + for (i = 0; i < 64; ++i) { + if (core_mask & BIT_ULL(i)) { + int j; + + for (j = 0; j < topo_max_cpus; ++j) { + if (!CPU_ISSET_S(j, present_cpumask_size, present_cpumask)) + continue; + + if (is_cpu_in_power_domain(j, id) && + cpu_map[j].punit_cpu_core == i) { + CPU_SET_S(j, core_cpumask_size, + core_cpumask); + ++cnt; + } + } + } + } + + *cpu_cnt = cnt; +} + +int find_phy_core_num(int logical_cpu) +{ + if (logical_cpu < topo_max_cpus) + return cpu_map[logical_cpu].punit_cpu_core; + + return -EINVAL; +} + +int use_cgroupv2(void) +{ + return cgroupv2; +} + +int enable_cpuset_controller(void) +{ + int fd, ret; + + fd = open("/sys/fs/cgroup/cgroup.subtree_control", O_RDWR, 0); + if (fd < 0) { + debug_printf("Can't activate cpuset controller\n"); + debug_printf("Either you are not root user or CGroup v2 is not supported\n"); + return fd; + } + + ret = write(fd, " +cpuset", strlen(" +cpuset")); + close(fd); + + if (ret == -1) { + debug_printf("Can't activate cpuset controller: Write failed\n"); + return ret; + } + + return 0; +} + +int isolate_cpus(struct isst_id *id, int mask_size, cpu_set_t *cpu_mask, int level) +{ + int i, first, curr_index, index, ret, fd; + static char str[512], dir_name[64]; + static char cpuset_cpus[128]; + int str_len = sizeof(str); + DIR *dir; + + snprintf(dir_name, sizeof(dir_name), "/sys/fs/cgroup/%d-%d-%d", id->pkg, id->die, id->punit); + dir = opendir(dir_name); + if (!dir) { + ret = mkdir(dir_name, 0744); + if (ret) { + debug_printf("Can't create dir:%s errno:%d\n", dir_name, errno); + return ret; + } + } + closedir(dir); + + if (!level) { + sprintf(cpuset_cpus, "%s/cpuset.cpus.partition", dir_name); + + fd = open(cpuset_cpus, O_RDWR, 0); + if (fd < 0) { + return fd; + } + + ret = write(fd, "member", strlen("member")); + if (ret == -1) { + printf("Can't update to member\n"); + return ret; + } + + return 0; + } + + if (!CPU_COUNT_S(mask_size, cpu_mask)) { + return -1; + } + + curr_index = 0; + first = 1; + str[0] = '\0'; + for (i = 0; i < get_topo_max_cpus(); ++i) { + if (!is_cpu_in_power_domain(i, id)) + continue; + + if (CPU_ISSET_S(i, mask_size, cpu_mask)) + continue; + + if (!first) { + index = snprintf(&str[curr_index], + str_len - curr_index, ","); + curr_index += index; + if (curr_index >= str_len) + break; + } + index = snprintf(&str[curr_index], str_len - curr_index, "%d", + i); + curr_index += index; + if (curr_index >= str_len) + break; + first = 0; + } + + debug_printf("isolated CPUs list: package:%d curr_index:%d [%s]\n", id->pkg, curr_index ,str); + + snprintf(cpuset_cpus, sizeof(cpuset_cpus), "%s/cpuset.cpus", dir_name); + + fd = open(cpuset_cpus, O_RDWR, 0); + if (fd < 0) { + return fd; + } + + ret = write(fd, str, strlen(str)); + close(fd); + + if (ret == -1) { + debug_printf("Can't activate cpuset controller: Write failed\n"); + return ret; + } + + snprintf(cpuset_cpus, sizeof(cpuset_cpus), "%s/cpuset.cpus.partition", dir_name); + + fd = open(cpuset_cpus, O_RDWR, 0); + if (fd < 0) { + return fd; + } + + ret = write(fd, "isolated", strlen("isolated")); + if (ret == -1) { + debug_printf("Can't update to isolated\n"); + ret = write(fd, "root", strlen("root")); + if (ret == -1) + debug_printf("Can't update to root\n"); + } + + close(fd); + + if (ret < 0) + return ret; + + return 0; +} + +static int isst_fill_platform_info(void) +{ + const char *pathname = "/dev/isst_interface"; + int fd; + + if (is_clx_n_platform()) { + isst_platform_info.api_version = 1; + goto set_platform_ops; + } + + fd = open(pathname, O_RDWR); + if (fd < 0) + err(-1, "%s open failed", pathname); + + if (ioctl(fd, ISST_IF_GET_PLATFORM_INFO, &isst_platform_info) == -1) { + perror("ISST_IF_GET_PLATFORM_INFO"); + close(fd); + return -1; + } + + close(fd); + + if (isst_platform_info.api_version > supported_api_ver) { + printf("Incompatible API versions; Upgrade of tool is required\n"); + return -1; + } + +set_platform_ops: + if (isst_set_platform_ops(isst_platform_info.api_version)) { + fprintf(stderr, "Failed to set platform callbacks\n"); + exit(0); + } + return 0; +} + +void get_isst_status(struct isst_id *id, void *arg1, void *arg2, void *arg3, void *arg4) +{ + struct isst_pkg_ctdp pkg_dev; + struct isst_id *tid = (struct isst_id *)arg2; + int *mask = (int *)arg3; + int *max_level = (int *)arg4; + int j, ret; + + /* Only check the first cpu power domain */ + if (id->cpu < 0 || tid->cpu >= 0) + return; + + ret = isst_get_ctdp_levels(id, &pkg_dev); + if (ret) + return; + + if (pkg_dev.enabled) + *mask |= BIT(0); + + if (pkg_dev.locked) + *mask |= BIT(1); + + if (*max_level < pkg_dev.levels) + *max_level = pkg_dev.levels; + + for (j = 0; j <= pkg_dev.levels; ++j) { + struct isst_pkg_ctdp_level_info ctdp_level; + + ret = isst_get_ctdp_control(id, j, &ctdp_level); + if (ret) + continue; + + if (ctdp_level.fact_support) + *mask |= BIT(2); + + if (ctdp_level.pbf_support) + *mask |= BIT(3); + } + + tid->cpu = id->cpu; + tid->pkg = id->pkg; + tid->die = id->die; + tid->punit = id->punit; +} + +static void isst_print_extended_platform_info(void) +{ + int cp_state, cp_cap; + struct isst_id id; + int mask = 0, max_level = 0; + + id.cpu = -1; + for_each_online_power_domain_in_set(get_isst_status, NULL, &id, &mask, &max_level); + + if (mask & BIT(0)) { + fprintf(outf, "Intel(R) SST-PP (feature perf-profile) is supported\n"); + } else { + fprintf(outf, "Intel(R) SST-PP (feature perf-profile) is not supported\n"); + fprintf(outf, "Only performance level 0 (base level) is present\n"); + } + + if (mask & BIT(1)) + fprintf(outf, "TDP level change control is locked\n"); + else + fprintf(outf, "TDP level change control is unlocked, max level: %d\n", max_level); + + if (mask & BIT(2)) + fprintf(outf, "Intel(R) SST-TF (feature turbo-freq) is supported\n"); + else + fprintf(outf, "Intel(R) SST-TF (feature turbo-freq) is not supported\n"); + + if (mask & BIT(3)) + fprintf(outf, "Intel(R) SST-BF (feature base-freq) is supported\n"); + else + fprintf(outf, "Intel(R) SST-BF (feature base-freq) is not supported\n"); + + if (isst_read_pm_config(&id, &cp_state, &cp_cap)) { + fprintf(outf, "Intel(R) SST-CP (feature core-power) status is unknown\n"); + return; + } + + if (cp_cap) + fprintf(outf, "Intel(R) SST-CP (feature core-power) is supported\n"); + else + fprintf(outf, "Intel(R) SST-CP (feature core-power) is not supported\n"); +} + +static void isst_print_platform_information(void) +{ + if (is_clx_n_platform()) { + fprintf(stderr, "\nThis option in not supported on this platform\n"); + exit(0); + } + + /* Early initialization to create working cpu_map */ + set_max_cpu_num(); + create_cpu_map(); + + fprintf(outf, "Platform: API version : %d\n", + isst_platform_info.api_version); + fprintf(outf, "Platform: Driver version : %d\n", + isst_platform_info.driver_version); + fprintf(outf, "Platform: mbox supported : %d\n", + isst_platform_info.mbox_supported); + fprintf(outf, "Platform: mmio supported : %d\n", + isst_platform_info.mmio_supported); + isst_print_extended_platform_info(); + + exit(0); +} + +static char *local_str0, *local_str1; +static void exec_on_get_ctdp_cpu(struct isst_id *id, void *arg1, void *arg2, void *arg3, + void *arg4) +{ + int (*fn_ptr)(struct isst_id *id, void *arg); + int ret; + + fn_ptr = arg1; + ret = fn_ptr(id, arg2); + if (ret) + isst_display_error_info_message(1, "get_tdp_* failed", 0, 0); + else + isst_ctdp_display_core_info(id, outf, arg3, + *(unsigned int *)arg4, + local_str0, local_str1); +} + +#define _get_tdp_level(desc, suffix, object, help, str0, str1) \ + static void get_tdp_##object(int arg) \ + { \ + struct isst_pkg_ctdp ctdp; \ +\ + if (cmd_help) { \ + fprintf(stderr, \ + "Print %s [No command arguments are required]\n", \ + help); \ + exit(0); \ + } \ + local_str0 = str0; \ + local_str1 = str1; \ + isst_ctdp_display_information_start(outf); \ + if (max_target_cpus) \ + for_each_online_target_cpu_in_set( \ + exec_on_get_ctdp_cpu, isst_get_ctdp_##suffix, \ + &ctdp, desc, &ctdp.object); \ + else \ + for_each_online_power_domain_in_set(exec_on_get_ctdp_cpu, \ + isst_get_ctdp_##suffix, \ + &ctdp, desc, \ + &ctdp.object); \ + isst_ctdp_display_information_end(outf); \ + } + +_get_tdp_level("get-config-levels", levels, levels, "Max TDP level", NULL, NULL); +_get_tdp_level("get-config-version", levels, version, "TDP version", NULL, NULL); +_get_tdp_level("get-config-enabled", levels, enabled, "perf-profile enable status", "disabled", "enabled"); +_get_tdp_level("get-config-current_level", levels, current_level, + "Current TDP Level", NULL, NULL); +_get_tdp_level("get-lock-status", levels, locked, "TDP lock status", "unlocked", "locked"); + +struct isst_pkg_ctdp clx_n_pkg_dev; + +static int clx_n_get_base_ratio(void) +{ + FILE *fp; + char *begin, *end, *line = NULL; + char number[5]; + float value = 0; + size_t n = 0; + + fp = fopen("/proc/cpuinfo", "r"); + if (!fp) + err(-1, "cannot open /proc/cpuinfo\n"); + + while (getline(&line, &n, fp) > 0) { + if (strstr(line, "model name")) { + /* this is true for CascadeLake-N */ + begin = strstr(line, "@ ") + 2; + end = strstr(line, "GHz"); + strncpy(number, begin, end - begin); + value = atof(number) * 10; + break; + } + } + free(line); + fclose(fp); + + return (int)(value); +} + +static int clx_n_config(struct isst_id *id) +{ + int i, ret; + unsigned long cpu_bf; + struct isst_pkg_ctdp_level_info *ctdp_level; + struct isst_pbf_info *pbf_info; + + ctdp_level = &clx_n_pkg_dev.ctdp_level[0]; + pbf_info = &ctdp_level->pbf_info; + ctdp_level->core_cpumask_size = + alloc_cpu_set(&ctdp_level->core_cpumask); + + /* find the frequency base ratio */ + ctdp_level->tdp_ratio = clx_n_get_base_ratio(); + if (ctdp_level->tdp_ratio == 0) { + debug_printf("CLX: cn base ratio is zero\n"); + ret = -1; + goto error_ret; + } + + /* find the high and low priority frequencies */ + pbf_info->p1_high = 0; + pbf_info->p1_low = ~0; + + for (i = 0; i < topo_max_cpus; i++) { + if (!CPU_ISSET_S(i, present_cpumask_size, present_cpumask)) + continue; + + if (!is_cpu_in_power_domain(i, id)) + continue; + + CPU_SET_S(i, ctdp_level->core_cpumask_size, + ctdp_level->core_cpumask); + + cpu_bf = parse_int_file(1, + "/sys/devices/system/cpu/cpu%d/cpufreq/base_frequency", + i); + if (cpu_bf > pbf_info->p1_high) + pbf_info->p1_high = cpu_bf; + if (cpu_bf < pbf_info->p1_low) + pbf_info->p1_low = cpu_bf; + } + + if (pbf_info->p1_high == ~0UL) { + debug_printf("CLX: maximum base frequency not set\n"); + ret = -1; + goto error_ret; + } + + if (pbf_info->p1_low == 0) { + debug_printf("CLX: minimum base frequency not set\n"); + ret = -1; + goto error_ret; + } + + /* convert frequencies back to ratios */ + pbf_info->p1_high = pbf_info->p1_high / 100000; + pbf_info->p1_low = pbf_info->p1_low / 100000; + + /* create high priority cpu mask */ + pbf_info->core_cpumask_size = alloc_cpu_set(&pbf_info->core_cpumask); + for (i = 0; i < topo_max_cpus; i++) { + if (!CPU_ISSET_S(i, present_cpumask_size, present_cpumask)) + continue; + + if (!is_cpu_in_power_domain(i, id)) + continue; + + cpu_bf = parse_int_file(1, + "/sys/devices/system/cpu/cpu%d/cpufreq/base_frequency", + i); + cpu_bf = cpu_bf / 100000; + if (cpu_bf == pbf_info->p1_high) + CPU_SET_S(i, pbf_info->core_cpumask_size, + pbf_info->core_cpumask); + } + + /* extra ctdp & pbf struct parameters */ + ctdp_level->processed = 1; + ctdp_level->pbf_support = 1; /* PBF is always supported and enabled */ + ctdp_level->pbf_enabled = 1; + ctdp_level->fact_support = 0; /* FACT is never supported */ + ctdp_level->fact_enabled = 0; + + return 0; + +error_ret: + free_cpu_set(ctdp_level->core_cpumask); + return ret; +} + +static void dump_clx_n_config_for_cpu(struct isst_id *id, void *arg1, void *arg2, + void *arg3, void *arg4) +{ + int ret; + + if (tdp_level != 0xff && tdp_level != 0) { + isst_display_error_info_message(1, "Invalid level", 1, tdp_level); + exit(0); + } + + ret = clx_n_config(id); + if (ret) { + debug_printf("clx_n_config failed"); + } else { + struct isst_pkg_ctdp_level_info *ctdp_level; + struct isst_pbf_info *pbf_info; + + ctdp_level = &clx_n_pkg_dev.ctdp_level[0]; + pbf_info = &ctdp_level->pbf_info; + clx_n_pkg_dev.processed = 1; + isst_ctdp_display_information(id, outf, tdp_level, &clx_n_pkg_dev); + free_cpu_set(ctdp_level->core_cpumask); + free_cpu_set(pbf_info->core_cpumask); + } +} + +static void dump_isst_config_for_cpu(struct isst_id *id, void *arg1, void *arg2, + void *arg3, void *arg4) +{ + struct isst_pkg_ctdp pkg_dev; + int ret; + + memset(&pkg_dev, 0, sizeof(pkg_dev)); + ret = isst_get_process_ctdp(id, tdp_level, &pkg_dev); + if (ret) { + isst_display_error_info_message(1, "Failed to get perf-profile info on cpu", 1, id->cpu); + isst_ctdp_display_information_end(outf); + exit(1); + } else { + isst_ctdp_display_information(id, outf, tdp_level, &pkg_dev); + isst_get_process_ctdp_complete(id, &pkg_dev); + } +} + +static void dump_isst_config(int arg) +{ + void *fn; + + if (cmd_help) { + fprintf(stderr, + "Print Intel(R) Speed Select Technology Performance profile configuration\n"); + fprintf(stderr, + "including base frequency and turbo frequency configurations\n"); + fprintf(stderr, "Optional: -l|--level : Specify tdp level\n"); + fprintf(stderr, + "\tIf no arguments, dump information for all TDP levels\n"); + exit(0); + } + + if (!is_clx_n_platform()) + fn = dump_isst_config_for_cpu; + else + fn = dump_clx_n_config_for_cpu; + + isst_ctdp_display_information_start(outf); + + if (max_target_cpus) + for_each_online_target_cpu_in_set(fn, NULL, NULL, NULL, NULL); + else + for_each_online_power_domain_in_set(fn, NULL, NULL, NULL, NULL); + + isst_ctdp_display_information_end(outf); +} + +static void adjust_scaling_max_from_base_freq(int cpu); + +static void set_tdp_level_for_cpu(struct isst_id *id, void *arg1, void *arg2, void *arg3, + void *arg4) +{ + struct isst_pkg_ctdp pkg_dev; + int ret; + + ret = isst_get_ctdp_levels(id, &pkg_dev); + if (ret) { + isst_display_error_info_message(1, "Get TDP level failed", 0, 0); + isst_ctdp_display_information_end(outf); + exit(1); + } + + if (pkg_dev.current_level == tdp_level) { + debug_printf("TDP level already set. Skipped\n"); + goto display_result; + } + + ret = isst_set_tdp_level(id, tdp_level); + if (ret) { + isst_display_error_info_message(1, "Set TDP level failed", 0, 0); + isst_ctdp_display_information_end(outf); + exit(1); + } + +display_result: + isst_display_result(id, outf, "perf-profile", "set_tdp_level", ret); + if (force_online_offline && id->cpu >= 0) { + struct isst_pkg_ctdp_level_info ctdp_level; + + /* Wait for updated base frequencies */ + usleep(2000); + + /* Adjusting uncore freq */ + isst_adjust_uncore_freq(id, tdp_level, &ctdp_level); + + fprintf(stderr, "Option is set to online/offline\n"); + ctdp_level.core_cpumask_size = + alloc_cpu_set(&ctdp_level.core_cpumask); + ret = isst_get_coremask_info(id, tdp_level, &ctdp_level); + if (ret) { + isst_display_error_info_message(1, "Can't get coremask, online/offline option is ignored", 0, 0); + goto free_mask; + } + + if (use_cgroupv2()) { + int ret; + + fprintf(stderr, "Using cgroup v2 in lieu of online/offline\n"); + ret = enable_cpuset_controller(); + if (ret) + goto use_offline; + + ret = isolate_cpus(id, ctdp_level.core_cpumask_size, ctdp_level.core_cpumask, tdp_level); + if (ret) + goto use_offline; + + goto free_mask; + } + +use_offline: + if (ctdp_level.cpu_count) { + int i, max_cpus = get_topo_max_cpus(); + for (i = 0; i < max_cpus; ++i) { + if (!is_cpu_in_power_domain(i, id)) + continue; + if (CPU_ISSET_S(i, ctdp_level.core_cpumask_size, ctdp_level.core_cpumask)) { + fprintf(stderr, "online cpu %d\n", i); + set_cpu_online_offline(i, 1); + adjust_scaling_max_from_base_freq(i); + } else { + fprintf(stderr, "offline cpu %d\n", i); + set_cpu_online_offline(i, 0); + } + } + } +free_mask: + free_cpu_set(ctdp_level.core_cpumask); + } +} + +static void set_tdp_level(int arg) +{ + if (cmd_help) { + fprintf(stderr, "Set Config TDP level\n"); + fprintf(stderr, + "\t Arguments: -l|--level : Specify tdp level\n"); + fprintf(stderr, + "\t Optional Arguments: -o | online : online/offline for the tdp level\n"); + fprintf(stderr, + "\t online/offline operation has limitations, refer to Linux hotplug documentation\n"); + exit(0); + } + + if (tdp_level == 0xff) { + isst_display_error_info_message(1, "Invalid command: specify tdp_level", 0, 0); + exit(1); + } + isst_ctdp_display_information_start(outf); + if (max_target_cpus) + for_each_online_target_cpu_in_set(set_tdp_level_for_cpu, NULL, + NULL, NULL, NULL); + else + for_each_online_power_domain_in_set(set_tdp_level_for_cpu, NULL, + NULL, NULL, NULL); + isst_ctdp_display_information_end(outf); +} + +static void clx_n_dump_pbf_config_for_cpu(struct isst_id *id, void *arg1, void *arg2, + void *arg3, void *arg4) +{ + int ret; + + ret = clx_n_config(id); + if (ret) { + isst_display_error_info_message(1, "clx_n_config failed", 0, 0); + } else { + struct isst_pkg_ctdp_level_info *ctdp_level; + struct isst_pbf_info *pbf_info; + + ctdp_level = &clx_n_pkg_dev.ctdp_level[0]; + pbf_info = &ctdp_level->pbf_info; + isst_pbf_display_information(id, outf, tdp_level, pbf_info); + free_cpu_set(ctdp_level->core_cpumask); + free_cpu_set(pbf_info->core_cpumask); + } +} + +static void dump_pbf_config_for_cpu(struct isst_id *id, void *arg1, void *arg2, void *arg3, + void *arg4) +{ + struct isst_pbf_info pbf_info; + int ret; + + ret = isst_get_pbf_info(id, tdp_level, &pbf_info); + if (ret) { + isst_display_error_info_message(1, "Failed to get base-freq info at this level", 1, tdp_level); + isst_ctdp_display_information_end(outf); + exit(1); + } else { + isst_pbf_display_information(id, outf, tdp_level, &pbf_info); + free_cpu_set(pbf_info.core_cpumask); + } +} + +static void dump_pbf_config(int arg) +{ + void *fn; + + if (cmd_help) { + fprintf(stderr, + "Print Intel(R) Speed Select Technology base frequency configuration for a TDP level\n"); + fprintf(stderr, + "\tArguments: -l|--level : Specify tdp level\n"); + exit(0); + } + + if (tdp_level == 0xff) { + isst_display_error_info_message(1, "Invalid command: specify tdp_level", 0, 0); + exit(1); + } + + if (!is_clx_n_platform()) + fn = dump_pbf_config_for_cpu; + else + fn = clx_n_dump_pbf_config_for_cpu; + + isst_ctdp_display_information_start(outf); + + if (max_target_cpus) + for_each_online_target_cpu_in_set(fn, NULL, NULL, NULL, NULL); + else + for_each_online_power_domain_in_set(fn, NULL, NULL, NULL, NULL); + + isst_ctdp_display_information_end(outf); +} + +static int set_clos_param(struct isst_id *id, int clos, int epp, int wt, int min, int max) +{ + struct isst_clos_config clos_config; + int ret; + + ret = isst_pm_get_clos(id, clos, &clos_config); + if (ret) { + isst_display_error_info_message(1, "isst_pm_get_clos failed", 0, 0); + return ret; + } + clos_config.clos_min = min; + clos_config.clos_max = max; + clos_config.epp = epp; + clos_config.clos_prop_prio = wt; + ret = isst_set_clos(id, clos, &clos_config); + if (ret) { + isst_display_error_info_message(1, "isst_set_clos failed", 0, 0); + return ret; + } + + return 0; +} + +static int set_cpufreq_scaling_min_max(int cpu, int max, int freq) +{ + char buffer[128], freq_str[16]; + int fd, ret, len; + + if (max) + snprintf(buffer, sizeof(buffer), + "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_max_freq", cpu); + else + snprintf(buffer, sizeof(buffer), + "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_min_freq", cpu); + + fd = open(buffer, O_WRONLY); + if (fd < 0) + return fd; + + snprintf(freq_str, sizeof(freq_str), "%d", freq); + len = strlen(freq_str); + ret = write(fd, freq_str, len); + if (ret == -1) { + close(fd); + return ret; + } + close(fd); + + return 0; +} + +static int no_turbo(void) +{ + return parse_int_file(0, "/sys/devices/system/cpu/intel_pstate/no_turbo"); +} + +static void adjust_scaling_max_from_base_freq(int cpu) +{ + int base_freq, scaling_max_freq; + + scaling_max_freq = parse_int_file(0, "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_max_freq", cpu); + base_freq = get_cpufreq_base_freq(cpu); + if (scaling_max_freq < base_freq || no_turbo()) + set_cpufreq_scaling_min_max(cpu, 1, base_freq); +} + +static void adjust_scaling_min_from_base_freq(int cpu) +{ + int base_freq, scaling_min_freq; + + scaling_min_freq = parse_int_file(0, "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_min_freq", cpu); + base_freq = get_cpufreq_base_freq(cpu); + if (scaling_min_freq < base_freq) + set_cpufreq_scaling_min_max(cpu, 0, base_freq); +} + +static int set_clx_pbf_cpufreq_scaling_min_max(struct isst_id *id) +{ + struct isst_pkg_ctdp_level_info *ctdp_level; + struct isst_pbf_info *pbf_info; + int i, freq, freq_high, freq_low; + int ret; + + ret = clx_n_config(id); + if (ret) { + debug_printf("cpufreq_scaling_min_max failed for CLX"); + return ret; + } + + ctdp_level = &clx_n_pkg_dev.ctdp_level[0]; + pbf_info = &ctdp_level->pbf_info; + freq_high = pbf_info->p1_high * 100000; + freq_low = pbf_info->p1_low * 100000; + + for (i = 0; i < get_topo_max_cpus(); ++i) { + if (!is_cpu_in_power_domain(i, id)) + continue; + + if (CPU_ISSET_S(i, pbf_info->core_cpumask_size, + pbf_info->core_cpumask)) + freq = freq_high; + else + freq = freq_low; + + set_cpufreq_scaling_min_max(i, 1, freq); + set_cpufreq_scaling_min_max(i, 0, freq); + } + + return 0; +} + +static int set_cpufreq_scaling_min_max_from_cpuinfo(int cpu, int cpuinfo_max, int scaling_max) +{ + char buffer[128], min_freq[16]; + int fd, ret, len; + + if (!CPU_ISSET_S(cpu, present_cpumask_size, present_cpumask)) + return -1; + + if (cpuinfo_max) + snprintf(buffer, sizeof(buffer), + "/sys/devices/system/cpu/cpu%d/cpufreq/cpuinfo_max_freq", cpu); + else + snprintf(buffer, sizeof(buffer), + "/sys/devices/system/cpu/cpu%d/cpufreq/cpuinfo_min_freq", cpu); + + fd = open(buffer, O_RDONLY); + if (fd < 0) + return fd; + + len = read(fd, min_freq, sizeof(min_freq)); + close(fd); + + if (len < 0) + return len; + + if (scaling_max) + snprintf(buffer, sizeof(buffer), + "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_max_freq", cpu); + else + snprintf(buffer, sizeof(buffer), + "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_min_freq", cpu); + + fd = open(buffer, O_WRONLY); + if (fd < 0) + return fd; + + min_freq[15] = '\0'; + len = strlen(min_freq); + ret = write(fd, min_freq, len); + if (ret == -1) { + close(fd); + return ret; + } + close(fd); + + return 0; +} + +static void set_scaling_min_to_cpuinfo_max(struct isst_id *id) +{ + int i; + + if (id->cpu < 0) + return; + + for (i = 0; i < get_topo_max_cpus(); ++i) { + if (!is_cpu_in_power_domain(i, id)) + continue; + + if (is_cpu_online(i) != 1) + continue; + + adjust_scaling_max_from_base_freq(i); + set_cpufreq_scaling_min_max_from_cpuinfo(i, 1, 0); + adjust_scaling_min_from_base_freq(i); + } +} + +static void set_scaling_min_to_cpuinfo_min(struct isst_id *id) +{ + int i; + + if (id->cpu < 0) + return; + + for (i = 0; i < get_topo_max_cpus(); ++i) { + if (!is_cpu_in_power_domain(i, id)) + continue; + + if (is_cpu_online(i) != 1) + continue; + + adjust_scaling_max_from_base_freq(i); + set_cpufreq_scaling_min_max_from_cpuinfo(i, 0, 0); + } +} + +static void set_scaling_max_to_cpuinfo_max(struct isst_id *id) +{ + int i; + + for (i = 0; i < get_topo_max_cpus(); ++i) { + if (!is_cpu_in_power_domain(i, id)) + continue; + + set_cpufreq_scaling_min_max_from_cpuinfo(i, 1, 1); + } +} + +static int set_core_priority_and_min(struct isst_id *id, int mask_size, + cpu_set_t *cpu_mask, int min_high, + int min_low) +{ + int ret, i; + + if (!CPU_COUNT_S(mask_size, cpu_mask)) + return -1; + + ret = set_clos_param(id, 0, 0, 0, min_high, 0xff); + if (ret) + return ret; + + ret = set_clos_param(id, 1, 15, 15, min_low, 0xff); + if (ret) + return ret; + + ret = set_clos_param(id, 2, 15, 15, min_low, 0xff); + if (ret) + return ret; + + ret = set_clos_param(id, 3, 15, 15, min_low, 0xff); + if (ret) + return ret; + + for (i = 0; i < get_topo_max_cpus(); ++i) { + int clos; + struct isst_id tid; + + if (!is_cpu_in_power_domain(i, id)) + continue; + + if (CPU_ISSET_S(i, mask_size, cpu_mask)) + clos = 0; + else + clos = 3; + + debug_printf("Associate cpu: %d clos: %d\n", i, clos); + set_isst_id(&tid, i); + ret = isst_clos_associate(&tid, clos); + if (ret) { + isst_display_error_info_message(1, "isst_clos_associate failed", 0, 0); + return ret; + } + } + + return 0; +} + +static int set_pbf_core_power(struct isst_id *id) +{ + struct isst_pbf_info pbf_info; + struct isst_pkg_ctdp pkg_dev; + int ret; + + if (id->cpu < 0) + return 0; + + ret = isst_get_ctdp_levels(id, &pkg_dev); + if (ret) { + debug_printf("isst_get_ctdp_levels failed"); + return ret; + } + debug_printf("Current_level: %d\n", pkg_dev.current_level); + + ret = isst_get_pbf_info(id, pkg_dev.current_level, &pbf_info); + if (ret) { + debug_printf("isst_get_pbf_info failed"); + return ret; + } + debug_printf("p1_high: %d p1_low: %d\n", pbf_info.p1_high, + pbf_info.p1_low); + + ret = set_core_priority_and_min(id, pbf_info.core_cpumask_size, + pbf_info.core_cpumask, + pbf_info.p1_high, pbf_info.p1_low); + if (ret) { + debug_printf("set_core_priority_and_min failed"); + return ret; + } + + ret = isst_pm_qos_config(id, 1, 1); + if (ret) { + debug_printf("isst_pm_qos_config failed"); + return ret; + } + + return 0; +} + +static void set_pbf_for_cpu(struct isst_id *id, void *arg1, void *arg2, void *arg3, + void *arg4) +{ + struct isst_pkg_ctdp_level_info ctdp_level; + struct isst_pkg_ctdp pkg_dev; + int ret; + int status = *(int *)arg4; + + if (is_clx_n_platform()) { + ret = 0; + if (status) { + set_clx_pbf_cpufreq_scaling_min_max(id); + + } else { + set_scaling_max_to_cpuinfo_max(id); + set_scaling_min_to_cpuinfo_min(id); + } + goto disp_result; + } + + ret = isst_get_ctdp_levels(id, &pkg_dev); + if (ret) { + isst_display_error_info_message(1, "Failed to get number of levels", 0, 0); + goto disp_result; + } + + ret = isst_get_ctdp_control(id, pkg_dev.current_level, &ctdp_level); + if (ret) { + isst_display_error_info_message(1, "Failed to get current level", 0, 0); + goto disp_result; + } + + if (!ctdp_level.pbf_support) { + isst_display_error_info_message(1, "base-freq feature is not present at this level", 1, pkg_dev.current_level); + ret = -1; + goto disp_result; + } + + if (auto_mode && status) { + ret = set_pbf_core_power(id); + if (ret) + goto disp_result; + } + + ret = isst_set_pbf_fact_status(id, 1, status); + if (ret) { + debug_printf("isst_set_pbf_fact_status failed"); + if (auto_mode) + isst_pm_qos_config(id, 0, 0); + } else { + if (auto_mode) { + if (status) + set_scaling_min_to_cpuinfo_max(id); + else + set_scaling_min_to_cpuinfo_min(id); + } + } + + if (auto_mode && !status) + isst_pm_qos_config(id, 0, 1); + +disp_result: + if (status) + isst_display_result(id, outf, "base-freq", "enable", + ret); + else + isst_display_result(id, outf, "base-freq", "disable", + ret); +} + +static void set_pbf_enable(int arg) +{ + int enable = arg; + + if (cmd_help) { + if (enable) { + fprintf(stderr, + "Enable Intel Speed Select Technology base frequency feature\n"); + if (is_clx_n_platform()) { + fprintf(stderr, + "\tOn this platform this command doesn't enable feature in the hardware.\n"); + fprintf(stderr, + "\tIt updates the cpufreq scaling_min_freq to match cpufreq base_frequency.\n"); + exit(0); + + } + fprintf(stderr, + "\tOptional Arguments: -a|--auto : Use priority of cores to set core-power associations\n"); + } else { + + if (is_clx_n_platform()) { + fprintf(stderr, + "\tOn this platform this command doesn't disable feature in the hardware.\n"); + fprintf(stderr, + "\tIt updates the cpufreq scaling_min_freq to match cpuinfo_min_freq\n"); + exit(0); + } + fprintf(stderr, + "Disable Intel Speed Select Technology base frequency feature\n"); + fprintf(stderr, + "\tOptional Arguments: -a|--auto : Also disable core-power associations\n"); + } + exit(0); + } + + isst_ctdp_display_information_start(outf); + if (max_target_cpus) + for_each_online_target_cpu_in_set(set_pbf_for_cpu, NULL, NULL, + NULL, &enable); + else + for_each_online_power_domain_in_set(set_pbf_for_cpu, NULL, NULL, + NULL, &enable); + isst_ctdp_display_information_end(outf); +} + +static void dump_fact_config_for_cpu(struct isst_id *id, void *arg1, void *arg2, + void *arg3, void *arg4) +{ + struct isst_fact_info fact_info; + int ret; + + ret = isst_get_fact_info(id, tdp_level, fact_bucket, &fact_info); + if (ret) { + isst_display_error_info_message(1, "Failed to get turbo-freq info at this level", 1, tdp_level); + isst_ctdp_display_information_end(outf); + exit(1); + } else { + isst_fact_display_information(id, outf, tdp_level, fact_bucket, + fact_avx, &fact_info); + } +} + +static void dump_fact_config(int arg) +{ + if (cmd_help) { + fprintf(stderr, + "Print complete Intel Speed Select Technology turbo frequency configuration for a TDP level. Other arguments are optional.\n"); + fprintf(stderr, + "\tArguments: -l|--level : Specify tdp level\n"); + fprintf(stderr, + "\tArguments: -b|--bucket : Bucket index to dump\n"); + fprintf(stderr, + "\tArguments: -r|--trl-type : Specify trl type: sse|avx2|avx512\n"); + exit(0); + } + + if (tdp_level == 0xff) { + isst_display_error_info_message(1, "Invalid command: specify tdp_level\n", 0, 0); + exit(1); + } + + isst_ctdp_display_information_start(outf); + if (max_target_cpus) + for_each_online_target_cpu_in_set(dump_fact_config_for_cpu, + NULL, NULL, NULL, NULL); + else + for_each_online_power_domain_in_set(dump_fact_config_for_cpu, NULL, + NULL, NULL, NULL); + isst_ctdp_display_information_end(outf); +} + +static void set_fact_for_cpu(struct isst_id *id, void *arg1, void *arg2, void *arg3, + void *arg4) +{ + struct isst_pkg_ctdp_level_info ctdp_level; + struct isst_pkg_ctdp pkg_dev; + int ret; + int status = *(int *)arg4; + + if (status && no_turbo()) { + isst_display_error_info_message(1, "Turbo mode is disabled", 0, 0); + ret = -1; + goto disp_results; + } + + ret = isst_get_ctdp_levels(id, &pkg_dev); + if (ret) { + isst_display_error_info_message(1, "Failed to get number of levels", 0, 0); + goto disp_results; + } + + ret = isst_get_ctdp_control(id, pkg_dev.current_level, &ctdp_level); + if (ret) { + isst_display_error_info_message(1, "Failed to get current level", 0, 0); + goto disp_results; + } + + if (!ctdp_level.fact_support) { + isst_display_error_info_message(1, "turbo-freq feature is not present at this level", 1, pkg_dev.current_level); + ret = -1; + goto disp_results; + } + + if (status) { + ret = isst_pm_qos_config(id, 1, 1); + if (ret) + goto disp_results; + } + + ret = isst_set_pbf_fact_status(id, 0, status); + if (ret) { + debug_printf("isst_set_pbf_fact_status failed"); + if (auto_mode) + isst_pm_qos_config(id, 0, 0); + + goto disp_results; + } + + /* Set TRL */ + if (status) { + struct isst_pkg_ctdp pkg_dev; + + ret = isst_get_ctdp_levels(id, &pkg_dev); + if (!ret && id->cpu >= 0) + ret = isst_set_trl(id, fact_trl); + if (ret && auto_mode) + isst_pm_qos_config(id, 0, 0); + } else { + if (auto_mode) + isst_pm_qos_config(id, 0, 0); + } + +disp_results: + if (status) { + isst_display_result(id, outf, "turbo-freq", "enable", ret); + if (ret) + fact_enable_fail = ret; + } else { + /* Since we modified TRL during Fact enable, restore it */ + isst_set_trl_from_current_tdp(id, fact_trl); + isst_display_result(id, outf, "turbo-freq", "disable", ret); + } +} + +static void set_fact_enable(int arg) +{ + int i, ret, enable = arg; + struct isst_id id; + + if (cmd_help) { + if (enable) { + fprintf(stderr, + "Enable Intel Speed Select Technology Turbo frequency feature\n"); + fprintf(stderr, + "Optional: -t|--trl : Specify turbo ratio limit\n"); + fprintf(stderr, + "\tOptional Arguments: -a|--auto : Designate specified target CPUs with"); + fprintf(stderr, + "-C|--cpu option as as high priority using core-power feature\n"); + } else { + fprintf(stderr, + "Disable Intel Speed Select Technology turbo frequency feature\n"); + fprintf(stderr, + "Optional: -t|--trl : Specify turbo ratio limit\n"); + fprintf(stderr, + "\tOptional Arguments: -a|--auto : Also disable core-power associations\n"); + } + exit(0); + } + + isst_ctdp_display_information_start(outf); + if (max_target_cpus) + for_each_online_target_cpu_in_set(set_fact_for_cpu, NULL, NULL, + NULL, &enable); + else + for_each_online_power_domain_in_set(set_fact_for_cpu, NULL, NULL, + NULL, &enable); + + if (!fact_enable_fail && enable && auto_mode) { + /* + * When we adjust CLOS param, we have to set for siblings also. + * So for the each user specified CPU, also add the sibling + * in the present_cpu_mask. + */ + for (i = 0; i < get_topo_max_cpus(); ++i) { + char buffer[128], sibling_list[128], *cpu_str; + int fd, len; + + if (!CPU_ISSET_S(i, target_cpumask_size, target_cpumask)) + continue; + + snprintf(buffer, sizeof(buffer), + "/sys/devices/system/cpu/cpu%d/topology/thread_siblings_list", i); + + fd = open(buffer, O_RDONLY); + if (fd < 0) + continue; + + len = read(fd, sibling_list, sizeof(sibling_list)); + close(fd); + + if (len < 0) + continue; + + sibling_list[127] = '\0'; + cpu_str = strtok(sibling_list, ","); + while (cpu_str != NULL) { + int cpu; + + sscanf(cpu_str, "%d", &cpu); + CPU_SET_S(cpu, target_cpumask_size, target_cpumask); + cpu_str = strtok(NULL, ","); + } + } + + for (i = 0; i < get_topo_max_cpus(); ++i) { + int clos; + + if (!CPU_ISSET_S(i, present_cpumask_size, present_cpumask)) + continue; + + if (is_cpu_online(i) != 1) + continue; + + set_isst_id(&id, i); + ret = set_clos_param(&id, 0, 0, 0, 0, 0xff); + if (ret) + goto error_disp; + + ret = set_clos_param(&id, 1, 15, 15, 0, 0xff); + if (ret) + goto error_disp; + + ret = set_clos_param(&id, 2, 15, 15, 0, 0xff); + if (ret) + goto error_disp; + + ret = set_clos_param(&id, 3, 15, 15, 0, 0xff); + if (ret) + goto error_disp; + + if (CPU_ISSET_S(i, target_cpumask_size, target_cpumask)) + clos = 0; + else + clos = 3; + + debug_printf("Associate cpu: %d clos: %d\n", i, clos); + ret = isst_clos_associate(&id, clos); + if (ret) + goto error_disp; + } + set_isst_id(&id, -1); + isst_display_result(&id, outf, "turbo-freq --auto", "enable", 0); + } + + isst_ctdp_display_information_end(outf); + + return; + +error_disp: + isst_display_result(&id, outf, "turbo-freq --auto", "enable", ret); + isst_ctdp_display_information_end(outf); + +} + +static void enable_clos_qos_config(struct isst_id *id, void *arg1, void *arg2, void *arg3, + void *arg4) +{ + int ret; + int status = *(int *)arg4; + + if (is_skx_based_platform()) + clos_priority_type = 1; + + ret = isst_pm_qos_config(id, status, clos_priority_type); + if (ret) + isst_display_error_info_message(1, "isst_pm_qos_config failed", 0, 0); + + if (status) + isst_display_result(id, outf, "core-power", "enable", + ret); + else + isst_display_result(id, outf, "core-power", "disable", + ret); +} + +static void set_clos_enable(int arg) +{ + int enable = arg; + + if (cmd_help) { + if (enable) { + fprintf(stderr, + "Enable core-power for a package/die\n"); + if (!is_skx_based_platform()) { + fprintf(stderr, + "\tClos Enable: Specify priority type with [--priority|-p]\n"); + fprintf(stderr, "\t\t 0: Proportional, 1: Ordered\n"); + } + } else { + fprintf(stderr, + "Disable core-power: [No command arguments are required]\n"); + } + exit(0); + } + + if (enable && cpufreq_sysfs_present()) { + fprintf(stderr, + "cpufreq subsystem and core-power enable will interfere with each other!\n"); + } + + isst_ctdp_display_information_start(outf); + if (max_target_cpus) + for_each_online_target_cpu_in_set(enable_clos_qos_config, NULL, + NULL, NULL, &enable); + else + for_each_online_power_domain_in_set(enable_clos_qos_config, NULL, + NULL, NULL, &enable); + isst_ctdp_display_information_end(outf); +} + +static void dump_clos_config_for_cpu(struct isst_id *id, void *arg1, void *arg2, + void *arg3, void *arg4) +{ + struct isst_clos_config clos_config; + int ret; + + ret = isst_pm_get_clos(id, current_clos, &clos_config); + if (ret) + isst_display_error_info_message(1, "isst_pm_get_clos failed", 0, 0); + else + isst_clos_display_information(id, outf, current_clos, + &clos_config); +} + +static void dump_clos_config(int arg) +{ + if (cmd_help) { + fprintf(stderr, + "Print Intel Speed Select Technology core power configuration\n"); + fprintf(stderr, + "\tArguments: [-c | --clos]: Specify clos id\n"); + exit(0); + } + if (current_clos < 0 || current_clos > 3) { + isst_display_error_info_message(1, "Invalid clos id\n", 0, 0); + isst_ctdp_display_information_end(outf); + exit(0); + } + + isst_ctdp_display_information_start(outf); + if (max_target_cpus) + for_each_online_target_cpu_in_set(dump_clos_config_for_cpu, + NULL, NULL, NULL, NULL); + else + for_each_online_power_domain_in_set(dump_clos_config_for_cpu, NULL, + NULL, NULL, NULL); + isst_ctdp_display_information_end(outf); +} + +static void get_clos_info_for_cpu(struct isst_id *id, void *arg1, void *arg2, void *arg3, + void *arg4) +{ + int enable, ret, prio_type; + + ret = isst_clos_get_clos_information(id, &enable, &prio_type); + if (ret) + isst_display_error_info_message(1, "isst_clos_get_info failed", 0, 0); + else { + int cp_state, cp_cap; + + isst_read_pm_config(id, &cp_state, &cp_cap); + isst_clos_display_clos_information(id, outf, enable, prio_type, + cp_state, cp_cap); + } +} + +static void dump_clos_info(int arg) +{ + if (cmd_help) { + fprintf(stderr, + "Print Intel Speed Select Technology core power information\n"); + fprintf(stderr, "\t Optionally specify targeted cpu id with [--cpu|-c]\n"); + exit(0); + } + + isst_ctdp_display_information_start(outf); + if (max_target_cpus) + for_each_online_target_cpu_in_set(get_clos_info_for_cpu, NULL, + NULL, NULL, NULL); + else + for_each_online_power_domain_in_set(get_clos_info_for_cpu, NULL, + NULL, NULL, NULL); + isst_ctdp_display_information_end(outf); + +} + +static void set_clos_config_for_cpu(struct isst_id *id, void *arg1, void *arg2, void *arg3, + void *arg4) +{ + struct isst_clos_config clos_config; + int ret; + + if (id->cpu < 0) + return; + + clos_config.epp = clos_epp; + clos_config.clos_prop_prio = clos_prop_prio; + clos_config.clos_min = clos_min; + clos_config.clos_max = clos_max; + clos_config.clos_desired = clos_desired; + ret = isst_set_clos(id, current_clos, &clos_config); + if (ret) + isst_display_error_info_message(1, "isst_set_clos failed", 0, 0); + else + isst_display_result(id, outf, "core-power", "config", ret); +} + +static void set_clos_config(int arg) +{ + if (cmd_help) { + fprintf(stderr, + "Set core-power configuration for one of the four clos ids\n"); + fprintf(stderr, + "\tSpecify targeted clos id with [--clos|-c]\n"); + if (!is_skx_based_platform()) { + fprintf(stderr, "\tSpecify clos EPP with [--epp|-e]\n"); + fprintf(stderr, + "\tSpecify clos Proportional Priority [--weight|-w]\n"); + } + fprintf(stderr, "\tSpecify clos min in MHz with [--min|-n]\n"); + fprintf(stderr, "\tSpecify clos max in MHz with [--max|-m]\n"); + exit(0); + } + + if (current_clos < 0 || current_clos > 3) { + isst_display_error_info_message(1, "Invalid clos id\n", 0, 0); + exit(0); + } + if (!is_skx_based_platform() && (clos_epp < 0 || clos_epp > 0x0F)) { + fprintf(stderr, "clos epp is not specified or invalid, default: 0\n"); + clos_epp = 0; + } + if (!is_skx_based_platform() && (clos_prop_prio < 0 || clos_prop_prio > 0x0F)) { + fprintf(stderr, + "clos frequency weight is not specified or invalid, default: 0\n"); + clos_prop_prio = 0; + } + if (clos_min < 0) { + fprintf(stderr, "clos min is not specified, default: 0\n"); + clos_min = 0; + } + if (clos_max < 0) { + fprintf(stderr, "clos max is not specified, default: Max frequency (ratio 0xff)\n"); + clos_max = 0xff; + } + if (clos_desired) { + fprintf(stderr, "clos desired is not supported on this platform\n"); + clos_desired = 0x00; + } + + isst_ctdp_display_information_start(outf); + if (max_target_cpus) + for_each_online_target_cpu_in_set(set_clos_config_for_cpu, NULL, + NULL, NULL, NULL); + else + for_each_online_power_domain_in_set(set_clos_config_for_cpu, NULL, + NULL, NULL, NULL); + isst_ctdp_display_information_end(outf); +} + +static void set_clos_assoc_for_cpu(struct isst_id *id, void *arg1, void *arg2, void *arg3, + void *arg4) +{ + int ret; + + ret = isst_clos_associate(id, current_clos); + if (ret) + debug_printf("isst_clos_associate failed"); + else + isst_display_result(id, outf, "core-power", "assoc", ret); +} + +static void set_clos_assoc(int arg) +{ + if (cmd_help) { + fprintf(stderr, "Associate a clos id to a CPU\n"); + fprintf(stderr, + "\tSpecify targeted clos id with [--clos|-c]\n"); + fprintf(stderr, + "\tFor example to associate clos 1 to CPU 0: issue\n"); + fprintf(stderr, + "\tintel-speed-select --cpu 0 core-power assoc --clos 1\n"); + exit(0); + } + + if (current_clos < 0 || current_clos > 3) { + isst_display_error_info_message(1, "Invalid clos id\n", 0, 0); + exit(0); + } + + isst_ctdp_display_information_start(outf); + + if (max_target_cpus) + for_each_online_target_cpu_in_set(set_clos_assoc_for_cpu, NULL, + NULL, NULL, NULL); + else { + isst_display_error_info_message(1, "Invalid target cpu. Specify with [-c|--cpu]", 0, 0); + } + isst_ctdp_display_information_end(outf); +} + +static void get_clos_assoc_for_cpu(struct isst_id *id, void *arg1, void *arg2, void *arg3, + void *arg4) +{ + int clos, ret; + + ret = isst_clos_get_assoc_status(id, &clos); + if (ret) + isst_display_error_info_message(1, "isst_clos_get_assoc_status failed", 0, 0); + else + isst_clos_display_assoc_information(id, outf, clos); +} + +static void get_clos_assoc(int arg) +{ + if (cmd_help) { + fprintf(stderr, "Get associate clos id to a CPU\n"); + fprintf(stderr, "\tSpecify targeted cpu id with [--cpu|-c]\n"); + exit(0); + } + + if (!max_target_cpus) { + isst_display_error_info_message(1, "Invalid target cpu. Specify with [-c|--cpu]", 0, 0); + exit(0); + } + + isst_ctdp_display_information_start(outf); + for_each_online_target_cpu_in_set(get_clos_assoc_for_cpu, NULL, + NULL, NULL, NULL); + isst_ctdp_display_information_end(outf); +} + +static void set_turbo_mode_for_cpu(struct isst_id *id, int status) +{ + int base_freq; + + if (status) { + base_freq = get_cpufreq_base_freq(id->cpu); + set_cpufreq_scaling_min_max(id->cpu, 1, base_freq); + } else { + set_scaling_max_to_cpuinfo_max(id); + } + + if (status) { + isst_display_result(id, outf, "turbo-mode", "enable", 0); + } else { + isst_display_result(id, outf, "turbo-mode", "disable", 0); + } +} + +static void set_turbo_mode(int arg) +{ + int i, enable = arg; + struct isst_id id; + + if (cmd_help) { + if (enable) + fprintf(stderr, "Set turbo mode enable\n"); + else + fprintf(stderr, "Set turbo mode disable\n"); + exit(0); + } + + isst_ctdp_display_information_start(outf); + + for (i = 0; i < topo_max_cpus; ++i) { + int online; + + if (i) + online = parse_int_file( + 1, "/sys/devices/system/cpu/cpu%d/online", i); + else + online = + 1; /* online entry for CPU 0 needs some special configs */ + + if (online) { + set_isst_id(&id, i); + set_turbo_mode_for_cpu(&id, enable); + } + + } + isst_ctdp_display_information_end(outf); +} + +static void get_set_trl(struct isst_id *id, void *arg1, void *arg2, void *arg3, + void *arg4) +{ + unsigned long long trl; + int set = *(int *)arg4; + int ret; + + if (set && !fact_trl) { + isst_display_error_info_message(1, "Invalid TRL. Specify with [-t|--trl]", 0, 0); + exit(0); + } + + if (set) { + ret = isst_set_trl(id, fact_trl); + isst_display_result(id, outf, "turbo-mode", "set-trl", ret); + return; + } + + ret = isst_get_trl(id, &trl); + if (ret) + isst_display_result(id, outf, "turbo-mode", "get-trl", ret); + else + isst_trl_display_information(id, outf, trl); +} + +static void process_trl(int arg) +{ + if (cmd_help) { + if (arg) { + fprintf(stderr, "Set TRL (turbo ratio limits)\n"); + fprintf(stderr, "\t t|--trl: Specify turbo ratio limit for setting TRL\n"); + } else { + fprintf(stderr, "Get TRL (turbo ratio limits)\n"); + } + exit(0); + } + + isst_ctdp_display_information_start(outf); + if (max_target_cpus) + for_each_online_target_cpu_in_set(get_set_trl, NULL, + NULL, NULL, &arg); + else + for_each_online_power_domain_in_set(get_set_trl, NULL, + NULL, NULL, &arg); + isst_ctdp_display_information_end(outf); +} + +static struct process_cmd_struct clx_n_cmds[] = { + { "perf-profile", "info", dump_isst_config, 0 }, + { "base-freq", "info", dump_pbf_config, 0 }, + { "base-freq", "enable", set_pbf_enable, 1 }, + { "base-freq", "disable", set_pbf_enable, 0 }, + { NULL, NULL, NULL, 0 } +}; + +static struct process_cmd_struct isst_cmds[] = { + { "perf-profile", "get-lock-status", get_tdp_locked, 0 }, + { "perf-profile", "get-config-levels", get_tdp_levels, 0 }, + { "perf-profile", "get-config-version", get_tdp_version, 0 }, + { "perf-profile", "get-config-enabled", get_tdp_enabled, 0 }, + { "perf-profile", "get-config-current-level", get_tdp_current_level, + 0 }, + { "perf-profile", "set-config-level", set_tdp_level, 0 }, + { "perf-profile", "info", dump_isst_config, 0 }, + { "base-freq", "info", dump_pbf_config, 0 }, + { "base-freq", "enable", set_pbf_enable, 1 }, + { "base-freq", "disable", set_pbf_enable, 0 }, + { "turbo-freq", "info", dump_fact_config, 0 }, + { "turbo-freq", "enable", set_fact_enable, 1 }, + { "turbo-freq", "disable", set_fact_enable, 0 }, + { "core-power", "info", dump_clos_info, 0 }, + { "core-power", "enable", set_clos_enable, 1 }, + { "core-power", "disable", set_clos_enable, 0 }, + { "core-power", "config", set_clos_config, 0 }, + { "core-power", "get-config", dump_clos_config, 0 }, + { "core-power", "assoc", set_clos_assoc, 0 }, + { "core-power", "get-assoc", get_clos_assoc, 0 }, + { "turbo-mode", "enable", set_turbo_mode, 0 }, + { "turbo-mode", "disable", set_turbo_mode, 1 }, + { "turbo-mode", "get-trl", process_trl, 0 }, + { "turbo-mode", "set-trl", process_trl, 1 }, + { NULL, NULL, NULL } +}; + +/* + * parse cpuset with following syntax + * 1,2,4..6,8-10 and set bits in cpu_subset + */ +void parse_cpu_command(char *optarg) +{ + unsigned int start, end, invalid_count; + char *next; + + next = optarg; + invalid_count = 0; + + while (next && *next) { + if (*next == '-') /* no negative cpu numbers */ + goto error; + + start = strtoul(next, &next, 10); + + if (max_target_cpus < MAX_CPUS_IN_ONE_REQ) + target_cpus[max_target_cpus++] = start; + else + invalid_count = 1; + + if (*next == '\0') + break; + + if (*next == ',') { + next += 1; + continue; + } + + if (*next == '-') { + next += 1; /* start range */ + } else if (*next == '.') { + next += 1; + if (*next == '.') + next += 1; /* start range */ + else + goto error; + } + + end = strtoul(next, &next, 10); + if (end <= start) + goto error; + + while (++start <= end) { + if (max_target_cpus < MAX_CPUS_IN_ONE_REQ) + target_cpus[max_target_cpus++] = start; + else + invalid_count = 1; + } + + if (*next == ',') + next += 1; + else if (*next != '\0') + goto error; + } + + if (invalid_count) { + isst_ctdp_display_information_start(outf); + isst_display_error_info_message(1, "Too many CPUs in one request: max is", 1, MAX_CPUS_IN_ONE_REQ - 1); + isst_ctdp_display_information_end(outf); + exit(-1); + } + +#ifdef DEBUG + { + int i; + + for (i = 0; i < max_target_cpus; ++i) + printf("cpu [%d] in arg\n", target_cpus[i]); + } +#endif + return; + +error: + fprintf(stderr, "\"--cpu %s\" malformed\n", optarg); + exit(-1); +} + +static void parse_cmd_args(int argc, int start, char **argv) +{ + int opt; + int option_index; + + static struct option long_options[] = { + { "bucket", required_argument, 0, 'b' }, + { "level", required_argument, 0, 'l' }, + { "online", required_argument, 0, 'o' }, + { "trl-type", required_argument, 0, 'r' }, + { "trl", required_argument, 0, 't' }, + { "help", no_argument, 0, 'h' }, + { "clos", required_argument, 0, 'c' }, + { "desired", required_argument, 0, 'd' }, + { "epp", required_argument, 0, 'e' }, + { "min", required_argument, 0, 'n' }, + { "max", required_argument, 0, 'm' }, + { "priority", required_argument, 0, 'p' }, + { "weight", required_argument, 0, 'w' }, + { "auto", no_argument, 0, 'a' }, + { 0, 0, 0, 0 } + }; + + option_index = start; + + optind = start + 1; + while ((opt = getopt_long(argc, argv, "b:l:t:c:d:e:n:m:p:w:r:hoa", + long_options, &option_index)) != -1) { + switch (opt) { + case 'a': + auto_mode = 1; + break; + case 'b': + fact_bucket = atoi(optarg); + break; + case 'h': + cmd_help = 1; + break; + case 'l': + tdp_level = atoi(optarg); + break; + case 'o': + force_online_offline = 1; + break; + case 't': + sscanf(optarg, "0x%llx", &fact_trl); + break; + case 'r': + if (!strncmp(optarg, "sse", 3)) { + fact_avx = 0x01; + } else if (!strncmp(optarg, "avx2", 4)) { + fact_avx = 0x02; + } else if (!strncmp(optarg, "avx512", 6)) { + fact_avx = 0x04; + } else { + fprintf(outf, "Invalid sse,avx options\n"); + exit(1); + } + break; + /* CLOS related */ + case 'c': + current_clos = atoi(optarg); + break; + case 'd': + clos_desired = atoi(optarg); + clos_desired /= isst_get_disp_freq_multiplier(); + break; + case 'e': + clos_epp = atoi(optarg); + if (is_skx_based_platform()) { + isst_display_error_info_message(1, "epp can't be specified on this platform", 0, 0); + exit(0); + } + break; + case 'n': + clos_min = atoi(optarg); + clos_min /= isst_get_disp_freq_multiplier(); + break; + case 'm': + clos_max = atoi(optarg); + clos_max /= isst_get_disp_freq_multiplier(); + break; + case 'p': + clos_priority_type = atoi(optarg); + if (is_skx_based_platform() && !clos_priority_type) { + isst_display_error_info_message(1, "Invalid clos priority type: proportional for this platform", 0, 0); + exit(0); + } + break; + case 'w': + clos_prop_prio = atoi(optarg); + if (is_skx_based_platform()) { + isst_display_error_info_message(1, "weight can't be specified on this platform", 0, 0); + exit(0); + } + break; + default: + printf("Unknown option: ignore\n"); + } + } + + if (argv[optind]) + printf("Garbage at the end of command: ignore\n"); +} + +static void isst_help(void) +{ + printf("perf-profile:\tAn architectural mechanism that allows multiple optimized \n\ + performance profiles per system via static and/or dynamic\n\ + adjustment of core count, workload, Tjmax, and\n\ + TDP, etc.\n"); + printf("\nCommands : For feature=perf-profile\n"); + printf("\tinfo\n"); + + if (!is_clx_n_platform()) { + printf("\tget-lock-status\n"); + printf("\tget-config-levels\n"); + printf("\tget-config-version\n"); + printf("\tget-config-enabled\n"); + printf("\tget-config-current-level\n"); + printf("\tset-config-level\n"); + } +} + +static void pbf_help(void) +{ + printf("base-freq:\tEnables users to increase guaranteed base frequency\n\ + on certain cores (high priority cores) in exchange for lower\n\ + base frequency on remaining cores (low priority cores).\n"); + printf("\tcommand : info\n"); + printf("\tcommand : enable\n"); + printf("\tcommand : disable\n"); +} + +static void fact_help(void) +{ + printf("turbo-freq:\tEnables the ability to set different turbo ratio\n\ + limits to cores based on priority.\n"); + printf("\nCommand: For feature=turbo-freq\n"); + printf("\tcommand : info\n"); + printf("\tcommand : enable\n"); + printf("\tcommand : disable\n"); +} + +static void turbo_mode_help(void) +{ + printf("turbo-mode:\tEnables users to enable/disable turbo mode by adjusting frequency settings. Also allows to get and set turbo ratio limits (TRL).\n"); + printf("\tcommand : enable\n"); + printf("\tcommand : disable\n"); + printf("\tcommand : get-trl\n"); + printf("\tcommand : set-trl\n"); +} + + +static void core_power_help(void) +{ + printf("core-power:\tInterface that allows user to define per core/tile\n\ + priority.\n"); + printf("\nCommands : For feature=core-power\n"); + printf("\tinfo\n"); + printf("\tenable\n"); + printf("\tdisable\n"); + printf("\tconfig\n"); + printf("\tget-config\n"); + printf("\tassoc\n"); + printf("\tget-assoc\n"); +} + +struct process_cmd_help_struct { + char *feature; + void (*process_fn)(void); +}; + +static struct process_cmd_help_struct isst_help_cmds[] = { + { "perf-profile", isst_help }, + { "base-freq", pbf_help }, + { "turbo-freq", fact_help }, + { "core-power", core_power_help }, + { "turbo-mode", turbo_mode_help }, + { NULL, NULL } +}; + +static struct process_cmd_help_struct clx_n_help_cmds[] = { + { "perf-profile", isst_help }, + { "base-freq", pbf_help }, + { NULL, NULL } +}; + +void process_command(int argc, char **argv, + struct process_cmd_help_struct *help_cmds, + struct process_cmd_struct *cmds) +{ + int i = 0, matched = 0; + char *feature = argv[optind]; + char *cmd = argv[optind + 1]; + + if (!feature || !cmd) + return; + + debug_printf("feature name [%s] command [%s]\n", feature, cmd); + if (!strcmp(cmd, "-h") || !strcmp(cmd, "--help")) { + while (help_cmds[i].feature) { + if (!strcmp(help_cmds[i].feature, feature)) { + help_cmds[i].process_fn(); + exit(0); + } + ++i; + } + } + + i = 0; + while (cmds[i].feature) { + if (!strcmp(cmds[i].feature, feature) && + !strcmp(cmds[i].command, cmd)) { + parse_cmd_args(argc, optind + 1, argv); + cmds[i].process_fn(cmds[i].arg); + matched = 1; + break; + } + ++i; + } + + if (!matched) + fprintf(stderr, "Invalid command\n"); +} + +static void usage(void) +{ + if (is_clx_n_platform()) { + fprintf(stderr, "\nThere is limited support of Intel Speed Select features on this platform.\n"); + fprintf(stderr, "Everything is pre-configured using BIOS options, this tool can't enable any feature in the hardware.\n\n"); + } + + printf("\nUsage:\n"); + printf("intel-speed-select [OPTIONS] FEATURE COMMAND COMMAND_ARGUMENTS\n"); + printf("\nUse this tool to enumerate and control the Intel Speed Select Technology features:\n"); + if (is_clx_n_platform()) + printf("\nFEATURE : [perf-profile|base-freq]\n"); + else + printf("\nFEATURE : [perf-profile|base-freq|turbo-freq|core-power|turbo-mode]\n"); + printf("\nFor help on each feature, use -h|--help\n"); + printf("\tFor example: intel-speed-select perf-profile -h\n"); + + printf("\nFor additional help on each command for a feature, use --h|--help\n"); + printf("\tFor example: intel-speed-select perf-profile get-lock-status -h\n"); + printf("\t\t This will print help for the command \"get-lock-status\" for the feature \"perf-profile\"\n"); + + printf("\nOPTIONS\n"); + printf("\t[-c|--cpu] : logical cpu number\n"); + printf("\t\tDefault: Die scoped for all dies in the system with multiple dies/package\n"); + printf("\t\t\t Or Package scoped for all Packages when each package contains one die\n"); + printf("\t[-d|--debug] : Debug mode\n"); + printf("\t[-f|--format] : output format [json|text]. Default: text\n"); + printf("\t[-h|--help] : Print help\n"); + printf("\t[-i|--info] : Print platform information\n"); + printf("\t[-a|--all-cpus-online] : Force online every CPU in the system\n"); + printf("\t[-o|--out] : Output file\n"); + printf("\t\t\tDefault : stderr\n"); + printf("\t[-p|--pause] : Delay between two mail box commands in milliseconds\n"); + printf("\t[-r|--retry] : Retry count for mail box commands on failure, default 3\n"); + printf("\t[-v|--version] : Print version\n"); + printf("\t[-b|--oob : Start a daemon to process HFI events for perf profile change from Out of Band agent.\n"); + printf("\t[-n|--no-daemon : Don't run as daemon. By default --oob will turn on daemon mode\n"); + printf("\t[-w|--delay : Delay for reading config level state change in OOB poll mode.\n"); + printf("\t[-g|--cgroupv2 : Try to use cgroup v2 CPU isolation instead of CPU online/offline.\n"); + printf("\nResult format\n"); + printf("\tResult display uses a common format for each command:\n"); + printf("\tResults are formatted in text/JSON with\n"); + printf("\t\tPackage, Die, CPU, and command specific results.\n"); + + printf("\nExamples\n"); + printf("\tTo get platform information:\n"); + printf("\t\tintel-speed-select --info\n"); + printf("\tTo get full perf-profile information dump:\n"); + printf("\t\tintel-speed-select perf-profile info\n"); + printf("\tTo get full base-freq information dump:\n"); + printf("\t\tintel-speed-select base-freq info -l 0\n"); + if (!is_clx_n_platform()) { + printf("\tTo get full turbo-freq information dump:\n"); + printf("\t\tintel-speed-select turbo-freq info -l 0\n"); + } + exit(1); +} + +static void print_version(void) +{ + fprintf(outf, "Version %s\n", version_str); + exit(0); +} + +static void cmdline(int argc, char **argv) +{ + const char *pathname = "/dev/isst_interface"; + char *ptr; + FILE *fp; + int opt, force_cpus_online = 0; + int option_index = 0; + int ret; + int oob_mode = 0; + int poll_interval = -1; + int no_daemon = 0; + int mbox_delay = 0, mbox_retries = 3; + + static struct option long_options[] = { + { "all-cpus-online", no_argument, 0, 'a' }, + { "cpu", required_argument, 0, 'c' }, + { "debug", no_argument, 0, 'd' }, + { "format", required_argument, 0, 'f' }, + { "help", no_argument, 0, 'h' }, + { "info", no_argument, 0, 'i' }, + { "pause", required_argument, 0, 'p' }, + { "out", required_argument, 0, 'o' }, + { "retry", required_argument, 0, 'r' }, + { "version", no_argument, 0, 'v' }, + { "oob", no_argument, 0, 'b' }, + { "no-daemon", no_argument, 0, 'n' }, + { "poll-interval", required_argument, 0, 'w' }, + { "cgroupv2", required_argument, 0, 'g' }, + { 0, 0, 0, 0 } + }; + + if (geteuid() != 0) { + fprintf(stderr, "Must run as root\n"); + exit(0); + } + + ret = update_cpu_model(); + if (ret) + err(-1, "Invalid CPU model (%d)\n", cpu_model); + printf("Intel(R) Speed Select Technology\n"); + printf("Executing on CPU model:%d[0x%x]\n", cpu_model, cpu_model); + + if (!is_clx_n_platform()) { + fp = fopen(pathname, "rb"); + if (!fp) { + fprintf(stderr, "Intel speed select drivers are not loaded on this system.\n"); + fprintf(stderr, "Verify that kernel config includes CONFIG_INTEL_SPEED_SELECT_INTERFACE.\n"); + fprintf(stderr, "If the config is included then this is not a supported platform.\n"); + exit(0); + } + fclose(fp); + } + + ret = isst_fill_platform_info(); + if (ret) + goto out; + + progname = argv[0]; + while ((opt = getopt_long_only(argc, argv, "+c:df:hio:vabw:ng", long_options, + &option_index)) != -1) { + switch (opt) { + case 'a': + force_cpus_online = 1; + break; + case 'c': + parse_cpu_command(optarg); + break; + case 'd': + debug_flag = 1; + printf("Debug Mode ON\n"); + break; + case 'f': + if (!strncmp(optarg, "json", 4)) + out_format_json = 1; + break; + case 'h': + usage(); + break; + case 'i': + isst_print_platform_information(); + break; + case 'o': + if (outf) + fclose(outf); + outf = fopen_or_exit(optarg, "w"); + break; + case 'p': + ret = strtol(optarg, &ptr, 10); + if (!ret) + fprintf(stderr, "Invalid pause interval, ignore\n"); + else + mbox_delay = ret; + break; + case 'r': + ret = strtol(optarg, &ptr, 10); + if (!ret) + fprintf(stderr, "Invalid retry count, ignore\n"); + else + mbox_retries = ret; + break; + case 'v': + print_version(); + break; + case 'b': + oob_mode = 1; + break; + case 'n': + no_daemon = 1; + break; + case 'w': + ret = strtol(optarg, &ptr, 10); + if (!ret) { + fprintf(stderr, "Invalid poll interval count\n"); + exit(0); + } + poll_interval = ret; + break; + case 'g': + cgroupv2 = 1; + break; + default: + usage(); + } + } + + if (optind > (argc - 2) && !oob_mode) { + usage(); + exit(0); + } + + isst_update_platform_param(ISST_PARAM_MBOX_DELAY, mbox_delay); + isst_update_platform_param(ISST_PARAM_MBOX_RETRIES, mbox_retries); + + set_max_cpu_num(); + if (force_cpus_online) + force_all_cpus_online(); + store_cpu_topology(); + create_cpu_map(); + + if (oob_mode) { + if (debug_flag) + fprintf(stderr, "OOB mode is enabled in debug mode\n"); + + ret = isst_daemon(debug_flag, poll_interval, no_daemon); + if (ret) + fprintf(stderr, "OOB mode enable failed\n"); + goto out; + } + + if (!is_clx_n_platform()) { + process_command(argc, argv, isst_help_cmds, isst_cmds); + } else { + process_command(argc, argv, clx_n_help_cmds, clx_n_cmds); + } +out: + free_cpu_set(present_cpumask); + free_cpu_set(target_cpumask); +} + +int main(int argc, char **argv) +{ + outf = stderr; + cmdline(argc, argv); + return 0; +} diff --git a/tools/power/x86/intel-speed-select/isst-core-mbox.c b/tools/power/x86/intel-speed-select/isst-core-mbox.c new file mode 100644 index 0000000000..24bea57f4f --- /dev/null +++ b/tools/power/x86/intel-speed-select/isst-core-mbox.c @@ -0,0 +1,1066 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Intel Speed Select -- Enumerate and control features for Mailbox Interface + * Copyright (c) 2023 Intel Corporation. + */ +#include "isst.h" + +static int mbox_delay; +static int mbox_retries = 3; + +#define MAX_TRL_LEVELS_EMR 5 + +static int mbox_get_disp_freq_multiplier(void) +{ + return DISP_FREQ_MULTIPLIER; +} + +static int mbox_get_trl_max_levels(void) +{ + if (is_emr_platform()) + return MAX_TRL_LEVELS_EMR; + + return 3; +} + +static char *mbox_get_trl_level_name(int level) +{ + if (is_emr_platform()) { + static char level_str[18]; + + if (level >= MAX_TRL_LEVELS_EMR) + return NULL; + + snprintf(level_str, sizeof(level_str), "level-%d", level); + return level_str; + } + + switch (level) { + case 0: + return "sse"; + case 1: + return "avx2"; + case 2: + return "avx512"; + default: + return NULL; + } +} + +static void mbox_update_platform_param(enum isst_platform_param param, int value) +{ + switch (param) { + case ISST_PARAM_MBOX_DELAY: + mbox_delay = value; + break; + case ISST_PARAM_MBOX_RETRIES: + mbox_retries = value; + break; + default: + break; + } +} + +static int mbox_is_punit_valid(struct isst_id *id) +{ + if (id->cpu < 0) + return 0; + + if (id->pkg < 0 || id->die < 0 || id->punit) + return 0; + + return 1; +} + +static int _send_mmio_command(unsigned int cpu, unsigned int reg, int write, + unsigned int *value) +{ + struct isst_if_io_regs io_regs; + const char *pathname = "/dev/isst_interface"; + int cmd; + FILE *outf = get_output_file(); + int fd; + + debug_printf("mmio_cmd cpu:%d reg:%d write:%d\n", cpu, reg, write); + + fd = open(pathname, O_RDWR); + if (fd < 0) + err(-1, "%s open failed", pathname); + + io_regs.req_count = 1; + io_regs.io_reg[0].logical_cpu = cpu; + io_regs.io_reg[0].reg = reg; + cmd = ISST_IF_IO_CMD; + if (write) { + io_regs.io_reg[0].read_write = 1; + io_regs.io_reg[0].value = *value; + } else { + io_regs.io_reg[0].read_write = 0; + } + + if (ioctl(fd, cmd, &io_regs) == -1) { + if (errno == ENOTTY) { + perror("ISST_IF_IO_COMMAND\n"); + fprintf(stderr, "Check presence of kernel modules: isst_if_mmio\n"); + exit(0); + } + fprintf(outf, "Error: mmio_cmd cpu:%d reg:%x read_write:%x\n", + cpu, reg, write); + } else { + if (!write) + *value = io_regs.io_reg[0].value; + + debug_printf( + "mmio_cmd response: cpu:%d reg:%x rd_write:%x resp:%x\n", + cpu, reg, write, *value); + } + + close(fd); + + return 0; +} + +int _send_mbox_command(unsigned int cpu, unsigned char command, + unsigned char sub_command, unsigned int parameter, + unsigned int req_data, unsigned int *resp) +{ + const char *pathname = "/dev/isst_interface"; + int fd, retry; + struct isst_if_mbox_cmds mbox_cmds = { 0 }; + + debug_printf( + "mbox_send: cpu:%d command:%x sub_command:%x parameter:%x req_data:%x\n", + cpu, command, sub_command, parameter, req_data); + + if (!is_skx_based_platform() && command == CONFIG_CLOS && + sub_command != CLOS_PM_QOS_CONFIG) { + unsigned int value; + int write = 0; + int clos_id, core_id, ret = 0; + + debug_printf("CPU %d\n", cpu); + + if (parameter & BIT(MBOX_CMD_WRITE_BIT)) { + value = req_data; + write = 1; + } + + switch (sub_command) { + case CLOS_PQR_ASSOC: + core_id = parameter & 0xff; + ret = _send_mmio_command( + cpu, PQR_ASSOC_OFFSET + core_id * 4, write, + &value); + if (!ret && !write) + *resp = value; + break; + case CLOS_PM_CLOS: + clos_id = parameter & 0x03; + ret = _send_mmio_command( + cpu, PM_CLOS_OFFSET + clos_id * 4, write, + &value); + if (!ret && !write) + *resp = value; + break; + case CLOS_STATUS: + break; + default: + break; + } + return ret; + } + + mbox_cmds.cmd_count = 1; + mbox_cmds.mbox_cmd[0].logical_cpu = cpu; + mbox_cmds.mbox_cmd[0].command = command; + mbox_cmds.mbox_cmd[0].sub_command = sub_command; + mbox_cmds.mbox_cmd[0].parameter = parameter; + mbox_cmds.mbox_cmd[0].req_data = req_data; + + if (mbox_delay) + usleep(mbox_delay * 1000); + + fd = open(pathname, O_RDWR); + if (fd < 0) + err(-1, "%s open failed", pathname); + + retry = mbox_retries; + do { + if (ioctl(fd, ISST_IF_MBOX_COMMAND, &mbox_cmds) == -1) { + if (errno == ENOTTY) { + perror("ISST_IF_MBOX_COMMAND\n"); + fprintf(stderr, "Check presence of kernel modules: isst_if_mbox_pci or isst_if_mbox_msr\n"); + exit(0); + } + debug_printf( + "Error: mbox_cmd cpu:%d command:%x sub_command:%x parameter:%x req_data:%x errorno:%d\n", + cpu, command, sub_command, parameter, req_data, errno); + --retry; + } else { + *resp = mbox_cmds.mbox_cmd[0].resp_data; + debug_printf( + "mbox_cmd response: cpu:%d command:%x sub_command:%x parameter:%x req_data:%x resp:%x\n", + cpu, command, sub_command, parameter, req_data, *resp); + break; + } + } while (retry); + + close(fd); + + if (!retry) { + debug_printf("Failed mbox command even after retries\n"); + return -1; + + } + + return 0; +} + +static int mbox_read_pm_config(struct isst_id *id, int *cp_state, int *cp_cap) +{ + unsigned int resp; + int ret; + + ret = _send_mbox_command(id->cpu, READ_PM_CONFIG, PM_FEATURE, 0, 0, + &resp); + if (ret) + return ret; + + debug_printf("cpu:%d READ_PM_CONFIG resp:%x\n", id->cpu, resp); + + *cp_state = resp & BIT(16); + *cp_cap = resp & BIT(0) ? 1 : 0; + + return 0; +} + +static int mbox_get_config_levels(struct isst_id *id, struct isst_pkg_ctdp *pkg_dev) +{ + unsigned int resp; + int ret; + + ret = _send_mbox_command(id->cpu, CONFIG_TDP, + CONFIG_TDP_GET_LEVELS_INFO, 0, 0, &resp); + if (ret) { + pkg_dev->levels = 0; + pkg_dev->locked = 1; + pkg_dev->current_level = 0; + pkg_dev->version = 0; + pkg_dev->enabled = 0; + return 0; + } + + debug_printf("cpu:%d CONFIG_TDP_GET_LEVELS_INFO resp:%x\n", id->cpu, resp); + + pkg_dev->version = resp & 0xff; + pkg_dev->levels = (resp >> 8) & 0xff; + pkg_dev->current_level = (resp >> 16) & 0xff; + pkg_dev->locked = !!(resp & BIT(24)); + pkg_dev->enabled = !!(resp & BIT(31)); + + return 0; +} + +static int mbox_get_ctdp_control(struct isst_id *id, int config_index, + struct isst_pkg_ctdp_level_info *ctdp_level) +{ + int cp_state, cp_cap; + unsigned int resp; + int ret; + + ret = _send_mbox_command(id->cpu, CONFIG_TDP, + CONFIG_TDP_GET_TDP_CONTROL, 0, + config_index, &resp); + if (ret) + return ret; + + ctdp_level->fact_support = resp & BIT(0); + ctdp_level->pbf_support = !!(resp & BIT(1)); + ctdp_level->fact_enabled = !!(resp & BIT(16)); + ctdp_level->pbf_enabled = !!(resp & BIT(17)); + + ret = isst_read_pm_config(id, &cp_state, &cp_cap); + if (ret) { + debug_printf("cpu:%d pm_config is not supported\n", id->cpu); + } else { + debug_printf("cpu:%d pm_config SST-CP state:%d cap:%d\n", id->cpu, cp_state, cp_cap); + ctdp_level->sst_cp_support = cp_cap; + ctdp_level->sst_cp_enabled = cp_state; + } + + debug_printf( + "cpu:%d CONFIG_TDP_GET_TDP_CONTROL resp:%x fact_support:%d pbf_support: %d fact_enabled:%d pbf_enabled:%d\n", + id->cpu, resp, ctdp_level->fact_support, ctdp_level->pbf_support, + ctdp_level->fact_enabled, ctdp_level->pbf_enabled); + + return 0; +} + +static void _get_uncore_p0_p1_info(struct isst_id *id, int config_index, + struct isst_pkg_ctdp_level_info *ctdp_level) +{ + unsigned int resp; + int ret; + + ctdp_level->uncore_pm = 0; + ctdp_level->uncore_p0 = 0; + ctdp_level->uncore_p1 = 0; + + ret = _send_mbox_command(id->cpu, CONFIG_TDP, + CONFIG_TDP_GET_RATIO_INFO, 0, + (BIT(16) | config_index) , &resp); + if (ret) { + goto try_uncore_mbox; + } + + ctdp_level->uncore_p0 = resp & GENMASK(7, 0); + ctdp_level->uncore_p1 = (resp & GENMASK(15, 8)) >> 8; + ctdp_level->uncore_pm = (resp & GENMASK(31, 24)) >> 24; + + debug_printf( + "cpu:%d ctdp:%d CONFIG_TDP_GET_RATIO_INFO resp:%x uncore p0:%d uncore p1:%d uncore pm:%d\n", + id->cpu, config_index, resp, ctdp_level->uncore_p0, ctdp_level->uncore_p1, + ctdp_level->uncore_pm); + + return; + +try_uncore_mbox: + ret = _send_mbox_command(id->cpu, CONFIG_TDP, + CONFIG_TDP_GET_UNCORE_P0_P1_INFO, 0, + config_index, &resp); + if (ret) { + ctdp_level->uncore_p0 = 0; + ctdp_level->uncore_p1 = 0; + return; + } + + ctdp_level->uncore_p0 = resp & GENMASK(7, 0); + ctdp_level->uncore_p1 = (resp & GENMASK(15, 8)) >> 8; + debug_printf( + "cpu:%d ctdp:%d CONFIG_TDP_GET_UNCORE_P0_P1_INFO resp:%x uncore p0:%d uncore p1:%d\n", + id->cpu, config_index, resp, ctdp_level->uncore_p0, + ctdp_level->uncore_p1); +} + +static int _set_uncore_min_max(struct isst_id *id, int max, int freq) +{ + char buffer[128], freq_str[16]; + int fd, ret, len; + + if (max) + snprintf(buffer, sizeof(buffer), + "/sys/devices/system/cpu/intel_uncore_frequency/package_%02d_die_%02d/max_freq_khz", id->pkg, id->die); + else + snprintf(buffer, sizeof(buffer), + "/sys/devices/system/cpu/intel_uncore_frequency/package_%02d_die_%02d/min_freq_khz", id->pkg, id->die); + + fd = open(buffer, O_WRONLY); + if (fd < 0) + return fd; + + snprintf(freq_str, sizeof(freq_str), "%d", freq); + len = strlen(freq_str); + ret = write(fd, freq_str, len); + if (ret == -1) { + close(fd); + return ret; + } + close(fd); + + return 0; +} + +static void mbox_adjust_uncore_freq(struct isst_id *id, int config_index, + struct isst_pkg_ctdp_level_info *ctdp_level) +{ + _get_uncore_p0_p1_info(id, config_index, ctdp_level); + if (ctdp_level->uncore_pm) + _set_uncore_min_max(id, 0, ctdp_level->uncore_pm * 100000); + + if (ctdp_level->uncore_p0) + _set_uncore_min_max(id, 1, ctdp_level->uncore_p0 * 100000); +} + +static void _get_p1_info(struct isst_id *id, int config_index, + struct isst_pkg_ctdp_level_info *ctdp_level) +{ + unsigned int resp; + int ret; + ret = _send_mbox_command(id->cpu, CONFIG_TDP, CONFIG_TDP_GET_P1_INFO, 0, + config_index, &resp); + if (ret) { + ctdp_level->sse_p1 = 0; + ctdp_level->avx2_p1 = 0; + ctdp_level->avx512_p1 = 0; + return; + } + + ctdp_level->sse_p1 = resp & GENMASK(7, 0); + ctdp_level->avx2_p1 = (resp & GENMASK(15, 8)) >> 8; + ctdp_level->avx512_p1 = (resp & GENMASK(23, 16)) >> 16; + ctdp_level->amx_p1 = (resp & GENMASK(31, 24)) >> 24; + debug_printf( + "cpu:%d ctdp:%d CONFIG_TDP_GET_P1_INFO resp:%x sse_p1:%d avx2_p1:%d avx512_p1:%d amx_p1:%d\n", + id->cpu, config_index, resp, ctdp_level->sse_p1, + ctdp_level->avx2_p1, ctdp_level->avx512_p1, ctdp_level->amx_p1); +} + +static void _get_uncore_mem_freq(struct isst_id *id, int config_index, + struct isst_pkg_ctdp_level_info *ctdp_level) +{ + unsigned int resp; + int ret; + + ret = _send_mbox_command(id->cpu, CONFIG_TDP, CONFIG_TDP_GET_MEM_FREQ, + 0, config_index, &resp); + if (ret) { + ctdp_level->mem_freq = 0; + return; + } + + ctdp_level->mem_freq = resp & GENMASK(7, 0); + if (is_spr_platform() || is_emr_platform()) { + ctdp_level->mem_freq *= 200; + } else if (is_icx_platform()) { + if (ctdp_level->mem_freq < 7) { + ctdp_level->mem_freq = (12 - ctdp_level->mem_freq) * 133.33 * 2 * 10; + ctdp_level->mem_freq /= 10; + if (ctdp_level->mem_freq % 10 > 5) + ctdp_level->mem_freq++; + } else { + ctdp_level->mem_freq = 0; + } + } else { + ctdp_level->mem_freq = 0; + } + debug_printf( + "cpu:%d ctdp:%d CONFIG_TDP_GET_MEM_FREQ resp:%x uncore mem_freq:%d\n", + id->cpu, config_index, resp, ctdp_level->mem_freq); +} + +static int mbox_get_tdp_info(struct isst_id *id, int config_index, + struct isst_pkg_ctdp_level_info *ctdp_level) +{ + unsigned int resp; + int ret; + + ret = _send_mbox_command(id->cpu, CONFIG_TDP, CONFIG_TDP_GET_TDP_INFO, + 0, config_index, &resp); + if (ret) { + isst_display_error_info_message(1, "Invalid level, Can't get TDP information at level", 1, config_index); + return ret; + } + + ctdp_level->pkg_tdp = resp & GENMASK(14, 0); + ctdp_level->tdp_ratio = (resp & GENMASK(23, 16)) >> 16; + + debug_printf( + "cpu:%d ctdp:%d CONFIG_TDP_GET_TDP_INFO resp:%x tdp_ratio:%d pkg_tdp:%d\n", + id->cpu, config_index, resp, ctdp_level->tdp_ratio, + ctdp_level->pkg_tdp); + + ret = _send_mbox_command(id->cpu, CONFIG_TDP, CONFIG_TDP_GET_TJMAX_INFO, + 0, config_index, &resp); + if (ret) + return ret; + + ctdp_level->t_proc_hot = resp & GENMASK(7, 0); + + _get_uncore_p0_p1_info(id, config_index, ctdp_level); + _get_p1_info(id, config_index, ctdp_level); + _get_uncore_mem_freq(id, config_index, ctdp_level); + + debug_printf( + "cpu:%d ctdp:%d CONFIG_TDP_GET_TJMAX_INFO resp:%x t_proc_hot:%d\n", + id->cpu, config_index, resp, ctdp_level->t_proc_hot); + + return 0; +} + +static int mbox_get_pwr_info(struct isst_id *id, int config_index, + struct isst_pkg_ctdp_level_info *ctdp_level) +{ + unsigned int resp; + int ret; + + ret = _send_mbox_command(id->cpu, CONFIG_TDP, CONFIG_TDP_GET_PWR_INFO, + 0, config_index, &resp); + if (ret) + return ret; + + ctdp_level->pkg_max_power = resp & GENMASK(14, 0); + ctdp_level->pkg_min_power = (resp & GENMASK(30, 16)) >> 16; + + debug_printf( + "cpu:%d ctdp:%d CONFIG_TDP_GET_PWR_INFO resp:%x pkg_max_power:%d pkg_min_power:%d\n", + id->cpu, config_index, resp, ctdp_level->pkg_max_power, + ctdp_level->pkg_min_power); + + return 0; +} + +static int mbox_get_coremask_info(struct isst_id *id, int config_index, + struct isst_pkg_ctdp_level_info *ctdp_level) +{ + unsigned int resp; + int i, ret; + + ctdp_level->cpu_count = 0; + for (i = 0; i < 2; ++i) { + unsigned long long mask; + int cpu_count = 0; + + ret = _send_mbox_command(id->cpu, CONFIG_TDP, + CONFIG_TDP_GET_CORE_MASK, 0, + (i << 8) | config_index, &resp); + if (ret) + return ret; + + debug_printf( + "cpu:%d ctdp:%d mask:%d CONFIG_TDP_GET_CORE_MASK resp:%x\n", + id->cpu, config_index, i, resp); + + mask = (unsigned long long)resp << (32 * i); + set_cpu_mask_from_punit_coremask(id, mask, + ctdp_level->core_cpumask_size, + ctdp_level->core_cpumask, + &cpu_count); + ctdp_level->cpu_count += cpu_count; + debug_printf("cpu:%d ctdp:%d mask:%d cpu count:%d\n", id->cpu, + config_index, i, ctdp_level->cpu_count); + } + + return 0; +} + +static int mbox_get_get_trl(struct isst_id *id, int level, int avx_level, int *trl) +{ + unsigned int req, resp; + int ret; + + req = level | (avx_level << 16); + ret = _send_mbox_command(id->cpu, CONFIG_TDP, + CONFIG_TDP_GET_TURBO_LIMIT_RATIOS, 0, req, + &resp); + if (ret) + return ret; + + debug_printf( + "cpu:%d CONFIG_TDP_GET_TURBO_LIMIT_RATIOS req:%x resp:%x\n", + id->cpu, req, resp); + + trl[0] = resp & GENMASK(7, 0); + trl[1] = (resp & GENMASK(15, 8)) >> 8; + trl[2] = (resp & GENMASK(23, 16)) >> 16; + trl[3] = (resp & GENMASK(31, 24)) >> 24; + + req = level | BIT(8) | (avx_level << 16); + ret = _send_mbox_command(id->cpu, CONFIG_TDP, + CONFIG_TDP_GET_TURBO_LIMIT_RATIOS, 0, req, + &resp); + if (ret) + return ret; + + debug_printf("cpu:%d CONFIG_TDP_GET_TURBO_LIMIT req:%x resp:%x\n", id->cpu, + req, resp); + + trl[4] = resp & GENMASK(7, 0); + trl[5] = (resp & GENMASK(15, 8)) >> 8; + trl[6] = (resp & GENMASK(23, 16)) >> 16; + trl[7] = (resp & GENMASK(31, 24)) >> 24; + + return 0; +} + +static int mbox_get_get_trls(struct isst_id *id, int level, struct isst_pkg_ctdp_level_info *ctdp_level) +{ + int trl_max_levels = isst_get_trl_max_levels(); + int i, ret; + + for (i = 0; i < trl_max_levels; i++) { + ret = mbox_get_get_trl(id, level, i, ctdp_level->trl_ratios[i]); + if (ret) + return ret; + } + return 0; +} + +static int mbox_get_trl_bucket_info(struct isst_id *id, int level, unsigned long long *buckets_info) +{ + int ret; + + debug_printf("cpu:%d bucket info via MSR\n", id->cpu); + + *buckets_info = 0; + + ret = isst_send_msr_command(id->cpu, 0x1ae, 0, buckets_info); + if (ret) + return ret; + + debug_printf("cpu:%d bucket info via MSR successful 0x%llx\n", id->cpu, + *buckets_info); + + return 0; +} + +static int mbox_set_tdp_level(struct isst_id *id, int tdp_level) +{ + unsigned int resp; + int ret; + + + if (isst_get_config_tdp_lock_status(id)) { + isst_display_error_info_message(1, "TDP is locked", 0, 0); + return -1; + + } + + ret = _send_mbox_command(id->cpu, CONFIG_TDP, CONFIG_TDP_SET_LEVEL, 0, + tdp_level, &resp); + if (ret) { + isst_display_error_info_message(1, "Set TDP level failed for level", 1, tdp_level); + return ret; + } + + return 0; +} + +static int mbox_get_pbf_info(struct isst_id *id, int level, struct isst_pbf_info *pbf_info) +{ + int max_punit_core, max_mask_index; + unsigned int req, resp; + int i, ret; + + max_punit_core = get_max_punit_core_id(id); + max_mask_index = max_punit_core > 32 ? 2 : 1; + + for (i = 0; i < max_mask_index; ++i) { + unsigned long long mask; + int count; + + ret = _send_mbox_command(id->cpu, CONFIG_TDP, + CONFIG_TDP_PBF_GET_CORE_MASK_INFO, + 0, (i << 8) | level, &resp); + if (ret) + break; + + debug_printf( + "cpu:%d CONFIG_TDP_PBF_GET_CORE_MASK_INFO resp:%x\n", + id->cpu, resp); + + mask = (unsigned long long)resp << (32 * i); + set_cpu_mask_from_punit_coremask(id, mask, + pbf_info->core_cpumask_size, + pbf_info->core_cpumask, + &count); + } + + req = level; + ret = _send_mbox_command(id->cpu, CONFIG_TDP, + CONFIG_TDP_PBF_GET_P1HI_P1LO_INFO, 0, req, + &resp); + if (ret) + return ret; + + debug_printf("cpu:%d CONFIG_TDP_PBF_GET_P1HI_P1LO_INFO resp:%x\n", id->cpu, + resp); + + pbf_info->p1_low = resp & 0xff; + pbf_info->p1_high = (resp & GENMASK(15, 8)) >> 8; + + req = level; + ret = _send_mbox_command( + id->cpu, CONFIG_TDP, CONFIG_TDP_PBF_GET_TDP_INFO, 0, req, &resp); + if (ret) + return ret; + + debug_printf("cpu:%d CONFIG_TDP_PBF_GET_TDP_INFO resp:%x\n", id->cpu, resp); + + pbf_info->tdp = resp & 0xffff; + + req = level; + ret = _send_mbox_command( + id->cpu, CONFIG_TDP, CONFIG_TDP_PBF_GET_TJ_MAX_INFO, 0, req, &resp); + if (ret) + return ret; + + debug_printf("cpu:%d CONFIG_TDP_PBF_GET_TJ_MAX_INFO resp:%x\n", id->cpu, + resp); + pbf_info->t_control = (resp >> 8) & 0xff; + pbf_info->t_prochot = resp & 0xff; + + return 0; +} + +static int mbox_set_pbf_fact_status(struct isst_id *id, int pbf, int enable) +{ + struct isst_pkg_ctdp pkg_dev; + struct isst_pkg_ctdp_level_info ctdp_level; + int current_level; + unsigned int req = 0, resp; + int ret; + + ret = isst_get_ctdp_levels(id, &pkg_dev); + if (ret) + debug_printf("cpu:%d No support for dynamic ISST\n", id->cpu); + + current_level = pkg_dev.current_level; + + ret = isst_get_ctdp_control(id, current_level, &ctdp_level); + if (ret) + return ret; + + if (pbf) { + if (ctdp_level.fact_enabled) + req = BIT(16); + + if (enable) + req |= BIT(17); + else + req &= ~BIT(17); + } else { + + if (enable && !ctdp_level.sst_cp_enabled) + isst_display_error_info_message(0, "Make sure to execute before: core-power enable", 0, 0); + + if (ctdp_level.pbf_enabled) + req = BIT(17); + + if (enable) + req |= BIT(16); + else + req &= ~BIT(16); + } + + ret = _send_mbox_command(id->cpu, CONFIG_TDP, + CONFIG_TDP_SET_TDP_CONTROL, 0, req, &resp); + if (ret) + return ret; + + debug_printf("cpu:%d CONFIG_TDP_SET_TDP_CONTROL pbf/fact:%d req:%x\n", + id->cpu, pbf, req); + + return 0; +} + +static int _get_fact_bucket_info(struct isst_id *id, int level, + struct isst_fact_bucket_info *bucket_info) +{ + unsigned int resp; + int i, k, ret; + + for (i = 0; i < 2; ++i) { + int j; + + ret = _send_mbox_command( + id->cpu, CONFIG_TDP, + CONFIG_TDP_GET_FACT_HP_TURBO_LIMIT_NUMCORES, 0, + (i << 8) | level, &resp); + if (ret) + return ret; + + debug_printf( + "cpu:%d CONFIG_TDP_GET_FACT_HP_TURBO_LIMIT_NUMCORES index:%d level:%d resp:%x\n", + id->cpu, i, level, resp); + + for (j = 0; j < 4; ++j) { + bucket_info[j + (i * 4)].hp_cores = + (resp >> (j * 8)) & 0xff; + } + } + + for (k = 0; k < 3; ++k) { + for (i = 0; i < 2; ++i) { + int j; + + ret = _send_mbox_command( + id->cpu, CONFIG_TDP, + CONFIG_TDP_GET_FACT_HP_TURBO_LIMIT_RATIOS, 0, + (k << 16) | (i << 8) | level, &resp); + if (ret) + return ret; + + debug_printf( + "cpu:%d CONFIG_TDP_GET_FACT_HP_TURBO_LIMIT_RATIOS index:%d level:%d avx:%d resp:%x\n", + id->cpu, i, level, k, resp); + + for (j = 0; j < 4; ++j) { + bucket_info[j + (i * 4)].hp_ratios[k] = + (resp >> (j * 8)) & 0xff; + } + } + } + + return 0; +} + +static int mbox_get_fact_info(struct isst_id *id, int level, int fact_bucket, struct isst_fact_info *fact_info) +{ + unsigned int resp; + int j, ret, print; + + ret = _send_mbox_command(id->cpu, CONFIG_TDP, + CONFIG_TDP_GET_FACT_LP_CLIPPING_RATIO, 0, + level, &resp); + if (ret) + return ret; + + debug_printf("cpu:%d CONFIG_TDP_GET_FACT_LP_CLIPPING_RATIO resp:%x\n", + id->cpu, resp); + + fact_info->lp_ratios[0] = resp & 0xff; + fact_info->lp_ratios[1] = (resp >> 8) & 0xff; + fact_info->lp_ratios[2] = (resp >> 16) & 0xff; + + ret = _get_fact_bucket_info(id, level, fact_info->bucket_info); + if (ret) + return ret; + + print = 0; + for (j = 0; j < ISST_FACT_MAX_BUCKETS; ++j) { + if (fact_bucket != 0xff && fact_bucket != j) + continue; + + if (!fact_info->bucket_info[j].hp_cores) + break; + + print = 1; + } + if (!print) { + isst_display_error_info_message(1, "Invalid bucket", 0, 0); + return -1; + } + + return 0; +} + +static int mbox_get_clos_information(struct isst_id *id, int *enable, int *type) +{ + unsigned int resp; + int ret; + + ret = _send_mbox_command(id->cpu, CONFIG_CLOS, CLOS_PM_QOS_CONFIG, 0, 0, + &resp); + if (ret) + return ret; + + debug_printf("cpu:%d CLOS_PM_QOS_CONFIG resp:%x\n", id->cpu, resp); + + if (resp & BIT(1)) + *enable = 1; + else + *enable = 0; + + if (resp & BIT(2)) + *type = 1; + else + *type = 0; + + return 0; +} + +static int _write_pm_config(struct isst_id *id, int cp_state) +{ + unsigned int req, resp; + int ret; + + if (cp_state) + req = BIT(16); + else + req = 0; + + ret = _send_mbox_command(id->cpu, WRITE_PM_CONFIG, PM_FEATURE, 0, req, + &resp); + if (ret) + return ret; + + debug_printf("cpu:%d WRITE_PM_CONFIG resp:%x\n", id->cpu, resp); + + return 0; +} + +static int mbox_pm_qos_config(struct isst_id *id, int enable_clos, int priority_type) +{ + unsigned int req, resp; + int ret; + + if (!enable_clos) { + struct isst_pkg_ctdp pkg_dev; + struct isst_pkg_ctdp_level_info ctdp_level; + + ret = isst_get_ctdp_levels(id, &pkg_dev); + if (ret) { + debug_printf("isst_get_ctdp_levels\n"); + return ret; + } + + ret = isst_get_ctdp_control(id, pkg_dev.current_level, + &ctdp_level); + if (ret) + return ret; + + if (ctdp_level.fact_enabled) { + isst_display_error_info_message(1, "Ignoring request, turbo-freq feature is still enabled", 0, 0); + return -EINVAL; + } + ret = _write_pm_config(id, 0); + if (ret) + isst_display_error_info_message(0, "WRITE_PM_CONFIG command failed, ignoring error", 0, 0); + } else { + ret = _write_pm_config(id, 1); + if (ret) + isst_display_error_info_message(0, "WRITE_PM_CONFIG command failed, ignoring error", 0, 0); + } + + ret = _send_mbox_command(id->cpu, CONFIG_CLOS, CLOS_PM_QOS_CONFIG, 0, 0, + &resp); + if (ret) { + isst_display_error_info_message(1, "CLOS_PM_QOS_CONFIG command failed", 0, 0); + return ret; + } + + debug_printf("cpu:%d CLOS_PM_QOS_CONFIG resp:%x\n", id->cpu, resp); + + req = resp; + + if (enable_clos) + req = req | BIT(1); + else + req = req & ~BIT(1); + + if (priority_type > 1) + isst_display_error_info_message(1, "Invalid priority type: Changing type to ordered", 0, 0); + + if (priority_type) + req = req | BIT(2); + else + req = req & ~BIT(2); + + ret = _send_mbox_command(id->cpu, CONFIG_CLOS, CLOS_PM_QOS_CONFIG, + BIT(MBOX_CMD_WRITE_BIT), req, &resp); + if (ret) + return ret; + + debug_printf("cpu:%d CLOS_PM_QOS_CONFIG priority type:%d req:%x\n", id->cpu, + priority_type, req); + + return 0; +} + +static int mbox_pm_get_clos(struct isst_id *id, int clos, struct isst_clos_config *clos_config) +{ + unsigned int resp; + int ret; + + ret = _send_mbox_command(id->cpu, CONFIG_CLOS, CLOS_PM_CLOS, clos, 0, + &resp); + if (ret) + return ret; + + clos_config->epp = resp & 0x0f; + clos_config->clos_prop_prio = (resp >> 4) & 0x0f; + clos_config->clos_min = (resp >> 8) & 0xff; + clos_config->clos_max = (resp >> 16) & 0xff; + clos_config->clos_desired = (resp >> 24) & 0xff; + + return 0; +} + +static int mbox_set_clos(struct isst_id *id, int clos, struct isst_clos_config *clos_config) +{ + unsigned int req, resp; + unsigned int param; + int ret; + + req = clos_config->epp & 0x0f; + req |= (clos_config->clos_prop_prio & 0x0f) << 4; + req |= (clos_config->clos_min & 0xff) << 8; + req |= (clos_config->clos_max & 0xff) << 16; + req |= (clos_config->clos_desired & 0xff) << 24; + + param = BIT(MBOX_CMD_WRITE_BIT) | clos; + + ret = _send_mbox_command(id->cpu, CONFIG_CLOS, CLOS_PM_CLOS, param, req, + &resp); + if (ret) + return ret; + + debug_printf("cpu:%d CLOS_PM_CLOS param:%x req:%x\n", id->cpu, param, req); + + return 0; +} + +static int mbox_clos_get_assoc_status(struct isst_id *id, int *clos_id) +{ + unsigned int resp; + unsigned int param; + int core_id, ret; + + core_id = find_phy_core_num(id->cpu); + param = core_id; + + ret = _send_mbox_command(id->cpu, CONFIG_CLOS, CLOS_PQR_ASSOC, param, 0, + &resp); + if (ret) + return ret; + + debug_printf("cpu:%d CLOS_PQR_ASSOC param:%x resp:%x\n", id->cpu, param, + resp); + *clos_id = (resp >> 16) & 0x03; + + return 0; +} + +static int mbox_clos_associate(struct isst_id *id, int clos_id) +{ + unsigned int req, resp; + unsigned int param; + int core_id, ret; + + req = (clos_id & 0x03) << 16; + core_id = find_phy_core_num(id->cpu); + param = BIT(MBOX_CMD_WRITE_BIT) | core_id; + + ret = _send_mbox_command(id->cpu, CONFIG_CLOS, CLOS_PQR_ASSOC, param, + req, &resp); + if (ret) + return ret; + + debug_printf("cpu:%d CLOS_PQR_ASSOC param:%x req:%x\n", id->cpu, param, + req); + + return 0; +} + +static struct isst_platform_ops mbox_ops = { + .get_disp_freq_multiplier = mbox_get_disp_freq_multiplier, + .get_trl_max_levels = mbox_get_trl_max_levels, + .get_trl_level_name = mbox_get_trl_level_name, + .update_platform_param = mbox_update_platform_param, + .is_punit_valid = mbox_is_punit_valid, + .read_pm_config = mbox_read_pm_config, + .get_config_levels = mbox_get_config_levels, + .get_ctdp_control = mbox_get_ctdp_control, + .get_tdp_info = mbox_get_tdp_info, + .get_pwr_info = mbox_get_pwr_info, + .get_coremask_info = mbox_get_coremask_info, + .get_get_trl = mbox_get_get_trl, + .get_get_trls = mbox_get_get_trls, + .get_trl_bucket_info = mbox_get_trl_bucket_info, + .set_tdp_level = mbox_set_tdp_level, + .get_pbf_info = mbox_get_pbf_info, + .set_pbf_fact_status = mbox_set_pbf_fact_status, + .get_fact_info = mbox_get_fact_info, + .adjust_uncore_freq = mbox_adjust_uncore_freq, + .get_clos_information = mbox_get_clos_information, + .pm_qos_config = mbox_pm_qos_config, + .pm_get_clos = mbox_pm_get_clos, + .set_clos = mbox_set_clos, + .clos_get_assoc_status = mbox_clos_get_assoc_status, + .clos_associate = mbox_clos_associate, +}; + +struct isst_platform_ops *mbox_get_platform_ops(void) +{ + return &mbox_ops; +} diff --git a/tools/power/x86/intel-speed-select/isst-core-tpmi.c b/tools/power/x86/intel-speed-select/isst-core-tpmi.c new file mode 100644 index 0000000000..3458768562 --- /dev/null +++ b/tools/power/x86/intel-speed-select/isst-core-tpmi.c @@ -0,0 +1,814 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Intel Speed Select -- Enumerate and control features for TPMI Interface + * Copyright (c) 2022 Intel Corporation. + */ + +#include <linux/isst_if.h> +#include "isst.h" + +int tpmi_process_ioctl(int ioctl_no, void *info) +{ + const char *pathname = "/dev/isst_interface"; + int fd; + + if (is_debug_enabled()) { + debug_printf("Issue IOCTL: "); + switch (ioctl_no) { + case ISST_IF_CORE_POWER_STATE: + debug_printf("ISST_IF_CORE_POWER_STATE\n"); + break; + case ISST_IF_CLOS_PARAM: + debug_printf("ISST_IF_CLOS_PARAM\n"); + break; + case ISST_IF_CLOS_ASSOC: + debug_printf("ISST_IF_CLOS_ASSOC\n"); + break; + case ISST_IF_PERF_LEVELS: + debug_printf("ISST_IF_PERF_LEVELS\n"); + break; + case ISST_IF_PERF_SET_LEVEL: + debug_printf("ISST_IF_PERF_SET_LEVEL\n"); + break; + case ISST_IF_PERF_SET_FEATURE: + debug_printf("ISST_IF_PERF_SET_FEATURE\n"); + break; + case ISST_IF_GET_PERF_LEVEL_INFO: + debug_printf("ISST_IF_GET_PERF_LEVEL_INFO\n"); + break; + case ISST_IF_GET_PERF_LEVEL_CPU_MASK: + debug_printf("ISST_IF_GET_PERF_LEVEL_CPU_MASK\n"); + break; + case ISST_IF_GET_BASE_FREQ_INFO: + debug_printf("ISST_IF_GET_BASE_FREQ_INFO\n"); + break; + case ISST_IF_GET_BASE_FREQ_CPU_MASK: + debug_printf("ISST_IF_GET_BASE_FREQ_CPU_MASK\n"); + break; + case ISST_IF_GET_TURBO_FREQ_INFO: + debug_printf("ISST_IF_GET_TURBO_FREQ_INFO\n"); + break; + case ISST_IF_COUNT_TPMI_INSTANCES: + debug_printf("ISST_IF_COUNT_TPMI_INSTANCES\n"); + break; + default: + debug_printf("%d\n", ioctl_no); + break; + } + } + + fd = open(pathname, O_RDWR); + if (fd < 0) + return -1; + + if (ioctl(fd, ioctl_no, info) == -1) { + debug_printf("IOCTL %d Failed\n", ioctl_no); + close(fd); + return -1; + } + + close(fd); + + return 0; +} + +static int tpmi_get_disp_freq_multiplier(void) +{ + return 1; +} + +static int tpmi_get_trl_max_levels(void) +{ + return TRL_MAX_LEVELS; +} + +static char *tpmi_get_trl_level_name(int level) +{ + switch (level) { + case 0: + return "level-0"; + case 1: + return "level-1"; + case 2: + return "level-2"; + case 3: + return "level-3"; + case 4: + return "level-4"; + case 5: + return "level-5"; + case 6: + return "level-6"; + case 7: + return "level-7"; + default: + return NULL; + } +} + + +static void tpmi_update_platform_param(enum isst_platform_param param, int value) +{ + /* No params need to be updated for now */ +} + +static int tpmi_is_punit_valid(struct isst_id *id) +{ + struct isst_tpmi_instance_count info; + int ret; + + if (id->punit < 0) + return 0; + + info.socket_id = id->pkg; + ret = tpmi_process_ioctl(ISST_IF_COUNT_TPMI_INSTANCES, &info); + if (ret == -1) + return 0; + + if (info.valid_mask & BIT(id->punit)) + return 1; + + return 0; +} + +static int tpmi_read_pm_config(struct isst_id *id, int *cp_state, int *cp_cap) +{ + struct isst_core_power info; + int ret; + + info.get_set = 0; + info.socket_id = id->pkg; + info.power_domain_id = id->punit; + ret = tpmi_process_ioctl(ISST_IF_CORE_POWER_STATE, &info); + if (ret == -1) + return ret; + + *cp_state = info.enable; + *cp_cap = info.supported; + + return 0; +} + +int tpmi_get_config_levels(struct isst_id *id, struct isst_pkg_ctdp *pkg_dev) +{ + struct isst_perf_level_info info; + int ret; + + info.socket_id = id->pkg; + info.power_domain_id = id->punit; + + ret = tpmi_process_ioctl(ISST_IF_PERF_LEVELS, &info); + if (ret == -1) + return ret; + + pkg_dev->version = info.feature_rev; + pkg_dev->levels = info.max_level; + pkg_dev->locked = info.locked; + pkg_dev->current_level = info.current_level; + pkg_dev->locked = info.locked; + pkg_dev->enabled = info.enabled; + + return 0; +} + +static int tpmi_get_ctdp_control(struct isst_id *id, int config_index, + struct isst_pkg_ctdp_level_info *ctdp_level) +{ + struct isst_core_power core_power_info; + struct isst_perf_level_info info; + int level_mask; + int ret; + + info.socket_id = id->pkg; + info.power_domain_id = id->punit; + + ret = tpmi_process_ioctl(ISST_IF_PERF_LEVELS, &info); + if (ret == -1) + return -1; + + if (config_index != 0xff) + level_mask = 1 << config_index; + else + level_mask = config_index; + + if (!(info.level_mask & level_mask)) + return -1; + + ctdp_level->fact_support = info.sst_tf_support; + ctdp_level->pbf_support = info.sst_bf_support; + ctdp_level->fact_enabled = !!(info.feature_state & BIT(1)); + ctdp_level->pbf_enabled = !!(info.feature_state & BIT(0)); + + core_power_info.get_set = 0; + core_power_info.socket_id = id->pkg; + core_power_info.power_domain_id = id->punit; + + ret = tpmi_process_ioctl(ISST_IF_CORE_POWER_STATE, &core_power_info); + if (ret == -1) + return ret; + + ctdp_level->sst_cp_support = core_power_info.supported; + ctdp_level->sst_cp_enabled = core_power_info.enable; + + debug_printf + ("cpu:%d CONFIG_TDP_GET_TDP_CONTROL fact_support:%d pbf_support: %d fact_enabled:%d pbf_enabled:%d\n", + id->cpu, ctdp_level->fact_support, ctdp_level->pbf_support, + ctdp_level->fact_enabled, ctdp_level->pbf_enabled); + + return 0; +} + +static int tpmi_get_tdp_info(struct isst_id *id, int config_index, + struct isst_pkg_ctdp_level_info *ctdp_level) +{ + struct isst_perf_level_data_info info; + int ret; + + info.socket_id = id->pkg; + info.power_domain_id = id->punit; + info.level = config_index; + + ret = tpmi_process_ioctl(ISST_IF_GET_PERF_LEVEL_INFO, &info); + if (ret == -1) + return ret; + + ctdp_level->pkg_tdp = info.thermal_design_power_w; + ctdp_level->tdp_ratio = info.tdp_ratio; + ctdp_level->sse_p1 = info.base_freq_mhz; + ctdp_level->avx2_p1 = info.base_freq_avx2_mhz; + ctdp_level->avx512_p1 = info.base_freq_avx512_mhz; + ctdp_level->amx_p1 = info.base_freq_amx_mhz; + + ctdp_level->t_proc_hot = info.tjunction_max_c; + ctdp_level->mem_freq = info.max_memory_freq_mhz; + ctdp_level->cooling_type = info.cooling_type; + + ctdp_level->uncore_p0 = info.p0_fabric_freq_mhz; + ctdp_level->uncore_p1 = info.p1_fabric_freq_mhz; + ctdp_level->uncore_pm = info.pm_fabric_freq_mhz; + + debug_printf + ("cpu:%d ctdp:%d CONFIG_TDP_GET_TDP_INFO tdp_ratio:%d pkg_tdp:%d ctdp_level->t_proc_hot:%d\n", + id->cpu, config_index, ctdp_level->tdp_ratio, ctdp_level->pkg_tdp, + ctdp_level->t_proc_hot); + + return 0; +} + +static int tpmi_get_pwr_info(struct isst_id *id, int config_index, + struct isst_pkg_ctdp_level_info *ctdp_level) +{ + /* TBD */ + ctdp_level->pkg_max_power = 0; + ctdp_level->pkg_min_power = 0; + + debug_printf + ("cpu:%d ctdp:%d CONFIG_TDP_GET_PWR_INFO pkg_max_power:%d pkg_min_power:%d\n", + id->cpu, config_index, ctdp_level->pkg_max_power, + ctdp_level->pkg_min_power); + + return 0; +} + +int tpmi_get_coremask_info(struct isst_id *id, int config_index, + struct isst_pkg_ctdp_level_info *ctdp_level) +{ + struct isst_perf_level_cpu_mask info; + int ret, cpu_count; + + info.socket_id = id->pkg; + info.power_domain_id = id->punit; + info.level = config_index; + info.punit_cpu_map = 1; + + ret = tpmi_process_ioctl(ISST_IF_GET_PERF_LEVEL_CPU_MASK, &info); + if (ret == -1) + return ret; + + set_cpu_mask_from_punit_coremask(id, info.mask, + ctdp_level->core_cpumask_size, + ctdp_level->core_cpumask, &cpu_count); + ctdp_level->cpu_count = cpu_count; + + debug_printf("cpu:%d ctdp:%d core_mask ino cpu count:%d\n", + id->cpu, config_index, ctdp_level->cpu_count); + + return 0; +} + +static int tpmi_get_get_trls(struct isst_id *id, int config_index, + struct isst_pkg_ctdp_level_info *ctdp_level) +{ + struct isst_perf_level_data_info info; + int ret, i, j; + + info.socket_id = id->pkg; + info.power_domain_id = id->punit; + info.level = config_index; + + ret = tpmi_process_ioctl(ISST_IF_GET_PERF_LEVEL_INFO, &info); + if (ret == -1) + return ret; + + if (info.max_buckets > TRL_MAX_BUCKETS) + info.max_buckets = TRL_MAX_BUCKETS; + + if (info.max_trl_levels > TRL_MAX_LEVELS) + info.max_trl_levels = TRL_MAX_LEVELS; + + for (i = 0; i < info.max_trl_levels; ++i) + for (j = 0; j < info.max_buckets; ++j) + ctdp_level->trl_ratios[i][j] = info.trl_freq_mhz[i][j]; + + return 0; +} + +static int tpmi_get_get_trl(struct isst_id *id, int level, int config_index, + int *trl) +{ + struct isst_pkg_ctdp_level_info ctdp_level; + int ret, i; + + ret = tpmi_get_get_trls(id, config_index, &ctdp_level); + if (ret) + return ret; + + /* FIX ME: Just return for level 0 */ + for (i = 0; i < 8; ++i) + trl[i] = ctdp_level.trl_ratios[0][i]; + + return 0; +} + +static int tpmi_get_trl_bucket_info(struct isst_id *id, int config_index, + unsigned long long *buckets_info) +{ + struct isst_perf_level_data_info info; + unsigned char *mask = (unsigned char *)buckets_info; + int ret, i; + + info.socket_id = id->pkg; + info.power_domain_id = id->punit; + info.level = config_index; + + ret = tpmi_process_ioctl(ISST_IF_GET_PERF_LEVEL_INFO, &info); + if (ret == -1) + return ret; + + if (info.max_buckets > TRL_MAX_BUCKETS) + info.max_buckets = TRL_MAX_BUCKETS; + + for (i = 0; i < info.max_buckets; ++i) + mask[i] = info.bucket_core_counts[i]; + + debug_printf("cpu:%d TRL bucket info: 0x%llx\n", id->cpu, + *buckets_info); + + return 0; +} + +static int tpmi_set_tdp_level(struct isst_id *id, int tdp_level) +{ + struct isst_perf_level_control info; + int ret; + + info.socket_id = id->pkg; + info.power_domain_id = id->punit; + info.level = tdp_level; + + ret = tpmi_process_ioctl(ISST_IF_PERF_SET_LEVEL, &info); + if (ret == -1) + return ret; + + return 0; +} + +static int _pbf_get_coremask_info(struct isst_id *id, int config_index, + struct isst_pbf_info *pbf_info) +{ + struct isst_perf_level_cpu_mask info; + int ret, cpu_count; + + info.socket_id = id->pkg; + info.power_domain_id = id->punit; + info.level = config_index; + info.punit_cpu_map = 1; + + ret = tpmi_process_ioctl(ISST_IF_GET_BASE_FREQ_CPU_MASK, &info); + if (ret == -1) + return ret; + + set_cpu_mask_from_punit_coremask(id, info.mask, + pbf_info->core_cpumask_size, + pbf_info->core_cpumask, &cpu_count); + + debug_printf("cpu:%d ctdp:%d pbf core_mask info cpu count:%d\n", + id->cpu, config_index, cpu_count); + + return 0; +} + +static int tpmi_get_pbf_info(struct isst_id *id, int level, + struct isst_pbf_info *pbf_info) +{ + struct isst_base_freq_info info; + int ret; + + info.socket_id = id->pkg; + info.power_domain_id = id->punit; + info.level = level; + + ret = tpmi_process_ioctl(ISST_IF_GET_BASE_FREQ_INFO, &info); + if (ret == -1) + return ret; + + pbf_info->p1_low = info.low_base_freq_mhz; + pbf_info->p1_high = info.high_base_freq_mhz; + pbf_info->tdp = info.thermal_design_power_w; + pbf_info->t_prochot = info.tjunction_max_c; + + debug_printf("cpu:%d ctdp:%d pbf info:%d:%d:%d:%d\n", + id->cpu, level, pbf_info->p1_low, pbf_info->p1_high, + pbf_info->tdp, pbf_info->t_prochot); + + return _pbf_get_coremask_info(id, level, pbf_info); +} + +static int tpmi_set_pbf_fact_status(struct isst_id *id, int pbf, int enable) +{ + struct isst_pkg_ctdp pkg_dev; + struct isst_pkg_ctdp_level_info ctdp_level; + int current_level; + struct isst_perf_feature_control info; + int ret; + + ret = isst_get_ctdp_levels(id, &pkg_dev); + if (ret) + debug_printf("cpu:%d No support for dynamic ISST\n", id->cpu); + + current_level = pkg_dev.current_level; + + ret = isst_get_ctdp_control(id, current_level, &ctdp_level); + if (ret) + return ret; + + info.socket_id = id->pkg; + info.power_domain_id = id->punit; + + info.feature = 0; + + if (pbf) { + if (ctdp_level.fact_enabled) + info.feature |= BIT(1); + + if (enable) + info.feature |= BIT(0); + else + info.feature &= ~BIT(0); + } else { + + if (enable && !ctdp_level.sst_cp_enabled) + isst_display_error_info_message(0, + "Make sure to execute before: core-power enable", + 0, 0); + + if (ctdp_level.pbf_enabled) + info.feature |= BIT(0); + + if (enable) + info.feature |= BIT(1); + else + info.feature &= ~BIT(1); + } + + ret = tpmi_process_ioctl(ISST_IF_PERF_SET_FEATURE, &info); + if (ret == -1) + return ret; + + return 0; +} + +static int tpmi_get_fact_info(struct isst_id *id, int level, int fact_bucket, + struct isst_fact_info *fact_info) +{ + struct isst_turbo_freq_info info; + int i, j; + int ret; + + info.socket_id = id->pkg; + info.power_domain_id = id->punit; + info.level = level; + + ret = tpmi_process_ioctl(ISST_IF_GET_TURBO_FREQ_INFO, &info); + if (ret == -1) + return ret; + + for (i = 0; i < info.max_clip_freqs; ++i) + fact_info->lp_ratios[i] = info.lp_clip_freq_mhz[i]; + + if (info.max_buckets > TRL_MAX_BUCKETS) + info.max_buckets = TRL_MAX_BUCKETS; + + if (info.max_trl_levels > TRL_MAX_LEVELS) + info.max_trl_levels = TRL_MAX_LEVELS; + + for (i = 0; i < info.max_trl_levels; ++i) { + for (j = 0; j < info.max_buckets; ++j) + fact_info->bucket_info[j].hp_ratios[i] = + info.trl_freq_mhz[i][j]; + } + + for (i = 0; i < info.max_buckets; ++i) + fact_info->bucket_info[i].hp_cores = info.bucket_core_counts[i]; + + return 0; +} + +static void _set_uncore_min_max(struct isst_id *id, int max, int freq) +{ + DIR *dir; + FILE *filep; + struct dirent *entry; + char buffer[512]; + unsigned int tmp_id; + int ret; + + dir = opendir("/sys/devices/system/cpu/intel_uncore_frequency/"); + if (!dir) + return; + + while ((entry = readdir(dir)) != NULL ) { + /* Check domain_id */ + snprintf(buffer, sizeof(buffer), + "/sys/devices/system/cpu/intel_uncore_frequency/%s/domain_id", entry->d_name); + + filep = fopen(buffer, "r"); + if (!filep) + goto end; + + ret = fscanf(filep, "%u", &tmp_id); + fclose(filep); + if (ret != 1) + goto end; + + if (tmp_id != id->punit) + continue; + + /* Check package_id */ + snprintf(buffer, sizeof(buffer), + "/sys/devices/system/cpu/intel_uncore_frequency/%s/package_id", entry->d_name); + + filep = fopen(buffer, "r"); + if (!filep) + goto end; + + ret = fscanf(filep, "%u", &tmp_id); + fclose(filep); + + if (ret != 1) + goto end; + + if (tmp_id != id->pkg) + continue; + + /* Found the right sysfs path, adjust and quit */ + if (max) + snprintf(buffer, sizeof(buffer), + "/sys/devices/system/cpu/intel_uncore_frequency/%s/max_freq_khz", entry->d_name); + else + snprintf(buffer, sizeof(buffer), + "/sys/devices/system/cpu/intel_uncore_frequency/%s/min_freq_khz", entry->d_name); + + filep = fopen(buffer, "w"); + if (!filep) + goto end; + + fprintf(filep, "%d\n", freq); + fclose(filep); + break; + } + +end: + closedir(dir); +} + +static void tpmi_adjust_uncore_freq(struct isst_id *id, int config_index, + struct isst_pkg_ctdp_level_info *ctdp_level) +{ + struct isst_perf_level_data_info info; + int ret; + + info.socket_id = id->pkg; + info.power_domain_id = id->punit; + info.level = config_index; + + ret = tpmi_process_ioctl(ISST_IF_GET_PERF_LEVEL_INFO, &info); + if (ret == -1) + return; + + ctdp_level->uncore_p0 = info.p0_fabric_freq_mhz; + ctdp_level->uncore_p1 = info.p1_fabric_freq_mhz; + ctdp_level->uncore_pm = info.pm_fabric_freq_mhz; + + if (ctdp_level->uncore_pm) + _set_uncore_min_max(id, 0, ctdp_level->uncore_pm * 100000); + + if (ctdp_level->uncore_p0) + _set_uncore_min_max(id, 1, ctdp_level->uncore_p0 * 100000); + + return; +} + +static int tpmi_get_clos_information(struct isst_id *id, int *enable, int *type) +{ + struct isst_core_power info; + int ret; + + info.get_set = 0; + info.socket_id = id->pkg; + info.power_domain_id = id->punit; + ret = tpmi_process_ioctl(ISST_IF_CORE_POWER_STATE, &info); + if (ret == -1) + return ret; + + *enable = info.enable; + *type = info.priority_type; + + return 0; +} + +static int tpmi_pm_qos_config(struct isst_id *id, int enable_clos, + int priority_type) +{ + struct isst_core_power info; + int i, ret, saved_punit; + + info.get_set = 1; + info.socket_id = id->pkg; + info.power_domain_id = id->punit; + info.enable = enable_clos; + info.priority_type = priority_type; + + saved_punit = id->punit; + + /* Set for all other dies also. This is per package setting */ + for (i = 0; i < MAX_PUNIT_PER_DIE; i++) { + id->punit = i; + if (isst_is_punit_valid(id)) { + info.power_domain_id = i; + ret = tpmi_process_ioctl(ISST_IF_CORE_POWER_STATE, &info); + if (ret == -1) { + id->punit = saved_punit; + return ret; + } + } + } + + id->punit = saved_punit; + + return 0; +} + +int tpmi_pm_get_clos(struct isst_id *id, int clos, + struct isst_clos_config *clos_config) +{ + struct isst_clos_param info; + int ret; + + info.get_set = 0; + info.socket_id = id->pkg; + info.power_domain_id = id->punit; + info.clos = clos; + + ret = tpmi_process_ioctl(ISST_IF_CLOS_PARAM, &info); + if (ret == -1) + return ret; + + clos_config->epp = 0; + clos_config->clos_prop_prio = info.prop_prio; + clos_config->clos_min = info.min_freq_mhz; + clos_config->clos_max = info.max_freq_mhz; + clos_config->clos_desired = 0; + + debug_printf("cpu:%d clos:%d min:%d max:%d\n", id->cpu, clos, + clos_config->clos_min, clos_config->clos_max); + + return 0; +} + +int tpmi_set_clos(struct isst_id *id, int clos, + struct isst_clos_config *clos_config) +{ + struct isst_clos_param info; + int i, ret, saved_punit; + + info.get_set = 1; + info.socket_id = id->pkg; + info.power_domain_id = id->punit; + info.clos = clos; + info.prop_prio = clos_config->clos_prop_prio; + + info.min_freq_mhz = clos_config->clos_min; + info.max_freq_mhz = clos_config->clos_max; + + if (info.min_freq_mhz <= 0xff) + info.min_freq_mhz *= 100; + if (info.max_freq_mhz <= 0xff) + info.max_freq_mhz *= 100; + + saved_punit = id->punit; + + /* Set for all other dies also. This is per package setting */ + for (i = 0; i < MAX_PUNIT_PER_DIE; i++) { + id->punit = i; + if (isst_is_punit_valid(id)) { + info.power_domain_id = i; + ret = tpmi_process_ioctl(ISST_IF_CLOS_PARAM, &info); + if (ret == -1) { + id->punit = saved_punit; + return ret; + } + } + } + + id->punit = saved_punit; + + debug_printf("set cpu:%d clos:%d min:%d max:%d\n", id->cpu, clos, + clos_config->clos_min, clos_config->clos_max); + + return 0; +} + +static int tpmi_clos_get_assoc_status(struct isst_id *id, int *clos_id) +{ + struct isst_if_clos_assoc_cmds assoc_cmds; + int ret; + + assoc_cmds.cmd_count = 1; + assoc_cmds.get_set = 0; + assoc_cmds.punit_cpu_map = 1; + assoc_cmds.assoc_info[0].logical_cpu = find_phy_core_num(id->cpu); + assoc_cmds.assoc_info[0].socket_id = id->pkg; + assoc_cmds.assoc_info[0].power_domain_id = id->punit; + + ret = tpmi_process_ioctl(ISST_IF_CLOS_ASSOC, &assoc_cmds); + if (ret == -1) + return ret; + + *clos_id = assoc_cmds.assoc_info[0].clos; + + return 0; +} + +static int tpmi_clos_associate(struct isst_id *id, int clos_id) +{ + struct isst_if_clos_assoc_cmds assoc_cmds; + int ret; + + assoc_cmds.cmd_count = 1; + assoc_cmds.get_set = 1; + assoc_cmds.punit_cpu_map = 1; + assoc_cmds.assoc_info[0].logical_cpu = find_phy_core_num(id->cpu); + assoc_cmds.assoc_info[0].clos = clos_id; + assoc_cmds.assoc_info[0].socket_id = id->pkg; + assoc_cmds.assoc_info[0].power_domain_id = id->punit; + + ret = tpmi_process_ioctl(ISST_IF_CLOS_ASSOC, &assoc_cmds); + if (ret == -1) + return ret; + + return 0; +} + +static struct isst_platform_ops tpmi_ops = { + .get_disp_freq_multiplier = tpmi_get_disp_freq_multiplier, + .get_trl_max_levels = tpmi_get_trl_max_levels, + .get_trl_level_name = tpmi_get_trl_level_name, + .update_platform_param = tpmi_update_platform_param, + .is_punit_valid = tpmi_is_punit_valid, + .read_pm_config = tpmi_read_pm_config, + .get_config_levels = tpmi_get_config_levels, + .get_ctdp_control = tpmi_get_ctdp_control, + .get_tdp_info = tpmi_get_tdp_info, + .get_pwr_info = tpmi_get_pwr_info, + .get_coremask_info = tpmi_get_coremask_info, + .get_get_trl = tpmi_get_get_trl, + .get_get_trls = tpmi_get_get_trls, + .get_trl_bucket_info = tpmi_get_trl_bucket_info, + .set_tdp_level = tpmi_set_tdp_level, + .get_pbf_info = tpmi_get_pbf_info, + .set_pbf_fact_status = tpmi_set_pbf_fact_status, + .get_fact_info = tpmi_get_fact_info, + .adjust_uncore_freq = tpmi_adjust_uncore_freq, + .get_clos_information = tpmi_get_clos_information, + .pm_qos_config = tpmi_pm_qos_config, + .pm_get_clos = tpmi_pm_get_clos, + .set_clos = tpmi_set_clos, + .clos_get_assoc_status = tpmi_clos_get_assoc_status, + .clos_associate = tpmi_clos_associate, +}; + +struct isst_platform_ops *tpmi_get_platform_ops(void) +{ + return &tpmi_ops; +} diff --git a/tools/power/x86/intel-speed-select/isst-core.c b/tools/power/x86/intel-speed-select/isst-core.c new file mode 100644 index 0000000000..f55fef4c13 --- /dev/null +++ b/tools/power/x86/intel-speed-select/isst-core.c @@ -0,0 +1,498 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Intel Speed Select -- Enumerate and control features + * Copyright (c) 2019 Intel Corporation. + */ + +#include "isst.h" + +static struct isst_platform_ops *isst_ops; + +#define CHECK_CB(_name) \ + do { \ + if (!isst_ops || !isst_ops->_name) { \ + fprintf(stderr, "Invalid ops\n"); \ + exit(0); \ + } \ + } while (0) + +int isst_set_platform_ops(int api_version) +{ + switch (api_version) { + case 1: + isst_ops = mbox_get_platform_ops(); + break; + case 2: + isst_ops = tpmi_get_platform_ops(); + break; + default: + isst_ops = NULL; + break; + } + + if (!isst_ops) + return -1; + return 0; +} + +void isst_update_platform_param(enum isst_platform_param param, int value) +{ + CHECK_CB(update_platform_param); + + isst_ops->update_platform_param(param, value); +} + +int isst_get_disp_freq_multiplier(void) +{ + CHECK_CB(get_disp_freq_multiplier); + return isst_ops->get_disp_freq_multiplier(); +} + +int isst_get_trl_max_levels(void) +{ + CHECK_CB(get_trl_max_levels); + return isst_ops->get_trl_max_levels(); +} + +char *isst_get_trl_level_name(int level) +{ + CHECK_CB(get_trl_level_name); + return isst_ops->get_trl_level_name(level); +} + +int isst_is_punit_valid(struct isst_id *id) +{ + CHECK_CB(is_punit_valid); + return isst_ops->is_punit_valid(id); +} + +int isst_send_msr_command(unsigned int cpu, unsigned int msr, int write, + unsigned long long *req_resp) +{ + struct isst_if_msr_cmds msr_cmds; + const char *pathname = "/dev/isst_interface"; + FILE *outf = get_output_file(); + int fd; + + fd = open(pathname, O_RDWR); + if (fd < 0) + err(-1, "%s open failed", pathname); + + msr_cmds.cmd_count = 1; + msr_cmds.msr_cmd[0].logical_cpu = cpu; + msr_cmds.msr_cmd[0].msr = msr; + msr_cmds.msr_cmd[0].read_write = write; + if (write) + msr_cmds.msr_cmd[0].data = *req_resp; + + if (ioctl(fd, ISST_IF_MSR_COMMAND, &msr_cmds) == -1) { + perror("ISST_IF_MSR_COMMAND"); + fprintf(outf, "Error: msr_cmd cpu:%d msr:%x read_write:%d\n", + cpu, msr, write); + } else { + if (!write) + *req_resp = msr_cmds.msr_cmd[0].data; + + debug_printf( + "msr_cmd response: cpu:%d msr:%x rd_write:%x resp:%llx %llx\n", + cpu, msr, write, *req_resp, msr_cmds.msr_cmd[0].data); + } + + close(fd); + + return 0; +} + +int isst_read_pm_config(struct isst_id *id, int *cp_state, int *cp_cap) +{ + CHECK_CB(read_pm_config); + return isst_ops->read_pm_config(id, cp_state, cp_cap); +} + +int isst_get_ctdp_levels(struct isst_id *id, struct isst_pkg_ctdp *pkg_dev) +{ + CHECK_CB(get_config_levels); + return isst_ops->get_config_levels(id, pkg_dev); +} + +int isst_get_ctdp_control(struct isst_id *id, int config_index, + struct isst_pkg_ctdp_level_info *ctdp_level) +{ + CHECK_CB(get_ctdp_control); + return isst_ops->get_ctdp_control(id, config_index, ctdp_level); +} + +int isst_get_tdp_info(struct isst_id *id, int config_index, + struct isst_pkg_ctdp_level_info *ctdp_level) +{ + CHECK_CB(get_tdp_info); + return isst_ops->get_tdp_info(id, config_index, ctdp_level); +} + +int isst_get_pwr_info(struct isst_id *id, int config_index, + struct isst_pkg_ctdp_level_info *ctdp_level) +{ + CHECK_CB(get_pwr_info); + return isst_ops->get_pwr_info(id, config_index, ctdp_level); +} + +int isst_get_coremask_info(struct isst_id *id, int config_index, + struct isst_pkg_ctdp_level_info *ctdp_level) +{ + CHECK_CB(get_coremask_info); + return isst_ops->get_coremask_info(id, config_index, ctdp_level); +} + +int isst_get_get_trl_from_msr(struct isst_id *id, int *trl) +{ + unsigned long long msr_trl; + int ret; + + ret = isst_send_msr_command(id->cpu, 0x1AD, 0, &msr_trl); + if (ret) + return ret; + + trl[0] = msr_trl & GENMASK(7, 0); + trl[1] = (msr_trl & GENMASK(15, 8)) >> 8; + trl[2] = (msr_trl & GENMASK(23, 16)) >> 16; + trl[3] = (msr_trl & GENMASK(31, 24)) >> 24; + trl[4] = (msr_trl & GENMASK(39, 32)) >> 32; + trl[5] = (msr_trl & GENMASK(47, 40)) >> 40; + trl[6] = (msr_trl & GENMASK(55, 48)) >> 48; + trl[7] = (msr_trl & GENMASK(63, 56)) >> 56; + + return 0; +} + +int isst_get_get_trl(struct isst_id *id, int level, int avx_level, int *trl) +{ + CHECK_CB(get_get_trl); + return isst_ops->get_get_trl(id, level, avx_level, trl); +} + +int isst_get_get_trls(struct isst_id *id, int level, struct isst_pkg_ctdp_level_info *ctdp_level) +{ + CHECK_CB(get_get_trls); + return isst_ops->get_get_trls(id, level, ctdp_level); +} + +int isst_get_trl_bucket_info(struct isst_id *id, int level, unsigned long long *buckets_info) +{ + CHECK_CB(get_trl_bucket_info); + return isst_ops->get_trl_bucket_info(id, level, buckets_info); +} + +int isst_set_tdp_level(struct isst_id *id, int tdp_level) +{ + CHECK_CB(set_tdp_level); + return isst_ops->set_tdp_level(id, tdp_level); +} + +int isst_get_pbf_info(struct isst_id *id, int level, struct isst_pbf_info *pbf_info) +{ + struct isst_pkg_ctdp_level_info ctdp_level; + struct isst_pkg_ctdp pkg_dev; + int ret; + + ret = isst_get_ctdp_levels(id, &pkg_dev); + if (ret) { + isst_display_error_info_message(1, "Failed to get number of levels", 0, 0); + return ret; + } + + if (level > pkg_dev.levels) { + isst_display_error_info_message(1, "Invalid level", 1, level); + return -1; + } + + ret = isst_get_ctdp_control(id, level, &ctdp_level); + if (ret) + return ret; + + if (!ctdp_level.pbf_support) { + isst_display_error_info_message(1, "base-freq feature is not present at this level", 1, level); + return -1; + } + + pbf_info->core_cpumask_size = alloc_cpu_set(&pbf_info->core_cpumask); + + CHECK_CB(get_pbf_info); + return isst_ops->get_pbf_info(id, level, pbf_info); +} + +int isst_set_pbf_fact_status(struct isst_id *id, int pbf, int enable) +{ + CHECK_CB(set_pbf_fact_status); + return isst_ops->set_pbf_fact_status(id, pbf, enable); +} + + + +int isst_get_fact_info(struct isst_id *id, int level, int fact_bucket, struct isst_fact_info *fact_info) +{ + struct isst_pkg_ctdp_level_info ctdp_level; + struct isst_pkg_ctdp pkg_dev; + int ret; + + ret = isst_get_ctdp_levels(id, &pkg_dev); + if (ret) { + isst_display_error_info_message(1, "Failed to get number of levels", 0, 0); + return ret; + } + + if (level > pkg_dev.levels) { + isst_display_error_info_message(1, "Invalid level", 1, level); + return -1; + } + + ret = isst_get_ctdp_control(id, level, &ctdp_level); + if (ret) + return ret; + + if (!ctdp_level.fact_support) { + isst_display_error_info_message(1, "turbo-freq feature is not present at this level", 1, level); + return -1; + } + CHECK_CB(get_fact_info); + return isst_ops->get_fact_info(id, level, fact_bucket, fact_info); +} + +int isst_get_trl(struct isst_id *id, unsigned long long *trl) +{ + int ret; + + ret = isst_send_msr_command(id->cpu, 0x1AD, 0, trl); + if (ret) + return ret; + + return 0; +} + +int isst_set_trl(struct isst_id *id, unsigned long long trl) +{ + int ret; + + if (!trl) + trl = 0xFFFFFFFFFFFFFFFFULL; + + ret = isst_send_msr_command(id->cpu, 0x1AD, 1, &trl); + if (ret) + return ret; + + return 0; +} + +int isst_set_trl_from_current_tdp(struct isst_id *id, unsigned long long trl) +{ + unsigned long long msr_trl; + int ret; + + if (id->cpu < 0) + return 0; + + if (trl) { + msr_trl = trl; + } else { + struct isst_pkg_ctdp pkg_dev; + int trl[8]; + int i; + + ret = isst_get_ctdp_levels(id, &pkg_dev); + if (ret) + return ret; + + ret = isst_get_get_trl(id, pkg_dev.current_level, 0, trl); + if (ret) + return ret; + + msr_trl = 0; + for (i = 0; i < 8; ++i) { + unsigned long long _trl = trl[i]; + + msr_trl |= (_trl << (i * 8)); + } + } + ret = isst_send_msr_command(id->cpu, 0x1AD, 1, &msr_trl); + if (ret) + return ret; + + return 0; +} + +/* Return 1 if locked */ +int isst_get_config_tdp_lock_status(struct isst_id *id) +{ + unsigned long long tdp_control = 0; + int ret; + + ret = isst_send_msr_command(id->cpu, 0x64b, 0, &tdp_control); + if (ret) + return ret; + + ret = !!(tdp_control & BIT(31)); + + return ret; +} + +void isst_get_process_ctdp_complete(struct isst_id *id, struct isst_pkg_ctdp *pkg_dev) +{ + int i; + + if (!pkg_dev->processed) + return; + + for (i = 0; i < pkg_dev->levels; ++i) { + struct isst_pkg_ctdp_level_info *ctdp_level; + + ctdp_level = &pkg_dev->ctdp_level[i]; + if (ctdp_level->pbf_support) + free_cpu_set(ctdp_level->pbf_info.core_cpumask); + free_cpu_set(ctdp_level->core_cpumask); + } +} + +void isst_adjust_uncore_freq(struct isst_id *id, int config_index, + struct isst_pkg_ctdp_level_info *ctdp_level) +{ + CHECK_CB(adjust_uncore_freq); + return isst_ops->adjust_uncore_freq(id, config_index, ctdp_level); +} + +int isst_get_process_ctdp(struct isst_id *id, int tdp_level, struct isst_pkg_ctdp *pkg_dev) +{ + int i, ret, valid = 0; + + if (pkg_dev->processed) + return 0; + + ret = isst_get_ctdp_levels(id, pkg_dev); + if (ret) + return ret; + + debug_printf("cpu: %d ctdp enable:%d current level: %d levels:%d\n", + id->cpu, pkg_dev->enabled, pkg_dev->current_level, + pkg_dev->levels); + + if (tdp_level != 0xff && tdp_level > pkg_dev->levels) { + isst_display_error_info_message(1, "Invalid level", 0, 0); + return -1; + } + + if (!pkg_dev->enabled) + isst_display_error_info_message(0, "perf-profile feature is not supported, just base-config level 0 is valid", 0, 0); + + for (i = 0; i <= pkg_dev->levels; ++i) { + struct isst_pkg_ctdp_level_info *ctdp_level; + + if (tdp_level != 0xff && i != tdp_level) + continue; + + debug_printf("cpu:%d Get Information for TDP level:%d\n", id->cpu, + i); + ctdp_level = &pkg_dev->ctdp_level[i]; + + ctdp_level->level = i; + ctdp_level->control_cpu = id->cpu; + ctdp_level->pkg_id = id->pkg; + ctdp_level->die_id = id->die; + + ret = isst_get_ctdp_control(id, i, ctdp_level); + if (ret) + continue; + + valid = 1; + pkg_dev->processed = 1; + ctdp_level->processed = 1; + + if (ctdp_level->pbf_support) { + ret = isst_get_pbf_info(id, i, &ctdp_level->pbf_info); + if (!ret) + ctdp_level->pbf_found = 1; + } + + if (ctdp_level->fact_support) { + ret = isst_get_fact_info(id, i, 0xff, + &ctdp_level->fact_info); + if (ret) + return ret; + } + + if (!pkg_dev->enabled && is_skx_based_platform()) { + int freq; + + freq = get_cpufreq_base_freq(id->cpu); + if (freq > 0) { + ctdp_level->sse_p1 = freq / 100000; + ctdp_level->tdp_ratio = ctdp_level->sse_p1; + } + + isst_get_get_trl_from_msr(id, ctdp_level->trl_ratios[0]); + isst_get_trl_bucket_info(id, i, &ctdp_level->trl_cores); + continue; + } + + ret = isst_get_tdp_info(id, i, ctdp_level); + if (ret) + return ret; + + ret = isst_get_pwr_info(id, i, ctdp_level); + if (ret) + return ret; + + ctdp_level->core_cpumask_size = + alloc_cpu_set(&ctdp_level->core_cpumask); + ret = isst_get_coremask_info(id, i, ctdp_level); + if (ret) + return ret; + + ret = isst_get_trl_bucket_info(id, i, &ctdp_level->trl_cores); + if (ret) + return ret; + + ret = isst_get_get_trls(id, i, ctdp_level); + if (ret) + return ret; + } + + if (!valid) + isst_display_error_info_message(0, "Invalid level, Can't get TDP control information at specified levels on cpu", 1, id->cpu); + + return 0; +} + +int isst_clos_get_clos_information(struct isst_id *id, int *enable, int *type) +{ + CHECK_CB(get_clos_information); + return isst_ops->get_clos_information(id, enable, type); +} + +int isst_pm_qos_config(struct isst_id *id, int enable_clos, int priority_type) +{ + CHECK_CB(pm_qos_config); + return isst_ops->pm_qos_config(id, enable_clos, priority_type); +} + +int isst_pm_get_clos(struct isst_id *id, int clos, struct isst_clos_config *clos_config) +{ + CHECK_CB(pm_get_clos); + return isst_ops->pm_get_clos(id, clos, clos_config); +} + +int isst_set_clos(struct isst_id *id, int clos, struct isst_clos_config *clos_config) +{ + CHECK_CB(set_clos); + return isst_ops->set_clos(id, clos, clos_config); +} + +int isst_clos_get_assoc_status(struct isst_id *id, int *clos_id) +{ + CHECK_CB(clos_get_assoc_status); + return isst_ops->clos_get_assoc_status(id, clos_id); +} + +int isst_clos_associate(struct isst_id *id, int clos_id) +{ + CHECK_CB(clos_associate); + return isst_ops->clos_associate(id, clos_id); + +} diff --git a/tools/power/x86/intel-speed-select/isst-daemon.c b/tools/power/x86/intel-speed-select/isst-daemon.c new file mode 100644 index 0000000000..12053fa435 --- /dev/null +++ b/tools/power/x86/intel-speed-select/isst-daemon.c @@ -0,0 +1,255 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Intel Speed Select -- Allow speed select to daemonize + * Copyright (c) 2022 Intel Corporation. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/file.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> +#include <getopt.h> +#include <signal.h> +#include <time.h> + +#include "isst.h" + +static int per_package_levels_info[MAX_PACKAGE_COUNT][MAX_DIE_PER_PACKAGE][MAX_PUNIT_PER_DIE]; +static time_t per_package_levels_tm[MAX_PACKAGE_COUNT][MAX_DIE_PER_PACKAGE][MAX_PUNIT_PER_DIE]; + +static void init_levels(void) +{ + int i, j, k; + + for (i = 0; i < MAX_PACKAGE_COUNT; ++i) + for (j = 0; j < MAX_DIE_PER_PACKAGE; ++j) + for (k = 0; k < MAX_PUNIT_PER_DIE; ++k) + per_package_levels_info[i][j][k] = -1; +} + +void process_level_change(struct isst_id *id) +{ + struct isst_pkg_ctdp_level_info ctdp_level; + struct isst_pkg_ctdp pkg_dev; + time_t tm; + int ret; + + if (id->pkg < 0 || id->die < 0 || id->punit < 0) { + debug_printf("Invalid package/die info for cpu:%d\n", id->cpu); + return; + } + + tm = time(NULL); + if (tm - per_package_levels_tm[id->pkg][id->die][id->punit] < 2) + return; + + per_package_levels_tm[id->pkg][id->die][id->punit] = tm; + + ret = isst_get_ctdp_levels(id, &pkg_dev); + if (ret) { + debug_printf("Can't get tdp levels for cpu:%d\n", id->cpu); + return; + } + + debug_printf("Get Config level %d pkg:%d die:%d current_level:%d\n", id->cpu, + id->pkg, id->die, pkg_dev.current_level); + + if (pkg_dev.locked) { + debug_printf("config TDP s locked \n"); + return; + } + + if (per_package_levels_info[id->pkg][id->die][id->punit] == pkg_dev.current_level) + return; + + debug_printf("**Config level change for cpu:%d pkg:%d die:%d from %d to %d\n", + id->cpu, id->pkg, id->die, per_package_levels_info[id->pkg][id->die][id->punit], + pkg_dev.current_level); + + per_package_levels_info[id->pkg][id->die][id->punit] = pkg_dev.current_level; + + ctdp_level.core_cpumask_size = + alloc_cpu_set(&ctdp_level.core_cpumask); + ret = isst_get_coremask_info(id, pkg_dev.current_level, &ctdp_level); + if (ret) { + free_cpu_set(ctdp_level.core_cpumask); + debug_printf("Can't get core_mask:%d\n", id->cpu); + return; + } + + if (use_cgroupv2()) { + int ret; + + ret = enable_cpuset_controller(); + if (ret) + goto use_offline; + + isolate_cpus(id, ctdp_level.core_cpumask_size, ctdp_level.core_cpumask, pkg_dev.current_level); + + goto free_mask; + } + +use_offline: + if (ctdp_level.cpu_count) { + int i, max_cpus = get_topo_max_cpus(); + for (i = 0; i < max_cpus; ++i) { + if (!is_cpu_in_power_domain(i, id)) + continue; + if (CPU_ISSET_S(i, ctdp_level.core_cpumask_size, ctdp_level.core_cpumask)) { + fprintf(stderr, "online cpu %d\n", i); + set_cpu_online_offline(i, 1); + } else { + fprintf(stderr, "offline cpu %d\n", i); + set_cpu_online_offline(i, 0); + } + } + } +free_mask: + free_cpu_set(ctdp_level.core_cpumask); +} + +static void _poll_for_config_change(struct isst_id *id, void *arg1, void *arg2, + void *arg3, void *arg4) +{ + process_level_change(id); +} + +static void poll_for_config_change(void) +{ + for_each_online_power_domain_in_set(_poll_for_config_change, NULL, NULL, + NULL, NULL); +} + +static int done = 0; +static int pid_file_handle; + +static void signal_handler(int sig) +{ + switch (sig) { + case SIGINT: + case SIGTERM: + done = 1; + hfi_exit(); + exit(0); + break; + default: + break; + } +} + +static void daemonize(char *rundir, char *pidfile) +{ + int pid, sid, i; + char str[10]; + struct sigaction sig_actions; + sigset_t sig_set; + int ret; + + if (getppid() == 1) + return; + + sigemptyset(&sig_set); + sigaddset(&sig_set, SIGCHLD); + sigaddset(&sig_set, SIGTSTP); + sigaddset(&sig_set, SIGTTOU); + sigaddset(&sig_set, SIGTTIN); + sigprocmask(SIG_BLOCK, &sig_set, NULL); + + sig_actions.sa_handler = signal_handler; + sigemptyset(&sig_actions.sa_mask); + sig_actions.sa_flags = 0; + + sigaction(SIGHUP, &sig_actions, NULL); + sigaction(SIGTERM, &sig_actions, NULL); + sigaction(SIGINT, &sig_actions, NULL); + + pid = fork(); + if (pid < 0) { + /* Could not fork */ + exit(EXIT_FAILURE); + } + if (pid > 0) + exit(EXIT_SUCCESS); + + umask(027); + + sid = setsid(); + if (sid < 0) + exit(EXIT_FAILURE); + + /* close all descriptors */ + for (i = getdtablesize(); i >= 0; --i) + close(i); + + i = open("/dev/null", O_RDWR); + if (i < 0) + exit(EXIT_FAILURE); + + ret = dup(i); + if (ret == -1) + exit(EXIT_FAILURE); + + ret = chdir(rundir); + if (ret == -1) + exit(EXIT_FAILURE); + + pid_file_handle = open(pidfile, O_RDWR | O_CREAT, 0600); + if (pid_file_handle == -1) { + /* Couldn't open lock file */ + exit(1); + } + /* Try to lock file */ +#ifdef LOCKF_SUPPORT + if (lockf(pid_file_handle, F_TLOCK, 0) == -1) { +#else + if (flock(pid_file_handle, LOCK_EX|LOCK_NB) < 0) { +#endif + /* Couldn't get lock on lock file */ + fprintf(stderr, "Couldn't get lock file %d\n", getpid()); + exit(1); + } + snprintf(str, sizeof(str), "%d\n", getpid()); + ret = write(pid_file_handle, str, strlen(str)); + if (ret == -1) + exit(EXIT_FAILURE); + + close(i); +} + +int isst_daemon(int debug_mode, int poll_interval, int no_daemon) +{ + int ret; + + if (!no_daemon && poll_interval < 0 && !debug_mode) { + fprintf(stderr, "OOB mode is enabled and will run as daemon\n"); + daemonize((char *) "/tmp/", + (char *)"/tmp/hfi-events.pid"); + } else { + signal(SIGINT, signal_handler); + } + + init_levels(); + + if (poll_interval < 0) { + ret = hfi_main(); + if (ret) { + fprintf(stderr, "HFI initialization failed\n"); + } + fprintf(stderr, "Must specify poll-interval\n"); + return ret; + } + + debug_printf("Starting loop\n"); + while (!done) { + sleep(poll_interval); + poll_for_config_change(); + } + + return 0; +} diff --git a/tools/power/x86/intel-speed-select/isst-display.c b/tools/power/x86/intel-speed-select/isst-display.c new file mode 100644 index 0000000000..14c9b03785 --- /dev/null +++ b/tools/power/x86/intel-speed-select/isst-display.c @@ -0,0 +1,765 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Intel dynamic_speed_select -- Enumerate and control features + * Copyright (c) 2019 Intel Corporation. + */ + +#include "isst.h" + +static void printcpulist(int str_len, char *str, int mask_size, + cpu_set_t *cpu_mask) +{ + int i, first, curr_index, index; + + if (!CPU_COUNT_S(mask_size, cpu_mask)) { + snprintf(str, str_len, "none"); + return; + } + + curr_index = 0; + first = 1; + for (i = 0; i < get_topo_max_cpus(); ++i) { + if (!CPU_ISSET_S(i, mask_size, cpu_mask)) + continue; + if (!first) { + index = snprintf(&str[curr_index], + str_len - curr_index, ","); + curr_index += index; + if (curr_index >= str_len) + break; + } + index = snprintf(&str[curr_index], str_len - curr_index, "%d", + i); + curr_index += index; + if (curr_index >= str_len) + break; + first = 0; + } +} + +static void printcpumask(int str_len, char *str, int mask_size, + cpu_set_t *cpu_mask) +{ + int i, max_cpus = get_topo_max_cpus(); + unsigned int *mask; + int size, index, curr_index; + + size = max_cpus / (sizeof(unsigned int) * 8); + if (max_cpus % (sizeof(unsigned int) * 8)) + size++; + + mask = calloc(size, sizeof(unsigned int)); + if (!mask) + return; + + for (i = 0; i < max_cpus; ++i) { + int mask_index, bit_index; + + if (!CPU_ISSET_S(i, mask_size, cpu_mask)) + continue; + + mask_index = i / (sizeof(unsigned int) * 8); + bit_index = i % (sizeof(unsigned int) * 8); + mask[mask_index] |= BIT(bit_index); + } + + curr_index = 0; + for (i = size - 1; i >= 0; --i) { + index = snprintf(&str[curr_index], str_len - curr_index, "%08x", + mask[i]); + curr_index += index; + if (curr_index >= str_len) + break; + if (i) { + strncat(&str[curr_index], ",", str_len - curr_index); + curr_index++; + } + if (curr_index >= str_len) + break; + } + + free(mask); +} + +static void format_and_print_txt(FILE *outf, int level, char *header, + char *value) +{ + char *spaces = " "; + static char delimiters[256]; + int i, j = 0; + + if (!level) + return; + + if (level == 1) { + strcpy(delimiters, " "); + } else { + for (i = 0; i < level - 1; ++i) + j += snprintf(&delimiters[j], sizeof(delimiters) - j, + "%s", spaces); + } + + if (header && value) { + fprintf(outf, "%s", delimiters); + fprintf(outf, "%s:%s\n", header, value); + } else if (header) { + fprintf(outf, "%s", delimiters); + fprintf(outf, "%s\n", header); + } +} + +static int last_level; +static void format_and_print(FILE *outf, int level, char *header, char *value) +{ + char *spaces = " "; + static char delimiters[256]; + int i; + + if (!out_format_is_json()) { + format_and_print_txt(outf, level, header, value); + return; + } + + if (level == 0) { + if (header) + fprintf(outf, "{"); + else + fprintf(outf, "\n}\n"); + + } else { + int j = 0; + + for (i = 0; i < level; ++i) + j += snprintf(&delimiters[j], sizeof(delimiters) - j, + "%s", spaces); + + if (last_level == level) + fprintf(outf, ",\n"); + + if (value) { + if (last_level != level) + fprintf(outf, "\n"); + + fprintf(outf, "%s\"%s\": ", delimiters, header); + fprintf(outf, "\"%s\"", value); + } else { + for (i = last_level - 1; i >= level; --i) { + int k = 0; + + for (j = i; j > 0; --j) + k += snprintf(&delimiters[k], + sizeof(delimiters) - k, + "%s", spaces); + if (i == level && header) + fprintf(outf, "\n%s},", delimiters); + else + fprintf(outf, "\n%s}", delimiters); + } + if (abs(last_level - level) < 3) + fprintf(outf, "\n"); + if (header) + fprintf(outf, "%s\"%s\": {", delimiters, + header); + } + } + + last_level = level; +} + +static int print_package_info(struct isst_id *id, FILE *outf) +{ + char header[256]; + int level = 1; + + if (out_format_is_json()) { + if (api_version() > 1) + snprintf(header, sizeof(header), "package-%d:die-%d:powerdomain-%d:cpu-%d", + id->pkg, id->die, id->punit, id->cpu); + else + snprintf(header, sizeof(header), "package-%d:die-%d:cpu-%d", + id->pkg, id->die, id->cpu); + format_and_print(outf, level, header, NULL); + return 1; + } + snprintf(header, sizeof(header), "package-%d", id->pkg); + format_and_print(outf, level++, header, NULL); + snprintf(header, sizeof(header), "die-%d", id->die); + format_and_print(outf, level++, header, NULL); + if (api_version() > 1) { + snprintf(header, sizeof(header), "powerdomain-%d", id->punit); + format_and_print(outf, level++, header, NULL); + } + snprintf(header, sizeof(header), "cpu-%d", id->cpu); + format_and_print(outf, level, header, NULL); + + return level; +} + +static void _isst_pbf_display_information(struct isst_id *id, FILE *outf, int level, + struct isst_pbf_info *pbf_info, + int disp_level) +{ + char header[256]; + char value[512]; + + snprintf(header, sizeof(header), "speed-select-base-freq-properties"); + format_and_print(outf, disp_level, header, NULL); + + snprintf(header, sizeof(header), "high-priority-base-frequency(MHz)"); + snprintf(value, sizeof(value), "%d", + pbf_info->p1_high * isst_get_disp_freq_multiplier()); + format_and_print(outf, disp_level + 1, header, value); + + snprintf(header, sizeof(header), "high-priority-cpu-mask"); + printcpumask(sizeof(value), value, pbf_info->core_cpumask_size, + pbf_info->core_cpumask); + format_and_print(outf, disp_level + 1, header, value); + + snprintf(header, sizeof(header), "high-priority-cpu-list"); + printcpulist(sizeof(value), value, + pbf_info->core_cpumask_size, + pbf_info->core_cpumask); + format_and_print(outf, disp_level + 1, header, value); + + snprintf(header, sizeof(header), "low-priority-base-frequency(MHz)"); + snprintf(value, sizeof(value), "%d", + pbf_info->p1_low * isst_get_disp_freq_multiplier()); + format_and_print(outf, disp_level + 1, header, value); + + if (is_clx_n_platform()) + return; + + snprintf(header, sizeof(header), "tjunction-temperature(C)"); + snprintf(value, sizeof(value), "%d", pbf_info->t_prochot); + format_and_print(outf, disp_level + 1, header, value); + + snprintf(header, sizeof(header), "thermal-design-power(W)"); + snprintf(value, sizeof(value), "%d", pbf_info->tdp); + format_and_print(outf, disp_level + 1, header, value); +} + +static void _isst_fact_display_information(struct isst_id *id, FILE *outf, int level, + int fact_bucket, int fact_avx, + struct isst_fact_info *fact_info, + int base_level) +{ + struct isst_fact_bucket_info *bucket_info = fact_info->bucket_info; + int trl_max_levels = isst_get_trl_max_levels(); + char header[256]; + char value[256]; + int print = 0, j; + + for (j = 0; j < ISST_FACT_MAX_BUCKETS; ++j) { + if (fact_bucket != 0xff && fact_bucket != j) + continue; + + /* core count must be valid for CPU power domain */ + if (!bucket_info[j].hp_cores && id->cpu >= 0) + break; + + print = 1; + } + if (!print) { + fprintf(stderr, "Invalid bucket\n"); + return; + } + + snprintf(header, sizeof(header), "speed-select-turbo-freq-properties"); + format_and_print(outf, base_level, header, NULL); + for (j = 0; j < ISST_FACT_MAX_BUCKETS; ++j) { + int i; + + if (fact_bucket != 0xff && fact_bucket != j) + continue; + + if (!bucket_info[j].hp_cores) + break; + + snprintf(header, sizeof(header), "bucket-%d", j); + format_and_print(outf, base_level + 1, header, NULL); + + snprintf(header, sizeof(header), "high-priority-cores-count"); + snprintf(value, sizeof(value), "%d", + bucket_info[j].hp_cores); + format_and_print(outf, base_level + 2, header, value); + for (i = 0; i < trl_max_levels; i++) { + if (!bucket_info[j].hp_ratios[i] || (fact_avx != 0xFF && !(fact_avx & (1 << i)))) + continue; + if (i == 0 && api_version() == 1 && !is_emr_platform()) + snprintf(header, sizeof(header), + "high-priority-max-frequency(MHz)"); + else + snprintf(header, sizeof(header), + "high-priority-max-%s-frequency(MHz)", isst_get_trl_level_name(i)); + snprintf(value, sizeof(value), "%d", + bucket_info[j].hp_ratios[i] * isst_get_disp_freq_multiplier()); + format_and_print(outf, base_level + 2, header, value); + } + } + snprintf(header, sizeof(header), + "speed-select-turbo-freq-clip-frequencies"); + format_and_print(outf, base_level + 1, header, NULL); + + for (j = 0; j < trl_max_levels; j++) { + if (!fact_info->lp_ratios[j]) + continue; + + /* No AVX level name for SSE to be consistent with previous formatting */ + if (j == 0 && api_version() == 1 && !is_emr_platform()) + snprintf(header, sizeof(header), "low-priority-max-frequency(MHz)"); + else + snprintf(header, sizeof(header), "low-priority-max-%s-frequency(MHz)", + isst_get_trl_level_name(j)); + snprintf(value, sizeof(value), "%d", + fact_info->lp_ratios[j] * isst_get_disp_freq_multiplier()); + format_and_print(outf, base_level + 2, header, value); + } +} + +void isst_ctdp_display_core_info(struct isst_id *id, FILE *outf, char *prefix, + unsigned int val, char *str0, char *str1) +{ + char value[256]; + int level = print_package_info(id, outf); + + level++; + + if (str0 && !val) + snprintf(value, sizeof(value), "%s", str0); + else if (str1 && val) + snprintf(value, sizeof(value), "%s", str1); + else + snprintf(value, sizeof(value), "%u", val); + format_and_print(outf, level, prefix, value); + + format_and_print(outf, 1, NULL, NULL); +} + +void isst_ctdp_display_information(struct isst_id *id, FILE *outf, int tdp_level, + struct isst_pkg_ctdp *pkg_dev) +{ + char header[256]; + char value[512]; + static int level; + int trl_max_levels = isst_get_trl_max_levels(); + int i; + + if (pkg_dev->processed) + level = print_package_info(id, outf); + + for (i = 0; i <= pkg_dev->levels; ++i) { + struct isst_pkg_ctdp_level_info *ctdp_level; + int j, k; + + ctdp_level = &pkg_dev->ctdp_level[i]; + if (!ctdp_level->processed) + continue; + + snprintf(header, sizeof(header), "perf-profile-level-%d", + ctdp_level->level); + format_and_print(outf, level + 1, header, NULL); + + if (id->cpu >= 0) { + snprintf(header, sizeof(header), "cpu-count"); + j = get_cpu_count(id); + snprintf(value, sizeof(value), "%d", j); + format_and_print(outf, level + 2, header, value); + + j = CPU_COUNT_S(ctdp_level->core_cpumask_size, + ctdp_level->core_cpumask); + if (j) { + snprintf(header, sizeof(header), "enable-cpu-count"); + snprintf(value, sizeof(value), "%d", j); + format_and_print(outf, level + 2, header, value); + } + + if (ctdp_level->core_cpumask_size) { + snprintf(header, sizeof(header), "enable-cpu-mask"); + printcpumask(sizeof(value), value, + ctdp_level->core_cpumask_size, + ctdp_level->core_cpumask); + format_and_print(outf, level + 2, header, value); + + snprintf(header, sizeof(header), "enable-cpu-list"); + printcpulist(sizeof(value), value, + ctdp_level->core_cpumask_size, + ctdp_level->core_cpumask); + format_and_print(outf, level + 2, header, value); + } + } + + snprintf(header, sizeof(header), "thermal-design-power-ratio"); + snprintf(value, sizeof(value), "%d", ctdp_level->tdp_ratio); + format_and_print(outf, level + 2, header, value); + + snprintf(header, sizeof(header), "base-frequency(MHz)"); + if (!ctdp_level->sse_p1) + ctdp_level->sse_p1 = ctdp_level->tdp_ratio; + snprintf(value, sizeof(value), "%d", + ctdp_level->sse_p1 * isst_get_disp_freq_multiplier()); + format_and_print(outf, level + 2, header, value); + + if (ctdp_level->avx2_p1) { + snprintf(header, sizeof(header), "base-frequency-avx2(MHz)"); + snprintf(value, sizeof(value), "%d", + ctdp_level->avx2_p1 * isst_get_disp_freq_multiplier()); + format_and_print(outf, level + 2, header, value); + } + + if (ctdp_level->avx512_p1) { + snprintf(header, sizeof(header), "base-frequency-avx512(MHz)"); + snprintf(value, sizeof(value), "%d", + ctdp_level->avx512_p1 * isst_get_disp_freq_multiplier()); + format_and_print(outf, level + 2, header, value); + } + + if (ctdp_level->uncore_pm) { + snprintf(header, sizeof(header), "uncore-frequency-min(MHz)"); + snprintf(value, sizeof(value), "%d", + ctdp_level->uncore_pm * isst_get_disp_freq_multiplier()); + format_and_print(outf, level + 2, header, value); + } + + if (ctdp_level->uncore_p0) { + snprintf(header, sizeof(header), "uncore-frequency-max(MHz)"); + snprintf(value, sizeof(value), "%d", + ctdp_level->uncore_p0 * isst_get_disp_freq_multiplier()); + format_and_print(outf, level + 2, header, value); + } + + if (ctdp_level->amx_p1) { + snprintf(header, sizeof(header), "base-frequency-amx(MHz)"); + snprintf(value, sizeof(value), "%d", + ctdp_level->amx_p1 * isst_get_disp_freq_multiplier()); + format_and_print(outf, level + 2, header, value); + } + + if (ctdp_level->uncore_p1) { + snprintf(header, sizeof(header), "uncore-frequency-base(MHz)"); + snprintf(value, sizeof(value), "%d", + ctdp_level->uncore_p1 * isst_get_disp_freq_multiplier()); + format_and_print(outf, level + 2, header, value); + } + + if (ctdp_level->mem_freq) { + snprintf(header, sizeof(header), "max-mem-frequency(MHz)"); + snprintf(value, sizeof(value), "%d", + ctdp_level->mem_freq); + format_and_print(outf, level + 2, header, value); + } + + if (api_version() > 1) { + snprintf(header, sizeof(header), "cooling_type"); + snprintf(value, sizeof(value), "%d", + ctdp_level->cooling_type); + format_and_print(outf, level + 2, header, value); + } + + snprintf(header, sizeof(header), + "speed-select-turbo-freq"); + if (ctdp_level->fact_support) { + if (ctdp_level->fact_enabled) + snprintf(value, sizeof(value), "enabled"); + else + snprintf(value, sizeof(value), "disabled"); + } else + snprintf(value, sizeof(value), "unsupported"); + format_and_print(outf, level + 2, header, value); + + snprintf(header, sizeof(header), + "speed-select-base-freq"); + if (ctdp_level->pbf_support) { + if (ctdp_level->pbf_enabled) + snprintf(value, sizeof(value), "enabled"); + else + snprintf(value, sizeof(value), "disabled"); + } else + snprintf(value, sizeof(value), "unsupported"); + format_and_print(outf, level + 2, header, value); + + snprintf(header, sizeof(header), + "speed-select-core-power"); + if (ctdp_level->sst_cp_support) { + if (ctdp_level->sst_cp_enabled) + snprintf(value, sizeof(value), "enabled"); + else + snprintf(value, sizeof(value), "disabled"); + } else + snprintf(value, sizeof(value), "unsupported"); + format_and_print(outf, level + 2, header, value); + + if (is_clx_n_platform()) { + if (ctdp_level->pbf_support) + _isst_pbf_display_information(id, outf, + tdp_level, + &ctdp_level->pbf_info, + level + 2); + continue; + } + + if (ctdp_level->pkg_tdp) { + snprintf(header, sizeof(header), "thermal-design-power(W)"); + snprintf(value, sizeof(value), "%d", ctdp_level->pkg_tdp); + format_and_print(outf, level + 2, header, value); + } + + if (ctdp_level->t_proc_hot) { + snprintf(header, sizeof(header), "tjunction-max(C)"); + snprintf(value, sizeof(value), "%d", ctdp_level->t_proc_hot); + format_and_print(outf, level + 2, header, value); + } + + for (k = 0; k < trl_max_levels; k++) { + if (!ctdp_level->trl_ratios[k][0]) + continue; + + snprintf(header, sizeof(header), "turbo-ratio-limits-%s", isst_get_trl_level_name(k)); + format_and_print(outf, level + 2, header, NULL); + + for (j = 0; j < 8; ++j) { + snprintf(header, sizeof(header), "bucket-%d", j); + format_and_print(outf, level + 3, header, NULL); + + snprintf(header, sizeof(header), "core-count"); + + snprintf(value, sizeof(value), "%llu", (ctdp_level->trl_cores >> (j * 8)) & 0xff); + format_and_print(outf, level + 4, header, value); + + snprintf(header, sizeof(header), "max-turbo-frequency(MHz)"); + snprintf(value, sizeof(value), "%d", ctdp_level->trl_ratios[k][j] * isst_get_disp_freq_multiplier()); + format_and_print(outf, level + 4, header, value); + } + } + + if (ctdp_level->pbf_support) + _isst_pbf_display_information(id, outf, i, + &ctdp_level->pbf_info, + level + 2); + if (ctdp_level->fact_support) + _isst_fact_display_information(id, outf, i, 0xff, 0xff, + &ctdp_level->fact_info, + level + 2); + } + + format_and_print(outf, 1, NULL, NULL); +} + +static int start; +void isst_ctdp_display_information_start(FILE *outf) +{ + last_level = 0; + format_and_print(outf, 0, "start", NULL); + start = 1; +} + +void isst_ctdp_display_information_end(FILE *outf) +{ + format_and_print(outf, 0, NULL, NULL); + start = 0; +} + +void isst_pbf_display_information(struct isst_id *id, FILE *outf, int level, + struct isst_pbf_info *pbf_info) +{ + int _level; + + _level = print_package_info(id, outf); + _isst_pbf_display_information(id, outf, level, pbf_info, _level + 1); + format_and_print(outf, 1, NULL, NULL); +} + +void isst_fact_display_information(struct isst_id *id, FILE *outf, int level, + int fact_bucket, int fact_avx, + struct isst_fact_info *fact_info) +{ + int _level; + + _level = print_package_info(id, outf); + _isst_fact_display_information(id, outf, level, fact_bucket, fact_avx, + fact_info, _level + 1); + format_and_print(outf, 1, NULL, NULL); +} + +void isst_clos_display_information(struct isst_id *id, FILE *outf, int clos, + struct isst_clos_config *clos_config) +{ + char header[256]; + char value[256]; + int level; + + level = print_package_info(id, outf); + + snprintf(header, sizeof(header), "core-power"); + format_and_print(outf, level + 1, header, NULL); + + snprintf(header, sizeof(header), "clos"); + snprintf(value, sizeof(value), "%d", clos); + format_and_print(outf, level + 2, header, value); + + snprintf(header, sizeof(header), "epp"); + snprintf(value, sizeof(value), "%d", clos_config->epp); + format_and_print(outf, level + 2, header, value); + + snprintf(header, sizeof(header), "clos-proportional-priority"); + snprintf(value, sizeof(value), "%d", clos_config->clos_prop_prio); + format_and_print(outf, level + 2, header, value); + + snprintf(header, sizeof(header), "clos-min"); + snprintf(value, sizeof(value), "%d MHz", clos_config->clos_min * isst_get_disp_freq_multiplier()); + format_and_print(outf, level + 2, header, value); + + snprintf(header, sizeof(header), "clos-max"); + if ((clos_config->clos_max * isst_get_disp_freq_multiplier()) == 25500) + snprintf(value, sizeof(value), "Max Turbo frequency"); + else + snprintf(value, sizeof(value), "%d MHz", clos_config->clos_max * isst_get_disp_freq_multiplier()); + format_and_print(outf, level + 2, header, value); + + snprintf(header, sizeof(header), "clos-desired"); + snprintf(value, sizeof(value), "%d MHz", clos_config->clos_desired * isst_get_disp_freq_multiplier()); + format_and_print(outf, level + 2, header, value); + + format_and_print(outf, level, NULL, NULL); +} + +void isst_clos_display_clos_information(struct isst_id *id, FILE *outf, + int clos_enable, int type, + int state, int cap) +{ + char header[256]; + char value[256]; + int level; + + level = print_package_info(id, outf); + + snprintf(header, sizeof(header), "core-power"); + format_and_print(outf, level + 1, header, NULL); + + snprintf(header, sizeof(header), "support-status"); + if (cap) + snprintf(value, sizeof(value), "supported"); + else + snprintf(value, sizeof(value), "unsupported"); + format_and_print(outf, level + 2, header, value); + + snprintf(header, sizeof(header), "enable-status"); + if (state) + snprintf(value, sizeof(value), "enabled"); + else + snprintf(value, sizeof(value), "disabled"); + format_and_print(outf, level + 2, header, value); + + snprintf(header, sizeof(header), "clos-enable-status"); + if (clos_enable) + snprintf(value, sizeof(value), "enabled"); + else + snprintf(value, sizeof(value), "disabled"); + format_and_print(outf, level + 2, header, value); + + snprintf(header, sizeof(header), "priority-type"); + if (type) + snprintf(value, sizeof(value), "ordered"); + else + snprintf(value, sizeof(value), "proportional"); + format_and_print(outf, level + 2, header, value); + + format_and_print(outf, level, NULL, NULL); +} + +void isst_clos_display_assoc_information(struct isst_id *id, FILE *outf, int clos) +{ + char header[256]; + char value[256]; + int level; + + level = print_package_info(id, outf); + + snprintf(header, sizeof(header), "get-assoc"); + format_and_print(outf, level + 1, header, NULL); + + snprintf(header, sizeof(header), "clos"); + snprintf(value, sizeof(value), "%d", clos); + format_and_print(outf, level + 2, header, value); + + format_and_print(outf, level, NULL, NULL); +} + +void isst_display_result(struct isst_id *id, FILE *outf, char *feature, char *cmd, + int result) +{ + char header[256]; + char value[256]; + int level = 3; + + level = print_package_info(id, outf); + + snprintf(header, sizeof(header), "%s", feature); + format_and_print(outf, level + 1, header, NULL); + snprintf(header, sizeof(header), "%s", cmd); + if (!result) + snprintf(value, sizeof(value), "success"); + else + snprintf(value, sizeof(value), "failed(error %d)", result); + format_and_print(outf, level + 2, header, value); + + format_and_print(outf, level, NULL, NULL); +} + +void isst_display_error_info_message(int error, char *msg, int arg_valid, int arg) +{ + FILE *outf = get_output_file(); + static int error_index; + char header[256]; + char value[256]; + + if (!out_format_is_json()) { + if (arg_valid) + snprintf(value, sizeof(value), "%s %d", msg, arg); + else + snprintf(value, sizeof(value), "%s", msg); + + if (error) + fprintf(outf, "Error: %s\n", value); + else + fprintf(outf, "Information: %s\n", value); + return; + } + + if (!start) + format_and_print(outf, 0, "start", NULL); + + if (error) + snprintf(header, sizeof(header), "Error%d", error_index++); + else + snprintf(header, sizeof(header), "Information:%d", error_index++); + format_and_print(outf, 1, header, NULL); + + snprintf(header, sizeof(header), "message"); + if (arg_valid) + snprintf(value, sizeof(value), "%s %d", msg, arg); + else + snprintf(value, sizeof(value), "%s", msg); + + format_and_print(outf, 2, header, value); + format_and_print(outf, 1, NULL, NULL); + if (!start) + format_and_print(outf, 0, NULL, NULL); +} + +void isst_trl_display_information(struct isst_id *id, FILE *outf, unsigned long long trl) +{ + char header[256]; + char value[256]; + int level; + + level = print_package_info(id, outf); + + snprintf(header, sizeof(header), "get-trl"); + format_and_print(outf, level + 1, header, NULL); + + snprintf(header, sizeof(header), "trl"); + snprintf(value, sizeof(value), "0x%llx", trl); + format_and_print(outf, level + 2, header, value); + + format_and_print(outf, level, NULL, NULL); +} diff --git a/tools/power/x86/intel-speed-select/isst.h b/tools/power/x86/intel-speed-select/isst.h new file mode 100644 index 0000000000..8def22dec4 --- /dev/null +++ b/tools/power/x86/intel-speed-select/isst.h @@ -0,0 +1,324 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Intel Speed Select -- Enumerate and control features + * Copyright (c) 2019 Intel Corporation. + */ + +#ifndef _ISST_H_ +#define _ISST_H_ + +#include <stdio.h> +#include <unistd.h> +#include <sys/types.h> +#include <sched.h> +#include <sys/stat.h> +#include <sys/resource.h> +#include <getopt.h> +#include <err.h> +#include <fcntl.h> +#include <signal.h> +#include <sys/time.h> +#include <limits.h> +#include <stdlib.h> +#include <string.h> +#include <cpuid.h> +#include <dirent.h> +#include <errno.h> + +#include <stdarg.h> +#include <sys/ioctl.h> + +#include <linux/isst_if.h> + +#define BIT(x) (1 << (x)) +#define BIT_ULL(nr) (1ULL << (nr)) +#define GENMASK(h, l) (((~0UL) << (l)) & (~0UL >> (sizeof(long) * 8 - 1 - (h)))) +#define GENMASK_ULL(h, l) \ + (((~0ULL) << (l)) & (~0ULL >> (sizeof(long long) * 8 - 1 - (h)))) + +#define CONFIG_TDP 0x7f +#define CONFIG_TDP_GET_LEVELS_INFO 0x00 +#define CONFIG_TDP_GET_TDP_CONTROL 0x01 +#define CONFIG_TDP_SET_TDP_CONTROL 0x02 +#define CONFIG_TDP_GET_TDP_INFO 0x03 +#define CONFIG_TDP_GET_PWR_INFO 0x04 +#define CONFIG_TDP_GET_TJMAX_INFO 0x05 +#define CONFIG_TDP_GET_CORE_MASK 0x06 +#define CONFIG_TDP_GET_TURBO_LIMIT_RATIOS 0x07 +#define CONFIG_TDP_SET_LEVEL 0x08 +#define CONFIG_TDP_GET_UNCORE_P0_P1_INFO 0X09 +#define CONFIG_TDP_GET_P1_INFO 0x0a +#define CONFIG_TDP_GET_MEM_FREQ 0x0b +#define CONFIG_TDP_GET_RATIO_INFO 0x0c + +#define CONFIG_TDP_GET_FACT_HP_TURBO_LIMIT_NUMCORES 0x10 +#define CONFIG_TDP_GET_FACT_HP_TURBO_LIMIT_RATIOS 0x11 +#define CONFIG_TDP_GET_FACT_LP_CLIPPING_RATIO 0x12 + +#define CONFIG_TDP_PBF_GET_CORE_MASK_INFO 0x20 +#define CONFIG_TDP_PBF_GET_P1HI_P1LO_INFO 0x21 +#define CONFIG_TDP_PBF_GET_TJ_MAX_INFO 0x22 +#define CONFIG_TDP_PBF_GET_TDP_INFO 0X23 + +#define CONFIG_CLOS 0xd0 +#define CLOS_PQR_ASSOC 0x00 +#define CLOS_PM_CLOS 0x01 +#define CLOS_PM_QOS_CONFIG 0x02 +#define CLOS_STATUS 0x03 + +#define MBOX_CMD_WRITE_BIT 0x08 + +#define PM_QOS_INFO_OFFSET 0x00 +#define PM_QOS_CONFIG_OFFSET 0x04 +#define PM_CLOS_OFFSET 0x08 +#define PQR_ASSOC_OFFSET 0x20 + +#define READ_PM_CONFIG 0x94 +#define WRITE_PM_CONFIG 0x95 +#define PM_FEATURE 0x03 + +#define DISP_FREQ_MULTIPLIER 100 + +#define MAX_PACKAGE_COUNT 32 +#define MAX_DIE_PER_PACKAGE 2 +#define MAX_PUNIT_PER_DIE 8 + +/* Unified structure to specific a CPU or a Power Domain */ +struct isst_id { + int cpu; + int pkg; + int die; + int punit; +}; + +struct isst_clos_config { + unsigned int clos_min; + unsigned int clos_max; + unsigned char epp; + unsigned char clos_prop_prio; + unsigned char clos_desired; +}; + +struct isst_fact_bucket_info { + int hp_cores; + int hp_ratios[TRL_MAX_LEVELS]; +}; + +struct isst_pbf_info { + int pbf_acticated; + int pbf_available; + size_t core_cpumask_size; + cpu_set_t *core_cpumask; + int p1_high; + int p1_low; + int t_control; + int t_prochot; + int tdp; +}; + +#define ISST_TRL_MAX_ACTIVE_CORES 8 +#define ISST_FACT_MAX_BUCKETS 8 +struct isst_fact_info { + int lp_ratios[TRL_MAX_LEVELS]; + struct isst_fact_bucket_info bucket_info[ISST_FACT_MAX_BUCKETS]; +}; + +struct isst_pkg_ctdp_level_info { + int processed; + int control_cpu; + int pkg_id; + int die_id; + int level; + int fact_support; + int pbf_support; + int fact_enabled; + int pbf_enabled; + int sst_cp_support; + int sst_cp_enabled; + int tdp_ratio; + int active; + int tdp_control; + int pkg_tdp; + int pkg_min_power; + int pkg_max_power; + int fact; + int t_proc_hot; + int cooling_type; + int uncore_p0; + int uncore_p1; + int uncore_pm; + int sse_p1; + int avx2_p1; + int avx512_p1; + int amx_p1; + int mem_freq; + size_t core_cpumask_size; + cpu_set_t *core_cpumask; + int cpu_count; + unsigned long long trl_cores; /* Buckets info */ + int trl_ratios[TRL_MAX_LEVELS][ISST_TRL_MAX_ACTIVE_CORES]; + int kobj_bucket_index; + int active_bucket; + int fact_max_index; + int fact_max_config; + int pbf_found; + int pbf_active; + struct isst_pbf_info pbf_info; + struct isst_fact_info fact_info; +}; + +#define ISST_MAX_TDP_LEVELS (4 + 1) /* +1 for base config */ +struct isst_pkg_ctdp { + int locked; + int version; + int processed; + int levels; + int current_level; + int enabled; + struct isst_pkg_ctdp_level_info ctdp_level[ISST_MAX_TDP_LEVELS]; +}; + +enum isst_platform_param { + ISST_PARAM_MBOX_DELAY, + ISST_PARAM_MBOX_RETRIES, +}; + +struct isst_platform_ops { + int (*get_disp_freq_multiplier)(void); + int (*get_trl_max_levels)(void); + char *(*get_trl_level_name)(int level); + void (*update_platform_param)(enum isst_platform_param param, int value); + int (*is_punit_valid)(struct isst_id *id); + int (*read_pm_config)(struct isst_id *id, int *cp_state, int *cp_cap); + int (*get_config_levels)(struct isst_id *id, struct isst_pkg_ctdp *pkg_ctdp); + int (*get_ctdp_control)(struct isst_id *id, int config_index, struct isst_pkg_ctdp_level_info *ctdp_level); + int (*get_tdp_info)(struct isst_id *id, int config_index, struct isst_pkg_ctdp_level_info *ctdp_level); + int (*get_pwr_info)(struct isst_id *id, int config_index, struct isst_pkg_ctdp_level_info *ctdp_level); + int (*get_coremask_info)(struct isst_id *id, int config_index, struct isst_pkg_ctdp_level_info *ctdp_level); + int (*get_get_trl)(struct isst_id *id, int level, int avx_level, int *trl); + int (*get_get_trls)(struct isst_id *id, int level, struct isst_pkg_ctdp_level_info *ctdp_level); + int (*get_trl_bucket_info)(struct isst_id *id, int level, unsigned long long *buckets_info); + int (*set_tdp_level)(struct isst_id *id, int tdp_level); + int (*get_pbf_info)(struct isst_id *id, int level, struct isst_pbf_info *pbf_info); + int (*set_pbf_fact_status)(struct isst_id *id, int pbf, int enable); + int (*get_fact_info)(struct isst_id *id, int level, int fact_bucket, struct isst_fact_info *fact_info); + void (*adjust_uncore_freq)(struct isst_id *id, int config_index, struct isst_pkg_ctdp_level_info *ctdp_level); + int (*get_clos_information)(struct isst_id *id, int *enable, int *type); + int (*pm_qos_config)(struct isst_id *id, int enable_clos, int priority_type); + int (*pm_get_clos)(struct isst_id *id, int clos, struct isst_clos_config *clos_config); + int (*set_clos)(struct isst_id *id, int clos, struct isst_clos_config *clos_config); + int (*clos_get_assoc_status)(struct isst_id *id, int *clos_id); + int (*clos_associate)(struct isst_id *id, int clos_id); +}; + +extern int is_cpu_in_power_domain(int cpu, struct isst_id *id); +extern int get_topo_max_cpus(void); +extern int get_cpu_count(struct isst_id *id); +extern int get_max_punit_core_id(struct isst_id *id); +extern int api_version(void); + +/* Common interfaces */ +FILE *get_output_file(void); +extern int is_debug_enabled(void); +extern void debug_printf(const char *format, ...); +extern int out_format_is_json(void); +extern void set_isst_id(struct isst_id *id, int cpu); +extern size_t alloc_cpu_set(cpu_set_t **cpu_set); +extern void free_cpu_set(cpu_set_t *cpu_set); +extern int find_phy_core_num(int logical_cpu); +extern void set_cpu_mask_from_punit_coremask(struct isst_id *id, + unsigned long long core_mask, + size_t core_cpumask_size, + cpu_set_t *core_cpumask, + int *cpu_cnt); +extern int isst_send_msr_command(unsigned int cpu, unsigned int command, + int write, unsigned long long *req_resp); + +extern int isst_set_platform_ops(int api_version); +extern void isst_update_platform_param(enum isst_platform_param, int vale); +extern int isst_get_disp_freq_multiplier(void); +extern int isst_get_trl_max_levels(void); +extern char *isst_get_trl_level_name(int level); +extern int isst_is_punit_valid(struct isst_id *id); + +extern int isst_get_ctdp_levels(struct isst_id *id, struct isst_pkg_ctdp *pkg_dev); +extern int isst_get_ctdp_control(struct isst_id *id, int config_index, + struct isst_pkg_ctdp_level_info *ctdp_level); +extern int isst_get_coremask_info(struct isst_id *id, int config_index, + struct isst_pkg_ctdp_level_info *ctdp_level); +extern void isst_adjust_uncore_freq(struct isst_id *id, int config_index, + struct isst_pkg_ctdp_level_info *ctdp_level); +extern int isst_get_process_ctdp(struct isst_id *id, int tdp_level, + struct isst_pkg_ctdp *pkg_dev); +extern void isst_get_process_ctdp_complete(struct isst_id *id, + struct isst_pkg_ctdp *pkg_dev); +extern void isst_ctdp_display_information(struct isst_id *id, FILE *outf, int tdp_level, + struct isst_pkg_ctdp *pkg_dev); +extern void isst_ctdp_display_core_info(struct isst_id *id, FILE *outf, char *prefix, + unsigned int val, char *str0, char *str1); +extern void isst_ctdp_display_information_start(FILE *outf); +extern void isst_ctdp_display_information_end(FILE *outf); +extern void isst_pbf_display_information(struct isst_id *id, FILE *outf, int level, + struct isst_pbf_info *info); +extern int isst_set_tdp_level(struct isst_id *id, int tdp_level); +extern int isst_set_pbf_fact_status(struct isst_id *id, int pbf, int enable); +extern int isst_get_pbf_info(struct isst_id *id, int level, + struct isst_pbf_info *pbf_info); +extern int isst_get_fact_info(struct isst_id *id, int level, int fact_bucket, + struct isst_fact_info *fact_info); +extern void isst_fact_display_information(struct isst_id *id, FILE *outf, int level, + int fact_bucket, int fact_avx, + struct isst_fact_info *fact_info); +extern int isst_set_trl(struct isst_id *id, unsigned long long trl); +extern int isst_get_trl(struct isst_id *id, unsigned long long *trl); +extern int isst_set_trl_from_current_tdp(struct isst_id *id, unsigned long long trl); +extern int isst_get_config_tdp_lock_status(struct isst_id *id); + +extern int isst_pm_qos_config(struct isst_id *id, int enable_clos, int priority_type); +extern int isst_pm_get_clos(struct isst_id *id, int clos, + struct isst_clos_config *clos_config); +extern int isst_set_clos(struct isst_id *id, int clos, + struct isst_clos_config *clos_config); +extern int isst_clos_associate(struct isst_id *id, int clos); +extern int isst_clos_get_assoc_status(struct isst_id *id, int *clos_id); +extern void isst_clos_display_information(struct isst_id *id, FILE *outf, int clos, + struct isst_clos_config *clos_config); +extern void isst_clos_display_assoc_information(struct isst_id *id, FILE *outf, int clos); + +extern void isst_display_result(struct isst_id *id, FILE *outf, char *feature, char *cmd, + int result); + +extern int isst_clos_get_clos_information(struct isst_id *id, int *enable, int *type); +extern void isst_clos_display_clos_information(struct isst_id *id, FILE *outf, + int clos_enable, int type, + int state, int cap); +extern int is_clx_n_platform(void); +extern int get_cpufreq_base_freq(int cpu); +extern int isst_read_pm_config(struct isst_id *id, int *cp_state, int *cp_cap); +extern void isst_display_error_info_message(int error, char *msg, int arg_valid, int arg); +extern int is_skx_based_platform(void); +extern int is_spr_platform(void); +extern int is_emr_platform(void); +extern int is_icx_platform(void); +extern void isst_trl_display_information(struct isst_id *id, FILE *outf, unsigned long long trl); + +extern void set_cpu_online_offline(int cpu, int state); +extern void for_each_online_power_domain_in_set(void (*callback)(struct isst_id *, void *, void *, + void *, void *), + void *arg1, void *arg2, void *arg3, + void *arg4); +extern int isst_daemon(int debug_mode, int poll_interval, int no_daemon); +extern void process_level_change(struct isst_id *id); +extern int hfi_main(void); +extern void hfi_exit(void); + +/* Interface specific callbacks */ +extern struct isst_platform_ops *mbox_get_platform_ops(void); +extern struct isst_platform_ops *tpmi_get_platform_ops(void); + +/* Cgroup related interface */ +extern int enable_cpuset_controller(void); +extern int isolate_cpus(struct isst_id *id, int mask_size, cpu_set_t *cpu_mask, int level); +extern int use_cgroupv2(void); + +#endif |