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