Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/devfreq/governor_simpleondemand.c
26378 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
3
* linux/drivers/devfreq/governor_simpleondemand.c
4
*
5
* Copyright (C) 2011 Samsung Electronics
6
* MyungJoo Ham <[email protected]>
7
*/
8
9
#include <linux/errno.h>
10
#include <linux/module.h>
11
#include <linux/devfreq.h>
12
#include <linux/math64.h>
13
#include "governor.h"
14
15
/* Default constants for DevFreq-Simple-Ondemand (DFSO) */
16
#define DFSO_UPTHRESHOLD (90)
17
#define DFSO_DOWNDIFFERENCTIAL (5)
18
static int devfreq_simple_ondemand_func(struct devfreq *df,
19
unsigned long *freq)
20
{
21
int err;
22
struct devfreq_dev_status *stat;
23
unsigned long long a, b;
24
unsigned int dfso_upthreshold = DFSO_UPTHRESHOLD;
25
unsigned int dfso_downdifferential = DFSO_DOWNDIFFERENCTIAL;
26
struct devfreq_simple_ondemand_data *data = df->data;
27
28
err = devfreq_update_stats(df);
29
if (err)
30
return err;
31
32
stat = &df->last_status;
33
34
if (data) {
35
if (data->upthreshold)
36
dfso_upthreshold = data->upthreshold;
37
if (data->downdifferential)
38
dfso_downdifferential = data->downdifferential;
39
}
40
if (dfso_upthreshold > 100 ||
41
dfso_upthreshold < dfso_downdifferential)
42
return -EINVAL;
43
44
/* Assume MAX if it is going to be divided by zero */
45
if (stat->total_time == 0) {
46
*freq = DEVFREQ_MAX_FREQ;
47
return 0;
48
}
49
50
/* Prevent overflow */
51
if (stat->busy_time >= (1 << 24) || stat->total_time >= (1 << 24)) {
52
stat->busy_time >>= 7;
53
stat->total_time >>= 7;
54
}
55
56
/* Set MAX if it's busy enough */
57
if (stat->busy_time * 100 >
58
stat->total_time * dfso_upthreshold) {
59
*freq = DEVFREQ_MAX_FREQ;
60
return 0;
61
}
62
63
/* Set MAX if we do not know the initial frequency */
64
if (stat->current_frequency == 0) {
65
*freq = DEVFREQ_MAX_FREQ;
66
return 0;
67
}
68
69
/* Keep the current frequency */
70
if (stat->busy_time * 100 >
71
stat->total_time * (dfso_upthreshold - dfso_downdifferential)) {
72
*freq = stat->current_frequency;
73
return 0;
74
}
75
76
/* Set the desired frequency based on the load */
77
a = stat->busy_time;
78
a *= stat->current_frequency;
79
b = div_u64(a, stat->total_time);
80
b *= 100;
81
b = div_u64(b, (dfso_upthreshold - dfso_downdifferential / 2));
82
*freq = (unsigned long) b;
83
84
return 0;
85
}
86
87
static int devfreq_simple_ondemand_handler(struct devfreq *devfreq,
88
unsigned int event, void *data)
89
{
90
switch (event) {
91
case DEVFREQ_GOV_START:
92
devfreq_monitor_start(devfreq);
93
break;
94
95
case DEVFREQ_GOV_STOP:
96
devfreq_monitor_stop(devfreq);
97
break;
98
99
case DEVFREQ_GOV_UPDATE_INTERVAL:
100
devfreq_update_interval(devfreq, (unsigned int *)data);
101
break;
102
103
case DEVFREQ_GOV_SUSPEND:
104
devfreq_monitor_suspend(devfreq);
105
break;
106
107
case DEVFREQ_GOV_RESUME:
108
devfreq_monitor_resume(devfreq);
109
break;
110
111
default:
112
break;
113
}
114
115
return 0;
116
}
117
118
static struct devfreq_governor devfreq_simple_ondemand = {
119
.name = DEVFREQ_GOV_SIMPLE_ONDEMAND,
120
.attrs = DEVFREQ_GOV_ATTR_POLLING_INTERVAL
121
| DEVFREQ_GOV_ATTR_TIMER,
122
.get_target_freq = devfreq_simple_ondemand_func,
123
.event_handler = devfreq_simple_ondemand_handler,
124
};
125
126
static int __init devfreq_simple_ondemand_init(void)
127
{
128
return devfreq_add_governor(&devfreq_simple_ondemand);
129
}
130
subsys_initcall(devfreq_simple_ondemand_init);
131
132
static void __exit devfreq_simple_ondemand_exit(void)
133
{
134
int ret;
135
136
ret = devfreq_remove_governor(&devfreq_simple_ondemand);
137
if (ret)
138
pr_err("%s: failed remove governor %d\n", __func__, ret);
139
140
return;
141
}
142
module_exit(devfreq_simple_ondemand_exit);
143
MODULE_DESCRIPTION("DEVFREQ Simple On-demand governor");
144
MODULE_LICENSE("GPL");
145
146