1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (C) 2014 - 2020 Intel Corporation
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>
15 #include "ipu-platform-regs.h"
16 #include "ipu-trace.h"
18 /* Input data processing states */
19 enum config_file_parse_states
{
25 struct trace_register_range
{
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
;
34 static struct trace_register_range trace_csi2_range_template
[] = {
36 .start
= TRACE_REG_CSI2_TM_RESET_REG_IDX
,
37 .end
= TRACE_REG_CSI2_TM_IRQ_ENABLE_REG_ID_N(7)
40 .start
= TRACE_REG_END_MARK
,
41 .end
= TRACE_REG_END_MARK
45 static struct trace_register_range trace_csi2_3ph_range_template
[] = {
47 .start
= TRACE_REG_CSI2_3PH_TM_RESET_REG_IDX
,
48 .end
= TRACE_REG_CSI2_3PH_TM_IRQ_ENABLE_REG_ID_N(7)
51 .start
= TRACE_REG_END_MARK
,
52 .end
= TRACE_REG_END_MARK
56 static struct trace_register_range trace_sig2cio_range_template
[] = {
58 .start
= TRACE_REG_SIG2CIO_ADDRESS
,
59 .end
= (TRACE_REG_SIG2CIO_STATUS
+ 8 * TRACE_REG_SIG2CIO_SIZE_OF
)
62 .start
= TRACE_REG_END_MARK
,
63 .end
= TRACE_REG_END_MARK
67 #define LINE_MAX_LEN 128
68 #define MEMORY_RING_BUFFER_SIZE (SZ_1M * 32)
69 #define TRACE_MESSAGE_SIZE 16
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.
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)
80 #define IPU_TRACE_TIME_RETRY 5
87 struct ipu_trace_buffer
{
88 dma_addr_t dma_handle
;
92 struct ipu_subsystem_trace_config
{
95 struct ipu_trace_buffer memory
; /* ring buffer */
97 struct ipu_trace_block
*blocks
;
98 unsigned int fill_level
; /* Nbr of regs in config table below */
100 /* Cached register values */
101 struct config_value config
[MAX_TRACE_REGISTERS
];
105 * State of the input data processing is kept in this structure.
106 * Only one user is supported at time.
109 char line_buffer
[LINE_MAX_LEN
];
110 enum config_file_parse_states state
;
111 int offset
; /* Offset to line_buffer */
115 struct mutex lock
; /* Protect ipu trace operations */
117 char *conf_dump_buffer
;
119 struct buf_state buffer_state
;
121 struct ipu_subsystem_trace_config isys
;
122 struct ipu_subsystem_trace_config psys
;
125 static void __ipu_trace_restore(struct device
*dev
)
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
;
138 dev_info(dev
, "Trace control file open. Skipping update\n");
145 /* leave if no trace configuration for this subsystem */
146 if (sys
->fill_level
== 0)
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
;
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
,
171 if (!sys
->memory
.memory_buffer
) {
172 dev_err(dev
, "No memory for tracing. Trace unit disabled\n");
176 config
= sys
->config
;
177 mapped_trace_buffer
= sys
->memory
.dma_handle
;
179 /* ring buffer base */
180 writel(mapped_trace_buffer
, addr
+ TRACE_REG_TUN_DRAM_BASE_ADDR
);
182 /* ring buffer end */
183 writel(mapped_trace_buffer
+ MEMORY_RING_BUFFER_SIZE
-
184 TRACE_MESSAGE_SIZE
, addr
+ TRACE_REG_TUN_DRAM_END_ADDR
);
186 /* Infobits for ddr trace */
187 writel(IPU_INFO_REQUEST_DESTINATION_PRIMARY
,
188 addr
+ TRACE_REG_TUN_DDR_INFO_VAL
);
190 /* Find trace timer reset address */
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
;
201 dev_err(dev
, "No trace reset addr\n");
205 /* Remove reset from trace timers */
206 writel(TRACE_REG_GPREG_TRACE_TIMER_RST_OFF
, addr
);
208 /* Register config received from userspace */
209 for (i
= 0; i
< sys
->fill_level
; i
++) {
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
);
219 void ipu_trace_restore(struct device
*dev
)
221 struct ipu_trace
*trace
= to_ipu_bus_device(dev
)->isp
->trace
;
226 mutex_lock(&trace
->lock
);
227 __ipu_trace_restore(dev
);
228 mutex_unlock(&trace
->lock
);
230 EXPORT_SYMBOL_GPL(ipu_trace_restore
);
232 static void __ipu_trace_stop(struct device
*dev
)
234 struct ipu_subsystem_trace_config
*sys
=
235 to_ipu_bus_device(dev
)->trace_cfg
;
236 struct ipu_trace_block
*blocks
;
243 sys
->running
= false;
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
);
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
);
262 writel(0, sys
->base
+ blocks
->offset
+
263 TRACE_REG_TM_TRACE_ENABLE_DDR
);
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
);
281 void ipu_trace_stop(struct device
*dev
)
283 struct ipu_trace
*trace
= to_ipu_bus_device(dev
)->isp
->trace
;
288 mutex_lock(&trace
->lock
);
289 __ipu_trace_stop(dev
);
290 mutex_unlock(&trace
->lock
);
292 EXPORT_SYMBOL_GPL(ipu_trace_stop
);
294 static int validate_register(u32 base
, u32 reg
, u16
*template)
298 while (template[i
] != TRACE_REG_END_MARK
) {
299 if (template[i
] + base
!= reg
) {
303 /* This is a valid register */
309 static int validate_register_range(u32 base
, u32 reg
,
310 struct trace_register_range
*template)
314 if (!IS_ALIGNED(reg
, sizeof(u32
)))
317 while (template[i
].start
!= TRACE_REG_END_MARK
) {
318 if ((reg
< template[i
].start
+ base
) ||
319 (reg
> template[i
].end
+ base
)) {
323 /* This is a valid register */
329 static int update_register_cache(struct ipu_device
*isp
, u32 reg
, u32 value
)
331 struct ipu_trace
*dctrl
= isp
->trace
;
332 const struct ipu_trace_block
*blocks
;
333 struct ipu_subsystem_trace_config
*sys
;
336 u16
*template = NULL
;
337 struct trace_register_range
*template_range
= NULL
;
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
)
346 else if (reg
>= IPU_PSYS_OFFSET
&&
347 reg
< IPU_PSYS_OFFSET
+ TRACE_REG_MAX_PSYS_OFFSET
)
352 if (dctrl
->isys
.offset
&&
353 reg
>= dctrl
->isys
.offset
&&
354 reg
< dctrl
->isys
.offset
+ TRACE_REG_MAX_ISYS_OFFSET
)
356 else if (dctrl
->psys
.offset
&&
357 reg
>= dctrl
->psys
.offset
&&
358 reg
< dctrl
->psys
.offset
+ TRACE_REG_MAX_PSYS_OFFSET
)
364 blocks
= sys
->blocks
;
367 /* Check registers block by block */
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
))
377 switch (blocks
[i
].type
) {
378 case IPU_TRACE_BLOCK_TUN
:
379 template = trace_unit_template
;
381 case IPU_TRACE_BLOCK_TM
:
382 template = trace_monitor_template
;
384 case IPU_TRACE_BLOCK_GPC
:
385 template = trace_gpc_template
;
389 template_range
= trace_csi2_range_template
;
391 case IPU_TRACE_CSI2_3PH
:
393 template_range
= trace_csi2_3ph_range_template
;
395 case IPU_TRACE_SIG2CIOS
:
397 template_range
= trace_sig2cio_range_template
;
404 rval
= validate_register_range(base
, reg
, template_range
);
406 rval
= validate_register(base
, reg
, template);
411 if (sys
->fill_level
< MAX_TRACE_REGISTERS
) {
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
;
423 dev_info(&isp
->pdev
->dev
,
424 "Trace register address 0x%08x ignored as invalid register\n",
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
435 * 2) line_buffer is full -> ignore rest of line and then try to decode
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
440 * Decode phase tries to find "reg = value" pairs and validates those
442 static int process_buffer(struct ipu_device
*isp
,
443 char *buffer
, int size
, struct buf_state
*state
)
446 int curr_state
= state
->state
;
449 for (i
= 0; i
< size
; i
++) {
451 * Comment mark in any position turns on comment mode
454 if (curr_state
!= STATE_COMMENT
&& buffer
[i
] == '#') {
455 state
->line_buffer
[state
->offset
] = '\0';
456 curr_state
= STATE_COMMENT
;
460 switch (curr_state
) {
462 /* Only new line can break this mode */
463 if (buffer
[i
] == '\n')
464 curr_state
= STATE_COMPLETE
;
467 state
->line_buffer
[state
->offset
] = buffer
[i
];
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
;
477 if (buffer
[i
] == '\n') {
478 state
->line_buffer
[state
->offset
] = '\0';
479 curr_state
= STATE_COMPLETE
;
484 state
->line_buffer
[state
->offset
] = '\0';
485 curr_state
= STATE_COMMENT
;
488 if (curr_state
== STATE_COMPLETE
) {
489 ret
= sscanf(state
->line_buffer
, "%x = %x",
492 update_register_cache(isp
, reg
, value
);
495 curr_state
= STATE_FILL
;
498 state
->state
= curr_state
;
502 static void traceconf_dump(struct ipu_device
*isp
)
504 struct ipu_subsystem_trace_config
*sys
[2] = {
511 isp
->trace
->size_conf_dump
= 0;
512 out
= isp
->trace
->conf_dump_buffer
;
513 rem_size
= TRACE_CONF_DUMP_BUFFER_SIZE
;
515 for (j
= 0; j
< ARRAY_SIZE(sys
); j
++) {
516 for (i
= 0; i
< sys
[j
]->fill_level
&& rem_size
> 0; i
++) {
518 int n
= snprintf(out
, rem_size
, "0x%08x = 0x%08x\n",
519 sys
[j
]->config
[i
].reg
,
520 sys
[j
]->config
[i
].value
);
522 bytes_print
= min(n
, rem_size
- 1);
523 rem_size
-= bytes_print
;
527 isp
->trace
->size_conf_dump
= out
- isp
->trace
->conf_dump_buffer
;
530 static void clear_trace_buffer(struct ipu_subsystem_trace_config
*sys
)
532 if (!sys
->memory
.memory_buffer
)
535 memset(sys
->memory
.memory_buffer
, 0, MEMORY_RING_BUFFER_SIZE
+
536 MEMORY_RING_BUFFER_OVERREAD
);
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
);
544 static int traceconf_open(struct inode
*inode
, struct file
*file
)
547 struct ipu_device
*isp
;
549 if (!inode
->i_private
)
552 isp
= inode
->i_private
;
554 ret
= mutex_trylock(&isp
->trace
->lock
);
558 if (isp
->trace
->open
) {
559 mutex_unlock(&isp
->trace
->lock
);
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
570 /* Forget old config if opened for write */
571 isp
->trace
->isys
.fill_level
= 0;
572 isp
->trace
->psys
.fill_level
= 0;
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
);
585 mutex_unlock(&isp
->trace
->lock
);
589 static ssize_t
traceconf_read(struct file
*file
, char __user
*buf
,
590 size_t len
, loff_t
*ppos
)
592 struct ipu_device
*isp
= file
->private_data
;
594 return simple_read_from_buffer(buf
, len
, ppos
,
595 isp
->trace
->conf_dump_buffer
,
596 isp
->trace
->size_conf_dump
);
599 static ssize_t
traceconf_write(struct file
*file
, const char __user
*buf
,
600 size_t len
, loff_t
*ppos
)
602 struct ipu_device
*isp
= file
->private_data
;
604 ssize_t bytes
, count
;
610 count
= min(len
, sizeof(buffer
));
611 bytes
= copy_from_user(buffer
, buf
, count
);
616 mutex_lock(&isp
->trace
->lock
);
617 process_buffer(isp
, buffer
, count
, &isp
->trace
->buffer_state
);
618 mutex_unlock(&isp
->trace
->lock
);
624 static int traceconf_release(struct inode
*inode
, struct file
*file
)
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
;
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.
640 if (file
->f_mode
& FMODE_WRITE
) {
642 pm_rval
= pm_runtime_get_sync(isys_dev
);
645 /* ISYS ok or missing */
647 pm_rval
= pm_runtime_get_sync(psys_dev
);
650 pm_runtime_put_noidle(psys_dev
);
652 pm_runtime_put(isys_dev
);
655 pm_runtime_put_noidle(&isp
->isys
->dev
);
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
;
665 /* Update new cfg to HW */
667 __ipu_trace_stop(isys_dev
);
668 clear_trace_buffer(isp
->isys
->trace_cfg
);
669 __ipu_trace_restore(isys_dev
);
673 __ipu_trace_stop(psys_dev
);
674 clear_trace_buffer(isp
->psys
->trace_cfg
);
675 __ipu_trace_restore(psys_dev
);
679 mutex_unlock(&isp
->trace
->lock
);
682 /* Again - this must be done with trace->lock not taken */
684 pm_runtime_put(psys_dev
);
686 pm_runtime_put(isys_dev
);
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
,
700 static int gettrace_open(struct inode
*inode
, struct file
*file
)
702 struct ipu_subsystem_trace_config
*sys
= inode
->i_private
;
707 if (!sys
->memory
.memory_buffer
)
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
);
715 file
->private_data
= sys
;
719 static ssize_t
gettrace_read(struct file
*file
, char __user
*buf
,
720 size_t len
, loff_t
*ppos
)
722 struct ipu_subsystem_trace_config
*sys
= file
->private_data
;
724 return simple_read_from_buffer(buf
, len
, ppos
,
725 sys
->memory
.memory_buffer
,
726 MEMORY_RING_BUFFER_SIZE
+
727 MEMORY_RING_BUFFER_OVERREAD
);
730 static ssize_t
gettrace_write(struct file
*file
, const char __user
*buf
,
731 size_t len
, loff_t
*ppos
)
733 struct ipu_subsystem_trace_config
*sys
= file
->private_data
;
734 static const char str
[] = "clear";
735 char buffer
[sizeof(str
)] = { 0 };
738 ret
= simple_write_to_buffer(buffer
, sizeof(buffer
), ppos
, buf
, len
);
742 if (ret
< sizeof(str
) - 1)
745 if (!strncmp(str
, buffer
, sizeof(str
) - 1)) {
746 clear_trace_buffer(sys
);
753 static int gettrace_release(struct inode
*inode
, struct file
*file
)
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
,
767 int ipu_trace_init(struct ipu_device
*isp
, void __iomem
*base
,
768 struct device
*dev
, struct ipu_trace_block
*blocks
)
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
;
778 mutex_lock(&isp
->trace
->lock
);
780 if (dev
== &isp
->isys
->dev
) {
782 } else if (dev
== &isp
->psys
->dev
) {
789 adev
->trace_cfg
= sys
;
791 sys
->offset
= base
- isp
->base
; /* sub system offset */
793 sys
->blocks
= blocks
;
796 mutex_unlock(&isp
->trace
->lock
);
800 EXPORT_SYMBOL_GPL(ipu_trace_init
);
802 void ipu_trace_uninit(struct device
*dev
)
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
;
812 mutex_lock(&trace
->lock
);
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
,
822 sys
->memory
.memory_buffer
= NULL
;
824 mutex_unlock(&trace
->lock
);
826 EXPORT_SYMBOL_GPL(ipu_trace_uninit
);
828 int ipu_trace_debugfs_add(struct ipu_device
*isp
, struct dentry
*dir
)
830 struct dentry
*files
[3];
833 files
[i
] = debugfs_create_file("traceconf", 0644,
834 dir
, isp
, &ipu_traceconf_fops
);
839 files
[i
] = debugfs_create_file("getisystrace", 0444,
841 &isp
->trace
->isys
, &ipu_gettrace_fops
);
847 files
[i
] = debugfs_create_file("getpsystrace", 0444,
849 &isp
->trace
->psys
, &ipu_gettrace_fops
);
857 debugfs_remove(files
[i
- 1]);
861 int ipu_trace_add(struct ipu_device
*isp
)
863 isp
->trace
= devm_kzalloc(&isp
->pdev
->dev
,
864 sizeof(struct ipu_trace
), GFP_KERNEL
);
868 mutex_init(&isp
->trace
->lock
);
873 void ipu_trace_release(struct ipu_device
*isp
)
877 mutex_destroy(&isp
->trace
->lock
);
880 MODULE_AUTHOR("Samu Onkalo <samu.onkalo@intel.com>");
881 MODULE_LICENSE("GPL");
882 MODULE_DESCRIPTION("Intel ipu trace support");