summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/nvgpu/common/sim_pci.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/nvgpu/common/sim_pci.c')
-rw-r--r--drivers/gpu/nvgpu/common/sim_pci.c260
1 files changed, 260 insertions, 0 deletions
diff --git a/drivers/gpu/nvgpu/common/sim_pci.c b/drivers/gpu/nvgpu/common/sim_pci.c
new file mode 100644
index 00000000..b4ca7bad
--- /dev/null
+++ b/drivers/gpu/nvgpu/common/sim_pci.c
@@ -0,0 +1,260 @@
1/*
2 * Copyright (c) 2017-2018, NVIDIA CORPORATION. All rights reserved.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 * DEALINGS IN THE SOFTWARE.
21 */
22#include <nvgpu/log.h>
23#include <nvgpu/bitops.h>
24#include <nvgpu/nvgpu_mem.h>
25#include <nvgpu/dma.h>
26#include <nvgpu/hw_sim_pci.h>
27#include "gk20a/gk20a.h"
28#include "linux/sim.h"
29
30static inline u32 sim_msg_header_size(void)
31{
32 return 32U;
33}
34
35static inline u32 *sim_msg_bfr(struct gk20a *g, u32 byte_offset)
36{
37 u8 *cpu_va;
38
39 cpu_va = (u8 *)g->sim->msg_bfr.cpu_va;
40
41 return (u32 *)(cpu_va + byte_offset);
42}
43
44static inline u32 *sim_msg_hdr(struct gk20a *g, u32 byte_offset)
45{
46 return sim_msg_bfr(g, byte_offset); /* starts at 0 */
47}
48
49static inline u32 *sim_msg_param(struct gk20a *g, u32 byte_offset)
50{
51 /* starts after msg header/cmn */
52 return sim_msg_bfr(g, byte_offset + sim_msg_header_size());
53}
54
55static inline void sim_write_hdr(struct gk20a *g, u32 func, u32 size)
56{
57 *sim_msg_hdr(g, sim_msg_header_version_r()) =
58 sim_msg_header_version_major_tot_v() |
59 sim_msg_header_version_minor_tot_v();
60 *sim_msg_hdr(g, sim_msg_signature_r()) = sim_msg_signature_valid_v();
61 *sim_msg_hdr(g, sim_msg_result_r()) = sim_msg_result_rpc_pending_v();
62 *sim_msg_hdr(g, sim_msg_spare_r()) = sim_msg_spare__init_v();
63 *sim_msg_hdr(g, sim_msg_function_r()) = func;
64 *sim_msg_hdr(g, sim_msg_length_r()) = size + sim_msg_header_size();
65}
66
67static inline u32 sim_escape_read_hdr_size(void)
68{
69 return 12U;
70}
71
72static u32 *sim_send_ring_bfr(struct gk20a *g, u32 byte_offset)
73{
74 u8 *cpu_va;
75
76 cpu_va = (u8 *)g->sim->send_bfr.cpu_va;
77
78 return (u32 *)(cpu_va + byte_offset);
79}
80
81static int rpc_send_message(struct gk20a *g)
82{
83 /* calculations done in units of u32s */
84 u32 send_base = sim_send_put_pointer_v(g->sim->send_ring_put) * 2;
85 u32 dma_offset = send_base + sim_dma_r()/sizeof(u32);
86 u32 dma_hi_offset = send_base + sim_dma_hi_r()/sizeof(u32);
87
88 *sim_send_ring_bfr(g, dma_offset*sizeof(u32)) =
89 sim_dma_target_phys_pci_coherent_f() |
90 sim_dma_status_valid_f() |
91 sim_dma_size_4kb_f() |
92 sim_dma_addr_lo_f(nvgpu_mem_get_addr(g, &g->sim->msg_bfr)
93 >> PAGE_SHIFT);
94
95 *sim_send_ring_bfr(g, dma_hi_offset*sizeof(u32)) =
96 u64_hi32(nvgpu_mem_get_addr(g, &g->sim->msg_bfr));
97
98 *sim_msg_hdr(g, sim_msg_sequence_r()) = g->sim->sequence_base++;
99
100 g->sim->send_ring_put = (g->sim->send_ring_put + 2 * sizeof(u32)) %
101 PAGE_SIZE;
102
103 /* Update the put pointer. This will trap into the host. */
104 sim_writel(g->sim, sim_send_put_r(), g->sim->send_ring_put);
105
106 return 0;
107}
108
109static inline u32 *sim_recv_ring_bfr(struct gk20a *g, u32 byte_offset)
110{
111 u8 *cpu_va;
112
113 cpu_va = (u8 *)g->sim->recv_bfr.cpu_va;
114
115 return (u32 *)(cpu_va + byte_offset);
116}
117
118static int rpc_recv_poll(struct gk20a *g)
119{
120 u64 recv_phys_addr;
121
122 /* Poll the recv ring get pointer in an infinite loop */
123 do {
124 g->sim->recv_ring_put = sim_readl(g->sim, sim_recv_put_r());
125 } while (g->sim->recv_ring_put == g->sim->recv_ring_get);
126
127 /* process all replies */
128 while (g->sim->recv_ring_put != g->sim->recv_ring_get) {
129 /* these are in u32 offsets */
130 u32 dma_lo_offset =
131 sim_recv_put_pointer_v(g->sim->recv_ring_get)*2 + 0;
132 u32 dma_hi_offset = dma_lo_offset + 1;
133 u32 recv_phys_addr_lo = sim_dma_addr_lo_v(
134 *sim_recv_ring_bfr(g, dma_lo_offset*4));
135 u32 recv_phys_addr_hi = sim_dma_hi_addr_v(
136 *sim_recv_ring_bfr(g, dma_hi_offset*4));
137
138 recv_phys_addr = (u64)recv_phys_addr_hi << 32 |
139 (u64)recv_phys_addr_lo << PAGE_SHIFT;
140
141 if (recv_phys_addr != nvgpu_mem_get_addr(g, &g->sim->msg_bfr)) {
142 nvgpu_err(g, "Error in RPC reply");
143 return -EINVAL;
144 }
145
146 /* Update GET pointer */
147 g->sim->recv_ring_get = (g->sim->recv_ring_get + 2*sizeof(u32))
148 % PAGE_SIZE;
149
150 sim_writel(g->sim, sim_recv_get_r(), g->sim->recv_ring_get);
151
152 g->sim->recv_ring_put = sim_readl(g->sim, sim_recv_put_r());
153 }
154
155 return 0;
156}
157
158static int issue_rpc_and_wait(struct gk20a *g)
159{
160 int err;
161
162 err = rpc_send_message(g);
163 if (err) {
164 nvgpu_err(g, "failed rpc_send_message");
165 return err;
166 }
167
168 err = rpc_recv_poll(g);
169 if (err) {
170 nvgpu_err(g, "failed rpc_recv_poll");
171 return err;
172 }
173
174 /* Now check if RPC really succeeded */
175 if (*sim_msg_hdr(g, sim_msg_result_r()) != sim_msg_result_success_v()) {
176 nvgpu_err(g, "received failed status!");
177 return -EINVAL;
178 }
179 return 0;
180}
181
182static int nvgpu_sim_esc_readl(struct gk20a *g,
183 char *path, u32 index, u32 *data)
184{
185 int err;
186 size_t pathlen = strlen(path);
187 u32 data_offset;
188
189 sim_write_hdr(g, sim_msg_function_sim_escape_read_v(),
190 sim_escape_read_hdr_size());
191 *sim_msg_param(g, 0) = index;
192 *sim_msg_param(g, 4) = sizeof(u32);
193 data_offset = roundup(pathlen + 1, sizeof(u32));
194 *sim_msg_param(g, 8) = data_offset;
195 strcpy((char *)sim_msg_param(g, 0xc), path);
196
197 err = issue_rpc_and_wait(g);
198
199 if (!err)
200 memcpy(data, sim_msg_param(g, data_offset + 0xc), sizeof(u32));
201 return err;
202}
203
204int nvgpu_init_sim_support_pci(struct gk20a *g)
205{
206 int err = -ENOMEM;
207 u64 phys;
208
209 if (!g->sim)
210 return 0;
211
212 /* allocate sim event/msg buffers */
213 err = nvgpu_alloc_sim_buffer(g, &g->sim->send_bfr);
214 err = err || nvgpu_alloc_sim_buffer(g, &g->sim->recv_bfr);
215 err = err || nvgpu_alloc_sim_buffer(g, &g->sim->msg_bfr);
216
217 if (err)
218 goto fail;
219 /* mark send ring invalid */
220 sim_writel(g->sim, sim_send_ring_r(), sim_send_ring_status_invalid_f());
221
222 /* read get pointer and make equal to put */
223 g->sim->send_ring_put = sim_readl(g->sim, sim_send_get_r());
224 sim_writel(g->sim, sim_send_put_r(), g->sim->send_ring_put);
225
226 /* write send ring address and make it valid */
227 phys = nvgpu_mem_get_addr(g, &g->sim->send_bfr);
228 sim_writel(g->sim, sim_send_ring_hi_r(),
229 sim_send_ring_hi_addr_f(u64_hi32(phys)));
230 sim_writel(g->sim, sim_send_ring_r(),
231 sim_send_ring_status_valid_f() |
232 sim_send_ring_target_phys_pci_coherent_f() |
233 sim_send_ring_size_4kb_f() |
234 sim_send_ring_addr_lo_f(phys >> PAGE_SHIFT));
235
236 /* repeat for recv ring (but swap put,get as roles are opposite) */
237 sim_writel(g->sim, sim_recv_ring_r(), sim_recv_ring_status_invalid_f());
238
239 /* read put pointer and make equal to get */
240 g->sim->recv_ring_get = sim_readl(g->sim, sim_recv_put_r());
241 sim_writel(g->sim, sim_recv_get_r(), g->sim->recv_ring_get);
242
243 /* write send ring address and make it valid */
244 phys = nvgpu_mem_get_addr(g, &g->sim->recv_bfr);
245 sim_writel(g->sim, sim_recv_ring_hi_r(),
246 sim_recv_ring_hi_addr_f(u64_hi32(phys)));
247 sim_writel(g->sim, sim_recv_ring_r(),
248 sim_recv_ring_status_valid_f() |
249 sim_recv_ring_target_phys_pci_coherent_f() |
250 sim_recv_ring_size_4kb_f() |
251 sim_recv_ring_addr_lo_f(phys >> PAGE_SHIFT));
252
253 g->sim->remove_support = nvgpu_remove_sim_support;
254 g->sim->esc_readl = nvgpu_sim_esc_readl;
255 return 0;
256
257 fail:
258 nvgpu_free_sim_support(g);
259 return err;
260}