]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blob - drivers/usb/musb/musb_debugfs.c
USB: add SPDX identifiers to all remaining files in drivers/usb/
[mirror_ubuntu-jammy-kernel.git] / drivers / usb / musb / musb_debugfs.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * MUSB OTG driver debugfs support
4 *
5 * Copyright 2010 Nokia Corporation
6 * Contact: Felipe Balbi <felipe.balbi@nokia.com>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * version 2 as published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA
21 *
22 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
23 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
24 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
25 * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
28 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
29 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 *
33 */
34
35 #include <linux/module.h>
36 #include <linux/kernel.h>
37 #include <linux/init.h>
38 #include <linux/debugfs.h>
39 #include <linux/seq_file.h>
40
41 #include <linux/uaccess.h>
42
43 #include "musb_core.h"
44 #include "musb_debug.h"
45
46 struct musb_register_map {
47 char *name;
48 unsigned offset;
49 unsigned size;
50 };
51
52 static const struct musb_register_map musb_regmap[] = {
53 { "FAddr", MUSB_FADDR, 8 },
54 { "Power", MUSB_POWER, 8 },
55 { "Frame", MUSB_FRAME, 16 },
56 { "Index", MUSB_INDEX, 8 },
57 { "Testmode", MUSB_TESTMODE, 8 },
58 { "TxMaxPp", MUSB_TXMAXP, 16 },
59 { "TxCSRp", MUSB_TXCSR, 16 },
60 { "RxMaxPp", MUSB_RXMAXP, 16 },
61 { "RxCSR", MUSB_RXCSR, 16 },
62 { "RxCount", MUSB_RXCOUNT, 16 },
63 { "IntrRxE", MUSB_INTRRXE, 16 },
64 { "IntrTxE", MUSB_INTRTXE, 16 },
65 { "IntrUsbE", MUSB_INTRUSBE, 8 },
66 { "DevCtl", MUSB_DEVCTL, 8 },
67 { "VControl", 0x68, 32 },
68 { "HWVers", 0x69, 16 },
69 { "LinkInfo", MUSB_LINKINFO, 8 },
70 { "VPLen", MUSB_VPLEN, 8 },
71 { "HS_EOF1", MUSB_HS_EOF1, 8 },
72 { "FS_EOF1", MUSB_FS_EOF1, 8 },
73 { "LS_EOF1", MUSB_LS_EOF1, 8 },
74 { "SOFT_RST", 0x7F, 8 },
75 { "DMA_CNTLch0", 0x204, 16 },
76 { "DMA_ADDRch0", 0x208, 32 },
77 { "DMA_COUNTch0", 0x20C, 32 },
78 { "DMA_CNTLch1", 0x214, 16 },
79 { "DMA_ADDRch1", 0x218, 32 },
80 { "DMA_COUNTch1", 0x21C, 32 },
81 { "DMA_CNTLch2", 0x224, 16 },
82 { "DMA_ADDRch2", 0x228, 32 },
83 { "DMA_COUNTch2", 0x22C, 32 },
84 { "DMA_CNTLch3", 0x234, 16 },
85 { "DMA_ADDRch3", 0x238, 32 },
86 { "DMA_COUNTch3", 0x23C, 32 },
87 { "DMA_CNTLch4", 0x244, 16 },
88 { "DMA_ADDRch4", 0x248, 32 },
89 { "DMA_COUNTch4", 0x24C, 32 },
90 { "DMA_CNTLch5", 0x254, 16 },
91 { "DMA_ADDRch5", 0x258, 32 },
92 { "DMA_COUNTch5", 0x25C, 32 },
93 { "DMA_CNTLch6", 0x264, 16 },
94 { "DMA_ADDRch6", 0x268, 32 },
95 { "DMA_COUNTch6", 0x26C, 32 },
96 { "DMA_CNTLch7", 0x274, 16 },
97 { "DMA_ADDRch7", 0x278, 32 },
98 { "DMA_COUNTch7", 0x27C, 32 },
99 #ifndef CONFIG_BLACKFIN
100 { "ConfigData", MUSB_CONFIGDATA,8 },
101 { "BabbleCtl", MUSB_BABBLE_CTL,8 },
102 { "TxFIFOsz", MUSB_TXFIFOSZ, 8 },
103 { "RxFIFOsz", MUSB_RXFIFOSZ, 8 },
104 { "TxFIFOadd", MUSB_TXFIFOADD, 16 },
105 { "RxFIFOadd", MUSB_RXFIFOADD, 16 },
106 { "EPInfo", MUSB_EPINFO, 8 },
107 { "RAMInfo", MUSB_RAMINFO, 8 },
108 #endif
109 { } /* Terminating Entry */
110 };
111
112 static int musb_regdump_show(struct seq_file *s, void *unused)
113 {
114 struct musb *musb = s->private;
115 unsigned i;
116
117 seq_printf(s, "MUSB (M)HDRC Register Dump\n");
118 pm_runtime_get_sync(musb->controller);
119
120 for (i = 0; i < ARRAY_SIZE(musb_regmap); i++) {
121 switch (musb_regmap[i].size) {
122 case 8:
123 seq_printf(s, "%-12s: %02x\n", musb_regmap[i].name,
124 musb_readb(musb->mregs, musb_regmap[i].offset));
125 break;
126 case 16:
127 seq_printf(s, "%-12s: %04x\n", musb_regmap[i].name,
128 musb_readw(musb->mregs, musb_regmap[i].offset));
129 break;
130 case 32:
131 seq_printf(s, "%-12s: %08x\n", musb_regmap[i].name,
132 musb_readl(musb->mregs, musb_regmap[i].offset));
133 break;
134 }
135 }
136
137 pm_runtime_mark_last_busy(musb->controller);
138 pm_runtime_put_autosuspend(musb->controller);
139 return 0;
140 }
141
142 static int musb_regdump_open(struct inode *inode, struct file *file)
143 {
144 return single_open(file, musb_regdump_show, inode->i_private);
145 }
146
147 static int musb_test_mode_show(struct seq_file *s, void *unused)
148 {
149 struct musb *musb = s->private;
150 unsigned test;
151
152 pm_runtime_get_sync(musb->controller);
153 test = musb_readb(musb->mregs, MUSB_TESTMODE);
154 pm_runtime_mark_last_busy(musb->controller);
155 pm_runtime_put_autosuspend(musb->controller);
156
157 if (test == (MUSB_TEST_FORCE_HOST | MUSB_TEST_FORCE_FS))
158 seq_printf(s, "force host full-speed\n");
159
160 else if (test == (MUSB_TEST_FORCE_HOST | MUSB_TEST_FORCE_HS))
161 seq_printf(s, "force host high-speed\n");
162
163 else if (test == MUSB_TEST_FORCE_HOST)
164 seq_printf(s, "force host\n");
165
166 else if (test == MUSB_TEST_FIFO_ACCESS)
167 seq_printf(s, "fifo access\n");
168
169 else if (test == MUSB_TEST_FORCE_FS)
170 seq_printf(s, "force full-speed\n");
171
172 else if (test == MUSB_TEST_FORCE_HS)
173 seq_printf(s, "force high-speed\n");
174
175 else if (test == MUSB_TEST_PACKET)
176 seq_printf(s, "test packet\n");
177
178 else if (test == MUSB_TEST_K)
179 seq_printf(s, "test K\n");
180
181 else if (test == MUSB_TEST_J)
182 seq_printf(s, "test J\n");
183
184 else if (test == MUSB_TEST_SE0_NAK)
185 seq_printf(s, "test SE0 NAK\n");
186
187 return 0;
188 }
189
190 static const struct file_operations musb_regdump_fops = {
191 .open = musb_regdump_open,
192 .read = seq_read,
193 .llseek = seq_lseek,
194 .release = single_release,
195 };
196
197 static int musb_test_mode_open(struct inode *inode, struct file *file)
198 {
199 return single_open(file, musb_test_mode_show, inode->i_private);
200 }
201
202 static ssize_t musb_test_mode_write(struct file *file,
203 const char __user *ubuf, size_t count, loff_t *ppos)
204 {
205 struct seq_file *s = file->private_data;
206 struct musb *musb = s->private;
207 u8 test;
208 char buf[24];
209
210 pm_runtime_get_sync(musb->controller);
211 test = musb_readb(musb->mregs, MUSB_TESTMODE);
212 if (test) {
213 dev_err(musb->controller, "Error: test mode is already set. "
214 "Please do USB Bus Reset to start a new test.\n");
215 goto ret;
216 }
217
218 memset(buf, 0x00, sizeof(buf));
219
220 if (copy_from_user(buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
221 return -EFAULT;
222
223 if (strstarts(buf, "force host full-speed"))
224 test = MUSB_TEST_FORCE_HOST | MUSB_TEST_FORCE_FS;
225
226 else if (strstarts(buf, "force host high-speed"))
227 test = MUSB_TEST_FORCE_HOST | MUSB_TEST_FORCE_HS;
228
229 else if (strstarts(buf, "force host"))
230 test = MUSB_TEST_FORCE_HOST;
231
232 else if (strstarts(buf, "fifo access"))
233 test = MUSB_TEST_FIFO_ACCESS;
234
235 else if (strstarts(buf, "force full-speed"))
236 test = MUSB_TEST_FORCE_FS;
237
238 else if (strstarts(buf, "force high-speed"))
239 test = MUSB_TEST_FORCE_HS;
240
241 else if (strstarts(buf, "test packet")) {
242 test = MUSB_TEST_PACKET;
243 musb_load_testpacket(musb);
244 }
245
246 else if (strstarts(buf, "test K"))
247 test = MUSB_TEST_K;
248
249 else if (strstarts(buf, "test J"))
250 test = MUSB_TEST_J;
251
252 else if (strstarts(buf, "test SE0 NAK"))
253 test = MUSB_TEST_SE0_NAK;
254
255 musb_writeb(musb->mregs, MUSB_TESTMODE, test);
256
257 ret:
258 pm_runtime_mark_last_busy(musb->controller);
259 pm_runtime_put_autosuspend(musb->controller);
260 return count;
261 }
262
263 static const struct file_operations musb_test_mode_fops = {
264 .open = musb_test_mode_open,
265 .write = musb_test_mode_write,
266 .read = seq_read,
267 .llseek = seq_lseek,
268 .release = single_release,
269 };
270
271 static int musb_softconnect_show(struct seq_file *s, void *unused)
272 {
273 struct musb *musb = s->private;
274 u8 reg;
275 int connect;
276
277 switch (musb->xceiv->otg->state) {
278 case OTG_STATE_A_HOST:
279 case OTG_STATE_A_WAIT_BCON:
280 pm_runtime_get_sync(musb->controller);
281
282 reg = musb_readb(musb->mregs, MUSB_DEVCTL);
283 connect = reg & MUSB_DEVCTL_SESSION ? 1 : 0;
284
285 pm_runtime_mark_last_busy(musb->controller);
286 pm_runtime_put_autosuspend(musb->controller);
287 break;
288 default:
289 connect = -1;
290 }
291
292 seq_printf(s, "%d\n", connect);
293
294 return 0;
295 }
296
297 static int musb_softconnect_open(struct inode *inode, struct file *file)
298 {
299 return single_open(file, musb_softconnect_show, inode->i_private);
300 }
301
302 static ssize_t musb_softconnect_write(struct file *file,
303 const char __user *ubuf, size_t count, loff_t *ppos)
304 {
305 struct seq_file *s = file->private_data;
306 struct musb *musb = s->private;
307 char buf[2];
308 u8 reg;
309
310 memset(buf, 0x00, sizeof(buf));
311
312 if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
313 return -EFAULT;
314
315 pm_runtime_get_sync(musb->controller);
316 if (!strncmp(buf, "0", 1)) {
317 switch (musb->xceiv->otg->state) {
318 case OTG_STATE_A_HOST:
319 musb_root_disconnect(musb);
320 reg = musb_readb(musb->mregs, MUSB_DEVCTL);
321 reg &= ~MUSB_DEVCTL_SESSION;
322 musb_writeb(musb->mregs, MUSB_DEVCTL, reg);
323 break;
324 default:
325 break;
326 }
327 } else if (!strncmp(buf, "1", 1)) {
328 switch (musb->xceiv->otg->state) {
329 case OTG_STATE_A_WAIT_BCON:
330 /*
331 * musb_save_context() called in musb_runtime_suspend()
332 * might cache devctl with SESSION bit cleared during
333 * soft-disconnect, so specifically set SESSION bit
334 * here to preserve it for musb_runtime_resume().
335 */
336 musb->context.devctl |= MUSB_DEVCTL_SESSION;
337 reg = musb_readb(musb->mregs, MUSB_DEVCTL);
338 reg |= MUSB_DEVCTL_SESSION;
339 musb_writeb(musb->mregs, MUSB_DEVCTL, reg);
340 break;
341 default:
342 break;
343 }
344 }
345
346 pm_runtime_mark_last_busy(musb->controller);
347 pm_runtime_put_autosuspend(musb->controller);
348 return count;
349 }
350
351 /*
352 * In host mode, connect/disconnect the bus without physically
353 * remove the devices.
354 */
355 static const struct file_operations musb_softconnect_fops = {
356 .open = musb_softconnect_open,
357 .write = musb_softconnect_write,
358 .read = seq_read,
359 .llseek = seq_lseek,
360 .release = single_release,
361 };
362
363 int musb_init_debugfs(struct musb *musb)
364 {
365 struct dentry *root;
366 struct dentry *file;
367 int ret;
368
369 root = debugfs_create_dir(dev_name(musb->controller), NULL);
370 if (!root) {
371 ret = -ENOMEM;
372 goto err0;
373 }
374
375 file = debugfs_create_file("regdump", S_IRUGO, root, musb,
376 &musb_regdump_fops);
377 if (!file) {
378 ret = -ENOMEM;
379 goto err1;
380 }
381
382 file = debugfs_create_file("testmode", S_IRUGO | S_IWUSR,
383 root, musb, &musb_test_mode_fops);
384 if (!file) {
385 ret = -ENOMEM;
386 goto err1;
387 }
388
389 file = debugfs_create_file("softconnect", S_IRUGO | S_IWUSR,
390 root, musb, &musb_softconnect_fops);
391 if (!file) {
392 ret = -ENOMEM;
393 goto err1;
394 }
395
396 musb->debugfs_root = root;
397
398 return 0;
399
400 err1:
401 debugfs_remove_recursive(root);
402
403 err0:
404 return ret;
405 }
406
407 void /* __init_or_exit */ musb_exit_debugfs(struct musb *musb)
408 {
409 debugfs_remove_recursive(musb->debugfs_root);
410 }