xref: /openbmc/linux/drivers/gpu/drm/lima/lima_devfreq.c (revision 904beebb3eb501e473d23f59de9c2d5173564a24)
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 
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 
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 
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 
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 = {
84*904beebbSLukasz 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 
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 
1042ce216edSRobin Murphy 	dev_pm_opp_of_remove_table(ldev->dev);
10519969707SMartin Blumenstingl 
10619969707SMartin Blumenstingl 	dev_pm_opp_put_regulators(devfreq->regulators_opp_table);
10719969707SMartin Blumenstingl 	dev_pm_opp_put_clkname(devfreq->clkname_opp_table);
10872ba9e22SViresh Kumar 	devfreq->regulators_opp_table = NULL;
10919969707SMartin Blumenstingl 	devfreq->clkname_opp_table = NULL;
11019969707SMartin Blumenstingl }
11119969707SMartin Blumenstingl 
11219969707SMartin Blumenstingl int lima_devfreq_init(struct lima_device *ldev)
11319969707SMartin Blumenstingl {
11419969707SMartin Blumenstingl 	struct thermal_cooling_device *cooling;
1152ce216edSRobin Murphy 	struct device *dev = ldev->dev;
11619969707SMartin Blumenstingl 	struct opp_table *opp_table;
11719969707SMartin Blumenstingl 	struct devfreq *devfreq;
11819969707SMartin Blumenstingl 	struct lima_devfreq *ldevfreq = &ldev->devfreq;
11919969707SMartin Blumenstingl 	struct dev_pm_opp *opp;
12019969707SMartin Blumenstingl 	unsigned long cur_freq;
12119969707SMartin Blumenstingl 	int ret;
12219969707SMartin Blumenstingl 
12319969707SMartin Blumenstingl 	if (!device_property_present(dev, "operating-points-v2"))
12419969707SMartin Blumenstingl 		/* Optional, continue without devfreq */
12519969707SMartin Blumenstingl 		return 0;
12619969707SMartin Blumenstingl 
12719969707SMartin Blumenstingl 	spin_lock_init(&ldevfreq->lock);
12819969707SMartin Blumenstingl 
12919969707SMartin Blumenstingl 	opp_table = dev_pm_opp_set_clkname(dev, "core");
13019969707SMartin Blumenstingl 	if (IS_ERR(opp_table)) {
13119969707SMartin Blumenstingl 		ret = PTR_ERR(opp_table);
13219969707SMartin Blumenstingl 		goto err_fini;
13319969707SMartin Blumenstingl 	}
13419969707SMartin Blumenstingl 
13519969707SMartin Blumenstingl 	ldevfreq->clkname_opp_table = opp_table;
13619969707SMartin Blumenstingl 
13719969707SMartin Blumenstingl 	opp_table = dev_pm_opp_set_regulators(dev,
13819969707SMartin Blumenstingl 					      (const char *[]){ "mali" },
13919969707SMartin Blumenstingl 					      1);
14019969707SMartin Blumenstingl 	if (IS_ERR(opp_table)) {
14119969707SMartin Blumenstingl 		ret = PTR_ERR(opp_table);
14219969707SMartin Blumenstingl 
14319969707SMartin Blumenstingl 		/* Continue if the optional regulator is missing */
14419969707SMartin Blumenstingl 		if (ret != -ENODEV)
14519969707SMartin Blumenstingl 			goto err_fini;
14619969707SMartin Blumenstingl 	} else {
14719969707SMartin Blumenstingl 		ldevfreq->regulators_opp_table = opp_table;
14819969707SMartin Blumenstingl 	}
14919969707SMartin Blumenstingl 
15019969707SMartin Blumenstingl 	ret = dev_pm_opp_of_add_table(dev);
15119969707SMartin Blumenstingl 	if (ret)
15219969707SMartin Blumenstingl 		goto err_fini;
15319969707SMartin Blumenstingl 
15419969707SMartin Blumenstingl 	lima_devfreq_reset(ldevfreq);
15519969707SMartin Blumenstingl 
15619969707SMartin Blumenstingl 	cur_freq = clk_get_rate(ldev->clk_gpu);
15719969707SMartin Blumenstingl 
15819969707SMartin Blumenstingl 	opp = devfreq_recommended_opp(dev, &cur_freq, 0);
15919969707SMartin Blumenstingl 	if (IS_ERR(opp)) {
16019969707SMartin Blumenstingl 		ret = PTR_ERR(opp);
16119969707SMartin Blumenstingl 		goto err_fini;
16219969707SMartin Blumenstingl 	}
16319969707SMartin Blumenstingl 
16419969707SMartin Blumenstingl 	lima_devfreq_profile.initial_freq = cur_freq;
16519969707SMartin Blumenstingl 	dev_pm_opp_put(opp);
16619969707SMartin Blumenstingl 
16719969707SMartin Blumenstingl 	devfreq = devm_devfreq_add_device(dev, &lima_devfreq_profile,
16819969707SMartin Blumenstingl 					  DEVFREQ_GOV_SIMPLE_ONDEMAND, NULL);
16919969707SMartin Blumenstingl 	if (IS_ERR(devfreq)) {
17019969707SMartin Blumenstingl 		dev_err(dev, "Couldn't initialize GPU devfreq\n");
17119969707SMartin Blumenstingl 		ret = PTR_ERR(devfreq);
17219969707SMartin Blumenstingl 		goto err_fini;
17319969707SMartin Blumenstingl 	}
17419969707SMartin Blumenstingl 
17519969707SMartin Blumenstingl 	ldevfreq->devfreq = devfreq;
17619969707SMartin Blumenstingl 
17719969707SMartin Blumenstingl 	cooling = of_devfreq_cooling_register(dev->of_node, devfreq);
17819969707SMartin Blumenstingl 	if (IS_ERR(cooling))
17919969707SMartin Blumenstingl 		dev_info(dev, "Failed to register cooling device\n");
18019969707SMartin Blumenstingl 	else
18119969707SMartin Blumenstingl 		ldevfreq->cooling = cooling;
18219969707SMartin Blumenstingl 
18319969707SMartin Blumenstingl 	return 0;
18419969707SMartin Blumenstingl 
18519969707SMartin Blumenstingl err_fini:
18619969707SMartin Blumenstingl 	lima_devfreq_fini(ldev);
18719969707SMartin Blumenstingl 	return ret;
18819969707SMartin Blumenstingl }
18919969707SMartin Blumenstingl 
19019969707SMartin Blumenstingl void lima_devfreq_record_busy(struct lima_devfreq *devfreq)
19119969707SMartin Blumenstingl {
19219969707SMartin Blumenstingl 	unsigned long irqflags;
19319969707SMartin Blumenstingl 
19419969707SMartin Blumenstingl 	if (!devfreq->devfreq)
19519969707SMartin Blumenstingl 		return;
19619969707SMartin Blumenstingl 
19719969707SMartin Blumenstingl 	spin_lock_irqsave(&devfreq->lock, irqflags);
19819969707SMartin Blumenstingl 
19919969707SMartin Blumenstingl 	lima_devfreq_update_utilization(devfreq);
20019969707SMartin Blumenstingl 
20119969707SMartin Blumenstingl 	devfreq->busy_count++;
20219969707SMartin Blumenstingl 
20319969707SMartin Blumenstingl 	spin_unlock_irqrestore(&devfreq->lock, irqflags);
20419969707SMartin Blumenstingl }
20519969707SMartin Blumenstingl 
20619969707SMartin Blumenstingl void lima_devfreq_record_idle(struct lima_devfreq *devfreq)
20719969707SMartin Blumenstingl {
20819969707SMartin Blumenstingl 	unsigned long irqflags;
20919969707SMartin Blumenstingl 
21019969707SMartin Blumenstingl 	if (!devfreq->devfreq)
21119969707SMartin Blumenstingl 		return;
21219969707SMartin Blumenstingl 
21319969707SMartin Blumenstingl 	spin_lock_irqsave(&devfreq->lock, irqflags);
21419969707SMartin Blumenstingl 
21519969707SMartin Blumenstingl 	lima_devfreq_update_utilization(devfreq);
21619969707SMartin Blumenstingl 
21719969707SMartin Blumenstingl 	WARN_ON(--devfreq->busy_count < 0);
21819969707SMartin Blumenstingl 
21919969707SMartin Blumenstingl 	spin_unlock_irqrestore(&devfreq->lock, irqflags);
22019969707SMartin Blumenstingl }
2214836cf04SQiang Yu 
2224836cf04SQiang Yu int lima_devfreq_resume(struct lima_devfreq *devfreq)
2234836cf04SQiang Yu {
2244836cf04SQiang Yu 	unsigned long irqflags;
2254836cf04SQiang Yu 
2264836cf04SQiang Yu 	if (!devfreq->devfreq)
2274836cf04SQiang Yu 		return 0;
2284836cf04SQiang Yu 
2294836cf04SQiang Yu 	spin_lock_irqsave(&devfreq->lock, irqflags);
2304836cf04SQiang Yu 
2314836cf04SQiang Yu 	lima_devfreq_reset(devfreq);
2324836cf04SQiang Yu 
2334836cf04SQiang Yu 	spin_unlock_irqrestore(&devfreq->lock, irqflags);
2344836cf04SQiang Yu 
2354836cf04SQiang Yu 	return devfreq_resume_device(devfreq->devfreq);
2364836cf04SQiang Yu }
2374836cf04SQiang Yu 
2384836cf04SQiang Yu int lima_devfreq_suspend(struct lima_devfreq *devfreq)
2394836cf04SQiang Yu {
2404836cf04SQiang Yu 	if (!devfreq->devfreq)
2414836cf04SQiang Yu 		return 0;
2424836cf04SQiang Yu 
2434836cf04SQiang Yu 	return devfreq_suspend_device(devfreq->devfreq);
2444836cf04SQiang Yu }
245