1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * linux/drivers/devfreq/governor_passive.c
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright (C) 2016 Samsung Electronics
6*4882a593Smuzhiyun * Author: Chanwoo Choi <cw00.choi@samsung.com>
7*4882a593Smuzhiyun * Author: MyungJoo Ham <myungjoo.ham@samsung.com>
8*4882a593Smuzhiyun */
9*4882a593Smuzhiyun
10*4882a593Smuzhiyun #include <linux/module.h>
11*4882a593Smuzhiyun #include <linux/device.h>
12*4882a593Smuzhiyun #include <linux/devfreq.h>
13*4882a593Smuzhiyun #include "governor.h"
14*4882a593Smuzhiyun
devfreq_passive_get_target_freq(struct devfreq * devfreq,unsigned long * freq)15*4882a593Smuzhiyun static int devfreq_passive_get_target_freq(struct devfreq *devfreq,
16*4882a593Smuzhiyun unsigned long *freq)
17*4882a593Smuzhiyun {
18*4882a593Smuzhiyun struct devfreq_passive_data *p_data
19*4882a593Smuzhiyun = (struct devfreq_passive_data *)devfreq->data;
20*4882a593Smuzhiyun struct devfreq *parent_devfreq = (struct devfreq *)p_data->parent;
21*4882a593Smuzhiyun unsigned long child_freq = ULONG_MAX;
22*4882a593Smuzhiyun struct dev_pm_opp *opp;
23*4882a593Smuzhiyun int i, count, ret = 0;
24*4882a593Smuzhiyun
25*4882a593Smuzhiyun /*
26*4882a593Smuzhiyun * If the devfreq device with passive governor has the specific method
27*4882a593Smuzhiyun * to determine the next frequency, should use the get_target_freq()
28*4882a593Smuzhiyun * of struct devfreq_passive_data.
29*4882a593Smuzhiyun */
30*4882a593Smuzhiyun if (p_data->get_target_freq) {
31*4882a593Smuzhiyun ret = p_data->get_target_freq(devfreq, freq);
32*4882a593Smuzhiyun goto out;
33*4882a593Smuzhiyun }
34*4882a593Smuzhiyun
35*4882a593Smuzhiyun /*
36*4882a593Smuzhiyun * If the parent and passive devfreq device uses the OPP table,
37*4882a593Smuzhiyun * get the next frequency by using the OPP table.
38*4882a593Smuzhiyun */
39*4882a593Smuzhiyun
40*4882a593Smuzhiyun /*
41*4882a593Smuzhiyun * - parent devfreq device uses the governors except for passive.
42*4882a593Smuzhiyun * - passive devfreq device uses the passive governor.
43*4882a593Smuzhiyun *
44*4882a593Smuzhiyun * Each devfreq has the OPP table. After deciding the new frequency
45*4882a593Smuzhiyun * from the governor of parent devfreq device, the passive governor
46*4882a593Smuzhiyun * need to get the index of new frequency on OPP table of parent
47*4882a593Smuzhiyun * device. And then the index is used for getting the suitable
48*4882a593Smuzhiyun * new frequency for passive devfreq device.
49*4882a593Smuzhiyun */
50*4882a593Smuzhiyun if (!devfreq->profile || !devfreq->profile->freq_table
51*4882a593Smuzhiyun || devfreq->profile->max_state <= 0)
52*4882a593Smuzhiyun return -EINVAL;
53*4882a593Smuzhiyun
54*4882a593Smuzhiyun /*
55*4882a593Smuzhiyun * The passive governor have to get the correct frequency from OPP
56*4882a593Smuzhiyun * list of parent device. Because in this case, *freq is temporary
57*4882a593Smuzhiyun * value which is decided by ondemand governor.
58*4882a593Smuzhiyun */
59*4882a593Smuzhiyun opp = devfreq_recommended_opp(parent_devfreq->dev.parent, freq, 0);
60*4882a593Smuzhiyun if (IS_ERR(opp)) {
61*4882a593Smuzhiyun ret = PTR_ERR(opp);
62*4882a593Smuzhiyun goto out;
63*4882a593Smuzhiyun }
64*4882a593Smuzhiyun
65*4882a593Smuzhiyun dev_pm_opp_put(opp);
66*4882a593Smuzhiyun
67*4882a593Smuzhiyun /*
68*4882a593Smuzhiyun * Get the OPP table's index of decided freqeuncy by governor
69*4882a593Smuzhiyun * of parent device.
70*4882a593Smuzhiyun */
71*4882a593Smuzhiyun for (i = 0; i < parent_devfreq->profile->max_state; i++)
72*4882a593Smuzhiyun if (parent_devfreq->profile->freq_table[i] == *freq)
73*4882a593Smuzhiyun break;
74*4882a593Smuzhiyun
75*4882a593Smuzhiyun if (i == parent_devfreq->profile->max_state) {
76*4882a593Smuzhiyun ret = -EINVAL;
77*4882a593Smuzhiyun goto out;
78*4882a593Smuzhiyun }
79*4882a593Smuzhiyun
80*4882a593Smuzhiyun /* Get the suitable frequency by using index of parent device. */
81*4882a593Smuzhiyun if (i < devfreq->profile->max_state) {
82*4882a593Smuzhiyun child_freq = devfreq->profile->freq_table[i];
83*4882a593Smuzhiyun } else {
84*4882a593Smuzhiyun count = devfreq->profile->max_state;
85*4882a593Smuzhiyun child_freq = devfreq->profile->freq_table[count - 1];
86*4882a593Smuzhiyun }
87*4882a593Smuzhiyun
88*4882a593Smuzhiyun /* Return the suitable frequency for passive device. */
89*4882a593Smuzhiyun *freq = child_freq;
90*4882a593Smuzhiyun
91*4882a593Smuzhiyun out:
92*4882a593Smuzhiyun return ret;
93*4882a593Smuzhiyun }
94*4882a593Smuzhiyun
update_devfreq_passive(struct devfreq * devfreq,unsigned long freq)95*4882a593Smuzhiyun static int update_devfreq_passive(struct devfreq *devfreq, unsigned long freq)
96*4882a593Smuzhiyun {
97*4882a593Smuzhiyun int ret;
98*4882a593Smuzhiyun
99*4882a593Smuzhiyun if (!devfreq->governor)
100*4882a593Smuzhiyun return -EINVAL;
101*4882a593Smuzhiyun
102*4882a593Smuzhiyun mutex_lock_nested(&devfreq->lock, SINGLE_DEPTH_NESTING);
103*4882a593Smuzhiyun
104*4882a593Smuzhiyun ret = devfreq->governor->get_target_freq(devfreq, &freq);
105*4882a593Smuzhiyun if (ret < 0)
106*4882a593Smuzhiyun goto out;
107*4882a593Smuzhiyun
108*4882a593Smuzhiyun ret = devfreq->profile->target(devfreq->dev.parent, &freq, 0);
109*4882a593Smuzhiyun if (ret < 0)
110*4882a593Smuzhiyun goto out;
111*4882a593Smuzhiyun
112*4882a593Smuzhiyun if (devfreq->profile->freq_table
113*4882a593Smuzhiyun && (devfreq_update_status(devfreq, freq)))
114*4882a593Smuzhiyun dev_err(&devfreq->dev,
115*4882a593Smuzhiyun "Couldn't update frequency transition information.\n");
116*4882a593Smuzhiyun
117*4882a593Smuzhiyun devfreq->previous_freq = freq;
118*4882a593Smuzhiyun
119*4882a593Smuzhiyun out:
120*4882a593Smuzhiyun mutex_unlock(&devfreq->lock);
121*4882a593Smuzhiyun
122*4882a593Smuzhiyun return 0;
123*4882a593Smuzhiyun }
124*4882a593Smuzhiyun
devfreq_passive_notifier_call(struct notifier_block * nb,unsigned long event,void * ptr)125*4882a593Smuzhiyun static int devfreq_passive_notifier_call(struct notifier_block *nb,
126*4882a593Smuzhiyun unsigned long event, void *ptr)
127*4882a593Smuzhiyun {
128*4882a593Smuzhiyun struct devfreq_passive_data *data
129*4882a593Smuzhiyun = container_of(nb, struct devfreq_passive_data, nb);
130*4882a593Smuzhiyun struct devfreq *devfreq = (struct devfreq *)data->this;
131*4882a593Smuzhiyun struct devfreq *parent = (struct devfreq *)data->parent;
132*4882a593Smuzhiyun struct devfreq_freqs *freqs = (struct devfreq_freqs *)ptr;
133*4882a593Smuzhiyun unsigned long freq = freqs->new;
134*4882a593Smuzhiyun
135*4882a593Smuzhiyun switch (event) {
136*4882a593Smuzhiyun case DEVFREQ_PRECHANGE:
137*4882a593Smuzhiyun if (parent->previous_freq > freq)
138*4882a593Smuzhiyun update_devfreq_passive(devfreq, freq);
139*4882a593Smuzhiyun break;
140*4882a593Smuzhiyun case DEVFREQ_POSTCHANGE:
141*4882a593Smuzhiyun if (parent->previous_freq < freq)
142*4882a593Smuzhiyun update_devfreq_passive(devfreq, freq);
143*4882a593Smuzhiyun break;
144*4882a593Smuzhiyun }
145*4882a593Smuzhiyun
146*4882a593Smuzhiyun return NOTIFY_DONE;
147*4882a593Smuzhiyun }
148*4882a593Smuzhiyun
devfreq_passive_event_handler(struct devfreq * devfreq,unsigned int event,void * data)149*4882a593Smuzhiyun static int devfreq_passive_event_handler(struct devfreq *devfreq,
150*4882a593Smuzhiyun unsigned int event, void *data)
151*4882a593Smuzhiyun {
152*4882a593Smuzhiyun struct devfreq_passive_data *p_data
153*4882a593Smuzhiyun = (struct devfreq_passive_data *)devfreq->data;
154*4882a593Smuzhiyun struct devfreq *parent = (struct devfreq *)p_data->parent;
155*4882a593Smuzhiyun struct notifier_block *nb = &p_data->nb;
156*4882a593Smuzhiyun int ret = 0;
157*4882a593Smuzhiyun
158*4882a593Smuzhiyun if (!parent)
159*4882a593Smuzhiyun return -EPROBE_DEFER;
160*4882a593Smuzhiyun
161*4882a593Smuzhiyun switch (event) {
162*4882a593Smuzhiyun case DEVFREQ_GOV_START:
163*4882a593Smuzhiyun if (!p_data->this)
164*4882a593Smuzhiyun p_data->this = devfreq;
165*4882a593Smuzhiyun
166*4882a593Smuzhiyun nb->notifier_call = devfreq_passive_notifier_call;
167*4882a593Smuzhiyun ret = devfreq_register_notifier(parent, nb,
168*4882a593Smuzhiyun DEVFREQ_TRANSITION_NOTIFIER);
169*4882a593Smuzhiyun break;
170*4882a593Smuzhiyun case DEVFREQ_GOV_STOP:
171*4882a593Smuzhiyun WARN_ON(devfreq_unregister_notifier(parent, nb,
172*4882a593Smuzhiyun DEVFREQ_TRANSITION_NOTIFIER));
173*4882a593Smuzhiyun break;
174*4882a593Smuzhiyun default:
175*4882a593Smuzhiyun break;
176*4882a593Smuzhiyun }
177*4882a593Smuzhiyun
178*4882a593Smuzhiyun return ret;
179*4882a593Smuzhiyun }
180*4882a593Smuzhiyun
181*4882a593Smuzhiyun static struct devfreq_governor devfreq_passive = {
182*4882a593Smuzhiyun .name = DEVFREQ_GOV_PASSIVE,
183*4882a593Smuzhiyun .immutable = 1,
184*4882a593Smuzhiyun .get_target_freq = devfreq_passive_get_target_freq,
185*4882a593Smuzhiyun .event_handler = devfreq_passive_event_handler,
186*4882a593Smuzhiyun };
187*4882a593Smuzhiyun
devfreq_passive_init(void)188*4882a593Smuzhiyun static int __init devfreq_passive_init(void)
189*4882a593Smuzhiyun {
190*4882a593Smuzhiyun return devfreq_add_governor(&devfreq_passive);
191*4882a593Smuzhiyun }
192*4882a593Smuzhiyun subsys_initcall(devfreq_passive_init);
193*4882a593Smuzhiyun
devfreq_passive_exit(void)194*4882a593Smuzhiyun static void __exit devfreq_passive_exit(void)
195*4882a593Smuzhiyun {
196*4882a593Smuzhiyun int ret;
197*4882a593Smuzhiyun
198*4882a593Smuzhiyun ret = devfreq_remove_governor(&devfreq_passive);
199*4882a593Smuzhiyun if (ret)
200*4882a593Smuzhiyun pr_err("%s: failed remove governor %d\n", __func__, ret);
201*4882a593Smuzhiyun }
202*4882a593Smuzhiyun module_exit(devfreq_passive_exit);
203*4882a593Smuzhiyun
204*4882a593Smuzhiyun MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>");
205*4882a593Smuzhiyun MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
206*4882a593Smuzhiyun MODULE_DESCRIPTION("DEVFREQ Passive governor");
207*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
208