nouveau_nvkm_subdev_instmem_nv50.c revision 1.7
1/* $NetBSD: nouveau_nvkm_subdev_instmem_nv50.c,v 1.7 2021/12/19 10:51:58 riastradh Exp $ */ 2 3/* 4 * Copyright 2012 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 * Authors: Ben Skeggs 25 */ 26#include <sys/cdefs.h> 27__KERNEL_RCSID(0, "$NetBSD: nouveau_nvkm_subdev_instmem_nv50.c,v 1.7 2021/12/19 10:51:58 riastradh Exp $"); 28 29#define nv50_instmem(p) container_of((p), struct nv50_instmem, base) 30#include "priv.h" 31 32#include <core/memory.h> 33#include <subdev/bar.h> 34#include <subdev/fb.h> 35#include <subdev/mmu.h> 36 37#ifdef __NetBSD__ 38# define __iomem __nvkm_memory_iomem 39#endif 40 41struct nv50_instmem { 42 struct nvkm_instmem base; 43 u64 addr; 44 45 /* Mappings that can be evicted when BAR2 space has been exhausted. */ 46 struct list_head lru; 47}; 48 49/****************************************************************************** 50 * instmem object implementation 51 *****************************************************************************/ 52#define nv50_instobj(p) container_of((p), struct nv50_instobj, base.memory) 53 54struct nv50_instobj { 55 struct nvkm_instobj base; 56 struct nv50_instmem *imem; 57 struct nvkm_memory *ram; 58 struct nvkm_vma *bar; 59#ifdef __NetBSD__ 60 bus_space_tag_t bst; 61 bus_space_handle_t bsh; 62#endif 63 refcount_t maps; 64 void *map; 65 struct list_head lru; 66}; 67 68static void 69nv50_instobj_wr32_slow(struct nvkm_memory *memory, u64 offset, u32 data) 70{ 71 struct nv50_instobj *iobj = nv50_instobj(memory); 72 struct nv50_instmem *imem = iobj->imem; 73 struct nvkm_device *device = imem->base.subdev.device; 74 u64 base = (nvkm_memory_addr(iobj->ram) + offset) & 0xffffff00000ULL; 75 u64 addr = (nvkm_memory_addr(iobj->ram) + offset) & 0x000000fffffULL; 76 unsigned long flags; 77 78 spin_lock_irqsave(&imem->base.lock, flags); 79 if (unlikely(imem->addr != base)) { 80 nvkm_wr32(device, 0x001700, base >> 16); 81 imem->addr = base; 82 } 83 nvkm_wr32(device, 0x700000 + addr, data); 84 spin_unlock_irqrestore(&imem->base.lock, flags); 85} 86 87static u32 88nv50_instobj_rd32_slow(struct nvkm_memory *memory, u64 offset) 89{ 90 struct nv50_instobj *iobj = nv50_instobj(memory); 91 struct nv50_instmem *imem = iobj->imem; 92 struct nvkm_device *device = imem->base.subdev.device; 93 u64 base = (nvkm_memory_addr(iobj->ram) + offset) & 0xffffff00000ULL; 94 u64 addr = (nvkm_memory_addr(iobj->ram) + offset) & 0x000000fffffULL; 95 u32 data; 96 unsigned long flags; 97 98 spin_lock_irqsave(&imem->base.lock, flags); 99 if (unlikely(imem->addr != base)) { 100 nvkm_wr32(device, 0x001700, base >> 16); 101 imem->addr = base; 102 } 103 data = nvkm_rd32(device, 0x700000 + addr); 104 spin_unlock_irqrestore(&imem->base.lock, flags); 105 return data; 106} 107 108static const struct nvkm_memory_ptrs 109nv50_instobj_slow = { 110 .rd32 = nv50_instobj_rd32_slow, 111 .wr32 = nv50_instobj_wr32_slow, 112}; 113 114static void 115nv50_instobj_wr32(struct nvkm_memory *memory, u64 offset, u32 data) 116{ 117#ifdef __NetBSD__ 118 struct nv50_instobj *iobj = nv50_instobj(memory); 119 bus_space_write_stream_4(iobj->bst, iobj->bsh, offset, data); 120#else 121 iowrite32_native(data, nv50_instobj(memory)->map + offset); 122#endif 123} 124 125static u32 126nv50_instobj_rd32(struct nvkm_memory *memory, u64 offset) 127{ 128#ifdef __NetBSD__ 129 struct nv50_instobj *iobj = nv50_instobj(memory); 130 return bus_space_read_stream_4(iobj->bst, iobj->bsh, offset); 131#else 132 return ioread32_native(nv50_instobj(memory)->map + offset); 133#endif 134} 135 136static const struct nvkm_memory_ptrs 137nv50_instobj_fast = { 138 .rd32 = nv50_instobj_rd32, 139 .wr32 = nv50_instobj_wr32, 140}; 141 142static void 143nv50_instobj_kmap(struct nv50_instobj *iobj, struct nvkm_vmm *vmm) 144{ 145 struct nv50_instmem *imem = iobj->imem; 146 struct nv50_instobj *eobj; 147 struct nvkm_memory *memory = &iobj->base.memory; 148 struct nvkm_subdev *subdev = &imem->base.subdev; 149 struct nvkm_device *device = subdev->device; 150 struct nvkm_vma *bar = NULL, *ebar; 151 u64 size = nvkm_memory_size(memory); 152 void *emap; 153#ifdef __NetBSD__ 154 bus_space_tag_t ebst; 155 bus_space_handle_t ebsh; 156 bus_size_t esize; 157#endif 158 int ret; 159 160 /* Attempt to allocate BAR2 address-space and map the object 161 * into it. The lock has to be dropped while doing this due 162 * to the possibility of recursion for page table allocation. 163 */ 164 mutex_unlock(&subdev->mutex); 165 while ((ret = nvkm_vmm_get(vmm, 12, size, &bar))) { 166 /* Evict unused mappings, and keep retrying until we either 167 * succeed,or there's no more objects left on the LRU. 168 */ 169 mutex_lock(&subdev->mutex); 170 eobj = list_first_entry_or_null(&imem->lru, typeof(*eobj), lru); 171 if (eobj) { 172 nvkm_debug(subdev, "evict %016"PRIx64" %016"PRIx64" @ %016"PRIx64"\n", 173 nvkm_memory_addr(&eobj->base.memory), 174 nvkm_memory_size(&eobj->base.memory), 175 eobj->bar->addr); 176 list_del_init(&eobj->lru); 177 ebar = eobj->bar; 178 eobj->bar = NULL; 179 emap = eobj->map; 180 eobj->map = NULL; 181#ifdef __NetBSD__ 182 ebst = eobj->bst; 183 ebsh = eobj->bsh; 184 esize = nvkm_memory_size(&eobj->base.memory); 185#endif 186 } 187 mutex_unlock(&subdev->mutex); 188 if (!eobj) 189 break; 190#ifdef __NetBSD__ 191 __USE(emap); 192 bus_space_unmap(ebst, ebsh, esize); 193#else 194 iounmap(emap); 195#endif 196 nvkm_vmm_put(vmm, &ebar); 197 } 198 199 if (ret == 0) 200 ret = nvkm_memory_map(memory, 0, vmm, bar, NULL, 0); 201 mutex_lock(&subdev->mutex); 202 if (ret || iobj->bar) { 203 /* We either failed, or another thread beat us. */ 204 mutex_unlock(&subdev->mutex); 205 nvkm_vmm_put(vmm, &bar); 206 mutex_lock(&subdev->mutex); 207 return; 208 } 209 210 /* Make the mapping visible to the host. */ 211 iobj->bar = bar; 212#ifdef __NetBSD__ 213 iobj->bst = device->func->resource_tag(device, 3); 214 if (bus_space_map(iobj->bst, 215 device->func->resource_addr(device, 3) + (u32)iobj->bar->addr, 216 size, BUS_SPACE_MAP_PREFETCHABLE|BUS_SPACE_MAP_LINEAR, 217 &iobj->bsh)) { 218 nvkm_warn(subdev, "PRAMIN ioremap failed\n"); 219 nvkm_vmm_put(vmm, &iobj->bar); 220 } 221 iobj->map = bus_space_vaddr(iobj->bst, iobj->bsh); 222#else 223 iobj->map = ioremap_wc(device->func->resource_addr(device, 3) + 224 (u32)iobj->bar->addr, size); 225 if (!iobj->map) { 226 nvkm_warn(subdev, "PRAMIN ioremap failed\n"); 227 nvkm_vmm_put(vmm, &iobj->bar); 228 } 229#endif 230} 231 232static int 233nv50_instobj_map(struct nvkm_memory *memory, u64 offset, struct nvkm_vmm *vmm, 234 struct nvkm_vma *vma, void *argv, u32 argc) 235{ 236 memory = nv50_instobj(memory)->ram; 237 return nvkm_memory_map(memory, offset, vmm, vma, argv, argc); 238} 239 240static void 241nv50_instobj_release(struct nvkm_memory *memory) 242{ 243 struct nv50_instobj *iobj = nv50_instobj(memory); 244 struct nv50_instmem *imem = iobj->imem; 245 struct nvkm_subdev *subdev = &imem->base.subdev; 246 247 wmb(); 248 nvkm_bar_flush(subdev->device->bar); 249 250 if (refcount_dec_and_mutex_lock(&iobj->maps, &subdev->mutex)) { 251 /* Add the now-unused mapping to the LRU instead of directly 252 * unmapping it here, in case we need to map it again later. 253 */ 254 if (likely(iobj->lru.next) && iobj->map) { 255 BUG_ON(!list_empty(&iobj->lru)); 256 list_add_tail(&iobj->lru, &imem->lru); 257 } 258 259 /* Switch back to NULL accessors when last map is gone. */ 260 iobj->base.memory.ptrs = NULL; 261 mutex_unlock(&subdev->mutex); 262 } 263} 264 265static void __iomem * 266nv50_instobj_acquire(struct nvkm_memory *memory) 267{ 268 struct nv50_instobj *iobj = nv50_instobj(memory); 269 struct nvkm_instmem *imem = &iobj->imem->base; 270 struct nvkm_vmm *vmm; 271 void __iomem *map = NULL; 272 273 /* Already mapped? */ 274 if (refcount_inc_not_zero(&iobj->maps)) 275 return iobj->map; 276 277 /* Take the lock, and re-check that another thread hasn't 278 * already mapped the object in the meantime. 279 */ 280 mutex_lock(&imem->subdev.mutex); 281 if (refcount_inc_not_zero(&iobj->maps)) { 282 mutex_unlock(&imem->subdev.mutex); 283 return iobj->map; 284 } 285 286 /* Attempt to get a direct CPU mapping of the object. */ 287 if ((vmm = nvkm_bar_bar2_vmm(imem->subdev.device))) { 288 if (!iobj->map) 289 nv50_instobj_kmap(iobj, vmm); 290 map = iobj->map; 291 } 292 293 if (!refcount_inc_not_zero(&iobj->maps)) { 294 /* Exclude object from eviction while it's being accessed. */ 295 if (likely(iobj->lru.next)) 296 list_del_init(&iobj->lru); 297 298 if (map) 299 iobj->base.memory.ptrs = &nv50_instobj_fast; 300 else 301 iobj->base.memory.ptrs = &nv50_instobj_slow; 302 refcount_set(&iobj->maps, 1); 303 } 304 305 mutex_unlock(&imem->subdev.mutex); 306 return map; 307} 308 309static void 310nv50_instobj_boot(struct nvkm_memory *memory, struct nvkm_vmm *vmm) 311{ 312 struct nv50_instobj *iobj = nv50_instobj(memory); 313 struct nvkm_instmem *imem = &iobj->imem->base; 314 315 /* Exclude bootstrapped objects (ie. the page tables for the 316 * instmem BAR itself) from eviction. 317 */ 318 mutex_lock(&imem->subdev.mutex); 319 if (likely(iobj->lru.next)) { 320 list_del_init(&iobj->lru); 321 iobj->lru.next = NULL; 322 } 323 324 nv50_instobj_kmap(iobj, vmm); 325 nvkm_instmem_boot(imem); 326 mutex_unlock(&imem->subdev.mutex); 327} 328 329static u64 330nv50_instobj_size(struct nvkm_memory *memory) 331{ 332 return nvkm_memory_size(nv50_instobj(memory)->ram); 333} 334 335static u64 336nv50_instobj_addr(struct nvkm_memory *memory) 337{ 338 return nvkm_memory_addr(nv50_instobj(memory)->ram); 339} 340 341static u64 342nv50_instobj_bar2(struct nvkm_memory *memory) 343{ 344 struct nv50_instobj *iobj = nv50_instobj(memory); 345 u64 addr = ~0ULL; 346 if (nv50_instobj_acquire(&iobj->base.memory)) { 347 iobj->lru.next = NULL; /* Exclude from eviction. */ 348 addr = iobj->bar->addr; 349 } 350 nv50_instobj_release(&iobj->base.memory); 351 return addr; 352} 353 354static enum nvkm_memory_target 355nv50_instobj_target(struct nvkm_memory *memory) 356{ 357 return nvkm_memory_target(nv50_instobj(memory)->ram); 358} 359 360static void * 361nv50_instobj_dtor(struct nvkm_memory *memory) 362{ 363 struct nv50_instobj *iobj = nv50_instobj(memory); 364 struct nvkm_instmem *imem = &iobj->imem->base; 365 struct nvkm_vma *bar; 366 void *map = map; 367 368 mutex_lock(&imem->subdev.mutex); 369 if (likely(iobj->lru.next)) 370 list_del(&iobj->lru); 371 map = iobj->map; 372 bar = iobj->bar; 373 mutex_unlock(&imem->subdev.mutex); 374 375 if (map) { 376 struct nvkm_vmm *vmm = nvkm_bar_bar2_vmm(imem->subdev.device); 377#ifdef __NetBSD__ 378 bus_space_unmap(iobj->bst, iobj->bsh, 379 nvkm_memory_size(&iobj->base.memory)); 380 iobj->map = NULL; 381#else 382 iounmap(map); 383#endif 384 if (likely(vmm)) /* Can be NULL during BAR destructor. */ 385 nvkm_vmm_put(vmm, &bar); 386 } 387 388 nvkm_memory_unref(&iobj->ram); 389 nvkm_instobj_dtor(imem, &iobj->base); 390 return iobj; 391} 392 393static const struct nvkm_memory_func 394nv50_instobj_func = { 395 .dtor = nv50_instobj_dtor, 396 .target = nv50_instobj_target, 397 .bar2 = nv50_instobj_bar2, 398 .addr = nv50_instobj_addr, 399 .size = nv50_instobj_size, 400 .boot = nv50_instobj_boot, 401 .acquire = nv50_instobj_acquire, 402 .release = nv50_instobj_release, 403 .map = nv50_instobj_map, 404}; 405 406static int 407nv50_instobj_new(struct nvkm_instmem *base, u32 size, u32 align, bool zero, 408 struct nvkm_memory **pmemory) 409{ 410 struct nv50_instmem *imem = nv50_instmem(base); 411 struct nv50_instobj *iobj; 412 struct nvkm_device *device = imem->base.subdev.device; 413 u8 page = max(order_base_2(align), 12); 414 415 if (!(iobj = kzalloc(sizeof(*iobj), GFP_KERNEL))) 416 return -ENOMEM; 417 *pmemory = &iobj->base.memory; 418 419 nvkm_instobj_ctor(&nv50_instobj_func, &imem->base, &iobj->base); 420 iobj->imem = imem; 421 refcount_set(&iobj->maps, 0); 422 INIT_LIST_HEAD(&iobj->lru); 423 424 return nvkm_ram_get(device, 0, 1, page, size, true, true, &iobj->ram); 425} 426 427/****************************************************************************** 428 * instmem subdev implementation 429 *****************************************************************************/ 430 431static void 432nv50_instmem_fini(struct nvkm_instmem *base) 433{ 434 nv50_instmem(base)->addr = ~0ULL; 435} 436 437static const struct nvkm_instmem_func 438nv50_instmem = { 439 .fini = nv50_instmem_fini, 440 .memory_new = nv50_instobj_new, 441 .zero = false, 442}; 443 444int 445nv50_instmem_new(struct nvkm_device *device, int index, 446 struct nvkm_instmem **pimem) 447{ 448 struct nv50_instmem *imem; 449 450 if (!(imem = kzalloc(sizeof(*imem), GFP_KERNEL))) 451 return -ENOMEM; 452 nvkm_instmem_ctor(&nv50_instmem, device, index, &imem->base); 453 INIT_LIST_HEAD(&imem->lru); 454 *pimem = &imem->base; 455 return 0; 456} 457