summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/nvgpu/common/linux/sim_gk20a.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/nvgpu/common/linux/sim_gk20a.c')
-rw-r--r--drivers/gpu/nvgpu/common/linux/sim_gk20a.c365
1 files changed, 365 insertions, 0 deletions
diff --git a/drivers/gpu/nvgpu/common/linux/sim_gk20a.c b/drivers/gpu/nvgpu/common/linux/sim_gk20a.c
new file mode 100644
index 00000000..6b148df6
--- /dev/null
+++ b/drivers/gpu/nvgpu/common/linux/sim_gk20a.c
@@ -0,0 +1,365 @@
1/*
2 * Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms and conditions of the GNU General Public License,
6 * version 2, as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11 * more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include <linux/io.h>
18#include <linux/highmem.h>
19#include <linux/platform_device.h>
20
21#include <nvgpu/log.h>
22#include <nvgpu/linux/vm.h>
23
24#include "gk20a/gk20a.h"
25#include "sim_gk20a.h"
26
27#include <nvgpu/hw/gk20a/hw_sim_gk20a.h>
28
29static inline void sim_writel(struct sim_gk20a_linux *sim_linux, u32 r, u32 v)
30{
31 writel(v, sim_linux->regs + r);
32}
33
34static inline u32 sim_readl(struct sim_gk20a_linux *sim_linux, u32 r)
35{
36 return readl(sim_linux->regs + r);
37}
38
39static void kunmap_and_free_iopage(void **kvaddr, struct page **page)
40{
41 if (*kvaddr) {
42 kunmap(*kvaddr);
43 *kvaddr = NULL;
44 }
45 if (*page) {
46 __free_page(*page);
47 *page = NULL;
48 }
49}
50
51static void gk20a_free_sim_support(struct gk20a *g)
52{
53 struct sim_gk20a_linux *sim_linux =
54 container_of(g->sim, struct sim_gk20a_linux, sim);
55 /* free sim mappings, bfrs */
56 kunmap_and_free_iopage(&sim_linux->send_bfr.kvaddr,
57 &sim_linux->send_bfr.page);
58
59 kunmap_and_free_iopage(&sim_linux->recv_bfr.kvaddr,
60 &sim_linux->recv_bfr.page);
61
62 kunmap_and_free_iopage(&sim_linux->msg_bfr.kvaddr,
63 &sim_linux->msg_bfr.page);
64}
65
66static void gk20a_remove_sim_support(struct sim_gk20a *s)
67{
68 struct gk20a *g = s->g;
69 struct sim_gk20a_linux *sim_linux =
70 container_of(g->sim, struct sim_gk20a_linux, sim);
71
72 if (sim_linux->regs)
73 sim_writel(sim_linux, sim_config_r(), sim_config_mode_disabled_v());
74 gk20a_free_sim_support(g);
75
76 if (sim_linux->regs) {
77 iounmap(sim_linux->regs);
78 sim_linux->regs = NULL;
79 }
80
81 nvgpu_kfree(g, sim_linux);
82 g->sim = NULL;
83}
84
85static int alloc_and_kmap_iopage(struct gk20a *g,
86 void **kvaddr,
87 u64 *phys,
88 struct page **page)
89{
90 int err = 0;
91 *page = alloc_page(GFP_KERNEL);
92
93 if (!*page) {
94 err = -ENOMEM;
95 nvgpu_err(g, "couldn't allocate io page");
96 goto fail;
97 }
98
99 *kvaddr = kmap(*page);
100 if (!*kvaddr) {
101 err = -ENOMEM;
102 nvgpu_err(g, "couldn't kmap io page");
103 goto fail;
104 }
105 *phys = page_to_phys(*page);
106 return 0;
107
108 fail:
109 kunmap_and_free_iopage(kvaddr, page);
110 return err;
111
112}
113
114static inline u32 sim_msg_header_size(void)
115{
116 return 24;/*TBD: fix the header to gt this from NV_VGPU_MSG_HEADER*/
117}
118
119static inline u32 *sim_msg_bfr(struct gk20a *g, u32 byte_offset)
120{
121 struct sim_gk20a_linux *sim_linux =
122 container_of(g->sim, struct sim_gk20a_linux, sim);
123 return (u32 *)(sim_linux->msg_bfr.kvaddr + byte_offset);
124}
125
126static inline u32 *sim_msg_hdr(struct gk20a *g, u32 byte_offset)
127{
128 return sim_msg_bfr(g, byte_offset); /*starts at 0*/
129}
130
131static inline u32 *sim_msg_param(struct gk20a *g, u32 byte_offset)
132{
133 /*starts after msg header/cmn*/
134 return sim_msg_bfr(g, byte_offset + sim_msg_header_size());
135}
136
137static inline void sim_write_hdr(struct gk20a *g, u32 func, u32 size)
138{
139 /*memset(g->sim->msg_bfr.kvaddr,0,min(PAGE_SIZE,size));*/
140 *sim_msg_hdr(g, sim_msg_signature_r()) = sim_msg_signature_valid_v();
141 *sim_msg_hdr(g, sim_msg_result_r()) = sim_msg_result_rpc_pending_v();
142 *sim_msg_hdr(g, sim_msg_spare_r()) = sim_msg_spare__init_v();
143 *sim_msg_hdr(g, sim_msg_function_r()) = func;
144 *sim_msg_hdr(g, sim_msg_length_r()) = size + sim_msg_header_size();
145}
146
147static inline u32 sim_escape_read_hdr_size(void)
148{
149 return 12; /*TBD: fix NV_VGPU_SIM_ESCAPE_READ_HEADER*/
150}
151
152static u32 *sim_send_ring_bfr(struct gk20a *g, u32 byte_offset)
153{
154 struct sim_gk20a_linux *sim_linux =
155 container_of(g->sim, struct sim_gk20a_linux, sim);
156 return (u32 *)(sim_linux->send_bfr.kvaddr + byte_offset);
157}
158
159static int rpc_send_message(struct gk20a *g)
160{
161 /* calculations done in units of u32s */
162 u32 send_base = sim_send_put_pointer_v(g->sim->send_ring_put) * 2;
163 u32 dma_offset = send_base + sim_dma_r()/sizeof(u32);
164 u32 dma_hi_offset = send_base + sim_dma_hi_r()/sizeof(u32);
165 struct sim_gk20a_linux *sim_linux =
166 container_of(g->sim, struct sim_gk20a_linux, sim);
167
168 *sim_send_ring_bfr(g, dma_offset*sizeof(u32)) =
169 sim_dma_target_phys_pci_coherent_f() |
170 sim_dma_status_valid_f() |
171 sim_dma_size_4kb_f() |
172 sim_dma_addr_lo_f(sim_linux->msg_bfr.phys >> PAGE_SHIFT);
173
174 *sim_send_ring_bfr(g, dma_hi_offset*sizeof(u32)) =
175 u64_hi32(sim_linux->msg_bfr.phys);
176
177 *sim_msg_hdr(g, sim_msg_sequence_r()) = g->sim->sequence_base++;
178
179 g->sim->send_ring_put = (g->sim->send_ring_put + 2 * sizeof(u32)) %
180 PAGE_SIZE;
181
182 __cpuc_flush_dcache_area(sim_linux->msg_bfr.kvaddr, PAGE_SIZE);
183 __cpuc_flush_dcache_area(sim_linux->send_bfr.kvaddr, PAGE_SIZE);
184 __cpuc_flush_dcache_area(sim_linux->recv_bfr.kvaddr, PAGE_SIZE);
185
186 /* Update the put pointer. This will trap into the host. */
187 sim_writel(sim_linux, sim_send_put_r(), g->sim->send_ring_put);
188
189 return 0;
190}
191
192static inline u32 *sim_recv_ring_bfr(struct gk20a *g, u32 byte_offset)
193{
194 struct sim_gk20a_linux *sim_linux =
195 container_of(g->sim, struct sim_gk20a_linux, sim);
196 return (u32 *)(sim_linux->recv_bfr.kvaddr + byte_offset);
197}
198
199static int rpc_recv_poll(struct gk20a *g)
200{
201 u64 recv_phys_addr;
202 struct sim_gk20a_linux *sim_linux =
203 container_of(g->sim, struct sim_gk20a_linux, sim);
204
205 /* XXX This read is not required (?) */
206 /*pVGpu->recv_ring_get = VGPU_REG_RD32(pGpu, NV_VGPU_RECV_GET);*/
207
208 /* Poll the recv ring get pointer in an infinite loop*/
209 do {
210 g->sim->recv_ring_put = sim_readl(sim_linux, sim_recv_put_r());
211 } while (g->sim->recv_ring_put == g->sim->recv_ring_get);
212
213 /* process all replies */
214 while (g->sim->recv_ring_put != g->sim->recv_ring_get) {
215 /* these are in u32 offsets*/
216 u32 dma_lo_offset =
217 sim_recv_put_pointer_v(g->sim->recv_ring_get)*2 + 0;
218 u32 dma_hi_offset = dma_lo_offset + 1;
219 u32 recv_phys_addr_lo = sim_dma_addr_lo_v(
220 *sim_recv_ring_bfr(g, dma_lo_offset*4));
221 u32 recv_phys_addr_hi = sim_dma_hi_addr_v(
222 *sim_recv_ring_bfr(g, dma_hi_offset*4));
223
224 recv_phys_addr = (u64)recv_phys_addr_hi << 32 |
225 (u64)recv_phys_addr_lo << PAGE_SHIFT;
226
227 if (recv_phys_addr != sim_linux->msg_bfr.phys) {
228 nvgpu_err(g, "%s Error in RPC reply",
229 __func__);
230 return -1;
231 }
232
233 /* Update GET pointer */
234 g->sim->recv_ring_get = (g->sim->recv_ring_get + 2*sizeof(u32)) %
235 PAGE_SIZE;
236
237 __cpuc_flush_dcache_area(sim_linux->msg_bfr.kvaddr, PAGE_SIZE);
238 __cpuc_flush_dcache_area(sim_linux->send_bfr.kvaddr, PAGE_SIZE);
239 __cpuc_flush_dcache_area(sim_linux->recv_bfr.kvaddr, PAGE_SIZE);
240
241 sim_writel(sim_linux, sim_recv_get_r(), g->sim->recv_ring_get);
242
243 g->sim->recv_ring_put = sim_readl(sim_linux, sim_recv_put_r());
244 }
245
246 return 0;
247}
248
249static int issue_rpc_and_wait(struct gk20a *g)
250{
251 int err;
252
253 err = rpc_send_message(g);
254 if (err) {
255 nvgpu_err(g, "%s failed rpc_send_message",
256 __func__);
257 return err;
258 }
259
260 err = rpc_recv_poll(g);
261 if (err) {
262 nvgpu_err(g, "%s failed rpc_recv_poll",
263 __func__);
264 return err;
265 }
266
267 /* Now check if RPC really succeeded */
268 if (*sim_msg_hdr(g, sim_msg_result_r()) != sim_msg_result_success_v()) {
269 nvgpu_err(g, "%s received failed status!",
270 __func__);
271 return -(*sim_msg_hdr(g, sim_msg_result_r()));
272 }
273 return 0;
274}
275
276static int gk20a_sim_esc_readl(struct gk20a *g, char *path, u32 index, u32 *data)
277{
278 int err;
279 size_t pathlen = strlen(path);
280 u32 data_offset;
281
282 sim_write_hdr(g, sim_msg_function_sim_escape_read_v(),
283 sim_escape_read_hdr_size());
284 *sim_msg_param(g, 0) = index;
285 *sim_msg_param(g, 4) = sizeof(u32);
286 data_offset = roundup(0xc + pathlen + 1, sizeof(u32));
287 *sim_msg_param(g, 8) = data_offset;
288 strcpy((char *)sim_msg_param(g, 0xc), path);
289
290 err = issue_rpc_and_wait(g);
291
292 if (!err)
293 memcpy(data, sim_msg_param(g, data_offset), sizeof(u32));
294 return err;
295}
296
297
298int gk20a_init_sim_support(struct gk20a *g)
299{
300 int err = 0;
301 u64 phys;
302 struct sim_gk20a_linux *sim_linux =
303 container_of(g->sim, struct sim_gk20a_linux, sim);
304
305 /* allocate sim event/msg buffers */
306 err = alloc_and_kmap_iopage(g, &sim_linux->send_bfr.kvaddr,
307 &sim_linux->send_bfr.phys,
308 &sim_linux->send_bfr.page);
309
310 err = err || alloc_and_kmap_iopage(g, &sim_linux->recv_bfr.kvaddr,
311 &sim_linux->recv_bfr.phys,
312 &sim_linux->recv_bfr.page);
313
314 err = err || alloc_and_kmap_iopage(g, &sim_linux->msg_bfr.kvaddr,
315 &sim_linux->msg_bfr.phys,
316 &sim_linux->msg_bfr.page);
317
318 if (!(sim_linux->send_bfr.kvaddr && sim_linux->recv_bfr.kvaddr &&
319 sim_linux->msg_bfr.kvaddr)) {
320 nvgpu_err(g, "couldn't allocate all sim buffers");
321 goto fail;
322 }
323
324 /*mark send ring invalid*/
325 sim_writel(sim_linux, sim_send_ring_r(), sim_send_ring_status_invalid_f());
326
327 /*read get pointer and make equal to put*/
328 g->sim->send_ring_put = sim_readl(sim_linux, sim_send_get_r());
329 sim_writel(sim_linux, sim_send_put_r(), g->sim->send_ring_put);
330
331 /*write send ring address and make it valid*/
332 phys = sim_linux->send_bfr.phys;
333 sim_writel(sim_linux, sim_send_ring_hi_r(),
334 sim_send_ring_hi_addr_f(u64_hi32(phys)));
335 sim_writel(sim_linux, sim_send_ring_r(),
336 sim_send_ring_status_valid_f() |
337 sim_send_ring_target_phys_pci_coherent_f() |
338 sim_send_ring_size_4kb_f() |
339 sim_send_ring_addr_lo_f(phys >> PAGE_SHIFT));
340
341 /*repeat for recv ring (but swap put,get as roles are opposite) */
342 sim_writel(sim_linux, sim_recv_ring_r(), sim_recv_ring_status_invalid_f());
343
344 /*read put pointer and make equal to get*/
345 g->sim->recv_ring_get = sim_readl(sim_linux, sim_recv_put_r());
346 sim_writel(sim_linux, sim_recv_get_r(), g->sim->recv_ring_get);
347
348 /*write send ring address and make it valid*/
349 phys = sim_linux->recv_bfr.phys;
350 sim_writel(sim_linux, sim_recv_ring_hi_r(),
351 sim_recv_ring_hi_addr_f(u64_hi32(phys)));
352 sim_writel(sim_linux, sim_recv_ring_r(),
353 sim_recv_ring_status_valid_f() |
354 sim_recv_ring_target_phys_pci_coherent_f() |
355 sim_recv_ring_size_4kb_f() |
356 sim_recv_ring_addr_lo_f(phys >> PAGE_SHIFT));
357
358 g->sim->remove_support = gk20a_remove_sim_support;
359 g->sim->esc_readl = gk20a_sim_esc_readl;
360 return 0;
361
362 fail:
363 gk20a_free_sim_support(g);
364 return err;
365} \ No newline at end of file