summaryrefslogtreecommitdiff
path: root/tools/power/x86/intel-speed-select/hfi-events.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2022-03-22 00:35:11 +0300
committerLinus Torvalds <torvalds@linux-foundation.org>2022-03-22 00:35:11 +0300
commitf648372dfe3e8e9dc8583c2b1790388be49bb47f (patch)
treeb14bd79b9574c79537887cf268dc3e397bb6eb41 /tools/power/x86/intel-speed-select/hfi-events.c
parent02b82b02c34321dde10d003aafcd831a769b2a8a (diff)
parent31035f3e20af4ede5f1c8162068327ea0b35a96e (diff)
downloadlinux-f648372dfe3e8e9dc8583c2b1790388be49bb47f.tar.xz
Merge tag 'thermal-5.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm
Pull thermal control updates from Rafael Wysocki: "As far as new functionality is concerned, there is a new thermal driver for the Intel Hardware Feedback Interface (HFI) along with some intel-speed-select utility changes to support it. There are also new DT compatible strings for a couple of platforms, and thermal zones on some platforms will be registered as HWmon sensors now. Apart from the above, some drivers are updated (fixes mostly) and there is a new piece of documentation for the Intel DPTF (Dynamic Power and Thermal Framework) sysfs interface. Specifics: - Add a new thermal driver for the Intel Hardware Feedback Interface (HFI) including the HFI initialization, HFI notification interrupt handling and sending CPU capabilities change messages to user space via the thermal netlink interface (Ricardo Neri, Srinivas Pandruvada, Nathan Chancellor, Randy Dunlap). - Extend the intel-speed-select utility to handle out-of-band CPU configuration changes and add support for the CPU capabilities change messages sent over the thermal netlink interface by the new HFI thermal driver to it (Srinivas Pandruvada). - Convert the DT bindings to yaml format for the Exynos platform and fix and update the MAINTAINERS file for this driver (Krzysztof Kozlowski). - Register the thermal zones as HWmon sensors for the QCom's Tsens driver and TI thermal platforms (Dmitry Baryshkov, Romain Naour). - Add the msm8953 compatible documentation in the bindings (Luca Weiss). - Add the sm8150 platform support to the QCom LMh driver's DT binding (Thara Gopinath). - Check the command result from the IPC command to the BPMP in the Tegra driver (Mikko Perttunen). - Silence the error for normal configuration where the interrupt is optionnal in the Broadcom thermal driver (Florian Fainelli). - Remove remaining dead code from the TI thermal driver (Yue Haibing). - Don't use bitmap_weight() in end_power_clamp() in the powerclamp driver (Yury Norov). - Update the OS policy capabilities handshake in the int340x thermal driver (Srinivas Pandruvada). - Increase the policies bitmap size in int340x (Srinivas Pandruvada). - Replace acpi_bus_get_device() with acpi_fetch_acpi_dev() in the int340x thermal driver (Rafael Wysocki). - Check for NULL after calling kmemdup() in int340x (Jiasheng Jiang). - Add Intel Dynamic Power and Thermal Framework (DPTF) kernel interface documentation (Srinivas Pandruvada). - Fix bullet list warning in the thermal documentation (Randy Dunlap)" * tag 'thermal-5.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: (30 commits) thermal: int340x: Update OS policy capability handshake thermal: int340x: Increase bitmap size Documentation: thermal: DPTF Documentation MAINTAINERS: thermal: samsung: update Krzysztof Kozlowski's email thermal/drivers/ti-soc-thermal: Remove unused function ti_thermal_get_temp() thermal/drivers/brcmstb_thermal: Interrupt is optional thermal: tegra-bpmp: Handle errors in BPMP response drivers/thermal/ti-soc-thermal: Add hwmon support dt-bindings: thermal: tsens: Add msm8953 compatible dt-bindings: thermal: Add sm8150 compatible string for LMh thermal/drivers/qcom/lmh: Add support for sm8150 thermal/drivers/tsens: register thermal zones as hwmon sensors MAINTAINERS: thermal: samsung: Drop obsolete properties dt-bindings: thermal: samsung: Convert to dtschema tools/power/x86/intel-speed-select: v1.12 release tools/power/x86/intel-speed-select: HFI support tools/power/x86/intel-speed-select: OOB daemon mode thermal: intel: hfi: INTEL_HFI_THERMAL depends on NET thermal: netlink: Fix parameter type of thermal_genl_cpu_capability_event() stub thermal: Replace acpi_bus_get_device() ...
Diffstat (limited to 'tools/power/x86/intel-speed-select/hfi-events.c')
-rw-r--r--tools/power/x86/intel-speed-select/hfi-events.c309
1 files changed, 309 insertions, 0 deletions
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 000000000000..e85676711372
--- /dev/null
+++ b/tools/power/x86/intel-speed-select/hfi-events.c
@@ -0,0 +1,309 @@
+// 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)
+{
+ process_level_change(perf_cap->cpu);
+}
+
+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;
+
+ 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;
+ int no_block = 0;
+
+ 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);
+
+ if (no_block)
+ nl_socket_set_nonblocking(sock);
+
+ 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;
+}