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