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