From 1fd722f592c2e0523c5e399a2406a4e387057188 Mon Sep 17 00:00:00 2001 From: Aingara Paramakuru Date: Mon, 5 May 2014 21:14:22 -0400 Subject: gpu: nvgpu: support gk20a virtualization The nvgpu driver now supports using the Tegra graphics virtualization interfaces to support gk20a in a virtualized environment. Bug 1509608 Change-Id: I6ede15ee7bf0b0ad8a13e8eb5f557c3516ead676 Signed-off-by: Aingara Paramakuru Reviewed-on: http://git-master/r/440122 Reviewed-by: Terje Bergstrom Tested-by: Terje Bergstrom --- drivers/gpu/nvgpu/vgpu/vgpu.c | 416 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 416 insertions(+) create mode 100644 drivers/gpu/nvgpu/vgpu/vgpu.c (limited to 'drivers/gpu/nvgpu/vgpu/vgpu.c') 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 @@ +/* + * Virtualized GPU + * + * Copyright (c) 2014 NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include +#include +#include +#include +#include "vgpu/vgpu.h" +#include "gk20a/debug_gk20a.h" +#include "gk20a/hal_gk20a.h" +#include "gk20a/hw_mc_gk20a.h" + +static inline int vgpu_comm_init(struct platform_device *pdev) +{ + size_t queue_sizes[] = { TEGRA_VGPU_QUEUE_SIZES }; + + return tegra_gr_comm_init(pdev, TEGRA_GR_COMM_CTX_CLIENT, 3, + queue_sizes, TEGRA_VGPU_QUEUE_CMD, + ARRAY_SIZE(queue_sizes)); +} + +static inline void vgpu_comm_deinit(void) +{ + size_t queue_sizes[] = { TEGRA_VGPU_QUEUE_SIZES }; + + tegra_gr_comm_deinit(TEGRA_GR_COMM_CTX_CLIENT, TEGRA_VGPU_QUEUE_CMD, + ARRAY_SIZE(queue_sizes)); +} + +int vgpu_comm_sendrecv(struct tegra_vgpu_cmd_msg *msg, size_t size_in, + size_t size_out) +{ + void *handle; + size_t size = size_in; + void *data = msg; + int err; + + err = tegra_gr_comm_sendrecv(TEGRA_GR_COMM_CTX_CLIENT, + tegra_gr_comm_get_server_vmid(), + TEGRA_VGPU_QUEUE_CMD, &handle, &data, &size); + if (!err) { + WARN_ON(size < size_out); + memcpy(msg, data, size_out); + tegra_gr_comm_release(handle); + } + + return err; +} + +static u64 vgpu_connect(void) +{ + struct tegra_vgpu_cmd_msg msg; + struct tegra_vgpu_connect_params *p = &msg.params.connect; + int err; + + msg.cmd = TEGRA_VGPU_CMD_CONNECT; + p->module = TEGRA_VGPU_MODULE_GPU; + err = vgpu_comm_sendrecv(&msg, sizeof(msg), sizeof(msg)); + + return (err || msg.ret) ? 0 : p->handle; +} + +int vgpu_get_attribute(u64 handle, u32 attrib, u32 *value) +{ + struct tegra_vgpu_cmd_msg msg; + struct tegra_vgpu_attrib_params *p = &msg.params.attrib; + int err; + + msg.cmd = TEGRA_VGPU_CMD_GET_ATTRIBUTE; + msg.handle = handle; + p->attrib = attrib; + err = vgpu_comm_sendrecv(&msg, sizeof(msg), sizeof(msg)); + + if (err || msg.ret) + return -1; + + *value = p->value; + return 0; +} + +static int vgpu_intr_thread(void *dev_id) +{ + struct gk20a *g = dev_id; + + while (true) { + struct tegra_vgpu_intr_msg *msg; + u32 sender; + void *handle; + size_t size; + int err; + + err = tegra_gr_comm_recv(TEGRA_GR_COMM_CTX_CLIENT, + TEGRA_VGPU_QUEUE_INTR, &handle, + (void **)&msg, &size, &sender); + if (WARN_ON(err)) + continue; + + if (msg->event == TEGRA_VGPU_EVENT_ABORT) { + tegra_gr_comm_release(handle); + break; + } + + if (msg->unit == TEGRA_VGPU_INTR_GR) + vgpu_gr_isr(g, &msg->info.gr_intr); + + tegra_gr_comm_release(handle); + } + + while (!kthread_should_stop()) + msleep(10); + return 0; +} + +static void vgpu_remove_support(struct platform_device *dev) +{ + struct gk20a *g = get_gk20a(dev); + struct gk20a_platform *platform = gk20a_get_platform(dev); + struct tegra_vgpu_intr_msg msg; + int err; + + if (g->pmu.remove_support) + g->pmu.remove_support(&g->pmu); + + if (g->gr.remove_support) + g->gr.remove_support(&g->gr); + + if (g->fifo.remove_support) + g->fifo.remove_support(&g->fifo); + + if (g->mm.remove_support) + g->mm.remove_support(&g->mm); + + msg.event = TEGRA_VGPU_EVENT_ABORT; + err = tegra_gr_comm_send(TEGRA_GR_COMM_CTX_CLIENT, + TEGRA_GR_COMM_ID_SELF, TEGRA_VGPU_QUEUE_INTR, + &msg, sizeof(msg)); + WARN_ON(err); + kthread_stop(platform->intr_handler); + + /* free mappings to registers, etc*/ + + if (g->bar1) { + iounmap(g->bar1); + g->bar1 = 0; + } +} + +static int vgpu_init_support(struct platform_device *dev) +{ + struct resource *r = platform_get_resource(dev, IORESOURCE_MEM, 0); + struct gk20a *g = get_gk20a(dev); + int err = 0; + + if (!r) { + dev_err(dev_from_gk20a(g), "faield to get gk20a bar1\n"); + err = -ENXIO; + goto fail; + } + + g->bar1 = devm_request_and_ioremap(&dev->dev, r); + if (!g->bar1) { + dev_err(dev_from_gk20a(g), "failed to remap gk20a bar1\n"); + err = -ENXIO; + goto fail; + } + + mutex_init(&g->dbg_sessions_lock); + mutex_init(&g->client_lock); + + g->remove_support = vgpu_remove_support; + return 0; + + fail: + vgpu_remove_support(dev); + return err; +} + +int vgpu_pm_prepare_poweroff(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct gk20a *g = get_gk20a(pdev); + int ret = 0; + + gk20a_dbg_fn(""); + + if (!g->power_on) + return 0; + + ret = gk20a_channel_suspend(g); + if (ret) + return ret; + + g->power_on = false; + + return ret; +} + +static void vgpu_detect_chip(struct gk20a *g) +{ + struct nvhost_gpu_characteristics *gpu = &g->gpu_characteristics; + struct gk20a_platform *platform = gk20a_get_platform(g->dev); + + u32 mc_boot_0_value; + + if (vgpu_get_attribute(platform->virt_handle, + TEGRA_VGPU_ATTRIB_PMC_BOOT_0, + &mc_boot_0_value)) { + gk20a_err(dev_from_gk20a(g), "failed to detect chip"); + return; + } + + gpu->arch = mc_boot_0_architecture_v(mc_boot_0_value) << + NVHOST_GPU_ARCHITECTURE_SHIFT; + gpu->impl = mc_boot_0_implementation_v(mc_boot_0_value); + gpu->rev = + (mc_boot_0_major_revision_v(mc_boot_0_value) << 4) | + mc_boot_0_minor_revision_v(mc_boot_0_value); + + gk20a_dbg_info("arch: %x, impl: %x, rev: %x\n", + g->gpu_characteristics.arch, + g->gpu_characteristics.impl, + g->gpu_characteristics.rev); +} + +static int vgpu_init_hal(struct gk20a *g) +{ + u32 ver = g->gpu_characteristics.arch + g->gpu_characteristics.impl; + + switch (ver) { + case GK20A_GPUID_GK20A: + gk20a_dbg_info("gk20a detected"); + /* init gk20a ops then override with virt extensions */ + gk20a_init_hal(&g->ops); + vgpu_init_fifo_ops(&g->ops); + vgpu_init_gr_ops(&g->ops); + vgpu_init_ltc_ops(&g->ops); + vgpu_init_mm_ops(&g->ops); + break; + default: + gk20a_err(&g->dev->dev, "no support for %x", ver); + return -ENODEV; + } + + return 0; +} + +int vgpu_pm_finalize_poweron(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct gk20a *g = get_gk20a(pdev); + int err; + + gk20a_dbg_fn(""); + + if (g->power_on) + return 0; + + g->power_on = true; + + vgpu_detect_chip(g); + err = vgpu_init_hal(g); + if (err) + goto done; + + err = vgpu_init_mm_support(g); + if (err) { + gk20a_err(dev, "failed to init gk20a mm"); + goto done; + } + + err = vgpu_init_fifo_support(g); + if (err) { + gk20a_err(dev, "failed to init gk20a fifo"); + goto done; + } + + err = vgpu_init_gr_support(g); + if (err) { + gk20a_err(dev, "failed to init gk20a gr"); + goto done; + } + + err = gk20a_init_gpu_characteristics(g); + if (err) { + gk20a_err(dev, "failed to init gk20a gpu characteristics"); + goto done; + } + + gk20a_channel_resume(g); + +done: + return err; +} + +static int vgpu_pm_init(struct platform_device *dev) +{ + int err = 0; + + gk20a_dbg_fn(""); + + pm_runtime_enable(&dev->dev); + return err; +} + +int vgpu_probe(struct platform_device *dev) +{ + struct gk20a *gk20a; + int err; + struct gk20a_platform *platform = gk20a_get_platform(dev); + + if (!platform) { + dev_err(&dev->dev, "no platform data\n"); + return -ENODATA; + } + + gk20a_dbg_fn(""); + + gk20a = kzalloc(sizeof(struct gk20a), GFP_KERNEL); + if (!gk20a) { + dev_err(&dev->dev, "couldn't allocate gk20a support"); + return -ENOMEM; + } + + platform->g = gk20a; + gk20a->dev = dev; + + err = gk20a_user_init(dev); + if (err) + return err; + + vgpu_init_support(dev); + + init_rwsem(&gk20a->busy_lock); + + spin_lock_init(&gk20a->mc_enable_lock); + + /* Initialize the platform interface. */ + err = platform->probe(dev); + if (err) { + dev_err(&dev->dev, "platform probe failed"); + return err; + } + + err = vgpu_pm_init(dev); + if (err) { + dev_err(&dev->dev, "pm init failed"); + return err; + } + + if (platform->late_probe) { + err = platform->late_probe(dev); + if (err) { + dev_err(&dev->dev, "late probe failed"); + return err; + } + } + + err = vgpu_comm_init(dev); + if (err) { + dev_err(&dev->dev, "failed to init comm interface\n"); + return -ENOSYS; + } + + platform->virt_handle = vgpu_connect(); + if (!platform->virt_handle) { + dev_err(&dev->dev, "failed to connect to server node\n"); + vgpu_comm_deinit(); + return -ENOSYS; + } + + platform->intr_handler = kthread_run(vgpu_intr_thread, gk20a, "gk20a"); + if (IS_ERR(platform->intr_handler)) + return -ENOMEM; + + gk20a_debug_init(dev); + + /* Set DMA parameters to allow larger sgt lists */ + dev->dev.dma_parms = &gk20a->dma_parms; + dma_set_max_seg_size(&dev->dev, UINT_MAX); + + gk20a->gr_idle_timeout_default = + CONFIG_GK20A_DEFAULT_TIMEOUT; + gk20a->timeouts_enabled = true; + + gk20a_create_sysfs(dev); + gk20a_init_gr(gk20a); + + return 0; +} + +int vgpu_remove(struct platform_device *dev) +{ + struct gk20a *g = get_gk20a(dev); + gk20a_dbg_fn(""); + + if (g->remove_support) + g->remove_support(dev); + + vgpu_comm_deinit(); + gk20a_user_deinit(dev); + gk20a_get_platform(dev)->g = NULL; + kfree(g); + return 0; +} -- cgit v1.2.2