xref: /openbmc/linux/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmnv44.c (revision 597473720f4dc69749542bfcfed4a927a43d935e)
103b0ba7bSBen Skeggs /*
203b0ba7bSBen Skeggs  * Copyright 2017 Red Hat Inc.
303b0ba7bSBen Skeggs  *
403b0ba7bSBen Skeggs  * Permission is hereby granted, free of charge, to any person obtaining a
503b0ba7bSBen Skeggs  * copy of this software and associated documentation files (the "Software"),
603b0ba7bSBen Skeggs  * to deal in the Software without restriction, including without limitation
703b0ba7bSBen Skeggs  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
803b0ba7bSBen Skeggs  * and/or sell copies of the Software, and to permit persons to whom the
903b0ba7bSBen Skeggs  * Software is furnished to do so, subject to the following conditions:
1003b0ba7bSBen Skeggs  *
1103b0ba7bSBen Skeggs  * The above copyright notice and this permission notice shall be included in
1203b0ba7bSBen Skeggs  * all copies or substantial portions of the Software.
1303b0ba7bSBen Skeggs  *
1403b0ba7bSBen Skeggs  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1503b0ba7bSBen Skeggs  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1603b0ba7bSBen Skeggs  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
1703b0ba7bSBen Skeggs  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
1803b0ba7bSBen Skeggs  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
1903b0ba7bSBen Skeggs  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
2003b0ba7bSBen Skeggs  * OTHER DEALINGS IN THE SOFTWARE.
2103b0ba7bSBen Skeggs  */
2203b0ba7bSBen Skeggs #include "vmm.h"
2303b0ba7bSBen Skeggs 
246ce51352SBen Skeggs #include <subdev/timer.h>
256ce51352SBen Skeggs 
266ce51352SBen Skeggs static void
nv44_vmm_pgt_fill(struct nvkm_vmm * vmm,struct nvkm_mmu_pt * pt,dma_addr_t * list,u32 ptei,u32 ptes)276ce51352SBen Skeggs nv44_vmm_pgt_fill(struct nvkm_vmm *vmm, struct nvkm_mmu_pt *pt,
286ce51352SBen Skeggs 		  dma_addr_t *list, u32 ptei, u32 ptes)
296ce51352SBen Skeggs {
306ce51352SBen Skeggs 	u32 pteo = (ptei << 2) & ~0x0000000f;
316ce51352SBen Skeggs 	u32 tmp[4];
326ce51352SBen Skeggs 
336ce51352SBen Skeggs 	tmp[0] = nvkm_ro32(pt->memory, pteo + 0x0);
346ce51352SBen Skeggs 	tmp[1] = nvkm_ro32(pt->memory, pteo + 0x4);
356ce51352SBen Skeggs 	tmp[2] = nvkm_ro32(pt->memory, pteo + 0x8);
366ce51352SBen Skeggs 	tmp[3] = nvkm_ro32(pt->memory, pteo + 0xc);
376ce51352SBen Skeggs 
386ce51352SBen Skeggs 	while (ptes--) {
396ce51352SBen Skeggs 		u32 addr = (list ? *list++ : vmm->null) >> 12;
406ce51352SBen Skeggs 		switch (ptei++ & 0x3) {
416ce51352SBen Skeggs 		case 0:
426ce51352SBen Skeggs 			tmp[0] &= ~0x07ffffff;
436ce51352SBen Skeggs 			tmp[0] |= addr;
446ce51352SBen Skeggs 			break;
456ce51352SBen Skeggs 		case 1:
466ce51352SBen Skeggs 			tmp[0] &= ~0xf8000000;
476ce51352SBen Skeggs 			tmp[0] |= addr << 27;
486ce51352SBen Skeggs 			tmp[1] &= ~0x003fffff;
496ce51352SBen Skeggs 			tmp[1] |= addr >> 5;
506ce51352SBen Skeggs 			break;
516ce51352SBen Skeggs 		case 2:
526ce51352SBen Skeggs 			tmp[1] &= ~0xffc00000;
536ce51352SBen Skeggs 			tmp[1] |= addr << 22;
546ce51352SBen Skeggs 			tmp[2] &= ~0x0001ffff;
556ce51352SBen Skeggs 			tmp[2] |= addr >> 10;
566ce51352SBen Skeggs 			break;
576ce51352SBen Skeggs 		case 3:
586ce51352SBen Skeggs 			tmp[2] &= ~0xfffe0000;
596ce51352SBen Skeggs 			tmp[2] |= addr << 17;
606ce51352SBen Skeggs 			tmp[3] &= ~0x00000fff;
616ce51352SBen Skeggs 			tmp[3] |= addr >> 15;
626ce51352SBen Skeggs 			break;
636ce51352SBen Skeggs 		}
646ce51352SBen Skeggs 	}
656ce51352SBen Skeggs 
666ce51352SBen Skeggs 	VMM_WO032(pt, vmm, pteo + 0x0, tmp[0]);
676ce51352SBen Skeggs 	VMM_WO032(pt, vmm, pteo + 0x4, tmp[1]);
686ce51352SBen Skeggs 	VMM_WO032(pt, vmm, pteo + 0x8, tmp[2]);
696ce51352SBen Skeggs 	VMM_WO032(pt, vmm, pteo + 0xc, tmp[3] | 0x40000000);
706ce51352SBen Skeggs }
716ce51352SBen Skeggs 
726ce51352SBen Skeggs static void
nv44_vmm_pgt_pte(struct nvkm_vmm * vmm,struct nvkm_mmu_pt * pt,u32 ptei,u32 ptes,struct nvkm_vmm_map * map,u64 addr)736ce51352SBen Skeggs nv44_vmm_pgt_pte(struct nvkm_vmm *vmm, struct nvkm_mmu_pt *pt,
746ce51352SBen Skeggs 		 u32 ptei, u32 ptes, struct nvkm_vmm_map *map, u64 addr)
756ce51352SBen Skeggs {
766ce51352SBen Skeggs 	dma_addr_t tmp[4], i;
776ce51352SBen Skeggs 
786ce51352SBen Skeggs 	if (ptei & 3) {
796ce51352SBen Skeggs 		const u32 pten = min(ptes, 4 - (ptei & 3));
806ce51352SBen Skeggs 		for (i = 0; i < pten; i++, addr += 0x1000)
816ce51352SBen Skeggs 			tmp[i] = addr;
826ce51352SBen Skeggs 		nv44_vmm_pgt_fill(vmm, pt, tmp, ptei, pten);
836ce51352SBen Skeggs 		ptei += pten;
846ce51352SBen Skeggs 		ptes -= pten;
856ce51352SBen Skeggs 	}
866ce51352SBen Skeggs 
876ce51352SBen Skeggs 	while (ptes >= 4) {
886ce51352SBen Skeggs 		for (i = 0; i < 4; i++, addr += 0x1000)
896ce51352SBen Skeggs 			tmp[i] = addr >> 12;
906ce51352SBen Skeggs 		VMM_WO032(pt, vmm, ptei++ * 4, tmp[0] >>  0 | tmp[1] << 27);
916ce51352SBen Skeggs 		VMM_WO032(pt, vmm, ptei++ * 4, tmp[1] >>  5 | tmp[2] << 22);
926ce51352SBen Skeggs 		VMM_WO032(pt, vmm, ptei++ * 4, tmp[2] >> 10 | tmp[3] << 17);
936ce51352SBen Skeggs 		VMM_WO032(pt, vmm, ptei++ * 4, tmp[3] >> 15 | 0x40000000);
946ce51352SBen Skeggs 		ptes -= 4;
956ce51352SBen Skeggs 	}
966ce51352SBen Skeggs 
976ce51352SBen Skeggs 	if (ptes) {
986ce51352SBen Skeggs 		for (i = 0; i < ptes; i++, addr += 0x1000)
996ce51352SBen Skeggs 			tmp[i] = addr;
1006ce51352SBen Skeggs 		nv44_vmm_pgt_fill(vmm, pt, tmp, ptei, ptes);
1016ce51352SBen Skeggs 	}
1026ce51352SBen Skeggs }
1036ce51352SBen Skeggs 
1046ce51352SBen Skeggs static void
nv44_vmm_pgt_sgl(struct nvkm_vmm * vmm,struct nvkm_mmu_pt * pt,u32 ptei,u32 ptes,struct nvkm_vmm_map * map)1056ce51352SBen Skeggs nv44_vmm_pgt_sgl(struct nvkm_vmm *vmm, struct nvkm_mmu_pt *pt,
1066ce51352SBen Skeggs 		 u32 ptei, u32 ptes, struct nvkm_vmm_map *map)
1076ce51352SBen Skeggs {
1086ce51352SBen Skeggs 	VMM_MAP_ITER_SGL(vmm, pt, ptei, ptes, map, nv44_vmm_pgt_pte);
1096ce51352SBen Skeggs }
1106ce51352SBen Skeggs 
1116ce51352SBen Skeggs static void
nv44_vmm_pgt_dma(struct nvkm_vmm * vmm,struct nvkm_mmu_pt * pt,u32 ptei,u32 ptes,struct nvkm_vmm_map * map)1126ce51352SBen Skeggs nv44_vmm_pgt_dma(struct nvkm_vmm *vmm, struct nvkm_mmu_pt *pt,
1136ce51352SBen Skeggs 		 u32 ptei, u32 ptes, struct nvkm_vmm_map *map)
1146ce51352SBen Skeggs {
1156ce51352SBen Skeggs #if PAGE_SHIFT == 12
1166ce51352SBen Skeggs 	nvkm_kmap(pt->memory);
1176ce51352SBen Skeggs 	if (ptei & 3) {
1186ce51352SBen Skeggs 		const u32 pten = min(ptes, 4 - (ptei & 3));
1196ce51352SBen Skeggs 		nv44_vmm_pgt_fill(vmm, pt, map->dma, ptei, pten);
1206ce51352SBen Skeggs 		ptei += pten;
1216ce51352SBen Skeggs 		ptes -= pten;
1226ce51352SBen Skeggs 		map->dma += pten;
1236ce51352SBen Skeggs 	}
1246ce51352SBen Skeggs 
1256ce51352SBen Skeggs 	while (ptes >= 4) {
1266ce51352SBen Skeggs 		u32 tmp[4], i;
1276ce51352SBen Skeggs 		for (i = 0; i < 4; i++)
1286ce51352SBen Skeggs 			tmp[i] = *map->dma++ >> 12;
1296ce51352SBen Skeggs 		VMM_WO032(pt, vmm, ptei++ * 4, tmp[0] >>  0 | tmp[1] << 27);
1306ce51352SBen Skeggs 		VMM_WO032(pt, vmm, ptei++ * 4, tmp[1] >>  5 | tmp[2] << 22);
1316ce51352SBen Skeggs 		VMM_WO032(pt, vmm, ptei++ * 4, tmp[2] >> 10 | tmp[3] << 17);
1326ce51352SBen Skeggs 		VMM_WO032(pt, vmm, ptei++ * 4, tmp[3] >> 15 | 0x40000000);
1336ce51352SBen Skeggs 		ptes -= 4;
1346ce51352SBen Skeggs 	}
1356ce51352SBen Skeggs 
1366ce51352SBen Skeggs 	if (ptes) {
1376ce51352SBen Skeggs 		nv44_vmm_pgt_fill(vmm, pt, map->dma, ptei, ptes);
1386ce51352SBen Skeggs 		map->dma += ptes;
1396ce51352SBen Skeggs 	}
1406ce51352SBen Skeggs 	nvkm_done(pt->memory);
1416ce51352SBen Skeggs #else
1426ce51352SBen Skeggs 	VMM_MAP_ITER_DMA(vmm, pt, ptei, ptes, map, nv44_vmm_pgt_pte);
1436ce51352SBen Skeggs #endif
1446ce51352SBen Skeggs }
1456ce51352SBen Skeggs 
1466ce51352SBen Skeggs static void
nv44_vmm_pgt_unmap(struct nvkm_vmm * vmm,struct nvkm_mmu_pt * pt,u32 ptei,u32 ptes)1476ce51352SBen Skeggs nv44_vmm_pgt_unmap(struct nvkm_vmm *vmm,
1486ce51352SBen Skeggs 		   struct nvkm_mmu_pt *pt, u32 ptei, u32 ptes)
1496ce51352SBen Skeggs {
1506ce51352SBen Skeggs 	nvkm_kmap(pt->memory);
1516ce51352SBen Skeggs 	if (ptei & 3) {
1526ce51352SBen Skeggs 		const u32 pten = min(ptes, 4 - (ptei & 3));
1536ce51352SBen Skeggs 		nv44_vmm_pgt_fill(vmm, pt, NULL, ptei, pten);
1546ce51352SBen Skeggs 		ptei += pten;
1556ce51352SBen Skeggs 		ptes -= pten;
1566ce51352SBen Skeggs 	}
1576ce51352SBen Skeggs 
1586ce51352SBen Skeggs 	while (ptes > 4) {
1596ce51352SBen Skeggs 		VMM_WO032(pt, vmm, ptei++ * 4, 0x00000000);
1606ce51352SBen Skeggs 		VMM_WO032(pt, vmm, ptei++ * 4, 0x00000000);
1616ce51352SBen Skeggs 		VMM_WO032(pt, vmm, ptei++ * 4, 0x00000000);
1626ce51352SBen Skeggs 		VMM_WO032(pt, vmm, ptei++ * 4, 0x00000000);
1636ce51352SBen Skeggs 		ptes -= 4;
1646ce51352SBen Skeggs 	}
1656ce51352SBen Skeggs 
1666ce51352SBen Skeggs 	if (ptes)
1676ce51352SBen Skeggs 		nv44_vmm_pgt_fill(vmm, pt, NULL, ptei, ptes);
1686ce51352SBen Skeggs 	nvkm_done(pt->memory);
1696ce51352SBen Skeggs }
1706ce51352SBen Skeggs 
17103b0ba7bSBen Skeggs static const struct nvkm_vmm_desc_func
17203b0ba7bSBen Skeggs nv44_vmm_desc_pgt = {
1736ce51352SBen Skeggs 	.unmap = nv44_vmm_pgt_unmap,
1746ce51352SBen Skeggs 	.dma = nv44_vmm_pgt_dma,
1756ce51352SBen Skeggs 	.sgl = nv44_vmm_pgt_sgl,
17603b0ba7bSBen Skeggs };
17703b0ba7bSBen Skeggs 
17803b0ba7bSBen Skeggs static const struct nvkm_vmm_desc
17903b0ba7bSBen Skeggs nv44_vmm_desc_12[] = {
18003b0ba7bSBen Skeggs 	{ PGT, 17, 4, 0x80000, &nv44_vmm_desc_pgt },
18103b0ba7bSBen Skeggs 	{}
18203b0ba7bSBen Skeggs };
18303b0ba7bSBen Skeggs 
1846ce51352SBen Skeggs static void
nv44_vmm_flush(struct nvkm_vmm * vmm,int level)1856ce51352SBen Skeggs nv44_vmm_flush(struct nvkm_vmm *vmm, int level)
1866ce51352SBen Skeggs {
1876ce51352SBen Skeggs 	struct nvkm_device *device = vmm->mmu->subdev.device;
1886ce51352SBen Skeggs 	nvkm_wr32(device, 0x100814, vmm->limit - 4096);
1896ce51352SBen Skeggs 	nvkm_wr32(device, 0x100808, 0x000000020);
1906ce51352SBen Skeggs 	nvkm_msec(device, 2000,
1916ce51352SBen Skeggs 		if (nvkm_rd32(device, 0x100808) & 0x00000001)
1926ce51352SBen Skeggs 			break;
1936ce51352SBen Skeggs 	);
1946ce51352SBen Skeggs 	nvkm_wr32(device, 0x100808, 0x00000000);
1956ce51352SBen Skeggs }
1966ce51352SBen Skeggs 
19703b0ba7bSBen Skeggs static const struct nvkm_vmm_func
19803b0ba7bSBen Skeggs nv44_vmm = {
1996ce51352SBen Skeggs 	.valid = nv04_vmm_valid,
2006ce51352SBen Skeggs 	.flush = nv44_vmm_flush,
20103b0ba7bSBen Skeggs 	.page = {
20203b0ba7bSBen Skeggs 		{ 12, &nv44_vmm_desc_12[0], NVKM_VMM_PAGE_HOST },
20303b0ba7bSBen Skeggs 		{}
20403b0ba7bSBen Skeggs 	}
20503b0ba7bSBen Skeggs };
20603b0ba7bSBen Skeggs 
20703b0ba7bSBen Skeggs int
nv44_vmm_new(struct nvkm_mmu * mmu,bool managed,u64 addr,u64 size,void * argv,u32 argc,struct lock_class_key * key,const char * name,struct nvkm_vmm ** pvmm)208*2606f291SBen Skeggs nv44_vmm_new(struct nvkm_mmu *mmu, bool managed, u64 addr, u64 size,
209*2606f291SBen Skeggs 	     void *argv, u32 argc, struct lock_class_key *key, const char *name,
21003b0ba7bSBen Skeggs 	     struct nvkm_vmm **pvmm)
21103b0ba7bSBen Skeggs {
21203b0ba7bSBen Skeggs 	struct nvkm_subdev *subdev = &mmu->subdev;
21303b0ba7bSBen Skeggs 	struct nvkm_vmm *vmm;
21403b0ba7bSBen Skeggs 	int ret;
21503b0ba7bSBen Skeggs 
216*2606f291SBen Skeggs 	ret = nv04_vmm_new_(&nv44_vmm, mmu, 0, managed, addr, size,
21703b0ba7bSBen Skeggs 			    argv, argc, key, name, &vmm);
21803b0ba7bSBen Skeggs 	*pvmm = vmm;
21903b0ba7bSBen Skeggs 	if (ret)
22003b0ba7bSBen Skeggs 		return ret;
22103b0ba7bSBen Skeggs 
22203b0ba7bSBen Skeggs 	vmm->nullp = dma_alloc_coherent(subdev->device->dev, 16 * 1024,
22303b0ba7bSBen Skeggs 					&vmm->null, GFP_KERNEL);
22403b0ba7bSBen Skeggs 	if (!vmm->nullp) {
22503b0ba7bSBen Skeggs 		nvkm_warn(subdev, "unable to allocate dummy pages\n");
22603b0ba7bSBen Skeggs 		vmm->null = 0;
22703b0ba7bSBen Skeggs 	}
22803b0ba7bSBen Skeggs 
22903b0ba7bSBen Skeggs 	return 0;
23003b0ba7bSBen Skeggs }
231