brintos

brintos / linux-shallow public Read only

0
0
Text · 8.1 KiB · 20b653d Raw
339 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2 3/* Copyright (c) 2020, The Linux Foundation. All rights reserved. */4/* Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. */5 6#include <linux/debugfs.h>7#include <linux/device.h>8#include <linux/fs.h>9#include <linux/list.h>10#include <linux/mhi.h>11#include <linux/mutex.h>12#include <linux/overflow.h>13#include <linux/pci.h>14#include <linux/seq_file.h>15#include <linux/sprintf.h>16#include <linux/string.h>17#include <linux/types.h>18#include <linux/workqueue.h>19 20#include "qaic.h"21#include "qaic_debugfs.h"22 23#define BOOTLOG_POOL_SIZE		1624#define BOOTLOG_MSG_SIZE		51225#define QAIC_DBC_DIR_NAME		926 27struct bootlog_msg {28	/* Buffer for bootlog messages */29	char str[BOOTLOG_MSG_SIZE];30	/* Root struct of device, used to access device resources */31	struct qaic_device *qdev;32	/* Work struct to schedule work coming on QAIC_LOGGING channel */33	struct work_struct work;34};35 36struct bootlog_page {37	/* Node in list of bootlog pages maintained by root device struct */38	struct list_head node;39	/* Total size of the buffer that holds the bootlogs. It is PAGE_SIZE */40	unsigned int size;41	/* Offset for the next bootlog */42	unsigned int offset;43};44 45static int bootlog_show(struct seq_file *s, void *unused)46{47	struct bootlog_page *page;48	struct qaic_device *qdev;49	void *page_end;50	void *log;51 52	qdev = s->private;53	mutex_lock(&qdev->bootlog_mutex);54	list_for_each_entry(page, &qdev->bootlog, node) {55		log = page + 1;56		page_end = (void *)page + page->offset;57		while (log < page_end) {58			seq_printf(s, "%s", (char *)log);59			log += strlen(log) + 1;60		}61	}62	mutex_unlock(&qdev->bootlog_mutex);63 64	return 0;65}66 67static int bootlog_fops_open(struct inode *inode, struct file *file)68{69	return single_open(file, bootlog_show, inode->i_private);70}71 72static const struct file_operations bootlog_fops = {73	.owner = THIS_MODULE,74	.open = bootlog_fops_open,75	.read = seq_read,76	.llseek = seq_lseek,77	.release = single_release,78};79 80static int read_dbc_fifo_size(struct seq_file *s, void *unused)81{82	struct dma_bridge_chan *dbc = s->private;83 84	seq_printf(s, "%u\n", dbc->nelem);85	return 0;86}87 88static int fifo_size_open(struct inode *inode, struct file *file)89{90	return single_open(file, read_dbc_fifo_size, inode->i_private);91}92 93static const struct file_operations fifo_size_fops = {94	.owner = THIS_MODULE,95	.open = fifo_size_open,96	.read = seq_read,97	.llseek = seq_lseek,98	.release = single_release,99};100 101static int read_dbc_queued(struct seq_file *s, void *unused)102{103	struct dma_bridge_chan *dbc = s->private;104	u32 tail = 0, head = 0;105 106	qaic_data_get_fifo_info(dbc, &head, &tail);107 108	if (head == U32_MAX || tail == U32_MAX)109		seq_printf(s, "%u\n", 0);110	else if (head > tail)111		seq_printf(s, "%u\n", dbc->nelem - head + tail);112	else113		seq_printf(s, "%u\n", tail - head);114 115	return 0;116}117 118static int queued_open(struct inode *inode, struct file *file)119{120	return single_open(file, read_dbc_queued, inode->i_private);121}122 123static const struct file_operations queued_fops = {124	.owner = THIS_MODULE,125	.open = queued_open,126	.read = seq_read,127	.llseek = seq_lseek,128	.release = single_release,129};130 131void qaic_debugfs_init(struct qaic_drm_device *qddev)132{133	struct qaic_device *qdev = qddev->qdev;134	struct dentry *debugfs_root;135	struct dentry *debugfs_dir;136	char name[QAIC_DBC_DIR_NAME];137	u32 i;138 139	debugfs_root = to_drm(qddev)->debugfs_root;140 141	debugfs_create_file("bootlog", 0400, debugfs_root, qdev, &bootlog_fops);142	/*143	 * 256 dbcs per device is likely the max we will ever see and lets static checking see a144	 * reasonable range.145	 */146	for (i = 0; i < qdev->num_dbc && i < 256; ++i) {147		snprintf(name, QAIC_DBC_DIR_NAME, "dbc%03u", i);148		debugfs_dir = debugfs_create_dir(name, debugfs_root);149		debugfs_create_file("fifo_size", 0400, debugfs_dir, &qdev->dbc[i], &fifo_size_fops);150		debugfs_create_file("queued", 0400, debugfs_dir, &qdev->dbc[i], &queued_fops);151	}152}153 154static struct bootlog_page *alloc_bootlog_page(struct qaic_device *qdev)155{156	struct bootlog_page *page;157 158	page = (struct bootlog_page *)devm_get_free_pages(&qdev->pdev->dev, GFP_KERNEL, 0);159	if (!page)160		return page;161 162	page->size = PAGE_SIZE;163	page->offset = sizeof(*page);164	list_add_tail(&page->node, &qdev->bootlog);165 166	return page;167}168 169static int reset_bootlog(struct qaic_device *qdev)170{171	struct bootlog_page *page;172	struct bootlog_page *i;173 174	mutex_lock(&qdev->bootlog_mutex);175	list_for_each_entry_safe(page, i, &qdev->bootlog, node) {176		list_del(&page->node);177		devm_free_pages(&qdev->pdev->dev, (unsigned long)page);178	}179 180	page = alloc_bootlog_page(qdev);181	mutex_unlock(&qdev->bootlog_mutex);182	if (!page)183		return -ENOMEM;184 185	return 0;186}187 188static void *bootlog_get_space(struct qaic_device *qdev, unsigned int size)189{190	struct bootlog_page *page;191 192	page = list_last_entry(&qdev->bootlog, struct bootlog_page, node);193 194	if (size_add(size, sizeof(*page)) > page->size)195		return NULL;196 197	if (page->offset + size > page->size) {198		page = alloc_bootlog_page(qdev);199		if (!page)200			return NULL;201	}202 203	return (void *)page + page->offset;204}205 206static void bootlog_commit(struct qaic_device *qdev, unsigned int size)207{208	struct bootlog_page *page;209 210	page = list_last_entry(&qdev->bootlog, struct bootlog_page, node);211 212	page->offset += size;213}214 215static void bootlog_log(struct work_struct *work)216{217	struct bootlog_msg *msg = container_of(work, struct bootlog_msg, work);218	unsigned int len = strlen(msg->str) + 1;219	struct qaic_device *qdev = msg->qdev;220	void *log;221 222	mutex_lock(&qdev->bootlog_mutex);223	log = bootlog_get_space(qdev, len);224	if (log) {225		memcpy(log, msg, len);226		bootlog_commit(qdev, len);227	}228	mutex_unlock(&qdev->bootlog_mutex);229 230	if (mhi_queue_buf(qdev->bootlog_ch, DMA_FROM_DEVICE, msg, BOOTLOG_MSG_SIZE, MHI_EOT))231		devm_kfree(&qdev->pdev->dev, msg);232}233 234static int qaic_bootlog_mhi_probe(struct mhi_device *mhi_dev, const struct mhi_device_id *id)235{236	struct qaic_device *qdev = pci_get_drvdata(to_pci_dev(mhi_dev->mhi_cntrl->cntrl_dev));237	struct bootlog_msg *msg;238	int i, ret;239 240	qdev->bootlog_wq = alloc_ordered_workqueue("qaic_bootlog", 0);241	if (!qdev->bootlog_wq) {242		ret = -ENOMEM;243		goto out;244	}245 246	ret = reset_bootlog(qdev);247	if (ret)248		goto destroy_workqueue;249 250	ret = mhi_prepare_for_transfer(mhi_dev);251	if (ret)252		goto destroy_workqueue;253 254	for (i = 0; i < BOOTLOG_POOL_SIZE; i++) {255		msg = devm_kzalloc(&qdev->pdev->dev, sizeof(*msg), GFP_KERNEL);256		if (!msg) {257			ret = -ENOMEM;258			goto mhi_unprepare;259		}260 261		msg->qdev = qdev;262		INIT_WORK(&msg->work, bootlog_log);263 264		ret = mhi_queue_buf(mhi_dev, DMA_FROM_DEVICE, msg, BOOTLOG_MSG_SIZE, MHI_EOT);265		if (ret)266			goto mhi_unprepare;267	}268 269	dev_set_drvdata(&mhi_dev->dev, qdev);270	qdev->bootlog_ch = mhi_dev;271	return 0;272 273mhi_unprepare:274	mhi_unprepare_from_transfer(mhi_dev);275destroy_workqueue:276	flush_workqueue(qdev->bootlog_wq);277	destroy_workqueue(qdev->bootlog_wq);278out:279	return ret;280}281 282static void qaic_bootlog_mhi_remove(struct mhi_device *mhi_dev)283{284	struct qaic_device *qdev;285 286	qdev = dev_get_drvdata(&mhi_dev->dev);287 288	mhi_unprepare_from_transfer(qdev->bootlog_ch);289	flush_workqueue(qdev->bootlog_wq);290	destroy_workqueue(qdev->bootlog_wq);291	qdev->bootlog_ch = NULL;292}293 294static void qaic_bootlog_mhi_ul_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result *mhi_result)295{296}297 298static void qaic_bootlog_mhi_dl_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result *mhi_result)299{300	struct qaic_device *qdev = dev_get_drvdata(&mhi_dev->dev);301	struct bootlog_msg *msg = mhi_result->buf_addr;302 303	if (mhi_result->transaction_status) {304		devm_kfree(&qdev->pdev->dev, msg);305		return;306	}307 308	/* Force a null at the end of the transferred string */309	msg->str[mhi_result->bytes_xferd - 1] = 0;310 311	queue_work(qdev->bootlog_wq, &msg->work);312}313 314static const struct mhi_device_id qaic_bootlog_mhi_match_table[] = {315	{ .chan = "QAIC_LOGGING", },316	{},317};318 319static struct mhi_driver qaic_bootlog_mhi_driver = {320	.id_table = qaic_bootlog_mhi_match_table,321	.remove = qaic_bootlog_mhi_remove,322	.probe = qaic_bootlog_mhi_probe,323	.ul_xfer_cb = qaic_bootlog_mhi_ul_xfer_cb,324	.dl_xfer_cb = qaic_bootlog_mhi_dl_xfer_cb,325	.driver = {326		.name = "qaic_bootlog",327	},328};329 330int qaic_bootlog_register(void)331{332	return mhi_driver_register(&qaic_bootlog_mhi_driver);333}334 335void qaic_bootlog_unregister(void)336{337	mhi_driver_unregister(&qaic_bootlog_mhi_driver);338}339