xref: /linux/drivers/media/platform/nvidia/tegra-vde/iommu.c (revision 9b18ef7c9ff408df170ac339c57a759145c055d2)
1*9b18ef7cSMauro Carvalho Chehab // SPDX-License-Identifier: GPL-2.0+
2*9b18ef7cSMauro Carvalho Chehab /*
3*9b18ef7cSMauro Carvalho Chehab  * NVIDIA Tegra Video decoder driver
4*9b18ef7cSMauro Carvalho Chehab  *
5*9b18ef7cSMauro Carvalho Chehab  * Copyright (C) 2016-2019 GRATE-DRIVER project
6*9b18ef7cSMauro Carvalho Chehab  */
7*9b18ef7cSMauro Carvalho Chehab 
8*9b18ef7cSMauro Carvalho Chehab #include <linux/iommu.h>
9*9b18ef7cSMauro Carvalho Chehab #include <linux/iova.h>
10*9b18ef7cSMauro Carvalho Chehab #include <linux/kernel.h>
11*9b18ef7cSMauro Carvalho Chehab #include <linux/platform_device.h>
12*9b18ef7cSMauro Carvalho Chehab 
13*9b18ef7cSMauro Carvalho Chehab #if IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU)
14*9b18ef7cSMauro Carvalho Chehab #include <asm/dma-iommu.h>
15*9b18ef7cSMauro Carvalho Chehab #endif
16*9b18ef7cSMauro Carvalho Chehab 
17*9b18ef7cSMauro Carvalho Chehab #include "vde.h"
18*9b18ef7cSMauro Carvalho Chehab 
19*9b18ef7cSMauro Carvalho Chehab int tegra_vde_iommu_map(struct tegra_vde *vde,
20*9b18ef7cSMauro Carvalho Chehab 			struct sg_table *sgt,
21*9b18ef7cSMauro Carvalho Chehab 			struct iova **iovap,
22*9b18ef7cSMauro Carvalho Chehab 			size_t size)
23*9b18ef7cSMauro Carvalho Chehab {
24*9b18ef7cSMauro Carvalho Chehab 	struct iova *iova;
25*9b18ef7cSMauro Carvalho Chehab 	unsigned long shift;
26*9b18ef7cSMauro Carvalho Chehab 	unsigned long end;
27*9b18ef7cSMauro Carvalho Chehab 	dma_addr_t addr;
28*9b18ef7cSMauro Carvalho Chehab 
29*9b18ef7cSMauro Carvalho Chehab 	end = vde->domain->geometry.aperture_end;
30*9b18ef7cSMauro Carvalho Chehab 	size = iova_align(&vde->iova, size);
31*9b18ef7cSMauro Carvalho Chehab 	shift = iova_shift(&vde->iova);
32*9b18ef7cSMauro Carvalho Chehab 
33*9b18ef7cSMauro Carvalho Chehab 	iova = alloc_iova(&vde->iova, size >> shift, end >> shift, true);
34*9b18ef7cSMauro Carvalho Chehab 	if (!iova)
35*9b18ef7cSMauro Carvalho Chehab 		return -ENOMEM;
36*9b18ef7cSMauro Carvalho Chehab 
37*9b18ef7cSMauro Carvalho Chehab 	addr = iova_dma_addr(&vde->iova, iova);
38*9b18ef7cSMauro Carvalho Chehab 
39*9b18ef7cSMauro Carvalho Chehab 	size = iommu_map_sgtable(vde->domain, addr, sgt,
40*9b18ef7cSMauro Carvalho Chehab 				 IOMMU_READ | IOMMU_WRITE);
41*9b18ef7cSMauro Carvalho Chehab 	if (!size) {
42*9b18ef7cSMauro Carvalho Chehab 		__free_iova(&vde->iova, iova);
43*9b18ef7cSMauro Carvalho Chehab 		return -ENXIO;
44*9b18ef7cSMauro Carvalho Chehab 	}
45*9b18ef7cSMauro Carvalho Chehab 
46*9b18ef7cSMauro Carvalho Chehab 	*iovap = iova;
47*9b18ef7cSMauro Carvalho Chehab 
48*9b18ef7cSMauro Carvalho Chehab 	return 0;
49*9b18ef7cSMauro Carvalho Chehab }
50*9b18ef7cSMauro Carvalho Chehab 
51*9b18ef7cSMauro Carvalho Chehab void tegra_vde_iommu_unmap(struct tegra_vde *vde, struct iova *iova)
52*9b18ef7cSMauro Carvalho Chehab {
53*9b18ef7cSMauro Carvalho Chehab 	unsigned long shift = iova_shift(&vde->iova);
54*9b18ef7cSMauro Carvalho Chehab 	unsigned long size = iova_size(iova) << shift;
55*9b18ef7cSMauro Carvalho Chehab 	dma_addr_t addr = iova_dma_addr(&vde->iova, iova);
56*9b18ef7cSMauro Carvalho Chehab 
57*9b18ef7cSMauro Carvalho Chehab 	iommu_unmap(vde->domain, addr, size);
58*9b18ef7cSMauro Carvalho Chehab 	__free_iova(&vde->iova, iova);
59*9b18ef7cSMauro Carvalho Chehab }
60*9b18ef7cSMauro Carvalho Chehab 
61*9b18ef7cSMauro Carvalho Chehab int tegra_vde_iommu_init(struct tegra_vde *vde)
62*9b18ef7cSMauro Carvalho Chehab {
63*9b18ef7cSMauro Carvalho Chehab 	struct device *dev = vde->dev;
64*9b18ef7cSMauro Carvalho Chehab 	struct iova *iova;
65*9b18ef7cSMauro Carvalho Chehab 	unsigned long order;
66*9b18ef7cSMauro Carvalho Chehab 	unsigned long shift;
67*9b18ef7cSMauro Carvalho Chehab 	int err;
68*9b18ef7cSMauro Carvalho Chehab 
69*9b18ef7cSMauro Carvalho Chehab 	vde->group = iommu_group_get(dev);
70*9b18ef7cSMauro Carvalho Chehab 	if (!vde->group)
71*9b18ef7cSMauro Carvalho Chehab 		return 0;
72*9b18ef7cSMauro Carvalho Chehab 
73*9b18ef7cSMauro Carvalho Chehab #if IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU)
74*9b18ef7cSMauro Carvalho Chehab 	if (dev->archdata.mapping) {
75*9b18ef7cSMauro Carvalho Chehab 		struct dma_iommu_mapping *mapping = to_dma_iommu_mapping(dev);
76*9b18ef7cSMauro Carvalho Chehab 
77*9b18ef7cSMauro Carvalho Chehab 		arm_iommu_detach_device(dev);
78*9b18ef7cSMauro Carvalho Chehab 		arm_iommu_release_mapping(mapping);
79*9b18ef7cSMauro Carvalho Chehab 	}
80*9b18ef7cSMauro Carvalho Chehab #endif
81*9b18ef7cSMauro Carvalho Chehab 	vde->domain = iommu_domain_alloc(&platform_bus_type);
82*9b18ef7cSMauro Carvalho Chehab 	if (!vde->domain) {
83*9b18ef7cSMauro Carvalho Chehab 		err = -ENOMEM;
84*9b18ef7cSMauro Carvalho Chehab 		goto put_group;
85*9b18ef7cSMauro Carvalho Chehab 	}
86*9b18ef7cSMauro Carvalho Chehab 
87*9b18ef7cSMauro Carvalho Chehab 	err = iova_cache_get();
88*9b18ef7cSMauro Carvalho Chehab 	if (err)
89*9b18ef7cSMauro Carvalho Chehab 		goto free_domain;
90*9b18ef7cSMauro Carvalho Chehab 
91*9b18ef7cSMauro Carvalho Chehab 	order = __ffs(vde->domain->pgsize_bitmap);
92*9b18ef7cSMauro Carvalho Chehab 	init_iova_domain(&vde->iova, 1UL << order, 0);
93*9b18ef7cSMauro Carvalho Chehab 
94*9b18ef7cSMauro Carvalho Chehab 	err = iommu_attach_group(vde->domain, vde->group);
95*9b18ef7cSMauro Carvalho Chehab 	if (err)
96*9b18ef7cSMauro Carvalho Chehab 		goto put_iova;
97*9b18ef7cSMauro Carvalho Chehab 
98*9b18ef7cSMauro Carvalho Chehab 	/*
99*9b18ef7cSMauro Carvalho Chehab 	 * We're using some static addresses that are not accessible by VDE
100*9b18ef7cSMauro Carvalho Chehab 	 * to trap invalid memory accesses.
101*9b18ef7cSMauro Carvalho Chehab 	 */
102*9b18ef7cSMauro Carvalho Chehab 	shift = iova_shift(&vde->iova);
103*9b18ef7cSMauro Carvalho Chehab 	iova = reserve_iova(&vde->iova, 0x60000000 >> shift,
104*9b18ef7cSMauro Carvalho Chehab 			    0x70000000 >> shift);
105*9b18ef7cSMauro Carvalho Chehab 	if (!iova) {
106*9b18ef7cSMauro Carvalho Chehab 		err = -ENOMEM;
107*9b18ef7cSMauro Carvalho Chehab 		goto detach_group;
108*9b18ef7cSMauro Carvalho Chehab 	}
109*9b18ef7cSMauro Carvalho Chehab 
110*9b18ef7cSMauro Carvalho Chehab 	vde->iova_resv_static_addresses = iova;
111*9b18ef7cSMauro Carvalho Chehab 
112*9b18ef7cSMauro Carvalho Chehab 	/*
113*9b18ef7cSMauro Carvalho Chehab 	 * BSEV's end-address wraps around due to integer overflow during
114*9b18ef7cSMauro Carvalho Chehab 	 * of hardware context preparation if IOVA is allocated at the end
115*9b18ef7cSMauro Carvalho Chehab 	 * of address space and VDE can't handle that. Hence simply reserve
116*9b18ef7cSMauro Carvalho Chehab 	 * the last page to avoid the problem.
117*9b18ef7cSMauro Carvalho Chehab 	 */
118*9b18ef7cSMauro Carvalho Chehab 	iova = reserve_iova(&vde->iova, 0xffffffff >> shift,
119*9b18ef7cSMauro Carvalho Chehab 			    (0xffffffff >> shift) + 1);
120*9b18ef7cSMauro Carvalho Chehab 	if (!iova) {
121*9b18ef7cSMauro Carvalho Chehab 		err = -ENOMEM;
122*9b18ef7cSMauro Carvalho Chehab 		goto unreserve_iova;
123*9b18ef7cSMauro Carvalho Chehab 	}
124*9b18ef7cSMauro Carvalho Chehab 
125*9b18ef7cSMauro Carvalho Chehab 	vde->iova_resv_last_page = iova;
126*9b18ef7cSMauro Carvalho Chehab 
127*9b18ef7cSMauro Carvalho Chehab 	return 0;
128*9b18ef7cSMauro Carvalho Chehab 
129*9b18ef7cSMauro Carvalho Chehab unreserve_iova:
130*9b18ef7cSMauro Carvalho Chehab 	__free_iova(&vde->iova, vde->iova_resv_static_addresses);
131*9b18ef7cSMauro Carvalho Chehab detach_group:
132*9b18ef7cSMauro Carvalho Chehab 	iommu_detach_group(vde->domain, vde->group);
133*9b18ef7cSMauro Carvalho Chehab put_iova:
134*9b18ef7cSMauro Carvalho Chehab 	put_iova_domain(&vde->iova);
135*9b18ef7cSMauro Carvalho Chehab 	iova_cache_put();
136*9b18ef7cSMauro Carvalho Chehab free_domain:
137*9b18ef7cSMauro Carvalho Chehab 	iommu_domain_free(vde->domain);
138*9b18ef7cSMauro Carvalho Chehab put_group:
139*9b18ef7cSMauro Carvalho Chehab 	iommu_group_put(vde->group);
140*9b18ef7cSMauro Carvalho Chehab 
141*9b18ef7cSMauro Carvalho Chehab 	return err;
142*9b18ef7cSMauro Carvalho Chehab }
143*9b18ef7cSMauro Carvalho Chehab 
144*9b18ef7cSMauro Carvalho Chehab void tegra_vde_iommu_deinit(struct tegra_vde *vde)
145*9b18ef7cSMauro Carvalho Chehab {
146*9b18ef7cSMauro Carvalho Chehab 	if (vde->domain) {
147*9b18ef7cSMauro Carvalho Chehab 		__free_iova(&vde->iova, vde->iova_resv_last_page);
148*9b18ef7cSMauro Carvalho Chehab 		__free_iova(&vde->iova, vde->iova_resv_static_addresses);
149*9b18ef7cSMauro Carvalho Chehab 		iommu_detach_group(vde->domain, vde->group);
150*9b18ef7cSMauro Carvalho Chehab 		put_iova_domain(&vde->iova);
151*9b18ef7cSMauro Carvalho Chehab 		iova_cache_put();
152*9b18ef7cSMauro Carvalho Chehab 		iommu_domain_free(vde->domain);
153*9b18ef7cSMauro Carvalho Chehab 		iommu_group_put(vde->group);
154*9b18ef7cSMauro Carvalho Chehab 
155*9b18ef7cSMauro Carvalho Chehab 		vde->domain = NULL;
156*9b18ef7cSMauro Carvalho Chehab 	}
157*9b18ef7cSMauro Carvalho Chehab }
158