linux/drivers/gpu/drm/v3d/v3d_perfmon.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright (C) 2021 Raspberry Pi
   4 */
   5
   6#include "v3d_drv.h"
   7#include "v3d_regs.h"
   8
   9#define V3D_PERFMONID_MIN       1
  10#define V3D_PERFMONID_MAX       U32_MAX
  11
  12void v3d_perfmon_get(struct v3d_perfmon *perfmon)
  13{
  14        if (perfmon)
  15                refcount_inc(&perfmon->refcnt);
  16}
  17
  18void v3d_perfmon_put(struct v3d_perfmon *perfmon)
  19{
  20        if (perfmon && refcount_dec_and_test(&perfmon->refcnt))
  21                kfree(perfmon);
  22}
  23
  24void v3d_perfmon_start(struct v3d_dev *v3d, struct v3d_perfmon *perfmon)
  25{
  26        unsigned int i;
  27        u32 mask;
  28        u8 ncounters = perfmon->ncounters;
  29
  30        if (WARN_ON_ONCE(!perfmon || v3d->active_perfmon))
  31                return;
  32
  33        mask = GENMASK(ncounters - 1, 0);
  34
  35        for (i = 0; i < ncounters; i++) {
  36                u32 source = i / 4;
  37                u32 channel = V3D_SET_FIELD(perfmon->counters[i], V3D_PCTR_S0);
  38
  39                i++;
  40                channel |= V3D_SET_FIELD(i < ncounters ? perfmon->counters[i] : 0,
  41                                         V3D_PCTR_S1);
  42                i++;
  43                channel |= V3D_SET_FIELD(i < ncounters ? perfmon->counters[i] : 0,
  44                                         V3D_PCTR_S2);
  45                i++;
  46                channel |= V3D_SET_FIELD(i < ncounters ? perfmon->counters[i] : 0,
  47                                         V3D_PCTR_S3);
  48                V3D_CORE_WRITE(0, V3D_V4_PCTR_0_SRC_X(source), channel);
  49        }
  50
  51        V3D_CORE_WRITE(0, V3D_V4_PCTR_0_CLR, mask);
  52        V3D_CORE_WRITE(0, V3D_PCTR_0_OVERFLOW, mask);
  53        V3D_CORE_WRITE(0, V3D_V4_PCTR_0_EN, mask);
  54
  55        v3d->active_perfmon = perfmon;
  56}
  57
  58void v3d_perfmon_stop(struct v3d_dev *v3d, struct v3d_perfmon *perfmon,
  59                      bool capture)
  60{
  61        unsigned int i;
  62
  63        if (!perfmon || !v3d->active_perfmon)
  64                return;
  65
  66        mutex_lock(&perfmon->lock);
  67        if (perfmon != v3d->active_perfmon) {
  68                mutex_unlock(&perfmon->lock);
  69                return;
  70        }
  71
  72        if (capture)
  73                for (i = 0; i < perfmon->ncounters; i++)
  74                        perfmon->values[i] += V3D_CORE_READ(0, V3D_PCTR_0_PCTRX(i));
  75
  76        V3D_CORE_WRITE(0, V3D_V4_PCTR_0_EN, 0);
  77
  78        v3d->active_perfmon = NULL;
  79        mutex_unlock(&perfmon->lock);
  80}
  81
  82struct v3d_perfmon *v3d_perfmon_find(struct v3d_file_priv *v3d_priv, int id)
  83{
  84        struct v3d_perfmon *perfmon;
  85
  86        mutex_lock(&v3d_priv->perfmon.lock);
  87        perfmon = idr_find(&v3d_priv->perfmon.idr, id);
  88        v3d_perfmon_get(perfmon);
  89        mutex_unlock(&v3d_priv->perfmon.lock);
  90
  91        return perfmon;
  92}
  93
  94void v3d_perfmon_open_file(struct v3d_file_priv *v3d_priv)
  95{
  96        mutex_init(&v3d_priv->perfmon.lock);
  97        idr_init(&v3d_priv->perfmon.idr);
  98}
  99
 100static int v3d_perfmon_idr_del(int id, void *elem, void *data)
 101{
 102        struct v3d_perfmon *perfmon = elem;
 103
 104        v3d_perfmon_put(perfmon);
 105
 106        return 0;
 107}
 108
 109void v3d_perfmon_close_file(struct v3d_file_priv *v3d_priv)
 110{
 111        mutex_lock(&v3d_priv->perfmon.lock);
 112        idr_for_each(&v3d_priv->perfmon.idr, v3d_perfmon_idr_del, NULL);
 113        idr_destroy(&v3d_priv->perfmon.idr);
 114        mutex_unlock(&v3d_priv->perfmon.lock);
 115}
 116
 117int v3d_perfmon_create_ioctl(struct drm_device *dev, void *data,
 118                             struct drm_file *file_priv)
 119{
 120        struct v3d_file_priv *v3d_priv = file_priv->driver_priv;
 121        struct drm_v3d_perfmon_create *req = data;
 122        struct v3d_perfmon *perfmon;
 123        unsigned int i;
 124        int ret;
 125
 126        /* Number of monitored counters cannot exceed HW limits. */
 127        if (req->ncounters > DRM_V3D_MAX_PERF_COUNTERS ||
 128            !req->ncounters)
 129                return -EINVAL;
 130
 131        /* Make sure all counters are valid. */
 132        for (i = 0; i < req->ncounters; i++) {
 133                if (req->counters[i] >= V3D_PERFCNT_NUM)
 134                        return -EINVAL;
 135        }
 136
 137        perfmon = kzalloc(struct_size(perfmon, values, req->ncounters),
 138                          GFP_KERNEL);
 139        if (!perfmon)
 140                return -ENOMEM;
 141
 142        for (i = 0; i < req->ncounters; i++)
 143                perfmon->counters[i] = req->counters[i];
 144
 145        perfmon->ncounters = req->ncounters;
 146
 147        refcount_set(&perfmon->refcnt, 1);
 148        mutex_init(&perfmon->lock);
 149
 150        mutex_lock(&v3d_priv->perfmon.lock);
 151        ret = idr_alloc(&v3d_priv->perfmon.idr, perfmon, V3D_PERFMONID_MIN,
 152                        V3D_PERFMONID_MAX, GFP_KERNEL);
 153        mutex_unlock(&v3d_priv->perfmon.lock);
 154
 155        if (ret < 0) {
 156                kfree(perfmon);
 157                return ret;
 158        }
 159
 160        req->id = ret;
 161
 162        return 0;
 163}
 164
 165int v3d_perfmon_destroy_ioctl(struct drm_device *dev, void *data,
 166                              struct drm_file *file_priv)
 167{
 168        struct v3d_file_priv *v3d_priv = file_priv->driver_priv;
 169        struct drm_v3d_perfmon_destroy *req = data;
 170        struct v3d_perfmon *perfmon;
 171
 172        mutex_lock(&v3d_priv->perfmon.lock);
 173        perfmon = idr_remove(&v3d_priv->perfmon.idr, req->id);
 174        mutex_unlock(&v3d_priv->perfmon.lock);
 175
 176        if (!perfmon)
 177                return -EINVAL;
 178
 179        v3d_perfmon_put(perfmon);
 180
 181        return 0;
 182}
 183
 184int v3d_perfmon_get_values_ioctl(struct drm_device *dev, void *data,
 185                                 struct drm_file *file_priv)
 186{
 187        struct v3d_dev *v3d = to_v3d_dev(dev);
 188        struct v3d_file_priv *v3d_priv = file_priv->driver_priv;
 189        struct drm_v3d_perfmon_get_values *req = data;
 190        struct v3d_perfmon *perfmon;
 191        int ret = 0;
 192
 193        if (req->pad != 0)
 194                return -EINVAL;
 195
 196        mutex_lock(&v3d_priv->perfmon.lock);
 197        perfmon = idr_find(&v3d_priv->perfmon.idr, req->id);
 198        v3d_perfmon_get(perfmon);
 199        mutex_unlock(&v3d_priv->perfmon.lock);
 200
 201        if (!perfmon)
 202                return -EINVAL;
 203
 204        v3d_perfmon_stop(v3d, perfmon, true);
 205
 206        if (copy_to_user(u64_to_user_ptr(req->values_ptr), perfmon->values,
 207                         perfmon->ncounters * sizeof(u64)))
 208                ret = -EFAULT;
 209
 210        v3d_perfmon_put(perfmon);
 211
 212        return ret;
 213}
 214