1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright 2020 Martin Blumenstingl <martin.blumenstingl@googlemail.com> 4 * 5 * Based on panfrost_devfreq.c: 6 * Copyright 2019 Collabora ltd. 7 */ 8 #include <linux/clk.h> 9 #include <linux/devfreq.h> 10 #include <linux/devfreq_cooling.h> 11 #include <linux/device.h> 12 #include <linux/platform_device.h> 13 #include <linux/pm_opp.h> 14 #include <linux/property.h> 15 16 #include "lima_device.h" 17 #include "lima_devfreq.h" 18 19 static void lima_devfreq_update_utilization(struct lima_devfreq *devfreq) 20 { 21 ktime_t now, last; 22 23 now = ktime_get(); 24 last = devfreq->time_last_update; 25 26 if (devfreq->busy_count > 0) 27 devfreq->busy_time += ktime_sub(now, last); 28 else 29 devfreq->idle_time += ktime_sub(now, last); 30 31 devfreq->time_last_update = now; 32 } 33 34 static int lima_devfreq_target(struct device *dev, unsigned long *freq, 35 u32 flags) 36 { 37 struct dev_pm_opp *opp; 38 int err; 39 40 opp = devfreq_recommended_opp(dev, freq, flags); 41 if (IS_ERR(opp)) 42 return PTR_ERR(opp); 43 dev_pm_opp_put(opp); 44 45 err = dev_pm_opp_set_rate(dev, *freq); 46 if (err) 47 return err; 48 49 return 0; 50 } 51 52 static void lima_devfreq_reset(struct lima_devfreq *devfreq) 53 { 54 devfreq->busy_time = 0; 55 devfreq->idle_time = 0; 56 devfreq->time_last_update = ktime_get(); 57 } 58 59 static int lima_devfreq_get_dev_status(struct device *dev, 60 struct devfreq_dev_status *status) 61 { 62 struct lima_device *ldev = dev_get_drvdata(dev); 63 struct lima_devfreq *devfreq = &ldev->devfreq; 64 unsigned long irqflags; 65 66 status->current_frequency = clk_get_rate(ldev->clk_gpu); 67 68 spin_lock_irqsave(&devfreq->lock, irqflags); 69 70 lima_devfreq_update_utilization(devfreq); 71 72 status->total_time = ktime_to_ns(ktime_add(devfreq->busy_time, 73 devfreq->idle_time)); 74 status->busy_time = ktime_to_ns(devfreq->busy_time); 75 76 lima_devfreq_reset(devfreq); 77 78 spin_unlock_irqrestore(&devfreq->lock, irqflags); 79 80 dev_dbg(ldev->dev, "busy %lu total %lu %lu %% freq %lu MHz\n", 81 status->busy_time, status->total_time, 82 status->busy_time / (status->total_time / 100), 83 status->current_frequency / 1000 / 1000); 84 85 return 0; 86 } 87 88 static struct devfreq_dev_profile lima_devfreq_profile = { 89 .polling_ms = 50, /* ~3 frames */ 90 .target = lima_devfreq_target, 91 .get_dev_status = lima_devfreq_get_dev_status, 92 }; 93 94 void lima_devfreq_fini(struct lima_device *ldev) 95 { 96 struct lima_devfreq *devfreq = &ldev->devfreq; 97 98 if (devfreq->cooling) { 99 devfreq_cooling_unregister(devfreq->cooling); 100 devfreq->cooling = NULL; 101 } 102 103 if (devfreq->devfreq) { 104 devm_devfreq_remove_device(ldev->dev, devfreq->devfreq); 105 devfreq->devfreq = NULL; 106 } 107 108 if (devfreq->opp_of_table_added) { 109 dev_pm_opp_of_remove_table(ldev->dev); 110 devfreq->opp_of_table_added = false; 111 } 112 113 if (devfreq->regulators_opp_table) { 114 dev_pm_opp_put_regulators(devfreq->regulators_opp_table); 115 devfreq->regulators_opp_table = NULL; 116 } 117 118 if (devfreq->clkname_opp_table) { 119 dev_pm_opp_put_clkname(devfreq->clkname_opp_table); 120 devfreq->clkname_opp_table = NULL; 121 } 122 } 123 124 int lima_devfreq_init(struct lima_device *ldev) 125 { 126 struct thermal_cooling_device *cooling; 127 struct device *dev = ldev->dev; 128 struct opp_table *opp_table; 129 struct devfreq *devfreq; 130 struct lima_devfreq *ldevfreq = &ldev->devfreq; 131 struct dev_pm_opp *opp; 132 unsigned long cur_freq; 133 int ret; 134 135 if (!device_property_present(dev, "operating-points-v2")) 136 /* Optional, continue without devfreq */ 137 return 0; 138 139 spin_lock_init(&ldevfreq->lock); 140 141 opp_table = dev_pm_opp_set_clkname(dev, "core"); 142 if (IS_ERR(opp_table)) { 143 ret = PTR_ERR(opp_table); 144 goto err_fini; 145 } 146 147 ldevfreq->clkname_opp_table = opp_table; 148 149 opp_table = dev_pm_opp_set_regulators(dev, 150 (const char *[]){ "mali" }, 151 1); 152 if (IS_ERR(opp_table)) { 153 ret = PTR_ERR(opp_table); 154 155 /* Continue if the optional regulator is missing */ 156 if (ret != -ENODEV) 157 goto err_fini; 158 } else { 159 ldevfreq->regulators_opp_table = opp_table; 160 } 161 162 ret = dev_pm_opp_of_add_table(dev); 163 if (ret) 164 goto err_fini; 165 ldevfreq->opp_of_table_added = true; 166 167 lima_devfreq_reset(ldevfreq); 168 169 cur_freq = clk_get_rate(ldev->clk_gpu); 170 171 opp = devfreq_recommended_opp(dev, &cur_freq, 0); 172 if (IS_ERR(opp)) { 173 ret = PTR_ERR(opp); 174 goto err_fini; 175 } 176 177 lima_devfreq_profile.initial_freq = cur_freq; 178 dev_pm_opp_put(opp); 179 180 devfreq = devm_devfreq_add_device(dev, &lima_devfreq_profile, 181 DEVFREQ_GOV_SIMPLE_ONDEMAND, NULL); 182 if (IS_ERR(devfreq)) { 183 dev_err(dev, "Couldn't initialize GPU devfreq\n"); 184 ret = PTR_ERR(devfreq); 185 goto err_fini; 186 } 187 188 ldevfreq->devfreq = devfreq; 189 190 cooling = of_devfreq_cooling_register(dev->of_node, devfreq); 191 if (IS_ERR(cooling)) 192 dev_info(dev, "Failed to register cooling device\n"); 193 else 194 ldevfreq->cooling = cooling; 195 196 return 0; 197 198 err_fini: 199 lima_devfreq_fini(ldev); 200 return ret; 201 } 202 203 void lima_devfreq_record_busy(struct lima_devfreq *devfreq) 204 { 205 unsigned long irqflags; 206 207 if (!devfreq->devfreq) 208 return; 209 210 spin_lock_irqsave(&devfreq->lock, irqflags); 211 212 lima_devfreq_update_utilization(devfreq); 213 214 devfreq->busy_count++; 215 216 spin_unlock_irqrestore(&devfreq->lock, irqflags); 217 } 218 219 void lima_devfreq_record_idle(struct lima_devfreq *devfreq) 220 { 221 unsigned long irqflags; 222 223 if (!devfreq->devfreq) 224 return; 225 226 spin_lock_irqsave(&devfreq->lock, irqflags); 227 228 lima_devfreq_update_utilization(devfreq); 229 230 WARN_ON(--devfreq->busy_count < 0); 231 232 spin_unlock_irqrestore(&devfreq->lock, irqflags); 233 } 234 235 int lima_devfreq_resume(struct lima_devfreq *devfreq) 236 { 237 unsigned long irqflags; 238 239 if (!devfreq->devfreq) 240 return 0; 241 242 spin_lock_irqsave(&devfreq->lock, irqflags); 243 244 lima_devfreq_reset(devfreq); 245 246 spin_unlock_irqrestore(&devfreq->lock, irqflags); 247 248 return devfreq_resume_device(devfreq->devfreq); 249 } 250 251 int lima_devfreq_suspend(struct lima_devfreq *devfreq) 252 { 253 if (!devfreq->devfreq) 254 return 0; 255 256 return devfreq_suspend_device(devfreq->devfreq); 257 } 258