aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMyungJoo Ham <myungjoo.ham@samsung.com>2012-03-16 16:54:53 -0400
committerRafael J. Wysocki <rjw@sisk.pl>2012-03-17 16:51:34 -0400
commitab5f299f51259fd2466cf35c89d79bd960e0fc32 (patch)
tree2c8b409511e48e04833b57457acdb98207201b0b
parente4c9d8efe6bdc844071d68960dfa2003c5cf6449 (diff)
PM / devfreq: add relation of recommended frequency.
The semantics of "target frequency" given to devfreq driver from devfreq framework has always been interpretted as "at least" or GLB (greatest lower bound). However, the framework might want the device driver to limit its max frequency (LUB: least upper bound), especially if it is given by thermal framework (it's too hot). Thus, the target fuction should have another parameter to express whether the framework wants GLB or LUB. And, the additional parameter, "u32 flags", does it. With the update, devfreq_recommended_opp() is also updated. Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com> Reviewed-by: Mike Turquette <mturquette@ti.com> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
-rw-r--r--drivers/devfreq/devfreq.c42
-rw-r--r--drivers/devfreq/exynos4_bus.c14
-rw-r--r--include/linux/devfreq.h16
3 files changed, 61 insertions, 11 deletions
diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c
index a129a7b6bfd1..70c31d43fff3 100644
--- a/drivers/devfreq/devfreq.c
+++ b/drivers/devfreq/devfreq.c
@@ -83,6 +83,7 @@ int update_devfreq(struct devfreq *devfreq)
83{ 83{
84 unsigned long freq; 84 unsigned long freq;
85 int err = 0; 85 int err = 0;
86 u32 flags = 0;
86 87
87 if (!mutex_is_locked(&devfreq->lock)) { 88 if (!mutex_is_locked(&devfreq->lock)) {
88 WARN(true, "devfreq->lock must be locked by the caller.\n"); 89 WARN(true, "devfreq->lock must be locked by the caller.\n");
@@ -94,7 +95,24 @@ int update_devfreq(struct devfreq *devfreq)
94 if (err) 95 if (err)
95 return err; 96 return err;
96 97
97 err = devfreq->profile->target(devfreq->dev.parent, &freq); 98 /*
99 * Adjust the freuqency with user freq and QoS.
100 *
101 * List from the highest proiority
102 * max_freq (probably called by thermal when it's too hot)
103 * min_freq
104 */
105
106 if (devfreq->min_freq && freq < devfreq->min_freq) {
107 freq = devfreq->min_freq;
108 flags &= ~DEVFREQ_FLAG_LEAST_UPPER_BOUND; /* Use GLB */
109 }
110 if (devfreq->max_freq && freq > devfreq->max_freq) {
111 freq = devfreq->max_freq;
112 flags |= DEVFREQ_FLAG_LEAST_UPPER_BOUND; /* Use LUB */
113 }
114
115 err = devfreq->profile->target(devfreq->dev.parent, &freq, flags);
98 if (err) 116 if (err)
99 return err; 117 return err;
100 118
@@ -625,14 +643,30 @@ module_exit(devfreq_exit);
625 * freq value given to target callback. 643 * freq value given to target callback.
626 * @dev The devfreq user device. (parent of devfreq) 644 * @dev The devfreq user device. (parent of devfreq)
627 * @freq The frequency given to target function 645 * @freq The frequency given to target function
646 * @flags Flags handed from devfreq framework.
628 * 647 *
629 */ 648 */
630struct opp *devfreq_recommended_opp(struct device *dev, unsigned long *freq) 649struct opp *devfreq_recommended_opp(struct device *dev, unsigned long *freq,
650 u32 flags)
631{ 651{
632 struct opp *opp = opp_find_freq_ceil(dev, freq); 652 struct opp *opp;
633 653
634 if (opp == ERR_PTR(-ENODEV)) 654 if (flags & DEVFREQ_FLAG_LEAST_UPPER_BOUND) {
655 /* The freq is an upper bound. opp should be lower */
635 opp = opp_find_freq_floor(dev, freq); 656 opp = opp_find_freq_floor(dev, freq);
657
658 /* If not available, use the closest opp */
659 if (opp == ERR_PTR(-ENODEV))
660 opp = opp_find_freq_ceil(dev, freq);
661 } else {
662 /* The freq is an lower bound. opp should be higher */
663 opp = opp_find_freq_ceil(dev, freq);
664
665 /* If not available, use the closest opp */
666 if (opp == ERR_PTR(-ENODEV))
667 opp = opp_find_freq_floor(dev, freq);
668 }
669
636 return opp; 670 return opp;
637} 671}
638 672
diff --git a/drivers/devfreq/exynos4_bus.c b/drivers/devfreq/exynos4_bus.c
index 590d6865e388..1a361e99965a 100644
--- a/drivers/devfreq/exynos4_bus.c
+++ b/drivers/devfreq/exynos4_bus.c
@@ -619,13 +619,19 @@ static int exynos4_bus_setvolt(struct busfreq_data *data, struct opp *opp,
619 return err; 619 return err;
620} 620}
621 621
622static int exynos4_bus_target(struct device *dev, unsigned long *_freq) 622static int exynos4_bus_target(struct device *dev, unsigned long *_freq,
623 u32 flags)
623{ 624{
624 int err = 0; 625 int err = 0;
625 struct busfreq_data *data = dev_get_drvdata(dev); 626 struct platform_device *pdev = container_of(dev, struct platform_device,
626 struct opp *opp = devfreq_recommended_opp(dev, _freq); 627 dev);
627 unsigned long old_freq = opp_get_freq(data->curr_opp); 628 struct busfreq_data *data = platform_get_drvdata(pdev);
629 struct opp *opp = devfreq_recommended_opp(dev, _freq, flags);
628 unsigned long freq = opp_get_freq(opp); 630 unsigned long freq = opp_get_freq(opp);
631 unsigned long old_freq = opp_get_freq(data->curr_opp);
632
633 if (IS_ERR(opp))
634 return PTR_ERR(opp);
629 635
630 if (old_freq == freq) 636 if (old_freq == freq)
631 return 0; 637 return 0;
diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h
index 5862475d05f8..281c72a3b9d5 100644
--- a/include/linux/devfreq.h
+++ b/include/linux/devfreq.h
@@ -44,6 +44,14 @@ struct devfreq_dev_status {
44 void *private_data; 44 void *private_data;
45}; 45};
46 46
47/*
48 * The resulting frequency should be at most this. (this bound is the
49 * least upper bound; thus, the resulting freq should be lower or same)
50 * If the flag is not set, the resulting frequency should be at most the
51 * bound (greatest lower bound)
52 */
53#define DEVFREQ_FLAG_LEAST_UPPER_BOUND 0x1
54
47/** 55/**
48 * struct devfreq_dev_profile - Devfreq's user device profile 56 * struct devfreq_dev_profile - Devfreq's user device profile
49 * @initial_freq The operating frequency when devfreq_add_device() is 57 * @initial_freq The operating frequency when devfreq_add_device() is
@@ -54,6 +62,8 @@ struct devfreq_dev_status {
54 * higher than any operable frequency, set maximum. 62 * higher than any operable frequency, set maximum.
55 * Before returning, target function should set 63 * Before returning, target function should set
56 * freq at the current frequency. 64 * freq at the current frequency.
65 * The "flags" parameter's possible values are
66 * explained above with "DEVFREQ_FLAG_*" macros.
57 * @get_dev_status The device should provide the current performance 67 * @get_dev_status The device should provide the current performance
58 * status to devfreq, which is used by governors. 68 * status to devfreq, which is used by governors.
59 * @exit An optional callback that is called when devfreq 69 * @exit An optional callback that is called when devfreq
@@ -66,7 +76,7 @@ struct devfreq_dev_profile {
66 unsigned long initial_freq; 76 unsigned long initial_freq;
67 unsigned int polling_ms; 77 unsigned int polling_ms;
68 78
69 int (*target)(struct device *dev, unsigned long *freq); 79 int (*target)(struct device *dev, unsigned long *freq, u32 flags);
70 int (*get_dev_status)(struct device *dev, 80 int (*get_dev_status)(struct device *dev,
71 struct devfreq_dev_status *stat); 81 struct devfreq_dev_status *stat);
72 void (*exit)(struct device *dev); 82 void (*exit)(struct device *dev);
@@ -165,7 +175,7 @@ extern int devfreq_remove_device(struct devfreq *devfreq);
165 175
166/* Helper functions for devfreq user device driver with OPP. */ 176/* Helper functions for devfreq user device driver with OPP. */
167extern struct opp *devfreq_recommended_opp(struct device *dev, 177extern struct opp *devfreq_recommended_opp(struct device *dev,
168 unsigned long *freq); 178 unsigned long *freq, u32 flags);
169extern int devfreq_register_opp_notifier(struct device *dev, 179extern int devfreq_register_opp_notifier(struct device *dev,
170 struct devfreq *devfreq); 180 struct devfreq *devfreq);
171extern int devfreq_unregister_opp_notifier(struct device *dev, 181extern int devfreq_unregister_opp_notifier(struct device *dev,
@@ -216,7 +226,7 @@ static int devfreq_remove_device(struct devfreq *devfreq)
216} 226}
217 227
218static struct opp *devfreq_recommended_opp(struct device *dev, 228static struct opp *devfreq_recommended_opp(struct device *dev,
219 unsigned long *freq) 229 unsigned long *freq, u32 flags)
220{ 230{
221 return -EINVAL; 231 return -EINVAL;
222} 232}