lkml.org 
[lkml]   [2022]   [Aug]   [15]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[RFC PATCH 07/14] drm/qaic: Add debugfs
    Date
    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@quicinc.com>
    ---
    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
    \
     
     \ /
      Last update: 2022-08-15 21:34    [W:3.934 / U:0.080 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site