summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/nvgpu/os/posix/io.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/nvgpu/os/posix/io.c')
-rw-r--r--drivers/gpu/nvgpu/os/posix/io.c316
1 files changed, 300 insertions, 16 deletions
diff --git a/drivers/gpu/nvgpu/os/posix/io.c b/drivers/gpu/nvgpu/os/posix/io.c
index 7bab8af6..8369e73f 100644
--- a/drivers/gpu/nvgpu/os/posix/io.c
+++ b/drivers/gpu/nvgpu/os/posix/io.c
@@ -24,15 +24,45 @@
24#include <nvgpu/io_usermode.h> 24#include <nvgpu/io_usermode.h>
25#include <nvgpu/bug.h> 25#include <nvgpu/bug.h>
26 26
27#include <nvgpu/posix/io.h>
28
29#include "os_posix.h"
30
31
27/* 32/*
28 * For now none of these make sense to execute in userspace. Eventually we 33 * This function sets the IO callbacks to the passed set of callbacks. It
29 * may want to use these to verify certain register read/write sequences 34 * returns the value of the old IO callback struct pointer. This function
30 * but for now, just hang. 35 * cannot fail.
36 *
37 * This is expected to be called from modules to set up their IO interaction.
31 */ 38 */
39struct nvgpu_posix_io_callbacks *nvgpu_posix_register_io(
40 struct gk20a *g,
41 struct nvgpu_posix_io_callbacks *io_callbacks)
42{
43 struct nvgpu_os_posix *p = nvgpu_os_posix_from_gk20a(g);
44 struct nvgpu_posix_io_callbacks *old_io = p->callbacks;
45
46 p->callbacks = io_callbacks;
47
48 return old_io;
49}
32 50
33void nvgpu_writel(struct gk20a *g, u32 r, u32 v) 51void nvgpu_writel(struct gk20a *g, u32 r, u32 v)
34{ 52{
35 BUG(); 53 struct nvgpu_posix_io_callbacks *callbacks =
54 nvgpu_os_posix_from_gk20a(g)->callbacks;
55
56 struct nvgpu_reg_access access = {
57 .addr = r,
58 .value = v
59 };
60
61 if (callbacks == NULL || callbacks->writel == NULL) {
62 BUG();
63 }
64
65 callbacks->writel(g, &access);
36} 66}
37 67
38void nvgpu_writel_relaxed(struct gk20a *g, u32 r, u32 v) 68void nvgpu_writel_relaxed(struct gk20a *g, u32 r, u32 v)
@@ -42,16 +72,21 @@ void nvgpu_writel_relaxed(struct gk20a *g, u32 r, u32 v)
42 72
43u32 nvgpu_readl(struct gk20a *g, u32 r) 73u32 nvgpu_readl(struct gk20a *g, u32 r)
44{ 74{
45 BUG(); 75 struct nvgpu_posix_io_callbacks *callbacks =
76 nvgpu_os_posix_from_gk20a(g)->callbacks;
46 77
47 return 0; 78 struct nvgpu_reg_access access = {
48} 79 .addr = r,
80 .value = 0L
81 };
49 82
50u32 __nvgpu_readl(struct gk20a *g, u32 r) 83 if (callbacks == NULL || callbacks->readl == NULL) {
51{ 84 BUG();
52 BUG(); 85 }
53 86
54 return 0; 87 callbacks->readl(g, &access);
88
89 return access.value;
55} 90}
56 91
57void nvgpu_writel_loop(struct gk20a *g, u32 r, u32 v) 92void nvgpu_writel_loop(struct gk20a *g, u32 r, u32 v)
@@ -59,16 +94,76 @@ void nvgpu_writel_loop(struct gk20a *g, u32 r, u32 v)
59 BUG(); 94 BUG();
60} 95}
61 96
97u32 __nvgpu_readl(struct gk20a *g, u32 r)
98{
99 struct nvgpu_posix_io_callbacks *callbacks =
100 nvgpu_os_posix_from_gk20a(g)->callbacks;
101
102 struct nvgpu_reg_access access = {
103 .addr = r,
104 .value = 0L
105 };
106
107 if (callbacks == NULL || callbacks->__readl == NULL) {
108 BUG();
109 }
110
111 callbacks->__readl(g, &access);
112
113 return access.value;
114}
115
62void nvgpu_bar1_writel(struct gk20a *g, u32 b, u32 v) 116void nvgpu_bar1_writel(struct gk20a *g, u32 b, u32 v)
63{ 117{
64 BUG(); 118 struct nvgpu_posix_io_callbacks *callbacks =
119 nvgpu_os_posix_from_gk20a(g)->callbacks;
120
121 struct nvgpu_reg_access access = {
122 .addr = b,
123 .value = v
124 };
125
126 if (callbacks == NULL || callbacks->bar1_writel == NULL) {
127 BUG();
128 }
129
130 callbacks->bar1_writel(g, &access);
65} 131}
66 132
67u32 nvgpu_bar1_readl(struct gk20a *g, u32 b) 133u32 nvgpu_bar1_readl(struct gk20a *g, u32 b)
68{ 134{
69 BUG(); 135 struct nvgpu_posix_io_callbacks *callbacks =
136 nvgpu_os_posix_from_gk20a(g)->callbacks;
70 137
71 return 0; 138 struct nvgpu_reg_access access = {
139 .addr = b,
140 .value = 0L
141 };
142
143 if (callbacks == NULL || callbacks->bar1_readl == NULL) {
144 BUG();
145 }
146
147 callbacks->bar1_readl(g, &access);
148
149 return access.value;
150}
151
152void nvgpu_usermode_writel(struct gk20a *g, u32 r, u32 v)
153{
154 struct nvgpu_posix_io_callbacks *callbacks =
155 nvgpu_os_posix_from_gk20a(g)->callbacks;
156
157 struct nvgpu_reg_access access = {
158 .addr = r,
159 .value = v
160 };
161
162 if (callbacks == NULL || callbacks->usermode_writel == NULL) {
163 BUG();
164 }
165
166 callbacks->usermode_writel(g, &access);
72} 167}
73 168
74bool nvgpu_io_exists(struct gk20a *g) 169bool nvgpu_io_exists(struct gk20a *g)
@@ -81,7 +176,196 @@ bool nvgpu_io_valid_reg(struct gk20a *g, u32 r)
81 return false; 176 return false;
82} 177}
83 178
84void nvgpu_usermode_writel(struct gk20a *g, u32 r, u32 v) 179void nvgpu_posix_io_init_reg_space(struct gk20a *g)
85{ 180{
86 BUG(); 181 struct nvgpu_os_posix *p = nvgpu_os_posix_from_gk20a(g);
182
183 p->recording = false;
184 p->error_code = 0;
185 nvgpu_init_list_node(&p->reg_space_head);
186 nvgpu_init_list_node(&p->recorder_head);
187}
188
189int nvgpu_posix_io_get_error_code(struct gk20a *g)
190{
191 struct nvgpu_os_posix *p = nvgpu_os_posix_from_gk20a(g);
192
193 return p->error_code;
194}
195
196void nvgpu_posix_io_reset_error_code(struct gk20a *g)
197{
198 struct nvgpu_os_posix *p = nvgpu_os_posix_from_gk20a(g);
199
200 p->error_code = 0;
201}
202
203/*
204 * Add a new register space to the list of spaces, defined by a base
205 * address and a size.
206 */
207int nvgpu_posix_io_add_reg_space(struct gk20a *g, u32 base, u32 size)
208{
209 struct nvgpu_os_posix *p = nvgpu_os_posix_from_gk20a(g);
210 struct nvgpu_posix_io_reg_space *new_reg_space =
211 nvgpu_kzalloc(g, sizeof(struct nvgpu_posix_io_reg_space));
212
213 if (new_reg_space == NULL) {
214 return -ENOMEM;
215 }
216
217 new_reg_space->base = base;
218 new_reg_space->size = size;
219
220 new_reg_space->data = nvgpu_vzalloc(g, size);
221 if (new_reg_space->data == NULL) {
222 return -ENOMEM;
223 }
224
225 nvgpu_list_add_tail(&new_reg_space->link, &p->reg_space_head);
226 return 0;
227}
228
229void nvgpu_posix_io_delete_reg_space(struct gk20a *g, u32 base)
230{
231 struct nvgpu_posix_io_reg_space *reg_space =
232 nvgpu_posix_io_get_reg_space(g, base);
233 if (reg_space == NULL) {
234 /* Invalid space, or already de-allocated */
235 return;
236 }
237 nvgpu_list_del(&reg_space->link);
238 nvgpu_vfree(g, reg_space->data);
239 nvgpu_kfree(g, reg_space);
240}
241
242/*
243 * Lookup a register space from a given address. If no register space is found
244 * this is a bug similar to a translation fault.
245 */
246struct nvgpu_posix_io_reg_space *nvgpu_posix_io_get_reg_space(struct gk20a *g,
247 u32 addr)
248{
249 struct nvgpu_os_posix *p = nvgpu_os_posix_from_gk20a(g);
250 struct nvgpu_posix_io_reg_space *reg_space;
251
252 nvgpu_list_for_each_entry(reg_space, &p->reg_space_head,
253 nvgpu_posix_io_reg_space, link) {
254 u32 offset = addr - reg_space->base;
255
256 if ((addr >= reg_space->base) && (offset <= reg_space->size)) {
257 return reg_space;
258 }
259 }
260 p->error_code = -EFAULT;
261 nvgpu_err(g, "ABORT for address 0x%x", addr);
262 return NULL;
263}
264
265void nvgpu_posix_io_writel_reg_space(struct gk20a *g, u32 addr, u32 data)
266{
267 struct nvgpu_posix_io_reg_space *space =
268 nvgpu_posix_io_get_reg_space(g, addr);
269
270 if (space != NULL) {
271 u32 offset = (addr - space->base) / ((u32) sizeof(u32));
272
273 *(space->data + offset) = data;
274 }
275}
276
277u32 nvgpu_posix_io_readl_reg_space(struct gk20a *g, u32 addr)
278{
279 struct nvgpu_posix_io_reg_space *space =
280 nvgpu_posix_io_get_reg_space(g, addr);
281
282 if (space != NULL) {
283 u32 offset = (addr - space->base) / ((u32) sizeof(u32));
284
285 return *(space->data + offset);
286 } else {
287 return 0;
288 }
289}
290
291/*
292 * Start recording register writes. If this function is called again,
293 * it will free all previously recorded events.
294 */
295void nvgpu_posix_io_start_recorder(struct gk20a *g)
296{
297 struct nvgpu_os_posix *p = nvgpu_os_posix_from_gk20a(g);
298 struct nvgpu_posix_io_reg_access *ptr;
299
300 /* If list already has events, delete them all */
301 if (p->recording == true) {
302 while (!nvgpu_list_empty(&p->recorder_head)) {
303 ptr = nvgpu_list_first_entry(&p->recorder_head,
304 nvgpu_posix_io_reg_access, link);
305 nvgpu_list_del(&ptr->link);
306 nvgpu_kfree(g, ptr);
307 }
308 }
309 p->recording = true;
310}
311
312void nvgpu_posix_io_record_access(struct gk20a *g,
313 struct nvgpu_reg_access *access)
314{
315 struct nvgpu_os_posix *p = nvgpu_os_posix_from_gk20a(g);
316
317 if (p->recording == true) {
318 struct nvgpu_posix_io_reg_access *new_event = nvgpu_kzalloc(g,
319 sizeof(struct nvgpu_posix_io_reg_access));
320 (void) memcpy(&(new_event->access), access,
321 sizeof(struct nvgpu_reg_access));
322 nvgpu_list_add_tail(&new_event->link, &p->recorder_head);
323 }
324}
325
326/*
327 * Take an array of accesses and compare to the recorded sequence. Returns true
328 * if the array matches the recorded sequence.
329 * If strict mode is false, this function allows extra accesses to be present
330 * in the recording.
331 */
332bool nvgpu_posix_io_check_sequence(struct gk20a *g,
333 struct nvgpu_reg_access *sequence, u32 size, bool strict)
334{
335 struct nvgpu_os_posix *p = nvgpu_os_posix_from_gk20a(g);
336 struct nvgpu_posix_io_reg_access *ptr;
337 u32 i = 0;
338
339 if (p->recording == false) {
340 return false;
341 }
342
343 nvgpu_list_for_each_entry(ptr, &p->recorder_head,
344 nvgpu_posix_io_reg_access, link) {
345 if ((sequence[i].addr == ptr->access.addr) &&
346 (sequence[i].value == ptr->access.value)) {
347 i++;
348 } else {
349 if (strict == true) {
350 return false;
351 }
352 }
353 }
354
355 if (i != size) {
356 /* Either missing or too many accesses */
357 return false;
358 }
359
360 if (&ptr->link == &p->recorder_head) {
361 /* Identical match */
362 return true;
363 }
364
365 /* Not an identical match */
366 if (strict) {
367 return false;
368 } else {
369 return true;
370 }
87} 371}