new file mode 100644
@@ -0,0 +1,297 @@
+/*
+ * URB OHCI HCD (Host Controller Driver) for USB.
+ *
+ * Implementation taken from U-Boot sources,
+ * copyright (c) 1999-2007
+ *
+ * Zhang Wei, Freescale Semiconductor, Inc. <wei.zhang@freescale.com>
+ * Gary Jennejohn, DENX Software Engineering <garyj@denx.de>
+ * Roman Weissgaerber <weissg@vienna.at>
+ * David Brownell
+ *
+ * Port to libpayload by Daniel Mack <daniel@caiaq.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <libpayload-config.h>
+
+#include <usb/usb.h>
+#include <arch/virtual.h>
+#include <arch/endian.h>
+
+#include "ohci.h"
+#include "ohci_rh.h"
+
+#ifdef DEBUG
+
+static int sohci_get_current_frame_number(ohci_t *ohci)
+{
+ return readl(&ohci->hcca->frame_no);
+}
+
+static const char *ep_type_to_str(endpoint_t *ep)
+{
+ switch (ep->type) {
+ case CONTROL: return "CONTROL"; break;
+ case BULK: return "BULK"; break;
+ case INTERRUPT: return "INTERRUPT"; break;
+ case ISOCHRONOUS: return "ISOCHRONOUS"; break;
+ }
+
+ return "BOGUS";
+}
+
+void pkt_print(urb_priv_t *urb, void *setup, const char *str, int verbose)
+{
+ endpoint_t *ep = urb->ep;
+ usbdev_t *dev = ep->dev;
+ ohci_t *ohci = OHCI_INST(dev->controller);
+
+ dbg("%s URB:[%4x] dev:%2lu,ep:%2lu-%c,type:%s,len:%d/%d\n",
+ str,
+ sohci_get_current_frame_number(ohci),
+ dev->address,
+ ep->endpoint,
+ (ep->direction == OUT) ? 'O':
+ (ep->direction == SETUP) ? 'S' : 'I',
+ ep_type_to_str(ep),
+ (urb ? urb->actual_length : 0),
+ urb->transfer_buffer_length);
+
+ if (!verbose)
+ return;
+
+ if (ep->direction != IN) {
+ int i, len;
+
+ if (ep->type == CONTROL) {
+ printf( "%s: cmd(8):\n", __func__);
+ for (i = 0; i < 8 ; i++)
+ printf(" %02x", ((u8 *) setup) [i]);
+ printf("\n");
+ }
+
+ if (urb->transfer_buffer_length > 0 && urb->transfer_buffer) {
+ printf("%s: data(%d/%d):",
+ __func__,
+ urb->actual_length,
+ urb->transfer_buffer_length);
+ len = (ep->direction == OUT) ? urb->actual_length : 0;
+
+ for (i = 0; i < 16 && i < len; i++)
+ printf(" %02x", ((u8 *) urb->transfer_buffer)[i]);
+
+ printf("%s\n", i < len ? "...": "");
+ }
+ }
+}
+
+/* prints non-empty branches of the int ed tree inclusive iso eds */
+void ep_print_int_eds(ohci_t *ohci, const char *str)
+{
+ int i, j;
+ volatile u32 *ed_p;
+
+ for (i = 0; i < 32; i++) {
+ j = 5;
+ ed_p = &(ohci->hcca->int_table[i]);
+ if (!*ed_p)
+ continue;
+
+ dbg("%s: %s branch int %d (%x):", __func__, str, i, i);
+ while (*ed_p && j--) {
+ ohci_ed_t *ed = (ohci_ed_t *) le32_to_cpu(ed_p);
+ dbg(" ed: %4x;", ed->hwINFO);
+ ed_p = &ed->hwNextED;
+ }
+ dbg("\n");
+ }
+}
+
+static void ohci_dump_intr_mask(char *label, u32 mask)
+{
+ dbg("%s: 0x%08x%s%s%s%s%s%s%s%s%s\n",
+ label,
+ mask,
+ (mask & OHCI_INTR_MIE) ? " MIE" : "",
+ (mask & OHCI_INTR_OC) ? " OC" : "",
+ (mask & OHCI_INTR_RHSC) ? " RHSC" : "",
+ (mask & OHCI_INTR_FNO) ? " FNO" : "",
+ (mask & OHCI_INTR_UE) ? " UE" : "",
+ (mask & OHCI_INTR_RD) ? " RD" : "",
+ (mask & OHCI_INTR_SF) ? " SF" : "",
+ (mask & OHCI_INTR_WDH) ? " WDH" : "",
+ (mask & OHCI_INTR_SO) ? " SO" : ""
+ );
+}
+
+static void print_eds(const char *label, u32 value)
+{
+ ohci_ed_t *ed = phys_to_virt(value);
+ u32 hwNext;
+
+ if (!value)
+ return;
+
+ dbg("%s @%p: hwINFO %08x hwTailP %08x hwHeadP %08x hwNextED %08x\n",
+ label, ed, ed->hwINFO, ed->hwTailP, ed->hwHeadP, ed->hwNextED);
+
+ hwNext = ed->hwHeadP & ~0xf;
+
+ while (hwNext) {
+ ohci_td_t *td = phys_to_virt(hwNext);
+ dbg(" --- TD @%p: hwINFO %08x hwCBP %08x hwBE %08x hwNextTD %08x ",
+ td, td->hwINFO, td->hwCBP, td->hwBE, td->hwNextTD);
+
+ if (td->hwCBP) {
+ u8 *buf = phys_to_virt(td->hwCBP);
+ dbg("(%02x %02x %02x %02x %02x %02x %02x %02x)",
+ buf[0], buf[1], buf[2], buf[3],
+ buf[4], buf[5], buf[6], buf[7]);
+ }
+
+ dbg("\n");
+
+ hwNext = td->hwNextTD;
+ }
+}
+
+static char *hcfs2string(int state)
+{
+ switch (state) {
+ case OHCI_USB_RESET: return "reset";
+ case OHCI_USB_RESUME: return "resume";
+ case OHCI_USB_OPER: return "operational";
+ case OHCI_USB_SUSPEND: return "suspend";
+ }
+
+ return "?";
+}
+
+/* dump control and status registers */
+static void ohci_dump_status(ohci_t *ohci)
+{
+ u32 temp;
+ struct ohci_regs *regs = ohci->regs;
+
+ temp = readl(®s->control);
+ dbg("control: 0x%08x%s%s%s HCFS=%s%s%s%s%s CBSR=%d\n", temp,
+ (temp & OHCI_CTRL_RWE) ? " RWE" : "",
+ (temp & OHCI_CTRL_RWC) ? " RWC" : "",
+ (temp & OHCI_CTRL_IR) ? " IR" : "",
+ hcfs2string(temp & OHCI_CTRL_HCFS),
+ (temp & OHCI_CTRL_BLE) ? " BLE" : "",
+ (temp & OHCI_CTRL_CLE) ? " CLE" : "",
+ (temp & OHCI_CTRL_IE) ? " IE" : "",
+ (temp & OHCI_CTRL_PLE) ? " PLE" : "",
+ temp & OHCI_CTRL_CBSR
+ );
+
+ temp = readl(®s->cmdstatus);
+ dbg("cmdstatus: 0x%08x SOC=%d%s%s%s%s\n", temp,
+ (temp & OHCI_SOC) >> 16,
+ (temp & OHCI_OCR) ? " OCR" : "",
+ (temp & OHCI_BLF) ? " BLF" : "",
+ (temp & OHCI_CLF) ? " CLF" : "",
+ (temp & OHCI_HCR) ? " HCR" : ""
+ );
+
+ ohci_dump_intr_mask("intrstatus", readl(®s->intrstatus));
+ ohci_dump_intr_mask("intrenable", readl(®s->intrenable));
+
+ print_eds("ed_periodcurrent", readl(®s->ed_periodcurrent));
+ print_eds("ed_controlhead", readl(®s->ed_controlhead));
+ print_eds("ed_controlcurrent", readl(®s->ed_controlcurrent));
+ print_eds("ed_bulkhead", readl(®s->ed_bulkhead));
+ print_eds("ed_bulkcurrent", readl(®s->ed_bulkcurrent));
+ print_eds("donehead", readl(®s->donehead));
+}
+
+void ohci_dump_roothub(ohci_t *ohci, int verbose)
+{
+ u32 temp, ndp, i;
+
+ temp = roothub_a(ohci);
+ ndp = (temp & RH_A_NDP) & 0xf;
+
+ if (verbose) {
+ dbg("roothub.a: %08x POTPGT=%d%s%s%s%s%s NDP=%d\n", temp,
+ ((temp & RH_A_POTPGT) >> 24) & 0xff,
+ (temp & RH_A_NOCP) ? " NOCP" : "",
+ (temp & RH_A_OCPM) ? " OCPM" : "",
+ (temp & RH_A_DT) ? " DT" : "",
+ (temp & RH_A_NPS) ? " NPS" : "",
+ (temp & RH_A_PSM) ? " PSM" : "",
+ ndp
+ );
+ temp = roothub_b(ohci);
+ dbg("roothub.b: %08x PPCM=%04x DR=%04x\n",
+ temp,
+ (temp & RH_B_PPCM) >> 16,
+ (temp & RH_B_DR)
+ );
+ temp = roothub_status(ohci);
+ dbg("roothub.status: %08x%s%s%s%s%s%s\n",
+ temp,
+ (temp & RH_HS_CRWE) ? " CRWE" : "",
+ (temp & RH_HS_OCIC) ? " OCIC" : "",
+ (temp & RH_HS_LPSC) ? " LPSC" : "",
+ (temp & RH_HS_DRWE) ? " DRWE" : "",
+ (temp & RH_HS_OCI) ? " OCI" : "",
+ (temp & RH_HS_LPS) ? " LPS" : ""
+ );
+ }
+
+ for (i = 0; i < ndp; i++) {
+ temp = roothub_portstatus(ohci, i);
+ dbg("roothub.portstatus[%d] = 0x%08x%s%s%s%s%s%s%s%s%s%s%s%s\n",
+ i,
+ temp,
+ (temp & RH_PS_PRSC) ? " PRSC" : "",
+ (temp & RH_PS_OCIC) ? " OCIC" : "",
+ (temp & RH_PS_PSSC) ? " PSSC" : "",
+ (temp & RH_PS_PESC) ? " PESC" : "",
+ (temp & RH_PS_CSC) ? " CSC" : "",
+
+ (temp & RH_PS_LSDA) ? " LSDA" : "",
+ (temp & RH_PS_PPS) ? " PPS" : "",
+ (temp & RH_PS_PRS) ? " PRS" : "",
+ (temp & RH_PS_POCI) ? " POCI" : "",
+ (temp & RH_PS_PSS) ? " PSS" : "",
+
+ (temp & RH_PS_PES) ? " PES" : "",
+ (temp & RH_PS_CCS) ? " CCS" : ""
+ );
+ }
+}
+
+void ohci_dump(ohci_t *ohci, int verbose)
+{
+ dbg("----------------------\n");
+ dbg("OHCI @%p ohci state:", ohci->regs);
+
+ ohci_dump_status(ohci);
+
+ if (verbose)
+ ep_print_int_eds(ohci, "hcca");
+
+ dbg("hcca frame #%04x\n", ohci->hcca->frame_no);
+ ohci_dump_roothub(ohci, 1);
+ dbg("----------------------\n");
+}
+
+#endif /* DEBUG */
Signed-off-by: Daniel Mack <daniel@caiaq.de> --- drivers/usb/ohci_dbg.c | 297 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 297 insertions(+), 0 deletions(-) create mode 100644 drivers/usb/ohci_dbg.c