blob: 97416c6a0d1b3843238a6d078e8a3d238624d422 [file] [log] [blame]
Ben Skeggs4b223ee2010-08-03 10:00:56 +10001/*
Ben Skeggsebb945a2012-07-20 08:17:34 +10002 * Copyright 2012 Red Hat Inc.
Ben Skeggs4b223ee2010-08-03 10:00:56 +10003 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20 * OTHER DEALINGS IN THE SOFTWARE.
21 *
22 * Authors: Ben Skeggs
23 */
24
Ben Skeggsebb945a2012-07-20 08:17:34 +100025#include <core/client.h>
26#include <core/handle.h>
27#include <core/namedb.h>
28#include <core/gpuobj.h>
29#include <core/engctx.h>
Ben Skeggs9bd2ddb2013-01-31 13:51:20 +100030#include <core/event.h>
Ben Skeggsebb945a2012-07-20 08:17:34 +100031#include <core/class.h>
Ben Skeggsebb945a2012-07-20 08:17:34 +100032#include <core/enum.h>
Ben Skeggs4b223ee2010-08-03 10:00:56 +100033
Ben Skeggsebb945a2012-07-20 08:17:34 +100034#include <subdev/timer.h>
35#include <subdev/bar.h>
Ben Skeggs52225552013-12-23 01:51:16 +100036#include <subdev/fb.h>
Ben Skeggsebb945a2012-07-20 08:17:34 +100037#include <subdev/vm.h>
38
39#include <engine/dmaobj.h>
Ben Skeggs02a841d2012-07-04 23:44:54 +100040#include <engine/fifo.h>
Ben Skeggsb2b09932010-11-24 10:47:15 +100041
42struct nvc0_fifo_priv {
Ben Skeggsebb945a2012-07-20 08:17:34 +100043 struct nouveau_fifo base;
Ben Skeggsa07d0e72014-02-22 00:28:47 +100044 struct {
45 struct nouveau_gpuobj *mem[2];
46 int active;
47 wait_queue_head_t wait;
48 } runlist;
Ben Skeggs9da226f2012-07-13 16:54:45 +100049 struct {
50 struct nouveau_gpuobj *mem;
51 struct nouveau_vma bar;
52 } user;
Ben Skeggsec9c0882010-12-31 12:10:49 +100053 int spoon_nr;
Ben Skeggsb2b09932010-11-24 10:47:15 +100054};
55
Ben Skeggsebb945a2012-07-20 08:17:34 +100056struct nvc0_fifo_base {
57 struct nouveau_fifo_base base;
58 struct nouveau_gpuobj *pgd;
59 struct nouveau_vm *vm;
60};
61
Ben Skeggsb2b09932010-11-24 10:47:15 +100062struct nvc0_fifo_chan {
Ben Skeggsc420b2d2012-05-01 20:48:08 +100063 struct nouveau_fifo_chan base;
Ben Skeggse2822b72014-02-22 00:52:45 +100064 enum {
65 STOPPED,
66 RUNNING,
67 KILLED
68 } state;
Ben Skeggsb2b09932010-11-24 10:47:15 +100069};
70
Ben Skeggsebb945a2012-07-20 08:17:34 +100071/*******************************************************************************
72 * FIFO channel objects
73 ******************************************************************************/
74
Ben Skeggsb2b09932010-11-24 10:47:15 +100075static void
Ben Skeggs03574662014-01-28 11:47:46 +100076nvc0_fifo_runlist_update(struct nvc0_fifo_priv *priv)
Ben Skeggsb2b09932010-11-24 10:47:15 +100077{
Ben Skeggsebb945a2012-07-20 08:17:34 +100078 struct nouveau_bar *bar = nouveau_bar(priv);
Ben Skeggsb2b09932010-11-24 10:47:15 +100079 struct nouveau_gpuobj *cur;
80 int i, p;
81
Ben Skeggsfadb1712013-05-13 10:02:11 +100082 mutex_lock(&nv_subdev(priv)->mutex);
Ben Skeggsa07d0e72014-02-22 00:28:47 +100083 cur = priv->runlist.mem[priv->runlist.active];
84 priv->runlist.active = !priv->runlist.active;
Ben Skeggsb2b09932010-11-24 10:47:15 +100085
86 for (i = 0, p = 0; i < 128; i++) {
Ben Skeggse2822b72014-02-22 00:52:45 +100087 struct nvc0_fifo_chan *chan = (void *)priv->base.channel[i];
88 if (chan && chan->state == RUNNING) {
89 nv_wo32(cur, p + 0, i);
90 nv_wo32(cur, p + 4, 0x00000004);
91 p += 8;
92 }
Ben Skeggsb2b09932010-11-24 10:47:15 +100093 }
Ben Skeggsebb945a2012-07-20 08:17:34 +100094 bar->flush(bar);
Ben Skeggsb2b09932010-11-24 10:47:15 +100095
Ben Skeggsebb945a2012-07-20 08:17:34 +100096 nv_wr32(priv, 0x002270, cur->addr >> 12);
97 nv_wr32(priv, 0x002274, 0x01f00000 | (p >> 3));
Ben Skeggse2822b72014-02-22 00:52:45 +100098
Ben Skeggsebb945a2012-07-20 08:17:34 +100099 if (!nv_wait(priv, 0x00227c, 0x00100000, 0x00000000))
Ben Skeggs03574662014-01-28 11:47:46 +1000100 nv_error(priv, "runlist update failed\n");
Ben Skeggsfadb1712013-05-13 10:02:11 +1000101 mutex_unlock(&nv_subdev(priv)->mutex);
Ben Skeggsb2b09932010-11-24 10:47:15 +1000102}
Ben Skeggs4b223ee2010-08-03 10:00:56 +1000103
Ben Skeggsc420b2d2012-05-01 20:48:08 +1000104static int
Ben Skeggsebb945a2012-07-20 08:17:34 +1000105nvc0_fifo_context_attach(struct nouveau_object *parent,
106 struct nouveau_object *object)
Ben Skeggs4b223ee2010-08-03 10:00:56 +1000107{
Ben Skeggsebb945a2012-07-20 08:17:34 +1000108 struct nouveau_bar *bar = nouveau_bar(parent);
109 struct nvc0_fifo_base *base = (void *)parent->parent;
110 struct nouveau_engctx *ectx = (void *)object;
111 u32 addr;
112 int ret;
Ben Skeggsb2b09932010-11-24 10:47:15 +1000113
Ben Skeggsebb945a2012-07-20 08:17:34 +1000114 switch (nv_engidx(object->engine)) {
115 case NVDEV_ENGINE_SW : return 0;
116 case NVDEV_ENGINE_GR : addr = 0x0210; break;
117 case NVDEV_ENGINE_COPY0: addr = 0x0230; break;
118 case NVDEV_ENGINE_COPY1: addr = 0x0240; break;
Maarten Lankhorst23c14ed2012-11-23 11:08:23 +1000119 case NVDEV_ENGINE_BSP : addr = 0x0270; break;
120 case NVDEV_ENGINE_VP : addr = 0x0250; break;
121 case NVDEV_ENGINE_PPP : addr = 0x0260; break;
Ben Skeggsebb945a2012-07-20 08:17:34 +1000122 default:
123 return -EINVAL;
Ben Skeggsb2b09932010-11-24 10:47:15 +1000124 }
125
Ben Skeggsebb945a2012-07-20 08:17:34 +1000126 if (!ectx->vma.node) {
127 ret = nouveau_gpuobj_map_vm(nv_gpuobj(ectx), base->vm,
128 NV_MEM_ACCESS_RW, &ectx->vma);
129 if (ret)
130 return ret;
Ben Skeggs4c2d4222012-08-10 15:10:34 +1000131
132 nv_engctx(ectx)->addr = nv_gpuobj(base)->addr >> 12;
Ben Skeggsebb945a2012-07-20 08:17:34 +1000133 }
Ben Skeggsb2b09932010-11-24 10:47:15 +1000134
Ben Skeggsebb945a2012-07-20 08:17:34 +1000135 nv_wo32(base, addr + 0x00, lower_32_bits(ectx->vma.offset) | 4);
136 nv_wo32(base, addr + 0x04, upper_32_bits(ectx->vma.offset));
137 bar->flush(bar);
138 return 0;
139}
Ben Skeggsb2b09932010-11-24 10:47:15 +1000140
Ben Skeggsebb945a2012-07-20 08:17:34 +1000141static int
142nvc0_fifo_context_detach(struct nouveau_object *parent, bool suspend,
143 struct nouveau_object *object)
144{
145 struct nouveau_bar *bar = nouveau_bar(parent);
146 struct nvc0_fifo_priv *priv = (void *)parent->engine;
147 struct nvc0_fifo_base *base = (void *)parent->parent;
148 struct nvc0_fifo_chan *chan = (void *)parent;
149 u32 addr;
150
151 switch (nv_engidx(object->engine)) {
152 case NVDEV_ENGINE_SW : return 0;
153 case NVDEV_ENGINE_GR : addr = 0x0210; break;
154 case NVDEV_ENGINE_COPY0: addr = 0x0230; break;
155 case NVDEV_ENGINE_COPY1: addr = 0x0240; break;
Maarten Lankhorst23c14ed2012-11-23 11:08:23 +1000156 case NVDEV_ENGINE_BSP : addr = 0x0270; break;
157 case NVDEV_ENGINE_VP : addr = 0x0250; break;
158 case NVDEV_ENGINE_PPP : addr = 0x0260; break;
Ben Skeggsebb945a2012-07-20 08:17:34 +1000159 default:
160 return -EINVAL;
161 }
162
Ben Skeggsebb945a2012-07-20 08:17:34 +1000163 nv_wr32(priv, 0x002634, chan->base.chid);
164 if (!nv_wait(priv, 0x002634, 0xffffffff, chan->base.chid)) {
Marcin Slusarz93260d32012-12-09 23:00:34 +0100165 nv_error(priv, "channel %d [%s] kick timeout\n",
166 chan->base.chid, nouveau_client_name(chan));
Ben Skeggsebb945a2012-07-20 08:17:34 +1000167 if (suspend)
168 return -EBUSY;
169 }
170
Ben Skeggsedc260d2012-11-27 11:05:36 +1000171 nv_wo32(base, addr + 0x00, 0x00000000);
172 nv_wo32(base, addr + 0x04, 0x00000000);
173 bar->flush(bar);
Ben Skeggsebb945a2012-07-20 08:17:34 +1000174 return 0;
175}
176
177static int
178nvc0_fifo_chan_ctor(struct nouveau_object *parent,
179 struct nouveau_object *engine,
180 struct nouveau_oclass *oclass, void *data, u32 size,
181 struct nouveau_object **pobject)
182{
183 struct nouveau_bar *bar = nouveau_bar(parent);
184 struct nvc0_fifo_priv *priv = (void *)engine;
185 struct nvc0_fifo_base *base = (void *)parent;
186 struct nvc0_fifo_chan *chan;
Ben Skeggsdbff2de2012-08-06 18:16:37 +1000187 struct nv50_channel_ind_class *args = data;
Ben Skeggsebb945a2012-07-20 08:17:34 +1000188 u64 usermem, ioffset, ilength;
189 int ret, i;
190
191 if (size < sizeof(*args))
192 return -EINVAL;
193
194 ret = nouveau_fifo_channel_create(parent, engine, oclass, 1,
195 priv->user.bar.offset, 0x1000,
196 args->pushbuf,
Martin Peres507ceb12012-11-27 00:30:32 +0100197 (1ULL << NVDEV_ENGINE_SW) |
198 (1ULL << NVDEV_ENGINE_GR) |
199 (1ULL << NVDEV_ENGINE_COPY0) |
200 (1ULL << NVDEV_ENGINE_COPY1) |
201 (1ULL << NVDEV_ENGINE_BSP) |
202 (1ULL << NVDEV_ENGINE_VP) |
203 (1ULL << NVDEV_ENGINE_PPP), &chan);
Ben Skeggsebb945a2012-07-20 08:17:34 +1000204 *pobject = nv_object(chan);
Ben Skeggsc420b2d2012-05-01 20:48:08 +1000205 if (ret)
Ben Skeggsebb945a2012-07-20 08:17:34 +1000206 return ret;
207
208 nv_parent(chan)->context_attach = nvc0_fifo_context_attach;
209 nv_parent(chan)->context_detach = nvc0_fifo_context_detach;
210
211 usermem = chan->base.chid * 0x1000;
212 ioffset = args->ioffset;
Ilia Mirkin57be0462013-07-27 00:27:00 -0400213 ilength = order_base_2(args->ilength / 8);
Ben Skeggsebb945a2012-07-20 08:17:34 +1000214
215 for (i = 0; i < 0x1000; i += 4)
216 nv_wo32(priv->user.mem, usermem + i, 0x00000000);
217
218 nv_wo32(base, 0x08, lower_32_bits(priv->user.mem->addr + usermem));
219 nv_wo32(base, 0x0c, upper_32_bits(priv->user.mem->addr + usermem));
220 nv_wo32(base, 0x10, 0x0000face);
221 nv_wo32(base, 0x30, 0xfffff902);
222 nv_wo32(base, 0x48, lower_32_bits(ioffset));
223 nv_wo32(base, 0x4c, upper_32_bits(ioffset) | (ilength << 16));
224 nv_wo32(base, 0x54, 0x00000002);
225 nv_wo32(base, 0x84, 0x20400000);
226 nv_wo32(base, 0x94, 0x30000001);
227 nv_wo32(base, 0x9c, 0x00000100);
228 nv_wo32(base, 0xa4, 0x1f1f1f1f);
229 nv_wo32(base, 0xa8, 0x1f1f1f1f);
230 nv_wo32(base, 0xac, 0x0000001f);
231 nv_wo32(base, 0xb8, 0xf8000000);
232 nv_wo32(base, 0xf8, 0x10003080); /* 0x002310 */
233 nv_wo32(base, 0xfc, 0x10000010); /* 0x002350 */
234 bar->flush(bar);
235 return 0;
236}
237
238static int
239nvc0_fifo_chan_init(struct nouveau_object *object)
240{
241 struct nouveau_gpuobj *base = nv_gpuobj(object->parent);
242 struct nvc0_fifo_priv *priv = (void *)object->engine;
243 struct nvc0_fifo_chan *chan = (void *)object;
244 u32 chid = chan->base.chid;
245 int ret;
246
247 ret = nouveau_fifo_channel_init(&chan->base);
248 if (ret)
249 return ret;
250
251 nv_wr32(priv, 0x003000 + (chid * 8), 0xc0000000 | base->addr >> 12);
Ben Skeggse2822b72014-02-22 00:52:45 +1000252
253 if (chan->state == STOPPED && (chan->state = RUNNING) == RUNNING) {
254 nv_wr32(priv, 0x003004 + (chid * 8), 0x001f0001);
255 nvc0_fifo_runlist_update(priv);
256 }
257
Ben Skeggsebb945a2012-07-20 08:17:34 +1000258 return 0;
259}
260
Ben Skeggse99bf012014-02-22 00:18:17 +1000261static void nvc0_fifo_intr_engine(struct nvc0_fifo_priv *priv);
262
Ben Skeggsebb945a2012-07-20 08:17:34 +1000263static int
264nvc0_fifo_chan_fini(struct nouveau_object *object, bool suspend)
265{
266 struct nvc0_fifo_priv *priv = (void *)object->engine;
267 struct nvc0_fifo_chan *chan = (void *)object;
268 u32 chid = chan->base.chid;
269
Ben Skeggse2822b72014-02-22 00:52:45 +1000270 if (chan->state == RUNNING && (chan->state = STOPPED) == STOPPED) {
271 nv_mask(priv, 0x003004 + (chid * 8), 0x00000001, 0x00000000);
272 nvc0_fifo_runlist_update(priv);
273 }
Ben Skeggse99bf012014-02-22 00:18:17 +1000274
275 nvc0_fifo_intr_engine(priv);
276
Ben Skeggsebb945a2012-07-20 08:17:34 +1000277 nv_wr32(priv, 0x003000 + (chid * 8), 0x00000000);
Ben Skeggsebb945a2012-07-20 08:17:34 +1000278 return nouveau_fifo_channel_fini(&chan->base, suspend);
279}
280
281static struct nouveau_ofuncs
282nvc0_fifo_ofuncs = {
283 .ctor = nvc0_fifo_chan_ctor,
284 .dtor = _nouveau_fifo_channel_dtor,
285 .init = nvc0_fifo_chan_init,
286 .fini = nvc0_fifo_chan_fini,
287 .rd32 = _nouveau_fifo_channel_rd32,
288 .wr32 = _nouveau_fifo_channel_wr32,
289};
290
291static struct nouveau_oclass
292nvc0_fifo_sclass[] = {
Ben Skeggsc97f8c92012-08-19 16:03:00 +1000293 { NVC0_CHANNEL_IND_CLASS, &nvc0_fifo_ofuncs },
Ben Skeggsebb945a2012-07-20 08:17:34 +1000294 {}
295};
296
297/*******************************************************************************
298 * FIFO context - instmem heap and vm setup
299 ******************************************************************************/
300
301static int
302nvc0_fifo_context_ctor(struct nouveau_object *parent,
303 struct nouveau_object *engine,
304 struct nouveau_oclass *oclass, void *data, u32 size,
305 struct nouveau_object **pobject)
306{
307 struct nvc0_fifo_base *base;
308 int ret;
309
310 ret = nouveau_fifo_context_create(parent, engine, oclass, NULL, 0x1000,
311 0x1000, NVOBJ_FLAG_ZERO_ALLOC |
312 NVOBJ_FLAG_HEAP, &base);
313 *pobject = nv_object(base);
314 if (ret)
315 return ret;
316
Ben Skeggsf50c8052013-04-24 18:02:35 +1000317 ret = nouveau_gpuobj_new(nv_object(base), NULL, 0x10000, 0x1000, 0,
318 &base->pgd);
Ben Skeggsebb945a2012-07-20 08:17:34 +1000319 if (ret)
320 return ret;
321
322 nv_wo32(base, 0x0200, lower_32_bits(base->pgd->addr));
323 nv_wo32(base, 0x0204, upper_32_bits(base->pgd->addr));
324 nv_wo32(base, 0x0208, 0xffffffff);
325 nv_wo32(base, 0x020c, 0x000000ff);
326
327 ret = nouveau_vm_ref(nouveau_client(parent)->vm, &base->vm, base->pgd);
328 if (ret)
329 return ret;
330
331 return 0;
Ben Skeggs4b223ee2010-08-03 10:00:56 +1000332}
333
Ben Skeggsc420b2d2012-05-01 20:48:08 +1000334static void
Ben Skeggsebb945a2012-07-20 08:17:34 +1000335nvc0_fifo_context_dtor(struct nouveau_object *object)
Ben Skeggs4b223ee2010-08-03 10:00:56 +1000336{
Ben Skeggsebb945a2012-07-20 08:17:34 +1000337 struct nvc0_fifo_base *base = (void *)object;
338 nouveau_vm_ref(NULL, &base->vm, base->pgd);
339 nouveau_gpuobj_ref(NULL, &base->pgd);
340 nouveau_fifo_context_destroy(&base->base);
Ben Skeggsb2b09932010-11-24 10:47:15 +1000341}
342
Ben Skeggsebb945a2012-07-20 08:17:34 +1000343static struct nouveau_oclass
344nvc0_fifo_cclass = {
345 .handle = NV_ENGCTX(FIFO, 0xc0),
346 .ofuncs = &(struct nouveau_ofuncs) {
347 .ctor = nvc0_fifo_context_ctor,
348 .dtor = nvc0_fifo_context_dtor,
349 .init = _nouveau_fifo_context_init,
350 .fini = _nouveau_fifo_context_fini,
351 .rd32 = _nouveau_fifo_context_rd32,
352 .wr32 = _nouveau_fifo_context_wr32,
353 },
354};
Ben Skeggsb2b09932010-11-24 10:47:15 +1000355
Ben Skeggsebb945a2012-07-20 08:17:34 +1000356/*******************************************************************************
357 * PFIFO engine
358 ******************************************************************************/
Ben Skeggsc420b2d2012-05-01 20:48:08 +1000359
Ben Skeggs083c2142014-02-22 00:31:29 +1000360static int
361nvc0_fifo_swmthd(struct nvc0_fifo_priv *priv, u32 chid, u32 mthd, u32 data)
362{
363 struct nvc0_fifo_chan *chan = NULL;
364 struct nouveau_handle *bind;
365 unsigned long flags;
366 int ret = -EINVAL;
367
368 spin_lock_irqsave(&priv->base.lock, flags);
369 if (likely(chid >= priv->base.min && chid <= priv->base.max))
370 chan = (void *)priv->base.channel[chid];
371 if (unlikely(!chan))
372 goto out;
373
374 bind = nouveau_namedb_get_class(nv_namedb(chan), 0x906e);
375 if (likely(bind)) {
376 if (!mthd || !nv_call(bind->object, mthd, data))
377 ret = 0;
378 nouveau_namedb_put(bind);
379 }
380
381out:
382 spin_unlock_irqrestore(&priv->base.lock, flags);
383 return ret;
384}
385
Ben Skeggsd439a5a2014-02-22 00:39:36 +1000386static const struct nouveau_enum
Ben Skeggs40476532014-02-22 01:18:46 +1000387nvc0_fifo_sched_reason[] = {
388 { 0x0a, "CTXSW_TIMEOUT" },
389 {}
390};
391
392static void
393nvc0_fifo_intr_sched(struct nvc0_fifo_priv *priv)
394{
395 u32 intr = nv_rd32(priv, 0x00254c);
396 u32 code = intr & 0x000000ff;
397 const struct nouveau_enum *en;
398 char enunk[6] = "";
399
400 en = nouveau_enum_find(nvc0_fifo_sched_reason, code);
401 if (!en)
402 snprintf(enunk, sizeof(enunk), "UNK%02x", code);
403
404 nv_error(priv, "SCHED_ERROR [ %s ]\n", en ? en->name : enunk);
405}
406
407static const struct nouveau_enum
Ben Skeggsd439a5a2014-02-22 00:39:36 +1000408nvc0_fifo_fault_engine[] = {
Marcin Slusarz93260d32012-12-09 23:00:34 +0100409 { 0x00, "PGRAPH", NULL, NVDEV_ENGINE_GR },
Ben Skeggs7a313472011-03-29 00:52:59 +1000410 { 0x03, "PEEPHOLE" },
411 { 0x04, "BAR1" },
412 { 0x05, "BAR3" },
Marcin Slusarz93260d32012-12-09 23:00:34 +0100413 { 0x07, "PFIFO", NULL, NVDEV_ENGINE_FIFO },
414 { 0x10, "PBSP", NULL, NVDEV_ENGINE_BSP },
415 { 0x11, "PPPP", NULL, NVDEV_ENGINE_PPP },
Ben Skeggs7a313472011-03-29 00:52:59 +1000416 { 0x13, "PCOUNTER" },
Marcin Slusarz93260d32012-12-09 23:00:34 +0100417 { 0x14, "PVP", NULL, NVDEV_ENGINE_VP },
418 { 0x15, "PCOPY0", NULL, NVDEV_ENGINE_COPY0 },
419 { 0x16, "PCOPY1", NULL, NVDEV_ENGINE_COPY1 },
Ben Skeggs7a313472011-03-29 00:52:59 +1000420 { 0x17, "PDAEMON" },
Ben Skeggsb2b09932010-11-24 10:47:15 +1000421 {}
422};
423
Ben Skeggsd439a5a2014-02-22 00:39:36 +1000424static const struct nouveau_enum
425nvc0_fifo_fault_reason[] = {
Ben Skeggse2966632011-03-29 08:57:34 +1000426 { 0x00, "PT_NOT_PRESENT" },
427 { 0x01, "PT_TOO_SHORT" },
428 { 0x02, "PAGE_NOT_PRESENT" },
429 { 0x03, "VM_LIMIT_EXCEEDED" },
430 { 0x04, "NO_CHANNEL" },
431 { 0x05, "PAGE_SYSTEM_ONLY" },
432 { 0x06, "PAGE_READ_ONLY" },
433 { 0x0a, "COMPRESSED_SYSRAM" },
434 { 0x0c, "INVALID_STORAGE_TYPE" },
Ben Skeggsb2b09932010-11-24 10:47:15 +1000435 {}
436};
437
Ben Skeggsd439a5a2014-02-22 00:39:36 +1000438static const struct nouveau_enum
439nvc0_fifo_fault_hubclient[] = {
Ben Skeggs7795bee2011-03-29 09:28:24 +1000440 { 0x01, "PCOPY0" },
441 { 0x02, "PCOPY1" },
442 { 0x04, "DISPATCH" },
443 { 0x05, "CTXCTL" },
444 { 0x06, "PFIFO" },
445 { 0x07, "BAR_READ" },
446 { 0x08, "BAR_WRITE" },
447 { 0x0b, "PVP" },
448 { 0x0c, "PPPP" },
449 { 0x0d, "PBSP" },
450 { 0x11, "PCOUNTER" },
451 { 0x12, "PDAEMON" },
452 { 0x14, "CCACHE" },
453 { 0x15, "CCACHE_POST" },
454 {}
455};
456
Ben Skeggsd439a5a2014-02-22 00:39:36 +1000457static const struct nouveau_enum
458nvc0_fifo_fault_gpcclient[] = {
Ben Skeggs7795bee2011-03-29 09:28:24 +1000459 { 0x01, "TEX" },
460 { 0x0c, "ESETUP" },
461 { 0x0e, "CTXCTL" },
462 { 0x0f, "PROP" },
463 {}
464};
465
Ben Skeggsb2b09932010-11-24 10:47:15 +1000466static void
Ben Skeggsd439a5a2014-02-22 00:39:36 +1000467nvc0_fifo_intr_fault(struct nvc0_fifo_priv *priv, int unit)
Ben Skeggsb2b09932010-11-24 10:47:15 +1000468{
Ben Skeggsb3ccd342012-09-06 20:26:38 -0400469 u32 inst = nv_rd32(priv, 0x002800 + (unit * 0x10));
470 u32 valo = nv_rd32(priv, 0x002804 + (unit * 0x10));
471 u32 vahi = nv_rd32(priv, 0x002808 + (unit * 0x10));
472 u32 stat = nv_rd32(priv, 0x00280c + (unit * 0x10));
Ben Skeggsd439a5a2014-02-22 00:39:36 +1000473 u32 gpc = (stat & 0x1f000000) >> 24;
Ben Skeggs7795bee2011-03-29 09:28:24 +1000474 u32 client = (stat & 0x00001f00) >> 8;
Ben Skeggsd439a5a2014-02-22 00:39:36 +1000475 u32 write = (stat & 0x00000080);
476 u32 hub = (stat & 0x00000040);
477 u32 reason = (stat & 0x0000000f);
Marcin Slusarz93260d32012-12-09 23:00:34 +0100478 struct nouveau_object *engctx = NULL;
Ben Skeggsd439a5a2014-02-22 00:39:36 +1000479 struct nouveau_engine *engine;
480 const struct nouveau_enum *er, *eu, *ec;
481 char erunk[6] = "";
482 char euunk[6] = "";
483 char ecunk[6] = "";
484 char gpcid[3] = "";
Ben Skeggsb2b09932010-11-24 10:47:15 +1000485
Ben Skeggsb3ccd342012-09-06 20:26:38 -0400486 switch (unit) {
487 case 3: /* PEEPHOLE */
488 nv_mask(priv, 0x001718, 0x00000000, 0x00000000);
489 break;
490 case 4: /* BAR1 */
491 nv_mask(priv, 0x001704, 0x00000000, 0x00000000);
492 break;
493 case 5: /* BAR3 */
494 nv_mask(priv, 0x001714, 0x00000000, 0x00000000);
495 break;
496 default:
497 break;
498 }
499
Ben Skeggsd439a5a2014-02-22 00:39:36 +1000500 er = nouveau_enum_find(nvc0_fifo_fault_reason, reason);
501 if (!er)
502 snprintf(erunk, sizeof(erunk), "UNK%02X", reason);
503
504 eu = nouveau_enum_find(nvc0_fifo_fault_engine, unit);
505 if (eu) {
506 if (eu->data2) {
507 engine = nouveau_engine(priv, eu->data2);
508 if (engine)
509 engctx = nouveau_engctx_get(engine, inst);
510 }
Ben Skeggs7795bee2011-03-29 09:28:24 +1000511 } else {
Ben Skeggsd439a5a2014-02-22 00:39:36 +1000512 snprintf(euunk, sizeof(euunk), "UNK%02x", unit);
Ben Skeggs7795bee2011-03-29 09:28:24 +1000513 }
Marcin Slusarz93260d32012-12-09 23:00:34 +0100514
Ben Skeggsd439a5a2014-02-22 00:39:36 +1000515 if (hub) {
516 ec = nouveau_enum_find(nvc0_fifo_fault_hubclient, client);
517 } else {
518 ec = nouveau_enum_find(nvc0_fifo_fault_gpcclient, client);
519 snprintf(gpcid, sizeof(gpcid), "%d", gpc);
Marcin Slusarz93260d32012-12-09 23:00:34 +0100520 }
Ben Skeggsd439a5a2014-02-22 00:39:36 +1000521
522 if (!ec)
523 snprintf(ecunk, sizeof(ecunk), "UNK%02x", client);
524
525 nv_error(priv, "%s fault at 0x%010llx [%s] from %s/%s%s%s%s on "
526 "channel 0x%010llx [%s]\n", write ? "write" : "read",
527 (u64)vahi << 32 | valo, er ? er->name : erunk,
528 eu ? eu->name : euunk, hub ? "" : "GPC", gpcid, hub ? "" : "/",
529 ec ? ec->name : ecunk, (u64)inst << 12,
530 nouveau_client_name(engctx));
Marcin Slusarz93260d32012-12-09 23:00:34 +0100531
532 nouveau_engctx_put(engctx);
Ben Skeggsb2b09932010-11-24 10:47:15 +1000533}
534
Ben Skeggs083c2142014-02-22 00:31:29 +1000535static const struct nouveau_bitfield
536nvc0_fifo_pbdma_intr[] = {
537/* { 0x00008000, "" } seen with null ib push */
538 { 0x00200000, "ILLEGAL_MTHD" },
539 { 0x00800000, "EMPTY_SUBC" },
540 {}
541};
Ben Skeggsd5316e22012-03-21 13:53:49 +1000542
Ben Skeggsb2b09932010-11-24 10:47:15 +1000543static void
Ben Skeggs083c2142014-02-22 00:31:29 +1000544nvc0_fifo_intr_pbdma(struct nvc0_fifo_priv *priv, int unit)
Ben Skeggsb2b09932010-11-24 10:47:15 +1000545{
Ben Skeggsebb945a2012-07-20 08:17:34 +1000546 u32 stat = nv_rd32(priv, 0x040108 + (unit * 0x2000));
547 u32 addr = nv_rd32(priv, 0x0400c0 + (unit * 0x2000));
548 u32 data = nv_rd32(priv, 0x0400c4 + (unit * 0x2000));
549 u32 chid = nv_rd32(priv, 0x040120 + (unit * 0x2000)) & 0x7f;
550 u32 subc = (addr & 0x00070000) >> 16;
Ben Skeggsb2b09932010-11-24 10:47:15 +1000551 u32 mthd = (addr & 0x00003ffc);
Ben Skeggsd5316e22012-03-21 13:53:49 +1000552 u32 show = stat;
Ben Skeggsb2b09932010-11-24 10:47:15 +1000553
Ben Skeggsebb945a2012-07-20 08:17:34 +1000554 if (stat & 0x00800000) {
555 if (!nvc0_fifo_swmthd(priv, chid, mthd, data))
556 show &= ~0x00800000;
Ben Skeggsd5316e22012-03-21 13:53:49 +1000557 }
Ben Skeggsb2b09932010-11-24 10:47:15 +1000558
Ben Skeggsebb945a2012-07-20 08:17:34 +1000559 if (show) {
Ben Skeggs03574662014-01-28 11:47:46 +1000560 nv_error(priv, "PBDMA%d:", unit);
561 nouveau_bitfield_print(nvc0_fifo_pbdma_intr, show);
Marcin Slusarzf533da12012-12-09 15:45:20 +0100562 pr_cont("\n");
Marcin Slusarz93260d32012-12-09 23:00:34 +0100563 nv_error(priv,
Ben Skeggs03574662014-01-28 11:47:46 +1000564 "PBDMA%d: ch %d [%s] subc %d mthd 0x%04x data 0x%08x\n",
Marcin Slusarz93260d32012-12-09 23:00:34 +0100565 unit, chid,
566 nouveau_client_name_for_fifo_chid(&priv->base, chid),
567 subc, mthd, data);
Ben Skeggsebb945a2012-07-20 08:17:34 +1000568 }
569
570 nv_wr32(priv, 0x0400c0 + (unit * 0x2000), 0x80600008);
571 nv_wr32(priv, 0x040108 + (unit * 0x2000), stat);
Ben Skeggsb2b09932010-11-24 10:47:15 +1000572}
573
574static void
Ben Skeggsa07d0e72014-02-22 00:28:47 +1000575nvc0_fifo_intr_runlist(struct nvc0_fifo_priv *priv)
576{
577 u32 intr = nv_rd32(priv, 0x002a00);
578
579 if (intr & 0x10000000) {
580 wake_up(&priv->runlist.wait);
581 nv_wr32(priv, 0x002a00, 0x10000000);
582 intr &= ~0x10000000;
583 }
584
585 if (intr) {
586 nv_error(priv, "RUNLIST 0x%08x\n", intr);
587 nv_wr32(priv, 0x002a00, intr);
588 }
589}
590
591static void
Ben Skeggse99bf012014-02-22 00:18:17 +1000592nvc0_fifo_intr_engine_unit(struct nvc0_fifo_priv *priv, int engn)
593{
594 u32 intr = nv_rd32(priv, 0x0025a8 + (engn * 0x04));
595 u32 inte = nv_rd32(priv, 0x002628);
596 u32 unkn;
597
598 for (unkn = 0; unkn < 8; unkn++) {
599 u32 ints = (intr >> (unkn * 0x04)) & inte;
600 if (ints & 0x1) {
601 nouveau_event_trigger(priv->base.uevent, 0);
602 ints &= ~1;
603 }
604 if (ints) {
605 nv_error(priv, "ENGINE %d %d %01x", engn, unkn, ints);
606 nv_mask(priv, 0x002628, ints, 0);
607 }
608 }
609
610 nv_wr32(priv, 0x0025a8 + (engn * 0x04), intr);
611}
612
613static void
614nvc0_fifo_intr_engine(struct nvc0_fifo_priv *priv)
615{
616 u32 mask = nv_rd32(priv, 0x0025a4);
617 while (mask) {
618 u32 unit = __ffs(mask);
619 nvc0_fifo_intr_engine_unit(priv, unit);
620 mask &= ~(1 << unit);
621 }
622}
623
624static void
Ben Skeggsebb945a2012-07-20 08:17:34 +1000625nvc0_fifo_intr(struct nouveau_subdev *subdev)
Ben Skeggsb2b09932010-11-24 10:47:15 +1000626{
Ben Skeggsebb945a2012-07-20 08:17:34 +1000627 struct nvc0_fifo_priv *priv = (void *)subdev;
628 u32 mask = nv_rd32(priv, 0x002140);
629 u32 stat = nv_rd32(priv, 0x002100) & mask;
Ben Skeggsb2b09932010-11-24 10:47:15 +1000630
Ben Skeggs32256c82013-01-31 19:49:33 -0500631 if (stat & 0x00000001) {
632 u32 intr = nv_rd32(priv, 0x00252c);
633 nv_warn(priv, "INTR 0x00000001: 0x%08x\n", intr);
634 nv_wr32(priv, 0x002100, 0x00000001);
635 stat &= ~0x00000001;
636 }
637
Ben Skeggscc8cd642011-01-28 13:42:16 +1000638 if (stat & 0x00000100) {
Ben Skeggs40476532014-02-22 01:18:46 +1000639 nvc0_fifo_intr_sched(priv);
Ben Skeggsebb945a2012-07-20 08:17:34 +1000640 nv_wr32(priv, 0x002100, 0x00000100);
Ben Skeggscc8cd642011-01-28 13:42:16 +1000641 stat &= ~0x00000100;
642 }
643
Ben Skeggs32256c82013-01-31 19:49:33 -0500644 if (stat & 0x00010000) {
645 u32 intr = nv_rd32(priv, 0x00256c);
646 nv_warn(priv, "INTR 0x00010000: 0x%08x\n", intr);
647 nv_wr32(priv, 0x002100, 0x00010000);
648 stat &= ~0x00010000;
649 }
650
651 if (stat & 0x01000000) {
652 u32 intr = nv_rd32(priv, 0x00258c);
653 nv_warn(priv, "INTR 0x01000000: 0x%08x\n", intr);
654 nv_wr32(priv, 0x002100, 0x01000000);
655 stat &= ~0x01000000;
656 }
657
Ben Skeggsb2b09932010-11-24 10:47:15 +1000658 if (stat & 0x10000000) {
Ben Skeggsd439a5a2014-02-22 00:39:36 +1000659 u32 mask = nv_rd32(priv, 0x00259c);
660 while (mask) {
661 u32 unit = __ffs(mask);
662 nvc0_fifo_intr_fault(priv, unit);
663 nv_wr32(priv, 0x00259c, (1 << unit));
664 mask &= ~(1 << unit);
Ben Skeggsb2b09932010-11-24 10:47:15 +1000665 }
Ben Skeggsb2b09932010-11-24 10:47:15 +1000666 stat &= ~0x10000000;
667 }
668
669 if (stat & 0x20000000) {
Ben Skeggs083c2142014-02-22 00:31:29 +1000670 u32 mask = nv_rd32(priv, 0x0025a0);
671 while (mask) {
672 u32 unit = __ffs(mask);
673 nvc0_fifo_intr_pbdma(priv, unit);
674 nv_wr32(priv, 0x0025a0, (1 << unit));
675 mask &= ~(1 << unit);
Ben Skeggsb2b09932010-11-24 10:47:15 +1000676 }
Ben Skeggsb2b09932010-11-24 10:47:15 +1000677 stat &= ~0x20000000;
678 }
679
Ben Skeggscc8cd642011-01-28 13:42:16 +1000680 if (stat & 0x40000000) {
Ben Skeggsa07d0e72014-02-22 00:28:47 +1000681 nvc0_fifo_intr_runlist(priv);
Ben Skeggscc8cd642011-01-28 13:42:16 +1000682 stat &= ~0x40000000;
683 }
684
Ben Skeggs32256c82013-01-31 19:49:33 -0500685 if (stat & 0x80000000) {
Ben Skeggse99bf012014-02-22 00:18:17 +1000686 nvc0_fifo_intr_engine(priv);
Ben Skeggs32256c82013-01-31 19:49:33 -0500687 stat &= ~0x80000000;
688 }
689
Ben Skeggsb2b09932010-11-24 10:47:15 +1000690 if (stat) {
Ben Skeggs22a7a272014-02-22 00:19:19 +1000691 nv_error(priv, "INTR 0x%08x\n", stat);
692 nv_mask(priv, 0x002140, stat, 0x00000000);
Ben Skeggsebb945a2012-07-20 08:17:34 +1000693 nv_wr32(priv, 0x002100, stat);
Ben Skeggsb2b09932010-11-24 10:47:15 +1000694 }
Ben Skeggsb2b09932010-11-24 10:47:15 +1000695}
Ben Skeggsc420b2d2012-05-01 20:48:08 +1000696
Ben Skeggs9bd2ddb2013-01-31 13:51:20 +1000697static void
698nvc0_fifo_uevent_enable(struct nouveau_event *event, int index)
699{
700 struct nvc0_fifo_priv *priv = event->priv;
701 nv_mask(priv, 0x002140, 0x80000000, 0x80000000);
702}
703
704static void
705nvc0_fifo_uevent_disable(struct nouveau_event *event, int index)
706{
707 struct nvc0_fifo_priv *priv = event->priv;
708 nv_mask(priv, 0x002140, 0x80000000, 0x00000000);
709}
710
Ben Skeggsebb945a2012-07-20 08:17:34 +1000711static int
712nvc0_fifo_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
713 struct nouveau_oclass *oclass, void *data, u32 size,
714 struct nouveau_object **pobject)
Ben Skeggsc420b2d2012-05-01 20:48:08 +1000715{
Ben Skeggsc420b2d2012-05-01 20:48:08 +1000716 struct nvc0_fifo_priv *priv;
717 int ret;
718
Ben Skeggsebb945a2012-07-20 08:17:34 +1000719 ret = nouveau_fifo_create(parent, engine, oclass, 0, 127, &priv);
720 *pobject = nv_object(priv);
Ben Skeggsc420b2d2012-05-01 20:48:08 +1000721 if (ret)
Ben Skeggsebb945a2012-07-20 08:17:34 +1000722 return ret;
Ben Skeggsc420b2d2012-05-01 20:48:08 +1000723
Ben Skeggsf50c8052013-04-24 18:02:35 +1000724 ret = nouveau_gpuobj_new(nv_object(priv), NULL, 0x1000, 0x1000, 0,
Ben Skeggsa07d0e72014-02-22 00:28:47 +1000725 &priv->runlist.mem[0]);
Ben Skeggsc420b2d2012-05-01 20:48:08 +1000726 if (ret)
Ben Skeggsebb945a2012-07-20 08:17:34 +1000727 return ret;
Ben Skeggsc420b2d2012-05-01 20:48:08 +1000728
Ben Skeggsf50c8052013-04-24 18:02:35 +1000729 ret = nouveau_gpuobj_new(nv_object(priv), NULL, 0x1000, 0x1000, 0,
Ben Skeggsa07d0e72014-02-22 00:28:47 +1000730 &priv->runlist.mem[1]);
Ben Skeggsc420b2d2012-05-01 20:48:08 +1000731 if (ret)
Ben Skeggsebb945a2012-07-20 08:17:34 +1000732 return ret;
Ben Skeggsc420b2d2012-05-01 20:48:08 +1000733
Ben Skeggsa07d0e72014-02-22 00:28:47 +1000734 init_waitqueue_head(&priv->runlist.wait);
735
Ben Skeggsf50c8052013-04-24 18:02:35 +1000736 ret = nouveau_gpuobj_new(nv_object(priv), NULL, 128 * 0x1000, 0x1000, 0,
Ben Skeggsebb945a2012-07-20 08:17:34 +1000737 &priv->user.mem);
Ben Skeggs9da226f2012-07-13 16:54:45 +1000738 if (ret)
Ben Skeggsebb945a2012-07-20 08:17:34 +1000739 return ret;
Ben Skeggs9da226f2012-07-13 16:54:45 +1000740
Ben Skeggsebb945a2012-07-20 08:17:34 +1000741 ret = nouveau_gpuobj_map(priv->user.mem, NV_MEM_ACCESS_RW,
742 &priv->user.bar);
Ben Skeggsc420b2d2012-05-01 20:48:08 +1000743 if (ret)
Ben Skeggsebb945a2012-07-20 08:17:34 +1000744 return ret;
745
Ben Skeggs9bd2ddb2013-01-31 13:51:20 +1000746 priv->base.uevent->enable = nvc0_fifo_uevent_enable;
747 priv->base.uevent->disable = nvc0_fifo_uevent_disable;
748 priv->base.uevent->priv = priv;
749
Ben Skeggsebb945a2012-07-20 08:17:34 +1000750 nv_subdev(priv)->unit = 0x00000100;
751 nv_subdev(priv)->intr = nvc0_fifo_intr;
752 nv_engine(priv)->cclass = &nvc0_fifo_cclass;
753 nv_engine(priv)->sclass = nvc0_fifo_sclass;
754 return 0;
Ben Skeggsc420b2d2012-05-01 20:48:08 +1000755}
Ben Skeggsebb945a2012-07-20 08:17:34 +1000756
757static void
758nvc0_fifo_dtor(struct nouveau_object *object)
759{
760 struct nvc0_fifo_priv *priv = (void *)object;
761
762 nouveau_gpuobj_unmap(&priv->user.bar);
763 nouveau_gpuobj_ref(NULL, &priv->user.mem);
Ben Skeggsa07d0e72014-02-22 00:28:47 +1000764 nouveau_gpuobj_ref(NULL, &priv->runlist.mem[0]);
765 nouveau_gpuobj_ref(NULL, &priv->runlist.mem[1]);
Ben Skeggsebb945a2012-07-20 08:17:34 +1000766
767 nouveau_fifo_destroy(&priv->base);
768}
769
770static int
771nvc0_fifo_init(struct nouveau_object *object)
772{
773 struct nvc0_fifo_priv *priv = (void *)object;
774 int ret, i;
775
776 ret = nouveau_fifo_init(&priv->base);
777 if (ret)
778 return ret;
779
780 nv_wr32(priv, 0x000204, 0xffffffff);
781 nv_wr32(priv, 0x002204, 0xffffffff);
782
783 priv->spoon_nr = hweight32(nv_rd32(priv, 0x002204));
Ben Skeggs03574662014-01-28 11:47:46 +1000784 nv_debug(priv, "%d PBDMA unit(s)\n", priv->spoon_nr);
Ben Skeggsebb945a2012-07-20 08:17:34 +1000785
Ben Skeggs03574662014-01-28 11:47:46 +1000786 /* assign engines to PBDMAs */
Ben Skeggsebb945a2012-07-20 08:17:34 +1000787 if (priv->spoon_nr >= 3) {
788 nv_wr32(priv, 0x002208, ~(1 << 0)); /* PGRAPH */
789 nv_wr32(priv, 0x00220c, ~(1 << 1)); /* PVP */
790 nv_wr32(priv, 0x002210, ~(1 << 1)); /* PPP */
791 nv_wr32(priv, 0x002214, ~(1 << 1)); /* PBSP */
792 nv_wr32(priv, 0x002218, ~(1 << 2)); /* PCE0 */
793 nv_wr32(priv, 0x00221c, ~(1 << 1)); /* PCE1 */
794 }
795
Ben Skeggs03574662014-01-28 11:47:46 +1000796 /* PBDMA[n] */
Ben Skeggsebb945a2012-07-20 08:17:34 +1000797 for (i = 0; i < priv->spoon_nr; i++) {
798 nv_mask(priv, 0x04013c + (i * 0x2000), 0x10000100, 0x00000000);
799 nv_wr32(priv, 0x040108 + (i * 0x2000), 0xffffffff); /* INTR */
800 nv_wr32(priv, 0x04010c + (i * 0x2000), 0xfffffeff); /* INTREN */
801 }
802
803 nv_mask(priv, 0x002200, 0x00000001, 0x00000001);
804 nv_wr32(priv, 0x002254, 0x10000000 | priv->user.bar.offset >> 12);
805
Ben Skeggsebb945a2012-07-20 08:17:34 +1000806 nv_wr32(priv, 0x002100, 0xffffffff);
Ben Skeggsa07d0e72014-02-22 00:28:47 +1000807 nv_wr32(priv, 0x002140, 0x7fffffff);
Ben Skeggse99bf012014-02-22 00:18:17 +1000808 nv_wr32(priv, 0x002628, 0x00000001); /* ENGINE_INTR_EN */
Ben Skeggsebb945a2012-07-20 08:17:34 +1000809 return 0;
810}
811
Ben Skeggs16c4f222013-11-05 14:26:58 +1000812struct nouveau_oclass *
813nvc0_fifo_oclass = &(struct nouveau_oclass) {
Ben Skeggsebb945a2012-07-20 08:17:34 +1000814 .handle = NV_ENGINE(FIFO, 0xc0),
815 .ofuncs = &(struct nouveau_ofuncs) {
816 .ctor = nvc0_fifo_ctor,
817 .dtor = nvc0_fifo_dtor,
818 .init = nvc0_fifo_init,
819 .fini = _nouveau_fifo_fini,
820 },
821};