/* * MUSB OTG driver debug support * * Copyright 2005 Mentor Graphics Corporation * Copyright (C) 2005-2006 by Texas Instruments * Copyright (C) 2006-2007 Nokia Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include #include #include #include /* FIXME remove procfs writes */ #include #include "musb_core.h" #include "davinci.h" #ifdef CONFIG_USB_MUSB_HDRC_HCD static int dump_qh(struct musb_qh *qh, char *buf, unsigned max) { int count; int tmp; struct usb_host_endpoint *hep; struct urb *urb; if (!qh) return 0; hep = qh->hep; count = snprintf(buf, max, " qh %p dev%d ep%d%s max%d\n", qh, qh->dev->devnum, qh->epnum, ({ char *s; switch (qh->type) { case USB_ENDPOINT_XFER_BULK: s = "-bulk"; break; case USB_ENDPOINT_XFER_INT: s = "-int"; break; case USB_ENDPOINT_XFER_CONTROL: s = ""; break; default: s = "iso"; break; }; s; }), qh->maxpacket); if (count <= 0) return 0; buf += count; max -= count; list_for_each_entry(urb, &hep->urb_list, urb_list) { tmp = snprintf(buf, max, "\t%s urb %p %d/%d\n", usb_pipein(urb->pipe) ? "in" : "out", urb, urb->actual_length, urb->transfer_buffer_length); if (tmp <= 0) break; tmp = min(tmp, (int)max); count += tmp; buf += tmp; max -= tmp; } return count; } static int dump_queue(struct list_head *q, char *buf, unsigned max) { int count = 0; struct musb_qh *qh; list_for_each_entry(qh, q, ring) { int tmp; tmp = dump_qh(qh, buf, max); if (tmp <= 0) break; tmp = min(tmp, (int)max); count += tmp; buf += tmp; max -= tmp; } return count; } #endif /* HCD */ #ifdef CONFIG_USB_GADGET_MUSB_HDRC static int dump_ep(struct musb_ep *ep, char *buffer, unsigned max) { char *buf = buffer; int code = 0; void __iomem *regs = ep->hw_ep->regs; char *mode = "1buf"; if (ep->is_in) { if (ep->hw_ep->tx_double_buffered) mode = "2buf"; } else { if (ep->hw_ep->rx_double_buffered) mode = "2buf"; } do { struct usb_request *req; code = snprintf(buf, max, "\n%s (hw%d): %s%s, csr %04x maxp %04x\n", ep->name, ep->current_epnum, mode, ep->dma ? " dma" : "", musb_readw(regs, (ep->is_in || !ep->current_epnum) ? MUSB_TXCSR : MUSB_RXCSR), musb_readw(regs, ep->is_in ? MUSB_TXMAXP : MUSB_RXMAXP) ); if (code <= 0) break; code = min(code, (int) max); buf += code; max -= code; if (is_cppi_enabled() && ep->current_epnum) { unsigned cppi = ep->current_epnum - 1; void __iomem *base = ep->musb->ctrl_base; unsigned off1 = cppi << 2; void __iomem *ram = base; char tmp[16]; if (ep->is_in) { ram += DAVINCI_TXCPPI_STATERAM_OFFSET(cppi); tmp[0] = 0; } else { ram += DAVINCI_RXCPPI_STATERAM_OFFSET(cppi); snprintf(tmp, sizeof tmp, "%d left, ", musb_readl(base, DAVINCI_RXCPPI_BUFCNT0_REG + off1)); } code = snprintf(buf, max, "%cX DMA%d: %s" "%08x %08x, %08x %08x; " "%08x %08x %08x .. %08x\n", ep->is_in ? 'T' : 'R', ep->current_epnum - 1, tmp, musb_readl(ram, 0 * 4), musb_readl(ram, 1 * 4), musb_readl(ram, 2 * 4), musb_readl(ram, 3 * 4), musb_readl(ram, 4 * 4), musb_readl(ram, 5 * 4), musb_readl(ram, 6 * 4), musb_readl(ram, 7 * 4)); if (code <= 0) break; code = min(code, (int) max); buf += code; max -= code; } if (list_empty(&ep->req_list)) { code = snprintf(buf, max, "\t(queue empty)\n"); if (code <= 0) break; code = min(code, (int) max); buf += code; max -= code; break; } list_for_each_entry(req, &ep->req_list, list) { code = snprintf(buf, max, "\treq %p, %s%s%d/%d\n", req, req->zero ? "zero, " : "", req->short_not_ok ? "!short, " : "", req->actual, req->length); if (code <= 0) break; code = min(code, (int) max); buf += code; max -= code; } } while (0); return buf - buffer; } #endif static int dump_end_info(struct musb *musb, u8 epnum, char *aBuffer, unsigned max) { int code = 0; char *buf = aBuffer; struct musb_hw_ep *hw_ep = &musb->endpoints[epnum]; do { musb_ep_select(musb->mregs, epnum); #ifdef CONFIG_USB_MUSB_HDRC_HCD if (is_host_active(musb)) { int dump_rx, dump_tx; void __iomem *regs = hw_ep->regs; /* TEMPORARY (!) until we have a real periodic * schedule tree ... */ if (!epnum) { /* control is shared, uses RX queue * but (mostly) shadowed tx registers */ dump_tx = !list_empty( &musb->endpoints[0].in_list); dump_rx = 0; } else if (hw_ep == &musb->endpoints[1]) { dump_tx = !list_empty(&hw_ep->out_list); dump_rx = !list_empty(&hw_ep->in_list); } else if (musb->in[epnum] || musb->out[epnum]) { if (musb->in[epnum]) dump_rx = 1; else dump_rx = 0; dump_tx = !dump_rx; } else break; /* END TEMPORARY */ if (dump_rx) { code = snprintf(buf, max, "\nRX%d: %s rxcsr %04x interval %02x " "max %04x type %02x; " "dev %d hub %d port %d" "\n", epnum, hw_ep->rx_double_buffered ? "2buf" : "1buf", musb_readw(regs, MUSB_RXCSR), musb_readb(regs, MUSB_RXINTERVAL), musb_readw(regs, MUSB_RXMAXP), musb_readb(regs, MUSB_RXTYPE), /* FIXME: assumes multipoint */ musb_readb(musb->mregs, MUSB_BUSCTL_OFFSET(epnum, MUSB_RXFUNCADDR)), musb_readb(musb->mregs, MUSB_BUSCTL_OFFSET(epnum, MUSB_RXHUBADDR)), musb_readb(musb->mregs, MUSB_BUSCTL_OFFSET(epnum, MUSB_RXHUBPORT)) ); if (code <= 0) break; code = min(code, (int) max); buf += code; max -= code; if (is_cppi_enabled() && epnum && hw_ep->rx_channel) { unsigned cppi = epnum - 1; unsigned off1 = cppi << 2; void __iomem *base; void __iomem *ram; char tmp[16]; base = musb->ctrl_base; ram = DAVINCI_RXCPPI_STATERAM_OFFSET( cppi) + base; snprintf(tmp, sizeof tmp, "%d left, ", musb_readl(base, DAVINCI_RXCPPI_BUFCNT0_REG + off1)); code = snprintf(buf, max, " rx dma%d: %s" "%08x %08x, %08x %08x; " "%08x %08x %08x .. %08x\n", cppi, tmp, musb_readl(ram, 0 * 4), musb_readl(ram, 1 * 4), musb_readl(ram, 2 * 4), musb_readl(ram, 3 * 4), musb_readl(ram, 4 * 4), musb_readl(ram, 5 * 4), musb_readl(ram, 6 * 4), musb_readl(ram, 7 * 4)); if (code <= 0) break; code = min(code, (int) max); buf += code; max -= code; } if ((hw_ep == &musb->endpoints[1] || hw_ep == musb->intr_ep) && !list_empty( &hw_ep->in_list)) { code = dump_queue(&hw_ep->in_list, buf, max); if (code <= 0) break; code = min(code, (int) max); buf += code; max -= code; } else if (musb->in[epnum]) { code = dump_qh(musb->in[epnum], buf, max); if (code <= 0) break; code = min(code, (int) max); buf += code; max -= code; } } if (dump_tx) { code = snprintf(buf, max, "\nTX%d: %s txcsr %04x interval %02x " "max %04x type %02x; " "dev %d hub %d port %d" "\n", epnum, hw_ep->tx_double_buffered ? "2buf" : "1buf", musb_readw(regs, MUSB_TXCSR), musb_readb(regs, MUSB_TXINTERVAL), musb_readw(regs, MUSB_TXMAXP), musb_readb(regs, MUSB_TXTYPE), /* FIXME: assumes multipoint */ musb_readb(musb->mregs, MUSB_BUSCTL_OFFSET(epnum, MUSB_TXFUNCADDR)), musb_readb(musb->mregs, MUSB_BUSCTL_OFFSET(epnum, MUSB_TXHUBADDR)), musb_readb(musb->mregs, MUSB_BUSCTL_OFFSET(epnum, MUSB_TXHUBPORT)) ); if (code <= 0) break; code = min(code, (int) max); buf += code; max -= code; if (is_cppi_enabled() && epnum && hw_ep->tx_channel) { unsigned cppi = epnum - 1; void __iomem *base; void __iomem *ram; base = musb->ctrl_base; ram = DAVINCI_RXCPPI_STATERAM_OFFSET( cppi) + base; code = snprintf(buf, max, " tx dma%d: " "%08x %08x, %08x %08x; " "%08x %08x %08x .. %08x\n", cppi, musb_readl(ram, 0 * 4), musb_readl(ram, 1 * 4), musb_readl(ram, 2 * 4), musb_readl(ram, 3 * 4), musb_readl(ram, 4 * 4), musb_readl(ram, 5 * 4), musb_readl(ram, 6 * 4), musb_readl(ram, 7 * 4)); if (code <= 0) break; code = min(code, (int) max); buf += code; max -= code; } if (hw_ep == musb->endpoints && !list_empty( &musb->endpoints[0].in_list)) { code = dump_queue( &musb->endpoints[0].in_list, buf, max); if (code <= 0) break; code = min(code, (int) max); buf += code; max -= code; } else if (hw_ep == &musb->endpoints[1] && !list_empty( &hw_ep->out_list)) { code = dump_queue(&hw_ep->out_list, buf, max); if (code <= 0) break; code = min(code, (int) max); buf += code; max -= code; } else if (musb->out[epnum]) { code = dump_qh(musb->out[epnum], buf, max); if (code <= 0) break; code = min(code, (int) max); buf += code; max -= code; } } } #endif #ifdef CONFIG_USB_GADGET_MUSB_HDRC if (is_peripheral_active(musb)) { code = 0; if (hw_ep->ep_in.desc || !epnum) { code = dump_ep(&hw_ep->ep_in, buf, max); if (code <= 0) break; code = min(code, (int) max); buf += code; max -= code; } if (hw_ep->ep_out.desc) { code = dump_ep(&hw_ep->ep_out, buf, max); if (code <= 0) break; code = min(code, (int) max); buf += code; max -= code; } } #endif } while (0); return buf - aBuffer; } /* Dump the current status and compile options. * @param musb the device driver instance * @param buffer where to dump the status; it must be big enough hold the * result otherwise "BAD THINGS HAPPENS(TM)". */ static int dump_header_stats(struct musb *musb, char *buffer) { int code, count = 0; const void __iomem *mbase = musb->mregs; *buffer = 0; count = sprintf(buffer, "Status: %sHDRC, Mode=%s " "(Power=%02x, DevCtl=%02x)\n", (musb->is_multipoint ? "M" : ""), MUSB_MODE(musb), musb_readb(mbase, MUSB_POWER), musb_readb(mbase, MUSB_DEVCTL)); if (count <= 0) return 0; buffer += count; code = sprintf(buffer, "OTG state: %s; %sactive\n", otg_state_string(musb), musb->is_active ? "" : "in"); if (code <= 0) goto done; buffer += code; count += code; code = sprintf(buffer, "Options: " #ifdef CONFIG_MUSB_PIO_ONLY "pio" #elif defined(CONFIG_USB_TI_CPPI_DMA) "cppi-dma" #elif defined(CONFIG_USB_INVENTRA_DMA) "musb-dma" #elif defined(CONFIG_USB_TUSB_OMAP_DMA) "tusb-omap-dma" #else "?dma?" #endif ", " #ifdef CONFIG_USB_MUSB_OTG "otg (peripheral+host)" #elif defined(CONFIG_USB_GADGET_MUSB_HDRC) "peripheral" #elif defined(CONFIG_USB_MUSB_HDRC_HCD) "host" #endif ", debug=%d [eps=%d]\n", debug, musb->nr_endpoints); if (code <= 0) goto done; count += code; buffer += code; #ifdef CONFIG_USB_GADGET_MUSB_HDRC code = sprintf(buffer, "Peripheral address: %02x\n", musb_readb(musb->ctrl_base, MUSB_FADDR)); if (code <= 0) goto done; buffer += code; count += code; #endif #ifdef CONFIG_USB_MUSB_HDRC_HCD code = sprintf(buffer, "Root port status: %08x\n", musb->port1_status); if (code <= 0) goto done; buffer += code; count += code; #endif #ifdef CONFIG_ARCH_DAVINCI code = sprintf(buffer, "DaVinci: ctrl=%02x stat=%1x phy=%03x\n" "\trndis=%05x auto=%04x intsrc=%08x intmsk=%08x" "\n", musb_readl(musb->ctrl_base, DAVINCI_USB_CTRL_REG), musb_readl(musb->ctrl_base, DAVINCI_USB_STAT_REG), __raw_readl((void __force __iomem *) IO_ADDRESS(USBPHY_CTL_PADDR)), musb_readl(musb->ctrl_base, DAVINCI_RNDIS_REG), musb_readl(musb->ctrl_base, DAVINCI_AUTOREQ_REG), musb_readl(musb->ctrl_base, DAVINCI_USB_INT_SOURCE_REG), musb_readl(musb->ctrl_base, DAVINCI_USB_INT_MASK_REG)); if (code <= 0) goto done; count += code; buffer += code; #endif /* DAVINCI */ #ifdef CONFIG_USB_TUSB6010 code = sprintf(buffer, "TUSB6010: devconf %08x, phy enable %08x drive %08x" "\n\totg %03x timer %08x" "\n\tprcm conf %08x mgmt %08x; int src %08x mask %08x" "\n", musb_readl(musb->ctrl_base, TUSB_DEV_CONF), musb_readl(musb->ctrl_base, TUSB_PHY_OTG_CTRL_ENABLE), musb_readl(musb->ctrl_base, TUSB_PHY_OTG_CTRL), musb_readl(musb->ctrl_base, TUSB_DEV_OTG_STAT), musb_readl(musb->ctrl_base, TUSB_DEV_OTG_TIMER), musb_readl(musb->ctrl_base, TUSB_PRCM_CONF), musb_readl(musb->ctrl_base, TUSB_PRCM_MNGMT), musb_readl(musb->ctrl_base, TUSB_INT_SRC), musb_readl(musb->ctrl_base, TUSB_INT_MASK)); if (code <= 0) goto done; count += code; buffer += code; #endif /* DAVINCI */ if (is_cppi_enabled() && musb->dma_controller) { code = sprintf(buffer, "CPPI: txcr=%d txsrc=%01x txena=%01x; " "rxcr=%d rxsrc=%01x rxena=%01x " "\n", musb_readl(musb->ctrl_base, DAVINCI_TXCPPI_CTRL_REG), musb_readl(musb->ctrl_base, DAVINCI_TXCPPI_RAW_REG), musb_readl(musb->ctrl_base, DAVINCI_TXCPPI_INTENAB_REG), musb_readl(musb->ctrl_base, DAVINCI_RXCPPI_CTRL_REG), musb_readl(musb->ctrl_base, DAVINCI_RXCPPI_RAW_REG), musb_readl(musb->ctrl_base, DAVINCI_RXCPPI_INTENAB_REG)); if (code <= 0) goto done; count += code; buffer += code; } #ifdef CONFIG_USB_GADGET_MUSB_HDRC if (is_peripheral_enabled(musb)) { code = sprintf(buffer, "Gadget driver: %s\n", musb->gadget_driver ? musb->gadget_driver->driver.name : "(none)"); if (code <= 0) goto done; count += code; buffer += code; } #endif done: return count; } /* Write to ProcFS * * C soft-connect * c soft-disconnect * I enable HS * i disable HS * s stop session * F force session (OTG-unfriendly) * E rElinquish bus (OTG) * H request host mode * h cancel host request * T start sending TEST_PACKET * D set/query the debug level */ static int musb_proc_write(struct file *file, const char __user *buffer, unsigned long count, void *data) { char cmd; u8 reg; struct musb *musb = (struct musb *)data; void __iomem *mbase = musb->mregs; /* MOD_INC_USE_COUNT; */ if (unlikely(copy_from_user(&cmd, buffer, 1))) return -EFAULT; switch (cmd) { case 'C': if (mbase) { reg = musb_readb(mbase, MUSB_POWER) | MUSB_POWER_SOFTCONN; musb_writeb(mbase, MUSB_POWER, reg); } break; case 'c': if (mbase) { reg = musb_readb(mbase, MUSB_POWER) & ~MUSB_POWER_SOFTCONN; musb_writeb(mbase, MUSB_POWER, reg); } break; case 'I': if (mbase) { reg = musb_readb(mbase, MUSB_POWER) | MUSB_POWER_HSENAB; musb_writeb(mbase, MUSB_POWER, reg); } break; case 'i': if (mbase) { reg = musb_readb(mbase, MUSB_POWER) & ~MUSB_POWER_HSENAB; musb_writeb(mbase, MUSB_POWER, reg); } break; case 'F': reg = musb_readb(mbase, MUSB_DEVCTL); reg |= MUSB_DEVCTL_SESSION; musb_writeb(mbase, MUSB_DEVCTL, reg); break; case 'H': if (mbase) { reg = musb_readb(mbase, MUSB_DEVCTL); reg |= MUSB_DEVCTL_HR; musb_writeb(mbase, MUSB_DEVCTL, reg); /* MUSB_HST_MODE( ((struct musb*)data) ); */ /* WARN("Host Mode\n"); */ } break; case 'h': if (mbase) { reg = musb_readb(mbase, MUSB_DEVCTL); reg &= ~MUSB_DEVCTL_HR; musb_writeb(mbase, MUSB_DEVCTL, reg); } break; case 'T': if (mbase) { musb_load_testpacket(musb); musb_writeb(mbase, MUSB_TESTMODE, MUSB_TEST_PACKET); } break; #if (MUSB_DEBUG > 0) /* set/read debug level */ case 'D':{ if (count > 1) { char digits[8], *p = digits; int i = 0, level = 0, sign = 1; int len = min(count - 1, (unsigned long)8); if (copy_from_user(&digits, &buffer[1], len)) return -EFAULT; /* optional sign */ if (*p == '-') { len -= 1; sign = -sign; p++; } /* read it */ while (i++ < len && *p > '0' && *p < '9') { level = level * 10 + (*p - '0'); p++; } level *= sign; DBG(1, "debug level %d\n", level); debug = level; } } break; case '?': INFO("?: you are seeing it\n"); INFO("C/c: soft connect enable/disable\n"); INFO("I/i: hispeed enable/disable\n"); INFO("F: force session start\n"); INFO("H: host mode\n"); INFO("T: start sending TEST_PACKET\n"); INFO("D: set/read dbug level\n"); break; #endif default: ERR("Command %c not implemented\n", cmd); break; } musb_platform_try_idle(musb, 0); return count; } static int musb_proc_read(char *page, char **start, off_t off, int count, int *eof, void *data) { char *buffer = page; int code = 0; unsigned long flags; struct musb *musb = data; unsigned epnum; count -= off; count -= 1; /* for NUL at end */ if (count <= 0) return -EINVAL; spin_lock_irqsave(&musb->lock, flags); code = dump_header_stats(musb, buffer); if (code > 0) { buffer += code; count -= code; } /* generate the report for the end points */ /* REVISIT ... not unless something's connected! */ for (epnum = 0; count >= 0 && epnum < musb->nr_endpoints; epnum++) { code = dump_end_info(musb, epnum, buffer, count); if (code > 0) { buffer += code; count -= code; } } musb_platform_try_idle(musb, 0); spin_unlock_irqrestore(&musb->lock, flags); *eof = 1; return buffer - page; } void __devexit musb_debug_delete(char *name, struct musb *musb) { if (musb->proc_entry) remove_proc_entry(name, NULL); } struct proc_dir_entry *__init musb_debug_create(char *name, struct musb *data) { struct proc_dir_entry *pde; /* FIXME convert everything to seq_file; then later, debugfs */ if (!name) return NULL; data->proc_entry = pde = create_proc_entry(name, S_IFREG | S_IRUGO | S_IWUSR, NULL); if (pde) { pde->data = data; /* pde->owner = THIS_MODULE; */ pde->read_proc = musb_proc_read; pde->write_proc = musb_proc_write; pde->size = 0; pr_debug("Registered /proc/%s\n", name); } else { pr_debug("Cannot create a valid proc file entry"); } return pde; }