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__ */
