summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/nvgpu/common/linux/sim_pci.c
diff options
context:
space:
mode:
authorTerje Bergstrom <tbergstrom@nvidia.com>2018-04-06 12:46:55 -0400
committermobile promotions <svcmobile_promotions@nvidia.com>2018-04-22 12:24:44 -0400
commitc918c42a4a3651f757c6966aead4b07eb4b56697 (patch)
tree09a13f857e14f58aed1e18d0dc9c7d61722936b9 /drivers/gpu/nvgpu/common/linux/sim_pci.c
parent32f6ffee1379f1458d35c8d74e8516f96a3e390e (diff)
gpu: nvgpu: Implement dGPU simulation support
Implement support for dGPU fmodel. The message protocol is slightly different and accessed via BAR0 aperture. JIRA NVGPUT-41 Change-Id: Ide3c52a751530f520854965c1eba19fa8339a315 Signed-off-by: Terje Bergstrom <tbergstrom@nvidia.com> Reviewed-on: https://git-master.nvidia.com/r/1694963 Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com> Tested-by: mobile promotions <svcmobile_promotions@nvidia.com>
Diffstat (limited to 'drivers/gpu/nvgpu/common/linux/sim_pci.c')
-rw-r--r--drivers/gpu/nvgpu/common/linux/sim_pci.c378
1 files changed, 378 insertions, 0 deletions
diff --git a/drivers/gpu/nvgpu/common/linux/sim_pci.c b/drivers/gpu/nvgpu/common/linux/sim_pci.c
new file mode 100644
index 00000000..889eeb24
--- /dev/null
+++ b/drivers/gpu/nvgpu/common/linux/sim_pci.c
@@ -0,0 +1,378 @@
1/*
2 * Copyright (c) 2017-2018, 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 "os_linux.h"
26#include "sim.h"
27#include "hw_sim_pci.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 32U;
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 *sim_msg_hdr(g, sim_msg_header_version_r()) =
140 sim_msg_header_version_major_tot_v() |
141 sim_msg_header_version_minor_tot_v();
142 *sim_msg_hdr(g, sim_msg_signature_r()) = sim_msg_signature_valid_v();
143 *sim_msg_hdr(g, sim_msg_result_r()) = sim_msg_result_rpc_pending_v();
144 *sim_msg_hdr(g, sim_msg_spare_r()) = sim_msg_spare__init_v();
145 *sim_msg_hdr(g, sim_msg_function_r()) = func;
146 *sim_msg_hdr(g, sim_msg_length_r()) = size + sim_msg_header_size();
147}
148
149static inline u32 sim_escape_read_hdr_size(void)
150{
151 return 12U;
152}
153
154static u32 *sim_send_ring_bfr(struct gk20a *g, u32 byte_offset)
155{
156 struct sim_gk20a_linux *sim_linux =
157 container_of(g->sim, struct sim_gk20a_linux, sim);
158 return (u32 *)(sim_linux->send_bfr.kvaddr + byte_offset);
159}
160
161static int rpc_send_message(struct gk20a *g)
162{
163 /* calculations done in units of u32s */
164 u32 send_base = sim_send_put_pointer_v(g->sim->send_ring_put) * 2;
165 u32 dma_offset = send_base + sim_dma_r()/sizeof(u32);
166 u32 dma_hi_offset = send_base + sim_dma_hi_r()/sizeof(u32);
167 struct sim_gk20a_linux *sim_linux =
168 container_of(g->sim, struct sim_gk20a_linux, sim);
169
170 *sim_send_ring_bfr(g, dma_offset*sizeof(u32)) =
171 sim_dma_target_phys_pci_coherent_f() |
172 sim_dma_status_valid_f() |
173 sim_dma_size_4kb_f() |
174 sim_dma_addr_lo_f(sim_linux->msg_bfr.phys >> PAGE_SHIFT);
175
176 *sim_send_ring_bfr(g, dma_hi_offset*sizeof(u32)) =
177 u64_hi32(sim_linux->msg_bfr.phys);
178
179 *sim_msg_hdr(g, sim_msg_sequence_r()) = g->sim->sequence_base++;
180
181 g->sim->send_ring_put = (g->sim->send_ring_put + 2 * sizeof(u32)) %
182 PAGE_SIZE;
183
184 /* Update the put pointer. This will trap into the host. */
185 sim_writel(sim_linux, sim_send_put_r(), g->sim->send_ring_put);
186
187 return 0;
188}
189
190static inline u32 *sim_recv_ring_bfr(struct gk20a *g, u32 byte_offset)
191{
192 struct sim_gk20a_linux *sim_linux =
193 container_of(g->sim, struct sim_gk20a_linux, sim);
194 return (u32 *)(sim_linux->recv_bfr.kvaddr + byte_offset);
195}
196
197static int rpc_recv_poll(struct gk20a *g)
198{
199 u64 recv_phys_addr;
200 struct sim_gk20a_linux *sim_linux =
201 container_of(g->sim, struct sim_gk20a_linux, sim);
202
203 /* Poll the recv ring get pointer in an infinite loop */
204 do {
205 g->sim->recv_ring_put = sim_readl(sim_linux, sim_recv_put_r());
206 } while (g->sim->recv_ring_put == g->sim->recv_ring_get);
207
208 /* process all replies */
209 while (g->sim->recv_ring_put != g->sim->recv_ring_get) {
210 /* these are in u32 offsets */
211 u32 dma_lo_offset =
212 sim_recv_put_pointer_v(g->sim->recv_ring_get)*2 + 0;
213 u32 dma_hi_offset = dma_lo_offset + 1;
214 u32 recv_phys_addr_lo = sim_dma_addr_lo_v(
215 *sim_recv_ring_bfr(g, dma_lo_offset*4));
216 u32 recv_phys_addr_hi = sim_dma_hi_addr_v(
217 *sim_recv_ring_bfr(g, dma_hi_offset*4));
218
219 recv_phys_addr = (u64)recv_phys_addr_hi << 32 |
220 (u64)recv_phys_addr_lo << PAGE_SHIFT;
221
222 if (recv_phys_addr != sim_linux->msg_bfr.phys) {
223 nvgpu_err(g, "Error in RPC reply");
224 return -EINVAL;
225 }
226
227 /* Update GET pointer */
228 g->sim->recv_ring_get = (g->sim->recv_ring_get + 2*sizeof(u32)) %
229 PAGE_SIZE;
230
231 sim_writel(sim_linux, sim_recv_get_r(), g->sim->recv_ring_get);
232
233 g->sim->recv_ring_put = sim_readl(sim_linux, sim_recv_put_r());
234 }
235
236 return 0;
237}
238
239static int issue_rpc_and_wait(struct gk20a *g)
240{
241 int err;
242
243 err = rpc_send_message(g);
244 if (err) {
245 nvgpu_err(g, "failed rpc_send_message");
246 return err;
247 }
248
249 err = rpc_recv_poll(g);
250 if (err) {
251 nvgpu_err(g, "failed rpc_recv_poll");
252 return err;
253 }
254
255 /* Now check if RPC really succeeded */
256 if (*sim_msg_hdr(g, sim_msg_result_r()) != sim_msg_result_success_v()) {
257 nvgpu_err(g, "received failed status!");
258 return -EINVAL;
259 }
260 return 0;
261}
262
263static int gk20a_sim_esc_readl(struct gk20a *g, char *path, u32 index, u32 *data)
264{
265 int err;
266 size_t pathlen = strlen(path);
267 u32 data_offset;
268
269 sim_write_hdr(g, sim_msg_function_sim_escape_read_v(),
270 sim_escape_read_hdr_size());
271 *sim_msg_param(g, 0) = index;
272 *sim_msg_param(g, 4) = sizeof(u32);
273 data_offset = roundup(pathlen + 1, sizeof(u32));
274 *sim_msg_param(g, 8) = data_offset;
275 strcpy((char *)sim_msg_param(g, 0xc), path);
276
277 err = issue_rpc_and_wait(g);
278
279 if (!err)
280 memcpy(data, sim_msg_param(g, data_offset + 0xc), sizeof(u32));
281 return err;
282}
283
284static bool _nvgpu_pci_is_simulation(struct gk20a *g, u32 sim_base)
285{
286 u32 cfg;
287 bool is_simulation = false;
288
289 cfg = nvgpu_readl(g, sim_base + sim_config_r());
290 if (sim_config_mode_v(cfg) == sim_config_mode_enabled_v())
291 is_simulation = true;
292
293 return is_simulation;
294}
295
296int nvgpu_pci_init_sim_support(struct gk20a *g)
297{
298 int err = 0;
299 u64 phys;
300 struct sim_gk20a_linux *sim_linux;
301 bool is_simulation;
302 struct nvgpu_os_linux *l = nvgpu_os_linux_from_gk20a(g);
303
304 /* initialize sim aperture */
305 is_simulation = _nvgpu_pci_is_simulation(g, sim_r());
306 __nvgpu_set_enabled(g, NVGPU_IS_FMODEL, is_simulation);
307
308 if (!is_simulation)
309 return 0;
310
311 sim_linux = nvgpu_kzalloc(g, sizeof(*sim_linux));
312 if (!sim_linux)
313 goto fail;
314
315 g->sim = &sim_linux->sim;
316 sim_linux->regs = l->regs + sim_r();
317
318 /* allocate sim event/msg buffers */
319 err = alloc_and_kmap_iopage(g, &sim_linux->send_bfr.kvaddr,
320 &sim_linux->send_bfr.phys,
321 &sim_linux->send_bfr.page);
322
323 err = err || alloc_and_kmap_iopage(g, &sim_linux->recv_bfr.kvaddr,
324 &sim_linux->recv_bfr.phys,
325 &sim_linux->recv_bfr.page);
326
327 err = err || alloc_and_kmap_iopage(g, &sim_linux->msg_bfr.kvaddr,
328 &sim_linux->msg_bfr.phys,
329 &sim_linux->msg_bfr.page);
330
331 if (!(sim_linux->send_bfr.kvaddr && sim_linux->recv_bfr.kvaddr &&
332 sim_linux->msg_bfr.kvaddr)) {
333 nvgpu_err(g, "couldn't allocate all sim buffers");
334 goto fail;
335 }
336
337 /* mark send ring invalid */
338 sim_writel(sim_linux, sim_send_ring_r(), sim_send_ring_status_invalid_f());
339
340 /* read get pointer and make equal to put */
341 g->sim->send_ring_put = sim_readl(sim_linux, sim_send_get_r());
342 sim_writel(sim_linux, sim_send_put_r(), g->sim->send_ring_put);
343
344 /* write send ring address and make it valid */
345 phys = sim_linux->send_bfr.phys;
346 sim_writel(sim_linux, sim_send_ring_hi_r(),
347 sim_send_ring_hi_addr_f(u64_hi32(phys)));
348 sim_writel(sim_linux, sim_send_ring_r(),
349 sim_send_ring_status_valid_f() |
350 sim_send_ring_target_phys_pci_coherent_f() |
351 sim_send_ring_size_4kb_f() |
352 sim_send_ring_addr_lo_f(phys >> PAGE_SHIFT));
353
354 /* repeat for recv ring (but swap put,get as roles are opposite) */
355 sim_writel(sim_linux, sim_recv_ring_r(), sim_recv_ring_status_invalid_f());
356
357 /* read put pointer and make equal to get */
358 g->sim->recv_ring_get = sim_readl(sim_linux, sim_recv_put_r());
359 sim_writel(sim_linux, sim_recv_get_r(), g->sim->recv_ring_get);
360
361 /* write send ring address and make it valid */
362 phys = sim_linux->recv_bfr.phys;
363 sim_writel(sim_linux, sim_recv_ring_hi_r(),
364 sim_recv_ring_hi_addr_f(u64_hi32(phys)));
365 sim_writel(sim_linux, sim_recv_ring_r(),
366 sim_recv_ring_status_valid_f() |
367 sim_recv_ring_target_phys_pci_coherent_f() |
368 sim_recv_ring_size_4kb_f() |
369 sim_recv_ring_addr_lo_f(phys >> PAGE_SHIFT));
370
371 g->sim->remove_support = gk20a_remove_sim_support;
372 g->sim->esc_readl = gk20a_sim_esc_readl;
373 return 0;
374
375 fail:
376 gk20a_free_sim_support(g);
377 return err;
378}