From 01e6fac4d61fdd7fff5433942ec93fc2ea1e4df1 Mon Sep 17 00:00:00 2001 From: Joshua Bakita Date: Wed, 28 Jun 2023 18:24:25 -0400 Subject: Include nvgpu headers These are needed to build on NVIDIA's Jetson boards for the time being. Only a couple structs are required, so it should be fairly easy to remove this dependency at some point in the future. --- include/os/linux/vgpu/fecs_trace_vgpu.c | 225 +++++++++ .../linux/vgpu/gv11b/platform_gv11b_vgpu_tegra.c | 103 ++++ include/os/linux/vgpu/platform_vgpu_tegra.c | 97 ++++ include/os/linux/vgpu/platform_vgpu_tegra.h | 24 + include/os/linux/vgpu/sysfs_vgpu.c | 143 ++++++ include/os/linux/vgpu/vgpu_ivc.c | 77 +++ include/os/linux/vgpu/vgpu_ivm.c | 53 +++ include/os/linux/vgpu/vgpu_linux.c | 525 +++++++++++++++++++++ include/os/linux/vgpu/vgpu_linux.h | 68 +++ 9 files changed, 1315 insertions(+) create mode 100644 include/os/linux/vgpu/fecs_trace_vgpu.c create mode 100644 include/os/linux/vgpu/gv11b/platform_gv11b_vgpu_tegra.c create mode 100644 include/os/linux/vgpu/platform_vgpu_tegra.c create mode 100644 include/os/linux/vgpu/platform_vgpu_tegra.h create mode 100644 include/os/linux/vgpu/sysfs_vgpu.c create mode 100644 include/os/linux/vgpu/vgpu_ivc.c create mode 100644 include/os/linux/vgpu/vgpu_ivm.c create mode 100644 include/os/linux/vgpu/vgpu_linux.c create mode 100644 include/os/linux/vgpu/vgpu_linux.h (limited to 'include/os/linux/vgpu') diff --git a/include/os/linux/vgpu/fecs_trace_vgpu.c b/include/os/linux/vgpu/fecs_trace_vgpu.c new file mode 100644 index 0000000..02a381e --- /dev/null +++ b/include/os/linux/vgpu/fecs_trace_vgpu.c @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2016-2018, 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "os/linux/os_linux.h" +#include "gk20a/fecs_trace_gk20a.h" +#include "vgpu/fecs_trace_vgpu.h" + +struct vgpu_fecs_trace { + struct tegra_hv_ivm_cookie *cookie; + struct nvgpu_ctxsw_ring_header *header; + struct nvgpu_gpu_ctxsw_trace_entry *entries; + int num_entries; + bool enabled; + void *buf; +}; + +int vgpu_fecs_trace_init(struct gk20a *g) +{ + struct device *dev = dev_from_gk20a(g); + struct device_node *np = dev->of_node; + struct of_phandle_args args; + struct vgpu_fecs_trace *vcst; + u32 mempool; + int err; + + nvgpu_log_fn(g, " "); + + vcst = nvgpu_kzalloc(g, sizeof(*vcst)); + if (!vcst) + return -ENOMEM; + + err = of_parse_phandle_with_fixed_args(np, + "mempool-fecs-trace", 1, 0, &args); + if (err) { + nvgpu_info(g, "does not support fecs trace"); + goto fail; + } + __nvgpu_set_enabled(g, NVGPU_SUPPORT_FECS_CTXSW_TRACE, true); + + mempool = args.args[0]; + vcst->cookie = vgpu_ivm_mempool_reserve(mempool); + if (IS_ERR(vcst->cookie)) { + nvgpu_info(g, + "mempool %u reserve failed", mempool); + vcst->cookie = NULL; + err = -EINVAL; + goto fail; + } + + vcst->buf = ioremap_cache(vgpu_ivm_get_ipa(vcst->cookie), + vgpu_ivm_get_size(vcst->cookie)); + if (!vcst->buf) { + nvgpu_info(g, "ioremap_cache failed"); + err = -EINVAL; + goto fail; + } + vcst->header = vcst->buf; + vcst->num_entries = vcst->header->num_ents; + if (unlikely(vcst->header->ent_size != sizeof(*vcst->entries))) { + nvgpu_err(g, "entry size mismatch"); + goto fail; + } + vcst->entries = vcst->buf + sizeof(*vcst->header); + g->fecs_trace = (struct gk20a_fecs_trace *)vcst; + + return 0; +fail: + iounmap(vcst->buf); + if (vcst->cookie) + vgpu_ivm_mempool_unreserve(vcst->cookie); + nvgpu_kfree(g, vcst); + return err; +} + +int vgpu_fecs_trace_deinit(struct gk20a *g) +{ + struct vgpu_fecs_trace *vcst = (struct vgpu_fecs_trace *)g->fecs_trace; + + iounmap(vcst->buf); + vgpu_ivm_mempool_unreserve(vcst->cookie); + nvgpu_kfree(g, vcst); + return 0; +} + +int vgpu_fecs_trace_enable(struct gk20a *g) +{ + struct vgpu_fecs_trace *vcst = (struct vgpu_fecs_trace *)g->fecs_trace; + struct tegra_vgpu_cmd_msg msg = { + .cmd = TEGRA_VGPU_CMD_FECS_TRACE_ENABLE, + .handle = vgpu_get_handle(g), + }; + int err; + + err = vgpu_comm_sendrecv(&msg, sizeof(msg), sizeof(msg)); + err = err ? err : msg.ret; + WARN_ON(err); + vcst->enabled = !err; + return err; +} + +int vgpu_fecs_trace_disable(struct gk20a *g) +{ + struct vgpu_fecs_trace *vcst = (struct vgpu_fecs_trace *)g->fecs_trace; + struct tegra_vgpu_cmd_msg msg = { + .cmd = TEGRA_VGPU_CMD_FECS_TRACE_DISABLE, + .handle = vgpu_get_handle(g), + }; + int err; + + vcst->enabled = false; + err = vgpu_comm_sendrecv(&msg, sizeof(msg), sizeof(msg)); + err = err ? err : msg.ret; + WARN_ON(err); + return err; +} + +bool vgpu_fecs_trace_is_enabled(struct gk20a *g) +{ + struct vgpu_fecs_trace *vcst = (struct vgpu_fecs_trace *)g->fecs_trace; + + return (vcst && vcst->enabled); +} + +int vgpu_fecs_trace_poll(struct gk20a *g) +{ + struct tegra_vgpu_cmd_msg msg = { + .cmd = TEGRA_VGPU_CMD_FECS_TRACE_POLL, + .handle = vgpu_get_handle(g), + }; + int err; + + err = vgpu_comm_sendrecv(&msg, sizeof(msg), sizeof(msg)); + err = err ? err : msg.ret; + WARN_ON(err); + return err; +} + +int vgpu_alloc_user_buffer(struct gk20a *g, void **buf, size_t *size) +{ + struct vgpu_fecs_trace *vcst = (struct vgpu_fecs_trace *)g->fecs_trace; + + *buf = vcst->buf; + *size = vgpu_ivm_get_size(vcst->cookie); + return 0; +} + +int vgpu_free_user_buffer(struct gk20a *g) +{ + return 0; +} + +int vgpu_mmap_user_buffer(struct gk20a *g, struct vm_area_struct *vma) +{ + struct vgpu_fecs_trace *vcst = (struct vgpu_fecs_trace *)g->fecs_trace; + unsigned long size = vgpu_ivm_get_size(vcst->cookie); + unsigned long vsize = vma->vm_end - vma->vm_start; + + size = min(size, vsize); + size = round_up(size, PAGE_SIZE); + + return remap_pfn_range(vma, vma->vm_start, + vgpu_ivm_get_ipa(vcst->cookie) >> PAGE_SHIFT, + size, + vma->vm_page_prot); +} + +#ifdef CONFIG_GK20A_CTXSW_TRACE +int vgpu_fecs_trace_max_entries(struct gk20a *g, + struct nvgpu_gpu_ctxsw_trace_filter *filter) +{ + struct vgpu_fecs_trace *vcst = (struct vgpu_fecs_trace *)g->fecs_trace; + + return vcst->header->num_ents; +} + +#if NVGPU_CTXSW_FILTER_SIZE != TEGRA_VGPU_FECS_TRACE_FILTER_SIZE +#error "FECS trace filter size mismatch!" +#endif + +int vgpu_fecs_trace_set_filter(struct gk20a *g, + struct nvgpu_gpu_ctxsw_trace_filter *filter) +{ + struct tegra_vgpu_cmd_msg msg = { + .cmd = TEGRA_VGPU_CMD_FECS_TRACE_SET_FILTER, + .handle = vgpu_get_handle(g), + }; + struct tegra_vgpu_fecs_trace_filter *p = &msg.params.fecs_trace_filter; + int err; + + memcpy(&p->tag_bits, &filter->tag_bits, sizeof(p->tag_bits)); + err = vgpu_comm_sendrecv(&msg, sizeof(msg), sizeof(msg)); + err = err ? err : msg.ret; + WARN_ON(err); + return err; +} + +void vgpu_fecs_trace_data_update(struct gk20a *g) +{ + gk20a_ctxsw_trace_wake_up(g, 0); +} +#endif /* CONFIG_GK20A_CTXSW_TRACE */ diff --git a/include/os/linux/vgpu/gv11b/platform_gv11b_vgpu_tegra.c b/include/os/linux/vgpu/gv11b/platform_gv11b_vgpu_tegra.c new file mode 100644 index 0000000..0304bcc --- /dev/null +++ b/include/os/linux/vgpu/gv11b/platform_gv11b_vgpu_tegra.c @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2017-2018, 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include +#include + +#include "vgpu/clk_vgpu.h" +#include "os/linux/platform_gk20a.h" +#include "os/linux/os_linux.h" +#include "os/linux/vgpu/vgpu_linux.h" +#include "os/linux/vgpu/platform_vgpu_tegra.h" + +static int gv11b_vgpu_probe(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct gk20a_platform *platform = dev_get_drvdata(dev); + struct resource *r; + void __iomem *regs; + struct nvgpu_os_linux *l = nvgpu_os_linux_from_gk20a(platform->g); + struct gk20a *g = platform->g; + int ret; + + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "usermode"); + if (!r) { + nvgpu_err(g, "failed to get usermode regs"); + return -ENXIO; + } + regs = devm_ioremap_resource(dev, r); + if (IS_ERR(regs)) { + nvgpu_err(g, "failed to map usermode regs"); + return PTR_ERR(regs); + } + l->usermode_regs = regs; + +#ifdef CONFIG_TEGRA_GK20A_NVHOST + ret = nvgpu_get_nvhost_dev(g); + if (ret) { + l->usermode_regs = NULL; + return ret; + } + + ret = nvgpu_nvhost_syncpt_unit_interface_get_aperture(g->nvhost_dev, + &g->syncpt_unit_base, + &g->syncpt_unit_size); + if (ret) { + nvgpu_err(g, "Failed to get syncpt interface"); + return -ENOSYS; + } + g->syncpt_size = nvgpu_nvhost_syncpt_unit_interface_get_byte_offset(1); + nvgpu_info(g, "syncpt_unit_base %llx syncpt_unit_size %zx size %x\n", + g->syncpt_unit_base, g->syncpt_unit_size, g->syncpt_size); +#endif + vgpu_init_clk_support(platform->g); + + return 0; +} + +struct gk20a_platform gv11b_vgpu_tegra_platform = { + .has_syncpoints = true, + + /* power management configuration */ + .can_railgate_init = false, + .can_elpg_init = false, + .enable_slcg = false, + .enable_blcg = false, + .enable_elcg = false, + .enable_elpg = false, + .enable_aelpg = false, + .can_slcg = false, + .can_blcg = false, + .can_elcg = false, + + .ch_wdt_timeout_ms = 5000, + + .probe = gv11b_vgpu_probe, + + .clk_round_rate = vgpu_plat_clk_round_rate, + .get_clk_freqs = vgpu_plat_clk_get_freqs, + + /* frequency scaling configuration */ + .devfreq_governor = "userspace", + + .virtual_dev = true, + + /* power management callbacks */ + .suspend = vgpu_tegra_suspend, + .resume = vgpu_tegra_resume, +}; diff --git a/include/os/linux/vgpu/platform_vgpu_tegra.c b/include/os/linux/vgpu/platform_vgpu_tegra.c new file mode 100644 index 0000000..948323e --- /dev/null +++ b/include/os/linux/vgpu/platform_vgpu_tegra.c @@ -0,0 +1,97 @@ +/* + * Tegra Virtualized GPU Platform Interface + * + * Copyright (c) 2014-2018, 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include "os/linux/platform_gk20a.h" +#include "vgpu/clk_vgpu.h" +#include "vgpu_linux.h" + +static int gk20a_tegra_probe(struct device *dev) +{ +#ifdef CONFIG_TEGRA_GK20A_NVHOST + struct gk20a_platform *platform = dev_get_drvdata(dev); + int ret; + + ret = nvgpu_get_nvhost_dev(platform->g); + if (ret) + return ret; + + vgpu_init_clk_support(platform->g); + return 0; +#else + return 0; +#endif +} + +long vgpu_plat_clk_round_rate(struct device *dev, unsigned long rate) +{ + /* server will handle frequency rounding */ + return rate; +} + +int vgpu_plat_clk_get_freqs(struct device *dev, unsigned long **freqs, + int *num_freqs) +{ + struct gk20a_platform *platform = gk20a_get_platform(dev); + struct gk20a *g = platform->g; + + return vgpu_clk_get_freqs(g, freqs, num_freqs); +} + +int vgpu_plat_clk_cap_rate(struct device *dev, unsigned long rate) +{ + struct gk20a_platform *platform = gk20a_get_platform(dev); + struct gk20a *g = platform->g; + + return vgpu_clk_cap_rate(g, rate); +} + +struct gk20a_platform vgpu_tegra_platform = { + .has_syncpoints = true, + .aggressive_sync_destroy_thresh = 64, + + /* power management configuration */ + .can_railgate_init = false, + .can_elpg_init = false, + .enable_slcg = false, + .enable_blcg = false, + .enable_elcg = false, + .enable_elpg = false, + .enable_aelpg = false, + .can_slcg = false, + .can_blcg = false, + .can_elcg = false, + + .ch_wdt_timeout_ms = 5000, + + .probe = gk20a_tegra_probe, + + .clk_round_rate = vgpu_plat_clk_round_rate, + .get_clk_freqs = vgpu_plat_clk_get_freqs, + + /* frequency scaling configuration */ + .devfreq_governor = "userspace", + + .virtual_dev = true, + + /* power management callbacks */ + .suspend = vgpu_tegra_suspend, + .resume = vgpu_tegra_resume, +}; diff --git a/include/os/linux/vgpu/platform_vgpu_tegra.h b/include/os/linux/vgpu/platform_vgpu_tegra.h new file mode 100644 index 0000000..fef346d --- /dev/null +++ b/include/os/linux/vgpu/platform_vgpu_tegra.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2018, 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef _VGPU_PLATFORM_H_ +#define _VGPU_PLATFORM_H_ + +long vgpu_plat_clk_round_rate(struct device *dev, unsigned long rate); +int vgpu_plat_clk_get_freqs(struct device *dev, unsigned long **freqs, + int *num_freqs); +int vgpu_plat_clk_cap_rate(struct device *dev, unsigned long rate); +#endif diff --git a/include/os/linux/vgpu/sysfs_vgpu.c b/include/os/linux/vgpu/sysfs_vgpu.c new file mode 100644 index 0000000..ade5d82 --- /dev/null +++ b/include/os/linux/vgpu/sysfs_vgpu.c @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2017-2019, 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include "os/linux/platform_gk20a.h" +#include "os/linux/os_linux.h" +#include "vgpu/ecc_vgpu.h" + +static ssize_t vgpu_load_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct gk20a *g = get_gk20a(dev); + struct tegra_vgpu_cmd_msg msg = {0}; + struct tegra_vgpu_gpu_load_params *p = &msg.params.gpu_load; + int err; + + msg.cmd = TEGRA_VGPU_CMD_GET_GPU_LOAD; + msg.handle = vgpu_get_handle(g); + err = vgpu_comm_sendrecv(&msg, sizeof(msg), sizeof(msg)); + if (err) + return err; + + return snprintf(buf, PAGE_SIZE, "%u\n", p->load); +} +static DEVICE_ATTR(load, S_IRUGO, vgpu_load_show, NULL); + +static ssize_t vgpu_ecc_stat_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct gk20a *g = get_gk20a(dev); + struct tegra_vgpu_cmd_msg msg = {0}; + struct tegra_vgpu_ecc_counter_params *p = &msg.params.ecc_counter; + struct dev_ext_attribute *ext_attr = container_of(attr, + struct dev_ext_attribute, attr); + struct vgpu_ecc_stat *ecc_stat = ext_attr->var; + int err; + + p->ecc_id = ecc_stat->ecc_id; + + msg.cmd = TEGRA_VGPU_CMD_GET_ECC_COUNTER_VALUE; + msg.handle = vgpu_get_handle(g); + err = vgpu_comm_sendrecv(&msg, sizeof(msg), sizeof(msg)); + err = err ? err : msg.ret; + if (unlikely(err)) { + nvgpu_err(g, "ecc: cannot get ECC counter value: %d", err); + return err; + } + + return snprintf(buf, PAGE_SIZE, "%u\n", p->value); +} + +static int vgpu_create_ecc_sysfs(struct device *dev) +{ + struct gk20a *g = get_gk20a(dev); + struct nvgpu_os_linux *l = nvgpu_os_linux_from_gk20a(g); + struct vgpu_priv_data *priv = vgpu_get_priv_data(g); + struct vgpu_ecc_stat *stats; + struct dev_ext_attribute *attrs; + int err, i, count; + + err = vgpu_ecc_get_info(g); + if (unlikely(err)) { + nvgpu_err(g, "ecc: cannot get ECC info: %d", err); + return err; + } + + stats = priv->ecc_stats; + count = priv->ecc_stats_count; + + attrs = nvgpu_kzalloc(g, count * sizeof(*attrs)); + if (unlikely(!attrs)) { + nvgpu_err(g, "ecc: no memory"); + vgpu_ecc_remove_info(g); + return -ENOMEM; + } + + for (i = 0; i < count; i++) { + sysfs_attr_init(&attrs[i].attr.attr); + attrs[i].attr.attr.name = stats[i].name; + attrs[i].attr.attr.mode = VERIFY_OCTAL_PERMISSIONS(S_IRUGO); + attrs[i].attr.show = vgpu_ecc_stat_show; + attrs[i].attr.store = NULL; + attrs[i].var = &stats[i]; + + err = device_create_file(dev, &attrs[i].attr); + if (unlikely(err)) { + nvgpu_warn(g, "ecc: cannot create file \"%s\": %d", + stats[i].name, err); + } + } + + l->ecc_attrs = attrs; + return 0; +} + +static void vgpu_remove_ecc_sysfs(struct device *dev) +{ + struct gk20a *g = get_gk20a(dev); + struct nvgpu_os_linux *l = nvgpu_os_linux_from_gk20a(g); + struct vgpu_priv_data *priv = vgpu_get_priv_data(g); + int i; + + if (l->ecc_attrs) { + for (i = 0; i < priv->ecc_stats_count; i++) + device_remove_file(dev, &l->ecc_attrs[i].attr); + + nvgpu_kfree(g, l->ecc_attrs); + l->ecc_attrs = NULL; + } + + vgpu_ecc_remove_info(g); +} + +void vgpu_create_sysfs(struct device *dev) +{ + if (device_create_file(dev, &dev_attr_load)) + dev_err(dev, "Failed to create vgpu sysfs attributes!\n"); + + vgpu_create_ecc_sysfs(dev); +} + +void vgpu_remove_sysfs(struct device *dev) +{ + device_remove_file(dev, &dev_attr_load); + vgpu_remove_ecc_sysfs(dev); +} diff --git a/include/os/linux/vgpu/vgpu_ivc.c b/include/os/linux/vgpu/vgpu_ivc.c new file mode 100644 index 0000000..950f0d4 --- /dev/null +++ b/include/os/linux/vgpu/vgpu_ivc.c @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2018, 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include "os/linux/os_linux.h" + +int vgpu_ivc_init(struct gk20a *g, u32 elems, + const size_t *queue_sizes, u32 queue_start, u32 num_queues) +{ + struct platform_device *pdev = to_platform_device(dev_from_gk20a(g)); + + return tegra_gr_comm_init(pdev, elems, queue_sizes, queue_start, + num_queues); +} + +void vgpu_ivc_deinit(u32 queue_start, u32 num_queues) +{ + tegra_gr_comm_deinit(queue_start, num_queues); +} + +void vgpu_ivc_release(void *handle) +{ + tegra_gr_comm_release(handle); +} + +u32 vgpu_ivc_get_server_vmid(void) +{ + return tegra_gr_comm_get_server_vmid(); +} + +int vgpu_ivc_recv(u32 index, void **handle, void **data, + size_t *size, u32 *sender) +{ + return tegra_gr_comm_recv(index, handle, data, size, sender); +} + +int vgpu_ivc_send(u32 peer, u32 index, void *data, size_t size) +{ + return tegra_gr_comm_send(peer, index, data, size); +} + +int vgpu_ivc_sendrecv(u32 peer, u32 index, void **handle, + void **data, size_t *size) +{ + return tegra_gr_comm_sendrecv(peer, index, handle, data, size); +} + +u32 vgpu_ivc_get_peer_self(void) +{ + return TEGRA_GR_COMM_ID_SELF; +} + +void *vgpu_ivc_oob_get_ptr(u32 peer, u32 index, void **ptr, + size_t *size) +{ + return tegra_gr_comm_oob_get_ptr(peer, index, ptr, size); +} + +void vgpu_ivc_oob_put_ptr(void *handle) +{ + tegra_gr_comm_oob_put_ptr(handle); +} diff --git a/include/os/linux/vgpu/vgpu_ivm.c b/include/os/linux/vgpu/vgpu_ivm.c new file mode 100644 index 0000000..bbd444d --- /dev/null +++ b/include/os/linux/vgpu/vgpu_ivm.c @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2018, 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include + +#include "os/linux/os_linux.h" + +struct tegra_hv_ivm_cookie *vgpu_ivm_mempool_reserve(unsigned int id) +{ + return tegra_hv_mempool_reserve(id); +} + +int vgpu_ivm_mempool_unreserve(struct tegra_hv_ivm_cookie *cookie) +{ + return tegra_hv_mempool_unreserve(cookie); +} + +u64 vgpu_ivm_get_ipa(struct tegra_hv_ivm_cookie *cookie) +{ + return cookie->ipa; +} + +u64 vgpu_ivm_get_size(struct tegra_hv_ivm_cookie *cookie) +{ + return cookie->size; +} + +void *vgpu_ivm_mempool_map(struct tegra_hv_ivm_cookie *cookie) +{ + return ioremap_cache(vgpu_ivm_get_ipa(cookie), + vgpu_ivm_get_size(cookie)); +} + +void vgpu_ivm_mempool_unmap(struct tegra_hv_ivm_cookie *cookie, + void *addr) +{ + iounmap(addr); +} diff --git a/include/os/linux/vgpu/vgpu_linux.c b/include/os/linux/vgpu/vgpu_linux.c new file mode 100644 index 0000000..80bcfff --- /dev/null +++ b/include/os/linux/vgpu/vgpu_linux.c @@ -0,0 +1,525 @@ +/* + * Virtualized GPU for Linux + * + * Copyright (c) 2018, 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "vgpu_linux.h" +#include "vgpu/fecs_trace_vgpu.h" +#include "vgpu/clk_vgpu.h" +#include "gk20a/regops_gk20a.h" +#include "gm20b/hal_gm20b.h" + +#include "os/linux/module.h" +#include "os/linux/os_linux.h" +#include "os/linux/ioctl.h" +#include "os/linux/scale.h" +#include "os/linux/driver_common.h" +#include "os/linux/platform_gk20a.h" +#include "os/linux/vgpu/platform_vgpu_tegra.h" + +struct vgpu_priv_data *vgpu_get_priv_data(struct gk20a *g) +{ + struct gk20a_platform *plat = gk20a_get_platform(dev_from_gk20a(g)); + + return (struct vgpu_priv_data *)plat->vgpu_priv; +} + +static void vgpu_remove_support(struct gk20a *g) +{ + vgpu_remove_support_common(g); +} + +static void vgpu_init_vars(struct gk20a *g, struct gk20a_platform *platform) +{ + struct nvgpu_os_linux *l = nvgpu_os_linux_from_gk20a(g); + struct vgpu_priv_data *priv = vgpu_get_priv_data(g); + + nvgpu_mutex_init(&g->power_lock); + nvgpu_mutex_init(&g->ctxsw_disable_lock); + nvgpu_mutex_init(&g->clk_arb_enable_lock); + nvgpu_mutex_init(&g->cg_pg_lock); + + nvgpu_mutex_init(&priv->vgpu_clk_get_freq_lock); + + nvgpu_mutex_init(&l->ctrl.privs_lock); + nvgpu_init_list_node(&l->ctrl.privs); + + l->regs_saved = l->regs; + l->bar1_saved = l->bar1; + + nvgpu_atomic_set(&g->clk_arb_global_nr, 0); + + g->aggressive_sync_destroy = platform->aggressive_sync_destroy; + g->aggressive_sync_destroy_thresh = platform->aggressive_sync_destroy_thresh; + __nvgpu_set_enabled(g, NVGPU_HAS_SYNCPOINTS, platform->has_syncpoints); + g->ptimer_src_freq = platform->ptimer_src_freq; + __nvgpu_set_enabled(g, NVGPU_CAN_RAILGATE, platform->can_railgate_init); + g->railgate_delay = platform->railgate_delay_init; + + __nvgpu_set_enabled(g, NVGPU_MM_UNIFY_ADDRESS_SPACES, + platform->unify_address_spaces); +} + +static int vgpu_init_support(struct platform_device *pdev) +{ + struct resource *r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + struct gk20a *g = get_gk20a(&pdev->dev); + struct nvgpu_os_linux *l = nvgpu_os_linux_from_gk20a(g); + void __iomem *regs; + int err = 0; + + if (!r) { + nvgpu_err(g, "failed to get gk20a bar1"); + err = -ENXIO; + goto fail; + } + + if (r->name && !strcmp(r->name, "/vgpu")) { + regs = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(regs)) { + nvgpu_err(g, "failed to remap gk20a bar1"); + err = PTR_ERR(regs); + goto fail; + } + l->bar1 = regs; + l->bar1_mem = r; + } + + nvgpu_mutex_init(&g->dbg_sessions_lock); + nvgpu_mutex_init(&g->client_lock); + + nvgpu_init_list_node(&g->profiler_objects); + + g->dbg_regops_tmp_buf = nvgpu_kzalloc(g, SZ_4K); + if (!g->dbg_regops_tmp_buf) { + nvgpu_err(g, "couldn't allocate regops tmp buf"); + return -ENOMEM; + } + g->dbg_regops_tmp_buf_ops = + SZ_4K / sizeof(g->dbg_regops_tmp_buf[0]); + + g->remove_support = vgpu_remove_support; + return 0; + + fail: + vgpu_remove_support(g); + return err; +} + +int vgpu_pm_prepare_poweroff(struct device *dev) +{ + struct gk20a *g = get_gk20a(dev); + int ret = 0; + + nvgpu_log_fn(g, " "); + + nvgpu_mutex_acquire(&g->power_lock); + + if (!g->power_on) + goto done; + + if (g->ops.fifo.channel_suspend) + ret = g->ops.fifo.channel_suspend(g); + if (ret) + goto done; + + g->power_on = false; + done: + nvgpu_mutex_release(&g->power_lock); + + return ret; +} + +int vgpu_pm_finalize_poweron(struct device *dev) +{ + struct gk20a *g = get_gk20a(dev); + struct nvgpu_os_linux *l = nvgpu_os_linux_from_gk20a(g); + int err = 0; + + nvgpu_log_fn(g, " "); + + nvgpu_mutex_acquire(&g->power_lock); + + if (g->power_on) + goto done; + + g->power_on = true; + + vgpu_detect_chip(g); + err = vgpu_init_hal(g); + if (err) + goto done; + + if (g->ops.ltc.init_fs_state) + g->ops.ltc.init_fs_state(g); + + err = nvgpu_init_ltc_support(g); + if (err) { + nvgpu_err(g, "failed to init ltc"); + goto done; + } + + err = vgpu_init_mm_support(g); + if (err) { + nvgpu_err(g, "failed to init gk20a mm"); + goto done; + } + + err = vgpu_init_fifo_support(g); + if (err) { + nvgpu_err(g, "failed to init gk20a fifo"); + goto done; + } + + err = vgpu_init_gr_support(g); + if (err) { + nvgpu_err(g, "failed to init gk20a gr"); + goto done; + } + + err = nvgpu_clk_arb_init_arbiter(g); + if (err) { + nvgpu_err(g, "failed to init clk arb"); + goto done; + } + + err = g->ops.chip_init_gpu_characteristics(g); + if (err) { + nvgpu_err(g, "failed to init gk20a gpu characteristics"); + goto done; + } + + err = nvgpu_finalize_poweron_linux(l); + if (err) + goto done; + +#ifdef CONFIG_GK20A_CTXSW_TRACE + gk20a_ctxsw_trace_init(g); +#endif + gk20a_sched_ctrl_init(g); + gk20a_channel_resume(g); + + g->sw_ready = true; + +done: + if (err) + g->power_on = false; + + nvgpu_mutex_release(&g->power_lock); + return err; +} + +static int vgpu_qos_notify(struct notifier_block *nb, + unsigned long n, void *data) +{ + struct gk20a_scale_profile *profile = + container_of(nb, struct gk20a_scale_profile, + qos_notify_block); + struct gk20a *g = get_gk20a(profile->dev); + u32 max_freq; + int err; + + nvgpu_log_fn(g, " "); + + max_freq = (u32)pm_qos_read_max_bound(PM_QOS_GPU_FREQ_BOUNDS); + err = vgpu_plat_clk_cap_rate(profile->dev, max_freq); + if (err) + nvgpu_err(g, "%s failed, err=%d", __func__, err); + + return NOTIFY_OK; /* need notify call further */ +} + +static int vgpu_pm_qos_init(struct device *dev) +{ + struct gk20a *g = get_gk20a(dev); + struct gk20a_scale_profile *profile = g->scale_profile; + + if (IS_ENABLED(CONFIG_GK20A_DEVFREQ)) { + if (!profile) + return -EINVAL; + } else { + profile = nvgpu_kzalloc(g, sizeof(*profile)); + if (!profile) + return -ENOMEM; + g->scale_profile = profile; + } + + profile->dev = dev; + profile->qos_notify_block.notifier_call = vgpu_qos_notify; + pm_qos_add_max_notifier(PM_QOS_GPU_FREQ_BOUNDS, + &profile->qos_notify_block); + return 0; +} + +static void vgpu_pm_qos_remove(struct device *dev) +{ + struct gk20a *g = get_gk20a(dev); + + pm_qos_remove_max_notifier(PM_QOS_GPU_FREQ_BOUNDS, + &g->scale_profile->qos_notify_block); + nvgpu_kfree(g, g->scale_profile); + g->scale_profile = NULL; +} + +static int vgpu_pm_init(struct device *dev) +{ + struct gk20a *g = get_gk20a(dev); + struct gk20a_platform *platform = gk20a_get_platform(dev); + struct nvgpu_os_linux *l = nvgpu_os_linux_from_gk20a(g); + unsigned long *freqs; + int num_freqs; + int err = 0; + + nvgpu_log_fn(g, " "); + + if (nvgpu_platform_is_simulation(g)) + return 0; + + __pm_runtime_disable(dev, false); + + if (IS_ENABLED(CONFIG_GK20A_DEVFREQ)) + gk20a_scale_init(dev); + + if (l->devfreq) { + /* set min/max frequency based on frequency table */ + err = platform->get_clk_freqs(dev, &freqs, &num_freqs); + if (err) + return err; + + if (num_freqs < 1) + return -EINVAL; + + l->devfreq->min_freq = freqs[0]; + l->devfreq->max_freq = freqs[num_freqs - 1]; + } + + err = vgpu_pm_qos_init(dev); + if (err) + return err; + + return err; +} + +int vgpu_probe(struct platform_device *pdev) +{ + struct nvgpu_os_linux *l; + struct gk20a *gk20a; + int err; + struct device *dev = &pdev->dev; + struct gk20a_platform *platform = gk20a_get_platform(dev); + struct vgpu_priv_data *priv; + + if (!platform) { + dev_err(dev, "no platform data\n"); + return -ENODATA; + } + + l = kzalloc(sizeof(*l), GFP_KERNEL); + if (!l) { + dev_err(dev, "couldn't allocate gk20a support"); + return -ENOMEM; + } + gk20a = &l->g; + + nvgpu_log_fn(gk20a, " "); + + nvgpu_init_gk20a(gk20a); + + nvgpu_kmem_init(gk20a); + + err = nvgpu_init_enabled_flags(gk20a); + if (err) { + kfree(gk20a); + return err; + } + + l->dev = dev; + if (tegra_platform_is_vdk()) + __nvgpu_set_enabled(gk20a, NVGPU_IS_FMODEL, true); + + gk20a->is_virtual = true; + + priv = nvgpu_kzalloc(gk20a, sizeof(*priv)); + if (!priv) { + kfree(gk20a); + return -ENOMEM; + } + + platform->g = gk20a; + platform->vgpu_priv = priv; + + err = gk20a_user_init(dev, INTERFACE_NAME, &nvgpu_class); + if (err) + return err; + + vgpu_init_support(pdev); + + vgpu_init_vars(gk20a, platform); + + init_rwsem(&l->busy_lock); + + nvgpu_spinlock_init(&gk20a->mc_enable_lock); + + gk20a->ch_wdt_timeout_ms = platform->ch_wdt_timeout_ms; + + /* Initialize the platform interface. */ + err = platform->probe(dev); + if (err) { + if (err == -EPROBE_DEFER) + nvgpu_info(gk20a, "platform probe failed"); + else + nvgpu_err(gk20a, "platform probe failed"); + return err; + } + + if (platform->late_probe) { + err = platform->late_probe(dev); + if (err) { + nvgpu_err(gk20a, "late probe failed"); + return err; + } + } + + err = vgpu_comm_init(gk20a); + if (err) { + nvgpu_err(gk20a, "failed to init comm interface"); + return -ENOSYS; + } + + priv->virt_handle = vgpu_connect(); + if (!priv->virt_handle) { + nvgpu_err(gk20a, "failed to connect to server node"); + vgpu_comm_deinit(); + return -ENOSYS; + } + + err = vgpu_get_constants(gk20a); + if (err) { + vgpu_comm_deinit(); + return err; + } + + err = vgpu_pm_init(dev); + if (err) { + nvgpu_err(gk20a, "pm init failed"); + return err; + } + + err = nvgpu_thread_create(&priv->intr_handler, gk20a, + vgpu_intr_thread, "gk20a"); + if (err) + return err; + + gk20a_debug_init(gk20a, "gpu.0"); + + /* Set DMA parameters to allow larger sgt lists */ + dev->dma_parms = &l->dma_parms; + dma_set_max_seg_size(dev, UINT_MAX); + + gk20a->gr_idle_timeout_default = NVGPU_DEFAULT_GR_IDLE_TIMEOUT; + gk20a->timeouts_disabled_by_user = false; + nvgpu_atomic_set(&gk20a->timeouts_disabled_refcount, 0); + + vgpu_create_sysfs(dev); + gk20a_init_gr(gk20a); + + nvgpu_log_info(gk20a, "total ram pages : %lu", totalram_pages); + gk20a->gr.max_comptag_mem = totalram_size_in_mb; + + nvgpu_ref_init(&gk20a->refcount); + + return 0; +} + +int vgpu_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct gk20a *g = get_gk20a(dev); + + nvgpu_log_fn(g, " "); + + vgpu_pm_qos_remove(dev); + if (g->remove_support) + g->remove_support(g); + + vgpu_comm_deinit(); + gk20a_sched_ctrl_cleanup(g); + gk20a_user_deinit(dev, &nvgpu_class); + vgpu_remove_sysfs(dev); + gk20a_get_platform(dev)->g = NULL; + gk20a_put(g); + + return 0; +} + +bool vgpu_is_reduced_bar1(struct gk20a *g) +{ + struct fifo_gk20a *f = &g->fifo; + struct nvgpu_os_linux *l = nvgpu_os_linux_from_gk20a(g); + + return resource_size(l->bar1_mem) == (resource_size_t)f->userd.size; +} + +int vgpu_tegra_suspend(struct device *dev) +{ + struct tegra_vgpu_cmd_msg msg = {}; + struct gk20a *g = get_gk20a(dev); + int err = 0; + + msg.cmd = TEGRA_VGPU_CMD_SUSPEND; + msg.handle = vgpu_get_handle(g); + err = vgpu_comm_sendrecv(&msg, sizeof(msg), sizeof(msg)); + err = err ? err : msg.ret; + if (err) + nvgpu_err(g, "vGPU suspend failed\n"); + + return err; +} + +int vgpu_tegra_resume(struct device *dev) +{ + struct tegra_vgpu_cmd_msg msg = {}; + struct gk20a *g = get_gk20a(dev); + int err = 0; + + msg.cmd = TEGRA_VGPU_CMD_RESUME; + msg.handle = vgpu_get_handle(g); + err = vgpu_comm_sendrecv(&msg, sizeof(msg), sizeof(msg)); + err = err ? err : msg.ret; + if (err) + nvgpu_err(g, "vGPU resume failed\n"); + + return err; +} diff --git a/include/os/linux/vgpu/vgpu_linux.h b/include/os/linux/vgpu/vgpu_linux.h new file mode 100644 index 0000000..ff7d3a6 --- /dev/null +++ b/include/os/linux/vgpu/vgpu_linux.h @@ -0,0 +1,68 @@ +/* + * Virtualized GPU Linux Interfaces + * + * Copyright (c) 2018, 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __VGPU_LINUX_H__ +#define __VGPU_LINUX_H__ + +struct device; +struct platform_device; + +#ifdef CONFIG_TEGRA_GR_VIRTUALIZATION + +#include + +int vgpu_pm_prepare_poweroff(struct device *dev); +int vgpu_pm_finalize_poweron(struct device *dev); +int vgpu_probe(struct platform_device *dev); +int vgpu_remove(struct platform_device *dev); + +void vgpu_create_sysfs(struct device *dev); +void vgpu_remove_sysfs(struct device *dev); + +int vgpu_tegra_suspend(struct device *dev); +int vgpu_tegra_resume(struct device *dev); +#else +/* define placeholders for functions used outside of vgpu */ + +static inline int vgpu_pm_prepare_poweroff(struct device *dev) +{ + return -ENOSYS; +} +static inline int vgpu_pm_finalize_poweron(struct device *dev) +{ + return -ENOSYS; +} +static inline int vgpu_probe(struct platform_device *dev) +{ + return -ENOSYS; +} +static inline int vgpu_remove(struct platform_device *dev) +{ + return -ENOSYS; +} +static inline int vgpu_tegra_suspend(struct device *dev) +{ + return -ENOSYS; +} +static inline int vgpu_tegra_resume(struct device *dev) +{ + return -ENOSYS; +} +#endif + +#endif -- cgit v1.2.2