// SPDX-License-Identifier: GPL-2.0-only /* SPDX-FileCopyrightText: Copyright (c) 2016-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * * NVDLA debug utils */ #include #include #include "port/nvdla_host_wrapper.h" #include #include #include #include #include "dla_os_interface.h" #include "nvdla.h" #include "nvdla_debug.h" #include "port/nvdla_fw.h" #include "port/nvdla_device.h" /* * Header in ring buffer consist (start, end) two uint32_t values. * Trace data content starts from the offset below. */ #define TRACE_DATA_OFFSET (2 * sizeof(uint32_t)) #define dla_set_trace_enable(pdev, trace_enable) \ debug_set_trace_event_config(pdev, trace_enable, \ DLA_SET_TRACE_ENABLE); \ #define dla_set_trace_event_mask(pdev, event_mask) \ debug_set_trace_event_config(pdev, event_mask, \ DLA_SET_TRACE_EVENT_MASK); \ static int nvdla_fw_ver_show(struct seq_file *s, void *unused) { struct nvdla_device *nvdla_dev; struct platform_device *pdev; int err; nvdla_dev = (struct nvdla_device *)s->private; pdev = nvdla_dev->pdev; /* update fw_version if engine is not yet powered on */ err = nvdla_module_busy(pdev); if (err) return err; nvdla_module_idle(pdev); seq_printf(s, "%u.%u.%u\n", ((nvdla_dev->fw_version >> 16) & 0xff), ((nvdla_dev->fw_version >> 8) & 0xff), (nvdla_dev->fw_version & 0xff)); return 0; } static int nvdla_fw_ver_open(struct inode *inode, struct file *file) { return single_open(file, nvdla_fw_ver_show, inode->i_private); } static const struct file_operations nvdla_fw_ver_fops = { .open = nvdla_fw_ver_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; static int debug_dla_tracedump_show(struct seq_file *s, void *data) { char *bufptr; struct nvdla_device *nvdla_dev; struct platform_device *pdev; uint32_t i = 0, cindex = 0; uint32_t offset = TRACE_DATA_OFFSET; uint32_t start, end, datasize; nvdla_dev = (struct nvdla_device *)s->private; pdev = nvdla_dev->pdev; if (nvdla_dev->trace_dump_va && nvdla_dev->trace_enable) { bufptr = (char *)nvdla_dev->trace_dump_va; if (!strcmp(bufptr, "")) return 0; memcpy(&start, bufptr, sizeof(uint32_t)); memcpy(&end, ((char *)bufptr + sizeof(uint32_t)), sizeof(uint32_t)); i = start; if (start == (end + 1)) datasize = (uint32_t)TRACE_BUFFER_SIZE - offset; else datasize = end - start; while (cindex < datasize) { seq_printf(s, "%c", bufptr[i]); i++; i = ((i - offset) % (TRACE_BUFFER_SIZE - offset)) + offset; cindex++; if ((bufptr[i] == '\n') && (cindex < datasize)) { seq_printf(s, "%c", bufptr[i]); /* skip extra new line chars */ while ((bufptr[i] == '\n') && (cindex < datasize)) { i++; i = ((i - offset) % (TRACE_BUFFER_SIZE - offset)) + offset; cindex++; } } } seq_printf(s, "%c", '\n'); } return 0; } static int debug_dla_enable_trace_show(struct seq_file *s, void *data) { struct nvdla_device *nvdla_dev = (struct nvdla_device *)s->private; seq_printf(s, "%u\n", nvdla_dev->trace_enable); return 0; } static int debug_dla_eventmask_show(struct seq_file *s, void *data) { struct nvdla_device *nvdla_dev = (struct nvdla_device *)s->private; seq_printf(s, "%u\n", nvdla_dev->events_mask); return 0; } static int debug_dla_eventmask_help_show(struct seq_file *s, void *data) { seq_printf(s, "%s\n", "\nDla Firmware has following different tracing categories:"); seq_printf(s, "%s\n", " BIT(0) - Processor\n" " BIT(1) - Falcon\n" " BIT(2) - Events\n" " BIT(3) - Scheduler Queue\n" " BIT(4) - Operation Cache\n"); seq_printf(s, "%s\n", "To enable all type of tracing events," "set all bits ( 0 - 4 ): "); seq_printf(s, "%s\n\n", " echo 31 > events_mask"); return 0; } static int debug_dla_bintracedump_show(struct seq_file *s, void *data) { char *bufptr; struct nvdla_device *nvdla_dev; struct platform_device *pdev; uint32_t i = 0; uint32_t offset = TRACE_DATA_OFFSET; uint32_t start, end, datasize; nvdla_dev = (struct nvdla_device *)s->private; pdev = nvdla_dev->pdev; if (nvdla_dev->trace_dump_va && nvdla_dev->trace_enable) { bufptr = (char *)nvdla_dev->trace_dump_va; if (!strcmp(bufptr, "")) return 0; memcpy(&start, bufptr, sizeof(uint32_t)); memcpy(&end, ((char *)bufptr + sizeof(uint32_t)), sizeof(uint32_t)); i = start; if (start == (end + 1)) datasize = (uint32_t)TRACE_BUFFER_SIZE - offset; else datasize = end - start; /* to read trace buffer from 0th index */ i = 0; /* in this case, datasize includes header data also */ datasize += offset; /* Dump data in binary format. */ while (i < datasize) seq_printf(s, "%c", bufptr[i++]); } 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 nvdla_get_stats(struct nvdla_device *nvdla_dev) { int err = 0; struct nvdla_cmd_data cmd_data; struct platform_device *pdev; /* prepare command data */ cmd_data.method_id = DLA_CMD_GET_STATISTICS; cmd_data.method_data = ALIGNED_DMA(nvdla_dev->utilization_mem_pa); cmd_data.wait = true; pdev = nvdla_dev->pdev; if (pdev == NULL) return -EFAULT; /* pass set debug command to falcon */ err = nvdla_fw_send_cmd(pdev, &cmd_data); if (err != 0) nvdla_dbg_err(pdev, "failed to send get stats command"); return err; } static int debug_dla_fw_resource_util_show(struct seq_file *s, void *data) { int err = 0; struct nvdla_device *nvdla_dev; struct platform_device *pdev; unsigned int utilization, util_rate_characteristic, util_rate_mantissa; if (s == NULL) { err = -EFAULT; goto fail_no_dev; } nvdla_dev = (struct nvdla_device *) s->private; if (nvdla_dev == NULL) { err = -EFAULT; goto fail_no_dev; } pdev = nvdla_dev->pdev; if (pdev == NULL) { err = -EFAULT; goto fail_no_dev; } if (atomic_read(&pdev->dev.power.usage_count) == 0) { /* Print 0% utilization rate if power refcount for DLA * is zero i.e., DLA is not turned on */ util_rate_characteristic = 0; util_rate_mantissa = 0; } else { /* make sure that device is powered on */ err = nvdla_module_busy(pdev); if (err != 0) { nvdla_dbg_err(pdev, "failed to power on\n"); err = -ENODEV; goto fail_no_dev; } err = nvdla_get_stats(nvdla_dev); if (err != 0) { nvdla_dbg_err(pdev, "Failed to send get stats command"); nvdla_module_idle(pdev); goto fail_no_dev; } utilization = *(unsigned int *)nvdla_dev->utilization_mem_va; util_rate_characteristic = (utilization / 10000); util_rate_mantissa = (utilization % 10000); nvdla_module_idle(pdev); } seq_printf(s, "%u.%04u\n", util_rate_characteristic, util_rate_mantissa); fail_no_dev: return err; } static int nvdla_get_window_size(struct nvdla_device *nvdla_dev) { int err = 0; struct nvdla_cmd_data cmd_data; struct platform_device *pdev; /* prepare command data */ cmd_data.method_id = DLA_CMD_GET_STAT_WINDOW_SIZE; cmd_data.method_data = ALIGNED_DMA(nvdla_dev->window_mem_pa); cmd_data.wait = true; pdev = nvdla_dev->pdev; if (pdev == NULL) { err = -EFAULT; goto fail_no_dev; } /* make sure that device is powered on */ err = nvdla_module_busy(pdev); if (err != 0) { nvdla_dbg_err(pdev, "failed to power on\n"); err = -ENODEV; goto fail_no_dev; } /* pass set debug command to falcon */ err = nvdla_fw_send_cmd(pdev, &cmd_data); if (err != 0) { nvdla_dbg_err(pdev, "failed to send set window command"); goto fail_to_send_cmd; } fail_to_send_cmd: nvdla_module_idle(pdev); fail_no_dev: return err; } static int debug_dla_fw_stat_window_show(struct seq_file *s, void *data) { int err; struct nvdla_device *nvdla_dev; struct platform_device *pdev; if (s == NULL) { err = -EFAULT; goto fail; } nvdla_dev = (struct nvdla_device *) s->private; if (nvdla_dev == NULL) { err = -EFAULT; goto fail; } pdev = nvdla_dev->pdev; if (pdev == NULL) { err = -EFAULT; goto fail; } err = nvdla_get_window_size(nvdla_dev); if (err != 0) { nvdla_dbg_err(pdev, "Failed to get window size"); goto fail; } seq_printf(s, "%u\n", *(unsigned int *)nvdla_dev->window_mem_va); return 0; fail: return err; } static int debug_dla_fw_ping_show(struct seq_file *s, void *data) { (void) data; seq_puts(s, "0\n"); return 0; } /* * When the user calls this debugfs node, the configurable * window size value is passed down to the FW */ static int nvdla_set_window_size(struct nvdla_device *nvdla_dev) { int err = 0; struct nvdla_cmd_data cmd_data; struct platform_device *pdev; /* prepare command data */ cmd_data.method_id = DLA_CMD_SET_STAT_WINDOW_SIZE; cmd_data.method_data = ALIGNED_DMA(nvdla_dev->window_mem_pa); cmd_data.wait = true; pdev = nvdla_dev->pdev; if (pdev == NULL) { err = -EFAULT; goto fail_no_dev; } /* make sure that device is powered on */ err = nvdla_module_busy(pdev); if (err != 0) { nvdla_dbg_err(pdev, "failed to power on\n"); err = -ENODEV; goto fail_no_dev; } /* pass set debug command to falcon */ err = nvdla_fw_send_cmd(pdev, &cmd_data); if (err != 0) { nvdla_dbg_err(pdev, "failed to send set window command"); goto fail_to_send_cmd; } fail_to_send_cmd: nvdla_module_idle(pdev); fail_no_dev: return err; } static ssize_t debug_dla_fw_stat_window_write(struct file *file, const char __user *buffer, size_t count, loff_t *off) { int err; struct seq_file *priv_data; struct nvdla_device *nvdla_dev; struct platform_device *pdev; long write_value; u32 *window_va; /* Fetch user requested write-value. */ err = kstrtol_from_user(buffer, count, 10, &write_value); if (err < 0) goto fail; priv_data = file->private_data; if (priv_data == NULL) goto fail; nvdla_dev = (struct nvdla_device *) priv_data->private; if (nvdla_dev == NULL) goto fail; pdev = nvdla_dev->pdev; if (pdev == NULL) goto fail; window_va = nvdla_dev->window_mem_va; if (write_value < UINT_MAX) *window_va = write_value; err = nvdla_set_window_size(nvdla_dev); if (err != 0) { nvdla_dbg_err(pdev, "Failed to send set window size command"); goto fail; } return count; fail: return -1; } static ssize_t debug_dla_fw_ping_write(struct file *file, const char __user *buffer, size_t count, loff_t *off) { int err; struct seq_file *priv_data; struct nvdla_device *nvdla_dev; struct platform_device *pdev; long write_value; /* Fetch user requested write-value. */ err = kstrtol_from_user(buffer, count, 10, &write_value); if (err < 0) goto fail; priv_data = file->private_data; if (priv_data == NULL) goto fail; nvdla_dev = (struct nvdla_device *) priv_data->private; if (nvdla_dev == NULL) goto fail; pdev = nvdla_dev->pdev; if (pdev == NULL) goto fail; if (write_value > 0) { struct nvdla_ping_args args = { write_value, 0 }; uint32_t golden = (write_value * 4U); nvdla_dbg_info(pdev, "[PING] challenge: %u\n", (unsigned int) write_value); nvdla_dbg_info(pdev, "[PING] golden: %u\n", golden); err = nvdla_ping(pdev, &args); if (err < 0) { nvdla_dbg_err(pdev, "failed to ping\n"); goto fail; } if (args.out_response != golden) { nvdla_dbg_err(pdev, "[PING] response != golden (%u != %u)\n", args.out_response, golden); goto fail; } nvdla_dbg_info(pdev, "[PING] successful\n"); } return count; fail: return -1; } 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); } static int debug_dla_eventmask_open(struct inode *inode, struct file *file) { return single_open(file, debug_dla_eventmask_show, inode->i_private); } static int debug_dla_eventmask_help_open(struct inode *inode, struct file *file) { return single_open(file, debug_dla_eventmask_help_show, inode->i_private); } static int debug_dla_trace_open(struct inode *inode, struct file *file) { return single_open(file, debug_dla_tracedump_show, inode->i_private); } 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_dla_fw_resource_util_open(struct inode *inode, struct file *file) { return single_open(file, debug_dla_fw_resource_util_show, inode->i_private); } static int debug_dla_fw_stat_window_open(struct inode *inode, struct file *file) { return single_open(file, debug_dla_fw_stat_window_show, inode->i_private); } static int debug_dla_fw_ping_open(struct inode *inode, struct file *file) { return single_open(file, debug_dla_fw_ping_show, inode->i_private); } static int debug_set_trace_event_config(struct platform_device *pdev, u32 value, u32 sub_cmd) { int err = 0; struct nvdla_cmd_mem_info trace_events_mem_info; struct dla_debug_config *trace_event; struct nvdla_cmd_data cmd_data; /* make sure that device is powered on */ err = nvdla_module_busy(pdev); if (err) { nvdla_dbg_err(pdev, "failed to power on\n"); err = -ENODEV; goto fail_to_on; } /* assign memory for command */ err = nvdla_get_cmd_memory(pdev, &trace_events_mem_info); if (err) { nvdla_dbg_err(pdev, "dma alloc for command failed"); goto alloc_failed; } trace_event = (struct dla_debug_config *)trace_events_mem_info.va; trace_event->sub_cmd = sub_cmd; trace_event->data = (u64)value; /* prepare command data */ cmd_data.method_id = DLA_CMD_SET_DEBUG; cmd_data.method_data = ALIGNED_DMA(trace_events_mem_info.pa); cmd_data.wait = true; /* pass set debug command to falcon */ err = nvdla_fw_send_cmd(pdev, &cmd_data); /* free memory allocated for trace event command */ nvdla_put_cmd_memory(pdev, trace_events_mem_info.index); if (err != 0) { nvdla_dbg_err(pdev, "failed to send set debug command"); goto send_cmd_failed; } nvdla_module_idle(pdev); return err; send_cmd_failed: alloc_failed: nvdla_module_idle(pdev); fail_to_on: return err; } static ssize_t debug_dla_eventmask_set(struct file *file, const char __user *buffer, size_t count, loff_t *off) { int ret; u32 val; struct platform_device *pdev; struct nvdla_device *nvdla_dev; 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); /* Check valid values for event_mask */ if (ret == 1 && val <= 31) nvdla_dev->events_mask = val; mutex_unlock(&p->lock); if (ret != 1) { nvdla_dbg_err(pdev, "Incorrect input!"); goto invalid_input; } /* * Currently only five trace categories are added, * and hence only five bits are being used to enable/disable * the trace categories. */ if (val > 31) { nvdla_dbg_err(pdev, "invalid input, please" " check /d/nvdla*/firmware/trace/events/help"); ret = -EINVAL; goto invalid_input; } /* set event_mask config */ ret = dla_set_trace_event_mask(pdev, nvdla_dev->events_mask); if (ret) { nvdla_dbg_err(pdev, "failed to set event mask."); goto set_event_mask_failed; } return count; set_event_mask_failed: invalid_input: return ret; } static ssize_t debug_dla_enable_trace_set(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); /* Check valid values for trace_enable */ if (ret == 1 && (val == 0 || val == 1)) nvdla_dev->trace_enable = val; mutex_unlock(&p->lock); if (ret != 1) { nvdla_dbg_err(pdev, "Incorrect input!"); goto invalid_input; } if (val != 0 && val != 1) { nvdla_dbg_err(pdev, "invalid input, please" " enter 0(disable) or 1(enable)!"); ret = -EINVAL; goto invalid_input; } /* set trace_enable config */ ret = dla_set_trace_enable(pdev, nvdla_dev->trace_enable); if (ret) { nvdla_dbg_err(pdev, "failed to enable trace events."); goto set_trace_enable_failed; } return count; set_trace_enable_failed: invalid_input: return ret; } static ssize_t debug_dla_fw_reload_set(struct file *file, const char __user *buffer, size_t count, loff_t *off) { int err; struct seq_file *p = file->private_data; struct nvdla_device *nvdla_dev; struct platform_device *pdev; long val; int ref_cnt; unsigned long end_jiffies; if (!p) return -EFAULT; nvdla_dev = (struct nvdla_device *)p->private; if (!nvdla_dev) return -EFAULT; pdev = nvdla_dev->pdev; if (!pdev) return -EFAULT; err = kstrtol_from_user(buffer, count, 10, &val); if (err < 0) return err; if (!val) return count; /* "0" does nothing */ /* check current power ref count and make forced idle to * suspend. */ ref_cnt = atomic_read(&pdev->dev.power.usage_count); nvdla_module_idle_mult(pdev, ref_cnt); /* check and wait until module is idle (with a timeout) */ end_jiffies = jiffies + msecs_to_jiffies(2000); do { msleep(1); ref_cnt = atomic_read(&pdev->dev.power.usage_count); } while (ref_cnt != 0 && time_before(jiffies, end_jiffies)); if (ref_cnt != 0) return -EBUSY; nvdla_dbg_info(pdev, "firmware reload requesting..\n"); err = nvdla_fw_reload(pdev); if (err) return err; /* propagate firmware reload errors */ /* make sure device in clean state by reset */ nvdla_module_reset(pdev, true); return count; } static int debug_dla_fw_reload_show(struct seq_file *s, void *data) { seq_puts(s, "0\n"); return 0; } static int debug_dla_fw_reload_open(struct inode *inode, struct file *file) { return single_open(file, debug_dla_fw_reload_show, inode->i_private); } static const struct file_operations debug_dla_enable_trace_fops = { .open = debug_dla_enable_trace_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, .write = debug_dla_enable_trace_set, }; static const struct file_operations debug_dla_eventmask_fops = { .open = debug_dla_eventmask_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, .write = debug_dla_eventmask_set, }; static const struct file_operations debug_dla_eventmask_help_fops = { .open = debug_dla_eventmask_help_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; static const struct file_operations debug_dla_event_trace_fops = { .open = debug_dla_trace_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; static const struct file_operations debug_dla_bin_event_trace_fops = { .open = debug_dla_bintrace_open, .read = seq_read, .llseek = seq_lseek, .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 const struct file_operations nvdla_fw_reload_fops = { .open = debug_dla_fw_reload_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, .write = debug_dla_fw_reload_set, }; static const struct file_operations debug_dla_resource_util_fops = { .open = debug_dla_fw_resource_util_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; static const struct file_operations debug_dla_stat_window_fops = { .open = debug_dla_fw_stat_window_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, .write = debug_dla_fw_stat_window_write, }; static const struct file_operations debug_dla_ping_fops = { .open = debug_dla_fw_ping_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, .write = debug_dla_fw_ping_write, }; static void dla_fw_debugfs_init(struct platform_device *pdev) { 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; if (!dla_debugfs_root) return; fw_dir = debugfs_create_dir("firmware", dla_debugfs_root); if (!fw_dir) return; if (!debugfs_create_file("version", S_IRUGO, fw_dir, nvdla_dev, &nvdla_fw_ver_fops)) goto trace_failed; if (!debugfs_create_file("reload", 0600, fw_dir, nvdla_dev, &nvdla_fw_reload_fops)) goto trace_failed; fw_trace = debugfs_create_dir("trace", fw_dir); if (!fw_trace) goto trace_failed; if (!debugfs_create_file("enable", S_IRUGO | S_IWUSR, fw_trace, nvdla_dev, &debug_dla_enable_trace_fops)) goto trace_failed; if (!debugfs_create_file("text_trace", S_IRUGO, fw_trace, nvdla_dev, &debug_dla_event_trace_fops)) goto trace_failed; if (!debugfs_create_file("bin_trace", S_IRUGO, fw_trace, nvdla_dev, &debug_dla_bin_event_trace_fops)) goto trace_failed; events = debugfs_create_dir("events", fw_trace); if (!events) goto event_failed; if (!debugfs_create_file("category", S_IWUSR | S_IRUGO, events, nvdla_dev, &debug_dla_eventmask_fops)) { goto event_failed; } if (!debugfs_create_file("help", S_IRUGO, events, nvdla_dev, &debug_dla_eventmask_help_fops)) { 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; if (!debugfs_create_file("utilization_rate", S_IRUSR, fw_dir, nvdla_dev, &debug_dla_resource_util_fops)) goto trace_failed; if (!debugfs_create_file("stat_window_size", S_IRUSR | S_IWUSR, fw_dir, nvdla_dev, &debug_dla_stat_window_fops)) goto trace_failed; if (!debugfs_create_file("ping", 0600, fw_dir, nvdla_dev, &debug_dla_ping_fops)) goto trace_failed; return; gcov_failed: debugfs_remove_recursive(fw_gcov); event_failed: debugfs_remove_recursive(events); return; trace_failed: debugfs_remove_recursive(fw_dir); } #ifdef CONFIG_PM static int debug_dla_pm_suspend_show(struct seq_file *s, void *data) { int err; struct nvdla_device *nvdla_dev; if (s == NULL) { err = -EFAULT; goto fail; } nvdla_dev = (struct nvdla_device *) s->private; if (nvdla_dev == NULL) { err = -EFAULT; goto fail; } seq_printf(s, "%x\n", (int) nvdla_dev->is_suspended); return 0; fail: return err; } static int debug_dla_pm_suspend_open(struct inode *inode, struct file *file) { return single_open(file, debug_dla_pm_suspend_show, inode->i_private); } static ssize_t debug_dla_pm_suspend_write(struct file *file, const char __user *buffer, size_t count, loff_t *off) { int err; struct seq_file *priv_data; struct nvdla_device *nvdla_dev; struct platform_device *pdev; long write_value; /* Fetch user requested write-value. */ err = kstrtol_from_user(buffer, count, 10, &write_value); if (err < 0) goto fail; /* Trigger suspend & response */ priv_data = file->private_data; if (priv_data == NULL) goto fail; nvdla_dev = (struct nvdla_device *) priv_data->private; if (nvdla_dev == NULL) goto fail; pdev = nvdla_dev->pdev; if (pdev == NULL) goto fail; if ((write_value > 0) && (!nvdla_dev->is_suspended)) { /* Trigger suspend sequence. */ err = nvdla_module_pm_ops.prepare(&pdev->dev); if (err < 0) goto fail; err = nvdla_module_pm_ops.suspend(&pdev->dev); if (err < 0) { nvdla_module_pm_ops.complete(&pdev->dev); goto fail; } } else if ((write_value == 0) && (nvdla_dev->is_suspended)) { /* Trigger resume sequence. */ err = nvdla_module_pm_ops.resume(&pdev->dev); if (err < 0) goto fail; nvdla_module_pm_ops.complete(&pdev->dev); } return count; fail: return -1; } static const struct file_operations debug_dla_pm_suspend_fops = { .open = debug_dla_pm_suspend_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, .write = debug_dla_pm_suspend_write, }; static void nvdla_pm_debugfs_init(struct platform_device *pdev) { struct nvhost_device_data *pdata = platform_get_drvdata(pdev); struct nvdla_device *nvdla_dev = pdata->private_data; struct dentry *dla_debugfs_root = pdata->debugfs; if (!debugfs_create_file("suspend", 0600, dla_debugfs_root, nvdla_dev, &debug_dla_pm_suspend_fops)) { goto fail_create_file_suspend; } fail_create_file_suspend: return; } #endif #if defined(NVDLA_HAVE_CONFIG_HSIERRINJ) && (NVDLA_HAVE_CONFIG_HSIERRINJ == 1) static ssize_t debug_dla_err_inj_write(struct file *file, const char __user *buffer, size_t count, loff_t *off) { int err; struct seq_file *priv_data; struct nvdla_device *nvdla_dev; struct platform_device *pdev; struct epl_error_report_frame frame; unsigned int instance_id; struct nvhost_device_data *pdata; long write_value; /* Fetch user requested write-value. */ err = kstrtol_from_user(buffer, count, 10, &write_value); if (err < 0) goto fail; if (write_value > 0) { /* Trigger error injection */ priv_data = file->private_data; if (priv_data == NULL) goto fail; nvdla_dev = (struct nvdla_device *) priv_data->private; if (nvdla_dev == NULL) goto fail; pdev = nvdla_dev->pdev; if (pdev == NULL) goto fail; pdata = platform_get_drvdata(pdev); if (pdata == NULL) goto fail; if (pdata->class == NV_DLA0_CLASS_ID) { instance_id = 0U; frame.error_attribute = 0U; frame.timestamp = 0U; if (write_value == 1) { frame.reporter_id = NVDLA0_UE_HSM_REPORTER_ID; frame.error_code = NVDLA0_UE_HSM_ERROR_CODE; } else if (write_value == 2) { frame.reporter_id = NVDLA0_CE_HSM_REPORTER_ID; frame.error_code = NVDLA0_CE_HSM_ERROR_CODE; } else { nvdla_dbg_err(pdev, "Write value out of range: [0,2]"); goto fail; } } else { instance_id = 1U; frame.error_attribute = 0U; frame.timestamp = 0U; if (write_value == 1) { frame.reporter_id = NVDLA1_UE_HSM_REPORTER_ID; frame.error_code = NVDLA1_UE_HSM_ERROR_CODE; } else if (write_value == 2) { frame.reporter_id = NVDLA1_CE_HSM_REPORTER_ID; frame.error_code = NVDLA1_CE_HSM_ERROR_CODE; } else { nvdla_dbg_err(pdev, "Write value out of range: [0,2]"); goto fail; } } err = nvdla_error_inj_handler(instance_id, frame, (void *) nvdla_dev); if (err < 0) goto fail; } return count; fail: return -1; } static int debug_dla_err_inj_show(struct seq_file *s, void *data) { seq_puts(s, "0\n"); return 0; } static int debug_dla_err_inj_open(struct inode *inode, struct file *file) { return single_open(file, debug_dla_err_inj_show, inode->i_private); } static const struct file_operations debug_dla_err_inj_fops = { .open = debug_dla_err_inj_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, .write = debug_dla_err_inj_write, }; static void nvdla_err_inj_debugfs_init(struct platform_device *pdev) { struct nvhost_device_data *pdata = platform_get_drvdata(pdev); struct nvdla_device *nvdla_dev = pdata->private_data; struct dentry *dla_debugfs_root = pdata->debugfs; if (!debugfs_create_file("hsi_error_inject", 0600, dla_debugfs_root, nvdla_dev, &debug_dla_err_inj_fops)) { goto fail_create_file_err_inj; } fail_create_file_err_inj: return; } #endif /* NVDLA_HAVE_CONFIG_HSIERRINJ */ void nvdla_debug_init(struct platform_device *pdev) { struct nvhost_device_data *pdata = platform_get_drvdata(pdev); struct nvdla_device *nvdla_dev = pdata->private_data; struct dentry *de = pdata->debugfs; if (!de) return; debugfs_create_u32("debug_mask", S_IRUGO | S_IWUSR, de, &nvdla_dev->dbg_mask); debugfs_create_u32("bitbang", 0644, de, &nvdla_dev->bitbang); #ifdef CONFIG_TEGRA_NVDLA_TRACE_PRINTK debugfs_create_u32("en_trace", S_IRUGO | S_IWUSR, de, &nvdla_dev->en_trace); #endif debugfs_create_u32("submit_mode", S_IRUGO | S_IWUSR, de, &nvdla_dev->submit_mode); /* Check if isolate context enabled if submit mode is CHANNEL */ nvdla_dev->submit_mode = nvdla_dev->submit_mode && pdata->isolate_contexts; #if defined(NVDLA_HAVE_CONFIG_HSIERRINJ) && (NVDLA_HAVE_CONFIG_HSIERRINJ == 1) nvdla_err_inj_debugfs_init(pdev); #endif /* NVDLA_HAVE_CONFIG_HSIERRINJ */ #ifdef CONFIG_PM nvdla_pm_debugfs_init(pdev); #endif dla_fw_debugfs_init(pdev); }