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 #ifndef CONFIG_BLACKFIN 74 { "ConfigData", MUSB_CONFIGDATA,8 }, 75 { "BabbleCtl", MUSB_BABBLE_CTL,8 }, 76 { "TxFIFOsz", MUSB_TXFIFOSZ, 8 }, 77 { "RxFIFOsz", MUSB_RXFIFOSZ, 8 }, 78 { "TxFIFOadd", MUSB_TXFIFOADD, 16 }, 79 { "RxFIFOadd", MUSB_RXFIFOADD, 16 }, 80 { "EPInfo", MUSB_EPINFO, 8 }, 81 { "RAMInfo", MUSB_RAMINFO, 8 }, 82 #endif 83 { } /* Terminating Entry */ 84 }; 85 86 static int musb_regdump_show(struct seq_file *s, void *unused) 87 { 88 struct musb *musb = s->private; 89 unsigned i; 90 91 seq_printf(s, "MUSB (M)HDRC Register Dump\n"); 92 pm_runtime_get_sync(musb->controller); 93 94 for (i = 0; i < ARRAY_SIZE(musb_regmap); i++) { 95 switch (musb_regmap[i].size) { 96 case 8: 97 seq_printf(s, "%-12s: %02x\n", musb_regmap[i].name, 98 musb_readb(musb->mregs, musb_regmap[i].offset)); 99 break; 100 case 16: 101 seq_printf(s, "%-12s: %04x\n", musb_regmap[i].name, 102 musb_readw(musb->mregs, musb_regmap[i].offset)); 103 break; 104 case 32: 105 seq_printf(s, "%-12s: %08x\n", musb_regmap[i].name, 106 musb_readl(musb->mregs, musb_regmap[i].offset)); 107 break; 108 } 109 } 110 111 pm_runtime_mark_last_busy(musb->controller); 112 pm_runtime_put_autosuspend(musb->controller); 113 return 0; 114 } 115 116 static int musb_regdump_open(struct inode *inode, struct file *file) 117 { 118 return single_open(file, musb_regdump_show, inode->i_private); 119 } 120 121 static int musb_test_mode_show(struct seq_file *s, void *unused) 122 { 123 struct musb *musb = s->private; 124 unsigned test; 125 126 pm_runtime_get_sync(musb->controller); 127 test = musb_readb(musb->mregs, MUSB_TESTMODE); 128 pm_runtime_mark_last_busy(musb->controller); 129 pm_runtime_put_autosuspend(musb->controller); 130 131 if (test == (MUSB_TEST_FORCE_HOST | MUSB_TEST_FORCE_FS)) 132 seq_printf(s, "force host full-speed\n"); 133 134 else if (test == (MUSB_TEST_FORCE_HOST | MUSB_TEST_FORCE_HS)) 135 seq_printf(s, "force host high-speed\n"); 136 137 else if (test == MUSB_TEST_FORCE_HOST) 138 seq_printf(s, "force host\n"); 139 140 else if (test == MUSB_TEST_FIFO_ACCESS) 141 seq_printf(s, "fifo access\n"); 142 143 else if (test == MUSB_TEST_FORCE_FS) 144 seq_printf(s, "force full-speed\n"); 145 146 else if (test == MUSB_TEST_FORCE_HS) 147 seq_printf(s, "force high-speed\n"); 148 149 else if (test == MUSB_TEST_PACKET) 150 seq_printf(s, "test packet\n"); 151 152 else if (test == MUSB_TEST_K) 153 seq_printf(s, "test K\n"); 154 155 else if (test == MUSB_TEST_J) 156 seq_printf(s, "test J\n"); 157 158 else if (test == MUSB_TEST_SE0_NAK) 159 seq_printf(s, "test SE0 NAK\n"); 160 161 return 0; 162 } 163 164 static const struct file_operations musb_regdump_fops = { 165 .open = musb_regdump_open, 166 .read = seq_read, 167 .llseek = seq_lseek, 168 .release = single_release, 169 }; 170 171 static int musb_test_mode_open(struct inode *inode, struct file *file) 172 { 173 return single_open(file, musb_test_mode_show, inode->i_private); 174 } 175 176 static ssize_t musb_test_mode_write(struct file *file, 177 const char __user *ubuf, size_t count, loff_t *ppos) 178 { 179 struct seq_file *s = file->private_data; 180 struct musb *musb = s->private; 181 u8 test; 182 char buf[24]; 183 184 pm_runtime_get_sync(musb->controller); 185 test = musb_readb(musb->mregs, MUSB_TESTMODE); 186 if (test) { 187 dev_err(musb->controller, "Error: test mode is already set. " 188 "Please do USB Bus Reset to start a new test.\n"); 189 goto ret; 190 } 191 192 memset(buf, 0x00, sizeof(buf)); 193 194 if (copy_from_user(buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) 195 return -EFAULT; 196 197 if (strstarts(buf, "force host full-speed")) 198 test = MUSB_TEST_FORCE_HOST | MUSB_TEST_FORCE_FS; 199 200 else if (strstarts(buf, "force host high-speed")) 201 test = MUSB_TEST_FORCE_HOST | MUSB_TEST_FORCE_HS; 202 203 else if (strstarts(buf, "force host")) 204 test = MUSB_TEST_FORCE_HOST; 205 206 else if (strstarts(buf, "fifo access")) 207 test = MUSB_TEST_FIFO_ACCESS; 208 209 else if (strstarts(buf, "force full-speed")) 210 test = MUSB_TEST_FORCE_FS; 211 212 else if (strstarts(buf, "force high-speed")) 213 test = MUSB_TEST_FORCE_HS; 214 215 else if (strstarts(buf, "test packet")) { 216 test = MUSB_TEST_PACKET; 217 musb_load_testpacket(musb); 218 } 219 220 else if (strstarts(buf, "test K")) 221 test = MUSB_TEST_K; 222 223 else if (strstarts(buf, "test J")) 224 test = MUSB_TEST_J; 225 226 else if (strstarts(buf, "test SE0 NAK")) 227 test = MUSB_TEST_SE0_NAK; 228 229 musb_writeb(musb->mregs, MUSB_TESTMODE, test); 230 231 ret: 232 pm_runtime_mark_last_busy(musb->controller); 233 pm_runtime_put_autosuspend(musb->controller); 234 return count; 235 } 236 237 static const struct file_operations musb_test_mode_fops = { 238 .open = musb_test_mode_open, 239 .write = musb_test_mode_write, 240 .read = seq_read, 241 .llseek = seq_lseek, 242 .release = single_release, 243 }; 244 245 static int musb_softconnect_show(struct seq_file *s, void *unused) 246 { 247 struct musb *musb = s->private; 248 u8 reg; 249 int connect; 250 251 switch (musb->xceiv->otg->state) { 252 case OTG_STATE_A_HOST: 253 case OTG_STATE_A_WAIT_BCON: 254 pm_runtime_get_sync(musb->controller); 255 256 reg = musb_readb(musb->mregs, MUSB_DEVCTL); 257 connect = reg & MUSB_DEVCTL_SESSION ? 1 : 0; 258 259 pm_runtime_mark_last_busy(musb->controller); 260 pm_runtime_put_autosuspend(musb->controller); 261 break; 262 default: 263 connect = -1; 264 } 265 266 seq_printf(s, "%d\n", connect); 267 268 return 0; 269 } 270 271 static int musb_softconnect_open(struct inode *inode, struct file *file) 272 { 273 return single_open(file, musb_softconnect_show, inode->i_private); 274 } 275 276 static ssize_t musb_softconnect_write(struct file *file, 277 const char __user *ubuf, size_t count, loff_t *ppos) 278 { 279 struct seq_file *s = file->private_data; 280 struct musb *musb = s->private; 281 char buf[2]; 282 u8 reg; 283 284 memset(buf, 0x00, sizeof(buf)); 285 286 if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) 287 return -EFAULT; 288 289 pm_runtime_get_sync(musb->controller); 290 if (!strncmp(buf, "0", 1)) { 291 switch (musb->xceiv->otg->state) { 292 case OTG_STATE_A_HOST: 293 musb_root_disconnect(musb); 294 reg = musb_readb(musb->mregs, MUSB_DEVCTL); 295 reg &= ~MUSB_DEVCTL_SESSION; 296 musb_writeb(musb->mregs, MUSB_DEVCTL, reg); 297 break; 298 default: 299 break; 300 } 301 } else if (!strncmp(buf, "1", 1)) { 302 switch (musb->xceiv->otg->state) { 303 case OTG_STATE_A_WAIT_BCON: 304 /* 305 * musb_save_context() called in musb_runtime_suspend() 306 * might cache devctl with SESSION bit cleared during 307 * soft-disconnect, so specifically set SESSION bit 308 * here to preserve it for musb_runtime_resume(). 309 */ 310 musb->context.devctl |= MUSB_DEVCTL_SESSION; 311 reg = musb_readb(musb->mregs, MUSB_DEVCTL); 312 reg |= MUSB_DEVCTL_SESSION; 313 musb_writeb(musb->mregs, MUSB_DEVCTL, reg); 314 break; 315 default: 316 break; 317 } 318 } 319 320 pm_runtime_mark_last_busy(musb->controller); 321 pm_runtime_put_autosuspend(musb->controller); 322 return count; 323 } 324 325 /* 326 * In host mode, connect/disconnect the bus without physically 327 * remove the devices. 328 */ 329 static const struct file_operations musb_softconnect_fops = { 330 .open = musb_softconnect_open, 331 .write = musb_softconnect_write, 332 .read = seq_read, 333 .llseek = seq_lseek, 334 .release = single_release, 335 }; 336 337 int musb_init_debugfs(struct musb *musb) 338 { 339 struct dentry *root; 340 struct dentry *file; 341 int ret; 342 343 root = debugfs_create_dir(dev_name(musb->controller), NULL); 344 if (!root) { 345 ret = -ENOMEM; 346 goto err0; 347 } 348 349 file = debugfs_create_file("regdump", S_IRUGO, root, musb, 350 &musb_regdump_fops); 351 if (!file) { 352 ret = -ENOMEM; 353 goto err1; 354 } 355 356 file = debugfs_create_file("testmode", S_IRUGO | S_IWUSR, 357 root, musb, &musb_test_mode_fops); 358 if (!file) { 359 ret = -ENOMEM; 360 goto err1; 361 } 362 363 file = debugfs_create_file("softconnect", S_IRUGO | S_IWUSR, 364 root, musb, &musb_softconnect_fops); 365 if (!file) { 366 ret = -ENOMEM; 367 goto err1; 368 } 369 370 musb->debugfs_root = root; 371 372 return 0; 373 374 err1: 375 debugfs_remove_recursive(root); 376 377 err0: 378 return ret; 379 } 380 381 void /* __init_or_exit */ musb_exit_debugfs(struct musb *musb) 382 { 383 debugfs_remove_recursive(musb->debugfs_root); 384 } 385