Skip to content

Commit 0fe3a66

Browse files
chanwoochoimyungjoo
authored andcommitted
PM / devfreq: Add new DEVFREQ_TRANSITION_NOTIFIER notifier
This patch adds the new DEVFREQ_TRANSITION_NOTIFIER notifier to send the notification when the frequency of device is changed. This notifier has two state as following: - DEVFREQ_PRECHANGE : Notify it before chaning the frequency of device - DEVFREQ_POSTCHANGE : Notify it after changed the frequency of device And this patch adds the resourced-managed function to release the resource automatically when error happen. Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com> [m.reichl and linux.amoon: Tested it on exynos4412-odroidu3 board] Tested-by: Markus Reichl <m.reichl@fivetechno.de> Tested-by: Anand Moon <linux.amoon@gmail.com> Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com> Acked-by: Krzysztof Kozlowski <k.kozlowski@samsung.com>
1 parent 8f510ae commit 0fe3a66

File tree

2 files changed

+220
-2
lines changed

2 files changed

+220
-2
lines changed

drivers/devfreq/devfreq.c

Lines changed: 162 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,29 @@ static struct devfreq_governor *find_devfreq_governor(const char *name)
189189
return ERR_PTR(-ENODEV);
190190
}
191191

192+
static int devfreq_notify_transition(struct devfreq *devfreq,
193+
struct devfreq_freqs *freqs, unsigned int state)
194+
{
195+
if (!devfreq)
196+
return -EINVAL;
197+
198+
switch (state) {
199+
case DEVFREQ_PRECHANGE:
200+
srcu_notifier_call_chain(&devfreq->transition_notifier_list,
201+
DEVFREQ_PRECHANGE, freqs);
202+
break;
203+
204+
case DEVFREQ_POSTCHANGE:
205+
srcu_notifier_call_chain(&devfreq->transition_notifier_list,
206+
DEVFREQ_POSTCHANGE, freqs);
207+
break;
208+
default:
209+
return -EINVAL;
210+
}
211+
212+
return 0;
213+
}
214+
192215
/* Load monitoring helper functions for governors use */
193216

194217
/**
@@ -200,7 +223,8 @@ static struct devfreq_governor *find_devfreq_governor(const char *name)
200223
*/
201224
int update_devfreq(struct devfreq *devfreq)
202225
{
203-
unsigned long freq;
226+
struct devfreq_freqs freqs;
227+
unsigned long freq, cur_freq;
204228
int err = 0;
205229
u32 flags = 0;
206230

@@ -234,10 +258,22 @@ int update_devfreq(struct devfreq *devfreq)
234258
flags |= DEVFREQ_FLAG_LEAST_UPPER_BOUND; /* Use LUB */
235259
}
236260

261+
if (devfreq->profile->get_cur_freq)
262+
devfreq->profile->get_cur_freq(devfreq->dev.parent, &cur_freq);
263+
else
264+
cur_freq = devfreq->previous_freq;
265+
266+
freqs.old = cur_freq;
267+
freqs.new = freq;
268+
devfreq_notify_transition(devfreq, &freqs, DEVFREQ_PRECHANGE);
269+
237270
err = devfreq->profile->target(devfreq->dev.parent, &freq, flags);
238271
if (err)
239272
return err;
240273

274+
freqs.new = freq;
275+
devfreq_notify_transition(devfreq, &freqs, DEVFREQ_POSTCHANGE);
276+
241277
if (devfreq->profile->freq_table)
242278
if (devfreq_update_status(devfreq, freq))
243279
dev_err(&devfreq->dev,
@@ -542,6 +578,8 @@ struct devfreq *devfreq_add_device(struct device *dev,
542578
goto err_out;
543579
}
544580

581+
srcu_init_notifier_head(&devfreq->transition_notifier_list);
582+
545583
mutex_unlock(&devfreq->lock);
546584

547585
mutex_lock(&devfreq_list_lock);
@@ -1310,6 +1348,129 @@ void devm_devfreq_unregister_opp_notifier(struct device *dev,
13101348
}
13111349
EXPORT_SYMBOL(devm_devfreq_unregister_opp_notifier);
13121350

1351+
/**
1352+
* devfreq_register_notifier() - Register a driver with devfreq
1353+
* @devfreq: The devfreq object.
1354+
* @nb: The notifier block to register.
1355+
* @list: DEVFREQ_TRANSITION_NOTIFIER.
1356+
*/
1357+
int devfreq_register_notifier(struct devfreq *devfreq,
1358+
struct notifier_block *nb,
1359+
unsigned int list)
1360+
{
1361+
int ret = 0;
1362+
1363+
if (!devfreq)
1364+
return -EINVAL;
1365+
1366+
switch (list) {
1367+
case DEVFREQ_TRANSITION_NOTIFIER:
1368+
ret = srcu_notifier_chain_register(
1369+
&devfreq->transition_notifier_list, nb);
1370+
break;
1371+
default:
1372+
ret = -EINVAL;
1373+
}
1374+
1375+
return ret;
1376+
}
1377+
EXPORT_SYMBOL(devfreq_register_notifier);
1378+
1379+
/*
1380+
* devfreq_unregister_notifier() - Unregister a driver with devfreq
1381+
* @devfreq: The devfreq object.
1382+
* @nb: The notifier block to be unregistered.
1383+
* @list: DEVFREQ_TRANSITION_NOTIFIER.
1384+
*/
1385+
int devfreq_unregister_notifier(struct devfreq *devfreq,
1386+
struct notifier_block *nb,
1387+
unsigned int list)
1388+
{
1389+
int ret = 0;
1390+
1391+
if (!devfreq)
1392+
return -EINVAL;
1393+
1394+
switch (list) {
1395+
case DEVFREQ_TRANSITION_NOTIFIER:
1396+
ret = srcu_notifier_chain_unregister(
1397+
&devfreq->transition_notifier_list, nb);
1398+
break;
1399+
default:
1400+
ret = -EINVAL;
1401+
}
1402+
1403+
return ret;
1404+
}
1405+
EXPORT_SYMBOL(devfreq_unregister_notifier);
1406+
1407+
struct devfreq_notifier_devres {
1408+
struct devfreq *devfreq;
1409+
struct notifier_block *nb;
1410+
unsigned int list;
1411+
};
1412+
1413+
static void devm_devfreq_notifier_release(struct device *dev, void *res)
1414+
{
1415+
struct devfreq_notifier_devres *this = res;
1416+
1417+
devfreq_unregister_notifier(this->devfreq, this->nb, this->list);
1418+
}
1419+
1420+
/**
1421+
* devm_devfreq_register_notifier()
1422+
- Resource-managed devfreq_register_notifier()
1423+
* @dev: The devfreq user device. (parent of devfreq)
1424+
* @devfreq: The devfreq object.
1425+
* @nb: The notifier block to be unregistered.
1426+
* @list: DEVFREQ_TRANSITION_NOTIFIER.
1427+
*/
1428+
int devm_devfreq_register_notifier(struct device *dev,
1429+
struct devfreq *devfreq,
1430+
struct notifier_block *nb,
1431+
unsigned int list)
1432+
{
1433+
struct devfreq_notifier_devres *ptr;
1434+
int ret;
1435+
1436+
ptr = devres_alloc(devm_devfreq_notifier_release, sizeof(*ptr),
1437+
GFP_KERNEL);
1438+
if (!ptr)
1439+
return -ENOMEM;
1440+
1441+
ret = devfreq_register_notifier(devfreq, nb, list);
1442+
if (ret) {
1443+
devres_free(ptr);
1444+
return ret;
1445+
}
1446+
1447+
ptr->devfreq = devfreq;
1448+
ptr->nb = nb;
1449+
ptr->list = list;
1450+
devres_add(dev, ptr);
1451+
1452+
return 0;
1453+
}
1454+
EXPORT_SYMBOL(devm_devfreq_register_notifier);
1455+
1456+
/**
1457+
* devm_devfreq_unregister_notifier()
1458+
- Resource-managed devfreq_unregister_notifier()
1459+
* @dev: The devfreq user device. (parent of devfreq)
1460+
* @devfreq: The devfreq object.
1461+
* @nb: The notifier block to be unregistered.
1462+
* @list: DEVFREQ_TRANSITION_NOTIFIER.
1463+
*/
1464+
void devm_devfreq_unregister_notifier(struct device *dev,
1465+
struct devfreq *devfreq,
1466+
struct notifier_block *nb,
1467+
unsigned int list)
1468+
{
1469+
WARN_ON(devres_release(dev, devm_devfreq_notifier_release,
1470+
devm_devfreq_dev_match, devfreq));
1471+
}
1472+
EXPORT_SYMBOL(devm_devfreq_unregister_notifier);
1473+
13131474
MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
13141475
MODULE_DESCRIPTION("devfreq class support");
13151476
MODULE_LICENSE("GPL");

include/linux/devfreq.h

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,13 @@
1919

2020
#define DEVFREQ_NAME_LEN 16
2121

22+
/* DEVFREQ notifier interface */
23+
#define DEVFREQ_TRANSITION_NOTIFIER (0)
24+
25+
/* Transition notifiers of DEVFREQ_TRANSITION_NOTIFIER */
26+
#define DEVFREQ_PRECHANGE (0)
27+
#define DEVFREQ_POSTCHANGE (1)
28+
2229
struct devfreq;
2330

2431
/**
@@ -143,6 +150,7 @@ struct devfreq_governor {
143150
* @trans_table: Statistics of devfreq transitions
144151
* @time_in_state: Statistics of devfreq states
145152
* @last_stat_updated: The last time stat updated
153+
* @transition_notifier_list: list head of DEVFREQ_TRANSITION_NOTIFIER notifier
146154
*
147155
* This structure stores the devfreq information for a give device.
148156
*
@@ -177,6 +185,13 @@ struct devfreq {
177185
unsigned int *trans_table;
178186
unsigned long *time_in_state;
179187
unsigned long last_stat_updated;
188+
189+
struct srcu_notifier_head transition_notifier_list;
190+
};
191+
192+
struct devfreq_freqs {
193+
unsigned long old;
194+
unsigned long new;
180195
};
181196

182197
#if defined(CONFIG_PM_DEVFREQ)
@@ -207,7 +222,20 @@ extern int devm_devfreq_register_opp_notifier(struct device *dev,
207222
struct devfreq *devfreq);
208223
extern void devm_devfreq_unregister_opp_notifier(struct device *dev,
209224
struct devfreq *devfreq);
210-
225+
extern int devfreq_register_notifier(struct devfreq *devfreq,
226+
struct notifier_block *nb,
227+
unsigned int list);
228+
extern int devfreq_unregister_notifier(struct devfreq *devfreq,
229+
struct notifier_block *nb,
230+
unsigned int list);
231+
extern int devm_devfreq_register_notifier(struct device *dev,
232+
struct devfreq *devfreq,
233+
struct notifier_block *nb,
234+
unsigned int list);
235+
extern void devm_devfreq_unregister_notifier(struct device *dev,
236+
struct devfreq *devfreq,
237+
struct notifier_block *nb,
238+
unsigned int list);
211239
extern struct devfreq *devfreq_get_devfreq_by_phandle(struct device *dev,
212240
int index);
213241

@@ -310,6 +338,35 @@ static inline void devm_devfreq_unregister_opp_notifier(struct device *dev,
310338
{
311339
}
312340

341+
static inline int devfreq_register_notifier(struct devfreq *devfreq,
342+
struct notifier_block *nb,
343+
unsigned int list)
344+
{
345+
return 0;
346+
}
347+
348+
static inline int devfreq_unregister_notifier(struct devfreq *devfreq,
349+
struct notifier_block *nb,
350+
unsigned int list)
351+
{
352+
return 0;
353+
}
354+
355+
static inline int devm_devfreq_register_notifier(struct device *dev,
356+
struct devfreq *devfreq,
357+
struct notifier_block *nb,
358+
unsigned int list)
359+
{
360+
return 0;
361+
}
362+
363+
static inline void devm_devfreq_unregister_notifier(struct device *dev,
364+
struct devfreq *devfreq,
365+
struct notifier_block *nb,
366+
unsigned int list)
367+
{
368+
}
369+
313370
static inline struct devfreq *devfreq_get_devfreq_by_phandle(struct device *dev,
314371
int index)
315372
{

0 commit comments

Comments
 (0)