diff options
author | Lukasz Luba <l.luba@partner.samsung.com> | 2018-12-05 14:05:53 +0300 |
---|---|---|
committer | MyungJoo Ham <myungjoo.ham@samsung.com> | 2018-12-11 05:09:47 +0300 |
commit | 83f8ca45afbf041e312909f442128b99657d90b7 (patch) | |
tree | d9f1ef6d6749aaaa6963cea58747a8024a24f61e | |
parent | 633141721b5bfce7017033a767208af591134b8f (diff) | |
download | linux-83f8ca45afbf041e312909f442128b99657d90b7.tar.xz |
PM / devfreq: add support for suspend/resume of a devfreq device
The patch prepares devfreq device for handling suspend/resume
functionality. The new fields will store needed information during this
process. Devfreq framework handles opp-suspend DT entry and there is no
need of modyfications in the drivers code. It uses atomic variables to
make sure no race condition affects the process.
Suggested-by: Tobias Jakobi <tjakobi@math.uni-bielefeld.de>
Suggested-by: Chanwoo Choi <cw00.choi@samsung.com>
Signed-off-by: Lukasz Luba <l.luba@partner.samsung.com>
Reviewed-by: Chanwoo Choi <cw00.choi@samsung.com>
Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com>
-rw-r--r-- | drivers/devfreq/devfreq.c | 47 | ||||
-rw-r--r-- | include/linux/devfreq.h | 7 |
2 files changed, 48 insertions, 6 deletions
diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index a9fd61bbacf1..46517b61b3a2 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -316,6 +316,10 @@ static int devfreq_set_target(struct devfreq *devfreq, unsigned long new_freq, "Couldn't update frequency transition information.\n"); devfreq->previous_freq = new_freq; + + if (devfreq->suspend_freq) + devfreq->resume_freq = cur_freq; + return err; } @@ -667,6 +671,9 @@ struct devfreq *devfreq_add_device(struct device *dev, } devfreq->max_freq = devfreq->scaling_max_freq; + devfreq->suspend_freq = dev_pm_opp_get_suspend_opp_freq(dev); + atomic_set(&devfreq->suspend_count, 0); + dev_set_name(&devfreq->dev, "devfreq%d", atomic_inc_return(&devfreq_no)); err = device_register(&devfreq->dev); @@ -867,14 +874,28 @@ EXPORT_SYMBOL(devm_devfreq_remove_device); */ int devfreq_suspend_device(struct devfreq *devfreq) { + int ret; + if (!devfreq) return -EINVAL; - if (!devfreq->governor) + if (atomic_inc_return(&devfreq->suspend_count) > 1) return 0; - return devfreq->governor->event_handler(devfreq, - DEVFREQ_GOV_SUSPEND, NULL); + if (devfreq->governor) { + ret = devfreq->governor->event_handler(devfreq, + DEVFREQ_GOV_SUSPEND, NULL); + if (ret) + return ret; + } + + if (devfreq->suspend_freq) { + ret = devfreq_set_target(devfreq, devfreq->suspend_freq, 0); + if (ret) + return ret; + } + + return 0; } EXPORT_SYMBOL(devfreq_suspend_device); @@ -888,14 +909,28 @@ EXPORT_SYMBOL(devfreq_suspend_device); */ int devfreq_resume_device(struct devfreq *devfreq) { + int ret; + if (!devfreq) return -EINVAL; - if (!devfreq->governor) + if (atomic_dec_return(&devfreq->suspend_count) >= 1) return 0; - return devfreq->governor->event_handler(devfreq, - DEVFREQ_GOV_RESUME, NULL); + if (devfreq->resume_freq) { + ret = devfreq_set_target(devfreq, devfreq->resume_freq, 0); + if (ret) + return ret; + } + + if (devfreq->governor) { + ret = devfreq->governor->event_handler(devfreq, + DEVFREQ_GOV_RESUME, NULL); + if (ret) + return ret; + } + + return 0; } EXPORT_SYMBOL(devfreq_resume_device); diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h index e4963b0f45da..d98519996927 100644 --- a/include/linux/devfreq.h +++ b/include/linux/devfreq.h @@ -131,6 +131,9 @@ struct devfreq_dev_profile { * @scaling_min_freq: Limit minimum frequency requested by OPP interface * @scaling_max_freq: Limit maximum frequency requested by OPP interface * @stop_polling: devfreq polling status of a device. + * @suspend_freq: frequency of a device set during suspend phase. + * @resume_freq: frequency of a device set in resume phase. + * @suspend_count: suspend requests counter for a device. * @total_trans: Number of devfreq transitions * @trans_table: Statistics of devfreq transitions * @time_in_state: Statistics of devfreq states @@ -167,6 +170,10 @@ struct devfreq { unsigned long scaling_max_freq; bool stop_polling; + unsigned long suspend_freq; + unsigned long resume_freq; + atomic_t suspend_count; + /* information for device frequency transition */ unsigned int total_trans; unsigned int *trans_table; |