Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1// SPDX-License-Identifier: GPL-2.0-only
  2
  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		16
 24#define BOOTLOG_MSG_SIZE		512
 25#define QAIC_DBC_DIR_NAME		9
 26
 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
 67DEFINE_SHOW_ATTRIBUTE(bootlog);
 68
 69static int fifo_size_show(struct seq_file *s, void *unused)
 70{
 71	struct dma_bridge_chan *dbc = s->private;
 72
 73	seq_printf(s, "%u\n", dbc->nelem);
 74	return 0;
 75}
 76
 77DEFINE_SHOW_ATTRIBUTE(fifo_size);
 78
 79static int queued_show(struct seq_file *s, void *unused)
 80{
 81	struct dma_bridge_chan *dbc = s->private;
 82	u32 tail = 0, head = 0;
 83
 84	qaic_data_get_fifo_info(dbc, &head, &tail);
 85
 86	if (head == U32_MAX || tail == U32_MAX)
 87		seq_printf(s, "%u\n", 0);
 88	else if (head > tail)
 89		seq_printf(s, "%u\n", dbc->nelem - head + tail);
 90	else
 91		seq_printf(s, "%u\n", tail - head);
 92
 93	return 0;
 94}
 95
 96DEFINE_SHOW_ATTRIBUTE(queued);
 97
 98void qaic_debugfs_init(struct qaic_drm_device *qddev)
 99{
100	struct qaic_device *qdev = qddev->qdev;
101	struct dentry *debugfs_root;
102	struct dentry *debugfs_dir;
103	char name[QAIC_DBC_DIR_NAME];
104	u32 i;
105
106	debugfs_root = to_drm(qddev)->debugfs_root;
107
108	debugfs_create_file("bootlog", 0400, debugfs_root, qdev, &bootlog_fops);
109	/*
110	 * 256 dbcs per device is likely the max we will ever see and lets static checking see a
111	 * reasonable range.
112	 */
113	for (i = 0; i < qdev->num_dbc && i < 256; ++i) {
114		snprintf(name, QAIC_DBC_DIR_NAME, "dbc%03u", i);
115		debugfs_dir = debugfs_create_dir(name, debugfs_root);
116		debugfs_create_file("fifo_size", 0400, debugfs_dir, &qdev->dbc[i], &fifo_size_fops);
117		debugfs_create_file("queued", 0400, debugfs_dir, &qdev->dbc[i], &queued_fops);
118	}
119}
120
121static struct bootlog_page *alloc_bootlog_page(struct qaic_device *qdev)
122{
123	struct bootlog_page *page;
124
125	page = (struct bootlog_page *)devm_get_free_pages(&qdev->pdev->dev, GFP_KERNEL, 0);
126	if (!page)
127		return page;
128
129	page->size = PAGE_SIZE;
130	page->offset = sizeof(*page);
131	list_add_tail(&page->node, &qdev->bootlog);
132
133	return page;
134}
135
136static int reset_bootlog(struct qaic_device *qdev)
137{
138	struct bootlog_page *page;
139	struct bootlog_page *i;
140
141	mutex_lock(&qdev->bootlog_mutex);
142	list_for_each_entry_safe(page, i, &qdev->bootlog, node) {
143		list_del(&page->node);
144		devm_free_pages(&qdev->pdev->dev, (unsigned long)page);
145	}
146
147	page = alloc_bootlog_page(qdev);
148	mutex_unlock(&qdev->bootlog_mutex);
149	if (!page)
150		return -ENOMEM;
151
152	return 0;
153}
154
155static void *bootlog_get_space(struct qaic_device *qdev, unsigned int size)
156{
157	struct bootlog_page *page;
158
159	page = list_last_entry(&qdev->bootlog, struct bootlog_page, node);
160
161	if (size_add(size, sizeof(*page)) > page->size)
162		return NULL;
163
164	if (page->offset + size > page->size) {
165		page = alloc_bootlog_page(qdev);
166		if (!page)
167			return NULL;
168	}
169
170	return (void *)page + page->offset;
171}
172
173static void bootlog_commit(struct qaic_device *qdev, unsigned int size)
174{
175	struct bootlog_page *page;
176
177	page = list_last_entry(&qdev->bootlog, struct bootlog_page, node);
178
179	page->offset += size;
180}
181
182static void bootlog_log(struct work_struct *work)
183{
184	struct bootlog_msg *msg = container_of(work, struct bootlog_msg, work);
185	unsigned int len = strlen(msg->str) + 1;
186	struct qaic_device *qdev = msg->qdev;
187	void *log;
188
189	mutex_lock(&qdev->bootlog_mutex);
190	log = bootlog_get_space(qdev, len);
191	if (log) {
192		memcpy(log, msg, len);
193		bootlog_commit(qdev, len);
194	}
195	mutex_unlock(&qdev->bootlog_mutex);
196
197	if (mhi_queue_buf(qdev->bootlog_ch, DMA_FROM_DEVICE, msg, BOOTLOG_MSG_SIZE, MHI_EOT))
198		devm_kfree(&qdev->pdev->dev, msg);
199}
200
201static int qaic_bootlog_mhi_probe(struct mhi_device *mhi_dev, const struct mhi_device_id *id)
202{
203	struct qaic_device *qdev = pci_get_drvdata(to_pci_dev(mhi_dev->mhi_cntrl->cntrl_dev));
204	struct bootlog_msg *msg;
205	int i, ret;
206
207	qdev->bootlog_wq = alloc_ordered_workqueue("qaic_bootlog", 0);
208	if (!qdev->bootlog_wq) {
209		ret = -ENOMEM;
210		goto out;
211	}
212
213	ret = reset_bootlog(qdev);
214	if (ret)
215		goto destroy_workqueue;
216
217	ret = mhi_prepare_for_transfer(mhi_dev);
218	if (ret)
219		goto destroy_workqueue;
220
221	for (i = 0; i < BOOTLOG_POOL_SIZE; i++) {
222		msg = devm_kzalloc(&qdev->pdev->dev, sizeof(*msg), GFP_KERNEL);
223		if (!msg) {
224			ret = -ENOMEM;
225			goto mhi_unprepare;
226		}
227
228		msg->qdev = qdev;
229		INIT_WORK(&msg->work, bootlog_log);
230
231		ret = mhi_queue_buf(mhi_dev, DMA_FROM_DEVICE, msg, BOOTLOG_MSG_SIZE, MHI_EOT);
232		if (ret)
233			goto mhi_unprepare;
234	}
235
236	dev_set_drvdata(&mhi_dev->dev, qdev);
237	qdev->bootlog_ch = mhi_dev;
238	return 0;
239
240mhi_unprepare:
241	mhi_unprepare_from_transfer(mhi_dev);
242destroy_workqueue:
243	flush_workqueue(qdev->bootlog_wq);
244	destroy_workqueue(qdev->bootlog_wq);
245out:
246	return ret;
247}
248
249static void qaic_bootlog_mhi_remove(struct mhi_device *mhi_dev)
250{
251	struct qaic_device *qdev;
252
253	qdev = dev_get_drvdata(&mhi_dev->dev);
254
255	mhi_unprepare_from_transfer(qdev->bootlog_ch);
256	flush_workqueue(qdev->bootlog_wq);
257	destroy_workqueue(qdev->bootlog_wq);
258	qdev->bootlog_ch = NULL;
259}
260
261static void qaic_bootlog_mhi_ul_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result *mhi_result)
262{
263}
264
265static void qaic_bootlog_mhi_dl_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result *mhi_result)
266{
267	struct qaic_device *qdev = dev_get_drvdata(&mhi_dev->dev);
268	struct bootlog_msg *msg = mhi_result->buf_addr;
269
270	if (mhi_result->transaction_status) {
271		devm_kfree(&qdev->pdev->dev, msg);
272		return;
273	}
274
275	/* Force a null at the end of the transferred string */
276	msg->str[mhi_result->bytes_xferd - 1] = 0;
277
278	queue_work(qdev->bootlog_wq, &msg->work);
279}
280
281static const struct mhi_device_id qaic_bootlog_mhi_match_table[] = {
282	{ .chan = "QAIC_LOGGING", },
283	{},
284};
285
286static struct mhi_driver qaic_bootlog_mhi_driver = {
287	.id_table = qaic_bootlog_mhi_match_table,
288	.remove = qaic_bootlog_mhi_remove,
289	.probe = qaic_bootlog_mhi_probe,
290	.ul_xfer_cb = qaic_bootlog_mhi_ul_xfer_cb,
291	.dl_xfer_cb = qaic_bootlog_mhi_dl_xfer_cb,
292	.driver = {
293		.name = "qaic_bootlog",
294	},
295};
296
297int qaic_bootlog_register(void)
298{
299	return mhi_driver_register(&qaic_bootlog_mhi_driver);
300}
301
302void qaic_bootlog_unregister(void)
303{
304	mhi_driver_unregister(&qaic_bootlog_mhi_driver);
305}