1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2a3c98b8bSMyungJoo Ham /*
3a3c98b8bSMyungJoo Ham * devfreq: Generic Dynamic Voltage and Frequency Scaling (DVFS) Framework
4a3c98b8bSMyungJoo Ham * for Non-CPU Devices.
5a3c98b8bSMyungJoo Ham *
6a3c98b8bSMyungJoo Ham * Copyright (C) 2011 Samsung Electronics
7a3c98b8bSMyungJoo Ham * MyungJoo Ham <myungjoo.ham@samsung.com>
8a3c98b8bSMyungJoo Ham */
9a3c98b8bSMyungJoo Ham
10a3c98b8bSMyungJoo Ham #include <linux/kernel.h>
1123c7b54cSEnric Balletbo i Serra #include <linux/kmod.h>
12a3c98b8bSMyungJoo Ham #include <linux/sched.h>
13490a421bSChanwoo Choi #include <linux/debugfs.h>
141224451bSDaniel Lezcano #include <linux/devfreq_cooling.h>
15a3c98b8bSMyungJoo Ham #include <linux/errno.h>
16a3c98b8bSMyungJoo Ham #include <linux/err.h>
17a3c98b8bSMyungJoo Ham #include <linux/init.h>
18417dc4bbSPaul Gortmaker #include <linux/export.h>
19a3c98b8bSMyungJoo Ham #include <linux/slab.h>
20952f6d13SMyungJoo Ham #include <linux/stat.h>
21e4db1c74SNishanth Menon #include <linux/pm_opp.h>
22a3c98b8bSMyungJoo Ham #include <linux/devfreq.h>
23a3c98b8bSMyungJoo Ham #include <linux/workqueue.h>
24a3c98b8bSMyungJoo Ham #include <linux/platform_device.h>
25a3c98b8bSMyungJoo Ham #include <linux/list.h>
26a3c98b8bSMyungJoo Ham #include <linux/printk.h>
27a3c98b8bSMyungJoo Ham #include <linux/hrtimer.h>
288f510aebSChanwoo Choi #include <linux/of.h>
2905d7ae15SLeonard Crestez #include <linux/pm_qos.h>
3004c8984aSDaniel Lezcano #include <linux/units.h>
31a3c98b8bSMyungJoo Ham #include "governor.h"
32a3c98b8bSMyungJoo Ham
33cf451adfSLukasz Luba #define CREATE_TRACE_POINTS
34cf451adfSLukasz Luba #include <trace/events/devfreq.h>
35cf451adfSLukasz Luba
360dd25a0dSChanwoo Choi #define IS_SUPPORTED_FLAG(f, name) ((f & DEVFREQ_GOV_FLAG_##name) ? true : false)
375f1a9066SChanwoo Choi #define IS_SUPPORTED_ATTR(f, name) ((f & DEVFREQ_GOV_ATTR_##name) ? true : false)
3805d7ae15SLeonard Crestez
391a1357eaSNishanth Menon static struct class *devfreq_class;
40490a421bSChanwoo Choi static struct dentry *devfreq_debugfs;
41a3c98b8bSMyungJoo Ham
42a3c98b8bSMyungJoo Ham /*
437e6fdd4bSRajagopal Venkat * devfreq core provides delayed work based load monitoring helper
447e6fdd4bSRajagopal Venkat * functions. Governors can use these or can implement their own
457e6fdd4bSRajagopal Venkat * monitoring mechanism.
46a3c98b8bSMyungJoo Ham */
47a3c98b8bSMyungJoo Ham static struct workqueue_struct *devfreq_wq;
48a3c98b8bSMyungJoo Ham
493aa173b8SNishanth Menon /* The list of all device-devfreq governors */
503aa173b8SNishanth Menon static LIST_HEAD(devfreq_governor_list);
51a3c98b8bSMyungJoo Ham /* The list of all device-devfreq */
52a3c98b8bSMyungJoo Ham static LIST_HEAD(devfreq_list);
53a3c98b8bSMyungJoo Ham static DEFINE_MUTEX(devfreq_list_lock);
54a3c98b8bSMyungJoo Ham
554dc3bab8SChanwoo Choi static const char timer_name[][DEVFREQ_NAME_LEN] = {
564dc3bab8SChanwoo Choi [DEVFREQ_TIMER_DEFERRABLE] = { "deferrable" },
574dc3bab8SChanwoo Choi [DEVFREQ_TIMER_DELAYED] = { "delayed" },
584dc3bab8SChanwoo Choi };
594dc3bab8SChanwoo Choi
60a3c98b8bSMyungJoo Ham /**
61a3c98b8bSMyungJoo Ham * find_device_devfreq() - find devfreq struct using device pointer
62a3c98b8bSMyungJoo Ham * @dev: device pointer used to lookup device devfreq.
63a3c98b8bSMyungJoo Ham *
64a3c98b8bSMyungJoo Ham * Search the list of device devfreqs and return the matched device's
65a3c98b8bSMyungJoo Ham * devfreq info. devfreq_list_lock should be held by the caller.
66a3c98b8bSMyungJoo Ham */
find_device_devfreq(struct device * dev)67a3c98b8bSMyungJoo Ham static struct devfreq *find_device_devfreq(struct device *dev)
68a3c98b8bSMyungJoo Ham {
69a3c98b8bSMyungJoo Ham struct devfreq *tmp_devfreq;
70a3c98b8bSMyungJoo Ham
718fc0e48eSKrzysztof Kozlowski lockdep_assert_held(&devfreq_list_lock);
728fc0e48eSKrzysztof Kozlowski
739348da2fSViresh Kumar if (IS_ERR_OR_NULL(dev)) {
74a3c98b8bSMyungJoo Ham pr_err("DEVFREQ: %s: Invalid parameters\n", __func__);
75a3c98b8bSMyungJoo Ham return ERR_PTR(-EINVAL);
76a3c98b8bSMyungJoo Ham }
77a3c98b8bSMyungJoo Ham
78a3c98b8bSMyungJoo Ham list_for_each_entry(tmp_devfreq, &devfreq_list, node) {
79a3c98b8bSMyungJoo Ham if (tmp_devfreq->dev.parent == dev)
80a3c98b8bSMyungJoo Ham return tmp_devfreq;
81a3c98b8bSMyungJoo Ham }
82a3c98b8bSMyungJoo Ham
83a3c98b8bSMyungJoo Ham return ERR_PTR(-ENODEV);
84a3c98b8bSMyungJoo Ham }
85a3c98b8bSMyungJoo Ham
find_available_min_freq(struct devfreq * devfreq)86ab8f58adSChanwoo Choi static unsigned long find_available_min_freq(struct devfreq *devfreq)
87ab8f58adSChanwoo Choi {
88ab8f58adSChanwoo Choi struct dev_pm_opp *opp;
89ab8f58adSChanwoo Choi unsigned long min_freq = 0;
90ab8f58adSChanwoo Choi
91ab8f58adSChanwoo Choi opp = dev_pm_opp_find_freq_ceil(devfreq->dev.parent, &min_freq);
92ab8f58adSChanwoo Choi if (IS_ERR(opp))
93ab8f58adSChanwoo Choi min_freq = 0;
94ab8f58adSChanwoo Choi else
95ab8f58adSChanwoo Choi dev_pm_opp_put(opp);
96ab8f58adSChanwoo Choi
97ab8f58adSChanwoo Choi return min_freq;
98ab8f58adSChanwoo Choi }
99ab8f58adSChanwoo Choi
find_available_max_freq(struct devfreq * devfreq)100ab8f58adSChanwoo Choi static unsigned long find_available_max_freq(struct devfreq *devfreq)
101ab8f58adSChanwoo Choi {
102ab8f58adSChanwoo Choi struct dev_pm_opp *opp;
103ab8f58adSChanwoo Choi unsigned long max_freq = ULONG_MAX;
104ab8f58adSChanwoo Choi
105ab8f58adSChanwoo Choi opp = dev_pm_opp_find_freq_floor(devfreq->dev.parent, &max_freq);
106ab8f58adSChanwoo Choi if (IS_ERR(opp))
107ab8f58adSChanwoo Choi max_freq = 0;
108ab8f58adSChanwoo Choi else
109ab8f58adSChanwoo Choi dev_pm_opp_put(opp);
110ab8f58adSChanwoo Choi
111ab8f58adSChanwoo Choi return max_freq;
112ab8f58adSChanwoo Choi }
113ab8f58adSChanwoo Choi
114a3c98b8bSMyungJoo Ham /**
115713472e5SChanwoo Choi * devfreq_get_freq_range() - Get the current freq range
11646cecc0bSLeonard Crestez * @devfreq: the devfreq instance
11746cecc0bSLeonard Crestez * @min_freq: the min frequency
11846cecc0bSLeonard Crestez * @max_freq: the max frequency
11946cecc0bSLeonard Crestez *
12046cecc0bSLeonard Crestez * This takes into consideration all constraints.
12146cecc0bSLeonard Crestez */
devfreq_get_freq_range(struct devfreq * devfreq,unsigned long * min_freq,unsigned long * max_freq)122713472e5SChanwoo Choi void devfreq_get_freq_range(struct devfreq *devfreq,
12346cecc0bSLeonard Crestez unsigned long *min_freq,
12446cecc0bSLeonard Crestez unsigned long *max_freq)
12546cecc0bSLeonard Crestez {
126b5d281f6SChristian Marangi unsigned long *freq_table = devfreq->freq_table;
12705d7ae15SLeonard Crestez s32 qos_min_freq, qos_max_freq;
12846cecc0bSLeonard Crestez
12946cecc0bSLeonard Crestez lockdep_assert_held(&devfreq->lock);
13046cecc0bSLeonard Crestez
13146cecc0bSLeonard Crestez /*
13246cecc0bSLeonard Crestez * Initialize minimum/maximum frequency from freq table.
13346cecc0bSLeonard Crestez * The devfreq drivers can initialize this in either ascending or
13446cecc0bSLeonard Crestez * descending order and devfreq core supports both.
13546cecc0bSLeonard Crestez */
136b5d281f6SChristian Marangi if (freq_table[0] < freq_table[devfreq->max_state - 1]) {
13746cecc0bSLeonard Crestez *min_freq = freq_table[0];
138b5d281f6SChristian Marangi *max_freq = freq_table[devfreq->max_state - 1];
13946cecc0bSLeonard Crestez } else {
140b5d281f6SChristian Marangi *min_freq = freq_table[devfreq->max_state - 1];
14146cecc0bSLeonard Crestez *max_freq = freq_table[0];
14246cecc0bSLeonard Crestez }
14346cecc0bSLeonard Crestez
14405d7ae15SLeonard Crestez /* Apply constraints from PM QoS */
14505d7ae15SLeonard Crestez qos_min_freq = dev_pm_qos_read_value(devfreq->dev.parent,
14605d7ae15SLeonard Crestez DEV_PM_QOS_MIN_FREQUENCY);
14705d7ae15SLeonard Crestez qos_max_freq = dev_pm_qos_read_value(devfreq->dev.parent,
14805d7ae15SLeonard Crestez DEV_PM_QOS_MAX_FREQUENCY);
14905d7ae15SLeonard Crestez *min_freq = max(*min_freq, (unsigned long)HZ_PER_KHZ * qos_min_freq);
15005d7ae15SLeonard Crestez if (qos_max_freq != PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE)
15105d7ae15SLeonard Crestez *max_freq = min(*max_freq,
15205d7ae15SLeonard Crestez (unsigned long)HZ_PER_KHZ * qos_max_freq);
15305d7ae15SLeonard Crestez
15446cecc0bSLeonard Crestez /* Apply constraints from OPP interface */
15546cecc0bSLeonard Crestez *min_freq = max(*min_freq, devfreq->scaling_min_freq);
15646cecc0bSLeonard Crestez *max_freq = min(*max_freq, devfreq->scaling_max_freq);
15746cecc0bSLeonard Crestez
15846cecc0bSLeonard Crestez if (*min_freq > *max_freq)
15946cecc0bSLeonard Crestez *min_freq = *max_freq;
16046cecc0bSLeonard Crestez }
161713472e5SChanwoo Choi EXPORT_SYMBOL(devfreq_get_freq_range);
16246cecc0bSLeonard Crestez
16346cecc0bSLeonard Crestez /**
164e552bbafSJonghwa Lee * devfreq_get_freq_level() - Lookup freq_table for the frequency
165e552bbafSJonghwa Lee * @devfreq: the devfreq instance
166e552bbafSJonghwa Lee * @freq: the target frequency
167e552bbafSJonghwa Lee */
devfreq_get_freq_level(struct devfreq * devfreq,unsigned long freq)168e552bbafSJonghwa Lee static int devfreq_get_freq_level(struct devfreq *devfreq, unsigned long freq)
169e552bbafSJonghwa Lee {
170e552bbafSJonghwa Lee int lev;
171e552bbafSJonghwa Lee
172b5d281f6SChristian Marangi for (lev = 0; lev < devfreq->max_state; lev++)
173b5d281f6SChristian Marangi if (freq == devfreq->freq_table[lev])
174e552bbafSJonghwa Lee return lev;
175e552bbafSJonghwa Lee
176e552bbafSJonghwa Lee return -EINVAL;
177e552bbafSJonghwa Lee }
178e552bbafSJonghwa Lee
set_freq_table(struct devfreq * devfreq)179ea572f81SChanwoo Choi static int set_freq_table(struct devfreq *devfreq)
1800ec09ac2SChanwoo Choi {
1810ec09ac2SChanwoo Choi struct dev_pm_opp *opp;
1820ec09ac2SChanwoo Choi unsigned long freq;
1830ec09ac2SChanwoo Choi int i, count;
1840ec09ac2SChanwoo Choi
1850ec09ac2SChanwoo Choi /* Initialize the freq_table from OPP table */
1860ec09ac2SChanwoo Choi count = dev_pm_opp_get_opp_count(devfreq->dev.parent);
1870ec09ac2SChanwoo Choi if (count <= 0)
188ea572f81SChanwoo Choi return -EINVAL;
1890ec09ac2SChanwoo Choi
190b5d281f6SChristian Marangi devfreq->max_state = count;
191b5d281f6SChristian Marangi devfreq->freq_table = devm_kcalloc(devfreq->dev.parent,
192b5d281f6SChristian Marangi devfreq->max_state,
193b5d281f6SChristian Marangi sizeof(*devfreq->freq_table),
1940ec09ac2SChanwoo Choi GFP_KERNEL);
195b5d281f6SChristian Marangi if (!devfreq->freq_table)
196ea572f81SChanwoo Choi return -ENOMEM;
1970ec09ac2SChanwoo Choi
198b5d281f6SChristian Marangi for (i = 0, freq = 0; i < devfreq->max_state; i++, freq++) {
1990ec09ac2SChanwoo Choi opp = dev_pm_opp_find_freq_ceil(devfreq->dev.parent, &freq);
2000ec09ac2SChanwoo Choi if (IS_ERR(opp)) {
201b5d281f6SChristian Marangi devm_kfree(devfreq->dev.parent, devfreq->freq_table);
202ea572f81SChanwoo Choi return PTR_ERR(opp);
2030ec09ac2SChanwoo Choi }
2048a31d9d9SViresh Kumar dev_pm_opp_put(opp);
205b5d281f6SChristian Marangi devfreq->freq_table[i] = freq;
2060ec09ac2SChanwoo Choi }
207ea572f81SChanwoo Choi
208ea572f81SChanwoo Choi return 0;
2090ec09ac2SChanwoo Choi }
2100ec09ac2SChanwoo Choi
2110ec09ac2SChanwoo Choi /**
212e552bbafSJonghwa Lee * devfreq_update_status() - Update statistics of devfreq behavior
213e552bbafSJonghwa Lee * @devfreq: the devfreq instance
214e552bbafSJonghwa Lee * @freq: the update target frequency
215e552bbafSJonghwa Lee */
devfreq_update_status(struct devfreq * devfreq,unsigned long freq)21630582c25SChanwoo Choi int devfreq_update_status(struct devfreq *devfreq, unsigned long freq)
217e552bbafSJonghwa Lee {
218e35d35a1SSaravana Kannan int lev, prev_lev, ret = 0;
219b76b3479SKamil Konieczny u64 cur_time;
220e552bbafSJonghwa Lee
2212abb0d52SLeonard Crestez lockdep_assert_held(&devfreq->lock);
222b76b3479SKamil Konieczny cur_time = get_jiffies_64();
223e35d35a1SSaravana Kannan
224d0563a03STobias Jakobi /* Immediately exit if previous_freq is not initialized yet. */
225d0563a03STobias Jakobi if (!devfreq->previous_freq)
226d0563a03STobias Jakobi goto out;
227d0563a03STobias Jakobi
228e35d35a1SSaravana Kannan prev_lev = devfreq_get_freq_level(devfreq, devfreq->previous_freq);
229e35d35a1SSaravana Kannan if (prev_lev < 0) {
230e35d35a1SSaravana Kannan ret = prev_lev;
231e35d35a1SSaravana Kannan goto out;
232e35d35a1SSaravana Kannan }
233e35d35a1SSaravana Kannan
2341ebd0bc0SKamil Konieczny devfreq->stats.time_in_state[prev_lev] +=
2351ebd0bc0SKamil Konieczny cur_time - devfreq->stats.last_update;
236e35d35a1SSaravana Kannan
237e35d35a1SSaravana Kannan lev = devfreq_get_freq_level(devfreq, freq);
238e35d35a1SSaravana Kannan if (lev < 0) {
239e35d35a1SSaravana Kannan ret = lev;
240e35d35a1SSaravana Kannan goto out;
241e35d35a1SSaravana Kannan }
242e35d35a1SSaravana Kannan
243e35d35a1SSaravana Kannan if (lev != prev_lev) {
2441ebd0bc0SKamil Konieczny devfreq->stats.trans_table[
245b5d281f6SChristian Marangi (prev_lev * devfreq->max_state) + lev]++;
2461ebd0bc0SKamil Konieczny devfreq->stats.total_trans++;
247e552bbafSJonghwa Lee }
248e552bbafSJonghwa Lee
249e35d35a1SSaravana Kannan out:
2501ebd0bc0SKamil Konieczny devfreq->stats.last_update = cur_time;
251e35d35a1SSaravana Kannan return ret;
252e552bbafSJonghwa Lee }
25330582c25SChanwoo Choi EXPORT_SYMBOL(devfreq_update_status);
254e552bbafSJonghwa Lee
2553aa173b8SNishanth Menon /**
2563aa173b8SNishanth Menon * find_devfreq_governor() - find devfreq governor from name
2573aa173b8SNishanth Menon * @name: name of the governor
2583aa173b8SNishanth Menon *
2593aa173b8SNishanth Menon * Search the list of devfreq governors and return the matched
2603aa173b8SNishanth Menon * governor's pointer. devfreq_list_lock should be held by the caller.
2613aa173b8SNishanth Menon */
find_devfreq_governor(const char * name)2623aa173b8SNishanth Menon static struct devfreq_governor *find_devfreq_governor(const char *name)
2633aa173b8SNishanth Menon {
2643aa173b8SNishanth Menon struct devfreq_governor *tmp_governor;
2653aa173b8SNishanth Menon
2668fc0e48eSKrzysztof Kozlowski lockdep_assert_held(&devfreq_list_lock);
2678fc0e48eSKrzysztof Kozlowski
2689348da2fSViresh Kumar if (IS_ERR_OR_NULL(name)) {
2693aa173b8SNishanth Menon pr_err("DEVFREQ: %s: Invalid parameters\n", __func__);
2703aa173b8SNishanth Menon return ERR_PTR(-EINVAL);
2713aa173b8SNishanth Menon }
2723aa173b8SNishanth Menon
2733aa173b8SNishanth Menon list_for_each_entry(tmp_governor, &devfreq_governor_list, node) {
2743aa173b8SNishanth Menon if (!strncmp(tmp_governor->name, name, DEVFREQ_NAME_LEN))
2753aa173b8SNishanth Menon return tmp_governor;
2763aa173b8SNishanth Menon }
2773aa173b8SNishanth Menon
2783aa173b8SNishanth Menon return ERR_PTR(-ENODEV);
2793aa173b8SNishanth Menon }
2803aa173b8SNishanth Menon
28123c7b54cSEnric Balletbo i Serra /**
28223c7b54cSEnric Balletbo i Serra * try_then_request_governor() - Try to find the governor and request the
28323c7b54cSEnric Balletbo i Serra * module if is not found.
28423c7b54cSEnric Balletbo i Serra * @name: name of the governor
28523c7b54cSEnric Balletbo i Serra *
28623c7b54cSEnric Balletbo i Serra * Search the list of devfreq governors and request the module and try again
28723c7b54cSEnric Balletbo i Serra * if is not found. This can happen when both drivers (the governor driver
28823c7b54cSEnric Balletbo i Serra * and the driver that call devfreq_add_device) are built as modules.
28923c7b54cSEnric Balletbo i Serra * devfreq_list_lock should be held by the caller. Returns the matched
290b53b0128SEnric Balletbo i Serra * governor's pointer or an error pointer.
29123c7b54cSEnric Balletbo i Serra */
try_then_request_governor(const char * name)29223c7b54cSEnric Balletbo i Serra static struct devfreq_governor *try_then_request_governor(const char *name)
29323c7b54cSEnric Balletbo i Serra {
29423c7b54cSEnric Balletbo i Serra struct devfreq_governor *governor;
29523c7b54cSEnric Balletbo i Serra int err = 0;
29623c7b54cSEnric Balletbo i Serra
2978fc0e48eSKrzysztof Kozlowski lockdep_assert_held(&devfreq_list_lock);
2988fc0e48eSKrzysztof Kozlowski
29923c7b54cSEnric Balletbo i Serra if (IS_ERR_OR_NULL(name)) {
30023c7b54cSEnric Balletbo i Serra pr_err("DEVFREQ: %s: Invalid parameters\n", __func__);
30123c7b54cSEnric Balletbo i Serra return ERR_PTR(-EINVAL);
30223c7b54cSEnric Balletbo i Serra }
30323c7b54cSEnric Balletbo i Serra
30423c7b54cSEnric Balletbo i Serra governor = find_devfreq_governor(name);
30523c7b54cSEnric Balletbo i Serra if (IS_ERR(governor)) {
30623c7b54cSEnric Balletbo i Serra mutex_unlock(&devfreq_list_lock);
30723c7b54cSEnric Balletbo i Serra
30823c7b54cSEnric Balletbo i Serra if (!strncmp(name, DEVFREQ_GOV_SIMPLE_ONDEMAND,
30923c7b54cSEnric Balletbo i Serra DEVFREQ_NAME_LEN))
31023c7b54cSEnric Balletbo i Serra err = request_module("governor_%s", "simpleondemand");
31123c7b54cSEnric Balletbo i Serra else
31223c7b54cSEnric Balletbo i Serra err = request_module("governor_%s", name);
31323c7b54cSEnric Balletbo i Serra /* Restore previous state before return */
31423c7b54cSEnric Balletbo i Serra mutex_lock(&devfreq_list_lock);
31523c7b54cSEnric Balletbo i Serra if (err)
3167544fd7fSEzequiel Garcia return (err < 0) ? ERR_PTR(err) : ERR_PTR(-EINVAL);
31723c7b54cSEnric Balletbo i Serra
31823c7b54cSEnric Balletbo i Serra governor = find_devfreq_governor(name);
31923c7b54cSEnric Balletbo i Serra }
32023c7b54cSEnric Balletbo i Serra
32123c7b54cSEnric Balletbo i Serra return governor;
32223c7b54cSEnric Balletbo i Serra }
32323c7b54cSEnric Balletbo i Serra
devfreq_notify_transition(struct devfreq * devfreq,struct devfreq_freqs * freqs,unsigned int state)3240fe3a664SChanwoo Choi static int devfreq_notify_transition(struct devfreq *devfreq,
3250fe3a664SChanwoo Choi struct devfreq_freqs *freqs, unsigned int state)
3260fe3a664SChanwoo Choi {
3270fe3a664SChanwoo Choi if (!devfreq)
3280fe3a664SChanwoo Choi return -EINVAL;
3290fe3a664SChanwoo Choi
3300fe3a664SChanwoo Choi switch (state) {
3310fe3a664SChanwoo Choi case DEVFREQ_PRECHANGE:
3320fe3a664SChanwoo Choi srcu_notifier_call_chain(&devfreq->transition_notifier_list,
3330fe3a664SChanwoo Choi DEVFREQ_PRECHANGE, freqs);
3340fe3a664SChanwoo Choi break;
3350fe3a664SChanwoo Choi
3360fe3a664SChanwoo Choi case DEVFREQ_POSTCHANGE:
3370fe3a664SChanwoo Choi srcu_notifier_call_chain(&devfreq->transition_notifier_list,
3380fe3a664SChanwoo Choi DEVFREQ_POSTCHANGE, freqs);
3390fe3a664SChanwoo Choi break;
3400fe3a664SChanwoo Choi default:
3410fe3a664SChanwoo Choi return -EINVAL;
3420fe3a664SChanwoo Choi }
3430fe3a664SChanwoo Choi
3440fe3a664SChanwoo Choi return 0;
3450fe3a664SChanwoo Choi }
3460fe3a664SChanwoo Choi
devfreq_set_target(struct devfreq * devfreq,unsigned long new_freq,u32 flags)34763314172SLukasz Luba static int devfreq_set_target(struct devfreq *devfreq, unsigned long new_freq,
34863314172SLukasz Luba u32 flags)
34963314172SLukasz Luba {
35063314172SLukasz Luba struct devfreq_freqs freqs;
35163314172SLukasz Luba unsigned long cur_freq;
35263314172SLukasz Luba int err = 0;
35363314172SLukasz Luba
35463314172SLukasz Luba if (devfreq->profile->get_cur_freq)
35563314172SLukasz Luba devfreq->profile->get_cur_freq(devfreq->dev.parent, &cur_freq);
35663314172SLukasz Luba else
35763314172SLukasz Luba cur_freq = devfreq->previous_freq;
35863314172SLukasz Luba
35963314172SLukasz Luba freqs.old = cur_freq;
36063314172SLukasz Luba freqs.new = new_freq;
36163314172SLukasz Luba devfreq_notify_transition(devfreq, &freqs, DEVFREQ_PRECHANGE);
36263314172SLukasz Luba
36363314172SLukasz Luba err = devfreq->profile->target(devfreq->dev.parent, &new_freq, flags);
36463314172SLukasz Luba if (err) {
36563314172SLukasz Luba freqs.new = cur_freq;
36663314172SLukasz Luba devfreq_notify_transition(devfreq, &freqs, DEVFREQ_POSTCHANGE);
36763314172SLukasz Luba return err;
36863314172SLukasz Luba }
36963314172SLukasz Luba
370cab477d0SMatthias Kaehlcke /*
371cab477d0SMatthias Kaehlcke * Print devfreq_frequency trace information between DEVFREQ_PRECHANGE
372cab477d0SMatthias Kaehlcke * and DEVFREQ_POSTCHANGE because for showing the correct frequency
373cab477d0SMatthias Kaehlcke * change order of between devfreq device and passive devfreq device.
374cab477d0SMatthias Kaehlcke */
375cab477d0SMatthias Kaehlcke if (trace_devfreq_frequency_enabled() && new_freq != cur_freq)
376cab477d0SMatthias Kaehlcke trace_devfreq_frequency(devfreq, new_freq, cur_freq);
377cab477d0SMatthias Kaehlcke
37863314172SLukasz Luba freqs.new = new_freq;
37963314172SLukasz Luba devfreq_notify_transition(devfreq, &freqs, DEVFREQ_POSTCHANGE);
38063314172SLukasz Luba
38163314172SLukasz Luba if (devfreq_update_status(devfreq, new_freq))
38246674314STzung-Bi Shih dev_warn(&devfreq->dev,
38363314172SLukasz Luba "Couldn't update frequency transition information.\n");
38463314172SLukasz Luba
38563314172SLukasz Luba devfreq->previous_freq = new_freq;
38683f8ca45SLukasz Luba
38783f8ca45SLukasz Luba if (devfreq->suspend_freq)
38862453f1bSDong Aisheng devfreq->resume_freq = new_freq;
38983f8ca45SLukasz Luba
39063314172SLukasz Luba return err;
39163314172SLukasz Luba }
39263314172SLukasz Luba
393a3c98b8bSMyungJoo Ham /**
394b4365423SChanwoo Choi * devfreq_update_target() - Reevaluate the device and configure frequency
395b4365423SChanwoo Choi * on the final stage.
396a3c98b8bSMyungJoo Ham * @devfreq: the devfreq instance.
397b4365423SChanwoo Choi * @freq: the new frequency of parent device. This argument
398b4365423SChanwoo Choi * is only used for devfreq device using passive governor.
399a3c98b8bSMyungJoo Ham *
400b4365423SChanwoo Choi * Note: Lock devfreq->lock before calling devfreq_update_target. This function
401b4365423SChanwoo Choi * should be only used by both update_devfreq() and devfreq governors.
402a3c98b8bSMyungJoo Ham */
devfreq_update_target(struct devfreq * devfreq,unsigned long freq)403b4365423SChanwoo Choi int devfreq_update_target(struct devfreq *devfreq, unsigned long freq)
404a3c98b8bSMyungJoo Ham {
405b4365423SChanwoo Choi unsigned long min_freq, max_freq;
406a3c98b8bSMyungJoo Ham int err = 0;
407ab5f299fSMyungJoo Ham u32 flags = 0;
408a3c98b8bSMyungJoo Ham
4098fc0e48eSKrzysztof Kozlowski lockdep_assert_held(&devfreq->lock);
410a3c98b8bSMyungJoo Ham
4111b5c1be2SNishanth Menon if (!devfreq->governor)
4121b5c1be2SNishanth Menon return -EINVAL;
4131b5c1be2SNishanth Menon
414a3c98b8bSMyungJoo Ham /* Reevaluate the proper frequency */
415a3c98b8bSMyungJoo Ham err = devfreq->governor->get_target_freq(devfreq, &freq);
416a3c98b8bSMyungJoo Ham if (err)
417a3c98b8bSMyungJoo Ham return err;
418713472e5SChanwoo Choi devfreq_get_freq_range(devfreq, &min_freq, &max_freq);
419ab5f299fSMyungJoo Ham
420df5cf4a3SMatthias Kaehlcke if (freq < min_freq) {
421f1d981eaSChanwoo Choi freq = min_freq;
422ab5f299fSMyungJoo Ham flags &= ~DEVFREQ_FLAG_LEAST_UPPER_BOUND; /* Use GLB */
423ab5f299fSMyungJoo Ham }
424df5cf4a3SMatthias Kaehlcke if (freq > max_freq) {
425f1d981eaSChanwoo Choi freq = max_freq;
426ab5f299fSMyungJoo Ham flags |= DEVFREQ_FLAG_LEAST_UPPER_BOUND; /* Use LUB */
427ab5f299fSMyungJoo Ham }
428ab5f299fSMyungJoo Ham
42963314172SLukasz Luba return devfreq_set_target(devfreq, freq, flags);
430b4365423SChanwoo Choi }
431b4365423SChanwoo Choi EXPORT_SYMBOL(devfreq_update_target);
4320fe3a664SChanwoo Choi
433b4365423SChanwoo Choi /* Load monitoring helper functions for governors use */
434b4365423SChanwoo Choi
435b4365423SChanwoo Choi /**
436b4365423SChanwoo Choi * update_devfreq() - Reevaluate the device and configure frequency.
437b4365423SChanwoo Choi * @devfreq: the devfreq instance.
438b4365423SChanwoo Choi *
439b4365423SChanwoo Choi * Note: Lock devfreq->lock before calling update_devfreq
440b4365423SChanwoo Choi * This function is exported for governors.
441b4365423SChanwoo Choi */
update_devfreq(struct devfreq * devfreq)442b4365423SChanwoo Choi int update_devfreq(struct devfreq *devfreq)
443b4365423SChanwoo Choi {
444b4365423SChanwoo Choi return devfreq_update_target(devfreq, 0L);
445a3c98b8bSMyungJoo Ham }
4462df5021fSNishanth Menon EXPORT_SYMBOL(update_devfreq);
447a3c98b8bSMyungJoo Ham
448a3c98b8bSMyungJoo Ham /**
4497e6fdd4bSRajagopal Venkat * devfreq_monitor() - Periodically poll devfreq objects.
4507e6fdd4bSRajagopal Venkat * @work: the work struct used to run devfreq_monitor periodically.
4517e6fdd4bSRajagopal Venkat *
4527e6fdd4bSRajagopal Venkat */
devfreq_monitor(struct work_struct * work)4537e6fdd4bSRajagopal Venkat static void devfreq_monitor(struct work_struct *work)
4547e6fdd4bSRajagopal Venkat {
4557e6fdd4bSRajagopal Venkat int err;
4567e6fdd4bSRajagopal Venkat struct devfreq *devfreq = container_of(work,
4577e6fdd4bSRajagopal Venkat struct devfreq, work.work);
4587e6fdd4bSRajagopal Venkat
4597e6fdd4bSRajagopal Venkat mutex_lock(&devfreq->lock);
4607e6fdd4bSRajagopal Venkat err = update_devfreq(devfreq);
4617e6fdd4bSRajagopal Venkat if (err)
4627e6fdd4bSRajagopal Venkat dev_err(&devfreq->dev, "dvfs failed with (%d) error\n", err);
4637e6fdd4bSRajagopal Venkat
464*0aedb319SMukesh Ojha if (devfreq->stop_polling)
465*0aedb319SMukesh Ojha goto out;
466*0aedb319SMukesh Ojha
4677e6fdd4bSRajagopal Venkat queue_delayed_work(devfreq_wq, &devfreq->work,
4687e6fdd4bSRajagopal Venkat msecs_to_jiffies(devfreq->profile->polling_ms));
469cf451adfSLukasz Luba
470*0aedb319SMukesh Ojha out:
471*0aedb319SMukesh Ojha mutex_unlock(&devfreq->lock);
472cf451adfSLukasz Luba trace_devfreq_monitor(devfreq);
4737e6fdd4bSRajagopal Venkat }
4747e6fdd4bSRajagopal Venkat
4757e6fdd4bSRajagopal Venkat /**
4767e6fdd4bSRajagopal Venkat * devfreq_monitor_start() - Start load monitoring of devfreq instance
4777e6fdd4bSRajagopal Venkat * @devfreq: the devfreq instance.
4787e6fdd4bSRajagopal Venkat *
479c46de2fbSManivannan Sadhasivam * Helper function for starting devfreq device load monitoring. By default,
480c46de2fbSManivannan Sadhasivam * deferrable timer is used for load monitoring. But the users can change this
481c46de2fbSManivannan Sadhasivam * behavior using the "timer" type in devfreq_dev_profile. This function will be
482c46de2fbSManivannan Sadhasivam * called by devfreq governor in response to the DEVFREQ_GOV_START event
483c46de2fbSManivannan Sadhasivam * generated while adding a device to the devfreq framework.
4847e6fdd4bSRajagopal Venkat */
devfreq_monitor_start(struct devfreq * devfreq)4857e6fdd4bSRajagopal Venkat void devfreq_monitor_start(struct devfreq *devfreq)
4867e6fdd4bSRajagopal Venkat {
4870dd25a0dSChanwoo Choi if (IS_SUPPORTED_FLAG(devfreq->governor->flags, IRQ_DRIVEN))
4885c0f6c79SDmitry Osipenko return;
4895c0f6c79SDmitry Osipenko
490*0aedb319SMukesh Ojha mutex_lock(&devfreq->lock);
491*0aedb319SMukesh Ojha if (delayed_work_pending(&devfreq->work))
492*0aedb319SMukesh Ojha goto out;
493*0aedb319SMukesh Ojha
4944dc3bab8SChanwoo Choi switch (devfreq->profile->timer) {
4954dc3bab8SChanwoo Choi case DEVFREQ_TIMER_DEFERRABLE:
4967e6fdd4bSRajagopal Venkat INIT_DEFERRABLE_WORK(&devfreq->work, devfreq_monitor);
4974dc3bab8SChanwoo Choi break;
4984dc3bab8SChanwoo Choi case DEVFREQ_TIMER_DELAYED:
4994dc3bab8SChanwoo Choi INIT_DELAYED_WORK(&devfreq->work, devfreq_monitor);
5004dc3bab8SChanwoo Choi break;
5014dc3bab8SChanwoo Choi default:
502*0aedb319SMukesh Ojha goto out;
5034dc3bab8SChanwoo Choi }
5044dc3bab8SChanwoo Choi
5057e6fdd4bSRajagopal Venkat if (devfreq->profile->polling_ms)
5067e6fdd4bSRajagopal Venkat queue_delayed_work(devfreq_wq, &devfreq->work,
5077e6fdd4bSRajagopal Venkat msecs_to_jiffies(devfreq->profile->polling_ms));
508*0aedb319SMukesh Ojha
509*0aedb319SMukesh Ojha out:
510*0aedb319SMukesh Ojha devfreq->stop_polling = false;
511*0aedb319SMukesh Ojha mutex_unlock(&devfreq->lock);
5127e6fdd4bSRajagopal Venkat }
5136dcdd8e3SMyungJoo Ham EXPORT_SYMBOL(devfreq_monitor_start);
5147e6fdd4bSRajagopal Venkat
5157e6fdd4bSRajagopal Venkat /**
5167e6fdd4bSRajagopal Venkat * devfreq_monitor_stop() - Stop load monitoring of a devfreq instance
5177e6fdd4bSRajagopal Venkat * @devfreq: the devfreq instance.
5187e6fdd4bSRajagopal Venkat *
5192c090832SGaël PORTAY * Helper function to stop devfreq device load monitoring. Function
5207e6fdd4bSRajagopal Venkat * to be called from governor in response to DEVFREQ_GOV_STOP
5217e6fdd4bSRajagopal Venkat * event when device is removed from devfreq framework.
5227e6fdd4bSRajagopal Venkat */
devfreq_monitor_stop(struct devfreq * devfreq)5237e6fdd4bSRajagopal Venkat void devfreq_monitor_stop(struct devfreq *devfreq)
5247e6fdd4bSRajagopal Venkat {
5250dd25a0dSChanwoo Choi if (IS_SUPPORTED_FLAG(devfreq->governor->flags, IRQ_DRIVEN))
5265c0f6c79SDmitry Osipenko return;
5275c0f6c79SDmitry Osipenko
528*0aedb319SMukesh Ojha mutex_lock(&devfreq->lock);
529*0aedb319SMukesh Ojha if (devfreq->stop_polling) {
530*0aedb319SMukesh Ojha mutex_unlock(&devfreq->lock);
531*0aedb319SMukesh Ojha return;
532*0aedb319SMukesh Ojha }
533*0aedb319SMukesh Ojha
534*0aedb319SMukesh Ojha devfreq->stop_polling = true;
535*0aedb319SMukesh Ojha mutex_unlock(&devfreq->lock);
5367e6fdd4bSRajagopal Venkat cancel_delayed_work_sync(&devfreq->work);
5377e6fdd4bSRajagopal Venkat }
5386dcdd8e3SMyungJoo Ham EXPORT_SYMBOL(devfreq_monitor_stop);
5397e6fdd4bSRajagopal Venkat
5407e6fdd4bSRajagopal Venkat /**
5417e6fdd4bSRajagopal Venkat * devfreq_monitor_suspend() - Suspend load monitoring of a devfreq instance
5427e6fdd4bSRajagopal Venkat * @devfreq: the devfreq instance.
5437e6fdd4bSRajagopal Venkat *
5442c090832SGaël PORTAY * Helper function to suspend devfreq device load monitoring. Function
5457e6fdd4bSRajagopal Venkat * to be called from governor in response to DEVFREQ_GOV_SUSPEND
5467e6fdd4bSRajagopal Venkat * event or when polling interval is set to zero.
5477e6fdd4bSRajagopal Venkat *
5487e6fdd4bSRajagopal Venkat * Note: Though this function is same as devfreq_monitor_stop(),
5497e6fdd4bSRajagopal Venkat * intentionally kept separate to provide hooks for collecting
5507e6fdd4bSRajagopal Venkat * transition statistics.
5517e6fdd4bSRajagopal Venkat */
devfreq_monitor_suspend(struct devfreq * devfreq)5527e6fdd4bSRajagopal Venkat void devfreq_monitor_suspend(struct devfreq *devfreq)
5537e6fdd4bSRajagopal Venkat {
5547e6fdd4bSRajagopal Venkat mutex_lock(&devfreq->lock);
5557e6fdd4bSRajagopal Venkat if (devfreq->stop_polling) {
5567e6fdd4bSRajagopal Venkat mutex_unlock(&devfreq->lock);
5577e6fdd4bSRajagopal Venkat return;
5587e6fdd4bSRajagopal Venkat }
5597e6fdd4bSRajagopal Venkat
56039688ce6SRajagopal Venkat devfreq_update_status(devfreq, devfreq->previous_freq);
5617e6fdd4bSRajagopal Venkat devfreq->stop_polling = true;
5627e6fdd4bSRajagopal Venkat mutex_unlock(&devfreq->lock);
5635c0f6c79SDmitry Osipenko
5640dd25a0dSChanwoo Choi if (IS_SUPPORTED_FLAG(devfreq->governor->flags, IRQ_DRIVEN))
5655c0f6c79SDmitry Osipenko return;
5665c0f6c79SDmitry Osipenko
5677e6fdd4bSRajagopal Venkat cancel_delayed_work_sync(&devfreq->work);
5687e6fdd4bSRajagopal Venkat }
5696dcdd8e3SMyungJoo Ham EXPORT_SYMBOL(devfreq_monitor_suspend);
5707e6fdd4bSRajagopal Venkat
5717e6fdd4bSRajagopal Venkat /**
5727e6fdd4bSRajagopal Venkat * devfreq_monitor_resume() - Resume load monitoring of a devfreq instance
5737e6fdd4bSRajagopal Venkat * @devfreq: the devfreq instance.
5747e6fdd4bSRajagopal Venkat *
5752c090832SGaël PORTAY * Helper function to resume devfreq device load monitoring. Function
5767e6fdd4bSRajagopal Venkat * to be called from governor in response to DEVFREQ_GOV_RESUME
5777e6fdd4bSRajagopal Venkat * event or when polling interval is set to non-zero.
5787e6fdd4bSRajagopal Venkat */
devfreq_monitor_resume(struct devfreq * devfreq)5797e6fdd4bSRajagopal Venkat void devfreq_monitor_resume(struct devfreq *devfreq)
5807e6fdd4bSRajagopal Venkat {
58139688ce6SRajagopal Venkat unsigned long freq;
58239688ce6SRajagopal Venkat
5837e6fdd4bSRajagopal Venkat mutex_lock(&devfreq->lock);
5840dd25a0dSChanwoo Choi
5850dd25a0dSChanwoo Choi if (IS_SUPPORTED_FLAG(devfreq->governor->flags, IRQ_DRIVEN))
5860dd25a0dSChanwoo Choi goto out_update;
5870dd25a0dSChanwoo Choi
5887e6fdd4bSRajagopal Venkat if (!devfreq->stop_polling)
5897e6fdd4bSRajagopal Venkat goto out;
5907e6fdd4bSRajagopal Venkat
5917e6fdd4bSRajagopal Venkat if (!delayed_work_pending(&devfreq->work) &&
5927e6fdd4bSRajagopal Venkat devfreq->profile->polling_ms)
5937e6fdd4bSRajagopal Venkat queue_delayed_work(devfreq_wq, &devfreq->work,
5947e6fdd4bSRajagopal Venkat msecs_to_jiffies(devfreq->profile->polling_ms));
59539688ce6SRajagopal Venkat
5965c0f6c79SDmitry Osipenko out_update:
5971ebd0bc0SKamil Konieczny devfreq->stats.last_update = get_jiffies_64();
5987e6fdd4bSRajagopal Venkat devfreq->stop_polling = false;
5997e6fdd4bSRajagopal Venkat
60039688ce6SRajagopal Venkat if (devfreq->profile->get_cur_freq &&
60139688ce6SRajagopal Venkat !devfreq->profile->get_cur_freq(devfreq->dev.parent, &freq))
60239688ce6SRajagopal Venkat devfreq->previous_freq = freq;
60339688ce6SRajagopal Venkat
6047e6fdd4bSRajagopal Venkat out:
6057e6fdd4bSRajagopal Venkat mutex_unlock(&devfreq->lock);
6067e6fdd4bSRajagopal Venkat }
6076dcdd8e3SMyungJoo Ham EXPORT_SYMBOL(devfreq_monitor_resume);
6087e6fdd4bSRajagopal Venkat
6097e6fdd4bSRajagopal Venkat /**
6103a1ec2e8SChanwoo Choi * devfreq_update_interval() - Update device devfreq monitoring interval
6117e6fdd4bSRajagopal Venkat * @devfreq: the devfreq instance.
6127e6fdd4bSRajagopal Venkat * @delay: new polling interval to be set.
6137e6fdd4bSRajagopal Venkat *
6147e6fdd4bSRajagopal Venkat * Helper function to set new load monitoring polling interval. Function
6153a1ec2e8SChanwoo Choi * to be called from governor in response to DEVFREQ_GOV_UPDATE_INTERVAL event.
6167e6fdd4bSRajagopal Venkat */
devfreq_update_interval(struct devfreq * devfreq,unsigned int * delay)6173a1ec2e8SChanwoo Choi void devfreq_update_interval(struct devfreq *devfreq, unsigned int *delay)
6187e6fdd4bSRajagopal Venkat {
6197e6fdd4bSRajagopal Venkat unsigned int cur_delay = devfreq->profile->polling_ms;
6207e6fdd4bSRajagopal Venkat unsigned int new_delay = *delay;
6217e6fdd4bSRajagopal Venkat
6227e6fdd4bSRajagopal Venkat mutex_lock(&devfreq->lock);
6237e6fdd4bSRajagopal Venkat devfreq->profile->polling_ms = new_delay;
6247e6fdd4bSRajagopal Venkat
6250dd25a0dSChanwoo Choi if (IS_SUPPORTED_FLAG(devfreq->governor->flags, IRQ_DRIVEN))
6267e6fdd4bSRajagopal Venkat goto out;
6277e6fdd4bSRajagopal Venkat
6280dd25a0dSChanwoo Choi if (devfreq->stop_polling)
6295c0f6c79SDmitry Osipenko goto out;
6305c0f6c79SDmitry Osipenko
6317e6fdd4bSRajagopal Venkat /* if new delay is zero, stop polling */
6327e6fdd4bSRajagopal Venkat if (!new_delay) {
6337e6fdd4bSRajagopal Venkat mutex_unlock(&devfreq->lock);
6347e6fdd4bSRajagopal Venkat cancel_delayed_work_sync(&devfreq->work);
6357e6fdd4bSRajagopal Venkat return;
6367e6fdd4bSRajagopal Venkat }
6377e6fdd4bSRajagopal Venkat
6387e6fdd4bSRajagopal Venkat /* if current delay is zero, start polling with new delay */
6397e6fdd4bSRajagopal Venkat if (!cur_delay) {
6407e6fdd4bSRajagopal Venkat queue_delayed_work(devfreq_wq, &devfreq->work,
6417e6fdd4bSRajagopal Venkat msecs_to_jiffies(devfreq->profile->polling_ms));
6427e6fdd4bSRajagopal Venkat goto out;
6437e6fdd4bSRajagopal Venkat }
6447e6fdd4bSRajagopal Venkat
6457e6fdd4bSRajagopal Venkat /* if current delay is greater than new delay, restart polling */
6467e6fdd4bSRajagopal Venkat if (cur_delay > new_delay) {
6477e6fdd4bSRajagopal Venkat mutex_unlock(&devfreq->lock);
6487e6fdd4bSRajagopal Venkat cancel_delayed_work_sync(&devfreq->work);
6497e6fdd4bSRajagopal Venkat mutex_lock(&devfreq->lock);
6507e6fdd4bSRajagopal Venkat if (!devfreq->stop_polling)
6517e6fdd4bSRajagopal Venkat queue_delayed_work(devfreq_wq, &devfreq->work,
6527e6fdd4bSRajagopal Venkat msecs_to_jiffies(devfreq->profile->polling_ms));
6537e6fdd4bSRajagopal Venkat }
6547e6fdd4bSRajagopal Venkat out:
6557e6fdd4bSRajagopal Venkat mutex_unlock(&devfreq->lock);
6567e6fdd4bSRajagopal Venkat }
6573a1ec2e8SChanwoo Choi EXPORT_SYMBOL(devfreq_update_interval);
658a3c98b8bSMyungJoo Ham
659a3c98b8bSMyungJoo Ham /**
660a3c98b8bSMyungJoo Ham * devfreq_notifier_call() - Notify that the device frequency requirements
661a3c98b8bSMyungJoo Ham * has been changed out of devfreq framework.
662c5b4a1c1SNishanth Menon * @nb: the notifier_block (supposed to be devfreq->nb)
663c5b4a1c1SNishanth Menon * @type: not used
664c5b4a1c1SNishanth Menon * @devp: not used
665a3c98b8bSMyungJoo Ham *
666a3c98b8bSMyungJoo Ham * Called by a notifier that uses devfreq->nb.
667a3c98b8bSMyungJoo Ham */
devfreq_notifier_call(struct notifier_block * nb,unsigned long type,void * devp)668a3c98b8bSMyungJoo Ham static int devfreq_notifier_call(struct notifier_block *nb, unsigned long type,
669a3c98b8bSMyungJoo Ham void *devp)
670a3c98b8bSMyungJoo Ham {
671a3c98b8bSMyungJoo Ham struct devfreq *devfreq = container_of(nb, struct devfreq, nb);
672e876e710SLeonard Crestez int err = -EINVAL;
673a3c98b8bSMyungJoo Ham
674a3c98b8bSMyungJoo Ham mutex_lock(&devfreq->lock);
675f1d981eaSChanwoo Choi
676f1d981eaSChanwoo Choi devfreq->scaling_min_freq = find_available_min_freq(devfreq);
677e876e710SLeonard Crestez if (!devfreq->scaling_min_freq)
678e876e710SLeonard Crestez goto out;
679f1d981eaSChanwoo Choi
680f1d981eaSChanwoo Choi devfreq->scaling_max_freq = find_available_max_freq(devfreq);
681e7cc792dSLeonard Crestez if (!devfreq->scaling_max_freq) {
682e7cc792dSLeonard Crestez devfreq->scaling_max_freq = ULONG_MAX;
683e876e710SLeonard Crestez goto out;
684e7cc792dSLeonard Crestez }
685f1d981eaSChanwoo Choi
686e876e710SLeonard Crestez err = update_devfreq(devfreq);
687a3c98b8bSMyungJoo Ham
688e876e710SLeonard Crestez out:
689e876e710SLeonard Crestez mutex_unlock(&devfreq->lock);
690e876e710SLeonard Crestez if (err)
691e876e710SLeonard Crestez dev_err(devfreq->dev.parent,
692e876e710SLeonard Crestez "failed to update frequency from OPP notifier (%d)\n",
693e876e710SLeonard Crestez err);
694e876e710SLeonard Crestez
695e876e710SLeonard Crestez return NOTIFY_OK;
696a3c98b8bSMyungJoo Ham }
697a3c98b8bSMyungJoo Ham
698a3c98b8bSMyungJoo Ham /**
69905d7ae15SLeonard Crestez * qos_notifier_call() - Common handler for QoS constraints.
70005d7ae15SLeonard Crestez * @devfreq: the devfreq instance.
70105d7ae15SLeonard Crestez */
qos_notifier_call(struct devfreq * devfreq)70205d7ae15SLeonard Crestez static int qos_notifier_call(struct devfreq *devfreq)
70305d7ae15SLeonard Crestez {
70405d7ae15SLeonard Crestez int err;
70505d7ae15SLeonard Crestez
70605d7ae15SLeonard Crestez mutex_lock(&devfreq->lock);
70705d7ae15SLeonard Crestez err = update_devfreq(devfreq);
70805d7ae15SLeonard Crestez mutex_unlock(&devfreq->lock);
70905d7ae15SLeonard Crestez if (err)
71005d7ae15SLeonard Crestez dev_err(devfreq->dev.parent,
71105d7ae15SLeonard Crestez "failed to update frequency from PM QoS (%d)\n",
71205d7ae15SLeonard Crestez err);
71305d7ae15SLeonard Crestez
71405d7ae15SLeonard Crestez return NOTIFY_OK;
71505d7ae15SLeonard Crestez }
71605d7ae15SLeonard Crestez
71705d7ae15SLeonard Crestez /**
71805d7ae15SLeonard Crestez * qos_min_notifier_call() - Callback for QoS min_freq changes.
71905d7ae15SLeonard Crestez * @nb: Should be devfreq->nb_min
720c9deb748SMauro Carvalho Chehab * @val: not used
721c9deb748SMauro Carvalho Chehab * @ptr: not used
72205d7ae15SLeonard Crestez */
qos_min_notifier_call(struct notifier_block * nb,unsigned long val,void * ptr)72305d7ae15SLeonard Crestez static int qos_min_notifier_call(struct notifier_block *nb,
72405d7ae15SLeonard Crestez unsigned long val, void *ptr)
72505d7ae15SLeonard Crestez {
72605d7ae15SLeonard Crestez return qos_notifier_call(container_of(nb, struct devfreq, nb_min));
72705d7ae15SLeonard Crestez }
72805d7ae15SLeonard Crestez
72905d7ae15SLeonard Crestez /**
73005d7ae15SLeonard Crestez * qos_max_notifier_call() - Callback for QoS max_freq changes.
73105d7ae15SLeonard Crestez * @nb: Should be devfreq->nb_max
732c9deb748SMauro Carvalho Chehab * @val: not used
733c9deb748SMauro Carvalho Chehab * @ptr: not used
73405d7ae15SLeonard Crestez */
qos_max_notifier_call(struct notifier_block * nb,unsigned long val,void * ptr)73505d7ae15SLeonard Crestez static int qos_max_notifier_call(struct notifier_block *nb,
73605d7ae15SLeonard Crestez unsigned long val, void *ptr)
73705d7ae15SLeonard Crestez {
73805d7ae15SLeonard Crestez return qos_notifier_call(container_of(nb, struct devfreq, nb_max));
73905d7ae15SLeonard Crestez }
74005d7ae15SLeonard Crestez
74105d7ae15SLeonard Crestez /**
74229b6968bSChanwoo Choi * devfreq_dev_release() - Callback for struct device to release the device.
74329b6968bSChanwoo Choi * @dev: the devfreq device
74429b6968bSChanwoo Choi *
74529b6968bSChanwoo Choi * Remove devfreq from the list and release its resources.
746a3c98b8bSMyungJoo Ham */
devfreq_dev_release(struct device * dev)74729b6968bSChanwoo Choi static void devfreq_dev_release(struct device *dev)
748a3c98b8bSMyungJoo Ham {
74929b6968bSChanwoo Choi struct devfreq *devfreq = to_devfreq(dev);
75005d7ae15SLeonard Crestez int err;
75129b6968bSChanwoo Choi
7527e6fdd4bSRajagopal Venkat mutex_lock(&devfreq_list_lock);
7537e6fdd4bSRajagopal Venkat list_del(&devfreq->node);
7547e6fdd4bSRajagopal Venkat mutex_unlock(&devfreq_list_lock);
755a3c98b8bSMyungJoo Ham
75605d7ae15SLeonard Crestez err = dev_pm_qos_remove_notifier(devfreq->dev.parent, &devfreq->nb_max,
75705d7ae15SLeonard Crestez DEV_PM_QOS_MAX_FREQUENCY);
75805d7ae15SLeonard Crestez if (err && err != -ENOENT)
75905d7ae15SLeonard Crestez dev_warn(dev->parent,
76005d7ae15SLeonard Crestez "Failed to remove max_freq notifier: %d\n", err);
76105d7ae15SLeonard Crestez err = dev_pm_qos_remove_notifier(devfreq->dev.parent, &devfreq->nb_min,
76205d7ae15SLeonard Crestez DEV_PM_QOS_MIN_FREQUENCY);
76305d7ae15SLeonard Crestez if (err && err != -ENOENT)
76405d7ae15SLeonard Crestez dev_warn(dev->parent,
76505d7ae15SLeonard Crestez "Failed to remove min_freq notifier: %d\n", err);
76605d7ae15SLeonard Crestez
76727dbc542SLeonard Crestez if (dev_pm_qos_request_active(&devfreq->user_max_freq_req)) {
76827dbc542SLeonard Crestez err = dev_pm_qos_remove_request(&devfreq->user_max_freq_req);
7694c6abef7SLeonard Crestez if (err < 0)
77027dbc542SLeonard Crestez dev_warn(dev->parent,
77127dbc542SLeonard Crestez "Failed to remove max_freq request: %d\n", err);
77227dbc542SLeonard Crestez }
77327dbc542SLeonard Crestez if (dev_pm_qos_request_active(&devfreq->user_min_freq_req)) {
77427dbc542SLeonard Crestez err = dev_pm_qos_remove_request(&devfreq->user_min_freq_req);
7754c6abef7SLeonard Crestez if (err < 0)
77627dbc542SLeonard Crestez dev_warn(dev->parent,
77727dbc542SLeonard Crestez "Failed to remove min_freq request: %d\n", err);
77827dbc542SLeonard Crestez }
77927dbc542SLeonard Crestez
780a3c98b8bSMyungJoo Ham if (devfreq->profile->exit)
781a3c98b8bSMyungJoo Ham devfreq->profile->exit(devfreq->dev.parent);
782a3c98b8bSMyungJoo Ham
78326f9c7ccSSaravana Kannan if (devfreq->opp_table)
78426f9c7ccSSaravana Kannan dev_pm_opp_put_opp_table(devfreq->opp_table);
78526f9c7ccSSaravana Kannan
786a3c98b8bSMyungJoo Ham mutex_destroy(&devfreq->lock);
7875693d077SBoris Brezillon srcu_cleanup_notifier_head(&devfreq->transition_notifier_list);
788a3c98b8bSMyungJoo Ham kfree(devfreq);
789a3c98b8bSMyungJoo Ham }
790a3c98b8bSMyungJoo Ham
7915f1a9066SChanwoo Choi static void create_sysfs_files(struct devfreq *devfreq,
7925f1a9066SChanwoo Choi const struct devfreq_governor *gov);
7935f1a9066SChanwoo Choi static void remove_sysfs_files(struct devfreq *devfreq,
7945f1a9066SChanwoo Choi const struct devfreq_governor *gov);
7955f1a9066SChanwoo Choi
796a3c98b8bSMyungJoo Ham /**
797a3c98b8bSMyungJoo Ham * devfreq_add_device() - Add devfreq feature to the device
798a3c98b8bSMyungJoo Ham * @dev: the device to add devfreq feature.
799a3c98b8bSMyungJoo Ham * @profile: device-specific profile to run devfreq.
8001b5c1be2SNishanth Menon * @governor_name: name of the policy to choose frequency.
8015fdded84SKant Fan * @data: devfreq driver pass to governors, governor should not change it.
802a3c98b8bSMyungJoo Ham */
devfreq_add_device(struct device * dev,struct devfreq_dev_profile * profile,const char * governor_name,void * data)803a3c98b8bSMyungJoo Ham struct devfreq *devfreq_add_device(struct device *dev,
804a3c98b8bSMyungJoo Ham struct devfreq_dev_profile *profile,
8051b5c1be2SNishanth Menon const char *governor_name,
806a3c98b8bSMyungJoo Ham void *data)
807a3c98b8bSMyungJoo Ham {
808a3c98b8bSMyungJoo Ham struct devfreq *devfreq;
8091b5c1be2SNishanth Menon struct devfreq_governor *governor;
810713472e5SChanwoo Choi unsigned long min_freq, max_freq;
811a3c98b8bSMyungJoo Ham int err = 0;
812a3c98b8bSMyungJoo Ham
8131b5c1be2SNishanth Menon if (!dev || !profile || !governor_name) {
814a3c98b8bSMyungJoo Ham dev_err(dev, "%s: Invalid parameters.\n", __func__);
815a3c98b8bSMyungJoo Ham return ERR_PTR(-EINVAL);
816a3c98b8bSMyungJoo Ham }
817a3c98b8bSMyungJoo Ham
818a3c98b8bSMyungJoo Ham mutex_lock(&devfreq_list_lock);
819a3c98b8bSMyungJoo Ham devfreq = find_device_devfreq(dev);
820a3c98b8bSMyungJoo Ham mutex_unlock(&devfreq_list_lock);
821a3c98b8bSMyungJoo Ham if (!IS_ERR(devfreq)) {
822df4d7b14SMatthias Kaehlcke dev_err(dev, "%s: devfreq device already exists!\n",
8239d0109beSChanwoo Choi __func__);
824a3c98b8bSMyungJoo Ham err = -EINVAL;
8253f19f08aSAxel Lin goto err_out;
826a3c98b8bSMyungJoo Ham }
827a3c98b8bSMyungJoo Ham
828a3c98b8bSMyungJoo Ham devfreq = kzalloc(sizeof(struct devfreq), GFP_KERNEL);
829a3c98b8bSMyungJoo Ham if (!devfreq) {
830a3c98b8bSMyungJoo Ham err = -ENOMEM;
8313f19f08aSAxel Lin goto err_out;
832a3c98b8bSMyungJoo Ham }
833a3c98b8bSMyungJoo Ham
834a3c98b8bSMyungJoo Ham mutex_init(&devfreq->lock);
835a3c98b8bSMyungJoo Ham mutex_lock(&devfreq->lock);
836a3c98b8bSMyungJoo Ham devfreq->dev.parent = dev;
837a3c98b8bSMyungJoo Ham devfreq->dev.class = devfreq_class;
838a3c98b8bSMyungJoo Ham devfreq->dev.release = devfreq_dev_release;
83942a6b25eSLeonard Crestez INIT_LIST_HEAD(&devfreq->node);
840a3c98b8bSMyungJoo Ham devfreq->profile = profile;
841a3c98b8bSMyungJoo Ham devfreq->previous_freq = profile->initial_freq;
8428d39fc08SLukasz Luba devfreq->last_status.current_frequency = profile->initial_freq;
843a3c98b8bSMyungJoo Ham devfreq->data = data;
844a3c98b8bSMyungJoo Ham devfreq->nb.notifier_call = devfreq_notifier_call;
845a3c98b8bSMyungJoo Ham
8464dc3bab8SChanwoo Choi if (devfreq->profile->timer < 0
8474dc3bab8SChanwoo Choi || devfreq->profile->timer >= DEVFREQ_TIMER_NUM) {
8488b50a799SLukasz Luba mutex_unlock(&devfreq->lock);
84918b380edSYueHaibing err = -EINVAL;
8508b50a799SLukasz Luba goto err_dev;
8514dc3bab8SChanwoo Choi }
8524dc3bab8SChanwoo Choi
8535cf79c29SSamuel Holland if (!devfreq->profile->max_state || !devfreq->profile->freq_table) {
8540ec09ac2SChanwoo Choi mutex_unlock(&devfreq->lock);
855ea572f81SChanwoo Choi err = set_freq_table(devfreq);
856ea572f81SChanwoo Choi if (err < 0)
857a9487917SYangtao Li goto err_dev;
8580ec09ac2SChanwoo Choi mutex_lock(&devfreq->lock);
859b5d281f6SChristian Marangi } else {
860b5d281f6SChristian Marangi devfreq->freq_table = devfreq->profile->freq_table;
861b5d281f6SChristian Marangi devfreq->max_state = devfreq->profile->max_state;
8620ec09ac2SChanwoo Choi }
8630ec09ac2SChanwoo Choi
8642c2cb1e6SMatthias Kaehlcke devfreq->scaling_min_freq = find_available_min_freq(devfreq);
8652c2cb1e6SMatthias Kaehlcke if (!devfreq->scaling_min_freq) {
866ab8f58adSChanwoo Choi mutex_unlock(&devfreq->lock);
867ab8f58adSChanwoo Choi err = -EINVAL;
868ab8f58adSChanwoo Choi goto err_dev;
869ab8f58adSChanwoo Choi }
870ab8f58adSChanwoo Choi
8712c2cb1e6SMatthias Kaehlcke devfreq->scaling_max_freq = find_available_max_freq(devfreq);
8722c2cb1e6SMatthias Kaehlcke if (!devfreq->scaling_max_freq) {
873ab8f58adSChanwoo Choi mutex_unlock(&devfreq->lock);
874ab8f58adSChanwoo Choi err = -EINVAL;
875ab8f58adSChanwoo Choi goto err_dev;
876ab8f58adSChanwoo Choi }
877ab8f58adSChanwoo Choi
878713472e5SChanwoo Choi devfreq_get_freq_range(devfreq, &min_freq, &max_freq);
879713472e5SChanwoo Choi
88083f8ca45SLukasz Luba devfreq->suspend_freq = dev_pm_opp_get_suspend_opp_freq(dev);
88126f9c7ccSSaravana Kannan devfreq->opp_table = dev_pm_opp_get_opp_table(dev);
88226f9c7ccSSaravana Kannan if (IS_ERR(devfreq->opp_table))
88326f9c7ccSSaravana Kannan devfreq->opp_table = NULL;
88426f9c7ccSSaravana Kannan
88583f8ca45SLukasz Luba atomic_set(&devfreq->suspend_count, 0);
88683f8ca45SLukasz Luba
88766d0e797SOrson Zhai dev_set_name(&devfreq->dev, "%s", dev_name(dev));
888a3c98b8bSMyungJoo Ham err = device_register(&devfreq->dev);
889a3c98b8bSMyungJoo Ham if (err) {
8907e6fdd4bSRajagopal Venkat mutex_unlock(&devfreq->lock);
8912d803dc8SArvind Yadav put_device(&devfreq->dev);
8922d803dc8SArvind Yadav goto err_out;
893a3c98b8bSMyungJoo Ham }
894a3c98b8bSMyungJoo Ham
8951ebd0bc0SKamil Konieczny devfreq->stats.trans_table = devm_kzalloc(&devfreq->dev,
896a86854d0SKees Cook array3_size(sizeof(unsigned int),
897b5d281f6SChristian Marangi devfreq->max_state,
898b5d281f6SChristian Marangi devfreq->max_state),
8993e1d7fb0SMyungJoo Ham GFP_KERNEL);
9001ebd0bc0SKamil Konieczny if (!devfreq->stats.trans_table) {
90125846fa1SYangtao Li mutex_unlock(&devfreq->lock);
90225846fa1SYangtao Li err = -ENOMEM;
90325846fa1SYangtao Li goto err_devfreq;
90425846fa1SYangtao Li }
90525846fa1SYangtao Li
9061ebd0bc0SKamil Konieczny devfreq->stats.time_in_state = devm_kcalloc(&devfreq->dev,
907b5d281f6SChristian Marangi devfreq->max_state,
9081ebd0bc0SKamil Konieczny sizeof(*devfreq->stats.time_in_state),
9093e1d7fb0SMyungJoo Ham GFP_KERNEL);
9101ebd0bc0SKamil Konieczny if (!devfreq->stats.time_in_state) {
91125846fa1SYangtao Li mutex_unlock(&devfreq->lock);
91225846fa1SYangtao Li err = -ENOMEM;
91325846fa1SYangtao Li goto err_devfreq;
91425846fa1SYangtao Li }
91525846fa1SYangtao Li
9161ebd0bc0SKamil Konieczny devfreq->stats.total_trans = 0;
9171ebd0bc0SKamil Konieczny devfreq->stats.last_update = get_jiffies_64();
9183e1d7fb0SMyungJoo Ham
9190fe3a664SChanwoo Choi srcu_init_notifier_head(&devfreq->transition_notifier_list);
9200fe3a664SChanwoo Choi
921a3c98b8bSMyungJoo Ham mutex_unlock(&devfreq->lock);
922a3c98b8bSMyungJoo Ham
92327dbc542SLeonard Crestez err = dev_pm_qos_add_request(dev, &devfreq->user_min_freq_req,
92427dbc542SLeonard Crestez DEV_PM_QOS_MIN_FREQUENCY, 0);
92527dbc542SLeonard Crestez if (err < 0)
92627dbc542SLeonard Crestez goto err_devfreq;
92727dbc542SLeonard Crestez err = dev_pm_qos_add_request(dev, &devfreq->user_max_freq_req,
92827dbc542SLeonard Crestez DEV_PM_QOS_MAX_FREQUENCY,
92927dbc542SLeonard Crestez PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE);
93027dbc542SLeonard Crestez if (err < 0)
93127dbc542SLeonard Crestez goto err_devfreq;
93227dbc542SLeonard Crestez
93305d7ae15SLeonard Crestez devfreq->nb_min.notifier_call = qos_min_notifier_call;
934ec894883Spierre Kuo err = dev_pm_qos_add_notifier(dev, &devfreq->nb_min,
93505d7ae15SLeonard Crestez DEV_PM_QOS_MIN_FREQUENCY);
93605d7ae15SLeonard Crestez if (err)
93705d7ae15SLeonard Crestez goto err_devfreq;
93805d7ae15SLeonard Crestez
93905d7ae15SLeonard Crestez devfreq->nb_max.notifier_call = qos_max_notifier_call;
940ec894883Spierre Kuo err = dev_pm_qos_add_notifier(dev, &devfreq->nb_max,
94105d7ae15SLeonard Crestez DEV_PM_QOS_MAX_FREQUENCY);
94205d7ae15SLeonard Crestez if (err)
94305d7ae15SLeonard Crestez goto err_devfreq;
94405d7ae15SLeonard Crestez
945a3c98b8bSMyungJoo Ham mutex_lock(&devfreq_list_lock);
946a3c98b8bSMyungJoo Ham
94796ffcdf2SChanwoo Choi governor = try_then_request_governor(governor_name);
94873613b16SChanwoo Choi if (IS_ERR(governor)) {
94973613b16SChanwoo Choi dev_err(dev, "%s: Unable to find governor for the device\n",
95073613b16SChanwoo Choi __func__);
95173613b16SChanwoo Choi err = PTR_ERR(governor);
95273613b16SChanwoo Choi goto err_init;
95373613b16SChanwoo Choi }
95473613b16SChanwoo Choi
9551b5c1be2SNishanth Menon devfreq->governor = governor;
95673613b16SChanwoo Choi err = devfreq->governor->event_handler(devfreq, DEVFREQ_GOV_START,
95773613b16SChanwoo Choi NULL);
9587e6fdd4bSRajagopal Venkat if (err) {
959e52b045fSChristian 'Ansuel' Marangi dev_err_probe(dev, err,
960e52b045fSChristian 'Ansuel' Marangi "%s: Unable to start governor for the device\n",
9617e6fdd4bSRajagopal Venkat __func__);
9627e6fdd4bSRajagopal Venkat goto err_init;
9637e6fdd4bSRajagopal Venkat }
9645f1a9066SChanwoo Choi create_sysfs_files(devfreq, devfreq->governor);
96523c7b54cSEnric Balletbo i Serra
96623c7b54cSEnric Balletbo i Serra list_add(&devfreq->node, &devfreq_list);
96723c7b54cSEnric Balletbo i Serra
9680f376c9cSAxel Lin mutex_unlock(&devfreq_list_lock);
9697e6fdd4bSRajagopal Venkat
9701224451bSDaniel Lezcano if (devfreq->profile->is_cooling_device) {
9711224451bSDaniel Lezcano devfreq->cdev = devfreq_cooling_em_register(devfreq, NULL);
9721224451bSDaniel Lezcano if (IS_ERR(devfreq->cdev))
9731224451bSDaniel Lezcano devfreq->cdev = NULL;
9741224451bSDaniel Lezcano }
9751224451bSDaniel Lezcano
9763f19f08aSAxel Lin return devfreq;
9773f19f08aSAxel Lin
978a3c98b8bSMyungJoo Ham err_init:
9790f376c9cSAxel Lin mutex_unlock(&devfreq_list_lock);
98025846fa1SYangtao Li err_devfreq:
9812f061fd0SVincent Donnefort devfreq_remove_device(devfreq);
9822d803dc8SArvind Yadav devfreq = NULL;
9839e14de10SChanwoo Choi err_dev:
9849e14de10SChanwoo Choi kfree(devfreq);
9853f19f08aSAxel Lin err_out:
986a3c98b8bSMyungJoo Ham return ERR_PTR(err);
987a3c98b8bSMyungJoo Ham }
9887e6fdd4bSRajagopal Venkat EXPORT_SYMBOL(devfreq_add_device);
989a3c98b8bSMyungJoo Ham
990a3c98b8bSMyungJoo Ham /**
991a3c98b8bSMyungJoo Ham * devfreq_remove_device() - Remove devfreq feature from a device.
992c5b4a1c1SNishanth Menon * @devfreq: the devfreq instance to be removed
993de9c7394SMyungJoo Ham *
994de9c7394SMyungJoo Ham * The opposite of devfreq_add_device().
995a3c98b8bSMyungJoo Ham */
devfreq_remove_device(struct devfreq * devfreq)996a3c98b8bSMyungJoo Ham int devfreq_remove_device(struct devfreq *devfreq)
997a3c98b8bSMyungJoo Ham {
998a3c98b8bSMyungJoo Ham if (!devfreq)
999a3c98b8bSMyungJoo Ham return -EINVAL;
1000a3c98b8bSMyungJoo Ham
10011224451bSDaniel Lezcano devfreq_cooling_unregister(devfreq->cdev);
10021224451bSDaniel Lezcano
10035f1a9066SChanwoo Choi if (devfreq->governor) {
10042f061fd0SVincent Donnefort devfreq->governor->event_handler(devfreq,
10052f061fd0SVincent Donnefort DEVFREQ_GOV_STOP, NULL);
10065f1a9066SChanwoo Choi remove_sysfs_files(devfreq, devfreq->governor);
10075f1a9066SChanwoo Choi }
10085f1a9066SChanwoo Choi
1009585fc83eSChanwoo Choi device_unregister(&devfreq->dev);
1010a3c98b8bSMyungJoo Ham
1011a3c98b8bSMyungJoo Ham return 0;
1012a3c98b8bSMyungJoo Ham }
10137e6fdd4bSRajagopal Venkat EXPORT_SYMBOL(devfreq_remove_device);
1014a3c98b8bSMyungJoo Ham
devm_devfreq_dev_match(struct device * dev,void * res,void * data)10158cd84092SChanwoo Choi static int devm_devfreq_dev_match(struct device *dev, void *res, void *data)
10168cd84092SChanwoo Choi {
10178cd84092SChanwoo Choi struct devfreq **r = res;
10188cd84092SChanwoo Choi
10198cd84092SChanwoo Choi if (WARN_ON(!r || !*r))
10208cd84092SChanwoo Choi return 0;
10218cd84092SChanwoo Choi
10228cd84092SChanwoo Choi return *r == data;
10238cd84092SChanwoo Choi }
10248cd84092SChanwoo Choi
devm_devfreq_dev_release(struct device * dev,void * res)10258cd84092SChanwoo Choi static void devm_devfreq_dev_release(struct device *dev, void *res)
10268cd84092SChanwoo Choi {
10278cd84092SChanwoo Choi devfreq_remove_device(*(struct devfreq **)res);
10288cd84092SChanwoo Choi }
10298cd84092SChanwoo Choi
10308cd84092SChanwoo Choi /**
10318cd84092SChanwoo Choi * devm_devfreq_add_device() - Resource-managed devfreq_add_device()
10328cd84092SChanwoo Choi * @dev: the device to add devfreq feature.
10338cd84092SChanwoo Choi * @profile: device-specific profile to run devfreq.
10348cd84092SChanwoo Choi * @governor_name: name of the policy to choose frequency.
10355fdded84SKant Fan * @data: devfreq driver pass to governors, governor should not change it.
10368cd84092SChanwoo Choi *
10378cd84092SChanwoo Choi * This function manages automatically the memory of devfreq device using device
10388cd84092SChanwoo Choi * resource management and simplify the free operation for memory of devfreq
10398cd84092SChanwoo Choi * device.
10408cd84092SChanwoo Choi */
devm_devfreq_add_device(struct device * dev,struct devfreq_dev_profile * profile,const char * governor_name,void * data)10418cd84092SChanwoo Choi struct devfreq *devm_devfreq_add_device(struct device *dev,
10428cd84092SChanwoo Choi struct devfreq_dev_profile *profile,
10438cd84092SChanwoo Choi const char *governor_name,
10448cd84092SChanwoo Choi void *data)
10458cd84092SChanwoo Choi {
10468cd84092SChanwoo Choi struct devfreq **ptr, *devfreq;
10478cd84092SChanwoo Choi
10488cd84092SChanwoo Choi ptr = devres_alloc(devm_devfreq_dev_release, sizeof(*ptr), GFP_KERNEL);
10498cd84092SChanwoo Choi if (!ptr)
10508cd84092SChanwoo Choi return ERR_PTR(-ENOMEM);
10518cd84092SChanwoo Choi
10528cd84092SChanwoo Choi devfreq = devfreq_add_device(dev, profile, governor_name, data);
10538cd84092SChanwoo Choi if (IS_ERR(devfreq)) {
10548cd84092SChanwoo Choi devres_free(ptr);
1055d1bf2d30SBjorn Andersson return devfreq;
10568cd84092SChanwoo Choi }
10578cd84092SChanwoo Choi
10588cd84092SChanwoo Choi *ptr = devfreq;
10598cd84092SChanwoo Choi devres_add(dev, ptr);
10608cd84092SChanwoo Choi
10618cd84092SChanwoo Choi return devfreq;
10628cd84092SChanwoo Choi }
10638cd84092SChanwoo Choi EXPORT_SYMBOL(devm_devfreq_add_device);
10648cd84092SChanwoo Choi
10658f510aebSChanwoo Choi #ifdef CONFIG_OF
10668f510aebSChanwoo Choi /*
10677b38b7b0SLeonard Crestez * devfreq_get_devfreq_by_node - Get the devfreq device from devicetree
10687b38b7b0SLeonard Crestez * @node - pointer to device_node
10697b38b7b0SLeonard Crestez *
10707b38b7b0SLeonard Crestez * return the instance of devfreq device
10717b38b7b0SLeonard Crestez */
devfreq_get_devfreq_by_node(struct device_node * node)10727b38b7b0SLeonard Crestez struct devfreq *devfreq_get_devfreq_by_node(struct device_node *node)
10737b38b7b0SLeonard Crestez {
10747b38b7b0SLeonard Crestez struct devfreq *devfreq;
10757b38b7b0SLeonard Crestez
10767b38b7b0SLeonard Crestez if (!node)
10777b38b7b0SLeonard Crestez return ERR_PTR(-EINVAL);
10787b38b7b0SLeonard Crestez
10797b38b7b0SLeonard Crestez mutex_lock(&devfreq_list_lock);
10807b38b7b0SLeonard Crestez list_for_each_entry(devfreq, &devfreq_list, node) {
10817b38b7b0SLeonard Crestez if (devfreq->dev.parent
1082fba39536Sye xingchen && device_match_of_node(devfreq->dev.parent, node)) {
10837b38b7b0SLeonard Crestez mutex_unlock(&devfreq_list_lock);
10847b38b7b0SLeonard Crestez return devfreq;
10857b38b7b0SLeonard Crestez }
10867b38b7b0SLeonard Crestez }
10877b38b7b0SLeonard Crestez mutex_unlock(&devfreq_list_lock);
10887b38b7b0SLeonard Crestez
10897b38b7b0SLeonard Crestez return ERR_PTR(-ENODEV);
10907b38b7b0SLeonard Crestez }
10917b38b7b0SLeonard Crestez
10927b38b7b0SLeonard Crestez /*
10938f510aebSChanwoo Choi * devfreq_get_devfreq_by_phandle - Get the devfreq device from devicetree
10948f510aebSChanwoo Choi * @dev - instance to the given device
109586d90fd9SChanwoo Choi * @phandle_name - name of property holding a phandle value
10968f510aebSChanwoo Choi * @index - index into list of devfreq
10978f510aebSChanwoo Choi *
10988f510aebSChanwoo Choi * return the instance of devfreq device
10998f510aebSChanwoo Choi */
devfreq_get_devfreq_by_phandle(struct device * dev,const char * phandle_name,int index)110086d90fd9SChanwoo Choi struct devfreq *devfreq_get_devfreq_by_phandle(struct device *dev,
110186d90fd9SChanwoo Choi const char *phandle_name, int index)
11028f510aebSChanwoo Choi {
11038f510aebSChanwoo Choi struct device_node *node;
11048f510aebSChanwoo Choi struct devfreq *devfreq;
11058f510aebSChanwoo Choi
110686d90fd9SChanwoo Choi if (!dev || !phandle_name)
11078f510aebSChanwoo Choi return ERR_PTR(-EINVAL);
11088f510aebSChanwoo Choi
11098f510aebSChanwoo Choi if (!dev->of_node)
11108f510aebSChanwoo Choi return ERR_PTR(-EINVAL);
11118f510aebSChanwoo Choi
111286d90fd9SChanwoo Choi node = of_parse_phandle(dev->of_node, phandle_name, index);
11138f510aebSChanwoo Choi if (!node)
11148f510aebSChanwoo Choi return ERR_PTR(-ENODEV);
11158f510aebSChanwoo Choi
11167b38b7b0SLeonard Crestez devfreq = devfreq_get_devfreq_by_node(node);
11173427c6f0SPeter Chen of_node_put(node);
11188f510aebSChanwoo Choi
11197b38b7b0SLeonard Crestez return devfreq;
11208f510aebSChanwoo Choi }
11217b38b7b0SLeonard Crestez
11228f510aebSChanwoo Choi #else
devfreq_get_devfreq_by_node(struct device_node * node)11237b38b7b0SLeonard Crestez struct devfreq *devfreq_get_devfreq_by_node(struct device_node *node)
11247b38b7b0SLeonard Crestez {
11257b38b7b0SLeonard Crestez return ERR_PTR(-ENODEV);
11267b38b7b0SLeonard Crestez }
11277b38b7b0SLeonard Crestez
devfreq_get_devfreq_by_phandle(struct device * dev,const char * phandle_name,int index)112886d90fd9SChanwoo Choi struct devfreq *devfreq_get_devfreq_by_phandle(struct device *dev,
112986d90fd9SChanwoo Choi const char *phandle_name, int index)
11308f510aebSChanwoo Choi {
11318f510aebSChanwoo Choi return ERR_PTR(-ENODEV);
11328f510aebSChanwoo Choi }
11338f510aebSChanwoo Choi #endif /* CONFIG_OF */
11347b38b7b0SLeonard Crestez EXPORT_SYMBOL_GPL(devfreq_get_devfreq_by_node);
11358f510aebSChanwoo Choi EXPORT_SYMBOL_GPL(devfreq_get_devfreq_by_phandle);
11368f510aebSChanwoo Choi
11378cd84092SChanwoo Choi /**
11388cd84092SChanwoo Choi * devm_devfreq_remove_device() - Resource-managed devfreq_remove_device()
1139e2fc1677SKrzysztof Kozlowski * @dev: the device from which to remove devfreq feature.
11408cd84092SChanwoo Choi * @devfreq: the devfreq instance to be removed
11418cd84092SChanwoo Choi */
devm_devfreq_remove_device(struct device * dev,struct devfreq * devfreq)11428cd84092SChanwoo Choi void devm_devfreq_remove_device(struct device *dev, struct devfreq *devfreq)
11438cd84092SChanwoo Choi {
11448cd84092SChanwoo Choi WARN_ON(devres_release(dev, devm_devfreq_dev_release,
11458cd84092SChanwoo Choi devm_devfreq_dev_match, devfreq));
11468cd84092SChanwoo Choi }
11478cd84092SChanwoo Choi EXPORT_SYMBOL(devm_devfreq_remove_device);
11488cd84092SChanwoo Choi
1149206c30cfSRajagopal Venkat /**
1150206c30cfSRajagopal Venkat * devfreq_suspend_device() - Suspend devfreq of a device.
1151206c30cfSRajagopal Venkat * @devfreq: the devfreq instance to be suspended
1152de9c7394SMyungJoo Ham *
1153de9c7394SMyungJoo Ham * This function is intended to be called by the pm callbacks
1154de9c7394SMyungJoo Ham * (e.g., runtime_suspend, suspend) of the device driver that
1155de9c7394SMyungJoo Ham * holds the devfreq.
1156206c30cfSRajagopal Venkat */
devfreq_suspend_device(struct devfreq * devfreq)1157206c30cfSRajagopal Venkat int devfreq_suspend_device(struct devfreq *devfreq)
1158206c30cfSRajagopal Venkat {
115983f8ca45SLukasz Luba int ret;
116083f8ca45SLukasz Luba
1161206c30cfSRajagopal Venkat if (!devfreq)
1162206c30cfSRajagopal Venkat return -EINVAL;
1163206c30cfSRajagopal Venkat
116483f8ca45SLukasz Luba if (atomic_inc_return(&devfreq->suspend_count) > 1)
11651b5c1be2SNishanth Menon return 0;
11661b5c1be2SNishanth Menon
116783f8ca45SLukasz Luba if (devfreq->governor) {
116883f8ca45SLukasz Luba ret = devfreq->governor->event_handler(devfreq,
1169206c30cfSRajagopal Venkat DEVFREQ_GOV_SUSPEND, NULL);
117083f8ca45SLukasz Luba if (ret)
117183f8ca45SLukasz Luba return ret;
117283f8ca45SLukasz Luba }
117383f8ca45SLukasz Luba
117483f8ca45SLukasz Luba if (devfreq->suspend_freq) {
1175e1e047acSMarek Szyprowski mutex_lock(&devfreq->lock);
117683f8ca45SLukasz Luba ret = devfreq_set_target(devfreq, devfreq->suspend_freq, 0);
1177e1e047acSMarek Szyprowski mutex_unlock(&devfreq->lock);
117883f8ca45SLukasz Luba if (ret)
117983f8ca45SLukasz Luba return ret;
118083f8ca45SLukasz Luba }
118183f8ca45SLukasz Luba
118283f8ca45SLukasz Luba return 0;
1183206c30cfSRajagopal Venkat }
1184206c30cfSRajagopal Venkat EXPORT_SYMBOL(devfreq_suspend_device);
1185206c30cfSRajagopal Venkat
1186206c30cfSRajagopal Venkat /**
1187206c30cfSRajagopal Venkat * devfreq_resume_device() - Resume devfreq of a device.
1188206c30cfSRajagopal Venkat * @devfreq: the devfreq instance to be resumed
1189de9c7394SMyungJoo Ham *
1190de9c7394SMyungJoo Ham * This function is intended to be called by the pm callbacks
1191de9c7394SMyungJoo Ham * (e.g., runtime_resume, resume) of the device driver that
1192de9c7394SMyungJoo Ham * holds the devfreq.
1193206c30cfSRajagopal Venkat */
devfreq_resume_device(struct devfreq * devfreq)1194206c30cfSRajagopal Venkat int devfreq_resume_device(struct devfreq *devfreq)
1195206c30cfSRajagopal Venkat {
119683f8ca45SLukasz Luba int ret;
119783f8ca45SLukasz Luba
1198206c30cfSRajagopal Venkat if (!devfreq)
1199206c30cfSRajagopal Venkat return -EINVAL;
1200206c30cfSRajagopal Venkat
120183f8ca45SLukasz Luba if (atomic_dec_return(&devfreq->suspend_count) >= 1)
12021b5c1be2SNishanth Menon return 0;
12031b5c1be2SNishanth Menon
120483f8ca45SLukasz Luba if (devfreq->resume_freq) {
1205e1e047acSMarek Szyprowski mutex_lock(&devfreq->lock);
120683f8ca45SLukasz Luba ret = devfreq_set_target(devfreq, devfreq->resume_freq, 0);
1207e1e047acSMarek Szyprowski mutex_unlock(&devfreq->lock);
120883f8ca45SLukasz Luba if (ret)
120983f8ca45SLukasz Luba return ret;
121083f8ca45SLukasz Luba }
121183f8ca45SLukasz Luba
121283f8ca45SLukasz Luba if (devfreq->governor) {
121383f8ca45SLukasz Luba ret = devfreq->governor->event_handler(devfreq,
1214206c30cfSRajagopal Venkat DEVFREQ_GOV_RESUME, NULL);
121583f8ca45SLukasz Luba if (ret)
121683f8ca45SLukasz Luba return ret;
121783f8ca45SLukasz Luba }
121883f8ca45SLukasz Luba
121983f8ca45SLukasz Luba return 0;
1220206c30cfSRajagopal Venkat }
1221206c30cfSRajagopal Venkat EXPORT_SYMBOL(devfreq_resume_device);
1222206c30cfSRajagopal Venkat
12233aa173b8SNishanth Menon /**
122459031956SLukasz Luba * devfreq_suspend() - Suspend devfreq governors and devices
122559031956SLukasz Luba *
122659031956SLukasz Luba * Called during system wide Suspend/Hibernate cycles for suspending governors
122759031956SLukasz Luba * and devices preserving the state for resume. On some platforms the devfreq
122859031956SLukasz Luba * device must have precise state (frequency) after resume in order to provide
122959031956SLukasz Luba * fully operating setup.
123059031956SLukasz Luba */
devfreq_suspend(void)123159031956SLukasz Luba void devfreq_suspend(void)
123259031956SLukasz Luba {
123359031956SLukasz Luba struct devfreq *devfreq;
123459031956SLukasz Luba int ret;
123559031956SLukasz Luba
123659031956SLukasz Luba mutex_lock(&devfreq_list_lock);
123759031956SLukasz Luba list_for_each_entry(devfreq, &devfreq_list, node) {
123859031956SLukasz Luba ret = devfreq_suspend_device(devfreq);
123959031956SLukasz Luba if (ret)
124059031956SLukasz Luba dev_err(&devfreq->dev,
124159031956SLukasz Luba "failed to suspend devfreq device\n");
124259031956SLukasz Luba }
124359031956SLukasz Luba mutex_unlock(&devfreq_list_lock);
124459031956SLukasz Luba }
124559031956SLukasz Luba
124659031956SLukasz Luba /**
124759031956SLukasz Luba * devfreq_resume() - Resume devfreq governors and devices
124859031956SLukasz Luba *
124959031956SLukasz Luba * Called during system wide Suspend/Hibernate cycle for resuming governors and
125059031956SLukasz Luba * devices that are suspended with devfreq_suspend().
125159031956SLukasz Luba */
devfreq_resume(void)125259031956SLukasz Luba void devfreq_resume(void)
125359031956SLukasz Luba {
125459031956SLukasz Luba struct devfreq *devfreq;
125559031956SLukasz Luba int ret;
125659031956SLukasz Luba
125759031956SLukasz Luba mutex_lock(&devfreq_list_lock);
125859031956SLukasz Luba list_for_each_entry(devfreq, &devfreq_list, node) {
125959031956SLukasz Luba ret = devfreq_resume_device(devfreq);
126059031956SLukasz Luba if (ret)
126159031956SLukasz Luba dev_warn(&devfreq->dev,
126259031956SLukasz Luba "failed to resume devfreq device\n");
126359031956SLukasz Luba }
126459031956SLukasz Luba mutex_unlock(&devfreq_list_lock);
126559031956SLukasz Luba }
126659031956SLukasz Luba
126759031956SLukasz Luba /**
12683aa173b8SNishanth Menon * devfreq_add_governor() - Add devfreq governor
12693aa173b8SNishanth Menon * @governor: the devfreq governor to be added
12703aa173b8SNishanth Menon */
devfreq_add_governor(struct devfreq_governor * governor)12713aa173b8SNishanth Menon int devfreq_add_governor(struct devfreq_governor *governor)
12723aa173b8SNishanth Menon {
12733aa173b8SNishanth Menon struct devfreq_governor *g;
12741b5c1be2SNishanth Menon struct devfreq *devfreq;
12753aa173b8SNishanth Menon int err = 0;
12763aa173b8SNishanth Menon
12773aa173b8SNishanth Menon if (!governor) {
12783aa173b8SNishanth Menon pr_err("%s: Invalid parameters.\n", __func__);
12793aa173b8SNishanth Menon return -EINVAL;
12803aa173b8SNishanth Menon }
12813aa173b8SNishanth Menon
12823aa173b8SNishanth Menon mutex_lock(&devfreq_list_lock);
12833aa173b8SNishanth Menon g = find_devfreq_governor(governor->name);
12843aa173b8SNishanth Menon if (!IS_ERR(g)) {
12853aa173b8SNishanth Menon pr_err("%s: governor %s already registered\n", __func__,
12863aa173b8SNishanth Menon g->name);
12873aa173b8SNishanth Menon err = -EINVAL;
12883aa173b8SNishanth Menon goto err_out;
12893aa173b8SNishanth Menon }
12903aa173b8SNishanth Menon
12913aa173b8SNishanth Menon list_add(&governor->node, &devfreq_governor_list);
12923aa173b8SNishanth Menon
12931b5c1be2SNishanth Menon list_for_each_entry(devfreq, &devfreq_list, node) {
12941b5c1be2SNishanth Menon int ret = 0;
12951b5c1be2SNishanth Menon struct device *dev = devfreq->dev.parent;
12961b5c1be2SNishanth Menon
129796ffcdf2SChanwoo Choi if (!strncmp(devfreq->governor->name, governor->name,
12981b5c1be2SNishanth Menon DEVFREQ_NAME_LEN)) {
12991b5c1be2SNishanth Menon /* The following should never occur */
13001b5c1be2SNishanth Menon if (devfreq->governor) {
13011b5c1be2SNishanth Menon dev_warn(dev,
13021b5c1be2SNishanth Menon "%s: Governor %s already present\n",
13031b5c1be2SNishanth Menon __func__, devfreq->governor->name);
13041b5c1be2SNishanth Menon ret = devfreq->governor->event_handler(devfreq,
13051b5c1be2SNishanth Menon DEVFREQ_GOV_STOP, NULL);
13061b5c1be2SNishanth Menon if (ret) {
13071b5c1be2SNishanth Menon dev_warn(dev,
13081b5c1be2SNishanth Menon "%s: Governor %s stop = %d\n",
13091b5c1be2SNishanth Menon __func__,
13101b5c1be2SNishanth Menon devfreq->governor->name, ret);
13111b5c1be2SNishanth Menon }
13121b5c1be2SNishanth Menon /* Fall through */
13131b5c1be2SNishanth Menon }
13141b5c1be2SNishanth Menon devfreq->governor = governor;
13151b5c1be2SNishanth Menon ret = devfreq->governor->event_handler(devfreq,
13161b5c1be2SNishanth Menon DEVFREQ_GOV_START, NULL);
13171b5c1be2SNishanth Menon if (ret) {
13181b5c1be2SNishanth Menon dev_warn(dev, "%s: Governor %s start=%d\n",
13191b5c1be2SNishanth Menon __func__, devfreq->governor->name,
13201b5c1be2SNishanth Menon ret);
13211b5c1be2SNishanth Menon }
13221b5c1be2SNishanth Menon }
13231b5c1be2SNishanth Menon }
13241b5c1be2SNishanth Menon
13253aa173b8SNishanth Menon err_out:
13263aa173b8SNishanth Menon mutex_unlock(&devfreq_list_lock);
13273aa173b8SNishanth Menon
13283aa173b8SNishanth Menon return err;
13293aa173b8SNishanth Menon }
13303aa173b8SNishanth Menon EXPORT_SYMBOL(devfreq_add_governor);
13313aa173b8SNishanth Menon
devm_devfreq_remove_governor(void * governor)13321cc55204SDmitry Osipenko static void devm_devfreq_remove_governor(void *governor)
13331cc55204SDmitry Osipenko {
13341cc55204SDmitry Osipenko WARN_ON(devfreq_remove_governor(governor));
13351cc55204SDmitry Osipenko }
13361cc55204SDmitry Osipenko
13371cc55204SDmitry Osipenko /**
13381cc55204SDmitry Osipenko * devm_devfreq_add_governor() - Add devfreq governor
13391cc55204SDmitry Osipenko * @dev: device which adds devfreq governor
13401cc55204SDmitry Osipenko * @governor: the devfreq governor to be added
13411cc55204SDmitry Osipenko *
13421cc55204SDmitry Osipenko * This is a resource-managed variant of devfreq_add_governor().
13431cc55204SDmitry Osipenko */
devm_devfreq_add_governor(struct device * dev,struct devfreq_governor * governor)13441cc55204SDmitry Osipenko int devm_devfreq_add_governor(struct device *dev,
13451cc55204SDmitry Osipenko struct devfreq_governor *governor)
13461cc55204SDmitry Osipenko {
13471cc55204SDmitry Osipenko int err;
13481cc55204SDmitry Osipenko
13491cc55204SDmitry Osipenko err = devfreq_add_governor(governor);
13501cc55204SDmitry Osipenko if (err)
13511cc55204SDmitry Osipenko return err;
13521cc55204SDmitry Osipenko
13531cc55204SDmitry Osipenko return devm_add_action_or_reset(dev, devm_devfreq_remove_governor,
13541cc55204SDmitry Osipenko governor);
13551cc55204SDmitry Osipenko }
13561cc55204SDmitry Osipenko EXPORT_SYMBOL(devm_devfreq_add_governor);
13571cc55204SDmitry Osipenko
13583aa173b8SNishanth Menon /**
1359bafeb42bSMyungJoo Ham * devfreq_remove_governor() - Remove devfreq feature from a device.
13603aa173b8SNishanth Menon * @governor: the devfreq governor to be removed
13613aa173b8SNishanth Menon */
devfreq_remove_governor(struct devfreq_governor * governor)13623aa173b8SNishanth Menon int devfreq_remove_governor(struct devfreq_governor *governor)
13633aa173b8SNishanth Menon {
13643aa173b8SNishanth Menon struct devfreq_governor *g;
13651b5c1be2SNishanth Menon struct devfreq *devfreq;
13663aa173b8SNishanth Menon int err = 0;
13673aa173b8SNishanth Menon
13683aa173b8SNishanth Menon if (!governor) {
13693aa173b8SNishanth Menon pr_err("%s: Invalid parameters.\n", __func__);
13703aa173b8SNishanth Menon return -EINVAL;
13713aa173b8SNishanth Menon }
13723aa173b8SNishanth Menon
13733aa173b8SNishanth Menon mutex_lock(&devfreq_list_lock);
13743aa173b8SNishanth Menon g = find_devfreq_governor(governor->name);
13753aa173b8SNishanth Menon if (IS_ERR(g)) {
13763aa173b8SNishanth Menon pr_err("%s: governor %s not registered\n", __func__,
1377b9e1c8e8SSachin Kamat governor->name);
1378f9c08e2aSSachin Kamat err = PTR_ERR(g);
13793aa173b8SNishanth Menon goto err_out;
13803aa173b8SNishanth Menon }
13811b5c1be2SNishanth Menon list_for_each_entry(devfreq, &devfreq_list, node) {
13821b5c1be2SNishanth Menon int ret;
13831b5c1be2SNishanth Menon struct device *dev = devfreq->dev.parent;
13841b5c1be2SNishanth Menon
138596ffcdf2SChanwoo Choi if (!strncmp(devfreq->governor->name, governor->name,
13861b5c1be2SNishanth Menon DEVFREQ_NAME_LEN)) {
13871b5c1be2SNishanth Menon /* we should have a devfreq governor! */
13881b5c1be2SNishanth Menon if (!devfreq->governor) {
13891b5c1be2SNishanth Menon dev_warn(dev, "%s: Governor %s NOT present\n",
13901b5c1be2SNishanth Menon __func__, governor->name);
13911b5c1be2SNishanth Menon continue;
13921b5c1be2SNishanth Menon /* Fall through */
13931b5c1be2SNishanth Menon }
13941b5c1be2SNishanth Menon ret = devfreq->governor->event_handler(devfreq,
13951b5c1be2SNishanth Menon DEVFREQ_GOV_STOP, NULL);
13961b5c1be2SNishanth Menon if (ret) {
13971b5c1be2SNishanth Menon dev_warn(dev, "%s: Governor %s stop=%d\n",
13981b5c1be2SNishanth Menon __func__, devfreq->governor->name,
13991b5c1be2SNishanth Menon ret);
14001b5c1be2SNishanth Menon }
14011b5c1be2SNishanth Menon devfreq->governor = NULL;
14021b5c1be2SNishanth Menon }
14031b5c1be2SNishanth Menon }
14043aa173b8SNishanth Menon
14053aa173b8SNishanth Menon list_del(&governor->node);
14063aa173b8SNishanth Menon err_out:
14073aa173b8SNishanth Menon mutex_unlock(&devfreq_list_lock);
14083aa173b8SNishanth Menon
14093aa173b8SNishanth Menon return err;
14103aa173b8SNishanth Menon }
14113aa173b8SNishanth Menon EXPORT_SYMBOL(devfreq_remove_governor);
1412a3c98b8bSMyungJoo Ham
name_show(struct device * dev,struct device_attribute * attr,char * buf)14132fee1a7cSChanwoo Choi static ssize_t name_show(struct device *dev,
14142fee1a7cSChanwoo Choi struct device_attribute *attr, char *buf)
14152fee1a7cSChanwoo Choi {
1416483d557eSChanwoo Choi struct devfreq *df = to_devfreq(dev);
1417483d557eSChanwoo Choi return sprintf(buf, "%s\n", dev_name(df->dev.parent));
14182fee1a7cSChanwoo Choi }
14192fee1a7cSChanwoo Choi static DEVICE_ATTR_RO(name);
14202fee1a7cSChanwoo Choi
governor_show(struct device * dev,struct device_attribute * attr,char * buf)1421a93d6b0aSGreg Kroah-Hartman static ssize_t governor_show(struct device *dev,
14229005b650SMyungJoo Ham struct device_attribute *attr, char *buf)
14239005b650SMyungJoo Ham {
1424483d557eSChanwoo Choi struct devfreq *df = to_devfreq(dev);
1425483d557eSChanwoo Choi
1426483d557eSChanwoo Choi if (!df->governor)
14271b5c1be2SNishanth Menon return -EINVAL;
14281b5c1be2SNishanth Menon
1429483d557eSChanwoo Choi return sprintf(buf, "%s\n", df->governor->name);
14309005b650SMyungJoo Ham }
14319005b650SMyungJoo Ham
governor_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)1432a93d6b0aSGreg Kroah-Hartman static ssize_t governor_store(struct device *dev, struct device_attribute *attr,
14330359d1afSNishanth Menon const char *buf, size_t count)
14340359d1afSNishanth Menon {
14350359d1afSNishanth Menon struct devfreq *df = to_devfreq(dev);
14360359d1afSNishanth Menon int ret;
14370359d1afSNishanth Menon char str_governor[DEVFREQ_NAME_LEN + 1];
1438bc658befSSaravana Kannan const struct devfreq_governor *governor, *prev_governor;
14390359d1afSNishanth Menon
1440483d557eSChanwoo Choi if (!df->governor)
1441483d557eSChanwoo Choi return -EINVAL;
1442483d557eSChanwoo Choi
14430359d1afSNishanth Menon ret = sscanf(buf, "%" __stringify(DEVFREQ_NAME_LEN) "s", str_governor);
14440359d1afSNishanth Menon if (ret != 1)
14450359d1afSNishanth Menon return -EINVAL;
14460359d1afSNishanth Menon
14470359d1afSNishanth Menon mutex_lock(&devfreq_list_lock);
144823c7b54cSEnric Balletbo i Serra governor = try_then_request_governor(str_governor);
14490359d1afSNishanth Menon if (IS_ERR(governor)) {
14500359d1afSNishanth Menon ret = PTR_ERR(governor);
14510359d1afSNishanth Menon goto out;
14520359d1afSNishanth Menon }
145314a21e7bSTobias Jakobi if (df->governor == governor) {
145414a21e7bSTobias Jakobi ret = 0;
14550359d1afSNishanth Menon goto out;
14560dd25a0dSChanwoo Choi } else if (IS_SUPPORTED_FLAG(df->governor->flags, IMMUTABLE)
14570dd25a0dSChanwoo Choi || IS_SUPPORTED_FLAG(governor->flags, IMMUTABLE)) {
1458bcf23c79SChanwoo Choi ret = -EINVAL;
1459bcf23c79SChanwoo Choi goto out;
146014a21e7bSTobias Jakobi }
14610359d1afSNishanth Menon
14625f1a9066SChanwoo Choi /*
14635f1a9066SChanwoo Choi * Stop the current governor and remove the specific sysfs files
14645f1a9066SChanwoo Choi * which depend on current governor.
14655f1a9066SChanwoo Choi */
14660359d1afSNishanth Menon ret = df->governor->event_handler(df, DEVFREQ_GOV_STOP, NULL);
14670359d1afSNishanth Menon if (ret) {
14680359d1afSNishanth Menon dev_warn(dev, "%s: Governor %s not stopped(%d)\n",
14690359d1afSNishanth Menon __func__, df->governor->name, ret);
14700359d1afSNishanth Menon goto out;
14710359d1afSNishanth Menon }
14725f1a9066SChanwoo Choi remove_sysfs_files(df, df->governor);
1473483d557eSChanwoo Choi
14745f1a9066SChanwoo Choi /*
14755f1a9066SChanwoo Choi * Start the new governor and create the specific sysfs files
14765f1a9066SChanwoo Choi * which depend on the new governor.
14775f1a9066SChanwoo Choi */
1478bc658befSSaravana Kannan prev_governor = df->governor;
14790359d1afSNishanth Menon df->governor = governor;
14800359d1afSNishanth Menon ret = df->governor->event_handler(df, DEVFREQ_GOV_START, NULL);
1481bc658befSSaravana Kannan if (ret) {
14820359d1afSNishanth Menon dev_warn(dev, "%s: Governor %s not started(%d)\n",
14830359d1afSNishanth Menon __func__, df->governor->name, ret);
14845f1a9066SChanwoo Choi
14855f1a9066SChanwoo Choi /* Restore previous governor */
1486bc658befSSaravana Kannan df->governor = prev_governor;
1487bc658befSSaravana Kannan ret = df->governor->event_handler(df, DEVFREQ_GOV_START, NULL);
1488bc658befSSaravana Kannan if (ret) {
1489bc658befSSaravana Kannan dev_err(dev,
1490bc658befSSaravana Kannan "%s: reverting to Governor %s failed (%d)\n",
149196ffcdf2SChanwoo Choi __func__, prev_governor->name, ret);
1492bc658befSSaravana Kannan df->governor = NULL;
14935f1a9066SChanwoo Choi goto out;
1494bc658befSSaravana Kannan }
1495bc658befSSaravana Kannan }
14965f1a9066SChanwoo Choi
14975f1a9066SChanwoo Choi /*
14985f1a9066SChanwoo Choi * Create the sysfs files for the new governor. But if failed to start
14995f1a9066SChanwoo Choi * the new governor, restore the sysfs files of previous governor.
15005f1a9066SChanwoo Choi */
15015f1a9066SChanwoo Choi create_sysfs_files(df, df->governor);
15025f1a9066SChanwoo Choi
15030359d1afSNishanth Menon out:
15040359d1afSNishanth Menon mutex_unlock(&devfreq_list_lock);
15050359d1afSNishanth Menon
15060359d1afSNishanth Menon if (!ret)
15070359d1afSNishanth Menon ret = count;
15080359d1afSNishanth Menon return ret;
15090359d1afSNishanth Menon }
1510a93d6b0aSGreg Kroah-Hartman static DEVICE_ATTR_RW(governor);
1511a93d6b0aSGreg Kroah-Hartman
available_governors_show(struct device * d,struct device_attribute * attr,char * buf)1512a93d6b0aSGreg Kroah-Hartman static ssize_t available_governors_show(struct device *d,
151350a5b33eSNishanth Menon struct device_attribute *attr,
151450a5b33eSNishanth Menon char *buf)
151550a5b33eSNishanth Menon {
1516bcf23c79SChanwoo Choi struct devfreq *df = to_devfreq(d);
151750a5b33eSNishanth Menon ssize_t count = 0;
151850a5b33eSNishanth Menon
1519483d557eSChanwoo Choi if (!df->governor)
1520483d557eSChanwoo Choi return -EINVAL;
1521483d557eSChanwoo Choi
152250a5b33eSNishanth Menon mutex_lock(&devfreq_list_lock);
1523bcf23c79SChanwoo Choi
1524bcf23c79SChanwoo Choi /*
1525bcf23c79SChanwoo Choi * The devfreq with immutable governor (e.g., passive) shows
1526bcf23c79SChanwoo Choi * only own governor.
1527bcf23c79SChanwoo Choi */
15280dd25a0dSChanwoo Choi if (IS_SUPPORTED_FLAG(df->governor->flags, IMMUTABLE)) {
1529bcf23c79SChanwoo Choi count = scnprintf(&buf[count], DEVFREQ_NAME_LEN,
153096ffcdf2SChanwoo Choi "%s ", df->governor->name);
1531bcf23c79SChanwoo Choi /*
1532bcf23c79SChanwoo Choi * The devfreq device shows the registered governor except for
1533bcf23c79SChanwoo Choi * immutable governors such as passive governor .
1534bcf23c79SChanwoo Choi */
1535bcf23c79SChanwoo Choi } else {
1536bcf23c79SChanwoo Choi struct devfreq_governor *governor;
1537bcf23c79SChanwoo Choi
1538bcf23c79SChanwoo Choi list_for_each_entry(governor, &devfreq_governor_list, node) {
15390dd25a0dSChanwoo Choi if (IS_SUPPORTED_FLAG(governor->flags, IMMUTABLE))
1540bcf23c79SChanwoo Choi continue;
154150a5b33eSNishanth Menon count += scnprintf(&buf[count], (PAGE_SIZE - count - 2),
1542bcf23c79SChanwoo Choi "%s ", governor->name);
1543bcf23c79SChanwoo Choi }
1544bcf23c79SChanwoo Choi }
1545bcf23c79SChanwoo Choi
154650a5b33eSNishanth Menon mutex_unlock(&devfreq_list_lock);
154750a5b33eSNishanth Menon
154850a5b33eSNishanth Menon /* Truncate the trailing space */
154950a5b33eSNishanth Menon if (count)
155050a5b33eSNishanth Menon count--;
155150a5b33eSNishanth Menon
155250a5b33eSNishanth Menon count += sprintf(&buf[count], "\n");
155350a5b33eSNishanth Menon
155450a5b33eSNishanth Menon return count;
155550a5b33eSNishanth Menon }
1556a93d6b0aSGreg Kroah-Hartman static DEVICE_ATTR_RO(available_governors);
15570359d1afSNishanth Menon
cur_freq_show(struct device * dev,struct device_attribute * attr,char * buf)1558a93d6b0aSGreg Kroah-Hartman static ssize_t cur_freq_show(struct device *dev, struct device_attribute *attr,
1559a93d6b0aSGreg Kroah-Hartman char *buf)
15609005b650SMyungJoo Ham {
15617f98a905SRajagopal Venkat unsigned long freq;
1562483d557eSChanwoo Choi struct devfreq *df = to_devfreq(dev);
15637f98a905SRajagopal Venkat
1564483d557eSChanwoo Choi if (!df->profile)
1565483d557eSChanwoo Choi return -EINVAL;
1566483d557eSChanwoo Choi
1567483d557eSChanwoo Choi if (df->profile->get_cur_freq &&
1568483d557eSChanwoo Choi !df->profile->get_cur_freq(df->dev.parent, &freq))
15697f98a905SRajagopal Venkat return sprintf(buf, "%lu\n", freq);
15707f98a905SRajagopal Venkat
1571483d557eSChanwoo Choi return sprintf(buf, "%lu\n", df->previous_freq);
15727f98a905SRajagopal Venkat }
1573a93d6b0aSGreg Kroah-Hartman static DEVICE_ATTR_RO(cur_freq);
15747f98a905SRajagopal Venkat
target_freq_show(struct device * dev,struct device_attribute * attr,char * buf)1575a93d6b0aSGreg Kroah-Hartman static ssize_t target_freq_show(struct device *dev,
15767f98a905SRajagopal Venkat struct device_attribute *attr, char *buf)
15777f98a905SRajagopal Venkat {
1578483d557eSChanwoo Choi struct devfreq *df = to_devfreq(dev);
1579483d557eSChanwoo Choi
1580483d557eSChanwoo Choi return sprintf(buf, "%lu\n", df->previous_freq);
15819005b650SMyungJoo Ham }
1582a93d6b0aSGreg Kroah-Hartman static DEVICE_ATTR_RO(target_freq);
15839005b650SMyungJoo Ham
min_freq_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)1584a93d6b0aSGreg Kroah-Hartman static ssize_t min_freq_store(struct device *dev, struct device_attribute *attr,
15856530b9deSMyungJoo Ham const char *buf, size_t count)
15866530b9deSMyungJoo Ham {
15876530b9deSMyungJoo Ham struct devfreq *df = to_devfreq(dev);
15886530b9deSMyungJoo Ham unsigned long value;
15896530b9deSMyungJoo Ham int ret;
15906530b9deSMyungJoo Ham
159127dbc542SLeonard Crestez /*
159227dbc542SLeonard Crestez * Protect against theoretical sysfs writes between
159327dbc542SLeonard Crestez * device_add and dev_pm_qos_add_request
159427dbc542SLeonard Crestez */
159527dbc542SLeonard Crestez if (!dev_pm_qos_request_active(&df->user_min_freq_req))
159627dbc542SLeonard Crestez return -EAGAIN;
159727dbc542SLeonard Crestez
15986530b9deSMyungJoo Ham ret = sscanf(buf, "%lu", &value);
15996530b9deSMyungJoo Ham if (ret != 1)
160012e26265SNishanth Menon return -EINVAL;
16016530b9deSMyungJoo Ham
160227dbc542SLeonard Crestez /* Round down to kHz for PM QoS */
160327dbc542SLeonard Crestez ret = dev_pm_qos_update_request(&df->user_min_freq_req,
160427dbc542SLeonard Crestez value / HZ_PER_KHZ);
160527dbc542SLeonard Crestez if (ret < 0)
160627dbc542SLeonard Crestez return ret;
160746cecc0bSLeonard Crestez
160846cecc0bSLeonard Crestez return count;
16096530b9deSMyungJoo Ham }
16106530b9deSMyungJoo Ham
min_freq_show(struct device * dev,struct device_attribute * attr,char * buf)16111051e2c3SChanwoo Choi static ssize_t min_freq_show(struct device *dev, struct device_attribute *attr,
16121051e2c3SChanwoo Choi char *buf)
16131051e2c3SChanwoo Choi {
1614f1d981eaSChanwoo Choi struct devfreq *df = to_devfreq(dev);
161546cecc0bSLeonard Crestez unsigned long min_freq, max_freq;
1616f1d981eaSChanwoo Choi
161746cecc0bSLeonard Crestez mutex_lock(&df->lock);
1618713472e5SChanwoo Choi devfreq_get_freq_range(df, &min_freq, &max_freq);
161946cecc0bSLeonard Crestez mutex_unlock(&df->lock);
162046cecc0bSLeonard Crestez
162146cecc0bSLeonard Crestez return sprintf(buf, "%lu\n", min_freq);
16221051e2c3SChanwoo Choi }
1623f9002b16SKamil Konieczny static DEVICE_ATTR_RW(min_freq);
16241051e2c3SChanwoo Choi
max_freq_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)1625a93d6b0aSGreg Kroah-Hartman static ssize_t max_freq_store(struct device *dev, struct device_attribute *attr,
16266530b9deSMyungJoo Ham const char *buf, size_t count)
16276530b9deSMyungJoo Ham {
16286530b9deSMyungJoo Ham struct devfreq *df = to_devfreq(dev);
16296530b9deSMyungJoo Ham unsigned long value;
16306530b9deSMyungJoo Ham int ret;
16316530b9deSMyungJoo Ham
163227dbc542SLeonard Crestez /*
163327dbc542SLeonard Crestez * Protect against theoretical sysfs writes between
163427dbc542SLeonard Crestez * device_add and dev_pm_qos_add_request
163527dbc542SLeonard Crestez */
163627dbc542SLeonard Crestez if (!dev_pm_qos_request_active(&df->user_max_freq_req))
163727dbc542SLeonard Crestez return -EINVAL;
163827dbc542SLeonard Crestez
16396530b9deSMyungJoo Ham ret = sscanf(buf, "%lu", &value);
16406530b9deSMyungJoo Ham if (ret != 1)
164112e26265SNishanth Menon return -EINVAL;
16426530b9deSMyungJoo Ham
164327dbc542SLeonard Crestez /*
164427dbc542SLeonard Crestez * PM QoS frequencies are in kHz so we need to convert. Convert by
164527dbc542SLeonard Crestez * rounding upwards so that the acceptable interval never shrinks.
164627dbc542SLeonard Crestez *
164727dbc542SLeonard Crestez * For example if the user writes "666666666" to sysfs this value will
164827dbc542SLeonard Crestez * be converted to 666667 kHz and back to 666667000 Hz before an OPP
164927dbc542SLeonard Crestez * lookup, this ensures that an OPP of 666666666Hz is still accepted.
165027dbc542SLeonard Crestez *
165127dbc542SLeonard Crestez * A value of zero means "no limit".
165227dbc542SLeonard Crestez */
165327dbc542SLeonard Crestez if (value)
165427dbc542SLeonard Crestez value = DIV_ROUND_UP(value, HZ_PER_KHZ);
165527dbc542SLeonard Crestez else
165627dbc542SLeonard Crestez value = PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE;
1657df5cf4a3SMatthias Kaehlcke
165827dbc542SLeonard Crestez ret = dev_pm_qos_update_request(&df->user_max_freq_req, value);
165927dbc542SLeonard Crestez if (ret < 0)
166027dbc542SLeonard Crestez return ret;
166146cecc0bSLeonard Crestez
166246cecc0bSLeonard Crestez return count;
16636530b9deSMyungJoo Ham }
16641051e2c3SChanwoo Choi
max_freq_show(struct device * dev,struct device_attribute * attr,char * buf)16651051e2c3SChanwoo Choi static ssize_t max_freq_show(struct device *dev, struct device_attribute *attr,
16661051e2c3SChanwoo Choi char *buf)
16671051e2c3SChanwoo Choi {
1668f1d981eaSChanwoo Choi struct devfreq *df = to_devfreq(dev);
166946cecc0bSLeonard Crestez unsigned long min_freq, max_freq;
1670f1d981eaSChanwoo Choi
167146cecc0bSLeonard Crestez mutex_lock(&df->lock);
1672713472e5SChanwoo Choi devfreq_get_freq_range(df, &min_freq, &max_freq);
167346cecc0bSLeonard Crestez mutex_unlock(&df->lock);
167446cecc0bSLeonard Crestez
167546cecc0bSLeonard Crestez return sprintf(buf, "%lu\n", max_freq);
16761051e2c3SChanwoo Choi }
1677a93d6b0aSGreg Kroah-Hartman static DEVICE_ATTR_RW(max_freq);
16786530b9deSMyungJoo Ham
available_frequencies_show(struct device * d,struct device_attribute * attr,char * buf)1679a93d6b0aSGreg Kroah-Hartman static ssize_t available_frequencies_show(struct device *d,
1680d287de85SNishanth Menon struct device_attribute *attr,
1681d287de85SNishanth Menon char *buf)
1682d287de85SNishanth Menon {
1683d287de85SNishanth Menon struct devfreq *df = to_devfreq(d);
1684d287de85SNishanth Menon ssize_t count = 0;
1685416b46a2SChanwoo Choi int i;
1686d287de85SNishanth Menon
1687483d557eSChanwoo Choi if (!df->profile)
1688483d557eSChanwoo Choi return -EINVAL;
1689483d557eSChanwoo Choi
1690416b46a2SChanwoo Choi mutex_lock(&df->lock);
1691d287de85SNishanth Menon
1692b5d281f6SChristian Marangi for (i = 0; i < df->max_state; i++)
1693d287de85SNishanth Menon count += scnprintf(&buf[count], (PAGE_SIZE - count - 2),
1694b5d281f6SChristian Marangi "%lu ", df->freq_table[i]);
1695d287de85SNishanth Menon
1696416b46a2SChanwoo Choi mutex_unlock(&df->lock);
1697d287de85SNishanth Menon /* Truncate the trailing space */
1698d287de85SNishanth Menon if (count)
1699d287de85SNishanth Menon count--;
1700d287de85SNishanth Menon
1701d287de85SNishanth Menon count += sprintf(&buf[count], "\n");
1702d287de85SNishanth Menon
1703d287de85SNishanth Menon return count;
1704d287de85SNishanth Menon }
1705a93d6b0aSGreg Kroah-Hartman static DEVICE_ATTR_RO(available_frequencies);
1706d287de85SNishanth Menon
trans_stat_show(struct device * dev,struct device_attribute * attr,char * buf)1707a93d6b0aSGreg Kroah-Hartman static ssize_t trans_stat_show(struct device *dev,
1708a93d6b0aSGreg Kroah-Hartman struct device_attribute *attr, char *buf)
1709e552bbafSJonghwa Lee {
1710483d557eSChanwoo Choi struct devfreq *df = to_devfreq(dev);
1711a979f56aSChristian Marangi ssize_t len = 0;
171239688ce6SRajagopal Venkat int i, j;
1713483d557eSChanwoo Choi unsigned int max_state;
1714483d557eSChanwoo Choi
1715483d557eSChanwoo Choi if (!df->profile)
1716483d557eSChanwoo Choi return -EINVAL;
1717b5d281f6SChristian Marangi max_state = df->max_state;
1718e552bbafSJonghwa Lee
171934bd3220SMyungJoo Ham if (max_state == 0)
1720a979f56aSChristian Marangi return scnprintf(buf, PAGE_SIZE, "Not Supported.\n");
1721e552bbafSJonghwa Lee
1722483d557eSChanwoo Choi mutex_lock(&df->lock);
1723483d557eSChanwoo Choi if (!df->stop_polling &&
1724483d557eSChanwoo Choi devfreq_update_status(df, df->previous_freq)) {
1725483d557eSChanwoo Choi mutex_unlock(&df->lock);
17262abb0d52SLeonard Crestez return 0;
17272abb0d52SLeonard Crestez }
1728483d557eSChanwoo Choi mutex_unlock(&df->lock);
17292abb0d52SLeonard Crestez
1730a979f56aSChristian Marangi len += scnprintf(buf + len, PAGE_SIZE - len, " From : To\n");
1731a979f56aSChristian Marangi len += scnprintf(buf + len, PAGE_SIZE - len, " :");
1732a979f56aSChristian Marangi for (i = 0; i < max_state; i++) {
1733a979f56aSChristian Marangi if (len >= PAGE_SIZE - 1)
1734a979f56aSChristian Marangi break;
1735a979f56aSChristian Marangi len += scnprintf(buf + len, PAGE_SIZE - len, "%10lu",
1736b5d281f6SChristian Marangi df->freq_table[i]);
1737a979f56aSChristian Marangi }
1738a979f56aSChristian Marangi if (len >= PAGE_SIZE - 1)
1739a979f56aSChristian Marangi return PAGE_SIZE - 1;
1740e552bbafSJonghwa Lee
1741a979f56aSChristian Marangi len += scnprintf(buf + len, PAGE_SIZE - len, " time(ms)\n");
1742e552bbafSJonghwa Lee
1743e552bbafSJonghwa Lee for (i = 0; i < max_state; i++) {
1744a979f56aSChristian Marangi if (len >= PAGE_SIZE - 1)
1745a979f56aSChristian Marangi break;
1746b5d281f6SChristian Marangi if (df->freq_table[i] == df->previous_freq)
1747a979f56aSChristian Marangi len += scnprintf(buf + len, PAGE_SIZE - len, "*");
1748b5d281f6SChristian Marangi else
1749a979f56aSChristian Marangi len += scnprintf(buf + len, PAGE_SIZE - len, " ");
1750a979f56aSChristian Marangi if (len >= PAGE_SIZE - 1)
1751a979f56aSChristian Marangi break;
1752b5d281f6SChristian Marangi
1753a979f56aSChristian Marangi len += scnprintf(buf + len, PAGE_SIZE - len, "%10lu:",
1754a979f56aSChristian Marangi df->freq_table[i]);
1755a979f56aSChristian Marangi for (j = 0; j < max_state; j++) {
1756a979f56aSChristian Marangi if (len >= PAGE_SIZE - 1)
1757a979f56aSChristian Marangi break;
1758a979f56aSChristian Marangi len += scnprintf(buf + len, PAGE_SIZE - len, "%10u",
1759483d557eSChanwoo Choi df->stats.trans_table[(i * max_state) + j]);
1760a979f56aSChristian Marangi }
1761a979f56aSChristian Marangi if (len >= PAGE_SIZE - 1)
1762a979f56aSChristian Marangi break;
1763a979f56aSChristian Marangi len += scnprintf(buf + len, PAGE_SIZE - len, "%10llu\n", (u64)
1764483d557eSChanwoo Choi jiffies64_to_msecs(df->stats.time_in_state[i]));
1765e552bbafSJonghwa Lee }
1766e552bbafSJonghwa Lee
1767a979f56aSChristian Marangi if (len < PAGE_SIZE - 1)
1768a979f56aSChristian Marangi len += scnprintf(buf + len, PAGE_SIZE - len, "Total transition : %u\n",
1769483d557eSChanwoo Choi df->stats.total_trans);
1770a979f56aSChristian Marangi
1771a979f56aSChristian Marangi if (len >= PAGE_SIZE - 1) {
1772a979f56aSChristian Marangi pr_warn_once("devfreq transition table exceeds PAGE_SIZE. Disabling\n");
1773a979f56aSChristian Marangi return -EFBIG;
1774a979f56aSChristian Marangi }
1775a979f56aSChristian Marangi
1776e552bbafSJonghwa Lee return len;
1777e552bbafSJonghwa Lee }
177814a34396SKamil Konieczny
trans_stat_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)177914a34396SKamil Konieczny static ssize_t trans_stat_store(struct device *dev,
178014a34396SKamil Konieczny struct device_attribute *attr,
178114a34396SKamil Konieczny const char *buf, size_t count)
178214a34396SKamil Konieczny {
178314a34396SKamil Konieczny struct devfreq *df = to_devfreq(dev);
178414a34396SKamil Konieczny int err, value;
178514a34396SKamil Konieczny
1786483d557eSChanwoo Choi if (!df->profile)
1787483d557eSChanwoo Choi return -EINVAL;
1788483d557eSChanwoo Choi
1789b5d281f6SChristian Marangi if (df->max_state == 0)
179014a34396SKamil Konieczny return count;
179114a34396SKamil Konieczny
179214a34396SKamil Konieczny err = kstrtoint(buf, 10, &value);
179314a34396SKamil Konieczny if (err || value != 0)
179414a34396SKamil Konieczny return -EINVAL;
179514a34396SKamil Konieczny
179614a34396SKamil Konieczny mutex_lock(&df->lock);
1797b5d281f6SChristian Marangi memset(df->stats.time_in_state, 0, (df->max_state *
17981ebd0bc0SKamil Konieczny sizeof(*df->stats.time_in_state)));
17991ebd0bc0SKamil Konieczny memset(df->stats.trans_table, 0, array3_size(sizeof(unsigned int),
1800b5d281f6SChristian Marangi df->max_state,
1801b5d281f6SChristian Marangi df->max_state));
18021ebd0bc0SKamil Konieczny df->stats.total_trans = 0;
18031ebd0bc0SKamil Konieczny df->stats.last_update = get_jiffies_64();
180414a34396SKamil Konieczny mutex_unlock(&df->lock);
180514a34396SKamil Konieczny
180614a34396SKamil Konieczny return count;
180714a34396SKamil Konieczny }
180814a34396SKamil Konieczny static DEVICE_ATTR_RW(trans_stat);
1809e552bbafSJonghwa Lee
18105f1a9066SChanwoo Choi static struct attribute *devfreq_attrs[] = {
18115f1a9066SChanwoo Choi &dev_attr_name.attr,
18125f1a9066SChanwoo Choi &dev_attr_governor.attr,
18135f1a9066SChanwoo Choi &dev_attr_available_governors.attr,
18145f1a9066SChanwoo Choi &dev_attr_cur_freq.attr,
18155f1a9066SChanwoo Choi &dev_attr_available_frequencies.attr,
18165f1a9066SChanwoo Choi &dev_attr_target_freq.attr,
18175f1a9066SChanwoo Choi &dev_attr_min_freq.attr,
18185f1a9066SChanwoo Choi &dev_attr_max_freq.attr,
18195f1a9066SChanwoo Choi &dev_attr_trans_stat.attr,
18205f1a9066SChanwoo Choi NULL,
18215f1a9066SChanwoo Choi };
18225f1a9066SChanwoo Choi ATTRIBUTE_GROUPS(devfreq);
18235f1a9066SChanwoo Choi
polling_interval_show(struct device * dev,struct device_attribute * attr,char * buf)18245f1a9066SChanwoo Choi static ssize_t polling_interval_show(struct device *dev,
18255f1a9066SChanwoo Choi struct device_attribute *attr, char *buf)
18265f1a9066SChanwoo Choi {
18275f1a9066SChanwoo Choi struct devfreq *df = to_devfreq(dev);
18285f1a9066SChanwoo Choi
18295f1a9066SChanwoo Choi if (!df->profile)
18305f1a9066SChanwoo Choi return -EINVAL;
18315f1a9066SChanwoo Choi
18325f1a9066SChanwoo Choi return sprintf(buf, "%d\n", df->profile->polling_ms);
18335f1a9066SChanwoo Choi }
18345f1a9066SChanwoo Choi
polling_interval_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)18355f1a9066SChanwoo Choi static ssize_t polling_interval_store(struct device *dev,
18365f1a9066SChanwoo Choi struct device_attribute *attr,
18375f1a9066SChanwoo Choi const char *buf, size_t count)
18385f1a9066SChanwoo Choi {
18395f1a9066SChanwoo Choi struct devfreq *df = to_devfreq(dev);
18405f1a9066SChanwoo Choi unsigned int value;
18415f1a9066SChanwoo Choi int ret;
18425f1a9066SChanwoo Choi
18435f1a9066SChanwoo Choi if (!df->governor)
18445f1a9066SChanwoo Choi return -EINVAL;
18455f1a9066SChanwoo Choi
18465f1a9066SChanwoo Choi ret = sscanf(buf, "%u", &value);
18475f1a9066SChanwoo Choi if (ret != 1)
18485f1a9066SChanwoo Choi return -EINVAL;
18495f1a9066SChanwoo Choi
18505f1a9066SChanwoo Choi df->governor->event_handler(df, DEVFREQ_GOV_UPDATE_INTERVAL, &value);
18515f1a9066SChanwoo Choi ret = count;
18525f1a9066SChanwoo Choi
18535f1a9066SChanwoo Choi return ret;
18545f1a9066SChanwoo Choi }
18555f1a9066SChanwoo Choi static DEVICE_ATTR_RW(polling_interval);
18565f1a9066SChanwoo Choi
timer_show(struct device * dev,struct device_attribute * attr,char * buf)18574dc3bab8SChanwoo Choi static ssize_t timer_show(struct device *dev,
18584dc3bab8SChanwoo Choi struct device_attribute *attr, char *buf)
18594dc3bab8SChanwoo Choi {
18604dc3bab8SChanwoo Choi struct devfreq *df = to_devfreq(dev);
18614dc3bab8SChanwoo Choi
18624dc3bab8SChanwoo Choi if (!df->profile)
18634dc3bab8SChanwoo Choi return -EINVAL;
18644dc3bab8SChanwoo Choi
18654dc3bab8SChanwoo Choi return sprintf(buf, "%s\n", timer_name[df->profile->timer]);
18664dc3bab8SChanwoo Choi }
18674dc3bab8SChanwoo Choi
timer_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)18684dc3bab8SChanwoo Choi static ssize_t timer_store(struct device *dev, struct device_attribute *attr,
18694dc3bab8SChanwoo Choi const char *buf, size_t count)
18704dc3bab8SChanwoo Choi {
18714dc3bab8SChanwoo Choi struct devfreq *df = to_devfreq(dev);
18724dc3bab8SChanwoo Choi char str_timer[DEVFREQ_NAME_LEN + 1];
18734dc3bab8SChanwoo Choi int timer = -1;
18744dc3bab8SChanwoo Choi int ret = 0, i;
18754dc3bab8SChanwoo Choi
18764dc3bab8SChanwoo Choi if (!df->governor || !df->profile)
18774dc3bab8SChanwoo Choi return -EINVAL;
18784dc3bab8SChanwoo Choi
18794dc3bab8SChanwoo Choi ret = sscanf(buf, "%16s", str_timer);
18804dc3bab8SChanwoo Choi if (ret != 1)
18814dc3bab8SChanwoo Choi return -EINVAL;
18824dc3bab8SChanwoo Choi
18834dc3bab8SChanwoo Choi for (i = 0; i < DEVFREQ_TIMER_NUM; i++) {
18844dc3bab8SChanwoo Choi if (!strncmp(timer_name[i], str_timer, DEVFREQ_NAME_LEN)) {
18854dc3bab8SChanwoo Choi timer = i;
18864dc3bab8SChanwoo Choi break;
18874dc3bab8SChanwoo Choi }
18884dc3bab8SChanwoo Choi }
18894dc3bab8SChanwoo Choi
18904dc3bab8SChanwoo Choi if (timer < 0) {
18914dc3bab8SChanwoo Choi ret = -EINVAL;
18924dc3bab8SChanwoo Choi goto out;
18934dc3bab8SChanwoo Choi }
18944dc3bab8SChanwoo Choi
18954dc3bab8SChanwoo Choi if (df->profile->timer == timer) {
18964dc3bab8SChanwoo Choi ret = 0;
18974dc3bab8SChanwoo Choi goto out;
18984dc3bab8SChanwoo Choi }
18994dc3bab8SChanwoo Choi
19004dc3bab8SChanwoo Choi mutex_lock(&df->lock);
19014dc3bab8SChanwoo Choi df->profile->timer = timer;
19024dc3bab8SChanwoo Choi mutex_unlock(&df->lock);
19034dc3bab8SChanwoo Choi
19044dc3bab8SChanwoo Choi ret = df->governor->event_handler(df, DEVFREQ_GOV_STOP, NULL);
19054dc3bab8SChanwoo Choi if (ret) {
19064dc3bab8SChanwoo Choi dev_warn(dev, "%s: Governor %s not stopped(%d)\n",
19074dc3bab8SChanwoo Choi __func__, df->governor->name, ret);
19084dc3bab8SChanwoo Choi goto out;
19094dc3bab8SChanwoo Choi }
19104dc3bab8SChanwoo Choi
19114dc3bab8SChanwoo Choi ret = df->governor->event_handler(df, DEVFREQ_GOV_START, NULL);
19124dc3bab8SChanwoo Choi if (ret)
19134dc3bab8SChanwoo Choi dev_warn(dev, "%s: Governor %s not started(%d)\n",
19144dc3bab8SChanwoo Choi __func__, df->governor->name, ret);
19154dc3bab8SChanwoo Choi out:
19164dc3bab8SChanwoo Choi return ret ? ret : count;
19174dc3bab8SChanwoo Choi }
19184dc3bab8SChanwoo Choi static DEVICE_ATTR_RW(timer);
19194dc3bab8SChanwoo Choi
19205f1a9066SChanwoo Choi #define CREATE_SYSFS_FILE(df, name) \
19215f1a9066SChanwoo Choi { \
19225f1a9066SChanwoo Choi int ret; \
19235f1a9066SChanwoo Choi ret = sysfs_create_file(&df->dev.kobj, &dev_attr_##name.attr); \
19245f1a9066SChanwoo Choi if (ret < 0) { \
19255f1a9066SChanwoo Choi dev_warn(&df->dev, \
19265f1a9066SChanwoo Choi "Unable to create attr(%s)\n", "##name"); \
19275f1a9066SChanwoo Choi } \
19285f1a9066SChanwoo Choi } \
19295f1a9066SChanwoo Choi
19305f1a9066SChanwoo Choi /* Create the specific sysfs files which depend on each governor. */
create_sysfs_files(struct devfreq * devfreq,const struct devfreq_governor * gov)19315f1a9066SChanwoo Choi static void create_sysfs_files(struct devfreq *devfreq,
19325f1a9066SChanwoo Choi const struct devfreq_governor *gov)
19335f1a9066SChanwoo Choi {
19345f1a9066SChanwoo Choi if (IS_SUPPORTED_ATTR(gov->attrs, POLLING_INTERVAL))
19355f1a9066SChanwoo Choi CREATE_SYSFS_FILE(devfreq, polling_interval);
19365f1a9066SChanwoo Choi if (IS_SUPPORTED_ATTR(gov->attrs, TIMER))
19375f1a9066SChanwoo Choi CREATE_SYSFS_FILE(devfreq, timer);
19385f1a9066SChanwoo Choi }
19395f1a9066SChanwoo Choi
19405f1a9066SChanwoo Choi /* Remove the specific sysfs files which depend on each governor. */
remove_sysfs_files(struct devfreq * devfreq,const struct devfreq_governor * gov)19415f1a9066SChanwoo Choi static void remove_sysfs_files(struct devfreq *devfreq,
19425f1a9066SChanwoo Choi const struct devfreq_governor *gov)
19435f1a9066SChanwoo Choi {
19445f1a9066SChanwoo Choi if (IS_SUPPORTED_ATTR(gov->attrs, POLLING_INTERVAL))
19455f1a9066SChanwoo Choi sysfs_remove_file(&devfreq->dev.kobj,
19465f1a9066SChanwoo Choi &dev_attr_polling_interval.attr);
19475f1a9066SChanwoo Choi if (IS_SUPPORTED_ATTR(gov->attrs, TIMER))
19485f1a9066SChanwoo Choi sysfs_remove_file(&devfreq->dev.kobj, &dev_attr_timer.attr);
19495f1a9066SChanwoo Choi }
19509005b650SMyungJoo Ham
1951490a421bSChanwoo Choi /**
1952490a421bSChanwoo Choi * devfreq_summary_show() - Show the summary of the devfreq devices
1953490a421bSChanwoo Choi * @s: seq_file instance to show the summary of devfreq devices
1954490a421bSChanwoo Choi * @data: not used
1955490a421bSChanwoo Choi *
1956490a421bSChanwoo Choi * Show the summary of the devfreq devices via 'devfreq_summary' debugfs file.
1957490a421bSChanwoo Choi * It helps that user can know the detailed information of the devfreq devices.
1958490a421bSChanwoo Choi *
1959490a421bSChanwoo Choi * Return 0 always because it shows the information without any data change.
1960490a421bSChanwoo Choi */
devfreq_summary_show(struct seq_file * s,void * data)1961490a421bSChanwoo Choi static int devfreq_summary_show(struct seq_file *s, void *data)
1962490a421bSChanwoo Choi {
1963490a421bSChanwoo Choi struct devfreq *devfreq;
1964490a421bSChanwoo Choi struct devfreq *p_devfreq = NULL;
1965490a421bSChanwoo Choi unsigned long cur_freq, min_freq, max_freq;
1966490a421bSChanwoo Choi unsigned int polling_ms;
19670c309ed1SChanwoo Choi unsigned int timer;
1968490a421bSChanwoo Choi
19690c309ed1SChanwoo Choi seq_printf(s, "%-30s %-30s %-15s %-10s %10s %12s %12s %12s\n",
1970490a421bSChanwoo Choi "dev",
1971490a421bSChanwoo Choi "parent_dev",
1972490a421bSChanwoo Choi "governor",
19730c309ed1SChanwoo Choi "timer",
1974490a421bSChanwoo Choi "polling_ms",
1975490a421bSChanwoo Choi "cur_freq_Hz",
1976490a421bSChanwoo Choi "min_freq_Hz",
1977490a421bSChanwoo Choi "max_freq_Hz");
19780c309ed1SChanwoo Choi seq_printf(s, "%30s %30s %15s %10s %10s %12s %12s %12s\n",
1979490a421bSChanwoo Choi "------------------------------",
19800aae11bcSChanwoo Choi "------------------------------",
1981490a421bSChanwoo Choi "---------------",
1982490a421bSChanwoo Choi "----------",
19830c309ed1SChanwoo Choi "----------",
1984490a421bSChanwoo Choi "------------",
1985490a421bSChanwoo Choi "------------",
1986490a421bSChanwoo Choi "------------");
1987490a421bSChanwoo Choi
1988490a421bSChanwoo Choi mutex_lock(&devfreq_list_lock);
1989490a421bSChanwoo Choi
1990490a421bSChanwoo Choi list_for_each_entry_reverse(devfreq, &devfreq_list, node) {
1991490a421bSChanwoo Choi #if IS_ENABLED(CONFIG_DEVFREQ_GOV_PASSIVE)
199296ffcdf2SChanwoo Choi if (!strncmp(devfreq->governor->name, DEVFREQ_GOV_PASSIVE,
1993490a421bSChanwoo Choi DEVFREQ_NAME_LEN)) {
1994490a421bSChanwoo Choi struct devfreq_passive_data *data = devfreq->data;
1995490a421bSChanwoo Choi
1996490a421bSChanwoo Choi if (data)
1997490a421bSChanwoo Choi p_devfreq = data->parent;
1998490a421bSChanwoo Choi } else {
1999490a421bSChanwoo Choi p_devfreq = NULL;
2000490a421bSChanwoo Choi }
2001490a421bSChanwoo Choi #endif
2002490a421bSChanwoo Choi
2003490a421bSChanwoo Choi mutex_lock(&devfreq->lock);
200427a69714SChanwoo Choi cur_freq = devfreq->previous_freq;
2005713472e5SChanwoo Choi devfreq_get_freq_range(devfreq, &min_freq, &max_freq);
20060c309ed1SChanwoo Choi timer = devfreq->profile->timer;
20075f1a9066SChanwoo Choi
20085f1a9066SChanwoo Choi if (IS_SUPPORTED_ATTR(devfreq->governor->attrs, POLLING_INTERVAL))
20095f1a9066SChanwoo Choi polling_ms = devfreq->profile->polling_ms;
20105f1a9066SChanwoo Choi else
20115f1a9066SChanwoo Choi polling_ms = 0;
2012490a421bSChanwoo Choi mutex_unlock(&devfreq->lock);
2013490a421bSChanwoo Choi
2014490a421bSChanwoo Choi seq_printf(s,
20150c309ed1SChanwoo Choi "%-30s %-30s %-15s %-10s %10d %12ld %12ld %12ld\n",
2016490a421bSChanwoo Choi dev_name(&devfreq->dev),
2017490a421bSChanwoo Choi p_devfreq ? dev_name(&p_devfreq->dev) : "null",
201896ffcdf2SChanwoo Choi devfreq->governor->name,
20190c309ed1SChanwoo Choi polling_ms ? timer_name[timer] : "null",
2020490a421bSChanwoo Choi polling_ms,
2021490a421bSChanwoo Choi cur_freq,
2022490a421bSChanwoo Choi min_freq,
2023490a421bSChanwoo Choi max_freq);
2024490a421bSChanwoo Choi }
2025490a421bSChanwoo Choi
2026490a421bSChanwoo Choi mutex_unlock(&devfreq_list_lock);
2027490a421bSChanwoo Choi
2028490a421bSChanwoo Choi return 0;
2029490a421bSChanwoo Choi }
2030490a421bSChanwoo Choi DEFINE_SHOW_ATTRIBUTE(devfreq_summary);
2031490a421bSChanwoo Choi
devfreq_init(void)2032a3c98b8bSMyungJoo Ham static int __init devfreq_init(void)
2033a3c98b8bSMyungJoo Ham {
20341aaba11dSGreg Kroah-Hartman devfreq_class = class_create("devfreq");
2035a3c98b8bSMyungJoo Ham if (IS_ERR(devfreq_class)) {
2036a3c98b8bSMyungJoo Ham pr_err("%s: couldn't create class\n", __FILE__);
2037a3c98b8bSMyungJoo Ham return PTR_ERR(devfreq_class);
2038a3c98b8bSMyungJoo Ham }
20397e6fdd4bSRajagopal Venkat
20407e6fdd4bSRajagopal Venkat devfreq_wq = create_freezable_workqueue("devfreq_wq");
2041ea7f4548SDan Carpenter if (!devfreq_wq) {
20427e6fdd4bSRajagopal Venkat class_destroy(devfreq_class);
20437e6fdd4bSRajagopal Venkat pr_err("%s: couldn't create workqueue\n", __FILE__);
2044ea7f4548SDan Carpenter return -ENOMEM;
20457e6fdd4bSRajagopal Venkat }
2046a93d6b0aSGreg Kroah-Hartman devfreq_class->dev_groups = devfreq_groups;
20477e6fdd4bSRajagopal Venkat
2048490a421bSChanwoo Choi devfreq_debugfs = debugfs_create_dir("devfreq", NULL);
2049490a421bSChanwoo Choi debugfs_create_file("devfreq_summary", 0444,
2050490a421bSChanwoo Choi devfreq_debugfs, NULL,
2051490a421bSChanwoo Choi &devfreq_summary_fops);
2052490a421bSChanwoo Choi
2053a3c98b8bSMyungJoo Ham return 0;
2054a3c98b8bSMyungJoo Ham }
2055a3c98b8bSMyungJoo Ham subsys_initcall(devfreq_init);
2056a3c98b8bSMyungJoo Ham
2057a3c98b8bSMyungJoo Ham /*
20584091fb95SMasahiro Yamada * The following are helper functions for devfreq user device drivers with
2059a3c98b8bSMyungJoo Ham * OPP framework.
2060a3c98b8bSMyungJoo Ham */
2061a3c98b8bSMyungJoo Ham
2062a3c98b8bSMyungJoo Ham /**
2063a3c98b8bSMyungJoo Ham * devfreq_recommended_opp() - Helper function to get proper OPP for the
2064a3c98b8bSMyungJoo Ham * freq value given to target callback.
2065c5b4a1c1SNishanth Menon * @dev: The devfreq user device. (parent of devfreq)
2066c5b4a1c1SNishanth Menon * @freq: The frequency given to target function
2067c5b4a1c1SNishanth Menon * @flags: Flags handed from devfreq framework.
2068a3c98b8bSMyungJoo Ham *
20698a31d9d9SViresh Kumar * The callers are required to call dev_pm_opp_put() for the returned OPP after
20708a31d9d9SViresh Kumar * use.
2071a3c98b8bSMyungJoo Ham */
devfreq_recommended_opp(struct device * dev,unsigned long * freq,u32 flags)207247d43ba7SNishanth Menon struct dev_pm_opp *devfreq_recommended_opp(struct device *dev,
207347d43ba7SNishanth Menon unsigned long *freq,
2074ab5f299fSMyungJoo Ham u32 flags)
2075a3c98b8bSMyungJoo Ham {
207647d43ba7SNishanth Menon struct dev_pm_opp *opp;
2077a3c98b8bSMyungJoo Ham
2078ab5f299fSMyungJoo Ham if (flags & DEVFREQ_FLAG_LEAST_UPPER_BOUND) {
2079ab5f299fSMyungJoo Ham /* The freq is an upper bound. opp should be lower */
20805d4879cdSNishanth Menon opp = dev_pm_opp_find_freq_floor(dev, freq);
2081ab5f299fSMyungJoo Ham
2082ab5f299fSMyungJoo Ham /* If not available, use the closest opp */
20830779726cSNishanth Menon if (opp == ERR_PTR(-ERANGE))
20845d4879cdSNishanth Menon opp = dev_pm_opp_find_freq_ceil(dev, freq);
2085ab5f299fSMyungJoo Ham } else {
2086ab5f299fSMyungJoo Ham /* The freq is an lower bound. opp should be higher */
20875d4879cdSNishanth Menon opp = dev_pm_opp_find_freq_ceil(dev, freq);
2088ab5f299fSMyungJoo Ham
2089ab5f299fSMyungJoo Ham /* If not available, use the closest opp */
20900779726cSNishanth Menon if (opp == ERR_PTR(-ERANGE))
20915d4879cdSNishanth Menon opp = dev_pm_opp_find_freq_floor(dev, freq);
2092ab5f299fSMyungJoo Ham }
2093ab5f299fSMyungJoo Ham
2094a3c98b8bSMyungJoo Ham return opp;
2095a3c98b8bSMyungJoo Ham }
2096bd7e9277SÃrjan Eide EXPORT_SYMBOL(devfreq_recommended_opp);
2097a3c98b8bSMyungJoo Ham
2098a3c98b8bSMyungJoo Ham /**
2099a3c98b8bSMyungJoo Ham * devfreq_register_opp_notifier() - Helper function to get devfreq notified
2100a3c98b8bSMyungJoo Ham * for any changes in the OPP availability
2101a3c98b8bSMyungJoo Ham * changes
2102c5b4a1c1SNishanth Menon * @dev: The devfreq user device. (parent of devfreq)
2103c5b4a1c1SNishanth Menon * @devfreq: The devfreq object.
2104a3c98b8bSMyungJoo Ham */
devfreq_register_opp_notifier(struct device * dev,struct devfreq * devfreq)2105a3c98b8bSMyungJoo Ham int devfreq_register_opp_notifier(struct device *dev, struct devfreq *devfreq)
2106a3c98b8bSMyungJoo Ham {
2107dc2c9ad5SViresh Kumar return dev_pm_opp_register_notifier(dev, &devfreq->nb);
2108a3c98b8bSMyungJoo Ham }
2109bd7e9277SÃrjan Eide EXPORT_SYMBOL(devfreq_register_opp_notifier);
2110a3c98b8bSMyungJoo Ham
2111a3c98b8bSMyungJoo Ham /**
2112a3c98b8bSMyungJoo Ham * devfreq_unregister_opp_notifier() - Helper function to stop getting devfreq
2113a3c98b8bSMyungJoo Ham * notified for any changes in the OPP
2114a3c98b8bSMyungJoo Ham * availability changes anymore.
2115c5b4a1c1SNishanth Menon * @dev: The devfreq user device. (parent of devfreq)
2116c5b4a1c1SNishanth Menon * @devfreq: The devfreq object.
2117a3c98b8bSMyungJoo Ham *
2118a3c98b8bSMyungJoo Ham * At exit() callback of devfreq_dev_profile, this must be included if
2119a3c98b8bSMyungJoo Ham * devfreq_recommended_opp is used.
2120a3c98b8bSMyungJoo Ham */
devfreq_unregister_opp_notifier(struct device * dev,struct devfreq * devfreq)2121a3c98b8bSMyungJoo Ham int devfreq_unregister_opp_notifier(struct device *dev, struct devfreq *devfreq)
2122a3c98b8bSMyungJoo Ham {
2123dc2c9ad5SViresh Kumar return dev_pm_opp_unregister_notifier(dev, &devfreq->nb);
2124a3c98b8bSMyungJoo Ham }
2125bd7e9277SÃrjan Eide EXPORT_SYMBOL(devfreq_unregister_opp_notifier);
2126a3c98b8bSMyungJoo Ham
devm_devfreq_opp_release(struct device * dev,void * res)2127d5b040d0SChanwoo Choi static void devm_devfreq_opp_release(struct device *dev, void *res)
2128d5b040d0SChanwoo Choi {
2129d5b040d0SChanwoo Choi devfreq_unregister_opp_notifier(dev, *(struct devfreq **)res);
2130d5b040d0SChanwoo Choi }
2131d5b040d0SChanwoo Choi
2132d5b040d0SChanwoo Choi /**
21336d690f77SMyungJoo Ham * devm_devfreq_register_opp_notifier() - Resource-managed
21346d690f77SMyungJoo Ham * devfreq_register_opp_notifier()
2135d5b040d0SChanwoo Choi * @dev: The devfreq user device. (parent of devfreq)
2136d5b040d0SChanwoo Choi * @devfreq: The devfreq object.
2137d5b040d0SChanwoo Choi */
devm_devfreq_register_opp_notifier(struct device * dev,struct devfreq * devfreq)2138d5b040d0SChanwoo Choi int devm_devfreq_register_opp_notifier(struct device *dev,
2139d5b040d0SChanwoo Choi struct devfreq *devfreq)
2140d5b040d0SChanwoo Choi {
2141d5b040d0SChanwoo Choi struct devfreq **ptr;
2142d5b040d0SChanwoo Choi int ret;
2143d5b040d0SChanwoo Choi
2144d5b040d0SChanwoo Choi ptr = devres_alloc(devm_devfreq_opp_release, sizeof(*ptr), GFP_KERNEL);
2145d5b040d0SChanwoo Choi if (!ptr)
2146d5b040d0SChanwoo Choi return -ENOMEM;
2147d5b040d0SChanwoo Choi
2148d5b040d0SChanwoo Choi ret = devfreq_register_opp_notifier(dev, devfreq);
2149d5b040d0SChanwoo Choi if (ret) {
2150d5b040d0SChanwoo Choi devres_free(ptr);
2151d5b040d0SChanwoo Choi return ret;
2152d5b040d0SChanwoo Choi }
2153d5b040d0SChanwoo Choi
2154d5b040d0SChanwoo Choi *ptr = devfreq;
2155d5b040d0SChanwoo Choi devres_add(dev, ptr);
2156d5b040d0SChanwoo Choi
2157d5b040d0SChanwoo Choi return 0;
2158d5b040d0SChanwoo Choi }
2159d5b040d0SChanwoo Choi EXPORT_SYMBOL(devm_devfreq_register_opp_notifier);
2160d5b040d0SChanwoo Choi
2161d5b040d0SChanwoo Choi /**
21626d690f77SMyungJoo Ham * devm_devfreq_unregister_opp_notifier() - Resource-managed
21636d690f77SMyungJoo Ham * devfreq_unregister_opp_notifier()
2164d5b040d0SChanwoo Choi * @dev: The devfreq user device. (parent of devfreq)
2165d5b040d0SChanwoo Choi * @devfreq: The devfreq object.
2166d5b040d0SChanwoo Choi */
devm_devfreq_unregister_opp_notifier(struct device * dev,struct devfreq * devfreq)2167d5b040d0SChanwoo Choi void devm_devfreq_unregister_opp_notifier(struct device *dev,
2168d5b040d0SChanwoo Choi struct devfreq *devfreq)
2169d5b040d0SChanwoo Choi {
2170d5b040d0SChanwoo Choi WARN_ON(devres_release(dev, devm_devfreq_opp_release,
2171d5b040d0SChanwoo Choi devm_devfreq_dev_match, devfreq));
2172d5b040d0SChanwoo Choi }
2173d5b040d0SChanwoo Choi EXPORT_SYMBOL(devm_devfreq_unregister_opp_notifier);
2174d5b040d0SChanwoo Choi
21750fe3a664SChanwoo Choi /**
21760fe3a664SChanwoo Choi * devfreq_register_notifier() - Register a driver with devfreq
21770fe3a664SChanwoo Choi * @devfreq: The devfreq object.
21780fe3a664SChanwoo Choi * @nb: The notifier block to register.
21790fe3a664SChanwoo Choi * @list: DEVFREQ_TRANSITION_NOTIFIER.
21800fe3a664SChanwoo Choi */
devfreq_register_notifier(struct devfreq * devfreq,struct notifier_block * nb,unsigned int list)21810fe3a664SChanwoo Choi int devfreq_register_notifier(struct devfreq *devfreq,
21820fe3a664SChanwoo Choi struct notifier_block *nb,
21830fe3a664SChanwoo Choi unsigned int list)
21840fe3a664SChanwoo Choi {
21850fe3a664SChanwoo Choi int ret = 0;
21860fe3a664SChanwoo Choi
21870fe3a664SChanwoo Choi if (!devfreq)
21880fe3a664SChanwoo Choi return -EINVAL;
21890fe3a664SChanwoo Choi
21900fe3a664SChanwoo Choi switch (list) {
21910fe3a664SChanwoo Choi case DEVFREQ_TRANSITION_NOTIFIER:
21920fe3a664SChanwoo Choi ret = srcu_notifier_chain_register(
21930fe3a664SChanwoo Choi &devfreq->transition_notifier_list, nb);
21940fe3a664SChanwoo Choi break;
21950fe3a664SChanwoo Choi default:
21960fe3a664SChanwoo Choi ret = -EINVAL;
21970fe3a664SChanwoo Choi }
21980fe3a664SChanwoo Choi
21990fe3a664SChanwoo Choi return ret;
22000fe3a664SChanwoo Choi }
22010fe3a664SChanwoo Choi EXPORT_SYMBOL(devfreq_register_notifier);
22020fe3a664SChanwoo Choi
22030fe3a664SChanwoo Choi /*
22040fe3a664SChanwoo Choi * devfreq_unregister_notifier() - Unregister a driver with devfreq
22050fe3a664SChanwoo Choi * @devfreq: The devfreq object.
22060fe3a664SChanwoo Choi * @nb: The notifier block to be unregistered.
22070fe3a664SChanwoo Choi * @list: DEVFREQ_TRANSITION_NOTIFIER.
22080fe3a664SChanwoo Choi */
devfreq_unregister_notifier(struct devfreq * devfreq,struct notifier_block * nb,unsigned int list)22090fe3a664SChanwoo Choi int devfreq_unregister_notifier(struct devfreq *devfreq,
22100fe3a664SChanwoo Choi struct notifier_block *nb,
22110fe3a664SChanwoo Choi unsigned int list)
22120fe3a664SChanwoo Choi {
22130fe3a664SChanwoo Choi int ret = 0;
22140fe3a664SChanwoo Choi
22150fe3a664SChanwoo Choi if (!devfreq)
22160fe3a664SChanwoo Choi return -EINVAL;
22170fe3a664SChanwoo Choi
22180fe3a664SChanwoo Choi switch (list) {
22190fe3a664SChanwoo Choi case DEVFREQ_TRANSITION_NOTIFIER:
22200fe3a664SChanwoo Choi ret = srcu_notifier_chain_unregister(
22210fe3a664SChanwoo Choi &devfreq->transition_notifier_list, nb);
22220fe3a664SChanwoo Choi break;
22230fe3a664SChanwoo Choi default:
22240fe3a664SChanwoo Choi ret = -EINVAL;
22250fe3a664SChanwoo Choi }
22260fe3a664SChanwoo Choi
22270fe3a664SChanwoo Choi return ret;
22280fe3a664SChanwoo Choi }
22290fe3a664SChanwoo Choi EXPORT_SYMBOL(devfreq_unregister_notifier);
22300fe3a664SChanwoo Choi
22310fe3a664SChanwoo Choi struct devfreq_notifier_devres {
22320fe3a664SChanwoo Choi struct devfreq *devfreq;
22330fe3a664SChanwoo Choi struct notifier_block *nb;
22340fe3a664SChanwoo Choi unsigned int list;
22350fe3a664SChanwoo Choi };
22360fe3a664SChanwoo Choi
devm_devfreq_notifier_release(struct device * dev,void * res)22370fe3a664SChanwoo Choi static void devm_devfreq_notifier_release(struct device *dev, void *res)
22380fe3a664SChanwoo Choi {
22390fe3a664SChanwoo Choi struct devfreq_notifier_devres *this = res;
22400fe3a664SChanwoo Choi
22410fe3a664SChanwoo Choi devfreq_unregister_notifier(this->devfreq, this->nb, this->list);
22420fe3a664SChanwoo Choi }
22430fe3a664SChanwoo Choi
22440fe3a664SChanwoo Choi /**
22450fe3a664SChanwoo Choi * devm_devfreq_register_notifier()
224654cb5740SRandy Dunlap * - Resource-managed devfreq_register_notifier()
22470fe3a664SChanwoo Choi * @dev: The devfreq user device. (parent of devfreq)
22480fe3a664SChanwoo Choi * @devfreq: The devfreq object.
22490fe3a664SChanwoo Choi * @nb: The notifier block to be unregistered.
22500fe3a664SChanwoo Choi * @list: DEVFREQ_TRANSITION_NOTIFIER.
22510fe3a664SChanwoo Choi */
devm_devfreq_register_notifier(struct device * dev,struct devfreq * devfreq,struct notifier_block * nb,unsigned int list)22520fe3a664SChanwoo Choi int devm_devfreq_register_notifier(struct device *dev,
22530fe3a664SChanwoo Choi struct devfreq *devfreq,
22540fe3a664SChanwoo Choi struct notifier_block *nb,
22550fe3a664SChanwoo Choi unsigned int list)
22560fe3a664SChanwoo Choi {
22570fe3a664SChanwoo Choi struct devfreq_notifier_devres *ptr;
22580fe3a664SChanwoo Choi int ret;
22590fe3a664SChanwoo Choi
22600fe3a664SChanwoo Choi ptr = devres_alloc(devm_devfreq_notifier_release, sizeof(*ptr),
22610fe3a664SChanwoo Choi GFP_KERNEL);
22620fe3a664SChanwoo Choi if (!ptr)
22630fe3a664SChanwoo Choi return -ENOMEM;
22640fe3a664SChanwoo Choi
22650fe3a664SChanwoo Choi ret = devfreq_register_notifier(devfreq, nb, list);
22660fe3a664SChanwoo Choi if (ret) {
22670fe3a664SChanwoo Choi devres_free(ptr);
22680fe3a664SChanwoo Choi return ret;
22690fe3a664SChanwoo Choi }
22700fe3a664SChanwoo Choi
22710fe3a664SChanwoo Choi ptr->devfreq = devfreq;
22720fe3a664SChanwoo Choi ptr->nb = nb;
22730fe3a664SChanwoo Choi ptr->list = list;
22740fe3a664SChanwoo Choi devres_add(dev, ptr);
22750fe3a664SChanwoo Choi
22760fe3a664SChanwoo Choi return 0;
22770fe3a664SChanwoo Choi }
22780fe3a664SChanwoo Choi EXPORT_SYMBOL(devm_devfreq_register_notifier);
22790fe3a664SChanwoo Choi
22800fe3a664SChanwoo Choi /**
22810fe3a664SChanwoo Choi * devm_devfreq_unregister_notifier()
228254cb5740SRandy Dunlap * - Resource-managed devfreq_unregister_notifier()
22830fe3a664SChanwoo Choi * @dev: The devfreq user device. (parent of devfreq)
22840fe3a664SChanwoo Choi * @devfreq: The devfreq object.
22850fe3a664SChanwoo Choi * @nb: The notifier block to be unregistered.
22860fe3a664SChanwoo Choi * @list: DEVFREQ_TRANSITION_NOTIFIER.
22870fe3a664SChanwoo Choi */
devm_devfreq_unregister_notifier(struct device * dev,struct devfreq * devfreq,struct notifier_block * nb,unsigned int list)22880fe3a664SChanwoo Choi void devm_devfreq_unregister_notifier(struct device *dev,
22890fe3a664SChanwoo Choi struct devfreq *devfreq,
22900fe3a664SChanwoo Choi struct notifier_block *nb,
22910fe3a664SChanwoo Choi unsigned int list)
22920fe3a664SChanwoo Choi {
22930fe3a664SChanwoo Choi WARN_ON(devres_release(dev, devm_devfreq_notifier_release,
22940fe3a664SChanwoo Choi devm_devfreq_dev_match, devfreq));
22950fe3a664SChanwoo Choi }
22960fe3a664SChanwoo Choi EXPORT_SYMBOL(devm_devfreq_unregister_notifier);
2297