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