xref: /openbmc/linux/drivers/gpu/drm/v3d/v3d_perfmon.c (revision fd5e9fccbd504c5179ab57ff695c610bca8809d6)
126a4dc29SJuan A. Suarez Romero // SPDX-License-Identifier: GPL-2.0
226a4dc29SJuan A. Suarez Romero /*
326a4dc29SJuan A. Suarez Romero  * Copyright (C) 2021 Raspberry Pi
426a4dc29SJuan A. Suarez Romero  */
526a4dc29SJuan A. Suarez Romero 
626a4dc29SJuan A. Suarez Romero #include "v3d_drv.h"
726a4dc29SJuan A. Suarez Romero #include "v3d_regs.h"
826a4dc29SJuan A. Suarez Romero 
926a4dc29SJuan A. Suarez Romero #define V3D_PERFMONID_MIN	1
1026a4dc29SJuan A. Suarez Romero #define V3D_PERFMONID_MAX	U32_MAX
1126a4dc29SJuan A. Suarez Romero 
v3d_perfmon_get(struct v3d_perfmon * perfmon)1226a4dc29SJuan A. Suarez Romero void v3d_perfmon_get(struct v3d_perfmon *perfmon)
1326a4dc29SJuan A. Suarez Romero {
1426a4dc29SJuan A. Suarez Romero 	if (perfmon)
1526a4dc29SJuan A. Suarez Romero 		refcount_inc(&perfmon->refcnt);
1626a4dc29SJuan A. Suarez Romero }
1726a4dc29SJuan A. Suarez Romero 
v3d_perfmon_put(struct v3d_perfmon * perfmon)1826a4dc29SJuan A. Suarez Romero void v3d_perfmon_put(struct v3d_perfmon *perfmon)
1926a4dc29SJuan A. Suarez Romero {
20d23a6e3fSMaíra Canal 	if (perfmon && refcount_dec_and_test(&perfmon->refcnt)) {
21d23a6e3fSMaíra Canal 		mutex_destroy(&perfmon->lock);
2226a4dc29SJuan A. Suarez Romero 		kfree(perfmon);
2326a4dc29SJuan A. Suarez Romero 	}
24d23a6e3fSMaíra Canal }
2526a4dc29SJuan A. Suarez Romero 
v3d_perfmon_start(struct v3d_dev * v3d,struct v3d_perfmon * perfmon)2626a4dc29SJuan A. Suarez Romero void v3d_perfmon_start(struct v3d_dev *v3d, struct v3d_perfmon *perfmon)
2726a4dc29SJuan A. Suarez Romero {
2826a4dc29SJuan A. Suarez Romero 	unsigned int i;
2926a4dc29SJuan A. Suarez Romero 	u32 mask;
30ce7a1ecfSColin Ian King 	u8 ncounters;
3126a4dc29SJuan A. Suarez Romero 
3226a4dc29SJuan A. Suarez Romero 	if (WARN_ON_ONCE(!perfmon || v3d->active_perfmon))
3326a4dc29SJuan A. Suarez Romero 		return;
3426a4dc29SJuan A. Suarez Romero 
35ce7a1ecfSColin Ian King 	ncounters = perfmon->ncounters;
3626a4dc29SJuan A. Suarez Romero 	mask = GENMASK(ncounters - 1, 0);
3726a4dc29SJuan A. Suarez Romero 
3826a4dc29SJuan A. Suarez Romero 	for (i = 0; i < ncounters; i++) {
3926a4dc29SJuan A. Suarez Romero 		u32 source = i / 4;
4026a4dc29SJuan A. Suarez Romero 		u32 channel = V3D_SET_FIELD(perfmon->counters[i], V3D_PCTR_S0);
4126a4dc29SJuan A. Suarez Romero 
4226a4dc29SJuan A. Suarez Romero 		i++;
4326a4dc29SJuan A. Suarez Romero 		channel |= V3D_SET_FIELD(i < ncounters ? perfmon->counters[i] : 0,
4426a4dc29SJuan A. Suarez Romero 					 V3D_PCTR_S1);
4526a4dc29SJuan A. Suarez Romero 		i++;
4626a4dc29SJuan A. Suarez Romero 		channel |= V3D_SET_FIELD(i < ncounters ? perfmon->counters[i] : 0,
4726a4dc29SJuan A. Suarez Romero 					 V3D_PCTR_S2);
4826a4dc29SJuan A. Suarez Romero 		i++;
4926a4dc29SJuan A. Suarez Romero 		channel |= V3D_SET_FIELD(i < ncounters ? perfmon->counters[i] : 0,
5026a4dc29SJuan A. Suarez Romero 					 V3D_PCTR_S3);
5126a4dc29SJuan A. Suarez Romero 		V3D_CORE_WRITE(0, V3D_V4_PCTR_0_SRC_X(source), channel);
5226a4dc29SJuan A. Suarez Romero 	}
5326a4dc29SJuan A. Suarez Romero 
546acd8e87SMaíra Canal 	V3D_CORE_WRITE(0, V3D_V4_PCTR_0_EN, mask);
5526a4dc29SJuan A. Suarez Romero 	V3D_CORE_WRITE(0, V3D_V4_PCTR_0_CLR, mask);
5626a4dc29SJuan A. Suarez Romero 	V3D_CORE_WRITE(0, V3D_PCTR_0_OVERFLOW, mask);
5726a4dc29SJuan A. Suarez Romero 
5826a4dc29SJuan A. Suarez Romero 	v3d->active_perfmon = perfmon;
5926a4dc29SJuan A. Suarez Romero }
6026a4dc29SJuan A. Suarez Romero 
v3d_perfmon_stop(struct v3d_dev * v3d,struct v3d_perfmon * perfmon,bool capture)6126a4dc29SJuan A. Suarez Romero void v3d_perfmon_stop(struct v3d_dev *v3d, struct v3d_perfmon *perfmon,
6226a4dc29SJuan A. Suarez Romero 		      bool capture)
6326a4dc29SJuan A. Suarez Romero {
6426a4dc29SJuan A. Suarez Romero 	unsigned int i;
6526a4dc29SJuan A. Suarez Romero 
6626a4dc29SJuan A. Suarez Romero 	if (!perfmon || !v3d->active_perfmon)
6726a4dc29SJuan A. Suarez Romero 		return;
6826a4dc29SJuan A. Suarez Romero 
6926a4dc29SJuan A. Suarez Romero 	mutex_lock(&perfmon->lock);
7026a4dc29SJuan A. Suarez Romero 	if (perfmon != v3d->active_perfmon) {
7126a4dc29SJuan A. Suarez Romero 		mutex_unlock(&perfmon->lock);
7226a4dc29SJuan A. Suarez Romero 		return;
7326a4dc29SJuan A. Suarez Romero 	}
7426a4dc29SJuan A. Suarez Romero 
7526a4dc29SJuan A. Suarez Romero 	if (capture)
7626a4dc29SJuan A. Suarez Romero 		for (i = 0; i < perfmon->ncounters; i++)
7726a4dc29SJuan A. Suarez Romero 			perfmon->values[i] += V3D_CORE_READ(0, V3D_PCTR_0_PCTRX(i));
7826a4dc29SJuan A. Suarez Romero 
7926a4dc29SJuan A. Suarez Romero 	V3D_CORE_WRITE(0, V3D_V4_PCTR_0_EN, 0);
8026a4dc29SJuan A. Suarez Romero 
8126a4dc29SJuan A. Suarez Romero 	v3d->active_perfmon = NULL;
8226a4dc29SJuan A. Suarez Romero 	mutex_unlock(&perfmon->lock);
8326a4dc29SJuan A. Suarez Romero }
8426a4dc29SJuan A. Suarez Romero 
v3d_perfmon_find(struct v3d_file_priv * v3d_priv,int id)8526a4dc29SJuan A. Suarez Romero struct v3d_perfmon *v3d_perfmon_find(struct v3d_file_priv *v3d_priv, int id)
8626a4dc29SJuan A. Suarez Romero {
8726a4dc29SJuan A. Suarez Romero 	struct v3d_perfmon *perfmon;
8826a4dc29SJuan A. Suarez Romero 
8926a4dc29SJuan A. Suarez Romero 	mutex_lock(&v3d_priv->perfmon.lock);
9026a4dc29SJuan A. Suarez Romero 	perfmon = idr_find(&v3d_priv->perfmon.idr, id);
9126a4dc29SJuan A. Suarez Romero 	v3d_perfmon_get(perfmon);
9226a4dc29SJuan A. Suarez Romero 	mutex_unlock(&v3d_priv->perfmon.lock);
9326a4dc29SJuan A. Suarez Romero 
9426a4dc29SJuan A. Suarez Romero 	return perfmon;
9526a4dc29SJuan A. Suarez Romero }
9626a4dc29SJuan A. Suarez Romero 
v3d_perfmon_open_file(struct v3d_file_priv * v3d_priv)9726a4dc29SJuan A. Suarez Romero void v3d_perfmon_open_file(struct v3d_file_priv *v3d_priv)
9826a4dc29SJuan A. Suarez Romero {
9926a4dc29SJuan A. Suarez Romero 	mutex_init(&v3d_priv->perfmon.lock);
100604bda63SDanilo Krummrich 	idr_init_base(&v3d_priv->perfmon.idr, 1);
10126a4dc29SJuan A. Suarez Romero }
10226a4dc29SJuan A. Suarez Romero 
v3d_perfmon_idr_del(int id,void * elem,void * data)10326a4dc29SJuan A. Suarez Romero static int v3d_perfmon_idr_del(int id, void *elem, void *data)
10426a4dc29SJuan A. Suarez Romero {
10526a4dc29SJuan A. Suarez Romero 	struct v3d_perfmon *perfmon = elem;
10607c51108SMaíra Canal 	struct v3d_dev *v3d = (struct v3d_dev *)data;
10707c51108SMaíra Canal 
10807c51108SMaíra Canal 	/* If the active perfmon is being destroyed, stop it first */
10907c51108SMaíra Canal 	if (perfmon == v3d->active_perfmon)
11007c51108SMaíra Canal 		v3d_perfmon_stop(v3d, perfmon, false);
11126a4dc29SJuan A. Suarez Romero 
11226a4dc29SJuan A. Suarez Romero 	v3d_perfmon_put(perfmon);
11326a4dc29SJuan A. Suarez Romero 
11426a4dc29SJuan A. Suarez Romero 	return 0;
11526a4dc29SJuan A. Suarez Romero }
11626a4dc29SJuan A. Suarez Romero 
v3d_perfmon_close_file(struct v3d_file_priv * v3d_priv)11726a4dc29SJuan A. Suarez Romero void v3d_perfmon_close_file(struct v3d_file_priv *v3d_priv)
11826a4dc29SJuan A. Suarez Romero {
11907c51108SMaíra Canal 	struct v3d_dev *v3d = v3d_priv->v3d;
12007c51108SMaíra Canal 
12126a4dc29SJuan A. Suarez Romero 	mutex_lock(&v3d_priv->perfmon.lock);
12207c51108SMaíra Canal 	idr_for_each(&v3d_priv->perfmon.idr, v3d_perfmon_idr_del, v3d);
12326a4dc29SJuan A. Suarez Romero 	idr_destroy(&v3d_priv->perfmon.idr);
12426a4dc29SJuan A. Suarez Romero 	mutex_unlock(&v3d_priv->perfmon.lock);
125d23a6e3fSMaíra Canal 	mutex_destroy(&v3d_priv->perfmon.lock);
12626a4dc29SJuan A. Suarez Romero }
12726a4dc29SJuan A. Suarez Romero 
v3d_perfmon_create_ioctl(struct drm_device * dev,void * data,struct drm_file * file_priv)12826a4dc29SJuan A. Suarez Romero int v3d_perfmon_create_ioctl(struct drm_device *dev, void *data,
12926a4dc29SJuan A. Suarez Romero 			     struct drm_file *file_priv)
13026a4dc29SJuan A. Suarez Romero {
13126a4dc29SJuan A. Suarez Romero 	struct v3d_file_priv *v3d_priv = file_priv->driver_priv;
13226a4dc29SJuan A. Suarez Romero 	struct drm_v3d_perfmon_create *req = data;
13326a4dc29SJuan A. Suarez Romero 	struct v3d_perfmon *perfmon;
13426a4dc29SJuan A. Suarez Romero 	unsigned int i;
13526a4dc29SJuan A. Suarez Romero 	int ret;
13626a4dc29SJuan A. Suarez Romero 
13726a4dc29SJuan A. Suarez Romero 	/* Number of monitored counters cannot exceed HW limits. */
13826a4dc29SJuan A. Suarez Romero 	if (req->ncounters > DRM_V3D_MAX_PERF_COUNTERS ||
13926a4dc29SJuan A. Suarez Romero 	    !req->ncounters)
14026a4dc29SJuan A. Suarez Romero 		return -EINVAL;
14126a4dc29SJuan A. Suarez Romero 
14226a4dc29SJuan A. Suarez Romero 	/* Make sure all counters are valid. */
14326a4dc29SJuan A. Suarez Romero 	for (i = 0; i < req->ncounters; i++) {
14426a4dc29SJuan A. Suarez Romero 		if (req->counters[i] >= V3D_PERFCNT_NUM)
14526a4dc29SJuan A. Suarez Romero 			return -EINVAL;
14626a4dc29SJuan A. Suarez Romero 	}
14726a4dc29SJuan A. Suarez Romero 
14826a4dc29SJuan A. Suarez Romero 	perfmon = kzalloc(struct_size(perfmon, values, req->ncounters),
14926a4dc29SJuan A. Suarez Romero 			  GFP_KERNEL);
15026a4dc29SJuan A. Suarez Romero 	if (!perfmon)
15126a4dc29SJuan A. Suarez Romero 		return -ENOMEM;
15226a4dc29SJuan A. Suarez Romero 
15326a4dc29SJuan A. Suarez Romero 	for (i = 0; i < req->ncounters; i++)
15426a4dc29SJuan A. Suarez Romero 		perfmon->counters[i] = req->counters[i];
15526a4dc29SJuan A. Suarez Romero 
15626a4dc29SJuan A. Suarez Romero 	perfmon->ncounters = req->ncounters;
15726a4dc29SJuan A. Suarez Romero 
15826a4dc29SJuan A. Suarez Romero 	refcount_set(&perfmon->refcnt, 1);
15926a4dc29SJuan A. Suarez Romero 	mutex_init(&perfmon->lock);
16026a4dc29SJuan A. Suarez Romero 
16126a4dc29SJuan A. Suarez Romero 	mutex_lock(&v3d_priv->perfmon.lock);
16226a4dc29SJuan A. Suarez Romero 	ret = idr_alloc(&v3d_priv->perfmon.idr, perfmon, V3D_PERFMONID_MIN,
16326a4dc29SJuan A. Suarez Romero 			V3D_PERFMONID_MAX, GFP_KERNEL);
16426a4dc29SJuan A. Suarez Romero 	mutex_unlock(&v3d_priv->perfmon.lock);
16526a4dc29SJuan A. Suarez Romero 
16626a4dc29SJuan A. Suarez Romero 	if (ret < 0) {
167d23a6e3fSMaíra Canal 		mutex_destroy(&perfmon->lock);
16826a4dc29SJuan A. Suarez Romero 		kfree(perfmon);
16926a4dc29SJuan A. Suarez Romero 		return ret;
17026a4dc29SJuan A. Suarez Romero 	}
17126a4dc29SJuan A. Suarez Romero 
17226a4dc29SJuan A. Suarez Romero 	req->id = ret;
17326a4dc29SJuan A. Suarez Romero 
17426a4dc29SJuan A. Suarez Romero 	return 0;
17526a4dc29SJuan A. Suarez Romero }
17626a4dc29SJuan A. Suarez Romero 
v3d_perfmon_destroy_ioctl(struct drm_device * dev,void * data,struct drm_file * file_priv)17726a4dc29SJuan A. Suarez Romero int v3d_perfmon_destroy_ioctl(struct drm_device *dev, void *data,
17826a4dc29SJuan A. Suarez Romero 			      struct drm_file *file_priv)
17926a4dc29SJuan A. Suarez Romero {
18026a4dc29SJuan A. Suarez Romero 	struct v3d_file_priv *v3d_priv = file_priv->driver_priv;
18126a4dc29SJuan A. Suarez Romero 	struct drm_v3d_perfmon_destroy *req = data;
182*eb0e0ecaSChristian Gmeiner 	struct v3d_dev *v3d = v3d_priv->v3d;
18326a4dc29SJuan A. Suarez Romero 	struct v3d_perfmon *perfmon;
18426a4dc29SJuan A. Suarez Romero 
18526a4dc29SJuan A. Suarez Romero 	mutex_lock(&v3d_priv->perfmon.lock);
18626a4dc29SJuan A. Suarez Romero 	perfmon = idr_remove(&v3d_priv->perfmon.idr, req->id);
18726a4dc29SJuan A. Suarez Romero 	mutex_unlock(&v3d_priv->perfmon.lock);
18826a4dc29SJuan A. Suarez Romero 
18926a4dc29SJuan A. Suarez Romero 	if (!perfmon)
19026a4dc29SJuan A. Suarez Romero 		return -EINVAL;
19126a4dc29SJuan A. Suarez Romero 
192*eb0e0ecaSChristian Gmeiner 	/* If the active perfmon is being destroyed, stop it first */
193*eb0e0ecaSChristian Gmeiner 	if (perfmon == v3d->active_perfmon)
194*eb0e0ecaSChristian Gmeiner 		v3d_perfmon_stop(v3d, perfmon, false);
195*eb0e0ecaSChristian Gmeiner 
19626a4dc29SJuan A. Suarez Romero 	v3d_perfmon_put(perfmon);
19726a4dc29SJuan A. Suarez Romero 
19826a4dc29SJuan A. Suarez Romero 	return 0;
19926a4dc29SJuan A. Suarez Romero }
20026a4dc29SJuan A. Suarez Romero 
v3d_perfmon_get_values_ioctl(struct drm_device * dev,void * data,struct drm_file * file_priv)20126a4dc29SJuan A. Suarez Romero int v3d_perfmon_get_values_ioctl(struct drm_device *dev, void *data,
20226a4dc29SJuan A. Suarez Romero 				 struct drm_file *file_priv)
20326a4dc29SJuan A. Suarez Romero {
20426a4dc29SJuan A. Suarez Romero 	struct v3d_dev *v3d = to_v3d_dev(dev);
20526a4dc29SJuan A. Suarez Romero 	struct v3d_file_priv *v3d_priv = file_priv->driver_priv;
20626a4dc29SJuan A. Suarez Romero 	struct drm_v3d_perfmon_get_values *req = data;
20726a4dc29SJuan A. Suarez Romero 	struct v3d_perfmon *perfmon;
20826a4dc29SJuan A. Suarez Romero 	int ret = 0;
20926a4dc29SJuan A. Suarez Romero 
21026a4dc29SJuan A. Suarez Romero 	if (req->pad != 0)
21126a4dc29SJuan A. Suarez Romero 		return -EINVAL;
21226a4dc29SJuan A. Suarez Romero 
21326a4dc29SJuan A. Suarez Romero 	mutex_lock(&v3d_priv->perfmon.lock);
21426a4dc29SJuan A. Suarez Romero 	perfmon = idr_find(&v3d_priv->perfmon.idr, req->id);
21526a4dc29SJuan A. Suarez Romero 	v3d_perfmon_get(perfmon);
21626a4dc29SJuan A. Suarez Romero 	mutex_unlock(&v3d_priv->perfmon.lock);
21726a4dc29SJuan A. Suarez Romero 
21826a4dc29SJuan A. Suarez Romero 	if (!perfmon)
21926a4dc29SJuan A. Suarez Romero 		return -EINVAL;
22026a4dc29SJuan A. Suarez Romero 
22126a4dc29SJuan A. Suarez Romero 	v3d_perfmon_stop(v3d, perfmon, true);
22226a4dc29SJuan A. Suarez Romero 
22326a4dc29SJuan A. Suarez Romero 	if (copy_to_user(u64_to_user_ptr(req->values_ptr), perfmon->values,
22426a4dc29SJuan A. Suarez Romero 			 perfmon->ncounters * sizeof(u64)))
22526a4dc29SJuan A. Suarez Romero 		ret = -EFAULT;
22626a4dc29SJuan A. Suarez Romero 
22726a4dc29SJuan A. Suarez Romero 	v3d_perfmon_put(perfmon);
22826a4dc29SJuan A. Suarez Romero 
22926a4dc29SJuan A. Suarez Romero 	return ret;
23026a4dc29SJuan A. Suarez Romero }
231