18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * devfreq: Generic Dynamic Voltage and Frequency Scaling (DVFS) Framework 48c2ecf20Sopenharmony_ci * for Non-CPU Devices. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (C) 2011 Samsung Electronics 78c2ecf20Sopenharmony_ci * MyungJoo Ham <myungjoo.ham@samsung.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/kmod.h> 128c2ecf20Sopenharmony_ci#include <linux/sched.h> 138c2ecf20Sopenharmony_ci#include <linux/debugfs.h> 148c2ecf20Sopenharmony_ci#include <linux/errno.h> 158c2ecf20Sopenharmony_ci#include <linux/err.h> 168c2ecf20Sopenharmony_ci#include <linux/init.h> 178c2ecf20Sopenharmony_ci#include <linux/export.h> 188c2ecf20Sopenharmony_ci#include <linux/slab.h> 198c2ecf20Sopenharmony_ci#include <linux/stat.h> 208c2ecf20Sopenharmony_ci#include <linux/pm_opp.h> 218c2ecf20Sopenharmony_ci#include <linux/devfreq.h> 228c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 238c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 248c2ecf20Sopenharmony_ci#include <linux/list.h> 258c2ecf20Sopenharmony_ci#include <linux/printk.h> 268c2ecf20Sopenharmony_ci#include <linux/hrtimer.h> 278c2ecf20Sopenharmony_ci#include <linux/of.h> 288c2ecf20Sopenharmony_ci#include <linux/pm_qos.h> 298c2ecf20Sopenharmony_ci#include "governor.h" 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define CREATE_TRACE_POINTS 328c2ecf20Sopenharmony_ci#include <trace/events/devfreq.h> 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define HZ_PER_KHZ 1000 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic struct class *devfreq_class; 378c2ecf20Sopenharmony_cistatic struct dentry *devfreq_debugfs; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci/* 408c2ecf20Sopenharmony_ci * devfreq core provides delayed work based load monitoring helper 418c2ecf20Sopenharmony_ci * functions. Governors can use these or can implement their own 428c2ecf20Sopenharmony_ci * monitoring mechanism. 438c2ecf20Sopenharmony_ci */ 448c2ecf20Sopenharmony_cistatic struct workqueue_struct *devfreq_wq; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci/* The list of all device-devfreq governors */ 478c2ecf20Sopenharmony_cistatic LIST_HEAD(devfreq_governor_list); 488c2ecf20Sopenharmony_ci/* The list of all device-devfreq */ 498c2ecf20Sopenharmony_cistatic LIST_HEAD(devfreq_list); 508c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(devfreq_list_lock); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic const char timer_name[][DEVFREQ_NAME_LEN] = { 538c2ecf20Sopenharmony_ci [DEVFREQ_TIMER_DEFERRABLE] = { "deferrable" }, 548c2ecf20Sopenharmony_ci [DEVFREQ_TIMER_DELAYED] = { "delayed" }, 558c2ecf20Sopenharmony_ci}; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci/** 588c2ecf20Sopenharmony_ci * find_device_devfreq() - find devfreq struct using device pointer 598c2ecf20Sopenharmony_ci * @dev: device pointer used to lookup device devfreq. 608c2ecf20Sopenharmony_ci * 618c2ecf20Sopenharmony_ci * Search the list of device devfreqs and return the matched device's 628c2ecf20Sopenharmony_ci * devfreq info. devfreq_list_lock should be held by the caller. 638c2ecf20Sopenharmony_ci */ 648c2ecf20Sopenharmony_cistatic struct devfreq *find_device_devfreq(struct device *dev) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci struct devfreq *tmp_devfreq; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci lockdep_assert_held(&devfreq_list_lock); 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci if (IS_ERR_OR_NULL(dev)) { 718c2ecf20Sopenharmony_ci pr_err("DEVFREQ: %s: Invalid parameters\n", __func__); 728c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 738c2ecf20Sopenharmony_ci } 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci list_for_each_entry(tmp_devfreq, &devfreq_list, node) { 768c2ecf20Sopenharmony_ci if (tmp_devfreq->dev.parent == dev) 778c2ecf20Sopenharmony_ci return tmp_devfreq; 788c2ecf20Sopenharmony_ci } 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic unsigned long find_available_min_freq(struct devfreq *devfreq) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci struct dev_pm_opp *opp; 868c2ecf20Sopenharmony_ci unsigned long min_freq = 0; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci opp = dev_pm_opp_find_freq_ceil(devfreq->dev.parent, &min_freq); 898c2ecf20Sopenharmony_ci if (IS_ERR(opp)) 908c2ecf20Sopenharmony_ci min_freq = 0; 918c2ecf20Sopenharmony_ci else 928c2ecf20Sopenharmony_ci dev_pm_opp_put(opp); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci return min_freq; 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic unsigned long find_available_max_freq(struct devfreq *devfreq) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci struct dev_pm_opp *opp; 1008c2ecf20Sopenharmony_ci unsigned long max_freq = ULONG_MAX; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci opp = dev_pm_opp_find_freq_floor(devfreq->dev.parent, &max_freq); 1038c2ecf20Sopenharmony_ci if (IS_ERR(opp)) 1048c2ecf20Sopenharmony_ci max_freq = 0; 1058c2ecf20Sopenharmony_ci else 1068c2ecf20Sopenharmony_ci dev_pm_opp_put(opp); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci return max_freq; 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci/** 1128c2ecf20Sopenharmony_ci * get_freq_range() - Get the current freq range 1138c2ecf20Sopenharmony_ci * @devfreq: the devfreq instance 1148c2ecf20Sopenharmony_ci * @min_freq: the min frequency 1158c2ecf20Sopenharmony_ci * @max_freq: the max frequency 1168c2ecf20Sopenharmony_ci * 1178c2ecf20Sopenharmony_ci * This takes into consideration all constraints. 1188c2ecf20Sopenharmony_ci */ 1198c2ecf20Sopenharmony_cistatic void get_freq_range(struct devfreq *devfreq, 1208c2ecf20Sopenharmony_ci unsigned long *min_freq, 1218c2ecf20Sopenharmony_ci unsigned long *max_freq) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci unsigned long *freq_table = devfreq->profile->freq_table; 1248c2ecf20Sopenharmony_ci s32 qos_min_freq, qos_max_freq; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci lockdep_assert_held(&devfreq->lock); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci /* 1298c2ecf20Sopenharmony_ci * Initialize minimum/maximum frequency from freq table. 1308c2ecf20Sopenharmony_ci * The devfreq drivers can initialize this in either ascending or 1318c2ecf20Sopenharmony_ci * descending order and devfreq core supports both. 1328c2ecf20Sopenharmony_ci */ 1338c2ecf20Sopenharmony_ci if (freq_table[0] < freq_table[devfreq->profile->max_state - 1]) { 1348c2ecf20Sopenharmony_ci *min_freq = freq_table[0]; 1358c2ecf20Sopenharmony_ci *max_freq = freq_table[devfreq->profile->max_state - 1]; 1368c2ecf20Sopenharmony_ci } else { 1378c2ecf20Sopenharmony_ci *min_freq = freq_table[devfreq->profile->max_state - 1]; 1388c2ecf20Sopenharmony_ci *max_freq = freq_table[0]; 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci /* Apply constraints from PM QoS */ 1428c2ecf20Sopenharmony_ci qos_min_freq = dev_pm_qos_read_value(devfreq->dev.parent, 1438c2ecf20Sopenharmony_ci DEV_PM_QOS_MIN_FREQUENCY); 1448c2ecf20Sopenharmony_ci qos_max_freq = dev_pm_qos_read_value(devfreq->dev.parent, 1458c2ecf20Sopenharmony_ci DEV_PM_QOS_MAX_FREQUENCY); 1468c2ecf20Sopenharmony_ci *min_freq = max(*min_freq, (unsigned long)HZ_PER_KHZ * qos_min_freq); 1478c2ecf20Sopenharmony_ci if (qos_max_freq != PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE) 1488c2ecf20Sopenharmony_ci *max_freq = min(*max_freq, 1498c2ecf20Sopenharmony_ci (unsigned long)HZ_PER_KHZ * qos_max_freq); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci /* Apply constraints from OPP interface */ 1528c2ecf20Sopenharmony_ci *min_freq = max(*min_freq, devfreq->scaling_min_freq); 1538c2ecf20Sopenharmony_ci *max_freq = min(*max_freq, devfreq->scaling_max_freq); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci if (*min_freq > *max_freq) 1568c2ecf20Sopenharmony_ci *min_freq = *max_freq; 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci/** 1608c2ecf20Sopenharmony_ci * devfreq_get_freq_level() - Lookup freq_table for the frequency 1618c2ecf20Sopenharmony_ci * @devfreq: the devfreq instance 1628c2ecf20Sopenharmony_ci * @freq: the target frequency 1638c2ecf20Sopenharmony_ci */ 1648c2ecf20Sopenharmony_cistatic int devfreq_get_freq_level(struct devfreq *devfreq, unsigned long freq) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci int lev; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci for (lev = 0; lev < devfreq->profile->max_state; lev++) 1698c2ecf20Sopenharmony_ci if (freq == devfreq->profile->freq_table[lev]) 1708c2ecf20Sopenharmony_ci return lev; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci return -EINVAL; 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic int set_freq_table(struct devfreq *devfreq) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci struct devfreq_dev_profile *profile = devfreq->profile; 1788c2ecf20Sopenharmony_ci struct dev_pm_opp *opp; 1798c2ecf20Sopenharmony_ci unsigned long freq; 1808c2ecf20Sopenharmony_ci int i, count; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci /* Initialize the freq_table from OPP table */ 1838c2ecf20Sopenharmony_ci count = dev_pm_opp_get_opp_count(devfreq->dev.parent); 1848c2ecf20Sopenharmony_ci if (count <= 0) 1858c2ecf20Sopenharmony_ci return -EINVAL; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci profile->max_state = count; 1888c2ecf20Sopenharmony_ci profile->freq_table = devm_kcalloc(devfreq->dev.parent, 1898c2ecf20Sopenharmony_ci profile->max_state, 1908c2ecf20Sopenharmony_ci sizeof(*profile->freq_table), 1918c2ecf20Sopenharmony_ci GFP_KERNEL); 1928c2ecf20Sopenharmony_ci if (!profile->freq_table) { 1938c2ecf20Sopenharmony_ci profile->max_state = 0; 1948c2ecf20Sopenharmony_ci return -ENOMEM; 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci for (i = 0, freq = 0; i < profile->max_state; i++, freq++) { 1988c2ecf20Sopenharmony_ci opp = dev_pm_opp_find_freq_ceil(devfreq->dev.parent, &freq); 1998c2ecf20Sopenharmony_ci if (IS_ERR(opp)) { 2008c2ecf20Sopenharmony_ci devm_kfree(devfreq->dev.parent, profile->freq_table); 2018c2ecf20Sopenharmony_ci profile->max_state = 0; 2028c2ecf20Sopenharmony_ci return PTR_ERR(opp); 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci dev_pm_opp_put(opp); 2058c2ecf20Sopenharmony_ci profile->freq_table[i] = freq; 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci return 0; 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci/** 2128c2ecf20Sopenharmony_ci * devfreq_update_status() - Update statistics of devfreq behavior 2138c2ecf20Sopenharmony_ci * @devfreq: the devfreq instance 2148c2ecf20Sopenharmony_ci * @freq: the update target frequency 2158c2ecf20Sopenharmony_ci */ 2168c2ecf20Sopenharmony_ciint devfreq_update_status(struct devfreq *devfreq, unsigned long freq) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci int lev, prev_lev, ret = 0; 2198c2ecf20Sopenharmony_ci u64 cur_time; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci lockdep_assert_held(&devfreq->lock); 2228c2ecf20Sopenharmony_ci cur_time = get_jiffies_64(); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci /* Immediately exit if previous_freq is not initialized yet. */ 2258c2ecf20Sopenharmony_ci if (!devfreq->previous_freq) 2268c2ecf20Sopenharmony_ci goto out; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci prev_lev = devfreq_get_freq_level(devfreq, devfreq->previous_freq); 2298c2ecf20Sopenharmony_ci if (prev_lev < 0) { 2308c2ecf20Sopenharmony_ci ret = prev_lev; 2318c2ecf20Sopenharmony_ci goto out; 2328c2ecf20Sopenharmony_ci } 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci devfreq->stats.time_in_state[prev_lev] += 2358c2ecf20Sopenharmony_ci cur_time - devfreq->stats.last_update; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci lev = devfreq_get_freq_level(devfreq, freq); 2388c2ecf20Sopenharmony_ci if (lev < 0) { 2398c2ecf20Sopenharmony_ci ret = lev; 2408c2ecf20Sopenharmony_ci goto out; 2418c2ecf20Sopenharmony_ci } 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci if (lev != prev_lev) { 2448c2ecf20Sopenharmony_ci devfreq->stats.trans_table[ 2458c2ecf20Sopenharmony_ci (prev_lev * devfreq->profile->max_state) + lev]++; 2468c2ecf20Sopenharmony_ci devfreq->stats.total_trans++; 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ciout: 2508c2ecf20Sopenharmony_ci devfreq->stats.last_update = cur_time; 2518c2ecf20Sopenharmony_ci return ret; 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ciEXPORT_SYMBOL(devfreq_update_status); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci/** 2568c2ecf20Sopenharmony_ci * find_devfreq_governor() - find devfreq governor from name 2578c2ecf20Sopenharmony_ci * @name: name of the governor 2588c2ecf20Sopenharmony_ci * 2598c2ecf20Sopenharmony_ci * Search the list of devfreq governors and return the matched 2608c2ecf20Sopenharmony_ci * governor's pointer. devfreq_list_lock should be held by the caller. 2618c2ecf20Sopenharmony_ci */ 2628c2ecf20Sopenharmony_cistatic struct devfreq_governor *find_devfreq_governor(const char *name) 2638c2ecf20Sopenharmony_ci{ 2648c2ecf20Sopenharmony_ci struct devfreq_governor *tmp_governor; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci lockdep_assert_held(&devfreq_list_lock); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci if (IS_ERR_OR_NULL(name)) { 2698c2ecf20Sopenharmony_ci pr_err("DEVFREQ: %s: Invalid parameters\n", __func__); 2708c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 2718c2ecf20Sopenharmony_ci } 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci list_for_each_entry(tmp_governor, &devfreq_governor_list, node) { 2748c2ecf20Sopenharmony_ci if (!strncmp(tmp_governor->name, name, DEVFREQ_NAME_LEN)) 2758c2ecf20Sopenharmony_ci return tmp_governor; 2768c2ecf20Sopenharmony_ci } 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 2798c2ecf20Sopenharmony_ci} 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci/** 2828c2ecf20Sopenharmony_ci * try_then_request_governor() - Try to find the governor and request the 2838c2ecf20Sopenharmony_ci * module if is not found. 2848c2ecf20Sopenharmony_ci * @name: name of the governor 2858c2ecf20Sopenharmony_ci * 2868c2ecf20Sopenharmony_ci * Search the list of devfreq governors and request the module and try again 2878c2ecf20Sopenharmony_ci * if is not found. This can happen when both drivers (the governor driver 2888c2ecf20Sopenharmony_ci * and the driver that call devfreq_add_device) are built as modules. 2898c2ecf20Sopenharmony_ci * devfreq_list_lock should be held by the caller. Returns the matched 2908c2ecf20Sopenharmony_ci * governor's pointer or an error pointer. 2918c2ecf20Sopenharmony_ci */ 2928c2ecf20Sopenharmony_cistatic struct devfreq_governor *try_then_request_governor(const char *name) 2938c2ecf20Sopenharmony_ci{ 2948c2ecf20Sopenharmony_ci struct devfreq_governor *governor; 2958c2ecf20Sopenharmony_ci int err = 0; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci lockdep_assert_held(&devfreq_list_lock); 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci if (IS_ERR_OR_NULL(name)) { 3008c2ecf20Sopenharmony_ci pr_err("DEVFREQ: %s: Invalid parameters\n", __func__); 3018c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 3028c2ecf20Sopenharmony_ci } 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci governor = find_devfreq_governor(name); 3058c2ecf20Sopenharmony_ci if (IS_ERR(governor)) { 3068c2ecf20Sopenharmony_ci mutex_unlock(&devfreq_list_lock); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci if (!strncmp(name, DEVFREQ_GOV_SIMPLE_ONDEMAND, 3098c2ecf20Sopenharmony_ci DEVFREQ_NAME_LEN)) 3108c2ecf20Sopenharmony_ci err = request_module("governor_%s", "simpleondemand"); 3118c2ecf20Sopenharmony_ci else 3128c2ecf20Sopenharmony_ci err = request_module("governor_%s", name); 3138c2ecf20Sopenharmony_ci /* Restore previous state before return */ 3148c2ecf20Sopenharmony_ci mutex_lock(&devfreq_list_lock); 3158c2ecf20Sopenharmony_ci if (err) 3168c2ecf20Sopenharmony_ci return (err < 0) ? ERR_PTR(err) : ERR_PTR(-EINVAL); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci governor = find_devfreq_governor(name); 3198c2ecf20Sopenharmony_ci } 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci return governor; 3228c2ecf20Sopenharmony_ci} 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_cistatic int devfreq_notify_transition(struct devfreq *devfreq, 3258c2ecf20Sopenharmony_ci struct devfreq_freqs *freqs, unsigned int state) 3268c2ecf20Sopenharmony_ci{ 3278c2ecf20Sopenharmony_ci if (!devfreq) 3288c2ecf20Sopenharmony_ci return -EINVAL; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci switch (state) { 3318c2ecf20Sopenharmony_ci case DEVFREQ_PRECHANGE: 3328c2ecf20Sopenharmony_ci srcu_notifier_call_chain(&devfreq->transition_notifier_list, 3338c2ecf20Sopenharmony_ci DEVFREQ_PRECHANGE, freqs); 3348c2ecf20Sopenharmony_ci break; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci case DEVFREQ_POSTCHANGE: 3378c2ecf20Sopenharmony_ci srcu_notifier_call_chain(&devfreq->transition_notifier_list, 3388c2ecf20Sopenharmony_ci DEVFREQ_POSTCHANGE, freqs); 3398c2ecf20Sopenharmony_ci break; 3408c2ecf20Sopenharmony_ci default: 3418c2ecf20Sopenharmony_ci return -EINVAL; 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci return 0; 3458c2ecf20Sopenharmony_ci} 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_cistatic int devfreq_set_target(struct devfreq *devfreq, unsigned long new_freq, 3488c2ecf20Sopenharmony_ci u32 flags) 3498c2ecf20Sopenharmony_ci{ 3508c2ecf20Sopenharmony_ci struct devfreq_freqs freqs; 3518c2ecf20Sopenharmony_ci unsigned long cur_freq; 3528c2ecf20Sopenharmony_ci int err = 0; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci if (devfreq->profile->get_cur_freq) 3558c2ecf20Sopenharmony_ci devfreq->profile->get_cur_freq(devfreq->dev.parent, &cur_freq); 3568c2ecf20Sopenharmony_ci else 3578c2ecf20Sopenharmony_ci cur_freq = devfreq->previous_freq; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci freqs.old = cur_freq; 3608c2ecf20Sopenharmony_ci freqs.new = new_freq; 3618c2ecf20Sopenharmony_ci devfreq_notify_transition(devfreq, &freqs, DEVFREQ_PRECHANGE); 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci err = devfreq->profile->target(devfreq->dev.parent, &new_freq, flags); 3648c2ecf20Sopenharmony_ci if (err) { 3658c2ecf20Sopenharmony_ci freqs.new = cur_freq; 3668c2ecf20Sopenharmony_ci devfreq_notify_transition(devfreq, &freqs, DEVFREQ_POSTCHANGE); 3678c2ecf20Sopenharmony_ci return err; 3688c2ecf20Sopenharmony_ci } 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci freqs.new = new_freq; 3718c2ecf20Sopenharmony_ci devfreq_notify_transition(devfreq, &freqs, DEVFREQ_POSTCHANGE); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci if (devfreq_update_status(devfreq, new_freq)) 3748c2ecf20Sopenharmony_ci dev_err(&devfreq->dev, 3758c2ecf20Sopenharmony_ci "Couldn't update frequency transition information.\n"); 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci devfreq->previous_freq = new_freq; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci if (devfreq->suspend_freq) 3808c2ecf20Sopenharmony_ci devfreq->resume_freq = new_freq; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci return err; 3838c2ecf20Sopenharmony_ci} 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci/* Load monitoring helper functions for governors use */ 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci/** 3888c2ecf20Sopenharmony_ci * update_devfreq() - Reevaluate the device and configure frequency. 3898c2ecf20Sopenharmony_ci * @devfreq: the devfreq instance. 3908c2ecf20Sopenharmony_ci * 3918c2ecf20Sopenharmony_ci * Note: Lock devfreq->lock before calling update_devfreq 3928c2ecf20Sopenharmony_ci * This function is exported for governors. 3938c2ecf20Sopenharmony_ci */ 3948c2ecf20Sopenharmony_ciint update_devfreq(struct devfreq *devfreq) 3958c2ecf20Sopenharmony_ci{ 3968c2ecf20Sopenharmony_ci unsigned long freq, min_freq, max_freq; 3978c2ecf20Sopenharmony_ci int err = 0; 3988c2ecf20Sopenharmony_ci u32 flags = 0; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci lockdep_assert_held(&devfreq->lock); 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci if (!devfreq->governor) 4038c2ecf20Sopenharmony_ci return -EINVAL; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci /* Reevaluate the proper frequency */ 4068c2ecf20Sopenharmony_ci err = devfreq->governor->get_target_freq(devfreq, &freq); 4078c2ecf20Sopenharmony_ci if (err) 4088c2ecf20Sopenharmony_ci return err; 4098c2ecf20Sopenharmony_ci get_freq_range(devfreq, &min_freq, &max_freq); 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci if (freq < min_freq) { 4128c2ecf20Sopenharmony_ci freq = min_freq; 4138c2ecf20Sopenharmony_ci flags &= ~DEVFREQ_FLAG_LEAST_UPPER_BOUND; /* Use GLB */ 4148c2ecf20Sopenharmony_ci } 4158c2ecf20Sopenharmony_ci if (freq > max_freq) { 4168c2ecf20Sopenharmony_ci freq = max_freq; 4178c2ecf20Sopenharmony_ci flags |= DEVFREQ_FLAG_LEAST_UPPER_BOUND; /* Use LUB */ 4188c2ecf20Sopenharmony_ci } 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci return devfreq_set_target(devfreq, freq, flags); 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci} 4238c2ecf20Sopenharmony_ciEXPORT_SYMBOL(update_devfreq); 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci/** 4268c2ecf20Sopenharmony_ci * devfreq_monitor() - Periodically poll devfreq objects. 4278c2ecf20Sopenharmony_ci * @work: the work struct used to run devfreq_monitor periodically. 4288c2ecf20Sopenharmony_ci * 4298c2ecf20Sopenharmony_ci */ 4308c2ecf20Sopenharmony_cistatic void devfreq_monitor(struct work_struct *work) 4318c2ecf20Sopenharmony_ci{ 4328c2ecf20Sopenharmony_ci int err; 4338c2ecf20Sopenharmony_ci struct devfreq *devfreq = container_of(work, 4348c2ecf20Sopenharmony_ci struct devfreq, work.work); 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci mutex_lock(&devfreq->lock); 4378c2ecf20Sopenharmony_ci err = update_devfreq(devfreq); 4388c2ecf20Sopenharmony_ci if (err) 4398c2ecf20Sopenharmony_ci dev_err(&devfreq->dev, "dvfs failed with (%d) error\n", err); 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci if (devfreq->stop_polling) 4428c2ecf20Sopenharmony_ci goto out; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci queue_delayed_work(devfreq_wq, &devfreq->work, 4458c2ecf20Sopenharmony_ci msecs_to_jiffies(devfreq->profile->polling_ms)); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ciout: 4488c2ecf20Sopenharmony_ci mutex_unlock(&devfreq->lock); 4498c2ecf20Sopenharmony_ci trace_devfreq_monitor(devfreq); 4508c2ecf20Sopenharmony_ci} 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci/** 4538c2ecf20Sopenharmony_ci * devfreq_monitor_start() - Start load monitoring of devfreq instance 4548c2ecf20Sopenharmony_ci * @devfreq: the devfreq instance. 4558c2ecf20Sopenharmony_ci * 4568c2ecf20Sopenharmony_ci * Helper function for starting devfreq device load monitoring. By 4578c2ecf20Sopenharmony_ci * default delayed work based monitoring is supported. Function 4588c2ecf20Sopenharmony_ci * to be called from governor in response to DEVFREQ_GOV_START 4598c2ecf20Sopenharmony_ci * event when device is added to devfreq framework. 4608c2ecf20Sopenharmony_ci */ 4618c2ecf20Sopenharmony_civoid devfreq_monitor_start(struct devfreq *devfreq) 4628c2ecf20Sopenharmony_ci{ 4638c2ecf20Sopenharmony_ci if (devfreq->governor->interrupt_driven) 4648c2ecf20Sopenharmony_ci return; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci mutex_lock(&devfreq->lock); 4678c2ecf20Sopenharmony_ci if (delayed_work_pending(&devfreq->work)) 4688c2ecf20Sopenharmony_ci goto out; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci switch (devfreq->profile->timer) { 4718c2ecf20Sopenharmony_ci case DEVFREQ_TIMER_DEFERRABLE: 4728c2ecf20Sopenharmony_ci INIT_DEFERRABLE_WORK(&devfreq->work, devfreq_monitor); 4738c2ecf20Sopenharmony_ci break; 4748c2ecf20Sopenharmony_ci case DEVFREQ_TIMER_DELAYED: 4758c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&devfreq->work, devfreq_monitor); 4768c2ecf20Sopenharmony_ci break; 4778c2ecf20Sopenharmony_ci default: 4788c2ecf20Sopenharmony_ci goto out; 4798c2ecf20Sopenharmony_ci } 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci if (devfreq->profile->polling_ms) 4828c2ecf20Sopenharmony_ci queue_delayed_work(devfreq_wq, &devfreq->work, 4838c2ecf20Sopenharmony_ci msecs_to_jiffies(devfreq->profile->polling_ms)); 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ciout: 4868c2ecf20Sopenharmony_ci devfreq->stop_polling = false; 4878c2ecf20Sopenharmony_ci mutex_unlock(&devfreq->lock); 4888c2ecf20Sopenharmony_ci} 4898c2ecf20Sopenharmony_ciEXPORT_SYMBOL(devfreq_monitor_start); 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci/** 4928c2ecf20Sopenharmony_ci * devfreq_monitor_stop() - Stop load monitoring of a devfreq instance 4938c2ecf20Sopenharmony_ci * @devfreq: the devfreq instance. 4948c2ecf20Sopenharmony_ci * 4958c2ecf20Sopenharmony_ci * Helper function to stop devfreq device load monitoring. Function 4968c2ecf20Sopenharmony_ci * to be called from governor in response to DEVFREQ_GOV_STOP 4978c2ecf20Sopenharmony_ci * event when device is removed from devfreq framework. 4988c2ecf20Sopenharmony_ci */ 4998c2ecf20Sopenharmony_civoid devfreq_monitor_stop(struct devfreq *devfreq) 5008c2ecf20Sopenharmony_ci{ 5018c2ecf20Sopenharmony_ci if (devfreq->governor->interrupt_driven) 5028c2ecf20Sopenharmony_ci return; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci mutex_lock(&devfreq->lock); 5058c2ecf20Sopenharmony_ci if (devfreq->stop_polling) { 5068c2ecf20Sopenharmony_ci mutex_unlock(&devfreq->lock); 5078c2ecf20Sopenharmony_ci return; 5088c2ecf20Sopenharmony_ci } 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci devfreq->stop_polling = true; 5118c2ecf20Sopenharmony_ci mutex_unlock(&devfreq->lock); 5128c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&devfreq->work); 5138c2ecf20Sopenharmony_ci} 5148c2ecf20Sopenharmony_ciEXPORT_SYMBOL(devfreq_monitor_stop); 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci/** 5178c2ecf20Sopenharmony_ci * devfreq_monitor_suspend() - Suspend load monitoring of a devfreq instance 5188c2ecf20Sopenharmony_ci * @devfreq: the devfreq instance. 5198c2ecf20Sopenharmony_ci * 5208c2ecf20Sopenharmony_ci * Helper function to suspend devfreq device load monitoring. Function 5218c2ecf20Sopenharmony_ci * to be called from governor in response to DEVFREQ_GOV_SUSPEND 5228c2ecf20Sopenharmony_ci * event or when polling interval is set to zero. 5238c2ecf20Sopenharmony_ci * 5248c2ecf20Sopenharmony_ci * Note: Though this function is same as devfreq_monitor_stop(), 5258c2ecf20Sopenharmony_ci * intentionally kept separate to provide hooks for collecting 5268c2ecf20Sopenharmony_ci * transition statistics. 5278c2ecf20Sopenharmony_ci */ 5288c2ecf20Sopenharmony_civoid devfreq_monitor_suspend(struct devfreq *devfreq) 5298c2ecf20Sopenharmony_ci{ 5308c2ecf20Sopenharmony_ci mutex_lock(&devfreq->lock); 5318c2ecf20Sopenharmony_ci if (devfreq->stop_polling) { 5328c2ecf20Sopenharmony_ci mutex_unlock(&devfreq->lock); 5338c2ecf20Sopenharmony_ci return; 5348c2ecf20Sopenharmony_ci } 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci devfreq_update_status(devfreq, devfreq->previous_freq); 5378c2ecf20Sopenharmony_ci devfreq->stop_polling = true; 5388c2ecf20Sopenharmony_ci mutex_unlock(&devfreq->lock); 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci if (devfreq->governor->interrupt_driven) 5418c2ecf20Sopenharmony_ci return; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&devfreq->work); 5448c2ecf20Sopenharmony_ci} 5458c2ecf20Sopenharmony_ciEXPORT_SYMBOL(devfreq_monitor_suspend); 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci/** 5488c2ecf20Sopenharmony_ci * devfreq_monitor_resume() - Resume load monitoring of a devfreq instance 5498c2ecf20Sopenharmony_ci * @devfreq: the devfreq instance. 5508c2ecf20Sopenharmony_ci * 5518c2ecf20Sopenharmony_ci * Helper function to resume devfreq device load monitoring. Function 5528c2ecf20Sopenharmony_ci * to be called from governor in response to DEVFREQ_GOV_RESUME 5538c2ecf20Sopenharmony_ci * event or when polling interval is set to non-zero. 5548c2ecf20Sopenharmony_ci */ 5558c2ecf20Sopenharmony_civoid devfreq_monitor_resume(struct devfreq *devfreq) 5568c2ecf20Sopenharmony_ci{ 5578c2ecf20Sopenharmony_ci unsigned long freq; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci mutex_lock(&devfreq->lock); 5608c2ecf20Sopenharmony_ci if (!devfreq->stop_polling) 5618c2ecf20Sopenharmony_ci goto out; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci if (devfreq->governor->interrupt_driven) 5648c2ecf20Sopenharmony_ci goto out_update; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci if (!delayed_work_pending(&devfreq->work) && 5678c2ecf20Sopenharmony_ci devfreq->profile->polling_ms) 5688c2ecf20Sopenharmony_ci queue_delayed_work(devfreq_wq, &devfreq->work, 5698c2ecf20Sopenharmony_ci msecs_to_jiffies(devfreq->profile->polling_ms)); 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ciout_update: 5728c2ecf20Sopenharmony_ci devfreq->stats.last_update = get_jiffies_64(); 5738c2ecf20Sopenharmony_ci devfreq->stop_polling = false; 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci if (devfreq->profile->get_cur_freq && 5768c2ecf20Sopenharmony_ci !devfreq->profile->get_cur_freq(devfreq->dev.parent, &freq)) 5778c2ecf20Sopenharmony_ci devfreq->previous_freq = freq; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ciout: 5808c2ecf20Sopenharmony_ci mutex_unlock(&devfreq->lock); 5818c2ecf20Sopenharmony_ci} 5828c2ecf20Sopenharmony_ciEXPORT_SYMBOL(devfreq_monitor_resume); 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci/** 5858c2ecf20Sopenharmony_ci * devfreq_update_interval() - Update device devfreq monitoring interval 5868c2ecf20Sopenharmony_ci * @devfreq: the devfreq instance. 5878c2ecf20Sopenharmony_ci * @delay: new polling interval to be set. 5888c2ecf20Sopenharmony_ci * 5898c2ecf20Sopenharmony_ci * Helper function to set new load monitoring polling interval. Function 5908c2ecf20Sopenharmony_ci * to be called from governor in response to DEVFREQ_GOV_UPDATE_INTERVAL event. 5918c2ecf20Sopenharmony_ci */ 5928c2ecf20Sopenharmony_civoid devfreq_update_interval(struct devfreq *devfreq, unsigned int *delay) 5938c2ecf20Sopenharmony_ci{ 5948c2ecf20Sopenharmony_ci unsigned int cur_delay = devfreq->profile->polling_ms; 5958c2ecf20Sopenharmony_ci unsigned int new_delay = *delay; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci mutex_lock(&devfreq->lock); 5988c2ecf20Sopenharmony_ci devfreq->profile->polling_ms = new_delay; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci if (devfreq->stop_polling) 6018c2ecf20Sopenharmony_ci goto out; 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci if (devfreq->governor->interrupt_driven) 6048c2ecf20Sopenharmony_ci goto out; 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci /* if new delay is zero, stop polling */ 6078c2ecf20Sopenharmony_ci if (!new_delay) { 6088c2ecf20Sopenharmony_ci mutex_unlock(&devfreq->lock); 6098c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&devfreq->work); 6108c2ecf20Sopenharmony_ci return; 6118c2ecf20Sopenharmony_ci } 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci /* if current delay is zero, start polling with new delay */ 6148c2ecf20Sopenharmony_ci if (!cur_delay) { 6158c2ecf20Sopenharmony_ci queue_delayed_work(devfreq_wq, &devfreq->work, 6168c2ecf20Sopenharmony_ci msecs_to_jiffies(devfreq->profile->polling_ms)); 6178c2ecf20Sopenharmony_ci goto out; 6188c2ecf20Sopenharmony_ci } 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci /* if current delay is greater than new delay, restart polling */ 6218c2ecf20Sopenharmony_ci if (cur_delay > new_delay) { 6228c2ecf20Sopenharmony_ci mutex_unlock(&devfreq->lock); 6238c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&devfreq->work); 6248c2ecf20Sopenharmony_ci mutex_lock(&devfreq->lock); 6258c2ecf20Sopenharmony_ci if (!devfreq->stop_polling) 6268c2ecf20Sopenharmony_ci queue_delayed_work(devfreq_wq, &devfreq->work, 6278c2ecf20Sopenharmony_ci msecs_to_jiffies(devfreq->profile->polling_ms)); 6288c2ecf20Sopenharmony_ci } 6298c2ecf20Sopenharmony_ciout: 6308c2ecf20Sopenharmony_ci mutex_unlock(&devfreq->lock); 6318c2ecf20Sopenharmony_ci} 6328c2ecf20Sopenharmony_ciEXPORT_SYMBOL(devfreq_update_interval); 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci/** 6358c2ecf20Sopenharmony_ci * devfreq_notifier_call() - Notify that the device frequency requirements 6368c2ecf20Sopenharmony_ci * has been changed out of devfreq framework. 6378c2ecf20Sopenharmony_ci * @nb: the notifier_block (supposed to be devfreq->nb) 6388c2ecf20Sopenharmony_ci * @type: not used 6398c2ecf20Sopenharmony_ci * @devp: not used 6408c2ecf20Sopenharmony_ci * 6418c2ecf20Sopenharmony_ci * Called by a notifier that uses devfreq->nb. 6428c2ecf20Sopenharmony_ci */ 6438c2ecf20Sopenharmony_cistatic int devfreq_notifier_call(struct notifier_block *nb, unsigned long type, 6448c2ecf20Sopenharmony_ci void *devp) 6458c2ecf20Sopenharmony_ci{ 6468c2ecf20Sopenharmony_ci struct devfreq *devfreq = container_of(nb, struct devfreq, nb); 6478c2ecf20Sopenharmony_ci int err = -EINVAL; 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci mutex_lock(&devfreq->lock); 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci devfreq->scaling_min_freq = find_available_min_freq(devfreq); 6528c2ecf20Sopenharmony_ci if (!devfreq->scaling_min_freq) 6538c2ecf20Sopenharmony_ci goto out; 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci devfreq->scaling_max_freq = find_available_max_freq(devfreq); 6568c2ecf20Sopenharmony_ci if (!devfreq->scaling_max_freq) { 6578c2ecf20Sopenharmony_ci devfreq->scaling_max_freq = ULONG_MAX; 6588c2ecf20Sopenharmony_ci goto out; 6598c2ecf20Sopenharmony_ci } 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci err = update_devfreq(devfreq); 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ciout: 6648c2ecf20Sopenharmony_ci mutex_unlock(&devfreq->lock); 6658c2ecf20Sopenharmony_ci if (err) 6668c2ecf20Sopenharmony_ci dev_err(devfreq->dev.parent, 6678c2ecf20Sopenharmony_ci "failed to update frequency from OPP notifier (%d)\n", 6688c2ecf20Sopenharmony_ci err); 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci return NOTIFY_OK; 6718c2ecf20Sopenharmony_ci} 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci/** 6748c2ecf20Sopenharmony_ci * qos_notifier_call() - Common handler for QoS constraints. 6758c2ecf20Sopenharmony_ci * @devfreq: the devfreq instance. 6768c2ecf20Sopenharmony_ci */ 6778c2ecf20Sopenharmony_cistatic int qos_notifier_call(struct devfreq *devfreq) 6788c2ecf20Sopenharmony_ci{ 6798c2ecf20Sopenharmony_ci int err; 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci mutex_lock(&devfreq->lock); 6828c2ecf20Sopenharmony_ci err = update_devfreq(devfreq); 6838c2ecf20Sopenharmony_ci mutex_unlock(&devfreq->lock); 6848c2ecf20Sopenharmony_ci if (err) 6858c2ecf20Sopenharmony_ci dev_err(devfreq->dev.parent, 6868c2ecf20Sopenharmony_ci "failed to update frequency from PM QoS (%d)\n", 6878c2ecf20Sopenharmony_ci err); 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci return NOTIFY_OK; 6908c2ecf20Sopenharmony_ci} 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci/** 6938c2ecf20Sopenharmony_ci * qos_min_notifier_call() - Callback for QoS min_freq changes. 6948c2ecf20Sopenharmony_ci * @nb: Should be devfreq->nb_min 6958c2ecf20Sopenharmony_ci */ 6968c2ecf20Sopenharmony_cistatic int qos_min_notifier_call(struct notifier_block *nb, 6978c2ecf20Sopenharmony_ci unsigned long val, void *ptr) 6988c2ecf20Sopenharmony_ci{ 6998c2ecf20Sopenharmony_ci return qos_notifier_call(container_of(nb, struct devfreq, nb_min)); 7008c2ecf20Sopenharmony_ci} 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci/** 7038c2ecf20Sopenharmony_ci * qos_max_notifier_call() - Callback for QoS max_freq changes. 7048c2ecf20Sopenharmony_ci * @nb: Should be devfreq->nb_max 7058c2ecf20Sopenharmony_ci */ 7068c2ecf20Sopenharmony_cistatic int qos_max_notifier_call(struct notifier_block *nb, 7078c2ecf20Sopenharmony_ci unsigned long val, void *ptr) 7088c2ecf20Sopenharmony_ci{ 7098c2ecf20Sopenharmony_ci return qos_notifier_call(container_of(nb, struct devfreq, nb_max)); 7108c2ecf20Sopenharmony_ci} 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci/** 7138c2ecf20Sopenharmony_ci * devfreq_dev_release() - Callback for struct device to release the device. 7148c2ecf20Sopenharmony_ci * @dev: the devfreq device 7158c2ecf20Sopenharmony_ci * 7168c2ecf20Sopenharmony_ci * Remove devfreq from the list and release its resources. 7178c2ecf20Sopenharmony_ci */ 7188c2ecf20Sopenharmony_cistatic void devfreq_dev_release(struct device *dev) 7198c2ecf20Sopenharmony_ci{ 7208c2ecf20Sopenharmony_ci struct devfreq *devfreq = to_devfreq(dev); 7218c2ecf20Sopenharmony_ci int err; 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci mutex_lock(&devfreq_list_lock); 7248c2ecf20Sopenharmony_ci list_del(&devfreq->node); 7258c2ecf20Sopenharmony_ci mutex_unlock(&devfreq_list_lock); 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci err = dev_pm_qos_remove_notifier(devfreq->dev.parent, &devfreq->nb_max, 7288c2ecf20Sopenharmony_ci DEV_PM_QOS_MAX_FREQUENCY); 7298c2ecf20Sopenharmony_ci if (err && err != -ENOENT) 7308c2ecf20Sopenharmony_ci dev_warn(dev->parent, 7318c2ecf20Sopenharmony_ci "Failed to remove max_freq notifier: %d\n", err); 7328c2ecf20Sopenharmony_ci err = dev_pm_qos_remove_notifier(devfreq->dev.parent, &devfreq->nb_min, 7338c2ecf20Sopenharmony_ci DEV_PM_QOS_MIN_FREQUENCY); 7348c2ecf20Sopenharmony_ci if (err && err != -ENOENT) 7358c2ecf20Sopenharmony_ci dev_warn(dev->parent, 7368c2ecf20Sopenharmony_ci "Failed to remove min_freq notifier: %d\n", err); 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci if (dev_pm_qos_request_active(&devfreq->user_max_freq_req)) { 7398c2ecf20Sopenharmony_ci err = dev_pm_qos_remove_request(&devfreq->user_max_freq_req); 7408c2ecf20Sopenharmony_ci if (err < 0) 7418c2ecf20Sopenharmony_ci dev_warn(dev->parent, 7428c2ecf20Sopenharmony_ci "Failed to remove max_freq request: %d\n", err); 7438c2ecf20Sopenharmony_ci } 7448c2ecf20Sopenharmony_ci if (dev_pm_qos_request_active(&devfreq->user_min_freq_req)) { 7458c2ecf20Sopenharmony_ci err = dev_pm_qos_remove_request(&devfreq->user_min_freq_req); 7468c2ecf20Sopenharmony_ci if (err < 0) 7478c2ecf20Sopenharmony_ci dev_warn(dev->parent, 7488c2ecf20Sopenharmony_ci "Failed to remove min_freq request: %d\n", err); 7498c2ecf20Sopenharmony_ci } 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci if (devfreq->profile->exit) 7528c2ecf20Sopenharmony_ci devfreq->profile->exit(devfreq->dev.parent); 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci mutex_destroy(&devfreq->lock); 7558c2ecf20Sopenharmony_ci srcu_cleanup_notifier_head(&devfreq->transition_notifier_list); 7568c2ecf20Sopenharmony_ci kfree(devfreq); 7578c2ecf20Sopenharmony_ci} 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci/** 7608c2ecf20Sopenharmony_ci * devfreq_add_device() - Add devfreq feature to the device 7618c2ecf20Sopenharmony_ci * @dev: the device to add devfreq feature. 7628c2ecf20Sopenharmony_ci * @profile: device-specific profile to run devfreq. 7638c2ecf20Sopenharmony_ci * @governor_name: name of the policy to choose frequency. 7648c2ecf20Sopenharmony_ci * @data: devfreq driver pass to governors, governor should not change it. 7658c2ecf20Sopenharmony_ci */ 7668c2ecf20Sopenharmony_cistruct devfreq *devfreq_add_device(struct device *dev, 7678c2ecf20Sopenharmony_ci struct devfreq_dev_profile *profile, 7688c2ecf20Sopenharmony_ci const char *governor_name, 7698c2ecf20Sopenharmony_ci void *data) 7708c2ecf20Sopenharmony_ci{ 7718c2ecf20Sopenharmony_ci struct devfreq *devfreq; 7728c2ecf20Sopenharmony_ci struct devfreq_governor *governor; 7738c2ecf20Sopenharmony_ci int err = 0; 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci if (!dev || !profile || !governor_name) { 7768c2ecf20Sopenharmony_ci dev_err(dev, "%s: Invalid parameters.\n", __func__); 7778c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 7788c2ecf20Sopenharmony_ci } 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci mutex_lock(&devfreq_list_lock); 7818c2ecf20Sopenharmony_ci devfreq = find_device_devfreq(dev); 7828c2ecf20Sopenharmony_ci mutex_unlock(&devfreq_list_lock); 7838c2ecf20Sopenharmony_ci if (!IS_ERR(devfreq)) { 7848c2ecf20Sopenharmony_ci dev_err(dev, "%s: devfreq device already exists!\n", 7858c2ecf20Sopenharmony_ci __func__); 7868c2ecf20Sopenharmony_ci err = -EINVAL; 7878c2ecf20Sopenharmony_ci goto err_out; 7888c2ecf20Sopenharmony_ci } 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci devfreq = kzalloc(sizeof(struct devfreq), GFP_KERNEL); 7918c2ecf20Sopenharmony_ci if (!devfreq) { 7928c2ecf20Sopenharmony_ci err = -ENOMEM; 7938c2ecf20Sopenharmony_ci goto err_out; 7948c2ecf20Sopenharmony_ci } 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci mutex_init(&devfreq->lock); 7978c2ecf20Sopenharmony_ci mutex_lock(&devfreq->lock); 7988c2ecf20Sopenharmony_ci devfreq->dev.parent = dev; 7998c2ecf20Sopenharmony_ci devfreq->dev.class = devfreq_class; 8008c2ecf20Sopenharmony_ci devfreq->dev.release = devfreq_dev_release; 8018c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&devfreq->node); 8028c2ecf20Sopenharmony_ci devfreq->profile = profile; 8038c2ecf20Sopenharmony_ci strscpy(devfreq->governor_name, governor_name, DEVFREQ_NAME_LEN); 8048c2ecf20Sopenharmony_ci devfreq->previous_freq = profile->initial_freq; 8058c2ecf20Sopenharmony_ci devfreq->last_status.current_frequency = profile->initial_freq; 8068c2ecf20Sopenharmony_ci devfreq->data = data; 8078c2ecf20Sopenharmony_ci devfreq->nb.notifier_call = devfreq_notifier_call; 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci if (devfreq->profile->timer < 0 8108c2ecf20Sopenharmony_ci || devfreq->profile->timer >= DEVFREQ_TIMER_NUM) { 8118c2ecf20Sopenharmony_ci mutex_unlock(&devfreq->lock); 8128c2ecf20Sopenharmony_ci err = -EINVAL; 8138c2ecf20Sopenharmony_ci goto err_dev; 8148c2ecf20Sopenharmony_ci } 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci if (!devfreq->profile->max_state && !devfreq->profile->freq_table) { 8178c2ecf20Sopenharmony_ci mutex_unlock(&devfreq->lock); 8188c2ecf20Sopenharmony_ci err = set_freq_table(devfreq); 8198c2ecf20Sopenharmony_ci if (err < 0) 8208c2ecf20Sopenharmony_ci goto err_dev; 8218c2ecf20Sopenharmony_ci mutex_lock(&devfreq->lock); 8228c2ecf20Sopenharmony_ci } 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci devfreq->scaling_min_freq = find_available_min_freq(devfreq); 8258c2ecf20Sopenharmony_ci if (!devfreq->scaling_min_freq) { 8268c2ecf20Sopenharmony_ci mutex_unlock(&devfreq->lock); 8278c2ecf20Sopenharmony_ci err = -EINVAL; 8288c2ecf20Sopenharmony_ci goto err_dev; 8298c2ecf20Sopenharmony_ci } 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci devfreq->scaling_max_freq = find_available_max_freq(devfreq); 8328c2ecf20Sopenharmony_ci if (!devfreq->scaling_max_freq) { 8338c2ecf20Sopenharmony_ci mutex_unlock(&devfreq->lock); 8348c2ecf20Sopenharmony_ci err = -EINVAL; 8358c2ecf20Sopenharmony_ci goto err_dev; 8368c2ecf20Sopenharmony_ci } 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci devfreq->suspend_freq = dev_pm_opp_get_suspend_opp_freq(dev); 8398c2ecf20Sopenharmony_ci atomic_set(&devfreq->suspend_count, 0); 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci dev_set_name(&devfreq->dev, "%s", dev_name(dev)); 8428c2ecf20Sopenharmony_ci err = device_register(&devfreq->dev); 8438c2ecf20Sopenharmony_ci if (err) { 8448c2ecf20Sopenharmony_ci mutex_unlock(&devfreq->lock); 8458c2ecf20Sopenharmony_ci put_device(&devfreq->dev); 8468c2ecf20Sopenharmony_ci goto err_out; 8478c2ecf20Sopenharmony_ci } 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci devfreq->stats.trans_table = devm_kzalloc(&devfreq->dev, 8508c2ecf20Sopenharmony_ci array3_size(sizeof(unsigned int), 8518c2ecf20Sopenharmony_ci devfreq->profile->max_state, 8528c2ecf20Sopenharmony_ci devfreq->profile->max_state), 8538c2ecf20Sopenharmony_ci GFP_KERNEL); 8548c2ecf20Sopenharmony_ci if (!devfreq->stats.trans_table) { 8558c2ecf20Sopenharmony_ci mutex_unlock(&devfreq->lock); 8568c2ecf20Sopenharmony_ci err = -ENOMEM; 8578c2ecf20Sopenharmony_ci goto err_devfreq; 8588c2ecf20Sopenharmony_ci } 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci devfreq->stats.time_in_state = devm_kcalloc(&devfreq->dev, 8618c2ecf20Sopenharmony_ci devfreq->profile->max_state, 8628c2ecf20Sopenharmony_ci sizeof(*devfreq->stats.time_in_state), 8638c2ecf20Sopenharmony_ci GFP_KERNEL); 8648c2ecf20Sopenharmony_ci if (!devfreq->stats.time_in_state) { 8658c2ecf20Sopenharmony_ci mutex_unlock(&devfreq->lock); 8668c2ecf20Sopenharmony_ci err = -ENOMEM; 8678c2ecf20Sopenharmony_ci goto err_devfreq; 8688c2ecf20Sopenharmony_ci } 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci devfreq->stats.total_trans = 0; 8718c2ecf20Sopenharmony_ci devfreq->stats.last_update = get_jiffies_64(); 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci srcu_init_notifier_head(&devfreq->transition_notifier_list); 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci mutex_unlock(&devfreq->lock); 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci err = dev_pm_qos_add_request(dev, &devfreq->user_min_freq_req, 8788c2ecf20Sopenharmony_ci DEV_PM_QOS_MIN_FREQUENCY, 0); 8798c2ecf20Sopenharmony_ci if (err < 0) 8808c2ecf20Sopenharmony_ci goto err_devfreq; 8818c2ecf20Sopenharmony_ci err = dev_pm_qos_add_request(dev, &devfreq->user_max_freq_req, 8828c2ecf20Sopenharmony_ci DEV_PM_QOS_MAX_FREQUENCY, 8838c2ecf20Sopenharmony_ci PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE); 8848c2ecf20Sopenharmony_ci if (err < 0) 8858c2ecf20Sopenharmony_ci goto err_devfreq; 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci devfreq->nb_min.notifier_call = qos_min_notifier_call; 8888c2ecf20Sopenharmony_ci err = dev_pm_qos_add_notifier(devfreq->dev.parent, &devfreq->nb_min, 8898c2ecf20Sopenharmony_ci DEV_PM_QOS_MIN_FREQUENCY); 8908c2ecf20Sopenharmony_ci if (err) 8918c2ecf20Sopenharmony_ci goto err_devfreq; 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci devfreq->nb_max.notifier_call = qos_max_notifier_call; 8948c2ecf20Sopenharmony_ci err = dev_pm_qos_add_notifier(devfreq->dev.parent, &devfreq->nb_max, 8958c2ecf20Sopenharmony_ci DEV_PM_QOS_MAX_FREQUENCY); 8968c2ecf20Sopenharmony_ci if (err) 8978c2ecf20Sopenharmony_ci goto err_devfreq; 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci mutex_lock(&devfreq_list_lock); 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci governor = try_then_request_governor(devfreq->governor_name); 9028c2ecf20Sopenharmony_ci if (IS_ERR(governor)) { 9038c2ecf20Sopenharmony_ci dev_err(dev, "%s: Unable to find governor for the device\n", 9048c2ecf20Sopenharmony_ci __func__); 9058c2ecf20Sopenharmony_ci err = PTR_ERR(governor); 9068c2ecf20Sopenharmony_ci goto err_init; 9078c2ecf20Sopenharmony_ci } 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci devfreq->governor = governor; 9108c2ecf20Sopenharmony_ci err = devfreq->governor->event_handler(devfreq, DEVFREQ_GOV_START, 9118c2ecf20Sopenharmony_ci NULL); 9128c2ecf20Sopenharmony_ci if (err) { 9138c2ecf20Sopenharmony_ci dev_err(dev, "%s: Unable to start governor for the device\n", 9148c2ecf20Sopenharmony_ci __func__); 9158c2ecf20Sopenharmony_ci goto err_init; 9168c2ecf20Sopenharmony_ci } 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci list_add(&devfreq->node, &devfreq_list); 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci mutex_unlock(&devfreq_list_lock); 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci return devfreq; 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_cierr_init: 9258c2ecf20Sopenharmony_ci mutex_unlock(&devfreq_list_lock); 9268c2ecf20Sopenharmony_cierr_devfreq: 9278c2ecf20Sopenharmony_ci devfreq_remove_device(devfreq); 9288c2ecf20Sopenharmony_ci devfreq = NULL; 9298c2ecf20Sopenharmony_cierr_dev: 9308c2ecf20Sopenharmony_ci kfree(devfreq); 9318c2ecf20Sopenharmony_cierr_out: 9328c2ecf20Sopenharmony_ci return ERR_PTR(err); 9338c2ecf20Sopenharmony_ci} 9348c2ecf20Sopenharmony_ciEXPORT_SYMBOL(devfreq_add_device); 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci/** 9378c2ecf20Sopenharmony_ci * devfreq_remove_device() - Remove devfreq feature from a device. 9388c2ecf20Sopenharmony_ci * @devfreq: the devfreq instance to be removed 9398c2ecf20Sopenharmony_ci * 9408c2ecf20Sopenharmony_ci * The opposite of devfreq_add_device(). 9418c2ecf20Sopenharmony_ci */ 9428c2ecf20Sopenharmony_ciint devfreq_remove_device(struct devfreq *devfreq) 9438c2ecf20Sopenharmony_ci{ 9448c2ecf20Sopenharmony_ci if (!devfreq) 9458c2ecf20Sopenharmony_ci return -EINVAL; 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci if (devfreq->governor) 9488c2ecf20Sopenharmony_ci devfreq->governor->event_handler(devfreq, 9498c2ecf20Sopenharmony_ci DEVFREQ_GOV_STOP, NULL); 9508c2ecf20Sopenharmony_ci device_unregister(&devfreq->dev); 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci return 0; 9538c2ecf20Sopenharmony_ci} 9548c2ecf20Sopenharmony_ciEXPORT_SYMBOL(devfreq_remove_device); 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_cistatic int devm_devfreq_dev_match(struct device *dev, void *res, void *data) 9578c2ecf20Sopenharmony_ci{ 9588c2ecf20Sopenharmony_ci struct devfreq **r = res; 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci if (WARN_ON(!r || !*r)) 9618c2ecf20Sopenharmony_ci return 0; 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci return *r == data; 9648c2ecf20Sopenharmony_ci} 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_cistatic void devm_devfreq_dev_release(struct device *dev, void *res) 9678c2ecf20Sopenharmony_ci{ 9688c2ecf20Sopenharmony_ci devfreq_remove_device(*(struct devfreq **)res); 9698c2ecf20Sopenharmony_ci} 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci/** 9728c2ecf20Sopenharmony_ci * devm_devfreq_add_device() - Resource-managed devfreq_add_device() 9738c2ecf20Sopenharmony_ci * @dev: the device to add devfreq feature. 9748c2ecf20Sopenharmony_ci * @profile: device-specific profile to run devfreq. 9758c2ecf20Sopenharmony_ci * @governor_name: name of the policy to choose frequency. 9768c2ecf20Sopenharmony_ci * @data: devfreq driver pass to governors, governor should not change it. 9778c2ecf20Sopenharmony_ci * 9788c2ecf20Sopenharmony_ci * This function manages automatically the memory of devfreq device using device 9798c2ecf20Sopenharmony_ci * resource management and simplify the free operation for memory of devfreq 9808c2ecf20Sopenharmony_ci * device. 9818c2ecf20Sopenharmony_ci */ 9828c2ecf20Sopenharmony_cistruct devfreq *devm_devfreq_add_device(struct device *dev, 9838c2ecf20Sopenharmony_ci struct devfreq_dev_profile *profile, 9848c2ecf20Sopenharmony_ci const char *governor_name, 9858c2ecf20Sopenharmony_ci void *data) 9868c2ecf20Sopenharmony_ci{ 9878c2ecf20Sopenharmony_ci struct devfreq **ptr, *devfreq; 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci ptr = devres_alloc(devm_devfreq_dev_release, sizeof(*ptr), GFP_KERNEL); 9908c2ecf20Sopenharmony_ci if (!ptr) 9918c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci devfreq = devfreq_add_device(dev, profile, governor_name, data); 9948c2ecf20Sopenharmony_ci if (IS_ERR(devfreq)) { 9958c2ecf20Sopenharmony_ci devres_free(ptr); 9968c2ecf20Sopenharmony_ci return devfreq; 9978c2ecf20Sopenharmony_ci } 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci *ptr = devfreq; 10008c2ecf20Sopenharmony_ci devres_add(dev, ptr); 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci return devfreq; 10038c2ecf20Sopenharmony_ci} 10048c2ecf20Sopenharmony_ciEXPORT_SYMBOL(devm_devfreq_add_device); 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 10078c2ecf20Sopenharmony_ci/* 10088c2ecf20Sopenharmony_ci * devfreq_get_devfreq_by_node - Get the devfreq device from devicetree 10098c2ecf20Sopenharmony_ci * @node - pointer to device_node 10108c2ecf20Sopenharmony_ci * 10118c2ecf20Sopenharmony_ci * return the instance of devfreq device 10128c2ecf20Sopenharmony_ci */ 10138c2ecf20Sopenharmony_cistruct devfreq *devfreq_get_devfreq_by_node(struct device_node *node) 10148c2ecf20Sopenharmony_ci{ 10158c2ecf20Sopenharmony_ci struct devfreq *devfreq; 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci if (!node) 10188c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci mutex_lock(&devfreq_list_lock); 10218c2ecf20Sopenharmony_ci list_for_each_entry(devfreq, &devfreq_list, node) { 10228c2ecf20Sopenharmony_ci if (devfreq->dev.parent 10238c2ecf20Sopenharmony_ci && devfreq->dev.parent->of_node == node) { 10248c2ecf20Sopenharmony_ci mutex_unlock(&devfreq_list_lock); 10258c2ecf20Sopenharmony_ci return devfreq; 10268c2ecf20Sopenharmony_ci } 10278c2ecf20Sopenharmony_ci } 10288c2ecf20Sopenharmony_ci mutex_unlock(&devfreq_list_lock); 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 10318c2ecf20Sopenharmony_ci} 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci/* 10348c2ecf20Sopenharmony_ci * devfreq_get_devfreq_by_phandle - Get the devfreq device from devicetree 10358c2ecf20Sopenharmony_ci * @dev - instance to the given device 10368c2ecf20Sopenharmony_ci * @phandle_name - name of property holding a phandle value 10378c2ecf20Sopenharmony_ci * @index - index into list of devfreq 10388c2ecf20Sopenharmony_ci * 10398c2ecf20Sopenharmony_ci * return the instance of devfreq device 10408c2ecf20Sopenharmony_ci */ 10418c2ecf20Sopenharmony_cistruct devfreq *devfreq_get_devfreq_by_phandle(struct device *dev, 10428c2ecf20Sopenharmony_ci const char *phandle_name, int index) 10438c2ecf20Sopenharmony_ci{ 10448c2ecf20Sopenharmony_ci struct device_node *node; 10458c2ecf20Sopenharmony_ci struct devfreq *devfreq; 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_ci if (!dev || !phandle_name) 10488c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ci if (!dev->of_node) 10518c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci node = of_parse_phandle(dev->of_node, phandle_name, index); 10548c2ecf20Sopenharmony_ci if (!node) 10558c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci devfreq = devfreq_get_devfreq_by_node(node); 10588c2ecf20Sopenharmony_ci of_node_put(node); 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci return devfreq; 10618c2ecf20Sopenharmony_ci} 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ci#else 10648c2ecf20Sopenharmony_cistruct devfreq *devfreq_get_devfreq_by_node(struct device_node *node) 10658c2ecf20Sopenharmony_ci{ 10668c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 10678c2ecf20Sopenharmony_ci} 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_cistruct devfreq *devfreq_get_devfreq_by_phandle(struct device *dev, 10708c2ecf20Sopenharmony_ci const char *phandle_name, int index) 10718c2ecf20Sopenharmony_ci{ 10728c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 10738c2ecf20Sopenharmony_ci} 10748c2ecf20Sopenharmony_ci#endif /* CONFIG_OF */ 10758c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(devfreq_get_devfreq_by_node); 10768c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(devfreq_get_devfreq_by_phandle); 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci/** 10798c2ecf20Sopenharmony_ci * devm_devfreq_remove_device() - Resource-managed devfreq_remove_device() 10808c2ecf20Sopenharmony_ci * @dev: the device from which to remove devfreq feature. 10818c2ecf20Sopenharmony_ci * @devfreq: the devfreq instance to be removed 10828c2ecf20Sopenharmony_ci */ 10838c2ecf20Sopenharmony_civoid devm_devfreq_remove_device(struct device *dev, struct devfreq *devfreq) 10848c2ecf20Sopenharmony_ci{ 10858c2ecf20Sopenharmony_ci WARN_ON(devres_release(dev, devm_devfreq_dev_release, 10868c2ecf20Sopenharmony_ci devm_devfreq_dev_match, devfreq)); 10878c2ecf20Sopenharmony_ci} 10888c2ecf20Sopenharmony_ciEXPORT_SYMBOL(devm_devfreq_remove_device); 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_ci/** 10918c2ecf20Sopenharmony_ci * devfreq_suspend_device() - Suspend devfreq of a device. 10928c2ecf20Sopenharmony_ci * @devfreq: the devfreq instance to be suspended 10938c2ecf20Sopenharmony_ci * 10948c2ecf20Sopenharmony_ci * This function is intended to be called by the pm callbacks 10958c2ecf20Sopenharmony_ci * (e.g., runtime_suspend, suspend) of the device driver that 10968c2ecf20Sopenharmony_ci * holds the devfreq. 10978c2ecf20Sopenharmony_ci */ 10988c2ecf20Sopenharmony_ciint devfreq_suspend_device(struct devfreq *devfreq) 10998c2ecf20Sopenharmony_ci{ 11008c2ecf20Sopenharmony_ci int ret; 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_ci if (!devfreq) 11038c2ecf20Sopenharmony_ci return -EINVAL; 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci if (atomic_inc_return(&devfreq->suspend_count) > 1) 11068c2ecf20Sopenharmony_ci return 0; 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci if (devfreq->governor) { 11098c2ecf20Sopenharmony_ci ret = devfreq->governor->event_handler(devfreq, 11108c2ecf20Sopenharmony_ci DEVFREQ_GOV_SUSPEND, NULL); 11118c2ecf20Sopenharmony_ci if (ret) 11128c2ecf20Sopenharmony_ci return ret; 11138c2ecf20Sopenharmony_ci } 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_ci if (devfreq->suspend_freq) { 11168c2ecf20Sopenharmony_ci mutex_lock(&devfreq->lock); 11178c2ecf20Sopenharmony_ci ret = devfreq_set_target(devfreq, devfreq->suspend_freq, 0); 11188c2ecf20Sopenharmony_ci mutex_unlock(&devfreq->lock); 11198c2ecf20Sopenharmony_ci if (ret) 11208c2ecf20Sopenharmony_ci return ret; 11218c2ecf20Sopenharmony_ci } 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_ci return 0; 11248c2ecf20Sopenharmony_ci} 11258c2ecf20Sopenharmony_ciEXPORT_SYMBOL(devfreq_suspend_device); 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_ci/** 11288c2ecf20Sopenharmony_ci * devfreq_resume_device() - Resume devfreq of a device. 11298c2ecf20Sopenharmony_ci * @devfreq: the devfreq instance to be resumed 11308c2ecf20Sopenharmony_ci * 11318c2ecf20Sopenharmony_ci * This function is intended to be called by the pm callbacks 11328c2ecf20Sopenharmony_ci * (e.g., runtime_resume, resume) of the device driver that 11338c2ecf20Sopenharmony_ci * holds the devfreq. 11348c2ecf20Sopenharmony_ci */ 11358c2ecf20Sopenharmony_ciint devfreq_resume_device(struct devfreq *devfreq) 11368c2ecf20Sopenharmony_ci{ 11378c2ecf20Sopenharmony_ci int ret; 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_ci if (!devfreq) 11408c2ecf20Sopenharmony_ci return -EINVAL; 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci if (atomic_dec_return(&devfreq->suspend_count) >= 1) 11438c2ecf20Sopenharmony_ci return 0; 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_ci if (devfreq->resume_freq) { 11468c2ecf20Sopenharmony_ci mutex_lock(&devfreq->lock); 11478c2ecf20Sopenharmony_ci ret = devfreq_set_target(devfreq, devfreq->resume_freq, 0); 11488c2ecf20Sopenharmony_ci mutex_unlock(&devfreq->lock); 11498c2ecf20Sopenharmony_ci if (ret) 11508c2ecf20Sopenharmony_ci return ret; 11518c2ecf20Sopenharmony_ci } 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_ci if (devfreq->governor) { 11548c2ecf20Sopenharmony_ci ret = devfreq->governor->event_handler(devfreq, 11558c2ecf20Sopenharmony_ci DEVFREQ_GOV_RESUME, NULL); 11568c2ecf20Sopenharmony_ci if (ret) 11578c2ecf20Sopenharmony_ci return ret; 11588c2ecf20Sopenharmony_ci } 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_ci return 0; 11618c2ecf20Sopenharmony_ci} 11628c2ecf20Sopenharmony_ciEXPORT_SYMBOL(devfreq_resume_device); 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci/** 11658c2ecf20Sopenharmony_ci * devfreq_suspend() - Suspend devfreq governors and devices 11668c2ecf20Sopenharmony_ci * 11678c2ecf20Sopenharmony_ci * Called during system wide Suspend/Hibernate cycles for suspending governors 11688c2ecf20Sopenharmony_ci * and devices preserving the state for resume. On some platforms the devfreq 11698c2ecf20Sopenharmony_ci * device must have precise state (frequency) after resume in order to provide 11708c2ecf20Sopenharmony_ci * fully operating setup. 11718c2ecf20Sopenharmony_ci */ 11728c2ecf20Sopenharmony_civoid devfreq_suspend(void) 11738c2ecf20Sopenharmony_ci{ 11748c2ecf20Sopenharmony_ci struct devfreq *devfreq; 11758c2ecf20Sopenharmony_ci int ret; 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci mutex_lock(&devfreq_list_lock); 11788c2ecf20Sopenharmony_ci list_for_each_entry(devfreq, &devfreq_list, node) { 11798c2ecf20Sopenharmony_ci ret = devfreq_suspend_device(devfreq); 11808c2ecf20Sopenharmony_ci if (ret) 11818c2ecf20Sopenharmony_ci dev_err(&devfreq->dev, 11828c2ecf20Sopenharmony_ci "failed to suspend devfreq device\n"); 11838c2ecf20Sopenharmony_ci } 11848c2ecf20Sopenharmony_ci mutex_unlock(&devfreq_list_lock); 11858c2ecf20Sopenharmony_ci} 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_ci/** 11888c2ecf20Sopenharmony_ci * devfreq_resume() - Resume devfreq governors and devices 11898c2ecf20Sopenharmony_ci * 11908c2ecf20Sopenharmony_ci * Called during system wide Suspend/Hibernate cycle for resuming governors and 11918c2ecf20Sopenharmony_ci * devices that are suspended with devfreq_suspend(). 11928c2ecf20Sopenharmony_ci */ 11938c2ecf20Sopenharmony_civoid devfreq_resume(void) 11948c2ecf20Sopenharmony_ci{ 11958c2ecf20Sopenharmony_ci struct devfreq *devfreq; 11968c2ecf20Sopenharmony_ci int ret; 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_ci mutex_lock(&devfreq_list_lock); 11998c2ecf20Sopenharmony_ci list_for_each_entry(devfreq, &devfreq_list, node) { 12008c2ecf20Sopenharmony_ci ret = devfreq_resume_device(devfreq); 12018c2ecf20Sopenharmony_ci if (ret) 12028c2ecf20Sopenharmony_ci dev_warn(&devfreq->dev, 12038c2ecf20Sopenharmony_ci "failed to resume devfreq device\n"); 12048c2ecf20Sopenharmony_ci } 12058c2ecf20Sopenharmony_ci mutex_unlock(&devfreq_list_lock); 12068c2ecf20Sopenharmony_ci} 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci/** 12098c2ecf20Sopenharmony_ci * devfreq_add_governor() - Add devfreq governor 12108c2ecf20Sopenharmony_ci * @governor: the devfreq governor to be added 12118c2ecf20Sopenharmony_ci */ 12128c2ecf20Sopenharmony_ciint devfreq_add_governor(struct devfreq_governor *governor) 12138c2ecf20Sopenharmony_ci{ 12148c2ecf20Sopenharmony_ci struct devfreq_governor *g; 12158c2ecf20Sopenharmony_ci struct devfreq *devfreq; 12168c2ecf20Sopenharmony_ci int err = 0; 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci if (!governor) { 12198c2ecf20Sopenharmony_ci pr_err("%s: Invalid parameters.\n", __func__); 12208c2ecf20Sopenharmony_ci return -EINVAL; 12218c2ecf20Sopenharmony_ci } 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ci mutex_lock(&devfreq_list_lock); 12248c2ecf20Sopenharmony_ci g = find_devfreq_governor(governor->name); 12258c2ecf20Sopenharmony_ci if (!IS_ERR(g)) { 12268c2ecf20Sopenharmony_ci pr_err("%s: governor %s already registered\n", __func__, 12278c2ecf20Sopenharmony_ci g->name); 12288c2ecf20Sopenharmony_ci err = -EINVAL; 12298c2ecf20Sopenharmony_ci goto err_out; 12308c2ecf20Sopenharmony_ci } 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ci list_add(&governor->node, &devfreq_governor_list); 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_ci list_for_each_entry(devfreq, &devfreq_list, node) { 12358c2ecf20Sopenharmony_ci int ret = 0; 12368c2ecf20Sopenharmony_ci struct device *dev = devfreq->dev.parent; 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_ci if (!strncmp(devfreq->governor_name, governor->name, 12398c2ecf20Sopenharmony_ci DEVFREQ_NAME_LEN)) { 12408c2ecf20Sopenharmony_ci /* The following should never occur */ 12418c2ecf20Sopenharmony_ci if (devfreq->governor) { 12428c2ecf20Sopenharmony_ci dev_warn(dev, 12438c2ecf20Sopenharmony_ci "%s: Governor %s already present\n", 12448c2ecf20Sopenharmony_ci __func__, devfreq->governor->name); 12458c2ecf20Sopenharmony_ci ret = devfreq->governor->event_handler(devfreq, 12468c2ecf20Sopenharmony_ci DEVFREQ_GOV_STOP, NULL); 12478c2ecf20Sopenharmony_ci if (ret) { 12488c2ecf20Sopenharmony_ci dev_warn(dev, 12498c2ecf20Sopenharmony_ci "%s: Governor %s stop = %d\n", 12508c2ecf20Sopenharmony_ci __func__, 12518c2ecf20Sopenharmony_ci devfreq->governor->name, ret); 12528c2ecf20Sopenharmony_ci } 12538c2ecf20Sopenharmony_ci /* Fall through */ 12548c2ecf20Sopenharmony_ci } 12558c2ecf20Sopenharmony_ci devfreq->governor = governor; 12568c2ecf20Sopenharmony_ci ret = devfreq->governor->event_handler(devfreq, 12578c2ecf20Sopenharmony_ci DEVFREQ_GOV_START, NULL); 12588c2ecf20Sopenharmony_ci if (ret) { 12598c2ecf20Sopenharmony_ci dev_warn(dev, "%s: Governor %s start=%d\n", 12608c2ecf20Sopenharmony_ci __func__, devfreq->governor->name, 12618c2ecf20Sopenharmony_ci ret); 12628c2ecf20Sopenharmony_ci } 12638c2ecf20Sopenharmony_ci } 12648c2ecf20Sopenharmony_ci } 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_cierr_out: 12678c2ecf20Sopenharmony_ci mutex_unlock(&devfreq_list_lock); 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_ci return err; 12708c2ecf20Sopenharmony_ci} 12718c2ecf20Sopenharmony_ciEXPORT_SYMBOL(devfreq_add_governor); 12728c2ecf20Sopenharmony_ci 12738c2ecf20Sopenharmony_ci/** 12748c2ecf20Sopenharmony_ci * devfreq_remove_governor() - Remove devfreq feature from a device. 12758c2ecf20Sopenharmony_ci * @governor: the devfreq governor to be removed 12768c2ecf20Sopenharmony_ci */ 12778c2ecf20Sopenharmony_ciint devfreq_remove_governor(struct devfreq_governor *governor) 12788c2ecf20Sopenharmony_ci{ 12798c2ecf20Sopenharmony_ci struct devfreq_governor *g; 12808c2ecf20Sopenharmony_ci struct devfreq *devfreq; 12818c2ecf20Sopenharmony_ci int err = 0; 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_ci if (!governor) { 12848c2ecf20Sopenharmony_ci pr_err("%s: Invalid parameters.\n", __func__); 12858c2ecf20Sopenharmony_ci return -EINVAL; 12868c2ecf20Sopenharmony_ci } 12878c2ecf20Sopenharmony_ci 12888c2ecf20Sopenharmony_ci mutex_lock(&devfreq_list_lock); 12898c2ecf20Sopenharmony_ci g = find_devfreq_governor(governor->name); 12908c2ecf20Sopenharmony_ci if (IS_ERR(g)) { 12918c2ecf20Sopenharmony_ci pr_err("%s: governor %s not registered\n", __func__, 12928c2ecf20Sopenharmony_ci governor->name); 12938c2ecf20Sopenharmony_ci err = PTR_ERR(g); 12948c2ecf20Sopenharmony_ci goto err_out; 12958c2ecf20Sopenharmony_ci } 12968c2ecf20Sopenharmony_ci list_for_each_entry(devfreq, &devfreq_list, node) { 12978c2ecf20Sopenharmony_ci int ret; 12988c2ecf20Sopenharmony_ci struct device *dev = devfreq->dev.parent; 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_ci if (!strncmp(devfreq->governor_name, governor->name, 13018c2ecf20Sopenharmony_ci DEVFREQ_NAME_LEN)) { 13028c2ecf20Sopenharmony_ci /* we should have a devfreq governor! */ 13038c2ecf20Sopenharmony_ci if (!devfreq->governor) { 13048c2ecf20Sopenharmony_ci dev_warn(dev, "%s: Governor %s NOT present\n", 13058c2ecf20Sopenharmony_ci __func__, governor->name); 13068c2ecf20Sopenharmony_ci continue; 13078c2ecf20Sopenharmony_ci /* Fall through */ 13088c2ecf20Sopenharmony_ci } 13098c2ecf20Sopenharmony_ci ret = devfreq->governor->event_handler(devfreq, 13108c2ecf20Sopenharmony_ci DEVFREQ_GOV_STOP, NULL); 13118c2ecf20Sopenharmony_ci if (ret) { 13128c2ecf20Sopenharmony_ci dev_warn(dev, "%s: Governor %s stop=%d\n", 13138c2ecf20Sopenharmony_ci __func__, devfreq->governor->name, 13148c2ecf20Sopenharmony_ci ret); 13158c2ecf20Sopenharmony_ci } 13168c2ecf20Sopenharmony_ci devfreq->governor = NULL; 13178c2ecf20Sopenharmony_ci } 13188c2ecf20Sopenharmony_ci } 13198c2ecf20Sopenharmony_ci 13208c2ecf20Sopenharmony_ci list_del(&governor->node); 13218c2ecf20Sopenharmony_cierr_out: 13228c2ecf20Sopenharmony_ci mutex_unlock(&devfreq_list_lock); 13238c2ecf20Sopenharmony_ci 13248c2ecf20Sopenharmony_ci return err; 13258c2ecf20Sopenharmony_ci} 13268c2ecf20Sopenharmony_ciEXPORT_SYMBOL(devfreq_remove_governor); 13278c2ecf20Sopenharmony_ci 13288c2ecf20Sopenharmony_cistatic ssize_t name_show(struct device *dev, 13298c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 13308c2ecf20Sopenharmony_ci{ 13318c2ecf20Sopenharmony_ci struct devfreq *df = to_devfreq(dev); 13328c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", dev_name(df->dev.parent)); 13338c2ecf20Sopenharmony_ci} 13348c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(name); 13358c2ecf20Sopenharmony_ci 13368c2ecf20Sopenharmony_cistatic ssize_t governor_show(struct device *dev, 13378c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 13388c2ecf20Sopenharmony_ci{ 13398c2ecf20Sopenharmony_ci struct devfreq *df = to_devfreq(dev); 13408c2ecf20Sopenharmony_ci 13418c2ecf20Sopenharmony_ci if (!df->governor) 13428c2ecf20Sopenharmony_ci return -EINVAL; 13438c2ecf20Sopenharmony_ci 13448c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", df->governor->name); 13458c2ecf20Sopenharmony_ci} 13468c2ecf20Sopenharmony_ci 13478c2ecf20Sopenharmony_cistatic ssize_t governor_store(struct device *dev, struct device_attribute *attr, 13488c2ecf20Sopenharmony_ci const char *buf, size_t count) 13498c2ecf20Sopenharmony_ci{ 13508c2ecf20Sopenharmony_ci struct devfreq *df = to_devfreq(dev); 13518c2ecf20Sopenharmony_ci int ret; 13528c2ecf20Sopenharmony_ci char str_governor[DEVFREQ_NAME_LEN + 1]; 13538c2ecf20Sopenharmony_ci const struct devfreq_governor *governor, *prev_governor; 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_ci if (!df->governor) 13568c2ecf20Sopenharmony_ci return -EINVAL; 13578c2ecf20Sopenharmony_ci 13588c2ecf20Sopenharmony_ci ret = sscanf(buf, "%" __stringify(DEVFREQ_NAME_LEN) "s", str_governor); 13598c2ecf20Sopenharmony_ci if (ret != 1) 13608c2ecf20Sopenharmony_ci return -EINVAL; 13618c2ecf20Sopenharmony_ci 13628c2ecf20Sopenharmony_ci mutex_lock(&devfreq_list_lock); 13638c2ecf20Sopenharmony_ci governor = try_then_request_governor(str_governor); 13648c2ecf20Sopenharmony_ci if (IS_ERR(governor)) { 13658c2ecf20Sopenharmony_ci ret = PTR_ERR(governor); 13668c2ecf20Sopenharmony_ci goto out; 13678c2ecf20Sopenharmony_ci } 13688c2ecf20Sopenharmony_ci if (df->governor == governor) { 13698c2ecf20Sopenharmony_ci ret = 0; 13708c2ecf20Sopenharmony_ci goto out; 13718c2ecf20Sopenharmony_ci } else if (df->governor->immutable || governor->immutable) { 13728c2ecf20Sopenharmony_ci ret = -EINVAL; 13738c2ecf20Sopenharmony_ci goto out; 13748c2ecf20Sopenharmony_ci } 13758c2ecf20Sopenharmony_ci 13768c2ecf20Sopenharmony_ci ret = df->governor->event_handler(df, DEVFREQ_GOV_STOP, NULL); 13778c2ecf20Sopenharmony_ci if (ret) { 13788c2ecf20Sopenharmony_ci dev_warn(dev, "%s: Governor %s not stopped(%d)\n", 13798c2ecf20Sopenharmony_ci __func__, df->governor->name, ret); 13808c2ecf20Sopenharmony_ci goto out; 13818c2ecf20Sopenharmony_ci } 13828c2ecf20Sopenharmony_ci 13838c2ecf20Sopenharmony_ci prev_governor = df->governor; 13848c2ecf20Sopenharmony_ci df->governor = governor; 13858c2ecf20Sopenharmony_ci strncpy(df->governor_name, governor->name, DEVFREQ_NAME_LEN); 13868c2ecf20Sopenharmony_ci ret = df->governor->event_handler(df, DEVFREQ_GOV_START, NULL); 13878c2ecf20Sopenharmony_ci if (ret) { 13888c2ecf20Sopenharmony_ci dev_warn(dev, "%s: Governor %s not started(%d)\n", 13898c2ecf20Sopenharmony_ci __func__, df->governor->name, ret); 13908c2ecf20Sopenharmony_ci df->governor = prev_governor; 13918c2ecf20Sopenharmony_ci strncpy(df->governor_name, prev_governor->name, 13928c2ecf20Sopenharmony_ci DEVFREQ_NAME_LEN); 13938c2ecf20Sopenharmony_ci ret = df->governor->event_handler(df, DEVFREQ_GOV_START, NULL); 13948c2ecf20Sopenharmony_ci if (ret) { 13958c2ecf20Sopenharmony_ci dev_err(dev, 13968c2ecf20Sopenharmony_ci "%s: reverting to Governor %s failed (%d)\n", 13978c2ecf20Sopenharmony_ci __func__, df->governor_name, ret); 13988c2ecf20Sopenharmony_ci df->governor = NULL; 13998c2ecf20Sopenharmony_ci } 14008c2ecf20Sopenharmony_ci } 14018c2ecf20Sopenharmony_ciout: 14028c2ecf20Sopenharmony_ci mutex_unlock(&devfreq_list_lock); 14038c2ecf20Sopenharmony_ci 14048c2ecf20Sopenharmony_ci if (!ret) 14058c2ecf20Sopenharmony_ci ret = count; 14068c2ecf20Sopenharmony_ci return ret; 14078c2ecf20Sopenharmony_ci} 14088c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(governor); 14098c2ecf20Sopenharmony_ci 14108c2ecf20Sopenharmony_cistatic ssize_t available_governors_show(struct device *d, 14118c2ecf20Sopenharmony_ci struct device_attribute *attr, 14128c2ecf20Sopenharmony_ci char *buf) 14138c2ecf20Sopenharmony_ci{ 14148c2ecf20Sopenharmony_ci struct devfreq *df = to_devfreq(d); 14158c2ecf20Sopenharmony_ci ssize_t count = 0; 14168c2ecf20Sopenharmony_ci 14178c2ecf20Sopenharmony_ci if (!df->governor) 14188c2ecf20Sopenharmony_ci return -EINVAL; 14198c2ecf20Sopenharmony_ci 14208c2ecf20Sopenharmony_ci mutex_lock(&devfreq_list_lock); 14218c2ecf20Sopenharmony_ci 14228c2ecf20Sopenharmony_ci /* 14238c2ecf20Sopenharmony_ci * The devfreq with immutable governor (e.g., passive) shows 14248c2ecf20Sopenharmony_ci * only own governor. 14258c2ecf20Sopenharmony_ci */ 14268c2ecf20Sopenharmony_ci if (df->governor->immutable) { 14278c2ecf20Sopenharmony_ci count = scnprintf(&buf[count], DEVFREQ_NAME_LEN, 14288c2ecf20Sopenharmony_ci "%s ", df->governor_name); 14298c2ecf20Sopenharmony_ci /* 14308c2ecf20Sopenharmony_ci * The devfreq device shows the registered governor except for 14318c2ecf20Sopenharmony_ci * immutable governors such as passive governor . 14328c2ecf20Sopenharmony_ci */ 14338c2ecf20Sopenharmony_ci } else { 14348c2ecf20Sopenharmony_ci struct devfreq_governor *governor; 14358c2ecf20Sopenharmony_ci 14368c2ecf20Sopenharmony_ci list_for_each_entry(governor, &devfreq_governor_list, node) { 14378c2ecf20Sopenharmony_ci if (governor->immutable) 14388c2ecf20Sopenharmony_ci continue; 14398c2ecf20Sopenharmony_ci count += scnprintf(&buf[count], (PAGE_SIZE - count - 2), 14408c2ecf20Sopenharmony_ci "%s ", governor->name); 14418c2ecf20Sopenharmony_ci } 14428c2ecf20Sopenharmony_ci } 14438c2ecf20Sopenharmony_ci 14448c2ecf20Sopenharmony_ci mutex_unlock(&devfreq_list_lock); 14458c2ecf20Sopenharmony_ci 14468c2ecf20Sopenharmony_ci /* Truncate the trailing space */ 14478c2ecf20Sopenharmony_ci if (count) 14488c2ecf20Sopenharmony_ci count--; 14498c2ecf20Sopenharmony_ci 14508c2ecf20Sopenharmony_ci count += sprintf(&buf[count], "\n"); 14518c2ecf20Sopenharmony_ci 14528c2ecf20Sopenharmony_ci return count; 14538c2ecf20Sopenharmony_ci} 14548c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(available_governors); 14558c2ecf20Sopenharmony_ci 14568c2ecf20Sopenharmony_cistatic ssize_t cur_freq_show(struct device *dev, struct device_attribute *attr, 14578c2ecf20Sopenharmony_ci char *buf) 14588c2ecf20Sopenharmony_ci{ 14598c2ecf20Sopenharmony_ci unsigned long freq; 14608c2ecf20Sopenharmony_ci struct devfreq *df = to_devfreq(dev); 14618c2ecf20Sopenharmony_ci 14628c2ecf20Sopenharmony_ci if (!df->profile) 14638c2ecf20Sopenharmony_ci return -EINVAL; 14648c2ecf20Sopenharmony_ci 14658c2ecf20Sopenharmony_ci if (df->profile->get_cur_freq && 14668c2ecf20Sopenharmony_ci !df->profile->get_cur_freq(df->dev.parent, &freq)) 14678c2ecf20Sopenharmony_ci return sprintf(buf, "%lu\n", freq); 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_ci return sprintf(buf, "%lu\n", df->previous_freq); 14708c2ecf20Sopenharmony_ci} 14718c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(cur_freq); 14728c2ecf20Sopenharmony_ci 14738c2ecf20Sopenharmony_cistatic ssize_t target_freq_show(struct device *dev, 14748c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 14758c2ecf20Sopenharmony_ci{ 14768c2ecf20Sopenharmony_ci struct devfreq *df = to_devfreq(dev); 14778c2ecf20Sopenharmony_ci 14788c2ecf20Sopenharmony_ci return sprintf(buf, "%lu\n", df->previous_freq); 14798c2ecf20Sopenharmony_ci} 14808c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(target_freq); 14818c2ecf20Sopenharmony_ci 14828c2ecf20Sopenharmony_cistatic ssize_t polling_interval_show(struct device *dev, 14838c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 14848c2ecf20Sopenharmony_ci{ 14858c2ecf20Sopenharmony_ci struct devfreq *df = to_devfreq(dev); 14868c2ecf20Sopenharmony_ci 14878c2ecf20Sopenharmony_ci if (!df->profile) 14888c2ecf20Sopenharmony_ci return -EINVAL; 14898c2ecf20Sopenharmony_ci 14908c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", df->profile->polling_ms); 14918c2ecf20Sopenharmony_ci} 14928c2ecf20Sopenharmony_ci 14938c2ecf20Sopenharmony_cistatic ssize_t polling_interval_store(struct device *dev, 14948c2ecf20Sopenharmony_ci struct device_attribute *attr, 14958c2ecf20Sopenharmony_ci const char *buf, size_t count) 14968c2ecf20Sopenharmony_ci{ 14978c2ecf20Sopenharmony_ci struct devfreq *df = to_devfreq(dev); 14988c2ecf20Sopenharmony_ci unsigned int value; 14998c2ecf20Sopenharmony_ci int ret; 15008c2ecf20Sopenharmony_ci 15018c2ecf20Sopenharmony_ci if (!df->governor) 15028c2ecf20Sopenharmony_ci return -EINVAL; 15038c2ecf20Sopenharmony_ci 15048c2ecf20Sopenharmony_ci ret = sscanf(buf, "%u", &value); 15058c2ecf20Sopenharmony_ci if (ret != 1) 15068c2ecf20Sopenharmony_ci return -EINVAL; 15078c2ecf20Sopenharmony_ci 15088c2ecf20Sopenharmony_ci df->governor->event_handler(df, DEVFREQ_GOV_UPDATE_INTERVAL, &value); 15098c2ecf20Sopenharmony_ci ret = count; 15108c2ecf20Sopenharmony_ci 15118c2ecf20Sopenharmony_ci return ret; 15128c2ecf20Sopenharmony_ci} 15138c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(polling_interval); 15148c2ecf20Sopenharmony_ci 15158c2ecf20Sopenharmony_cistatic ssize_t min_freq_store(struct device *dev, struct device_attribute *attr, 15168c2ecf20Sopenharmony_ci const char *buf, size_t count) 15178c2ecf20Sopenharmony_ci{ 15188c2ecf20Sopenharmony_ci struct devfreq *df = to_devfreq(dev); 15198c2ecf20Sopenharmony_ci unsigned long value; 15208c2ecf20Sopenharmony_ci int ret; 15218c2ecf20Sopenharmony_ci 15228c2ecf20Sopenharmony_ci /* 15238c2ecf20Sopenharmony_ci * Protect against theoretical sysfs writes between 15248c2ecf20Sopenharmony_ci * device_add and dev_pm_qos_add_request 15258c2ecf20Sopenharmony_ci */ 15268c2ecf20Sopenharmony_ci if (!dev_pm_qos_request_active(&df->user_min_freq_req)) 15278c2ecf20Sopenharmony_ci return -EAGAIN; 15288c2ecf20Sopenharmony_ci 15298c2ecf20Sopenharmony_ci ret = sscanf(buf, "%lu", &value); 15308c2ecf20Sopenharmony_ci if (ret != 1) 15318c2ecf20Sopenharmony_ci return -EINVAL; 15328c2ecf20Sopenharmony_ci 15338c2ecf20Sopenharmony_ci /* Round down to kHz for PM QoS */ 15348c2ecf20Sopenharmony_ci ret = dev_pm_qos_update_request(&df->user_min_freq_req, 15358c2ecf20Sopenharmony_ci value / HZ_PER_KHZ); 15368c2ecf20Sopenharmony_ci if (ret < 0) 15378c2ecf20Sopenharmony_ci return ret; 15388c2ecf20Sopenharmony_ci 15398c2ecf20Sopenharmony_ci return count; 15408c2ecf20Sopenharmony_ci} 15418c2ecf20Sopenharmony_ci 15428c2ecf20Sopenharmony_cistatic ssize_t min_freq_show(struct device *dev, struct device_attribute *attr, 15438c2ecf20Sopenharmony_ci char *buf) 15448c2ecf20Sopenharmony_ci{ 15458c2ecf20Sopenharmony_ci struct devfreq *df = to_devfreq(dev); 15468c2ecf20Sopenharmony_ci unsigned long min_freq, max_freq; 15478c2ecf20Sopenharmony_ci 15488c2ecf20Sopenharmony_ci mutex_lock(&df->lock); 15498c2ecf20Sopenharmony_ci get_freq_range(df, &min_freq, &max_freq); 15508c2ecf20Sopenharmony_ci mutex_unlock(&df->lock); 15518c2ecf20Sopenharmony_ci 15528c2ecf20Sopenharmony_ci return sprintf(buf, "%lu\n", min_freq); 15538c2ecf20Sopenharmony_ci} 15548c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(min_freq); 15558c2ecf20Sopenharmony_ci 15568c2ecf20Sopenharmony_cistatic ssize_t max_freq_store(struct device *dev, struct device_attribute *attr, 15578c2ecf20Sopenharmony_ci const char *buf, size_t count) 15588c2ecf20Sopenharmony_ci{ 15598c2ecf20Sopenharmony_ci struct devfreq *df = to_devfreq(dev); 15608c2ecf20Sopenharmony_ci unsigned long value; 15618c2ecf20Sopenharmony_ci int ret; 15628c2ecf20Sopenharmony_ci 15638c2ecf20Sopenharmony_ci /* 15648c2ecf20Sopenharmony_ci * Protect against theoretical sysfs writes between 15658c2ecf20Sopenharmony_ci * device_add and dev_pm_qos_add_request 15668c2ecf20Sopenharmony_ci */ 15678c2ecf20Sopenharmony_ci if (!dev_pm_qos_request_active(&df->user_max_freq_req)) 15688c2ecf20Sopenharmony_ci return -EINVAL; 15698c2ecf20Sopenharmony_ci 15708c2ecf20Sopenharmony_ci ret = sscanf(buf, "%lu", &value); 15718c2ecf20Sopenharmony_ci if (ret != 1) 15728c2ecf20Sopenharmony_ci return -EINVAL; 15738c2ecf20Sopenharmony_ci 15748c2ecf20Sopenharmony_ci /* 15758c2ecf20Sopenharmony_ci * PM QoS frequencies are in kHz so we need to convert. Convert by 15768c2ecf20Sopenharmony_ci * rounding upwards so that the acceptable interval never shrinks. 15778c2ecf20Sopenharmony_ci * 15788c2ecf20Sopenharmony_ci * For example if the user writes "666666666" to sysfs this value will 15798c2ecf20Sopenharmony_ci * be converted to 666667 kHz and back to 666667000 Hz before an OPP 15808c2ecf20Sopenharmony_ci * lookup, this ensures that an OPP of 666666666Hz is still accepted. 15818c2ecf20Sopenharmony_ci * 15828c2ecf20Sopenharmony_ci * A value of zero means "no limit". 15838c2ecf20Sopenharmony_ci */ 15848c2ecf20Sopenharmony_ci if (value) 15858c2ecf20Sopenharmony_ci value = DIV_ROUND_UP(value, HZ_PER_KHZ); 15868c2ecf20Sopenharmony_ci else 15878c2ecf20Sopenharmony_ci value = PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE; 15888c2ecf20Sopenharmony_ci 15898c2ecf20Sopenharmony_ci ret = dev_pm_qos_update_request(&df->user_max_freq_req, value); 15908c2ecf20Sopenharmony_ci if (ret < 0) 15918c2ecf20Sopenharmony_ci return ret; 15928c2ecf20Sopenharmony_ci 15938c2ecf20Sopenharmony_ci return count; 15948c2ecf20Sopenharmony_ci} 15958c2ecf20Sopenharmony_ci 15968c2ecf20Sopenharmony_cistatic ssize_t max_freq_show(struct device *dev, struct device_attribute *attr, 15978c2ecf20Sopenharmony_ci char *buf) 15988c2ecf20Sopenharmony_ci{ 15998c2ecf20Sopenharmony_ci struct devfreq *df = to_devfreq(dev); 16008c2ecf20Sopenharmony_ci unsigned long min_freq, max_freq; 16018c2ecf20Sopenharmony_ci 16028c2ecf20Sopenharmony_ci mutex_lock(&df->lock); 16038c2ecf20Sopenharmony_ci get_freq_range(df, &min_freq, &max_freq); 16048c2ecf20Sopenharmony_ci mutex_unlock(&df->lock); 16058c2ecf20Sopenharmony_ci 16068c2ecf20Sopenharmony_ci return sprintf(buf, "%lu\n", max_freq); 16078c2ecf20Sopenharmony_ci} 16088c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(max_freq); 16098c2ecf20Sopenharmony_ci 16108c2ecf20Sopenharmony_cistatic ssize_t available_frequencies_show(struct device *d, 16118c2ecf20Sopenharmony_ci struct device_attribute *attr, 16128c2ecf20Sopenharmony_ci char *buf) 16138c2ecf20Sopenharmony_ci{ 16148c2ecf20Sopenharmony_ci struct devfreq *df = to_devfreq(d); 16158c2ecf20Sopenharmony_ci ssize_t count = 0; 16168c2ecf20Sopenharmony_ci int i; 16178c2ecf20Sopenharmony_ci 16188c2ecf20Sopenharmony_ci if (!df->profile) 16198c2ecf20Sopenharmony_ci return -EINVAL; 16208c2ecf20Sopenharmony_ci 16218c2ecf20Sopenharmony_ci mutex_lock(&df->lock); 16228c2ecf20Sopenharmony_ci 16238c2ecf20Sopenharmony_ci for (i = 0; i < df->profile->max_state; i++) 16248c2ecf20Sopenharmony_ci count += scnprintf(&buf[count], (PAGE_SIZE - count - 2), 16258c2ecf20Sopenharmony_ci "%lu ", df->profile->freq_table[i]); 16268c2ecf20Sopenharmony_ci 16278c2ecf20Sopenharmony_ci mutex_unlock(&df->lock); 16288c2ecf20Sopenharmony_ci /* Truncate the trailing space */ 16298c2ecf20Sopenharmony_ci if (count) 16308c2ecf20Sopenharmony_ci count--; 16318c2ecf20Sopenharmony_ci 16328c2ecf20Sopenharmony_ci count += sprintf(&buf[count], "\n"); 16338c2ecf20Sopenharmony_ci 16348c2ecf20Sopenharmony_ci return count; 16358c2ecf20Sopenharmony_ci} 16368c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(available_frequencies); 16378c2ecf20Sopenharmony_ci 16388c2ecf20Sopenharmony_cistatic ssize_t trans_stat_show(struct device *dev, 16398c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 16408c2ecf20Sopenharmony_ci{ 16418c2ecf20Sopenharmony_ci struct devfreq *df = to_devfreq(dev); 16428c2ecf20Sopenharmony_ci ssize_t len = 0; 16438c2ecf20Sopenharmony_ci int i, j; 16448c2ecf20Sopenharmony_ci unsigned int max_state; 16458c2ecf20Sopenharmony_ci 16468c2ecf20Sopenharmony_ci if (!df->profile) 16478c2ecf20Sopenharmony_ci return -EINVAL; 16488c2ecf20Sopenharmony_ci max_state = df->profile->max_state; 16498c2ecf20Sopenharmony_ci 16508c2ecf20Sopenharmony_ci if (max_state == 0) 16518c2ecf20Sopenharmony_ci return scnprintf(buf, PAGE_SIZE, "Not Supported.\n"); 16528c2ecf20Sopenharmony_ci 16538c2ecf20Sopenharmony_ci mutex_lock(&df->lock); 16548c2ecf20Sopenharmony_ci if (!df->stop_polling && 16558c2ecf20Sopenharmony_ci devfreq_update_status(df, df->previous_freq)) { 16568c2ecf20Sopenharmony_ci mutex_unlock(&df->lock); 16578c2ecf20Sopenharmony_ci return 0; 16588c2ecf20Sopenharmony_ci } 16598c2ecf20Sopenharmony_ci mutex_unlock(&df->lock); 16608c2ecf20Sopenharmony_ci 16618c2ecf20Sopenharmony_ci len += scnprintf(buf + len, PAGE_SIZE - len, " From : To\n"); 16628c2ecf20Sopenharmony_ci len += scnprintf(buf + len, PAGE_SIZE - len, " :"); 16638c2ecf20Sopenharmony_ci for (i = 0; i < max_state; i++) { 16648c2ecf20Sopenharmony_ci if (len >= PAGE_SIZE - 1) 16658c2ecf20Sopenharmony_ci break; 16668c2ecf20Sopenharmony_ci len += scnprintf(buf + len, PAGE_SIZE - len, "%10lu", 16678c2ecf20Sopenharmony_ci df->profile->freq_table[i]); 16688c2ecf20Sopenharmony_ci } 16698c2ecf20Sopenharmony_ci if (len >= PAGE_SIZE - 1) 16708c2ecf20Sopenharmony_ci return PAGE_SIZE - 1; 16718c2ecf20Sopenharmony_ci 16728c2ecf20Sopenharmony_ci len += scnprintf(buf + len, PAGE_SIZE - len, " time(ms)\n"); 16738c2ecf20Sopenharmony_ci 16748c2ecf20Sopenharmony_ci for (i = 0; i < max_state; i++) { 16758c2ecf20Sopenharmony_ci if (len >= PAGE_SIZE - 1) 16768c2ecf20Sopenharmony_ci break; 16778c2ecf20Sopenharmony_ci if (df->profile->freq_table[i] 16788c2ecf20Sopenharmony_ci == df->previous_freq) { 16798c2ecf20Sopenharmony_ci len += scnprintf(buf + len, PAGE_SIZE - len, "*"); 16808c2ecf20Sopenharmony_ci } else { 16818c2ecf20Sopenharmony_ci len += scnprintf(buf + len, PAGE_SIZE - len, " "); 16828c2ecf20Sopenharmony_ci } 16838c2ecf20Sopenharmony_ci if (len >= PAGE_SIZE - 1) 16848c2ecf20Sopenharmony_ci break; 16858c2ecf20Sopenharmony_ci 16868c2ecf20Sopenharmony_ci len += scnprintf(buf + len, PAGE_SIZE - len, "%10lu:", 16878c2ecf20Sopenharmony_ci df->profile->freq_table[i]); 16888c2ecf20Sopenharmony_ci for (j = 0; j < max_state; j++) { 16898c2ecf20Sopenharmony_ci if (len >= PAGE_SIZE - 1) 16908c2ecf20Sopenharmony_ci break; 16918c2ecf20Sopenharmony_ci len += scnprintf(buf + len, PAGE_SIZE - len, "%10u", 16928c2ecf20Sopenharmony_ci df->stats.trans_table[(i * max_state) + j]); 16938c2ecf20Sopenharmony_ci } 16948c2ecf20Sopenharmony_ci if (len >= PAGE_SIZE - 1) 16958c2ecf20Sopenharmony_ci break; 16968c2ecf20Sopenharmony_ci len += scnprintf(buf + len, PAGE_SIZE - len, "%10llu\n", (u64) 16978c2ecf20Sopenharmony_ci jiffies64_to_msecs(df->stats.time_in_state[i])); 16988c2ecf20Sopenharmony_ci } 16998c2ecf20Sopenharmony_ci 17008c2ecf20Sopenharmony_ci if (len < PAGE_SIZE - 1) 17018c2ecf20Sopenharmony_ci len += scnprintf(buf + len, PAGE_SIZE - len, "Total transition : %u\n", 17028c2ecf20Sopenharmony_ci df->stats.total_trans); 17038c2ecf20Sopenharmony_ci 17048c2ecf20Sopenharmony_ci if (len >= PAGE_SIZE - 1) { 17058c2ecf20Sopenharmony_ci pr_warn_once("devfreq transition table exceeds PAGE_SIZE. Disabling\n"); 17068c2ecf20Sopenharmony_ci return -EFBIG; 17078c2ecf20Sopenharmony_ci } 17088c2ecf20Sopenharmony_ci 17098c2ecf20Sopenharmony_ci return len; 17108c2ecf20Sopenharmony_ci} 17118c2ecf20Sopenharmony_ci 17128c2ecf20Sopenharmony_cistatic ssize_t trans_stat_store(struct device *dev, 17138c2ecf20Sopenharmony_ci struct device_attribute *attr, 17148c2ecf20Sopenharmony_ci const char *buf, size_t count) 17158c2ecf20Sopenharmony_ci{ 17168c2ecf20Sopenharmony_ci struct devfreq *df = to_devfreq(dev); 17178c2ecf20Sopenharmony_ci int err, value; 17188c2ecf20Sopenharmony_ci 17198c2ecf20Sopenharmony_ci if (!df->profile) 17208c2ecf20Sopenharmony_ci return -EINVAL; 17218c2ecf20Sopenharmony_ci 17228c2ecf20Sopenharmony_ci if (df->profile->max_state == 0) 17238c2ecf20Sopenharmony_ci return count; 17248c2ecf20Sopenharmony_ci 17258c2ecf20Sopenharmony_ci err = kstrtoint(buf, 10, &value); 17268c2ecf20Sopenharmony_ci if (err || value != 0) 17278c2ecf20Sopenharmony_ci return -EINVAL; 17288c2ecf20Sopenharmony_ci 17298c2ecf20Sopenharmony_ci mutex_lock(&df->lock); 17308c2ecf20Sopenharmony_ci memset(df->stats.time_in_state, 0, (df->profile->max_state * 17318c2ecf20Sopenharmony_ci sizeof(*df->stats.time_in_state))); 17328c2ecf20Sopenharmony_ci memset(df->stats.trans_table, 0, array3_size(sizeof(unsigned int), 17338c2ecf20Sopenharmony_ci df->profile->max_state, 17348c2ecf20Sopenharmony_ci df->profile->max_state)); 17358c2ecf20Sopenharmony_ci df->stats.total_trans = 0; 17368c2ecf20Sopenharmony_ci df->stats.last_update = get_jiffies_64(); 17378c2ecf20Sopenharmony_ci mutex_unlock(&df->lock); 17388c2ecf20Sopenharmony_ci 17398c2ecf20Sopenharmony_ci return count; 17408c2ecf20Sopenharmony_ci} 17418c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(trans_stat); 17428c2ecf20Sopenharmony_ci 17438c2ecf20Sopenharmony_cistatic ssize_t timer_show(struct device *dev, 17448c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 17458c2ecf20Sopenharmony_ci{ 17468c2ecf20Sopenharmony_ci struct devfreq *df = to_devfreq(dev); 17478c2ecf20Sopenharmony_ci 17488c2ecf20Sopenharmony_ci if (!df->profile) 17498c2ecf20Sopenharmony_ci return -EINVAL; 17508c2ecf20Sopenharmony_ci 17518c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", timer_name[df->profile->timer]); 17528c2ecf20Sopenharmony_ci} 17538c2ecf20Sopenharmony_ci 17548c2ecf20Sopenharmony_cistatic ssize_t timer_store(struct device *dev, struct device_attribute *attr, 17558c2ecf20Sopenharmony_ci const char *buf, size_t count) 17568c2ecf20Sopenharmony_ci{ 17578c2ecf20Sopenharmony_ci struct devfreq *df = to_devfreq(dev); 17588c2ecf20Sopenharmony_ci char str_timer[DEVFREQ_NAME_LEN + 1]; 17598c2ecf20Sopenharmony_ci int timer = -1; 17608c2ecf20Sopenharmony_ci int ret = 0, i; 17618c2ecf20Sopenharmony_ci 17628c2ecf20Sopenharmony_ci if (!df->governor || !df->profile) 17638c2ecf20Sopenharmony_ci return -EINVAL; 17648c2ecf20Sopenharmony_ci 17658c2ecf20Sopenharmony_ci ret = sscanf(buf, "%16s", str_timer); 17668c2ecf20Sopenharmony_ci if (ret != 1) 17678c2ecf20Sopenharmony_ci return -EINVAL; 17688c2ecf20Sopenharmony_ci 17698c2ecf20Sopenharmony_ci for (i = 0; i < DEVFREQ_TIMER_NUM; i++) { 17708c2ecf20Sopenharmony_ci if (!strncmp(timer_name[i], str_timer, DEVFREQ_NAME_LEN)) { 17718c2ecf20Sopenharmony_ci timer = i; 17728c2ecf20Sopenharmony_ci break; 17738c2ecf20Sopenharmony_ci } 17748c2ecf20Sopenharmony_ci } 17758c2ecf20Sopenharmony_ci 17768c2ecf20Sopenharmony_ci if (timer < 0) { 17778c2ecf20Sopenharmony_ci ret = -EINVAL; 17788c2ecf20Sopenharmony_ci goto out; 17798c2ecf20Sopenharmony_ci } 17808c2ecf20Sopenharmony_ci 17818c2ecf20Sopenharmony_ci if (df->profile->timer == timer) { 17828c2ecf20Sopenharmony_ci ret = 0; 17838c2ecf20Sopenharmony_ci goto out; 17848c2ecf20Sopenharmony_ci } 17858c2ecf20Sopenharmony_ci 17868c2ecf20Sopenharmony_ci mutex_lock(&df->lock); 17878c2ecf20Sopenharmony_ci df->profile->timer = timer; 17888c2ecf20Sopenharmony_ci mutex_unlock(&df->lock); 17898c2ecf20Sopenharmony_ci 17908c2ecf20Sopenharmony_ci ret = df->governor->event_handler(df, DEVFREQ_GOV_STOP, NULL); 17918c2ecf20Sopenharmony_ci if (ret) { 17928c2ecf20Sopenharmony_ci dev_warn(dev, "%s: Governor %s not stopped(%d)\n", 17938c2ecf20Sopenharmony_ci __func__, df->governor->name, ret); 17948c2ecf20Sopenharmony_ci goto out; 17958c2ecf20Sopenharmony_ci } 17968c2ecf20Sopenharmony_ci 17978c2ecf20Sopenharmony_ci ret = df->governor->event_handler(df, DEVFREQ_GOV_START, NULL); 17988c2ecf20Sopenharmony_ci if (ret) 17998c2ecf20Sopenharmony_ci dev_warn(dev, "%s: Governor %s not started(%d)\n", 18008c2ecf20Sopenharmony_ci __func__, df->governor->name, ret); 18018c2ecf20Sopenharmony_ciout: 18028c2ecf20Sopenharmony_ci return ret ? ret : count; 18038c2ecf20Sopenharmony_ci} 18048c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(timer); 18058c2ecf20Sopenharmony_ci 18068c2ecf20Sopenharmony_cistatic struct attribute *devfreq_attrs[] = { 18078c2ecf20Sopenharmony_ci &dev_attr_name.attr, 18088c2ecf20Sopenharmony_ci &dev_attr_governor.attr, 18098c2ecf20Sopenharmony_ci &dev_attr_available_governors.attr, 18108c2ecf20Sopenharmony_ci &dev_attr_cur_freq.attr, 18118c2ecf20Sopenharmony_ci &dev_attr_available_frequencies.attr, 18128c2ecf20Sopenharmony_ci &dev_attr_target_freq.attr, 18138c2ecf20Sopenharmony_ci &dev_attr_polling_interval.attr, 18148c2ecf20Sopenharmony_ci &dev_attr_min_freq.attr, 18158c2ecf20Sopenharmony_ci &dev_attr_max_freq.attr, 18168c2ecf20Sopenharmony_ci &dev_attr_trans_stat.attr, 18178c2ecf20Sopenharmony_ci &dev_attr_timer.attr, 18188c2ecf20Sopenharmony_ci NULL, 18198c2ecf20Sopenharmony_ci}; 18208c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(devfreq); 18218c2ecf20Sopenharmony_ci 18228c2ecf20Sopenharmony_ci/** 18238c2ecf20Sopenharmony_ci * devfreq_summary_show() - Show the summary of the devfreq devices 18248c2ecf20Sopenharmony_ci * @s: seq_file instance to show the summary of devfreq devices 18258c2ecf20Sopenharmony_ci * @data: not used 18268c2ecf20Sopenharmony_ci * 18278c2ecf20Sopenharmony_ci * Show the summary of the devfreq devices via 'devfreq_summary' debugfs file. 18288c2ecf20Sopenharmony_ci * It helps that user can know the detailed information of the devfreq devices. 18298c2ecf20Sopenharmony_ci * 18308c2ecf20Sopenharmony_ci * Return 0 always because it shows the information without any data change. 18318c2ecf20Sopenharmony_ci */ 18328c2ecf20Sopenharmony_cistatic int devfreq_summary_show(struct seq_file *s, void *data) 18338c2ecf20Sopenharmony_ci{ 18348c2ecf20Sopenharmony_ci struct devfreq *devfreq; 18358c2ecf20Sopenharmony_ci struct devfreq *p_devfreq = NULL; 18368c2ecf20Sopenharmony_ci unsigned long cur_freq, min_freq, max_freq; 18378c2ecf20Sopenharmony_ci unsigned int polling_ms; 18388c2ecf20Sopenharmony_ci unsigned int timer; 18398c2ecf20Sopenharmony_ci 18408c2ecf20Sopenharmony_ci seq_printf(s, "%-30s %-30s %-15s %-10s %10s %12s %12s %12s\n", 18418c2ecf20Sopenharmony_ci "dev", 18428c2ecf20Sopenharmony_ci "parent_dev", 18438c2ecf20Sopenharmony_ci "governor", 18448c2ecf20Sopenharmony_ci "timer", 18458c2ecf20Sopenharmony_ci "polling_ms", 18468c2ecf20Sopenharmony_ci "cur_freq_Hz", 18478c2ecf20Sopenharmony_ci "min_freq_Hz", 18488c2ecf20Sopenharmony_ci "max_freq_Hz"); 18498c2ecf20Sopenharmony_ci seq_printf(s, "%30s %30s %15s %10s %10s %12s %12s %12s\n", 18508c2ecf20Sopenharmony_ci "------------------------------", 18518c2ecf20Sopenharmony_ci "------------------------------", 18528c2ecf20Sopenharmony_ci "---------------", 18538c2ecf20Sopenharmony_ci "----------", 18548c2ecf20Sopenharmony_ci "----------", 18558c2ecf20Sopenharmony_ci "------------", 18568c2ecf20Sopenharmony_ci "------------", 18578c2ecf20Sopenharmony_ci "------------"); 18588c2ecf20Sopenharmony_ci 18598c2ecf20Sopenharmony_ci mutex_lock(&devfreq_list_lock); 18608c2ecf20Sopenharmony_ci 18618c2ecf20Sopenharmony_ci list_for_each_entry_reverse(devfreq, &devfreq_list, node) { 18628c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_DEVFREQ_GOV_PASSIVE) 18638c2ecf20Sopenharmony_ci if (!strncmp(devfreq->governor_name, DEVFREQ_GOV_PASSIVE, 18648c2ecf20Sopenharmony_ci DEVFREQ_NAME_LEN)) { 18658c2ecf20Sopenharmony_ci struct devfreq_passive_data *data = devfreq->data; 18668c2ecf20Sopenharmony_ci 18678c2ecf20Sopenharmony_ci if (data) 18688c2ecf20Sopenharmony_ci p_devfreq = data->parent; 18698c2ecf20Sopenharmony_ci } else { 18708c2ecf20Sopenharmony_ci p_devfreq = NULL; 18718c2ecf20Sopenharmony_ci } 18728c2ecf20Sopenharmony_ci#endif 18738c2ecf20Sopenharmony_ci 18748c2ecf20Sopenharmony_ci mutex_lock(&devfreq->lock); 18758c2ecf20Sopenharmony_ci cur_freq = devfreq->previous_freq; 18768c2ecf20Sopenharmony_ci get_freq_range(devfreq, &min_freq, &max_freq); 18778c2ecf20Sopenharmony_ci polling_ms = devfreq->profile->polling_ms; 18788c2ecf20Sopenharmony_ci timer = devfreq->profile->timer; 18798c2ecf20Sopenharmony_ci mutex_unlock(&devfreq->lock); 18808c2ecf20Sopenharmony_ci 18818c2ecf20Sopenharmony_ci seq_printf(s, 18828c2ecf20Sopenharmony_ci "%-30s %-30s %-15s %-10s %10d %12ld %12ld %12ld\n", 18838c2ecf20Sopenharmony_ci dev_name(&devfreq->dev), 18848c2ecf20Sopenharmony_ci p_devfreq ? dev_name(&p_devfreq->dev) : "null", 18858c2ecf20Sopenharmony_ci devfreq->governor_name, 18868c2ecf20Sopenharmony_ci polling_ms ? timer_name[timer] : "null", 18878c2ecf20Sopenharmony_ci polling_ms, 18888c2ecf20Sopenharmony_ci cur_freq, 18898c2ecf20Sopenharmony_ci min_freq, 18908c2ecf20Sopenharmony_ci max_freq); 18918c2ecf20Sopenharmony_ci } 18928c2ecf20Sopenharmony_ci 18938c2ecf20Sopenharmony_ci mutex_unlock(&devfreq_list_lock); 18948c2ecf20Sopenharmony_ci 18958c2ecf20Sopenharmony_ci return 0; 18968c2ecf20Sopenharmony_ci} 18978c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(devfreq_summary); 18988c2ecf20Sopenharmony_ci 18998c2ecf20Sopenharmony_cistatic int __init devfreq_init(void) 19008c2ecf20Sopenharmony_ci{ 19018c2ecf20Sopenharmony_ci devfreq_class = class_create(THIS_MODULE, "devfreq"); 19028c2ecf20Sopenharmony_ci if (IS_ERR(devfreq_class)) { 19038c2ecf20Sopenharmony_ci pr_err("%s: couldn't create class\n", __FILE__); 19048c2ecf20Sopenharmony_ci return PTR_ERR(devfreq_class); 19058c2ecf20Sopenharmony_ci } 19068c2ecf20Sopenharmony_ci 19078c2ecf20Sopenharmony_ci devfreq_wq = create_freezable_workqueue("devfreq_wq"); 19088c2ecf20Sopenharmony_ci if (!devfreq_wq) { 19098c2ecf20Sopenharmony_ci class_destroy(devfreq_class); 19108c2ecf20Sopenharmony_ci pr_err("%s: couldn't create workqueue\n", __FILE__); 19118c2ecf20Sopenharmony_ci return -ENOMEM; 19128c2ecf20Sopenharmony_ci } 19138c2ecf20Sopenharmony_ci devfreq_class->dev_groups = devfreq_groups; 19148c2ecf20Sopenharmony_ci 19158c2ecf20Sopenharmony_ci devfreq_debugfs = debugfs_create_dir("devfreq", NULL); 19168c2ecf20Sopenharmony_ci debugfs_create_file("devfreq_summary", 0444, 19178c2ecf20Sopenharmony_ci devfreq_debugfs, NULL, 19188c2ecf20Sopenharmony_ci &devfreq_summary_fops); 19198c2ecf20Sopenharmony_ci 19208c2ecf20Sopenharmony_ci return 0; 19218c2ecf20Sopenharmony_ci} 19228c2ecf20Sopenharmony_cisubsys_initcall(devfreq_init); 19238c2ecf20Sopenharmony_ci 19248c2ecf20Sopenharmony_ci/* 19258c2ecf20Sopenharmony_ci * The following are helper functions for devfreq user device drivers with 19268c2ecf20Sopenharmony_ci * OPP framework. 19278c2ecf20Sopenharmony_ci */ 19288c2ecf20Sopenharmony_ci 19298c2ecf20Sopenharmony_ci/** 19308c2ecf20Sopenharmony_ci * devfreq_recommended_opp() - Helper function to get proper OPP for the 19318c2ecf20Sopenharmony_ci * freq value given to target callback. 19328c2ecf20Sopenharmony_ci * @dev: The devfreq user device. (parent of devfreq) 19338c2ecf20Sopenharmony_ci * @freq: The frequency given to target function 19348c2ecf20Sopenharmony_ci * @flags: Flags handed from devfreq framework. 19358c2ecf20Sopenharmony_ci * 19368c2ecf20Sopenharmony_ci * The callers are required to call dev_pm_opp_put() for the returned OPP after 19378c2ecf20Sopenharmony_ci * use. 19388c2ecf20Sopenharmony_ci */ 19398c2ecf20Sopenharmony_cistruct dev_pm_opp *devfreq_recommended_opp(struct device *dev, 19408c2ecf20Sopenharmony_ci unsigned long *freq, 19418c2ecf20Sopenharmony_ci u32 flags) 19428c2ecf20Sopenharmony_ci{ 19438c2ecf20Sopenharmony_ci struct dev_pm_opp *opp; 19448c2ecf20Sopenharmony_ci 19458c2ecf20Sopenharmony_ci if (flags & DEVFREQ_FLAG_LEAST_UPPER_BOUND) { 19468c2ecf20Sopenharmony_ci /* The freq is an upper bound. opp should be lower */ 19478c2ecf20Sopenharmony_ci opp = dev_pm_opp_find_freq_floor(dev, freq); 19488c2ecf20Sopenharmony_ci 19498c2ecf20Sopenharmony_ci /* If not available, use the closest opp */ 19508c2ecf20Sopenharmony_ci if (opp == ERR_PTR(-ERANGE)) 19518c2ecf20Sopenharmony_ci opp = dev_pm_opp_find_freq_ceil(dev, freq); 19528c2ecf20Sopenharmony_ci } else { 19538c2ecf20Sopenharmony_ci /* The freq is an lower bound. opp should be higher */ 19548c2ecf20Sopenharmony_ci opp = dev_pm_opp_find_freq_ceil(dev, freq); 19558c2ecf20Sopenharmony_ci 19568c2ecf20Sopenharmony_ci /* If not available, use the closest opp */ 19578c2ecf20Sopenharmony_ci if (opp == ERR_PTR(-ERANGE)) 19588c2ecf20Sopenharmony_ci opp = dev_pm_opp_find_freq_floor(dev, freq); 19598c2ecf20Sopenharmony_ci } 19608c2ecf20Sopenharmony_ci 19618c2ecf20Sopenharmony_ci return opp; 19628c2ecf20Sopenharmony_ci} 19638c2ecf20Sopenharmony_ciEXPORT_SYMBOL(devfreq_recommended_opp); 19648c2ecf20Sopenharmony_ci 19658c2ecf20Sopenharmony_ci/** 19668c2ecf20Sopenharmony_ci * devfreq_register_opp_notifier() - Helper function to get devfreq notified 19678c2ecf20Sopenharmony_ci * for any changes in the OPP availability 19688c2ecf20Sopenharmony_ci * changes 19698c2ecf20Sopenharmony_ci * @dev: The devfreq user device. (parent of devfreq) 19708c2ecf20Sopenharmony_ci * @devfreq: The devfreq object. 19718c2ecf20Sopenharmony_ci */ 19728c2ecf20Sopenharmony_ciint devfreq_register_opp_notifier(struct device *dev, struct devfreq *devfreq) 19738c2ecf20Sopenharmony_ci{ 19748c2ecf20Sopenharmony_ci return dev_pm_opp_register_notifier(dev, &devfreq->nb); 19758c2ecf20Sopenharmony_ci} 19768c2ecf20Sopenharmony_ciEXPORT_SYMBOL(devfreq_register_opp_notifier); 19778c2ecf20Sopenharmony_ci 19788c2ecf20Sopenharmony_ci/** 19798c2ecf20Sopenharmony_ci * devfreq_unregister_opp_notifier() - Helper function to stop getting devfreq 19808c2ecf20Sopenharmony_ci * notified for any changes in the OPP 19818c2ecf20Sopenharmony_ci * availability changes anymore. 19828c2ecf20Sopenharmony_ci * @dev: The devfreq user device. (parent of devfreq) 19838c2ecf20Sopenharmony_ci * @devfreq: The devfreq object. 19848c2ecf20Sopenharmony_ci * 19858c2ecf20Sopenharmony_ci * At exit() callback of devfreq_dev_profile, this must be included if 19868c2ecf20Sopenharmony_ci * devfreq_recommended_opp is used. 19878c2ecf20Sopenharmony_ci */ 19888c2ecf20Sopenharmony_ciint devfreq_unregister_opp_notifier(struct device *dev, struct devfreq *devfreq) 19898c2ecf20Sopenharmony_ci{ 19908c2ecf20Sopenharmony_ci return dev_pm_opp_unregister_notifier(dev, &devfreq->nb); 19918c2ecf20Sopenharmony_ci} 19928c2ecf20Sopenharmony_ciEXPORT_SYMBOL(devfreq_unregister_opp_notifier); 19938c2ecf20Sopenharmony_ci 19948c2ecf20Sopenharmony_cistatic void devm_devfreq_opp_release(struct device *dev, void *res) 19958c2ecf20Sopenharmony_ci{ 19968c2ecf20Sopenharmony_ci devfreq_unregister_opp_notifier(dev, *(struct devfreq **)res); 19978c2ecf20Sopenharmony_ci} 19988c2ecf20Sopenharmony_ci 19998c2ecf20Sopenharmony_ci/** 20008c2ecf20Sopenharmony_ci * devm_devfreq_register_opp_notifier() - Resource-managed 20018c2ecf20Sopenharmony_ci * devfreq_register_opp_notifier() 20028c2ecf20Sopenharmony_ci * @dev: The devfreq user device. (parent of devfreq) 20038c2ecf20Sopenharmony_ci * @devfreq: The devfreq object. 20048c2ecf20Sopenharmony_ci */ 20058c2ecf20Sopenharmony_ciint devm_devfreq_register_opp_notifier(struct device *dev, 20068c2ecf20Sopenharmony_ci struct devfreq *devfreq) 20078c2ecf20Sopenharmony_ci{ 20088c2ecf20Sopenharmony_ci struct devfreq **ptr; 20098c2ecf20Sopenharmony_ci int ret; 20108c2ecf20Sopenharmony_ci 20118c2ecf20Sopenharmony_ci ptr = devres_alloc(devm_devfreq_opp_release, sizeof(*ptr), GFP_KERNEL); 20128c2ecf20Sopenharmony_ci if (!ptr) 20138c2ecf20Sopenharmony_ci return -ENOMEM; 20148c2ecf20Sopenharmony_ci 20158c2ecf20Sopenharmony_ci ret = devfreq_register_opp_notifier(dev, devfreq); 20168c2ecf20Sopenharmony_ci if (ret) { 20178c2ecf20Sopenharmony_ci devres_free(ptr); 20188c2ecf20Sopenharmony_ci return ret; 20198c2ecf20Sopenharmony_ci } 20208c2ecf20Sopenharmony_ci 20218c2ecf20Sopenharmony_ci *ptr = devfreq; 20228c2ecf20Sopenharmony_ci devres_add(dev, ptr); 20238c2ecf20Sopenharmony_ci 20248c2ecf20Sopenharmony_ci return 0; 20258c2ecf20Sopenharmony_ci} 20268c2ecf20Sopenharmony_ciEXPORT_SYMBOL(devm_devfreq_register_opp_notifier); 20278c2ecf20Sopenharmony_ci 20288c2ecf20Sopenharmony_ci/** 20298c2ecf20Sopenharmony_ci * devm_devfreq_unregister_opp_notifier() - Resource-managed 20308c2ecf20Sopenharmony_ci * devfreq_unregister_opp_notifier() 20318c2ecf20Sopenharmony_ci * @dev: The devfreq user device. (parent of devfreq) 20328c2ecf20Sopenharmony_ci * @devfreq: The devfreq object. 20338c2ecf20Sopenharmony_ci */ 20348c2ecf20Sopenharmony_civoid devm_devfreq_unregister_opp_notifier(struct device *dev, 20358c2ecf20Sopenharmony_ci struct devfreq *devfreq) 20368c2ecf20Sopenharmony_ci{ 20378c2ecf20Sopenharmony_ci WARN_ON(devres_release(dev, devm_devfreq_opp_release, 20388c2ecf20Sopenharmony_ci devm_devfreq_dev_match, devfreq)); 20398c2ecf20Sopenharmony_ci} 20408c2ecf20Sopenharmony_ciEXPORT_SYMBOL(devm_devfreq_unregister_opp_notifier); 20418c2ecf20Sopenharmony_ci 20428c2ecf20Sopenharmony_ci/** 20438c2ecf20Sopenharmony_ci * devfreq_register_notifier() - Register a driver with devfreq 20448c2ecf20Sopenharmony_ci * @devfreq: The devfreq object. 20458c2ecf20Sopenharmony_ci * @nb: The notifier block to register. 20468c2ecf20Sopenharmony_ci * @list: DEVFREQ_TRANSITION_NOTIFIER. 20478c2ecf20Sopenharmony_ci */ 20488c2ecf20Sopenharmony_ciint devfreq_register_notifier(struct devfreq *devfreq, 20498c2ecf20Sopenharmony_ci struct notifier_block *nb, 20508c2ecf20Sopenharmony_ci unsigned int list) 20518c2ecf20Sopenharmony_ci{ 20528c2ecf20Sopenharmony_ci int ret = 0; 20538c2ecf20Sopenharmony_ci 20548c2ecf20Sopenharmony_ci if (!devfreq) 20558c2ecf20Sopenharmony_ci return -EINVAL; 20568c2ecf20Sopenharmony_ci 20578c2ecf20Sopenharmony_ci switch (list) { 20588c2ecf20Sopenharmony_ci case DEVFREQ_TRANSITION_NOTIFIER: 20598c2ecf20Sopenharmony_ci ret = srcu_notifier_chain_register( 20608c2ecf20Sopenharmony_ci &devfreq->transition_notifier_list, nb); 20618c2ecf20Sopenharmony_ci break; 20628c2ecf20Sopenharmony_ci default: 20638c2ecf20Sopenharmony_ci ret = -EINVAL; 20648c2ecf20Sopenharmony_ci } 20658c2ecf20Sopenharmony_ci 20668c2ecf20Sopenharmony_ci return ret; 20678c2ecf20Sopenharmony_ci} 20688c2ecf20Sopenharmony_ciEXPORT_SYMBOL(devfreq_register_notifier); 20698c2ecf20Sopenharmony_ci 20708c2ecf20Sopenharmony_ci/* 20718c2ecf20Sopenharmony_ci * devfreq_unregister_notifier() - Unregister a driver with devfreq 20728c2ecf20Sopenharmony_ci * @devfreq: The devfreq object. 20738c2ecf20Sopenharmony_ci * @nb: The notifier block to be unregistered. 20748c2ecf20Sopenharmony_ci * @list: DEVFREQ_TRANSITION_NOTIFIER. 20758c2ecf20Sopenharmony_ci */ 20768c2ecf20Sopenharmony_ciint devfreq_unregister_notifier(struct devfreq *devfreq, 20778c2ecf20Sopenharmony_ci struct notifier_block *nb, 20788c2ecf20Sopenharmony_ci unsigned int list) 20798c2ecf20Sopenharmony_ci{ 20808c2ecf20Sopenharmony_ci int ret = 0; 20818c2ecf20Sopenharmony_ci 20828c2ecf20Sopenharmony_ci if (!devfreq) 20838c2ecf20Sopenharmony_ci return -EINVAL; 20848c2ecf20Sopenharmony_ci 20858c2ecf20Sopenharmony_ci switch (list) { 20868c2ecf20Sopenharmony_ci case DEVFREQ_TRANSITION_NOTIFIER: 20878c2ecf20Sopenharmony_ci ret = srcu_notifier_chain_unregister( 20888c2ecf20Sopenharmony_ci &devfreq->transition_notifier_list, nb); 20898c2ecf20Sopenharmony_ci break; 20908c2ecf20Sopenharmony_ci default: 20918c2ecf20Sopenharmony_ci ret = -EINVAL; 20928c2ecf20Sopenharmony_ci } 20938c2ecf20Sopenharmony_ci 20948c2ecf20Sopenharmony_ci return ret; 20958c2ecf20Sopenharmony_ci} 20968c2ecf20Sopenharmony_ciEXPORT_SYMBOL(devfreq_unregister_notifier); 20978c2ecf20Sopenharmony_ci 20988c2ecf20Sopenharmony_cistruct devfreq_notifier_devres { 20998c2ecf20Sopenharmony_ci struct devfreq *devfreq; 21008c2ecf20Sopenharmony_ci struct notifier_block *nb; 21018c2ecf20Sopenharmony_ci unsigned int list; 21028c2ecf20Sopenharmony_ci}; 21038c2ecf20Sopenharmony_ci 21048c2ecf20Sopenharmony_cistatic void devm_devfreq_notifier_release(struct device *dev, void *res) 21058c2ecf20Sopenharmony_ci{ 21068c2ecf20Sopenharmony_ci struct devfreq_notifier_devres *this = res; 21078c2ecf20Sopenharmony_ci 21088c2ecf20Sopenharmony_ci devfreq_unregister_notifier(this->devfreq, this->nb, this->list); 21098c2ecf20Sopenharmony_ci} 21108c2ecf20Sopenharmony_ci 21118c2ecf20Sopenharmony_ci/** 21128c2ecf20Sopenharmony_ci * devm_devfreq_register_notifier() 21138c2ecf20Sopenharmony_ci * - Resource-managed devfreq_register_notifier() 21148c2ecf20Sopenharmony_ci * @dev: The devfreq user device. (parent of devfreq) 21158c2ecf20Sopenharmony_ci * @devfreq: The devfreq object. 21168c2ecf20Sopenharmony_ci * @nb: The notifier block to be unregistered. 21178c2ecf20Sopenharmony_ci * @list: DEVFREQ_TRANSITION_NOTIFIER. 21188c2ecf20Sopenharmony_ci */ 21198c2ecf20Sopenharmony_ciint devm_devfreq_register_notifier(struct device *dev, 21208c2ecf20Sopenharmony_ci struct devfreq *devfreq, 21218c2ecf20Sopenharmony_ci struct notifier_block *nb, 21228c2ecf20Sopenharmony_ci unsigned int list) 21238c2ecf20Sopenharmony_ci{ 21248c2ecf20Sopenharmony_ci struct devfreq_notifier_devres *ptr; 21258c2ecf20Sopenharmony_ci int ret; 21268c2ecf20Sopenharmony_ci 21278c2ecf20Sopenharmony_ci ptr = devres_alloc(devm_devfreq_notifier_release, sizeof(*ptr), 21288c2ecf20Sopenharmony_ci GFP_KERNEL); 21298c2ecf20Sopenharmony_ci if (!ptr) 21308c2ecf20Sopenharmony_ci return -ENOMEM; 21318c2ecf20Sopenharmony_ci 21328c2ecf20Sopenharmony_ci ret = devfreq_register_notifier(devfreq, nb, list); 21338c2ecf20Sopenharmony_ci if (ret) { 21348c2ecf20Sopenharmony_ci devres_free(ptr); 21358c2ecf20Sopenharmony_ci return ret; 21368c2ecf20Sopenharmony_ci } 21378c2ecf20Sopenharmony_ci 21388c2ecf20Sopenharmony_ci ptr->devfreq = devfreq; 21398c2ecf20Sopenharmony_ci ptr->nb = nb; 21408c2ecf20Sopenharmony_ci ptr->list = list; 21418c2ecf20Sopenharmony_ci devres_add(dev, ptr); 21428c2ecf20Sopenharmony_ci 21438c2ecf20Sopenharmony_ci return 0; 21448c2ecf20Sopenharmony_ci} 21458c2ecf20Sopenharmony_ciEXPORT_SYMBOL(devm_devfreq_register_notifier); 21468c2ecf20Sopenharmony_ci 21478c2ecf20Sopenharmony_ci/** 21488c2ecf20Sopenharmony_ci * devm_devfreq_unregister_notifier() 21498c2ecf20Sopenharmony_ci * - Resource-managed devfreq_unregister_notifier() 21508c2ecf20Sopenharmony_ci * @dev: The devfreq user device. (parent of devfreq) 21518c2ecf20Sopenharmony_ci * @devfreq: The devfreq object. 21528c2ecf20Sopenharmony_ci * @nb: The notifier block to be unregistered. 21538c2ecf20Sopenharmony_ci * @list: DEVFREQ_TRANSITION_NOTIFIER. 21548c2ecf20Sopenharmony_ci */ 21558c2ecf20Sopenharmony_civoid devm_devfreq_unregister_notifier(struct device *dev, 21568c2ecf20Sopenharmony_ci struct devfreq *devfreq, 21578c2ecf20Sopenharmony_ci struct notifier_block *nb, 21588c2ecf20Sopenharmony_ci unsigned int list) 21598c2ecf20Sopenharmony_ci{ 21608c2ecf20Sopenharmony_ci WARN_ON(devres_release(dev, devm_devfreq_notifier_release, 21618c2ecf20Sopenharmony_ci devm_devfreq_dev_match, devfreq)); 21628c2ecf20Sopenharmony_ci} 21638c2ecf20Sopenharmony_ciEXPORT_SYMBOL(devm_devfreq_unregister_notifier); 2164