// SPDX-License-Identifier: GPL-2.0-only /* * Hardware Feedback Interface Driver * * Copyright (c) 2021, Intel Corporation. * * Authors: Aubrey Li <[email protected]> * Ricardo Neri <[email protected]> * * * The Hardware Feedback Interface provides a performance and energy efficiency * capability information for each CPU in the system. Depending on the processor * model, hardware may periodically update these capabilities as a result of * changes in the operating conditions (e.g., power limits or thermal * constraints). On other processor models, there is a single HFI update * at boot. * * This file provides functionality to process HFI updates and relay these * updates to userspace. */ #define pr_fmt(fmt) … #include <linux/bitops.h> #include <linux/cpufeature.h> #include <linux/cpumask.h> #include <linux/delay.h> #include <linux/gfp.h> #include <linux/io.h> #include <linux/kernel.h> #include <linux/math.h> #include <linux/mutex.h> #include <linux/percpu-defs.h> #include <linux/printk.h> #include <linux/processor.h> #include <linux/slab.h> #include <linux/spinlock.h> #include <linux/suspend.h> #include <linux/string.h> #include <linux/syscore_ops.h> #include <linux/topology.h> #include <linux/workqueue.h> #include <asm/msr.h> #include "intel_hfi.h" #include "thermal_interrupt.h" #include "../thermal_netlink.h" /* Hardware Feedback Interface MSR configuration bits */ #define HW_FEEDBACK_PTR_VALID_BIT … #define HW_FEEDBACK_CONFIG_HFI_ENABLE_BIT … /* CPUID detection and enumeration definitions for HFI */ #define CPUID_HFI_LEAF … hfi_capabilities; cpuid6_edx; /** * struct hfi_cpu_data - HFI capabilities per CPU * @perf_cap: Performance capability * @ee_cap: Energy efficiency capability * * Capabilities of a logical processor in the HFI table. These capabilities are * unitless. */ struct hfi_cpu_data { … } __packed; /** * struct hfi_hdr - Header of the HFI table * @perf_updated: Hardware updated performance capabilities * @ee_updated: Hardware updated energy efficiency capabilities * * Properties of the data in an HFI table. */ struct hfi_hdr { … } __packed; /** * struct hfi_instance - Representation of an HFI instance (i.e., a table) * @local_table: Base of the local copy of the HFI table * @timestamp: Timestamp of the last update of the local table. * Located at the base of the local table. * @hdr: Base address of the header of the local table * @data: Base address of the data of the local table * @cpus: CPUs represented in this HFI table instance * @hw_table: Pointer to the HFI table of this instance * @update_work: Delayed work to process HFI updates * @table_lock: Lock to protect acceses to the table of this instance * @event_lock: Lock to process HFI interrupts * * A set of parameters to parse and navigate a specific HFI table. */ struct hfi_instance { … }; /** * struct hfi_features - Supported HFI features * @nr_table_pages: Size of the HFI table in 4KB pages * @cpu_stride: Stride size to locate the capability data of a logical * processor within the table (i.e., row stride) * @hdr_size: Size of the table header * * Parameters and supported features that are common to all HFI instances */ struct hfi_features { … }; /** * struct hfi_cpu_info - Per-CPU attributes to consume HFI data * @index: Row of this CPU in its HFI table * @hfi_instance: Attributes of the HFI table to which this CPU belongs * * Parameters to link a logical processor to an HFI table and a row within it. */ struct hfi_cpu_info { … }; static DEFINE_PER_CPU(struct hfi_cpu_info, hfi_cpu_info) = …; static int max_hfi_instances; static int hfi_clients_nr; static struct hfi_instance *hfi_instances; static struct hfi_features hfi_features; static DEFINE_MUTEX(hfi_instance_lock); static struct workqueue_struct *hfi_updates_wq; #define HFI_UPDATE_DELAY_MS … #define HFI_THERMNL_CAPS_PER_EVENT … static void get_hfi_caps(struct hfi_instance *hfi_instance, struct thermal_genl_cpu_caps *cpu_caps) { … } /* * Call update_capabilities() when there are changes in the HFI table. */ static void update_capabilities(struct hfi_instance *hfi_instance) { … } static void hfi_update_work_fn(struct work_struct *work) { … } void intel_hfi_process_event(__u64 pkg_therm_status_msr_val) { … } static void init_hfi_cpu_index(struct hfi_cpu_info *info) { … } /* * The format of the HFI table depends on the number of capabilities that the * hardware supports. Keep a data structure to navigate the table. */ static void init_hfi_instance(struct hfi_instance *hfi_instance) { … } /* Caller must hold hfi_instance_lock. */ static void hfi_enable(void) { … } static void hfi_set_hw_table(struct hfi_instance *hfi_instance) { … } /* Caller must hold hfi_instance_lock. */ static void hfi_disable(void) { … } /** * intel_hfi_online() - Enable HFI on @cpu * @cpu: CPU in which the HFI will be enabled * * Enable the HFI to be used in @cpu. The HFI is enabled at the package * level. The first CPU in the package to come online does the full HFI * initialization. Subsequent CPUs will just link themselves to the HFI * instance of their package. * * This function is called before enabling the thermal vector in the local APIC * in order to ensure that @cpu has an associated HFI instance when it receives * an HFI event. */ void intel_hfi_online(unsigned int cpu) { … } /** * intel_hfi_offline() - Disable HFI on @cpu * @cpu: CPU in which the HFI will be disabled * * Remove @cpu from those covered by its HFI instance. * * On some processors, hardware remembers previous programming settings even * after being reprogrammed. Thus, keep HFI enabled even if all CPUs in the * package of @cpu are offline. See note in intel_hfi_online(). */ void intel_hfi_offline(unsigned int cpu) { … } static __init int hfi_parse_features(void) { … } /* * If concurrency is not prevented by other means, the HFI enable/disable * routines must be called under hfi_instance_lock." */ static void hfi_enable_instance(void *ptr) { … } static void hfi_disable_instance(void *ptr) { … } static void hfi_syscore_resume(void) { … } static int hfi_syscore_suspend(void) { … } static struct syscore_ops hfi_pm_ops = …; static int hfi_thermal_notify(struct notifier_block *nb, unsigned long state, void *_notify) { … } static struct notifier_block hfi_thermal_nb = …; void __init intel_hfi_init(void) { … }