xref: /openbmc/linux/drivers/gpu/drm/panfrost/panfrost_devfreq.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
1f3ba9122SRob Herring // SPDX-License-Identifier: GPL-2.0
2f3ba9122SRob Herring /* Copyright 2019 Collabora ltd. */
39713e942SClément Péron 
49713e942SClément Péron #include <linux/clk.h>
5f3ba9122SRob Herring #include <linux/devfreq.h>
6bc1152b0SRobin Murphy #include <linux/devfreq_cooling.h>
7*7d690f93SAngeloGioacchino Del Regno #include <linux/nvmem-consumer.h>
8f3ba9122SRob Herring #include <linux/platform_device.h>
9f3ba9122SRob Herring #include <linux/pm_opp.h>
10f3ba9122SRob Herring 
11f3ba9122SRob Herring #include "panfrost_device.h"
1282c81085SSteven Price #include "panfrost_devfreq.h"
13f3ba9122SRob Herring 
panfrost_devfreq_update_utilization(struct panfrost_devfreq * pfdevfreq)149bfacfc8SClément Péron static void panfrost_devfreq_update_utilization(struct panfrost_devfreq *pfdevfreq)
15862cc626SClément Péron {
16ed85df3fSClément Péron 	ktime_t now, last;
17862cc626SClément Péron 
18862cc626SClément Péron 	now = ktime_get();
199bfacfc8SClément Péron 	last = pfdevfreq->time_last_update;
20862cc626SClément Péron 
21ed85df3fSClément Péron 	if (pfdevfreq->busy_count > 0)
229bfacfc8SClément Péron 		pfdevfreq->busy_time += ktime_sub(now, last);
23862cc626SClément Péron 	else
249bfacfc8SClément Péron 		pfdevfreq->idle_time += ktime_sub(now, last);
25862cc626SClément Péron 
269bfacfc8SClément Péron 	pfdevfreq->time_last_update = now;
27862cc626SClément Péron }
28f3ba9122SRob Herring 
panfrost_devfreq_target(struct device * dev,unsigned long * freq,u32 flags)29f3ba9122SRob Herring static int panfrost_devfreq_target(struct device *dev, unsigned long *freq,
30f3ba9122SRob Herring 				   u32 flags)
31f3ba9122SRob Herring {
3222bd4df9SSteven Price 	struct dev_pm_opp *opp;
33f3ba9122SRob Herring 
3422bd4df9SSteven Price 	opp = devfreq_recommended_opp(dev, freq, flags);
3522bd4df9SSteven Price 	if (IS_ERR(opp))
3622bd4df9SSteven Price 		return PTR_ERR(opp);
3722bd4df9SSteven Price 	dev_pm_opp_put(opp);
3822bd4df9SSteven Price 
390c503659SQinglang Miao 	return dev_pm_opp_set_rate(dev, *freq);
40f3ba9122SRob Herring }
41f3ba9122SRob Herring 
panfrost_devfreq_reset(struct panfrost_devfreq * pfdevfreq)429bfacfc8SClément Péron static void panfrost_devfreq_reset(struct panfrost_devfreq *pfdevfreq)
43f3ba9122SRob Herring {
449bfacfc8SClément Péron 	pfdevfreq->busy_time = 0;
459bfacfc8SClément Péron 	pfdevfreq->idle_time = 0;
469bfacfc8SClément Péron 	pfdevfreq->time_last_update = ktime_get();
47f3ba9122SRob Herring }
48f3ba9122SRob Herring 
panfrost_devfreq_get_dev_status(struct device * dev,struct devfreq_dev_status * status)49f3ba9122SRob Herring static int panfrost_devfreq_get_dev_status(struct device *dev,
50f3ba9122SRob Herring 					   struct devfreq_dev_status *status)
51f3ba9122SRob Herring {
52221bc779SSteven Price 	struct panfrost_device *pfdev = dev_get_drvdata(dev);
539bfacfc8SClément Péron 	struct panfrost_devfreq *pfdevfreq = &pfdev->pfdevfreq;
54ed85df3fSClément Péron 	unsigned long irqflags;
55ed85df3fSClément Péron 
56ed85df3fSClément Péron 	status->current_frequency = clk_get_rate(pfdev->clock);
57ed85df3fSClément Péron 
58ed85df3fSClément Péron 	spin_lock_irqsave(&pfdevfreq->lock, irqflags);
59f3ba9122SRob Herring 
609bfacfc8SClément Péron 	panfrost_devfreq_update_utilization(pfdevfreq);
61f3ba9122SRob Herring 
629bfacfc8SClément Péron 	status->total_time = ktime_to_ns(ktime_add(pfdevfreq->busy_time,
639bfacfc8SClément Péron 						   pfdevfreq->idle_time));
64f3ba9122SRob Herring 
659bfacfc8SClément Péron 	status->busy_time = ktime_to_ns(pfdevfreq->busy_time);
66f3ba9122SRob Herring 
679bfacfc8SClément Péron 	panfrost_devfreq_reset(pfdevfreq);
68f3ba9122SRob Herring 
69ed85df3fSClément Péron 	spin_unlock_irqrestore(&pfdevfreq->lock, irqflags);
70ed85df3fSClément Péron 
719bfacfc8SClément Péron 	dev_dbg(pfdev->dev, "busy %lu total %lu %lu %% freq %lu MHz\n",
729bfacfc8SClément Péron 		status->busy_time, status->total_time,
73f3ba9122SRob Herring 		status->busy_time / (status->total_time / 100),
74f3ba9122SRob Herring 		status->current_frequency / 1000 / 1000);
75f3ba9122SRob Herring 
76f3ba9122SRob Herring 	return 0;
77f3ba9122SRob Herring }
78f3ba9122SRob Herring 
79f3ba9122SRob Herring static struct devfreq_dev_profile panfrost_devfreq_profile = {
8042dceab0SLukasz Luba 	.timer = DEVFREQ_TIMER_DELAYED,
81f3ba9122SRob Herring 	.polling_ms = 50, /* ~3 frames */
82f3ba9122SRob Herring 	.target = panfrost_devfreq_target,
83f3ba9122SRob Herring 	.get_dev_status = panfrost_devfreq_get_dev_status,
84f3ba9122SRob Herring };
85f3ba9122SRob Herring 
panfrost_read_speedbin(struct device * dev)86*7d690f93SAngeloGioacchino Del Regno static int panfrost_read_speedbin(struct device *dev)
87*7d690f93SAngeloGioacchino Del Regno {
88*7d690f93SAngeloGioacchino Del Regno 	u32 val;
89*7d690f93SAngeloGioacchino Del Regno 	int ret;
90*7d690f93SAngeloGioacchino Del Regno 
91*7d690f93SAngeloGioacchino Del Regno 	ret = nvmem_cell_read_variable_le_u32(dev, "speed-bin", &val);
92*7d690f93SAngeloGioacchino Del Regno 	if (ret) {
93*7d690f93SAngeloGioacchino Del Regno 		/*
94*7d690f93SAngeloGioacchino Del Regno 		 * -ENOENT means that this platform doesn't support speedbins
95*7d690f93SAngeloGioacchino Del Regno 		 * as it didn't declare any speed-bin nvmem: in this case, we
96*7d690f93SAngeloGioacchino Del Regno 		 * keep going without it; any other error means that we are
97*7d690f93SAngeloGioacchino Del Regno 		 * supposed to read the bin value, but we failed doing so.
98*7d690f93SAngeloGioacchino Del Regno 		 */
99*7d690f93SAngeloGioacchino Del Regno 		if (ret != -ENOENT && ret != -EOPNOTSUPP) {
100*7d690f93SAngeloGioacchino Del Regno 			DRM_DEV_ERROR(dev, "Cannot read speed-bin (%d).", ret);
101*7d690f93SAngeloGioacchino Del Regno 			return ret;
102*7d690f93SAngeloGioacchino Del Regno 		}
103*7d690f93SAngeloGioacchino Del Regno 
104*7d690f93SAngeloGioacchino Del Regno 		return 0;
105*7d690f93SAngeloGioacchino Del Regno 	}
106*7d690f93SAngeloGioacchino Del Regno 	DRM_DEV_DEBUG(dev, "Using speed-bin = 0x%x\n", val);
107*7d690f93SAngeloGioacchino Del Regno 
108*7d690f93SAngeloGioacchino Del Regno 	return devm_pm_opp_set_supported_hw(dev, &val, 1);
109*7d690f93SAngeloGioacchino Del Regno }
110*7d690f93SAngeloGioacchino Del Regno 
panfrost_devfreq_init(struct panfrost_device * pfdev)111f3ba9122SRob Herring int panfrost_devfreq_init(struct panfrost_device *pfdev)
112f3ba9122SRob Herring {
113f3ba9122SRob Herring 	int ret;
114f3ba9122SRob Herring 	struct dev_pm_opp *opp;
115221bc779SSteven Price 	unsigned long cur_freq;
116bc1152b0SRobin Murphy 	struct device *dev = &pfdev->pdev->dev;
117bc1152b0SRobin Murphy 	struct devfreq *devfreq;
118bc1152b0SRobin Murphy 	struct thermal_cooling_device *cooling;
1199bfacfc8SClément Péron 	struct panfrost_devfreq *pfdevfreq = &pfdev->pfdevfreq;
120f3ba9122SRob Herring 
12109da3191SNicolas Boichat 	if (pfdev->comp->num_supplies > 1) {
12209da3191SNicolas Boichat 		/*
12309da3191SNicolas Boichat 		 * GPUs with more than 1 supply require platform-specific handling:
12409da3191SNicolas Boichat 		 * continue without devfreq
12509da3191SNicolas Boichat 		 */
12609da3191SNicolas Boichat 		DRM_DEV_INFO(dev, "More than 1 supply is not supported yet\n");
12709da3191SNicolas Boichat 		return 0;
12809da3191SNicolas Boichat 	}
12909da3191SNicolas Boichat 
130*7d690f93SAngeloGioacchino Del Regno 	ret = panfrost_read_speedbin(dev);
131*7d690f93SAngeloGioacchino Del Regno 	if (ret)
132*7d690f93SAngeloGioacchino Del Regno 		return ret;
133*7d690f93SAngeloGioacchino Del Regno 
13487686cc8SViresh Kumar 	ret = devm_pm_opp_set_regulators(dev, pfdev->comp->supply_names);
1356d8a154fSYangtao Li 	if (ret) {
136fd587ff0SClément Péron 		/* Continue if the optional regulator is missing */
137fd587ff0SClément Péron 		if (ret != -ENODEV) {
1388626e63eSChris Morgan 			if (ret != -EPROBE_DEFER)
139fd587ff0SClément Péron 				DRM_DEV_ERROR(dev, "Couldn't set OPP regulators\n");
1406d8a154fSYangtao Li 			return ret;
141fd587ff0SClément Péron 		}
142fd587ff0SClément Péron 	}
143fd587ff0SClément Péron 
1446d8a154fSYangtao Li 	ret = devm_pm_opp_of_add_table(dev);
145fd587ff0SClément Péron 	if (ret) {
146fd587ff0SClément Péron 		/* Optional, continue without devfreq */
147fd587ff0SClément Péron 		if (ret == -ENODEV)
148fd587ff0SClément Péron 			ret = 0;
1496d8a154fSYangtao Li 		return ret;
150fd587ff0SClément Péron 	}
15181f2fbe6SClément Péron 	pfdevfreq->opp_of_table_added = true;
152f3ba9122SRob Herring 
153ed85df3fSClément Péron 	spin_lock_init(&pfdevfreq->lock);
154ed85df3fSClément Péron 
1559bfacfc8SClément Péron 	panfrost_devfreq_reset(pfdevfreq);
156f3ba9122SRob Herring 
157221bc779SSteven Price 	cur_freq = clk_get_rate(pfdev->clock);
158f3ba9122SRob Herring 
159bc1152b0SRobin Murphy 	opp = devfreq_recommended_opp(dev, &cur_freq, 0);
1606d8a154fSYangtao Li 	if (IS_ERR(opp))
1616d8a154fSYangtao Li 		return PTR_ERR(opp);
162f3ba9122SRob Herring 
163221bc779SSteven Price 	panfrost_devfreq_profile.initial_freq = cur_freq;
164d76034a4SClément Péron 
165d76034a4SClément Péron 	/*
166d76034a4SClément Péron 	 * Set the recommend OPP this will enable and configure the regulator
167d76034a4SClément Péron 	 * if any and will avoid a switch off by regulator_late_cleanup()
168d76034a4SClément Péron 	 */
169d76034a4SClément Péron 	ret = dev_pm_opp_set_opp(dev, opp);
170d76034a4SClément Péron 	if (ret) {
171d76034a4SClément Péron 		DRM_DEV_ERROR(dev, "Couldn't set recommended OPP\n");
172d76034a4SClément Péron 		return ret;
173d76034a4SClément Péron 	}
174d76034a4SClément Péron 
175f3ba9122SRob Herring 	dev_pm_opp_put(opp);
176f3ba9122SRob Herring 
1771f8644d5SLukasz Luba 	/*
1781f8644d5SLukasz Luba 	 * Setup default thresholds for the simple_ondemand governor.
1791f8644d5SLukasz Luba 	 * The values are chosen based on experiments.
1801f8644d5SLukasz Luba 	 */
1811f8644d5SLukasz Luba 	pfdevfreq->gov_data.upthreshold = 45;
1821f8644d5SLukasz Luba 	pfdevfreq->gov_data.downdifferential = 5;
1831f8644d5SLukasz Luba 
184bc1152b0SRobin Murphy 	devfreq = devm_devfreq_add_device(dev, &panfrost_devfreq_profile,
1851f8644d5SLukasz Luba 					  DEVFREQ_GOV_SIMPLE_ONDEMAND,
1861f8644d5SLukasz Luba 					  &pfdevfreq->gov_data);
187bc1152b0SRobin Murphy 	if (IS_ERR(devfreq)) {
188bc1152b0SRobin Murphy 		DRM_DEV_ERROR(dev, "Couldn't initialize GPU devfreq\n");
1896d8a154fSYangtao Li 		return PTR_ERR(devfreq);
190f3ba9122SRob Herring 	}
1919bfacfc8SClément Péron 	pfdevfreq->devfreq = devfreq;
192bc1152b0SRobin Murphy 
19323e9d781SLukasz Luba 	cooling = devfreq_cooling_em_register(devfreq, NULL);
194bc1152b0SRobin Murphy 	if (IS_ERR(cooling))
195bc1152b0SRobin Murphy 		DRM_DEV_INFO(dev, "Failed to register cooling device\n");
196bc1152b0SRobin Murphy 	else
1979bfacfc8SClément Péron 		pfdevfreq->cooling = cooling;
198f3ba9122SRob Herring 
199f3ba9122SRob Herring 	return 0;
200f3ba9122SRob Herring }
201f3ba9122SRob Herring 
panfrost_devfreq_fini(struct panfrost_device * pfdev)202aa4fffecSSteven Price void panfrost_devfreq_fini(struct panfrost_device *pfdev)
203aa4fffecSSteven Price {
2049bfacfc8SClément Péron 	struct panfrost_devfreq *pfdevfreq = &pfdev->pfdevfreq;
2059bfacfc8SClément Péron 
20681f2fbe6SClément Péron 	if (pfdevfreq->cooling) {
2079bfacfc8SClément Péron 		devfreq_cooling_unregister(pfdevfreq->cooling);
20881f2fbe6SClément Péron 		pfdevfreq->cooling = NULL;
20981f2fbe6SClément Péron 	}
210fd587ff0SClément Péron }
211aa4fffecSSteven Price 
panfrost_devfreq_resume(struct panfrost_device * pfdev)212f3ba9122SRob Herring void panfrost_devfreq_resume(struct panfrost_device *pfdev)
213f3ba9122SRob Herring {
2149bfacfc8SClément Péron 	struct panfrost_devfreq *pfdevfreq = &pfdev->pfdevfreq;
2159bfacfc8SClément Péron 
2169bfacfc8SClément Péron 	if (!pfdevfreq->devfreq)
2171c3b526eSNeil Armstrong 		return;
2181c3b526eSNeil Armstrong 
2199bfacfc8SClément Péron 	panfrost_devfreq_reset(pfdevfreq);
220f3ba9122SRob Herring 
2219bfacfc8SClément Péron 	devfreq_resume_device(pfdevfreq->devfreq);
222f3ba9122SRob Herring }
223f3ba9122SRob Herring 
panfrost_devfreq_suspend(struct panfrost_device * pfdev)224f3ba9122SRob Herring void panfrost_devfreq_suspend(struct panfrost_device *pfdev)
225f3ba9122SRob Herring {
2269bfacfc8SClément Péron 	struct panfrost_devfreq *pfdevfreq = &pfdev->pfdevfreq;
2279bfacfc8SClément Péron 
2289bfacfc8SClément Péron 	if (!pfdevfreq->devfreq)
2291c3b526eSNeil Armstrong 		return;
2301c3b526eSNeil Armstrong 
2319bfacfc8SClément Péron 	devfreq_suspend_device(pfdevfreq->devfreq);
232f3ba9122SRob Herring }
233f3ba9122SRob Herring 
panfrost_devfreq_record_busy(struct panfrost_devfreq * pfdevfreq)2349bfacfc8SClément Péron void panfrost_devfreq_record_busy(struct panfrost_devfreq *pfdevfreq)
235f3ba9122SRob Herring {
236ed85df3fSClément Péron 	unsigned long irqflags;
237ed85df3fSClément Péron 
238ed85df3fSClément Péron 	if (!pfdevfreq->devfreq)
239ed85df3fSClément Péron 		return;
240ed85df3fSClément Péron 
241ed85df3fSClément Péron 	spin_lock_irqsave(&pfdevfreq->lock, irqflags);
242ed85df3fSClément Péron 
2439bfacfc8SClément Péron 	panfrost_devfreq_update_utilization(pfdevfreq);
244ed85df3fSClément Péron 
245ed85df3fSClément Péron 	pfdevfreq->busy_count++;
246ed85df3fSClément Péron 
247ed85df3fSClément Péron 	spin_unlock_irqrestore(&pfdevfreq->lock, irqflags);
2489e62b885SSteven Price }
249f3ba9122SRob Herring 
panfrost_devfreq_record_idle(struct panfrost_devfreq * pfdevfreq)2509bfacfc8SClément Péron void panfrost_devfreq_record_idle(struct panfrost_devfreq *pfdevfreq)
2519e62b885SSteven Price {
252ed85df3fSClément Péron 	unsigned long irqflags;
253ed85df3fSClément Péron 
254ed85df3fSClément Péron 	if (!pfdevfreq->devfreq)
255ed85df3fSClément Péron 		return;
256ed85df3fSClément Péron 
257ed85df3fSClément Péron 	spin_lock_irqsave(&pfdevfreq->lock, irqflags);
2589e62b885SSteven Price 
2599bfacfc8SClément Péron 	panfrost_devfreq_update_utilization(pfdevfreq);
260ed85df3fSClément Péron 
261ed85df3fSClément Péron 	WARN_ON(--pfdevfreq->busy_count < 0);
262ed85df3fSClément Péron 
263ed85df3fSClément Péron 	spin_unlock_irqrestore(&pfdevfreq->lock, irqflags);
264f3ba9122SRob Herring }
265