summaryrefslogtreecommitdiff
path: root/debug.c
diff options
context:
space:
mode:
authorVictor Liu <victorliu@google.com>2024-01-23 12:11:45 -0800
committerCherrypicker Worker QA <android-build-cherrypicker-worker@system.gserviceaccount.com>2024-01-26 16:32:18 +0000
commitd6ba9826149e0b866bb7cb3941f779792c5346e2 (patch)
tree3eb1567a49d00c80f7da835017274ca3b8ad1697 /debug.c
parentba464ee86a61e4db307dea64cecc118a1f6b5706 (diff)
downloadqm35-d6ba9826149e0b866bb7cb3941f779792c5346e2.tar.gz
merge partner-android/kernel/private/google-modules/uwb:android14-gs-pixel-6.1-zuma-pro @ 42cac21da18a20d29eca83be028a902c579df339
Bug: 316022384 Signed-off-by: Victor Liu <victorliu@google.com> (cherry picked from https://partner-android-review.googlesource.com/q/commit:c8831dc674f0fdacc042eba3323ac6b2c61a5233) Merged-In: Ie94435c181e387ef701485781f9ce841093496dc Change-Id: Ie94435c181e387ef701485781f9ce841093496dc
Diffstat (limited to 'debug.c')
-rw-r--r--debug.c511
1 files changed, 511 insertions, 0 deletions
diff --git a/debug.c b/debug.c
new file mode 100644
index 0000000..1432eee
--- /dev/null
+++ b/debug.c
@@ -0,0 +1,511 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file is part of the QM35 UCI stack for linux.
+ *
+ * Copyright (c) 2022 Qorvo US, Inc.
+ *
+ * This software is provided under the GNU General Public License, version 2
+ * (GPLv2), as well as under a Qorvo commercial license.
+ *
+ * You may choose to use this software under the terms of the GPLv2 License,
+ * version 2 ("GPLv2"), as published by the Free Software Foundation.
+ * You should have received a copy of the GPLv2 along with this program. If
+ * not, see <http://www.gnu.org/licenses/>.
+ *
+ * This program is distributed under the GPLv2 in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GPLv2 for more
+ * details.
+ *
+ * If you cannot meet the requirements of the GPLv2, you may not use this
+ * software for any purpose without first obtaining a commercial license from
+ * Qorvo.
+ * Please contact Qorvo to inquire about licensing terms.
+ *
+ * QM35 LOG layer HSSPI Protocol
+ */
+
+#include <linux/debugfs.h>
+#include <linux/poll.h>
+#include <linux/fsnotify.h>
+
+#include <qmrom.h>
+#include <qmrom_spi.h>
+
+#include "qm35.h"
+#include "debug.h"
+#include "hsspi_test.h"
+
+#if IS_ENABLED(CONFIG_QM35_SPI_DEBUG_FW)
+extern void debug_rom_code_init(struct debug *debug);
+#endif
+
+static const struct file_operations debug_enable_fops;
+static const struct file_operations debug_log_level_fops;
+static const struct file_operations debug_test_hsspi_sleep_fops;
+
+static void *priv_from_file(const struct file *filp)
+{
+ return filp->f_path.dentry->d_inode->i_private;
+}
+
+static ssize_t debug_enable_write(struct file *filp, const char __user *buff,
+ size_t count, loff_t *off)
+{
+ struct debug *debug;
+ u8 enabled;
+
+ debug = priv_from_file(filp);
+
+ if (kstrtou8_from_user(buff, count, 10, &enabled))
+ return -EFAULT;
+
+ if (debug->trace_ops)
+ debug->trace_ops->enable_set(debug, enabled == 1 ? 1 : 0);
+ else
+ return -ENOSYS;
+
+ return count;
+}
+
+static ssize_t debug_enable_read(struct file *filp, char __user *buff,
+ size_t count, loff_t *off)
+{
+ char enabled[2];
+ struct debug *debug;
+
+ debug = priv_from_file(filp);
+
+ if (debug->trace_ops)
+ enabled[0] = debug->trace_ops->enable_get(debug) + '0';
+ else
+ return -ENOSYS;
+
+ enabled[1] = '\n';
+
+ return simple_read_from_buffer(buff, count, off, enabled,
+ sizeof(enabled));
+}
+
+static ssize_t debug_log_level_write(struct file *filp, const char __user *buff,
+ size_t count, loff_t *off)
+{
+ u8 log_level = 0;
+ struct log_module *log_module;
+
+ log_module = priv_from_file(filp);
+ if (kstrtou8_from_user(buff, count, 10, &log_level))
+ return -EFAULT;
+
+ if (log_module->debug->trace_ops)
+ log_module->debug->trace_ops->level_set(log_module->debug,
+ log_module, log_level);
+ else
+ return -ENOSYS;
+
+ return count;
+}
+
+static ssize_t debug_log_level_read(struct file *filp, char __user *buff,
+ size_t count, loff_t *off)
+{
+ char log_level[2];
+ struct log_module *log_module;
+
+ log_module = priv_from_file(filp);
+
+ if (log_module->debug->trace_ops)
+ log_level[0] = log_module->debug->trace_ops->level_get(
+ log_module->debug, log_module) +
+ '0';
+ else
+ return -ENOSYS;
+
+ log_level[1] = '\n';
+
+ return simple_read_from_buffer(buff, count, off, log_level,
+ sizeof(log_level));
+}
+
+static ssize_t debug_test_hsspi_sleep_write(struct file *filp,
+ const char __user *buff,
+ size_t count, loff_t *off)
+{
+ int sleep_inter_frame_ms;
+
+ if (kstrtoint_from_user(buff, count, 10, &sleep_inter_frame_ms))
+ return -EFAULT;
+
+ hsspi_test_set_inter_frame_ms(sleep_inter_frame_ms);
+ return count;
+}
+
+static ssize_t debug_traces_read(struct file *filp, char __user *buff,
+ size_t count, loff_t *off)
+{
+ char *entry;
+ rb_entry_size_t entry_size;
+ uint16_t ret;
+ struct debug *debug;
+
+ debug = priv_from_file(filp);
+
+ if (!debug->trace_ops)
+ return -ENOSYS;
+
+ entry_size = debug->trace_ops->trace_get_next_size(debug);
+ if (!entry_size) {
+ if (filp->f_flags & O_NONBLOCK)
+ return 0;
+
+ ret = wait_event_interruptible(
+ debug->wq,
+ (entry_size =
+ debug->trace_ops->trace_get_next_size(debug)));
+ if (ret)
+ return ret;
+ }
+
+ if (entry_size > count)
+ return -EMSGSIZE;
+
+ entry = debug->trace_ops->trace_get_next(debug, &entry_size);
+ if (!entry)
+ return 0;
+
+ ret = copy_to_user(buff, entry, entry_size);
+
+ kfree(entry);
+
+ return ret ? -EFAULT : entry_size;
+}
+
+static __poll_t debug_traces_poll(struct file *filp,
+ struct poll_table_struct *wait)
+{
+ struct debug *debug;
+ __poll_t mask = 0;
+
+ debug = priv_from_file(filp);
+
+ poll_wait(filp, &debug->wq, wait);
+
+ if (debug->trace_ops && debug->trace_ops->trace_next_avail(debug))
+ mask |= POLLIN;
+
+ return mask;
+}
+
+static int debug_traces_open(struct inode *inodep, struct file *filep)
+{
+ struct debug *debug;
+
+ debug = priv_from_file(filep);
+
+ mutex_lock(&debug->pv_filp_lock);
+ if (debug->pv_filp) {
+ mutex_unlock(&debug->pv_filp_lock);
+ return -EBUSY;
+ }
+
+ debug->pv_filp = filep;
+
+ if (debug->trace_ops)
+ debug->trace_ops->trace_reset(debug);
+
+ mutex_unlock(&debug->pv_filp_lock);
+
+ return 0;
+}
+
+static int debug_traces_release(struct inode *inodep, struct file *filep)
+{
+ struct debug *debug;
+
+ debug = priv_from_file(filep);
+
+ mutex_lock(&debug->pv_filp_lock);
+ debug->pv_filp = NULL;
+ mutex_unlock(&debug->pv_filp_lock);
+
+ return 0;
+}
+
+static ssize_t debug_coredump_read(struct file *filep, char __user *buff,
+ size_t count, loff_t *off)
+{
+ struct qm35_ctx *qm35_hdl;
+ struct debug *debug;
+ char *cd;
+ size_t cd_len = 0;
+
+ debug = priv_from_file(filep);
+ qm35_hdl = container_of(debug, struct qm35_ctx, debug);
+
+ if (!debug->coredump_ops)
+ return -ENOSYS;
+
+ cd = debug->coredump_ops->coredump_get(debug, &cd_len);
+
+ return simple_read_from_buffer(buff, count, off, cd, cd_len);
+}
+
+static ssize_t debug_coredump_write(struct file *filp, const char __user *buff,
+ size_t count, loff_t *off)
+{
+ struct debug *debug;
+ u8 force;
+
+ debug = priv_from_file(filp);
+
+ if (kstrtou8_from_user(buff, count, 10, &force))
+ return -EFAULT;
+
+ if (debug->coredump_ops && force != 0)
+ debug->coredump_ops->coredump_force(debug);
+ else if (force == 0)
+ pr_warn("qm35: write non null value to force coredump\n");
+ else
+ return -ENOSYS;
+
+ return count;
+}
+
+static ssize_t debug_hw_reset_write(struct file *filp, const char __user *buff,
+ size_t count, loff_t *off)
+{
+ struct qm35_ctx *qm35_hdl;
+ struct debug *debug;
+ int ret;
+ u8 reset;
+
+ debug = priv_from_file(filp);
+ qm35_hdl = container_of(debug, struct qm35_ctx, debug);
+
+ if (kstrtou8_from_user(buff, count, 10, &reset))
+ return -EFAULT;
+
+ ret = -1;
+ if (reset != 0) {
+ pr_info("qm35: resetting chip...\n");
+ ret = qm35_reset_sync(qm35_hdl);
+ } else
+ pr_warn("qm35: write non null value to force a hw reset\n");
+
+ if (ret)
+ return -ENOSYS;
+
+ return count;
+}
+
+static const struct file_operations debug_enable_fops = {
+ .owner = THIS_MODULE,
+ .write = debug_enable_write,
+ .read = debug_enable_read,
+};
+
+static const struct file_operations debug_log_level_fops = {
+ .owner = THIS_MODULE,
+ .write = debug_log_level_write,
+ .read = debug_log_level_read
+};
+
+static const struct file_operations debug_test_hsspi_sleep_fops = {
+ .owner = THIS_MODULE,
+ .write = debug_test_hsspi_sleep_write
+};
+
+static const struct file_operations debug_traces_fops = {
+ .owner = THIS_MODULE,
+ .open = debug_traces_open,
+ .release = debug_traces_release,
+ .read = debug_traces_read,
+ .poll = debug_traces_poll,
+ .llseek = no_llseek,
+};
+
+static const struct file_operations debug_coredump_fops = {
+ .owner = THIS_MODULE,
+ .read = debug_coredump_read,
+ .write = debug_coredump_write,
+};
+
+static const struct file_operations debug_hw_reset_fops = {
+ .owner = THIS_MODULE,
+ .write = debug_hw_reset_write,
+};
+
+int debug_create_module_entry(struct debug *debug,
+ struct log_module *log_module)
+{
+ struct dentry *dir;
+ struct dentry *file;
+
+ dir = debugfs_create_dir(log_module->name, debug->fw_dir);
+ if (!dir) {
+ pr_err("qm35: failed to create /sys/kernel/debug/uwb0/%s\n",
+ log_module->name);
+ return -1;
+ }
+
+ file = debugfs_create_file("log_level", 0644, dir, log_module,
+ &debug_log_level_fops);
+ if (!file) {
+ pr_err("qm35: failed to create /sys/kernel/debug/uwb0/%s/log_level\n",
+ log_module->name);
+ return -1;
+ }
+
+ pr_info("qm35 debug: created /sys/kernel/debug/uwb0/%s/log_level\n",
+ log_module->name);
+
+ return 0;
+}
+
+void debug_new_trace_available(struct debug *debug)
+{
+ if (debug->pv_filp)
+ fsnotify_modify(debug->pv_filp);
+
+ wake_up_interruptible(&debug->wq);
+}
+
+static int debug_devid_show(struct seq_file *s, void *unused)
+{
+ struct debug *debug = (struct debug *)s->private;
+ uint16_t dev_id;
+ int rc;
+
+ if (debug->trace_ops && debug->trace_ops->get_dev_id) {
+ rc = debug->trace_ops->get_dev_id(debug, &dev_id);
+ if (rc < 0)
+ return -EIO;
+ seq_printf(s, "deca%04x\n", dev_id);
+ }
+ return 0;
+}
+
+static int debug_socid_show(struct seq_file *s, void *unused)
+{
+ struct debug *debug = (struct debug *)s->private;
+ uint8_t soc_id[QM357XX_ROM_SOC_ID_LEN];
+ int rc;
+
+ if (debug->trace_ops && debug->trace_ops->get_soc_id) {
+ rc = debug->trace_ops->get_soc_id(debug, soc_id);
+ if (rc < 0)
+ return -EIO;
+ seq_printf(s, "%*phN\n", QM357XX_ROM_SOC_ID_LEN, soc_id);
+ }
+ return 0;
+}
+
+DEFINE_SHOW_ATTRIBUTE(debug_devid);
+DEFINE_SHOW_ATTRIBUTE(debug_socid);
+
+void debug_soc_info_available(struct debug *debug)
+{
+ struct dentry *file;
+
+ file = debugfs_create_file("dev_id", 0444, debug->chip_dir, debug,
+ &debug_devid_fops);
+ if (!file) {
+ pr_err("qm35: failed to create /sys/kernel/debug/uwb0/fw/dev_id\n");
+ goto unregister;
+ }
+
+ file = debugfs_create_file("soc_id", 0444, debug->chip_dir, debug,
+ &debug_socid_fops);
+ if (!file) {
+ pr_err("qm35: failed to create /sys/kernel/debug/uwb0/fw/soc_id\n");
+ goto unregister;
+ }
+
+ return;
+
+unregister:
+ debugfs_remove_recursive(debug->chip_dir);
+}
+
+int debug_init_root(struct debug *debug, struct dentry *root)
+{
+ debug->root_dir = debugfs_create_dir("uwb0", root);
+ if (!debug->root_dir) {
+ pr_err("qm35: failed to create /sys/kernel/debug/uwb0\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+int debug_init(struct debug *debug)
+{
+ struct dentry *file;
+
+ init_waitqueue_head(&debug->wq);
+ mutex_init(&debug->pv_filp_lock);
+ debug->pv_filp = NULL;
+
+ debug->fw_dir = debugfs_create_dir("fw", debug->root_dir);
+ if (!debug->fw_dir) {
+ pr_err("qm35: failed to create /sys/kernel/debug/uwb0/fw\n");
+ goto unregister;
+ }
+
+ debug->chip_dir = debugfs_create_dir("chip", debug->root_dir);
+ if (!debug->chip_dir) {
+ pr_err("qm35: failed to create /sys/kernel/debug/uwb0/chip\n");
+ goto unregister;
+ }
+
+ file = debugfs_create_file("hw_reset", 0444, debug->chip_dir, debug,
+ &debug_hw_reset_fops);
+ if (!file) {
+ pr_err("qm35: failed to create /sys/kernel/debug/uwb0/chip/hw_reset\n");
+ goto unregister;
+ }
+
+ file = debugfs_create_file("enable", 0644, debug->fw_dir, debug,
+ &debug_enable_fops);
+ if (!file) {
+ pr_err("qm35: failed to create /sys/kernel/debug/uwb0/fw/enable\n");
+ goto unregister;
+ }
+
+ file = debugfs_create_file("traces", 0444, debug->fw_dir, debug,
+ &debug_traces_fops);
+ if (!file) {
+ pr_err("qm35: failed to create /sys/kernel/debug/uwb0/fw/traces\n");
+ goto unregister;
+ }
+
+ file = debugfs_create_file("coredump", 0444, debug->fw_dir, debug,
+ &debug_coredump_fops);
+ if (!file) {
+ pr_err("qm35: failed to create /sys/kernel/debug/uwb0/fw/coredump\n");
+ goto unregister;
+ }
+
+ file = debugfs_create_file("test_sleep_hsspi_ms", 0200, debug->fw_dir,
+ debug, &debug_test_hsspi_sleep_fops);
+ if (!file) {
+ pr_err("qm35: failed to create /sys/kernel/debug/uwb0/fw/test_sleep_hsspi\n");
+ goto unregister;
+ }
+
+#if IS_ENABLED(CONFIG_QM35_SPI_DEBUG_FW)
+ debug_rom_code_init(debug);
+#endif
+
+ return 0;
+
+unregister:
+ debug_deinit(debug);
+ return -1;
+}
+
+void debug_deinit(struct debug *debug)
+{
+ wake_up_interruptible(&debug->wq);
+ debugfs_remove_recursive(debug->root_dir);
+}