Add debugfs entries that dump information about the dma_bridge fifo state and also the SBL boot log. Change-Id: Ib46b84c07c25afcf0ac2c73304cf6275689d002e Signed-off-by: Jeffrey Hugo <quic_jhugo@xxxxxxxxxxx> --- drivers/gpu/drm/qaic/qaic_debugfs.c | 335 ++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/qaic/qaic_debugfs.h | 33 ++++ 2 files changed, 368 insertions(+) create mode 100644 drivers/gpu/drm/qaic/qaic_debugfs.c create mode 100644 drivers/gpu/drm/qaic/qaic_debugfs.h diff --git a/drivers/gpu/drm/qaic/qaic_debugfs.c b/drivers/gpu/drm/qaic/qaic_debugfs.c new file mode 100644 index 0000000..82478e3 --- /dev/null +++ b/drivers/gpu/drm/qaic/qaic_debugfs.c @@ -0,0 +1,335 @@ +// SPDX-License-Identifier: GPL-2.0-only + +/* Copyright (c) 2020, The Linux Foundation. All rights reserved. */ +/* Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved. */ + +#include <linux/debugfs.h> +#include <linux/fs.h> +#include <linux/list.h> +#include <linux/mhi.h> +#include <linux/mutex.h> +#include <linux/pci.h> +#include <linux/seq_file.h> +#include <linux/string.h> +#include <linux/types.h> +#include <linux/workqueue.h> + +#include <drm/drm_file.h> + +#include "qaic.h" +#include "qaic_debugfs.h" + +#define BOOTLOG_POOL_SIZE 16 +#define BOOTLOG_MSG_SIZE 512 + +struct bootlog_msg { + /* Buffer for bootlog messages */ + char str[BOOTLOG_MSG_SIZE]; + /* Root struct of device, used to access device resources */ + struct qaic_device *qdev; + /* Work struct to schedule work coming on QAIC_LOGGING channel */ + struct work_struct work; +}; + +struct bootlog_page { + /* Node in list of bootlog pages maintained by root device struct */ + struct list_head node; + /* Total size of the buffer that holds the bootlogs. It is PAGE_SIZE */ + unsigned int size; + /* Offset for the next bootlog */ + unsigned int offset; +}; + +static int bootlog_show(struct seq_file *s, void *data) +{ + struct qaic_device *qdev = s->private; + struct bootlog_page *page; + void *log; + void *page_end; + + mutex_lock(&qdev->bootlog_mutex); + list_for_each_entry(page, &qdev->bootlog, node) { + log = page + 1; + page_end = (void *)page + page->offset; + while (log < page_end) { + seq_printf(s, "%s", (char *)log); + log += strlen(log) + 1; + } + } + mutex_unlock(&qdev->bootlog_mutex); + + return 0; +} + +static int bootlog_open(struct inode *inode, struct file *file) +{ + struct qaic_device *qdev = inode->i_private; + + return single_open(file, bootlog_show, qdev); +} + +static const struct file_operations bootlog_fops = { + .owner = THIS_MODULE, + .open = bootlog_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int read_dbc_fifo_size(void *data, u64 *value) +{ + struct dma_bridge_chan *dbc = (struct dma_bridge_chan *)data; + + *value = dbc->nelem; + return 0; +} + +static int read_dbc_queued(void *data, u64 *value) +{ + struct dma_bridge_chan *dbc = (struct dma_bridge_chan *)data; + u32 tail, head; + + qaic_data_get_fifo_info(dbc, &head, &tail); + + if (head == U32_MAX || tail == U32_MAX) + *value = 0; + else if (head > tail) + *value = dbc->nelem - head + tail; + else + *value = tail - head; + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(dbc_fifo_size_fops, read_dbc_fifo_size, NULL, "%llu\n"); +DEFINE_SIMPLE_ATTRIBUTE(dbc_queued_fops, read_dbc_queued, NULL, "%llu\n"); + +static void qaic_debugfs_add_dbc_entry(struct qaic_device *qdev, uint16_t dbc_id, + struct dentry *parent) +{ + struct dma_bridge_chan *dbc = &qdev->dbc[dbc_id]; + char name[16]; + + snprintf(name, 16, "%s%03u", QAIC_DEBUGFS_DBC_PREFIX, dbc_id); + + dbc->debugfs_root = debugfs_create_dir(name, parent); + + debugfs_create_file(QAIC_DEBUGFS_DBC_FIFO_SIZE, 0444, dbc->debugfs_root, + dbc, &dbc_fifo_size_fops); + + debugfs_create_file(QAIC_DEBUGFS_DBC_QUEUED, 0444, dbc->debugfs_root, + dbc, &dbc_queued_fops); +} + +void qaic_debugfs_init(struct drm_minor *minor) +{ + struct qaic_drm_device *qddev = minor->dev->dev_private; + struct qaic_device *qdev = qddev->qdev; + int i; + + for (i = 0; i < qdev->num_dbc; ++i) + qaic_debugfs_add_dbc_entry(qdev, i, minor->debugfs_root); + + debugfs_create_file("bootlog", 0444, minor->debugfs_root, qdev, + &bootlog_fops); +} + +static struct bootlog_page *alloc_bootlog_page(struct qaic_device *qdev) +{ + struct bootlog_page *page; + + page = (struct bootlog_page *)__get_free_page(GFP_KERNEL); + if (!page) + return page; + + page->size = PAGE_SIZE; + page->offset = sizeof(*page); + list_add_tail(&page->node, &qdev->bootlog); + + return page; +} + +static int reset_bootlog(struct qaic_device *qdev) +{ + struct bootlog_page *page; + struct bootlog_page *i; + + list_for_each_entry_safe(page, i, &qdev->bootlog, node) { + list_del(&page->node); + free_page((unsigned long)page); + } + + page = alloc_bootlog_page(qdev); + if (!page) + return -ENOMEM; + + return 0; +} + +static void *bootlog_get_space(struct qaic_device *qdev, unsigned int size) +{ + struct bootlog_page *page; + + page = list_last_entry(&qdev->bootlog, struct bootlog_page, node); + + if (size > page->size - sizeof(*page)) + return NULL; + + if (page->offset + size >= page->size) { + page = alloc_bootlog_page(qdev); + if (!page) + return NULL; + } + + return (void *)page + page->offset; +} + +static void bootlog_commit(struct qaic_device *qdev, unsigned int size) +{ + struct bootlog_page *page; + + page = list_last_entry(&qdev->bootlog, struct bootlog_page, node); + + page->offset += size; +} + +static void bootlog_log(struct work_struct *work) +{ + struct bootlog_msg *msg = container_of(work, struct bootlog_msg, work); + struct qaic_device *qdev = msg->qdev; + unsigned int len = strlen(msg->str) + 1; + void *log; + + mutex_lock(&qdev->bootlog_mutex); + log = bootlog_get_space(qdev, len); + if (log) { + memcpy(log, msg, len); + bootlog_commit(qdev, len); + } + mutex_unlock(&qdev->bootlog_mutex); + mhi_queue_buf(qdev->bootlog_ch, DMA_FROM_DEVICE, msg, BOOTLOG_MSG_SIZE, + MHI_EOT); +} + +static int qaic_bootlog_mhi_probe(struct mhi_device *mhi_dev, + const struct mhi_device_id *id) +{ + struct qaic_device *qdev; + struct bootlog_msg *msg; + int ret; + int i; + + qdev = pci_get_drvdata(to_pci_dev(mhi_dev->mhi_cntrl->cntrl_dev)); + + dev_set_drvdata(&mhi_dev->dev, qdev); + qdev->bootlog_ch = mhi_dev; + + qdev->bootlog_wq = alloc_ordered_workqueue("qaic_bootlog", 0); + if (!qdev->bootlog_wq) { + ret = -ENOMEM; + goto fail; + } + + mutex_lock(&qdev->bootlog_mutex); + ret = reset_bootlog(qdev); + mutex_unlock(&qdev->bootlog_mutex); + if (ret) + goto reset_fail; + + ret = mhi_prepare_for_transfer(qdev->bootlog_ch); + + if (ret) + goto prepare_fail; + + for (i = 0; i < BOOTLOG_POOL_SIZE; i++) { + msg = kmalloc(sizeof(*msg), GFP_KERNEL); + if (!msg) { + ret = -ENOMEM; + goto alloc_fail; + } + + msg->qdev = qdev; + INIT_WORK(&msg->work, bootlog_log); + + ret = mhi_queue_buf(qdev->bootlog_ch, DMA_FROM_DEVICE, + msg, BOOTLOG_MSG_SIZE, MHI_EOT); + if (ret) + goto queue_fail; + } + + return 0; + +queue_fail: +alloc_fail: + mhi_unprepare_from_transfer(qdev->bootlog_ch); +prepare_fail: +reset_fail: + flush_workqueue(qdev->bootlog_wq); + destroy_workqueue(qdev->bootlog_wq); +fail: + return ret; +} + +static void qaic_bootlog_mhi_remove(struct mhi_device *mhi_dev) +{ + struct qaic_device *qdev; + + qdev = dev_get_drvdata(&mhi_dev->dev); + + mhi_unprepare_from_transfer(qdev->bootlog_ch); + flush_workqueue(qdev->bootlog_wq); + destroy_workqueue(qdev->bootlog_wq); +} + +static void qaic_bootlog_mhi_ul_xfer_cb(struct mhi_device *mhi_dev, + struct mhi_result *mhi_result) +{ +} + +static void qaic_bootlog_mhi_dl_xfer_cb(struct mhi_device *mhi_dev, + struct mhi_result *mhi_result) +{ + struct qaic_device *qdev = dev_get_drvdata(&mhi_dev->dev); + struct bootlog_msg *msg = mhi_result->buf_addr; + + if (mhi_result->transaction_status) { + kfree(msg); + return; + } + + /* force a null at the end of the transferred string */ + msg->str[mhi_result->bytes_xferd - 1] = 0; + + queue_work(qdev->bootlog_wq, &msg->work); +} + +static const struct mhi_device_id qaic_bootlog_mhi_match_table[] = { + { .chan = "QAIC_LOGGING", }, + {}, +}; + +static struct mhi_driver qaic_bootlog_mhi_driver = { + .id_table = qaic_bootlog_mhi_match_table, + .remove = qaic_bootlog_mhi_remove, + .probe = qaic_bootlog_mhi_probe, + .ul_xfer_cb = qaic_bootlog_mhi_ul_xfer_cb, + .dl_xfer_cb = qaic_bootlog_mhi_dl_xfer_cb, + .driver = { + .name = "qaic_bootlog", + .owner = THIS_MODULE, + }, +}; + +void qaic_logging_register(void) +{ + int ret; + + ret = mhi_driver_register(&qaic_bootlog_mhi_driver); + if (ret) + DRM_DEBUG("qaic: logging register failed %d\n", ret); +} + +void qaic_logging_unregister(void) +{ + mhi_driver_unregister(&qaic_bootlog_mhi_driver); +} diff --git a/drivers/gpu/drm/qaic/qaic_debugfs.h b/drivers/gpu/drm/qaic/qaic_debugfs.h new file mode 100644 index 0000000..3d7878c --- /dev/null +++ b/drivers/gpu/drm/qaic/qaic_debugfs.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +/* Copyright (c) 2020, The Linux Foundation. All rights reserved. */ +/* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. */ + +#ifndef __QAIC_DEBUGFS_H__ +#define __QAIC_DEBUGFS_H__ + +#include <linux/debugfs.h> +#include <linux/pci.h> +#include <drm/drm_file.h> + +#define QAIC_DEBUGFS_ROOT "qaic" +#define QAIC_DEBUGFS_DBC_PREFIX "dbc" +#define QAIC_DEBUGFS_DBC_FIFO_SIZE "fifo_size" +#define QAIC_DEBUGFS_DBC_QUEUED "queued" + +extern struct dentry *qaic_debugfs_dir; + +#ifdef CONFIG_DEBUG_FS + +void qaic_logging_register(void); +void qaic_logging_unregister(void); +void qaic_debugfs_init(struct drm_minor *minor); + +#else /* !CONFIG_DEBUG_FS */ + +void qaic_logging_register(void) {} +void qaic_logging_unregister(void) {} +void qaic_debugfs_init(struct drm_minor *minor) {} + +#endif /* !CONFIG_DEBUG_FS */ +#endif /* __QAIC_DEBUGFS_H__ */ -- 2.7.4