// SPDX-License-Identifier: GPL-2.0-only /* * devfreq: Generic Dynamic Voltage and Frequency Scaling (DVFS) Framework * for Non-CPU Devices. * * Copyright (C) 2011 Samsung Electronics * MyungJoo Ham <[email protected]> */ #include <linux/kernel.h> #include <linux/kmod.h> #include <linux/sched.h> #include <linux/debugfs.h> #include <linux/devfreq_cooling.h> #include <linux/errno.h> #include <linux/err.h> #include <linux/init.h> #include <linux/export.h> #include <linux/slab.h> #include <linux/stat.h> #include <linux/pm_opp.h> #include <linux/devfreq.h> #include <linux/workqueue.h> #include <linux/platform_device.h> #include <linux/list.h> #include <linux/printk.h> #include <linux/hrtimer.h> #include <linux/of.h> #include <linux/pm_qos.h> #include <linux/units.h> #include "governor.h" #define CREATE_TRACE_POINTS #include <trace/events/devfreq.h> #define IS_SUPPORTED_FLAG(f, name) … #define IS_SUPPORTED_ATTR(f, name) … static struct class *devfreq_class; static struct dentry *devfreq_debugfs; /* * devfreq core provides delayed work based load monitoring helper * functions. Governors can use these or can implement their own * monitoring mechanism. */ static struct workqueue_struct *devfreq_wq; /* The list of all device-devfreq governors */ static LIST_HEAD(devfreq_governor_list); /* The list of all device-devfreq */ static LIST_HEAD(devfreq_list); static DEFINE_MUTEX(devfreq_list_lock); static const char timer_name[][DEVFREQ_NAME_LEN] = …; /** * find_device_devfreq() - find devfreq struct using device pointer * @dev: device pointer used to lookup device devfreq. * * Search the list of device devfreqs and return the matched device's * devfreq info. devfreq_list_lock should be held by the caller. */ static struct devfreq *find_device_devfreq(struct device *dev) { … } static unsigned long find_available_min_freq(struct devfreq *devfreq) { … } static unsigned long find_available_max_freq(struct devfreq *devfreq) { … } /** * devfreq_get_freq_range() - Get the current freq range * @devfreq: the devfreq instance * @min_freq: the min frequency * @max_freq: the max frequency * * This takes into consideration all constraints. */ void devfreq_get_freq_range(struct devfreq *devfreq, unsigned long *min_freq, unsigned long *max_freq) { … } EXPORT_SYMBOL(…); /** * devfreq_get_freq_level() - Lookup freq_table for the frequency * @devfreq: the devfreq instance * @freq: the target frequency */ static int devfreq_get_freq_level(struct devfreq *devfreq, unsigned long freq) { … } static int set_freq_table(struct devfreq *devfreq) { … } /** * devfreq_update_status() - Update statistics of devfreq behavior * @devfreq: the devfreq instance * @freq: the update target frequency */ int devfreq_update_status(struct devfreq *devfreq, unsigned long freq) { … } EXPORT_SYMBOL(…); /** * find_devfreq_governor() - find devfreq governor from name * @name: name of the governor * * Search the list of devfreq governors and return the matched * governor's pointer. devfreq_list_lock should be held by the caller. */ static struct devfreq_governor *find_devfreq_governor(const char *name) { … } /** * try_then_request_governor() - Try to find the governor and request the * module if is not found. * @name: name of the governor * * Search the list of devfreq governors and request the module and try again * if is not found. This can happen when both drivers (the governor driver * and the driver that call devfreq_add_device) are built as modules. * devfreq_list_lock should be held by the caller. Returns the matched * governor's pointer or an error pointer. */ static struct devfreq_governor *try_then_request_governor(const char *name) { … } static int devfreq_notify_transition(struct devfreq *devfreq, struct devfreq_freqs *freqs, unsigned int state) { … } static int devfreq_set_target(struct devfreq *devfreq, unsigned long new_freq, u32 flags) { … } /** * devfreq_update_target() - Reevaluate the device and configure frequency * on the final stage. * @devfreq: the devfreq instance. * @freq: the new frequency of parent device. This argument * is only used for devfreq device using passive governor. * * Note: Lock devfreq->lock before calling devfreq_update_target. This function * should be only used by both update_devfreq() and devfreq governors. */ int devfreq_update_target(struct devfreq *devfreq, unsigned long freq) { … } EXPORT_SYMBOL(…); /* Load monitoring helper functions for governors use */ /** * update_devfreq() - Reevaluate the device and configure frequency. * @devfreq: the devfreq instance. * * Note: Lock devfreq->lock before calling update_devfreq * This function is exported for governors. */ int update_devfreq(struct devfreq *devfreq) { … } EXPORT_SYMBOL(…); /** * devfreq_monitor() - Periodically poll devfreq objects. * @work: the work struct used to run devfreq_monitor periodically. * */ static void devfreq_monitor(struct work_struct *work) { … } /** * devfreq_monitor_start() - Start load monitoring of devfreq instance * @devfreq: the devfreq instance. * * Helper function for starting devfreq device load monitoring. By default, * deferrable timer is used for load monitoring. But the users can change this * behavior using the "timer" type in devfreq_dev_profile. This function will be * called by devfreq governor in response to the DEVFREQ_GOV_START event * generated while adding a device to the devfreq framework. */ void devfreq_monitor_start(struct devfreq *devfreq) { … } EXPORT_SYMBOL(…); /** * devfreq_monitor_stop() - Stop load monitoring of a devfreq instance * @devfreq: the devfreq instance. * * Helper function to stop devfreq device load monitoring. Function * to be called from governor in response to DEVFREQ_GOV_STOP * event when device is removed from devfreq framework. */ void devfreq_monitor_stop(struct devfreq *devfreq) { … } EXPORT_SYMBOL(…); /** * devfreq_monitor_suspend() - Suspend load monitoring of a devfreq instance * @devfreq: the devfreq instance. * * Helper function to suspend devfreq device load monitoring. Function * to be called from governor in response to DEVFREQ_GOV_SUSPEND * event or when polling interval is set to zero. * * Note: Though this function is same as devfreq_monitor_stop(), * intentionally kept separate to provide hooks for collecting * transition statistics. */ void devfreq_monitor_suspend(struct devfreq *devfreq) { … } EXPORT_SYMBOL(…); /** * devfreq_monitor_resume() - Resume load monitoring of a devfreq instance * @devfreq: the devfreq instance. * * Helper function to resume devfreq device load monitoring. Function * to be called from governor in response to DEVFREQ_GOV_RESUME * event or when polling interval is set to non-zero. */ void devfreq_monitor_resume(struct devfreq *devfreq) { … } EXPORT_SYMBOL(…); /** * devfreq_update_interval() - Update device devfreq monitoring interval * @devfreq: the devfreq instance. * @delay: new polling interval to be set. * * Helper function to set new load monitoring polling interval. Function * to be called from governor in response to DEVFREQ_GOV_UPDATE_INTERVAL event. */ void devfreq_update_interval(struct devfreq *devfreq, unsigned int *delay) { … } EXPORT_SYMBOL(…); /** * devfreq_notifier_call() - Notify that the device frequency requirements * has been changed out of devfreq framework. * @nb: the notifier_block (supposed to be devfreq->nb) * @type: not used * @devp: not used * * Called by a notifier that uses devfreq->nb. */ static int devfreq_notifier_call(struct notifier_block *nb, unsigned long type, void *devp) { … } /** * qos_notifier_call() - Common handler for QoS constraints. * @devfreq: the devfreq instance. */ static int qos_notifier_call(struct devfreq *devfreq) { … } /** * qos_min_notifier_call() - Callback for QoS min_freq changes. * @nb: Should be devfreq->nb_min * @val: not used * @ptr: not used */ static int qos_min_notifier_call(struct notifier_block *nb, unsigned long val, void *ptr) { … } /** * qos_max_notifier_call() - Callback for QoS max_freq changes. * @nb: Should be devfreq->nb_max * @val: not used * @ptr: not used */ static int qos_max_notifier_call(struct notifier_block *nb, unsigned long val, void *ptr) { … } /** * devfreq_dev_release() - Callback for struct device to release the device. * @dev: the devfreq device * * Remove devfreq from the list and release its resources. */ static void devfreq_dev_release(struct device *dev) { … } static void create_sysfs_files(struct devfreq *devfreq, const struct devfreq_governor *gov); static void remove_sysfs_files(struct devfreq *devfreq, const struct devfreq_governor *gov); /** * devfreq_add_device() - Add devfreq feature to the device * @dev: the device to add devfreq feature. * @profile: device-specific profile to run devfreq. * @governor_name: name of the policy to choose frequency. * @data: devfreq driver pass to governors, governor should not change it. */ struct devfreq *devfreq_add_device(struct device *dev, struct devfreq_dev_profile *profile, const char *governor_name, void *data) { … } EXPORT_SYMBOL(…); /** * devfreq_remove_device() - Remove devfreq feature from a device. * @devfreq: the devfreq instance to be removed * * The opposite of devfreq_add_device(). */ int devfreq_remove_device(struct devfreq *devfreq) { … } EXPORT_SYMBOL(…); static int devm_devfreq_dev_match(struct device *dev, void *res, void *data) { … } static void devm_devfreq_dev_release(struct device *dev, void *res) { … } /** * devm_devfreq_add_device() - Resource-managed devfreq_add_device() * @dev: the device to add devfreq feature. * @profile: device-specific profile to run devfreq. * @governor_name: name of the policy to choose frequency. * @data: devfreq driver pass to governors, governor should not change it. * * This function manages automatically the memory of devfreq device using device * resource management and simplify the free operation for memory of devfreq * device. */ struct devfreq *devm_devfreq_add_device(struct device *dev, struct devfreq_dev_profile *profile, const char *governor_name, void *data) { … } EXPORT_SYMBOL(…); #ifdef CONFIG_OF /* * devfreq_get_devfreq_by_node - Get the devfreq device from devicetree * @node - pointer to device_node * * return the instance of devfreq device */ struct devfreq *devfreq_get_devfreq_by_node(struct device_node *node) { … } /* * devfreq_get_devfreq_by_phandle - Get the devfreq device from devicetree * @dev - instance to the given device * @phandle_name - name of property holding a phandle value * @index - index into list of devfreq * * return the instance of devfreq device */ struct devfreq *devfreq_get_devfreq_by_phandle(struct device *dev, const char *phandle_name, int index) { … } #else struct devfreq *devfreq_get_devfreq_by_node(struct device_node *node) { return ERR_PTR(-ENODEV); } struct devfreq *devfreq_get_devfreq_by_phandle(struct device *dev, const char *phandle_name, int index) { return ERR_PTR(-ENODEV); } #endif /* CONFIG_OF */ EXPORT_SYMBOL_GPL(…); EXPORT_SYMBOL_GPL(…); /** * devm_devfreq_remove_device() - Resource-managed devfreq_remove_device() * @dev: the device from which to remove devfreq feature. * @devfreq: the devfreq instance to be removed */ void devm_devfreq_remove_device(struct device *dev, struct devfreq *devfreq) { … } EXPORT_SYMBOL(…); /** * devfreq_suspend_device() - Suspend devfreq of a device. * @devfreq: the devfreq instance to be suspended * * This function is intended to be called by the pm callbacks * (e.g., runtime_suspend, suspend) of the device driver that * holds the devfreq. */ int devfreq_suspend_device(struct devfreq *devfreq) { … } EXPORT_SYMBOL(…); /** * devfreq_resume_device() - Resume devfreq of a device. * @devfreq: the devfreq instance to be resumed * * This function is intended to be called by the pm callbacks * (e.g., runtime_resume, resume) of the device driver that * holds the devfreq. */ int devfreq_resume_device(struct devfreq *devfreq) { … } EXPORT_SYMBOL(…); /** * devfreq_suspend() - Suspend devfreq governors and devices * * Called during system wide Suspend/Hibernate cycles for suspending governors * and devices preserving the state for resume. On some platforms the devfreq * device must have precise state (frequency) after resume in order to provide * fully operating setup. */ void devfreq_suspend(void) { … } /** * devfreq_resume() - Resume devfreq governors and devices * * Called during system wide Suspend/Hibernate cycle for resuming governors and * devices that are suspended with devfreq_suspend(). */ void devfreq_resume(void) { … } /** * devfreq_add_governor() - Add devfreq governor * @governor: the devfreq governor to be added */ int devfreq_add_governor(struct devfreq_governor *governor) { … } EXPORT_SYMBOL(…); static void devm_devfreq_remove_governor(void *governor) { … } /** * devm_devfreq_add_governor() - Add devfreq governor * @dev: device which adds devfreq governor * @governor: the devfreq governor to be added * * This is a resource-managed variant of devfreq_add_governor(). */ int devm_devfreq_add_governor(struct device *dev, struct devfreq_governor *governor) { … } EXPORT_SYMBOL(…); /** * devfreq_remove_governor() - Remove devfreq feature from a device. * @governor: the devfreq governor to be removed */ int devfreq_remove_governor(struct devfreq_governor *governor) { … } EXPORT_SYMBOL(…); static ssize_t name_show(struct device *dev, struct device_attribute *attr, char *buf) { … } static DEVICE_ATTR_RO(name); static ssize_t governor_show(struct device *dev, struct device_attribute *attr, char *buf) { … } static ssize_t governor_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { … } static DEVICE_ATTR_RW(governor); static ssize_t available_governors_show(struct device *d, struct device_attribute *attr, char *buf) { … } static DEVICE_ATTR_RO(available_governors); static ssize_t cur_freq_show(struct device *dev, struct device_attribute *attr, char *buf) { … } static DEVICE_ATTR_RO(cur_freq); static ssize_t target_freq_show(struct device *dev, struct device_attribute *attr, char *buf) { … } static DEVICE_ATTR_RO(target_freq); static ssize_t min_freq_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { … } static ssize_t min_freq_show(struct device *dev, struct device_attribute *attr, char *buf) { … } static DEVICE_ATTR_RW(min_freq); static ssize_t max_freq_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { … } static ssize_t max_freq_show(struct device *dev, struct device_attribute *attr, char *buf) { … } static DEVICE_ATTR_RW(max_freq); static ssize_t available_frequencies_show(struct device *d, struct device_attribute *attr, char *buf) { … } static DEVICE_ATTR_RO(available_frequencies); static ssize_t trans_stat_show(struct device *dev, struct device_attribute *attr, char *buf) { … } static ssize_t trans_stat_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { … } static DEVICE_ATTR_RW(trans_stat); static struct attribute *devfreq_attrs[] = …; ATTRIBUTE_GROUPS(…); static ssize_t polling_interval_show(struct device *dev, struct device_attribute *attr, char *buf) { … } static ssize_t polling_interval_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { … } static DEVICE_ATTR_RW(polling_interval); static ssize_t timer_show(struct device *dev, struct device_attribute *attr, char *buf) { … } static ssize_t timer_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { … } static DEVICE_ATTR_RW(timer); #define CREATE_SYSFS_FILE(df, name) … \ /* Create the specific sysfs files which depend on each governor. */ static void create_sysfs_files(struct devfreq *devfreq, const struct devfreq_governor *gov) { … } /* Remove the specific sysfs files which depend on each governor. */ static void remove_sysfs_files(struct devfreq *devfreq, const struct devfreq_governor *gov) { … } /** * devfreq_summary_show() - Show the summary of the devfreq devices * @s: seq_file instance to show the summary of devfreq devices * @data: not used * * Show the summary of the devfreq devices via 'devfreq_summary' debugfs file. * It helps that user can know the detailed information of the devfreq devices. * * Return 0 always because it shows the information without any data change. */ static int devfreq_summary_show(struct seq_file *s, void *data) { … } DEFINE_SHOW_ATTRIBUTE(…); static int __init devfreq_init(void) { … } subsys_initcall(devfreq_init); /* * The following are helper functions for devfreq user device drivers with * OPP framework. */ /** * devfreq_recommended_opp() - Helper function to get proper OPP for the * freq value given to target callback. * @dev: The devfreq user device. (parent of devfreq) * @freq: The frequency given to target function * @flags: Flags handed from devfreq framework. * * The callers are required to call dev_pm_opp_put() for the returned OPP after * use. */ struct dev_pm_opp *devfreq_recommended_opp(struct device *dev, unsigned long *freq, u32 flags) { … } EXPORT_SYMBOL(…); /** * devfreq_register_opp_notifier() - Helper function to get devfreq notified * for any changes in the OPP availability * changes * @dev: The devfreq user device. (parent of devfreq) * @devfreq: The devfreq object. */ int devfreq_register_opp_notifier(struct device *dev, struct devfreq *devfreq) { … } EXPORT_SYMBOL(…); /** * devfreq_unregister_opp_notifier() - Helper function to stop getting devfreq * notified for any changes in the OPP * availability changes anymore. * @dev: The devfreq user device. (parent of devfreq) * @devfreq: The devfreq object. * * At exit() callback of devfreq_dev_profile, this must be included if * devfreq_recommended_opp is used. */ int devfreq_unregister_opp_notifier(struct device *dev, struct devfreq *devfreq) { … } EXPORT_SYMBOL(…); static void devm_devfreq_opp_release(struct device *dev, void *res) { … } /** * devm_devfreq_register_opp_notifier() - Resource-managed * devfreq_register_opp_notifier() * @dev: The devfreq user device. (parent of devfreq) * @devfreq: The devfreq object. */ int devm_devfreq_register_opp_notifier(struct device *dev, struct devfreq *devfreq) { … } EXPORT_SYMBOL(…); /** * devm_devfreq_unregister_opp_notifier() - Resource-managed * devfreq_unregister_opp_notifier() * @dev: The devfreq user device. (parent of devfreq) * @devfreq: The devfreq object. */ void devm_devfreq_unregister_opp_notifier(struct device *dev, struct devfreq *devfreq) { … } EXPORT_SYMBOL(…); /** * devfreq_register_notifier() - Register a driver with devfreq * @devfreq: The devfreq object. * @nb: The notifier block to register. * @list: DEVFREQ_TRANSITION_NOTIFIER. */ int devfreq_register_notifier(struct devfreq *devfreq, struct notifier_block *nb, unsigned int list) { … } EXPORT_SYMBOL(…); /* * devfreq_unregister_notifier() - Unregister a driver with devfreq * @devfreq: The devfreq object. * @nb: The notifier block to be unregistered. * @list: DEVFREQ_TRANSITION_NOTIFIER. */ int devfreq_unregister_notifier(struct devfreq *devfreq, struct notifier_block *nb, unsigned int list) { … } EXPORT_SYMBOL(…); struct devfreq_notifier_devres { … }; static void devm_devfreq_notifier_release(struct device *dev, void *res) { … } /** * devm_devfreq_register_notifier() * - Resource-managed devfreq_register_notifier() * @dev: The devfreq user device. (parent of devfreq) * @devfreq: The devfreq object. * @nb: The notifier block to be unregistered. * @list: DEVFREQ_TRANSITION_NOTIFIER. */ int devm_devfreq_register_notifier(struct device *dev, struct devfreq *devfreq, struct notifier_block *nb, unsigned int list) { … } EXPORT_SYMBOL(…); /** * devm_devfreq_unregister_notifier() * - Resource-managed devfreq_unregister_notifier() * @dev: The devfreq user device. (parent of devfreq) * @devfreq: The devfreq object. * @nb: The notifier block to be unregistered. * @list: DEVFREQ_TRANSITION_NOTIFIER. */ void devm_devfreq_unregister_notifier(struct device *dev, struct devfreq *devfreq, struct notifier_block *nb, unsigned int list) { … } EXPORT_SYMBOL(…);