diff options
Diffstat (limited to 'drivers/devfreq/devfreq.c')
-rw-r--r-- | drivers/devfreq/devfreq.c | 112 |
1 files changed, 108 insertions, 4 deletions
diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index c189b82f5ece..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 | ||
@@ -501,12 +519,82 @@ static ssize_t show_central_polling(struct device *dev, | |||
501 | !to_devfreq(dev)->governor->no_central_polling); | 519 | !to_devfreq(dev)->governor->no_central_polling); |
502 | } | 520 | } |
503 | 521 | ||
522 | static ssize_t store_min_freq(struct device *dev, struct device_attribute *attr, | ||
523 | const char *buf, size_t count) | ||
524 | { | ||
525 | struct devfreq *df = to_devfreq(dev); | ||
526 | unsigned long value; | ||
527 | int ret; | ||
528 | unsigned long max; | ||
529 | |||
530 | ret = sscanf(buf, "%lu", &value); | ||
531 | if (ret != 1) | ||
532 | goto out; | ||
533 | |||
534 | mutex_lock(&df->lock); | ||
535 | max = df->max_freq; | ||
536 | if (value && max && value > max) { | ||
537 | ret = -EINVAL; | ||
538 | goto unlock; | ||
539 | } | ||
540 | |||
541 | df->min_freq = value; | ||
542 | update_devfreq(df); | ||
543 | ret = count; | ||
544 | unlock: | ||
545 | mutex_unlock(&df->lock); | ||
546 | out: | ||
547 | return ret; | ||
548 | } | ||
549 | |||
550 | static ssize_t show_min_freq(struct device *dev, struct device_attribute *attr, | ||
551 | char *buf) | ||
552 | { | ||
553 | return sprintf(buf, "%lu\n", to_devfreq(dev)->min_freq); | ||
554 | } | ||
555 | |||
556 | static ssize_t store_max_freq(struct device *dev, struct device_attribute *attr, | ||
557 | const char *buf, size_t count) | ||
558 | { | ||
559 | struct devfreq *df = to_devfreq(dev); | ||
560 | unsigned long value; | ||
561 | int ret; | ||
562 | unsigned long min; | ||
563 | |||
564 | ret = sscanf(buf, "%lu", &value); | ||
565 | if (ret != 1) | ||
566 | goto out; | ||
567 | |||
568 | mutex_lock(&df->lock); | ||
569 | min = df->min_freq; | ||
570 | if (value && min && value < min) { | ||
571 | ret = -EINVAL; | ||
572 | goto unlock; | ||
573 | } | ||
574 | |||
575 | df->max_freq = value; | ||
576 | update_devfreq(df); | ||
577 | ret = count; | ||
578 | unlock: | ||
579 | mutex_unlock(&df->lock); | ||
580 | out: | ||
581 | return ret; | ||
582 | } | ||
583 | |||
584 | static ssize_t show_max_freq(struct device *dev, struct device_attribute *attr, | ||
585 | char *buf) | ||
586 | { | ||
587 | return sprintf(buf, "%lu\n", to_devfreq(dev)->max_freq); | ||
588 | } | ||
589 | |||
504 | static struct device_attribute devfreq_attrs[] = { | 590 | static struct device_attribute devfreq_attrs[] = { |
505 | __ATTR(governor, S_IRUGO, show_governor, NULL), | 591 | __ATTR(governor, S_IRUGO, show_governor, NULL), |
506 | __ATTR(cur_freq, S_IRUGO, show_freq, NULL), | 592 | __ATTR(cur_freq, S_IRUGO, show_freq, NULL), |
507 | __ATTR(central_polling, S_IRUGO, show_central_polling, NULL), | 593 | __ATTR(central_polling, S_IRUGO, show_central_polling, NULL), |
508 | __ATTR(polling_interval, S_IRUGO | S_IWUSR, show_polling_interval, | 594 | __ATTR(polling_interval, S_IRUGO | S_IWUSR, show_polling_interval, |
509 | store_polling_interval), | 595 | store_polling_interval), |
596 | __ATTR(min_freq, S_IRUGO | S_IWUSR, show_min_freq, store_min_freq), | ||
597 | __ATTR(max_freq, S_IRUGO | S_IWUSR, show_max_freq, store_max_freq), | ||
510 | { }, | 598 | { }, |
511 | }; | 599 | }; |
512 | 600 | ||
@@ -555,14 +643,30 @@ module_exit(devfreq_exit); | |||
555 | * freq value given to target callback. | 643 | * freq value given to target callback. |
556 | * @dev The devfreq user device. (parent of devfreq) | 644 | * @dev The devfreq user device. (parent of devfreq) |
557 | * @freq The frequency given to target function | 645 | * @freq The frequency given to target function |
646 | * @flags Flags handed from devfreq framework. | ||
558 | * | 647 | * |
559 | */ | 648 | */ |
560 | struct opp *devfreq_recommended_opp(struct device *dev, unsigned long *freq) | 649 | struct opp *devfreq_recommended_opp(struct device *dev, unsigned long *freq, |
650 | u32 flags) | ||
561 | { | 651 | { |
562 | struct opp *opp = opp_find_freq_ceil(dev, freq); | 652 | struct opp *opp; |
563 | 653 | ||
564 | if (opp == ERR_PTR(-ENODEV)) | 654 | if (flags & DEVFREQ_FLAG_LEAST_UPPER_BOUND) { |
655 | /* The freq is an upper bound. opp should be lower */ | ||
565 | 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 | |||
566 | return opp; | 670 | return opp; |
567 | } | 671 | } |
568 | 672 | ||