Commit db4b1c4a authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

USB OHCI driver: Added SA1111 support

  
Added the patch from Russell King <rmk@arm.linux.org.uk> that splits
the usb-ohci driver into two pieces, enabling the sa1111 hardware to
work with the driver.
  
I also added some changes to get the usb-ohci-pci.o module to build
and run properly.
parent 1138d8a0
......@@ -4,7 +4,13 @@
mainmenu_option next_comment
comment 'USB support'
dep_tristate 'Support for USB' CONFIG_USB $CONFIG_PCI
# ARM SA1111 chips have a non-PCI based "OHCI-compatible" USB host interface.
if [ "$CONFIG_PCI" = "y" -o "$CONFIG_SA1111" = "y" ]; then
tristate 'Support for USB' CONFIG_USB
else
define_bool CONFIG_USB n
fi
if [ "$CONFIG_USB" = "y" -o "$CONFIG_USB" = "m" ]; then
source drivers/usb/core/Config.in
......
......@@ -6,9 +6,11 @@
mod-subdirs := serial
obj-$(CONFIG_USB) += core/
obj-$(CONFIG_USB_EHCI_HCD) += host/
obj-$(CONFIG_USB_OHCI_HCD) += host/
obj-$(CONFIG_USB_OHCI) += host/
obj-$(CONFIG_USB_OHCI_SA1111) += host/
obj-$(CONFIG_USB_SL811HS) += host/
obj-$(CONFIG_USB_UHCI_ALT) += host/
obj-$(CONFIG_USB_UHCI_HCD_ALT) += host/
......
......@@ -20,5 +20,6 @@ fi
#fi
#dep_tristate ' OHCI (Compaq, iMacs, OPTi, SiS, ALi, ...) support' CONFIG_USB_OHCI $CONFIG_USB
if [ "$CONFIG_ARM" = "y" ]; then
dep_tristate ' SA1111 OHCI-compatible host interface support' CONFIG_USB_OHCI_SA1111 $CONFIG_USB
dep_tristate ' SL811HS support' CONFIG_USB_SL811HS $CONFIG_USB
fi
......@@ -3,6 +3,8 @@
# framework and drivers
#
export-objs := usb-ohci.o
obj-$(CONFIG_USB_EHCI_HCD) += ehci-hcd.o
obj-$(CONFIG_USB_OHCI_HCD) += ohci-hcd.o
obj-$(CONFIG_USB_UHCI_HCD) += usb-uhci-hcd.o
......@@ -10,7 +12,8 @@ obj-$(CONFIG_USB_UHCI_HCD_ALT) += uhci-hcd.o
obj-$(CONFIG_USB_UHCI) += usb-uhci.o
obj-$(CONFIG_USB_UHCI_ALT) += uhci.o
obj-$(CONFIG_USB_OHCI) += usb-ohci.o
obj-$(CONFIG_USB_OHCI) += usb-ohci.o usb-ohci-pci.o
obj-$(CONFIG_USB_OHCI_SA1111) += usb-ohci.o usb-ohci-sa1111.o
obj-$(CONFIG_USB_SL811HS) += hc_sl811.o
include $(TOPDIR)/Rules.make
#include <linux/config.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/interrupt.h> /* for in_interrupt() */
#undef DEBUG
#include <linux/usb.h>
#include "../core/hcd.h"
#include "usb-ohci.h"
#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
/*-------------------------------------------------------------------------*/
/* Increment the module usage count, start the control thread and
* return success. */
static struct pci_driver ohci_pci_driver;
static int __devinit
hc_found_ohci (struct pci_dev *dev, int irq,
void *mem_base, const struct pci_device_id *id)
{
u8 latency, limit;
ohci_t * ohci;
int ret;
printk(KERN_INFO __FILE__ ": usb-%s, %s\n", dev->slot_name, dev->name);
/* bad pci latencies can contribute to overruns */
pci_read_config_byte (dev, PCI_LATENCY_TIMER, &latency);
if (latency) {
pci_read_config_byte (dev, PCI_MAX_LAT, &limit);
if (limit && limit < latency) {
dbg ("PCI latency reduced to max %d", limit);
pci_write_config_byte (dev, PCI_LATENCY_TIMER, limit);
latency = limit;
}
}
ret = hc_add_ohci(dev, irq, mem_base, id->driver_data,
&ohci, ohci_pci_driver.name, dev->slot_name);
if (ret == 0) {
ohci->pci_latency = latency;
if (hc_start (ohci, &ohci->ohci_dev->dev) < 0) {
err ("can't start usb-%s", ohci->slot_name);
hc_remove_ohci(ohci);
return -EBUSY;
}
#ifdef DEBUG
ohci_dump (ohci, 1);
#endif
}
return ret;
}
/*-------------------------------------------------------------------------*/
#ifdef CONFIG_PM
/* controller died; cleanup debris, then restart */
/* must not be called from interrupt context */
static void hc_restart (ohci_t *ohci)
{
int temp;
int i;
if (ohci->pci_latency)
pci_write_config_byte (ohci->ohci_dev, PCI_LATENCY_TIMER, ohci->pci_latency);
ohci->disabled = 1;
ohci->sleeping = 0;
if (ohci->bus->root_hub)
usb_disconnect (&ohci->bus->root_hub);
/* empty the interrupt branches */
for (i = 0; i < NUM_INTS; i++) ohci->ohci_int_load[i] = 0;
for (i = 0; i < NUM_INTS; i++) ohci->hcca->int_table[i] = 0;
/* no EDs to remove */
ohci->ed_rm_list [0] = NULL;
ohci->ed_rm_list [1] = NULL;
/* empty control and bulk lists */
ohci->ed_isotail = NULL;
ohci->ed_controltail = NULL;
ohci->ed_bulktail = NULL;
if ((temp = hc_reset (ohci)) < 0 || (temp = hc_start (ohci)) < 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);
}
#endif /* CONFIG_PM */
/*-------------------------------------------------------------------------*/
/* configured so that an OHCI device is always provided */
/* always called with process context; sleeping is OK */
static int __devinit
ohci_pci_probe (struct pci_dev *dev, const struct pci_device_id *id)
{
unsigned long mem_resource, mem_len;
void *mem_base;
int status;
if (pci_enable_device(dev) < 0)
return -ENODEV;
if (!dev->irq) {
err("found OHCI device with no IRQ assigned. check BIOS settings!");
pci_disable_device (dev);
return -ENODEV;
}
/* we read its hardware registers as memory */
mem_resource = pci_resource_start(dev, 0);
mem_len = pci_resource_len(dev, 0);
if (!request_mem_region (mem_resource, mem_len, ohci_pci_driver.name)) {
dbg ("controller already in use");
pci_disable_device (dev);
return -EBUSY;
}
mem_base = ioremap_nocache (mem_resource, mem_len);
if (!mem_base) {
err("Error mapping OHCI memory");
release_mem_region(mem_resource, mem_len);
pci_disable_device (dev);
return -EFAULT;
}
/* controller writes into our memory */
pci_set_master (dev);
status = hc_found_ohci (dev, dev->irq, mem_base, id);
if (status < 0) {
iounmap (mem_base);
release_mem_region(mem_resource, mem_len);
pci_disable_device (dev);
}
return status;
}
/*-------------------------------------------------------------------------*/
/* may be called from interrupt context [interface spec] */
/* may be called without controller present */
/* may be called with controller, bus, and devices active */
static void __devexit
ohci_pci_remove (struct pci_dev *dev)
{
ohci_t *ohci = (ohci_t *) pci_get_drvdata(dev);
dbg ("remove %s controller usb-%s%s%s",
hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS),
dev->slot_name,
ohci->disabled ? " (disabled)" : "",
in_interrupt () ? " in interrupt" : ""
);
hc_remove_ohci(ohci);
release_mem_region (pci_resource_start (dev, 0), pci_resource_len (dev, 0));
}
#ifdef CONFIG_PM
/*-------------------------------------------------------------------------*/
static int
ohci_pci_suspend (struct pci_dev *dev, u32 state)
{
ohci_t *ohci = (ohci_t *) pci_get_drvdata(dev);
unsigned long flags;
u16 cmd;
if ((ohci->hc_control & OHCI_CTRL_HCFS) != OHCI_USB_OPER) {
dbg ("can't suspend usb-%s (state is %s)", dev->slot_name,
hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS));
return -EIO;
}
/* act as if usb suspend can always be used */
info ("USB suspend: usb-%s", dev->slot_name);
ohci->sleeping = 1;
/* First stop processing */
spin_lock_irqsave (&usb_ed_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 (&usb_ed_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 (ohci->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("Bus in reset phase ???");
break;
case OHCI_USB_RESUME:
dbg("Bus in resume phase ???");
break;
case OHCI_USB_OPER:
dbg("Bus in operational phase ???");
break;
case OHCI_USB_SUSPEND:
dbg("Bus suspended");
break;
}
/* In some rare situations, Apple's OHCI have happily trashed
* memory during sleep. We disable it's bus master bit during
* suspend
*/
pci_read_config_word (dev, PCI_COMMAND, &cmd);
cmd &= ~PCI_COMMAND_MASTER;
pci_write_config_word (dev, PCI_COMMAND, cmd);
#ifdef CONFIG_PMAC_PBOOK
{
struct device_node *of_node;
/* Disable USB PAD & cell clock */
of_node = pci_device_to_OF_node (ohci->ohci_dev);
if (of_node && _machine == _MACH_Pmac)
pmac_call_feature(PMAC_FTR_USB_ENABLE, of_node, 0, 0);
}
#endif
return 0;
}
/*-------------------------------------------------------------------------*/
static int
ohci_pci_resume (struct pci_dev *dev)
{
ohci_t *ohci = (ohci_t *) pci_get_drvdata(dev);
int temp;
unsigned long flags;
/* guard against multiple resumes */
atomic_inc (&ohci->resume_count);
if (atomic_read (&ohci->resume_count) != 1) {
err ("concurrent PCI resumes for usb-%s", dev->slot_name);
atomic_dec (&ohci->resume_count);
return 0;
}
#ifdef CONFIG_PMAC_PBOOK
{
struct device_node *of_node;
/* Re-enable USB PAD & cell clock */
of_node = pci_device_to_OF_node (ohci->ohci_dev);
if (of_node && _machine == _MACH_Pmac)
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->ohci_dev);
switch (temp) {
case OHCI_USB_RESET: // lost power
info ("USB restart: usb-%s", dev->slot_name);
hc_restart (ohci);
break;
case OHCI_USB_SUSPEND: // host wakeup
case OHCI_USB_RESUME: // remote wakeup
info ("USB continue: usb-%s from %s wakeup", dev->slot_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 usb-%s won't resume", dev->slot_name);
ohci->disabled = 1;
return -EIO;
}
/* 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 (&usb_ed_lock, flags);
ohci->disabled = 0;
ohci->sleeping = 0;
ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER;
if (!ohci->ed_rm_list[0] && !ohci->ed_rm_list[1]) {
if (ohci->ed_controltail)
ohci->hc_control |= OHCI_CTRL_CLE;
if (ohci->ed_bulktail)
ohci->hc_control |= OHCI_CTRL_BLE;
}
writel (ohci->hc_control, &ohci->regs->control);
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 (&usb_ed_lock, flags);
#ifdef CONFIG_PMAC_PBOOK
if (_machine == _MACH_Pmac)
enable_irq (ohci->irq);
#endif
if (ohci->hcca->done_head)
dl_done_list (ohci, dl_reverse_done_list (ohci));
writel (OHCI_INTR_WDH, &ohci->regs->intrenable);
writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */
writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */
break;
default:
warn ("odd PCI resume for usb-%s", dev->slot_name);
}
/* controller is operational, extra resumes are harmless */
atomic_dec (&ohci->resume_count);
return 0;
}
#endif /* CONFIG_PM */
/*-------------------------------------------------------------------------*/
static const struct pci_device_id __devinitdata ohci_pci_ids [] = { {
/*
* AMD-756 [Viper] USB has a serious erratum when used with
* lowspeed devices like mice.
*/
vendor: 0x1022,
device: 0x740c,
subvendor: PCI_ANY_ID,
subdevice: PCI_ANY_ID,
driver_data: OHCI_QUIRK_AMD756,
} , {
/* handle any USB OHCI controller */
class: ((PCI_CLASS_SERIAL_USB << 8) | 0x10),
class_mask: ~0,
/* 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, ohci_pci_ids);
static struct pci_driver ohci_pci_driver = {
name: "usb-ohci",
id_table: &ohci_pci_ids [0],
probe: ohci_pci_probe,
remove: __devexit_p(ohci_pci_remove),
#ifdef CONFIG_PM
suspend: ohci_pci_suspend,
resume: ohci_pci_resume,
#endif /* PM */
};
/*-------------------------------------------------------------------------*/
static int __init ohci_hcd_init (void)
{
return pci_module_init (&ohci_pci_driver);
}
/*-------------------------------------------------------------------------*/
static void __exit ohci_hcd_cleanup (void)
{
pci_unregister_driver (&ohci_pci_driver);
}
module_init (ohci_hcd_init);
module_exit (ohci_hcd_cleanup);
MODULE_LICENSE("GPL");
/*
* linux/drivers/usb/usb-ohci-sa1111.c
*
* The outline of this code was taken from Brad Parkers <brad@heeltoe.com>
* original OHCI driver modifications, and reworked into a cleaner form
* by Russell King <rmk@arm.linux.org.uk>.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/usb.h>
#include <linux/errno.h>
#include <asm/hardware.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/assabet.h>
#include <asm/arch/badge4.h>
#include <asm/hardware/sa1111.h>
#include "usb-ohci.h"
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);
static ohci_t *sa1111_ohci;
static void __init sa1111_ohci_configure(void)
{
unsigned int usb_rst = 0;
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 int __init sa1111_ohci_init(void)
{
int ret;
if (!sa1111)
return -ENODEV;
/*
* Request memory resources.
*/
// 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,
(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;
}
#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
}
// else
// release_mem_region(_USB_OHCI_OP_BASE, _USB_EXTENT);
return ret;
}
static void __exit sa1111_ohci_exit(void)
{
hc_remove_ohci(sa1111_ohci);
/*
* Put the USB host controller into reset.
*/
USB_RESET |= USB_RESET_FORCEIFRESET | USB_RESET_FORCEHCRESET;
/*
* Stop the USB clock.
*/
SKPCR &= ~SKPCR_UCLKEN;
/*
* Release memory resources.
*/
// 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
}
module_init(sa1111_ohci_init);
module_exit(sa1111_ohci_exit);
......@@ -82,16 +82,6 @@
#include "usb-ohci.h"
#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
/*
* Version Information
*/
......@@ -99,14 +89,10 @@
#define DRIVER_AUTHOR "Roman Weissgaerber <weissg@vienna.at>, David Brownell"
#define DRIVER_DESC "USB OHCI Host Controller Driver"
/* For initializing controller (mask in an HCFS mode too) */
#define OHCI_CONTROL_INIT \
(OHCI_CTRL_CBSR & 0x3) | OHCI_CTRL_IE | OHCI_CTRL_PLE
#define OHCI_UNLINK_TIMEOUT (HZ / 10)
static LIST_HEAD (ohci_hcd_list);
static spinlock_t usb_ed_lock = SPIN_LOCK_UNLOCKED;
spinlock_t usb_ed_lock = SPIN_LOCK_UNLOCKED;
/*-------------------------------------------------------------------------*/
......@@ -443,7 +429,7 @@ static void ohci_dump_roothub (ohci_t *controller, int verbose)
static void ohci_dump (ohci_t *controller, int verbose)
{
dbg ("OHCI controller usb-%s state", controller->ohci_dev->slot_name);
dbg ("OHCI controller usb-%s state", controller->slot_name);
// dumps some of the state we know about
ohci_dump_status (controller);
......@@ -465,7 +451,7 @@ static void ohci_dump (ohci_t *controller, int verbose)
static int sohci_return_urb (struct ohci *hc, struct urb * urb)
{
urb_priv_t * urb_priv = urb->hcpriv;
struct urb * urbt;
struct urb * urbt = NULL;
unsigned long flags;
int i;
......@@ -499,7 +485,7 @@ static int sohci_return_urb (struct ohci *hc, struct urb * urb)
break;
case PIPE_ISOCHRONOUS:
for (urbt = urb->next; urbt && (urbt != urb); urbt = urbt->next);
// for (urbt = urb->next; urbt && (urbt != urb); urbt = urbt->next);
if (urbt) { /* send the reply and requeue URB */
pci_unmap_single (hc->ohci_dev,
urb_priv->td [0]->data_dma,
......@@ -865,7 +851,7 @@ static int sohci_free_dev (struct usb_device * usb_dev)
if (ed->state == ED_OPER) {
/* driver on that interface didn't unlink an urb */
dbg ("driver usb-%s dev %d ed 0x%x unfreed URB",
ohci->ohci_dev->slot_name, usb_dev->devnum, i);
ohci->slot_name, usb_dev->devnum, i);
ep_unlink (ohci, ed);
}
ep_rm_ed (usb_dev, ed);
......@@ -910,7 +896,7 @@ static int sohci_free_dev (struct usb_device * usb_dev)
} else {
/* likely some interface's driver has a refcount bug */
err ("bus %s devnum %d deletion in interrupt",
ohci->ohci_dev->slot_name, usb_dev->devnum);
ohci->slot_name, usb_dev->devnum);
BUG ();
}
}
......@@ -1529,7 +1515,7 @@ static void dl_del_urb (struct urb * urb)
/* 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)
td_t * dl_reverse_done_list (ohci_t * ohci)
{
__u32 td_list_hc;
td_t * td_rev = NULL;
......@@ -1677,7 +1663,7 @@ static void dl_del_list (ohci_t * ohci, unsigned int frame)
/* td done list */
static void dl_done_list (ohci_t * ohci, td_t * td_list)
void dl_done_list (ohci_t * ohci, td_t * td_list)
{
td_t * td_list_next = NULL;
ed_t * ed;
......@@ -1822,7 +1808,7 @@ static int rh_send_irq (ohci_t * ohci, void * rh_data, int rh_len)
num_ports = roothub_a (ohci) & RH_A_NDP;
if (num_ports > MAX_ROOT_PORTS) {
err ("bogus NDP=%d for OHCI usb-%s", num_ports,
ohci->ohci_dev->slot_name);
ohci->slot_name);
err ("rereads as NDP=%d",
readl (&ohci->regs->roothub.a) & RH_A_NDP);
/* retry later; "should not happen" */
......@@ -2148,7 +2134,7 @@ static int rh_unlink_urb (struct urb * urb)
/* reset the HC and BUS */
static int hc_reset (ohci_t * ohci)
int hc_reset (ohci_t * ohci)
{
int timeout = 30;
int smm_timeout = 50; /* 0,5 sec */
......@@ -2169,7 +2155,7 @@ static int hc_reset (ohci_t * ohci)
writel (OHCI_INTR_MIE, &ohci->regs->intrdisable);
dbg("USB HC reset_hc usb-%s: ctrl = 0x%x ;",
ohci->ohci_dev->slot_name,
ohci->slot_name,
readl (&ohci->regs->control));
/* Reset USB (needed by some controllers) */
......@@ -2193,7 +2179,7 @@ static int hc_reset (ohci_t * ohci)
* enable interrupts
* connect the virtual root hub */
static int hc_start (ohci_t * ohci)
int hc_start (ohci_t * ohci, struct device *parent_dev)
{
__u32 mask;
unsigned int fminterval;
......@@ -2247,7 +2233,7 @@ static int hc_start (ohci_t * ohci)
dev = usb_to_ohci (usb_dev);
ohci->bus->root_hub = usb_dev;
usb_connect (usb_dev);
if (usb_register_root_hub (usb_dev, &ohci->ohci_dev->dev) != 0) {
if (usb_register_root_hub (usb_dev, parent_dev) != 0) {
usb_free_dev (usb_dev);
ohci->disabled = 1;
return -ENODEV;
......@@ -2308,7 +2294,7 @@ static void hc_interrupt (int irq, void * __ohci, struct pt_regs * r)
if (ints & OHCI_INTR_UE) {
ohci->disabled++;
err ("OHCI Unrecoverable Error, controller usb-%s disabled",
ohci->ohci_dev->slot_name);
ohci->slot_name);
// e.g. due to PCI Master/Target Abort
#ifdef DEBUG
......@@ -2385,7 +2371,9 @@ static ohci_t * __devinit hc_alloc_ohci (struct pci_dev *dev, void * mem_base)
ohci->regs = mem_base;
ohci->ohci_dev = dev;
#ifdef CONFIG_PCI
pci_set_drvdata(dev, ohci);
#endif
INIT_LIST_HEAD (&ohci->ohci_hcd_list);
list_add (&ohci->ohci_hcd_list, &ohci_hcd_list);
......@@ -2394,7 +2382,9 @@ static ohci_t * __devinit hc_alloc_ohci (struct pci_dev *dev, void * mem_base)
ohci->bus = usb_alloc_bus (&sohci_device_operations);
if (!ohci->bus) {
#ifdef CONFIG_PCI
pci_set_drvdata (dev, NULL);
#endif
pci_free_consistent (ohci->ohci_dev, sizeof *ohci->hcca,
ohci->hcca, ohci->hcca_dma);
kfree (ohci);
......@@ -2413,7 +2403,7 @@ static ohci_t * __devinit hc_alloc_ohci (struct pci_dev *dev, void * mem_base)
static void hc_release_ohci (ohci_t * ohci)
{
dbg ("USB HC release ohci usb-%s", ohci->ohci_dev->slot_name);
dbg ("USB HC release ohci usb-%s", ohci->slot_name);
/* disconnect all devices */
if (ohci->bus->root_hub)
......@@ -2426,7 +2416,9 @@ static void hc_release_ohci (ohci_t * ohci)
free_irq (ohci->irq, ohci);
ohci->irq = -1;
}
#ifdef CONFIG_PCI
pci_set_drvdata(ohci->ohci_dev, NULL);
#endif
if (ohci->bus) {
if (ohci->bus->busnum)
usb_deregister_bus (ohci->bus);
......@@ -2448,18 +2440,15 @@ static void hc_release_ohci (ohci_t * ohci)
/*-------------------------------------------------------------------------*/
/* Increment the module usage count, start the control thread and
* return success. */
static struct pci_driver ohci_pci_driver;
static int __devinit
hc_found_ohci (struct pci_dev *dev, int irq,
void *mem_base, const struct pci_device_id *id)
/*
* Host bus independent add one OHCI host controller.
*/
int
hc_add_ohci(struct pci_dev *dev, int irq, void *mem_base, unsigned long flags,
ohci_t **ohcip, const char *name, const char *slot_name)
{
ohci_t * ohci;
u8 latency, limit;
char buf[8], *bufp = buf;
ohci_t * ohci;
int ret;
#ifndef __sparc__
......@@ -2469,34 +2458,20 @@ hc_found_ohci (struct pci_dev *dev, int irq,
#endif
printk(KERN_INFO __FILE__ ": USB OHCI at membase 0x%lx, IRQ %s\n",
(unsigned long) mem_base, bufp);
printk(KERN_INFO __FILE__ ": usb-%s, %s\n", dev->slot_name, dev->name);
ohci = hc_alloc_ohci (dev, mem_base);
if (!ohci) {
return -ENOMEM;
}
ohci->slot_name = slot_name;
if ((ret = ohci_mem_init (ohci)) < 0) {
hc_release_ohci (ohci);
return ret;
}
ohci->flags = id->driver_data;
ohci->flags = flags;
if (ohci->flags & OHCI_QUIRK_AMD756)
printk (KERN_INFO __FILE__ ": AMD756 erratum 4 workaround\n");
/* bad pci latencies can contribute to overruns */
pci_read_config_byte (dev, PCI_LATENCY_TIMER, &latency);
if (latency) {
pci_read_config_byte (dev, PCI_MAX_LAT, &limit);
if (limit && limit < latency) {
dbg ("PCI latency reduced to max %d", limit);
pci_write_config_byte (dev, PCI_LATENCY_TIMER, limit);
ohci->pci_latency = limit;
} else {
/* it might already have been reduced */
ohci->pci_latency = latency;
}
}
if (hc_reset (ohci) < 0) {
hc_release_ohci (ohci);
return -ENODEV;
......@@ -2508,134 +2483,23 @@ hc_found_ohci (struct pci_dev *dev, int irq,
usb_register_bus (ohci->bus);
if (request_irq (irq, hc_interrupt, SA_SHIRQ,
ohci_pci_driver.name, ohci) != 0) {
if (request_irq (irq, hc_interrupt, SA_SHIRQ, name, ohci) != 0) {
err ("request interrupt %s failed", bufp);
hc_release_ohci (ohci);
return -EBUSY;
}
ohci->irq = irq;
if (hc_start (ohci) < 0) {
err ("can't start usb-%s", dev->slot_name);
hc_release_ohci (ohci);
return -EBUSY;
}
*ohcip = ohci;
#ifdef DEBUG
ohci_dump (ohci, 1);
#endif
return 0;
}
/*-------------------------------------------------------------------------*/
#ifdef CONFIG_PM
/* controller died; cleanup debris, then restart */
/* must not be called from interrupt context */
static void hc_restart (ohci_t *ohci)
{
int temp;
int i;
if (ohci->pci_latency)
pci_write_config_byte (ohci->ohci_dev, PCI_LATENCY_TIMER, ohci->pci_latency);
ohci->disabled = 1;
ohci->sleeping = 0;
if (ohci->bus->root_hub)
usb_disconnect (&ohci->bus->root_hub);
/* empty the interrupt branches */
for (i = 0; i < NUM_INTS; i++) ohci->ohci_int_load[i] = 0;
for (i = 0; i < NUM_INTS; i++) ohci->hcca->int_table[i] = 0;
/* no EDs to remove */
ohci->ed_rm_list [0] = NULL;
ohci->ed_rm_list [1] = NULL;
/* empty control and bulk lists */
ohci->ed_isotail = NULL;
ohci->ed_controltail = NULL;
ohci->ed_bulktail = NULL;
if ((temp = hc_reset (ohci)) < 0 || (temp = hc_start (ohci)) < 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);
}
#endif /* CONFIG_PM */
/*-------------------------------------------------------------------------*/
/* configured so that an OHCI device is always provided */
/* always called with process context; sleeping is OK */
static int __devinit
ohci_pci_probe (struct pci_dev *dev, const struct pci_device_id *id)
{
unsigned long mem_resource, mem_len;
void *mem_base;
int status;
if (pci_enable_device(dev) < 0)
return -ENODEV;
if (!dev->irq) {
err("found OHCI device with no IRQ assigned. check BIOS settings!");
pci_disable_device (dev);
return -ENODEV;
}
/* we read its hardware registers as memory */
mem_resource = pci_resource_start(dev, 0);
mem_len = pci_resource_len(dev, 0);
if (!request_mem_region (mem_resource, mem_len, ohci_pci_driver.name)) {
dbg ("controller already in use");
pci_disable_device (dev);
return -EBUSY;
}
mem_base = ioremap_nocache (mem_resource, mem_len);
if (!mem_base) {
err("Error mapping OHCI memory");
release_mem_region (mem_resource, mem_len);
pci_disable_device (dev);
return -EFAULT;
}
/* controller writes into our memory */
pci_set_master (dev);
status = hc_found_ohci (dev, dev->irq, mem_base, id);
if (status < 0) {
iounmap (mem_base);
release_mem_region (mem_resource, mem_len);
pci_disable_device (dev);
}
return status;
}
/*-------------------------------------------------------------------------*/
/* may be called from interrupt context [interface spec] */
/* may be called without controller present */
/* may be called with controller, bus, and devices active */
static void __devexit
ohci_pci_remove (struct pci_dev *dev)
/*
* Host bus independent remove one OHCI host controller.
*/
void hc_remove_ohci(ohci_t *ohci)
{
ohci_t *ohci = pci_get_drvdata(dev);
dbg ("remove %s controller usb-%s%s%s",
hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS),
dev->slot_name,
ohci->disabled ? " (disabled)" : "",
in_interrupt () ? " in interrupt" : ""
);
#ifdef DEBUG
ohci_dump (ohci, 1);
#endif
......@@ -2652,270 +2516,16 @@ ohci_pci_remove (struct pci_dev *dev)
&ohci->regs->control);
hc_release_ohci (ohci);
release_mem_region (pci_resource_start (dev, 0), pci_resource_len (dev, 0));
pci_disable_device (dev);
}
#ifdef CONFIG_PM
/*-------------------------------------------------------------------------*/
static int
ohci_pci_suspend (struct pci_dev *dev, u32 state)
{
ohci_t *ohci = pci_get_drvdata(dev);
unsigned long flags;
u16 cmd;
if ((ohci->hc_control & OHCI_CTRL_HCFS) != OHCI_USB_OPER) {
dbg ("can't suspend usb-%s (state is %s)", dev->slot_name,
hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS));
return -EIO;
}
/* act as if usb suspend can always be used */
info ("USB suspend: usb-%s", dev->slot_name);
ohci->sleeping = 1;
/* First stop processing */
spin_lock_irqsave (&usb_ed_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 (&usb_ed_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 (ohci->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("Bus in reset phase ???");
break;
case OHCI_USB_RESUME:
dbg("Bus in resume phase ???");
break;
case OHCI_USB_OPER:
dbg("Bus in operational phase ???");
break;
case OHCI_USB_SUSPEND:
dbg("Bus suspended");
break;
}
/* In some rare situations, Apple's OHCI have happily trashed
* memory during sleep. We disable it's bus master bit during
* suspend
*/
pci_read_config_word (dev, PCI_COMMAND, &cmd);
cmd &= ~PCI_COMMAND_MASTER;
pci_write_config_word (dev, PCI_COMMAND, cmd);
#ifdef CONFIG_PMAC_PBOOK
{
struct device_node *of_node;
/* Disable USB PAD & cell clock */
of_node = pci_device_to_OF_node (ohci->ohci_dev);
if (of_node)
pmac_call_feature(PMAC_FTR_USB_ENABLE, of_node, 0, 0);
}
#endif
return 0;
}
/*-------------------------------------------------------------------------*/
static int
ohci_pci_resume (struct pci_dev *dev)
{
ohci_t *ohci = pci_get_drvdata(dev);
int temp;
unsigned long flags;
/* guard against multiple resumes */
atomic_inc (&ohci->resume_count);
if (atomic_read (&ohci->resume_count) != 1) {
err ("concurrent PCI resumes for usb-%s", dev->slot_name);
atomic_dec (&ohci->resume_count);
return 0;
}
#ifdef CONFIG_PMAC_PBOOK
{
struct device_node *of_node;
/* Re-enable USB PAD & cell clock */
of_node = pci_device_to_OF_node (ohci->ohci_dev);
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->ohci_dev);
switch (temp) {
case OHCI_USB_RESET: // lost power
info ("USB restart: usb-%s", dev->slot_name);
hc_restart (ohci);
break;
case OHCI_USB_SUSPEND: // host wakeup
case OHCI_USB_RESUME: // remote wakeup
info ("USB continue: usb-%s from %s wakeup", dev->slot_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 usb-%s won't resume", dev->slot_name);
ohci->disabled = 1;
return -EIO;
}
/* 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 (&usb_ed_lock, flags);
ohci->disabled = 0;
ohci->sleeping = 0;
ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER;
if (!ohci->ed_rm_list[0] && !ohci->ed_rm_list[1]) {
if (ohci->ed_controltail)
ohci->hc_control |= OHCI_CTRL_CLE;
if (ohci->ed_bulktail)
ohci->hc_control |= OHCI_CTRL_BLE;
}
writel (ohci->hc_control, &ohci->regs->control);
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 (&usb_ed_lock, flags);
#ifdef CONFIG_PMAC_PBOOK
if (_machine == _MACH_Pmac)
enable_irq (ohci->irq);
#endif
if (ohci->hcca->done_head)
dl_done_list (ohci, dl_reverse_done_list (ohci));
writel (OHCI_INTR_WDH, &ohci->regs->intrenable);
writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */
writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */
break;
default:
warn ("odd PCI resume for usb-%s", dev->slot_name);
}
/* controller is operational, extra resumes are harmless */
atomic_dec (&ohci->resume_count);
return 0;
}
#endif /* CONFIG_PM */
/*-------------------------------------------------------------------------*/
static const struct pci_device_id __devinitdata ohci_pci_ids [] = { {
/*
* AMD-756 [Viper] USB has a serious erratum when used with
* lowspeed devices like mice.
*/
vendor: 0x1022,
device: 0x740c,
subvendor: PCI_ANY_ID,
subdevice: PCI_ANY_ID,
driver_data: OHCI_QUIRK_AMD756,
} , {
/* handle any USB OHCI controller */
class: ((PCI_CLASS_SERIAL_USB << 8) | 0x10),
class_mask: ~0,
/* 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, ohci_pci_ids);
static struct pci_driver ohci_pci_driver = {
name: "usb-ohci",
id_table: &ohci_pci_ids [0],
probe: ohci_pci_probe,
remove: __devexit_p(ohci_pci_remove),
#ifdef CONFIG_PM
suspend: ohci_pci_suspend,
resume: ohci_pci_resume,
#endif /* PM */
};
/*-------------------------------------------------------------------------*/
static int __init ohci_hcd_init (void)
{
return pci_module_init (&ohci_pci_driver);
}
/*-------------------------------------------------------------------------*/
static void __exit ohci_hcd_cleanup (void)
{
pci_unregister_driver (&ohci_pci_driver);
}
module_init (ohci_hcd_init);
module_exit (ohci_hcd_cleanup);
MODULE_AUTHOR( DRIVER_AUTHOR );
MODULE_DESCRIPTION( DRIVER_DESC );
MODULE_LICENSE("GPL");
EXPORT_SYMBOL(hc_add_ohci);
EXPORT_SYMBOL(hc_remove_ohci);
EXPORT_SYMBOL(hc_start);
EXPORT_SYMBOL(hc_reset);
EXPORT_SYMBOL(dl_done_list);
EXPORT_SYMBOL(dl_reverse_done_list);
EXPORT_SYMBOL(usb_ed_lock);
......@@ -403,6 +403,7 @@ typedef struct ohci {
/* PCI device handle, settings, ... */
struct pci_dev *ohci_dev;
const char *slot_name;
u8 pci_latency;
struct pci_pool *td_cache;
struct pci_pool *dev_cache;
......@@ -423,6 +424,10 @@ struct ohci_device {
// #define ohci_to_usb(ohci) ((ohci)->usb)
#define usb_to_ohci(usb) ((struct ohci_device *)(usb)->hcpriv)
/* For initializing controller (mask in an HCFS mode too) */
#define OHCI_CONTROL_INIT \
(OHCI_CTRL_CBSR & 0x3) | OHCI_CTRL_IE | OHCI_CTRL_PLE
/* hcd */
/* endpoint */
static int ep_link(ohci_t * ohci, ed_t * ed);
......@@ -447,11 +452,6 @@ static int rh_init_int_timer(struct urb * urb);
# define OHCI_MEM_FLAGS 0
#endif
#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 void *
......@@ -641,3 +641,8 @@ dev_free (struct ohci *hc, struct ohci_device *dev)
pci_pool_free (hc->dev_cache, dev, dev->dma);
}
extern spinlock_t usb_ed_lock;
extern void dl_done_list (ohci_t * ohci, td_t * td_list);
extern td_t * dl_reverse_done_list (ohci_t * 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