1/* $NetBSD: nouveau_subdev_vm_nv44.c,v 1.3 2015/10/14 00:12:55 mrg 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_subdev_vm_nv44.c,v 1.3 2015/10/14 00:12:55 mrg Exp $");
29
30#include <core/gpuobj.h>
31#include <core/option.h>
32
33#include <subdev/timer.h>
34#include <subdev/vm.h>
35
36#include "nv04.h"
37
38#define NV44_GART_SIZE (512 * 1024 * 1024)
39#define NV44_GART_PAGE ( 4 * 1024)
40
41/*******************************************************************************
42 * VM map/unmap callbacks
43 ******************************************************************************/
44
45static void
46nv44_vm_fill(struct nouveau_gpuobj *pgt, dma_addr_t null,
47 dma_addr_t *list, u32 pte, u32 cnt)
48{
49 u32 base = (pte << 2) & ~0x0000000f;
50 u32 tmp[4];
51
52 tmp[0] = nv_ro32(pgt, base + 0x0);
53 tmp[1] = nv_ro32(pgt, base + 0x4);
54 tmp[2] = nv_ro32(pgt, base + 0x8);
55 tmp[3] = nv_ro32(pgt, base + 0xc);
56
57 while (cnt--) {
58 u32 addr = list ? (*list++ >> 12) : (null >> 12);
59 switch (pte++ & 0x3) {
60 case 0:
61 tmp[0] &= ~0x07ffffff;
62 tmp[0] |= addr;
63 break;
64 case 1:
65 tmp[0] &= ~0xf8000000;
66 tmp[0] |= addr << 27;
67 tmp[1] &= ~0x003fffff;
68 tmp[1] |= addr >> 5;
69 break;
70 case 2:
71 tmp[1] &= ~0xffc00000;
72 tmp[1] |= addr << 22;
73 tmp[2] &= ~0x0001ffff;
74 tmp[2] |= addr >> 10;
75 break;
76 case 3:
77 tmp[2] &= ~0xfffe0000;
78 tmp[2] |= addr << 17;
79 tmp[3] &= ~0x00000fff;
80 tmp[3] |= addr >> 15;
81 break;
82 }
83 }
84
85 nv_wo32(pgt, base + 0x0, tmp[0]);
86 nv_wo32(pgt, base + 0x4, tmp[1]);
87 nv_wo32(pgt, base + 0x8, tmp[2]);
88 nv_wo32(pgt, base + 0xc, tmp[3] | 0x40000000);
89}
90
91static void
92nv44_vm_map_sg(struct nouveau_vma *vma, struct nouveau_gpuobj *pgt,
93 struct nouveau_mem *mem, u32 pte, u32 cnt, dma_addr_t *list)
94{
95 struct nv04_vmmgr_priv *priv = (void *)vma->vm->vmm;
96 u32 tmp[4];
97 int i;
98
99 if (pte & 3) {
100 u32 max = 4 - (pte & 3);
101 u32 part = (cnt > max) ? max : cnt;
102 nv44_vm_fill(pgt, priv->null, list, pte, part);
103 pte += part;
104 list += part;
105 cnt -= part;
106 }
107
108 while (cnt >= 4) {
109 for (i = 0; i < 4; i++)
110 tmp[i] = *list++ >> 12;
111 nv_wo32(pgt, pte++ * 4, tmp[0] >> 0 | tmp[1] << 27);
112 nv_wo32(pgt, pte++ * 4, tmp[1] >> 5 | tmp[2] << 22);
113 nv_wo32(pgt, pte++ * 4, tmp[2] >> 10 | tmp[3] << 17);
114 nv_wo32(pgt, pte++ * 4, tmp[3] >> 15 | 0x40000000);
115 cnt -= 4;
116 }
117
118 if (cnt)
119 nv44_vm_fill(pgt, priv->null, list, pte, cnt);
120}
121
122static void
123nv44_vm_unmap(struct nouveau_gpuobj *pgt, u32 pte, u32 cnt)
124{
125 struct nv04_vmmgr_priv *priv = (void *)nouveau_vmmgr(pgt);
126
127 if (pte & 3) {
128 u32 max = 4 - (pte & 3);
129 u32 part = (cnt > max) ? max : cnt;
130 nv44_vm_fill(pgt, priv->null, NULL, pte, part);
131 pte += part;
132 cnt -= part;
133 }
134
135 while (cnt >= 4) {
136 nv_wo32(pgt, pte++ * 4, 0x00000000);
137 nv_wo32(pgt, pte++ * 4, 0x00000000);
138 nv_wo32(pgt, pte++ * 4, 0x00000000);
139 nv_wo32(pgt, pte++ * 4, 0x00000000);
140 cnt -= 4;
141 }
142
143 if (cnt)
144 nv44_vm_fill(pgt, priv->null, NULL, pte, cnt);
145}
146
147static void
148nv44_vm_flush(struct nouveau_vm *vm)
149{
150 struct nv04_vmmgr_priv *priv = (void *)vm->vmm;
151 nv_wr32(priv, 0x100814, priv->base.limit - NV44_GART_PAGE);
152 nv_wr32(priv, 0x100808, 0x00000020);
153 if (!nv_wait(priv, 0x100808, 0x00000001, 0x00000001))
154 nv_error(priv, "timeout: 0x%08x\n", nv_rd32(priv, 0x100808));
155 nv_wr32(priv, 0x100808, 0x00000000);
156}
157
158/*******************************************************************************
159 * VMMGR subdev
160 ******************************************************************************/
161
162static int
163nv44_vmmgr_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
164 struct nouveau_oclass *oclass, void *data, u32 size,
165 struct nouveau_object **pobject)
166{
167 struct nouveau_device *device = nv_device(parent);
168 struct nv04_vmmgr_priv *priv;
169 int ret;
170
171 if (pci_find_capability(device->pdev, PCI_CAP_ID_AGP) ||
172 !nouveau_boolopt(device->cfgopt, "NvPCIE", true)) {
173 return nouveau_object_ctor(parent, engine, &nv04_vmmgr_oclass,
174 data, size, pobject);
175 }
176
177 ret = nouveau_vmmgr_create(parent, engine, oclass, "PCIEGART",
178 "pciegart", &priv);
179 *pobject = nv_object(priv);
180 if (ret)
181 return ret;
182
183 priv->base.create = nv04_vm_create;
184 priv->base.limit = NV44_GART_SIZE;
185 priv->base.dma_bits = 39;
186 priv->base.pgt_bits = 32 - 12;
187 priv->base.spg_shift = 12;
188 priv->base.lpg_shift = 12;
189 priv->base.map_sg = nv44_vm_map_sg;
190 priv->base.unmap = nv44_vm_unmap;
191 priv->base.flush = nv44_vm_flush;
192
193#ifdef __NetBSD__
194 {
195 const bus_dma_tag_t dmat = pci_dma64_available(&device->pdev->pd_pa) ?
196 device->pdev->pd_pa.pa_dmat64 : device->pdev->pd_pa.pa_dmat;
197 int nsegs;
198
199 /* XXX errno NetBSD->Linux */
200 ret = -bus_dmamem_alloc(dmat, PAGE_SIZE, PAGE_SIZE, 0,
201 &priv->nullseg, 1, &nsegs, BUS_DMA_WAITOK);
202 if (ret) {
203 /* XXX Need to destroy stuff... */
204fail0: nv_error(priv, "unable to allocate dummy pages\n");
205 return ret;
206 }
207 KASSERT(nsegs == 1);
208
209 /* XXX errno NetBSD->Linux */
210 ret = -bus_dmamap_create(dmat, PAGE_SIZE, 1, PAGE_SIZE, 0,
211 BUS_DMA_WAITOK, &priv->nullmap);
212 if (ret) {
213fail1: bus_dmamem_free(dmat, &priv->nullseg, 1);
214 goto fail0;
215 }
216
217 /* XXX errno NetBSD->Linux */
218 ret = -bus_dmamem_map(dmat, &priv->nullseg, 1, PAGE_SIZE,
219 &priv->nullp, BUS_DMA_WAITOK);
220 if (ret) {
221fail2: bus_dmamap_destroy(dmat, priv->nullmap);
222 goto fail1;
223 }
224
225 /* XXX errno NetBSD->Linux */
226 ret = -bus_dmamap_load(dmat, priv->nullmap, priv->nullp, PAGE_SIZE,
227 NULL, BUS_DMA_WAITOK);
228 if (ret) {
229fail3: __unused bus_dmamem_unmap(dmat, priv->nullp, PAGE_SIZE);
230 goto fail2;
231 }
232 priv->null = priv->nullmap->dm_segs[0].ds_addr;
233 }
234#else
235 priv->nullp = pci_alloc_consistent(device->pdev, 16 * 1024, &priv->null);
236 if (!priv->nullp) {
237 nv_error(priv, "unable to allocate dummy pages\n");
238 return -ENOMEM;
239 }
240#endif
241
242 ret = nouveau_vm_create(&priv->base, 0, NV44_GART_SIZE, 0, 4096,
243 &priv->vm);
244 if (ret)
245 return ret;
246
247 ret = nouveau_gpuobj_new(nv_object(priv), NULL,
248 (NV44_GART_SIZE / NV44_GART_PAGE) * 4,
249 512 * 1024, NVOBJ_FLAG_ZERO_ALLOC,
250 &priv->vm->pgt[0].obj[0]);
251 priv->vm->pgt[0].refcount[0] = 1;
252 if (ret)
253 return ret;
254
255 return 0;
256}
257
258static int
259nv44_vmmgr_init(struct nouveau_object *object)
260{
261 struct nv04_vmmgr_priv *priv = (void *)object;
262 struct nouveau_gpuobj *gart = priv->vm->pgt[0].obj[0];
263 u32 addr;
264 int ret;
265
266 ret = nouveau_vmmgr_init(&priv->base);
267 if (ret)
268 return ret;
269
270 /* calculate vram address of this PRAMIN block, object must be
271 * allocated on 512KiB alignment, and not exceed a total size
272 * of 512KiB for this to work correctly
273 */
274 addr = nv_rd32(priv, 0x10020c);
275 addr -= ((gart->addr >> 19) + 1) << 19;
276
277 nv_wr32(priv, 0x100850, 0x80000000);
278 nv_wr32(priv, 0x100818, priv->null);
279 nv_wr32(priv, 0x100804, NV44_GART_SIZE);
280 nv_wr32(priv, 0x100850, 0x00008000);
281 nv_mask(priv, 0x10008c, 0x00000200, 0x00000200);
282 nv_wr32(priv, 0x100820, 0x00000000);
283 nv_wr32(priv, 0x10082c, 0x00000001);
284 nv_wr32(priv, 0x100800, addr | 0x00000010);
285 return 0;
286}
287
288struct nouveau_oclass
289nv44_vmmgr_oclass = {
290 .handle = NV_SUBDEV(VM, 0x44),
291 .ofuncs = &(struct nouveau_ofuncs) {
292 .ctor = nv44_vmmgr_ctor,
293 .dtor = nv04_vmmgr_dtor,
294 .init = nv44_vmmgr_init,
295 .fini = _nouveau_vmmgr_fini,
296 },
297};
298