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