xref: /OK3568_Linux_fs/kernel/drivers/gpu/drm/lima/lima_devfreq.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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