|
@@ -37,13 +37,14 @@ struct nv50_instmem {
|
|
|
/******************************************************************************
|
|
|
* instmem object implementation
|
|
|
*****************************************************************************/
|
|
|
-#define nv50_instobj(p) container_of((p), struct nv50_instobj, memory)
|
|
|
+#define nv50_instobj(p) container_of((p), struct nv50_instobj, base.memory)
|
|
|
|
|
|
struct nv50_instobj {
|
|
|
- struct nvkm_memory memory;
|
|
|
+ struct nvkm_instobj base;
|
|
|
struct nv50_instmem *imem;
|
|
|
struct nvkm_mem *mem;
|
|
|
struct nvkm_vma bar;
|
|
|
+ refcount_t maps;
|
|
|
void *map;
|
|
|
};
|
|
|
|
|
@@ -93,31 +94,59 @@ nv50_instobj_slow = {
|
|
|
.wr32 = nv50_instobj_wr32_slow,
|
|
|
};
|
|
|
|
|
|
+static void
|
|
|
+nv50_instobj_wr32(struct nvkm_memory *memory, u64 offset, u32 data)
|
|
|
+{
|
|
|
+ iowrite32_native(data, nv50_instobj(memory)->map + offset);
|
|
|
+}
|
|
|
+
|
|
|
+static u32
|
|
|
+nv50_instobj_rd32(struct nvkm_memory *memory, u64 offset)
|
|
|
+{
|
|
|
+ return ioread32_native(nv50_instobj(memory)->map + offset);
|
|
|
+}
|
|
|
+
|
|
|
+static const struct nvkm_memory_ptrs
|
|
|
+nv50_instobj_fast = {
|
|
|
+ .rd32 = nv50_instobj_rd32,
|
|
|
+ .wr32 = nv50_instobj_wr32,
|
|
|
+};
|
|
|
+
|
|
|
static void
|
|
|
nv50_instobj_kmap(struct nv50_instobj *iobj, struct nvkm_vmm *vmm)
|
|
|
{
|
|
|
- struct nvkm_memory *memory = &iobj->memory;
|
|
|
- struct nvkm_subdev *subdev = &iobj->imem->base.subdev;
|
|
|
+ struct nv50_instmem *imem = iobj->imem;
|
|
|
+ struct nvkm_memory *memory = &iobj->base.memory;
|
|
|
+ struct nvkm_subdev *subdev = &imem->base.subdev;
|
|
|
struct nvkm_device *device = subdev->device;
|
|
|
+ struct nvkm_vma bar = {};
|
|
|
u64 size = nvkm_memory_size(memory);
|
|
|
- void __iomem *map;
|
|
|
int ret;
|
|
|
|
|
|
- iobj->map = ERR_PTR(-ENOMEM);
|
|
|
-
|
|
|
- ret = nvkm_vm_get(vmm, size, 12, NV_MEM_ACCESS_RW, &iobj->bar);
|
|
|
- if (ret == 0) {
|
|
|
- map = ioremap(device->func->resource_addr(device, 3) +
|
|
|
- (u32)iobj->bar.offset, size);
|
|
|
- if (map) {
|
|
|
- nvkm_memory_map(memory, &iobj->bar, 0);
|
|
|
- iobj->map = map;
|
|
|
- } else {
|
|
|
- nvkm_warn(subdev, "PRAMIN ioremap failed\n");
|
|
|
- nvkm_vm_put(&iobj->bar);
|
|
|
- }
|
|
|
- } else {
|
|
|
- nvkm_warn(subdev, "PRAMIN exhausted\n");
|
|
|
+ /* Attempt to allocate BAR2 address-space and map the object
|
|
|
+ * into it. The lock has to be dropped while doing this due
|
|
|
+ * to the possibility of recursion for page table allocation.
|
|
|
+ */
|
|
|
+ mutex_unlock(&subdev->mutex);
|
|
|
+ ret = nvkm_vm_get(vmm, size, 12, NV_MEM_ACCESS_RW, &bar);
|
|
|
+ if (ret == 0)
|
|
|
+ nvkm_memory_map(memory, &bar, 0);
|
|
|
+ mutex_lock(&subdev->mutex);
|
|
|
+ if (ret || iobj->bar.node) {
|
|
|
+ /* We either failed, or another thread beat us. */
|
|
|
+ mutex_unlock(&subdev->mutex);
|
|
|
+ nvkm_vm_put(&bar);
|
|
|
+ mutex_lock(&subdev->mutex);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Make the mapping visible to the host. */
|
|
|
+ iobj->bar = bar;
|
|
|
+ iobj->map = ioremap(device->func->resource_addr(device, 3) +
|
|
|
+ (u32)iobj->bar.offset, size);
|
|
|
+ if (!iobj->map) {
|
|
|
+ nvkm_warn(subdev, "PRAMIN ioremap failed\n");
|
|
|
+ nvkm_vm_put(&iobj->bar);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -131,28 +160,66 @@ nv50_instobj_map(struct nvkm_memory *memory, struct nvkm_vma *vma, u64 offset)
|
|
|
static void
|
|
|
nv50_instobj_release(struct nvkm_memory *memory)
|
|
|
{
|
|
|
+ struct nv50_instobj *iobj = nv50_instobj(memory);
|
|
|
+ struct nv50_instmem *imem = iobj->imem;
|
|
|
+ struct nvkm_subdev *subdev = &imem->base.subdev;
|
|
|
+
|
|
|
+ nvkm_bar_flush(subdev->device->bar);
|
|
|
+
|
|
|
+ if (refcount_dec_and_mutex_lock(&iobj->maps, &subdev->mutex)) {
|
|
|
+ /* Switch back to NULL accessors when last map is gone. */
|
|
|
+ iobj->base.memory.ptrs = &nv50_instobj_slow;
|
|
|
+ mutex_unlock(&subdev->mutex);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
static void __iomem *
|
|
|
nv50_instobj_acquire(struct nvkm_memory *memory)
|
|
|
{
|
|
|
struct nv50_instobj *iobj = nv50_instobj(memory);
|
|
|
- struct nv50_instmem *imem = iobj->imem;
|
|
|
- struct nvkm_vm *vm;
|
|
|
+ struct nvkm_instmem *imem = &iobj->imem->base;
|
|
|
+ struct nvkm_vmm *vmm;
|
|
|
+ void __iomem *map = NULL;
|
|
|
|
|
|
- if (!iobj->map && (vm = nvkm_bar_bar2_vmm(imem->base.subdev.device)))
|
|
|
- nv50_instobj_kmap(iobj, vm);
|
|
|
- if (!IS_ERR_OR_NULL(iobj->map))
|
|
|
+ /* Already mapped? */
|
|
|
+ if (refcount_inc_not_zero(&iobj->maps))
|
|
|
return iobj->map;
|
|
|
|
|
|
- return NULL;
|
|
|
+ /* Take the lock, and re-check that another thread hasn't
|
|
|
+ * already mapped the object in the meantime.
|
|
|
+ */
|
|
|
+ mutex_lock(&imem->subdev.mutex);
|
|
|
+ if (refcount_inc_not_zero(&iobj->maps)) {
|
|
|
+ mutex_unlock(&imem->subdev.mutex);
|
|
|
+ return iobj->map;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Attempt to get a direct CPU mapping of the object. */
|
|
|
+ if (!iobj->map && (vmm = nvkm_bar_bar2_vmm(imem->subdev.device)))
|
|
|
+ nv50_instobj_kmap(iobj, vmm);
|
|
|
+ map = iobj->map;
|
|
|
+
|
|
|
+ if (!refcount_inc_not_zero(&iobj->maps)) {
|
|
|
+ if (map)
|
|
|
+ iobj->base.memory.ptrs = &nv50_instobj_fast;
|
|
|
+ else
|
|
|
+ iobj->base.memory.ptrs = &nv50_instobj_slow;
|
|
|
+ refcount_inc(&iobj->maps);
|
|
|
+ }
|
|
|
+
|
|
|
+ mutex_unlock(&imem->subdev.mutex);
|
|
|
+ return map;
|
|
|
}
|
|
|
|
|
|
static void
|
|
|
nv50_instobj_boot(struct nvkm_memory *memory, struct nvkm_vmm *vmm)
|
|
|
{
|
|
|
struct nv50_instobj *iobj = nv50_instobj(memory);
|
|
|
+ struct nvkm_instmem *imem = &iobj->imem->base;
|
|
|
+
|
|
|
+ mutex_lock(&imem->subdev.mutex);
|
|
|
nv50_instobj_kmap(iobj, vmm);
|
|
|
+ mutex_unlock(&imem->subdev.mutex);
|
|
|
}
|
|
|
|
|
|
static u64
|
|
@@ -177,12 +244,14 @@ static void *
|
|
|
nv50_instobj_dtor(struct nvkm_memory *memory)
|
|
|
{
|
|
|
struct nv50_instobj *iobj = nv50_instobj(memory);
|
|
|
- struct nvkm_ram *ram = iobj->imem->base.subdev.device->fb->ram;
|
|
|
- if (!IS_ERR_OR_NULL(iobj->map)) {
|
|
|
+ struct nvkm_instmem *imem = &iobj->imem->base;
|
|
|
+ struct nvkm_ram *ram = imem->subdev.device->fb->ram;
|
|
|
+ if (iobj->map) {
|
|
|
iounmap(iobj->map);
|
|
|
nvkm_vm_put(&iobj->bar);
|
|
|
}
|
|
|
ram->func->put(ram, &iobj->mem);
|
|
|
+ nvkm_instobj_dtor(imem, &iobj->base);
|
|
|
return iobj;
|
|
|
}
|
|
|
|
|
@@ -209,11 +278,12 @@ nv50_instobj_new(struct nvkm_instmem *base, u32 size, u32 align, bool zero,
|
|
|
|
|
|
if (!(iobj = kzalloc(sizeof(*iobj), GFP_KERNEL)))
|
|
|
return -ENOMEM;
|
|
|
- *pmemory = &iobj->memory;
|
|
|
+ *pmemory = &iobj->base.memory;
|
|
|
|
|
|
- nvkm_memory_ctor(&nv50_instobj_func, &iobj->memory);
|
|
|
- iobj->memory.ptrs = &nv50_instobj_slow;
|
|
|
+ nvkm_instobj_ctor(&nv50_instobj_func, &imem->base, &iobj->base);
|
|
|
+ iobj->base.memory.ptrs = &nv50_instobj_slow;
|
|
|
iobj->imem = imem;
|
|
|
+ refcount_set(&iobj->maps, 0);
|
|
|
|
|
|
size = max((size + 4095) & ~4095, (u32)4096);
|
|
|
align = max((align + 4095) & ~4095, (u32)4096);
|
|
@@ -240,7 +310,7 @@ static const struct nvkm_instmem_func
|
|
|
nv50_instmem = {
|
|
|
.fini = nv50_instmem_fini,
|
|
|
.memory_new = nv50_instobj_new,
|
|
|
- .persistent = false,
|
|
|
+ .persistent = true,
|
|
|
.zero = false,
|
|
|
};
|
|
|
|