diff options
author | Markus Klotzbuecher <mk@denx.de> | 2007-03-23 09:52:17 +0100 |
---|---|---|
committer | Markus Klotzbuecher <mk@pollux.denx.de> | 2007-03-23 09:52:17 +0100 |
commit | f2b07ebd32e42a5b1126c98efc768ddb8908de62 (patch) | |
tree | e2a374a15d0abf6872fbbdeecf8e34855e56d3c9 /cpu/arm920t/at91rm9200/usb_ohci.c | |
parent | 44ba464b99001f8bd1c456a1e9d59726252f707a (diff) | |
parent | 83dc830b1693252d996bda920cd5f3161d7c64a9 (diff) | |
download | u-boot-imx-f2b07ebd32e42a5b1126c98efc768ddb8908de62.zip u-boot-imx-f2b07ebd32e42a5b1126c98efc768ddb8908de62.tar.gz u-boot-imx-f2b07ebd32e42a5b1126c98efc768ddb8908de62.tar.bz2 |
Merge with git://www.denx.de/git/u-boot.git#testing-USB
Diffstat (limited to 'cpu/arm920t/at91rm9200/usb_ohci.c')
-rw-r--r-- | cpu/arm920t/at91rm9200/usb_ohci.c | 1635 |
1 files changed, 0 insertions, 1635 deletions
diff --git a/cpu/arm920t/at91rm9200/usb_ohci.c b/cpu/arm920t/at91rm9200/usb_ohci.c deleted file mode 100644 index 5b2c56c..0000000 --- a/cpu/arm920t/at91rm9200/usb_ohci.c +++ /dev/null @@ -1,1635 +0,0 @@ -/* - * URB OHCI HCD (Host Controller Driver) for USB on the AT91RM9200. - * - * (C) Copyright 2003 - * Gary Jennejohn, DENX Software Engineering <gj@denx.de> - * - * Note: Much of this code has been derived from Linux 2.4 - * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at> - * (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 <common.h> -/* #include <pci.h> no PCI on the S3C24X0 */ - -#ifdef CONFIG_USB_OHCI - -#include <asm/arch/hardware.h> - -#include <malloc.h> -#include <usb.h> -#include "usb_ohci.h" - -#define OHCI_USE_NPS /* force NoPowerSwitching mode */ -#undef OHCI_VERBOSE_DEBUG /* not always helpful */ - -/* For initializing controller (mask in an HCFS mode too) */ -#define OHCI_CONTROL_INIT \ - (OHCI_CTRL_CBSR & 0x3) | OHCI_CTRL_IE | OHCI_CTRL_PLE - -#define readl(a) (*((vu_long *)(a))) -#define writel(a, b) (*((vu_long *)(b)) = ((vu_long)a)) - -#define min_t(type,x,y) ({ type __x = (x); type __y = (y); __x < __y ? __x: __y; }) - -#undef DEBUG -#ifdef DEBUG -#define dbg(format, arg...) printf("DEBUG: " format "\n", ## arg) -#else -#define dbg(format, arg...) do {} while(0) -#endif /* DEBUG */ -#define err(format, arg...) printf("ERROR: " format "\n", ## arg) -#undef SHOW_INFO -#ifdef SHOW_INFO -#define info(format, arg...) printf("INFO: " format "\n", ## arg) -#else -#define info(format, arg...) do {} while(0) -#endif - -#define m16_swap(x) swap_16(x) -#define m32_swap(x) swap_32(x) - -/* global ohci_t */ -static ohci_t gohci; -/* this must be aligned to a 256 byte boundary */ -struct ohci_hcca ghcca[1]; -/* a pointer to the aligned storage */ -struct ohci_hcca *phcca; -/* this allocates EDs for all possible endpoints */ -struct ohci_device ohci_dev; -/* urb_priv */ -urb_priv_t urb_priv; -/* RHSC flag */ -int got_rhsc; -/* device which was disconnected */ -struct usb_device *devgone; - -/*-------------------------------------------------------------------------*/ - -/* AMD-756 (D2 rev) reports corrupt register contents in some cases. - * The erratum (#4) description is incorrect. AMD's workaround waits - * till some bits (mostly reserved) are clear; ok for all revs. - */ -#define OHCI_QUIRK_AMD756 0xabcd -#define read_roothub(hc, register, mask) ({ \ - u32 temp = readl (&hc->regs->roothub.register); \ - if (hc->flags & OHCI_QUIRK_AMD756) \ - while (temp & mask) \ - temp = readl (&hc->regs->roothub.register); \ - temp; }) - -static u32 roothub_a (struct ohci *hc) - { return read_roothub (hc, a, 0xfc0fe000); } -static inline u32 roothub_b (struct ohci *hc) - { return readl (&hc->regs->roothub.b); } -static inline u32 roothub_status (struct ohci *hc) - { return readl (&hc->regs->roothub.status); } -static u32 roothub_portstatus (struct ohci *hc, int i) - { return read_roothub (hc, portstatus [i], 0xffe0fce0); } - - -/* forward declaration */ -static int hc_interrupt (void); -static void -td_submit_job (struct usb_device * dev, unsigned long pipe, void * buffer, - int transfer_len, struct devrequest * setup, urb_priv_t * urb, int interval); - -/*-------------------------------------------------------------------------* - * URB support functions - *-------------------------------------------------------------------------*/ - -/* free HCD-private data associated with this URB */ - -static void urb_free_priv (urb_priv_t * urb) -{ - int i; - int last; - struct td * td; - - last = urb->length - 1; - if (last >= 0) { - for (i = 0; i <= last; i++) { - td = urb->td[i]; - if (td) { - td->usb_dev = NULL; - urb->td[i] = NULL; - } - } - } -} - -/*-------------------------------------------------------------------------*/ - -#ifdef DEBUG -static int sohci_get_current_frame_number (struct usb_device * dev); - -/* debug| print the main components of an URB - * small: 0) header + data packets 1) just header */ - -static void pkt_print (struct usb_device * dev, unsigned long pipe, void * buffer, - int transfer_len, struct devrequest * setup, char * str, int small) -{ - urb_priv_t * purb = &urb_priv; - - dbg("%s URB:[%4x] dev:%2d,ep:%2d-%c,type:%s,len:%d/%d stat:%#lx", - str, - sohci_get_current_frame_number (dev), - usb_pipedevice (pipe), - usb_pipeendpoint (pipe), - usb_pipeout (pipe)? 'O': 'I', - usb_pipetype (pipe) < 2? (usb_pipeint (pipe)? "INTR": "ISOC"): - (usb_pipecontrol (pipe)? "CTRL": "BULK"), - purb->actual_length, - transfer_len, dev->status); -#ifdef OHCI_VERBOSE_DEBUG - if (!small) { - int i, len; - - if (usb_pipecontrol (pipe)) { - printf (__FILE__ ": cmd(8):"); - for (i = 0; i < 8 ; i++) - printf (" %02x", ((__u8 *) setup) [i]); - printf ("\n"); - } - if (transfer_len > 0 && buffer) { - printf (__FILE__ ": data(%d/%d):", - purb->actual_length, - transfer_len); - len = usb_pipeout (pipe)? - transfer_len: purb->actual_length; - for (i = 0; i < 16 && i < len; i++) - printf (" %02x", ((__u8 *) buffer) [i]); - printf ("%s\n", i < len? "...": ""); - } - } -#endif -} - -/* just for debugging; prints non-empty branches of the int ed tree inclusive iso eds*/ -void ep_print_int_eds (ohci_t *ohci, char * str) { - int i, j; - __u32 * ed_p; - for (i= 0; i < 32; i++) { - j = 5; - ed_p = &(ohci->hcca->int_table [i]); - if (*ed_p == 0) - continue; - printf (__FILE__ ": %s branch int %2d(%2x):", str, i, i); - while (*ed_p != 0 && j--) { - ed_t *ed = (ed_t *)m32_swap(ed_p); - printf (" ed: %4x;", ed->hwINFO); - ed_p = &ed->hwNextED; - } - printf ("\n"); - } -} - -static void ohci_dump_intr_mask (char *label, __u32 mask) -{ - dbg ("%s: 0x%08x%s%s%s%s%s%s%s%s%s", - label, - mask, - (mask & OHCI_INTR_MIE) ? " MIE" : "", - (mask & OHCI_INTR_OC) ? " OC" : "", - (mask & OHCI_INTR_RHSC) ? " RHSC" : "", - (mask & OHCI_INTR_FNO) ? " FNO" : "", - (mask & OHCI_INTR_UE) ? " UE" : "", - (mask & OHCI_INTR_RD) ? " RD" : "", - (mask & OHCI_INTR_SF) ? " SF" : "", - (mask & OHCI_INTR_WDH) ? " WDH" : "", - (mask & OHCI_INTR_SO) ? " SO" : "" - ); -} - -static void maybe_print_eds (char *label, __u32 value) -{ - ed_t *edp = (ed_t *)value; - - if (value) { - dbg ("%s %08x", label, value); - dbg ("%08x", edp->hwINFO); - dbg ("%08x", edp->hwTailP); - dbg ("%08x", edp->hwHeadP); - dbg ("%08x", edp->hwNextED); - } -} - -static char * hcfs2string (int state) -{ - switch (state) { - case OHCI_USB_RESET: return "reset"; - case OHCI_USB_RESUME: return "resume"; - case OHCI_USB_OPER: return "operational"; - case OHCI_USB_SUSPEND: return "suspend"; - } - return "?"; -} - -/* dump control and status registers */ -static void ohci_dump_status (ohci_t *controller) -{ - struct ohci_regs *regs = controller->regs; - __u32 temp; - - temp = readl (®s->revision) & 0xff; - if (temp != 0x10) - dbg ("spec %d.%d", (temp >> 4), (temp & 0x0f)); - - temp = readl (®s->control); - dbg ("control: 0x%08x%s%s%s HCFS=%s%s%s%s%s CBSR=%d", temp, - (temp & OHCI_CTRL_RWE) ? " RWE" : "", - (temp & OHCI_CTRL_RWC) ? " RWC" : "", - (temp & OHCI_CTRL_IR) ? " IR" : "", - hcfs2string (temp & OHCI_CTRL_HCFS), - (temp & OHCI_CTRL_BLE) ? " BLE" : "", - (temp & OHCI_CTRL_CLE) ? " CLE" : "", - (temp & OHCI_CTRL_IE) ? " IE" : "", - (temp & OHCI_CTRL_PLE) ? " PLE" : "", - temp & OHCI_CTRL_CBSR - ); - - temp = readl (®s->cmdstatus); - dbg ("cmdstatus: 0x%08x SOC=%d%s%s%s%s", temp, - (temp & OHCI_SOC) >> 16, - (temp & OHCI_OCR) ? " OCR" : "", - (temp & OHCI_BLF) ? " BLF" : "", - (temp & OHCI_CLF) ? " CLF" : "", - (temp & OHCI_HCR) ? " HCR" : "" - ); - - ohci_dump_intr_mask ("intrstatus", readl (®s->intrstatus)); - ohci_dump_intr_mask ("intrenable", readl (®s->intrenable)); - - maybe_print_eds ("ed_periodcurrent", readl (®s->ed_periodcurrent)); - - maybe_print_eds ("ed_controlhead", readl (®s->ed_controlhead)); - maybe_print_eds ("ed_controlcurrent", readl (®s->ed_controlcurrent)); - - maybe_print_eds ("ed_bulkhead", readl (®s->ed_bulkhead)); - maybe_print_eds ("ed_bulkcurrent", readl (®s->ed_bulkcurrent)); - - maybe_print_eds ("donehead", readl (®s->donehead)); -} - -static void ohci_dump_roothub (ohci_t *controller, int verbose) -{ - __u32 temp, ndp, i; - - temp = roothub_a (controller); - ndp = (temp & RH_A_NDP); -#ifdef CONFIG_AT91C_PQFP_UHPBUG - ndp = (ndp == 2) ? 1:0; -#endif - if (verbose) { - dbg ("roothub.a: %08x POTPGT=%d%s%s%s%s%s NDP=%d", temp, - ((temp & RH_A_POTPGT) >> 24) & 0xff, - (temp & RH_A_NOCP) ? " NOCP" : "", - (temp & RH_A_OCPM) ? " OCPM" : "", - (temp & RH_A_DT) ? " DT" : "", - (temp & RH_A_NPS) ? " NPS" : "", - (temp & RH_A_PSM) ? " PSM" : "", - ndp - ); - temp = roothub_b (controller); - dbg ("roothub.b: %08x PPCM=%04x DR=%04x", - temp, - (temp & RH_B_PPCM) >> 16, - (temp & RH_B_DR) - ); - temp = roothub_status (controller); - dbg ("roothub.status: %08x%s%s%s%s%s%s", - temp, - (temp & RH_HS_CRWE) ? " CRWE" : "", - (temp & RH_HS_OCIC) ? " OCIC" : "", - (temp & RH_HS_LPSC) ? " LPSC" : "", - (temp & RH_HS_DRWE) ? " DRWE" : "", - (temp & RH_HS_OCI) ? " OCI" : "", - (temp & RH_HS_LPS) ? " LPS" : "" - ); - } - - for (i = 0; i < ndp; i++) { - temp = roothub_portstatus (controller, i); - dbg ("roothub.portstatus [%d] = 0x%08x%s%s%s%s%s%s%s%s%s%s%s%s", - i, - temp, - (temp & RH_PS_PRSC) ? " PRSC" : "", - (temp & RH_PS_OCIC) ? " OCIC" : "", - (temp & RH_PS_PSSC) ? " PSSC" : "", - (temp & RH_PS_PESC) ? " PESC" : "", - (temp & RH_PS_CSC) ? " CSC" : "", - - (temp & RH_PS_LSDA) ? " LSDA" : "", - (temp & RH_PS_PPS) ? " PPS" : "", - (temp & RH_PS_PRS) ? " PRS" : "", - (temp & RH_PS_POCI) ? " POCI" : "", - (temp & RH_PS_PSS) ? " PSS" : "", - - (temp & RH_PS_PES) ? " PES" : "", - (temp & RH_PS_CCS) ? " CCS" : "" - ); - } -} - -static void ohci_dump (ohci_t *controller, int verbose) -{ - dbg ("OHCI controller usb-%s state", controller->slot_name); - - /* dumps some of the state we know about */ - ohci_dump_status (controller); - if (verbose) - ep_print_int_eds (controller, "hcca"); - dbg ("hcca frame #%04x", controller->hcca->frame_no); - ohci_dump_roothub (controller, 1); -} - - -#endif /* DEBUG */ - -/*-------------------------------------------------------------------------* - * Interface functions (URB) - *-------------------------------------------------------------------------*/ - -/* get a transfer request */ - -int sohci_submit_job(struct usb_device *dev, unsigned long pipe, void *buffer, - int transfer_len, struct devrequest *setup, int interval) -{ - ohci_t *ohci; - ed_t * ed; - urb_priv_t *purb_priv; - int i, size = 0; - - ohci = &gohci; - - /* when controller's hung, permit only roothub cleanup attempts - * such as powering down ports */ - if (ohci->disabled) { - err("sohci_submit_job: EPIPE"); - return -1; - } - - /* every endpoint has a ed, locate and fill it */ - if (!(ed = ep_add_ed (dev, pipe))) { - err("sohci_submit_job: ENOMEM"); - return -1; - } - - /* for the private part of the URB we need the number of TDs (size) */ - switch (usb_pipetype (pipe)) { - case PIPE_BULK: /* one TD for every 4096 Byte */ - size = (transfer_len - 1) / 4096 + 1; - break; - case PIPE_CONTROL: /* 1 TD for setup, 1 for ACK and 1 for every 4096 B */ - size = (transfer_len == 0)? 2: - (transfer_len - 1) / 4096 + 3; - break; - } - - if (size >= (N_URB_TD - 1)) { - err("need %d TDs, only have %d", size, N_URB_TD); - return -1; - } - purb_priv = &urb_priv; - purb_priv->pipe = pipe; - - /* fill the private part of the URB */ - purb_priv->length = size; - purb_priv->ed = ed; - purb_priv->actual_length = 0; - - /* allocate the TDs */ - /* note that td[0] was allocated in ep_add_ed */ - for (i = 0; i < size; i++) { - purb_priv->td[i] = td_alloc (dev); - if (!purb_priv->td[i]) { - purb_priv->length = i; - urb_free_priv (purb_priv); - err("sohci_submit_job: ENOMEM"); - return -1; - } - } - - if (ed->state == ED_NEW || (ed->state & ED_DEL)) { - urb_free_priv (purb_priv); - err("sohci_submit_job: EINVAL"); - return -1; - } - - /* link the ed into a chain if is not already */ - if (ed->state != ED_OPER) - ep_link (ohci, ed); - - /* fill the TDs and link it to the ed */ - td_submit_job(dev, pipe, buffer, transfer_len, setup, purb_priv, interval); - - return 0; -} - -/*-------------------------------------------------------------------------*/ - -#ifdef DEBUG -/* tell us the current USB frame number */ - -static int sohci_get_current_frame_number (struct usb_device *usb_dev) -{ - ohci_t *ohci = &gohci; - - return m16_swap (ohci->hcca->frame_no); -} -#endif - -/*-------------------------------------------------------------------------* - * ED handling functions - *-------------------------------------------------------------------------*/ - -/* link an ed into one of the HC chains */ - -static int ep_link (ohci_t *ohci, ed_t *edi) -{ - volatile ed_t *ed = edi; - - ed->state = ED_OPER; - - switch (ed->type) { - case PIPE_CONTROL: - ed->hwNextED = 0; - if (ohci->ed_controltail == NULL) { - writel (ed, &ohci->regs->ed_controlhead); - } else { - ohci->ed_controltail->hwNextED = m32_swap (ed); - } - ed->ed_prev = ohci->ed_controltail; - if (!ohci->ed_controltail && !ohci->ed_rm_list[0] && - !ohci->ed_rm_list[1] && !ohci->sleeping) { - ohci->hc_control |= OHCI_CTRL_CLE; - writel (ohci->hc_control, &ohci->regs->control); - } - ohci->ed_controltail = edi; - break; - - case PIPE_BULK: - ed->hwNextED = 0; - if (ohci->ed_bulktail == NULL) { - writel (ed, &ohci->regs->ed_bulkhead); - } else { - ohci->ed_bulktail->hwNextED = m32_swap (ed); - } - ed->ed_prev = ohci->ed_bulktail; - if (!ohci->ed_bulktail && !ohci->ed_rm_list[0] && - !ohci->ed_rm_list[1] && !ohci->sleeping) { - ohci->hc_control |= OHCI_CTRL_BLE; - writel (ohci->hc_control, &ohci->regs->control); - } - ohci->ed_bulktail = edi; - break; - } - return 0; -} - -/*-------------------------------------------------------------------------*/ - -/* unlink an ed from one of the HC chains. - * just the link to the ed is unlinked. - * the link from the ed still points to another operational ed or 0 - * so the HC can eventually finish the processing of the unlinked ed */ - -static int ep_unlink (ohci_t *ohci, ed_t *ed) -{ - ed->hwINFO |= m32_swap (OHCI_ED_SKIP); - - switch (ed->type) { - case PIPE_CONTROL: - if (ed->ed_prev == NULL) { - if (!ed->hwNextED) { - ohci->hc_control &= ~OHCI_CTRL_CLE; - writel (ohci->hc_control, &ohci->regs->control); - } - writel (m32_swap (*((__u32 *)&ed->hwNextED)), &ohci->regs->ed_controlhead); - } else { - ed->ed_prev->hwNextED = ed->hwNextED; - } - if (ohci->ed_controltail == ed) { - ohci->ed_controltail = ed->ed_prev; - } else { - ((ed_t *)m32_swap (*((__u32 *)&ed->hwNextED)))->ed_prev = ed->ed_prev; - } - break; - - case PIPE_BULK: - if (ed->ed_prev == NULL) { - if (!ed->hwNextED) { - ohci->hc_control &= ~OHCI_CTRL_BLE; - writel (ohci->hc_control, &ohci->regs->control); - } - writel (m32_swap (*((__u32 *)&ed->hwNextED)), &ohci->regs->ed_bulkhead); - } else { - ed->ed_prev->hwNextED = ed->hwNextED; - } - if (ohci->ed_bulktail == ed) { - ohci->ed_bulktail = ed->ed_prev; - } else { - ((ed_t *)m32_swap (*((__u32 *)&ed->hwNextED)))->ed_prev = ed->ed_prev; - } - break; - } - ed->state = ED_UNLINK; - return 0; -} - - -/*-------------------------------------------------------------------------*/ - -/* add/reinit an endpoint; this should be done once at the usb_set_configuration command, - * but the USB stack is a little bit stateless so we do it at every transaction - * if the state of the ed is ED_NEW then a dummy td is added and the state is changed to ED_UNLINK - * in all other cases the state is left unchanged - * the ed info fields are setted anyway even though most of them should not change */ - -static ed_t * ep_add_ed (struct usb_device *usb_dev, unsigned long pipe) -{ - td_t *td; - ed_t *ed_ret; - volatile ed_t *ed; - - ed = ed_ret = &ohci_dev.ed[(usb_pipeendpoint (pipe) << 1) | - (usb_pipecontrol (pipe)? 0: usb_pipeout (pipe))]; - - if ((ed->state & ED_DEL) || (ed->state & ED_URB_DEL)) { - err("ep_add_ed: pending delete"); - /* pending delete request */ - return NULL; - } - - if (ed->state == ED_NEW) { - ed->hwINFO = m32_swap (OHCI_ED_SKIP); /* skip ed */ - /* dummy td; end of td list for ed */ - td = td_alloc (usb_dev); - ed->hwTailP = m32_swap (td); - ed->hwHeadP = ed->hwTailP; - ed->state = ED_UNLINK; - ed->type = usb_pipetype (pipe); - ohci_dev.ed_cnt++; - } - - ed->hwINFO = m32_swap (usb_pipedevice (pipe) - | usb_pipeendpoint (pipe) << 7 - | (usb_pipeisoc (pipe)? 0x8000: 0) - | (usb_pipecontrol (pipe)? 0: (usb_pipeout (pipe)? 0x800: 0x1000)) - | usb_pipeslow (pipe) << 13 - | usb_maxpacket (usb_dev, pipe) << 16); - - return ed_ret; -} - -/*-------------------------------------------------------------------------* - * TD handling functions - *-------------------------------------------------------------------------*/ - -/* enqueue next TD for this URB (OHCI spec 5.2.8.2) */ - -static void td_fill (ohci_t *ohci, unsigned int info, - void *data, int len, - struct usb_device *dev, int index, urb_priv_t *urb_priv) -{ - volatile td_t *td, *td_pt; -#ifdef OHCI_FILL_TRACE - int i; -#endif - - if (index > urb_priv->length) { - err("index > length"); - return; - } - /* use this td as the next dummy */ - td_pt = urb_priv->td [index]; - td_pt->hwNextTD = 0; - - /* fill the old dummy TD */ - td = urb_priv->td [index] = (td_t *)(m32_swap (urb_priv->ed->hwTailP) & ~0xf); - - td->ed = urb_priv->ed; - td->next_dl_td = NULL; - td->index = index; - td->data = (__u32)data; -#ifdef OHCI_FILL_TRACE - if ((usb_pipetype(urb_priv->pipe) == PIPE_BULK) && usb_pipeout(urb_priv->pipe)) { - for (i = 0; i < len; i++) - printf("td->data[%d] %#2x ",i, ((unsigned char *)td->data)[i]); - printf("\n"); - } -#endif - if (!len) - data = 0; - - td->hwINFO = m32_swap (info); - td->hwCBP = m32_swap (data); - if (data) - td->hwBE = m32_swap (data + len - 1); - else - td->hwBE = 0; - td->hwNextTD = m32_swap (td_pt); - td->hwPSW [0] = m16_swap (((__u32)data & 0x0FFF) | 0xE000); - - /* append to queue */ - td->ed->hwTailP = td->hwNextTD; -} - -/*-------------------------------------------------------------------------*/ - -/* prepare all TDs of a transfer */ - -static void td_submit_job (struct usb_device *dev, unsigned long pipe, void *buffer, - int transfer_len, struct devrequest *setup, urb_priv_t *urb, int interval) -{ - ohci_t *ohci = &gohci; - int data_len = transfer_len; - void *data; - int cnt = 0; - __u32 info = 0; - unsigned int toggle = 0; - - /* OHCI handles the DATA-toggles itself, we just use the USB-toggle bits for reseting */ - if(usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe))) { - toggle = TD_T_TOGGLE; - } else { - toggle = TD_T_DATA0; - usb_settoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe), 1); - } - urb->td_cnt = 0; - if (data_len) - data = buffer; - else - data = 0; - - switch (usb_pipetype (pipe)) { - case PIPE_BULK: - info = usb_pipeout (pipe)? - TD_CC | TD_DP_OUT : TD_CC | TD_DP_IN ; - while(data_len > 4096) { - td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), data, 4096, dev, cnt, urb); - data += 4096; data_len -= 4096; cnt++; - } - info = usb_pipeout (pipe)? - TD_CC | TD_DP_OUT : TD_CC | TD_R | TD_DP_IN ; - td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), data, data_len, dev, cnt, urb); - cnt++; - - if (!ohci->sleeping) - writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */ - break; - - case PIPE_CONTROL: - info = TD_CC | TD_DP_SETUP | TD_T_DATA0; - td_fill (ohci, info, setup, 8, dev, cnt++, urb); - if (data_len > 0) { - info = usb_pipeout (pipe)? - TD_CC | TD_R | TD_DP_OUT | TD_T_DATA1 : TD_CC | TD_R | TD_DP_IN | TD_T_DATA1; - /* NOTE: mishandles transfers >8K, some >4K */ - td_fill (ohci, info, data, data_len, dev, cnt++, urb); - } - info = usb_pipeout (pipe)? - TD_CC | TD_DP_IN | TD_T_DATA1: TD_CC | TD_DP_OUT | TD_T_DATA1; - td_fill (ohci, info, data, 0, dev, cnt++, urb); - if (!ohci->sleeping) - writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */ - break; - } - if (urb->length != cnt) - dbg("TD LENGTH %d != CNT %d", urb->length, cnt); -} - -/*-------------------------------------------------------------------------* - * Done List handling functions - *-------------------------------------------------------------------------*/ - - -/* calculate the transfer length and update the urb */ - -static void dl_transfer_length(td_t * td) -{ - __u32 tdINFO, tdBE, tdCBP; - urb_priv_t *lurb_priv = &urb_priv; - - tdINFO = m32_swap (td->hwINFO); - tdBE = m32_swap (td->hwBE); - tdCBP = m32_swap (td->hwCBP); - - - if (!(usb_pipetype (lurb_priv->pipe) == PIPE_CONTROL && - ((td->index == 0) || (td->index == lurb_priv->length - 1)))) { - if (tdBE != 0) { - if (td->hwCBP == 0) - lurb_priv->actual_length += tdBE - td->data + 1; - else - lurb_priv->actual_length += tdCBP - td->data; - } - } -} - -/*-------------------------------------------------------------------------*/ - -/* replies to the request have to be on a FIFO basis so - * we reverse the reversed done-list */ - -static td_t * dl_reverse_done_list (ohci_t *ohci) -{ - __u32 td_list_hc; - td_t *td_rev = NULL; - td_t *td_list = NULL; - urb_priv_t *lurb_priv = NULL; - - td_list_hc = m32_swap (ohci->hcca->done_head) & 0xfffffff0; - ohci->hcca->done_head = 0; - - while (td_list_hc) { - td_list = (td_t *)td_list_hc; - - if (TD_CC_GET (m32_swap (td_list->hwINFO))) { - lurb_priv = &urb_priv; - dbg(" USB-error/status: %x : %p", - TD_CC_GET (m32_swap (td_list->hwINFO)), td_list); - if (td_list->ed->hwHeadP & m32_swap (0x1)) { - if (lurb_priv && ((td_list->index + 1) < lurb_priv->length)) { - td_list->ed->hwHeadP = - (lurb_priv->td[lurb_priv->length - 1]->hwNextTD & m32_swap (0xfffffff0)) | - (td_list->ed->hwHeadP & m32_swap (0x2)); - lurb_priv->td_cnt += lurb_priv->length - td_list->index - 1; - } else - td_list->ed->hwHeadP &= m32_swap (0xfffffff2); - } - } - - td_list->next_dl_td = td_rev; - td_rev = td_list; - td_list_hc = m32_swap (td_list->hwNextTD) & 0xfffffff0; - } - return td_list; -} - -/*-------------------------------------------------------------------------*/ - -/* td done list */ -static int dl_done_list (ohci_t *ohci, td_t *td_list) -{ - td_t *td_list_next = NULL; - ed_t *ed; - int cc = 0; - int stat = 0; - /* urb_t *urb; */ - urb_priv_t *lurb_priv; - __u32 tdINFO, edHeadP, edTailP; - - while (td_list) { - td_list_next = td_list->next_dl_td; - - lurb_priv = &urb_priv; - tdINFO = m32_swap (td_list->hwINFO); - - ed = td_list->ed; - - dl_transfer_length(td_list); - - /* error code of transfer */ - cc = TD_CC_GET (tdINFO); - if (cc != 0) { - dbg("ConditionCode %#x", cc); - stat = cc_to_error[cc]; - } - - if (ed->state != ED_NEW) { - edHeadP = m32_swap (ed->hwHeadP) & 0xfffffff0; - edTailP = m32_swap (ed->hwTailP); - - /* unlink eds if they are not busy */ - if ((edHeadP == edTailP) && (ed->state == ED_OPER)) - ep_unlink (ohci, ed); - } - - td_list = td_list_next; - } - return stat; -} - -/*-------------------------------------------------------------------------* - * Virtual Root Hub - *-------------------------------------------------------------------------*/ - -/* Device descriptor */ -static __u8 root_hub_dev_des[] = -{ - 0x12, /* __u8 bLength; */ - 0x01, /* __u8 bDescriptorType; Device */ - 0x10, /* __u16 bcdUSB; v1.1 */ - 0x01, - 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ - 0x00, /* __u8 bDeviceSubClass; */ - 0x00, /* __u8 bDeviceProtocol; */ - 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */ - 0x00, /* __u16 idVendor; */ - 0x00, - 0x00, /* __u16 idProduct; */ - 0x00, - 0x00, /* __u16 bcdDevice; */ - 0x00, - 0x00, /* __u8 iManufacturer; */ - 0x01, /* __u8 iProduct; */ - 0x00, /* __u8 iSerialNumber; */ - 0x01 /* __u8 bNumConfigurations; */ -}; - - -/* Configuration descriptor */ -static __u8 root_hub_config_des[] = -{ - 0x09, /* __u8 bLength; */ - 0x02, /* __u8 bDescriptorType; Configuration */ - 0x19, /* __u16 wTotalLength; */ - 0x00, - 0x01, /* __u8 bNumInterfaces; */ - 0x01, /* __u8 bConfigurationValue; */ - 0x00, /* __u8 iConfiguration; */ - 0x40, /* __u8 bmAttributes; - Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup, 4..0: resvd */ - 0x00, /* __u8 MaxPower; */ - - /* interface */ - 0x09, /* __u8 if_bLength; */ - 0x04, /* __u8 if_bDescriptorType; Interface */ - 0x00, /* __u8 if_bInterfaceNumber; */ - 0x00, /* __u8 if_bAlternateSetting; */ - 0x01, /* __u8 if_bNumEndpoints; */ - 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */ - 0x00, /* __u8 if_bInterfaceSubClass; */ - 0x00, /* __u8 if_bInterfaceProtocol; */ - 0x00, /* __u8 if_iInterface; */ - - /* endpoint */ - 0x07, /* __u8 ep_bLength; */ - 0x05, /* __u8 ep_bDescriptorType; Endpoint */ - 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ - 0x03, /* __u8 ep_bmAttributes; Interrupt */ - 0x02, /* __u16 ep_wMaxPacketSize; ((MAX_ROOT_PORTS + 1) / 8 */ - 0x00, - 0xff /* __u8 ep_bInterval; 255 ms */ -}; - -static unsigned char root_hub_str_index0[] = -{ - 0x04, /* __u8 bLength; */ - 0x03, /* __u8 bDescriptorType; String-descriptor */ - 0x09, /* __u8 lang ID */ - 0x04, /* __u8 lang ID */ -}; - -static unsigned char root_hub_str_index1[] = -{ - 28, /* __u8 bLength; */ - 0x03, /* __u8 bDescriptorType; String-descriptor */ - 'O', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - 'H', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - 'C', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - 'I', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - ' ', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - 'R', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - 'o', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - 'o', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - 't', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - ' ', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - 'H', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - 'u', /* __u8 Unicode */ - 0, /* __u8 Unicode */ - 'b', /* __u8 Unicode */ - 0, /* __u8 Unicode */ -}; - -/* Hub class-specific descriptor is constructed dynamically */ - - -/*-------------------------------------------------------------------------*/ - -#define OK(x) len = (x); break -#ifdef DEBUG -#define WR_RH_STAT(x) {info("WR:status %#8x", (x));writel((x), &gohci.regs->roothub.status);} -#define WR_RH_PORTSTAT(x) {info("WR:portstatus[%d] %#8x", wIndex-1, (x));writel((x), &gohci.regs->roothub.portstatus[wIndex-1]);} -#else -#define WR_RH_STAT(x) writel((x), &gohci.regs->roothub.status) -#define WR_RH_PORTSTAT(x) writel((x), &gohci.regs->roothub.portstatus[wIndex-1]) -#endif -#define RD_RH_STAT roothub_status(&gohci) -#define RD_RH_PORTSTAT roothub_portstatus(&gohci,wIndex-1) - -/* request to virtual root hub */ - -int rh_check_port_status(ohci_t *controller) -{ - __u32 temp, ndp, i; - int res; - - res = -1; - temp = roothub_a (controller); - ndp = (temp & RH_A_NDP); -#ifdef CONFIG_AT91C_PQFP_UHPBUG - ndp = (ndp == 2) ? 1:0; -#endif - - for (i = 0; i < ndp; i++) { - temp = roothub_portstatus (controller, i); - /* check for a device disconnect */ - if (((temp & (RH_PS_PESC | RH_PS_CSC)) == - (RH_PS_PESC | RH_PS_CSC)) && - ((temp & RH_PS_CCS) == 0)) { - res = i; - break; - } - } - return res; -} - -static int ohci_submit_rh_msg(struct usb_device *dev, unsigned long pipe, - void *buffer, int transfer_len, struct devrequest *cmd) -{ - void * data = buffer; - int leni = transfer_len; - int len = 0; - int stat = 0; - __u32 datab[4]; - __u8 *data_buf = (__u8 *)datab; - __u16 bmRType_bReq; - __u16 wValue; - __u16 wIndex; - __u16 wLength; - -#ifdef DEBUG -urb_priv.actual_length = 0; -pkt_print(dev, pipe, buffer, transfer_len, cmd, "SUB(rh)", usb_pipein(pipe)); -#else - wait_ms(1); -#endif - if ((pipe & PIPE_INTERRUPT) == PIPE_INTERRUPT) { - info("Root-Hub submit IRQ: NOT implemented"); - return 0; - } - - bmRType_bReq = cmd->requesttype | (cmd->request << 8); - wValue = m16_swap (cmd->value); - wIndex = m16_swap (cmd->index); - wLength = m16_swap (cmd->length); - - info("Root-Hub: adr: %2x cmd(%1x): %08x %04x %04x %04x", - dev->devnum, 8, bmRType_bReq, wValue, wIndex, wLength); - - switch (bmRType_bReq) { - /* Request Destination: - without flags: Device, - RH_INTERFACE: interface, - RH_ENDPOINT: endpoint, - RH_CLASS means HUB here, - RH_OTHER | RH_CLASS almost ever means HUB_PORT here - */ - - case RH_GET_STATUS: - *(__u16 *) data_buf = m16_swap (1); OK (2); - case RH_GET_STATUS | RH_INTERFACE: - *(__u16 *) data_buf = m16_swap (0); OK (2); - case RH_GET_STATUS | RH_ENDPOINT: - *(__u16 *) data_buf = m16_swap (0); OK (2); - case RH_GET_STATUS | RH_CLASS: - *(__u32 *) data_buf = m32_swap ( - RD_RH_STAT & ~(RH_HS_CRWE | RH_HS_DRWE)); - OK (4); - case RH_GET_STATUS | RH_OTHER | RH_CLASS: - *(__u32 *) data_buf = m32_swap (RD_RH_PORTSTAT); OK (4); - - case RH_CLEAR_FEATURE | RH_ENDPOINT: - switch (wValue) { - case (RH_ENDPOINT_STALL): OK (0); - } - break; - - case RH_CLEAR_FEATURE | RH_CLASS: - switch (wValue) { - case RH_C_HUB_LOCAL_POWER: - OK(0); - case (RH_C_HUB_OVER_CURRENT): - WR_RH_STAT(RH_HS_OCIC); OK (0); - } - break; - - case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS: - switch (wValue) { - case (RH_PORT_ENABLE): - WR_RH_PORTSTAT (RH_PS_CCS ); OK (0); - case (RH_PORT_SUSPEND): - WR_RH_PORTSTAT (RH_PS_POCI); OK (0); - case (RH_PORT_POWER): - WR_RH_PORTSTAT (RH_PS_LSDA); OK (0); - case (RH_C_PORT_CONNECTION): - WR_RH_PORTSTAT (RH_PS_CSC ); OK (0); - case (RH_C_PORT_ENABLE): - WR_RH_PORTSTAT (RH_PS_PESC); OK (0); - case (RH_C_PORT_SUSPEND): - WR_RH_PORTSTAT (RH_PS_PSSC); OK (0); - case (RH_C_PORT_OVER_CURRENT): - WR_RH_PORTSTAT (RH_PS_OCIC); OK (0); - case (RH_C_PORT_RESET): - WR_RH_PORTSTAT (RH_PS_PRSC); OK (0); - } - break; - - case RH_SET_FEATURE | RH_OTHER | RH_CLASS: - switch (wValue) { - case (RH_PORT_SUSPEND): - WR_RH_PORTSTAT (RH_PS_PSS ); OK (0); - case (RH_PORT_RESET): /* BUG IN HUP CODE *********/ - if (RD_RH_PORTSTAT & RH_PS_CCS) - WR_RH_PORTSTAT (RH_PS_PRS); - OK (0); - case (RH_PORT_POWER): - WR_RH_PORTSTAT (RH_PS_PPS ); OK (0); - case (RH_PORT_ENABLE): /* BUG IN HUP CODE *********/ - if (RD_RH_PORTSTAT & RH_PS_CCS) - WR_RH_PORTSTAT (RH_PS_PES ); - OK (0); - } - break; - - case RH_SET_ADDRESS: gohci.rh.devnum = wValue; OK(0); - - case RH_GET_DESCRIPTOR: - switch ((wValue & 0xff00) >> 8) { - case (0x01): /* device descriptor */ - len = min_t(unsigned int, - leni, - min_t(unsigned int, - sizeof (root_hub_dev_des), - wLength)); - data_buf = root_hub_dev_des; OK(len); - case (0x02): /* configuration descriptor */ - len = min_t(unsigned int, - leni, - min_t(unsigned int, - sizeof (root_hub_config_des), - wLength)); - data_buf = root_hub_config_des; OK(len); - case (0x03): /* string descriptors */ - if(wValue==0x0300) { - len = min_t(unsigned int, - leni, - min_t(unsigned int, - sizeof (root_hub_str_index0), - wLength)); - data_buf = root_hub_str_index0; - OK(len); - } - if(wValue==0x0301) { - len = min_t(unsigned int, - leni, - min_t(unsigned int, - sizeof (root_hub_str_index1), - wLength)); - data_buf = root_hub_str_index1; - OK(len); - } - default: - stat = USB_ST_STALLED; - } - break; - - case RH_GET_DESCRIPTOR | RH_CLASS: - { - __u32 temp = roothub_a (&gohci); - - data_buf [0] = 9; /* min length; */ - data_buf [1] = 0x29; - data_buf [2] = temp & RH_A_NDP; -#ifdef CONFIG_AT91C_PQFP_UHPBUG - data_buf [2] = (data_buf [2] == 2) ? 1:0; -#endif - data_buf [3] = 0; - if (temp & RH_A_PSM) /* per-port power switching? */ - data_buf [3] |= 0x1; - if (temp & RH_A_NOCP) /* no overcurrent reporting? */ - data_buf [3] |= 0x10; - else if (temp & RH_A_OCPM) /* per-port overcurrent reporting? */ - data_buf [3] |= 0x8; - - /* corresponds to data_buf[4-7] */ - datab [1] = 0; - data_buf [5] = (temp & RH_A_POTPGT) >> 24; - temp = roothub_b (&gohci); - data_buf [7] = temp & RH_B_DR; - if (data_buf [2] < 7) { - data_buf [8] = 0xff; - } else { - data_buf [0] += 2; - data_buf [8] = (temp & RH_B_DR) >> 8; - data_buf [10] = data_buf [9] = 0xff; - } - - len = min_t(unsigned int, leni, - min_t(unsigned int, data_buf [0], wLength)); - OK (len); - } - - case RH_GET_CONFIGURATION: *(__u8 *) data_buf = 0x01; OK (1); - - case RH_SET_CONFIGURATION: WR_RH_STAT (0x10000); OK (0); - - default: - dbg ("unsupported root hub command"); - stat = USB_ST_STALLED; - } - -#ifdef DEBUG - ohci_dump_roothub (&gohci, 1); -#else - wait_ms(1); -#endif - - len = min_t(int, len, leni); - if (data != data_buf) - memcpy (data, data_buf, len); - dev->act_len = len; - dev->status = stat; - -#ifdef DEBUG - if (transfer_len) - urb_priv.actual_length = transfer_len; - pkt_print(dev, pipe, buffer, transfer_len, cmd, "RET(rh)", 0/*usb_pipein(pipe)*/); -#else - wait_ms(1); -#endif - - return stat; -} - -/*-------------------------------------------------------------------------*/ - -/* common code for handling submit messages - used for all but root hub */ -/* accesses. */ -int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer, - int transfer_len, struct devrequest *setup, int interval) -{ - int stat = 0; - int maxsize = usb_maxpacket(dev, pipe); - int timeout; - - /* device pulled? Shortcut the action. */ - if (devgone == dev) { - dev->status = USB_ST_CRC_ERR; - return 0; - } - -#ifdef DEBUG - urb_priv.actual_length = 0; - pkt_print(dev, pipe, buffer, transfer_len, setup, "SUB", usb_pipein(pipe)); -#else - wait_ms(1); -#endif - if (!maxsize) { - err("submit_common_message: pipesize for pipe %lx is zero", - pipe); - return -1; - } - - if (sohci_submit_job(dev, pipe, buffer, transfer_len, setup, interval) < 0) { - err("sohci_submit_job failed"); - return -1; - } - - wait_ms(10); - /* ohci_dump_status(&gohci); */ - - /* allow more time for a BULK device to react - some are slow */ -#define BULK_TO 5000 /* timeout in milliseconds */ - if (usb_pipetype (pipe) == PIPE_BULK) - timeout = BULK_TO; - else - timeout = 100; - - /* wait for it to complete */ - for (;;) { - /* check whether the controller is done */ - stat = hc_interrupt(); - if (stat < 0) { - stat = USB_ST_CRC_ERR; - break; - } - if (stat >= 0 && stat != 0xff) { - /* 0xff is returned for an SF-interrupt */ - break; - } - if (--timeout) { - wait_ms(1); - } else { - err("CTL:TIMEOUT "); - stat = USB_ST_CRC_ERR; - break; - } - } - /* we got an Root Hub Status Change interrupt */ - if (got_rhsc) { -#ifdef DEBUG - ohci_dump_roothub (&gohci, 1); -#endif - got_rhsc = 0; - /* abuse timeout */ - timeout = rh_check_port_status(&gohci); - if (timeout >= 0) { -#if 0 /* this does nothing useful, but leave it here in case that changes */ - /* the called routine adds 1 to the passed value */ - usb_hub_port_connect_change(gohci.rh.dev, timeout - 1); -#endif - /* - * XXX - * This is potentially dangerous because it assumes - * that only one device is ever plugged in! - */ - devgone = dev; - } - } - - dev->status = stat; - dev->act_len = transfer_len; - -#ifdef DEBUG - pkt_print(dev, pipe, buffer, transfer_len, setup, "RET(ctlr)", usb_pipein(pipe)); -#else - wait_ms(1); -#endif - - /* free TDs in urb_priv */ - urb_free_priv (&urb_priv); - return 0; -} - -/* submit routines called from usb.c */ -int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, - int transfer_len) -{ - info("submit_bulk_msg"); - return submit_common_msg(dev, pipe, buffer, transfer_len, NULL, 0); -} - -int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, - int transfer_len, struct devrequest *setup) -{ - int maxsize = usb_maxpacket(dev, pipe); - - info("submit_control_msg"); -#ifdef DEBUG - urb_priv.actual_length = 0; - pkt_print(dev, pipe, buffer, transfer_len, setup, "SUB", usb_pipein(pipe)); -#else - wait_ms(1); -#endif - if (!maxsize) { - err("submit_control_message: pipesize for pipe %lx is zero", - pipe); - return -1; - } - if (((pipe >> 8) & 0x7f) == gohci.rh.devnum) { - gohci.rh.dev = dev; - /* root hub - redirect */ - return ohci_submit_rh_msg(dev, pipe, buffer, transfer_len, - setup); - } - - return submit_common_msg(dev, pipe, buffer, transfer_len, setup, 0); -} - -int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, - int transfer_len, int interval) -{ - info("submit_int_msg"); - return -1; -} - -/*-------------------------------------------------------------------------* - * HC functions - *-------------------------------------------------------------------------*/ - -/* reset the HC and BUS */ - -static int hc_reset (ohci_t *ohci) -{ - int timeout = 30; - int smm_timeout = 50; /* 0,5 sec */ - - dbg("%s\n", __FUNCTION__); - - if (readl (&ohci->regs->control) & OHCI_CTRL_IR) { /* SMM owns the HC */ - writel (OHCI_OCR, &ohci->regs->cmdstatus); /* request ownership */ - info("USB HC TakeOver from SMM"); - while (readl (&ohci->regs->control) & OHCI_CTRL_IR) { - wait_ms (10); - if (--smm_timeout == 0) { - err("USB HC TakeOver failed!"); - return -1; - } - } - } - - /* Disable HC interrupts */ - writel (OHCI_INTR_MIE, &ohci->regs->intrdisable); - - dbg("USB HC reset_hc usb-%s: ctrl = 0x%X ;\n", - ohci->slot_name, - readl(&ohci->regs->control)); - - /* Reset USB (needed by some controllers) */ - writel (0, &ohci->regs->control); - - /* HC Reset requires max 10 us delay */ - writel (OHCI_HCR, &ohci->regs->cmdstatus); - while ((readl (&ohci->regs->cmdstatus) & OHCI_HCR) != 0) { - if (--timeout == 0) { - err("USB HC reset timed out!"); - return -1; - } - udelay (1); - } - return 0; -} - -/*-------------------------------------------------------------------------*/ - -/* Start an OHCI controller, set the BUS operational - * enable interrupts - * connect the virtual root hub */ - -static int hc_start (ohci_t * ohci) -{ - __u32 mask; - unsigned int fminterval; - - ohci->disabled = 1; - - /* Tell the controller where the control and bulk lists are - * The lists are empty now. */ - - writel (0, &ohci->regs->ed_controlhead); - writel (0, &ohci->regs->ed_bulkhead); - - writel ((__u32)ohci->hcca, &ohci->regs->hcca); /* a reset clears this */ - - fminterval = 0x2edf; - writel ((fminterval * 9) / 10, &ohci->regs->periodicstart); - fminterval |= ((((fminterval - 210) * 6) / 7) << 16); - writel (fminterval, &ohci->regs->fminterval); - writel (0x628, &ohci->regs->lsthresh); - - /* start controller operations */ - ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER; - ohci->disabled = 0; - writel (ohci->hc_control, &ohci->regs->control); - - /* disable all interrupts */ - mask = (OHCI_INTR_SO | OHCI_INTR_WDH | OHCI_INTR_SF | OHCI_INTR_RD | - OHCI_INTR_UE | OHCI_INTR_FNO | OHCI_INTR_RHSC | - OHCI_INTR_OC | OHCI_INTR_MIE); - writel (mask, &ohci->regs->intrdisable); - /* clear all interrupts */ - mask &= ~OHCI_INTR_MIE; - writel (mask, &ohci->regs->intrstatus); - /* Choose the interrupts we care about now - but w/o MIE */ - mask = OHCI_INTR_RHSC | OHCI_INTR_UE | OHCI_INTR_WDH | OHCI_INTR_SO; - writel (mask, &ohci->regs->intrenable); - -#ifdef OHCI_USE_NPS - /* required for AMD-756 and some Mac platforms */ - writel ((roothub_a (ohci) | RH_A_NPS) & ~RH_A_PSM, - &ohci->regs->roothub.a); - writel (RH_HS_LPSC, &ohci->regs->roothub.status); -#endif /* OHCI_USE_NPS */ - -#define mdelay(n) ({unsigned long msec=(n); while (msec--) udelay(1000);}) - /* POTPGT delay is bits 24-31, in 2 ms units. */ - mdelay ((roothub_a (ohci) >> 23) & 0x1fe); - - /* connect the virtual root hub */ - ohci->rh.devnum = 0; - - return 0; -} - -/*-------------------------------------------------------------------------*/ - -/* an interrupt happens */ - -static int -hc_interrupt (void) -{ - ohci_t *ohci = &gohci; - struct ohci_regs *regs = ohci->regs; - int ints; - int stat = -1; - - if ((ohci->hcca->done_head != 0) && !(m32_swap (ohci->hcca->done_head) & 0x01)) { - ints = OHCI_INTR_WDH; - } else { - ints = readl (®s->intrstatus); - } - - /* dbg("Interrupt: %x frame: %x", ints, le16_to_cpu (ohci->hcca->frame_no)); */ - - if (ints & OHCI_INTR_RHSC) { - got_rhsc = 1; - } - - if (ints & OHCI_INTR_UE) { - ohci->disabled++; - err ("OHCI Unrecoverable Error, controller usb-%s disabled", - ohci->slot_name); - /* e.g. due to PCI Master/Target Abort */ - -#ifdef DEBUG - ohci_dump (ohci, 1); -#else - wait_ms(1); -#endif - /* FIXME: be optimistic, hope that bug won't repeat often. */ - /* Make some non-interrupt context restart the controller. */ - /* Count and limit the retries though; either hardware or */ - /* software errors can go forever... */ - hc_reset (ohci); - return -1; - } - - if (ints & OHCI_INTR_WDH) { - wait_ms(1); - writel (OHCI_INTR_WDH, ®s->intrdisable); - stat = dl_done_list (&gohci, dl_reverse_done_list (&gohci)); - writel (OHCI_INTR_WDH, ®s->intrenable); - } - - if (ints & OHCI_INTR_SO) { - dbg("USB Schedule overrun\n"); - writel (OHCI_INTR_SO, ®s->intrenable); - stat = -1; - } - - /* FIXME: this assumes SOF (1/ms) interrupts don't get lost... */ - if (ints & OHCI_INTR_SF) { - unsigned int frame = m16_swap (ohci->hcca->frame_no) & 1; - wait_ms(1); - writel (OHCI_INTR_SF, ®s->intrdisable); - if (ohci->ed_rm_list[frame] != NULL) - writel (OHCI_INTR_SF, ®s->intrenable); - stat = 0xff; - } - - writel (ints, ®s->intrstatus); - return stat; -} - -/*-------------------------------------------------------------------------*/ - -/*-------------------------------------------------------------------------*/ - -/* De-allocate all resources.. */ - -static void hc_release_ohci (ohci_t *ohci) -{ - dbg ("USB HC release ohci usb-%s", ohci->slot_name); - - if (!ohci->disabled) - hc_reset (ohci); -} - -/*-------------------------------------------------------------------------*/ - -/* - * low level initalisation routine, called from usb.c - */ -static char ohci_inited = 0; - -int usb_lowlevel_init(void) -{ - /* - * Enable USB host clock. - */ - *AT91C_PMC_SCER = AT91C_PMC_UHP; /* 48MHz clock enabled for UHP */ - *AT91C_PMC_PCER = 1 << AT91C_ID_UHP; /* Peripheral Clock Enable Register */ - - memset (&gohci, 0, sizeof (ohci_t)); - memset (&urb_priv, 0, sizeof (urb_priv_t)); - - /* align the storage */ - if ((__u32)&ghcca[0] & 0xff) { - err("HCCA not aligned!!"); - return -1; - } - phcca = &ghcca[0]; - info("aligned ghcca %p", phcca); - memset(&ohci_dev, 0, sizeof(struct ohci_device)); - if ((__u32)&ohci_dev.ed[0] & 0x7) { - err("EDs not aligned!!"); - return -1; - } - memset(gtd, 0, sizeof(td_t) * (NUM_TD + 1)); - if ((__u32)gtd & 0x7) { - err("TDs not aligned!!"); - return -1; - } - ptd = gtd; - gohci.hcca = phcca; - memset (phcca, 0, sizeof (struct ohci_hcca)); - - gohci.disabled = 1; - gohci.sleeping = 0; - gohci.irq = -1; - gohci.regs = (struct ohci_regs *)AT91_USB_HOST_BASE; - - gohci.flags = 0; - gohci.slot_name = "at91rm9200"; - - if (hc_reset (&gohci) < 0) { - hc_release_ohci (&gohci); - /* Initialization failed */ - *AT91C_PMC_PCER = AT91C_ID_UHP; - *AT91C_PMC_SCDR = 1 << AT91C_PMC_UHP; /* 48MHz clock disabled for UHP */ - return -1; - } - - /* FIXME this is a second HC reset; why?? */ -/* writel (gohci.hc_control = OHCI_USB_RESET, &gohci.regs->control); - wait_ms (10);*/ - - if (hc_start (&gohci) < 0) { - err ("can't start usb-%s", gohci.slot_name); - hc_release_ohci (&gohci); - /* Initialization failed */ - *AT91C_PMC_PCER = AT91C_ID_UHP; - *AT91C_PMC_SCDR = 1 << AT91C_PMC_UHP; /* 48MHz clock disabled for UHP */ - return -1; - } - -#ifdef DEBUG - ohci_dump (&gohci, 1); -#else - wait_ms(1); -#endif - ohci_inited = 1; - return 0; -} - -int usb_lowlevel_stop(void) -{ - /* this gets called really early - before the controller has */ - /* even been initialized! */ - if (!ohci_inited) - return 0; - /* TODO release any interrupts, etc. */ - /* call hc_release_ohci() here ? */ - hc_reset (&gohci); - /* may not want to do this */ - *AT91C_PMC_PCER = 1 << AT91C_ID_UHP; - *AT91C_PMC_SCDR = 1 << AT91C_PMC_UHP; /* 48MHz clock disabled for UHP */ - return 0; -} - -#endif /* CONFIG_USB_OHCI */ |