// SPDX-License-Identifier: GPL-2.0 /* * Energy Model of devices * * Copyright (c) 2018-2021, Arm ltd. * Written by: Quentin Perret, Arm ltd. * Improvements provided by: Lukasz Luba, Arm ltd. */ #define pr_fmt(fmt) … #include <linux/cpu.h> #include <linux/cpufreq.h> #include <linux/cpumask.h> #include <linux/debugfs.h> #include <linux/energy_model.h> #include <linux/sched/topology.h> #include <linux/slab.h> /* * Mutex serializing the registrations of performance domains and letting * callbacks defined by drivers sleep. */ static DEFINE_MUTEX(em_pd_mutex); static void em_cpufreq_update_efficiencies(struct device *dev, struct em_perf_state *table); static void em_check_capacity_update(void); static void em_update_workfn(struct work_struct *work); static DECLARE_DELAYED_WORK(em_update_work, em_update_workfn); static bool _is_cpu_device(struct device *dev) { … } #ifdef CONFIG_DEBUG_FS static struct dentry *rootdir; struct em_dbg_info { … }; #define DEFINE_EM_DBG_SHOW(name, fname) … DEFINE_EM_DBG_SHOW(…) …; DEFINE_EM_DBG_SHOW(…) …; DEFINE_EM_DBG_SHOW(…) …; DEFINE_EM_DBG_SHOW(…) …; DEFINE_EM_DBG_SHOW(…) …; static void em_debug_create_ps(struct em_perf_domain *em_pd, struct em_dbg_info *em_dbg, int i, struct dentry *pd) { … } static int em_debug_cpus_show(struct seq_file *s, void *unused) { … } DEFINE_SHOW_ATTRIBUTE(…); static int em_debug_flags_show(struct seq_file *s, void *unused) { … } DEFINE_SHOW_ATTRIBUTE(…); static void em_debug_create_pd(struct device *dev) { … } static void em_debug_remove_pd(struct device *dev) { … } static int __init em_debug_init(void) { … } fs_initcall(em_debug_init); #else /* CONFIG_DEBUG_FS */ static void em_debug_create_pd(struct device *dev) {} static void em_debug_remove_pd(struct device *dev) {} #endif static void em_destroy_table_rcu(struct rcu_head *rp) { … } static void em_release_table_kref(struct kref *kref) { … } /** * em_table_free() - Handles safe free of the EM table when needed * @table : EM table which is going to be freed * * No return values. */ void em_table_free(struct em_perf_table __rcu *table) { … } /** * em_table_alloc() - Allocate a new EM table * @pd : EM performance domain for which this must be done * * Allocate a new EM table and initialize its kref to indicate that it * has a user. * Returns allocated table or NULL. */ struct em_perf_table __rcu *em_table_alloc(struct em_perf_domain *pd) { … } static void em_init_performance(struct device *dev, struct em_perf_domain *pd, struct em_perf_state *table, int nr_states) { … } static int em_compute_costs(struct device *dev, struct em_perf_state *table, struct em_data_callback *cb, int nr_states, unsigned long flags) { … } /** * em_dev_compute_costs() - Calculate cost values for new runtime EM table * @dev : Device for which the EM table is to be updated * @table : The new EM table that is going to get the costs calculated * @nr_states : Number of performance states * * Calculate the em_perf_state::cost values for new runtime EM table. The * values are used for EAS during task placement. It also calculates and sets * the efficiency flag for each performance state. When the function finish * successfully the EM table is ready to be updated and used by EAS. * * Return 0 on success or a proper error in case of failure. */ int em_dev_compute_costs(struct device *dev, struct em_perf_state *table, int nr_states) { … } /** * em_dev_update_perf_domain() - Update runtime EM table for a device * @dev : Device for which the EM is to be updated * @new_table : The new EM table that is going to be used from now * * Update EM runtime modifiable table for the @dev using the provided @table. * * This function uses a mutex to serialize writers, so it must not be called * from a non-sleeping context. * * Return 0 on success or an error code on failure. */ int em_dev_update_perf_domain(struct device *dev, struct em_perf_table __rcu *new_table) { … } EXPORT_SYMBOL_GPL(…); static int em_create_perf_table(struct device *dev, struct em_perf_domain *pd, struct em_perf_state *table, struct em_data_callback *cb, unsigned long flags) { … } static int em_create_pd(struct device *dev, int nr_states, struct em_data_callback *cb, cpumask_t *cpus, unsigned long flags) { … } static void em_cpufreq_update_efficiencies(struct device *dev, struct em_perf_state *table) { … } /** * em_pd_get() - Return the performance domain for a device * @dev : Device to find the performance domain for * * Returns the performance domain to which @dev belongs, or NULL if it doesn't * exist. */ struct em_perf_domain *em_pd_get(struct device *dev) { … } EXPORT_SYMBOL_GPL(…); /** * em_cpu_get() - Return the performance domain for a CPU * @cpu : CPU to find the performance domain for * * Returns the performance domain to which @cpu belongs, or NULL if it doesn't * exist. */ struct em_perf_domain *em_cpu_get(int cpu) { … } EXPORT_SYMBOL_GPL(…); /** * em_dev_register_perf_domain() - Register the Energy Model (EM) for a device * @dev : Device for which the EM is to register * @nr_states : Number of performance states to register * @cb : Callback functions providing the data of the Energy Model * @cpus : Pointer to cpumask_t, which in case of a CPU device is * obligatory. It can be taken from i.e. 'policy->cpus'. For other * type of devices this should be set to NULL. * @microwatts : Flag indicating that the power values are in micro-Watts or * in some other scale. It must be set properly. * * Create Energy Model tables for a performance domain using the callbacks * defined in cb. * * The @microwatts is important to set with correct value. Some kernel * sub-systems might rely on this flag and check if all devices in the EM are * using the same scale. * * If multiple clients register the same performance domain, all but the first * registration will be ignored. * * Return 0 on success */ int em_dev_register_perf_domain(struct device *dev, unsigned int nr_states, struct em_data_callback *cb, cpumask_t *cpus, bool microwatts) { … } EXPORT_SYMBOL_GPL(…); /** * em_dev_unregister_perf_domain() - Unregister Energy Model (EM) for a device * @dev : Device for which the EM is registered * * Unregister the EM for the specified @dev (but not a CPU device). */ void em_dev_unregister_perf_domain(struct device *dev) { … } EXPORT_SYMBOL_GPL(…); static struct em_perf_table __rcu *em_table_dup(struct em_perf_domain *pd) { … } static int em_recalc_and_update(struct device *dev, struct em_perf_domain *pd, struct em_perf_table __rcu *em_table) { … } /* * Adjustment of CPU performance values after boot, when all CPUs capacites * are correctly calculated. */ static void em_adjust_new_capacity(struct device *dev, struct em_perf_domain *pd, u64 max_cap) { … } static void em_check_capacity_update(void) { … } static void em_update_workfn(struct work_struct *work) { … } /** * em_dev_update_chip_binning() - Update Energy Model after the new voltage * information is present in the OPPs. * @dev : Device for which the Energy Model has to be updated. * * This function allows to update easily the EM with new values available in * the OPP framework and DT. It can be used after the chip has been properly * verified by device drivers and the voltages adjusted for the 'chip binning'. */ int em_dev_update_chip_binning(struct device *dev) { … } EXPORT_SYMBOL_GPL(…);