| // SPDX-License-Identifier: GPL-2.0 |
| /** |
| * debugfs.c - Designware USB2 DRD controller debugfs |
| * |
| * Copyright (C) 2015 Intel Corporation |
| * Mian Yousaf Kaukab <yousaf.kaukab@intel.com> |
| */ |
| |
| #include <linux/spinlock.h> |
| #include <linux/debugfs.h> |
| #include <linux/seq_file.h> |
| #include <linux/uaccess.h> |
| |
| #include "core.h" |
| #include "debug.h" |
| |
| #if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || \ |
| IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE) |
| /** |
| * testmode_write - debugfs: change usb test mode |
| * @seq: The seq file to write to. |
| * @v: Unused parameter. |
| * |
| * This debugfs entry modify the current usb test mode. |
| */ |
| static ssize_t testmode_write(struct file *file, const char __user *ubuf, size_t |
| count, loff_t *ppos) |
| { |
| struct seq_file *s = file->private_data; |
| struct dwc2_hsotg *hsotg = s->private; |
| unsigned long flags; |
| u32 testmode = 0; |
| char buf[32]; |
| |
| if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) |
| return -EFAULT; |
| |
| if (!strncmp(buf, "test_j", 6)) |
| testmode = TEST_J; |
| else if (!strncmp(buf, "test_k", 6)) |
| testmode = TEST_K; |
| else if (!strncmp(buf, "test_se0_nak", 12)) |
| testmode = TEST_SE0_NAK; |
| else if (!strncmp(buf, "test_packet", 11)) |
| testmode = TEST_PACKET; |
| else if (!strncmp(buf, "test_force_enable", 17)) |
| testmode = TEST_FORCE_EN; |
| else |
| testmode = 0; |
| |
| spin_lock_irqsave(&hsotg->lock, flags); |
| dwc2_hsotg_set_test_mode(hsotg, testmode); |
| spin_unlock_irqrestore(&hsotg->lock, flags); |
| return count; |
| } |
| |
| /** |
| * testmode_show - debugfs: show usb test mode state |
| * @seq: The seq file to write to. |
| * @v: Unused parameter. |
| * |
| * This debugfs entry shows which usb test mode is currently enabled. |
| */ |
| static int testmode_show(struct seq_file *s, void *unused) |
| { |
| struct dwc2_hsotg *hsotg = s->private; |
| unsigned long flags; |
| int dctl; |
| |
| spin_lock_irqsave(&hsotg->lock, flags); |
| dctl = dwc2_readl(hsotg->regs + DCTL); |
| dctl &= DCTL_TSTCTL_MASK; |
| dctl >>= DCTL_TSTCTL_SHIFT; |
| spin_unlock_irqrestore(&hsotg->lock, flags); |
| |
| switch (dctl) { |
| case 0: |
| seq_puts(s, "no test\n"); |
| break; |
| case TEST_J: |
| seq_puts(s, "test_j\n"); |
| break; |
| case TEST_K: |
| seq_puts(s, "test_k\n"); |
| break; |
| case TEST_SE0_NAK: |
| seq_puts(s, "test_se0_nak\n"); |
| break; |
| case TEST_PACKET: |
| seq_puts(s, "test_packet\n"); |
| break; |
| case TEST_FORCE_EN: |
| seq_puts(s, "test_force_enable\n"); |
| break; |
| default: |
| seq_printf(s, "UNKNOWN %d\n", dctl); |
| } |
| |
| return 0; |
| } |
| |
| static int testmode_open(struct inode *inode, struct file *file) |
| { |
| return single_open(file, testmode_show, inode->i_private); |
| } |
| |
| static const struct file_operations testmode_fops = { |
| .owner = THIS_MODULE, |
| .open = testmode_open, |
| .write = testmode_write, |
| .read = seq_read, |
| .llseek = seq_lseek, |
| .release = single_release, |
| }; |
| |
| /** |
| * state_show - debugfs: show overall driver and device state. |
| * @seq: The seq file to write to. |
| * @v: Unused parameter. |
| * |
| * This debugfs entry shows the overall state of the hardware and |
| * some general information about each of the endpoints available |
| * to the system. |
| */ |
| static int state_show(struct seq_file *seq, void *v) |
| { |
| struct dwc2_hsotg *hsotg = seq->private; |
| void __iomem *regs = hsotg->regs; |
| int idx; |
| |
| seq_printf(seq, "DCFG=0x%08x, DCTL=0x%08x, DSTS=0x%08x\n", |
| dwc2_readl(regs + DCFG), |
| dwc2_readl(regs + DCTL), |
| dwc2_readl(regs + DSTS)); |
| |
| seq_printf(seq, "DIEPMSK=0x%08x, DOEPMASK=0x%08x\n", |
| dwc2_readl(regs + DIEPMSK), dwc2_readl(regs + DOEPMSK)); |
| |
| seq_printf(seq, "GINTMSK=0x%08x, GINTSTS=0x%08x\n", |
| dwc2_readl(regs + GINTMSK), |
| dwc2_readl(regs + GINTSTS)); |
| |
| seq_printf(seq, "DAINTMSK=0x%08x, DAINT=0x%08x\n", |
| dwc2_readl(regs + DAINTMSK), |
| dwc2_readl(regs + DAINT)); |
| |
| seq_printf(seq, "GNPTXSTS=0x%08x, GRXSTSR=%08x\n", |
| dwc2_readl(regs + GNPTXSTS), |
| dwc2_readl(regs + GRXSTSR)); |
| |
| seq_puts(seq, "\nEndpoint status:\n"); |
| |
| for (idx = 0; idx < hsotg->num_of_eps; idx++) { |
| u32 in, out; |
| |
| in = dwc2_readl(regs + DIEPCTL(idx)); |
| out = dwc2_readl(regs + DOEPCTL(idx)); |
| |
| seq_printf(seq, "ep%d: DIEPCTL=0x%08x, DOEPCTL=0x%08x", |
| idx, in, out); |
| |
| in = dwc2_readl(regs + DIEPTSIZ(idx)); |
| out = dwc2_readl(regs + DOEPTSIZ(idx)); |
| |
| seq_printf(seq, ", DIEPTSIZ=0x%08x, DOEPTSIZ=0x%08x", |
| in, out); |
| |
| seq_puts(seq, "\n"); |
| } |
| |
| return 0; |
| } |
| DEFINE_SHOW_ATTRIBUTE(state); |
| |
| /** |
| * fifo_show - debugfs: show the fifo information |
| * @seq: The seq_file to write data to. |
| * @v: Unused parameter. |
| * |
| * Show the FIFO information for the overall fifo and all the |
| * periodic transmission FIFOs. |
| */ |
| static int fifo_show(struct seq_file *seq, void *v) |
| { |
| struct dwc2_hsotg *hsotg = seq->private; |
| void __iomem *regs = hsotg->regs; |
| u32 val; |
| int idx; |
| |
| seq_puts(seq, "Non-periodic FIFOs:\n"); |
| seq_printf(seq, "RXFIFO: Size %d\n", dwc2_readl(regs + GRXFSIZ)); |
| |
| val = dwc2_readl(regs + GNPTXFSIZ); |
| seq_printf(seq, "NPTXFIFO: Size %d, Start 0x%08x\n", |
| val >> FIFOSIZE_DEPTH_SHIFT, |
| val & FIFOSIZE_STARTADDR_MASK); |
| |
| seq_puts(seq, "\nPeriodic TXFIFOs:\n"); |
| |
| for (idx = 1; idx < hsotg->num_of_eps; idx++) { |
| val = dwc2_readl(regs + DPTXFSIZN(idx)); |
| |
| seq_printf(seq, "\tDPTXFIFO%2d: Size %d, Start 0x%08x\n", idx, |
| val >> FIFOSIZE_DEPTH_SHIFT, |
| val & FIFOSIZE_STARTADDR_MASK); |
| } |
| |
| return 0; |
| } |
| DEFINE_SHOW_ATTRIBUTE(fifo); |
| |
| static const char *decode_direction(int is_in) |
| { |
| return is_in ? "in" : "out"; |
| } |
| |
| /** |
| * ep_show - debugfs: show the state of an endpoint. |
| * @seq: The seq_file to write data to. |
| * @v: Unused parameter. |
| * |
| * This debugfs entry shows the state of the given endpoint (one is |
| * registered for each available). |
| */ |
| static int ep_show(struct seq_file *seq, void *v) |
| { |
| struct dwc2_hsotg_ep *ep = seq->private; |
| struct dwc2_hsotg *hsotg = ep->parent; |
| struct dwc2_hsotg_req *req; |
| void __iomem *regs = hsotg->regs; |
| int index = ep->index; |
| int show_limit = 15; |
| unsigned long flags; |
| |
| seq_printf(seq, "Endpoint index %d, named %s, dir %s:\n", |
| ep->index, ep->ep.name, decode_direction(ep->dir_in)); |
| |
| /* first show the register state */ |
| |
| seq_printf(seq, "\tDIEPCTL=0x%08x, DOEPCTL=0x%08x\n", |
| dwc2_readl(regs + DIEPCTL(index)), |
| dwc2_readl(regs + DOEPCTL(index))); |
| |
| seq_printf(seq, "\tDIEPDMA=0x%08x, DOEPDMA=0x%08x\n", |
| dwc2_readl(regs + DIEPDMA(index)), |
| dwc2_readl(regs + DOEPDMA(index))); |
| |
| seq_printf(seq, "\tDIEPINT=0x%08x, DOEPINT=0x%08x\n", |
| dwc2_readl(regs + DIEPINT(index)), |
| dwc2_readl(regs + DOEPINT(index))); |
| |
| seq_printf(seq, "\tDIEPTSIZ=0x%08x, DOEPTSIZ=0x%08x\n", |
| dwc2_readl(regs + DIEPTSIZ(index)), |
| dwc2_readl(regs + DOEPTSIZ(index))); |
| |
| seq_puts(seq, "\n"); |
| seq_printf(seq, "mps %d\n", ep->ep.maxpacket); |
| seq_printf(seq, "total_data=%ld\n", ep->total_data); |
| |
| seq_printf(seq, "request list (%p,%p):\n", |
| ep->queue.next, ep->queue.prev); |
| |
| spin_lock_irqsave(&hsotg->lock, flags); |
| |
| list_for_each_entry(req, &ep->queue, queue) { |
| if (--show_limit < 0) { |
| seq_puts(seq, "not showing more requests...\n"); |
| break; |
| } |
| |
| seq_printf(seq, "%c req %p: %d bytes @%p, ", |
| req == ep->req ? '*' : ' ', |
| req, req->req.length, req->req.buf); |
| seq_printf(seq, "%d done, res %d\n", |
| req->req.actual, req->req.status); |
| } |
| |
| spin_unlock_irqrestore(&hsotg->lock, flags); |
| |
| return 0; |
| } |
| DEFINE_SHOW_ATTRIBUTE(ep); |
| |
| /** |
| * dwc2_hsotg_create_debug - create debugfs directory and files |
| * @hsotg: The driver state |
| * |
| * Create the debugfs files to allow the user to get information |
| * about the state of the system. The directory name is created |
| * with the same name as the device itself, in case we end up |
| * with multiple blocks in future systems. |
| */ |
| static void dwc2_hsotg_create_debug(struct dwc2_hsotg *hsotg) |
| { |
| struct dentry *root; |
| struct dentry *file; |
| unsigned int epidx; |
| |
| root = hsotg->debug_root; |
| |
| /* create general state file */ |
| |
| file = debugfs_create_file("state", 0444, root, hsotg, &state_fops); |
| if (IS_ERR(file)) |
| dev_err(hsotg->dev, "%s: failed to create state\n", __func__); |
| |
| file = debugfs_create_file("testmode", 0644, root, hsotg, |
| &testmode_fops); |
| if (IS_ERR(file)) |
| dev_err(hsotg->dev, "%s: failed to create testmode\n", |
| __func__); |
| |
| file = debugfs_create_file("fifo", 0444, root, hsotg, &fifo_fops); |
| if (IS_ERR(file)) |
| dev_err(hsotg->dev, "%s: failed to create fifo\n", __func__); |
| |
| /* Create one file for each out endpoint */ |
| for (epidx = 0; epidx < hsotg->num_of_eps; epidx++) { |
| struct dwc2_hsotg_ep *ep; |
| |
| ep = hsotg->eps_out[epidx]; |
| if (ep) { |
| file = debugfs_create_file(ep->name, 0444, |
| root, ep, &ep_fops); |
| if (IS_ERR(file)) |
| dev_err(hsotg->dev, "failed to create %s debug file\n", |
| ep->name); |
| } |
| } |
| /* Create one file for each in endpoint. EP0 is handled with out eps */ |
| for (epidx = 1; epidx < hsotg->num_of_eps; epidx++) { |
| struct dwc2_hsotg_ep *ep; |
| |
| ep = hsotg->eps_in[epidx]; |
| if (ep) { |
| file = debugfs_create_file(ep->name, 0444, |
| root, ep, &ep_fops); |
| if (IS_ERR(file)) |
| dev_err(hsotg->dev, "failed to create %s debug file\n", |
| ep->name); |
| } |
| } |
| } |
| #else |
| static inline void dwc2_hsotg_create_debug(struct dwc2_hsotg *hsotg) {} |
| #endif |
| |
| /* dwc2_hsotg_delete_debug is removed as cleanup in done in dwc2_debugfs_exit */ |
| |
| #define dump_register(nm) \ |
| { \ |
| .name = #nm, \ |
| .offset = nm, \ |
| } |
| |
| static const struct debugfs_reg32 dwc2_regs[] = { |
| /* |
| * Accessing registers like this can trigger mode mismatch interrupt. |
| * However, according to dwc2 databook, the register access, in this |
| * case, is completed on the processor bus but is ignored by the core |
| * and does not affect its operation. |
| */ |
| dump_register(GOTGCTL), |
| dump_register(GOTGINT), |
| dump_register(GAHBCFG), |
| dump_register(GUSBCFG), |
| dump_register(GRSTCTL), |
| dump_register(GINTSTS), |
| dump_register(GINTMSK), |
| dump_register(GRXSTSR), |
| dump_register(GRXSTSP), |
| dump_register(GRXFSIZ), |
| dump_register(GNPTXFSIZ), |
| dump_register(GNPTXSTS), |
| dump_register(GI2CCTL), |
| dump_register(GPVNDCTL), |
| dump_register(GGPIO), |
| dump_register(GUID), |
| dump_register(GSNPSID), |
| dump_register(GHWCFG1), |
| dump_register(GHWCFG2), |
| dump_register(GHWCFG3), |
| dump_register(GHWCFG4), |
| dump_register(GLPMCFG), |
| dump_register(GPWRDN), |
| dump_register(GDFIFOCFG), |
| dump_register(ADPCTL), |
| dump_register(HPTXFSIZ), |
| dump_register(DPTXFSIZN(1)), |
| dump_register(DPTXFSIZN(2)), |
| dump_register(DPTXFSIZN(3)), |
| dump_register(DPTXFSIZN(4)), |
| dump_register(DPTXFSIZN(5)), |
| dump_register(DPTXFSIZN(6)), |
| dump_register(DPTXFSIZN(7)), |
| dump_register(DPTXFSIZN(8)), |
| dump_register(DPTXFSIZN(9)), |
| dump_register(DPTXFSIZN(10)), |
| dump_register(DPTXFSIZN(11)), |
| dump_register(DPTXFSIZN(12)), |
| dump_register(DPTXFSIZN(13)), |
| dump_register(DPTXFSIZN(14)), |
| dump_register(DPTXFSIZN(15)), |
| dump_register(DCFG), |
| dump_register(DCTL), |
| dump_register(DSTS), |
| dump_register(DIEPMSK), |
| dump_register(DOEPMSK), |
| dump_register(DAINT), |
| dump_register(DAINTMSK), |
| dump_register(DTKNQR1), |
| dump_register(DTKNQR2), |
| dump_register(DTKNQR3), |
| dump_register(DTKNQR4), |
| dump_register(DVBUSDIS), |
| dump_register(DVBUSPULSE), |
| dump_register(DIEPCTL(0)), |
| dump_register(DIEPCTL(1)), |
| dump_register(DIEPCTL(2)), |
| dump_register(DIEPCTL(3)), |
| dump_register(DIEPCTL(4)), |
| dump_register(DIEPCTL(5)), |
| dump_register(DIEPCTL(6)), |
| dump_register(DIEPCTL(7)), |
| dump_register(DIEPCTL(8)), |
| dump_register(DIEPCTL(9)), |
| dump_register(DIEPCTL(10)), |
| dump_register(DIEPCTL(11)), |
| dump_register(DIEPCTL(12)), |
| dump_register(DIEPCTL(13)), |
| dump_register(DIEPCTL(14)), |
| dump_register(DIEPCTL(15)), |
| dump_register(DOEPCTL(0)), |
| dump_register(DOEPCTL(1)), |
| dump_register(DOEPCTL(2)), |
| dump_register(DOEPCTL(3)), |
| dump_register(DOEPCTL(4)), |
| dump_register(DOEPCTL(5)), |
| dump_register(DOEPCTL(6)), |
| dump_register(DOEPCTL(7)), |
| dump_register(DOEPCTL(8)), |
| dump_register(DOEPCTL(9)), |
| dump_register(DOEPCTL(10)), |
| dump_register(DOEPCTL(11)), |
| dump_register(DOEPCTL(12)), |
| dump_register(DOEPCTL(13)), |
| dump_register(DOEPCTL(14)), |
| dump_register(DOEPCTL(15)), |
| dump_register(DIEPINT(0)), |
| dump_register(DIEPINT(1)), |
| dump_register(DIEPINT(2)), |
| dump_register(DIEPINT(3)), |
| dump_register(DIEPINT(4)), |
| dump_register(DIEPINT(5)), |
| dump_register(DIEPINT(6)), |
| dump_register(DIEPINT(7)), |
| dump_register(DIEPINT(8)), |
| dump_register(DIEPINT(9)), |
| dump_register(DIEPINT(10)), |
| dump_register(DIEPINT(11)), |
| dump_register(DIEPINT(12)), |
| dump_register(DIEPINT(13)), |
| dump_register(DIEPINT(14)), |
| dump_register(DIEPINT(15)), |
| dump_register(DOEPINT(0)), |
| dump_register(DOEPINT(1)), |
| dump_register(DOEPINT(2)), |
| dump_register(DOEPINT(3)), |
| dump_register(DOEPINT(4)), |
| dump_register(DOEPINT(5)), |
| dump_register(DOEPINT(6)), |
| dump_register(DOEPINT(7)), |
| dump_register(DOEPINT(8)), |
| dump_register(DOEPINT(9)), |
| dump_register(DOEPINT(10)), |
| dump_register(DOEPINT(11)), |
| dump_register(DOEPINT(12)), |
| dump_register(DOEPINT(13)), |
| dump_register(DOEPINT(14)), |
| dump_register(DOEPINT(15)), |
| dump_register(DIEPTSIZ(0)), |
| dump_register(DIEPTSIZ(1)), |
| dump_register(DIEPTSIZ(2)), |
| dump_register(DIEPTSIZ(3)), |
| dump_register(DIEPTSIZ(4)), |
| dump_register(DIEPTSIZ(5)), |
| dump_register(DIEPTSIZ(6)), |
| dump_register(DIEPTSIZ(7)), |
| dump_register(DIEPTSIZ(8)), |
| dump_register(DIEPTSIZ(9)), |
| dump_register(DIEPTSIZ(10)), |
| dump_register(DIEPTSIZ(11)), |
| dump_register(DIEPTSIZ(12)), |
| dump_register(DIEPTSIZ(13)), |
| dump_register(DIEPTSIZ(14)), |
| dump_register(DIEPTSIZ(15)), |
| dump_register(DOEPTSIZ(0)), |
| dump_register(DOEPTSIZ(1)), |
| dump_register(DOEPTSIZ(2)), |
| dump_register(DOEPTSIZ(3)), |
| dump_register(DOEPTSIZ(4)), |
| dump_register(DOEPTSIZ(5)), |
| dump_register(DOEPTSIZ(6)), |
| dump_register(DOEPTSIZ(7)), |
| dump_register(DOEPTSIZ(8)), |
| dump_register(DOEPTSIZ(9)), |
| dump_register(DOEPTSIZ(10)), |
| dump_register(DOEPTSIZ(11)), |
| dump_register(DOEPTSIZ(12)), |
| dump_register(DOEPTSIZ(13)), |
| dump_register(DOEPTSIZ(14)), |
| dump_register(DOEPTSIZ(15)), |
| dump_register(DIEPDMA(0)), |
| dump_register(DIEPDMA(1)), |
| dump_register(DIEPDMA(2)), |
| dump_register(DIEPDMA(3)), |
| dump_register(DIEPDMA(4)), |
| dump_register(DIEPDMA(5)), |
| dump_register(DIEPDMA(6)), |
| dump_register(DIEPDMA(7)), |
| dump_register(DIEPDMA(8)), |
| dump_register(DIEPDMA(9)), |
| dump_register(DIEPDMA(10)), |
| dump_register(DIEPDMA(11)), |
| dump_register(DIEPDMA(12)), |
| dump_register(DIEPDMA(13)), |
| dump_register(DIEPDMA(14)), |
| dump_register(DIEPDMA(15)), |
| dump_register(DOEPDMA(0)), |
| dump_register(DOEPDMA(1)), |
| dump_register(DOEPDMA(2)), |
| dump_register(DOEPDMA(3)), |
| dump_register(DOEPDMA(4)), |
| dump_register(DOEPDMA(5)), |
| dump_register(DOEPDMA(6)), |
| dump_register(DOEPDMA(7)), |
| dump_register(DOEPDMA(8)), |
| dump_register(DOEPDMA(9)), |
| dump_register(DOEPDMA(10)), |
| dump_register(DOEPDMA(11)), |
| dump_register(DOEPDMA(12)), |
| dump_register(DOEPDMA(13)), |
| dump_register(DOEPDMA(14)), |
| dump_register(DOEPDMA(15)), |
| dump_register(DTXFSTS(0)), |
| dump_register(DTXFSTS(1)), |
| dump_register(DTXFSTS(2)), |
| dump_register(DTXFSTS(3)), |
| dump_register(DTXFSTS(4)), |
| dump_register(DTXFSTS(5)), |
| dump_register(DTXFSTS(6)), |
| dump_register(DTXFSTS(7)), |
| dump_register(DTXFSTS(8)), |
| dump_register(DTXFSTS(9)), |
| dump_register(DTXFSTS(10)), |
| dump_register(DTXFSTS(11)), |
| dump_register(DTXFSTS(12)), |
| dump_register(DTXFSTS(13)), |
| dump_register(DTXFSTS(14)), |
| dump_register(DTXFSTS(15)), |
| dump_register(PCGCTL), |
| dump_register(HCFG), |
| dump_register(HFIR), |
| dump_register(HFNUM), |
| dump_register(HPTXSTS), |
| dump_register(HAINT), |
| dump_register(HAINTMSK), |
| dump_register(HFLBADDR), |
| dump_register(HPRT0), |
| dump_register(HCCHAR(0)), |
| dump_register(HCCHAR(1)), |
| dump_register(HCCHAR(2)), |
| dump_register(HCCHAR(3)), |
| dump_register(HCCHAR(4)), |
| dump_register(HCCHAR(5)), |
| dump_register(HCCHAR(6)), |
| dump_register(HCCHAR(7)), |
| dump_register(HCCHAR(8)), |
| dump_register(HCCHAR(9)), |
| dump_register(HCCHAR(10)), |
| dump_register(HCCHAR(11)), |
| dump_register(HCCHAR(12)), |
| dump_register(HCCHAR(13)), |
| dump_register(HCCHAR(14)), |
| dump_register(HCCHAR(15)), |
| dump_register(HCSPLT(0)), |
| dump_register(HCSPLT(1)), |
| dump_register(HCSPLT(2)), |
| dump_register(HCSPLT(3)), |
| dump_register(HCSPLT(4)), |
| dump_register(HCSPLT(5)), |
| dump_register(HCSPLT(6)), |
| dump_register(HCSPLT(7)), |
| dump_register(HCSPLT(8)), |
| dump_register(HCSPLT(9)), |
| dump_register(HCSPLT(10)), |
| dump_register(HCSPLT(11)), |
| dump_register(HCSPLT(12)), |
| dump_register(HCSPLT(13)), |
| dump_register(HCSPLT(14)), |
| dump_register(HCSPLT(15)), |
| dump_register(HCINT(0)), |
| dump_register(HCINT(1)), |
| dump_register(HCINT(2)), |
| dump_register(HCINT(3)), |
| dump_register(HCINT(4)), |
| dump_register(HCINT(5)), |
| dump_register(HCINT(6)), |
| dump_register(HCINT(7)), |
| dump_register(HCINT(8)), |
| dump_register(HCINT(9)), |
| dump_register(HCINT(10)), |
| dump_register(HCINT(11)), |
| dump_register(HCINT(12)), |
| dump_register(HCINT(13)), |
| dump_register(HCINT(14)), |
| dump_register(HCINT(15)), |
| dump_register(HCINTMSK(0)), |
| dump_register(HCINTMSK(1)), |
| dump_register(HCINTMSK(2)), |
| dump_register(HCINTMSK(3)), |
| dump_register(HCINTMSK(4)), |
| dump_register(HCINTMSK(5)), |
| dump_register(HCINTMSK(6)), |
| dump_register(HCINTMSK(7)), |
| dump_register(HCINTMSK(8)), |
| dump_register(HCINTMSK(9)), |
| dump_register(HCINTMSK(10)), |
| dump_register(HCINTMSK(11)), |
| dump_register(HCINTMSK(12)), |
| dump_register(HCINTMSK(13)), |
| dump_register(HCINTMSK(14)), |
| dump_register(HCINTMSK(15)), |
| dump_register(HCTSIZ(0)), |
| dump_register(HCTSIZ(1)), |
| dump_register(HCTSIZ(2)), |
| dump_register(HCTSIZ(3)), |
| dump_register(HCTSIZ(4)), |
| dump_register(HCTSIZ(5)), |
| dump_register(HCTSIZ(6)), |
| dump_register(HCTSIZ(7)), |
| dump_register(HCTSIZ(8)), |
| dump_register(HCTSIZ(9)), |
| dump_register(HCTSIZ(10)), |
| dump_register(HCTSIZ(11)), |
| dump_register(HCTSIZ(12)), |
| dump_register(HCTSIZ(13)), |
| dump_register(HCTSIZ(14)), |
| dump_register(HCTSIZ(15)), |
| dump_register(HCDMA(0)), |
| dump_register(HCDMA(1)), |
| dump_register(HCDMA(2)), |
| dump_register(HCDMA(3)), |
| dump_register(HCDMA(4)), |
| dump_register(HCDMA(5)), |
| dump_register(HCDMA(6)), |
| dump_register(HCDMA(7)), |
| dump_register(HCDMA(8)), |
| dump_register(HCDMA(9)), |
| dump_register(HCDMA(10)), |
| dump_register(HCDMA(11)), |
| dump_register(HCDMA(12)), |
| dump_register(HCDMA(13)), |
| dump_register(HCDMA(14)), |
| dump_register(HCDMA(15)), |
| dump_register(HCDMAB(0)), |
| dump_register(HCDMAB(1)), |
| dump_register(HCDMAB(2)), |
| dump_register(HCDMAB(3)), |
| dump_register(HCDMAB(4)), |
| dump_register(HCDMAB(5)), |
| dump_register(HCDMAB(6)), |
| dump_register(HCDMAB(7)), |
| dump_register(HCDMAB(8)), |
| dump_register(HCDMAB(9)), |
| dump_register(HCDMAB(10)), |
| dump_register(HCDMAB(11)), |
| dump_register(HCDMAB(12)), |
| dump_register(HCDMAB(13)), |
| dump_register(HCDMAB(14)), |
| dump_register(HCDMAB(15)), |
| }; |
| |
| #define print_param(_seq, _ptr, _param) \ |
| seq_printf((_seq), "%-30s: %d\n", #_param, (_ptr)->_param) |
| |
| #define print_param_hex(_seq, _ptr, _param) \ |
| seq_printf((_seq), "%-30s: 0x%x\n", #_param, (_ptr)->_param) |
| |
| static int params_show(struct seq_file *seq, void *v) |
| { |
| struct dwc2_hsotg *hsotg = seq->private; |
| struct dwc2_core_params *p = &hsotg->params; |
| int i; |
| |
| print_param(seq, p, otg_cap); |
| print_param(seq, p, dma_desc_enable); |
| print_param(seq, p, dma_desc_fs_enable); |
| print_param(seq, p, speed); |
| print_param(seq, p, enable_dynamic_fifo); |
| print_param(seq, p, en_multiple_tx_fifo); |
| print_param(seq, p, host_rx_fifo_size); |
| print_param(seq, p, host_nperio_tx_fifo_size); |
| print_param(seq, p, host_perio_tx_fifo_size); |
| print_param(seq, p, max_transfer_size); |
| print_param(seq, p, max_packet_count); |
| print_param(seq, p, host_channels); |
| print_param(seq, p, phy_type); |
| print_param(seq, p, phy_utmi_width); |
| print_param(seq, p, phy_ulpi_ddr); |
| print_param(seq, p, phy_ulpi_ext_vbus); |
| print_param(seq, p, i2c_enable); |
| print_param(seq, p, ulpi_fs_ls); |
| print_param(seq, p, host_support_fs_ls_low_power); |
| print_param(seq, p, host_ls_low_power_phy_clk); |
| print_param(seq, p, ts_dline); |
| print_param(seq, p, reload_ctl); |
| print_param_hex(seq, p, ahbcfg); |
| print_param(seq, p, uframe_sched); |
| print_param(seq, p, external_id_pin_ctl); |
| print_param(seq, p, power_down); |
| print_param(seq, p, lpm); |
| print_param(seq, p, lpm_clock_gating); |
| print_param(seq, p, besl); |
| print_param(seq, p, hird_threshold_en); |
| print_param(seq, p, hird_threshold); |
| print_param(seq, p, host_dma); |
| print_param(seq, p, g_dma); |
| print_param(seq, p, g_dma_desc); |
| print_param(seq, p, g_rx_fifo_size); |
| print_param(seq, p, g_np_tx_fifo_size); |
| |
| for (i = 0; i < MAX_EPS_CHANNELS; i++) { |
| char str[32]; |
| |
| snprintf(str, 32, "g_tx_fifo_size[%d]", i); |
| seq_printf(seq, "%-30s: %d\n", str, p->g_tx_fifo_size[i]); |
| } |
| |
| return 0; |
| } |
| DEFINE_SHOW_ATTRIBUTE(params); |
| |
| static int hw_params_show(struct seq_file *seq, void *v) |
| { |
| struct dwc2_hsotg *hsotg = seq->private; |
| struct dwc2_hw_params *hw = &hsotg->hw_params; |
| |
| print_param(seq, hw, op_mode); |
| print_param(seq, hw, arch); |
| print_param(seq, hw, dma_desc_enable); |
| print_param(seq, hw, enable_dynamic_fifo); |
| print_param(seq, hw, en_multiple_tx_fifo); |
| print_param(seq, hw, rx_fifo_size); |
| print_param(seq, hw, host_nperio_tx_fifo_size); |
| print_param(seq, hw, dev_nperio_tx_fifo_size); |
| print_param(seq, hw, host_perio_tx_fifo_size); |
| print_param(seq, hw, nperio_tx_q_depth); |
| print_param(seq, hw, host_perio_tx_q_depth); |
| print_param(seq, hw, dev_token_q_depth); |
| print_param(seq, hw, max_transfer_size); |
| print_param(seq, hw, max_packet_count); |
| print_param(seq, hw, host_channels); |
| print_param(seq, hw, hs_phy_type); |
| print_param(seq, hw, fs_phy_type); |
| print_param(seq, hw, i2c_enable); |
| print_param(seq, hw, num_dev_ep); |
| print_param(seq, hw, num_dev_perio_in_ep); |
| print_param(seq, hw, total_fifo_size); |
| print_param(seq, hw, power_optimized); |
| print_param(seq, hw, utmi_phy_data_width); |
| print_param_hex(seq, hw, snpsid); |
| print_param_hex(seq, hw, dev_ep_dirs); |
| |
| return 0; |
| } |
| DEFINE_SHOW_ATTRIBUTE(hw_params); |
| |
| static int dr_mode_show(struct seq_file *seq, void *v) |
| { |
| struct dwc2_hsotg *hsotg = seq->private; |
| const char *dr_mode = ""; |
| |
| device_property_read_string(hsotg->dev, "dr_mode", &dr_mode); |
| seq_printf(seq, "%s\n", dr_mode); |
| return 0; |
| } |
| DEFINE_SHOW_ATTRIBUTE(dr_mode); |
| |
| int dwc2_debugfs_init(struct dwc2_hsotg *hsotg) |
| { |
| int ret; |
| struct dentry *file; |
| |
| hsotg->debug_root = debugfs_create_dir(dev_name(hsotg->dev), NULL); |
| if (!hsotg->debug_root) { |
| ret = -ENOMEM; |
| goto err0; |
| } |
| |
| file = debugfs_create_file("params", 0444, |
| hsotg->debug_root, |
| hsotg, ¶ms_fops); |
| if (IS_ERR(file)) |
| dev_err(hsotg->dev, "%s: failed to create params\n", __func__); |
| |
| file = debugfs_create_file("hw_params", 0444, |
| hsotg->debug_root, |
| hsotg, &hw_params_fops); |
| if (IS_ERR(file)) |
| dev_err(hsotg->dev, "%s: failed to create hw_params\n", |
| __func__); |
| |
| file = debugfs_create_file("dr_mode", 0444, |
| hsotg->debug_root, |
| hsotg, &dr_mode_fops); |
| if (IS_ERR(file)) |
| dev_err(hsotg->dev, "%s: failed to create dr_mode\n", __func__); |
| |
| /* Add gadget debugfs nodes */ |
| dwc2_hsotg_create_debug(hsotg); |
| |
| hsotg->regset = devm_kzalloc(hsotg->dev, sizeof(*hsotg->regset), |
| GFP_KERNEL); |
| if (!hsotg->regset) { |
| ret = -ENOMEM; |
| goto err1; |
| } |
| |
| hsotg->regset->regs = dwc2_regs; |
| hsotg->regset->nregs = ARRAY_SIZE(dwc2_regs); |
| hsotg->regset->base = hsotg->regs; |
| |
| file = debugfs_create_regset32("regdump", 0444, hsotg->debug_root, |
| hsotg->regset); |
| if (!file) { |
| ret = -ENOMEM; |
| goto err1; |
| } |
| |
| return 0; |
| err1: |
| debugfs_remove_recursive(hsotg->debug_root); |
| err0: |
| return ret; |
| } |
| |
| void dwc2_debugfs_exit(struct dwc2_hsotg *hsotg) |
| { |
| debugfs_remove_recursive(hsotg->debug_root); |
| hsotg->debug_root = NULL; |
| } |