Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright (C) 2018 Broadcom
0004  */
0005 
0006 /**
0007  * DOC: VC4 V3D performance monitor module
0008  *
0009  * The V3D block provides 16 hardware counters which can count various events.
0010  */
0011 
0012 #include "vc4_drv.h"
0013 #include "vc4_regs.h"
0014 
0015 #define VC4_PERFMONID_MIN   1
0016 #define VC4_PERFMONID_MAX   U32_MAX
0017 
0018 void vc4_perfmon_get(struct vc4_perfmon *perfmon)
0019 {
0020     struct vc4_dev *vc4;
0021 
0022     if (!perfmon)
0023         return;
0024 
0025     vc4 = perfmon->dev;
0026     if (WARN_ON_ONCE(vc4->is_vc5))
0027         return;
0028 
0029     refcount_inc(&perfmon->refcnt);
0030 }
0031 
0032 void vc4_perfmon_put(struct vc4_perfmon *perfmon)
0033 {
0034     struct vc4_dev *vc4;
0035 
0036     if (!perfmon)
0037         return;
0038 
0039     vc4 = perfmon->dev;
0040     if (WARN_ON_ONCE(vc4->is_vc5))
0041         return;
0042 
0043     if (refcount_dec_and_test(&perfmon->refcnt))
0044         kfree(perfmon);
0045 }
0046 
0047 void vc4_perfmon_start(struct vc4_dev *vc4, struct vc4_perfmon *perfmon)
0048 {
0049     unsigned int i;
0050     u32 mask;
0051 
0052     if (WARN_ON_ONCE(vc4->is_vc5))
0053         return;
0054 
0055     if (WARN_ON_ONCE(!perfmon || vc4->active_perfmon))
0056         return;
0057 
0058     for (i = 0; i < perfmon->ncounters; i++)
0059         V3D_WRITE(V3D_PCTRS(i), perfmon->events[i]);
0060 
0061     mask = GENMASK(perfmon->ncounters - 1, 0);
0062     V3D_WRITE(V3D_PCTRC, mask);
0063     V3D_WRITE(V3D_PCTRE, V3D_PCTRE_EN | mask);
0064     vc4->active_perfmon = perfmon;
0065 }
0066 
0067 void vc4_perfmon_stop(struct vc4_dev *vc4, struct vc4_perfmon *perfmon,
0068               bool capture)
0069 {
0070     unsigned int i;
0071 
0072     if (WARN_ON_ONCE(vc4->is_vc5))
0073         return;
0074 
0075     if (WARN_ON_ONCE(!vc4->active_perfmon ||
0076              perfmon != vc4->active_perfmon))
0077         return;
0078 
0079     if (capture) {
0080         for (i = 0; i < perfmon->ncounters; i++)
0081             perfmon->counters[i] += V3D_READ(V3D_PCTR(i));
0082     }
0083 
0084     V3D_WRITE(V3D_PCTRE, 0);
0085     vc4->active_perfmon = NULL;
0086 }
0087 
0088 struct vc4_perfmon *vc4_perfmon_find(struct vc4_file *vc4file, int id)
0089 {
0090     struct vc4_dev *vc4 = vc4file->dev;
0091     struct vc4_perfmon *perfmon;
0092 
0093     if (WARN_ON_ONCE(vc4->is_vc5))
0094         return NULL;
0095 
0096     mutex_lock(&vc4file->perfmon.lock);
0097     perfmon = idr_find(&vc4file->perfmon.idr, id);
0098     vc4_perfmon_get(perfmon);
0099     mutex_unlock(&vc4file->perfmon.lock);
0100 
0101     return perfmon;
0102 }
0103 
0104 void vc4_perfmon_open_file(struct vc4_file *vc4file)
0105 {
0106     struct vc4_dev *vc4 = vc4file->dev;
0107 
0108     if (WARN_ON_ONCE(vc4->is_vc5))
0109         return;
0110 
0111     mutex_init(&vc4file->perfmon.lock);
0112     idr_init_base(&vc4file->perfmon.idr, VC4_PERFMONID_MIN);
0113     vc4file->dev = vc4;
0114 }
0115 
0116 static int vc4_perfmon_idr_del(int id, void *elem, void *data)
0117 {
0118     struct vc4_perfmon *perfmon = elem;
0119 
0120     vc4_perfmon_put(perfmon);
0121 
0122     return 0;
0123 }
0124 
0125 void vc4_perfmon_close_file(struct vc4_file *vc4file)
0126 {
0127     struct vc4_dev *vc4 = vc4file->dev;
0128 
0129     if (WARN_ON_ONCE(vc4->is_vc5))
0130         return;
0131 
0132     mutex_lock(&vc4file->perfmon.lock);
0133     idr_for_each(&vc4file->perfmon.idr, vc4_perfmon_idr_del, NULL);
0134     idr_destroy(&vc4file->perfmon.idr);
0135     mutex_unlock(&vc4file->perfmon.lock);
0136 }
0137 
0138 int vc4_perfmon_create_ioctl(struct drm_device *dev, void *data,
0139                  struct drm_file *file_priv)
0140 {
0141     struct vc4_dev *vc4 = to_vc4_dev(dev);
0142     struct vc4_file *vc4file = file_priv->driver_priv;
0143     struct drm_vc4_perfmon_create *req = data;
0144     struct vc4_perfmon *perfmon;
0145     unsigned int i;
0146     int ret;
0147 
0148     if (WARN_ON_ONCE(vc4->is_vc5))
0149         return -ENODEV;
0150 
0151     if (!vc4->v3d) {
0152         DRM_DEBUG("Creating perfmon no VC4 V3D probed\n");
0153         return -ENODEV;
0154     }
0155 
0156     /* Number of monitored counters cannot exceed HW limits. */
0157     if (req->ncounters > DRM_VC4_MAX_PERF_COUNTERS ||
0158         !req->ncounters)
0159         return -EINVAL;
0160 
0161     /* Make sure all events are valid. */
0162     for (i = 0; i < req->ncounters; i++) {
0163         if (req->events[i] >= VC4_PERFCNT_NUM_EVENTS)
0164             return -EINVAL;
0165     }
0166 
0167     perfmon = kzalloc(struct_size(perfmon, counters, req->ncounters),
0168               GFP_KERNEL);
0169     if (!perfmon)
0170         return -ENOMEM;
0171     perfmon->dev = vc4;
0172 
0173     for (i = 0; i < req->ncounters; i++)
0174         perfmon->events[i] = req->events[i];
0175 
0176     perfmon->ncounters = req->ncounters;
0177 
0178     refcount_set(&perfmon->refcnt, 1);
0179 
0180     mutex_lock(&vc4file->perfmon.lock);
0181     ret = idr_alloc(&vc4file->perfmon.idr, perfmon, VC4_PERFMONID_MIN,
0182             VC4_PERFMONID_MAX, GFP_KERNEL);
0183     mutex_unlock(&vc4file->perfmon.lock);
0184 
0185     if (ret < 0) {
0186         kfree(perfmon);
0187         return ret;
0188     }
0189 
0190     req->id = ret;
0191     return 0;
0192 }
0193 
0194 int vc4_perfmon_destroy_ioctl(struct drm_device *dev, void *data,
0195                   struct drm_file *file_priv)
0196 {
0197     struct vc4_dev *vc4 = to_vc4_dev(dev);
0198     struct vc4_file *vc4file = file_priv->driver_priv;
0199     struct drm_vc4_perfmon_destroy *req = data;
0200     struct vc4_perfmon *perfmon;
0201 
0202     if (WARN_ON_ONCE(vc4->is_vc5))
0203         return -ENODEV;
0204 
0205     if (!vc4->v3d) {
0206         DRM_DEBUG("Destroying perfmon no VC4 V3D probed\n");
0207         return -ENODEV;
0208     }
0209 
0210     mutex_lock(&vc4file->perfmon.lock);
0211     perfmon = idr_remove(&vc4file->perfmon.idr, req->id);
0212     mutex_unlock(&vc4file->perfmon.lock);
0213 
0214     if (!perfmon)
0215         return -EINVAL;
0216 
0217     vc4_perfmon_put(perfmon);
0218     return 0;
0219 }
0220 
0221 int vc4_perfmon_get_values_ioctl(struct drm_device *dev, void *data,
0222                  struct drm_file *file_priv)
0223 {
0224     struct vc4_dev *vc4 = to_vc4_dev(dev);
0225     struct vc4_file *vc4file = file_priv->driver_priv;
0226     struct drm_vc4_perfmon_get_values *req = data;
0227     struct vc4_perfmon *perfmon;
0228     int ret;
0229 
0230     if (WARN_ON_ONCE(vc4->is_vc5))
0231         return -ENODEV;
0232 
0233     if (!vc4->v3d) {
0234         DRM_DEBUG("Getting perfmon no VC4 V3D probed\n");
0235         return -ENODEV;
0236     }
0237 
0238     mutex_lock(&vc4file->perfmon.lock);
0239     perfmon = idr_find(&vc4file->perfmon.idr, req->id);
0240     vc4_perfmon_get(perfmon);
0241     mutex_unlock(&vc4file->perfmon.lock);
0242 
0243     if (!perfmon)
0244         return -EINVAL;
0245 
0246     if (copy_to_user(u64_to_user_ptr(req->values_ptr), perfmon->counters,
0247              perfmon->ncounters * sizeof(u64)))
0248         ret = -EFAULT;
0249     else
0250         ret = 0;
0251 
0252     vc4_perfmon_put(perfmon);
0253     return ret;
0254 }