xref: /openbmc/linux/drivers/gpu/drm/lima/lima_devfreq.c (revision 7ae9fb1b7ecbb5d85d07857943f677fd1a559b18)
119969707SMartin Blumenstingl // SPDX-License-Identifier: GPL-2.0
219969707SMartin Blumenstingl /*
319969707SMartin Blumenstingl  * Copyright 2020 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
419969707SMartin Blumenstingl  *
519969707SMartin Blumenstingl  * Based on panfrost_devfreq.c:
619969707SMartin Blumenstingl  *   Copyright 2019 Collabora ltd.
719969707SMartin Blumenstingl  */
819969707SMartin Blumenstingl #include <linux/clk.h>
919969707SMartin Blumenstingl #include <linux/devfreq.h>
1019969707SMartin Blumenstingl #include <linux/devfreq_cooling.h>
1119969707SMartin Blumenstingl #include <linux/device.h>
1219969707SMartin Blumenstingl #include <linux/platform_device.h>
1319969707SMartin Blumenstingl #include <linux/pm_opp.h>
1419969707SMartin Blumenstingl #include <linux/property.h>
1519969707SMartin Blumenstingl 
1619969707SMartin Blumenstingl #include "lima_device.h"
1719969707SMartin Blumenstingl #include "lima_devfreq.h"
1819969707SMartin Blumenstingl 
lima_devfreq_update_utilization(struct lima_devfreq * devfreq)1919969707SMartin Blumenstingl static void lima_devfreq_update_utilization(struct lima_devfreq *devfreq)
2019969707SMartin Blumenstingl {
2119969707SMartin Blumenstingl 	ktime_t now, last;
2219969707SMartin Blumenstingl 
2319969707SMartin Blumenstingl 	now = ktime_get();
2419969707SMartin Blumenstingl 	last = devfreq->time_last_update;
2519969707SMartin Blumenstingl 
2619969707SMartin Blumenstingl 	if (devfreq->busy_count > 0)
2719969707SMartin Blumenstingl 		devfreq->busy_time += ktime_sub(now, last);
2819969707SMartin Blumenstingl 	else
2919969707SMartin Blumenstingl 		devfreq->idle_time += ktime_sub(now, last);
3019969707SMartin Blumenstingl 
3119969707SMartin Blumenstingl 	devfreq->time_last_update = now;
3219969707SMartin Blumenstingl }
3319969707SMartin Blumenstingl 
lima_devfreq_target(struct device * dev,unsigned long * freq,u32 flags)3419969707SMartin Blumenstingl static int lima_devfreq_target(struct device *dev, unsigned long *freq,
3519969707SMartin Blumenstingl 			       u32 flags)
3619969707SMartin Blumenstingl {
3719969707SMartin Blumenstingl 	struct dev_pm_opp *opp;
3819969707SMartin Blumenstingl 
3919969707SMartin Blumenstingl 	opp = devfreq_recommended_opp(dev, freq, flags);
4019969707SMartin Blumenstingl 	if (IS_ERR(opp))
4119969707SMartin Blumenstingl 		return PTR_ERR(opp);
4219969707SMartin Blumenstingl 	dev_pm_opp_put(opp);
4319969707SMartin Blumenstingl 
448f9d7ef3SLiu Shixin 	return dev_pm_opp_set_rate(dev, *freq);
4519969707SMartin Blumenstingl }
4619969707SMartin Blumenstingl 
lima_devfreq_reset(struct lima_devfreq * devfreq)4719969707SMartin Blumenstingl static void lima_devfreq_reset(struct lima_devfreq *devfreq)
4819969707SMartin Blumenstingl {
4919969707SMartin Blumenstingl 	devfreq->busy_time = 0;
5019969707SMartin Blumenstingl 	devfreq->idle_time = 0;
5119969707SMartin Blumenstingl 	devfreq->time_last_update = ktime_get();
5219969707SMartin Blumenstingl }
5319969707SMartin Blumenstingl 
lima_devfreq_get_dev_status(struct device * dev,struct devfreq_dev_status * status)5419969707SMartin Blumenstingl static int lima_devfreq_get_dev_status(struct device *dev,
5519969707SMartin Blumenstingl 				       struct devfreq_dev_status *status)
5619969707SMartin Blumenstingl {
5719969707SMartin Blumenstingl 	struct lima_device *ldev = dev_get_drvdata(dev);
5819969707SMartin Blumenstingl 	struct lima_devfreq *devfreq = &ldev->devfreq;
5919969707SMartin Blumenstingl 	unsigned long irqflags;
6019969707SMartin Blumenstingl 
6119969707SMartin Blumenstingl 	status->current_frequency = clk_get_rate(ldev->clk_gpu);
6219969707SMartin Blumenstingl 
6319969707SMartin Blumenstingl 	spin_lock_irqsave(&devfreq->lock, irqflags);
6419969707SMartin Blumenstingl 
6519969707SMartin Blumenstingl 	lima_devfreq_update_utilization(devfreq);
6619969707SMartin Blumenstingl 
6719969707SMartin Blumenstingl 	status->total_time = ktime_to_ns(ktime_add(devfreq->busy_time,
6819969707SMartin Blumenstingl 						   devfreq->idle_time));
6919969707SMartin Blumenstingl 	status->busy_time = ktime_to_ns(devfreq->busy_time);
7019969707SMartin Blumenstingl 
7119969707SMartin Blumenstingl 	lima_devfreq_reset(devfreq);
7219969707SMartin Blumenstingl 
7319969707SMartin Blumenstingl 	spin_unlock_irqrestore(&devfreq->lock, irqflags);
7419969707SMartin Blumenstingl 
7519969707SMartin Blumenstingl 	dev_dbg(ldev->dev, "busy %lu total %lu %lu %% freq %lu MHz\n",
7619969707SMartin Blumenstingl 		status->busy_time, status->total_time,
7719969707SMartin Blumenstingl 		status->busy_time / (status->total_time / 100),
7819969707SMartin Blumenstingl 		status->current_frequency / 1000 / 1000);
7919969707SMartin Blumenstingl 
8019969707SMartin Blumenstingl 	return 0;
8119969707SMartin Blumenstingl }
8219969707SMartin Blumenstingl 
8319969707SMartin Blumenstingl static struct devfreq_dev_profile lima_devfreq_profile = {
84904beebbSLukasz Luba 	.timer = DEVFREQ_TIMER_DELAYED,
8519969707SMartin Blumenstingl 	.polling_ms = 50, /* ~3 frames */
8619969707SMartin Blumenstingl 	.target = lima_devfreq_target,
8719969707SMartin Blumenstingl 	.get_dev_status = lima_devfreq_get_dev_status,
8819969707SMartin Blumenstingl };
8919969707SMartin Blumenstingl 
lima_devfreq_fini(struct lima_device * ldev)9019969707SMartin Blumenstingl void lima_devfreq_fini(struct lima_device *ldev)
9119969707SMartin Blumenstingl {
9219969707SMartin Blumenstingl 	struct lima_devfreq *devfreq = &ldev->devfreq;
9319969707SMartin Blumenstingl 
9419969707SMartin Blumenstingl 	if (devfreq->cooling) {
9519969707SMartin Blumenstingl 		devfreq_cooling_unregister(devfreq->cooling);
9619969707SMartin Blumenstingl 		devfreq->cooling = NULL;
9719969707SMartin Blumenstingl 	}
9819969707SMartin Blumenstingl 
9919969707SMartin Blumenstingl 	if (devfreq->devfreq) {
1002ce216edSRobin Murphy 		devm_devfreq_remove_device(ldev->dev, devfreq->devfreq);
10119969707SMartin Blumenstingl 		devfreq->devfreq = NULL;
10219969707SMartin Blumenstingl 	}
10319969707SMartin Blumenstingl }
10419969707SMartin Blumenstingl 
lima_devfreq_init(struct lima_device * ldev)10519969707SMartin Blumenstingl int lima_devfreq_init(struct lima_device *ldev)
10619969707SMartin Blumenstingl {
10719969707SMartin Blumenstingl 	struct thermal_cooling_device *cooling;
1082ce216edSRobin Murphy 	struct device *dev = ldev->dev;
10919969707SMartin Blumenstingl 	struct devfreq *devfreq;
11019969707SMartin Blumenstingl 	struct lima_devfreq *ldevfreq = &ldev->devfreq;
11119969707SMartin Blumenstingl 	struct dev_pm_opp *opp;
11219969707SMartin Blumenstingl 	unsigned long cur_freq;
11319969707SMartin Blumenstingl 	int ret;
11487686cc8SViresh Kumar 	const char *regulator_names[] = { "mali", NULL };
11519969707SMartin Blumenstingl 
11619969707SMartin Blumenstingl 	if (!device_property_present(dev, "operating-points-v2"))
11719969707SMartin Blumenstingl 		/* Optional, continue without devfreq */
11819969707SMartin Blumenstingl 		return 0;
11919969707SMartin Blumenstingl 
12019969707SMartin Blumenstingl 	spin_lock_init(&ldevfreq->lock);
12119969707SMartin Blumenstingl 
122*e17a025aSErico Nunes 	/*
123*e17a025aSErico Nunes 	 * clkname is set separately so it is not affected by the optional
124*e17a025aSErico Nunes 	 * regulator setting which may return error.
125*e17a025aSErico Nunes 	 */
126*e17a025aSErico Nunes 	ret = devm_pm_opp_set_clkname(dev, "core");
127*e17a025aSErico Nunes 	if (ret)
128*e17a025aSErico Nunes 		return ret;
129*e17a025aSErico Nunes 
130*e17a025aSErico Nunes 	ret = devm_pm_opp_set_regulators(dev, regulator_names);
131864a2701SYangtao Li 	if (ret) {
13219969707SMartin Blumenstingl 		/* Continue if the optional regulator is missing */
13319969707SMartin Blumenstingl 		if (ret != -ENODEV)
134864a2701SYangtao Li 			return ret;
13519969707SMartin Blumenstingl 	}
13619969707SMartin Blumenstingl 
137864a2701SYangtao Li 	ret = devm_pm_opp_of_add_table(dev);
13819969707SMartin Blumenstingl 	if (ret)
139864a2701SYangtao Li 		return ret;
14019969707SMartin Blumenstingl 
14119969707SMartin Blumenstingl 	lima_devfreq_reset(ldevfreq);
14219969707SMartin Blumenstingl 
14319969707SMartin Blumenstingl 	cur_freq = clk_get_rate(ldev->clk_gpu);
14419969707SMartin Blumenstingl 
14519969707SMartin Blumenstingl 	opp = devfreq_recommended_opp(dev, &cur_freq, 0);
146864a2701SYangtao Li 	if (IS_ERR(opp))
147864a2701SYangtao Li 		return PTR_ERR(opp);
14819969707SMartin Blumenstingl 
14919969707SMartin Blumenstingl 	lima_devfreq_profile.initial_freq = cur_freq;
15019969707SMartin Blumenstingl 	dev_pm_opp_put(opp);
15119969707SMartin Blumenstingl 
1521d048afeSChristian Hewitt 	/*
1531d048afeSChristian Hewitt 	 * Setup default thresholds for the simple_ondemand governor.
1541d048afeSChristian Hewitt 	 * The values are chosen based on experiments.
1551d048afeSChristian Hewitt 	 */
1561d048afeSChristian Hewitt 	ldevfreq->gov_data.upthreshold = 30;
1571d048afeSChristian Hewitt 	ldevfreq->gov_data.downdifferential = 5;
1581d048afeSChristian Hewitt 
15919969707SMartin Blumenstingl 	devfreq = devm_devfreq_add_device(dev, &lima_devfreq_profile,
1601d048afeSChristian Hewitt 					  DEVFREQ_GOV_SIMPLE_ONDEMAND,
1611d048afeSChristian Hewitt 					  &ldevfreq->gov_data);
16219969707SMartin Blumenstingl 	if (IS_ERR(devfreq)) {
16319969707SMartin Blumenstingl 		dev_err(dev, "Couldn't initialize GPU devfreq\n");
164864a2701SYangtao Li 		return PTR_ERR(devfreq);
16519969707SMartin Blumenstingl 	}
16619969707SMartin Blumenstingl 
16719969707SMartin Blumenstingl 	ldevfreq->devfreq = devfreq;
16819969707SMartin Blumenstingl 
16919969707SMartin Blumenstingl 	cooling = of_devfreq_cooling_register(dev->of_node, devfreq);
17019969707SMartin Blumenstingl 	if (IS_ERR(cooling))
17119969707SMartin Blumenstingl 		dev_info(dev, "Failed to register cooling device\n");
17219969707SMartin Blumenstingl 	else
17319969707SMartin Blumenstingl 		ldevfreq->cooling = cooling;
17419969707SMartin Blumenstingl 
17519969707SMartin Blumenstingl 	return 0;
17619969707SMartin Blumenstingl }
17719969707SMartin Blumenstingl 
lima_devfreq_record_busy(struct lima_devfreq * devfreq)17819969707SMartin Blumenstingl void lima_devfreq_record_busy(struct lima_devfreq *devfreq)
17919969707SMartin Blumenstingl {
18019969707SMartin Blumenstingl 	unsigned long irqflags;
18119969707SMartin Blumenstingl 
18219969707SMartin Blumenstingl 	if (!devfreq->devfreq)
18319969707SMartin Blumenstingl 		return;
18419969707SMartin Blumenstingl 
18519969707SMartin Blumenstingl 	spin_lock_irqsave(&devfreq->lock, irqflags);
18619969707SMartin Blumenstingl 
18719969707SMartin Blumenstingl 	lima_devfreq_update_utilization(devfreq);
18819969707SMartin Blumenstingl 
18919969707SMartin Blumenstingl 	devfreq->busy_count++;
19019969707SMartin Blumenstingl 
19119969707SMartin Blumenstingl 	spin_unlock_irqrestore(&devfreq->lock, irqflags);
19219969707SMartin Blumenstingl }
19319969707SMartin Blumenstingl 
lima_devfreq_record_idle(struct lima_devfreq * devfreq)19419969707SMartin Blumenstingl void lima_devfreq_record_idle(struct lima_devfreq *devfreq)
19519969707SMartin Blumenstingl {
19619969707SMartin Blumenstingl 	unsigned long irqflags;
19719969707SMartin Blumenstingl 
19819969707SMartin Blumenstingl 	if (!devfreq->devfreq)
19919969707SMartin Blumenstingl 		return;
20019969707SMartin Blumenstingl 
20119969707SMartin Blumenstingl 	spin_lock_irqsave(&devfreq->lock, irqflags);
20219969707SMartin Blumenstingl 
20319969707SMartin Blumenstingl 	lima_devfreq_update_utilization(devfreq);
20419969707SMartin Blumenstingl 
20519969707SMartin Blumenstingl 	WARN_ON(--devfreq->busy_count < 0);
20619969707SMartin Blumenstingl 
20719969707SMartin Blumenstingl 	spin_unlock_irqrestore(&devfreq->lock, irqflags);
20819969707SMartin Blumenstingl }
2094836cf04SQiang Yu 
lima_devfreq_resume(struct lima_devfreq * devfreq)2104836cf04SQiang Yu int lima_devfreq_resume(struct lima_devfreq *devfreq)
2114836cf04SQiang Yu {
2124836cf04SQiang Yu 	unsigned long irqflags;
2134836cf04SQiang Yu 
2144836cf04SQiang Yu 	if (!devfreq->devfreq)
2154836cf04SQiang Yu 		return 0;
2164836cf04SQiang Yu 
2174836cf04SQiang Yu 	spin_lock_irqsave(&devfreq->lock, irqflags);
2184836cf04SQiang Yu 
2194836cf04SQiang Yu 	lima_devfreq_reset(devfreq);
2204836cf04SQiang Yu 
2214836cf04SQiang Yu 	spin_unlock_irqrestore(&devfreq->lock, irqflags);
2224836cf04SQiang Yu 
2234836cf04SQiang Yu 	return devfreq_resume_device(devfreq->devfreq);
2244836cf04SQiang Yu }
2254836cf04SQiang Yu 
lima_devfreq_suspend(struct lima_devfreq * devfreq)2264836cf04SQiang Yu int lima_devfreq_suspend(struct lima_devfreq *devfreq)
2274836cf04SQiang Yu {
2284836cf04SQiang Yu 	if (!devfreq->devfreq)
2294836cf04SQiang Yu 		return 0;
2304836cf04SQiang Yu 
2314836cf04SQiang Yu 	return devfreq_suspend_device(devfreq->devfreq);
2324836cf04SQiang Yu }
233