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