From 3e326ece9eba8184f5d48aa4fb87760a8f6f0f10 Mon Sep 17 00:00:00 2001 From: Markus Klotzbuecher Date: Mon, 22 May 2006 16:33:54 +0200 Subject: This patch adds USB storage support for the delta board. This is the first board to make use of a generic OHCI driver, that calls hooks for board dependant initialization. --- drivers/Makefile | 2 +- drivers/usb_ohci.c | 1642 ++++++++++++++++++++++++++++++++++++++++++++++++++++ drivers/usb_ohci.h | 422 ++++++++++++++ 3 files changed, 2065 insertions(+), 1 deletion(-) create mode 100644 drivers/usb_ohci.c create mode 100644 drivers/usb_ohci.h (limited to 'drivers') diff --git a/drivers/Makefile b/drivers/Makefile index e6176ed86..d5b681182 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -46,7 +46,7 @@ OBJS = 3c589.o 5701rls.o ali512x.o \ sl811_usb.o sm501.o smc91111.o smiLynxEM.o \ status_led.o sym53c8xx.o \ ti_pci1410a.o tigon3.o tsec.o \ - usbdcore.o usbdcore_ep0.o usbdcore_omap1510.o usbtty.o \ + usb_ohci.o usbdcore.o usbdcore_ep0.o usbdcore_omap1510.o usbtty.o \ videomodes.o w83c553f.o \ ks8695eth.o diff --git a/drivers/usb_ohci.c b/drivers/usb_ohci.c new file mode 100644 index 000000000..4065489f3 --- /dev/null +++ b/drivers/usb_ohci.c @@ -0,0 +1,1642 @@ +/* + * URB OHCI HCD (Host Controller Driver) for USB on the AT91RM9200. + * + * (C) Copyright 2003 + * Gary Jennejohn, DENX Software Engineering + * + * Note: Much of this code has been derived from Linux 2.4 + * (C) Copyright 1999 Roman Weissgaerber + * (C) Copyright 2000-2002 David Brownell + * + * Modified for the MP2USB by (C) Copyright 2005 Eric Benard + * ebenard@eukrea.com - based on s3c24x0's driver + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 + * + */ +/* + * IMPORTANT NOTES + * 1 - you MUST define LITTLEENDIAN in the configuration file for the + * board or this driver will NOT work! + * 2 - this driver is intended for use with USB Mass Storage Devices + * (BBB) ONLY. There is NO support for Interrupt or Isochronous pipes! + * 3 - when running on a PQFP208 AT91RM9200, define CONFIG_AT91C_PQFP_UHPBUG + * to activate workaround for bug #41 or this driver will NOT work! + */ + +#include +/* #include no PCI on the S3C24X0 */ + +#ifdef CONFIG_USB_OHCI + +#include + +#include +#include +#include "usb_ohci.h" + +/* #define OHCI_USE_NPS /\* force NoPowerSwitching mode *\/ */ +#undef OHCI_VERBOSE_DEBUG /* not always helpful */ + +/* For initializing controller (mask in an HCFS mode too) */ +#define OHCI_CONTROL_INIT \ + (OHCI_CTRL_CBSR & 0x3) | OHCI_CTRL_IE | OHCI_CTRL_PLE + +#define readl(a) (*((vu_long *)(a))) +#define writel(a, b) (*((vu_long *)(b)) = ((vu_long)a)) + +#define min_t(type,x,y) ({ type __x = (x); type __y = (y); __x < __y ? __x: __y; }) + +#undef DEBUG +#ifdef DEBUG +#define dbg(format, arg...) printf("DEBUG: " format "\n", ## arg) +#else +#define dbg(format, arg...) do {} while(0) +#endif /* DEBUG */ +#define err(format, arg...) printf("ERROR: " format "\n", ## arg) +#undef SHOW_INFO +#ifdef SHOW_INFO +#define info(format, arg...) printf("INFO: " format "\n", ## arg) +#else +#define info(format, arg...) do {} while(0) +#endif + +#define m16_swap(x) swap_16(x) +#define m32_swap(x) swap_32(x) + +/* global ohci_t */ +static ohci_t gohci; +/* this must be aligned to a 256 byte boundary */ +struct ohci_hcca ghcca[1]; +/* a pointer to the aligned storage */ +struct ohci_hcca *phcca; +/* this allocates EDs for all possible endpoints */ +struct ohci_device ohci_dev; +/* urb_priv */ +urb_priv_t urb_priv; +/* RHSC flag */ +int got_rhsc; +/* device which was disconnected */ +struct usb_device *devgone; + +/*-------------------------------------------------------------------------*/ + +/* AMD-756 (D2 rev) reports corrupt register contents in some cases. + * The erratum (#4) description is incorrect. AMD's workaround waits + * till some bits (mostly reserved) are clear; ok for all revs. + */ +#define OHCI_QUIRK_AMD756 0xabcd +#define read_roothub(hc, register, mask) ({ \ + u32 temp = readl (&hc->regs->roothub.register); \ + if (hc->flags & OHCI_QUIRK_AMD756) \ + while (temp & mask) \ + temp = readl (&hc->regs->roothub.register); \ + temp; }) + +static u32 roothub_a (struct ohci *hc) + { return read_roothub (hc, a, 0xfc0fe000); } +static inline u32 roothub_b (struct ohci *hc) + { return readl (&hc->regs->roothub.b); } +static inline u32 roothub_status (struct ohci *hc) + { return readl (&hc->regs->roothub.status); } +static u32 roothub_portstatus (struct ohci *hc, int i) + { return read_roothub (hc, portstatus [i], 0xffe0fce0); } + + +/* forward declaration */ +static int hc_interrupt (void); +static void +td_submit_job (struct usb_device * dev, unsigned long pipe, void * buffer, + int transfer_len, struct devrequest * setup, urb_priv_t * urb, int interval); + +/*-------------------------------------------------------------------------* + * URB support functions + *-------------------------------------------------------------------------*/ + +/* free HCD-private data associated with this URB */ + +static void urb_free_priv (urb_priv_t * urb) +{ + int i; + int last; + struct td * td; + + last = urb->length - 1; + if (last >= 0) { + for (i = 0; i <= last; i++) { + td = urb->td[i]; + if (td) { + td->usb_dev = NULL; + urb->td[i] = NULL; + } + } + } +} + +/*-------------------------------------------------------------------------*/ + +#ifdef DEBUG +static int sohci_get_current_frame_number (struct usb_device * dev); + +/* debug| print the main components of an URB + * small: 0) header + data packets 1) just header */ + +static void pkt_print (struct usb_device * dev, unsigned long pipe, void * buffer, + int transfer_len, struct devrequest * setup, char * str, int small) +{ + urb_priv_t * purb = &urb_priv; + + dbg("%s URB:[%4x] dev:%2d,ep:%2d-%c,type:%s,len:%d/%d stat:%#lx", + str, + sohci_get_current_frame_number (dev), + usb_pipedevice (pipe), + usb_pipeendpoint (pipe), + usb_pipeout (pipe)? 'O': 'I', + usb_pipetype (pipe) < 2? (usb_pipeint (pipe)? "INTR": "ISOC"): + (usb_pipecontrol (pipe)? "CTRL": "BULK"), + purb->actual_length, + transfer_len, dev->status); +#ifdef OHCI_VERBOSE_DEBUG + if (!small) { + int i, len; + + if (usb_pipecontrol (pipe)) { + printf (__FILE__ ": cmd(8):"); + for (i = 0; i < 8 ; i++) + printf (" %02x", ((__u8 *) setup) [i]); + printf ("\n"); + } + if (transfer_len > 0 && buffer) { + printf (__FILE__ ": data(%d/%d):", + purb->actual_length, + transfer_len); + len = usb_pipeout (pipe)? + transfer_len: purb->actual_length; + for (i = 0; i < 16 && i < len; i++) + printf (" %02x", ((__u8 *) buffer) [i]); + printf ("%s\n", i < len? "...": ""); + } + } +#endif +} + +/* just for debugging; prints non-empty branches of the int ed tree inclusive iso eds*/ +void ep_print_int_eds (ohci_t *ohci, char * str) { + int i, j; + __u32 * ed_p; + for (i= 0; i < 32; i++) { + j = 5; + ed_p = &(ohci->hcca->int_table [i]); + if (*ed_p == 0) + continue; + printf (__FILE__ ": %s branch int %2d(%2x):", str, i, i); + while (*ed_p != 0 && j--) { + ed_t *ed = (ed_t *)m32_swap(ed_p); + printf (" ed: %4x;", ed->hwINFO); + ed_p = &ed->hwNextED; + } + printf ("\n"); + } +} + +static void ohci_dump_intr_mask (char *label, __u32 mask) +{ + dbg ("%s: 0x%08x%s%s%s%s%s%s%s%s%s", + 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 maybe_print_eds (char *label, __u32 value) +{ + ed_t *edp = (ed_t *)value; + + if (value) { + dbg ("%s %08x", label, value); + dbg ("%08x", edp->hwINFO); + dbg ("%08x", edp->hwTailP); + dbg ("%08x", edp->hwHeadP); + dbg ("%08x", edp->hwNextED); + } +} + +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 *controller) +{ + struct ohci_regs *regs = controller->regs; + __u32 temp; + + temp = readl (®s->revision) & 0xff; + if (temp != 0x10) + dbg ("spec %d.%d", (temp >> 4), (temp & 0x0f)); + + temp = readl (®s->control); + dbg ("control: 0x%08x%s%s%s HCFS=%s%s%s%s%s CBSR=%d", 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", 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)); + + maybe_print_eds ("ed_periodcurrent", readl (®s->ed_periodcurrent)); + + maybe_print_eds ("ed_controlhead", readl (®s->ed_controlhead)); + maybe_print_eds ("ed_controlcurrent", readl (®s->ed_controlcurrent)); + + maybe_print_eds ("ed_bulkhead", readl (®s->ed_bulkhead)); + maybe_print_eds ("ed_bulkcurrent", readl (®s->ed_bulkcurrent)); + + maybe_print_eds ("donehead", readl (®s->donehead)); +} + +static void ohci_dump_roothub (ohci_t *controller, int verbose) +{ + __u32 temp, ndp, i; + + temp = roothub_a (controller); + ndp = (temp & RH_A_NDP); +#ifdef CONFIG_AT91C_PQFP_UHPBUG + ndp = (ndp == 2) ? 1:0; +#endif +#if 0 /* def CONFIG_CPU_MONAHANS */ + data_buf [2] = (data_buf [2] == 2) ? 3:0; +#endif + if (verbose) { + dbg ("roothub.a: %08x POTPGT=%d%s%s%s%s%s NDP=%d", 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 (controller); + dbg ("roothub.b: %08x PPCM=%04x DR=%04x", + temp, + (temp & RH_B_PPCM) >> 16, + (temp & RH_B_DR) + ); + temp = roothub_status (controller); + dbg ("roothub.status: %08x%s%s%s%s%s%s", + 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 (controller, i); + dbg ("roothub.portstatus [%d] = 0x%08x%s%s%s%s%s%s%s%s%s%s%s%s", + 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" : "" + ); + } +} + +static void ohci_dump (ohci_t *controller, int verbose) +{ + dbg ("OHCI controller usb-%s state", controller->slot_name); + + /* dumps some of the state we know about */ + ohci_dump_status (controller); + if (verbose) + ep_print_int_eds (controller, "hcca"); + dbg ("hcca frame #%04x", controller->hcca->frame_no); + ohci_dump_roothub (controller, 1); +} + + +#endif /* DEBUG */ + +/*-------------------------------------------------------------------------* + * Interface functions (URB) + *-------------------------------------------------------------------------*/ + +/* get a transfer request */ + +int sohci_submit_job(struct usb_device *dev, unsigned long pipe, void *buffer, + int transfer_len, struct devrequest *setup, int interval) +{ + ohci_t *ohci; + ed_t * ed; + urb_priv_t *purb_priv; + int i, size = 0; + + ohci = &gohci; + + /* when controller's hung, permit only roothub cleanup attempts + * such as powering down ports */ + if (ohci->disabled) { + err("sohci_submit_job: EPIPE"); + return -1; + } + + /* every endpoint has a ed, locate and fill it */ + if (!(ed = ep_add_ed (dev, pipe))) { + err("sohci_submit_job: ENOMEM"); + return -1; + } + + /* for the private part of the URB we need the number of TDs (size) */ + switch (usb_pipetype (pipe)) { + case PIPE_BULK: /* one TD for every 4096 Byte */ + size = (transfer_len - 1) / 4096 + 1; + break; + case PIPE_CONTROL: /* 1 TD for setup, 1 for ACK and 1 for every 4096 B */ + size = (transfer_len == 0)? 2: + (transfer_len - 1) / 4096 + 3; + break; + } + + if (size >= (N_URB_TD - 1)) { + err("need %d TDs, only have %d", size, N_URB_TD); + return -1; + } + purb_priv = &urb_priv; + purb_priv->pipe = pipe; + + /* fill the private part of the URB */ + purb_priv->length = size; + purb_priv->ed = ed; + purb_priv->actual_length = 0; + + /* allocate the TDs */ + /* note that td[0] was allocated in ep_add_ed */ + for (i = 0; i < size; i++) { + purb_priv->td[i] = td_alloc (dev); + if (!purb_priv->td[i]) { + purb_priv->length = i; + urb_free_priv (purb_priv); + err("sohci_submit_job: ENOMEM"); + return -1; + } + } + + if (ed->state == ED_NEW || (ed->state & ED_DEL)) { + urb_free_priv (purb_priv); + err("sohci_submit_job: EINVAL"); + return -1; + } + + /* link the ed into a chain if is not already */ + if (ed->state != ED_OPER) + ep_link (ohci, ed); + + /* fill the TDs and link it to the ed */ + td_submit_job(dev, pipe, buffer, transfer_len, setup, purb_priv, interval); + + return 0; +} + +/*-------------------------------------------------------------------------*/ + +#ifdef DEBUG +/* tell us the current USB frame number */ + +static int sohci_get_current_frame_number (struct usb_device *usb_dev) +{ + ohci_t *ohci = &gohci; + + return m16_swap (ohci->hcca->frame_no); +} +#endif + +/*-------------------------------------------------------------------------* + * ED handling functions + *-------------------------------------------------------------------------*/ + +/* link an ed into one of the HC chains */ + +static int ep_link (ohci_t *ohci, ed_t *edi) +{ + volatile ed_t *ed = edi; + + ed->state = ED_OPER; + + switch (ed->type) { + case PIPE_CONTROL: + ed->hwNextED = 0; + if (ohci->ed_controltail == NULL) { + writel (ed, &ohci->regs->ed_controlhead); + } else { + ohci->ed_controltail->hwNextED = m32_swap (ed); + } + ed->ed_prev = ohci->ed_controltail; + if (!ohci->ed_controltail && !ohci->ed_rm_list[0] && + !ohci->ed_rm_list[1] && !ohci->sleeping) { + ohci->hc_control |= OHCI_CTRL_CLE; + writel (ohci->hc_control, &ohci->regs->control); + } + ohci->ed_controltail = edi; + break; + + case PIPE_BULK: + ed->hwNextED = 0; + if (ohci->ed_bulktail == NULL) { + writel (ed, &ohci->regs->ed_bulkhead); + } else { + ohci->ed_bulktail->hwNextED = m32_swap (ed); + } + ed->ed_prev = ohci->ed_bulktail; + if (!ohci->ed_bulktail && !ohci->ed_rm_list[0] && + !ohci->ed_rm_list[1] && !ohci->sleeping) { + ohci->hc_control |= OHCI_CTRL_BLE; + writel (ohci->hc_control, &ohci->regs->control); + } + ohci->ed_bulktail = edi; + break; + } + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* unlink an ed from one of the HC chains. + * just the link to the ed is unlinked. + * the link from the ed still points to another operational ed or 0 + * so the HC can eventually finish the processing of the unlinked ed */ + +static int ep_unlink (ohci_t *ohci, ed_t *ed) +{ + ed->hwINFO |= m32_swap (OHCI_ED_SKIP); + + switch (ed->type) { + case PIPE_CONTROL: + if (ed->ed_prev == NULL) { + if (!ed->hwNextED) { + ohci->hc_control &= ~OHCI_CTRL_CLE; + writel (ohci->hc_control, &ohci->regs->control); + } + writel (m32_swap (*((__u32 *)&ed->hwNextED)), &ohci->regs->ed_controlhead); + } else { + ed->ed_prev->hwNextED = ed->hwNextED; + } + if (ohci->ed_controltail == ed) { + ohci->ed_controltail = ed->ed_prev; + } else { + ((ed_t *)m32_swap (*((__u32 *)&ed->hwNextED)))->ed_prev = ed->ed_prev; + } + break; + + case PIPE_BULK: + if (ed->ed_prev == NULL) { + if (!ed->hwNextED) { + ohci->hc_control &= ~OHCI_CTRL_BLE; + writel (ohci->hc_control, &ohci->regs->control); + } + writel (m32_swap (*((__u32 *)&ed->hwNextED)), &ohci->regs->ed_bulkhead); + } else { + ed->ed_prev->hwNextED = ed->hwNextED; + } + if (ohci->ed_bulktail == ed) { + ohci->ed_bulktail = ed->ed_prev; + } else { + ((ed_t *)m32_swap (*((__u32 *)&ed->hwNextED)))->ed_prev = ed->ed_prev; + } + break; + } + ed->state = ED_UNLINK; + return 0; +} + + +/*-------------------------------------------------------------------------*/ + +/* add/reinit an endpoint; this should be done once at the usb_set_configuration command, + * but the USB stack is a little bit stateless so we do it at every transaction + * if the state of the ed is ED_NEW then a dummy td is added and the state is changed to ED_UNLINK + * in all other cases the state is left unchanged + * the ed info fields are setted anyway even though most of them should not change */ + +static ed_t * ep_add_ed (struct usb_device *usb_dev, unsigned long pipe) +{ + td_t *td; + ed_t *ed_ret; + volatile ed_t *ed; + + ed = ed_ret = &ohci_dev.ed[(usb_pipeendpoint (pipe) << 1) | + (usb_pipecontrol (pipe)? 0: usb_pipeout (pipe))]; + + if ((ed->state & ED_DEL) || (ed->state & ED_URB_DEL)) { + err("ep_add_ed: pending delete"); + /* pending delete request */ + return NULL; + } + + if (ed->state == ED_NEW) { + ed->hwINFO = m32_swap (OHCI_ED_SKIP); /* skip ed */ + /* dummy td; end of td list for ed */ + td = td_alloc (usb_dev); + ed->hwTailP = m32_swap (td); + ed->hwHeadP = ed->hwTailP; + ed->state = ED_UNLINK; + ed->type = usb_pipetype (pipe); + ohci_dev.ed_cnt++; + } + + ed->hwINFO = m32_swap (usb_pipedevice (pipe) + | usb_pipeendpoint (pipe) << 7 + | (usb_pipeisoc (pipe)? 0x8000: 0) + | (usb_pipecontrol (pipe)? 0: (usb_pipeout (pipe)? 0x800: 0x1000)) + | usb_pipeslow (pipe) << 13 + | usb_maxpacket (usb_dev, pipe) << 16); + + return ed_ret; +} + +/*-------------------------------------------------------------------------* + * TD handling functions + *-------------------------------------------------------------------------*/ + +/* enqueue next TD for this URB (OHCI spec 5.2.8.2) */ + +static void td_fill (ohci_t *ohci, unsigned int info, + void *data, int len, + struct usb_device *dev, int index, urb_priv_t *urb_priv) +{ + volatile td_t *td, *td_pt; +#ifdef OHCI_FILL_TRACE + int i; +#endif + + if (index > urb_priv->length) { + err("index > length"); + return; + } + /* use this td as the next dummy */ + td_pt = urb_priv->td [index]; + td_pt->hwNextTD = 0; + + /* fill the old dummy TD */ + td = urb_priv->td [index] = (td_t *)(m32_swap (urb_priv->ed->hwTailP) & ~0xf); + + td->ed = urb_priv->ed; + td->next_dl_td = NULL; + td->index = index; + td->data = (__u32)data; +#ifdef OHCI_FILL_TRACE + if ((usb_pipetype(urb_priv->pipe) == PIPE_BULK) && usb_pipeout(urb_priv->pipe)) { + for (i = 0; i < len; i++) + printf("td->data[%d] %#2x ",i, ((unsigned char *)td->data)[i]); + printf("\n"); + } +#endif + if (!len) + data = 0; + + td->hwINFO = m32_swap (info); + td->hwCBP = m32_swap (data); + if (data) + td->hwBE = m32_swap (data + len - 1); + else + td->hwBE = 0; + td->hwNextTD = m32_swap (td_pt); + td->hwPSW [0] = m16_swap (((__u32)data & 0x0FFF) | 0xE000); + + /* append to queue */ + td->ed->hwTailP = td->hwNextTD; +} + +/*-------------------------------------------------------------------------*/ + +/* prepare all TDs of a transfer */ + +static void td_submit_job (struct usb_device *dev, unsigned long pipe, void *buffer, + int transfer_len, struct devrequest *setup, urb_priv_t *urb, int interval) +{ + ohci_t *ohci = &gohci; + int data_len = transfer_len; + void *data; + int cnt = 0; + __u32 info = 0; + unsigned int toggle = 0; + + /* OHCI handles the DATA-toggles itself, we just use the USB-toggle bits for reseting */ + if(usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe))) { + toggle = TD_T_TOGGLE; + } else { + toggle = TD_T_DATA0; + usb_settoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe), 1); + } + urb->td_cnt = 0; + if (data_len) + data = buffer; + else + data = 0; + + switch (usb_pipetype (pipe)) { + case PIPE_BULK: + info = usb_pipeout (pipe)? + TD_CC | TD_DP_OUT : TD_CC | TD_DP_IN ; + while(data_len > 4096) { + td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), data, 4096, dev, cnt, urb); + data += 4096; data_len -= 4096; cnt++; + } + info = usb_pipeout (pipe)? + TD_CC | TD_DP_OUT : TD_CC | TD_R | TD_DP_IN ; + td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), data, data_len, dev, cnt, urb); + cnt++; + + if (!ohci->sleeping) + writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */ + break; + + case PIPE_CONTROL: + info = TD_CC | TD_DP_SETUP | TD_T_DATA0; + td_fill (ohci, info, setup, 8, dev, cnt++, urb); + if (data_len > 0) { + info = usb_pipeout (pipe)? + TD_CC | TD_R | TD_DP_OUT | TD_T_DATA1 : TD_CC | TD_R | TD_DP_IN | TD_T_DATA1; + /* NOTE: mishandles transfers >8K, some >4K */ + td_fill (ohci, info, data, data_len, dev, cnt++, urb); + } + info = usb_pipeout (pipe)? + TD_CC | TD_DP_IN | TD_T_DATA1: TD_CC | TD_DP_OUT | TD_T_DATA1; + td_fill (ohci, info, data, 0, dev, cnt++, urb); + if (!ohci->sleeping) + writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */ + break; + } + if (urb->length != cnt) + dbg("TD LENGTH %d != CNT %d", urb->length, cnt); +} + +/*-------------------------------------------------------------------------* + * Done List handling functions + *-------------------------------------------------------------------------*/ + + +/* calculate the transfer length and update the urb */ + +static void dl_transfer_length(td_t * td) +{ + __u32 tdINFO, tdBE, tdCBP; + urb_priv_t *lurb_priv = &urb_priv; + + tdINFO = m32_swap (td->hwINFO); + tdBE = m32_swap (td->hwBE); + tdCBP = m32_swap (td->hwCBP); + + + if (!(usb_pipetype (lurb_priv->pipe) == PIPE_CONTROL && + ((td->index == 0) || (td->index == lurb_priv->length - 1)))) { + if (tdBE != 0) { + if (td->hwCBP == 0) + lurb_priv->actual_length += tdBE - td->data + 1; + else + lurb_priv->actual_length += tdCBP - td->data; + } + } +} + +/*-------------------------------------------------------------------------*/ + +/* replies to the request have to be on a FIFO basis so + * we reverse the reversed done-list */ + +static td_t * dl_reverse_done_list (ohci_t *ohci) +{ + __u32 td_list_hc; + td_t *td_rev = NULL; + td_t *td_list = NULL; + urb_priv_t *lurb_priv = NULL; + + td_list_hc = m32_swap (ohci->hcca->done_head) & 0xfffffff0; + ohci->hcca->done_head = 0; + + while (td_list_hc) { + td_list = (td_t *)td_list_hc; + + if (TD_CC_GET (m32_swap (td_list->hwINFO))) { + lurb_priv = &urb_priv; + dbg(" USB-error/status: %x : %p", + TD_CC_GET (m32_swap (td_list->hwINFO)), td_list); + if (td_list->ed->hwHeadP & m32_swap (0x1)) { + if (lurb_priv && ((td_list->index + 1) < lurb_priv->length)) { + td_list->ed->hwHeadP = + (lurb_priv->td[lurb_priv->length - 1]->hwNextTD & m32_swap (0xfffffff0)) | + (td_list->ed->hwHeadP & m32_swap (0x2)); + lurb_priv->td_cnt += lurb_priv->length - td_list->index - 1; + } else + td_list->ed->hwHeadP &= m32_swap (0xfffffff2); + } + } + + td_list->next_dl_td = td_rev; + td_rev = td_list; + td_list_hc = m32_swap (td_list->hwNextTD) & 0xfffffff0; + } + return td_list; +} + +/*-------------------------------------------------------------------------*/ + +/* td done list */ +static int dl_done_list (ohci_t *ohci, td_t *td_list) +{ + td_t *td_list_next = NULL; + ed_t *ed; + int cc = 0; + int stat = 0; + /* urb_t *urb; */ + urb_priv_t *lurb_priv; + __u32 tdINFO, edHeadP, edTailP; + + while (td_list) { + td_list_next = td_list->next_dl_td; + + lurb_priv = &urb_priv; + tdINFO = m32_swap (td_list->hwINFO); + + ed = td_list->ed; + + dl_transfer_length(td_list); + + /* error code of transfer */ + cc = TD_CC_GET (tdINFO); + if (cc != 0) { + dbg("ConditionCode %#x", cc); + stat = cc_to_error[cc]; + } + + if (ed->state != ED_NEW) { + edHeadP = m32_swap (ed->hwHeadP) & 0xfffffff0; + edTailP = m32_swap (ed->hwTailP); + + /* unlink eds if they are not busy */ + if ((edHeadP == edTailP) && (ed->state == ED_OPER)) + ep_unlink (ohci, ed); + } + + td_list = td_list_next; + } + return stat; +} + +/*-------------------------------------------------------------------------* + * Virtual Root Hub + *-------------------------------------------------------------------------*/ + +/* Device descriptor */ +static __u8 root_hub_dev_des[] = +{ + 0x12, /* __u8 bLength; */ + 0x01, /* __u8 bDescriptorType; Device */ + 0x10, /* __u16 bcdUSB; v1.1 */ + 0x01, + 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ + 0x00, /* __u8 bDeviceSubClass; */ + 0x00, /* __u8 bDeviceProtocol; */ + 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */ + 0x00, /* __u16 idVendor; */ + 0x00, + 0x00, /* __u16 idProduct; */ + 0x00, + 0x00, /* __u16 bcdDevice; */ + 0x00, + 0x00, /* __u8 iManufacturer; */ + 0x01, /* __u8 iProduct; */ + 0x00, /* __u8 iSerialNumber; */ + 0x01 /* __u8 bNumConfigurations; */ +}; + + +/* Configuration descriptor */ +static __u8 root_hub_config_des[] = +{ + 0x09, /* __u8 bLength; */ + 0x02, /* __u8 bDescriptorType; Configuration */ + 0x19, /* __u16 wTotalLength; */ + 0x00, + 0x01, /* __u8 bNumInterfaces; */ + 0x01, /* __u8 bConfigurationValue; */ + 0x00, /* __u8 iConfiguration; */ + 0x40, /* __u8 bmAttributes; + Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup, 4..0: resvd */ + 0x00, /* __u8 MaxPower; */ + + /* interface */ + 0x09, /* __u8 if_bLength; */ + 0x04, /* __u8 if_bDescriptorType; Interface */ + 0x00, /* __u8 if_bInterfaceNumber; */ + 0x00, /* __u8 if_bAlternateSetting; */ + 0x01, /* __u8 if_bNumEndpoints; */ + 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */ + 0x00, /* __u8 if_bInterfaceSubClass; */ + 0x00, /* __u8 if_bInterfaceProtocol; */ + 0x00, /* __u8 if_iInterface; */ + + /* endpoint */ + 0x07, /* __u8 ep_bLength; */ + 0x05, /* __u8 ep_bDescriptorType; Endpoint */ + 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ + 0x03, /* __u8 ep_bmAttributes; Interrupt */ + 0x02, /* __u16 ep_wMaxPacketSize; ((MAX_ROOT_PORTS + 1) / 8 */ + 0x00, + 0xff /* __u8 ep_bInterval; 255 ms */ +}; + +static unsigned char root_hub_str_index0[] = +{ + 0x04, /* __u8 bLength; */ + 0x03, /* __u8 bDescriptorType; String-descriptor */ + 0x09, /* __u8 lang ID */ + 0x04, /* __u8 lang ID */ +}; + +static unsigned char root_hub_str_index1[] = +{ + 28, /* __u8 bLength; */ + 0x03, /* __u8 bDescriptorType; String-descriptor */ + 'O', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'H', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'C', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'I', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + ' ', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'R', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'o', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'o', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 't', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + ' ', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'H', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'u', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'b', /* __u8 Unicode */ + 0, /* __u8 Unicode */ +}; + +/* Hub class-specific descriptor is constructed dynamically */ + + +/*-------------------------------------------------------------------------*/ + +#define OK(x) len = (x); break +#ifdef DEBUG +#define WR_RH_STAT(x) {info("WR:status %#8x", (x));writel((x), &gohci.regs->roothub.status);} +#define WR_RH_PORTSTAT(x) {info("WR:portstatus[%d] %#8x", wIndex-1, (x));writel((x), &gohci.regs->roothub.portstatus[wIndex-1]);} +#else +#define WR_RH_STAT(x) writel((x), &gohci.regs->roothub.status) +#define WR_RH_PORTSTAT(x) writel((x), &gohci.regs->roothub.portstatus[wIndex-1]) +#endif +#define RD_RH_STAT roothub_status(&gohci) +#define RD_RH_PORTSTAT roothub_portstatus(&gohci,wIndex-1) + +/* request to virtual root hub */ + +int rh_check_port_status(ohci_t *controller) +{ + __u32 temp, ndp, i; + int res; + + res = -1; + temp = roothub_a (controller); + ndp = (temp & RH_A_NDP); +#ifdef CONFIG_AT91C_PQFP_UHPBUG + ndp = (ndp == 2) ? 1:0; +#endif + for (i = 0; i < ndp; i++) { + temp = roothub_portstatus (controller, i); + /* check for a device disconnect */ + if (((temp & (RH_PS_PESC | RH_PS_CSC)) == + (RH_PS_PESC | RH_PS_CSC)) && + ((temp & RH_PS_CCS) == 0)) { + res = i; + break; + } + } + return res; +} + +static int ohci_submit_rh_msg(struct usb_device *dev, unsigned long pipe, + void *buffer, int transfer_len, struct devrequest *cmd) +{ + void * data = buffer; + int leni = transfer_len; + int len = 0; + int stat = 0; + __u32 datab[4]; + __u8 *data_buf = (__u8 *)datab; + __u16 bmRType_bReq; + __u16 wValue; + __u16 wIndex; + __u16 wLength; + +#ifdef DEBUG +urb_priv.actual_length = 0; +pkt_print(dev, pipe, buffer, transfer_len, cmd, "SUB(rh)", usb_pipein(pipe)); +#else + wait_ms(1); +#endif + if ((pipe & PIPE_INTERRUPT) == PIPE_INTERRUPT) { + info("Root-Hub submit IRQ: NOT implemented"); + return 0; + } + + bmRType_bReq = cmd->requesttype | (cmd->request << 8); + wValue = m16_swap (cmd->value); + wIndex = m16_swap (cmd->index); + wLength = m16_swap (cmd->length); + + info("Root-Hub: adr: %2x cmd(%1x): %08x %04x %04x %04x", + dev->devnum, 8, bmRType_bReq, wValue, wIndex, wLength); + + switch (bmRType_bReq) { + /* Request Destination: + without flags: Device, + RH_INTERFACE: interface, + RH_ENDPOINT: endpoint, + RH_CLASS means HUB here, + RH_OTHER | RH_CLASS almost ever means HUB_PORT here + */ + + case RH_GET_STATUS: + *(__u16 *) data_buf = m16_swap (1); OK (2); + case RH_GET_STATUS | RH_INTERFACE: + *(__u16 *) data_buf = m16_swap (0); OK (2); + case RH_GET_STATUS | RH_ENDPOINT: + *(__u16 *) data_buf = m16_swap (0); OK (2); + case RH_GET_STATUS | RH_CLASS: + *(__u32 *) data_buf = m32_swap ( + RD_RH_STAT & ~(RH_HS_CRWE | RH_HS_DRWE)); + OK (4); + case RH_GET_STATUS | RH_OTHER | RH_CLASS: + *(__u32 *) data_buf = m32_swap (RD_RH_PORTSTAT); OK (4); + + case RH_CLEAR_FEATURE | RH_ENDPOINT: + switch (wValue) { + case (RH_ENDPOINT_STALL): OK (0); + } + break; + + case RH_CLEAR_FEATURE | RH_CLASS: + switch (wValue) { + case RH_C_HUB_LOCAL_POWER: + OK(0); + case (RH_C_HUB_OVER_CURRENT): + WR_RH_STAT(RH_HS_OCIC); OK (0); + } + break; + + case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS: + switch (wValue) { + case (RH_PORT_ENABLE): + WR_RH_PORTSTAT (RH_PS_CCS ); OK (0); + case (RH_PORT_SUSPEND): + WR_RH_PORTSTAT (RH_PS_POCI); OK (0); + case (RH_PORT_POWER): + WR_RH_PORTSTAT (RH_PS_LSDA); OK (0); + case (RH_C_PORT_CONNECTION): + WR_RH_PORTSTAT (RH_PS_CSC ); OK (0); + case (RH_C_PORT_ENABLE): + WR_RH_PORTSTAT (RH_PS_PESC); OK (0); + case (RH_C_PORT_SUSPEND): + WR_RH_PORTSTAT (RH_PS_PSSC); OK (0); + case (RH_C_PORT_OVER_CURRENT): + WR_RH_PORTSTAT (RH_PS_OCIC); OK (0); + case (RH_C_PORT_RESET): + WR_RH_PORTSTAT (RH_PS_PRSC); OK (0); + } + break; + + case RH_SET_FEATURE | RH_OTHER | RH_CLASS: + switch (wValue) { + case (RH_PORT_SUSPEND): + WR_RH_PORTSTAT (RH_PS_PSS ); OK (0); + case (RH_PORT_RESET): /* BUG IN HUP CODE *********/ + if (RD_RH_PORTSTAT & RH_PS_CCS) + WR_RH_PORTSTAT (RH_PS_PRS); + OK (0); + case (RH_PORT_POWER): + WR_RH_PORTSTAT (RH_PS_PPS ); OK (0); + case (RH_PORT_ENABLE): /* BUG IN HUP CODE *********/ + if (RD_RH_PORTSTAT & RH_PS_CCS) + WR_RH_PORTSTAT (RH_PS_PES ); + OK (0); + } + break; + + case RH_SET_ADDRESS: gohci.rh.devnum = wValue; OK(0); + + case RH_GET_DESCRIPTOR: + switch ((wValue & 0xff00) >> 8) { + case (0x01): /* device descriptor */ + len = min_t(unsigned int, + leni, + min_t(unsigned int, + sizeof (root_hub_dev_des), + wLength)); + data_buf = root_hub_dev_des; OK(len); + case (0x02): /* configuration descriptor */ + len = min_t(unsigned int, + leni, + min_t(unsigned int, + sizeof (root_hub_config_des), + wLength)); + data_buf = root_hub_config_des; OK(len); + case (0x03): /* string descriptors */ + if(wValue==0x0300) { + len = min_t(unsigned int, + leni, + min_t(unsigned int, + sizeof (root_hub_str_index0), + wLength)); + data_buf = root_hub_str_index0; + OK(len); + } + if(wValue==0x0301) { + len = min_t(unsigned int, + leni, + min_t(unsigned int, + sizeof (root_hub_str_index1), + wLength)); + data_buf = root_hub_str_index1; + OK(len); + } + default: + stat = USB_ST_STALLED; + } + break; + + case RH_GET_DESCRIPTOR | RH_CLASS: + { + __u32 temp = roothub_a (&gohci); + + data_buf [0] = 9; /* min length; */ + data_buf [1] = 0x29; + data_buf [2] = temp & RH_A_NDP; +#ifdef CONFIG_AT91C_PQFP_UHPBUG + data_buf [2] = (data_buf [2] == 2) ? 1:0; +#endif +#if 0 /* def CONFIG_CPU_MONAHANS */ + data_buf [2] = (data_buf [2] == 2) ? 3:0; +#endif + + data_buf [3] = 0; + if (temp & RH_A_PSM) /* per-port power switching? */ + data_buf [3] |= 0x1; + if (temp & RH_A_NOCP) /* no overcurrent reporting? */ + data_buf [3] |= 0x10; +#if 1 + else if (temp & RH_A_OCPM) /* per-port overcurrent reporting? */ + data_buf [3] |= 0x8; +#endif + + /* corresponds to data_buf[4-7] */ + datab [1] = 0; + data_buf [5] = (temp & RH_A_POTPGT) >> 24; + temp = roothub_b (&gohci); + data_buf [7] = temp & RH_B_DR; + if (data_buf [2] < 7) { + data_buf [8] = 0xff; + } else { + data_buf [0] += 2; + data_buf [8] = (temp & RH_B_DR) >> 8; + data_buf [10] = data_buf [9] = 0xff; + } + + len = min_t(unsigned int, leni, + min_t(unsigned int, data_buf [0], wLength)); + OK (len); + } + + case RH_GET_CONFIGURATION: *(__u8 *) data_buf = 0x01; OK (1); + + case RH_SET_CONFIGURATION: WR_RH_STAT (0x10000); OK (0); + + default: + dbg ("unsupported root hub command"); + stat = USB_ST_STALLED; + } + +#ifdef DEBUG + ohci_dump_roothub (&gohci, 1); +#else + wait_ms(1); +#endif + + len = min_t(int, len, leni); + if (data != data_buf) + memcpy (data, data_buf, len); + dev->act_len = len; + dev->status = stat; + +#ifdef DEBUG + if (transfer_len) + urb_priv.actual_length = transfer_len; + pkt_print(dev, pipe, buffer, transfer_len, cmd, "RET(rh)", 0/*usb_pipein(pipe)*/); +#else + wait_ms(1); +#endif + + return stat; +} + +/*-------------------------------------------------------------------------*/ + +/* common code for handling submit messages - used for all but root hub */ +/* accesses. */ +int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int transfer_len, struct devrequest *setup, int interval) +{ + int stat = 0; + int maxsize = usb_maxpacket(dev, pipe); + int timeout; + + /* device pulled? Shortcut the action. */ + if (devgone == dev) { + dev->status = USB_ST_CRC_ERR; + return 0; + } + +#ifdef DEBUG + urb_priv.actual_length = 0; + pkt_print(dev, pipe, buffer, transfer_len, setup, "SUB", usb_pipein(pipe)); +#else + wait_ms(1); +#endif + if (!maxsize) { + err("submit_common_message: pipesize for pipe %lx is zero", + pipe); + return -1; + } + + if (sohci_submit_job(dev, pipe, buffer, transfer_len, setup, interval) < 0) { + err("sohci_submit_job failed"); + return -1; + } + + wait_ms(10); + /* ohci_dump_status(&gohci); */ + + /* allow more time for a BULK device to react - some are slow */ +#define BULK_TO 5000 /* timeout in milliseconds */ + if (usb_pipetype (pipe) == PIPE_BULK) + timeout = BULK_TO; + else + timeout = 100; + + /* wait for it to complete */ + for (;;) { + /* check whether the controller is done */ + stat = hc_interrupt(); + if (stat < 0) { + stat = USB_ST_CRC_ERR; + break; + } + if (stat >= 0 && stat != 0xff) { + /* 0xff is returned for an SF-interrupt */ + break; + } + if (--timeout) { + wait_ms(1); + } else { + err("CTL:TIMEOUT "); + stat = USB_ST_CRC_ERR; + break; + } + } + /* we got an Root Hub Status Change interrupt */ + if (got_rhsc) { +#ifdef DEBUG + ohci_dump_roothub (&gohci, 1); +#endif + got_rhsc = 0; + /* abuse timeout */ + timeout = rh_check_port_status(&gohci); + if (timeout >= 0) { +#if 0 /* this does nothing useful, but leave it here in case that changes */ + /* the called routine adds 1 to the passed value */ + usb_hub_port_connect_change(gohci.rh.dev, timeout - 1); +#endif + /* + * XXX + * This is potentially dangerous because it assumes + * that only one device is ever plugged in! + */ + devgone = dev; + } + } + + dev->status = stat; + dev->act_len = transfer_len; + +#ifdef DEBUG + pkt_print(dev, pipe, buffer, transfer_len, setup, "RET(ctlr)", usb_pipein(pipe)); +#else + wait_ms(1); +#endif + + /* free TDs in urb_priv */ + urb_free_priv (&urb_priv); + return 0; +} + +/* submit routines called from usb.c */ +int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int transfer_len) +{ + info("submit_bulk_msg"); + return submit_common_msg(dev, pipe, buffer, transfer_len, NULL, 0); +} + +int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int transfer_len, struct devrequest *setup) +{ + int maxsize = usb_maxpacket(dev, pipe); + + info("submit_control_msg"); +#ifdef DEBUG + urb_priv.actual_length = 0; + pkt_print(dev, pipe, buffer, transfer_len, setup, "SUB", usb_pipein(pipe)); +#else + wait_ms(1); +#endif + if (!maxsize) { + err("submit_control_message: pipesize for pipe %lx is zero", + pipe); + return -1; + } + if (((pipe >> 8) & 0x7f) == gohci.rh.devnum) { + gohci.rh.dev = dev; + /* root hub - redirect */ + return ohci_submit_rh_msg(dev, pipe, buffer, transfer_len, + setup); + } + + return submit_common_msg(dev, pipe, buffer, transfer_len, setup, 0); +} + +int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int transfer_len, int interval) +{ + info("submit_int_msg"); + return -1; +} + +/*-------------------------------------------------------------------------* + * HC functions + *-------------------------------------------------------------------------*/ + +/* reset the HC and BUS */ + +static int hc_reset (ohci_t *ohci) +{ + int timeout = 30; + int smm_timeout = 50; /* 0,5 sec */ + + dbg("%s\n", __FUNCTION__); + + if (readl (&ohci->regs->control) & OHCI_CTRL_IR) { /* SMM owns the HC */ + writel (OHCI_OCR, &ohci->regs->cmdstatus); /* request ownership */ + info("USB HC TakeOver from SMM"); + while (readl (&ohci->regs->control) & OHCI_CTRL_IR) { + wait_ms (10); + if (--smm_timeout == 0) { + err("USB HC TakeOver failed!"); + return -1; + } + } + } + + /* Disable HC interrupts */ + writel (OHCI_INTR_MIE, &ohci->regs->intrdisable); + + dbg("USB HC reset_hc usb-%s: ctrl = 0x%X ;\n", + ohci->slot_name, + readl(&ohci->regs->control)); + + /* Reset USB (needed by some controllers) */ + writel (0, &ohci->regs->control); + + /* HC Reset requires max 10 us delay */ + writel (OHCI_HCR, &ohci->regs->cmdstatus); + while ((readl (&ohci->regs->cmdstatus) & OHCI_HCR) != 0) { + if (--timeout == 0) { + err("USB HC reset timed out!"); + return -1; + } + udelay (1); + } + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* Start an OHCI controller, set the BUS operational + * enable interrupts + * connect the virtual root hub */ + +static int hc_start (ohci_t * ohci) +{ + __u32 mask; + unsigned int fminterval; + + ohci->disabled = 1; + + /* Tell the controller where the control and bulk lists are + * The lists are empty now. */ + + writel (0, &ohci->regs->ed_controlhead); + writel (0, &ohci->regs->ed_bulkhead); + + writel ((__u32)ohci->hcca, &ohci->regs->hcca); /* a reset clears this */ + + fminterval = 0x2edf; + writel ((fminterval * 9) / 10, &ohci->regs->periodicstart); + fminterval |= ((((fminterval - 210) * 6) / 7) << 16); + writel (fminterval, &ohci->regs->fminterval); + writel (0x628, &ohci->regs->lsthresh); + + /* start controller operations */ + ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER; + ohci->disabled = 0; + writel (ohci->hc_control, &ohci->regs->control); + + /* disable all interrupts */ + mask = (OHCI_INTR_SO | OHCI_INTR_WDH | OHCI_INTR_SF | OHCI_INTR_RD | + OHCI_INTR_UE | OHCI_INTR_FNO | OHCI_INTR_RHSC | + OHCI_INTR_OC | OHCI_INTR_MIE); + writel (mask, &ohci->regs->intrdisable); + /* clear all interrupts */ + mask &= ~OHCI_INTR_MIE; + writel (mask, &ohci->regs->intrstatus); + /* Choose the interrupts we care about now - but w/o MIE */ + mask = OHCI_INTR_RHSC | OHCI_INTR_UE | OHCI_INTR_WDH | OHCI_INTR_SO; + writel (mask, &ohci->regs->intrenable); + +#ifdef OHCI_USE_NPS + /* required for AMD-756 and some Mac platforms */ + writel ((roothub_a (ohci) | RH_A_NPS) & ~RH_A_PSM, + &ohci->regs->roothub.a); + writel (RH_HS_LPSC, &ohci->regs->roothub.status); +#endif /* OHCI_USE_NPS */ + +#define mdelay(n) ({unsigned long msec=(n); while (msec--) udelay(1000);}) + /* POTPGT delay is bits 24-31, in 2 ms units. */ + mdelay ((roothub_a (ohci) >> 23) & 0x1fe); + + /* connect the virtual root hub */ + ohci->rh.devnum = 0; + + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* an interrupt happens */ + +static int +hc_interrupt (void) +{ + ohci_t *ohci = &gohci; + struct ohci_regs *regs = ohci->regs; + int ints; + int stat = -1; + + if ((ohci->hcca->done_head != 0) && !(m32_swap (ohci->hcca->done_head) & 0x01)) { + ints = OHCI_INTR_WDH; + } else { + ints = readl (®s->intrstatus); + } + + /* dbg("Interrupt: %x frame: %x", ints, le16_to_cpu (ohci->hcca->frame_no)); */ + + if (ints & OHCI_INTR_RHSC) { + got_rhsc = 1; + } + + if (ints & OHCI_INTR_UE) { + ohci->disabled++; + err ("OHCI Unrecoverable Error, controller usb-%s disabled", + ohci->slot_name); + /* e.g. due to PCI Master/Target Abort */ + +#ifdef DEBUG + ohci_dump (ohci, 1); +#else + wait_ms(1); +#endif + /* FIXME: be optimistic, hope that bug won't repeat often. */ + /* Make some non-interrupt context restart the controller. */ + /* Count and limit the retries though; either hardware or */ + /* software errors can go forever... */ + hc_reset (ohci); + return -1; + } + + if (ints & OHCI_INTR_WDH) { + wait_ms(1); + writel (OHCI_INTR_WDH, ®s->intrdisable); + stat = dl_done_list (&gohci, dl_reverse_done_list (&gohci)); + writel (OHCI_INTR_WDH, ®s->intrenable); + } + + if (ints & OHCI_INTR_SO) { + dbg("USB Schedule overrun\n"); + writel (OHCI_INTR_SO, ®s->intrenable); + stat = -1; + } + + /* FIXME: this assumes SOF (1/ms) interrupts don't get lost... */ + if (ints & OHCI_INTR_SF) { + unsigned int frame = m16_swap (ohci->hcca->frame_no) & 1; + wait_ms(1); + writel (OHCI_INTR_SF, ®s->intrdisable); + if (ohci->ed_rm_list[frame] != NULL) + writel (OHCI_INTR_SF, ®s->intrenable); + stat = 0xff; + } + + writel (ints, ®s->intrstatus); + return stat; +} + +/*-------------------------------------------------------------------------*/ + +/*-------------------------------------------------------------------------*/ + +/* De-allocate all resources.. */ + +static void hc_release_ohci (ohci_t *ohci) +{ + dbg ("USB HC release ohci usb-%s", ohci->slot_name); + + if (!ohci->disabled) + hc_reset (ohci); +} + +/*-------------------------------------------------------------------------*/ + +/* + * low level initalisation routine, called from usb.c + */ +static char ohci_inited = 0; + +int usb_lowlevel_init(void) +{ + /* do board dependant init */ + if(usb_board_init()) + return -1; + + memset (&gohci, 0, sizeof (ohci_t)); + memset (&urb_priv, 0, sizeof (urb_priv_t)); + + /* align the storage */ + if ((__u32)&ghcca[0] & 0xff) { + err("HCCA not aligned!!"); + return -1; + } + phcca = &ghcca[0]; + info("aligned ghcca %p", phcca); + memset(&ohci_dev, 0, sizeof(struct ohci_device)); + if ((__u32)&ohci_dev.ed[0] & 0x7) { + err("EDs not aligned!!"); + return -1; + } + memset(gtd, 0, sizeof(td_t) * (NUM_TD + 1)); + if ((__u32)gtd & 0x7) { + err("TDs not aligned!!"); + return -1; + } + ptd = gtd; + gohci.hcca = phcca; + memset (phcca, 0, sizeof (struct ohci_hcca)); + + gohci.disabled = 1; + gohci.sleeping = 0; + gohci.irq = -1; + gohci.regs = (struct ohci_regs *)OHCI_REGS_BASE; + + gohci.flags = 0; + gohci.slot_name = "delta/zylonite"; + + if (hc_reset (&gohci) < 0) { + hc_release_ohci (&gohci); + err ("can't reset usb-%s", gohci.slot_name); + /* Initialization failed disable clocks */ + CKENA &= ~(CKENA_2_USBHOST | CKENA_20_UDC); + return -1; + } + + /* FIXME this is a second HC reset; why?? */ + /* writel(gohci.hc_control = OHCI_USB_RESET, &gohci.regs->control); + wait_ms(10); */ + + if (hc_start (&gohci) < 0) { + err ("can't start usb-%s", gohci.slot_name); + hc_release_ohci (&gohci); + /* Initialization failed */ + CKENA &= ~(CKENA_2_USBHOST | CKENA_20_UDC); + return -1; + } + +#ifdef DEBUG + ohci_dump (&gohci, 1); +#else + wait_ms(1); +#endif + ohci_inited = 1; + return 0; +} + +int usb_lowlevel_stop(void) +{ + /* this gets called really early - before the controller has */ + /* even been initialized! */ + if (!ohci_inited) + return 0; + /* TODO release any interrupts, etc. */ + /* call hc_release_ohci() here ? */ + hc_reset (&gohci); + + /* board dependant cleanup */ + if(usb_board_stop()) + return -1; + + return 0; +} + +#endif /* CONFIG_USB_OHCI */ diff --git a/drivers/usb_ohci.h b/drivers/usb_ohci.h new file mode 100644 index 000000000..7a1d9d9cc --- /dev/null +++ b/drivers/usb_ohci.h @@ -0,0 +1,422 @@ +/* + * URB OHCI HCD (Host Controller Driver) for USB. + * + * (C) Copyright 1999 Roman Weissgaerber + * (C) Copyright 2000-2001 David Brownell + * + * usb-ohci.h + */ + +/* functions for doing board specific setup/cleanup */ +extern int usb_board_init(void); +extern int usb_board_stop(void); + +static int cc_to_error[16] = { + +/* mapping of the OHCI CC status to error codes */ + /* No Error */ 0, + /* CRC Error */ USB_ST_CRC_ERR, + /* Bit Stuff */ USB_ST_BIT_ERR, + /* Data Togg */ USB_ST_CRC_ERR, + /* Stall */ USB_ST_STALLED, + /* DevNotResp */ -1, + /* PIDCheck */ USB_ST_BIT_ERR, + /* UnExpPID */ USB_ST_BIT_ERR, + /* DataOver */ USB_ST_BUF_ERR, + /* DataUnder */ USB_ST_BUF_ERR, + /* reservd */ -1, + /* reservd */ -1, + /* BufferOver */ USB_ST_BUF_ERR, + /* BuffUnder */ USB_ST_BUF_ERR, + /* Not Access */ -1, + /* Not Access */ -1 +}; + +/* ED States */ + +#define ED_NEW 0x00 +#define ED_UNLINK 0x01 +#define ED_OPER 0x02 +#define ED_DEL 0x04 +#define ED_URB_DEL 0x08 + +/* usb_ohci_ed */ +struct ed { + __u32 hwINFO; + __u32 hwTailP; + __u32 hwHeadP; + __u32 hwNextED; + + struct ed *ed_prev; + __u8 int_period; + __u8 int_branch; + __u8 int_load; + __u8 int_interval; + __u8 state; + __u8 type; + __u16 last_iso; + struct ed *ed_rm_list; + + struct usb_device *usb_dev; + __u32 unused[3]; +} __attribute((aligned(16))); +typedef struct ed ed_t; + + +/* TD info field */ +#define TD_CC 0xf0000000 +#define TD_CC_GET(td_p) ((td_p >>28) & 0x0f) +#define TD_CC_SET(td_p, cc) (td_p) = ((td_p) & 0x0fffffff) | (((cc) & 0x0f) << 28) +#define TD_EC 0x0C000000 +#define TD_T 0x03000000 +#define TD_T_DATA0 0x02000000 +#define TD_T_DATA1 0x03000000 +#define TD_T_TOGGLE 0x00000000 +#define TD_R 0x00040000 +#define TD_DI 0x00E00000 +#define TD_DI_SET(X) (((X) & 0x07)<< 21) +#define TD_DP 0x00180000 +#define TD_DP_SETUP 0x00000000 +#define TD_DP_IN 0x00100000 +#define TD_DP_OUT 0x00080000 + +#define TD_ISO 0x00010000 +#define TD_DEL 0x00020000 + +/* CC Codes */ +#define TD_CC_NOERROR 0x00 +#define TD_CC_CRC 0x01 +#define TD_CC_BITSTUFFING 0x02 +#define TD_CC_DATATOGGLEM 0x03 +#define TD_CC_STALL 0x04 +#define TD_DEVNOTRESP 0x05 +#define TD_PIDCHECKFAIL 0x06 +#define TD_UNEXPECTEDPID 0x07 +#define TD_DATAOVERRUN 0x08 +#define TD_DATAUNDERRUN 0x09 +#define TD_BUFFEROVERRUN 0x0C +#define TD_BUFFERUNDERRUN 0x0D +#define TD_NOTACCESSED 0x0F + + +#define MAXPSW 1 + +struct td { + __u32 hwINFO; + __u32 hwCBP; /* Current Buffer Pointer */ + __u32 hwNextTD; /* Next TD Pointer */ + __u32 hwBE; /* Memory Buffer End Pointer */ + + __u16 hwPSW[MAXPSW]; + __u8 unused; + __u8 index; + struct ed *ed; + struct td *next_dl_td; + struct usb_device *usb_dev; + int transfer_len; + __u32 data; + + __u32 unused2[2]; +} __attribute((aligned(32))); +typedef struct td td_t; + +#define OHCI_ED_SKIP (1 << 14) + +/* + * The HCCA (Host Controller Communications Area) is a 256 byte + * structure defined in the OHCI spec. that the host controller is + * told the base address of. It must be 256-byte aligned. + */ + +#define NUM_INTS 32 /* part of the OHCI standard */ +struct ohci_hcca { + __u32 int_table[NUM_INTS]; /* Interrupt ED table */ + __u16 frame_no; /* current frame number */ + __u16 pad1; /* set to 0 on each frame_no change */ + __u32 done_head; /* info returned for an interrupt */ + u8 reserved_for_hc[116]; +} __attribute((aligned(256))); + + +/* + * Maximum number of root hub ports. + */ +#define MAX_ROOT_PORTS 3 /* maximum OHCI root hub ports */ + +/* + * This is the structure of the OHCI controller's memory mapped I/O + * region. This is Memory Mapped I/O. You must use the readl() and + * writel() macros defined in asm/io.h to access these!! + */ +struct ohci_regs { + /* control and status registers */ + __u32 revision; + __u32 control; + __u32 cmdstatus; + __u32 intrstatus; + __u32 intrenable; + __u32 intrdisable; + /* memory pointers */ + __u32 hcca; + __u32 ed_periodcurrent; + __u32 ed_controlhead; + __u32 ed_controlcurrent; + __u32 ed_bulkhead; + __u32 ed_bulkcurrent; + __u32 donehead; + /* frame counters */ + __u32 fminterval; + __u32 fmremaining; + __u32 fmnumber; + __u32 periodicstart; + __u32 lsthresh; + /* Root hub ports */ + struct ohci_roothub_regs { + __u32 a; + __u32 b; + __u32 status; + __u32 portstatus[MAX_ROOT_PORTS]; + } roothub; +} __attribute((aligned(32))); + + +/* OHCI CONTROL AND STATUS REGISTER MASKS */ + +/* + * HcControl (control) register masks + */ +#define OHCI_CTRL_CBSR (3 << 0) /* control/bulk service ratio */ +#define OHCI_CTRL_PLE (1 << 2) /* periodic list enable */ +#define OHCI_CTRL_IE (1 << 3) /* isochronous enable */ +#define OHCI_CTRL_CLE (1 << 4) /* control list enable */ +#define OHCI_CTRL_BLE (1 << 5) /* bulk list enable */ +#define OHCI_CTRL_HCFS (3 << 6) /* host controller functional state */ +#define OHCI_CTRL_IR (1 << 8) /* interrupt routing */ +#define OHCI_CTRL_RWC (1 << 9) /* remote wakeup connected */ +#define OHCI_CTRL_RWE (1 << 10) /* remote wakeup enable */ + +/* pre-shifted values for HCFS */ +# define OHCI_USB_RESET (0 << 6) +# define OHCI_USB_RESUME (1 << 6) +# define OHCI_USB_OPER (2 << 6) +# define OHCI_USB_SUSPEND (3 << 6) + +/* + * HcCommandStatus (cmdstatus) register masks + */ +#define OHCI_HCR (1 << 0) /* host controller reset */ +#define OHCI_CLF (1 << 1) /* control list filled */ +#define OHCI_BLF (1 << 2) /* bulk list filled */ +#define OHCI_OCR (1 << 3) /* ownership change request */ +#define OHCI_SOC (3 << 16) /* scheduling overrun count */ + +/* + * masks used with interrupt registers: + * HcInterruptStatus (intrstatus) + * HcInterruptEnable (intrenable) + * HcInterruptDisable (intrdisable) + */ +#define OHCI_INTR_SO (1 << 0) /* scheduling overrun */ +#define OHCI_INTR_WDH (1 << 1) /* writeback of done_head */ +#define OHCI_INTR_SF (1 << 2) /* start frame */ +#define OHCI_INTR_RD (1 << 3) /* resume detect */ +#define OHCI_INTR_UE (1 << 4) /* unrecoverable error */ +#define OHCI_INTR_FNO (1 << 5) /* frame number overflow */ +#define OHCI_INTR_RHSC (1 << 6) /* root hub status change */ +#define OHCI_INTR_OC (1 << 30) /* ownership change */ +#define OHCI_INTR_MIE (1 << 31) /* master interrupt enable */ + + +/* Virtual Root HUB */ +struct virt_root_hub { + int devnum; /* Address of Root Hub endpoint */ + void *dev; /* was urb */ + void *int_addr; + int send; + int interval; +}; + +/* USB HUB CONSTANTS (not OHCI-specific; see hub.h) */ + +/* destination of request */ +#define RH_INTERFACE 0x01 +#define RH_ENDPOINT 0x02 +#define RH_OTHER 0x03 + +#define RH_CLASS 0x20 +#define RH_VENDOR 0x40 + +/* Requests: bRequest << 8 | bmRequestType */ +#define RH_GET_STATUS 0x0080 +#define RH_CLEAR_FEATURE 0x0100 +#define RH_SET_FEATURE 0x0300 +#define RH_SET_ADDRESS 0x0500 +#define RH_GET_DESCRIPTOR 0x0680 +#define RH_SET_DESCRIPTOR 0x0700 +#define RH_GET_CONFIGURATION 0x0880 +#define RH_SET_CONFIGURATION 0x0900 +#define RH_GET_STATE 0x0280 +#define RH_GET_INTERFACE 0x0A80 +#define RH_SET_INTERFACE 0x0B00 +#define RH_SYNC_FRAME 0x0C80 +/* Our Vendor Specific Request */ +#define RH_SET_EP 0x2000 + + +/* Hub port features */ +#define RH_PORT_CONNECTION 0x00 +#define RH_PORT_ENABLE 0x01 +#define RH_PORT_SUSPEND 0x02 +#define RH_PORT_OVER_CURRENT 0x03 +#define RH_PORT_RESET 0x04 +#define RH_PORT_POWER 0x08 +#define RH_PORT_LOW_SPEED 0x09 + +#define RH_C_PORT_CONNECTION 0x10 +#define RH_C_PORT_ENABLE 0x11 +#define RH_C_PORT_SUSPEND 0x12 +#define RH_C_PORT_OVER_CURRENT 0x13 +#define RH_C_PORT_RESET 0x14 + +/* Hub features */ +#define RH_C_HUB_LOCAL_POWER 0x00 +#define RH_C_HUB_OVER_CURRENT 0x01 + +#define RH_DEVICE_REMOTE_WAKEUP 0x00 +#define RH_ENDPOINT_STALL 0x01 + +#define RH_ACK 0x01 +#define RH_REQ_ERR -1 +#define RH_NACK 0x00 + + +/* OHCI ROOT HUB REGISTER MASKS */ + +/* roothub.portstatus [i] bits */ +#define RH_PS_CCS 0x00000001 /* current connect status */ +#define RH_PS_PES 0x00000002 /* port enable status*/ +#define RH_PS_PSS 0x00000004 /* port suspend status */ +#define RH_PS_POCI 0x00000008 /* port over current indicator */ +#define RH_PS_PRS 0x00000010 /* port reset status */ +#define RH_PS_PPS 0x00000100 /* port power status */ +#define RH_PS_LSDA 0x00000200 /* low speed device attached */ +#define RH_PS_CSC 0x00010000 /* connect status change */ +#define RH_PS_PESC 0x00020000 /* port enable status change */ +#define RH_PS_PSSC 0x00040000 /* port suspend status change */ +#define RH_PS_OCIC 0x00080000 /* over current indicator change */ +#define RH_PS_PRSC 0x00100000 /* port reset status change */ + +/* roothub.status bits */ +#define RH_HS_LPS 0x00000001 /* local power status */ +#define RH_HS_OCI 0x00000002 /* over current indicator */ +#define RH_HS_DRWE 0x00008000 /* device remote wakeup enable */ +#define RH_HS_LPSC 0x00010000 /* local power status change */ +#define RH_HS_OCIC 0x00020000 /* over current indicator change */ +#define RH_HS_CRWE 0x80000000 /* clear remote wakeup enable */ + +/* roothub.b masks */ +#define RH_B_DR 0x0000ffff /* device removable flags */ +#define RH_B_PPCM 0xffff0000 /* port power control mask */ + +/* roothub.a masks */ +#define RH_A_NDP (0xff << 0) /* number of downstream ports */ +#define RH_A_PSM (1 << 8) /* power switching mode */ +#define RH_A_NPS (1 << 9) /* no power switching */ +#define RH_A_DT (1 << 10) /* device type (mbz) */ +#define RH_A_OCPM (1 << 11) /* over current protection mode */ +#define RH_A_NOCP (1 << 12) /* no over current protection */ +#define RH_A_POTPGT (0xff << 24) /* power on to power good time */ + +/* urb */ +#define N_URB_TD 48 +typedef struct +{ + ed_t *ed; + __u16 length; /* number of tds associated with this request */ + __u16 td_cnt; /* number of tds already serviced */ + int state; + unsigned long pipe; + int actual_length; + td_t *td[N_URB_TD]; /* list pointer to all corresponding TDs associated with this request */ +} urb_priv_t; +#define URB_DEL 1 + +/* + * This is the full ohci controller description + * + * Note how the "proper" USB information is just + * a subset of what the full implementation needs. (Linus) + */ + + +typedef struct ohci { + struct ohci_hcca *hcca; /* hcca */ + /*dma_addr_t hcca_dma;*/ + + int irq; + int disabled; /* e.g. got a UE, we're hung */ + int sleeping; + unsigned long flags; /* for HC bugs */ + + struct ohci_regs *regs; /* OHCI controller's memory */ + + ed_t *ed_rm_list[2]; /* lists of all endpoints to be removed */ + ed_t *ed_bulktail; /* last endpoint of bulk list */ + ed_t *ed_controltail; /* last endpoint of control list */ + int intrstatus; + __u32 hc_control; /* copy of the hc control reg */ + struct usb_device *dev[32]; + struct virt_root_hub rh; + + const char *slot_name; +} ohci_t; + +#define NUM_EDS 8 /* num of preallocated endpoint descriptors */ + +struct ohci_device { + ed_t ed[NUM_EDS]; + int ed_cnt; +}; + +/* hcd */ +/* endpoint */ +static int ep_link(ohci_t * ohci, ed_t * ed); +static int ep_unlink(ohci_t * ohci, ed_t * ed); +static ed_t * ep_add_ed(struct usb_device * usb_dev, unsigned long pipe); + +/*-------------------------------------------------------------------------*/ + +/* we need more TDs than EDs */ +#define NUM_TD 64 + +/* +1 so we can align the storage */ +td_t gtd[NUM_TD+1]; +/* pointers to aligned storage */ +td_t *ptd; + +/* TDs ... */ +static inline struct td * +td_alloc (struct usb_device *usb_dev) +{ + int i; + struct td *td; + + td = NULL; + for (i = 0; i < NUM_TD; i++) + { + if (ptd[i].usb_dev == NULL) + { + td = &ptd[i]; + td->usb_dev = usb_dev; + break; + } + } + + return td; +} + +static inline void +ed_free (struct ed *ed) +{ + ed->usb_dev = NULL; +} -- cgit v1.2.3 From 24e37645e7378b20fa8f20e2996c8fb8e90c70c9 Mon Sep 17 00:00:00 2001 From: Markus Klotzbuecher Date: Tue, 23 May 2006 10:33:11 +0200 Subject: More cleanup for the delta board and the generic usb_ohci driver. Added CFG_USB_BOARD_INIT and CFG_USB_CPU_INIT for enabling board and cpu specific initialization and cleanup hooks respectively. --- board/delta/delta.c | 55 ++------------------------------------ cpu/pxa/Makefile | 2 +- cpu/pxa/usb.c | 71 +++++++++++++++++++++++++++++++++++++++++++++++++ drivers/usb_ohci.c | 59 ++++++++++++++++++++++++++++------------ drivers/usb_ohci.h | 8 ++++++ include/configs/delta.h | 8 ++++-- 6 files changed, 130 insertions(+), 73 deletions(-) create mode 100644 cpu/pxa/usb.c (limited to 'drivers') diff --git a/board/delta/delta.c b/board/delta/delta.c index d9fe8cbf8..6e227748b 100644 --- a/board/delta/delta.c +++ b/board/delta/delta.c @@ -1,10 +1,6 @@ /* - * (C) Copyright 2002 - * Kyle Harris, Nexus Technologies, Inc. kharris@nexus-tech.net - * - * (C) Copyright 2002 - * Sysgo Real-Time Solutions, GmbH - * Marius Groeger + * (C) Copyright 2006 + * DENX Software Engineering * * See file CREDITS for list of people who contributed to this * project. @@ -98,53 +94,6 @@ int board_late_init(void) return 0; } - -/* board dependant usb stuff */ -int usb_board_init() -{ - /* - * Enable USB host clock. - */ - CKENA |= (CKENA_2_USBHOST | CKENA_20_UDC); - udelay(100); - - /* Configure Port 2 for Host (USB Client Registers) */ - UP2OCR = 0x3000c; - -#if 0 - GPIO2_2 = 0x801; /* USBHPEN - Alt. Fkt. 1 */ - GPIO3_2 = 0x801; /* USBHPWR - Alt. Fkt. 1 */ -#endif - - UHCHR |= UHCHR_FHR; - wait_ms(11); /* udelay(11); */ - UHCHR &= ~UHCHR_FHR; - - UHCHR |= UHCHR_FSBIR; - while (UHCHR & UHCHR_FSBIR) - udelay(1); - -#if 0 - UHCHR |= UHCHR_PCPL; /* USBHPEN is active low */ - UHCHR |= UHCHR_PSPL; /* USBHPWR is active low */ -#endif - - UHCHR &= ~UHCHR_SSEP0; - UHCHR &= ~UHCHR_SSEP1; - UHCHR &= ~UHCHR_SSE; - - return 0; -} - -int usb_board_stop() -{ - /* may not want to do this */ - /* CKENA &= ~(CKENA_2_USBHOST | CKENA_20_UDC); */ - - return 0; -} - - /* * Magic Key Handling, mainly copied from board/lwmon/lwmon.c */ diff --git a/cpu/pxa/Makefile b/cpu/pxa/Makefile index 1af53d6ad..bd476f150 100644 --- a/cpu/pxa/Makefile +++ b/cpu/pxa/Makefile @@ -26,7 +26,7 @@ include $(TOPDIR)/config.mk LIB = lib$(CPU).a START = start.o -OBJS = serial.o interrupts.o cpu.o i2c.o pxafb.o mmc.o +OBJS = serial.o interrupts.o cpu.o i2c.o pxafb.o mmc.o usb.o all: .depend $(START) $(LIB) diff --git a/cpu/pxa/usb.c b/cpu/pxa/usb.c new file mode 100644 index 000000000..e89391cf5 --- /dev/null +++ b/cpu/pxa/usb.c @@ -0,0 +1,71 @@ +/* + * (C) Copyright 2006 + * Markus Klotzbuecher, DENX Software Engineering + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 +#include + +#ifdef CFG_USB_CPU_INIT +# ifdef CONFIG_CPU_MONAHANS +int usb_cpu_init() +{ + /* Enable USB host clock. */ + CKENA |= (CKENA_2_USBHOST | CKENA_20_UDC); + udelay(100); + + /* Configure Port 2 for Host (USB Client Registers) */ + UP2OCR = 0x3000c; + +#if 0 + GPIO2_2 = 0x801; /* USBHPEN - Alt. Fkt. 1 */ + GPIO3_2 = 0x801; /* USBHPWR - Alt. Fkt. 1 */ +#endif + + UHCHR |= UHCHR_FHR; + wait_ms(11); + UHCHR &= ~UHCHR_FHR; + + UHCHR |= UHCHR_FSBIR; + while (UHCHR & UHCHR_FSBIR) + udelay(1); + +#if 0 + UHCHR |= UHCHR_PCPL; /* USBHPEN is active low */ + UHCHR |= UHCHR_PSPL; /* USBHPWR is active low */ +#endif + + UHCHR &= ~UHCHR_SSEP0; + UHCHR &= ~UHCHR_SSEP1; + UHCHR &= ~UHCHR_SSE; + + return 0; +} + +int usb_cpu_stop() +{ + /* may not want to do this */ + /* CKENA &= ~(CKENA_2_USBHOST | CKENA_20_UDC); */ + + return 0; +} +# endif /* CONFIG_CPU_MONAHANS */ +#endif /* CFG_USB_CPU_INIT */ diff --git a/drivers/usb_ohci.c b/drivers/usb_ohci.c index 4065489f3..f5af7192f 100644 --- a/drivers/usb_ohci.c +++ b/drivers/usb_ohci.c @@ -51,7 +51,10 @@ #include #include "usb_ohci.h" -/* #define OHCI_USE_NPS /\* force NoPowerSwitching mode *\/ */ +#ifdef CONFIG_ARM920T +# define OHCI_USE_NPS /* force NoPowerSwitching mode */ +#endif + #undef OHCI_VERBOSE_DEBUG /* not always helpful */ /* For initializing controller (mask in an HCFS mode too) */ @@ -310,9 +313,6 @@ static void ohci_dump_roothub (ohci_t *controller, int verbose) ndp = (temp & RH_A_NDP); #ifdef CONFIG_AT91C_PQFP_UHPBUG ndp = (ndp == 2) ? 1:0; -#endif -#if 0 /* def CONFIG_CPU_MONAHANS */ - data_buf [2] = (data_buf [2] == 2) ? 3:0; #endif if (verbose) { dbg ("roothub.a: %08x POTPGT=%d%s%s%s%s%s NDP=%d", temp, @@ -1150,19 +1150,13 @@ pkt_print(dev, pipe, buffer, transfer_len, cmd, "SUB(rh)", usb_pipein(pipe)); #ifdef CONFIG_AT91C_PQFP_UHPBUG data_buf [2] = (data_buf [2] == 2) ? 1:0; #endif -#if 0 /* def CONFIG_CPU_MONAHANS */ - data_buf [2] = (data_buf [2] == 2) ? 3:0; -#endif - data_buf [3] = 0; if (temp & RH_A_PSM) /* per-port power switching? */ data_buf [3] |= 0x1; if (temp & RH_A_NOCP) /* no overcurrent reporting? */ data_buf [3] |= 0x10; -#if 1 else if (temp & RH_A_OCPM) /* per-port overcurrent reporting? */ data_buf [3] |= 0x8; -#endif /* corresponds to data_buf[4-7] */ datab [1] = 0; @@ -1557,10 +1551,18 @@ static char ohci_inited = 0; int usb_lowlevel_init(void) { - /* do board dependant init */ - if(usb_board_init()) + +#if CFG_USB_CPU_INIT + /* cpu dependant init */ + if(usb_cpu_init()) return -1; +#endif +#if CFG_USB_BOARD_INIT + /* board dependant init */ + if(usb_board_init()) + return -1; +#endif memset (&gohci, 0, sizeof (ohci_t)); memset (&urb_priv, 0, sizeof (urb_priv_t)); @@ -1588,28 +1590,43 @@ int usb_lowlevel_init(void) gohci.disabled = 1; gohci.sleeping = 0; gohci.irq = -1; - gohci.regs = (struct ohci_regs *)OHCI_REGS_BASE; + gohci.regs = (struct ohci_regs *)CFG_USB_OHCI_REGS_BASE; gohci.flags = 0; - gohci.slot_name = "delta/zylonite"; + gohci.slot_name = CFG_USB_SLOT_NAME; if (hc_reset (&gohci) < 0) { hc_release_ohci (&gohci); err ("can't reset usb-%s", gohci.slot_name); /* Initialization failed disable clocks */ - CKENA &= ~(CKENA_2_USBHOST | CKENA_20_UDC); +#if CFG_USB_BOARD_INIT + /* board dependant cleanup */ + usb_board_stop(); +#endif + +#if CFG_USB_CPU_INIT + /* cpu dependant cleanup */ + usb_cpu_stop(); +#endif return -1; } /* FIXME this is a second HC reset; why?? */ /* writel(gohci.hc_control = OHCI_USB_RESET, &gohci.regs->control); wait_ms(10); */ - if (hc_start (&gohci) < 0) { err ("can't start usb-%s", gohci.slot_name); hc_release_ohci (&gohci); /* Initialization failed */ - CKENA &= ~(CKENA_2_USBHOST | CKENA_20_UDC); +#if CFG_USB_BOARD_INIT + /* board dependant cleanup */ + usb_board_stop(); +#endif + +#if CFG_USB_CPU_INIT + /* cpu dependant cleanup */ + usb_cpu_stop(); +#endif return -1; } @@ -1632,9 +1649,17 @@ int usb_lowlevel_stop(void) /* call hc_release_ohci() here ? */ hc_reset (&gohci); +#if CFG_USB_BOARD_INIT /* board dependant cleanup */ if(usb_board_stop()) return -1; +#endif + +#if CFG_USB_CPU_INIT + /* cpu dependant cleanup */ + if(usb_cpu_stop()) + return -1; +#endif return 0; } diff --git a/drivers/usb_ohci.h b/drivers/usb_ohci.h index 7a1d9d9cc..c37b5f600 100644 --- a/drivers/usb_ohci.h +++ b/drivers/usb_ohci.h @@ -8,8 +8,16 @@ */ /* functions for doing board specific setup/cleanup */ +#ifdef CFG_USB_BOARD_INIT extern int usb_board_init(void); extern int usb_board_stop(void); +#endif + +#ifdef CFG_USB_CPU_INIT +extern int usb_cpu_init(void); +extern int usb_cpu_stop(void); +#endif + static int cc_to_error[16] = { diff --git a/include/configs/delta.h b/include/configs/delta.h index 0423bcbdb..e6b05e059 100644 --- a/include/configs/delta.h +++ b/include/configs/delta.h @@ -102,13 +102,17 @@ | CFG_CMD_IMLS)) #endif - +/* USB */ #define CONFIG_USB_OHCI 1 #define CONFIG_USB_STORAGE 1 #define CONFIG_DOS_PARTITION 1 -#define LITTLEENDIAN 1 /* used by usb_ohci.c */ +#undef CFG_USB_BOARD_INIT +#define CFG_USB_CPU_INIT 1 +#define CFG_USB_OHCI_REGS_BASE OHCI_REGS_BASE +#define CFG_USB_SLOT_NAME "delta" +#define LITTLEENDIAN 1 /* used by usb_ohci.c */ /* this must be included AFTER the definition of CONFIG_COMMANDS (if any) */ #include -- cgit v1.2.3 From 301f1aa384d0edcae6a22fd9adb933ad71695ecc Mon Sep 17 00:00:00 2001 From: Markus Klotzbuecher Date: Tue, 23 May 2006 13:38:35 +0200 Subject: Changed the mp2usb (at91rm9200) board to use the generic OHCI driver. Some fixes to the latter. --- cpu/arm920t/at91rm9200/Makefile | 2 +- cpu/arm920t/at91rm9200/usb.c | 47 ++ cpu/arm920t/at91rm9200/usb_ohci.c | 1635 ------------------------------------- cpu/arm920t/at91rm9200/usb_ohci.h | 419 ---------- cpu/pxa/usb.c | 8 +- drivers/usb_ohci.c | 18 +- include/configs/delta.h | 6 +- include/configs/mp2usb.h | 11 +- 8 files changed, 73 insertions(+), 2073 deletions(-) create mode 100644 cpu/arm920t/at91rm9200/usb.c delete mode 100644 cpu/arm920t/at91rm9200/usb_ohci.c delete mode 100644 cpu/arm920t/at91rm9200/usb_ohci.h (limited to 'drivers') diff --git a/cpu/arm920t/at91rm9200/Makefile b/cpu/arm920t/at91rm9200/Makefile index aec9cb640..f9fccbc06 100644 --- a/cpu/arm920t/at91rm9200/Makefile +++ b/cpu/arm920t/at91rm9200/Makefile @@ -26,7 +26,7 @@ include $(TOPDIR)/config.mk LIB = lib$(SOC).a OBJS = bcm5221.o dm9161.o ether.o i2c.o interrupts.o \ - lxt972.o serial.o usb_ohci.o + lxt972.o serial.o usb.o SOBJS = lowlevel_init.o all: .depend $(LIB) diff --git a/cpu/arm920t/at91rm9200/usb.c b/cpu/arm920t/at91rm9200/usb.c new file mode 100644 index 000000000..7b83f23eb --- /dev/null +++ b/cpu/arm920t/at91rm9200/usb.c @@ -0,0 +1,47 @@ +/* + * (C) Copyright 2006 + * DENX Software Engineering + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 + +#if defined(CONFIG_USB_OHCI) && defined(CFG_USB_OHCI_CPU_INIT) +# ifdef CONFIG_AT91RM9200 + +#include + +int usb_cpu_init() +{ + /* Enable USB host clock. */ + *AT91C_PMC_SCER = AT91C_PMC_UHP; /* 48MHz clock enabled for UHP */ + *AT91C_PMC_PCER = 1 << AT91C_ID_UHP; /* Peripheral Clock Enable Register */ + return 0; +} + +int usb_cpu_stop() +{ + /* Initialization failed */ + *AT91C_PMC_PCDR = 1 << AT91C_ID_UHP; /* Peripheral Clock Disable Register */ + *AT91C_PMC_SCDR = AT91C_PMC_UHP; /* 48MHz clock disabled for UHP */ + return 0; +} +# endif /* CONFIG_AT91RM9200 */ +#endif /* defined(CONFIG_USB_OHCI) && defined(CFG_USB_OHCI_CPU_INIT) */ diff --git a/cpu/arm920t/at91rm9200/usb_ohci.c b/cpu/arm920t/at91rm9200/usb_ohci.c deleted file mode 100644 index 5b2c56cff..000000000 --- a/cpu/arm920t/at91rm9200/usb_ohci.c +++ /dev/null @@ -1,1635 +0,0 @@ -/* - * URB OHCI HCD (Host Controller Driver) for USB on the AT91RM9200. - * - * (C) Copyright 2003 - * Gary Jennejohn, DENX Software Engineering - * - * Note: Much of this code has been derived from Linux 2.4 - * (C) Copyright 1999 Roman Weissgaerber - * (C) Copyright 2000-2002 David Brownell - * - * Modified for the MP2USB by (C) Copyright 2005 Eric Benard - * ebenard@eukrea.com - based on s3c24x0's driver - * - * See file CREDITS for list of people who contributed to this - * project. - * - * 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 - * - */ -/* - * IMPORTANT NOTES - * 1 - you MUST define LITTLEENDIAN in the configuration file for the - * board or this driver will NOT work! - * 2 - this driver is intended for use with USB Mass Storage Devices - * (BBB) ONLY. There is NO support for Interrupt or Isochronous pipes! - * 3 - when running on a PQFP208 AT91RM9200, define CONFIG_AT91C_PQFP_UHPBUG - * to activate workaround for bug #41 or this driver will NOT work! - */ - -#include -/* #include no PCI on the S3C24X0 */ - -#ifdef CONFIG_USB_OHCI - -#include - -#include -#include -#include "usb_ohci.h" - -#define OHCI_USE_NPS /* force NoPowerSwitching mode */ -#undef OHCI_VERBOSE_DEBUG /* not always helpful */ - -/* For initializing controller (mask in an HCFS mode too) */ -#define OHCI_CONTROL_INIT \ - (OHCI_CTRL_CBSR & 0x3) | OHCI_CTRL_IE | OHCI_CTRL_PLE - -#define readl(a) (*((vu_long *)(a))) -#define writel(a, b) (*((vu_long *)(b)) = ((vu_long)a)) - -#define min_t(type,x,y) ({ type __x = (x); type __y = (y); __x < __y ? __x: __y; }) - -#undef DEBUG -#ifdef DEBUG -#define dbg(format, arg...) printf("DEBUG: " format "\n", ## arg) -#else -#define dbg(format, arg...) do {} while(0) -#endif /* DEBUG */ -#define err(format, arg...) printf("ERROR: " format "\n", ## arg) -#undef SHOW_INFO -#ifdef SHOW_INFO -#define info(format, arg...) printf("INFO: " format "\n", ## arg) -#else -#define info(format, arg...) do {} while(0) -#endif - -#define m16_swap(x) swap_16(x) -#define m32_swap(x) swap_32(x) - -/* global ohci_t */ -static ohci_t gohci; -/* this must be aligned to a 256 byte boundary */ -struct ohci_hcca ghcca[1]; -/* a pointer to the aligned storage */ -struct ohci_hcca *phcca; -/* this allocates EDs for all possible endpoints */ -struct ohci_device ohci_dev; -/* urb_priv */ -urb_priv_t urb_priv; -/* RHSC flag */ -int got_rhsc; -/* device which was disconnected */ -struct usb_device *devgone; - -/*-------------------------------------------------------------------------*/ - -/* AMD-756 (D2 rev) reports corrupt register contents in some cases. - * The erratum (#4) description is incorrect. AMD's workaround waits - * till some bits (mostly reserved) are clear; ok for all revs. - */ -#define OHCI_QUIRK_AMD756 0xabcd -#define read_roothub(hc, register, mask) ({ \ - u32 temp = readl (&hc->regs->roothub.register); \ - if (hc->flags & OHCI_QUIRK_AMD756) \ - while (temp & mask) \ - temp = readl (&hc->regs->roothub.register); \ - temp; }) - -static u32 roothub_a (struct ohci *hc) - { return read_roothub (hc, a, 0xfc0fe000); } -static inline u32 roothub_b (struct ohci *hc) - { return readl (&hc->regs->roothub.b); } -static inline u32 roothub_status (struct ohci *hc) - { return readl (&hc->regs->roothub.status); } -static u32 roothub_portstatus (struct ohci *hc, int i) - { return read_roothub (hc, portstatus [i], 0xffe0fce0); } - - -/* forward declaration */ -static int hc_interrupt (void); -static void -td_submit_job (struct usb_device * dev, unsigned long pipe, void * buffer, - int transfer_len, struct devrequest * setup, urb_priv_t * urb, int interval); - -/*-------------------------------------------------------------------------* - * URB support functions - *-------------------------------------------------------------------------*/ - -/* free HCD-private data associated with this URB */ - -static void urb_free_priv (urb_priv_t * urb) -{ - int i; - int last; - struct td * td; - - last = urb->length - 1; - if (last >= 0) { - for (i = 0; i <= last; i++) { - td = urb->td[i]; - if (td) { - td->usb_dev = NULL; - urb->td[i] = NULL; - } - } - } -} - -/*-------------------------------------------------------------------------*/ - -#ifdef DEBUG -static int sohci_get_current_frame_number (struct usb_device * dev); - -/* debug| print the main components of an URB - * small: 0) header + data packets 1) just header */ - -static void pkt_print (struct usb_device * dev, unsigned long pipe, void * buffer, - int transfer_len, struct devrequest * setup, char * str, int small) -{ - urb_priv_t * purb = &urb_priv; - - dbg("%s URB:[%4x] dev:%2d,ep:%2d-%c,type:%s,len:%d/%d stat:%#lx", - str, - sohci_get_current_frame_number (dev), - usb_pipedevice (pipe), - usb_pipeendpoint (pipe), - usb_pipeout (pipe)? 'O': 'I', - usb_pipetype (pipe) < 2? (usb_pipeint (pipe)? "INTR": "ISOC"): - (usb_pipecontrol (pipe)? "CTRL": "BULK"), - purb->actual_length, - transfer_len, dev->status); -#ifdef OHCI_VERBOSE_DEBUG - if (!small) { - int i, len; - - if (usb_pipecontrol (pipe)) { - printf (__FILE__ ": cmd(8):"); - for (i = 0; i < 8 ; i++) - printf (" %02x", ((__u8 *) setup) [i]); - printf ("\n"); - } - if (transfer_len > 0 && buffer) { - printf (__FILE__ ": data(%d/%d):", - purb->actual_length, - transfer_len); - len = usb_pipeout (pipe)? - transfer_len: purb->actual_length; - for (i = 0; i < 16 && i < len; i++) - printf (" %02x", ((__u8 *) buffer) [i]); - printf ("%s\n", i < len? "...": ""); - } - } -#endif -} - -/* just for debugging; prints non-empty branches of the int ed tree inclusive iso eds*/ -void ep_print_int_eds (ohci_t *ohci, char * str) { - int i, j; - __u32 * ed_p; - for (i= 0; i < 32; i++) { - j = 5; - ed_p = &(ohci->hcca->int_table [i]); - if (*ed_p == 0) - continue; - printf (__FILE__ ": %s branch int %2d(%2x):", str, i, i); - while (*ed_p != 0 && j--) { - ed_t *ed = (ed_t *)m32_swap(ed_p); - printf (" ed: %4x;", ed->hwINFO); - ed_p = &ed->hwNextED; - } - printf ("\n"); - } -} - -static void ohci_dump_intr_mask (char *label, __u32 mask) -{ - dbg ("%s: 0x%08x%s%s%s%s%s%s%s%s%s", - 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 maybe_print_eds (char *label, __u32 value) -{ - ed_t *edp = (ed_t *)value; - - if (value) { - dbg ("%s %08x", label, value); - dbg ("%08x", edp->hwINFO); - dbg ("%08x", edp->hwTailP); - dbg ("%08x", edp->hwHeadP); - dbg ("%08x", edp->hwNextED); - } -} - -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 *controller) -{ - struct ohci_regs *regs = controller->regs; - __u32 temp; - - temp = readl (®s->revision) & 0xff; - if (temp != 0x10) - dbg ("spec %d.%d", (temp >> 4), (temp & 0x0f)); - - temp = readl (®s->control); - dbg ("control: 0x%08x%s%s%s HCFS=%s%s%s%s%s CBSR=%d", 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", 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)); - - maybe_print_eds ("ed_periodcurrent", readl (®s->ed_periodcurrent)); - - maybe_print_eds ("ed_controlhead", readl (®s->ed_controlhead)); - maybe_print_eds ("ed_controlcurrent", readl (®s->ed_controlcurrent)); - - maybe_print_eds ("ed_bulkhead", readl (®s->ed_bulkhead)); - maybe_print_eds ("ed_bulkcurrent", readl (®s->ed_bulkcurrent)); - - maybe_print_eds ("donehead", readl (®s->donehead)); -} - -static void ohci_dump_roothub (ohci_t *controller, int verbose) -{ - __u32 temp, ndp, i; - - temp = roothub_a (controller); - ndp = (temp & RH_A_NDP); -#ifdef CONFIG_AT91C_PQFP_UHPBUG - ndp = (ndp == 2) ? 1:0; -#endif - if (verbose) { - dbg ("roothub.a: %08x POTPGT=%d%s%s%s%s%s NDP=%d", 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 (controller); - dbg ("roothub.b: %08x PPCM=%04x DR=%04x", - temp, - (temp & RH_B_PPCM) >> 16, - (temp & RH_B_DR) - ); - temp = roothub_status (controller); - dbg ("roothub.status: %08x%s%s%s%s%s%s", - 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 (controller, i); - dbg ("roothub.portstatus [%d] = 0x%08x%s%s%s%s%s%s%s%s%s%s%s%s", - 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" : "" - ); - } -} - -static void ohci_dump (ohci_t *controller, int verbose) -{ - dbg ("OHCI controller usb-%s state", controller->slot_name); - - /* dumps some of the state we know about */ - ohci_dump_status (controller); - if (verbose) - ep_print_int_eds (controller, "hcca"); - dbg ("hcca frame #%04x", controller->hcca->frame_no); - ohci_dump_roothub (controller, 1); -} - - -#endif /* DEBUG */ - -/*-------------------------------------------------------------------------* - * Interface functions (URB) - *-------------------------------------------------------------------------*/ - -/* get a transfer request */ - -int sohci_submit_job(struct usb_device *dev, unsigned long pipe, void *buffer, - int transfer_len, struct devrequest *setup, int interval) -{ - ohci_t *ohci; - ed_t * ed; - urb_priv_t *purb_priv; - int i, size = 0; - - ohci = &gohci; - - /* when controller's hung, permit only roothub cleanup attempts - * such as powering down ports */ - if (ohci->disabled) { - err("sohci_submit_job: EPIPE"); - return -1; - } - - /* every endpoint has a ed, locate and fill it */ - if (!(ed = ep_add_ed (dev, pipe))) { - err("sohci_submit_job: ENOMEM"); - return -1; - } - - /* for the private part of the URB we need the number of TDs (size) */ - switch (usb_pipetype (pipe)) { - case PIPE_BULK: /* one TD for every 4096 Byte */ - size = (transfer_len - 1) / 4096 + 1; - break; - case PIPE_CONTROL: /* 1 TD for setup, 1 for ACK and 1 for every 4096 B */ - size = (transfer_len == 0)? 2: - (transfer_len - 1) / 4096 + 3; - break; - } - - if (size >= (N_URB_TD - 1)) { - err("need %d TDs, only have %d", size, N_URB_TD); - return -1; - } - purb_priv = &urb_priv; - purb_priv->pipe = pipe; - - /* fill the private part of the URB */ - purb_priv->length = size; - purb_priv->ed = ed; - purb_priv->actual_length = 0; - - /* allocate the TDs */ - /* note that td[0] was allocated in ep_add_ed */ - for (i = 0; i < size; i++) { - purb_priv->td[i] = td_alloc (dev); - if (!purb_priv->td[i]) { - purb_priv->length = i; - urb_free_priv (purb_priv); - err("sohci_submit_job: ENOMEM"); - return -1; - } - } - - if (ed->state == ED_NEW || (ed->state & ED_DEL)) { - urb_free_priv (purb_priv); - err("sohci_submit_job: EINVAL"); - return -1; - } - - /* link the ed into a chain if is not already */ - if (ed->state != ED_OPER) - ep_link (ohci, ed); - - /* fill the TDs and link it to the ed */ - td_submit_job(dev, pipe, buffer, transfer_len, setup, purb_priv, interval); - - return 0; -} - -/*-------------------------------------------------------------------------*/ - -#ifdef DEBUG -/* tell us the current USB frame number */ - -static int sohci_get_current_frame_number (struct usb_device *usb_dev) -{ - ohci_t *ohci = &gohci; - - return m16_swap (ohci->hcca->frame_no); -} -#endif - -/*-------------------------------------------------------------------------* - * ED handling functions - *-------------------------------------------------------------------------*/ - -/* link an ed into one of the HC chains */ - -static int ep_link (ohci_t *ohci, ed_t *edi) -{ - volatile ed_t *ed = edi; - - ed->state = ED_OPER; - - switch (ed->type) { - case PIPE_CONTROL: - ed->hwNextED = 0; - if (ohci->ed_controltail == NULL) { - writel (ed, &ohci->regs->ed_controlhead); - } else { - ohci->ed_controltail->hwNextED = m32_swap (ed); - } - ed->ed_prev = ohci->ed_controltail; - if (!ohci->ed_controltail && !ohci->ed_rm_list[0] && - !ohci->ed_rm_list[1] && !ohci->sleeping) { - ohci->hc_control |= OHCI_CTRL_CLE; - writel (ohci->hc_control, &ohci->regs->control); - } - ohci->ed_controltail = edi; - break; - - case PIPE_BULK: - ed->hwNextED = 0; - if (ohci->ed_bulktail == NULL) { - writel (ed, &ohci->regs->ed_bulkhead); - } else { - ohci->ed_bulktail->hwNextED = m32_swap (ed); - } - ed->ed_prev = ohci->ed_bulktail; - if (!ohci->ed_bulktail && !ohci->ed_rm_list[0] && - !ohci->ed_rm_list[1] && !ohci->sleeping) { - ohci->hc_control |= OHCI_CTRL_BLE; - writel (ohci->hc_control, &ohci->regs->control); - } - ohci->ed_bulktail = edi; - break; - } - return 0; -} - -/*-------------------------------------------------------------------------*/ - -/* unlink an ed from one of the HC chains. - * just the link to the ed is unlinked. - * the link from the ed still points to another operational ed or 0 - * so the HC can eventually finish the processing of the unlinked ed */ - -static int ep_unlink (ohci_t *ohci, ed_t *ed) -{ - ed->hwINFO |= m32_swap (OHCI_ED_SKIP); - - switch (ed->type) { - case PIPE_CONTROL: - if (ed->ed_prev == NULL) { - if (!ed->hwNextED) { - ohci->hc_control &= ~OHCI_CTRL_CLE; - writel (ohci->hc_control, &ohci->regs->control); - } - writel (m32_swap (*((__u32 *)&ed->hwNextED)), &ohci->regs->ed_controlhead); - } else { - ed->ed_prev->hwNextED = ed->hwNextED; - } - if (ohci->ed_controltail == ed) { - ohci->ed_controltail = ed->ed_prev; - } else { - ((ed_t *)m32_swap (*((__u32 *)&ed->hwNextED)))->ed_prev = ed->ed_prev; - } - break; - - case PIPE_BULK: - if (ed->ed_prev == NULL) { - if (!ed->hwNextED) { - ohci->hc_control &= ~OHCI_CTRL_BLE; - writel (ohci->hc_control, &ohci->regs->control); - } - writel (m32_swap (*((__u32 *)&ed->hwNextED)), &ohci->regs->ed_bulkhead); - } else { - ed->ed_prev->hwNextED = ed->hwNextED; - } - if (ohci->ed_bulktail == ed) { - ohci->ed_bulktail = ed->ed_prev; - } else { - ((ed_t *)m32_swap (*((__u32 *)&ed->hwNextED)))->ed_prev = ed->ed_prev; - } - break; - } - ed->state = ED_UNLINK; - return 0; -} - - -/*-------------------------------------------------------------------------*/ - -/* add/reinit an endpoint; this should be done once at the usb_set_configuration command, - * but the USB stack is a little bit stateless so we do it at every transaction - * if the state of the ed is ED_NEW then a dummy td is added and the state is changed to ED_UNLINK - * in all other cases the state is left unchanged - * the ed info fields are setted anyway even though most of them should not change */ - -static ed_t * ep_add_ed (struct usb_device *usb_dev, unsigned long pipe) -{ - td_t *td; - ed_t *ed_ret; - volatile ed_t *ed; - - ed = ed_ret = &ohci_dev.ed[(usb_pipeendpoint (pipe) << 1) | - (usb_pipecontrol (pipe)? 0: usb_pipeout (pipe))]; - - if ((ed->state & ED_DEL) || (ed->state & ED_URB_DEL)) { - err("ep_add_ed: pending delete"); - /* pending delete request */ - return NULL; - } - - if (ed->state == ED_NEW) { - ed->hwINFO = m32_swap (OHCI_ED_SKIP); /* skip ed */ - /* dummy td; end of td list for ed */ - td = td_alloc (usb_dev); - ed->hwTailP = m32_swap (td); - ed->hwHeadP = ed->hwTailP; - ed->state = ED_UNLINK; - ed->type = usb_pipetype (pipe); - ohci_dev.ed_cnt++; - } - - ed->hwINFO = m32_swap (usb_pipedevice (pipe) - | usb_pipeendpoint (pipe) << 7 - | (usb_pipeisoc (pipe)? 0x8000: 0) - | (usb_pipecontrol (pipe)? 0: (usb_pipeout (pipe)? 0x800: 0x1000)) - | usb_pipeslow (pipe) << 13 - | usb_maxpacket (usb_dev, pipe) << 16); - - return ed_ret; -} - -/*-------------------------------------------------------------------------* - * TD handling functions - *-------------------------------------------------------------------------*/ - -/* enqueue next TD for this URB (OHCI spec 5.2.8.2) */ - -static void td_fill (ohci_t *ohci, unsigned int info, - void *data, int len, - struct usb_device *dev, int index, urb_priv_t *urb_priv) -{ - volatile td_t *td, *td_pt; -#ifdef OHCI_FILL_TRACE - int i; -#endif - - if (index > urb_priv->length) { - err("index > length"); - return; - } - /* use this td as the next dummy */ - td_pt = urb_priv->td [index]; - td_pt->hwNextTD = 0; - - /* fill the old dummy TD */ - td = urb_priv->td [index] = (td_t *)(m32_swap (urb_priv->ed->hwTailP) & ~0xf); - - td->ed = urb_priv->ed; - td->next_dl_td = NULL; - td->index = index; - td->data = (__u32)data; -#ifdef OHCI_FILL_TRACE - if ((usb_pipetype(urb_priv->pipe) == PIPE_BULK) && usb_pipeout(urb_priv->pipe)) { - for (i = 0; i < len; i++) - printf("td->data[%d] %#2x ",i, ((unsigned char *)td->data)[i]); - printf("\n"); - } -#endif - if (!len) - data = 0; - - td->hwINFO = m32_swap (info); - td->hwCBP = m32_swap (data); - if (data) - td->hwBE = m32_swap (data + len - 1); - else - td->hwBE = 0; - td->hwNextTD = m32_swap (td_pt); - td->hwPSW [0] = m16_swap (((__u32)data & 0x0FFF) | 0xE000); - - /* append to queue */ - td->ed->hwTailP = td->hwNextTD; -} - -/*-------------------------------------------------------------------------*/ - -/* prepare all TDs of a transfer */ - -static void td_submit_job (struct usb_device *dev, unsigned long pipe, void *buffer, - int transfer_len, struct devrequest *setup, urb_priv_t *urb, int interval) -{ - ohci_t *ohci = &gohci; - int data_len = transfer_len; - void *data; - int cnt = 0; - __u32 info = 0; - unsigned int toggle = 0; - - /* OHCI handles the DATA-toggles itself, we just use the USB-toggle bits for reseting */ - if(usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe))) { - toggle = TD_T_TOGGLE; - } else { - toggle = TD_T_DATA0; - usb_settoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe), 1); - } - urb->td_cnt = 0; - if (data_len) - data = buffer; - else - data = 0; - - switch (usb_pipetype (pipe)) { - case PIPE_BULK: - info = usb_pipeout (pipe)? - TD_CC | TD_DP_OUT : TD_CC | TD_DP_IN ; - while(data_len > 4096) { - td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), data, 4096, dev, cnt, urb); - data += 4096; data_len -= 4096; cnt++; - } - info = usb_pipeout (pipe)? - TD_CC | TD_DP_OUT : TD_CC | TD_R | TD_DP_IN ; - td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), data, data_len, dev, cnt, urb); - cnt++; - - if (!ohci->sleeping) - writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */ - break; - - case PIPE_CONTROL: - info = TD_CC | TD_DP_SETUP | TD_T_DATA0; - td_fill (ohci, info, setup, 8, dev, cnt++, urb); - if (data_len > 0) { - info = usb_pipeout (pipe)? - TD_CC | TD_R | TD_DP_OUT | TD_T_DATA1 : TD_CC | TD_R | TD_DP_IN | TD_T_DATA1; - /* NOTE: mishandles transfers >8K, some >4K */ - td_fill (ohci, info, data, data_len, dev, cnt++, urb); - } - info = usb_pipeout (pipe)? - TD_CC | TD_DP_IN | TD_T_DATA1: TD_CC | TD_DP_OUT | TD_T_DATA1; - td_fill (ohci, info, data, 0, dev, cnt++, urb); - if (!ohci->sleeping) - writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */ - break; - } - if (urb->length != cnt) - dbg("TD LENGTH %d != CNT %d", urb->length, cnt); -} - -/*-------------------------------------------------------------------------* - * Done List handling functions - *-------------------------------------------------------------------------*/ - - -/* calculate the transfer length and update the urb */ - -static void dl_transfer_length(td_t * td) -{ - __u32 tdINFO, tdBE, tdCBP; - urb_priv_t *lurb_priv = &urb_priv; - - tdINFO = m32_swap (td->hwINFO); - tdBE = m32_swap (td->hwBE); - tdCBP = m32_swap (td->hwCBP); - - - if (!(usb_pipetype (lurb_priv->pipe) == PIPE_CONTROL && - ((td->index == 0) || (td->index == lurb_priv->length - 1)))) { - if (tdBE != 0) { - if (td->hwCBP == 0) - lurb_priv->actual_length += tdBE - td->data + 1; - else - lurb_priv->actual_length += tdCBP - td->data; - } - } -} - -/*-------------------------------------------------------------------------*/ - -/* replies to the request have to be on a FIFO basis so - * we reverse the reversed done-list */ - -static td_t * dl_reverse_done_list (ohci_t *ohci) -{ - __u32 td_list_hc; - td_t *td_rev = NULL; - td_t *td_list = NULL; - urb_priv_t *lurb_priv = NULL; - - td_list_hc = m32_swap (ohci->hcca->done_head) & 0xfffffff0; - ohci->hcca->done_head = 0; - - while (td_list_hc) { - td_list = (td_t *)td_list_hc; - - if (TD_CC_GET (m32_swap (td_list->hwINFO))) { - lurb_priv = &urb_priv; - dbg(" USB-error/status: %x : %p", - TD_CC_GET (m32_swap (td_list->hwINFO)), td_list); - if (td_list->ed->hwHeadP & m32_swap (0x1)) { - if (lurb_priv && ((td_list->index + 1) < lurb_priv->length)) { - td_list->ed->hwHeadP = - (lurb_priv->td[lurb_priv->length - 1]->hwNextTD & m32_swap (0xfffffff0)) | - (td_list->ed->hwHeadP & m32_swap (0x2)); - lurb_priv->td_cnt += lurb_priv->length - td_list->index - 1; - } else - td_list->ed->hwHeadP &= m32_swap (0xfffffff2); - } - } - - td_list->next_dl_td = td_rev; - td_rev = td_list; - td_list_hc = m32_swap (td_list->hwNextTD) & 0xfffffff0; - } - return td_list; -} - -/*-------------------------------------------------------------------------*/ - -/* td done list */ -static int dl_done_list (ohci_t *ohci, td_t *td_list) -{ - td_t *td_list_next = NULL; - ed_t *ed; - int cc = 0; - int stat = 0; - /* urb_t *urb; */ - urb_priv_t *lurb_priv; - __u32 tdINFO, edHeadP, edTailP; - - while (td_list) { - td_list_next = td_list->next_dl_td; - - lurb_priv = &urb_priv; - tdINFO = m32_swap (td_list->hwINFO); - - ed = td_list->ed; - - dl_transfer_length(td_list); - - /* error code of transfer */ - cc = TD_CC_GET (tdINFO); - if (cc != 0) { - dbg("ConditionCode %#x", cc); - stat = cc_to_error[cc]; - } - - if (ed->state != ED_NEW) { - edHeadP = m32_swap (ed->hwHeadP) & 0xfffffff0; - edTailP = m32_swap (ed->hwTailP); - - /* unlink eds if they are not busy */ - if ((edHeadP == edTailP) && (ed->state == ED_OPER)) - ep_unlink (ohci, ed); - } - - td_list = td_list_next; - } - return stat; -} - -/*-------------------------------------------------------------------------* - * Virtual Root Hub - *-------------------------------------------------------------------------*/ - -/* Device descriptor */ -static __u8 root_hub_dev_des[] = -{ - 0x12, /* __u8 bLength; */ - 0x01, /* __u8 bDescriptorType; Device */ - 0x10, /* __u16 bcdUSB; v1.1 */ - 0x01, - 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ - 0x00, /* __u8 bDeviceSubClass; */ - 0x00, /* __u8 bDeviceProtocol; */ - 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */ - 0x00, /* __u16 idVendor; */ - 0x00, - 0x00, /* __u16 idProduct; */ - 0x00, - 0x00, /* __u16 bcdDevice; */ - 0x00, - 0x00, /* __u8 iManufacturer; */ - 0x01, /* __u8 iProduct; */ - 0x00, /* __u8 iSerialNumber; */ - 0x01 /* __u8 bNumConfigurations; */ -}; - - -/* Configuration descriptor */ -static __u8 root_hub_config_des[] = -{ - 0x09, /* __u8 bLength; */ - 0x02, /* __u8 bDescriptorType; Configuration */ - 0x19, /* __u16 wTotalLength; */ - 0x00, - 0x01, /* __u8 bNumInterfaces; */ - 0x01, /* __u8 bConfigurationValue; */ - 0x00, /* __u8 iConfiguration; */ - 0x40, /* __u8 bmAttributes; - Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup, 4..0: resvd */ - 0x00, /* __u8 MaxPower; */ - - /* interface */ - 0x09, /* __u8 if_bLength; */ - 0x04, /* __u8 if_bDescriptorType; Interface */ - 0x00, /* __u8 if_bInterfaceNumber; */ - 0x00, /* __u8 if_bAlternateSetting; */ - 0x01, /* __u8 if_bNumEndpoints; */ - 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */ - 0x00, /* __u8 if_bInterfaceSubClass; */ - 0x00, /* __u8 if_bInterfaceProtocol; */ - 0x00, /* __u8 if_iInterface; */ - - /* endpoint */ - 0x07, /* __u8 ep_bLength; */ - 0x05, /* __u8 ep_bDescriptorType; Endpoint */ - 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ - 0x03, /* __u8 ep_bmAttributes; Interrupt */ - 0x02, /* __u16 ep_wMaxPacketSize; ((MAX_ROOT_PORTS + 1) / 8 */ - 0x00, - 0xff /* __u8 ep_bInterval; 255 ms */ -}; - -static unsigned char root_hub_str_index0[] = -{ - 0x04, /* __u8 bLength; */ - 0x03, /* __u8 bDescriptorType; String-descriptor */ - 0x09, /* __u8 lang ID */ - 0x04, /* __u8 lang ID */ -}; - -static unsigned char root_hub_str_index1[] = -{ - 28, /* __u8 bLength; */ - 0x03, /* __u8 bDescriptorType; String-descriptor */ - 'O', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - 'H', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - 'C', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - 'I', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - ' ', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - 'R', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - 'o', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - 'o', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - 't', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - ' ', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - 'H', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - 'u', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - 'b', /* __u8 Unicode */ - 0, /* __u8 Unicode */ -}; - -/* Hub class-specific descriptor is constructed dynamically */ - - -/*-------------------------------------------------------------------------*/ - -#define OK(x) len = (x); break -#ifdef DEBUG -#define WR_RH_STAT(x) {info("WR:status %#8x", (x));writel((x), &gohci.regs->roothub.status);} -#define WR_RH_PORTSTAT(x) {info("WR:portstatus[%d] %#8x", wIndex-1, (x));writel((x), &gohci.regs->roothub.portstatus[wIndex-1]);} -#else -#define WR_RH_STAT(x) writel((x), &gohci.regs->roothub.status) -#define WR_RH_PORTSTAT(x) writel((x), &gohci.regs->roothub.portstatus[wIndex-1]) -#endif -#define RD_RH_STAT roothub_status(&gohci) -#define RD_RH_PORTSTAT roothub_portstatus(&gohci,wIndex-1) - -/* request to virtual root hub */ - -int rh_check_port_status(ohci_t *controller) -{ - __u32 temp, ndp, i; - int res; - - res = -1; - temp = roothub_a (controller); - ndp = (temp & RH_A_NDP); -#ifdef CONFIG_AT91C_PQFP_UHPBUG - ndp = (ndp == 2) ? 1:0; -#endif - - for (i = 0; i < ndp; i++) { - temp = roothub_portstatus (controller, i); - /* check for a device disconnect */ - if (((temp & (RH_PS_PESC | RH_PS_CSC)) == - (RH_PS_PESC | RH_PS_CSC)) && - ((temp & RH_PS_CCS) == 0)) { - res = i; - break; - } - } - return res; -} - -static int ohci_submit_rh_msg(struct usb_device *dev, unsigned long pipe, - void *buffer, int transfer_len, struct devrequest *cmd) -{ - void * data = buffer; - int leni = transfer_len; - int len = 0; - int stat = 0; - __u32 datab[4]; - __u8 *data_buf = (__u8 *)datab; - __u16 bmRType_bReq; - __u16 wValue; - __u16 wIndex; - __u16 wLength; - -#ifdef DEBUG -urb_priv.actual_length = 0; -pkt_print(dev, pipe, buffer, transfer_len, cmd, "SUB(rh)", usb_pipein(pipe)); -#else - wait_ms(1); -#endif - if ((pipe & PIPE_INTERRUPT) == PIPE_INTERRUPT) { - info("Root-Hub submit IRQ: NOT implemented"); - return 0; - } - - bmRType_bReq = cmd->requesttype | (cmd->request << 8); - wValue = m16_swap (cmd->value); - wIndex = m16_swap (cmd->index); - wLength = m16_swap (cmd->length); - - info("Root-Hub: adr: %2x cmd(%1x): %08x %04x %04x %04x", - dev->devnum, 8, bmRType_bReq, wValue, wIndex, wLength); - - switch (bmRType_bReq) { - /* Request Destination: - without flags: Device, - RH_INTERFACE: interface, - RH_ENDPOINT: endpoint, - RH_CLASS means HUB here, - RH_OTHER | RH_CLASS almost ever means HUB_PORT here - */ - - case RH_GET_STATUS: - *(__u16 *) data_buf = m16_swap (1); OK (2); - case RH_GET_STATUS | RH_INTERFACE: - *(__u16 *) data_buf = m16_swap (0); OK (2); - case RH_GET_STATUS | RH_ENDPOINT: - *(__u16 *) data_buf = m16_swap (0); OK (2); - case RH_GET_STATUS | RH_CLASS: - *(__u32 *) data_buf = m32_swap ( - RD_RH_STAT & ~(RH_HS_CRWE | RH_HS_DRWE)); - OK (4); - case RH_GET_STATUS | RH_OTHER | RH_CLASS: - *(__u32 *) data_buf = m32_swap (RD_RH_PORTSTAT); OK (4); - - case RH_CLEAR_FEATURE | RH_ENDPOINT: - switch (wValue) { - case (RH_ENDPOINT_STALL): OK (0); - } - break; - - case RH_CLEAR_FEATURE | RH_CLASS: - switch (wValue) { - case RH_C_HUB_LOCAL_POWER: - OK(0); - case (RH_C_HUB_OVER_CURRENT): - WR_RH_STAT(RH_HS_OCIC); OK (0); - } - break; - - case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS: - switch (wValue) { - case (RH_PORT_ENABLE): - WR_RH_PORTSTAT (RH_PS_CCS ); OK (0); - case (RH_PORT_SUSPEND): - WR_RH_PORTSTAT (RH_PS_POCI); OK (0); - case (RH_PORT_POWER): - WR_RH_PORTSTAT (RH_PS_LSDA); OK (0); - case (RH_C_PORT_CONNECTION): - WR_RH_PORTSTAT (RH_PS_CSC ); OK (0); - case (RH_C_PORT_ENABLE): - WR_RH_PORTSTAT (RH_PS_PESC); OK (0); - case (RH_C_PORT_SUSPEND): - WR_RH_PORTSTAT (RH_PS_PSSC); OK (0); - case (RH_C_PORT_OVER_CURRENT): - WR_RH_PORTSTAT (RH_PS_OCIC); OK (0); - case (RH_C_PORT_RESET): - WR_RH_PORTSTAT (RH_PS_PRSC); OK (0); - } - break; - - case RH_SET_FEATURE | RH_OTHER | RH_CLASS: - switch (wValue) { - case (RH_PORT_SUSPEND): - WR_RH_PORTSTAT (RH_PS_PSS ); OK (0); - case (RH_PORT_RESET): /* BUG IN HUP CODE *********/ - if (RD_RH_PORTSTAT & RH_PS_CCS) - WR_RH_PORTSTAT (RH_PS_PRS); - OK (0); - case (RH_PORT_POWER): - WR_RH_PORTSTAT (RH_PS_PPS ); OK (0); - case (RH_PORT_ENABLE): /* BUG IN HUP CODE *********/ - if (RD_RH_PORTSTAT & RH_PS_CCS) - WR_RH_PORTSTAT (RH_PS_PES ); - OK (0); - } - break; - - case RH_SET_ADDRESS: gohci.rh.devnum = wValue; OK(0); - - case RH_GET_DESCRIPTOR: - switch ((wValue & 0xff00) >> 8) { - case (0x01): /* device descriptor */ - len = min_t(unsigned int, - leni, - min_t(unsigned int, - sizeof (root_hub_dev_des), - wLength)); - data_buf = root_hub_dev_des; OK(len); - case (0x02): /* configuration descriptor */ - len = min_t(unsigned int, - leni, - min_t(unsigned int, - sizeof (root_hub_config_des), - wLength)); - data_buf = root_hub_config_des; OK(len); - case (0x03): /* string descriptors */ - if(wValue==0x0300) { - len = min_t(unsigned int, - leni, - min_t(unsigned int, - sizeof (root_hub_str_index0), - wLength)); - data_buf = root_hub_str_index0; - OK(len); - } - if(wValue==0x0301) { - len = min_t(unsigned int, - leni, - min_t(unsigned int, - sizeof (root_hub_str_index1), - wLength)); - data_buf = root_hub_str_index1; - OK(len); - } - default: - stat = USB_ST_STALLED; - } - break; - - case RH_GET_DESCRIPTOR | RH_CLASS: - { - __u32 temp = roothub_a (&gohci); - - data_buf [0] = 9; /* min length; */ - data_buf [1] = 0x29; - data_buf [2] = temp & RH_A_NDP; -#ifdef CONFIG_AT91C_PQFP_UHPBUG - data_buf [2] = (data_buf [2] == 2) ? 1:0; -#endif - data_buf [3] = 0; - if (temp & RH_A_PSM) /* per-port power switching? */ - data_buf [3] |= 0x1; - if (temp & RH_A_NOCP) /* no overcurrent reporting? */ - data_buf [3] |= 0x10; - else if (temp & RH_A_OCPM) /* per-port overcurrent reporting? */ - data_buf [3] |= 0x8; - - /* corresponds to data_buf[4-7] */ - datab [1] = 0; - data_buf [5] = (temp & RH_A_POTPGT) >> 24; - temp = roothub_b (&gohci); - data_buf [7] = temp & RH_B_DR; - if (data_buf [2] < 7) { - data_buf [8] = 0xff; - } else { - data_buf [0] += 2; - data_buf [8] = (temp & RH_B_DR) >> 8; - data_buf [10] = data_buf [9] = 0xff; - } - - len = min_t(unsigned int, leni, - min_t(unsigned int, data_buf [0], wLength)); - OK (len); - } - - case RH_GET_CONFIGURATION: *(__u8 *) data_buf = 0x01; OK (1); - - case RH_SET_CONFIGURATION: WR_RH_STAT (0x10000); OK (0); - - default: - dbg ("unsupported root hub command"); - stat = USB_ST_STALLED; - } - -#ifdef DEBUG - ohci_dump_roothub (&gohci, 1); -#else - wait_ms(1); -#endif - - len = min_t(int, len, leni); - if (data != data_buf) - memcpy (data, data_buf, len); - dev->act_len = len; - dev->status = stat; - -#ifdef DEBUG - if (transfer_len) - urb_priv.actual_length = transfer_len; - pkt_print(dev, pipe, buffer, transfer_len, cmd, "RET(rh)", 0/*usb_pipein(pipe)*/); -#else - wait_ms(1); -#endif - - return stat; -} - -/*-------------------------------------------------------------------------*/ - -/* common code for handling submit messages - used for all but root hub */ -/* accesses. */ -int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer, - int transfer_len, struct devrequest *setup, int interval) -{ - int stat = 0; - int maxsize = usb_maxpacket(dev, pipe); - int timeout; - - /* device pulled? Shortcut the action. */ - if (devgone == dev) { - dev->status = USB_ST_CRC_ERR; - return 0; - } - -#ifdef DEBUG - urb_priv.actual_length = 0; - pkt_print(dev, pipe, buffer, transfer_len, setup, "SUB", usb_pipein(pipe)); -#else - wait_ms(1); -#endif - if (!maxsize) { - err("submit_common_message: pipesize for pipe %lx is zero", - pipe); - return -1; - } - - if (sohci_submit_job(dev, pipe, buffer, transfer_len, setup, interval) < 0) { - err("sohci_submit_job failed"); - return -1; - } - - wait_ms(10); - /* ohci_dump_status(&gohci); */ - - /* allow more time for a BULK device to react - some are slow */ -#define BULK_TO 5000 /* timeout in milliseconds */ - if (usb_pipetype (pipe) == PIPE_BULK) - timeout = BULK_TO; - else - timeout = 100; - - /* wait for it to complete */ - for (;;) { - /* check whether the controller is done */ - stat = hc_interrupt(); - if (stat < 0) { - stat = USB_ST_CRC_ERR; - break; - } - if (stat >= 0 && stat != 0xff) { - /* 0xff is returned for an SF-interrupt */ - break; - } - if (--timeout) { - wait_ms(1); - } else { - err("CTL:TIMEOUT "); - stat = USB_ST_CRC_ERR; - break; - } - } - /* we got an Root Hub Status Change interrupt */ - if (got_rhsc) { -#ifdef DEBUG - ohci_dump_roothub (&gohci, 1); -#endif - got_rhsc = 0; - /* abuse timeout */ - timeout = rh_check_port_status(&gohci); - if (timeout >= 0) { -#if 0 /* this does nothing useful, but leave it here in case that changes */ - /* the called routine adds 1 to the passed value */ - usb_hub_port_connect_change(gohci.rh.dev, timeout - 1); -#endif - /* - * XXX - * This is potentially dangerous because it assumes - * that only one device is ever plugged in! - */ - devgone = dev; - } - } - - dev->status = stat; - dev->act_len = transfer_len; - -#ifdef DEBUG - pkt_print(dev, pipe, buffer, transfer_len, setup, "RET(ctlr)", usb_pipein(pipe)); -#else - wait_ms(1); -#endif - - /* free TDs in urb_priv */ - urb_free_priv (&urb_priv); - return 0; -} - -/* submit routines called from usb.c */ -int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, - int transfer_len) -{ - info("submit_bulk_msg"); - return submit_common_msg(dev, pipe, buffer, transfer_len, NULL, 0); -} - -int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, - int transfer_len, struct devrequest *setup) -{ - int maxsize = usb_maxpacket(dev, pipe); - - info("submit_control_msg"); -#ifdef DEBUG - urb_priv.actual_length = 0; - pkt_print(dev, pipe, buffer, transfer_len, setup, "SUB", usb_pipein(pipe)); -#else - wait_ms(1); -#endif - if (!maxsize) { - err("submit_control_message: pipesize for pipe %lx is zero", - pipe); - return -1; - } - if (((pipe >> 8) & 0x7f) == gohci.rh.devnum) { - gohci.rh.dev = dev; - /* root hub - redirect */ - return ohci_submit_rh_msg(dev, pipe, buffer, transfer_len, - setup); - } - - return submit_common_msg(dev, pipe, buffer, transfer_len, setup, 0); -} - -int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, - int transfer_len, int interval) -{ - info("submit_int_msg"); - return -1; -} - -/*-------------------------------------------------------------------------* - * HC functions - *-------------------------------------------------------------------------*/ - -/* reset the HC and BUS */ - -static int hc_reset (ohci_t *ohci) -{ - int timeout = 30; - int smm_timeout = 50; /* 0,5 sec */ - - dbg("%s\n", __FUNCTION__); - - if (readl (&ohci->regs->control) & OHCI_CTRL_IR) { /* SMM owns the HC */ - writel (OHCI_OCR, &ohci->regs->cmdstatus); /* request ownership */ - info("USB HC TakeOver from SMM"); - while (readl (&ohci->regs->control) & OHCI_CTRL_IR) { - wait_ms (10); - if (--smm_timeout == 0) { - err("USB HC TakeOver failed!"); - return -1; - } - } - } - - /* Disable HC interrupts */ - writel (OHCI_INTR_MIE, &ohci->regs->intrdisable); - - dbg("USB HC reset_hc usb-%s: ctrl = 0x%X ;\n", - ohci->slot_name, - readl(&ohci->regs->control)); - - /* Reset USB (needed by some controllers) */ - writel (0, &ohci->regs->control); - - /* HC Reset requires max 10 us delay */ - writel (OHCI_HCR, &ohci->regs->cmdstatus); - while ((readl (&ohci->regs->cmdstatus) & OHCI_HCR) != 0) { - if (--timeout == 0) { - err("USB HC reset timed out!"); - return -1; - } - udelay (1); - } - return 0; -} - -/*-------------------------------------------------------------------------*/ - -/* Start an OHCI controller, set the BUS operational - * enable interrupts - * connect the virtual root hub */ - -static int hc_start (ohci_t * ohci) -{ - __u32 mask; - unsigned int fminterval; - - ohci->disabled = 1; - - /* Tell the controller where the control and bulk lists are - * The lists are empty now. */ - - writel (0, &ohci->regs->ed_controlhead); - writel (0, &ohci->regs->ed_bulkhead); - - writel ((__u32)ohci->hcca, &ohci->regs->hcca); /* a reset clears this */ - - fminterval = 0x2edf; - writel ((fminterval * 9) / 10, &ohci->regs->periodicstart); - fminterval |= ((((fminterval - 210) * 6) / 7) << 16); - writel (fminterval, &ohci->regs->fminterval); - writel (0x628, &ohci->regs->lsthresh); - - /* start controller operations */ - ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER; - ohci->disabled = 0; - writel (ohci->hc_control, &ohci->regs->control); - - /* disable all interrupts */ - mask = (OHCI_INTR_SO | OHCI_INTR_WDH | OHCI_INTR_SF | OHCI_INTR_RD | - OHCI_INTR_UE | OHCI_INTR_FNO | OHCI_INTR_RHSC | - OHCI_INTR_OC | OHCI_INTR_MIE); - writel (mask, &ohci->regs->intrdisable); - /* clear all interrupts */ - mask &= ~OHCI_INTR_MIE; - writel (mask, &ohci->regs->intrstatus); - /* Choose the interrupts we care about now - but w/o MIE */ - mask = OHCI_INTR_RHSC | OHCI_INTR_UE | OHCI_INTR_WDH | OHCI_INTR_SO; - writel (mask, &ohci->regs->intrenable); - -#ifdef OHCI_USE_NPS - /* required for AMD-756 and some Mac platforms */ - writel ((roothub_a (ohci) | RH_A_NPS) & ~RH_A_PSM, - &ohci->regs->roothub.a); - writel (RH_HS_LPSC, &ohci->regs->roothub.status); -#endif /* OHCI_USE_NPS */ - -#define mdelay(n) ({unsigned long msec=(n); while (msec--) udelay(1000);}) - /* POTPGT delay is bits 24-31, in 2 ms units. */ - mdelay ((roothub_a (ohci) >> 23) & 0x1fe); - - /* connect the virtual root hub */ - ohci->rh.devnum = 0; - - return 0; -} - -/*-------------------------------------------------------------------------*/ - -/* an interrupt happens */ - -static int -hc_interrupt (void) -{ - ohci_t *ohci = &gohci; - struct ohci_regs *regs = ohci->regs; - int ints; - int stat = -1; - - if ((ohci->hcca->done_head != 0) && !(m32_swap (ohci->hcca->done_head) & 0x01)) { - ints = OHCI_INTR_WDH; - } else { - ints = readl (®s->intrstatus); - } - - /* dbg("Interrupt: %x frame: %x", ints, le16_to_cpu (ohci->hcca->frame_no)); */ - - if (ints & OHCI_INTR_RHSC) { - got_rhsc = 1; - } - - if (ints & OHCI_INTR_UE) { - ohci->disabled++; - err ("OHCI Unrecoverable Error, controller usb-%s disabled", - ohci->slot_name); - /* e.g. due to PCI Master/Target Abort */ - -#ifdef DEBUG - ohci_dump (ohci, 1); -#else - wait_ms(1); -#endif - /* FIXME: be optimistic, hope that bug won't repeat often. */ - /* Make some non-interrupt context restart the controller. */ - /* Count and limit the retries though; either hardware or */ - /* software errors can go forever... */ - hc_reset (ohci); - return -1; - } - - if (ints & OHCI_INTR_WDH) { - wait_ms(1); - writel (OHCI_INTR_WDH, ®s->intrdisable); - stat = dl_done_list (&gohci, dl_reverse_done_list (&gohci)); - writel (OHCI_INTR_WDH, ®s->intrenable); - } - - if (ints & OHCI_INTR_SO) { - dbg("USB Schedule overrun\n"); - writel (OHCI_INTR_SO, ®s->intrenable); - stat = -1; - } - - /* FIXME: this assumes SOF (1/ms) interrupts don't get lost... */ - if (ints & OHCI_INTR_SF) { - unsigned int frame = m16_swap (ohci->hcca->frame_no) & 1; - wait_ms(1); - writel (OHCI_INTR_SF, ®s->intrdisable); - if (ohci->ed_rm_list[frame] != NULL) - writel (OHCI_INTR_SF, ®s->intrenable); - stat = 0xff; - } - - writel (ints, ®s->intrstatus); - return stat; -} - -/*-------------------------------------------------------------------------*/ - -/*-------------------------------------------------------------------------*/ - -/* De-allocate all resources.. */ - -static void hc_release_ohci (ohci_t *ohci) -{ - dbg ("USB HC release ohci usb-%s", ohci->slot_name); - - if (!ohci->disabled) - hc_reset (ohci); -} - -/*-------------------------------------------------------------------------*/ - -/* - * low level initalisation routine, called from usb.c - */ -static char ohci_inited = 0; - -int usb_lowlevel_init(void) -{ - /* - * Enable USB host clock. - */ - *AT91C_PMC_SCER = AT91C_PMC_UHP; /* 48MHz clock enabled for UHP */ - *AT91C_PMC_PCER = 1 << AT91C_ID_UHP; /* Peripheral Clock Enable Register */ - - memset (&gohci, 0, sizeof (ohci_t)); - memset (&urb_priv, 0, sizeof (urb_priv_t)); - - /* align the storage */ - if ((__u32)&ghcca[0] & 0xff) { - err("HCCA not aligned!!"); - return -1; - } - phcca = &ghcca[0]; - info("aligned ghcca %p", phcca); - memset(&ohci_dev, 0, sizeof(struct ohci_device)); - if ((__u32)&ohci_dev.ed[0] & 0x7) { - err("EDs not aligned!!"); - return -1; - } - memset(gtd, 0, sizeof(td_t) * (NUM_TD + 1)); - if ((__u32)gtd & 0x7) { - err("TDs not aligned!!"); - return -1; - } - ptd = gtd; - gohci.hcca = phcca; - memset (phcca, 0, sizeof (struct ohci_hcca)); - - gohci.disabled = 1; - gohci.sleeping = 0; - gohci.irq = -1; - gohci.regs = (struct ohci_regs *)AT91_USB_HOST_BASE; - - gohci.flags = 0; - gohci.slot_name = "at91rm9200"; - - if (hc_reset (&gohci) < 0) { - hc_release_ohci (&gohci); - /* Initialization failed */ - *AT91C_PMC_PCER = AT91C_ID_UHP; - *AT91C_PMC_SCDR = 1 << AT91C_PMC_UHP; /* 48MHz clock disabled for UHP */ - return -1; - } - - /* FIXME this is a second HC reset; why?? */ -/* writel (gohci.hc_control = OHCI_USB_RESET, &gohci.regs->control); - wait_ms (10);*/ - - if (hc_start (&gohci) < 0) { - err ("can't start usb-%s", gohci.slot_name); - hc_release_ohci (&gohci); - /* Initialization failed */ - *AT91C_PMC_PCER = AT91C_ID_UHP; - *AT91C_PMC_SCDR = 1 << AT91C_PMC_UHP; /* 48MHz clock disabled for UHP */ - return -1; - } - -#ifdef DEBUG - ohci_dump (&gohci, 1); -#else - wait_ms(1); -#endif - ohci_inited = 1; - return 0; -} - -int usb_lowlevel_stop(void) -{ - /* this gets called really early - before the controller has */ - /* even been initialized! */ - if (!ohci_inited) - return 0; - /* TODO release any interrupts, etc. */ - /* call hc_release_ohci() here ? */ - hc_reset (&gohci); - /* may not want to do this */ - *AT91C_PMC_PCER = 1 << AT91C_ID_UHP; - *AT91C_PMC_SCDR = 1 << AT91C_PMC_UHP; /* 48MHz clock disabled for UHP */ - return 0; -} - -#endif /* CONFIG_USB_OHCI */ diff --git a/cpu/arm920t/at91rm9200/usb_ohci.h b/cpu/arm920t/at91rm9200/usb_ohci.h deleted file mode 100644 index ecb4e937b..000000000 --- a/cpu/arm920t/at91rm9200/usb_ohci.h +++ /dev/null @@ -1,419 +0,0 @@ -/* - * URB OHCI HCD (Host Controller Driver) for USB. - * - * (C) Copyright 1999 Roman Weissgaerber - * (C) Copyright 2000-2001 David Brownell - * - * usb-ohci.h - */ - - -static int cc_to_error[16] = { - -/* mapping of the OHCI CC status to error codes */ - /* No Error */ 0, - /* CRC Error */ USB_ST_CRC_ERR, - /* Bit Stuff */ USB_ST_BIT_ERR, - /* Data Togg */ USB_ST_CRC_ERR, - /* Stall */ USB_ST_STALLED, - /* DevNotResp */ -1, - /* PIDCheck */ USB_ST_BIT_ERR, - /* UnExpPID */ USB_ST_BIT_ERR, - /* DataOver */ USB_ST_BUF_ERR, - /* DataUnder */ USB_ST_BUF_ERR, - /* reservd */ -1, - /* reservd */ -1, - /* BufferOver */ USB_ST_BUF_ERR, - /* BuffUnder */ USB_ST_BUF_ERR, - /* Not Access */ -1, - /* Not Access */ -1 -}; - -/* ED States */ - -#define ED_NEW 0x00 -#define ED_UNLINK 0x01 -#define ED_OPER 0x02 -#define ED_DEL 0x04 -#define ED_URB_DEL 0x08 - -/* usb_ohci_ed */ -struct ed { - __u32 hwINFO; - __u32 hwTailP; - __u32 hwHeadP; - __u32 hwNextED; - - struct ed *ed_prev; - __u8 int_period; - __u8 int_branch; - __u8 int_load; - __u8 int_interval; - __u8 state; - __u8 type; - __u16 last_iso; - struct ed *ed_rm_list; - - struct usb_device *usb_dev; - __u32 unused[3]; -} __attribute((aligned(16))); -typedef struct ed ed_t; - - -/* TD info field */ -#define TD_CC 0xf0000000 -#define TD_CC_GET(td_p) ((td_p >>28) & 0x0f) -#define TD_CC_SET(td_p, cc) (td_p) = ((td_p) & 0x0fffffff) | (((cc) & 0x0f) << 28) -#define TD_EC 0x0C000000 -#define TD_T 0x03000000 -#define TD_T_DATA0 0x02000000 -#define TD_T_DATA1 0x03000000 -#define TD_T_TOGGLE 0x00000000 -#define TD_R 0x00040000 -#define TD_DI 0x00E00000 -#define TD_DI_SET(X) (((X) & 0x07)<< 21) -#define TD_DP 0x00180000 -#define TD_DP_SETUP 0x00000000 -#define TD_DP_IN 0x00100000 -#define TD_DP_OUT 0x00080000 - -#define TD_ISO 0x00010000 -#define TD_DEL 0x00020000 - -/* CC Codes */ -#define TD_CC_NOERROR 0x00 -#define TD_CC_CRC 0x01 -#define TD_CC_BITSTUFFING 0x02 -#define TD_CC_DATATOGGLEM 0x03 -#define TD_CC_STALL 0x04 -#define TD_DEVNOTRESP 0x05 -#define TD_PIDCHECKFAIL 0x06 -#define TD_UNEXPECTEDPID 0x07 -#define TD_DATAOVERRUN 0x08 -#define TD_DATAUNDERRUN 0x09 -#define TD_BUFFEROVERRUN 0x0C -#define TD_BUFFERUNDERRUN 0x0D -#define TD_NOTACCESSED 0x0F - - -#define MAXPSW 1 - -struct td { - __u32 hwINFO; - __u32 hwCBP; /* Current Buffer Pointer */ - __u32 hwNextTD; /* Next TD Pointer */ - __u32 hwBE; /* Memory Buffer End Pointer */ - - __u16 hwPSW[MAXPSW]; - __u8 unused; - __u8 index; - struct ed *ed; - struct td *next_dl_td; - struct usb_device *usb_dev; - int transfer_len; - __u32 data; - - __u32 unused2[2]; -} __attribute((aligned(32))); -typedef struct td td_t; - -#define OHCI_ED_SKIP (1 << 14) - -/* - * The HCCA (Host Controller Communications Area) is a 256 byte - * structure defined in the OHCI spec. that the host controller is - * told the base address of. It must be 256-byte aligned. - */ - -#define NUM_INTS 32 /* part of the OHCI standard */ -struct ohci_hcca { - __u32 int_table[NUM_INTS]; /* Interrupt ED table */ - __u16 frame_no; /* current frame number */ - __u16 pad1; /* set to 0 on each frame_no change */ - __u32 done_head; /* info returned for an interrupt */ - u8 reserved_for_hc[116]; -} __attribute((aligned(256))); - - -/* - * Maximum number of root hub ports. - */ -#define MAX_ROOT_PORTS 15 /* maximum OHCI root hub ports */ - -/* - * This is the structure of the OHCI controller's memory mapped I/O - * region. This is Memory Mapped I/O. You must use the readl() and - * writel() macros defined in asm/io.h to access these!! - */ -struct ohci_regs { - /* control and status registers */ - __u32 revision; - __u32 control; - __u32 cmdstatus; - __u32 intrstatus; - __u32 intrenable; - __u32 intrdisable; - /* memory pointers */ - __u32 hcca; - __u32 ed_periodcurrent; - __u32 ed_controlhead; - __u32 ed_controlcurrent; - __u32 ed_bulkhead; - __u32 ed_bulkcurrent; - __u32 donehead; - /* frame counters */ - __u32 fminterval; - __u32 fmremaining; - __u32 fmnumber; - __u32 periodicstart; - __u32 lsthresh; - /* Root hub ports */ - struct ohci_roothub_regs { - __u32 a; - __u32 b; - __u32 status; - __u32 portstatus[MAX_ROOT_PORTS]; - } roothub; -} __attribute((aligned(32))); - - -/* OHCI CONTROL AND STATUS REGISTER MASKS */ - -/* - * HcControl (control) register masks - */ -#define OHCI_CTRL_CBSR (3 << 0) /* control/bulk service ratio */ -#define OHCI_CTRL_PLE (1 << 2) /* periodic list enable */ -#define OHCI_CTRL_IE (1 << 3) /* isochronous enable */ -#define OHCI_CTRL_CLE (1 << 4) /* control list enable */ -#define OHCI_CTRL_BLE (1 << 5) /* bulk list enable */ -#define OHCI_CTRL_HCFS (3 << 6) /* host controller functional state */ -#define OHCI_CTRL_IR (1 << 8) /* interrupt routing */ -#define OHCI_CTRL_RWC (1 << 9) /* remote wakeup connected */ -#define OHCI_CTRL_RWE (1 << 10) /* remote wakeup enable */ - -/* pre-shifted values for HCFS */ -# define OHCI_USB_RESET (0 << 6) -# define OHCI_USB_RESUME (1 << 6) -# define OHCI_USB_OPER (2 << 6) -# define OHCI_USB_SUSPEND (3 << 6) - -/* - * HcCommandStatus (cmdstatus) register masks - */ -#define OHCI_HCR (1 << 0) /* host controller reset */ -#define OHCI_CLF (1 << 1) /* control list filled */ -#define OHCI_BLF (1 << 2) /* bulk list filled */ -#define OHCI_OCR (1 << 3) /* ownership change request */ -#define OHCI_SOC (3 << 16) /* scheduling overrun count */ - -/* - * masks used with interrupt registers: - * HcInterruptStatus (intrstatus) - * HcInterruptEnable (intrenable) - * HcInterruptDisable (intrdisable) - */ -#define OHCI_INTR_SO (1 << 0) /* scheduling overrun */ -#define OHCI_INTR_WDH (1 << 1) /* writeback of done_head */ -#define OHCI_INTR_SF (1 << 2) /* start frame */ -#define OHCI_INTR_RD (1 << 3) /* resume detect */ -#define OHCI_INTR_UE (1 << 4) /* unrecoverable error */ -#define OHCI_INTR_FNO (1 << 5) /* frame number overflow */ -#define OHCI_INTR_RHSC (1 << 6) /* root hub status change */ -#define OHCI_INTR_OC (1 << 30) /* ownership change */ -#define OHCI_INTR_MIE (1 << 31) /* master interrupt enable */ - - -/* Virtual Root HUB */ -struct virt_root_hub { - int devnum; /* Address of Root Hub endpoint */ - void *dev; /* was urb */ - void *int_addr; - int send; - int interval; -}; - -/* USB HUB CONSTANTS (not OHCI-specific; see hub.h) */ - -/* destination of request */ -#define RH_INTERFACE 0x01 -#define RH_ENDPOINT 0x02 -#define RH_OTHER 0x03 - -#define RH_CLASS 0x20 -#define RH_VENDOR 0x40 - -/* Requests: bRequest << 8 | bmRequestType */ -#define RH_GET_STATUS 0x0080 -#define RH_CLEAR_FEATURE 0x0100 -#define RH_SET_FEATURE 0x0300 -#define RH_SET_ADDRESS 0x0500 -#define RH_GET_DESCRIPTOR 0x0680 -#define RH_SET_DESCRIPTOR 0x0700 -#define RH_GET_CONFIGURATION 0x0880 -#define RH_SET_CONFIGURATION 0x0900 -#define RH_GET_STATE 0x0280 -#define RH_GET_INTERFACE 0x0A80 -#define RH_SET_INTERFACE 0x0B00 -#define RH_SYNC_FRAME 0x0C80 -/* Our Vendor Specific Request */ -#define RH_SET_EP 0x2000 - - -/* Hub port features */ -#define RH_PORT_CONNECTION 0x00 -#define RH_PORT_ENABLE 0x01 -#define RH_PORT_SUSPEND 0x02 -#define RH_PORT_OVER_CURRENT 0x03 -#define RH_PORT_RESET 0x04 -#define RH_PORT_POWER 0x08 -#define RH_PORT_LOW_SPEED 0x09 - -#define RH_C_PORT_CONNECTION 0x10 -#define RH_C_PORT_ENABLE 0x11 -#define RH_C_PORT_SUSPEND 0x12 -#define RH_C_PORT_OVER_CURRENT 0x13 -#define RH_C_PORT_RESET 0x14 - -/* Hub features */ -#define RH_C_HUB_LOCAL_POWER 0x00 -#define RH_C_HUB_OVER_CURRENT 0x01 - -#define RH_DEVICE_REMOTE_WAKEUP 0x00 -#define RH_ENDPOINT_STALL 0x01 - -#define RH_ACK 0x01 -#define RH_REQ_ERR -1 -#define RH_NACK 0x00 - - -/* OHCI ROOT HUB REGISTER MASKS */ - -/* roothub.portstatus [i] bits */ -#define RH_PS_CCS 0x00000001 /* current connect status */ -#define RH_PS_PES 0x00000002 /* port enable status*/ -#define RH_PS_PSS 0x00000004 /* port suspend status */ -#define RH_PS_POCI 0x00000008 /* port over current indicator */ -#define RH_PS_PRS 0x00000010 /* port reset status */ -#define RH_PS_PPS 0x00000100 /* port power status */ -#define RH_PS_LSDA 0x00000200 /* low speed device attached */ -#define RH_PS_CSC 0x00010000 /* connect status change */ -#define RH_PS_PESC 0x00020000 /* port enable status change */ -#define RH_PS_PSSC 0x00040000 /* port suspend status change */ -#define RH_PS_OCIC 0x00080000 /* over current indicator change */ -#define RH_PS_PRSC 0x00100000 /* port reset status change */ - -/* roothub.status bits */ -#define RH_HS_LPS 0x00000001 /* local power status */ -#define RH_HS_OCI 0x00000002 /* over current indicator */ -#define RH_HS_DRWE 0x00008000 /* device remote wakeup enable */ -#define RH_HS_LPSC 0x00010000 /* local power status change */ -#define RH_HS_OCIC 0x00020000 /* over current indicator change */ -#define RH_HS_CRWE 0x80000000 /* clear remote wakeup enable */ - -/* roothub.b masks */ -#define RH_B_DR 0x0000ffff /* device removable flags */ -#define RH_B_PPCM 0xffff0000 /* port power control mask */ - -/* roothub.a masks */ -#define RH_A_NDP (0xff << 0) /* number of downstream ports */ -#define RH_A_PSM (1 << 8) /* power switching mode */ -#define RH_A_NPS (1 << 9) /* no power switching */ -#define RH_A_DT (1 << 10) /* device type (mbz) */ -#define RH_A_OCPM (1 << 11) /* over current protection mode */ -#define RH_A_NOCP (1 << 12) /* no over current protection */ -#define RH_A_POTPGT (0xff << 24) /* power on to power good time */ - -/* urb */ -#define N_URB_TD 48 -typedef struct -{ - ed_t *ed; - __u16 length; /* number of tds associated with this request */ - __u16 td_cnt; /* number of tds already serviced */ - int state; - unsigned long pipe; - int actual_length; - td_t *td[N_URB_TD]; /* list pointer to all corresponding TDs associated with this request */ -} urb_priv_t; -#define URB_DEL 1 - -/* - * This is the full ohci controller description - * - * Note how the "proper" USB information is just - * a subset of what the full implementation needs. (Linus) - */ - - -typedef struct ohci { - struct ohci_hcca *hcca; /* hcca */ - /*dma_addr_t hcca_dma;*/ - - int irq; - int disabled; /* e.g. got a UE, we're hung */ - int sleeping; - unsigned long flags; /* for HC bugs */ - - struct ohci_regs *regs; /* OHCI controller's memory */ - - ed_t *ed_rm_list[2]; /* lists of all endpoints to be removed */ - ed_t *ed_bulktail; /* last endpoint of bulk list */ - ed_t *ed_controltail; /* last endpoint of control list */ - int intrstatus; - __u32 hc_control; /* copy of the hc control reg */ - struct usb_device *dev[32]; - struct virt_root_hub rh; - - const char *slot_name; -} ohci_t; - -#define NUM_EDS 8 /* num of preallocated endpoint descriptors */ - -struct ohci_device { - ed_t ed[NUM_EDS]; - int ed_cnt; -}; - -/* hcd */ -/* endpoint */ -static int ep_link(ohci_t * ohci, ed_t * ed); -static int ep_unlink(ohci_t * ohci, ed_t * ed); -static ed_t * ep_add_ed(struct usb_device * usb_dev, unsigned long pipe); - -/*-------------------------------------------------------------------------*/ - -/* we need more TDs than EDs */ -#define NUM_TD 64 - -/* +1 so we can align the storage */ -td_t gtd[NUM_TD+1]; -/* pointers to aligned storage */ -td_t *ptd; - -/* TDs ... */ -static inline struct td * -td_alloc (struct usb_device *usb_dev) -{ - int i; - struct td *td; - - td = NULL; - for (i = 0; i < NUM_TD; i++) - { - if (ptd[i].usb_dev == NULL) - { - td = &ptd[i]; - td->usb_dev = usb_dev; - break; - } - } - - return td; -} - -static inline void -ed_free (struct ed *ed) -{ - ed->usb_dev = NULL; -} diff --git a/cpu/pxa/usb.c b/cpu/pxa/usb.c index e89391cf5..171615704 100644 --- a/cpu/pxa/usb.c +++ b/cpu/pxa/usb.c @@ -22,10 +22,12 @@ */ #include -#include -#ifdef CFG_USB_CPU_INIT +#if defined(CONFIG_USB_OHCI) && defined(CFG_USB_OHCI_CPU_INIT) # ifdef CONFIG_CPU_MONAHANS + +#include + int usb_cpu_init() { /* Enable USB host clock. */ @@ -68,4 +70,4 @@ int usb_cpu_stop() return 0; } # endif /* CONFIG_CPU_MONAHANS */ -#endif /* CFG_USB_CPU_INIT */ +#endif /* defined(CONFIG_USB_OHCI) && defined(CFG_USB_OHCI_CPU_INIT) */ diff --git a/drivers/usb_ohci.c b/drivers/usb_ohci.c index f5af7192f..c32245a24 100644 --- a/drivers/usb_ohci.c +++ b/drivers/usb_ohci.c @@ -1552,13 +1552,13 @@ static char ohci_inited = 0; int usb_lowlevel_init(void) { -#if CFG_USB_CPU_INIT +#if CFG_USB_OHCI_CPU_INIT /* cpu dependant init */ if(usb_cpu_init()) return -1; #endif -#if CFG_USB_BOARD_INIT +#if CFG_USB_OHCI_BOARD_INIT /* board dependant init */ if(usb_board_init()) return -1; @@ -1593,18 +1593,18 @@ int usb_lowlevel_init(void) gohci.regs = (struct ohci_regs *)CFG_USB_OHCI_REGS_BASE; gohci.flags = 0; - gohci.slot_name = CFG_USB_SLOT_NAME; + gohci.slot_name = CFG_USB_OHCI_SLOT_NAME; if (hc_reset (&gohci) < 0) { hc_release_ohci (&gohci); err ("can't reset usb-%s", gohci.slot_name); /* Initialization failed disable clocks */ -#if CFG_USB_BOARD_INIT +#if CFG_USB_OHCI_BOARD_INIT /* board dependant cleanup */ usb_board_stop(); #endif -#if CFG_USB_CPU_INIT +#if CFG_USB_OHCI_CPU_INIT /* cpu dependant cleanup */ usb_cpu_stop(); #endif @@ -1618,12 +1618,12 @@ int usb_lowlevel_init(void) err ("can't start usb-%s", gohci.slot_name); hc_release_ohci (&gohci); /* Initialization failed */ -#if CFG_USB_BOARD_INIT +#if CFG_USB_OHCI_BOARD_INIT /* board dependant cleanup */ usb_board_stop(); #endif -#if CFG_USB_CPU_INIT +#if CFG_USB_OHCI_CPU_INIT /* cpu dependant cleanup */ usb_cpu_stop(); #endif @@ -1649,13 +1649,13 @@ int usb_lowlevel_stop(void) /* call hc_release_ohci() here ? */ hc_reset (&gohci); -#if CFG_USB_BOARD_INIT +#if CFG_USB_OHCI_BOARD_INIT /* board dependant cleanup */ if(usb_board_stop()) return -1; #endif -#if CFG_USB_CPU_INIT +#if CFG_USB_OHCI_CPU_INIT /* cpu dependant cleanup */ if(usb_cpu_stop()) return -1; diff --git a/include/configs/delta.h b/include/configs/delta.h index e6b05e059..fea821ab9 100644 --- a/include/configs/delta.h +++ b/include/configs/delta.h @@ -107,10 +107,10 @@ #define CONFIG_USB_STORAGE 1 #define CONFIG_DOS_PARTITION 1 -#undef CFG_USB_BOARD_INIT -#define CFG_USB_CPU_INIT 1 +#undef CFG_USB_OHCI_BOARD_INIT +#define CFG_USB_OHCI_CPU_INIT 1 #define CFG_USB_OHCI_REGS_BASE OHCI_REGS_BASE -#define CFG_USB_SLOT_NAME "delta" +#define CFG_USB_OHCI_SLOT_NAME "delta" #define LITTLEENDIAN 1 /* used by usb_ohci.c */ diff --git a/include/configs/mp2usb.h b/include/configs/mp2usb.h index 04f1f2408..edb20f8d8 100644 --- a/include/configs/mp2usb.h +++ b/include/configs/mp2usb.h @@ -107,6 +107,11 @@ #define CONFIG_DOS_PARTITION 1 #define CONFIG_AT91C_PQFP_UHPBUG 1 +#undef CFG_USB_OHCI_BOARD_INIT +#define CFG_USB_OHCI_CPU_INIT 1 +#define CFG_USB_OHCI_REGS_BASE AT91_USB_HOST_BASE +#define CFG_USB_OHCI_SLOT_NAME "at91rm9200" + #undef CONFIG_HARD_I2C #ifdef CONFIG_HARD_I2C @@ -127,7 +132,7 @@ #define CONFIG_COMMANDS \ ((CONFIG_CMD_DFL | \ CFG_CMD_DATE | \ - CFG_CMD_DHCP | \ + CFG_CMD_DHCP | \ CFG_CMD_EEPROM | \ CFG_CMD_I2C | \ CFG_CMD_NFS | \ @@ -136,7 +141,7 @@ #else #define CONFIG_COMMANDS \ ((CONFIG_CMD_DFL | \ - CFG_CMD_DHCP | \ + CFG_CMD_DHCP | \ CFG_CMD_NFS | \ CFG_CMD_SNTP | \ CFG_CMD_USB | \ @@ -156,7 +161,7 @@ #define CONFIG_NR_DRAM_BANKS 1 #define PHYS_SDRAM 0x20000000 -#define PHYS_SDRAM_SIZE 0x08000000 /* 128 megs */ +#define PHYS_SDRAM_SIZE 0x08000000 /* 128 megs */ #define CFG_MEMTEST_START PHYS_SDRAM #define CFG_MEMTEST_END CFG_MEMTEST_START + PHYS_SDRAM_SIZE - 262144 -- cgit v1.2.3 From ddf83a2fcef1a670c45fc585119dcc1fe062c4a9 Mon Sep 17 00:00:00 2001 From: Markus Klotzbuecher Date: Tue, 30 May 2006 16:56:14 +0200 Subject: Support generic OHCI support for the s3c24x0 cpu. --- cpu/arm920t/at91rm9200/usb.c | 6 ++ cpu/arm920t/s3c24x0/Makefile | 2 +- cpu/arm920t/s3c24x0/usb.c | 72 +++++++++++++++++++++++ cpu/pxa/usb.c | 5 ++ drivers/usb_ohci.c | 133 +++++++++++++++++++++++++++++++++++-------- drivers/usb_ohci.h | 2 + include/configs/trab.h | 5 ++ 7 files changed, 199 insertions(+), 26 deletions(-) create mode 100644 cpu/arm920t/s3c24x0/usb.c (limited to 'drivers') diff --git a/cpu/arm920t/at91rm9200/usb.c b/cpu/arm920t/at91rm9200/usb.c index 7b83f23eb..98e3cdd58 100644 --- a/cpu/arm920t/at91rm9200/usb.c +++ b/cpu/arm920t/at91rm9200/usb.c @@ -43,5 +43,11 @@ int usb_cpu_stop() *AT91C_PMC_SCDR = AT91C_PMC_UHP; /* 48MHz clock disabled for UHP */ return 0; } + +int usb_cpu_init_fail() +{ + usb_cpu_stop(); +} + # endif /* CONFIG_AT91RM9200 */ #endif /* defined(CONFIG_USB_OHCI) && defined(CFG_USB_OHCI_CPU_INIT) */ diff --git a/cpu/arm920t/s3c24x0/Makefile b/cpu/arm920t/s3c24x0/Makefile index af9e4effc..f81f84dd2 100644 --- a/cpu/arm920t/s3c24x0/Makefile +++ b/cpu/arm920t/s3c24x0/Makefile @@ -26,7 +26,7 @@ include $(TOPDIR)/config.mk LIB = lib$(SOC).a OBJS = i2c.o interrupts.o serial.o speed.o \ - usb_ohci.o + usb.o all: .depend $(LIB) diff --git a/cpu/arm920t/s3c24x0/usb.c b/cpu/arm920t/s3c24x0/usb.c new file mode 100644 index 000000000..8114034cc --- /dev/null +++ b/cpu/arm920t/s3c24x0/usb.c @@ -0,0 +1,72 @@ +/* + * (C) Copyright 2006 + * DENX Software Engineering + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 + +#if defined(CONFIG_USB_OHCI) && defined(CFG_USB_OHCI_CPU_INIT) +# if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410) + +#if defined(CONFIG_S3C2400) +# include +#elif defined(CONFIG_S3C2410) +# include +#endif + +int usb_cpu_init() +{ + + S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER(); + S3C24X0_GPIO * const gpio = S3C24X0_GetBase_GPIO(); + + /* + * Set the 48 MHz UPLL clocking. Values are taken from + * "PLL value selection guide", 6-23, s3c2400_UM.pdf. + */ + clk_power->UPLLCON = ((40 << 12) + (1 << 4) + 2); + gpio->MISCCR |= 0x8; /* 1 = use pads related USB for USB host */ + + /* + * Enable USB host clock. + */ + clk_power->CLKCON |= (1 << 4); + + return 0; +} + +int usb_cpu_stop() +{ + S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER(); + /* may not want to do this */ + clk_power->CLKCON &= ~(1 << 4); + return 0; +} + +int usb_cpu_init_fail() +{ + S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER(); + clk_power->CLKCON &= ~(1 << 4); + return 0; +} + +# endif /* defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410) */ +#endif /* defined(CONFIG_USB_OHCI) && defined(CFG_USB_OHCI_CPU_INIT) */ diff --git a/cpu/pxa/usb.c b/cpu/pxa/usb.c index 171615704..bff5bfb6a 100644 --- a/cpu/pxa/usb.c +++ b/cpu/pxa/usb.c @@ -66,8 +66,13 @@ int usb_cpu_stop() { /* may not want to do this */ /* CKENA &= ~(CKENA_2_USBHOST | CKENA_20_UDC); */ + return 0; +} +int usb_cpu_init_fail() +{ return 0; } + # endif /* CONFIG_CPU_MONAHANS */ #endif /* defined(CONFIG_USB_OHCI) && defined(CFG_USB_OHCI_CPU_INIT) */ diff --git a/drivers/usb_ohci.c b/drivers/usb_ohci.c index c32245a24..9b3ca1232 100644 --- a/drivers/usb_ohci.c +++ b/drivers/usb_ohci.c @@ -21,7 +21,7 @@ * * 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 + * 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 @@ -45,13 +45,25 @@ #ifdef CONFIG_USB_OHCI -#include +#if defined(CONFIG_S3C2400) +# include +#elif defined(CONFIG_S3C2410) +# include +#elif defined(CONFIG_ARM920T) +# include +#elif defined(CONFIG_CPU_MONAHANS) +# include +#endif #include #include #include "usb_ohci.h" -#ifdef CONFIG_ARM920T +#undef S3C24X0_merge + +#if defined(CONFIG_ARM920T) || \ + defined(CONFIG_S3C2400) || \ + defined(CONFIG_S3C2410) # define OHCI_USE_NPS /* force NoPowerSwitching mode */ #endif @@ -98,6 +110,12 @@ int got_rhsc; /* device which was disconnected */ struct usb_device *devgone; +#ifdef S3C24X0_merge +/* flag guarding URB transation */ +int urb_finished = 0; +#endif + + /*-------------------------------------------------------------------------*/ /* AMD-756 (D2 rev) reports corrupt register contents in some cases. @@ -402,6 +420,17 @@ int sohci_submit_job(struct usb_device *dev, unsigned long pipe, void *buffer, err("sohci_submit_job: EPIPE"); return -1; } +#ifdef S3C24X0_merge + /* if we have an unfinished URB from previous transaction let's + * fail and scream as quickly as possible so as not to corrupt + * further communication */ + if (!urb_finished) { + err("sohci_submit_job: URB NOT FINISHED"); + return -1; + } + /* we're about to begin a new transaction here so mark the URB unfinished */ + urb_finished = 0; +#endif /* every endpoint has a ed, locate and fill it */ if (!(ed = ep_add_ed (dev, pipe))) { @@ -574,12 +603,14 @@ static int ep_unlink (ohci_t *ohci, ed_t *ed) /*-------------------------------------------------------------------------*/ -/* add/reinit an endpoint; this should be done once at the usb_set_configuration command, - * but the USB stack is a little bit stateless so we do it at every transaction - * if the state of the ed is ED_NEW then a dummy td is added and the state is changed to ED_UNLINK - * in all other cases the state is left unchanged - * the ed info fields are setted anyway even though most of them should not change */ - +/* add/reinit an endpoint; this should be done once at the + * usb_set_configuration command, but the USB stack is a little bit + * stateless so we do it at every transaction if the state of the ed + * is ED_NEW then a dummy td is added and the state is changed to + * ED_UNLINK in all other cases the state is left unchanged the ed + * info fields are setted anyway even though most of them should not + * change + */ static ed_t * ep_add_ed (struct usb_device *usb_dev, unsigned long pipe) { td_t *td; @@ -663,7 +694,9 @@ static void td_fill (ohci_t *ohci, unsigned int info, else td->hwBE = 0; td->hwNextTD = m32_swap (td_pt); +#ifndef S3C24X0_merge td->hwPSW [0] = m16_swap (((__u32)data & 0x0FFF) | 0xE000); +#endif /* append to queue */ td->ed->hwTailP = td->hwNextTD; @@ -830,7 +863,18 @@ static int dl_done_list (ohci_t *ohci, td_t *td_list) dbg("ConditionCode %#x", cc); stat = cc_to_error[cc]; } - +#ifdef S3C24X0_merge + /* see if this done list makes for all TD's of current URB, + * and mark the URB finished if so */ + if (++(lurb_priv->td_cnt) == lurb_priv->length) { + if ((ed->state & (ED_OPER | ED_UNLINK))) + urb_finished = 1; + else + dbg("dl_done_list: strange.., ED state %x, ed->state\n"); + } else + dbg("dl_done_list: processing TD %x, len %x\n", lurb_priv->td_cnt, + lurb_priv->length); +#endif if (ed->state != ED_NEW) { edHeadP = m32_swap (ed->hwHeadP) & 0xfffffff0; edTailP = m32_swap (ed->hwTailP); @@ -1172,7 +1216,7 @@ pkt_print(dev, pipe, buffer, transfer_len, cmd, "SUB(rh)", usb_pipein(pipe)); } len = min_t(unsigned int, leni, - min_t(unsigned int, data_buf [0], wLength)); + min_t(unsigned int, data_buf [0], wLength)); OK (len); } @@ -1260,18 +1304,38 @@ int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer, stat = USB_ST_CRC_ERR; break; } + +#ifdef S3C24X0_merge + /* NOTE: since we are not interrupt driven in U-Boot and always + * handle only one URB at a time, we cannot assume the + * transaction finished on the first successful return from + * hc_interrupt().. unless the flag for current URB is set, + * meaning that all TD's to/from device got actually + * transferred and processed. If the current URB is not + * finished we need to re-iterate this loop so as + * hc_interrupt() gets called again as there needs to be some + * more TD's to process still */ + if ((stat >= 0) && (stat != 0xff) && (urb_finished)) { +#else if (stat >= 0 && stat != 0xff) { +#endif /* 0xff is returned for an SF-interrupt */ break; } + if (--timeout) { wait_ms(1); } else { err("CTL:TIMEOUT "); +#ifdef S3C24X0_merge + dbg("submit_common_msg: TO status %x\n", stat); + urb_finished = 1; +#endif stat = USB_ST_CRC_ERR; break; } } +#ifndef S3C24X0_merge /* we got an Root Hub Status Change interrupt */ if (got_rhsc) { #ifdef DEBUG @@ -1293,6 +1357,7 @@ int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer, devgone = dev; } } +#endif /* S3C24X0_merge */ dev->status = stat; dev->act_len = transfer_len; @@ -1462,24 +1527,40 @@ static int hc_start (ohci_t * ohci) /* an interrupt happens */ -static int -hc_interrupt (void) +static int hc_interrupt (void) { ohci_t *ohci = &gohci; struct ohci_regs *regs = ohci->regs; int ints; int stat = -1; +#ifdef S3C24X0_merge + + if ((ohci->hcca->done_head != 0) && + !(m32_swap (ohci->hcca->done_head) & 0x01)) { + ints = OHCI_INTR_WDH; + } else if ((ints = readl (®s->intrstatus)) == ~(u32)0) { + ohci->disabled++; + err ("%s device removed!", ohci->slot_name); + return -1; + } else if ((ints &= readl (®s->intrenable)) == 0) { + dbg("hc_interrupt: returning..\n"); + return 0xff; + } +#else if ((ohci->hcca->done_head != 0) && !(m32_swap (ohci->hcca->done_head) & 0x01)) { ints = OHCI_INTR_WDH; } else { ints = readl (®s->intrstatus); } - +#endif /* dbg("Interrupt: %x frame: %x", ints, le16_to_cpu (ohci->hcca->frame_no)); */ if (ints & OHCI_INTR_RHSC) { got_rhsc = 1; +#ifdef S3C24X0_merge + stat = 0xff; +#endif } if (ints & OHCI_INTR_UE) { @@ -1552,13 +1633,13 @@ static char ohci_inited = 0; int usb_lowlevel_init(void) { -#if CFG_USB_OHCI_CPU_INIT +#ifdef CFG_USB_OHCI_CPU_INIT /* cpu dependant init */ if(usb_cpu_init()) return -1; #endif -#if CFG_USB_OHCI_BOARD_INIT +#ifdef CFG_USB_OHCI_BOARD_INIT /* board dependant init */ if(usb_board_init()) return -1; @@ -1598,15 +1679,14 @@ int usb_lowlevel_init(void) if (hc_reset (&gohci) < 0) { hc_release_ohci (&gohci); err ("can't reset usb-%s", gohci.slot_name); - /* Initialization failed disable clocks */ -#if CFG_USB_OHCI_BOARD_INIT +#ifdef CFG_USB_OHCI_BOARD_INIT /* board dependant cleanup */ - usb_board_stop(); + usb_board_init_fail(); #endif -#if CFG_USB_OHCI_CPU_INIT +#ifdef CFG_USB_OHCI_CPU_INIT /* cpu dependant cleanup */ - usb_cpu_stop(); + usb_cpu_init_fail(); #endif return -1; } @@ -1618,12 +1698,12 @@ int usb_lowlevel_init(void) err ("can't start usb-%s", gohci.slot_name); hc_release_ohci (&gohci); /* Initialization failed */ -#if CFG_USB_OHCI_BOARD_INIT +#ifdef CFG_USB_OHCI_BOARD_INIT /* board dependant cleanup */ usb_board_stop(); #endif -#if CFG_USB_OHCI_CPU_INIT +#ifdef CFG_USB_OHCI_CPU_INIT /* cpu dependant cleanup */ usb_cpu_stop(); #endif @@ -1634,6 +1714,9 @@ int usb_lowlevel_init(void) ohci_dump (&gohci, 1); #else wait_ms(1); +# ifdef S3C24X0_merge + urb_finished = 1; +# endif #endif ohci_inited = 1; return 0; @@ -1649,13 +1732,13 @@ int usb_lowlevel_stop(void) /* call hc_release_ohci() here ? */ hc_reset (&gohci); -#if CFG_USB_OHCI_BOARD_INIT +#ifdef CFG_USB_OHCI_BOARD_INIT /* board dependant cleanup */ if(usb_board_stop()) return -1; #endif -#if CFG_USB_OHCI_CPU_INIT +#ifdef CFG_USB_OHCI_CPU_INIT /* cpu dependant cleanup */ if(usb_cpu_stop()) return -1; diff --git a/drivers/usb_ohci.h b/drivers/usb_ohci.h index c37b5f600..a1b36ed83 100644 --- a/drivers/usb_ohci.h +++ b/drivers/usb_ohci.h @@ -11,11 +11,13 @@ #ifdef CFG_USB_BOARD_INIT extern int usb_board_init(void); extern int usb_board_stop(void); +extern int usb_cpu_init_fail(void); #endif #ifdef CFG_USB_CPU_INIT extern int usb_cpu_init(void); extern int usb_cpu_stop(void); +extern int usb_cpu_init_fail(void); #endif diff --git a/include/configs/trab.h b/include/configs/trab.h index 85ee756e0..b6d4bcf17 100644 --- a/include/configs/trab.h +++ b/include/configs/trab.h @@ -84,6 +84,11 @@ #define CONFIG_USB_STORAGE 1 #define CONFIG_DOS_PARTITION 1 +#undef CFG_USB_OHCI_BOARD_INIT +#define CFG_USB_OHCI_CPU_INIT 1 +#define CFG_USB_OHCI_REGS_BASE S3C24X0_USB_HOST_BASE +#define CFG_USB_OHCI_SLOT_NAME "s3c2400" + /* * Size of malloc() pool */ -- cgit v1.2.3 From 16c8d5e76ae0f78f39a60608574adfe0feb9cc70 Mon Sep 17 00:00:00 2001 From: Wolfgang Denk Date: Wed, 14 Jun 2006 17:45:53 +0200 Subject: Various USB related patches - Add support for mpc8xx USB device. - Add support for Common Device Class - Abstract Control Model USB console. - Add support for flow control in USB slave devices. - Add support for switching between gserial and cdc_acm using environment. - Minor changes to usbdcore_omap1510.c usbdcore_omap1510.h - Update usbcore slightly to ease host enumeration. - Fix non-portable endian problems in usbdcore and usbdcore_ep0. - Add AdderUSB_config as a defconfig to enable usage of the USB console by default with the Adder87x U-Boot port. Patches by Bryan O'Donoghue , 29 May 2006 --- CHANGELOG | 12 + Makefile | 5 +- README | 64 +- drivers/Makefile | 2 +- drivers/usbdcore_ep0.c | 163 ++--- drivers/usbdcore_mpc8xx.c | 1412 +++++++++++++++++++++++++++++++++++++++++++ drivers/usbdcore_omap1510.c | 29 +- drivers/usbtty.c | 707 ++++++++++++++++------ drivers/usbtty.h | 60 +- include/configs/AdderUSB.h | 51 ++ include/usb_cdc_acm.h | 43 ++ include/usbdcore.h | 3 + include/usbdcore_mpc8xx.h | 210 +++++++ include/usbdcore_omap1510.h | 18 +- include/usbdescriptors.h | 38 +- 15 files changed, 2467 insertions(+), 350 deletions(-) create mode 100644 drivers/usbdcore_mpc8xx.c create mode 100644 include/configs/AdderUSB.h create mode 100644 include/usb_cdc_acm.h create mode 100644 include/usbdcore_mpc8xx.h (limited to 'drivers') diff --git a/CHANGELOG b/CHANGELOG index 4aab6fc04..63d327ab1 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,6 +2,18 @@ Changes since U-Boot 1.1.4: ====================================================================== +* Various USB related patches + - Add support for mpc8xx USB device. + - Add support for Common Device Class - Abstract Control Model USB console. + - Add support for flow control in USB slave devices. + - Add support for switching between gserial and cdc_acm using environment. + - Minor changes to usbdcore_omap1510.c usbdcore_omap1510.h + - Update usbcore slightly to ease host enumeration. + - Fix non-portable endian problems in usbdcore and usbdcore_ep0. + - Add AdderUSB_config as a defconfig to enable usage of the USB console + by default with the Adder87x U-Boot port. + Patch by Bryan O'Donoghue , 29 May 2006 + * Fix PCI to memory window size problems on PM82x boards We use the "automatic" mode that was used for the MPC8266ADS and MPC8272 boards. Eventually this should be used on all boards?] diff --git a/Makefile b/Makefile index 41a3624ff..45e850c5f 100644 --- a/Makefile +++ b/Makefile @@ -58,7 +58,7 @@ ifeq ($(HOSTARCH),ppc) CROSS_COMPILE = else ifeq ($(ARCH),ppc) -CROSS_COMPILE = powerpc-linux- +CROSS_COMPILE = ppc_8xx- endif ifeq ($(ARCH),arm) CROSS_COMPILE = arm-linux- @@ -437,6 +437,9 @@ AdderII_config \ @echo "#define CONFIG_MPC852T" > include/config.h) @./mkconfig -a Adder ppc mpc8xx adder +AdderUSB_config: unconfig + @./mkconfig -a AdderUSB ppc mpc8xx adder + ADS860_config \ FADS823_config \ FADS850SAR_config \ diff --git a/README b/README index 3ffef6253..25e8971bc 100644 --- a/README +++ b/README @@ -862,7 +862,69 @@ The following options need to be configured: for differential drivers: 0x00001000 for single ended drivers: 0x00005000 - +- USB Device: + Define the below if you wish to use the USB console. + Once firmware is rebuilt from a serial console issue the + command "setenv stdin usbtty; setenv stdout usbtty" and + attach your usb cable. The Unix command "dmesg" should print + it has found a new device. The environment variable usbtty + can be set to gserial or cdc_acm to enable your device to + appear to a USB host as a Linux gserial device or a + Common Device Class Abstract Control Model serial device. + If you select usbtty = gserial you should be able to enumerate + a Linux host by + # modprobe usbserial vendor=0xVendorID product=0xProductID + else if using cdc_acm, simply setting the environment + variable usbtty to be cdc_acm should suffice. The following + might be defined in YourBoardName.h + + CONFIG_USB_DEVICE + Define this to build a UDC device + + CONFIG_USB_TTY + Define this to have a tty type of device available to + talk to the UDC device + + CFG_CONSOLE_IS_IN_ENV + Define this if you want stdin, stdout &/or stderr to + be set to usbtty. + + mpc8xx: + CFG_USB_EXTC_CLK 0xBLAH + Derive USB clock from external clock "blah" + - CFG_USB_EXTC_CLK 0x02 + + CFG_USB_BRG_CLK 0xBLAH + Derive USB clock from brgclk + - CFG_USB_BRG_CLK 0x04 + + If you have a USB-IF assigned VendorID then you may wish to + define your own vendor specific values either in BoardName.h + or directly in usbd_vendor_info.h. If you don't define + CONFIG_USBD_MANUFACTURER, CONFIG_USBD_PRODUCT_NAME, + CONFIG_USBD_VENDORID and CONFIG_USBD_PRODUCTID, then U-Boot + should pretend to be a Linux device to it's target host. + + CONFIG_USBD_MANUFACTURER + Define this string as the name of your company for + - CONFIG_USBD_MANUFACTURER "my company" + + CONFIG_USBD_PRODUCT_NAME + Define this string as the name of your product + - CONFIG_USBD_PRODUCT_NAME "acme usb device" + + CONFIG_USBD_VENDORID + Define this as your assigned Vendor ID from the USB + Implementors Forum. This *must* be a genuine Vendor ID + to avoid polluting the USB namespace. + - CONFIG_USBD_VENDORID 0xFFFF + + CONFIG_USBD_PRODUCTID + Define this as the unique Product ID + for your device + - CONFIG_USBD_PRODUCTID 0xFFFF + + - MMC Support: The MMC controller on the Intel PXA is supported. To enable this define CONFIG_MMC. The MMC can be diff --git a/drivers/Makefile b/drivers/Makefile index d5b681182..8732e1626 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -48,7 +48,7 @@ OBJS = 3c589.o 5701rls.o ali512x.o \ ti_pci1410a.o tigon3.o tsec.o \ usb_ohci.o usbdcore.o usbdcore_ep0.o usbdcore_omap1510.o usbtty.o \ videomodes.o w83c553f.o \ - ks8695eth.o + ks8695eth.o usbdcore_mpc8xx.o all: $(LIB) diff --git a/drivers/usbdcore_ep0.c b/drivers/usbdcore_ep0.c index 260befe97..5e7443be8 100644 --- a/drivers/usbdcore_ep0.c +++ b/drivers/usbdcore_ep0.c @@ -2,6 +2,9 @@ * (C) Copyright 2003 * Gerry Hamel, geh@ti.com, Texas Instruments * + * (C) Copyright 2006 + * Bryan O'Donoghue, deckard@CodeHermit.ie + * * Based on * linux/drivers/usbd/ep0.c * @@ -39,11 +42,17 @@ * function driver. This may need to change. * * XXX + * + * As alluded to above, a simple callback cdc_recv_setup has been implemented + * in the usb_device data structure to facilicate passing + * Common Device Class packets to a function driver. + * + * XXX */ #include -#if defined(CONFIG_OMAP1510) && defined(CONFIG_USB_DEVICE) +#if defined(CONFIG_USB_DEVICE) #include "usbdcore.h" #if 0 @@ -69,7 +78,7 @@ static int ep0_get_status (struct usb_device_instance *device, char *cp; urb->actual_length = 2; - cp = urb->buffer; + cp = (char*)urb->buffer; cp[0] = cp[1] = 0; switch (requesttype) { @@ -115,7 +124,7 @@ static int ep0_get_one (struct usb_device_instance *device, struct urb *urb, * * Copy configuration data to urb transfer buffer if there is room for it. */ -static void copy_config (struct urb *urb, void *data, int max_length, +void copy_config (struct urb *urb, void *data, int max_length, int max_buf) { int available; @@ -128,10 +137,7 @@ static void copy_config (struct urb *urb, void *data, int max_length, dbg_ep0 (1, "data is NULL"); return; } - if (!(length = *(unsigned char *) data)) { - dbg_ep0 (1, "length is zero"); - return; - } + length = max_length; if (length > max_length) { dbg_ep0 (1, "length: %d >= max_length: %d", length, @@ -192,7 +198,7 @@ static int ep0_get_descriptor (struct usb_device_instance *device, /* setup tx urb */ urb->actual_length = 0; - cp = urb->buffer; + cp = (char*)urb->buffer; dbg_ep0 (2, "%s", USBD_DEVICE_DESCRIPTORS (descriptor_type)); @@ -200,7 +206,6 @@ static int ep0_get_descriptor (struct usb_device_instance *device, case USB_DESCRIPTOR_TYPE_DEVICE: { struct usb_device_descriptor *device_descriptor; - if (! (device_descriptor = usbd_device_device_descriptor (device, port))) { @@ -214,20 +219,16 @@ static int ep0_get_descriptor (struct usb_device_instance *device, /* correct the correct control endpoint 0 max packet size into the descriptor */ device_descriptor = (struct usb_device_descriptor *) urb->buffer; - device_descriptor->bMaxPacketSize0 = - urb->device->bus->maxpacketsize; } - /*dbg_ep0(3, "copied device configuration, actual_length: %x", urb->actual_length); */ + dbg_ep0(3, "copied device configuration, actual_length: 0x%x", urb->actual_length); break; case USB_DESCRIPTOR_TYPE_CONFIGURATION: { - int bNumInterface; struct usb_configuration_descriptor *configuration_descriptor; struct usb_device_descriptor *device_descriptor; - if (! (device_descriptor = usbd_device_device_descriptor (device, port))) { @@ -251,130 +252,35 @@ static int ep0_get_descriptor (struct usb_device_instance *device, index); return -1; } + dbg_ep0(0, "attempt to copy %d bytes to urb\n",cpu_to_le16(configuration_descriptor->wTotalLength)); copy_config (urb, configuration_descriptor, - sizeof (struct - usb_configuration_descriptor), - max); - - - /* iterate across interfaces for specified configuration */ - dbg_ep0 (0, "bNumInterfaces: %d", - configuration_descriptor->bNumInterfaces); - for (bNumInterface = 0; - bNumInterface < - configuration_descriptor->bNumInterfaces; - bNumInterface++) { - int bAlternateSetting; - struct usb_interface_instance - *interface_instance; - - dbg_ep0 (3, "[%d] bNumInterfaces: %d", - bNumInterface, - configuration_descriptor->bNumInterfaces); - - if (! (interface_instance = usbd_device_interface_instance (device, - port, index, bNumInterface))) - { - dbg_ep0 (3, "[%d] interface_instance NULL", - bNumInterface); - return -1; - } - /* iterate across interface alternates */ - for (bAlternateSetting = 0; - bAlternateSetting < interface_instance->alternates; - bAlternateSetting++) { - /*int class; */ - int bNumEndpoint; - struct usb_interface_descriptor *interface_descriptor; - - struct usb_alternate_instance *alternate_instance; - - dbg_ep0 (3, "[%d:%d] alternates: %d", - bNumInterface, - bAlternateSetting, - interface_instance->alternates); - - if (! (alternate_instance = usbd_device_alternate_instance (device, port, index, bNumInterface, bAlternateSetting))) { - dbg_ep0 (3, "[%d] alternate_instance NULL", - bNumInterface); - return -1; - } - /* copy descriptor for this interface */ - copy_config (urb, alternate_instance->interface_descriptor, - sizeof (struct usb_interface_descriptor), - max); - - /*dbg_ep0(3, "[%d:%d] classes: %d endpoints: %d", bNumInterface, bAlternateSetting, */ - /* alternate_instance->classes, alternate_instance->endpoints); */ - - /* iterate across classes for this alternate interface */ -#if 0 - for (class = 0; - class < alternate_instance->classes; - class++) { - struct usb_class_descriptor *class_descriptor; - /*dbg_ep0(3, "[%d:%d:%d] classes: %d", bNumInterface, bAlternateSetting, */ - /* class, alternate_instance->classes); */ - if (!(class_descriptor = usbd_device_class_descriptor_index (device, port, index, bNumInterface, bAlternateSetting, class))) { - dbg_ep0 (3, "[%d] class NULL", - class); - return -1; - } - /* copy descriptor for this class */ - copy_config (urb, class_descriptor, - sizeof (struct usb_class_descriptor), - max); - } -#endif - - /* iterate across endpoints for this alternate interface */ - interface_descriptor = alternate_instance->interface_descriptor; - for (bNumEndpoint = 0; - bNumEndpoint < alternate_instance->endpoints; - bNumEndpoint++) { - struct usb_endpoint_descriptor *endpoint_descriptor; - dbg_ep0 (3, "[%d:%d:%d] endpoint: %d", - bNumInterface, - bAlternateSetting, - bNumEndpoint, - interface_descriptor-> - bNumEndpoints); - if (!(endpoint_descriptor = usbd_device_endpoint_descriptor_index (device, port, index, bNumInterface, bAlternateSetting, bNumEndpoint))) { - dbg_ep0 (3, "[%d] endpoint NULL", - bNumEndpoint); - return -1; - } - /* copy descriptor for this endpoint */ - copy_config (urb, endpoint_descriptor, - sizeof (struct usb_endpoint_descriptor), - max); - } - } - } - dbg_ep0 (3, "lengths: %d %d", - le16_to_cpu (configuration_descriptor->wTotalLength), - urb->actual_length); + cpu_to_le16(configuration_descriptor->wTotalLength), + max); } + break; case USB_DESCRIPTOR_TYPE_STRING: { struct usb_string_descriptor *string_descriptor; - if (!(string_descriptor = usbd_get_string (index))) { + serial_printf("Invalid string index %d\n", index); return -1; } - /*dbg_ep0(3, "string_descriptor: %p", string_descriptor); */ + dbg_ep0(3, "string_descriptor: %p length %d", string_descriptor, string_descriptor->bLength); copy_config (urb, string_descriptor, string_descriptor->bLength, max); } break; case USB_DESCRIPTOR_TYPE_INTERFACE: + serial_printf("USB_DESCRIPTOR_TYPE_INTERFACE - error not implemented\n"); return -1; case USB_DESCRIPTOR_TYPE_ENDPOINT: + serial_printf("USB_DESCRIPTOR_TYPE_ENDPOINT - error not implemented\n"); return -1; case USB_DESCRIPTOR_TYPE_HID: { + serial_printf("USB_DESCRIPTOR_TYPE_HID - error not implemented\n"); return -1; /* unsupported at this time */ #if 0 int bNumInterface = @@ -403,6 +309,7 @@ static int ep0_get_descriptor (struct usb_device_instance *device, break; case USB_DESCRIPTOR_TYPE_REPORT: { + serial_printf("USB_DESCRIPTOR_TYPE_REPORT - error not implemented\n"); return -1; /* unsupported at this time */ #if 0 int bNumInterface = @@ -434,12 +341,19 @@ static int ep0_get_descriptor (struct usb_device_instance *device, #endif } break; + case USB_DESCRIPTOR_TYPE_DEVICE_QUALIFIER: + { + /* If a USB device supports both a full speed and low speed operation + * we must send a Device_Qualifier descriptor here + */ + return -1; + } default: return -1; } - dbg_ep0 (1, "urb: buffer: %p buffer_length: %2d actual_length: %2d packet size: %2d", + dbg_ep0 (1, "urb: buffer: %p buffer_length: %2d actual_length: %2d tx_packetSize: %2d", urb->buffer, urb->buffer_length, urb->actual_length, device->bus->endpoint_array[0].tx_packetSize); /* @@ -495,6 +409,12 @@ int ep0_recv_setup (struct urb *urb) /* handle USB Standard Request (c.f. USB Spec table 9-2) */ if ((request->bmRequestType & USB_REQ_TYPE_MASK) != 0) { + if(device->device_state <= STATE_CONFIGURED){ + /* Attempt to handle a CDC specific request if we are + * in the configured state. + */ + return device->cdc_recv_setup(request,urb); + } dbg_ep0 (1, "non standard request: %x", request->bmRequestType & USB_REQ_TYPE_MASK); return -1; /* Stall here */ @@ -567,6 +487,7 @@ int ep0_recv_setup (struct urb *urb) le16_to_cpu (request->wValue) & 0xff); case USB_REQ_GET_CONFIGURATION: + serial_printf("get config %d\n", device->configuration); return ep0_get_one (device, urb, device->configuration); @@ -642,7 +563,6 @@ int ep0_recv_setup (struct urb *urb) /*dbg_ep0(2, "address: %d %d %d", */ /* request->wValue, le16_to_cpu(request->wValue), device->address); */ - serial_printf ("DEVICE_ADDRESS_ASSIGNED.. event?\n"); return 0; case USB_REQ_SET_DESCRIPTOR: /* XXX should we support this? */ @@ -653,9 +573,10 @@ int ep0_recv_setup (struct urb *urb) /* c.f. 9.4.7 - the top half of wValue is reserved */ /* */ if ((device->configuration = - le16_to_cpu (request->wValue) & 0x7f) != 0) { + le16_to_cpu (request->wValue) & 0xFF80) != 0) { /* c.f. 9.4.7 - zero is the default or addressed state, in our case this */ /* is the same is configuration zero */ + serial_printf("error setting dev->config to zero!\n"); device->configuration = 0; /* TBR - ?????? */ } /* reset interface and alternate settings */ diff --git a/drivers/usbdcore_mpc8xx.c b/drivers/usbdcore_mpc8xx.c new file mode 100644 index 000000000..9bd2c231a --- /dev/null +++ b/drivers/usbdcore_mpc8xx.c @@ -0,0 +1,1412 @@ +/* + * Copyright (C) 2006 by Bryan O'Donoghue, CodeHermit + * bodonoghue@CodeHermit.ie + * + * References + * DasUBoot/drivers/usbdcore_omap1510.c, for design and implementation ideas. + * + * 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. + * + */ + +/* + * Notes : + * 1. #define __SIMULATE_ERROR__ to inject a CRC error into every 2nd TX + * packet to force the USB re-transmit protocol. + * + * 2. #define __DEBUG_UDC__ to switch on debug tracing to serial console + * be careful that tracing doesn't create Hiesen-bugs with respect to + * response timeouts to control requests. + * + * 3. This driver should be able to support any higher level driver that + * that wants to do either of the two standard UDC implementations + * Control-Bulk-Interrupt or Bulk-IN/Bulk-Out standards. Hence + * gserial and cdc_acm should work with this code. + * + * 4. NAK events never actually get raised at all, the documentation + * is just wrong ! + * + * 5. For some reason, cbd_datlen is *always* +2 the value it should be. + * this means that having an RX cbd of 16 bytes is not possible, since + * the same size is reported for 14 bytes received as 16 bytes received + * until we can find out why this happens, RX cbds must be limited to 8 + * bytes. TODO: check errata for this behaviour. + * + * 6. Right now this code doesn't support properly powering up with the USB + * cable attached to the USB host my development board the Adder87x doesn't + * have a pull-up fitted to allow this, so it is necessary to power the + * board and *then* attached the USB cable to the host. However somebody + * with a different design in their board may be able to keep the cable + * constantly connected and simply enable/disable a pull-up re + * figure 31.1 in MPC885RM.pdf instead of having to power up the board and + * then attach the cable ! + * + */ +#include +#include + +#if defined(CONFIG_MPC885_FAMILY) && defined(CONFIG_USB_DEVICE) +#include +#include "usbdcore.h" +#include "usbdcore_mpc8xx.h" +#include "usbdcore_ep0.h" + +#define ERR(fmt, args...)\ + serial_printf("ERROR : [%s] %s:%d: "fmt,\ + __FILE__,__FUNCTION__,__LINE__, ##args) +#ifdef __DEBUG_UDC__ + #define DBG(fmt,args...)\ + serial_printf("[%s] %s:%d: "fmt,\ + __FILE__,__FUNCTION__,__LINE__, ##args) +#else + #define DBG(fmt,args...) +#endif + +/* Static Data */ +#ifdef __SIMULATE_ERROR__ + static char err_poison_test = 0; +#endif +static struct mpc8xx_ep ep_ref[MAX_ENDPOINTS]; +static u32 address_base = STATE_NOT_READY; +static mpc8xx_udc_state_t udc_state = 0; +static struct usb_device_instance *udc_device = 0; +static volatile usb_epb_t *endpoints[MAX_ENDPOINTS]; +static volatile cbd_t * tx_cbd[TX_RING_SIZE]; +static volatile cbd_t * rx_cbd[RX_RING_SIZE]; +static volatile immap_t *immr = 0; +static volatile cpm8xx_t *cp = 0; +static volatile usb_pram_t *usb_paramp = 0; +static volatile usb_t *usbp = 0; +static int rx_ct = 0; +static int tx_ct = 0; + +/* Static Function Declarations */ +static void mpc8xx_udc_state_transition_up (usb_device_state_t initial, + usb_device_state_t final); +static void mpc8xx_udc_state_transition_down (usb_device_state_t initial, + usb_device_state_t final); +static void mpc8xx_udc_stall (unsigned int ep); +static void mpc8xx_udc_flush_tx_fifo(int epid); +static void mpc8xx_udc_flush_rx_fifo(void); +static void mpc8xx_udc_clear_rxbd (volatile cbd_t * rx_cbdp); +static void mpc8xx_udc_init_tx(struct usb_endpoint_instance *epi, + struct urb * tx_urb); +static void mpc8xx_udc_dump_request(struct usb_device_request *request); +static void mpc8xx_udc_clock_init (volatile immap_t * immr, + volatile cpm8xx_t * cp); +static int mpc8xx_udc_ep_tx (struct usb_endpoint_instance *epi); +static int mpc8xx_udc_epn_rx (unsigned int epid, volatile cbd_t * rx_cbdp); +static void mpc8xx_udc_ep0_rx(volatile cbd_t * rx_cbdp); +static void mpc8xx_udc_cbd_init (void); +static void mpc8xx_udc_endpoint_init (void); +static void mpc8xx_udc_cbd_attach (int ep, uchar tx_size, uchar rx_size); +static u32 mpc8xx_udc_alloc (u32 data_size, u32 alignment); +static int mpc8xx_udc_ep0_rx_setup (volatile cbd_t * rx_cbdp); +static void mpc8xx_udc_set_nak (unsigned int ep); +static short mpc8xx_udc_handle_txerr(void); +static void mpc8xx_udc_advance_rx(volatile cbd_t ** rx_cbdp, int epid); + +/****************************************************************************** + Global Linkage + *****************************************************************************/ + +/* udc_init + * + * Do initial bus gluing + */ +int udc_init(void) +{ + /* Init various pointers */ + immr = (immap_t *) CFG_IMMR; + cp = (cpm8xx_t *)&(immr->im_cpm); + usb_paramp = (usb_pram_t*)&(cp->cp_dparam[PROFF_USB]); + usbp = (usb_t *) &(cp->cp_scc[0]); + + memset(ep_ref, 0x00, (sizeof(struct mpc8xx_ep) * MAX_ENDPOINTS)); + + udc_device = 0; + udc_state = STATE_NOT_READY; + + usbp->usmod= 0x00; + usbp->uscom= 0; + + /* Set USB Frame #0, Respond at Address & Get a clock source */ + usbp->usaddr = 0x00; + mpc8xx_udc_clock_init (immr, cp); + + /* PA15, PA14 as perhiperal USBRXD and USBOE */ + immr->im_ioport.iop_padir&= ~0x0003; + immr->im_ioport.iop_papar|= 0x0003; + + /* PC11/PC10 as peripheral USBRXP USBRXN */ + immr->im_ioport.iop_pcso|= 0x0030; + + /* PC7/PC6 as perhiperal USBTXP and USBTXN */ + immr->im_ioport.iop_pcdir|= 0x0300; + immr->im_ioport.iop_pcpar|= 0x0300; + + /* Set the base address */ + address_base = (u32)(cp->cp_dpmem + CPM_USB_BASE); + + /* Initialise endpoints and circular buffers */ + mpc8xx_udc_endpoint_init(); + mpc8xx_udc_cbd_init(); + + /* Assign allocated Dual Port Endpoint descriptors */ + usb_paramp->ep0ptr = (u32)endpoints[0]; + usb_paramp->ep1ptr = (u32)endpoints[1]; + usb_paramp->ep2ptr = (u32)endpoints[2]; + usb_paramp->ep3ptr = (u32)endpoints[3]; + usb_paramp->frame_n = 0; + + DBG("ep0ptr=0x%08x ep1ptr=0x%08x ep2ptr=0x%08x ep3ptr=0x%08x\n", + usb_paramp->ep0ptr, usb_paramp->ep1ptr, usb_paramp->ep2ptr, + usb_paramp->ep3ptr); + + return 0; +} + +/* udc_irq + * + * Poll for whatever events may have occured + */ +void udc_irq(void) +{ + int epid = 0; + volatile cbd_t * rx_cbdp = 0; + volatile cbd_t * rx_cbdp_base = 0; + + if(udc_state!=STATE_READY){ + return; + } + + if(usbp->usber&USB_E_BSY){ + /* This shouldn't happen. If it does then it's a bug ! */ + usbp->usber|=USB_E_BSY; + mpc8xx_udc_flush_rx_fifo(); + } + + + /* Scan all RX/Bidirectional Endpoints for RX data. */ + for(epid = 0; epidcbd_sc&RX_BD_E)){ + + if(rx_cbdp->cbd_sc&0x1F){ + /* Corrupt data discard it. + * Controller has NAK'd this packet. + */ + mpc8xx_udc_clear_rxbd(rx_cbdp); + + }else{ + if(!epid){ + mpc8xx_udc_ep0_rx(rx_cbdp); + + }else{ + /* Process data */ + mpc8xx_udc_set_nak(epid); + mpc8xx_udc_epn_rx(epid,rx_cbdp); + mpc8xx_udc_clear_rxbd(rx_cbdp); + } + } + + /* Advance RX CBD pointer */ + mpc8xx_udc_advance_rx(&rx_cbdp, epid); + ep_ref[epid].prx = rx_cbdp; + }else{ + /* Advance RX CBD pointer */ + mpc8xx_udc_advance_rx(&rx_cbdp, epid); + } + + }while(rx_cbdp != rx_cbdp_base); + } + + /* Handle TX events as appropiate, the correct place to do this is + * in a tx routine. Perhaps TX on epn was pre-empted by ep0 + */ + + if(usbp->usber&USB_E_TXB){ + usbp->usber|=USB_E_TXB; + } + + if(usbp->usber&(USB_TX_ERRMASK)){ + mpc8xx_udc_handle_txerr(); + } + + /* Switch to the default state, respond at the default address */ + if(usbp->usber&USB_E_RESET){ + usbp->usber|=USB_E_RESET; + usbp->usaddr = 0x00; + udc_device->device_state = STATE_DEFAULT; + } + + /*if(usbp->usber&USB_E_IDLE){ + We could suspend here ! + usbp->usber|=USB_E_IDLE; + DBG("idle state change\n"); + } + if(usbp->usbs){ + We could resume here when IDLE is deasserted ! + Not worth doing, so long as we are self powered though. + }*/ + + return; +} + + + +/* udc_endpoint_write + * + * Write some data to an endpoint + */ +int udc_endpoint_write(struct usb_endpoint_instance *epi) +{ + int ep = 0; + short epid = 1, unnak = 0, ret = 0; + + if(udc_state != STATE_READY){ + ERR("invalid udc_state != STATE_READY!\n"); + return -1; + } + + if(!udc_device || !epi){ + return -1; + } + + if(udc_device->device_state!=STATE_CONFIGURED){ + return -1; + } + + ep = epi->endpoint_address & 0x03; + if(ep >= MAX_ENDPOINTS){ + return -1; + } + + /* Set NAK for all RX endpoints during TX */ + for(epid = 1; epidusep[epid]&( USEP_THS_NAK | USEP_RHS_NAK ))){ + unnak |= 1<bus->endpoint_array[ep],epi->tx_urb); + ret = mpc8xx_udc_ep_tx(&udc_device->bus->endpoint_array[ep]); + + /* Remove temporary NAK */ + for(epid = 1; epid= MAX_ENDPOINTS){ + goto err; + } + epi = &udc_device->bus->endpoint_array[ep]; + if(!epi){ + goto err; + } + + if(!ep_ref[ep].urb){ + ep_ref[ep].urb = usbd_alloc_urb(udc_device, + udc_device->bus->endpoint_array); + if(!ep_ref[ep].urb){ + goto err; + } + }else{ + ep_ref[ep].urb->actual_length = 0; + } + + switch(direction){ + case USB_DIR_IN: + epi->tx_urb = ep_ref[ep].urb; + break; + case USB_DIR_OUT: + epi->rcv_urb = ep_ref[ep].urb; + break; + default: + goto err; + } + return 0; + +err: + udc_state = STATE_ERROR; + return -1; +} + +/* udc_setup_ep + * + * Associate U-Boot software endpoints to mpc8xx endpoint parameter ram + * Isochronous endpoints aren't yet supported! + */ +void udc_setup_ep(struct usb_device_instance *device, unsigned int ep, + struct usb_endpoint_instance *epi) +{ + uchar direction = 0; + int ep_attrib = 0; + + if(epi && (ep < MAX_ENDPOINTS)){ + + if(ep == 0){ + if (epi->rcv_attributes!=USB_ENDPOINT_XFER_CONTROL + ||epi->tx_attributes!= + USB_ENDPOINT_XFER_CONTROL){ + + /* ep0 must be a control endpoint*/ + udc_state = STATE_ERROR; + return; + + } + if(!(ep_ref[ep].sc & EP_ATTACHED)){ + mpc8xx_udc_cbd_attach (ep, epi->tx_packetSize, + epi->rcv_packetSize); + } + usbp->usep[ep] = 0x0000; + return; + } + + if ((epi->endpoint_address & USB_ENDPOINT_DIR_MASK) + == USB_DIR_IN) { + + direction = 1; + ep_attrib = epi->tx_attributes; + epi->rcv_packetSize = 0; + ep_ref[ep].sc |= USB_DIR_IN; + } else { + + direction = 0; + ep_attrib = epi->rcv_attributes; + epi->tx_packetSize = 0; + ep_ref[ep].sc &= ~USB_DIR_IN; + } + + if(mpc8xx_udc_assign_urb(ep, epi->endpoint_address + &USB_ENDPOINT_DIR_MASK)){ + return; + } + + switch(ep_attrib){ + case USB_ENDPOINT_XFER_CONTROL: + if(!(ep_ref[ep].sc & EP_ATTACHED)){ + mpc8xx_udc_cbd_attach (ep, + epi->tx_packetSize, + epi->rcv_packetSize); + } + usbp->usep[ep] = ep<<12; + epi->rcv_urb = epi->tx_urb = ep_ref[ep].urb; + + break; + case USB_ENDPOINT_XFER_BULK : + case USB_ENDPOINT_XFER_INT: + if(!(ep_ref[ep].sc & EP_ATTACHED)){ + if(direction){ + mpc8xx_udc_cbd_attach (ep, + epi->tx_packetSize, 0); + }else{ + mpc8xx_udc_cbd_attach (ep, + 0, epi->rcv_packetSize); + } + } + usbp->usep[ep]= (ep<<12)|((ep_attrib)<<8); + + break; + case USB_ENDPOINT_XFER_ISOC: + default: + serial_printf("Error endpoint attrib %d>3\n", + ep_attrib); + udc_state = STATE_ERROR; + break; + } + } + +} + +/* udc_connect + * + * Move state, switch on the USB + */ +void udc_connect(void) +{ + /* Enable pull-up resistor on D+ + * TODO: fit a pull-up resistor to drive SE0 for > 2.5us + */ + + if(udc_state!=STATE_ERROR){ + udc_state = STATE_READY; + usbp->usmod|= USMOD_EN; + } +} + +/* udc_disconnect + * + * Disconnect is not used but, is included for completeness + */ +void udc_disconnect(void) +{ + /* Disable pull-up resistor on D- + * TODO: fix a pullup resistor to control this + */ + + if(udc_state!=STATE_ERROR){ + udc_state = STATE_NOT_READY; + } + usbp->usmod&=~USMOD_EN; +} + +/* udc_enable + * + * Grab an EP0 URB, register interest in a subset of USB events + */ +void udc_enable(struct usb_device_instance *device) +{ + if(udc_state == STATE_ERROR){ + return; + } + + udc_device = device; + + if(!ep_ref[0].urb){ + ep_ref[0].urb = usbd_alloc_urb(device, + device->bus->endpoint_array); + } + + /* Register interest in all events except SOF, enable transceiver */ + usbp->usber= 0x03FF; + usbp->usbmr= 0x02F7; + + return; +} + +/* udc_disable + * + * disable the currently hooked device + */ +void udc_disable(void) +{ + int i = 0; + + if(udc_state == STATE_ERROR){ + DBG("Won't disable UDC. udc_state==STATE_ERROR !\n"); + return; + } + + udc_device = 0; + + for(;iusbmr= 0x00; + usbp->usmod= ~USMOD_EN; + udc_state = STATE_NOT_READY; +} + +/* udc_startup_events + * + * Enable the specified device + */ +void udc_startup_events(struct usb_device_instance *device) +{ + udc_enable(device); + if(udc_state == STATE_READY){ + usbd_device_event_irq (device, DEVICE_CREATE, 0); + } +} + +/* udc_set_nak + * + * Allow upper layers to signal lower layers should not accept more RX data + * + */ +void udc_set_nak(int epid) +{ + if(epid){ + mpc8xx_udc_set_nak(epid); + } +} + +/* udc_unset_nak + * + * Suspend sending of NAK tokens for DATA OUT tokens on a given endpoint. + * Switch off NAKing on this endpoint to accept more data output from host. + * + */ +void udc_unset_nak (int epid) +{ + if(epid > MAX_ENDPOINTS){ + return; + } + + if(usbp->usep[epid]&(USEP_THS_NAK | USEP_RHS_NAK)){ + usbp->usep[epid]&= ~(USEP_THS_NAK | USEP_RHS_NAK); + __asm__ ("eieio"); + } +} + +/****************************************************************************** + Static Linkage +******************************************************************************/ + +/* udc_state_transition_up + * udc_state_transition_down + * + * Helper functions to implement device state changes. The device states and + * the events that transition between them are: + * + * STATE_ATTACHED + * || /\ + * \/ || + * DEVICE_HUB_CONFIGURED DEVICE_HUB_RESET + * || /\ + * \/ || + * STATE_POWERED + * || /\ + * \/ || + * DEVICE_RESET DEVICE_POWER_INTERRUPTION + * || /\ + * \/ || + * STATE_DEFAULT + * || /\ + * \/ || + * DEVICE_ADDRESS_ASSIGNED DEVICE_RESET + * || /\ + * \/ || + * STATE_ADDRESSED + * || /\ + * \/ || + * DEVICE_CONFIGURED DEVICE_DE_CONFIGURED + * || /\ + * \/ || + * STATE_CONFIGURED + * + * udc_state_transition_up transitions up (in the direction from STATE_ATTACHED + * to STATE_CONFIGURED) from the specified initial state to the specified final + * state, passing through each intermediate state on the way. If the initial + * state is at or above (i.e. nearer to STATE_CONFIGURED) the final state, then + * no state transitions will take place. + * + * udc_state_transition_down transitions down (in the direction from + * STATE_CONFIGURED to STATE_ATTACHED) from the specified initial state to the + * specified final state, passing through each intermediate state on the way. + * If the initial state is at or below (i.e. nearer to STATE_ATTACHED) the final + * state, then no state transitions will take place. + * + */ + +static void mpc8xx_udc_state_transition_up (usb_device_state_t initial, + usb_device_state_t final) +{ + if (initial < final) { + switch (initial) { + case STATE_ATTACHED: + usbd_device_event_irq (udc_device, + DEVICE_HUB_CONFIGURED, 0); + if (final == STATE_POWERED) + break; + case STATE_POWERED: + usbd_device_event_irq (udc_device, DEVICE_RESET, 0); + if (final == STATE_DEFAULT) + break; + case STATE_DEFAULT: + usbd_device_event_irq (udc_device, + DEVICE_ADDRESS_ASSIGNED, 0); + if (final == STATE_ADDRESSED) + break; + case STATE_ADDRESSED: + usbd_device_event_irq (udc_device, DEVICE_CONFIGURED, + 0); + case STATE_CONFIGURED: + break; + default: + break; + } + } +} + +static void mpc8xx_udc_state_transition_down (usb_device_state_t initial, + usb_device_state_t final) +{ + if (initial > final) { + switch (initial) { + case STATE_CONFIGURED: + usbd_device_event_irq (udc_device, + DEVICE_DE_CONFIGURED, 0); + if (final == STATE_ADDRESSED) + break; + case STATE_ADDRESSED: + usbd_device_event_irq (udc_device, DEVICE_RESET, 0); + if (final == STATE_DEFAULT) + break; + case STATE_DEFAULT: + usbd_device_event_irq (udc_device, + DEVICE_POWER_INTERRUPTION, 0); + if (final == STATE_POWERED) + break; + case STATE_POWERED: + usbd_device_event_irq (udc_device, DEVICE_HUB_RESET, + 0); + case STATE_ATTACHED: + break; + default: + break; + } + } +} + +/* mpc8xx_udc_stall + * + * Force returning of STALL tokens on the given endpoint. Protocol or function + * STALL conditions are permissable here + */ +static void mpc8xx_udc_stall (unsigned int ep) +{ + usbp->usep[ep] |= STALL_BITMASK; +} + +/* mpc8xx_udc_set_nak + * + * Force returning of NAK responses for the given endpoint as a kind of very + * simple flow control + */ +static void mpc8xx_udc_set_nak (unsigned int ep) +{ + usbp->usep[ep] |= NAK_BITMASK; + __asm__ ("eieio"); +} + +/* mpc8xx_udc_handle_txerr + * + * Handle errors relevant to TX. Return a status code to allow calling + * indicative of what if anything happened + */ +static short mpc8xx_udc_handle_txerr() +{ + short ep = 0, ret = 0; + + for(; epusber&(0x10<cbd_sc&0x06){ + ret = 1; + mpc8xx_udc_flush_tx_fifo(ep); + + }else{ + if(usbp->usep[ep]&STALL_BITMASK){ + if(!ep){ + usbp->usep[ep]&= + ~STALL_BITMASK; + } + }/* else NAK */ + } + usbp->usber|=(0x10<cbd_sc & RX_BD_W){ + *rx_cbdp = (volatile cbd_t*) + (endpoints[epid]->rbase + CFG_IMMR); + + }else{ + (*rx_cbdp)++; + } +} + + +/* mpc8xx_udc_flush_tx_fifo + * + * Flush a given TX fifo. Assumes one tx cbd per endpoint + */ +static void mpc8xx_udc_flush_tx_fifo(int epid) +{ + volatile cbd_t * tx_cbdp = 0; + + if(epid > MAX_ENDPOINTS){ + return; + } + + /* TX stop */ + immr->im_cpm.cp_cpcr = ((epid<<2) | 0x1D01); + __asm__ ("eieio"); + while(immr->im_cpm.cp_cpcr & 0x01); + + usbp->uscom = 0x40 | 0; + + /* reset ring */ + tx_cbdp = (cbd_t*)(endpoints[epid]->tbptr + CFG_IMMR); + tx_cbdp->cbd_sc = (TX_BD_I | TX_BD_W); + + + endpoints[epid]->tptr = endpoints[epid]->tbase; + endpoints[epid]->tstate = 0x00; + endpoints[epid]->tbcnt = 0x00; + + /* TX start */ + immr->im_cpm.cp_cpcr = ((epid<<2) | 0x2D01); + __asm__ ("eieio"); + while(immr->im_cpm.cp_cpcr & 0x01); + + return; +} + +/* mpc8xx_udc_flush_rx_fifo + * + * For the sake of completeness of the namespace, it seems like + * a good-design-decision (tm) to include mpc8xx_udc_flush_rx_fifo(); + * If RX_BD_E is true => a driver bug either here or in an upper layer + * not polling frequently enough. If RX_BD_E is true we have told the host + * we have accepted data but, the CPM found it had no-where to put that data + * which needless to say would be a bad thing. + */ +static void mpc8xx_udc_flush_rx_fifo() +{ + int i = 0; + for(i = 0;icbd_sc&RX_BD_E)){ + ERR("buf %p used rx data len = 0x%x sc=0x%x!\n", + rx_cbd[i], rx_cbd[i]->cbd_datlen, + rx_cbd[i]->cbd_sc); + + } + } + ERR("BUG : Input over-run\n"); +} + +/* mpc8xx_udc_clear_rxbd + * + * Release control of RX CBD to CP. + */ +static void mpc8xx_udc_clear_rxbd(volatile cbd_t * rx_cbdp) +{ + rx_cbdp->cbd_datlen = 0x0000; + rx_cbdp->cbd_sc= ((rx_cbdp->cbd_sc & RX_BD_W)|(RX_BD_E | RX_BD_I)); + __asm__ ("eieio"); +} + +/* mpc8xx_udc_tx_irq + * + * Parse for tx timeout, control RX or USB reset/busy conditions + * Return -1 on timeout, -2 on fatal error, else return zero + */ +static int mpc8xx_udc_tx_irq(int ep) +{ + int i = 0; + + if(usbp->usber&(USB_TX_ERRMASK)){ + if(mpc8xx_udc_handle_txerr()){ + /* Timeout, controlling function must retry send */ + return -1; + } + } + + if(usbp->usber & (USB_E_RESET|USB_E_BSY)){ + /* Fatal, abandon TX transaction */ + return -2; + } + + if(usbp->usber & USB_E_RXB){ + for(i = 0;icbd_sc&RX_BD_E)){ + if((rx_cbd[i] == ep_ref[0].prx) || ep){ + return -2; + } + } + } + } + + return 0; +} + +/* mpc8xx_udc_ep_tx + * + * Transmit in a re-entrant fashion outbound USB packets. + * Implement retry/timeout mechanism described in USB specification + * Toggle DATA0/DATA1 pids as necessary + * Introduces non-standard tx_retry. The USB standard has no scope for slave + * devices to give up TX, however tx_retry stops us getting stuck in an endless + * TX loop. + */ +static int mpc8xx_udc_ep_tx (struct usb_endpoint_instance *epi) +{ + struct urb *urb = epi->tx_urb; + volatile cbd_t * tx_cbdp = 0; + unsigned int ep = 0, pkt_len = 0, x = 0, tx_retry = 0; + int ret = 0; + + if(!epi || (epi->endpoint_address&0x03)>=MAX_ENDPOINTS || !urb){ + return -1; + } + + ep = epi->endpoint_address & 0x03; + tx_cbdp = (cbd_t*)(endpoints[ep]->tbptr + CFG_IMMR); + + if(tx_cbdp->cbd_sc&TX_BD_R || usbp->usber&USB_E_TXB){ + mpc8xx_udc_flush_tx_fifo(ep); + usbp->usber |= USB_E_TXB; + }; + + while(tx_retry++ < 100){ + ret = mpc8xx_udc_tx_irq(ep); + if(ret == -1){ + /* ignore timeout here */ + }else if(ret == -2){ + /* Abandon TX */ + mpc8xx_udc_flush_tx_fifo(ep); + return -1; + } + + tx_cbdp = (cbd_t*)(endpoints[ep]->tbptr + CFG_IMMR); + while(tx_cbdp->cbd_sc&TX_BD_R){}; + tx_cbdp->cbd_sc = (tx_cbdp->cbd_sc&TX_BD_W); + + pkt_len = urb->actual_length - epi->sent; + + if(pkt_len> epi->tx_packetSize || pkt_len > EP_MAX_PKT){ + pkt_len = MIN(epi->tx_packetSize, EP_MAX_PKT); + } + + for(x=0; xcbd_bufaddr+x)) = + urb->buffer[epi->sent + x]; + } + tx_cbdp->cbd_datlen = pkt_len; + tx_cbdp->cbd_sc|=(CBD_TX_BITMASK | ep_ref[ep].pid); + __asm__ ("eieio"); + + #ifdef __SIMULATE_ERROR__ + if(++err_poison_test == 2){ + err_poison_test = 0; + tx_cbdp->cbd_sc&=~TX_BD_TC; + } + #endif + + usbp->uscom = (USCOM_STR | ep); + + while(!(usbp->usber&USB_E_TXB)){ + ret = mpc8xx_udc_tx_irq(ep); + if(ret == -1){ + /* TX timeout */ + break; + }else if(ret == -2){ + if(usbp->usber & USB_E_TXB){ + usbp->usber|=USB_E_TXB; + } + mpc8xx_udc_flush_tx_fifo(ep); + return -1; + } + }; + + if(usbp->usber & USB_E_TXB){ + usbp->usber|=USB_E_TXB; + } + + /* ACK must be present <= 18bit times from TX */ + if(ret == -1){ + continue; + } + + /* TX ACK : USB 2.0 8.7.2, Toggle PID, Advance TX */ + epi->sent += pkt_len; + epi->last = MIN (urb->actual_length - epi->sent, + epi->tx_packetSize); + TOGGLE_TX_PID(ep_ref[ep].pid); + + if(epi->sent >= epi->tx_urb->actual_length){ + + epi->tx_urb->actual_length = 0; + epi->sent = 0; + + if(ep_ref[ep].sc & EP_SEND_ZLP){ + ep_ref[ep].sc &= ~EP_SEND_ZLP; + }else{ + return 0; + } + } + } + + ERR("TX fail, endpoint 0x%x tx bytes 0x%x/0x%x\n",ep, epi->sent, + epi->tx_urb->actual_length); + + return -1; +} + +/* mpc8xx_udc_dump_request + * + * Dump a control request to console + */ +static void mpc8xx_udc_dump_request(struct usb_device_request *request) +{ + DBG( + "bmRequestType:%02x bRequest:%02x wValue:%04x " + "wIndex:%04x wLength:%04x ?\n", + request->bmRequestType, + request->bRequest, + request->wValue, + request->wIndex, + request->wLength); + + return; +} + +/* mpc8xx_udc_ep0_rx_setup + * + * Decode received ep0 SETUP packet. return non-zero on error + */ +static int mpc8xx_udc_ep0_rx_setup (volatile cbd_t * rx_cbdp) +{ + unsigned int x = 0; + struct urb * purb = ep_ref[0].urb; + struct usb_endpoint_instance *epi = + &udc_device->bus->endpoint_array[0]; + + for(; xcbd_datlen; x++){ + *(((unsigned char*)&ep_ref[0].urb->device_request)+x) = + *((unsigned char*)(rx_cbdp->cbd_bufaddr+x)); + } + + mpc8xx_udc_clear_rxbd(rx_cbdp); + + if (ep0_recv_setup(purb)) { + mpc8xx_udc_dump_request(&purb->device_request); + return -1; + } + + if ((purb->device_request.bmRequestType&USB_REQ_DIRECTION_MASK) + == USB_REQ_HOST2DEVICE) { + + switch (purb->device_request.bRequest){ + case USB_REQ_SET_ADDRESS: + /* Send the Status OUT ZLP */ + ep_ref[0].pid = TX_BD_PID_DATA1; + purb->actual_length = 0; + mpc8xx_udc_init_tx(epi,purb); + mpc8xx_udc_ep_tx(epi); + + /* Move to the addressed state */ + usbp->usaddr = udc_device->address; + mpc8xx_udc_state_transition_up(udc_device->device_state, + STATE_ADDRESSED); + return 0; + + case USB_REQ_SET_CONFIGURATION: + if(!purb->device_request.wValue){ + + /* Respond at default address */ + usbp->usaddr = 0x00; + mpc8xx_udc_state_transition_down(udc_device->device_state, + STATE_ADDRESSED); + + } else { + + /* TODO: Support multiple configurations */ + mpc8xx_udc_state_transition_up(udc_device->device_state,STATE_CONFIGURED); + for(x=1; xbus->endpoint_array[x].endpoint_address&USB_ENDPOINT_DIR_MASK) + == USB_DIR_IN){ + ep_ref[x].pid = TX_BD_PID_DATA0; + }else{ + ep_ref[x].pid = RX_BD_PID_DATA0; + } + /* Set configuration must unstall endpoints */ + usbp->usep[x]&=~STALL_BITMASK; + } + + } + break; + default: + /* CDC/Vendor specific */ + break; + } + + /* Send ZLP as ACK in Status OUT phase */ + ep_ref[0].pid = TX_BD_PID_DATA1; + purb->actual_length = 0; + mpc8xx_udc_init_tx(epi,purb); + mpc8xx_udc_ep_tx(epi); + + }else{ + if(purb->actual_length){ + ep_ref[0].pid = TX_BD_PID_DATA1; + mpc8xx_udc_init_tx(epi,purb); + + if(!(purb->actual_length%EP0_MAX_PACKET_SIZE)){ + ep_ref[0].sc |= EP_SEND_ZLP; + } + + if(purb->device_request.wValue== + USB_DESCRIPTOR_TYPE_DEVICE){ + if(le16_to_cpu(purb->device_request.wLength)> + purb->actual_length){ + /* Send EP0_MAX_PACKET_SIZE bytes + * unless correct size requested. + */ + if(purb->actual_length > + epi->tx_packetSize){ + + purb->actual_length = + epi->tx_packetSize; + } + + } + } + mpc8xx_udc_ep_tx(epi); + + }else{ + /* Corrupt SETUP packet? */ + ERR("Zero length data or SETUP with DATA-IN phase ?\n"); + return 1; + } + } + return 0; +} + +/* mpc8xx_udc_init_tx + * + * Setup some basic parameters for a TX transaction + */ +static void mpc8xx_udc_init_tx(struct usb_endpoint_instance *epi, + struct urb * tx_urb) +{ + epi->sent = 0; + epi->last = 0; + epi->tx_urb = tx_urb; +} + +/* mpc8xx_udc_ep0_rx + * + * Receive ep0/control USB data. Parse and possibly send a response. + */ +static void mpc8xx_udc_ep0_rx(volatile cbd_t * rx_cbdp) +{ + if(rx_cbdp->cbd_sc&RX_BD_PID_SETUP){ + + /* Unconditionally accept SETUP packets */ + if(mpc8xx_udc_ep0_rx_setup(rx_cbdp)){ + mpc8xx_udc_stall (0); + } + + } else { + + mpc8xx_udc_clear_rxbd(rx_cbdp); + + if((rx_cbdp->cbd_datlen-2)){ + /* SETUP with a DATA phase + * outside of SETUP packet. + * Reply with STALL. + */ + mpc8xx_udc_stall (0); + } + } +} + +/* mpc8xx_udc_epn_rx + * + * Receive some data from cbd into USB system urb data abstraction + * Upper layers should NAK if there is insufficient RX data space + */ +static int mpc8xx_udc_epn_rx (unsigned int epid, volatile cbd_t * rx_cbdp) +{ + struct usb_endpoint_instance *epi = 0; + struct urb *urb = 0; + unsigned int x = 0; + + if(epid >= MAX_ENDPOINTS || !rx_cbdp->cbd_datlen){ + return 0; + } + + /* USB 2.0 PDF section 8.6.4 + * Discard data with invalid PID it is a resend. + */ + if(ep_ref[epid].pid!=(rx_cbdp->cbd_sc&0xC0)){ + return 1; + } + TOGGLE_RX_PID(ep_ref[epid].pid); + + epi = &udc_device->bus->endpoint_array[epid]; + urb = epi->rcv_urb; + + for(; x<(rx_cbdp->cbd_datlen-2); x++){ + *((unsigned char*)(urb->buffer + urb->actual_length +x)) = + *((unsigned char*)(rx_cbdp->cbd_bufaddr+x)); + } + + if(x){ + usbd_rcv_complete (epi, x, 0); + if(ep_ref[epid].urb->status == RECV_ERROR){ + DBG("RX error unset NAK\n"); + udc_unset_nak(epid); + } + } + return x; +} + +/* mpc8xx_udc_clock_init + * + * Obtain a clock reference for Full Speed Signaling + */ +static void mpc8xx_udc_clock_init (volatile immap_t * immr, + volatile cpm8xx_t * cp) +{ + +#if defined(CFG_USB_EXTC_CLK) + + /* This has been tested with a 48MHz crystal on CLK6 */ + switch(CFG_USB_EXTC_CLK){ + case 1: + immr->im_ioport.iop_papar|= 0x0100; + immr->im_ioport.iop_padir&= ~0x0100; + cp->cp_sicr|= 0x24; + break; + case 2: + immr->im_ioport.iop_papar|= 0x0200; + immr->im_ioport.iop_padir&= ~0x0200; + cp->cp_sicr|= 0x2D; + break; + case 3: + immr->im_ioport.iop_papar|= 0x0400; + immr->im_ioport.iop_padir&= ~0x0400; + cp->cp_sicr|= 0x36; + break; + case 4: + immr->im_ioport.iop_papar|= 0x0800; + immr->im_ioport.iop_padir&= ~0x0800; + cp->cp_sicr|= 0x3F; + break; + default: + udc_state = STATE_ERROR; + break; + } + +#elif defined(CFG_USB_BRGCLK) + + /* This has been tested with brgclk == 50MHz */ + DECLARE_GLOBAL_DATA_PTR; + int divisor = 0; + + if(gd->cpu_clk<48000000L){ + ERR("brgclk is too slow for full-speed USB!\n"); + udc_state = STATE_ERROR; + return; + } + + /* Assume the brgclk is 'good enough', we want !(gd->cpu_clk%48Mhz) + * but, can /probably/ live with close-ish alternative rates. + */ + divisor = (gd->cpu_clk/48000000L)-1; + cp->cp_sicr &= ~0x0000003F; + + switch(CFG_USB_BRGCLK){ + case 1: + cp->cp_brgc1 |= (divisor|CPM_BRG_EN); + cp->cp_sicr &= ~0x2F; + break; + case 2: + cp->cp_brgc2 |= (divisor|CPM_BRG_EN); + cp->cp_sicr |= 0x00000009; + break; + case 3: + cp->cp_brgc3 |= (divisor|CPM_BRG_EN); + cp->cp_sicr |= 0x00000012; + break; + case 4: + cp->cp_brgc4 = (divisor|CPM_BRG_EN); + cp->cp_sicr |= 0x0000001B; + break; + default: + udc_state = STATE_ERROR; + break; + } + +#else + #error "CFG_USB_EXTC_CLK or CFG_USB_BRGCLK must be defined" +#endif + +} + +/* mpc8xx_udc_cbd_attach + * + * attach a cbd to and endpoint + */ +static void mpc8xx_udc_cbd_attach (int ep, uchar tx_size, uchar rx_size) +{ + + if (!tx_cbd[ep] || !rx_cbd[ep] || ep >= MAX_ENDPOINTS){ + udc_state = STATE_ERROR; + return; + } + + if (tx_size>USB_MAX_PKT || rx_size>USB_MAX_PKT || + (!tx_size && !rx_size)){ + udc_state = STATE_ERROR; + return; + } + + /* Attach CBD to appropiate Parameter RAM Endpoint data structure */ + if(rx_size){ + endpoints[ep]->rbase = (u32)rx_cbd[rx_ct]; + endpoints[ep]->rbptr = (u32)rx_cbd[rx_ct]; + rx_ct++; + + if(!ep){ + + endpoints[ep]->rbptr = (u32)rx_cbd[rx_ct]; + rx_cbd[rx_ct]->cbd_sc |= RX_BD_W; + rx_ct++; + + }else{ + rx_ct += 2; + endpoints[ep]->rbptr = (u32)rx_cbd[rx_ct]; + rx_cbd[rx_ct]->cbd_sc |= RX_BD_W; + rx_ct++; + } + + /* Where we expect to RX data on this endpoint */ + ep_ref[ep].prx = rx_cbd[rx_ct-1]; + }else{ + + ep_ref[ep].prx = 0; + endpoints[ep]->rbase = 0; + endpoints[ep]->rbptr = 0; + } + + if(tx_size){ + endpoints[ep]->tbase = (u32)tx_cbd[tx_ct]; + endpoints[ep]->tbptr = (u32)tx_cbd[tx_ct]; + tx_ct++; + }else{ + endpoints[ep]->tbase = 0; + endpoints[ep]->tbptr = 0; + } + + endpoints[ep]->tstate = 0; + endpoints[ep]->tbcnt = 0; + endpoints[ep]->mrblr = EP_MAX_PKT; + endpoints[ep]->rfcr = 0x18; + endpoints[ep]->tfcr = 0x18; + ep_ref[ep].sc |= EP_ATTACHED; + + DBG("ep %d rbase 0x%08x rbptr 0x%08x tbase 0x%08x tbptr 0x%08x prx = %p\n", + ep, endpoints[ep]->rbase, endpoints[ep]->rbptr, endpoints[ep]->tbase, + endpoints[ep]->tbptr, ep_ref[ep].prx); + + return; +} + +/* mpc8xx_udc_cbd_init + * + * Allocate space for a cbd and allocate TX/RX data space + */ +static void mpc8xx_udc_cbd_init (void) +{ + int i = 0; + + for(; icbd_bufaddr = + mpc8xx_udc_alloc(EP_MAX_PKT, sizeof(int)); + + tx_cbd[i]->cbd_sc = (TX_BD_I | TX_BD_W); + tx_cbd[i]->cbd_datlen = 0x0000; + } + + + for(i=0; i< RX_RING_SIZE; i++){ + rx_cbd[i]->cbd_bufaddr = + mpc8xx_udc_alloc(EP_MAX_PKT, sizeof(int)); + rx_cbd[i]->cbd_sc = (RX_BD_I | RX_BD_E); + rx_cbd[i]->cbd_datlen = 0x0000; + + } + + return; +} + +/* mpc8xx_udc_endpoint_init + * + * Attach an endpoint to some dpram + */ +static void mpc8xx_udc_endpoint_init (void) +{ + int i = 0; + + for(; i Entering device setup"); - + do { const int setup_pktsize = 8; unsigned char *datap = @@ -1517,4 +1517,31 @@ void udc_startup_events (struct usb_device_instance *device) udc_enable (device); } +/** + * udc_irq - do pseudo interrupts + */ +void udc_irq(void) +{ + /* Loop while we have interrupts. + * If we don't do this, the input chain + * polling delay is likely to miss + * host requests. + */ + while (inw (UDC_IRQ_SRC) & ~UDC_SOF_Flg) { + /* Handle any new IRQs */ + omap1510_udc_irq (); + omap1510_udc_noniso_irq (); + } +} + +/* Flow control */ +void udc_set_nak(int epid) +{ + /* TODO: implement this functionality in omap1510 */ +} + +void udc_unset_nak (int epid) +{ + /* TODO: implement this functionality in omap1510 */ +} #endif diff --git a/drivers/usbtty.c b/drivers/usbtty.c index ce4a12e16..ed96999e8 100644 --- a/drivers/usbtty.c +++ b/drivers/usbtty.c @@ -1,6 +1,9 @@ /* * (C) Copyright 2003 * Gerry Hamel, geh@ti.com, Texas Instruments + * + * (C) Copyright 2006 + * Bryan O'Donoghue, bodonoghue@codehermit.ie * * 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 @@ -25,19 +28,41 @@ #include #include #include "usbtty.h" +#include "usb_cdc_acm.h" +#include "usbdescriptors.h" +#include /* If defined, override Linux identifiers with + * vendor specific ones */ #if 0 -#define TTYDBG(fmt,args...) serial_printf("[%s] %s %d: "fmt, __FILE__,__FUNCTION__,__LINE__,##args) +#define TTYDBG(fmt,args...)\ + serial_printf("[%s] %s %d: "fmt, __FILE__,__FUNCTION__,__LINE__,##args) #else #define TTYDBG(fmt,args...) do{}while(0) #endif -#if 0 -#define TTYERR(fmt,args...) serial_printf("ERROR![%s] %s %d: "fmt, __FILE__,__FUNCTION__,__LINE__,##args) +#if 1 +#define TTYERR(fmt,args...)\ + serial_printf("ERROR![%s] %s %d: "fmt, __FILE__,__FUNCTION__,\ + __LINE__,##args) #else #define TTYERR(fmt,args...) do{}while(0) #endif +/* + * Defines + */ +#define NUM_CONFIGS 1 +#define MAX_INTERFACES 2 +#define NUM_ENDPOINTS 3 +#define ACM_TX_ENDPOINT 3 +#define ACM_RX_ENDPOINT 2 +#define GSERIAL_TX_ENDPOINT 2 +#define GSERIAL_RX_ENDPOINT 1 +#define NUM_ACM_INTERFACES 2 +#define NUM_GSERIAL_INTERFACES 1 +#define CONFIG_USBD_DATA_INTERFACE_STR "Bulk Data Interface" +#define CONFIG_USBD_CTRL_INTERFACE_STR "Control Interface" + /* * Buffers to hold input and output data */ @@ -50,157 +75,336 @@ static circbuf_t usbtty_output; * Instance variables */ static device_t usbttydev; -static struct usb_device_instance device_instance[1]; -static struct usb_bus_instance bus_instance[1]; +static struct usb_device_instance device_instance[1]; +static struct usb_bus_instance bus_instance[1]; static struct usb_configuration_instance config_instance[NUM_CONFIGS]; -static struct usb_interface_instance interface_instance[NUM_INTERFACES]; -static struct usb_alternate_instance alternate_instance[NUM_INTERFACES]; -static struct usb_endpoint_instance endpoint_instance[NUM_ENDPOINTS+1]; /* one extra for control endpoint */ - -/* - * Static allocation of urbs - */ -#define RECV_ENDPOINT 1 -#define TX_ENDPOINT 2 +static struct usb_interface_instance interface_instance[MAX_INTERFACES]; +static struct usb_alternate_instance alternate_instance[MAX_INTERFACES]; +/* one extra for control endpoint */ +static struct usb_endpoint_instance endpoint_instance[NUM_ENDPOINTS+1]; /* * Global flag */ int usbtty_configured_flag = 0; - /* * Serial number */ static char serial_number[16]; + /* - * Descriptors + * Descriptors, Strings, Local variables. */ + +/* defined and used by usbdcore_ep0.c */ +extern struct usb_string_descriptor **usb_strings; + +/* Indicies, References */ +static unsigned short rx_endpoint = 0; +static unsigned short tx_endpoint = 0; +static unsigned short interface_count = 0; +static struct usb_string_descriptor *usbtty_string_table[STR_COUNT]; + +/* USB Descriptor Strings */ static u8 wstrLang[4] = {4,USB_DT_STRING,0x9,0x4}; static u8 wstrManufacturer[2 + 2*(sizeof(CONFIG_USBD_MANUFACTURER)-1)]; static u8 wstrProduct[2 + 2*(sizeof(CONFIG_USBD_PRODUCT_NAME)-1)]; static u8 wstrSerial[2 + 2*(sizeof(serial_number) - 1)]; static u8 wstrConfiguration[2 + 2*(sizeof(CONFIG_USBD_CONFIGURATION_STR)-1)]; -static u8 wstrInterface[2 + 2*(sizeof(CONFIG_USBD_INTERFACE_STR)-1)]; - -static struct usb_string_descriptor *usbtty_string_table[] = { - (struct usb_string_descriptor*)wstrLang, - (struct usb_string_descriptor*)wstrManufacturer, - (struct usb_string_descriptor*)wstrProduct, - (struct usb_string_descriptor*)wstrSerial, - (struct usb_string_descriptor*)wstrConfiguration, - (struct usb_string_descriptor*)wstrInterface -}; -extern struct usb_string_descriptor **usb_strings; /* defined and used by omap1510_ep0.c */ +static u8 wstrDataInterface[2 + 2*(sizeof(CONFIG_USBD_DATA_INTERFACE_STR)-1)]; +static u8 wstrCtrlInterface[2 + 2*(sizeof(CONFIG_USBD_DATA_INTERFACE_STR)-1)]; +/* Standard USB Data Structures */ +static struct usb_interface_descriptor interface_descriptors[MAX_INTERFACES]; +static struct usb_endpoint_descriptor *ep_descriptor_ptrs[NUM_ENDPOINTS]; +static struct usb_configuration_descriptor *configuration_descriptor = 0; static struct usb_device_descriptor device_descriptor = { - bLength: sizeof(struct usb_device_descriptor), - bDescriptorType: USB_DT_DEVICE, - bcdUSB: USB_BCD_VERSION, - bDeviceClass: USBTTY_DEVICE_CLASS, - bDeviceSubClass: USBTTY_DEVICE_SUBCLASS, - bDeviceProtocol: USBTTY_DEVICE_PROTOCOL, - bMaxPacketSize0: EP0_MAX_PACKET_SIZE, - idVendor: CONFIG_USBD_VENDORID, - idProduct: CONFIG_USBD_PRODUCTID, - bcdDevice: USBTTY_BCD_DEVICE, - iManufacturer: STR_MANUFACTURER, - iProduct: STR_PRODUCT, - iSerialNumber: STR_SERIAL, - bNumConfigurations: NUM_CONFIGS - }; -static struct usb_configuration_descriptor config_descriptors[NUM_CONFIGS] = { - { - bLength: sizeof(struct usb_configuration_descriptor), - bDescriptorType: USB_DT_CONFIG, - wTotalLength: (sizeof(struct usb_configuration_descriptor)*NUM_CONFIGS) + - (sizeof(struct usb_interface_descriptor)*NUM_INTERFACES) + - (sizeof(struct usb_endpoint_descriptor)*NUM_ENDPOINTS), - bNumInterfaces: NUM_INTERFACES, - bConfigurationValue: 1, - iConfiguration: STR_CONFIG, - bmAttributes: BMATTRIBUTE_SELF_POWERED | BMATTRIBUTE_RESERVED, - bMaxPower: USBTTY_MAXPOWER - }, -}; -static struct usb_interface_descriptor interface_descriptors[NUM_INTERFACES] = { - { - bLength: sizeof(struct usb_interface_descriptor), - bDescriptorType: USB_DT_INTERFACE, - bInterfaceNumber: 0, - bAlternateSetting: 0, - bNumEndpoints: NUM_ENDPOINTS, - bInterfaceClass: USBTTY_INTERFACE_CLASS, - bInterfaceSubClass: USBTTY_INTERFACE_SUBCLASS, - bInterfaceProtocol: USBTTY_INTERFACE_PROTOCOL, - iInterface: STR_INTERFACE - }, + .bLength = sizeof(struct usb_device_descriptor), + .bDescriptorType = USB_DT_DEVICE, + .bcdUSB = cpu_to_le16(USB_BCD_VERSION), + .bDeviceSubClass = 0x00, + .bDeviceProtocol = 0x00, + .bMaxPacketSize0 = EP0_MAX_PACKET_SIZE, + .idVendor = cpu_to_le16(CONFIG_USBD_VENDORID), + .bcdDevice = cpu_to_le16(USBTTY_BCD_DEVICE), + .iManufacturer = STR_MANUFACTURER, + .iProduct = STR_PRODUCT, + .iSerialNumber = STR_SERIAL, + .bNumConfigurations = NUM_CONFIGS }; -static struct usb_endpoint_descriptor ep_descriptors[NUM_ENDPOINTS] = { - { - bLength: sizeof(struct usb_endpoint_descriptor), - bDescriptorType: USB_DT_ENDPOINT, - bEndpointAddress: CONFIG_USBD_SERIAL_OUT_ENDPOINT | USB_DIR_OUT, - bmAttributes: USB_ENDPOINT_XFER_BULK, - wMaxPacketSize: CONFIG_USBD_SERIAL_OUT_PKTSIZE, - bInterval: 0 - }, - { - bLength: sizeof(struct usb_endpoint_descriptor), - bDescriptorType: USB_DT_ENDPOINT, - bEndpointAddress: CONFIG_USBD_SERIAL_IN_ENDPOINT | USB_DIR_IN, - bmAttributes: USB_ENDPOINT_XFER_BULK, - wMaxPacketSize: CONFIG_USBD_SERIAL_IN_PKTSIZE, - bInterval: 0 - }, - { - bLength: sizeof(struct usb_endpoint_descriptor), - bDescriptorType: USB_DT_ENDPOINT, - bEndpointAddress: CONFIG_USBD_SERIAL_INT_ENDPOINT | USB_DIR_IN, - bmAttributes: USB_ENDPOINT_XFER_INT, - wMaxPacketSize: CONFIG_USBD_SERIAL_INT_PKTSIZE, - bInterval: 0 - }, -}; -static struct usb_endpoint_descriptor *ep_descriptor_ptrs[NUM_ENDPOINTS] = { - &(ep_descriptors[0]), - &(ep_descriptors[1]), - &(ep_descriptors[2]), + + +/* + * Static CDC ACM specific descriptors + */ + +struct acm_config_desc { + struct usb_configuration_descriptor configuration_desc; + + /* Master Interface */ + struct usb_interface_descriptor interface_desc; + + struct usb_class_header_function_descriptor usb_class_header; + struct usb_class_call_management_descriptor usb_class_call_mgt; + struct usb_class_abstract_control_descriptor usb_class_acm; + struct usb_class_union_function_descriptor usb_class_union; + struct usb_endpoint_descriptor notification_endpoint; + + /* Slave Interface */ + struct usb_interface_descriptor data_class_interface; + struct usb_endpoint_descriptor + data_endpoints[NUM_ENDPOINTS-1] __attribute__((packed)); +} __attribute__((packed)); + +static struct acm_config_desc acm_configuration_descriptors[NUM_CONFIGS] = { + { + .configuration_desc ={ + .bLength = + sizeof(struct usb_configuration_descriptor), + .bDescriptorType = USB_DT_CONFIG, + .wTotalLength = + cpu_to_le16(sizeof(struct acm_config_desc)), + .bNumInterfaces = NUM_ACM_INTERFACES, + .bConfigurationValue = 1, + .iConfiguration = STR_CONFIG, + .bmAttributes = + BMATTRIBUTE_SELF_POWERED|BMATTRIBUTE_RESERVED, + .bMaxPower = USBTTY_MAXPOWER + }, + /* Interface 1 */ + .interface_desc = { + .bLength = sizeof(struct usb_interface_descriptor), + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, + .bAlternateSetting = 0, + .bNumEndpoints = 0x01, + .bInterfaceClass = + COMMUNICATIONS_INTERFACE_CLASS_CONTROL, + .bInterfaceSubClass = COMMUNICATIONS_ACM_SUBCLASS, + .bInterfaceProtocol = COMMUNICATIONS_V25TER_PROTOCOL, + .iInterface = STR_CTRL_INTERFACE, + }, + .usb_class_header = { + .bFunctionLength = + sizeof(struct usb_class_header_function_descriptor), + .bDescriptorType = CS_INTERFACE, + .bDescriptorSubtype = USB_ST_HEADER, + .bcdCDC = cpu_to_le16(110), + }, + .usb_class_call_mgt = { + .bFunctionLength = + sizeof(struct usb_class_call_management_descriptor), + .bDescriptorType = CS_INTERFACE, + .bDescriptorSubtype = USB_ST_CMF, + .bmCapabilities = 0x00, + .bDataInterface = 0x01, + }, + .usb_class_acm = { + .bFunctionLength = + sizeof(struct usb_class_abstract_control_descriptor), + .bDescriptorType = CS_INTERFACE, + .bDescriptorSubtype = USB_ST_ACMF, + .bmCapabilities = 0x00, + }, + .usb_class_union = { + .bFunctionLength = + sizeof(struct usb_class_union_function_descriptor), + .bDescriptorType = CS_INTERFACE, + .bDescriptorSubtype = USB_ST_UF, + .bMasterInterface = 0x00, + .bSlaveInterface0 = 0x01, + }, + .notification_endpoint = { + .bLength = + sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 0x01 | USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize + = cpu_to_le16(CONFIG_USBD_SERIAL_INT_PKTSIZE), + .bInterval = 0xFF, + }, + + /* Interface 2 */ + .data_class_interface = { + .bLength = + sizeof(struct usb_interface_descriptor), + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0x01, + .bAlternateSetting = 0x00, + .bNumEndpoints = 0x02, + .bInterfaceClass = + COMMUNICATIONS_INTERFACE_CLASS_DATA, + .bInterfaceSubClass = DATA_INTERFACE_SUBCLASS_NONE, + .bInterfaceProtocol = DATA_INTERFACE_PROTOCOL_NONE, + .iInterface = STR_DATA_INTERFACE, + }, + .data_endpoints = { + { + .bLength = + sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 0x02 | USB_DIR_OUT, + .bmAttributes = + USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = + cpu_to_le16(CONFIG_USBD_SERIAL_BULK_PKTSIZE), + .bInterval = 0xFF, + }, + { + .bLength = + sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 0x03 | USB_DIR_IN, + .bmAttributes = + USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = + cpu_to_le16(CONFIG_USBD_SERIAL_BULK_PKTSIZE), + .bInterval = 0xFF, + }, + }, + }, +}; + +static struct rs232_emu rs232_desc={ + .dter = 115200, + .stop_bits = 0x00, + .parity = 0x00, + .data_bits = 0x08 }; -/* utility function for converting char* to wide string used by USB */ -static void str2wide (char *str, u16 * wide) -{ - int i; - for (i = 0; i < strlen (str) && str[i]; i++) - wide[i] = (u16) str[i]; -} +/* + * Static Generic Serial specific data + */ + + +struct gserial_config_desc { + + struct usb_configuration_descriptor configuration_desc; + struct usb_interface_descriptor + interface_desc[NUM_GSERIAL_INTERFACES] __attribute__((packed)); + struct usb_endpoint_descriptor + data_endpoints[NUM_ENDPOINTS] __attribute__((packed)); + +} __attribute__((packed)); + +static struct gserial_config_desc +gserial_configuration_descriptors[NUM_CONFIGS] ={ + { + .configuration_desc ={ + .bLength = sizeof(struct usb_configuration_descriptor), + .bDescriptorType = USB_DT_CONFIG, + .wTotalLength = + cpu_to_le16(sizeof(struct gserial_config_desc)), + .bNumInterfaces = NUM_GSERIAL_INTERFACES, + .bConfigurationValue = 1, + .iConfiguration = STR_CONFIG, + .bmAttributes = + BMATTRIBUTE_SELF_POWERED|BMATTRIBUTE_RESERVED, + .bMaxPower = USBTTY_MAXPOWER + }, + .interface_desc = { + { + .bLength = + sizeof(struct usb_interface_descriptor), + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, + .bAlternateSetting = 0, + .bNumEndpoints = NUM_ENDPOINTS, + .bInterfaceClass = + COMMUNICATIONS_INTERFACE_CLASS_VENDOR, + .bInterfaceSubClass = + COMMUNICATIONS_NO_SUBCLASS, + .bInterfaceProtocol = + COMMUNICATIONS_NO_PROTOCOL, + .iInterface = STR_DATA_INTERFACE + }, + }, + .data_endpoints = { + { + .bLength = + sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 0x01 | USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = + cpu_to_le16(CONFIG_USBD_SERIAL_OUT_PKTSIZE), + .bInterval= 0xFF, + }, + { + .bLength = + sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 0x02 | USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = + cpu_to_le16(CONFIG_USBD_SERIAL_IN_PKTSIZE), + .bInterval = 0xFF, + }, + { + .bLength = + sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 0x03 | USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = + cpu_to_le16(CONFIG_USBD_SERIAL_INT_PKTSIZE), + .bInterval = 0xFF, + }, + }, + }, +}; /* - * Prototypes + * Static Function Prototypes */ + static void usbtty_init_strings (void); static void usbtty_init_instances (void); static void usbtty_init_endpoints (void); - +static void usbtty_init_terminal_type(short type); static void usbtty_event_handler (struct usb_device_instance *device, - usb_device_event_t event, int data); + usb_device_event_t event, int data); +static int usbtty_cdc_setup(struct usb_device_request *request, + struct urb *urb); static int usbtty_configured (void); - static int write_buffer (circbuf_t * buf); static int fill_buffer (circbuf_t * buf); void usbtty_poll (void); -static void pretend_interrupts (void); +/* utility function for converting char* to wide string used by USB */ +static void str2wide (char *str, u16 * wide) +{ + int i; + for (i = 0; i < strlen (str) && str[i]; i++){ + #if defined(__LITTLE_ENDIAN__) + wide[i] = (u16) str[i]; + #elif defined(__BIG_ENDIAN__) + wide[i] = ((u16)(str[i])<<8); + #else + #error "__LITTLE_ENDIAN__ or __BIG_ENDIAN__ undefined" + #endif + } +} /* * Test whether a character is in the RX buffer */ + int usbtty_tstc (void) { + struct usb_endpoint_instance *endpoint = + &endpoint_instance[rx_endpoint]; + + /* If no input data exists, allow more RX to be accepted */ + if(usbtty_input.size <= 0){ + udc_unset_nak(endpoint->endpoint_address&0x03); + } + usbtty_poll (); return (usbtty_input.size > 0); } @@ -210,15 +414,21 @@ int usbtty_tstc (void) * otherwise. When the function is succesfull, the character read is * written into its argument c. */ + int usbtty_getc (void) { char c; + struct usb_endpoint_instance *endpoint = + &endpoint_instance[rx_endpoint]; while (usbtty_input.size <= 0) { + udc_unset_nak(endpoint->endpoint_address&0x03); usbtty_poll (); } buf_pop (&usbtty_input, &c, 1); + udc_set_nak(endpoint->endpoint_address&0x03); + return c; } @@ -238,7 +448,6 @@ void usbtty_putc (const char c) } } - /* usbtty_puts() helper function for finding the next '\n' in a string */ static int next_nl_pos (const char *s) { @@ -252,8 +461,9 @@ static int next_nl_pos (const char *s) } /* - * Output a string to the usb client port. + * Output a string to the usb client port - implementing flow control */ + static void __usbtty_puts (const char *str, int len) { int maxlen = usbtty_output.totalsize; @@ -261,22 +471,19 @@ static void __usbtty_puts (const char *str, int len) /* break str into chunks < buffer size, if needed */ while (len > 0) { - space = maxlen - usbtty_output.size; + usbtty_poll (); + space = maxlen - usbtty_output.size; /* Empty buffer here, if needed, to ensure space... */ - if (space <= 0) { + if (space) { write_buffer (&usbtty_output); - space = maxlen - usbtty_output.size; - if (space <= 0) { - space = len; /* allow old data to be overwritten. */ - } - } - - n = MIN (space, MIN (len, maxlen)); - buf_push (&usbtty_output, str, n); + + n = MIN (space, MIN (len, maxlen)); + buf_push (&usbtty_output, str, n); - str += n; - len -= n; + str += n; + len -= n; + } } } @@ -313,8 +520,10 @@ int drv_usbtty_init (void) { int rc; char * sn; + char * tt; int snlen; + /* Ger seiral number */ if (!(sn = getenv("serial#"))) { sn = "000000000000"; } @@ -327,6 +536,14 @@ int drv_usbtty_init (void) memcpy (serial_number, sn, snlen); serial_number[snlen] = '\0'; + /* Decide on which type of UDC device to be. + */ + + if(!(tt = getenv("usbtty"))) { + tt = "generic"; + } + usbtty_init_terminal_type(strcmp(tt,"cdc_acm")); + /* prepare buffers... */ buf_init (&usbtty_input, USBTTY_BUFFER_SIZE); buf_init (&usbtty_output, USBTTY_BUFFER_SIZE); @@ -337,7 +554,7 @@ int drv_usbtty_init (void) usbtty_init_strings (); usbtty_init_instances (); - udc_startup_events (device_instance); /* Enable our device, initialize udc pointers */ + udc_startup_events (device_instance);/* Enable dev, init udc pointers */ udc_connect (); /* Enable pullup for host detection */ usbtty_init_endpoints (); @@ -362,34 +579,52 @@ static void usbtty_init_strings (void) { struct usb_string_descriptor *string; + usbtty_string_table[STR_LANG] = + (struct usb_string_descriptor*)wstrLang; + string = (struct usb_string_descriptor *) wstrManufacturer; - string->bLength = sizeof (wstrManufacturer); + string->bLength = sizeof(wstrManufacturer); string->bDescriptorType = USB_DT_STRING; str2wide (CONFIG_USBD_MANUFACTURER, string->wData); + usbtty_string_table[STR_MANUFACTURER]=string; + string = (struct usb_string_descriptor *) wstrProduct; - string->bLength = sizeof (wstrProduct); + string->bLength = sizeof(wstrProduct); string->bDescriptorType = USB_DT_STRING; str2wide (CONFIG_USBD_PRODUCT_NAME, string->wData); + usbtty_string_table[STR_PRODUCT]=string; + string = (struct usb_string_descriptor *) wstrSerial; - string->bLength = 2 + 2*strlen(serial_number); + string->bLength = sizeof(serial_number); string->bDescriptorType = USB_DT_STRING; str2wide (serial_number, string->wData); + usbtty_string_table[STR_SERIAL]=string; + string = (struct usb_string_descriptor *) wstrConfiguration; - string->bLength = sizeof (wstrConfiguration); + string->bLength = sizeof(wstrConfiguration); string->bDescriptorType = USB_DT_STRING; str2wide (CONFIG_USBD_CONFIGURATION_STR, string->wData); + usbtty_string_table[STR_CONFIG]=string; + + + string = (struct usb_string_descriptor *) wstrDataInterface; + string->bLength = sizeof(wstrDataInterface); + string->bDescriptorType = USB_DT_STRING; + str2wide (CONFIG_USBD_DATA_INTERFACE_STR, string->wData); + usbtty_string_table[STR_DATA_INTERFACE]=string; - string = (struct usb_string_descriptor *) wstrInterface; - string->bLength = sizeof (wstrInterface); + string = (struct usb_string_descriptor *) wstrCtrlInterface; + string->bLength = sizeof(wstrCtrlInterface); string->bDescriptorType = USB_DT_STRING; - str2wide (CONFIG_USBD_INTERFACE_STR, string->wData); + str2wide (CONFIG_USBD_CTRL_INTERFACE_STR, string->wData); + usbtty_string_table[STR_CTRL_INTERFACE]=string; /* Now, initialize the string table for ep0 handling */ usb_strings = usbtty_string_table; -} +} static void usbtty_init_instances (void) { @@ -400,6 +635,7 @@ static void usbtty_init_instances (void) device_instance->device_state = STATE_INIT; device_instance->device_descriptor = &device_descriptor; device_instance->event = usbtty_event_handler; + device_instance->cdc_recv_setup = usbtty_cdc_setup; device_instance->bus = bus_instance; device_instance->configurations = NUM_CONFIGS; device_instance->configuration_instance_array = config_instance; @@ -415,8 +651,8 @@ static void usbtty_init_instances (void) /* configuration instance */ memset (config_instance, 0, sizeof (struct usb_configuration_instance)); - config_instance->interfaces = NUM_INTERFACES; - config_instance->configuration_descriptor = config_descriptors; + config_instance->interfaces = interface_count; + config_instance->configuration_descriptor = configuration_descriptor; config_instance->interface_instance_array = interface_instance; /* interface instance */ @@ -447,17 +683,22 @@ static void usbtty_init_instances (void) sizeof (struct usb_endpoint_instance)); endpoint_instance[i].endpoint_address = - ep_descriptors[i - 1].bEndpointAddress; + ep_descriptor_ptrs[i - 1]->bEndpointAddress; - endpoint_instance[i].rcv_packetSize = - ep_descriptors[i - 1].wMaxPacketSize; endpoint_instance[i].rcv_attributes = - ep_descriptors[i - 1].bmAttributes; + ep_descriptor_ptrs[i - 1]->bmAttributes; + + endpoint_instance[i].rcv_packetSize = + le16_to_cpu(ep_descriptor_ptrs[i - 1]->wMaxPacketSize); + + endpoint_instance[i].tx_attributes = + ep_descriptor_ptrs[i - 1]->bmAttributes; endpoint_instance[i].tx_packetSize = - ep_descriptors[i - 1].wMaxPacketSize; + le16_to_cpu(ep_descriptor_ptrs[i - 1]->wMaxPacketSize); + endpoint_instance[i].tx_attributes = - ep_descriptors[i - 1].bmAttributes; + ep_descriptor_ptrs[i - 1]->bmAttributes; urb_link_init (&endpoint_instance[i].rcv); urb_link_init (&endpoint_instance[i].rdy); @@ -480,13 +721,79 @@ static void usbtty_init_endpoints (void) int i; bus_instance->max_endpoints = NUM_ENDPOINTS + 1; - for (i = 0; i <= NUM_ENDPOINTS; i++) { + for (i = 1; i <= NUM_ENDPOINTS; i++) { udc_setup_ep (device_instance, i, &endpoint_instance[i]); } } +/* usbtty_init_terminal_type + * + * Do some late binding for our device type. + */ +static void usbtty_init_terminal_type(short type) +{ + switch(type){ + /* CDC ACM */ + case 0: + /* Assign endpoint descriptors */ + ep_descriptor_ptrs[0] = + &acm_configuration_descriptors[0].notification_endpoint; + ep_descriptor_ptrs[1] = + &acm_configuration_descriptors[0].data_endpoints[0]; + ep_descriptor_ptrs[2] = + &acm_configuration_descriptors[0].data_endpoints[1]; + + /* Enumerate Device Descriptor */ + device_descriptor.bDeviceClass = + COMMUNICATIONS_DEVICE_CLASS; + device_descriptor.idProduct = + cpu_to_le16(CONFIG_USBD_PRODUCTID_CDCACM); + + /* Assign endpoint indices */ + tx_endpoint = ACM_TX_ENDPOINT; + rx_endpoint = ACM_RX_ENDPOINT; + + /* Configuration Descriptor */ + configuration_descriptor = + (struct usb_configuration_descriptor*) + &acm_configuration_descriptors; + + /* Interface count */ + interface_count = NUM_ACM_INTERFACES; + break; -/*********************************************************************************/ + /* BULK IN/OUT & Default */ + case 1: + default: + /* Assign endpoint descriptors */ + ep_descriptor_ptrs[0] = + &gserial_configuration_descriptors[0].data_endpoints[0]; + ep_descriptor_ptrs[1] = + &gserial_configuration_descriptors[0].data_endpoints[1]; + ep_descriptor_ptrs[2] = + &gserial_configuration_descriptors[0].data_endpoints[2]; + + /* Enumerate Device Descriptor */ + device_descriptor.bDeviceClass = 0xFF; + device_descriptor.idProduct = + cpu_to_le16(CONFIG_USBD_PRODUCTID_GSERIAL); + + /* Assign endpoint indices */ + tx_endpoint = GSERIAL_TX_ENDPOINT; + rx_endpoint = GSERIAL_RX_ENDPOINT; + + /* Configuration Descriptor */ + configuration_descriptor = + (struct usb_configuration_descriptor*) + &gserial_configuration_descriptors; + + /* Interface count */ + interface_count = NUM_GSERIAL_INTERFACES; + break; + } +} + +/******************************************************************************/ static struct urb *next_urb (struct usb_device_instance *device, struct usb_endpoint_instance *endpoint) @@ -525,28 +832,40 @@ static int write_buffer (circbuf_t * buf) if (!usbtty_configured ()) { return 0; } + + struct usb_endpoint_instance *endpoint = + &endpoint_instance[tx_endpoint]; + struct urb *current_urb = NULL; - if (buf->size) { + current_urb = next_urb (device_instance, endpoint); + /* TX data still exists - send it now + */ + if(endpoint->sent < current_urb->actual_length){ + if(udc_endpoint_write (endpoint)){ + /* Write pre-empted by RX */ + return -1; + } + } - struct usb_endpoint_instance *endpoint = - &endpoint_instance[TX_ENDPOINT]; - struct urb *current_urb = NULL; + if (buf->size) { char *dest; int space_avail; int popnum, popped; int total = 0; - /* Break buffer into urb sized pieces, and link each to the endpoint */ + /* Break buffer into urb sized pieces, + * and link each to the endpoint + */ while (buf->size > 0) { - current_urb = next_urb (device_instance, endpoint); + if (!current_urb) { TTYERR ("current_urb is NULL, buf->size %d\n", buf->size); return total; } - dest = current_urb->buffer + + dest = (char*)current_urb->buffer + current_urb->actual_length; space_avail = @@ -562,14 +881,19 @@ static int write_buffer (circbuf_t * buf) current_urb->actual_length += popped; total += popped; - /* If endpoint->last == 0, then transfers have not started on this endpoint */ + /* If endpoint->last == 0, then transfers have + * not started on this endpoint + */ if (endpoint->last == 0) { - udc_endpoint_write (endpoint); + if(udc_endpoint_write (endpoint)){ + /* Write pre-empted by RX */ + return -1; + } } - } /* end while */ + }/* end while */ return total; - } /* end if tx_urb */ + } return 0; } @@ -577,18 +901,22 @@ static int write_buffer (circbuf_t * buf) static int fill_buffer (circbuf_t * buf) { struct usb_endpoint_instance *endpoint = - &endpoint_instance[RECV_ENDPOINT]; + &endpoint_instance[rx_endpoint]; if (endpoint->rcv_urb && endpoint->rcv_urb->actual_length) { - unsigned int nb = endpoint->rcv_urb->actual_length; + unsigned int nb = 0; char *src = (char *) endpoint->rcv_urb->buffer; + unsigned int rx_avail = buf->totalsize - buf->size; - buf_push (buf, src, nb); - endpoint->rcv_urb->actual_length = 0; + if(rx_avail >= endpoint->rcv_urb->actual_length){ + nb = endpoint->rcv_urb->actual_length; + buf_push (buf, src, nb); + endpoint->rcv_urb->actual_length = 0; + + } return nb; } - return 0; } @@ -597,7 +925,7 @@ static int usbtty_configured (void) return usbtty_configured_flag; } -/*********************************************************************************/ +/******************************************************************************/ static void usbtty_event_handler (struct usb_device_instance *device, usb_device_event_t event, int data) @@ -619,8 +947,34 @@ static void usbtty_event_handler (struct usb_device_instance *device, } } -/*********************************************************************************/ +/******************************************************************************/ + +int usbtty_cdc_setup(struct usb_device_request *request, struct urb *urb) +{ + switch (request->bRequest){ + case ACM_SET_CONTROL_LINE_STATE: /* Implies DTE ready */ + break; + case ACM_SEND_ENCAPSULATED_COMMAND : /* Required */ + break; + case ACM_SET_LINE_ENCODING : /* DTE stop/parity bits + * per character */ + break; + case ACM_GET_ENCAPSULATED_RESPONSE : /* request response */ + break; + case ACM_GET_LINE_ENCODING : /* request DTE rate, + * stop/parity bits */ + memcpy (urb->buffer , &rs232_desc, sizeof(rs232_desc)); + urb->actual_length = sizeof(rs232_desc); + + break; + default: + return 1; + } + return 0; +} + +/******************************************************************************/ /* * Since interrupt handling has not yet been implemented, we use this function @@ -630,36 +984,29 @@ static void usbtty_event_handler (struct usb_device_instance *device, void usbtty_poll (void) { /* New interrupts? */ - pretend_interrupts (); + udc_irq(); - /* Write any output data to host buffer (do this before checking interrupts to avoid missing one) */ + /* Write any output data to host buffer + * (do this before checking interrupts to avoid missing one) + */ if (usbtty_configured ()) { write_buffer (&usbtty_output); } /* New interrupts? */ - pretend_interrupts (); - - /* Check for new data from host.. (do this after checking interrupts to get latest data) */ + udc_irq(); + + /* Check for new data from host.. + * (do this after checking interrupts to get latest data) + */ if (usbtty_configured ()) { fill_buffer (&usbtty_input); } /* New interrupts? */ - pretend_interrupts (); -} + udc_irq(); -static void pretend_interrupts (void) -{ - /* Loop while we have interrupts. - * If we don't do this, the input chain - * polling delay is likely to miss - * host requests. - */ - while (inw (UDC_IRQ_SRC) & ~UDC_SOF_Flg) { - /* Handle any new IRQs */ - omap1510_udc_irq (); - omap1510_udc_noniso_irq (); - } } + + #endif diff --git a/drivers/usbtty.h b/drivers/usbtty.h index 79c2fe57d..731b76330 100644 --- a/drivers/usbtty.h +++ b/drivers/usbtty.h @@ -2,6 +2,9 @@ * (C) Copyright 2003 * Gerry Hamel, geh@ti.com, Texas Instruments * + * (C) Copyright 2006 + * Bryan O'Donoghue, bodonoghue@codehermit.ie, CodeHermit + * * 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 @@ -21,44 +24,47 @@ #ifndef __USB_TTY_H__ #define __USB_TTY_H__ - #include "usbdcore.h" +#if defined(CONFIG_PPC) +#include "usbdcore_mpc8xx.h" +#elif defined(CONFIG_ARM) #include "usbdcore_omap1510.h" +#endif +#include -#define NUM_CONFIGS 1 -#define NUM_INTERFACES 1 -#define NUM_ENDPOINTS 3 +/* If no VendorID/ProductID is defined in config.h, pretend to be Linux + * DO NOT Reuse this Vendor/Product setup with protocol incompatible devices */ -#define EP0_MAX_PACKET_SIZE 64 +#define CONFIG_USBD_VENDORID 0x0525 /* Linux/NetChip */ +#define CONFIG_USBD_PRODUCTID_GSERIAL 0xa4a6 /* gserial */ +#define CONFIG_USBD_PRODUCTID_CDCACM 0xa4a7 /* CDC ACM */ +#define CONFIG_USBD_MANUFACTURER "Das U-Boot" +#define CONFIG_USBD_PRODUCT_NAME U_BOOT_VERSION -#define CONFIG_USBD_CONFIGURATION_STR "TTY via USB" -#define CONFIG_USBD_INTERFACE_STR "Simple Serial Data Interface - Bulk Mode" +#define CONFIG_USBD_CONFIGURATION_STR "TTY via USB" -#define CONFIG_USBD_SERIAL_OUT_ENDPOINT 2 -#define CONFIG_USBD_SERIAL_OUT_PKTSIZE 64 -#define CONFIG_USBD_SERIAL_IN_ENDPOINT 1 -#define CONFIG_USBD_SERIAL_IN_PKTSIZE 64 -#define CONFIG_USBD_SERIAL_INT_ENDPOINT 5 -#define CONFIG_USBD_SERIAL_INT_PKTSIZE 16 - +#define CONFIG_USBD_SERIAL_OUT_ENDPOINT UDC_OUT_ENDPOINT +#define CONFIG_USBD_SERIAL_OUT_PKTSIZE UDC_OUT_PACKET_SIZE +#define CONFIG_USBD_SERIAL_IN_ENDPOINT UDC_IN_ENDPOINT +#define CONFIG_USBD_SERIAL_IN_PKTSIZE UDC_IN_PACKET_SIZE +#define CONFIG_USBD_SERIAL_INT_ENDPOINT UDC_INT_ENDPOINT +#define CONFIG_USBD_SERIAL_INT_PKTSIZE UDC_INT_PACKET_SIZE +#define CONFIG_USBD_SERIAL_BULK_PKTSIZE UDC_BULK_PACKET_SIZE #define USBTTY_DEVICE_CLASS COMMUNICATIONS_DEVICE_CLASS -#define USBTTY_DEVICE_SUBCLASS COMMUNICATIONS_NO_SUBCLASS -#define USBTTY_DEVICE_PROTOCOL COMMUNICATIONS_NO_PROTOCOL - -#define USBTTY_INTERFACE_CLASS 0xFF /* Vendor Specific */ -#define USBTTY_INTERFACE_SUBCLASS 0x02 -#define USBTTY_INTERFACE_PROTOCOL 0x01 -#define USBTTY_BCD_DEVICE 0x0 -#define USBTTY_MAXPOWER 0x0 +#define USBTTY_BCD_DEVICE 0x00 +#define USBTTY_MAXPOWER 0x00 -#define STR_MANUFACTURER 1 -#define STR_PRODUCT 2 -#define STR_SERIAL 3 -#define STR_CONFIG 4 -#define STR_INTERFACE 5 +#define STR_LANG 0x00 +#define STR_MANUFACTURER 0x01 +#define STR_PRODUCT 0x02 +#define STR_SERIAL 0x03 +#define STR_CONFIG 0x04 +#define STR_DATA_INTERFACE 0x05 +#define STR_CTRL_INTERFACE 0x06 +#define STR_COUNT 0x07 #endif diff --git a/include/configs/AdderUSB.h b/include/configs/AdderUSB.h new file mode 100644 index 000000000..2112e56e0 --- /dev/null +++ b/include/configs/AdderUSB.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2006 CodeHermit. + * Bryan O'Donoghue + * + * Provides support for USB console on the Analogue & Micro Adder87x + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 + */ + +#ifndef __ADDERUSB__ +#define __ADDERUSB__ + +/* Include the board port */ +#include "Adder.h" + +#define CONFIG_USB_DEVICE /* Include UDC driver */ +#define CONFIG_USB_TTY /* Bind the TTY driver to UDC */ +#define CFG_USB_EXTC_CLK 0x02 /* Oscillator on EXTC_CLK 2 */ +#define CFG_USB_BRG_CLK 0x04 /* or use Baud rate generator 0x04 */ +#define CFG_CONSOLE_IS_IN_ENV /* Console is in env */ + +/* If you have a USB-IF assigned VendorID then you may wish to define + * your own vendor specific values either in BoardName.h or directly in + * usbd_vendor_info.h + */ + +/* +#define CONFIG_USBD_MANUFACTURER "CodeHermit.ie" +#define CONFIG_USBD_PRODUCT_NAME "Das U-Boot" +#define CONFIG_USBD_VENDORID 0xFFFF +#define CONFIG_USBD_PRODUCTID_GSERIAL 0xFFFF +#define CONFIG_USBD_PRODUCTID_CDCACM 0xFFFE +*/ + +#endif /* __ADDERUSB_H__ */ diff --git a/include/usb_cdc_acm.h b/include/usb_cdc_acm.h new file mode 100644 index 000000000..8cb16545d --- /dev/null +++ b/include/usb_cdc_acm.h @@ -0,0 +1,43 @@ +/* + * (C) Copyright 2006 + * Bryan O'Donoghue, deckard@codehermit.ie, CodeHermit + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* ACM Control Requests */ +#define ACM_SEND_ENCAPSULATED_COMMAND 0x00 +#define ACM_GET_ENCAPSULATED_RESPONSE 0x01 +#define ACM_SET_COMM_FEATURE 0x02 +#define ACM_GET_COMM_FEATRUE 0x03 +#define ACM_CLEAR_COMM_FEATURE 0x04 +#define ACM_SET_LINE_ENCODING 0x20 +#define ACM_GET_LINE_ENCODING 0x21 +#define ACM_SET_CONTROL_LINE_STATE 0x22 +#define ACM_SEND_BREAK 0x23 + +/* ACM Notification Codes */ +#define ACM_NETWORK_CONNECTION 0x00 +#define ACM_RESPONSE_AVAILABLE 0x01 +#define ACM_SERIAL_STATE 0x20 + +/* Format of response expected by a ACM_GET_LINE_ENCODING request */ +struct rs232_emu{ + unsigned long dter; + unsigned char stop_bits; + unsigned char parity; + unsigned char data_bits; +}__attribute__((packed)); diff --git a/include/usbdcore.h b/include/usbdcore.h index 6e92df13b..cb2be7280 100644 --- a/include/usbdcore.h +++ b/include/usbdcore.h @@ -576,6 +576,9 @@ struct usb_device_instance { void (*event) (struct usb_device_instance *device, usb_device_event_t event, int data); + /* Do cdc device specific control requests */ + int (*cdc_recv_setup)(struct usb_device_request *request, struct urb *urb); + /* bus interface */ struct usb_bus_instance *bus; /* which bus interface driver */ diff --git a/include/usbdcore_mpc8xx.h b/include/usbdcore_mpc8xx.h new file mode 100644 index 000000000..e54acd9a1 --- /dev/null +++ b/include/usbdcore_mpc8xx.h @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2006 Bryan O'Donoghue, CodeHermit + * bodonoghue@codehermit.ie + * + * + * 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 + +/* Mode Register */ +#define USMOD_EN 0x01 +#define USMOD_HOST 0x02 +#define USMOD_TEST 0x04 +#define USMOD_SFTE 0x08 +#define USMOD_RESUME 0x40 +#define USMOD_LSS 0x80 + +/* Endpoint Registers */ +#define USEP_RHS_NORM 0x00 +#define USEP_RHS_IGNORE 0x01 +#define USEP_RHS_NAK 0x02 +#define USEP_RHS_STALL 0x03 + +#define USEP_THS_NORM 0x00 +#define USEP_THS_IGNORE 0x04 +#define USEP_THS_NAK 0x08 +#define USEP_THS_STALL 0x0C + +#define USEP_RTE 0x10 +#define USEP_MF 0x20 + +#define USEP_TM_CONTROL 0x00 +#define USEP_TM_INT 0x100 +#define USEP_TM_BULK 0x200 +#define USEP_TM_ISO 0x300 + +/* Command Register */ +#define USCOM_EP0 0x00 +#define USCOM_EP1 0x01 +#define USCOM_EP2 0x02 +#define USCOM_EP3 0x03 + +#define USCOM_FLUSH 0x40 +#define USCOM_STR 0x80 + +/* Event Register */ +#define USB_E_RXB 0x0001 +#define USB_E_TXB 0x0002 +#define USB_E_BSY 0x0004 +#define USB_E_SOF 0x0008 +#define USB_E_TXE1 0x0010 +#define USB_E_TXE2 0x0020 +#define USB_E_TXE3 0x0040 +#define USB_E_TXE4 0x0080 +#define USB_TX_ERRMASK (USB_E_TXE1|USB_E_TXE2|USB_E_TXE3|USB_E_TXE4) +#define USB_E_IDLE 0x0100 +#define USB_E_RESET 0x0200 + +/* Mask Register */ +#define USBS_IDLE 0x01 + +/* RX Buffer Descriptor */ +#define RX_BD_OV 0x02 +#define RX_BD_CR 0x04 +#define RX_BD_AB 0x08 +#define RX_BD_NO 0x10 +#define RX_BD_PID_DATA0 0x00 +#define RX_BD_PID_DATA1 0x40 +#define RX_BD_PID_SETUP 0x80 +#define RX_BD_F 0x400 +#define RX_BD_L 0x800 +#define RX_BD_I 0x1000 +#define RX_BD_W 0x2000 +#define RX_BD_E 0x8000 + +/* Useful masks */ +#define RX_BD_PID_BITMASK (RX_BD_PID_DATA1 | RX_BD_PID_SETUP) +#define STALL_BITMASK (USEP_THS_STALL | USEP_RHS_STALL) +#define NAK_BITMASK (USEP_THS_NAK | USEP_RHS_NAK) +#define CBD_TX_BITMASK (TX_BD_R | TX_BD_L | TX_BD_TC | TX_BD_I | TX_BD_CNF) + +/* TX Buffer Descriptor */ +#define TX_BD_UN 0x02 +#define TX_BD_TO 0x04 +#define TX_BD_NO_PID 0x00 +#define TX_BD_PID_DATA0 0x80 +#define TX_BD_PID_DATA1 0xC0 +#define TX_BD_CNF 0x200 +#define TX_BD_TC 0x400 +#define TX_BD_L 0x800 +#define TX_BD_I 0x1000 +#define TX_BD_W 0x2000 +#define TX_BD_R 0x8000 + +/* Implementation specific defines */ + +#define EP_MIN_PACKET_SIZE 0x08 +#define MAX_ENDPOINTS 0x04 +#define FIFO_SIZE 0x10 +#define EP_MAX_PKT FIFO_SIZE +#define TX_RING_SIZE 0x04 +#define RX_RING_SIZE 0x06 +#define USB_MAX_PKT 0x40 +#define TOGGLE_TX_PID(x) x= ((~x)&0x40)|0x80 +#define TOGGLE_RX_PID(x) x^= 0x40 +#define EP_ATTACHED 0x01 /* Endpoint has a urb attached or not */ +#define EP_SEND_ZLP 0x02 /* Send ZLP y/n ? */ + +#define PROFF_USB 0x00000000 +#define CPM_USB_BASE 0x00000A00 + +/* UDC device defines */ +#define EP0_MAX_PACKET_SIZE EP_MAX_PKT +#define UDC_OUT_ENDPOINT 0x02 +#define UDC_OUT_PACKET_SIZE EP_MIN_PACKET_SIZE +#define UDC_IN_ENDPOINT 0x03 +#define UDC_IN_PACKET_SIZE EP_MIN_PACKET_SIZE +#define UDC_INT_ENDPOINT 0x01 +#define UDC_INT_PACKET_SIZE UDC_IN_PACKET_SIZE +#define UDC_BULK_PACKET_SIZE EP_MIN_PACKET_SIZE + +struct mpc8xx_ep { + struct urb * urb; + unsigned char pid; + unsigned char sc; + volatile cbd_t * prx; +}; + +typedef struct mpc8xx_usb{ + char usmod; /* Mode Register */ + char usaddr; /* Slave Address Register */ + char uscom; /* Command Register */ + char res1; /* Reserved */ + ushort usep[4]; + ulong res2; /* Reserved */ + ushort usber; /* Event Register */ + ushort res3; /* Reserved */ + ushort usbmr; /* Mask Register */ + char res4; /* Reserved */ + char usbs; /* Status Register */ + char res5[8]; /* Reserved */ +}usb_t; + +typedef struct mpc8xx_parameter_ram{ + ushort ep0ptr; /* Endpoint Pointer Register 0 */ + ushort ep1ptr; /* Endpoint Pointer Register 1 */ + ushort ep2ptr; /* Endpoint Pointer Register 2 */ + ushort ep3ptr; /* Endpoint Pointer Register 3 */ + uint rstate; /* Receive state */ + uint rptr; /* Receive internal data pointer */ + ushort frame_n; /* Frame number */ + ushort rbcnt; /* Receive byte count */ + uint rtemp; /* Receive temp cp use only */ + uint rxusb; /* Rx Data Temp */ + ushort rxuptr; /* Rx microcode return address temp */ +}usb_pram_t; + +typedef struct endpoint_parameter_block_pointer{ + ushort rbase; /* RxBD base address */ + ushort tbase; /* TxBD base address */ + char rfcr; /* Rx Function code */ + char tfcr; /* Tx Function code */ + ushort mrblr; /* Maximum Receive Buffer Length */ + ushort rbptr; /* RxBD pointer Next Buffer Descriptor */ + ushort tbptr; /* TxBD pointer Next Buffer Descriptor */ + ulong tstate; /* Transmit internal state */ + ulong tptr; /* Transmit internal data pointer */ + ushort tcrc; /* Transmit temp CRC */ + ushort tbcnt; /* Transmit internal bye count */ + ulong ttemp; /* Tx temp */ + ushort txuptr; /* Tx microcode return address */ + ushort res1; /* Reserved */ +}usb_epb_t; + +typedef enum mpc8xx_udc_state{ + STATE_NOT_READY, + STATE_ERROR, + STATE_READY, +}mpc8xx_udc_state_t; + +/* Declarations */ +int udc_init(void); +void udc_irq(void); +int udc_endpoint_write(struct usb_endpoint_instance *endpoint); +void udc_setup_ep(struct usb_device_instance *device, unsigned int ep, + struct usb_endpoint_instance *endpoint); +void udc_connect(void); +void udc_disconnect(void); +void udc_enable(struct usb_device_instance *device); +void udc_disable(void); +void udc_startup_events(struct usb_device_instance *device); + +/* Flow control */ +void udc_set_nak(int epid); +void udc_unset_nak (int epid); diff --git a/include/usbdcore_omap1510.h b/include/usbdcore_omap1510.h index 6ea333122..526fcd920 100644 --- a/include/usbdcore_omap1510.h +++ b/include/usbdcore_omap1510.h @@ -161,10 +161,20 @@ #define UDC_VBUS_CTRL (1 << 19) #define UDC_VBUS_MODE (1 << 18) - -void omap1510_udc_irq(void); -void omap1510_udc_noniso_irq(void); - +/* OMAP Endpoint parameters */ +#define EP0_MAX_PACKET_SIZE 64 +#define UDC_OUT_ENDPOINT 2 +#define UDC_OUT_PACKET_SIZE 64 +#define UDC_IN_ENDPOINT 1 +#define UDC_IN_PACKET_SIZE 64 +#define UDC_INT_ENDPOINT 5 +#define UDC_INT_PKTSIZE 16 +#define UDC_BULK_PKTSIZE 16 + +void udc_irq (void); +/* Flow control */ +void udc_set_nak(int epid); +void udc_unset_nak (int epid); /* Higher level functions for abstracting away from specific device */ void udc_endpoint_write(struct usb_endpoint_instance *endpoint); diff --git a/include/usbdescriptors.h b/include/usbdescriptors.h index 2d9f73934..8336c188c 100644 --- a/include/usbdescriptors.h +++ b/include/usbdescriptors.h @@ -92,33 +92,42 @@ #define COMMUNICATIONS_DEVICE_CLASS 0x02 /* c.f. CDC 4.2 Table 15 */ -#define COMMUNICATIONS_INTERFACE_CLASS 0x02 +#define COMMUNICATIONS_INTERFACE_CLASS_CONTROL 0x02 +#define COMMUNICATIONS_INTERFACE_CLASS_DATA 0x0A +#define COMMUNICATIONS_INTERFACE_CLASS_VENDOR 0x0FF /* c.f. CDC 4.3 Table 16 */ -#define COMMUNICATIONS_NO_SUBCLASS 0x00 +#define COMMUNICATIONS_NO_SUBCLASS 0x00 #define COMMUNICATIONS_DLCM_SUBCLASS 0x01 -#define COMMUNICATIONS_ACM_SUBCLASS 0x02 -#define COMMUNICATIONS_TCM_SUBCLASS 0x03 +#define COMMUNICATIONS_ACM_SUBCLASS 0x02 +#define COMMUNICATIONS_TCM_SUBCLASS 0x03 #define COMMUNICATIONS_MCCM_SUBCLASS 0x04 -#define COMMUNICATIONS_CCM_SUBCLASS 0x05 +#define COMMUNICATIONS_CCM_SUBCLASS 0x05 #define COMMUNICATIONS_ENCM_SUBCLASS 0x06 #define COMMUNICATIONS_ANCM_SUBCLASS 0x07 /* c.f. WMCD 5.1 */ #define COMMUNICATIONS_WHCM_SUBCLASS 0x08 -#define COMMUNICATIONS_DMM_SUBCLASS 0x09 +#define COMMUNICATIONS_DMM_SUBCLASS 0x09 #define COMMUNICATIONS_MDLM_SUBCLASS 0x0a #define COMMUNICATIONS_OBEX_SUBCLASS 0x0b -/* c.f. CDC 4.6 Table 18 */ +/* c.f. CDC 4.4 Table 17 */ +#define COMMUNICATIONS_NO_PROTOCOL 0x00 +#define COMMUNICATIONS_V25TER_PROTOCOL 0x01 /*Common AT Hayes compatible*/ + +/* c.f. CDC 4.5 Table 18 */ #define DATA_INTERFACE_CLASS 0x0a +/* c.f. CDC 4.6 No Table */ +#define DATA_INTERFACE_SUBCLASS_NONE 0x00 /* No subclass pertinent */ + /* c.f. CDC 4.7 Table 19 */ -#define COMMUNICATIONS_NO_PROTOCOL 0x00 +#define DATA_INTERFACE_PROTOCOL_NONE 0x00 /* No class protcol required */ /* c.f. CDC 5.2.3 Table 24 */ -#define CS_INTERFACE 0x24 +#define CS_INTERFACE 0x24 #define CS_ENDPOINT 0x25 /* @@ -128,7 +137,7 @@ * c.f. WMCD 5.3 Table 5.3 */ -#define USB_ST_HEADER 0x00 +#define USB_ST_HEADER 0x00 #define USB_ST_CMF 0x01 #define USB_ST_ACMF 0x02 #define USB_ST_DLMF 0x03 @@ -137,18 +146,18 @@ #define USB_ST_UF 0x06 #define USB_ST_CSF 0x07 #define USB_ST_TOMF 0x08 -#define USB_ST_USBTF 0x09 +#define USB_ST_USBTF 0x09 #define USB_ST_NCT 0x0a #define USB_ST_PUF 0x0b #define USB_ST_EUF 0x0c #define USB_ST_MCMF 0x0d #define USB_ST_CCMF 0x0e #define USB_ST_ENF 0x0f -#define USB_ST_ATMNF 0x10 +#define USB_ST_ATMNF 0x10 #define USB_ST_WHCM 0x11 #define USB_ST_MDLM 0x12 -#define USB_ST_MDLMD 0x13 +#define USB_ST_MDLMD 0x13 #define USB_ST_DMM 0x14 #define USB_ST_OBEX 0x15 #define USB_ST_CS 0x16 @@ -312,7 +321,8 @@ struct usb_class_union_function_descriptor { u8 bDescriptorType; u8 bDescriptorSubtype; /* 0x06 */ u8 bMasterInterface; - u8 bSlaveInterface0[0]; + //u8 bSlaveInterface0[0]; + u8 bSlaveInterface0; } __attribute__ ((packed)); struct usb_class_country_selection_descriptor { -- cgit v1.2.3 From 386eda022473394ad8f36b86f2bdc9b4cb816291 Mon Sep 17 00:00:00 2001 From: Wolfgang Denk Date: Wed, 14 Jun 2006 18:14:56 +0200 Subject: Code cleanup --- CHANGELOG | 4 +- README | 22 +- drivers/usbdcore_ep0.c | 8 +- drivers/usbdcore_mpc8xx.c | 1124 +++++++++++++++++------------------ drivers/usbdcore_omap1510.c | 4 +- drivers/usbtty.c | 164 ++--- drivers/usbtty.h | 4 +- include/asm-arm/arch-pxa/pxa-regs.h | 2 - include/configs/AdderUSB.h | 8 +- include/usb_cdc_acm.h | 2 +- include/usbdcore_mpc8xx.h | 14 +- include/usbdescriptors.h | 2 +- 12 files changed, 673 insertions(+), 685 deletions(-) (limited to 'drivers') diff --git a/CHANGELOG b/CHANGELOG index 449db66fd..bc334820d 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,7 +2,9 @@ Changes since U-Boot 1.1.4: ====================================================================== -* Various USB related patches +* Code cleanup + +* Various USB related patches - Add support for mpc8xx USB device. - Add support for Common Device Class - Abstract Control Model USB console. - Add support for flow control in USB slave devices. diff --git a/README b/README index 284a68823..178e51e91 100644 --- a/README +++ b/README @@ -870,7 +870,7 @@ The following options need to be configured: attach your usb cable. The Unix command "dmesg" should print it has found a new device. The environment variable usbtty can be set to gserial or cdc_acm to enable your device to - appear to a USB host as a Linux gserial device or a + appear to a USB host as a Linux gserial device or a Common Device Class Abstract Control Model serial device. If you select usbtty = gserial you should be able to enumerate a Linux host by @@ -878,14 +878,14 @@ The following options need to be configured: else if using cdc_acm, simply setting the environment variable usbtty to be cdc_acm should suffice. The following might be defined in YourBoardName.h - + CONFIG_USB_DEVICE Define this to build a UDC device CONFIG_USB_TTY Define this to have a tty type of device available to talk to the UDC device - + CFG_CONSOLE_IS_IN_ENV Define this if you want stdin, stdout &/or stderr to be set to usbtty. @@ -893,15 +893,15 @@ The following options need to be configured: mpc8xx: CFG_USB_EXTC_CLK 0xBLAH Derive USB clock from external clock "blah" - - CFG_USB_EXTC_CLK 0x02 - + - CFG_USB_EXTC_CLK 0x02 + CFG_USB_BRG_CLK 0xBLAH Derive USB clock from brgclk - CFG_USB_BRG_CLK 0x04 - If you have a USB-IF assigned VendorID then you may wish to + If you have a USB-IF assigned VendorID then you may wish to define your own vendor specific values either in BoardName.h - or directly in usbd_vendor_info.h. If you don't define + or directly in usbd_vendor_info.h. If you don't define CONFIG_USBD_MANUFACTURER, CONFIG_USBD_PRODUCT_NAME, CONFIG_USBD_VENDORID and CONFIG_USBD_PRODUCTID, then U-Boot should pretend to be a Linux device to it's target host. @@ -909,7 +909,7 @@ The following options need to be configured: CONFIG_USBD_MANUFACTURER Define this string as the name of your company for - CONFIG_USBD_MANUFACTURER "my company" - + CONFIG_USBD_PRODUCT_NAME Define this string as the name of your product - CONFIG_USBD_PRODUCT_NAME "acme usb device" @@ -919,13 +919,13 @@ The following options need to be configured: Implementors Forum. This *must* be a genuine Vendor ID to avoid polluting the USB namespace. - CONFIG_USBD_VENDORID 0xFFFF - + CONFIG_USBD_PRODUCTID Define this as the unique Product ID for your device - CONFIG_USBD_PRODUCTID 0xFFFF - - + + - MMC Support: The MMC controller on the Intel PXA is supported. To enable this define CONFIG_MMC. The MMC can be diff --git a/drivers/usbdcore_ep0.c b/drivers/usbdcore_ep0.c index 5e7443be8..1e44f322a 100644 --- a/drivers/usbdcore_ep0.c +++ b/drivers/usbdcore_ep0.c @@ -44,7 +44,7 @@ * XXX * * As alluded to above, a simple callback cdc_recv_setup has been implemented - * in the usb_device data structure to facilicate passing + * in the usb_device data structure to facilicate passing * Common Device Class packets to a function driver. * * XXX @@ -221,7 +221,7 @@ static int ep0_get_descriptor (struct usb_device_instance *device, (struct usb_device_descriptor *) urb->buffer; } - dbg_ep0(3, "copied device configuration, actual_length: 0x%x", urb->actual_length); + dbg_ep0(3, "copied device configuration, actual_length: 0x%x", urb->actual_length); break; case USB_DESCRIPTOR_TYPE_CONFIGURATION: @@ -268,7 +268,7 @@ static int ep0_get_descriptor (struct usb_device_instance *device, serial_printf("Invalid string index %d\n", index); return -1; } - dbg_ep0(3, "string_descriptor: %p length %d", string_descriptor, string_descriptor->bLength); + dbg_ep0(3, "string_descriptor: %p length %d", string_descriptor, string_descriptor->bLength); copy_config (urb, string_descriptor, string_descriptor->bLength, max); } break; @@ -344,7 +344,7 @@ static int ep0_get_descriptor (struct usb_device_instance *device, case USB_DESCRIPTOR_TYPE_DEVICE_QUALIFIER: { /* If a USB device supports both a full speed and low speed operation - * we must send a Device_Qualifier descriptor here + * we must send a Device_Qualifier descriptor here */ return -1; } diff --git a/drivers/usbdcore_mpc8xx.c b/drivers/usbdcore_mpc8xx.c index 9bd2c231a..e87284b17 100644 --- a/drivers/usbdcore_mpc8xx.c +++ b/drivers/usbdcore_mpc8xx.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2006 by Bryan O'Donoghue, CodeHermit - * bodonoghue@CodeHermit.ie + * bodonoghue@CodeHermit.ie * * References * DasUBoot/drivers/usbdcore_omap1510.c, for design and implementation ideas. @@ -12,7 +12,7 @@ * * 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 + * 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 @@ -24,12 +24,12 @@ /* * Notes : - * 1. #define __SIMULATE_ERROR__ to inject a CRC error into every 2nd TX + * 1. #define __SIMULATE_ERROR__ to inject a CRC error into every 2nd TX * packet to force the USB re-transmit protocol. * * 2. #define __DEBUG_UDC__ to switch on debug tracing to serial console - * be careful that tracing doesn't create Hiesen-bugs with respect to - * response timeouts to control requests. + * be careful that tracing doesn't create Hiesen-bugs with respect to + * response timeouts to control requests. * * 3. This driver should be able to support any higher level driver that * that wants to do either of the two standard UDC implementations @@ -41,18 +41,18 @@ * * 5. For some reason, cbd_datlen is *always* +2 the value it should be. * this means that having an RX cbd of 16 bytes is not possible, since - * the same size is reported for 14 bytes received as 16 bytes received + * the same size is reported for 14 bytes received as 16 bytes received * until we can find out why this happens, RX cbds must be limited to 8 * bytes. TODO: check errata for this behaviour. * * 6. Right now this code doesn't support properly powering up with the USB - * cable attached to the USB host my development board the Adder87x doesn't - * have a pull-up fitted to allow this, so it is necessary to power the - * board and *then* attached the USB cable to the host. However somebody - * with a different design in their board may be able to keep the cable - * constantly connected and simply enable/disable a pull-up re - * figure 31.1 in MPC885RM.pdf instead of having to power up the board and - * then attach the cable ! + * cable attached to the USB host my development board the Adder87x doesn't + * have a pull-up fitted to allow this, so it is necessary to power the + * board and *then* attached the USB cable to the host. However somebody + * with a different design in their board may be able to keep the cable + * constantly connected and simply enable/disable a pull-up re + * figure 31.1 in MPC885RM.pdf instead of having to power up the board and + * then attach the cable ! * */ #include @@ -60,7 +60,7 @@ #if defined(CONFIG_MPC885_FAMILY) && defined(CONFIG_USB_DEVICE) #include -#include "usbdcore.h" +#include "usbdcore.h" #include "usbdcore_mpc8xx.h" #include "usbdcore_ep0.h" @@ -68,24 +68,24 @@ serial_printf("ERROR : [%s] %s:%d: "fmt,\ __FILE__,__FUNCTION__,__LINE__, ##args) #ifdef __DEBUG_UDC__ - #define DBG(fmt,args...)\ +#define DBG(fmt,args...)\ serial_printf("[%s] %s:%d: "fmt,\ __FILE__,__FUNCTION__,__LINE__, ##args) #else - #define DBG(fmt,args...) +#define DBG(fmt,args...) #endif /* Static Data */ #ifdef __SIMULATE_ERROR__ - static char err_poison_test = 0; +static char err_poison_test = 0; #endif static struct mpc8xx_ep ep_ref[MAX_ENDPOINTS]; static u32 address_base = STATE_NOT_READY; static mpc8xx_udc_state_t udc_state = 0; static struct usb_device_instance *udc_device = 0; -static volatile usb_epb_t *endpoints[MAX_ENDPOINTS]; -static volatile cbd_t * tx_cbd[TX_RING_SIZE]; -static volatile cbd_t * rx_cbd[RX_RING_SIZE]; +static volatile usb_epb_t *endpoints[MAX_ENDPOINTS]; +static volatile cbd_t *tx_cbd[TX_RING_SIZE]; +static volatile cbd_t *rx_cbd[RX_RING_SIZE]; static volatile immap_t *immr = 0; static volatile cpm8xx_t *cp = 0; static volatile usb_pram_t *usb_paramp = 0; @@ -95,87 +95,87 @@ static int tx_ct = 0; /* Static Function Declarations */ static void mpc8xx_udc_state_transition_up (usb_device_state_t initial, - usb_device_state_t final); + usb_device_state_t final); static void mpc8xx_udc_state_transition_down (usb_device_state_t initial, - usb_device_state_t final); + usb_device_state_t final); static void mpc8xx_udc_stall (unsigned int ep); -static void mpc8xx_udc_flush_tx_fifo(int epid); -static void mpc8xx_udc_flush_rx_fifo(void); +static void mpc8xx_udc_flush_tx_fifo (int epid); +static void mpc8xx_udc_flush_rx_fifo (void); static void mpc8xx_udc_clear_rxbd (volatile cbd_t * rx_cbdp); -static void mpc8xx_udc_init_tx(struct usb_endpoint_instance *epi, - struct urb * tx_urb); -static void mpc8xx_udc_dump_request(struct usb_device_request *request); -static void mpc8xx_udc_clock_init (volatile immap_t * immr, - volatile cpm8xx_t * cp); +static void mpc8xx_udc_init_tx (struct usb_endpoint_instance *epi, + struct urb *tx_urb); +static void mpc8xx_udc_dump_request (struct usb_device_request *request); +static void mpc8xx_udc_clock_init (volatile immap_t * immr, + volatile cpm8xx_t * cp); static int mpc8xx_udc_ep_tx (struct usb_endpoint_instance *epi); static int mpc8xx_udc_epn_rx (unsigned int epid, volatile cbd_t * rx_cbdp); -static void mpc8xx_udc_ep0_rx(volatile cbd_t * rx_cbdp); +static void mpc8xx_udc_ep0_rx (volatile cbd_t * rx_cbdp); static void mpc8xx_udc_cbd_init (void); static void mpc8xx_udc_endpoint_init (void); static void mpc8xx_udc_cbd_attach (int ep, uchar tx_size, uchar rx_size); static u32 mpc8xx_udc_alloc (u32 data_size, u32 alignment); static int mpc8xx_udc_ep0_rx_setup (volatile cbd_t * rx_cbdp); static void mpc8xx_udc_set_nak (unsigned int ep); -static short mpc8xx_udc_handle_txerr(void); -static void mpc8xx_udc_advance_rx(volatile cbd_t ** rx_cbdp, int epid); +static short mpc8xx_udc_handle_txerr (void); +static void mpc8xx_udc_advance_rx (volatile cbd_t ** rx_cbdp, int epid); /****************************************************************************** - Global Linkage + Global Linkage *****************************************************************************/ /* udc_init * * Do initial bus gluing */ -int udc_init(void) +int udc_init (void) { /* Init various pointers */ immr = (immap_t *) CFG_IMMR; - cp = (cpm8xx_t *)&(immr->im_cpm); - usb_paramp = (usb_pram_t*)&(cp->cp_dparam[PROFF_USB]); - usbp = (usb_t *) &(cp->cp_scc[0]); + cp = (cpm8xx_t *) & (immr->im_cpm); + usb_paramp = (usb_pram_t *) & (cp->cp_dparam[PROFF_USB]); + usbp = (usb_t *) & (cp->cp_scc[0]); + + memset (ep_ref, 0x00, (sizeof (struct mpc8xx_ep) * MAX_ENDPOINTS)); - memset(ep_ref, 0x00, (sizeof(struct mpc8xx_ep) * MAX_ENDPOINTS)); - udc_device = 0; udc_state = STATE_NOT_READY; - - usbp->usmod= 0x00; - usbp->uscom= 0; - + + usbp->usmod = 0x00; + usbp->uscom = 0; + /* Set USB Frame #0, Respond at Address & Get a clock source */ usbp->usaddr = 0x00; mpc8xx_udc_clock_init (immr, cp); - + /* PA15, PA14 as perhiperal USBRXD and USBOE */ - immr->im_ioport.iop_padir&= ~0x0003; - immr->im_ioport.iop_papar|= 0x0003; - + immr->im_ioport.iop_padir &= ~0x0003; + immr->im_ioport.iop_papar |= 0x0003; + /* PC11/PC10 as peripheral USBRXP USBRXN */ - immr->im_ioport.iop_pcso|= 0x0030; - + immr->im_ioport.iop_pcso |= 0x0030; + /* PC7/PC6 as perhiperal USBTXP and USBTXN */ - immr->im_ioport.iop_pcdir|= 0x0300; - immr->im_ioport.iop_pcpar|= 0x0300; - + immr->im_ioport.iop_pcdir |= 0x0300; + immr->im_ioport.iop_pcpar |= 0x0300; + /* Set the base address */ - address_base = (u32)(cp->cp_dpmem + CPM_USB_BASE); + address_base = (u32) (cp->cp_dpmem + CPM_USB_BASE); /* Initialise endpoints and circular buffers */ - mpc8xx_udc_endpoint_init(); - mpc8xx_udc_cbd_init(); - + mpc8xx_udc_endpoint_init (); + mpc8xx_udc_cbd_init (); + /* Assign allocated Dual Port Endpoint descriptors */ - usb_paramp->ep0ptr = (u32)endpoints[0]; - usb_paramp->ep1ptr = (u32)endpoints[1]; - usb_paramp->ep2ptr = (u32)endpoints[2]; - usb_paramp->ep3ptr = (u32)endpoints[3]; + usb_paramp->ep0ptr = (u32) endpoints[0]; + usb_paramp->ep1ptr = (u32) endpoints[1]; + usb_paramp->ep2ptr = (u32) endpoints[2]; + usb_paramp->ep3ptr = (u32) endpoints[3]; usb_paramp->frame_n = 0; - DBG("ep0ptr=0x%08x ep1ptr=0x%08x ep2ptr=0x%08x ep3ptr=0x%08x\n", - usb_paramp->ep0ptr, usb_paramp->ep1ptr, usb_paramp->ep2ptr, - usb_paramp->ep3ptr); - + DBG ("ep0ptr=0x%08x ep1ptr=0x%08x ep2ptr=0x%08x ep3ptr=0x%08x\n", + usb_paramp->ep0ptr, usb_paramp->ep1ptr, usb_paramp->ep2ptr, + usb_paramp->ep3ptr); + return 0; } @@ -183,149 +183,147 @@ int udc_init(void) * * Poll for whatever events may have occured */ -void udc_irq(void) +void udc_irq (void) { int epid = 0; - volatile cbd_t * rx_cbdp = 0; - volatile cbd_t * rx_cbdp_base = 0; + volatile cbd_t *rx_cbdp = 0; + volatile cbd_t *rx_cbdp_base = 0; - if(udc_state!=STATE_READY){ + if (udc_state != STATE_READY) { return; } - - if(usbp->usber&USB_E_BSY){ + + if (usbp->usber & USB_E_BSY) { /* This shouldn't happen. If it does then it's a bug ! */ - usbp->usber|=USB_E_BSY; - mpc8xx_udc_flush_rx_fifo(); + usbp->usber |= USB_E_BSY; + mpc8xx_udc_flush_rx_fifo (); } - /* Scan all RX/Bidirectional Endpoints for RX data. */ - for(epid = 0; epidcbd_sc&RX_BD_E)){ - - if(rx_cbdp->cbd_sc&0x1F){ + + do { + if (!(rx_cbdp->cbd_sc & RX_BD_E)) { + + if (rx_cbdp->cbd_sc & 0x1F) { /* Corrupt data discard it. - * Controller has NAK'd this packet. + * Controller has NAK'd this packet. */ - mpc8xx_udc_clear_rxbd(rx_cbdp); + mpc8xx_udc_clear_rxbd (rx_cbdp); - }else{ - if(!epid){ - mpc8xx_udc_ep0_rx(rx_cbdp); + } else { + if (!epid) { + mpc8xx_udc_ep0_rx (rx_cbdp); - }else{ + } else { /* Process data */ - mpc8xx_udc_set_nak(epid); - mpc8xx_udc_epn_rx(epid,rx_cbdp); - mpc8xx_udc_clear_rxbd(rx_cbdp); - } + mpc8xx_udc_set_nak (epid); + mpc8xx_udc_epn_rx (epid, rx_cbdp); + mpc8xx_udc_clear_rxbd (rx_cbdp); + } } - + /* Advance RX CBD pointer */ - mpc8xx_udc_advance_rx(&rx_cbdp, epid); + mpc8xx_udc_advance_rx (&rx_cbdp, epid); ep_ref[epid].prx = rx_cbdp; - }else{ + } else { /* Advance RX CBD pointer */ - mpc8xx_udc_advance_rx(&rx_cbdp, epid); + mpc8xx_udc_advance_rx (&rx_cbdp, epid); } - }while(rx_cbdp != rx_cbdp_base); + } while (rx_cbdp != rx_cbdp_base); } /* Handle TX events as appropiate, the correct place to do this is * in a tx routine. Perhaps TX on epn was pre-empted by ep0 */ - if(usbp->usber&USB_E_TXB){ - usbp->usber|=USB_E_TXB; + if (usbp->usber & USB_E_TXB) { + usbp->usber |= USB_E_TXB; } - - if(usbp->usber&(USB_TX_ERRMASK)){ - mpc8xx_udc_handle_txerr(); + + if (usbp->usber & (USB_TX_ERRMASK)) { + mpc8xx_udc_handle_txerr (); } /* Switch to the default state, respond at the default address */ - if(usbp->usber&USB_E_RESET){ - usbp->usber|=USB_E_RESET; - usbp->usaddr = 0x00; + if (usbp->usber & USB_E_RESET) { + usbp->usber |= USB_E_RESET; + usbp->usaddr = 0x00; udc_device->device_state = STATE_DEFAULT; } - /*if(usbp->usber&USB_E_IDLE){ - We could suspend here ! - usbp->usber|=USB_E_IDLE; - DBG("idle state change\n"); - } - if(usbp->usbs){ - We could resume here when IDLE is deasserted ! - Not worth doing, so long as we are self powered though. - }*/ + /* if(usbp->usber&USB_E_IDLE){ + We could suspend here ! + usbp->usber|=USB_E_IDLE; + DBG("idle state change\n"); + } + if(usbp->usbs){ + We could resume here when IDLE is deasserted ! + Not worth doing, so long as we are self powered though. + } + */ return; } - - /* udc_endpoint_write * * Write some data to an endpoint */ -int udc_endpoint_write(struct usb_endpoint_instance *epi) +int udc_endpoint_write (struct usb_endpoint_instance *epi) { int ep = 0; short epid = 1, unnak = 0, ret = 0; - if(udc_state != STATE_READY){ - ERR("invalid udc_state != STATE_READY!\n"); + if (udc_state != STATE_READY) { + ERR ("invalid udc_state != STATE_READY!\n"); return -1; } - if(!udc_device || !epi){ + if (!udc_device || !epi) { return -1; } - - if(udc_device->device_state!=STATE_CONFIGURED){ + + if (udc_device->device_state != STATE_CONFIGURED) { return -1; } ep = epi->endpoint_address & 0x03; - if(ep >= MAX_ENDPOINTS){ + if (ep >= MAX_ENDPOINTS) { return -1; } - + /* Set NAK for all RX endpoints during TX */ - for(epid = 1; epidusep[epid]&( USEP_THS_NAK | USEP_RHS_NAK ))){ - unnak |= 1<usep[epid] & (USEP_THS_NAK | USEP_RHS_NAK))) { + unnak |= 1 << epid; } - mpc8xx_udc_set_nak(epid); + mpc8xx_udc_set_nak (epid); } - mpc8xx_udc_init_tx(&udc_device->bus->endpoint_array[ep],epi->tx_urb); - ret = mpc8xx_udc_ep_tx(&udc_device->bus->endpoint_array[ep]); - + mpc8xx_udc_init_tx (&udc_device->bus->endpoint_array[ep], + epi->tx_urb); + ret = mpc8xx_udc_ep_tx (&udc_device->bus->endpoint_array[ep]); + /* Remove temporary NAK */ - for(epid = 1; epid= MAX_ENDPOINTS){ + + if (ep >= MAX_ENDPOINTS) { goto err; } epi = &udc_device->bus->endpoint_array[ep]; - if(!epi){ + if (!epi) { goto err; } - if(!ep_ref[ep].urb){ - ep_ref[ep].urb = usbd_alloc_urb(udc_device, - udc_device->bus->endpoint_array); - if(!ep_ref[ep].urb){ + if (!ep_ref[ep].urb) { + ep_ref[ep].urb = usbd_alloc_urb (udc_device, udc_device->bus->endpoint_array); + if (!ep_ref[ep].urb) { goto err; } - }else{ + } else { ep_ref[ep].urb->actual_length = 0; } - switch(direction){ - case USB_DIR_IN: - epi->tx_urb = ep_ref[ep].urb; - break; - case USB_DIR_OUT: - epi->rcv_urb = ep_ref[ep].urb; - break; - default: - goto err; + switch (direction) { + case USB_DIR_IN: + epi->tx_urb = ep_ref[ep].urb; + break; + case USB_DIR_OUT: + epi->rcv_urb = ep_ref[ep].urb; + break; + default: + goto err; } return 0; -err: + err: udc_state = STATE_ERROR; return -1; } @@ -377,83 +374,84 @@ err: * Associate U-Boot software endpoints to mpc8xx endpoint parameter ram * Isochronous endpoints aren't yet supported! */ -void udc_setup_ep(struct usb_device_instance *device, unsigned int ep, - struct usb_endpoint_instance *epi) +void udc_setup_ep (struct usb_device_instance *device, unsigned int ep, + struct usb_endpoint_instance *epi) { uchar direction = 0; int ep_attrib = 0; - if(epi && (ep < MAX_ENDPOINTS)){ - - if(ep == 0){ - if (epi->rcv_attributes!=USB_ENDPOINT_XFER_CONTROL - ||epi->tx_attributes!= - USB_ENDPOINT_XFER_CONTROL){ + if (epi && (ep < MAX_ENDPOINTS)) { - /* ep0 must be a control endpoint*/ + if (ep == 0) { + if (epi->rcv_attributes != USB_ENDPOINT_XFER_CONTROL + || epi->tx_attributes != + USB_ENDPOINT_XFER_CONTROL) { + + /* ep0 must be a control endpoint */ udc_state = STATE_ERROR; return; } - if(!(ep_ref[ep].sc & EP_ATTACHED)){ - mpc8xx_udc_cbd_attach (ep, epi->tx_packetSize, - epi->rcv_packetSize); + if (!(ep_ref[ep].sc & EP_ATTACHED)) { + mpc8xx_udc_cbd_attach (ep, epi->tx_packetSize, + epi->rcv_packetSize); } usbp->usep[ep] = 0x0000; return; } - - if ((epi->endpoint_address & USB_ENDPOINT_DIR_MASK) - == USB_DIR_IN) { + + if ((epi->endpoint_address & USB_ENDPOINT_DIR_MASK) + == USB_DIR_IN) { direction = 1; ep_attrib = epi->tx_attributes; epi->rcv_packetSize = 0; - ep_ref[ep].sc |= USB_DIR_IN; + ep_ref[ep].sc |= USB_DIR_IN; } else { - + direction = 0; ep_attrib = epi->rcv_attributes; - epi->tx_packetSize = 0; + epi->tx_packetSize = 0; ep_ref[ep].sc &= ~USB_DIR_IN; } - if(mpc8xx_udc_assign_urb(ep, epi->endpoint_address - &USB_ENDPOINT_DIR_MASK)){ + if (mpc8xx_udc_assign_urb (ep, epi->endpoint_address + & USB_ENDPOINT_DIR_MASK)) { return; } - switch(ep_attrib){ - case USB_ENDPOINT_XFER_CONTROL: - if(!(ep_ref[ep].sc & EP_ATTACHED)){ + switch (ep_attrib) { + case USB_ENDPOINT_XFER_CONTROL: + if (!(ep_ref[ep].sc & EP_ATTACHED)) { + mpc8xx_udc_cbd_attach (ep, + epi->tx_packetSize, + epi->rcv_packetSize); + } + usbp->usep[ep] = ep << 12; + epi->rcv_urb = epi->tx_urb = ep_ref[ep].urb; + + break; + case USB_ENDPOINT_XFER_BULK: + case USB_ENDPOINT_XFER_INT: + if (!(ep_ref[ep].sc & EP_ATTACHED)) { + if (direction) { mpc8xx_udc_cbd_attach (ep, - epi->tx_packetSize, - epi->rcv_packetSize); + epi->tx_packetSize, + 0); + } else { + mpc8xx_udc_cbd_attach (ep, + 0, + epi->rcv_packetSize); } - usbp->usep[ep] = ep<<12; - epi->rcv_urb = epi->tx_urb = ep_ref[ep].urb; + } + usbp->usep[ep] = (ep << 12) | ((ep_attrib) << 8); - break; - case USB_ENDPOINT_XFER_BULK : - case USB_ENDPOINT_XFER_INT: - if(!(ep_ref[ep].sc & EP_ATTACHED)){ - if(direction){ - mpc8xx_udc_cbd_attach (ep, - epi->tx_packetSize, 0); - }else{ - mpc8xx_udc_cbd_attach (ep, - 0, epi->rcv_packetSize); - } - } - usbp->usep[ep]= (ep<<12)|((ep_attrib)<<8); - - break; - case USB_ENDPOINT_XFER_ISOC: - default: - serial_printf("Error endpoint attrib %d>3\n", - ep_attrib); - udc_state = STATE_ERROR; - break; + break; + case USB_ENDPOINT_XFER_ISOC: + default: + serial_printf ("Error endpoint attrib %d>3\n", ep_attrib); + udc_state = STATE_ERROR; + break; } } @@ -463,54 +461,53 @@ void udc_setup_ep(struct usb_device_instance *device, unsigned int ep, * * Move state, switch on the USB */ -void udc_connect(void) +void udc_connect (void) { - /* Enable pull-up resistor on D+ + /* Enable pull-up resistor on D+ * TODO: fit a pull-up resistor to drive SE0 for > 2.5us */ - - if(udc_state!=STATE_ERROR){ + + if (udc_state != STATE_ERROR) { udc_state = STATE_READY; - usbp->usmod|= USMOD_EN; + usbp->usmod |= USMOD_EN; } -} +} /* udc_disconnect * * Disconnect is not used but, is included for completeness */ -void udc_disconnect(void) +void udc_disconnect (void) { /* Disable pull-up resistor on D- * TODO: fix a pullup resistor to control this */ - if(udc_state!=STATE_ERROR){ + if (udc_state != STATE_ERROR) { udc_state = STATE_NOT_READY; } - usbp->usmod&=~USMOD_EN; + usbp->usmod &= ~USMOD_EN; } /* udc_enable - * + * * Grab an EP0 URB, register interest in a subset of USB events */ -void udc_enable(struct usb_device_instance *device) +void udc_enable (struct usb_device_instance *device) { - if(udc_state == STATE_ERROR){ + if (udc_state == STATE_ERROR) { return; } udc_device = device; - - if(!ep_ref[0].urb){ - ep_ref[0].urb = usbd_alloc_urb(device, - device->bus->endpoint_array); + + if (!ep_ref[0].urb) { + ep_ref[0].urb = usbd_alloc_urb (device, device->bus->endpoint_array); } /* Register interest in all events except SOF, enable transceiver */ - usbp->usber= 0x03FF; - usbp->usbmr= 0x02F7; + usbp->usber = 0x03FF; + usbp->usbmr = 0x02F7; return; } @@ -519,26 +516,26 @@ void udc_enable(struct usb_device_instance *device) * * disable the currently hooked device */ -void udc_disable(void) +void udc_disable (void) { int i = 0; - if(udc_state == STATE_ERROR){ - DBG("Won't disable UDC. udc_state==STATE_ERROR !\n"); + if (udc_state == STATE_ERROR) { + DBG ("Won't disable UDC. udc_state==STATE_ERROR !\n"); return; } udc_device = 0; - for(;iusbmr= 0x00; - usbp->usmod= ~USMOD_EN; + + usbp->usbmr = 0x00; + usbp->usmod = ~USMOD_EN; udc_state = STATE_NOT_READY; } @@ -546,46 +543,46 @@ void udc_disable(void) * * Enable the specified device */ -void udc_startup_events(struct usb_device_instance *device) +void udc_startup_events (struct usb_device_instance *device) { - udc_enable(device); - if(udc_state == STATE_READY){ + udc_enable (device); + if (udc_state == STATE_READY) { usbd_device_event_irq (device, DEVICE_CREATE, 0); } } /* udc_set_nak - * + * * Allow upper layers to signal lower layers should not accept more RX data * */ -void udc_set_nak(int epid) +void udc_set_nak (int epid) { - if(epid){ - mpc8xx_udc_set_nak(epid); + if (epid) { + mpc8xx_udc_set_nak (epid); } } -/* udc_unset_nak - * +/* udc_unset_nak + * * Suspend sending of NAK tokens for DATA OUT tokens on a given endpoint. * Switch off NAKing on this endpoint to accept more data output from host. * */ void udc_unset_nak (int epid) { - if(epid > MAX_ENDPOINTS){ + if (epid > MAX_ENDPOINTS) { return; } - if(usbp->usep[epid]&(USEP_THS_NAK | USEP_RHS_NAK)){ - usbp->usep[epid]&= ~(USEP_THS_NAK | USEP_RHS_NAK); + if (usbp->usep[epid] & (USEP_THS_NAK | USEP_RHS_NAK)) { + usbp->usep[epid] &= ~(USEP_THS_NAK | USEP_RHS_NAK); __asm__ ("eieio"); } } /****************************************************************************** - Static Linkage + Static Linkage ******************************************************************************/ /* udc_state_transition_up @@ -633,10 +630,10 @@ void udc_unset_nak (int epid) * state, then no state transitions will take place. * */ - + static void mpc8xx_udc_state_transition_up (usb_device_state_t initial, - usb_device_state_t final) -{ + usb_device_state_t final) +{ if (initial < final) { switch (initial) { case STATE_ATTACHED: @@ -665,13 +662,13 @@ static void mpc8xx_udc_state_transition_up (usb_device_state_t initial, } static void mpc8xx_udc_state_transition_down (usb_device_state_t initial, - usb_device_state_t final) + usb_device_state_t final) { if (initial > final) { switch (initial) { case STATE_CONFIGURED: - usbd_device_event_irq (udc_device, - DEVICE_DE_CONFIGURED, 0); + usbd_device_event_irq (udc_device, + DEVICE_DE_CONFIGURED, 0); if (final == STATE_ADDRESSED) break; case STATE_ADDRESSED: @@ -679,13 +676,13 @@ static void mpc8xx_udc_state_transition_down (usb_device_state_t initial, if (final == STATE_DEFAULT) break; case STATE_DEFAULT: - usbd_device_event_irq (udc_device, - DEVICE_POWER_INTERRUPTION, 0); + usbd_device_event_irq (udc_device, + DEVICE_POWER_INTERRUPTION, 0); if (final == STATE_POWERED) break; case STATE_POWERED: usbd_device_event_irq (udc_device, DEVICE_HUB_RESET, - 0); + 0); case STATE_ATTACHED: break; default: @@ -708,7 +705,7 @@ static void mpc8xx_udc_stall (unsigned int ep) * * Force returning of NAK responses for the given endpoint as a kind of very * simple flow control - */ + */ static void mpc8xx_udc_set_nak (unsigned int ep) { usbp->usep[ep] |= NAK_BITMASK; @@ -720,27 +717,26 @@ static void mpc8xx_udc_set_nak (unsigned int ep) * Handle errors relevant to TX. Return a status code to allow calling * indicative of what if anything happened */ -static short mpc8xx_udc_handle_txerr() +static short mpc8xx_udc_handle_txerr () { short ep = 0, ret = 0; - - for(; epusber&(0x10<usber & (0x10 << ep)) { + /* Timeout or underrun */ - if(tx_cbd[ep]->cbd_sc&0x06){ + if (tx_cbd[ep]->cbd_sc & 0x06) { ret = 1; - mpc8xx_udc_flush_tx_fifo(ep); + mpc8xx_udc_flush_tx_fifo (ep); - }else{ - if(usbp->usep[ep]&STALL_BITMASK){ - if(!ep){ - usbp->usep[ep]&= - ~STALL_BITMASK; + } else { + if (usbp->usep[ep] & STALL_BITMASK) { + if (!ep) { + usbp->usep[ep] &= ~STALL_BITMASK; } - }/* else NAK */ + } /* else NAK */ } - usbp->usber|=(0x10<usber |= (0x10 << ep); } } return ret; @@ -750,13 +746,12 @@ static short mpc8xx_udc_handle_txerr() * * Advance cbd rx */ -static void mpc8xx_udc_advance_rx(volatile cbd_t ** rx_cbdp, int epid) +static void mpc8xx_udc_advance_rx (volatile cbd_t ** rx_cbdp, int epid) { - if((*rx_cbdp)->cbd_sc & RX_BD_W){ - *rx_cbdp = (volatile cbd_t*) - (endpoints[epid]->rbase + CFG_IMMR); - - }else{ + if ((*rx_cbdp)->cbd_sc & RX_BD_W) { + *rx_cbdp = (volatile cbd_t *) (endpoints[epid]->rbase + CFG_IMMR); + + } else { (*rx_cbdp)++; } } @@ -766,34 +761,34 @@ static void mpc8xx_udc_advance_rx(volatile cbd_t ** rx_cbdp, int epid) * * Flush a given TX fifo. Assumes one tx cbd per endpoint */ -static void mpc8xx_udc_flush_tx_fifo(int epid) -{ - volatile cbd_t * tx_cbdp = 0; +static void mpc8xx_udc_flush_tx_fifo (int epid) +{ + volatile cbd_t *tx_cbdp = 0; - if(epid > MAX_ENDPOINTS){ + if (epid > MAX_ENDPOINTS) { return; } /* TX stop */ - immr->im_cpm.cp_cpcr = ((epid<<2) | 0x1D01); + immr->im_cpm.cp_cpcr = ((epid << 2) | 0x1D01); __asm__ ("eieio"); - while(immr->im_cpm.cp_cpcr & 0x01); - + while (immr->im_cpm.cp_cpcr & 0x01); + usbp->uscom = 0x40 | 0; - + /* reset ring */ - tx_cbdp = (cbd_t*)(endpoints[epid]->tbptr + CFG_IMMR); + tx_cbdp = (cbd_t *) (endpoints[epid]->tbptr + CFG_IMMR); tx_cbdp->cbd_sc = (TX_BD_I | TX_BD_W); - + endpoints[epid]->tptr = endpoints[epid]->tbase; - endpoints[epid]->tstate = 0x00; - endpoints[epid]->tbcnt = 0x00; + endpoints[epid]->tstate = 0x00; + endpoints[epid]->tbcnt = 0x00; /* TX start */ - immr->im_cpm.cp_cpcr = ((epid<<2) | 0x2D01); + immr->im_cpm.cp_cpcr = ((epid << 2) | 0x2D01); __asm__ ("eieio"); - while(immr->im_cpm.cp_cpcr & 0x01); + while (immr->im_cpm.cp_cpcr & 0x01); return; } @@ -807,28 +802,29 @@ static void mpc8xx_udc_flush_tx_fifo(int epid) * we have accepted data but, the CPM found it had no-where to put that data * which needless to say would be a bad thing. */ -static void mpc8xx_udc_flush_rx_fifo() +static void mpc8xx_udc_flush_rx_fifo () { int i = 0; - for(i = 0;icbd_sc&RX_BD_E)){ - ERR("buf %p used rx data len = 0x%x sc=0x%x!\n", - rx_cbd[i], rx_cbd[i]->cbd_datlen, - rx_cbd[i]->cbd_sc); + + for (i = 0; i < RX_RING_SIZE; i++) { + if (!(rx_cbd[i]->cbd_sc & RX_BD_E)) { + ERR ("buf %p used rx data len = 0x%x sc=0x%x!\n", + rx_cbd[i], rx_cbd[i]->cbd_datlen, + rx_cbd[i]->cbd_sc); } } - ERR("BUG : Input over-run\n"); + ERR ("BUG : Input over-run\n"); } /* mpc8xx_udc_clear_rxbd - * + * * Release control of RX CBD to CP. */ -static void mpc8xx_udc_clear_rxbd(volatile cbd_t * rx_cbdp) +static void mpc8xx_udc_clear_rxbd (volatile cbd_t * rx_cbdp) { rx_cbdp->cbd_datlen = 0x0000; - rx_cbdp->cbd_sc= ((rx_cbdp->cbd_sc & RX_BD_W)|(RX_BD_E | RX_BD_I)); + rx_cbdp->cbd_sc = ((rx_cbdp->cbd_sc & RX_BD_W) | (RX_BD_E | RX_BD_I)); __asm__ ("eieio"); } @@ -837,27 +833,27 @@ static void mpc8xx_udc_clear_rxbd(volatile cbd_t * rx_cbdp) * Parse for tx timeout, control RX or USB reset/busy conditions * Return -1 on timeout, -2 on fatal error, else return zero */ -static int mpc8xx_udc_tx_irq(int ep) +static int mpc8xx_udc_tx_irq (int ep) { int i = 0; - if(usbp->usber&(USB_TX_ERRMASK)){ - if(mpc8xx_udc_handle_txerr()){ + if (usbp->usber & (USB_TX_ERRMASK)) { + if (mpc8xx_udc_handle_txerr ()) { /* Timeout, controlling function must retry send */ return -1; } } - if(usbp->usber & (USB_E_RESET|USB_E_BSY)){ + if (usbp->usber & (USB_E_RESET | USB_E_BSY)) { /* Fatal, abandon TX transaction */ return -2; } - - if(usbp->usber & USB_E_RXB){ - for(i = 0;icbd_sc&RX_BD_E)){ - if((rx_cbd[i] == ep_ref[0].prx) || ep){ - return -2; + + if (usbp->usber & USB_E_RXB) { + for (i = 0; i < RX_RING_SIZE; i++) { + if (!(rx_cbd[i]->cbd_sc & RX_BD_E)) { + if ((rx_cbd[i] == ep_ref[0].prx) || ep) { + return -2; } } } @@ -875,106 +871,106 @@ static int mpc8xx_udc_tx_irq(int ep) * devices to give up TX, however tx_retry stops us getting stuck in an endless * TX loop. */ -static int mpc8xx_udc_ep_tx (struct usb_endpoint_instance *epi) +static int mpc8xx_udc_ep_tx (struct usb_endpoint_instance *epi) { struct urb *urb = epi->tx_urb; - volatile cbd_t * tx_cbdp = 0; + volatile cbd_t *tx_cbdp = 0; unsigned int ep = 0, pkt_len = 0, x = 0, tx_retry = 0; int ret = 0; - - if(!epi || (epi->endpoint_address&0x03)>=MAX_ENDPOINTS || !urb){ + + if (!epi || (epi->endpoint_address & 0x03) >= MAX_ENDPOINTS || !urb) { return -1; } ep = epi->endpoint_address & 0x03; - tx_cbdp = (cbd_t*)(endpoints[ep]->tbptr + CFG_IMMR); - - if(tx_cbdp->cbd_sc&TX_BD_R || usbp->usber&USB_E_TXB){ - mpc8xx_udc_flush_tx_fifo(ep); + tx_cbdp = (cbd_t *) (endpoints[ep]->tbptr + CFG_IMMR); + + if (tx_cbdp->cbd_sc & TX_BD_R || usbp->usber & USB_E_TXB) { + mpc8xx_udc_flush_tx_fifo (ep); usbp->usber |= USB_E_TXB; }; - while(tx_retry++ < 100){ - ret = mpc8xx_udc_tx_irq(ep); - if(ret == -1){ + while (tx_retry++ < 100) { + ret = mpc8xx_udc_tx_irq (ep); + if (ret == -1) { /* ignore timeout here */ - }else if(ret == -2){ + } else if (ret == -2) { /* Abandon TX */ - mpc8xx_udc_flush_tx_fifo(ep); + mpc8xx_udc_flush_tx_fifo (ep); return -1; - } + } + + tx_cbdp = (cbd_t *) (endpoints[ep]->tbptr + CFG_IMMR); + while (tx_cbdp->cbd_sc & TX_BD_R) { + }; + tx_cbdp->cbd_sc = (tx_cbdp->cbd_sc & TX_BD_W); - tx_cbdp = (cbd_t*)(endpoints[ep]->tbptr + CFG_IMMR); - while(tx_cbdp->cbd_sc&TX_BD_R){}; - tx_cbdp->cbd_sc = (tx_cbdp->cbd_sc&TX_BD_W); - pkt_len = urb->actual_length - epi->sent; - if(pkt_len> epi->tx_packetSize || pkt_len > EP_MAX_PKT){ - pkt_len = MIN(epi->tx_packetSize, EP_MAX_PKT); + if (pkt_len > epi->tx_packetSize || pkt_len > EP_MAX_PKT) { + pkt_len = MIN (epi->tx_packetSize, EP_MAX_PKT); } - for(x=0; xcbd_bufaddr+x)) = + for (x = 0; x < pkt_len; x++) { + *((unsigned char *) (tx_cbdp->cbd_bufaddr + x)) = urb->buffer[epi->sent + x]; } tx_cbdp->cbd_datlen = pkt_len; - tx_cbdp->cbd_sc|=(CBD_TX_BITMASK | ep_ref[ep].pid); + tx_cbdp->cbd_sc |= (CBD_TX_BITMASK | ep_ref[ep].pid); __asm__ ("eieio"); - #ifdef __SIMULATE_ERROR__ - if(++err_poison_test == 2){ - err_poison_test = 0; - tx_cbdp->cbd_sc&=~TX_BD_TC; - } - #endif +#ifdef __SIMULATE_ERROR__ + if (++err_poison_test == 2) { + err_poison_test = 0; + tx_cbdp->cbd_sc &= ~TX_BD_TC; + } +#endif - usbp->uscom = (USCOM_STR | ep); + usbp->uscom = (USCOM_STR | ep); - while(!(usbp->usber&USB_E_TXB)){ - ret = mpc8xx_udc_tx_irq(ep); - if(ret == -1){ + while (!(usbp->usber & USB_E_TXB)) { + ret = mpc8xx_udc_tx_irq (ep); + if (ret == -1) { /* TX timeout */ break; - }else if(ret == -2){ - if(usbp->usber & USB_E_TXB){ - usbp->usber|=USB_E_TXB; + } else if (ret == -2) { + if (usbp->usber & USB_E_TXB) { + usbp->usber |= USB_E_TXB; } - mpc8xx_udc_flush_tx_fifo(ep); + mpc8xx_udc_flush_tx_fifo (ep); return -1; } }; - if(usbp->usber & USB_E_TXB){ - usbp->usber|=USB_E_TXB; + if (usbp->usber & USB_E_TXB) { + usbp->usber |= USB_E_TXB; } /* ACK must be present <= 18bit times from TX */ - if(ret == -1){ + if (ret == -1) { continue; } - + /* TX ACK : USB 2.0 8.7.2, Toggle PID, Advance TX */ epi->sent += pkt_len; - epi->last = MIN (urb->actual_length - epi->sent, - epi->tx_packetSize); - TOGGLE_TX_PID(ep_ref[ep].pid); + epi->last = MIN (urb->actual_length - epi->sent, epi->tx_packetSize); + TOGGLE_TX_PID (ep_ref[ep].pid); + + if (epi->sent >= epi->tx_urb->actual_length) { - if(epi->sent >= epi->tx_urb->actual_length){ - epi->tx_urb->actual_length = 0; epi->sent = 0; - - if(ep_ref[ep].sc & EP_SEND_ZLP){ + + if (ep_ref[ep].sc & EP_SEND_ZLP) { ep_ref[ep].sc &= ~EP_SEND_ZLP; - }else{ + } else { return 0; } } } - - ERR("TX fail, endpoint 0x%x tx bytes 0x%x/0x%x\n",ep, epi->sent, - epi->tx_urb->actual_length); + + ERR ("TX fail, endpoint 0x%x tx bytes 0x%x/0x%x\n", ep, epi->sent, + epi->tx_urb->actual_length); return -1; } @@ -983,126 +979,117 @@ static int mpc8xx_udc_ep_tx (struct usb_endpoint_instance *epi) * * Dump a control request to console */ -static void mpc8xx_udc_dump_request(struct usb_device_request *request) +static void mpc8xx_udc_dump_request (struct usb_device_request *request) { - DBG( - "bmRequestType:%02x bRequest:%02x wValue:%04x " - "wIndex:%04x wLength:%04x ?\n", - request->bmRequestType, - request->bRequest, - request->wValue, - request->wIndex, - request->wLength); + DBG ("bmRequestType:%02x bRequest:%02x wValue:%04x " + "wIndex:%04x wLength:%04x ?\n", + request->bmRequestType, + request->bRequest, + request->wValue, request->wIndex, request->wLength); return; } -/* mpc8xx_udc_ep0_rx_setup - * +/* mpc8xx_udc_ep0_rx_setup + * * Decode received ep0 SETUP packet. return non-zero on error */ static int mpc8xx_udc_ep0_rx_setup (volatile cbd_t * rx_cbdp) { unsigned int x = 0; - struct urb * purb = ep_ref[0].urb; - struct usb_endpoint_instance *epi = + struct urb *purb = ep_ref[0].urb; + struct usb_endpoint_instance *epi = &udc_device->bus->endpoint_array[0]; - for(; xcbd_datlen; x++){ - *(((unsigned char*)&ep_ref[0].urb->device_request)+x) = - *((unsigned char*)(rx_cbdp->cbd_bufaddr+x)); + for (; x < rx_cbdp->cbd_datlen; x++) { + *(((unsigned char *) &ep_ref[0].urb->device_request) + x) = + *((unsigned char *) (rx_cbdp->cbd_bufaddr + x)); } - - mpc8xx_udc_clear_rxbd(rx_cbdp); - if (ep0_recv_setup(purb)) { - mpc8xx_udc_dump_request(&purb->device_request); + mpc8xx_udc_clear_rxbd (rx_cbdp); + + if (ep0_recv_setup (purb)) { + mpc8xx_udc_dump_request (&purb->device_request); return -1; } - if ((purb->device_request.bmRequestType&USB_REQ_DIRECTION_MASK) + if ((purb->device_request.bmRequestType & USB_REQ_DIRECTION_MASK) == USB_REQ_HOST2DEVICE) { - switch (purb->device_request.bRequest){ - case USB_REQ_SET_ADDRESS: - /* Send the Status OUT ZLP */ - ep_ref[0].pid = TX_BD_PID_DATA1; - purb->actual_length = 0; - mpc8xx_udc_init_tx(epi,purb); - mpc8xx_udc_ep_tx(epi); - - /* Move to the addressed state */ - usbp->usaddr = udc_device->address; - mpc8xx_udc_state_transition_up(udc_device->device_state, - STATE_ADDRESSED); - return 0; - - case USB_REQ_SET_CONFIGURATION: - if(!purb->device_request.wValue){ - - /* Respond at default address */ - usbp->usaddr = 0x00; - mpc8xx_udc_state_transition_down(udc_device->device_state, - STATE_ADDRESSED); - - } else { - - /* TODO: Support multiple configurations */ - mpc8xx_udc_state_transition_up(udc_device->device_state,STATE_CONFIGURED); - for(x=1; xbus->endpoint_array[x].endpoint_address&USB_ENDPOINT_DIR_MASK) - == USB_DIR_IN){ - ep_ref[x].pid = TX_BD_PID_DATA0; - }else{ - ep_ref[x].pid = RX_BD_PID_DATA0; - } - /* Set configuration must unstall endpoints */ - usbp->usep[x]&=~STALL_BITMASK; + switch (purb->device_request.bRequest) { + case USB_REQ_SET_ADDRESS: + /* Send the Status OUT ZLP */ + ep_ref[0].pid = TX_BD_PID_DATA1; + purb->actual_length = 0; + mpc8xx_udc_init_tx (epi, purb); + mpc8xx_udc_ep_tx (epi); + + /* Move to the addressed state */ + usbp->usaddr = udc_device->address; + mpc8xx_udc_state_transition_up (udc_device->device_state, + STATE_ADDRESSED); + return 0; + + case USB_REQ_SET_CONFIGURATION: + if (!purb->device_request.wValue) { + /* Respond at default address */ + usbp->usaddr = 0x00; + mpc8xx_udc_state_transition_down (udc_device->device_state, + STATE_ADDRESSED); + } else { + /* TODO: Support multiple configurations */ + mpc8xx_udc_state_transition_up (udc_device->device_state, + STATE_CONFIGURED); + for (x = 1; x < MAX_ENDPOINTS; x++) { + if ((udc_device->bus->endpoint_array[x].endpoint_address & USB_ENDPOINT_DIR_MASK) + == USB_DIR_IN) { + ep_ref[x].pid = TX_BD_PID_DATA0; + } else { + ep_ref[x].pid = RX_BD_PID_DATA0; } - + /* Set configuration must unstall endpoints */ + usbp->usep[x] &= ~STALL_BITMASK; } - break; - default: - /* CDC/Vendor specific */ - break; + } + break; + default: + /* CDC/Vendor specific */ + break; } /* Send ZLP as ACK in Status OUT phase */ ep_ref[0].pid = TX_BD_PID_DATA1; purb->actual_length = 0; - mpc8xx_udc_init_tx(epi,purb); - mpc8xx_udc_ep_tx(epi); + mpc8xx_udc_init_tx (epi, purb); + mpc8xx_udc_ep_tx (epi); - }else{ - if(purb->actual_length){ + } else { + + if (purb->actual_length) { ep_ref[0].pid = TX_BD_PID_DATA1; - mpc8xx_udc_init_tx(epi,purb); + mpc8xx_udc_init_tx (epi, purb); - if(!(purb->actual_length%EP0_MAX_PACKET_SIZE)){ + if (!(purb->actual_length % EP0_MAX_PACKET_SIZE)) { ep_ref[0].sc |= EP_SEND_ZLP; } - if(purb->device_request.wValue== - USB_DESCRIPTOR_TYPE_DEVICE){ - if(le16_to_cpu(purb->device_request.wLength)> - purb->actual_length){ + if (purb->device_request.wValue == + USB_DESCRIPTOR_TYPE_DEVICE) { + if (le16_to_cpu (purb->device_request.wLength) + > purb->actual_length) { /* Send EP0_MAX_PACKET_SIZE bytes * unless correct size requested. */ - if(purb->actual_length > - epi->tx_packetSize){ - - purb->actual_length = - epi->tx_packetSize; + if (purb->actual_length > epi->tx_packetSize) { + purb->actual_length = epi->tx_packetSize; } - } } - mpc8xx_udc_ep_tx(epi); + mpc8xx_udc_ep_tx (epi); - }else{ + } else { /* Corrupt SETUP packet? */ - ERR("Zero length data or SETUP with DATA-IN phase ?\n"); + ERR ("Zero length data or SETUP with DATA-IN phase ?\n"); return 1; } } @@ -1113,8 +1100,8 @@ static int mpc8xx_udc_ep0_rx_setup (volatile cbd_t * rx_cbdp) * * Setup some basic parameters for a TX transaction */ -static void mpc8xx_udc_init_tx(struct usb_endpoint_instance *epi, - struct urb * tx_urb) +static void mpc8xx_udc_init_tx (struct usb_endpoint_instance *epi, + struct urb *tx_urb) { epi->sent = 0; epi->last = 0; @@ -1125,24 +1112,24 @@ static void mpc8xx_udc_init_tx(struct usb_endpoint_instance *epi, * * Receive ep0/control USB data. Parse and possibly send a response. */ -static void mpc8xx_udc_ep0_rx(volatile cbd_t * rx_cbdp) +static void mpc8xx_udc_ep0_rx (volatile cbd_t * rx_cbdp) { - if(rx_cbdp->cbd_sc&RX_BD_PID_SETUP){ - + if (rx_cbdp->cbd_sc & RX_BD_PID_SETUP) { + /* Unconditionally accept SETUP packets */ - if(mpc8xx_udc_ep0_rx_setup(rx_cbdp)){ - mpc8xx_udc_stall (0); + if (mpc8xx_udc_ep0_rx_setup (rx_cbdp)) { + mpc8xx_udc_stall (0); } - + } else { - - mpc8xx_udc_clear_rxbd(rx_cbdp); - - if((rx_cbdp->cbd_datlen-2)){ + + mpc8xx_udc_clear_rxbd (rx_cbdp); + + if ((rx_cbdp->cbd_datlen - 2)) { /* SETUP with a DATA phase - * outside of SETUP packet. - * Reply with STALL. - */ + * outside of SETUP packet. + * Reply with STALL. + */ mpc8xx_udc_stall (0); } } @@ -1151,7 +1138,7 @@ static void mpc8xx_udc_ep0_rx(volatile cbd_t * rx_cbdp) /* mpc8xx_udc_epn_rx * * Receive some data from cbd into USB system urb data abstraction - * Upper layers should NAK if there is insufficient RX data space + * Upper layers should NAK if there is insufficient RX data space */ static int mpc8xx_udc_epn_rx (unsigned int epid, volatile cbd_t * rx_cbdp) { @@ -1159,115 +1146,115 @@ static int mpc8xx_udc_epn_rx (unsigned int epid, volatile cbd_t * rx_cbdp) struct urb *urb = 0; unsigned int x = 0; - if(epid >= MAX_ENDPOINTS || !rx_cbdp->cbd_datlen){ + if (epid >= MAX_ENDPOINTS || !rx_cbdp->cbd_datlen) { return 0; } - - /* USB 2.0 PDF section 8.6.4 + + /* USB 2.0 PDF section 8.6.4 * Discard data with invalid PID it is a resend. */ - if(ep_ref[epid].pid!=(rx_cbdp->cbd_sc&0xC0)){ + if (ep_ref[epid].pid != (rx_cbdp->cbd_sc & 0xC0)) { return 1; } - TOGGLE_RX_PID(ep_ref[epid].pid); - + TOGGLE_RX_PID (ep_ref[epid].pid); + epi = &udc_device->bus->endpoint_array[epid]; urb = epi->rcv_urb; - for(; x<(rx_cbdp->cbd_datlen-2); x++){ - *((unsigned char*)(urb->buffer + urb->actual_length +x)) = - *((unsigned char*)(rx_cbdp->cbd_bufaddr+x)); + for (; x < (rx_cbdp->cbd_datlen - 2); x++) { + *((unsigned char *) (urb->buffer + urb->actual_length + x)) = + *((unsigned char *) (rx_cbdp->cbd_bufaddr + x)); } - if(x){ + if (x) { usbd_rcv_complete (epi, x, 0); - if(ep_ref[epid].urb->status == RECV_ERROR){ - DBG("RX error unset NAK\n"); - udc_unset_nak(epid); + if (ep_ref[epid].urb->status == RECV_ERROR) { + DBG ("RX error unset NAK\n"); + udc_unset_nak (epid); } - } + } return x; } /* mpc8xx_udc_clock_init * - * Obtain a clock reference for Full Speed Signaling + * Obtain a clock reference for Full Speed Signaling */ -static void mpc8xx_udc_clock_init (volatile immap_t * immr, - volatile cpm8xx_t * cp) +static void mpc8xx_udc_clock_init (volatile immap_t * immr, + volatile cpm8xx_t * cp) { #if defined(CFG_USB_EXTC_CLK) /* This has been tested with a 48MHz crystal on CLK6 */ - switch(CFG_USB_EXTC_CLK){ - case 1: - immr->im_ioport.iop_papar|= 0x0100; - immr->im_ioport.iop_padir&= ~0x0100; - cp->cp_sicr|= 0x24; - break; - case 2: - immr->im_ioport.iop_papar|= 0x0200; - immr->im_ioport.iop_padir&= ~0x0200; - cp->cp_sicr|= 0x2D; - break; - case 3: - immr->im_ioport.iop_papar|= 0x0400; - immr->im_ioport.iop_padir&= ~0x0400; - cp->cp_sicr|= 0x36; - break; - case 4: - immr->im_ioport.iop_papar|= 0x0800; - immr->im_ioport.iop_padir&= ~0x0800; - cp->cp_sicr|= 0x3F; - break; - default: - udc_state = STATE_ERROR; - break; + switch (CFG_USB_EXTC_CLK) { + case 1: + immr->im_ioport.iop_papar |= 0x0100; + immr->im_ioport.iop_padir &= ~0x0100; + cp->cp_sicr |= 0x24; + break; + case 2: + immr->im_ioport.iop_papar |= 0x0200; + immr->im_ioport.iop_padir &= ~0x0200; + cp->cp_sicr |= 0x2D; + break; + case 3: + immr->im_ioport.iop_papar |= 0x0400; + immr->im_ioport.iop_padir &= ~0x0400; + cp->cp_sicr |= 0x36; + break; + case 4: + immr->im_ioport.iop_papar |= 0x0800; + immr->im_ioport.iop_padir &= ~0x0800; + cp->cp_sicr |= 0x3F; + break; + default: + udc_state = STATE_ERROR; + break; } #elif defined(CFG_USB_BRGCLK) - /* This has been tested with brgclk == 50MHz */ + /* This has been tested with brgclk == 50MHz */ DECLARE_GLOBAL_DATA_PTR; int divisor = 0; - if(gd->cpu_clk<48000000L){ - ERR("brgclk is too slow for full-speed USB!\n"); + if (gd->cpu_clk < 48000000L) { + ERR ("brgclk is too slow for full-speed USB!\n"); udc_state = STATE_ERROR; return; } /* Assume the brgclk is 'good enough', we want !(gd->cpu_clk%48Mhz) * but, can /probably/ live with close-ish alternative rates. - */ - divisor = (gd->cpu_clk/48000000L)-1; + */ + divisor = (gd->cpu_clk / 48000000L) - 1; cp->cp_sicr &= ~0x0000003F; - - switch(CFG_USB_BRGCLK){ - case 1: - cp->cp_brgc1 |= (divisor|CPM_BRG_EN); - cp->cp_sicr &= ~0x2F; - break; - case 2: - cp->cp_brgc2 |= (divisor|CPM_BRG_EN); - cp->cp_sicr |= 0x00000009; - break; - case 3: - cp->cp_brgc3 |= (divisor|CPM_BRG_EN); - cp->cp_sicr |= 0x00000012; - break; - case 4: - cp->cp_brgc4 = (divisor|CPM_BRG_EN); - cp->cp_sicr |= 0x0000001B; - break; - default: - udc_state = STATE_ERROR; - break; + + switch (CFG_USB_BRGCLK) { + case 1: + cp->cp_brgc1 |= (divisor | CPM_BRG_EN); + cp->cp_sicr &= ~0x2F; + break; + case 2: + cp->cp_brgc2 |= (divisor | CPM_BRG_EN); + cp->cp_sicr |= 0x00000009; + break; + case 3: + cp->cp_brgc3 |= (divisor | CPM_BRG_EN); + cp->cp_sicr |= 0x00000012; + break; + case 4: + cp->cp_brgc4 = (divisor | CPM_BRG_EN); + cp->cp_sicr |= 0x0000001B; + break; + default: + udc_state = STATE_ERROR; + break; } #else - #error "CFG_USB_EXTC_CLK or CFG_USB_BRGCLK must be defined" +#error "CFG_USB_EXTC_CLK or CFG_USB_BRGCLK must be defined" #endif } @@ -1278,51 +1265,51 @@ static void mpc8xx_udc_clock_init (volatile immap_t * immr, */ static void mpc8xx_udc_cbd_attach (int ep, uchar tx_size, uchar rx_size) { - - if (!tx_cbd[ep] || !rx_cbd[ep] || ep >= MAX_ENDPOINTS){ + + if (!tx_cbd[ep] || !rx_cbd[ep] || ep >= MAX_ENDPOINTS) { udc_state = STATE_ERROR; return; } - if (tx_size>USB_MAX_PKT || rx_size>USB_MAX_PKT || - (!tx_size && !rx_size)){ + if (tx_size > USB_MAX_PKT || rx_size > USB_MAX_PKT || + (!tx_size && !rx_size)) { udc_state = STATE_ERROR; return; } /* Attach CBD to appropiate Parameter RAM Endpoint data structure */ - if(rx_size){ - endpoints[ep]->rbase = (u32)rx_cbd[rx_ct]; - endpoints[ep]->rbptr = (u32)rx_cbd[rx_ct]; + if (rx_size) { + endpoints[ep]->rbase = (u32) rx_cbd[rx_ct]; + endpoints[ep]->rbptr = (u32) rx_cbd[rx_ct]; rx_ct++; - if(!ep){ - - endpoints[ep]->rbptr = (u32)rx_cbd[rx_ct]; + if (!ep) { + + endpoints[ep]->rbptr = (u32) rx_cbd[rx_ct]; rx_cbd[rx_ct]->cbd_sc |= RX_BD_W; rx_ct++; - }else{ + } else { rx_ct += 2; - endpoints[ep]->rbptr = (u32)rx_cbd[rx_ct]; + endpoints[ep]->rbptr = (u32) rx_cbd[rx_ct]; rx_cbd[rx_ct]->cbd_sc |= RX_BD_W; rx_ct++; } /* Where we expect to RX data on this endpoint */ - ep_ref[ep].prx = rx_cbd[rx_ct-1]; - }else{ + ep_ref[ep].prx = rx_cbd[rx_ct - 1]; + } else { ep_ref[ep].prx = 0; endpoints[ep]->rbase = 0; endpoints[ep]->rbptr = 0; } - if(tx_size){ - endpoints[ep]->tbase = (u32)tx_cbd[tx_ct]; - endpoints[ep]->tbptr = (u32)tx_cbd[tx_ct]; + if (tx_size) { + endpoints[ep]->tbase = (u32) tx_cbd[tx_ct]; + endpoints[ep]->tbptr = (u32) tx_cbd[tx_ct]; tx_ct++; - }else{ + } else { endpoints[ep]->tbase = 0; endpoints[ep]->tbptr = 0; } @@ -1330,13 +1317,14 @@ static void mpc8xx_udc_cbd_attach (int ep, uchar tx_size, uchar rx_size) endpoints[ep]->tstate = 0; endpoints[ep]->tbcnt = 0; endpoints[ep]->mrblr = EP_MAX_PKT; - endpoints[ep]->rfcr = 0x18; + endpoints[ep]->rfcr = 0x18; endpoints[ep]->tfcr = 0x18; ep_ref[ep].sc |= EP_ATTACHED; - DBG("ep %d rbase 0x%08x rbptr 0x%08x tbase 0x%08x tbptr 0x%08x prx = %p\n", - ep, endpoints[ep]->rbase, endpoints[ep]->rbptr, endpoints[ep]->tbase, - endpoints[ep]->tbptr, ep_ref[ep].prx); + DBG ("ep %d rbase 0x%08x rbptr 0x%08x tbase 0x%08x tbptr 0x%08x prx = %p\n", + ep, endpoints[ep]->rbase, endpoints[ep]->rbptr, + endpoints[ep]->tbase, endpoints[ep]->tbptr, + ep_ref[ep].prx); return; } @@ -1349,28 +1337,28 @@ static void mpc8xx_udc_cbd_init (void) { int i = 0; - for(; icbd_bufaddr = + mpc8xx_udc_alloc (EP_MAX_PKT, sizeof (int)); - for(i=0; i< TX_RING_SIZE; i++){ - tx_cbd[i]->cbd_bufaddr = - mpc8xx_udc_alloc(EP_MAX_PKT, sizeof(int)); - - tx_cbd[i]->cbd_sc = (TX_BD_I | TX_BD_W); + tx_cbd[i]->cbd_sc = (TX_BD_I | TX_BD_W); tx_cbd[i]->cbd_datlen = 0x0000; } - for(i=0; i< RX_RING_SIZE; i++){ - rx_cbd[i]->cbd_bufaddr = - mpc8xx_udc_alloc(EP_MAX_PKT, sizeof(int)); + for (i = 0; i < RX_RING_SIZE; i++) { + rx_cbd[i]->cbd_bufaddr = + mpc8xx_udc_alloc (EP_MAX_PKT, sizeof (int)); rx_cbd[i]->cbd_sc = (RX_BD_I | RX_BD_E); rx_cbd[i]->cbd_datlen = 0x0000; @@ -1387,25 +1375,25 @@ static void mpc8xx_udc_endpoint_init (void) { int i = 0; - for(; i Entering device setup"); - + do { const int setup_pktsize = 8; unsigned char *datap = @@ -1518,7 +1518,7 @@ void udc_startup_events (struct usb_device_instance *device) } /** - * udc_irq - do pseudo interrupts + * udc_irq - do pseudo interrupts */ void udc_irq(void) { diff --git a/drivers/usbtty.c b/drivers/usbtty.c index ed96999e8..d41a00b8c 100644 --- a/drivers/usbtty.c +++ b/drivers/usbtty.c @@ -1,7 +1,7 @@ /* * (C) Copyright 2003 * Gerry Hamel, geh@ti.com, Texas Instruments - * + * * (C) Copyright 2006 * Bryan O'Donoghue, bodonoghue@codehermit.ie * @@ -31,7 +31,7 @@ #include "usb_cdc_acm.h" #include "usbdescriptors.h" #include /* If defined, override Linux identifiers with - * vendor specific ones */ + * vendor specific ones */ #if 0 #define TTYDBG(fmt,args...)\ @@ -142,10 +142,10 @@ static struct usb_device_descriptor device_descriptor = { struct acm_config_desc { struct usb_configuration_descriptor configuration_desc; - + /* Master Interface */ struct usb_interface_descriptor interface_desc; - + struct usb_class_header_function_descriptor usb_class_header; struct usb_class_call_management_descriptor usb_class_call_mgt; struct usb_class_abstract_control_descriptor usb_class_acm; @@ -154,22 +154,22 @@ struct acm_config_desc { /* Slave Interface */ struct usb_interface_descriptor data_class_interface; - struct usb_endpoint_descriptor + struct usb_endpoint_descriptor data_endpoints[NUM_ENDPOINTS-1] __attribute__((packed)); } __attribute__((packed)); static struct acm_config_desc acm_configuration_descriptors[NUM_CONFIGS] = { { .configuration_desc ={ - .bLength = + .bLength = sizeof(struct usb_configuration_descriptor), .bDescriptorType = USB_DT_CONFIG, - .wTotalLength = + .wTotalLength = cpu_to_le16(sizeof(struct acm_config_desc)), .bNumInterfaces = NUM_ACM_INTERFACES, .bConfigurationValue = 1, .iConfiguration = STR_CONFIG, - .bmAttributes = + .bmAttributes = BMATTRIBUTE_SELF_POWERED|BMATTRIBUTE_RESERVED, .bMaxPower = USBTTY_MAXPOWER }, @@ -180,62 +180,62 @@ static struct acm_config_desc acm_configuration_descriptors[NUM_CONFIGS] = { .bInterfaceNumber = 0, .bAlternateSetting = 0, .bNumEndpoints = 0x01, - .bInterfaceClass = + .bInterfaceClass = COMMUNICATIONS_INTERFACE_CLASS_CONTROL, .bInterfaceSubClass = COMMUNICATIONS_ACM_SUBCLASS, .bInterfaceProtocol = COMMUNICATIONS_V25TER_PROTOCOL, .iInterface = STR_CTRL_INTERFACE, }, .usb_class_header = { - .bFunctionLength = + .bFunctionLength = sizeof(struct usb_class_header_function_descriptor), - .bDescriptorType = CS_INTERFACE, + .bDescriptorType = CS_INTERFACE, .bDescriptorSubtype = USB_ST_HEADER, .bcdCDC = cpu_to_le16(110), }, .usb_class_call_mgt = { - .bFunctionLength = + .bFunctionLength = sizeof(struct usb_class_call_management_descriptor), .bDescriptorType = CS_INTERFACE, .bDescriptorSubtype = USB_ST_CMF, - .bmCapabilities = 0x00, - .bDataInterface = 0x01, + .bmCapabilities = 0x00, + .bDataInterface = 0x01, }, .usb_class_acm = { - .bFunctionLength = + .bFunctionLength = sizeof(struct usb_class_abstract_control_descriptor), .bDescriptorType = CS_INTERFACE, - .bDescriptorSubtype = USB_ST_ACMF, - .bmCapabilities = 0x00, + .bDescriptorSubtype = USB_ST_ACMF, + .bmCapabilities = 0x00, }, .usb_class_union = { - .bFunctionLength = + .bFunctionLength = sizeof(struct usb_class_union_function_descriptor), .bDescriptorType = CS_INTERFACE, .bDescriptorSubtype = USB_ST_UF, - .bMasterInterface = 0x00, - .bSlaveInterface0 = 0x01, + .bMasterInterface = 0x00, + .bSlaveInterface0 = 0x01, }, .notification_endpoint = { - .bLength = + .bLength = sizeof(struct usb_endpoint_descriptor), .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = 0x01 | USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize + .wMaxPacketSize = cpu_to_le16(CONFIG_USBD_SERIAL_INT_PKTSIZE), .bInterval = 0xFF, }, /* Interface 2 */ .data_class_interface = { - .bLength = + .bLength = sizeof(struct usb_interface_descriptor), .bDescriptorType = USB_DT_INTERFACE, .bInterfaceNumber = 0x01, .bAlternateSetting = 0x00, .bNumEndpoints = 0x02, - .bInterfaceClass = + .bInterfaceClass = COMMUNICATIONS_INTERFACE_CLASS_DATA, .bInterfaceSubClass = DATA_INTERFACE_SUBCLASS_NONE, .bInterfaceProtocol = DATA_INTERFACE_PROTOCOL_NONE, @@ -243,30 +243,30 @@ static struct acm_config_desc acm_configuration_descriptors[NUM_CONFIGS] = { }, .data_endpoints = { { - .bLength = + .bLength = sizeof(struct usb_endpoint_descriptor), .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = 0x02 | USB_DIR_OUT, - .bmAttributes = + .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = + .wMaxPacketSize = cpu_to_le16(CONFIG_USBD_SERIAL_BULK_PKTSIZE), .bInterval = 0xFF, }, { - .bLength = + .bLength = sizeof(struct usb_endpoint_descriptor), .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = 0x03 | USB_DIR_IN, - .bmAttributes = + .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = + .wMaxPacketSize = cpu_to_le16(CONFIG_USBD_SERIAL_BULK_PKTSIZE), .bInterval = 0xFF, }, }, }, -}; +}; static struct rs232_emu rs232_desc={ .dter = 115200, @@ -282,75 +282,75 @@ static struct rs232_emu rs232_desc={ struct gserial_config_desc { - + struct usb_configuration_descriptor configuration_desc; - struct usb_interface_descriptor + struct usb_interface_descriptor interface_desc[NUM_GSERIAL_INTERFACES] __attribute__((packed)); - struct usb_endpoint_descriptor + struct usb_endpoint_descriptor data_endpoints[NUM_ENDPOINTS] __attribute__((packed)); } __attribute__((packed)); -static struct gserial_config_desc +static struct gserial_config_desc gserial_configuration_descriptors[NUM_CONFIGS] ={ { .configuration_desc ={ .bLength = sizeof(struct usb_configuration_descriptor), .bDescriptorType = USB_DT_CONFIG, - .wTotalLength = + .wTotalLength = cpu_to_le16(sizeof(struct gserial_config_desc)), .bNumInterfaces = NUM_GSERIAL_INTERFACES, .bConfigurationValue = 1, .iConfiguration = STR_CONFIG, - .bmAttributes = + .bmAttributes = BMATTRIBUTE_SELF_POWERED|BMATTRIBUTE_RESERVED, .bMaxPower = USBTTY_MAXPOWER }, .interface_desc = { { - .bLength = + .bLength = sizeof(struct usb_interface_descriptor), .bDescriptorType = USB_DT_INTERFACE, .bInterfaceNumber = 0, .bAlternateSetting = 0, .bNumEndpoints = NUM_ENDPOINTS, - .bInterfaceClass = + .bInterfaceClass = COMMUNICATIONS_INTERFACE_CLASS_VENDOR, - .bInterfaceSubClass = + .bInterfaceSubClass = COMMUNICATIONS_NO_SUBCLASS, - .bInterfaceProtocol = + .bInterfaceProtocol = COMMUNICATIONS_NO_PROTOCOL, .iInterface = STR_DATA_INTERFACE }, }, .data_endpoints = { { - .bLength = + .bLength = sizeof(struct usb_endpoint_descriptor), .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = 0x01 | USB_DIR_OUT, .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = + .wMaxPacketSize = cpu_to_le16(CONFIG_USBD_SERIAL_OUT_PKTSIZE), .bInterval= 0xFF, }, { - .bLength = + .bLength = sizeof(struct usb_endpoint_descriptor), .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = 0x02 | USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = + .wMaxPacketSize = cpu_to_le16(CONFIG_USBD_SERIAL_IN_PKTSIZE), .bInterval = 0xFF, }, { - .bLength = + .bLength = sizeof(struct usb_endpoint_descriptor), .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = 0x03 | USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = + .wMaxPacketSize = cpu_to_le16(CONFIG_USBD_SERIAL_INT_PKTSIZE), .bInterval = 0xFF, }, @@ -368,7 +368,7 @@ static void usbtty_init_endpoints (void); static void usbtty_init_terminal_type(short type); static void usbtty_event_handler (struct usb_device_instance *device, usb_device_event_t event, int data); -static int usbtty_cdc_setup(struct usb_device_request *request, +static int usbtty_cdc_setup(struct usb_device_request *request, struct urb *urb); static int usbtty_configured (void); static int write_buffer (circbuf_t * buf); @@ -477,12 +477,12 @@ static void __usbtty_puts (const char *str, int len) /* Empty buffer here, if needed, to ensure space... */ if (space) { write_buffer (&usbtty_output); - + n = MIN (space, MIN (len, maxlen)); buf_push (&usbtty_output, str, n); str += n; - len -= n; + len -= n; } } } @@ -543,7 +543,7 @@ int drv_usbtty_init (void) tt = "generic"; } usbtty_init_terminal_type(strcmp(tt,"cdc_acm")); - + /* prepare buffers... */ buf_init (&usbtty_input, USBTTY_BUFFER_SIZE); buf_init (&usbtty_output, USBTTY_BUFFER_SIZE); @@ -579,7 +579,7 @@ static void usbtty_init_strings (void) { struct usb_string_descriptor *string; - usbtty_string_table[STR_LANG] = + usbtty_string_table[STR_LANG] = (struct usb_string_descriptor*)wstrLang; string = (struct usb_string_descriptor *) wstrManufacturer; @@ -624,7 +624,7 @@ static void usbtty_init_strings (void) /* Now, initialize the string table for ep0 handling */ usb_strings = usbtty_string_table; -} +} static void usbtty_init_instances (void) { @@ -690,7 +690,7 @@ static void usbtty_init_instances (void) endpoint_instance[i].rcv_packetSize = le16_to_cpu(ep_descriptor_ptrs[i - 1]->wMaxPacketSize); - + endpoint_instance[i].tx_attributes = ep_descriptor_ptrs[i - 1]->bmAttributes; @@ -721,30 +721,30 @@ static void usbtty_init_endpoints (void) int i; bus_instance->max_endpoints = NUM_ENDPOINTS + 1; - for (i = 1; i <= NUM_ENDPOINTS; i++) { + for (i = 1; i <= NUM_ENDPOINTS; i++) { udc_setup_ep (device_instance, i, &endpoint_instance[i]); } } /* usbtty_init_terminal_type - * + * * Do some late binding for our device type. */ static void usbtty_init_terminal_type(short type) { switch(type){ - /* CDC ACM */ + /* CDC ACM */ case 0: /* Assign endpoint descriptors */ - ep_descriptor_ptrs[0] = + ep_descriptor_ptrs[0] = &acm_configuration_descriptors[0].notification_endpoint; - ep_descriptor_ptrs[1] = + ep_descriptor_ptrs[1] = &acm_configuration_descriptors[0].data_endpoints[0]; - ep_descriptor_ptrs[2] = + ep_descriptor_ptrs[2] = &acm_configuration_descriptors[0].data_endpoints[1]; /* Enumerate Device Descriptor */ - device_descriptor.bDeviceClass = + device_descriptor.bDeviceClass = COMMUNICATIONS_DEVICE_CLASS; device_descriptor.idProduct = cpu_to_le16(CONFIG_USBD_PRODUCTID_CDCACM); @@ -752,7 +752,7 @@ static void usbtty_init_terminal_type(short type) /* Assign endpoint indices */ tx_endpoint = ACM_TX_ENDPOINT; rx_endpoint = ACM_RX_ENDPOINT; - + /* Configuration Descriptor */ configuration_descriptor = (struct usb_configuration_descriptor*) @@ -766,11 +766,11 @@ static void usbtty_init_terminal_type(short type) case 1: default: /* Assign endpoint descriptors */ - ep_descriptor_ptrs[0] = + ep_descriptor_ptrs[0] = &gserial_configuration_descriptors[0].data_endpoints[0]; - ep_descriptor_ptrs[1] = + ep_descriptor_ptrs[1] = &gserial_configuration_descriptors[0].data_endpoints[1]; - ep_descriptor_ptrs[2] = + ep_descriptor_ptrs[2] = &gserial_configuration_descriptors[0].data_endpoints[2]; /* Enumerate Device Descriptor */ @@ -783,7 +783,7 @@ static void usbtty_init_terminal_type(short type) rx_endpoint = GSERIAL_RX_ENDPOINT; /* Configuration Descriptor */ - configuration_descriptor = + configuration_descriptor = (struct usb_configuration_descriptor*) &gserial_configuration_descriptors; @@ -832,14 +832,14 @@ static int write_buffer (circbuf_t * buf) if (!usbtty_configured ()) { return 0; } - + struct usb_endpoint_instance *endpoint = &endpoint_instance[tx_endpoint]; struct urb *current_urb = NULL; current_urb = next_urb (device_instance, endpoint); - /* TX data still exists - send it now - */ + /* TX data still exists - send it now + */ if(endpoint->sent < current_urb->actual_length){ if(udc_endpoint_write (endpoint)){ /* Write pre-empted by RX */ @@ -854,11 +854,11 @@ static int write_buffer (circbuf_t * buf) int popnum, popped; int total = 0; - /* Break buffer into urb sized pieces, - * and link each to the endpoint + /* Break buffer into urb sized pieces, + * and link each to the endpoint */ while (buf->size > 0) { - + if (!current_urb) { TTYERR ("current_urb is NULL, buf->size %d\n", buf->size); @@ -881,8 +881,8 @@ static int write_buffer (circbuf_t * buf) current_urb->actual_length += popped; total += popped; - /* If endpoint->last == 0, then transfers have - * not started on this endpoint + /* If endpoint->last == 0, then transfers have + * not started on this endpoint */ if (endpoint->last == 0) { if(udc_endpoint_write (endpoint)){ @@ -904,7 +904,7 @@ static int fill_buffer (circbuf_t * buf) &endpoint_instance[rx_endpoint]; if (endpoint->rcv_urb && endpoint->rcv_urb->actual_length) { - unsigned int nb = 0; + unsigned int nb = 0; char *src = (char *) endpoint->rcv_urb->buffer; unsigned int rx_avail = buf->totalsize - buf->size; @@ -913,7 +913,7 @@ static int fill_buffer (circbuf_t * buf) nb = endpoint->rcv_urb->actual_length; buf_push (buf, src, nb); endpoint->rcv_urb->actual_length = 0; - + } return nb; } @@ -958,7 +958,7 @@ int usbtty_cdc_setup(struct usb_device_request *request, struct urb *urb) case ACM_SEND_ENCAPSULATED_COMMAND : /* Required */ break; case ACM_SET_LINE_ENCODING : /* DTE stop/parity bits - * per character */ + * per character */ break; case ACM_GET_ENCAPSULATED_RESPONSE : /* request response */ break; @@ -986,8 +986,8 @@ void usbtty_poll (void) /* New interrupts? */ udc_irq(); - /* Write any output data to host buffer - * (do this before checking interrupts to avoid missing one) + /* Write any output data to host buffer + * (do this before checking interrupts to avoid missing one) */ if (usbtty_configured ()) { write_buffer (&usbtty_output); @@ -995,9 +995,9 @@ void usbtty_poll (void) /* New interrupts? */ udc_irq(); - - /* Check for new data from host.. - * (do this after checking interrupts to get latest data) + + /* Check for new data from host.. + * (do this after checking interrupts to get latest data) */ if (usbtty_configured ()) { fill_buffer (&usbtty_input); diff --git a/drivers/usbtty.h b/drivers/usbtty.h index 731b76330..8154e3072 100644 --- a/drivers/usbtty.h +++ b/drivers/usbtty.h @@ -33,7 +33,7 @@ #include -/* If no VendorID/ProductID is defined in config.h, pretend to be Linux +/* If no VendorID/ProductID is defined in config.h, pretend to be Linux * DO NOT Reuse this Vendor/Product setup with protocol incompatible devices */ #define CONFIG_USBD_VENDORID 0x0525 /* Linux/NetChip */ @@ -45,7 +45,7 @@ #define CONFIG_USBD_CONFIGURATION_STR "TTY via USB" -#define CONFIG_USBD_SERIAL_OUT_ENDPOINT UDC_OUT_ENDPOINT +#define CONFIG_USBD_SERIAL_OUT_ENDPOINT UDC_OUT_ENDPOINT #define CONFIG_USBD_SERIAL_OUT_PKTSIZE UDC_OUT_PACKET_SIZE #define CONFIG_USBD_SERIAL_IN_ENDPOINT UDC_IN_ENDPOINT #define CONFIG_USBD_SERIAL_IN_PKTSIZE UDC_IN_PACKET_SIZE diff --git a/include/asm-arm/arch-pxa/pxa-regs.h b/include/asm-arm/arch-pxa/pxa-regs.h index c47306cff..e8cb29903 100644 --- a/include/asm-arm/arch-pxa/pxa-regs.h +++ b/include/asm-arm/arch-pxa/pxa-regs.h @@ -769,8 +769,6 @@ typedef void (*ExcpHndlr) (void) ; #define UP2OCR __REG(0x40600020) #define UP3OCR __REG(0x40600024) - - /* * USB Host Controller */ diff --git a/include/configs/AdderUSB.h b/include/configs/AdderUSB.h index 2112e56e0..a4f7f9a28 100644 --- a/include/configs/AdderUSB.h +++ b/include/configs/AdderUSB.h @@ -3,7 +3,7 @@ * Bryan O'Donoghue * * Provides support for USB console on the Analogue & Micro Adder87x - * + * * See file CREDITS for list of people who contributed to this * project. * @@ -37,13 +37,13 @@ /* If you have a USB-IF assigned VendorID then you may wish to define * your own vendor specific values either in BoardName.h or directly in - * usbd_vendor_info.h + * usbd_vendor_info.h */ -/* +/* #define CONFIG_USBD_MANUFACTURER "CodeHermit.ie" #define CONFIG_USBD_PRODUCT_NAME "Das U-Boot" -#define CONFIG_USBD_VENDORID 0xFFFF +#define CONFIG_USBD_VENDORID 0xFFFF #define CONFIG_USBD_PRODUCTID_GSERIAL 0xFFFF #define CONFIG_USBD_PRODUCTID_CDCACM 0xFFFE */ diff --git a/include/usb_cdc_acm.h b/include/usb_cdc_acm.h index 8cb16545d..87bf50c52 100644 --- a/include/usb_cdc_acm.h +++ b/include/usb_cdc_acm.h @@ -34,7 +34,7 @@ #define ACM_RESPONSE_AVAILABLE 0x01 #define ACM_SERIAL_STATE 0x20 -/* Format of response expected by a ACM_GET_LINE_ENCODING request */ +/* Format of response expected by a ACM_GET_LINE_ENCODING request */ struct rs232_emu{ unsigned long dter; unsigned char stop_bits; diff --git a/include/usbdcore_mpc8xx.h b/include/usbdcore_mpc8xx.h index e54acd9a1..9df62f4ca 100644 --- a/include/usbdcore_mpc8xx.h +++ b/include/usbdcore_mpc8xx.h @@ -99,7 +99,7 @@ #define TX_BD_TO 0x04 #define TX_BD_NO_PID 0x00 #define TX_BD_PID_DATA0 0x80 -#define TX_BD_PID_DATA1 0xC0 +#define TX_BD_PID_DATA1 0xC0 #define TX_BD_CNF 0x200 #define TX_BD_TC 0x400 #define TX_BD_L 0x800 @@ -151,16 +151,16 @@ typedef struct mpc8xx_usb{ ushort usber; /* Event Register */ ushort res3; /* Reserved */ ushort usbmr; /* Mask Register */ - char res4; /* Reserved */ + char res4; /* Reserved */ char usbs; /* Status Register */ char res5[8]; /* Reserved */ }usb_t; typedef struct mpc8xx_parameter_ram{ - ushort ep0ptr; /* Endpoint Pointer Register 0 */ - ushort ep1ptr; /* Endpoint Pointer Register 1 */ - ushort ep2ptr; /* Endpoint Pointer Register 2 */ - ushort ep3ptr; /* Endpoint Pointer Register 3 */ + ushort ep0ptr; /* Endpoint Pointer Register 0 */ + ushort ep1ptr; /* Endpoint Pointer Register 1 */ + ushort ep2ptr; /* Endpoint Pointer Register 2 */ + ushort ep3ptr; /* Endpoint Pointer Register 3 */ uint rstate; /* Receive state */ uint rptr; /* Receive internal data pointer */ ushort frame_n; /* Frame number */ @@ -198,7 +198,7 @@ int udc_init(void); void udc_irq(void); int udc_endpoint_write(struct usb_endpoint_instance *endpoint); void udc_setup_ep(struct usb_device_instance *device, unsigned int ep, - struct usb_endpoint_instance *endpoint); + struct usb_endpoint_instance *endpoint); void udc_connect(void); void udc_disconnect(void); void udc_enable(struct usb_device_instance *device); diff --git a/include/usbdescriptors.h b/include/usbdescriptors.h index 8336c188c..a752097e5 100644 --- a/include/usbdescriptors.h +++ b/include/usbdescriptors.h @@ -321,7 +321,7 @@ struct usb_class_union_function_descriptor { u8 bDescriptorType; u8 bDescriptorSubtype; /* 0x06 */ u8 bMasterInterface; - //u8 bSlaveInterface0[0]; + /* u8 bSlaveInterface0[0]; */ u8 bSlaveInterface0; } __attribute__ ((packed)); -- cgit v1.2.3 From 99d70e3a47affb9bae041a2caece7cd516e213b3 Mon Sep 17 00:00:00 2001 From: Wolfgang Denk Date: Mon, 26 Jun 2006 11:06:00 +0200 Subject: More code cleanup --- cpu/arm920t/s3c24x0/usb.c | 6 +++--- drivers/usb_ohci.h | 6 +----- 2 files changed, 4 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/cpu/arm920t/s3c24x0/usb.c b/cpu/arm920t/s3c24x0/usb.c index 8114034cc..4bc7961a0 100644 --- a/cpu/arm920t/s3c24x0/usb.c +++ b/cpu/arm920t/s3c24x0/usb.c @@ -32,7 +32,7 @@ # include #endif -int usb_cpu_init() +int usb_cpu_init (void) { S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER(); @@ -53,7 +53,7 @@ int usb_cpu_init() return 0; } -int usb_cpu_stop() +int usb_cpu_stop (void) { S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER(); /* may not want to do this */ @@ -61,7 +61,7 @@ int usb_cpu_stop() return 0; } -int usb_cpu_init_fail() +int usb_cpu_init_fail (void) { S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER(); clk_power->CLKCON &= ~(1 << 4); diff --git a/drivers/usb_ohci.h b/drivers/usb_ohci.h index a1b36ed83..68dd4ecaf 100644 --- a/drivers/usb_ohci.h +++ b/drivers/usb_ohci.h @@ -7,18 +7,14 @@ * usb-ohci.h */ -/* functions for doing board specific setup/cleanup */ -#ifdef CFG_USB_BOARD_INIT +/* functions for doing board or CPU specific setup/cleanup */ extern int usb_board_init(void); extern int usb_board_stop(void); extern int usb_cpu_init_fail(void); -#endif -#ifdef CFG_USB_CPU_INIT extern int usb_cpu_init(void); extern int usb_cpu_stop(void); extern int usb_cpu_init_fail(void); -#endif static int cc_to_error[16] = { -- cgit v1.2.3 From 53e336e9ffc51035bdc4e5867631b3378761b4df Mon Sep 17 00:00:00 2001 From: Markus Klotzbuecher Date: Mon, 27 Nov 2006 11:43:09 +0100 Subject: Modified the mpc5xxx and the ppc4xx cpu to use the generic OHCI driver and adapted board configs TQM5200 and yosemite accordingly. This commit also makes the maximum number of root hub ports configurable (CFG_USB_OHCI_MAX_ROOT_PORTS). --- cpu/mpc5xxx/Makefile | 2 +- cpu/ppc4xx/Makefile | 3 ++- drivers/usb_ohci.c | 13 +++++++++++-- drivers/usb_ohci.h | 13 +++++++++++-- include/configs/TQM5200.h | 7 +++++++ include/configs/delta.h | 1 + include/configs/mp2usb.h | 1 + include/configs/trab.h | 1 + include/configs/yosemite.h | 6 ++++++ 9 files changed, 41 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/cpu/mpc5xxx/Makefile b/cpu/mpc5xxx/Makefile index a97b62517..94f82b225 100644 --- a/cpu/mpc5xxx/Makefile +++ b/cpu/mpc5xxx/Makefile @@ -28,7 +28,7 @@ LIB = lib$(CPU).a START = start.o ASOBJS = io.o firmware_sc_task_bestcomm.impl.o firmware_sc_task.impl.o OBJS = i2c.o traps.o cpu.o cpu_init.o fec.o ide.o interrupts.o \ - loadtask.o pci_mpc5200.o serial.o speed.o usb_ohci.o + loadtask.o pci_mpc5200.o serial.o speed.o usb_ohci.o usb.o all: .depend $(START) $(ASOBJS) $(LIB) diff --git a/cpu/ppc4xx/Makefile b/cpu/ppc4xx/Makefile index c56345700..ca68599f7 100644 --- a/cpu/ppc4xx/Makefile +++ b/cpu/ppc4xx/Makefile @@ -31,7 +31,8 @@ COBJS = 405gp_pci.o 4xx_enet.o \ bedbug_405.o commproc.o \ cpu.o cpu_init.o i2c.o interrupts.o \ miiphy.o sdram.o serial.o \ - spd_sdram.o speed.o traps.o usb_ohci.o usbdev.o + spd_sdram.o speed.o traps.o \ + usb_ohci.o usbdev.o usb.o OBJS = $(AOBJS) $(COBJS) diff --git a/drivers/usb_ohci.c b/drivers/usb_ohci.c index 9b3ca1232..be1a615eb 100644 --- a/drivers/usb_ohci.c +++ b/drivers/usb_ohci.c @@ -45,6 +45,7 @@ #ifdef CONFIG_USB_OHCI +/* mk: are these really required? */ #if defined(CONFIG_S3C2400) # include #elif defined(CONFIG_S3C2410) @@ -53,6 +54,8 @@ # include #elif defined(CONFIG_CPU_MONAHANS) # include +#elif defined(CONFIG_MPC5200) +# include #endif #include @@ -557,8 +560,10 @@ static int ep_link (ohci_t *ohci, ed_t *edi) * the link from the ed still points to another operational ed or 0 * so the HC can eventually finish the processing of the unlinked ed */ -static int ep_unlink (ohci_t *ohci, ed_t *ed) +static int ep_unlink (ohci_t *ohci, ed_t *edi) { + volatile ed_t *ed = edi; + ed->hwINFO |= m32_swap (OHCI_ED_SKIP); switch (ed->type) { @@ -825,6 +830,9 @@ static td_t * dl_reverse_done_list (ohci_t *ohci) } else td_list->ed->hwHeadP &= m32_swap (0xfffffff2); } +#ifdef CONFIG_MPC5200 + td_list->hwNextTD = 0; +#endif } td_list->next_dl_td = td_rev; @@ -1448,7 +1456,8 @@ static int hc_reset (ohci_t *ohci) readl(&ohci->regs->control)); /* Reset USB (needed by some controllers) */ - writel (0, &ohci->regs->control); + ohci->hc_control = 0; + writel (ohci->hc_control, &ohci->regs->control); /* HC Reset requires max 10 us delay */ writel (OHCI_HCR, &ohci->regs->cmdstatus); diff --git a/drivers/usb_ohci.h b/drivers/usb_ohci.h index 68dd4ecaf..95fbc4465 100644 --- a/drivers/usb_ohci.h +++ b/drivers/usb_ohci.h @@ -113,7 +113,9 @@ struct td { __u32 hwNextTD; /* Next TD Pointer */ __u32 hwBE; /* Memory Buffer End Pointer */ +/* #ifndef CONFIG_MPC5200 /\* this seems wrong *\/ */ __u16 hwPSW[MAXPSW]; +/* #endif */ __u8 unused; __u8 index; struct ed *ed; @@ -137,8 +139,13 @@ typedef struct td td_t; #define NUM_INTS 32 /* part of the OHCI standard */ struct ohci_hcca { __u32 int_table[NUM_INTS]; /* Interrupt ED table */ +#if defined(CONFIG_MPC5200) + __u16 pad1; /* set to 0 on each frame_no change */ + __u16 frame_no; /* current frame number */ +#else __u16 frame_no; /* current frame number */ __u16 pad1; /* set to 0 on each frame_no change */ +#endif __u32 done_head; /* info returned for an interrupt */ u8 reserved_for_hc[116]; } __attribute((aligned(256))); @@ -147,7 +154,9 @@ struct ohci_hcca { /* * Maximum number of root hub ports. */ -#define MAX_ROOT_PORTS 3 /* maximum OHCI root hub ports */ +#ifndef CFG_USB_OHCI_MAX_ROOT_PORTS +# error "CFG_USB_OHCI_MAX_ROOT_PORTS undefined!" +#endif /* * This is the structure of the OHCI controller's memory mapped I/O @@ -181,7 +190,7 @@ struct ohci_regs { __u32 a; __u32 b; __u32 status; - __u32 portstatus[MAX_ROOT_PORTS]; + __u32 portstatus[CFG_USB_OHCI_MAX_ROOT_PORTS]; } roothub; } __attribute((aligned(32))); diff --git a/include/configs/TQM5200.h b/include/configs/TQM5200.h index be83b6767..832dcdde5 100644 --- a/include/configs/TQM5200.h +++ b/include/configs/TQM5200.h @@ -128,6 +128,13 @@ #define CONFIG_USB_OHCI #define ADD_USB_CMD CFG_CMD_USB | CFG_CMD_FAT #define CONFIG_USB_STORAGE + +#undef CFG_USB_OHCI_BOARD_INIT +#define CFG_USB_OHCI_CPU_INIT +#define CFG_USB_OHCI_REGS_BASE MPC5XXX_USB +#define CFG_USB_OHCI_SLOT_NAME "mpc5200" +#define CFG_USB_OHCI_MAX_ROOT_PORTS 15 + #else #define ADD_USB_CMD 0 #endif diff --git a/include/configs/delta.h b/include/configs/delta.h index fea821ab9..0617ad864 100644 --- a/include/configs/delta.h +++ b/include/configs/delta.h @@ -111,6 +111,7 @@ #define CFG_USB_OHCI_CPU_INIT 1 #define CFG_USB_OHCI_REGS_BASE OHCI_REGS_BASE #define CFG_USB_OHCI_SLOT_NAME "delta" +#define CFG_USB_OHCI_MAX_ROOT_PORTS 3 #define LITTLEENDIAN 1 /* used by usb_ohci.c */ diff --git a/include/configs/mp2usb.h b/include/configs/mp2usb.h index edb20f8d8..d182d4948 100644 --- a/include/configs/mp2usb.h +++ b/include/configs/mp2usb.h @@ -111,6 +111,7 @@ #define CFG_USB_OHCI_CPU_INIT 1 #define CFG_USB_OHCI_REGS_BASE AT91_USB_HOST_BASE #define CFG_USB_OHCI_SLOT_NAME "at91rm9200" +#define CFG_USB_OHCI_MAX_ROOT_PORTS 15 #undef CONFIG_HARD_I2C diff --git a/include/configs/trab.h b/include/configs/trab.h index ae979475b..33617cdcd 100644 --- a/include/configs/trab.h +++ b/include/configs/trab.h @@ -88,6 +88,7 @@ #define CFG_USB_OHCI_CPU_INIT 1 #define CFG_USB_OHCI_REGS_BASE S3C24X0_USB_HOST_BASE #define CFG_USB_OHCI_SLOT_NAME "s3c2400" +#define CFG_USB_OHCI_MAX_ROOT_PORTS 15 /* * Size of malloc() pool diff --git a/include/configs/yosemite.h b/include/configs/yosemite.h index a81cf34f1..e6a02c631 100644 --- a/include/configs/yosemite.h +++ b/include/configs/yosemite.h @@ -216,6 +216,12 @@ #define CONFIG_USB_OHCI #define CONFIG_USB_STORAGE +#undef CFG_USB_OHCI_BOARD_INIT +#define CFG_USB_OHCI_CPU_INIT 1 +#define CFG_USB_OHCI_REGS_BASE (CFG_PERIPHERAL_BASE | 0x1000) +#define CFG_USB_OHCI_SLOT_NAME "ppc440" +#define CFG_USB_OHCI_MAX_ROOT_PORTS 15 + /*Comment this out to enable USB 1.1 device*/ #define USB_2_0_DEVICE #endif /*CONFIG_440EP*/ -- cgit v1.2.3 From 7b59b3c7a8ce2e4b567abf99c1cd667bf35b9418 Mon Sep 17 00:00:00 2001 From: Markus Klotzbuecher Date: Mon, 27 Nov 2006 11:44:58 +0100 Subject: Introduced the configuration option CONFIG_USB_OHCI_NEW in order to be able to choose between the old and the generic OHCI drivers. --- board/trab/auto_update.c | 2 +- cpu/arm920t/at91rm9200/usb.c | 2 +- cpu/arm920t/s3c24x0/usb.c | 2 +- cpu/pxa/usb.c | 2 +- drivers/usb_ohci.c | 4 ++-- include/configs/TQM5200.h | 2 +- include/configs/delta.h | 2 +- include/configs/mp2usb.h | 2 +- include/configs/trab.h | 2 +- include/configs/yosemite.h | 2 +- include/usb.h | 2 +- 11 files changed, 12 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/board/trab/auto_update.c b/board/trab/auto_update.c index d2c8d44a7..7684499c2 100644 --- a/board/trab/auto_update.c +++ b/board/trab/auto_update.c @@ -34,7 +34,7 @@ #ifdef CONFIG_AUTO_UPDATE -#ifndef CONFIG_USB_OHCI +#ifndef CONFIG_USB_OHCI_NEW #error "must define CONFIG_USB_OHCI" #endif diff --git a/cpu/arm920t/at91rm9200/usb.c b/cpu/arm920t/at91rm9200/usb.c index 98e3cdd58..366262e4c 100644 --- a/cpu/arm920t/at91rm9200/usb.c +++ b/cpu/arm920t/at91rm9200/usb.c @@ -23,7 +23,7 @@ #include -#if defined(CONFIG_USB_OHCI) && defined(CFG_USB_OHCI_CPU_INIT) +#if defined(CONFIG_USB_OHCI_NEW) && defined(CFG_USB_OHCI_CPU_INIT) # ifdef CONFIG_AT91RM9200 #include diff --git a/cpu/arm920t/s3c24x0/usb.c b/cpu/arm920t/s3c24x0/usb.c index 4bc7961a0..ef5d5bf71 100644 --- a/cpu/arm920t/s3c24x0/usb.c +++ b/cpu/arm920t/s3c24x0/usb.c @@ -23,7 +23,7 @@ #include -#if defined(CONFIG_USB_OHCI) && defined(CFG_USB_OHCI_CPU_INIT) +#if defined(CONFIG_USB_OHCI_NEW) && defined(CFG_USB_OHCI_CPU_INIT) # if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410) #if defined(CONFIG_S3C2400) diff --git a/cpu/pxa/usb.c b/cpu/pxa/usb.c index bff5bfb6a..5d273cb35 100644 --- a/cpu/pxa/usb.c +++ b/cpu/pxa/usb.c @@ -23,7 +23,7 @@ #include -#if defined(CONFIG_USB_OHCI) && defined(CFG_USB_OHCI_CPU_INIT) +#if defined(CONFIG_USB_OHCI_NEW) && defined(CFG_USB_OHCI_CPU_INIT) # ifdef CONFIG_CPU_MONAHANS #include diff --git a/drivers/usb_ohci.c b/drivers/usb_ohci.c index be1a615eb..482633fc6 100644 --- a/drivers/usb_ohci.c +++ b/drivers/usb_ohci.c @@ -43,7 +43,7 @@ #include /* #include no PCI on the S3C24X0 */ -#ifdef CONFIG_USB_OHCI +#ifdef CONFIG_USB_OHCI_NEW /* mk: are these really required? */ #if defined(CONFIG_S3C2400) @@ -1756,4 +1756,4 @@ int usb_lowlevel_stop(void) return 0; } -#endif /* CONFIG_USB_OHCI */ +#endif /* CONFIG_USB_OHCI_NEW */ diff --git a/include/configs/TQM5200.h b/include/configs/TQM5200.h index 832dcdde5..6c8712b68 100644 --- a/include/configs/TQM5200.h +++ b/include/configs/TQM5200.h @@ -125,7 +125,7 @@ /* USB */ #ifdef CONFIG_STK52XX -#define CONFIG_USB_OHCI +#define CONFIG_USB_OHCI_NEW #define ADD_USB_CMD CFG_CMD_USB | CFG_CMD_FAT #define CONFIG_USB_STORAGE diff --git a/include/configs/delta.h b/include/configs/delta.h index 0617ad864..4038f2196 100644 --- a/include/configs/delta.h +++ b/include/configs/delta.h @@ -103,7 +103,7 @@ #endif /* USB */ -#define CONFIG_USB_OHCI 1 +#define CONFIG_USB_OHCI_NEW 1 #define CONFIG_USB_STORAGE 1 #define CONFIG_DOS_PARTITION 1 diff --git a/include/configs/mp2usb.h b/include/configs/mp2usb.h index d182d4948..2cf78a3b1 100644 --- a/include/configs/mp2usb.h +++ b/include/configs/mp2usb.h @@ -101,7 +101,7 @@ #undef CONFIG_MODEM_SUPPORT /* disable modem initialization stuff */ -#define CONFIG_USB_OHCI 1 +#define CONFIG_USB_OHCI_NEW 1 #define CONFIG_USB_KEYBOARD 1 #define CONFIG_USB_STORAGE 1 #define CONFIG_DOS_PARTITION 1 diff --git a/include/configs/trab.h b/include/configs/trab.h index 33617cdcd..acf86d08a 100644 --- a/include/configs/trab.h +++ b/include/configs/trab.h @@ -80,7 +80,7 @@ #define CFG_EEPROM_PAGE_WRITE_DELAY_MS 10 /* USB stuff */ -#define CONFIG_USB_OHCI 1 +#define CONFIG_USB_OHCI_NEW 1 #define CONFIG_USB_STORAGE 1 #define CONFIG_DOS_PARTITION 1 diff --git a/include/configs/yosemite.h b/include/configs/yosemite.h index e6a02c631..7f434e93f 100644 --- a/include/configs/yosemite.h +++ b/include/configs/yosemite.h @@ -213,7 +213,7 @@ #ifdef CONFIG_440EP /* USB */ -#define CONFIG_USB_OHCI +#define CONFIG_USB_OHCI_NEW #define CONFIG_USB_STORAGE #undef CFG_USB_OHCI_BOARD_INIT diff --git a/include/usb.h b/include/usb.h index bf7155404..fafac8934 100644 --- a/include/usb.h +++ b/include/usb.h @@ -169,7 +169,7 @@ struct usb_device { * this is how the lowlevel part communicate with the outer world */ -#if defined(CONFIG_USB_UHCI) || defined(CONFIG_USB_OHCI) || defined (CONFIG_USB_SL811HS) +#if defined(CONFIG_USB_UHCI) || defined(CONFIG_USB_OHCI) || defined (CONFIG_USB_SL811HS) || defined(CONFIG_USB_OHCI_NEW) int usb_lowlevel_init(void); int usb_lowlevel_stop(void); int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer,int transfer_len); -- cgit v1.2.3 From ae3b770e4eae8e98b6e9e29662e18c47fdf0171f Mon Sep 17 00:00:00 2001 From: Markus Klotzbuecher Date: Mon, 27 Nov 2006 11:46:46 +0100 Subject: Fix some endianness issues related to the generic ohci driver --- drivers/usb_ohci.c | 60 ++++++++++++++++++++++++++++++++++++++--------- include/configs/IceCube.h | 9 ++++++- include/usb.h | 15 +++++++----- 3 files changed, 66 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/usb_ohci.c b/drivers/usb_ohci.c index 482633fc6..c5e4c38ef 100644 --- a/drivers/usb_ohci.c +++ b/drivers/usb_ohci.c @@ -62,15 +62,20 @@ #include #include "usb_ohci.h" -#undef S3C24X0_merge +#define S3C24X0_merge #if defined(CONFIG_ARM920T) || \ defined(CONFIG_S3C2400) || \ - defined(CONFIG_S3C2410) + defined(CONFIG_S3C2410) || \ + defined(CONFIG_440EP) || \ + defined(CONFIG_MPC5200) # define OHCI_USE_NPS /* force NoPowerSwitching mode */ #endif #undef OHCI_VERBOSE_DEBUG /* not always helpful */ +#undef DEBUG +#undef SHOW_INFO +#undef OHCI_FILL_TRACE /* For initializing controller (mask in an HCFS mode too) */ #define OHCI_CONTROL_INIT \ @@ -95,8 +100,13 @@ #define info(format, arg...) do {} while(0) #endif -#define m16_swap(x) swap_16(x) -#define m32_swap(x) swap_32(x) +#if defined(CONFIG_440EP) || defined(CONFIG_MPC5200) +# define m16_swap(x) (x) +# define m32_swap(x) (x) +#else +# define m16_swap(x) swap_16(x) +# define m32_swap(x) swap_32(x) +#endif /* global ohci_t */ static ohci_t gohci; @@ -523,7 +533,7 @@ static int ep_link (ohci_t *ohci, ed_t *edi) if (ohci->ed_controltail == NULL) { writel (ed, &ohci->regs->ed_controlhead); } else { - ohci->ed_controltail->hwNextED = m32_swap (ed); + ohci->ed_controltail->hwNextED = m32_swap ((unsigned long)ed); } ed->ed_prev = ohci->ed_controltail; if (!ohci->ed_controltail && !ohci->ed_rm_list[0] && @@ -539,7 +549,7 @@ static int ep_link (ohci_t *ohci, ed_t *edi) if (ohci->ed_bulktail == NULL) { writel (ed, &ohci->regs->ed_bulkhead); } else { - ohci->ed_bulktail->hwNextED = m32_swap (ed); + ohci->ed_bulktail->hwNextED = m32_swap ((unsigned long)ed); } ed->ed_prev = ohci->ed_bulktail; if (!ohci->ed_bulktail && !ohci->ed_rm_list[0] && @@ -635,7 +645,7 @@ static ed_t * ep_add_ed (struct usb_device *usb_dev, unsigned long pipe) ed->hwINFO = m32_swap (OHCI_ED_SKIP); /* skip ed */ /* dummy td; end of td list for ed */ td = td_alloc (usb_dev); - ed->hwTailP = m32_swap (td); + ed->hwTailP = m32_swap ((unsigned long)td); ed->hwHeadP = ed->hwTailP; ed->state = ED_UNLINK; ed->type = usb_pipetype (pipe); @@ -693,12 +703,12 @@ static void td_fill (ohci_t *ohci, unsigned int info, data = 0; td->hwINFO = m32_swap (info); - td->hwCBP = m32_swap (data); + td->hwCBP = m32_swap ((unsigned long)data); if (data) - td->hwBE = m32_swap (data + len - 1); + td->hwBE = m32_swap ((unsigned long)(data + len - 1)); else td->hwBE = 0; - td->hwNextTD = m32_swap (td_pt); + td->hwNextTD = m32_swap ((unsigned long)td_pt); #ifndef S3C24X0_merge td->hwPSW [0] = m16_swap (((__u32)data & 0x0FFF) | 0xE000); #endif @@ -875,7 +885,12 @@ static int dl_done_list (ohci_t *ohci, td_t *td_list) /* see if this done list makes for all TD's of current URB, * and mark the URB finished if so */ if (++(lurb_priv->td_cnt) == lurb_priv->length) { +#if 1 + if ((ed->state & (ED_OPER | ED_UNLINK)) && + (lurb_priv->state != URB_DEL)) +#else if ((ed->state & (ED_OPER | ED_UNLINK))) +#endif urb_finished = 1; else dbg("dl_done_list: strange.., ED state %x, ed->state\n"); @@ -1068,9 +1083,15 @@ pkt_print(dev, pipe, buffer, transfer_len, cmd, "SUB(rh)", usb_pipein(pipe)); } bmRType_bReq = cmd->requesttype | (cmd->request << 8); +#if defined(CONFIG_440EP) || defined(CONFIG_MPC5200) + wValue = __swap_16(cmd->value); + wIndex = __swap_16(cmd->index); + wLength = __swap_16(cmd->length); +#else wValue = m16_swap (cmd->value); wIndex = m16_swap (cmd->index); wLength = m16_swap (cmd->length); +#endif /* CONFIG_440EP || CONFIG_MPC5200 */ info("Root-Hub: adr: %2x cmd(%1x): %08x %04x %04x %04x", dev->devnum, 8, bmRType_bReq, wValue, wIndex, wLength); @@ -1084,6 +1105,20 @@ pkt_print(dev, pipe, buffer, transfer_len, cmd, "SUB(rh)", usb_pipein(pipe)); RH_OTHER | RH_CLASS almost ever means HUB_PORT here */ +#if defined(CONFIG_440EP) || defined(CONFIG_MPC5200) + case RH_GET_STATUS: + *(__u16 *) data_buf = __swap_16(1); OK (2); + case RH_GET_STATUS | RH_INTERFACE: + *(__u16 *) data_buf = __swap_16(0); OK (2); + case RH_GET_STATUS | RH_ENDPOINT: + *(__u16 *) data_buf = __swap_16(0); OK (2); + case RH_GET_STATUS | RH_CLASS: + *(__u32 *) data_buf = __swap_32( + RD_RH_STAT & ~(RH_HS_CRWE | RH_HS_DRWE)); + OK (4); + case RH_GET_STATUS | RH_OTHER | RH_CLASS: + *(__u32 *) data_buf = __swap_32(RD_RH_PORTSTAT); OK (4); +#else case RH_GET_STATUS: *(__u16 *) data_buf = m16_swap (1); OK (2); case RH_GET_STATUS | RH_INTERFACE: @@ -1096,6 +1131,7 @@ pkt_print(dev, pipe, buffer, transfer_len, cmd, "SUB(rh)", usb_pipein(pipe)); OK (4); case RH_GET_STATUS | RH_OTHER | RH_CLASS: *(__u32 *) data_buf = m32_swap (RD_RH_PORTSTAT); OK (4); +#endif /* CONFIG_440EP || CONFIG_MPC5200 */ case RH_CLEAR_FEATURE | RH_ENDPOINT: switch (wValue) { @@ -1294,8 +1330,10 @@ int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer, return -1; } +#if 0 wait_ms(10); /* ohci_dump_status(&gohci); */ +#endif /* allow more time for a BULK device to react - some are slow */ #define BULK_TO 5000 /* timeout in milliseconds */ @@ -1337,6 +1375,7 @@ int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer, err("CTL:TIMEOUT "); #ifdef S3C24X0_merge dbg("submit_common_msg: TO status %x\n", stat); + stat = USB_ST_CRC_ERR; urb_finished = 1; #endif stat = USB_ST_CRC_ERR; @@ -1755,5 +1794,4 @@ int usb_lowlevel_stop(void) return 0; } - #endif /* CONFIG_USB_OHCI_NEW */ diff --git a/include/configs/IceCube.h b/include/configs/IceCube.h index 1152f838d..8753b9ff6 100644 --- a/include/configs/IceCube.h +++ b/include/configs/IceCube.h @@ -94,9 +94,16 @@ /* USB */ #if 1 -#define CONFIG_USB_OHCI +#define CONFIG_USB_OHCI_NEW #define ADD_USB_CMD CFG_CMD_USB | CFG_CMD_FAT #define CONFIG_USB_STORAGE + +#undef CFG_USB_OHCI_BOARD_INIT +#define CFG_USB_OHCI_CPU_INIT +#define CFG_USB_OHCI_REGS_BASE MPC5XXX_USB +#define CFG_USB_OHCI_SLOT_NAME "mpc5200" +#define CFG_USB_OHCI_MAX_ROOT_PORTS 15 + #else #define ADD_USB_CMD 0 #endif diff --git a/include/usb.h b/include/usb.h index fafac8934..419a7e364 100644 --- a/include/usb.h +++ b/include/usb.h @@ -230,16 +230,12 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate); /* big endian -> little endian conversion */ /* some CPUs are already little endian e.g. the ARM920T */ -#ifdef LITTLEENDIAN -#define swap_16(x) ((unsigned short)(x)) -#define swap_32(x) ((unsigned long)(x)) -#else -#define swap_16(x) \ +#define __swap_16(x) \ ({ unsigned short x_ = (unsigned short)x; \ (unsigned short)( \ ((x_ & 0x00FFU) << 8) | ((x_ & 0xFF00U) >> 8) ); \ }) -#define swap_32(x) \ +#define __swap_32(x) \ ({ unsigned long x_ = (unsigned long)x; \ (unsigned long)( \ ((x_ & 0x000000FFUL) << 24) | \ @@ -247,6 +243,13 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate); ((x_ & 0x00FF0000UL) >> 8) | \ ((x_ & 0xFF000000UL) >> 24) ); \ }) + +#ifdef LITTLEENDIAN +# define swap_16(x) (x) +# define swap_32(x) (x) +#else +# define swap_16(x) __swap_16(x) +# define swap_32(x) __swap_32(x) #endif /* LITTLEENDIAN */ /* -- cgit v1.2.3 From ae79f60677c208326535647dcbd5c3ec40dbcb0b Mon Sep 17 00:00:00 2001 From: Markus Klotzbuecher Date: Mon, 26 Mar 2007 11:21:05 +0200 Subject: USB: remove the S3C24X0_merge #define, which was introduced while merging OHCI drivers. Signed-off-by: Markus Klotzbuecher --- drivers/usb_ohci.c | 56 +++--------------------------------------------------- 1 file changed, 3 insertions(+), 53 deletions(-) (limited to 'drivers') diff --git a/drivers/usb_ohci.c b/drivers/usb_ohci.c index c5e4c38ef..1c3f622d5 100644 --- a/drivers/usb_ohci.c +++ b/drivers/usb_ohci.c @@ -62,8 +62,6 @@ #include #include "usb_ohci.h" -#define S3C24X0_merge - #if defined(CONFIG_ARM920T) || \ defined(CONFIG_S3C2400) || \ defined(CONFIG_S3C2410) || \ @@ -123,10 +121,8 @@ int got_rhsc; /* device which was disconnected */ struct usb_device *devgone; -#ifdef S3C24X0_merge /* flag guarding URB transation */ int urb_finished = 0; -#endif /*-------------------------------------------------------------------------*/ @@ -433,7 +429,7 @@ int sohci_submit_job(struct usb_device *dev, unsigned long pipe, void *buffer, err("sohci_submit_job: EPIPE"); return -1; } -#ifdef S3C24X0_merge + /* if we have an unfinished URB from previous transaction let's * fail and scream as quickly as possible so as not to corrupt * further communication */ @@ -443,7 +439,6 @@ int sohci_submit_job(struct usb_device *dev, unsigned long pipe, void *buffer, } /* we're about to begin a new transaction here so mark the URB unfinished */ urb_finished = 0; -#endif /* every endpoint has a ed, locate and fill it */ if (!(ed = ep_add_ed (dev, pipe))) { @@ -709,9 +704,6 @@ static void td_fill (ohci_t *ohci, unsigned int info, else td->hwBE = 0; td->hwNextTD = m32_swap ((unsigned long)td_pt); -#ifndef S3C24X0_merge - td->hwPSW [0] = m16_swap (((__u32)data & 0x0FFF) | 0xE000); -#endif /* append to queue */ td->ed->hwTailP = td->hwNextTD; @@ -881,7 +873,7 @@ static int dl_done_list (ohci_t *ohci, td_t *td_list) dbg("ConditionCode %#x", cc); stat = cc_to_error[cc]; } -#ifdef S3C24X0_merge + /* see if this done list makes for all TD's of current URB, * and mark the URB finished if so */ if (++(lurb_priv->td_cnt) == lurb_priv->length) { @@ -890,7 +882,6 @@ static int dl_done_list (ohci_t *ohci, td_t *td_list) (lurb_priv->state != URB_DEL)) #else if ((ed->state & (ED_OPER | ED_UNLINK))) -#endif urb_finished = 1; else dbg("dl_done_list: strange.., ED state %x, ed->state\n"); @@ -1351,7 +1342,6 @@ int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer, break; } -#ifdef S3C24X0_merge /* NOTE: since we are not interrupt driven in U-Boot and always * handle only one URB at a time, we cannot assume the * transaction finished on the first successful return from @@ -1362,9 +1352,6 @@ int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer, * hc_interrupt() gets called again as there needs to be some * more TD's to process still */ if ((stat >= 0) && (stat != 0xff) && (urb_finished)) { -#else - if (stat >= 0 && stat != 0xff) { -#endif /* 0xff is returned for an SF-interrupt */ break; } @@ -1373,38 +1360,13 @@ int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer, wait_ms(1); } else { err("CTL:TIMEOUT "); -#ifdef S3C24X0_merge dbg("submit_common_msg: TO status %x\n", stat); stat = USB_ST_CRC_ERR; urb_finished = 1; -#endif stat = USB_ST_CRC_ERR; break; } } -#ifndef S3C24X0_merge - /* we got an Root Hub Status Change interrupt */ - if (got_rhsc) { -#ifdef DEBUG - ohci_dump_roothub (&gohci, 1); -#endif - got_rhsc = 0; - /* abuse timeout */ - timeout = rh_check_port_status(&gohci); - if (timeout >= 0) { -#if 0 /* this does nothing useful, but leave it here in case that changes */ - /* the called routine adds 1 to the passed value */ - usb_hub_port_connect_change(gohci.rh.dev, timeout - 1); -#endif - /* - * XXX - * This is potentially dangerous because it assumes - * that only one device is ever plugged in! - */ - devgone = dev; - } - } -#endif /* S3C24X0_merge */ dev->status = stat; dev->act_len = transfer_len; @@ -1582,8 +1544,6 @@ static int hc_interrupt (void) int ints; int stat = -1; -#ifdef S3C24X0_merge - if ((ohci->hcca->done_head != 0) && !(m32_swap (ohci->hcca->done_head) & 0x01)) { ints = OHCI_INTR_WDH; @@ -1595,20 +1555,12 @@ static int hc_interrupt (void) dbg("hc_interrupt: returning..\n"); return 0xff; } -#else - if ((ohci->hcca->done_head != 0) && !(m32_swap (ohci->hcca->done_head) & 0x01)) { - ints = OHCI_INTR_WDH; - } else { - ints = readl (®s->intrstatus); - } -#endif + /* dbg("Interrupt: %x frame: %x", ints, le16_to_cpu (ohci->hcca->frame_no)); */ if (ints & OHCI_INTR_RHSC) { got_rhsc = 1; -#ifdef S3C24X0_merge stat = 0xff; -#endif } if (ints & OHCI_INTR_UE) { @@ -1762,9 +1714,7 @@ int usb_lowlevel_init(void) ohci_dump (&gohci, 1); #else wait_ms(1); -# ifdef S3C24X0_merge urb_finished = 1; -# endif #endif ohci_inited = 1; return 0; -- cgit v1.2.3 From 822af351ad2babc7d99033361a5fcacd30f6bc78 Mon Sep 17 00:00:00 2001 From: Rodolfo Giometti Date: Tue, 3 Apr 2007 14:27:18 +0200 Subject: Support for the Philips ISP116x HCD (Host Controller Driver) Signed-off-by: Rodolfo Giometti --- drivers/Makefile | 2 +- drivers/isp116x-hcd.c | 1412 +++++++++++++++++++++++++++++++++++++++++++++++++ drivers/isp116x.h | 489 +++++++++++++++++ include/usb.h | 5 +- 4 files changed, 1906 insertions(+), 2 deletions(-) create mode 100644 drivers/isp116x-hcd.c create mode 100644 drivers/isp116x.h (limited to 'drivers') diff --git a/drivers/Makefile b/drivers/Makefile index 0ca400c68..52cd388a0 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -31,7 +31,7 @@ COBJS = 3c589.o 5701rls.o ali512x.o atmel_usart.o \ bcm570x.o bcm570x_autoneg.o cfb_console.o cfi_flash.o \ cs8900.o ct69000.o dataflash.o dc2114x.o dm9000x.o \ e1000.o eepro100.o \ - i8042.o inca-ip_sw.o keyboard.o \ + i8042.o inca-ip_sw.o isp116x-hcd.o keyboard.o \ lan91c96.o \ natsemi.o ne2000.o netarm_eth.o netconsole.o \ ns16550.o ns8382x.o ns87308.o ns7520_eth.o omap1510_i2c.o \ diff --git a/drivers/isp116x-hcd.c b/drivers/isp116x-hcd.c new file mode 100644 index 000000000..d57b8ece2 --- /dev/null +++ b/drivers/isp116x-hcd.c @@ -0,0 +1,1412 @@ +/* + * ISP116x HCD (Host Controller Driver) for u-boot. + * + * Copyright (C) 2006-2007 Rodolfo Giometti + * Copyright (C) 2006-2007 Eurotech S.p.A. + * + * 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 + * + * + * Derived in part from the SL811 HCD driver "u-boot/drivers/sl811_usb.c" + * (original copyright message follows): + * + * (C) Copyright 2004 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * This code is based on linux driver for sl811hs chip, source at + * drivers/usb/host/sl811.c: + * + * SL811 Host Controller Interface driver for USB. + * + * Copyright (c) 2003/06, Courage Co., Ltd. + * + * Based on: + * 1.uhci.c by Linus Torvalds, Johannes Erdfelt, Randy Dunlap, + * Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber, + * Adam Richter, Gregory P. Smith; + * 2.Original SL811 driver (hc_sl811.o) by Pei Liu + * 3.Rewrited as sl811.o by Yin Aihua + * + * [[GNU/GPL disclaimer]] + * + * and in part from AU1x00 OHCI HCD driver "u-boot/cpu/mips/au1x00_usb_ohci.c" + * (original copyright message follows): + * + * URB OHCI HCD (Host Controller Driver) for USB on the AU1x00. + * + * (C) Copyright 2003 + * Gary Jennejohn, DENX Software Engineering + * + * [[GNU/GPL disclaimer]] + * + * Note: Part of this code has been derived from linux + */ + +#include + +#ifdef CONFIG_USB_ISP116X_HCD +#include +#include +#include +#include + +/* + * ISP116x chips require certain delays between accesses to its + * registers. The following timing options exist. + * + * 1. Configure your memory controller (the best) + * 2. Use ndelay (easiest, poorest). For that, enable the following macro. + * + * Value is in microseconds. + */ +#ifdef ISP116X_HCD_USE_UDELAY +#define UDELAY 1 +#endif + +/* + * On some (slowly?) machines an extra delay after data packing into + * controller's FIFOs is required, * otherwise you may get the following + * error: + * + * uboot> usb start + * (Re)start USB... + * USB: scanning bus for devices... isp116x: isp116x_submit_job: CTL:TIMEOUT + * isp116x: isp116x_submit_job: ****** FIFO not ready! ****** + * + * USB device not responding, giving up (status=4) + * isp116x: isp116x_submit_job: ****** FIFO not empty! ****** + * isp116x: isp116x_submit_job: ****** FIFO not empty! ****** + * isp116x: isp116x_submit_job: ****** FIFO not empty! ****** + * 3 USB Device(s) found + * scanning bus for storage devices... 0 Storage Device(s) found + * + * Value is in milliseconds. + */ +#ifdef ISP116X_HCD_USE_EXTRA_DELAY +#define EXTRA_DELAY 2 +#endif + +/* + * Enable the following defines if you wish enable debugging messages. + */ +#undef DEBUG /* enable debugging messages */ +#undef TRACE /* enable tracing code */ +#undef VERBOSE /* verbose debugging messages */ + +#include "isp116x.h" + +#define DRIVER_VERSION "08 Jan 2007" +static const char hcd_name[] = "isp116x-hcd"; + +struct isp116x isp116x_dev; +struct isp116x_platform_data isp116x_board; +int got_rhsc = 0; /* root hub status change */ +struct usb_device *devgone; /* device which was disconnected */ +int rh_devnum = 0; /* address of Root Hub endpoint */ + +/* ------------------------------------------------------------------------- */ + +#define ALIGN(x,a) (((x)+(a)-1UL)&~((a)-1UL)) +#define min_t(type,x,y) \ + ({ type __x = (x); type __y = (y); __x < __y ? __x : __y; }) + +/* ------------------------------------------------------------------------- */ + +static int isp116x_reset(struct isp116x *isp116x); + +/* --- Debugging functions ------------------------------------------------- */ + +#define isp116x_show_reg(d, r) { \ + if ((r) < 0x20) { \ + DBG("%-12s[%02x]: %08x", #r, \ + r, isp116x_read_reg32(d, r)); \ + } else { \ + DBG("%-12s[%02x]: %04x", #r, \ + r, isp116x_read_reg16(d, r)); \ + } \ +} + +#define isp116x_show_regs(d) { \ + isp116x_show_reg(d, HCREVISION); \ + isp116x_show_reg(d, HCCONTROL); \ + isp116x_show_reg(d, HCCMDSTAT); \ + isp116x_show_reg(d, HCINTSTAT); \ + isp116x_show_reg(d, HCINTENB); \ + isp116x_show_reg(d, HCFMINTVL); \ + isp116x_show_reg(d, HCFMREM); \ + isp116x_show_reg(d, HCFMNUM); \ + isp116x_show_reg(d, HCLSTHRESH); \ + isp116x_show_reg(d, HCRHDESCA); \ + isp116x_show_reg(d, HCRHDESCB); \ + isp116x_show_reg(d, HCRHSTATUS); \ + isp116x_show_reg(d, HCRHPORT1); \ + isp116x_show_reg(d, HCRHPORT2); \ + isp116x_show_reg(d, HCHWCFG); \ + isp116x_show_reg(d, HCDMACFG); \ + isp116x_show_reg(d, HCXFERCTR); \ + isp116x_show_reg(d, HCuPINT); \ + isp116x_show_reg(d, HCuPINTENB); \ + isp116x_show_reg(d, HCCHIPID); \ + isp116x_show_reg(d, HCSCRATCH); \ + isp116x_show_reg(d, HCITLBUFLEN); \ + isp116x_show_reg(d, HCATLBUFLEN); \ + isp116x_show_reg(d, HCBUFSTAT); \ + isp116x_show_reg(d, HCRDITL0LEN); \ + isp116x_show_reg(d, HCRDITL1LEN); \ +} + +#if defined(TRACE) + +static int isp116x_get_current_frame_number(struct usb_device *usb_dev) +{ + struct isp116x *isp116x = &isp116x_dev; + + return isp116x_read_reg32(isp116x, HCFMNUM); +} + +static void dump_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int len, char *str) +{ +#if defined(VERBOSE) + int i; +#endif + + DBG("%s URB:[%4x] dev:%2d,ep:%2d-%c,type:%s,len:%d stat:%#lx", + str, + isp116x_get_current_frame_number(dev), + usb_pipedevice(pipe), + usb_pipeendpoint(pipe), + usb_pipeout(pipe) ? 'O' : 'I', + usb_pipetype(pipe) < 2 ? + (usb_pipeint(pipe) ? + "INTR" : "ISOC") : + (usb_pipecontrol(pipe) ? "CTRL" : "BULK"), len, dev->status); +#if defined(VERBOSE) + if (len > 0 && buffer) { + printf(__FILE__ ": data(%d):", len); + for (i = 0; i < 16 && i < len; i++) + printf(" %02x", ((__u8 *) buffer)[i]); + printf("%s\n", i < len ? "..." : ""); + } +#endif +} + +#define PTD_DIR_STR(ptd) ({char __c; \ + switch(PTD_GET_DIR(ptd)){ \ + case 0: __c = 's'; break; \ + case 1: __c = 'o'; break; \ + default: __c = 'i'; break; \ + }; __c;}) + +/* + Dump PTD info. The code documents the format + perfectly, right :) +*/ +static inline void dump_ptd(struct ptd *ptd) +{ +#if defined(VERBOSE) + int k; +#endif + + DBG("PTD(ext) : cc:%x %d%c%d %d,%d,%d t:%x %x%x%x", + PTD_GET_CC(ptd), + PTD_GET_FA(ptd), PTD_DIR_STR(ptd), PTD_GET_EP(ptd), + PTD_GET_COUNT(ptd), PTD_GET_LEN(ptd), PTD_GET_MPS(ptd), + PTD_GET_TOGGLE(ptd), + PTD_GET_ACTIVE(ptd), PTD_GET_SPD(ptd), PTD_GET_LAST(ptd)); +#if defined(VERBOSE) + printf("isp116x: %s: PTD(byte): ", __FUNCTION__); + for (k = 0; k < sizeof(struct ptd); ++k) + printf("%02x ", ((u8 *) ptd)[k]); + printf("\n"); +#endif +} + +static inline void dump_ptd_data(struct ptd *ptd, u8 * buf, int type) +{ +#if defined(VERBOSE) + int k; + + if (type == 0 /* 0ut data */ ) { + printf("isp116x: %s: out data: ", __FUNCTION__); + for (k = 0; k < PTD_GET_LEN(ptd); ++k) + printf("%02x ", ((u8 *) buf)[k]); + printf("\n"); + } + if (type == 1 /* 1n data */ ) { + printf("isp116x: %s: in data: ", __FUNCTION__); + for (k = 0; k < PTD_GET_COUNT(ptd); ++k) + printf("%02x ", ((u8 *) buf)[k]); + printf("\n"); + } + + if (PTD_GET_LAST(ptd)) + DBG("--- last PTD ---"); +#endif +} + +#else + +#define dump_msg(dev, pipe, buffer, len, str) do { } while (0) +#define dump_pkt(dev, pipe, buffer, len, setup, str, small) do {} while (0) + +#define dump_ptd(ptd) do {} while (0) +#define dump_ptd_data(ptd, buf, type) do {} while (0) + +#endif + +/* --- Virtual Root Hub ---------------------------------------------------- */ + +/* Device descriptor */ +static __u8 root_hub_dev_des[] = { + 0x12, /* __u8 bLength; */ + 0x01, /* __u8 bDescriptorType; Device */ + 0x10, /* __u16 bcdUSB; v1.1 */ + 0x01, + 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ + 0x00, /* __u8 bDeviceSubClass; */ + 0x00, /* __u8 bDeviceProtocol; */ + 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */ + 0x00, /* __u16 idVendor; */ + 0x00, + 0x00, /* __u16 idProduct; */ + 0x00, + 0x00, /* __u16 bcdDevice; */ + 0x00, + 0x00, /* __u8 iManufacturer; */ + 0x01, /* __u8 iProduct; */ + 0x00, /* __u8 iSerialNumber; */ + 0x01 /* __u8 bNumConfigurations; */ +}; + +/* Configuration descriptor */ +static __u8 root_hub_config_des[] = { + 0x09, /* __u8 bLength; */ + 0x02, /* __u8 bDescriptorType; Configuration */ + 0x19, /* __u16 wTotalLength; */ + 0x00, + 0x01, /* __u8 bNumInterfaces; */ + 0x01, /* __u8 bConfigurationValue; */ + 0x00, /* __u8 iConfiguration; */ + 0x40, /* __u8 bmAttributes; + Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup, 4..0: resvd */ + 0x00, /* __u8 MaxPower; */ + + /* interface */ + 0x09, /* __u8 if_bLength; */ + 0x04, /* __u8 if_bDescriptorType; Interface */ + 0x00, /* __u8 if_bInterfaceNumber; */ + 0x00, /* __u8 if_bAlternateSetting; */ + 0x01, /* __u8 if_bNumEndpoints; */ + 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */ + 0x00, /* __u8 if_bInterfaceSubClass; */ + 0x00, /* __u8 if_bInterfaceProtocol; */ + 0x00, /* __u8 if_iInterface; */ + + /* endpoint */ + 0x07, /* __u8 ep_bLength; */ + 0x05, /* __u8 ep_bDescriptorType; Endpoint */ + 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ + 0x03, /* __u8 ep_bmAttributes; Interrupt */ + 0x00, /* __u16 ep_wMaxPacketSize; ((MAX_ROOT_PORTS + 1) / 8 */ + 0x02, + 0xff /* __u8 ep_bInterval; 255 ms */ +}; + +static unsigned char root_hub_str_index0[] = { + 0x04, /* __u8 bLength; */ + 0x03, /* __u8 bDescriptorType; String-descriptor */ + 0x09, /* __u8 lang ID */ + 0x04, /* __u8 lang ID */ +}; + +static unsigned char root_hub_str_index1[] = { + 0x22, /* __u8 bLength; */ + 0x03, /* __u8 bDescriptorType; String-descriptor */ + 'I', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'S', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'P', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + '1', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + '1', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + '6', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'x', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + ' ', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'R', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'o', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'o', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 't', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + ' ', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'H', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'u', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'b', /* __u8 Unicode */ + 0, /* __u8 Unicode */ +}; + +/* + * Hub class-specific descriptor is constructed dynamically + */ + +/* --- Virtual root hub management functions ------------------------------- */ + +static int rh_check_port_status(struct isp116x *isp116x) +{ + u32 temp, ndp, i; + int res; + + res = -1; + temp = isp116x_read_reg32(isp116x, HCRHSTATUS); + ndp = (temp & RH_A_NDP); + for (i = 0; i < ndp; i++) { + temp = isp116x_read_reg32(isp116x, HCRHPORT1 + i); + /* check for a device disconnect */ + if (((temp & (RH_PS_PESC | RH_PS_CSC)) == + (RH_PS_PESC | RH_PS_CSC)) && ((temp & RH_PS_CCS) == 0)) { + res = i; + break; + } + } + return res; +} + +/* --- HC management functions --------------------------------------------- */ + +/* Write len bytes to fifo, pad till 32-bit boundary + */ +static void write_ptddata_to_fifo(struct isp116x *isp116x, void *buf, int len) +{ + u8 *dp = (u8 *) buf; + u16 *dp2 = (u16 *) buf; + u16 w; + int quot = len % 4; + + if ((unsigned long)dp2 & 1) { + /* not aligned */ + for (; len > 1; len -= 2) { + w = *dp++; + w |= *dp++ << 8; + isp116x_raw_write_data16(isp116x, w); + } + if (len) + isp116x_write_data16(isp116x, (u16) * dp); + } else { + /* aligned */ + for (; len > 1; len -= 2) + isp116x_raw_write_data16(isp116x, *dp2++); + if (len) + isp116x_write_data16(isp116x, 0xff & *((u8 *) dp2)); + } + if (quot == 1 || quot == 2) + isp116x_raw_write_data16(isp116x, 0); +} + +/* Read len bytes from fifo and then read till 32-bit boundary + */ +static void read_ptddata_from_fifo(struct isp116x *isp116x, void *buf, int len) +{ + u8 *dp = (u8 *) buf; + u16 *dp2 = (u16 *) buf; + u16 w; + int quot = len % 4; + + if ((unsigned long)dp2 & 1) { + /* not aligned */ + for (; len > 1; len -= 2) { + w = isp116x_raw_read_data16(isp116x); + *dp++ = w & 0xff; + *dp++ = (w >> 8) & 0xff; + } + if (len) + *dp = 0xff & isp116x_read_data16(isp116x); + } else { + /* aligned */ + for (; len > 1; len -= 2) + *dp2++ = isp116x_raw_read_data16(isp116x); + if (len) + *(u8 *) dp2 = 0xff & isp116x_read_data16(isp116x); + } + if (quot == 1 || quot == 2) + isp116x_raw_read_data16(isp116x); +} + +/* Write PTD's and data for scheduled transfers into the fifo ram. + * Fifo must be empty and ready */ +static void pack_fifo(struct isp116x *isp116x, struct usb_device *dev, + unsigned long pipe, struct ptd *ptd, int n, void *data, + int len) +{ + int buflen = n * sizeof(struct ptd) + len; + int i, done; + + DBG("--- pack buffer %p - %d bytes (fifo %d) ---", data, len, buflen); + + isp116x_write_reg16(isp116x, HCuPINT, HCuPINT_AIIEOT); + isp116x_write_reg16(isp116x, HCXFERCTR, buflen); + isp116x_write_addr(isp116x, HCATLPORT | ISP116x_WRITE_OFFSET); + + done = 0; + for (i = 0; i < n; i++) { + DBG("i=%d - done=%d - len=%d", i, done, PTD_GET_LEN(&ptd[i])); + + dump_ptd(&ptd[i]); + isp116x_write_data16(isp116x, ptd[i].count); + isp116x_write_data16(isp116x, ptd[i].mps); + isp116x_write_data16(isp116x, ptd[i].len); + isp116x_write_data16(isp116x, ptd[i].faddr); + + dump_ptd_data(&ptd[i], (__u8 *) data + done, 0); + write_ptddata_to_fifo(isp116x, + (__u8 *) data + done, + PTD_GET_LEN(&ptd[i])); + + done += PTD_GET_LEN(&ptd[i]); + } +} + +/* Read the processed PTD's and data from fifo ram back to URBs' buffers. + * Fifo must be full and done */ +static int unpack_fifo(struct isp116x *isp116x, struct usb_device *dev, + unsigned long pipe, struct ptd *ptd, int n, void *data, + int len) +{ + int buflen = n * sizeof(struct ptd) + len; + int i, done, cc, ret; + + isp116x_write_reg16(isp116x, HCuPINT, HCuPINT_AIIEOT); + isp116x_write_reg16(isp116x, HCXFERCTR, buflen); + isp116x_write_addr(isp116x, HCATLPORT); + + ret = TD_CC_NOERROR; + done = 0; + for (i = 0; i < n; i++) { + DBG("i=%d - done=%d - len=%d", i, done, PTD_GET_LEN(&ptd[i])); + + ptd[i].count = isp116x_read_data16(isp116x); + ptd[i].mps = isp116x_read_data16(isp116x); + ptd[i].len = isp116x_read_data16(isp116x); + ptd[i].faddr = isp116x_read_data16(isp116x); + dump_ptd(&ptd[i]); + + read_ptddata_from_fifo(isp116x, + (__u8 *) data + done, + PTD_GET_LEN(&ptd[i])); + dump_ptd_data(&ptd[i], (__u8 *) data + done, 1); + + done += PTD_GET_LEN(&ptd[i]); + + cc = PTD_GET_CC(&ptd[i]); + if (cc == TD_DATAUNDERRUN) { /* underrun is no error... */ + DBG("allowed data underrun"); + cc = TD_CC_NOERROR; + } + if (cc != TD_CC_NOERROR && ret == TD_CC_NOERROR) + ret = cc; + } + + DBG("--- unpack buffer %p - %d bytes (fifo %d) ---", data, len, buflen); + + return ret; +} + +/* Interrupt handling + */ +static int isp116x_interrupt(struct isp116x *isp116x) +{ + u16 irqstat; + u32 intstat; + int ret = 0; + + isp116x_write_reg16(isp116x, HCuPINTENB, 0); + irqstat = isp116x_read_reg16(isp116x, HCuPINT); + isp116x_write_reg16(isp116x, HCuPINT, irqstat); + DBG(">>>>>> irqstat %x <<<<<<", irqstat); + + if (irqstat & HCuPINT_ATL) { + DBG(">>>>>> HCuPINT_ATL <<<<<<"); + ret = 1; + } + + if (irqstat & HCuPINT_OPR) { + intstat = isp116x_read_reg32(isp116x, HCINTSTAT); + isp116x_write_reg32(isp116x, HCINTSTAT, intstat); + DBG(">>>>>> HCuPINT_OPR %x <<<<<<", intstat); + + if (intstat & HCINT_UE) { + ERR("unrecoverable error, controller disabled"); + + /* FIXME: be optimistic, hope that bug won't repeat + * often. Make some non-interrupt context restart the + * controller. Count and limit the retries though; + * either hardware or software errors can go forever... + */ + isp116x_reset(isp116x); + ret = -1; + return -1; + } + + if (intstat & HCINT_RHSC) { + got_rhsc = 1; + ret = 1; + /* When root hub or any of its ports is going + to come out of suspend, it may take more + than 10ms for status bits to stabilize. */ + wait_ms(20); + } + + if (intstat & HCINT_SO) { + ERR("schedule overrun"); + ret = -1; + } + + irqstat &= ~HCuPINT_OPR; + } + + return ret; +} + +#define PTD_NUM 64 /* it should be enougth... */ +struct ptd ptd[PTD_NUM]; +static inline int max_transfer_len(struct usb_device *dev, unsigned long pipe) +{ + return min(PTD_NUM * usb_maxpacket(dev, pipe), PTD_NUM * 16); +} + +/* Do an USB transfer + */ +static int isp116x_submit_job(struct usb_device *dev, unsigned long pipe, + int dir, void *buffer, int len) +{ + struct isp116x *isp116x = &isp116x_dev; + int type = usb_pipetype(pipe); + int epnum = usb_pipeendpoint(pipe); + int max = usb_maxpacket(dev, pipe); + int dir_out = usb_pipeout(pipe); + int speed_low = usb_pipeslow(pipe); + int i, done, stat, timeout, cc; + int retries = 10; + + DBG("------------------------------------------------"); + dump_msg(dev, pipe, buffer, len, "SUBMIT"); + DBG("------------------------------------------------"); + + if (isp116x->disabled) { + ERR("EPIPE"); + dev->status = USB_ST_CRC_ERR; + return -1; + } + + /* device pulled? Shortcut the action. */ + if (devgone == dev) { + ERR("ENODEV"); + dev->status = USB_ST_CRC_ERR; + return USB_ST_CRC_ERR; + } + + if (!max) { + ERR("pipesize for pipe %lx is zero", pipe); + dev->status = USB_ST_CRC_ERR; + return -1; + } + + if (type == PIPE_ISOCHRONOUS) { + ERR("isochronous transfers not supported"); + dev->status = USB_ST_CRC_ERR; + return -1; + } + + /* FIFO not empty? */ + if (isp116x_read_reg16(isp116x, HCBUFSTAT) & HCBUFSTAT_ATL_FULL) { + ERR("****** FIFO not empty! ******"); + dev->status = USB_ST_BUF_ERR; + return -1; + } + + retry: + isp116x_write_reg32(isp116x, HCINTSTAT, 0xff); + + /* Prepare the PTD data */ + done = 0; + i = 0; + do { + ptd[i].count = PTD_CC_MSK | PTD_ACTIVE_MSK | + PTD_TOGGLE(usb_gettoggle(dev, epnum, dir_out)); + ptd[i].mps = PTD_MPS(max) | PTD_SPD(speed_low) | PTD_EP(epnum); + ptd[i].len = PTD_LEN(max > len - done ? len - done : max) | + PTD_DIR(dir); + ptd[i].faddr = PTD_FA(usb_pipedevice(pipe)); + + usb_dotoggle(dev, epnum, dir_out); + done += PTD_GET_LEN(&ptd[i]); + i++; + if (i >= PTD_NUM) { + ERR("****** Cannot pack buffer! ******"); + dev->status = USB_ST_BUF_ERR; + return -1; + } + } while (done < len); + ptd[i - 1].mps |= PTD_LAST_MSK; + + /* Pack data into FIFO ram */ + pack_fifo(isp116x, dev, pipe, ptd, i, buffer, len); +#ifdef EXTRA_DELAY + wait_ms(EXTRA_DELAY); +#endif + + /* Start the data transfer */ + + /* Allow more time for a BULK device to react - some are slow */ + if (usb_pipetype(pipe) == PIPE_BULK) + timeout = 5000; + else + timeout = 100; + + /* Wait for it to complete */ + for (;;) { + /* Check whether the controller is done */ + stat = isp116x_interrupt(isp116x); + + if (stat < 0) { + dev->status = USB_ST_CRC_ERR; + break; + } + if (stat > 0) + break; + + /* Check the timeout */ + if (--timeout) + udelay(1); + else { + ERR("CTL:TIMEOUT "); + stat = USB_ST_CRC_ERR; + break; + } + } + + /* We got an Root Hub Status Change interrupt */ + if (got_rhsc) { + isp116x_show_regs(isp116x); + + got_rhsc = 0; + + /* Abuse timeout */ + timeout = rh_check_port_status(isp116x); + if (timeout >= 0) { + /* + * FIXME! NOTE! AAAARGH! + * This is potentially dangerous because it assumes + * that only one device is ever plugged in! + */ + devgone = dev; + } + } + + /* Ok, now we can read transfer status */ + + /* FIFO not ready? */ + if (!(isp116x_read_reg16(isp116x, HCBUFSTAT) & HCBUFSTAT_ATL_DONE)) { + ERR("****** FIFO not ready! ******"); + dev->status = USB_ST_BUF_ERR; + return -1; + } + + /* Unpack data from FIFO ram */ + cc = unpack_fifo(isp116x, dev, pipe, ptd, i, buffer, len); + + /* Mmm... sometime we get 0x0f as cc which is a non sense! + * Just retry the transfer... + */ + if (cc == 0x0f && retries-- > 0) { + usb_dotoggle(dev, epnum, dir_out); + goto retry; + } + + if (cc != TD_CC_NOERROR) { + DBG("****** completition code error %x ******", cc); + switch (cc) { + case TD_CC_BITSTUFFING: + dev->status = USB_ST_BIT_ERR; + break; + case TD_CC_STALL: + dev->status = USB_ST_STALLED; + break; + case TD_BUFFEROVERRUN: + case TD_BUFFERUNDERRUN: + dev->status = USB_ST_BUF_ERR; + break; + default: + dev->status = USB_ST_CRC_ERR; + } + return -cc; + } + + dump_msg(dev, pipe, buffer, len, "SUBMIT(ret)"); + + dev->status = 0; + return done; +} + +/* Adapted from au1x00_usb_ohci.c + */ +static int isp116x_submit_rh_msg(struct usb_device *dev, unsigned long pipe, + void *buffer, int transfer_len, + struct devrequest *cmd) +{ + struct isp116x *isp116x = &isp116x_dev; + u32 tmp = 0; + + int leni = transfer_len; + int len = 0; + int stat = 0; + u32 datab[4]; + u8 *data_buf = (u8 *) datab; + u16 bmRType_bReq; + u16 wValue; + u16 wIndex; + u16 wLength; + + if ((pipe & PIPE_INTERRUPT) == PIPE_INTERRUPT) { + INFO("Root-Hub submit IRQ: NOT implemented"); + return 0; + } + + bmRType_bReq = cmd->requesttype | (cmd->request << 8); + wValue = swap_16(cmd->value); + wIndex = swap_16(cmd->index); + wLength = swap_16(cmd->length); + + DBG("--- HUB ----------------------------------------"); + DBG("submit rh urb, req=%x val=%#x index=%#x len=%d", + bmRType_bReq, wValue, wIndex, wLength); + dump_msg(dev, pipe, buffer, transfer_len, "RH"); + DBG("------------------------------------------------"); + + switch (bmRType_bReq) { + case RH_GET_STATUS: + DBG("RH_GET_STATUS"); + + *(__u16 *) data_buf = swap_16(1); + len = 2; + break; + + case RH_GET_STATUS | RH_INTERFACE: + DBG("RH_GET_STATUS | RH_INTERFACE"); + + *(__u16 *) data_buf = swap_16(0); + len = 2; + break; + + case RH_GET_STATUS | RH_ENDPOINT: + DBG("RH_GET_STATUS | RH_ENDPOINT"); + + *(__u16 *) data_buf = swap_16(0); + len = 2; + break; + + case RH_GET_STATUS | RH_CLASS: + DBG("RH_GET_STATUS | RH_CLASS"); + + tmp = isp116x_read_reg32(isp116x, HCRHSTATUS); + + *(__u32 *) data_buf = swap_32(tmp & ~(RH_HS_CRWE | RH_HS_DRWE)); + len = 4; + break; + + case RH_GET_STATUS | RH_OTHER | RH_CLASS: + DBG("RH_GET_STATUS | RH_OTHER | RH_CLASS"); + + tmp = isp116x_read_reg32(isp116x, HCRHPORT1 + wIndex - 1); + *(__u32 *) data_buf = swap_32(tmp); + isp116x_show_regs(isp116x); + len = 4; + break; + + case RH_CLEAR_FEATURE | RH_ENDPOINT: + DBG("RH_CLEAR_FEATURE | RH_ENDPOINT"); + + switch (wValue) { + case RH_ENDPOINT_STALL: + DBG("C_HUB_ENDPOINT_STALL"); + len = 0; + break; + } + break; + + case RH_CLEAR_FEATURE | RH_CLASS: + DBG("RH_CLEAR_FEATURE | RH_CLASS"); + + switch (wValue) { + case RH_C_HUB_LOCAL_POWER: + DBG("C_HUB_LOCAL_POWER"); + len = 0; + break; + + case RH_C_HUB_OVER_CURRENT: + DBG("C_HUB_OVER_CURRENT"); + isp116x_write_reg32(isp116x, HCRHSTATUS, RH_HS_OCIC); + len = 0; + break; + } + break; + + case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS: + DBG("RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS"); + + switch (wValue) { + case RH_PORT_ENABLE: + isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, + RH_PS_CCS); + len = 0; + break; + + case RH_PORT_SUSPEND: + isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, + RH_PS_POCI); + len = 0; + break; + + case RH_PORT_POWER: + isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, + RH_PS_LSDA); + len = 0; + break; + + case RH_C_PORT_CONNECTION: + isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, + RH_PS_CSC); + len = 0; + break; + + case RH_C_PORT_ENABLE: + isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, + RH_PS_PESC); + len = 0; + break; + + case RH_C_PORT_SUSPEND: + isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, + RH_PS_PSSC); + len = 0; + break; + + case RH_C_PORT_OVER_CURRENT: + isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, + RH_PS_POCI); + len = 0; + break; + + case RH_C_PORT_RESET: + isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, + RH_PS_PRSC); + len = 0; + break; + + default: + ERR("invalid wValue"); + stat = USB_ST_STALLED; + } + + isp116x_show_regs(isp116x); + + break; + + case RH_SET_FEATURE | RH_OTHER | RH_CLASS: + DBG("RH_SET_FEATURE | RH_OTHER | RH_CLASS"); + + switch (wValue) { + case RH_PORT_SUSPEND: + isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, + RH_PS_PSS); + len = 0; + break; + + case RH_PORT_RESET: + /* Spin until any current reset finishes */ + while (1) { + tmp = + isp116x_read_reg32(isp116x, + HCRHPORT1 + wIndex - 1); + if (!(tmp & RH_PS_PRS)) + break; + wait_ms(1); + } + isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, + RH_PS_PRS); + wait_ms(10); + + len = 0; + break; + + case RH_PORT_POWER: + isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, + RH_PS_PPS); + len = 0; + break; + + case RH_PORT_ENABLE: + isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, + RH_PS_PES); + len = 0; + break; + + default: + ERR("invalid wValue"); + stat = USB_ST_STALLED; + } + + isp116x_show_regs(isp116x); + + break; + + case RH_SET_ADDRESS: + DBG("RH_SET_ADDRESS"); + + rh_devnum = wValue; + len = 0; + break; + + case RH_GET_DESCRIPTOR: + DBG("RH_GET_DESCRIPTOR: %x, %d", wValue, wLength); + + switch (wValue) { + case (USB_DT_DEVICE << 8): /* device descriptor */ + len = min_t(unsigned int, + leni, min_t(unsigned int, + sizeof(root_hub_dev_des), + wLength)); + data_buf = root_hub_dev_des; + break; + + case (USB_DT_CONFIG << 8): /* configuration descriptor */ + len = min_t(unsigned int, + leni, min_t(unsigned int, + sizeof(root_hub_config_des), + wLength)); + data_buf = root_hub_config_des; + break; + + case ((USB_DT_STRING << 8) | 0x00): /* string 0 descriptors */ + len = min_t(unsigned int, + leni, min_t(unsigned int, + sizeof(root_hub_str_index0), + wLength)); + data_buf = root_hub_str_index0; + break; + + case ((USB_DT_STRING << 8) | 0x01): /* string 1 descriptors */ + len = min_t(unsigned int, + leni, min_t(unsigned int, + sizeof(root_hub_str_index1), + wLength)); + data_buf = root_hub_str_index1; + break; + + default: + ERR("invalid wValue"); + stat = USB_ST_STALLED; + } + + break; + + case RH_GET_DESCRIPTOR | RH_CLASS: + DBG("RH_GET_DESCRIPTOR | RH_CLASS"); + + tmp = isp116x_read_reg32(isp116x, HCRHDESCA); + + data_buf[0] = 0x09; /* min length; */ + data_buf[1] = 0x29; + data_buf[2] = tmp & RH_A_NDP; + data_buf[3] = 0; + if (tmp & RH_A_PSM) /* per-port power switching? */ + data_buf[3] |= 0x01; + if (tmp & RH_A_NOCP) /* no overcurrent reporting? */ + data_buf[3] |= 0x10; + else if (tmp & RH_A_OCPM) /* per-port overcurrent rep? */ + data_buf[3] |= 0x08; + + /* Corresponds to data_buf[4-7] */ + datab[1] = 0; + data_buf[5] = (tmp & RH_A_POTPGT) >> 24; + + tmp = isp116x_read_reg32(isp116x, HCRHDESCB); + + data_buf[7] = tmp & RH_B_DR; + if (data_buf[2] < 7) + data_buf[8] = 0xff; + else { + data_buf[0] += 2; + data_buf[8] = (tmp & RH_B_DR) >> 8; + data_buf[10] = data_buf[9] = 0xff; + } + + len = min_t(unsigned int, leni, + min_t(unsigned int, data_buf[0], wLength)); + break; + + case RH_GET_CONFIGURATION: + DBG("RH_GET_CONFIGURATION"); + + *(__u8 *) data_buf = 0x01; + len = 1; + break; + + case RH_SET_CONFIGURATION: + DBG("RH_SET_CONFIGURATION"); + + isp116x_write_reg32(isp116x, HCRHSTATUS, RH_HS_LPSC); + len = 0; + break; + + default: + ERR("*** *** *** unsupported root hub command *** *** ***"); + stat = USB_ST_STALLED; + } + + len = min_t(int, len, leni); + if (buffer != data_buf) + memcpy(buffer, data_buf, len); + + dev->act_len = len; + dev->status = stat; + DBG("dev act_len %d, status %d", dev->act_len, dev->status); + + dump_msg(dev, pipe, buffer, transfer_len, "RH(ret)"); + + return stat; +} + +/* --- Transfer functions -------------------------------------------------- */ + +int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int len, int interval) +{ + DBG("dev=%p pipe=%#lx buf=%p size=%d int=%d", + dev, pipe, buffer, len, interval); + + return -1; +} + +int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int len, struct devrequest *setup) +{ + int devnum = usb_pipedevice(pipe); + int epnum = usb_pipeendpoint(pipe); + int max = max_transfer_len(dev, pipe); + int dir_in = usb_pipein(pipe); + int done, ret; + + /* Control message is for the HUB? */ + if (devnum == rh_devnum) + return isp116x_submit_rh_msg(dev, pipe, buffer, len, setup); + + /* Ok, no HUB message so send the message to the device */ + + /* Setup phase */ + DBG("--- SETUP PHASE --------------------------------"); + usb_settoggle(dev, epnum, 1, 0); + ret = isp116x_submit_job(dev, pipe, + PTD_DIR_SETUP, + setup, sizeof(struct devrequest)); + if (ret < 0) { + DBG("control setup phase error (ret = %d", ret); + return -1; + } + + /* Data phase */ + DBG("--- DATA PHASE ---------------------------------"); + done = 0; + usb_settoggle(dev, epnum, !dir_in, 1); + while (done < len) { + ret = isp116x_submit_job(dev, pipe, + dir_in ? PTD_DIR_IN : PTD_DIR_OUT, + (__u8 *) buffer + done, + max > len - done ? len - done : max); + if (ret < 0) { + DBG("control data phase error (ret = %d)", ret); + return -1; + } + done += ret; + + if (dir_in && ret < max) /* short packet */ + break; + } + + /* Status phase */ + DBG("--- STATUS PHASE -------------------------------"); + usb_settoggle(dev, epnum, !dir_in, 1); + ret = isp116x_submit_job(dev, pipe, + !dir_in ? PTD_DIR_IN : PTD_DIR_OUT, NULL, 0); + if (ret < 0) { + DBG("control status phase error (ret = %d", ret); + return -1; + } + + dev->act_len = done; + + dump_msg(dev, pipe, buffer, len, "DEV(ret)"); + + return done; +} + +int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int len) +{ + int dir_out = usb_pipeout(pipe); + int max = max_transfer_len(dev, pipe); + int done, ret; + + DBG("--- BULK ---------------------------------------"); + DBG("dev=%ld pipe=%ld buf=%p size=%d dir_out=%d", + usb_pipedevice(pipe), usb_pipeendpoint(pipe), buffer, len, dir_out); + + done = 0; + while (done < len) { + ret = isp116x_submit_job(dev, pipe, + !dir_out ? PTD_DIR_IN : PTD_DIR_OUT, + (__u8 *) buffer + done, + max > len - done ? len - done : max); + if (ret < 0) { + DBG("error on bulk message (ret = %d)", ret); + return -1; + } + + done += ret; + + if (!dir_out && ret < max) /* short packet */ + break; + } + + dev->act_len = done; + + return 0; +} + +/* --- Basic functions ----------------------------------------------------- */ + +static int isp116x_sw_reset(struct isp116x *isp116x) +{ + int retries = 15; + int ret = 0; + + DBG(""); + + isp116x->disabled = 1; + + isp116x_write_reg16(isp116x, HCSWRES, HCSWRES_MAGIC); + isp116x_write_reg32(isp116x, HCCMDSTAT, HCCMDSTAT_HCR); + while (--retries) { + /* It usually resets within 1 ms */ + wait_ms(1); + if (!(isp116x_read_reg32(isp116x, HCCMDSTAT) & HCCMDSTAT_HCR)) + break; + } + if (!retries) { + ERR("software reset timeout"); + ret = -1; + } + return ret; +} + +static int isp116x_reset(struct isp116x *isp116x) +{ + unsigned long t; + u16 clkrdy = 0; + int ret, timeout = 15 /* ms */ ; + + DBG(""); + + ret = isp116x_sw_reset(isp116x); + if (ret) + return ret; + + for (t = 0; t < timeout; t++) { + clkrdy = isp116x_read_reg16(isp116x, HCuPINT) & HCuPINT_CLKRDY; + if (clkrdy) + break; + wait_ms(1); + } + if (!clkrdy) { + ERR("clock not ready after %dms", timeout); + /* After sw_reset the clock won't report to be ready, if + H_WAKEUP pin is high. */ + ERR("please make sure that the H_WAKEUP pin is pulled low!"); + ret = -1; + } + return ret; +} + +static void isp116x_stop(struct isp116x *isp116x) +{ + u32 val; + + DBG(""); + + isp116x_write_reg16(isp116x, HCuPINTENB, 0); + + /* Switch off ports' power, some devices don't come up + after next 'start' without this */ + val = isp116x_read_reg32(isp116x, HCRHDESCA); + val &= ~(RH_A_NPS | RH_A_PSM); + isp116x_write_reg32(isp116x, HCRHDESCA, val); + isp116x_write_reg32(isp116x, HCRHSTATUS, RH_HS_LPS); + + isp116x_sw_reset(isp116x); +} + +/* + * Configure the chip. The chip must be successfully reset by now. + */ +static int isp116x_start(struct isp116x *isp116x) +{ + struct isp116x_platform_data *board = isp116x->board; + u32 val; + + DBG(""); + + /* Clear interrupt status and disable all interrupt sources */ + isp116x_write_reg16(isp116x, HCuPINT, 0xff); + isp116x_write_reg16(isp116x, HCuPINTENB, 0); + + isp116x_write_reg16(isp116x, HCITLBUFLEN, ISP116x_ITL_BUFSIZE); + isp116x_write_reg16(isp116x, HCATLBUFLEN, ISP116x_ATL_BUFSIZE); + + /* Hardware configuration */ + val = HCHWCFG_DBWIDTH(1); + if (board->sel15Kres) + val |= HCHWCFG_15KRSEL; + /* Remote wakeup won't work without working clock */ + if (board->remote_wakeup_enable) + val |= HCHWCFG_CLKNOTSTOP; + if (board->oc_enable) + val |= HCHWCFG_ANALOG_OC; + isp116x_write_reg16(isp116x, HCHWCFG, val); + + /* --- Root hub configuration */ + val = (25 << 24) & RH_A_POTPGT; + /* AN10003_1.pdf recommends RH_A_NPS (no power switching) to + be always set. Yet, instead, we request individual port + power switching. */ + val |= RH_A_PSM; + /* Report overcurrent per port */ + val |= RH_A_OCPM; + isp116x_write_reg32(isp116x, HCRHDESCA, val); + isp116x->rhdesca = isp116x_read_reg32(isp116x, HCRHDESCA); + + val = RH_B_PPCM; + isp116x_write_reg32(isp116x, HCRHDESCB, val); + isp116x->rhdescb = isp116x_read_reg32(isp116x, HCRHDESCB); + + val = 0; + if (board->remote_wakeup_enable) + val |= RH_HS_DRWE; + isp116x_write_reg32(isp116x, HCRHSTATUS, val); + isp116x->rhstatus = isp116x_read_reg32(isp116x, HCRHSTATUS); + + isp116x_write_reg32(isp116x, HCFMINTVL, 0x27782edf); + + /* Go operational */ + val = HCCONTROL_USB_OPER; + if (board->remote_wakeup_enable) + val |= HCCONTROL_RWE; + isp116x_write_reg32(isp116x, HCCONTROL, val); + + /* Disable ports to avoid race in device enumeration */ + isp116x_write_reg32(isp116x, HCRHPORT1, RH_PS_CCS); + isp116x_write_reg32(isp116x, HCRHPORT2, RH_PS_CCS); + + isp116x_show_regs(isp116x); + + isp116x->disabled = 0; + + return 0; +} + +/* --- Init functions ------------------------------------------------------ */ + +int isp116x_check_id(struct isp116x *isp116x) +{ + int val; + + val = isp116x_read_reg16(isp116x, HCCHIPID); + if ((val & HCCHIPID_MASK) != HCCHIPID_MAGIC) { + ERR("invalid chip ID %04x", val); + return -1; + } + + return 0; +} + +int usb_lowlevel_init(void) +{ + struct isp116x *isp116x = &isp116x_dev; + + DBG(""); + + /* Init device registers addr */ + isp116x->addr_reg = (u16 *) ISP116X_HCD_ADDR; + isp116x->data_reg = (u16 *) ISP116X_HCD_DATA; + + /* Setup specific board settings */ +#ifdef ISP116X_HCD_SEL15kRES + isp116x_board.sel15Kres = 1; +#endif +#ifdef ISP116X_HCD_OC_ENABLE + isp116x_board.oc_enable = 1; +#endif +#ifdef ISP116X_HCD_REMOTE_WAKEUP_ENABLE + isp116x_board.remote_wakeup_enable = 1; +#endif + isp116x->board = &isp116x_board; + + /* Try to get ISP116x silicon chip ID */ + if (isp116x_check_id(isp116x) < 0) + return -1; + + isp116x->disabled = 1; + isp116x->sleeping = 0; + + isp116x_reset(isp116x); + isp116x_start(isp116x); + + return 0; +} + +int usb_lowlevel_stop(void) +{ + struct isp116x *isp116x = &isp116x_dev; + + DBG(""); + + if (!isp116x->disabled) + isp116x_stop(isp116x); + + return 0; +} + +#endif /* CONFIG_USB_ISP116X_HCD */ diff --git a/drivers/isp116x.h b/drivers/isp116x.h new file mode 100644 index 000000000..a3ce3b582 --- /dev/null +++ b/drivers/isp116x.h @@ -0,0 +1,489 @@ +/* + * ISP116x register declarations and HCD data structures + * + * Copyright (C) 2007 Rodolfo Giometti + * Copyright (C) 2007 Eurotech S.p.A. + * Copyright (C) 2005 Olav Kongas + * Portions: + * Copyright (C) 2004 Lothar Wassmann + * Copyright (C) 2004 Psion Teklogix + * Copyright (C) 2004 David Brownell + * + * 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 + */ + +#ifdef DEBUG +#define DBG(fmt, args...) \ + printf("isp116x: %s: " fmt "\n" , __FUNCTION__ , ## args) +#else +#define DBG(fmt, args...) do {} while (0) +#endif + +#ifdef VERBOSE +# define VDBG DBG +#else +# define VDBG(fmt, args...) do {} while (0) +#endif + +#define ERR(fmt, args...) \ + printf("isp116x: %s: " fmt "\n" , __FUNCTION__ , ## args) +#define WARN(fmt, args...) \ + printf("isp116x: %s: " fmt "\n" , __FUNCTION__ , ## args) +#define INFO(fmt, args...) \ + printf("isp116x: " fmt "\n" , ## args) + +/* ------------------------------------------------------------------------- */ + +/* us of 1ms frame */ +#define MAX_LOAD_LIMIT 850 + +/* Full speed: max # of bytes to transfer for a single urb + at a time must be < 1024 && must be multiple of 64. + 832 allows transfering 4kiB within 5 frames. */ +#define MAX_TRANSFER_SIZE_FULLSPEED 832 + +/* Low speed: there is no reason to schedule in very big + chunks; often the requested long transfers are for + string descriptors containing short strings. */ +#define MAX_TRANSFER_SIZE_LOWSPEED 64 + +/* Bytetime (us), a rough indication of how much time it + would take to transfer a byte of useful data over USB */ +#define BYTE_TIME_FULLSPEED 1 +#define BYTE_TIME_LOWSPEED 20 + +/* Buffer sizes */ +#define ISP116x_BUF_SIZE 4096 +#define ISP116x_ITL_BUFSIZE 0 +#define ISP116x_ATL_BUFSIZE ((ISP116x_BUF_SIZE) - 2*(ISP116x_ITL_BUFSIZE)) + +#define ISP116x_WRITE_OFFSET 0x80 + +/* --- ISP116x registers/bits ---------------------------------------------- */ + +#define HCREVISION 0x00 +#define HCCONTROL 0x01 +#define HCCONTROL_HCFS (3 << 6) /* host controller + functional state */ +#define HCCONTROL_USB_RESET (0 << 6) +#define HCCONTROL_USB_RESUME (1 << 6) +#define HCCONTROL_USB_OPER (2 << 6) +#define HCCONTROL_USB_SUSPEND (3 << 6) +#define HCCONTROL_RWC (1 << 9) /* remote wakeup connected */ +#define HCCONTROL_RWE (1 << 10) /* remote wakeup enable */ +#define HCCMDSTAT 0x02 +#define HCCMDSTAT_HCR (1 << 0) /* host controller reset */ +#define HCCMDSTAT_SOC (3 << 16) /* scheduling overrun count */ +#define HCINTSTAT 0x03 +#define HCINT_SO (1 << 0) /* scheduling overrun */ +#define HCINT_WDH (1 << 1) /* writeback of done_head */ +#define HCINT_SF (1 << 2) /* start frame */ +#define HCINT_RD (1 << 3) /* resume detect */ +#define HCINT_UE (1 << 4) /* unrecoverable error */ +#define HCINT_FNO (1 << 5) /* frame number overflow */ +#define HCINT_RHSC (1 << 6) /* root hub status change */ +#define HCINT_OC (1 << 30) /* ownership change */ +#define HCINT_MIE (1 << 31) /* master interrupt enable */ +#define HCINTENB 0x04 +#define HCINTDIS 0x05 +#define HCFMINTVL 0x0d +#define HCFMREM 0x0e +#define HCFMNUM 0x0f +#define HCLSTHRESH 0x11 +#define HCRHDESCA 0x12 +#define RH_A_NDP (0x3 << 0) /* # downstream ports */ +#define RH_A_PSM (1 << 8) /* power switching mode */ +#define RH_A_NPS (1 << 9) /* no power switching */ +#define RH_A_DT (1 << 10) /* device type (mbz) */ +#define RH_A_OCPM (1 << 11) /* overcurrent protection + mode */ +#define RH_A_NOCP (1 << 12) /* no overcurrent protection */ +#define RH_A_POTPGT (0xff << 24) /* power on -> power good + time */ +#define HCRHDESCB 0x13 +#define RH_B_DR (0xffff << 0) /* device removable flags */ +#define RH_B_PPCM (0xffff << 16) /* port power control mask */ +#define HCRHSTATUS 0x14 +#define RH_HS_LPS (1 << 0) /* local power status */ +#define RH_HS_OCI (1 << 1) /* over current indicator */ +#define RH_HS_DRWE (1 << 15) /* device remote wakeup + enable */ +#define RH_HS_LPSC (1 << 16) /* local power status change */ +#define RH_HS_OCIC (1 << 17) /* over current indicator + change */ +#define RH_HS_CRWE (1 << 31) /* clear remote wakeup + enable */ +#define HCRHPORT1 0x15 +#define RH_PS_CCS (1 << 0) /* current connect status */ +#define RH_PS_PES (1 << 1) /* port enable status */ +#define RH_PS_PSS (1 << 2) /* port suspend status */ +#define RH_PS_POCI (1 << 3) /* port over current + indicator */ +#define RH_PS_PRS (1 << 4) /* port reset status */ +#define RH_PS_PPS (1 << 8) /* port power status */ +#define RH_PS_LSDA (1 << 9) /* low speed device attached */ +#define RH_PS_CSC (1 << 16) /* connect status change */ +#define RH_PS_PESC (1 << 17) /* port enable status change */ +#define RH_PS_PSSC (1 << 18) /* port suspend status + change */ +#define RH_PS_OCIC (1 << 19) /* over current indicator + change */ +#define RH_PS_PRSC (1 << 20) /* port reset status change */ +#define HCRHPORT_CLRMASK (0x1f << 16) +#define HCRHPORT2 0x16 +#define HCHWCFG 0x20 +#define HCHWCFG_15KRSEL (1 << 12) +#define HCHWCFG_CLKNOTSTOP (1 << 11) +#define HCHWCFG_ANALOG_OC (1 << 10) +#define HCHWCFG_DACK_MODE (1 << 8) +#define HCHWCFG_EOT_POL (1 << 7) +#define HCHWCFG_DACK_POL (1 << 6) +#define HCHWCFG_DREQ_POL (1 << 5) +#define HCHWCFG_DBWIDTH_MASK (0x03 << 3) +#define HCHWCFG_DBWIDTH(n) (((n) << 3) & HCHWCFG_DBWIDTH_MASK) +#define HCHWCFG_INT_POL (1 << 2) +#define HCHWCFG_INT_TRIGGER (1 << 1) +#define HCHWCFG_INT_ENABLE (1 << 0) +#define HCDMACFG 0x21 +#define HCDMACFG_BURST_LEN_MASK (0x03 << 5) +#define HCDMACFG_BURST_LEN(n) (((n) << 5) & HCDMACFG_BURST_LEN_MASK) +#define HCDMACFG_BURST_LEN_1 HCDMACFG_BURST_LEN(0) +#define HCDMACFG_BURST_LEN_4 HCDMACFG_BURST_LEN(1) +#define HCDMACFG_BURST_LEN_8 HCDMACFG_BURST_LEN(2) +#define HCDMACFG_DMA_ENABLE (1 << 4) +#define HCDMACFG_BUF_TYPE_MASK (0x07 << 1) +#define HCDMACFG_CTR_SEL (1 << 2) +#define HCDMACFG_ITLATL_SEL (1 << 1) +#define HCDMACFG_DMA_RW_SELECT (1 << 0) +#define HCXFERCTR 0x22 +#define HCuPINT 0x24 +#define HCuPINT_SOF (1 << 0) +#define HCuPINT_ATL (1 << 1) +#define HCuPINT_AIIEOT (1 << 2) +#define HCuPINT_OPR (1 << 4) +#define HCuPINT_SUSP (1 << 5) +#define HCuPINT_CLKRDY (1 << 6) +#define HCuPINTENB 0x25 +#define HCCHIPID 0x27 +#define HCCHIPID_MASK 0xff00 +#define HCCHIPID_MAGIC 0x6100 +#define HCSCRATCH 0x28 +#define HCSWRES 0x29 +#define HCSWRES_MAGIC 0x00f6 +#define HCITLBUFLEN 0x2a +#define HCATLBUFLEN 0x2b +#define HCBUFSTAT 0x2c +#define HCBUFSTAT_ITL0_FULL (1 << 0) +#define HCBUFSTAT_ITL1_FULL (1 << 1) +#define HCBUFSTAT_ATL_FULL (1 << 2) +#define HCBUFSTAT_ITL0_DONE (1 << 3) +#define HCBUFSTAT_ITL1_DONE (1 << 4) +#define HCBUFSTAT_ATL_DONE (1 << 5) +#define HCRDITL0LEN 0x2d +#define HCRDITL1LEN 0x2e +#define HCITLPORT 0x40 +#define HCATLPORT 0x41 + +/* PTD accessor macros. */ +#define PTD_GET_COUNT(p) (((p)->count & PTD_COUNT_MSK) >> 0) +#define PTD_COUNT(v) (((v) << 0) & PTD_COUNT_MSK) +#define PTD_GET_TOGGLE(p) (((p)->count & PTD_TOGGLE_MSK) >> 10) +#define PTD_TOGGLE(v) (((v) << 10) & PTD_TOGGLE_MSK) +#define PTD_GET_ACTIVE(p) (((p)->count & PTD_ACTIVE_MSK) >> 11) +#define PTD_ACTIVE(v) (((v) << 11) & PTD_ACTIVE_MSK) +#define PTD_GET_CC(p) (((p)->count & PTD_CC_MSK) >> 12) +#define PTD_CC(v) (((v) << 12) & PTD_CC_MSK) +#define PTD_GET_MPS(p) (((p)->mps & PTD_MPS_MSK) >> 0) +#define PTD_MPS(v) (((v) << 0) & PTD_MPS_MSK) +#define PTD_GET_SPD(p) (((p)->mps & PTD_SPD_MSK) >> 10) +#define PTD_SPD(v) (((v) << 10) & PTD_SPD_MSK) +#define PTD_GET_LAST(p) (((p)->mps & PTD_LAST_MSK) >> 11) +#define PTD_LAST(v) (((v) << 11) & PTD_LAST_MSK) +#define PTD_GET_EP(p) (((p)->mps & PTD_EP_MSK) >> 12) +#define PTD_EP(v) (((v) << 12) & PTD_EP_MSK) +#define PTD_GET_LEN(p) (((p)->len & PTD_LEN_MSK) >> 0) +#define PTD_LEN(v) (((v) << 0) & PTD_LEN_MSK) +#define PTD_GET_DIR(p) (((p)->len & PTD_DIR_MSK) >> 10) +#define PTD_DIR(v) (((v) << 10) & PTD_DIR_MSK) +#define PTD_GET_B5_5(p) (((p)->len & PTD_B5_5_MSK) >> 13) +#define PTD_B5_5(v) (((v) << 13) & PTD_B5_5_MSK) +#define PTD_GET_FA(p) (((p)->faddr & PTD_FA_MSK) >> 0) +#define PTD_FA(v) (((v) << 0) & PTD_FA_MSK) +#define PTD_GET_FMT(p) (((p)->faddr & PTD_FMT_MSK) >> 7) +#define PTD_FMT(v) (((v) << 7) & PTD_FMT_MSK) + +/* Hardware transfer status codes -- CC from ptd->count */ +#define TD_CC_NOERROR 0x00 +#define TD_CC_CRC 0x01 +#define TD_CC_BITSTUFFING 0x02 +#define TD_CC_DATATOGGLEM 0x03 +#define TD_CC_STALL 0x04 +#define TD_DEVNOTRESP 0x05 +#define TD_PIDCHECKFAIL 0x06 +#define TD_UNEXPECTEDPID 0x07 +#define TD_DATAOVERRUN 0x08 +#define TD_DATAUNDERRUN 0x09 + /* 0x0A, 0x0B reserved for hardware */ +#define TD_BUFFEROVERRUN 0x0C +#define TD_BUFFERUNDERRUN 0x0D + /* 0x0E, 0x0F reserved for HCD */ +#define TD_NOTACCESSED 0x0F + +/* ------------------------------------------------------------------------- */ + +#define LOG2_PERIODIC_SIZE 5 /* arbitrary; this matches OHCI */ +#define PERIODIC_SIZE (1 << LOG2_PERIODIC_SIZE) + +/* Philips transfer descriptor */ +struct ptd { + u16 count; +#define PTD_COUNT_MSK (0x3ff << 0) +#define PTD_TOGGLE_MSK (1 << 10) +#define PTD_ACTIVE_MSK (1 << 11) +#define PTD_CC_MSK (0xf << 12) + u16 mps; +#define PTD_MPS_MSK (0x3ff << 0) +#define PTD_SPD_MSK (1 << 10) +#define PTD_LAST_MSK (1 << 11) +#define PTD_EP_MSK (0xf << 12) + u16 len; +#define PTD_LEN_MSK (0x3ff << 0) +#define PTD_DIR_MSK (3 << 10) +#define PTD_DIR_SETUP (0) +#define PTD_DIR_OUT (1) +#define PTD_DIR_IN (2) +#define PTD_B5_5_MSK (1 << 13) + u16 faddr; +#define PTD_FA_MSK (0x7f << 0) +#define PTD_FMT_MSK (1 << 7) +} __attribute__ ((packed, aligned(2))); + +struct isp116x_ep { + struct usb_device *udev; + struct ptd ptd; + + u8 maxpacket; + u8 epnum; + u8 nextpid; + + u16 length; /* of current packet */ + unsigned char *data; /* to databuf */ + + u16 error_count; +}; + +/* URB struct */ +#define N_URB_TD 48 +#define URB_DEL 1 +typedef struct { + struct isp116x_ep *ed; + void *transfer_buffer; /* (in) associated data buffer */ + int actual_length; /* (return) actual transfer length */ + unsigned long pipe; /* (in) pipe information */ +#if 0 + int state; +#endif +} urb_priv_t; + +struct isp116x_platform_data { + /* Enable internal resistors on downstream ports */ + unsigned sel15Kres:1; + /* On-chip overcurrent detection */ + unsigned oc_enable:1; + /* Enable wakeup by devices on usb bus (e.g. wakeup + by attachment/detachment or by device activity + such as moving a mouse). When chosen, this option + prevents stopping internal clock, increasing + thereby power consumption in suspended state. */ + unsigned remote_wakeup_enable:1; +}; + +struct isp116x { + u16 *addr_reg; + u16 *data_reg; + + struct isp116x_platform_data *board; + + struct dentry *dentry; + unsigned long stat1, stat2, stat4, stat8, stat16; + + /* Status flags */ + unsigned disabled:1; + unsigned sleeping:1; + + /* Root hub registers */ + u32 rhdesca; + u32 rhdescb; + u32 rhstatus; + u32 rhport[2]; + + /* Schedule for the current frame */ + struct isp116x_ep *atl_active; + int atl_buflen; + int atl_bufshrt; + int atl_last_dir; + int atl_finishing; +}; + +/* ------------------------------------------------- */ + +/* Inter-io delay (ns). The chip is picky about access timings; it + * expects at least: + * 150ns delay between consecutive accesses to DATA_REG, + * 300ns delay between access to ADDR_REG and DATA_REG + * OE, WE MUST NOT be changed during these intervals + */ +#if defined(UDELAY) +#define isp116x_delay(h,d) udelay(d) +#else +#define isp116x_delay(h,d) do {} while (0) +#endif + +static inline void isp116x_write_addr(struct isp116x *isp116x, unsigned reg) +{ + writew(reg & 0xff, isp116x->addr_reg); + isp116x_delay(isp116x, UDELAY); +} + +static inline void isp116x_write_data16(struct isp116x *isp116x, u16 val) +{ + writew(val, isp116x->data_reg); + isp116x_delay(isp116x, UDELAY); +} + +static inline void isp116x_raw_write_data16(struct isp116x *isp116x, u16 val) +{ + __raw_writew(val, isp116x->data_reg); + isp116x_delay(isp116x, UDELAY); +} + +static inline u16 isp116x_read_data16(struct isp116x *isp116x) +{ + u16 val; + + val = readw(isp116x->data_reg); + isp116x_delay(isp116x, UDELAY); + return val; +} + +static inline u16 isp116x_raw_read_data16(struct isp116x *isp116x) +{ + u16 val; + + val = __raw_readw(isp116x->data_reg); + isp116x_delay(isp116x, UDELAY); + return val; +} + +static inline void isp116x_write_data32(struct isp116x *isp116x, u32 val) +{ + writew(val & 0xffff, isp116x->data_reg); + isp116x_delay(isp116x, UDELAY); + writew(val >> 16, isp116x->data_reg); + isp116x_delay(isp116x, UDELAY); +} + +static inline u32 isp116x_read_data32(struct isp116x *isp116x) +{ + u32 val; + + val = (u32) readw(isp116x->data_reg); + isp116x_delay(isp116x, UDELAY); + val |= ((u32) readw(isp116x->data_reg)) << 16; + isp116x_delay(isp116x, UDELAY); + return val; +} + +/* Let's keep register access functions out of line. Hint: + we wait at least 150 ns at every access. +*/ +static u16 isp116x_read_reg16(struct isp116x *isp116x, unsigned reg) +{ + isp116x_write_addr(isp116x, reg); + return isp116x_read_data16(isp116x); +} + +static u32 isp116x_read_reg32(struct isp116x *isp116x, unsigned reg) +{ + isp116x_write_addr(isp116x, reg); + return isp116x_read_data32(isp116x); +} + +static void isp116x_write_reg16(struct isp116x *isp116x, unsigned reg, + unsigned val) +{ + isp116x_write_addr(isp116x, reg | ISP116x_WRITE_OFFSET); + isp116x_write_data16(isp116x, (u16) (val & 0xffff)); +} + +static void isp116x_write_reg32(struct isp116x *isp116x, unsigned reg, + unsigned val) +{ + isp116x_write_addr(isp116x, reg | ISP116x_WRITE_OFFSET); + isp116x_write_data32(isp116x, (u32) val); +} + +/* --- USB HUB constants (not OHCI-specific; see hub.h) -------------------- */ + +/* destination of request */ +#define RH_INTERFACE 0x01 +#define RH_ENDPOINT 0x02 +#define RH_OTHER 0x03 + +#define RH_CLASS 0x20 +#define RH_VENDOR 0x40 + +/* Requests: bRequest << 8 | bmRequestType */ +#define RH_GET_STATUS 0x0080 +#define RH_CLEAR_FEATURE 0x0100 +#define RH_SET_FEATURE 0x0300 +#define RH_SET_ADDRESS 0x0500 +#define RH_GET_DESCRIPTOR 0x0680 +#define RH_SET_DESCRIPTOR 0x0700 +#define RH_GET_CONFIGURATION 0x0880 +#define RH_SET_CONFIGURATION 0x0900 +#define RH_GET_STATE 0x0280 +#define RH_GET_INTERFACE 0x0A80 +#define RH_SET_INTERFACE 0x0B00 +#define RH_SYNC_FRAME 0x0C80 +/* Our Vendor Specific Request */ +#define RH_SET_EP 0x2000 + +/* Hub port features */ +#define RH_PORT_CONNECTION 0x00 +#define RH_PORT_ENABLE 0x01 +#define RH_PORT_SUSPEND 0x02 +#define RH_PORT_OVER_CURRENT 0x03 +#define RH_PORT_RESET 0x04 +#define RH_PORT_POWER 0x08 +#define RH_PORT_LOW_SPEED 0x09 + +#define RH_C_PORT_CONNECTION 0x10 +#define RH_C_PORT_ENABLE 0x11 +#define RH_C_PORT_SUSPEND 0x12 +#define RH_C_PORT_OVER_CURRENT 0x13 +#define RH_C_PORT_RESET 0x14 + +/* Hub features */ +#define RH_C_HUB_LOCAL_POWER 0x00 +#define RH_C_HUB_OVER_CURRENT 0x01 + +#define RH_DEVICE_REMOTE_WAKEUP 0x00 +#define RH_ENDPOINT_STALL 0x01 + +#define RH_ACK 0x01 +#define RH_REQ_ERR -1 +#define RH_NACK 0x00 diff --git a/include/usb.h b/include/usb.h index 419a7e364..504ccc40f 100644 --- a/include/usb.h +++ b/include/usb.h @@ -169,7 +169,10 @@ struct usb_device { * this is how the lowlevel part communicate with the outer world */ -#if defined(CONFIG_USB_UHCI) || defined(CONFIG_USB_OHCI) || defined (CONFIG_USB_SL811HS) || defined(CONFIG_USB_OHCI_NEW) +#if defined(CONFIG_USB_UHCI) || defined(CONFIG_USB_OHCI) || \ + defined(CONFIG_USB_OHCI_NEW) || defined (CONFIG_USB_SL811HS) || \ + defined(CONFIG_USB_ISP116X_HCD) + int usb_lowlevel_init(void); int usb_lowlevel_stop(void); int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer,int transfer_len); -- cgit v1.2.3 From d98c0885ad617fccf21e7c26ef8cb728fbfb2459 Mon Sep 17 00:00:00 2001 From: Rodolfo Giometti Date: Mon, 23 Apr 2007 13:10:52 +0200 Subject: USB: (Another) delay for crappy USB keys. Some USB keys are slow in giving back an answer when the Root HUB enables power lines. Signed-off-by: Rodolfo Giometti --- drivers/usb_ohci.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb_ohci.c b/drivers/usb_ohci.c index 1c3f622d5..70cb6a384 100644 --- a/drivers/usb_ohci.c +++ b/drivers/usb_ohci.c @@ -1169,7 +1169,9 @@ pkt_print(dev, pipe, buffer, transfer_len, cmd, "SUB(rh)", usb_pipein(pipe)); WR_RH_PORTSTAT (RH_PS_PRS); OK (0); case (RH_PORT_POWER): - WR_RH_PORTSTAT (RH_PS_PPS ); OK (0); + WR_RH_PORTSTAT (RH_PS_PPS ); + wait_ms(100); + OK (0); case (RH_PORT_ENABLE): /* BUG IN HUP CODE *********/ if (RD_RH_PORTSTAT & RH_PS_CCS) WR_RH_PORTSTAT (RH_PS_PES ); -- cgit v1.2.3 From 09444143670c9c2243cb7aba9f70b3713d33bed1 Mon Sep 17 00:00:00 2001 From: Markus Klotzbuecher Date: Wed, 6 Jun 2007 10:08:12 +0200 Subject: Change duplicate usb_cpu_init_fail to usb_board_init_fail Thanks to Liew Tsi Chung for pointing this out. Signed-off-by: Markus Klotzbuecher --- drivers/usb_ohci.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb_ohci.h b/drivers/usb_ohci.h index 95fbc4465..d2b03c0ee 100644 --- a/drivers/usb_ohci.h +++ b/drivers/usb_ohci.h @@ -10,7 +10,7 @@ /* functions for doing board or CPU specific setup/cleanup */ extern int usb_board_init(void); extern int usb_board_stop(void); -extern int usb_cpu_init_fail(void); +extern int usb_board_init_fail(void); extern int usb_cpu_init(void); extern int usb_cpu_stop(void); -- cgit v1.2.3 From 9a1d00fa47c1e05e3fdb60b33213af4e18d4c18e Mon Sep 17 00:00:00 2001 From: Rodolfo Giometti Date: Wed, 6 Jun 2007 10:08:12 +0200 Subject: ISP116x: delay for crappy USB keys Using some (very) slow USB keys cause the USB host controller buffers are not ready to be read by the CPU so we need an extra delay before reading the USB storage data. Signed-off-by: Rodolfo Giometti --- drivers/isp116x-hcd.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/isp116x-hcd.c b/drivers/isp116x-hcd.c index d57b8ece2..8e2bc7adc 100644 --- a/drivers/isp116x-hcd.c +++ b/drivers/isp116x-hcd.c @@ -550,6 +550,7 @@ static int isp116x_interrupt(struct isp116x *isp116x) if (irqstat & HCuPINT_ATL) { DBG(">>>>>> HCuPINT_ATL <<<<<<"); + udelay(500); ret = 1; } -- cgit v1.2.3 From 4dae14ce8fbdf380017dc54f172218e7d2acc889 Mon Sep 17 00:00:00 2001 From: Zhang Wei Date: Wed, 6 Jun 2007 10:08:14 +0200 Subject: USB PCI-OHCI, interrupt pipe and usb event poll support This patch added USB PCI-OHCI chips support, interrupt pipe support and usb event poll support. For supporting the USB interrupt pipe, the globe urb_priv is moved to purb in ed struct. Now, we can process several urbs at one time. The interrupt pipe support codes are ported from Linux kernel 2.4. Signed-off-by: Zhang Wei --- drivers/usb_ohci.c | 310 ++++++++++++++++++++++++++++++++++++++++++++--------- drivers/usb_ohci.h | 12 ++- 2 files changed, 267 insertions(+), 55 deletions(-) (limited to 'drivers') diff --git a/drivers/usb_ohci.c b/drivers/usb_ohci.c index 70cb6a384..459c809ca 100644 --- a/drivers/usb_ohci.c +++ b/drivers/usb_ohci.c @@ -1,5 +1,11 @@ /* - * URB OHCI HCD (Host Controller Driver) for USB on the AT91RM9200. + * URB OHCI HCD (Host Controller Driver) for USB on the AT91RM9200 and PCI bus. + * + * Interrupt support is added. Now, it has been tested + * on ULI1575 chip and works well with USB keyboard. + * + * (C) Copyright 2007 + * Zhang Wei, Freescale Semiconductor, Inc. * * (C) Copyright 2003 * Gary Jennejohn, DENX Software Engineering @@ -35,7 +41,7 @@ * 1 - you MUST define LITTLEENDIAN in the configuration file for the * board or this driver will NOT work! * 2 - this driver is intended for use with USB Mass Storage Devices - * (BBB) ONLY. There is NO support for Interrupt or Isochronous pipes! + * (BBB) and USB keyboard. There is NO support for Isochronous pipes! * 3 - when running on a PQFP208 AT91RM9200, define CONFIG_AT91C_PQFP_UHPBUG * to activate workaround for bug #41 or this driver will NOT work! */ @@ -56,6 +62,8 @@ # include #elif defined(CONFIG_MPC5200) # include +#elif defined(CONFIG_PCI_OHCI) +# include #endif #include @@ -66,6 +74,7 @@ defined(CONFIG_S3C2400) || \ defined(CONFIG_S3C2410) || \ defined(CONFIG_440EP) || \ + defined(CONFIG_PCI_OHCI) || \ defined(CONFIG_MPC5200) # define OHCI_USE_NPS /* force NoPowerSwitching mode */ #endif @@ -79,12 +88,19 @@ #define OHCI_CONTROL_INIT \ (OHCI_CTRL_CBSR & 0x3) | OHCI_CTRL_IE | OHCI_CTRL_PLE -#define readl(a) (*((vu_long *)(a))) -#define writel(a, b) (*((vu_long *)(b)) = ((vu_long)a)) +#define readl(a) m32_swap(*((vu_long *)(a))) +#define writel(a, b) (*((vu_long *)(b)) = m32_swap((vu_long)a)) #define min_t(type,x,y) ({ type __x = (x); type __y = (y); __x < __y ? __x: __y; }) -#undef DEBUG +#ifdef CONFIG_PCI_OHCI +static struct pci_device_id ohci_pci_ids[] = { + {0x10b9, 0x5237}, /* ULI1575 PCI OHCI module ids */ + /* Please add supported PCI OHCI controller ids here */ + {0, 0} +}; +#endif + #ifdef DEBUG #define dbg(format, arg...) printf("DEBUG: " format "\n", ## arg) #else @@ -114,15 +130,11 @@ struct ohci_hcca ghcca[1]; struct ohci_hcca *phcca; /* this allocates EDs for all possible endpoints */ struct ohci_device ohci_dev; -/* urb_priv */ -urb_priv_t urb_priv; /* RHSC flag */ int got_rhsc; /* device which was disconnected */ struct usb_device *devgone; -/* flag guarding URB transation */ -int urb_finished = 0; /*-------------------------------------------------------------------------*/ @@ -177,6 +189,7 @@ static void urb_free_priv (urb_priv_t * urb) } } } + free(urb); } /*-------------------------------------------------------------------------*/ @@ -187,11 +200,10 @@ static int sohci_get_current_frame_number (struct usb_device * dev); /* debug| print the main components of an URB * small: 0) header + data packets 1) just header */ -static void pkt_print (struct usb_device * dev, unsigned long pipe, void * buffer, +static void pkt_print (urb_priv_t *purb, struct usb_device * dev, + unsigned long pipe, void * buffer, int transfer_len, struct devrequest * setup, char * str, int small) { - urb_priv_t * purb = &urb_priv; - dbg("%s URB:[%4x] dev:%2d,ep:%2d-%c,type:%s,len:%d/%d stat:%#lx", str, sohci_get_current_frame_number (dev), @@ -200,7 +212,7 @@ static void pkt_print (struct usb_device * dev, unsigned long pipe, void * buffe usb_pipeout (pipe)? 'O': 'I', usb_pipetype (pipe) < 2? (usb_pipeint (pipe)? "INTR": "ISOC"): (usb_pipecontrol (pipe)? "CTRL": "BULK"), - purb->actual_length, + (purb ? purb->actual_length : 0), transfer_len, dev->status); #ifdef OHCI_VERBOSE_DEBUG if (!small) { @@ -214,10 +226,11 @@ static void pkt_print (struct usb_device * dev, unsigned long pipe, void * buffe } if (transfer_len > 0 && buffer) { printf (__FILE__ ": data(%d/%d):", - purb->actual_length, + (purb ? purb->actual_length : 0), transfer_len); len = usb_pipeout (pipe)? - transfer_len: purb->actual_length; + transfer_len: + (purb ? purb->actual_length : 0); for (i = 0; i < 16 && i < len; i++) printf (" %02x", ((__u8 *) buffer) [i]); printf ("%s\n", i < len? "...": ""); @@ -413,13 +426,17 @@ static void ohci_dump (ohci_t *controller, int verbose) /* get a transfer request */ -int sohci_submit_job(struct usb_device *dev, unsigned long pipe, void *buffer, - int transfer_len, struct devrequest *setup, int interval) +int sohci_submit_job(urb_priv_t *urb, struct devrequest *setup) { ohci_t *ohci; ed_t * ed; - urb_priv_t *purb_priv; + urb_priv_t *purb_priv = urb; int i, size = 0; + struct usb_device *dev = urb->dev; + unsigned long pipe = urb->pipe; + void *buffer = urb->transfer_buffer; + int transfer_len = urb->transfer_buffer_length; + int interval = urb->interval; ohci = &gohci; @@ -430,18 +447,11 @@ int sohci_submit_job(struct usb_device *dev, unsigned long pipe, void *buffer, return -1; } - /* if we have an unfinished URB from previous transaction let's - * fail and scream as quickly as possible so as not to corrupt - * further communication */ - if (!urb_finished) { - err("sohci_submit_job: URB NOT FINISHED"); - return -1; - } /* we're about to begin a new transaction here so mark the URB unfinished */ - urb_finished = 0; + urb->finished = 0; /* every endpoint has a ed, locate and fill it */ - if (!(ed = ep_add_ed (dev, pipe))) { + if (!(ed = ep_add_ed (dev, pipe, interval, 1))) { err("sohci_submit_job: ENOMEM"); return -1; } @@ -455,13 +465,17 @@ int sohci_submit_job(struct usb_device *dev, unsigned long pipe, void *buffer, size = (transfer_len == 0)? 2: (transfer_len - 1) / 4096 + 3; break; + case PIPE_INTERRUPT: /* 1 TD */ + size = 1; + break; } + ed->purb = urb; + if (size >= (N_URB_TD - 1)) { err("need %d TDs, only have %d", size, N_URB_TD); return -1; } - purb_priv = &urb_priv; purb_priv->pipe = pipe; /* fill the private part of the URB */ @@ -497,6 +511,40 @@ int sohci_submit_job(struct usb_device *dev, unsigned long pipe, void *buffer, return 0; } +static inline int sohci_return_job(struct ohci *hc, urb_priv_t *urb) +{ + struct ohci_regs *regs = hc->regs; + + switch (usb_pipetype (urb->pipe)) { + case PIPE_INTERRUPT: + /* implicitly requeued */ + if (urb->dev->irq_handle && + (urb->dev->irq_act_len = urb->actual_length)) { + writel (OHCI_INTR_WDH, ®s->intrenable); + readl (®s->intrenable); /* PCI posting flush */ + urb->dev->irq_handle(urb->dev); + writel (OHCI_INTR_WDH, ®s->intrdisable); + readl (®s->intrdisable); /* PCI posting flush */ + } + urb->actual_length = 0; + td_submit_job ( + urb->dev, + urb->pipe, + urb->transfer_buffer, + urb->transfer_buffer_length, + NULL, + urb, + urb->interval); + break; + case PIPE_CONTROL: + case PIPE_BULK: + break; + default: + return 0; + } + return 1; +} + /*-------------------------------------------------------------------------*/ #ifdef DEBUG @@ -510,6 +558,59 @@ static int sohci_get_current_frame_number (struct usb_device *usb_dev) } #endif +/*-------------------------------------------------------------------------* + * ED handling functions + *-------------------------------------------------------------------------*/ + +/* search for the right branch to insert an interrupt ed into the int tree + * do some load ballancing; + * returns the branch and + * sets the interval to interval = 2^integer (ld (interval)) */ + +static int ep_int_ballance (ohci_t * ohci, int interval, int load) +{ + int i, branch = 0; + + /* search for the least loaded interrupt endpoint + * branch of all 32 branches + */ + for (i = 0; i < 32; i++) + if (ohci->ohci_int_load [branch] > ohci->ohci_int_load [i]) + branch = i; + + branch = branch % interval; + for (i = branch; i < 32; i += interval) + ohci->ohci_int_load [i] += load; + + return branch; +} + +/*-------------------------------------------------------------------------*/ + +/* 2^int( ld (inter)) */ + +static int ep_2_n_interval (int inter) +{ + int i; + for (i = 0; ((inter >> i) > 1 ) && (i < 5); i++); + return 1 << i; +} + +/*-------------------------------------------------------------------------*/ + +/* the int tree is a binary tree + * in order to process it sequentially the indexes of the branches have to be mapped + * the mapping reverses the bits of a word of num_bits length */ + +static int ep_rev (int num_bits, int word) +{ + int i, wout = 0; + + for (i = 0; i < num_bits; i++) + wout |= (((word >> i) & 1) << (num_bits - i - 1)); + return wout; +} + /*-------------------------------------------------------------------------* * ED handling functions *-------------------------------------------------------------------------*/ @@ -519,8 +620,15 @@ static int sohci_get_current_frame_number (struct usb_device *usb_dev) static int ep_link (ohci_t *ohci, ed_t *edi) { volatile ed_t *ed = edi; + int int_branch; + int i; + int inter; + int interval; + int load; + __u32 * ed_p; ed->state = ED_OPER; + ed->int_interval = 0; switch (ed->type) { case PIPE_CONTROL: @@ -554,12 +662,49 @@ static int ep_link (ohci_t *ohci, ed_t *edi) } ohci->ed_bulktail = edi; break; + + case PIPE_INTERRUPT: + load = ed->int_load; + interval = ep_2_n_interval (ed->int_period); + ed->int_interval = interval; + int_branch = ep_int_ballance (ohci, interval, load); + ed->int_branch = int_branch; + + for (i = 0; i < ep_rev (6, interval); i += inter) { + inter = 1; + for (ed_p = &(ohci->hcca->int_table[ep_rev (5, i) + int_branch]); + (*ed_p != 0) && (((ed_t *)ed_p)->int_interval >= interval); + ed_p = &(((ed_t *)ed_p)->hwNextED)) + inter = ep_rev (6, ((ed_t *)ed_p)->int_interval); + ed->hwNextED = *ed_p; + *ed_p = m32_swap(ed); + } + break; } return 0; } /*-------------------------------------------------------------------------*/ +/* scan the periodic table to find and unlink this ED */ +static void periodic_unlink ( struct ohci *ohci, volatile struct ed *ed, + unsigned index, unsigned period) +{ + for (; index < NUM_INTS; index += period) { + __u32 *ed_p = &ohci->hcca->int_table [index]; + + /* ED might have been unlinked through another path */ + while (*ed_p != 0) { + if (((struct ed *)m32_swap (ed_p)) == ed) { + *ed_p = ed->hwNextED; + break; + } + ed_p = & (((struct ed *)m32_swap (ed_p))->hwNextED); + } + } +} + + /* unlink an ed from one of the HC chains. * just the link to the ed is unlinked. * the link from the ed still points to another operational ed or 0 @@ -568,6 +713,7 @@ static int ep_link (ohci_t *ohci, ed_t *edi) static int ep_unlink (ohci_t *ohci, ed_t *edi) { volatile ed_t *ed = edi; + int i; ed->hwINFO |= m32_swap (OHCI_ED_SKIP); @@ -605,6 +751,12 @@ static int ep_unlink (ohci_t *ohci, ed_t *edi) ((ed_t *)m32_swap (*((__u32 *)&ed->hwNextED)))->ed_prev = ed->ed_prev; } break; + + case PIPE_INTERRUPT: + periodic_unlink (ohci, ed, 0, 1); + for (i = ed->int_branch; i < 32; i += ed->int_interval) + ohci->ohci_int_load[i] -= ed->int_load; + break; } ed->state = ED_UNLINK; return 0; @@ -621,7 +773,8 @@ static int ep_unlink (ohci_t *ohci, ed_t *edi) * info fields are setted anyway even though most of them should not * change */ -static ed_t * ep_add_ed (struct usb_device *usb_dev, unsigned long pipe) +static ed_t * ep_add_ed (struct usb_device *usb_dev, unsigned long pipe, + int interval, int load) { td_t *td; ed_t *ed_ret; @@ -654,6 +807,11 @@ static ed_t * ep_add_ed (struct usb_device *usb_dev, unsigned long pipe) | usb_pipeslow (pipe) << 13 | usb_maxpacket (usb_dev, pipe) << 16); + if (ed->type == PIPE_INTERRUPT && ed->state == ED_UNLINK) { + ed->int_period = interval; + ed->int_load = load; + } + return ed_ret; } @@ -768,6 +926,13 @@ static void td_submit_job (struct usb_device *dev, unsigned long pipe, void *buf if (!ohci->sleeping) writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */ break; + + case PIPE_INTERRUPT: + info = usb_pipeout (urb->pipe)? + TD_CC | TD_DP_OUT | toggle: + TD_CC | TD_R | TD_DP_IN | toggle; + td_fill (ohci, info, data, data_len, dev, cnt++, urb); + break; } if (urb->length != cnt) dbg("TD LENGTH %d != CNT %d", urb->length, cnt); @@ -783,7 +948,7 @@ static void td_submit_job (struct usb_device *dev, unsigned long pipe, void *buf static void dl_transfer_length(td_t * td) { __u32 tdINFO, tdBE, tdCBP; - urb_priv_t *lurb_priv = &urb_priv; + urb_priv_t *lurb_priv = td->ed->purb; tdINFO = m32_swap (td->hwINFO); tdBE = m32_swap (td->hwBE); @@ -820,7 +985,7 @@ static td_t * dl_reverse_done_list (ohci_t *ohci) td_list = (td_t *)td_list_hc; if (TD_CC_GET (m32_swap (td_list->hwINFO))) { - lurb_priv = &urb_priv; + lurb_priv = td_list->ed->purb; dbg(" USB-error/status: %x : %p", TD_CC_GET (m32_swap (td_list->hwINFO)), td_list); if (td_list->ed->hwHeadP & m32_swap (0x1)) { @@ -860,10 +1025,10 @@ static int dl_done_list (ohci_t *ohci, td_t *td_list) while (td_list) { td_list_next = td_list->next_dl_td; - lurb_priv = &urb_priv; tdINFO = m32_swap (td_list->hwINFO); ed = td_list->ed; + lurb_priv = ed->purb; dl_transfer_length(td_list); @@ -882,14 +1047,16 @@ static int dl_done_list (ohci_t *ohci, td_t *td_list) (lurb_priv->state != URB_DEL)) #else if ((ed->state & (ED_OPER | ED_UNLINK))) - urb_finished = 1; +#endif + lurb_priv->finished = sohci_return_job(ohci, + lurb_priv); else dbg("dl_done_list: strange.., ED state %x, ed->state\n"); } else dbg("dl_done_list: processing TD %x, len %x\n", lurb_priv->td_cnt, lurb_priv->length); -#endif - if (ed->state != ED_NEW) { + if (ed->state != ED_NEW && + (usb_pipetype (lurb_priv->pipe) != PIPE_INTERRUPT)) { edHeadP = m32_swap (ed->hwHeadP) & 0xfffffff0; edTailP = m32_swap (ed->hwTailP); @@ -1063,8 +1230,7 @@ static int ohci_submit_rh_msg(struct usb_device *dev, unsigned long pipe, __u16 wLength; #ifdef DEBUG -urb_priv.actual_length = 0; -pkt_print(dev, pipe, buffer, transfer_len, cmd, "SUB(rh)", usb_pipein(pipe)); +pkt_print(NULL, dev, pipe, buffer, transfer_len, cmd, "SUB(rh)", usb_pipein(pipe)); #else wait_ms(1); #endif @@ -1279,9 +1445,7 @@ pkt_print(dev, pipe, buffer, transfer_len, cmd, "SUB(rh)", usb_pipein(pipe)); dev->status = stat; #ifdef DEBUG - if (transfer_len) - urb_priv.actual_length = transfer_len; - pkt_print(dev, pipe, buffer, transfer_len, cmd, "RET(rh)", 0/*usb_pipein(pipe)*/); + pkt_print(NULL, dev, pipe, buffer, transfer_len, cmd, "RET(rh)", 0/*usb_pipein(pipe)*/); #else wait_ms(1); #endif @@ -1299,6 +1463,16 @@ int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer, int stat = 0; int maxsize = usb_maxpacket(dev, pipe); int timeout; + urb_priv_t *urb; + + urb = malloc(sizeof(urb_priv_t)); + memset(urb, 0, sizeof(urb_priv_t)); + + urb->dev = dev; + urb->pipe = pipe; + urb->transfer_buffer = buffer; + urb->transfer_buffer_length = transfer_len; + urb->interval = interval; /* device pulled? Shortcut the action. */ if (devgone == dev) { @@ -1307,8 +1481,8 @@ int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer, } #ifdef DEBUG - urb_priv.actual_length = 0; - pkt_print(dev, pipe, buffer, transfer_len, setup, "SUB", usb_pipein(pipe)); + urb->actual_length = 0; + pkt_print(urb, dev, pipe, buffer, transfer_len, setup, "SUB", usb_pipein(pipe)); #else wait_ms(1); #endif @@ -1318,7 +1492,7 @@ int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer, return -1; } - if (sohci_submit_job(dev, pipe, buffer, transfer_len, setup, interval) < 0) { + if (sohci_submit_job(urb, setup) < 0) { err("sohci_submit_job failed"); return -1; } @@ -1353,18 +1527,20 @@ int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer, * finished we need to re-iterate this loop so as * hc_interrupt() gets called again as there needs to be some * more TD's to process still */ - if ((stat >= 0) && (stat != 0xff) && (urb_finished)) { + if ((stat >= 0) && (stat != 0xff) && (urb->finished)) { /* 0xff is returned for an SF-interrupt */ break; } if (--timeout) { wait_ms(1); + if (!urb->finished) + dbg("\%"); + } else { err("CTL:TIMEOUT "); dbg("submit_common_msg: TO status %x\n", stat); - stat = USB_ST_CRC_ERR; - urb_finished = 1; + urb->finished = 1; stat = USB_ST_CRC_ERR; break; } @@ -1374,13 +1550,14 @@ int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer, dev->act_len = transfer_len; #ifdef DEBUG - pkt_print(dev, pipe, buffer, transfer_len, setup, "RET(ctlr)", usb_pipein(pipe)); + pkt_print(urb, dev, pipe, buffer, transfer_len, setup, "RET(ctlr)", usb_pipein(pipe)); #else wait_ms(1); #endif /* free TDs in urb_priv */ - urb_free_priv (&urb_priv); + if (usb_pipetype (pipe) != PIPE_INTERRUPT) + urb_free_priv (urb); return 0; } @@ -1399,8 +1576,7 @@ int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, info("submit_control_msg"); #ifdef DEBUG - urb_priv.actual_length = 0; - pkt_print(dev, pipe, buffer, transfer_len, setup, "SUB", usb_pipein(pipe)); + pkt_print(NULL, dev, pipe, buffer, transfer_len, setup, "SUB", usb_pipein(pipe)); #else wait_ms(1); #endif @@ -1423,7 +1599,8 @@ int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, int transfer_len, int interval) { info("submit_int_msg"); - return -1; + return submit_common_msg(dev, pipe, buffer, transfer_len, NULL, + interval); } /*-------------------------------------------------------------------------* @@ -1537,6 +1714,12 @@ static int hc_start (ohci_t * ohci) /*-------------------------------------------------------------------------*/ +/* Poll USB interrupt. */ +void usb_event_poll(void) +{ + hc_interrupt(); +} + /* an interrupt happens */ static int hc_interrupt (void) @@ -1587,8 +1770,10 @@ static int hc_interrupt (void) if (ints & OHCI_INTR_WDH) { wait_ms(1); writel (OHCI_INTR_WDH, ®s->intrdisable); + (void)readl (®s->intrdisable); /* flush */ stat = dl_done_list (&gohci, dl_reverse_done_list (&gohci)); writel (OHCI_INTR_WDH, ®s->intrenable); + (void)readl (®s->intrdisable); /* flush */ } if (ints & OHCI_INTR_SO) { @@ -1634,6 +1819,9 @@ static char ohci_inited = 0; int usb_lowlevel_init(void) { +#ifdef CONFIG_PCI_OHCI + pci_dev_t pdev; +#endif #ifdef CFG_USB_OHCI_CPU_INIT /* cpu dependant init */ @@ -1647,7 +1835,6 @@ int usb_lowlevel_init(void) return -1; #endif memset (&gohci, 0, sizeof (ohci_t)); - memset (&urb_priv, 0, sizeof (urb_priv_t)); /* align the storage */ if ((__u32)&ghcca[0] & 0xff) { @@ -1673,7 +1860,25 @@ int usb_lowlevel_init(void) gohci.disabled = 1; gohci.sleeping = 0; gohci.irq = -1; +#ifdef CONFIG_PCI_OHCI + pdev = pci_find_devices(ohci_pci_ids, 0); + + if (pdev != -1) { + u16 vid, did; + u32 base; + pci_read_config_word(pdev, PCI_VENDOR_ID, &vid); + pci_read_config_word(pdev, PCI_DEVICE_ID, &did); + printf("OHCI pci controller (%04x, %04x) found @(%d:%d:%d)\n", + vid, did, (pdev >> 16) & 0xff, + (pdev >> 11) & 0x1f, (pdev >> 8) & 0x7); + pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0, &base); + printf("OHCI regs address 0x%08x\n", base); + gohci.regs = (struct ohci_regs *)base; + } else + return -1; +#else gohci.regs = (struct ohci_regs *)CFG_USB_OHCI_REGS_BASE; +#endif gohci.flags = 0; gohci.slot_name = CFG_USB_OHCI_SLOT_NAME; @@ -1716,7 +1921,6 @@ int usb_lowlevel_init(void) ohci_dump (&gohci, 1); #else wait_ms(1); - urb_finished = 1; #endif ohci_inited = 1; return 0; diff --git a/drivers/usb_ohci.h b/drivers/usb_ohci.h index d2b03c0ee..380cb4c92 100644 --- a/drivers/usb_ohci.h +++ b/drivers/usb_ohci.h @@ -64,7 +64,8 @@ struct ed { struct ed *ed_rm_list; struct usb_device *usb_dev; - __u32 unused[3]; + void *purb; + __u32 unused[2]; } __attribute((aligned(16))); typedef struct ed ed_t; @@ -349,9 +350,14 @@ typedef struct ed_t *ed; __u16 length; /* number of tds associated with this request */ __u16 td_cnt; /* number of tds already serviced */ + struct usb_device *dev; int state; unsigned long pipe; + void *transfer_buffer; + int transfer_buffer_length; + int interval; int actual_length; + int finished; td_t *td[N_URB_TD]; /* list pointer to all corresponding TDs associated with this request */ } urb_priv_t; #define URB_DEL 1 @@ -375,6 +381,7 @@ typedef struct ohci { struct ohci_regs *regs; /* OHCI controller's memory */ + int ohci_int_load[32]; /* load of the 32 Interrupt Chains (for load balancing)*/ ed_t *ed_rm_list[2]; /* lists of all endpoints to be removed */ ed_t *ed_bulktail; /* last endpoint of bulk list */ ed_t *ed_controltail; /* last endpoint of control list */ @@ -397,7 +404,8 @@ struct ohci_device { /* endpoint */ static int ep_link(ohci_t * ohci, ed_t * ed); static int ep_unlink(ohci_t * ohci, ed_t * ed); -static ed_t * ep_add_ed(struct usb_device * usb_dev, unsigned long pipe); +static ed_t * ep_add_ed(struct usb_device * usb_dev, unsigned long pipe, + int interval, int load); /*-------------------------------------------------------------------------*/ -- cgit v1.2.3 From 18135125f909948b85d1d6881ab4ac0efb4a1c58 Mon Sep 17 00:00:00 2001 From: Rodolfo Giometti Date: Wed, 6 Jun 2007 10:08:14 +0200 Subject: Files include/linux/byteorder/{big,little}_endian.h define __BIG_ENDIAN and __LITTLE_ENDIAN. Signed-off-by: Rodolfo Giometti --- drivers/usbtty.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usbtty.c b/drivers/usbtty.c index d41a00b8c..a3b50131d 100644 --- a/drivers/usbtty.c +++ b/drivers/usbtty.c @@ -381,12 +381,12 @@ static void str2wide (char *str, u16 * wide) { int i; for (i = 0; i < strlen (str) && str[i]; i++){ - #if defined(__LITTLE_ENDIAN__) + #if defined(__LITTLE_ENDIAN) wide[i] = (u16) str[i]; - #elif defined(__BIG_ENDIAN__) + #elif defined(__BIG_ENDIAN) wide[i] = ((u16)(str[i])<<8); #else - #error "__LITTLE_ENDIAN__ or __BIG_ENDIAN__ undefined" + #error "__LITTLE_ENDIAN or __BIG_ENDIAN undefined" #endif } } -- cgit v1.2.3 From fc43be478f2aa37ce38acd85355038866e4162af Mon Sep 17 00:00:00 2001 From: Markus Klotzbuecher Date: Wed, 6 Jun 2007 11:49:35 +0200 Subject: USB/OHCI: endianness cleanup in the generic ohci driver --- doc/README.generic_usb_ohci | 49 ++++++++++++++++++--------- drivers/usb_ohci.c | 82 ++++++++++++++++----------------------------- 2 files changed, 62 insertions(+), 69 deletions(-) (limited to 'drivers') diff --git a/doc/README.generic_usb_ohci b/doc/README.generic_usb_ohci index 017c69ab0..c343dfdf9 100644 --- a/doc/README.generic_usb_ohci +++ b/doc/README.generic_usb_ohci @@ -9,35 +9,52 @@ into cpu/board directories and are called via the hooks below. Configuration options ---------------------- -CONFIG_USB_OHCI_NEW: enable the new OHCI driver + CONFIG_USB_OHCI_NEW: enable the new OHCI driver -CFG_USB_OHCI_BOARD_INIT: call the board dependant hooks: + CFG_USB_OHCI_BOARD_INIT: call the board dependant hooks: - - extern int usb_board_init(void); - - extern int usb_board_stop(void); - - extern int usb_cpu_init_fail(void); + - extern int usb_board_init(void); + - extern int usb_board_stop(void); + - extern int usb_cpu_init_fail(void); -CFG_USB_OHCI_CPU_INIT: call the cpu dependant hooks: + CFG_USB_OHCI_CPU_INIT: call the cpu dependant hooks: - - extern int usb_cpu_init(void); - - extern int usb_cpu_stop(void); - - extern int usb_cpu_init_fail(void); + - extern int usb_cpu_init(void); + - extern int usb_cpu_stop(void); + - extern int usb_cpu_init_fail(void); -CFG_USB_OHCI_REGS_BASE: defines the base address of the OHCI registers + CFG_USB_OHCI_REGS_BASE: defines the base address of the OHCI + registers -CFG_USB_OHCI_SLOT_NAME: slot name + CFG_USB_OHCI_SLOT_NAME: slot name -CFG_USB_OHCI_MAX_ROOT_PORTS: maximal number of ports of the root hub. + CFG_USB_OHCI_MAX_ROOT_PORTS: maximal number of ports of the + root hub. Endianness issues ------------------ -The LITTLEENDIAN #define determines if the 'swap_16' and 'swap_32' -macros do byte swapping or not. But some cpus OHCI-controllers such as -ppc4xx and mpc5xxx operate in little endian mode, so some extra ifdefs -were necessary to make this work. +The USB bus operates in little endian, but unfortunately there are +OHCI controllers that operate in big endian such as ppc4xx and +mpc5xxx. For these the config option + CFG_OHCI_BE_CONTROLLER + +needs to be defined. + + +PCI Controllers +---------------- + +You'll need to define + + CONFIG_PCI_OHCI + +PCI Controllers need to do byte swapping on register accesses, so they +should to define: + + CFG_OHCI_SWAP_REG_ACCESS diff --git a/drivers/usb_ohci.c b/drivers/usb_ohci.c index 459c809ca..3cef576b2 100644 --- a/drivers/usb_ohci.c +++ b/drivers/usb_ohci.c @@ -38,31 +38,20 @@ */ /* * IMPORTANT NOTES - * 1 - you MUST define LITTLEENDIAN in the configuration file for the - * board or this driver will NOT work! + * 1 - Read doc/README.generic_usb_ohci * 2 - this driver is intended for use with USB Mass Storage Devices * (BBB) and USB keyboard. There is NO support for Isochronous pipes! - * 3 - when running on a PQFP208 AT91RM9200, define CONFIG_AT91C_PQFP_UHPBUG + * 2 - when running on a PQFP208 AT91RM9200, define CONFIG_AT91C_PQFP_UHPBUG * to activate workaround for bug #41 or this driver will NOT work! */ #include -/* #include no PCI on the S3C24X0 */ #ifdef CONFIG_USB_OHCI_NEW -/* mk: are these really required? */ -#if defined(CONFIG_S3C2400) -# include -#elif defined(CONFIG_S3C2410) -# include -#elif defined(CONFIG_ARM920T) -# include -#elif defined(CONFIG_CPU_MONAHANS) -# include -#elif defined(CONFIG_MPC5200) -# include -#elif defined(CONFIG_PCI_OHCI) +#include + +#if defined(CONFIG_PCI_OHCI) # include #endif @@ -88,8 +77,16 @@ #define OHCI_CONTROL_INIT \ (OHCI_CTRL_CBSR & 0x3) | OHCI_CTRL_IE | OHCI_CTRL_PLE -#define readl(a) m32_swap(*((vu_long *)(a))) -#define writel(a, b) (*((vu_long *)(b)) = m32_swap((vu_long)a)) +/* + * e.g. PCI controllers need this + */ +#ifdef CFG_OHCI_SWAP_REG_ACCESS +# define readl(a) __swap_16(*((vu_long *)(a))) +# define writel(a, b) (*((vu_long *)(b)) = __swap_32((vu_long)a)) +#else +# define readl(a) (*((vu_long *)(a))) +# define writel(a, b) (*((vu_long *)(b)) = ((vu_long)a)) +#endif /* CFG_OHCI_SWAP_REG_ACCESS */ #define min_t(type,x,y) ({ type __x = (x); type __y = (y); __x < __y ? __x: __y; }) @@ -114,13 +111,13 @@ static struct pci_device_id ohci_pci_ids[] = { #define info(format, arg...) do {} while(0) #endif -#if defined(CONFIG_440EP) || defined(CONFIG_MPC5200) -# define m16_swap(x) (x) -# define m32_swap(x) (x) +#ifdef CFG_OHCI_BE_CONTROLLER +# define m16_swap(x) cpu_to_be16(x) +# define m32_swap(x) cpu_to_be32(x) #else -# define m16_swap(x) swap_16(x) -# define m32_swap(x) swap_32(x) -#endif +# define m16_swap(x) cpu_to_le16(x) +# define m32_swap(x) cpu_to_le32(x) +#endif /* CFG_OHCI_BE_CONTROLLER */ /* global ohci_t */ static ohci_t gohci; @@ -1240,15 +1237,9 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len, cmd, "SUB(rh)", usb_pipein(pipe } bmRType_bReq = cmd->requesttype | (cmd->request << 8); -#if defined(CONFIG_440EP) || defined(CONFIG_MPC5200) - wValue = __swap_16(cmd->value); - wIndex = __swap_16(cmd->index); - wLength = __swap_16(cmd->length); -#else - wValue = m16_swap (cmd->value); - wIndex = m16_swap (cmd->index); - wLength = m16_swap (cmd->length); -#endif /* CONFIG_440EP || CONFIG_MPC5200 */ + wValue = cpu_to_le16 (cmd->value); + wIndex = cpu_to_le16 (cmd->index); + wLength = cpu_to_le16 (cmd->length); info("Root-Hub: adr: %2x cmd(%1x): %08x %04x %04x %04x", dev->devnum, 8, bmRType_bReq, wValue, wIndex, wLength); @@ -1262,33 +1253,18 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len, cmd, "SUB(rh)", usb_pipein(pipe RH_OTHER | RH_CLASS almost ever means HUB_PORT here */ -#if defined(CONFIG_440EP) || defined(CONFIG_MPC5200) - case RH_GET_STATUS: - *(__u16 *) data_buf = __swap_16(1); OK (2); - case RH_GET_STATUS | RH_INTERFACE: - *(__u16 *) data_buf = __swap_16(0); OK (2); - case RH_GET_STATUS | RH_ENDPOINT: - *(__u16 *) data_buf = __swap_16(0); OK (2); - case RH_GET_STATUS | RH_CLASS: - *(__u32 *) data_buf = __swap_32( - RD_RH_STAT & ~(RH_HS_CRWE | RH_HS_DRWE)); - OK (4); - case RH_GET_STATUS | RH_OTHER | RH_CLASS: - *(__u32 *) data_buf = __swap_32(RD_RH_PORTSTAT); OK (4); -#else case RH_GET_STATUS: - *(__u16 *) data_buf = m16_swap (1); OK (2); + *(__u16 *) data_buf = cpu_to_le16 (1); OK (2); case RH_GET_STATUS | RH_INTERFACE: - *(__u16 *) data_buf = m16_swap (0); OK (2); + *(__u16 *) data_buf = cpu_to_le16 (0); OK (2); case RH_GET_STATUS | RH_ENDPOINT: - *(__u16 *) data_buf = m16_swap (0); OK (2); + *(__u16 *) data_buf = cpu_to_le16 (0); OK (2); case RH_GET_STATUS | RH_CLASS: - *(__u32 *) data_buf = m32_swap ( + *(__u32 *) data_buf = cpu_to_le32 ( RD_RH_STAT & ~(RH_HS_CRWE | RH_HS_DRWE)); OK (4); case RH_GET_STATUS | RH_OTHER | RH_CLASS: - *(__u32 *) data_buf = m32_swap (RD_RH_PORTSTAT); OK (4); -#endif /* CONFIG_440EP || CONFIG_MPC5200 */ + *(__u32 *) data_buf = cpu_to_le32 (RD_RH_PORTSTAT); OK (4); case RH_CLEAR_FEATURE | RH_ENDPOINT: switch (wValue) { -- cgit v1.2.3 From 9b7464a2c88614e1061f509c48930a3d240d1a35 Mon Sep 17 00:00:00 2001 From: Jason Jin Date: Mon, 11 Jun 2007 15:14:24 +0200 Subject: USB: This patch fix readl in ohci swap reg access. Signed-off-by: Jason Jin --- drivers/usb_ohci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb_ohci.c b/drivers/usb_ohci.c index 3cef576b2..d6b745fad 100644 --- a/drivers/usb_ohci.c +++ b/drivers/usb_ohci.c @@ -81,7 +81,7 @@ * e.g. PCI controllers need this */ #ifdef CFG_OHCI_SWAP_REG_ACCESS -# define readl(a) __swap_16(*((vu_long *)(a))) +# define readl(a) __swap_32(*((vu_long *)(a))) # define writel(a, b) (*((vu_long *)(b)) = __swap_32((vu_long)a)) #else # define readl(a) (*((vu_long *)(a))) -- cgit v1.2.3