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 9 #include <linux/module.h> 10 #include <linux/kernel.h> 11 #include <linux/init.h> 12 #include <linux/debugfs.h> 13 #include <linux/seq_file.h> 14 15 #include <linux/uaccess.h> 16 17 #include "musb_core.h" 18 #include "musb_debug.h" 19 20 struct musb_register_map { 21 char *name; 22 unsigned offset; 23 unsigned size; 24 }; 25 26 static const struct musb_register_map musb_regmap[] = { 27 { "FAddr", MUSB_FADDR, 8 }, 28 { "Power", MUSB_POWER, 8 }, 29 { "Frame", MUSB_FRAME, 16 }, 30 { "Index", MUSB_INDEX, 8 }, 31 { "Testmode", MUSB_TESTMODE, 8 }, 32 { "TxMaxPp", MUSB_TXMAXP, 16 }, 33 { "TxCSRp", MUSB_TXCSR, 16 }, 34 { "RxMaxPp", MUSB_RXMAXP, 16 }, 35 { "RxCSR", MUSB_RXCSR, 16 }, 36 { "RxCount", MUSB_RXCOUNT, 16 }, 37 { "IntrRxE", MUSB_INTRRXE, 16 }, 38 { "IntrTxE", MUSB_INTRTXE, 16 }, 39 { "IntrUsbE", MUSB_INTRUSBE, 8 }, 40 { "DevCtl", MUSB_DEVCTL, 8 }, 41 { "VControl", 0x68, 32 }, 42 { "HWVers", 0x69, 16 }, 43 { "LinkInfo", MUSB_LINKINFO, 8 }, 44 { "VPLen", MUSB_VPLEN, 8 }, 45 { "HS_EOF1", MUSB_HS_EOF1, 8 }, 46 { "FS_EOF1", MUSB_FS_EOF1, 8 }, 47 { "LS_EOF1", MUSB_LS_EOF1, 8 }, 48 { "SOFT_RST", 0x7F, 8 }, 49 { "DMA_CNTLch0", 0x204, 16 }, 50 { "DMA_ADDRch0", 0x208, 32 }, 51 { "DMA_COUNTch0", 0x20C, 32 }, 52 { "DMA_CNTLch1", 0x214, 16 }, 53 { "DMA_ADDRch1", 0x218, 32 }, 54 { "DMA_COUNTch1", 0x21C, 32 }, 55 { "DMA_CNTLch2", 0x224, 16 }, 56 { "DMA_ADDRch2", 0x228, 32 }, 57 { "DMA_COUNTch2", 0x22C, 32 }, 58 { "DMA_CNTLch3", 0x234, 16 }, 59 { "DMA_ADDRch3", 0x238, 32 }, 60 { "DMA_COUNTch3", 0x23C, 32 }, 61 { "DMA_CNTLch4", 0x244, 16 }, 62 { "DMA_ADDRch4", 0x248, 32 }, 63 { "DMA_COUNTch4", 0x24C, 32 }, 64 { "DMA_CNTLch5", 0x254, 16 }, 65 { "DMA_ADDRch5", 0x258, 32 }, 66 { "DMA_COUNTch5", 0x25C, 32 }, 67 { "DMA_CNTLch6", 0x264, 16 }, 68 { "DMA_ADDRch6", 0x268, 32 }, 69 { "DMA_COUNTch6", 0x26C, 32 }, 70 { "DMA_CNTLch7", 0x274, 16 }, 71 { "DMA_ADDRch7", 0x278, 32 }, 72 { "DMA_COUNTch7", 0x27C, 32 }, 73 { "ConfigData", MUSB_CONFIGDATA,8 }, 74 { "BabbleCtl", MUSB_BABBLE_CTL,8 }, 75 { "TxFIFOsz", MUSB_TXFIFOSZ, 8 }, 76 { "RxFIFOsz", MUSB_RXFIFOSZ, 8 }, 77 { "TxFIFOadd", MUSB_TXFIFOADD, 16 }, 78 { "RxFIFOadd", MUSB_RXFIFOADD, 16 }, 79 { "EPInfo", MUSB_EPINFO, 8 }, 80 { "RAMInfo", MUSB_RAMINFO, 8 }, 81 { } /* Terminating Entry */ 82 }; 83 84 static int musb_regdump_show(struct seq_file *s, void *unused) 85 { 86 struct musb *musb = s->private; 87 unsigned i; 88 89 seq_printf(s, "MUSB (M)HDRC Register Dump\n"); 90 pm_runtime_get_sync(musb->controller); 91 92 for (i = 0; i < ARRAY_SIZE(musb_regmap); i++) { 93 switch (musb_regmap[i].size) { 94 case 8: 95 seq_printf(s, "%-12s: %02x\n", musb_regmap[i].name, 96 musb_readb(musb->mregs, musb_regmap[i].offset)); 97 break; 98 case 16: 99 seq_printf(s, "%-12s: %04x\n", musb_regmap[i].name, 100 musb_readw(musb->mregs, musb_regmap[i].offset)); 101 break; 102 case 32: 103 seq_printf(s, "%-12s: %08x\n", musb_regmap[i].name, 104 musb_readl(musb->mregs, musb_regmap[i].offset)); 105 break; 106 } 107 } 108 109 pm_runtime_mark_last_busy(musb->controller); 110 pm_runtime_put_autosuspend(musb->controller); 111 return 0; 112 } 113 114 static int musb_regdump_open(struct inode *inode, struct file *file) 115 { 116 return single_open(file, musb_regdump_show, inode->i_private); 117 } 118 119 static int musb_test_mode_show(struct seq_file *s, void *unused) 120 { 121 struct musb *musb = s->private; 122 unsigned test; 123 124 pm_runtime_get_sync(musb->controller); 125 test = musb_readb(musb->mregs, MUSB_TESTMODE); 126 pm_runtime_mark_last_busy(musb->controller); 127 pm_runtime_put_autosuspend(musb->controller); 128 129 if (test == (MUSB_TEST_FORCE_HOST | MUSB_TEST_FORCE_FS)) 130 seq_printf(s, "force host full-speed\n"); 131 132 else if (test == (MUSB_TEST_FORCE_HOST | MUSB_TEST_FORCE_HS)) 133 seq_printf(s, "force host high-speed\n"); 134 135 else if (test == MUSB_TEST_FORCE_HOST) 136 seq_printf(s, "force host\n"); 137 138 else if (test == MUSB_TEST_FIFO_ACCESS) 139 seq_printf(s, "fifo access\n"); 140 141 else if (test == MUSB_TEST_FORCE_FS) 142 seq_printf(s, "force full-speed\n"); 143 144 else if (test == MUSB_TEST_FORCE_HS) 145 seq_printf(s, "force high-speed\n"); 146 147 else if (test == MUSB_TEST_PACKET) 148 seq_printf(s, "test packet\n"); 149 150 else if (test == MUSB_TEST_K) 151 seq_printf(s, "test K\n"); 152 153 else if (test == MUSB_TEST_J) 154 seq_printf(s, "test J\n"); 155 156 else if (test == MUSB_TEST_SE0_NAK) 157 seq_printf(s, "test SE0 NAK\n"); 158 159 return 0; 160 } 161 162 static const struct file_operations musb_regdump_fops = { 163 .open = musb_regdump_open, 164 .read = seq_read, 165 .llseek = seq_lseek, 166 .release = single_release, 167 }; 168 169 static int musb_test_mode_open(struct inode *inode, struct file *file) 170 { 171 return single_open(file, musb_test_mode_show, inode->i_private); 172 } 173 174 static ssize_t musb_test_mode_write(struct file *file, 175 const char __user *ubuf, size_t count, loff_t *ppos) 176 { 177 struct seq_file *s = file->private_data; 178 struct musb *musb = s->private; 179 u8 test; 180 char buf[24]; 181 182 pm_runtime_get_sync(musb->controller); 183 test = musb_readb(musb->mregs, MUSB_TESTMODE); 184 if (test) { 185 dev_err(musb->controller, "Error: test mode is already set. " 186 "Please do USB Bus Reset to start a new test.\n"); 187 goto ret; 188 } 189 190 memset(buf, 0x00, sizeof(buf)); 191 192 if (copy_from_user(buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) 193 return -EFAULT; 194 195 if (strstarts(buf, "force host full-speed")) 196 test = MUSB_TEST_FORCE_HOST | MUSB_TEST_FORCE_FS; 197 198 else if (strstarts(buf, "force host high-speed")) 199 test = MUSB_TEST_FORCE_HOST | MUSB_TEST_FORCE_HS; 200 201 else if (strstarts(buf, "force host")) 202 test = MUSB_TEST_FORCE_HOST; 203 204 else if (strstarts(buf, "fifo access")) 205 test = MUSB_TEST_FIFO_ACCESS; 206 207 else if (strstarts(buf, "force full-speed")) 208 test = MUSB_TEST_FORCE_FS; 209 210 else if (strstarts(buf, "force high-speed")) 211 test = MUSB_TEST_FORCE_HS; 212 213 else if (strstarts(buf, "test packet")) { 214 test = MUSB_TEST_PACKET; 215 musb_load_testpacket(musb); 216 } 217 218 else if (strstarts(buf, "test K")) 219 test = MUSB_TEST_K; 220 221 else if (strstarts(buf, "test J")) 222 test = MUSB_TEST_J; 223 224 else if (strstarts(buf, "test SE0 NAK")) 225 test = MUSB_TEST_SE0_NAK; 226 227 musb_writeb(musb->mregs, MUSB_TESTMODE, test); 228 229 ret: 230 pm_runtime_mark_last_busy(musb->controller); 231 pm_runtime_put_autosuspend(musb->controller); 232 return count; 233 } 234 235 static const struct file_operations musb_test_mode_fops = { 236 .open = musb_test_mode_open, 237 .write = musb_test_mode_write, 238 .read = seq_read, 239 .llseek = seq_lseek, 240 .release = single_release, 241 }; 242 243 static int musb_softconnect_show(struct seq_file *s, void *unused) 244 { 245 struct musb *musb = s->private; 246 u8 reg; 247 int connect; 248 249 switch (musb->xceiv->otg->state) { 250 case OTG_STATE_A_HOST: 251 case OTG_STATE_A_WAIT_BCON: 252 pm_runtime_get_sync(musb->controller); 253 254 reg = musb_readb(musb->mregs, MUSB_DEVCTL); 255 connect = reg & MUSB_DEVCTL_SESSION ? 1 : 0; 256 257 pm_runtime_mark_last_busy(musb->controller); 258 pm_runtime_put_autosuspend(musb->controller); 259 break; 260 default: 261 connect = -1; 262 } 263 264 seq_printf(s, "%d\n", connect); 265 266 return 0; 267 } 268 269 static int musb_softconnect_open(struct inode *inode, struct file *file) 270 { 271 return single_open(file, musb_softconnect_show, inode->i_private); 272 } 273 274 static ssize_t musb_softconnect_write(struct file *file, 275 const char __user *ubuf, size_t count, loff_t *ppos) 276 { 277 struct seq_file *s = file->private_data; 278 struct musb *musb = s->private; 279 char buf[2]; 280 u8 reg; 281 282 memset(buf, 0x00, sizeof(buf)); 283 284 if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) 285 return -EFAULT; 286 287 pm_runtime_get_sync(musb->controller); 288 if (!strncmp(buf, "0", 1)) { 289 switch (musb->xceiv->otg->state) { 290 case OTG_STATE_A_HOST: 291 musb_root_disconnect(musb); 292 reg = musb_readb(musb->mregs, MUSB_DEVCTL); 293 reg &= ~MUSB_DEVCTL_SESSION; 294 musb_writeb(musb->mregs, MUSB_DEVCTL, reg); 295 break; 296 default: 297 break; 298 } 299 } else if (!strncmp(buf, "1", 1)) { 300 switch (musb->xceiv->otg->state) { 301 case OTG_STATE_A_WAIT_BCON: 302 /* 303 * musb_save_context() called in musb_runtime_suspend() 304 * might cache devctl with SESSION bit cleared during 305 * soft-disconnect, so specifically set SESSION bit 306 * here to preserve it for musb_runtime_resume(). 307 */ 308 musb->context.devctl |= MUSB_DEVCTL_SESSION; 309 reg = musb_readb(musb->mregs, MUSB_DEVCTL); 310 reg |= MUSB_DEVCTL_SESSION; 311 musb_writeb(musb->mregs, MUSB_DEVCTL, reg); 312 break; 313 default: 314 break; 315 } 316 } 317 318 pm_runtime_mark_last_busy(musb->controller); 319 pm_runtime_put_autosuspend(musb->controller); 320 return count; 321 } 322 323 /* 324 * In host mode, connect/disconnect the bus without physically 325 * remove the devices. 326 */ 327 static const struct file_operations musb_softconnect_fops = { 328 .open = musb_softconnect_open, 329 .write = musb_softconnect_write, 330 .read = seq_read, 331 .llseek = seq_lseek, 332 .release = single_release, 333 }; 334 335 int musb_init_debugfs(struct musb *musb) 336 { 337 struct dentry *root; 338 struct dentry *file; 339 int ret; 340 341 root = debugfs_create_dir(dev_name(musb->controller), NULL); 342 if (!root) { 343 ret = -ENOMEM; 344 goto err0; 345 } 346 347 file = debugfs_create_file("regdump", S_IRUGO, root, musb, 348 &musb_regdump_fops); 349 if (!file) { 350 ret = -ENOMEM; 351 goto err1; 352 } 353 354 file = debugfs_create_file("testmode", S_IRUGO | S_IWUSR, 355 root, musb, &musb_test_mode_fops); 356 if (!file) { 357 ret = -ENOMEM; 358 goto err1; 359 } 360 361 file = debugfs_create_file("softconnect", S_IRUGO | S_IWUSR, 362 root, musb, &musb_softconnect_fops); 363 if (!file) { 364 ret = -ENOMEM; 365 goto err1; 366 } 367 368 musb->debugfs_root = root; 369 370 return 0; 371 372 err1: 373 debugfs_remove_recursive(root); 374 375 err0: 376 return ret; 377 } 378 379 void /* __init_or_exit */ musb_exit_debugfs(struct musb *musb) 380 { 381 debugfs_remove_recursive(musb->debugfs_root); 382 } 383