1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (C) 2018 Broadcom 4 */ 5 6 /** 7 * DOC: VC4 V3D performance monitor module 8 * 9 * The V3D block provides 16 hardware counters which can count various events. 10 */ 11 12 #include <drm/drm_print.h> 13 14 #include "vc4_drv.h" 15 #include "vc4_regs.h" 16 17 #define VC4_PERFMONID_MIN 1 18 #define VC4_PERFMONID_MAX U32_MAX 19 20 void vc4_perfmon_get(struct vc4_perfmon *perfmon) 21 { 22 struct vc4_dev *vc4; 23 24 if (!perfmon) 25 return; 26 27 vc4 = perfmon->dev; 28 if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4)) 29 return; 30 31 refcount_inc(&perfmon->refcnt); 32 } 33 34 void vc4_perfmon_put(struct vc4_perfmon *perfmon) 35 { 36 struct vc4_dev *vc4; 37 38 if (!perfmon) 39 return; 40 41 vc4 = perfmon->dev; 42 if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4)) 43 return; 44 45 if (refcount_dec_and_test(&perfmon->refcnt)) 46 kfree(perfmon); 47 } 48 49 void vc4_perfmon_start(struct vc4_dev *vc4, struct vc4_perfmon *perfmon) 50 { 51 unsigned int i; 52 u32 mask; 53 54 if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4)) 55 return; 56 57 if (WARN_ON_ONCE(!perfmon || vc4->active_perfmon)) 58 return; 59 60 for (i = 0; i < perfmon->ncounters; i++) 61 V3D_WRITE(V3D_PCTRS(i), perfmon->events[i]); 62 63 mask = GENMASK(perfmon->ncounters - 1, 0); 64 V3D_WRITE(V3D_PCTRC, mask); 65 V3D_WRITE(V3D_PCTRE, V3D_PCTRE_EN | mask); 66 vc4->active_perfmon = perfmon; 67 } 68 69 void vc4_perfmon_stop(struct vc4_dev *vc4, struct vc4_perfmon *perfmon, 70 bool capture) 71 { 72 unsigned int i; 73 74 if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4)) 75 return; 76 77 if (WARN_ON_ONCE(!vc4->active_perfmon || 78 perfmon != vc4->active_perfmon)) 79 return; 80 81 if (capture) { 82 for (i = 0; i < perfmon->ncounters; i++) 83 perfmon->counters[i] += V3D_READ(V3D_PCTR(i)); 84 } 85 86 V3D_WRITE(V3D_PCTRE, 0); 87 vc4->active_perfmon = NULL; 88 } 89 90 struct vc4_perfmon *vc4_perfmon_find(struct vc4_file *vc4file, int id) 91 { 92 struct vc4_dev *vc4 = vc4file->dev; 93 struct vc4_perfmon *perfmon; 94 95 if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4)) 96 return NULL; 97 98 mutex_lock(&vc4file->perfmon.lock); 99 perfmon = idr_find(&vc4file->perfmon.idr, id); 100 vc4_perfmon_get(perfmon); 101 mutex_unlock(&vc4file->perfmon.lock); 102 103 return perfmon; 104 } 105 106 void vc4_perfmon_open_file(struct vc4_file *vc4file) 107 { 108 struct vc4_dev *vc4 = vc4file->dev; 109 110 if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4)) 111 return; 112 113 mutex_init(&vc4file->perfmon.lock); 114 idr_init_base(&vc4file->perfmon.idr, VC4_PERFMONID_MIN); 115 vc4file->dev = vc4; 116 } 117 118 static int vc4_perfmon_idr_del(int id, void *elem, void *data) 119 { 120 struct vc4_perfmon *perfmon = elem; 121 struct vc4_dev *vc4 = (struct vc4_dev *)data; 122 123 /* If the active perfmon is being destroyed, stop it first */ 124 if (perfmon == vc4->active_perfmon) 125 vc4_perfmon_stop(vc4, perfmon, false); 126 127 vc4_perfmon_put(perfmon); 128 129 return 0; 130 } 131 132 void vc4_perfmon_close_file(struct vc4_file *vc4file) 133 { 134 struct vc4_dev *vc4 = vc4file->dev; 135 136 if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4)) 137 return; 138 139 mutex_lock(&vc4file->perfmon.lock); 140 idr_for_each(&vc4file->perfmon.idr, vc4_perfmon_idr_del, vc4); 141 idr_destroy(&vc4file->perfmon.idr); 142 mutex_unlock(&vc4file->perfmon.lock); 143 mutex_destroy(&vc4file->perfmon.lock); 144 } 145 146 int vc4_perfmon_create_ioctl(struct drm_device *dev, void *data, 147 struct drm_file *file_priv) 148 { 149 struct vc4_dev *vc4 = to_vc4_dev(dev); 150 struct vc4_file *vc4file = file_priv->driver_priv; 151 struct drm_vc4_perfmon_create *req = data; 152 struct vc4_perfmon *perfmon; 153 unsigned int i; 154 int ret; 155 156 if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4)) 157 return -ENODEV; 158 159 if (!vc4->v3d) { 160 DRM_DEBUG("Creating perfmon no VC4 V3D probed\n"); 161 return -ENODEV; 162 } 163 164 /* Number of monitored counters cannot exceed HW limits. */ 165 if (req->ncounters > DRM_VC4_MAX_PERF_COUNTERS || 166 !req->ncounters) 167 return -EINVAL; 168 169 /* Make sure all events are valid. */ 170 for (i = 0; i < req->ncounters; i++) { 171 if (req->events[i] >= VC4_PERFCNT_NUM_EVENTS) 172 return -EINVAL; 173 } 174 175 perfmon = kzalloc(struct_size(perfmon, counters, req->ncounters), 176 GFP_KERNEL); 177 if (!perfmon) 178 return -ENOMEM; 179 perfmon->dev = vc4; 180 181 for (i = 0; i < req->ncounters; i++) 182 perfmon->events[i] = req->events[i]; 183 184 perfmon->ncounters = req->ncounters; 185 186 refcount_set(&perfmon->refcnt, 1); 187 188 mutex_lock(&vc4file->perfmon.lock); 189 ret = idr_alloc(&vc4file->perfmon.idr, perfmon, VC4_PERFMONID_MIN, 190 VC4_PERFMONID_MAX, GFP_KERNEL); 191 mutex_unlock(&vc4file->perfmon.lock); 192 193 if (ret < 0) { 194 kfree(perfmon); 195 return ret; 196 } 197 198 req->id = ret; 199 return 0; 200 } 201 202 int vc4_perfmon_destroy_ioctl(struct drm_device *dev, void *data, 203 struct drm_file *file_priv) 204 { 205 struct vc4_dev *vc4 = to_vc4_dev(dev); 206 struct vc4_file *vc4file = file_priv->driver_priv; 207 struct drm_vc4_perfmon_destroy *req = data; 208 struct vc4_perfmon *perfmon; 209 210 if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4)) 211 return -ENODEV; 212 213 if (!vc4->v3d) { 214 DRM_DEBUG("Destroying perfmon no VC4 V3D probed\n"); 215 return -ENODEV; 216 } 217 218 mutex_lock(&vc4file->perfmon.lock); 219 perfmon = idr_remove(&vc4file->perfmon.idr, req->id); 220 mutex_unlock(&vc4file->perfmon.lock); 221 222 if (!perfmon) 223 return -EINVAL; 224 225 vc4_perfmon_put(perfmon); 226 return 0; 227 } 228 229 int vc4_perfmon_get_values_ioctl(struct drm_device *dev, void *data, 230 struct drm_file *file_priv) 231 { 232 struct vc4_dev *vc4 = to_vc4_dev(dev); 233 struct vc4_file *vc4file = file_priv->driver_priv; 234 struct drm_vc4_perfmon_get_values *req = data; 235 struct vc4_perfmon *perfmon; 236 int ret; 237 238 if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4)) 239 return -ENODEV; 240 241 if (!vc4->v3d) { 242 DRM_DEBUG("Getting perfmon no VC4 V3D probed\n"); 243 return -ENODEV; 244 } 245 246 perfmon = vc4_perfmon_find(vc4file, req->id); 247 if (!perfmon) 248 return -EINVAL; 249 250 if (copy_to_user(u64_to_user_ptr(req->values_ptr), perfmon->counters, 251 perfmon->ncounters * sizeof(u64))) 252 ret = -EFAULT; 253 else 254 ret = 0; 255 256 vc4_perfmon_put(perfmon); 257 return ret; 258 } 259