diff options
author | Sinan Kaya <okaya@codeaurora.org> | 2016-05-01 00:25:27 -0400 |
---|---|---|
committer | Vinod Koul <vinod.koul@intel.com> | 2016-05-14 02:24:45 -0400 |
commit | 570d0176296f0d17c4b5ab206ad4a4bc027b863b (patch) | |
tree | 1c8d62592e8c332a5de3247d3dfcb65df8a83ff9 /drivers/dma/qcom | |
parent | d1615ca2e085222025118793b7b4af2cf4867b6e (diff) |
dmaengine: qcom_hidma: add debugfs hooks
Add debugfs hooks for debugging the execution behavior of the DMA
channel. The debugfs hooks get initialized by the probe function and
uninitialized by the remove function.
A stats file is created in debugfs. The stats file will show the
information about each HIDMA channel as well as each asynchronous job
queued and completed at a given time.
Signed-off-by: Sinan Kaya <okaya@codeaurora.org>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
Diffstat (limited to 'drivers/dma/qcom')
-rw-r--r-- | drivers/dma/qcom/Makefile | 2 | ||||
-rw-r--r-- | drivers/dma/qcom/hidma.c | 5 | ||||
-rw-r--r-- | drivers/dma/qcom/hidma.h | 2 | ||||
-rw-r--r-- | drivers/dma/qcom/hidma_dbg.c | 217 |
4 files changed, 224 insertions, 2 deletions
diff --git a/drivers/dma/qcom/Makefile b/drivers/dma/qcom/Makefile index 6bf9267e6387..4bfc38b45220 100644 --- a/drivers/dma/qcom/Makefile +++ b/drivers/dma/qcom/Makefile | |||
@@ -2,4 +2,4 @@ obj-$(CONFIG_QCOM_BAM_DMA) += bam_dma.o | |||
2 | obj-$(CONFIG_QCOM_HIDMA_MGMT) += hdma_mgmt.o | 2 | obj-$(CONFIG_QCOM_HIDMA_MGMT) += hdma_mgmt.o |
3 | hdma_mgmt-objs := hidma_mgmt.o hidma_mgmt_sys.o | 3 | hdma_mgmt-objs := hidma_mgmt.o hidma_mgmt_sys.o |
4 | obj-$(CONFIG_QCOM_HIDMA) += hdma.o | 4 | obj-$(CONFIG_QCOM_HIDMA) += hdma.o |
5 | hdma-objs := hidma_ll.o hidma.o | 5 | hdma-objs := hidma_ll.o hidma.o hidma_dbg.o |
diff --git a/drivers/dma/qcom/hidma.c b/drivers/dma/qcom/hidma.c index af5e542f453b..1ad27c28d00c 100644 --- a/drivers/dma/qcom/hidma.c +++ b/drivers/dma/qcom/hidma.c | |||
@@ -1,7 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * Qualcomm Technologies HIDMA DMA engine interface | 2 | * Qualcomm Technologies HIDMA DMA engine interface |
3 | * | 3 | * |
4 | * Copyright (c) 2015, The Linux Foundation. All rights reserved. | 4 | * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. |
5 | * | 5 | * |
6 | * This program is free software; you can redistribute it and/or modify | 6 | * This program is free software; you can redistribute it and/or modify |
7 | * it under the terms of the GNU General Public License version 2 and | 7 | * it under the terms of the GNU General Public License version 2 and |
@@ -644,6 +644,7 @@ static int hidma_probe(struct platform_device *pdev) | |||
644 | 644 | ||
645 | dmadev->irq = chirq; | 645 | dmadev->irq = chirq; |
646 | tasklet_init(&dmadev->task, hidma_issue_task, (unsigned long)dmadev); | 646 | tasklet_init(&dmadev->task, hidma_issue_task, (unsigned long)dmadev); |
647 | hidma_debug_init(dmadev); | ||
647 | dev_info(&pdev->dev, "HI-DMA engine driver registration complete\n"); | 648 | dev_info(&pdev->dev, "HI-DMA engine driver registration complete\n"); |
648 | platform_set_drvdata(pdev, dmadev); | 649 | platform_set_drvdata(pdev, dmadev); |
649 | pm_runtime_mark_last_busy(dmadev->ddev.dev); | 650 | pm_runtime_mark_last_busy(dmadev->ddev.dev); |
@@ -651,6 +652,7 @@ static int hidma_probe(struct platform_device *pdev) | |||
651 | return 0; | 652 | return 0; |
652 | 653 | ||
653 | uninit: | 654 | uninit: |
655 | hidma_debug_uninit(dmadev); | ||
654 | hidma_ll_uninit(dmadev->lldev); | 656 | hidma_ll_uninit(dmadev->lldev); |
655 | dmafree: | 657 | dmafree: |
656 | if (dmadev) | 658 | if (dmadev) |
@@ -668,6 +670,7 @@ static int hidma_remove(struct platform_device *pdev) | |||
668 | pm_runtime_get_sync(dmadev->ddev.dev); | 670 | pm_runtime_get_sync(dmadev->ddev.dev); |
669 | dma_async_device_unregister(&dmadev->ddev); | 671 | dma_async_device_unregister(&dmadev->ddev); |
670 | devm_free_irq(dmadev->ddev.dev, dmadev->irq, dmadev->lldev); | 672 | devm_free_irq(dmadev->ddev.dev, dmadev->irq, dmadev->lldev); |
673 | hidma_debug_uninit(dmadev); | ||
671 | hidma_ll_uninit(dmadev->lldev); | 674 | hidma_ll_uninit(dmadev->lldev); |
672 | hidma_free(dmadev); | 675 | hidma_free(dmadev); |
673 | 676 | ||
diff --git a/drivers/dma/qcom/hidma.h b/drivers/dma/qcom/hidma.h index b1362ef7b747..db413a5efc4e 100644 --- a/drivers/dma/qcom/hidma.h +++ b/drivers/dma/qcom/hidma.h | |||
@@ -153,4 +153,6 @@ int hidma_ll_uninit(struct hidma_lldev *llhndl); | |||
153 | irqreturn_t hidma_ll_inthandler(int irq, void *arg); | 153 | irqreturn_t hidma_ll_inthandler(int irq, void *arg); |
154 | void hidma_cleanup_pending_tre(struct hidma_lldev *llhndl, u8 err_info, | 154 | void hidma_cleanup_pending_tre(struct hidma_lldev *llhndl, u8 err_info, |
155 | u8 err_code); | 155 | u8 err_code); |
156 | int hidma_debug_init(struct hidma_dev *dmadev); | ||
157 | void hidma_debug_uninit(struct hidma_dev *dmadev); | ||
156 | #endif | 158 | #endif |
diff --git a/drivers/dma/qcom/hidma_dbg.c b/drivers/dma/qcom/hidma_dbg.c new file mode 100644 index 000000000000..fa827e5ffd68 --- /dev/null +++ b/drivers/dma/qcom/hidma_dbg.c | |||
@@ -0,0 +1,217 @@ | |||
1 | /* | ||
2 | * Qualcomm Technologies HIDMA debug file | ||
3 | * | ||
4 | * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 and | ||
8 | * only version 2 as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | */ | ||
15 | |||
16 | #include <linux/debugfs.h> | ||
17 | #include <linux/device.h> | ||
18 | #include <linux/list.h> | ||
19 | #include <linux/pm_runtime.h> | ||
20 | |||
21 | #include "hidma.h" | ||
22 | |||
23 | static void hidma_ll_chstats(struct seq_file *s, void *llhndl, u32 tre_ch) | ||
24 | { | ||
25 | struct hidma_lldev *lldev = llhndl; | ||
26 | struct hidma_tre *tre; | ||
27 | u32 length; | ||
28 | dma_addr_t src_start; | ||
29 | dma_addr_t dest_start; | ||
30 | u32 *tre_local; | ||
31 | |||
32 | if (tre_ch >= lldev->nr_tres) { | ||
33 | dev_err(lldev->dev, "invalid TRE number in chstats:%d", tre_ch); | ||
34 | return; | ||
35 | } | ||
36 | tre = &lldev->trepool[tre_ch]; | ||
37 | seq_printf(s, "------Channel %d -----\n", tre_ch); | ||
38 | seq_printf(s, "allocated=%d\n", atomic_read(&tre->allocated)); | ||
39 | seq_printf(s, "queued = 0x%x\n", tre->queued); | ||
40 | seq_printf(s, "err_info = 0x%x\n", tre->err_info); | ||
41 | seq_printf(s, "err_code = 0x%x\n", tre->err_code); | ||
42 | seq_printf(s, "status = 0x%x\n", tre->status); | ||
43 | seq_printf(s, "idx = 0x%x\n", tre->idx); | ||
44 | seq_printf(s, "dma_sig = 0x%x\n", tre->dma_sig); | ||
45 | seq_printf(s, "dev_name=%s\n", tre->dev_name); | ||
46 | seq_printf(s, "callback=%p\n", tre->callback); | ||
47 | seq_printf(s, "data=%p\n", tre->data); | ||
48 | seq_printf(s, "tre_index = 0x%x\n", tre->tre_index); | ||
49 | |||
50 | tre_local = &tre->tre_local[0]; | ||
51 | src_start = tre_local[HIDMA_TRE_SRC_LOW_IDX]; | ||
52 | src_start = ((u64) (tre_local[HIDMA_TRE_SRC_HI_IDX]) << 32) + src_start; | ||
53 | dest_start = tre_local[HIDMA_TRE_DEST_LOW_IDX]; | ||
54 | dest_start += ((u64) (tre_local[HIDMA_TRE_DEST_HI_IDX]) << 32); | ||
55 | length = tre_local[HIDMA_TRE_LEN_IDX]; | ||
56 | |||
57 | seq_printf(s, "src=%pap\n", &src_start); | ||
58 | seq_printf(s, "dest=%pap\n", &dest_start); | ||
59 | seq_printf(s, "length = 0x%x\n", length); | ||
60 | } | ||
61 | |||
62 | static void hidma_ll_devstats(struct seq_file *s, void *llhndl) | ||
63 | { | ||
64 | struct hidma_lldev *lldev = llhndl; | ||
65 | |||
66 | seq_puts(s, "------Device -----\n"); | ||
67 | seq_printf(s, "lldev init = 0x%x\n", lldev->initialized); | ||
68 | seq_printf(s, "trch_state = 0x%x\n", lldev->trch_state); | ||
69 | seq_printf(s, "evch_state = 0x%x\n", lldev->evch_state); | ||
70 | seq_printf(s, "chidx = 0x%x\n", lldev->chidx); | ||
71 | seq_printf(s, "nr_tres = 0x%x\n", lldev->nr_tres); | ||
72 | seq_printf(s, "trca=%p\n", lldev->trca); | ||
73 | seq_printf(s, "tre_ring=%p\n", lldev->tre_ring); | ||
74 | seq_printf(s, "tre_ring_handle=%pap\n", &lldev->tre_dma); | ||
75 | seq_printf(s, "tre_ring_size = 0x%x\n", lldev->tre_ring_size); | ||
76 | seq_printf(s, "tre_processed_off = 0x%x\n", lldev->tre_processed_off); | ||
77 | seq_printf(s, "pending_tre_count=%d\n", lldev->pending_tre_count); | ||
78 | seq_printf(s, "evca=%p\n", lldev->evca); | ||
79 | seq_printf(s, "evre_ring=%p\n", lldev->evre_ring); | ||
80 | seq_printf(s, "evre_ring_handle=%pap\n", &lldev->evre_dma); | ||
81 | seq_printf(s, "evre_ring_size = 0x%x\n", lldev->evre_ring_size); | ||
82 | seq_printf(s, "evre_processed_off = 0x%x\n", lldev->evre_processed_off); | ||
83 | seq_printf(s, "tre_write_offset = 0x%x\n", lldev->tre_write_offset); | ||
84 | } | ||
85 | |||
86 | /* | ||
87 | * hidma_chan_stats: display HIDMA channel statistics | ||
88 | * | ||
89 | * Display the statistics for the current HIDMA virtual channel device. | ||
90 | */ | ||
91 | static int hidma_chan_stats(struct seq_file *s, void *unused) | ||
92 | { | ||
93 | struct hidma_chan *mchan = s->private; | ||
94 | struct hidma_desc *mdesc; | ||
95 | struct hidma_dev *dmadev = mchan->dmadev; | ||
96 | |||
97 | pm_runtime_get_sync(dmadev->ddev.dev); | ||
98 | seq_printf(s, "paused=%u\n", mchan->paused); | ||
99 | seq_printf(s, "dma_sig=%u\n", mchan->dma_sig); | ||
100 | seq_puts(s, "prepared\n"); | ||
101 | list_for_each_entry(mdesc, &mchan->prepared, node) | ||
102 | hidma_ll_chstats(s, mchan->dmadev->lldev, mdesc->tre_ch); | ||
103 | |||
104 | seq_puts(s, "active\n"); | ||
105 | list_for_each_entry(mdesc, &mchan->active, node) | ||
106 | hidma_ll_chstats(s, mchan->dmadev->lldev, mdesc->tre_ch); | ||
107 | |||
108 | seq_puts(s, "completed\n"); | ||
109 | list_for_each_entry(mdesc, &mchan->completed, node) | ||
110 | hidma_ll_chstats(s, mchan->dmadev->lldev, mdesc->tre_ch); | ||
111 | |||
112 | hidma_ll_devstats(s, mchan->dmadev->lldev); | ||
113 | pm_runtime_mark_last_busy(dmadev->ddev.dev); | ||
114 | pm_runtime_put_autosuspend(dmadev->ddev.dev); | ||
115 | return 0; | ||
116 | } | ||
117 | |||
118 | /* | ||
119 | * hidma_dma_info: display HIDMA device info | ||
120 | * | ||
121 | * Display the info for the current HIDMA device. | ||
122 | */ | ||
123 | static int hidma_dma_info(struct seq_file *s, void *unused) | ||
124 | { | ||
125 | struct hidma_dev *dmadev = s->private; | ||
126 | resource_size_t sz; | ||
127 | |||
128 | seq_printf(s, "nr_descriptors=%d\n", dmadev->nr_descriptors); | ||
129 | seq_printf(s, "dev_trca=%p\n", &dmadev->dev_trca); | ||
130 | seq_printf(s, "dev_trca_phys=%pa\n", &dmadev->trca_resource->start); | ||
131 | sz = resource_size(dmadev->trca_resource); | ||
132 | seq_printf(s, "dev_trca_size=%pa\n", &sz); | ||
133 | seq_printf(s, "dev_evca=%p\n", &dmadev->dev_evca); | ||
134 | seq_printf(s, "dev_evca_phys=%pa\n", &dmadev->evca_resource->start); | ||
135 | sz = resource_size(dmadev->evca_resource); | ||
136 | seq_printf(s, "dev_evca_size=%pa\n", &sz); | ||
137 | return 0; | ||
138 | } | ||
139 | |||
140 | static int hidma_chan_stats_open(struct inode *inode, struct file *file) | ||
141 | { | ||
142 | return single_open(file, hidma_chan_stats, inode->i_private); | ||
143 | } | ||
144 | |||
145 | static int hidma_dma_info_open(struct inode *inode, struct file *file) | ||
146 | { | ||
147 | return single_open(file, hidma_dma_info, inode->i_private); | ||
148 | } | ||
149 | |||
150 | static const struct file_operations hidma_chan_fops = { | ||
151 | .open = hidma_chan_stats_open, | ||
152 | .read = seq_read, | ||
153 | .llseek = seq_lseek, | ||
154 | .release = single_release, | ||
155 | }; | ||
156 | |||
157 | static const struct file_operations hidma_dma_fops = { | ||
158 | .open = hidma_dma_info_open, | ||
159 | .read = seq_read, | ||
160 | .llseek = seq_lseek, | ||
161 | .release = single_release, | ||
162 | }; | ||
163 | |||
164 | void hidma_debug_uninit(struct hidma_dev *dmadev) | ||
165 | { | ||
166 | debugfs_remove_recursive(dmadev->debugfs); | ||
167 | debugfs_remove_recursive(dmadev->stats); | ||
168 | } | ||
169 | |||
170 | int hidma_debug_init(struct hidma_dev *dmadev) | ||
171 | { | ||
172 | int rc = 0; | ||
173 | int chidx = 0; | ||
174 | struct list_head *position = NULL; | ||
175 | |||
176 | dmadev->debugfs = debugfs_create_dir(dev_name(dmadev->ddev.dev), NULL); | ||
177 | if (!dmadev->debugfs) { | ||
178 | rc = -ENODEV; | ||
179 | return rc; | ||
180 | } | ||
181 | |||
182 | /* walk through the virtual channel list */ | ||
183 | list_for_each(position, &dmadev->ddev.channels) { | ||
184 | struct hidma_chan *chan; | ||
185 | |||
186 | chan = list_entry(position, struct hidma_chan, | ||
187 | chan.device_node); | ||
188 | sprintf(chan->dbg_name, "chan%d", chidx); | ||
189 | chan->debugfs = debugfs_create_dir(chan->dbg_name, | ||
190 | dmadev->debugfs); | ||
191 | if (!chan->debugfs) { | ||
192 | rc = -ENOMEM; | ||
193 | goto cleanup; | ||
194 | } | ||
195 | chan->stats = debugfs_create_file("stats", S_IRUGO, | ||
196 | chan->debugfs, chan, | ||
197 | &hidma_chan_fops); | ||
198 | if (!chan->stats) { | ||
199 | rc = -ENOMEM; | ||
200 | goto cleanup; | ||
201 | } | ||
202 | chidx++; | ||
203 | } | ||
204 | |||
205 | dmadev->stats = debugfs_create_file("stats", S_IRUGO, | ||
206 | dmadev->debugfs, dmadev, | ||
207 | &hidma_dma_fops); | ||
208 | if (!dmadev->stats) { | ||
209 | rc = -ENOMEM; | ||
210 | goto cleanup; | ||
211 | } | ||
212 | |||
213 | return 0; | ||
214 | cleanup: | ||
215 | hidma_debug_uninit(dmadev); | ||
216 | return rc; | ||
217 | } | ||