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
diff --git a/drivers/usb/ohci_dbg.c b/drivers/usb/ohci_dbg.c new file mode 100644 index 0000000..8df83cb --- /dev/null +++ b/drivers/usb/ohci_dbg.c @@ -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 */