From 7520eea4dd48828e98b0bcc719340e0f255f748e Mon Sep 17 00:00:00 2001 From: Shridhar Rasal Date: Fri, 27 Oct 2017 12:08:14 +0530 Subject: [PATCH] video: tegra: host: enable firmware gcov support - add debugfs to enable firmware gcov and dump gcov data - on request for enabling gcov, allocate gcov region and inform firmware about gcov PA - gcda debugfs dumps gcda data stored by firmware Change-Id: Ibca37048120eba21aa5f1d4936bd4ae5254fdddf Signed-off-by: Shridhar Rasal Reviewed-on: https://git-master.nvidia.com/r/1586783 Reviewed-by: mobile promotions Tested-by: mobile promotions --- .../video/tegra/host/nvdla/dla_os_interface.h | 1 + drivers/video/tegra/host/nvdla/nvdla.c | 114 ++++++++++++++++ drivers/video/tegra/host/nvdla/nvdla.h | 13 ++ drivers/video/tegra/host/nvdla/nvdla_debug.c | 122 +++++++++++++++++- 4 files changed, 249 insertions(+), 1 deletion(-) diff --git a/drivers/video/tegra/host/nvdla/dla_os_interface.h b/drivers/video/tegra/host/nvdla/dla_os_interface.h index b74c6d8b..8a1de709 100644 --- a/drivers/video/tegra/host/nvdla/dla_os_interface.h +++ b/drivers/video/tegra/host/nvdla/dla_os_interface.h @@ -250,6 +250,7 @@ enum dla_regions_e { DLA_REGION_PRINTF = 1, DLA_REGION_GOS = 2, DLA_REGION_TRACE = 3, + DLA_REGION_GCOV = 4, }; /** diff --git a/drivers/video/tegra/host/nvdla/nvdla.c b/drivers/video/tegra/host/nvdla/nvdla.c index 84483f55..817d2055 100644 --- a/drivers/video/tegra/host/nvdla/nvdla.c +++ b/drivers/video/tegra/host/nvdla/nvdla.c @@ -224,6 +224,118 @@ int nvdla_send_cmd(struct platform_device *pdev, return ret; } +static int nvdla_set_gcov_region(struct platform_device *pdev, bool unset_region) +{ + int err = 0; + struct nvdla_cmd_mem_info gcov_cmd_mem_info; + struct nvdla_cmd_data cmd_data; + struct dla_region_printf *gcov_region = NULL; + struct nvhost_device_data *pdata = platform_get_drvdata(pdev); + struct nvdla_device *nvdla_dev = pdata->private_data; + + if (!pdata->flcn_isr) + return 0; + + err = nvhost_module_busy(pdev); + if (err) { + nvdla_dbg_err(pdev, "failed to power on\n"); + err = -ENODEV; + goto fail_to_power_on; + } + + /* assign memory for gcov command */ + err = nvdla_get_cmd_memory(pdev, &gcov_cmd_mem_info); + if (err) { + nvdla_dbg_err(pdev, + "dma allocation failed for gcov command."); + goto alloc_gcov_cmd_failed; + } + + gcov_region = (struct dla_region_printf *)(gcov_cmd_mem_info.va); + gcov_region->region = DLA_REGION_GCOV; + if (unset_region) + gcov_region->address = 0; + else + gcov_region->address = nvdla_dev->gcov_dump_pa; + gcov_region->size = GCOV_BUFFER_SIZE; + + cmd_data.method_id = DLA_CMD_SET_REGIONS; + cmd_data.method_data = ALIGNED_DMA(gcov_cmd_mem_info.pa); + cmd_data.wait = true; + + err = nvdla_send_cmd(pdev, &cmd_data); + + /* release memory allocated for gcov command */ + nvdla_put_cmd_memory(pdev, gcov_cmd_mem_info.index); + + if (err != 0) { + nvdla_dbg_err(pdev, "failed to send gcov command"); + goto gcov_send_cmd_failed; + } + + nvhost_module_idle(pdev); + + return err; + +gcov_send_cmd_failed: +alloc_gcov_cmd_failed: + nvhost_module_idle(pdev); +fail_to_power_on: + return err; +} + +int nvdla_free_gcov_region(struct platform_device *pdev, bool update_region) +{ + struct nvhost_device_data *pdata = platform_get_drvdata(pdev); + struct nvdla_device *nvdla_dev = pdata->private_data; + int ret = 0; + + if (update_region) { + ret = nvdla_set_gcov_region(pdev, true); + if (ret) + return ret; + } + + if (nvdla_dev->gcov_dump_pa) { + dma_free_attrs(&pdev->dev, GCOV_BUFFER_SIZE, + nvdla_dev->gcov_dump_va, + nvdla_dev->gcov_dump_pa, + __DMA_ATTR(attrs)); + nvdla_dev->gcov_dump_va = NULL; + nvdla_dev->gcov_dump_pa = 0; + } + + return 0; +} + +int nvdla_alloc_gcov_region(struct platform_device *pdev) +{ + int err = 0; + struct nvhost_device_data *pdata = platform_get_drvdata(pdev); + struct nvdla_device *nvdla_dev = pdata->private_data; + + /* Gcov buffer allocation must be done at once only. */ + if (!nvdla_dev->gcov_dump_va) { + /* allocate gcov region */ + nvdla_dev->gcov_dump_va = dma_alloc_attrs(&pdev->dev, + GCOV_BUFFER_SIZE, &nvdla_dev->gcov_dump_pa, + GFP_KERNEL, __DMA_ATTR(attrs)); + + if (!nvdla_dev->gcov_dump_va) { + nvdla_dbg_err(pdev, + "dma gcov memory allocation failed"); + err = -ENOMEM; + goto fail_alloc_gcov_dma; + } + } + err = nvdla_set_gcov_region(pdev, false); + if (err) + nvdla_free_gcov_region(pdev, false); + +fail_alloc_gcov_dma: + return err; +} + static int nvdla_alloc_trace_region(struct platform_device *pdev) { int err = 0; @@ -644,6 +756,8 @@ static int __exit nvdla_remove(struct platform_device *pdev) nvhost_queue_deinit(nvdla_dev->pool); nvhost_client_device_release(pdev); + nvdla_free_gcov_region(pdev, false); + if (nvdla_dev->trace_dump_pa) { dma_free_attrs(&pdev->dev, TRACE_BUFFER_SIZE, nvdla_dev->trace_dump_va, diff --git a/drivers/video/tegra/host/nvdla/nvdla.h b/drivers/video/tegra/host/nvdla/nvdla.h index 83b26c62..87f62a9c 100644 --- a/drivers/video/tegra/host/nvdla/nvdla.h +++ b/drivers/video/tegra/host/nvdla/nvdla.h @@ -87,6 +87,11 @@ */ #define DEBUG_BUFFER_SIZE SZ_256 +/** + * Firmware GCOV Buffer Size + */ +#define GCOV_BUFFER_SIZE SZ_64K + /* * CMD submission timeout in msec */ @@ -167,6 +172,9 @@ struct nvdla_cmd_data { * @debug_dump_va virtual address of print buffer * @trace_dump_pa physical address of trace buffer * @trace_dump_va virtual address of trace buffer + * @en_fw_gcov flag to enable firmware gcov + * @gcov_dump_pa physical address of fw gcov buffer + * @gcov_dump_va virtual address of fw gcovbuffer */ struct nvdla_device { struct platform_device *pdev; @@ -186,6 +194,9 @@ struct nvdla_device { u32 *debug_dump_va; dma_addr_t trace_dump_pa; u32 *trace_dump_va; + u32 en_fw_gcov; + dma_addr_t gcov_dump_pa; + u32 *gcov_dump_va; }; /** @@ -350,5 +361,7 @@ int nvdla_get_task_mem(struct nvhost_queue *queue, struct nvdla_task **task); void nvdla_put_task_mem(struct nvdla_task *task); size_t nvdla_get_max_task_size(void); +int nvdla_alloc_gcov_region(struct platform_device *pdev); +int nvdla_free_gcov_region(struct platform_device *pdev, bool update_region); #endif /* End of __NVHOST_NVDLA_H__ */ diff --git a/drivers/video/tegra/host/nvdla/nvdla_debug.c b/drivers/video/tegra/host/nvdla/nvdla_debug.c index 13da6703..28a9addc 100644 --- a/drivers/video/tegra/host/nvdla/nvdla_debug.c +++ b/drivers/video/tegra/host/nvdla/nvdla_debug.c @@ -208,6 +208,87 @@ static int debug_dla_bintracedump_show(struct seq_file *s, void *data) return 0; } +static int debug_dla_en_fw_gcov_show(struct seq_file *s, void *data) +{ + struct nvdla_device *nvdla_dev = (struct nvdla_device *)s->private; + + seq_printf(s, "%u\n", nvdla_dev->en_fw_gcov); + return 0; +} + +static ssize_t debug_dla_en_fw_gcov_alloc(struct file *file, + const char __user *buffer, size_t count, loff_t *off) +{ + int ret; + u32 val; + struct nvdla_device *nvdla_dev; + struct platform_device *pdev; + struct seq_file *p = file->private_data; + char str[] = "0123456789abcdef"; + + nvdla_dev = (struct nvdla_device *)p->private; + pdev = nvdla_dev->pdev; + count = min_t(size_t, strlen(str), count); + if (copy_from_user(str, buffer, count)) + return -EFAULT; + + mutex_lock(&p->lock); + /* get value entered by user in variable val */ + ret = sscanf(str, "%u", &val); + mutex_unlock(&p->lock); + + if (ret != 1) { + nvdla_dbg_err(pdev, "Incorrect input!"); + goto invalid_input; + } + + /* alloc gcov region */ + if (val == 1) { + ret = nvdla_alloc_gcov_region(pdev); + if (ret) { + nvdla_dbg_err(pdev, "failed to allocate gcov region."); + goto op_failed; + } + nvdla_dev->en_fw_gcov = 1; + } else if (val == 0) { + if (nvdla_dev->en_fw_gcov == 0) + return count; + ret = nvdla_free_gcov_region(pdev, true); + if (ret) { + nvdla_dbg_err(pdev, "failed to free gcov region."); + goto op_failed; + } + nvdla_dev->en_fw_gcov = 0; + } else { + nvdla_dbg_err(pdev, "inval i/p. Valid i/p: 0 and 1"); + ret = -EINVAL; + goto op_failed; + } + + return count; + +op_failed: +invalid_input: + return ret; +} + +static int debug_dla_fw_gcov_gcda_show(struct seq_file *s, void *data) +{ + char *bufptr; + uint32_t datasize; + struct nvdla_device *nvdla_dev; + + nvdla_dev = (struct nvdla_device *)s->private; + if (nvdla_dev->gcov_dump_va && nvdla_dev->en_fw_gcov) { + bufptr = (char *)nvdla_dev->gcov_dump_va; + + datasize = (uint32_t)GCOV_BUFFER_SIZE; + seq_write(s, bufptr, datasize); + } + + return 0; +} + static int debug_dla_enable_trace_open(struct inode *inode, struct file *file) { return single_open(file, debug_dla_enable_trace_show, inode->i_private); @@ -233,6 +314,16 @@ static int debug_dla_bintrace_open(struct inode *inode, struct file *file) return single_open(file, debug_dla_bintracedump_show, inode->i_private); } +static int debug_dla_en_fw_gcov_open(struct inode *inode, struct file *file) +{ + return single_open(file, debug_dla_en_fw_gcov_show, inode->i_private); +} + +static int debug_dla_fw_gcov_gcda_open(struct inode *inode, struct file *file) +{ + return single_open(file, debug_dla_fw_gcov_gcda_show, inode->i_private); +} + static int debug_set_trace_event_config(struct platform_device *pdev, u32 value, u32 sub_cmd) { @@ -430,9 +521,24 @@ static const struct file_operations debug_dla_bin_event_trace_fops = { .release = single_release, }; +static const struct file_operations debug_dla_en_fw_gcov_fops = { + .open = debug_dla_en_fw_gcov_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = debug_dla_en_fw_gcov_alloc, +}; + +static const struct file_operations debug_dla_fw_gcov_gcda_fops = { + .open = debug_dla_fw_gcov_gcda_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + static void dla_fw_debugfs_init(struct platform_device *pdev) { - struct dentry *fw_dir, *fw_trace, *events; + struct dentry *fw_dir, *fw_trace, *events, *fw_gcov; struct nvhost_device_data *pdata = platform_get_drvdata(pdev); struct nvdla_device *nvdla_dev = pdata->private_data; struct dentry *dla_debugfs_root = pdata->debugfs; @@ -478,8 +584,22 @@ static void dla_fw_debugfs_init(struct platform_device *pdev) goto event_failed; } + fw_gcov = debugfs_create_dir("gcov", fw_dir); + if (!fw_gcov) + goto gcov_failed; + + if (!debugfs_create_file("enable", S_IRUGO | S_IWUSR, fw_gcov, + nvdla_dev, &debug_dla_en_fw_gcov_fops)) + goto gcov_failed; + + if (!debugfs_create_file("gcda", S_IRUGO, fw_gcov, + nvdla_dev, &debug_dla_fw_gcov_gcda_fops)) + goto gcov_failed; + return; +gcov_failed: + debugfs_remove_recursive(fw_gcov); event_failed: debugfs_remove_recursive(events); return;