1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright 2020 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
4 *
5 * Based on panfrost_devfreq.c:
6 * Copyright 2019 Collabora ltd.
7 */
8 #include <linux/clk.h>
9 #include <linux/devfreq.h>
10 #include <linux/devfreq_cooling.h>
11 #include <linux/device.h>
12 #include <linux/platform_device.h>
13 #include <linux/pm_opp.h>
14 #include <linux/property.h>
15
16 #include "lima_device.h"
17 #include "lima_devfreq.h"
18
lima_devfreq_update_utilization(struct lima_devfreq * devfreq)19 static void lima_devfreq_update_utilization(struct lima_devfreq *devfreq)
20 {
21 ktime_t now, last;
22
23 now = ktime_get();
24 last = devfreq->time_last_update;
25
26 if (devfreq->busy_count > 0)
27 devfreq->busy_time += ktime_sub(now, last);
28 else
29 devfreq->idle_time += ktime_sub(now, last);
30
31 devfreq->time_last_update = now;
32 }
33
lima_devfreq_target(struct device * dev,unsigned long * freq,u32 flags)34 static int lima_devfreq_target(struct device *dev, unsigned long *freq,
35 u32 flags)
36 {
37 struct dev_pm_opp *opp;
38 int err;
39
40 opp = devfreq_recommended_opp(dev, freq, flags);
41 if (IS_ERR(opp))
42 return PTR_ERR(opp);
43 dev_pm_opp_put(opp);
44
45 err = dev_pm_opp_set_rate(dev, *freq);
46 if (err)
47 return err;
48
49 return 0;
50 }
51
lima_devfreq_reset(struct lima_devfreq * devfreq)52 static void lima_devfreq_reset(struct lima_devfreq *devfreq)
53 {
54 devfreq->busy_time = 0;
55 devfreq->idle_time = 0;
56 devfreq->time_last_update = ktime_get();
57 }
58
lima_devfreq_get_dev_status(struct device * dev,struct devfreq_dev_status * status)59 static int lima_devfreq_get_dev_status(struct device *dev,
60 struct devfreq_dev_status *status)
61 {
62 struct lima_device *ldev = dev_get_drvdata(dev);
63 struct lima_devfreq *devfreq = &ldev->devfreq;
64 unsigned long irqflags;
65
66 status->current_frequency = clk_get_rate(ldev->clk_gpu);
67
68 spin_lock_irqsave(&devfreq->lock, irqflags);
69
70 lima_devfreq_update_utilization(devfreq);
71
72 status->total_time = ktime_to_ns(ktime_add(devfreq->busy_time,
73 devfreq->idle_time));
74 status->busy_time = ktime_to_ns(devfreq->busy_time);
75
76 lima_devfreq_reset(devfreq);
77
78 spin_unlock_irqrestore(&devfreq->lock, irqflags);
79
80 dev_dbg(ldev->dev, "busy %lu total %lu %lu %% freq %lu MHz\n",
81 status->busy_time, status->total_time,
82 status->busy_time / (status->total_time / 100),
83 status->current_frequency / 1000 / 1000);
84
85 return 0;
86 }
87
88 static struct devfreq_dev_profile lima_devfreq_profile = {
89 .polling_ms = 50, /* ~3 frames */
90 .target = lima_devfreq_target,
91 .get_dev_status = lima_devfreq_get_dev_status,
92 };
93
lima_devfreq_fini(struct lima_device * ldev)94 void lima_devfreq_fini(struct lima_device *ldev)
95 {
96 struct lima_devfreq *devfreq = &ldev->devfreq;
97
98 if (devfreq->cooling) {
99 devfreq_cooling_unregister(devfreq->cooling);
100 devfreq->cooling = NULL;
101 }
102
103 if (devfreq->devfreq) {
104 devm_devfreq_remove_device(ldev->dev, devfreq->devfreq);
105 devfreq->devfreq = NULL;
106 }
107
108 if (devfreq->opp_of_table_added) {
109 dev_pm_opp_of_remove_table(ldev->dev);
110 devfreq->opp_of_table_added = false;
111 }
112
113 if (devfreq->regulators_opp_table) {
114 dev_pm_opp_put_regulators(devfreq->regulators_opp_table);
115 devfreq->regulators_opp_table = NULL;
116 }
117
118 if (devfreq->clkname_opp_table) {
119 dev_pm_opp_put_clkname(devfreq->clkname_opp_table);
120 devfreq->clkname_opp_table = NULL;
121 }
122 }
123
lima_devfreq_init(struct lima_device * ldev)124 int lima_devfreq_init(struct lima_device *ldev)
125 {
126 struct thermal_cooling_device *cooling;
127 struct device *dev = ldev->dev;
128 struct opp_table *opp_table;
129 struct devfreq *devfreq;
130 struct lima_devfreq *ldevfreq = &ldev->devfreq;
131 struct dev_pm_opp *opp;
132 unsigned long cur_freq;
133 int ret;
134
135 if (!device_property_present(dev, "operating-points-v2"))
136 /* Optional, continue without devfreq */
137 return 0;
138
139 spin_lock_init(&ldevfreq->lock);
140
141 opp_table = dev_pm_opp_set_clkname(dev, "core");
142 if (IS_ERR(opp_table)) {
143 ret = PTR_ERR(opp_table);
144 goto err_fini;
145 }
146
147 ldevfreq->clkname_opp_table = opp_table;
148
149 opp_table = dev_pm_opp_set_regulators(dev,
150 (const char *[]){ "mali" },
151 1);
152 if (IS_ERR(opp_table)) {
153 ret = PTR_ERR(opp_table);
154
155 /* Continue if the optional regulator is missing */
156 if (ret != -ENODEV)
157 goto err_fini;
158 } else {
159 ldevfreq->regulators_opp_table = opp_table;
160 }
161
162 ret = dev_pm_opp_of_add_table(dev);
163 if (ret)
164 goto err_fini;
165 ldevfreq->opp_of_table_added = true;
166
167 lima_devfreq_reset(ldevfreq);
168
169 cur_freq = clk_get_rate(ldev->clk_gpu);
170
171 opp = devfreq_recommended_opp(dev, &cur_freq, 0);
172 if (IS_ERR(opp)) {
173 ret = PTR_ERR(opp);
174 goto err_fini;
175 }
176
177 lima_devfreq_profile.initial_freq = cur_freq;
178 dev_pm_opp_put(opp);
179
180 devfreq = devm_devfreq_add_device(dev, &lima_devfreq_profile,
181 DEVFREQ_GOV_SIMPLE_ONDEMAND, NULL);
182 if (IS_ERR(devfreq)) {
183 dev_err(dev, "Couldn't initialize GPU devfreq\n");
184 ret = PTR_ERR(devfreq);
185 goto err_fini;
186 }
187
188 ldevfreq->devfreq = devfreq;
189
190 cooling = of_devfreq_cooling_register(dev->of_node, devfreq);
191 if (IS_ERR(cooling))
192 dev_info(dev, "Failed to register cooling device\n");
193 else
194 ldevfreq->cooling = cooling;
195
196 return 0;
197
198 err_fini:
199 lima_devfreq_fini(ldev);
200 return ret;
201 }
202
lima_devfreq_record_busy(struct lima_devfreq * devfreq)203 void lima_devfreq_record_busy(struct lima_devfreq *devfreq)
204 {
205 unsigned long irqflags;
206
207 if (!devfreq->devfreq)
208 return;
209
210 spin_lock_irqsave(&devfreq->lock, irqflags);
211
212 lima_devfreq_update_utilization(devfreq);
213
214 devfreq->busy_count++;
215
216 spin_unlock_irqrestore(&devfreq->lock, irqflags);
217 }
218
lima_devfreq_record_idle(struct lima_devfreq * devfreq)219 void lima_devfreq_record_idle(struct lima_devfreq *devfreq)
220 {
221 unsigned long irqflags;
222
223 if (!devfreq->devfreq)
224 return;
225
226 spin_lock_irqsave(&devfreq->lock, irqflags);
227
228 lima_devfreq_update_utilization(devfreq);
229
230 WARN_ON(--devfreq->busy_count < 0);
231
232 spin_unlock_irqrestore(&devfreq->lock, irqflags);
233 }
234
lima_devfreq_resume(struct lima_devfreq * devfreq)235 int lima_devfreq_resume(struct lima_devfreq *devfreq)
236 {
237 unsigned long irqflags;
238
239 if (!devfreq->devfreq)
240 return 0;
241
242 spin_lock_irqsave(&devfreq->lock, irqflags);
243
244 lima_devfreq_reset(devfreq);
245
246 spin_unlock_irqrestore(&devfreq->lock, irqflags);
247
248 return devfreq_resume_device(devfreq->devfreq);
249 }
250
lima_devfreq_suspend(struct lima_devfreq * devfreq)251 int lima_devfreq_suspend(struct lima_devfreq *devfreq)
252 {
253 if (!devfreq->devfreq)
254 return 0;
255
256 return devfreq_suspend_device(devfreq->devfreq);
257 }
258