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