]>
Commit | Line | Data |
---|---|---|
3863c9bc BS |
1 | /* |
2 | * Copyright 2012 Red Hat Inc. | |
3 | * | |
4 | * Permission is hereby granted, free of charge, to any person obtaining a | |
5 | * copy of this software and associated documentation files (the "Software"), | |
6 | * to deal in the Software without restriction, including without limitation | |
7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
8 | * and/or sell copies of the Software, and to permit persons to whom the | |
9 | * Software is furnished to do so, subject to the following conditions: | |
10 | * | |
11 | * The above copyright notice and this permission notice shall be included in | |
12 | * all copies or substantial portions of the Software. | |
13 | * | |
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
17 | * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
18 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
19 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
20 | * OTHER DEALINGS IN THE SOFTWARE. | |
21 | * | |
22 | * Authors: Ben Skeggs | |
23 | */ | |
42594600 | 24 | #include "nv04.h" |
3863c9bc BS |
25 | |
26 | #include <core/gpuobj.h> | |
e5f186c4 | 27 | #include <core/option.h> |
3863c9bc | 28 | #include <subdev/timer.h> |
3863c9bc BS |
29 | |
30 | #define NV44_GART_SIZE (512 * 1024 * 1024) | |
31 | #define NV44_GART_PAGE ( 4 * 1024) | |
32 | ||
33 | /******************************************************************************* | |
34 | * VM map/unmap callbacks | |
35 | ******************************************************************************/ | |
36 | ||
3863c9bc | 37 | static void |
d0659d32 | 38 | nv44_vm_fill(struct nvkm_memory *pgt, dma_addr_t null, |
3863c9bc BS |
39 | dma_addr_t *list, u32 pte, u32 cnt) |
40 | { | |
41 | u32 base = (pte << 2) & ~0x0000000f; | |
42 | u32 tmp[4]; | |
43 | ||
cd821077 BS |
44 | tmp[0] = nvkm_ro32(pgt, base + 0x0); |
45 | tmp[1] = nvkm_ro32(pgt, base + 0x4); | |
46 | tmp[2] = nvkm_ro32(pgt, base + 0x8); | |
47 | tmp[3] = nvkm_ro32(pgt, base + 0xc); | |
e5f186c4 | 48 | |
3863c9bc BS |
49 | while (cnt--) { |
50 | u32 addr = list ? (*list++ >> 12) : (null >> 12); | |
51 | switch (pte++ & 0x3) { | |
52 | case 0: | |
53 | tmp[0] &= ~0x07ffffff; | |
54 | tmp[0] |= addr; | |
55 | break; | |
56 | case 1: | |
57 | tmp[0] &= ~0xf8000000; | |
58 | tmp[0] |= addr << 27; | |
59 | tmp[1] &= ~0x003fffff; | |
60 | tmp[1] |= addr >> 5; | |
61 | break; | |
62 | case 2: | |
63 | tmp[1] &= ~0xffc00000; | |
64 | tmp[1] |= addr << 22; | |
65 | tmp[2] &= ~0x0001ffff; | |
66 | tmp[2] |= addr >> 10; | |
67 | break; | |
68 | case 3: | |
69 | tmp[2] &= ~0xfffe0000; | |
70 | tmp[2] |= addr << 17; | |
71 | tmp[3] &= ~0x00000fff; | |
72 | tmp[3] |= addr >> 15; | |
73 | break; | |
74 | } | |
75 | } | |
76 | ||
cd821077 BS |
77 | nvkm_wo32(pgt, base + 0x0, tmp[0]); |
78 | nvkm_wo32(pgt, base + 0x4, tmp[1]); | |
79 | nvkm_wo32(pgt, base + 0x8, tmp[2]); | |
80 | nvkm_wo32(pgt, base + 0xc, tmp[3] | 0x40000000); | |
3863c9bc BS |
81 | } |
82 | ||
83 | static void | |
d0659d32 | 84 | nv44_vm_map_sg(struct nvkm_vma *vma, struct nvkm_memory *pgt, |
42594600 | 85 | struct nvkm_mem *mem, u32 pte, u32 cnt, dma_addr_t *list) |
3863c9bc | 86 | { |
d0659d32 | 87 | struct nv04_mmu *mmu = nv04_mmu(vma->vm->mmu); |
3863c9bc BS |
88 | u32 tmp[4]; |
89 | int i; | |
90 | ||
cd821077 | 91 | nvkm_kmap(pgt); |
3863c9bc BS |
92 | if (pte & 3) { |
93 | u32 max = 4 - (pte & 3); | |
94 | u32 part = (cnt > max) ? max : cnt; | |
1f5bffca | 95 | nv44_vm_fill(pgt, mmu->null, list, pte, part); |
3863c9bc BS |
96 | pte += part; |
97 | list += part; | |
98 | cnt -= part; | |
99 | } | |
100 | ||
101 | while (cnt >= 4) { | |
102 | for (i = 0; i < 4; i++) | |
103 | tmp[i] = *list++ >> 12; | |
cd821077 BS |
104 | nvkm_wo32(pgt, pte++ * 4, tmp[0] >> 0 | tmp[1] << 27); |
105 | nvkm_wo32(pgt, pte++ * 4, tmp[1] >> 5 | tmp[2] << 22); | |
106 | nvkm_wo32(pgt, pte++ * 4, tmp[2] >> 10 | tmp[3] << 17); | |
107 | nvkm_wo32(pgt, pte++ * 4, tmp[3] >> 15 | 0x40000000); | |
3863c9bc BS |
108 | cnt -= 4; |
109 | } | |
110 | ||
111 | if (cnt) | |
1f5bffca | 112 | nv44_vm_fill(pgt, mmu->null, list, pte, cnt); |
cd821077 | 113 | nvkm_done(pgt); |
3863c9bc BS |
114 | } |
115 | ||
116 | static void | |
d0659d32 | 117 | nv44_vm_unmap(struct nvkm_vma *vma, struct nvkm_memory *pgt, u32 pte, u32 cnt) |
3863c9bc | 118 | { |
d0659d32 | 119 | struct nv04_mmu *mmu = nv04_mmu(vma->vm->mmu); |
3863c9bc | 120 | |
cd821077 | 121 | nvkm_kmap(pgt); |
3863c9bc BS |
122 | if (pte & 3) { |
123 | u32 max = 4 - (pte & 3); | |
124 | u32 part = (cnt > max) ? max : cnt; | |
1f5bffca | 125 | nv44_vm_fill(pgt, mmu->null, NULL, pte, part); |
3863c9bc BS |
126 | pte += part; |
127 | cnt -= part; | |
128 | } | |
129 | ||
130 | while (cnt >= 4) { | |
cd821077 BS |
131 | nvkm_wo32(pgt, pte++ * 4, 0x00000000); |
132 | nvkm_wo32(pgt, pte++ * 4, 0x00000000); | |
133 | nvkm_wo32(pgt, pte++ * 4, 0x00000000); | |
134 | nvkm_wo32(pgt, pte++ * 4, 0x00000000); | |
3863c9bc BS |
135 | cnt -= 4; |
136 | } | |
137 | ||
138 | if (cnt) | |
1f5bffca | 139 | nv44_vm_fill(pgt, mmu->null, NULL, pte, cnt); |
cd821077 | 140 | nvkm_done(pgt); |
3863c9bc BS |
141 | } |
142 | ||
143 | static void | |
42594600 | 144 | nv44_vm_flush(struct nvkm_vm *vm) |
3863c9bc | 145 | { |
d0659d32 | 146 | struct nv04_mmu *mmu = nv04_mmu(vm->mmu); |
83f56106 BS |
147 | struct nvkm_device *device = mmu->base.subdev.device; |
148 | nvkm_wr32(device, 0x100814, mmu->base.limit - NV44_GART_PAGE); | |
149 | nvkm_wr32(device, 0x100808, 0x00000020); | |
909604d4 BS |
150 | nvkm_msec(device, 2000, |
151 | if (nvkm_rd32(device, 0x100808) & 0x00000001) | |
152 | break; | |
153 | ); | |
83f56106 | 154 | nvkm_wr32(device, 0x100808, 0x00000000); |
3863c9bc BS |
155 | } |
156 | ||
157 | /******************************************************************************* | |
5ce3bf3c | 158 | * MMU subdev |
3863c9bc BS |
159 | ******************************************************************************/ |
160 | ||
161 | static int | |
c9582455 | 162 | nv44_mmu_oneinit(struct nvkm_mmu *base) |
3863c9bc | 163 | { |
c9582455 BS |
164 | struct nv04_mmu *mmu = nv04_mmu(base); |
165 | struct nvkm_device *device = mmu->base.subdev.device; | |
3863c9bc BS |
166 | int ret; |
167 | ||
26c9e8ef BS |
168 | mmu->nullp = dma_alloc_coherent(device->dev, 16 * 1024, |
169 | &mmu->null, GFP_KERNEL); | |
1f5bffca | 170 | if (!mmu->nullp) { |
85ae830f | 171 | nvkm_warn(&mmu->base.subdev, "unable to allocate dummy pages\n"); |
1f5bffca | 172 | mmu->null = 0; |
3863c9bc BS |
173 | } |
174 | ||
1de68568 | 175 | ret = nvkm_vm_create(&mmu->base, 0, NV44_GART_SIZE, 0, 4096, NULL, |
0b11b30d | 176 | &mmu->base.vmm); |
3863c9bc BS |
177 | if (ret) |
178 | return ret; | |
179 | ||
d0659d32 | 180 | ret = nvkm_memory_new(device, NVKM_MEM_TARGET_INST, |
42594600 | 181 | (NV44_GART_SIZE / NV44_GART_PAGE) * 4, |
d0659d32 | 182 | 512 * 1024, true, |
0b11b30d BS |
183 | &mmu->base.vmm->pgt[0].mem[0]); |
184 | mmu->base.vmm->pgt[0].refcount[0] = 1; | |
c9582455 | 185 | return ret; |
3863c9bc BS |
186 | } |
187 | ||
c9582455 BS |
188 | static void |
189 | nv44_mmu_init(struct nvkm_mmu *base) | |
3863c9bc | 190 | { |
c9582455 | 191 | struct nv04_mmu *mmu = nv04_mmu(base); |
83f56106 | 192 | struct nvkm_device *device = mmu->base.subdev.device; |
0b11b30d | 193 | struct nvkm_memory *gart = mmu->base.vmm->pgt[0].mem[0]; |
3863c9bc | 194 | u32 addr; |
3863c9bc BS |
195 | |
196 | /* calculate vram address of this PRAMIN block, object must be | |
197 | * allocated on 512KiB alignment, and not exceed a total size | |
198 | * of 512KiB for this to work correctly | |
199 | */ | |
83f56106 | 200 | addr = nvkm_rd32(device, 0x10020c); |
d0659d32 | 201 | addr -= ((nvkm_memory_addr(gart) >> 19) + 1) << 19; |
3863c9bc | 202 | |
83f56106 BS |
203 | nvkm_wr32(device, 0x100850, 0x80000000); |
204 | nvkm_wr32(device, 0x100818, mmu->null); | |
205 | nvkm_wr32(device, 0x100804, NV44_GART_SIZE); | |
206 | nvkm_wr32(device, 0x100850, 0x00008000); | |
207 | nvkm_mask(device, 0x10008c, 0x00000200, 0x00000200); | |
208 | nvkm_wr32(device, 0x100820, 0x00000000); | |
209 | nvkm_wr32(device, 0x10082c, 0x00000001); | |
210 | nvkm_wr32(device, 0x100800, addr | 0x00000010); | |
3863c9bc BS |
211 | } |
212 | ||
c9582455 BS |
213 | static const struct nvkm_mmu_func |
214 | nv44_mmu = { | |
215 | .dtor = nv04_mmu_dtor, | |
216 | .oneinit = nv44_mmu_oneinit, | |
217 | .init = nv44_mmu_init, | |
218 | .limit = NV44_GART_SIZE, | |
219 | .dma_bits = 39, | |
220 | .pgt_bits = 32 - 12, | |
221 | .spg_shift = 12, | |
222 | .lpg_shift = 12, | |
223 | .map_sg = nv44_vm_map_sg, | |
224 | .unmap = nv44_vm_unmap, | |
225 | .flush = nv44_vm_flush, | |
3863c9bc | 226 | }; |
c9582455 BS |
227 | |
228 | int | |
229 | nv44_mmu_new(struct nvkm_device *device, int index, struct nvkm_mmu **pmmu) | |
230 | { | |
26c9e8ef | 231 | if (device->type == NVKM_DEVICE_AGP || |
c9582455 BS |
232 | !nvkm_boolopt(device->cfgopt, "NvPCIE", true)) |
233 | return nv04_mmu_new(device, index, pmmu); | |
234 | ||
235 | return nv04_mmu_new_(&nv44_mmu, device, index, pmmu); | |
236 | } |