Home | History | Annotate | Line # | Download | only in mmu
      1 /*	$NetBSD: nouveau_nvkm_subdev_mmu_vmmnv44.c,v 1.4 2021/12/19 11:26:26 riastradh Exp $	*/
      2 
      3 /*
      4  * Copyright 2017 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 #include <sys/cdefs.h>
     25 __KERNEL_RCSID(0, "$NetBSD: nouveau_nvkm_subdev_mmu_vmmnv44.c,v 1.4 2021/12/19 11:26:26 riastradh Exp $");
     26 
     27 #include "vmm.h"
     28 
     29 #include <subdev/timer.h>
     30 
     31 static void
     32 nv44_vmm_pgt_fill(struct nvkm_vmm *vmm, struct nvkm_mmu_pt *pt,
     33 		  dma_addr_t *list, u32 ptei, u32 ptes)
     34 {
     35 	u32 pteo = (ptei << 2) & ~0x0000000f;
     36 	u32 tmp[4];
     37 
     38 	tmp[0] = nvkm_ro32(pt->memory, pteo + 0x0);
     39 	tmp[1] = nvkm_ro32(pt->memory, pteo + 0x4);
     40 	tmp[2] = nvkm_ro32(pt->memory, pteo + 0x8);
     41 	tmp[3] = nvkm_ro32(pt->memory, pteo + 0xc);
     42 
     43 	while (ptes--) {
     44 		u32 addr = (list ? *list++ : vmm->null) >> 12;
     45 		switch (ptei++ & 0x3) {
     46 		case 0:
     47 			tmp[0] &= ~0x07ffffff;
     48 			tmp[0] |= addr;
     49 			break;
     50 		case 1:
     51 			tmp[0] &= ~0xf8000000;
     52 			tmp[0] |= addr << 27;
     53 			tmp[1] &= ~0x003fffff;
     54 			tmp[1] |= addr >> 5;
     55 			break;
     56 		case 2:
     57 			tmp[1] &= ~0xffc00000;
     58 			tmp[1] |= addr << 22;
     59 			tmp[2] &= ~0x0001ffff;
     60 			tmp[2] |= addr >> 10;
     61 			break;
     62 		case 3:
     63 			tmp[2] &= ~0xfffe0000;
     64 			tmp[2] |= addr << 17;
     65 			tmp[3] &= ~0x00000fff;
     66 			tmp[3] |= addr >> 15;
     67 			break;
     68 		}
     69 	}
     70 
     71 	VMM_WO032(pt, vmm, pteo + 0x0, tmp[0]);
     72 	VMM_WO032(pt, vmm, pteo + 0x4, tmp[1]);
     73 	VMM_WO032(pt, vmm, pteo + 0x8, tmp[2]);
     74 	VMM_WO032(pt, vmm, pteo + 0xc, tmp[3] | 0x40000000);
     75 }
     76 
     77 static void __unused
     78 nv44_vmm_pgt_pte(struct nvkm_vmm *vmm, struct nvkm_mmu_pt *pt,
     79 		 u32 ptei, u32 ptes, struct nvkm_vmm_map *map, u64 addr)
     80 {
     81 	dma_addr_t tmp[4], i;
     82 
     83 	if (ptei & 3) {
     84 		const u32 pten = min(ptes, 4 - (ptei & 3));
     85 		for (i = 0; i < pten; i++, addr += 0x1000)
     86 			tmp[i] = addr;
     87 		nv44_vmm_pgt_fill(vmm, pt, tmp, ptei, pten);
     88 		ptei += pten;
     89 		ptes -= pten;
     90 	}
     91 
     92 	while (ptes >= 4) {
     93 		for (i = 0; i < 4; i++, addr += 0x1000)
     94 			tmp[i] = addr >> 12;
     95 		VMM_WO032(pt, vmm, ptei++ * 4, tmp[0] >>  0 | tmp[1] << 27);
     96 		VMM_WO032(pt, vmm, ptei++ * 4, tmp[1] >>  5 | tmp[2] << 22);
     97 		VMM_WO032(pt, vmm, ptei++ * 4, tmp[2] >> 10 | tmp[3] << 17);
     98 		VMM_WO032(pt, vmm, ptei++ * 4, tmp[3] >> 15 | 0x40000000);
     99 		ptes -= 4;
    100 	}
    101 
    102 	if (ptes) {
    103 		for (i = 0; i < ptes; i++, addr += 0x1000)
    104 			tmp[i] = addr;
    105 		nv44_vmm_pgt_fill(vmm, pt, tmp, ptei, ptes);
    106 	}
    107 }
    108 
    109 #ifndef __NetBSD__
    110 static void
    111 nv44_vmm_pgt_sgl(struct nvkm_vmm *vmm, struct nvkm_mmu_pt *pt,
    112 		 u32 ptei, u32 ptes, struct nvkm_vmm_map *map)
    113 {
    114 	VMM_MAP_ITER_SGL(vmm, pt, ptei, ptes, map, nv44_vmm_pgt_pte);
    115 }
    116 #endif
    117 
    118 static void
    119 nv44_vmm_pgt_dma(struct nvkm_vmm *vmm, struct nvkm_mmu_pt *pt,
    120 		 u32 ptei, u32 ptes, struct nvkm_vmm_map *map)
    121 {
    122 #if PAGE_SHIFT == 12
    123 	nvkm_kmap(pt->memory);
    124 	if (ptei & 3) {
    125 		const u32 pten = min(ptes, 4 - (ptei & 3));
    126 		nv44_vmm_pgt_fill(vmm, pt, map->dma, ptei, pten);
    127 		ptei += pten;
    128 		ptes -= pten;
    129 		map->dma += pten;
    130 	}
    131 
    132 	while (ptes >= 4) {
    133 		u32 tmp[4], i;
    134 		for (i = 0; i < 4; i++)
    135 			tmp[i] = *map->dma++ >> 12;
    136 		VMM_WO032(pt, vmm, ptei++ * 4, tmp[0] >>  0 | tmp[1] << 27);
    137 		VMM_WO032(pt, vmm, ptei++ * 4, tmp[1] >>  5 | tmp[2] << 22);
    138 		VMM_WO032(pt, vmm, ptei++ * 4, tmp[2] >> 10 | tmp[3] << 17);
    139 		VMM_WO032(pt, vmm, ptei++ * 4, tmp[3] >> 15 | 0x40000000);
    140 		ptes -= 4;
    141 	}
    142 
    143 	if (ptes) {
    144 		nv44_vmm_pgt_fill(vmm, pt, map->dma, ptei, ptes);
    145 		map->dma += ptes;
    146 	}
    147 	nvkm_done(pt->memory);
    148 #else
    149 	VMM_MAP_ITER_DMA(vmm, pt, ptei, ptes, map, nv44_vmm_pgt_pte);
    150 #endif
    151 }
    152 
    153 static void
    154 nv44_vmm_pgt_unmap(struct nvkm_vmm *vmm,
    155 		   struct nvkm_mmu_pt *pt, u32 ptei, u32 ptes)
    156 {
    157 	nvkm_kmap(pt->memory);
    158 	if (ptei & 3) {
    159 		const u32 pten = min(ptes, 4 - (ptei & 3));
    160 		nv44_vmm_pgt_fill(vmm, pt, NULL, ptei, pten);
    161 		ptei += pten;
    162 		ptes -= pten;
    163 	}
    164 
    165 	while (ptes > 4) {
    166 		VMM_WO032(pt, vmm, ptei++ * 4, 0x00000000);
    167 		VMM_WO032(pt, vmm, ptei++ * 4, 0x00000000);
    168 		VMM_WO032(pt, vmm, ptei++ * 4, 0x00000000);
    169 		VMM_WO032(pt, vmm, ptei++ * 4, 0x00000000);
    170 		ptes -= 4;
    171 	}
    172 
    173 	if (ptes)
    174 		nv44_vmm_pgt_fill(vmm, pt, NULL, ptei, ptes);
    175 	nvkm_done(pt->memory);
    176 }
    177 
    178 static const struct nvkm_vmm_desc_func
    179 nv44_vmm_desc_pgt = {
    180 	.unmap = nv44_vmm_pgt_unmap,
    181 	.dma = nv44_vmm_pgt_dma,
    182 #ifndef __NetBSD__
    183 	.sgl = nv44_vmm_pgt_sgl,
    184 #endif
    185 };
    186 
    187 static const struct nvkm_vmm_desc
    188 nv44_vmm_desc_12[] = {
    189 	{ PGT, 17, 4, 0x80000, &nv44_vmm_desc_pgt },
    190 	{}
    191 };
    192 
    193 static void
    194 nv44_vmm_flush(struct nvkm_vmm *vmm, int level)
    195 {
    196 	struct nvkm_device *device = vmm->mmu->subdev.device;
    197 	nvkm_wr32(device, 0x100814, vmm->limit - 4096);
    198 	nvkm_wr32(device, 0x100808, 0x000000020);
    199 	nvkm_msec(device, 2000,
    200 		if (nvkm_rd32(device, 0x100808) & 0x00000001)
    201 			break;
    202 	);
    203 	nvkm_wr32(device, 0x100808, 0x00000000);
    204 }
    205 
    206 static const struct nvkm_vmm_func
    207 nv44_vmm = {
    208 	.valid = nv04_vmm_valid,
    209 	.flush = nv44_vmm_flush,
    210 	.page = {
    211 		{ 12, &nv44_vmm_desc_12[0], NVKM_VMM_PAGE_HOST },
    212 		{}
    213 	}
    214 };
    215 
    216 int
    217 nv44_vmm_new(struct nvkm_mmu *mmu, bool managed, u64 addr, u64 size,
    218 	     void *argv, u32 argc, struct lock_class_key *key, const char *name,
    219 	     struct nvkm_vmm **pvmm)
    220 {
    221 	struct nvkm_subdev *subdev = &mmu->subdev;
    222 	struct nvkm_vmm *vmm;
    223 	int ret;
    224 
    225 	ret = nv04_vmm_new_(&nv44_vmm, mmu, 0, managed, addr, size,
    226 			    argv, argc, key, name, &vmm);
    227 	*pvmm = vmm;
    228 	if (ret)
    229 		return ret;
    230 
    231 #ifdef __NetBSD__
    232     do {
    233 	const bus_dma_tag_t dmat =
    234 	    subdev->device->func->dma_tag(subdev->device);
    235 	const unsigned nullsz = 16 * 1024;
    236 	int nsegs;
    237 
    238 	/* XXX errno NetBSD->Linux */
    239 	ret = -bus_dmamem_alloc(dmat, nullsz, PAGE_SIZE, 0,
    240 	    &vmm->nullseg, 1, &nsegs, BUS_DMA_WAITOK);
    241 	if (ret) {
    242 fail0:		vmm->nullp = NULL;
    243 		break;
    244 	}
    245 	KASSERT(nsegs == 1);
    246 
    247 	/* XXX errno NetBSD->Linux */
    248 	ret = -bus_dmamap_create(dmat, nullsz /* size */, 1 /* maxnseg */,
    249 	    nullsz /* maxsegsz */, 0, BUS_DMA_WAITOK, &vmm->nullmap);
    250 	if (ret) {
    251 fail1:		bus_dmamem_free(dmat, &vmm->nullseg, 1);
    252 		goto fail0;
    253 	}
    254 
    255 	/* XXX errno NetBSD->Linux */
    256 	ret = -bus_dmamem_map(dmat, &vmm->nullseg, 1, nullsz,
    257 	    &vmm->nullp, BUS_DMA_WAITOK|BUS_DMA_COHERENT);
    258 	if (ret) {
    259 fail2:		bus_dmamap_destroy(dmat, vmm->nullmap);
    260 		goto fail1;
    261 	}
    262 
    263 	/* XXX errno NetBSD->Linux */
    264 	ret = -bus_dmamap_load(dmat, vmm->nullmap, vmm->nullp, nullsz,
    265 	    NULL, BUS_DMA_WAITOK);
    266 	if (ret) {
    267 fail3: __unused	bus_dmamem_unmap(dmat, vmm->nullp, nullsz);
    268 		goto fail2;
    269 	}
    270 	vmm->null = vmm->nullmap->dm_segs[0].ds_addr;
    271     } while (0);
    272 #else
    273 	vmm->nullp = dma_alloc_coherent(subdev->device->dev, 16 * 1024,
    274 					&vmm->null, GFP_KERNEL);
    275 #endif
    276 	if (!vmm->nullp) {
    277 		nvkm_warn(subdev, "unable to allocate dummy pages\n");
    278 		vmm->null = 0;
    279 	}
    280 
    281 	return 0;
    282 }
    283