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