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