xref: /openbmc/linux/drivers/gpu/drm/lima/lima_devfreq.c (revision 2ce216edf2c661fd9d2f5e19ce72fd80c25abc64)
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 	int err;
3919969707SMartin Blumenstingl 
4019969707SMartin Blumenstingl 	opp = devfreq_recommended_opp(dev, freq, flags);
4119969707SMartin Blumenstingl 	if (IS_ERR(opp))
4219969707SMartin Blumenstingl 		return PTR_ERR(opp);
4319969707SMartin Blumenstingl 	dev_pm_opp_put(opp);
4419969707SMartin Blumenstingl 
4519969707SMartin Blumenstingl 	err = dev_pm_opp_set_rate(dev, *freq);
4619969707SMartin Blumenstingl 	if (err)
4719969707SMartin Blumenstingl 		return err;
4819969707SMartin Blumenstingl 
4919969707SMartin Blumenstingl 	return 0;
5019969707SMartin Blumenstingl }
5119969707SMartin Blumenstingl 
5219969707SMartin Blumenstingl static void lima_devfreq_reset(struct lima_devfreq *devfreq)
5319969707SMartin Blumenstingl {
5419969707SMartin Blumenstingl 	devfreq->busy_time = 0;
5519969707SMartin Blumenstingl 	devfreq->idle_time = 0;
5619969707SMartin Blumenstingl 	devfreq->time_last_update = ktime_get();
5719969707SMartin Blumenstingl }
5819969707SMartin Blumenstingl 
5919969707SMartin Blumenstingl static int lima_devfreq_get_dev_status(struct device *dev,
6019969707SMartin Blumenstingl 				       struct devfreq_dev_status *status)
6119969707SMartin Blumenstingl {
6219969707SMartin Blumenstingl 	struct lima_device *ldev = dev_get_drvdata(dev);
6319969707SMartin Blumenstingl 	struct lima_devfreq *devfreq = &ldev->devfreq;
6419969707SMartin Blumenstingl 	unsigned long irqflags;
6519969707SMartin Blumenstingl 
6619969707SMartin Blumenstingl 	status->current_frequency = clk_get_rate(ldev->clk_gpu);
6719969707SMartin Blumenstingl 
6819969707SMartin Blumenstingl 	spin_lock_irqsave(&devfreq->lock, irqflags);
6919969707SMartin Blumenstingl 
7019969707SMartin Blumenstingl 	lima_devfreq_update_utilization(devfreq);
7119969707SMartin Blumenstingl 
7219969707SMartin Blumenstingl 	status->total_time = ktime_to_ns(ktime_add(devfreq->busy_time,
7319969707SMartin Blumenstingl 						   devfreq->idle_time));
7419969707SMartin Blumenstingl 	status->busy_time = ktime_to_ns(devfreq->busy_time);
7519969707SMartin Blumenstingl 
7619969707SMartin Blumenstingl 	lima_devfreq_reset(devfreq);
7719969707SMartin Blumenstingl 
7819969707SMartin Blumenstingl 	spin_unlock_irqrestore(&devfreq->lock, irqflags);
7919969707SMartin Blumenstingl 
8019969707SMartin Blumenstingl 	dev_dbg(ldev->dev, "busy %lu total %lu %lu %% freq %lu MHz\n",
8119969707SMartin Blumenstingl 		status->busy_time, status->total_time,
8219969707SMartin Blumenstingl 		status->busy_time / (status->total_time / 100),
8319969707SMartin Blumenstingl 		status->current_frequency / 1000 / 1000);
8419969707SMartin Blumenstingl 
8519969707SMartin Blumenstingl 	return 0;
8619969707SMartin Blumenstingl }
8719969707SMartin Blumenstingl 
8819969707SMartin Blumenstingl static struct devfreq_dev_profile lima_devfreq_profile = {
8919969707SMartin Blumenstingl 	.polling_ms = 50, /* ~3 frames */
9019969707SMartin Blumenstingl 	.target = lima_devfreq_target,
9119969707SMartin Blumenstingl 	.get_dev_status = lima_devfreq_get_dev_status,
9219969707SMartin Blumenstingl };
9319969707SMartin Blumenstingl 
9419969707SMartin Blumenstingl void lima_devfreq_fini(struct lima_device *ldev)
9519969707SMartin Blumenstingl {
9619969707SMartin Blumenstingl 	struct lima_devfreq *devfreq = &ldev->devfreq;
9719969707SMartin Blumenstingl 
9819969707SMartin Blumenstingl 	if (devfreq->cooling) {
9919969707SMartin Blumenstingl 		devfreq_cooling_unregister(devfreq->cooling);
10019969707SMartin Blumenstingl 		devfreq->cooling = NULL;
10119969707SMartin Blumenstingl 	}
10219969707SMartin Blumenstingl 
10319969707SMartin Blumenstingl 	if (devfreq->devfreq) {
104*2ce216edSRobin Murphy 		devm_devfreq_remove_device(ldev->dev, devfreq->devfreq);
10519969707SMartin Blumenstingl 		devfreq->devfreq = NULL;
10619969707SMartin Blumenstingl 	}
10719969707SMartin Blumenstingl 
10819969707SMartin Blumenstingl 	if (devfreq->opp_of_table_added) {
109*2ce216edSRobin Murphy 		dev_pm_opp_of_remove_table(ldev->dev);
11019969707SMartin Blumenstingl 		devfreq->opp_of_table_added = false;
11119969707SMartin Blumenstingl 	}
11219969707SMartin Blumenstingl 
11319969707SMartin Blumenstingl 	if (devfreq->regulators_opp_table) {
11419969707SMartin Blumenstingl 		dev_pm_opp_put_regulators(devfreq->regulators_opp_table);
11519969707SMartin Blumenstingl 		devfreq->regulators_opp_table = NULL;
11619969707SMartin Blumenstingl 	}
11719969707SMartin Blumenstingl 
11819969707SMartin Blumenstingl 	if (devfreq->clkname_opp_table) {
11919969707SMartin Blumenstingl 		dev_pm_opp_put_clkname(devfreq->clkname_opp_table);
12019969707SMartin Blumenstingl 		devfreq->clkname_opp_table = NULL;
12119969707SMartin Blumenstingl 	}
12219969707SMartin Blumenstingl }
12319969707SMartin Blumenstingl 
12419969707SMartin Blumenstingl int lima_devfreq_init(struct lima_device *ldev)
12519969707SMartin Blumenstingl {
12619969707SMartin Blumenstingl 	struct thermal_cooling_device *cooling;
127*2ce216edSRobin Murphy 	struct device *dev = ldev->dev;
12819969707SMartin Blumenstingl 	struct opp_table *opp_table;
12919969707SMartin Blumenstingl 	struct devfreq *devfreq;
13019969707SMartin Blumenstingl 	struct lima_devfreq *ldevfreq = &ldev->devfreq;
13119969707SMartin Blumenstingl 	struct dev_pm_opp *opp;
13219969707SMartin Blumenstingl 	unsigned long cur_freq;
13319969707SMartin Blumenstingl 	int ret;
13419969707SMartin Blumenstingl 
13519969707SMartin Blumenstingl 	if (!device_property_present(dev, "operating-points-v2"))
13619969707SMartin Blumenstingl 		/* Optional, continue without devfreq */
13719969707SMartin Blumenstingl 		return 0;
13819969707SMartin Blumenstingl 
13919969707SMartin Blumenstingl 	spin_lock_init(&ldevfreq->lock);
14019969707SMartin Blumenstingl 
14119969707SMartin Blumenstingl 	opp_table = dev_pm_opp_set_clkname(dev, "core");
14219969707SMartin Blumenstingl 	if (IS_ERR(opp_table)) {
14319969707SMartin Blumenstingl 		ret = PTR_ERR(opp_table);
14419969707SMartin Blumenstingl 		goto err_fini;
14519969707SMartin Blumenstingl 	}
14619969707SMartin Blumenstingl 
14719969707SMartin Blumenstingl 	ldevfreq->clkname_opp_table = opp_table;
14819969707SMartin Blumenstingl 
14919969707SMartin Blumenstingl 	opp_table = dev_pm_opp_set_regulators(dev,
15019969707SMartin Blumenstingl 					      (const char *[]){ "mali" },
15119969707SMartin Blumenstingl 					      1);
15219969707SMartin Blumenstingl 	if (IS_ERR(opp_table)) {
15319969707SMartin Blumenstingl 		ret = PTR_ERR(opp_table);
15419969707SMartin Blumenstingl 
15519969707SMartin Blumenstingl 		/* Continue if the optional regulator is missing */
15619969707SMartin Blumenstingl 		if (ret != -ENODEV)
15719969707SMartin Blumenstingl 			goto err_fini;
15819969707SMartin Blumenstingl 	} else {
15919969707SMartin Blumenstingl 		ldevfreq->regulators_opp_table = opp_table;
16019969707SMartin Blumenstingl 	}
16119969707SMartin Blumenstingl 
16219969707SMartin Blumenstingl 	ret = dev_pm_opp_of_add_table(dev);
16319969707SMartin Blumenstingl 	if (ret)
16419969707SMartin Blumenstingl 		goto err_fini;
16519969707SMartin Blumenstingl 	ldevfreq->opp_of_table_added = true;
16619969707SMartin Blumenstingl 
16719969707SMartin Blumenstingl 	lima_devfreq_reset(ldevfreq);
16819969707SMartin Blumenstingl 
16919969707SMartin Blumenstingl 	cur_freq = clk_get_rate(ldev->clk_gpu);
17019969707SMartin Blumenstingl 
17119969707SMartin Blumenstingl 	opp = devfreq_recommended_opp(dev, &cur_freq, 0);
17219969707SMartin Blumenstingl 	if (IS_ERR(opp)) {
17319969707SMartin Blumenstingl 		ret = PTR_ERR(opp);
17419969707SMartin Blumenstingl 		goto err_fini;
17519969707SMartin Blumenstingl 	}
17619969707SMartin Blumenstingl 
17719969707SMartin Blumenstingl 	lima_devfreq_profile.initial_freq = cur_freq;
17819969707SMartin Blumenstingl 	dev_pm_opp_put(opp);
17919969707SMartin Blumenstingl 
18019969707SMartin Blumenstingl 	devfreq = devm_devfreq_add_device(dev, &lima_devfreq_profile,
18119969707SMartin Blumenstingl 					  DEVFREQ_GOV_SIMPLE_ONDEMAND, NULL);
18219969707SMartin Blumenstingl 	if (IS_ERR(devfreq)) {
18319969707SMartin Blumenstingl 		dev_err(dev, "Couldn't initialize GPU devfreq\n");
18419969707SMartin Blumenstingl 		ret = PTR_ERR(devfreq);
18519969707SMartin Blumenstingl 		goto err_fini;
18619969707SMartin Blumenstingl 	}
18719969707SMartin Blumenstingl 
18819969707SMartin Blumenstingl 	ldevfreq->devfreq = devfreq;
18919969707SMartin Blumenstingl 
19019969707SMartin Blumenstingl 	cooling = of_devfreq_cooling_register(dev->of_node, devfreq);
19119969707SMartin Blumenstingl 	if (IS_ERR(cooling))
19219969707SMartin Blumenstingl 		dev_info(dev, "Failed to register cooling device\n");
19319969707SMartin Blumenstingl 	else
19419969707SMartin Blumenstingl 		ldevfreq->cooling = cooling;
19519969707SMartin Blumenstingl 
19619969707SMartin Blumenstingl 	return 0;
19719969707SMartin Blumenstingl 
19819969707SMartin Blumenstingl err_fini:
19919969707SMartin Blumenstingl 	lima_devfreq_fini(ldev);
20019969707SMartin Blumenstingl 	return ret;
20119969707SMartin Blumenstingl }
20219969707SMartin Blumenstingl 
20319969707SMartin Blumenstingl void lima_devfreq_record_busy(struct lima_devfreq *devfreq)
20419969707SMartin Blumenstingl {
20519969707SMartin Blumenstingl 	unsigned long irqflags;
20619969707SMartin Blumenstingl 
20719969707SMartin Blumenstingl 	if (!devfreq->devfreq)
20819969707SMartin Blumenstingl 		return;
20919969707SMartin Blumenstingl 
21019969707SMartin Blumenstingl 	spin_lock_irqsave(&devfreq->lock, irqflags);
21119969707SMartin Blumenstingl 
21219969707SMartin Blumenstingl 	lima_devfreq_update_utilization(devfreq);
21319969707SMartin Blumenstingl 
21419969707SMartin Blumenstingl 	devfreq->busy_count++;
21519969707SMartin Blumenstingl 
21619969707SMartin Blumenstingl 	spin_unlock_irqrestore(&devfreq->lock, irqflags);
21719969707SMartin Blumenstingl }
21819969707SMartin Blumenstingl 
21919969707SMartin Blumenstingl void lima_devfreq_record_idle(struct lima_devfreq *devfreq)
22019969707SMartin Blumenstingl {
22119969707SMartin Blumenstingl 	unsigned long irqflags;
22219969707SMartin Blumenstingl 
22319969707SMartin Blumenstingl 	if (!devfreq->devfreq)
22419969707SMartin Blumenstingl 		return;
22519969707SMartin Blumenstingl 
22619969707SMartin Blumenstingl 	spin_lock_irqsave(&devfreq->lock, irqflags);
22719969707SMartin Blumenstingl 
22819969707SMartin Blumenstingl 	lima_devfreq_update_utilization(devfreq);
22919969707SMartin Blumenstingl 
23019969707SMartin Blumenstingl 	WARN_ON(--devfreq->busy_count < 0);
23119969707SMartin Blumenstingl 
23219969707SMartin Blumenstingl 	spin_unlock_irqrestore(&devfreq->lock, irqflags);
23319969707SMartin Blumenstingl }
234