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 e6176ed86ae..d5b6811829e 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 00000000000..4065489f3e7 --- /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 00000000000..7a1d9d9ccda --- /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. --- drivers/usb_ohci.c | 59 ++++++++++++++++++++++++++++++++++++++---------------- drivers/usb_ohci.h | 8 ++++++++ 2 files changed, 50 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/usb_ohci.c b/drivers/usb_ohci.c index 4065489f3e7..f5af7192f9f 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 7a1d9d9ccda..c37b5f6005c 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] = { -- 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. --- drivers/usb_ohci.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/usb_ohci.c b/drivers/usb_ohci.c index f5af7192f9f..c32245a2497 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; -- 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. --- drivers/usb_ohci.c | 133 +++++++++++++++++++++++++++++++++++++++++++---------- drivers/usb_ohci.h | 2 + 2 files changed, 110 insertions(+), 25 deletions(-) (limited to 'drivers') diff --git a/drivers/usb_ohci.c b/drivers/usb_ohci.c index c32245a2497..9b3ca1232da 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 c37b5f6005c..a1b36ed83cc 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 -- 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 --- 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 +- 6 files changed, 2043 insertions(+), 330 deletions(-) create mode 100644 drivers/usbdcore_mpc8xx.c (limited to 'drivers') diff --git a/drivers/Makefile b/drivers/Makefile index d5b6811829e..8732e162622 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 260befe9786..5e7443be8fc 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 00000000000..9bd2c231acd --- /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 ce4a12e16e0..ed96999e820 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 79c2fe57d7a..731b76330d4 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 -- 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 --- drivers/usbdcore_ep0.c | 8 +- drivers/usbdcore_mpc8xx.c | 1124 +++++++++++++++++++++---------------------- drivers/usbdcore_omap1510.c | 4 +- drivers/usbtty.c | 164 +++---- drivers/usbtty.h | 4 +- 5 files changed, 646 insertions(+), 658 deletions(-) (limited to 'drivers') diff --git a/drivers/usbdcore_ep0.c b/drivers/usbdcore_ep0.c index 5e7443be8fc..1e44f322a79 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 9bd2c231acd..e87284b1787 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 ed96999e820..d41a00b8cf4 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 731b76330d4..8154e3072ef 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 -- 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 --- drivers/usb_ohci.h | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/usb_ohci.h b/drivers/usb_ohci.h index a1b36ed83cc..68dd4ecafee 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). --- drivers/usb_ohci.c | 13 +++++++++++-- drivers/usb_ohci.h | 13 +++++++++++-- 2 files changed, 22 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/usb_ohci.c b/drivers/usb_ohci.c index 9b3ca1232da..be1a615ebeb 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 68dd4ecafee..95fbc446549 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))); -- 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. --- drivers/usb_ohci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb_ohci.c b/drivers/usb_ohci.c index be1a615ebeb..482633fc67e 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 */ -- 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 ++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 49 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/usb_ohci.c b/drivers/usb_ohci.c index 482633fc67e..c5e4c38efdf 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 */ -- cgit v1.2.3