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/usb_ohci.c | 1642 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1642 insertions(+) create mode 100644 drivers/usb_ohci.c (limited to 'drivers/usb_ohci.c') diff --git a/drivers/usb_ohci.c b/drivers/usb_ohci.c new file mode 100644 index 0000000..4065489 --- /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 */ -- cgit v1.1 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 ++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 42 insertions(+), 17 deletions(-) (limited to 'drivers/usb_ohci.c') diff --git a/drivers/usb_ohci.c b/drivers/usb_ohci.c index 4065489..f5af719 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) */ @@ -311,9 +314,6 @@ static void ohci_dump_roothub (ohci_t *controller, int verbose) #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, @@ -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; } -- cgit v1.1 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/usb_ohci.c') diff --git a/drivers/usb_ohci.c b/drivers/usb_ohci.c index f5af719..c32245a 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.1 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 +++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 108 insertions(+), 25 deletions(-) (limited to 'drivers/usb_ohci.c') diff --git a/drivers/usb_ohci.c b/drivers/usb_ohci.c index c32245a..9b3ca12 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; -- cgit v1.1 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 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'drivers/usb_ohci.c') diff --git a/drivers/usb_ohci.c b/drivers/usb_ohci.c index 9b3ca12..be1a615 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); -- cgit v1.1 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/usb_ohci.c') diff --git a/drivers/usb_ohci.c b/drivers/usb_ohci.c index be1a615..482633f 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.1 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/usb_ohci.c') diff --git a/drivers/usb_ohci.c b/drivers/usb_ohci.c index 482633f..c5e4c38 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.1 From ae79f60677c208326535647dcbd5c3ec40dbcb0b Mon Sep 17 00:00:00 2001 From: Markus Klotzbuecher Date: Mon, 26 Mar 2007 11:21:05 +0200 Subject: USB: remove the S3C24X0_merge #define, which was introduced while merging OHCI drivers. Signed-off-by: Markus Klotzbuecher --- drivers/usb_ohci.c | 56 +++--------------------------------------------------- 1 file changed, 3 insertions(+), 53 deletions(-) (limited to 'drivers/usb_ohci.c') diff --git a/drivers/usb_ohci.c b/drivers/usb_ohci.c index c5e4c38..1c3f622 100644 --- a/drivers/usb_ohci.c +++ b/drivers/usb_ohci.c @@ -62,8 +62,6 @@ #include #include "usb_ohci.h" -#define S3C24X0_merge - #if defined(CONFIG_ARM920T) || \ defined(CONFIG_S3C2400) || \ defined(CONFIG_S3C2410) || \ @@ -123,10 +121,8 @@ int got_rhsc; /* device which was disconnected */ struct usb_device *devgone; -#ifdef S3C24X0_merge /* flag guarding URB transation */ int urb_finished = 0; -#endif /*-------------------------------------------------------------------------*/ @@ -433,7 +429,7 @@ int sohci_submit_job(struct usb_device *dev, unsigned long pipe, void *buffer, err("sohci_submit_job: EPIPE"); return -1; } -#ifdef S3C24X0_merge + /* if we have an unfinished URB from previous transaction let's * fail and scream as quickly as possible so as not to corrupt * further communication */ @@ -443,7 +439,6 @@ int sohci_submit_job(struct usb_device *dev, unsigned long pipe, void *buffer, } /* we're about to begin a new transaction here so mark the URB unfinished */ urb_finished = 0; -#endif /* every endpoint has a ed, locate and fill it */ if (!(ed = ep_add_ed (dev, pipe))) { @@ -709,9 +704,6 @@ static void td_fill (ohci_t *ohci, unsigned int info, else td->hwBE = 0; td->hwNextTD = m32_swap ((unsigned long)td_pt); -#ifndef S3C24X0_merge - td->hwPSW [0] = m16_swap (((__u32)data & 0x0FFF) | 0xE000); -#endif /* append to queue */ td->ed->hwTailP = td->hwNextTD; @@ -881,7 +873,7 @@ static int dl_done_list (ohci_t *ohci, td_t *td_list) dbg("ConditionCode %#x", cc); stat = cc_to_error[cc]; } -#ifdef S3C24X0_merge + /* see if this done list makes for all TD's of current URB, * and mark the URB finished if so */ if (++(lurb_priv->td_cnt) == lurb_priv->length) { @@ -890,7 +882,6 @@ static int dl_done_list (ohci_t *ohci, td_t *td_list) (lurb_priv->state != URB_DEL)) #else if ((ed->state & (ED_OPER | ED_UNLINK))) -#endif urb_finished = 1; else dbg("dl_done_list: strange.., ED state %x, ed->state\n"); @@ -1351,7 +1342,6 @@ int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer, break; } -#ifdef S3C24X0_merge /* NOTE: since we are not interrupt driven in U-Boot and always * handle only one URB at a time, we cannot assume the * transaction finished on the first successful return from @@ -1362,9 +1352,6 @@ int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer, * hc_interrupt() gets called again as there needs to be some * more TD's to process still */ if ((stat >= 0) && (stat != 0xff) && (urb_finished)) { -#else - if (stat >= 0 && stat != 0xff) { -#endif /* 0xff is returned for an SF-interrupt */ break; } @@ -1373,38 +1360,13 @@ int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer, wait_ms(1); } else { err("CTL:TIMEOUT "); -#ifdef S3C24X0_merge dbg("submit_common_msg: TO status %x\n", stat); stat = USB_ST_CRC_ERR; urb_finished = 1; -#endif stat = USB_ST_CRC_ERR; break; } } -#ifndef S3C24X0_merge - /* we got an Root Hub Status Change interrupt */ - if (got_rhsc) { -#ifdef DEBUG - ohci_dump_roothub (&gohci, 1); -#endif - got_rhsc = 0; - /* abuse timeout */ - timeout = rh_check_port_status(&gohci); - if (timeout >= 0) { -#if 0 /* this does nothing useful, but leave it here in case that changes */ - /* the called routine adds 1 to the passed value */ - usb_hub_port_connect_change(gohci.rh.dev, timeout - 1); -#endif - /* - * XXX - * This is potentially dangerous because it assumes - * that only one device is ever plugged in! - */ - devgone = dev; - } - } -#endif /* S3C24X0_merge */ dev->status = stat; dev->act_len = transfer_len; @@ -1582,8 +1544,6 @@ static int hc_interrupt (void) int ints; int stat = -1; -#ifdef S3C24X0_merge - if ((ohci->hcca->done_head != 0) && !(m32_swap (ohci->hcca->done_head) & 0x01)) { ints = OHCI_INTR_WDH; @@ -1595,20 +1555,12 @@ static int hc_interrupt (void) dbg("hc_interrupt: returning..\n"); return 0xff; } -#else - if ((ohci->hcca->done_head != 0) && !(m32_swap (ohci->hcca->done_head) & 0x01)) { - ints = OHCI_INTR_WDH; - } else { - ints = readl (®s->intrstatus); - } -#endif + /* dbg("Interrupt: %x frame: %x", ints, le16_to_cpu (ohci->hcca->frame_no)); */ if (ints & OHCI_INTR_RHSC) { got_rhsc = 1; -#ifdef S3C24X0_merge stat = 0xff; -#endif } if (ints & OHCI_INTR_UE) { @@ -1762,9 +1714,7 @@ int usb_lowlevel_init(void) ohci_dump (&gohci, 1); #else wait_ms(1); -# ifdef S3C24X0_merge urb_finished = 1; -# endif #endif ohci_inited = 1; return 0; -- cgit v1.1 From d98c0885ad617fccf21e7c26ef8cb728fbfb2459 Mon Sep 17 00:00:00 2001 From: Rodolfo Giometti Date: Mon, 23 Apr 2007 13:10:52 +0200 Subject: USB: (Another) delay for crappy USB keys. Some USB keys are slow in giving back an answer when the Root HUB enables power lines. Signed-off-by: Rodolfo Giometti --- drivers/usb_ohci.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/usb_ohci.c') diff --git a/drivers/usb_ohci.c b/drivers/usb_ohci.c index 1c3f622..70cb6a3 100644 --- a/drivers/usb_ohci.c +++ b/drivers/usb_ohci.c @@ -1169,7 +1169,9 @@ pkt_print(dev, pipe, buffer, transfer_len, cmd, "SUB(rh)", usb_pipein(pipe)); WR_RH_PORTSTAT (RH_PS_PRS); OK (0); case (RH_PORT_POWER): - WR_RH_PORTSTAT (RH_PS_PPS ); OK (0); + WR_RH_PORTSTAT (RH_PS_PPS ); + wait_ms(100); + OK (0); case (RH_PORT_ENABLE): /* BUG IN HUP CODE *********/ if (RD_RH_PORTSTAT & RH_PS_CCS) WR_RH_PORTSTAT (RH_PS_PES ); -- cgit v1.1 From 4dae14ce8fbdf380017dc54f172218e7d2acc889 Mon Sep 17 00:00:00 2001 From: Zhang Wei Date: Wed, 6 Jun 2007 10:08:14 +0200 Subject: USB PCI-OHCI, interrupt pipe and usb event poll support This patch added USB PCI-OHCI chips support, interrupt pipe support and usb event poll support. For supporting the USB interrupt pipe, the globe urb_priv is moved to purb in ed struct. Now, we can process several urbs at one time. The interrupt pipe support codes are ported from Linux kernel 2.4. Signed-off-by: Zhang Wei --- drivers/usb_ohci.c | 310 ++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 257 insertions(+), 53 deletions(-) (limited to 'drivers/usb_ohci.c') diff --git a/drivers/usb_ohci.c b/drivers/usb_ohci.c index 70cb6a3..459c809 100644 --- a/drivers/usb_ohci.c +++ b/drivers/usb_ohci.c @@ -1,5 +1,11 @@ /* - * URB OHCI HCD (Host Controller Driver) for USB on the AT91RM9200. + * URB OHCI HCD (Host Controller Driver) for USB on the AT91RM9200 and PCI bus. + * + * Interrupt support is added. Now, it has been tested + * on ULI1575 chip and works well with USB keyboard. + * + * (C) Copyright 2007 + * Zhang Wei, Freescale Semiconductor, Inc. * * (C) Copyright 2003 * Gary Jennejohn, DENX Software Engineering @@ -35,7 +41,7 @@ * 1 - you MUST define LITTLEENDIAN in the configuration file for the * board or this driver will NOT work! * 2 - this driver is intended for use with USB Mass Storage Devices - * (BBB) ONLY. There is NO support for Interrupt or Isochronous pipes! + * (BBB) and USB keyboard. There is NO support for Isochronous pipes! * 3 - when running on a PQFP208 AT91RM9200, define CONFIG_AT91C_PQFP_UHPBUG * to activate workaround for bug #41 or this driver will NOT work! */ @@ -56,6 +62,8 @@ # include #elif defined(CONFIG_MPC5200) # include +#elif defined(CONFIG_PCI_OHCI) +# include #endif #include @@ -66,6 +74,7 @@ defined(CONFIG_S3C2400) || \ defined(CONFIG_S3C2410) || \ defined(CONFIG_440EP) || \ + defined(CONFIG_PCI_OHCI) || \ defined(CONFIG_MPC5200) # define OHCI_USE_NPS /* force NoPowerSwitching mode */ #endif @@ -79,12 +88,19 @@ #define OHCI_CONTROL_INIT \ (OHCI_CTRL_CBSR & 0x3) | OHCI_CTRL_IE | OHCI_CTRL_PLE -#define readl(a) (*((vu_long *)(a))) -#define writel(a, b) (*((vu_long *)(b)) = ((vu_long)a)) +#define readl(a) m32_swap(*((vu_long *)(a))) +#define writel(a, b) (*((vu_long *)(b)) = m32_swap((vu_long)a)) #define min_t(type,x,y) ({ type __x = (x); type __y = (y); __x < __y ? __x: __y; }) -#undef DEBUG +#ifdef CONFIG_PCI_OHCI +static struct pci_device_id ohci_pci_ids[] = { + {0x10b9, 0x5237}, /* ULI1575 PCI OHCI module ids */ + /* Please add supported PCI OHCI controller ids here */ + {0, 0} +}; +#endif + #ifdef DEBUG #define dbg(format, arg...) printf("DEBUG: " format "\n", ## arg) #else @@ -114,15 +130,11 @@ struct ohci_hcca ghcca[1]; struct ohci_hcca *phcca; /* this allocates EDs for all possible endpoints */ struct ohci_device ohci_dev; -/* urb_priv */ -urb_priv_t urb_priv; /* RHSC flag */ int got_rhsc; /* device which was disconnected */ struct usb_device *devgone; -/* flag guarding URB transation */ -int urb_finished = 0; /*-------------------------------------------------------------------------*/ @@ -177,6 +189,7 @@ static void urb_free_priv (urb_priv_t * urb) } } } + free(urb); } /*-------------------------------------------------------------------------*/ @@ -187,11 +200,10 @@ static int sohci_get_current_frame_number (struct usb_device * dev); /* debug| print the main components of an URB * small: 0) header + data packets 1) just header */ -static void pkt_print (struct usb_device * dev, unsigned long pipe, void * buffer, +static void pkt_print (urb_priv_t *purb, struct usb_device * dev, + unsigned long pipe, void * buffer, int transfer_len, struct devrequest * setup, char * str, int small) { - urb_priv_t * purb = &urb_priv; - dbg("%s URB:[%4x] dev:%2d,ep:%2d-%c,type:%s,len:%d/%d stat:%#lx", str, sohci_get_current_frame_number (dev), @@ -200,7 +212,7 @@ static void pkt_print (struct usb_device * dev, unsigned long pipe, void * buffe usb_pipeout (pipe)? 'O': 'I', usb_pipetype (pipe) < 2? (usb_pipeint (pipe)? "INTR": "ISOC"): (usb_pipecontrol (pipe)? "CTRL": "BULK"), - purb->actual_length, + (purb ? purb->actual_length : 0), transfer_len, dev->status); #ifdef OHCI_VERBOSE_DEBUG if (!small) { @@ -214,10 +226,11 @@ static void pkt_print (struct usb_device * dev, unsigned long pipe, void * buffe } if (transfer_len > 0 && buffer) { printf (__FILE__ ": data(%d/%d):", - purb->actual_length, + (purb ? purb->actual_length : 0), transfer_len); len = usb_pipeout (pipe)? - transfer_len: purb->actual_length; + transfer_len: + (purb ? purb->actual_length : 0); for (i = 0; i < 16 && i < len; i++) printf (" %02x", ((__u8 *) buffer) [i]); printf ("%s\n", i < len? "...": ""); @@ -413,13 +426,17 @@ static void ohci_dump (ohci_t *controller, int verbose) /* get a transfer request */ -int sohci_submit_job(struct usb_device *dev, unsigned long pipe, void *buffer, - int transfer_len, struct devrequest *setup, int interval) +int sohci_submit_job(urb_priv_t *urb, struct devrequest *setup) { ohci_t *ohci; ed_t * ed; - urb_priv_t *purb_priv; + urb_priv_t *purb_priv = urb; int i, size = 0; + struct usb_device *dev = urb->dev; + unsigned long pipe = urb->pipe; + void *buffer = urb->transfer_buffer; + int transfer_len = urb->transfer_buffer_length; + int interval = urb->interval; ohci = &gohci; @@ -430,18 +447,11 @@ int sohci_submit_job(struct usb_device *dev, unsigned long pipe, void *buffer, return -1; } - /* if we have an unfinished URB from previous transaction let's - * fail and scream as quickly as possible so as not to corrupt - * further communication */ - if (!urb_finished) { - err("sohci_submit_job: URB NOT FINISHED"); - return -1; - } /* we're about to begin a new transaction here so mark the URB unfinished */ - urb_finished = 0; + urb->finished = 0; /* every endpoint has a ed, locate and fill it */ - if (!(ed = ep_add_ed (dev, pipe))) { + if (!(ed = ep_add_ed (dev, pipe, interval, 1))) { err("sohci_submit_job: ENOMEM"); return -1; } @@ -455,13 +465,17 @@ int sohci_submit_job(struct usb_device *dev, unsigned long pipe, void *buffer, size = (transfer_len == 0)? 2: (transfer_len - 1) / 4096 + 3; break; + case PIPE_INTERRUPT: /* 1 TD */ + size = 1; + break; } + ed->purb = urb; + if (size >= (N_URB_TD - 1)) { err("need %d TDs, only have %d", size, N_URB_TD); return -1; } - purb_priv = &urb_priv; purb_priv->pipe = pipe; /* fill the private part of the URB */ @@ -497,6 +511,40 @@ int sohci_submit_job(struct usb_device *dev, unsigned long pipe, void *buffer, return 0; } +static inline int sohci_return_job(struct ohci *hc, urb_priv_t *urb) +{ + struct ohci_regs *regs = hc->regs; + + switch (usb_pipetype (urb->pipe)) { + case PIPE_INTERRUPT: + /* implicitly requeued */ + if (urb->dev->irq_handle && + (urb->dev->irq_act_len = urb->actual_length)) { + writel (OHCI_INTR_WDH, ®s->intrenable); + readl (®s->intrenable); /* PCI posting flush */ + urb->dev->irq_handle(urb->dev); + writel (OHCI_INTR_WDH, ®s->intrdisable); + readl (®s->intrdisable); /* PCI posting flush */ + } + urb->actual_length = 0; + td_submit_job ( + urb->dev, + urb->pipe, + urb->transfer_buffer, + urb->transfer_buffer_length, + NULL, + urb, + urb->interval); + break; + case PIPE_CONTROL: + case PIPE_BULK: + break; + default: + return 0; + } + return 1; +} + /*-------------------------------------------------------------------------*/ #ifdef DEBUG @@ -514,13 +562,73 @@ static int sohci_get_current_frame_number (struct usb_device *usb_dev) * ED handling functions *-------------------------------------------------------------------------*/ +/* search for the right branch to insert an interrupt ed into the int tree + * do some load ballancing; + * returns the branch and + * sets the interval to interval = 2^integer (ld (interval)) */ + +static int ep_int_ballance (ohci_t * ohci, int interval, int load) +{ + int i, branch = 0; + + /* search for the least loaded interrupt endpoint + * branch of all 32 branches + */ + for (i = 0; i < 32; i++) + if (ohci->ohci_int_load [branch] > ohci->ohci_int_load [i]) + branch = i; + + branch = branch % interval; + for (i = branch; i < 32; i += interval) + ohci->ohci_int_load [i] += load; + + return branch; +} + +/*-------------------------------------------------------------------------*/ + +/* 2^int( ld (inter)) */ + +static int ep_2_n_interval (int inter) +{ + int i; + for (i = 0; ((inter >> i) > 1 ) && (i < 5); i++); + return 1 << i; +} + +/*-------------------------------------------------------------------------*/ + +/* the int tree is a binary tree + * in order to process it sequentially the indexes of the branches have to be mapped + * the mapping reverses the bits of a word of num_bits length */ + +static int ep_rev (int num_bits, int word) +{ + int i, wout = 0; + + for (i = 0; i < num_bits; i++) + wout |= (((word >> i) & 1) << (num_bits - i - 1)); + return wout; +} + +/*-------------------------------------------------------------------------* + * ED handling functions + *-------------------------------------------------------------------------*/ + /* link an ed into one of the HC chains */ static int ep_link (ohci_t *ohci, ed_t *edi) { volatile ed_t *ed = edi; + int int_branch; + int i; + int inter; + int interval; + int load; + __u32 * ed_p; ed->state = ED_OPER; + ed->int_interval = 0; switch (ed->type) { case PIPE_CONTROL: @@ -554,12 +662,49 @@ static int ep_link (ohci_t *ohci, ed_t *edi) } ohci->ed_bulktail = edi; break; + + case PIPE_INTERRUPT: + load = ed->int_load; + interval = ep_2_n_interval (ed->int_period); + ed->int_interval = interval; + int_branch = ep_int_ballance (ohci, interval, load); + ed->int_branch = int_branch; + + for (i = 0; i < ep_rev (6, interval); i += inter) { + inter = 1; + for (ed_p = &(ohci->hcca->int_table[ep_rev (5, i) + int_branch]); + (*ed_p != 0) && (((ed_t *)ed_p)->int_interval >= interval); + ed_p = &(((ed_t *)ed_p)->hwNextED)) + inter = ep_rev (6, ((ed_t *)ed_p)->int_interval); + ed->hwNextED = *ed_p; + *ed_p = m32_swap(ed); + } + break; } return 0; } /*-------------------------------------------------------------------------*/ +/* scan the periodic table to find and unlink this ED */ +static void periodic_unlink ( struct ohci *ohci, volatile struct ed *ed, + unsigned index, unsigned period) +{ + for (; index < NUM_INTS; index += period) { + __u32 *ed_p = &ohci->hcca->int_table [index]; + + /* ED might have been unlinked through another path */ + while (*ed_p != 0) { + if (((struct ed *)m32_swap (ed_p)) == ed) { + *ed_p = ed->hwNextED; + break; + } + ed_p = & (((struct ed *)m32_swap (ed_p))->hwNextED); + } + } +} + + /* unlink an ed from one of the HC chains. * just the link to the ed is unlinked. * the link from the ed still points to another operational ed or 0 @@ -568,6 +713,7 @@ static int ep_link (ohci_t *ohci, ed_t *edi) static int ep_unlink (ohci_t *ohci, ed_t *edi) { volatile ed_t *ed = edi; + int i; ed->hwINFO |= m32_swap (OHCI_ED_SKIP); @@ -605,6 +751,12 @@ static int ep_unlink (ohci_t *ohci, ed_t *edi) ((ed_t *)m32_swap (*((__u32 *)&ed->hwNextED)))->ed_prev = ed->ed_prev; } break; + + case PIPE_INTERRUPT: + periodic_unlink (ohci, ed, 0, 1); + for (i = ed->int_branch; i < 32; i += ed->int_interval) + ohci->ohci_int_load[i] -= ed->int_load; + break; } ed->state = ED_UNLINK; return 0; @@ -621,7 +773,8 @@ static int ep_unlink (ohci_t *ohci, ed_t *edi) * info fields are setted anyway even though most of them should not * change */ -static ed_t * ep_add_ed (struct usb_device *usb_dev, unsigned long pipe) +static ed_t * ep_add_ed (struct usb_device *usb_dev, unsigned long pipe, + int interval, int load) { td_t *td; ed_t *ed_ret; @@ -654,6 +807,11 @@ static ed_t * ep_add_ed (struct usb_device *usb_dev, unsigned long pipe) | usb_pipeslow (pipe) << 13 | usb_maxpacket (usb_dev, pipe) << 16); + if (ed->type == PIPE_INTERRUPT && ed->state == ED_UNLINK) { + ed->int_period = interval; + ed->int_load = load; + } + return ed_ret; } @@ -768,6 +926,13 @@ static void td_submit_job (struct usb_device *dev, unsigned long pipe, void *buf if (!ohci->sleeping) writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */ break; + + case PIPE_INTERRUPT: + info = usb_pipeout (urb->pipe)? + TD_CC | TD_DP_OUT | toggle: + TD_CC | TD_R | TD_DP_IN | toggle; + td_fill (ohci, info, data, data_len, dev, cnt++, urb); + break; } if (urb->length != cnt) dbg("TD LENGTH %d != CNT %d", urb->length, cnt); @@ -783,7 +948,7 @@ static void td_submit_job (struct usb_device *dev, unsigned long pipe, void *buf static void dl_transfer_length(td_t * td) { __u32 tdINFO, tdBE, tdCBP; - urb_priv_t *lurb_priv = &urb_priv; + urb_priv_t *lurb_priv = td->ed->purb; tdINFO = m32_swap (td->hwINFO); tdBE = m32_swap (td->hwBE); @@ -820,7 +985,7 @@ static td_t * dl_reverse_done_list (ohci_t *ohci) td_list = (td_t *)td_list_hc; if (TD_CC_GET (m32_swap (td_list->hwINFO))) { - lurb_priv = &urb_priv; + lurb_priv = td_list->ed->purb; dbg(" USB-error/status: %x : %p", TD_CC_GET (m32_swap (td_list->hwINFO)), td_list); if (td_list->ed->hwHeadP & m32_swap (0x1)) { @@ -860,10 +1025,10 @@ static int dl_done_list (ohci_t *ohci, td_t *td_list) while (td_list) { td_list_next = td_list->next_dl_td; - lurb_priv = &urb_priv; tdINFO = m32_swap (td_list->hwINFO); ed = td_list->ed; + lurb_priv = ed->purb; dl_transfer_length(td_list); @@ -882,14 +1047,16 @@ static int dl_done_list (ohci_t *ohci, td_t *td_list) (lurb_priv->state != URB_DEL)) #else if ((ed->state & (ED_OPER | ED_UNLINK))) - urb_finished = 1; +#endif + lurb_priv->finished = sohci_return_job(ohci, + lurb_priv); else dbg("dl_done_list: strange.., ED state %x, ed->state\n"); } else dbg("dl_done_list: processing TD %x, len %x\n", lurb_priv->td_cnt, lurb_priv->length); -#endif - if (ed->state != ED_NEW) { + if (ed->state != ED_NEW && + (usb_pipetype (lurb_priv->pipe) != PIPE_INTERRUPT)) { edHeadP = m32_swap (ed->hwHeadP) & 0xfffffff0; edTailP = m32_swap (ed->hwTailP); @@ -1063,8 +1230,7 @@ static int ohci_submit_rh_msg(struct usb_device *dev, unsigned long pipe, __u16 wLength; #ifdef DEBUG -urb_priv.actual_length = 0; -pkt_print(dev, pipe, buffer, transfer_len, cmd, "SUB(rh)", usb_pipein(pipe)); +pkt_print(NULL, dev, pipe, buffer, transfer_len, cmd, "SUB(rh)", usb_pipein(pipe)); #else wait_ms(1); #endif @@ -1279,9 +1445,7 @@ pkt_print(dev, pipe, buffer, transfer_len, cmd, "SUB(rh)", usb_pipein(pipe)); dev->status = stat; #ifdef DEBUG - if (transfer_len) - urb_priv.actual_length = transfer_len; - pkt_print(dev, pipe, buffer, transfer_len, cmd, "RET(rh)", 0/*usb_pipein(pipe)*/); + pkt_print(NULL, dev, pipe, buffer, transfer_len, cmd, "RET(rh)", 0/*usb_pipein(pipe)*/); #else wait_ms(1); #endif @@ -1299,6 +1463,16 @@ int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer, int stat = 0; int maxsize = usb_maxpacket(dev, pipe); int timeout; + urb_priv_t *urb; + + urb = malloc(sizeof(urb_priv_t)); + memset(urb, 0, sizeof(urb_priv_t)); + + urb->dev = dev; + urb->pipe = pipe; + urb->transfer_buffer = buffer; + urb->transfer_buffer_length = transfer_len; + urb->interval = interval; /* device pulled? Shortcut the action. */ if (devgone == dev) { @@ -1307,8 +1481,8 @@ int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer, } #ifdef DEBUG - urb_priv.actual_length = 0; - pkt_print(dev, pipe, buffer, transfer_len, setup, "SUB", usb_pipein(pipe)); + urb->actual_length = 0; + pkt_print(urb, dev, pipe, buffer, transfer_len, setup, "SUB", usb_pipein(pipe)); #else wait_ms(1); #endif @@ -1318,7 +1492,7 @@ int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer, return -1; } - if (sohci_submit_job(dev, pipe, buffer, transfer_len, setup, interval) < 0) { + if (sohci_submit_job(urb, setup) < 0) { err("sohci_submit_job failed"); return -1; } @@ -1353,18 +1527,20 @@ int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer, * finished we need to re-iterate this loop so as * hc_interrupt() gets called again as there needs to be some * more TD's to process still */ - if ((stat >= 0) && (stat != 0xff) && (urb_finished)) { + if ((stat >= 0) && (stat != 0xff) && (urb->finished)) { /* 0xff is returned for an SF-interrupt */ break; } if (--timeout) { wait_ms(1); + if (!urb->finished) + dbg("\%"); + } else { err("CTL:TIMEOUT "); dbg("submit_common_msg: TO status %x\n", stat); - stat = USB_ST_CRC_ERR; - urb_finished = 1; + urb->finished = 1; stat = USB_ST_CRC_ERR; break; } @@ -1374,13 +1550,14 @@ int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer, dev->act_len = transfer_len; #ifdef DEBUG - pkt_print(dev, pipe, buffer, transfer_len, setup, "RET(ctlr)", usb_pipein(pipe)); + pkt_print(urb, dev, pipe, buffer, transfer_len, setup, "RET(ctlr)", usb_pipein(pipe)); #else wait_ms(1); #endif /* free TDs in urb_priv */ - urb_free_priv (&urb_priv); + if (usb_pipetype (pipe) != PIPE_INTERRUPT) + urb_free_priv (urb); return 0; } @@ -1399,8 +1576,7 @@ int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, info("submit_control_msg"); #ifdef DEBUG - urb_priv.actual_length = 0; - pkt_print(dev, pipe, buffer, transfer_len, setup, "SUB", usb_pipein(pipe)); + pkt_print(NULL, dev, pipe, buffer, transfer_len, setup, "SUB", usb_pipein(pipe)); #else wait_ms(1); #endif @@ -1423,7 +1599,8 @@ int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, int transfer_len, int interval) { info("submit_int_msg"); - return -1; + return submit_common_msg(dev, pipe, buffer, transfer_len, NULL, + interval); } /*-------------------------------------------------------------------------* @@ -1537,6 +1714,12 @@ static int hc_start (ohci_t * ohci) /*-------------------------------------------------------------------------*/ +/* Poll USB interrupt. */ +void usb_event_poll(void) +{ + hc_interrupt(); +} + /* an interrupt happens */ static int hc_interrupt (void) @@ -1587,8 +1770,10 @@ static int hc_interrupt (void) if (ints & OHCI_INTR_WDH) { wait_ms(1); writel (OHCI_INTR_WDH, ®s->intrdisable); + (void)readl (®s->intrdisable); /* flush */ stat = dl_done_list (&gohci, dl_reverse_done_list (&gohci)); writel (OHCI_INTR_WDH, ®s->intrenable); + (void)readl (®s->intrdisable); /* flush */ } if (ints & OHCI_INTR_SO) { @@ -1634,6 +1819,9 @@ static char ohci_inited = 0; int usb_lowlevel_init(void) { +#ifdef CONFIG_PCI_OHCI + pci_dev_t pdev; +#endif #ifdef CFG_USB_OHCI_CPU_INIT /* cpu dependant init */ @@ -1647,7 +1835,6 @@ int usb_lowlevel_init(void) return -1; #endif memset (&gohci, 0, sizeof (ohci_t)); - memset (&urb_priv, 0, sizeof (urb_priv_t)); /* align the storage */ if ((__u32)&ghcca[0] & 0xff) { @@ -1673,7 +1860,25 @@ int usb_lowlevel_init(void) gohci.disabled = 1; gohci.sleeping = 0; gohci.irq = -1; +#ifdef CONFIG_PCI_OHCI + pdev = pci_find_devices(ohci_pci_ids, 0); + + if (pdev != -1) { + u16 vid, did; + u32 base; + pci_read_config_word(pdev, PCI_VENDOR_ID, &vid); + pci_read_config_word(pdev, PCI_DEVICE_ID, &did); + printf("OHCI pci controller (%04x, %04x) found @(%d:%d:%d)\n", + vid, did, (pdev >> 16) & 0xff, + (pdev >> 11) & 0x1f, (pdev >> 8) & 0x7); + pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0, &base); + printf("OHCI regs address 0x%08x\n", base); + gohci.regs = (struct ohci_regs *)base; + } else + return -1; +#else gohci.regs = (struct ohci_regs *)CFG_USB_OHCI_REGS_BASE; +#endif gohci.flags = 0; gohci.slot_name = CFG_USB_OHCI_SLOT_NAME; @@ -1716,7 +1921,6 @@ int usb_lowlevel_init(void) ohci_dump (&gohci, 1); #else wait_ms(1); - urb_finished = 1; #endif ohci_inited = 1; return 0; -- cgit v1.1 From fc43be478f2aa37ce38acd85355038866e4162af Mon Sep 17 00:00:00 2001 From: Markus Klotzbuecher Date: Wed, 6 Jun 2007 11:49:35 +0200 Subject: USB/OHCI: endianness cleanup in the generic ohci driver --- drivers/usb_ohci.c | 82 +++++++++++++++++++----------------------------------- 1 file changed, 29 insertions(+), 53 deletions(-) (limited to 'drivers/usb_ohci.c') diff --git a/drivers/usb_ohci.c b/drivers/usb_ohci.c index 459c809..3cef576 100644 --- a/drivers/usb_ohci.c +++ b/drivers/usb_ohci.c @@ -38,31 +38,20 @@ */ /* * IMPORTANT NOTES - * 1 - you MUST define LITTLEENDIAN in the configuration file for the - * board or this driver will NOT work! + * 1 - Read doc/README.generic_usb_ohci * 2 - this driver is intended for use with USB Mass Storage Devices * (BBB) and USB keyboard. There is NO support for Isochronous pipes! - * 3 - when running on a PQFP208 AT91RM9200, define CONFIG_AT91C_PQFP_UHPBUG + * 2 - when running on a PQFP208 AT91RM9200, define CONFIG_AT91C_PQFP_UHPBUG * to activate workaround for bug #41 or this driver will NOT work! */ #include -/* #include no PCI on the S3C24X0 */ #ifdef CONFIG_USB_OHCI_NEW -/* mk: are these really required? */ -#if defined(CONFIG_S3C2400) -# include -#elif defined(CONFIG_S3C2410) -# include -#elif defined(CONFIG_ARM920T) -# include -#elif defined(CONFIG_CPU_MONAHANS) -# include -#elif defined(CONFIG_MPC5200) -# include -#elif defined(CONFIG_PCI_OHCI) +#include + +#if defined(CONFIG_PCI_OHCI) # include #endif @@ -88,8 +77,16 @@ #define OHCI_CONTROL_INIT \ (OHCI_CTRL_CBSR & 0x3) | OHCI_CTRL_IE | OHCI_CTRL_PLE -#define readl(a) m32_swap(*((vu_long *)(a))) -#define writel(a, b) (*((vu_long *)(b)) = m32_swap((vu_long)a)) +/* + * e.g. PCI controllers need this + */ +#ifdef CFG_OHCI_SWAP_REG_ACCESS +# define readl(a) __swap_16(*((vu_long *)(a))) +# define writel(a, b) (*((vu_long *)(b)) = __swap_32((vu_long)a)) +#else +# define readl(a) (*((vu_long *)(a))) +# define writel(a, b) (*((vu_long *)(b)) = ((vu_long)a)) +#endif /* CFG_OHCI_SWAP_REG_ACCESS */ #define min_t(type,x,y) ({ type __x = (x); type __y = (y); __x < __y ? __x: __y; }) @@ -114,13 +111,13 @@ static struct pci_device_id ohci_pci_ids[] = { #define info(format, arg...) do {} while(0) #endif -#if defined(CONFIG_440EP) || defined(CONFIG_MPC5200) -# define m16_swap(x) (x) -# define m32_swap(x) (x) +#ifdef CFG_OHCI_BE_CONTROLLER +# define m16_swap(x) cpu_to_be16(x) +# define m32_swap(x) cpu_to_be32(x) #else -# define m16_swap(x) swap_16(x) -# define m32_swap(x) swap_32(x) -#endif +# define m16_swap(x) cpu_to_le16(x) +# define m32_swap(x) cpu_to_le32(x) +#endif /* CFG_OHCI_BE_CONTROLLER */ /* global ohci_t */ static ohci_t gohci; @@ -1240,15 +1237,9 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len, cmd, "SUB(rh)", usb_pipein(pipe } bmRType_bReq = cmd->requesttype | (cmd->request << 8); -#if defined(CONFIG_440EP) || defined(CONFIG_MPC5200) - wValue = __swap_16(cmd->value); - wIndex = __swap_16(cmd->index); - wLength = __swap_16(cmd->length); -#else - wValue = m16_swap (cmd->value); - wIndex = m16_swap (cmd->index); - wLength = m16_swap (cmd->length); -#endif /* CONFIG_440EP || CONFIG_MPC5200 */ + wValue = cpu_to_le16 (cmd->value); + wIndex = cpu_to_le16 (cmd->index); + wLength = cpu_to_le16 (cmd->length); info("Root-Hub: adr: %2x cmd(%1x): %08x %04x %04x %04x", dev->devnum, 8, bmRType_bReq, wValue, wIndex, wLength); @@ -1262,33 +1253,18 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len, cmd, "SUB(rh)", usb_pipein(pipe RH_OTHER | RH_CLASS almost ever means HUB_PORT here */ -#if defined(CONFIG_440EP) || defined(CONFIG_MPC5200) - case RH_GET_STATUS: - *(__u16 *) data_buf = __swap_16(1); OK (2); - case RH_GET_STATUS | RH_INTERFACE: - *(__u16 *) data_buf = __swap_16(0); OK (2); - case RH_GET_STATUS | RH_ENDPOINT: - *(__u16 *) data_buf = __swap_16(0); OK (2); - case RH_GET_STATUS | RH_CLASS: - *(__u32 *) data_buf = __swap_32( - RD_RH_STAT & ~(RH_HS_CRWE | RH_HS_DRWE)); - OK (4); - case RH_GET_STATUS | RH_OTHER | RH_CLASS: - *(__u32 *) data_buf = __swap_32(RD_RH_PORTSTAT); OK (4); -#else case RH_GET_STATUS: - *(__u16 *) data_buf = m16_swap (1); OK (2); + *(__u16 *) data_buf = cpu_to_le16 (1); OK (2); case RH_GET_STATUS | RH_INTERFACE: - *(__u16 *) data_buf = m16_swap (0); OK (2); + *(__u16 *) data_buf = cpu_to_le16 (0); OK (2); case RH_GET_STATUS | RH_ENDPOINT: - *(__u16 *) data_buf = m16_swap (0); OK (2); + *(__u16 *) data_buf = cpu_to_le16 (0); OK (2); case RH_GET_STATUS | RH_CLASS: - *(__u32 *) data_buf = m32_swap ( + *(__u32 *) data_buf = cpu_to_le32 ( RD_RH_STAT & ~(RH_HS_CRWE | RH_HS_DRWE)); OK (4); case RH_GET_STATUS | RH_OTHER | RH_CLASS: - *(__u32 *) data_buf = m32_swap (RD_RH_PORTSTAT); OK (4); -#endif /* CONFIG_440EP || CONFIG_MPC5200 */ + *(__u32 *) data_buf = cpu_to_le32 (RD_RH_PORTSTAT); OK (4); case RH_CLEAR_FEATURE | RH_ENDPOINT: switch (wValue) { -- cgit v1.1 From 9b7464a2c88614e1061f509c48930a3d240d1a35 Mon Sep 17 00:00:00 2001 From: Jason Jin Date: Mon, 11 Jun 2007 15:14:24 +0200 Subject: USB: This patch fix readl in ohci swap reg access. Signed-off-by: Jason Jin --- drivers/usb_ohci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb_ohci.c') diff --git a/drivers/usb_ohci.c b/drivers/usb_ohci.c index 3cef576..d6b745f 100644 --- a/drivers/usb_ohci.c +++ b/drivers/usb_ohci.c @@ -81,7 +81,7 @@ * e.g. PCI controllers need this */ #ifdef CFG_OHCI_SWAP_REG_ACCESS -# define readl(a) __swap_16(*((vu_long *)(a))) +# define readl(a) __swap_32(*((vu_long *)(a))) # define writel(a, b) (*((vu_long *)(b)) = __swap_32((vu_long)a)) #else # define readl(a) (*((vu_long *)(a))) -- cgit v1.1