blob: 855941ef5f955f1a1ddbbe77baf66fc39ec2aa19 [file] [log] [blame]
b.liue9582032025-04-17 19:18:16 +08001/*
2 * linux/drivers/devfreq/governor_throughput.c
3 *
4 * Copyright (C) 2013 Marvell
5 * Leo Song <liangs@marvell.com>
6 * Qiming Wu <wuqm@marvell.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 */
12
13#include <linux/errno.h>
14#include <linux/module.h>
15#include <linux/devfreq.h>
16#include <linux/math64.h>
17#include "governor.h"
18
19
20/* Default constants for DevFreq-Throughput */
21#define DFSO_UPTHRESHOLD (90)
22#define DFSO_DOWNDIFFERENCTIAL (5)
23#define DFSO_MAX_UPTHRESHOLD (100)
24#define MAX_TABLE_ITEM (20)
25
26static int devfreq_throughput_func(struct devfreq *df, unsigned long *freq)
27{
28 struct devfreq_throughput_data *data = df->data;
29 struct throughput_threshold *throughput_table = data->throughput_table;
30 u32 *freq_table = data->freq_table;
31 u32 table_len = data->table_len;
32 struct devfreq_dev_status stat;
33 int i;
34 int err;
35
36 /* only 1 freq point can not use this governor */
37 if ((data->table_len <= 1) || (data->table_len > MAX_TABLE_ITEM)) {
38 dev_err(&df->dev, "freq table too small or too big!\n");
39 return -EINVAL;
40 }
41
42 err = df->profile->get_dev_status(df->dev.parent, &stat);
43 if (err) {
44 /* Used to represent ignoring the profiling result */
45 if (err == -EINVAL) {
46 *freq = stat.current_frequency;
47 return 0;
48 } else
49 return err;
50 }
51
52 /*
53 * make sure get_dev_status() has updated the current_frequency;
54 * at least set *freq to a reansonable values
55 */
56 *freq = stat.current_frequency;
57
58 /*
59 * throughput == -1 is set by low level driver deliberately, to inform
60 * the governor that the throughput data may not correct this time.
61 * This is not fatal error, just end this time's caculation, and do it
62 * next time.
63 */
64 if (stat.throughput == -1) {
65 dev_dbg(&df->dev, "throughput is not suitable!\n");
66 return -EAGAIN;
67 } else if (stat.throughput < -1) {
68 dev_err(&df->dev, "fatal: throughput is not as expected!\n");
69 return -EINVAL;
70 }
71
72 if ((stat.throughput >= 0)
73 && (stat.throughput < throughput_table[0].down)) {
74 /* set to lowest freq */
75 *freq = freq_table[0];
76 } else if (stat.throughput > throughput_table[table_len - 2].up) {
77 /* set to highest freq */
78 *freq = freq_table[table_len - 1];
79 } else {
80 for (i = 0; i <= (table_len - 2); i++) {
81 if ((stat.throughput >= throughput_table[i].down)
82 && (stat.throughput <= throughput_table[i].up)) {
83 /*
84 * If cur_speed is between UP and DOWN
85 * keep freq unchanged in most cases
86 */
87 *freq = stat.current_frequency;
88 /* Handle corner case */
89 if ((*freq != freq_table[i])
90 && (*freq != freq_table[i + 1])) {
91 /* also can choose freq_table[i + 1] */
92 *freq = freq_table[i];
93 }
94 break;
95 } else if ((stat.throughput > throughput_table[i].up)
96 && (stat.throughput < throughput_table[i + 1].down)) {
97 *freq = freq_table[i + 1];
98 break;
99 }
100 }
101 }
102
103 if (df->min_freq && *freq < df->min_freq)
104 *freq = df->min_freq;
105 if (df->max_freq && *freq > df->max_freq)
106 *freq = df->max_freq;
107
108 dev_dbg(&df->dev, "speed=%d, *freq=%lu\n", stat.throughput, *freq);
109
110 return 0;
111}
112
113static int devfreq_throughput_handler(struct devfreq *devfreq,
114 unsigned int event, void *data)
115{
116 switch (event) {
117 case DEVFREQ_GOV_START:
118 devfreq_monitor_start(devfreq);
119 break;
120
121 case DEVFREQ_GOV_STOP:
122 devfreq_monitor_stop(devfreq);
123 break;
124
125 case DEVFREQ_GOV_INTERVAL:
126 devfreq_interval_update(devfreq, (unsigned int *)data);
127 break;
128
129 case DEVFREQ_GOV_SUSPEND:
130 devfreq_monitor_suspend(devfreq);
131 break;
132
133 case DEVFREQ_GOV_RESUME:
134 devfreq_monitor_resume(devfreq);
135 break;
136
137 default:
138 break;
139 }
140
141 return 0;
142}
143
144static struct devfreq_governor devfreq_throughput = {
145 .name = "throughput",
146 .get_target_freq = devfreq_throughput_func,
147 .event_handler = devfreq_throughput_handler,
148};
149
150static int __init devfreq_throughput_init(void)
151{
152 return devfreq_add_governor(&devfreq_throughput);
153}
154subsys_initcall(devfreq_throughput_init);
155
156static void __exit devfreq_throughput_exit(void)
157{
158 int ret;
159
160 ret = devfreq_remove_governor(&devfreq_throughput);
161 if (ret)
162 pr_err("%s: failed remove governor %d\n", __func__, ret);
163
164 return;
165}
166module_exit(devfreq_throughput_exit);
167MODULE_LICENSE("GPL");