mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1370 lines
36 KiB
1370 lines
36 KiB
// SPDX-License-Identifier: GPL-1.0+ |
|
/* |
|
* Open Host Controller Interface (OHCI) driver for USB. |
|
* |
|
* Maintainer: Alan Stern <[email protected]> |
|
* |
|
* (C) Copyright 1999 Roman Weissgaerber <[email protected]> |
|
* (C) Copyright 2000-2004 David Brownell <[email protected]> |
|
* |
|
* [ Initialisation is based on Linus' ] |
|
* [ uhci code and gregs ohci fragments ] |
|
* [ (C) Copyright 1999 Linus Torvalds ] |
|
* [ (C) Copyright 1999 Gregory P. Smith] |
|
* |
|
* |
|
* OHCI is the main "non-Intel/VIA" standard for USB 1.1 host controller |
|
* interfaces (though some non-x86 Intel chips use it). It supports |
|
* smarter hardware than UHCI. A download link for the spec available |
|
* through the https://www.usb.org website. |
|
* |
|
* This file is licenced under the GPL. |
|
*/ |
|
|
|
#include <linux/module.h> |
|
#include <linux/moduleparam.h> |
|
#include <linux/pci.h> |
|
#include <linux/kernel.h> |
|
#include <linux/delay.h> |
|
#include <linux/ioport.h> |
|
#include <linux/sched.h> |
|
#include <linux/slab.h> |
|
#include <linux/errno.h> |
|
#include <linux/init.h> |
|
#include <linux/timer.h> |
|
#include <linux/list.h> |
|
#include <linux/usb.h> |
|
#include <linux/usb/otg.h> |
|
#include <linux/usb/hcd.h> |
|
#include <linux/dma-mapping.h> |
|
#include <linux/dmapool.h> |
|
#include <linux/workqueue.h> |
|
#include <linux/debugfs.h> |
|
#include <linux/genalloc.h> |
|
|
|
#include <asm/io.h> |
|
#include <asm/irq.h> |
|
#include <asm/unaligned.h> |
|
#include <asm/byteorder.h> |
|
|
|
|
|
#define DRIVER_AUTHOR "Roman Weissgaerber, David Brownell" |
|
#define DRIVER_DESC "USB 1.1 'Open' Host Controller (OHCI) Driver" |
|
|
|
/*-------------------------------------------------------------------------*/ |
|
|
|
/* For initializing controller (mask in an HCFS mode too) */ |
|
#define OHCI_CONTROL_INIT OHCI_CTRL_CBSR |
|
#define OHCI_INTR_INIT \ |
|
(OHCI_INTR_MIE | OHCI_INTR_RHSC | OHCI_INTR_UE \ |
|
| OHCI_INTR_RD | OHCI_INTR_WDH) |
|
|
|
#ifdef __hppa__ |
|
/* On PA-RISC, PDC can leave IR set incorrectly; ignore it there. */ |
|
#define IR_DISABLE |
|
#endif |
|
|
|
#ifdef CONFIG_ARCH_OMAP |
|
/* OMAP doesn't support IR (no SMM; not needed) */ |
|
#define IR_DISABLE |
|
#endif |
|
|
|
/*-------------------------------------------------------------------------*/ |
|
|
|
static const char hcd_name [] = "ohci_hcd"; |
|
|
|
#define STATECHANGE_DELAY msecs_to_jiffies(300) |
|
#define IO_WATCHDOG_DELAY msecs_to_jiffies(275) |
|
#define IO_WATCHDOG_OFF 0xffffff00 |
|
|
|
#include "ohci.h" |
|
#include "pci-quirks.h" |
|
|
|
static void ohci_dump(struct ohci_hcd *ohci); |
|
static void ohci_stop(struct usb_hcd *hcd); |
|
static void io_watchdog_func(struct timer_list *t); |
|
|
|
#include "ohci-hub.c" |
|
#include "ohci-dbg.c" |
|
#include "ohci-mem.c" |
|
#include "ohci-q.c" |
|
|
|
|
|
/* |
|
* On architectures with edge-triggered interrupts we must never return |
|
* IRQ_NONE. |
|
*/ |
|
#if defined(CONFIG_SA1111) /* ... or other edge-triggered systems */ |
|
#define IRQ_NOTMINE IRQ_HANDLED |
|
#else |
|
#define IRQ_NOTMINE IRQ_NONE |
|
#endif |
|
|
|
|
|
/* Some boards misreport power switching/overcurrent */ |
|
static bool distrust_firmware; |
|
module_param (distrust_firmware, bool, 0); |
|
MODULE_PARM_DESC (distrust_firmware, |
|
"true to distrust firmware power/overcurrent setup"); |
|
|
|
/* Some boards leave IR set wrongly, since they fail BIOS/SMM handshakes */ |
|
static bool no_handshake; |
|
module_param (no_handshake, bool, 0); |
|
MODULE_PARM_DESC (no_handshake, "true (not default) disables BIOS handshake"); |
|
|
|
/*-------------------------------------------------------------------------*/ |
|
|
|
static int number_of_tds(struct urb *urb) |
|
{ |
|
int len, i, num, this_sg_len; |
|
struct scatterlist *sg; |
|
|
|
len = urb->transfer_buffer_length; |
|
i = urb->num_mapped_sgs; |
|
|
|
if (len > 0 && i > 0) { /* Scatter-gather transfer */ |
|
num = 0; |
|
sg = urb->sg; |
|
for (;;) { |
|
this_sg_len = min_t(int, sg_dma_len(sg), len); |
|
num += DIV_ROUND_UP(this_sg_len, 4096); |
|
len -= this_sg_len; |
|
if (--i <= 0 || len <= 0) |
|
break; |
|
sg = sg_next(sg); |
|
} |
|
|
|
} else { /* Non-SG transfer */ |
|
/* one TD for every 4096 Bytes (could be up to 8K) */ |
|
num = DIV_ROUND_UP(len, 4096); |
|
} |
|
return num; |
|
} |
|
|
|
/* |
|
* queue up an urb for anything except the root hub |
|
*/ |
|
static int ohci_urb_enqueue ( |
|
struct usb_hcd *hcd, |
|
struct urb *urb, |
|
gfp_t mem_flags |
|
) { |
|
struct ohci_hcd *ohci = hcd_to_ohci (hcd); |
|
struct ed *ed; |
|
urb_priv_t *urb_priv; |
|
unsigned int pipe = urb->pipe; |
|
int i, size = 0; |
|
unsigned long flags; |
|
int retval = 0; |
|
|
|
/* every endpoint has a ed, locate and maybe (re)initialize it */ |
|
ed = ed_get(ohci, urb->ep, urb->dev, pipe, urb->interval); |
|
if (! ed) |
|
return -ENOMEM; |
|
|
|
/* for the private part of the URB we need the number of TDs (size) */ |
|
switch (ed->type) { |
|
case PIPE_CONTROL: |
|
/* td_submit_urb() doesn't yet handle these */ |
|
if (urb->transfer_buffer_length > 4096) |
|
return -EMSGSIZE; |
|
|
|
/* 1 TD for setup, 1 for ACK, plus ... */ |
|
size = 2; |
|
fallthrough; |
|
// case PIPE_INTERRUPT: |
|
// case PIPE_BULK: |
|
default: |
|
size += number_of_tds(urb); |
|
/* maybe a zero-length packet to wrap it up */ |
|
if (size == 0) |
|
size++; |
|
else if ((urb->transfer_flags & URB_ZERO_PACKET) != 0 |
|
&& (urb->transfer_buffer_length |
|
% usb_maxpacket (urb->dev, pipe, |
|
usb_pipeout (pipe))) == 0) |
|
size++; |
|
break; |
|
case PIPE_ISOCHRONOUS: /* number of packets from URB */ |
|
size = urb->number_of_packets; |
|
break; |
|
} |
|
|
|
/* allocate the private part of the URB */ |
|
urb_priv = kzalloc (sizeof (urb_priv_t) + size * sizeof (struct td *), |
|
mem_flags); |
|
if (!urb_priv) |
|
return -ENOMEM; |
|
INIT_LIST_HEAD (&urb_priv->pending); |
|
urb_priv->length = size; |
|
urb_priv->ed = ed; |
|
|
|
/* allocate the TDs (deferring hash chain updates) */ |
|
for (i = 0; i < size; i++) { |
|
urb_priv->td [i] = td_alloc (ohci, mem_flags); |
|
if (!urb_priv->td [i]) { |
|
urb_priv->length = i; |
|
urb_free_priv (ohci, urb_priv); |
|
return -ENOMEM; |
|
} |
|
} |
|
|
|
spin_lock_irqsave (&ohci->lock, flags); |
|
|
|
/* don't submit to a dead HC */ |
|
if (!HCD_HW_ACCESSIBLE(hcd)) { |
|
retval = -ENODEV; |
|
goto fail; |
|
} |
|
if (ohci->rh_state != OHCI_RH_RUNNING) { |
|
retval = -ENODEV; |
|
goto fail; |
|
} |
|
retval = usb_hcd_link_urb_to_ep(hcd, urb); |
|
if (retval) |
|
goto fail; |
|
|
|
/* schedule the ed if needed */ |
|
if (ed->state == ED_IDLE) { |
|
retval = ed_schedule (ohci, ed); |
|
if (retval < 0) { |
|
usb_hcd_unlink_urb_from_ep(hcd, urb); |
|
goto fail; |
|
} |
|
|
|
/* Start up the I/O watchdog timer, if it's not running */ |
|
if (ohci->prev_frame_no == IO_WATCHDOG_OFF && |
|
list_empty(&ohci->eds_in_use) && |
|
!(ohci->flags & OHCI_QUIRK_QEMU)) { |
|
ohci->prev_frame_no = ohci_frame_no(ohci); |
|
mod_timer(&ohci->io_watchdog, |
|
jiffies + IO_WATCHDOG_DELAY); |
|
} |
|
list_add(&ed->in_use_list, &ohci->eds_in_use); |
|
|
|
if (ed->type == PIPE_ISOCHRONOUS) { |
|
u16 frame = ohci_frame_no(ohci); |
|
|
|
/* delay a few frames before the first TD */ |
|
frame += max_t (u16, 8, ed->interval); |
|
frame &= ~(ed->interval - 1); |
|
frame |= ed->branch; |
|
urb->start_frame = frame; |
|
ed->last_iso = frame + ed->interval * (size - 1); |
|
} |
|
} else if (ed->type == PIPE_ISOCHRONOUS) { |
|
u16 next = ohci_frame_no(ohci) + 1; |
|
u16 frame = ed->last_iso + ed->interval; |
|
u16 length = ed->interval * (size - 1); |
|
|
|
/* Behind the scheduling threshold? */ |
|
if (unlikely(tick_before(frame, next))) { |
|
|
|
/* URB_ISO_ASAP: Round up to the first available slot */ |
|
if (urb->transfer_flags & URB_ISO_ASAP) { |
|
frame += (next - frame + ed->interval - 1) & |
|
-ed->interval; |
|
|
|
/* |
|
* Not ASAP: Use the next slot in the stream, |
|
* no matter what. |
|
*/ |
|
} else { |
|
/* |
|
* Some OHCI hardware doesn't handle late TDs |
|
* correctly. After retiring them it proceeds |
|
* to the next ED instead of the next TD. |
|
* Therefore we have to omit the late TDs |
|
* entirely. |
|
*/ |
|
urb_priv->td_cnt = DIV_ROUND_UP( |
|
(u16) (next - frame), |
|
ed->interval); |
|
if (urb_priv->td_cnt >= urb_priv->length) { |
|
++urb_priv->td_cnt; /* Mark it */ |
|
ohci_dbg(ohci, "iso underrun %p (%u+%u < %u)\n", |
|
urb, frame, length, |
|
next); |
|
} |
|
} |
|
} |
|
urb->start_frame = frame; |
|
ed->last_iso = frame + length; |
|
} |
|
|
|
/* fill the TDs and link them to the ed; and |
|
* enable that part of the schedule, if needed |
|
* and update count of queued periodic urbs |
|
*/ |
|
urb->hcpriv = urb_priv; |
|
td_submit_urb (ohci, urb); |
|
|
|
fail: |
|
if (retval) |
|
urb_free_priv (ohci, urb_priv); |
|
spin_unlock_irqrestore (&ohci->lock, flags); |
|
return retval; |
|
} |
|
|
|
/* |
|
* decouple the URB from the HC queues (TDs, urb_priv). |
|
* reporting is always done |
|
* asynchronously, and we might be dealing with an urb that's |
|
* partially transferred, or an ED with other urbs being unlinked. |
|
*/ |
|
static int ohci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) |
|
{ |
|
struct ohci_hcd *ohci = hcd_to_ohci (hcd); |
|
unsigned long flags; |
|
int rc; |
|
urb_priv_t *urb_priv; |
|
|
|
spin_lock_irqsave (&ohci->lock, flags); |
|
rc = usb_hcd_check_unlink_urb(hcd, urb, status); |
|
if (rc == 0) { |
|
|
|
/* Unless an IRQ completed the unlink while it was being |
|
* handed to us, flag it for unlink and giveback, and force |
|
* some upcoming INTR_SF to call finish_unlinks() |
|
*/ |
|
urb_priv = urb->hcpriv; |
|
if (urb_priv->ed->state == ED_OPER) |
|
start_ed_unlink(ohci, urb_priv->ed); |
|
|
|
if (ohci->rh_state != OHCI_RH_RUNNING) { |
|
/* With HC dead, we can clean up right away */ |
|
ohci_work(ohci); |
|
} |
|
} |
|
spin_unlock_irqrestore (&ohci->lock, flags); |
|
return rc; |
|
} |
|
|
|
/*-------------------------------------------------------------------------*/ |
|
|
|
/* frees config/altsetting state for endpoints, |
|
* including ED memory, dummy TD, and bulk/intr data toggle |
|
*/ |
|
|
|
static void |
|
ohci_endpoint_disable (struct usb_hcd *hcd, struct usb_host_endpoint *ep) |
|
{ |
|
struct ohci_hcd *ohci = hcd_to_ohci (hcd); |
|
unsigned long flags; |
|
struct ed *ed = ep->hcpriv; |
|
unsigned limit = 1000; |
|
|
|
/* ASSERT: any requests/urbs are being unlinked */ |
|
/* ASSERT: nobody can be submitting urbs for this any more */ |
|
|
|
if (!ed) |
|
return; |
|
|
|
rescan: |
|
spin_lock_irqsave (&ohci->lock, flags); |
|
|
|
if (ohci->rh_state != OHCI_RH_RUNNING) { |
|
sanitize: |
|
ed->state = ED_IDLE; |
|
ohci_work(ohci); |
|
} |
|
|
|
switch (ed->state) { |
|
case ED_UNLINK: /* wait for hw to finish? */ |
|
/* major IRQ delivery trouble loses INTR_SF too... */ |
|
if (limit-- == 0) { |
|
ohci_warn(ohci, "ED unlink timeout\n"); |
|
goto sanitize; |
|
} |
|
spin_unlock_irqrestore (&ohci->lock, flags); |
|
schedule_timeout_uninterruptible(1); |
|
goto rescan; |
|
case ED_IDLE: /* fully unlinked */ |
|
if (list_empty (&ed->td_list)) { |
|
td_free (ohci, ed->dummy); |
|
ed_free (ohci, ed); |
|
break; |
|
} |
|
fallthrough; |
|
default: |
|
/* caller was supposed to have unlinked any requests; |
|
* that's not our job. can't recover; must leak ed. |
|
*/ |
|
ohci_err (ohci, "leak ed %p (#%02x) state %d%s\n", |
|
ed, ep->desc.bEndpointAddress, ed->state, |
|
list_empty (&ed->td_list) ? "" : " (has tds)"); |
|
td_free (ohci, ed->dummy); |
|
break; |
|
} |
|
ep->hcpriv = NULL; |
|
spin_unlock_irqrestore (&ohci->lock, flags); |
|
} |
|
|
|
static int ohci_get_frame (struct usb_hcd *hcd) |
|
{ |
|
struct ohci_hcd *ohci = hcd_to_ohci (hcd); |
|
|
|
return ohci_frame_no(ohci); |
|
} |
|
|
|
static void ohci_usb_reset (struct ohci_hcd *ohci) |
|
{ |
|
ohci->hc_control = ohci_readl (ohci, &ohci->regs->control); |
|
ohci->hc_control &= OHCI_CTRL_RWC; |
|
ohci_writel (ohci, ohci->hc_control, &ohci->regs->control); |
|
ohci->rh_state = OHCI_RH_HALTED; |
|
} |
|
|
|
/* ohci_shutdown forcibly disables IRQs and DMA, helping kexec and |
|
* other cases where the next software may expect clean state from the |
|
* "firmware". this is bus-neutral, unlike shutdown() methods. |
|
*/ |
|
static void _ohci_shutdown(struct usb_hcd *hcd) |
|
{ |
|
struct ohci_hcd *ohci; |
|
|
|
ohci = hcd_to_ohci (hcd); |
|
ohci_writel(ohci, (u32) ~0, &ohci->regs->intrdisable); |
|
|
|
/* Software reset, after which the controller goes into SUSPEND */ |
|
ohci_writel(ohci, OHCI_HCR, &ohci->regs->cmdstatus); |
|
ohci_readl(ohci, &ohci->regs->cmdstatus); /* flush the writes */ |
|
udelay(10); |
|
|
|
ohci_writel(ohci, ohci->fminterval, &ohci->regs->fminterval); |
|
ohci->rh_state = OHCI_RH_HALTED; |
|
} |
|
|
|
static void ohci_shutdown(struct usb_hcd *hcd) |
|
{ |
|
struct ohci_hcd *ohci = hcd_to_ohci(hcd); |
|
unsigned long flags; |
|
|
|
spin_lock_irqsave(&ohci->lock, flags); |
|
_ohci_shutdown(hcd); |
|
spin_unlock_irqrestore(&ohci->lock, flags); |
|
} |
|
|
|
/*-------------------------------------------------------------------------* |
|
* HC functions |
|
*-------------------------------------------------------------------------*/ |
|
|
|
/* init memory, and kick BIOS/SMM off */ |
|
|
|
static int ohci_init (struct ohci_hcd *ohci) |
|
{ |
|
int ret; |
|
struct usb_hcd *hcd = ohci_to_hcd(ohci); |
|
|
|
/* Accept arbitrarily long scatter-gather lists */ |
|
if (!hcd->localmem_pool) |
|
hcd->self.sg_tablesize = ~0; |
|
|
|
if (distrust_firmware) |
|
ohci->flags |= OHCI_QUIRK_HUB_POWER; |
|
|
|
ohci->rh_state = OHCI_RH_HALTED; |
|
ohci->regs = hcd->regs; |
|
|
|
/* REVISIT this BIOS handshake is now moved into PCI "quirks", and |
|
* was never needed for most non-PCI systems ... remove the code? |
|
*/ |
|
|
|
#ifndef IR_DISABLE |
|
/* SMM owns the HC? not for long! */ |
|
if (!no_handshake && ohci_readl (ohci, |
|
&ohci->regs->control) & OHCI_CTRL_IR) { |
|
u32 temp; |
|
|
|
ohci_dbg (ohci, "USB HC TakeOver from BIOS/SMM\n"); |
|
|
|
/* this timeout is arbitrary. we make it long, so systems |
|
* depending on usb keyboards may be usable even if the |
|
* BIOS/SMM code seems pretty broken. |
|
*/ |
|
temp = 500; /* arbitrary: five seconds */ |
|
|
|
ohci_writel (ohci, OHCI_INTR_OC, &ohci->regs->intrenable); |
|
ohci_writel (ohci, OHCI_OCR, &ohci->regs->cmdstatus); |
|
while (ohci_readl (ohci, &ohci->regs->control) & OHCI_CTRL_IR) { |
|
msleep (10); |
|
if (--temp == 0) { |
|
ohci_err (ohci, "USB HC takeover failed!" |
|
" (BIOS/SMM bug)\n"); |
|
return -EBUSY; |
|
} |
|
} |
|
ohci_usb_reset (ohci); |
|
} |
|
#endif |
|
|
|
/* Disable HC interrupts */ |
|
ohci_writel (ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable); |
|
|
|
/* flush the writes, and save key bits like RWC */ |
|
if (ohci_readl (ohci, &ohci->regs->control) & OHCI_CTRL_RWC) |
|
ohci->hc_control |= OHCI_CTRL_RWC; |
|
|
|
/* Read the number of ports unless overridden */ |
|
if (ohci->num_ports == 0) |
|
ohci->num_ports = roothub_a(ohci) & RH_A_NDP; |
|
|
|
if (ohci->hcca) |
|
return 0; |
|
|
|
timer_setup(&ohci->io_watchdog, io_watchdog_func, 0); |
|
ohci->prev_frame_no = IO_WATCHDOG_OFF; |
|
|
|
if (hcd->localmem_pool) |
|
ohci->hcca = gen_pool_dma_alloc_align(hcd->localmem_pool, |
|
sizeof(*ohci->hcca), |
|
&ohci->hcca_dma, 256); |
|
else |
|
ohci->hcca = dma_alloc_coherent(hcd->self.controller, |
|
sizeof(*ohci->hcca), |
|
&ohci->hcca_dma, |
|
GFP_KERNEL); |
|
if (!ohci->hcca) |
|
return -ENOMEM; |
|
|
|
if ((ret = ohci_mem_init (ohci)) < 0) |
|
ohci_stop (hcd); |
|
else { |
|
create_debug_files (ohci); |
|
} |
|
|
|
return ret; |
|
} |
|
|
|
/*-------------------------------------------------------------------------*/ |
|
|
|
/* Start an OHCI controller, set the BUS operational |
|
* resets USB and controller |
|
* enable interrupts |
|
*/ |
|
static int ohci_run (struct ohci_hcd *ohci) |
|
{ |
|
u32 mask, val; |
|
int first = ohci->fminterval == 0; |
|
struct usb_hcd *hcd = ohci_to_hcd(ohci); |
|
|
|
ohci->rh_state = OHCI_RH_HALTED; |
|
|
|
/* boot firmware should have set this up (5.1.1.3.1) */ |
|
if (first) { |
|
|
|
val = ohci_readl (ohci, &ohci->regs->fminterval); |
|
ohci->fminterval = val & 0x3fff; |
|
if (ohci->fminterval != FI) |
|
ohci_dbg (ohci, "fminterval delta %d\n", |
|
ohci->fminterval - FI); |
|
ohci->fminterval |= FSMP (ohci->fminterval) << 16; |
|
/* also: power/overcurrent flags in roothub.a */ |
|
} |
|
|
|
/* Reset USB nearly "by the book". RemoteWakeupConnected has |
|
* to be checked in case boot firmware (BIOS/SMM/...) has set up |
|
* wakeup in a way the bus isn't aware of (e.g., legacy PCI PM). |
|
* If the bus glue detected wakeup capability then it should |
|
* already be enabled; if so we'll just enable it again. |
|
*/ |
|
if ((ohci->hc_control & OHCI_CTRL_RWC) != 0) |
|
device_set_wakeup_capable(hcd->self.controller, 1); |
|
|
|
switch (ohci->hc_control & OHCI_CTRL_HCFS) { |
|
case OHCI_USB_OPER: |
|
val = 0; |
|
break; |
|
case OHCI_USB_SUSPEND: |
|
case OHCI_USB_RESUME: |
|
ohci->hc_control &= OHCI_CTRL_RWC; |
|
ohci->hc_control |= OHCI_USB_RESUME; |
|
val = 10 /* msec wait */; |
|
break; |
|
// case OHCI_USB_RESET: |
|
default: |
|
ohci->hc_control &= OHCI_CTRL_RWC; |
|
ohci->hc_control |= OHCI_USB_RESET; |
|
val = 50 /* msec wait */; |
|
break; |
|
} |
|
ohci_writel (ohci, ohci->hc_control, &ohci->regs->control); |
|
// flush the writes |
|
(void) ohci_readl (ohci, &ohci->regs->control); |
|
msleep(val); |
|
|
|
memset (ohci->hcca, 0, sizeof (struct ohci_hcca)); |
|
|
|
/* 2msec timelimit here means no irqs/preempt */ |
|
spin_lock_irq (&ohci->lock); |
|
|
|
retry: |
|
/* HC Reset requires max 10 us delay */ |
|
ohci_writel (ohci, OHCI_HCR, &ohci->regs->cmdstatus); |
|
val = 30; /* ... allow extra time */ |
|
while ((ohci_readl (ohci, &ohci->regs->cmdstatus) & OHCI_HCR) != 0) { |
|
if (--val == 0) { |
|
spin_unlock_irq (&ohci->lock); |
|
ohci_err (ohci, "USB HC reset timed out!\n"); |
|
return -1; |
|
} |
|
udelay (1); |
|
} |
|
|
|
/* now we're in the SUSPEND state ... must go OPERATIONAL |
|
* within 2msec else HC enters RESUME |
|
* |
|
* ... but some hardware won't init fmInterval "by the book" |
|
* (SiS, OPTi ...), so reset again instead. SiS doesn't need |
|
* this if we write fmInterval after we're OPERATIONAL. |
|
* Unclear about ALi, ServerWorks, and others ... this could |
|
* easily be a longstanding bug in chip init on Linux. |
|
*/ |
|
if (ohci->flags & OHCI_QUIRK_INITRESET) { |
|
ohci_writel (ohci, ohci->hc_control, &ohci->regs->control); |
|
// flush those writes |
|
(void) ohci_readl (ohci, &ohci->regs->control); |
|
} |
|
|
|
/* Tell the controller where the control and bulk lists are |
|
* The lists are empty now. */ |
|
ohci_writel (ohci, 0, &ohci->regs->ed_controlhead); |
|
ohci_writel (ohci, 0, &ohci->regs->ed_bulkhead); |
|
|
|
/* a reset clears this */ |
|
ohci_writel (ohci, (u32) ohci->hcca_dma, &ohci->regs->hcca); |
|
|
|
periodic_reinit (ohci); |
|
|
|
/* some OHCI implementations are finicky about how they init. |
|
* bogus values here mean not even enumeration could work. |
|
*/ |
|
if ((ohci_readl (ohci, &ohci->regs->fminterval) & 0x3fff0000) == 0 |
|
|| !ohci_readl (ohci, &ohci->regs->periodicstart)) { |
|
if (!(ohci->flags & OHCI_QUIRK_INITRESET)) { |
|
ohci->flags |= OHCI_QUIRK_INITRESET; |
|
ohci_dbg (ohci, "enabling initreset quirk\n"); |
|
goto retry; |
|
} |
|
spin_unlock_irq (&ohci->lock); |
|
ohci_err (ohci, "init err (%08x %04x)\n", |
|
ohci_readl (ohci, &ohci->regs->fminterval), |
|
ohci_readl (ohci, &ohci->regs->periodicstart)); |
|
return -EOVERFLOW; |
|
} |
|
|
|
/* use rhsc irqs after hub_wq is allocated */ |
|
set_bit(HCD_FLAG_POLL_RH, &hcd->flags); |
|
hcd->uses_new_polling = 1; |
|
|
|
/* start controller operations */ |
|
ohci->hc_control &= OHCI_CTRL_RWC; |
|
ohci->hc_control |= OHCI_CONTROL_INIT | OHCI_USB_OPER; |
|
ohci_writel (ohci, ohci->hc_control, &ohci->regs->control); |
|
ohci->rh_state = OHCI_RH_RUNNING; |
|
|
|
/* wake on ConnectStatusChange, matching external hubs */ |
|
ohci_writel (ohci, RH_HS_DRWE, &ohci->regs->roothub.status); |
|
|
|
/* Choose the interrupts we care about now, others later on demand */ |
|
mask = OHCI_INTR_INIT; |
|
ohci_writel (ohci, ~0, &ohci->regs->intrstatus); |
|
ohci_writel (ohci, mask, &ohci->regs->intrenable); |
|
|
|
/* handle root hub init quirks ... */ |
|
val = roothub_a (ohci); |
|
/* Configure for per-port over-current protection by default */ |
|
val &= ~RH_A_NOCP; |
|
val |= RH_A_OCPM; |
|
if (ohci->flags & OHCI_QUIRK_SUPERIO) { |
|
/* NSC 87560 and maybe others. |
|
* Ganged power switching, no over-current protection. |
|
*/ |
|
val |= RH_A_NOCP; |
|
val &= ~(RH_A_POTPGT | RH_A_NPS | RH_A_PSM | RH_A_OCPM); |
|
} else if ((ohci->flags & OHCI_QUIRK_AMD756) || |
|
(ohci->flags & OHCI_QUIRK_HUB_POWER)) { |
|
/* hub power always on; required for AMD-756 and some |
|
* Mac platforms. |
|
*/ |
|
val |= RH_A_NPS; |
|
} |
|
ohci_writel(ohci, val, &ohci->regs->roothub.a); |
|
|
|
ohci_writel (ohci, RH_HS_LPSC, &ohci->regs->roothub.status); |
|
ohci_writel (ohci, (val & RH_A_NPS) ? 0 : RH_B_PPCM, |
|
&ohci->regs->roothub.b); |
|
// flush those writes |
|
(void) ohci_readl (ohci, &ohci->regs->control); |
|
|
|
ohci->next_statechange = jiffies + STATECHANGE_DELAY; |
|
spin_unlock_irq (&ohci->lock); |
|
|
|
// POTPGT delay is bits 24-31, in 2 ms units. |
|
mdelay ((val >> 23) & 0x1fe); |
|
|
|
ohci_dump(ohci); |
|
|
|
return 0; |
|
} |
|
|
|
/* ohci_setup routine for generic controller initialization */ |
|
|
|
int ohci_setup(struct usb_hcd *hcd) |
|
{ |
|
struct ohci_hcd *ohci = hcd_to_ohci(hcd); |
|
|
|
ohci_hcd_init(ohci); |
|
|
|
return ohci_init(ohci); |
|
} |
|
EXPORT_SYMBOL_GPL(ohci_setup); |
|
|
|
/* ohci_start routine for generic controller start of all OHCI bus glue */ |
|
static int ohci_start(struct usb_hcd *hcd) |
|
{ |
|
struct ohci_hcd *ohci = hcd_to_ohci(hcd); |
|
int ret; |
|
|
|
ret = ohci_run(ohci); |
|
if (ret < 0) { |
|
ohci_err(ohci, "can't start\n"); |
|
ohci_stop(hcd); |
|
} |
|
return ret; |
|
} |
|
|
|
/*-------------------------------------------------------------------------*/ |
|
|
|
/* |
|
* Some OHCI controllers are known to lose track of completed TDs. They |
|
* don't add the TDs to the hardware done queue, which means we never see |
|
* them as being completed. |
|
* |
|
* This watchdog routine checks for such problems. Without some way to |
|
* tell when those TDs have completed, we would never take their EDs off |
|
* the unlink list. As a result, URBs could never be dequeued and |
|
* endpoints could never be released. |
|
*/ |
|
static void io_watchdog_func(struct timer_list *t) |
|
{ |
|
struct ohci_hcd *ohci = from_timer(ohci, t, io_watchdog); |
|
bool takeback_all_pending = false; |
|
u32 status; |
|
u32 head; |
|
struct ed *ed; |
|
struct td *td, *td_start, *td_next; |
|
unsigned frame_no, prev_frame_no = IO_WATCHDOG_OFF; |
|
unsigned long flags; |
|
|
|
spin_lock_irqsave(&ohci->lock, flags); |
|
|
|
/* |
|
* One way to lose track of completed TDs is if the controller |
|
* never writes back the done queue head. If it hasn't been |
|
* written back since the last time this function ran and if it |
|
* was non-empty at that time, something is badly wrong with the |
|
* hardware. |
|
*/ |
|
status = ohci_readl(ohci, &ohci->regs->intrstatus); |
|
if (!(status & OHCI_INTR_WDH) && ohci->wdh_cnt == ohci->prev_wdh_cnt) { |
|
if (ohci->prev_donehead) { |
|
ohci_err(ohci, "HcDoneHead not written back; disabled\n"); |
|
died: |
|
usb_hc_died(ohci_to_hcd(ohci)); |
|
ohci_dump(ohci); |
|
_ohci_shutdown(ohci_to_hcd(ohci)); |
|
goto done; |
|
} else { |
|
/* No write back because the done queue was empty */ |
|
takeback_all_pending = true; |
|
} |
|
} |
|
|
|
/* Check every ED which might have pending TDs */ |
|
list_for_each_entry(ed, &ohci->eds_in_use, in_use_list) { |
|
if (ed->pending_td) { |
|
if (takeback_all_pending || |
|
OKAY_TO_TAKEBACK(ohci, ed)) { |
|
unsigned tmp = hc32_to_cpu(ohci, ed->hwINFO); |
|
|
|
ohci_dbg(ohci, "takeback pending TD for dev %d ep 0x%x\n", |
|
0x007f & tmp, |
|
(0x000f & (tmp >> 7)) + |
|
((tmp & ED_IN) >> 5)); |
|
add_to_done_list(ohci, ed->pending_td); |
|
} |
|
} |
|
|
|
/* Starting from the latest pending TD, */ |
|
td = ed->pending_td; |
|
|
|
/* or the last TD on the done list, */ |
|
if (!td) { |
|
list_for_each_entry(td_next, &ed->td_list, td_list) { |
|
if (!td_next->next_dl_td) |
|
break; |
|
td = td_next; |
|
} |
|
} |
|
|
|
/* find the last TD processed by the controller. */ |
|
head = hc32_to_cpu(ohci, READ_ONCE(ed->hwHeadP)) & TD_MASK; |
|
td_start = td; |
|
td_next = list_prepare_entry(td, &ed->td_list, td_list); |
|
list_for_each_entry_continue(td_next, &ed->td_list, td_list) { |
|
if (head == (u32) td_next->td_dma) |
|
break; |
|
td = td_next; /* head pointer has passed this TD */ |
|
} |
|
if (td != td_start) { |
|
/* |
|
* In case a WDH cycle is in progress, we will wait |
|
* for the next two cycles to complete before assuming |
|
* this TD will never get on the done queue. |
|
*/ |
|
ed->takeback_wdh_cnt = ohci->wdh_cnt + 2; |
|
ed->pending_td = td; |
|
} |
|
} |
|
|
|
ohci_work(ohci); |
|
|
|
if (ohci->rh_state == OHCI_RH_RUNNING) { |
|
|
|
/* |
|
* Sometimes a controller just stops working. We can tell |
|
* by checking that the frame counter has advanced since |
|
* the last time we ran. |
|
* |
|
* But be careful: Some controllers violate the spec by |
|
* stopping their frame counter when no ports are active. |
|
*/ |
|
frame_no = ohci_frame_no(ohci); |
|
if (frame_no == ohci->prev_frame_no) { |
|
int active_cnt = 0; |
|
int i; |
|
unsigned tmp; |
|
|
|
for (i = 0; i < ohci->num_ports; ++i) { |
|
tmp = roothub_portstatus(ohci, i); |
|
/* Enabled and not suspended? */ |
|
if ((tmp & RH_PS_PES) && !(tmp & RH_PS_PSS)) |
|
++active_cnt; |
|
} |
|
|
|
if (active_cnt > 0) { |
|
ohci_err(ohci, "frame counter not updating; disabled\n"); |
|
goto died; |
|
} |
|
} |
|
if (!list_empty(&ohci->eds_in_use)) { |
|
prev_frame_no = frame_no; |
|
ohci->prev_wdh_cnt = ohci->wdh_cnt; |
|
ohci->prev_donehead = ohci_readl(ohci, |
|
&ohci->regs->donehead); |
|
mod_timer(&ohci->io_watchdog, |
|
jiffies + IO_WATCHDOG_DELAY); |
|
} |
|
} |
|
|
|
done: |
|
ohci->prev_frame_no = prev_frame_no; |
|
spin_unlock_irqrestore(&ohci->lock, flags); |
|
} |
|
|
|
/* an interrupt happens */ |
|
|
|
static irqreturn_t ohci_irq (struct usb_hcd *hcd) |
|
{ |
|
struct ohci_hcd *ohci = hcd_to_ohci (hcd); |
|
struct ohci_regs __iomem *regs = ohci->regs; |
|
int ints; |
|
|
|
/* Read interrupt status (and flush pending writes). We ignore the |
|
* optimization of checking the LSB of hcca->done_head; it doesn't |
|
* work on all systems (edge triggering for OHCI can be a factor). |
|
*/ |
|
ints = ohci_readl(ohci, ®s->intrstatus); |
|
|
|
/* Check for an all 1's result which is a typical consequence |
|
* of dead, unclocked, or unplugged (CardBus...) devices |
|
*/ |
|
if (ints == ~(u32)0) { |
|
ohci->rh_state = OHCI_RH_HALTED; |
|
ohci_dbg (ohci, "device removed!\n"); |
|
usb_hc_died(hcd); |
|
return IRQ_HANDLED; |
|
} |
|
|
|
/* We only care about interrupts that are enabled */ |
|
ints &= ohci_readl(ohci, ®s->intrenable); |
|
|
|
/* interrupt for some other device? */ |
|
if (ints == 0 || unlikely(ohci->rh_state == OHCI_RH_HALTED)) |
|
return IRQ_NOTMINE; |
|
|
|
if (ints & OHCI_INTR_UE) { |
|
// e.g. due to PCI Master/Target Abort |
|
if (quirk_nec(ohci)) { |
|
/* Workaround for a silicon bug in some NEC chips used |
|
* in Apple's PowerBooks. Adapted from Darwin code. |
|
*/ |
|
ohci_err (ohci, "OHCI Unrecoverable Error, scheduling NEC chip restart\n"); |
|
|
|
ohci_writel (ohci, OHCI_INTR_UE, ®s->intrdisable); |
|
|
|
schedule_work (&ohci->nec_work); |
|
} else { |
|
ohci_err (ohci, "OHCI Unrecoverable Error, disabled\n"); |
|
ohci->rh_state = OHCI_RH_HALTED; |
|
usb_hc_died(hcd); |
|
} |
|
|
|
ohci_dump(ohci); |
|
ohci_usb_reset (ohci); |
|
} |
|
|
|
if (ints & OHCI_INTR_RHSC) { |
|
ohci_dbg(ohci, "rhsc\n"); |
|
ohci->next_statechange = jiffies + STATECHANGE_DELAY; |
|
ohci_writel(ohci, OHCI_INTR_RD | OHCI_INTR_RHSC, |
|
®s->intrstatus); |
|
|
|
/* NOTE: Vendors didn't always make the same implementation |
|
* choices for RHSC. Many followed the spec; RHSC triggers |
|
* on an edge, like setting and maybe clearing a port status |
|
* change bit. With others it's level-triggered, active |
|
* until hub_wq clears all the port status change bits. We'll |
|
* always disable it here and rely on polling until hub_wq |
|
* re-enables it. |
|
*/ |
|
ohci_writel(ohci, OHCI_INTR_RHSC, ®s->intrdisable); |
|
usb_hcd_poll_rh_status(hcd); |
|
} |
|
|
|
/* For connect and disconnect events, we expect the controller |
|
* to turn on RHSC along with RD. But for remote wakeup events |
|
* this might not happen. |
|
*/ |
|
else if (ints & OHCI_INTR_RD) { |
|
ohci_dbg(ohci, "resume detect\n"); |
|
ohci_writel(ohci, OHCI_INTR_RD, ®s->intrstatus); |
|
set_bit(HCD_FLAG_POLL_RH, &hcd->flags); |
|
if (ohci->autostop) { |
|
spin_lock (&ohci->lock); |
|
ohci_rh_resume (ohci); |
|
spin_unlock (&ohci->lock); |
|
} else |
|
usb_hcd_resume_root_hub(hcd); |
|
} |
|
|
|
spin_lock(&ohci->lock); |
|
if (ints & OHCI_INTR_WDH) |
|
update_done_list(ohci); |
|
|
|
/* could track INTR_SO to reduce available PCI/... bandwidth */ |
|
|
|
/* handle any pending URB/ED unlinks, leaving INTR_SF enabled |
|
* when there's still unlinking to be done (next frame). |
|
*/ |
|
ohci_work(ohci); |
|
if ((ints & OHCI_INTR_SF) != 0 && !ohci->ed_rm_list |
|
&& ohci->rh_state == OHCI_RH_RUNNING) |
|
ohci_writel (ohci, OHCI_INTR_SF, ®s->intrdisable); |
|
|
|
if (ohci->rh_state == OHCI_RH_RUNNING) { |
|
ohci_writel (ohci, ints, ®s->intrstatus); |
|
if (ints & OHCI_INTR_WDH) |
|
++ohci->wdh_cnt; |
|
|
|
ohci_writel (ohci, OHCI_INTR_MIE, ®s->intrenable); |
|
// flush those writes |
|
(void) ohci_readl (ohci, &ohci->regs->control); |
|
} |
|
spin_unlock(&ohci->lock); |
|
|
|
return IRQ_HANDLED; |
|
} |
|
|
|
/*-------------------------------------------------------------------------*/ |
|
|
|
static void ohci_stop (struct usb_hcd *hcd) |
|
{ |
|
struct ohci_hcd *ohci = hcd_to_ohci (hcd); |
|
|
|
ohci_dump(ohci); |
|
|
|
if (quirk_nec(ohci)) |
|
flush_work(&ohci->nec_work); |
|
del_timer_sync(&ohci->io_watchdog); |
|
ohci->prev_frame_no = IO_WATCHDOG_OFF; |
|
|
|
ohci_writel (ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable); |
|
ohci_usb_reset(ohci); |
|
free_irq(hcd->irq, hcd); |
|
hcd->irq = 0; |
|
|
|
if (quirk_amdiso(ohci)) |
|
usb_amd_dev_put(); |
|
|
|
remove_debug_files (ohci); |
|
ohci_mem_cleanup (ohci); |
|
if (ohci->hcca) { |
|
if (hcd->localmem_pool) |
|
gen_pool_free(hcd->localmem_pool, |
|
(unsigned long)ohci->hcca, |
|
sizeof(*ohci->hcca)); |
|
else |
|
dma_free_coherent(hcd->self.controller, |
|
sizeof(*ohci->hcca), |
|
ohci->hcca, ohci->hcca_dma); |
|
ohci->hcca = NULL; |
|
ohci->hcca_dma = 0; |
|
} |
|
} |
|
|
|
/*-------------------------------------------------------------------------*/ |
|
|
|
#if defined(CONFIG_PM) || defined(CONFIG_USB_PCI) |
|
|
|
/* must not be called from interrupt context */ |
|
int ohci_restart(struct ohci_hcd *ohci) |
|
{ |
|
int temp; |
|
int i; |
|
struct urb_priv *priv; |
|
|
|
ohci_init(ohci); |
|
spin_lock_irq(&ohci->lock); |
|
ohci->rh_state = OHCI_RH_HALTED; |
|
|
|
/* Recycle any "live" eds/tds (and urbs). */ |
|
if (!list_empty (&ohci->pending)) |
|
ohci_dbg(ohci, "abort schedule...\n"); |
|
list_for_each_entry (priv, &ohci->pending, pending) { |
|
struct urb *urb = priv->td[0]->urb; |
|
struct ed *ed = priv->ed; |
|
|
|
switch (ed->state) { |
|
case ED_OPER: |
|
ed->state = ED_UNLINK; |
|
ed->hwINFO |= cpu_to_hc32(ohci, ED_DEQUEUE); |
|
ed_deschedule (ohci, ed); |
|
|
|
ed->ed_next = ohci->ed_rm_list; |
|
ed->ed_prev = NULL; |
|
ohci->ed_rm_list = ed; |
|
fallthrough; |
|
case ED_UNLINK: |
|
break; |
|
default: |
|
ohci_dbg(ohci, "bogus ed %p state %d\n", |
|
ed, ed->state); |
|
} |
|
|
|
if (!urb->unlinked) |
|
urb->unlinked = -ESHUTDOWN; |
|
} |
|
ohci_work(ohci); |
|
spin_unlock_irq(&ohci->lock); |
|
|
|
/* paranoia, in case that didn't work: */ |
|
|
|
/* empty the interrupt branches */ |
|
for (i = 0; i < NUM_INTS; i++) ohci->load [i] = 0; |
|
for (i = 0; i < NUM_INTS; i++) ohci->hcca->int_table [i] = 0; |
|
|
|
/* no EDs to remove */ |
|
ohci->ed_rm_list = NULL; |
|
|
|
/* empty control and bulk lists */ |
|
ohci->ed_controltail = NULL; |
|
ohci->ed_bulktail = NULL; |
|
|
|
if ((temp = ohci_run (ohci)) < 0) { |
|
ohci_err (ohci, "can't restart, %d\n", temp); |
|
return temp; |
|
} |
|
ohci_dbg(ohci, "restart complete\n"); |
|
return 0; |
|
} |
|
EXPORT_SYMBOL_GPL(ohci_restart); |
|
|
|
#endif |
|
|
|
#ifdef CONFIG_PM |
|
|
|
int ohci_suspend(struct usb_hcd *hcd, bool do_wakeup) |
|
{ |
|
struct ohci_hcd *ohci = hcd_to_ohci (hcd); |
|
unsigned long flags; |
|
int rc = 0; |
|
|
|
/* Disable irq emission and mark HW unaccessible. Use |
|
* the spinlock to properly synchronize with possible pending |
|
* RH suspend or resume activity. |
|
*/ |
|
spin_lock_irqsave (&ohci->lock, flags); |
|
ohci_writel(ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable); |
|
(void)ohci_readl(ohci, &ohci->regs->intrdisable); |
|
|
|
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); |
|
spin_unlock_irqrestore (&ohci->lock, flags); |
|
|
|
synchronize_irq(hcd->irq); |
|
|
|
if (do_wakeup && HCD_WAKEUP_PENDING(hcd)) { |
|
ohci_resume(hcd, false); |
|
rc = -EBUSY; |
|
} |
|
return rc; |
|
} |
|
EXPORT_SYMBOL_GPL(ohci_suspend); |
|
|
|
|
|
int ohci_resume(struct usb_hcd *hcd, bool hibernated) |
|
{ |
|
struct ohci_hcd *ohci = hcd_to_ohci(hcd); |
|
int port; |
|
bool need_reinit = false; |
|
|
|
set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); |
|
|
|
/* Make sure resume from hibernation re-enumerates everything */ |
|
if (hibernated) |
|
ohci_usb_reset(ohci); |
|
|
|
/* See if the controller is already running or has been reset */ |
|
ohci->hc_control = ohci_readl(ohci, &ohci->regs->control); |
|
if (ohci->hc_control & (OHCI_CTRL_IR | OHCI_SCHED_ENABLES)) { |
|
need_reinit = true; |
|
} else { |
|
switch (ohci->hc_control & OHCI_CTRL_HCFS) { |
|
case OHCI_USB_OPER: |
|
case OHCI_USB_RESET: |
|
need_reinit = true; |
|
} |
|
} |
|
|
|
/* If needed, reinitialize and suspend the root hub */ |
|
if (need_reinit) { |
|
spin_lock_irq(&ohci->lock); |
|
ohci_rh_resume(ohci); |
|
ohci_rh_suspend(ohci, 0); |
|
spin_unlock_irq(&ohci->lock); |
|
} |
|
|
|
/* Normally just turn on port power and enable interrupts */ |
|
else { |
|
ohci_dbg(ohci, "powerup ports\n"); |
|
for (port = 0; port < ohci->num_ports; port++) |
|
ohci_writel(ohci, RH_PS_PPS, |
|
&ohci->regs->roothub.portstatus[port]); |
|
|
|
ohci_writel(ohci, OHCI_INTR_MIE, &ohci->regs->intrenable); |
|
ohci_readl(ohci, &ohci->regs->intrenable); |
|
msleep(20); |
|
} |
|
|
|
usb_hcd_resume_root_hub(hcd); |
|
|
|
return 0; |
|
} |
|
EXPORT_SYMBOL_GPL(ohci_resume); |
|
|
|
#endif |
|
|
|
/*-------------------------------------------------------------------------*/ |
|
|
|
/* |
|
* Generic structure: This gets copied for platform drivers so that |
|
* individual entries can be overridden as needed. |
|
*/ |
|
|
|
static const struct hc_driver ohci_hc_driver = { |
|
.description = hcd_name, |
|
.product_desc = "OHCI Host Controller", |
|
.hcd_priv_size = sizeof(struct ohci_hcd), |
|
|
|
/* |
|
* generic hardware linkage |
|
*/ |
|
.irq = ohci_irq, |
|
.flags = HCD_MEMORY | HCD_DMA | HCD_USB11, |
|
|
|
/* |
|
* basic lifecycle operations |
|
*/ |
|
.reset = ohci_setup, |
|
.start = ohci_start, |
|
.stop = ohci_stop, |
|
.shutdown = ohci_shutdown, |
|
|
|
/* |
|
* managing i/o requests and associated device resources |
|
*/ |
|
.urb_enqueue = ohci_urb_enqueue, |
|
.urb_dequeue = ohci_urb_dequeue, |
|
.endpoint_disable = ohci_endpoint_disable, |
|
|
|
/* |
|
* scheduling support |
|
*/ |
|
.get_frame_number = ohci_get_frame, |
|
|
|
/* |
|
* root hub support |
|
*/ |
|
.hub_status_data = ohci_hub_status_data, |
|
.hub_control = ohci_hub_control, |
|
#ifdef CONFIG_PM |
|
.bus_suspend = ohci_bus_suspend, |
|
.bus_resume = ohci_bus_resume, |
|
#endif |
|
.start_port_reset = ohci_start_port_reset, |
|
}; |
|
|
|
void ohci_init_driver(struct hc_driver *drv, |
|
const struct ohci_driver_overrides *over) |
|
{ |
|
/* Copy the generic table to drv and then apply the overrides */ |
|
*drv = ohci_hc_driver; |
|
|
|
if (over) { |
|
drv->product_desc = over->product_desc; |
|
drv->hcd_priv_size += over->extra_priv_size; |
|
if (over->reset) |
|
drv->reset = over->reset; |
|
} |
|
} |
|
EXPORT_SYMBOL_GPL(ohci_init_driver); |
|
|
|
/*-------------------------------------------------------------------------*/ |
|
|
|
MODULE_AUTHOR (DRIVER_AUTHOR); |
|
MODULE_DESCRIPTION(DRIVER_DESC); |
|
MODULE_LICENSE ("GPL"); |
|
|
|
#if defined(CONFIG_ARCH_SA1100) && defined(CONFIG_SA1111) |
|
#include "ohci-sa1111.c" |
|
#define SA1111_DRIVER ohci_hcd_sa1111_driver |
|
#endif |
|
|
|
#ifdef CONFIG_USB_OHCI_HCD_PPC_OF |
|
#include "ohci-ppc-of.c" |
|
#define OF_PLATFORM_DRIVER ohci_hcd_ppc_of_driver |
|
#endif |
|
|
|
#ifdef CONFIG_PPC_PS3 |
|
#include "ohci-ps3.c" |
|
#define PS3_SYSTEM_BUS_DRIVER ps3_ohci_driver |
|
#endif |
|
|
|
#ifdef CONFIG_MFD_SM501 |
|
#include "ohci-sm501.c" |
|
#define SM501_OHCI_DRIVER ohci_hcd_sm501_driver |
|
#endif |
|
|
|
#ifdef CONFIG_MFD_TC6393XB |
|
#include "ohci-tmio.c" |
|
#define TMIO_OHCI_DRIVER ohci_hcd_tmio_driver |
|
#endif |
|
|
|
static int __init ohci_hcd_mod_init(void) |
|
{ |
|
int retval = 0; |
|
|
|
if (usb_disabled()) |
|
return -ENODEV; |
|
|
|
printk(KERN_INFO "%s: " DRIVER_DESC "\n", hcd_name); |
|
pr_debug ("%s: block sizes: ed %zd td %zd\n", hcd_name, |
|
sizeof (struct ed), sizeof (struct td)); |
|
set_bit(USB_OHCI_LOADED, &usb_hcds_loaded); |
|
|
|
ohci_debug_root = debugfs_create_dir("ohci", usb_debug_root); |
|
|
|
#ifdef PS3_SYSTEM_BUS_DRIVER |
|
retval = ps3_ohci_driver_register(&PS3_SYSTEM_BUS_DRIVER); |
|
if (retval < 0) |
|
goto error_ps3; |
|
#endif |
|
|
|
#ifdef OF_PLATFORM_DRIVER |
|
retval = platform_driver_register(&OF_PLATFORM_DRIVER); |
|
if (retval < 0) |
|
goto error_of_platform; |
|
#endif |
|
|
|
#ifdef SA1111_DRIVER |
|
retval = sa1111_driver_register(&SA1111_DRIVER); |
|
if (retval < 0) |
|
goto error_sa1111; |
|
#endif |
|
|
|
#ifdef SM501_OHCI_DRIVER |
|
retval = platform_driver_register(&SM501_OHCI_DRIVER); |
|
if (retval < 0) |
|
goto error_sm501; |
|
#endif |
|
|
|
#ifdef TMIO_OHCI_DRIVER |
|
retval = platform_driver_register(&TMIO_OHCI_DRIVER); |
|
if (retval < 0) |
|
goto error_tmio; |
|
#endif |
|
|
|
return retval; |
|
|
|
/* Error path */ |
|
#ifdef TMIO_OHCI_DRIVER |
|
platform_driver_unregister(&TMIO_OHCI_DRIVER); |
|
error_tmio: |
|
#endif |
|
#ifdef SM501_OHCI_DRIVER |
|
platform_driver_unregister(&SM501_OHCI_DRIVER); |
|
error_sm501: |
|
#endif |
|
#ifdef SA1111_DRIVER |
|
sa1111_driver_unregister(&SA1111_DRIVER); |
|
error_sa1111: |
|
#endif |
|
#ifdef OF_PLATFORM_DRIVER |
|
platform_driver_unregister(&OF_PLATFORM_DRIVER); |
|
error_of_platform: |
|
#endif |
|
#ifdef PS3_SYSTEM_BUS_DRIVER |
|
ps3_ohci_driver_unregister(&PS3_SYSTEM_BUS_DRIVER); |
|
error_ps3: |
|
#endif |
|
debugfs_remove(ohci_debug_root); |
|
ohci_debug_root = NULL; |
|
|
|
clear_bit(USB_OHCI_LOADED, &usb_hcds_loaded); |
|
return retval; |
|
} |
|
module_init(ohci_hcd_mod_init); |
|
|
|
static void __exit ohci_hcd_mod_exit(void) |
|
{ |
|
#ifdef TMIO_OHCI_DRIVER |
|
platform_driver_unregister(&TMIO_OHCI_DRIVER); |
|
#endif |
|
#ifdef SM501_OHCI_DRIVER |
|
platform_driver_unregister(&SM501_OHCI_DRIVER); |
|
#endif |
|
#ifdef SA1111_DRIVER |
|
sa1111_driver_unregister(&SA1111_DRIVER); |
|
#endif |
|
#ifdef OF_PLATFORM_DRIVER |
|
platform_driver_unregister(&OF_PLATFORM_DRIVER); |
|
#endif |
|
#ifdef PS3_SYSTEM_BUS_DRIVER |
|
ps3_ohci_driver_unregister(&PS3_SYSTEM_BUS_DRIVER); |
|
#endif |
|
debugfs_remove(ohci_debug_root); |
|
clear_bit(USB_OHCI_LOADED, &usb_hcds_loaded); |
|
} |
|
module_exit(ohci_hcd_mod_exit); |
|
|
|
|