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