aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChanwoo Choi <cw00.choi@samsung.com>2016-01-25 23:21:26 -0500
committerMyungJoo Ham <myungjoo.ham@samsung.com>2016-05-02 22:20:07 -0400
commit0fe3a66410a3ba96679be903f1e287d7a0a264a9 (patch)
tree1ce223e8508fce15daebc93b6ce511721e418fc9
parent8f510aeb223b26c4ffbece9fa92e4befea470f57 (diff)
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>
-rw-r--r--drivers/devfreq/devfreq.c163
-rw-r--r--include/linux/devfreq.h59
2 files changed, 220 insertions, 2 deletions
diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c
index 20a9422c2552..1d6c803804d5 100644
--- a/drivers/devfreq/devfreq.c
+++ b/drivers/devfreq/devfreq.c
@@ -189,6 +189,29 @@ static struct devfreq_governor *find_devfreq_governor(const char *name)
189 return ERR_PTR(-ENODEV); 189 return ERR_PTR(-ENODEV);
190} 190}
191 191
192static 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
192/* Load monitoring helper functions for governors use */ 215/* Load monitoring helper functions for governors use */
193 216
194/** 217/**
@@ -200,7 +223,8 @@ static struct devfreq_governor *find_devfreq_governor(const char *name)
200 */ 223 */
201int update_devfreq(struct devfreq *devfreq) 224int update_devfreq(struct devfreq *devfreq)
202{ 225{
203 unsigned long freq; 226 struct devfreq_freqs freqs;
227 unsigned long freq, cur_freq;
204 int err = 0; 228 int err = 0;
205 u32 flags = 0; 229 u32 flags = 0;
206 230
@@ -234,10 +258,22 @@ int update_devfreq(struct devfreq *devfreq)
234 flags |= DEVFREQ_FLAG_LEAST_UPPER_BOUND; /* Use LUB */ 258 flags |= DEVFREQ_FLAG_LEAST_UPPER_BOUND; /* Use LUB */
235 } 259 }
236 260
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
237 err = devfreq->profile->target(devfreq->dev.parent, &freq, flags); 270 err = devfreq->profile->target(devfreq->dev.parent, &freq, flags);
238 if (err) 271 if (err)
239 return err; 272 return err;
240 273
274 freqs.new = freq;
275 devfreq_notify_transition(devfreq, &freqs, DEVFREQ_POSTCHANGE);
276
241 if (devfreq->profile->freq_table) 277 if (devfreq->profile->freq_table)
242 if (devfreq_update_status(devfreq, freq)) 278 if (devfreq_update_status(devfreq, freq))
243 dev_err(&devfreq->dev, 279 dev_err(&devfreq->dev,
@@ -542,6 +578,8 @@ struct devfreq *devfreq_add_device(struct device *dev,
542 goto err_out; 578 goto err_out;
543 } 579 }
544 580
581 srcu_init_notifier_head(&devfreq->transition_notifier_list);
582
545 mutex_unlock(&devfreq->lock); 583 mutex_unlock(&devfreq->lock);
546 584
547 mutex_lock(&devfreq_list_lock); 585 mutex_lock(&devfreq_list_lock);
@@ -1310,6 +1348,129 @@ void devm_devfreq_unregister_opp_notifier(struct device *dev,
1310} 1348}
1311EXPORT_SYMBOL(devm_devfreq_unregister_opp_notifier); 1349EXPORT_SYMBOL(devm_devfreq_unregister_opp_notifier);
1312 1350
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 */
1357int 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}
1377EXPORT_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 */
1385int 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}
1405EXPORT_SYMBOL(devfreq_unregister_notifier);
1406
1407struct devfreq_notifier_devres {
1408 struct devfreq *devfreq;
1409 struct notifier_block *nb;
1410 unsigned int list;
1411};
1412
1413static 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 */
1428int 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}
1454EXPORT_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 */
1464void 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}
1472EXPORT_SYMBOL(devm_devfreq_unregister_notifier);
1473
1313MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>"); 1474MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
1314MODULE_DESCRIPTION("devfreq class support"); 1475MODULE_DESCRIPTION("devfreq class support");
1315MODULE_LICENSE("GPL"); 1476MODULE_LICENSE("GPL");
diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h
index aa0b8424ebc3..98c699304e12 100644
--- a/include/linux/devfreq.h
+++ b/include/linux/devfreq.h
@@ -19,6 +19,13 @@
19 19
20#define DEVFREQ_NAME_LEN 16 20#define DEVFREQ_NAME_LEN 16
21 21
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
22struct devfreq; 29struct devfreq;
23 30
24/** 31/**
@@ -143,6 +150,7 @@ struct devfreq_governor {
143 * @trans_table: Statistics of devfreq transitions 150 * @trans_table: Statistics of devfreq transitions
144 * @time_in_state: Statistics of devfreq states 151 * @time_in_state: Statistics of devfreq states
145 * @last_stat_updated: The last time stat updated 152 * @last_stat_updated: The last time stat updated
153 * @transition_notifier_list: list head of DEVFREQ_TRANSITION_NOTIFIER notifier
146 * 154 *
147 * This structure stores the devfreq information for a give device. 155 * This structure stores the devfreq information for a give device.
148 * 156 *
@@ -177,6 +185,13 @@ struct devfreq {
177 unsigned int *trans_table; 185 unsigned int *trans_table;
178 unsigned long *time_in_state; 186 unsigned long *time_in_state;
179 unsigned long last_stat_updated; 187 unsigned long last_stat_updated;
188
189 struct srcu_notifier_head transition_notifier_list;
190};
191
192struct devfreq_freqs {
193 unsigned long old;
194 unsigned long new;
180}; 195};
181 196
182#if defined(CONFIG_PM_DEVFREQ) 197#if defined(CONFIG_PM_DEVFREQ)
@@ -207,7 +222,20 @@ extern int devm_devfreq_register_opp_notifier(struct device *dev,
207 struct devfreq *devfreq); 222 struct devfreq *devfreq);
208extern void devm_devfreq_unregister_opp_notifier(struct device *dev, 223extern void devm_devfreq_unregister_opp_notifier(struct device *dev,
209 struct devfreq *devfreq); 224 struct devfreq *devfreq);
210 225extern int devfreq_register_notifier(struct devfreq *devfreq,
226 struct notifier_block *nb,
227 unsigned int list);
228extern int devfreq_unregister_notifier(struct devfreq *devfreq,
229 struct notifier_block *nb,
230 unsigned int list);
231extern int devm_devfreq_register_notifier(struct device *dev,
232 struct devfreq *devfreq,
233 struct notifier_block *nb,
234 unsigned int list);
235extern void devm_devfreq_unregister_notifier(struct device *dev,
236 struct devfreq *devfreq,
237 struct notifier_block *nb,
238 unsigned int list);
211extern struct devfreq *devfreq_get_devfreq_by_phandle(struct device *dev, 239extern struct devfreq *devfreq_get_devfreq_by_phandle(struct device *dev,
212 int index); 240 int index);
213 241
@@ -310,6 +338,35 @@ static inline void devm_devfreq_unregister_opp_notifier(struct device *dev,
310{ 338{
311} 339}
312 340
341static inline int devfreq_register_notifier(struct devfreq *devfreq,
342 struct notifier_block *nb,
343 unsigned int list)
344{
345 return 0;
346}
347
348static inline int devfreq_unregister_notifier(struct devfreq *devfreq,
349 struct notifier_block *nb,
350 unsigned int list)
351{
352 return 0;
353}
354
355static 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
363static 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
313static inline struct devfreq *devfreq_get_devfreq_by_phandle(struct device *dev, 370static inline struct devfreq *devfreq_get_devfreq_by_phandle(struct device *dev,
314 int index) 371 int index)
315{ 372{