xref: /openbmc/linux/drivers/gpu/drm/lima/lima_devfreq.c (revision 1d048afe7e52462d6b2a31dd6ee09c4475f39b4b)
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 = {
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 
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 
167*1d048afeSChristian Hewitt 	/*
168*1d048afeSChristian Hewitt 	 * Setup default thresholds for the simple_ondemand governor.
169*1d048afeSChristian Hewitt 	 * The values are chosen based on experiments.
170*1d048afeSChristian Hewitt 	 */
171*1d048afeSChristian Hewitt 	ldevfreq->gov_data.upthreshold = 30;
172*1d048afeSChristian Hewitt 	ldevfreq->gov_data.downdifferential = 5;
173*1d048afeSChristian Hewitt 
17419969707SMartin Blumenstingl 	devfreq = devm_devfreq_add_device(dev, &lima_devfreq_profile,
175*1d048afeSChristian Hewitt 					  DEVFREQ_GOV_SIMPLE_ONDEMAND,
176*1d048afeSChristian Hewitt 					  &ldevfreq->gov_data);
17719969707SMartin Blumenstingl 	if (IS_ERR(devfreq)) {
17819969707SMartin Blumenstingl 		dev_err(dev, "Couldn't initialize GPU devfreq\n");
17919969707SMartin Blumenstingl 		ret = PTR_ERR(devfreq);
18019969707SMartin Blumenstingl 		goto err_fini;
18119969707SMartin Blumenstingl 	}
18219969707SMartin Blumenstingl 
18319969707SMartin Blumenstingl 	ldevfreq->devfreq = devfreq;
18419969707SMartin Blumenstingl 
18519969707SMartin Blumenstingl 	cooling = of_devfreq_cooling_register(dev->of_node, devfreq);
18619969707SMartin Blumenstingl 	if (IS_ERR(cooling))
18719969707SMartin Blumenstingl 		dev_info(dev, "Failed to register cooling device\n");
18819969707SMartin Blumenstingl 	else
18919969707SMartin Blumenstingl 		ldevfreq->cooling = cooling;
19019969707SMartin Blumenstingl 
19119969707SMartin Blumenstingl 	return 0;
19219969707SMartin Blumenstingl 
19319969707SMartin Blumenstingl err_fini:
19419969707SMartin Blumenstingl 	lima_devfreq_fini(ldev);
19519969707SMartin Blumenstingl 	return ret;
19619969707SMartin Blumenstingl }
19719969707SMartin Blumenstingl 
19819969707SMartin Blumenstingl void lima_devfreq_record_busy(struct lima_devfreq *devfreq)
19919969707SMartin Blumenstingl {
20019969707SMartin Blumenstingl 	unsigned long irqflags;
20119969707SMartin Blumenstingl 
20219969707SMartin Blumenstingl 	if (!devfreq->devfreq)
20319969707SMartin Blumenstingl 		return;
20419969707SMartin Blumenstingl 
20519969707SMartin Blumenstingl 	spin_lock_irqsave(&devfreq->lock, irqflags);
20619969707SMartin Blumenstingl 
20719969707SMartin Blumenstingl 	lima_devfreq_update_utilization(devfreq);
20819969707SMartin Blumenstingl 
20919969707SMartin Blumenstingl 	devfreq->busy_count++;
21019969707SMartin Blumenstingl 
21119969707SMartin Blumenstingl 	spin_unlock_irqrestore(&devfreq->lock, irqflags);
21219969707SMartin Blumenstingl }
21319969707SMartin Blumenstingl 
21419969707SMartin Blumenstingl void lima_devfreq_record_idle(struct lima_devfreq *devfreq)
21519969707SMartin Blumenstingl {
21619969707SMartin Blumenstingl 	unsigned long irqflags;
21719969707SMartin Blumenstingl 
21819969707SMartin Blumenstingl 	if (!devfreq->devfreq)
21919969707SMartin Blumenstingl 		return;
22019969707SMartin Blumenstingl 
22119969707SMartin Blumenstingl 	spin_lock_irqsave(&devfreq->lock, irqflags);
22219969707SMartin Blumenstingl 
22319969707SMartin Blumenstingl 	lima_devfreq_update_utilization(devfreq);
22419969707SMartin Blumenstingl 
22519969707SMartin Blumenstingl 	WARN_ON(--devfreq->busy_count < 0);
22619969707SMartin Blumenstingl 
22719969707SMartin Blumenstingl 	spin_unlock_irqrestore(&devfreq->lock, irqflags);
22819969707SMartin Blumenstingl }
2294836cf04SQiang Yu 
2304836cf04SQiang Yu int lima_devfreq_resume(struct lima_devfreq *devfreq)
2314836cf04SQiang Yu {
2324836cf04SQiang Yu 	unsigned long irqflags;
2334836cf04SQiang Yu 
2344836cf04SQiang Yu 	if (!devfreq->devfreq)
2354836cf04SQiang Yu 		return 0;
2364836cf04SQiang Yu 
2374836cf04SQiang Yu 	spin_lock_irqsave(&devfreq->lock, irqflags);
2384836cf04SQiang Yu 
2394836cf04SQiang Yu 	lima_devfreq_reset(devfreq);
2404836cf04SQiang Yu 
2414836cf04SQiang Yu 	spin_unlock_irqrestore(&devfreq->lock, irqflags);
2424836cf04SQiang Yu 
2434836cf04SQiang Yu 	return devfreq_resume_device(devfreq->devfreq);
2444836cf04SQiang Yu }
2454836cf04SQiang Yu 
2464836cf04SQiang Yu int lima_devfreq_suspend(struct lima_devfreq *devfreq)
2474836cf04SQiang Yu {
2484836cf04SQiang Yu 	if (!devfreq->devfreq)
2494836cf04SQiang Yu 		return 0;
2504836cf04SQiang Yu 
2514836cf04SQiang Yu 	return devfreq_suspend_device(devfreq->devfreq);
2524836cf04SQiang Yu }
253