Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/accel/qaic/qaic_debugfs.c
26428 views
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
27
struct 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
36
struct 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
45
static 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
67
DEFINE_SHOW_ATTRIBUTE(bootlog);
68
69
static 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
77
DEFINE_SHOW_ATTRIBUTE(fifo_size);
78
79
static 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
96
DEFINE_SHOW_ATTRIBUTE(queued);
97
98
void 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
121
static 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
136
static 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
155
static 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
173
static 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
182
static 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
201
static 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
240
mhi_unprepare:
241
mhi_unprepare_from_transfer(mhi_dev);
242
destroy_workqueue:
243
destroy_workqueue(qdev->bootlog_wq);
244
out:
245
return ret;
246
}
247
248
static void qaic_bootlog_mhi_remove(struct mhi_device *mhi_dev)
249
{
250
struct qaic_device *qdev;
251
252
qdev = dev_get_drvdata(&mhi_dev->dev);
253
254
mhi_unprepare_from_transfer(qdev->bootlog_ch);
255
destroy_workqueue(qdev->bootlog_wq);
256
qdev->bootlog_ch = NULL;
257
}
258
259
static void qaic_bootlog_mhi_ul_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result *mhi_result)
260
{
261
}
262
263
static void qaic_bootlog_mhi_dl_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result *mhi_result)
264
{
265
struct qaic_device *qdev = dev_get_drvdata(&mhi_dev->dev);
266
struct bootlog_msg *msg = mhi_result->buf_addr;
267
268
if (mhi_result->transaction_status) {
269
devm_kfree(&qdev->pdev->dev, msg);
270
return;
271
}
272
273
/* Force a null at the end of the transferred string */
274
msg->str[mhi_result->bytes_xferd - 1] = 0;
275
276
queue_work(qdev->bootlog_wq, &msg->work);
277
}
278
279
static const struct mhi_device_id qaic_bootlog_mhi_match_table[] = {
280
{ .chan = "QAIC_LOGGING", },
281
{},
282
};
283
284
static struct mhi_driver qaic_bootlog_mhi_driver = {
285
.id_table = qaic_bootlog_mhi_match_table,
286
.remove = qaic_bootlog_mhi_remove,
287
.probe = qaic_bootlog_mhi_probe,
288
.ul_xfer_cb = qaic_bootlog_mhi_ul_xfer_cb,
289
.dl_xfer_cb = qaic_bootlog_mhi_dl_xfer_cb,
290
.driver = {
291
.name = "qaic_bootlog",
292
},
293
};
294
295
int qaic_bootlog_register(void)
296
{
297
return mhi_driver_register(&qaic_bootlog_mhi_driver);
298
}
299
300
void qaic_bootlog_unregister(void)
301
{
302
mhi_driver_unregister(&qaic_bootlog_mhi_driver);
303
}
304
305