Commit b0a19b4f authored by Christopher Hoover's avatar Christopher Hoover Committed by Greg Kroah-Hartman

[PATCH] USB SA-1111 patch against usb-2.5 bitkeeper

This adds SA-1111 support for ohci-hcd and fixes usb-ohci too.
parent 47c8189f
......@@ -24,6 +24,7 @@
#include <linux/config.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/completion.h>
......
......@@ -50,9 +50,9 @@ struct usb_hcd { /* usb_bus.hcpriv points to this */
int irq; /* irq allocated */
void *regs; /* device memory/io */
#ifdef CONFIG_PCI
/* a few non-PCI controllers exist, mostly for OHCI */
struct pci_dev *pdev; /* pci is typical */
#ifdef CONFIG_PCI
int region; /* pci region for regs */
u32 pci_state [16]; /* for PM state save */
atomic_t resume_count; /* multiple resumes issue */
......
This diff is collapsed.
......@@ -42,12 +42,6 @@ static void ohci_hcd_free (struct usb_hcd *hcd)
/*-------------------------------------------------------------------------*/
#ifndef CONFIG_PCI
# error "usb-ohci currently requires PCI-based controllers"
/* to support non-PCI OHCIs, you need custom bus/mem/... glue */
#endif
/* Recover a TD/ED using its collision chain */
static inline void *
dma_to_ed_td (struct hash_list_t * entry, dma_addr_t dma)
......
/*
* OHCI HCD (Host Controller Driver) for USB.
*
* (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
* (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
*
* [ Initialisation is based on Linus' ]
* [ uhci code and gregs ohci fragments ]
* [ (C) Copyright 1999 Linus Torvalds ]
* [ (C) Copyright 1999 Gregory P. Smith]
*
* PCI Bus Glue
*
* This file is licenced under the GPL.
*/
#ifdef CONFIG_PMAC_PBOOK
#include <asm/machdep.h>
#include <asm/pmac_feature.h>
#include <asm/pci-bridge.h>
#ifndef CONFIG_PM
# define CONFIG_PM
#endif
#endif
#ifndef CONFIG_PCI
#error "This file is PCI bus glue. CONFIG_PCI must be defined."
#endif
/*-------------------------------------------------------------------------*/
static int __devinit
ohci_pci_start (struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
int ret;
if (hcd->pdev) {
ohci->hcca = pci_alloc_consistent (hcd->pdev,
sizeof *ohci->hcca, &ohci->hcca_dma);
if (!ohci->hcca)
return -ENOMEM;
/* AMD 756, for most chips (early revs), corrupts register
* values on read ... so enable the vendor workaround.
*/
if (hcd->pdev->vendor == 0x1022
&& hcd->pdev->device == 0x740c) {
ohci->flags = OHCI_QUIRK_AMD756;
info ("%s: AMD756 erratum 4 workaround",
hcd->self.bus_name);
}
/* Apple's OHCI driver has a lot of bizarre workarounds
* for this chip. Evidently control and bulk lists
* can get confused. (B&W G3 models, and ...)
*/
else if (hcd->pdev->vendor == 0x1045
&& hcd->pdev->device == 0xc861) {
info ("%s: WARNING: OPTi workarounds unavailable",
hcd->self.bus_name);
}
}
memset (ohci->hcca, 0, sizeof (struct ohci_hcca));
if ((ret = ohci_mem_init (ohci)) < 0) {
ohci_stop (hcd);
return ret;
}
ohci->regs = hcd->regs;
ohci->parent_dev = &ohci->hcd.pdev->dev;
if (hc_reset (ohci) < 0) {
ohci_stop (hcd);
return -ENODEV;
}
if (hc_start (ohci) < 0) {
err ("can't start %s", ohci->hcd.self.bus_name);
ohci_stop (hcd);
return -EBUSY;
}
#ifdef DEBUG
ohci_dump (ohci, 1);
#endif
return 0;
}
#ifdef CONFIG_PM
static int ohci_pci_suspend (struct usb_hcd *hcd, u32 state)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
unsigned long flags;
u16 cmd;
if ((ohci->hc_control & OHCI_CTRL_HCFS) != OHCI_USB_OPER) {
dbg ("can't suspend %s (state is %s)", hcd->self.bus_name,
hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS));
return -EIO;
}
/* act as if usb suspend can always be used */
dbg ("%s: suspend to %d", hcd->self.bus_name, state);
ohci->sleeping = 1;
/* First stop processing */
spin_lock_irqsave (&ohci->lock, flags);
ohci->hc_control &=
~(OHCI_CTRL_PLE|OHCI_CTRL_CLE|OHCI_CTRL_BLE|OHCI_CTRL_IE);
writel (ohci->hc_control, &ohci->regs->control);
writel (OHCI_INTR_SF, &ohci->regs->intrstatus);
(void) readl (&ohci->regs->intrstatus);
spin_unlock_irqrestore (&ohci->lock, flags);
/* Wait a frame or two */
mdelay (1);
if (!readl (&ohci->regs->intrstatus) & OHCI_INTR_SF)
mdelay (1);
#ifdef CONFIG_PMAC_PBOOK
if (_machine == _MACH_Pmac)
disable_irq (hcd->pdev->irq);
/* else, 2.4 assumes shared irqs -- don't disable */
#endif
/* Enable remote wakeup */
writel (readl (&ohci->regs->intrenable) | OHCI_INTR_RD,
&ohci->regs->intrenable);
/* Suspend chip and let things settle down a bit */
ohci->hc_control = OHCI_USB_SUSPEND;
writel (ohci->hc_control, &ohci->regs->control);
(void) readl (&ohci->regs->control);
mdelay (500); /* No schedule here ! */
switch (readl (&ohci->regs->control) & OHCI_CTRL_HCFS) {
case OHCI_USB_RESET:
dbg ("%s suspend->reset ?", hcd->self.bus_name);
break;
case OHCI_USB_RESUME:
dbg ("%s suspend->resume ?", hcd->self.bus_name);
break;
case OHCI_USB_OPER:
dbg ("%s suspend->operational ?", hcd->self.bus_name);
break;
case OHCI_USB_SUSPEND:
dbg ("%s suspended", hcd->self.bus_name);
break;
}
/* In some rare situations, Apple's OHCI have happily trashed
* memory during sleep. We disable its bus master bit during
* suspend
*/
pci_read_config_word (hcd->pdev, PCI_COMMAND, &cmd);
cmd &= ~PCI_COMMAND_MASTER;
pci_write_config_word (hcd->pdev, PCI_COMMAND, cmd);
#ifdef CONFIG_PMAC_PBOOK
{
struct device_node *of_node;
/* Disable USB PAD & cell clock */
of_node = pci_device_to_OF_node (hcd->pdev);
if (of_node)
pmac_call_feature(PMAC_FTR_USB_ENABLE, of_node, 0, 0);
}
#endif
return 0;
}
static int ohci_pci_resume (struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
int temp;
int retval = 0;
unsigned long flags;
#ifdef CONFIG_PMAC_PBOOK
{
struct device_node *of_node;
/* Re-enable USB PAD & cell clock */
of_node = pci_device_to_OF_node (hcd->pdev);
if (of_node)
pmac_call_feature (PMAC_FTR_USB_ENABLE, of_node, 0, 1);
}
#endif
/* did we suspend, or were we powered off? */
ohci->hc_control = readl (&ohci->regs->control);
temp = ohci->hc_control & OHCI_CTRL_HCFS;
#ifdef DEBUG
/* the registers may look crazy here */
ohci_dump_status (ohci);
#endif
/* Re-enable bus mastering */
pci_set_master (ohci->hcd.pdev);
switch (temp) {
case OHCI_USB_RESET: // lost power
info ("USB restart: %s", hcd->self.bus_name);
retval = hc_restart (ohci);
break;
case OHCI_USB_SUSPEND: // host wakeup
case OHCI_USB_RESUME: // remote wakeup
info ("USB continue: %s from %s wakeup", hcd->self.bus_name,
(temp == OHCI_USB_SUSPEND)
? "host" : "remote");
ohci->hc_control = OHCI_USB_RESUME;
writel (ohci->hc_control, &ohci->regs->control);
(void) readl (&ohci->regs->control);
mdelay (20); /* no schedule here ! */
/* Some controllers (lucent) need a longer delay here */
mdelay (15);
temp = readl (&ohci->regs->control);
temp = ohci->hc_control & OHCI_CTRL_HCFS;
if (temp != OHCI_USB_RESUME) {
err ("controller %s won't resume", hcd->self.bus_name);
ohci->disabled = 1;
retval = -EIO;
break;
}
/* Some chips likes being resumed first */
writel (OHCI_USB_OPER, &ohci->regs->control);
(void) readl (&ohci->regs->control);
mdelay (3);
/* Then re-enable operations */
spin_lock_irqsave (&ohci->lock, flags);
ohci->disabled = 0;
ohci->sleeping = 0;
ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER;
if (!ohci->ed_rm_list) {
if (ohci->ed_controltail)
ohci->hc_control |= OHCI_CTRL_CLE;
if (ohci->ed_bulktail)
ohci->hc_control |= OHCI_CTRL_BLE;
}
hcd->state = USB_STATE_READY;
writel (ohci->hc_control, &ohci->regs->control);
/* trigger a start-frame interrupt (why?) */
writel (OHCI_INTR_SF, &ohci->regs->intrstatus);
writel (OHCI_INTR_SF, &ohci->regs->intrenable);
/* Check for a pending done list */
writel (OHCI_INTR_WDH, &ohci->regs->intrdisable);
(void) readl (&ohci->regs->intrdisable);
spin_unlock_irqrestore (&ohci->lock, flags);
#ifdef CONFIG_PMAC_PBOOK
if (_machine == _MACH_Pmac)
enable_irq (hcd->pdev->irq);
#endif
if (ohci->hcca->done_head)
dl_done_list (ohci, dl_reverse_done_list (ohci));
writel (OHCI_INTR_WDH, &ohci->regs->intrenable);
/* assume there are TDs on the bulk and control lists */
writel (OHCI_BLF | OHCI_CLF, &ohci->regs->cmdstatus);
// ohci_dump_status (ohci);
dbg ("sleeping = %d, disabled = %d", ohci->sleeping, ohci->disabled);
break;
default:
warn ("odd PCI resume for %s", hcd->self.bus_name);
}
return retval;
}
#endif /* CONFIG_PM */
/*-------------------------------------------------------------------------*/
static const struct hc_driver ohci_pci_hc_driver = {
description: hcd_name,
/*
* generic hardware linkage
*/
irq: ohci_irq,
flags: HCD_MEMORY | HCD_USB11,
/*
* basic lifecycle operations
*/
start: ohci_pci_start,
#ifdef CONFIG_PM
suspend: ohci_pci_suspend,
resume: ohci_pci_resume,
#endif
stop: ohci_stop,
/*
* memory lifecycle (except per-request)
*/
hcd_alloc: ohci_hcd_alloc,
hcd_free: ohci_hcd_free,
/*
* managing i/o requests and associated device resources
*/
urb_enqueue: ohci_urb_enqueue,
urb_dequeue: ohci_urb_dequeue,
free_config: ohci_free_config,
/*
* scheduling support
*/
get_frame_number: ohci_get_frame,
/*
* root hub support
*/
hub_status_data: ohci_hub_status_data,
hub_control: ohci_hub_control,
};
/*-------------------------------------------------------------------------*/
static const struct pci_device_id __devinitdata pci_ids [] = { {
/* handle any USB OHCI controller */
class: (PCI_CLASS_SERIAL_USB << 8) | 0x10,
class_mask: ~0,
driver_data: (unsigned long) &ohci_pci_hc_driver,
/* no matter who makes it */
vendor: PCI_ANY_ID,
device: PCI_ANY_ID,
subvendor: PCI_ANY_ID,
subdevice: PCI_ANY_ID,
}, { /* end: all zeroes */ }
};
MODULE_DEVICE_TABLE (pci, pci_ids);
/* pci driver glue; this is a "new style" PCI driver module */
static struct pci_driver ohci_pci_driver = {
name: (char *) hcd_name,
id_table: pci_ids,
probe: usb_hcd_pci_probe,
remove: usb_hcd_pci_remove,
#ifdef CONFIG_PM
suspend: usb_hcd_pci_suspend,
resume: usb_hcd_pci_resume,
#endif
};
static int __init ohci_hcd_pci_init (void)
{
dbg (DRIVER_INFO " (PCI)");
dbg ("block sizes: ed %d td %d",
sizeof (struct ed), sizeof (struct td));
return pci_module_init (&ohci_pci_driver);
}
module_init (ohci_hcd_pci_init);
/*-------------------------------------------------------------------------*/
static void __exit ohci_hcd_pci_cleanup (void)
{
pci_unregister_driver (&ohci_pci_driver);
}
module_exit (ohci_hcd_pci_cleanup);
......@@ -15,7 +15,6 @@ static void urb_free_priv (struct ohci_hcd *hc, urb_priv_t *urb_priv)
if (last >= 0) {
int i;
struct td *td = urb_priv->td [0];
#ifdef CONFIG_PCI
int len = td->urb->transfer_buffer_length;
int dir = usb_pipeout (td->urb->pipe)
? PCI_DMA_TODEVICE
......@@ -36,10 +35,6 @@ static void urb_free_priv (struct ohci_hcd *hc, urb_priv_t *urb_priv)
if (len && td->data_dma)
pci_unmap_single (hc->hcd.pdev,
td->data_dma, len, dir);
#else
# warning "assuming no buffer unmapping is needed"
#endif
for (i = 0; i <= last; i++) {
td = urb_priv->td [i];
if (td)
......@@ -90,7 +85,6 @@ static void intr_resub (struct ohci_hcd *hc, struct urb *urb)
urb_priv_t *urb_priv = urb->hcpriv;
unsigned long flags;
#ifdef CONFIG_PCI
// FIXME rewrite this resubmit path. use pci_dma_sync_single()
// and requeue more cheaply, and only if needed.
// Better yet ... abolish the notion of automagic resubmission.
......@@ -100,7 +94,6 @@ static void intr_resub (struct ohci_hcd *hc, struct urb *urb)
usb_pipeout (urb->pipe)
? PCI_DMA_TODEVICE
: PCI_DMA_FROMDEVICE);
#endif
/* FIXME: MP race. If another CPU partially unlinks
* this URB (urb->status was updated, hasn't yet told
* us to dequeue) before we call complete() here, an
......@@ -236,11 +229,11 @@ static int ep_link (struct ohci_hcd *ohci, struct ed *edi)
break;
case PIPE_INTERRUPT:
load = ed->int_load;
interval = ep_2_n_interval (ed->int_period);
load = ed->intriso.intr_info.int_load;
interval = ep_2_n_interval (ed->intriso.intr_info.int_period);
ed->interval = interval;
int_branch = ep_int_balance (ohci, interval, load);
ed->int_branch = int_branch;
ed->intriso.intr_info.int_branch = int_branch;
for (i = 0; i < ep_rev (6, interval); i += inter) {
inter = 1;
......@@ -355,9 +348,9 @@ static int start_ed_unlink (struct ohci_hcd *ohci, struct ed *ed)
break;
case PIPE_INTERRUPT:
periodic_unlink (ohci, ed, ed->int_branch, ed->interval);
for (i = ed->int_branch; i < NUM_INTS; i += ed->interval)
ohci->ohci_int_load [i] -= ed->int_load;
periodic_unlink (ohci, ed, ed->intriso.intr_info.int_branch, ed->interval);
for (i = ed->intriso.intr_info.int_branch; i < NUM_INTS; i += ed->interval)
ohci->ohci_int_load [i] -= ed->intriso.intr_info.int_load;
#ifdef OHCI_VERBOSE_DEBUG
ohci_dump_periodic (ohci, "UNLINK_INT");
#endif
......@@ -466,8 +459,8 @@ static struct ed *ep_add_ed (
<< 16);
if (ed->type == PIPE_INTERRUPT && ed->state == ED_UNLINK) {
ed->int_period = interval;
ed->int_load = load;
ed->intriso.intr_info.int_period = interval;
ed->intriso.intr_info.int_load = load;
}
spin_unlock_irqrestore (&ohci->lock, flags);
......@@ -563,7 +556,7 @@ td_fill (struct ohci_hcd *ohci, unsigned int info,
td->hwINFO = cpu_to_le32 (info);
if ((td->ed->type) == PIPE_ISOCHRONOUS) {
td->hwCBP = cpu_to_le32 (data & 0xFFFFF000);
td->ed->last_iso = info & 0xffff;
td->ed->intriso.last_iso = info & 0xffff;
} else {
td->hwCBP = cpu_to_le32 (data);
}
......@@ -610,16 +603,11 @@ static void td_submit_urb (struct urb *urb)
urb_priv->td_cnt = 0;
if (data_len) {
#ifdef CONFIG_PCI
data = pci_map_single (ohci->hcd.pdev,
urb->transfer_buffer, data_len,
usb_pipeout (urb->pipe)
? PCI_DMA_TODEVICE
: PCI_DMA_FROMDEVICE
);
#else
# error "what dma addr to use"
#endif
urb->transfer_buffer, data_len,
usb_pipeout (urb->pipe)
? PCI_DMA_TODEVICE
: PCI_DMA_FROMDEVICE);
} else
data = 0;
......@@ -667,14 +655,10 @@ static void td_submit_urb (struct urb *urb)
/* control requests don't use toggle state */
info = TD_CC | TD_DP_SETUP | TD_T_DATA0;
td_fill (ohci, info,
#ifdef CONFIG_PCI
pci_map_single (ohci->hcd.pdev,
urb->setup_packet, 8,
PCI_DMA_TODEVICE),
#else
# error "what dma addr to use"
#endif
8, urb, cnt++);
urb->setup_packet, 8,
PCI_DMA_TODEVICE),
8, urb, cnt++);
if (data_len > 0) {
info = TD_CC | TD_R | TD_T_DATA1;
info |= usb_pipeout (urb->pipe)
......
/*
* OHCI HCD (Host Controller Driver) for USB.
*
* (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
* (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
* (C) Hewlett-Packard Company
*
* SA1111 Bus Glue
*
* Written by Christopher Hoover <ch@hpl.hp.com>
* Based on fragments of previous driver by Rusell King et al.
*
* This file is licenced under the GPL.
*/
#include <asm/hardware.h>
#include <asm/arch/assabet.h>
#include <asm/arch/badge4.h>
#include <asm/hardware/sa1111.h>
#ifndef CONFIG_SA1111
#error "This file is SA-1111 bus glue. CONFIG_SA1111 must be defined."
#endif
/*-------------------------------------------------------------------------*/
static void sa1111_start_hc(void)
{
unsigned int usb_rst = 0;
printk(KERN_DEBUG __FILE__
": starting SA-1111 OHCI USB Controller\n");
#ifdef CONFIG_SA1100_BADGE4
if (machine_is_badge4()) {
badge4_set_5V(BADGE4_5V_USB, 1);
}
#endif
if (machine_is_xp860() ||
machine_has_neponset() ||
machine_is_pfs168() ||
machine_is_badge4())
usb_rst = USB_RESET_PWRSENSELOW | USB_RESET_PWRCTRLLOW;
/*
* Configure the power sense and control lines. Place the USB
* host controller in reset.
*/
USB_RESET = usb_rst | USB_RESET_FORCEIFRESET | USB_RESET_FORCEHCRESET;
/*
* Now, carefully enable the USB clock, and take
* the USB host controller out of reset.
*/
SKPCR |= SKPCR_UCLKEN;
udelay(11);
USB_RESET = usb_rst;
}
static void sa1111_stop_hc(void)
{
printk(KERN_DEBUG __FILE__
": stopping SA-1111 OHCI USB Controller\n");
/*
* Put the USB host controller into reset.
*/
USB_RESET |= USB_RESET_FORCEIFRESET | USB_RESET_FORCEHCRESET;
/*
* Stop the USB clock.
*/
SKPCR &= ~SKPCR_UCLKEN;
#ifdef CONFIG_SA1100_BADGE4
if (machine_is_badge4()) {
/* Disable power to the USB bus */
badge4_set_5V(BADGE4_5V_USB, 0);
}
#endif
}
/*-------------------------------------------------------------------------*/
#if 0
static void dump_hci_status(const char *label)
{
unsigned long status = USB_STATUS;
dbg ("%s USB_STATUS = { %s%s%s%s%s}", label,
((status & USB_STATUS_IRQHCIRMTWKUP) ? "IRQHCIRMTWKUP " : ""),
((status & USB_STATUS_IRQHCIBUFFACC) ? "IRQHCIBUFFACC " : ""),
((status & USB_STATUS_NIRQHCIM) ? "" : "IRQHCIM "),
((status & USB_STATUS_NHCIMFCLR) ? "" : "HCIMFCLR "),
((status & USB_STATUS_USBPWRSENSE) ? "USBPWRSENSE " : ""));
}
#endif
static void usb_hcd_sa1111_hcim_irq (int irq, void *__hcd, struct pt_regs * r)
{
//dump_hci_status("irq");
#if 0
/* may work better this way -- need to investigate further */
if (USB_STATUS & USB_STATUS_NIRQHCIM) {
//dbg ("not normal HC interrupt; ignoring");
return;
}
#endif
usb_hcd_irq(irq, __hcd, r);
}
/*-------------------------------------------------------------------------*/
void usb_hcd_sa1111_remove (struct usb_hcd *);
/* configure so an HC device and id are always provided */
/* always called with process context; sleeping is OK */
/**
* usb_hcd_sa1111_probe - initialize SA-1111-based HCDs
* Context: !in_interrupt()
*
* Allocates basic resources for this USB host controller, and
* then invokes the start() method for the HCD associated with it
* through the hotplug entry's driver_data.
*
* Store this function in the HCD's struct pci_driver as probe().
*/
int usb_hcd_sa1111_probe (const struct hc_driver *driver, struct usb_hcd **hcd_out)
{
int retval;
struct usb_hcd *hcd = 0;
if (!sa1111)
return -ENODEV;
if (!request_mem_region(_USB_OHCI_OP_BASE,
_USB_EXTENT, hcd_name)) {
dbg("request_mem_region failed");
return -EBUSY;
}
sa1111_start_hc();
hcd = driver->hcd_alloc ();
if (hcd == NULL){
dbg ("hcd_alloc failed");
retval = -ENOMEM;
goto err1;
}
hcd->driver = (struct hc_driver *) driver;
hcd->description = driver->description;
hcd->irq = NIRQHCIM;
hcd->regs = (void *) &USB_OHCI_OP_BASE;
hcd->pdev = SA1111_FAKE_PCIDEV;
set_irq_type(NIRQHCIM, IRQT_RISING);
retval = request_irq (NIRQHCIM, usb_hcd_sa1111_hcim_irq, SA_INTERRUPT,
hcd->description, hcd);
if (retval != 0) {
dbg("request_irq failed");
retval = -EBUSY;
goto err2;
}
info ("%s (SA-1111) at 0x%p, irq %d\n",
hcd->description, hcd->regs, hcd->irq);
usb_bus_init (&hcd->self);
hcd->self.op = &usb_hcd_operations;
hcd->self.hcpriv = (void *) hcd;
hcd->self.bus_name = "SA-1111";
hcd->product_desc = "SA-1111 OHCI";
INIT_LIST_HEAD (&hcd->dev_list);
usb_register_bus (&hcd->self);
if ((retval = driver->start (hcd)) < 0)
{
usb_hcd_sa1111_remove(hcd);
return retval;
}
*hcd_out = hcd;
return 0;
err2:
if (hcd) driver->hcd_free(hcd);
err1:
sa1111_stop_hc();
release_mem_region(_USB_OHCI_OP_BASE, _USB_EXTENT);
return retval;
}
/* may be called without controller electrically present */
/* may be called with controller, bus, and devices active */
/**
* usb_hcd_sa1111_remove - shutdown processing for SA-1111-based HCDs
* @dev: USB Host Controller being removed
* Context: !in_interrupt()
*
* Reverses the effect of usb_hcd_sa1111_probe(), first invoking
* the HCD's stop() method. It is always called from a thread
* context, normally "rmmod", "apmd", or something similar.
*
*/
void usb_hcd_sa1111_remove (struct usb_hcd *hcd)
{
struct usb_device *hub;
void *base;
info ("remove: %s, state %x", hcd->self.bus_name, hcd->state);
if (in_interrupt ()) BUG ();
hub = hcd->self.root_hub;
hcd->state = USB_STATE_QUIESCING;
dbg ("%s: roothub graceful disconnect", hcd->self.bus_name);
usb_disconnect (&hub);
hcd->driver->stop (hcd);
hcd->state = USB_STATE_HALT;
free_irq (hcd->irq, hcd);
usb_deregister_bus (&hcd->self);
if (atomic_read (&hcd->self.refcnt) != 1)
err (__FUNCTION__ ": %s, count != 1", hcd->self.bus_name);
base = hcd->regs;
hcd->driver->hcd_free (hcd);
sa1111_stop_hc();
release_mem_region(_USB_OHCI_OP_BASE, _USB_EXTENT);
}
/*-------------------------------------------------------------------------*/
static int __devinit
ohci_sa1111_start (struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
int ret;
if (hcd->pdev) {
ohci->hcca = pci_alloc_consistent (hcd->pdev,
sizeof *ohci->hcca, &ohci->hcca_dma);
if (!ohci->hcca)
return -ENOMEM;
}
memset (ohci->hcca, 0, sizeof (struct ohci_hcca));
if ((ret = ohci_mem_init (ohci)) < 0) {
ohci_stop (hcd);
return ret;
}
ohci->regs = hcd->regs;
ohci->parent_dev = &sa1111->dev;
if (hc_reset (ohci) < 0) {
ohci_stop (hcd);
return -ENODEV;
}
if (hc_start (ohci) < 0) {
err ("can't start %s", ohci->hcd.self.bus_name);
ohci_stop (hcd);
return -EBUSY;
}
#ifdef DEBUG
ohci_dump (ohci, 1);
#endif
return 0;
}
/*-------------------------------------------------------------------------*/
static const struct hc_driver ohci_sa1111_hc_driver = {
description: hcd_name,
/*
* generic hardware linkage
*/
irq: ohci_irq,
flags: HCD_USB11,
/*
* basic lifecycle operations
*/
start: ohci_sa1111_start,
#ifdef CONFIG_PM
/* suspend: ohci_sa1111_suspend, -- tbd */
/* resume: ohci_sa1111_resume, -- tbd */
#endif
stop: ohci_stop,
/*
* memory lifecycle (except per-request)
*/
hcd_alloc: ohci_hcd_alloc,
hcd_free: ohci_hcd_free,
/*
* managing i/o requests and associated device resources
*/
urb_enqueue: ohci_urb_enqueue,
urb_dequeue: ohci_urb_dequeue,
free_config: ohci_free_config,
/*
* scheduling support
*/
get_frame_number: ohci_get_frame,
/*
* root hub support
*/
hub_status_data: ohci_hub_status_data,
hub_control: ohci_hub_control,
};
/*-------------------------------------------------------------------------*/
/* Only one SA-1111 ever exists. */
static struct usb_hcd *the_sa1111_hcd;
static int __init ohci_hcd_sa1111_init (void)
{
dbg (DRIVER_INFO " (SA-1111)");
dbg ("block sizes: ed %d td %d",
sizeof (struct ed), sizeof (struct td));
the_sa1111_hcd = 0;
return usb_hcd_sa1111_probe(&ohci_sa1111_hc_driver, &the_sa1111_hcd);
}
module_init (ohci_hcd_sa1111_init);
static void __exit ohci_hcd_sa1111_cleanup (void)
{
if (the_sa1111_hcd) {
usb_hcd_sa1111_remove(the_sa1111_hcd);
the_sa1111_hcd = 0;
}
}
module_exit (ohci_hcd_sa1111_cleanup);
......@@ -37,9 +37,9 @@ struct ed {
u8 int_period;
u8 int_branch;
u8 int_load;
};
} intr_info;
u16 last_iso; /* isochronous */
};
} intriso;
u8 state; /* ED_{NEW,UNLINK,OPER} */
#define ED_NEW 0x00 /* unused, no dummy td */
......@@ -331,6 +331,11 @@ struct hash_list_t {
struct ohci_hcd {
spinlock_t lock;
/*
* parent device
*/
struct device *parent_dev;
/*
* I/O memory used to communicate with the HC (uncached);
*/
......@@ -348,12 +353,10 @@ struct ohci_hcd {
struct ed *ed_controltail; /* last in ctrl list */
struct ed *ed_isotail; /* last in iso list */
#ifdef CONFIG_PCI
struct pci_pool *td_cache;
struct pci_pool *ed_cache;
struct hash_list_t td_hash [TD_HASH_SIZE];
struct hash_list_t ed_hash [ED_HASH_SIZE];
#endif
/*
* driver state
......
......@@ -21,6 +21,12 @@
#endif
#endif
int __devinit
hc_add_ohci(struct pci_dev *dev, int irq, void *membase, unsigned long flags,
ohci_t **ohci, const char *name, const char *slot_name);
extern void hc_remove_ohci(ohci_t *ohci);
extern int hc_start (ohci_t * ohci, struct device *parent_dev);
extern int hc_reset (ohci_t * ohci);
/*-------------------------------------------------------------------------*/
......@@ -103,7 +109,8 @@ static void hc_restart (ohci_t *ohci)
ohci->ed_controltail = NULL;
ohci->ed_bulktail = NULL;
if ((temp = hc_reset (ohci)) < 0 || (temp = hc_start (ohci)) < 0) {
if ((temp = hc_reset (ohci)) < 0 ||
(temp = hc_start (ohci, &ohci->ohci_dev->dev)) < 0) {
err ("can't restart usb-%s, %d", ohci->ohci_dev->slot_name, temp);
} else
dbg ("restart usb-%s completed", ohci->ohci_dev->slot_name);
......@@ -171,6 +178,7 @@ static void __devexit
ohci_pci_remove (struct pci_dev *dev)
{
ohci_t *ohci = (ohci_t *) pci_get_drvdata(dev);
void *membase = ohci->regs;
dbg ("remove %s controller usb-%s%s%s",
hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS),
......@@ -181,6 +189,9 @@ ohci_pci_remove (struct pci_dev *dev)
hc_remove_ohci(ohci);
/* unmap the IO address space */
iounmap (membase);
release_mem_region (pci_resource_start (dev, 0), pci_resource_len (dev, 0));
}
......
......@@ -12,6 +12,7 @@
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/usb.h>
#include <linux/pci.h>
#include <linux/errno.h>
#include <asm/hardware.h>
......@@ -27,6 +28,9 @@ int __devinit
hc_add_ohci(struct pci_dev *dev, int irq, void *membase, unsigned long flags,
ohci_t **ohci, const char *name, const char *slot_name);
extern void hc_remove_ohci(ohci_t *ohci);
extern int hc_start (ohci_t * ohci, struct device *parent_dev);
extern int hc_reset (ohci_t * ohci);
static ohci_t *sa1111_ohci;
......@@ -34,6 +38,15 @@ static void __init sa1111_ohci_configure(void)
{
unsigned int usb_rst = 0;
printk(KERN_DEBUG __FILE__
": starting SA-1111 OHCI USB Controller\n");
#ifdef CONFIG_SA1100_BADGE4
if (machine_is_badge4())
/* power the bus */
badge4_set_5V(BADGE4_5V_USB, 1);
#endif
if (machine_is_xp860() ||
machine_has_neponset() ||
machine_is_pfs168() ||
......@@ -55,6 +68,28 @@ static void __init sa1111_ohci_configure(void)
USB_RESET = usb_rst;
}
static void __exit sa1111_ohci_unconfigure(void)
{
printk(KERN_DEBUG __FILE__
": stopping SA-1111 OHCI USB Controller\n");
/*
* Put the USB host controller into reset.
*/
USB_RESET |= USB_RESET_FORCEIFRESET | USB_RESET_FORCEHCRESET;
/*
* Stop the USB clock.
*/
SKPCR &= ~SKPCR_UCLKEN;
#ifdef CONFIG_SA1100_BADGE4
if (machine_is_badge4())
badge4_set_5V(BADGE4_5V_USB, 0);
#endif
}
static int __init sa1111_ohci_init(void)
{
int ret;
......@@ -65,66 +100,54 @@ static int __init sa1111_ohci_init(void)
/*
* Request memory resources.
*/
// if (!request_mem_region(_USB_OHCI_OP_BASE, _USB_EXTENT, "usb-ohci"))
// return -EBUSY;
if (!request_mem_region(_USB_OHCI_OP_BASE, _USB_EXTENT, "usb-ohci"))
return -EBUSY;
sa1111_ohci_configure();
/*
* Initialise the generic OHCI driver.
*/
ret = hc_add_ohci((struct pci_dev *)1, NIRQHCIM,
sa1111_ohci = 0;
ret = hc_add_ohci(SA1111_FAKE_PCIDEV, NIRQHCIM,
(void *)&USB_OHCI_OP_BASE, 0, &sa1111_ohci,
"usb-ohci", "sa1111");
if (ret == 0) {
if (hc_start (sa1111_ohci, &sa1111->dev) < 0) {
err ("can't start usb-%s", sa1111_ohci->slot_name);
hc_remove_ohci (sa1111_ohci);
return -EBUSY;
}
if (ret || !sa1111_ohci) {
sa1111_ohci = 0;
sa1111_ohci_unconfigure();
release_mem_region(_USB_OHCI_OP_BASE, _USB_EXTENT);
return -EBUSY;
}
#ifdef DEBUG
ohci_dump (ohci, 1);
#endif
#ifdef CONFIG_SA1100_BADGE4
if (machine_is_badge4()) {
/* found the controller, so now power the bus */
badge4_set_5V(BADGE4_5V_USB, 1);
}
#endif
if (hc_start (sa1111_ohci, &sa1111->dev) < 0) {
err ("can't start usb-%s", sa1111_ohci->slot_name);
hc_remove_ohci (sa1111_ohci);
sa1111_ohci = 0;
sa1111_ohci_unconfigure();
release_mem_region(_USB_OHCI_OP_BASE, _USB_EXTENT);
return -EBUSY;
}
// else
// release_mem_region(_USB_OHCI_OP_BASE, _USB_EXTENT);
return ret;
return 0;
}
static void __exit sa1111_ohci_exit(void)
{
hc_remove_ohci(sa1111_ohci);
printk(KERN_DEBUG __FUNCTION__ ": cleaning up\n");
/*
* Put the USB host controller into reset.
*/
USB_RESET |= USB_RESET_FORCEIFRESET | USB_RESET_FORCEHCRESET;
/*
* Stop the USB clock.
*/
SKPCR &= ~SKPCR_UCLKEN;
if (sa1111_ohci) {
hc_remove_ohci(sa1111_ohci);
sa1111_ohci = 0;
}
/*
* Release memory resources.
*/
// release_mem_region(_USB_OHCI_OP_BASE, _USB_EXTENT);
sa1111_ohci_unconfigure();
release_mem_region(_USB_OHCI_OP_BASE, _USB_EXTENT);
#ifdef CONFIG_SA1100_BADGE4
if (machine_is_badge4()) {
badge4_set_5V(BADGE4_5V_USB, 0);
}
#endif
printk(KERN_DEBUG __FUNCTION__ ": exiting\n");
}
module_init(sa1111_ohci_init);
module_exit(sa1111_ohci_exit);
MODULE_LICENSE("GPL");
......@@ -66,7 +66,12 @@
#include <linux/timer.h>
#include <linux/list.h>
#include <linux/interrupt.h> /* for in_interrupt() */
#undef DEBUG
#ifdef CONFIG_USB_DEBUG
# define DEBUG
#else
# undef DEBUG
#endif
#include <linux/usb.h>
#include <asm/io.h>
......@@ -2391,7 +2396,11 @@ static ohci_t * __devinit hc_alloc_ohci (struct pci_dev *dev, void * mem_base)
return NULL;
}
ohci->bus->hcpriv = (void *) ohci;
#ifdef CONFIG_PCI
ohci->bus->bus_name = dev->slot_name;
#else
ohci->bus->bus_name = "ohci-hc";
#endif
return ohci;
}
......@@ -2430,9 +2439,6 @@ static void hc_release_ohci (ohci_t * ohci)
ohci_mem_cleanup (ohci);
/* unmap the IO address space */
iounmap (ohci->regs);
pci_free_consistent (ohci->ohci_dev, sizeof *ohci->hcca,
ohci->hcca, ohci->hcca_dma);
kfree (ohci);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment