diff options
Diffstat (limited to 'drivers/gpu/nvgpu/vgpu/vgpu.c')
-rw-r--r-- | drivers/gpu/nvgpu/vgpu/vgpu.c | 416 |
1 files changed, 416 insertions, 0 deletions
diff --git a/drivers/gpu/nvgpu/vgpu/vgpu.c b/drivers/gpu/nvgpu/vgpu/vgpu.c new file mode 100644 index 00000000..cfe307ff --- /dev/null +++ b/drivers/gpu/nvgpu/vgpu/vgpu.c | |||
@@ -0,0 +1,416 @@ | |||
1 | /* | ||
2 | * Virtualized GPU | ||
3 | * | ||
4 | * Copyright (c) 2014 NVIDIA CORPORATION. All rights reserved. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify it | ||
7 | * under the terms and conditions of the GNU General Public License, | ||
8 | * version 2, as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
13 | * more details. | ||
14 | */ | ||
15 | |||
16 | #include <linux/kthread.h> | ||
17 | #include <linux/delay.h> | ||
18 | #include <linux/dma-mapping.h> | ||
19 | #include <linux/pm_runtime.h> | ||
20 | #include "vgpu/vgpu.h" | ||
21 | #include "gk20a/debug_gk20a.h" | ||
22 | #include "gk20a/hal_gk20a.h" | ||
23 | #include "gk20a/hw_mc_gk20a.h" | ||
24 | |||
25 | static inline int vgpu_comm_init(struct platform_device *pdev) | ||
26 | { | ||
27 | size_t queue_sizes[] = { TEGRA_VGPU_QUEUE_SIZES }; | ||
28 | |||
29 | return tegra_gr_comm_init(pdev, TEGRA_GR_COMM_CTX_CLIENT, 3, | ||
30 | queue_sizes, TEGRA_VGPU_QUEUE_CMD, | ||
31 | ARRAY_SIZE(queue_sizes)); | ||
32 | } | ||
33 | |||
34 | static inline void vgpu_comm_deinit(void) | ||
35 | { | ||
36 | size_t queue_sizes[] = { TEGRA_VGPU_QUEUE_SIZES }; | ||
37 | |||
38 | tegra_gr_comm_deinit(TEGRA_GR_COMM_CTX_CLIENT, TEGRA_VGPU_QUEUE_CMD, | ||
39 | ARRAY_SIZE(queue_sizes)); | ||
40 | } | ||
41 | |||
42 | int vgpu_comm_sendrecv(struct tegra_vgpu_cmd_msg *msg, size_t size_in, | ||
43 | size_t size_out) | ||
44 | { | ||
45 | void *handle; | ||
46 | size_t size = size_in; | ||
47 | void *data = msg; | ||
48 | int err; | ||
49 | |||
50 | err = tegra_gr_comm_sendrecv(TEGRA_GR_COMM_CTX_CLIENT, | ||
51 | tegra_gr_comm_get_server_vmid(), | ||
52 | TEGRA_VGPU_QUEUE_CMD, &handle, &data, &size); | ||
53 | if (!err) { | ||
54 | WARN_ON(size < size_out); | ||
55 | memcpy(msg, data, size_out); | ||
56 | tegra_gr_comm_release(handle); | ||
57 | } | ||
58 | |||
59 | return err; | ||
60 | } | ||
61 | |||
62 | static u64 vgpu_connect(void) | ||
63 | { | ||
64 | struct tegra_vgpu_cmd_msg msg; | ||
65 | struct tegra_vgpu_connect_params *p = &msg.params.connect; | ||
66 | int err; | ||
67 | |||
68 | msg.cmd = TEGRA_VGPU_CMD_CONNECT; | ||
69 | p->module = TEGRA_VGPU_MODULE_GPU; | ||
70 | err = vgpu_comm_sendrecv(&msg, sizeof(msg), sizeof(msg)); | ||
71 | |||
72 | return (err || msg.ret) ? 0 : p->handle; | ||
73 | } | ||
74 | |||
75 | int vgpu_get_attribute(u64 handle, u32 attrib, u32 *value) | ||
76 | { | ||
77 | struct tegra_vgpu_cmd_msg msg; | ||
78 | struct tegra_vgpu_attrib_params *p = &msg.params.attrib; | ||
79 | int err; | ||
80 | |||
81 | msg.cmd = TEGRA_VGPU_CMD_GET_ATTRIBUTE; | ||
82 | msg.handle = handle; | ||
83 | p->attrib = attrib; | ||
84 | err = vgpu_comm_sendrecv(&msg, sizeof(msg), sizeof(msg)); | ||
85 | |||
86 | if (err || msg.ret) | ||
87 | return -1; | ||
88 | |||
89 | *value = p->value; | ||
90 | return 0; | ||
91 | } | ||
92 | |||
93 | static int vgpu_intr_thread(void *dev_id) | ||
94 | { | ||
95 | struct gk20a *g = dev_id; | ||
96 | |||
97 | while (true) { | ||
98 | struct tegra_vgpu_intr_msg *msg; | ||
99 | u32 sender; | ||
100 | void *handle; | ||
101 | size_t size; | ||
102 | int err; | ||
103 | |||
104 | err = tegra_gr_comm_recv(TEGRA_GR_COMM_CTX_CLIENT, | ||
105 | TEGRA_VGPU_QUEUE_INTR, &handle, | ||
106 | (void **)&msg, &size, &sender); | ||
107 | if (WARN_ON(err)) | ||
108 | continue; | ||
109 | |||
110 | if (msg->event == TEGRA_VGPU_EVENT_ABORT) { | ||
111 | tegra_gr_comm_release(handle); | ||
112 | break; | ||
113 | } | ||
114 | |||
115 | if (msg->unit == TEGRA_VGPU_INTR_GR) | ||
116 | vgpu_gr_isr(g, &msg->info.gr_intr); | ||
117 | |||
118 | tegra_gr_comm_release(handle); | ||
119 | } | ||
120 | |||
121 | while (!kthread_should_stop()) | ||
122 | msleep(10); | ||
123 | return 0; | ||
124 | } | ||
125 | |||
126 | static void vgpu_remove_support(struct platform_device *dev) | ||
127 | { | ||
128 | struct gk20a *g = get_gk20a(dev); | ||
129 | struct gk20a_platform *platform = gk20a_get_platform(dev); | ||
130 | struct tegra_vgpu_intr_msg msg; | ||
131 | int err; | ||
132 | |||
133 | if (g->pmu.remove_support) | ||
134 | g->pmu.remove_support(&g->pmu); | ||
135 | |||
136 | if (g->gr.remove_support) | ||
137 | g->gr.remove_support(&g->gr); | ||
138 | |||
139 | if (g->fifo.remove_support) | ||
140 | g->fifo.remove_support(&g->fifo); | ||
141 | |||
142 | if (g->mm.remove_support) | ||
143 | g->mm.remove_support(&g->mm); | ||
144 | |||
145 | msg.event = TEGRA_VGPU_EVENT_ABORT; | ||
146 | err = tegra_gr_comm_send(TEGRA_GR_COMM_CTX_CLIENT, | ||
147 | TEGRA_GR_COMM_ID_SELF, TEGRA_VGPU_QUEUE_INTR, | ||
148 | &msg, sizeof(msg)); | ||
149 | WARN_ON(err); | ||
150 | kthread_stop(platform->intr_handler); | ||
151 | |||
152 | /* free mappings to registers, etc*/ | ||
153 | |||
154 | if (g->bar1) { | ||
155 | iounmap(g->bar1); | ||
156 | g->bar1 = 0; | ||
157 | } | ||
158 | } | ||
159 | |||
160 | static int vgpu_init_support(struct platform_device *dev) | ||
161 | { | ||
162 | struct resource *r = platform_get_resource(dev, IORESOURCE_MEM, 0); | ||
163 | struct gk20a *g = get_gk20a(dev); | ||
164 | int err = 0; | ||
165 | |||
166 | if (!r) { | ||
167 | dev_err(dev_from_gk20a(g), "faield to get gk20a bar1\n"); | ||
168 | err = -ENXIO; | ||
169 | goto fail; | ||
170 | } | ||
171 | |||
172 | g->bar1 = devm_request_and_ioremap(&dev->dev, r); | ||
173 | if (!g->bar1) { | ||
174 | dev_err(dev_from_gk20a(g), "failed to remap gk20a bar1\n"); | ||
175 | err = -ENXIO; | ||
176 | goto fail; | ||
177 | } | ||
178 | |||
179 | mutex_init(&g->dbg_sessions_lock); | ||
180 | mutex_init(&g->client_lock); | ||
181 | |||
182 | g->remove_support = vgpu_remove_support; | ||
183 | return 0; | ||
184 | |||
185 | fail: | ||
186 | vgpu_remove_support(dev); | ||
187 | return err; | ||
188 | } | ||
189 | |||
190 | int vgpu_pm_prepare_poweroff(struct device *dev) | ||
191 | { | ||
192 | struct platform_device *pdev = to_platform_device(dev); | ||
193 | struct gk20a *g = get_gk20a(pdev); | ||
194 | int ret = 0; | ||
195 | |||
196 | gk20a_dbg_fn(""); | ||
197 | |||
198 | if (!g->power_on) | ||
199 | return 0; | ||
200 | |||
201 | ret = gk20a_channel_suspend(g); | ||
202 | if (ret) | ||
203 | return ret; | ||
204 | |||
205 | g->power_on = false; | ||
206 | |||
207 | return ret; | ||
208 | } | ||
209 | |||
210 | static void vgpu_detect_chip(struct gk20a *g) | ||
211 | { | ||
212 | struct nvhost_gpu_characteristics *gpu = &g->gpu_characteristics; | ||
213 | struct gk20a_platform *platform = gk20a_get_platform(g->dev); | ||
214 | |||
215 | u32 mc_boot_0_value; | ||
216 | |||
217 | if (vgpu_get_attribute(platform->virt_handle, | ||
218 | TEGRA_VGPU_ATTRIB_PMC_BOOT_0, | ||
219 | &mc_boot_0_value)) { | ||
220 | gk20a_err(dev_from_gk20a(g), "failed to detect chip"); | ||
221 | return; | ||
222 | } | ||
223 | |||
224 | gpu->arch = mc_boot_0_architecture_v(mc_boot_0_value) << | ||
225 | NVHOST_GPU_ARCHITECTURE_SHIFT; | ||
226 | gpu->impl = mc_boot_0_implementation_v(mc_boot_0_value); | ||
227 | gpu->rev = | ||
228 | (mc_boot_0_major_revision_v(mc_boot_0_value) << 4) | | ||
229 | mc_boot_0_minor_revision_v(mc_boot_0_value); | ||
230 | |||
231 | gk20a_dbg_info("arch: %x, impl: %x, rev: %x\n", | ||
232 | g->gpu_characteristics.arch, | ||
233 | g->gpu_characteristics.impl, | ||
234 | g->gpu_characteristics.rev); | ||
235 | } | ||
236 | |||
237 | static int vgpu_init_hal(struct gk20a *g) | ||
238 | { | ||
239 | u32 ver = g->gpu_characteristics.arch + g->gpu_characteristics.impl; | ||
240 | |||
241 | switch (ver) { | ||
242 | case GK20A_GPUID_GK20A: | ||
243 | gk20a_dbg_info("gk20a detected"); | ||
244 | /* init gk20a ops then override with virt extensions */ | ||
245 | gk20a_init_hal(&g->ops); | ||
246 | vgpu_init_fifo_ops(&g->ops); | ||
247 | vgpu_init_gr_ops(&g->ops); | ||
248 | vgpu_init_ltc_ops(&g->ops); | ||
249 | vgpu_init_mm_ops(&g->ops); | ||
250 | break; | ||
251 | default: | ||
252 | gk20a_err(&g->dev->dev, "no support for %x", ver); | ||
253 | return -ENODEV; | ||
254 | } | ||
255 | |||
256 | return 0; | ||
257 | } | ||
258 | |||
259 | int vgpu_pm_finalize_poweron(struct device *dev) | ||
260 | { | ||
261 | struct platform_device *pdev = to_platform_device(dev); | ||
262 | struct gk20a *g = get_gk20a(pdev); | ||
263 | int err; | ||
264 | |||
265 | gk20a_dbg_fn(""); | ||
266 | |||
267 | if (g->power_on) | ||
268 | return 0; | ||
269 | |||
270 | g->power_on = true; | ||
271 | |||
272 | vgpu_detect_chip(g); | ||
273 | err = vgpu_init_hal(g); | ||
274 | if (err) | ||
275 | goto done; | ||
276 | |||
277 | err = vgpu_init_mm_support(g); | ||
278 | if (err) { | ||
279 | gk20a_err(dev, "failed to init gk20a mm"); | ||
280 | goto done; | ||
281 | } | ||
282 | |||
283 | err = vgpu_init_fifo_support(g); | ||
284 | if (err) { | ||
285 | gk20a_err(dev, "failed to init gk20a fifo"); | ||
286 | goto done; | ||
287 | } | ||
288 | |||
289 | err = vgpu_init_gr_support(g); | ||
290 | if (err) { | ||
291 | gk20a_err(dev, "failed to init gk20a gr"); | ||
292 | goto done; | ||
293 | } | ||
294 | |||
295 | err = gk20a_init_gpu_characteristics(g); | ||
296 | if (err) { | ||
297 | gk20a_err(dev, "failed to init gk20a gpu characteristics"); | ||
298 | goto done; | ||
299 | } | ||
300 | |||
301 | gk20a_channel_resume(g); | ||
302 | |||
303 | done: | ||
304 | return err; | ||
305 | } | ||
306 | |||
307 | static int vgpu_pm_init(struct platform_device *dev) | ||
308 | { | ||
309 | int err = 0; | ||
310 | |||
311 | gk20a_dbg_fn(""); | ||
312 | |||
313 | pm_runtime_enable(&dev->dev); | ||
314 | return err; | ||
315 | } | ||
316 | |||
317 | int vgpu_probe(struct platform_device *dev) | ||
318 | { | ||
319 | struct gk20a *gk20a; | ||
320 | int err; | ||
321 | struct gk20a_platform *platform = gk20a_get_platform(dev); | ||
322 | |||
323 | if (!platform) { | ||
324 | dev_err(&dev->dev, "no platform data\n"); | ||
325 | return -ENODATA; | ||
326 | } | ||
327 | |||
328 | gk20a_dbg_fn(""); | ||
329 | |||
330 | gk20a = kzalloc(sizeof(struct gk20a), GFP_KERNEL); | ||
331 | if (!gk20a) { | ||
332 | dev_err(&dev->dev, "couldn't allocate gk20a support"); | ||
333 | return -ENOMEM; | ||
334 | } | ||
335 | |||
336 | platform->g = gk20a; | ||
337 | gk20a->dev = dev; | ||
338 | |||
339 | err = gk20a_user_init(dev); | ||
340 | if (err) | ||
341 | return err; | ||
342 | |||
343 | vgpu_init_support(dev); | ||
344 | |||
345 | init_rwsem(&gk20a->busy_lock); | ||
346 | |||
347 | spin_lock_init(&gk20a->mc_enable_lock); | ||
348 | |||
349 | /* Initialize the platform interface. */ | ||
350 | err = platform->probe(dev); | ||
351 | if (err) { | ||
352 | dev_err(&dev->dev, "platform probe failed"); | ||
353 | return err; | ||
354 | } | ||
355 | |||
356 | err = vgpu_pm_init(dev); | ||
357 | if (err) { | ||
358 | dev_err(&dev->dev, "pm init failed"); | ||
359 | return err; | ||
360 | } | ||
361 | |||
362 | if (platform->late_probe) { | ||
363 | err = platform->late_probe(dev); | ||
364 | if (err) { | ||
365 | dev_err(&dev->dev, "late probe failed"); | ||
366 | return err; | ||
367 | } | ||
368 | } | ||
369 | |||
370 | err = vgpu_comm_init(dev); | ||
371 | if (err) { | ||
372 | dev_err(&dev->dev, "failed to init comm interface\n"); | ||
373 | return -ENOSYS; | ||
374 | } | ||
375 | |||
376 | platform->virt_handle = vgpu_connect(); | ||
377 | if (!platform->virt_handle) { | ||
378 | dev_err(&dev->dev, "failed to connect to server node\n"); | ||
379 | vgpu_comm_deinit(); | ||
380 | return -ENOSYS; | ||
381 | } | ||
382 | |||
383 | platform->intr_handler = kthread_run(vgpu_intr_thread, gk20a, "gk20a"); | ||
384 | if (IS_ERR(platform->intr_handler)) | ||
385 | return -ENOMEM; | ||
386 | |||
387 | gk20a_debug_init(dev); | ||
388 | |||
389 | /* Set DMA parameters to allow larger sgt lists */ | ||
390 | dev->dev.dma_parms = &gk20a->dma_parms; | ||
391 | dma_set_max_seg_size(&dev->dev, UINT_MAX); | ||
392 | |||
393 | gk20a->gr_idle_timeout_default = | ||
394 | CONFIG_GK20A_DEFAULT_TIMEOUT; | ||
395 | gk20a->timeouts_enabled = true; | ||
396 | |||
397 | gk20a_create_sysfs(dev); | ||
398 | gk20a_init_gr(gk20a); | ||
399 | |||
400 | return 0; | ||
401 | } | ||
402 | |||
403 | int vgpu_remove(struct platform_device *dev) | ||
404 | { | ||
405 | struct gk20a *g = get_gk20a(dev); | ||
406 | gk20a_dbg_fn(""); | ||
407 | |||
408 | if (g->remove_support) | ||
409 | g->remove_support(dev); | ||
410 | |||
411 | vgpu_comm_deinit(); | ||
412 | gk20a_user_deinit(dev); | ||
413 | gk20a_get_platform(dev)->g = NULL; | ||
414 | kfree(g); | ||
415 | return 0; | ||
416 | } | ||