1 | /* $NetBSD: nouveau_engine_graph_nvc0.c,v 1.3 2015/10/18 15:42:00 jmcneill Exp $ */ |
2 | |
3 | /* |
4 | * Copyright 2012 Red Hat Inc. |
5 | * |
6 | * Permission is hereby granted, free of charge, to any person obtaining a |
7 | * copy of this software and associated documentation files (the "Software"), |
8 | * to deal in the Software without restriction, including without limitation |
9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
10 | * and/or sell copies of the Software, and to permit persons to whom the |
11 | * Software is furnished to do so, subject to the following conditions: |
12 | * |
13 | * The above copyright notice and this permission notice shall be included in |
14 | * all copies or substantial portions of the Software. |
15 | * |
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
19 | * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR |
20 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, |
21 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
22 | * OTHER DEALINGS IN THE SOFTWARE. |
23 | * |
24 | * Authors: Ben Skeggs |
25 | */ |
26 | |
27 | #include <sys/cdefs.h> |
28 | __KERNEL_RCSID(0, "$NetBSD: nouveau_engine_graph_nvc0.c,v 1.3 2015/10/18 15:42:00 jmcneill Exp $" ); |
29 | |
30 | #include <linux/string.h> /* XXX */ |
31 | |
32 | #include "nvc0.h" |
33 | #include "ctxnvc0.h" |
34 | |
35 | /******************************************************************************* |
36 | * Graphics object classes |
37 | ******************************************************************************/ |
38 | |
39 | struct nouveau_oclass |
40 | nvc0_graph_sclass[] = { |
41 | { 0x902d, &nouveau_object_ofuncs }, |
42 | { 0x9039, &nouveau_object_ofuncs }, |
43 | { 0x9097, &nouveau_object_ofuncs }, |
44 | { 0x90c0, &nouveau_object_ofuncs }, |
45 | {} |
46 | }; |
47 | |
48 | /******************************************************************************* |
49 | * PGRAPH context |
50 | ******************************************************************************/ |
51 | |
52 | int |
53 | nvc0_graph_context_ctor(struct nouveau_object *parent, |
54 | struct nouveau_object *engine, |
55 | struct nouveau_oclass *oclass, void *args, u32 size, |
56 | struct nouveau_object **pobject) |
57 | { |
58 | struct nouveau_vm *vm = nouveau_client(parent)->vm; |
59 | struct nvc0_graph_priv *priv = (void *)engine; |
60 | struct nvc0_graph_data *data = priv->mmio_data; |
61 | struct nvc0_graph_mmio *mmio = priv->mmio_list; |
62 | struct nvc0_graph_chan *chan; |
63 | int ret, i; |
64 | |
65 | /* allocate memory for context, and fill with default values */ |
66 | ret = nouveau_graph_context_create(parent, engine, oclass, NULL, |
67 | priv->size, 0x100, |
68 | NVOBJ_FLAG_ZERO_ALLOC, &chan); |
69 | *pobject = nv_object(chan); |
70 | if (ret) |
71 | return ret; |
72 | |
73 | /* allocate memory for a "mmio list" buffer that's used by the HUB |
74 | * fuc to modify some per-context register settings on first load |
75 | * of the context. |
76 | */ |
77 | ret = nouveau_gpuobj_new(nv_object(chan), NULL, 0x1000, 0x100, 0, |
78 | &chan->mmio); |
79 | if (ret) |
80 | return ret; |
81 | |
82 | ret = nouveau_gpuobj_map_vm(nv_gpuobj(chan->mmio), vm, |
83 | NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS, |
84 | &chan->mmio_vma); |
85 | if (ret) |
86 | return ret; |
87 | |
88 | /* allocate buffers referenced by mmio list */ |
89 | for (i = 0; data->size && i < ARRAY_SIZE(priv->mmio_data); i++) { |
90 | ret = nouveau_gpuobj_new(nv_object(chan), NULL, data->size, |
91 | data->align, 0, &chan->data[i].mem); |
92 | if (ret) |
93 | return ret; |
94 | |
95 | ret = nouveau_gpuobj_map_vm(chan->data[i].mem, vm, data->access, |
96 | &chan->data[i].vma); |
97 | if (ret) |
98 | return ret; |
99 | |
100 | data++; |
101 | } |
102 | |
103 | /* finally, fill in the mmio list and point the context at it */ |
104 | for (i = 0; mmio->addr && i < ARRAY_SIZE(priv->mmio_list); i++) { |
105 | u32 addr = mmio->addr; |
106 | u32 data = mmio->data; |
107 | |
108 | if (mmio->shift) { |
109 | u64 info = chan->data[mmio->buffer].vma.offset; |
110 | data |= info >> mmio->shift; |
111 | } |
112 | |
113 | nv_wo32(chan->mmio, chan->mmio_nr++ * 4, addr); |
114 | nv_wo32(chan->mmio, chan->mmio_nr++ * 4, data); |
115 | mmio++; |
116 | } |
117 | |
118 | for (i = 0; i < priv->size; i += 4) |
119 | nv_wo32(chan, i, priv->data[i / 4]); |
120 | |
121 | if (!priv->firmware) { |
122 | nv_wo32(chan, 0x00, chan->mmio_nr / 2); |
123 | nv_wo32(chan, 0x04, chan->mmio_vma.offset >> 8); |
124 | } else { |
125 | nv_wo32(chan, 0xf4, 0); |
126 | nv_wo32(chan, 0xf8, 0); |
127 | nv_wo32(chan, 0x10, chan->mmio_nr / 2); |
128 | nv_wo32(chan, 0x14, lower_32_bits(chan->mmio_vma.offset)); |
129 | nv_wo32(chan, 0x18, upper_32_bits(chan->mmio_vma.offset)); |
130 | nv_wo32(chan, 0x1c, 1); |
131 | nv_wo32(chan, 0x20, 0); |
132 | nv_wo32(chan, 0x28, 0); |
133 | nv_wo32(chan, 0x2c, 0); |
134 | } |
135 | |
136 | return 0; |
137 | } |
138 | |
139 | void |
140 | nvc0_graph_context_dtor(struct nouveau_object *object) |
141 | { |
142 | struct nvc0_graph_chan *chan = (void *)object; |
143 | int i; |
144 | |
145 | for (i = 0; i < ARRAY_SIZE(chan->data); i++) { |
146 | nouveau_gpuobj_unmap(&chan->data[i].vma); |
147 | nouveau_gpuobj_ref(NULL, &chan->data[i].mem); |
148 | } |
149 | |
150 | nouveau_gpuobj_unmap(&chan->mmio_vma); |
151 | nouveau_gpuobj_ref(NULL, &chan->mmio); |
152 | |
153 | nouveau_graph_context_destroy(&chan->base); |
154 | } |
155 | |
156 | /******************************************************************************* |
157 | * PGRAPH register lists |
158 | ******************************************************************************/ |
159 | |
160 | const struct nvc0_graph_init |
161 | nvc0_graph_init_main_0[] = { |
162 | { 0x400080, 1, 0x04, 0x003083c2 }, |
163 | { 0x400088, 1, 0x04, 0x00006fe7 }, |
164 | { 0x40008c, 1, 0x04, 0x00000000 }, |
165 | { 0x400090, 1, 0x04, 0x00000030 }, |
166 | { 0x40013c, 1, 0x04, 0x013901f7 }, |
167 | { 0x400140, 1, 0x04, 0x00000100 }, |
168 | { 0x400144, 1, 0x04, 0x00000000 }, |
169 | { 0x400148, 1, 0x04, 0x00000110 }, |
170 | { 0x400138, 1, 0x04, 0x00000000 }, |
171 | { 0x400130, 2, 0x04, 0x00000000 }, |
172 | { 0x400124, 1, 0x04, 0x00000002 }, |
173 | {} |
174 | }; |
175 | |
176 | const struct nvc0_graph_init |
177 | nvc0_graph_init_fe_0[] = { |
178 | { 0x40415c, 1, 0x04, 0x00000000 }, |
179 | { 0x404170, 1, 0x04, 0x00000000 }, |
180 | {} |
181 | }; |
182 | |
183 | const struct nvc0_graph_init |
184 | nvc0_graph_init_pri_0[] = { |
185 | { 0x404488, 2, 0x04, 0x00000000 }, |
186 | {} |
187 | }; |
188 | |
189 | const struct nvc0_graph_init |
190 | nvc0_graph_init_rstr2d_0[] = { |
191 | { 0x407808, 1, 0x04, 0x00000000 }, |
192 | {} |
193 | }; |
194 | |
195 | const struct nvc0_graph_init |
196 | nvc0_graph_init_pd_0[] = { |
197 | { 0x406024, 1, 0x04, 0x00000000 }, |
198 | {} |
199 | }; |
200 | |
201 | const struct nvc0_graph_init |
202 | nvc0_graph_init_ds_0[] = { |
203 | { 0x405844, 1, 0x04, 0x00ffffff }, |
204 | { 0x405850, 1, 0x04, 0x00000000 }, |
205 | { 0x405908, 1, 0x04, 0x00000000 }, |
206 | {} |
207 | }; |
208 | |
209 | const struct nvc0_graph_init |
210 | nvc0_graph_init_scc_0[] = { |
211 | { 0x40803c, 1, 0x04, 0x00000000 }, |
212 | {} |
213 | }; |
214 | |
215 | const struct nvc0_graph_init |
216 | nvc0_graph_init_prop_0[] = { |
217 | { 0x4184a0, 1, 0x04, 0x00000000 }, |
218 | {} |
219 | }; |
220 | |
221 | const struct nvc0_graph_init |
222 | nvc0_graph_init_gpc_unk_0[] = { |
223 | { 0x418604, 1, 0x04, 0x00000000 }, |
224 | { 0x418680, 1, 0x04, 0x00000000 }, |
225 | { 0x418714, 1, 0x04, 0x80000000 }, |
226 | { 0x418384, 1, 0x04, 0x00000000 }, |
227 | {} |
228 | }; |
229 | |
230 | const struct nvc0_graph_init |
231 | nvc0_graph_init_setup_0[] = { |
232 | { 0x418814, 3, 0x04, 0x00000000 }, |
233 | {} |
234 | }; |
235 | |
236 | const struct nvc0_graph_init |
237 | nvc0_graph_init_crstr_0[] = { |
238 | { 0x418b04, 1, 0x04, 0x00000000 }, |
239 | {} |
240 | }; |
241 | |
242 | const struct nvc0_graph_init |
243 | nvc0_graph_init_setup_1[] = { |
244 | { 0x4188c8, 1, 0x04, 0x80000000 }, |
245 | { 0x4188cc, 1, 0x04, 0x00000000 }, |
246 | { 0x4188d0, 1, 0x04, 0x00010000 }, |
247 | { 0x4188d4, 1, 0x04, 0x00000001 }, |
248 | {} |
249 | }; |
250 | |
251 | const struct nvc0_graph_init |
252 | nvc0_graph_init_zcull_0[] = { |
253 | { 0x418910, 1, 0x04, 0x00010001 }, |
254 | { 0x418914, 1, 0x04, 0x00000301 }, |
255 | { 0x418918, 1, 0x04, 0x00800000 }, |
256 | { 0x418980, 1, 0x04, 0x77777770 }, |
257 | { 0x418984, 3, 0x04, 0x77777777 }, |
258 | {} |
259 | }; |
260 | |
261 | const struct nvc0_graph_init |
262 | nvc0_graph_init_gpm_0[] = { |
263 | { 0x418c04, 1, 0x04, 0x00000000 }, |
264 | { 0x418c88, 1, 0x04, 0x00000000 }, |
265 | {} |
266 | }; |
267 | |
268 | const struct nvc0_graph_init |
269 | nvc0_graph_init_gpc_unk_1[] = { |
270 | { 0x418d00, 1, 0x04, 0x00000000 }, |
271 | { 0x418f08, 1, 0x04, 0x00000000 }, |
272 | { 0x418e00, 1, 0x04, 0x00000050 }, |
273 | { 0x418e08, 1, 0x04, 0x00000000 }, |
274 | {} |
275 | }; |
276 | |
277 | const struct nvc0_graph_init |
278 | nvc0_graph_init_gcc_0[] = { |
279 | { 0x41900c, 1, 0x04, 0x00000000 }, |
280 | { 0x419018, 1, 0x04, 0x00000000 }, |
281 | {} |
282 | }; |
283 | |
284 | const struct nvc0_graph_init |
285 | nvc0_graph_init_tpccs_0[] = { |
286 | { 0x419d08, 2, 0x04, 0x00000000 }, |
287 | { 0x419d10, 1, 0x04, 0x00000014 }, |
288 | {} |
289 | }; |
290 | |
291 | const struct nvc0_graph_init |
292 | nvc0_graph_init_tex_0[] = { |
293 | { 0x419ab0, 1, 0x04, 0x00000000 }, |
294 | { 0x419ab8, 1, 0x04, 0x000000e7 }, |
295 | { 0x419abc, 2, 0x04, 0x00000000 }, |
296 | {} |
297 | }; |
298 | |
299 | const struct nvc0_graph_init |
300 | nvc0_graph_init_pe_0[] = { |
301 | { 0x41980c, 3, 0x04, 0x00000000 }, |
302 | { 0x419844, 1, 0x04, 0x00000000 }, |
303 | { 0x41984c, 1, 0x04, 0x00005bc5 }, |
304 | { 0x419850, 4, 0x04, 0x00000000 }, |
305 | {} |
306 | }; |
307 | |
308 | const struct nvc0_graph_init |
309 | nvc0_graph_init_l1c_0[] = { |
310 | { 0x419c98, 1, 0x04, 0x00000000 }, |
311 | { 0x419ca8, 1, 0x04, 0x80000000 }, |
312 | { 0x419cb4, 1, 0x04, 0x00000000 }, |
313 | { 0x419cb8, 1, 0x04, 0x00008bf4 }, |
314 | { 0x419cbc, 1, 0x04, 0x28137606 }, |
315 | { 0x419cc0, 2, 0x04, 0x00000000 }, |
316 | {} |
317 | }; |
318 | |
319 | const struct nvc0_graph_init |
320 | nvc0_graph_init_wwdx_0[] = { |
321 | { 0x419bd4, 1, 0x04, 0x00800000 }, |
322 | { 0x419bdc, 1, 0x04, 0x00000000 }, |
323 | {} |
324 | }; |
325 | |
326 | const struct nvc0_graph_init |
327 | nvc0_graph_init_tpccs_1[] = { |
328 | { 0x419d2c, 1, 0x04, 0x00000000 }, |
329 | {} |
330 | }; |
331 | |
332 | const struct nvc0_graph_init |
333 | nvc0_graph_init_mpc_0[] = { |
334 | { 0x419c0c, 1, 0x04, 0x00000000 }, |
335 | {} |
336 | }; |
337 | |
338 | static const struct nvc0_graph_init |
339 | nvc0_graph_init_sm_0[] = { |
340 | { 0x419e00, 1, 0x04, 0x00000000 }, |
341 | { 0x419ea0, 1, 0x04, 0x00000000 }, |
342 | { 0x419ea4, 1, 0x04, 0x00000100 }, |
343 | { 0x419ea8, 1, 0x04, 0x00001100 }, |
344 | { 0x419eac, 1, 0x04, 0x11100702 }, |
345 | { 0x419eb0, 1, 0x04, 0x00000003 }, |
346 | { 0x419eb4, 4, 0x04, 0x00000000 }, |
347 | { 0x419ec8, 1, 0x04, 0x06060618 }, |
348 | { 0x419ed0, 1, 0x04, 0x0eff0e38 }, |
349 | { 0x419ed4, 1, 0x04, 0x011104f1 }, |
350 | { 0x419edc, 1, 0x04, 0x00000000 }, |
351 | { 0x419f00, 1, 0x04, 0x00000000 }, |
352 | { 0x419f2c, 1, 0x04, 0x00000000 }, |
353 | {} |
354 | }; |
355 | |
356 | const struct nvc0_graph_init |
357 | nvc0_graph_init_be_0[] = { |
358 | { 0x40880c, 1, 0x04, 0x00000000 }, |
359 | { 0x408910, 9, 0x04, 0x00000000 }, |
360 | { 0x408950, 1, 0x04, 0x00000000 }, |
361 | { 0x408954, 1, 0x04, 0x0000ffff }, |
362 | { 0x408984, 1, 0x04, 0x00000000 }, |
363 | { 0x408988, 1, 0x04, 0x08040201 }, |
364 | { 0x40898c, 1, 0x04, 0x80402010 }, |
365 | {} |
366 | }; |
367 | |
368 | const struct nvc0_graph_init |
369 | nvc0_graph_init_fe_1[] = { |
370 | { 0x4040f0, 1, 0x04, 0x00000000 }, |
371 | {} |
372 | }; |
373 | |
374 | const struct nvc0_graph_init |
375 | nvc0_graph_init_pe_1[] = { |
376 | { 0x419880, 1, 0x04, 0x00000002 }, |
377 | {} |
378 | }; |
379 | |
380 | static const struct nvc0_graph_pack |
381 | nvc0_graph_pack_mmio[] = { |
382 | { nvc0_graph_init_main_0 }, |
383 | { nvc0_graph_init_fe_0 }, |
384 | { nvc0_graph_init_pri_0 }, |
385 | { nvc0_graph_init_rstr2d_0 }, |
386 | { nvc0_graph_init_pd_0 }, |
387 | { nvc0_graph_init_ds_0 }, |
388 | { nvc0_graph_init_scc_0 }, |
389 | { nvc0_graph_init_prop_0 }, |
390 | { nvc0_graph_init_gpc_unk_0 }, |
391 | { nvc0_graph_init_setup_0 }, |
392 | { nvc0_graph_init_crstr_0 }, |
393 | { nvc0_graph_init_setup_1 }, |
394 | { nvc0_graph_init_zcull_0 }, |
395 | { nvc0_graph_init_gpm_0 }, |
396 | { nvc0_graph_init_gpc_unk_1 }, |
397 | { nvc0_graph_init_gcc_0 }, |
398 | { nvc0_graph_init_tpccs_0 }, |
399 | { nvc0_graph_init_tex_0 }, |
400 | { nvc0_graph_init_pe_0 }, |
401 | { nvc0_graph_init_l1c_0 }, |
402 | { nvc0_graph_init_wwdx_0 }, |
403 | { nvc0_graph_init_tpccs_1 }, |
404 | { nvc0_graph_init_mpc_0 }, |
405 | { nvc0_graph_init_sm_0 }, |
406 | { nvc0_graph_init_be_0 }, |
407 | { nvc0_graph_init_fe_1 }, |
408 | { nvc0_graph_init_pe_1 }, |
409 | {} |
410 | }; |
411 | |
412 | /******************************************************************************* |
413 | * PGRAPH engine/subdev functions |
414 | ******************************************************************************/ |
415 | |
416 | void |
417 | nvc0_graph_mmio(struct nvc0_graph_priv *priv, const struct nvc0_graph_pack *p) |
418 | { |
419 | const struct nvc0_graph_pack *pack; |
420 | const struct nvc0_graph_init *init; |
421 | |
422 | pack_for_each_init(init, pack, p) { |
423 | u32 next = init->addr + init->count * init->pitch; |
424 | u32 addr = init->addr; |
425 | while (addr < next) { |
426 | nv_wr32(priv, addr, init->data); |
427 | addr += init->pitch; |
428 | } |
429 | } |
430 | } |
431 | |
432 | void |
433 | nvc0_graph_icmd(struct nvc0_graph_priv *priv, const struct nvc0_graph_pack *p) |
434 | { |
435 | const struct nvc0_graph_pack *pack; |
436 | const struct nvc0_graph_init *init; |
437 | u32 data = 0; |
438 | |
439 | nv_wr32(priv, 0x400208, 0x80000000); |
440 | |
441 | pack_for_each_init(init, pack, p) { |
442 | u32 next = init->addr + init->count * init->pitch; |
443 | u32 addr = init->addr; |
444 | |
445 | if ((pack == p && init == p->init) || data != init->data) { |
446 | nv_wr32(priv, 0x400204, init->data); |
447 | data = init->data; |
448 | } |
449 | |
450 | while (addr < next) { |
451 | nv_wr32(priv, 0x400200, addr); |
452 | nv_wait(priv, 0x400700, 0x00000002, 0x00000000); |
453 | addr += init->pitch; |
454 | } |
455 | } |
456 | |
457 | nv_wr32(priv, 0x400208, 0x00000000); |
458 | } |
459 | |
460 | void |
461 | nvc0_graph_mthd(struct nvc0_graph_priv *priv, const struct nvc0_graph_pack *p) |
462 | { |
463 | const struct nvc0_graph_pack *pack; |
464 | const struct nvc0_graph_init *init; |
465 | u32 data = 0; |
466 | |
467 | pack_for_each_init(init, pack, p) { |
468 | u32 ctrl = 0x80000000 | pack->type; |
469 | u32 next = init->addr + init->count * init->pitch; |
470 | u32 addr = init->addr; |
471 | |
472 | if ((pack == p && init == p->init) || data != init->data) { |
473 | nv_wr32(priv, 0x40448c, init->data); |
474 | data = init->data; |
475 | } |
476 | |
477 | while (addr < next) { |
478 | nv_wr32(priv, 0x404488, ctrl | (addr << 14)); |
479 | addr += init->pitch; |
480 | } |
481 | } |
482 | } |
483 | |
484 | u64 |
485 | nvc0_graph_units(struct nouveau_graph *graph) |
486 | { |
487 | struct nvc0_graph_priv *priv = (void *)graph; |
488 | u64 cfg; |
489 | |
490 | cfg = (u32)priv->gpc_nr; |
491 | cfg |= (u32)priv->tpc_total << 8; |
492 | cfg |= (u64)priv->rop_nr << 32; |
493 | |
494 | return cfg; |
495 | } |
496 | |
497 | static const struct nouveau_enum nve0_sked_error[] = { |
498 | { 7, "CONSTANT_BUFFER_SIZE" }, |
499 | { 9, "LOCAL_MEMORY_SIZE_POS" }, |
500 | { 10, "LOCAL_MEMORY_SIZE_NEG" }, |
501 | { 11, "WARP_CSTACK_SIZE" }, |
502 | { 12, "TOTAL_TEMP_SIZE" }, |
503 | { 13, "REGISTER_COUNT" }, |
504 | { 18, "TOTAL_THREADS" }, |
505 | { 20, "PROGRAM_OFFSET" }, |
506 | { 21, "SHARED_MEMORY_SIZE" }, |
507 | { 25, "SHARED_CONFIG_TOO_SMALL" }, |
508 | { 26, "TOTAL_REGISTER_COUNT" }, |
509 | {} |
510 | }; |
511 | |
512 | static const struct nouveau_enum nvc0_gpc_rop_error[] = { |
513 | { 1, "RT_PITCH_OVERRUN" }, |
514 | { 4, "RT_WIDTH_OVERRUN" }, |
515 | { 5, "RT_HEIGHT_OVERRUN" }, |
516 | { 7, "ZETA_STORAGE_TYPE_MISMATCH" }, |
517 | { 8, "RT_STORAGE_TYPE_MISMATCH" }, |
518 | { 10, "RT_LINEAR_MISMATCH" }, |
519 | {} |
520 | }; |
521 | |
522 | static void |
523 | nvc0_graph_trap_gpc_rop(struct nvc0_graph_priv *priv, int gpc) |
524 | { |
525 | u32 trap[4]; |
526 | int i; |
527 | |
528 | trap[0] = nv_rd32(priv, GPC_UNIT(gpc, 0x0420)); |
529 | trap[1] = nv_rd32(priv, GPC_UNIT(gpc, 0x0434)); |
530 | trap[2] = nv_rd32(priv, GPC_UNIT(gpc, 0x0438)); |
531 | trap[3] = nv_rd32(priv, GPC_UNIT(gpc, 0x043c)); |
532 | |
533 | nv_error(priv, "GPC%d/PROP trap:" , gpc); |
534 | for (i = 0; i <= 29; ++i) { |
535 | if (!(trap[0] & (1 << i))) |
536 | continue; |
537 | pr_cont(" " ); |
538 | nouveau_enum_print(nvc0_gpc_rop_error, i); |
539 | } |
540 | pr_cont("\n" ); |
541 | |
542 | nv_error(priv, "x = %u, y = %u, format = %x, storage type = %x\n" , |
543 | trap[1] & 0xffff, trap[1] >> 16, (trap[2] >> 8) & 0x3f, |
544 | trap[3] & 0xff); |
545 | nv_wr32(priv, GPC_UNIT(gpc, 0x0420), 0xc0000000); |
546 | } |
547 | |
548 | static const struct nouveau_enum nvc0_mp_warp_error[] = { |
549 | { 0x00, "NO_ERROR" }, |
550 | { 0x01, "STACK_MISMATCH" }, |
551 | { 0x05, "MISALIGNED_PC" }, |
552 | { 0x08, "MISALIGNED_GPR" }, |
553 | { 0x09, "INVALID_OPCODE" }, |
554 | { 0x0d, "GPR_OUT_OF_BOUNDS" }, |
555 | { 0x0e, "MEM_OUT_OF_BOUNDS" }, |
556 | { 0x0f, "UNALIGNED_MEM_ACCESS" }, |
557 | { 0x11, "INVALID_PARAM" }, |
558 | {} |
559 | }; |
560 | |
561 | static const struct nouveau_bitfield nvc0_mp_global_error[] = { |
562 | { 0x00000004, "MULTIPLE_WARP_ERRORS" }, |
563 | { 0x00000008, "OUT_OF_STACK_SPACE" }, |
564 | {} |
565 | }; |
566 | |
567 | static void |
568 | nvc0_graph_trap_mp(struct nvc0_graph_priv *priv, int gpc, int tpc) |
569 | { |
570 | u32 werr = nv_rd32(priv, TPC_UNIT(gpc, tpc, 0x648)); |
571 | u32 gerr = nv_rd32(priv, TPC_UNIT(gpc, tpc, 0x650)); |
572 | |
573 | nv_error(priv, "GPC%i/TPC%i/MP trap:" , gpc, tpc); |
574 | nouveau_bitfield_print(nvc0_mp_global_error, gerr); |
575 | if (werr) { |
576 | pr_cont(" " ); |
577 | nouveau_enum_print(nvc0_mp_warp_error, werr & 0xffff); |
578 | } |
579 | pr_cont("\n" ); |
580 | |
581 | nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x648), 0x00000000); |
582 | nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x650), gerr); |
583 | } |
584 | |
585 | static void |
586 | nvc0_graph_trap_tpc(struct nvc0_graph_priv *priv, int gpc, int tpc) |
587 | { |
588 | u32 stat = nv_rd32(priv, TPC_UNIT(gpc, tpc, 0x0508)); |
589 | |
590 | if (stat & 0x00000001) { |
591 | u32 trap = nv_rd32(priv, TPC_UNIT(gpc, tpc, 0x0224)); |
592 | nv_error(priv, "GPC%d/TPC%d/TEX: 0x%08x\n" , gpc, tpc, trap); |
593 | nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x0224), 0xc0000000); |
594 | stat &= ~0x00000001; |
595 | } |
596 | |
597 | if (stat & 0x00000002) { |
598 | nvc0_graph_trap_mp(priv, gpc, tpc); |
599 | stat &= ~0x00000002; |
600 | } |
601 | |
602 | if (stat & 0x00000004) { |
603 | u32 trap = nv_rd32(priv, TPC_UNIT(gpc, tpc, 0x0084)); |
604 | nv_error(priv, "GPC%d/TPC%d/POLY: 0x%08x\n" , gpc, tpc, trap); |
605 | nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x0084), 0xc0000000); |
606 | stat &= ~0x00000004; |
607 | } |
608 | |
609 | if (stat & 0x00000008) { |
610 | u32 trap = nv_rd32(priv, TPC_UNIT(gpc, tpc, 0x048c)); |
611 | nv_error(priv, "GPC%d/TPC%d/L1C: 0x%08x\n" , gpc, tpc, trap); |
612 | nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x048c), 0xc0000000); |
613 | stat &= ~0x00000008; |
614 | } |
615 | |
616 | if (stat) { |
617 | nv_error(priv, "GPC%d/TPC%d/0x%08x: unknown\n" , gpc, tpc, stat); |
618 | } |
619 | } |
620 | |
621 | static void |
622 | nvc0_graph_trap_gpc(struct nvc0_graph_priv *priv, int gpc) |
623 | { |
624 | u32 stat = nv_rd32(priv, GPC_UNIT(gpc, 0x2c90)); |
625 | int tpc; |
626 | |
627 | if (stat & 0x00000001) { |
628 | nvc0_graph_trap_gpc_rop(priv, gpc); |
629 | stat &= ~0x00000001; |
630 | } |
631 | |
632 | if (stat & 0x00000002) { |
633 | u32 trap = nv_rd32(priv, GPC_UNIT(gpc, 0x0900)); |
634 | nv_error(priv, "GPC%d/ZCULL: 0x%08x\n" , gpc, trap); |
635 | nv_wr32(priv, GPC_UNIT(gpc, 0x0900), 0xc0000000); |
636 | stat &= ~0x00000002; |
637 | } |
638 | |
639 | if (stat & 0x00000004) { |
640 | u32 trap = nv_rd32(priv, GPC_UNIT(gpc, 0x1028)); |
641 | nv_error(priv, "GPC%d/CCACHE: 0x%08x\n" , gpc, trap); |
642 | nv_wr32(priv, GPC_UNIT(gpc, 0x1028), 0xc0000000); |
643 | stat &= ~0x00000004; |
644 | } |
645 | |
646 | if (stat & 0x00000008) { |
647 | u32 trap = nv_rd32(priv, GPC_UNIT(gpc, 0x0824)); |
648 | nv_error(priv, "GPC%d/ESETUP: 0x%08x\n" , gpc, trap); |
649 | nv_wr32(priv, GPC_UNIT(gpc, 0x0824), 0xc0000000); |
650 | stat &= ~0x00000009; |
651 | } |
652 | |
653 | for (tpc = 0; tpc < priv->tpc_nr[gpc]; tpc++) { |
654 | u32 mask = 0x00010000 << tpc; |
655 | if (stat & mask) { |
656 | nvc0_graph_trap_tpc(priv, gpc, tpc); |
657 | nv_wr32(priv, GPC_UNIT(gpc, 0x2c90), mask); |
658 | stat &= ~mask; |
659 | } |
660 | } |
661 | |
662 | if (stat) { |
663 | nv_error(priv, "GPC%d/0x%08x: unknown\n" , gpc, stat); |
664 | } |
665 | } |
666 | |
667 | static void |
668 | nvc0_graph_trap_intr(struct nvc0_graph_priv *priv) |
669 | { |
670 | u32 trap = nv_rd32(priv, 0x400108); |
671 | int rop, gpc, i; |
672 | |
673 | if (trap & 0x00000001) { |
674 | u32 stat = nv_rd32(priv, 0x404000); |
675 | nv_error(priv, "DISPATCH 0x%08x\n" , stat); |
676 | nv_wr32(priv, 0x404000, 0xc0000000); |
677 | nv_wr32(priv, 0x400108, 0x00000001); |
678 | trap &= ~0x00000001; |
679 | } |
680 | |
681 | if (trap & 0x00000002) { |
682 | u32 stat = nv_rd32(priv, 0x404600); |
683 | nv_error(priv, "M2MF 0x%08x\n" , stat); |
684 | nv_wr32(priv, 0x404600, 0xc0000000); |
685 | nv_wr32(priv, 0x400108, 0x00000002); |
686 | trap &= ~0x00000002; |
687 | } |
688 | |
689 | if (trap & 0x00000008) { |
690 | u32 stat = nv_rd32(priv, 0x408030); |
691 | nv_error(priv, "CCACHE 0x%08x\n" , stat); |
692 | nv_wr32(priv, 0x408030, 0xc0000000); |
693 | nv_wr32(priv, 0x400108, 0x00000008); |
694 | trap &= ~0x00000008; |
695 | } |
696 | |
697 | if (trap & 0x00000010) { |
698 | u32 stat = nv_rd32(priv, 0x405840); |
699 | nv_error(priv, "SHADER 0x%08x\n" , stat); |
700 | nv_wr32(priv, 0x405840, 0xc0000000); |
701 | nv_wr32(priv, 0x400108, 0x00000010); |
702 | trap &= ~0x00000010; |
703 | } |
704 | |
705 | if (trap & 0x00000040) { |
706 | u32 stat = nv_rd32(priv, 0x40601c); |
707 | nv_error(priv, "UNK6 0x%08x\n" , stat); |
708 | nv_wr32(priv, 0x40601c, 0xc0000000); |
709 | nv_wr32(priv, 0x400108, 0x00000040); |
710 | trap &= ~0x00000040; |
711 | } |
712 | |
713 | if (trap & 0x00000080) { |
714 | u32 stat = nv_rd32(priv, 0x404490); |
715 | nv_error(priv, "MACRO 0x%08x\n" , stat); |
716 | nv_wr32(priv, 0x404490, 0xc0000000); |
717 | nv_wr32(priv, 0x400108, 0x00000080); |
718 | trap &= ~0x00000080; |
719 | } |
720 | |
721 | if (trap & 0x00000100) { |
722 | u32 stat = nv_rd32(priv, 0x407020); |
723 | |
724 | nv_error(priv, "SKED:" ); |
725 | for (i = 0; i <= 29; ++i) { |
726 | if (!(stat & (1 << i))) |
727 | continue; |
728 | pr_cont(" " ); |
729 | nouveau_enum_print(nve0_sked_error, i); |
730 | } |
731 | pr_cont("\n" ); |
732 | |
733 | if (stat & 0x3fffffff) |
734 | nv_wr32(priv, 0x407020, 0x40000000); |
735 | nv_wr32(priv, 0x400108, 0x00000100); |
736 | trap &= ~0x00000100; |
737 | } |
738 | |
739 | if (trap & 0x01000000) { |
740 | u32 stat = nv_rd32(priv, 0x400118); |
741 | for (gpc = 0; stat && gpc < priv->gpc_nr; gpc++) { |
742 | u32 mask = 0x00000001 << gpc; |
743 | if (stat & mask) { |
744 | nvc0_graph_trap_gpc(priv, gpc); |
745 | nv_wr32(priv, 0x400118, mask); |
746 | stat &= ~mask; |
747 | } |
748 | } |
749 | nv_wr32(priv, 0x400108, 0x01000000); |
750 | trap &= ~0x01000000; |
751 | } |
752 | |
753 | if (trap & 0x02000000) { |
754 | for (rop = 0; rop < priv->rop_nr; rop++) { |
755 | u32 statz = nv_rd32(priv, ROP_UNIT(rop, 0x070)); |
756 | u32 statc = nv_rd32(priv, ROP_UNIT(rop, 0x144)); |
757 | nv_error(priv, "ROP%d 0x%08x 0x%08x\n" , |
758 | rop, statz, statc); |
759 | nv_wr32(priv, ROP_UNIT(rop, 0x070), 0xc0000000); |
760 | nv_wr32(priv, ROP_UNIT(rop, 0x144), 0xc0000000); |
761 | } |
762 | nv_wr32(priv, 0x400108, 0x02000000); |
763 | trap &= ~0x02000000; |
764 | } |
765 | |
766 | if (trap) { |
767 | nv_error(priv, "TRAP UNHANDLED 0x%08x\n" , trap); |
768 | nv_wr32(priv, 0x400108, trap); |
769 | } |
770 | } |
771 | |
772 | static void |
773 | nvc0_graph_ctxctl_debug_unit(struct nvc0_graph_priv *priv, u32 base) |
774 | { |
775 | nv_error(priv, "%06x - done 0x%08x\n" , base, |
776 | nv_rd32(priv, base + 0x400)); |
777 | nv_error(priv, "%06x - stat 0x%08x 0x%08x 0x%08x 0x%08x\n" , base, |
778 | nv_rd32(priv, base + 0x800), nv_rd32(priv, base + 0x804), |
779 | nv_rd32(priv, base + 0x808), nv_rd32(priv, base + 0x80c)); |
780 | nv_error(priv, "%06x - stat 0x%08x 0x%08x 0x%08x 0x%08x\n" , base, |
781 | nv_rd32(priv, base + 0x810), nv_rd32(priv, base + 0x814), |
782 | nv_rd32(priv, base + 0x818), nv_rd32(priv, base + 0x81c)); |
783 | } |
784 | |
785 | void |
786 | nvc0_graph_ctxctl_debug(struct nvc0_graph_priv *priv) |
787 | { |
788 | u32 gpcnr = nv_rd32(priv, 0x409604) & 0xffff; |
789 | u32 gpc; |
790 | |
791 | nvc0_graph_ctxctl_debug_unit(priv, 0x409000); |
792 | for (gpc = 0; gpc < gpcnr; gpc++) |
793 | nvc0_graph_ctxctl_debug_unit(priv, 0x502000 + (gpc * 0x8000)); |
794 | } |
795 | |
796 | static void |
797 | nvc0_graph_ctxctl_isr(struct nvc0_graph_priv *priv) |
798 | { |
799 | u32 ustat = nv_rd32(priv, 0x409c18); |
800 | |
801 | if (ustat & 0x00000001) |
802 | nv_error(priv, "CTXCTL ucode error\n" ); |
803 | if (ustat & 0x00080000) |
804 | nv_error(priv, "CTXCTL watchdog timeout\n" ); |
805 | if (ustat & ~0x00080001) |
806 | nv_error(priv, "CTXCTL 0x%08x\n" , ustat); |
807 | |
808 | nvc0_graph_ctxctl_debug(priv); |
809 | nv_wr32(priv, 0x409c20, ustat); |
810 | } |
811 | |
812 | static void |
813 | nvc0_graph_intr(struct nouveau_subdev *subdev) |
814 | { |
815 | struct nouveau_fifo *pfifo = nouveau_fifo(subdev); |
816 | struct nouveau_engine *engine = nv_engine(subdev); |
817 | struct nouveau_object *engctx; |
818 | struct nouveau_handle *handle; |
819 | struct nvc0_graph_priv *priv = (void *)subdev; |
820 | u64 inst = nv_rd32(priv, 0x409b00) & 0x0fffffff; |
821 | u32 stat = nv_rd32(priv, 0x400100); |
822 | u32 addr = nv_rd32(priv, 0x400704); |
823 | u32 mthd = (addr & 0x00003ffc); |
824 | u32 subc = (addr & 0x00070000) >> 16; |
825 | u32 data = nv_rd32(priv, 0x400708); |
826 | u32 code = nv_rd32(priv, 0x400110); |
827 | u32 class = nv_rd32(priv, 0x404200 + (subc * 4)); |
828 | int chid; |
829 | |
830 | engctx = nouveau_engctx_get(engine, inst); |
831 | chid = pfifo->chid(pfifo, engctx); |
832 | |
833 | if (stat & 0x00000010) { |
834 | handle = nouveau_handle_get_class(engctx, class); |
835 | if (!handle || nv_call(handle->object, mthd, data)) { |
836 | nv_error(priv, |
837 | "ILLEGAL_MTHD ch %d [0x%010" PRIx64" %s] subc %d class 0x%04x mthd 0x%04x data 0x%08x\n" , |
838 | chid, inst << 12, nouveau_client_name(engctx), |
839 | subc, class, mthd, data); |
840 | } |
841 | nouveau_handle_put(handle); |
842 | nv_wr32(priv, 0x400100, 0x00000010); |
843 | stat &= ~0x00000010; |
844 | } |
845 | |
846 | if (stat & 0x00000020) { |
847 | nv_error(priv, |
848 | "ILLEGAL_CLASS ch %d [0x%010" PRIx64" %s] subc %d class 0x%04x mthd 0x%04x data 0x%08x\n" , |
849 | chid, inst << 12, nouveau_client_name(engctx), subc, |
850 | class, mthd, data); |
851 | nv_wr32(priv, 0x400100, 0x00000020); |
852 | stat &= ~0x00000020; |
853 | } |
854 | |
855 | if (stat & 0x00100000) { |
856 | nv_error(priv, "DATA_ERROR [" ); |
857 | nouveau_enum_print(nv50_data_error_names, code); |
858 | pr_cont("] ch %d [0x%010" PRIx64" %s] subc %d class 0x%04x mthd 0x%04x data 0x%08x\n" , |
859 | chid, inst << 12, nouveau_client_name(engctx), subc, |
860 | class, mthd, data); |
861 | nv_wr32(priv, 0x400100, 0x00100000); |
862 | stat &= ~0x00100000; |
863 | } |
864 | |
865 | if (stat & 0x00200000) { |
866 | nv_error(priv, "TRAP ch %d [0x%010" PRIx64" %s]\n" , chid, inst << 12, |
867 | nouveau_client_name(engctx)); |
868 | nvc0_graph_trap_intr(priv); |
869 | nv_wr32(priv, 0x400100, 0x00200000); |
870 | stat &= ~0x00200000; |
871 | } |
872 | |
873 | if (stat & 0x00080000) { |
874 | nvc0_graph_ctxctl_isr(priv); |
875 | nv_wr32(priv, 0x400100, 0x00080000); |
876 | stat &= ~0x00080000; |
877 | } |
878 | |
879 | if (stat) { |
880 | nv_error(priv, "unknown stat 0x%08x\n" , stat); |
881 | nv_wr32(priv, 0x400100, stat); |
882 | } |
883 | |
884 | nv_wr32(priv, 0x400500, 0x00010001); |
885 | nouveau_engctx_put(engctx); |
886 | } |
887 | |
888 | static void |
889 | nvc0_graph_init_fw(struct nvc0_graph_priv *priv, u32 fuc_base, |
890 | struct nvc0_graph_fuc *code, struct nvc0_graph_fuc *data) |
891 | { |
892 | int i; |
893 | |
894 | nv_wr32(priv, fuc_base + 0x01c0, 0x01000000); |
895 | for (i = 0; i < data->size / 4; i++) |
896 | nv_wr32(priv, fuc_base + 0x01c4, data->data[i]); |
897 | |
898 | nv_wr32(priv, fuc_base + 0x0180, 0x01000000); |
899 | for (i = 0; i < code->size / 4; i++) { |
900 | if ((i & 0x3f) == 0) |
901 | nv_wr32(priv, fuc_base + 0x0188, i >> 6); |
902 | nv_wr32(priv, fuc_base + 0x0184, code->data[i]); |
903 | } |
904 | |
905 | /* code must be padded to 0x40 words */ |
906 | for (; i & 0x3f; i++) |
907 | nv_wr32(priv, fuc_base + 0x0184, 0); |
908 | } |
909 | |
910 | static void |
911 | nvc0_graph_init_csdata(struct nvc0_graph_priv *priv, |
912 | const struct nvc0_graph_pack *pack, |
913 | u32 falcon, u32 starstar, u32 base) |
914 | { |
915 | const struct nvc0_graph_pack *iter; |
916 | const struct nvc0_graph_init *init; |
917 | u32 addr = ~0, prev = ~0, xfer = 0; |
918 | u32 star, temp; |
919 | |
920 | nv_wr32(priv, falcon + 0x01c0, 0x02000000 + starstar); |
921 | star = nv_rd32(priv, falcon + 0x01c4); |
922 | temp = nv_rd32(priv, falcon + 0x01c4); |
923 | if (temp > star) |
924 | star = temp; |
925 | nv_wr32(priv, falcon + 0x01c0, 0x01000000 + star); |
926 | |
927 | pack_for_each_init(init, iter, pack) { |
928 | u32 head = init->addr - base; |
929 | u32 tail = head + init->count * init->pitch; |
930 | while (head < tail) { |
931 | if (head != prev + 4 || xfer >= 32) { |
932 | if (xfer) { |
933 | u32 data = ((--xfer << 26) | addr); |
934 | nv_wr32(priv, falcon + 0x01c4, data); |
935 | star += 4; |
936 | } |
937 | addr = head; |
938 | xfer = 0; |
939 | } |
940 | prev = head; |
941 | xfer = xfer + 1; |
942 | head = head + init->pitch; |
943 | } |
944 | } |
945 | |
946 | nv_wr32(priv, falcon + 0x01c4, (--xfer << 26) | addr); |
947 | nv_wr32(priv, falcon + 0x01c0, 0x01000004 + starstar); |
948 | nv_wr32(priv, falcon + 0x01c4, star + 4); |
949 | } |
950 | |
951 | int |
952 | nvc0_graph_init_ctxctl(struct nvc0_graph_priv *priv) |
953 | { |
954 | struct nvc0_graph_oclass *oclass = (void *)nv_object(priv)->oclass; |
955 | struct nvc0_grctx_oclass *cclass = (void *)nv_engine(priv)->cclass; |
956 | u32 r000260; |
957 | int i; |
958 | |
959 | if (priv->firmware) { |
960 | /* load fuc microcode */ |
961 | r000260 = nv_mask(priv, 0x000260, 0x00000001, 0x00000000); |
962 | nvc0_graph_init_fw(priv, 0x409000, &priv->fuc409c, |
963 | &priv->fuc409d); |
964 | nvc0_graph_init_fw(priv, 0x41a000, &priv->fuc41ac, |
965 | &priv->fuc41ad); |
966 | nv_wr32(priv, 0x000260, r000260); |
967 | |
968 | /* start both of them running */ |
969 | nv_wr32(priv, 0x409840, 0xffffffff); |
970 | nv_wr32(priv, 0x41a10c, 0x00000000); |
971 | nv_wr32(priv, 0x40910c, 0x00000000); |
972 | nv_wr32(priv, 0x41a100, 0x00000002); |
973 | nv_wr32(priv, 0x409100, 0x00000002); |
974 | if (!nv_wait(priv, 0x409800, 0x00000001, 0x00000001)) |
975 | nv_warn(priv, "0x409800 wait failed\n" ); |
976 | |
977 | nv_wr32(priv, 0x409840, 0xffffffff); |
978 | nv_wr32(priv, 0x409500, 0x7fffffff); |
979 | nv_wr32(priv, 0x409504, 0x00000021); |
980 | |
981 | nv_wr32(priv, 0x409840, 0xffffffff); |
982 | nv_wr32(priv, 0x409500, 0x00000000); |
983 | nv_wr32(priv, 0x409504, 0x00000010); |
984 | if (!nv_wait_ne(priv, 0x409800, 0xffffffff, 0x00000000)) { |
985 | nv_error(priv, "fuc09 req 0x10 timeout\n" ); |
986 | return -EBUSY; |
987 | } |
988 | priv->size = nv_rd32(priv, 0x409800); |
989 | |
990 | nv_wr32(priv, 0x409840, 0xffffffff); |
991 | nv_wr32(priv, 0x409500, 0x00000000); |
992 | nv_wr32(priv, 0x409504, 0x00000016); |
993 | if (!nv_wait_ne(priv, 0x409800, 0xffffffff, 0x00000000)) { |
994 | nv_error(priv, "fuc09 req 0x16 timeout\n" ); |
995 | return -EBUSY; |
996 | } |
997 | |
998 | nv_wr32(priv, 0x409840, 0xffffffff); |
999 | nv_wr32(priv, 0x409500, 0x00000000); |
1000 | nv_wr32(priv, 0x409504, 0x00000025); |
1001 | if (!nv_wait_ne(priv, 0x409800, 0xffffffff, 0x00000000)) { |
1002 | nv_error(priv, "fuc09 req 0x25 timeout\n" ); |
1003 | return -EBUSY; |
1004 | } |
1005 | |
1006 | if (nv_device(priv)->chipset >= 0xe0) { |
1007 | nv_wr32(priv, 0x409800, 0x00000000); |
1008 | nv_wr32(priv, 0x409500, 0x00000001); |
1009 | nv_wr32(priv, 0x409504, 0x00000030); |
1010 | if (!nv_wait_ne(priv, 0x409800, 0xffffffff, 0x00000000)) { |
1011 | nv_error(priv, "fuc09 req 0x30 timeout\n" ); |
1012 | return -EBUSY; |
1013 | } |
1014 | |
1015 | nv_wr32(priv, 0x409810, 0xb00095c8); |
1016 | nv_wr32(priv, 0x409800, 0x00000000); |
1017 | nv_wr32(priv, 0x409500, 0x00000001); |
1018 | nv_wr32(priv, 0x409504, 0x00000031); |
1019 | if (!nv_wait_ne(priv, 0x409800, 0xffffffff, 0x00000000)) { |
1020 | nv_error(priv, "fuc09 req 0x31 timeout\n" ); |
1021 | return -EBUSY; |
1022 | } |
1023 | |
1024 | nv_wr32(priv, 0x409810, 0x00080420); |
1025 | nv_wr32(priv, 0x409800, 0x00000000); |
1026 | nv_wr32(priv, 0x409500, 0x00000001); |
1027 | nv_wr32(priv, 0x409504, 0x00000032); |
1028 | if (!nv_wait_ne(priv, 0x409800, 0xffffffff, 0x00000000)) { |
1029 | nv_error(priv, "fuc09 req 0x32 timeout\n" ); |
1030 | return -EBUSY; |
1031 | } |
1032 | |
1033 | nv_wr32(priv, 0x409614, 0x00000070); |
1034 | nv_wr32(priv, 0x409614, 0x00000770); |
1035 | nv_wr32(priv, 0x40802c, 0x00000001); |
1036 | } |
1037 | |
1038 | if (priv->data == NULL) { |
1039 | int ret = nvc0_grctx_generate(priv); |
1040 | if (ret) { |
1041 | nv_error(priv, "failed to construct context\n" ); |
1042 | return ret; |
1043 | } |
1044 | } |
1045 | |
1046 | return 0; |
1047 | } else |
1048 | if (!oclass->fecs.ucode) { |
1049 | return -ENOSYS; |
1050 | } |
1051 | |
1052 | /* load HUB microcode */ |
1053 | r000260 = nv_mask(priv, 0x000260, 0x00000001, 0x00000000); |
1054 | nv_wr32(priv, 0x4091c0, 0x01000000); |
1055 | for (i = 0; i < oclass->fecs.ucode->data.size / 4; i++) |
1056 | nv_wr32(priv, 0x4091c4, oclass->fecs.ucode->data.data[i]); |
1057 | |
1058 | nv_wr32(priv, 0x409180, 0x01000000); |
1059 | for (i = 0; i < oclass->fecs.ucode->code.size / 4; i++) { |
1060 | if ((i & 0x3f) == 0) |
1061 | nv_wr32(priv, 0x409188, i >> 6); |
1062 | nv_wr32(priv, 0x409184, oclass->fecs.ucode->code.data[i]); |
1063 | } |
1064 | |
1065 | /* load GPC microcode */ |
1066 | nv_wr32(priv, 0x41a1c0, 0x01000000); |
1067 | for (i = 0; i < oclass->gpccs.ucode->data.size / 4; i++) |
1068 | nv_wr32(priv, 0x41a1c4, oclass->gpccs.ucode->data.data[i]); |
1069 | |
1070 | nv_wr32(priv, 0x41a180, 0x01000000); |
1071 | for (i = 0; i < oclass->gpccs.ucode->code.size / 4; i++) { |
1072 | if ((i & 0x3f) == 0) |
1073 | nv_wr32(priv, 0x41a188, i >> 6); |
1074 | nv_wr32(priv, 0x41a184, oclass->gpccs.ucode->code.data[i]); |
1075 | } |
1076 | nv_wr32(priv, 0x000260, r000260); |
1077 | |
1078 | /* load register lists */ |
1079 | nvc0_graph_init_csdata(priv, cclass->hub, 0x409000, 0x000, 0x000000); |
1080 | nvc0_graph_init_csdata(priv, cclass->gpc, 0x41a000, 0x000, 0x418000); |
1081 | nvc0_graph_init_csdata(priv, cclass->tpc, 0x41a000, 0x004, 0x419800); |
1082 | nvc0_graph_init_csdata(priv, cclass->ppc, 0x41a000, 0x008, 0x41be00); |
1083 | |
1084 | /* start HUB ucode running, it'll init the GPCs */ |
1085 | nv_wr32(priv, 0x40910c, 0x00000000); |
1086 | nv_wr32(priv, 0x409100, 0x00000002); |
1087 | if (!nv_wait(priv, 0x409800, 0x80000000, 0x80000000)) { |
1088 | nv_error(priv, "HUB_INIT timed out\n" ); |
1089 | nvc0_graph_ctxctl_debug(priv); |
1090 | return -EBUSY; |
1091 | } |
1092 | |
1093 | priv->size = nv_rd32(priv, 0x409804); |
1094 | if (priv->data == NULL) { |
1095 | int ret = nvc0_grctx_generate(priv); |
1096 | if (ret) { |
1097 | nv_error(priv, "failed to construct context\n" ); |
1098 | return ret; |
1099 | } |
1100 | } |
1101 | |
1102 | return 0; |
1103 | } |
1104 | |
1105 | int |
1106 | nvc0_graph_init(struct nouveau_object *object) |
1107 | { |
1108 | struct nvc0_graph_oclass *oclass = (void *)object->oclass; |
1109 | struct nvc0_graph_priv *priv = (void *)object; |
1110 | const u32 magicgpc918 = DIV_ROUND_UP(0x00800000, priv->tpc_total); |
1111 | u32 data[TPC_MAX / 8] = {}; |
1112 | u8 tpcnr[GPC_MAX]; |
1113 | int gpc, tpc, rop; |
1114 | int ret, i; |
1115 | |
1116 | ret = nouveau_graph_init(&priv->base); |
1117 | if (ret) |
1118 | return ret; |
1119 | |
1120 | nv_wr32(priv, GPC_BCAST(0x0880), 0x00000000); |
1121 | nv_wr32(priv, GPC_BCAST(0x08a4), 0x00000000); |
1122 | nv_wr32(priv, GPC_BCAST(0x0888), 0x00000000); |
1123 | nv_wr32(priv, GPC_BCAST(0x088c), 0x00000000); |
1124 | nv_wr32(priv, GPC_BCAST(0x0890), 0x00000000); |
1125 | nv_wr32(priv, GPC_BCAST(0x0894), 0x00000000); |
1126 | nv_wr32(priv, GPC_BCAST(0x08b4), priv->unk4188b4->addr >> 8); |
1127 | nv_wr32(priv, GPC_BCAST(0x08b8), priv->unk4188b8->addr >> 8); |
1128 | |
1129 | nvc0_graph_mmio(priv, oclass->mmio); |
1130 | |
1131 | memcpy(tpcnr, priv->tpc_nr, sizeof(priv->tpc_nr)); |
1132 | for (i = 0, gpc = -1; i < priv->tpc_total; i++) { |
1133 | do { |
1134 | gpc = (gpc + 1) % priv->gpc_nr; |
1135 | } while (!tpcnr[gpc]); |
1136 | tpc = priv->tpc_nr[gpc] - tpcnr[gpc]--; |
1137 | |
1138 | data[i / 8] |= tpc << ((i % 8) * 4); |
1139 | } |
1140 | |
1141 | nv_wr32(priv, GPC_BCAST(0x0980), data[0]); |
1142 | nv_wr32(priv, GPC_BCAST(0x0984), data[1]); |
1143 | nv_wr32(priv, GPC_BCAST(0x0988), data[2]); |
1144 | nv_wr32(priv, GPC_BCAST(0x098c), data[3]); |
1145 | |
1146 | for (gpc = 0; gpc < priv->gpc_nr; gpc++) { |
1147 | nv_wr32(priv, GPC_UNIT(gpc, 0x0914), |
1148 | priv->magic_not_rop_nr << 8 | priv->tpc_nr[gpc]); |
1149 | nv_wr32(priv, GPC_UNIT(gpc, 0x0910), 0x00040000 | |
1150 | priv->tpc_total); |
1151 | nv_wr32(priv, GPC_UNIT(gpc, 0x0918), magicgpc918); |
1152 | } |
1153 | |
1154 | if (nv_device(priv)->chipset != 0xd7) |
1155 | nv_wr32(priv, GPC_BCAST(0x1bd4), magicgpc918); |
1156 | else |
1157 | nv_wr32(priv, GPC_BCAST(0x3fd4), magicgpc918); |
1158 | |
1159 | nv_wr32(priv, GPC_BCAST(0x08ac), nv_rd32(priv, 0x100800)); |
1160 | |
1161 | nv_wr32(priv, 0x400500, 0x00010001); |
1162 | |
1163 | nv_wr32(priv, 0x400100, 0xffffffff); |
1164 | nv_wr32(priv, 0x40013c, 0xffffffff); |
1165 | |
1166 | nv_wr32(priv, 0x409c24, 0x000f0000); |
1167 | nv_wr32(priv, 0x404000, 0xc0000000); |
1168 | nv_wr32(priv, 0x404600, 0xc0000000); |
1169 | nv_wr32(priv, 0x408030, 0xc0000000); |
1170 | nv_wr32(priv, 0x40601c, 0xc0000000); |
1171 | nv_wr32(priv, 0x404490, 0xc0000000); |
1172 | nv_wr32(priv, 0x406018, 0xc0000000); |
1173 | nv_wr32(priv, 0x405840, 0xc0000000); |
1174 | nv_wr32(priv, 0x405844, 0x00ffffff); |
1175 | nv_mask(priv, 0x419cc0, 0x00000008, 0x00000008); |
1176 | nv_mask(priv, 0x419eb4, 0x00001000, 0x00001000); |
1177 | |
1178 | for (gpc = 0; gpc < priv->gpc_nr; gpc++) { |
1179 | nv_wr32(priv, GPC_UNIT(gpc, 0x0420), 0xc0000000); |
1180 | nv_wr32(priv, GPC_UNIT(gpc, 0x0900), 0xc0000000); |
1181 | nv_wr32(priv, GPC_UNIT(gpc, 0x1028), 0xc0000000); |
1182 | nv_wr32(priv, GPC_UNIT(gpc, 0x0824), 0xc0000000); |
1183 | for (tpc = 0; tpc < priv->tpc_nr[gpc]; tpc++) { |
1184 | nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x508), 0xffffffff); |
1185 | nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x50c), 0xffffffff); |
1186 | nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x224), 0xc0000000); |
1187 | nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x48c), 0xc0000000); |
1188 | nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x084), 0xc0000000); |
1189 | nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x644), 0x001ffffe); |
1190 | nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x64c), 0x0000000f); |
1191 | } |
1192 | nv_wr32(priv, GPC_UNIT(gpc, 0x2c90), 0xffffffff); |
1193 | nv_wr32(priv, GPC_UNIT(gpc, 0x2c94), 0xffffffff); |
1194 | } |
1195 | |
1196 | for (rop = 0; rop < priv->rop_nr; rop++) { |
1197 | nv_wr32(priv, ROP_UNIT(rop, 0x144), 0xc0000000); |
1198 | nv_wr32(priv, ROP_UNIT(rop, 0x070), 0xc0000000); |
1199 | nv_wr32(priv, ROP_UNIT(rop, 0x204), 0xffffffff); |
1200 | nv_wr32(priv, ROP_UNIT(rop, 0x208), 0xffffffff); |
1201 | } |
1202 | |
1203 | nv_wr32(priv, 0x400108, 0xffffffff); |
1204 | nv_wr32(priv, 0x400138, 0xffffffff); |
1205 | nv_wr32(priv, 0x400118, 0xffffffff); |
1206 | nv_wr32(priv, 0x400130, 0xffffffff); |
1207 | nv_wr32(priv, 0x40011c, 0xffffffff); |
1208 | nv_wr32(priv, 0x400134, 0xffffffff); |
1209 | |
1210 | nv_wr32(priv, 0x400054, 0x34ce3464); |
1211 | return nvc0_graph_init_ctxctl(priv); |
1212 | } |
1213 | |
1214 | static void |
1215 | nvc0_graph_dtor_fw(struct nvc0_graph_fuc *fuc) |
1216 | { |
1217 | kfree(fuc->data); |
1218 | fuc->data = NULL; |
1219 | } |
1220 | |
1221 | static int |
1222 | nvc0_graph_ctor_fw(struct nvc0_graph_priv *priv, const char *fwname, |
1223 | struct nvc0_graph_fuc *fuc) |
1224 | { |
1225 | struct nouveau_device *device = nv_device(priv); |
1226 | const struct firmware *fw; |
1227 | char f[32]; |
1228 | int ret; |
1229 | |
1230 | snprintf(f, sizeof(f), "nouveau/nv%02x_%s" , device->chipset, fwname); |
1231 | ret = request_firmware(&fw, f, nv_device_base(device)); |
1232 | if (ret) { |
1233 | snprintf(f, sizeof(f), "nouveau/%s" , fwname); |
1234 | ret = request_firmware(&fw, f, nv_device_base(device)); |
1235 | if (ret) { |
1236 | nv_error(priv, "failed to load %s\n" , fwname); |
1237 | return ret; |
1238 | } |
1239 | } |
1240 | |
1241 | fuc->size = fw->size; |
1242 | fuc->data = kmemdup(fw->data, fuc->size, GFP_KERNEL); |
1243 | release_firmware(fw); |
1244 | return (fuc->data != NULL) ? 0 : -ENOMEM; |
1245 | } |
1246 | |
1247 | void |
1248 | nvc0_graph_dtor(struct nouveau_object *object) |
1249 | { |
1250 | struct nvc0_graph_priv *priv = (void *)object; |
1251 | |
1252 | kfree(priv->data); |
1253 | |
1254 | nvc0_graph_dtor_fw(&priv->fuc409c); |
1255 | nvc0_graph_dtor_fw(&priv->fuc409d); |
1256 | nvc0_graph_dtor_fw(&priv->fuc41ac); |
1257 | nvc0_graph_dtor_fw(&priv->fuc41ad); |
1258 | |
1259 | nouveau_gpuobj_ref(NULL, &priv->unk4188b8); |
1260 | nouveau_gpuobj_ref(NULL, &priv->unk4188b4); |
1261 | |
1262 | nouveau_graph_destroy(&priv->base); |
1263 | } |
1264 | |
1265 | int |
1266 | nvc0_graph_ctor(struct nouveau_object *parent, struct nouveau_object *engine, |
1267 | struct nouveau_oclass *bclass, void *data, u32 size, |
1268 | struct nouveau_object **pobject) |
1269 | { |
1270 | struct nvc0_graph_oclass *oclass = (void *)bclass; |
1271 | struct nouveau_device *device = nv_device(parent); |
1272 | struct nvc0_graph_priv *priv; |
1273 | bool use_ext_fw, enable; |
1274 | int ret, i; |
1275 | |
1276 | use_ext_fw = nouveau_boolopt(device->cfgopt, "NvGrUseFW" , |
1277 | oclass->fecs.ucode == NULL); |
1278 | enable = use_ext_fw || oclass->fecs.ucode != NULL; |
1279 | |
1280 | ret = nouveau_graph_create(parent, engine, bclass, enable, &priv); |
1281 | *pobject = nv_object(priv); |
1282 | if (ret) |
1283 | return ret; |
1284 | |
1285 | nv_subdev(priv)->unit = 0x08001000; |
1286 | nv_subdev(priv)->intr = nvc0_graph_intr; |
1287 | |
1288 | priv->base.units = nvc0_graph_units; |
1289 | |
1290 | if (use_ext_fw) { |
1291 | nv_info(priv, "using external firmware\n" ); |
1292 | if (nvc0_graph_ctor_fw(priv, "fuc409c" , &priv->fuc409c) || |
1293 | nvc0_graph_ctor_fw(priv, "fuc409d" , &priv->fuc409d) || |
1294 | nvc0_graph_ctor_fw(priv, "fuc41ac" , &priv->fuc41ac) || |
1295 | nvc0_graph_ctor_fw(priv, "fuc41ad" , &priv->fuc41ad)) |
1296 | return -EINVAL; |
1297 | priv->firmware = true; |
1298 | } |
1299 | |
1300 | ret = nouveau_gpuobj_new(nv_object(priv), NULL, 0x1000, 256, 0, |
1301 | &priv->unk4188b4); |
1302 | if (ret) |
1303 | return ret; |
1304 | |
1305 | ret = nouveau_gpuobj_new(nv_object(priv), NULL, 0x1000, 256, 0, |
1306 | &priv->unk4188b8); |
1307 | if (ret) |
1308 | return ret; |
1309 | |
1310 | for (i = 0; i < 0x1000; i += 4) { |
1311 | nv_wo32(priv->unk4188b4, i, 0x00000010); |
1312 | nv_wo32(priv->unk4188b8, i, 0x00000010); |
1313 | } |
1314 | |
1315 | priv->rop_nr = (nv_rd32(priv, 0x409604) & 0x001f0000) >> 16; |
1316 | priv->gpc_nr = nv_rd32(priv, 0x409604) & 0x0000001f; |
1317 | for (i = 0; i < priv->gpc_nr; i++) { |
1318 | priv->tpc_nr[i] = nv_rd32(priv, GPC_UNIT(i, 0x2608)); |
1319 | priv->tpc_total += priv->tpc_nr[i]; |
1320 | } |
1321 | |
1322 | /*XXX: these need figuring out... though it might not even matter */ |
1323 | switch (nv_device(priv)->chipset) { |
1324 | case 0xc0: |
1325 | if (priv->tpc_total == 11) { /* 465, 3/4/4/0, 4 */ |
1326 | priv->magic_not_rop_nr = 0x07; |
1327 | } else |
1328 | if (priv->tpc_total == 14) { /* 470, 3/3/4/4, 5 */ |
1329 | priv->magic_not_rop_nr = 0x05; |
1330 | } else |
1331 | if (priv->tpc_total == 15) { /* 480, 3/4/4/4, 6 */ |
1332 | priv->magic_not_rop_nr = 0x06; |
1333 | } |
1334 | break; |
1335 | case 0xc3: /* 450, 4/0/0/0, 2 */ |
1336 | priv->magic_not_rop_nr = 0x03; |
1337 | break; |
1338 | case 0xc4: /* 460, 3/4/0/0, 4 */ |
1339 | priv->magic_not_rop_nr = 0x01; |
1340 | break; |
1341 | case 0xc1: /* 2/0/0/0, 1 */ |
1342 | priv->magic_not_rop_nr = 0x01; |
1343 | break; |
1344 | case 0xc8: /* 4/4/3/4, 5 */ |
1345 | priv->magic_not_rop_nr = 0x06; |
1346 | break; |
1347 | case 0xce: /* 4/4/0/0, 4 */ |
1348 | priv->magic_not_rop_nr = 0x03; |
1349 | break; |
1350 | case 0xcf: /* 4/0/0/0, 3 */ |
1351 | priv->magic_not_rop_nr = 0x03; |
1352 | break; |
1353 | case 0xd7: |
1354 | case 0xd9: /* 1/0/0/0, 1 */ |
1355 | priv->magic_not_rop_nr = 0x01; |
1356 | break; |
1357 | } |
1358 | |
1359 | nv_engine(priv)->cclass = *oclass->cclass; |
1360 | nv_engine(priv)->sclass = oclass->sclass; |
1361 | return 0; |
1362 | } |
1363 | |
1364 | #include "fuc/hubnvc0.fuc.h" |
1365 | |
1366 | struct nvc0_graph_ucode |
1367 | nvc0_graph_fecs_ucode = { |
1368 | .code.data = nvc0_grhub_code, |
1369 | .code.size = sizeof(nvc0_grhub_code), |
1370 | .data.data = nvc0_grhub_data, |
1371 | .data.size = sizeof(nvc0_grhub_data), |
1372 | }; |
1373 | |
1374 | #include "fuc/gpcnvc0.fuc.h" |
1375 | |
1376 | struct nvc0_graph_ucode |
1377 | nvc0_graph_gpccs_ucode = { |
1378 | .code.data = nvc0_grgpc_code, |
1379 | .code.size = sizeof(nvc0_grgpc_code), |
1380 | .data.data = nvc0_grgpc_data, |
1381 | .data.size = sizeof(nvc0_grgpc_data), |
1382 | }; |
1383 | |
1384 | struct nouveau_oclass * |
1385 | nvc0_graph_oclass = &(struct nvc0_graph_oclass) { |
1386 | .base.handle = NV_ENGINE(GR, 0xc0), |
1387 | .base.ofuncs = &(struct nouveau_ofuncs) { |
1388 | .ctor = nvc0_graph_ctor, |
1389 | .dtor = nvc0_graph_dtor, |
1390 | .init = nvc0_graph_init, |
1391 | .fini = _nouveau_graph_fini, |
1392 | }, |
1393 | .cclass = &nvc0_grctx_oclass, |
1394 | .sclass = nvc0_graph_sclass, |
1395 | .mmio = nvc0_graph_pack_mmio, |
1396 | .fecs.ucode = &nvc0_graph_fecs_ucode, |
1397 | .gpccs.ucode = &nvc0_graph_gpccs_ucode, |
1398 | }.base; |
1399 | |