13d0407baSopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 23d0407baSopenharmony_ci/* 33d0407baSopenharmony_ci * devfreq: Generic Dynamic Voltage and Frequency Scaling (DVFS) Framework 43d0407baSopenharmony_ci * for Non-CPU Devices. 53d0407baSopenharmony_ci * 63d0407baSopenharmony_ci * Copyright (C) 2011 Samsung Electronics 73d0407baSopenharmony_ci * MyungJoo Ham <myungjoo.ham@samsung.com> 83d0407baSopenharmony_ci */ 93d0407baSopenharmony_ci 103d0407baSopenharmony_ci#include <linux/kernel.h> 113d0407baSopenharmony_ci#include <linux/kmod.h> 123d0407baSopenharmony_ci#include <linux/sched.h> 133d0407baSopenharmony_ci#include <linux/debugfs.h> 143d0407baSopenharmony_ci#include <linux/errno.h> 153d0407baSopenharmony_ci#include <linux/err.h> 163d0407baSopenharmony_ci#include <linux/init.h> 173d0407baSopenharmony_ci#include <linux/export.h> 183d0407baSopenharmony_ci#include <linux/slab.h> 193d0407baSopenharmony_ci#include <linux/stat.h> 203d0407baSopenharmony_ci#include <linux/pm_opp.h> 213d0407baSopenharmony_ci#include <linux/devfreq.h> 223d0407baSopenharmony_ci#include <linux/workqueue.h> 233d0407baSopenharmony_ci#include <linux/platform_device.h> 243d0407baSopenharmony_ci#include <linux/list.h> 253d0407baSopenharmony_ci#include <linux/printk.h> 263d0407baSopenharmony_ci#include <linux/hrtimer.h> 273d0407baSopenharmony_ci#include <linux/of.h> 283d0407baSopenharmony_ci#include <linux/pm_qos.h> 293d0407baSopenharmony_ci#include "governor.h" 303d0407baSopenharmony_ci 313d0407baSopenharmony_ci#define CREATE_TRACE_POINTS 323d0407baSopenharmony_ci#include <trace/events/devfreq.h> 333d0407baSopenharmony_ci 343d0407baSopenharmony_ci#define HZ_PER_KHZ 1000 353d0407baSopenharmony_ci 363d0407baSopenharmony_cistatic struct class *devfreq_class; 373d0407baSopenharmony_cistatic struct dentry *devfreq_debugfs; 383d0407baSopenharmony_ci 393d0407baSopenharmony_ci/* 403d0407baSopenharmony_ci * devfreq core provides delayed work based load monitoring helper 413d0407baSopenharmony_ci * functions. Governors can use these or can implement their own 423d0407baSopenharmony_ci * monitoring mechanism. 433d0407baSopenharmony_ci */ 443d0407baSopenharmony_cistatic struct workqueue_struct *devfreq_wq; 453d0407baSopenharmony_ci 463d0407baSopenharmony_ci/* The list of all device-devfreq governors */ 473d0407baSopenharmony_cistatic LIST_HEAD(devfreq_governor_list); 483d0407baSopenharmony_ci/* The list of all device-devfreq */ 493d0407baSopenharmony_cistatic LIST_HEAD(devfreq_list); 503d0407baSopenharmony_cistatic DEFINE_MUTEX(devfreq_list_lock); 513d0407baSopenharmony_ci 523d0407baSopenharmony_cistatic const char timer_name[][DEVFREQ_NAME_LEN] = { 533d0407baSopenharmony_ci [DEVFREQ_TIMER_DEFERRABLE] = {"deferrable"}, 543d0407baSopenharmony_ci [DEVFREQ_TIMER_DELAYED] = {"delayed"}, 553d0407baSopenharmony_ci}; 563d0407baSopenharmony_ci 573d0407baSopenharmony_ci/** 583d0407baSopenharmony_ci * find_device_devfreq() - find devfreq struct using device pointer 593d0407baSopenharmony_ci * @dev: device pointer used to lookup device devfreq. 603d0407baSopenharmony_ci * 613d0407baSopenharmony_ci * Search the list of device devfreqs and return the matched device's 623d0407baSopenharmony_ci * devfreq info. devfreq_list_lock should be held by the caller. 633d0407baSopenharmony_ci */ 643d0407baSopenharmony_cistatic struct devfreq *find_device_devfreq(struct device *dev) 653d0407baSopenharmony_ci{ 663d0407baSopenharmony_ci struct devfreq *tmp_devfreq; 673d0407baSopenharmony_ci 683d0407baSopenharmony_ci lockdep_assert_held(&devfreq_list_lock); 693d0407baSopenharmony_ci 703d0407baSopenharmony_ci if (IS_ERR_OR_NULL(dev)) { 713d0407baSopenharmony_ci pr_err("DEVFREQ: %s: Invalid parameters\n", __func__); 723d0407baSopenharmony_ci return ERR_PTR(-EINVAL); 733d0407baSopenharmony_ci } 743d0407baSopenharmony_ci 753d0407baSopenharmony_ci list_for_each_entry(tmp_devfreq, &devfreq_list, node) 763d0407baSopenharmony_ci { 773d0407baSopenharmony_ci if (tmp_devfreq->dev.parent == dev) { 783d0407baSopenharmony_ci return tmp_devfreq; 793d0407baSopenharmony_ci } 803d0407baSopenharmony_ci } 813d0407baSopenharmony_ci 823d0407baSopenharmony_ci return ERR_PTR(-ENODEV); 833d0407baSopenharmony_ci} 843d0407baSopenharmony_ci 853d0407baSopenharmony_cistatic unsigned long find_available_min_freq(struct devfreq *devfreq) 863d0407baSopenharmony_ci{ 873d0407baSopenharmony_ci struct dev_pm_opp *opp; 883d0407baSopenharmony_ci unsigned long min_freq = 0; 893d0407baSopenharmony_ci 903d0407baSopenharmony_ci opp = dev_pm_opp_find_freq_ceil(devfreq->dev.parent, &min_freq); 913d0407baSopenharmony_ci if (IS_ERR(opp)) { 923d0407baSopenharmony_ci min_freq = 0; 933d0407baSopenharmony_ci } else { 943d0407baSopenharmony_ci dev_pm_opp_put(opp); 953d0407baSopenharmony_ci } 963d0407baSopenharmony_ci 973d0407baSopenharmony_ci return min_freq; 983d0407baSopenharmony_ci} 993d0407baSopenharmony_ci 1003d0407baSopenharmony_cistatic unsigned long find_available_max_freq(struct devfreq *devfreq) 1013d0407baSopenharmony_ci{ 1023d0407baSopenharmony_ci struct dev_pm_opp *opp; 1033d0407baSopenharmony_ci unsigned long max_freq = ULONG_MAX; 1043d0407baSopenharmony_ci 1053d0407baSopenharmony_ci opp = dev_pm_opp_find_freq_floor(devfreq->dev.parent, &max_freq); 1063d0407baSopenharmony_ci if (IS_ERR(opp)) { 1073d0407baSopenharmony_ci max_freq = 0; 1083d0407baSopenharmony_ci } else { 1093d0407baSopenharmony_ci dev_pm_opp_put(opp); 1103d0407baSopenharmony_ci } 1113d0407baSopenharmony_ci 1123d0407baSopenharmony_ci return max_freq; 1133d0407baSopenharmony_ci} 1143d0407baSopenharmony_ci 1153d0407baSopenharmony_ci/** 1163d0407baSopenharmony_ci * get_freq_range() - Get the current freq range 1173d0407baSopenharmony_ci * @devfreq: the devfreq instance 1183d0407baSopenharmony_ci * @min_freq: the min frequency 1193d0407baSopenharmony_ci * @max_freq: the max frequency 1203d0407baSopenharmony_ci * 1213d0407baSopenharmony_ci * This takes into consideration all constraints. 1223d0407baSopenharmony_ci */ 1233d0407baSopenharmony_cistatic void get_freq_range(struct devfreq *devfreq, unsigned long *min_freq, unsigned long *max_freq) 1243d0407baSopenharmony_ci{ 1253d0407baSopenharmony_ci unsigned long *freq_table = devfreq->profile->freq_table; 1263d0407baSopenharmony_ci s32 qos_min_freq, qos_max_freq; 1273d0407baSopenharmony_ci 1283d0407baSopenharmony_ci lockdep_assert_held(&devfreq->lock); 1293d0407baSopenharmony_ci 1303d0407baSopenharmony_ci /* 1313d0407baSopenharmony_ci * Initialize minimum/maximum frequency from freq table. 1323d0407baSopenharmony_ci * The devfreq drivers can initialize this in either ascending or 1333d0407baSopenharmony_ci * descending order and devfreq core supports both. 1343d0407baSopenharmony_ci */ 1353d0407baSopenharmony_ci if (freq_table[0] < freq_table[devfreq->profile->max_state - 1]) { 1363d0407baSopenharmony_ci *min_freq = freq_table[0]; 1373d0407baSopenharmony_ci *max_freq = freq_table[devfreq->profile->max_state - 1]; 1383d0407baSopenharmony_ci } else { 1393d0407baSopenharmony_ci *min_freq = freq_table[devfreq->profile->max_state - 1]; 1403d0407baSopenharmony_ci *max_freq = freq_table[0]; 1413d0407baSopenharmony_ci } 1423d0407baSopenharmony_ci 1433d0407baSopenharmony_ci /* Apply constraints from PM QoS */ 1443d0407baSopenharmony_ci qos_min_freq = dev_pm_qos_read_value(devfreq->dev.parent, DEV_PM_QOS_MIN_FREQUENCY); 1453d0407baSopenharmony_ci qos_max_freq = dev_pm_qos_read_value(devfreq->dev.parent, DEV_PM_QOS_MAX_FREQUENCY); 1463d0407baSopenharmony_ci *min_freq = max(*min_freq, (unsigned long)HZ_PER_KHZ * qos_min_freq); 1473d0407baSopenharmony_ci if (qos_max_freq != PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE) { 1483d0407baSopenharmony_ci *max_freq = min(*max_freq, (unsigned long)HZ_PER_KHZ * qos_max_freq); 1493d0407baSopenharmony_ci } 1503d0407baSopenharmony_ci 1513d0407baSopenharmony_ci /* Apply constraints from OPP interface */ 1523d0407baSopenharmony_ci *min_freq = max(*min_freq, devfreq->scaling_min_freq); 1533d0407baSopenharmony_ci *max_freq = min(*max_freq, devfreq->scaling_max_freq); 1543d0407baSopenharmony_ci 1553d0407baSopenharmony_ci if (*min_freq > *max_freq) { 1563d0407baSopenharmony_ci *min_freq = *max_freq; 1573d0407baSopenharmony_ci } 1583d0407baSopenharmony_ci} 1593d0407baSopenharmony_ci 1603d0407baSopenharmony_ci/** 1613d0407baSopenharmony_ci * devfreq_get_freq_level() - Lookup freq_table for the frequency 1623d0407baSopenharmony_ci * @devfreq: the devfreq instance 1633d0407baSopenharmony_ci * @freq: the target frequency 1643d0407baSopenharmony_ci */ 1653d0407baSopenharmony_cistatic int devfreq_get_freq_level(struct devfreq *devfreq, unsigned long freq) 1663d0407baSopenharmony_ci{ 1673d0407baSopenharmony_ci int lev; 1683d0407baSopenharmony_ci 1693d0407baSopenharmony_ci for (lev = 0; lev < devfreq->profile->max_state; lev++) { 1703d0407baSopenharmony_ci if (freq == devfreq->profile->freq_table[lev]) { 1713d0407baSopenharmony_ci return lev; 1723d0407baSopenharmony_ci } 1733d0407baSopenharmony_ci } 1743d0407baSopenharmony_ci 1753d0407baSopenharmony_ci return -EINVAL; 1763d0407baSopenharmony_ci} 1773d0407baSopenharmony_ci 1783d0407baSopenharmony_cistatic int set_freq_table(struct devfreq *devfreq) 1793d0407baSopenharmony_ci{ 1803d0407baSopenharmony_ci struct devfreq_dev_profile *profile = devfreq->profile; 1813d0407baSopenharmony_ci struct dev_pm_opp *opp; 1823d0407baSopenharmony_ci unsigned long freq; 1833d0407baSopenharmony_ci int i, count; 1843d0407baSopenharmony_ci 1853d0407baSopenharmony_ci /* Initialize the freq_table from OPP table */ 1863d0407baSopenharmony_ci count = dev_pm_opp_get_opp_count(devfreq->dev.parent); 1873d0407baSopenharmony_ci if (count <= 0) { 1883d0407baSopenharmony_ci return -EINVAL; 1893d0407baSopenharmony_ci } 1903d0407baSopenharmony_ci 1913d0407baSopenharmony_ci profile->max_state = count; 1923d0407baSopenharmony_ci profile->freq_table = 1933d0407baSopenharmony_ci devm_kcalloc(devfreq->dev.parent, profile->max_state, sizeof(*profile->freq_table), GFP_KERNEL); 1943d0407baSopenharmony_ci if (!profile->freq_table) { 1953d0407baSopenharmony_ci profile->max_state = 0; 1963d0407baSopenharmony_ci return -ENOMEM; 1973d0407baSopenharmony_ci } 1983d0407baSopenharmony_ci 1993d0407baSopenharmony_ci for (i = 0, freq = 0; i < profile->max_state; i++, freq++) { 2003d0407baSopenharmony_ci opp = dev_pm_opp_find_freq_ceil(devfreq->dev.parent, &freq); 2013d0407baSopenharmony_ci if (IS_ERR(opp)) { 2023d0407baSopenharmony_ci devm_kfree(devfreq->dev.parent, profile->freq_table); 2033d0407baSopenharmony_ci profile->max_state = 0; 2043d0407baSopenharmony_ci return PTR_ERR(opp); 2053d0407baSopenharmony_ci } 2063d0407baSopenharmony_ci dev_pm_opp_put(opp); 2073d0407baSopenharmony_ci profile->freq_table[i] = freq; 2083d0407baSopenharmony_ci } 2093d0407baSopenharmony_ci 2103d0407baSopenharmony_ci return 0; 2113d0407baSopenharmony_ci} 2123d0407baSopenharmony_ci 2133d0407baSopenharmony_ci/** 2143d0407baSopenharmony_ci * devfreq_update_status() - Update statistics of devfreq behavior 2153d0407baSopenharmony_ci * @devfreq: the devfreq instance 2163d0407baSopenharmony_ci * @freq: the update target frequency 2173d0407baSopenharmony_ci */ 2183d0407baSopenharmony_ciint devfreq_update_status(struct devfreq *devfreq, unsigned long freq) 2193d0407baSopenharmony_ci{ 2203d0407baSopenharmony_ci int lev, prev_lev, ret = 0; 2213d0407baSopenharmony_ci u64 cur_time; 2223d0407baSopenharmony_ci 2233d0407baSopenharmony_ci lockdep_assert_held(&devfreq->lock); 2243d0407baSopenharmony_ci cur_time = get_jiffies_64(); 2253d0407baSopenharmony_ci 2263d0407baSopenharmony_ci /* Immediately exit if previous_freq is not initialized yet. */ 2273d0407baSopenharmony_ci if (!devfreq->previous_freq) { 2283d0407baSopenharmony_ci goto out; 2293d0407baSopenharmony_ci } 2303d0407baSopenharmony_ci 2313d0407baSopenharmony_ci prev_lev = devfreq_get_freq_level(devfreq, devfreq->previous_freq); 2323d0407baSopenharmony_ci if (prev_lev < 0) { 2333d0407baSopenharmony_ci ret = prev_lev; 2343d0407baSopenharmony_ci goto out; 2353d0407baSopenharmony_ci } 2363d0407baSopenharmony_ci 2373d0407baSopenharmony_ci devfreq->stats.time_in_state[prev_lev] += cur_time - devfreq->stats.last_update; 2383d0407baSopenharmony_ci 2393d0407baSopenharmony_ci lev = devfreq_get_freq_level(devfreq, freq); 2403d0407baSopenharmony_ci if (lev < 0) { 2413d0407baSopenharmony_ci ret = lev; 2423d0407baSopenharmony_ci goto out; 2433d0407baSopenharmony_ci } 2443d0407baSopenharmony_ci 2453d0407baSopenharmony_ci if (lev != prev_lev) { 2463d0407baSopenharmony_ci devfreq->stats.trans_table[(prev_lev * devfreq->profile->max_state) + lev]++; 2473d0407baSopenharmony_ci devfreq->stats.total_trans++; 2483d0407baSopenharmony_ci } 2493d0407baSopenharmony_ci 2503d0407baSopenharmony_ciout: 2513d0407baSopenharmony_ci devfreq->stats.last_update = cur_time; 2523d0407baSopenharmony_ci return ret; 2533d0407baSopenharmony_ci} 2543d0407baSopenharmony_ciEXPORT_SYMBOL(devfreq_update_status); 2553d0407baSopenharmony_ci 2563d0407baSopenharmony_ci/** 2573d0407baSopenharmony_ci * find_devfreq_governor() - find devfreq governor from name 2583d0407baSopenharmony_ci * @name: name of the governor 2593d0407baSopenharmony_ci * 2603d0407baSopenharmony_ci * Search the list of devfreq governors and return the matched 2613d0407baSopenharmony_ci * governor's pointer. devfreq_list_lock should be held by the caller. 2623d0407baSopenharmony_ci */ 2633d0407baSopenharmony_cistatic struct devfreq_governor *find_devfreq_governor(const char *name) 2643d0407baSopenharmony_ci{ 2653d0407baSopenharmony_ci struct devfreq_governor *tmp_governor; 2663d0407baSopenharmony_ci 2673d0407baSopenharmony_ci lockdep_assert_held(&devfreq_list_lock); 2683d0407baSopenharmony_ci 2693d0407baSopenharmony_ci if (IS_ERR_OR_NULL(name)) { 2703d0407baSopenharmony_ci pr_err("DEVFREQ: %s: Invalid parameters\n", __func__); 2713d0407baSopenharmony_ci return ERR_PTR(-EINVAL); 2723d0407baSopenharmony_ci } 2733d0407baSopenharmony_ci 2743d0407baSopenharmony_ci list_for_each_entry(tmp_governor, &devfreq_governor_list, node) 2753d0407baSopenharmony_ci { 2763d0407baSopenharmony_ci if (!strncmp(tmp_governor->name, name, DEVFREQ_NAME_LEN)) { 2773d0407baSopenharmony_ci return tmp_governor; 2783d0407baSopenharmony_ci } 2793d0407baSopenharmony_ci } 2803d0407baSopenharmony_ci 2813d0407baSopenharmony_ci return ERR_PTR(-ENODEV); 2823d0407baSopenharmony_ci} 2833d0407baSopenharmony_ci 2843d0407baSopenharmony_ci/** 2853d0407baSopenharmony_ci * try_then_request_governor() - Try to find the governor and request the 2863d0407baSopenharmony_ci * module if is not found. 2873d0407baSopenharmony_ci * @name: name of the governor 2883d0407baSopenharmony_ci * 2893d0407baSopenharmony_ci * Search the list of devfreq governors and request the module and try again 2903d0407baSopenharmony_ci * if is not found. This can happen when both drivers (the governor driver 2913d0407baSopenharmony_ci * and the driver that call devfreq_add_device) are built as modules. 2923d0407baSopenharmony_ci * devfreq_list_lock should be held by the caller. Returns the matched 2933d0407baSopenharmony_ci * governor's pointer or an error pointer. 2943d0407baSopenharmony_ci */ 2953d0407baSopenharmony_cistatic struct devfreq_governor *try_then_request_governor(const char *name) 2963d0407baSopenharmony_ci{ 2973d0407baSopenharmony_ci struct devfreq_governor *governor; 2983d0407baSopenharmony_ci int err = 0; 2993d0407baSopenharmony_ci 3003d0407baSopenharmony_ci lockdep_assert_held(&devfreq_list_lock); 3013d0407baSopenharmony_ci 3023d0407baSopenharmony_ci if (IS_ERR_OR_NULL(name)) { 3033d0407baSopenharmony_ci pr_err("DEVFREQ: %s: Invalid parameters\n", __func__); 3043d0407baSopenharmony_ci return ERR_PTR(-EINVAL); 3053d0407baSopenharmony_ci } 3063d0407baSopenharmony_ci 3073d0407baSopenharmony_ci governor = find_devfreq_governor(name); 3083d0407baSopenharmony_ci if (IS_ERR(governor)) { 3093d0407baSopenharmony_ci mutex_unlock(&devfreq_list_lock); 3103d0407baSopenharmony_ci 3113d0407baSopenharmony_ci if (!strncmp(name, DEVFREQ_GOV_SIMPLE_ONDEMAND, DEVFREQ_NAME_LEN)) { 3123d0407baSopenharmony_ci err = request_module("governor_%s", "simpleondemand"); 3133d0407baSopenharmony_ci } else { 3143d0407baSopenharmony_ci err = request_module("governor_%s", name); 3153d0407baSopenharmony_ci } 3163d0407baSopenharmony_ci /* Restore previous state before return */ 3173d0407baSopenharmony_ci mutex_lock(&devfreq_list_lock); 3183d0407baSopenharmony_ci if (err) { 3193d0407baSopenharmony_ci return (err < 0) ? ERR_PTR(err) : ERR_PTR(-EINVAL); 3203d0407baSopenharmony_ci } 3213d0407baSopenharmony_ci 3223d0407baSopenharmony_ci governor = find_devfreq_governor(name); 3233d0407baSopenharmony_ci } 3243d0407baSopenharmony_ci 3253d0407baSopenharmony_ci return governor; 3263d0407baSopenharmony_ci} 3273d0407baSopenharmony_ci 3283d0407baSopenharmony_cistatic int devfreq_notify_transition(struct devfreq *devfreq, struct devfreq_freqs *freqs, unsigned int state) 3293d0407baSopenharmony_ci{ 3303d0407baSopenharmony_ci if (!devfreq) { 3313d0407baSopenharmony_ci return -EINVAL; 3323d0407baSopenharmony_ci } 3333d0407baSopenharmony_ci 3343d0407baSopenharmony_ci switch (state) { 3353d0407baSopenharmony_ci case DEVFREQ_PRECHANGE: 3363d0407baSopenharmony_ci srcu_notifier_call_chain(&devfreq->transition_notifier_list, DEVFREQ_PRECHANGE, freqs); 3373d0407baSopenharmony_ci break; 3383d0407baSopenharmony_ci 3393d0407baSopenharmony_ci case DEVFREQ_POSTCHANGE: 3403d0407baSopenharmony_ci srcu_notifier_call_chain(&devfreq->transition_notifier_list, DEVFREQ_POSTCHANGE, freqs); 3413d0407baSopenharmony_ci break; 3423d0407baSopenharmony_ci default: 3433d0407baSopenharmony_ci return -EINVAL; 3443d0407baSopenharmony_ci } 3453d0407baSopenharmony_ci 3463d0407baSopenharmony_ci return 0; 3473d0407baSopenharmony_ci} 3483d0407baSopenharmony_ci 3493d0407baSopenharmony_cistatic int devfreq_set_target(struct devfreq *devfreq, unsigned long new_freq, u32 flags) 3503d0407baSopenharmony_ci{ 3513d0407baSopenharmony_ci struct devfreq_freqs freqs; 3523d0407baSopenharmony_ci unsigned long cur_freq; 3533d0407baSopenharmony_ci int err = 0; 3543d0407baSopenharmony_ci 3553d0407baSopenharmony_ci if (devfreq->profile->get_cur_freq) { 3563d0407baSopenharmony_ci devfreq->profile->get_cur_freq(devfreq->dev.parent, &cur_freq); 3573d0407baSopenharmony_ci } else { 3583d0407baSopenharmony_ci cur_freq = devfreq->previous_freq; 3593d0407baSopenharmony_ci } 3603d0407baSopenharmony_ci 3613d0407baSopenharmony_ci freqs.old = cur_freq; 3623d0407baSopenharmony_ci freqs.new = new_freq; 3633d0407baSopenharmony_ci devfreq_notify_transition(devfreq, &freqs, DEVFREQ_PRECHANGE); 3643d0407baSopenharmony_ci 3653d0407baSopenharmony_ci err = devfreq->profile->target(devfreq->dev.parent, &new_freq, flags); 3663d0407baSopenharmony_ci if (err) { 3673d0407baSopenharmony_ci freqs.new = cur_freq; 3683d0407baSopenharmony_ci devfreq_notify_transition(devfreq, &freqs, DEVFREQ_POSTCHANGE); 3693d0407baSopenharmony_ci return err; 3703d0407baSopenharmony_ci } 3713d0407baSopenharmony_ci 3723d0407baSopenharmony_ci freqs.new = new_freq; 3733d0407baSopenharmony_ci devfreq_notify_transition(devfreq, &freqs, DEVFREQ_POSTCHANGE); 3743d0407baSopenharmony_ci 3753d0407baSopenharmony_ci if (devfreq_update_status(devfreq, new_freq)) { 3763d0407baSopenharmony_ci dev_err(&devfreq->dev, "Couldn't update frequency transition information.\n"); 3773d0407baSopenharmony_ci } 3783d0407baSopenharmony_ci 3793d0407baSopenharmony_ci devfreq->previous_freq = new_freq; 3803d0407baSopenharmony_ci 3813d0407baSopenharmony_ci if (devfreq->suspend_freq) { 3823d0407baSopenharmony_ci devfreq->resume_freq = new_freq; 3833d0407baSopenharmony_ci } 3843d0407baSopenharmony_ci 3853d0407baSopenharmony_ci return err; 3863d0407baSopenharmony_ci} 3873d0407baSopenharmony_ci 3883d0407baSopenharmony_ci/* Load monitoring helper functions for governors use */ 3893d0407baSopenharmony_ci 3903d0407baSopenharmony_ci/** 3913d0407baSopenharmony_ci * update_devfreq() - Reevaluate the device and configure frequency. 3923d0407baSopenharmony_ci * @devfreq: the devfreq instance. 3933d0407baSopenharmony_ci * 3943d0407baSopenharmony_ci * Note: Lock devfreq->lock before calling update_devfreq 3953d0407baSopenharmony_ci * This function is exported for governors. 3963d0407baSopenharmony_ci */ 3973d0407baSopenharmony_ciint update_devfreq(struct devfreq *devfreq) 3983d0407baSopenharmony_ci{ 3993d0407baSopenharmony_ci unsigned long freq, min_freq, max_freq; 4003d0407baSopenharmony_ci int err = 0; 4013d0407baSopenharmony_ci u32 flags = 0; 4023d0407baSopenharmony_ci 4033d0407baSopenharmony_ci lockdep_assert_held(&devfreq->lock); 4043d0407baSopenharmony_ci 4053d0407baSopenharmony_ci if (!devfreq->governor) { 4063d0407baSopenharmony_ci return -EINVAL; 4073d0407baSopenharmony_ci } 4083d0407baSopenharmony_ci 4093d0407baSopenharmony_ci /* Reevaluate the proper frequency */ 4103d0407baSopenharmony_ci err = devfreq->governor->get_target_freq(devfreq, &freq); 4113d0407baSopenharmony_ci if (err) { 4123d0407baSopenharmony_ci return err; 4133d0407baSopenharmony_ci } 4143d0407baSopenharmony_ci get_freq_range(devfreq, &min_freq, &max_freq); 4153d0407baSopenharmony_ci 4163d0407baSopenharmony_ci if (freq < min_freq) { 4173d0407baSopenharmony_ci freq = min_freq; 4183d0407baSopenharmony_ci flags &= ~DEVFREQ_FLAG_LEAST_UPPER_BOUND; /* Use GLB */ 4193d0407baSopenharmony_ci } 4203d0407baSopenharmony_ci if (freq > max_freq) { 4213d0407baSopenharmony_ci freq = max_freq; 4223d0407baSopenharmony_ci flags |= DEVFREQ_FLAG_LEAST_UPPER_BOUND; /* Use LUB */ 4233d0407baSopenharmony_ci } 4243d0407baSopenharmony_ci 4253d0407baSopenharmony_ci return devfreq_set_target(devfreq, freq, flags); 4263d0407baSopenharmony_ci} 4273d0407baSopenharmony_ciEXPORT_SYMBOL(update_devfreq); 4283d0407baSopenharmony_ci 4293d0407baSopenharmony_ci/** 4303d0407baSopenharmony_ci * devfreq_monitor() - Periodically poll devfreq objects. 4313d0407baSopenharmony_ci * @work: the work struct used to run devfreq_monitor periodically. 4323d0407baSopenharmony_ci * 4333d0407baSopenharmony_ci */ 4343d0407baSopenharmony_cistatic void devfreq_monitor(struct work_struct *work) 4353d0407baSopenharmony_ci{ 4363d0407baSopenharmony_ci int err; 4373d0407baSopenharmony_ci struct devfreq *devfreq = container_of(work, struct devfreq, work.work); 4383d0407baSopenharmony_ci 4393d0407baSopenharmony_ci mutex_lock(&devfreq->lock); 4403d0407baSopenharmony_ci err = update_devfreq(devfreq); 4413d0407baSopenharmony_ci if (err) { 4423d0407baSopenharmony_ci dev_err(&devfreq->dev, "dvfs failed with (%d) error\n", err); 4433d0407baSopenharmony_ci } 4443d0407baSopenharmony_ci 4453d0407baSopenharmony_ci queue_delayed_work(devfreq_wq, &devfreq->work, msecs_to_jiffies(devfreq->profile->polling_ms)); 4463d0407baSopenharmony_ci mutex_unlock(&devfreq->lock); 4473d0407baSopenharmony_ci 4483d0407baSopenharmony_ci trace_devfreq_monitor(devfreq); 4493d0407baSopenharmony_ci} 4503d0407baSopenharmony_ci 4513d0407baSopenharmony_ci/** 4523d0407baSopenharmony_ci * devfreq_monitor_start() - Start load monitoring of devfreq instance 4533d0407baSopenharmony_ci * @devfreq: the devfreq instance. 4543d0407baSopenharmony_ci * 4553d0407baSopenharmony_ci * Helper function for starting devfreq device load monitoring. By 4563d0407baSopenharmony_ci * default delayed work based monitoring is supported. Function 4573d0407baSopenharmony_ci * to be called from governor in response to DEVFREQ_GOV_START 4583d0407baSopenharmony_ci * event when device is added to devfreq framework. 4593d0407baSopenharmony_ci */ 4603d0407baSopenharmony_civoid devfreq_monitor_start(struct devfreq *devfreq) 4613d0407baSopenharmony_ci{ 4623d0407baSopenharmony_ci if (devfreq->governor->interrupt_driven) { 4633d0407baSopenharmony_ci return; 4643d0407baSopenharmony_ci } 4653d0407baSopenharmony_ci 4663d0407baSopenharmony_ci switch (devfreq->profile->timer) { 4673d0407baSopenharmony_ci case DEVFREQ_TIMER_DEFERRABLE: 4683d0407baSopenharmony_ci INIT_DEFERRABLE_WORK(&devfreq->work, devfreq_monitor); 4693d0407baSopenharmony_ci break; 4703d0407baSopenharmony_ci case DEVFREQ_TIMER_DELAYED: 4713d0407baSopenharmony_ci INIT_DELAYED_WORK(&devfreq->work, devfreq_monitor); 4723d0407baSopenharmony_ci break; 4733d0407baSopenharmony_ci default: 4743d0407baSopenharmony_ci return; 4753d0407baSopenharmony_ci } 4763d0407baSopenharmony_ci 4773d0407baSopenharmony_ci if (devfreq->profile->polling_ms) { 4783d0407baSopenharmony_ci queue_delayed_work(devfreq_wq, &devfreq->work, msecs_to_jiffies(devfreq->profile->polling_ms)); 4793d0407baSopenharmony_ci } 4803d0407baSopenharmony_ci} 4813d0407baSopenharmony_ciEXPORT_SYMBOL(devfreq_monitor_start); 4823d0407baSopenharmony_ci 4833d0407baSopenharmony_ci/** 4843d0407baSopenharmony_ci * devfreq_monitor_stop() - Stop load monitoring of a devfreq instance 4853d0407baSopenharmony_ci * @devfreq: the devfreq instance. 4863d0407baSopenharmony_ci * 4873d0407baSopenharmony_ci * Helper function to stop devfreq device load monitoring. Function 4883d0407baSopenharmony_ci * to be called from governor in response to DEVFREQ_GOV_STOP 4893d0407baSopenharmony_ci * event when device is removed from devfreq framework. 4903d0407baSopenharmony_ci */ 4913d0407baSopenharmony_civoid devfreq_monitor_stop(struct devfreq *devfreq) 4923d0407baSopenharmony_ci{ 4933d0407baSopenharmony_ci if (devfreq->governor->interrupt_driven) { 4943d0407baSopenharmony_ci return; 4953d0407baSopenharmony_ci } 4963d0407baSopenharmony_ci 4973d0407baSopenharmony_ci cancel_delayed_work_sync(&devfreq->work); 4983d0407baSopenharmony_ci} 4993d0407baSopenharmony_ciEXPORT_SYMBOL(devfreq_monitor_stop); 5003d0407baSopenharmony_ci 5013d0407baSopenharmony_ci/** 5023d0407baSopenharmony_ci * devfreq_monitor_suspend() - Suspend load monitoring of a devfreq instance 5033d0407baSopenharmony_ci * @devfreq: the devfreq instance. 5043d0407baSopenharmony_ci * 5053d0407baSopenharmony_ci * Helper function to suspend devfreq device load monitoring. Function 5063d0407baSopenharmony_ci * to be called from governor in response to DEVFREQ_GOV_SUSPEND 5073d0407baSopenharmony_ci * event or when polling interval is set to zero. 5083d0407baSopenharmony_ci * 5093d0407baSopenharmony_ci * Note: Though this function is same as devfreq_monitor_stop(), 5103d0407baSopenharmony_ci * intentionally kept separate to provide hooks for collecting 5113d0407baSopenharmony_ci * transition statistics. 5123d0407baSopenharmony_ci */ 5133d0407baSopenharmony_civoid devfreq_monitor_suspend(struct devfreq *devfreq) 5143d0407baSopenharmony_ci{ 5153d0407baSopenharmony_ci mutex_lock(&devfreq->lock); 5163d0407baSopenharmony_ci if (devfreq->stop_polling) { 5173d0407baSopenharmony_ci mutex_unlock(&devfreq->lock); 5183d0407baSopenharmony_ci return; 5193d0407baSopenharmony_ci } 5203d0407baSopenharmony_ci 5213d0407baSopenharmony_ci devfreq_update_status(devfreq, devfreq->previous_freq); 5223d0407baSopenharmony_ci devfreq->stop_polling = true; 5233d0407baSopenharmony_ci mutex_unlock(&devfreq->lock); 5243d0407baSopenharmony_ci 5253d0407baSopenharmony_ci if (devfreq->governor->interrupt_driven) { 5263d0407baSopenharmony_ci return; 5273d0407baSopenharmony_ci } 5283d0407baSopenharmony_ci 5293d0407baSopenharmony_ci cancel_delayed_work_sync(&devfreq->work); 5303d0407baSopenharmony_ci} 5313d0407baSopenharmony_ciEXPORT_SYMBOL(devfreq_monitor_suspend); 5323d0407baSopenharmony_ci 5333d0407baSopenharmony_ci/** 5343d0407baSopenharmony_ci * devfreq_monitor_resume() - Resume load monitoring of a devfreq instance 5353d0407baSopenharmony_ci * @devfreq: the devfreq instance. 5363d0407baSopenharmony_ci * 5373d0407baSopenharmony_ci * Helper function to resume devfreq device load monitoring. Function 5383d0407baSopenharmony_ci * to be called from governor in response to DEVFREQ_GOV_RESUME 5393d0407baSopenharmony_ci * event or when polling interval is set to non-zero. 5403d0407baSopenharmony_ci */ 5413d0407baSopenharmony_civoid devfreq_monitor_resume(struct devfreq *devfreq) 5423d0407baSopenharmony_ci{ 5433d0407baSopenharmony_ci unsigned long freq; 5443d0407baSopenharmony_ci 5453d0407baSopenharmony_ci mutex_lock(&devfreq->lock); 5463d0407baSopenharmony_ci if (!devfreq->stop_polling) { 5473d0407baSopenharmony_ci goto out; 5483d0407baSopenharmony_ci } 5493d0407baSopenharmony_ci 5503d0407baSopenharmony_ci if (devfreq->governor->interrupt_driven) { 5513d0407baSopenharmony_ci goto out_update; 5523d0407baSopenharmony_ci } 5533d0407baSopenharmony_ci 5543d0407baSopenharmony_ci if (!delayed_work_pending(&devfreq->work) && devfreq->profile->polling_ms) { 5553d0407baSopenharmony_ci queue_delayed_work(devfreq_wq, &devfreq->work, msecs_to_jiffies(devfreq->profile->polling_ms)); 5563d0407baSopenharmony_ci } 5573d0407baSopenharmony_ci 5583d0407baSopenharmony_ciout_update: 5593d0407baSopenharmony_ci devfreq->stats.last_update = get_jiffies_64(); 5603d0407baSopenharmony_ci devfreq->stop_polling = false; 5613d0407baSopenharmony_ci 5623d0407baSopenharmony_ci if (devfreq->profile->get_cur_freq && !devfreq->profile->get_cur_freq(devfreq->dev.parent, &freq)) { 5633d0407baSopenharmony_ci devfreq->previous_freq = freq; 5643d0407baSopenharmony_ci } 5653d0407baSopenharmony_ci 5663d0407baSopenharmony_ciout: 5673d0407baSopenharmony_ci mutex_unlock(&devfreq->lock); 5683d0407baSopenharmony_ci} 5693d0407baSopenharmony_ciEXPORT_SYMBOL(devfreq_monitor_resume); 5703d0407baSopenharmony_ci 5713d0407baSopenharmony_ci/** 5723d0407baSopenharmony_ci * devfreq_update_interval() - Update device devfreq monitoring interval 5733d0407baSopenharmony_ci * @devfreq: the devfreq instance. 5743d0407baSopenharmony_ci * @delay: new polling interval to be set. 5753d0407baSopenharmony_ci * 5763d0407baSopenharmony_ci * Helper function to set new load monitoring polling interval. Function 5773d0407baSopenharmony_ci * to be called from governor in response to DEVFREQ_GOV_UPDATE_INTERVAL event. 5783d0407baSopenharmony_ci */ 5793d0407baSopenharmony_civoid devfreq_update_interval(struct devfreq *devfreq, unsigned int *delay) 5803d0407baSopenharmony_ci{ 5813d0407baSopenharmony_ci unsigned int cur_delay = devfreq->profile->polling_ms; 5823d0407baSopenharmony_ci unsigned int new_delay = *delay; 5833d0407baSopenharmony_ci 5843d0407baSopenharmony_ci mutex_lock(&devfreq->lock); 5853d0407baSopenharmony_ci devfreq->profile->polling_ms = new_delay; 5863d0407baSopenharmony_ci 5873d0407baSopenharmony_ci if (devfreq->stop_polling) { 5883d0407baSopenharmony_ci goto out; 5893d0407baSopenharmony_ci } 5903d0407baSopenharmony_ci 5913d0407baSopenharmony_ci if (devfreq->governor->interrupt_driven) { 5923d0407baSopenharmony_ci goto out; 5933d0407baSopenharmony_ci } 5943d0407baSopenharmony_ci 5953d0407baSopenharmony_ci /* if new delay is zero, stop polling */ 5963d0407baSopenharmony_ci if (!new_delay) { 5973d0407baSopenharmony_ci mutex_unlock(&devfreq->lock); 5983d0407baSopenharmony_ci cancel_delayed_work_sync(&devfreq->work); 5993d0407baSopenharmony_ci return; 6003d0407baSopenharmony_ci } 6013d0407baSopenharmony_ci 6023d0407baSopenharmony_ci /* if current delay is zero, start polling with new delay */ 6033d0407baSopenharmony_ci if (!cur_delay) { 6043d0407baSopenharmony_ci queue_delayed_work(devfreq_wq, &devfreq->work, msecs_to_jiffies(devfreq->profile->polling_ms)); 6053d0407baSopenharmony_ci goto out; 6063d0407baSopenharmony_ci } 6073d0407baSopenharmony_ci 6083d0407baSopenharmony_ci /* if current delay is greater than new delay, restart polling */ 6093d0407baSopenharmony_ci if (cur_delay > new_delay) { 6103d0407baSopenharmony_ci mutex_unlock(&devfreq->lock); 6113d0407baSopenharmony_ci cancel_delayed_work_sync(&devfreq->work); 6123d0407baSopenharmony_ci mutex_lock(&devfreq->lock); 6133d0407baSopenharmony_ci if (!devfreq->stop_polling) { 6143d0407baSopenharmony_ci queue_delayed_work(devfreq_wq, &devfreq->work, msecs_to_jiffies(devfreq->profile->polling_ms)); 6153d0407baSopenharmony_ci } 6163d0407baSopenharmony_ci } 6173d0407baSopenharmony_ciout: 6183d0407baSopenharmony_ci mutex_unlock(&devfreq->lock); 6193d0407baSopenharmony_ci} 6203d0407baSopenharmony_ciEXPORT_SYMBOL(devfreq_update_interval); 6213d0407baSopenharmony_ci 6223d0407baSopenharmony_ci/** 6233d0407baSopenharmony_ci * devfreq_notifier_call() - Notify that the device frequency requirements 6243d0407baSopenharmony_ci * has been changed out of devfreq framework. 6253d0407baSopenharmony_ci * @nb: the notifier_block (supposed to be devfreq->nb) 6263d0407baSopenharmony_ci * @type: not used 6273d0407baSopenharmony_ci * @devp: not used 6283d0407baSopenharmony_ci * 6293d0407baSopenharmony_ci * Called by a notifier that uses devfreq->nb. 6303d0407baSopenharmony_ci */ 6313d0407baSopenharmony_cistatic int devfreq_notifier_call(struct notifier_block *nb, unsigned long type, void *devp) 6323d0407baSopenharmony_ci{ 6333d0407baSopenharmony_ci struct devfreq *devfreq = container_of(nb, struct devfreq, nb); 6343d0407baSopenharmony_ci int err = -EINVAL; 6353d0407baSopenharmony_ci 6363d0407baSopenharmony_ci mutex_lock(&devfreq->lock); 6373d0407baSopenharmony_ci 6383d0407baSopenharmony_ci devfreq->scaling_min_freq = find_available_min_freq(devfreq); 6393d0407baSopenharmony_ci if (!devfreq->scaling_min_freq) { 6403d0407baSopenharmony_ci goto out; 6413d0407baSopenharmony_ci } 6423d0407baSopenharmony_ci 6433d0407baSopenharmony_ci devfreq->scaling_max_freq = find_available_max_freq(devfreq); 6443d0407baSopenharmony_ci if (!devfreq->scaling_max_freq) { 6453d0407baSopenharmony_ci devfreq->scaling_max_freq = ULONG_MAX; 6463d0407baSopenharmony_ci goto out; 6473d0407baSopenharmony_ci } 6483d0407baSopenharmony_ci 6493d0407baSopenharmony_ci err = update_devfreq(devfreq); 6503d0407baSopenharmony_ci 6513d0407baSopenharmony_ciout: 6523d0407baSopenharmony_ci mutex_unlock(&devfreq->lock); 6533d0407baSopenharmony_ci if (err) { 6543d0407baSopenharmony_ci dev_err(devfreq->dev.parent, "failed to update frequency from OPP notifier (%d)\n", err); 6553d0407baSopenharmony_ci } 6563d0407baSopenharmony_ci 6573d0407baSopenharmony_ci return NOTIFY_OK; 6583d0407baSopenharmony_ci} 6593d0407baSopenharmony_ci 6603d0407baSopenharmony_ci/** 6613d0407baSopenharmony_ci * qos_notifier_call() - Common handler for QoS constraints. 6623d0407baSopenharmony_ci * @devfreq: the devfreq instance. 6633d0407baSopenharmony_ci */ 6643d0407baSopenharmony_cistatic int qos_notifier_call(struct devfreq *devfreq) 6653d0407baSopenharmony_ci{ 6663d0407baSopenharmony_ci int err; 6673d0407baSopenharmony_ci 6683d0407baSopenharmony_ci mutex_lock(&devfreq->lock); 6693d0407baSopenharmony_ci err = update_devfreq(devfreq); 6703d0407baSopenharmony_ci mutex_unlock(&devfreq->lock); 6713d0407baSopenharmony_ci if (err) { 6723d0407baSopenharmony_ci dev_err(devfreq->dev.parent, "failed to update frequency from PM QoS (%d)\n", err); 6733d0407baSopenharmony_ci } 6743d0407baSopenharmony_ci 6753d0407baSopenharmony_ci return NOTIFY_OK; 6763d0407baSopenharmony_ci} 6773d0407baSopenharmony_ci 6783d0407baSopenharmony_ci/** 6793d0407baSopenharmony_ci * qos_min_notifier_call() - Callback for QoS min_freq changes. 6803d0407baSopenharmony_ci * @nb: Should be devfreq->nb_min 6813d0407baSopenharmony_ci */ 6823d0407baSopenharmony_cistatic int qos_min_notifier_call(struct notifier_block *nb, unsigned long val, void *ptr) 6833d0407baSopenharmony_ci{ 6843d0407baSopenharmony_ci return qos_notifier_call(container_of(nb, struct devfreq, nb_min)); 6853d0407baSopenharmony_ci} 6863d0407baSopenharmony_ci 6873d0407baSopenharmony_ci/** 6883d0407baSopenharmony_ci * qos_max_notifier_call() - Callback for QoS max_freq changes. 6893d0407baSopenharmony_ci * @nb: Should be devfreq->nb_max 6903d0407baSopenharmony_ci */ 6913d0407baSopenharmony_cistatic int qos_max_notifier_call(struct notifier_block *nb, unsigned long val, void *ptr) 6923d0407baSopenharmony_ci{ 6933d0407baSopenharmony_ci return qos_notifier_call(container_of(nb, struct devfreq, nb_max)); 6943d0407baSopenharmony_ci} 6953d0407baSopenharmony_ci 6963d0407baSopenharmony_ci/** 6973d0407baSopenharmony_ci * devfreq_dev_release() - Callback for struct device to release the device. 6983d0407baSopenharmony_ci * @dev: the devfreq device 6993d0407baSopenharmony_ci * 7003d0407baSopenharmony_ci * Remove devfreq from the list and release its resources. 7013d0407baSopenharmony_ci */ 7023d0407baSopenharmony_cistatic void devfreq_dev_release(struct device *dev) 7033d0407baSopenharmony_ci{ 7043d0407baSopenharmony_ci struct devfreq *devfreq = to_devfreq(dev); 7053d0407baSopenharmony_ci int err; 7063d0407baSopenharmony_ci 7073d0407baSopenharmony_ci mutex_lock(&devfreq_list_lock); 7083d0407baSopenharmony_ci list_del(&devfreq->node); 7093d0407baSopenharmony_ci mutex_unlock(&devfreq_list_lock); 7103d0407baSopenharmony_ci 7113d0407baSopenharmony_ci err = dev_pm_qos_remove_notifier(devfreq->dev.parent, &devfreq->nb_max, DEV_PM_QOS_MAX_FREQUENCY); 7123d0407baSopenharmony_ci if (err && err != -ENOENT) { 7133d0407baSopenharmony_ci dev_warn(dev->parent, "Failed to remove max_freq notifier: %d\n", err); 7143d0407baSopenharmony_ci } 7153d0407baSopenharmony_ci err = dev_pm_qos_remove_notifier(devfreq->dev.parent, &devfreq->nb_min, DEV_PM_QOS_MIN_FREQUENCY); 7163d0407baSopenharmony_ci if (err && err != -ENOENT) { 7173d0407baSopenharmony_ci dev_warn(dev->parent, "Failed to remove min_freq notifier: %d\n", err); 7183d0407baSopenharmony_ci } 7193d0407baSopenharmony_ci 7203d0407baSopenharmony_ci if (dev_pm_qos_request_active(&devfreq->user_max_freq_req)) { 7213d0407baSopenharmony_ci err = dev_pm_qos_remove_request(&devfreq->user_max_freq_req); 7223d0407baSopenharmony_ci if (err < 0) { 7233d0407baSopenharmony_ci dev_warn(dev->parent, "Failed to remove max_freq request: %d\n", err); 7243d0407baSopenharmony_ci } 7253d0407baSopenharmony_ci } 7263d0407baSopenharmony_ci if (dev_pm_qos_request_active(&devfreq->user_min_freq_req)) { 7273d0407baSopenharmony_ci err = dev_pm_qos_remove_request(&devfreq->user_min_freq_req); 7283d0407baSopenharmony_ci if (err < 0) { 7293d0407baSopenharmony_ci dev_warn(dev->parent, "Failed to remove min_freq request: %d\n", err); 7303d0407baSopenharmony_ci } 7313d0407baSopenharmony_ci } 7323d0407baSopenharmony_ci 7333d0407baSopenharmony_ci if (devfreq->profile->exit) { 7343d0407baSopenharmony_ci devfreq->profile->exit(devfreq->dev.parent); 7353d0407baSopenharmony_ci } 7363d0407baSopenharmony_ci 7373d0407baSopenharmony_ci mutex_destroy(&devfreq->lock); 7383d0407baSopenharmony_ci kfree(devfreq); 7393d0407baSopenharmony_ci} 7403d0407baSopenharmony_ci 7413d0407baSopenharmony_ci/** 7423d0407baSopenharmony_ci * devfreq_remove_device() - Remove devfreq feature from a device. 7433d0407baSopenharmony_ci * @devfreq: the devfreq instance to be removed 7443d0407baSopenharmony_ci * 7453d0407baSopenharmony_ci * The opposite of devfreq_add_device(). 7463d0407baSopenharmony_ci */ 7473d0407baSopenharmony_ciint devfreq_remove_device(struct devfreq *devfreq) 7483d0407baSopenharmony_ci{ 7493d0407baSopenharmony_ci if (!devfreq) { 7503d0407baSopenharmony_ci return -EINVAL; 7513d0407baSopenharmony_ci } 7523d0407baSopenharmony_ci 7533d0407baSopenharmony_ci if (devfreq->governor) { 7543d0407baSopenharmony_ci devfreq->governor->event_handler(devfreq, DEVFREQ_GOV_STOP, NULL); 7553d0407baSopenharmony_ci } 7563d0407baSopenharmony_ci device_unregister(&devfreq->dev); 7573d0407baSopenharmony_ci 7583d0407baSopenharmony_ci return 0; 7593d0407baSopenharmony_ci} 7603d0407baSopenharmony_ciEXPORT_SYMBOL(devfreq_remove_device); 7613d0407baSopenharmony_ci 7623d0407baSopenharmony_ci/** 7633d0407baSopenharmony_ci * devfreq_add_device() - Add devfreq feature to the device 7643d0407baSopenharmony_ci * @dev: the device to add devfreq feature. 7653d0407baSopenharmony_ci * @profile: device-specific profile to run devfreq. 7663d0407baSopenharmony_ci * @governor_name: name of the policy to choose frequency. 7673d0407baSopenharmony_ci * @data: private data for the governor. The devfreq framework does not 7683d0407baSopenharmony_ci * touch this value. 7693d0407baSopenharmony_ci */ 7703d0407baSopenharmony_cistruct devfreq *devfreq_add_device(struct device *dev, struct devfreq_dev_profile *profile, const char *governor_name, 7713d0407baSopenharmony_ci void *data) 7723d0407baSopenharmony_ci{ 7733d0407baSopenharmony_ci struct devfreq *devfreq; 7743d0407baSopenharmony_ci struct devfreq_governor *governor; 7753d0407baSopenharmony_ci int err = 0; 7763d0407baSopenharmony_ci 7773d0407baSopenharmony_ci if (!dev || !profile || !governor_name) { 7783d0407baSopenharmony_ci dev_err(dev, "%s: Invalid parameters.\n", __func__); 7793d0407baSopenharmony_ci return ERR_PTR(-EINVAL); 7803d0407baSopenharmony_ci } 7813d0407baSopenharmony_ci 7823d0407baSopenharmony_ci mutex_lock(&devfreq_list_lock); 7833d0407baSopenharmony_ci devfreq = find_device_devfreq(dev); 7843d0407baSopenharmony_ci mutex_unlock(&devfreq_list_lock); 7853d0407baSopenharmony_ci if (!IS_ERR(devfreq)) { 7863d0407baSopenharmony_ci dev_err(dev, "%s: devfreq device already exists!\n", __func__); 7873d0407baSopenharmony_ci err = -EINVAL; 7883d0407baSopenharmony_ci goto err_out; 7893d0407baSopenharmony_ci } 7903d0407baSopenharmony_ci 7913d0407baSopenharmony_ci devfreq = kzalloc(sizeof(struct devfreq), GFP_KERNEL); 7923d0407baSopenharmony_ci if (!devfreq) { 7933d0407baSopenharmony_ci err = -ENOMEM; 7943d0407baSopenharmony_ci goto err_out; 7953d0407baSopenharmony_ci } 7963d0407baSopenharmony_ci 7973d0407baSopenharmony_ci mutex_init(&devfreq->lock); 7983d0407baSopenharmony_ci mutex_lock(&devfreq->lock); 7993d0407baSopenharmony_ci devfreq->dev.parent = dev; 8003d0407baSopenharmony_ci devfreq->dev.class = devfreq_class; 8013d0407baSopenharmony_ci devfreq->dev.release = devfreq_dev_release; 8023d0407baSopenharmony_ci INIT_LIST_HEAD(&devfreq->node); 8033d0407baSopenharmony_ci devfreq->profile = profile; 8043d0407baSopenharmony_ci strscpy(devfreq->governor_name, governor_name, DEVFREQ_NAME_LEN); 8053d0407baSopenharmony_ci devfreq->previous_freq = profile->initial_freq; 8063d0407baSopenharmony_ci devfreq->last_status.current_frequency = profile->initial_freq; 8073d0407baSopenharmony_ci devfreq->data = data; 8083d0407baSopenharmony_ci devfreq->nb.notifier_call = devfreq_notifier_call; 8093d0407baSopenharmony_ci 8103d0407baSopenharmony_ci if (devfreq->profile->timer < 0 || devfreq->profile->timer >= DEVFREQ_TIMER_NUM) { 8113d0407baSopenharmony_ci mutex_unlock(&devfreq->lock); 8123d0407baSopenharmony_ci err = -EINVAL; 8133d0407baSopenharmony_ci goto err_dev; 8143d0407baSopenharmony_ci } 8153d0407baSopenharmony_ci 8163d0407baSopenharmony_ci if (!devfreq->profile->max_state && !devfreq->profile->freq_table) { 8173d0407baSopenharmony_ci mutex_unlock(&devfreq->lock); 8183d0407baSopenharmony_ci err = set_freq_table(devfreq); 8193d0407baSopenharmony_ci if (err < 0) { 8203d0407baSopenharmony_ci goto err_dev; 8213d0407baSopenharmony_ci } 8223d0407baSopenharmony_ci mutex_lock(&devfreq->lock); 8233d0407baSopenharmony_ci } 8243d0407baSopenharmony_ci 8253d0407baSopenharmony_ci devfreq->scaling_min_freq = find_available_min_freq(devfreq); 8263d0407baSopenharmony_ci if (!devfreq->scaling_min_freq) { 8273d0407baSopenharmony_ci mutex_unlock(&devfreq->lock); 8283d0407baSopenharmony_ci err = -EINVAL; 8293d0407baSopenharmony_ci goto err_dev; 8303d0407baSopenharmony_ci } 8313d0407baSopenharmony_ci 8323d0407baSopenharmony_ci devfreq->scaling_max_freq = find_available_max_freq(devfreq); 8333d0407baSopenharmony_ci if (!devfreq->scaling_max_freq) { 8343d0407baSopenharmony_ci mutex_unlock(&devfreq->lock); 8353d0407baSopenharmony_ci err = -EINVAL; 8363d0407baSopenharmony_ci goto err_dev; 8373d0407baSopenharmony_ci } 8383d0407baSopenharmony_ci 8393d0407baSopenharmony_ci devfreq->suspend_freq = dev_pm_opp_get_suspend_opp_freq(dev); 8403d0407baSopenharmony_ci atomic_set(&devfreq->suspend_count, 0); 8413d0407baSopenharmony_ci 8423d0407baSopenharmony_ci dev_set_name(&devfreq->dev, "%s", dev_name(dev)); 8433d0407baSopenharmony_ci err = device_register(&devfreq->dev); 8443d0407baSopenharmony_ci if (err) { 8453d0407baSopenharmony_ci mutex_unlock(&devfreq->lock); 8463d0407baSopenharmony_ci put_device(&devfreq->dev); 8473d0407baSopenharmony_ci goto err_out; 8483d0407baSopenharmony_ci } 8493d0407baSopenharmony_ci 8503d0407baSopenharmony_ci devfreq->stats.trans_table = devm_kzalloc( 8513d0407baSopenharmony_ci &devfreq->dev, array3_size(sizeof(unsigned int), devfreq->profile->max_state, devfreq->profile->max_state), 8523d0407baSopenharmony_ci GFP_KERNEL); 8533d0407baSopenharmony_ci if (!devfreq->stats.trans_table) { 8543d0407baSopenharmony_ci mutex_unlock(&devfreq->lock); 8553d0407baSopenharmony_ci err = -ENOMEM; 8563d0407baSopenharmony_ci goto err_devfreq; 8573d0407baSopenharmony_ci } 8583d0407baSopenharmony_ci 8593d0407baSopenharmony_ci devfreq->stats.time_in_state = 8603d0407baSopenharmony_ci devm_kcalloc(&devfreq->dev, devfreq->profile->max_state, sizeof(*devfreq->stats.time_in_state), GFP_KERNEL); 8613d0407baSopenharmony_ci if (!devfreq->stats.time_in_state) { 8623d0407baSopenharmony_ci mutex_unlock(&devfreq->lock); 8633d0407baSopenharmony_ci err = -ENOMEM; 8643d0407baSopenharmony_ci goto err_devfreq; 8653d0407baSopenharmony_ci } 8663d0407baSopenharmony_ci 8673d0407baSopenharmony_ci devfreq->stats.total_trans = 0; 8683d0407baSopenharmony_ci devfreq->stats.last_update = get_jiffies_64(); 8693d0407baSopenharmony_ci 8703d0407baSopenharmony_ci srcu_init_notifier_head(&devfreq->transition_notifier_list); 8713d0407baSopenharmony_ci 8723d0407baSopenharmony_ci mutex_unlock(&devfreq->lock); 8733d0407baSopenharmony_ci 8743d0407baSopenharmony_ci err = dev_pm_qos_add_request(dev, &devfreq->user_min_freq_req, DEV_PM_QOS_MIN_FREQUENCY, 0); 8753d0407baSopenharmony_ci if (err < 0) { 8763d0407baSopenharmony_ci goto err_devfreq; 8773d0407baSopenharmony_ci } 8783d0407baSopenharmony_ci err = dev_pm_qos_add_request(dev, &devfreq->user_max_freq_req, DEV_PM_QOS_MAX_FREQUENCY, 8793d0407baSopenharmony_ci PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE); 8803d0407baSopenharmony_ci if (err < 0) { 8813d0407baSopenharmony_ci goto err_devfreq; 8823d0407baSopenharmony_ci } 8833d0407baSopenharmony_ci 8843d0407baSopenharmony_ci devfreq->nb_min.notifier_call = qos_min_notifier_call; 8853d0407baSopenharmony_ci err = dev_pm_qos_add_notifier(devfreq->dev.parent, &devfreq->nb_min, DEV_PM_QOS_MIN_FREQUENCY); 8863d0407baSopenharmony_ci if (err) { 8873d0407baSopenharmony_ci goto err_devfreq; 8883d0407baSopenharmony_ci } 8893d0407baSopenharmony_ci 8903d0407baSopenharmony_ci devfreq->nb_max.notifier_call = qos_max_notifier_call; 8913d0407baSopenharmony_ci err = dev_pm_qos_add_notifier(devfreq->dev.parent, &devfreq->nb_max, DEV_PM_QOS_MAX_FREQUENCY); 8923d0407baSopenharmony_ci if (err) { 8933d0407baSopenharmony_ci goto err_devfreq; 8943d0407baSopenharmony_ci } 8953d0407baSopenharmony_ci 8963d0407baSopenharmony_ci mutex_lock(&devfreq_list_lock); 8973d0407baSopenharmony_ci 8983d0407baSopenharmony_ci governor = try_then_request_governor(devfreq->governor_name); 8993d0407baSopenharmony_ci if (IS_ERR(governor)) { 9003d0407baSopenharmony_ci dev_err(dev, "%s: Unable to find governor for the device\n", __func__); 9013d0407baSopenharmony_ci err = PTR_ERR(governor); 9023d0407baSopenharmony_ci goto err_init; 9033d0407baSopenharmony_ci } 9043d0407baSopenharmony_ci 9053d0407baSopenharmony_ci devfreq->governor = governor; 9063d0407baSopenharmony_ci err = devfreq->governor->event_handler(devfreq, DEVFREQ_GOV_START, NULL); 9073d0407baSopenharmony_ci if (err) { 9083d0407baSopenharmony_ci dev_err(dev, "%s: Unable to start governor for the device\n", __func__); 9093d0407baSopenharmony_ci goto err_init; 9103d0407baSopenharmony_ci } 9113d0407baSopenharmony_ci 9123d0407baSopenharmony_ci list_add(&devfreq->node, &devfreq_list); 9133d0407baSopenharmony_ci 9143d0407baSopenharmony_ci mutex_unlock(&devfreq_list_lock); 9153d0407baSopenharmony_ci 9163d0407baSopenharmony_ci return devfreq; 9173d0407baSopenharmony_ci 9183d0407baSopenharmony_cierr_init: 9193d0407baSopenharmony_ci mutex_unlock(&devfreq_list_lock); 9203d0407baSopenharmony_cierr_devfreq: 9213d0407baSopenharmony_ci devfreq_remove_device(devfreq); 9223d0407baSopenharmony_ci devfreq = NULL; 9233d0407baSopenharmony_cierr_dev: 9243d0407baSopenharmony_ci kfree(devfreq); 9253d0407baSopenharmony_cierr_out: 9263d0407baSopenharmony_ci return ERR_PTR(err); 9273d0407baSopenharmony_ci} 9283d0407baSopenharmony_ciEXPORT_SYMBOL(devfreq_add_device); 9293d0407baSopenharmony_ci 9303d0407baSopenharmony_cistatic int devm_devfreq_dev_match(struct device *dev, void *res, void *data) 9313d0407baSopenharmony_ci{ 9323d0407baSopenharmony_ci struct devfreq **r = res; 9333d0407baSopenharmony_ci 9343d0407baSopenharmony_ci if (WARN_ON(!r || !*r)) { 9353d0407baSopenharmony_ci return 0; 9363d0407baSopenharmony_ci } 9373d0407baSopenharmony_ci 9383d0407baSopenharmony_ci return *r == data; 9393d0407baSopenharmony_ci} 9403d0407baSopenharmony_ci 9413d0407baSopenharmony_cistatic void devm_devfreq_dev_release(struct device *dev, void *res) 9423d0407baSopenharmony_ci{ 9433d0407baSopenharmony_ci devfreq_remove_device(*(struct devfreq **)res); 9443d0407baSopenharmony_ci} 9453d0407baSopenharmony_ci 9463d0407baSopenharmony_ci/** 9473d0407baSopenharmony_ci * devm_devfreq_add_device() - Resource-managed devfreq_add_device() 9483d0407baSopenharmony_ci * @dev: the device to add devfreq feature. 9493d0407baSopenharmony_ci * @profile: device-specific profile to run devfreq. 9503d0407baSopenharmony_ci * @governor_name: name of the policy to choose frequency. 9513d0407baSopenharmony_ci * @data: private data for the governor. The devfreq framework does not 9523d0407baSopenharmony_ci * touch this value. 9533d0407baSopenharmony_ci * 9543d0407baSopenharmony_ci * This function manages automatically the memory of devfreq device using device 9553d0407baSopenharmony_ci * resource management and simplify the free operation for memory of devfreq 9563d0407baSopenharmony_ci * device. 9573d0407baSopenharmony_ci */ 9583d0407baSopenharmony_cistruct devfreq *devm_devfreq_add_device(struct device *dev, struct devfreq_dev_profile *profile, 9593d0407baSopenharmony_ci const char *governor_name, void *data) 9603d0407baSopenharmony_ci{ 9613d0407baSopenharmony_ci struct devfreq **ptr, *devfreq; 9623d0407baSopenharmony_ci 9633d0407baSopenharmony_ci ptr = devres_alloc(devm_devfreq_dev_release, sizeof(*ptr), GFP_KERNEL); 9643d0407baSopenharmony_ci if (!ptr) { 9653d0407baSopenharmony_ci return ERR_PTR(-ENOMEM); 9663d0407baSopenharmony_ci } 9673d0407baSopenharmony_ci 9683d0407baSopenharmony_ci devfreq = devfreq_add_device(dev, profile, governor_name, data); 9693d0407baSopenharmony_ci if (IS_ERR(devfreq)) { 9703d0407baSopenharmony_ci devres_free(ptr); 9713d0407baSopenharmony_ci return devfreq; 9723d0407baSopenharmony_ci } 9733d0407baSopenharmony_ci 9743d0407baSopenharmony_ci *ptr = devfreq; 9753d0407baSopenharmony_ci devres_add(dev, ptr); 9763d0407baSopenharmony_ci 9773d0407baSopenharmony_ci return devfreq; 9783d0407baSopenharmony_ci} 9793d0407baSopenharmony_ciEXPORT_SYMBOL(devm_devfreq_add_device); 9803d0407baSopenharmony_ci 9813d0407baSopenharmony_ci#ifdef CONFIG_OF 9823d0407baSopenharmony_ci/* 9833d0407baSopenharmony_ci * devfreq_get_devfreq_by_node - Get the devfreq device from devicetree 9843d0407baSopenharmony_ci * @node - pointer to device_node 9853d0407baSopenharmony_ci * 9863d0407baSopenharmony_ci * return the instance of devfreq device 9873d0407baSopenharmony_ci */ 9883d0407baSopenharmony_cistruct devfreq *devfreq_get_devfreq_by_node(struct device_node *node) 9893d0407baSopenharmony_ci{ 9903d0407baSopenharmony_ci struct devfreq *devfreq; 9913d0407baSopenharmony_ci 9923d0407baSopenharmony_ci if (!node) { 9933d0407baSopenharmony_ci return ERR_PTR(-EINVAL); 9943d0407baSopenharmony_ci } 9953d0407baSopenharmony_ci 9963d0407baSopenharmony_ci mutex_lock(&devfreq_list_lock); 9973d0407baSopenharmony_ci list_for_each_entry(devfreq, &devfreq_list, node) 9983d0407baSopenharmony_ci { 9993d0407baSopenharmony_ci if (devfreq->dev.parent && devfreq->dev.parent->of_node == node) { 10003d0407baSopenharmony_ci mutex_unlock(&devfreq_list_lock); 10013d0407baSopenharmony_ci return devfreq; 10023d0407baSopenharmony_ci } 10033d0407baSopenharmony_ci } 10043d0407baSopenharmony_ci mutex_unlock(&devfreq_list_lock); 10053d0407baSopenharmony_ci 10063d0407baSopenharmony_ci return ERR_PTR(-ENODEV); 10073d0407baSopenharmony_ci} 10083d0407baSopenharmony_ci 10093d0407baSopenharmony_ci/* 10103d0407baSopenharmony_ci * devfreq_get_devfreq_by_phandle - Get the devfreq device from devicetree 10113d0407baSopenharmony_ci * @dev - instance to the given device 10123d0407baSopenharmony_ci * @phandle_name - name of property holding a phandle value 10133d0407baSopenharmony_ci * @index - index into list of devfreq 10143d0407baSopenharmony_ci * 10153d0407baSopenharmony_ci * return the instance of devfreq device 10163d0407baSopenharmony_ci */ 10173d0407baSopenharmony_cistruct devfreq *devfreq_get_devfreq_by_phandle(struct device *dev, const char *phandle_name, int index) 10183d0407baSopenharmony_ci{ 10193d0407baSopenharmony_ci struct device_node *node; 10203d0407baSopenharmony_ci struct devfreq *devfreq; 10213d0407baSopenharmony_ci 10223d0407baSopenharmony_ci if (!dev || !phandle_name) { 10233d0407baSopenharmony_ci return ERR_PTR(-EINVAL); 10243d0407baSopenharmony_ci } 10253d0407baSopenharmony_ci 10263d0407baSopenharmony_ci if (!dev->of_node) { 10273d0407baSopenharmony_ci return ERR_PTR(-EINVAL); 10283d0407baSopenharmony_ci } 10293d0407baSopenharmony_ci 10303d0407baSopenharmony_ci node = of_parse_phandle(dev->of_node, phandle_name, index); 10313d0407baSopenharmony_ci if (!node) { 10323d0407baSopenharmony_ci return ERR_PTR(-ENODEV); 10333d0407baSopenharmony_ci } 10343d0407baSopenharmony_ci 10353d0407baSopenharmony_ci devfreq = devfreq_get_devfreq_by_node(node); 10363d0407baSopenharmony_ci of_node_put(node); 10373d0407baSopenharmony_ci 10383d0407baSopenharmony_ci return devfreq; 10393d0407baSopenharmony_ci} 10403d0407baSopenharmony_ci 10413d0407baSopenharmony_ci#else 10423d0407baSopenharmony_cistruct devfreq *devfreq_get_devfreq_by_node(struct device_node *node) 10433d0407baSopenharmony_ci{ 10443d0407baSopenharmony_ci return ERR_PTR(-ENODEV); 10453d0407baSopenharmony_ci} 10463d0407baSopenharmony_ci 10473d0407baSopenharmony_cistruct devfreq *devfreq_get_devfreq_by_phandle(struct device *dev, const char *phandle_name, int index) 10483d0407baSopenharmony_ci{ 10493d0407baSopenharmony_ci return ERR_PTR(-ENODEV); 10503d0407baSopenharmony_ci} 10513d0407baSopenharmony_ci#endif /* CONFIG_OF */ 10523d0407baSopenharmony_ciEXPORT_SYMBOL_GPL(devfreq_get_devfreq_by_node); 10533d0407baSopenharmony_ciEXPORT_SYMBOL_GPL(devfreq_get_devfreq_by_phandle); 10543d0407baSopenharmony_ci 10553d0407baSopenharmony_ci/** 10563d0407baSopenharmony_ci * devm_devfreq_remove_device() - Resource-managed devfreq_remove_device() 10573d0407baSopenharmony_ci * @dev: the device from which to remove devfreq feature. 10583d0407baSopenharmony_ci * @devfreq: the devfreq instance to be removed 10593d0407baSopenharmony_ci */ 10603d0407baSopenharmony_civoid devm_devfreq_remove_device(struct device *dev, struct devfreq *devfreq) 10613d0407baSopenharmony_ci{ 10623d0407baSopenharmony_ci WARN_ON(devres_release(dev, devm_devfreq_dev_release, devm_devfreq_dev_match, devfreq)); 10633d0407baSopenharmony_ci} 10643d0407baSopenharmony_ciEXPORT_SYMBOL(devm_devfreq_remove_device); 10653d0407baSopenharmony_ci 10663d0407baSopenharmony_ci/** 10673d0407baSopenharmony_ci * devfreq_suspend_device() - Suspend devfreq of a device. 10683d0407baSopenharmony_ci * @devfreq: the devfreq instance to be suspended 10693d0407baSopenharmony_ci * 10703d0407baSopenharmony_ci * This function is intended to be called by the pm callbacks 10713d0407baSopenharmony_ci * (e.g., runtime_suspend, suspend) of the device driver that 10723d0407baSopenharmony_ci * holds the devfreq. 10733d0407baSopenharmony_ci */ 10743d0407baSopenharmony_ciint devfreq_suspend_device(struct devfreq *devfreq) 10753d0407baSopenharmony_ci{ 10763d0407baSopenharmony_ci int ret; 10773d0407baSopenharmony_ci 10783d0407baSopenharmony_ci if (!devfreq) { 10793d0407baSopenharmony_ci return -EINVAL; 10803d0407baSopenharmony_ci } 10813d0407baSopenharmony_ci 10823d0407baSopenharmony_ci if (atomic_inc_return(&devfreq->suspend_count) > 1) { 10833d0407baSopenharmony_ci return 0; 10843d0407baSopenharmony_ci } 10853d0407baSopenharmony_ci 10863d0407baSopenharmony_ci if (devfreq->governor) { 10873d0407baSopenharmony_ci ret = devfreq->governor->event_handler(devfreq, DEVFREQ_GOV_SUSPEND, NULL); 10883d0407baSopenharmony_ci if (ret) { 10893d0407baSopenharmony_ci return ret; 10903d0407baSopenharmony_ci } 10913d0407baSopenharmony_ci } 10923d0407baSopenharmony_ci 10933d0407baSopenharmony_ci if (devfreq->suspend_freq) { 10943d0407baSopenharmony_ci mutex_lock(&devfreq->lock); 10953d0407baSopenharmony_ci ret = devfreq_set_target(devfreq, devfreq->suspend_freq, 0); 10963d0407baSopenharmony_ci mutex_unlock(&devfreq->lock); 10973d0407baSopenharmony_ci if (ret) { 10983d0407baSopenharmony_ci return ret; 10993d0407baSopenharmony_ci } 11003d0407baSopenharmony_ci } 11013d0407baSopenharmony_ci 11023d0407baSopenharmony_ci return 0; 11033d0407baSopenharmony_ci} 11043d0407baSopenharmony_ciEXPORT_SYMBOL(devfreq_suspend_device); 11053d0407baSopenharmony_ci 11063d0407baSopenharmony_ci/** 11073d0407baSopenharmony_ci * devfreq_resume_device() - Resume devfreq of a device. 11083d0407baSopenharmony_ci * @devfreq: the devfreq instance to be resumed 11093d0407baSopenharmony_ci * 11103d0407baSopenharmony_ci * This function is intended to be called by the pm callbacks 11113d0407baSopenharmony_ci * (e.g., runtime_resume, resume) of the device driver that 11123d0407baSopenharmony_ci * holds the devfreq. 11133d0407baSopenharmony_ci */ 11143d0407baSopenharmony_ciint devfreq_resume_device(struct devfreq *devfreq) 11153d0407baSopenharmony_ci{ 11163d0407baSopenharmony_ci int ret; 11173d0407baSopenharmony_ci 11183d0407baSopenharmony_ci if (!devfreq) { 11193d0407baSopenharmony_ci return -EINVAL; 11203d0407baSopenharmony_ci } 11213d0407baSopenharmony_ci 11223d0407baSopenharmony_ci if (atomic_dec_return(&devfreq->suspend_count) >= 1) { 11233d0407baSopenharmony_ci return 0; 11243d0407baSopenharmony_ci } 11253d0407baSopenharmony_ci 11263d0407baSopenharmony_ci if (devfreq->resume_freq) { 11273d0407baSopenharmony_ci mutex_lock(&devfreq->lock); 11283d0407baSopenharmony_ci ret = devfreq_set_target(devfreq, devfreq->resume_freq, 0); 11293d0407baSopenharmony_ci mutex_unlock(&devfreq->lock); 11303d0407baSopenharmony_ci if (ret) { 11313d0407baSopenharmony_ci return ret; 11323d0407baSopenharmony_ci } 11333d0407baSopenharmony_ci } 11343d0407baSopenharmony_ci 11353d0407baSopenharmony_ci if (devfreq->governor) { 11363d0407baSopenharmony_ci ret = devfreq->governor->event_handler(devfreq, DEVFREQ_GOV_RESUME, NULL); 11373d0407baSopenharmony_ci if (ret) { 11383d0407baSopenharmony_ci return ret; 11393d0407baSopenharmony_ci } 11403d0407baSopenharmony_ci } 11413d0407baSopenharmony_ci 11423d0407baSopenharmony_ci return 0; 11433d0407baSopenharmony_ci} 11443d0407baSopenharmony_ciEXPORT_SYMBOL(devfreq_resume_device); 11453d0407baSopenharmony_ci 11463d0407baSopenharmony_ci/** 11473d0407baSopenharmony_ci * devfreq_suspend() - Suspend devfreq governors and devices 11483d0407baSopenharmony_ci * 11493d0407baSopenharmony_ci * Called during system wide Suspend/Hibernate cycles for suspending governors 11503d0407baSopenharmony_ci * and devices preserving the state for resume. On some platforms the devfreq 11513d0407baSopenharmony_ci * device must have precise state (frequency) after resume in order to provide 11523d0407baSopenharmony_ci * fully operating setup. 11533d0407baSopenharmony_ci */ 11543d0407baSopenharmony_civoid devfreq_suspend(void) 11553d0407baSopenharmony_ci{ 11563d0407baSopenharmony_ci struct devfreq *devfreq; 11573d0407baSopenharmony_ci int ret; 11583d0407baSopenharmony_ci 11593d0407baSopenharmony_ci mutex_lock(&devfreq_list_lock); 11603d0407baSopenharmony_ci list_for_each_entry(devfreq, &devfreq_list, node) 11613d0407baSopenharmony_ci { 11623d0407baSopenharmony_ci ret = devfreq_suspend_device(devfreq); 11633d0407baSopenharmony_ci if (ret) { 11643d0407baSopenharmony_ci dev_err(&devfreq->dev, "failed to suspend devfreq device\n"); 11653d0407baSopenharmony_ci } 11663d0407baSopenharmony_ci } 11673d0407baSopenharmony_ci mutex_unlock(&devfreq_list_lock); 11683d0407baSopenharmony_ci} 11693d0407baSopenharmony_ci 11703d0407baSopenharmony_ci/** 11713d0407baSopenharmony_ci * devfreq_resume() - Resume devfreq governors and devices 11723d0407baSopenharmony_ci * 11733d0407baSopenharmony_ci * Called during system wide Suspend/Hibernate cycle for resuming governors and 11743d0407baSopenharmony_ci * devices that are suspended with devfreq_suspend(). 11753d0407baSopenharmony_ci */ 11763d0407baSopenharmony_civoid devfreq_resume(void) 11773d0407baSopenharmony_ci{ 11783d0407baSopenharmony_ci struct devfreq *devfreq; 11793d0407baSopenharmony_ci int ret; 11803d0407baSopenharmony_ci 11813d0407baSopenharmony_ci mutex_lock(&devfreq_list_lock); 11823d0407baSopenharmony_ci list_for_each_entry(devfreq, &devfreq_list, node) 11833d0407baSopenharmony_ci { 11843d0407baSopenharmony_ci ret = devfreq_resume_device(devfreq); 11853d0407baSopenharmony_ci if (ret) { 11863d0407baSopenharmony_ci dev_warn(&devfreq->dev, "failed to resume devfreq device\n"); 11873d0407baSopenharmony_ci } 11883d0407baSopenharmony_ci } 11893d0407baSopenharmony_ci mutex_unlock(&devfreq_list_lock); 11903d0407baSopenharmony_ci} 11913d0407baSopenharmony_ci 11923d0407baSopenharmony_ci/** 11933d0407baSopenharmony_ci * devfreq_add_governor() - Add devfreq governor 11943d0407baSopenharmony_ci * @governor: the devfreq governor to be added 11953d0407baSopenharmony_ci */ 11963d0407baSopenharmony_ciint devfreq_add_governor(struct devfreq_governor *governor) 11973d0407baSopenharmony_ci{ 11983d0407baSopenharmony_ci struct devfreq_governor *g; 11993d0407baSopenharmony_ci struct devfreq *devfreq; 12003d0407baSopenharmony_ci int err = 0; 12013d0407baSopenharmony_ci 12023d0407baSopenharmony_ci if (!governor) { 12033d0407baSopenharmony_ci pr_err("%s: Invalid parameters.\n", __func__); 12043d0407baSopenharmony_ci return -EINVAL; 12053d0407baSopenharmony_ci } 12063d0407baSopenharmony_ci 12073d0407baSopenharmony_ci mutex_lock(&devfreq_list_lock); 12083d0407baSopenharmony_ci g = find_devfreq_governor(governor->name); 12093d0407baSopenharmony_ci if (!IS_ERR(g)) { 12103d0407baSopenharmony_ci pr_err("%s: governor %s already registered\n", __func__, g->name); 12113d0407baSopenharmony_ci err = -EINVAL; 12123d0407baSopenharmony_ci goto err_out; 12133d0407baSopenharmony_ci } 12143d0407baSopenharmony_ci 12153d0407baSopenharmony_ci list_add(&governor->node, &devfreq_governor_list); 12163d0407baSopenharmony_ci 12173d0407baSopenharmony_ci list_for_each_entry(devfreq, &devfreq_list, node) 12183d0407baSopenharmony_ci { 12193d0407baSopenharmony_ci int ret = 0; 12203d0407baSopenharmony_ci struct device *dev = devfreq->dev.parent; 12213d0407baSopenharmony_ci 12223d0407baSopenharmony_ci if (!strncmp(devfreq->governor_name, governor->name, DEVFREQ_NAME_LEN)) { 12233d0407baSopenharmony_ci /* The following should never occur */ 12243d0407baSopenharmony_ci if (devfreq->governor) { 12253d0407baSopenharmony_ci dev_warn(dev, "%s: Governor %s already present\n", __func__, devfreq->governor->name); 12263d0407baSopenharmony_ci ret = devfreq->governor->event_handler(devfreq, DEVFREQ_GOV_STOP, NULL); 12273d0407baSopenharmony_ci if (ret) { 12283d0407baSopenharmony_ci dev_warn(dev, "%s: Governor %s stop = %d\n", __func__, devfreq->governor->name, ret); 12293d0407baSopenharmony_ci } 12303d0407baSopenharmony_ci /* Fall through */ 12313d0407baSopenharmony_ci } 12323d0407baSopenharmony_ci devfreq->governor = governor; 12333d0407baSopenharmony_ci ret = devfreq->governor->event_handler(devfreq, DEVFREQ_GOV_START, NULL); 12343d0407baSopenharmony_ci if (ret) { 12353d0407baSopenharmony_ci dev_warn(dev, "%s: Governor %s start=%d\n", __func__, devfreq->governor->name, ret); 12363d0407baSopenharmony_ci } 12373d0407baSopenharmony_ci } 12383d0407baSopenharmony_ci } 12393d0407baSopenharmony_ci 12403d0407baSopenharmony_cierr_out: 12413d0407baSopenharmony_ci mutex_unlock(&devfreq_list_lock); 12423d0407baSopenharmony_ci 12433d0407baSopenharmony_ci return err; 12443d0407baSopenharmony_ci} 12453d0407baSopenharmony_ciEXPORT_SYMBOL(devfreq_add_governor); 12463d0407baSopenharmony_ci 12473d0407baSopenharmony_ci/** 12483d0407baSopenharmony_ci * devfreq_remove_governor() - Remove devfreq feature from a device. 12493d0407baSopenharmony_ci * @governor: the devfreq governor to be removed 12503d0407baSopenharmony_ci */ 12513d0407baSopenharmony_ciint devfreq_remove_governor(struct devfreq_governor *governor) 12523d0407baSopenharmony_ci{ 12533d0407baSopenharmony_ci struct devfreq_governor *g; 12543d0407baSopenharmony_ci struct devfreq *devfreq; 12553d0407baSopenharmony_ci int err = 0; 12563d0407baSopenharmony_ci 12573d0407baSopenharmony_ci if (!governor) { 12583d0407baSopenharmony_ci pr_err("%s: Invalid parameters.\n", __func__); 12593d0407baSopenharmony_ci return -EINVAL; 12603d0407baSopenharmony_ci } 12613d0407baSopenharmony_ci 12623d0407baSopenharmony_ci mutex_lock(&devfreq_list_lock); 12633d0407baSopenharmony_ci g = find_devfreq_governor(governor->name); 12643d0407baSopenharmony_ci if (IS_ERR(g)) { 12653d0407baSopenharmony_ci pr_err("%s: governor %s not registered\n", __func__, governor->name); 12663d0407baSopenharmony_ci err = PTR_ERR(g); 12673d0407baSopenharmony_ci goto err_out; 12683d0407baSopenharmony_ci } 12693d0407baSopenharmony_ci list_for_each_entry(devfreq, &devfreq_list, node) 12703d0407baSopenharmony_ci { 12713d0407baSopenharmony_ci int ret; 12723d0407baSopenharmony_ci struct device *dev = devfreq->dev.parent; 12733d0407baSopenharmony_ci 12743d0407baSopenharmony_ci if (!strncmp(devfreq->governor_name, governor->name, DEVFREQ_NAME_LEN)) { 12753d0407baSopenharmony_ci /* we should have a devfreq governor! */ 12763d0407baSopenharmony_ci if (!devfreq->governor) { 12773d0407baSopenharmony_ci dev_warn(dev, "%s: Governor %s NOT present\n", __func__, governor->name); 12783d0407baSopenharmony_ci continue; 12793d0407baSopenharmony_ci /* Fall through */ 12803d0407baSopenharmony_ci } 12813d0407baSopenharmony_ci ret = devfreq->governor->event_handler(devfreq, DEVFREQ_GOV_STOP, NULL); 12823d0407baSopenharmony_ci if (ret) { 12833d0407baSopenharmony_ci dev_warn(dev, "%s: Governor %s stop=%d\n", __func__, devfreq->governor->name, ret); 12843d0407baSopenharmony_ci } 12853d0407baSopenharmony_ci devfreq->governor = NULL; 12863d0407baSopenharmony_ci } 12873d0407baSopenharmony_ci } 12883d0407baSopenharmony_ci 12893d0407baSopenharmony_ci list_del(&governor->node); 12903d0407baSopenharmony_cierr_out: 12913d0407baSopenharmony_ci mutex_unlock(&devfreq_list_lock); 12923d0407baSopenharmony_ci 12933d0407baSopenharmony_ci return err; 12943d0407baSopenharmony_ci} 12953d0407baSopenharmony_ciEXPORT_SYMBOL(devfreq_remove_governor); 12963d0407baSopenharmony_ci 12973d0407baSopenharmony_cistatic ssize_t name_show(struct device *dev, struct device_attribute *attr, char *buf) 12983d0407baSopenharmony_ci{ 12993d0407baSopenharmony_ci struct devfreq *df = to_devfreq(dev); 13003d0407baSopenharmony_ci return sprintf(buf, "%s\n", dev_name(df->dev.parent)); 13013d0407baSopenharmony_ci} 13023d0407baSopenharmony_cistatic DEVICE_ATTR_RO(name); 13033d0407baSopenharmony_ci 13043d0407baSopenharmony_cistatic ssize_t governor_show(struct device *dev, struct device_attribute *attr, char *buf) 13053d0407baSopenharmony_ci{ 13063d0407baSopenharmony_ci struct devfreq *df = to_devfreq(dev); 13073d0407baSopenharmony_ci 13083d0407baSopenharmony_ci if (!df->governor) { 13093d0407baSopenharmony_ci return -EINVAL; 13103d0407baSopenharmony_ci } 13113d0407baSopenharmony_ci 13123d0407baSopenharmony_ci return sprintf(buf, "%s\n", df->governor->name); 13133d0407baSopenharmony_ci} 13143d0407baSopenharmony_ci 13153d0407baSopenharmony_cistatic ssize_t governor_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) 13163d0407baSopenharmony_ci{ 13173d0407baSopenharmony_ci struct devfreq *df = to_devfreq(dev); 13183d0407baSopenharmony_ci int ret; 13193d0407baSopenharmony_ci char str_governor[DEVFREQ_NAME_LEN + 1]; 13203d0407baSopenharmony_ci const struct devfreq_governor *governor, *prev_governor; 13213d0407baSopenharmony_ci 13223d0407baSopenharmony_ci if (!df->governor) { 13233d0407baSopenharmony_ci return -EINVAL; 13243d0407baSopenharmony_ci } 13253d0407baSopenharmony_ci 13263d0407baSopenharmony_ci ret = sscanf(buf, "%" __stringify(DEVFREQ_NAME_LEN) "s", str_governor); 13273d0407baSopenharmony_ci if (ret != 1) { 13283d0407baSopenharmony_ci return -EINVAL; 13293d0407baSopenharmony_ci } 13303d0407baSopenharmony_ci 13313d0407baSopenharmony_ci mutex_lock(&devfreq_list_lock); 13323d0407baSopenharmony_ci governor = try_then_request_governor(str_governor); 13333d0407baSopenharmony_ci if (IS_ERR(governor)) { 13343d0407baSopenharmony_ci ret = PTR_ERR(governor); 13353d0407baSopenharmony_ci goto out; 13363d0407baSopenharmony_ci } 13373d0407baSopenharmony_ci if (df->governor == governor) { 13383d0407baSopenharmony_ci ret = 0; 13393d0407baSopenharmony_ci goto out; 13403d0407baSopenharmony_ci } else if (df->governor->immutable || governor->immutable) { 13413d0407baSopenharmony_ci ret = -EINVAL; 13423d0407baSopenharmony_ci goto out; 13433d0407baSopenharmony_ci } 13443d0407baSopenharmony_ci 13453d0407baSopenharmony_ci ret = df->governor->event_handler(df, DEVFREQ_GOV_STOP, NULL); 13463d0407baSopenharmony_ci if (ret) { 13473d0407baSopenharmony_ci dev_warn(dev, "%s: Governor %s not stopped(%d)\n", __func__, df->governor->name, ret); 13483d0407baSopenharmony_ci goto out; 13493d0407baSopenharmony_ci } 13503d0407baSopenharmony_ci 13513d0407baSopenharmony_ci prev_governor = df->governor; 13523d0407baSopenharmony_ci df->governor = governor; 13533d0407baSopenharmony_ci strncpy(df->governor_name, governor->name, DEVFREQ_NAME_LEN); 13543d0407baSopenharmony_ci ret = df->governor->event_handler(df, DEVFREQ_GOV_START, NULL); 13553d0407baSopenharmony_ci if (ret) { 13563d0407baSopenharmony_ci dev_warn(dev, "%s: Governor %s not started(%d)\n", __func__, df->governor->name, ret); 13573d0407baSopenharmony_ci df->governor = prev_governor; 13583d0407baSopenharmony_ci strncpy(df->governor_name, prev_governor->name, DEVFREQ_NAME_LEN); 13593d0407baSopenharmony_ci ret = df->governor->event_handler(df, DEVFREQ_GOV_START, NULL); 13603d0407baSopenharmony_ci if (ret) { 13613d0407baSopenharmony_ci dev_err(dev, "%s: reverting to Governor %s failed (%d)\n", __func__, df->governor_name, ret); 13623d0407baSopenharmony_ci df->governor = NULL; 13633d0407baSopenharmony_ci } 13643d0407baSopenharmony_ci } 13653d0407baSopenharmony_ciout: 13663d0407baSopenharmony_ci mutex_unlock(&devfreq_list_lock); 13673d0407baSopenharmony_ci 13683d0407baSopenharmony_ci if (!ret) { 13693d0407baSopenharmony_ci ret = count; 13703d0407baSopenharmony_ci } 13713d0407baSopenharmony_ci return ret; 13723d0407baSopenharmony_ci} 13733d0407baSopenharmony_cistatic DEVICE_ATTR_RW(governor); 13743d0407baSopenharmony_ci 13753d0407baSopenharmony_cistatic ssize_t available_governors_show(struct device *d, struct device_attribute *attr, char *buf) 13763d0407baSopenharmony_ci{ 13773d0407baSopenharmony_ci struct devfreq *df = to_devfreq(d); 13783d0407baSopenharmony_ci ssize_t count = 0; 13793d0407baSopenharmony_ci 13803d0407baSopenharmony_ci if (!df->governor) { 13813d0407baSopenharmony_ci return -EINVAL; 13823d0407baSopenharmony_ci } 13833d0407baSopenharmony_ci 13843d0407baSopenharmony_ci mutex_lock(&devfreq_list_lock); 13853d0407baSopenharmony_ci 13863d0407baSopenharmony_ci /* 13873d0407baSopenharmony_ci * The devfreq with immutable governor (e.g., passive) shows 13883d0407baSopenharmony_ci * only own governor. 13893d0407baSopenharmony_ci */ 13903d0407baSopenharmony_ci if (df->governor->immutable) { 13913d0407baSopenharmony_ci count = scnprintf(&buf[count], DEVFREQ_NAME_LEN, "%s ", df->governor_name); 13923d0407baSopenharmony_ci /* 13933d0407baSopenharmony_ci * The devfreq device shows the registered governor except for 13943d0407baSopenharmony_ci * immutable governors such as passive governor . 13953d0407baSopenharmony_ci */ 13963d0407baSopenharmony_ci } else { 13973d0407baSopenharmony_ci struct devfreq_governor *governor; 13983d0407baSopenharmony_ci 13993d0407baSopenharmony_ci list_for_each_entry(governor, &devfreq_governor_list, node) 14003d0407baSopenharmony_ci { 14013d0407baSopenharmony_ci if (governor->immutable) { 14023d0407baSopenharmony_ci continue; 14033d0407baSopenharmony_ci } 14043d0407baSopenharmony_ci count += scnprintf(&buf[count], (PAGE_SIZE - count - 0x2), "%s ", governor->name); 14053d0407baSopenharmony_ci } 14063d0407baSopenharmony_ci } 14073d0407baSopenharmony_ci 14083d0407baSopenharmony_ci mutex_unlock(&devfreq_list_lock); 14093d0407baSopenharmony_ci 14103d0407baSopenharmony_ci /* Truncate the trailing space */ 14113d0407baSopenharmony_ci if (count) { 14123d0407baSopenharmony_ci count--; 14133d0407baSopenharmony_ci } 14143d0407baSopenharmony_ci 14153d0407baSopenharmony_ci count += sprintf(&buf[count], "\n"); 14163d0407baSopenharmony_ci 14173d0407baSopenharmony_ci return count; 14183d0407baSopenharmony_ci} 14193d0407baSopenharmony_cistatic DEVICE_ATTR_RO(available_governors); 14203d0407baSopenharmony_ci 14213d0407baSopenharmony_cistatic ssize_t cur_freq_show(struct device *dev, struct device_attribute *attr, char *buf) 14223d0407baSopenharmony_ci{ 14233d0407baSopenharmony_ci unsigned long freq; 14243d0407baSopenharmony_ci struct devfreq *df = to_devfreq(dev); 14253d0407baSopenharmony_ci 14263d0407baSopenharmony_ci if (!df->profile) { 14273d0407baSopenharmony_ci return -EINVAL; 14283d0407baSopenharmony_ci } 14293d0407baSopenharmony_ci 14303d0407baSopenharmony_ci if (df->profile->get_cur_freq && !df->profile->get_cur_freq(df->dev.parent, &freq)) { 14313d0407baSopenharmony_ci return sprintf(buf, "%lu\n", freq); 14323d0407baSopenharmony_ci } 14333d0407baSopenharmony_ci 14343d0407baSopenharmony_ci return sprintf(buf, "%lu\n", df->previous_freq); 14353d0407baSopenharmony_ci} 14363d0407baSopenharmony_cistatic DEVICE_ATTR_RO(cur_freq); 14373d0407baSopenharmony_ci 14383d0407baSopenharmony_cistatic ssize_t target_freq_show(struct device *dev, struct device_attribute *attr, char *buf) 14393d0407baSopenharmony_ci{ 14403d0407baSopenharmony_ci struct devfreq *df = to_devfreq(dev); 14413d0407baSopenharmony_ci 14423d0407baSopenharmony_ci return sprintf(buf, "%lu\n", df->previous_freq); 14433d0407baSopenharmony_ci} 14443d0407baSopenharmony_cistatic DEVICE_ATTR_RO(target_freq); 14453d0407baSopenharmony_ci 14463d0407baSopenharmony_cistatic ssize_t polling_interval_show(struct device *dev, struct device_attribute *attr, char *buf) 14473d0407baSopenharmony_ci{ 14483d0407baSopenharmony_ci struct devfreq *df = to_devfreq(dev); 14493d0407baSopenharmony_ci 14503d0407baSopenharmony_ci if (!df->profile) { 14513d0407baSopenharmony_ci return -EINVAL; 14523d0407baSopenharmony_ci } 14533d0407baSopenharmony_ci 14543d0407baSopenharmony_ci return sprintf(buf, "%d\n", df->profile->polling_ms); 14553d0407baSopenharmony_ci} 14563d0407baSopenharmony_ci 14573d0407baSopenharmony_cistatic ssize_t polling_interval_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) 14583d0407baSopenharmony_ci{ 14593d0407baSopenharmony_ci struct devfreq *df = to_devfreq(dev); 14603d0407baSopenharmony_ci unsigned int value; 14613d0407baSopenharmony_ci int ret; 14623d0407baSopenharmony_ci 14633d0407baSopenharmony_ci if (!df->governor) { 14643d0407baSopenharmony_ci return -EINVAL; 14653d0407baSopenharmony_ci } 14663d0407baSopenharmony_ci 14673d0407baSopenharmony_ci ret = sscanf(buf, "%u", &value); 14683d0407baSopenharmony_ci if (ret != 1) { 14693d0407baSopenharmony_ci return -EINVAL; 14703d0407baSopenharmony_ci } 14713d0407baSopenharmony_ci 14723d0407baSopenharmony_ci df->governor->event_handler(df, DEVFREQ_GOV_UPDATE_INTERVAL, &value); 14733d0407baSopenharmony_ci ret = count; 14743d0407baSopenharmony_ci 14753d0407baSopenharmony_ci return ret; 14763d0407baSopenharmony_ci} 14773d0407baSopenharmony_cistatic DEVICE_ATTR_RW(polling_interval); 14783d0407baSopenharmony_ci 14793d0407baSopenharmony_cistatic ssize_t min_freq_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) 14803d0407baSopenharmony_ci{ 14813d0407baSopenharmony_ci struct devfreq *df = to_devfreq(dev); 14823d0407baSopenharmony_ci unsigned long value; 14833d0407baSopenharmony_ci int ret; 14843d0407baSopenharmony_ci 14853d0407baSopenharmony_ci /* 14863d0407baSopenharmony_ci * Protect against theoretical sysfs writes between 14873d0407baSopenharmony_ci * device_add and dev_pm_qos_add_request 14883d0407baSopenharmony_ci */ 14893d0407baSopenharmony_ci if (!dev_pm_qos_request_active(&df->user_min_freq_req)) { 14903d0407baSopenharmony_ci return -EAGAIN; 14913d0407baSopenharmony_ci } 14923d0407baSopenharmony_ci 14933d0407baSopenharmony_ci ret = sscanf(buf, "%lu", &value); 14943d0407baSopenharmony_ci if (ret != 1) { 14953d0407baSopenharmony_ci return -EINVAL; 14963d0407baSopenharmony_ci } 14973d0407baSopenharmony_ci 14983d0407baSopenharmony_ci /* Round down to kHz for PM QoS */ 14993d0407baSopenharmony_ci ret = dev_pm_qos_update_request(&df->user_min_freq_req, value / HZ_PER_KHZ); 15003d0407baSopenharmony_ci if (ret < 0) { 15013d0407baSopenharmony_ci return ret; 15023d0407baSopenharmony_ci } 15033d0407baSopenharmony_ci 15043d0407baSopenharmony_ci return count; 15053d0407baSopenharmony_ci} 15063d0407baSopenharmony_ci 15073d0407baSopenharmony_cistatic ssize_t min_freq_show(struct device *dev, struct device_attribute *attr, char *buf) 15083d0407baSopenharmony_ci{ 15093d0407baSopenharmony_ci struct devfreq *df = to_devfreq(dev); 15103d0407baSopenharmony_ci unsigned long min_freq, max_freq; 15113d0407baSopenharmony_ci 15123d0407baSopenharmony_ci mutex_lock(&df->lock); 15133d0407baSopenharmony_ci get_freq_range(df, &min_freq, &max_freq); 15143d0407baSopenharmony_ci mutex_unlock(&df->lock); 15153d0407baSopenharmony_ci 15163d0407baSopenharmony_ci return sprintf(buf, "%lu\n", min_freq); 15173d0407baSopenharmony_ci} 15183d0407baSopenharmony_cistatic DEVICE_ATTR_RW(min_freq); 15193d0407baSopenharmony_ci 15203d0407baSopenharmony_cistatic ssize_t max_freq_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) 15213d0407baSopenharmony_ci{ 15223d0407baSopenharmony_ci struct devfreq *df = to_devfreq(dev); 15233d0407baSopenharmony_ci unsigned long value; 15243d0407baSopenharmony_ci int ret; 15253d0407baSopenharmony_ci 15263d0407baSopenharmony_ci /* 15273d0407baSopenharmony_ci * Protect against theoretical sysfs writes between 15283d0407baSopenharmony_ci * device_add and dev_pm_qos_add_request 15293d0407baSopenharmony_ci */ 15303d0407baSopenharmony_ci if (!dev_pm_qos_request_active(&df->user_max_freq_req)) { 15313d0407baSopenharmony_ci return -EINVAL; 15323d0407baSopenharmony_ci } 15333d0407baSopenharmony_ci 15343d0407baSopenharmony_ci ret = sscanf(buf, "%lu", &value); 15353d0407baSopenharmony_ci if (ret != 1) { 15363d0407baSopenharmony_ci return -EINVAL; 15373d0407baSopenharmony_ci } 15383d0407baSopenharmony_ci 15393d0407baSopenharmony_ci /* 15403d0407baSopenharmony_ci * PM QoS frequencies are in kHz so we need to convert. Convert by 15413d0407baSopenharmony_ci * rounding upwards so that the acceptable interval never shrinks. 15423d0407baSopenharmony_ci * 15433d0407baSopenharmony_ci * For example if the user writes "666666666" to sysfs this value will 15443d0407baSopenharmony_ci * be converted to 666667 kHz and back to 666667000 Hz before an OPP 15453d0407baSopenharmony_ci * lookup, this ensures that an OPP of 666666666Hz is still accepted. 15463d0407baSopenharmony_ci * 15473d0407baSopenharmony_ci * A value of zero means "no limit". 15483d0407baSopenharmony_ci */ 15493d0407baSopenharmony_ci if (value) { 15503d0407baSopenharmony_ci value = DIV_ROUND_UP(value, HZ_PER_KHZ); 15513d0407baSopenharmony_ci } else { 15523d0407baSopenharmony_ci value = PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE; 15533d0407baSopenharmony_ci } 15543d0407baSopenharmony_ci 15553d0407baSopenharmony_ci ret = dev_pm_qos_update_request(&df->user_max_freq_req, value); 15563d0407baSopenharmony_ci if (ret < 0) { 15573d0407baSopenharmony_ci return ret; 15583d0407baSopenharmony_ci } 15593d0407baSopenharmony_ci 15603d0407baSopenharmony_ci return count; 15613d0407baSopenharmony_ci} 15623d0407baSopenharmony_ci 15633d0407baSopenharmony_cistatic ssize_t max_freq_show(struct device *dev, struct device_attribute *attr, char *buf) 15643d0407baSopenharmony_ci{ 15653d0407baSopenharmony_ci struct devfreq *df = to_devfreq(dev); 15663d0407baSopenharmony_ci unsigned long min_freq, max_freq; 15673d0407baSopenharmony_ci 15683d0407baSopenharmony_ci mutex_lock(&df->lock); 15693d0407baSopenharmony_ci get_freq_range(df, &min_freq, &max_freq); 15703d0407baSopenharmony_ci mutex_unlock(&df->lock); 15713d0407baSopenharmony_ci 15723d0407baSopenharmony_ci return sprintf(buf, "%lu\n", max_freq); 15733d0407baSopenharmony_ci} 15743d0407baSopenharmony_cistatic DEVICE_ATTR_RW(max_freq); 15753d0407baSopenharmony_ci 15763d0407baSopenharmony_cistatic ssize_t available_frequencies_show(struct device *d, struct device_attribute *attr, char *buf) 15773d0407baSopenharmony_ci{ 15783d0407baSopenharmony_ci struct devfreq *df = to_devfreq(d); 15793d0407baSopenharmony_ci ssize_t count = 0; 15803d0407baSopenharmony_ci int i; 15813d0407baSopenharmony_ci 15823d0407baSopenharmony_ci if (!df->profile) { 15833d0407baSopenharmony_ci return -EINVAL; 15843d0407baSopenharmony_ci } 15853d0407baSopenharmony_ci 15863d0407baSopenharmony_ci mutex_lock(&df->lock); 15873d0407baSopenharmony_ci 15883d0407baSopenharmony_ci for (i = 0; i < df->profile->max_state; i++) { 15893d0407baSopenharmony_ci count += scnprintf(&buf[count], (PAGE_SIZE - count - 0x2), "%lu ", df->profile->freq_table[i]); 15903d0407baSopenharmony_ci } 15913d0407baSopenharmony_ci 15923d0407baSopenharmony_ci mutex_unlock(&df->lock); 15933d0407baSopenharmony_ci /* Truncate the trailing space */ 15943d0407baSopenharmony_ci if (count) { 15953d0407baSopenharmony_ci count--; 15963d0407baSopenharmony_ci } 15973d0407baSopenharmony_ci 15983d0407baSopenharmony_ci count += sprintf(&buf[count], "\n"); 15993d0407baSopenharmony_ci 16003d0407baSopenharmony_ci return count; 16013d0407baSopenharmony_ci} 16023d0407baSopenharmony_cistatic DEVICE_ATTR_RO(available_frequencies); 16033d0407baSopenharmony_ci 16043d0407baSopenharmony_cistatic ssize_t trans_stat_show(struct device *dev, struct device_attribute *attr, char *buf) 16053d0407baSopenharmony_ci{ 16063d0407baSopenharmony_ci struct devfreq *df = to_devfreq(dev); 16073d0407baSopenharmony_ci ssize_t len; 16083d0407baSopenharmony_ci int i, j; 16093d0407baSopenharmony_ci unsigned int max_state; 16103d0407baSopenharmony_ci 16113d0407baSopenharmony_ci if (!df->profile) { 16123d0407baSopenharmony_ci return -EINVAL; 16133d0407baSopenharmony_ci } 16143d0407baSopenharmony_ci max_state = df->profile->max_state; 16153d0407baSopenharmony_ci 16163d0407baSopenharmony_ci if (max_state == 0) { 16173d0407baSopenharmony_ci return sprintf(buf, "Not Supported.\n"); 16183d0407baSopenharmony_ci } 16193d0407baSopenharmony_ci 16203d0407baSopenharmony_ci mutex_lock(&df->lock); 16213d0407baSopenharmony_ci if (!df->stop_polling && devfreq_update_status(df, df->previous_freq)) { 16223d0407baSopenharmony_ci mutex_unlock(&df->lock); 16233d0407baSopenharmony_ci return 0; 16243d0407baSopenharmony_ci } 16253d0407baSopenharmony_ci mutex_unlock(&df->lock); 16263d0407baSopenharmony_ci 16273d0407baSopenharmony_ci len = sprintf(buf, " From : To\n"); 16283d0407baSopenharmony_ci len += sprintf(buf + len, " :"); 16293d0407baSopenharmony_ci for (i = 0; i < max_state; i++) { 16303d0407baSopenharmony_ci len += sprintf(buf + len, "%10lu", df->profile->freq_table[i]); 16313d0407baSopenharmony_ci } 16323d0407baSopenharmony_ci 16333d0407baSopenharmony_ci len += sprintf(buf + len, " time(ms)\n"); 16343d0407baSopenharmony_ci 16353d0407baSopenharmony_ci for (i = 0; i < max_state; i++) { 16363d0407baSopenharmony_ci if (df->profile->freq_table[i] == df->previous_freq) { 16373d0407baSopenharmony_ci len += sprintf(buf + len, "*"); 16383d0407baSopenharmony_ci } else { 16393d0407baSopenharmony_ci len += sprintf(buf + len, " "); 16403d0407baSopenharmony_ci } 16413d0407baSopenharmony_ci len += sprintf(buf + len, "%10lu:", df->profile->freq_table[i]); 16423d0407baSopenharmony_ci for (j = 0; j < max_state; j++) { 16433d0407baSopenharmony_ci len += sprintf(buf + len, "%10u", df->stats.trans_table[(i * max_state) + j]); 16443d0407baSopenharmony_ci } 16453d0407baSopenharmony_ci 16463d0407baSopenharmony_ci len += sprintf(buf + len, "%10llu\n", (u64)jiffies64_to_msecs(df->stats.time_in_state[i])); 16473d0407baSopenharmony_ci } 16483d0407baSopenharmony_ci 16493d0407baSopenharmony_ci len += sprintf(buf + len, "Total transition : %u\n", df->stats.total_trans); 16503d0407baSopenharmony_ci return len; 16513d0407baSopenharmony_ci} 16523d0407baSopenharmony_ci 16533d0407baSopenharmony_cistatic ssize_t trans_stat_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) 16543d0407baSopenharmony_ci{ 16553d0407baSopenharmony_ci struct devfreq *df = to_devfreq(dev); 16563d0407baSopenharmony_ci int err, value; 16573d0407baSopenharmony_ci 16583d0407baSopenharmony_ci if (!df->profile) { 16593d0407baSopenharmony_ci return -EINVAL; 16603d0407baSopenharmony_ci } 16613d0407baSopenharmony_ci 16623d0407baSopenharmony_ci if (df->profile->max_state == 0) { 16633d0407baSopenharmony_ci return count; 16643d0407baSopenharmony_ci } 16653d0407baSopenharmony_ci 16663d0407baSopenharmony_ci err = kstrtoint(buf, 0xa, &value); 16673d0407baSopenharmony_ci if (err || value != 0) { 16683d0407baSopenharmony_ci return -EINVAL; 16693d0407baSopenharmony_ci } 16703d0407baSopenharmony_ci 16713d0407baSopenharmony_ci mutex_lock(&df->lock); 16723d0407baSopenharmony_ci memset(df->stats.time_in_state, 0, (df->profile->max_state * sizeof(*df->stats.time_in_state))); 16733d0407baSopenharmony_ci memset(df->stats.trans_table, 0, array3_size(sizeof(unsigned int), df->profile->max_state, df->profile->max_state)); 16743d0407baSopenharmony_ci df->stats.total_trans = 0; 16753d0407baSopenharmony_ci df->stats.last_update = get_jiffies_64(); 16763d0407baSopenharmony_ci mutex_unlock(&df->lock); 16773d0407baSopenharmony_ci 16783d0407baSopenharmony_ci return count; 16793d0407baSopenharmony_ci} 16803d0407baSopenharmony_cistatic DEVICE_ATTR_RW(trans_stat); 16813d0407baSopenharmony_ci 16823d0407baSopenharmony_cistatic ssize_t timer_show(struct device *dev, struct device_attribute *attr, char *buf) 16833d0407baSopenharmony_ci{ 16843d0407baSopenharmony_ci struct devfreq *df = to_devfreq(dev); 16853d0407baSopenharmony_ci 16863d0407baSopenharmony_ci if (!df->profile) { 16873d0407baSopenharmony_ci return -EINVAL; 16883d0407baSopenharmony_ci } 16893d0407baSopenharmony_ci 16903d0407baSopenharmony_ci return sprintf(buf, "%s\n", timer_name[df->profile->timer]); 16913d0407baSopenharmony_ci} 16923d0407baSopenharmony_ci 16933d0407baSopenharmony_cistatic ssize_t timer_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) 16943d0407baSopenharmony_ci{ 16953d0407baSopenharmony_ci struct devfreq *df = to_devfreq(dev); 16963d0407baSopenharmony_ci char str_timer[DEVFREQ_NAME_LEN + 1]; 16973d0407baSopenharmony_ci int timer = -1; 16983d0407baSopenharmony_ci int ret = 0, i; 16993d0407baSopenharmony_ci 17003d0407baSopenharmony_ci if (!df->governor || !df->profile) { 17013d0407baSopenharmony_ci return -EINVAL; 17023d0407baSopenharmony_ci } 17033d0407baSopenharmony_ci 17043d0407baSopenharmony_ci ret = sscanf(buf, "%16s", str_timer); 17053d0407baSopenharmony_ci if (ret != 1) { 17063d0407baSopenharmony_ci return -EINVAL; 17073d0407baSopenharmony_ci } 17083d0407baSopenharmony_ci 17093d0407baSopenharmony_ci for (i = 0; i < DEVFREQ_TIMER_NUM; i++) { 17103d0407baSopenharmony_ci if (!strncmp(timer_name[i], str_timer, DEVFREQ_NAME_LEN)) { 17113d0407baSopenharmony_ci timer = i; 17123d0407baSopenharmony_ci break; 17133d0407baSopenharmony_ci } 17143d0407baSopenharmony_ci } 17153d0407baSopenharmony_ci 17163d0407baSopenharmony_ci if (timer < 0) { 17173d0407baSopenharmony_ci ret = -EINVAL; 17183d0407baSopenharmony_ci goto out; 17193d0407baSopenharmony_ci } 17203d0407baSopenharmony_ci 17213d0407baSopenharmony_ci if (df->profile->timer == timer) { 17223d0407baSopenharmony_ci ret = 0; 17233d0407baSopenharmony_ci goto out; 17243d0407baSopenharmony_ci } 17253d0407baSopenharmony_ci 17263d0407baSopenharmony_ci mutex_lock(&df->lock); 17273d0407baSopenharmony_ci df->profile->timer = timer; 17283d0407baSopenharmony_ci mutex_unlock(&df->lock); 17293d0407baSopenharmony_ci 17303d0407baSopenharmony_ci ret = df->governor->event_handler(df, DEVFREQ_GOV_STOP, NULL); 17313d0407baSopenharmony_ci if (ret) { 17323d0407baSopenharmony_ci dev_warn(dev, "%s: Governor %s not stopped(%d)\n", __func__, df->governor->name, ret); 17333d0407baSopenharmony_ci goto out; 17343d0407baSopenharmony_ci } 17353d0407baSopenharmony_ci 17363d0407baSopenharmony_ci ret = df->governor->event_handler(df, DEVFREQ_GOV_START, NULL); 17373d0407baSopenharmony_ci if (ret) { 17383d0407baSopenharmony_ci dev_warn(dev, "%s: Governor %s not started(%d)\n", __func__, df->governor->name, ret); 17393d0407baSopenharmony_ci } 17403d0407baSopenharmony_ciout: 17413d0407baSopenharmony_ci return ret ? ret : count; 17423d0407baSopenharmony_ci} 17433d0407baSopenharmony_cistatic DEVICE_ATTR_RW(timer); 17443d0407baSopenharmony_ci 17453d0407baSopenharmony_cistatic ssize_t load_show(struct device *dev, struct device_attribute *attr, char *buf) 17463d0407baSopenharmony_ci{ 17473d0407baSopenharmony_ci int err; 17483d0407baSopenharmony_ci struct devfreq *devfreq = to_devfreq(dev); 17493d0407baSopenharmony_ci struct devfreq_dev_status stat = devfreq->last_status; 17503d0407baSopenharmony_ci unsigned long freq; 17513d0407baSopenharmony_ci ssize_t len; 17523d0407baSopenharmony_ci 17533d0407baSopenharmony_ci err = devfreq_update_stats(devfreq); 17543d0407baSopenharmony_ci if (err) { 17553d0407baSopenharmony_ci return err; 17563d0407baSopenharmony_ci } 17573d0407baSopenharmony_ci 17583d0407baSopenharmony_ci if (stat.total_time < stat.busy_time) { 17593d0407baSopenharmony_ci err = devfreq_update_stats(devfreq); 17603d0407baSopenharmony_ci if (err) { 17613d0407baSopenharmony_ci return err; 17623d0407baSopenharmony_ci } 17633d0407baSopenharmony_ci }; 17643d0407baSopenharmony_ci 17653d0407baSopenharmony_ci if (!stat.total_time) { 17663d0407baSopenharmony_ci return 0; 17673d0407baSopenharmony_ci } 17683d0407baSopenharmony_ci 17693d0407baSopenharmony_ci len = sprintf(buf, "%lu", stat.busy_time * 0x64 / stat.total_time); 17703d0407baSopenharmony_ci 17713d0407baSopenharmony_ci if (devfreq->profile->get_cur_freq && !devfreq->profile->get_cur_freq(devfreq->dev.parent, &freq)) { 17723d0407baSopenharmony_ci len += sprintf(buf + len, "@%luHz\n", freq); 17733d0407baSopenharmony_ci } else { 17743d0407baSopenharmony_ci len += sprintf(buf + len, "@%luHz\n", devfreq->previous_freq); 17753d0407baSopenharmony_ci } 17763d0407baSopenharmony_ci 17773d0407baSopenharmony_ci return len; 17783d0407baSopenharmony_ci} 17793d0407baSopenharmony_cistatic DEVICE_ATTR_RO(load); 17803d0407baSopenharmony_ci 17813d0407baSopenharmony_cistatic struct attribute *devfreq_attrs[] = { 17823d0407baSopenharmony_ci &dev_attr_name.attr, 17833d0407baSopenharmony_ci &dev_attr_governor.attr, 17843d0407baSopenharmony_ci &dev_attr_available_governors.attr, 17853d0407baSopenharmony_ci &dev_attr_cur_freq.attr, 17863d0407baSopenharmony_ci &dev_attr_available_frequencies.attr, 17873d0407baSopenharmony_ci &dev_attr_target_freq.attr, 17883d0407baSopenharmony_ci &dev_attr_polling_interval.attr, 17893d0407baSopenharmony_ci &dev_attr_min_freq.attr, 17903d0407baSopenharmony_ci &dev_attr_max_freq.attr, 17913d0407baSopenharmony_ci &dev_attr_trans_stat.attr, 17923d0407baSopenharmony_ci &dev_attr_timer.attr, 17933d0407baSopenharmony_ci &dev_attr_load.attr, 17943d0407baSopenharmony_ci NULL, 17953d0407baSopenharmony_ci}; 17963d0407baSopenharmony_ciATTRIBUTE_GROUPS(devfreq); 17973d0407baSopenharmony_ci 17983d0407baSopenharmony_ci/** 17993d0407baSopenharmony_ci * devfreq_summary_show() - Show the summary of the devfreq devices 18003d0407baSopenharmony_ci * @s: seq_file instance to show the summary of devfreq devices 18013d0407baSopenharmony_ci * @data: not used 18023d0407baSopenharmony_ci * 18033d0407baSopenharmony_ci * Show the summary of the devfreq devices via 'devfreq_summary' debugfs file. 18043d0407baSopenharmony_ci * It helps that user can know the detailed information of the devfreq devices. 18053d0407baSopenharmony_ci * 18063d0407baSopenharmony_ci * Return 0 always because it shows the information without any data change. 18073d0407baSopenharmony_ci */ 18083d0407baSopenharmony_cistatic int devfreq_summary_show(struct seq_file *s, void *data) 18093d0407baSopenharmony_ci{ 18103d0407baSopenharmony_ci struct devfreq *devfreq; 18113d0407baSopenharmony_ci struct devfreq *p_devfreq = NULL; 18123d0407baSopenharmony_ci unsigned long cur_freq, min_freq, max_freq; 18133d0407baSopenharmony_ci unsigned int polling_ms; 18143d0407baSopenharmony_ci unsigned int timer; 18153d0407baSopenharmony_ci 18163d0407baSopenharmony_ci seq_printf(s, "%-30s %-30s %-15s %-10s %10s %12s %12s %12s\n", "dev", "parent_dev", "governor", "timer", 18173d0407baSopenharmony_ci "polling_ms", "cur_freq_Hz", "min_freq_Hz", "max_freq_Hz"); 18183d0407baSopenharmony_ci seq_printf(s, "%30s %30s %15s %10s %10s %12s %12s %12s\n", "------------------------------", 18193d0407baSopenharmony_ci "------------------------------", "---------------", "----------", "----------", "------------", 18203d0407baSopenharmony_ci "------------", "------------"); 18213d0407baSopenharmony_ci 18223d0407baSopenharmony_ci mutex_lock(&devfreq_list_lock); 18233d0407baSopenharmony_ci 18243d0407baSopenharmony_ci list_for_each_entry_reverse(devfreq, &devfreq_list, node) 18253d0407baSopenharmony_ci { 18263d0407baSopenharmony_ci#if IS_ENABLED(CONFIG_DEVFREQ_GOV_PASSIVE) 18273d0407baSopenharmony_ci if (!strncmp(devfreq->governor_name, DEVFREQ_GOV_PASSIVE, DEVFREQ_NAME_LEN)) { 18283d0407baSopenharmony_ci struct devfreq_passive_data *data = devfreq->data; 18293d0407baSopenharmony_ci 18303d0407baSopenharmony_ci if (data) { 18313d0407baSopenharmony_ci p_devfreq = data->parent; 18323d0407baSopenharmony_ci } 18333d0407baSopenharmony_ci } else { 18343d0407baSopenharmony_ci p_devfreq = NULL; 18353d0407baSopenharmony_ci } 18363d0407baSopenharmony_ci#endif 18373d0407baSopenharmony_ci 18383d0407baSopenharmony_ci mutex_lock(&devfreq->lock); 18393d0407baSopenharmony_ci cur_freq = devfreq->previous_freq; 18403d0407baSopenharmony_ci get_freq_range(devfreq, &min_freq, &max_freq); 18413d0407baSopenharmony_ci polling_ms = devfreq->profile->polling_ms; 18423d0407baSopenharmony_ci timer = devfreq->profile->timer; 18433d0407baSopenharmony_ci mutex_unlock(&devfreq->lock); 18443d0407baSopenharmony_ci 18453d0407baSopenharmony_ci seq_printf(s, "%-30s %-30s %-15s %-10s %10d %12ld %12ld %12ld\n", dev_name(&devfreq->dev), 18463d0407baSopenharmony_ci p_devfreq ? dev_name(&p_devfreq->dev) : "null", devfreq->governor_name, 18473d0407baSopenharmony_ci polling_ms ? timer_name[timer] : "null", polling_ms, cur_freq, min_freq, max_freq); 18483d0407baSopenharmony_ci } 18493d0407baSopenharmony_ci 18503d0407baSopenharmony_ci mutex_unlock(&devfreq_list_lock); 18513d0407baSopenharmony_ci 18523d0407baSopenharmony_ci return 0; 18533d0407baSopenharmony_ci} 18543d0407baSopenharmony_ciDEFINE_SHOW_ATTRIBUTE(devfreq_summary); 18553d0407baSopenharmony_ci 18563d0407baSopenharmony_cistatic int __init devfreq_init(void) 18573d0407baSopenharmony_ci{ 18583d0407baSopenharmony_ci devfreq_class = class_create(THIS_MODULE, "devfreq"); 18593d0407baSopenharmony_ci if (IS_ERR(devfreq_class)) { 18603d0407baSopenharmony_ci pr_err("%s: couldn't create class\n", __FILE__); 18613d0407baSopenharmony_ci return PTR_ERR(devfreq_class); 18623d0407baSopenharmony_ci } 18633d0407baSopenharmony_ci 18643d0407baSopenharmony_ci devfreq_wq = create_freezable_workqueue("devfreq_wq"); 18653d0407baSopenharmony_ci if (!devfreq_wq) { 18663d0407baSopenharmony_ci class_destroy(devfreq_class); 18673d0407baSopenharmony_ci pr_err("%s: couldn't create workqueue\n", __FILE__); 18683d0407baSopenharmony_ci return -ENOMEM; 18693d0407baSopenharmony_ci } 18703d0407baSopenharmony_ci devfreq_class->dev_groups = devfreq_groups; 18713d0407baSopenharmony_ci 18723d0407baSopenharmony_ci devfreq_debugfs = debugfs_create_dir("devfreq", NULL); 18733d0407baSopenharmony_ci debugfs_create_file("devfreq_summary", 0444, devfreq_debugfs, NULL, &devfreq_summary_fops); 18743d0407baSopenharmony_ci 18753d0407baSopenharmony_ci return 0; 18763d0407baSopenharmony_ci} 18773d0407baSopenharmony_cisubsys_initcall(devfreq_init); 18783d0407baSopenharmony_ci 18793d0407baSopenharmony_ci/* 18803d0407baSopenharmony_ci * The following are helper functions for devfreq user device drivers with 18813d0407baSopenharmony_ci * OPP framework. 18823d0407baSopenharmony_ci */ 18833d0407baSopenharmony_ci 18843d0407baSopenharmony_ci/** 18853d0407baSopenharmony_ci * devfreq_recommended_opp() - Helper function to get proper OPP for the 18863d0407baSopenharmony_ci * freq value given to target callback. 18873d0407baSopenharmony_ci * @dev: The devfreq user device. (parent of devfreq) 18883d0407baSopenharmony_ci * @freq: The frequency given to target function 18893d0407baSopenharmony_ci * @flags: Flags handed from devfreq framework. 18903d0407baSopenharmony_ci * 18913d0407baSopenharmony_ci * The callers are required to call dev_pm_opp_put() for the returned OPP after 18923d0407baSopenharmony_ci * use. 18933d0407baSopenharmony_ci */ 18943d0407baSopenharmony_cistruct dev_pm_opp *devfreq_recommended_opp(struct device *dev, unsigned long *freq, u32 flags) 18953d0407baSopenharmony_ci{ 18963d0407baSopenharmony_ci struct dev_pm_opp *opp; 18973d0407baSopenharmony_ci 18983d0407baSopenharmony_ci if (flags & DEVFREQ_FLAG_LEAST_UPPER_BOUND) { 18993d0407baSopenharmony_ci /* The freq is an upper bound. opp should be lower */ 19003d0407baSopenharmony_ci opp = dev_pm_opp_find_freq_floor(dev, freq); 19013d0407baSopenharmony_ci /* If not available, use the closest opp */ 19023d0407baSopenharmony_ci if (opp == ERR_PTR(-ERANGE)) { 19033d0407baSopenharmony_ci opp = dev_pm_opp_find_freq_ceil(dev, freq); 19043d0407baSopenharmony_ci } 19053d0407baSopenharmony_ci } else { 19063d0407baSopenharmony_ci /* The freq is an lower bound. opp should be higher */ 19073d0407baSopenharmony_ci opp = dev_pm_opp_find_freq_ceil(dev, freq); 19083d0407baSopenharmony_ci /* If not available, use the closest opp */ 19093d0407baSopenharmony_ci if (opp == ERR_PTR(-ERANGE)) { 19103d0407baSopenharmony_ci opp = dev_pm_opp_find_freq_floor(dev, freq); 19113d0407baSopenharmony_ci } 19123d0407baSopenharmony_ci } 19133d0407baSopenharmony_ci 19143d0407baSopenharmony_ci return opp; 19153d0407baSopenharmony_ci} 19163d0407baSopenharmony_ciEXPORT_SYMBOL(devfreq_recommended_opp); 19173d0407baSopenharmony_ci 19183d0407baSopenharmony_ci/** 19193d0407baSopenharmony_ci * devfreq_register_opp_notifier() - Helper function to get devfreq notified 19203d0407baSopenharmony_ci * for any changes in the OPP availability 19213d0407baSopenharmony_ci * changes 19223d0407baSopenharmony_ci * @dev: The devfreq user device. (parent of devfreq) 19233d0407baSopenharmony_ci * @devfreq: The devfreq object. 19243d0407baSopenharmony_ci */ 19253d0407baSopenharmony_ciint devfreq_register_opp_notifier(struct device *dev, struct devfreq *devfreq) 19263d0407baSopenharmony_ci{ 19273d0407baSopenharmony_ci return dev_pm_opp_register_notifier(dev, &devfreq->nb); 19283d0407baSopenharmony_ci} 19293d0407baSopenharmony_ciEXPORT_SYMBOL(devfreq_register_opp_notifier); 19303d0407baSopenharmony_ci 19313d0407baSopenharmony_ci/** 19323d0407baSopenharmony_ci * devfreq_unregister_opp_notifier() - Helper function to stop getting devfreq 19333d0407baSopenharmony_ci * notified for any changes in the OPP 19343d0407baSopenharmony_ci * availability changes anymore. 19353d0407baSopenharmony_ci * @dev: The devfreq user device. (parent of devfreq) 19363d0407baSopenharmony_ci * @devfreq: The devfreq object. 19373d0407baSopenharmony_ci * 19383d0407baSopenharmony_ci * At exit() callback of devfreq_dev_profile, this must be included if 19393d0407baSopenharmony_ci * devfreq_recommended_opp is used. 19403d0407baSopenharmony_ci */ 19413d0407baSopenharmony_ciint devfreq_unregister_opp_notifier(struct device *dev, struct devfreq *devfreq) 19423d0407baSopenharmony_ci{ 19433d0407baSopenharmony_ci return dev_pm_opp_unregister_notifier(dev, &devfreq->nb); 19443d0407baSopenharmony_ci} 19453d0407baSopenharmony_ciEXPORT_SYMBOL(devfreq_unregister_opp_notifier); 19463d0407baSopenharmony_ci 19473d0407baSopenharmony_cistatic void devm_devfreq_opp_release(struct device *dev, void *res) 19483d0407baSopenharmony_ci{ 19493d0407baSopenharmony_ci devfreq_unregister_opp_notifier(dev, *(struct devfreq **)res); 19503d0407baSopenharmony_ci} 19513d0407baSopenharmony_ci 19523d0407baSopenharmony_ci/** 19533d0407baSopenharmony_ci * devm_devfreq_register_opp_notifier() - Resource-managed 19543d0407baSopenharmony_ci * devfreq_register_opp_notifier() 19553d0407baSopenharmony_ci * @dev: The devfreq user device. (parent of devfreq) 19563d0407baSopenharmony_ci * @devfreq: The devfreq object. 19573d0407baSopenharmony_ci */ 19583d0407baSopenharmony_ciint devm_devfreq_register_opp_notifier(struct device *dev, struct devfreq *devfreq) 19593d0407baSopenharmony_ci{ 19603d0407baSopenharmony_ci struct devfreq **ptr; 19613d0407baSopenharmony_ci int ret; 19623d0407baSopenharmony_ci 19633d0407baSopenharmony_ci ptr = devres_alloc(devm_devfreq_opp_release, sizeof(*ptr), GFP_KERNEL); 19643d0407baSopenharmony_ci if (!ptr) { 19653d0407baSopenharmony_ci return -ENOMEM; 19663d0407baSopenharmony_ci } 19673d0407baSopenharmony_ci 19683d0407baSopenharmony_ci ret = devfreq_register_opp_notifier(dev, devfreq); 19693d0407baSopenharmony_ci if (ret) { 19703d0407baSopenharmony_ci devres_free(ptr); 19713d0407baSopenharmony_ci return ret; 19723d0407baSopenharmony_ci } 19733d0407baSopenharmony_ci 19743d0407baSopenharmony_ci *ptr = devfreq; 19753d0407baSopenharmony_ci devres_add(dev, ptr); 19763d0407baSopenharmony_ci 19773d0407baSopenharmony_ci return 0; 19783d0407baSopenharmony_ci} 19793d0407baSopenharmony_ciEXPORT_SYMBOL(devm_devfreq_register_opp_notifier); 19803d0407baSopenharmony_ci 19813d0407baSopenharmony_ci/** 19823d0407baSopenharmony_ci * devm_devfreq_unregister_opp_notifier() - Resource-managed 19833d0407baSopenharmony_ci * devfreq_unregister_opp_notifier() 19843d0407baSopenharmony_ci * @dev: The devfreq user device. (parent of devfreq) 19853d0407baSopenharmony_ci * @devfreq: The devfreq object. 19863d0407baSopenharmony_ci */ 19873d0407baSopenharmony_civoid devm_devfreq_unregister_opp_notifier(struct device *dev, struct devfreq *devfreq) 19883d0407baSopenharmony_ci{ 19893d0407baSopenharmony_ci WARN_ON(devres_release(dev, devm_devfreq_opp_release, devm_devfreq_dev_match, devfreq)); 19903d0407baSopenharmony_ci} 19913d0407baSopenharmony_ciEXPORT_SYMBOL(devm_devfreq_unregister_opp_notifier); 19923d0407baSopenharmony_ci 19933d0407baSopenharmony_ci/** 19943d0407baSopenharmony_ci * devfreq_register_notifier() - Register a driver with devfreq 19953d0407baSopenharmony_ci * @devfreq: The devfreq object. 19963d0407baSopenharmony_ci * @nb: The notifier block to register. 19973d0407baSopenharmony_ci * @list: DEVFREQ_TRANSITION_NOTIFIER. 19983d0407baSopenharmony_ci */ 19993d0407baSopenharmony_ciint devfreq_register_notifier(struct devfreq *devfreq, struct notifier_block *nb, unsigned int list) 20003d0407baSopenharmony_ci{ 20013d0407baSopenharmony_ci int ret = 0; 20023d0407baSopenharmony_ci 20033d0407baSopenharmony_ci if (!devfreq) { 20043d0407baSopenharmony_ci return -EINVAL; 20053d0407baSopenharmony_ci } 20063d0407baSopenharmony_ci 20073d0407baSopenharmony_ci switch (list) { 20083d0407baSopenharmony_ci case DEVFREQ_TRANSITION_NOTIFIER: 20093d0407baSopenharmony_ci ret = srcu_notifier_chain_register(&devfreq->transition_notifier_list, nb); 20103d0407baSopenharmony_ci break; 20113d0407baSopenharmony_ci default: 20123d0407baSopenharmony_ci ret = -EINVAL; 20133d0407baSopenharmony_ci } 20143d0407baSopenharmony_ci 20153d0407baSopenharmony_ci return ret; 20163d0407baSopenharmony_ci} 20173d0407baSopenharmony_ciEXPORT_SYMBOL(devfreq_register_notifier); 20183d0407baSopenharmony_ci 20193d0407baSopenharmony_ci/* 20203d0407baSopenharmony_ci * devfreq_unregister_notifier() - Unregister a driver with devfreq 20213d0407baSopenharmony_ci * @devfreq: The devfreq object. 20223d0407baSopenharmony_ci * @nb: The notifier block to be unregistered. 20233d0407baSopenharmony_ci * @list: DEVFREQ_TRANSITION_NOTIFIER. 20243d0407baSopenharmony_ci */ 20253d0407baSopenharmony_ciint devfreq_unregister_notifier(struct devfreq *devfreq, struct notifier_block *nb, unsigned int list) 20263d0407baSopenharmony_ci{ 20273d0407baSopenharmony_ci int ret = 0; 20283d0407baSopenharmony_ci 20293d0407baSopenharmony_ci if (!devfreq) { 20303d0407baSopenharmony_ci return -EINVAL; 20313d0407baSopenharmony_ci } 20323d0407baSopenharmony_ci 20333d0407baSopenharmony_ci switch (list) { 20343d0407baSopenharmony_ci case DEVFREQ_TRANSITION_NOTIFIER: 20353d0407baSopenharmony_ci ret = srcu_notifier_chain_unregister(&devfreq->transition_notifier_list, nb); 20363d0407baSopenharmony_ci break; 20373d0407baSopenharmony_ci default: 20383d0407baSopenharmony_ci ret = -EINVAL; 20393d0407baSopenharmony_ci } 20403d0407baSopenharmony_ci 20413d0407baSopenharmony_ci return ret; 20423d0407baSopenharmony_ci} 20433d0407baSopenharmony_ciEXPORT_SYMBOL(devfreq_unregister_notifier); 20443d0407baSopenharmony_ci 20453d0407baSopenharmony_cistruct devfreq_notifier_devres { 20463d0407baSopenharmony_ci struct devfreq *devfreq; 20473d0407baSopenharmony_ci struct notifier_block *nb; 20483d0407baSopenharmony_ci unsigned int list; 20493d0407baSopenharmony_ci}; 20503d0407baSopenharmony_ci 20513d0407baSopenharmony_cistatic void devm_devfreq_notifier_release(struct device *dev, void *res) 20523d0407baSopenharmony_ci{ 20533d0407baSopenharmony_ci struct devfreq_notifier_devres *this = res; 20543d0407baSopenharmony_ci 20553d0407baSopenharmony_ci devfreq_unregister_notifier(this->devfreq, this->nb, this->list); 20563d0407baSopenharmony_ci} 20573d0407baSopenharmony_ci 20583d0407baSopenharmony_ci/** 20593d0407baSopenharmony_ci * devm_devfreq_register_notifier() 20603d0407baSopenharmony_ci * - Resource-managed devfreq_register_notifier() 20613d0407baSopenharmony_ci * @dev: The devfreq user device. (parent of devfreq) 20623d0407baSopenharmony_ci * @devfreq: The devfreq object. 20633d0407baSopenharmony_ci * @nb: The notifier block to be unregistered. 20643d0407baSopenharmony_ci * @list: DEVFREQ_TRANSITION_NOTIFIER. 20653d0407baSopenharmony_ci */ 20663d0407baSopenharmony_ciint devm_devfreq_register_notifier(struct device *dev, struct devfreq *devfreq, struct notifier_block *nb, 20673d0407baSopenharmony_ci unsigned int list) 20683d0407baSopenharmony_ci{ 20693d0407baSopenharmony_ci struct devfreq_notifier_devres *ptr; 20703d0407baSopenharmony_ci int ret; 20713d0407baSopenharmony_ci 20723d0407baSopenharmony_ci ptr = devres_alloc(devm_devfreq_notifier_release, sizeof(*ptr), GFP_KERNEL); 20733d0407baSopenharmony_ci if (!ptr) { 20743d0407baSopenharmony_ci return -ENOMEM; 20753d0407baSopenharmony_ci } 20763d0407baSopenharmony_ci 20773d0407baSopenharmony_ci ret = devfreq_register_notifier(devfreq, nb, list); 20783d0407baSopenharmony_ci if (ret) { 20793d0407baSopenharmony_ci devres_free(ptr); 20803d0407baSopenharmony_ci return ret; 20813d0407baSopenharmony_ci } 20823d0407baSopenharmony_ci 20833d0407baSopenharmony_ci ptr->devfreq = devfreq; 20843d0407baSopenharmony_ci ptr->nb = nb; 20853d0407baSopenharmony_ci ptr->list = list; 20863d0407baSopenharmony_ci devres_add(dev, ptr); 20873d0407baSopenharmony_ci 20883d0407baSopenharmony_ci return 0; 20893d0407baSopenharmony_ci} 20903d0407baSopenharmony_ciEXPORT_SYMBOL(devm_devfreq_register_notifier); 20913d0407baSopenharmony_ci 20923d0407baSopenharmony_ci/** 20933d0407baSopenharmony_ci * devm_devfreq_unregister_notifier() 20943d0407baSopenharmony_ci * - Resource-managed devfreq_unregister_notifier() 20953d0407baSopenharmony_ci * @dev: The devfreq user device. (parent of devfreq) 20963d0407baSopenharmony_ci * @devfreq: The devfreq object. 20973d0407baSopenharmony_ci * @nb: The notifier block to be unregistered. 20983d0407baSopenharmony_ci * @list: DEVFREQ_TRANSITION_NOTIFIER. 20993d0407baSopenharmony_ci */ 21003d0407baSopenharmony_civoid devm_devfreq_unregister_notifier(struct device *dev, struct devfreq *devfreq, struct notifier_block *nb, 21013d0407baSopenharmony_ci unsigned int list) 21023d0407baSopenharmony_ci{ 21033d0407baSopenharmony_ci WARN_ON(devres_release(dev, devm_devfreq_notifier_release, devm_devfreq_dev_match, devfreq)); 21043d0407baSopenharmony_ci} 21053d0407baSopenharmony_ciEXPORT_SYMBOL(devm_devfreq_unregister_notifier); 2106