162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * MUSB OTG driver debugfs support 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2010 Nokia Corporation 662306a36Sopenharmony_ci * Contact: Felipe Balbi <felipe.balbi@nokia.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/init.h> 1262306a36Sopenharmony_ci#include <linux/debugfs.h> 1362306a36Sopenharmony_ci#include <linux/seq_file.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <linux/uaccess.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include "musb_core.h" 1862306a36Sopenharmony_ci#include "musb_debug.h" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistruct musb_register_map { 2162306a36Sopenharmony_ci char *name; 2262306a36Sopenharmony_ci unsigned offset; 2362306a36Sopenharmony_ci unsigned size; 2462306a36Sopenharmony_ci}; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic const struct musb_register_map musb_regmap[] = { 2762306a36Sopenharmony_ci { "FAddr", MUSB_FADDR, 8 }, 2862306a36Sopenharmony_ci { "Power", MUSB_POWER, 8 }, 2962306a36Sopenharmony_ci { "Frame", MUSB_FRAME, 16 }, 3062306a36Sopenharmony_ci { "Index", MUSB_INDEX, 8 }, 3162306a36Sopenharmony_ci { "Testmode", MUSB_TESTMODE, 8 }, 3262306a36Sopenharmony_ci { "TxMaxPp", MUSB_TXMAXP, 16 }, 3362306a36Sopenharmony_ci { "TxCSRp", MUSB_TXCSR, 16 }, 3462306a36Sopenharmony_ci { "RxMaxPp", MUSB_RXMAXP, 16 }, 3562306a36Sopenharmony_ci { "RxCSR", MUSB_RXCSR, 16 }, 3662306a36Sopenharmony_ci { "RxCount", MUSB_RXCOUNT, 16 }, 3762306a36Sopenharmony_ci { "IntrRxE", MUSB_INTRRXE, 16 }, 3862306a36Sopenharmony_ci { "IntrTxE", MUSB_INTRTXE, 16 }, 3962306a36Sopenharmony_ci { "IntrUsbE", MUSB_INTRUSBE, 8 }, 4062306a36Sopenharmony_ci { "DevCtl", MUSB_DEVCTL, 8 }, 4162306a36Sopenharmony_ci { "VControl", 0x68, 32 }, 4262306a36Sopenharmony_ci { "HWVers", MUSB_HWVERS, 16 }, 4362306a36Sopenharmony_ci { "LinkInfo", MUSB_LINKINFO, 8 }, 4462306a36Sopenharmony_ci { "VPLen", MUSB_VPLEN, 8 }, 4562306a36Sopenharmony_ci { "HS_EOF1", MUSB_HS_EOF1, 8 }, 4662306a36Sopenharmony_ci { "FS_EOF1", MUSB_FS_EOF1, 8 }, 4762306a36Sopenharmony_ci { "LS_EOF1", MUSB_LS_EOF1, 8 }, 4862306a36Sopenharmony_ci { "SOFT_RST", 0x7F, 8 }, 4962306a36Sopenharmony_ci { "DMA_CNTLch0", 0x204, 16 }, 5062306a36Sopenharmony_ci { "DMA_ADDRch0", 0x208, 32 }, 5162306a36Sopenharmony_ci { "DMA_COUNTch0", 0x20C, 32 }, 5262306a36Sopenharmony_ci { "DMA_CNTLch1", 0x214, 16 }, 5362306a36Sopenharmony_ci { "DMA_ADDRch1", 0x218, 32 }, 5462306a36Sopenharmony_ci { "DMA_COUNTch1", 0x21C, 32 }, 5562306a36Sopenharmony_ci { "DMA_CNTLch2", 0x224, 16 }, 5662306a36Sopenharmony_ci { "DMA_ADDRch2", 0x228, 32 }, 5762306a36Sopenharmony_ci { "DMA_COUNTch2", 0x22C, 32 }, 5862306a36Sopenharmony_ci { "DMA_CNTLch3", 0x234, 16 }, 5962306a36Sopenharmony_ci { "DMA_ADDRch3", 0x238, 32 }, 6062306a36Sopenharmony_ci { "DMA_COUNTch3", 0x23C, 32 }, 6162306a36Sopenharmony_ci { "DMA_CNTLch4", 0x244, 16 }, 6262306a36Sopenharmony_ci { "DMA_ADDRch4", 0x248, 32 }, 6362306a36Sopenharmony_ci { "DMA_COUNTch4", 0x24C, 32 }, 6462306a36Sopenharmony_ci { "DMA_CNTLch5", 0x254, 16 }, 6562306a36Sopenharmony_ci { "DMA_ADDRch5", 0x258, 32 }, 6662306a36Sopenharmony_ci { "DMA_COUNTch5", 0x25C, 32 }, 6762306a36Sopenharmony_ci { "DMA_CNTLch6", 0x264, 16 }, 6862306a36Sopenharmony_ci { "DMA_ADDRch6", 0x268, 32 }, 6962306a36Sopenharmony_ci { "DMA_COUNTch6", 0x26C, 32 }, 7062306a36Sopenharmony_ci { "DMA_CNTLch7", 0x274, 16 }, 7162306a36Sopenharmony_ci { "DMA_ADDRch7", 0x278, 32 }, 7262306a36Sopenharmony_ci { "DMA_COUNTch7", 0x27C, 32 }, 7362306a36Sopenharmony_ci { "ConfigData", MUSB_CONFIGDATA,8 }, 7462306a36Sopenharmony_ci { "BabbleCtl", MUSB_BABBLE_CTL,8 }, 7562306a36Sopenharmony_ci { "TxFIFOsz", MUSB_TXFIFOSZ, 8 }, 7662306a36Sopenharmony_ci { "RxFIFOsz", MUSB_RXFIFOSZ, 8 }, 7762306a36Sopenharmony_ci { "TxFIFOadd", MUSB_TXFIFOADD, 16 }, 7862306a36Sopenharmony_ci { "RxFIFOadd", MUSB_RXFIFOADD, 16 }, 7962306a36Sopenharmony_ci { "EPInfo", MUSB_EPINFO, 8 }, 8062306a36Sopenharmony_ci { "RAMInfo", MUSB_RAMINFO, 8 }, 8162306a36Sopenharmony_ci { } /* Terminating Entry */ 8262306a36Sopenharmony_ci}; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic int musb_regdump_show(struct seq_file *s, void *unused) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci struct musb *musb = s->private; 8762306a36Sopenharmony_ci unsigned i; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci seq_printf(s, "MUSB (M)HDRC Register Dump\n"); 9062306a36Sopenharmony_ci pm_runtime_get_sync(musb->controller); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(musb_regmap); i++) { 9362306a36Sopenharmony_ci switch (musb_regmap[i].size) { 9462306a36Sopenharmony_ci case 8: 9562306a36Sopenharmony_ci seq_printf(s, "%-12s: %02x\n", musb_regmap[i].name, 9662306a36Sopenharmony_ci musb_readb(musb->mregs, musb_regmap[i].offset)); 9762306a36Sopenharmony_ci break; 9862306a36Sopenharmony_ci case 16: 9962306a36Sopenharmony_ci seq_printf(s, "%-12s: %04x\n", musb_regmap[i].name, 10062306a36Sopenharmony_ci musb_readw(musb->mregs, musb_regmap[i].offset)); 10162306a36Sopenharmony_ci break; 10262306a36Sopenharmony_ci case 32: 10362306a36Sopenharmony_ci seq_printf(s, "%-12s: %08x\n", musb_regmap[i].name, 10462306a36Sopenharmony_ci musb_readl(musb->mregs, musb_regmap[i].offset)); 10562306a36Sopenharmony_ci break; 10662306a36Sopenharmony_ci } 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci pm_runtime_mark_last_busy(musb->controller); 11062306a36Sopenharmony_ci pm_runtime_put_autosuspend(musb->controller); 11162306a36Sopenharmony_ci return 0; 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(musb_regdump); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic int musb_test_mode_show(struct seq_file *s, void *unused) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci struct musb *musb = s->private; 11862306a36Sopenharmony_ci unsigned test; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci pm_runtime_get_sync(musb->controller); 12162306a36Sopenharmony_ci test = musb_readb(musb->mregs, MUSB_TESTMODE); 12262306a36Sopenharmony_ci pm_runtime_mark_last_busy(musb->controller); 12362306a36Sopenharmony_ci pm_runtime_put_autosuspend(musb->controller); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci if (test == (MUSB_TEST_FORCE_HOST | MUSB_TEST_FORCE_FS)) 12662306a36Sopenharmony_ci seq_printf(s, "force host full-speed\n"); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci else if (test == (MUSB_TEST_FORCE_HOST | MUSB_TEST_FORCE_HS)) 12962306a36Sopenharmony_ci seq_printf(s, "force host high-speed\n"); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci else if (test == MUSB_TEST_FORCE_HOST) 13262306a36Sopenharmony_ci seq_printf(s, "force host\n"); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci else if (test == MUSB_TEST_FIFO_ACCESS) 13562306a36Sopenharmony_ci seq_printf(s, "fifo access\n"); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci else if (test == MUSB_TEST_FORCE_FS) 13862306a36Sopenharmony_ci seq_printf(s, "force full-speed\n"); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci else if (test == MUSB_TEST_FORCE_HS) 14162306a36Sopenharmony_ci seq_printf(s, "force high-speed\n"); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci else if (test == MUSB_TEST_PACKET) 14462306a36Sopenharmony_ci seq_printf(s, "test packet\n"); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci else if (test == MUSB_TEST_K) 14762306a36Sopenharmony_ci seq_printf(s, "test K\n"); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci else if (test == MUSB_TEST_J) 15062306a36Sopenharmony_ci seq_printf(s, "test J\n"); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci else if (test == MUSB_TEST_SE0_NAK) 15362306a36Sopenharmony_ci seq_printf(s, "test SE0 NAK\n"); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci return 0; 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cistatic int musb_test_mode_open(struct inode *inode, struct file *file) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci return single_open(file, musb_test_mode_show, inode->i_private); 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_cistatic ssize_t musb_test_mode_write(struct file *file, 16462306a36Sopenharmony_ci const char __user *ubuf, size_t count, loff_t *ppos) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci struct seq_file *s = file->private_data; 16762306a36Sopenharmony_ci struct musb *musb = s->private; 16862306a36Sopenharmony_ci u8 test; 16962306a36Sopenharmony_ci char buf[24]; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci memset(buf, 0x00, sizeof(buf)); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci if (copy_from_user(buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) 17462306a36Sopenharmony_ci return -EFAULT; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci pm_runtime_get_sync(musb->controller); 17762306a36Sopenharmony_ci test = musb_readb(musb->mregs, MUSB_TESTMODE); 17862306a36Sopenharmony_ci if (test) { 17962306a36Sopenharmony_ci dev_err(musb->controller, "Error: test mode is already set. " 18062306a36Sopenharmony_ci "Please do USB Bus Reset to start a new test.\n"); 18162306a36Sopenharmony_ci goto ret; 18262306a36Sopenharmony_ci } 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci if (strstarts(buf, "force host full-speed")) 18562306a36Sopenharmony_ci test = MUSB_TEST_FORCE_HOST | MUSB_TEST_FORCE_FS; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci else if (strstarts(buf, "force host high-speed")) 18862306a36Sopenharmony_ci test = MUSB_TEST_FORCE_HOST | MUSB_TEST_FORCE_HS; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci else if (strstarts(buf, "force host")) 19162306a36Sopenharmony_ci test = MUSB_TEST_FORCE_HOST; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci else if (strstarts(buf, "fifo access")) 19462306a36Sopenharmony_ci test = MUSB_TEST_FIFO_ACCESS; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci else if (strstarts(buf, "force full-speed")) 19762306a36Sopenharmony_ci test = MUSB_TEST_FORCE_FS; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci else if (strstarts(buf, "force high-speed")) 20062306a36Sopenharmony_ci test = MUSB_TEST_FORCE_HS; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci else if (strstarts(buf, "test packet")) { 20362306a36Sopenharmony_ci test = MUSB_TEST_PACKET; 20462306a36Sopenharmony_ci musb_load_testpacket(musb); 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci else if (strstarts(buf, "test K")) 20862306a36Sopenharmony_ci test = MUSB_TEST_K; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci else if (strstarts(buf, "test J")) 21162306a36Sopenharmony_ci test = MUSB_TEST_J; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci else if (strstarts(buf, "test SE0 NAK")) 21462306a36Sopenharmony_ci test = MUSB_TEST_SE0_NAK; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci musb_writeb(musb->mregs, MUSB_TESTMODE, test); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ciret: 21962306a36Sopenharmony_ci pm_runtime_mark_last_busy(musb->controller); 22062306a36Sopenharmony_ci pm_runtime_put_autosuspend(musb->controller); 22162306a36Sopenharmony_ci return count; 22262306a36Sopenharmony_ci} 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_cistatic const struct file_operations musb_test_mode_fops = { 22562306a36Sopenharmony_ci .open = musb_test_mode_open, 22662306a36Sopenharmony_ci .write = musb_test_mode_write, 22762306a36Sopenharmony_ci .read = seq_read, 22862306a36Sopenharmony_ci .llseek = seq_lseek, 22962306a36Sopenharmony_ci .release = single_release, 23062306a36Sopenharmony_ci}; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_cistatic int musb_softconnect_show(struct seq_file *s, void *unused) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci struct musb *musb = s->private; 23562306a36Sopenharmony_ci u8 reg; 23662306a36Sopenharmony_ci int connect; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci switch (musb_get_state(musb)) { 23962306a36Sopenharmony_ci case OTG_STATE_A_HOST: 24062306a36Sopenharmony_ci case OTG_STATE_A_WAIT_BCON: 24162306a36Sopenharmony_ci pm_runtime_get_sync(musb->controller); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci reg = musb_readb(musb->mregs, MUSB_DEVCTL); 24462306a36Sopenharmony_ci connect = reg & MUSB_DEVCTL_SESSION ? 1 : 0; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci pm_runtime_mark_last_busy(musb->controller); 24762306a36Sopenharmony_ci pm_runtime_put_autosuspend(musb->controller); 24862306a36Sopenharmony_ci break; 24962306a36Sopenharmony_ci default: 25062306a36Sopenharmony_ci connect = -1; 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci seq_printf(s, "%d\n", connect); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci return 0; 25662306a36Sopenharmony_ci} 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_cistatic int musb_softconnect_open(struct inode *inode, struct file *file) 25962306a36Sopenharmony_ci{ 26062306a36Sopenharmony_ci return single_open(file, musb_softconnect_show, inode->i_private); 26162306a36Sopenharmony_ci} 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_cistatic ssize_t musb_softconnect_write(struct file *file, 26462306a36Sopenharmony_ci const char __user *ubuf, size_t count, loff_t *ppos) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci struct seq_file *s = file->private_data; 26762306a36Sopenharmony_ci struct musb *musb = s->private; 26862306a36Sopenharmony_ci char buf[2]; 26962306a36Sopenharmony_ci u8 reg; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci memset(buf, 0x00, sizeof(buf)); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) 27462306a36Sopenharmony_ci return -EFAULT; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci pm_runtime_get_sync(musb->controller); 27762306a36Sopenharmony_ci if (!strncmp(buf, "0", 1)) { 27862306a36Sopenharmony_ci switch (musb_get_state(musb)) { 27962306a36Sopenharmony_ci case OTG_STATE_A_HOST: 28062306a36Sopenharmony_ci musb_root_disconnect(musb); 28162306a36Sopenharmony_ci reg = musb_readb(musb->mregs, MUSB_DEVCTL); 28262306a36Sopenharmony_ci reg &= ~MUSB_DEVCTL_SESSION; 28362306a36Sopenharmony_ci musb_writeb(musb->mregs, MUSB_DEVCTL, reg); 28462306a36Sopenharmony_ci break; 28562306a36Sopenharmony_ci default: 28662306a36Sopenharmony_ci break; 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci } else if (!strncmp(buf, "1", 1)) { 28962306a36Sopenharmony_ci switch (musb_get_state(musb)) { 29062306a36Sopenharmony_ci case OTG_STATE_A_WAIT_BCON: 29162306a36Sopenharmony_ci /* 29262306a36Sopenharmony_ci * musb_save_context() called in musb_runtime_suspend() 29362306a36Sopenharmony_ci * might cache devctl with SESSION bit cleared during 29462306a36Sopenharmony_ci * soft-disconnect, so specifically set SESSION bit 29562306a36Sopenharmony_ci * here to preserve it for musb_runtime_resume(). 29662306a36Sopenharmony_ci */ 29762306a36Sopenharmony_ci musb->context.devctl |= MUSB_DEVCTL_SESSION; 29862306a36Sopenharmony_ci reg = musb_readb(musb->mregs, MUSB_DEVCTL); 29962306a36Sopenharmony_ci reg |= MUSB_DEVCTL_SESSION; 30062306a36Sopenharmony_ci musb_writeb(musb->mregs, MUSB_DEVCTL, reg); 30162306a36Sopenharmony_ci break; 30262306a36Sopenharmony_ci default: 30362306a36Sopenharmony_ci break; 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci pm_runtime_mark_last_busy(musb->controller); 30862306a36Sopenharmony_ci pm_runtime_put_autosuspend(musb->controller); 30962306a36Sopenharmony_ci return count; 31062306a36Sopenharmony_ci} 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci/* 31362306a36Sopenharmony_ci * In host mode, connect/disconnect the bus without physically 31462306a36Sopenharmony_ci * remove the devices. 31562306a36Sopenharmony_ci */ 31662306a36Sopenharmony_cistatic const struct file_operations musb_softconnect_fops = { 31762306a36Sopenharmony_ci .open = musb_softconnect_open, 31862306a36Sopenharmony_ci .write = musb_softconnect_write, 31962306a36Sopenharmony_ci .read = seq_read, 32062306a36Sopenharmony_ci .llseek = seq_lseek, 32162306a36Sopenharmony_ci .release = single_release, 32262306a36Sopenharmony_ci}; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_civoid musb_init_debugfs(struct musb *musb) 32562306a36Sopenharmony_ci{ 32662306a36Sopenharmony_ci struct dentry *root; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci root = debugfs_create_dir(dev_name(musb->controller), usb_debug_root); 32962306a36Sopenharmony_ci musb->debugfs_root = root; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci debugfs_create_file("regdump", S_IRUGO, root, musb, &musb_regdump_fops); 33262306a36Sopenharmony_ci debugfs_create_file("testmode", S_IRUGO | S_IWUSR, root, musb, 33362306a36Sopenharmony_ci &musb_test_mode_fops); 33462306a36Sopenharmony_ci debugfs_create_file("softconnect", S_IRUGO | S_IWUSR, root, musb, 33562306a36Sopenharmony_ci &musb_softconnect_fops); 33662306a36Sopenharmony_ci} 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_civoid /* __init_or_exit */ musb_exit_debugfs(struct musb *musb) 33962306a36Sopenharmony_ci{ 34062306a36Sopenharmony_ci debugfs_remove_recursive(musb->debugfs_root); 34162306a36Sopenharmony_ci} 342