diff options
Diffstat (limited to 'drivers/gpu/drm/vc4/vc4_perfmon.c')
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_perfmon.c | 188 |
1 files changed, 188 insertions, 0 deletions
diff --git a/drivers/gpu/drm/vc4/vc4_perfmon.c b/drivers/gpu/drm/vc4/vc4_perfmon.c new file mode 100644 index 000000000000..437e7a27f21d --- /dev/null +++ b/drivers/gpu/drm/vc4/vc4_perfmon.c | |||
@@ -0,0 +1,188 @@ | |||
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 | if (perfmon) | ||
21 | refcount_inc(&perfmon->refcnt); | ||
22 | } | ||
23 | |||
24 | void vc4_perfmon_put(struct vc4_perfmon *perfmon) | ||
25 | { | ||
26 | if (perfmon && refcount_dec_and_test(&perfmon->refcnt)) | ||
27 | kfree(perfmon); | ||
28 | } | ||
29 | |||
30 | void vc4_perfmon_start(struct vc4_dev *vc4, struct vc4_perfmon *perfmon) | ||
31 | { | ||
32 | unsigned int i; | ||
33 | u32 mask; | ||
34 | |||
35 | if (WARN_ON_ONCE(!perfmon || vc4->active_perfmon)) | ||
36 | return; | ||
37 | |||
38 | for (i = 0; i < perfmon->ncounters; i++) | ||
39 | V3D_WRITE(V3D_PCTRS(i), perfmon->events[i]); | ||
40 | |||
41 | mask = GENMASK(perfmon->ncounters - 1, 0); | ||
42 | V3D_WRITE(V3D_PCTRC, mask); | ||
43 | V3D_WRITE(V3D_PCTRE, V3D_PCTRE_EN | mask); | ||
44 | vc4->active_perfmon = perfmon; | ||
45 | } | ||
46 | |||
47 | void vc4_perfmon_stop(struct vc4_dev *vc4, struct vc4_perfmon *perfmon, | ||
48 | bool capture) | ||
49 | { | ||
50 | unsigned int i; | ||
51 | |||
52 | if (WARN_ON_ONCE(!vc4->active_perfmon || | ||
53 | perfmon != vc4->active_perfmon)) | ||
54 | return; | ||
55 | |||
56 | if (capture) { | ||
57 | for (i = 0; i < perfmon->ncounters; i++) | ||
58 | perfmon->counters[i] += V3D_READ(V3D_PCTR(i)); | ||
59 | } | ||
60 | |||
61 | V3D_WRITE(V3D_PCTRE, 0); | ||
62 | vc4->active_perfmon = NULL; | ||
63 | } | ||
64 | |||
65 | struct vc4_perfmon *vc4_perfmon_find(struct vc4_file *vc4file, int id) | ||
66 | { | ||
67 | struct vc4_perfmon *perfmon; | ||
68 | |||
69 | mutex_lock(&vc4file->perfmon.lock); | ||
70 | perfmon = idr_find(&vc4file->perfmon.idr, id); | ||
71 | vc4_perfmon_get(perfmon); | ||
72 | mutex_unlock(&vc4file->perfmon.lock); | ||
73 | |||
74 | return perfmon; | ||
75 | } | ||
76 | |||
77 | void vc4_perfmon_open_file(struct vc4_file *vc4file) | ||
78 | { | ||
79 | mutex_init(&vc4file->perfmon.lock); | ||
80 | idr_init(&vc4file->perfmon.idr); | ||
81 | } | ||
82 | |||
83 | static int vc4_perfmon_idr_del(int id, void *elem, void *data) | ||
84 | { | ||
85 | struct vc4_perfmon *perfmon = elem; | ||
86 | |||
87 | vc4_perfmon_put(perfmon); | ||
88 | |||
89 | return 0; | ||
90 | } | ||
91 | |||
92 | void vc4_perfmon_close_file(struct vc4_file *vc4file) | ||
93 | { | ||
94 | mutex_lock(&vc4file->perfmon.lock); | ||
95 | idr_for_each(&vc4file->perfmon.idr, vc4_perfmon_idr_del, NULL); | ||
96 | idr_destroy(&vc4file->perfmon.idr); | ||
97 | mutex_unlock(&vc4file->perfmon.lock); | ||
98 | } | ||
99 | |||
100 | int vc4_perfmon_create_ioctl(struct drm_device *dev, void *data, | ||
101 | struct drm_file *file_priv) | ||
102 | { | ||
103 | struct vc4_file *vc4file = file_priv->driver_priv; | ||
104 | struct drm_vc4_perfmon_create *req = data; | ||
105 | struct vc4_perfmon *perfmon; | ||
106 | unsigned int i; | ||
107 | int ret; | ||
108 | |||
109 | /* Number of monitored counters cannot exceed HW limits. */ | ||
110 | if (req->ncounters > DRM_VC4_MAX_PERF_COUNTERS || | ||
111 | !req->ncounters) | ||
112 | return -EINVAL; | ||
113 | |||
114 | /* Make sure all events are valid. */ | ||
115 | for (i = 0; i < req->ncounters; i++) { | ||
116 | if (req->events[i] >= VC4_PERFCNT_NUM_EVENTS) | ||
117 | return -EINVAL; | ||
118 | } | ||
119 | |||
120 | perfmon = kzalloc(sizeof(*perfmon) + (req->ncounters * sizeof(u64)), | ||
121 | GFP_KERNEL); | ||
122 | if (!perfmon) | ||
123 | return -ENOMEM; | ||
124 | |||
125 | for (i = 0; i < req->ncounters; i++) | ||
126 | perfmon->events[i] = req->events[i]; | ||
127 | |||
128 | perfmon->ncounters = req->ncounters; | ||
129 | |||
130 | refcount_set(&perfmon->refcnt, 1); | ||
131 | |||
132 | mutex_lock(&vc4file->perfmon.lock); | ||
133 | ret = idr_alloc(&vc4file->perfmon.idr, perfmon, VC4_PERFMONID_MIN, | ||
134 | VC4_PERFMONID_MAX, GFP_KERNEL); | ||
135 | mutex_unlock(&vc4file->perfmon.lock); | ||
136 | |||
137 | if (ret < 0) { | ||
138 | kfree(perfmon); | ||
139 | return ret; | ||
140 | } | ||
141 | |||
142 | req->id = ret; | ||
143 | return 0; | ||
144 | } | ||
145 | |||
146 | int vc4_perfmon_destroy_ioctl(struct drm_device *dev, void *data, | ||
147 | struct drm_file *file_priv) | ||
148 | { | ||
149 | struct vc4_file *vc4file = file_priv->driver_priv; | ||
150 | struct drm_vc4_perfmon_destroy *req = data; | ||
151 | struct vc4_perfmon *perfmon; | ||
152 | |||
153 | mutex_lock(&vc4file->perfmon.lock); | ||
154 | perfmon = idr_remove(&vc4file->perfmon.idr, req->id); | ||
155 | mutex_unlock(&vc4file->perfmon.lock); | ||
156 | |||
157 | if (!perfmon) | ||
158 | return -EINVAL; | ||
159 | |||
160 | vc4_perfmon_put(perfmon); | ||
161 | return 0; | ||
162 | } | ||
163 | |||
164 | int vc4_perfmon_get_values_ioctl(struct drm_device *dev, void *data, | ||
165 | struct drm_file *file_priv) | ||
166 | { | ||
167 | struct vc4_file *vc4file = file_priv->driver_priv; | ||
168 | struct drm_vc4_perfmon_get_values *req = data; | ||
169 | struct vc4_perfmon *perfmon; | ||
170 | int ret; | ||
171 | |||
172 | mutex_lock(&vc4file->perfmon.lock); | ||
173 | perfmon = idr_find(&vc4file->perfmon.idr, req->id); | ||
174 | vc4_perfmon_get(perfmon); | ||
175 | mutex_unlock(&vc4file->perfmon.lock); | ||
176 | |||
177 | if (!perfmon) | ||
178 | return -EINVAL; | ||
179 | |||
180 | if (copy_to_user(u64_to_user_ptr(req->values_ptr), perfmon->counters, | ||
181 | perfmon->ncounters * sizeof(u64))) | ||
182 | ret = -EFAULT; | ||
183 | else | ||
184 | ret = 0; | ||
185 | |||
186 | vc4_perfmon_put(perfmon); | ||
187 | return ret; | ||
188 | } | ||