/* * drivers/video/tegra/host/gk20a/semaphore_gk20a.c * * GK20A Semaphores * * Copyright (c) 2014-2015, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. */ #include "semaphore_gk20a.h" #include #include #include "gk20a.h" #include "mm_gk20a.h" static const int SEMAPHORE_SIZE = 16; struct gk20a_semaphore_pool *gk20a_semaphore_pool_alloc(struct device *d, const char *unique_name, size_t capacity) { struct gk20a_semaphore_pool *p; p = kzalloc(sizeof(*p), GFP_KERNEL); if (!p) return NULL; kref_init(&p->ref); INIT_LIST_HEAD(&p->maps); mutex_init(&p->maps_mutex); p->dev = d; /* Alloc one 4k page of semaphore per channel. */ p->size = roundup(capacity * SEMAPHORE_SIZE, PAGE_SIZE); p->cpu_va = dma_alloc_coherent(d, p->size, &p->iova, GFP_KERNEL); if (!p->cpu_va) goto clean_up; if (gk20a_get_sgtable(d, &p->sgt, p->cpu_va, p->iova, p->size)) goto clean_up; /* Sacrifice one semaphore in the name of returning error codes. */ if (gk20a_allocator_init(&p->alloc, unique_name, SEMAPHORE_SIZE, p->size - SEMAPHORE_SIZE, SEMAPHORE_SIZE)) goto clean_up; gk20a_dbg_info("cpuva=%p iova=%llx phys=%llx", p->cpu_va, (u64)sg_dma_address(p->sgt->sgl), (u64)sg_phys(p->sgt->sgl)); return p; clean_up: if (p->cpu_va) dma_free_coherent(d, p->size, p->cpu_va, p->iova); if (p->sgt) gk20a_free_sgtable(&p->sgt); kfree(p); return NULL; } static void gk20a_semaphore_pool_free(struct kref *ref) { struct gk20a_semaphore_pool *p = container_of(ref, struct gk20a_semaphore_pool, ref); mutex_lock(&p->maps_mutex); WARN_ON(!list_empty(&p->maps)); mutex_unlock(&p->maps_mutex); gk20a_free_sgtable(&p->sgt); dma_free_coherent(p->dev, p->size, p->cpu_va, p->iova); gk20a_allocator_destroy(&p->alloc); kfree(p); } static void gk20a_semaphore_pool_get(struct gk20a_semaphore_pool *p) { kref_get(&p->ref); } void gk20a_semaphore_pool_put(struct gk20a_semaphore_pool *p) { kref_put(&p->ref, gk20a_semaphore_pool_free); } static struct gk20a_semaphore_pool_map * gk20a_semaphore_pool_find_map_locked(struct gk20a_semaphore_pool *p, struct vm_gk20a *vm) { struct gk20a_semaphore_pool_map *map, *found = NULL; list_for_each_entry(map, &p->maps, list) { if (map->vm == vm) { found = map; break; } } return found; } int gk20a_semaphore_pool_map(struct gk20a_semaphore_pool *p, struct vm_gk20a *vm, enum gk20a_mem_rw_flag rw_flag) { struct gk20a_semaphore_pool_map *map; map = kzalloc(sizeof(*map), GFP_KERNEL); if (!map) return -ENOMEM; map->vm = vm; map->rw_flag = rw_flag; map->gpu_va = gk20a_gmmu_map(vm, &p->sgt, p->size, 0/*uncached*/, rw_flag, false); if (!map->gpu_va) { kfree(map); return -ENOMEM; } gk20a_vm_get(vm); mutex_lock(&p->maps_mutex); WARN_ON(gk20a_semaphore_pool_find_map_locked(p, vm)); list_add(&map->list, &p->maps); mutex_unlock(&p->maps_mutex); return 0; } void gk20a_semaphore_pool_unmap(struct gk20a_semaphore_pool *p, struct vm_gk20a *vm) { struct gk20a_semaphore_pool_map *map; WARN_ON(!vm); mutex_lock(&p->maps_mutex); map = gk20a_semaphore_pool_find_map_locked(p, vm); if (map) { gk20a_gmmu_unmap(vm, map->gpu_va, p->size, map->rw_flag); gk20a_vm_put(vm); list_del(&map->list); kfree(map); } mutex_unlock(&p->maps_mutex); } u64 gk20a_semaphore_pool_gpu_va(struct gk20a_semaphore_pool *p, struct vm_gk20a *vm) { struct gk20a_semaphore_pool_map *map; u64 gpu_va = 0; mutex_lock(&p->maps_mutex); map = gk20a_semaphore_pool_find_map_locked(p, vm); if (map) gpu_va = map->gpu_va; mutex_unlock(&p->maps_mutex); return gpu_va; } struct gk20a_semaphore *gk20a_semaphore_alloc(struct gk20a_semaphore_pool *pool) { struct gk20a_semaphore *s; s = kzalloc(sizeof(*s), GFP_KERNEL); if (!s) return NULL; s->offset = gk20a_balloc(&pool->alloc, SEMAPHORE_SIZE); if (!s->offset) { gk20a_err(pool->dev, "failed to allocate semaphore"); kfree(s); return NULL; } gk20a_semaphore_pool_get(pool); s->pool = pool; kref_init(&s->ref); s->value = (volatile u32 *)((uintptr_t)pool->cpu_va + s->offset); *s->value = 0; /* Initially acquired. */ gk20a_dbg_info("created semaphore offset=%d, value_cpu=%p, value=%d", s->offset, s->value, *s->value); return s; } static void gk20a_semaphore_free(struct kref *ref) { struct gk20a_semaphore *s = container_of(ref, struct gk20a_semaphore, ref); gk20a_bfree(&s->pool->alloc, s->offset); gk20a_semaphore_pool_put(s->pool); kfree(s); } void gk20a_semaphore_put(struct gk20a_semaphore *s) { kref_put(&s->ref, gk20a_semaphore_free); } void gk20a_semaphore_get(struct gk20a_semaphore *s) { kref_get(&s->ref); }