xref: /linux/drivers/gpu/drm/panfrost/panfrost_devfreq.c (revision 9713e942a539c55b5e0bc64ba83b736bda1087fe)
1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright 2019 Collabora ltd. */
3 
4 #include <linux/clk.h>
5 #include <linux/devfreq.h>
6 #include <linux/devfreq_cooling.h>
7 #include <linux/platform_device.h>
8 #include <linux/pm_opp.h>
9 
10 #include "panfrost_device.h"
11 #include "panfrost_devfreq.h"
12 
13 static void panfrost_devfreq_update_utilization(struct panfrost_device *pfdev)
14 {
15 	ktime_t now;
16 	ktime_t last;
17 
18 	if (!pfdev->devfreq.devfreq)
19 		return;
20 
21 	now = ktime_get();
22 	last = pfdev->devfreq.time_last_update;
23 
24 	if (atomic_read(&pfdev->devfreq.busy_count) > 0)
25 		pfdev->devfreq.busy_time += ktime_sub(now, last);
26 	else
27 		pfdev->devfreq.idle_time += ktime_sub(now, last);
28 
29 	pfdev->devfreq.time_last_update = now;
30 }
31 
32 static int panfrost_devfreq_target(struct device *dev, unsigned long *freq,
33 				   u32 flags)
34 {
35 	struct dev_pm_opp *opp;
36 	int err;
37 
38 	opp = devfreq_recommended_opp(dev, freq, flags);
39 	if (IS_ERR(opp))
40 		return PTR_ERR(opp);
41 	dev_pm_opp_put(opp);
42 
43 	err = dev_pm_opp_set_rate(dev, *freq);
44 	if (err)
45 		return err;
46 
47 	return 0;
48 }
49 
50 static void panfrost_devfreq_reset(struct panfrost_device *pfdev)
51 {
52 	pfdev->devfreq.busy_time = 0;
53 	pfdev->devfreq.idle_time = 0;
54 	pfdev->devfreq.time_last_update = ktime_get();
55 }
56 
57 static int panfrost_devfreq_get_dev_status(struct device *dev,
58 					   struct devfreq_dev_status *status)
59 {
60 	struct panfrost_device *pfdev = dev_get_drvdata(dev);
61 
62 	panfrost_devfreq_update_utilization(pfdev);
63 
64 	status->current_frequency = clk_get_rate(pfdev->clock);
65 	status->total_time = ktime_to_ns(ktime_add(pfdev->devfreq.busy_time,
66 						   pfdev->devfreq.idle_time));
67 
68 	status->busy_time = ktime_to_ns(pfdev->devfreq.busy_time);
69 
70 	panfrost_devfreq_reset(pfdev);
71 
72 	dev_dbg(pfdev->dev, "busy %lu total %lu %lu %% freq %lu MHz\n", status->busy_time,
73 		status->total_time,
74 		status->busy_time / (status->total_time / 100),
75 		status->current_frequency / 1000 / 1000);
76 
77 	return 0;
78 }
79 
80 static struct devfreq_dev_profile panfrost_devfreq_profile = {
81 	.polling_ms = 50, /* ~3 frames */
82 	.target = panfrost_devfreq_target,
83 	.get_dev_status = panfrost_devfreq_get_dev_status,
84 };
85 
86 int panfrost_devfreq_init(struct panfrost_device *pfdev)
87 {
88 	int ret;
89 	struct dev_pm_opp *opp;
90 	unsigned long cur_freq;
91 	struct device *dev = &pfdev->pdev->dev;
92 	struct devfreq *devfreq;
93 	struct thermal_cooling_device *cooling;
94 
95 	ret = dev_pm_opp_of_add_table(dev);
96 	if (ret == -ENODEV) /* Optional, continue without devfreq */
97 		return 0;
98 	else if (ret)
99 		return ret;
100 
101 	panfrost_devfreq_reset(pfdev);
102 
103 	cur_freq = clk_get_rate(pfdev->clock);
104 
105 	opp = devfreq_recommended_opp(dev, &cur_freq, 0);
106 	if (IS_ERR(opp))
107 		return PTR_ERR(opp);
108 
109 	panfrost_devfreq_profile.initial_freq = cur_freq;
110 	dev_pm_opp_put(opp);
111 
112 	devfreq = devm_devfreq_add_device(dev, &panfrost_devfreq_profile,
113 					  DEVFREQ_GOV_SIMPLE_ONDEMAND, NULL);
114 	if (IS_ERR(devfreq)) {
115 		DRM_DEV_ERROR(dev, "Couldn't initialize GPU devfreq\n");
116 		dev_pm_opp_of_remove_table(dev);
117 		return PTR_ERR(devfreq);
118 	}
119 	pfdev->devfreq.devfreq = devfreq;
120 
121 	cooling = of_devfreq_cooling_register(dev->of_node, devfreq);
122 	if (IS_ERR(cooling))
123 		DRM_DEV_INFO(dev, "Failed to register cooling device\n");
124 	else
125 		pfdev->devfreq.cooling = cooling;
126 
127 	return 0;
128 }
129 
130 void panfrost_devfreq_fini(struct panfrost_device *pfdev)
131 {
132 	if (pfdev->devfreq.cooling)
133 		devfreq_cooling_unregister(pfdev->devfreq.cooling);
134 	dev_pm_opp_of_remove_table(&pfdev->pdev->dev);
135 }
136 
137 void panfrost_devfreq_resume(struct panfrost_device *pfdev)
138 {
139 	if (!pfdev->devfreq.devfreq)
140 		return;
141 
142 	panfrost_devfreq_reset(pfdev);
143 
144 	devfreq_resume_device(pfdev->devfreq.devfreq);
145 }
146 
147 void panfrost_devfreq_suspend(struct panfrost_device *pfdev)
148 {
149 	if (!pfdev->devfreq.devfreq)
150 		return;
151 
152 	devfreq_suspend_device(pfdev->devfreq.devfreq);
153 }
154 
155 void panfrost_devfreq_record_busy(struct panfrost_device *pfdev)
156 {
157 	panfrost_devfreq_update_utilization(pfdev);
158 	atomic_inc(&pfdev->devfreq.busy_count);
159 }
160 
161 void panfrost_devfreq_record_idle(struct panfrost_device *pfdev)
162 {
163 	int count;
164 
165 	panfrost_devfreq_update_utilization(pfdev);
166 	count = atomic_dec_if_positive(&pfdev->devfreq.busy_count);
167 	WARN_ON(count < 0);
168 }
169