]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blob - drivers/media/pci/intel/ipu-trace.c
UBUNTU: SAUCE: IPU driver release WW52
[mirror_ubuntu-jammy-kernel.git] / drivers / media / pci / intel / ipu-trace.c
1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (C) 2014 - 2020 Intel Corporation
3
4 #include <linux/debugfs.h>
5 #include <linux/delay.h>
6 #include <linux/device.h>
7 #include <linux/dma-mapping.h>
8 #include <linux/module.h>
9 #include <linux/pm_runtime.h>
10 #include <linux/sizes.h>
11 #include <linux/uaccess.h>
12 #include <linux/vmalloc.h>
13
14 #include "ipu.h"
15 #include "ipu-platform-regs.h"
16 #include "ipu-trace.h"
17
18 /* Input data processing states */
19 enum config_file_parse_states {
20 STATE_FILL = 0,
21 STATE_COMMENT,
22 STATE_COMPLETE,
23 };
24
25 struct trace_register_range {
26 u32 start;
27 u32 end;
28 };
29
30 static u16 trace_unit_template[] = TRACE_REG_CREATE_TUN_REGISTER_LIST;
31 static u16 trace_monitor_template[] = TRACE_REG_CREATE_TM_REGISTER_LIST;
32 static u16 trace_gpc_template[] = TRACE_REG_CREATE_GPC_REGISTER_LIST;
33
34 static struct trace_register_range trace_csi2_range_template[] = {
35 {
36 .start = TRACE_REG_CSI2_TM_RESET_REG_IDX,
37 .end = TRACE_REG_CSI2_TM_IRQ_ENABLE_REG_ID_N(7)
38 },
39 {
40 .start = TRACE_REG_END_MARK,
41 .end = TRACE_REG_END_MARK
42 }
43 };
44
45 static struct trace_register_range trace_csi2_3ph_range_template[] = {
46 {
47 .start = TRACE_REG_CSI2_3PH_TM_RESET_REG_IDX,
48 .end = TRACE_REG_CSI2_3PH_TM_IRQ_ENABLE_REG_ID_N(7)
49 },
50 {
51 .start = TRACE_REG_END_MARK,
52 .end = TRACE_REG_END_MARK
53 }
54 };
55
56 static struct trace_register_range trace_sig2cio_range_template[] = {
57 {
58 .start = TRACE_REG_SIG2CIO_ADDRESS,
59 .end = (TRACE_REG_SIG2CIO_STATUS + 8 * TRACE_REG_SIG2CIO_SIZE_OF)
60 },
61 {
62 .start = TRACE_REG_END_MARK,
63 .end = TRACE_REG_END_MARK
64 }
65 };
66
67 #define LINE_MAX_LEN 128
68 #define MEMORY_RING_BUFFER_SIZE (SZ_1M * 32)
69 #define TRACE_MESSAGE_SIZE 16
70 /*
71 * It looks that the trace unit sometimes writes outside the given buffer.
72 * To avoid memory corruption one extra page is reserved at the end
73 * of the buffer. Read also the extra area since it may contain valid data.
74 */
75 #define MEMORY_RING_BUFFER_GUARD PAGE_SIZE
76 #define MEMORY_RING_BUFFER_OVERREAD MEMORY_RING_BUFFER_GUARD
77 #define MAX_TRACE_REGISTERS 200
78 #define TRACE_CONF_DUMP_BUFFER_SIZE (MAX_TRACE_REGISTERS * 2 * 32)
79
80 #define IPU_TRACE_TIME_RETRY 5
81
82 struct config_value {
83 u32 reg;
84 u32 value;
85 };
86
87 struct ipu_trace_buffer {
88 dma_addr_t dma_handle;
89 void *memory_buffer;
90 };
91
92 struct ipu_subsystem_trace_config {
93 u32 offset;
94 void __iomem *base;
95 struct ipu_trace_buffer memory; /* ring buffer */
96 struct device *dev;
97 struct ipu_trace_block *blocks;
98 unsigned int fill_level; /* Nbr of regs in config table below */
99 bool running;
100 /* Cached register values */
101 struct config_value config[MAX_TRACE_REGISTERS];
102 };
103
104 /*
105 * State of the input data processing is kept in this structure.
106 * Only one user is supported at time.
107 */
108 struct buf_state {
109 char line_buffer[LINE_MAX_LEN];
110 enum config_file_parse_states state;
111 int offset; /* Offset to line_buffer */
112 };
113
114 struct ipu_trace {
115 struct mutex lock; /* Protect ipu trace operations */
116 bool open;
117 char *conf_dump_buffer;
118 int size_conf_dump;
119 struct buf_state buffer_state;
120
121 struct ipu_subsystem_trace_config isys;
122 struct ipu_subsystem_trace_config psys;
123 };
124
125 static void __ipu_trace_restore(struct device *dev)
126 {
127 struct ipu_bus_device *adev = to_ipu_bus_device(dev);
128 struct ipu_device *isp = adev->isp;
129 struct ipu_trace *trace = isp->trace;
130 struct config_value *config;
131 struct ipu_subsystem_trace_config *sys = adev->trace_cfg;
132 struct ipu_trace_block *blocks;
133 u32 mapped_trace_buffer;
134 void __iomem *addr = NULL;
135 int i;
136
137 if (trace->open) {
138 dev_info(dev, "Trace control file open. Skipping update\n");
139 return;
140 }
141
142 if (!sys)
143 return;
144
145 /* leave if no trace configuration for this subsystem */
146 if (sys->fill_level == 0)
147 return;
148
149 /* Find trace unit base address */
150 blocks = sys->blocks;
151 while (blocks->type != IPU_TRACE_BLOCK_END) {
152 if (blocks->type == IPU_TRACE_BLOCK_TUN) {
153 addr = sys->base + blocks->offset;
154 break;
155 }
156 blocks++;
157 }
158 if (!addr)
159 return;
160
161 if (!sys->memory.memory_buffer) {
162 sys->memory.memory_buffer =
163 dma_alloc_noncoherent(dev,
164 MEMORY_RING_BUFFER_SIZE +
165 MEMORY_RING_BUFFER_GUARD,
166 &sys->memory.dma_handle,
167 DMA_BIDIRECTIONAL,
168 GFP_KERNEL);
169 }
170
171 if (!sys->memory.memory_buffer) {
172 dev_err(dev, "No memory for tracing. Trace unit disabled\n");
173 return;
174 }
175
176 config = sys->config;
177 mapped_trace_buffer = sys->memory.dma_handle;
178
179 /* ring buffer base */
180 writel(mapped_trace_buffer, addr + TRACE_REG_TUN_DRAM_BASE_ADDR);
181
182 /* ring buffer end */
183 writel(mapped_trace_buffer + MEMORY_RING_BUFFER_SIZE -
184 TRACE_MESSAGE_SIZE, addr + TRACE_REG_TUN_DRAM_END_ADDR);
185
186 /* Infobits for ddr trace */
187 writel(IPU_INFO_REQUEST_DESTINATION_PRIMARY,
188 addr + TRACE_REG_TUN_DDR_INFO_VAL);
189
190 /* Find trace timer reset address */
191 addr = NULL;
192 blocks = sys->blocks;
193 while (blocks->type != IPU_TRACE_BLOCK_END) {
194 if (blocks->type == IPU_TRACE_TIMER_RST) {
195 addr = sys->base + blocks->offset;
196 break;
197 }
198 blocks++;
199 }
200 if (!addr) {
201 dev_err(dev, "No trace reset addr\n");
202 return;
203 }
204
205 /* Remove reset from trace timers */
206 writel(TRACE_REG_GPREG_TRACE_TIMER_RST_OFF, addr);
207
208 /* Register config received from userspace */
209 for (i = 0; i < sys->fill_level; i++) {
210 dev_dbg(dev,
211 "Trace restore: reg 0x%08x, value 0x%08x\n",
212 config[i].reg, config[i].value);
213 writel(config[i].value, isp->base + config[i].reg);
214 }
215
216 sys->running = true;
217 }
218
219 void ipu_trace_restore(struct device *dev)
220 {
221 struct ipu_trace *trace = to_ipu_bus_device(dev)->isp->trace;
222
223 if (!trace)
224 return;
225
226 mutex_lock(&trace->lock);
227 __ipu_trace_restore(dev);
228 mutex_unlock(&trace->lock);
229 }
230 EXPORT_SYMBOL_GPL(ipu_trace_restore);
231
232 static void __ipu_trace_stop(struct device *dev)
233 {
234 struct ipu_subsystem_trace_config *sys =
235 to_ipu_bus_device(dev)->trace_cfg;
236 struct ipu_trace_block *blocks;
237
238 if (!sys)
239 return;
240
241 if (!sys->running)
242 return;
243 sys->running = false;
244
245 /* Turn off all the gpc blocks */
246 blocks = sys->blocks;
247 while (blocks->type != IPU_TRACE_BLOCK_END) {
248 if (blocks->type == IPU_TRACE_BLOCK_GPC) {
249 writel(0, sys->base + blocks->offset +
250 TRACE_REG_GPC_OVERALL_ENABLE);
251 }
252 blocks++;
253 }
254
255 /* Turn off all the trace monitors */
256 blocks = sys->blocks;
257 while (blocks->type != IPU_TRACE_BLOCK_END) {
258 if (blocks->type == IPU_TRACE_BLOCK_TM) {
259 writel(0, sys->base + blocks->offset +
260 TRACE_REG_TM_TRACE_ENABLE_NPK);
261
262 writel(0, sys->base + blocks->offset +
263 TRACE_REG_TM_TRACE_ENABLE_DDR);
264 }
265 blocks++;
266 }
267
268 /* Turn off trace units */
269 blocks = sys->blocks;
270 while (blocks->type != IPU_TRACE_BLOCK_END) {
271 if (blocks->type == IPU_TRACE_BLOCK_TUN) {
272 writel(0, sys->base + blocks->offset +
273 TRACE_REG_TUN_DDR_ENABLE);
274 writel(0, sys->base + blocks->offset +
275 TRACE_REG_TUN_NPK_ENABLE);
276 }
277 blocks++;
278 }
279 }
280
281 void ipu_trace_stop(struct device *dev)
282 {
283 struct ipu_trace *trace = to_ipu_bus_device(dev)->isp->trace;
284
285 if (!trace)
286 return;
287
288 mutex_lock(&trace->lock);
289 __ipu_trace_stop(dev);
290 mutex_unlock(&trace->lock);
291 }
292 EXPORT_SYMBOL_GPL(ipu_trace_stop);
293
294 static int validate_register(u32 base, u32 reg, u16 *template)
295 {
296 int i = 0;
297
298 while (template[i] != TRACE_REG_END_MARK) {
299 if (template[i] + base != reg) {
300 i++;
301 continue;
302 }
303 /* This is a valid register */
304 return 0;
305 }
306 return -EINVAL;
307 }
308
309 static int validate_register_range(u32 base, u32 reg,
310 struct trace_register_range *template)
311 {
312 unsigned int i = 0;
313
314 if (!IS_ALIGNED(reg, sizeof(u32)))
315 return -EINVAL;
316
317 while (template[i].start != TRACE_REG_END_MARK) {
318 if ((reg < template[i].start + base) ||
319 (reg > template[i].end + base)) {
320 i++;
321 continue;
322 }
323 /* This is a valid register */
324 return 0;
325 }
326 return -EINVAL;
327 }
328
329 static int update_register_cache(struct ipu_device *isp, u32 reg, u32 value)
330 {
331 struct ipu_trace *dctrl = isp->trace;
332 const struct ipu_trace_block *blocks;
333 struct ipu_subsystem_trace_config *sys;
334 struct device *dev;
335 u32 base = 0;
336 u16 *template = NULL;
337 struct trace_register_range *template_range = NULL;
338 int i, range;
339 int rval = -EINVAL;
340
341 if (dctrl->isys.offset == dctrl->psys.offset) {
342 /* For the IPU with uniform address space */
343 if (reg >= IPU_ISYS_OFFSET &&
344 reg < IPU_ISYS_OFFSET + TRACE_REG_MAX_ISYS_OFFSET)
345 sys = &dctrl->isys;
346 else if (reg >= IPU_PSYS_OFFSET &&
347 reg < IPU_PSYS_OFFSET + TRACE_REG_MAX_PSYS_OFFSET)
348 sys = &dctrl->psys;
349 else
350 goto error;
351 } else {
352 if (dctrl->isys.offset &&
353 reg >= dctrl->isys.offset &&
354 reg < dctrl->isys.offset + TRACE_REG_MAX_ISYS_OFFSET)
355 sys = &dctrl->isys;
356 else if (dctrl->psys.offset &&
357 reg >= dctrl->psys.offset &&
358 reg < dctrl->psys.offset + TRACE_REG_MAX_PSYS_OFFSET)
359 sys = &dctrl->psys;
360 else
361 goto error;
362 }
363
364 blocks = sys->blocks;
365 dev = sys->dev;
366
367 /* Check registers block by block */
368 i = 0;
369 while (blocks[i].type != IPU_TRACE_BLOCK_END) {
370 base = blocks[i].offset + sys->offset;
371 if ((reg >= base && reg < base + TRACE_REG_MAX_BLOCK_SIZE))
372 break;
373 i++;
374 }
375
376 range = 0;
377 switch (blocks[i].type) {
378 case IPU_TRACE_BLOCK_TUN:
379 template = trace_unit_template;
380 break;
381 case IPU_TRACE_BLOCK_TM:
382 template = trace_monitor_template;
383 break;
384 case IPU_TRACE_BLOCK_GPC:
385 template = trace_gpc_template;
386 break;
387 case IPU_TRACE_CSI2:
388 range = 1;
389 template_range = trace_csi2_range_template;
390 break;
391 case IPU_TRACE_CSI2_3PH:
392 range = 1;
393 template_range = trace_csi2_3ph_range_template;
394 break;
395 case IPU_TRACE_SIG2CIOS:
396 range = 1;
397 template_range = trace_sig2cio_range_template;
398 break;
399 default:
400 goto error;
401 }
402
403 if (range)
404 rval = validate_register_range(base, reg, template_range);
405 else
406 rval = validate_register(base, reg, template);
407
408 if (rval)
409 goto error;
410
411 if (sys->fill_level < MAX_TRACE_REGISTERS) {
412 dev_dbg(dev,
413 "Trace reg addr 0x%08x value 0x%08x\n", reg, value);
414 sys->config[sys->fill_level].reg = reg;
415 sys->config[sys->fill_level].value = value;
416 sys->fill_level++;
417 } else {
418 rval = -ENOMEM;
419 goto error;
420 }
421 return 0;
422 error:
423 dev_info(&isp->pdev->dev,
424 "Trace register address 0x%08x ignored as invalid register\n",
425 reg);
426 return rval;
427 }
428
429 /*
430 * We don't know how much data is received this time. Process given data
431 * character by character.
432 * Fill the line buffer until either
433 * 1) new line is got -> go to decode
434 * or
435 * 2) line_buffer is full -> ignore rest of line and then try to decode
436 * or
437 * 3) Comment mark is found -> ignore rest of the line and then try to decode
438 * the data which was received before the comment mark
439 *
440 * Decode phase tries to find "reg = value" pairs and validates those
441 */
442 static int process_buffer(struct ipu_device *isp,
443 char *buffer, int size, struct buf_state *state)
444 {
445 int i, ret;
446 int curr_state = state->state;
447 u32 reg, value;
448
449 for (i = 0; i < size; i++) {
450 /*
451 * Comment mark in any position turns on comment mode
452 * until end of line
453 */
454 if (curr_state != STATE_COMMENT && buffer[i] == '#') {
455 state->line_buffer[state->offset] = '\0';
456 curr_state = STATE_COMMENT;
457 continue;
458 }
459
460 switch (curr_state) {
461 case STATE_COMMENT:
462 /* Only new line can break this mode */
463 if (buffer[i] == '\n')
464 curr_state = STATE_COMPLETE;
465 break;
466 case STATE_FILL:
467 state->line_buffer[state->offset] = buffer[i];
468 state->offset++;
469
470 if (state->offset >= sizeof(state->line_buffer) - 1) {
471 /* Line buffer full - ignore rest */
472 state->line_buffer[state->offset] = '\0';
473 curr_state = STATE_COMMENT;
474 break;
475 }
476
477 if (buffer[i] == '\n') {
478 state->line_buffer[state->offset] = '\0';
479 curr_state = STATE_COMPLETE;
480 }
481 break;
482 default:
483 state->offset = 0;
484 state->line_buffer[state->offset] = '\0';
485 curr_state = STATE_COMMENT;
486 }
487
488 if (curr_state == STATE_COMPLETE) {
489 ret = sscanf(state->line_buffer, "%x = %x",
490 &reg, &value);
491 if (ret == 2)
492 update_register_cache(isp, reg, value);
493
494 state->offset = 0;
495 curr_state = STATE_FILL;
496 }
497 }
498 state->state = curr_state;
499 return 0;
500 }
501
502 static void traceconf_dump(struct ipu_device *isp)
503 {
504 struct ipu_subsystem_trace_config *sys[2] = {
505 &isp->trace->isys,
506 &isp->trace->psys
507 };
508 int i, j, rem_size;
509 char *out;
510
511 isp->trace->size_conf_dump = 0;
512 out = isp->trace->conf_dump_buffer;
513 rem_size = TRACE_CONF_DUMP_BUFFER_SIZE;
514
515 for (j = 0; j < ARRAY_SIZE(sys); j++) {
516 for (i = 0; i < sys[j]->fill_level && rem_size > 0; i++) {
517 int bytes_print;
518 int n = snprintf(out, rem_size, "0x%08x = 0x%08x\n",
519 sys[j]->config[i].reg,
520 sys[j]->config[i].value);
521
522 bytes_print = min(n, rem_size - 1);
523 rem_size -= bytes_print;
524 out += bytes_print;
525 }
526 }
527 isp->trace->size_conf_dump = out - isp->trace->conf_dump_buffer;
528 }
529
530 static void clear_trace_buffer(struct ipu_subsystem_trace_config *sys)
531 {
532 if (!sys->memory.memory_buffer)
533 return;
534
535 memset(sys->memory.memory_buffer, 0, MEMORY_RING_BUFFER_SIZE +
536 MEMORY_RING_BUFFER_OVERREAD);
537
538 dma_sync_single_for_device(sys->dev,
539 sys->memory.dma_handle,
540 MEMORY_RING_BUFFER_SIZE +
541 MEMORY_RING_BUFFER_GUARD, DMA_FROM_DEVICE);
542 }
543
544 static int traceconf_open(struct inode *inode, struct file *file)
545 {
546 int ret;
547 struct ipu_device *isp;
548
549 if (!inode->i_private)
550 return -EACCES;
551
552 isp = inode->i_private;
553
554 ret = mutex_trylock(&isp->trace->lock);
555 if (!ret)
556 return -EBUSY;
557
558 if (isp->trace->open) {
559 mutex_unlock(&isp->trace->lock);
560 return -EBUSY;
561 }
562
563 file->private_data = isp;
564 isp->trace->open = 1;
565 if (file->f_mode & FMODE_WRITE) {
566 /* TBD: Allocate temp buffer for processing.
567 * Push validated buffer to active config
568 */
569
570 /* Forget old config if opened for write */
571 isp->trace->isys.fill_level = 0;
572 isp->trace->psys.fill_level = 0;
573 }
574
575 if (file->f_mode & FMODE_READ) {
576 isp->trace->conf_dump_buffer =
577 vzalloc(TRACE_CONF_DUMP_BUFFER_SIZE);
578 if (!isp->trace->conf_dump_buffer) {
579 isp->trace->open = 0;
580 mutex_unlock(&isp->trace->lock);
581 return -ENOMEM;
582 }
583 traceconf_dump(isp);
584 }
585 mutex_unlock(&isp->trace->lock);
586 return 0;
587 }
588
589 static ssize_t traceconf_read(struct file *file, char __user *buf,
590 size_t len, loff_t *ppos)
591 {
592 struct ipu_device *isp = file->private_data;
593
594 return simple_read_from_buffer(buf, len, ppos,
595 isp->trace->conf_dump_buffer,
596 isp->trace->size_conf_dump);
597 }
598
599 static ssize_t traceconf_write(struct file *file, const char __user *buf,
600 size_t len, loff_t *ppos)
601 {
602 struct ipu_device *isp = file->private_data;
603 char buffer[64];
604 ssize_t bytes, count;
605 loff_t pos = *ppos;
606
607 if (*ppos < 0)
608 return -EINVAL;
609
610 count = min(len, sizeof(buffer));
611 bytes = copy_from_user(buffer, buf, count);
612 if (bytes == count)
613 return -EFAULT;
614
615 count -= bytes;
616 mutex_lock(&isp->trace->lock);
617 process_buffer(isp, buffer, count, &isp->trace->buffer_state);
618 mutex_unlock(&isp->trace->lock);
619 *ppos = pos + count;
620
621 return count;
622 }
623
624 static int traceconf_release(struct inode *inode, struct file *file)
625 {
626 struct ipu_device *isp = file->private_data;
627 struct device *psys_dev = isp->psys ? &isp->psys->dev : NULL;
628 struct device *isys_dev = isp->isys ? &isp->isys->dev : NULL;
629 int pm_rval = -EINVAL;
630
631 /*
632 * Turn devices on outside trace->lock mutex. PM transition may
633 * cause call to function which tries to take the same lock.
634 * Also do this before trace->open is set back to 0 to avoid
635 * double restore (one here and one in pm transition). We can't
636 * rely purely on the restore done by pm call backs since trace
637 * configuration can occur in any phase compared to other activity.
638 */
639
640 if (file->f_mode & FMODE_WRITE) {
641 if (isys_dev)
642 pm_rval = pm_runtime_get_sync(isys_dev);
643
644 if (pm_rval >= 0) {
645 /* ISYS ok or missing */
646 if (psys_dev)
647 pm_rval = pm_runtime_get_sync(psys_dev);
648
649 if (pm_rval < 0) {
650 pm_runtime_put_noidle(psys_dev);
651 if (isys_dev)
652 pm_runtime_put(isys_dev);
653 }
654 } else {
655 pm_runtime_put_noidle(&isp->isys->dev);
656 }
657 }
658
659 mutex_lock(&isp->trace->lock);
660 isp->trace->open = 0;
661 vfree(isp->trace->conf_dump_buffer);
662 isp->trace->conf_dump_buffer = NULL;
663
664 if (pm_rval >= 0) {
665 /* Update new cfg to HW */
666 if (isys_dev) {
667 __ipu_trace_stop(isys_dev);
668 clear_trace_buffer(isp->isys->trace_cfg);
669 __ipu_trace_restore(isys_dev);
670 }
671
672 if (psys_dev) {
673 __ipu_trace_stop(psys_dev);
674 clear_trace_buffer(isp->psys->trace_cfg);
675 __ipu_trace_restore(psys_dev);
676 }
677 }
678
679 mutex_unlock(&isp->trace->lock);
680
681 if (pm_rval >= 0) {
682 /* Again - this must be done with trace->lock not taken */
683 if (psys_dev)
684 pm_runtime_put(psys_dev);
685 if (isys_dev)
686 pm_runtime_put(isys_dev);
687 }
688 return 0;
689 }
690
691 static const struct file_operations ipu_traceconf_fops = {
692 .owner = THIS_MODULE,
693 .open = traceconf_open,
694 .release = traceconf_release,
695 .read = traceconf_read,
696 .write = traceconf_write,
697 .llseek = no_llseek,
698 };
699
700 static int gettrace_open(struct inode *inode, struct file *file)
701 {
702 struct ipu_subsystem_trace_config *sys = inode->i_private;
703
704 if (!sys)
705 return -EACCES;
706
707 if (!sys->memory.memory_buffer)
708 return -EACCES;
709
710 dma_sync_single_for_cpu(sys->dev,
711 sys->memory.dma_handle,
712 MEMORY_RING_BUFFER_SIZE +
713 MEMORY_RING_BUFFER_GUARD, DMA_FROM_DEVICE);
714
715 file->private_data = sys;
716 return 0;
717 };
718
719 static ssize_t gettrace_read(struct file *file, char __user *buf,
720 size_t len, loff_t *ppos)
721 {
722 struct ipu_subsystem_trace_config *sys = file->private_data;
723
724 return simple_read_from_buffer(buf, len, ppos,
725 sys->memory.memory_buffer,
726 MEMORY_RING_BUFFER_SIZE +
727 MEMORY_RING_BUFFER_OVERREAD);
728 }
729
730 static ssize_t gettrace_write(struct file *file, const char __user *buf,
731 size_t len, loff_t *ppos)
732 {
733 struct ipu_subsystem_trace_config *sys = file->private_data;
734 static const char str[] = "clear";
735 char buffer[sizeof(str)] = { 0 };
736 ssize_t ret;
737
738 ret = simple_write_to_buffer(buffer, sizeof(buffer), ppos, buf, len);
739 if (ret < 0)
740 return ret;
741
742 if (ret < sizeof(str) - 1)
743 return -EINVAL;
744
745 if (!strncmp(str, buffer, sizeof(str) - 1)) {
746 clear_trace_buffer(sys);
747 return len;
748 }
749
750 return -EINVAL;
751 }
752
753 static int gettrace_release(struct inode *inode, struct file *file)
754 {
755 return 0;
756 }
757
758 static const struct file_operations ipu_gettrace_fops = {
759 .owner = THIS_MODULE,
760 .open = gettrace_open,
761 .release = gettrace_release,
762 .read = gettrace_read,
763 .write = gettrace_write,
764 .llseek = no_llseek,
765 };
766
767 int ipu_trace_init(struct ipu_device *isp, void __iomem *base,
768 struct device *dev, struct ipu_trace_block *blocks)
769 {
770 struct ipu_bus_device *adev = to_ipu_bus_device(dev);
771 struct ipu_trace *trace = isp->trace;
772 struct ipu_subsystem_trace_config *sys;
773 int ret = 0;
774
775 if (!isp->trace)
776 return 0;
777
778 mutex_lock(&isp->trace->lock);
779
780 if (dev == &isp->isys->dev) {
781 sys = &trace->isys;
782 } else if (dev == &isp->psys->dev) {
783 sys = &trace->psys;
784 } else {
785 ret = -EINVAL;
786 goto leave;
787 }
788
789 adev->trace_cfg = sys;
790 sys->dev = dev;
791 sys->offset = base - isp->base; /* sub system offset */
792 sys->base = base;
793 sys->blocks = blocks;
794
795 leave:
796 mutex_unlock(&isp->trace->lock);
797
798 return ret;
799 }
800 EXPORT_SYMBOL_GPL(ipu_trace_init);
801
802 void ipu_trace_uninit(struct device *dev)
803 {
804 struct ipu_bus_device *adev = to_ipu_bus_device(dev);
805 struct ipu_device *isp = adev->isp;
806 struct ipu_trace *trace = isp->trace;
807 struct ipu_subsystem_trace_config *sys = adev->trace_cfg;
808
809 if (!trace || !sys)
810 return;
811
812 mutex_lock(&trace->lock);
813
814 if (sys->memory.memory_buffer)
815 dma_free_noncoherent(sys->dev,
816 MEMORY_RING_BUFFER_SIZE + MEMORY_RING_BUFFER_GUARD,
817 sys->memory.memory_buffer,
818 sys->memory.dma_handle,
819 DMA_BIDIRECTIONAL);
820
821 sys->dev = NULL;
822 sys->memory.memory_buffer = NULL;
823
824 mutex_unlock(&trace->lock);
825 }
826 EXPORT_SYMBOL_GPL(ipu_trace_uninit);
827
828 int ipu_trace_debugfs_add(struct ipu_device *isp, struct dentry *dir)
829 {
830 struct dentry *files[3];
831 int i = 0;
832
833 files[i] = debugfs_create_file("traceconf", 0644,
834 dir, isp, &ipu_traceconf_fops);
835 if (!files[i])
836 return -ENOMEM;
837 i++;
838
839 files[i] = debugfs_create_file("getisystrace", 0444,
840 dir,
841 &isp->trace->isys, &ipu_gettrace_fops);
842
843 if (!files[i])
844 goto error;
845 i++;
846
847 files[i] = debugfs_create_file("getpsystrace", 0444,
848 dir,
849 &isp->trace->psys, &ipu_gettrace_fops);
850 if (!files[i])
851 goto error;
852
853 return 0;
854
855 error:
856 for (; i > 0; i--)
857 debugfs_remove(files[i - 1]);
858 return -ENOMEM;
859 }
860
861 int ipu_trace_add(struct ipu_device *isp)
862 {
863 isp->trace = devm_kzalloc(&isp->pdev->dev,
864 sizeof(struct ipu_trace), GFP_KERNEL);
865 if (!isp->trace)
866 return -ENOMEM;
867
868 mutex_init(&isp->trace->lock);
869
870 return 0;
871 }
872
873 void ipu_trace_release(struct ipu_device *isp)
874 {
875 if (!isp->trace)
876 return;
877 mutex_destroy(&isp->trace->lock);
878 }
879
880 MODULE_AUTHOR("Samu Onkalo <samu.onkalo@intel.com>");
881 MODULE_LICENSE("GPL");
882 MODULE_DESCRIPTION("Intel ipu trace support");