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