Maemo patchset 20101501+0m5
[h-e-n] / drivers / usb / musb / musb_procfs.c
diff --git a/drivers/usb/musb/musb_procfs.c b/drivers/usb/musb/musb_procfs.c
new file mode 100644 (file)
index 0000000..8915b62
--- /dev/null
@@ -0,0 +1,775 @@
+/*
+ * 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 <linux/kernel.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>     /* FIXME remove procfs writes */
+#include <mach/hardware.h>
+
+#include "musb_core.h"
+
+#include "davinci.h"
+
+extern unsigned musb_debug;
+
+#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 = qh->hep;
+       struct urb                      *urb;
+
+       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 (cppi_ti_dma() && 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(&hw_ep->in_list);
+                               dump_rx = 0;
+                       } else if (hw_ep == musb->bulk_ep) {
+                               dump_tx = !list_empty(&hw_ep->out_list);
+                               dump_rx = !list_empty(&hw_ep->in_list);
+                       } 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 (cppi_ti_dma()
+                                               && 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->bulk_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;
+                               }
+                       }
+
+                       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 (cppi_ti_dma()
+                                               && 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->control_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 (hw_ep == musb->bulk_ep
+                                               && !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;
+                               }
+                       }
+               }
+#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 to 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",
+               musb_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 (cppi_ti_dma() && 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<num> 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) ); */
+                       /* WARNING("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;
+
+       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");
+               break;
+
+       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;
+
+       pde = create_proc_entry(name, S_IFREG | S_IRUGO | S_IWUSR, NULL);
+       data->proc_entry = pde;
+       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;
+}