diff options
Diffstat (limited to 'drivers/gpu/nvgpu/gk20a')
-rw-r--r-- | drivers/gpu/nvgpu/gk20a/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpu/nvgpu/gk20a/semaphore_gk20a.c | 191 | ||||
-rw-r--r-- | drivers/gpu/nvgpu/gk20a/semaphore_gk20a.h | 97 |
3 files changed, 289 insertions, 0 deletions
diff --git a/drivers/gpu/nvgpu/gk20a/Makefile b/drivers/gpu/nvgpu/gk20a/Makefile index 81ae027e..e5eb817d 100644 --- a/drivers/gpu/nvgpu/gk20a/Makefile +++ b/drivers/gpu/nvgpu/gk20a/Makefile | |||
@@ -19,6 +19,7 @@ nvgpu-y := \ | |||
19 | mm_gk20a.o \ | 19 | mm_gk20a.o \ |
20 | pmu_gk20a.o \ | 20 | pmu_gk20a.o \ |
21 | priv_ring_gk20a.o \ | 21 | priv_ring_gk20a.o \ |
22 | semaphore_gk20a.o \ | ||
22 | clk_gk20a.o \ | 23 | clk_gk20a.o \ |
23 | therm_gk20a.o \ | 24 | therm_gk20a.o \ |
24 | gr_ctx_gk20a_sim.o \ | 25 | gr_ctx_gk20a_sim.o \ |
diff --git a/drivers/gpu/nvgpu/gk20a/semaphore_gk20a.c b/drivers/gpu/nvgpu/gk20a/semaphore_gk20a.c new file mode 100644 index 00000000..55fa0e32 --- /dev/null +++ b/drivers/gpu/nvgpu/gk20a/semaphore_gk20a.c | |||
@@ -0,0 +1,191 @@ | |||
1 | /* | ||
2 | * drivers/video/tegra/host/gk20a/semaphore_gk20a.c | ||
3 | * | ||
4 | * GK20A Semaphores | ||
5 | * | ||
6 | * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify it | ||
9 | * under the terms and conditions of the GNU General Public License, | ||
10 | * version 2, as published by the Free Software Foundation. | ||
11 | * | ||
12 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
14 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
15 | * more details. | ||
16 | */ | ||
17 | |||
18 | #include "semaphore_gk20a.h" | ||
19 | #include <linux/dma-mapping.h> | ||
20 | #include <linux/slab.h> | ||
21 | #include "gk20a.h" | ||
22 | #include "mm_gk20a.h" | ||
23 | |||
24 | static const int SEMAPHORE_SIZE = 16; | ||
25 | |||
26 | struct gk20a_semaphore_pool *gk20a_semaphore_pool_alloc(struct device *d, | ||
27 | const char *unique_name, size_t capacity) | ||
28 | { | ||
29 | struct gk20a_semaphore_pool *p; | ||
30 | p = kzalloc(sizeof(*p), GFP_KERNEL); | ||
31 | if (!p) | ||
32 | return NULL; | ||
33 | |||
34 | kref_init(&p->ref); | ||
35 | INIT_LIST_HEAD(&p->maps); | ||
36 | mutex_init(&p->maps_mutex); | ||
37 | p->dev = d; | ||
38 | |||
39 | /* Alloc one 4k page of semaphore per channel. */ | ||
40 | p->size = roundup(capacity * SEMAPHORE_SIZE, PAGE_SIZE); | ||
41 | p->cpu_va = dma_alloc_coherent(d, p->size, &p->iova, GFP_KERNEL); | ||
42 | if (!p->cpu_va) | ||
43 | goto clean_up; | ||
44 | if (gk20a_get_sgtable(d, &p->sgt, p->cpu_va, p->iova, p->size)) | ||
45 | goto clean_up; | ||
46 | |||
47 | if (gk20a_allocator_init(&p->alloc, unique_name, 0, | ||
48 | p->size, SEMAPHORE_SIZE)) | ||
49 | goto clean_up; | ||
50 | |||
51 | gk20a_dbg_info("cpuva=%p iova=%llx phys=%llx", p->cpu_va, | ||
52 | (u64)sg_dma_address(p->sgt->sgl), (u64)sg_phys(p->sgt->sgl)); | ||
53 | return p; | ||
54 | clean_up: | ||
55 | if (p->cpu_va) | ||
56 | dma_free_coherent(d, p->size, p->cpu_va, p->iova); | ||
57 | if (p->sgt) | ||
58 | gk20a_free_sgtable(&p->sgt); | ||
59 | kfree(p); | ||
60 | return NULL; | ||
61 | } | ||
62 | |||
63 | static void gk20a_semaphore_pool_free(struct kref *ref) | ||
64 | { | ||
65 | struct gk20a_semaphore_pool *p = | ||
66 | container_of(ref, struct gk20a_semaphore_pool, ref); | ||
67 | mutex_lock(&p->maps_mutex); | ||
68 | WARN_ON(!list_empty(&p->maps)); | ||
69 | mutex_unlock(&p->maps_mutex); | ||
70 | gk20a_free_sgtable(&p->sgt); | ||
71 | dma_free_coherent(p->dev, p->size, p->cpu_va, p->iova); | ||
72 | gk20a_allocator_destroy(&p->alloc); | ||
73 | kfree(p); | ||
74 | } | ||
75 | |||
76 | static void gk20a_semaphore_pool_get(struct gk20a_semaphore_pool *p) | ||
77 | { | ||
78 | kref_get(&p->ref); | ||
79 | } | ||
80 | |||
81 | void gk20a_semaphore_pool_put(struct gk20a_semaphore_pool *p) | ||
82 | { | ||
83 | kref_put(&p->ref, gk20a_semaphore_pool_free); | ||
84 | } | ||
85 | |||
86 | static struct gk20a_semaphore_pool_map * | ||
87 | gk20a_semaphore_pool_find_map(struct gk20a_semaphore_pool *p, | ||
88 | struct vm_gk20a *vm) | ||
89 | { | ||
90 | struct gk20a_semaphore_pool_map *map, *found = NULL; | ||
91 | mutex_lock(&p->maps_mutex); | ||
92 | list_for_each_entry(map, &p->maps, list) { | ||
93 | if (map->vm == vm) { | ||
94 | found = map; | ||
95 | break; | ||
96 | } | ||
97 | } | ||
98 | mutex_unlock(&p->maps_mutex); | ||
99 | return found; | ||
100 | } | ||
101 | |||
102 | int gk20a_semaphore_pool_map(struct gk20a_semaphore_pool *p, | ||
103 | struct vm_gk20a *vm, | ||
104 | enum gk20a_mem_rw_flag rw_flag) | ||
105 | { | ||
106 | struct gk20a_semaphore_pool_map *map; | ||
107 | |||
108 | WARN_ON(gk20a_semaphore_pool_find_map(p, vm)); | ||
109 | map = kzalloc(sizeof(*map), GFP_KERNEL); | ||
110 | if (!map) | ||
111 | return -ENOMEM; | ||
112 | map->vm = vm; | ||
113 | map->rw_flag = rw_flag; | ||
114 | map->gpu_va = gk20a_gmmu_map(vm, &p->sgt, p->size, | ||
115 | 0/*uncached*/, rw_flag); | ||
116 | if (!map->gpu_va) { | ||
117 | kfree(map); | ||
118 | return -ENOMEM; | ||
119 | } | ||
120 | mutex_lock(&p->maps_mutex); | ||
121 | list_add(&map->list, &p->maps); | ||
122 | mutex_unlock(&p->maps_mutex); | ||
123 | return 0; | ||
124 | } | ||
125 | |||
126 | void gk20a_semaphore_pool_unmap(struct gk20a_semaphore_pool *p, | ||
127 | struct vm_gk20a *vm) | ||
128 | { | ||
129 | struct gk20a_semaphore_pool_map *map = | ||
130 | gk20a_semaphore_pool_find_map(p, vm); | ||
131 | if (!map) | ||
132 | return; | ||
133 | gk20a_gmmu_unmap(vm, map->gpu_va, p->size, map->rw_flag); | ||
134 | list_del(&map->list); | ||
135 | kfree(map); | ||
136 | } | ||
137 | |||
138 | u64 gk20a_semaphore_pool_gpu_va(struct gk20a_semaphore_pool *p, | ||
139 | struct vm_gk20a *vm) | ||
140 | { | ||
141 | struct gk20a_semaphore_pool_map *map = | ||
142 | gk20a_semaphore_pool_find_map(p, vm); | ||
143 | if (!map) | ||
144 | return 0; | ||
145 | return map->gpu_va; | ||
146 | } | ||
147 | |||
148 | struct gk20a_semaphore *gk20a_semaphore_alloc(struct gk20a_semaphore_pool *pool) | ||
149 | { | ||
150 | struct gk20a_semaphore *s; | ||
151 | |||
152 | s = kzalloc(sizeof(*s), GFP_KERNEL); | ||
153 | if (!s) | ||
154 | return NULL; | ||
155 | |||
156 | if (pool->alloc.alloc(&pool->alloc, &s->offset, SEMAPHORE_SIZE)) { | ||
157 | gk20a_err(pool->dev, "failed to allocate semaphore"); | ||
158 | kfree(s); | ||
159 | return NULL; | ||
160 | } | ||
161 | |||
162 | gk20a_semaphore_pool_get(pool); | ||
163 | s->pool = pool; | ||
164 | |||
165 | kref_init(&s->ref); | ||
166 | s->value = (volatile u32 *)((uintptr_t)pool->cpu_va + s->offset); | ||
167 | *s->value = 0; /* Initially acquired. */ | ||
168 | gk20a_dbg_info("created semaphore offset=%d, value_cpu=%p, value=%d", | ||
169 | s->offset, s->value, *s->value); | ||
170 | return s; | ||
171 | } | ||
172 | |||
173 | static void gk20a_semaphore_free(struct kref *ref) | ||
174 | { | ||
175 | struct gk20a_semaphore *s = | ||
176 | container_of(ref, struct gk20a_semaphore, ref); | ||
177 | |||
178 | s->pool->alloc.free(&s->pool->alloc, s->offset, SEMAPHORE_SIZE); | ||
179 | gk20a_semaphore_pool_put(s->pool); | ||
180 | kfree(s); | ||
181 | } | ||
182 | |||
183 | void gk20a_semaphore_put(struct gk20a_semaphore *s) | ||
184 | { | ||
185 | kref_put(&s->ref, gk20a_semaphore_free); | ||
186 | } | ||
187 | |||
188 | void gk20a_semaphore_get(struct gk20a_semaphore *s) | ||
189 | { | ||
190 | kref_get(&s->ref); | ||
191 | } | ||
diff --git a/drivers/gpu/nvgpu/gk20a/semaphore_gk20a.h b/drivers/gpu/nvgpu/gk20a/semaphore_gk20a.h new file mode 100644 index 00000000..214db398 --- /dev/null +++ b/drivers/gpu/nvgpu/gk20a/semaphore_gk20a.h | |||
@@ -0,0 +1,97 @@ | |||
1 | /* | ||
2 | * drivers/video/tegra/host/gk20a/semaphore_gk20a.h | ||
3 | * | ||
4 | * GK20A Semaphores | ||
5 | * | ||
6 | * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify it | ||
9 | * under the terms and conditions of the GNU General Public License, | ||
10 | * version 2, as published by the Free Software Foundation. | ||
11 | * | ||
12 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
14 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
15 | * more details. | ||
16 | */ | ||
17 | |||
18 | #ifndef _GK20A_SEMAPHORE_H_ | ||
19 | #define _GK20A_SEMAPHORE_H_ | ||
20 | |||
21 | #include <linux/kref.h> | ||
22 | #include "gk20a_allocator.h" | ||
23 | #include "mm_gk20a.h" | ||
24 | |||
25 | /* A memory pool for holding semaphores. */ | ||
26 | struct gk20a_semaphore_pool { | ||
27 | void *cpu_va; | ||
28 | dma_addr_t iova; | ||
29 | size_t size; | ||
30 | struct device *dev; | ||
31 | struct sg_table *sgt; | ||
32 | struct list_head maps; | ||
33 | struct mutex maps_mutex; | ||
34 | struct kref ref; | ||
35 | struct gk20a_allocator alloc; | ||
36 | }; | ||
37 | |||
38 | /* A semaphore pool can be mapped to multiple GPU address spaces. */ | ||
39 | struct gk20a_semaphore_pool_map { | ||
40 | u64 gpu_va; | ||
41 | enum gk20a_mem_rw_flag rw_flag; | ||
42 | struct vm_gk20a *vm; | ||
43 | struct list_head list; | ||
44 | }; | ||
45 | |||
46 | /* A semaphore that lives inside a semaphore pool. */ | ||
47 | struct gk20a_semaphore { | ||
48 | struct gk20a_semaphore_pool *pool; | ||
49 | u32 offset; /* byte offset within pool */ | ||
50 | struct kref ref; | ||
51 | /* value is a pointer within the pool's coherent cpu_va. | ||
52 | * It is shared between CPU and GPU, hence volatile. */ | ||
53 | volatile u32 *value; /* 0=acquired, 1=released */ | ||
54 | }; | ||
55 | |||
56 | /* Create a semaphore pool that can hold at most 'capacity' semaphores. */ | ||
57 | struct gk20a_semaphore_pool * | ||
58 | gk20a_semaphore_pool_alloc(struct device *, const char *unique_name, | ||
59 | size_t capacity); | ||
60 | void gk20a_semaphore_pool_put(struct gk20a_semaphore_pool *); | ||
61 | int gk20a_semaphore_pool_map(struct gk20a_semaphore_pool *, | ||
62 | struct vm_gk20a *, | ||
63 | enum gk20a_mem_rw_flag); | ||
64 | void gk20a_semaphore_pool_unmap(struct gk20a_semaphore_pool *, | ||
65 | struct vm_gk20a *); | ||
66 | u64 gk20a_semaphore_pool_gpu_va(struct gk20a_semaphore_pool *, | ||
67 | struct vm_gk20a *); | ||
68 | |||
69 | /* Allocate a semaphore from the semaphore pool. The newly allocated | ||
70 | * semaphore will be in acquired state (value=0). */ | ||
71 | struct gk20a_semaphore * | ||
72 | gk20a_semaphore_alloc(struct gk20a_semaphore_pool *); | ||
73 | void gk20a_semaphore_put(struct gk20a_semaphore *); | ||
74 | void gk20a_semaphore_get(struct gk20a_semaphore *); | ||
75 | |||
76 | static inline u64 gk20a_semaphore_gpu_va(struct gk20a_semaphore *s, | ||
77 | struct vm_gk20a *vm) | ||
78 | { | ||
79 | return gk20a_semaphore_pool_gpu_va(s->pool, vm) + s->offset; | ||
80 | } | ||
81 | |||
82 | static inline bool gk20a_semaphore_is_acquired(struct gk20a_semaphore *s) | ||
83 | { | ||
84 | u32 v = *s->value; | ||
85 | |||
86 | /* When often block on value reaching a certain threshold. We must make | ||
87 | * sure that if we get unblocked, we haven't read anything too early. */ | ||
88 | smp_rmb(); | ||
89 | return v == 0; | ||
90 | } | ||
91 | |||
92 | static inline void gk20a_semaphore_release(struct gk20a_semaphore *s) | ||
93 | { | ||
94 | smp_wmb(); | ||
95 | *s->value = 1; | ||
96 | } | ||
97 | #endif | ||