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