diff options
Diffstat (limited to 'drivers/usb/host')
-rw-r--r-- | drivers/usb/host/Kconfig | 22 | ||||
-rw-r--r-- | drivers/usb/host/ehci-au1xxx.c | 1 | ||||
-rw-r--r-- | drivers/usb/host/ehci-dbg.c | 231 | ||||
-rw-r--r-- | drivers/usb/host/ehci-fsl.c | 3 | ||||
-rw-r--r-- | drivers/usb/host/ehci-hcd.c | 159 | ||||
-rw-r--r-- | drivers/usb/host/ehci-hub.c | 98 | ||||
-rw-r--r-- | drivers/usb/host/ehci-ixp4xx.c | 152 | ||||
-rw-r--r-- | drivers/usb/host/ehci-orion.c | 272 | ||||
-rw-r--r-- | drivers/usb/host/ehci-pci.c | 3 | ||||
-rw-r--r-- | drivers/usb/host/ehci-ppc-of.c | 238 | ||||
-rw-r--r-- | drivers/usb/host/ehci-ppc-soc.c | 1 | ||||
-rw-r--r-- | drivers/usb/host/ehci-ps3.c | 1 | ||||
-rw-r--r-- | drivers/usb/host/ehci-q.c | 25 | ||||
-rw-r--r-- | drivers/usb/host/ehci-sched.c | 152 | ||||
-rw-r--r-- | drivers/usb/host/ehci.h | 40 | ||||
-rw-r--r-- | drivers/usb/host/isp116x-hcd.c | 1 | ||||
-rw-r--r-- | drivers/usb/host/ohci-at91.c | 31 | ||||
-rw-r--r-- | drivers/usb/host/ohci-dbg.c | 213 | ||||
-rw-r--r-- | drivers/usb/host/ohci-hcd.c | 34 | ||||
-rw-r--r-- | drivers/usb/host/ohci-sh.c | 143 | ||||
-rw-r--r-- | drivers/usb/host/ohci-sm501.c | 264 | ||||
-rw-r--r-- | drivers/usb/host/ohci.h | 7 | ||||
-rw-r--r-- | drivers/usb/host/r8a66597.h | 2 |
23 files changed, 1834 insertions, 259 deletions
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 49a91c5ee51b..d97b16b52efa 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -29,15 +29,6 @@ config USB_EHCI_HCD To compile this driver as a module, choose M here: the module will be called ehci-hcd. -config USB_EHCI_SPLIT_ISO - bool "Full speed ISO transactions (EXPERIMENTAL)" - depends on USB_EHCI_HCD && EXPERIMENTAL - default n - ---help--- - This code is new and hasn't been used with many different - EHCI or USB 2.0 transaction translator implementations. - It should work for ISO-OUT transfers, like audio. - config USB_EHCI_ROOT_HUB_TT bool "Root Hub Transaction Translators (EXPERIMENTAL)" depends on USB_EHCI_HCD && EXPERIMENTAL @@ -69,21 +60,30 @@ config USB_EHCI_TT_NEWSCHED config USB_EHCI_BIG_ENDIAN_MMIO bool - depends on USB_EHCI_HCD && (PPC_CELLEB || PPC_PS3 || 440EPX) + depends on USB_EHCI_HCD && (PPC_CELLEB || PPC_PS3 || 440EPX || ARCH_IXP4XX) default y config USB_EHCI_BIG_ENDIAN_DESC bool - depends on USB_EHCI_HCD && 440EPX + depends on USB_EHCI_HCD && (440EPX || ARCH_IXP4XX) default y config USB_EHCI_FSL bool + depends on USB_EHCI_HCD select USB_EHCI_ROOT_HUB_TT default y if MPC834x || PPC_MPC831x ---help--- Variation of ARC USB block used in some Freescale chips. +config USB_EHCI_HCD_PPC_OF + bool "EHCI support for PPC USB controller on OF platform bus" + depends on USB_EHCI_HCD && PPC_OF + default y + ---help--- + Enables support for the USB controller present on the PowerPC + OpenFirmware platform bus. + config USB_ISP116X_HCD tristate "ISP116X HCD support" depends on USB diff --git a/drivers/usb/host/ehci-au1xxx.c b/drivers/usb/host/ehci-au1xxx.c index 766ef68a0b43..da7532d38bf1 100644 --- a/drivers/usb/host/ehci-au1xxx.c +++ b/drivers/usb/host/ehci-au1xxx.c @@ -222,6 +222,7 @@ static const struct hc_driver ehci_au1xxx_hc_driver = { .hub_control = ehci_hub_control, .bus_suspend = ehci_bus_suspend, .bus_resume = ehci_bus_resume, + .relinquish_port = ehci_relinquish_port, }; /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c index c9cc4413198e..64ebfc5548a3 100644 --- a/drivers/usb/host/ehci-dbg.c +++ b/drivers/usb/host/ehci-dbg.c @@ -323,7 +323,43 @@ static inline void remove_debug_files (struct ehci_hcd *bus) { } #else -/* troubleshooting help: expose state in sysfs */ +/* troubleshooting help: expose state in debugfs */ + +static int debug_async_open(struct inode *, struct file *); +static int debug_periodic_open(struct inode *, struct file *); +static int debug_registers_open(struct inode *, struct file *); +static int debug_async_open(struct inode *, struct file *); +static ssize_t debug_output(struct file*, char __user*, size_t, loff_t*); +static int debug_close(struct inode *, struct file *); + +static const struct file_operations debug_async_fops = { + .owner = THIS_MODULE, + .open = debug_async_open, + .read = debug_output, + .release = debug_close, +}; +static const struct file_operations debug_periodic_fops = { + .owner = THIS_MODULE, + .open = debug_periodic_open, + .read = debug_output, + .release = debug_close, +}; +static const struct file_operations debug_registers_fops = { + .owner = THIS_MODULE, + .open = debug_registers_open, + .read = debug_output, + .release = debug_close, +}; + +static struct dentry *ehci_debug_root; + +struct debug_buffer { + ssize_t (*fill_func)(struct debug_buffer *); /* fill method */ + struct usb_bus *bus; + struct mutex mutex; /* protect filling of buffer */ + size_t count; /* number of characters filled into buffer */ + char *page; +}; #define speed_char(info1) ({ char tmp; \ switch (info1 & (3 << 12)) { \ @@ -441,10 +477,8 @@ done: *nextp = next; } -static ssize_t -show_async (struct class_device *class_dev, char *buf) +static ssize_t fill_async_buffer(struct debug_buffer *buf) { - struct usb_bus *bus; struct usb_hcd *hcd; struct ehci_hcd *ehci; unsigned long flags; @@ -452,14 +486,13 @@ show_async (struct class_device *class_dev, char *buf) char *next; struct ehci_qh *qh; - *buf = 0; - - bus = class_get_devdata(class_dev); - hcd = bus_to_hcd(bus); + hcd = bus_to_hcd(buf->bus); ehci = hcd_to_ehci (hcd); - next = buf; + next = buf->page; size = PAGE_SIZE; + *next = 0; + /* dumps a snapshot of the async schedule. * usually empty except for long-term bulk reads, or head. * one QH per line, and TDs we know about @@ -477,16 +510,12 @@ show_async (struct class_device *class_dev, char *buf) } spin_unlock_irqrestore (&ehci->lock, flags); - return strlen (buf); + return strlen(buf->page); } -static CLASS_DEVICE_ATTR (async, S_IRUGO, show_async, NULL); #define DBG_SCHED_LIMIT 64 - -static ssize_t -show_periodic (struct class_device *class_dev, char *buf) +static ssize_t fill_periodic_buffer(struct debug_buffer *buf) { - struct usb_bus *bus; struct usb_hcd *hcd; struct ehci_hcd *ehci; unsigned long flags; @@ -500,10 +529,9 @@ show_periodic (struct class_device *class_dev, char *buf) return 0; seen_count = 0; - bus = class_get_devdata(class_dev); - hcd = bus_to_hcd(bus); + hcd = bus_to_hcd(buf->bus); ehci = hcd_to_ehci (hcd); - next = buf; + next = buf->page; size = PAGE_SIZE; temp = scnprintf (next, size, "size = %d\n", ehci->periodic_size); @@ -623,14 +651,10 @@ show_periodic (struct class_device *class_dev, char *buf) return PAGE_SIZE - size; } -static CLASS_DEVICE_ATTR (periodic, S_IRUGO, show_periodic, NULL); - #undef DBG_SCHED_LIMIT -static ssize_t -show_registers (struct class_device *class_dev, char *buf) +static ssize_t fill_registers_buffer(struct debug_buffer *buf) { - struct usb_bus *bus; struct usb_hcd *hcd; struct ehci_hcd *ehci; unsigned long flags; @@ -639,15 +663,14 @@ show_registers (struct class_device *class_dev, char *buf) static char fmt [] = "%*s\n"; static char label [] = ""; - bus = class_get_devdata(class_dev); - hcd = bus_to_hcd(bus); + hcd = bus_to_hcd(buf->bus); ehci = hcd_to_ehci (hcd); - next = buf; + next = buf->page; size = PAGE_SIZE; spin_lock_irqsave (&ehci->lock, flags); - if (bus->controller->power.power_state.event) { + if (buf->bus->controller->power.power_state.event) { size = scnprintf (next, size, "bus %s, device %s (driver " DRIVER_VERSION ")\n" "%s\n" @@ -763,9 +786,7 @@ show_registers (struct class_device *class_dev, char *buf) } if (ehci->reclaim) { - temp = scnprintf (next, size, "reclaim qh %p%s\n", - ehci->reclaim, - ehci->reclaim_ready ? " ready" : ""); + temp = scnprintf(next, size, "reclaim qh %p\n", ehci->reclaim); size -= temp; next += temp; } @@ -789,26 +810,150 @@ done: return PAGE_SIZE - size; } -static CLASS_DEVICE_ATTR (registers, S_IRUGO, show_registers, NULL); -static inline void create_debug_files (struct ehci_hcd *ehci) +static struct debug_buffer *alloc_buffer(struct usb_bus *bus, + ssize_t (*fill_func)(struct debug_buffer *)) { - struct class_device *cldev = ehci_to_hcd(ehci)->self.class_dev; - int retval; + struct debug_buffer *buf; + + buf = kzalloc(sizeof(struct debug_buffer), GFP_KERNEL); - retval = class_device_create_file(cldev, &class_device_attr_async); - retval = class_device_create_file(cldev, &class_device_attr_periodic); - retval = class_device_create_file(cldev, &class_device_attr_registers); + if (buf) { + buf->bus = bus; + buf->fill_func = fill_func; + mutex_init(&buf->mutex); + } + + return buf; } -static inline void remove_debug_files (struct ehci_hcd *ehci) +static int fill_buffer(struct debug_buffer *buf) { - struct class_device *cldev = ehci_to_hcd(ehci)->self.class_dev; + int ret = 0; + + if (!buf->page) + buf->page = (char *)get_zeroed_page(GFP_KERNEL); + + if (!buf->page) { + ret = -ENOMEM; + goto out; + } + + ret = buf->fill_func(buf); - class_device_remove_file(cldev, &class_device_attr_async); - class_device_remove_file(cldev, &class_device_attr_periodic); - class_device_remove_file(cldev, &class_device_attr_registers); + if (ret >= 0) { + buf->count = ret; + ret = 0; + } + +out: + return ret; } -#endif /* STUB_DEBUG_FILES */ +static ssize_t debug_output(struct file *file, char __user *user_buf, + size_t len, loff_t *offset) +{ + struct debug_buffer *buf = file->private_data; + int ret = 0; + + mutex_lock(&buf->mutex); + if (buf->count == 0) { + ret = fill_buffer(buf); + if (ret != 0) { + mutex_unlock(&buf->mutex); + goto out; + } + } + mutex_unlock(&buf->mutex); + + ret = simple_read_from_buffer(user_buf, len, offset, + buf->page, buf->count); + +out: + return ret; + +} + +static int debug_close(struct inode *inode, struct file *file) +{ + struct debug_buffer *buf = file->private_data; + if (buf) { + if (buf->page) + free_page((unsigned long)buf->page); + kfree(buf); + } + + return 0; +} +static int debug_async_open(struct inode *inode, struct file *file) +{ + file->private_data = alloc_buffer(inode->i_private, fill_async_buffer); + + return file->private_data ? 0 : -ENOMEM; +} + +static int debug_periodic_open(struct inode *inode, struct file *file) +{ + file->private_data = alloc_buffer(inode->i_private, + fill_periodic_buffer); + + return file->private_data ? 0 : -ENOMEM; +} + +static int debug_registers_open(struct inode *inode, struct file *file) +{ + file->private_data = alloc_buffer(inode->i_private, + fill_registers_buffer); + + return file->private_data ? 0 : -ENOMEM; +} + +static inline void create_debug_files (struct ehci_hcd *ehci) +{ + struct usb_bus *bus = &ehci_to_hcd(ehci)->self; + + ehci->debug_dir = debugfs_create_dir(bus->bus_name, ehci_debug_root); + if (!ehci->debug_dir) + goto dir_error; + + ehci->debug_async = debugfs_create_file("async", S_IRUGO, + ehci->debug_dir, bus, + &debug_async_fops); + if (!ehci->debug_async) + goto async_error; + + ehci->debug_periodic = debugfs_create_file("periodic", S_IRUGO, + ehci->debug_dir, bus, + &debug_periodic_fops); + if (!ehci->debug_periodic) + goto periodic_error; + + ehci->debug_registers = debugfs_create_file("registers", S_IRUGO, + ehci->debug_dir, bus, + &debug_registers_fops); + if (!ehci->debug_registers) + goto registers_error; + return; + +registers_error: + debugfs_remove(ehci->debug_periodic); +periodic_error: + debugfs_remove(ehci->debug_async); +async_error: + debugfs_remove(ehci->debug_dir); +dir_error: + ehci->debug_periodic = NULL; + ehci->debug_async = NULL; + ehci->debug_dir = NULL; +} + +static inline void remove_debug_files (struct ehci_hcd *ehci) +{ + debugfs_remove(ehci->debug_registers); + debugfs_remove(ehci->debug_periodic); + debugfs_remove(ehci->debug_async); + debugfs_remove(ehci->debug_dir); +} + +#endif /* STUB_DEBUG_FILES */ diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index 430821cb95c8..adb0defa1631 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -25,7 +25,7 @@ #include "ehci-fsl.h" -/* FIXME: Power Managment is un-ported so temporarily disable it */ +/* FIXME: Power Management is un-ported so temporarily disable it */ #undef CONFIG_PM /* PCI-based HCs are common, but plenty of non-PCI HCs are used too */ @@ -323,6 +323,7 @@ static const struct hc_driver ehci_fsl_hc_driver = { .hub_control = ehci_hub_control, .bus_suspend = ehci_bus_suspend, .bus_resume = ehci_bus_resume, + .relinquish_port = ehci_relinquish_port, }; static int ehci_fsl_drv_probe(struct platform_device *pdev) diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 5f2d74ed5ad7..4caa6a8b9a37 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -33,6 +33,7 @@ #include <linux/usb.h> #include <linux/moduleparam.h> #include <linux/dma-mapping.h> +#include <linux/debugfs.h> #include "../core/hcd.h" @@ -109,7 +110,7 @@ static const char hcd_name [] = "ehci_hcd"; #define EHCI_TUNE_MULT_TT 1 #define EHCI_TUNE_FLS 2 /* (small) 256 frame schedule */ -#define EHCI_IAA_JIFFIES (HZ/100) /* arbitrary; ~10 msec */ +#define EHCI_IAA_MSECS 10 /* arbitrary */ #define EHCI_IO_JIFFIES (HZ/10) /* io watchdog > irq_thresh */ #define EHCI_ASYNC_JIFFIES (HZ/20) /* async idle timeout */ #define EHCI_SHRINK_JIFFIES (HZ/200) /* async qh unlink delay */ @@ -266,6 +267,7 @@ static void ehci_quiesce (struct ehci_hcd *ehci) /*-------------------------------------------------------------------------*/ +static void end_unlink_async(struct ehci_hcd *ehci); static void ehci_work(struct ehci_hcd *ehci); #include "ehci-hub.c" @@ -275,25 +277,41 @@ static void ehci_work(struct ehci_hcd *ehci); /*-------------------------------------------------------------------------*/ -static void ehci_watchdog (unsigned long param) +static void ehci_iaa_watchdog(unsigned long param) { struct ehci_hcd *ehci = (struct ehci_hcd *) param; unsigned long flags; + u32 status, cmd; spin_lock_irqsave (&ehci->lock, flags); + WARN_ON(!ehci->reclaim); - /* lost IAA irqs wedge things badly; seen with a vt8235 */ + status = ehci_readl(ehci, &ehci->regs->status); + cmd = ehci_readl(ehci, &ehci->regs->command); + ehci_dbg(ehci, "IAA watchdog: status %x cmd %x\n", status, cmd); + + /* lost IAA irqs wedge things badly; seen first with a vt8235 */ if (ehci->reclaim) { - u32 status = ehci_readl(ehci, &ehci->regs->status); if (status & STS_IAA) { ehci_vdbg (ehci, "lost IAA\n"); COUNT (ehci->stats.lost_iaa); ehci_writel(ehci, STS_IAA, &ehci->regs->status); - ehci->reclaim_ready = 1; } + ehci_writel(ehci, cmd & ~CMD_IAAD, &ehci->regs->command); + end_unlink_async(ehci); } - /* stop async processing after it's idled a bit */ + spin_unlock_irqrestore(&ehci->lock, flags); +} + +static void ehci_watchdog(unsigned long param) +{ + struct ehci_hcd *ehci = (struct ehci_hcd *) param; + unsigned long flags; + + spin_lock_irqsave(&ehci->lock, flags); + + /* stop async processing after it's idled a bit */ if (test_bit (TIMER_ASYNC_OFF, &ehci->actions)) start_unlink_async (ehci, ehci->async); @@ -363,8 +381,6 @@ static void ehci_port_power (struct ehci_hcd *ehci, int is_on) static void ehci_work (struct ehci_hcd *ehci) { timer_action_done (ehci, TIMER_IO_WATCHDOG); - if (ehci->reclaim_ready) - end_unlink_async (ehci); /* another CPU may drop ehci->lock during a schedule scan while * it reports urb completions. this flag guards against bogus @@ -399,6 +415,7 @@ static void ehci_stop (struct usb_hcd *hcd) /* no more interrupts ... */ del_timer_sync (&ehci->watchdog); + del_timer_sync(&ehci->iaa_watchdog); spin_lock_irq(&ehci->lock); if (HC_IS_RUNNING (hcd->state)) @@ -447,6 +464,10 @@ static int ehci_init(struct usb_hcd *hcd) ehci->watchdog.function = ehci_watchdog; ehci->watchdog.data = (unsigned long) ehci; + init_timer(&ehci->iaa_watchdog); + ehci->iaa_watchdog.function = ehci_iaa_watchdog; + ehci->iaa_watchdog.data = (unsigned long) ehci; + /* * hw default: 1K periodic list heads, one per frame. * periodic_size can shrink by USBCMD update if hcc_params allows. @@ -463,7 +484,6 @@ static int ehci_init(struct usb_hcd *hcd) ehci->i_thresh = 2 + HCC_ISOC_THRES(hcc_params); ehci->reclaim = NULL; - ehci->reclaim_ready = 0; ehci->next_uframe = -1; /* @@ -654,8 +674,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) /* complete the unlinking of some qh [4.15.2.3] */ if (status & STS_IAA) { COUNT (ehci->stats.reclaim); - ehci->reclaim_ready = 1; - bh = 1; + end_unlink_async(ehci); } /* remote wakeup [4.3.1] */ @@ -761,10 +780,16 @@ static int ehci_urb_enqueue ( static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) { - /* if we need to use IAA and it's busy, defer */ - if (qh->qh_state == QH_STATE_LINKED - && ehci->reclaim - && HC_IS_RUNNING (ehci_to_hcd(ehci)->state)) { + /* failfast */ + if (!HC_IS_RUNNING(ehci_to_hcd(ehci)->state)) + end_unlink_async(ehci); + + /* if it's not linked then there's nothing to do */ + if (qh->qh_state != QH_STATE_LINKED) + ; + + /* defer till later if busy */ + else if (ehci->reclaim) { struct ehci_qh *last; for (last = ehci->reclaim; @@ -774,12 +799,8 @@ static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) qh->qh_state = QH_STATE_UNLINK_WAIT; last->reclaim = qh; - /* bypass IAA if the hc can't care */ - } else if (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state) && ehci->reclaim) - end_unlink_async (ehci); - - /* something else might have unlinked the qh by now */ - if (qh->qh_state == QH_STATE_LINKED) + /* start IAA cycle */ + } else start_unlink_async (ehci, qh); } @@ -806,7 +827,19 @@ static int ehci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) qh = (struct ehci_qh *) urb->hcpriv; if (!qh) break; - unlink_async (ehci, qh); + switch (qh->qh_state) { + case QH_STATE_LINKED: + case QH_STATE_COMPLETING: + unlink_async(ehci, qh); + break; + case QH_STATE_UNLINK: + case QH_STATE_UNLINK_WAIT: + /* already started */ + break; + case QH_STATE_IDLE: + WARN_ON(1); + break; + } break; case PIPE_INTERRUPT: @@ -829,16 +862,16 @@ static int ehci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) /* reschedule QH iff another request is queued */ if (!list_empty (&qh->qtd_list) && HC_IS_RUNNING (hcd->state)) { - int status; + int schedule_status; - status = qh_schedule (ehci, qh); + schedule_status = qh_schedule (ehci, qh); spin_unlock_irqrestore (&ehci->lock, flags); - if (status != 0) { + if (schedule_status != 0) { // shouldn't happen often, but ... // FIXME kill those tds' urbs err ("can't reschedule qh %p, err %d", - qh, status); + qh, schedule_status); } return status; } @@ -898,6 +931,7 @@ rescan: unlink_async (ehci, qh); /* FALL THROUGH */ case QH_STATE_UNLINK: /* wait for hw to finish? */ + case QH_STATE_UNLINK_WAIT: idle_timeout: spin_unlock_irqrestore (&ehci->lock, flags); schedule_timeout_uninterruptible(1); @@ -959,11 +993,26 @@ MODULE_LICENSE ("GPL"); #define PS3_SYSTEM_BUS_DRIVER ps3_ehci_driver #endif -#ifdef CONFIG_440EPX +#if defined(CONFIG_440EPX) && !defined(CONFIG_PPC_MERGE) #include "ehci-ppc-soc.c" #define PLATFORM_DRIVER ehci_ppc_soc_driver #endif +#ifdef CONFIG_USB_EHCI_HCD_PPC_OF +#include "ehci-ppc-of.c" +#define OF_PLATFORM_DRIVER ehci_hcd_ppc_of_driver +#endif + +#ifdef CONFIG_ARCH_ORION +#include "ehci-orion.c" +#define PLATFORM_DRIVER ehci_orion_driver +#endif + +#ifdef CONFIG_ARCH_IXP4XX +#include "ehci-ixp4xx.c" +#define PLATFORM_DRIVER ixp4xx_ehci_driver +#endif + #if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \ !defined(PS3_SYSTEM_BUS_DRIVER) #error "missing bus glue for ehci-hcd" @@ -978,41 +1027,66 @@ static int __init ehci_hcd_init(void) sizeof(struct ehci_qh), sizeof(struct ehci_qtd), sizeof(struct ehci_itd), sizeof(struct ehci_sitd)); +#ifdef DEBUG + ehci_debug_root = debugfs_create_dir("ehci", NULL); + if (!ehci_debug_root) + return -ENOENT; +#endif + #ifdef PLATFORM_DRIVER retval = platform_driver_register(&PLATFORM_DRIVER); if (retval < 0) - return retval; + goto clean0; #endif #ifdef PCI_DRIVER retval = pci_register_driver(&PCI_DRIVER); - if (retval < 0) { -#ifdef PLATFORM_DRIVER - platform_driver_unregister(&PLATFORM_DRIVER); -#endif - return retval; - } + if (retval < 0) + goto clean1; #endif #ifdef PS3_SYSTEM_BUS_DRIVER retval = ps3_ehci_driver_register(&PS3_SYSTEM_BUS_DRIVER); - if (retval < 0) { -#ifdef PLATFORM_DRIVER - platform_driver_unregister(&PLATFORM_DRIVER); + if (retval < 0) + goto clean2; +#endif + +#ifdef OF_PLATFORM_DRIVER + retval = of_register_platform_driver(&OF_PLATFORM_DRIVER); + if (retval < 0) + goto clean3; +#endif + return retval; + +#ifdef OF_PLATFORM_DRIVER + /* of_unregister_platform_driver(&OF_PLATFORM_DRIVER); */ +clean3: +#endif +#ifdef PS3_SYSTEM_BUS_DRIVER + ps3_ehci_driver_unregister(&PS3_SYSTEM_BUS_DRIVER); +clean2: #endif #ifdef PCI_DRIVER - pci_unregister_driver(&PCI_DRIVER); + pci_unregister_driver(&PCI_DRIVER); +clean1: #endif - return retval; - } +#ifdef PLATFORM_DRIVER + platform_driver_unregister(&PLATFORM_DRIVER); +clean0: +#endif +#ifdef DEBUG + debugfs_remove(ehci_debug_root); + ehci_debug_root = NULL; #endif - return retval; } module_init(ehci_hcd_init); static void __exit ehci_hcd_cleanup(void) { +#ifdef OF_PLATFORM_DRIVER + of_unregister_platform_driver(&OF_PLATFORM_DRIVER); +#endif #ifdef PLATFORM_DRIVER platform_driver_unregister(&PLATFORM_DRIVER); #endif @@ -1022,6 +1096,9 @@ static void __exit ehci_hcd_cleanup(void) #ifdef PS3_SYSTEM_BUS_DRIVER ps3_ehci_driver_unregister(&PS3_SYSTEM_BUS_DRIVER); #endif +#ifdef DEBUG + debugfs_remove(ehci_debug_root); +#endif } module_exit(ehci_hcd_cleanup); diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 735db4aec831..40e8240b7851 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -123,6 +123,8 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) if (time_before (jiffies, ehci->next_statechange)) msleep(5); + del_timer_sync(&ehci->watchdog); + del_timer_sync(&ehci->iaa_watchdog); port = HCS_N_PORTS (ehci->hcs_params); spin_lock_irq (&ehci->lock); @@ -134,7 +136,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) } ehci->command = ehci_readl(ehci, &ehci->regs->command); if (ehci->reclaim) - ehci->reclaim_ready = 1; + end_unlink_async(ehci); ehci_work(ehci); /* Unlike other USB host controller types, EHCI doesn't have @@ -170,8 +172,11 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) } } + /* Apparently some devices need a >= 1-uframe delay here */ + if (ehci->bus_suspended) + udelay(150); + /* turn off now-idle HC */ - del_timer_sync (&ehci->watchdog); ehci_halt (ehci); hcd->state = HC_STATE_SUSPENDED; @@ -291,14 +296,16 @@ static int ehci_bus_resume (struct usb_hcd *hcd) /*-------------------------------------------------------------------------*/ /* Display the ports dedicated to the companion controller */ -static ssize_t show_companion(struct class_device *class_dev, char *buf) +static ssize_t show_companion(struct device *dev, + struct device_attribute *attr, + char *buf) { struct ehci_hcd *ehci; int nports, index, n; int count = PAGE_SIZE; char *ptr = buf; - ehci = hcd_to_ehci(bus_to_hcd(class_get_devdata(class_dev))); + ehci = hcd_to_ehci(bus_to_hcd(dev_get_drvdata(dev))); nports = HCS_N_PORTS(ehci->hcs_params); for (index = 0; index < nports; ++index) { @@ -312,40 +319,21 @@ static ssize_t show_companion(struct class_device *class_dev, char *buf) } /* - * Dedicate or undedicate a port to the companion controller. - * Syntax is "[-]portnum", where a leading '-' sign means - * return control of the port to the EHCI controller. + * Sets the owner of a port */ -static ssize_t store_companion(struct class_device *class_dev, - const char *buf, size_t count) +static void set_owner(struct ehci_hcd *ehci, int portnum, int new_owner) { - struct ehci_hcd *ehci; - int portnum, new_owner, try; u32 __iomem *status_reg; u32 port_status; + int try; - ehci = hcd_to_ehci(bus_to_hcd(class_get_devdata(class_dev))); - new_owner = PORT_OWNER; /* Owned by companion */ - if (sscanf(buf, "%d", &portnum) != 1) - return -EINVAL; - if (portnum < 0) { - portnum = - portnum; - new_owner = 0; /* Owned by EHCI */ - } - if (portnum <= 0 || portnum > HCS_N_PORTS(ehci->hcs_params)) - return -ENOENT; - status_reg = &ehci->regs->port_status[--portnum]; - if (new_owner) - set_bit(portnum, &ehci->companion_ports); - else - clear_bit(portnum, &ehci->companion_ports); + status_reg = &ehci->regs->port_status[portnum]; /* * The controller won't set the OWNER bit if the port is * enabled, so this loop will sometimes require at least two * iterations: one to disable the port and one to set OWNER. */ - for (try = 4; try > 0; --try) { spin_lock_irq(&ehci->lock); port_status = ehci_readl(ehci, status_reg); @@ -362,9 +350,39 @@ static ssize_t store_companion(struct class_device *class_dev, if (try > 1) msleep(5); } +} + +/* + * Dedicate or undedicate a port to the companion controller. + * Syntax is "[-]portnum", where a leading '-' sign means + * return control of the port to the EHCI controller. + */ +static ssize_t store_companion(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ehci_hcd *ehci; + int portnum, new_owner; + + ehci = hcd_to_ehci(bus_to_hcd(dev_get_drvdata(dev))); + new_owner = PORT_OWNER; /* Owned by companion */ + if (sscanf(buf, "%d", &portnum) != 1) + return -EINVAL; + if (portnum < 0) { + portnum = - portnum; + new_owner = 0; /* Owned by EHCI */ + } + if (portnum <= 0 || portnum > HCS_N_PORTS(ehci->hcs_params)) + return -ENOENT; + portnum--; + if (new_owner) + set_bit(portnum, &ehci->companion_ports); + else + clear_bit(portnum, &ehci->companion_ports); + set_owner(ehci, portnum, new_owner); return count; } -static CLASS_DEVICE_ATTR(companion, 0644, show_companion, store_companion); +static DEVICE_ATTR(companion, 0644, show_companion, store_companion); static inline void create_companion_file(struct ehci_hcd *ehci) { @@ -372,16 +390,16 @@ static inline void create_companion_file(struct ehci_hcd *ehci) /* with integrated TT there is no companion! */ if (!ehci_is_TDI(ehci)) - i = class_device_create_file(ehci_to_hcd(ehci)->self.class_dev, - &class_device_attr_companion); + i = device_create_file(ehci_to_hcd(ehci)->self.dev, + &dev_attr_companion); } static inline void remove_companion_file(struct ehci_hcd *ehci) { /* with integrated TT there is no companion! */ if (!ehci_is_TDI(ehci)) - class_device_remove_file(ehci_to_hcd(ehci)->self.class_dev, - &class_device_attr_companion); + device_remove_file(ehci_to_hcd(ehci)->self.dev, + &dev_attr_companion); } @@ -393,10 +411,8 @@ static int check_reset_complete ( u32 __iomem *status_reg, int port_status ) { - if (!(port_status & PORT_CONNECT)) { - ehci->reset_done [index] = 0; + if (!(port_status & PORT_CONNECT)) return port_status; - } /* if reset finished and it's still not enabled -- handoff */ if (!(port_status & PORT_PE)) { @@ -475,8 +491,6 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf) * controller by the user. */ - if (!(temp & PORT_CONNECT)) - ehci->reset_done [i] = 0; if ((temp & mask) != 0 || ((temp & PORT_RESUME) != 0 && time_after_eq(jiffies, @@ -864,3 +878,13 @@ error: spin_unlock_irqrestore (&ehci->lock, flags); return retval; } + +static void ehci_relinquish_port(struct usb_hcd *hcd, int portnum) +{ + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + + if (ehci_is_TDI(ehci)) + return; + set_owner(ehci, --portnum, PORT_OWNER); +} + diff --git a/drivers/usb/host/ehci-ixp4xx.c b/drivers/usb/host/ehci-ixp4xx.c new file mode 100644 index 000000000000..3041d8f055f4 --- /dev/null +++ b/drivers/usb/host/ehci-ixp4xx.c @@ -0,0 +1,152 @@ +/* + * IXP4XX EHCI Host Controller Driver + * + * Author: Vladimir Barinov <vbarinov@ru.mvista.com> + * + * Based on "ehci-fsl.c" by Randy Vinson <rvinson@mvista.com> + * + * 2007 (c) MontaVista Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include <linux/platform_device.h> + +static int ixp4xx_ehci_init(struct usb_hcd *hcd) +{ + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + int retval = 0; + + ehci->big_endian_desc = 1; + ehci->big_endian_mmio = 1; + + ehci->caps = hcd->regs + 0x100; + ehci->regs = hcd->regs + 0x100 + + HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); + ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); + + ehci->is_tdi_rh_tt = 1; + ehci_reset(ehci); + + retval = ehci_init(hcd); + if (retval) + return retval; + + ehci_port_power(ehci, 0); + + return retval; +} + +static const struct hc_driver ixp4xx_ehci_hc_driver = { + .description = hcd_name, + .product_desc = "IXP4XX EHCI Host Controller", + .hcd_priv_size = sizeof(struct ehci_hcd), + .irq = ehci_irq, + .flags = HCD_MEMORY | HCD_USB2, + .reset = ixp4xx_ehci_init, + .start = ehci_run, + .stop = ehci_stop, + .shutdown = ehci_shutdown, + .urb_enqueue = ehci_urb_enqueue, + .urb_dequeue = ehci_urb_dequeue, + .endpoint_disable = ehci_endpoint_disable, + .get_frame_number = ehci_get_frame, + .hub_status_data = ehci_hub_status_data, + .hub_control = ehci_hub_control, +#if defined(CONFIG_PM) + .bus_suspend = ehci_bus_suspend, + .bus_resume = ehci_bus_resume, +#endif +}; + +static int ixp4xx_ehci_probe(struct platform_device *pdev) +{ + struct usb_hcd *hcd; + const struct hc_driver *driver = &ixp4xx_ehci_hc_driver; + struct resource *res; + int irq; + int retval; + + if (usb_disabled()) + return -ENODEV; + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) { + dev_err(&pdev->dev, + "Found HC with no IRQ. Check %s setup!\n", + pdev->dev.bus_id); + return -ENODEV; + } + irq = res->start; + + hcd = usb_create_hcd(driver, &pdev->dev, pdev->dev.bus_id); + if (!hcd) { + retval = -ENOMEM; + goto fail_create_hcd; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, + "Found HC with no register addr. Check %s setup!\n", + pdev->dev.bus_id); + retval = -ENODEV; + goto fail_request_resource; + } + hcd->rsrc_start = res->start; + hcd->rsrc_len = res->end - res->start + 1; + + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, + driver->description)) { + dev_dbg(&pdev->dev, "controller already in use\n"); + retval = -EBUSY; + goto fail_request_resource; + } + + hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len); + if (hcd->regs == NULL) { + dev_dbg(&pdev->dev, "error mapping memory\n"); + retval = -EFAULT; + goto fail_ioremap; + } + + retval = usb_add_hcd(hcd, irq, IRQF_SHARED); + if (retval) + goto fail_add_hcd; + + return retval; + +fail_add_hcd: + iounmap(hcd->regs); +fail_ioremap: + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); +fail_request_resource: + usb_put_hcd(hcd); +fail_create_hcd: + dev_err(&pdev->dev, "init %s fail, %d\n", pdev->dev.bus_id, retval); + return retval; +} + +static int ixp4xx_ehci_remove(struct platform_device *pdev) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + + usb_remove_hcd(hcd); + iounmap(hcd->regs); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + usb_put_hcd(hcd); + + return 0; +} + +MODULE_ALIAS("ixp4xx-ehci"); + +static struct platform_driver ixp4xx_ehci_driver = { + .probe = ixp4xx_ehci_probe, + .remove = ixp4xx_ehci_remove, + .driver = { + .name = "ixp4xx-ehci", + .bus = &platform_bus_type + }, +}; diff --git a/drivers/usb/host/ehci-orion.c b/drivers/usb/host/ehci-orion.c new file mode 100644 index 000000000000..e129981f139f --- /dev/null +++ b/drivers/usb/host/ehci-orion.c @@ -0,0 +1,272 @@ +/* + * drivers/usb/host/ehci-orion.c + * + * Tzachi Perelstein <tzachi@marvell.com> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <asm/arch/orion.h> + +#define rdl(off) __raw_readl(hcd->regs + (off)) +#define wrl(off, val) __raw_writel((val), hcd->regs + (off)) + +#define USB_CAUSE 0x310 +#define USB_MASK 0x314 +#define USB_CMD 0x140 +#define USB_MODE 0x1a8 +#define USB_IPG 0x360 +#define USB_PHY_PWR_CTRL 0x400 +#define USB_PHY_TX_CTRL 0x420 +#define USB_PHY_RX_CTRL 0x430 +#define USB_PHY_IVREF_CTRL 0x440 +#define USB_PHY_TST_GRP_CTRL 0x450 + +/* + * Implement Orion USB controller specification guidelines + */ +static void orion_usb_setup(struct usb_hcd *hcd) +{ + /* + * Clear interrupt cause and mask + */ + wrl(USB_CAUSE, 0); + wrl(USB_MASK, 0); + + /* + * Reset controller + */ + wrl(USB_CMD, rdl(USB_CMD) | 0x2); + while (rdl(USB_CMD) & 0x2); + + /* + * GL# USB-10: Set IPG for non start of frame packets + * Bits[14:8]=0xc + */ + wrl(USB_IPG, (rdl(USB_IPG) & ~0x7f00) | 0xc00); + + /* + * GL# USB-9: USB 2.0 Power Control + * BG_VSEL[7:6]=0x1 + */ + wrl(USB_PHY_PWR_CTRL, (rdl(USB_PHY_PWR_CTRL) & ~0xc0)| 0x40); + + /* + * GL# USB-1: USB PHY Tx Control - force calibration to '8' + * TXDATA_BLOCK_EN[21]=0x1, EXT_RCAL_EN[13]=0x1, IMP_CAL[6:3]=0x8 + */ + wrl(USB_PHY_TX_CTRL, (rdl(USB_PHY_TX_CTRL) & ~0x78) | 0x202040); + + /* + * GL# USB-3 GL# USB-9: USB PHY Rx Control + * RXDATA_BLOCK_LENGHT[31:30]=0x3, EDGE_DET_SEL[27:26]=0, + * CDR_FASTLOCK_EN[21]=0, DISCON_THRESHOLD[9:8]=0, SQ_THRESH[7:4]=0x1 + */ + wrl(USB_PHY_RX_CTRL, (rdl(USB_PHY_RX_CTRL) & ~0xc2003f0) | 0xc0000010); + + /* + * GL# USB-3 GL# USB-9: USB PHY IVREF Control + * PLLVDD12[1:0]=0x2, RXVDD[5:4]=0x3, Reserved[19]=0 + */ + wrl(USB_PHY_IVREF_CTRL, (rdl(USB_PHY_IVREF_CTRL) & ~0x80003 ) | 0x32); + + /* + * GL# USB-3 GL# USB-9: USB PHY Test Group Control + * REG_FIFO_SQ_RST[15]=0 + */ + wrl(USB_PHY_TST_GRP_CTRL, rdl(USB_PHY_TST_GRP_CTRL) & ~0x8000); + + /* + * Stop and reset controller + */ + wrl(USB_CMD, rdl(USB_CMD) & ~0x1); + wrl(USB_CMD, rdl(USB_CMD) | 0x2); + while (rdl(USB_CMD) & 0x2); + + /* + * GL# USB-5 Streaming disable REG_USB_MODE[4]=1 + * TBD: This need to be done after each reset! + * GL# USB-4 Setup USB Host mode + */ + wrl(USB_MODE, 0x13); +} + +static int ehci_orion_setup(struct usb_hcd *hcd) +{ + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + int retval; + + retval = ehci_halt(ehci); + if (retval) + return retval; + + /* + * data structure init + */ + retval = ehci_init(hcd); + if (retval) + return retval; + + ehci_reset(ehci); + ehci_port_power(ehci, 0); + + return retval; +} + +static const struct hc_driver ehci_orion_hc_driver = { + .description = hcd_name, + .product_desc = "Marvell Orion EHCI", + .hcd_priv_size = sizeof(struct ehci_hcd), + + /* + * generic hardware linkage + */ + .irq = ehci_irq, + .flags = HCD_MEMORY | HCD_USB2, + + /* + * basic lifecycle operations + */ + .reset = ehci_orion_setup, + .start = ehci_run, +#ifdef CONFIG_PM + .suspend = ehci_bus_suspend, + .resume = ehci_bus_resume, +#endif + .stop = ehci_stop, + .shutdown = ehci_shutdown, + + /* + * managing i/o requests and associated device resources + */ + .urb_enqueue = ehci_urb_enqueue, + .urb_dequeue = ehci_urb_dequeue, + .endpoint_disable = ehci_endpoint_disable, + + /* + * scheduling support + */ + .get_frame_number = ehci_get_frame, + + /* + * root hub support + */ + .hub_status_data = ehci_hub_status_data, + .hub_control = ehci_hub_control, + .bus_suspend = ehci_bus_suspend, + .bus_resume = ehci_bus_resume, +}; + +static int __init ehci_orion_drv_probe(struct platform_device *pdev) +{ + struct resource *res; + struct usb_hcd *hcd; + struct ehci_hcd *ehci; + void __iomem *regs; + int irq, err; + + if (usb_disabled()) + return -ENODEV; + + pr_debug("Initializing Orion-SoC USB Host Controller\n"); + + irq = platform_get_irq(pdev, 0); + if (irq <= 0) { + dev_err(&pdev->dev, + "Found HC with no IRQ. Check %s setup!\n", + pdev->dev.bus_id); + err = -ENODEV; + goto err1; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, + "Found HC with no register addr. Check %s setup!\n", + pdev->dev.bus_id); + err = -ENODEV; + goto err1; + } + + if (!request_mem_region(res->start, res->end - res->start + 1, + ehci_orion_hc_driver.description)) { + dev_dbg(&pdev->dev, "controller already in use\n"); + err = -EBUSY; + goto err1; + } + + regs = ioremap(res->start, res->end - res->start + 1); + if (regs == NULL) { + dev_dbg(&pdev->dev, "error mapping memory\n"); + err = -EFAULT; + goto err2; + } + + hcd = usb_create_hcd(&ehci_orion_hc_driver, + &pdev->dev, pdev->dev.bus_id); + if (!hcd) { + err = -ENOMEM; + goto err3; + } + + hcd->rsrc_start = res->start; + hcd->rsrc_len = res->end - res->start + 1; + hcd->regs = regs; + + ehci = hcd_to_ehci(hcd); + ehci->caps = hcd->regs + 0x100; + ehci->regs = hcd->regs + 0x100 + + HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); + ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); + ehci->is_tdi_rh_tt = 1; + ehci->sbrn = 0x20; + + /* + * setup Orion USB controller + */ + orion_usb_setup(hcd); + + err = usb_add_hcd(hcd, irq, IRQF_SHARED | IRQF_DISABLED); + if (err) + goto err4; + + return 0; + +err4: + usb_put_hcd(hcd); +err3: + iounmap(regs); +err2: + release_mem_region(res->start, res->end - res->start + 1); +err1: + dev_err(&pdev->dev, "init %s fail, %d\n", + pdev->dev.bus_id, err); + + return err; +} + +static int __exit ehci_orion_drv_remove(struct platform_device *pdev) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + + usb_remove_hcd(hcd); + iounmap(hcd->regs); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + usb_put_hcd(hcd); + + return 0; +} + +MODULE_ALIAS("platform:orion-ehci"); + +static struct platform_driver ehci_orion_driver = { + .probe = ehci_orion_drv_probe, + .remove = __exit_p(ehci_orion_drv_remove), + .shutdown = usb_hcd_platform_shutdown, + .driver.name = "orion-ehci", +}; diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index ad0d4965f2fb..3ba01664f821 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -305,7 +305,7 @@ static int ehci_pci_resume(struct usb_hcd *hcd) /* emptying the schedule aborts any urbs */ spin_lock_irq(&ehci->lock); if (ehci->reclaim) - ehci->reclaim_ready = 1; + end_unlink_async(ehci); ehci_work(ehci); spin_unlock_irq(&ehci->lock); @@ -364,6 +364,7 @@ static const struct hc_driver ehci_pci_hc_driver = { .hub_control = ehci_hub_control, .bus_suspend = ehci_bus_suspend, .bus_resume = ehci_bus_resume, + .relinquish_port = ehci_relinquish_port, }; /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/host/ehci-ppc-of.c b/drivers/usb/host/ehci-ppc-of.c new file mode 100644 index 000000000000..ee305b1f99ff --- /dev/null +++ b/drivers/usb/host/ehci-ppc-of.c @@ -0,0 +1,238 @@ +/* + * EHCI HCD (Host Controller Driver) for USB. + * + * Bus Glue for PPC On-Chip EHCI driver on the of_platform bus + * Tested on AMCC PPC 440EPx + * + * Valentine Barshak <vbarshak@ru.mvista.com> + * + * Based on "ehci-ppc-soc.c" by Stefan Roese <sr@denx.de> + * and "ohci-ppc-of.c" by Sylvain Munaut <tnt@246tNt.com> + * + * This file is licenced under the GPL. + */ + +#include <linux/signal.h> + +#include <linux/of.h> +#include <linux/of_platform.h> + +/* called during probe() after chip reset completes */ +static int ehci_ppc_of_setup(struct usb_hcd *hcd) +{ + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + int retval; + + retval = ehci_halt(ehci); + if (retval) + return retval; + + retval = ehci_init(hcd); + if (retval) + return retval; + + ehci->sbrn = 0x20; + return ehci_reset(ehci); +} + + +static const struct hc_driver ehci_ppc_of_hc_driver = { + .description = hcd_name, + .product_desc = "OF EHCI", + .hcd_priv_size = sizeof(struct ehci_hcd), + + /* + * generic hardware linkage + */ + .irq = ehci_irq, + .flags = HCD_MEMORY | HCD_USB2, + + /* + * basic lifecycle operations + */ + .reset = ehci_ppc_of_setup, + .start = ehci_run, + .stop = ehci_stop, + .shutdown = ehci_shutdown, + + /* + * managing i/o requests and associated device resources + */ + .urb_enqueue = ehci_urb_enqueue, + .urb_dequeue = ehci_urb_dequeue, + .endpoint_disable = ehci_endpoint_disable, + + /* + * scheduling support + */ + .get_frame_number = ehci_get_frame, + + /* + * root hub support + */ + .hub_status_data = ehci_hub_status_data, + .hub_control = ehci_hub_control, +#ifdef CONFIG_PM + .bus_suspend = ehci_bus_suspend, + .bus_resume = ehci_bus_resume, +#endif +}; + + +/* + * 440EPx Errata USBH_3 + * Fix: Enable Break Memory Transfer (BMT) in INSNREG3 + */ +#define PPC440EPX_EHCI0_INSREG_BMT (0x1 << 0) +static int __devinit +ppc44x_enable_bmt(struct device_node *dn) +{ + __iomem u32 *insreg_virt; + + insreg_virt = of_iomap(dn, 1); + if (!insreg_virt) + return -EINVAL; + + out_be32(insreg_virt + 3, PPC440EPX_EHCI0_INSREG_BMT); + + iounmap(insreg_virt); + return 0; +} + + +static int __devinit +ehci_hcd_ppc_of_probe(struct of_device *op, const struct of_device_id *match) +{ + struct device_node *dn = op->node; + struct usb_hcd *hcd; + struct ehci_hcd *ehci; + struct resource res; + int irq; + int rv; + + if (usb_disabled()) + return -ENODEV; + + dev_dbg(&op->dev, "initializing PPC-OF USB Controller\n"); + + rv = of_address_to_resource(dn, 0, &res); + if (rv) + return rv; + + hcd = usb_create_hcd(&ehci_ppc_of_hc_driver, &op->dev, "PPC-OF USB"); + if (!hcd) + return -ENOMEM; + + hcd->rsrc_start = res.start; + hcd->rsrc_len = res.end - res.start + 1; + + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { + printk(KERN_ERR __FILE__ ": request_mem_region failed\n"); + rv = -EBUSY; + goto err_rmr; + } + + irq = irq_of_parse_and_map(dn, 0); + if (irq == NO_IRQ) { + printk(KERN_ERR __FILE__ ": irq_of_parse_and_map failed\n"); + rv = -EBUSY; + goto err_irq; + } + + hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); + if (!hcd->regs) { + printk(KERN_ERR __FILE__ ": ioremap failed\n"); + rv = -ENOMEM; + goto err_ioremap; + } + + ehci = hcd_to_ehci(hcd); + + if (of_get_property(dn, "big-endian", NULL)) { + ehci->big_endian_mmio = 1; + ehci->big_endian_desc = 1; + } + if (of_get_property(dn, "big-endian-regs", NULL)) + ehci->big_endian_mmio = 1; + if (of_get_property(dn, "big-endian-desc", NULL)) + ehci->big_endian_desc = 1; + + ehci->caps = hcd->regs; + ehci->regs = hcd->regs + + HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); + + /* cache this readonly data; minimize chip reads */ + ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); + + if (of_device_is_compatible(dn, "ibm,usb-ehci-440epx")) { + rv = ppc44x_enable_bmt(dn); + ehci_dbg(ehci, "Break Memory Transfer (BMT) is %senabled!\n", + rv ? "NOT ": ""); + } + + rv = usb_add_hcd(hcd, irq, 0); + if (rv == 0) + return 0; + + iounmap(hcd->regs); +err_ioremap: + irq_dispose_mapping(irq); +err_irq: + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); +err_rmr: + usb_put_hcd(hcd); + + return rv; +} + + +static int ehci_hcd_ppc_of_remove(struct of_device *op) +{ + struct usb_hcd *hcd = dev_get_drvdata(&op->dev); + dev_set_drvdata(&op->dev, NULL); + + dev_dbg(&op->dev, "stopping PPC-OF USB Controller\n"); + + usb_remove_hcd(hcd); + + iounmap(hcd->regs); + irq_dispose_mapping(hcd->irq); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + + usb_put_hcd(hcd); + + return 0; +} + + +static int ehci_hcd_ppc_of_shutdown(struct of_device *op) +{ + struct usb_hcd *hcd = dev_get_drvdata(&op->dev); + + if (hcd->driver->shutdown) + hcd->driver->shutdown(hcd); + + return 0; +} + + +static struct of_device_id ehci_hcd_ppc_of_match[] = { + { + .compatible = "usb-ehci", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, ehci_hcd_ppc_of_match); + + +static struct of_platform_driver ehci_hcd_ppc_of_driver = { + .name = "ppc-of-ehci", + .match_table = ehci_hcd_ppc_of_match, + .probe = ehci_hcd_ppc_of_probe, + .remove = ehci_hcd_ppc_of_remove, + .shutdown = ehci_hcd_ppc_of_shutdown, + .driver = { + .name = "ppc-of-ehci", + .owner = THIS_MODULE, + }, +}; diff --git a/drivers/usb/host/ehci-ppc-soc.c b/drivers/usb/host/ehci-ppc-soc.c index 452d4b1bc859..a3249078c808 100644 --- a/drivers/usb/host/ehci-ppc-soc.c +++ b/drivers/usb/host/ehci-ppc-soc.c @@ -162,6 +162,7 @@ static const struct hc_driver ehci_ppc_soc_hc_driver = { .hub_control = ehci_hub_control, .bus_suspend = ehci_bus_suspend, .bus_resume = ehci_bus_resume, + .relinquish_port = ehci_relinquish_port, }; static int ehci_hcd_ppc_soc_drv_probe(struct platform_device *pdev) diff --git a/drivers/usb/host/ehci-ps3.c b/drivers/usb/host/ehci-ps3.c index 03a6b2f4e6ed..bbda58eb8813 100644 --- a/drivers/usb/host/ehci-ps3.c +++ b/drivers/usb/host/ehci-ps3.c @@ -72,6 +72,7 @@ static const struct hc_driver ps3_ehci_hc_driver = { .bus_suspend = ehci_bus_suspend, .bus_resume = ehci_bus_resume, #endif + .relinquish_port = ehci_relinquish_port, }; static int ps3_ehci_probe(struct ps3_system_bus_device *dev) diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index b10f39c047e9..776a97f33914 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -198,7 +198,8 @@ static int qtd_copy_status ( /* if async CSPLIT failed, try cleaning out the TT buffer */ if (status != -EPIPE - && urb->dev->tt && !usb_pipeint (urb->pipe) + && urb->dev->tt + && !usb_pipeint(urb->pipe) && ((token & QTD_STS_MMF) != 0 || QTD_CERR(token) == 0) && (!ehci_is_TDI(ehci) @@ -211,6 +212,9 @@ static int qtd_copy_status ( urb->dev->ttport, urb->dev->devnum, usb_pipeendpoint (urb->pipe), token); #endif /* DEBUG */ + /* REVISIT ARC-derived cores don't clear the root + * hub TT buffer in this way... + */ usb_hub_tt_clear_buffer (urb->dev, urb->pipe); } } @@ -638,6 +642,7 @@ qh_make ( u32 info1 = 0, info2 = 0; int is_input, type; int maxp = 0; + struct usb_tt *tt = urb->dev->tt; if (!qh) return qh; @@ -661,8 +666,9 @@ qh_make ( * For control/bulk requests, the HC or TT handles these. */ if (type == PIPE_INTERRUPT) { - qh->usecs = NS_TO_US (usb_calc_bus_time (USB_SPEED_HIGH, is_input, 0, - hb_mult (maxp) * max_packet (maxp))); + qh->usecs = NS_TO_US(usb_calc_bus_time(USB_SPEED_HIGH, + is_input, 0, + hb_mult(maxp) * max_packet(maxp))); qh->start = NO_FRAME; if (urb->dev->speed == USB_SPEED_HIGH) { @@ -680,7 +686,6 @@ qh_make ( goto done; } } else { - struct usb_tt *tt = urb->dev->tt; int think_time; /* gap is f(FS/LS transfer times) */ @@ -736,10 +741,8 @@ qh_make ( /* set the address of the TT; for TDI's integrated * root hub tt, leave it zeroed. */ - if (!ehci_is_TDI(ehci) - || urb->dev->tt->hub != - ehci_to_hcd(ehci)->self.root_hub) - info2 |= urb->dev->tt->hub->devnum << 16; + if (tt && tt->hub != ehci_to_hcd(ehci)->self.root_hub) + info2 |= tt->hub->devnum << 16; /* NOTE: if (PIPE_INTERRUPT) { scheduler sets c-mask } */ @@ -973,7 +976,7 @@ static void end_unlink_async (struct ehci_hcd *ehci) struct ehci_qh *qh = ehci->reclaim; struct ehci_qh *next; - timer_action_done (ehci, TIMER_IAA_WATCHDOG); + iaa_watchdog_done(ehci); // qh->hw_next = cpu_to_hc32(qh->qh_dma); qh->qh_state = QH_STATE_IDLE; @@ -983,7 +986,6 @@ static void end_unlink_async (struct ehci_hcd *ehci) /* other unlink(s) may be pending (in QH_STATE_UNLINK_WAIT) */ next = qh->reclaim; ehci->reclaim = next; - ehci->reclaim_ready = 0; qh->reclaim = NULL; qh_completions (ehci, qh); @@ -1059,11 +1061,10 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) return; } - ehci->reclaim_ready = 0; cmd |= CMD_IAAD; ehci_writel(ehci, cmd, &ehci->regs->command); (void)ehci_readl(ehci, &ehci->regs->command); - timer_action (ehci, TIMER_IAA_WATCHDOG); + iaa_watchdog_start(ehci); } /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 80d99bce2b38..8a8e08a51ba3 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -119,7 +119,8 @@ periodic_usecs (struct ehci_hcd *ehci, unsigned frame, unsigned uframe) q = &q->fstn->fstn_next; break; case Q_TYPE_ITD: - usecs += q->itd->usecs [uframe]; + if (q->itd->hw_transaction[uframe]) + usecs += q->itd->stream->usecs; hw_p = &q->itd->hw_next; q = &q->itd->itd_next; break; @@ -211,7 +212,7 @@ static inline void carryover_tt_bandwidth(unsigned short tt_usecs[8]) * low/fullspeed transfer can "carry over" from one uframe to the next, * since the TT just performs downstream transfers in sequence. * - * For example two seperate 100 usec transfers can start in the same uframe, + * For example two separate 100 usec transfers can start in the same uframe, * and the second one would "carry over" 75 usecs into the next uframe. */ static void @@ -1536,7 +1537,6 @@ itd_link_urb ( uframe = next_uframe & 0x07; frame = next_uframe >> 3; - itd->usecs [uframe] = stream->usecs; itd_patch(ehci, itd, iso_sched, packet, uframe); next_uframe += stream->interval; @@ -1565,6 +1565,16 @@ itd_link_urb ( #define ISO_ERRS (EHCI_ISOC_BUF_ERR | EHCI_ISOC_BABBLE | EHCI_ISOC_XACTERR) +/* Process and recycle a completed ITD. Return true iff its urb completed, + * and hence its completion callback probably added things to the hardware + * schedule. + * + * Note that we carefully avoid recycling this descriptor until after any + * completion callback runs, so that it won't be reused quickly. That is, + * assuming (a) no more than two urbs per frame on this endpoint, and also + * (b) only this endpoint's completions submit URBs. It seems some silicon + * corrupts things if you reuse completed descriptors very quickly... + */ static unsigned itd_complete ( struct ehci_hcd *ehci, @@ -1577,6 +1587,7 @@ itd_complete ( int urb_index = -1; struct ehci_iso_stream *stream = itd->stream; struct usb_device *dev; + unsigned retval = false; /* for each uframe with a packet */ for (uframe = 0; uframe < 8; uframe++) { @@ -1610,30 +1621,21 @@ itd_complete ( } } - usb_put_urb (urb); - itd->urb = NULL; - itd->stream = NULL; - list_move (&itd->itd_list, &stream->free_list); - iso_stream_put (ehci, stream); - /* handle completion now? */ if (likely ((urb_index + 1) != urb->number_of_packets)) - return 0; + goto done; /* ASSERT: it's really the last itd for this urb list_for_each_entry (itd, &stream->td_list, itd_list) BUG_ON (itd->urb == urb); */ - /* give urb back to the driver ... can be out-of-order */ + /* give urb back to the driver; completion often (re)submits */ dev = urb->dev; ehci_urb_done(ehci, urb, 0); + retval = true; urb = NULL; - - /* defer stopping schedule; completion can submit */ ehci->periodic_sched--; - if (unlikely (!ehci->periodic_sched)) - (void) disable_periodic (ehci); ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--; if (unlikely (list_empty (&stream->td_list))) { @@ -1645,8 +1647,15 @@ itd_complete ( (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out"); } iso_stream_put (ehci, stream); + /* OK to recycle this ITD now that its completion callback ran. */ +done: + usb_put_urb(urb); + itd->urb = NULL; + itd->stream = NULL; + list_move(&itd->itd_list, &stream->free_list); + iso_stream_put(ehci, stream); - return 1; + return retval; } /*-------------------------------------------------------------------------*/ @@ -1712,8 +1721,6 @@ done: return status; } -#ifdef CONFIG_USB_EHCI_SPLIT_ISO - /*-------------------------------------------------------------------------*/ /* @@ -1950,6 +1957,16 @@ sitd_link_urb ( #define SITD_ERRS (SITD_STS_ERR | SITD_STS_DBE | SITD_STS_BABBLE \ | SITD_STS_XACT | SITD_STS_MMF) +/* Process and recycle a completed SITD. Return true iff its urb completed, + * and hence its completion callback probably added things to the hardware + * schedule. + * + * Note that we carefully avoid recycling this descriptor until after any + * completion callback runs, so that it won't be reused quickly. That is, + * assuming (a) no more than two urbs per frame on this endpoint, and also + * (b) only this endpoint's completions submit URBs. It seems some silicon + * corrupts things if you reuse completed descriptors very quickly... + */ static unsigned sitd_complete ( struct ehci_hcd *ehci, @@ -1961,6 +1978,7 @@ sitd_complete ( int urb_index = -1; struct ehci_iso_stream *stream = sitd->stream; struct usb_device *dev; + unsigned retval = false; urb_index = sitd->index; desc = &urb->iso_frame_desc [urb_index]; @@ -1981,32 +1999,23 @@ sitd_complete ( desc->status = 0; desc->actual_length = desc->length - SITD_LENGTH (t); } - - usb_put_urb (urb); - sitd->urb = NULL; - sitd->stream = NULL; - list_move (&sitd->sitd_list, &stream->free_list); stream->depth -= stream->interval << 3; - iso_stream_put (ehci, stream); /* handle completion now? */ if ((urb_index + 1) != urb->number_of_packets) - return 0; + goto done; /* ASSERT: it's really the last sitd for this urb list_for_each_entry (sitd, &stream->td_list, sitd_list) BUG_ON (sitd->urb == urb); */ - /* give urb back to the driver */ + /* give urb back to the driver; completion often (re)submits */ dev = urb->dev; ehci_urb_done(ehci, urb, 0); + retval = true; urb = NULL; - - /* defer stopping schedule; completion can submit */ ehci->periodic_sched--; - if (!ehci->periodic_sched) - (void) disable_periodic (ehci); ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--; if (list_empty (&stream->td_list)) { @@ -2018,8 +2027,15 @@ sitd_complete ( (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out"); } iso_stream_put (ehci, stream); + /* OK to recycle this SITD now that its completion callback ran. */ +done: + usb_put_urb(urb); + sitd->urb = NULL; + sitd->stream = NULL; + list_move(&sitd->sitd_list, &stream->free_list); + iso_stream_put(ehci, stream); - return 1; + return retval; } @@ -2082,26 +2098,6 @@ done: return status; } -#else - -static inline int -sitd_submit (struct ehci_hcd *ehci, struct urb *urb, gfp_t mem_flags) -{ - ehci_dbg (ehci, "split iso support is disabled\n"); - return -ENOSYS; -} - -static inline unsigned -sitd_complete ( - struct ehci_hcd *ehci, - struct ehci_sitd *sitd -) { - ehci_err (ehci, "sitd_complete %p?\n", sitd); - return 0; -} - -#endif /* USB_EHCI_SPLIT_ISO */ - /*-------------------------------------------------------------------------*/ static void @@ -2127,17 +2123,9 @@ scan_periodic (struct ehci_hcd *ehci) for (;;) { union ehci_shadow q, *q_p; __hc32 type, *hw_p; - unsigned uframes; + unsigned incomplete = false; - /* don't scan past the live uframe */ frame = now_uframe >> 3; - if (frame == (clock >> 3)) - uframes = now_uframe & 0x07; - else { - /* safe to scan the whole frame at once */ - now_uframe |= 0x07; - uframes = 8; - } restart: /* scan each element in frame's queue for completions */ @@ -2175,12 +2163,15 @@ restart: q = q.fstn->fstn_next; break; case Q_TYPE_ITD: - /* skip itds for later in the frame */ + /* If this ITD is still active, leave it for + * later processing ... check the next entry. + */ rmb (); - for (uf = live ? uframes : 8; uf < 8; uf++) { + for (uf = 0; uf < 8 && live; uf++) { if (0 == (q.itd->hw_transaction [uf] & ITD_ACTIVE(ehci))) continue; + incomplete = true; q_p = &q.itd->itd_next; hw_p = &q.itd->hw_next; type = Q_NEXT_TYPE(ehci, @@ -2188,10 +2179,12 @@ restart: q = *q_p; break; } - if (uf != 8) + if (uf < 8 && live) break; - /* this one's ready ... HC won't cache the + /* Take finished ITDs out of the schedule + * and process them: recycle, maybe report + * URB completion. HC won't cache the * pointer for much longer, if at all. */ *q_p = q.itd->itd_next; @@ -2202,8 +2195,12 @@ restart: q = *q_p; break; case Q_TYPE_SITD: + /* If this SITD is still active, leave it for + * later processing ... check the next entry. + */ if ((q.sitd->hw_results & SITD_ACTIVE(ehci)) && live) { + incomplete = true; q_p = &q.sitd->sitd_next; hw_p = &q.sitd->hw_next; type = Q_NEXT_TYPE(ehci, @@ -2211,6 +2208,11 @@ restart: q = *q_p; break; } + + /* Take finished SITDs out of the schedule + * and process them: recycle, maybe report + * URB completion. + */ *q_p = q.sitd->sitd_next; *hw_p = q.sitd->hw_next; type = Q_NEXT_TYPE(ehci, q.sitd->hw_next); @@ -2226,11 +2228,24 @@ restart: } /* assume completion callbacks modify the queue */ - if (unlikely (modified)) - goto restart; + if (unlikely (modified)) { + if (likely(ehci->periodic_sched > 0)) + goto restart; + /* maybe we can short-circuit this scan! */ + disable_periodic(ehci); + now_uframe = clock; + break; + } } - /* stop when we catch up to the HC */ + /* If we can tell we caught up to the hardware, stop now. + * We can't advance our scan without collecting the ISO + * transfers that are still pending in this frame. + */ + if (incomplete && HC_IS_RUNNING(ehci_to_hcd(ehci)->state)) { + ehci->next_uframe = now_uframe; + break; + } // FIXME: this assumes we won't get lapped when // latencies climb; that should be rare, but... @@ -2243,7 +2258,8 @@ restart: if (now_uframe == clock) { unsigned now; - if (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state)) + if (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state) + || ehci->periodic_sched == 0) break; ehci->next_uframe = now_uframe; now = ehci_readl(ehci, &ehci->regs->frame_index) % mod; diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 951d69fec513..bf92d209a1a9 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -74,7 +74,6 @@ struct ehci_hcd { /* one per controller */ /* async schedule support */ struct ehci_qh *async; struct ehci_qh *reclaim; - unsigned reclaim_ready : 1; unsigned scanning : 1; /* periodic schedule support */ @@ -105,6 +104,7 @@ struct ehci_hcd { /* one per controller */ struct dma_pool *itd_pool; /* itd per iso urb */ struct dma_pool *sitd_pool; /* sitd per split iso urb */ + struct timer_list iaa_watchdog; struct timer_list watchdog; unsigned long actions; unsigned stamp; @@ -127,6 +127,14 @@ struct ehci_hcd { /* one per controller */ #else # define COUNT(x) do {} while (0) #endif + + /* debug files */ +#ifdef DEBUG + struct dentry *debug_dir; + struct dentry *debug_async; + struct dentry *debug_periodic; + struct dentry *debug_registers; +#endif }; /* convert between an HCD pointer and the corresponding EHCI_HCD */ @@ -140,9 +148,21 @@ static inline struct usb_hcd *ehci_to_hcd (struct ehci_hcd *ehci) } +static inline void +iaa_watchdog_start(struct ehci_hcd *ehci) +{ + WARN_ON(timer_pending(&ehci->iaa_watchdog)); + mod_timer(&ehci->iaa_watchdog, + jiffies + msecs_to_jiffies(EHCI_IAA_MSECS)); +} + +static inline void iaa_watchdog_done(struct ehci_hcd *ehci) +{ + del_timer(&ehci->iaa_watchdog); +} + enum ehci_timer_action { TIMER_IO_WATCHDOG, - TIMER_IAA_WATCHDOG, TIMER_ASYNC_SHRINK, TIMER_ASYNC_OFF, }; @@ -160,9 +180,6 @@ timer_action (struct ehci_hcd *ehci, enum ehci_timer_action action) unsigned long t; switch (action) { - case TIMER_IAA_WATCHDOG: - t = EHCI_IAA_JIFFIES; - break; case TIMER_IO_WATCHDOG: t = EHCI_IO_JIFFIES; break; @@ -179,8 +196,7 @@ timer_action (struct ehci_hcd *ehci, enum ehci_timer_action action) // async queue SHRINK often precedes IAA. while it's ready // to go OFF neither can matter, and afterwards the IO // watchdog stops unless there's still periodic traffic. - if (action != TIMER_IAA_WATCHDOG - && t > ehci->watchdog.expires + if (time_before_eq(t, ehci->watchdog.expires) && timer_pending (&ehci->watchdog)) return; mod_timer (&ehci->watchdog, t); @@ -534,8 +550,8 @@ struct ehci_iso_stream { * trusting urb->interval == f(epdesc->bInterval) and * including the extra info for hw_bufp[0..2] */ - u8 interval; u8 usecs, c_usecs; + u16 interval; u16 tt_usecs; u16 maxp; u16 raw_mask; @@ -586,7 +602,6 @@ struct ehci_itd { unsigned frame; /* where scheduled */ unsigned pg; unsigned index[8]; /* in urb->iso_frame_desc */ - u8 usecs[8]; } __attribute__ ((aligned (32))); /*-------------------------------------------------------------------------*/ @@ -725,11 +740,16 @@ ehci_port_speed(struct ehci_hcd *ehci, unsigned int portsc) * definition below can die once the 4xx support is * finally ported over. */ -#if defined(CONFIG_PPC) +#if defined(CONFIG_PPC) && !defined(CONFIG_PPC_MERGE) #define readl_be(addr) in_be32((__force unsigned *)addr) #define writel_be(val, addr) out_be32((__force unsigned *)addr, val) #endif +#if defined(CONFIG_ARM) && defined(CONFIG_ARCH_IXP4XX) +#define readl_be(addr) __raw_readl((__force unsigned *)addr) +#define writel_be(val, addr) __raw_writel(val, (__force unsigned *)addr) +#endif + static inline unsigned int ehci_readl(const struct ehci_hcd *ehci, __u32 __iomem * regs) { diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c index c27417f5b9d8..0130fd8571e4 100644 --- a/drivers/usb/host/isp116x-hcd.c +++ b/drivers/usb/host/isp116x-hcd.c @@ -918,7 +918,6 @@ static int isp116x_hub_status_data(struct usb_hcd *hcd, char *buf) | RH_PS_OCIC | RH_PS_PRSC)) { changed = 1; buf[0] |= 1 << (i + 1); - continue; } } spin_unlock_irqrestore(&isp116x->lock, flags); diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c index d849c809acbd..126fcbdd6408 100644 --- a/drivers/usb/host/ohci-at91.c +++ b/drivers/usb/host/ohci-at91.c @@ -17,6 +17,8 @@ #include <asm/mach-types.h> #include <asm/hardware.h> +#include <asm/gpio.h> + #include <asm/arch/board.h> #include <asm/arch/cpu.h> @@ -271,12 +273,41 @@ static const struct hc_driver ohci_at91_hc_driver = { static int ohci_hcd_at91_drv_probe(struct platform_device *pdev) { + struct at91_usbh_data *pdata = pdev->dev.platform_data; + int i; + + if (pdata) { + /* REVISIT make the driver support per-port power switching, + * and also overcurrent detection. Here we assume the ports + * are always powered while this driver is active, and use + * active-low power switches. + */ + for (i = 0; i < pdata->ports; i++) { + if (pdata->vbus_pin[i] <= 0) + continue; + gpio_request(pdata->vbus_pin[i], "ohci_vbus"); + gpio_direction_output(pdata->vbus_pin[i], 0); + } + } + device_init_wakeup(&pdev->dev, 1); return usb_hcd_at91_probe(&ohci_at91_hc_driver, pdev); } static int ohci_hcd_at91_drv_remove(struct platform_device *pdev) { + struct at91_usbh_data *pdata = pdev->dev.platform_data; + int i; + + if (pdata) { + for (i = 0; i < pdata->ports; i++) { + if (pdata->vbus_pin[i] <= 0) + continue; + gpio_direction_output(pdata->vbus_pin[i], 1); + gpio_free(pdata->vbus_pin[i]); + } + } + device_init_wakeup(&pdev->dev, 0); return usb_hcd_at91_remove(platform_get_drvdata(pdev), pdev); } diff --git a/drivers/usb/host/ohci-dbg.c b/drivers/usb/host/ohci-dbg.c index ebab5ce8f5ce..a22c30aa745d 100644 --- a/drivers/usb/host/ohci-dbg.c +++ b/drivers/usb/host/ohci-dbg.c @@ -401,6 +401,42 @@ static inline void remove_debug_files (struct ohci_hcd *bus) { } #else +static int debug_async_open(struct inode *, struct file *); +static int debug_periodic_open(struct inode *, struct file *); +static int debug_registers_open(struct inode *, struct file *); +static int debug_async_open(struct inode *, struct file *); +static ssize_t debug_output(struct file*, char __user*, size_t, loff_t*); +static int debug_close(struct inode *, struct file *); + +static const struct file_operations debug_async_fops = { + .owner = THIS_MODULE, + .open = debug_async_open, + .read = debug_output, + .release = debug_close, +}; +static const struct file_operations debug_periodic_fops = { + .owner = THIS_MODULE, + .open = debug_periodic_open, + .read = debug_output, + .release = debug_close, +}; +static const struct file_operations debug_registers_fops = { + .owner = THIS_MODULE, + .open = debug_registers_open, + .read = debug_output, + .release = debug_close, +}; + +static struct dentry *ohci_debug_root; + +struct debug_buffer { + ssize_t (*fill_func)(struct debug_buffer *); /* fill method */ + struct device *dev; + struct mutex mutex; /* protect filling of buffer */ + size_t count; /* number of characters filled into buffer */ + char *page; +}; + static ssize_t show_list (struct ohci_hcd *ohci, char *buf, size_t count, struct ed *ed) { @@ -467,8 +503,7 @@ show_list (struct ohci_hcd *ohci, char *buf, size_t count, struct ed *ed) return count - size; } -static ssize_t -show_async (struct class_device *class_dev, char *buf) +static ssize_t fill_async_buffer(struct debug_buffer *buf) { struct usb_bus *bus; struct usb_hcd *hcd; @@ -476,25 +511,23 @@ show_async (struct class_device *class_dev, char *buf) size_t temp; unsigned long flags; - bus = class_get_devdata(class_dev); + bus = dev_get_drvdata(buf->dev); hcd = bus_to_hcd(bus); ohci = hcd_to_ohci(hcd); /* display control and bulk lists together, for simplicity */ spin_lock_irqsave (&ohci->lock, flags); - temp = show_list (ohci, buf, PAGE_SIZE, ohci->ed_controltail); - temp += show_list (ohci, buf + temp, PAGE_SIZE - temp, ohci->ed_bulktail); + temp = show_list(ohci, buf->page, buf->count, ohci->ed_controltail); + temp += show_list(ohci, buf->page + temp, buf->count - temp, + ohci->ed_bulktail); spin_unlock_irqrestore (&ohci->lock, flags); return temp; } -static CLASS_DEVICE_ATTR (async, S_IRUGO, show_async, NULL); - #define DBG_SCHED_LIMIT 64 -static ssize_t -show_periodic (struct class_device *class_dev, char *buf) +static ssize_t fill_periodic_buffer(struct debug_buffer *buf) { struct usb_bus *bus; struct usb_hcd *hcd; @@ -509,10 +542,10 @@ show_periodic (struct class_device *class_dev, char *buf) return 0; seen_count = 0; - bus = class_get_devdata(class_dev); + bus = (struct usb_bus *)dev_get_drvdata(buf->dev); hcd = bus_to_hcd(bus); ohci = hcd_to_ohci(hcd); - next = buf; + next = buf->page; size = PAGE_SIZE; temp = scnprintf (next, size, "size = %d\n", NUM_INTS); @@ -589,13 +622,9 @@ show_periodic (struct class_device *class_dev, char *buf) return PAGE_SIZE - size; } -static CLASS_DEVICE_ATTR (periodic, S_IRUGO, show_periodic, NULL); - - #undef DBG_SCHED_LIMIT -static ssize_t -show_registers (struct class_device *class_dev, char *buf) +static ssize_t fill_registers_buffer(struct debug_buffer *buf) { struct usb_bus *bus; struct usb_hcd *hcd; @@ -606,11 +635,11 @@ show_registers (struct class_device *class_dev, char *buf) char *next; u32 rdata; - bus = class_get_devdata(class_dev); + bus = (struct usb_bus *)dev_get_drvdata(buf->dev); hcd = bus_to_hcd(bus); ohci = hcd_to_ohci(hcd); regs = ohci->regs; - next = buf; + next = buf->page; size = PAGE_SIZE; spin_lock_irqsave (&ohci->lock, flags); @@ -677,29 +706,155 @@ show_registers (struct class_device *class_dev, char *buf) done: spin_unlock_irqrestore (&ohci->lock, flags); + return PAGE_SIZE - size; } -static CLASS_DEVICE_ATTR (registers, S_IRUGO, show_registers, NULL); +static struct debug_buffer *alloc_buffer(struct device *dev, + ssize_t (*fill_func)(struct debug_buffer *)) +{ + struct debug_buffer *buf; + + buf = kzalloc(sizeof(struct debug_buffer), GFP_KERNEL); + if (buf) { + buf->dev = dev; + buf->fill_func = fill_func; + mutex_init(&buf->mutex); + } + + return buf; +} + +static int fill_buffer(struct debug_buffer *buf) +{ + int ret = 0; + + if (!buf->page) + buf->page = (char *)get_zeroed_page(GFP_KERNEL); + + if (!buf->page) { + ret = -ENOMEM; + goto out; + } + + ret = buf->fill_func(buf); + + if (ret >= 0) { + buf->count = ret; + ret = 0; + } + +out: + return ret; +} + +static ssize_t debug_output(struct file *file, char __user *user_buf, + size_t len, loff_t *offset) +{ + struct debug_buffer *buf = file->private_data; + int ret = 0; + + mutex_lock(&buf->mutex); + if (buf->count == 0) { + ret = fill_buffer(buf); + if (ret != 0) { + mutex_unlock(&buf->mutex); + goto out; + } + } + mutex_unlock(&buf->mutex); + + ret = simple_read_from_buffer(user_buf, len, offset, + buf->page, buf->count); + +out: + return ret; + +} + +static int debug_close(struct inode *inode, struct file *file) +{ + struct debug_buffer *buf = file->private_data; + + if (buf) { + if (buf->page) + free_page((unsigned long)buf->page); + kfree(buf); + } + + return 0; +} +static int debug_async_open(struct inode *inode, struct file *file) +{ + file->private_data = alloc_buffer(inode->i_private, fill_async_buffer); + + return file->private_data ? 0 : -ENOMEM; +} + +static int debug_periodic_open(struct inode *inode, struct file *file) +{ + file->private_data = alloc_buffer(inode->i_private, + fill_periodic_buffer); + + return file->private_data ? 0 : -ENOMEM; +} + +static int debug_registers_open(struct inode *inode, struct file *file) +{ + file->private_data = alloc_buffer(inode->i_private, + fill_registers_buffer); + + return file->private_data ? 0 : -ENOMEM; +} static inline void create_debug_files (struct ohci_hcd *ohci) { - struct class_device *cldev = ohci_to_hcd(ohci)->self.class_dev; - int retval; + struct usb_bus *bus = &ohci_to_hcd(ohci)->self; + struct device *dev = bus->dev; + + ohci->debug_dir = debugfs_create_dir(bus->bus_name, ohci_debug_root); + if (!ohci->debug_dir) + goto dir_error; + + ohci->debug_async = debugfs_create_file("async", S_IRUGO, + ohci->debug_dir, dev, + &debug_async_fops); + if (!ohci->debug_async) + goto async_error; + + ohci->debug_periodic = debugfs_create_file("periodic", S_IRUGO, + ohci->debug_dir, dev, + &debug_periodic_fops); + if (!ohci->debug_periodic) + goto periodic_error; + + ohci->debug_registers = debugfs_create_file("registers", S_IRUGO, + ohci->debug_dir, dev, + &debug_registers_fops); + if (!ohci->debug_registers) + goto registers_error; - retval = class_device_create_file(cldev, &class_device_attr_async); - retval = class_device_create_file(cldev, &class_device_attr_periodic); - retval = class_device_create_file(cldev, &class_device_attr_registers); ohci_dbg (ohci, "created debug files\n"); + return; + +registers_error: + debugfs_remove(ohci->debug_periodic); +periodic_error: + debugfs_remove(ohci->debug_async); +async_error: + debugfs_remove(ohci->debug_dir); +dir_error: + ohci->debug_periodic = NULL; + ohci->debug_async = NULL; + ohci->debug_dir = NULL; } static inline void remove_debug_files (struct ohci_hcd *ohci) { - struct class_device *cldev = ohci_to_hcd(ohci)->self.class_dev; - - class_device_remove_file(cldev, &class_device_attr_async); - class_device_remove_file(cldev, &class_device_attr_periodic); - class_device_remove_file(cldev, &class_device_attr_registers); + debugfs_remove(ohci->debug_registers); + debugfs_remove(ohci->debug_periodic); + debugfs_remove(ohci->debug_async); + debugfs_remove(ohci->debug_dir); } #endif diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index ddd4ee1f2413..dd4798ee028e 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -36,6 +36,7 @@ #include <linux/dmapool.h> #include <linux/reboot.h> #include <linux/workqueue.h> +#include <linux/debugfs.h> #include <asm/io.h> #include <asm/irq.h> @@ -809,13 +810,9 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd) } if (ints & OHCI_INTR_WDH) { - if (HC_IS_RUNNING(hcd->state)) - ohci_writel (ohci, OHCI_INTR_WDH, ®s->intrdisable); spin_lock (&ohci->lock); dl_done_list (ohci); spin_unlock (&ohci->lock); - if (HC_IS_RUNNING(hcd->state)) - ohci_writel (ohci, OHCI_INTR_WDH, ®s->intrenable); } if (quirk_zfmicro(ohci) && (ints & OHCI_INTR_SF)) { @@ -1032,6 +1029,13 @@ MODULE_LICENSE ("GPL"); #define PLATFORM_DRIVER usb_hcd_pnx4008_driver #endif +#if defined(CONFIG_CPU_SUBTYPE_SH7720) || \ + defined(CONFIG_CPU_SUBTYPE_SH7721) || \ + defined(CONFIG_CPU_SUBTYPE_SH7763) +#include "ohci-sh.c" +#define PLATFORM_DRIVER ohci_hcd_sh_driver +#endif + #ifdef CONFIG_USB_OHCI_HCD_PPC_OF #include "ohci-ppc-of.c" @@ -1048,6 +1052,11 @@ MODULE_LICENSE ("GPL"); #define SSB_OHCI_DRIVER ssb_ohci_driver #endif +#ifdef CONFIG_MFD_SM501 +#include "ohci-sm501.c" +#define PLATFORM_DRIVER ohci_hcd_sm501_driver +#endif + #if !defined(PCI_DRIVER) && \ !defined(PLATFORM_DRIVER) && \ !defined(OF_PLATFORM_DRIVER) && \ @@ -1068,6 +1077,14 @@ static int __init ohci_hcd_mod_init(void) pr_debug ("%s: block sizes: ed %Zd td %Zd\n", hcd_name, sizeof (struct ed), sizeof (struct td)); +#ifdef DEBUG + ohci_debug_root = debugfs_create_dir("ohci", NULL); + if (!ohci_debug_root) { + retval = -ENOENT; + goto error_debug; + } +#endif + #ifdef PS3_SYSTEM_BUS_DRIVER retval = ps3_ohci_driver_register(&PS3_SYSTEM_BUS_DRIVER); if (retval < 0) @@ -1130,6 +1147,12 @@ static int __init ohci_hcd_mod_init(void) ps3_ohci_driver_unregister(&PS3_SYSTEM_BUS_DRIVER); error_ps3: #endif +#ifdef DEBUG + debugfs_remove(ohci_debug_root); + ohci_debug_root = NULL; + error_debug: +#endif + return retval; } module_init(ohci_hcd_mod_init); @@ -1154,6 +1177,9 @@ static void __exit ohci_hcd_mod_exit(void) #ifdef PS3_SYSTEM_BUS_DRIVER ps3_ohci_driver_unregister(&PS3_SYSTEM_BUS_DRIVER); #endif +#ifdef DEBUG + debugfs_remove(ohci_debug_root); +#endif } module_exit(ohci_hcd_mod_exit); diff --git a/drivers/usb/host/ohci-sh.c b/drivers/usb/host/ohci-sh.c new file mode 100644 index 000000000000..5309ac039e15 --- /dev/null +++ b/drivers/usb/host/ohci-sh.c @@ -0,0 +1,143 @@ +/* + * OHCI HCD (Host Controller Driver) for USB. + * + * Copyright (C) 2008 Renesas Solutions Corp. + * + * Author : Yoshihiro Shimoda <shimoda.yoshihiro@renesas.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include <linux/platform_device.h> + +static int ohci_sh_start(struct usb_hcd *hcd) +{ + struct ohci_hcd *ohci = hcd_to_ohci(hcd); + + ohci_hcd_init(ohci); + ohci_init(ohci); + ohci_run(ohci); + hcd->state = HC_STATE_RUNNING; + return 0; +} + +static const struct hc_driver ohci_sh_hc_driver = { + .description = hcd_name, + .product_desc = "SuperH OHCI", + .hcd_priv_size = sizeof(struct ohci_hcd), + + /* + * generic hardware linkage + */ + .irq = ohci_irq, + .flags = HCD_USB11 | HCD_MEMORY, + + /* + * basic lifecycle operations + */ + .start = ohci_sh_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, + .hub_irq_enable = ohci_rhsc_enable, +#ifdef CONFIG_PM + .bus_suspend = ohci_bus_suspend, + .bus_resume = ohci_bus_resume, +#endif + .start_port_reset = ohci_start_port_reset, +}; + +/*-------------------------------------------------------------------------*/ + +#define resource_len(r) (((r)->end - (r)->start) + 1) +static int ohci_hcd_sh_probe(struct platform_device *pdev) +{ + struct resource *res = NULL; + struct usb_hcd *hcd = NULL; + int irq = -1; + int ret; + + if (usb_disabled()) + return -ENODEV; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + err("platform_get_resource error."); + return -ENODEV; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + err("platform_get_irq error."); + return -ENODEV; + } + + /* initialize hcd */ + hcd = usb_create_hcd(&ohci_sh_hc_driver, &pdev->dev, (char *)hcd_name); + if (!hcd) { + err("Failed to create hcd"); + return -ENOMEM; + } + + hcd->regs = (void __iomem *)res->start; + hcd->rsrc_start = res->start; + hcd->rsrc_len = resource_len(res); + ret = usb_add_hcd(hcd, irq, IRQF_DISABLED); + if (ret != 0) { + err("Failed to add hcd"); + usb_put_hcd(hcd); + return ret; + } + + return ret; +} + +static int ohci_hcd_sh_remove(struct platform_device *pdev) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + + usb_remove_hcd(hcd); + usb_put_hcd(hcd); + + return 0; +} + +static struct platform_driver ohci_hcd_sh_driver = { + .probe = ohci_hcd_sh_probe, + .remove = ohci_hcd_sh_remove, + .shutdown = usb_hcd_platform_shutdown, + .driver = { + .name = "sh_ohci", + .owner = THIS_MODULE, + }, +}; + diff --git a/drivers/usb/host/ohci-sm501.c b/drivers/usb/host/ohci-sm501.c new file mode 100644 index 000000000000..a97070142869 --- /dev/null +++ b/drivers/usb/host/ohci-sm501.c @@ -0,0 +1,264 @@ +/* + * OHCI HCD (Host Controller Driver) for USB. + * + * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at> + * (C) Copyright 2000-2005 David Brownell + * (C) Copyright 2002 Hewlett-Packard Company + * (C) Copyright 2008 Magnus Damm + * + * SM501 Bus Glue - based on ohci-omap.c + * + * This file is licenced under the GPL. + */ + +#include <linux/interrupt.h> +#include <linux/jiffies.h> +#include <linux/platform_device.h> +#include <linux/dma-mapping.h> +#include <linux/sm501.h> +#include <linux/sm501-regs.h> + +static int ohci_sm501_init(struct usb_hcd *hcd) +{ + return ohci_init(hcd_to_ohci(hcd)); +} + +static int ohci_sm501_start(struct usb_hcd *hcd) +{ + struct device *dev = hcd->self.controller; + int ret; + + ret = ohci_run(hcd_to_ohci(hcd)); + if (ret < 0) { + dev_err(dev, "can't start %s", hcd->self.bus_name); + ohci_stop(hcd); + } + + return ret; +} + +/*-------------------------------------------------------------------------*/ + +static const struct hc_driver ohci_sm501_hc_driver = { + .description = hcd_name, + .product_desc = "SM501 OHCI", + .hcd_priv_size = sizeof(struct ohci_hcd), + + /* + * generic hardware linkage + */ + .irq = ohci_irq, + .flags = HCD_USB11 | HCD_MEMORY | HCD_LOCAL_MEM, + + /* + * basic lifecycle operations + */ + .reset = ohci_sm501_init, + .start = ohci_sm501_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, + .hub_irq_enable = ohci_rhsc_enable, +#ifdef CONFIG_PM + .bus_suspend = ohci_bus_suspend, + .bus_resume = ohci_bus_resume, +#endif + .start_port_reset = ohci_start_port_reset, +}; + +/*-------------------------------------------------------------------------*/ + +static int ohci_hcd_sm501_drv_probe(struct platform_device *pdev) +{ + const struct hc_driver *driver = &ohci_sm501_hc_driver; + struct device *dev = &pdev->dev; + struct resource *res, *mem; + int retval, irq; + struct usb_hcd *hcd = 0; + + irq = retval = platform_get_irq(pdev, 0); + if (retval < 0) + goto err0; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (mem == NULL) { + dev_err(dev, "no resource definition for memory\n"); + retval = -ENOENT; + goto err0; + } + + if (!request_mem_region(mem->start, mem->end - mem->start + 1, + pdev->name)) { + dev_err(dev, "request_mem_region failed\n"); + retval = -EBUSY; + goto err0; + } + + /* The sm501 chip is equipped with local memory that may be used + * by on-chip devices such as the video controller and the usb host. + * This driver uses dma_declare_coherent_memory() to make sure + * usb allocations with dma_alloc_coherent() allocate from + * this local memory. The dma_handle returned by dma_alloc_coherent() + * will be an offset starting from 0 for the first local memory byte. + * + * So as long as data is allocated using dma_alloc_coherent() all is + * fine. This is however not always the case - buffers may be allocated + * using kmalloc() - so the usb core needs to be told that it must copy + * data into our local memory if the buffers happen to be placed in + * regular memory. The HCD_LOCAL_MEM flag does just that. + */ + + if (!dma_declare_coherent_memory(dev, mem->start, + mem->start - mem->parent->start, + (mem->end - mem->start) + 1, + DMA_MEMORY_MAP | + DMA_MEMORY_EXCLUSIVE)) { + dev_err(dev, "cannot declare coherent memory\n"); + retval = -ENXIO; + goto err1; + } + + /* allocate, reserve and remap resources for registers */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(dev, "no resource definition for registers\n"); + retval = -ENOENT; + goto err2; + } + + hcd = usb_create_hcd(driver, &pdev->dev, pdev->dev.bus_id); + if (!hcd) { + retval = -ENOMEM; + goto err2; + } + + hcd->rsrc_start = res->start; + hcd->rsrc_len = res->end - res->start + 1; + + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, pdev->name)) { + dev_err(dev, "request_mem_region failed\n"); + retval = -EBUSY; + goto err3; + } + + hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); + if (hcd->regs == NULL) { + dev_err(dev, "cannot remap registers\n"); + retval = -ENXIO; + goto err4; + } + + ohci_hcd_init(hcd_to_ohci(hcd)); + + retval = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); + if (retval) + goto err4; + + /* enable power and unmask interrupts */ + + sm501_unit_power(dev->parent, SM501_GATE_USB_HOST, 1); + sm501_modify_reg(dev->parent, SM501_IRQ_MASK, 1 << 6, 0); + + return 0; +err4: + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); +err3: + usb_put_hcd(hcd); +err2: + dma_release_declared_memory(dev); +err1: + release_mem_region(mem->start, mem->end - mem->start + 1); +err0: + return retval; +} + +static int ohci_hcd_sm501_drv_remove(struct platform_device *pdev) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct resource *mem; + + usb_remove_hcd(hcd); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + usb_put_hcd(hcd); + dma_release_declared_memory(&pdev->dev); + mem = platform_get_resource(pdev, IORESOURCE_MEM, 1); + release_mem_region(mem->start, mem->end - mem->start + 1); + + /* mask interrupts and disable power */ + + sm501_modify_reg(pdev->dev.parent, SM501_IRQ_MASK, 0, 1 << 6); + sm501_unit_power(pdev->dev.parent, SM501_GATE_USB_HOST, 0); + + platform_set_drvdata(pdev, NULL); + return 0; +} + +/*-------------------------------------------------------------------------*/ + +#ifdef CONFIG_PM +static int ohci_sm501_suspend(struct platform_device *pdev, pm_message_t msg) +{ + struct device *dev = &pdev->dev; + struct ohci_hcd *ohci = hcd_to_ohci(platform_get_drvdata(pdev)); + + if (time_before(jiffies, ohci->next_statechange)) + msleep(5); + ohci->next_statechange = jiffies; + + sm501_unit_power(dev->parent, SM501_GATE_USB_HOST, 0); + ohci_to_hcd(ohci)->state = HC_STATE_SUSPENDED; + dev->power.power_state = PMSG_SUSPEND; + return 0; +} + +static int ohci_sm501_resume(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct ohci_hcd *ohci = hcd_to_ohci(platform_get_drvdata(pdev)); + + if (time_before(jiffies, ohci->next_statechange)) + msleep(5); + ohci->next_statechange = jiffies; + + sm501_unit_power(dev->parent, SM501_GATE_USB_HOST, 1); + dev->power.power_state = PMSG_ON; + usb_hcd_resume_root_hub(platform_get_drvdata(pdev)); + return 0; +} +#endif + +/*-------------------------------------------------------------------------*/ + +/* + * Driver definition to register with the SM501 bus + */ +static struct platform_driver ohci_hcd_sm501_driver = { + .probe = ohci_hcd_sm501_drv_probe, + .remove = ohci_hcd_sm501_drv_remove, + .shutdown = usb_hcd_platform_shutdown, +#ifdef CONFIG_PM + .suspend = ohci_sm501_suspend, + .resume = ohci_sm501_resume, +#endif + .driver = { + .owner = THIS_MODULE, + .name = "sm501-usb", + }, +}; diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h index 47c5c66a282c..dc544ddc7849 100644 --- a/drivers/usb/host/ohci.h +++ b/drivers/usb/host/ohci.h @@ -408,6 +408,13 @@ struct ohci_hcd { unsigned eds_scheduled; struct ed *ed_to_check; unsigned zf_delay; + +#ifdef DEBUG + struct dentry *debug_dir; + struct dentry *debug_async; + struct dentry *debug_periodic; + struct dentry *debug_registers; +#endif }; #ifdef CONFIG_PCI diff --git a/drivers/usb/host/r8a66597.h b/drivers/usb/host/r8a66597.h index fe9ceb077d9b..57388252b693 100644 --- a/drivers/usb/host/r8a66597.h +++ b/drivers/usb/host/r8a66597.h @@ -405,7 +405,7 @@ struct r8a66597_pipe_info { u16 pipenum; - u16 address; /* R8A66597 HCD usb addres */ + u16 address; /* R8A66597 HCD usb address */ u16 epnum; u16 maxpacket; u16 type; |