9ef02d0ea35d693b0e75596b4a12a2da55c058a6
[openwrt/staging/blogic.git] /
1 From 26984d9d581e5049bd75091d2e789b9cc3ea12e0 Mon Sep 17 00:00:00 2001
2 From: Chanwoo Choi <cw00.choi@samsung.com>
3 Date: Wed, 27 Apr 2022 03:49:19 +0900
4 Subject: [PATCH 4/5] PM / devfreq: passive: Keep cpufreq_policy for possible
5 cpus
6
7 The passive governor requires the cpu data to get the next target frequency
8 of devfreq device if depending on cpu. In order to reduce the unnecessary
9 memory data, keep cpufreq_policy data for possible cpus instead of NR_CPU.
10
11 Tested-by: Chen-Yu Tsai <wenst@chromium.org>
12 Tested-by: Johnson Wang <johnson.wang@mediatek.com>
13 Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
14 ---
15 drivers/devfreq/governor.h | 3 ++
16 drivers/devfreq/governor_passive.c | 75 +++++++++++++++++++++++-------
17 include/linux/devfreq.h | 4 +-
18 3 files changed, 64 insertions(+), 18 deletions(-)
19
20 --- a/drivers/devfreq/governor.h
21 +++ b/drivers/devfreq/governor.h
22 @@ -49,6 +49,7 @@
23
24 /**
25 * struct devfreq_cpu_data - Hold the per-cpu data
26 + * @node: list node
27 * @dev: reference to cpu device.
28 * @first_cpu: the cpumask of the first cpu of a policy.
29 * @opp_table: reference to cpu opp table.
30 @@ -60,6 +61,8 @@
31 * This is auto-populated by the governor.
32 */
33 struct devfreq_cpu_data {
34 + struct list_head node;
35 +
36 struct device *dev;
37 unsigned int first_cpu;
38
39 --- a/drivers/devfreq/governor_passive.c
40 +++ b/drivers/devfreq/governor_passive.c
41 @@ -1,4 +1,4 @@
42 -// SPDX-License-Identifier: GPL-2.0-only
43 + // SPDX-License-Identifier: GPL-2.0-only
44 /*
45 * linux/drivers/devfreq/governor_passive.c
46 *
47 @@ -18,6 +18,22 @@
48
49 #define HZ_PER_KHZ 1000
50
51 +static struct devfreq_cpu_data *
52 +get_parent_cpu_data(struct devfreq_passive_data *p_data,
53 + struct cpufreq_policy *policy)
54 +{
55 + struct devfreq_cpu_data *parent_cpu_data;
56 +
57 + if (!p_data || !policy)
58 + return NULL;
59 +
60 + list_for_each_entry(parent_cpu_data, &p_data->cpu_data_list, node)
61 + if (parent_cpu_data->first_cpu == cpumask_first(policy->related_cpus))
62 + return parent_cpu_data;
63 +
64 + return NULL;
65 +}
66 +
67 static unsigned long get_target_freq_by_required_opp(struct device *p_dev,
68 struct opp_table *p_opp_table,
69 struct opp_table *opp_table,
70 @@ -51,14 +67,24 @@ static int get_target_freq_with_cpufreq(
71 struct devfreq_passive_data *p_data =
72 (struct devfreq_passive_data *)devfreq->data;
73 struct devfreq_cpu_data *parent_cpu_data;
74 + struct cpufreq_policy *policy;
75 unsigned long cpu, cpu_cur, cpu_min, cpu_max, cpu_percent;
76 unsigned long dev_min, dev_max;
77 unsigned long freq = 0;
78 + int ret = 0;
79
80 for_each_online_cpu(cpu) {
81 - parent_cpu_data = p_data->parent_cpu_data[cpu];
82 - if (!parent_cpu_data || parent_cpu_data->first_cpu != cpu)
83 + policy = cpufreq_cpu_get(cpu);
84 + if (!policy) {
85 + ret = -EINVAL;
86 + continue;
87 + }
88 +
89 + parent_cpu_data = get_parent_cpu_data(p_data, policy);
90 + if (!parent_cpu_data) {
91 + cpufreq_cpu_put(policy);
92 continue;
93 + }
94
95 /* Get target freq via required opps */
96 cpu_cur = parent_cpu_data->cur_freq * HZ_PER_KHZ;
97 @@ -67,6 +93,7 @@ static int get_target_freq_with_cpufreq(
98 devfreq->opp_table, &cpu_cur);
99 if (freq) {
100 *target_freq = max(freq, *target_freq);
101 + cpufreq_cpu_put(policy);
102 continue;
103 }
104
105 @@ -81,9 +108,10 @@ static int get_target_freq_with_cpufreq(
106 freq = dev_min + mult_frac(dev_max - dev_min, cpu_percent, 100);
107
108 *target_freq = max(freq, *target_freq);
109 + cpufreq_cpu_put(policy);
110 }
111
112 - return 0;
113 + return ret;
114 }
115
116 static int get_target_freq_with_devfreq(struct devfreq *devfreq,
117 @@ -168,12 +196,11 @@ static int cpufreq_passive_notifier_call
118 unsigned int cur_freq;
119 int ret;
120
121 - if (event != CPUFREQ_POSTCHANGE || !freqs ||
122 - !p_data->parent_cpu_data[freqs->policy->cpu])
123 + if (event != CPUFREQ_POSTCHANGE || !freqs)
124 return 0;
125
126 - parent_cpu_data = p_data->parent_cpu_data[freqs->policy->cpu];
127 - if (parent_cpu_data->cur_freq == freqs->new)
128 + parent_cpu_data = get_parent_cpu_data(p_data, freqs->policy);
129 + if (!parent_cpu_data || parent_cpu_data->cur_freq == freqs->new)
130 return 0;
131
132 cur_freq = parent_cpu_data->cur_freq;
133 @@ -196,7 +223,7 @@ static int cpufreq_passive_unregister_no
134 struct devfreq_passive_data *p_data
135 = (struct devfreq_passive_data *)devfreq->data;
136 struct devfreq_cpu_data *parent_cpu_data;
137 - int cpu, ret;
138 + int cpu, ret = 0;
139
140 if (p_data->nb.notifier_call) {
141 ret = cpufreq_unregister_notifier(&p_data->nb,
142 @@ -206,16 +233,26 @@ static int cpufreq_passive_unregister_no
143 }
144
145 for_each_possible_cpu(cpu) {
146 - parent_cpu_data = p_data->parent_cpu_data[cpu];
147 - if (!parent_cpu_data)
148 + struct cpufreq_policy *policy = cpufreq_cpu_get(cpu);
149 + if (!policy) {
150 + ret = -EINVAL;
151 + continue;
152 + }
153 +
154 + parent_cpu_data = get_parent_cpu_data(p_data, policy);
155 + if (!parent_cpu_data) {
156 + cpufreq_cpu_put(policy);
157 continue;
158 + }
159
160 + list_del(&parent_cpu_data->node);
161 if (parent_cpu_data->opp_table)
162 dev_pm_opp_put_opp_table(parent_cpu_data->opp_table);
163 kfree(parent_cpu_data);
164 + cpufreq_cpu_put(policy);
165 }
166
167 - return 0;
168 + return ret;
169 }
170
171 static int cpufreq_passive_register_notifier(struct devfreq *devfreq)
172 @@ -230,6 +267,9 @@ static int cpufreq_passive_register_noti
173 unsigned int cpu;
174 int ret;
175
176 + p_data->cpu_data_list
177 + = (struct list_head)LIST_HEAD_INIT(p_data->cpu_data_list);
178 +
179 p_data->nb.notifier_call = cpufreq_passive_notifier_call;
180 ret = cpufreq_register_notifier(&p_data->nb, CPUFREQ_TRANSITION_NOTIFIER);
181 if (ret) {
182 @@ -239,15 +279,18 @@ static int cpufreq_passive_register_noti
183 }
184
185 for_each_possible_cpu(cpu) {
186 - if (p_data->parent_cpu_data[cpu])
187 - continue;
188 -
189 policy = cpufreq_cpu_get(cpu);
190 if (!policy) {
191 ret = -EPROBE_DEFER;
192 goto err;
193 }
194
195 + parent_cpu_data = get_parent_cpu_data(p_data, policy);
196 + if (parent_cpu_data) {
197 + cpufreq_cpu_put(policy);
198 + continue;
199 + }
200 +
201 parent_cpu_data = kzalloc(sizeof(*parent_cpu_data),
202 GFP_KERNEL);
203 if (!parent_cpu_data) {
204 @@ -276,7 +319,7 @@ static int cpufreq_passive_register_noti
205 parent_cpu_data->min_freq = policy->cpuinfo.min_freq;
206 parent_cpu_data->max_freq = policy->cpuinfo.max_freq;
207
208 - p_data->parent_cpu_data[cpu] = parent_cpu_data;
209 + list_add_tail(&parent_cpu_data->node, &p_data->cpu_data_list);
210 cpufreq_cpu_put(policy);
211 }
212
213 --- a/include/linux/devfreq.h
214 +++ b/include/linux/devfreq.h
215 @@ -310,7 +310,7 @@ enum devfreq_parent_dev_type {
216 * @this: the devfreq instance of own device.
217 * @nb: the notifier block for DEVFREQ_TRANSITION_NOTIFIER or
218 * CPUFREQ_TRANSITION_NOTIFIER list.
219 - * @parent_cpu_data: the state min/max/current frequency of all online cpu's.
220 + * @cpu_data_list: the list of cpu frequency data for all cpufreq_policy.
221 *
222 * The devfreq_passive_data have to set the devfreq instance of parent
223 * device with governors except for the passive governor. But, don't need to
224 @@ -330,7 +330,7 @@ struct devfreq_passive_data {
225 /* For passive governor's internal use. Don't need to set them */
226 struct devfreq *this;
227 struct notifier_block nb;
228 - struct devfreq_cpu_data *parent_cpu_data[NR_CPUS];
229 + struct list_head cpu_data_list;
230 };
231 #endif
232