summaryrefslogtreecommitdiff
path: root/drivers/usb
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/atm/ueagle-atm.c19
-rw-r--r--drivers/usb/core/buffer.c26
-rw-r--r--drivers/usb/core/driver.c5
-rw-r--r--drivers/usb/core/hcd-pci.c56
-rw-r--r--drivers/usb/core/hcd.c281
-rw-r--r--drivers/usb/core/hub.c199
-rw-r--r--drivers/usb/core/message.c22
-rw-r--r--drivers/usb/core/urb.c11
-rw-r--r--drivers/usb/core/usb.h13
-rw-r--r--drivers/usb/early/ehci-dbgp.c2
-rw-r--r--drivers/usb/gadget/Kconfig14
-rw-r--r--drivers/usb/gadget/Makefile1
-rw-r--r--drivers/usb/gadget/at91_udc.c4
-rw-r--r--drivers/usb/gadget/ci13xxx_udc.c344
-rw-r--r--drivers/usb/gadget/ci13xxx_udc.h9
-rw-r--r--drivers/usb/gadget/composite.c14
-rw-r--r--drivers/usb/gadget/dummy_hcd.c4
-rw-r--r--drivers/usb/gadget/epautoconf.c7
-rw-r--r--drivers/usb/gadget/f_fs.c8
-rw-r--r--drivers/usb/gadget/fsl_mxc_udc.c21
-rw-r--r--drivers/usb/gadget/fsl_udc_core.c2
-rw-r--r--drivers/usb/gadget/fusb300_udc.c1744
-rw-r--r--drivers/usb/gadget/fusb300_udc.h687
-rw-r--r--drivers/usb/gadget/m66592-udc.c2
-rw-r--r--drivers/usb/gadget/pch_udc.c178
-rw-r--r--drivers/usb/gadget/s3c2410_udc.c76
-rw-r--r--drivers/usb/gadget/u_ether.c2
-rw-r--r--drivers/usb/host/Kconfig39
-rw-r--r--drivers/usb/host/ehci-atmel.c6
-rw-r--r--drivers/usb/host/ehci-dbg.c4
-rw-r--r--drivers/usb/host/ehci-hcd.c29
-rw-r--r--drivers/usb/host/ehci-hub.c32
-rw-r--r--drivers/usb/host/ehci-lpm.c5
-rw-r--r--drivers/usb/host/ehci-msm.c96
-rw-r--r--drivers/usb/host/ehci-omap.c882
-rw-r--r--drivers/usb/host/ehci-orion.c5
-rw-r--r--drivers/usb/host/ehci-pci.c45
-rw-r--r--drivers/usb/host/ehci-pmcmsp.c383
-rw-r--r--drivers/usb/host/ehci-q.c18
-rw-r--r--drivers/usb/host/ehci-sched.c76
-rw-r--r--drivers/usb/host/ehci-tegra.c715
-rw-r--r--drivers/usb/host/ehci.h2
-rw-r--r--drivers/usb/host/imx21-hcd.c9
-rw-r--r--drivers/usb/host/isp116x-hcd.c6
-rw-r--r--drivers/usb/host/isp1362-hcd.c13
-rw-r--r--drivers/usb/host/isp1760-hcd.c1395
-rw-r--r--drivers/usb/host/isp1760-hcd.h56
-rw-r--r--drivers/usb/host/ohci-hcd.c18
-rw-r--r--drivers/usb/host/ohci-hub.c13
-rw-r--r--drivers/usb/host/ohci-omap3.c584
-rw-r--r--drivers/usb/host/ohci-pci.c114
-rw-r--r--drivers/usb/host/ohci-q.c4
-rw-r--r--drivers/usb/host/ohci.h4
-rw-r--r--drivers/usb/host/oxu210hp-hcd.c6
-rw-r--r--drivers/usb/host/pci-quirks.c258
-rw-r--r--drivers/usb/host/pci-quirks.h10
-rw-r--r--drivers/usb/host/r8a66597-hcd.c5
-rw-r--r--drivers/usb/host/sl811-hcd.c6
-rw-r--r--drivers/usb/host/u132-hcd.c11
-rw-r--r--drivers/usb/host/uhci-hcd.c2
-rw-r--r--drivers/usb/host/xhci-ext-caps.h4
-rw-r--r--drivers/usb/host/xhci-hub.c392
-rw-r--r--drivers/usb/host/xhci-mem.c93
-rw-r--r--drivers/usb/host/xhci-pci.c124
-rw-r--r--drivers/usb/host/xhci-ring.c195
-rw-r--r--drivers/usb/host/xhci.c129
-rw-r--r--drivers/usb/host/xhci.h53
-rw-r--r--drivers/usb/misc/usbtest.c236
-rw-r--r--drivers/usb/mon/mon_text.c3
-rw-r--r--drivers/usb/musb/musb_core.c65
-rw-r--r--drivers/usb/musb/musb_core.h8
-rw-r--r--drivers/usb/musb/musb_gadget.c217
-rw-r--r--drivers/usb/musb/musb_gadget.h7
-rw-r--r--drivers/usb/musb/musb_gadget_ep0.c24
-rw-r--r--drivers/usb/musb/musb_host.c4
-rw-r--r--drivers/usb/musb/musb_virthub.c4
-rw-r--r--drivers/usb/musb/omap2430.c176
-rw-r--r--drivers/usb/musb/tusb6010_omap.c4
-rw-r--r--drivers/usb/otg/Kconfig9
-rw-r--r--drivers/usb/otg/Makefile3
-rw-r--r--drivers/usb/otg/ab8500-usb.c6
-rw-r--r--drivers/usb/otg/langwell_otg.c48
-rw-r--r--drivers/usb/otg/msm_otg.c (renamed from drivers/usb/otg/msm72k_otg.c)31
-rw-r--r--drivers/usb/otg/nop-usb-xceiv.c2
-rw-r--r--drivers/usb/otg/twl4030-usb.c8
-rw-r--r--drivers/usb/otg/twl6030-usb.c56
-rw-r--r--drivers/usb/otg/ulpi_viewport.c80
-rw-r--r--drivers/usb/serial/ch341.c1
-rw-r--r--drivers/usb/serial/ftdi_sio.c4
-rw-r--r--drivers/usb/serial/ftdi_sio_ids.h1
-rw-r--r--drivers/usb/serial/io_edgeport.c3
-rw-r--r--drivers/usb/serial/keyspan.c10
-rw-r--r--drivers/usb/serial/keyspan_pda.c15
-rw-r--r--drivers/usb/serial/kobil_sct.c2
-rw-r--r--drivers/usb/serial/mct_u232.c107
-rw-r--r--drivers/usb/serial/mos7720.c3
-rw-r--r--drivers/usb/serial/opticon.c156
-rw-r--r--drivers/usb/serial/option.c3
-rw-r--r--drivers/usb/serial/sierra.c16
-rw-r--r--drivers/usb/serial/usb-serial.c5
-rw-r--r--drivers/usb/serial/usb_wwan.c43
-rw-r--r--drivers/usb/storage/Kconfig10
-rw-r--r--drivers/usb/storage/Makefile2
-rw-r--r--drivers/usb/storage/realtek_cr.c675
-rw-r--r--drivers/usb/storage/sierra_ms.c2
-rw-r--r--drivers/usb/storage/unusual_realtek.h41
-rw-r--r--drivers/usb/storage/usual-tables.c1
-rw-r--r--drivers/usb/wusbcore/rh.c6
-rw-r--r--drivers/usb/wusbcore/wusbhc.c2
-rw-r--r--drivers/usb/wusbcore/wusbhc.h2
110 files changed, 8104 insertions, 3589 deletions
diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c
index 99ac70e32556..b268e9fccb47 100644
--- a/drivers/usb/atm/ueagle-atm.c
+++ b/drivers/usb/atm/ueagle-atm.c
@@ -168,7 +168,6 @@ struct uea_softc {
union cmv_dsc cmv_dsc;
struct work_struct task;
- struct workqueue_struct *work_q;
u16 pageno;
u16 ovl;
@@ -1879,7 +1878,7 @@ static int uea_start_reset(struct uea_softc *sc)
/* start loading DSP */
sc->pageno = 0;
sc->ovl = 0;
- queue_work(sc->work_q, &sc->task);
+ schedule_work(&sc->task);
/* wait for modem ready CMV */
ret = wait_cmv_ack(sc);
@@ -2091,14 +2090,14 @@ static void uea_schedule_load_page_e1(struct uea_softc *sc,
{
sc->pageno = intr->e1_bSwapPageNo;
sc->ovl = intr->e1_bOvl >> 4 | intr->e1_bOvl << 4;
- queue_work(sc->work_q, &sc->task);
+ schedule_work(&sc->task);
}
static void uea_schedule_load_page_e4(struct uea_softc *sc,
struct intr_pkt *intr)
{
sc->pageno = intr->e4_bSwapPageNo;
- queue_work(sc->work_q, &sc->task);
+ schedule_work(&sc->task);
}
/*
@@ -2170,13 +2169,6 @@ static int uea_boot(struct uea_softc *sc)
init_waitqueue_head(&sc->sync_q);
- sc->work_q = create_workqueue("ueagle-dsp");
- if (!sc->work_q) {
- uea_err(INS_TO_USBDEV(sc), "cannot allocate workqueue\n");
- uea_leaves(INS_TO_USBDEV(sc));
- return -ENOMEM;
- }
-
if (UEA_CHIP_VERSION(sc) == ADI930)
load_XILINX_firmware(sc);
@@ -2225,7 +2217,6 @@ err1:
sc->urb_int = NULL;
kfree(intr);
err0:
- destroy_workqueue(sc->work_q);
uea_leaves(INS_TO_USBDEV(sc));
return -ENOMEM;
}
@@ -2246,8 +2237,8 @@ static void uea_stop(struct uea_softc *sc)
kfree(sc->urb_int->transfer_buffer);
usb_free_urb(sc->urb_int);
- /* stop any pending boot process, when no one can schedule work */
- destroy_workqueue(sc->work_q);
+ /* flush the work item, when no one can schedule it */
+ flush_work_sync(&sc->task);
if (sc->dsp_firm)
release_firmware(sc->dsp_firm);
diff --git a/drivers/usb/core/buffer.c b/drivers/usb/core/buffer.c
index 2c6965484fe8..b0585e623ba9 100644
--- a/drivers/usb/core/buffer.c
+++ b/drivers/usb/core/buffer.c
@@ -10,7 +10,7 @@
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/mm.h>
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/dma-mapping.h>
#include <linux/dmapool.h>
#include <linux/usb.h>
@@ -22,7 +22,7 @@
*/
/* FIXME tune these based on pool statistics ... */
-static const size_t pool_max [HCD_BUFFER_POOLS] = {
+static const size_t pool_max[HCD_BUFFER_POOLS] = {
/* platforms without dma-friendly caches might need to
* prevent cacheline sharing...
*/
@@ -51,7 +51,7 @@ static const size_t pool_max [HCD_BUFFER_POOLS] = {
int hcd_buffer_create(struct usb_hcd *hcd)
{
char name[16];
- int i, size;
+ int i, size;
if (!hcd->self.controller->dma_mask &&
!(hcd->driver->flags & HCD_LOCAL_MEM))
@@ -64,7 +64,7 @@ int hcd_buffer_create(struct usb_hcd *hcd)
snprintf(name, sizeof name, "buffer-%d", size);
hcd->pool[i] = dma_pool_create(name, hcd->self.controller,
size, size, 0);
- if (!hcd->pool [i]) {
+ if (!hcd->pool[i]) {
hcd_buffer_destroy(hcd);
return -ENOMEM;
}
@@ -99,14 +99,14 @@ void hcd_buffer_destroy(struct usb_hcd *hcd)
*/
void *hcd_buffer_alloc(
- struct usb_bus *bus,
+ struct usb_bus *bus,
size_t size,
gfp_t mem_flags,
dma_addr_t *dma
)
{
struct usb_hcd *hcd = bus_to_hcd(bus);
- int i;
+ int i;
/* some USB hosts just use PIO */
if (!bus->controller->dma_mask &&
@@ -116,21 +116,21 @@ void *hcd_buffer_alloc(
}
for (i = 0; i < HCD_BUFFER_POOLS; i++) {
- if (size <= pool_max [i])
- return dma_pool_alloc(hcd->pool [i], mem_flags, dma);
+ if (size <= pool_max[i])
+ return dma_pool_alloc(hcd->pool[i], mem_flags, dma);
}
return dma_alloc_coherent(hcd->self.controller, size, dma, mem_flags);
}
void hcd_buffer_free(
- struct usb_bus *bus,
+ struct usb_bus *bus,
size_t size,
- void *addr,
+ void *addr,
dma_addr_t dma
)
{
struct usb_hcd *hcd = bus_to_hcd(bus);
- int i;
+ int i;
if (!addr)
return;
@@ -142,8 +142,8 @@ void hcd_buffer_free(
}
for (i = 0; i < HCD_BUFFER_POOLS; i++) {
- if (size <= pool_max [i]) {
- dma_pool_free(hcd->pool [i], addr, dma);
+ if (size <= pool_max[i]) {
+ dma_pool_free(hcd->pool[i], addr, dma);
return;
}
}
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index fca61720b873..38072e4e74bd 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -1659,6 +1659,11 @@ static int usb_runtime_suspend(struct device *dev)
return -EAGAIN;
status = usb_suspend_both(udev, PMSG_AUTO_SUSPEND);
+ /* The PM core reacts badly unless the return code is 0,
+ * -EAGAIN, or -EBUSY, so always return -EBUSY on an error.
+ */
+ if (status != 0)
+ return -EBUSY;
return status;
}
diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c
index 64a035ba2eab..ce22f4a84ed0 100644
--- a/drivers/usb/core/hcd-pci.c
+++ b/drivers/usb/core/hcd-pci.c
@@ -192,13 +192,13 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
"Found HC with no IRQ. Check BIOS/PCI %s setup!\n",
pci_name(dev));
retval = -ENODEV;
- goto err1;
+ goto disable_pci;
}
hcd = usb_create_hcd(driver, &dev->dev, pci_name(dev));
if (!hcd) {
retval = -ENOMEM;
- goto err1;
+ goto disable_pci;
}
if (driver->flags & HCD_MEMORY) {
@@ -209,13 +209,13 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
driver->description)) {
dev_dbg(&dev->dev, "controller already in use\n");
retval = -EBUSY;
- goto err2;
+ goto clear_companion;
}
hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len);
if (hcd->regs == NULL) {
dev_dbg(&dev->dev, "error mapping memory\n");
retval = -EFAULT;
- goto err3;
+ goto release_mem_region;
}
} else {
@@ -236,7 +236,7 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
if (region == PCI_ROM_RESOURCE) {
dev_dbg(&dev->dev, "no i/o regions available\n");
retval = -EBUSY;
- goto err2;
+ goto clear_companion;
}
}
@@ -244,24 +244,24 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
retval = usb_add_hcd(hcd, dev->irq, IRQF_DISABLED | IRQF_SHARED);
if (retval != 0)
- goto err4;
+ goto unmap_registers;
set_hs_companion(dev, hcd);
if (pci_dev_run_wake(dev))
pm_runtime_put_noidle(&dev->dev);
return retval;
- err4:
+unmap_registers:
if (driver->flags & HCD_MEMORY) {
iounmap(hcd->regs);
- err3:
+release_mem_region:
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
} else
release_region(hcd->rsrc_start, hcd->rsrc_len);
- err2:
+clear_companion:
clear_hs_companion(dev, hcd);
usb_put_hcd(hcd);
- err1:
+disable_pci:
pci_disable_device(dev);
dev_err(&dev->dev, "init %s fail, %d\n", pci_name(dev), retval);
return retval;
@@ -363,11 +363,17 @@ static int check_root_hub_suspended(struct device *dev)
struct pci_dev *pci_dev = to_pci_dev(dev);
struct usb_hcd *hcd = pci_get_drvdata(pci_dev);
- if (!(hcd->state == HC_STATE_SUSPENDED ||
- hcd->state == HC_STATE_HALT)) {
+ if (HCD_RH_RUNNING(hcd)) {
dev_warn(dev, "Root hub is not suspended\n");
return -EBUSY;
}
+ if (hcd->shared_hcd) {
+ hcd = hcd->shared_hcd;
+ if (HCD_RH_RUNNING(hcd)) {
+ dev_warn(dev, "Secondary root hub is not suspended\n");
+ return -EBUSY;
+ }
+ }
return 0;
}
@@ -386,17 +392,22 @@ static int suspend_common(struct device *dev, bool do_wakeup)
if (retval)
return retval;
- if (hcd->driver->pci_suspend) {
+ if (hcd->driver->pci_suspend && !HCD_DEAD(hcd)) {
/* Optimization: Don't suspend if a root-hub wakeup is
* pending and it would cause the HCD to wake up anyway.
*/
if (do_wakeup && HCD_WAKEUP_PENDING(hcd))
return -EBUSY;
+ if (do_wakeup && hcd->shared_hcd &&
+ HCD_WAKEUP_PENDING(hcd->shared_hcd))
+ return -EBUSY;
retval = hcd->driver->pci_suspend(hcd, do_wakeup);
suspend_report_result(hcd->driver->pci_suspend, retval);
/* Check again in case wakeup raced with pci_suspend */
- if (retval == 0 && do_wakeup && HCD_WAKEUP_PENDING(hcd)) {
+ if ((retval == 0 && do_wakeup && HCD_WAKEUP_PENDING(hcd)) ||
+ (retval == 0 && do_wakeup && hcd->shared_hcd &&
+ HCD_WAKEUP_PENDING(hcd->shared_hcd))) {
if (hcd->driver->pci_resume)
hcd->driver->pci_resume(hcd, false);
retval = -EBUSY;
@@ -427,7 +438,9 @@ static int resume_common(struct device *dev, int event)
struct usb_hcd *hcd = pci_get_drvdata(pci_dev);
int retval;
- if (hcd->state != HC_STATE_SUSPENDED) {
+ if (HCD_RH_RUNNING(hcd) ||
+ (hcd->shared_hcd &&
+ HCD_RH_RUNNING(hcd->shared_hcd))) {
dev_dbg(dev, "can't resume, not suspended!\n");
return 0;
}
@@ -441,8 +454,10 @@ static int resume_common(struct device *dev, int event)
pci_set_master(pci_dev);
clear_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
+ if (hcd->shared_hcd)
+ clear_bit(HCD_FLAG_SAW_IRQ, &hcd->shared_hcd->flags);
- if (hcd->driver->pci_resume) {
+ if (hcd->driver->pci_resume && !HCD_DEAD(hcd)) {
if (event != PM_EVENT_AUTO_RESUME)
wait_for_companions(pci_dev, hcd);
@@ -450,6 +465,8 @@ static int resume_common(struct device *dev, int event)
event == PM_EVENT_RESTORE);
if (retval) {
dev_err(dev, "PCI post-resume error %d!\n", retval);
+ if (hcd->shared_hcd)
+ usb_hc_died(hcd->shared_hcd);
usb_hc_died(hcd);
}
}
@@ -475,10 +492,11 @@ static int hcd_pci_suspend_noirq(struct device *dev)
pci_save_state(pci_dev);
- /* If the root hub is HALTed rather than SUSPENDed,
- * disallow remote wakeup.
+ /* If the root hub is dead rather than suspended, disallow remote
+ * wakeup. usb_hc_died() should ensure that both hosts are marked as
+ * dying, so we only need to check the primary roothub.
*/
- if (hcd->state == HC_STATE_HALT)
+ if (HCD_DEAD(hcd))
device_set_wakeup_enable(dev, 0);
dev_dbg(dev, "wakeup: %d\n", device_may_wakeup(dev));
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index e935f71d7a34..02b4dbfa488a 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -297,7 +297,7 @@ static const u8 ss_rh_config_descriptor[] = {
/* one configuration */
0x09, /* __u8 bLength; */
0x02, /* __u8 bDescriptorType; Configuration */
- 0x19, 0x00, /* __le16 wTotalLength; FIXME */
+ 0x1f, 0x00, /* __le16 wTotalLength; */
0x01, /* __u8 bNumInterfaces; (1) */
0x01, /* __u8 bConfigurationValue; */
0x00, /* __u8 iConfiguration; */
@@ -327,11 +327,14 @@ static const u8 ss_rh_config_descriptor[] = {
/* __le16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8)
* see hub.c:hub_configure() for details. */
(USB_MAXCHILDREN + 1 + 7) / 8, 0x00,
- 0x0c /* __u8 ep_bInterval; (256ms -- usb 2.0 spec) */
- /*
- * All 3.0 hubs should have an endpoint companion descriptor,
- * but we're ignoring that for now. FIXME?
- */
+ 0x0c, /* __u8 ep_bInterval; (256ms -- usb 2.0 spec) */
+
+ /* one SuperSpeed endpoint companion descriptor */
+ 0x06, /* __u8 ss_bLength */
+ 0x30, /* __u8 ss_bDescriptorType; SuperSpeed EP Companion */
+ 0x00, /* __u8 ss_bMaxBurst; allows 1 TX between ACKs */
+ 0x00, /* __u8 ss_bmAttributes; 1 packet per service interval */
+ 0x02, 0x00 /* __le16 ss_wBytesPerInterval; 15 bits for max 15 ports */
};
/*-------------------------------------------------------------------------*/
@@ -504,7 +507,7 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
switch (wValue & 0xff00) {
case USB_DT_DEVICE << 8:
- switch (hcd->driver->flags & HCD_MASK) {
+ switch (hcd->speed) {
case HCD_USB3:
bufp = usb3_rh_dev_descriptor;
break;
@@ -522,7 +525,7 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
patch_protocol = 1;
break;
case USB_DT_CONFIG << 8:
- switch (hcd->driver->flags & HCD_MASK) {
+ switch (hcd->speed) {
case HCD_USB3:
bufp = ss_rh_config_descriptor;
len = sizeof ss_rh_config_descriptor;
@@ -983,7 +986,7 @@ static int register_root_hub(struct usb_hcd *hcd)
spin_unlock_irq (&hcd_root_hub_lock);
/* Did the HC die before the root hub was registered? */
- if (hcd->state == HC_STATE_HALT)
+ if (HCD_DEAD(hcd) || hcd->state == HC_STATE_HALT)
usb_hc_died (hcd); /* This time clean up */
}
@@ -1089,13 +1092,10 @@ int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb)
* Check the host controller's state and add the URB to the
* endpoint's queue.
*/
- switch (hcd->state) {
- case HC_STATE_RUNNING:
- case HC_STATE_RESUMING:
+ if (HCD_RH_RUNNING(hcd)) {
urb->unlinked = 0;
list_add_tail(&urb->urb_list, &urb->ep->urb_list);
- break;
- default:
+ } else {
rc = -ESHUTDOWN;
goto done;
}
@@ -1153,6 +1153,8 @@ int usb_hcd_check_unlink_urb(struct usb_hcd *hcd, struct urb *urb,
dev_warn(hcd->self.controller, "Unlink after no-IRQ? "
"Controller is probably using the wrong IRQ.\n");
set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
+ if (hcd->shared_hcd)
+ set_bit(HCD_FLAG_SAW_IRQ, &hcd->shared_hcd->flags);
}
return 0;
@@ -1262,7 +1264,7 @@ static void hcd_free_coherent(struct usb_bus *bus, dma_addr_t *dma_handle,
*dma_handle = 0;
}
-void unmap_urb_setup_for_dma(struct usb_hcd *hcd, struct urb *urb)
+void usb_hcd_unmap_urb_setup_for_dma(struct usb_hcd *hcd, struct urb *urb)
{
if (urb->transfer_flags & URB_SETUP_MAP_SINGLE)
dma_unmap_single(hcd->self.controller,
@@ -1279,13 +1281,21 @@ void unmap_urb_setup_for_dma(struct usb_hcd *hcd, struct urb *urb)
/* Make it safe to call this routine more than once */
urb->transfer_flags &= ~(URB_SETUP_MAP_SINGLE | URB_SETUP_MAP_LOCAL);
}
-EXPORT_SYMBOL_GPL(unmap_urb_setup_for_dma);
+EXPORT_SYMBOL_GPL(usb_hcd_unmap_urb_setup_for_dma);
-void unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb)
+static void unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb)
+{
+ if (hcd->driver->unmap_urb_for_dma)
+ hcd->driver->unmap_urb_for_dma(hcd, urb);
+ else
+ usb_hcd_unmap_urb_for_dma(hcd, urb);
+}
+
+void usb_hcd_unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb)
{
enum dma_data_direction dir;
- unmap_urb_setup_for_dma(hcd, urb);
+ usb_hcd_unmap_urb_setup_for_dma(hcd, urb);
dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
if (urb->transfer_flags & URB_DMA_MAP_SG)
@@ -1314,11 +1324,20 @@ void unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb)
urb->transfer_flags &= ~(URB_DMA_MAP_SG | URB_DMA_MAP_PAGE |
URB_DMA_MAP_SINGLE | URB_MAP_LOCAL);
}
-EXPORT_SYMBOL_GPL(unmap_urb_for_dma);
+EXPORT_SYMBOL_GPL(usb_hcd_unmap_urb_for_dma);
static int map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
gfp_t mem_flags)
{
+ if (hcd->driver->map_urb_for_dma)
+ return hcd->driver->map_urb_for_dma(hcd, urb, mem_flags);
+ else
+ return usb_hcd_map_urb_for_dma(hcd, urb, mem_flags);
+}
+
+int usb_hcd_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
+ gfp_t mem_flags)
+{
enum dma_data_direction dir;
int ret = 0;
@@ -1410,10 +1429,11 @@ static int map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
}
if (ret && (urb->transfer_flags & (URB_SETUP_MAP_SINGLE |
URB_SETUP_MAP_LOCAL)))
- unmap_urb_for_dma(hcd, urb);
+ usb_hcd_unmap_urb_for_dma(hcd, urb);
}
return ret;
}
+EXPORT_SYMBOL_GPL(usb_hcd_map_urb_for_dma);
/*-------------------------------------------------------------------------*/
@@ -1913,7 +1933,7 @@ int usb_hcd_get_frame_number (struct usb_device *udev)
{
struct usb_hcd *hcd = bus_to_hcd(udev->bus);
- if (!HC_IS_RUNNING (hcd->state))
+ if (!HCD_RH_RUNNING(hcd))
return -ESHUTDOWN;
return hcd->driver->get_frame_number (hcd);
}
@@ -1930,9 +1950,15 @@ int hcd_bus_suspend(struct usb_device *rhdev, pm_message_t msg)
dev_dbg(&rhdev->dev, "bus %s%s\n",
(msg.event & PM_EVENT_AUTO ? "auto-" : ""), "suspend");
+ if (HCD_DEAD(hcd)) {
+ dev_dbg(&rhdev->dev, "skipped %s of dead bus\n", "suspend");
+ return 0;
+ }
+
if (!hcd->driver->bus_suspend) {
status = -ENOENT;
} else {
+ clear_bit(HCD_FLAG_RH_RUNNING, &hcd->flags);
hcd->state = HC_STATE_QUIESCING;
status = hcd->driver->bus_suspend(hcd);
}
@@ -1940,7 +1966,12 @@ int hcd_bus_suspend(struct usb_device *rhdev, pm_message_t msg)
usb_set_device_state(rhdev, USB_STATE_SUSPENDED);
hcd->state = HC_STATE_SUSPENDED;
} else {
- hcd->state = old_state;
+ spin_lock_irq(&hcd_root_hub_lock);
+ if (!HCD_DEAD(hcd)) {
+ set_bit(HCD_FLAG_RH_RUNNING, &hcd->flags);
+ hcd->state = old_state;
+ }
+ spin_unlock_irq(&hcd_root_hub_lock);
dev_dbg(&rhdev->dev, "bus %s fail, err %d\n",
"suspend", status);
}
@@ -1955,9 +1986,13 @@ int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg)
dev_dbg(&rhdev->dev, "usb %s%s\n",
(msg.event & PM_EVENT_AUTO ? "auto-" : ""), "resume");
+ if (HCD_DEAD(hcd)) {
+ dev_dbg(&rhdev->dev, "skipped %s of dead bus\n", "resume");
+ return 0;
+ }
if (!hcd->driver->bus_resume)
return -ENOENT;
- if (hcd->state == HC_STATE_RUNNING)
+ if (HCD_RH_RUNNING(hcd))
return 0;
hcd->state = HC_STATE_RESUMING;
@@ -1966,10 +2001,15 @@ int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg)
if (status == 0) {
/* TRSMRCY = 10 msec */
msleep(10);
- usb_set_device_state(rhdev, rhdev->actconfig
- ? USB_STATE_CONFIGURED
- : USB_STATE_ADDRESS);
- hcd->state = HC_STATE_RUNNING;
+ spin_lock_irq(&hcd_root_hub_lock);
+ if (!HCD_DEAD(hcd)) {
+ usb_set_device_state(rhdev, rhdev->actconfig
+ ? USB_STATE_CONFIGURED
+ : USB_STATE_ADDRESS);
+ set_bit(HCD_FLAG_RH_RUNNING, &hcd->flags);
+ hcd->state = HC_STATE_RUNNING;
+ }
+ spin_unlock_irq(&hcd_root_hub_lock);
} else {
hcd->state = old_state;
dev_dbg(&rhdev->dev, "bus %s fail, err %d\n",
@@ -2080,12 +2120,14 @@ irqreturn_t usb_hcd_irq (int irq, void *__hcd)
*/
local_irq_save(flags);
- if (unlikely(hcd->state == HC_STATE_HALT || !HCD_HW_ACCESSIBLE(hcd))) {
+ if (unlikely(HCD_DEAD(hcd) || !HCD_HW_ACCESSIBLE(hcd))) {
rc = IRQ_NONE;
} else if (hcd->driver->irq(hcd) == IRQ_NONE) {
rc = IRQ_NONE;
} else {
set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
+ if (hcd->shared_hcd)
+ set_bit(HCD_FLAG_SAW_IRQ, &hcd->shared_hcd->flags);
if (unlikely(hcd->state == HC_STATE_HALT))
usb_hc_died(hcd);
@@ -2105,7 +2147,9 @@ EXPORT_SYMBOL_GPL(usb_hcd_irq);
*
* This is called by bus glue to report a USB host controller that died
* while operations may still have been pending. It's called automatically
- * by the PCI glue, so only glue for non-PCI busses should need to call it.
+ * by the PCI glue, so only glue for non-PCI busses should need to call it.
+ *
+ * Only call this function with the primary HCD.
*/
void usb_hc_died (struct usb_hcd *hcd)
{
@@ -2114,6 +2158,8 @@ void usb_hc_died (struct usb_hcd *hcd)
dev_err (hcd->self.controller, "HC died; cleaning up\n");
spin_lock_irqsave (&hcd_root_hub_lock, flags);
+ clear_bit(HCD_FLAG_RH_RUNNING, &hcd->flags);
+ set_bit(HCD_FLAG_DEAD, &hcd->flags);
if (hcd->rh_registered) {
clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
@@ -2122,17 +2168,31 @@ void usb_hc_died (struct usb_hcd *hcd)
USB_STATE_NOTATTACHED);
usb_kick_khubd (hcd->self.root_hub);
}
+ if (usb_hcd_is_primary_hcd(hcd) && hcd->shared_hcd) {
+ hcd = hcd->shared_hcd;
+ if (hcd->rh_registered) {
+ clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
+
+ /* make khubd clean up old urbs and devices */
+ usb_set_device_state(hcd->self.root_hub,
+ USB_STATE_NOTATTACHED);
+ usb_kick_khubd(hcd->self.root_hub);
+ }
+ }
spin_unlock_irqrestore (&hcd_root_hub_lock, flags);
+ /* Make sure that the other roothub is also deallocated. */
}
EXPORT_SYMBOL_GPL (usb_hc_died);
/*-------------------------------------------------------------------------*/
/**
- * usb_create_hcd - create and initialize an HCD structure
+ * usb_create_shared_hcd - create and initialize an HCD structure
* @driver: HC driver that will use this hcd
* @dev: device for this HC, stored in hcd->self.controller
* @bus_name: value to store in hcd->self.bus_name
+ * @primary_hcd: a pointer to the usb_hcd structure that is sharing the
+ * PCI device. Only allocate certain resources for the primary HCD
* Context: !in_interrupt()
*
* Allocate a struct usb_hcd, with extra space at the end for the
@@ -2141,8 +2201,9 @@ EXPORT_SYMBOL_GPL (usb_hc_died);
*
* If memory is unavailable, returns NULL.
*/
-struct usb_hcd *usb_create_hcd (const struct hc_driver *driver,
- struct device *dev, const char *bus_name)
+struct usb_hcd *usb_create_shared_hcd(const struct hc_driver *driver,
+ struct device *dev, const char *bus_name,
+ struct usb_hcd *primary_hcd)
{
struct usb_hcd *hcd;
@@ -2151,7 +2212,24 @@ struct usb_hcd *usb_create_hcd (const struct hc_driver *driver,
dev_dbg (dev, "hcd alloc failed\n");
return NULL;
}
- dev_set_drvdata(dev, hcd);
+ if (primary_hcd == NULL) {
+ hcd->bandwidth_mutex = kmalloc(sizeof(*hcd->bandwidth_mutex),
+ GFP_KERNEL);
+ if (!hcd->bandwidth_mutex) {
+ kfree(hcd);
+ dev_dbg(dev, "hcd bandwidth mutex alloc failed\n");
+ return NULL;
+ }
+ mutex_init(hcd->bandwidth_mutex);
+ dev_set_drvdata(dev, hcd);
+ } else {
+ hcd->bandwidth_mutex = primary_hcd->bandwidth_mutex;
+ hcd->primary_hcd = primary_hcd;
+ primary_hcd->primary_hcd = primary_hcd;
+ hcd->shared_hcd = primary_hcd;
+ primary_hcd->shared_hcd = hcd;
+ }
+
kref_init(&hcd->kref);
usb_bus_init(&hcd->self);
@@ -2165,19 +2243,53 @@ struct usb_hcd *usb_create_hcd (const struct hc_driver *driver,
#ifdef CONFIG_USB_SUSPEND
INIT_WORK(&hcd->wakeup_work, hcd_resume_work);
#endif
- mutex_init(&hcd->bandwidth_mutex);
hcd->driver = driver;
+ hcd->speed = driver->flags & HCD_MASK;
hcd->product_desc = (driver->product_desc) ? driver->product_desc :
"USB Host Controller";
return hcd;
}
+EXPORT_SYMBOL_GPL(usb_create_shared_hcd);
+
+/**
+ * usb_create_hcd - create and initialize an HCD structure
+ * @driver: HC driver that will use this hcd
+ * @dev: device for this HC, stored in hcd->self.controller
+ * @bus_name: value to store in hcd->self.bus_name
+ * Context: !in_interrupt()
+ *
+ * Allocate a struct usb_hcd, with extra space at the end for the
+ * HC driver's private data. Initialize the generic members of the
+ * hcd structure.
+ *
+ * If memory is unavailable, returns NULL.
+ */
+struct usb_hcd *usb_create_hcd(const struct hc_driver *driver,
+ struct device *dev, const char *bus_name)
+{
+ return usb_create_shared_hcd(driver, dev, bus_name, NULL);
+}
EXPORT_SYMBOL_GPL(usb_create_hcd);
+/*
+ * Roothubs that share one PCI device must also share the bandwidth mutex.
+ * Don't deallocate the bandwidth_mutex until the last shared usb_hcd is
+ * deallocated.
+ *
+ * Make sure to only deallocate the bandwidth_mutex when the primary HCD is
+ * freed. When hcd_release() is called for the non-primary HCD, set the
+ * primary_hcd's shared_hcd pointer to null (since the non-primary HCD will be
+ * freed shortly).
+ */
static void hcd_release (struct kref *kref)
{
struct usb_hcd *hcd = container_of (kref, struct usb_hcd, kref);
+ if (usb_hcd_is_primary_hcd(hcd))
+ kfree(hcd->bandwidth_mutex);
+ else
+ hcd->shared_hcd->shared_hcd = NULL;
kfree(hcd);
}
@@ -2196,6 +2308,54 @@ void usb_put_hcd (struct usb_hcd *hcd)
}
EXPORT_SYMBOL_GPL(usb_put_hcd);
+int usb_hcd_is_primary_hcd(struct usb_hcd *hcd)
+{
+ if (!hcd->primary_hcd)
+ return 1;
+ return hcd == hcd->primary_hcd;
+}
+EXPORT_SYMBOL_GPL(usb_hcd_is_primary_hcd);
+
+static int usb_hcd_request_irqs(struct usb_hcd *hcd,
+ unsigned int irqnum, unsigned long irqflags)
+{
+ int retval;
+
+ if (hcd->driver->irq) {
+
+ /* IRQF_DISABLED doesn't work as advertised when used together
+ * with IRQF_SHARED. As usb_hcd_irq() will always disable
+ * interrupts we can remove it here.
+ */
+ if (irqflags & IRQF_SHARED)
+ irqflags &= ~IRQF_DISABLED;
+
+ snprintf(hcd->irq_descr, sizeof(hcd->irq_descr), "%s:usb%d",
+ hcd->driver->description, hcd->self.busnum);
+ retval = request_irq(irqnum, &usb_hcd_irq, irqflags,
+ hcd->irq_descr, hcd);
+ if (retval != 0) {
+ dev_err(hcd->self.controller,
+ "request interrupt %d failed\n",
+ irqnum);
+ return retval;
+ }
+ hcd->irq = irqnum;
+ dev_info(hcd->self.controller, "irq %d, %s 0x%08llx\n", irqnum,
+ (hcd->driver->flags & HCD_MEMORY) ?
+ "io mem" : "io base",
+ (unsigned long long)hcd->rsrc_start);
+ } else {
+ hcd->irq = -1;
+ if (hcd->rsrc_start)
+ dev_info(hcd->self.controller, "%s 0x%08llx\n",
+ (hcd->driver->flags & HCD_MEMORY) ?
+ "io mem" : "io base",
+ (unsigned long long)hcd->rsrc_start);
+ }
+ return 0;
+}
+
/**
* usb_add_hcd - finish generic HCD structure initialization and register
* @hcd: the usb_hcd structure to initialize
@@ -2236,7 +2396,7 @@ int usb_add_hcd(struct usb_hcd *hcd,
}
hcd->self.root_hub = rhdev;
- switch (hcd->driver->flags & HCD_MASK) {
+ switch (hcd->speed) {
case HCD_USB11:
rhdev->speed = USB_SPEED_FULL;
break;
@@ -2256,6 +2416,12 @@ int usb_add_hcd(struct usb_hcd *hcd,
*/
device_init_wakeup(&rhdev->dev, 1);
+ /* HCD_FLAG_RH_RUNNING doesn't matter until the root hub is
+ * registered. But since the controller can die at any time,
+ * let's initialize the flag before touching the hardware.
+ */
+ set_bit(HCD_FLAG_RH_RUNNING, &hcd->flags);
+
/* "reset" is misnamed; its role is now one-time init. the controller
* should already have been reset (and boot firmware kicked off etc).
*/
@@ -2271,38 +2437,15 @@ int usb_add_hcd(struct usb_hcd *hcd,
dev_dbg(hcd->self.controller, "supports USB remote wakeup\n");
/* enable irqs just before we start the controller */
- if (hcd->driver->irq) {
-
- /* IRQF_DISABLED doesn't work as advertised when used together
- * with IRQF_SHARED. As usb_hcd_irq() will always disable
- * interrupts we can remove it here.
- */
- if (irqflags & IRQF_SHARED)
- irqflags &= ~IRQF_DISABLED;
-
- snprintf(hcd->irq_descr, sizeof(hcd->irq_descr), "%s:usb%d",
- hcd->driver->description, hcd->self.busnum);
- if ((retval = request_irq(irqnum, &usb_hcd_irq, irqflags,
- hcd->irq_descr, hcd)) != 0) {
- dev_err(hcd->self.controller,
- "request interrupt %d failed\n", irqnum);
+ if (usb_hcd_is_primary_hcd(hcd)) {
+ retval = usb_hcd_request_irqs(hcd, irqnum, irqflags);
+ if (retval)
goto err_request_irq;
- }
- hcd->irq = irqnum;
- dev_info(hcd->self.controller, "irq %d, %s 0x%08llx\n", irqnum,
- (hcd->driver->flags & HCD_MEMORY) ?
- "io mem" : "io base",
- (unsigned long long)hcd->rsrc_start);
- } else {
- hcd->irq = -1;
- if (hcd->rsrc_start)
- dev_info(hcd->self.controller, "%s 0x%08llx\n",
- (hcd->driver->flags & HCD_MEMORY) ?
- "io mem" : "io base",
- (unsigned long long)hcd->rsrc_start);
}
- if ((retval = hcd->driver->start(hcd)) < 0) {
+ hcd->state = HC_STATE_RUNNING;
+ retval = hcd->driver->start(hcd);
+ if (retval < 0) {
dev_err(hcd->self.controller, "startup error %d\n", retval);
goto err_hcd_driver_start;
}
@@ -2323,6 +2466,7 @@ int usb_add_hcd(struct usb_hcd *hcd,
return retval;
error_create_attr_group:
+ clear_bit(HCD_FLAG_RH_RUNNING, &hcd->flags);
if (HC_IS_RUNNING(hcd->state))
hcd->state = HC_STATE_QUIESCING;
spin_lock_irq(&hcd_root_hub_lock);
@@ -2344,7 +2488,7 @@ err_register_root_hub:
clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
del_timer_sync(&hcd->rh_timer);
err_hcd_driver_start:
- if (hcd->irq >= 0)
+ if (usb_hcd_is_primary_hcd(hcd) && hcd->irq >= 0)
free_irq(irqnum, hcd);
err_request_irq:
err_hcd_driver_setup:
@@ -2375,6 +2519,7 @@ void usb_remove_hcd(struct usb_hcd *hcd)
usb_get_dev(rhdev);
sysfs_remove_group(&rhdev->dev.kobj, &usb_bus_attr_group);
+ clear_bit(HCD_FLAG_RH_RUNNING, &hcd->flags);
if (HC_IS_RUNNING (hcd->state))
hcd->state = HC_STATE_QUIESCING;
@@ -2407,8 +2552,10 @@ void usb_remove_hcd(struct usb_hcd *hcd)
clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
del_timer_sync(&hcd->rh_timer);
- if (hcd->irq >= 0)
- free_irq(hcd->irq, hcd);
+ if (usb_hcd_is_primary_hcd(hcd)) {
+ if (hcd->irq >= 0)
+ free_irq(hcd->irq, hcd);
+ }
usb_put_dev(hcd->self.root_hub);
usb_deregister_bus(&hcd->self);
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 19d3435e6140..564eaa5525d7 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -82,6 +82,10 @@ struct usb_hub {
void **port_owners;
};
+static inline int hub_is_superspeed(struct usb_device *hdev)
+{
+ return (hdev->descriptor.bDeviceProtocol == 3);
+}
/* Protect struct usb_device->state and ->children members
* Note: Both are also protected by ->dev.sem, except that ->state can
@@ -151,14 +155,14 @@ EXPORT_SYMBOL_GPL(ehci_cf_port_reset_rwsem);
static int usb_reset_and_verify_device(struct usb_device *udev);
-static inline char *portspeed(int portstatus)
+static inline char *portspeed(struct usb_hub *hub, int portstatus)
{
+ if (hub_is_superspeed(hub->hdev))
+ return "5.0 Gb/s";
if (portstatus & USB_PORT_STAT_HIGH_SPEED)
return "480 Mb/s";
else if (portstatus & USB_PORT_STAT_LOW_SPEED)
return "1.5 Mb/s";
- else if (portstatus & USB_PORT_STAT_SUPER_SPEED)
- return "5.0 Gb/s";
else
return "12 Mb/s";
}
@@ -172,14 +176,23 @@ static struct usb_hub *hdev_to_hub(struct usb_device *hdev)
}
/* USB 2.0 spec Section 11.24.4.5 */
-static int get_hub_descriptor(struct usb_device *hdev, void *data, int size)
+static int get_hub_descriptor(struct usb_device *hdev, void *data)
{
- int i, ret;
+ int i, ret, size;
+ unsigned dtype;
+
+ if (hub_is_superspeed(hdev)) {
+ dtype = USB_DT_SS_HUB;
+ size = USB_DT_SS_HUB_SIZE;
+ } else {
+ dtype = USB_DT_HUB;
+ size = sizeof(struct usb_hub_descriptor);
+ }
for (i = 0; i < 3; i++) {
ret = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0),
USB_REQ_GET_DESCRIPTOR, USB_DIR_IN | USB_RT_HUB,
- USB_DT_HUB << 8, 0, data, size,
+ dtype << 8, 0, data, size,
USB_CTRL_GET_TIMEOUT);
if (ret >= (USB_DT_HUB_NONVAR_SIZE + 2))
return ret;
@@ -365,6 +378,16 @@ static int hub_port_status(struct usb_hub *hub, int port1,
} else {
*status = le16_to_cpu(hub->status->port.wPortStatus);
*change = le16_to_cpu(hub->status->port.wPortChange);
+
+ if ((hub->hdev->parent != NULL) &&
+ hub_is_superspeed(hub->hdev)) {
+ /* Translate the USB 3 port status */
+ u16 tmp = *status & USB_SS_PORT_STAT_MASK;
+ if (*status & USB_SS_PORT_STAT_POWER)
+ tmp |= USB_PORT_STAT_POWER;
+ *status = tmp;
+ }
+
ret = 0;
}
mutex_unlock(&hub->status_mutex);
@@ -607,7 +630,7 @@ static int hub_port_disable(struct usb_hub *hub, int port1, int set_state)
if (hdev->children[port1-1] && set_state)
usb_set_device_state(hdev->children[port1-1],
USB_STATE_NOTATTACHED);
- if (!hub->error)
+ if (!hub->error && !hub_is_superspeed(hub->hdev))
ret = clear_port_feature(hdev, port1, USB_PORT_FEAT_ENABLE);
if (ret)
dev_err(hub->intfdev, "cannot disable port %d (err = %d)\n",
@@ -616,7 +639,7 @@ static int hub_port_disable(struct usb_hub *hub, int port1, int set_state)
}
/*
- * Disable a port and mark a logical connnect-change event, so that some
+ * Disable a port and mark a logical connect-change event, so that some
* time later khubd will disconnect() any existing usb_device on the port
* and will re-enumerate if there actually is a device attached.
*/
@@ -769,12 +792,8 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
* USB3 protocol ports will automatically transition
* to Enabled state when detect an USB3.0 device attach.
* Do not disable USB3 protocol ports.
- * FIXME: USB3 root hub and external hubs are treated
- * differently here.
*/
- if (hdev->descriptor.bDeviceProtocol != 3 ||
- (!hdev->parent &&
- !(portstatus & USB_PORT_STAT_SUPER_SPEED))) {
+ if (!hub_is_superspeed(hdev)) {
clear_port_feature(hdev, port1,
USB_PORT_FEAT_ENABLE);
portstatus &= ~USB_PORT_STAT_ENABLE;
@@ -795,6 +814,11 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
clear_port_feature(hub->hdev, port1,
USB_PORT_FEAT_C_ENABLE);
}
+ if (portchange & USB_PORT_STAT_C_LINK_STATE) {
+ need_debounce_delay = true;
+ clear_port_feature(hub->hdev, port1,
+ USB_PORT_FEAT_C_PORT_LINK_STATE);
+ }
/* We can forget about a "removed" device when there's a
* physical disconnect or the connect status changes.
@@ -964,12 +988,23 @@ static int hub_configure(struct usb_hub *hub,
goto fail;
}
+ if (hub_is_superspeed(hdev) && (hdev->parent != NULL)) {
+ ret = usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
+ HUB_SET_DEPTH, USB_RT_HUB,
+ hdev->level - 1, 0, NULL, 0,
+ USB_CTRL_SET_TIMEOUT);
+
+ if (ret < 0) {
+ message = "can't set hub depth";
+ goto fail;
+ }
+ }
+
/* Request the entire hub descriptor.
* hub->descriptor can handle USB_MAXCHILDREN ports,
* but the hub can/will return fewer bytes here.
*/
- ret = get_hub_descriptor(hdev, hub->descriptor,
- sizeof(*hub->descriptor));
+ ret = get_hub_descriptor(hdev, hub->descriptor);
if (ret < 0) {
message = "can't read hub descriptor";
goto fail;
@@ -991,12 +1026,14 @@ static int hub_configure(struct usb_hub *hub,
wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics);
- if (wHubCharacteristics & HUB_CHAR_COMPOUND) {
+ /* FIXME for USB 3.0, skip for now */
+ if ((wHubCharacteristics & HUB_CHAR_COMPOUND) &&
+ !(hub_is_superspeed(hdev))) {
int i;
char portstr [USB_MAXCHILDREN + 1];
for (i = 0; i < hdev->maxchild; i++)
- portstr[i] = hub->descriptor->DeviceRemovable
+ portstr[i] = hub->descriptor->u.hs.DeviceRemovable
[((i + 1) / 8)] & (1 << ((i + 1) % 8))
? 'F' : 'R';
portstr[hdev->maxchild] = 0;
@@ -1253,8 +1290,14 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
desc = intf->cur_altsetting;
hdev = interface_to_usbdev(intf);
- /* Hubs have proper suspend/resume support */
- usb_enable_autosuspend(hdev);
+ /* Hubs have proper suspend/resume support. USB 3.0 device suspend is
+ * different from USB 2.0/1.1 device suspend, and unfortunately we
+ * don't support it yet. So leave autosuspend disabled for USB 3.0
+ * external hubs for now. Enable autosuspend for USB 3.0 roothubs,
+ * since that isn't a "real" hub.
+ */
+ if (!hub_is_superspeed(hdev) || !hdev->parent)
+ usb_enable_autosuspend(hdev);
if (hdev->level == MAX_TOPO_LEVEL) {
dev_err(&intf->dev,
@@ -1501,6 +1544,13 @@ void usb_set_device_state(struct usb_device *udev,
EXPORT_SYMBOL_GPL(usb_set_device_state);
/*
+ * Choose a device number.
+ *
+ * Device numbers are used as filenames in usbfs. On USB-1.1 and
+ * USB-2.0 buses they are also used as device addresses, however on
+ * USB-3.0 buses the address is assigned by the controller hardware
+ * and it usually is not the same as the device number.
+ *
* WUSB devices are simple: they have no hubs behind, so the mapping
* device <-> virtual port number becomes 1:1. Why? to simplify the
* life of the device connection logic in
@@ -1522,7 +1572,7 @@ EXPORT_SYMBOL_GPL(usb_set_device_state);
* the HCD must setup data structures before issuing a set address
* command to the hardware.
*/
-static void choose_address(struct usb_device *udev)
+static void choose_devnum(struct usb_device *udev)
{
int devnum;
struct usb_bus *bus = udev->bus;
@@ -1547,7 +1597,7 @@ static void choose_address(struct usb_device *udev)
}
}
-static void release_address(struct usb_device *udev)
+static void release_devnum(struct usb_device *udev)
{
if (udev->devnum > 0) {
clear_bit(udev->devnum, udev->bus->devmap.devicemap);
@@ -1555,7 +1605,7 @@ static void release_address(struct usb_device *udev)
}
}
-static void update_address(struct usb_device *udev, int devnum)
+static void update_devnum(struct usb_device *udev, int devnum)
{
/* The address for a WUSB device is managed by wusbcore. */
if (!udev->wusb)
@@ -1602,7 +1652,8 @@ void usb_disconnect(struct usb_device **pdev)
* this quiesces everyting except pending urbs.
*/
usb_set_device_state(udev, USB_STATE_NOTATTACHED);
- dev_info (&udev->dev, "USB disconnect, address %d\n", udev->devnum);
+ dev_info(&udev->dev, "USB disconnect, device number %d\n",
+ udev->devnum);
usb_lock_device(udev);
@@ -1632,7 +1683,7 @@ void usb_disconnect(struct usb_device **pdev)
/* Free the device number and delete the parent's children[]
* (or root_hub) pointer.
*/
- release_address(udev);
+ release_devnum(udev);
/* Avoid races with recursively_mark_NOTATTACHED() */
spin_lock_irq(&device_state_lock);
@@ -2017,7 +2068,7 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
(portstatus & USB_PORT_STAT_ENABLE)) {
if (hub_is_wusb(hub))
udev->speed = USB_SPEED_WIRELESS;
- else if (portstatus & USB_PORT_STAT_SUPER_SPEED)
+ else if (hub_is_superspeed(hub->hdev))
udev->speed = USB_SPEED_SUPER;
else if (portstatus & USB_PORT_STAT_HIGH_SPEED)
udev->speed = USB_SPEED_HIGH;
@@ -2073,7 +2124,7 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
case 0:
/* TRSTRCY = 10 ms; plus some extra */
msleep(10 + 40);
- update_address(udev, 0);
+ update_devnum(udev, 0);
if (hcd->driver->reset_device) {
status = hcd->driver->reset_device(hcd, udev);
if (status < 0) {
@@ -2636,7 +2687,7 @@ static int hub_set_address(struct usb_device *udev, int devnum)
USB_REQ_SET_ADDRESS, 0, devnum, 0,
NULL, 0, USB_CTRL_SET_TIMEOUT);
if (retval == 0) {
- update_address(udev, devnum);
+ update_devnum(udev, devnum);
/* Device now using proper address. */
usb_set_device_state(udev, USB_STATE_ADDRESS);
usb_ep0_reinit(udev);
@@ -2741,9 +2792,9 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
}
if (udev->speed != USB_SPEED_SUPER)
dev_info(&udev->dev,
- "%s %s speed %sUSB device using %s and address %d\n",
+ "%s %s speed %sUSB device number %d using %s\n",
(udev->config) ? "reset" : "new", speed, type,
- udev->bus->controller->driver->name, devnum);
+ devnum, udev->bus->controller->driver->name);
/* Set up TT records, if needed */
if (hdev->tt) {
@@ -2773,10 +2824,6 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
* value.
*/
for (i = 0; i < GET_DESCRIPTOR_TRIES; (++i, msleep(100))) {
- /*
- * An xHCI controller cannot send any packets to a device until
- * a set address command successfully completes.
- */
if (USE_NEW_SCHEME(retry_counter) && !(hcd->driver->flags & HCD_USB3)) {
struct usb_device_descriptor *buf;
int r = 0;
@@ -2859,9 +2906,9 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
if (udev->speed == USB_SPEED_SUPER) {
devnum = udev->devnum;
dev_info(&udev->dev,
- "%s SuperSpeed USB device using %s and address %d\n",
+ "%s SuperSpeed USB device number %d using %s\n",
(udev->config) ? "reset" : "new",
- udev->bus->controller->driver->name, devnum);
+ devnum, udev->bus->controller->driver->name);
}
/* cope with hardware quirkiness:
@@ -2924,7 +2971,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
fail:
if (retval) {
hub_port_disable(hub, port1, 0);
- update_address(udev, devnum); /* for disconnect processing */
+ update_devnum(udev, devnum); /* for disconnect processing */
}
mutex_unlock(&usb_address0_mutex);
return retval;
@@ -3015,7 +3062,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
dev_dbg (hub_dev,
"port %d, status %04x, change %04x, %s\n",
- port1, portstatus, portchange, portspeed (portstatus));
+ port1, portstatus, portchange, portspeed(hub, portstatus));
if (hub->has_indicators) {
set_port_led(hub, port1, HUB_LED_AUTO);
@@ -3116,32 +3163,13 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
udev->level = hdev->level + 1;
udev->wusb = hub_is_wusb(hub);
- /*
- * USB 3.0 devices are reset automatically before the connect
- * port status change appears, and the root hub port status
- * shows the correct speed. We also get port change
- * notifications for USB 3.0 devices from the USB 3.0 portion of
- * an external USB 3.0 hub, but this isn't handled correctly yet
- * FIXME.
- */
-
- if (!(hcd->driver->flags & HCD_USB3))
- udev->speed = USB_SPEED_UNKNOWN;
- else if ((hdev->parent == NULL) &&
- (portstatus & USB_PORT_STAT_SUPER_SPEED))
+ /* Only USB 3.0 devices are connected to SuperSpeed hubs. */
+ if (hub_is_superspeed(hub->hdev))
udev->speed = USB_SPEED_SUPER;
else
udev->speed = USB_SPEED_UNKNOWN;
- /*
- * Set the address.
- * Note xHCI needs to issue an address device command later
- * in the hub_port_init sequence for SS/HS/FS/LS devices,
- * and xHC will assign an address to the device. But use
- * kernel assigned address here, to avoid any address conflict
- * issue.
- */
- choose_address(udev);
+ choose_devnum(udev);
if (udev->devnum <= 0) {
status = -ENOTCONN; /* Don't retry */
goto loop;
@@ -3233,7 +3261,7 @@ loop_disable:
hub_port_disable(hub, port1, 1);
loop:
usb_ep0_reinit(udev);
- release_address(udev);
+ release_devnum(udev);
hub_free_dev(udev);
usb_put_dev(udev);
if ((status == -ENOTCONN) || (status == -ENOTSUPP))
@@ -3410,12 +3438,19 @@ static void hub_events(void)
}
if (portchange & USB_PORT_STAT_C_OVERCURRENT) {
- dev_err (hub_dev,
- "over-current change on port %d\n",
- i);
+ u16 status = 0;
+ u16 unused;
+
+ dev_dbg(hub_dev, "over-current change on port "
+ "%d\n", i);
clear_port_feature(hdev, i,
USB_PORT_FEAT_C_OVER_CURRENT);
+ msleep(100); /* Cool down */
hub_power_on(hub, true);
+ hub_port_status(hub, i, &status, &unused);
+ if (status & USB_PORT_STAT_OVERCURRENT)
+ dev_err(hub_dev, "over-current "
+ "condition on port %d\n", i);
}
if (portchange & USB_PORT_STAT_C_RESET) {
@@ -3425,6 +3460,25 @@ static void hub_events(void)
clear_port_feature(hdev, i,
USB_PORT_FEAT_C_RESET);
}
+ if ((portchange & USB_PORT_STAT_C_BH_RESET) &&
+ hub_is_superspeed(hub->hdev)) {
+ dev_dbg(hub_dev,
+ "warm reset change on port %d\n",
+ i);
+ clear_port_feature(hdev, i,
+ USB_PORT_FEAT_C_BH_PORT_RESET);
+ }
+ if (portchange & USB_PORT_STAT_C_LINK_STATE) {
+ clear_port_feature(hub->hdev, i,
+ USB_PORT_FEAT_C_PORT_LINK_STATE);
+ }
+ if (portchange & USB_PORT_STAT_C_CONFIG_ERROR) {
+ dev_warn(hub_dev,
+ "config error on port %d\n",
+ i);
+ clear_port_feature(hub->hdev, i,
+ USB_PORT_FEAT_C_PORT_CONFIG_ERROR);
+ }
if (connect_change)
hub_port_connect_change(hub, i,
@@ -3447,10 +3501,17 @@ static void hub_events(void)
hub->limited_power = 0;
}
if (hubchange & HUB_CHANGE_OVERCURRENT) {
- dev_dbg (hub_dev, "overcurrent change\n");
- msleep(500); /* Cool down */
+ u16 status = 0;
+ u16 unused;
+
+ dev_dbg(hub_dev, "over-current change\n");
clear_hub_feature(hdev, C_HUB_OVER_CURRENT);
+ msleep(500); /* Cool down */
hub_power_on(hub, true);
+ hub_hub_status(hub, &status, &unused);
+ if (status & HUB_STATUS_OVERCURRENT)
+ dev_err(hub_dev, "over-current "
+ "condition\n");
}
}
@@ -3699,13 +3760,13 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
if (!udev->actconfig)
goto done;
- mutex_lock(&hcd->bandwidth_mutex);
+ mutex_lock(hcd->bandwidth_mutex);
ret = usb_hcd_alloc_bandwidth(udev, udev->actconfig, NULL, NULL);
if (ret < 0) {
dev_warn(&udev->dev,
"Busted HC? Not enough HCD resources for "
"old configuration.\n");
- mutex_unlock(&hcd->bandwidth_mutex);
+ mutex_unlock(hcd->bandwidth_mutex);
goto re_enumerate;
}
ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
@@ -3716,10 +3777,10 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
dev_err(&udev->dev,
"can't restore configuration #%d (error=%d)\n",
udev->actconfig->desc.bConfigurationValue, ret);
- mutex_unlock(&hcd->bandwidth_mutex);
+ mutex_unlock(hcd->bandwidth_mutex);
goto re_enumerate;
}
- mutex_unlock(&hcd->bandwidth_mutex);
+ mutex_unlock(hcd->bandwidth_mutex);
usb_set_device_state(udev, USB_STATE_CONFIGURED);
/* Put interfaces back into the same altsettings as before.
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
index 832487423826..5701e857392b 100644
--- a/drivers/usb/core/message.c
+++ b/drivers/usb/core/message.c
@@ -1284,12 +1284,12 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate)
/* Make sure we have enough bandwidth for this alternate interface.
* Remove the current alt setting and add the new alt setting.
*/
- mutex_lock(&hcd->bandwidth_mutex);
+ mutex_lock(hcd->bandwidth_mutex);
ret = usb_hcd_alloc_bandwidth(dev, NULL, iface->cur_altsetting, alt);
if (ret < 0) {
dev_info(&dev->dev, "Not enough bandwidth for altsetting %d\n",
alternate);
- mutex_unlock(&hcd->bandwidth_mutex);
+ mutex_unlock(hcd->bandwidth_mutex);
return ret;
}
@@ -1311,10 +1311,10 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate)
} else if (ret < 0) {
/* Re-instate the old alt setting */
usb_hcd_alloc_bandwidth(dev, NULL, alt, iface->cur_altsetting);
- mutex_unlock(&hcd->bandwidth_mutex);
+ mutex_unlock(hcd->bandwidth_mutex);
return ret;
}
- mutex_unlock(&hcd->bandwidth_mutex);
+ mutex_unlock(hcd->bandwidth_mutex);
/* FIXME drivers shouldn't need to replicate/bugfix the logic here
* when they implement async or easily-killable versions of this or
@@ -1413,7 +1413,7 @@ int usb_reset_configuration(struct usb_device *dev)
config = dev->actconfig;
retval = 0;
- mutex_lock(&hcd->bandwidth_mutex);
+ mutex_lock(hcd->bandwidth_mutex);
/* Make sure we have enough bandwidth for each alternate setting 0 */
for (i = 0; i < config->desc.bNumInterfaces; i++) {
struct usb_interface *intf = config->interface[i];
@@ -1442,7 +1442,7 @@ reset_old_alts:
usb_hcd_alloc_bandwidth(dev, NULL,
alt, intf->cur_altsetting);
}
- mutex_unlock(&hcd->bandwidth_mutex);
+ mutex_unlock(hcd->bandwidth_mutex);
return retval;
}
retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
@@ -1451,7 +1451,7 @@ reset_old_alts:
NULL, 0, USB_CTRL_SET_TIMEOUT);
if (retval < 0)
goto reset_old_alts;
- mutex_unlock(&hcd->bandwidth_mutex);
+ mutex_unlock(hcd->bandwidth_mutex);
/* re-init hc/hcd interface/endpoint state */
for (i = 0; i < config->desc.bNumInterfaces; i++) {
@@ -1739,10 +1739,10 @@ free_interfaces:
* host controller will not allow submissions to dropped endpoints. If
* this call fails, the device state is unchanged.
*/
- mutex_lock(&hcd->bandwidth_mutex);
+ mutex_lock(hcd->bandwidth_mutex);
ret = usb_hcd_alloc_bandwidth(dev, cp, NULL, NULL);
if (ret < 0) {
- mutex_unlock(&hcd->bandwidth_mutex);
+ mutex_unlock(hcd->bandwidth_mutex);
usb_autosuspend_device(dev);
goto free_interfaces;
}
@@ -1761,11 +1761,11 @@ free_interfaces:
if (!cp) {
usb_set_device_state(dev, USB_STATE_ADDRESS);
usb_hcd_alloc_bandwidth(dev, NULL, NULL, NULL);
- mutex_unlock(&hcd->bandwidth_mutex);
+ mutex_unlock(hcd->bandwidth_mutex);
usb_autosuspend_device(dev);
goto free_interfaces;
}
- mutex_unlock(&hcd->bandwidth_mutex);
+ mutex_unlock(hcd->bandwidth_mutex);
usb_set_device_state(dev, USB_STATE_CONFIGURED);
/* Initialize the new interface structures and the
diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c
index c14fc082864f..ae334b067c13 100644
--- a/drivers/usb/core/urb.c
+++ b/drivers/usb/core/urb.c
@@ -366,7 +366,16 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
if (xfertype == USB_ENDPOINT_XFER_ISOC) {
int n, len;
- /* FIXME SuperSpeed isoc endpoints have up to 16 bursts */
+ /* SuperSpeed isoc endpoints have up to 16 bursts of up to
+ * 3 packets each
+ */
+ if (dev->speed == USB_SPEED_SUPER) {
+ int burst = 1 + ep->ss_ep_comp.bMaxBurst;
+ int mult = USB_SS_MULT(ep->ss_ep_comp.bmAttributes);
+ max *= burst;
+ max *= mult;
+ }
+
/* "high bandwidth" mode, 1-3 packets/uframe? */
if (dev->speed == USB_SPEED_HIGH) {
int mult = 1 + ((max >> 11) & 0x03);
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
index b975450f403e..a9cf484ecae4 100644
--- a/drivers/usb/core/usb.h
+++ b/drivers/usb/core/usb.h
@@ -122,6 +122,19 @@ static inline int is_usb_device_driver(struct device_driver *drv)
for_devices;
}
+/* translate USB error codes to codes user space understands */
+static inline int usb_translate_errors(int error_code)
+{
+ switch (error_code) {
+ case 0:
+ case -ENOMEM:
+ case -ENODEV:
+ return error_code;
+ default:
+ return -EIO;
+ }
+}
+
/* for labeling diagnostics */
extern const char *usbcore_name;
diff --git a/drivers/usb/early/ehci-dbgp.c b/drivers/usb/early/ehci-dbgp.c
index 94ecdbc758ce..0bc06e2bcfcb 100644
--- a/drivers/usb/early/ehci-dbgp.c
+++ b/drivers/usb/early/ehci-dbgp.c
@@ -601,7 +601,7 @@ try_again:
dbgp_printk("dbgp_bulk_write failed: %d\n", ret);
goto err;
}
- dbgp_printk("small write doned\n");
+ dbgp_printk("small write done\n");
dbgp_not_safe = 0;
return 0;
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index d50099675f28..bfde50e20b30 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -176,6 +176,18 @@ config USB_FSL_USB2
default USB_GADGET
select USB_GADGET_SELECTED
+config USB_GADGET_FUSB300
+ boolean "Faraday FUSB300 USB Peripheral Controller"
+ select USB_GADGET_DUALSPEED
+ help
+ Faraday usb device controller FUSB300 driver
+
+config USB_FUSB300
+ tristate
+ depends on USB_GADGET_FUSB300
+ default USB_GADGET
+ select USB_GADGET_SELECTED
+
config USB_GADGET_LH7A40X
boolean "LH7A40X"
depends on ARCH_LH7A40X
@@ -540,7 +552,7 @@ config USB_GADGET_CI13XXX_MSM
boolean "MIPS USB CI13xxx for MSM"
depends on ARCH_MSM
select USB_GADGET_DUALSPEED
- select USB_MSM_OTG_72K
+ select USB_MSM_OTG
help
MSM SoC has chipidea USB controller. This driver uses
ci13xxx_udc core.
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 55f5e8ae5924..305286e181d5 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -28,6 +28,7 @@ obj-$(CONFIG_USB_EG20T) += pch_udc.o
obj-$(CONFIG_USB_PXA_U2O) += mv_udc.o
mv_udc-y := mv_udc_core.o mv_udc_phy.o
obj-$(CONFIG_USB_CI13XXX_MSM) += ci13xxx_msm.o
+obj-$(CONFIG_USB_FUSB300) += fusb300_udc.o
#
# USB gadget drivers
diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c
index bdec36acd0fa..bb8ddf0469f9 100644
--- a/drivers/usb/gadget/at91_udc.c
+++ b/drivers/usb/gadget/at91_udc.c
@@ -1798,8 +1798,10 @@ static int __init at91udc_probe(struct platform_device *pdev)
}
retval = device_register(&udc->gadget.dev);
- if (retval < 0)
+ if (retval < 0) {
+ put_device(&udc->gadget.dev);
goto fail0b;
+ }
/* don't do anything until we have both gadget driver and VBUS */
clk_enable(udc->iclk);
diff --git a/drivers/usb/gadget/ci13xxx_udc.c b/drivers/usb/gadget/ci13xxx_udc.c
index a1c67ae1572a..e09178bc1450 100644
--- a/drivers/usb/gadget/ci13xxx_udc.c
+++ b/drivers/usb/gadget/ci13xxx_udc.c
@@ -435,20 +435,6 @@ static int hw_ep_get_halt(int num, int dir)
}
/**
- * hw_ep_is_primed: test if endpoint is primed (execute without interruption)
- * @num: endpoint number
- * @dir: endpoint direction
- *
- * This function returns true if endpoint primed
- */
-static int hw_ep_is_primed(int num, int dir)
-{
- u32 reg = hw_cread(CAP_ENDPTPRIME, ~0) | hw_cread(CAP_ENDPTSTAT, ~0);
-
- return test_bit(hw_ep_bit(num, dir), (void *)&reg);
-}
-
-/**
* hw_test_and_clear_setup_status: test & clear setup status (execute without
* interruption)
* @n: bit number (endpoint)
@@ -472,10 +458,6 @@ static int hw_ep_prime(int num, int dir, int is_ctrl)
{
int n = hw_ep_bit(num, dir);
- /* the caller should flush first */
- if (hw_ep_is_primed(num, dir))
- return -EBUSY;
-
if (is_ctrl && dir == RX && hw_cread(CAP_ENDPTSETUPSTAT, BIT(num)))
return -EAGAIN;
@@ -1434,6 +1416,8 @@ static inline u8 _usb_addr(struct ci13xxx_ep *ep)
static int _hardware_enqueue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq)
{
unsigned i;
+ int ret = 0;
+ unsigned length = mReq->req.length;
trace("%p, %p", mEp, mReq);
@@ -1441,53 +1425,91 @@ static int _hardware_enqueue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq)
if (mReq->req.status == -EALREADY)
return -EALREADY;
- if (hw_ep_is_primed(mEp->num, mEp->dir))
- return -EBUSY;
-
mReq->req.status = -EALREADY;
-
- if (mReq->req.length && !mReq->req.dma) {
+ if (length && !mReq->req.dma) {
mReq->req.dma = \
dma_map_single(mEp->device, mReq->req.buf,
- mReq->req.length, mEp->dir ?
- DMA_TO_DEVICE : DMA_FROM_DEVICE);
+ length, mEp->dir ? DMA_TO_DEVICE :
+ DMA_FROM_DEVICE);
if (mReq->req.dma == 0)
return -ENOMEM;
mReq->map = 1;
}
+ if (mReq->req.zero && length && (length % mEp->ep.maxpacket == 0)) {
+ mReq->zptr = dma_pool_alloc(mEp->td_pool, GFP_ATOMIC,
+ &mReq->zdma);
+ if (mReq->zptr == NULL) {
+ if (mReq->map) {
+ dma_unmap_single(mEp->device, mReq->req.dma,
+ length, mEp->dir ? DMA_TO_DEVICE :
+ DMA_FROM_DEVICE);
+ mReq->req.dma = 0;
+ mReq->map = 0;
+ }
+ return -ENOMEM;
+ }
+ memset(mReq->zptr, 0, sizeof(*mReq->zptr));
+ mReq->zptr->next = TD_TERMINATE;
+ mReq->zptr->token = TD_STATUS_ACTIVE;
+ if (!mReq->req.no_interrupt)
+ mReq->zptr->token |= TD_IOC;
+ }
/*
* TD configuration
* TODO - handle requests which spawns into several TDs
*/
memset(mReq->ptr, 0, sizeof(*mReq->ptr));
- mReq->ptr->next |= TD_TERMINATE;
- mReq->ptr->token = mReq->req.length << ffs_nr(TD_TOTAL_BYTES);
+ mReq->ptr->token = length << ffs_nr(TD_TOTAL_BYTES);
mReq->ptr->token &= TD_TOTAL_BYTES;
- mReq->ptr->token |= TD_IOC;
mReq->ptr->token |= TD_STATUS_ACTIVE;
+ if (mReq->zptr) {
+ mReq->ptr->next = mReq->zdma;
+ } else {
+ mReq->ptr->next = TD_TERMINATE;
+ if (!mReq->req.no_interrupt)
+ mReq->ptr->token |= TD_IOC;
+ }
mReq->ptr->page[0] = mReq->req.dma;
for (i = 1; i < 5; i++)
mReq->ptr->page[i] =
(mReq->req.dma + i * CI13XXX_PAGE_SIZE) & ~TD_RESERVED_MASK;
- /*
- * QH configuration
- * At this point it's guaranteed exclusive access to qhead
- * (endpt is not primed) so it's no need to use tripwire
- */
+ if (!list_empty(&mEp->qh.queue)) {
+ struct ci13xxx_req *mReqPrev;
+ int n = hw_ep_bit(mEp->num, mEp->dir);
+ int tmp_stat;
+
+ mReqPrev = list_entry(mEp->qh.queue.prev,
+ struct ci13xxx_req, queue);
+ if (mReqPrev->zptr)
+ mReqPrev->zptr->next = mReq->dma & TD_ADDR_MASK;
+ else
+ mReqPrev->ptr->next = mReq->dma & TD_ADDR_MASK;
+ wmb();
+ if (hw_cread(CAP_ENDPTPRIME, BIT(n)))
+ goto done;
+ do {
+ hw_cwrite(CAP_USBCMD, USBCMD_ATDTW, USBCMD_ATDTW);
+ tmp_stat = hw_cread(CAP_ENDPTSTAT, BIT(n));
+ } while (!hw_cread(CAP_USBCMD, USBCMD_ATDTW));
+ hw_cwrite(CAP_USBCMD, USBCMD_ATDTW, 0);
+ if (tmp_stat)
+ goto done;
+ }
+
+ /* QH configuration */
mEp->qh.ptr->td.next = mReq->dma; /* TERMINATE = 0 */
mEp->qh.ptr->td.token &= ~TD_STATUS; /* clear status */
- if (mReq->req.zero == 0)
- mEp->qh.ptr->cap |= QH_ZLT;
- else
- mEp->qh.ptr->cap &= ~QH_ZLT;
+ mEp->qh.ptr->cap |= QH_ZLT;
wmb(); /* synchronize before ep prime */
- return hw_ep_prime(mEp->num, mEp->dir,
+ ret = hw_ep_prime(mEp->num, mEp->dir,
mEp->type == USB_ENDPOINT_XFER_CONTROL);
+done:
+ return ret;
}
/**
@@ -1504,8 +1526,15 @@ static int _hardware_dequeue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq)
if (mReq->req.status != -EALREADY)
return -EINVAL;
- if (hw_ep_is_primed(mEp->num, mEp->dir))
- hw_ep_flush(mEp->num, mEp->dir);
+ if ((TD_STATUS_ACTIVE & mReq->ptr->token) != 0)
+ return -EBUSY;
+
+ if (mReq->zptr) {
+ if ((TD_STATUS_ACTIVE & mReq->zptr->token) != 0)
+ return -EBUSY;
+ dma_pool_free(mEp->td_pool, mReq->zptr, mReq->zdma);
+ mReq->zptr = NULL;
+ }
mReq->req.status = 0;
@@ -1517,9 +1546,7 @@ static int _hardware_dequeue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq)
}
mReq->req.status = mReq->ptr->token & TD_STATUS;
- if ((TD_STATUS_ACTIVE & mReq->req.status) != 0)
- mReq->req.status = -ECONNRESET;
- else if ((TD_STATUS_HALTED & mReq->req.status) != 0)
+ if ((TD_STATUS_HALTED & mReq->req.status) != 0)
mReq->req.status = -1;
else if ((TD_STATUS_DT_ERR & mReq->req.status) != 0)
mReq->req.status = -1;
@@ -1581,12 +1608,19 @@ static int _gadget_stop_activity(struct usb_gadget *gadget)
{
struct usb_ep *ep;
struct ci13xxx *udc = container_of(gadget, struct ci13xxx, gadget);
+ unsigned long flags;
trace("%p", gadget);
if (gadget == NULL)
return -EINVAL;
+ spin_lock_irqsave(udc->lock, flags);
+ udc->gadget.speed = USB_SPEED_UNKNOWN;
+ udc->remote_wakeup = 0;
+ udc->suspended = 0;
+ spin_unlock_irqrestore(udc->lock, flags);
+
/* flush all endpoints */
gadget_for_each_ep(ep, gadget) {
usb_ep_fifo_flush(ep);
@@ -1720,7 +1754,8 @@ __acquires(mEp->lock)
}
if ((setup->bRequestType & USB_RECIP_MASK) == USB_RECIP_DEVICE) {
- /* TODO: D1 - Remote Wakeup; D0 - Self Powered */
+ /* Assume that device is bus powered for now. */
+ *((u16 *)req->buf) = _udc->remote_wakeup << 1;
retval = 0;
} else if ((setup->bRequestType & USB_RECIP_MASK) \
== USB_RECIP_ENDPOINT) {
@@ -1749,6 +1784,28 @@ __acquires(mEp->lock)
}
/**
+ * isr_setup_status_complete: setup_status request complete function
+ * @ep: endpoint
+ * @req: request handled
+ *
+ * Caller must release lock. Put the port in test mode if test mode
+ * feature is selected.
+ */
+static void
+isr_setup_status_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct ci13xxx *udc = req->context;
+ unsigned long flags;
+
+ trace("%p, %p", ep, req);
+
+ spin_lock_irqsave(udc->lock, flags);
+ if (udc->test_mode)
+ hw_port_test_set(udc->test_mode);
+ spin_unlock_irqrestore(udc->lock, flags);
+}
+
+/**
* isr_setup_status_phase: queues the status phase of a setup transation
* @udc: udc struct
*
@@ -1764,6 +1821,8 @@ __acquires(mEp->lock)
trace("%p", udc);
mEp = (udc->ep0_dir == TX) ? &udc->ep0out : &udc->ep0in;
+ udc->status->context = udc;
+ udc->status->complete = isr_setup_status_complete;
spin_unlock(mEp->lock);
retval = usb_ep_queue(&mEp->ep, udc->status, GFP_ATOMIC);
@@ -1783,7 +1842,7 @@ static int isr_tr_complete_low(struct ci13xxx_ep *mEp)
__releases(mEp->lock)
__acquires(mEp->lock)
{
- struct ci13xxx_req *mReq;
+ struct ci13xxx_req *mReq, *mReqTemp;
int retval;
trace("%p", mEp);
@@ -1791,34 +1850,25 @@ __acquires(mEp->lock)
if (list_empty(&mEp->qh.queue))
return -EINVAL;
- /* pop oldest request */
- mReq = list_entry(mEp->qh.queue.next,
- struct ci13xxx_req, queue);
- list_del_init(&mReq->queue);
-
- retval = _hardware_dequeue(mEp, mReq);
- if (retval < 0) {
- dbg_event(_usb_addr(mEp), "DONE", retval);
- goto done;
- }
-
- dbg_done(_usb_addr(mEp), mReq->ptr->token, retval);
-
- if (!list_empty(&mEp->qh.queue)) {
- struct ci13xxx_req* mReqEnq;
-
- mReqEnq = list_entry(mEp->qh.queue.next,
- struct ci13xxx_req, queue);
- _hardware_enqueue(mEp, mReqEnq);
+ list_for_each_entry_safe(mReq, mReqTemp, &mEp->qh.queue,
+ queue) {
+ retval = _hardware_dequeue(mEp, mReq);
+ if (retval < 0)
+ break;
+ list_del_init(&mReq->queue);
+ dbg_done(_usb_addr(mEp), mReq->ptr->token, retval);
+ if (mReq->req.complete != NULL) {
+ spin_unlock(mEp->lock);
+ mReq->req.complete(&mEp->ep, &mReq->req);
+ spin_lock(mEp->lock);
+ }
}
- if (mReq->req.complete != NULL) {
- spin_unlock(mEp->lock);
- mReq->req.complete(&mEp->ep, &mReq->req);
- spin_lock(mEp->lock);
- }
+ if (retval == EBUSY)
+ retval = 0;
+ if (retval < 0)
+ dbg_event(_usb_addr(mEp), "DONE", retval);
- done:
return retval;
}
@@ -1833,6 +1883,7 @@ __releases(udc->lock)
__acquires(udc->lock)
{
unsigned i;
+ u8 tmode = 0;
trace("%p", udc);
@@ -1895,22 +1946,32 @@ __acquires(udc->lock)
switch (req.bRequest) {
case USB_REQ_CLEAR_FEATURE:
- if (type != (USB_DIR_OUT|USB_RECIP_ENDPOINT) &&
- le16_to_cpu(req.wValue) != USB_ENDPOINT_HALT)
- goto delegate;
- if (req.wLength != 0)
- break;
- num = le16_to_cpu(req.wIndex);
- num &= USB_ENDPOINT_NUMBER_MASK;
- if (!udc->ci13xxx_ep[num].wedge) {
- spin_unlock(udc->lock);
- err = usb_ep_clear_halt(
- &udc->ci13xxx_ep[num].ep);
- spin_lock(udc->lock);
- if (err)
+ if (type == (USB_DIR_OUT|USB_RECIP_ENDPOINT) &&
+ le16_to_cpu(req.wValue) ==
+ USB_ENDPOINT_HALT) {
+ if (req.wLength != 0)
break;
+ num = le16_to_cpu(req.wIndex);
+ num &= USB_ENDPOINT_NUMBER_MASK;
+ if (!udc->ci13xxx_ep[num].wedge) {
+ spin_unlock(udc->lock);
+ err = usb_ep_clear_halt(
+ &udc->ci13xxx_ep[num].ep);
+ spin_lock(udc->lock);
+ if (err)
+ break;
+ }
+ err = isr_setup_status_phase(udc);
+ } else if (type == (USB_DIR_OUT|USB_RECIP_DEVICE) &&
+ le16_to_cpu(req.wValue) ==
+ USB_DEVICE_REMOTE_WAKEUP) {
+ if (req.wLength != 0)
+ break;
+ udc->remote_wakeup = 0;
+ err = isr_setup_status_phase(udc);
+ } else {
+ goto delegate;
}
- err = isr_setup_status_phase(udc);
break;
case USB_REQ_GET_STATUS:
if (type != (USB_DIR_IN|USB_RECIP_DEVICE) &&
@@ -1934,20 +1995,48 @@ __acquires(udc->lock)
err = isr_setup_status_phase(udc);
break;
case USB_REQ_SET_FEATURE:
- if (type != (USB_DIR_OUT|USB_RECIP_ENDPOINT) &&
- le16_to_cpu(req.wValue) != USB_ENDPOINT_HALT)
- goto delegate;
- if (req.wLength != 0)
- break;
- num = le16_to_cpu(req.wIndex);
- num &= USB_ENDPOINT_NUMBER_MASK;
+ if (type == (USB_DIR_OUT|USB_RECIP_ENDPOINT) &&
+ le16_to_cpu(req.wValue) ==
+ USB_ENDPOINT_HALT) {
+ if (req.wLength != 0)
+ break;
+ num = le16_to_cpu(req.wIndex);
+ num &= USB_ENDPOINT_NUMBER_MASK;
- spin_unlock(udc->lock);
- err = usb_ep_set_halt(&udc->ci13xxx_ep[num].ep);
- spin_lock(udc->lock);
- if (err)
- break;
- err = isr_setup_status_phase(udc);
+ spin_unlock(udc->lock);
+ err = usb_ep_set_halt(&udc->ci13xxx_ep[num].ep);
+ spin_lock(udc->lock);
+ if (!err)
+ isr_setup_status_phase(udc);
+ } else if (type == (USB_DIR_OUT|USB_RECIP_DEVICE)) {
+ if (req.wLength != 0)
+ break;
+ switch (le16_to_cpu(req.wValue)) {
+ case USB_DEVICE_REMOTE_WAKEUP:
+ udc->remote_wakeup = 1;
+ err = isr_setup_status_phase(udc);
+ break;
+ case USB_DEVICE_TEST_MODE:
+ tmode = le16_to_cpu(req.wIndex) >> 8;
+ switch (tmode) {
+ case TEST_J:
+ case TEST_K:
+ case TEST_SE0_NAK:
+ case TEST_PACKET:
+ case TEST_FORCE_EN:
+ udc->test_mode = tmode;
+ err = isr_setup_status_phase(
+ udc);
+ break;
+ default:
+ break;
+ }
+ default:
+ goto delegate;
+ }
+ } else {
+ goto delegate;
+ }
break;
default:
delegate:
@@ -2178,15 +2267,15 @@ static int ep_queue(struct usb_ep *ep, struct usb_request *req,
/* push request */
mReq->req.status = -EINPROGRESS;
mReq->req.actual = 0;
- list_add_tail(&mReq->queue, &mEp->qh.queue);
- if (list_is_singular(&mEp->qh.queue))
- retval = _hardware_enqueue(mEp, mReq);
+ retval = _hardware_enqueue(mEp, mReq);
if (retval == -EALREADY) {
dbg_event(_usb_addr(mEp), "QUEUE", retval);
retval = 0;
}
+ if (!retval)
+ list_add_tail(&mReq->queue, &mEp->qh.queue);
done:
spin_unlock_irqrestore(mEp->lock, flags);
@@ -2206,19 +2295,25 @@ static int ep_dequeue(struct usb_ep *ep, struct usb_request *req)
trace("%p, %p", ep, req);
- if (ep == NULL || req == NULL || mEp->desc == NULL ||
- list_empty(&mReq->queue) || list_empty(&mEp->qh.queue))
+ if (ep == NULL || req == NULL || mReq->req.status != -EALREADY ||
+ mEp->desc == NULL || list_empty(&mReq->queue) ||
+ list_empty(&mEp->qh.queue))
return -EINVAL;
spin_lock_irqsave(mEp->lock, flags);
dbg_event(_usb_addr(mEp), "DEQUEUE", 0);
- if (mReq->req.status == -EALREADY)
- _hardware_dequeue(mEp, mReq);
+ hw_ep_flush(mEp->num, mEp->dir);
/* pop request */
list_del_init(&mReq->queue);
+ if (mReq->map) {
+ dma_unmap_single(mEp->device, mReq->req.dma, mReq->req.length,
+ mEp->dir ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+ mReq->req.dma = 0;
+ mReq->map = 0;
+ }
req->status = -ECONNRESET;
if (mReq->req.complete != NULL) {
@@ -2377,6 +2472,31 @@ static int ci13xxx_vbus_session(struct usb_gadget *_gadget, int is_active)
return 0;
}
+static int ci13xxx_wakeup(struct usb_gadget *_gadget)
+{
+ struct ci13xxx *udc = container_of(_gadget, struct ci13xxx, gadget);
+ unsigned long flags;
+ int ret = 0;
+
+ trace();
+
+ spin_lock_irqsave(udc->lock, flags);
+ if (!udc->remote_wakeup) {
+ ret = -EOPNOTSUPP;
+ dbg_trace("remote wakeup feature is not enabled\n");
+ goto out;
+ }
+ if (!hw_cread(CAP_PORTSC, PORTSC_SUSP)) {
+ ret = -EINVAL;
+ dbg_trace("port is not suspended\n");
+ goto out;
+ }
+ hw_cwrite(CAP_PORTSC, PORTSC_FPR, PORTSC_FPR);
+out:
+ spin_unlock_irqrestore(udc->lock, flags);
+ return ret;
+}
+
/**
* Device operations part of the API to the USB controller hardware,
* which don't involve endpoints (or i/o)
@@ -2384,6 +2504,7 @@ static int ci13xxx_vbus_session(struct usb_gadget *_gadget, int is_active)
*/
static const struct usb_gadget_ops usb_gadget_ops = {
.vbus_session = ci13xxx_vbus_session,
+ .wakeup = ci13xxx_wakeup,
};
/**
@@ -2626,6 +2747,12 @@ static irqreturn_t udc_irq(void)
isr_statistics.pci++;
udc->gadget.speed = hw_port_is_high_speed() ?
USB_SPEED_HIGH : USB_SPEED_FULL;
+ if (udc->suspended) {
+ spin_unlock(udc->lock);
+ udc->driver->resume(&udc->gadget);
+ spin_lock(udc->lock);
+ udc->suspended = 0;
+ }
}
if (USBi_UEI & intr)
isr_statistics.uei++;
@@ -2633,8 +2760,15 @@ static irqreturn_t udc_irq(void)
isr_statistics.ui++;
isr_tr_complete_handler(udc);
}
- if (USBi_SLI & intr)
+ if (USBi_SLI & intr) {
+ if (udc->gadget.speed != USB_SPEED_UNKNOWN) {
+ udc->suspended = 1;
+ spin_unlock(udc->lock);
+ udc->driver->suspend(&udc->gadget);
+ spin_lock(udc->lock);
+ }
isr_statistics.sli++;
+ }
retval = IRQ_HANDLED;
} else {
isr_statistics.none++;
diff --git a/drivers/usb/gadget/ci13xxx_udc.h b/drivers/usb/gadget/ci13xxx_udc.h
index a2492b65f98c..23707775cb43 100644
--- a/drivers/usb/gadget/ci13xxx_udc.h
+++ b/drivers/usb/gadget/ci13xxx_udc.h
@@ -33,6 +33,7 @@ struct ci13xxx_td {
/* 0 */
u32 next;
#define TD_TERMINATE BIT(0)
+#define TD_ADDR_MASK (0xFFFFFFEUL << 5)
/* 1 */
u32 token;
#define TD_STATUS (0x00FFUL << 0)
@@ -74,6 +75,8 @@ struct ci13xxx_req {
struct list_head queue;
struct ci13xxx_td *ptr;
dma_addr_t dma;
+ struct ci13xxx_td *zptr;
+ dma_addr_t zdma;
};
/* Extension of usb_ep */
@@ -125,6 +128,10 @@ struct ci13xxx {
u32 ep0_dir; /* ep0 direction */
#define ep0out ci13xxx_ep[0]
#define ep0in ci13xxx_ep[16]
+ u8 remote_wakeup; /* Is remote wakeup feature
+ enabled by the host? */
+ u8 suspended; /* suspended by the host */
+ u8 test_mode; /* the selected test mode */
struct usb_gadget_driver *driver; /* 3rd party gadget driver */
struct ci13xxx_udc_driver *udc_driver; /* device controller driver */
@@ -152,6 +159,7 @@ struct ci13xxx {
#define USBCMD_RS BIT(0)
#define USBCMD_RST BIT(1)
#define USBCMD_SUTW BIT(13)
+#define USBCMD_ATDTW BIT(14)
/* USBSTS & USBINTR */
#define USBi_UI BIT(0)
@@ -165,6 +173,7 @@ struct ci13xxx {
#define DEVICEADDR_USBADR (0x7FUL << 25)
/* PORTSC */
+#define PORTSC_FPR BIT(6)
#define PORTSC_SUSP BIT(7)
#define PORTSC_HSP BIT(9)
#define PORTSC_PTC (0x0FUL << 16)
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index 1ba4befe336b..c2251c40a205 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -813,7 +813,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
*/
req->zero = 0;
req->complete = composite_setup_complete;
- req->length = USB_BUFSIZ;
+ req->length = 0;
gadget->ep0->driver_data = cdev;
switch (ctrl->bRequest) {
@@ -887,7 +887,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
case USB_REQ_SET_INTERFACE:
if (ctrl->bRequestType != USB_RECIP_INTERFACE)
goto unknown;
- if (!cdev->config || w_index >= MAX_CONFIG_INTERFACES)
+ if (!cdev->config || intf >= MAX_CONFIG_INTERFACES)
break;
f = cdev->config->interface[intf];
if (!f)
@@ -899,7 +899,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
case USB_REQ_GET_INTERFACE:
if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE))
goto unknown;
- if (!cdev->config || w_index >= MAX_CONFIG_INTERFACES)
+ if (!cdev->config || intf >= MAX_CONFIG_INTERFACES)
break;
f = cdev->config->interface[intf];
if (!f)
@@ -928,7 +928,7 @@ unknown:
*/
switch (ctrl->bRequestType & USB_RECIP_MASK) {
case USB_RECIP_INTERFACE:
- if (!cdev->config || w_index >= MAX_CONFIG_INTERFACES)
+ if (!cdev->config || intf >= MAX_CONFIG_INTERFACES)
break;
f = cdev->config->interface[intf];
break;
@@ -1258,16 +1258,16 @@ static struct usb_gadget_driver composite_driver = {
* while it was binding. That would usually be done in order to wait for
* some userspace participation.
*/
-extern int usb_composite_probe(struct usb_composite_driver *driver,
+int usb_composite_probe(struct usb_composite_driver *driver,
int (*bind)(struct usb_composite_dev *cdev))
{
if (!driver || !driver->dev || !bind || composite)
return -EINVAL;
- if (!driver->iProduct)
- driver->iProduct = driver->name;
if (!driver->name)
driver->name = "composite";
+ if (!driver->iProduct)
+ driver->iProduct = driver->name;
composite_driver.function = (char *) driver->name;
composite_driver.driver.name = driver->name;
composite = driver;
diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c
index 13b9f47feecd..3214ca375d64 100644
--- a/drivers/usb/gadget/dummy_hcd.c
+++ b/drivers/usb/gadget/dummy_hcd.c
@@ -1593,8 +1593,8 @@ hub_descriptor (struct usb_hub_descriptor *desc)
desc->bDescLength = 9;
desc->wHubCharacteristics = cpu_to_le16(0x0001);
desc->bNbrPorts = 1;
- desc->bitmap [0] = 0xff;
- desc->bitmap [1] = 0xff;
+ desc->u.hs.DeviceRemovable[0] = 0xff;
+ desc->u.hs.DeviceRemovable[1] = 0xff;
}
static int dummy_hub_control (
diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c
index 8a832488ccdd..9b7360ff5aa7 100644
--- a/drivers/usb/gadget/epautoconf.c
+++ b/drivers/usb/gadget/epautoconf.c
@@ -128,6 +128,13 @@ ep_matches (
}
}
+ /*
+ * If the protocol driver hasn't yet decided on wMaxPacketSize
+ * and wants to know the maximum possible, provide the info.
+ */
+ if (desc->wMaxPacketSize == 0)
+ desc->wMaxPacketSize = cpu_to_le16(ep->maxpacket);
+
/* endpoint maxpacket size is an input parameter, except for bulk
* where it's an output parameter representing the full speed limit.
* the usb spec fixes high speed bulk maxpacket at 512 bytes.
diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c
index 1499f9e4afa8..19fffccc370d 100644
--- a/drivers/usb/gadget/f_fs.c
+++ b/drivers/usb/gadget/f_fs.c
@@ -368,6 +368,14 @@ static int __ffs_ep0_queue_wait(struct ffs_data *ffs, char *data, size_t len)
req->buf = data;
req->length = len;
+ /*
+ * UDC layer requires to provide a buffer even for ZLP, but should
+ * not use it at all. Let's provide some poisoned pointer to catch
+ * possible bug in the driver.
+ */
+ if (req->buf == NULL)
+ req->buf = (void *)0xDEADBABE;
+
INIT_COMPLETION(ffs->ep0req_completion);
ret = usb_ep_queue(ffs->gadget->ep0, req, GFP_ATOMIC);
diff --git a/drivers/usb/gadget/fsl_mxc_udc.c b/drivers/usb/gadget/fsl_mxc_udc.c
index 77b1eb577029..43a49ecc1f36 100644
--- a/drivers/usb/gadget/fsl_mxc_udc.c
+++ b/drivers/usb/gadget/fsl_mxc_udc.c
@@ -88,15 +88,18 @@ eenahb:
void fsl_udc_clk_finalize(struct platform_device *pdev)
{
struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data;
-#if defined(CONFIG_ARCH_MX35)
- unsigned int v;
-
- /* workaround ENGcm09152 for i.MX35 */
- if (pdata->workaround & FLS_USB2_WORKAROUND_ENGCM09152) {
- v = readl(MX35_IO_ADDRESS(MX35_USB_BASE_ADDR +
- USBPHYCTRL_OTGBASE_OFFSET));
- writel(v | USBPHYCTRL_EVDO, MX35_IO_ADDRESS(MX35_USB_BASE_ADDR +
- USBPHYCTRL_OTGBASE_OFFSET));
+#if defined(CONFIG_SOC_IMX35)
+ if (cpu_is_mx35()) {
+ unsigned int v;
+
+ /* workaround ENGcm09152 for i.MX35 */
+ if (pdata->workaround & FLS_USB2_WORKAROUND_ENGCM09152) {
+ v = readl(MX35_IO_ADDRESS(MX35_USB_BASE_ADDR +
+ USBPHYCTRL_OTGBASE_OFFSET));
+ writel(v | USBPHYCTRL_EVDO,
+ MX35_IO_ADDRESS(MX35_USB_BASE_ADDR +
+ USBPHYCTRL_OTGBASE_OFFSET));
+ }
}
#endif
diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c
index 4c55eda4bd20..912cb8e63fe3 100644
--- a/drivers/usb/gadget/fsl_udc_core.c
+++ b/drivers/usb/gadget/fsl_udc_core.c
@@ -766,7 +766,6 @@ fsl_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
struct fsl_req *req = container_of(_req, struct fsl_req, req);
struct fsl_udc *udc;
unsigned long flags;
- int is_iso = 0;
/* catch various bogus parameters */
if (!_req || !req->req.complete || !req->req.buf
@@ -781,7 +780,6 @@ fsl_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
if (ep->desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) {
if (req->req.length > ep->ep.maxpacket)
return -EMSGSIZE;
- is_iso = 1;
}
udc = ep->udc;
diff --git a/drivers/usb/gadget/fusb300_udc.c b/drivers/usb/gadget/fusb300_udc.c
new file mode 100644
index 000000000000..763d462454b9
--- /dev/null
+++ b/drivers/usb/gadget/fusb300_udc.c
@@ -0,0 +1,1744 @@
+/*
+ * Fusb300 UDC (USB gadget)
+ *
+ * Copyright (C) 2010 Faraday Technology Corp.
+ *
+ * Author : Yuan-hsin Chen <yhchen@faraday-tech.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/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
+#include "fusb300_udc.h"
+
+MODULE_DESCRIPTION("FUSB300 USB gadget driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Yuan Hsin Chen <yhchen@faraday-tech.com>");
+MODULE_ALIAS("platform:fusb300_udc");
+
+#define DRIVER_VERSION "20 October 2010"
+
+static const char udc_name[] = "fusb300_udc";
+static const char * const fusb300_ep_name[] = {
+ "ep0", "ep1", "ep2", "ep3", "ep4", "ep5", "ep6", "ep7", "ep8", "ep9",
+ "ep10", "ep11", "ep12", "ep13", "ep14", "ep15"
+};
+
+static void done(struct fusb300_ep *ep, struct fusb300_request *req,
+ int status);
+
+static void fusb300_enable_bit(struct fusb300 *fusb300, u32 offset,
+ u32 value)
+{
+ u32 reg = ioread32(fusb300->reg + offset);
+
+ reg |= value;
+ iowrite32(reg, fusb300->reg + offset);
+}
+
+static void fusb300_disable_bit(struct fusb300 *fusb300, u32 offset,
+ u32 value)
+{
+ u32 reg = ioread32(fusb300->reg + offset);
+
+ reg &= ~value;
+ iowrite32(reg, fusb300->reg + offset);
+}
+
+
+static void fusb300_ep_setting(struct fusb300_ep *ep,
+ struct fusb300_ep_info info)
+{
+ ep->epnum = info.epnum;
+ ep->type = info.type;
+}
+
+static int fusb300_ep_release(struct fusb300_ep *ep)
+{
+ if (!ep->epnum)
+ return 0;
+ ep->epnum = 0;
+ ep->stall = 0;
+ ep->wedged = 0;
+ return 0;
+}
+
+static void fusb300_set_fifo_entry(struct fusb300 *fusb300,
+ u32 ep)
+{
+ u32 val = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET1(ep));
+
+ val &= ~FUSB300_EPSET1_FIFOENTRY_MSK;
+ val |= FUSB300_EPSET1_FIFOENTRY(FUSB300_FIFO_ENTRY_NUM);
+ iowrite32(val, fusb300->reg + FUSB300_OFFSET_EPSET1(ep));
+}
+
+static void fusb300_set_start_entry(struct fusb300 *fusb300,
+ u8 ep)
+{
+ u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET1(ep));
+ u32 start_entry = fusb300->fifo_entry_num * FUSB300_FIFO_ENTRY_NUM;
+
+ reg &= ~FUSB300_EPSET1_START_ENTRY_MSK ;
+ reg |= FUSB300_EPSET1_START_ENTRY(start_entry);
+ iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET1(ep));
+ if (fusb300->fifo_entry_num == FUSB300_MAX_FIFO_ENTRY) {
+ fusb300->fifo_entry_num = 0;
+ fusb300->addrofs = 0;
+ pr_err("fifo entry is over the maximum number!\n");
+ } else
+ fusb300->fifo_entry_num++;
+}
+
+/* set fusb300_set_start_entry first before fusb300_set_epaddrofs */
+static void fusb300_set_epaddrofs(struct fusb300 *fusb300,
+ struct fusb300_ep_info info)
+{
+ u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET2(info.epnum));
+
+ reg &= ~FUSB300_EPSET2_ADDROFS_MSK;
+ reg |= FUSB300_EPSET2_ADDROFS(fusb300->addrofs);
+ iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET2(info.epnum));
+ fusb300->addrofs += (info.maxpacket + 7) / 8 * FUSB300_FIFO_ENTRY_NUM;
+}
+
+static void ep_fifo_setting(struct fusb300 *fusb300,
+ struct fusb300_ep_info info)
+{
+ fusb300_set_fifo_entry(fusb300, info.epnum);
+ fusb300_set_start_entry(fusb300, info.epnum);
+ fusb300_set_epaddrofs(fusb300, info);
+}
+
+static void fusb300_set_eptype(struct fusb300 *fusb300,
+ struct fusb300_ep_info info)
+{
+ u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET1(info.epnum));
+
+ reg &= ~FUSB300_EPSET1_TYPE_MSK;
+ reg |= FUSB300_EPSET1_TYPE(info.type);
+ iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET1(info.epnum));
+}
+
+static void fusb300_set_epdir(struct fusb300 *fusb300,
+ struct fusb300_ep_info info)
+{
+ u32 reg;
+
+ if (!info.dir_in)
+ return;
+ reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET1(info.epnum));
+ reg &= ~FUSB300_EPSET1_DIR_MSK;
+ reg |= FUSB300_EPSET1_DIRIN;
+ iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET1(info.epnum));
+}
+
+static void fusb300_set_ep_active(struct fusb300 *fusb300,
+ u8 ep)
+{
+ u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET1(ep));
+
+ reg |= FUSB300_EPSET1_ACTEN;
+ iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET1(ep));
+}
+
+static void fusb300_set_epmps(struct fusb300 *fusb300,
+ struct fusb300_ep_info info)
+{
+ u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET2(info.epnum));
+
+ reg &= ~FUSB300_EPSET2_MPS_MSK;
+ reg |= FUSB300_EPSET2_MPS(info.maxpacket);
+ iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET2(info.epnum));
+}
+
+static void fusb300_set_interval(struct fusb300 *fusb300,
+ struct fusb300_ep_info info)
+{
+ u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET1(info.epnum));
+
+ reg &= ~FUSB300_EPSET1_INTERVAL(0x7);
+ reg |= FUSB300_EPSET1_INTERVAL(info.interval);
+ iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET1(info.epnum));
+}
+
+static void fusb300_set_bwnum(struct fusb300 *fusb300,
+ struct fusb300_ep_info info)
+{
+ u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET1(info.epnum));
+
+ reg &= ~FUSB300_EPSET1_BWNUM(0x3);
+ reg |= FUSB300_EPSET1_BWNUM(info.bw_num);
+ iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET1(info.epnum));
+}
+
+static void set_ep_reg(struct fusb300 *fusb300,
+ struct fusb300_ep_info info)
+{
+ fusb300_set_eptype(fusb300, info);
+ fusb300_set_epdir(fusb300, info);
+ fusb300_set_epmps(fusb300, info);
+
+ if (info.interval)
+ fusb300_set_interval(fusb300, info);
+
+ if (info.bw_num)
+ fusb300_set_bwnum(fusb300, info);
+
+ fusb300_set_ep_active(fusb300, info.epnum);
+}
+
+static int config_ep(struct fusb300_ep *ep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ struct fusb300 *fusb300 = ep->fusb300;
+ struct fusb300_ep_info info;
+
+ ep->desc = desc;
+
+ info.interval = 0;
+ info.addrofs = 0;
+ info.bw_num = 0;
+
+ info.type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+ info.dir_in = (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) ? 1 : 0;
+ info.maxpacket = le16_to_cpu(desc->wMaxPacketSize);
+ info.epnum = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
+
+ if ((info.type == USB_ENDPOINT_XFER_INT) ||
+ (info.type == USB_ENDPOINT_XFER_ISOC)) {
+ info.interval = desc->bInterval;
+ if (info.type == USB_ENDPOINT_XFER_ISOC)
+ info.bw_num = ((desc->wMaxPacketSize & 0x1800) >> 11);
+ }
+
+ ep_fifo_setting(fusb300, info);
+
+ set_ep_reg(fusb300, info);
+
+ fusb300_ep_setting(ep, info);
+
+ fusb300->ep[info.epnum] = ep;
+
+ return 0;
+}
+
+static int fusb300_enable(struct usb_ep *_ep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ struct fusb300_ep *ep;
+
+ ep = container_of(_ep, struct fusb300_ep, ep);
+
+ if (ep->fusb300->reenum) {
+ ep->fusb300->fifo_entry_num = 0;
+ ep->fusb300->addrofs = 0;
+ ep->fusb300->reenum = 0;
+ }
+
+ return config_ep(ep, desc);
+}
+
+static int fusb300_disable(struct usb_ep *_ep)
+{
+ struct fusb300_ep *ep;
+ struct fusb300_request *req;
+ unsigned long flags;
+
+ ep = container_of(_ep, struct fusb300_ep, ep);
+
+ BUG_ON(!ep);
+
+ while (!list_empty(&ep->queue)) {
+ req = list_entry(ep->queue.next, struct fusb300_request, queue);
+ spin_lock_irqsave(&ep->fusb300->lock, flags);
+ done(ep, req, -ECONNRESET);
+ spin_unlock_irqrestore(&ep->fusb300->lock, flags);
+ }
+
+ return fusb300_ep_release(ep);
+}
+
+static struct usb_request *fusb300_alloc_request(struct usb_ep *_ep,
+ gfp_t gfp_flags)
+{
+ struct fusb300_request *req;
+
+ req = kzalloc(sizeof(struct fusb300_request), gfp_flags);
+ if (!req)
+ return NULL;
+ INIT_LIST_HEAD(&req->queue);
+
+ return &req->req;
+}
+
+static void fusb300_free_request(struct usb_ep *_ep, struct usb_request *_req)
+{
+ struct fusb300_request *req;
+
+ req = container_of(_req, struct fusb300_request, req);
+ kfree(req);
+}
+
+static int enable_fifo_int(struct fusb300_ep *ep)
+{
+ struct fusb300 *fusb300 = ep->fusb300;
+
+ if (ep->epnum) {
+ fusb300_enable_bit(fusb300, FUSB300_OFFSET_IGER0,
+ FUSB300_IGER0_EEPn_FIFO_INT(ep->epnum));
+ } else {
+ pr_err("can't enable_fifo_int ep0\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int disable_fifo_int(struct fusb300_ep *ep)
+{
+ struct fusb300 *fusb300 = ep->fusb300;
+
+ if (ep->epnum) {
+ fusb300_disable_bit(fusb300, FUSB300_OFFSET_IGER0,
+ FUSB300_IGER0_EEPn_FIFO_INT(ep->epnum));
+ } else {
+ pr_err("can't disable_fifo_int ep0\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void fusb300_set_cxlen(struct fusb300 *fusb300, u32 length)
+{
+ u32 reg;
+
+ reg = ioread32(fusb300->reg + FUSB300_OFFSET_CSR);
+ reg &= ~FUSB300_CSR_LEN_MSK;
+ reg |= FUSB300_CSR_LEN(length);
+ iowrite32(reg, fusb300->reg + FUSB300_OFFSET_CSR);
+}
+
+/* write data to cx fifo */
+static void fusb300_wrcxf(struct fusb300_ep *ep,
+ struct fusb300_request *req)
+{
+ int i = 0;
+ u8 *tmp;
+ u32 data;
+ struct fusb300 *fusb300 = ep->fusb300;
+ u32 length = req->req.length - req->req.actual;
+
+ tmp = req->req.buf + req->req.actual;
+
+ if (length > SS_CTL_MAX_PACKET_SIZE) {
+ fusb300_set_cxlen(fusb300, SS_CTL_MAX_PACKET_SIZE);
+ for (i = (SS_CTL_MAX_PACKET_SIZE >> 2); i > 0; i--) {
+ data = *tmp | *(tmp + 1) << 8 | *(tmp + 2) << 16 |
+ *(tmp + 3) << 24;
+ iowrite32(data, fusb300->reg + FUSB300_OFFSET_CXPORT);
+ tmp += 4;
+ }
+ req->req.actual += SS_CTL_MAX_PACKET_SIZE;
+ } else { /* length is less than max packet size */
+ fusb300_set_cxlen(fusb300, length);
+ for (i = length >> 2; i > 0; i--) {
+ data = *tmp | *(tmp + 1) << 8 | *(tmp + 2) << 16 |
+ *(tmp + 3) << 24;
+ printk(KERN_DEBUG " 0x%x\n", data);
+ iowrite32(data, fusb300->reg + FUSB300_OFFSET_CXPORT);
+ tmp = tmp + 4;
+ }
+ switch (length % 4) {
+ case 1:
+ data = *tmp;
+ printk(KERN_DEBUG " 0x%x\n", data);
+ iowrite32(data, fusb300->reg + FUSB300_OFFSET_CXPORT);
+ break;
+ case 2:
+ data = *tmp | *(tmp + 1) << 8;
+ printk(KERN_DEBUG " 0x%x\n", data);
+ iowrite32(data, fusb300->reg + FUSB300_OFFSET_CXPORT);
+ break;
+ case 3:
+ data = *tmp | *(tmp + 1) << 8 | *(tmp + 2) << 16;
+ printk(KERN_DEBUG " 0x%x\n", data);
+ iowrite32(data, fusb300->reg + FUSB300_OFFSET_CXPORT);
+ break;
+ default:
+ break;
+ }
+ req->req.actual += length;
+ }
+}
+
+static void fusb300_set_epnstall(struct fusb300 *fusb300, u8 ep)
+{
+ fusb300_enable_bit(fusb300, FUSB300_OFFSET_EPSET0(ep),
+ FUSB300_EPSET0_STL);
+}
+
+static void fusb300_clear_epnstall(struct fusb300 *fusb300, u8 ep)
+{
+ u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET0(ep));
+
+ if (reg & FUSB300_EPSET0_STL) {
+ printk(KERN_DEBUG "EP%d stall... Clear!!\n", ep);
+ reg &= ~FUSB300_EPSET0_STL;
+ iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET0(ep));
+ }
+}
+
+static void ep0_queue(struct fusb300_ep *ep, struct fusb300_request *req)
+{
+ if (ep->fusb300->ep0_dir) { /* if IN */
+ if (req->req.length) {
+ fusb300_wrcxf(ep, req);
+ } else
+ printk(KERN_DEBUG "%s : req->req.length = 0x%x\n",
+ __func__, req->req.length);
+ if ((req->req.length == req->req.actual) ||
+ (req->req.actual < ep->ep.maxpacket))
+ done(ep, req, 0);
+ } else { /* OUT */
+ if (!req->req.length)
+ done(ep, req, 0);
+ else
+ fusb300_enable_bit(ep->fusb300, FUSB300_OFFSET_IGER1,
+ FUSB300_IGER1_CX_OUT_INT);
+ }
+}
+
+static int fusb300_queue(struct usb_ep *_ep, struct usb_request *_req,
+ gfp_t gfp_flags)
+{
+ struct fusb300_ep *ep;
+ struct fusb300_request *req;
+ unsigned long flags;
+ int request = 0;
+
+ ep = container_of(_ep, struct fusb300_ep, ep);
+ req = container_of(_req, struct fusb300_request, req);
+
+ if (ep->fusb300->gadget.speed == USB_SPEED_UNKNOWN)
+ return -ESHUTDOWN;
+
+ spin_lock_irqsave(&ep->fusb300->lock, flags);
+
+ if (list_empty(&ep->queue))
+ request = 1;
+
+ list_add_tail(&req->queue, &ep->queue);
+
+ req->req.actual = 0;
+ req->req.status = -EINPROGRESS;
+
+ if (ep->desc == NULL) /* ep0 */
+ ep0_queue(ep, req);
+ else if (request && !ep->stall)
+ enable_fifo_int(ep);
+
+ spin_unlock_irqrestore(&ep->fusb300->lock, flags);
+
+ return 0;
+}
+
+static int fusb300_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+ struct fusb300_ep *ep;
+ struct fusb300_request *req;
+ unsigned long flags;
+
+ ep = container_of(_ep, struct fusb300_ep, ep);
+ req = container_of(_req, struct fusb300_request, req);
+
+ spin_lock_irqsave(&ep->fusb300->lock, flags);
+ if (!list_empty(&ep->queue))
+ done(ep, req, -ECONNRESET);
+ spin_unlock_irqrestore(&ep->fusb300->lock, flags);
+
+ return 0;
+}
+
+static int fusb300_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedge)
+{
+ struct fusb300_ep *ep;
+ struct fusb300 *fusb300;
+ unsigned long flags;
+ int ret = 0;
+
+ ep = container_of(_ep, struct fusb300_ep, ep);
+
+ fusb300 = ep->fusb300;
+
+ spin_lock_irqsave(&ep->fusb300->lock, flags);
+
+ if (!list_empty(&ep->queue)) {
+ ret = -EAGAIN;
+ goto out;
+ }
+
+ if (value) {
+ fusb300_set_epnstall(fusb300, ep->epnum);
+ ep->stall = 1;
+ if (wedge)
+ ep->wedged = 1;
+ } else {
+ fusb300_clear_epnstall(fusb300, ep->epnum);
+ ep->stall = 0;
+ ep->wedged = 0;
+ }
+
+out:
+ spin_unlock_irqrestore(&ep->fusb300->lock, flags);
+ return ret;
+}
+
+static int fusb300_set_halt(struct usb_ep *_ep, int value)
+{
+ return fusb300_set_halt_and_wedge(_ep, value, 0);
+}
+
+static int fusb300_set_wedge(struct usb_ep *_ep)
+{
+ return fusb300_set_halt_and_wedge(_ep, 1, 1);
+}
+
+static void fusb300_fifo_flush(struct usb_ep *_ep)
+{
+}
+
+static struct usb_ep_ops fusb300_ep_ops = {
+ .enable = fusb300_enable,
+ .disable = fusb300_disable,
+
+ .alloc_request = fusb300_alloc_request,
+ .free_request = fusb300_free_request,
+
+ .queue = fusb300_queue,
+ .dequeue = fusb300_dequeue,
+
+ .set_halt = fusb300_set_halt,
+ .fifo_flush = fusb300_fifo_flush,
+ .set_wedge = fusb300_set_wedge,
+};
+
+/*****************************************************************************/
+static void fusb300_clear_int(struct fusb300 *fusb300, u32 offset,
+ u32 value)
+{
+ iowrite32(value, fusb300->reg + offset);
+}
+
+static void fusb300_reset(void)
+{
+}
+
+static void fusb300_set_cxstall(struct fusb300 *fusb300)
+{
+ fusb300_enable_bit(fusb300, FUSB300_OFFSET_CSR,
+ FUSB300_CSR_STL);
+}
+
+static void fusb300_set_cxdone(struct fusb300 *fusb300)
+{
+ fusb300_enable_bit(fusb300, FUSB300_OFFSET_CSR,
+ FUSB300_CSR_DONE);
+}
+
+/* read data from cx fifo */
+void fusb300_rdcxf(struct fusb300 *fusb300,
+ u8 *buffer, u32 length)
+{
+ int i = 0;
+ u8 *tmp;
+ u32 data;
+
+ tmp = buffer;
+
+ for (i = (length >> 2); i > 0; i--) {
+ data = ioread32(fusb300->reg + FUSB300_OFFSET_CXPORT);
+ printk(KERN_DEBUG " 0x%x\n", data);
+ *tmp = data & 0xFF;
+ *(tmp + 1) = (data >> 8) & 0xFF;
+ *(tmp + 2) = (data >> 16) & 0xFF;
+ *(tmp + 3) = (data >> 24) & 0xFF;
+ tmp = tmp + 4;
+ }
+
+ switch (length % 4) {
+ case 1:
+ data = ioread32(fusb300->reg + FUSB300_OFFSET_CXPORT);
+ printk(KERN_DEBUG " 0x%x\n", data);
+ *tmp = data & 0xFF;
+ break;
+ case 2:
+ data = ioread32(fusb300->reg + FUSB300_OFFSET_CXPORT);
+ printk(KERN_DEBUG " 0x%x\n", data);
+ *tmp = data & 0xFF;
+ *(tmp + 1) = (data >> 8) & 0xFF;
+ break;
+ case 3:
+ data = ioread32(fusb300->reg + FUSB300_OFFSET_CXPORT);
+ printk(KERN_DEBUG " 0x%x\n", data);
+ *tmp = data & 0xFF;
+ *(tmp + 1) = (data >> 8) & 0xFF;
+ *(tmp + 2) = (data >> 16) & 0xFF;
+ break;
+ default:
+ break;
+ }
+}
+
+#if 0
+static void fusb300_dbg_fifo(struct fusb300_ep *ep,
+ u8 entry, u16 length)
+{
+ u32 reg;
+ u32 i = 0;
+ u32 j = 0;
+
+ reg = ioread32(ep->fusb300->reg + FUSB300_OFFSET_GTM);
+ reg &= ~(FUSB300_GTM_TST_EP_ENTRY(0xF) |
+ FUSB300_GTM_TST_EP_NUM(0xF) | FUSB300_GTM_TST_FIFO_DEG);
+ reg |= (FUSB300_GTM_TST_EP_ENTRY(entry) |
+ FUSB300_GTM_TST_EP_NUM(ep->epnum) | FUSB300_GTM_TST_FIFO_DEG);
+ iowrite32(reg, ep->fusb300->reg + FUSB300_OFFSET_GTM);
+
+ for (i = 0; i < (length >> 2); i++) {
+ if (i * 4 == 1024)
+ break;
+ reg = ioread32(ep->fusb300->reg +
+ FUSB300_OFFSET_BUFDBG_START + i * 4);
+ printk(KERN_DEBUG" 0x%-8x", reg);
+ j++;
+ if ((j % 4) == 0)
+ printk(KERN_DEBUG "\n");
+ }
+
+ if (length % 4) {
+ reg = ioread32(ep->fusb300->reg +
+ FUSB300_OFFSET_BUFDBG_START + i * 4);
+ printk(KERN_DEBUG " 0x%x\n", reg);
+ }
+
+ if ((j % 4) != 0)
+ printk(KERN_DEBUG "\n");
+
+ fusb300_disable_bit(ep->fusb300, FUSB300_OFFSET_GTM,
+ FUSB300_GTM_TST_FIFO_DEG);
+}
+
+static void fusb300_cmp_dbg_fifo(struct fusb300_ep *ep,
+ u8 entry, u16 length, u8 *golden)
+{
+ u32 reg;
+ u32 i = 0;
+ u32 golden_value;
+ u8 *tmp;
+
+ tmp = golden;
+
+ printk(KERN_DEBUG "fusb300_cmp_dbg_fifo (entry %d) : start\n", entry);
+
+ reg = ioread32(ep->fusb300->reg + FUSB300_OFFSET_GTM);
+ reg &= ~(FUSB300_GTM_TST_EP_ENTRY(0xF) |
+ FUSB300_GTM_TST_EP_NUM(0xF) | FUSB300_GTM_TST_FIFO_DEG);
+ reg |= (FUSB300_GTM_TST_EP_ENTRY(entry) |
+ FUSB300_GTM_TST_EP_NUM(ep->epnum) | FUSB300_GTM_TST_FIFO_DEG);
+ iowrite32(reg, ep->fusb300->reg + FUSB300_OFFSET_GTM);
+
+ for (i = 0; i < (length >> 2); i++) {
+ if (i * 4 == 1024)
+ break;
+ golden_value = *tmp | *(tmp + 1) << 8 |
+ *(tmp + 2) << 16 | *(tmp + 3) << 24;
+
+ reg = ioread32(ep->fusb300->reg +
+ FUSB300_OFFSET_BUFDBG_START + i*4);
+
+ if (reg != golden_value) {
+ printk(KERN_DEBUG "0x%x : ", (u32)(ep->fusb300->reg +
+ FUSB300_OFFSET_BUFDBG_START + i*4));
+ printk(KERN_DEBUG " golden = 0x%x, reg = 0x%x\n",
+ golden_value, reg);
+ }
+ tmp += 4;
+ }
+
+ switch (length % 4) {
+ case 1:
+ golden_value = *tmp;
+ case 2:
+ golden_value = *tmp | *(tmp + 1) << 8;
+ case 3:
+ golden_value = *tmp | *(tmp + 1) << 8 | *(tmp + 2) << 16;
+ default:
+ break;
+
+ reg = ioread32(ep->fusb300->reg + FUSB300_OFFSET_BUFDBG_START + i*4);
+ if (reg != golden_value) {
+ printk(KERN_DEBUG "0x%x:", (u32)(ep->fusb300->reg +
+ FUSB300_OFFSET_BUFDBG_START + i*4));
+ printk(KERN_DEBUG " golden = 0x%x, reg = 0x%x\n",
+ golden_value, reg);
+ }
+ }
+
+ printk(KERN_DEBUG "fusb300_cmp_dbg_fifo : end\n");
+ fusb300_disable_bit(ep->fusb300, FUSB300_OFFSET_GTM,
+ FUSB300_GTM_TST_FIFO_DEG);
+}
+#endif
+
+static void fusb300_rdfifo(struct fusb300_ep *ep,
+ struct fusb300_request *req,
+ u32 length)
+{
+ int i = 0;
+ u8 *tmp;
+ u32 data, reg;
+ struct fusb300 *fusb300 = ep->fusb300;
+
+ tmp = req->req.buf + req->req.actual;
+ req->req.actual += length;
+
+ if (req->req.actual > req->req.length)
+ printk(KERN_DEBUG "req->req.actual > req->req.length\n");
+
+ for (i = (length >> 2); i > 0; i--) {
+ data = ioread32(fusb300->reg +
+ FUSB300_OFFSET_EPPORT(ep->epnum));
+ *tmp = data & 0xFF;
+ *(tmp + 1) = (data >> 8) & 0xFF;
+ *(tmp + 2) = (data >> 16) & 0xFF;
+ *(tmp + 3) = (data >> 24) & 0xFF;
+ tmp = tmp + 4;
+ }
+
+ switch (length % 4) {
+ case 1:
+ data = ioread32(fusb300->reg +
+ FUSB300_OFFSET_EPPORT(ep->epnum));
+ *tmp = data & 0xFF;
+ break;
+ case 2:
+ data = ioread32(fusb300->reg +
+ FUSB300_OFFSET_EPPORT(ep->epnum));
+ *tmp = data & 0xFF;
+ *(tmp + 1) = (data >> 8) & 0xFF;
+ break;
+ case 3:
+ data = ioread32(fusb300->reg +
+ FUSB300_OFFSET_EPPORT(ep->epnum));
+ *tmp = data & 0xFF;
+ *(tmp + 1) = (data >> 8) & 0xFF;
+ *(tmp + 2) = (data >> 16) & 0xFF;
+ break;
+ default:
+ break;
+ }
+
+ do {
+ reg = ioread32(fusb300->reg + FUSB300_OFFSET_IGR1);
+ reg &= FUSB300_IGR1_SYNF0_EMPTY_INT;
+ if (i)
+ printk(KERN_INFO "sync fifo is not empty!\n");
+ i++;
+ } while (!reg);
+}
+
+/* write data to fifo */
+static void fusb300_wrfifo(struct fusb300_ep *ep,
+ struct fusb300_request *req)
+{
+ int i = 0;
+ u8 *tmp;
+ u32 data, reg;
+ struct fusb300 *fusb300 = ep->fusb300;
+
+ tmp = req->req.buf;
+ req->req.actual = req->req.length;
+
+ for (i = (req->req.length >> 2); i > 0; i--) {
+ data = *tmp | *(tmp + 1) << 8 |
+ *(tmp + 2) << 16 | *(tmp + 3) << 24;
+
+ iowrite32(data, fusb300->reg +
+ FUSB300_OFFSET_EPPORT(ep->epnum));
+ tmp += 4;
+ }
+
+ switch (req->req.length % 4) {
+ case 1:
+ data = *tmp;
+ iowrite32(data, fusb300->reg +
+ FUSB300_OFFSET_EPPORT(ep->epnum));
+ break;
+ case 2:
+ data = *tmp | *(tmp + 1) << 8;
+ iowrite32(data, fusb300->reg +
+ FUSB300_OFFSET_EPPORT(ep->epnum));
+ break;
+ case 3:
+ data = *tmp | *(tmp + 1) << 8 | *(tmp + 2) << 16;
+ iowrite32(data, fusb300->reg +
+ FUSB300_OFFSET_EPPORT(ep->epnum));
+ break;
+ default:
+ break;
+ }
+
+ do {
+ reg = ioread32(fusb300->reg + FUSB300_OFFSET_IGR1);
+ reg &= FUSB300_IGR1_SYNF0_EMPTY_INT;
+ if (i)
+ printk(KERN_INFO"sync fifo is not empty!\n");
+ i++;
+ } while (!reg);
+}
+
+static u8 fusb300_get_epnstall(struct fusb300 *fusb300, u8 ep)
+{
+ u8 value;
+ u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET0(ep));
+
+ value = reg & FUSB300_EPSET0_STL;
+
+ return value;
+}
+
+static u8 fusb300_get_cxstall(struct fusb300 *fusb300)
+{
+ u8 value;
+ u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_CSR);
+
+ value = (reg & FUSB300_CSR_STL) >> 1;
+
+ return value;
+}
+
+static void request_error(struct fusb300 *fusb300)
+{
+ fusb300_set_cxstall(fusb300);
+ printk(KERN_DEBUG "request error!!\n");
+}
+
+static void get_status(struct fusb300 *fusb300, struct usb_ctrlrequest *ctrl)
+__releases(fusb300->lock)
+__acquires(fusb300->lock)
+{
+ u8 ep;
+ u16 status = 0;
+ u16 w_index = ctrl->wIndex;
+
+ switch (ctrl->bRequestType & USB_RECIP_MASK) {
+ case USB_RECIP_DEVICE:
+ status = 1 << USB_DEVICE_SELF_POWERED;
+ break;
+ case USB_RECIP_INTERFACE:
+ status = 0;
+ break;
+ case USB_RECIP_ENDPOINT:
+ ep = w_index & USB_ENDPOINT_NUMBER_MASK;
+ if (ep) {
+ if (fusb300_get_epnstall(fusb300, ep))
+ status = 1 << USB_ENDPOINT_HALT;
+ } else {
+ if (fusb300_get_cxstall(fusb300))
+ status = 0;
+ }
+ break;
+
+ default:
+ request_error(fusb300);
+ return; /* exit */
+ }
+
+ fusb300->ep0_data = cpu_to_le16(status);
+ fusb300->ep0_req->buf = &fusb300->ep0_data;
+ fusb300->ep0_req->length = 2;
+
+ spin_unlock(&fusb300->lock);
+ fusb300_queue(fusb300->gadget.ep0, fusb300->ep0_req, GFP_KERNEL);
+ spin_lock(&fusb300->lock);
+}
+
+static void set_feature(struct fusb300 *fusb300, struct usb_ctrlrequest *ctrl)
+{
+ u8 ep;
+
+ switch (ctrl->bRequestType & USB_RECIP_MASK) {
+ case USB_RECIP_DEVICE:
+ fusb300_set_cxdone(fusb300);
+ break;
+ case USB_RECIP_INTERFACE:
+ fusb300_set_cxdone(fusb300);
+ break;
+ case USB_RECIP_ENDPOINT: {
+ u16 w_index = le16_to_cpu(ctrl->wIndex);
+
+ ep = w_index & USB_ENDPOINT_NUMBER_MASK;
+ if (ep)
+ fusb300_set_epnstall(fusb300, ep);
+ else
+ fusb300_set_cxstall(fusb300);
+ fusb300_set_cxdone(fusb300);
+ }
+ break;
+ default:
+ request_error(fusb300);
+ break;
+ }
+}
+
+static void fusb300_clear_seqnum(struct fusb300 *fusb300, u8 ep)
+{
+ fusb300_enable_bit(fusb300, FUSB300_OFFSET_EPSET0(ep),
+ FUSB300_EPSET0_CLRSEQNUM);
+}
+
+static void clear_feature(struct fusb300 *fusb300, struct usb_ctrlrequest *ctrl)
+{
+ struct fusb300_ep *ep =
+ fusb300->ep[ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK];
+
+ switch (ctrl->bRequestType & USB_RECIP_MASK) {
+ case USB_RECIP_DEVICE:
+ fusb300_set_cxdone(fusb300);
+ break;
+ case USB_RECIP_INTERFACE:
+ fusb300_set_cxdone(fusb300);
+ break;
+ case USB_RECIP_ENDPOINT:
+ if (ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK) {
+ if (ep->wedged) {
+ fusb300_set_cxdone(fusb300);
+ break;
+ }
+ if (ep->stall) {
+ ep->stall = 0;
+ fusb300_clear_seqnum(fusb300, ep->epnum);
+ fusb300_clear_epnstall(fusb300, ep->epnum);
+ if (!list_empty(&ep->queue))
+ enable_fifo_int(ep);
+ }
+ }
+ fusb300_set_cxdone(fusb300);
+ break;
+ default:
+ request_error(fusb300);
+ break;
+ }
+}
+
+static void fusb300_set_dev_addr(struct fusb300 *fusb300, u16 addr)
+{
+ u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_DAR);
+
+ reg &= ~FUSB300_DAR_DRVADDR_MSK;
+ reg |= FUSB300_DAR_DRVADDR(addr);
+
+ iowrite32(reg, fusb300->reg + FUSB300_OFFSET_DAR);
+}
+
+static void set_address(struct fusb300 *fusb300, struct usb_ctrlrequest *ctrl)
+{
+ if (ctrl->wValue >= 0x0100)
+ request_error(fusb300);
+ else {
+ fusb300_set_dev_addr(fusb300, ctrl->wValue);
+ fusb300_set_cxdone(fusb300);
+ }
+}
+
+#define UVC_COPY_DESCRIPTORS(mem, src) \
+ do { \
+ const struct usb_descriptor_header * const *__src; \
+ for (__src = src; *__src; ++__src) { \
+ memcpy(mem, *__src, (*__src)->bLength); \
+ mem += (*__src)->bLength; \
+ } \
+ } while (0)
+
+static void fusb300_ep0_complete(struct usb_ep *ep,
+ struct usb_request *req)
+{
+}
+
+static int setup_packet(struct fusb300 *fusb300, struct usb_ctrlrequest *ctrl)
+{
+ u8 *p = (u8 *)ctrl;
+ u8 ret = 0;
+ u8 i = 0;
+
+ fusb300_rdcxf(fusb300, p, 8);
+ fusb300->ep0_dir = ctrl->bRequestType & USB_DIR_IN;
+ fusb300->ep0_length = ctrl->wLength;
+
+ /* check request */
+ if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) {
+ switch (ctrl->bRequest) {
+ case USB_REQ_GET_STATUS:
+ get_status(fusb300, ctrl);
+ break;
+ case USB_REQ_CLEAR_FEATURE:
+ clear_feature(fusb300, ctrl);
+ break;
+ case USB_REQ_SET_FEATURE:
+ set_feature(fusb300, ctrl);
+ break;
+ case USB_REQ_SET_ADDRESS:
+ set_address(fusb300, ctrl);
+ break;
+ case USB_REQ_SET_CONFIGURATION:
+ fusb300_enable_bit(fusb300, FUSB300_OFFSET_DAR,
+ FUSB300_DAR_SETCONFG);
+ /* clear sequence number */
+ for (i = 1; i <= FUSB300_MAX_NUM_EP; i++)
+ fusb300_clear_seqnum(fusb300, i);
+ fusb300->reenum = 1;
+ ret = 1;
+ break;
+ default:
+ ret = 1;
+ break;
+ }
+ } else
+ ret = 1;
+
+ return ret;
+}
+
+static void fusb300_set_ep_bycnt(struct fusb300_ep *ep, u32 bycnt)
+{
+ struct fusb300 *fusb300 = ep->fusb300;
+ u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPFFR(ep->epnum));
+
+ reg &= ~FUSB300_FFR_BYCNT;
+ reg |= bycnt & FUSB300_FFR_BYCNT;
+
+ iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPFFR(ep->epnum));
+}
+
+static void done(struct fusb300_ep *ep, struct fusb300_request *req,
+ int status)
+{
+ list_del_init(&req->queue);
+
+ /* don't modify queue heads during completion callback */
+ if (ep->fusb300->gadget.speed == USB_SPEED_UNKNOWN)
+ req->req.status = -ESHUTDOWN;
+ else
+ req->req.status = status;
+
+ spin_unlock(&ep->fusb300->lock);
+ req->req.complete(&ep->ep, &req->req);
+ spin_lock(&ep->fusb300->lock);
+
+ if (ep->epnum) {
+ disable_fifo_int(ep);
+ if (!list_empty(&ep->queue))
+ enable_fifo_int(ep);
+ } else
+ fusb300_set_cxdone(ep->fusb300);
+}
+
+void fusb300_fill_idma_prdtbl(struct fusb300_ep *ep,
+ struct fusb300_request *req)
+{
+ u32 value;
+ u32 reg;
+
+ /* wait SW owner */
+ do {
+ reg = ioread32(ep->fusb300->reg +
+ FUSB300_OFFSET_EPPRD_W0(ep->epnum));
+ reg &= FUSB300_EPPRD0_H;
+ } while (reg);
+
+ iowrite32((u32) req->req.buf, ep->fusb300->reg +
+ FUSB300_OFFSET_EPPRD_W1(ep->epnum));
+
+ value = FUSB300_EPPRD0_BTC(req->req.length) | FUSB300_EPPRD0_H |
+ FUSB300_EPPRD0_F | FUSB300_EPPRD0_L | FUSB300_EPPRD0_I;
+ iowrite32(value, ep->fusb300->reg + FUSB300_OFFSET_EPPRD_W0(ep->epnum));
+
+ iowrite32(0x0, ep->fusb300->reg + FUSB300_OFFSET_EPPRD_W2(ep->epnum));
+
+ fusb300_enable_bit(ep->fusb300, FUSB300_OFFSET_EPPRDRDY,
+ FUSB300_EPPRDR_EP_PRD_RDY(ep->epnum));
+}
+
+static void fusb300_wait_idma_finished(struct fusb300_ep *ep)
+{
+ u32 reg;
+
+ do {
+ reg = ioread32(ep->fusb300->reg + FUSB300_OFFSET_IGR1);
+ if ((reg & FUSB300_IGR1_VBUS_CHG_INT) ||
+ (reg & FUSB300_IGR1_WARM_RST_INT) ||
+ (reg & FUSB300_IGR1_HOT_RST_INT) ||
+ (reg & FUSB300_IGR1_USBRST_INT)
+ )
+ goto IDMA_RESET;
+ reg = ioread32(ep->fusb300->reg + FUSB300_OFFSET_IGR0);
+ reg &= FUSB300_IGR0_EPn_PRD_INT(ep->epnum);
+ } while (!reg);
+
+ fusb300_clear_int(ep->fusb300, FUSB300_OFFSET_IGR0,
+ FUSB300_IGR0_EPn_PRD_INT(ep->epnum));
+IDMA_RESET:
+ fusb300_clear_int(ep->fusb300, FUSB300_OFFSET_IGER0,
+ FUSB300_IGER0_EEPn_PRD_INT(ep->epnum));
+}
+
+static void fusb300_set_idma(struct fusb300_ep *ep,
+ struct fusb300_request *req)
+{
+ dma_addr_t d;
+ u8 *tmp = NULL;
+
+ d = dma_map_single(NULL, req->req.buf, req->req.length, DMA_TO_DEVICE);
+
+ if (dma_mapping_error(NULL, d)) {
+ kfree(req->req.buf);
+ printk(KERN_DEBUG "dma_mapping_error\n");
+ }
+
+ dma_sync_single_for_device(NULL, d, req->req.length, DMA_TO_DEVICE);
+
+ fusb300_enable_bit(ep->fusb300, FUSB300_OFFSET_IGER0,
+ FUSB300_IGER0_EEPn_PRD_INT(ep->epnum));
+
+ tmp = req->req.buf;
+ req->req.buf = (u8 *)d;
+
+ fusb300_fill_idma_prdtbl(ep, req);
+ /* check idma is done */
+ fusb300_wait_idma_finished(ep);
+
+ req->req.buf = tmp;
+
+ if (d)
+ dma_unmap_single(NULL, d, req->req.length, DMA_TO_DEVICE);
+}
+
+static void in_ep_fifo_handler(struct fusb300_ep *ep)
+{
+ struct fusb300_request *req = list_entry(ep->queue.next,
+ struct fusb300_request, queue);
+
+ if (req->req.length) {
+#if 0
+ fusb300_set_ep_bycnt(ep, req->req.length);
+ fusb300_wrfifo(ep, req);
+#else
+ fusb300_set_idma(ep, req);
+#endif
+ }
+ done(ep, req, 0);
+}
+
+static void out_ep_fifo_handler(struct fusb300_ep *ep)
+{
+ struct fusb300 *fusb300 = ep->fusb300;
+ struct fusb300_request *req = list_entry(ep->queue.next,
+ struct fusb300_request, queue);
+ u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPFFR(ep->epnum));
+ u32 length = reg & FUSB300_FFR_BYCNT;
+
+ fusb300_rdfifo(ep, req, length);
+
+ /* finish out transfer */
+ if ((req->req.length == req->req.actual) || (length < ep->ep.maxpacket))
+ done(ep, req, 0);
+}
+
+static void check_device_mode(struct fusb300 *fusb300)
+{
+ u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_GCR);
+
+ switch (reg & FUSB300_GCR_DEVEN_MSK) {
+ case FUSB300_GCR_DEVEN_SS:
+ fusb300->gadget.speed = USB_SPEED_SUPER;
+ break;
+ case FUSB300_GCR_DEVEN_HS:
+ fusb300->gadget.speed = USB_SPEED_HIGH;
+ break;
+ case FUSB300_GCR_DEVEN_FS:
+ fusb300->gadget.speed = USB_SPEED_FULL;
+ break;
+ default:
+ fusb300->gadget.speed = USB_SPEED_UNKNOWN;
+ break;
+ }
+ printk(KERN_INFO "dev_mode = %d\n", (reg & FUSB300_GCR_DEVEN_MSK));
+}
+
+
+static void fusb300_ep0out(struct fusb300 *fusb300)
+{
+ struct fusb300_ep *ep = fusb300->ep[0];
+ u32 reg;
+
+ if (!list_empty(&ep->queue)) {
+ struct fusb300_request *req;
+
+ req = list_first_entry(&ep->queue,
+ struct fusb300_request, queue);
+ if (req->req.length)
+ fusb300_rdcxf(ep->fusb300, req->req.buf,
+ req->req.length);
+ done(ep, req, 0);
+ reg = ioread32(fusb300->reg + FUSB300_OFFSET_IGER1);
+ reg &= ~FUSB300_IGER1_CX_OUT_INT;
+ iowrite32(reg, fusb300->reg + FUSB300_OFFSET_IGER1);
+ } else
+ pr_err("%s : empty queue\n", __func__);
+}
+
+static void fusb300_ep0in(struct fusb300 *fusb300)
+{
+ struct fusb300_request *req;
+ struct fusb300_ep *ep = fusb300->ep[0];
+
+ if ((!list_empty(&ep->queue)) && (fusb300->ep0_dir)) {
+ req = list_entry(ep->queue.next,
+ struct fusb300_request, queue);
+ if (req->req.length)
+ fusb300_wrcxf(ep, req);
+ if ((req->req.length - req->req.actual) < ep->ep.maxpacket)
+ done(ep, req, 0);
+ } else
+ fusb300_set_cxdone(fusb300);
+}
+
+static void fusb300_grp2_handler(void)
+{
+}
+
+static void fusb300_grp3_handler(void)
+{
+}
+
+static void fusb300_grp4_handler(void)
+{
+}
+
+static void fusb300_grp5_handler(void)
+{
+}
+
+static irqreturn_t fusb300_irq(int irq, void *_fusb300)
+{
+ struct fusb300 *fusb300 = _fusb300;
+ u32 int_grp1 = ioread32(fusb300->reg + FUSB300_OFFSET_IGR1);
+ u32 int_grp1_en = ioread32(fusb300->reg + FUSB300_OFFSET_IGER1);
+ u32 int_grp0 = ioread32(fusb300->reg + FUSB300_OFFSET_IGR0);
+ u32 int_grp0_en = ioread32(fusb300->reg + FUSB300_OFFSET_IGER0);
+ struct usb_ctrlrequest ctrl;
+ u8 in;
+ u32 reg;
+ int i;
+
+ spin_lock(&fusb300->lock);
+
+ int_grp1 &= int_grp1_en;
+ int_grp0 &= int_grp0_en;
+
+ if (int_grp1 & FUSB300_IGR1_WARM_RST_INT) {
+ fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1,
+ FUSB300_IGR1_WARM_RST_INT);
+ printk(KERN_INFO"fusb300_warmreset\n");
+ fusb300_reset();
+ }
+
+ if (int_grp1 & FUSB300_IGR1_HOT_RST_INT) {
+ fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1,
+ FUSB300_IGR1_HOT_RST_INT);
+ printk(KERN_INFO"fusb300_hotreset\n");
+ fusb300_reset();
+ }
+
+ if (int_grp1 & FUSB300_IGR1_USBRST_INT) {
+ fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1,
+ FUSB300_IGR1_USBRST_INT);
+ fusb300_reset();
+ }
+ /* COMABT_INT has a highest priority */
+
+ if (int_grp1 & FUSB300_IGR1_CX_COMABT_INT) {
+ fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1,
+ FUSB300_IGR1_CX_COMABT_INT);
+ printk(KERN_INFO"fusb300_ep0abt\n");
+ }
+
+ if (int_grp1 & FUSB300_IGR1_VBUS_CHG_INT) {
+ fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1,
+ FUSB300_IGR1_VBUS_CHG_INT);
+ printk(KERN_INFO"fusb300_vbus_change\n");
+ }
+
+ if (int_grp1 & FUSB300_IGR1_U3_EXIT_FAIL_INT) {
+ fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1,
+ FUSB300_IGR1_U3_EXIT_FAIL_INT);
+ }
+
+ if (int_grp1 & FUSB300_IGR1_U2_EXIT_FAIL_INT) {
+ fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1,
+ FUSB300_IGR1_U2_EXIT_FAIL_INT);
+ }
+
+ if (int_grp1 & FUSB300_IGR1_U1_EXIT_FAIL_INT) {
+ fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1,
+ FUSB300_IGR1_U1_EXIT_FAIL_INT);
+ }
+
+ if (int_grp1 & FUSB300_IGR1_U2_ENTRY_FAIL_INT) {
+ fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1,
+ FUSB300_IGR1_U2_ENTRY_FAIL_INT);
+ }
+
+ if (int_grp1 & FUSB300_IGR1_U1_ENTRY_FAIL_INT) {
+ fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1,
+ FUSB300_IGR1_U1_ENTRY_FAIL_INT);
+ }
+
+ if (int_grp1 & FUSB300_IGR1_U3_EXIT_INT) {
+ fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1,
+ FUSB300_IGR1_U3_EXIT_INT);
+ printk(KERN_INFO "FUSB300_IGR1_U3_EXIT_INT\n");
+ }
+
+ if (int_grp1 & FUSB300_IGR1_U2_EXIT_INT) {
+ fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1,
+ FUSB300_IGR1_U2_EXIT_INT);
+ printk(KERN_INFO "FUSB300_IGR1_U2_EXIT_INT\n");
+ }
+
+ if (int_grp1 & FUSB300_IGR1_U1_EXIT_INT) {
+ fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1,
+ FUSB300_IGR1_U1_EXIT_INT);
+ printk(KERN_INFO "FUSB300_IGR1_U1_EXIT_INT\n");
+ }
+
+ if (int_grp1 & FUSB300_IGR1_U3_ENTRY_INT) {
+ fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1,
+ FUSB300_IGR1_U3_ENTRY_INT);
+ printk(KERN_INFO "FUSB300_IGR1_U3_ENTRY_INT\n");
+ fusb300_enable_bit(fusb300, FUSB300_OFFSET_SSCR1,
+ FUSB300_SSCR1_GO_U3_DONE);
+ }
+
+ if (int_grp1 & FUSB300_IGR1_U2_ENTRY_INT) {
+ fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1,
+ FUSB300_IGR1_U2_ENTRY_INT);
+ printk(KERN_INFO "FUSB300_IGR1_U2_ENTRY_INT\n");
+ }
+
+ if (int_grp1 & FUSB300_IGR1_U1_ENTRY_INT) {
+ fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1,
+ FUSB300_IGR1_U1_ENTRY_INT);
+ printk(KERN_INFO "FUSB300_IGR1_U1_ENTRY_INT\n");
+ }
+
+ if (int_grp1 & FUSB300_IGR1_RESM_INT) {
+ fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1,
+ FUSB300_IGR1_RESM_INT);
+ printk(KERN_INFO "fusb300_resume\n");
+ }
+
+ if (int_grp1 & FUSB300_IGR1_SUSP_INT) {
+ fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1,
+ FUSB300_IGR1_SUSP_INT);
+ printk(KERN_INFO "fusb300_suspend\n");
+ }
+
+ if (int_grp1 & FUSB300_IGR1_HS_LPM_INT) {
+ fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1,
+ FUSB300_IGR1_HS_LPM_INT);
+ printk(KERN_INFO "fusb300_HS_LPM_INT\n");
+ }
+
+ if (int_grp1 & FUSB300_IGR1_DEV_MODE_CHG_INT) {
+ fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1,
+ FUSB300_IGR1_DEV_MODE_CHG_INT);
+ check_device_mode(fusb300);
+ }
+
+ if (int_grp1 & FUSB300_IGR1_CX_COMFAIL_INT) {
+ fusb300_set_cxstall(fusb300);
+ printk(KERN_INFO "fusb300_ep0fail\n");
+ }
+
+ if (int_grp1 & FUSB300_IGR1_CX_SETUP_INT) {
+ printk(KERN_INFO "fusb300_ep0setup\n");
+ if (setup_packet(fusb300, &ctrl)) {
+ spin_unlock(&fusb300->lock);
+ if (fusb300->driver->setup(&fusb300->gadget, &ctrl) < 0)
+ fusb300_set_cxstall(fusb300);
+ spin_lock(&fusb300->lock);
+ }
+ }
+
+ if (int_grp1 & FUSB300_IGR1_CX_CMDEND_INT)
+ printk(KERN_INFO "fusb300_cmdend\n");
+
+
+ if (int_grp1 & FUSB300_IGR1_CX_OUT_INT) {
+ printk(KERN_INFO "fusb300_cxout\n");
+ fusb300_ep0out(fusb300);
+ }
+
+ if (int_grp1 & FUSB300_IGR1_CX_IN_INT) {
+ printk(KERN_INFO "fusb300_cxin\n");
+ fusb300_ep0in(fusb300);
+ }
+
+ if (int_grp1 & FUSB300_IGR1_INTGRP5)
+ fusb300_grp5_handler();
+
+ if (int_grp1 & FUSB300_IGR1_INTGRP4)
+ fusb300_grp4_handler();
+
+ if (int_grp1 & FUSB300_IGR1_INTGRP3)
+ fusb300_grp3_handler();
+
+ if (int_grp1 & FUSB300_IGR1_INTGRP2)
+ fusb300_grp2_handler();
+
+ if (int_grp0) {
+ for (i = 1; i < FUSB300_MAX_NUM_EP; i++) {
+ if (int_grp0 & FUSB300_IGR0_EPn_FIFO_INT(i)) {
+ reg = ioread32(fusb300->reg +
+ FUSB300_OFFSET_EPSET1(i));
+ in = (reg & FUSB300_EPSET1_DIRIN) ? 1 : 0;
+ if (in)
+ in_ep_fifo_handler(fusb300->ep[i]);
+ else
+ out_ep_fifo_handler(fusb300->ep[i]);
+ }
+ }
+ }
+
+ spin_unlock(&fusb300->lock);
+
+ return IRQ_HANDLED;
+}
+
+static void fusb300_set_u2_timeout(struct fusb300 *fusb300,
+ u32 time)
+{
+ u32 reg;
+
+ reg = ioread32(fusb300->reg + FUSB300_OFFSET_TT);
+ reg &= ~0xff;
+ reg |= FUSB300_SSCR2_U2TIMEOUT(time);
+
+ iowrite32(reg, fusb300->reg + FUSB300_OFFSET_TT);
+}
+
+static void fusb300_set_u1_timeout(struct fusb300 *fusb300,
+ u32 time)
+{
+ u32 reg;
+
+ reg = ioread32(fusb300->reg + FUSB300_OFFSET_TT);
+ reg &= ~(0xff << 8);
+ reg |= FUSB300_SSCR2_U1TIMEOUT(time);
+
+ iowrite32(reg, fusb300->reg + FUSB300_OFFSET_TT);
+}
+
+static void init_controller(struct fusb300 *fusb300)
+{
+ u32 reg;
+ u32 mask = 0;
+ u32 val = 0;
+
+ /* split on */
+ mask = val = FUSB300_AHBBCR_S0_SPLIT_ON | FUSB300_AHBBCR_S1_SPLIT_ON;
+ reg = ioread32(fusb300->reg + FUSB300_OFFSET_AHBCR);
+ reg &= ~mask;
+ reg |= val;
+ iowrite32(reg, fusb300->reg + FUSB300_OFFSET_AHBCR);
+
+ /* enable high-speed LPM */
+ mask = val = FUSB300_HSCR_HS_LPM_PERMIT;
+ reg = ioread32(fusb300->reg + FUSB300_OFFSET_HSCR);
+ reg &= ~mask;
+ reg |= val;
+ iowrite32(reg, fusb300->reg + FUSB300_OFFSET_HSCR);
+
+ /*set u1 u2 timmer*/
+ fusb300_set_u2_timeout(fusb300, 0xff);
+ fusb300_set_u1_timeout(fusb300, 0xff);
+
+ /* enable all grp1 interrupt */
+ iowrite32(0xcfffff9f, fusb300->reg + FUSB300_OFFSET_IGER1);
+}
+/*------------------------------------------------------------------------*/
+static struct fusb300 *the_controller;
+
+int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
+ int (*bind)(struct usb_gadget *))
+{
+ struct fusb300 *fusb300 = the_controller;
+ int retval;
+
+ if (!driver
+ || driver->speed < USB_SPEED_FULL
+ || !bind
+ || !driver->setup)
+ return -EINVAL;
+
+ if (!fusb300)
+ return -ENODEV;
+
+ if (fusb300->driver)
+ return -EBUSY;
+
+ /* hook up the driver */
+ driver->driver.bus = NULL;
+ fusb300->driver = driver;
+ fusb300->gadget.dev.driver = &driver->driver;
+
+ retval = device_add(&fusb300->gadget.dev);
+ if (retval) {
+ pr_err("device_add error (%d)\n", retval);
+ goto error;
+ }
+
+ retval = bind(&fusb300->gadget);
+ if (retval) {
+ pr_err("bind to driver error (%d)\n", retval);
+ device_del(&fusb300->gadget.dev);
+ goto error;
+ }
+
+ return 0;
+
+error:
+ fusb300->driver = NULL;
+ fusb300->gadget.dev.driver = NULL;
+
+ return retval;
+}
+EXPORT_SYMBOL(usb_gadget_probe_driver);
+
+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+{
+ struct fusb300 *fusb300 = the_controller;
+
+ if (driver != fusb300->driver || !driver->unbind)
+ return -EINVAL;
+
+ driver->unbind(&fusb300->gadget);
+ fusb300->gadget.dev.driver = NULL;
+
+ init_controller(fusb300);
+ device_del(&fusb300->gadget.dev);
+ fusb300->driver = NULL;
+
+ return 0;
+}
+EXPORT_SYMBOL(usb_gadget_unregister_driver);
+/*--------------------------------------------------------------------------*/
+
+static int fusb300_udc_pullup(struct usb_gadget *_gadget, int is_active)
+{
+ return 0;
+}
+
+static struct usb_gadget_ops fusb300_gadget_ops = {
+ .pullup = fusb300_udc_pullup,
+};
+
+static int __exit fusb300_remove(struct platform_device *pdev)
+{
+ struct fusb300 *fusb300 = dev_get_drvdata(&pdev->dev);
+
+ iounmap(fusb300->reg);
+ free_irq(platform_get_irq(pdev, 0), fusb300);
+
+ fusb300_free_request(&fusb300->ep[0]->ep, fusb300->ep0_req);
+ kfree(fusb300);
+
+ return 0;
+}
+
+static int __init fusb300_probe(struct platform_device *pdev)
+{
+ struct resource *res, *ires, *ires1;
+ void __iomem *reg = NULL;
+ struct fusb300 *fusb300 = NULL;
+ struct fusb300_ep *_ep[FUSB300_MAX_NUM_EP];
+ int ret = 0;
+ int i;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ ret = -ENODEV;
+ pr_err("platform_get_resource error.\n");
+ goto clean_up;
+ }
+
+ ires = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!ires) {
+ ret = -ENODEV;
+ dev_err(&pdev->dev,
+ "platform_get_resource IORESOURCE_IRQ error.\n");
+ goto clean_up;
+ }
+
+ ires1 = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
+ if (!ires1) {
+ ret = -ENODEV;
+ dev_err(&pdev->dev,
+ "platform_get_resource IORESOURCE_IRQ 1 error.\n");
+ goto clean_up;
+ }
+
+ reg = ioremap(res->start, resource_size(res));
+ if (reg == NULL) {
+ ret = -ENOMEM;
+ pr_err("ioremap error.\n");
+ goto clean_up;
+ }
+
+ /* initialize udc */
+ fusb300 = kzalloc(sizeof(struct fusb300), GFP_KERNEL);
+ if (fusb300 == NULL) {
+ pr_err("kzalloc error\n");
+ goto clean_up;
+ }
+
+ for (i = 0; i < FUSB300_MAX_NUM_EP; i++) {
+ _ep[i] = kzalloc(sizeof(struct fusb300_ep), GFP_KERNEL);
+ if (_ep[i] == NULL) {
+ pr_err("_ep kzalloc error\n");
+ goto clean_up;
+ }
+ fusb300->ep[i] = _ep[i];
+ }
+
+ spin_lock_init(&fusb300->lock);
+
+ dev_set_drvdata(&pdev->dev, fusb300);
+
+ fusb300->gadget.ops = &fusb300_gadget_ops;
+
+ device_initialize(&fusb300->gadget.dev);
+
+ dev_set_name(&fusb300->gadget.dev, "gadget");
+
+ fusb300->gadget.is_dualspeed = 1;
+ fusb300->gadget.dev.parent = &pdev->dev;
+ fusb300->gadget.dev.dma_mask = pdev->dev.dma_mask;
+ fusb300->gadget.dev.release = pdev->dev.release;
+ fusb300->gadget.name = udc_name;
+ fusb300->reg = reg;
+
+ ret = request_irq(ires->start, fusb300_irq, IRQF_DISABLED | IRQF_SHARED,
+ udc_name, fusb300);
+ if (ret < 0) {
+ pr_err("request_irq error (%d)\n", ret);
+ goto clean_up;
+ }
+
+ ret = request_irq(ires1->start, fusb300_irq,
+ IRQF_DISABLED | IRQF_SHARED, udc_name, fusb300);
+ if (ret < 0) {
+ pr_err("request_irq1 error (%d)\n", ret);
+ goto clean_up;
+ }
+
+ INIT_LIST_HEAD(&fusb300->gadget.ep_list);
+
+ for (i = 0; i < FUSB300_MAX_NUM_EP ; i++) {
+ struct fusb300_ep *ep = fusb300->ep[i];
+
+ if (i != 0) {
+ INIT_LIST_HEAD(&fusb300->ep[i]->ep.ep_list);
+ list_add_tail(&fusb300->ep[i]->ep.ep_list,
+ &fusb300->gadget.ep_list);
+ }
+ ep->fusb300 = fusb300;
+ INIT_LIST_HEAD(&ep->queue);
+ ep->ep.name = fusb300_ep_name[i];
+ ep->ep.ops = &fusb300_ep_ops;
+ ep->ep.maxpacket = HS_BULK_MAX_PACKET_SIZE;
+ }
+ fusb300->ep[0]->ep.maxpacket = HS_CTL_MAX_PACKET_SIZE;
+ fusb300->ep[0]->epnum = 0;
+ fusb300->gadget.ep0 = &fusb300->ep[0]->ep;
+ INIT_LIST_HEAD(&fusb300->gadget.ep0->ep_list);
+
+ the_controller = fusb300;
+
+ fusb300->ep0_req = fusb300_alloc_request(&fusb300->ep[0]->ep,
+ GFP_KERNEL);
+ if (fusb300->ep0_req == NULL)
+ goto clean_up3;
+
+ init_controller(fusb300);
+ dev_info(&pdev->dev, "version %s\n", DRIVER_VERSION);
+
+ return 0;
+
+clean_up3:
+ free_irq(ires->start, fusb300);
+
+clean_up:
+ if (fusb300) {
+ if (fusb300->ep0_req)
+ fusb300_free_request(&fusb300->ep[0]->ep,
+ fusb300->ep0_req);
+ kfree(fusb300);
+ }
+ if (reg)
+ iounmap(reg);
+
+ return ret;
+}
+
+static struct platform_driver fusb300_driver = {
+ .remove = __exit_p(fusb300_remove),
+ .driver = {
+ .name = (char *) udc_name,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init fusb300_udc_init(void)
+{
+ return platform_driver_probe(&fusb300_driver, fusb300_probe);
+}
+
+module_init(fusb300_udc_init);
+
+static void __exit fusb300_udc_cleanup(void)
+{
+ platform_driver_unregister(&fusb300_driver);
+}
+module_exit(fusb300_udc_cleanup);
diff --git a/drivers/usb/gadget/fusb300_udc.h b/drivers/usb/gadget/fusb300_udc.h
new file mode 100644
index 000000000000..f51aa2ef1f90
--- /dev/null
+++ b/drivers/usb/gadget/fusb300_udc.h
@@ -0,0 +1,687 @@
+/*
+ * Fusb300 UDC (USB gadget)
+ *
+ * Copyright (C) 2010 Faraday Technology Corp.
+ *
+ * Author : Yuan-hsin Chen <yhchen@faraday-tech.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
+ *
+ */
+
+
+#ifndef __FUSB300_UDC_H__
+#define __FUSB300_UDC_H_
+
+#include <linux/kernel.h>
+
+#define FUSB300_OFFSET_GCR 0x00
+#define FUSB300_OFFSET_GTM 0x04
+#define FUSB300_OFFSET_DAR 0x08
+#define FUSB300_OFFSET_CSR 0x0C
+#define FUSB300_OFFSET_CXPORT 0x10
+#define FUSB300_OFFSET_EPSET0(n) (0x20 + (n - 1) * 0x30)
+#define FUSB300_OFFSET_EPSET1(n) (0x24 + (n - 1) * 0x30)
+#define FUSB300_OFFSET_EPSET2(n) (0x28 + (n - 1) * 0x30)
+#define FUSB300_OFFSET_EPFFR(n) (0x2c + (n - 1) * 0x30)
+#define FUSB300_OFFSET_EPSTRID(n) (0x40 + (n - 1) * 0x30)
+#define FUSB300_OFFSET_HSPTM 0x300
+#define FUSB300_OFFSET_HSCR 0x304
+#define FUSB300_OFFSET_SSCR0 0x308
+#define FUSB300_OFFSET_SSCR1 0x30C
+#define FUSB300_OFFSET_TT 0x310
+#define FUSB300_OFFSET_DEVNOTF 0x314
+#define FUSB300_OFFSET_DNC1 0x318
+#define FUSB300_OFFSET_CS 0x31C
+#define FUSB300_OFFSET_SOF 0x324
+#define FUSB300_OFFSET_EFCS 0x328
+#define FUSB300_OFFSET_IGR0 0x400
+#define FUSB300_OFFSET_IGR1 0x404
+#define FUSB300_OFFSET_IGR2 0x408
+#define FUSB300_OFFSET_IGR3 0x40C
+#define FUSB300_OFFSET_IGR4 0x410
+#define FUSB300_OFFSET_IGR5 0x414
+#define FUSB300_OFFSET_IGER0 0x420
+#define FUSB300_OFFSET_IGER1 0x424
+#define FUSB300_OFFSET_IGER2 0x428
+#define FUSB300_OFFSET_IGER3 0x42C
+#define FUSB300_OFFSET_IGER4 0x430
+#define FUSB300_OFFSET_IGER5 0x434
+#define FUSB300_OFFSET_DMAHMER 0x500
+#define FUSB300_OFFSET_EPPRDRDY 0x504
+#define FUSB300_OFFSET_DMAEPMR 0x508
+#define FUSB300_OFFSET_DMAENR 0x50C
+#define FUSB300_OFFSET_DMAAPR 0x510
+#define FUSB300_OFFSET_AHBCR 0x514
+#define FUSB300_OFFSET_EPPRD_W0(n) (0x520 + (n - 1) * 0x10)
+#define FUSB300_OFFSET_EPPRD_W1(n) (0x524 + (n - 1) * 0x10)
+#define FUSB300_OFFSET_EPPRD_W2(n) (0x528 + (n - 1) * 0x10)
+#define FUSB300_OFFSET_EPRD_PTR(n) (0x52C + (n - 1) * 0x10)
+#define FUSB300_OFFSET_BUFDBG_START 0x800
+#define FUSB300_OFFSET_BUFDBG_END 0xBFC
+#define FUSB300_OFFSET_EPPORT(n) (0x1010 + (n - 1) * 0x10)
+
+/*
+ * * Global Control Register (offset = 000H)
+ * */
+#define FUSB300_GCR_SF_RST (1 << 8)
+#define FUSB300_GCR_VBUS_STATUS (1 << 7)
+#define FUSB300_GCR_FORCE_HS_SUSP (1 << 6)
+#define FUSB300_GCR_SYNC_FIFO1_CLR (1 << 5)
+#define FUSB300_GCR_SYNC_FIFO0_CLR (1 << 4)
+#define FUSB300_GCR_FIFOCLR (1 << 3)
+#define FUSB300_GCR_GLINTEN (1 << 2)
+#define FUSB300_GCR_DEVEN_FS 0x3
+#define FUSB300_GCR_DEVEN_HS 0x2
+#define FUSB300_GCR_DEVEN_SS 0x1
+#define FUSB300_GCR_DEVDIS 0x0
+#define FUSB300_GCR_DEVEN_MSK 0x3
+
+
+/*
+ * *Global Test Mode (offset = 004H)
+ * */
+#define FUSB300_GTM_TST_DIS_SOFGEN (1 << 16)
+#define FUSB300_GTM_TST_CUR_EP_ENTRY(n) ((n & 0xF) << 12)
+#define FUSB300_GTM_TST_EP_ENTRY(n) ((n & 0xF) << 8)
+#define FUSB300_GTM_TST_EP_NUM(n) ((n & 0xF) << 4)
+#define FUSB300_GTM_TST_FIFO_DEG (1 << 1)
+#define FUSB300_GTM_TSTMODE (1 << 0)
+
+/*
+ * * Device Address Register (offset = 008H)
+ * */
+#define FUSB300_DAR_SETCONFG (1 << 7)
+#define FUSB300_DAR_DRVADDR(x) (x & 0x7F)
+#define FUSB300_DAR_DRVADDR_MSK 0x7F
+
+/*
+ * *Control Transfer Configuration and Status Register
+ * (CX_Config_Status, offset = 00CH)
+ * */
+#define FUSB300_CSR_LEN(x) ((x & 0xFFFF) << 8)
+#define FUSB300_CSR_LEN_MSK (0xFFFF << 8)
+#define FUSB300_CSR_EMP (1 << 4)
+#define FUSB300_CSR_FUL (1 << 3)
+#define FUSB300_CSR_CLR (1 << 2)
+#define FUSB300_CSR_STL (1 << 1)
+#define FUSB300_CSR_DONE (1 << 0)
+
+/*
+ * * EPn Setting 0 (EPn_SET0, offset = 020H+(n-1)*30H, n=1~15 )
+ * */
+#define FUSB300_EPSET0_CLRSEQNUM (1 << 2)
+#define FUSB300_EPSET0_EPn_TX0BYTE (1 << 1)
+#define FUSB300_EPSET0_STL (1 << 0)
+
+/*
+ * * EPn Setting 1 (EPn_SET1, offset = 024H+(n-1)*30H, n=1~15)
+ * */
+#define FUSB300_EPSET1_START_ENTRY(x) ((x & 0xFF) << 24)
+#define FUSB300_EPSET1_START_ENTRY_MSK (0xFF << 24)
+#define FUSB300_EPSET1_FIFOENTRY(x) ((x & 0x1F) << 12)
+#define FUSB300_EPSET1_FIFOENTRY_MSK (0x1f << 12)
+#define FUSB300_EPSET1_INTERVAL(x) ((x & 0x7) << 6)
+#define FUSB300_EPSET1_BWNUM(x) ((x & 0x3) << 4)
+#define FUSB300_EPSET1_TYPEISO (1 << 2)
+#define FUSB300_EPSET1_TYPEBLK (2 << 2)
+#define FUSB300_EPSET1_TYPEINT (3 << 2)
+#define FUSB300_EPSET1_TYPE(x) ((x & 0x3) << 2)
+#define FUSB300_EPSET1_TYPE_MSK (0x3 << 2)
+#define FUSB300_EPSET1_DIROUT (0 << 1)
+#define FUSB300_EPSET1_DIRIN (1 << 1)
+#define FUSB300_EPSET1_DIR(x) ((x & 0x1) << 1)
+#define FUSB300_EPSET1_DIRIN (1 << 1)
+#define FUSB300_EPSET1_DIR_MSK ((0x1) << 1)
+#define FUSB300_EPSET1_ACTDIS 0
+#define FUSB300_EPSET1_ACTEN 1
+
+/*
+ * *EPn Setting 2 (EPn_SET2, offset = 028H+(n-1)*30H, n=1~15)
+ * */
+#define FUSB300_EPSET2_ADDROFS(x) ((x & 0x7FFF) << 16)
+#define FUSB300_EPSET2_ADDROFS_MSK (0x7fff << 16)
+#define FUSB300_EPSET2_MPS(x) (x & 0x7FF)
+#define FUSB300_EPSET2_MPS_MSK 0x7FF
+
+/*
+ * * EPn FIFO Register (offset = 2cH+(n-1)*30H)
+ * */
+#define FUSB300_FFR_RST (1 << 31)
+#define FUSB300_FF_FUL (1 << 30)
+#define FUSB300_FF_EMPTY (1 << 29)
+#define FUSB300_FFR_BYCNT 0x1FFFF
+
+/*
+ * *EPn Stream ID (EPn_STR_ID, offset = 040H+(n-1)*30H, n=1~15)
+ * */
+#define FUSB300_STRID_STREN (1 << 16)
+#define FUSB300_STRID_STRID(x) (x & 0xFFFF)
+
+/*
+ * *HS PHY Test Mode (offset = 300H)
+ * */
+#define FUSB300_HSPTM_TSTPKDONE (1 << 4)
+#define FUSB300_HSPTM_TSTPKT (1 << 3)
+#define FUSB300_HSPTM_TSTSET0NAK (1 << 2)
+#define FUSB300_HSPTM_TSTKSTA (1 << 1)
+#define FUSB300_HSPTM_TSTJSTA (1 << 0)
+
+/*
+ * *HS Control Register (offset = 304H)
+ * */
+#define FUSB300_HSCR_HS_LPM_PERMIT (1 << 8)
+#define FUSB300_HSCR_HS_LPM_RMWKUP (1 << 7)
+#define FUSB300_HSCR_CAP_LPM_RMWKUP (1 << 6)
+#define FUSB300_HSCR_HS_GOSUSP (1 << 5)
+#define FUSB300_HSCR_HS_GORMWKU (1 << 4)
+#define FUSB300_HSCR_CAP_RMWKUP (1 << 3)
+#define FUSB300_HSCR_IDLECNT_0MS 0
+#define FUSB300_HSCR_IDLECNT_1MS 1
+#define FUSB300_HSCR_IDLECNT_2MS 2
+#define FUSB300_HSCR_IDLECNT_3MS 3
+#define FUSB300_HSCR_IDLECNT_4MS 4
+#define FUSB300_HSCR_IDLECNT_5MS 5
+#define FUSB300_HSCR_IDLECNT_6MS 6
+#define FUSB300_HSCR_IDLECNT_7MS 7
+
+/*
+ * * SS Controller Register 0 (offset = 308H)
+ * */
+#define FUSB300_SSCR0_MAX_INTERVAL(x) ((x & 0x7) << 4)
+#define FUSB300_SSCR0_U2_FUN_EN (1 << 1)
+#define FUSB300_SSCR0_U1_FUN_EN (1 << 0)
+
+/*
+ * * SS Controller Register 1 (offset = 30CH)
+ * */
+#define FUSB300_SSCR1_GO_U3_DONE (1 << 8)
+#define FUSB300_SSCR1_TXDEEMPH_LEVEL (1 << 7)
+#define FUSB300_SSCR1_DIS_SCRMB (1 << 6)
+#define FUSB300_SSCR1_FORCE_RECOVERY (1 << 5)
+#define FUSB300_SSCR1_U3_WAKEUP_EN (1 << 4)
+#define FUSB300_SSCR1_U2_EXIT_EN (1 << 3)
+#define FUSB300_SSCR1_U1_EXIT_EN (1 << 2)
+#define FUSB300_SSCR1_U2_ENTRY_EN (1 << 1)
+#define FUSB300_SSCR1_U1_ENTRY_EN (1 << 0)
+
+/*
+ * *SS Controller Register 2 (offset = 310H)
+ * */
+#define FUSB300_SSCR2_SS_TX_SWING (1 << 25)
+#define FUSB300_SSCR2_FORCE_LINKPM_ACCEPT (1 << 24)
+#define FUSB300_SSCR2_U2_INACT_TIMEOUT(x) ((x & 0xFF) << 16)
+#define FUSB300_SSCR2_U1TIMEOUT(x) ((x & 0xFF) << 8)
+#define FUSB300_SSCR2_U2TIMEOUT(x) (x & 0xFF)
+
+/*
+ * *SS Device Notification Control (DEV_NOTF, offset = 314H)
+ * */
+#define FUSB300_DEVNOTF_CONTEXT0(x) ((x & 0xFFFFFF) << 8)
+#define FUSB300_DEVNOTF_TYPE_DIS 0
+#define FUSB300_DEVNOTF_TYPE_FUNCWAKE 1
+#define FUSB300_DEVNOTF_TYPE_LTM 2
+#define FUSB300_DEVNOTF_TYPE_BUSINT_ADJMSG 3
+
+/*
+ * *BFM Arbiter Priority Register (BFM_ARB offset = 31CH)
+ * */
+#define FUSB300_BFMARB_ARB_M1 (1 << 3)
+#define FUSB300_BFMARB_ARB_M0 (1 << 2)
+#define FUSB300_BFMARB_ARB_S1 (1 << 1)
+#define FUSB300_BFMARB_ARB_S0 1
+
+/*
+ * *Vendor Specific IO Control Register (offset = 320H)
+ * */
+#define FUSB300_VSIC_VCTLOAD_N (1 << 8)
+#define FUSB300_VSIC_VCTL(x) (x & 0x3F)
+
+/*
+ * *SOF Mask Timer (offset = 324H)
+ * */
+#define FUSB300_SOF_MASK_TIMER_HS 0x044c
+#define FUSB300_SOF_MASK_TIMER_FS 0x2710
+
+/*
+ * *Error Flag and Control Status (offset = 328H)
+ * */
+#define FUSB300_EFCS_PM_STATE_U3 3
+#define FUSB300_EFCS_PM_STATE_U2 2
+#define FUSB300_EFCS_PM_STATE_U1 1
+#define FUSB300_EFCS_PM_STATE_U0 0
+
+/*
+ * *Interrupt Group 0 Register (offset = 400H)
+ * */
+#define FUSB300_IGR0_EP15_PRD_INT (1 << 31)
+#define FUSB300_IGR0_EP14_PRD_INT (1 << 30)
+#define FUSB300_IGR0_EP13_PRD_INT (1 << 29)
+#define FUSB300_IGR0_EP12_PRD_INT (1 << 28)
+#define FUSB300_IGR0_EP11_PRD_INT (1 << 27)
+#define FUSB300_IGR0_EP10_PRD_INT (1 << 26)
+#define FUSB300_IGR0_EP9_PRD_INT (1 << 25)
+#define FUSB300_IGR0_EP8_PRD_INT (1 << 24)
+#define FUSB300_IGR0_EP7_PRD_INT (1 << 23)
+#define FUSB300_IGR0_EP6_PRD_INT (1 << 22)
+#define FUSB300_IGR0_EP5_PRD_INT (1 << 21)
+#define FUSB300_IGR0_EP4_PRD_INT (1 << 20)
+#define FUSB300_IGR0_EP3_PRD_INT (1 << 19)
+#define FUSB300_IGR0_EP2_PRD_INT (1 << 18)
+#define FUSB300_IGR0_EP1_PRD_INT (1 << 17)
+#define FUSB300_IGR0_EPn_PRD_INT(n) (1 << (n + 16))
+
+#define FUSB300_IGR0_EP15_FIFO_INT (1 << 15)
+#define FUSB300_IGR0_EP14_FIFO_INT (1 << 14)
+#define FUSB300_IGR0_EP13_FIFO_INT (1 << 13)
+#define FUSB300_IGR0_EP12_FIFO_INT (1 << 12)
+#define FUSB300_IGR0_EP11_FIFO_INT (1 << 11)
+#define FUSB300_IGR0_EP10_FIFO_INT (1 << 10)
+#define FUSB300_IGR0_EP9_FIFO_INT (1 << 9)
+#define FUSB300_IGR0_EP8_FIFO_INT (1 << 8)
+#define FUSB300_IGR0_EP7_FIFO_INT (1 << 7)
+#define FUSB300_IGR0_EP6_FIFO_INT (1 << 6)
+#define FUSB300_IGR0_EP5_FIFO_INT (1 << 5)
+#define FUSB300_IGR0_EP4_FIFO_INT (1 << 4)
+#define FUSB300_IGR0_EP3_FIFO_INT (1 << 3)
+#define FUSB300_IGR0_EP2_FIFO_INT (1 << 2)
+#define FUSB300_IGR0_EP1_FIFO_INT (1 << 1)
+#define FUSB300_IGR0_EPn_FIFO_INT(n) (1 << n)
+
+/*
+ * *Interrupt Group 1 Register (offset = 404H)
+ * */
+#define FUSB300_IGR1_INTGRP5 (1 << 31)
+#define FUSB300_IGR1_VBUS_CHG_INT (1 << 30)
+#define FUSB300_IGR1_SYNF1_EMPTY_INT (1 << 29)
+#define FUSB300_IGR1_SYNF0_EMPTY_INT (1 << 28)
+#define FUSB300_IGR1_U3_EXIT_FAIL_INT (1 << 27)
+#define FUSB300_IGR1_U2_EXIT_FAIL_INT (1 << 26)
+#define FUSB300_IGR1_U1_EXIT_FAIL_INT (1 << 25)
+#define FUSB300_IGR1_U2_ENTRY_FAIL_INT (1 << 24)
+#define FUSB300_IGR1_U1_ENTRY_FAIL_INT (1 << 23)
+#define FUSB300_IGR1_U3_EXIT_INT (1 << 22)
+#define FUSB300_IGR1_U2_EXIT_INT (1 << 21)
+#define FUSB300_IGR1_U1_EXIT_INT (1 << 20)
+#define FUSB300_IGR1_U3_ENTRY_INT (1 << 19)
+#define FUSB300_IGR1_U2_ENTRY_INT (1 << 18)
+#define FUSB300_IGR1_U1_ENTRY_INT (1 << 17)
+#define FUSB300_IGR1_HOT_RST_INT (1 << 16)
+#define FUSB300_IGR1_WARM_RST_INT (1 << 15)
+#define FUSB300_IGR1_RESM_INT (1 << 14)
+#define FUSB300_IGR1_SUSP_INT (1 << 13)
+#define FUSB300_IGR1_HS_LPM_INT (1 << 12)
+#define FUSB300_IGR1_USBRST_INT (1 << 11)
+#define FUSB300_IGR1_DEV_MODE_CHG_INT (1 << 9)
+#define FUSB300_IGR1_CX_COMABT_INT (1 << 8)
+#define FUSB300_IGR1_CX_COMFAIL_INT (1 << 7)
+#define FUSB300_IGR1_CX_CMDEND_INT (1 << 6)
+#define FUSB300_IGR1_CX_OUT_INT (1 << 5)
+#define FUSB300_IGR1_CX_IN_INT (1 << 4)
+#define FUSB300_IGR1_CX_SETUP_INT (1 << 3)
+#define FUSB300_IGR1_INTGRP4 (1 << 2)
+#define FUSB300_IGR1_INTGRP3 (1 << 1)
+#define FUSB300_IGR1_INTGRP2 (1 << 0)
+
+/*
+ * *Interrupt Group 2 Register (offset = 408H)
+ * */
+#define FUSB300_IGR2_EP6_STR_ACCEPT_INT (1 << 29)
+#define FUSB300_IGR2_EP6_STR_RESUME_INT (1 << 28)
+#define FUSB300_IGR2_EP6_STR_REQ_INT (1 << 27)
+#define FUSB300_IGR2_EP6_STR_NOTRDY_INT (1 << 26)
+#define FUSB300_IGR2_EP6_STR_PRIME_INT (1 << 25)
+#define FUSB300_IGR2_EP5_STR_ACCEPT_INT (1 << 24)
+#define FUSB300_IGR2_EP5_STR_RESUME_INT (1 << 23)
+#define FUSB300_IGR2_EP5_STR_REQ_INT (1 << 22)
+#define FUSB300_IGR2_EP5_STR_NOTRDY_INT (1 << 21)
+#define FUSB300_IGR2_EP5_STR_PRIME_INT (1 << 20)
+#define FUSB300_IGR2_EP4_STR_ACCEPT_INT (1 << 19)
+#define FUSB300_IGR2_EP4_STR_RESUME_INT (1 << 18)
+#define FUSB300_IGR2_EP4_STR_REQ_INT (1 << 17)
+#define FUSB300_IGR2_EP4_STR_NOTRDY_INT (1 << 16)
+#define FUSB300_IGR2_EP4_STR_PRIME_INT (1 << 15)
+#define FUSB300_IGR2_EP3_STR_ACCEPT_INT (1 << 14)
+#define FUSB300_IGR2_EP3_STR_RESUME_INT (1 << 13)
+#define FUSB300_IGR2_EP3_STR_REQ_INT (1 << 12)
+#define FUSB300_IGR2_EP3_STR_NOTRDY_INT (1 << 11)
+#define FUSB300_IGR2_EP3_STR_PRIME_INT (1 << 10)
+#define FUSB300_IGR2_EP2_STR_ACCEPT_INT (1 << 9)
+#define FUSB300_IGR2_EP2_STR_RESUME_INT (1 << 8)
+#define FUSB300_IGR2_EP2_STR_REQ_INT (1 << 7)
+#define FUSB300_IGR2_EP2_STR_NOTRDY_INT (1 << 6)
+#define FUSB300_IGR2_EP2_STR_PRIME_INT (1 << 5)
+#define FUSB300_IGR2_EP1_STR_ACCEPT_INT (1 << 4)
+#define FUSB300_IGR2_EP1_STR_RESUME_INT (1 << 3)
+#define FUSB300_IGR2_EP1_STR_REQ_INT (1 << 2)
+#define FUSB300_IGR2_EP1_STR_NOTRDY_INT (1 << 1)
+#define FUSB300_IGR2_EP1_STR_PRIME_INT (1 << 0)
+
+#define FUSB300_IGR2_EP_STR_ACCEPT_INT(n) (1 << (5 * n - 1))
+#define FUSB300_IGR2_EP_STR_RESUME_INT(n) (1 << (5 * n - 2))
+#define FUSB300_IGR2_EP_STR_REQ_INT(n) (1 << (5 * n - 3))
+#define FUSB300_IGR2_EP_STR_NOTRDY_INT(n) (1 << (5 * n - 4))
+#define FUSB300_IGR2_EP_STR_PRIME_INT(n) (1 << (5 * n - 5))
+
+/*
+ * *Interrupt Group 3 Register (offset = 40CH)
+ * */
+#define FUSB300_IGR3_EP12_STR_ACCEPT_INT (1 << 29)
+#define FUSB300_IGR3_EP12_STR_RESUME_INT (1 << 28)
+#define FUSB300_IGR3_EP12_STR_REQ_INT (1 << 27)
+#define FUSB300_IGR3_EP12_STR_NOTRDY_INT (1 << 26)
+#define FUSB300_IGR3_EP12_STR_PRIME_INT (1 << 25)
+#define FUSB300_IGR3_EP11_STR_ACCEPT_INT (1 << 24)
+#define FUSB300_IGR3_EP11_STR_RESUME_INT (1 << 23)
+#define FUSB300_IGR3_EP11_STR_REQ_INT (1 << 22)
+#define FUSB300_IGR3_EP11_STR_NOTRDY_INT (1 << 21)
+#define FUSB300_IGR3_EP11_STR_PRIME_INT (1 << 20)
+#define FUSB300_IGR3_EP10_STR_ACCEPT_INT (1 << 19)
+#define FUSB300_IGR3_EP10_STR_RESUME_INT (1 << 18)
+#define FUSB300_IGR3_EP10_STR_REQ_INT (1 << 17)
+#define FUSB300_IGR3_EP10_STR_NOTRDY_INT (1 << 16)
+#define FUSB300_IGR3_EP10_STR_PRIME_INT (1 << 15)
+#define FUSB300_IGR3_EP9_STR_ACCEPT_INT (1 << 14)
+#define FUSB300_IGR3_EP9_STR_RESUME_INT (1 << 13)
+#define FUSB300_IGR3_EP9_STR_REQ_INT (1 << 12)
+#define FUSB300_IGR3_EP9_STR_NOTRDY_INT (1 << 11)
+#define FUSB300_IGR3_EP9_STR_PRIME_INT (1 << 10)
+#define FUSB300_IGR3_EP8_STR_ACCEPT_INT (1 << 9)
+#define FUSB300_IGR3_EP8_STR_RESUME_INT (1 << 8)
+#define FUSB300_IGR3_EP8_STR_REQ_INT (1 << 7)
+#define FUSB300_IGR3_EP8_STR_NOTRDY_INT (1 << 6)
+#define FUSB300_IGR3_EP8_STR_PRIME_INT (1 << 5)
+#define FUSB300_IGR3_EP7_STR_ACCEPT_INT (1 << 4)
+#define FUSB300_IGR3_EP7_STR_RESUME_INT (1 << 3)
+#define FUSB300_IGR3_EP7_STR_REQ_INT (1 << 2)
+#define FUSB300_IGR3_EP7_STR_NOTRDY_INT (1 << 1)
+#define FUSB300_IGR3_EP7_STR_PRIME_INT (1 << 0)
+
+#define FUSB300_IGR3_EP_STR_ACCEPT_INT(n) (1 << (5 * (n - 6) - 1))
+#define FUSB300_IGR3_EP_STR_RESUME_INT(n) (1 << (5 * (n - 6) - 2))
+#define FUSB300_IGR3_EP_STR_REQ_INT(n) (1 << (5 * (n - 6) - 3))
+#define FUSB300_IGR3_EP_STR_NOTRDY_INT(n) (1 << (5 * (n - 6) - 4))
+#define FUSB300_IGR3_EP_STR_PRIME_INT(n) (1 << (5 * (n - 6) - 5))
+
+/*
+ * *Interrupt Group 4 Register (offset = 410H)
+ * */
+#define FUSB300_IGR4_EP15_RX0_INT (1 << 31)
+#define FUSB300_IGR4_EP14_RX0_INT (1 << 30)
+#define FUSB300_IGR4_EP13_RX0_INT (1 << 29)
+#define FUSB300_IGR4_EP12_RX0_INT (1 << 28)
+#define FUSB300_IGR4_EP11_RX0_INT (1 << 27)
+#define FUSB300_IGR4_EP10_RX0_INT (1 << 26)
+#define FUSB300_IGR4_EP9_RX0_INT (1 << 25)
+#define FUSB300_IGR4_EP8_RX0_INT (1 << 24)
+#define FUSB300_IGR4_EP7_RX0_INT (1 << 23)
+#define FUSB300_IGR4_EP6_RX0_INT (1 << 22)
+#define FUSB300_IGR4_EP5_RX0_INT (1 << 21)
+#define FUSB300_IGR4_EP4_RX0_INT (1 << 20)
+#define FUSB300_IGR4_EP3_RX0_INT (1 << 19)
+#define FUSB300_IGR4_EP2_RX0_INT (1 << 18)
+#define FUSB300_IGR4_EP1_RX0_INT (1 << 17)
+#define FUSB300_IGR4_EP_RX0_INT(x) (1 << (x + 16))
+#define FUSB300_IGR4_EP15_STR_ACCEPT_INT (1 << 14)
+#define FUSB300_IGR4_EP15_STR_RESUME_INT (1 << 13)
+#define FUSB300_IGR4_EP15_STR_REQ_INT (1 << 12)
+#define FUSB300_IGR4_EP15_STR_NOTRDY_INT (1 << 11)
+#define FUSB300_IGR4_EP15_STR_PRIME_INT (1 << 10)
+#define FUSB300_IGR4_EP14_STR_ACCEPT_INT (1 << 9)
+#define FUSB300_IGR4_EP14_STR_RESUME_INT (1 << 8)
+#define FUSB300_IGR4_EP14_STR_REQ_INT (1 << 7)
+#define FUSB300_IGR4_EP14_STR_NOTRDY_INT (1 << 6)
+#define FUSB300_IGR4_EP14_STR_PRIME_INT (1 << 5)
+#define FUSB300_IGR4_EP13_STR_ACCEPT_INT (1 << 4)
+#define FUSB300_IGR4_EP13_STR_RESUME_INT (1 << 3)
+#define FUSB300_IGR4_EP13_STR_REQ_INT (1 << 2)
+#define FUSB300_IGR4_EP13_STR_NOTRDY_INT (1 << 1)
+#define FUSB300_IGR4_EP13_STR_PRIME_INT (1 << 0)
+
+#define FUSB300_IGR4_EP_STR_ACCEPT_INT(n) (1 << (5 * (n - 12) - 1))
+#define FUSB300_IGR4_EP_STR_RESUME_INT(n) (1 << (5 * (n - 12) - 2))
+#define FUSB300_IGR4_EP_STR_REQ_INT(n) (1 << (5 * (n - 12) - 3))
+#define FUSB300_IGR4_EP_STR_NOTRDY_INT(n) (1 << (5 * (n - 12) - 4))
+#define FUSB300_IGR4_EP_STR_PRIME_INT(n) (1 << (5 * (n - 12) - 5))
+
+/*
+ * *Interrupt Group 5 Register (offset = 414H)
+ * */
+#define FUSB300_IGR5_EP_STL_INT(n) (1 << n)
+
+/*
+ * *Interrupt Enable Group 0 Register (offset = 420H)
+ * */
+#define FUSB300_IGER0_EEP15_PRD_INT (1 << 31)
+#define FUSB300_IGER0_EEP14_PRD_INT (1 << 30)
+#define FUSB300_IGER0_EEP13_PRD_INT (1 << 29)
+#define FUSB300_IGER0_EEP12_PRD_INT (1 << 28)
+#define FUSB300_IGER0_EEP11_PRD_INT (1 << 27)
+#define FUSB300_IGER0_EEP10_PRD_INT (1 << 26)
+#define FUSB300_IGER0_EEP9_PRD_INT (1 << 25)
+#define FUSB300_IGER0_EP8_PRD_INT (1 << 24)
+#define FUSB300_IGER0_EEP7_PRD_INT (1 << 23)
+#define FUSB300_IGER0_EEP6_PRD_INT (1 << 22)
+#define FUSB300_IGER0_EEP5_PRD_INT (1 << 21)
+#define FUSB300_IGER0_EEP4_PRD_INT (1 << 20)
+#define FUSB300_IGER0_EEP3_PRD_INT (1 << 19)
+#define FUSB300_IGER0_EEP2_PRD_INT (1 << 18)
+#define FUSB300_IGER0_EEP1_PRD_INT (1 << 17)
+#define FUSB300_IGER0_EEPn_PRD_INT(n) (1 << (n + 16))
+
+#define FUSB300_IGER0_EEP15_FIFO_INT (1 << 15)
+#define FUSB300_IGER0_EEP14_FIFO_INT (1 << 14)
+#define FUSB300_IGER0_EEP13_FIFO_INT (1 << 13)
+#define FUSB300_IGER0_EEP12_FIFO_INT (1 << 12)
+#define FUSB300_IGER0_EEP11_FIFO_INT (1 << 11)
+#define FUSB300_IGER0_EEP10_FIFO_INT (1 << 10)
+#define FUSB300_IGER0_EEP9_FIFO_INT (1 << 9)
+#define FUSB300_IGER0_EEP8_FIFO_INT (1 << 8)
+#define FUSB300_IGER0_EEP7_FIFO_INT (1 << 7)
+#define FUSB300_IGER0_EEP6_FIFO_INT (1 << 6)
+#define FUSB300_IGER0_EEP5_FIFO_INT (1 << 5)
+#define FUSB300_IGER0_EEP4_FIFO_INT (1 << 4)
+#define FUSB300_IGER0_EEP3_FIFO_INT (1 << 3)
+#define FUSB300_IGER0_EEP2_FIFO_INT (1 << 2)
+#define FUSB300_IGER0_EEP1_FIFO_INT (1 << 1)
+#define FUSB300_IGER0_EEPn_FIFO_INT(n) (1 << n)
+
+/*
+ * *Interrupt Enable Group 1 Register (offset = 424H)
+ * */
+#define FUSB300_IGER1_EINT_GRP5 (1 << 31)
+#define FUSB300_IGER1_VBUS_CHG_INT (1 << 30)
+#define FUSB300_IGER1_SYNF1_EMPTY_INT (1 << 29)
+#define FUSB300_IGER1_SYNF0_EMPTY_INT (1 << 28)
+#define FUSB300_IGER1_U3_EXIT_FAIL_INT (1 << 27)
+#define FUSB300_IGER1_U2_EXIT_FAIL_INT (1 << 26)
+#define FUSB300_IGER1_U1_EXIT_FAIL_INT (1 << 25)
+#define FUSB300_IGER1_U2_ENTRY_FAIL_INT (1 << 24)
+#define FUSB300_IGER1_U1_ENTRY_FAIL_INT (1 << 23)
+#define FUSB300_IGER1_U3_EXIT_INT (1 << 22)
+#define FUSB300_IGER1_U2_EXIT_INT (1 << 21)
+#define FUSB300_IGER1_U1_EXIT_INT (1 << 20)
+#define FUSB300_IGER1_U3_ENTRY_INT (1 << 19)
+#define FUSB300_IGER1_U2_ENTRY_INT (1 << 18)
+#define FUSB300_IGER1_U1_ENTRY_INT (1 << 17)
+#define FUSB300_IGER1_HOT_RST_INT (1 << 16)
+#define FUSB300_IGER1_WARM_RST_INT (1 << 15)
+#define FUSB300_IGER1_RESM_INT (1 << 14)
+#define FUSB300_IGER1_SUSP_INT (1 << 13)
+#define FUSB300_IGER1_LPM_INT (1 << 12)
+#define FUSB300_IGER1_HS_RST_INT (1 << 11)
+#define FUSB300_IGER1_EDEV_MODE_CHG_INT (1 << 9)
+#define FUSB300_IGER1_CX_COMABT_INT (1 << 8)
+#define FUSB300_IGER1_CX_COMFAIL_INT (1 << 7)
+#define FUSB300_IGER1_CX_CMDEND_INT (1 << 6)
+#define FUSB300_IGER1_CX_OUT_INT (1 << 5)
+#define FUSB300_IGER1_CX_IN_INT (1 << 4)
+#define FUSB300_IGER1_CX_SETUP_INT (1 << 3)
+#define FUSB300_IGER1_INTGRP4 (1 << 2)
+#define FUSB300_IGER1_INTGRP3 (1 << 1)
+#define FUSB300_IGER1_INTGRP2 (1 << 0)
+
+/*
+ * *Interrupt Enable Group 2 Register (offset = 428H)
+ * */
+#define FUSB300_IGER2_EEP_STR_ACCEPT_INT(n) (1 << (5 * n - 1))
+#define FUSB300_IGER2_EEP_STR_RESUME_INT(n) (1 << (5 * n - 2))
+#define FUSB300_IGER2_EEP_STR_REQ_INT(n) (1 << (5 * n - 3))
+#define FUSB300_IGER2_EEP_STR_NOTRDY_INT(n) (1 << (5 * n - 4))
+#define FUSB300_IGER2_EEP_STR_PRIME_INT(n) (1 << (5 * n - 5))
+
+/*
+ * *Interrupt Enable Group 3 Register (offset = 42CH)
+ * */
+
+#define FUSB300_IGER3_EEP_STR_ACCEPT_INT(n) (1 << (5 * (n - 6) - 1))
+#define FUSB300_IGER3_EEP_STR_RESUME_INT(n) (1 << (5 * (n - 6) - 2))
+#define FUSB300_IGER3_EEP_STR_REQ_INT(n) (1 << (5 * (n - 6) - 3))
+#define FUSB300_IGER3_EEP_STR_NOTRDY_INT(n) (1 << (5 * (n - 6) - 4))
+#define FUSB300_IGER3_EEP_STR_PRIME_INT(n) (1 << (5 * (n - 6) - 5))
+
+/*
+ * *Interrupt Enable Group 4 Register (offset = 430H)
+ * */
+
+#define FUSB300_IGER4_EEP_RX0_INT(n) (1 << (n + 16))
+#define FUSB300_IGER4_EEP_STR_ACCEPT_INT(n) (1 << (5 * (n - 6) - 1))
+#define FUSB300_IGER4_EEP_STR_RESUME_INT(n) (1 << (5 * (n - 6) - 2))
+#define FUSB300_IGER4_EEP_STR_REQ_INT(n) (1 << (5 * (n - 6) - 3))
+#define FUSB300_IGER4_EEP_STR_NOTRDY_INT(n) (1 << (5 * (n - 6) - 4))
+#define FUSB300_IGER4_EEP_STR_PRIME_INT(n) (1 << (5 * (n - 6) - 5))
+
+/* EP PRD Ready (EP_PRD_RDY, offset = 504H) */
+
+#define FUSB300_EPPRDR_EP15_PRD_RDY (1 << 15)
+#define FUSB300_EPPRDR_EP14_PRD_RDY (1 << 14)
+#define FUSB300_EPPRDR_EP13_PRD_RDY (1 << 13)
+#define FUSB300_EPPRDR_EP12_PRD_RDY (1 << 12)
+#define FUSB300_EPPRDR_EP11_PRD_RDY (1 << 11)
+#define FUSB300_EPPRDR_EP10_PRD_RDY (1 << 10)
+#define FUSB300_EPPRDR_EP9_PRD_RDY (1 << 9)
+#define FUSB300_EPPRDR_EP8_PRD_RDY (1 << 8)
+#define FUSB300_EPPRDR_EP7_PRD_RDY (1 << 7)
+#define FUSB300_EPPRDR_EP6_PRD_RDY (1 << 6)
+#define FUSB300_EPPRDR_EP5_PRD_RDY (1 << 5)
+#define FUSB300_EPPRDR_EP4_PRD_RDY (1 << 4)
+#define FUSB300_EPPRDR_EP3_PRD_RDY (1 << 3)
+#define FUSB300_EPPRDR_EP2_PRD_RDY (1 << 2)
+#define FUSB300_EPPRDR_EP1_PRD_RDY (1 << 1)
+#define FUSB300_EPPRDR_EP_PRD_RDY(n) (1 << n)
+
+/* AHB Bus Control Register (offset = 514H) */
+#define FUSB300_AHBBCR_S1_SPLIT_ON (1 << 17)
+#define FUSB300_AHBBCR_S0_SPLIT_ON (1 << 16)
+#define FUSB300_AHBBCR_S1_1entry (0 << 12)
+#define FUSB300_AHBBCR_S1_4entry (3 << 12)
+#define FUSB300_AHBBCR_S1_8entry (5 << 12)
+#define FUSB300_AHBBCR_S1_16entry (7 << 12)
+#define FUSB300_AHBBCR_S0_1entry (0 << 8)
+#define FUSB300_AHBBCR_S0_4entry (3 << 8)
+#define FUSB300_AHBBCR_S0_8entry (5 << 8)
+#define FUSB300_AHBBCR_S0_16entry (7 << 8)
+#define FUSB300_AHBBCR_M1_BURST_SINGLE (0 << 4)
+#define FUSB300_AHBBCR_M1_BURST_INCR (1 << 4)
+#define FUSB300_AHBBCR_M1_BURST_INCR4 (3 << 4)
+#define FUSB300_AHBBCR_M1_BURST_INCR8 (5 << 4)
+#define FUSB300_AHBBCR_M1_BURST_INCR16 (7 << 4)
+#define FUSB300_AHBBCR_M0_BURST_SINGLE 0
+#define FUSB300_AHBBCR_M0_BURST_INCR 1
+#define FUSB300_AHBBCR_M0_BURST_INCR4 3
+#define FUSB300_AHBBCR_M0_BURST_INCR8 5
+#define FUSB300_AHBBCR_M0_BURST_INCR16 7
+#define FUSB300_IGER5_EEP_STL_INT(n) (1 << n)
+
+/* WORD 0 Data Structure of PRD Table */
+#define FUSB300_EPPRD0_M (1 << 30)
+#define FUSB300_EPPRD0_O (1 << 29)
+/* The finished prd */
+#define FUSB300_EPPRD0_F (1 << 28)
+#define FUSB300_EPPRD0_I (1 << 27)
+#define FUSB300_EPPRD0_A (1 << 26)
+/* To decide HW point to first prd at next time */
+#define FUSB300_EPPRD0_L (1 << 25)
+#define FUSB300_EPPRD0_H (1 << 24)
+#define FUSB300_EPPRD0_BTC(n) (n & 0xFFFFFF)
+
+/*----------------------------------------------------------------------*/
+#define FUSB300_MAX_NUM_EP 16
+
+#define FUSB300_FIFO_ENTRY_NUM 8
+#define FUSB300_MAX_FIFO_ENTRY 8
+
+#define SS_CTL_MAX_PACKET_SIZE 0x200
+#define SS_BULK_MAX_PACKET_SIZE 0x400
+#define SS_INT_MAX_PACKET_SIZE 0x400
+#define SS_ISO_MAX_PACKET_SIZE 0x400
+
+#define HS_BULK_MAX_PACKET_SIZE 0x200
+#define HS_CTL_MAX_PACKET_SIZE 0x40
+#define HS_INT_MAX_PACKET_SIZE 0x400
+#define HS_ISO_MAX_PACKET_SIZE 0x400
+
+struct fusb300_ep_info {
+ u8 epnum;
+ u8 type;
+ u8 interval;
+ u8 dir_in;
+ u16 maxpacket;
+ u16 addrofs;
+ u16 bw_num;
+};
+
+struct fusb300_request {
+
+ struct usb_request req;
+ struct list_head queue;
+};
+
+
+struct fusb300_ep {
+ struct usb_ep ep;
+ struct fusb300 *fusb300;
+
+ struct list_head queue;
+ unsigned stall:1;
+ unsigned wedged:1;
+ unsigned use_dma:1;
+
+ unsigned char epnum;
+ unsigned char type;
+ const struct usb_endpoint_descriptor *desc;
+};
+
+struct fusb300 {
+ spinlock_t lock;
+ void __iomem *reg;
+
+ unsigned long irq_trigger;
+
+ struct usb_gadget gadget;
+ struct usb_gadget_driver *driver;
+
+ struct fusb300_ep *ep[FUSB300_MAX_NUM_EP];
+
+ struct usb_request *ep0_req; /* for internal request */
+ __le16 ep0_data;
+ u32 ep0_length; /* for internal request */
+ u8 ep0_dir; /* 0/0x80 out/in */
+
+ u8 fifo_entry_num; /* next start fifo entry */
+ u32 addrofs; /* next fifo address offset */
+ u8 reenum; /* if re-enumeration */
+};
+
+#endif
diff --git a/drivers/usb/gadget/m66592-udc.c b/drivers/usb/gadget/m66592-udc.c
index 51b19f3027e7..084aa080a2d5 100644
--- a/drivers/usb/gadget/m66592-udc.c
+++ b/drivers/usb/gadget/m66592-udc.c
@@ -258,7 +258,7 @@ static int pipe_buffer_setting(struct m66592 *m66592,
break;
case M66592_BULK:
/* isochronous pipes may be used as bulk pipes */
- if (info->pipe > M66592_BASE_PIPENUM_BULK)
+ if (info->pipe >= M66592_BASE_PIPENUM_BULK)
bufnum = info->pipe - M66592_BASE_PIPENUM_BULK;
else
bufnum = info->pipe - M66592_BASE_PIPENUM_ISOC;
diff --git a/drivers/usb/gadget/pch_udc.c b/drivers/usb/gadget/pch_udc.c
index b120dbb64d0f..3e4b35e50c24 100644
--- a/drivers/usb/gadget/pch_udc.c
+++ b/drivers/usb/gadget/pch_udc.c
@@ -367,7 +367,6 @@ struct pch_udc_dev {
static const char ep0_string[] = "ep0in";
static DEFINE_SPINLOCK(udc_stall_spinlock); /* stall spin lock */
struct pch_udc_dev *pch_udc; /* pointer to device object */
-
static int speed_fs;
module_param_named(speed_fs, speed_fs, bool, S_IRUGO);
MODULE_PARM_DESC(speed_fs, "true for Full speed operation");
@@ -383,6 +382,8 @@ MODULE_PARM_DESC(speed_fs, "true for Full speed operation");
* @dma_mapped: DMA memory mapped for request
* @dma_done: DMA completed for request
* @chain_len: chain length
+ * @buf: Buffer memory for align adjustment
+ * @dma: DMA memory for align adjustment
*/
struct pch_udc_request {
struct usb_request req;
@@ -394,6 +395,8 @@ struct pch_udc_request {
dma_mapped:1,
dma_done:1;
unsigned chain_len;
+ void *buf;
+ dma_addr_t dma;
};
static inline u32 pch_udc_readl(struct pch_udc_dev *dev, unsigned long reg)
@@ -615,7 +618,7 @@ static inline void pch_udc_ep_set_trfr_type(struct pch_udc_ep *ep,
/**
* pch_udc_ep_set_bufsz() - Set the maximum packet size for the endpoint
* @ep: Reference to structure of type pch_udc_ep_regs
- * @buf_size: The buffer size
+ * @buf_size: The buffer word size
*/
static void pch_udc_ep_set_bufsz(struct pch_udc_ep *ep,
u32 buf_size, u32 ep_in)
@@ -635,7 +638,7 @@ static void pch_udc_ep_set_bufsz(struct pch_udc_ep *ep,
/**
* pch_udc_ep_set_maxpkt() - Set the Max packet size for the endpoint
* @ep: Reference to structure of type pch_udc_ep_regs
- * @pkt_size: The packet size
+ * @pkt_size: The packet byte size
*/
static void pch_udc_ep_set_maxpkt(struct pch_udc_ep *ep, u32 pkt_size)
{
@@ -920,25 +923,10 @@ static void pch_udc_ep_clear_nak(struct pch_udc_ep *ep)
*/
static void pch_udc_ep_fifo_flush(struct pch_udc_ep *ep, int dir)
{
- unsigned int loopcnt = 0;
- struct pch_udc_dev *dev = ep->dev;
-
if (dir) { /* IN ep */
pch_udc_ep_bit_set(ep, UDC_EPCTL_ADDR, UDC_EPCTL_F);
return;
}
-
- if (pch_udc_read_ep_status(ep) & UDC_EPSTS_MRXFIFO_EMP)
- return;
- pch_udc_ep_bit_set(ep, UDC_EPCTL_ADDR, UDC_EPCTL_MRXFLUSH);
- /* Wait for RxFIFO Empty */
- loopcnt = 10000;
- while (!(pch_udc_read_ep_status(ep) & UDC_EPSTS_MRXFIFO_EMP) &&
- --loopcnt)
- udelay(5);
- if (!loopcnt)
- dev_err(&dev->pdev->dev, "RxFIFO not Empty\n");
- pch_udc_ep_bit_clr(ep, UDC_EPCTL_ADDR, UDC_EPCTL_MRXFLUSH);
}
/**
@@ -1220,14 +1208,31 @@ static void complete_req(struct pch_udc_ep *ep, struct pch_udc_request *req,
dev = ep->dev;
if (req->dma_mapped) {
- if (ep->in)
- dma_unmap_single(&dev->pdev->dev, req->req.dma,
- req->req.length, DMA_TO_DEVICE);
- else
- dma_unmap_single(&dev->pdev->dev, req->req.dma,
- req->req.length, DMA_FROM_DEVICE);
+ if (req->dma == DMA_ADDR_INVALID) {
+ if (ep->in)
+ dma_unmap_single(&dev->pdev->dev, req->req.dma,
+ req->req.length,
+ DMA_TO_DEVICE);
+ else
+ dma_unmap_single(&dev->pdev->dev, req->req.dma,
+ req->req.length,
+ DMA_FROM_DEVICE);
+ req->req.dma = DMA_ADDR_INVALID;
+ } else {
+ if (ep->in)
+ dma_unmap_single(&dev->pdev->dev, req->dma,
+ req->req.length,
+ DMA_TO_DEVICE);
+ else {
+ dma_unmap_single(&dev->pdev->dev, req->dma,
+ req->req.length,
+ DMA_FROM_DEVICE);
+ memcpy(req->req.buf, req->buf, req->req.length);
+ }
+ kfree(req->buf);
+ req->dma = DMA_ADDR_INVALID;
+ }
req->dma_mapped = 0;
- req->req.dma = DMA_ADDR_INVALID;
}
ep->halted = 1;
spin_unlock(&dev->lock);
@@ -1268,12 +1273,18 @@ static void pch_udc_free_dma_chain(struct pch_udc_dev *dev,
struct pch_udc_data_dma_desc *td = req->td_data;
unsigned i = req->chain_len;
+ dma_addr_t addr2;
+ dma_addr_t addr = (dma_addr_t)td->next;
+ td->next = 0x00;
for (; i > 1; --i) {
- dma_addr_t addr = (dma_addr_t)td->next;
/* do not free first desc., will be done by free for request */
td = phys_to_virt(addr);
+ addr2 = (dma_addr_t)td->next;
pci_pool_free(dev->data_requests, td, addr);
+ td->next = 0x00;
+ addr = addr2;
}
+ req->chain_len = 1;
}
/**
@@ -1301,23 +1312,23 @@ static int pch_udc_create_dma_chain(struct pch_udc_ep *ep,
if (req->chain_len > 1)
pch_udc_free_dma_chain(ep->dev, req);
- for (; ; bytes -= buf_len, ++len) {
- if (ep->in)
- td->status = PCH_UDC_BS_HST_BSY | min(buf_len, bytes);
- else
- td->status = PCH_UDC_BS_HST_BSY;
+ if (req->dma == DMA_ADDR_INVALID)
+ td->dataptr = req->req.dma;
+ else
+ td->dataptr = req->dma;
+ td->status = PCH_UDC_BS_HST_BSY;
+ for (; ; bytes -= buf_len, ++len) {
+ td->status = PCH_UDC_BS_HST_BSY | min(buf_len, bytes);
if (bytes <= buf_len)
break;
-
last = td;
td = pci_pool_alloc(ep->dev->data_requests, gfp_flags,
&dma_addr);
if (!td)
goto nomem;
-
i += buf_len;
- td->dataptr = req->req.dma + i;
+ td->dataptr = req->td_data->dataptr + i;
last->next = dma_addr;
}
@@ -1352,28 +1363,15 @@ static int prepare_dma(struct pch_udc_ep *ep, struct pch_udc_request *req,
{
int retval;
- req->td_data->dataptr = req->req.dma;
- req->td_data->status |= PCH_UDC_DMA_LAST;
/* Allocate and create a DMA chain */
retval = pch_udc_create_dma_chain(ep, req, ep->ep.maxpacket, gfp);
if (retval) {
- pr_err("%s: could not create DMA chain: %d\n",
- __func__, retval);
+ pr_err("%s: could not create DMA chain:%d\n", __func__, retval);
return retval;
}
- if (!ep->in)
- return 0;
- if (req->req.length <= ep->ep.maxpacket)
- req->td_data->status = PCH_UDC_DMA_LAST | PCH_UDC_BS_HST_BSY |
- req->req.length;
- /* if bytes < max packet then tx bytes must
- * be written in packet per buffer mode
- */
- if ((req->req.length < ep->ep.maxpacket) || !ep->num)
+ if (ep->in)
req->td_data->status = (req->td_data->status &
- ~PCH_UDC_RXTX_BYTES) | req->req.length;
- req->td_data->status = (req->td_data->status &
- ~PCH_UDC_BUFF_STS) | PCH_UDC_BS_HST_BSY;
+ ~PCH_UDC_BUFF_STS) | PCH_UDC_BS_HST_RDY;
return 0;
}
@@ -1529,6 +1527,7 @@ static struct usb_request *pch_udc_alloc_request(struct usb_ep *usbep,
if (!req)
return NULL;
req->req.dma = DMA_ADDR_INVALID;
+ req->dma = DMA_ADDR_INVALID;
INIT_LIST_HEAD(&req->queue);
if (!ep->dev->dma_addr)
return &req->req;
@@ -1613,16 +1612,33 @@ static int pch_udc_pcd_queue(struct usb_ep *usbep, struct usb_request *usbreq,
/* map the buffer for dma */
if (usbreq->length &&
((usbreq->dma == DMA_ADDR_INVALID) || !usbreq->dma)) {
- if (ep->in)
- usbreq->dma = dma_map_single(&dev->pdev->dev,
- usbreq->buf,
- usbreq->length,
- DMA_TO_DEVICE);
- else
- usbreq->dma = dma_map_single(&dev->pdev->dev,
- usbreq->buf,
- usbreq->length,
- DMA_FROM_DEVICE);
+ if (!((unsigned long)(usbreq->buf) & 0x03)) {
+ if (ep->in)
+ usbreq->dma = dma_map_single(&dev->pdev->dev,
+ usbreq->buf,
+ usbreq->length,
+ DMA_TO_DEVICE);
+ else
+ usbreq->dma = dma_map_single(&dev->pdev->dev,
+ usbreq->buf,
+ usbreq->length,
+ DMA_FROM_DEVICE);
+ } else {
+ req->buf = kzalloc(usbreq->length, GFP_ATOMIC);
+ if (!req->buf)
+ return -ENOMEM;
+ if (ep->in) {
+ memcpy(req->buf, usbreq->buf, usbreq->length);
+ req->dma = dma_map_single(&dev->pdev->dev,
+ req->buf,
+ usbreq->length,
+ DMA_TO_DEVICE);
+ } else
+ req->dma = dma_map_single(&dev->pdev->dev,
+ req->buf,
+ usbreq->length,
+ DMA_FROM_DEVICE);
+ }
req->dma_mapped = 1;
}
if (usbreq->length > 0) {
@@ -1920,32 +1936,46 @@ static void pch_udc_complete_receiver(struct pch_udc_ep *ep)
struct pch_udc_request *req;
struct pch_udc_dev *dev = ep->dev;
unsigned int count;
+ struct pch_udc_data_dma_desc *td;
+ dma_addr_t addr;
if (list_empty(&ep->queue))
return;
-
/* next request */
req = list_entry(ep->queue.next, struct pch_udc_request, queue);
- if ((req->td_data_last->status & PCH_UDC_BUFF_STS) !=
- PCH_UDC_BS_DMA_DONE)
- return;
pch_udc_clear_dma(ep->dev, DMA_DIR_RX);
pch_udc_ep_set_ddptr(ep, 0);
- if ((req->td_data_last->status & PCH_UDC_RXTX_STS) !=
- PCH_UDC_RTS_SUCC) {
- dev_err(&dev->pdev->dev, "Invalid RXTX status (0x%08x) "
- "epstatus=0x%08x\n",
- (req->td_data_last->status & PCH_UDC_RXTX_STS),
- (int)(ep->epsts));
- return;
- }
- count = req->td_data_last->status & PCH_UDC_RXTX_BYTES;
+ if ((req->td_data_last->status & PCH_UDC_BUFF_STS) ==
+ PCH_UDC_BS_DMA_DONE)
+ td = req->td_data_last;
+ else
+ td = req->td_data;
+ while (1) {
+ if ((td->status & PCH_UDC_RXTX_STS) != PCH_UDC_RTS_SUCC) {
+ dev_err(&dev->pdev->dev, "Invalid RXTX status=0x%08x "
+ "epstatus=0x%08x\n",
+ (req->td_data->status & PCH_UDC_RXTX_STS),
+ (int)(ep->epsts));
+ return;
+ }
+ if ((td->status & PCH_UDC_BUFF_STS) == PCH_UDC_BS_DMA_DONE)
+ if (td->status | PCH_UDC_DMA_LAST) {
+ count = td->status & PCH_UDC_RXTX_BYTES;
+ break;
+ }
+ if (td == req->td_data_last) {
+ dev_err(&dev->pdev->dev, "Not complete RX descriptor");
+ return;
+ }
+ addr = (dma_addr_t)td->next;
+ td = phys_to_virt(addr);
+ }
/* on 64k packets the RXBYTES field is zero */
if (!count && (req->req.length == UDC_DMA_MAXPACKET))
count = UDC_DMA_MAXPACKET;
req->td_data->status |= PCH_UDC_DMA_LAST;
- req->td_data_last->status |= PCH_UDC_BS_HST_BSY;
+ td->status |= PCH_UDC_BS_HST_BSY;
req->dma_going = 0;
req->req.actual = count;
diff --git a/drivers/usb/gadget/s3c2410_udc.c b/drivers/usb/gadget/s3c2410_udc.c
index c2448950a8d8..6d8b04061d5d 100644
--- a/drivers/usb/gadget/s3c2410_udc.c
+++ b/drivers/usb/gadget/s3c2410_udc.c
@@ -902,7 +902,7 @@ static irqreturn_t s3c2410_udc_irq(int dummy, void *_dev)
int pwr_reg;
int ep0csr;
int i;
- u32 idx;
+ u32 idx, idx2;
unsigned long flags;
spin_lock_irqsave(&dev->lock, flags);
@@ -1017,6 +1017,20 @@ static irqreturn_t s3c2410_udc_irq(int dummy, void *_dev)
}
}
+ /* what else causes this interrupt? a receive! who is it? */
+ if (!usb_status && !usbd_status && !pwr_reg && !ep0csr) {
+ for (i = 1; i < S3C2410_ENDPOINTS; i++) {
+ idx2 = udc_read(S3C2410_UDC_INDEX_REG);
+ udc_write(i, S3C2410_UDC_INDEX_REG);
+
+ if (udc_read(S3C2410_UDC_OUT_CSR1_REG) & 0x1)
+ s3c2410_udc_handle_ep(&dev->ep[i]);
+
+ /* restore index */
+ udc_write(idx2, S3C2410_UDC_INDEX_REG);
+ }
+ }
+
dprintk(DEBUG_VERBOSE, "irq: %d s3c2410_udc_done.\n", IRQ_USBD);
/* Restore old index */
@@ -1467,7 +1481,9 @@ static int s3c2410_udc_set_pullup(struct s3c2410_udc *udc, int is_on)
{
dprintk(DEBUG_NORMAL, "%s()\n", __func__);
- if (udc_info && udc_info->udc_command) {
+ if (udc_info && (udc_info->udc_command ||
+ gpio_is_valid(udc_info->pullup_pin))) {
+
if (is_on)
s3c2410_udc_enable(udc);
else {
@@ -1544,6 +1560,32 @@ static const struct usb_gadget_ops s3c2410_ops = {
.vbus_draw = s3c2410_vbus_draw,
};
+static void s3c2410_udc_command(enum s3c2410_udc_cmd_e cmd)
+{
+ if (!udc_info)
+ return;
+
+ if (udc_info->udc_command) {
+ udc_info->udc_command(S3C2410_UDC_P_DISABLE);
+ } else if (gpio_is_valid(udc_info->pullup_pin)) {
+ int value;
+
+ switch (cmd) {
+ case S3C2410_UDC_P_ENABLE:
+ value = 1;
+ break;
+ case S3C2410_UDC_P_DISABLE:
+ value = 0;
+ break;
+ default:
+ return;
+ }
+ value ^= udc_info->pullup_pin_inverted;
+
+ gpio_set_value(udc_info->pullup_pin, value);
+ }
+}
+
/*------------------------- gadget driver handling---------------------------*/
/*
* s3c2410_udc_disable
@@ -1565,8 +1607,7 @@ static void s3c2410_udc_disable(struct s3c2410_udc *dev)
udc_write(0x1F, S3C2410_UDC_EP_INT_REG);
/* Good bye, cruel world */
- if (udc_info && udc_info->udc_command)
- udc_info->udc_command(S3C2410_UDC_P_DISABLE);
+ s3c2410_udc_command(S3C2410_UDC_P_DISABLE);
/* Set speed to unknown */
dev->gadget.speed = USB_SPEED_UNKNOWN;
@@ -1627,8 +1668,7 @@ static void s3c2410_udc_enable(struct s3c2410_udc *dev)
udc_write(S3C2410_UDC_INT_EP0, S3C2410_UDC_EP_INT_EN_REG);
/* time to say "hello, world" */
- if (udc_info && udc_info->udc_command)
- udc_info->udc_command(S3C2410_UDC_P_ENABLE);
+ s3c2410_udc_command(S3C2410_UDC_P_ENABLE);
}
/*
@@ -1903,6 +1943,17 @@ static int s3c2410_udc_probe(struct platform_device *pdev)
udc->vbus = 1;
}
+ if (udc_info && !udc_info->udc_command &&
+ gpio_is_valid(udc_info->pullup_pin)) {
+
+ retval = gpio_request_one(udc_info->pullup_pin,
+ udc_info->vbus_pin_inverted ?
+ GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW,
+ "udc pullup");
+ if (retval)
+ goto err_vbus_irq;
+ }
+
if (s3c2410_udc_debugfs_root) {
udc->regs_info = debugfs_create_file("registers", S_IRUGO,
s3c2410_udc_debugfs_root,
@@ -1915,6 +1966,9 @@ static int s3c2410_udc_probe(struct platform_device *pdev)
return 0;
+err_vbus_irq:
+ if (udc_info && udc_info->vbus_pin > 0)
+ free_irq(gpio_to_irq(udc_info->vbus_pin), udc);
err_gpio_claim:
if (udc_info && udc_info->vbus_pin > 0)
gpio_free(udc_info->vbus_pin);
@@ -1942,6 +1996,10 @@ static int s3c2410_udc_remove(struct platform_device *pdev)
debugfs_remove(udc->regs_info);
+ if (udc_info && !udc_info->udc_command &&
+ gpio_is_valid(udc_info->pullup_pin))
+ gpio_free(udc_info->pullup_pin);
+
if (udc_info && udc_info->vbus_pin > 0) {
irq = gpio_to_irq(udc_info->vbus_pin);
free_irq(irq, udc);
@@ -1973,16 +2031,14 @@ static int s3c2410_udc_remove(struct platform_device *pdev)
#ifdef CONFIG_PM
static int s3c2410_udc_suspend(struct platform_device *pdev, pm_message_t message)
{
- if (udc_info && udc_info->udc_command)
- udc_info->udc_command(S3C2410_UDC_P_DISABLE);
+ s3c2410_udc_command(S3C2410_UDC_P_DISABLE);
return 0;
}
static int s3c2410_udc_resume(struct platform_device *pdev)
{
- if (udc_info && udc_info->udc_command)
- udc_info->udc_command(S3C2410_UDC_P_ENABLE);
+ s3c2410_udc_command(S3C2410_UDC_P_ENABLE);
return 0;
}
diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c
index 1eda968b5644..2ac1d2147325 100644
--- a/drivers/usb/gadget/u_ether.c
+++ b/drivers/usb/gadget/u_ether.c
@@ -241,7 +241,7 @@ rx_submit(struct eth_dev *dev, struct usb_request *req, gfp_t gfp_flags)
size -= size % out->maxpacket;
if (dev->port_usb->is_fixed)
- size = max(size, dev->port_usb->fixed_out_len);
+ size = max_t(size_t, size, dev->port_usb->fixed_out_len);
skb = alloc_skb(size + NET_IP_ALIGN, gfp_flags);
if (skb == NULL) {
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 0e6afa260ed8..9483acdf2e9e 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -91,17 +91,28 @@ config USB_EHCI_TT_NEWSCHED
If unsure, say Y.
+config USB_EHCI_HCD_PMC_MSP
+ tristate "EHCI support for on-chip PMC MSP71xx USB controller"
+ depends on USB_EHCI_HCD && MSP_HAS_USB
+ default n
+ select USB_EHCI_BIG_ENDIAN_DESC
+ select USB_EHCI_BIG_ENDIAN_MMIO
+ ---help---
+ Enables support for the onchip USB controller on the PMC_MSP7100 Family SoC's.
+ If unsure, say N.
+
config USB_EHCI_BIG_ENDIAN_MMIO
bool
depends on USB_EHCI_HCD && (PPC_CELLEB || PPC_PS3 || 440EPX || \
ARCH_IXP4XX || XPS_USB_HCD_XILINX || \
- PPC_MPC512x || CPU_CAVIUM_OCTEON)
+ PPC_MPC512x || CPU_CAVIUM_OCTEON || \
+ PMC_MSP)
default y
config USB_EHCI_BIG_ENDIAN_DESC
bool
depends on USB_EHCI_HCD && (440EPX || ARCH_IXP4XX || XPS_USB_HCD_XILINX || \
- PPC_MPC512x)
+ PPC_MPC512x || PMC_MSP)
default y
config XPS_USB_HCD_XILINX
@@ -145,7 +156,7 @@ config USB_EHCI_MSM
bool "Support for MSM on-chip EHCI USB controller"
depends on USB_EHCI_HCD && ARCH_MSM
select USB_EHCI_ROOT_HUB_TT
- select USB_MSM_OTG_72K
+ select USB_MSM_OTG
---help---
Enables support for the USB Host controller present on the
Qualcomm chipsets. Root Hub has inbuilt TT.
@@ -154,6 +165,14 @@ config USB_EHCI_MSM
This driver is not supported on boards like trout which
has an external PHY.
+config USB_EHCI_TEGRA
+ boolean "NVIDIA Tegra HCD support"
+ depends on USB_EHCI_HCD && ARCH_TEGRA
+ select USB_EHCI_ROOT_HUB_TT
+ help
+ This driver enables support for the internal USB Host Controllers
+ found in NVIDIA Tegra SoCs. The controllers are EHCI compliant.
+
config USB_EHCI_HCD_PPC_OF
bool "EHCI support for PPC USB controller on OF platform bus"
depends on USB_EHCI_HCD && PPC_OF
@@ -162,6 +181,13 @@ config USB_EHCI_HCD_PPC_OF
Enables support for the USB controller present on the PowerPC
OpenFirmware platform bus.
+config USB_EHCI_SH
+ bool "EHCI support for SuperH USB controller"
+ depends on USB_EHCI_HCD && SUPERH
+ ---help---
+ Enables support for the on-chip EHCI controller on the SuperH.
+ If you use the PCI EHCI controller, this option is not necessary.
+
config USB_W90X900_EHCI
bool "W90X900(W90P910) EHCI support"
depends on USB_EHCI_HCD && ARCH_W90X900
@@ -315,6 +341,13 @@ config USB_OHCI_HCD_SSB
If unsure, say N.
+config USB_OHCI_SH
+ bool "OHCI support for SuperH USB controller"
+ depends on USB_OHCI_HCD && SUPERH
+ ---help---
+ Enables support for the on-chip OHCI controller on the SuperH.
+ If you use the PCI OHCI controller, this option is not necessary.
+
config USB_CNS3XXX_OHCI
bool "Cavium CNS3XXX OHCI Module"
depends on USB_OHCI_HCD && ARCH_CNS3XXX
diff --git a/drivers/usb/host/ehci-atmel.c b/drivers/usb/host/ehci-atmel.c
index d6a69d514a84..b2ed55cb811d 100644
--- a/drivers/usb/host/ehci-atmel.c
+++ b/drivers/usb/host/ehci-atmel.c
@@ -115,7 +115,7 @@ static const struct hc_driver ehci_atmel_hc_driver = {
.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
};
-static int __init ehci_atmel_drv_probe(struct platform_device *pdev)
+static int __devinit ehci_atmel_drv_probe(struct platform_device *pdev)
{
struct usb_hcd *hcd;
const struct hc_driver *driver = &ehci_atmel_hc_driver;
@@ -207,7 +207,7 @@ fail_create_hcd:
return retval;
}
-static int __exit ehci_atmel_drv_remove(struct platform_device *pdev)
+static int __devexit ehci_atmel_drv_remove(struct platform_device *pdev)
{
struct usb_hcd *hcd = platform_get_drvdata(pdev);
@@ -227,7 +227,7 @@ static int __exit ehci_atmel_drv_remove(struct platform_device *pdev)
static struct platform_driver ehci_atmel_driver = {
.probe = ehci_atmel_drv_probe,
- .remove = __exit_p(ehci_atmel_drv_remove),
+ .remove = __devexit_p(ehci_atmel_drv_remove),
.shutdown = usb_hcd_platform_shutdown,
.driver.name = "atmel-ehci",
};
diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c
index 3be238a24cc5..693c29b30521 100644
--- a/drivers/usb/host/ehci-dbg.c
+++ b/drivers/usb/host/ehci-dbg.c
@@ -28,11 +28,9 @@
dev_warn (ehci_to_hcd(ehci)->self.controller , fmt , ## args )
#ifdef VERBOSE_DEBUG
-# define vdbg dbg
# define ehci_vdbg ehci_dbg
#else
-# define vdbg(fmt,args...) do { } while (0)
-# define ehci_vdbg(ehci, fmt, args...) do { } while (0)
+ static inline void ehci_vdbg(struct ehci_hcd *ehci, ...) {}
#endif
#ifdef DEBUG
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 74dcf49bd015..d30c4e08c137 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -114,13 +114,11 @@ MODULE_PARM_DESC(hird, "host initiated resume duration, +1 for each 75us\n");
#define INTR_MASK (STS_IAA | STS_FATAL | STS_PCD | STS_ERR | STS_INT)
-/* for ASPM quirk of ISOC on AMD SB800 */
-static struct pci_dev *amd_nb_dev;
-
/*-------------------------------------------------------------------------*/
#include "ehci.h"
#include "ehci-dbg.c"
+#include "pci-quirks.h"
/*-------------------------------------------------------------------------*/
@@ -532,10 +530,8 @@ static void ehci_stop (struct usb_hcd *hcd)
spin_unlock_irq (&ehci->lock);
ehci_mem_cleanup (ehci);
- if (amd_nb_dev) {
- pci_dev_put(amd_nb_dev);
- amd_nb_dev = NULL;
- }
+ if (ehci->amd_pll_fix == 1)
+ usb_amd_dev_put();
#ifdef EHCI_STATS
ehci_dbg (ehci, "irq normal %ld err %ld reclaim %ld (lost %ld)\n",
@@ -679,7 +675,12 @@ static int ehci_run (struct usb_hcd *hcd)
hcd->uses_new_polling = 1;
/* EHCI spec section 4.1 */
- if ((retval = ehci_reset(ehci)) != 0) {
+ /*
+ * TDI driver does the ehci_reset in their reset callback.
+ * Don't reset here, because configuration settings will
+ * vanish.
+ */
+ if (!ehci_is_TDI(ehci) && (retval = ehci_reset(ehci)) != 0) {
ehci_mem_cleanup(ehci);
return retval;
}
@@ -1179,7 +1180,7 @@ MODULE_LICENSE ("GPL");
#define PLATFORM_DRIVER ehci_mxc_driver
#endif
-#ifdef CONFIG_CPU_SUBTYPE_SH7786
+#ifdef CONFIG_USB_EHCI_SH
#include "ehci-sh.c"
#define PLATFORM_DRIVER ehci_hcd_sh_driver
#endif
@@ -1254,6 +1255,16 @@ MODULE_LICENSE ("GPL");
#define PLATFORM_DRIVER ehci_msm_driver
#endif
+#ifdef CONFIG_USB_EHCI_HCD_PMC_MSP
+#include "ehci-pmcmsp.c"
+#define PLATFORM_DRIVER ehci_hcd_msp_driver
+#endif
+
+#ifdef CONFIG_USB_EHCI_TEGRA
+#include "ehci-tegra.c"
+#define PLATFORM_DRIVER tegra_ehci_driver
+#endif
+
#if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \
!defined(PS3_SYSTEM_BUS_DRIVER) && !defined(OF_PLATFORM_DRIVER) && \
!defined(XILINX_OF_PLATFORM_DRIVER)
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index 8a515f0d5988..d05ea03cfb4d 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -106,6 +106,27 @@ static void ehci_handover_companion_ports(struct ehci_hcd *ehci)
ehci->owned_ports = 0;
}
+static int ehci_port_change(struct ehci_hcd *ehci)
+{
+ int i = HCS_N_PORTS(ehci->hcs_params);
+
+ /* First check if the controller indicates a change event */
+
+ if (ehci_readl(ehci, &ehci->regs->status) & STS_PCD)
+ return 1;
+
+ /*
+ * Not all controllers appear to update this while going from D3 to D0,
+ * so check the individual port status registers as well
+ */
+
+ while (i--)
+ if (ehci_readl(ehci, &ehci->regs->port_status[i]) & PORT_CSC)
+ return 1;
+
+ return 0;
+}
+
static void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci,
bool suspending, bool do_wakeup)
{
@@ -173,7 +194,7 @@ static void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci,
}
/* Does the root hub have a port wakeup pending? */
- if (!suspending && (ehci_readl(ehci, &ehci->regs->status) & STS_PCD))
+ if (!suspending && ehci_port_change(ehci))
usb_hcd_resume_root_hub(ehci_to_hcd(ehci));
spin_unlock_irqrestore(&ehci->lock, flags);
@@ -538,14 +559,15 @@ static ssize_t store_companion(struct device *dev,
}
static DEVICE_ATTR(companion, 0644, show_companion, store_companion);
-static inline void create_companion_file(struct ehci_hcd *ehci)
+static inline int create_companion_file(struct ehci_hcd *ehci)
{
- int i;
+ int i = 0;
/* with integrated TT there is no companion! */
if (!ehci_is_TDI(ehci))
i = device_create_file(ehci_to_hcd(ehci)->self.controller,
&dev_attr_companion);
+ return i;
}
static inline void remove_companion_file(struct ehci_hcd *ehci)
@@ -695,8 +717,8 @@ ehci_hub_descriptor (
desc->bDescLength = 7 + 2 * temp;
/* two bitmaps: ports removable, and usb 1.0 legacy PortPwrCtrlMask */
- memset (&desc->bitmap [0], 0, temp);
- memset (&desc->bitmap [temp], 0xff, temp);
+ memset(&desc->u.hs.DeviceRemovable[0], 0, temp);
+ memset(&desc->u.hs.DeviceRemovable[temp], 0xff, temp);
temp = 0x0008; /* per-port overcurrent reporting */
if (HCS_PPC (ehci->hcs_params))
diff --git a/drivers/usb/host/ehci-lpm.c b/drivers/usb/host/ehci-lpm.c
index b4d4d63c13ed..2111627a19de 100644
--- a/drivers/usb/host/ehci-lpm.c
+++ b/drivers/usb/host/ehci-lpm.c
@@ -17,7 +17,8 @@
*/
/* this file is part of ehci-hcd.c */
-static int ehci_lpm_set_da(struct ehci_hcd *ehci, int dev_addr, int port_num)
+static int __maybe_unused ehci_lpm_set_da(struct ehci_hcd *ehci,
+ int dev_addr, int port_num)
{
u32 __iomem portsc;
@@ -37,7 +38,7 @@ static int ehci_lpm_set_da(struct ehci_hcd *ehci, int dev_addr, int port_num)
* this function is used to check if the device support LPM
* if yes, mark the PORTSC register with PORT_LPM bit
*/
-static int ehci_lpm_check(struct ehci_hcd *ehci, int port)
+static int __maybe_unused ehci_lpm_check(struct ehci_hcd *ehci, int port)
{
u32 __iomem *portsc ;
u32 val32;
diff --git a/drivers/usb/host/ehci-msm.c b/drivers/usb/host/ehci-msm.c
index 413f4deca532..9ce1b0bc186d 100644
--- a/drivers/usb/host/ehci-msm.c
+++ b/drivers/usb/host/ehci-msm.c
@@ -1,6 +1,6 @@
/* ehci-msm.c - HSUSB Host Controller Driver Implementation
*
- * Copyright (c) 2008-2010, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2008-2011, Code Aurora Forum. All rights reserved.
*
* Partly derived from ehci-fsl.c and ehci-hcd.c
* Copyright (c) 2000-2004 by David Brownell
@@ -34,92 +34,6 @@
static struct otg_transceiver *otg;
-/*
- * ehci_run defined in drivers/usb/host/ehci-hcd.c reset the controller and
- * the configuration settings in ehci_msm_reset vanish after controller is
- * reset. Resetting the controler in ehci_run seems to be un-necessary
- * provided HCD reset the controller before calling ehci_run. Most of the HCD
- * do but some are not. So this function is same as ehci_run but we don't
- * reset the controller here.
- */
-static int ehci_msm_run(struct usb_hcd *hcd)
-{
- struct ehci_hcd *ehci = hcd_to_ehci(hcd);
- u32 temp;
- u32 hcc_params;
-
- hcd->uses_new_polling = 1;
-
- ehci_writel(ehci, ehci->periodic_dma, &ehci->regs->frame_list);
- ehci_writel(ehci, (u32)ehci->async->qh_dma, &ehci->regs->async_next);
-
- /*
- * hcc_params controls whether ehci->regs->segment must (!!!)
- * be used; it constrains QH/ITD/SITD and QTD locations.
- * pci_pool consistent memory always uses segment zero.
- * streaming mappings for I/O buffers, like pci_map_single(),
- * can return segments above 4GB, if the device allows.
- *
- * NOTE: the dma mask is visible through dma_supported(), so
- * drivers can pass this info along ... like NETIF_F_HIGHDMA,
- * Scsi_Host.highmem_io, and so forth. It's readonly to all
- * host side drivers though.
- */
- hcc_params = ehci_readl(ehci, &ehci->caps->hcc_params);
- if (HCC_64BIT_ADDR(hcc_params))
- ehci_writel(ehci, 0, &ehci->regs->segment);
-
- /*
- * Philips, Intel, and maybe others need CMD_RUN before the
- * root hub will detect new devices (why?); NEC doesn't
- */
- ehci->command &= ~(CMD_LRESET|CMD_IAAD|CMD_PSE|CMD_ASE|CMD_RESET);
- ehci->command |= CMD_RUN;
- ehci_writel(ehci, ehci->command, &ehci->regs->command);
- dbg_cmd(ehci, "init", ehci->command);
-
- /*
- * Start, enabling full USB 2.0 functionality ... usb 1.1 devices
- * are explicitly handed to companion controller(s), so no TT is
- * involved with the root hub. (Except where one is integrated,
- * and there's no companion controller unless maybe for USB OTG.)
- *
- * Turning on the CF flag will transfer ownership of all ports
- * from the companions to the EHCI controller. If any of the
- * companions are in the middle of a port reset at the time, it
- * could cause trouble. Write-locking ehci_cf_port_reset_rwsem
- * guarantees that no resets are in progress. After we set CF,
- * a short delay lets the hardware catch up; new resets shouldn't
- * be started before the port switching actions could complete.
- */
- down_write(&ehci_cf_port_reset_rwsem);
- hcd->state = HC_STATE_RUNNING;
- ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag);
- ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */
- usleep_range(5000, 5500);
- up_write(&ehci_cf_port_reset_rwsem);
- ehci->last_periodic_enable = ktime_get_real();
-
- temp = HC_VERSION(ehci_readl(ehci, &ehci->caps->hc_capbase));
- ehci_info(ehci,
- "USB %x.%x started, EHCI %x.%02x%s\n",
- ((ehci->sbrn & 0xf0)>>4), (ehci->sbrn & 0x0f),
- temp >> 8, temp & 0xff,
- ignore_oc ? ", overcurrent ignored" : "");
-
- ehci_writel(ehci, INTR_MASK,
- &ehci->regs->intr_enable); /* Turn On Interrupts */
-
- /* GRR this is run-once init(), being done every time the HC starts.
- * So long as they're part of class devices, we can't do it init()
- * since the class device isn't created that early.
- */
- create_debug_files(ehci);
- create_companion_file(ehci);
-
- return 0;
-}
-
static int ehci_msm_reset(struct usb_hcd *hcd)
{
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
@@ -128,6 +42,8 @@ static int ehci_msm_reset(struct usb_hcd *hcd)
ehci->caps = USB_CAPLENGTH;
ehci->regs = USB_CAPLENGTH +
HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase));
+ dbg_hcs_params(ehci, "reset");
+ dbg_hcc_params(ehci, "reset");
/* cache the data to minimize the chip reads*/
ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
@@ -135,6 +51,10 @@ static int ehci_msm_reset(struct usb_hcd *hcd)
hcd->has_tt = 1;
ehci->sbrn = HCD_USB2;
+ retval = ehci_halt(ehci);
+ if (retval)
+ return retval;
+
/* data structure init */
retval = ehci_init(hcd);
if (retval)
@@ -167,7 +87,7 @@ static struct hc_driver msm_hc_driver = {
.flags = HCD_USB2 | HCD_MEMORY,
.reset = ehci_msm_reset,
- .start = ehci_msm_run,
+ .start = ehci_run,
.stop = ehci_stop,
.shutdown = ehci_shutdown,
diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c
index f784ceb862a3..7e41a95c5ceb 100644
--- a/drivers/usb/host/ehci-omap.c
+++ b/drivers/usb/host/ehci-omap.c
@@ -4,9 +4,10 @@
* Bus Glue for the EHCI controllers in OMAP3/4
* Tested on several OMAP3 boards, and OMAP4 Pandaboard
*
- * Copyright (C) 2007-2010 Texas Instruments, Inc.
+ * Copyright (C) 2007-2011 Texas Instruments, Inc.
* Author: Vikram Pandita <vikram.pandita@ti.com>
* Author: Anand Gadiyar <gadiyar@ti.com>
+ * Author: Keshava Munegowda <keshava_mgowda@ti.com>
*
* Copyright (C) 2009 Nokia Corporation
* Contact: Felipe Balbi <felipe.balbi@nokia.com>
@@ -27,116 +28,19 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
- * TODO (last updated Nov 21, 2010):
+ * TODO (last updated Feb 27, 2010):
* - add kernel-doc
* - enable AUTOIDLE
* - add suspend/resume
- * - move workarounds to board-files
- * - factor out code common to OHCI
* - add HSIC and TLL support
* - convert to use hwmod and runtime PM
*/
#include <linux/platform_device.h>
-#include <linux/clk.h>
-#include <linux/gpio.h>
-#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/usb/ulpi.h>
#include <plat/usb.h>
-/*
- * OMAP USBHOST Register addresses: VIRTUAL ADDRESSES
- * Use ehci_omap_readl()/ehci_omap_writel() functions
- */
-
-/* TLL Register Set */
-#define OMAP_USBTLL_REVISION (0x00)
-#define OMAP_USBTLL_SYSCONFIG (0x10)
-#define OMAP_USBTLL_SYSCONFIG_CACTIVITY (1 << 8)
-#define OMAP_USBTLL_SYSCONFIG_SIDLEMODE (1 << 3)
-#define OMAP_USBTLL_SYSCONFIG_ENAWAKEUP (1 << 2)
-#define OMAP_USBTLL_SYSCONFIG_SOFTRESET (1 << 1)
-#define OMAP_USBTLL_SYSCONFIG_AUTOIDLE (1 << 0)
-
-#define OMAP_USBTLL_SYSSTATUS (0x14)
-#define OMAP_USBTLL_SYSSTATUS_RESETDONE (1 << 0)
-
-#define OMAP_USBTLL_IRQSTATUS (0x18)
-#define OMAP_USBTLL_IRQENABLE (0x1C)
-
-#define OMAP_TLL_SHARED_CONF (0x30)
-#define OMAP_TLL_SHARED_CONF_USB_90D_DDR_EN (1 << 6)
-#define OMAP_TLL_SHARED_CONF_USB_180D_SDR_EN (1 << 5)
-#define OMAP_TLL_SHARED_CONF_USB_DIVRATION (1 << 2)
-#define OMAP_TLL_SHARED_CONF_FCLK_REQ (1 << 1)
-#define OMAP_TLL_SHARED_CONF_FCLK_IS_ON (1 << 0)
-
-#define OMAP_TLL_CHANNEL_CONF(num) (0x040 + 0x004 * num)
-#define OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF (1 << 11)
-#define OMAP_TLL_CHANNEL_CONF_ULPI_ULPIAUTOIDLE (1 << 10)
-#define OMAP_TLL_CHANNEL_CONF_UTMIAUTOIDLE (1 << 9)
-#define OMAP_TLL_CHANNEL_CONF_ULPIDDRMODE (1 << 8)
-#define OMAP_TLL_CHANNEL_CONF_CHANEN (1 << 0)
-
-#define OMAP_TLL_ULPI_FUNCTION_CTRL(num) (0x804 + 0x100 * num)
-#define OMAP_TLL_ULPI_INTERFACE_CTRL(num) (0x807 + 0x100 * num)
-#define OMAP_TLL_ULPI_OTG_CTRL(num) (0x80A + 0x100 * num)
-#define OMAP_TLL_ULPI_INT_EN_RISE(num) (0x80D + 0x100 * num)
-#define OMAP_TLL_ULPI_INT_EN_FALL(num) (0x810 + 0x100 * num)
-#define OMAP_TLL_ULPI_INT_STATUS(num) (0x813 + 0x100 * num)
-#define OMAP_TLL_ULPI_INT_LATCH(num) (0x814 + 0x100 * num)
-#define OMAP_TLL_ULPI_DEBUG(num) (0x815 + 0x100 * num)
-#define OMAP_TLL_ULPI_SCRATCH_REGISTER(num) (0x816 + 0x100 * num)
-
-#define OMAP_TLL_CHANNEL_COUNT 3
-#define OMAP_TLL_CHANNEL_1_EN_MASK (1 << 0)
-#define OMAP_TLL_CHANNEL_2_EN_MASK (1 << 1)
-#define OMAP_TLL_CHANNEL_3_EN_MASK (1 << 2)
-
-/* UHH Register Set */
-#define OMAP_UHH_REVISION (0x00)
-#define OMAP_UHH_SYSCONFIG (0x10)
-#define OMAP_UHH_SYSCONFIG_MIDLEMODE (1 << 12)
-#define OMAP_UHH_SYSCONFIG_CACTIVITY (1 << 8)
-#define OMAP_UHH_SYSCONFIG_SIDLEMODE (1 << 3)
-#define OMAP_UHH_SYSCONFIG_ENAWAKEUP (1 << 2)
-#define OMAP_UHH_SYSCONFIG_SOFTRESET (1 << 1)
-#define OMAP_UHH_SYSCONFIG_AUTOIDLE (1 << 0)
-
-#define OMAP_UHH_SYSSTATUS (0x14)
-#define OMAP_UHH_HOSTCONFIG (0x40)
-#define OMAP_UHH_HOSTCONFIG_ULPI_BYPASS (1 << 0)
-#define OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS (1 << 0)
-#define OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS (1 << 11)
-#define OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS (1 << 12)
-#define OMAP_UHH_HOSTCONFIG_INCR4_BURST_EN (1 << 2)
-#define OMAP_UHH_HOSTCONFIG_INCR8_BURST_EN (1 << 3)
-#define OMAP_UHH_HOSTCONFIG_INCR16_BURST_EN (1 << 4)
-#define OMAP_UHH_HOSTCONFIG_INCRX_ALIGN_EN (1 << 5)
-#define OMAP_UHH_HOSTCONFIG_P1_CONNECT_STATUS (1 << 8)
-#define OMAP_UHH_HOSTCONFIG_P2_CONNECT_STATUS (1 << 9)
-#define OMAP_UHH_HOSTCONFIG_P3_CONNECT_STATUS (1 << 10)
-
-/* OMAP4-specific defines */
-#define OMAP4_UHH_SYSCONFIG_IDLEMODE_CLEAR (3 << 2)
-#define OMAP4_UHH_SYSCONFIG_NOIDLE (1 << 2)
-
-#define OMAP4_UHH_SYSCONFIG_STDBYMODE_CLEAR (3 << 4)
-#define OMAP4_UHH_SYSCONFIG_NOSTDBY (1 << 4)
-#define OMAP4_UHH_SYSCONFIG_SOFTRESET (1 << 0)
-
-#define OMAP4_P1_MODE_CLEAR (3 << 16)
-#define OMAP4_P1_MODE_TLL (1 << 16)
-#define OMAP4_P1_MODE_HSIC (3 << 16)
-#define OMAP4_P2_MODE_CLEAR (3 << 18)
-#define OMAP4_P2_MODE_TLL (1 << 18)
-#define OMAP4_P2_MODE_HSIC (3 << 18)
-
-#define OMAP_REV2_TLL_CHANNEL_COUNT 2
-
-#define OMAP_UHH_DEBUG_CSR (0x44)
-
/* EHCI Register Set */
#define EHCI_INSNREG04 (0xA0)
#define EHCI_INSNREG04_DISABLE_UNSUSPEND (1 << 5)
@@ -148,137 +52,24 @@
#define EHCI_INSNREG05_ULPI_EXTREGADD_SHIFT 8
#define EHCI_INSNREG05_ULPI_WRDATA_SHIFT 0
-/* Values of UHH_REVISION - Note: these are not given in the TRM */
-#define OMAP_EHCI_REV1 0x00000010 /* OMAP3 */
-#define OMAP_EHCI_REV2 0x50700100 /* OMAP4 */
-
-#define is_omap_ehci_rev1(x) (x->omap_ehci_rev == OMAP_EHCI_REV1)
-#define is_omap_ehci_rev2(x) (x->omap_ehci_rev == OMAP_EHCI_REV2)
+/*-------------------------------------------------------------------------*/
-#define is_ehci_phy_mode(x) (x == EHCI_HCD_OMAP_MODE_PHY)
-#define is_ehci_tll_mode(x) (x == EHCI_HCD_OMAP_MODE_TLL)
-#define is_ehci_hsic_mode(x) (x == EHCI_HCD_OMAP_MODE_HSIC)
+static const struct hc_driver ehci_omap_hc_driver;
-/*-------------------------------------------------------------------------*/
-static inline void ehci_omap_writel(void __iomem *base, u32 reg, u32 val)
+static inline void ehci_write(void __iomem *base, u32 reg, u32 val)
{
__raw_writel(val, base + reg);
}
-static inline u32 ehci_omap_readl(void __iomem *base, u32 reg)
+static inline u32 ehci_read(void __iomem *base, u32 reg)
{
return __raw_readl(base + reg);
}
-static inline void ehci_omap_writeb(void __iomem *base, u8 reg, u8 val)
-{
- __raw_writeb(val, base + reg);
-}
-
-static inline u8 ehci_omap_readb(void __iomem *base, u8 reg)
-{
- return __raw_readb(base + reg);
-}
-
-/*-------------------------------------------------------------------------*/
-
-struct ehci_hcd_omap {
- struct ehci_hcd *ehci;
- struct device *dev;
-
- struct clk *usbhost_ick;
- struct clk *usbhost_hs_fck;
- struct clk *usbhost_fs_fck;
- struct clk *usbtll_fck;
- struct clk *usbtll_ick;
- struct clk *xclk60mhsp1_ck;
- struct clk *xclk60mhsp2_ck;
- struct clk *utmi_p1_fck;
- struct clk *utmi_p2_fck;
-
- /* FIXME the following two workarounds are
- * board specific not silicon-specific so these
- * should be moved to board-file instead.
- *
- * Maybe someone from TI will know better which
- * board is affected and needs the workarounds
- * to be applied
- */
-
- /* gpio for resetting phy */
- int reset_gpio_port[OMAP3_HS_USB_PORTS];
-
- /* phy reset workaround */
- int phy_reset;
-
- /* IP revision */
- u32 omap_ehci_rev;
-
- /* desired phy_mode: TLL, PHY */
- enum ehci_hcd_omap_mode port_mode[OMAP3_HS_USB_PORTS];
-
- void __iomem *uhh_base;
- void __iomem *tll_base;
- void __iomem *ehci_base;
-
- /* Regulators for USB PHYs.
- * Each PHY can have a separate regulator.
- */
- struct regulator *regulator[OMAP3_HS_USB_PORTS];
-};
-
-/*-------------------------------------------------------------------------*/
-
-static void omap_usb_utmi_init(struct ehci_hcd_omap *omap, u8 tll_channel_mask,
- u8 tll_channel_count)
-{
- unsigned reg;
- int i;
-
- /* Program the 3 TLL channels upfront */
- for (i = 0; i < tll_channel_count; i++) {
- reg = ehci_omap_readl(omap->tll_base, OMAP_TLL_CHANNEL_CONF(i));
-
- /* Disable AutoIdle, BitStuffing and use SDR Mode */
- reg &= ~(OMAP_TLL_CHANNEL_CONF_UTMIAUTOIDLE
- | OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF
- | OMAP_TLL_CHANNEL_CONF_ULPIDDRMODE);
- ehci_omap_writel(omap->tll_base, OMAP_TLL_CHANNEL_CONF(i), reg);
- }
-
- /* Program Common TLL register */
- reg = ehci_omap_readl(omap->tll_base, OMAP_TLL_SHARED_CONF);
- reg |= (OMAP_TLL_SHARED_CONF_FCLK_IS_ON
- | OMAP_TLL_SHARED_CONF_USB_DIVRATION
- | OMAP_TLL_SHARED_CONF_USB_180D_SDR_EN);
- reg &= ~OMAP_TLL_SHARED_CONF_USB_90D_DDR_EN;
-
- ehci_omap_writel(omap->tll_base, OMAP_TLL_SHARED_CONF, reg);
-
- /* Enable channels now */
- for (i = 0; i < tll_channel_count; i++) {
- reg = ehci_omap_readl(omap->tll_base, OMAP_TLL_CHANNEL_CONF(i));
-
- /* Enable only the reg that is needed */
- if (!(tll_channel_mask & 1<<i))
- continue;
-
- reg |= OMAP_TLL_CHANNEL_CONF_CHANEN;
- ehci_omap_writel(omap->tll_base, OMAP_TLL_CHANNEL_CONF(i), reg);
-
- ehci_omap_writeb(omap->tll_base,
- OMAP_TLL_ULPI_SCRATCH_REGISTER(i), 0xbe);
- dev_dbg(omap->dev, "ULPI_SCRATCH_REG[ch=%d]= 0x%02x\n",
- i+1, ehci_omap_readb(omap->tll_base,
- OMAP_TLL_ULPI_SCRATCH_REGISTER(i)));
- }
-}
-
-/*-------------------------------------------------------------------------*/
-
-static void omap_ehci_soft_phy_reset(struct ehci_hcd_omap *omap, u8 port)
+static void omap_ehci_soft_phy_reset(struct platform_device *pdev, u8 port)
{
+ struct usb_hcd *hcd = dev_get_drvdata(&pdev->dev);
unsigned long timeout = jiffies + msecs_to_jiffies(1000);
unsigned reg = 0;
@@ -292,266 +83,87 @@ static void omap_ehci_soft_phy_reset(struct ehci_hcd_omap *omap, u8 port)
/* start ULPI access*/
| (1 << EHCI_INSNREG05_ULPI_CONTROL_SHIFT);
- ehci_omap_writel(omap->ehci_base, EHCI_INSNREG05_ULPI, reg);
+ ehci_write(hcd->regs, EHCI_INSNREG05_ULPI, reg);
/* Wait for ULPI access completion */
- while ((ehci_omap_readl(omap->ehci_base, EHCI_INSNREG05_ULPI)
+ while ((ehci_read(hcd->regs, EHCI_INSNREG05_ULPI)
& (1 << EHCI_INSNREG05_ULPI_CONTROL_SHIFT))) {
cpu_relax();
if (time_after(jiffies, timeout)) {
- dev_dbg(omap->dev, "phy reset operation timed out\n");
+ dev_dbg(&pdev->dev, "phy reset operation timed out\n");
break;
}
}
}
-/* omap_start_ehc
- * - Start the TI USBHOST controller
- */
-static int omap_start_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd)
-{
- unsigned long timeout = jiffies + msecs_to_jiffies(1000);
- u8 tll_ch_mask = 0;
- unsigned reg = 0;
- int ret = 0;
-
- dev_dbg(omap->dev, "starting TI EHCI USB Controller\n");
- /* Enable Clocks for USBHOST */
- omap->usbhost_ick = clk_get(omap->dev, "usbhost_ick");
- if (IS_ERR(omap->usbhost_ick)) {
- ret = PTR_ERR(omap->usbhost_ick);
- goto err_host_ick;
- }
- clk_enable(omap->usbhost_ick);
-
- omap->usbhost_hs_fck = clk_get(omap->dev, "hs_fck");
- if (IS_ERR(omap->usbhost_hs_fck)) {
- ret = PTR_ERR(omap->usbhost_hs_fck);
- goto err_host_120m_fck;
- }
- clk_enable(omap->usbhost_hs_fck);
+/* configure so an HC device and id are always provided */
+/* always called with process context; sleeping is OK */
- omap->usbhost_fs_fck = clk_get(omap->dev, "fs_fck");
- if (IS_ERR(omap->usbhost_fs_fck)) {
- ret = PTR_ERR(omap->usbhost_fs_fck);
- goto err_host_48m_fck;
- }
- clk_enable(omap->usbhost_fs_fck);
-
- if (omap->phy_reset) {
- /* Refer: ISSUE1 */
- if (gpio_is_valid(omap->reset_gpio_port[0])) {
- gpio_request(omap->reset_gpio_port[0],
- "USB1 PHY reset");
- gpio_direction_output(omap->reset_gpio_port[0], 0);
- }
+/**
+ * ehci_hcd_omap_probe - initialize TI-based HCDs
+ *
+ * Allocates basic resources for this USB host controller, and
+ * then invokes the start() method for the HCD associated with it
+ * through the hotplug entry's driver_data.
+ */
+static int ehci_hcd_omap_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct ehci_hcd_omap_platform_data *pdata = dev->platform_data;
+ struct resource *res;
+ struct usb_hcd *hcd;
+ void __iomem *regs;
+ struct ehci_hcd *omap_ehci;
+ int ret = -ENODEV;
+ int irq;
- if (gpio_is_valid(omap->reset_gpio_port[1])) {
- gpio_request(omap->reset_gpio_port[1],
- "USB2 PHY reset");
- gpio_direction_output(omap->reset_gpio_port[1], 0);
- }
+ if (usb_disabled())
+ return -ENODEV;
- /* Hold the PHY in RESET for enough time till DIR is high */
- udelay(10);
+ if (!dev->parent) {
+ dev_err(dev, "Missing parent device\n");
+ return -ENODEV;
}
- /* Configure TLL for 60Mhz clk for ULPI */
- omap->usbtll_fck = clk_get(omap->dev, "usbtll_fck");
- if (IS_ERR(omap->usbtll_fck)) {
- ret = PTR_ERR(omap->usbtll_fck);
- goto err_tll_fck;
+ irq = platform_get_irq_byname(pdev, "ehci-irq");
+ if (irq < 0) {
+ dev_err(dev, "EHCI irq failed\n");
+ return -ENODEV;
}
- clk_enable(omap->usbtll_fck);
- omap->usbtll_ick = clk_get(omap->dev, "usbtll_ick");
- if (IS_ERR(omap->usbtll_ick)) {
- ret = PTR_ERR(omap->usbtll_ick);
- goto err_tll_ick;
+ res = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "ehci");
+ if (!res) {
+ dev_err(dev, "UHH EHCI get resource failed\n");
+ return -ENODEV;
}
- clk_enable(omap->usbtll_ick);
-
- omap->omap_ehci_rev = ehci_omap_readl(omap->uhh_base,
- OMAP_UHH_REVISION);
- dev_dbg(omap->dev, "OMAP UHH_REVISION 0x%x\n",
- omap->omap_ehci_rev);
- /*
- * Enable per-port clocks as needed (newer controllers only).
- * - External ULPI clock for PHY mode
- * - Internal clocks for TLL and HSIC modes (TODO)
- */
- if (is_omap_ehci_rev2(omap)) {
- switch (omap->port_mode[0]) {
- case EHCI_HCD_OMAP_MODE_PHY:
- omap->xclk60mhsp1_ck = clk_get(omap->dev,
- "xclk60mhsp1_ck");
- if (IS_ERR(omap->xclk60mhsp1_ck)) {
- ret = PTR_ERR(omap->xclk60mhsp1_ck);
- dev_err(omap->dev,
- "Unable to get Port1 ULPI clock\n");
- }
-
- omap->utmi_p1_fck = clk_get(omap->dev,
- "utmi_p1_gfclk");
- if (IS_ERR(omap->utmi_p1_fck)) {
- ret = PTR_ERR(omap->utmi_p1_fck);
- dev_err(omap->dev,
- "Unable to get utmi_p1_fck\n");
- }
-
- ret = clk_set_parent(omap->utmi_p1_fck,
- omap->xclk60mhsp1_ck);
- if (ret != 0) {
- dev_err(omap->dev,
- "Unable to set P1 f-clock\n");
- }
- break;
- case EHCI_HCD_OMAP_MODE_TLL:
- /* TODO */
- default:
- break;
- }
- switch (omap->port_mode[1]) {
- case EHCI_HCD_OMAP_MODE_PHY:
- omap->xclk60mhsp2_ck = clk_get(omap->dev,
- "xclk60mhsp2_ck");
- if (IS_ERR(omap->xclk60mhsp2_ck)) {
- ret = PTR_ERR(omap->xclk60mhsp2_ck);
- dev_err(omap->dev,
- "Unable to get Port2 ULPI clock\n");
- }
-
- omap->utmi_p2_fck = clk_get(omap->dev,
- "utmi_p2_gfclk");
- if (IS_ERR(omap->utmi_p2_fck)) {
- ret = PTR_ERR(omap->utmi_p2_fck);
- dev_err(omap->dev,
- "Unable to get utmi_p2_fck\n");
- }
-
- ret = clk_set_parent(omap->utmi_p2_fck,
- omap->xclk60mhsp2_ck);
- if (ret != 0) {
- dev_err(omap->dev,
- "Unable to set P2 f-clock\n");
- }
- break;
- case EHCI_HCD_OMAP_MODE_TLL:
- /* TODO */
- default:
- break;
- }
+ regs = ioremap(res->start, resource_size(res));
+ if (!regs) {
+ dev_err(dev, "UHH EHCI ioremap failed\n");
+ return -ENOMEM;
}
-
- /* perform TLL soft reset, and wait until reset is complete */
- ehci_omap_writel(omap->tll_base, OMAP_USBTLL_SYSCONFIG,
- OMAP_USBTLL_SYSCONFIG_SOFTRESET);
-
- /* Wait for TLL reset to complete */
- while (!(ehci_omap_readl(omap->tll_base, OMAP_USBTLL_SYSSTATUS)
- & OMAP_USBTLL_SYSSTATUS_RESETDONE)) {
- cpu_relax();
-
- if (time_after(jiffies, timeout)) {
- dev_dbg(omap->dev, "operation timed out\n");
- ret = -EINVAL;
- goto err_sys_status;
- }
+ hcd = usb_create_hcd(&ehci_omap_hc_driver, dev,
+ dev_name(dev));
+ if (!hcd) {
+ dev_err(dev, "failed to create hcd with err %d\n", ret);
+ ret = -ENOMEM;
+ goto err_io;
}
- dev_dbg(omap->dev, "TLL RESET DONE\n");
-
- /* (1<<3) = no idle mode only for initial debugging */
- ehci_omap_writel(omap->tll_base, OMAP_USBTLL_SYSCONFIG,
- OMAP_USBTLL_SYSCONFIG_ENAWAKEUP |
- OMAP_USBTLL_SYSCONFIG_SIDLEMODE |
- OMAP_USBTLL_SYSCONFIG_CACTIVITY);
-
-
- /* Put UHH in NoIdle/NoStandby mode */
- reg = ehci_omap_readl(omap->uhh_base, OMAP_UHH_SYSCONFIG);
- if (is_omap_ehci_rev1(omap)) {
- reg |= (OMAP_UHH_SYSCONFIG_ENAWAKEUP
- | OMAP_UHH_SYSCONFIG_SIDLEMODE
- | OMAP_UHH_SYSCONFIG_CACTIVITY
- | OMAP_UHH_SYSCONFIG_MIDLEMODE);
- reg &= ~OMAP_UHH_SYSCONFIG_AUTOIDLE;
-
+ hcd->rsrc_start = res->start;
+ hcd->rsrc_len = resource_size(res);
+ hcd->regs = regs;
- } else if (is_omap_ehci_rev2(omap)) {
- reg &= ~OMAP4_UHH_SYSCONFIG_IDLEMODE_CLEAR;
- reg |= OMAP4_UHH_SYSCONFIG_NOIDLE;
- reg &= ~OMAP4_UHH_SYSCONFIG_STDBYMODE_CLEAR;
- reg |= OMAP4_UHH_SYSCONFIG_NOSTDBY;
- }
- ehci_omap_writel(omap->uhh_base, OMAP_UHH_SYSCONFIG, reg);
-
- reg = ehci_omap_readl(omap->uhh_base, OMAP_UHH_HOSTCONFIG);
-
- /* setup ULPI bypass and burst configurations */
- reg |= (OMAP_UHH_HOSTCONFIG_INCR4_BURST_EN
- | OMAP_UHH_HOSTCONFIG_INCR8_BURST_EN
- | OMAP_UHH_HOSTCONFIG_INCR16_BURST_EN);
- reg &= ~OMAP_UHH_HOSTCONFIG_INCRX_ALIGN_EN;
-
- if (is_omap_ehci_rev1(omap)) {
- if (omap->port_mode[0] == EHCI_HCD_OMAP_MODE_UNKNOWN)
- reg &= ~OMAP_UHH_HOSTCONFIG_P1_CONNECT_STATUS;
- if (omap->port_mode[1] == EHCI_HCD_OMAP_MODE_UNKNOWN)
- reg &= ~OMAP_UHH_HOSTCONFIG_P2_CONNECT_STATUS;
- if (omap->port_mode[2] == EHCI_HCD_OMAP_MODE_UNKNOWN)
- reg &= ~OMAP_UHH_HOSTCONFIG_P3_CONNECT_STATUS;
-
- /* Bypass the TLL module for PHY mode operation */
- if (cpu_is_omap3430() && (omap_rev() <= OMAP3430_REV_ES2_1)) {
- dev_dbg(omap->dev, "OMAP3 ES version <= ES2.1\n");
- if (is_ehci_phy_mode(omap->port_mode[0]) ||
- is_ehci_phy_mode(omap->port_mode[1]) ||
- is_ehci_phy_mode(omap->port_mode[2]))
- reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_BYPASS;
- else
- reg |= OMAP_UHH_HOSTCONFIG_ULPI_BYPASS;
- } else {
- dev_dbg(omap->dev, "OMAP3 ES version > ES2.1\n");
- if (is_ehci_phy_mode(omap->port_mode[0]))
- reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS;
- else if (is_ehci_tll_mode(omap->port_mode[0]))
- reg |= OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS;
-
- if (is_ehci_phy_mode(omap->port_mode[1]))
- reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS;
- else if (is_ehci_tll_mode(omap->port_mode[1]))
- reg |= OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS;
-
- if (is_ehci_phy_mode(omap->port_mode[2]))
- reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS;
- else if (is_ehci_tll_mode(omap->port_mode[2]))
- reg |= OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS;
- }
- } else if (is_omap_ehci_rev2(omap)) {
- /* Clear port mode fields for PHY mode*/
- reg &= ~OMAP4_P1_MODE_CLEAR;
- reg &= ~OMAP4_P2_MODE_CLEAR;
-
- if (is_ehci_tll_mode(omap->port_mode[0]))
- reg |= OMAP4_P1_MODE_TLL;
- else if (is_ehci_hsic_mode(omap->port_mode[0]))
- reg |= OMAP4_P1_MODE_HSIC;
-
- if (is_ehci_tll_mode(omap->port_mode[1]))
- reg |= OMAP4_P2_MODE_TLL;
- else if (is_ehci_hsic_mode(omap->port_mode[1]))
- reg |= OMAP4_P2_MODE_HSIC;
+ ret = omap_usbhs_enable(dev);
+ if (ret) {
+ dev_err(dev, "failed to start usbhs with err %d\n", ret);
+ goto err_enable;
}
- ehci_omap_writel(omap->uhh_base, OMAP_UHH_HOSTCONFIG, reg);
- dev_dbg(omap->dev, "UHH setup done, uhh_hostconfig=%x\n", reg);
-
-
/*
* An undocumented "feature" in the OMAP3 EHCI controller,
* causes suspended ports to be taken out of suspend when
@@ -561,363 +173,50 @@ static int omap_start_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd)
* to suspend. Writing 1 to this undocumented register bit
* disables this feature and restores normal behavior.
*/
- ehci_omap_writel(omap->ehci_base, EHCI_INSNREG04,
+ ehci_write(regs, EHCI_INSNREG04,
EHCI_INSNREG04_DISABLE_UNSUSPEND);
- if ((omap->port_mode[0] == EHCI_HCD_OMAP_MODE_TLL) ||
- (omap->port_mode[1] == EHCI_HCD_OMAP_MODE_TLL) ||
- (omap->port_mode[2] == EHCI_HCD_OMAP_MODE_TLL)) {
-
- if (omap->port_mode[0] == EHCI_HCD_OMAP_MODE_TLL)
- tll_ch_mask |= OMAP_TLL_CHANNEL_1_EN_MASK;
- if (omap->port_mode[1] == EHCI_HCD_OMAP_MODE_TLL)
- tll_ch_mask |= OMAP_TLL_CHANNEL_2_EN_MASK;
- if (omap->port_mode[2] == EHCI_HCD_OMAP_MODE_TLL)
- tll_ch_mask |= OMAP_TLL_CHANNEL_3_EN_MASK;
-
- /* Enable UTMI mode for required TLL channels */
- omap_usb_utmi_init(omap, tll_ch_mask, OMAP_TLL_CHANNEL_COUNT);
- }
-
- if (omap->phy_reset) {
- /* Refer ISSUE1:
- * Hold the PHY in RESET for enough time till
- * PHY is settled and ready
- */
- udelay(10);
-
- if (gpio_is_valid(omap->reset_gpio_port[0]))
- gpio_set_value(omap->reset_gpio_port[0], 1);
-
- if (gpio_is_valid(omap->reset_gpio_port[1]))
- gpio_set_value(omap->reset_gpio_port[1], 1);
- }
-
/* Soft reset the PHY using PHY reset command over ULPI */
- if (omap->port_mode[0] == EHCI_HCD_OMAP_MODE_PHY)
- omap_ehci_soft_phy_reset(omap, 0);
- if (omap->port_mode[1] == EHCI_HCD_OMAP_MODE_PHY)
- omap_ehci_soft_phy_reset(omap, 1);
-
- return 0;
-
-err_sys_status:
- clk_disable(omap->utmi_p2_fck);
- clk_put(omap->utmi_p2_fck);
- clk_disable(omap->xclk60mhsp2_ck);
- clk_put(omap->xclk60mhsp2_ck);
- clk_disable(omap->utmi_p1_fck);
- clk_put(omap->utmi_p1_fck);
- clk_disable(omap->xclk60mhsp1_ck);
- clk_put(omap->xclk60mhsp1_ck);
- clk_disable(omap->usbtll_ick);
- clk_put(omap->usbtll_ick);
-
-err_tll_ick:
- clk_disable(omap->usbtll_fck);
- clk_put(omap->usbtll_fck);
-
-err_tll_fck:
- clk_disable(omap->usbhost_fs_fck);
- clk_put(omap->usbhost_fs_fck);
-
- if (omap->phy_reset) {
- if (gpio_is_valid(omap->reset_gpio_port[0]))
- gpio_free(omap->reset_gpio_port[0]);
-
- if (gpio_is_valid(omap->reset_gpio_port[1]))
- gpio_free(omap->reset_gpio_port[1]);
- }
-
-err_host_48m_fck:
- clk_disable(omap->usbhost_hs_fck);
- clk_put(omap->usbhost_hs_fck);
+ if (pdata->port_mode[0] == OMAP_EHCI_PORT_MODE_PHY)
+ omap_ehci_soft_phy_reset(pdev, 0);
+ if (pdata->port_mode[1] == OMAP_EHCI_PORT_MODE_PHY)
+ omap_ehci_soft_phy_reset(pdev, 1);
-err_host_120m_fck:
- clk_disable(omap->usbhost_ick);
- clk_put(omap->usbhost_ick);
-
-err_host_ick:
- return ret;
-}
-
-static void omap_stop_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd)
-{
- unsigned long timeout = jiffies + msecs_to_jiffies(100);
-
- dev_dbg(omap->dev, "stopping TI EHCI USB Controller\n");
-
- /* Reset OMAP modules for insmod/rmmod to work */
- ehci_omap_writel(omap->uhh_base, OMAP_UHH_SYSCONFIG,
- is_omap_ehci_rev2(omap) ?
- OMAP4_UHH_SYSCONFIG_SOFTRESET :
- OMAP_UHH_SYSCONFIG_SOFTRESET);
- while (!(ehci_omap_readl(omap->uhh_base, OMAP_UHH_SYSSTATUS)
- & (1 << 0))) {
- cpu_relax();
-
- if (time_after(jiffies, timeout))
- dev_dbg(omap->dev, "operation timed out\n");
- }
-
- while (!(ehci_omap_readl(omap->uhh_base, OMAP_UHH_SYSSTATUS)
- & (1 << 1))) {
- cpu_relax();
-
- if (time_after(jiffies, timeout))
- dev_dbg(omap->dev, "operation timed out\n");
- }
-
- while (!(ehci_omap_readl(omap->uhh_base, OMAP_UHH_SYSSTATUS)
- & (1 << 2))) {
- cpu_relax();
-
- if (time_after(jiffies, timeout))
- dev_dbg(omap->dev, "operation timed out\n");
- }
-
- ehci_omap_writel(omap->tll_base, OMAP_USBTLL_SYSCONFIG, (1 << 1));
-
- while (!(ehci_omap_readl(omap->tll_base, OMAP_USBTLL_SYSSTATUS)
- & (1 << 0))) {
- cpu_relax();
-
- if (time_after(jiffies, timeout))
- dev_dbg(omap->dev, "operation timed out\n");
- }
-
- if (omap->usbtll_fck != NULL) {
- clk_disable(omap->usbtll_fck);
- clk_put(omap->usbtll_fck);
- omap->usbtll_fck = NULL;
- }
-
- if (omap->usbhost_ick != NULL) {
- clk_disable(omap->usbhost_ick);
- clk_put(omap->usbhost_ick);
- omap->usbhost_ick = NULL;
- }
-
- if (omap->usbhost_fs_fck != NULL) {
- clk_disable(omap->usbhost_fs_fck);
- clk_put(omap->usbhost_fs_fck);
- omap->usbhost_fs_fck = NULL;
- }
-
- if (omap->usbhost_hs_fck != NULL) {
- clk_disable(omap->usbhost_hs_fck);
- clk_put(omap->usbhost_hs_fck);
- omap->usbhost_hs_fck = NULL;
- }
-
- if (omap->usbtll_ick != NULL) {
- clk_disable(omap->usbtll_ick);
- clk_put(omap->usbtll_ick);
- omap->usbtll_ick = NULL;
- }
-
- if (is_omap_ehci_rev2(omap)) {
- if (omap->xclk60mhsp1_ck != NULL) {
- clk_disable(omap->xclk60mhsp1_ck);
- clk_put(omap->xclk60mhsp1_ck);
- omap->xclk60mhsp1_ck = NULL;
- }
-
- if (omap->utmi_p1_fck != NULL) {
- clk_disable(omap->utmi_p1_fck);
- clk_put(omap->utmi_p1_fck);
- omap->utmi_p1_fck = NULL;
- }
-
- if (omap->xclk60mhsp2_ck != NULL) {
- clk_disable(omap->xclk60mhsp2_ck);
- clk_put(omap->xclk60mhsp2_ck);
- omap->xclk60mhsp2_ck = NULL;
- }
-
- if (omap->utmi_p2_fck != NULL) {
- clk_disable(omap->utmi_p2_fck);
- clk_put(omap->utmi_p2_fck);
- omap->utmi_p2_fck = NULL;
- }
- }
-
- if (omap->phy_reset) {
- if (gpio_is_valid(omap->reset_gpio_port[0]))
- gpio_free(omap->reset_gpio_port[0]);
-
- if (gpio_is_valid(omap->reset_gpio_port[1]))
- gpio_free(omap->reset_gpio_port[1]);
- }
-
- dev_dbg(omap->dev, "Clock to USB host has been disabled\n");
-}
-
-/*-------------------------------------------------------------------------*/
-
-static const struct hc_driver ehci_omap_hc_driver;
-
-/* configure so an HC device and id are always provided */
-/* always called with process context; sleeping is OK */
-
-/**
- * ehci_hcd_omap_probe - initialize TI-based HCDs
- *
- * Allocates basic resources for this USB host controller, and
- * then invokes the start() method for the HCD associated with it
- * through the hotplug entry's driver_data.
- */
-static int ehci_hcd_omap_probe(struct platform_device *pdev)
-{
- struct ehci_hcd_omap_platform_data *pdata = pdev->dev.platform_data;
- struct ehci_hcd_omap *omap;
- struct resource *res;
- struct usb_hcd *hcd;
-
- int irq = platform_get_irq(pdev, 0);
- int ret = -ENODEV;
- int i;
- char supply[7];
-
- if (!pdata) {
- dev_dbg(&pdev->dev, "missing platform_data\n");
- goto err_pdata;
- }
-
- if (usb_disabled())
- goto err_disabled;
-
- omap = kzalloc(sizeof(*omap), GFP_KERNEL);
- if (!omap) {
- ret = -ENOMEM;
- goto err_disabled;
- }
-
- hcd = usb_create_hcd(&ehci_omap_hc_driver, &pdev->dev,
- dev_name(&pdev->dev));
- if (!hcd) {
- dev_err(&pdev->dev, "failed to create hcd with err %d\n", ret);
- ret = -ENOMEM;
- goto err_create_hcd;
- }
-
- platform_set_drvdata(pdev, omap);
- omap->dev = &pdev->dev;
- omap->phy_reset = pdata->phy_reset;
- omap->reset_gpio_port[0] = pdata->reset_gpio_port[0];
- omap->reset_gpio_port[1] = pdata->reset_gpio_port[1];
- omap->reset_gpio_port[2] = pdata->reset_gpio_port[2];
- omap->port_mode[0] = pdata->port_mode[0];
- omap->port_mode[1] = pdata->port_mode[1];
- omap->port_mode[2] = pdata->port_mode[2];
- omap->ehci = hcd_to_ehci(hcd);
- omap->ehci->sbrn = 0x20;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-
- hcd->rsrc_start = res->start;
- hcd->rsrc_len = resource_size(res);
-
- hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
- if (!hcd->regs) {
- dev_err(&pdev->dev, "EHCI ioremap failed\n");
- ret = -ENOMEM;
- goto err_ioremap;
- }
+ omap_ehci = hcd_to_ehci(hcd);
+ omap_ehci->sbrn = 0x20;
/* we know this is the memory we want, no need to ioremap again */
- omap->ehci->caps = hcd->regs;
- omap->ehci_base = hcd->regs;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- omap->uhh_base = ioremap(res->start, resource_size(res));
- if (!omap->uhh_base) {
- dev_err(&pdev->dev, "UHH ioremap failed\n");
- ret = -ENOMEM;
- goto err_uhh_ioremap;
- }
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
- omap->tll_base = ioremap(res->start, resource_size(res));
- if (!omap->tll_base) {
- dev_err(&pdev->dev, "TLL ioremap failed\n");
- ret = -ENOMEM;
- goto err_tll_ioremap;
- }
-
- /* get ehci regulator and enable */
- for (i = 0 ; i < OMAP3_HS_USB_PORTS ; i++) {
- if (omap->port_mode[i] != EHCI_HCD_OMAP_MODE_PHY) {
- omap->regulator[i] = NULL;
- continue;
- }
- snprintf(supply, sizeof(supply), "hsusb%d", i);
- omap->regulator[i] = regulator_get(omap->dev, supply);
- if (IS_ERR(omap->regulator[i])) {
- omap->regulator[i] = NULL;
- dev_dbg(&pdev->dev,
- "failed to get ehci port%d regulator\n", i);
- } else {
- regulator_enable(omap->regulator[i]);
- }
- }
-
- ret = omap_start_ehc(omap, hcd);
- if (ret) {
- dev_err(&pdev->dev, "failed to start ehci with err %d\n", ret);
- goto err_start;
- }
+ omap_ehci->caps = hcd->regs;
+ omap_ehci->regs = hcd->regs
+ + HC_LENGTH(readl(&omap_ehci->caps->hc_capbase));
- omap->ehci->regs = hcd->regs
- + HC_LENGTH(readl(&omap->ehci->caps->hc_capbase));
-
- dbg_hcs_params(omap->ehci, "reset");
- dbg_hcc_params(omap->ehci, "reset");
+ dbg_hcs_params(omap_ehci, "reset");
+ dbg_hcc_params(omap_ehci, "reset");
/* cache this readonly data; minimize chip reads */
- omap->ehci->hcs_params = readl(&omap->ehci->caps->hcs_params);
+ omap_ehci->hcs_params = readl(&omap_ehci->caps->hcs_params);
ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED);
if (ret) {
- dev_err(&pdev->dev, "failed to add hcd with err %d\n", ret);
+ dev_err(dev, "failed to add hcd with err %d\n", ret);
goto err_add_hcd;
}
/* root ports should always stay powered */
- ehci_port_power(omap->ehci, 1);
+ ehci_port_power(omap_ehci, 1);
return 0;
err_add_hcd:
- omap_stop_ehc(omap, hcd);
+ omap_usbhs_disable(dev);
-err_start:
- for (i = 0 ; i < OMAP3_HS_USB_PORTS ; i++) {
- if (omap->regulator[i]) {
- regulator_disable(omap->regulator[i]);
- regulator_put(omap->regulator[i]);
- }
- }
- iounmap(omap->tll_base);
-
-err_tll_ioremap:
- iounmap(omap->uhh_base);
-
-err_uhh_ioremap:
- iounmap(hcd->regs);
-
-err_ioremap:
+err_enable:
usb_put_hcd(hcd);
-err_create_hcd:
- kfree(omap);
-err_disabled:
-err_pdata:
+err_io:
return ret;
}
-/* may be called without controller electrically present */
-/* may be called with controller, bus, and devices active */
/**
* ehci_hcd_omap_remove - shutdown processing for EHCI HCDs
@@ -929,31 +228,18 @@ err_pdata:
*/
static int ehci_hcd_omap_remove(struct platform_device *pdev)
{
- struct ehci_hcd_omap *omap = platform_get_drvdata(pdev);
- struct usb_hcd *hcd = ehci_to_hcd(omap->ehci);
- int i;
+ struct device *dev = &pdev->dev;
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
usb_remove_hcd(hcd);
- omap_stop_ehc(omap, hcd);
- iounmap(hcd->regs);
- for (i = 0 ; i < OMAP3_HS_USB_PORTS ; i++) {
- if (omap->regulator[i]) {
- regulator_disable(omap->regulator[i]);
- regulator_put(omap->regulator[i]);
- }
- }
- iounmap(omap->tll_base);
- iounmap(omap->uhh_base);
+ omap_usbhs_disable(dev);
usb_put_hcd(hcd);
- kfree(omap);
-
return 0;
}
static void ehci_hcd_omap_shutdown(struct platform_device *pdev)
{
- struct ehci_hcd_omap *omap = platform_get_drvdata(pdev);
- struct usb_hcd *hcd = ehci_to_hcd(omap->ehci);
+ struct usb_hcd *hcd = dev_get_drvdata(&pdev->dev);
if (hcd->driver->shutdown)
hcd->driver->shutdown(hcd);
diff --git a/drivers/usb/host/ehci-orion.c b/drivers/usb/host/ehci-orion.c
index 0f87dc72820a..281e094e1c18 100644
--- a/drivers/usb/host/ehci-orion.c
+++ b/drivers/usb/host/ehci-orion.c
@@ -105,7 +105,8 @@ static int ehci_orion_setup(struct usb_hcd *hcd)
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
int retval;
- ehci_reset(ehci);
+ hcd->has_tt = 1;
+
retval = ehci_halt(ehci);
if (retval)
return retval;
@@ -117,7 +118,7 @@ static int ehci_orion_setup(struct usb_hcd *hcd)
if (retval)
return retval;
- hcd->has_tt = 1;
+ ehci_reset(ehci);
ehci_port_power(ehci, 0);
diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c
index 07bb982e59f6..d5eaea7caf89 100644
--- a/drivers/usb/host/ehci-pci.c
+++ b/drivers/usb/host/ehci-pci.c
@@ -44,42 +44,6 @@ static int ehci_pci_reinit(struct ehci_hcd *ehci, struct pci_dev *pdev)
return 0;
}
-static int ehci_quirk_amd_hudson(struct ehci_hcd *ehci)
-{
- struct pci_dev *amd_smbus_dev;
- u8 rev = 0;
-
- amd_smbus_dev = pci_get_device(PCI_VENDOR_ID_ATI, 0x4385, NULL);
- if (amd_smbus_dev) {
- pci_read_config_byte(amd_smbus_dev, PCI_REVISION_ID, &rev);
- if (rev < 0x40) {
- pci_dev_put(amd_smbus_dev);
- amd_smbus_dev = NULL;
- return 0;
- }
- } else {
- amd_smbus_dev = pci_get_device(PCI_VENDOR_ID_AMD, 0x780b, NULL);
- if (!amd_smbus_dev)
- return 0;
- pci_read_config_byte(amd_smbus_dev, PCI_REVISION_ID, &rev);
- if (rev < 0x11 || rev > 0x18) {
- pci_dev_put(amd_smbus_dev);
- amd_smbus_dev = NULL;
- return 0;
- }
- }
-
- if (!amd_nb_dev)
- amd_nb_dev = pci_get_device(PCI_VENDOR_ID_AMD, 0x1510, NULL);
-
- ehci_info(ehci, "QUIRK: Enable exception for AMD Hudson ASPM\n");
-
- pci_dev_put(amd_smbus_dev);
- amd_smbus_dev = NULL;
-
- return 1;
-}
-
/* called during probe() after chip reset completes */
static int ehci_pci_setup(struct usb_hcd *hcd)
{
@@ -138,9 +102,6 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
/* cache this readonly data; minimize chip reads */
ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
- if (ehci_quirk_amd_hudson(ehci))
- ehci->amd_l1_fix = 1;
-
retval = ehci_halt(ehci);
if (retval)
return retval;
@@ -191,6 +152,9 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
}
break;
case PCI_VENDOR_ID_AMD:
+ /* AMD PLL quirk */
+ if (usb_amd_find_chipset_info())
+ ehci->amd_pll_fix = 1;
/* AMD8111 EHCI doesn't work, according to AMD errata */
if (pdev->device == 0x7463) {
ehci_info(ehci, "ignoring AMD8111 (errata)\n");
@@ -236,6 +200,9 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
}
break;
case PCI_VENDOR_ID_ATI:
+ /* AMD PLL quirk */
+ if (usb_amd_find_chipset_info())
+ ehci->amd_pll_fix = 1;
/* SB600 and old version of SB700 have a bug in EHCI controller,
* which causes usb devices lose response in some cases.
*/
diff --git a/drivers/usb/host/ehci-pmcmsp.c b/drivers/usb/host/ehci-pmcmsp.c
new file mode 100644
index 000000000000..a2168642175b
--- /dev/null
+++ b/drivers/usb/host/ehci-pmcmsp.c
@@ -0,0 +1,383 @@
+/*
+ * PMC MSP EHCI (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 2006-2010 PMC-Sierra Inc
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ */
+
+/* includes */
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/usb.h>
+#include <msp_usb.h>
+
+/* stream disable*/
+#define USB_CTRL_MODE_STREAM_DISABLE 0x10
+
+/* threshold */
+#define USB_CTRL_FIFO_THRESH 0x00300000
+
+/* register offset for usb_mode */
+#define USB_EHCI_REG_USB_MODE 0x68
+
+/* register offset for usb fifo */
+#define USB_EHCI_REG_USB_FIFO 0x24
+
+/* register offset for usb status */
+#define USB_EHCI_REG_USB_STATUS 0x44
+
+/* serial/parallel transceiver */
+#define USB_EHCI_REG_BIT_STAT_STS (1<<29)
+
+/* TWI USB0 host device pin */
+#define MSP_PIN_USB0_HOST_DEV 49
+
+/* TWI USB1 host device pin */
+#define MSP_PIN_USB1_HOST_DEV 50
+
+
+static void usb_hcd_tdi_set_mode(struct ehci_hcd *ehci)
+{
+ u8 *base;
+ u8 *statreg;
+ u8 *fiforeg;
+ u32 val;
+ struct ehci_regs *reg_base = ehci->regs;
+
+ /* get register base */
+ base = (u8 *)reg_base + USB_EHCI_REG_USB_MODE;
+ statreg = (u8 *)reg_base + USB_EHCI_REG_USB_STATUS;
+ fiforeg = (u8 *)reg_base + USB_EHCI_REG_USB_FIFO;
+
+ /* Disable controller mode stream */
+ val = ehci_readl(ehci, (u32 *)base);
+ ehci_writel(ehci, (val | USB_CTRL_MODE_STREAM_DISABLE),
+ (u32 *)base);
+
+ /* clear STS to select parallel transceiver interface */
+ val = ehci_readl(ehci, (u32 *)statreg);
+ val = val & ~USB_EHCI_REG_BIT_STAT_STS;
+ ehci_writel(ehci, val, (u32 *)statreg);
+
+ /* write to set the proper fifo threshold */
+ ehci_writel(ehci, USB_CTRL_FIFO_THRESH, (u32 *)fiforeg);
+
+ /* set TWI GPIO USB_HOST_DEV pin high */
+ gpio_direction_output(MSP_PIN_USB0_HOST_DEV, 1);
+#ifdef CONFIG_MSP_HAS_DUAL_USB
+ gpio_direction_output(MSP_PIN_USB1_HOST_DEV, 1);
+#endif
+}
+
+/* called during probe() after chip reset completes */
+static int ehci_msp_setup(struct usb_hcd *hcd)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ int retval;
+ ehci->big_endian_mmio = 1;
+ ehci->big_endian_desc = 1;
+
+ ehci->caps = hcd->regs;
+ ehci->regs = hcd->regs +
+ HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase));
+ dbg_hcs_params(ehci, "reset");
+ dbg_hcc_params(ehci, "reset");
+
+ /* cache this readonly data; minimize chip reads */
+ ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
+ hcd->has_tt = 1;
+
+ retval = ehci_halt(ehci);
+ if (retval)
+ return retval;
+
+ ehci_reset(ehci);
+
+ /* data structure init */
+ retval = ehci_init(hcd);
+ if (retval)
+ return retval;
+
+ usb_hcd_tdi_set_mode(ehci);
+ ehci_port_power(ehci, 0);
+
+ return retval;
+}
+
+
+/* configure so an HC device and id are always provided
+ * always called with process context; sleeping is OK
+ */
+
+static int usb_hcd_msp_map_regs(struct mspusb_device *dev)
+{
+ struct resource *res;
+ struct platform_device *pdev = &dev->dev;
+ u32 res_len;
+ int retval;
+
+ /* MAB register space */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (res == NULL)
+ return -ENOMEM;
+ res_len = res->end - res->start + 1;
+ if (!request_mem_region(res->start, res_len, "mab regs"))
+ return -EBUSY;
+
+ dev->mab_regs = ioremap_nocache(res->start, res_len);
+ if (dev->mab_regs == NULL) {
+ retval = -ENOMEM;
+ goto err1;
+ }
+
+ /* MSP USB register space */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+ if (res == NULL) {
+ retval = -ENOMEM;
+ goto err2;
+ }
+ res_len = res->end - res->start + 1;
+ if (!request_mem_region(res->start, res_len, "usbid regs")) {
+ retval = -EBUSY;
+ goto err2;
+ }
+ dev->usbid_regs = ioremap_nocache(res->start, res_len);
+ if (dev->usbid_regs == NULL) {
+ retval = -ENOMEM;
+ goto err3;
+ }
+
+ return 0;
+err3:
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+ res_len = res->end - res->start + 1;
+ release_mem_region(res->start, res_len);
+err2:
+ iounmap(dev->mab_regs);
+err1:
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ res_len = res->end - res->start + 1;
+ release_mem_region(res->start, res_len);
+ dev_err(&pdev->dev, "Failed to map non-EHCI regs.\n");
+ return retval;
+}
+
+/**
+ * usb_hcd_msp_probe - initialize PMC MSP-based HCDs
+ * Context: !in_interrupt()
+ *
+ * Allocates basic resources for this USB host controller, and
+ * then invokes the start() method for the HCD associated with it
+ * through the hotplug entry's driver_data.
+ *
+ */
+int usb_hcd_msp_probe(const struct hc_driver *driver,
+ struct platform_device *dev)
+{
+ int retval;
+ struct usb_hcd *hcd;
+ struct resource *res;
+ struct ehci_hcd *ehci ;
+
+ hcd = usb_create_hcd(driver, &dev->dev, "pmcmsp");
+ if (!hcd)
+ return -ENOMEM;
+
+ res = platform_get_resource(dev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ pr_debug("No IOMEM resource info for %s.\n", dev->name);
+ retval = -ENOMEM;
+ goto err1;
+ }
+ hcd->rsrc_start = res->start;
+ hcd->rsrc_len = res->end - res->start + 1;
+ if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, dev->name)) {
+ retval = -EBUSY;
+ goto err1;
+ }
+ hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len);
+ if (!hcd->regs) {
+ pr_debug("ioremap failed");
+ retval = -ENOMEM;
+ goto err2;
+ }
+
+ res = platform_get_resource(dev, IORESOURCE_IRQ, 0);
+ if (res == NULL) {
+ dev_err(&dev->dev, "No IRQ resource info for %s.\n", dev->name);
+ retval = -ENOMEM;
+ goto err3;
+ }
+
+ /* Map non-EHCI register spaces */
+ retval = usb_hcd_msp_map_regs(to_mspusb_device(dev));
+ if (retval != 0)
+ goto err3;
+
+ ehci = hcd_to_ehci(hcd);
+ ehci->big_endian_mmio = 1;
+ ehci->big_endian_desc = 1;
+
+
+ retval = usb_add_hcd(hcd, res->start, IRQF_SHARED);
+ if (retval == 0)
+ return 0;
+
+ usb_remove_hcd(hcd);
+err3:
+ iounmap(hcd->regs);
+err2:
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+err1:
+ usb_put_hcd(hcd);
+
+ return retval;
+}
+
+
+
+/**
+ * usb_hcd_msp_remove - shutdown processing for PMC MSP-based HCDs
+ * @dev: USB Host Controller being removed
+ * Context: !in_interrupt()
+ *
+ * Reverses the effect of usb_hcd_msp_probe(), first invoking
+ * the HCD's stop() method. It is always called from a thread
+ * context, normally "rmmod", "apmd", or something similar.
+ *
+ * may be called without controller electrically present
+ * may be called with controller, bus, and devices active
+ */
+void usb_hcd_msp_remove(struct usb_hcd *hcd, struct platform_device *dev)
+{
+ usb_remove_hcd(hcd);
+ iounmap(hcd->regs);
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+ usb_put_hcd(hcd);
+}
+
+#ifdef CONFIG_MSP_HAS_DUAL_USB
+/*
+ * Wrapper around the main ehci_irq. Since both USB host controllers are
+ * sharing the same IRQ, need to first determine whether we're the intended
+ * recipient of this interrupt.
+ */
+static irqreturn_t ehci_msp_irq(struct usb_hcd *hcd)
+{
+ u32 int_src;
+ struct device *dev = hcd->self.controller;
+ struct platform_device *pdev;
+ struct mspusb_device *mdev;
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ /* need to reverse-map a couple of containers to get our device */
+ pdev = to_platform_device(dev);
+ mdev = to_mspusb_device(pdev);
+
+ /* Check to see if this interrupt is for this host controller */
+ int_src = ehci_readl(ehci, &mdev->mab_regs->int_stat);
+ if (int_src & (1 << pdev->id))
+ return ehci_irq(hcd);
+
+ /* Not for this device */
+ return IRQ_NONE;
+}
+#endif /* DUAL_USB */
+
+static const struct hc_driver ehci_msp_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "PMC MSP EHCI",
+ .hcd_priv_size = sizeof(struct ehci_hcd),
+
+ /*
+ * generic hardware linkage
+ */
+#ifdef CONFIG_MSP_HAS_DUAL_USB
+ .irq = ehci_msp_irq,
+#else
+ .irq = ehci_irq,
+#endif
+ .flags = HCD_MEMORY | HCD_USB2,
+
+ /*
+ * basic lifecycle operations
+ */
+ .reset = ehci_msp_setup,
+ .start = ehci_run,
+ .shutdown = ehci_shutdown,
+ .start = ehci_run,
+ .stop = ehci_stop,
+
+ /*
+ * managing i/o requests and associated device resources
+ */
+ .urb_enqueue = ehci_urb_enqueue,
+ .urb_dequeue = ehci_urb_dequeue,
+ .endpoint_disable = ehci_endpoint_disable,
+ .endpoint_reset = ehci_endpoint_reset,
+
+ /*
+ * 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,
+ .relinquish_port = ehci_relinquish_port,
+ .port_handed_over = ehci_port_handed_over,
+
+ .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
+};
+
+static int ehci_hcd_msp_drv_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ pr_debug("In ehci_hcd_msp_drv_probe");
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ gpio_request(MSP_PIN_USB0_HOST_DEV, "USB0_HOST_DEV_GPIO");
+#ifdef CONFIG_MSP_HAS_DUAL_USB
+ gpio_request(MSP_PIN_USB1_HOST_DEV, "USB1_HOST_DEV_GPIO");
+#endif
+
+ ret = usb_hcd_msp_probe(&ehci_msp_hc_driver, pdev);
+
+ return ret;
+}
+
+static int ehci_hcd_msp_drv_remove(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+
+ usb_hcd_msp_remove(hcd, pdev);
+
+ /* free TWI GPIO USB_HOST_DEV pin */
+ gpio_free(MSP_PIN_USB0_HOST_DEV);
+#ifdef CONFIG_MSP_HAS_DUAL_USB
+ gpio_free(MSP_PIN_USB1_HOST_DEV);
+#endif
+
+ return 0;
+}
+
+MODULE_ALIAS("pmcmsp-ehci");
+
+static struct platform_driver ehci_hcd_msp_driver = {
+ .probe = ehci_hcd_msp_drv_probe,
+ .remove = ehci_hcd_msp_drv_remove,
+ .driver = {
+ .name = "pmcmsp-ehci",
+ .owner = THIS_MODULE,
+ },
+};
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
index 233c288e3f93..fe99895fb098 100644
--- a/drivers/usb/host/ehci-q.c
+++ b/drivers/usb/host/ehci-q.c
@@ -1107,22 +1107,24 @@ submit_async (
struct list_head *qtd_list,
gfp_t mem_flags
) {
- struct ehci_qtd *qtd;
int epnum;
unsigned long flags;
struct ehci_qh *qh = NULL;
int rc;
- qtd = list_entry (qtd_list->next, struct ehci_qtd, qtd_list);
epnum = urb->ep->desc.bEndpointAddress;
#ifdef EHCI_URB_TRACE
- ehci_dbg (ehci,
- "%s %s urb %p ep%d%s len %d, qtd %p [qh %p]\n",
- __func__, urb->dev->devpath, urb,
- epnum & 0x0f, (epnum & USB_DIR_IN) ? "in" : "out",
- urb->transfer_buffer_length,
- qtd, urb->ep->hcpriv);
+ {
+ struct ehci_qtd *qtd;
+ qtd = list_entry(qtd_list->next, struct ehci_qtd, qtd_list);
+ ehci_dbg(ehci,
+ "%s %s urb %p ep%d%s len %d, qtd %p [qh %p]\n",
+ __func__, urb->dev->devpath, urb,
+ epnum & 0x0f, (epnum & USB_DIR_IN) ? "in" : "out",
+ urb->transfer_buffer_length,
+ qtd, urb->ep->hcpriv);
+ }
#endif
spin_lock_irqsave (&ehci->lock, flags);
diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c
index aa46f57f9ec8..1543c838b3d1 100644
--- a/drivers/usb/host/ehci-sched.c
+++ b/drivers/usb/host/ehci-sched.c
@@ -1048,8 +1048,6 @@ iso_stream_put(struct ehci_hcd *ehci, struct ehci_iso_stream *stream)
* not like a QH -- no persistent state (toggle, halt)
*/
if (stream->refcount == 1) {
- int is_in;
-
// BUG_ON (!list_empty(&stream->td_list));
while (!list_empty (&stream->free_list)) {
@@ -1076,7 +1074,6 @@ iso_stream_put(struct ehci_hcd *ehci, struct ehci_iso_stream *stream)
}
}
- is_in = (stream->bEndpointAddress & USB_DIR_IN) ? 0x10 : 0;
stream->bEndpointAddress &= 0x0f;
if (stream->ep)
stream->ep->hcpriv = NULL;
@@ -1590,63 +1587,6 @@ itd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_itd *itd)
*hw_p = cpu_to_hc32(ehci, itd->itd_dma | Q_TYPE_ITD);
}
-#define AB_REG_BAR_LOW 0xe0
-#define AB_REG_BAR_HIGH 0xe1
-#define AB_INDX(addr) ((addr) + 0x00)
-#define AB_DATA(addr) ((addr) + 0x04)
-#define NB_PCIE_INDX_ADDR 0xe0
-#define NB_PCIE_INDX_DATA 0xe4
-#define NB_PIF0_PWRDOWN_0 0x01100012
-#define NB_PIF0_PWRDOWN_1 0x01100013
-
-static void ehci_quirk_amd_L1(struct ehci_hcd *ehci, int disable)
-{
- u32 addr, addr_low, addr_high, val;
-
- outb_p(AB_REG_BAR_LOW, 0xcd6);
- addr_low = inb_p(0xcd7);
- outb_p(AB_REG_BAR_HIGH, 0xcd6);
- addr_high = inb_p(0xcd7);
- addr = addr_high << 8 | addr_low;
- outl_p(0x30, AB_INDX(addr));
- outl_p(0x40, AB_DATA(addr));
- outl_p(0x34, AB_INDX(addr));
- val = inl_p(AB_DATA(addr));
-
- if (disable) {
- val &= ~0x8;
- val |= (1 << 4) | (1 << 9);
- } else {
- val |= 0x8;
- val &= ~((1 << 4) | (1 << 9));
- }
- outl_p(val, AB_DATA(addr));
-
- if (amd_nb_dev) {
- addr = NB_PIF0_PWRDOWN_0;
- pci_write_config_dword(amd_nb_dev, NB_PCIE_INDX_ADDR, addr);
- pci_read_config_dword(amd_nb_dev, NB_PCIE_INDX_DATA, &val);
- if (disable)
- val &= ~(0x3f << 7);
- else
- val |= 0x3f << 7;
-
- pci_write_config_dword(amd_nb_dev, NB_PCIE_INDX_DATA, val);
-
- addr = NB_PIF0_PWRDOWN_1;
- pci_write_config_dword(amd_nb_dev, NB_PCIE_INDX_ADDR, addr);
- pci_read_config_dword(amd_nb_dev, NB_PCIE_INDX_DATA, &val);
- if (disable)
- val &= ~(0x3f << 7);
- else
- val |= 0x3f << 7;
-
- pci_write_config_dword(amd_nb_dev, NB_PCIE_INDX_DATA, val);
- }
-
- return;
-}
-
/* fit urb's itds into the selected schedule slot; activate as needed */
static int
itd_link_urb (
@@ -1675,8 +1615,8 @@ itd_link_urb (
}
if (ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs == 0) {
- if (ehci->amd_l1_fix == 1)
- ehci_quirk_amd_L1(ehci, 1);
+ if (ehci->amd_pll_fix == 1)
+ usb_amd_quirk_pll_disable();
}
ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs++;
@@ -1804,8 +1744,8 @@ itd_complete (
ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--;
if (ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs == 0) {
- if (ehci->amd_l1_fix == 1)
- ehci_quirk_amd_L1(ehci, 0);
+ if (ehci->amd_pll_fix == 1)
+ usb_amd_quirk_pll_enable();
}
if (unlikely(list_is_singular(&stream->td_list))) {
@@ -2095,8 +2035,8 @@ sitd_link_urb (
}
if (ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs == 0) {
- if (ehci->amd_l1_fix == 1)
- ehci_quirk_amd_L1(ehci, 1);
+ if (ehci->amd_pll_fix == 1)
+ usb_amd_quirk_pll_disable();
}
ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs++;
@@ -2200,8 +2140,8 @@ sitd_complete (
ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--;
if (ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs == 0) {
- if (ehci->amd_l1_fix == 1)
- ehci_quirk_amd_L1(ehci, 0);
+ if (ehci->amd_pll_fix == 1)
+ usb_amd_quirk_pll_enable();
}
if (list_is_singular(&stream->td_list)) {
diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c
new file mode 100644
index 000000000000..a516af28c29b
--- /dev/null
+++ b/drivers/usb/host/ehci-tegra.c
@@ -0,0 +1,715 @@
+/*
+ * EHCI-compliant USB host controller driver for NVIDIA Tegra SoCs
+ *
+ * Copyright (C) 2010 Google, Inc.
+ * Copyright (C) 2009 NVIDIA Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/tegra_usb.h>
+#include <linux/irq.h>
+#include <linux/usb/otg.h>
+#include <mach/usb_phy.h>
+
+#define TEGRA_USB_DMA_ALIGN 32
+
+struct tegra_ehci_hcd {
+ struct ehci_hcd *ehci;
+ struct tegra_usb_phy *phy;
+ struct clk *clk;
+ struct clk *emc_clk;
+ struct otg_transceiver *transceiver;
+ int host_resumed;
+ int bus_suspended;
+ int port_resuming;
+ int power_down_on_bus_suspend;
+ enum tegra_usb_phy_port_speed port_speed;
+};
+
+static void tegra_ehci_power_up(struct usb_hcd *hcd)
+{
+ struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller);
+
+ clk_enable(tegra->emc_clk);
+ clk_enable(tegra->clk);
+ tegra_usb_phy_power_on(tegra->phy);
+ tegra->host_resumed = 1;
+}
+
+static void tegra_ehci_power_down(struct usb_hcd *hcd)
+{
+ struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller);
+
+ tegra->host_resumed = 0;
+ tegra_usb_phy_power_off(tegra->phy);
+ clk_disable(tegra->clk);
+ clk_disable(tegra->emc_clk);
+}
+
+static int tegra_ehci_hub_control(
+ struct usb_hcd *hcd,
+ u16 typeReq,
+ u16 wValue,
+ u16 wIndex,
+ char *buf,
+ u16 wLength
+)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller);
+ u32 __iomem *status_reg;
+ u32 temp;
+ unsigned long flags;
+ int retval = 0;
+
+ status_reg = &ehci->regs->port_status[(wIndex & 0xff) - 1];
+
+ spin_lock_irqsave(&ehci->lock, flags);
+
+ /*
+ * In ehci_hub_control() for USB_PORT_FEAT_ENABLE clears the other bits
+ * that are write on clear, by writing back the register read value, so
+ * USB_PORT_FEAT_ENABLE is handled by masking the set on clear bits
+ */
+ if (typeReq == ClearPortFeature && wValue == USB_PORT_FEAT_ENABLE) {
+ temp = ehci_readl(ehci, status_reg) & ~PORT_RWC_BITS;
+ ehci_writel(ehci, temp & ~PORT_PE, status_reg);
+ goto done;
+ }
+
+ else if (typeReq == GetPortStatus) {
+ temp = ehci_readl(ehci, status_reg);
+ if (tegra->port_resuming && !(temp & PORT_SUSPEND)) {
+ /* Resume completed, re-enable disconnect detection */
+ tegra->port_resuming = 0;
+ tegra_usb_phy_postresume(tegra->phy);
+ }
+ }
+
+ else if (typeReq == SetPortFeature && wValue == USB_PORT_FEAT_SUSPEND) {
+ temp = ehci_readl(ehci, status_reg);
+ if ((temp & PORT_PE) == 0 || (temp & PORT_RESET) != 0) {
+ retval = -EPIPE;
+ goto done;
+ }
+
+ temp &= ~PORT_WKCONN_E;
+ temp |= PORT_WKDISC_E | PORT_WKOC_E;
+ ehci_writel(ehci, temp | PORT_SUSPEND, status_reg);
+
+ /*
+ * If a transaction is in progress, there may be a delay in
+ * suspending the port. Poll until the port is suspended.
+ */
+ if (handshake(ehci, status_reg, PORT_SUSPEND,
+ PORT_SUSPEND, 5000))
+ pr_err("%s: timeout waiting for SUSPEND\n", __func__);
+
+ set_bit((wIndex & 0xff) - 1, &ehci->suspended_ports);
+ goto done;
+ }
+
+ /*
+ * Tegra host controller will time the resume operation to clear the bit
+ * when the port control state switches to HS or FS Idle. This behavior
+ * is different from EHCI where the host controller driver is required
+ * to set this bit to a zero after the resume duration is timed in the
+ * driver.
+ */
+ else if (typeReq == ClearPortFeature &&
+ wValue == USB_PORT_FEAT_SUSPEND) {
+ temp = ehci_readl(ehci, status_reg);
+ if ((temp & PORT_RESET) || !(temp & PORT_PE)) {
+ retval = -EPIPE;
+ goto done;
+ }
+
+ if (!(temp & PORT_SUSPEND))
+ goto done;
+
+ /* Disable disconnect detection during port resume */
+ tegra_usb_phy_preresume(tegra->phy);
+
+ ehci->reset_done[wIndex-1] = jiffies + msecs_to_jiffies(25);
+
+ temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS);
+ /* start resume signalling */
+ ehci_writel(ehci, temp | PORT_RESUME, status_reg);
+
+ spin_unlock_irqrestore(&ehci->lock, flags);
+ msleep(20);
+ spin_lock_irqsave(&ehci->lock, flags);
+
+ /* Poll until the controller clears RESUME and SUSPEND */
+ if (handshake(ehci, status_reg, PORT_RESUME, 0, 2000))
+ pr_err("%s: timeout waiting for RESUME\n", __func__);
+ if (handshake(ehci, status_reg, PORT_SUSPEND, 0, 2000))
+ pr_err("%s: timeout waiting for SUSPEND\n", __func__);
+
+ ehci->reset_done[wIndex-1] = 0;
+
+ tegra->port_resuming = 1;
+ goto done;
+ }
+
+ spin_unlock_irqrestore(&ehci->lock, flags);
+
+ /* Handle the hub control events here */
+ return ehci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength);
+done:
+ spin_unlock_irqrestore(&ehci->lock, flags);
+ return retval;
+}
+
+static void tegra_ehci_restart(struct usb_hcd *hcd)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+
+ ehci_reset(ehci);
+
+ /* setup the frame list and Async q heads */
+ ehci_writel(ehci, ehci->periodic_dma, &ehci->regs->frame_list);
+ ehci_writel(ehci, (u32)ehci->async->qh_dma, &ehci->regs->async_next);
+ /* setup the command register and set the controller in RUN mode */
+ ehci->command &= ~(CMD_LRESET|CMD_IAAD|CMD_PSE|CMD_ASE|CMD_RESET);
+ ehci->command |= CMD_RUN;
+ ehci_writel(ehci, ehci->command, &ehci->regs->command);
+
+ down_write(&ehci_cf_port_reset_rwsem);
+ ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag);
+ /* flush posted writes */
+ ehci_readl(ehci, &ehci->regs->command);
+ up_write(&ehci_cf_port_reset_rwsem);
+}
+
+static int tegra_usb_suspend(struct usb_hcd *hcd)
+{
+ struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller);
+ struct ehci_regs __iomem *hw = tegra->ehci->regs;
+ unsigned long flags;
+
+ spin_lock_irqsave(&tegra->ehci->lock, flags);
+
+ tegra->port_speed = (readl(&hw->port_status[0]) >> 26) & 0x3;
+ ehci_halt(tegra->ehci);
+ clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+
+ spin_unlock_irqrestore(&tegra->ehci->lock, flags);
+
+ tegra_ehci_power_down(hcd);
+ return 0;
+}
+
+static int tegra_usb_resume(struct usb_hcd *hcd)
+{
+ struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller);
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ struct ehci_regs __iomem *hw = ehci->regs;
+ unsigned long val;
+
+ set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+ tegra_ehci_power_up(hcd);
+
+ if (tegra->port_speed > TEGRA_USB_PHY_PORT_SPEED_HIGH) {
+ /* Wait for the phy to detect new devices
+ * before we restart the controller */
+ msleep(10);
+ goto restart;
+ }
+
+ /* Force the phy to keep data lines in suspend state */
+ tegra_ehci_phy_restore_start(tegra->phy, tegra->port_speed);
+
+ /* Enable host mode */
+ tdi_reset(ehci);
+
+ /* Enable Port Power */
+ val = readl(&hw->port_status[0]);
+ val |= PORT_POWER;
+ writel(val, &hw->port_status[0]);
+ udelay(10);
+
+ /* Check if the phy resume from LP0. When the phy resume from LP0
+ * USB register will be reset. */
+ if (!readl(&hw->async_next)) {
+ /* Program the field PTC based on the saved speed mode */
+ val = readl(&hw->port_status[0]);
+ val &= ~PORT_TEST(~0);
+ if (tegra->port_speed == TEGRA_USB_PHY_PORT_SPEED_HIGH)
+ val |= PORT_TEST_FORCE;
+ else if (tegra->port_speed == TEGRA_USB_PHY_PORT_SPEED_FULL)
+ val |= PORT_TEST(6);
+ else if (tegra->port_speed == TEGRA_USB_PHY_PORT_SPEED_LOW)
+ val |= PORT_TEST(7);
+ writel(val, &hw->port_status[0]);
+ udelay(10);
+
+ /* Disable test mode by setting PTC field to NORMAL_OP */
+ val = readl(&hw->port_status[0]);
+ val &= ~PORT_TEST(~0);
+ writel(val, &hw->port_status[0]);
+ udelay(10);
+ }
+
+ /* Poll until CCS is enabled */
+ if (handshake(ehci, &hw->port_status[0], PORT_CONNECT,
+ PORT_CONNECT, 2000)) {
+ pr_err("%s: timeout waiting for PORT_CONNECT\n", __func__);
+ goto restart;
+ }
+
+ /* Poll until PE is enabled */
+ if (handshake(ehci, &hw->port_status[0], PORT_PE,
+ PORT_PE, 2000)) {
+ pr_err("%s: timeout waiting for USB_PORTSC1_PE\n", __func__);
+ goto restart;
+ }
+
+ /* Clear the PCI status, to avoid an interrupt taken upon resume */
+ val = readl(&hw->status);
+ val |= STS_PCD;
+ writel(val, &hw->status);
+
+ /* Put controller in suspend mode by writing 1 to SUSP bit of PORTSC */
+ val = readl(&hw->port_status[0]);
+ if ((val & PORT_POWER) && (val & PORT_PE)) {
+ val |= PORT_SUSPEND;
+ writel(val, &hw->port_status[0]);
+
+ /* Wait until port suspend completes */
+ if (handshake(ehci, &hw->port_status[0], PORT_SUSPEND,
+ PORT_SUSPEND, 1000)) {
+ pr_err("%s: timeout waiting for PORT_SUSPEND\n",
+ __func__);
+ goto restart;
+ }
+ }
+
+ tegra_ehci_phy_restore_end(tegra->phy);
+ return 0;
+
+restart:
+ if (tegra->port_speed <= TEGRA_USB_PHY_PORT_SPEED_HIGH)
+ tegra_ehci_phy_restore_end(tegra->phy);
+
+ tegra_ehci_restart(hcd);
+ return 0;
+}
+
+static void tegra_ehci_shutdown(struct usb_hcd *hcd)
+{
+ struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller);
+
+ /* ehci_shutdown touches the USB controller registers, make sure
+ * controller has clocks to it */
+ if (!tegra->host_resumed)
+ tegra_ehci_power_up(hcd);
+
+ ehci_shutdown(hcd);
+}
+
+static int tegra_ehci_setup(struct usb_hcd *hcd)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ int retval;
+
+ /* EHCI registers start at offset 0x100 */
+ ehci->caps = hcd->regs + 0x100;
+ ehci->regs = hcd->regs + 0x100 +
+ HC_LENGTH(readl(&ehci->caps->hc_capbase));
+
+ dbg_hcs_params(ehci, "reset");
+ dbg_hcc_params(ehci, "reset");
+
+ /* cache this readonly data; minimize chip reads */
+ ehci->hcs_params = readl(&ehci->caps->hcs_params);
+
+ /* switch to host mode */
+ hcd->has_tt = 1;
+ ehci_reset(ehci);
+
+ retval = ehci_halt(ehci);
+ if (retval)
+ return retval;
+
+ /* data structure init */
+ retval = ehci_init(hcd);
+ if (retval)
+ return retval;
+
+ ehci->sbrn = 0x20;
+
+ ehci_port_power(ehci, 1);
+ return retval;
+}
+
+#ifdef CONFIG_PM
+static int tegra_ehci_bus_suspend(struct usb_hcd *hcd)
+{
+ struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller);
+ int error_status = 0;
+
+ error_status = ehci_bus_suspend(hcd);
+ if (!error_status && tegra->power_down_on_bus_suspend) {
+ tegra_usb_suspend(hcd);
+ tegra->bus_suspended = 1;
+ }
+
+ return error_status;
+}
+
+static int tegra_ehci_bus_resume(struct usb_hcd *hcd)
+{
+ struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller);
+
+ if (tegra->bus_suspended && tegra->power_down_on_bus_suspend) {
+ tegra_usb_resume(hcd);
+ tegra->bus_suspended = 0;
+ }
+
+ tegra_usb_phy_preresume(tegra->phy);
+ tegra->port_resuming = 1;
+ return ehci_bus_resume(hcd);
+}
+#endif
+
+struct temp_buffer {
+ void *kmalloc_ptr;
+ void *old_xfer_buffer;
+ u8 data[0];
+};
+
+static void free_temp_buffer(struct urb *urb)
+{
+ enum dma_data_direction dir;
+ struct temp_buffer *temp;
+
+ if (!(urb->transfer_flags & URB_ALIGNED_TEMP_BUFFER))
+ return;
+
+ dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+
+ temp = container_of(urb->transfer_buffer, struct temp_buffer,
+ data);
+
+ if (dir == DMA_FROM_DEVICE)
+ memcpy(temp->old_xfer_buffer, temp->data,
+ urb->transfer_buffer_length);
+ urb->transfer_buffer = temp->old_xfer_buffer;
+ kfree(temp->kmalloc_ptr);
+
+ urb->transfer_flags &= ~URB_ALIGNED_TEMP_BUFFER;
+}
+
+static int alloc_temp_buffer(struct urb *urb, gfp_t mem_flags)
+{
+ enum dma_data_direction dir;
+ struct temp_buffer *temp, *kmalloc_ptr;
+ size_t kmalloc_size;
+
+ if (urb->num_sgs || urb->sg ||
+ urb->transfer_buffer_length == 0 ||
+ !((uintptr_t)urb->transfer_buffer & (TEGRA_USB_DMA_ALIGN - 1)))
+ return 0;
+
+ dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+
+ /* Allocate a buffer with enough padding for alignment */
+ kmalloc_size = urb->transfer_buffer_length +
+ sizeof(struct temp_buffer) + TEGRA_USB_DMA_ALIGN - 1;
+
+ kmalloc_ptr = kmalloc(kmalloc_size, mem_flags);
+ if (!kmalloc_ptr)
+ return -ENOMEM;
+
+ /* Position our struct temp_buffer such that data is aligned */
+ temp = PTR_ALIGN(kmalloc_ptr + 1, TEGRA_USB_DMA_ALIGN) - 1;
+
+ temp->kmalloc_ptr = kmalloc_ptr;
+ temp->old_xfer_buffer = urb->transfer_buffer;
+ if (dir == DMA_TO_DEVICE)
+ memcpy(temp->data, urb->transfer_buffer,
+ urb->transfer_buffer_length);
+ urb->transfer_buffer = temp->data;
+
+ urb->transfer_flags |= URB_ALIGNED_TEMP_BUFFER;
+
+ return 0;
+}
+
+static int tegra_ehci_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
+ gfp_t mem_flags)
+{
+ int ret;
+
+ ret = alloc_temp_buffer(urb, mem_flags);
+ if (ret)
+ return ret;
+
+ ret = usb_hcd_map_urb_for_dma(hcd, urb, mem_flags);
+ if (ret)
+ free_temp_buffer(urb);
+
+ return ret;
+}
+
+static void tegra_ehci_unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb)
+{
+ usb_hcd_unmap_urb_for_dma(hcd, urb);
+ free_temp_buffer(urb);
+}
+
+static const struct hc_driver tegra_ehci_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "Tegra EHCI Host Controller",
+ .hcd_priv_size = sizeof(struct ehci_hcd),
+
+ .flags = HCD_USB2 | HCD_MEMORY,
+
+ .reset = tegra_ehci_setup,
+ .irq = ehci_irq,
+
+ .start = ehci_run,
+ .stop = ehci_stop,
+ .shutdown = tegra_ehci_shutdown,
+ .urb_enqueue = ehci_urb_enqueue,
+ .urb_dequeue = ehci_urb_dequeue,
+ .map_urb_for_dma = tegra_ehci_map_urb_for_dma,
+ .unmap_urb_for_dma = tegra_ehci_unmap_urb_for_dma,
+ .endpoint_disable = ehci_endpoint_disable,
+ .endpoint_reset = ehci_endpoint_reset,
+ .get_frame_number = ehci_get_frame,
+ .hub_status_data = ehci_hub_status_data,
+ .hub_control = tegra_ehci_hub_control,
+ .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
+#ifdef CONFIG_PM
+ .bus_suspend = tegra_ehci_bus_suspend,
+ .bus_resume = tegra_ehci_bus_resume,
+#endif
+ .relinquish_port = ehci_relinquish_port,
+ .port_handed_over = ehci_port_handed_over,
+};
+
+static int tegra_ehci_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct usb_hcd *hcd;
+ struct tegra_ehci_hcd *tegra;
+ struct tegra_ehci_platform_data *pdata;
+ int err = 0;
+ int irq;
+ int instance = pdev->id;
+
+ pdata = pdev->dev.platform_data;
+ if (!pdata) {
+ dev_err(&pdev->dev, "Platform data missing\n");
+ return -EINVAL;
+ }
+
+ tegra = kzalloc(sizeof(struct tegra_ehci_hcd), GFP_KERNEL);
+ if (!tegra)
+ return -ENOMEM;
+
+ hcd = usb_create_hcd(&tegra_ehci_hc_driver, &pdev->dev,
+ dev_name(&pdev->dev));
+ if (!hcd) {
+ dev_err(&pdev->dev, "Unable to create HCD\n");
+ err = -ENOMEM;
+ goto fail_hcd;
+ }
+
+ platform_set_drvdata(pdev, tegra);
+
+ tegra->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(tegra->clk)) {
+ dev_err(&pdev->dev, "Can't get ehci clock\n");
+ err = PTR_ERR(tegra->clk);
+ goto fail_clk;
+ }
+
+ err = clk_enable(tegra->clk);
+ if (err)
+ goto fail_clken;
+
+ tegra->emc_clk = clk_get(&pdev->dev, "emc");
+ if (IS_ERR(tegra->emc_clk)) {
+ dev_err(&pdev->dev, "Can't get emc clock\n");
+ err = PTR_ERR(tegra->emc_clk);
+ goto fail_emc_clk;
+ }
+
+ clk_enable(tegra->emc_clk);
+ clk_set_rate(tegra->emc_clk, 400000000);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "Failed to get I/O memory\n");
+ err = -ENXIO;
+ goto fail_io;
+ }
+ hcd->rsrc_start = res->start;
+ hcd->rsrc_len = resource_size(res);
+ hcd->regs = ioremap(res->start, resource_size(res));
+ if (!hcd->regs) {
+ dev_err(&pdev->dev, "Failed to remap I/O memory\n");
+ err = -ENOMEM;
+ goto fail_io;
+ }
+
+ tegra->phy = tegra_usb_phy_open(instance, hcd->regs, pdata->phy_config,
+ TEGRA_USB_PHY_MODE_HOST);
+ if (IS_ERR(tegra->phy)) {
+ dev_err(&pdev->dev, "Failed to open USB phy\n");
+ err = -ENXIO;
+ goto fail_phy;
+ }
+
+ err = tegra_usb_phy_power_on(tegra->phy);
+ if (err) {
+ dev_err(&pdev->dev, "Failed to power on the phy\n");
+ goto fail;
+ }
+
+ tegra->host_resumed = 1;
+ tegra->power_down_on_bus_suspend = pdata->power_down_on_bus_suspend;
+ tegra->ehci = hcd_to_ehci(hcd);
+
+ irq = platform_get_irq(pdev, 0);
+ if (!irq) {
+ dev_err(&pdev->dev, "Failed to get IRQ\n");
+ err = -ENODEV;
+ goto fail;
+ }
+ set_irq_flags(irq, IRQF_VALID);
+
+#ifdef CONFIG_USB_OTG_UTILS
+ if (pdata->operating_mode == TEGRA_USB_OTG) {
+ tegra->transceiver = otg_get_transceiver();
+ if (tegra->transceiver)
+ otg_set_host(tegra->transceiver, &hcd->self);
+ }
+#endif
+
+ err = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED);
+ if (err) {
+ dev_err(&pdev->dev, "Failed to add USB HCD\n");
+ goto fail;
+ }
+
+ return err;
+
+fail:
+#ifdef CONFIG_USB_OTG_UTILS
+ if (tegra->transceiver) {
+ otg_set_host(tegra->transceiver, NULL);
+ otg_put_transceiver(tegra->transceiver);
+ }
+#endif
+ tegra_usb_phy_close(tegra->phy);
+fail_phy:
+ iounmap(hcd->regs);
+fail_io:
+ clk_disable(tegra->emc_clk);
+ clk_put(tegra->emc_clk);
+fail_emc_clk:
+ clk_disable(tegra->clk);
+fail_clken:
+ clk_put(tegra->clk);
+fail_clk:
+ usb_put_hcd(hcd);
+fail_hcd:
+ kfree(tegra);
+ return err;
+}
+
+#ifdef CONFIG_PM
+static int tegra_ehci_resume(struct platform_device *pdev)
+{
+ struct tegra_ehci_hcd *tegra = platform_get_drvdata(pdev);
+ struct usb_hcd *hcd = ehci_to_hcd(tegra->ehci);
+
+ if (tegra->bus_suspended)
+ return 0;
+
+ return tegra_usb_resume(hcd);
+}
+
+static int tegra_ehci_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct tegra_ehci_hcd *tegra = platform_get_drvdata(pdev);
+ struct usb_hcd *hcd = ehci_to_hcd(tegra->ehci);
+
+ if (tegra->bus_suspended)
+ return 0;
+
+ if (time_before(jiffies, tegra->ehci->next_statechange))
+ msleep(10);
+
+ return tegra_usb_suspend(hcd);
+}
+#endif
+
+static int tegra_ehci_remove(struct platform_device *pdev)
+{
+ struct tegra_ehci_hcd *tegra = platform_get_drvdata(pdev);
+ struct usb_hcd *hcd = ehci_to_hcd(tegra->ehci);
+
+ if (tegra == NULL || hcd == NULL)
+ return -EINVAL;
+
+#ifdef CONFIG_USB_OTG_UTILS
+ if (tegra->transceiver) {
+ otg_set_host(tegra->transceiver, NULL);
+ otg_put_transceiver(tegra->transceiver);
+ }
+#endif
+
+ usb_remove_hcd(hcd);
+ usb_put_hcd(hcd);
+
+ tegra_usb_phy_close(tegra->phy);
+ iounmap(hcd->regs);
+
+ clk_disable(tegra->clk);
+ clk_put(tegra->clk);
+
+ clk_disable(tegra->emc_clk);
+ clk_put(tegra->emc_clk);
+
+ kfree(tegra);
+ return 0;
+}
+
+static void tegra_ehci_hcd_shutdown(struct platform_device *pdev)
+{
+ struct tegra_ehci_hcd *tegra = platform_get_drvdata(pdev);
+ struct usb_hcd *hcd = ehci_to_hcd(tegra->ehci);
+
+ if (hcd->driver->shutdown)
+ hcd->driver->shutdown(hcd);
+}
+
+static struct platform_driver tegra_ehci_driver = {
+ .probe = tegra_ehci_probe,
+ .remove = tegra_ehci_remove,
+#ifdef CONFIG_PM
+ .suspend = tegra_ehci_suspend,
+ .resume = tegra_ehci_resume,
+#endif
+ .shutdown = tegra_ehci_hcd_shutdown,
+ .driver = {
+ .name = "tegra-ehci",
+ }
+};
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index 799ac16a54b4..f86d3fa20214 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -131,7 +131,7 @@ struct ehci_hcd { /* one per controller */
unsigned has_amcc_usb23:1;
unsigned need_io_watchdog:1;
unsigned broken_periodic:1;
- unsigned amd_l1_fix:1;
+ unsigned amd_pll_fix:1;
unsigned fs_i_thresh:1; /* Intel iso scheduling */
unsigned use_dummy_qh:1; /* AMD Frame List table quirk*/
diff --git a/drivers/usb/host/imx21-hcd.c b/drivers/usb/host/imx21-hcd.c
index f90d003f2302..2562e92e3178 100644
--- a/drivers/usb/host/imx21-hcd.c
+++ b/drivers/usb/host/imx21-hcd.c
@@ -927,7 +927,8 @@ static void schedule_nonisoc_etd(struct imx21 *imx21, struct urb *urb)
if (state == US_CTRL_SETUP) {
dir = TD_DIR_SETUP;
if (unsuitable_for_dma(urb->setup_dma))
- unmap_urb_setup_for_dma(imx21->hcd, urb);
+ usb_hcd_unmap_urb_setup_for_dma(imx21->hcd,
+ urb);
etd->dma_handle = urb->setup_dma;
etd->cpu_buffer = urb->setup_packet;
bufround = 0;
@@ -943,7 +944,7 @@ static void schedule_nonisoc_etd(struct imx21 *imx21, struct urb *urb)
dir = usb_pipeout(pipe) ? TD_DIR_OUT : TD_DIR_IN;
bufround = (dir == TD_DIR_IN) ? 1 : 0;
if (unsuitable_for_dma(urb->transfer_dma))
- unmap_urb_for_dma(imx21->hcd, urb);
+ usb_hcd_unmap_urb_for_dma(imx21->hcd, urb);
etd->dma_handle = urb->transfer_dma;
etd->cpu_buffer = urb->transfer_buffer;
@@ -1471,8 +1472,8 @@ static int get_hub_descriptor(struct usb_hcd *hcd,
0x0010 | /* No over current protection */
0);
- desc->bitmap[0] = 1 << 1;
- desc->bitmap[1] = ~0;
+ desc->u.hs.DeviceRemovable[0] = 1 << 1;
+ desc->u.hs.DeviceRemovable[1] = ~0;
return 0;
}
diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c
index 0da7fc05f453..c0e22f26da19 100644
--- a/drivers/usb/host/isp116x-hcd.c
+++ b/drivers/usb/host/isp116x-hcd.c
@@ -951,9 +951,9 @@ static void isp116x_hub_descriptor(struct isp116x *isp116x,
/* Power switching, device type, overcurrent. */
desc->wHubCharacteristics = cpu_to_le16((u16) ((reg >> 8) & 0x1f));
desc->bPwrOn2PwrGood = (u8) ((reg >> 24) & 0xff);
- /* two bitmaps: ports removable, and legacy PortPwrCtrlMask */
- desc->bitmap[0] = 0;
- desc->bitmap[1] = ~0;
+ /* ports removable, and legacy PortPwrCtrlMask */
+ desc->u.hs.DeviceRemovable[0] = 0;
+ desc->u.hs.DeviceRemovable[1] = ~0;
}
/* Perform reset of a given port.
diff --git a/drivers/usb/host/isp1362-hcd.c b/drivers/usb/host/isp1362-hcd.c
index 43a39eb56cc6..662cd002adfc 100644
--- a/drivers/usb/host/isp1362-hcd.c
+++ b/drivers/usb/host/isp1362-hcd.c
@@ -226,7 +226,6 @@ static int claim_ptd_buffers(struct isp1362_ep_queue *epq,
static inline void release_ptd_buffers(struct isp1362_ep_queue *epq, struct isp1362_ep *ep)
{
- int index = ep->ptd_index;
int last = ep->ptd_index + ep->num_ptds;
if (last > epq->buf_count)
@@ -236,10 +235,8 @@ static inline void release_ptd_buffers(struct isp1362_ep_queue *epq, struct isp1
epq->buf_map, epq->skip_map);
BUG_ON(last > epq->buf_count);
- for (; index < last; index++) {
- __clear_bit(index, &epq->buf_map);
- __set_bit(index, &epq->skip_map);
- }
+ bitmap_clear(&epq->buf_map, ep->ptd_index, ep->num_ptds);
+ bitmap_set(&epq->skip_map, ep->ptd_index, ep->num_ptds);
epq->buf_avail += ep->num_ptds;
epq->ptd_count--;
@@ -1555,9 +1552,9 @@ static void isp1362_hub_descriptor(struct isp1362_hcd *isp1362_hcd,
desc->wHubCharacteristics = cpu_to_le16((reg >> 8) & 0x1f);
DBG(0, "%s: hubcharacteristics = %02x\n", __func__, cpu_to_le16((reg >> 8) & 0x1f));
desc->bPwrOn2PwrGood = (reg >> 24) & 0xff;
- /* two bitmaps: ports removable, and legacy PortPwrCtrlMask */
- desc->bitmap[0] = desc->bNbrPorts == 1 ? 1 << 1 : 3 << 1;
- desc->bitmap[1] = ~0;
+ /* ports removable, and legacy PortPwrCtrlMask */
+ desc->u.hs.DeviceRemovable[0] = desc->bNbrPorts == 1 ? 1 << 1 : 3 << 1;
+ desc->u.hs.DeviceRemovable[1] = ~0;
DBG(3, "%s: exit\n", __func__);
}
diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c
index bdba8c5d844a..f50e84ac570a 100644
--- a/drivers/usb/host/isp1760-hcd.c
+++ b/drivers/usb/host/isp1760-hcd.c
@@ -33,6 +33,7 @@ struct isp1760_hcd {
struct inter_packet_info atl_ints[32];
struct inter_packet_info int_ints[32];
struct memory_chunk memory_pool[BLOCKS];
+ u32 atl_queued;
/* periodic schedule support */
#define DEFAULT_I_TDPS 1024
@@ -47,10 +48,6 @@ static inline struct isp1760_hcd *hcd_to_priv(struct usb_hcd *hcd)
{
return (struct isp1760_hcd *) (hcd->hcd_priv);
}
-static inline struct usb_hcd *priv_to_hcd(struct isp1760_hcd *priv)
-{
- return container_of((void *) priv, struct usb_hcd, hcd_priv);
-}
/* Section 2.2 Host Controller Capability Registers */
#define HC_LENGTH(p) (((p)>>00)&0x00ff) /* bits 7:0 */
@@ -80,11 +77,10 @@ static inline struct usb_hcd *priv_to_hcd(struct isp1760_hcd *priv)
#define PORT_RWC_BITS (PORT_CSC)
struct isp1760_qtd {
- struct isp1760_qtd *hw_next;
u8 packet_type;
- u8 toggle;
-
void *data_buffer;
+ u32 payload_addr;
+
/* the rest is HCD-private */
struct list_head qtd_list;
struct urb *urb;
@@ -92,205 +88,267 @@ struct isp1760_qtd {
/* isp special*/
u32 status;
-#define URB_COMPLETE_NOTIFY (1 << 0)
#define URB_ENQUEUED (1 << 1)
-#define URB_TYPE_ATL (1 << 2)
-#define URB_TYPE_INT (1 << 3)
};
struct isp1760_qh {
/* first part defined by EHCI spec */
struct list_head qtd_list;
- struct isp1760_hcd *priv;
-
- /* periodic schedule info */
- unsigned short period; /* polling interval */
- struct usb_device *dev;
u32 toggle;
u32 ping;
};
-#define ehci_port_speed(priv, portsc) USB_PORT_STAT_HIGH_SPEED
-
-static unsigned int isp1760_readl(__u32 __iomem *regs)
+/*
+ * Access functions for isp176x registers (addresses 0..0x03FF).
+ */
+static u32 reg_read32(void __iomem *base, u32 reg)
{
- return readl(regs);
+ return readl(base + reg);
}
-static void isp1760_writel(const unsigned int val, __u32 __iomem *regs)
+static void reg_write32(void __iomem *base, u32 reg, u32 val)
{
- writel(val, regs);
+ writel(val, base + reg);
}
/*
- * The next two copy via MMIO data to/from the device. memcpy_{to|from}io()
+ * Access functions for isp176x memory (offset >= 0x0400).
+ *
+ * bank_reads8() reads memory locations prefetched by an earlier write to
+ * HC_MEMORY_REG (see isp176x datasheet). Unless you want to do fancy multi-
+ * bank optimizations, you should use the more generic mem_reads8() below.
+ *
+ * For access to ptd memory, use the specialized ptd_read() and ptd_write()
+ * below.
+ *
+ * These functions copy via MMIO data to/from the device. memcpy_{to|from}io()
* doesn't quite work because some people have to enforce 32-bit access
*/
-static void priv_read_copy(struct isp1760_hcd *priv, u32 *src,
- __u32 __iomem *dst, u32 len)
+static void bank_reads8(void __iomem *src_base, u32 src_offset, u32 bank_addr,
+ __u32 *dst, u32 bytes)
{
+ __u32 __iomem *src;
u32 val;
- u8 *buff8;
+ __u8 *src_byteptr;
+ __u8 *dst_byteptr;
- if (!src) {
- printk(KERN_ERR "ERROR: buffer: %p len: %d\n", src, len);
- return;
- }
+ src = src_base + (bank_addr | src_offset);
- while (len >= 4) {
- *src = __raw_readl(dst);
- len -= 4;
- src++;
- dst++;
+ if (src_offset < PAYLOAD_OFFSET) {
+ while (bytes >= 4) {
+ *dst = le32_to_cpu(__raw_readl(src));
+ bytes -= 4;
+ src++;
+ dst++;
+ }
+ } else {
+ while (bytes >= 4) {
+ *dst = __raw_readl(src);
+ bytes -= 4;
+ src++;
+ dst++;
+ }
}
- if (!len)
+ if (!bytes)
return;
/* in case we have 3, 2 or 1 by left. The dst buffer may not be fully
* allocated.
*/
- val = isp1760_readl(dst);
-
- buff8 = (u8 *)src;
- while (len) {
-
- *buff8 = val;
- val >>= 8;
- len--;
- buff8++;
+ if (src_offset < PAYLOAD_OFFSET)
+ val = le32_to_cpu(__raw_readl(src));
+ else
+ val = __raw_readl(src);
+
+ dst_byteptr = (void *) dst;
+ src_byteptr = (void *) &val;
+ while (bytes > 0) {
+ *dst_byteptr = *src_byteptr;
+ dst_byteptr++;
+ src_byteptr++;
+ bytes--;
}
}
-static void priv_write_copy(const struct isp1760_hcd *priv, const u32 *src,
- __u32 __iomem *dst, u32 len)
+static void mem_reads8(void __iomem *src_base, u32 src_offset, void *dst,
+ u32 bytes)
{
- while (len >= 4) {
- __raw_writel(*src, dst);
- len -= 4;
- src++;
- dst++;
+ reg_write32(src_base, HC_MEMORY_REG, src_offset + ISP_BANK(0));
+ ndelay(90);
+ bank_reads8(src_base, src_offset, ISP_BANK(0), dst, bytes);
+}
+
+static void mem_writes8(void __iomem *dst_base, u32 dst_offset,
+ __u32 const *src, u32 bytes)
+{
+ __u32 __iomem *dst;
+
+ dst = dst_base + dst_offset;
+
+ if (dst_offset < PAYLOAD_OFFSET) {
+ while (bytes >= 4) {
+ __raw_writel(cpu_to_le32(*src), dst);
+ bytes -= 4;
+ src++;
+ dst++;
+ }
+ } else {
+ while (bytes >= 4) {
+ __raw_writel(*src, dst);
+ bytes -= 4;
+ src++;
+ dst++;
+ }
}
- if (!len)
+ if (!bytes)
return;
- /* in case we have 3, 2 or 1 by left. The buffer is allocated and the
- * extra bytes should not be read by the HW
+ /* in case we have 3, 2 or 1 bytes left. The buffer is allocated and the
+ * extra bytes should not be read by the HW.
*/
- __raw_writel(*src, dst);
+ if (dst_offset < PAYLOAD_OFFSET)
+ __raw_writel(cpu_to_le32(*src), dst);
+ else
+ __raw_writel(*src, dst);
+}
+
+/*
+ * Read and write ptds. 'ptd_offset' should be one of ISO_PTD_OFFSET,
+ * INT_PTD_OFFSET, and ATL_PTD_OFFSET. 'slot' should be less than 32.
+ */
+static void ptd_read(void __iomem *base, u32 ptd_offset, u32 slot,
+ struct ptd *ptd)
+{
+ reg_write32(base, HC_MEMORY_REG,
+ ISP_BANK(0) + ptd_offset + slot*sizeof(*ptd));
+ ndelay(90);
+ bank_reads8(base, ptd_offset + slot*sizeof(*ptd), ISP_BANK(0),
+ (void *) ptd, sizeof(*ptd));
+}
+
+static void ptd_write(void __iomem *base, u32 ptd_offset, u32 slot,
+ struct ptd *ptd)
+{
+ mem_writes8(base, ptd_offset + slot*sizeof(*ptd) + sizeof(ptd->dw0),
+ &ptd->dw1, 7*sizeof(ptd->dw1));
+ /* Make sure dw0 gets written last (after other dw's and after payload)
+ since it contains the enable bit */
+ wmb();
+ mem_writes8(base, ptd_offset + slot*sizeof(*ptd), &ptd->dw0,
+ sizeof(ptd->dw0));
}
+
/* memory management of the 60kb on the chip from 0x1000 to 0xffff */
static void init_memory(struct isp1760_hcd *priv)
{
- int i;
- u32 payload;
+ int i, curr;
+ u32 payload_addr;
- payload = 0x1000;
+ payload_addr = PAYLOAD_OFFSET;
for (i = 0; i < BLOCK_1_NUM; i++) {
- priv->memory_pool[i].start = payload;
+ priv->memory_pool[i].start = payload_addr;
priv->memory_pool[i].size = BLOCK_1_SIZE;
priv->memory_pool[i].free = 1;
- payload += priv->memory_pool[i].size;
+ payload_addr += priv->memory_pool[i].size;
}
-
- for (i = BLOCK_1_NUM; i < BLOCK_1_NUM + BLOCK_2_NUM; i++) {
- priv->memory_pool[i].start = payload;
- priv->memory_pool[i].size = BLOCK_2_SIZE;
- priv->memory_pool[i].free = 1;
- payload += priv->memory_pool[i].size;
+ curr = i;
+ for (i = 0; i < BLOCK_2_NUM; i++) {
+ priv->memory_pool[curr + i].start = payload_addr;
+ priv->memory_pool[curr + i].size = BLOCK_2_SIZE;
+ priv->memory_pool[curr + i].free = 1;
+ payload_addr += priv->memory_pool[curr + i].size;
}
-
- for (i = BLOCK_1_NUM + BLOCK_2_NUM; i < BLOCKS; i++) {
- priv->memory_pool[i].start = payload;
- priv->memory_pool[i].size = BLOCK_3_SIZE;
- priv->memory_pool[i].free = 1;
- payload += priv->memory_pool[i].size;
+ curr = i;
+ for (i = 0; i < BLOCK_3_NUM; i++) {
+ priv->memory_pool[curr + i].start = payload_addr;
+ priv->memory_pool[curr + i].size = BLOCK_3_SIZE;
+ priv->memory_pool[curr + i].free = 1;
+ payload_addr += priv->memory_pool[curr + i].size;
}
- BUG_ON(payload - priv->memory_pool[i - 1].size > PAYLOAD_SIZE);
+ BUG_ON(payload_addr - priv->memory_pool[0].start > PAYLOAD_AREA_SIZE);
}
-static u32 alloc_mem(struct isp1760_hcd *priv, u32 size)
+static void alloc_mem(struct usb_hcd *hcd, struct isp1760_qtd *qtd)
{
+ struct isp1760_hcd *priv = hcd_to_priv(hcd);
int i;
- if (!size)
- return ISP1760_NULL_POINTER;
+ BUG_ON(qtd->payload_addr);
+
+ if (!qtd->length)
+ return;
for (i = 0; i < BLOCKS; i++) {
- if (priv->memory_pool[i].size >= size &&
+ if (priv->memory_pool[i].size >= qtd->length &&
priv->memory_pool[i].free) {
-
priv->memory_pool[i].free = 0;
- return priv->memory_pool[i].start;
+ qtd->payload_addr = priv->memory_pool[i].start;
+ return;
}
}
- printk(KERN_ERR "ISP1760 MEM: can not allocate %d bytes of memory\n",
- size);
- printk(KERN_ERR "Current memory map:\n");
+ dev_err(hcd->self.controller,
+ "%s: Can not allocate %lu bytes of memory\n"
+ "Current memory map:\n",
+ __func__, qtd->length);
for (i = 0; i < BLOCKS; i++) {
- printk(KERN_ERR "Pool %2d size %4d status: %d\n",
+ dev_err(hcd->self.controller, "Pool %2d size %4d status: %d\n",
i, priv->memory_pool[i].size,
priv->memory_pool[i].free);
}
/* XXX maybe -ENOMEM could be possible */
BUG();
- return 0;
+ return;
}
-static void free_mem(struct isp1760_hcd *priv, u32 mem)
+static void free_mem(struct usb_hcd *hcd, struct isp1760_qtd *qtd)
{
+ struct isp1760_hcd *priv = hcd_to_priv(hcd);
int i;
- if (mem == ISP1760_NULL_POINTER)
+ if (!qtd->payload_addr)
return;
for (i = 0; i < BLOCKS; i++) {
- if (priv->memory_pool[i].start == mem) {
-
+ if (priv->memory_pool[i].start == qtd->payload_addr) {
BUG_ON(priv->memory_pool[i].free);
-
priv->memory_pool[i].free = 1;
- return ;
+ qtd->payload_addr = 0;
+ return;
}
}
- printk(KERN_ERR "Trying to free not-here-allocated memory :%08x\n",
- mem);
+ dev_err(hcd->self.controller, "%s: Invalid pointer: %08x\n",
+ __func__, qtd->payload_addr);
BUG();
}
static void isp1760_init_regs(struct usb_hcd *hcd)
{
- isp1760_writel(0, hcd->regs + HC_BUFFER_STATUS_REG);
- isp1760_writel(NO_TRANSFER_ACTIVE, hcd->regs +
- HC_ATL_PTD_SKIPMAP_REG);
- isp1760_writel(NO_TRANSFER_ACTIVE, hcd->regs +
- HC_INT_PTD_SKIPMAP_REG);
- isp1760_writel(NO_TRANSFER_ACTIVE, hcd->regs +
- HC_ISO_PTD_SKIPMAP_REG);
-
- isp1760_writel(~NO_TRANSFER_ACTIVE, hcd->regs +
- HC_ATL_PTD_DONEMAP_REG);
- isp1760_writel(~NO_TRANSFER_ACTIVE, hcd->regs +
- HC_INT_PTD_DONEMAP_REG);
- isp1760_writel(~NO_TRANSFER_ACTIVE, hcd->regs +
- HC_ISO_PTD_DONEMAP_REG);
+ reg_write32(hcd->regs, HC_BUFFER_STATUS_REG, 0);
+ reg_write32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG, NO_TRANSFER_ACTIVE);
+ reg_write32(hcd->regs, HC_INT_PTD_SKIPMAP_REG, NO_TRANSFER_ACTIVE);
+ reg_write32(hcd->regs, HC_ISO_PTD_SKIPMAP_REG, NO_TRANSFER_ACTIVE);
+
+ reg_write32(hcd->regs, HC_ATL_PTD_DONEMAP_REG, ~NO_TRANSFER_ACTIVE);
+ reg_write32(hcd->regs, HC_INT_PTD_DONEMAP_REG, ~NO_TRANSFER_ACTIVE);
+ reg_write32(hcd->regs, HC_ISO_PTD_DONEMAP_REG, ~NO_TRANSFER_ACTIVE);
}
-static int handshake(struct isp1760_hcd *priv, void __iomem *ptr,
+static int handshake(struct usb_hcd *hcd, u32 reg,
u32 mask, u32 done, int usec)
{
u32 result;
do {
- result = isp1760_readl(ptr);
+ result = reg_read32(hcd->regs, reg);
if (result == ~0)
return -ENODEV;
result &= mask;
@@ -303,17 +361,18 @@ static int handshake(struct isp1760_hcd *priv, void __iomem *ptr,
}
/* reset a non-running (STS_HALT == 1) controller */
-static int ehci_reset(struct isp1760_hcd *priv)
+static int ehci_reset(struct usb_hcd *hcd)
{
int retval;
- struct usb_hcd *hcd = priv_to_hcd(priv);
- u32 command = isp1760_readl(hcd->regs + HC_USBCMD);
+ struct isp1760_hcd *priv = hcd_to_priv(hcd);
+
+ u32 command = reg_read32(hcd->regs, HC_USBCMD);
command |= CMD_RESET;
- isp1760_writel(command, hcd->regs + HC_USBCMD);
+ reg_write32(hcd->regs, HC_USBCMD, command);
hcd->state = HC_STATE_HALT;
priv->next_statechange = jiffies;
- retval = handshake(priv, hcd->regs + HC_USBCMD,
+ retval = handshake(hcd, HC_USBCMD,
CMD_RESET, 0, 250 * 1000);
return retval;
}
@@ -324,8 +383,7 @@ static void qh_destroy(struct isp1760_qh *qh)
kmem_cache_free(qh_cachep, qh);
}
-static struct isp1760_qh *isp1760_qh_alloc(struct isp1760_hcd *priv,
- gfp_t flags)
+static struct isp1760_qh *isp1760_qh_alloc(gfp_t flags)
{
struct isp1760_qh *qh;
@@ -334,7 +392,6 @@ static struct isp1760_qh *isp1760_qh_alloc(struct isp1760_hcd *priv,
return qh;
INIT_LIST_HEAD(&qh->qtd_list);
- qh->priv = priv;
return qh;
}
@@ -361,7 +418,7 @@ static int priv_init(struct usb_hcd *hcd)
priv->periodic_size = DEFAULT_I_TDPS;
/* controllers may cache some of the periodic schedule ... */
- hcc_params = isp1760_readl(hcd->regs + HC_HCCPARAMS);
+ hcc_params = reg_read32(hcd->regs, HC_HCCPARAMS);
/* full frame cache */
if (HCC_ISOC_CACHE(hcc_params))
priv->i_thresh = 8;
@@ -398,15 +455,15 @@ static int isp1760_hc_setup(struct usb_hcd *hcd)
* Write it twice to ensure correct upper bits if switching
* to 16-bit mode.
*/
- isp1760_writel(hwmode, hcd->regs + HC_HW_MODE_CTRL);
- isp1760_writel(hwmode, hcd->regs + HC_HW_MODE_CTRL);
+ reg_write32(hcd->regs, HC_HW_MODE_CTRL, hwmode);
+ reg_write32(hcd->regs, HC_HW_MODE_CTRL, hwmode);
- isp1760_writel(0xdeadbabe, hcd->regs + HC_SCRATCH_REG);
+ reg_write32(hcd->regs, HC_SCRATCH_REG, 0xdeadbabe);
/* Change bus pattern */
- scratch = isp1760_readl(hcd->regs + HC_CHIP_ID_REG);
- scratch = isp1760_readl(hcd->regs + HC_SCRATCH_REG);
+ scratch = reg_read32(hcd->regs, HC_CHIP_ID_REG);
+ scratch = reg_read32(hcd->regs, HC_SCRATCH_REG);
if (scratch != 0xdeadbabe) {
- printk(KERN_ERR "ISP1760: Scratch test failed.\n");
+ dev_err(hcd->self.controller, "Scratch test failed.\n");
return -ENODEV;
}
@@ -414,30 +471,30 @@ static int isp1760_hc_setup(struct usb_hcd *hcd)
isp1760_init_regs(hcd);
/* reset */
- isp1760_writel(SW_RESET_RESET_ALL, hcd->regs + HC_RESET_REG);
+ reg_write32(hcd->regs, HC_RESET_REG, SW_RESET_RESET_ALL);
mdelay(100);
- isp1760_writel(SW_RESET_RESET_HC, hcd->regs + HC_RESET_REG);
+ reg_write32(hcd->regs, HC_RESET_REG, SW_RESET_RESET_HC);
mdelay(100);
- result = ehci_reset(priv);
+ result = ehci_reset(hcd);
if (result)
return result;
/* Step 11 passed */
- isp1760_info(priv, "bus width: %d, oc: %s\n",
+ dev_info(hcd->self.controller, "bus width: %d, oc: %s\n",
(priv->devflags & ISP1760_FLAG_BUS_WIDTH_16) ?
16 : 32, (priv->devflags & ISP1760_FLAG_ANALOG_OC) ?
"analog" : "digital");
/* ATL reset */
- isp1760_writel(hwmode | ALL_ATX_RESET, hcd->regs + HC_HW_MODE_CTRL);
+ reg_write32(hcd->regs, HC_HW_MODE_CTRL, hwmode | ALL_ATX_RESET);
mdelay(10);
- isp1760_writel(hwmode, hcd->regs + HC_HW_MODE_CTRL);
+ reg_write32(hcd->regs, HC_HW_MODE_CTRL, hwmode);
- isp1760_writel(INTERRUPT_ENABLE_MASK, hcd->regs + HC_INTERRUPT_REG);
- isp1760_writel(INTERRUPT_ENABLE_MASK, hcd->regs + HC_INTERRUPT_ENABLE);
+ reg_write32(hcd->regs, HC_INTERRUPT_REG, INTERRUPT_ENABLE_MASK);
+ reg_write32(hcd->regs, HC_INTERRUPT_ENABLE, INTERRUPT_ENABLE_MASK);
/*
* PORT 1 Control register of the ISP1760 is the OTG control
@@ -445,11 +502,10 @@ static int isp1760_hc_setup(struct usb_hcd *hcd)
* support in this driver, we use port 1 as a "normal" USB host port on
* both chips.
*/
- isp1760_writel(PORT1_POWER | PORT1_INIT2,
- hcd->regs + HC_PORT1_CTRL);
+ reg_write32(hcd->regs, HC_PORT1_CTRL, PORT1_POWER | PORT1_INIT2);
mdelay(10);
- priv->hcs_params = isp1760_readl(hcd->regs + HC_HCSPARAMS);
+ priv->hcs_params = reg_read32(hcd->regs, HC_HCSPARAMS);
return priv_init(hcd);
}
@@ -457,25 +513,24 @@ static int isp1760_hc_setup(struct usb_hcd *hcd)
static void isp1760_init_maps(struct usb_hcd *hcd)
{
/*set last maps, for iso its only 1, else 32 tds bitmap*/
- isp1760_writel(0x80000000, hcd->regs + HC_ATL_PTD_LASTPTD_REG);
- isp1760_writel(0x80000000, hcd->regs + HC_INT_PTD_LASTPTD_REG);
- isp1760_writel(0x00000001, hcd->regs + HC_ISO_PTD_LASTPTD_REG);
+ reg_write32(hcd->regs, HC_ATL_PTD_LASTPTD_REG, 0x80000000);
+ reg_write32(hcd->regs, HC_INT_PTD_LASTPTD_REG, 0x80000000);
+ reg_write32(hcd->regs, HC_ISO_PTD_LASTPTD_REG, 0x00000001);
}
static void isp1760_enable_interrupts(struct usb_hcd *hcd)
{
- isp1760_writel(0, hcd->regs + HC_ATL_IRQ_MASK_AND_REG);
- isp1760_writel(0, hcd->regs + HC_ATL_IRQ_MASK_OR_REG);
- isp1760_writel(0, hcd->regs + HC_INT_IRQ_MASK_AND_REG);
- isp1760_writel(0, hcd->regs + HC_INT_IRQ_MASK_OR_REG);
- isp1760_writel(0, hcd->regs + HC_ISO_IRQ_MASK_AND_REG);
- isp1760_writel(0xffffffff, hcd->regs + HC_ISO_IRQ_MASK_OR_REG);
+ reg_write32(hcd->regs, HC_ATL_IRQ_MASK_AND_REG, 0);
+ reg_write32(hcd->regs, HC_ATL_IRQ_MASK_OR_REG, 0);
+ reg_write32(hcd->regs, HC_INT_IRQ_MASK_AND_REG, 0);
+ reg_write32(hcd->regs, HC_INT_IRQ_MASK_OR_REG, 0);
+ reg_write32(hcd->regs, HC_ISO_IRQ_MASK_AND_REG, 0);
+ reg_write32(hcd->regs, HC_ISO_IRQ_MASK_OR_REG, 0xffffffff);
/* step 23 passed */
}
static int isp1760_run(struct usb_hcd *hcd)
{
- struct isp1760_hcd *priv = hcd_to_priv(hcd);
int retval;
u32 temp;
u32 command;
@@ -485,15 +540,15 @@ static int isp1760_run(struct usb_hcd *hcd)
hcd->state = HC_STATE_RUNNING;
isp1760_enable_interrupts(hcd);
- temp = isp1760_readl(hcd->regs + HC_HW_MODE_CTRL);
- isp1760_writel(temp | HW_GLOBAL_INTR_EN, hcd->regs + HC_HW_MODE_CTRL);
+ temp = reg_read32(hcd->regs, HC_HW_MODE_CTRL);
+ reg_write32(hcd->regs, HC_HW_MODE_CTRL, temp | HW_GLOBAL_INTR_EN);
- command = isp1760_readl(hcd->regs + HC_USBCMD);
+ command = reg_read32(hcd->regs, HC_USBCMD);
command &= ~(CMD_LRESET|CMD_RESET);
command |= CMD_RUN;
- isp1760_writel(command, hcd->regs + HC_USBCMD);
+ reg_write32(hcd->regs, HC_USBCMD, command);
- retval = handshake(priv, hcd->regs + HC_USBCMD, CMD_RUN, CMD_RUN,
+ retval = handshake(hcd, HC_USBCMD, CMD_RUN, CMD_RUN,
250 * 1000);
if (retval)
return retval;
@@ -504,17 +559,16 @@ static int isp1760_run(struct usb_hcd *hcd)
* the semaphore while doing so.
*/
down_write(&ehci_cf_port_reset_rwsem);
- isp1760_writel(FLAG_CF, hcd->regs + HC_CONFIGFLAG);
+ reg_write32(hcd->regs, HC_CONFIGFLAG, FLAG_CF);
- retval = handshake(priv, hcd->regs + HC_CONFIGFLAG, FLAG_CF, FLAG_CF,
- 250 * 1000);
+ retval = handshake(hcd, HC_CONFIGFLAG, FLAG_CF, FLAG_CF, 250 * 1000);
up_write(&ehci_cf_port_reset_rwsem);
if (retval)
return retval;
- chipid = isp1760_readl(hcd->regs + HC_CHIP_ID_REG);
- isp1760_info(priv, "USB ISP %04x HW rev. %d started\n", chipid & 0xffff,
- chipid >> 16);
+ chipid = reg_read32(hcd->regs, HC_CHIP_ID_REG);
+ dev_info(hcd->self.controller, "USB ISP %04x HW rev. %d started\n",
+ chipid & 0xffff, chipid >> 16);
/* PTD Register Init Part 2, Step 28 */
/* enable INTs */
@@ -532,160 +586,156 @@ static u32 base_to_chip(u32 base)
return ((base - 0x400) >> 3);
}
-static void transform_into_atl(struct isp1760_hcd *priv, struct isp1760_qh *qh,
- struct isp1760_qtd *qtd, struct urb *urb,
- u32 payload, struct ptd *ptd)
+static int last_qtd_of_urb(struct isp1760_qtd *qtd, struct isp1760_qh *qh)
+{
+ struct urb *urb;
+
+ if (list_is_last(&qtd->qtd_list, &qh->qtd_list))
+ return 1;
+
+ urb = qtd->urb;
+ qtd = list_entry(qtd->qtd_list.next, typeof(*qtd), qtd_list);
+ return (qtd->urb != urb);
+}
+
+static void transform_into_atl(struct isp1760_qh *qh,
+ struct isp1760_qtd *qtd, struct ptd *ptd)
{
- u32 dw0;
- u32 dw1;
- u32 dw2;
- u32 dw3;
u32 maxpacket;
u32 multi;
u32 pid_code;
u32 rl = RL_COUNTER;
u32 nak = NAK_COUNTER;
+ memset(ptd, 0, sizeof(*ptd));
+
/* according to 3.6.2, max packet len can not be > 0x400 */
- maxpacket = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+ maxpacket = usb_maxpacket(qtd->urb->dev, qtd->urb->pipe,
+ usb_pipeout(qtd->urb->pipe));
multi = 1 + ((maxpacket >> 11) & 0x3);
maxpacket &= 0x7ff;
/* DW0 */
- dw0 = PTD_VALID;
- dw0 |= PTD_LENGTH(qtd->length);
- dw0 |= PTD_MAXPACKET(maxpacket);
- dw0 |= PTD_ENDPOINT(usb_pipeendpoint(urb->pipe));
- dw1 = usb_pipeendpoint(urb->pipe) >> 1;
+ ptd->dw0 = PTD_VALID;
+ ptd->dw0 |= PTD_LENGTH(qtd->length);
+ ptd->dw0 |= PTD_MAXPACKET(maxpacket);
+ ptd->dw0 |= PTD_ENDPOINT(usb_pipeendpoint(qtd->urb->pipe));
/* DW1 */
- dw1 |= PTD_DEVICE_ADDR(usb_pipedevice(urb->pipe));
+ ptd->dw1 = usb_pipeendpoint(qtd->urb->pipe) >> 1;
+ ptd->dw1 |= PTD_DEVICE_ADDR(usb_pipedevice(qtd->urb->pipe));
pid_code = qtd->packet_type;
- dw1 |= PTD_PID_TOKEN(pid_code);
+ ptd->dw1 |= PTD_PID_TOKEN(pid_code);
- if (usb_pipebulk(urb->pipe))
- dw1 |= PTD_TRANS_BULK;
- else if (usb_pipeint(urb->pipe))
- dw1 |= PTD_TRANS_INT;
+ if (usb_pipebulk(qtd->urb->pipe))
+ ptd->dw1 |= PTD_TRANS_BULK;
+ else if (usb_pipeint(qtd->urb->pipe))
+ ptd->dw1 |= PTD_TRANS_INT;
- if (urb->dev->speed != USB_SPEED_HIGH) {
+ if (qtd->urb->dev->speed != USB_SPEED_HIGH) {
/* split transaction */
- dw1 |= PTD_TRANS_SPLIT;
- if (urb->dev->speed == USB_SPEED_LOW)
- dw1 |= PTD_SE_USB_LOSPEED;
+ ptd->dw1 |= PTD_TRANS_SPLIT;
+ if (qtd->urb->dev->speed == USB_SPEED_LOW)
+ ptd->dw1 |= PTD_SE_USB_LOSPEED;
- dw1 |= PTD_PORT_NUM(urb->dev->ttport);
- dw1 |= PTD_HUB_NUM(urb->dev->tt->hub->devnum);
+ ptd->dw1 |= PTD_PORT_NUM(qtd->urb->dev->ttport);
+ ptd->dw1 |= PTD_HUB_NUM(qtd->urb->dev->tt->hub->devnum);
/* SE bit for Split INT transfers */
- if (usb_pipeint(urb->pipe) &&
- (urb->dev->speed == USB_SPEED_LOW))
- dw1 |= 2 << 16;
+ if (usb_pipeint(qtd->urb->pipe) &&
+ (qtd->urb->dev->speed == USB_SPEED_LOW))
+ ptd->dw1 |= 2 << 16;
- dw3 = 0;
+ ptd->dw3 = 0;
rl = 0;
nak = 0;
} else {
- dw0 |= PTD_MULTI(multi);
- if (usb_pipecontrol(urb->pipe) || usb_pipebulk(urb->pipe))
- dw3 = qh->ping;
+ ptd->dw0 |= PTD_MULTI(multi);
+ if (usb_pipecontrol(qtd->urb->pipe) ||
+ usb_pipebulk(qtd->urb->pipe))
+ ptd->dw3 = qh->ping;
else
- dw3 = 0;
+ ptd->dw3 = 0;
}
/* DW2 */
- dw2 = 0;
- dw2 |= PTD_DATA_START_ADDR(base_to_chip(payload));
- dw2 |= PTD_RL_CNT(rl);
- dw3 |= PTD_NAC_CNT(nak);
+ ptd->dw2 = 0;
+ ptd->dw2 |= PTD_DATA_START_ADDR(base_to_chip(qtd->payload_addr));
+ ptd->dw2 |= PTD_RL_CNT(rl);
+ ptd->dw3 |= PTD_NAC_CNT(nak);
/* DW3 */
- if (usb_pipecontrol(urb->pipe))
- dw3 |= PTD_DATA_TOGGLE(qtd->toggle);
- else
- dw3 |= qh->toggle;
-
+ ptd->dw3 |= qh->toggle;
+ if (usb_pipecontrol(qtd->urb->pipe)) {
+ if (qtd->data_buffer == qtd->urb->setup_packet)
+ ptd->dw3 &= ~PTD_DATA_TOGGLE(1);
+ else if (last_qtd_of_urb(qtd, qh))
+ ptd->dw3 |= PTD_DATA_TOGGLE(1);
+ }
- dw3 |= PTD_ACTIVE;
+ ptd->dw3 |= PTD_ACTIVE;
/* Cerr */
- dw3 |= PTD_CERR(ERR_COUNTER);
-
- memset(ptd, 0, sizeof(*ptd));
-
- ptd->dw0 = cpu_to_le32(dw0);
- ptd->dw1 = cpu_to_le32(dw1);
- ptd->dw2 = cpu_to_le32(dw2);
- ptd->dw3 = cpu_to_le32(dw3);
+ ptd->dw3 |= PTD_CERR(ERR_COUNTER);
}
-static void transform_add_int(struct isp1760_hcd *priv, struct isp1760_qh *qh,
- struct isp1760_qtd *qtd, struct urb *urb,
- u32 payload, struct ptd *ptd)
+static void transform_add_int(struct isp1760_qh *qh,
+ struct isp1760_qtd *qtd, struct ptd *ptd)
{
- u32 maxpacket;
- u32 multi;
- u32 numberofusofs;
- u32 i;
- u32 usofmask, usof;
+ u32 usof;
u32 period;
- maxpacket = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
- multi = 1 + ((maxpacket >> 11) & 0x3);
- maxpacket &= 0x7ff;
- /* length of the data per uframe */
- maxpacket = multi * maxpacket;
-
- numberofusofs = urb->transfer_buffer_length / maxpacket;
- if (urb->transfer_buffer_length % maxpacket)
- numberofusofs += 1;
-
- usofmask = 1;
- usof = 0;
- for (i = 0; i < numberofusofs; i++) {
- usof |= usofmask;
- usofmask <<= 1;
- }
-
- if (urb->dev->speed != USB_SPEED_HIGH) {
- /* split */
- ptd->dw5 = cpu_to_le32(0x1c);
+ /*
+ * Most of this is guessing. ISP1761 datasheet is quite unclear, and
+ * the algorithm from the original Philips driver code, which was
+ * pretty much used in this driver before as well, is quite horrendous
+ * and, i believe, incorrect. The code below follows the datasheet and
+ * USB2.0 spec as far as I can tell, and plug/unplug seems to be much
+ * more reliable this way (fingers crossed...).
+ */
- if (qh->period >= 32)
- period = qh->period / 2;
+ if (qtd->urb->dev->speed == USB_SPEED_HIGH) {
+ /* urb->interval is in units of microframes (1/8 ms) */
+ period = qtd->urb->interval >> 3;
+
+ if (qtd->urb->interval > 4)
+ usof = 0x01; /* One bit set =>
+ interval 1 ms * uFrame-match */
+ else if (qtd->urb->interval > 2)
+ usof = 0x22; /* Two bits set => interval 1/2 ms */
+ else if (qtd->urb->interval > 1)
+ usof = 0x55; /* Four bits set => interval 1/4 ms */
else
- period = qh->period;
-
+ usof = 0xff; /* All bits set => interval 1/8 ms */
} else {
+ /* urb->interval is in units of frames (1 ms) */
+ period = qtd->urb->interval;
+ usof = 0x0f; /* Execute Start Split on any of the
+ four first uFrames */
- if (qh->period >= 8)
- period = qh->period/8;
- else
- period = qh->period;
-
- if (period >= 32)
- period = 16;
-
- if (qh->period >= 8) {
- /* millisecond period */
- period = (period << 3);
- } else {
- /* usof based tranmsfers */
- /* minimum 4 usofs */
- usof = 0x11;
- }
+ /*
+ * First 8 bits in dw5 is uSCS and "specifies which uSOF the
+ * complete split needs to be sent. Valid only for IN." Also,
+ * "All bits can be set to one for every transfer." (p 82,
+ * ISP1761 data sheet.) 0x1c is from Philips driver. Where did
+ * that number come from? 0xff seems to work fine...
+ */
+ /* ptd->dw5 = 0x1c; */
+ ptd->dw5 = 0xff; /* Execute Complete Split on any uFrame */
}
- ptd->dw2 |= cpu_to_le32(period);
- ptd->dw4 = cpu_to_le32(usof);
+ period = period >> 1;/* Ensure equal or shorter period than requested */
+ period &= 0xf8; /* Mask off too large values and lowest unused 3 bits */
+
+ ptd->dw2 |= period;
+ ptd->dw4 = usof;
}
-static void transform_into_int(struct isp1760_hcd *priv, struct isp1760_qh *qh,
- struct isp1760_qtd *qtd, struct urb *urb,
- u32 payload, struct ptd *ptd)
+static void transform_into_int(struct isp1760_qh *qh,
+ struct isp1760_qtd *qtd, struct ptd *ptd)
{
- transform_into_atl(priv, qh, qtd, urb, payload, ptd);
- transform_add_int(priv, qh, qtd, urb, payload, ptd);
+ transform_into_atl(qh, qtd, ptd);
+ transform_add_int(qh, qtd, ptd);
}
static int qtd_fill(struct isp1760_qtd *qtd, void *databuffer, size_t len,
@@ -695,10 +745,9 @@ static int qtd_fill(struct isp1760_qtd *qtd, void *databuffer, size_t len,
qtd->data_buffer = databuffer;
qtd->packet_type = GET_QTD_TOKEN_TYPE(token);
- qtd->toggle = GET_DATA_TOGGLE(token);
- if (len > HC_ATL_PL_SIZE)
- count = HC_ATL_PL_SIZE;
+ if (len > MAX_PAYLOAD_SIZE)
+ count = MAX_PAYLOAD_SIZE;
else
count = len;
@@ -706,29 +755,27 @@ static int qtd_fill(struct isp1760_qtd *qtd, void *databuffer, size_t len,
return count;
}
-static int check_error(struct ptd *ptd)
+static int check_error(struct usb_hcd *hcd, struct ptd *ptd)
{
int error = 0;
- u32 dw3;
- dw3 = le32_to_cpu(ptd->dw3);
- if (dw3 & DW3_HALT_BIT) {
+ if (ptd->dw3 & DW3_HALT_BIT) {
error = -EPIPE;
- if (dw3 & DW3_ERROR_BIT)
+ if (ptd->dw3 & DW3_ERROR_BIT)
pr_err("error bit is set in DW3\n");
}
- if (dw3 & DW3_QTD_ACTIVE) {
- printk(KERN_ERR "transfer active bit is set DW3\n");
- printk(KERN_ERR "nak counter: %d, rl: %d\n", (dw3 >> 19) & 0xf,
- (le32_to_cpu(ptd->dw2) >> 25) & 0xf);
+ if (ptd->dw3 & DW3_QTD_ACTIVE) {
+ dev_err(hcd->self.controller, "Transfer active bit is set DW3\n"
+ "nak counter: %d, rl: %d\n",
+ (ptd->dw3 >> 19) & 0xf, (ptd->dw2 >> 25) & 0xf);
}
return error;
}
-static void check_int_err_status(u32 dw4)
+static void check_int_err_status(struct usb_hcd *hcd, u32 dw4)
{
u32 i;
@@ -737,79 +784,67 @@ static void check_int_err_status(u32 dw4)
for (i = 0; i < 8; i++) {
switch (dw4 & 0x7) {
case INT_UNDERRUN:
- printk(KERN_ERR "ERROR: under run , %d\n", i);
+ dev_err(hcd->self.controller, "Underrun (%d)\n", i);
break;
case INT_EXACT:
- printk(KERN_ERR "ERROR: transaction error, %d\n", i);
+ dev_err(hcd->self.controller,
+ "Transaction error (%d)\n", i);
break;
case INT_BABBLE:
- printk(KERN_ERR "ERROR: babble error, %d\n", i);
+ dev_err(hcd->self.controller, "Babble error (%d)\n", i);
break;
}
dw4 >>= 3;
}
}
-static void enqueue_one_qtd(struct isp1760_qtd *qtd, struct isp1760_hcd *priv,
- u32 payload)
+static void enqueue_one_qtd(struct usb_hcd *hcd, struct isp1760_qtd *qtd)
{
- u32 token;
- struct usb_hcd *hcd = priv_to_hcd(priv);
-
- token = qtd->packet_type;
-
- if (qtd->length && (qtd->length <= HC_ATL_PL_SIZE)) {
- switch (token) {
+ if (qtd->length && (qtd->length <= MAX_PAYLOAD_SIZE)) {
+ switch (qtd->packet_type) {
case IN_PID:
break;
case OUT_PID:
case SETUP_PID:
- priv_write_copy(priv, qtd->data_buffer,
- hcd->regs + payload,
- qtd->length);
+ mem_writes8(hcd->regs, qtd->payload_addr,
+ qtd->data_buffer, qtd->length);
}
}
}
-static void enqueue_one_atl_qtd(u32 atl_regs, u32 payload,
- struct isp1760_hcd *priv, struct isp1760_qh *qh,
- struct urb *urb, u32 slot, struct isp1760_qtd *qtd)
+static void enqueue_one_atl_qtd(struct usb_hcd *hcd, struct isp1760_qh *qh,
+ u32 slot, struct isp1760_qtd *qtd)
{
+ struct isp1760_hcd *priv = hcd_to_priv(hcd);
struct ptd ptd;
- struct usb_hcd *hcd = priv_to_hcd(priv);
- transform_into_atl(priv, qh, qtd, urb, payload, &ptd);
- priv_write_copy(priv, (u32 *)&ptd, hcd->regs + atl_regs, sizeof(ptd));
- enqueue_one_qtd(qtd, priv, payload);
+ alloc_mem(hcd, qtd);
+ transform_into_atl(qh, qtd, &ptd);
+ ptd_write(hcd->regs, ATL_PTD_OFFSET, slot, &ptd);
+ enqueue_one_qtd(hcd, qtd);
- priv->atl_ints[slot].urb = urb;
priv->atl_ints[slot].qh = qh;
priv->atl_ints[slot].qtd = qtd;
- priv->atl_ints[slot].data_buffer = qtd->data_buffer;
- priv->atl_ints[slot].payload = payload;
- qtd->status |= URB_ENQUEUED | URB_TYPE_ATL;
+ qtd->status |= URB_ENQUEUED;
qtd->status |= slot << 16;
}
-static void enqueue_one_int_qtd(u32 int_regs, u32 payload,
- struct isp1760_hcd *priv, struct isp1760_qh *qh,
- struct urb *urb, u32 slot, struct isp1760_qtd *qtd)
+static void enqueue_one_int_qtd(struct usb_hcd *hcd, struct isp1760_qh *qh,
+ u32 slot, struct isp1760_qtd *qtd)
{
+ struct isp1760_hcd *priv = hcd_to_priv(hcd);
struct ptd ptd;
- struct usb_hcd *hcd = priv_to_hcd(priv);
- transform_into_int(priv, qh, qtd, urb, payload, &ptd);
- priv_write_copy(priv, (u32 *)&ptd, hcd->regs + int_regs, sizeof(ptd));
- enqueue_one_qtd(qtd, priv, payload);
+ alloc_mem(hcd, qtd);
+ transform_into_int(qh, qtd, &ptd);
+ ptd_write(hcd->regs, INT_PTD_OFFSET, slot, &ptd);
+ enqueue_one_qtd(hcd, qtd);
- priv->int_ints[slot].urb = urb;
priv->int_ints[slot].qh = qh;
priv->int_ints[slot].qtd = qtd;
- priv->int_ints[slot].data_buffer = qtd->data_buffer;
- priv->int_ints[slot].payload = payload;
- qtd->status |= URB_ENQUEUED | URB_TYPE_INT;
+ qtd->status |= URB_ENQUEUED;
qtd->status |= slot << 16;
}
@@ -818,9 +853,7 @@ static void enqueue_an_ATL_packet(struct usb_hcd *hcd, struct isp1760_qh *qh,
{
struct isp1760_hcd *priv = hcd_to_priv(hcd);
u32 skip_map, or_map;
- u32 queue_entry;
u32 slot;
- u32 atl_regs, payload;
u32 buffstatus;
/*
@@ -831,38 +864,35 @@ static void enqueue_an_ATL_packet(struct usb_hcd *hcd, struct isp1760_qh *qh,
*/
mmiowb();
ndelay(195);
- skip_map = isp1760_readl(hcd->regs + HC_ATL_PTD_SKIPMAP_REG);
+ skip_map = reg_read32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG);
BUG_ON(!skip_map);
slot = __ffs(skip_map);
- queue_entry = 1 << slot;
-
- atl_regs = ATL_REGS_OFFSET + slot * sizeof(struct ptd);
- payload = alloc_mem(priv, qtd->length);
+ enqueue_one_atl_qtd(hcd, qh, slot, qtd);
- enqueue_one_atl_qtd(atl_regs, payload, priv, qh, qtd->urb, slot, qtd);
+ or_map = reg_read32(hcd->regs, HC_ATL_IRQ_MASK_OR_REG);
+ or_map |= (1 << slot);
+ reg_write32(hcd->regs, HC_ATL_IRQ_MASK_OR_REG, or_map);
- or_map = isp1760_readl(hcd->regs + HC_ATL_IRQ_MASK_OR_REG);
- or_map |= queue_entry;
- isp1760_writel(or_map, hcd->regs + HC_ATL_IRQ_MASK_OR_REG);
+ skip_map &= ~(1 << slot);
+ reg_write32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG, skip_map);
- skip_map &= ~queue_entry;
- isp1760_writel(skip_map, hcd->regs + HC_ATL_PTD_SKIPMAP_REG);
+ priv->atl_queued++;
+ if (priv->atl_queued == 2)
+ reg_write32(hcd->regs, HC_INTERRUPT_ENABLE,
+ INTERRUPT_ENABLE_SOT_MASK);
- buffstatus = isp1760_readl(hcd->regs + HC_BUFFER_STATUS_REG);
+ buffstatus = reg_read32(hcd->regs, HC_BUFFER_STATUS_REG);
buffstatus |= ATL_BUFFER;
- isp1760_writel(buffstatus, hcd->regs + HC_BUFFER_STATUS_REG);
+ reg_write32(hcd->regs, HC_BUFFER_STATUS_REG, buffstatus);
}
static void enqueue_an_INT_packet(struct usb_hcd *hcd, struct isp1760_qh *qh,
struct isp1760_qtd *qtd)
{
- struct isp1760_hcd *priv = hcd_to_priv(hcd);
u32 skip_map, or_map;
- u32 queue_entry;
u32 slot;
- u32 int_regs, payload;
u32 buffstatus;
/*
@@ -873,37 +903,34 @@ static void enqueue_an_INT_packet(struct usb_hcd *hcd, struct isp1760_qh *qh,
*/
mmiowb();
ndelay(195);
- skip_map = isp1760_readl(hcd->regs + HC_INT_PTD_SKIPMAP_REG);
+ skip_map = reg_read32(hcd->regs, HC_INT_PTD_SKIPMAP_REG);
BUG_ON(!skip_map);
slot = __ffs(skip_map);
- queue_entry = 1 << slot;
-
- int_regs = INT_REGS_OFFSET + slot * sizeof(struct ptd);
- payload = alloc_mem(priv, qtd->length);
+ enqueue_one_int_qtd(hcd, qh, slot, qtd);
- enqueue_one_int_qtd(int_regs, payload, priv, qh, qtd->urb, slot, qtd);
+ or_map = reg_read32(hcd->regs, HC_INT_IRQ_MASK_OR_REG);
+ or_map |= (1 << slot);
+ reg_write32(hcd->regs, HC_INT_IRQ_MASK_OR_REG, or_map);
- or_map = isp1760_readl(hcd->regs + HC_INT_IRQ_MASK_OR_REG);
- or_map |= queue_entry;
- isp1760_writel(or_map, hcd->regs + HC_INT_IRQ_MASK_OR_REG);
+ skip_map &= ~(1 << slot);
+ reg_write32(hcd->regs, HC_INT_PTD_SKIPMAP_REG, skip_map);
- skip_map &= ~queue_entry;
- isp1760_writel(skip_map, hcd->regs + HC_INT_PTD_SKIPMAP_REG);
-
- buffstatus = isp1760_readl(hcd->regs + HC_BUFFER_STATUS_REG);
+ buffstatus = reg_read32(hcd->regs, HC_BUFFER_STATUS_REG);
buffstatus |= INT_BUFFER;
- isp1760_writel(buffstatus, hcd->regs + HC_BUFFER_STATUS_REG);
+ reg_write32(hcd->regs, HC_BUFFER_STATUS_REG, buffstatus);
}
-static void isp1760_urb_done(struct isp1760_hcd *priv, struct urb *urb, int status)
+static void isp1760_urb_done(struct usb_hcd *hcd, struct urb *urb)
__releases(priv->lock)
__acquires(priv->lock)
{
+ struct isp1760_hcd *priv = hcd_to_priv(hcd);
+
if (!urb->unlinked) {
- if (status == -EINPROGRESS)
- status = 0;
+ if (urb->status == -EINPROGRESS)
+ urb->status = 0;
}
if (usb_pipein(urb->pipe) && usb_pipetype(urb->pipe) != PIPE_CONTROL) {
@@ -915,22 +942,28 @@ __acquires(priv->lock)
}
/* complete() can reenter this HCD */
- usb_hcd_unlink_urb_from_ep(priv_to_hcd(priv), urb);
+ usb_hcd_unlink_urb_from_ep(hcd, urb);
spin_unlock(&priv->lock);
- usb_hcd_giveback_urb(priv_to_hcd(priv), urb, status);
+ usb_hcd_giveback_urb(hcd, urb, urb->status);
spin_lock(&priv->lock);
}
static void isp1760_qtd_free(struct isp1760_qtd *qtd)
{
+ BUG_ON(qtd->payload_addr);
kmem_cache_free(qtd_cachep, qtd);
}
-static struct isp1760_qtd *clean_this_qtd(struct isp1760_qtd *qtd)
+static struct isp1760_qtd *clean_this_qtd(struct isp1760_qtd *qtd,
+ struct isp1760_qh *qh)
{
struct isp1760_qtd *tmp_qtd;
- tmp_qtd = qtd->hw_next;
+ if (list_is_last(&qtd->qtd_list, &qh->qtd_list))
+ tmp_qtd = NULL;
+ else
+ tmp_qtd = list_entry(qtd->qtd_list.next, struct isp1760_qtd,
+ qtd_list);
list_del(&qtd->qtd_list);
isp1760_qtd_free(qtd);
return tmp_qtd;
@@ -941,32 +974,26 @@ static struct isp1760_qtd *clean_this_qtd(struct isp1760_qtd *qtd)
* isn't the last one than remove also his successor(s).
* Returns the QTD which is part of an new URB and should be enqueued.
*/
-static struct isp1760_qtd *clean_up_qtdlist(struct isp1760_qtd *qtd)
+static struct isp1760_qtd *clean_up_qtdlist(struct isp1760_qtd *qtd,
+ struct isp1760_qh *qh)
{
- struct isp1760_qtd *tmp_qtd;
- int last_one;
+ struct urb *urb;
+ urb = qtd->urb;
do {
- tmp_qtd = qtd->hw_next;
- last_one = qtd->status & URB_COMPLETE_NOTIFY;
- list_del(&qtd->qtd_list);
- isp1760_qtd_free(qtd);
- qtd = tmp_qtd;
- } while (!last_one && qtd);
+ qtd = clean_this_qtd(qtd, qh);
+ } while (qtd && (qtd->urb == urb));
return qtd;
}
-static void do_atl_int(struct usb_hcd *usb_hcd)
+static void do_atl_int(struct usb_hcd *hcd)
{
- struct isp1760_hcd *priv = hcd_to_priv(usb_hcd);
+ struct isp1760_hcd *priv = hcd_to_priv(hcd);
u32 done_map, skip_map;
struct ptd ptd;
- struct urb *urb = NULL;
- u32 atl_regs_base;
- u32 atl_regs;
- u32 queue_entry;
- u32 payload;
+ struct urb *urb;
+ u32 slot;
u32 length;
u32 or_map;
u32 status = -EINVAL;
@@ -976,62 +1003,36 @@ static void do_atl_int(struct usb_hcd *usb_hcd)
u32 rl;
u32 nakcount;
- done_map = isp1760_readl(usb_hcd->regs +
- HC_ATL_PTD_DONEMAP_REG);
- skip_map = isp1760_readl(usb_hcd->regs +
- HC_ATL_PTD_SKIPMAP_REG);
+ done_map = reg_read32(hcd->regs, HC_ATL_PTD_DONEMAP_REG);
+ skip_map = reg_read32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG);
- or_map = isp1760_readl(usb_hcd->regs + HC_ATL_IRQ_MASK_OR_REG);
+ or_map = reg_read32(hcd->regs, HC_ATL_IRQ_MASK_OR_REG);
or_map &= ~done_map;
- isp1760_writel(or_map, usb_hcd->regs + HC_ATL_IRQ_MASK_OR_REG);
+ reg_write32(hcd->regs, HC_ATL_IRQ_MASK_OR_REG, or_map);
- atl_regs_base = ATL_REGS_OFFSET;
while (done_map) {
- u32 dw1;
- u32 dw2;
- u32 dw3;
-
status = 0;
+ priv->atl_queued--;
- queue_entry = __ffs(done_map);
- done_map &= ~(1 << queue_entry);
- skip_map |= 1 << queue_entry;
-
- atl_regs = atl_regs_base + queue_entry * sizeof(struct ptd);
+ slot = __ffs(done_map);
+ done_map &= ~(1 << slot);
+ skip_map |= (1 << slot);
- urb = priv->atl_ints[queue_entry].urb;
- qtd = priv->atl_ints[queue_entry].qtd;
- qh = priv->atl_ints[queue_entry].qh;
- payload = priv->atl_ints[queue_entry].payload;
+ qtd = priv->atl_ints[slot].qtd;
+ qh = priv->atl_ints[slot].qh;
if (!qh) {
- printk(KERN_ERR "qh is 0\n");
+ dev_err(hcd->self.controller, "qh is 0\n");
continue;
}
- isp1760_writel(atl_regs + ISP_BANK(0), usb_hcd->regs +
- HC_MEMORY_REG);
- isp1760_writel(payload + ISP_BANK(1), usb_hcd->regs +
- HC_MEMORY_REG);
- /*
- * write bank1 address twice to ensure the 90ns delay (time
- * between BANK0 write and the priv_read_copy() call is at
- * least 3*t_WHWL + 2*t_w11 = 3*25ns + 2*17ns = 109ns)
- */
- isp1760_writel(payload + ISP_BANK(1), usb_hcd->regs +
- HC_MEMORY_REG);
+ ptd_read(hcd->regs, ATL_PTD_OFFSET, slot, &ptd);
- priv_read_copy(priv, (u32 *)&ptd, usb_hcd->regs + atl_regs +
- ISP_BANK(0), sizeof(ptd));
-
- dw1 = le32_to_cpu(ptd.dw1);
- dw2 = le32_to_cpu(ptd.dw2);
- dw3 = le32_to_cpu(ptd.dw3);
- rl = (dw2 >> 25) & 0x0f;
- nakcount = (dw3 >> 19) & 0xf;
+ rl = (ptd.dw2 >> 25) & 0x0f;
+ nakcount = (ptd.dw3 >> 19) & 0xf;
/* Transfer Error, *but* active and no HALT -> reload */
- if ((dw3 & DW3_ERROR_BIT) && (dw3 & DW3_QTD_ACTIVE) &&
- !(dw3 & DW3_HALT_BIT)) {
+ if ((ptd.dw3 & DW3_ERROR_BIT) && (ptd.dw3 & DW3_QTD_ACTIVE) &&
+ !(ptd.dw3 & DW3_HALT_BIT)) {
/* according to ppriv code, we have to
* reload this one if trasfered bytes != requested bytes
@@ -1040,13 +1041,14 @@ static void do_atl_int(struct usb_hcd *usb_hcd)
* triggered so far.
*/
- length = PTD_XFERRED_LENGTH(dw3);
- printk(KERN_ERR "Should reload now.... transfered %d "
+ length = PTD_XFERRED_LENGTH(ptd.dw3);
+ dev_err(hcd->self.controller,
+ "Should reload now... transferred %d "
"of %zu\n", length, qtd->length);
BUG();
}
- if (!nakcount && (dw3 & DW3_QTD_ACTIVE)) {
+ if (!nakcount && (ptd.dw3 & DW3_QTD_ACTIVE)) {
u32 buffstatus;
/*
@@ -1054,52 +1056,45 @@ static void do_atl_int(struct usb_hcd *usb_hcd)
* device is not able to send data fast enough.
* This happens mostly on slower hardware.
*/
- printk(KERN_NOTICE "Reloading ptd %p/%p... qh %p read: "
- "%d of %zu done: %08x cur: %08x\n", qtd,
- urb, qh, PTD_XFERRED_LENGTH(dw3),
- qtd->length, done_map,
- (1 << queue_entry));
/* RL counter = ERR counter */
- dw3 &= ~(0xf << 19);
- dw3 |= rl << 19;
- dw3 &= ~(3 << (55 - 32));
- dw3 |= ERR_COUNTER << (55 - 32);
+ ptd.dw3 &= ~(0xf << 19);
+ ptd.dw3 |= rl << 19;
+ ptd.dw3 &= ~(3 << (55 - 32));
+ ptd.dw3 |= ERR_COUNTER << (55 - 32);
/*
* It is not needed to write skip map back because it
* is unchanged. Just make sure that this entry is
* unskipped once it gets written to the HW.
*/
- skip_map &= ~(1 << queue_entry);
- or_map = isp1760_readl(usb_hcd->regs +
- HC_ATL_IRQ_MASK_OR_REG);
- or_map |= 1 << queue_entry;
- isp1760_writel(or_map, usb_hcd->regs +
- HC_ATL_IRQ_MASK_OR_REG);
-
- ptd.dw3 = cpu_to_le32(dw3);
- priv_write_copy(priv, (u32 *)&ptd, usb_hcd->regs +
- atl_regs, sizeof(ptd));
-
- ptd.dw0 |= cpu_to_le32(PTD_VALID);
- priv_write_copy(priv, (u32 *)&ptd, usb_hcd->regs +
- atl_regs, sizeof(ptd));
-
- buffstatus = isp1760_readl(usb_hcd->regs +
- HC_BUFFER_STATUS_REG);
+ skip_map &= ~(1 << slot);
+ or_map = reg_read32(hcd->regs, HC_ATL_IRQ_MASK_OR_REG);
+ or_map |= 1 << slot;
+ reg_write32(hcd->regs, HC_ATL_IRQ_MASK_OR_REG, or_map);
+
+ ptd.dw0 |= PTD_VALID;
+ ptd_write(hcd->regs, ATL_PTD_OFFSET, slot, &ptd);
+
+ priv->atl_queued++;
+ if (priv->atl_queued == 2)
+ reg_write32(hcd->regs, HC_INTERRUPT_ENABLE,
+ INTERRUPT_ENABLE_SOT_MASK);
+
+ buffstatus = reg_read32(hcd->regs,
+ HC_BUFFER_STATUS_REG);
buffstatus |= ATL_BUFFER;
- isp1760_writel(buffstatus, usb_hcd->regs +
- HC_BUFFER_STATUS_REG);
+ reg_write32(hcd->regs, HC_BUFFER_STATUS_REG,
+ buffstatus);
continue;
}
- error = check_error(&ptd);
+ error = check_error(hcd, &ptd);
if (error) {
status = error;
- priv->atl_ints[queue_entry].qh->toggle = 0;
- priv->atl_ints[queue_entry].qh->ping = 0;
- urb->status = -EPIPE;
+ priv->atl_ints[slot].qh->toggle = 0;
+ priv->atl_ints[slot].qh->ping = 0;
+ qtd->urb->status = -EPIPE;
#if 0
printk(KERN_ERR "Error in %s().\n", __func__);
@@ -1110,154 +1105,123 @@ static void do_atl_int(struct usb_hcd *usb_hcd)
ptd.dw4, ptd.dw5, ptd.dw6, ptd.dw7);
#endif
} else {
- if (usb_pipetype(urb->pipe) == PIPE_BULK) {
- priv->atl_ints[queue_entry].qh->toggle = dw3 &
- (1 << 25);
- priv->atl_ints[queue_entry].qh->ping = dw3 &
- (1 << 26);
- }
+ priv->atl_ints[slot].qh->toggle = ptd.dw3 & (1 << 25);
+ priv->atl_ints[slot].qh->ping = ptd.dw3 & (1 << 26);
}
- length = PTD_XFERRED_LENGTH(dw3);
+ length = PTD_XFERRED_LENGTH(ptd.dw3);
if (length) {
- switch (DW1_GET_PID(dw1)) {
+ switch (DW1_GET_PID(ptd.dw1)) {
case IN_PID:
- priv_read_copy(priv,
- priv->atl_ints[queue_entry].data_buffer,
- usb_hcd->regs + payload + ISP_BANK(1),
- length);
+ mem_reads8(hcd->regs, qtd->payload_addr,
+ qtd->data_buffer, length);
case OUT_PID:
- urb->actual_length += length;
+ qtd->urb->actual_length += length;
case SETUP_PID:
break;
}
}
- priv->atl_ints[queue_entry].data_buffer = NULL;
- priv->atl_ints[queue_entry].urb = NULL;
- priv->atl_ints[queue_entry].qtd = NULL;
- priv->atl_ints[queue_entry].qh = NULL;
+ priv->atl_ints[slot].qtd = NULL;
+ priv->atl_ints[slot].qh = NULL;
- free_mem(priv, payload);
+ free_mem(hcd, qtd);
- isp1760_writel(skip_map, usb_hcd->regs +
- HC_ATL_PTD_SKIPMAP_REG);
+ reg_write32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG, skip_map);
- if (urb->status == -EPIPE) {
+ if (qtd->urb->status == -EPIPE) {
/* HALT was received */
- qtd = clean_up_qtdlist(qtd);
- isp1760_urb_done(priv, urb, urb->status);
+ urb = qtd->urb;
+ qtd = clean_up_qtdlist(qtd, qh);
+ isp1760_urb_done(hcd, urb);
- } else if (usb_pipebulk(urb->pipe) && (length < qtd->length)) {
+ } else if (usb_pipebulk(qtd->urb->pipe) &&
+ (length < qtd->length)) {
/* short BULK received */
- if (urb->transfer_flags & URB_SHORT_NOT_OK) {
- urb->status = -EREMOTEIO;
- isp1760_dbg(priv, "short bulk, %d instead %zu "
- "with URB_SHORT_NOT_OK flag.\n",
- length, qtd->length);
+ if (qtd->urb->transfer_flags & URB_SHORT_NOT_OK) {
+ qtd->urb->status = -EREMOTEIO;
+ dev_dbg(hcd->self.controller,
+ "short bulk, %d instead %zu "
+ "with URB_SHORT_NOT_OK flag.\n",
+ length, qtd->length);
}
- if (urb->status == -EINPROGRESS)
- urb->status = 0;
-
- qtd = clean_up_qtdlist(qtd);
+ if (qtd->urb->status == -EINPROGRESS)
+ qtd->urb->status = 0;
- isp1760_urb_done(priv, urb, urb->status);
+ urb = qtd->urb;
+ qtd = clean_up_qtdlist(qtd, qh);
+ isp1760_urb_done(hcd, urb);
- } else if (qtd->status & URB_COMPLETE_NOTIFY) {
+ } else if (last_qtd_of_urb(qtd, qh)) {
/* that was the last qtd of that URB */
- if (urb->status == -EINPROGRESS)
- urb->status = 0;
+ if (qtd->urb->status == -EINPROGRESS)
+ qtd->urb->status = 0;
- qtd = clean_this_qtd(qtd);
- isp1760_urb_done(priv, urb, urb->status);
+ urb = qtd->urb;
+ qtd = clean_up_qtdlist(qtd, qh);
+ isp1760_urb_done(hcd, urb);
} else {
/* next QTD of this URB */
- qtd = clean_this_qtd(qtd);
+ qtd = clean_this_qtd(qtd, qh);
BUG_ON(!qtd);
}
if (qtd)
- enqueue_an_ATL_packet(usb_hcd, qh, qtd);
+ enqueue_an_ATL_packet(hcd, qh, qtd);
- skip_map = isp1760_readl(usb_hcd->regs +
- HC_ATL_PTD_SKIPMAP_REG);
+ skip_map = reg_read32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG);
}
+ if (priv->atl_queued <= 1)
+ reg_write32(hcd->regs, HC_INTERRUPT_ENABLE,
+ INTERRUPT_ENABLE_MASK);
}
-static void do_intl_int(struct usb_hcd *usb_hcd)
+static void do_intl_int(struct usb_hcd *hcd)
{
- struct isp1760_hcd *priv = hcd_to_priv(usb_hcd);
+ struct isp1760_hcd *priv = hcd_to_priv(hcd);
u32 done_map, skip_map;
struct ptd ptd;
- struct urb *urb = NULL;
- u32 int_regs;
- u32 int_regs_base;
- u32 payload;
+ struct urb *urb;
u32 length;
u32 or_map;
int error;
- u32 queue_entry;
+ u32 slot;
struct isp1760_qtd *qtd;
struct isp1760_qh *qh;
- done_map = isp1760_readl(usb_hcd->regs +
- HC_INT_PTD_DONEMAP_REG);
- skip_map = isp1760_readl(usb_hcd->regs +
- HC_INT_PTD_SKIPMAP_REG);
+ done_map = reg_read32(hcd->regs, HC_INT_PTD_DONEMAP_REG);
+ skip_map = reg_read32(hcd->regs, HC_INT_PTD_SKIPMAP_REG);
- or_map = isp1760_readl(usb_hcd->regs + HC_INT_IRQ_MASK_OR_REG);
+ or_map = reg_read32(hcd->regs, HC_INT_IRQ_MASK_OR_REG);
or_map &= ~done_map;
- isp1760_writel(or_map, usb_hcd->regs + HC_INT_IRQ_MASK_OR_REG);
-
- int_regs_base = INT_REGS_OFFSET;
+ reg_write32(hcd->regs, HC_INT_IRQ_MASK_OR_REG, or_map);
while (done_map) {
- u32 dw1;
- u32 dw3;
+ slot = __ffs(done_map);
+ done_map &= ~(1 << slot);
+ skip_map |= (1 << slot);
- queue_entry = __ffs(done_map);
- done_map &= ~(1 << queue_entry);
- skip_map |= 1 << queue_entry;
-
- int_regs = int_regs_base + queue_entry * sizeof(struct ptd);
- urb = priv->int_ints[queue_entry].urb;
- qtd = priv->int_ints[queue_entry].qtd;
- qh = priv->int_ints[queue_entry].qh;
- payload = priv->int_ints[queue_entry].payload;
+ qtd = priv->int_ints[slot].qtd;
+ qh = priv->int_ints[slot].qh;
if (!qh) {
- printk(KERN_ERR "(INT) qh is 0\n");
+ dev_err(hcd->self.controller, "(INT) qh is 0\n");
continue;
}
- isp1760_writel(int_regs + ISP_BANK(0), usb_hcd->regs +
- HC_MEMORY_REG);
- isp1760_writel(payload + ISP_BANK(1), usb_hcd->regs +
- HC_MEMORY_REG);
- /*
- * write bank1 address twice to ensure the 90ns delay (time
- * between BANK0 write and the priv_read_copy() call is at
- * least 3*t_WHWL + 2*t_w11 = 3*25ns + 2*17ns = 92ns)
- */
- isp1760_writel(payload + ISP_BANK(1), usb_hcd->regs +
- HC_MEMORY_REG);
+ ptd_read(hcd->regs, INT_PTD_OFFSET, slot, &ptd);
+ check_int_err_status(hcd, ptd.dw4);
- priv_read_copy(priv, (u32 *)&ptd, usb_hcd->regs + int_regs +
- ISP_BANK(0), sizeof(ptd));
- dw1 = le32_to_cpu(ptd.dw1);
- dw3 = le32_to_cpu(ptd.dw3);
- check_int_err_status(le32_to_cpu(ptd.dw4));
-
- error = check_error(&ptd);
+ error = check_error(hcd, &ptd);
if (error) {
#if 0
printk(KERN_ERR "Error in %s().\n", __func__);
@@ -1267,83 +1231,77 @@ static void do_intl_int(struct usb_hcd *usb_hcd)
ptd.dw0, ptd.dw1, ptd.dw2, ptd.dw3,
ptd.dw4, ptd.dw5, ptd.dw6, ptd.dw7);
#endif
- urb->status = -EPIPE;
- priv->int_ints[queue_entry].qh->toggle = 0;
- priv->int_ints[queue_entry].qh->ping = 0;
+ qtd->urb->status = -EPIPE;
+ priv->int_ints[slot].qh->toggle = 0;
+ priv->int_ints[slot].qh->ping = 0;
} else {
- priv->int_ints[queue_entry].qh->toggle =
- dw3 & (1 << 25);
- priv->int_ints[queue_entry].qh->ping = dw3 & (1 << 26);
+ priv->int_ints[slot].qh->toggle = ptd.dw3 & (1 << 25);
+ priv->int_ints[slot].qh->ping = ptd.dw3 & (1 << 26);
}
- if (urb->dev->speed != USB_SPEED_HIGH)
- length = PTD_XFERRED_LENGTH_LO(dw3);
+ if (qtd->urb->dev->speed != USB_SPEED_HIGH)
+ length = PTD_XFERRED_LENGTH_LO(ptd.dw3);
else
- length = PTD_XFERRED_LENGTH(dw3);
+ length = PTD_XFERRED_LENGTH(ptd.dw3);
if (length) {
- switch (DW1_GET_PID(dw1)) {
+ switch (DW1_GET_PID(ptd.dw1)) {
case IN_PID:
- priv_read_copy(priv,
- priv->int_ints[queue_entry].data_buffer,
- usb_hcd->regs + payload + ISP_BANK(1),
- length);
+ mem_reads8(hcd->regs, qtd->payload_addr,
+ qtd->data_buffer, length);
case OUT_PID:
- urb->actual_length += length;
+ qtd->urb->actual_length += length;
case SETUP_PID:
break;
}
}
- priv->int_ints[queue_entry].data_buffer = NULL;
- priv->int_ints[queue_entry].urb = NULL;
- priv->int_ints[queue_entry].qtd = NULL;
- priv->int_ints[queue_entry].qh = NULL;
+ priv->int_ints[slot].qtd = NULL;
+ priv->int_ints[slot].qh = NULL;
- isp1760_writel(skip_map, usb_hcd->regs +
- HC_INT_PTD_SKIPMAP_REG);
- free_mem(priv, payload);
+ reg_write32(hcd->regs, HC_INT_PTD_SKIPMAP_REG, skip_map);
+ free_mem(hcd, qtd);
- if (urb->status == -EPIPE) {
+ if (qtd->urb->status == -EPIPE) {
/* HALT received */
- qtd = clean_up_qtdlist(qtd);
- isp1760_urb_done(priv, urb, urb->status);
+ urb = qtd->urb;
+ qtd = clean_up_qtdlist(qtd, qh);
+ isp1760_urb_done(hcd, urb);
- } else if (qtd->status & URB_COMPLETE_NOTIFY) {
+ } else if (last_qtd_of_urb(qtd, qh)) {
- if (urb->status == -EINPROGRESS)
- urb->status = 0;
+ if (qtd->urb->status == -EINPROGRESS)
+ qtd->urb->status = 0;
- qtd = clean_this_qtd(qtd);
- isp1760_urb_done(priv, urb, urb->status);
+ urb = qtd->urb;
+ qtd = clean_up_qtdlist(qtd, qh);
+ isp1760_urb_done(hcd, urb);
} else {
/* next QTD of this URB */
- qtd = clean_this_qtd(qtd);
+ qtd = clean_this_qtd(qtd, qh);
BUG_ON(!qtd);
}
if (qtd)
- enqueue_an_INT_packet(usb_hcd, qh, qtd);
+ enqueue_an_INT_packet(hcd, qh, qtd);
- skip_map = isp1760_readl(usb_hcd->regs +
- HC_INT_PTD_SKIPMAP_REG);
+ skip_map = reg_read32(hcd->regs, HC_INT_PTD_SKIPMAP_REG);
}
}
-#define max_packet(wMaxPacketSize) ((wMaxPacketSize) & 0x07ff)
-static struct isp1760_qh *qh_make(struct isp1760_hcd *priv, struct urb *urb,
+static struct isp1760_qh *qh_make(struct usb_hcd *hcd, struct urb *urb,
gfp_t flags)
{
struct isp1760_qh *qh;
int is_input, type;
- qh = isp1760_qh_alloc(priv, flags);
+ qh = isp1760_qh_alloc(flags);
if (!qh)
return qh;
@@ -1353,29 +1311,6 @@ static struct isp1760_qh *qh_make(struct isp1760_hcd *priv, struct urb *urb,
is_input = usb_pipein(urb->pipe);
type = usb_pipetype(urb->pipe);
- if (type == PIPE_INTERRUPT) {
-
- if (urb->dev->speed == USB_SPEED_HIGH) {
-
- qh->period = urb->interval >> 3;
- if (qh->period == 0 && urb->interval != 1) {
- /* NOTE interval 2 or 4 uframes could work.
- * But interval 1 scheduling is simpler, and
- * includes high bandwidth.
- */
- printk(KERN_ERR "intr period %d uframes, NYET!",
- urb->interval);
- qh_destroy(qh);
- return NULL;
- }
- } else {
- qh->period = urb->interval;
- }
- }
-
- /* support for tt scheduling, and access to toggles */
- qh->dev = urb->dev;
-
if (!usb_pipecontrol(urb->pipe))
usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), !is_input,
1);
@@ -1388,43 +1323,27 @@ static struct isp1760_qh *qh_make(struct isp1760_hcd *priv, struct urb *urb,
* Returns null if it can't allocate a QH it needs to.
* If the QH has TDs (urbs) already, that's great.
*/
-static struct isp1760_qh *qh_append_tds(struct isp1760_hcd *priv,
+static struct isp1760_qh *qh_append_tds(struct usb_hcd *hcd,
struct urb *urb, struct list_head *qtd_list, int epnum,
void **ptr)
{
struct isp1760_qh *qh;
- struct isp1760_qtd *qtd;
- struct isp1760_qtd *prev_qtd;
qh = (struct isp1760_qh *)*ptr;
if (!qh) {
/* can't sleep here, we have priv->lock... */
- qh = qh_make(priv, urb, GFP_ATOMIC);
+ qh = qh_make(hcd, urb, GFP_ATOMIC);
if (!qh)
return qh;
*ptr = qh;
}
- qtd = list_entry(qtd_list->next, struct isp1760_qtd,
- qtd_list);
- if (!list_empty(&qh->qtd_list))
- prev_qtd = list_entry(qh->qtd_list.prev,
- struct isp1760_qtd, qtd_list);
- else
- prev_qtd = NULL;
-
list_splice(qtd_list, qh->qtd_list.prev);
- if (prev_qtd) {
- BUG_ON(prev_qtd->hw_next);
- prev_qtd->hw_next = qtd;
- }
- urb->hcpriv = qh;
return qh;
}
-static void qtd_list_free(struct isp1760_hcd *priv, struct urb *urb,
- struct list_head *qtd_list)
+static void qtd_list_free(struct urb *urb, struct list_head *qtd_list)
{
struct list_head *entry, *temp;
@@ -1437,9 +1356,10 @@ static void qtd_list_free(struct isp1760_hcd *priv, struct urb *urb,
}
}
-static int isp1760_prepare_enqueue(struct isp1760_hcd *priv, struct urb *urb,
+static int isp1760_prepare_enqueue(struct usb_hcd *hcd, struct urb *urb,
struct list_head *qtd_list, gfp_t mem_flags, packet_enqueue *p)
{
+ struct isp1760_hcd *priv = hcd_to_priv(hcd);
struct isp1760_qtd *qtd;
int epnum;
unsigned long flags;
@@ -1451,11 +1371,11 @@ static int isp1760_prepare_enqueue(struct isp1760_hcd *priv, struct urb *urb,
epnum = urb->ep->desc.bEndpointAddress;
spin_lock_irqsave(&priv->lock, flags);
- if (!HCD_HW_ACCESSIBLE(priv_to_hcd(priv))) {
+ if (!HCD_HW_ACCESSIBLE(hcd)) {
rc = -ESHUTDOWN;
goto done;
}
- rc = usb_hcd_link_urb_to_ep(priv_to_hcd(priv), urb);
+ rc = usb_hcd_link_urb_to_ep(hcd, urb);
if (rc)
goto done;
@@ -1465,25 +1385,24 @@ static int isp1760_prepare_enqueue(struct isp1760_hcd *priv, struct urb *urb,
else
qh_busy = 0;
- qh = qh_append_tds(priv, urb, qtd_list, epnum, &urb->ep->hcpriv);
+ qh = qh_append_tds(hcd, urb, qtd_list, epnum, &urb->ep->hcpriv);
if (!qh) {
- usb_hcd_unlink_urb_from_ep(priv_to_hcd(priv), urb);
+ usb_hcd_unlink_urb_from_ep(hcd, urb);
rc = -ENOMEM;
goto done;
}
if (!qh_busy)
- p(priv_to_hcd(priv), qh, qtd);
+ p(hcd, qh, qtd);
done:
spin_unlock_irqrestore(&priv->lock, flags);
if (!qh)
- qtd_list_free(priv, urb, qtd_list);
+ qtd_list_free(urb, qtd_list);
return rc;
}
-static struct isp1760_qtd *isp1760_qtd_alloc(struct isp1760_hcd *priv,
- gfp_t flags)
+static struct isp1760_qtd *isp1760_qtd_alloc(gfp_t flags)
{
struct isp1760_qtd *qtd;
@@ -1497,10 +1416,11 @@ static struct isp1760_qtd *isp1760_qtd_alloc(struct isp1760_hcd *priv,
/*
* create a list of filled qtds for this URB; won't link into qh.
*/
-static struct list_head *qh_urb_transaction(struct isp1760_hcd *priv,
+#define max_packet(wMaxPacketSize) ((wMaxPacketSize) & 0x07ff)
+static struct list_head *qh_urb_transaction(struct usb_hcd *hcd,
struct urb *urb, struct list_head *head, gfp_t flags)
{
- struct isp1760_qtd *qtd, *qtd_prev;
+ struct isp1760_qtd *qtd;
void *buf;
int len, maxpacket;
int is_input;
@@ -1509,7 +1429,7 @@ static struct list_head *qh_urb_transaction(struct isp1760_hcd *priv,
/*
* URBs map to sequences of QTDs: one logical transaction
*/
- qtd = isp1760_qtd_alloc(priv, flags);
+ qtd = isp1760_qtd_alloc(flags);
if (!qtd)
return NULL;
@@ -1529,13 +1449,10 @@ static struct list_head *qh_urb_transaction(struct isp1760_hcd *priv,
token | SETUP_PID);
/* ... and always at least one more pid */
- token ^= DATA_TOGGLE;
- qtd_prev = qtd;
- qtd = isp1760_qtd_alloc(priv, flags);
+ qtd = isp1760_qtd_alloc(flags);
if (!qtd)
goto cleanup;
qtd->urb = urb;
- qtd_prev->hw_next = qtd;
list_add_tail(&qtd->qtd_list, head);
/* for zero length DATA stages, STATUS is always IN */
@@ -1565,7 +1482,7 @@ static struct list_head *qh_urb_transaction(struct isp1760_hcd *priv,
if (!buf && len) {
/* XXX This looks like usb storage / SCSI bug */
- printk(KERN_ERR "buf is null, dma is %08lx len is %d\n",
+ dev_err(hcd->self.controller, "buf is null, dma is %08lx len is %d\n",
(long unsigned)urb->transfer_dma, len);
WARN_ON(1);
}
@@ -1574,19 +1491,13 @@ static struct list_head *qh_urb_transaction(struct isp1760_hcd *priv,
len -= this_qtd_len;
buf += this_qtd_len;
- /* qh makes control packets use qtd toggle; maybe switch it */
- if ((maxpacket & (this_qtd_len + (maxpacket - 1))) == 0)
- token ^= DATA_TOGGLE;
-
if (len <= 0)
break;
- qtd_prev = qtd;
- qtd = isp1760_qtd_alloc(priv, flags);
+ qtd = isp1760_qtd_alloc(flags);
if (!qtd)
goto cleanup;
qtd->urb = urb;
- qtd_prev->hw_next = qtd;
list_add_tail(&qtd->qtd_list, head);
}
@@ -1601,20 +1512,16 @@ static struct list_head *qh_urb_transaction(struct isp1760_hcd *priv,
one_more = 1;
/* "in" <--> "out" */
token ^= IN_PID;
- /* force DATA1 */
- token |= DATA_TOGGLE;
} else if (usb_pipebulk(urb->pipe)
&& (urb->transfer_flags & URB_ZERO_PACKET)
&& !(urb->transfer_buffer_length % maxpacket)) {
one_more = 1;
}
if (one_more) {
- qtd_prev = qtd;
- qtd = isp1760_qtd_alloc(priv, flags);
+ qtd = isp1760_qtd_alloc(flags);
if (!qtd)
goto cleanup;
qtd->urb = urb;
- qtd_prev->hw_next = qtd;
list_add_tail(&qtd->qtd_list, head);
/* never any data in such packets */
@@ -1622,18 +1529,17 @@ static struct list_head *qh_urb_transaction(struct isp1760_hcd *priv,
}
}
- qtd->status = URB_COMPLETE_NOTIFY;
+ qtd->status = 0;
return head;
cleanup:
- qtd_list_free(priv, urb, head);
+ qtd_list_free(urb, head);
return NULL;
}
static int isp1760_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
gfp_t mem_flags)
{
- struct isp1760_hcd *priv = hcd_to_priv(hcd);
struct list_head qtd_list;
packet_enqueue *pe;
@@ -1642,29 +1548,27 @@ static int isp1760_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
switch (usb_pipetype(urb->pipe)) {
case PIPE_CONTROL:
case PIPE_BULK:
-
- if (!qh_urb_transaction(priv, urb, &qtd_list, mem_flags))
+ if (!qh_urb_transaction(hcd, urb, &qtd_list, mem_flags))
return -ENOMEM;
pe = enqueue_an_ATL_packet;
break;
case PIPE_INTERRUPT:
- if (!qh_urb_transaction(priv, urb, &qtd_list, mem_flags))
+ if (!qh_urb_transaction(hcd, urb, &qtd_list, mem_flags))
return -ENOMEM;
pe = enqueue_an_INT_packet;
break;
case PIPE_ISOCHRONOUS:
- printk(KERN_ERR "PIPE_ISOCHRONOUS ain't supported\n");
+ dev_err(hcd->self.controller, "PIPE_ISOCHRONOUS ain't supported\n");
default:
return -EPIPE;
}
- return isp1760_prepare_enqueue(priv, urb, &qtd_list, mem_flags, pe);
+ return isp1760_prepare_enqueue(hcd, urb, &qtd_list, mem_flags, pe);
}
-static int isp1760_urb_dequeue(struct usb_hcd *hcd, struct urb *urb,
- int status)
+static int isp1760_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
{
struct isp1760_hcd *priv = hcd_to_priv(hcd);
struct inter_packet_info *ints;
@@ -1681,7 +1585,7 @@ static int isp1760_urb_dequeue(struct usb_hcd *hcd, struct urb *urb,
case PIPE_INTERRUPT:
ints = priv->int_ints;
- reg_base = INT_REGS_OFFSET;
+ reg_base = INT_PTD_OFFSET;
or_reg = HC_INT_IRQ_MASK_OR_REG;
skip_reg = HC_INT_PTD_SKIPMAP_REG;
pe = enqueue_an_INT_packet;
@@ -1689,7 +1593,7 @@ static int isp1760_urb_dequeue(struct usb_hcd *hcd, struct urb *urb,
default:
ints = priv->atl_ints;
- reg_base = ATL_REGS_OFFSET;
+ reg_base = ATL_PTD_OFFSET;
or_reg = HC_ATL_IRQ_MASK_OR_REG;
skip_reg = HC_ATL_PTD_SKIPMAP_REG;
pe = enqueue_an_ATL_packet;
@@ -1700,81 +1604,84 @@ static int isp1760_urb_dequeue(struct usb_hcd *hcd, struct urb *urb,
spin_lock_irqsave(&priv->lock, flags);
for (i = 0; i < 32; i++) {
- if (ints->urb == urb) {
+ if (!ints[i].qh)
+ continue;
+ BUG_ON(!ints[i].qtd);
+
+ if (ints[i].qtd->urb == urb) {
u32 skip_map;
u32 or_map;
struct isp1760_qtd *qtd;
- struct isp1760_qh *qh = ints->qh;
+ struct isp1760_qh *qh;
- skip_map = isp1760_readl(hcd->regs + skip_reg);
+ skip_map = reg_read32(hcd->regs, skip_reg);
skip_map |= 1 << i;
- isp1760_writel(skip_map, hcd->regs + skip_reg);
+ reg_write32(hcd->regs, skip_reg, skip_map);
- or_map = isp1760_readl(hcd->regs + or_reg);
+ or_map = reg_read32(hcd->regs, or_reg);
or_map &= ~(1 << i);
- isp1760_writel(or_map, hcd->regs + or_reg);
+ reg_write32(hcd->regs, or_reg, or_map);
+
+ ptd_write(hcd->regs, reg_base, i, &ptd);
- priv_write_copy(priv, (u32 *)&ptd, hcd->regs + reg_base
- + i * sizeof(ptd), sizeof(ptd));
- qtd = ints->qtd;
- qtd = clean_up_qtdlist(qtd);
+ qtd = ints[i].qtd;
+ qh = ints[i].qh;
- free_mem(priv, ints->payload);
+ free_mem(hcd, qtd);
+ qtd = clean_up_qtdlist(qtd, qh);
- ints->urb = NULL;
- ints->qh = NULL;
- ints->qtd = NULL;
- ints->data_buffer = NULL;
- ints->payload = 0;
+ ints[i].qh = NULL;
+ ints[i].qtd = NULL;
- isp1760_urb_done(priv, urb, status);
+ isp1760_urb_done(hcd, urb);
if (qtd)
pe(hcd, qh, qtd);
break;
- } else if (ints->qtd) {
- struct isp1760_qtd *qtd, *prev_qtd = ints->qtd;
+ } else {
+ struct isp1760_qtd *qtd;
- for (qtd = ints->qtd->hw_next; qtd; qtd = qtd->hw_next) {
+ list_for_each_entry(qtd, &ints[i].qtd->qtd_list,
+ qtd_list) {
if (qtd->urb == urb) {
- prev_qtd->hw_next = clean_up_qtdlist(qtd);
- isp1760_urb_done(priv, urb, status);
+ clean_up_qtdlist(qtd, ints[i].qh);
+ isp1760_urb_done(hcd, urb);
+ qtd = NULL;
break;
}
- prev_qtd = qtd;
}
- /* we found the urb before the end of the list */
- if (qtd)
+
+ /* We found the urb before the last slot */
+ if (!qtd)
break;
}
- ints++;
}
spin_unlock_irqrestore(&priv->lock, flags);
return 0;
}
-static irqreturn_t isp1760_irq(struct usb_hcd *usb_hcd)
+static irqreturn_t isp1760_irq(struct usb_hcd *hcd)
{
- struct isp1760_hcd *priv = hcd_to_priv(usb_hcd);
+ struct isp1760_hcd *priv = hcd_to_priv(hcd);
u32 imask;
irqreturn_t irqret = IRQ_NONE;
spin_lock(&priv->lock);
- if (!(usb_hcd->state & HC_STATE_RUNNING))
+ if (!(hcd->state & HC_STATE_RUNNING))
goto leave;
- imask = isp1760_readl(usb_hcd->regs + HC_INTERRUPT_REG);
+ imask = reg_read32(hcd->regs, HC_INTERRUPT_REG);
if (unlikely(!imask))
goto leave;
- isp1760_writel(imask, usb_hcd->regs + HC_INTERRUPT_REG);
- if (imask & HC_ATL_INT)
- do_atl_int(usb_hcd);
+ reg_write32(hcd->regs, HC_INTERRUPT_REG, imask);
+ if (imask & (HC_ATL_INT | HC_SOT_INT))
+ do_atl_int(hcd);
if (imask & HC_INTL_INT)
- do_intl_int(usb_hcd);
+ do_intl_int(hcd);
irqret = IRQ_HANDLED;
leave:
@@ -1799,12 +1706,12 @@ static int isp1760_hub_status_data(struct usb_hcd *hcd, char *buf)
mask = PORT_CSC;
spin_lock_irqsave(&priv->lock, flags);
- temp = isp1760_readl(hcd->regs + HC_PORTSC1);
+ temp = reg_read32(hcd->regs, HC_PORTSC1);
if (temp & PORT_OWNER) {
if (temp & PORT_CSC) {
temp &= ~PORT_CSC;
- isp1760_writel(temp, hcd->regs + HC_PORTSC1);
+ reg_write32(hcd->regs, HC_PORTSC1, temp);
goto done;
}
}
@@ -1844,9 +1751,9 @@ static void isp1760_hub_descriptor(struct isp1760_hcd *priv,
temp = 1 + (ports / 8);
desc->bDescLength = 7 + 2 * temp;
- /* two bitmaps: ports removable, and usb 1.0 legacy PortPwrCtrlMask */
- memset(&desc->bitmap[0], 0, temp);
- memset(&desc->bitmap[temp], 0xff, temp);
+ /* ports removable, and usb 1.0 legacy PortPwrCtrlMask */
+ memset(&desc->u.hs.DeviceRemovable[0], 0, temp);
+ memset(&desc->u.hs.DeviceRemovable[temp], 0xff, temp);
/* per-port overcurrent reporting */
temp = 0x0008;
@@ -1861,8 +1768,8 @@ static void isp1760_hub_descriptor(struct isp1760_hcd *priv,
#define PORT_WAKE_BITS (PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E)
-static int check_reset_complete(struct isp1760_hcd *priv, int index,
- u32 __iomem *status_reg, int port_status)
+static int check_reset_complete(struct usb_hcd *hcd, int index,
+ int port_status)
{
if (!(port_status & PORT_CONNECT))
return port_status;
@@ -1870,15 +1777,17 @@ static int check_reset_complete(struct isp1760_hcd *priv, int index,
/* if reset finished and it's still not enabled -- handoff */
if (!(port_status & PORT_PE)) {
- printk(KERN_ERR "port %d full speed --> companion\n",
- index + 1);
+ dev_err(hcd->self.controller,
+ "port %d full speed --> companion\n",
+ index + 1);
port_status |= PORT_OWNER;
port_status &= ~PORT_RWC_BITS;
- isp1760_writel(port_status, status_reg);
+ reg_write32(hcd->regs, HC_PORTSC1, port_status);
} else
- printk(KERN_ERR "port %d high speed\n", index + 1);
+ dev_err(hcd->self.controller, "port %d high speed\n",
+ index + 1);
return port_status;
}
@@ -1888,7 +1797,6 @@ static int isp1760_hub_control(struct usb_hcd *hcd, u16 typeReq,
{
struct isp1760_hcd *priv = hcd_to_priv(hcd);
int ports = HCS_N_PORTS(priv->hcs_params);
- u32 __iomem *status_reg = hcd->regs + HC_PORTSC1;
u32 temp, status;
unsigned long flags;
int retval = 0;
@@ -1917,7 +1825,7 @@ static int isp1760_hub_control(struct usb_hcd *hcd, u16 typeReq,
if (!wIndex || wIndex > ports)
goto error;
wIndex--;
- temp = isp1760_readl(status_reg);
+ temp = reg_read32(hcd->regs, HC_PORTSC1);
/*
* Even if OWNER is set, so the port is owned by the
@@ -1928,7 +1836,7 @@ static int isp1760_hub_control(struct usb_hcd *hcd, u16 typeReq,
switch (wValue) {
case USB_PORT_FEAT_ENABLE:
- isp1760_writel(temp & ~PORT_PE, status_reg);
+ reg_write32(hcd->regs, HC_PORTSC1, temp & ~PORT_PE);
break;
case USB_PORT_FEAT_C_ENABLE:
/* XXX error? */
@@ -1942,8 +1850,8 @@ static int isp1760_hub_control(struct usb_hcd *hcd, u16 typeReq,
goto error;
/* resume signaling for 20 msec */
temp &= ~(PORT_RWC_BITS);
- isp1760_writel(temp | PORT_RESUME,
- status_reg);
+ reg_write32(hcd->regs, HC_PORTSC1,
+ temp | PORT_RESUME);
priv->reset_done = jiffies +
msecs_to_jiffies(20);
}
@@ -1953,11 +1861,11 @@ static int isp1760_hub_control(struct usb_hcd *hcd, u16 typeReq,
break;
case USB_PORT_FEAT_POWER:
if (HCS_PPC(priv->hcs_params))
- isp1760_writel(temp & ~PORT_POWER, status_reg);
+ reg_write32(hcd->regs, HC_PORTSC1,
+ temp & ~PORT_POWER);
break;
case USB_PORT_FEAT_C_CONNECTION:
- isp1760_writel(temp | PORT_CSC,
- status_reg);
+ reg_write32(hcd->regs, HC_PORTSC1, temp | PORT_CSC);
break;
case USB_PORT_FEAT_C_OVER_CURRENT:
/* XXX error ?*/
@@ -1968,7 +1876,7 @@ static int isp1760_hub_control(struct usb_hcd *hcd, u16 typeReq,
default:
goto error;
}
- isp1760_readl(hcd->regs + HC_USBCMD);
+ reg_read32(hcd->regs, HC_USBCMD);
break;
case GetHubDescriptor:
isp1760_hub_descriptor(priv, (struct usb_hub_descriptor *)
@@ -1983,7 +1891,7 @@ static int isp1760_hub_control(struct usb_hcd *hcd, u16 typeReq,
goto error;
wIndex--;
status = 0;
- temp = isp1760_readl(status_reg);
+ temp = reg_read32(hcd->regs, HC_PORTSC1);
/* wPortChange bits */
if (temp & PORT_CSC)
@@ -1992,7 +1900,7 @@ static int isp1760_hub_control(struct usb_hcd *hcd, u16 typeReq,
/* whoever resumes must GetPortStatus to complete it!! */
if (temp & PORT_RESUME) {
- printk(KERN_ERR "Port resume should be skipped.\n");
+ dev_err(hcd->self.controller, "Port resume should be skipped.\n");
/* Remote Wakeup received? */
if (!priv->reset_done) {
@@ -2000,8 +1908,7 @@ static int isp1760_hub_control(struct usb_hcd *hcd, u16 typeReq,
priv->reset_done = jiffies
+ msecs_to_jiffies(20);
/* check the port again */
- mod_timer(&priv_to_hcd(priv)->rh_timer,
- priv->reset_done);
+ mod_timer(&hcd->rh_timer, priv->reset_done);
}
/* resume completed? */
@@ -2011,14 +1918,13 @@ static int isp1760_hub_control(struct usb_hcd *hcd, u16 typeReq,
priv->reset_done = 0;
/* stop resume signaling */
- temp = isp1760_readl(status_reg);
- isp1760_writel(
- temp & ~(PORT_RWC_BITS | PORT_RESUME),
- status_reg);
- retval = handshake(priv, status_reg,
+ temp = reg_read32(hcd->regs, HC_PORTSC1);
+ reg_write32(hcd->regs, HC_PORTSC1,
+ temp & ~(PORT_RWC_BITS | PORT_RESUME));
+ retval = handshake(hcd, HC_PORTSC1,
PORT_RESUME, 0, 2000 /* 2msec */);
if (retval != 0) {
- isp1760_err(priv,
+ dev_err(hcd->self.controller,
"port %d resume error %d\n",
wIndex + 1, retval);
goto error;
@@ -2035,22 +1941,21 @@ static int isp1760_hub_control(struct usb_hcd *hcd, u16 typeReq,
priv->reset_done = 0;
/* force reset to complete */
- isp1760_writel(temp & ~PORT_RESET,
- status_reg);
+ reg_write32(hcd->regs, HC_PORTSC1, temp & ~PORT_RESET);
/* REVISIT: some hardware needs 550+ usec to clear
* this bit; seems too long to spin routinely...
*/
- retval = handshake(priv, status_reg,
+ retval = handshake(hcd, HC_PORTSC1,
PORT_RESET, 0, 750);
if (retval != 0) {
- isp1760_err(priv, "port %d reset error %d\n",
+ dev_err(hcd->self.controller, "port %d reset error %d\n",
wIndex + 1, retval);
goto error;
}
/* see what we found out */
- temp = check_reset_complete(priv, wIndex, status_reg,
- isp1760_readl(status_reg));
+ temp = check_reset_complete(hcd, wIndex,
+ reg_read32(hcd->regs, HC_PORTSC1));
}
/*
* Even if OWNER is set, there's no harm letting khubd
@@ -2059,12 +1964,12 @@ static int isp1760_hub_control(struct usb_hcd *hcd, u16 typeReq,
*/
if (temp & PORT_OWNER)
- printk(KERN_ERR "Warning: PORT_OWNER is set\n");
+ dev_err(hcd->self.controller, "PORT_OWNER is set\n");
if (temp & PORT_CONNECT) {
status |= USB_PORT_STAT_CONNECTION;
/* status may be from integrated TT */
- status |= ehci_port_speed(priv, temp);
+ status |= USB_PORT_STAT_HIGH_SPEED;
}
if (temp & PORT_PE)
status |= USB_PORT_STAT_ENABLE;
@@ -2093,14 +1998,14 @@ static int isp1760_hub_control(struct usb_hcd *hcd, u16 typeReq,
if (!wIndex || wIndex > ports)
goto error;
wIndex--;
- temp = isp1760_readl(status_reg);
+ temp = reg_read32(hcd->regs, HC_PORTSC1);
if (temp & PORT_OWNER)
break;
/* temp &= ~PORT_RWC_BITS; */
switch (wValue) {
case USB_PORT_FEAT_ENABLE:
- isp1760_writel(temp | PORT_PE, status_reg);
+ reg_write32(hcd->regs, HC_PORTSC1, temp | PORT_PE);
break;
case USB_PORT_FEAT_SUSPEND:
@@ -2108,12 +2013,12 @@ static int isp1760_hub_control(struct usb_hcd *hcd, u16 typeReq,
|| (temp & PORT_RESET) != 0)
goto error;
- isp1760_writel(temp | PORT_SUSPEND, status_reg);
+ reg_write32(hcd->regs, HC_PORTSC1, temp | PORT_SUSPEND);
break;
case USB_PORT_FEAT_POWER:
if (HCS_PPC(priv->hcs_params))
- isp1760_writel(temp | PORT_POWER,
- status_reg);
+ reg_write32(hcd->regs, HC_PORTSC1,
+ temp | PORT_POWER);
break;
case USB_PORT_FEAT_RESET:
if (temp & PORT_RESUME)
@@ -2136,12 +2041,12 @@ static int isp1760_hub_control(struct usb_hcd *hcd, u16 typeReq,
priv->reset_done = jiffies +
msecs_to_jiffies(50);
}
- isp1760_writel(temp, status_reg);
+ reg_write32(hcd->regs, HC_PORTSC1, temp);
break;
default:
goto error;
}
- isp1760_readl(hcd->regs + HC_USBCMD);
+ reg_read32(hcd->regs, HC_USBCMD);
break;
default:
@@ -2153,10 +2058,10 @@ error:
return retval;
}
-static void isp1760_endpoint_disable(struct usb_hcd *usb_hcd,
+static void isp1760_endpoint_disable(struct usb_hcd *hcd,
struct usb_host_endpoint *ep)
{
- struct isp1760_hcd *priv = hcd_to_priv(usb_hcd);
+ struct isp1760_hcd *priv = hcd_to_priv(hcd);
struct isp1760_qh *qh;
struct isp1760_qtd *qtd;
unsigned long flags;
@@ -2176,16 +2081,16 @@ static void isp1760_endpoint_disable(struct usb_hcd *usb_hcd,
qtd_list);
if (qtd->status & URB_ENQUEUED) {
-
spin_unlock_irqrestore(&priv->lock, flags);
- isp1760_urb_dequeue(usb_hcd, qtd->urb, -ECONNRESET);
+ isp1760_urb_dequeue(hcd, qtd->urb, -ECONNRESET);
spin_lock_irqsave(&priv->lock, flags);
} else {
struct urb *urb;
urb = qtd->urb;
- clean_up_qtdlist(qtd);
- isp1760_urb_done(priv, urb, -ECONNRESET);
+ clean_up_qtdlist(qtd, qh);
+ urb->status = -ECONNRESET;
+ isp1760_urb_done(hcd, urb);
}
} while (1);
@@ -2203,7 +2108,7 @@ static int isp1760_get_frame(struct usb_hcd *hcd)
struct isp1760_hcd *priv = hcd_to_priv(hcd);
u32 fr;
- fr = isp1760_readl(hcd->regs + HC_FRINDEX);
+ fr = reg_read32(hcd->regs, HC_FRINDEX);
return (fr >> 3) % priv->periodic_size;
}
@@ -2217,13 +2122,13 @@ static void isp1760_stop(struct usb_hcd *hcd)
mdelay(20);
spin_lock_irq(&priv->lock);
- ehci_reset(priv);
+ ehci_reset(hcd);
/* Disable IRQ */
- temp = isp1760_readl(hcd->regs + HC_HW_MODE_CTRL);
- isp1760_writel(temp &= ~HW_GLOBAL_INTR_EN, hcd->regs + HC_HW_MODE_CTRL);
+ temp = reg_read32(hcd->regs, HC_HW_MODE_CTRL);
+ reg_write32(hcd->regs, HC_HW_MODE_CTRL, temp &= ~HW_GLOBAL_INTR_EN);
spin_unlock_irq(&priv->lock);
- isp1760_writel(0, hcd->regs + HC_CONFIGFLAG);
+ reg_write32(hcd->regs, HC_CONFIGFLAG, 0);
}
static void isp1760_shutdown(struct usb_hcd *hcd)
@@ -2231,12 +2136,12 @@ static void isp1760_shutdown(struct usb_hcd *hcd)
u32 command, temp;
isp1760_stop(hcd);
- temp = isp1760_readl(hcd->regs + HC_HW_MODE_CTRL);
- isp1760_writel(temp &= ~HW_GLOBAL_INTR_EN, hcd->regs + HC_HW_MODE_CTRL);
+ temp = reg_read32(hcd->regs, HC_HW_MODE_CTRL);
+ reg_write32(hcd->regs, HC_HW_MODE_CTRL, temp &= ~HW_GLOBAL_INTR_EN);
- command = isp1760_readl(hcd->regs + HC_USBCMD);
+ command = reg_read32(hcd->regs, HC_USBCMD);
command &= ~CMD_RUN;
- isp1760_writel(command, hcd->regs + HC_USBCMD);
+ reg_write32(hcd->regs, HC_USBCMD, command);
}
static const struct hc_driver isp1760_hc_driver = {
diff --git a/drivers/usb/host/isp1760-hcd.h b/drivers/usb/host/isp1760-hcd.h
index 6931ef5c9650..870507690607 100644
--- a/drivers/usb/host/isp1760-hcd.h
+++ b/drivers/usb/host/isp1760-hcd.h
@@ -69,6 +69,7 @@ void deinit_kmem_cache(void);
#define HC_INTERRUPT_ENABLE 0x314
#define INTERRUPT_ENABLE_MASK (HC_INTL_INT | HC_ATL_INT | HC_EOT_INT)
+#define INTERRUPT_ENABLE_SOT_MASK (HC_INTL_INT | HC_SOT_INT | HC_EOT_INT)
#define HC_ISO_INT (1 << 9)
#define HC_ATL_INT (1 << 8)
@@ -83,37 +84,29 @@ void deinit_kmem_cache(void);
#define HC_INT_IRQ_MASK_AND_REG 0x328
#define HC_ATL_IRQ_MASK_AND_REG 0x32C
-/* Register sets */
-#define HC_BEGIN_OF_ATL 0x0c00
-#define HC_BEGIN_OF_INT 0x0800
-#define HC_BEGIN_OF_ISO 0x0400
-#define HC_BEGIN_OF_PAYLOAD 0x1000
-
/* urb state*/
#define DELETE_URB (0x0008)
#define NO_TRANSFER_ACTIVE (0xffffffff)
-#define ATL_REGS_OFFSET (0xc00)
-#define INT_REGS_OFFSET (0x800)
-
-/* Philips Transfer Descriptor (PTD) */
+/* Philips Proprietary Transfer Descriptor (PTD) */
+typedef __u32 __bitwise __dw;
struct ptd {
- __le32 dw0;
- __le32 dw1;
- __le32 dw2;
- __le32 dw3;
- __le32 dw4;
- __le32 dw5;
- __le32 dw6;
- __le32 dw7;
+ __dw dw0;
+ __dw dw1;
+ __dw dw2;
+ __dw dw3;
+ __dw dw4;
+ __dw dw5;
+ __dw dw6;
+ __dw dw7;
};
+#define PTD_OFFSET 0x0400
+#define ISO_PTD_OFFSET 0x0400
+#define INT_PTD_OFFSET 0x0800
+#define ATL_PTD_OFFSET 0x0c00
+#define PAYLOAD_OFFSET 0x1000
struct inter_packet_info {
- void *data_buffer;
- u32 payload;
-#define PTD_FIRE_NEXT (1 << 0)
-#define PTD_URB_FINISHED (1 << 1)
- struct urb *urb;
struct isp1760_qh *qh;
struct isp1760_qtd *qtd;
};
@@ -122,15 +115,6 @@ struct inter_packet_info {
typedef void (packet_enqueue)(struct usb_hcd *hcd, struct isp1760_qh *qh,
struct isp1760_qtd *qtd);
-#define isp1760_dbg(priv, fmt, args...) \
- dev_dbg(priv_to_hcd(priv)->self.controller, fmt, ##args)
-
-#define isp1760_info(priv, fmt, args...) \
- dev_info(priv_to_hcd(priv)->self.controller, fmt, ##args)
-
-#define isp1760_err(priv, fmt, args...) \
- dev_err(priv_to_hcd(priv)->self.controller, fmt, ##args)
-
/*
* Device flags that can vary from board to board. All of these
* indicate the most "atypical" case, so that a devflags of 0 is
@@ -167,10 +151,8 @@ struct memory_chunk {
#define BLOCK_2_SIZE 1024
#define BLOCK_3_SIZE 8192
#define BLOCKS (BLOCK_1_NUM + BLOCK_2_NUM + BLOCK_3_NUM)
-#define PAYLOAD_SIZE 0xf000
-
-/* I saw if some reloads if the pointer was negative */
-#define ISP1760_NULL_POINTER (0x400)
+#define MAX_PAYLOAD_SIZE BLOCK_3_SIZE
+#define PAYLOAD_AREA_SIZE 0xf000
/* ATL */
/* DW0 */
@@ -224,6 +206,4 @@ struct memory_chunk {
#define NAK_COUNTER (0)
#define ERR_COUNTER (2)
-#define HC_ATL_PL_SIZE (8192)
-
#endif
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index 759a12ff8048..fb035751e4b2 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -75,6 +75,7 @@ static const char hcd_name [] = "ohci_hcd";
#define STATECHANGE_DELAY msecs_to_jiffies(300)
#include "ohci.h"
+#include "pci-quirks.h"
static void ohci_dump (struct ohci_hcd *ohci, int verbose);
static int ohci_init (struct ohci_hcd *ohci);
@@ -85,18 +86,8 @@ static int ohci_restart (struct ohci_hcd *ohci);
#endif
#ifdef CONFIG_PCI
-static void quirk_amd_pll(int state);
-static void amd_iso_dev_put(void);
static void sb800_prefetch(struct ohci_hcd *ohci, int on);
#else
-static inline void quirk_amd_pll(int state)
-{
- return;
-}
-static inline void amd_iso_dev_put(void)
-{
- return;
-}
static inline void sb800_prefetch(struct ohci_hcd *ohci, int on)
{
return;
@@ -912,7 +903,7 @@ static void ohci_stop (struct usb_hcd *hcd)
if (quirk_zfmicro(ohci))
del_timer(&ohci->unlink_watchdog);
if (quirk_amdiso(ohci))
- amd_iso_dev_put();
+ usb_amd_dev_put();
remove_debug_files (ohci);
ohci_mem_cleanup (ohci);
@@ -1068,10 +1059,7 @@ MODULE_LICENSE ("GPL");
#define PLATFORM_DRIVER ohci_hcd_da8xx_driver
#endif
-#if defined(CONFIG_CPU_SUBTYPE_SH7720) || \
- defined(CONFIG_CPU_SUBTYPE_SH7721) || \
- defined(CONFIG_CPU_SUBTYPE_SH7763) || \
- defined(CONFIG_CPU_SUBTYPE_SH7786)
+#ifdef CONFIG_USB_OHCI_SH
#include "ohci-sh.c"
#define PLATFORM_DRIVER ohci_hcd_sh_driver
#endif
diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c
index cddcda95b579..9154615292db 100644
--- a/drivers/usb/host/ohci-hub.c
+++ b/drivers/usb/host/ohci-hub.c
@@ -580,15 +580,16 @@ ohci_hub_descriptor (
temp |= 0x0008;
desc->wHubCharacteristics = (__force __u16)cpu_to_hc16(ohci, temp);
- /* two bitmaps: ports removable, and usb 1.0 legacy PortPwrCtrlMask */
+ /* ports removable, and usb 1.0 legacy PortPwrCtrlMask */
rh = roothub_b (ohci);
- memset(desc->bitmap, 0xff, sizeof(desc->bitmap));
- desc->bitmap [0] = rh & RH_B_DR;
+ memset(desc->u.hs.DeviceRemovable, 0xff,
+ sizeof(desc->u.hs.DeviceRemovable));
+ desc->u.hs.DeviceRemovable[0] = rh & RH_B_DR;
if (ohci->num_ports > 7) {
- desc->bitmap [1] = (rh & RH_B_DR) >> 8;
- desc->bitmap [2] = 0xff;
+ desc->u.hs.DeviceRemovable[1] = (rh & RH_B_DR) >> 8;
+ desc->u.hs.DeviceRemovable[2] = 0xff;
} else
- desc->bitmap [1] = 0xff;
+ desc->u.hs.DeviceRemovable[1] = 0xff;
}
/*-------------------------------------------------------------------------*/
diff --git a/drivers/usb/host/ohci-omap3.c b/drivers/usb/host/ohci-omap3.c
index a37d5993e4e3..6048f2f64f73 100644
--- a/drivers/usb/host/ohci-omap3.c
+++ b/drivers/usb/host/ohci-omap3.c
@@ -7,6 +7,7 @@
* Copyright (C) 2007-2010 Texas Instruments, Inc.
* Author: Vikram Pandita <vikram.pandita@ti.com>
* Author: Anand Gadiyar <gadiyar@ti.com>
+ * Author: Keshava Munegowda <keshava_mgowda@ti.com>
*
* Based on ehci-omap.c and some other ohci glue layers
*
@@ -24,150 +25,15 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
- * TODO (last updated Mar 10th, 2010):
+ * TODO (last updated Feb 27, 2011):
* - add kernel-doc
- * - Factor out code common to EHCI to a separate file
- * - Make EHCI and OHCI coexist together
- * - needs newer silicon versions to actually work
- * - the last one to be loaded currently steps on the other's toes
- * - Add hooks for configuring transceivers, etc. at init/exit
- * - Add aggressive clock-management code
*/
#include <linux/platform_device.h>
-#include <linux/clk.h>
-
#include <plat/usb.h>
-/*
- * OMAP USBHOST Register addresses: VIRTUAL ADDRESSES
- * Use ohci_omap_readl()/ohci_omap_writel() functions
- */
-
-/* TLL Register Set */
-#define OMAP_USBTLL_REVISION (0x00)
-#define OMAP_USBTLL_SYSCONFIG (0x10)
-#define OMAP_USBTLL_SYSCONFIG_CACTIVITY (1 << 8)
-#define OMAP_USBTLL_SYSCONFIG_SIDLEMODE (1 << 3)
-#define OMAP_USBTLL_SYSCONFIG_ENAWAKEUP (1 << 2)
-#define OMAP_USBTLL_SYSCONFIG_SOFTRESET (1 << 1)
-#define OMAP_USBTLL_SYSCONFIG_AUTOIDLE (1 << 0)
-
-#define OMAP_USBTLL_SYSSTATUS (0x14)
-#define OMAP_USBTLL_SYSSTATUS_RESETDONE (1 << 0)
-
-#define OMAP_USBTLL_IRQSTATUS (0x18)
-#define OMAP_USBTLL_IRQENABLE (0x1C)
-
-#define OMAP_TLL_SHARED_CONF (0x30)
-#define OMAP_TLL_SHARED_CONF_USB_90D_DDR_EN (1 << 6)
-#define OMAP_TLL_SHARED_CONF_USB_180D_SDR_EN (1 << 5)
-#define OMAP_TLL_SHARED_CONF_USB_DIVRATION (1 << 2)
-#define OMAP_TLL_SHARED_CONF_FCLK_REQ (1 << 1)
-#define OMAP_TLL_SHARED_CONF_FCLK_IS_ON (1 << 0)
-
-#define OMAP_TLL_CHANNEL_CONF(num) (0x040 + 0x004 * num)
-#define OMAP_TLL_CHANNEL_CONF_FSLSMODE_SHIFT 24
-#define OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF (1 << 11)
-#define OMAP_TLL_CHANNEL_CONF_ULPI_ULPIAUTOIDLE (1 << 10)
-#define OMAP_TLL_CHANNEL_CONF_UTMIAUTOIDLE (1 << 9)
-#define OMAP_TLL_CHANNEL_CONF_ULPIDDRMODE (1 << 8)
-#define OMAP_TLL_CHANNEL_CONF_CHANMODE_FSLS (1 << 1)
-#define OMAP_TLL_CHANNEL_CONF_CHANEN (1 << 0)
-
-#define OMAP_TLL_CHANNEL_COUNT 3
-
-/* UHH Register Set */
-#define OMAP_UHH_REVISION (0x00)
-#define OMAP_UHH_SYSCONFIG (0x10)
-#define OMAP_UHH_SYSCONFIG_MIDLEMODE (1 << 12)
-#define OMAP_UHH_SYSCONFIG_CACTIVITY (1 << 8)
-#define OMAP_UHH_SYSCONFIG_SIDLEMODE (1 << 3)
-#define OMAP_UHH_SYSCONFIG_ENAWAKEUP (1 << 2)
-#define OMAP_UHH_SYSCONFIG_SOFTRESET (1 << 1)
-#define OMAP_UHH_SYSCONFIG_AUTOIDLE (1 << 0)
-
-#define OMAP_UHH_SYSSTATUS (0x14)
-#define OMAP_UHH_SYSSTATUS_UHHRESETDONE (1 << 0)
-#define OMAP_UHH_SYSSTATUS_OHCIRESETDONE (1 << 1)
-#define OMAP_UHH_SYSSTATUS_EHCIRESETDONE (1 << 2)
-#define OMAP_UHH_HOSTCONFIG (0x40)
-#define OMAP_UHH_HOSTCONFIG_ULPI_BYPASS (1 << 0)
-#define OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS (1 << 0)
-#define OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS (1 << 11)
-#define OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS (1 << 12)
-#define OMAP_UHH_HOSTCONFIG_INCR4_BURST_EN (1 << 2)
-#define OMAP_UHH_HOSTCONFIG_INCR8_BURST_EN (1 << 3)
-#define OMAP_UHH_HOSTCONFIG_INCR16_BURST_EN (1 << 4)
-#define OMAP_UHH_HOSTCONFIG_INCRX_ALIGN_EN (1 << 5)
-#define OMAP_UHH_HOSTCONFIG_P1_CONNECT_STATUS (1 << 8)
-#define OMAP_UHH_HOSTCONFIG_P2_CONNECT_STATUS (1 << 9)
-#define OMAP_UHH_HOSTCONFIG_P3_CONNECT_STATUS (1 << 10)
-
-#define OMAP_UHH_DEBUG_CSR (0x44)
-
/*-------------------------------------------------------------------------*/
-static inline void ohci_omap_writel(void __iomem *base, u32 reg, u32 val)
-{
- __raw_writel(val, base + reg);
-}
-
-static inline u32 ohci_omap_readl(void __iomem *base, u32 reg)
-{
- return __raw_readl(base + reg);
-}
-
-static inline void ohci_omap_writeb(void __iomem *base, u8 reg, u8 val)
-{
- __raw_writeb(val, base + reg);
-}
-
-static inline u8 ohci_omap_readb(void __iomem *base, u8 reg)
-{
- return __raw_readb(base + reg);
-}
-
-/*-------------------------------------------------------------------------*/
-
-struct ohci_hcd_omap3 {
- struct ohci_hcd *ohci;
- struct device *dev;
-
- struct clk *usbhost_ick;
- struct clk *usbhost2_120m_fck;
- struct clk *usbhost1_48m_fck;
- struct clk *usbtll_fck;
- struct clk *usbtll_ick;
-
- /* port_mode: TLL/PHY, 2/3/4/6-PIN, DP-DM/DAT-SE0 */
- enum ohci_omap3_port_mode port_mode[OMAP3_HS_USB_PORTS];
- void __iomem *uhh_base;
- void __iomem *tll_base;
- void __iomem *ohci_base;
-
- unsigned es2_compatibility:1;
-};
-
-/*-------------------------------------------------------------------------*/
-
-static void ohci_omap3_clock_power(struct ohci_hcd_omap3 *omap, int on)
-{
- if (on) {
- clk_enable(omap->usbtll_ick);
- clk_enable(omap->usbtll_fck);
- clk_enable(omap->usbhost_ick);
- clk_enable(omap->usbhost1_48m_fck);
- clk_enable(omap->usbhost2_120m_fck);
- } else {
- clk_disable(omap->usbhost2_120m_fck);
- clk_disable(omap->usbhost1_48m_fck);
- clk_disable(omap->usbhost_ick);
- clk_disable(omap->usbtll_fck);
- clk_disable(omap->usbtll_ick);
- }
-}
-
static int ohci_omap3_init(struct usb_hcd *hcd)
{
dev_dbg(hcd->self.controller, "starting OHCI controller\n");
@@ -175,7 +41,6 @@ static int ohci_omap3_init(struct usb_hcd *hcd)
return ohci_init(hcd_to_ohci(hcd));
}
-
/*-------------------------------------------------------------------------*/
static int ohci_omap3_start(struct usb_hcd *hcd)
@@ -202,325 +67,6 @@ static int ohci_omap3_start(struct usb_hcd *hcd)
/*-------------------------------------------------------------------------*/
-/*
- * convert the port-mode enum to a value we can use in the FSLSMODE
- * field of USBTLL_CHANNEL_CONF
- */
-static unsigned ohci_omap3_fslsmode(enum ohci_omap3_port_mode mode)
-{
- switch (mode) {
- case OMAP_OHCI_PORT_MODE_UNUSED:
- case OMAP_OHCI_PORT_MODE_PHY_6PIN_DATSE0:
- return 0x0;
-
- case OMAP_OHCI_PORT_MODE_PHY_6PIN_DPDM:
- return 0x1;
-
- case OMAP_OHCI_PORT_MODE_PHY_3PIN_DATSE0:
- return 0x2;
-
- case OMAP_OHCI_PORT_MODE_PHY_4PIN_DPDM:
- return 0x3;
-
- case OMAP_OHCI_PORT_MODE_TLL_6PIN_DATSE0:
- return 0x4;
-
- case OMAP_OHCI_PORT_MODE_TLL_6PIN_DPDM:
- return 0x5;
-
- case OMAP_OHCI_PORT_MODE_TLL_3PIN_DATSE0:
- return 0x6;
-
- case OMAP_OHCI_PORT_MODE_TLL_4PIN_DPDM:
- return 0x7;
-
- case OMAP_OHCI_PORT_MODE_TLL_2PIN_DATSE0:
- return 0xA;
-
- case OMAP_OHCI_PORT_MODE_TLL_2PIN_DPDM:
- return 0xB;
- default:
- pr_warning("Invalid port mode, using default\n");
- return 0x0;
- }
-}
-
-static void ohci_omap3_tll_config(struct ohci_hcd_omap3 *omap)
-{
- u32 reg;
- int i;
-
- /* Program TLL SHARED CONF */
- reg = ohci_omap_readl(omap->tll_base, OMAP_TLL_SHARED_CONF);
- reg &= ~OMAP_TLL_SHARED_CONF_USB_90D_DDR_EN;
- reg &= ~OMAP_TLL_SHARED_CONF_USB_180D_SDR_EN;
- reg |= OMAP_TLL_SHARED_CONF_USB_DIVRATION;
- reg |= OMAP_TLL_SHARED_CONF_FCLK_IS_ON;
- ohci_omap_writel(omap->tll_base, OMAP_TLL_SHARED_CONF, reg);
-
- /* Program each TLL channel */
- /*
- * REVISIT: Only the 3-pin and 4-pin PHY modes have
- * actually been tested.
- */
- for (i = 0; i < OMAP_TLL_CHANNEL_COUNT; i++) {
-
- /* Enable only those channels that are actually used */
- if (omap->port_mode[i] == OMAP_OHCI_PORT_MODE_UNUSED)
- continue;
-
- reg = ohci_omap_readl(omap->tll_base, OMAP_TLL_CHANNEL_CONF(i));
- reg |= ohci_omap3_fslsmode(omap->port_mode[i])
- << OMAP_TLL_CHANNEL_CONF_FSLSMODE_SHIFT;
- reg |= OMAP_TLL_CHANNEL_CONF_CHANMODE_FSLS;
- reg |= OMAP_TLL_CHANNEL_CONF_CHANEN;
- ohci_omap_writel(omap->tll_base, OMAP_TLL_CHANNEL_CONF(i), reg);
- }
-}
-
-/* omap3_start_ohci
- * - Start the TI USBHOST controller
- */
-static int omap3_start_ohci(struct ohci_hcd_omap3 *omap, struct usb_hcd *hcd)
-{
- unsigned long timeout = jiffies + msecs_to_jiffies(1000);
- u32 reg = 0;
- int ret = 0;
-
- dev_dbg(omap->dev, "starting TI OHCI USB Controller\n");
-
- /* Get all the clock handles we need */
- omap->usbhost_ick = clk_get(omap->dev, "usbhost_ick");
- if (IS_ERR(omap->usbhost_ick)) {
- dev_err(omap->dev, "could not get usbhost_ick\n");
- ret = PTR_ERR(omap->usbhost_ick);
- goto err_host_ick;
- }
-
- omap->usbhost2_120m_fck = clk_get(omap->dev, "usbhost_120m_fck");
- if (IS_ERR(omap->usbhost2_120m_fck)) {
- dev_err(omap->dev, "could not get usbhost_120m_fck\n");
- ret = PTR_ERR(omap->usbhost2_120m_fck);
- goto err_host_120m_fck;
- }
-
- omap->usbhost1_48m_fck = clk_get(omap->dev, "usbhost_48m_fck");
- if (IS_ERR(omap->usbhost1_48m_fck)) {
- dev_err(omap->dev, "could not get usbhost_48m_fck\n");
- ret = PTR_ERR(omap->usbhost1_48m_fck);
- goto err_host_48m_fck;
- }
-
- omap->usbtll_fck = clk_get(omap->dev, "usbtll_fck");
- if (IS_ERR(omap->usbtll_fck)) {
- dev_err(omap->dev, "could not get usbtll_fck\n");
- ret = PTR_ERR(omap->usbtll_fck);
- goto err_tll_fck;
- }
-
- omap->usbtll_ick = clk_get(omap->dev, "usbtll_ick");
- if (IS_ERR(omap->usbtll_ick)) {
- dev_err(omap->dev, "could not get usbtll_ick\n");
- ret = PTR_ERR(omap->usbtll_ick);
- goto err_tll_ick;
- }
-
- /* Now enable all the clocks in the correct order */
- ohci_omap3_clock_power(omap, 1);
-
- /* perform TLL soft reset, and wait until reset is complete */
- ohci_omap_writel(omap->tll_base, OMAP_USBTLL_SYSCONFIG,
- OMAP_USBTLL_SYSCONFIG_SOFTRESET);
-
- /* Wait for TLL reset to complete */
- while (!(ohci_omap_readl(omap->tll_base, OMAP_USBTLL_SYSSTATUS)
- & OMAP_USBTLL_SYSSTATUS_RESETDONE)) {
- cpu_relax();
-
- if (time_after(jiffies, timeout)) {
- dev_dbg(omap->dev, "operation timed out\n");
- ret = -EINVAL;
- goto err_sys_status;
- }
- }
-
- dev_dbg(omap->dev, "TLL reset done\n");
-
- /* (1<<3) = no idle mode only for initial debugging */
- ohci_omap_writel(omap->tll_base, OMAP_USBTLL_SYSCONFIG,
- OMAP_USBTLL_SYSCONFIG_ENAWAKEUP |
- OMAP_USBTLL_SYSCONFIG_SIDLEMODE |
- OMAP_USBTLL_SYSCONFIG_CACTIVITY);
-
-
- /* Put UHH in NoIdle/NoStandby mode */
- reg = ohci_omap_readl(omap->uhh_base, OMAP_UHH_SYSCONFIG);
- reg |= (OMAP_UHH_SYSCONFIG_ENAWAKEUP
- | OMAP_UHH_SYSCONFIG_SIDLEMODE
- | OMAP_UHH_SYSCONFIG_CACTIVITY
- | OMAP_UHH_SYSCONFIG_MIDLEMODE);
- reg &= ~OMAP_UHH_SYSCONFIG_AUTOIDLE;
- reg &= ~OMAP_UHH_SYSCONFIG_SOFTRESET;
-
- ohci_omap_writel(omap->uhh_base, OMAP_UHH_SYSCONFIG, reg);
-
- reg = ohci_omap_readl(omap->uhh_base, OMAP_UHH_HOSTCONFIG);
-
- /* setup ULPI bypass and burst configurations */
- reg |= (OMAP_UHH_HOSTCONFIG_INCR4_BURST_EN
- | OMAP_UHH_HOSTCONFIG_INCR8_BURST_EN
- | OMAP_UHH_HOSTCONFIG_INCR16_BURST_EN);
- reg &= ~OMAP_UHH_HOSTCONFIG_INCRX_ALIGN_EN;
-
- /*
- * REVISIT: Pi_CONNECT_STATUS controls MStandby
- * assertion and Swakeup generation - let us not
- * worry about this for now. OMAP HWMOD framework
- * might take care of this later. If not, we can
- * update these registers when adding aggressive
- * clock management code.
- *
- * For now, turn off all the Pi_CONNECT_STATUS bits
- *
- if (omap->port_mode[0] == OMAP_OHCI_PORT_MODE_UNUSED)
- reg &= ~OMAP_UHH_HOSTCONFIG_P1_CONNECT_STATUS;
- if (omap->port_mode[1] == OMAP_OHCI_PORT_MODE_UNUSED)
- reg &= ~OMAP_UHH_HOSTCONFIG_P2_CONNECT_STATUS;
- if (omap->port_mode[2] == OMAP_OHCI_PORT_MODE_UNUSED)
- reg &= ~OMAP_UHH_HOSTCONFIG_P3_CONNECT_STATUS;
- */
- reg &= ~OMAP_UHH_HOSTCONFIG_P1_CONNECT_STATUS;
- reg &= ~OMAP_UHH_HOSTCONFIG_P2_CONNECT_STATUS;
- reg &= ~OMAP_UHH_HOSTCONFIG_P3_CONNECT_STATUS;
-
- if (omap->es2_compatibility) {
- /*
- * All OHCI modes need to go through the TLL,
- * unlike in the EHCI case. So use UTMI mode
- * for all ports for OHCI, on ES2.x silicon
- */
- dev_dbg(omap->dev, "OMAP3 ES version <= ES2.1\n");
- reg |= OMAP_UHH_HOSTCONFIG_ULPI_BYPASS;
- } else {
- dev_dbg(omap->dev, "OMAP3 ES version > ES2.1\n");
- if (omap->port_mode[0] == OMAP_OHCI_PORT_MODE_UNUSED)
- reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS;
- else
- reg |= OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS;
-
- if (omap->port_mode[1] == OMAP_OHCI_PORT_MODE_UNUSED)
- reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS;
- else
- reg |= OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS;
-
- if (omap->port_mode[2] == OMAP_OHCI_PORT_MODE_UNUSED)
- reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS;
- else
- reg |= OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS;
-
- }
- ohci_omap_writel(omap->uhh_base, OMAP_UHH_HOSTCONFIG, reg);
- dev_dbg(omap->dev, "UHH setup done, uhh_hostconfig=%x\n", reg);
-
- ohci_omap3_tll_config(omap);
-
- return 0;
-
-err_sys_status:
- ohci_omap3_clock_power(omap, 0);
- clk_put(omap->usbtll_ick);
-
-err_tll_ick:
- clk_put(omap->usbtll_fck);
-
-err_tll_fck:
- clk_put(omap->usbhost1_48m_fck);
-
-err_host_48m_fck:
- clk_put(omap->usbhost2_120m_fck);
-
-err_host_120m_fck:
- clk_put(omap->usbhost_ick);
-
-err_host_ick:
- return ret;
-}
-
-static void omap3_stop_ohci(struct ohci_hcd_omap3 *omap, struct usb_hcd *hcd)
-{
- unsigned long timeout = jiffies + msecs_to_jiffies(100);
-
- dev_dbg(omap->dev, "stopping TI EHCI USB Controller\n");
-
- /* Reset USBHOST for insmod/rmmod to work */
- ohci_omap_writel(omap->uhh_base, OMAP_UHH_SYSCONFIG,
- OMAP_UHH_SYSCONFIG_SOFTRESET);
- while (!(ohci_omap_readl(omap->uhh_base, OMAP_UHH_SYSSTATUS)
- & OMAP_UHH_SYSSTATUS_UHHRESETDONE)) {
- cpu_relax();
-
- if (time_after(jiffies, timeout))
- dev_dbg(omap->dev, "operation timed out\n");
- }
-
- while (!(ohci_omap_readl(omap->uhh_base, OMAP_UHH_SYSSTATUS)
- & OMAP_UHH_SYSSTATUS_OHCIRESETDONE)) {
- cpu_relax();
-
- if (time_after(jiffies, timeout))
- dev_dbg(omap->dev, "operation timed out\n");
- }
-
- while (!(ohci_omap_readl(omap->uhh_base, OMAP_UHH_SYSSTATUS)
- & OMAP_UHH_SYSSTATUS_EHCIRESETDONE)) {
- cpu_relax();
-
- if (time_after(jiffies, timeout))
- dev_dbg(omap->dev, "operation timed out\n");
- }
-
- ohci_omap_writel(omap->tll_base, OMAP_USBTLL_SYSCONFIG, (1 << 1));
-
- while (!(ohci_omap_readl(omap->tll_base, OMAP_USBTLL_SYSSTATUS)
- & (1 << 0))) {
- cpu_relax();
-
- if (time_after(jiffies, timeout))
- dev_dbg(omap->dev, "operation timed out\n");
- }
-
- ohci_omap3_clock_power(omap, 0);
-
- if (omap->usbtll_fck != NULL) {
- clk_put(omap->usbtll_fck);
- omap->usbtll_fck = NULL;
- }
-
- if (omap->usbhost_ick != NULL) {
- clk_put(omap->usbhost_ick);
- omap->usbhost_ick = NULL;
- }
-
- if (omap->usbhost1_48m_fck != NULL) {
- clk_put(omap->usbhost1_48m_fck);
- omap->usbhost1_48m_fck = NULL;
- }
-
- if (omap->usbhost2_120m_fck != NULL) {
- clk_put(omap->usbhost2_120m_fck);
- omap->usbhost2_120m_fck = NULL;
- }
-
- if (omap->usbtll_ick != NULL) {
- clk_put(omap->usbtll_ick);
- omap->usbtll_ick = NULL;
- }
-
- dev_dbg(omap->dev, "Clock to USB host has been disabled\n");
-}
-
-/*-------------------------------------------------------------------------*/
-
static const struct hc_driver ohci_omap3_hc_driver = {
.description = hcd_name,
.product_desc = "OMAP3 OHCI Host Controller",
@@ -580,107 +126,77 @@ static const struct hc_driver ohci_omap3_hc_driver = {
*/
static int __devinit ohci_hcd_omap3_probe(struct platform_device *pdev)
{
- struct ohci_hcd_omap_platform_data *pdata = pdev->dev.platform_data;
- struct ohci_hcd_omap3 *omap;
- struct resource *res;
- struct usb_hcd *hcd;
- int ret = -ENODEV;
- int irq;
+ struct device *dev = &pdev->dev;
+ struct usb_hcd *hcd = NULL;
+ void __iomem *regs = NULL;
+ struct resource *res;
+ int ret = -ENODEV;
+ int irq;
if (usb_disabled())
- goto err_disabled;
+ goto err_end;
- if (!pdata) {
- dev_dbg(&pdev->dev, "missing platform_data\n");
- goto err_pdata;
+ if (!dev->parent) {
+ dev_err(dev, "Missing parent device\n");
+ return -ENODEV;
}
- irq = platform_get_irq(pdev, 0);
+ irq = platform_get_irq_byname(pdev, "ohci-irq");
+ if (irq < 0) {
+ dev_err(dev, "OHCI irq failed\n");
+ return -ENODEV;
+ }
- omap = kzalloc(sizeof(*omap), GFP_KERNEL);
- if (!omap) {
- ret = -ENOMEM;
- goto err_disabled;
+ res = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "ohci");
+ if (!ret) {
+ dev_err(dev, "UHH OHCI get resource failed\n");
+ return -ENOMEM;
}
- hcd = usb_create_hcd(&ohci_omap3_hc_driver, &pdev->dev,
- dev_name(&pdev->dev));
- if (!hcd) {
- ret = -ENOMEM;
- goto err_create_hcd;
+ regs = ioremap(res->start, resource_size(res));
+ if (!regs) {
+ dev_err(dev, "UHH OHCI ioremap failed\n");
+ return -ENOMEM;
}
- platform_set_drvdata(pdev, omap);
- omap->dev = &pdev->dev;
- omap->port_mode[0] = pdata->port_mode[0];
- omap->port_mode[1] = pdata->port_mode[1];
- omap->port_mode[2] = pdata->port_mode[2];
- omap->es2_compatibility = pdata->es2_compatibility;
- omap->ohci = hcd_to_ohci(hcd);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ hcd = usb_create_hcd(&ohci_omap3_hc_driver, dev,
+ dev_name(dev));
+ if (!hcd) {
+ dev_err(dev, "usb_create_hcd failed\n");
+ goto err_io;
+ }
hcd->rsrc_start = res->start;
hcd->rsrc_len = resource_size(res);
+ hcd->regs = regs;
- hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
- if (!hcd->regs) {
- dev_err(&pdev->dev, "OHCI ioremap failed\n");
- ret = -ENOMEM;
- goto err_ioremap;
- }
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- omap->uhh_base = ioremap(res->start, resource_size(res));
- if (!omap->uhh_base) {
- dev_err(&pdev->dev, "UHH ioremap failed\n");
- ret = -ENOMEM;
- goto err_uhh_ioremap;
- }
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
- omap->tll_base = ioremap(res->start, resource_size(res));
- if (!omap->tll_base) {
- dev_err(&pdev->dev, "TLL ioremap failed\n");
- ret = -ENOMEM;
- goto err_tll_ioremap;
- }
-
- ret = omap3_start_ohci(omap, hcd);
+ ret = omap_usbhs_enable(dev);
if (ret) {
- dev_dbg(&pdev->dev, "failed to start ohci\n");
- goto err_start;
+ dev_dbg(dev, "failed to start ohci\n");
+ goto err_end;
}
- ohci_hcd_init(omap->ohci);
+ ohci_hcd_init(hcd_to_ohci(hcd));
ret = usb_add_hcd(hcd, irq, IRQF_DISABLED);
if (ret) {
- dev_dbg(&pdev->dev, "failed to add hcd with err %d\n", ret);
+ dev_dbg(dev, "failed to add hcd with err %d\n", ret);
goto err_add_hcd;
}
return 0;
err_add_hcd:
- omap3_stop_ohci(omap, hcd);
-
-err_start:
- iounmap(omap->tll_base);
-
-err_tll_ioremap:
- iounmap(omap->uhh_base);
-
-err_uhh_ioremap:
- iounmap(hcd->regs);
+ omap_usbhs_disable(dev);
-err_ioremap:
+err_end:
usb_put_hcd(hcd);
-err_create_hcd:
- kfree(omap);
-err_pdata:
-err_disabled:
+err_io:
+ iounmap(regs);
+
return ret;
}
@@ -699,24 +215,20 @@ err_disabled:
*/
static int __devexit ohci_hcd_omap3_remove(struct platform_device *pdev)
{
- struct ohci_hcd_omap3 *omap = platform_get_drvdata(pdev);
- struct usb_hcd *hcd = ohci_to_hcd(omap->ohci);
+ struct device *dev = &pdev->dev;
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
- usb_remove_hcd(hcd);
- omap3_stop_ohci(omap, hcd);
iounmap(hcd->regs);
- iounmap(omap->tll_base);
- iounmap(omap->uhh_base);
+ usb_remove_hcd(hcd);
+ omap_usbhs_disable(dev);
usb_put_hcd(hcd);
- kfree(omap);
return 0;
}
static void ohci_hcd_omap3_shutdown(struct platform_device *pdev)
{
- struct ohci_hcd_omap3 *omap = platform_get_drvdata(pdev);
- struct usb_hcd *hcd = ohci_to_hcd(omap->ohci);
+ struct usb_hcd *hcd = dev_get_drvdata(&pdev->dev);
if (hcd->driver->shutdown)
hcd->driver->shutdown(hcd);
diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c
index 36ee9a666e93..d84d6f0314f9 100644
--- a/drivers/usb/host/ohci-pci.c
+++ b/drivers/usb/host/ohci-pci.c
@@ -22,24 +22,6 @@
#include <linux/io.h>
-/* constants used to work around PM-related transfer
- * glitches in some AMD 700 series southbridges
- */
-#define AB_REG_BAR 0xf0
-#define AB_INDX(addr) ((addr) + 0x00)
-#define AB_DATA(addr) ((addr) + 0x04)
-#define AX_INDXC 0X30
-#define AX_DATAC 0x34
-
-#define NB_PCIE_INDX_ADDR 0xe0
-#define NB_PCIE_INDX_DATA 0xe4
-#define PCIE_P_CNTL 0x10040
-#define BIF_NB 0x10002
-
-static struct pci_dev *amd_smbus_dev;
-static struct pci_dev *amd_hb_dev;
-static int amd_ohci_iso_count;
-
/*-------------------------------------------------------------------------*/
static int broken_suspend(struct usb_hcd *hcd)
@@ -168,15 +150,18 @@ static int ohci_quirk_nec(struct usb_hcd *hcd)
static int ohci_quirk_amd700(struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci(hcd);
- u8 rev = 0;
+ struct pci_dev *amd_smbus_dev;
+ u8 rev;
- if (!amd_smbus_dev)
- amd_smbus_dev = pci_get_device(PCI_VENDOR_ID_ATI,
- PCI_DEVICE_ID_ATI_SBX00_SMBUS, NULL);
+ if (usb_amd_find_chipset_info())
+ ohci->flags |= OHCI_QUIRK_AMD_PLL;
+
+ amd_smbus_dev = pci_get_device(PCI_VENDOR_ID_ATI,
+ PCI_DEVICE_ID_ATI_SBX00_SMBUS, NULL);
if (!amd_smbus_dev)
return 0;
- pci_read_config_byte(amd_smbus_dev, PCI_REVISION_ID, &rev);
+ rev = amd_smbus_dev->revision;
/* SB800 needs pre-fetch fix */
if ((rev >= 0x40) && (rev <= 0x4f)) {
@@ -184,19 +169,8 @@ static int ohci_quirk_amd700(struct usb_hcd *hcd)
ohci_dbg(ohci, "enabled AMD prefetch quirk\n");
}
- if ((rev > 0x3b) || (rev < 0x30)) {
- pci_dev_put(amd_smbus_dev);
- amd_smbus_dev = NULL;
- return 0;
- }
-
- amd_ohci_iso_count++;
-
- if (!amd_hb_dev)
- amd_hb_dev = pci_get_device(PCI_VENDOR_ID_AMD, 0x9600, NULL);
-
- ohci->flags |= OHCI_QUIRK_AMD_ISO;
- ohci_dbg(ohci, "enabled AMD ISO transfers quirk\n");
+ pci_dev_put(amd_smbus_dev);
+ amd_smbus_dev = NULL;
return 0;
}
@@ -215,74 +189,6 @@ static int ohci_quirk_nvidia_shutdown(struct usb_hcd *hcd)
return 0;
}
-/*
- * The hardware normally enables the A-link power management feature, which
- * lets the system lower the power consumption in idle states.
- *
- * Assume the system is configured to have USB 1.1 ISO transfers going
- * to or from a USB device. Without this quirk, that stream may stutter
- * or have breaks occasionally. For transfers going to speakers, this
- * makes a very audible mess...
- *
- * That audio playback corruption is due to the audio stream getting
- * interrupted occasionally when the link goes in lower power state
- * This USB quirk prevents the link going into that lower power state
- * during audio playback or other ISO operations.
- */
-static void quirk_amd_pll(int on)
-{
- u32 addr;
- u32 val;
- u32 bit = (on > 0) ? 1 : 0;
-
- pci_read_config_dword(amd_smbus_dev, AB_REG_BAR, &addr);
-
- /* BIT names/meanings are NDA-protected, sorry ... */
-
- outl(AX_INDXC, AB_INDX(addr));
- outl(0x40, AB_DATA(addr));
- outl(AX_DATAC, AB_INDX(addr));
- val = inl(AB_DATA(addr));
- val &= ~((1 << 3) | (1 << 4) | (1 << 9));
- val |= (bit << 3) | ((!bit) << 4) | ((!bit) << 9);
- outl(val, AB_DATA(addr));
-
- if (amd_hb_dev) {
- addr = PCIE_P_CNTL;
- pci_write_config_dword(amd_hb_dev, NB_PCIE_INDX_ADDR, addr);
-
- pci_read_config_dword(amd_hb_dev, NB_PCIE_INDX_DATA, &val);
- val &= ~(1 | (1 << 3) | (1 << 4) | (1 << 9) | (1 << 12));
- val |= bit | (bit << 3) | (bit << 12);
- val |= ((!bit) << 4) | ((!bit) << 9);
- pci_write_config_dword(amd_hb_dev, NB_PCIE_INDX_DATA, val);
-
- addr = BIF_NB;
- pci_write_config_dword(amd_hb_dev, NB_PCIE_INDX_ADDR, addr);
-
- pci_read_config_dword(amd_hb_dev, NB_PCIE_INDX_DATA, &val);
- val &= ~(1 << 8);
- val |= bit << 8;
- pci_write_config_dword(amd_hb_dev, NB_PCIE_INDX_DATA, val);
- }
-}
-
-static void amd_iso_dev_put(void)
-{
- amd_ohci_iso_count--;
- if (amd_ohci_iso_count == 0) {
- if (amd_smbus_dev) {
- pci_dev_put(amd_smbus_dev);
- amd_smbus_dev = NULL;
- }
- if (amd_hb_dev) {
- pci_dev_put(amd_hb_dev);
- amd_hb_dev = NULL;
- }
- }
-
-}
-
static void sb800_prefetch(struct ohci_hcd *ohci, int on)
{
struct pci_dev *pdev;
diff --git a/drivers/usb/host/ohci-q.c b/drivers/usb/host/ohci-q.c
index 83094d067e0f..dd24fc115e48 100644
--- a/drivers/usb/host/ohci-q.c
+++ b/drivers/usb/host/ohci-q.c
@@ -52,7 +52,7 @@ __acquires(ohci->lock)
ohci_to_hcd(ohci)->self.bandwidth_isoc_reqs--;
if (ohci_to_hcd(ohci)->self.bandwidth_isoc_reqs == 0) {
if (quirk_amdiso(ohci))
- quirk_amd_pll(1);
+ usb_amd_quirk_pll_enable();
if (quirk_amdprefetch(ohci))
sb800_prefetch(ohci, 0);
}
@@ -686,7 +686,7 @@ static void td_submit_urb (
}
if (ohci_to_hcd(ohci)->self.bandwidth_isoc_reqs == 0) {
if (quirk_amdiso(ohci))
- quirk_amd_pll(0);
+ usb_amd_quirk_pll_disable();
if (quirk_amdprefetch(ohci))
sb800_prefetch(ohci, 1);
}
diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h
index 51facb985c84..bad11a72c202 100644
--- a/drivers/usb/host/ohci.h
+++ b/drivers/usb/host/ohci.h
@@ -401,7 +401,7 @@ struct ohci_hcd {
#define OHCI_QUIRK_NEC 0x40 /* lost interrupts */
#define OHCI_QUIRK_FRAME_NO 0x80 /* no big endian frame_no shift */
#define OHCI_QUIRK_HUB_POWER 0x100 /* distrust firmware power/oc setup */
-#define OHCI_QUIRK_AMD_ISO 0x200 /* ISO transfers*/
+#define OHCI_QUIRK_AMD_PLL 0x200 /* AMD PLL quirk*/
#define OHCI_QUIRK_AMD_PREFETCH 0x400 /* pre-fetch for ISO transfer */
#define OHCI_QUIRK_SHUTDOWN 0x800 /* nVidia power bug */
// there are also chip quirks/bugs in init logic
@@ -433,7 +433,7 @@ static inline int quirk_zfmicro(struct ohci_hcd *ohci)
}
static inline int quirk_amdiso(struct ohci_hcd *ohci)
{
- return ohci->flags & OHCI_QUIRK_AMD_ISO;
+ return ohci->flags & OHCI_QUIRK_AMD_PLL;
}
static inline int quirk_amdprefetch(struct ohci_hcd *ohci)
{
diff --git a/drivers/usb/host/oxu210hp-hcd.c b/drivers/usb/host/oxu210hp-hcd.c
index e0cb12b573f9..38193f4e980e 100644
--- a/drivers/usb/host/oxu210hp-hcd.c
+++ b/drivers/usb/host/oxu210hp-hcd.c
@@ -451,9 +451,9 @@ static void ehci_hub_descriptor(struct oxu_hcd *oxu,
temp = 1 + (ports / 8);
desc->bDescLength = 7 + 2 * temp;
- /* two bitmaps: ports removable, and usb 1.0 legacy PortPwrCtrlMask */
- memset(&desc->bitmap[0], 0, temp);
- memset(&desc->bitmap[temp], 0xff, temp);
+ /* ports removable, and usb 1.0 legacy PortPwrCtrlMask */
+ memset(&desc->u.hs.DeviceRemovable[0], 0, temp);
+ memset(&desc->u.hs.DeviceRemovable[temp], 0xff, temp);
temp = 0x0008; /* per-port overcurrent reporting */
if (HCS_PPC(oxu->hcs_params))
diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c
index 4c502c890ebd..1d586d4f7b56 100644
--- a/drivers/usb/host/pci-quirks.c
+++ b/drivers/usb/host/pci-quirks.c
@@ -52,6 +52,264 @@
#define EHCI_USBLEGCTLSTS 4 /* legacy control/status */
#define EHCI_USBLEGCTLSTS_SOOE (1 << 13) /* SMI on ownership change */
+/* AMD quirk use */
+#define AB_REG_BAR_LOW 0xe0
+#define AB_REG_BAR_HIGH 0xe1
+#define AB_REG_BAR_SB700 0xf0
+#define AB_INDX(addr) ((addr) + 0x00)
+#define AB_DATA(addr) ((addr) + 0x04)
+#define AX_INDXC 0x30
+#define AX_DATAC 0x34
+
+#define NB_PCIE_INDX_ADDR 0xe0
+#define NB_PCIE_INDX_DATA 0xe4
+#define PCIE_P_CNTL 0x10040
+#define BIF_NB 0x10002
+#define NB_PIF0_PWRDOWN_0 0x01100012
+#define NB_PIF0_PWRDOWN_1 0x01100013
+
+static struct amd_chipset_info {
+ struct pci_dev *nb_dev;
+ struct pci_dev *smbus_dev;
+ int nb_type;
+ int sb_type;
+ int isoc_reqs;
+ int probe_count;
+ int probe_result;
+} amd_chipset;
+
+static DEFINE_SPINLOCK(amd_lock);
+
+int usb_amd_find_chipset_info(void)
+{
+ u8 rev = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&amd_lock, flags);
+
+ amd_chipset.probe_count++;
+ /* probe only once */
+ if (amd_chipset.probe_count > 1) {
+ spin_unlock_irqrestore(&amd_lock, flags);
+ return amd_chipset.probe_result;
+ }
+
+ amd_chipset.smbus_dev = pci_get_device(PCI_VENDOR_ID_ATI, 0x4385, NULL);
+ if (amd_chipset.smbus_dev) {
+ rev = amd_chipset.smbus_dev->revision;
+ if (rev >= 0x40)
+ amd_chipset.sb_type = 1;
+ else if (rev >= 0x30 && rev <= 0x3b)
+ amd_chipset.sb_type = 3;
+ } else {
+ amd_chipset.smbus_dev = pci_get_device(PCI_VENDOR_ID_AMD,
+ 0x780b, NULL);
+ if (!amd_chipset.smbus_dev) {
+ spin_unlock_irqrestore(&amd_lock, flags);
+ return 0;
+ }
+ rev = amd_chipset.smbus_dev->revision;
+ if (rev >= 0x11 && rev <= 0x18)
+ amd_chipset.sb_type = 2;
+ }
+
+ if (amd_chipset.sb_type == 0) {
+ if (amd_chipset.smbus_dev) {
+ pci_dev_put(amd_chipset.smbus_dev);
+ amd_chipset.smbus_dev = NULL;
+ }
+ spin_unlock_irqrestore(&amd_lock, flags);
+ return 0;
+ }
+
+ amd_chipset.nb_dev = pci_get_device(PCI_VENDOR_ID_AMD, 0x9601, NULL);
+ if (amd_chipset.nb_dev) {
+ amd_chipset.nb_type = 1;
+ } else {
+ amd_chipset.nb_dev = pci_get_device(PCI_VENDOR_ID_AMD,
+ 0x1510, NULL);
+ if (amd_chipset.nb_dev) {
+ amd_chipset.nb_type = 2;
+ } else {
+ amd_chipset.nb_dev = pci_get_device(PCI_VENDOR_ID_AMD,
+ 0x9600, NULL);
+ if (amd_chipset.nb_dev)
+ amd_chipset.nb_type = 3;
+ }
+ }
+
+ amd_chipset.probe_result = 1;
+ printk(KERN_DEBUG "QUIRK: Enable AMD PLL fix\n");
+
+ spin_unlock_irqrestore(&amd_lock, flags);
+ return amd_chipset.probe_result;
+}
+EXPORT_SYMBOL_GPL(usb_amd_find_chipset_info);
+
+/*
+ * The hardware normally enables the A-link power management feature, which
+ * lets the system lower the power consumption in idle states.
+ *
+ * This USB quirk prevents the link going into that lower power state
+ * during isochronous transfers.
+ *
+ * Without this quirk, isochronous stream on OHCI/EHCI/xHCI controllers of
+ * some AMD platforms may stutter or have breaks occasionally.
+ */
+static void usb_amd_quirk_pll(int disable)
+{
+ u32 addr, addr_low, addr_high, val;
+ u32 bit = disable ? 0 : 1;
+ unsigned long flags;
+
+ spin_lock_irqsave(&amd_lock, flags);
+
+ if (disable) {
+ amd_chipset.isoc_reqs++;
+ if (amd_chipset.isoc_reqs > 1) {
+ spin_unlock_irqrestore(&amd_lock, flags);
+ return;
+ }
+ } else {
+ amd_chipset.isoc_reqs--;
+ if (amd_chipset.isoc_reqs > 0) {
+ spin_unlock_irqrestore(&amd_lock, flags);
+ return;
+ }
+ }
+
+ if (amd_chipset.sb_type == 1 || amd_chipset.sb_type == 2) {
+ outb_p(AB_REG_BAR_LOW, 0xcd6);
+ addr_low = inb_p(0xcd7);
+ outb_p(AB_REG_BAR_HIGH, 0xcd6);
+ addr_high = inb_p(0xcd7);
+ addr = addr_high << 8 | addr_low;
+
+ outl_p(0x30, AB_INDX(addr));
+ outl_p(0x40, AB_DATA(addr));
+ outl_p(0x34, AB_INDX(addr));
+ val = inl_p(AB_DATA(addr));
+ } else if (amd_chipset.sb_type == 3) {
+ pci_read_config_dword(amd_chipset.smbus_dev,
+ AB_REG_BAR_SB700, &addr);
+ outl(AX_INDXC, AB_INDX(addr));
+ outl(0x40, AB_DATA(addr));
+ outl(AX_DATAC, AB_INDX(addr));
+ val = inl(AB_DATA(addr));
+ } else {
+ spin_unlock_irqrestore(&amd_lock, flags);
+ return;
+ }
+
+ if (disable) {
+ val &= ~0x08;
+ val |= (1 << 4) | (1 << 9);
+ } else {
+ val |= 0x08;
+ val &= ~((1 << 4) | (1 << 9));
+ }
+ outl_p(val, AB_DATA(addr));
+
+ if (!amd_chipset.nb_dev) {
+ spin_unlock_irqrestore(&amd_lock, flags);
+ return;
+ }
+
+ if (amd_chipset.nb_type == 1 || amd_chipset.nb_type == 3) {
+ addr = PCIE_P_CNTL;
+ pci_write_config_dword(amd_chipset.nb_dev,
+ NB_PCIE_INDX_ADDR, addr);
+ pci_read_config_dword(amd_chipset.nb_dev,
+ NB_PCIE_INDX_DATA, &val);
+
+ val &= ~(1 | (1 << 3) | (1 << 4) | (1 << 9) | (1 << 12));
+ val |= bit | (bit << 3) | (bit << 12);
+ val |= ((!bit) << 4) | ((!bit) << 9);
+ pci_write_config_dword(amd_chipset.nb_dev,
+ NB_PCIE_INDX_DATA, val);
+
+ addr = BIF_NB;
+ pci_write_config_dword(amd_chipset.nb_dev,
+ NB_PCIE_INDX_ADDR, addr);
+ pci_read_config_dword(amd_chipset.nb_dev,
+ NB_PCIE_INDX_DATA, &val);
+ val &= ~(1 << 8);
+ val |= bit << 8;
+
+ pci_write_config_dword(amd_chipset.nb_dev,
+ NB_PCIE_INDX_DATA, val);
+ } else if (amd_chipset.nb_type == 2) {
+ addr = NB_PIF0_PWRDOWN_0;
+ pci_write_config_dword(amd_chipset.nb_dev,
+ NB_PCIE_INDX_ADDR, addr);
+ pci_read_config_dword(amd_chipset.nb_dev,
+ NB_PCIE_INDX_DATA, &val);
+ if (disable)
+ val &= ~(0x3f << 7);
+ else
+ val |= 0x3f << 7;
+
+ pci_write_config_dword(amd_chipset.nb_dev,
+ NB_PCIE_INDX_DATA, val);
+
+ addr = NB_PIF0_PWRDOWN_1;
+ pci_write_config_dword(amd_chipset.nb_dev,
+ NB_PCIE_INDX_ADDR, addr);
+ pci_read_config_dword(amd_chipset.nb_dev,
+ NB_PCIE_INDX_DATA, &val);
+ if (disable)
+ val &= ~(0x3f << 7);
+ else
+ val |= 0x3f << 7;
+
+ pci_write_config_dword(amd_chipset.nb_dev,
+ NB_PCIE_INDX_DATA, val);
+ }
+
+ spin_unlock_irqrestore(&amd_lock, flags);
+ return;
+}
+
+void usb_amd_quirk_pll_disable(void)
+{
+ usb_amd_quirk_pll(1);
+}
+EXPORT_SYMBOL_GPL(usb_amd_quirk_pll_disable);
+
+void usb_amd_quirk_pll_enable(void)
+{
+ usb_amd_quirk_pll(0);
+}
+EXPORT_SYMBOL_GPL(usb_amd_quirk_pll_enable);
+
+void usb_amd_dev_put(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&amd_lock, flags);
+
+ amd_chipset.probe_count--;
+ if (amd_chipset.probe_count > 0) {
+ spin_unlock_irqrestore(&amd_lock, flags);
+ return;
+ }
+
+ if (amd_chipset.nb_dev) {
+ pci_dev_put(amd_chipset.nb_dev);
+ amd_chipset.nb_dev = NULL;
+ }
+ if (amd_chipset.smbus_dev) {
+ pci_dev_put(amd_chipset.smbus_dev);
+ amd_chipset.smbus_dev = NULL;
+ }
+ amd_chipset.nb_type = 0;
+ amd_chipset.sb_type = 0;
+ amd_chipset.isoc_reqs = 0;
+ amd_chipset.probe_result = 0;
+
+ spin_unlock_irqrestore(&amd_lock, flags);
+}
+EXPORT_SYMBOL_GPL(usb_amd_dev_put);
/*
* Make sure the controller is completely inactive, unable to
diff --git a/drivers/usb/host/pci-quirks.h b/drivers/usb/host/pci-quirks.h
index 1564edfff6fe..6ae9f78e9938 100644
--- a/drivers/usb/host/pci-quirks.h
+++ b/drivers/usb/host/pci-quirks.h
@@ -1,7 +1,17 @@
#ifndef __LINUX_USB_PCI_QUIRKS_H
#define __LINUX_USB_PCI_QUIRKS_H
+#ifdef CONFIG_PCI
void uhci_reset_hc(struct pci_dev *pdev, unsigned long base);
int uhci_check_and_reset_hc(struct pci_dev *pdev, unsigned long base);
+int usb_amd_find_chipset_info(void);
+void usb_amd_dev_put(void);
+void usb_amd_quirk_pll_disable(void);
+void usb_amd_quirk_pll_enable(void);
+#else
+static inline void usb_amd_quirk_pll_disable(void) {}
+static inline void usb_amd_quirk_pll_enable(void) {}
+static inline void usb_amd_dev_put(void) {}
+#endif /* CONFIG_PCI */
#endif /* __LINUX_USB_PCI_QUIRKS_H */
diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c
index 3076b1cc05df..db6f8b9c19b6 100644
--- a/drivers/usb/host/r8a66597-hcd.c
+++ b/drivers/usb/host/r8a66597-hcd.c
@@ -2150,8 +2150,9 @@ static void r8a66597_hub_descriptor(struct r8a66597 *r8a66597,
desc->bDescLength = 9;
desc->bPwrOn2PwrGood = 0;
desc->wHubCharacteristics = cpu_to_le16(0x0011);
- desc->bitmap[0] = ((1 << r8a66597->max_root_hub) - 1) << 1;
- desc->bitmap[1] = ~0;
+ desc->u.hs.DeviceRemovable[0] =
+ ((1 << r8a66597->max_root_hub) - 1) << 1;
+ desc->u.hs.DeviceRemovable[1] = ~0;
}
static int r8a66597_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c
index 2e9602a10e9b..18b7099a8125 100644
--- a/drivers/usb/host/sl811-hcd.c
+++ b/drivers/usb/host/sl811-hcd.c
@@ -1111,9 +1111,9 @@ sl811h_hub_descriptor (
desc->wHubCharacteristics = cpu_to_le16(temp);
- /* two bitmaps: ports removable, and legacy PortPwrCtrlMask */
- desc->bitmap[0] = 0 << 1;
- desc->bitmap[1] = ~0;
+ /* ports removable, and legacy PortPwrCtrlMask */
+ desc->u.hs.DeviceRemovable[0] = 0 << 1;
+ desc->u.hs.DeviceRemovable[1] = ~0;
}
static void
diff --git a/drivers/usb/host/u132-hcd.c b/drivers/usb/host/u132-hcd.c
index fab764946c74..b4785934e091 100644
--- a/drivers/usb/host/u132-hcd.c
+++ b/drivers/usb/host/u132-hcd.c
@@ -2604,13 +2604,14 @@ static int u132_roothub_descriptor(struct u132 *u132,
retval = u132_read_pcimem(u132, roothub.b, &rh_b);
if (retval)
return retval;
- memset(desc->bitmap, 0xff, sizeof(desc->bitmap));
- desc->bitmap[0] = rh_b & RH_B_DR;
+ memset(desc->u.hs.DeviceRemovable, 0xff,
+ sizeof(desc->u.hs.DeviceRemovable));
+ desc->u.hs.DeviceRemovable[0] = rh_b & RH_B_DR;
if (u132->num_ports > 7) {
- desc->bitmap[1] = (rh_b & RH_B_DR) >> 8;
- desc->bitmap[2] = 0xff;
+ desc->u.hs.DeviceRemovable[1] = (rh_b & RH_B_DR) >> 8;
+ desc->u.hs.DeviceRemovable[2] = 0xff;
} else
- desc->bitmap[1] = 0xff;
+ desc->u.hs.DeviceRemovable[1] = 0xff;
return 0;
}
diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c
index cee867829ec9..4f65b14e5e08 100644
--- a/drivers/usb/host/uhci-hcd.c
+++ b/drivers/usb/host/uhci-hcd.c
@@ -471,7 +471,7 @@ static irqreturn_t uhci_irq(struct usb_hcd *hcd)
/*
* Store the current frame number in uhci->frame_number if the controller
- * is runnning. Expand from 11 bits (of which we use only 10) to a
+ * is running. Expand from 11 bits (of which we use only 10) to a
* full-sized integer.
*
* Like many other parts of the driver, this code relies on being polled
diff --git a/drivers/usb/host/xhci-ext-caps.h b/drivers/usb/host/xhci-ext-caps.h
index 78c4edac1db1..ce5c9e51748e 100644
--- a/drivers/usb/host/xhci-ext-caps.h
+++ b/drivers/usb/host/xhci-ext-caps.h
@@ -19,8 +19,8 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-/* Up to 16 microframes to halt an HC - one microframe is 125 microsectonds */
-#define XHCI_MAX_HALT_USEC (16*125)
+/* Up to 16 ms to halt an HC */
+#define XHCI_MAX_HALT_USEC (16*1000)
/* HC not running - set to 1 when run/stop bit is cleared. */
#define XHCI_STS_HALT (1<<0)
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index 5d963e350494..a78f2ebd11b7 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -28,27 +28,15 @@
#define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_WRC | PORT_OCC | \
PORT_RC | PORT_PLC | PORT_PE)
-static void xhci_hub_descriptor(struct xhci_hcd *xhci,
- struct usb_hub_descriptor *desc)
+static void xhci_common_hub_descriptor(struct xhci_hcd *xhci,
+ struct usb_hub_descriptor *desc, int ports)
{
- int ports;
u16 temp;
- ports = HCS_MAX_PORTS(xhci->hcs_params1);
-
- /* USB 3.0 hubs have a different descriptor, but we fake this for now */
- desc->bDescriptorType = 0x29;
desc->bPwrOn2PwrGood = 10; /* xhci section 5.4.9 says 20ms max */
desc->bHubContrCurrent = 0;
desc->bNbrPorts = ports;
- temp = 1 + (ports / 8);
- desc->bDescLength = 7 + 2 * temp;
-
- /* Why does core/hcd.h define bitmap? It's just confusing. */
- memset(&desc->DeviceRemovable[0], 0, temp);
- memset(&desc->DeviceRemovable[temp], 0xff, temp);
-
/* Ugh, these should be #defines, FIXME */
/* Using table 11-13 in USB 2.0 spec. */
temp = 0;
@@ -65,14 +53,108 @@ static void xhci_hub_descriptor(struct xhci_hcd *xhci,
desc->wHubCharacteristics = (__force __u16) cpu_to_le16(temp);
}
+/* Fill in the USB 2.0 roothub descriptor */
+static void xhci_usb2_hub_descriptor(struct usb_hcd *hcd, struct xhci_hcd *xhci,
+ struct usb_hub_descriptor *desc)
+{
+ int ports;
+ u16 temp;
+ __u8 port_removable[(USB_MAXCHILDREN + 1 + 7) / 8];
+ u32 portsc;
+ unsigned int i;
+
+ ports = xhci->num_usb2_ports;
+
+ xhci_common_hub_descriptor(xhci, desc, ports);
+ desc->bDescriptorType = 0x29;
+ temp = 1 + (ports / 8);
+ desc->bDescLength = 7 + 2 * temp;
+
+ /* The Device Removable bits are reported on a byte granularity.
+ * If the port doesn't exist within that byte, the bit is set to 0.
+ */
+ memset(port_removable, 0, sizeof(port_removable));
+ for (i = 0; i < ports; i++) {
+ portsc = xhci_readl(xhci, xhci->usb3_ports[i]);
+ /* If a device is removable, PORTSC reports a 0, same as in the
+ * hub descriptor DeviceRemovable bits.
+ */
+ if (portsc & PORT_DEV_REMOVE)
+ /* This math is hairy because bit 0 of DeviceRemovable
+ * is reserved, and bit 1 is for port 1, etc.
+ */
+ port_removable[(i + 1) / 8] |= 1 << ((i + 1) % 8);
+ }
+
+ /* ch11.h defines a hub descriptor that has room for USB_MAXCHILDREN
+ * ports on it. The USB 2.0 specification says that there are two
+ * variable length fields at the end of the hub descriptor:
+ * DeviceRemovable and PortPwrCtrlMask. But since we can have less than
+ * USB_MAXCHILDREN ports, we may need to use the DeviceRemovable array
+ * to set PortPwrCtrlMask bits. PortPwrCtrlMask must always be set to
+ * 0xFF, so we initialize the both arrays (DeviceRemovable and
+ * PortPwrCtrlMask) to 0xFF. Then we set the DeviceRemovable for each
+ * set of ports that actually exist.
+ */
+ memset(desc->u.hs.DeviceRemovable, 0xff,
+ sizeof(desc->u.hs.DeviceRemovable));
+ memset(desc->u.hs.PortPwrCtrlMask, 0xff,
+ sizeof(desc->u.hs.PortPwrCtrlMask));
+
+ for (i = 0; i < (ports + 1 + 7) / 8; i++)
+ memset(&desc->u.hs.DeviceRemovable[i], port_removable[i],
+ sizeof(__u8));
+}
+
+/* Fill in the USB 3.0 roothub descriptor */
+static void xhci_usb3_hub_descriptor(struct usb_hcd *hcd, struct xhci_hcd *xhci,
+ struct usb_hub_descriptor *desc)
+{
+ int ports;
+ u16 port_removable;
+ u32 portsc;
+ unsigned int i;
+
+ ports = xhci->num_usb3_ports;
+ xhci_common_hub_descriptor(xhci, desc, ports);
+ desc->bDescriptorType = 0x2a;
+ desc->bDescLength = 12;
+
+ /* header decode latency should be zero for roothubs,
+ * see section 4.23.5.2.
+ */
+ desc->u.ss.bHubHdrDecLat = 0;
+ desc->u.ss.wHubDelay = 0;
+
+ port_removable = 0;
+ /* bit 0 is reserved, bit 1 is for port 1, etc. */
+ for (i = 0; i < ports; i++) {
+ portsc = xhci_readl(xhci, xhci->usb3_ports[i]);
+ if (portsc & PORT_DEV_REMOVE)
+ port_removable |= 1 << (i + 1);
+ }
+ memset(&desc->u.ss.DeviceRemovable,
+ (__force __u16) cpu_to_le16(port_removable),
+ sizeof(__u16));
+}
+
+static void xhci_hub_descriptor(struct usb_hcd *hcd, struct xhci_hcd *xhci,
+ struct usb_hub_descriptor *desc)
+{
+
+ if (hcd->speed == HCD_USB3)
+ xhci_usb3_hub_descriptor(hcd, xhci, desc);
+ else
+ xhci_usb2_hub_descriptor(hcd, xhci, desc);
+
+}
+
static unsigned int xhci_port_speed(unsigned int port_status)
{
if (DEV_LOWSPEED(port_status))
return USB_PORT_STAT_LOW_SPEED;
if (DEV_HIGHSPEED(port_status))
return USB_PORT_STAT_HIGH_SPEED;
- if (DEV_SUPERSPEED(port_status))
- return USB_PORT_STAT_SUPER_SPEED;
/*
* FIXME: Yes, we should check for full speed, but the core uses that as
* a default in portspeed() in usb/core/hub.c (which is the only place
@@ -135,17 +217,22 @@ u32 xhci_port_state_to_neutral(u32 state)
/*
* find slot id based on port number.
+ * @port: The one-based port number from one of the two split roothubs.
*/
-int xhci_find_slot_id_by_port(struct xhci_hcd *xhci, u16 port)
+int xhci_find_slot_id_by_port(struct usb_hcd *hcd, struct xhci_hcd *xhci,
+ u16 port)
{
int slot_id;
int i;
+ enum usb_device_speed speed;
slot_id = 0;
for (i = 0; i < MAX_HC_SLOTS; i++) {
if (!xhci->devs[i])
continue;
- if (xhci->devs[i]->port == port) {
+ speed = xhci->devs[i]->udev->speed;
+ if (((speed == USB_SPEED_SUPER) == (hcd->speed == HCD_USB3))
+ && xhci->devs[i]->port == port) {
slot_id = i;
break;
}
@@ -226,11 +313,11 @@ void xhci_ring_device(struct xhci_hcd *xhci, int slot_id)
return;
}
-static void xhci_disable_port(struct xhci_hcd *xhci, u16 wIndex,
- u32 __iomem *addr, u32 port_status)
+static void xhci_disable_port(struct usb_hcd *hcd, struct xhci_hcd *xhci,
+ u16 wIndex, u32 __iomem *addr, u32 port_status)
{
/* Don't allow the USB core to disable SuperSpeed ports. */
- if (xhci->port_array[wIndex] == 0x03) {
+ if (hcd->speed == HCD_USB3) {
xhci_dbg(xhci, "Ignoring request to disable "
"SuperSpeed port.\n");
return;
@@ -289,10 +376,18 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
unsigned long flags;
u32 temp, temp1, status;
int retval = 0;
- u32 __iomem *addr;
+ u32 __iomem **port_array;
int slot_id;
-
- ports = HCS_MAX_PORTS(xhci->hcs_params1);
+ struct xhci_bus_state *bus_state;
+
+ if (hcd->speed == HCD_USB3) {
+ ports = xhci->num_usb3_ports;
+ port_array = xhci->usb3_ports;
+ } else {
+ ports = xhci->num_usb2_ports;
+ port_array = xhci->usb2_ports;
+ }
+ bus_state = &xhci->bus_state[hcd_index(hcd)];
spin_lock_irqsave(&xhci->lock, flags);
switch (typeReq) {
@@ -301,17 +396,35 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
memset(buf, 0, 4);
break;
case GetHubDescriptor:
- xhci_hub_descriptor(xhci, (struct usb_hub_descriptor *) buf);
+ /* Check to make sure userspace is asking for the USB 3.0 hub
+ * descriptor for the USB 3.0 roothub. If not, we stall the
+ * endpoint, like external hubs do.
+ */
+ if (hcd->speed == HCD_USB3 &&
+ (wLength < USB_DT_SS_HUB_SIZE ||
+ wValue != (USB_DT_SS_HUB << 8))) {
+ xhci_dbg(xhci, "Wrong hub descriptor type for "
+ "USB 3.0 roothub.\n");
+ goto error;
+ }
+ xhci_hub_descriptor(hcd, xhci,
+ (struct usb_hub_descriptor *) buf);
break;
case GetPortStatus:
if (!wIndex || wIndex > ports)
goto error;
wIndex--;
status = 0;
- addr = &xhci->op_regs->port_status_base + NUM_PORT_REGS*(wIndex & 0xff);
- temp = xhci_readl(xhci, addr);
+ temp = xhci_readl(xhci, port_array[wIndex]);
+ if (temp == 0xffffffff) {
+ retval = -ENODEV;
+ break;
+ }
xhci_dbg(xhci, "get port status, actual port %d status = 0x%x\n", wIndex, temp);
+ /* FIXME - should we return a port status value like the USB
+ * 3.0 external hubs do?
+ */
/* wPortChange bits */
if (temp & PORT_CSC)
status |= USB_PORT_STAT_C_CONNECTION << 16;
@@ -330,38 +443,33 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
if ((temp & PORT_RESET) || !(temp & PORT_PE))
goto error;
if (!DEV_SUPERSPEED(temp) && time_after_eq(jiffies,
- xhci->resume_done[wIndex])) {
+ bus_state->resume_done[wIndex])) {
xhci_dbg(xhci, "Resume USB2 port %d\n",
wIndex + 1);
- xhci->resume_done[wIndex] = 0;
+ bus_state->resume_done[wIndex] = 0;
temp1 = xhci_port_state_to_neutral(temp);
temp1 &= ~PORT_PLS_MASK;
temp1 |= PORT_LINK_STROBE | XDEV_U0;
- xhci_writel(xhci, temp1, addr);
+ xhci_writel(xhci, temp1, port_array[wIndex]);
xhci_dbg(xhci, "set port %d resume\n",
wIndex + 1);
- slot_id = xhci_find_slot_id_by_port(xhci,
+ slot_id = xhci_find_slot_id_by_port(hcd, xhci,
wIndex + 1);
if (!slot_id) {
xhci_dbg(xhci, "slot_id is zero\n");
goto error;
}
xhci_ring_device(xhci, slot_id);
- xhci->port_c_suspend[wIndex >> 5] |=
- 1 << (wIndex & 31);
- xhci->suspended_ports[wIndex >> 5] &=
- ~(1 << (wIndex & 31));
+ bus_state->port_c_suspend |= 1 << wIndex;
+ bus_state->suspended_ports &= ~(1 << wIndex);
}
}
if ((temp & PORT_PLS_MASK) == XDEV_U0
&& (temp & PORT_POWER)
- && (xhci->suspended_ports[wIndex >> 5] &
- (1 << (wIndex & 31)))) {
- xhci->suspended_ports[wIndex >> 5] &=
- ~(1 << (wIndex & 31));
- xhci->port_c_suspend[wIndex >> 5] |=
- 1 << (wIndex & 31);
+ && (bus_state->suspended_ports & (1 << wIndex))) {
+ bus_state->suspended_ports &= ~(1 << wIndex);
+ bus_state->port_c_suspend |= 1 << wIndex;
}
if (temp & PORT_CONNECT) {
status |= USB_PORT_STAT_CONNECTION;
@@ -375,7 +483,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
status |= USB_PORT_STAT_RESET;
if (temp & PORT_POWER)
status |= USB_PORT_STAT_POWER;
- if (xhci->port_c_suspend[wIndex >> 5] & (1 << (wIndex & 31)))
+ if (bus_state->port_c_suspend & (1 << wIndex))
status |= 1 << USB_PORT_FEAT_C_SUSPEND;
xhci_dbg(xhci, "Get port status returned 0x%x\n", status);
put_unaligned(cpu_to_le32(status), (__le32 *) buf);
@@ -385,12 +493,16 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
if (!wIndex || wIndex > ports)
goto error;
wIndex--;
- addr = &xhci->op_regs->port_status_base + NUM_PORT_REGS*(wIndex & 0xff);
- temp = xhci_readl(xhci, addr);
+ temp = xhci_readl(xhci, port_array[wIndex]);
+ if (temp == 0xffffffff) {
+ retval = -ENODEV;
+ break;
+ }
temp = xhci_port_state_to_neutral(temp);
+ /* FIXME: What new port features do we need to support? */
switch (wValue) {
case USB_PORT_FEAT_SUSPEND:
- temp = xhci_readl(xhci, addr);
+ temp = xhci_readl(xhci, port_array[wIndex]);
/* In spec software should not attempt to suspend
* a port unless the port reports that it is in the
* enabled (PED = ‘1’,PLS < ‘3’) state.
@@ -402,7 +514,8 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
goto error;
}
- slot_id = xhci_find_slot_id_by_port(xhci, wIndex + 1);
+ slot_id = xhci_find_slot_id_by_port(hcd, xhci,
+ wIndex + 1);
if (!slot_id) {
xhci_warn(xhci, "slot_id is zero\n");
goto error;
@@ -415,15 +528,14 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
temp = xhci_port_state_to_neutral(temp);
temp &= ~PORT_PLS_MASK;
temp |= PORT_LINK_STROBE | XDEV_U3;
- xhci_writel(xhci, temp, addr);
+ xhci_writel(xhci, temp, port_array[wIndex]);
spin_unlock_irqrestore(&xhci->lock, flags);
msleep(10); /* wait device to enter */
spin_lock_irqsave(&xhci->lock, flags);
- temp = xhci_readl(xhci, addr);
- xhci->suspended_ports[wIndex >> 5] |=
- 1 << (wIndex & (31));
+ temp = xhci_readl(xhci, port_array[wIndex]);
+ bus_state->suspended_ports |= 1 << wIndex;
break;
case USB_PORT_FEAT_POWER:
/*
@@ -432,34 +544,39 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
* However, khubd will ignore the roothub events until
* the roothub is registered.
*/
- xhci_writel(xhci, temp | PORT_POWER, addr);
+ xhci_writel(xhci, temp | PORT_POWER,
+ port_array[wIndex]);
- temp = xhci_readl(xhci, addr);
+ temp = xhci_readl(xhci, port_array[wIndex]);
xhci_dbg(xhci, "set port power, actual port %d status = 0x%x\n", wIndex, temp);
break;
case USB_PORT_FEAT_RESET:
temp = (temp | PORT_RESET);
- xhci_writel(xhci, temp, addr);
+ xhci_writel(xhci, temp, port_array[wIndex]);
- temp = xhci_readl(xhci, addr);
+ temp = xhci_readl(xhci, port_array[wIndex]);
xhci_dbg(xhci, "set port reset, actual port %d status = 0x%x\n", wIndex, temp);
break;
default:
goto error;
}
- temp = xhci_readl(xhci, addr); /* unblock any posted writes */
+ /* unblock any posted writes */
+ temp = xhci_readl(xhci, port_array[wIndex]);
break;
case ClearPortFeature:
if (!wIndex || wIndex > ports)
goto error;
wIndex--;
- addr = &xhci->op_regs->port_status_base +
- NUM_PORT_REGS*(wIndex & 0xff);
- temp = xhci_readl(xhci, addr);
+ temp = xhci_readl(xhci, port_array[wIndex]);
+ if (temp == 0xffffffff) {
+ retval = -ENODEV;
+ break;
+ }
+ /* FIXME: What new port features do we need to support? */
temp = xhci_port_state_to_neutral(temp);
switch (wValue) {
case USB_PORT_FEAT_SUSPEND:
- temp = xhci_readl(xhci, addr);
+ temp = xhci_readl(xhci, port_array[wIndex]);
xhci_dbg(xhci, "clear USB_PORT_FEAT_SUSPEND\n");
xhci_dbg(xhci, "PORTSC %04x\n", temp);
if (temp & PORT_RESET)
@@ -471,30 +588,34 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
temp = xhci_port_state_to_neutral(temp);
temp &= ~PORT_PLS_MASK;
temp |= PORT_LINK_STROBE | XDEV_U0;
- xhci_writel(xhci, temp, addr);
- xhci_readl(xhci, addr);
+ xhci_writel(xhci, temp,
+ port_array[wIndex]);
+ xhci_readl(xhci, port_array[wIndex]);
} else {
temp = xhci_port_state_to_neutral(temp);
temp &= ~PORT_PLS_MASK;
temp |= PORT_LINK_STROBE | XDEV_RESUME;
- xhci_writel(xhci, temp, addr);
+ xhci_writel(xhci, temp,
+ port_array[wIndex]);
spin_unlock_irqrestore(&xhci->lock,
flags);
msleep(20);
spin_lock_irqsave(&xhci->lock, flags);
- temp = xhci_readl(xhci, addr);
+ temp = xhci_readl(xhci,
+ port_array[wIndex]);
temp = xhci_port_state_to_neutral(temp);
temp &= ~PORT_PLS_MASK;
temp |= PORT_LINK_STROBE | XDEV_U0;
- xhci_writel(xhci, temp, addr);
+ xhci_writel(xhci, temp,
+ port_array[wIndex]);
}
- xhci->port_c_suspend[wIndex >> 5] |=
- 1 << (wIndex & 31);
+ bus_state->port_c_suspend |= 1 << wIndex;
}
- slot_id = xhci_find_slot_id_by_port(xhci, wIndex + 1);
+ slot_id = xhci_find_slot_id_by_port(hcd, xhci,
+ wIndex + 1);
if (!slot_id) {
xhci_dbg(xhci, "slot_id is zero\n");
goto error;
@@ -502,17 +623,17 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
xhci_ring_device(xhci, slot_id);
break;
case USB_PORT_FEAT_C_SUSPEND:
- xhci->port_c_suspend[wIndex >> 5] &=
- ~(1 << (wIndex & 31));
+ bus_state->port_c_suspend &= ~(1 << wIndex);
case USB_PORT_FEAT_C_RESET:
case USB_PORT_FEAT_C_CONNECTION:
case USB_PORT_FEAT_C_OVER_CURRENT:
case USB_PORT_FEAT_C_ENABLE:
xhci_clear_port_change_bit(xhci, wValue, wIndex,
- addr, temp);
+ port_array[wIndex], temp);
break;
case USB_PORT_FEAT_ENABLE:
- xhci_disable_port(xhci, wIndex, addr, temp);
+ xhci_disable_port(hcd, xhci, wIndex,
+ port_array[wIndex], temp);
break;
default:
goto error;
@@ -543,9 +664,17 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf)
int i, retval;
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
int ports;
- u32 __iomem *addr;
-
- ports = HCS_MAX_PORTS(xhci->hcs_params1);
+ u32 __iomem **port_array;
+ struct xhci_bus_state *bus_state;
+
+ if (hcd->speed == HCD_USB3) {
+ ports = xhci->num_usb3_ports;
+ port_array = xhci->usb3_ports;
+ } else {
+ ports = xhci->num_usb2_ports;
+ port_array = xhci->usb2_ports;
+ }
+ bus_state = &xhci->bus_state[hcd_index(hcd)];
/* Initial status is no changes */
retval = (ports + 8) / 8;
@@ -557,13 +686,15 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf)
spin_lock_irqsave(&xhci->lock, flags);
/* For each port, did anything change? If so, set that bit in buf. */
for (i = 0; i < ports; i++) {
- addr = &xhci->op_regs->port_status_base +
- NUM_PORT_REGS*i;
- temp = xhci_readl(xhci, addr);
+ temp = xhci_readl(xhci, port_array[i]);
+ if (temp == 0xffffffff) {
+ retval = -ENODEV;
+ break;
+ }
if ((temp & mask) != 0 ||
- (xhci->port_c_suspend[i >> 5] & 1 << (i & 31)) ||
- (xhci->resume_done[i] && time_after_eq(
- jiffies, xhci->resume_done[i]))) {
+ (bus_state->port_c_suspend & 1 << i) ||
+ (bus_state->resume_done[i] && time_after_eq(
+ jiffies, bus_state->resume_done[i]))) {
buf[(i + 1) / 8] |= 1 << (i + 1) % 8;
status = 1;
}
@@ -577,42 +708,51 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf)
int xhci_bus_suspend(struct usb_hcd *hcd)
{
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
- int port;
+ int max_ports, port_index;
+ u32 __iomem **port_array;
+ struct xhci_bus_state *bus_state;
unsigned long flags;
- xhci_dbg(xhci, "suspend root hub\n");
+ if (hcd->speed == HCD_USB3) {
+ max_ports = xhci->num_usb3_ports;
+ port_array = xhci->usb3_ports;
+ xhci_dbg(xhci, "suspend USB 3.0 root hub\n");
+ } else {
+ max_ports = xhci->num_usb2_ports;
+ port_array = xhci->usb2_ports;
+ xhci_dbg(xhci, "suspend USB 2.0 root hub\n");
+ }
+ bus_state = &xhci->bus_state[hcd_index(hcd)];
spin_lock_irqsave(&xhci->lock, flags);
if (hcd->self.root_hub->do_remote_wakeup) {
- port = HCS_MAX_PORTS(xhci->hcs_params1);
- while (port--) {
- if (xhci->resume_done[port] != 0) {
+ port_index = max_ports;
+ while (port_index--) {
+ if (bus_state->resume_done[port_index] != 0) {
spin_unlock_irqrestore(&xhci->lock, flags);
xhci_dbg(xhci, "suspend failed because "
"port %d is resuming\n",
- port + 1);
+ port_index + 1);
return -EBUSY;
}
}
}
- port = HCS_MAX_PORTS(xhci->hcs_params1);
- xhci->bus_suspended = 0;
- while (port--) {
+ port_index = max_ports;
+ bus_state->bus_suspended = 0;
+ while (port_index--) {
/* suspend the port if the port is not suspended */
- u32 __iomem *addr;
u32 t1, t2;
int slot_id;
- addr = &xhci->op_regs->port_status_base +
- NUM_PORT_REGS * (port & 0xff);
- t1 = xhci_readl(xhci, addr);
+ t1 = xhci_readl(xhci, port_array[port_index]);
t2 = xhci_port_state_to_neutral(t1);
if ((t1 & PORT_PE) && !(t1 & PORT_PLS_MASK)) {
- xhci_dbg(xhci, "port %d not suspended\n", port);
- slot_id = xhci_find_slot_id_by_port(xhci, port + 1);
+ xhci_dbg(xhci, "port %d not suspended\n", port_index);
+ slot_id = xhci_find_slot_id_by_port(hcd, xhci,
+ port_index + 1);
if (slot_id) {
spin_unlock_irqrestore(&xhci->lock, flags);
xhci_stop_device(xhci, slot_id, 1);
@@ -620,7 +760,7 @@ int xhci_bus_suspend(struct usb_hcd *hcd)
}
t2 &= ~PORT_PLS_MASK;
t2 |= PORT_LINK_STROBE | XDEV_U3;
- set_bit(port, &xhci->bus_suspended);
+ set_bit(port_index, &bus_state->bus_suspended);
}
if (hcd->self.root_hub->do_remote_wakeup) {
if (t1 & PORT_CONNECT) {
@@ -635,22 +775,24 @@ int xhci_bus_suspend(struct usb_hcd *hcd)
t1 = xhci_port_state_to_neutral(t1);
if (t1 != t2)
- xhci_writel(xhci, t2, addr);
+ xhci_writel(xhci, t2, port_array[port_index]);
if (DEV_HIGHSPEED(t1)) {
/* enable remote wake up for USB 2.0 */
u32 __iomem *addr;
u32 tmp;
- addr = &xhci->op_regs->port_power_base +
- NUM_PORT_REGS * (port & 0xff);
+ /* Add one to the port status register address to get
+ * the port power control register address.
+ */
+ addr = port_array[port_index] + 1;
tmp = xhci_readl(xhci, addr);
tmp |= PORT_RWE;
xhci_writel(xhci, tmp, addr);
}
}
hcd->state = HC_STATE_SUSPENDED;
- xhci->next_statechange = jiffies + msecs_to_jiffies(10);
+ bus_state->next_statechange = jiffies + msecs_to_jiffies(10);
spin_unlock_irqrestore(&xhci->lock, flags);
return 0;
}
@@ -658,13 +800,24 @@ int xhci_bus_suspend(struct usb_hcd *hcd)
int xhci_bus_resume(struct usb_hcd *hcd)
{
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
- int port;
+ int max_ports, port_index;
+ u32 __iomem **port_array;
+ struct xhci_bus_state *bus_state;
u32 temp;
unsigned long flags;
- xhci_dbg(xhci, "resume root hub\n");
+ if (hcd->speed == HCD_USB3) {
+ max_ports = xhci->num_usb3_ports;
+ port_array = xhci->usb3_ports;
+ xhci_dbg(xhci, "resume USB 3.0 root hub\n");
+ } else {
+ max_ports = xhci->num_usb2_ports;
+ port_array = xhci->usb2_ports;
+ xhci_dbg(xhci, "resume USB 2.0 root hub\n");
+ }
+ bus_state = &xhci->bus_state[hcd_index(hcd)];
- if (time_before(jiffies, xhci->next_statechange))
+ if (time_before(jiffies, bus_state->next_statechange))
msleep(5);
spin_lock_irqsave(&xhci->lock, flags);
@@ -678,57 +831,57 @@ int xhci_bus_resume(struct usb_hcd *hcd)
temp &= ~CMD_EIE;
xhci_writel(xhci, temp, &xhci->op_regs->command);
- port = HCS_MAX_PORTS(xhci->hcs_params1);
- while (port--) {
+ port_index = max_ports;
+ while (port_index--) {
/* Check whether need resume ports. If needed
resume port and disable remote wakeup */
- u32 __iomem *addr;
u32 temp;
int slot_id;
- addr = &xhci->op_regs->port_status_base +
- NUM_PORT_REGS * (port & 0xff);
- temp = xhci_readl(xhci, addr);
+ temp = xhci_readl(xhci, port_array[port_index]);
if (DEV_SUPERSPEED(temp))
temp &= ~(PORT_RWC_BITS | PORT_CEC | PORT_WAKE_BITS);
else
temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS);
- if (test_bit(port, &xhci->bus_suspended) &&
+ if (test_bit(port_index, &bus_state->bus_suspended) &&
(temp & PORT_PLS_MASK)) {
if (DEV_SUPERSPEED(temp)) {
temp = xhci_port_state_to_neutral(temp);
temp &= ~PORT_PLS_MASK;
temp |= PORT_LINK_STROBE | XDEV_U0;
- xhci_writel(xhci, temp, addr);
+ xhci_writel(xhci, temp, port_array[port_index]);
} else {
temp = xhci_port_state_to_neutral(temp);
temp &= ~PORT_PLS_MASK;
temp |= PORT_LINK_STROBE | XDEV_RESUME;
- xhci_writel(xhci, temp, addr);
+ xhci_writel(xhci, temp, port_array[port_index]);
spin_unlock_irqrestore(&xhci->lock, flags);
msleep(20);
spin_lock_irqsave(&xhci->lock, flags);
- temp = xhci_readl(xhci, addr);
+ temp = xhci_readl(xhci, port_array[port_index]);
temp = xhci_port_state_to_neutral(temp);
temp &= ~PORT_PLS_MASK;
temp |= PORT_LINK_STROBE | XDEV_U0;
- xhci_writel(xhci, temp, addr);
+ xhci_writel(xhci, temp, port_array[port_index]);
}
- slot_id = xhci_find_slot_id_by_port(xhci, port + 1);
+ slot_id = xhci_find_slot_id_by_port(hcd,
+ xhci, port_index + 1);
if (slot_id)
xhci_ring_device(xhci, slot_id);
} else
- xhci_writel(xhci, temp, addr);
+ xhci_writel(xhci, temp, port_array[port_index]);
if (DEV_HIGHSPEED(temp)) {
/* disable remote wake up for USB 2.0 */
u32 __iomem *addr;
u32 tmp;
- addr = &xhci->op_regs->port_power_base +
- NUM_PORT_REGS * (port & 0xff);
+ /* Add one to the port status register address to get
+ * the port power control register address.
+ */
+ addr = port_array[port_index] + 1;
tmp = xhci_readl(xhci, addr);
tmp &= ~PORT_RWE;
xhci_writel(xhci, tmp, addr);
@@ -737,8 +890,7 @@ int xhci_bus_resume(struct usb_hcd *hcd)
(void) xhci_readl(xhci, &xhci->op_regs->command);
- xhci->next_statechange = jiffies + msecs_to_jiffies(5);
- hcd->state = HC_STATE_RUNNING;
+ bus_state->next_statechange = jiffies + msecs_to_jiffies(5);
/* re-enable irqs */
temp = xhci_readl(xhci, &xhci->op_regs->command);
temp |= CMD_EIE;
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index a9534396e85b..a003e79aacdc 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -814,14 +814,64 @@ void xhci_copy_ep0_dequeue_into_input_ctx(struct xhci_hcd *xhci,
ep0_ctx->deq |= ep_ring->cycle_state;
}
+/*
+ * The xHCI roothub may have ports of differing speeds in any order in the port
+ * status registers. xhci->port_array provides an array of the port speed for
+ * each offset into the port status registers.
+ *
+ * The xHCI hardware wants to know the roothub port number that the USB device
+ * is attached to (or the roothub port its ancestor hub is attached to). All we
+ * know is the index of that port under either the USB 2.0 or the USB 3.0
+ * roothub, but that doesn't give us the real index into the HW port status
+ * registers. Scan through the xHCI roothub port array, looking for the Nth
+ * entry of the correct port speed. Return the port number of that entry.
+ */
+static u32 xhci_find_real_port_number(struct xhci_hcd *xhci,
+ struct usb_device *udev)
+{
+ struct usb_device *top_dev;
+ unsigned int num_similar_speed_ports;
+ unsigned int faked_port_num;
+ int i;
+
+ for (top_dev = udev; top_dev->parent && top_dev->parent->parent;
+ top_dev = top_dev->parent)
+ /* Found device below root hub */;
+ faked_port_num = top_dev->portnum;
+ for (i = 0, num_similar_speed_ports = 0;
+ i < HCS_MAX_PORTS(xhci->hcs_params1); i++) {
+ u8 port_speed = xhci->port_array[i];
+
+ /*
+ * Skip ports that don't have known speeds, or have duplicate
+ * Extended Capabilities port speed entries.
+ */
+ if (port_speed == 0 || port_speed == -1)
+ continue;
+
+ /*
+ * USB 3.0 ports are always under a USB 3.0 hub. USB 2.0 and
+ * 1.1 ports are under the USB 2.0 hub. If the port speed
+ * matches the device speed, it's a similar speed port.
+ */
+ if ((port_speed == 0x03) == (udev->speed == USB_SPEED_SUPER))
+ num_similar_speed_ports++;
+ if (num_similar_speed_ports == faked_port_num)
+ /* Roothub ports are numbered from 1 to N */
+ return i+1;
+ }
+ return 0;
+}
+
/* Setup an xHCI virtual device for a Set Address command */
int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *udev)
{
struct xhci_virt_device *dev;
struct xhci_ep_ctx *ep0_ctx;
- struct usb_device *top_dev;
struct xhci_slot_ctx *slot_ctx;
struct xhci_input_control_ctx *ctrl_ctx;
+ u32 port_num;
+ struct usb_device *top_dev;
dev = xhci->devs[udev->slot_id];
/* Slot ID 0 is reserved */
@@ -863,16 +913,20 @@ int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *ud
BUG();
}
/* Find the root hub port this device is under */
+ port_num = xhci_find_real_port_number(xhci, udev);
+ if (!port_num)
+ return -EINVAL;
+ slot_ctx->dev_info2 |= (u32) ROOT_HUB_PORT(port_num);
+ /* Set the port number in the virtual_device to the faked port number */
for (top_dev = udev; top_dev->parent && top_dev->parent->parent;
top_dev = top_dev->parent)
/* Found device below root hub */;
- slot_ctx->dev_info2 |= (u32) ROOT_HUB_PORT(top_dev->portnum);
dev->port = top_dev->portnum;
- xhci_dbg(xhci, "Set root hub portnum to %d\n", top_dev->portnum);
+ xhci_dbg(xhci, "Set root hub portnum to %d\n", port_num);
+ xhci_dbg(xhci, "Set fake root hub portnum to %d\n", dev->port);
- /* Is this a LS/FS device under a HS hub? */
- if ((udev->speed == USB_SPEED_LOW || udev->speed == USB_SPEED_FULL) &&
- udev->tt) {
+ /* Is this a LS/FS device under an external HS hub? */
+ if (udev->tt && udev->tt->hub->parent) {
slot_ctx->tt_info = udev->tt->hub->slot_id;
slot_ctx->tt_info |= udev->ttport << 8;
if (udev->tt->multi)
@@ -1452,7 +1506,8 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
xhci->page_size = 0;
xhci->page_shift = 0;
- xhci->bus_suspended = 0;
+ xhci->bus_state[0].bus_suspended = 0;
+ xhci->bus_state[1].bus_suspended = 0;
}
static int xhci_test_trb_in_td(struct xhci_hcd *xhci,
@@ -1748,6 +1803,20 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags)
}
xhci_dbg(xhci, "Found %u USB 2.0 ports and %u USB 3.0 ports.\n",
xhci->num_usb2_ports, xhci->num_usb3_ports);
+
+ /* Place limits on the number of roothub ports so that the hub
+ * descriptors aren't longer than the USB core will allocate.
+ */
+ if (xhci->num_usb3_ports > 15) {
+ xhci_dbg(xhci, "Limiting USB 3.0 roothub ports to 15.\n");
+ xhci->num_usb3_ports = 15;
+ }
+ if (xhci->num_usb2_ports > USB_MAXCHILDREN) {
+ xhci_dbg(xhci, "Limiting USB 2.0 roothub ports to %u.\n",
+ USB_MAXCHILDREN);
+ xhci->num_usb2_ports = USB_MAXCHILDREN;
+ }
+
/*
* Note we could have all USB 3.0 ports, or all USB 2.0 ports.
* Not sure how the USB core will handle a hub with no ports...
@@ -1772,6 +1841,8 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags)
"addr = %p\n", i,
xhci->usb2_ports[port_index]);
port_index++;
+ if (port_index == xhci->num_usb2_ports)
+ break;
}
}
if (xhci->num_usb3_ports) {
@@ -1790,6 +1861,8 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags)
"addr = %p\n", i,
xhci->usb3_ports[port_index]);
port_index++;
+ if (port_index == xhci->num_usb3_ports)
+ break;
}
}
return 0;
@@ -1971,8 +2044,10 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
init_completion(&xhci->addr_dev);
for (i = 0; i < MAX_HC_SLOTS; ++i)
xhci->devs[i] = NULL;
- for (i = 0; i < MAX_HC_PORTS; ++i)
- xhci->resume_done[i] = 0;
+ for (i = 0; i < USB_MAXCHILDREN; ++i) {
+ xhci->bus_state[0].resume_done[i] = 0;
+ xhci->bus_state[1].resume_done[i] = 0;
+ }
if (scratchpad_alloc(xhci, flags))
goto fail;
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index bb668a894ab9..ceea9f33491c 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -50,13 +50,45 @@ static int xhci_pci_reinit(struct xhci_hcd *xhci, struct pci_dev *pdev)
/* called during probe() after chip reset completes */
static int xhci_pci_setup(struct usb_hcd *hcd)
{
- struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+ struct xhci_hcd *xhci;
struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
int retval;
u32 temp;
hcd->self.sg_tablesize = TRBS_PER_SEGMENT - 2;
+ if (usb_hcd_is_primary_hcd(hcd)) {
+ xhci = kzalloc(sizeof(struct xhci_hcd), GFP_KERNEL);
+ if (!xhci)
+ return -ENOMEM;
+ *((struct xhci_hcd **) hcd->hcd_priv) = xhci;
+ xhci->main_hcd = hcd;
+ /* Mark the first roothub as being USB 2.0.
+ * The xHCI driver will register the USB 3.0 roothub.
+ */
+ hcd->speed = HCD_USB2;
+ hcd->self.root_hub->speed = USB_SPEED_HIGH;
+ /*
+ * USB 2.0 roothub under xHCI has an integrated TT,
+ * (rate matching hub) as opposed to having an OHCI/UHCI
+ * companion controller.
+ */
+ hcd->has_tt = 1;
+ } else {
+ /* xHCI private pointer was set in xhci_pci_probe for the second
+ * registered roothub.
+ */
+ xhci = hcd_to_xhci(hcd);
+ temp = xhci_readl(xhci, &xhci->cap_regs->hcc_params);
+ if (HCC_64BIT_ADDR(temp)) {
+ xhci_dbg(xhci, "Enabling 64-bit DMA addresses.\n");
+ dma_set_mask(hcd->self.controller, DMA_BIT_MASK(64));
+ } else {
+ dma_set_mask(hcd->self.controller, DMA_BIT_MASK(32));
+ }
+ return 0;
+ }
+
xhci->cap_regs = hcd->regs;
xhci->op_regs = hcd->regs +
HC_LENGTH(xhci_readl(xhci, &xhci->cap_regs->hc_capbase));
@@ -85,13 +117,13 @@ static int xhci_pci_setup(struct usb_hcd *hcd)
/* Make sure the HC is halted. */
retval = xhci_halt(xhci);
if (retval)
- return retval;
+ goto error;
xhci_dbg(xhci, "Resetting HCD\n");
/* Reset the internal HC memory state and registers. */
retval = xhci_reset(xhci);
if (retval)
- return retval;
+ goto error;
xhci_dbg(xhci, "Reset complete\n");
temp = xhci_readl(xhci, &xhci->cap_regs->hcc_params);
@@ -106,14 +138,85 @@ static int xhci_pci_setup(struct usb_hcd *hcd)
/* Initialize HCD and host controller data structures. */
retval = xhci_init(hcd);
if (retval)
- return retval;
+ goto error;
xhci_dbg(xhci, "Called HCD init\n");
pci_read_config_byte(pdev, XHCI_SBRN_OFFSET, &xhci->sbrn);
xhci_dbg(xhci, "Got SBRN %u\n", (unsigned int) xhci->sbrn);
/* Find any debug ports */
- return xhci_pci_reinit(xhci, pdev);
+ retval = xhci_pci_reinit(xhci, pdev);
+ if (!retval)
+ return retval;
+
+error:
+ kfree(xhci);
+ return retval;
+}
+
+/*
+ * We need to register our own PCI probe function (instead of the USB core's
+ * function) in order to create a second roothub under xHCI.
+ */
+static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+ int retval;
+ struct xhci_hcd *xhci;
+ struct hc_driver *driver;
+ struct usb_hcd *hcd;
+
+ driver = (struct hc_driver *)id->driver_data;
+ /* Register the USB 2.0 roothub.
+ * FIXME: USB core must know to register the USB 2.0 roothub first.
+ * This is sort of silly, because we could just set the HCD driver flags
+ * to say USB 2.0, but I'm not sure what the implications would be in
+ * the other parts of the HCD code.
+ */
+ retval = usb_hcd_pci_probe(dev, id);
+
+ if (retval)
+ return retval;
+
+ /* USB 2.0 roothub is stored in the PCI device now. */
+ hcd = dev_get_drvdata(&dev->dev);
+ xhci = hcd_to_xhci(hcd);
+ xhci->shared_hcd = usb_create_shared_hcd(driver, &dev->dev,
+ pci_name(dev), hcd);
+ if (!xhci->shared_hcd) {
+ retval = -ENOMEM;
+ goto dealloc_usb2_hcd;
+ }
+
+ /* Set the xHCI pointer before xhci_pci_setup() (aka hcd_driver.reset)
+ * is called by usb_add_hcd().
+ */
+ *((struct xhci_hcd **) xhci->shared_hcd->hcd_priv) = xhci;
+
+ retval = usb_add_hcd(xhci->shared_hcd, dev->irq,
+ IRQF_DISABLED | IRQF_SHARED);
+ if (retval)
+ goto put_usb3_hcd;
+ /* Roothub already marked as USB 3.0 speed */
+ return 0;
+
+put_usb3_hcd:
+ usb_put_hcd(xhci->shared_hcd);
+dealloc_usb2_hcd:
+ usb_hcd_pci_remove(dev);
+ return retval;
+}
+
+static void xhci_pci_remove(struct pci_dev *dev)
+{
+ struct xhci_hcd *xhci;
+
+ xhci = hcd_to_xhci(pci_get_drvdata(dev));
+ if (xhci->shared_hcd) {
+ usb_remove_hcd(xhci->shared_hcd);
+ usb_put_hcd(xhci->shared_hcd);
+ }
+ usb_hcd_pci_remove(dev);
+ kfree(xhci);
}
#ifdef CONFIG_PM
@@ -122,7 +225,8 @@ static int xhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup)
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
int retval = 0;
- if (hcd->state != HC_STATE_SUSPENDED)
+ if (hcd->state != HC_STATE_SUSPENDED ||
+ xhci->shared_hcd->state != HC_STATE_SUSPENDED)
return -EINVAL;
retval = xhci_suspend(xhci);
@@ -143,13 +247,13 @@ static int xhci_pci_resume(struct usb_hcd *hcd, bool hibernated)
static const struct hc_driver xhci_pci_hc_driver = {
.description = hcd_name,
.product_desc = "xHCI Host Controller",
- .hcd_priv_size = sizeof(struct xhci_hcd),
+ .hcd_priv_size = sizeof(struct xhci_hcd *),
/*
* generic hardware linkage
*/
.irq = xhci_irq,
- .flags = HCD_MEMORY | HCD_USB3,
+ .flags = HCD_MEMORY | HCD_USB3 | HCD_SHARED,
/*
* basic lifecycle operations
@@ -210,8 +314,8 @@ static struct pci_driver xhci_pci_driver = {
.name = (char *) hcd_name,
.id_table = pci_ids,
- .probe = usb_hcd_pci_probe,
- .remove = usb_hcd_pci_remove,
+ .probe = xhci_pci_probe,
+ .remove = xhci_pci_remove,
/* suspend and resume implemented later */
.shutdown = usb_hcd_pci_shutdown,
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 3289bf4832c9..cfc1ad92473f 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -380,10 +380,8 @@ static struct xhci_segment *find_trb_seg(
while (cur_seg->trbs > trb ||
&cur_seg->trbs[TRBS_PER_SEGMENT - 1] < trb) {
generic_trb = &cur_seg->trbs[TRBS_PER_SEGMENT - 1].generic;
- if ((generic_trb->field[3] & TRB_TYPE_BITMASK) ==
- TRB_TYPE(TRB_LINK) &&
- (generic_trb->field[3] & LINK_TOGGLE))
- *cycle_state = ~(*cycle_state) & 0x1;
+ if (generic_trb->field[3] & LINK_TOGGLE)
+ *cycle_state ^= 0x1;
cur_seg = cur_seg->next;
if (cur_seg == start_seg)
/* Looped over the entire list. Oops! */
@@ -497,18 +495,29 @@ void xhci_find_new_dequeue_state(struct xhci_hcd *xhci,
trb = &state->new_deq_ptr->generic;
if ((trb->field[3] & TRB_TYPE_BITMASK) == TRB_TYPE(TRB_LINK) &&
(trb->field[3] & LINK_TOGGLE))
- state->new_cycle_state = ~(state->new_cycle_state) & 0x1;
+ state->new_cycle_state ^= 0x1;
next_trb(xhci, ep_ring, &state->new_deq_seg, &state->new_deq_ptr);
+ /*
+ * If there is only one segment in a ring, find_trb_seg()'s while loop
+ * will not run, and it will return before it has a chance to see if it
+ * needs to toggle the cycle bit. It can't tell if the stalled transfer
+ * ended just before the link TRB on a one-segment ring, or if the TD
+ * wrapped around the top of the ring, because it doesn't have the TD in
+ * question. Look for the one-segment case where stalled TRB's address
+ * is greater than the new dequeue pointer address.
+ */
+ if (ep_ring->first_seg == ep_ring->first_seg->next &&
+ state->new_deq_ptr < dev->eps[ep_index].stopped_trb)
+ state->new_cycle_state ^= 0x1;
+ xhci_dbg(xhci, "Cycle state = 0x%x\n", state->new_cycle_state);
+
/* Don't update the ring cycle state for the producer (us). */
xhci_dbg(xhci, "New dequeue segment = %p (virtual)\n",
state->new_deq_seg);
addr = xhci_trb_virt_to_dma(state->new_deq_seg, state->new_deq_ptr);
xhci_dbg(xhci, "New dequeue pointer = 0x%llx (DMA)\n",
(unsigned long long) addr);
- xhci_dbg(xhci, "Setting dequeue pointer in internal ring state.\n");
- ep_ring->dequeue = state->new_deq_ptr;
- ep_ring->deq_seg = state->new_deq_seg;
}
static void td_to_noop(struct xhci_hcd *xhci, struct xhci_ring *ep_ring,
@@ -599,13 +608,14 @@ static inline void xhci_stop_watchdog_timer_in_irq(struct xhci_hcd *xhci,
static void xhci_giveback_urb_in_irq(struct xhci_hcd *xhci,
struct xhci_td *cur_td, int status, char *adjective)
{
- struct usb_hcd *hcd = xhci_to_hcd(xhci);
+ struct usb_hcd *hcd;
struct urb *urb;
struct urb_priv *urb_priv;
urb = cur_td->urb;
urb_priv = urb->hcpriv;
urb_priv->td_cnt++;
+ hcd = bus_to_hcd(urb->dev->bus);
/* Only giveback urb when this is the last td in urb */
if (urb_priv->td_cnt == urb_priv->length) {
@@ -824,8 +834,7 @@ void xhci_stop_endpoint_command_watchdog(unsigned long arg)
if (ret < 0) {
/* This is bad; the host is not responding to commands and it's
* not allowing itself to be halted. At least interrupts are
- * disabled, so we can set HC_STATE_HALT and notify the
- * USB core. But if we call usb_hc_died(), it will attempt to
+ * disabled. If we call usb_hc_died(), it will attempt to
* disconnect all device drivers under this host. Those
* disconnect() methods will wait for all URBs to be unlinked,
* so we must complete them.
@@ -870,9 +879,8 @@ void xhci_stop_endpoint_command_watchdog(unsigned long arg)
}
}
spin_unlock(&xhci->lock);
- xhci_to_hcd(xhci)->state = HC_STATE_HALT;
xhci_dbg(xhci, "Calling usb_hc_died()\n");
- usb_hc_died(xhci_to_hcd(xhci));
+ usb_hc_died(xhci_to_hcd(xhci)->primary_hcd);
xhci_dbg(xhci, "xHCI host controller is dead.\n");
}
@@ -951,9 +959,26 @@ static void handle_set_deq_completion(struct xhci_hcd *xhci,
} else {
xhci_dbg(xhci, "Successful Set TR Deq Ptr cmd, deq = @%08llx\n",
ep_ctx->deq);
+ if (xhci_trb_virt_to_dma(dev->eps[ep_index].queued_deq_seg,
+ dev->eps[ep_index].queued_deq_ptr) ==
+ (ep_ctx->deq & ~(EP_CTX_CYCLE_MASK))) {
+ /* Update the ring's dequeue segment and dequeue pointer
+ * to reflect the new position.
+ */
+ ep_ring->deq_seg = dev->eps[ep_index].queued_deq_seg;
+ ep_ring->dequeue = dev->eps[ep_index].queued_deq_ptr;
+ } else {
+ xhci_warn(xhci, "Mismatch between completed Set TR Deq "
+ "Ptr command & xHCI internal state.\n");
+ xhci_warn(xhci, "ep deq seg = %p, deq ptr = %p\n",
+ dev->eps[ep_index].queued_deq_seg,
+ dev->eps[ep_index].queued_deq_ptr);
+ }
}
dev->eps[ep_index].ep_state &= ~SET_DEQ_PENDING;
+ dev->eps[ep_index].queued_deq_seg = NULL;
+ dev->eps[ep_index].queued_deq_ptr = NULL;
/* Restart any rings with pending URBs */
ring_doorbell_for_active_rings(xhci, slot_id, ep_index);
}
@@ -1118,7 +1143,6 @@ bandwidth_change:
handle_set_deq_completion(xhci, event, xhci->cmd_ring->dequeue);
break;
case TRB_TYPE(TRB_CMD_NOOP):
- ++xhci->noops_handled;
break;
case TRB_TYPE(TRB_RESET_EP):
handle_reset_ep_completion(xhci, event, xhci->cmd_ring->dequeue);
@@ -1162,15 +1186,55 @@ static void handle_vendor_event(struct xhci_hcd *xhci,
handle_cmd_completion(xhci, &event->event_cmd);
}
+/* @port_id: the one-based port ID from the hardware (indexed from array of all
+ * port registers -- USB 3.0 and USB 2.0).
+ *
+ * Returns a zero-based port number, which is suitable for indexing into each of
+ * the split roothubs' port arrays and bus state arrays.
+ */
+static unsigned int find_faked_portnum_from_hw_portnum(struct usb_hcd *hcd,
+ struct xhci_hcd *xhci, u32 port_id)
+{
+ unsigned int i;
+ unsigned int num_similar_speed_ports = 0;
+
+ /* port_id from the hardware is 1-based, but port_array[], usb3_ports[],
+ * and usb2_ports are 0-based indexes. Count the number of similar
+ * speed ports, up to 1 port before this port.
+ */
+ for (i = 0; i < (port_id - 1); i++) {
+ u8 port_speed = xhci->port_array[i];
+
+ /*
+ * Skip ports that don't have known speeds, or have duplicate
+ * Extended Capabilities port speed entries.
+ */
+ if (port_speed == 0 || port_speed == -1)
+ continue;
+
+ /*
+ * USB 3.0 ports are always under a USB 3.0 hub. USB 2.0 and
+ * 1.1 ports are under the USB 2.0 hub. If the port speed
+ * matches the device speed, it's a similar speed port.
+ */
+ if ((port_speed == 0x03) == (hcd->speed == HCD_USB3))
+ num_similar_speed_ports++;
+ }
+ return num_similar_speed_ports;
+}
+
static void handle_port_status(struct xhci_hcd *xhci,
union xhci_trb *event)
{
- struct usb_hcd *hcd = xhci_to_hcd(xhci);
+ struct usb_hcd *hcd;
u32 port_id;
u32 temp, temp1;
- u32 __iomem *addr;
- int ports;
+ int max_ports;
int slot_id;
+ unsigned int faked_port_index;
+ u8 major_revision;
+ struct xhci_bus_state *bus_state;
+ u32 __iomem **port_array;
/* Port status change events always have a successful completion code */
if (GET_COMP_CODE(event->generic.field[2]) != COMP_SUCCESS) {
@@ -1180,14 +1244,50 @@ static void handle_port_status(struct xhci_hcd *xhci,
port_id = GET_PORT_ID(event->generic.field[0]);
xhci_dbg(xhci, "Port Status Change Event for port %d\n", port_id);
- ports = HCS_MAX_PORTS(xhci->hcs_params1);
- if ((port_id <= 0) || (port_id > ports)) {
+ max_ports = HCS_MAX_PORTS(xhci->hcs_params1);
+ if ((port_id <= 0) || (port_id > max_ports)) {
xhci_warn(xhci, "Invalid port id %d\n", port_id);
goto cleanup;
}
- addr = &xhci->op_regs->port_status_base + NUM_PORT_REGS * (port_id - 1);
- temp = xhci_readl(xhci, addr);
+ /* Figure out which usb_hcd this port is attached to:
+ * is it a USB 3.0 port or a USB 2.0/1.1 port?
+ */
+ major_revision = xhci->port_array[port_id - 1];
+ if (major_revision == 0) {
+ xhci_warn(xhci, "Event for port %u not in "
+ "Extended Capabilities, ignoring.\n",
+ port_id);
+ goto cleanup;
+ }
+ if (major_revision == (u8) -1) {
+ xhci_warn(xhci, "Event for port %u duplicated in"
+ "Extended Capabilities, ignoring.\n",
+ port_id);
+ goto cleanup;
+ }
+
+ /*
+ * Hardware port IDs reported by a Port Status Change Event include USB
+ * 3.0 and USB 2.0 ports. We want to check if the port has reported a
+ * resume event, but we first need to translate the hardware port ID
+ * into the index into the ports on the correct split roothub, and the
+ * correct bus_state structure.
+ */
+ /* Find the right roothub. */
+ hcd = xhci_to_hcd(xhci);
+ if ((major_revision == 0x03) != (hcd->speed == HCD_USB3))
+ hcd = xhci->shared_hcd;
+ bus_state = &xhci->bus_state[hcd_index(hcd)];
+ if (hcd->speed == HCD_USB3)
+ port_array = xhci->usb3_ports;
+ else
+ port_array = xhci->usb2_ports;
+ /* Find the faked port hub number */
+ faked_port_index = find_faked_portnum_from_hw_portnum(hcd, xhci,
+ port_id);
+
+ temp = xhci_readl(xhci, port_array[faked_port_index]);
if (hcd->state == HC_STATE_SUSPENDED) {
xhci_dbg(xhci, "resume root hub\n");
usb_hcd_resume_root_hub(hcd);
@@ -1207,8 +1307,9 @@ static void handle_port_status(struct xhci_hcd *xhci,
temp = xhci_port_state_to_neutral(temp);
temp &= ~PORT_PLS_MASK;
temp |= PORT_LINK_STROBE | XDEV_U0;
- xhci_writel(xhci, temp, addr);
- slot_id = xhci_find_slot_id_by_port(xhci, port_id);
+ xhci_writel(xhci, temp, port_array[faked_port_index]);
+ slot_id = xhci_find_slot_id_by_port(hcd, xhci,
+ faked_port_index);
if (!slot_id) {
xhci_dbg(xhci, "slot_id is zero\n");
goto cleanup;
@@ -1216,16 +1317,16 @@ static void handle_port_status(struct xhci_hcd *xhci,
xhci_ring_device(xhci, slot_id);
xhci_dbg(xhci, "resume SS port %d finished\n", port_id);
/* Clear PORT_PLC */
- temp = xhci_readl(xhci, addr);
+ temp = xhci_readl(xhci, port_array[faked_port_index]);
temp = xhci_port_state_to_neutral(temp);
temp |= PORT_PLC;
- xhci_writel(xhci, temp, addr);
+ xhci_writel(xhci, temp, port_array[faked_port_index]);
} else {
xhci_dbg(xhci, "resume HS port %d\n", port_id);
- xhci->resume_done[port_id - 1] = jiffies +
+ bus_state->resume_done[faked_port_index] = jiffies +
msecs_to_jiffies(20);
mod_timer(&hcd->rh_timer,
- xhci->resume_done[port_id - 1]);
+ bus_state->resume_done[faked_port_index]);
/* Do the rest in GetPortStatus */
}
}
@@ -1236,7 +1337,7 @@ cleanup:
spin_unlock(&xhci->lock);
/* Pass this up to the core */
- usb_hcd_poll_rh_status(xhci_to_hcd(xhci));
+ usb_hcd_poll_rh_status(hcd);
spin_lock(&xhci->lock);
}
@@ -1990,12 +2091,12 @@ cleanup:
trb_comp_code != COMP_BABBLE))
xhci_urb_free_priv(xhci, urb_priv);
- usb_hcd_unlink_urb_from_ep(xhci_to_hcd(xhci), urb);
+ usb_hcd_unlink_urb_from_ep(bus_to_hcd(urb->dev->bus), urb);
xhci_dbg(xhci, "Giveback URB %p, len = %d, "
"status = %d\n",
urb, urb->actual_length, status);
spin_unlock(&xhci->lock);
- usb_hcd_giveback_urb(xhci_to_hcd(xhci), urb, status);
+ usb_hcd_giveback_urb(bus_to_hcd(urb->dev->bus), urb, status);
spin_lock(&xhci->lock);
}
@@ -2119,7 +2220,6 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd)
xhci_warn(xhci, "WARNING: Host System Error\n");
xhci_halt(xhci);
hw_died:
- xhci_to_hcd(xhci)->state = HC_STATE_HALT;
spin_unlock(&xhci->lock);
return -ESHUTDOWN;
}
@@ -2187,8 +2287,12 @@ hw_died:
irqreturn_t xhci_msi_irq(int irq, struct usb_hcd *hcd)
{
irqreturn_t ret;
+ struct xhci_hcd *xhci;
+ xhci = hcd_to_xhci(hcd);
set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
+ if (xhci->shared_hcd)
+ set_bit(HCD_FLAG_SAW_IRQ, &xhci->shared_hcd->flags);
ret = xhci_irq(hcd);
@@ -2332,7 +2436,7 @@ static int prepare_transfer(struct xhci_hcd *xhci,
INIT_LIST_HEAD(&td->cancelled_td_list);
if (td_index == 0) {
- ret = usb_hcd_link_urb_to_ep(xhci_to_hcd(xhci), urb);
+ ret = usb_hcd_link_urb_to_ep(bus_to_hcd(urb->dev->bus), urb);
if (unlikely(ret)) {
xhci_urb_free_priv(xhci, urb_priv);
urb->hcpriv = NULL;
@@ -3131,24 +3235,6 @@ static int queue_command(struct xhci_hcd *xhci, u32 field1, u32 field2,
return 0;
}
-/* Queue a no-op command on the command ring */
-static int queue_cmd_noop(struct xhci_hcd *xhci)
-{
- return queue_command(xhci, 0, 0, 0, TRB_TYPE(TRB_CMD_NOOP), false);
-}
-
-/*
- * Place a no-op command on the command ring to test the command and
- * event ring.
- */
-void *xhci_setup_one_noop(struct xhci_hcd *xhci)
-{
- if (queue_cmd_noop(xhci) < 0)
- return NULL;
- xhci->noops_submitted++;
- return xhci_ring_cmd_db;
-}
-
/* Queue a slot enable or disable request on the command ring */
int xhci_queue_slot_control(struct xhci_hcd *xhci, u32 trb_type, u32 slot_id)
{
@@ -3229,6 +3315,7 @@ static int queue_set_tr_deq(struct xhci_hcd *xhci, int slot_id,
u32 trb_ep_index = EP_ID_FOR_TRB(ep_index);
u32 trb_stream_id = STREAM_ID_FOR_TRB(stream_id);
u32 type = TRB_TYPE(TRB_SET_DEQ);
+ struct xhci_virt_ep *ep;
addr = xhci_trb_virt_to_dma(deq_seg, deq_ptr);
if (addr == 0) {
@@ -3237,6 +3324,14 @@ static int queue_set_tr_deq(struct xhci_hcd *xhci, int slot_id,
deq_seg, deq_ptr);
return 0;
}
+ ep = &xhci->devs[slot_id]->eps[ep_index];
+ if ((ep->ep_state & SET_DEQ_PENDING)) {
+ xhci_warn(xhci, "WARN Cannot submit Set TR Deq Ptr\n");
+ xhci_warn(xhci, "A Set TR Deq Ptr command is pending.\n");
+ return 0;
+ }
+ ep->queued_deq_seg = deq_seg;
+ ep->queued_deq_ptr = deq_ptr;
return queue_command(xhci, lower_32_bits(addr) | cycle_state,
upper_32_bits(addr), trb_stream_id,
trb_slot_id | trb_ep_index | type, false);
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 2083fc2179b2..9a3645fd759b 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -93,17 +93,20 @@ void xhci_quiesce(struct xhci_hcd *xhci)
*
* Disable any IRQs and clear the run/stop bit.
* HC will complete any current and actively pipelined transactions, and
- * should halt within 16 microframes of the run/stop bit being cleared.
+ * should halt within 16 ms of the run/stop bit being cleared.
* Read HC Halted bit in the status register to see when the HC is finished.
- * XXX: shouldn't we set HC_STATE_HALT here somewhere?
*/
int xhci_halt(struct xhci_hcd *xhci)
{
+ int ret;
xhci_dbg(xhci, "// Halt the HC\n");
xhci_quiesce(xhci);
- return handshake(xhci, &xhci->op_regs->status,
+ ret = handshake(xhci, &xhci->op_regs->status,
STS_HALT, STS_HALT, XHCI_MAX_HALT_USEC);
+ if (!ret)
+ xhci->xhc_state |= XHCI_STATE_HALTED;
+ return ret;
}
/*
@@ -130,11 +133,13 @@ static int xhci_start(struct xhci_hcd *xhci)
xhci_err(xhci, "Host took too long to start, "
"waited %u microseconds.\n",
XHCI_MAX_HALT_USEC);
+ if (!ret)
+ xhci->xhc_state &= ~XHCI_STATE_HALTED;
return ret;
}
/*
- * Reset a halted HC, and set the internal HC state to HC_STATE_HALT.
+ * Reset a halted HC.
*
* This resets pipelines, timers, counters, state machines, etc.
* Transactions will be terminated immediately, and operational registers
@@ -156,8 +161,6 @@ int xhci_reset(struct xhci_hcd *xhci)
command = xhci_readl(xhci, &xhci->op_regs->command);
command |= CMD_RESET;
xhci_writel(xhci, command, &xhci->op_regs->command);
- /* XXX: Why does EHCI set this here? Shouldn't other code do this? */
- xhci_to_hcd(xhci)->state = HC_STATE_HALT;
ret = handshake(xhci, &xhci->op_regs->command,
CMD_RESET, 0, 250 * 1000);
@@ -350,7 +353,6 @@ static void xhci_event_ring_work(unsigned long arg)
temp = xhci_readl(xhci, &xhci->ir_set->irq_pending);
xhci_dbg(xhci, "ir_set 0 pending = 0x%x\n", temp);
- xhci_dbg(xhci, "No-op commands handled = %d\n", xhci->noops_handled);
xhci_dbg(xhci, "HC error bitmask = 0x%x\n", xhci->error_bitmask);
xhci->error_bitmask = 0;
xhci_dbg(xhci, "Event ring:\n");
@@ -370,10 +372,6 @@ static void xhci_event_ring_work(unsigned long arg)
xhci_dbg_ep_rings(xhci, i, j, &xhci->devs[i]->eps[j]);
}
}
-
- if (xhci->noops_submitted != NUM_TEST_NOOPS)
- if (xhci_setup_one_noop(xhci))
- xhci_ring_cmd_db(xhci);
spin_unlock_irqrestore(&xhci->lock, flags);
if (!xhci->zombie)
@@ -383,6 +381,21 @@ static void xhci_event_ring_work(unsigned long arg)
}
#endif
+static int xhci_run_finished(struct xhci_hcd *xhci)
+{
+ if (xhci_start(xhci)) {
+ xhci_halt(xhci);
+ return -ENODEV;
+ }
+ xhci->shared_hcd->state = HC_STATE_RUNNING;
+
+ if (xhci->quirks & XHCI_NEC_HOST)
+ xhci_ring_cmd_db(xhci);
+
+ xhci_dbg(xhci, "Finished xhci_run for USB3 roothub\n");
+ return 0;
+}
+
/*
* Start the HC after it was halted.
*
@@ -402,9 +415,14 @@ int xhci_run(struct usb_hcd *hcd)
u32 ret;
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller);
- void (*doorbell)(struct xhci_hcd *) = NULL;
+
+ /* Start the xHCI host controller running only after the USB 2.0 roothub
+ * is setup.
+ */
hcd->uses_new_polling = 1;
+ if (!usb_hcd_is_primary_hcd(hcd))
+ return xhci_run_finished(xhci);
xhci_dbg(xhci, "xhci_run\n");
/* unregister the legacy interrupt */
@@ -461,7 +479,6 @@ int xhci_run(struct usb_hcd *hcd)
xhci_writel(xhci, temp, &xhci->ir_set->irq_control);
/* Set the HCD state before we enable the irqs */
- hcd->state = HC_STATE_RUNNING;
temp = xhci_readl(xhci, &xhci->op_regs->command);
temp |= (CMD_EIE);
xhci_dbg(xhci, "// Enable interrupts, cmd = 0x%x.\n",
@@ -475,24 +492,27 @@ int xhci_run(struct usb_hcd *hcd)
&xhci->ir_set->irq_pending);
xhci_print_ir_set(xhci, 0);
- if (NUM_TEST_NOOPS > 0)
- doorbell = xhci_setup_one_noop(xhci);
if (xhci->quirks & XHCI_NEC_HOST)
xhci_queue_vendor_command(xhci, 0, 0, 0,
TRB_TYPE(TRB_NEC_GET_FW));
- if (xhci_start(xhci)) {
- xhci_halt(xhci);
- return -ENODEV;
- }
+ xhci_dbg(xhci, "Finished xhci_run for USB2 roothub\n");
+ return 0;
+}
- if (doorbell)
- (*doorbell)(xhci);
- if (xhci->quirks & XHCI_NEC_HOST)
- xhci_ring_cmd_db(xhci);
+static void xhci_only_stop_hcd(struct usb_hcd *hcd)
+{
+ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
- xhci_dbg(xhci, "Finished xhci_run\n");
- return 0;
+ spin_lock_irq(&xhci->lock);
+ xhci_halt(xhci);
+
+ /* The shared_hcd is going to be deallocated shortly (the USB core only
+ * calls this function when allocation fails in usb_add_hcd(), or
+ * usb_remove_hcd() is called). So we need to unset xHCI's pointer.
+ */
+ xhci->shared_hcd = NULL;
+ spin_unlock_irq(&xhci->lock);
}
/*
@@ -509,7 +529,15 @@ void xhci_stop(struct usb_hcd *hcd)
u32 temp;
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+ if (!usb_hcd_is_primary_hcd(hcd)) {
+ xhci_only_stop_hcd(xhci->shared_hcd);
+ return;
+ }
+
spin_lock_irq(&xhci->lock);
+ /* Make sure the xHC is halted for a USB3 roothub
+ * (xhci_stop() could be called as part of failed init).
+ */
xhci_halt(xhci);
xhci_reset(xhci);
spin_unlock_irq(&xhci->lock);
@@ -542,6 +570,8 @@ void xhci_stop(struct usb_hcd *hcd)
* This is called when the machine is rebooting or halting. We assume that the
* machine will be powered off, and the HC's internal state will be reset.
* Don't bother to free memory.
+ *
+ * This will only ever be called with the main usb_hcd (the USB3 roothub).
*/
void xhci_shutdown(struct usb_hcd *hcd)
{
@@ -657,6 +687,7 @@ int xhci_suspend(struct xhci_hcd *xhci)
spin_lock_irq(&xhci->lock);
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+ clear_bit(HCD_FLAG_HW_ACCESSIBLE, &xhci->shared_hcd->flags);
/* step 1: stop endpoint */
/* skipped assuming that port suspend has done */
@@ -706,10 +737,15 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated)
{
u32 command, temp = 0;
struct usb_hcd *hcd = xhci_to_hcd(xhci);
- int old_state, retval;
+ struct usb_hcd *secondary_hcd;
+ int retval;
- old_state = hcd->state;
- if (time_before(jiffies, xhci->next_statechange))
+ /* Wait a bit if either of the roothubs need to settle from the
+ * transistion into bus suspend.
+ */
+ if (time_before(jiffies, xhci->bus_state[0].next_statechange) ||
+ time_before(jiffies,
+ xhci->bus_state[1].next_statechange))
msleep(100);
spin_lock_irq(&xhci->lock);
@@ -762,16 +798,34 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated)
xhci_dbg(xhci, "xhci_stop completed - status = %x\n",
xhci_readl(xhci, &xhci->op_regs->status));
- xhci_dbg(xhci, "Initialize the HCD\n");
- retval = xhci_init(hcd);
+ /* USB core calls the PCI reinit and start functions twice:
+ * first with the primary HCD, and then with the secondary HCD.
+ * If we don't do the same, the host will never be started.
+ */
+ if (!usb_hcd_is_primary_hcd(hcd))
+ secondary_hcd = hcd;
+ else
+ secondary_hcd = xhci->shared_hcd;
+
+ xhci_dbg(xhci, "Initialize the xhci_hcd\n");
+ retval = xhci_init(hcd->primary_hcd);
if (retval)
return retval;
+ xhci_dbg(xhci, "Start the primary HCD\n");
+ retval = xhci_run(hcd->primary_hcd);
+ if (retval)
+ goto failed_restart;
- xhci_dbg(xhci, "Start the HCD\n");
- retval = xhci_run(hcd);
- if (!retval)
+ xhci_dbg(xhci, "Start the secondary HCD\n");
+ retval = xhci_run(secondary_hcd);
+ if (!retval) {
set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+ set_bit(HCD_FLAG_HW_ACCESSIBLE,
+ &xhci->shared_hcd->flags);
+ }
+failed_restart:
hcd->state = HC_STATE_SUSPENDED;
+ xhci->shared_hcd->state = HC_STATE_SUSPENDED;
return retval;
}
@@ -792,10 +846,7 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated)
*/
set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
- if (!hibernated)
- hcd->state = old_state;
- else
- hcd->state = HC_STATE_SUSPENDED;
+ set_bit(HCD_FLAG_HW_ACCESSIBLE, &xhci->shared_hcd->flags);
spin_unlock_irq(&xhci->lock);
return 0;
@@ -1167,13 +1218,13 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
if (ret || !urb->hcpriv)
goto done;
temp = xhci_readl(xhci, &xhci->op_regs->status);
- if (temp == 0xffffffff) {
+ if (temp == 0xffffffff || (xhci->xhc_state & XHCI_STATE_HALTED)) {
xhci_dbg(xhci, "HW died, freeing TD.\n");
urb_priv = urb->hcpriv;
usb_hcd_unlink_urb_from_ep(hcd, urb);
spin_unlock_irqrestore(&xhci->lock, flags);
- usb_hcd_giveback_urb(xhci_to_hcd(xhci), urb, -ESHUTDOWN);
+ usb_hcd_giveback_urb(hcd, urb, -ESHUTDOWN);
xhci_urb_free_priv(xhci, urb_priv);
return ret;
}
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 7f127df6dd55..711de253bc0f 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -644,6 +644,9 @@ struct xhci_ep_ctx {
#define AVG_TRB_LENGTH_FOR_EP(p) ((p) & 0xffff)
#define MAX_ESIT_PAYLOAD_FOR_EP(p) (((p) & 0xffff) << 16)
+/* deq bitmasks */
+#define EP_CTX_CYCLE_MASK (1 << 0)
+
/**
* struct xhci_input_control_context
@@ -746,6 +749,12 @@ struct xhci_virt_ep {
struct timer_list stop_cmd_timer;
int stop_cmds_pending;
struct xhci_hcd *xhci;
+ /* Dequeue pointer and dequeue segment for a submitted Set TR Dequeue
+ * command. We'll need to update the ring's dequeue segment and dequeue
+ * pointer after the command completes.
+ */
+ struct xhci_segment *queued_deq_seg;
+ union xhci_trb *queued_deq_ptr;
/*
* Sometimes the xHC can not process isochronous endpoint ring quickly
* enough, and it will miss some isoc tds on the ring and generate
@@ -1161,8 +1170,29 @@ struct s3_save {
u64 erst_dequeue;
};
+struct xhci_bus_state {
+ unsigned long bus_suspended;
+ unsigned long next_statechange;
+
+ /* Port suspend arrays are indexed by the portnum of the fake roothub */
+ /* ports suspend status arrays - max 31 ports for USB2, 15 for USB3 */
+ u32 port_c_suspend;
+ u32 suspended_ports;
+ unsigned long resume_done[USB_MAXCHILDREN];
+};
+
+static inline unsigned int hcd_index(struct usb_hcd *hcd)
+{
+ if (hcd->speed == HCD_USB3)
+ return 0;
+ else
+ return 1;
+}
+
/* There is one ehci_hci structure per controller */
struct xhci_hcd {
+ struct usb_hcd *main_hcd;
+ struct usb_hcd *shared_hcd;
/* glue to PCI and HCD framework */
struct xhci_cap_regs __iomem *cap_regs;
struct xhci_op_regs __iomem *op_regs;
@@ -1224,9 +1254,6 @@ struct xhci_hcd {
/* Host controller watchdog timer structures */
unsigned int xhc_state;
- unsigned long bus_suspended;
- unsigned long next_statechange;
-
u32 command;
struct s3_save s3;
/* Host controller is dying - not responding to commands. "I'm not dead yet!"
@@ -1242,18 +1269,15 @@ struct xhci_hcd {
* There are no reports of xHCI host controllers that display this issue.
*/
#define XHCI_STATE_DYING (1 << 0)
+#define XHCI_STATE_HALTED (1 << 1)
/* Statistics */
- int noops_submitted;
- int noops_handled;
int error_bitmask;
unsigned int quirks;
#define XHCI_LINK_TRB_QUIRK (1 << 0)
#define XHCI_RESET_EP_QUIRK (1 << 1)
#define XHCI_NEC_HOST (1 << 2)
- u32 port_c_suspend[8]; /* port suspend change*/
- u32 suspended_ports[8]; /* which ports are
- suspended */
- unsigned long resume_done[MAX_HC_PORTS];
+ /* There are two roothubs to keep track of bus suspend info for */
+ struct xhci_bus_state bus_state[2];
/* Is each xHCI roothub port a USB 3.0, USB 2.0, or USB 1.1 port? */
u8 *port_array;
/* Array of pointers to USB 3.0 PORTSC registers */
@@ -1264,18 +1288,15 @@ struct xhci_hcd {
unsigned int num_usb2_ports;
};
-/* For testing purposes */
-#define NUM_TEST_NOOPS 0
-
/* convert between an HCD pointer and the corresponding EHCI_HCD */
static inline struct xhci_hcd *hcd_to_xhci(struct usb_hcd *hcd)
{
- return (struct xhci_hcd *) (hcd->hcd_priv);
+ return *((struct xhci_hcd **) (hcd->hcd_priv));
}
static inline struct usb_hcd *xhci_to_hcd(struct xhci_hcd *xhci)
{
- return container_of((void *) xhci, struct usb_hcd, hcd_priv);
+ return xhci->main_hcd;
}
#ifdef CONFIG_USB_XHCI_HCD_DEBUGGING
@@ -1471,7 +1492,6 @@ struct xhci_segment *trb_in_td(struct xhci_segment *start_seg,
dma_addr_t suspect_dma);
int xhci_is_vendor_info_code(struct xhci_hcd *xhci, unsigned int trb_comp_code);
void xhci_ring_cmd_db(struct xhci_hcd *xhci);
-void *xhci_setup_one_noop(struct xhci_hcd *xhci);
int xhci_queue_slot_control(struct xhci_hcd *xhci, u32 trb_type, u32 slot_id);
int xhci_queue_address_device(struct xhci_hcd *xhci, dma_addr_t in_ctx_ptr,
u32 slot_id);
@@ -1525,7 +1545,8 @@ int xhci_bus_resume(struct usb_hcd *hcd);
#endif /* CONFIG_PM */
u32 xhci_port_state_to_neutral(u32 state);
-int xhci_find_slot_id_by_port(struct xhci_hcd *xhci, u16 port);
+int xhci_find_slot_id_by_port(struct usb_hcd *hcd, struct xhci_hcd *xhci,
+ u16 port);
void xhci_ring_device(struct xhci_hcd *xhci, int slot_id);
/* xHCI contexts */
diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c
index a35b427c0bac..388cc128072a 100644
--- a/drivers/usb/misc/usbtest.c
+++ b/drivers/usb/misc/usbtest.c
@@ -83,6 +83,8 @@ static struct usb_device *testdev_to_usbdev(struct usbtest_dev *test)
#define WARNING(tdev, fmt, args...) \
dev_warn(&(tdev)->intf->dev , fmt , ## args)
+#define GUARD_BYTE 0xA5
+
/*-------------------------------------------------------------------------*/
static int
@@ -186,11 +188,12 @@ static void simple_callback(struct urb *urb)
complete(urb->context);
}
-static struct urb *simple_alloc_urb(
+static struct urb *usbtest_alloc_urb(
struct usb_device *udev,
int pipe,
- unsigned long bytes
-)
+ unsigned long bytes,
+ unsigned transfer_flags,
+ unsigned offset)
{
struct urb *urb;
@@ -201,19 +204,46 @@ static struct urb *simple_alloc_urb(
urb->interval = (udev->speed == USB_SPEED_HIGH)
? (INTERRUPT_RATE << 3)
: INTERRUPT_RATE;
- urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
+ urb->transfer_flags = transfer_flags;
if (usb_pipein(pipe))
urb->transfer_flags |= URB_SHORT_NOT_OK;
- urb->transfer_buffer = usb_alloc_coherent(udev, bytes, GFP_KERNEL,
- &urb->transfer_dma);
+
+ if (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)
+ urb->transfer_buffer = usb_alloc_coherent(udev, bytes + offset,
+ GFP_KERNEL, &urb->transfer_dma);
+ else
+ urb->transfer_buffer = kmalloc(bytes + offset, GFP_KERNEL);
+
if (!urb->transfer_buffer) {
usb_free_urb(urb);
- urb = NULL;
- } else
- memset(urb->transfer_buffer, 0, bytes);
+ return NULL;
+ }
+
+ /* To test unaligned transfers add an offset and fill the
+ unused memory with a guard value */
+ if (offset) {
+ memset(urb->transfer_buffer, GUARD_BYTE, offset);
+ urb->transfer_buffer += offset;
+ if (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)
+ urb->transfer_dma += offset;
+ }
+
+ /* For inbound transfers use guard byte so that test fails if
+ data not correctly copied */
+ memset(urb->transfer_buffer,
+ usb_pipein(urb->pipe) ? GUARD_BYTE : 0,
+ bytes);
return urb;
}
+static struct urb *simple_alloc_urb(
+ struct usb_device *udev,
+ int pipe,
+ unsigned long bytes)
+{
+ return usbtest_alloc_urb(udev, pipe, bytes, URB_NO_TRANSFER_DMA_MAP, 0);
+}
+
static unsigned pattern;
static unsigned mod_pattern;
module_param_named(pattern, mod_pattern, uint, S_IRUGO | S_IWUSR);
@@ -238,13 +268,38 @@ static inline void simple_fill_buf(struct urb *urb)
}
}
-static inline int simple_check_buf(struct usbtest_dev *tdev, struct urb *urb)
+static inline unsigned buffer_offset(void *buf)
+{
+ return (unsigned)buf & (ARCH_KMALLOC_MINALIGN - 1);
+}
+
+static int check_guard_bytes(struct usbtest_dev *tdev, struct urb *urb)
+{
+ u8 *buf = urb->transfer_buffer;
+ u8 *guard = buf - buffer_offset(buf);
+ unsigned i;
+
+ for (i = 0; guard < buf; i++, guard++) {
+ if (*guard != GUARD_BYTE) {
+ ERROR(tdev, "guard byte[%d] %d (not %d)\n",
+ i, *guard, GUARD_BYTE);
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+
+static int simple_check_buf(struct usbtest_dev *tdev, struct urb *urb)
{
unsigned i;
u8 expected;
u8 *buf = urb->transfer_buffer;
unsigned len = urb->actual_length;
+ int ret = check_guard_bytes(tdev, urb);
+ if (ret)
+ return ret;
+
for (i = 0; i < len; i++, buf++) {
switch (pattern) {
/* all-zeroes has no synchronization issues */
@@ -274,8 +329,16 @@ static inline int simple_check_buf(struct usbtest_dev *tdev, struct urb *urb)
static void simple_free_urb(struct urb *urb)
{
- usb_free_coherent(urb->dev, urb->transfer_buffer_length,
- urb->transfer_buffer, urb->transfer_dma);
+ unsigned offset = buffer_offset(urb->transfer_buffer);
+
+ if (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)
+ usb_free_coherent(
+ urb->dev,
+ urb->transfer_buffer_length + offset,
+ urb->transfer_buffer - offset,
+ urb->transfer_dma - offset);
+ else
+ kfree(urb->transfer_buffer - offset);
usb_free_urb(urb);
}
@@ -1256,7 +1319,7 @@ done:
* try whatever we're told to try.
*/
static int ctrl_out(struct usbtest_dev *dev,
- unsigned count, unsigned length, unsigned vary)
+ unsigned count, unsigned length, unsigned vary, unsigned offset)
{
unsigned i, j, len;
int retval;
@@ -1267,10 +1330,11 @@ static int ctrl_out(struct usbtest_dev *dev,
if (length < 1 || length > 0xffff || vary >= length)
return -EINVAL;
- buf = kmalloc(length, GFP_KERNEL);
+ buf = kmalloc(length + offset, GFP_KERNEL);
if (!buf)
return -ENOMEM;
+ buf += offset;
udev = testdev_to_usbdev(dev);
len = length;
retval = 0;
@@ -1337,7 +1401,7 @@ static int ctrl_out(struct usbtest_dev *dev,
ERROR(dev, "ctrl_out %s failed, code %d, count %d\n",
what, retval, i);
- kfree(buf);
+ kfree(buf - offset);
return retval;
}
@@ -1373,6 +1437,8 @@ static void iso_callback(struct urb *urb)
ctx->errors += urb->number_of_packets;
else if (urb->actual_length != urb->transfer_buffer_length)
ctx->errors++;
+ else if (check_guard_bytes(ctx->dev, urb) != 0)
+ ctx->errors++;
if (urb->status == 0 && ctx->count > (ctx->pending - 1)
&& !ctx->submit_error) {
@@ -1408,7 +1474,8 @@ static struct urb *iso_alloc_urb(
struct usb_device *udev,
int pipe,
struct usb_endpoint_descriptor *desc,
- long bytes
+ long bytes,
+ unsigned offset
)
{
struct urb *urb;
@@ -1428,13 +1495,24 @@ static struct urb *iso_alloc_urb(
urb->number_of_packets = packets;
urb->transfer_buffer_length = bytes;
- urb->transfer_buffer = usb_alloc_coherent(udev, bytes, GFP_KERNEL,
- &urb->transfer_dma);
+ urb->transfer_buffer = usb_alloc_coherent(udev, bytes + offset,
+ GFP_KERNEL,
+ &urb->transfer_dma);
if (!urb->transfer_buffer) {
usb_free_urb(urb);
return NULL;
}
- memset(urb->transfer_buffer, 0, bytes);
+ if (offset) {
+ memset(urb->transfer_buffer, GUARD_BYTE, offset);
+ urb->transfer_buffer += offset;
+ urb->transfer_dma += offset;
+ }
+ /* For inbound transfers use guard byte so that test fails if
+ data not correctly copied */
+ memset(urb->transfer_buffer,
+ usb_pipein(urb->pipe) ? GUARD_BYTE : 0,
+ bytes);
+
for (i = 0; i < packets; i++) {
/* here, only the last packet will be short */
urb->iso_frame_desc[i].length = min((unsigned) bytes, maxp);
@@ -1452,7 +1530,7 @@ static struct urb *iso_alloc_urb(
static int
test_iso_queue(struct usbtest_dev *dev, struct usbtest_param *param,
- int pipe, struct usb_endpoint_descriptor *desc)
+ int pipe, struct usb_endpoint_descriptor *desc, unsigned offset)
{
struct iso_context context;
struct usb_device *udev;
@@ -1480,7 +1558,7 @@ test_iso_queue(struct usbtest_dev *dev, struct usbtest_param *param,
for (i = 0; i < param->sglen; i++) {
urbs[i] = iso_alloc_urb(udev, pipe, desc,
- param->length);
+ param->length, offset);
if (!urbs[i]) {
status = -ENOMEM;
goto fail;
@@ -1542,6 +1620,26 @@ fail:
return status;
}
+static int test_unaligned_bulk(
+ struct usbtest_dev *tdev,
+ int pipe,
+ unsigned length,
+ int iterations,
+ unsigned transfer_flags,
+ const char *label)
+{
+ int retval;
+ struct urb *urb = usbtest_alloc_urb(
+ testdev_to_usbdev(tdev), pipe, length, transfer_flags, 1);
+
+ if (!urb)
+ return -ENOMEM;
+
+ retval = simple_io(tdev, urb, iterations, 0, 0, label);
+ simple_free_urb(urb);
+ return retval;
+}
+
/*-------------------------------------------------------------------------*/
/* We only have this one interface to user space, through usbfs.
@@ -1843,7 +1941,7 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf)
realworld ? 1 : 0, param->length,
param->vary);
retval = ctrl_out(dev, param->iterations,
- param->length, param->vary);
+ param->length, param->vary, 0);
break;
/* iso write tests */
@@ -1856,7 +1954,7 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf)
param->sglen, param->length);
/* FIRMWARE: iso sink */
retval = test_iso_queue(dev, param,
- dev->out_iso_pipe, dev->iso_out);
+ dev->out_iso_pipe, dev->iso_out, 0);
break;
/* iso read tests */
@@ -1869,13 +1967,103 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf)
param->sglen, param->length);
/* FIRMWARE: iso source */
retval = test_iso_queue(dev, param,
- dev->in_iso_pipe, dev->iso_in);
+ dev->in_iso_pipe, dev->iso_in, 0);
break;
/* FIXME unlink from queue (ring with N urbs) */
/* FIXME scatterlist cancel (needs helper thread) */
+ /* Tests for bulk I/O using DMA mapping by core and odd address */
+ case 17:
+ if (dev->out_pipe == 0)
+ break;
+ dev_info(&intf->dev,
+ "TEST 17: write odd addr %d bytes %u times core map\n",
+ param->length, param->iterations);
+
+ retval = test_unaligned_bulk(
+ dev, dev->out_pipe,
+ param->length, param->iterations,
+ 0, "test17");
+ break;
+
+ case 18:
+ if (dev->in_pipe == 0)
+ break;
+ dev_info(&intf->dev,
+ "TEST 18: read odd addr %d bytes %u times core map\n",
+ param->length, param->iterations);
+
+ retval = test_unaligned_bulk(
+ dev, dev->in_pipe,
+ param->length, param->iterations,
+ 0, "test18");
+ break;
+
+ /* Tests for bulk I/O using premapped coherent buffer and odd address */
+ case 19:
+ if (dev->out_pipe == 0)
+ break;
+ dev_info(&intf->dev,
+ "TEST 19: write odd addr %d bytes %u times premapped\n",
+ param->length, param->iterations);
+
+ retval = test_unaligned_bulk(
+ dev, dev->out_pipe,
+ param->length, param->iterations,
+ URB_NO_TRANSFER_DMA_MAP, "test19");
+ break;
+
+ case 20:
+ if (dev->in_pipe == 0)
+ break;
+ dev_info(&intf->dev,
+ "TEST 20: read odd addr %d bytes %u times premapped\n",
+ param->length, param->iterations);
+
+ retval = test_unaligned_bulk(
+ dev, dev->in_pipe,
+ param->length, param->iterations,
+ URB_NO_TRANSFER_DMA_MAP, "test20");
+ break;
+
+ /* control write tests with unaligned buffer */
+ case 21:
+ if (!dev->info->ctrl_out)
+ break;
+ dev_info(&intf->dev,
+ "TEST 21: %d ep0out odd addr, %d..%d vary %d\n",
+ param->iterations,
+ realworld ? 1 : 0, param->length,
+ param->vary);
+ retval = ctrl_out(dev, param->iterations,
+ param->length, param->vary, 1);
+ break;
+
+ /* unaligned iso tests */
+ case 22:
+ if (dev->out_iso_pipe == 0 || param->sglen == 0)
+ break;
+ dev_info(&intf->dev,
+ "TEST 22: write %d iso odd, %d entries of %d bytes\n",
+ param->iterations,
+ param->sglen, param->length);
+ retval = test_iso_queue(dev, param,
+ dev->out_iso_pipe, dev->iso_out, 1);
+ break;
+
+ case 23:
+ if (dev->in_iso_pipe == 0 || param->sglen == 0)
+ break;
+ dev_info(&intf->dev,
+ "TEST 23: read %d iso odd, %d entries of %d bytes\n",
+ param->iterations,
+ param->sglen, param->length);
+ retval = test_iso_queue(dev, param,
+ dev->in_iso_pipe, dev->iso_in, 1);
+ break;
+
}
do_gettimeofday(&param->duration);
param->duration.tv_sec -= start.tv_sec;
diff --git a/drivers/usb/mon/mon_text.c b/drivers/usb/mon/mon_text.c
index a545d65f6e57..c302e1983c70 100644
--- a/drivers/usb/mon/mon_text.c
+++ b/drivers/usb/mon/mon_text.c
@@ -236,6 +236,9 @@ static void mon_text_event(struct mon_reader_text *rp, struct urb *urb,
fp++;
dp++;
}
+ /* Wasteful, but simple to understand: ISO 'C' is sparse. */
+ if (ev_type == 'C')
+ ep->length = urb->transfer_buffer_length;
}
ep->setup_flag = mon_text_get_setup(ep, urb, ev_type, rp->r.m_bus);
diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
index c292d5c499e7..a914010d9d12 100644
--- a/drivers/usb/musb/musb_core.c
+++ b/drivers/usb/musb/musb_core.c
@@ -1950,31 +1950,6 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
goto fail0;
}
- switch (plat->mode) {
- case MUSB_HOST:
-#ifdef CONFIG_USB_MUSB_HDRC_HCD
- break;
-#else
- goto bad_config;
-#endif
- case MUSB_PERIPHERAL:
-#ifdef CONFIG_USB_GADGET_MUSB_HDRC
- break;
-#else
- goto bad_config;
-#endif
- case MUSB_OTG:
-#ifdef CONFIG_USB_MUSB_OTG
- break;
-#else
-bad_config:
-#endif
- default:
- dev_err(dev, "incompatible Kconfig role setting\n");
- status = -EINVAL;
- goto fail0;
- }
-
/* allocate */
musb = allocate_instance(dev, plat->config, ctrl);
if (!musb) {
@@ -1982,6 +1957,10 @@ bad_config:
goto fail0;
}
+ pm_runtime_use_autosuspend(musb->controller);
+ pm_runtime_set_autosuspend_delay(musb->controller, 200);
+ pm_runtime_enable(musb->controller);
+
spin_lock_init(&musb->lock);
musb->board_mode = plat->mode;
musb->board_set_power = plat->set_power;
@@ -2117,6 +2096,8 @@ bad_config:
if (status < 0)
goto fail3;
+ pm_runtime_put(musb->controller);
+
status = musb_init_debugfs(musb);
if (status < 0)
goto fail4;
@@ -2216,9 +2197,11 @@ static int __exit musb_remove(struct platform_device *pdev)
* - Peripheral mode: peripheral is deactivated (or never-activated)
* - OTG mode: both roles are deactivated (or never-activated)
*/
+ pm_runtime_get_sync(musb->controller);
musb_exit_debugfs(musb);
musb_shutdown(pdev);
+ pm_runtime_put(musb->controller);
musb_free(musb);
iounmap(ctrl_base);
device_init_wakeup(&pdev->dev, 0);
@@ -2404,9 +2387,41 @@ static int musb_resume_noirq(struct device *dev)
return 0;
}
+static int musb_runtime_suspend(struct device *dev)
+{
+ struct musb *musb = dev_to_musb(dev);
+
+ musb_save_context(musb);
+
+ return 0;
+}
+
+static int musb_runtime_resume(struct device *dev)
+{
+ struct musb *musb = dev_to_musb(dev);
+ static int first = 1;
+
+ /*
+ * When pm_runtime_get_sync called for the first time in driver
+ * init, some of the structure is still not initialized which is
+ * used in restore function. But clock needs to be
+ * enabled before any register access, so
+ * pm_runtime_get_sync has to be called.
+ * Also context restore without save does not make
+ * any sense
+ */
+ if (!first)
+ musb_restore_context(musb);
+ first = 0;
+
+ return 0;
+}
+
static const struct dev_pm_ops musb_dev_pm_ops = {
.suspend = musb_suspend,
.resume_noirq = musb_resume_noirq,
+ .runtime_suspend = musb_runtime_suspend,
+ .runtime_resume = musb_runtime_resume,
};
#define MUSB_DEV_PM_OPS (&musb_dev_pm_ops)
diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h
index e6400be8a0f8..4f0dd2ed3964 100644
--- a/drivers/usb/musb/musb_core.h
+++ b/drivers/usb/musb/musb_core.h
@@ -328,7 +328,7 @@ struct musb_hw_ep {
#endif
};
-static inline struct usb_request *next_in_request(struct musb_hw_ep *hw_ep)
+static inline struct musb_request *next_in_request(struct musb_hw_ep *hw_ep)
{
#ifdef CONFIG_USB_GADGET_MUSB_HDRC
return next_request(&hw_ep->ep_in);
@@ -337,7 +337,7 @@ static inline struct usb_request *next_in_request(struct musb_hw_ep *hw_ep)
#endif
}
-static inline struct usb_request *next_out_request(struct musb_hw_ep *hw_ep)
+static inline struct musb_request *next_out_request(struct musb_hw_ep *hw_ep)
{
#ifdef CONFIG_USB_GADGET_MUSB_HDRC
return next_request(&hw_ep->ep_out);
@@ -358,10 +358,6 @@ struct musb_csr_regs {
struct musb_context_registers {
-#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3) || \
- defined(CONFIG_ARCH_OMAP4)
- u32 otg_sysconfig, otg_forcestandby;
-#endif
u8 power;
u16 intrtxe, intrrxe;
u8 intrusbe;
diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c
index 2fe304611dcf..5c7b321d3959 100644
--- a/drivers/usb/musb/musb_gadget.c
+++ b/drivers/usb/musb/musb_gadget.c
@@ -189,7 +189,7 @@ __acquires(ep->musb->lock)
req = to_musb_request(request);
- list_del(&request->list);
+ list_del(&req->list);
if (req->request.status == -EINPROGRESS)
req->request.status = status;
musb = req->musb;
@@ -251,9 +251,8 @@ static void nuke(struct musb_ep *ep, const int status)
ep->dma = NULL;
}
- while (!list_empty(&(ep->req_list))) {
- req = container_of(ep->req_list.next, struct musb_request,
- request.list);
+ while (!list_empty(&ep->req_list)) {
+ req = list_first_entry(&ep->req_list, struct musb_request, list);
musb_g_giveback(ep, &req->request, status);
}
}
@@ -485,6 +484,7 @@ static void txstate(struct musb *musb, struct musb_request *req)
void musb_g_tx(struct musb *musb, u8 epnum)
{
u16 csr;
+ struct musb_request *req;
struct usb_request *request;
u8 __iomem *mbase = musb->mregs;
struct musb_ep *musb_ep = &musb->endpoints[epnum].ep_in;
@@ -492,7 +492,8 @@ void musb_g_tx(struct musb *musb, u8 epnum)
struct dma_channel *dma;
musb_ep_select(mbase, epnum);
- request = next_request(musb_ep);
+ req = next_request(musb_ep);
+ request = &req->request;
csr = musb_readw(epio, MUSB_TXCSR);
DBG(4, "<== %s, txcsr %04x\n", musb_ep->end_point.name, csr);
@@ -571,15 +572,15 @@ void musb_g_tx(struct musb *musb, u8 epnum)
if (request->actual == request->length) {
musb_g_giveback(musb_ep, request, 0);
- request = musb_ep->desc ? next_request(musb_ep) : NULL;
- if (!request) {
+ req = musb_ep->desc ? next_request(musb_ep) : NULL;
+ if (!req) {
DBG(4, "%s idle now\n",
musb_ep->end_point.name);
return;
}
}
- txstate(musb, to_musb_request(request));
+ txstate(musb, req);
}
}
@@ -821,6 +822,7 @@ static void rxstate(struct musb *musb, struct musb_request *req)
void musb_g_rx(struct musb *musb, u8 epnum)
{
u16 csr;
+ struct musb_request *req;
struct usb_request *request;
void __iomem *mbase = musb->mregs;
struct musb_ep *musb_ep;
@@ -835,10 +837,12 @@ void musb_g_rx(struct musb *musb, u8 epnum)
musb_ep_select(mbase, epnum);
- request = next_request(musb_ep);
- if (!request)
+ req = next_request(musb_ep);
+ if (!req)
return;
+ request = &req->request;
+
csr = musb_readw(epio, MUSB_RXCSR);
dma = is_dma_capable() ? musb_ep->dma : NULL;
@@ -914,15 +918,15 @@ void musb_g_rx(struct musb *musb, u8 epnum)
#endif
musb_g_giveback(musb_ep, request, 0);
- request = next_request(musb_ep);
- if (!request)
+ req = next_request(musb_ep);
+ if (!req)
return;
}
#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_TUSB_OMAP_DMA)
exit:
#endif
/* Analyze request */
- rxstate(musb, to_musb_request(request));
+ rxstate(musb, req);
}
/* ------------------------------------------------------------ */
@@ -974,7 +978,7 @@ static int musb_gadget_enable(struct usb_ep *ep,
ok = musb->hb_iso_rx;
if (!ok) {
- DBG(4, "%s: not support ISO high bandwidth\n", __func__);
+ DBG(4, "no support for high bandwidth ISO\n");
goto fail;
}
musb_ep->hb_mult = (tmp >> 11) & 3;
@@ -998,7 +1002,7 @@ static int musb_gadget_enable(struct usb_ep *ep,
goto fail;
if (tmp > hw_ep->max_packet_sz_tx) {
- DBG(4, "%s: packet size beyond hw fifo size\n", __func__);
+ DBG(4, "packet size beyond hardware FIFO size\n");
goto fail;
}
@@ -1038,7 +1042,7 @@ static int musb_gadget_enable(struct usb_ep *ep,
goto fail;
if (tmp > hw_ep->max_packet_sz_rx) {
- DBG(4, "%s: packet size beyond hw fifo size\n", __func__);
+ DBG(4, "packet size beyond hardware FIFO size\n");
goto fail;
}
@@ -1171,7 +1175,6 @@ struct usb_request *musb_alloc_request(struct usb_ep *ep, gfp_t gfp_flags)
return NULL;
}
- INIT_LIST_HEAD(&request->request.list);
request->request.dma = DMA_ADDR_INVALID;
request->epnum = musb_ep->current_epnum;
request->ep = musb_ep;
@@ -1257,10 +1260,10 @@ static int musb_gadget_queue(struct usb_ep *ep, struct usb_request *req,
}
/* add request to the list */
- list_add_tail(&(request->request.list), &(musb_ep->req_list));
+ list_add_tail(&request->list, &musb_ep->req_list);
/* it this is the head of the queue, start i/o ... */
- if (!musb_ep->busy && &request->request.list == musb_ep->req_list.next)
+ if (!musb_ep->busy && &request->list == musb_ep->req_list.next)
musb_ep_restart(musb, request);
cleanup:
@@ -1271,7 +1274,8 @@ cleanup:
static int musb_gadget_dequeue(struct usb_ep *ep, struct usb_request *request)
{
struct musb_ep *musb_ep = to_musb_ep(ep);
- struct usb_request *r;
+ struct musb_request *req = to_musb_request(request);
+ struct musb_request *r;
unsigned long flags;
int status = 0;
struct musb *musb = musb_ep->musb;
@@ -1282,10 +1286,10 @@ static int musb_gadget_dequeue(struct usb_ep *ep, struct usb_request *request)
spin_lock_irqsave(&musb->lock, flags);
list_for_each_entry(r, &musb_ep->req_list, list) {
- if (r == request)
+ if (r == req)
break;
}
- if (r != request) {
+ if (r != req) {
DBG(3, "request %p not queued to %s\n", request, ep->name);
status = -EINVAL;
goto done;
@@ -1349,7 +1353,7 @@ static int musb_gadget_set_halt(struct usb_ep *ep, int value)
musb_ep_select(mbase, epnum);
- request = to_musb_request(next_request(musb_ep));
+ request = next_request(musb_ep);
if (value) {
if (request) {
DBG(3, "request in progress, cannot halt %s\n",
@@ -1801,90 +1805,105 @@ void musb_gadget_cleanup(struct musb *musb)
int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
int (*bind)(struct usb_gadget *))
{
- int retval;
- unsigned long flags;
- struct musb *musb = the_gadget;
+ struct musb *musb = the_gadget;
+ unsigned long flags;
+ int retval = -EINVAL;
if (!driver
|| driver->speed != USB_SPEED_HIGH
|| !bind || !driver->setup)
- return -EINVAL;
+ goto err0;
/* driver must be initialized to support peripheral mode */
if (!musb) {
- DBG(1, "%s, no dev??\n", __func__);
- return -ENODEV;
+ DBG(1, "no dev??\n");
+ retval = -ENODEV;
+ goto err0;
}
+ pm_runtime_get_sync(musb->controller);
+
DBG(3, "registering driver %s\n", driver->function);
- spin_lock_irqsave(&musb->lock, flags);
if (musb->gadget_driver) {
DBG(1, "%s is already bound to %s\n",
musb_driver_name,
musb->gadget_driver->driver.name);
retval = -EBUSY;
- } else {
- musb->gadget_driver = driver;
- musb->g.dev.driver = &driver->driver;
- driver->driver.bus = NULL;
- musb->softconnect = 1;
- retval = 0;
+ goto err0;
}
+ spin_lock_irqsave(&musb->lock, flags);
+ musb->gadget_driver = driver;
+ musb->g.dev.driver = &driver->driver;
+ driver->driver.bus = NULL;
+ musb->softconnect = 1;
spin_unlock_irqrestore(&musb->lock, flags);
- if (retval == 0) {
- retval = bind(&musb->g);
- if (retval != 0) {
- DBG(3, "bind to driver %s failed --> %d\n",
- driver->driver.name, retval);
- musb->gadget_driver = NULL;
- musb->g.dev.driver = NULL;
- }
+ retval = bind(&musb->g);
+ if (retval) {
+ DBG(3, "bind to driver %s failed --> %d\n",
+ driver->driver.name, retval);
+ goto err1;
+ }
- spin_lock_irqsave(&musb->lock, flags);
+ spin_lock_irqsave(&musb->lock, flags);
- otg_set_peripheral(musb->xceiv, &musb->g);
- musb->xceiv->state = OTG_STATE_B_IDLE;
- musb->is_active = 1;
+ otg_set_peripheral(musb->xceiv, &musb->g);
+ musb->xceiv->state = OTG_STATE_B_IDLE;
+ musb->is_active = 1;
- /* FIXME this ignores the softconnect flag. Drivers are
- * allowed hold the peripheral inactive until for example
- * userspace hooks up printer hardware or DSP codecs, so
- * hosts only see fully functional devices.
- */
+ /*
+ * FIXME this ignores the softconnect flag. Drivers are
+ * allowed hold the peripheral inactive until for example
+ * userspace hooks up printer hardware or DSP codecs, so
+ * hosts only see fully functional devices.
+ */
- if (!is_otg_enabled(musb))
- musb_start(musb);
+ if (!is_otg_enabled(musb))
+ musb_start(musb);
- otg_set_peripheral(musb->xceiv, &musb->g);
+ otg_set_peripheral(musb->xceiv, &musb->g);
- spin_unlock_irqrestore(&musb->lock, flags);
+ spin_unlock_irqrestore(&musb->lock, flags);
- if (is_otg_enabled(musb)) {
- struct usb_hcd *hcd = musb_to_hcd(musb);
+ if (is_otg_enabled(musb)) {
+ struct usb_hcd *hcd = musb_to_hcd(musb);
- DBG(3, "OTG startup...\n");
+ DBG(3, "OTG startup...\n");
- /* REVISIT: funcall to other code, which also
- * handles power budgeting ... this way also
- * ensures HdrcStart is indirectly called.
- */
- retval = usb_add_hcd(musb_to_hcd(musb), -1, 0);
- if (retval < 0) {
- DBG(1, "add_hcd failed, %d\n", retval);
- spin_lock_irqsave(&musb->lock, flags);
- otg_set_peripheral(musb->xceiv, NULL);
- musb->gadget_driver = NULL;
- musb->g.dev.driver = NULL;
- spin_unlock_irqrestore(&musb->lock, flags);
- } else {
- hcd->self.uses_pio_for_control = 1;
- }
+ /* REVISIT: funcall to other code, which also
+ * handles power budgeting ... this way also
+ * ensures HdrcStart is indirectly called.
+ */
+ retval = usb_add_hcd(musb_to_hcd(musb), -1, 0);
+ if (retval < 0) {
+ DBG(1, "add_hcd failed, %d\n", retval);
+ goto err2;
+
+ if ((musb->xceiv->last_event == USB_EVENT_ID)
+ && musb->xceiv->set_vbus)
+ otg_set_vbus(musb->xceiv, 1);
}
+
+ hcd->self.uses_pio_for_control = 1;
+
+ if (musb->xceiv->last_event == USB_EVENT_NONE)
+ pm_runtime_put(musb->controller);
+
}
+ return 0;
+
+err2:
+ if (!is_otg_enabled(musb))
+ musb_stop(musb);
+
+err1:
+ musb->gadget_driver = NULL;
+ musb->g.dev.driver = NULL;
+
+err0:
return retval;
}
EXPORT_SYMBOL(usb_gadget_probe_driver);
@@ -1939,14 +1958,20 @@ static void stop_activity(struct musb *musb, struct usb_gadget_driver *driver)
*/
int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
{
- unsigned long flags;
- int retval = 0;
struct musb *musb = the_gadget;
+ unsigned long flags;
if (!driver || !driver->unbind || !musb)
return -EINVAL;
- /* REVISIT always use otg_set_peripheral() here too;
+ if (!musb->gadget_driver)
+ return -EINVAL;
+
+ if (musb->xceiv->last_event == USB_EVENT_NONE)
+ pm_runtime_get_sync(musb->controller);
+
+ /*
+ * REVISIT always use otg_set_peripheral() here too;
* this needs to shut down the OTG engine.
*/
@@ -1956,29 +1981,26 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
musb_hnp_stop(musb);
#endif
- if (musb->gadget_driver == driver) {
+ (void) musb_gadget_vbus_draw(&musb->g, 0);
- (void) musb_gadget_vbus_draw(&musb->g, 0);
+ musb->xceiv->state = OTG_STATE_UNDEFINED;
+ stop_activity(musb, driver);
+ otg_set_peripheral(musb->xceiv, NULL);
- musb->xceiv->state = OTG_STATE_UNDEFINED;
- stop_activity(musb, driver);
- otg_set_peripheral(musb->xceiv, NULL);
+ DBG(3, "unregistering driver %s\n", driver->function);
- DBG(3, "unregistering driver %s\n", driver->function);
- spin_unlock_irqrestore(&musb->lock, flags);
- driver->unbind(&musb->g);
- spin_lock_irqsave(&musb->lock, flags);
+ spin_unlock_irqrestore(&musb->lock, flags);
+ driver->unbind(&musb->g);
+ spin_lock_irqsave(&musb->lock, flags);
- musb->gadget_driver = NULL;
- musb->g.dev.driver = NULL;
+ musb->gadget_driver = NULL;
+ musb->g.dev.driver = NULL;
- musb->is_active = 0;
- musb_platform_try_idle(musb, 0);
- } else
- retval = -EINVAL;
+ musb->is_active = 0;
+ musb_platform_try_idle(musb, 0);
spin_unlock_irqrestore(&musb->lock, flags);
- if (is_otg_enabled(musb) && retval == 0) {
+ if (is_otg_enabled(musb)) {
usb_remove_hcd(musb_to_hcd(musb));
/* FIXME we need to be able to register another
* gadget driver here and have everything work;
@@ -1986,7 +2008,12 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
*/
}
- return retval;
+ if (!is_otg_enabled(musb))
+ musb_stop(musb);
+
+ pm_runtime_put(musb->controller);
+
+ return 0;
}
EXPORT_SYMBOL(usb_gadget_unregister_driver);
diff --git a/drivers/usb/musb/musb_gadget.h b/drivers/usb/musb/musb_gadget.h
index a55354fbccf5..66b7c5e0fb44 100644
--- a/drivers/usb/musb/musb_gadget.h
+++ b/drivers/usb/musb/musb_gadget.h
@@ -35,6 +35,8 @@
#ifndef __MUSB_GADGET_H
#define __MUSB_GADGET_H
+#include <linux/list.h>
+
enum buffer_map_state {
UN_MAPPED = 0,
PRE_MAPPED,
@@ -43,6 +45,7 @@ enum buffer_map_state {
struct musb_request {
struct usb_request request;
+ struct list_head list;
struct musb_ep *ep;
struct musb *musb;
u8 tx; /* endpoint direction */
@@ -94,13 +97,13 @@ static inline struct musb_ep *to_musb_ep(struct usb_ep *ep)
return ep ? container_of(ep, struct musb_ep, end_point) : NULL;
}
-static inline struct usb_request *next_request(struct musb_ep *ep)
+static inline struct musb_request *next_request(struct musb_ep *ep)
{
struct list_head *queue = &ep->req_list;
if (list_empty(queue))
return NULL;
- return container_of(queue->next, struct usb_request, list);
+ return container_of(queue->next, struct musb_request, list);
}
extern void musb_g_tx(struct musb *musb, u8 epnum);
diff --git a/drivers/usb/musb/musb_gadget_ep0.c b/drivers/usb/musb/musb_gadget_ep0.c
index 6dd03f4c5f49..75a542e42fdf 100644
--- a/drivers/usb/musb/musb_gadget_ep0.c
+++ b/drivers/usb/musb/musb_gadget_ep0.c
@@ -304,8 +304,7 @@ __acquires(musb->lock)
}
/* Maybe start the first request in the queue */
- request = to_musb_request(
- next_request(musb_ep));
+ request = next_request(musb_ep);
if (!musb_ep->busy && request) {
DBG(3, "restarting the request\n");
musb_ep_restart(musb, request);
@@ -491,10 +490,12 @@ stall:
static void ep0_rxstate(struct musb *musb)
{
void __iomem *regs = musb->control_ep->regs;
+ struct musb_request *request;
struct usb_request *req;
u16 count, csr;
- req = next_ep0_request(musb);
+ request = next_ep0_request(musb);
+ req = &request->request;
/* read packet and ack; or stall because of gadget driver bug:
* should have provided the rx buffer before setup() returned.
@@ -544,17 +545,20 @@ static void ep0_rxstate(struct musb *musb)
static void ep0_txstate(struct musb *musb)
{
void __iomem *regs = musb->control_ep->regs;
- struct usb_request *request = next_ep0_request(musb);
+ struct musb_request *req = next_ep0_request(musb);
+ struct usb_request *request;
u16 csr = MUSB_CSR0_TXPKTRDY;
u8 *fifo_src;
u8 fifo_count;
- if (!request) {
+ if (!req) {
/* WARN_ON(1); */
DBG(2, "odd; csr0 %04x\n", musb_readw(regs, MUSB_CSR0));
return;
}
+ request = &req->request;
+
/* load the data */
fifo_src = (u8 *) request->buf + request->actual;
fifo_count = min((unsigned) MUSB_EP0_FIFOSIZE,
@@ -598,7 +602,7 @@ static void ep0_txstate(struct musb *musb)
static void
musb_read_setup(struct musb *musb, struct usb_ctrlrequest *req)
{
- struct usb_request *r;
+ struct musb_request *r;
void __iomem *regs = musb->control_ep->regs;
musb_read_fifo(&musb->endpoints[0], sizeof *req, (u8 *)req);
@@ -616,7 +620,7 @@ musb_read_setup(struct musb *musb, struct usb_ctrlrequest *req)
/* clean up any leftover transfers */
r = next_ep0_request(musb);
if (r)
- musb_g_ep0_giveback(musb, r);
+ musb_g_ep0_giveback(musb, &r->request);
/* For zero-data requests we want to delay the STATUS stage to
* avoid SETUPEND errors. If we read data (OUT), delay accepting
@@ -758,11 +762,11 @@ irqreturn_t musb_g_ep0_irq(struct musb *musb)
case MUSB_EP0_STAGE_STATUSOUT:
/* end of sequence #1: write to host (TX state) */
{
- struct usb_request *req;
+ struct musb_request *req;
req = next_ep0_request(musb);
if (req)
- musb_g_ep0_giveback(musb, req);
+ musb_g_ep0_giveback(musb, &req->request);
}
/*
@@ -961,7 +965,7 @@ musb_g_ep0_queue(struct usb_ep *e, struct usb_request *r, gfp_t gfp_flags)
}
/* add request to the list */
- list_add_tail(&(req->request.list), &(ep->req_list));
+ list_add_tail(&req->list, &ep->req_list);
DBG(3, "queue to %s (%s), length=%d\n",
ep->name, ep->is_in ? "IN/TX" : "OUT/RX",
diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c
index 0f523d7db57b..5eef4a8847db 100644
--- a/drivers/usb/musb/musb_host.c
+++ b/drivers/usb/musb/musb_host.c
@@ -1335,7 +1335,7 @@ void musb_host_tx(struct musb *musb, u8 epnum)
if (length > qh->maxpacket)
length = qh->maxpacket;
/* Unmap the buffer so that CPU can use it */
- unmap_urb_for_dma(musb_to_hcd(musb), urb);
+ usb_hcd_unmap_urb_for_dma(musb_to_hcd(musb), urb);
musb_write_fifo(hw_ep, length, urb->transfer_buffer + offset);
qh->segsize = length;
@@ -1757,7 +1757,7 @@ void musb_host_rx(struct musb *musb, u8 epnum)
if (!dma) {
/* Unmap the buffer so that CPU can use it */
- unmap_urb_for_dma(musb_to_hcd(musb), urb);
+ usb_hcd_unmap_urb_for_dma(musb_to_hcd(musb), urb);
done = musb_host_packet_rx(musb, urb,
epnum, iso_err);
DBG(6, "read %spacket\n", done ? "last " : "");
diff --git a/drivers/usb/musb/musb_virthub.c b/drivers/usb/musb/musb_virthub.c
index b46d1877e28e..489104a5ae14 100644
--- a/drivers/usb/musb/musb_virthub.c
+++ b/drivers/usb/musb/musb_virthub.c
@@ -305,8 +305,8 @@ int musb_hub_control(
desc->bHubContrCurrent = 0;
/* workaround bogus struct definition */
- desc->DeviceRemovable[0] = 0x02; /* port 1 */
- desc->DeviceRemovable[1] = 0xff;
+ desc->u.hs.DeviceRemovable[0] = 0x02; /* port 1 */
+ desc->u.hs.DeviceRemovable[1] = 0xff;
}
break;
case GetHubStatus:
diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c
index bc8badd16897..25cb8b0003b1 100644
--- a/drivers/usb/musb/omap2430.c
+++ b/drivers/usb/musb/omap2430.c
@@ -33,6 +33,8 @@
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
+#include <linux/pm_runtime.h>
+#include <linux/err.h>
#include "musb_core.h"
#include "omap2430.h"
@@ -40,7 +42,6 @@
struct omap2430_glue {
struct device *dev;
struct platform_device *musb;
- struct clk *clk;
};
#define glue_to_musb(g) platform_get_drvdata(g->musb)
@@ -216,20 +217,12 @@ static inline void omap2430_low_level_exit(struct musb *musb)
l = musb_readl(musb->mregs, OTG_FORCESTDBY);
l |= ENABLEFORCE; /* enable MSTANDBY */
musb_writel(musb->mregs, OTG_FORCESTDBY, l);
-
- l = musb_readl(musb->mregs, OTG_SYSCONFIG);
- l |= ENABLEWAKEUP; /* enable wakeup */
- musb_writel(musb->mregs, OTG_SYSCONFIG, l);
}
static inline void omap2430_low_level_init(struct musb *musb)
{
u32 l;
- l = musb_readl(musb->mregs, OTG_SYSCONFIG);
- l &= ~ENABLEWAKEUP; /* disable wakeup */
- musb_writel(musb->mregs, OTG_SYSCONFIG, l);
-
l = musb_readl(musb->mregs, OTG_FORCESTDBY);
l &= ~ENABLEFORCE; /* disable MSTANDBY */
musb_writel(musb->mregs, OTG_FORCESTDBY, l);
@@ -251,31 +244,39 @@ static int musb_otg_notifications(struct notifier_block *nb,
if (is_otg_enabled(musb)) {
#ifdef CONFIG_USB_GADGET_MUSB_HDRC
if (musb->gadget_driver) {
+ pm_runtime_get_sync(musb->controller);
otg_init(musb->xceiv);
-
- if (data->interface_type ==
- MUSB_INTERFACE_UTMI)
- omap2430_musb_set_vbus(musb, 1);
-
+ omap2430_musb_set_vbus(musb, 1);
}
#endif
} else {
+ pm_runtime_get_sync(musb->controller);
otg_init(musb->xceiv);
- if (data->interface_type ==
- MUSB_INTERFACE_UTMI)
- omap2430_musb_set_vbus(musb, 1);
+ omap2430_musb_set_vbus(musb, 1);
}
break;
case USB_EVENT_VBUS:
DBG(4, "VBUS Connect\n");
+ if (musb->gadget_driver)
+ pm_runtime_get_sync(musb->controller);
+
otg_init(musb->xceiv);
break;
case USB_EVENT_NONE:
DBG(4, "VBUS Disconnect\n");
+#ifdef CONFIG_USB_GADGET_MUSB_HDRC
+ if (is_otg_enabled(musb))
+ if (musb->gadget_driver)
+#endif
+ {
+ pm_runtime_mark_last_busy(musb->controller);
+ pm_runtime_put_autosuspend(musb->controller);
+ }
+
if (data->interface_type == MUSB_INTERFACE_UTMI) {
if (musb->xceiv->set_vbus)
otg_set_vbus(musb->xceiv, 0);
@@ -307,22 +308,11 @@ static int omap2430_musb_init(struct musb *musb)
return -ENODEV;
}
- omap2430_low_level_init(musb);
-
- l = musb_readl(musb->mregs, OTG_SYSCONFIG);
- l &= ~ENABLEWAKEUP; /* disable wakeup */
- l &= ~NOSTDBY; /* remove possible nostdby */
- l |= SMARTSTDBY; /* enable smart standby */
- l &= ~AUTOIDLE; /* disable auto idle */
- l &= ~NOIDLE; /* remove possible noidle */
- l |= SMARTIDLE; /* enable smart idle */
- /*
- * MUSB AUTOIDLE don't work in 3430.
- * Workaround by Richard Woodruff/TI
- */
- if (!cpu_is_omap3430())
- l |= AUTOIDLE; /* enable auto idle */
- musb_writel(musb->mregs, OTG_SYSCONFIG, l);
+ status = pm_runtime_get_sync(dev);
+ if (status < 0) {
+ dev_err(dev, "pm_runtime_get_sync FAILED");
+ goto err1;
+ }
l = musb_readl(musb->mregs, OTG_INTERFSEL);
@@ -350,14 +340,58 @@ static int omap2430_musb_init(struct musb *musb)
if (status)
DBG(1, "notification register failed\n");
- /* check whether cable is already connected */
- if (musb->xceiv->state ==OTG_STATE_B_IDLE)
- musb_otg_notifications(&musb->nb, 1,
- musb->xceiv->gadget);
-
setup_timer(&musb_idle_timer, musb_do_idle, (unsigned long) musb);
return 0;
+
+err1:
+ pm_runtime_disable(dev);
+ return status;
+}
+
+static void omap2430_musb_enable(struct musb *musb)
+{
+ u8 devctl;
+ unsigned long timeout = jiffies + msecs_to_jiffies(1000);
+ struct device *dev = musb->controller;
+ struct musb_hdrc_platform_data *pdata = dev->platform_data;
+ struct omap_musb_board_data *data = pdata->board_data;
+
+ switch (musb->xceiv->last_event) {
+
+ case USB_EVENT_ID:
+ otg_init(musb->xceiv);
+ if (data->interface_type == MUSB_INTERFACE_UTMI) {
+ devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
+ /* start the session */
+ devctl |= MUSB_DEVCTL_SESSION;
+ musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
+ while (musb_readb(musb->mregs, MUSB_DEVCTL) &
+ MUSB_DEVCTL_BDEVICE) {
+ cpu_relax();
+
+ if (time_after(jiffies, timeout)) {
+ dev_err(musb->controller,
+ "configured as A device timeout");
+ break;
+ }
+ }
+ }
+ break;
+
+ case USB_EVENT_VBUS:
+ otg_init(musb->xceiv);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void omap2430_musb_disable(struct musb *musb)
+{
+ if (musb->xceiv->last_event)
+ otg_shutdown(musb->xceiv);
}
static int omap2430_musb_exit(struct musb *musb)
@@ -378,6 +412,9 @@ static const struct musb_platform_ops omap2430_ops = {
.try_idle = omap2430_musb_try_idle,
.set_vbus = omap2430_musb_set_vbus,
+
+ .enable = omap2430_musb_enable,
+ .disable = omap2430_musb_disable,
};
static u64 omap2430_dmamask = DMA_BIT_MASK(32);
@@ -387,8 +424,6 @@ static int __init omap2430_probe(struct platform_device *pdev)
struct musb_hdrc_platform_data *pdata = pdev->dev.platform_data;
struct platform_device *musb;
struct omap2430_glue *glue;
- struct clk *clk;
-
int ret = -ENOMEM;
glue = kzalloc(sizeof(*glue), GFP_KERNEL);
@@ -403,26 +438,12 @@ static int __init omap2430_probe(struct platform_device *pdev)
goto err1;
}
- clk = clk_get(&pdev->dev, "ick");
- if (IS_ERR(clk)) {
- dev_err(&pdev->dev, "failed to get clock\n");
- ret = PTR_ERR(clk);
- goto err2;
- }
-
- ret = clk_enable(clk);
- if (ret) {
- dev_err(&pdev->dev, "failed to enable clock\n");
- goto err3;
- }
-
musb->dev.parent = &pdev->dev;
musb->dev.dma_mask = &omap2430_dmamask;
musb->dev.coherent_dma_mask = omap2430_dmamask;
glue->dev = &pdev->dev;
glue->musb = musb;
- glue->clk = clk;
pdata->platform_ops = &omap2430_ops;
@@ -432,28 +453,24 @@ static int __init omap2430_probe(struct platform_device *pdev)
pdev->num_resources);
if (ret) {
dev_err(&pdev->dev, "failed to add resources\n");
- goto err4;
+ goto err2;
}
ret = platform_device_add_data(musb, pdata, sizeof(*pdata));
if (ret) {
dev_err(&pdev->dev, "failed to add platform_data\n");
- goto err4;
+ goto err2;
}
ret = platform_device_add(musb);
if (ret) {
dev_err(&pdev->dev, "failed to register musb device\n");
- goto err4;
+ goto err2;
}
- return 0;
-
-err4:
- clk_disable(clk);
+ pm_runtime_enable(&pdev->dev);
-err3:
- clk_put(clk);
+ return 0;
err2:
platform_device_put(musb);
@@ -471,61 +488,40 @@ static int __exit omap2430_remove(struct platform_device *pdev)
platform_device_del(glue->musb);
platform_device_put(glue->musb);
- clk_disable(glue->clk);
- clk_put(glue->clk);
+ pm_runtime_put(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
kfree(glue);
return 0;
}
#ifdef CONFIG_PM
-static void omap2430_save_context(struct musb *musb)
-{
- musb->context.otg_sysconfig = musb_readl(musb->mregs, OTG_SYSCONFIG);
- musb->context.otg_forcestandby = musb_readl(musb->mregs, OTG_FORCESTDBY);
-}
-static void omap2430_restore_context(struct musb *musb)
-{
- musb_writel(musb->mregs, OTG_SYSCONFIG, musb->context.otg_sysconfig);
- musb_writel(musb->mregs, OTG_FORCESTDBY, musb->context.otg_forcestandby);
-}
-
-static int omap2430_suspend(struct device *dev)
+static int omap2430_runtime_suspend(struct device *dev)
{
struct omap2430_glue *glue = dev_get_drvdata(dev);
struct musb *musb = glue_to_musb(glue);
omap2430_low_level_exit(musb);
otg_set_suspend(musb->xceiv, 1);
- omap2430_save_context(musb);
- clk_disable(glue->clk);
return 0;
}
-static int omap2430_resume(struct device *dev)
+static int omap2430_runtime_resume(struct device *dev)
{
struct omap2430_glue *glue = dev_get_drvdata(dev);
struct musb *musb = glue_to_musb(glue);
- int ret;
-
- ret = clk_enable(glue->clk);
- if (ret) {
- dev_err(dev, "faled to enable clock\n");
- return ret;
- }
omap2430_low_level_init(musb);
- omap2430_restore_context(musb);
otg_set_suspend(musb->xceiv, 0);
return 0;
}
static struct dev_pm_ops omap2430_pm_ops = {
- .suspend = omap2430_suspend,
- .resume = omap2430_resume,
+ .runtime_suspend = omap2430_runtime_suspend,
+ .runtime_resume = omap2430_runtime_resume,
};
#define DEV_PM_OPS (&omap2430_pm_ops)
diff --git a/drivers/usb/musb/tusb6010_omap.c b/drivers/usb/musb/tusb6010_omap.c
index c061a88f2b0f..99cb541e4ef0 100644
--- a/drivers/usb/musb/tusb6010_omap.c
+++ b/drivers/usb/musb/tusb6010_omap.c
@@ -680,7 +680,7 @@ dma_controller_create(struct musb *musb, void __iomem *base)
tusb_dma = kzalloc(sizeof(struct tusb_omap_dma), GFP_KERNEL);
if (!tusb_dma)
- goto cleanup;
+ goto out;
tusb_dma->musb = musb;
tusb_dma->tbase = musb->ctrl_base;
@@ -721,6 +721,6 @@ dma_controller_create(struct musb *musb, void __iomem *base)
cleanup:
dma_controller_destroy(&tusb_dma->controller);
-
+out:
return NULL;
}
diff --git a/drivers/usb/otg/Kconfig b/drivers/usb/otg/Kconfig
index 9ffc8237fb4b..daf3e5f1a0e7 100644
--- a/drivers/usb/otg/Kconfig
+++ b/drivers/usb/otg/Kconfig
@@ -49,6 +49,13 @@ config USB_ULPI
Enable this to support ULPI connected USB OTG transceivers which
are likely found on embedded boards.
+config USB_ULPI_VIEWPORT
+ bool
+ depends on USB_ULPI
+ help
+ Provides read/write operations to the ULPI phy register set for
+ controllers with a viewport register (e.g. Chipidea/ARC controllers).
+
config TWL4030_USB
tristate "TWL4030 USB Transceiver Driver"
depends on TWL4030_CORE && REGULATOR_TWL4030
@@ -93,7 +100,7 @@ config USB_LANGWELL_OTG
To compile this driver as a module, choose M here: the
module will be called langwell_otg.
-config USB_MSM_OTG_72K
+config USB_MSM_OTG
tristate "OTG support for Qualcomm on-chip USB controller"
depends on (USB || USB_GADGET) && ARCH_MSM
select USB_OTG_UTILS
diff --git a/drivers/usb/otg/Makefile b/drivers/usb/otg/Makefile
index a520e715cfd6..e22d917de017 100644
--- a/drivers/usb/otg/Makefile
+++ b/drivers/usb/otg/Makefile
@@ -16,5 +16,6 @@ obj-$(CONFIG_TWL6030_USB) += twl6030-usb.o
obj-$(CONFIG_USB_LANGWELL_OTG) += langwell_otg.o
obj-$(CONFIG_NOP_USB_XCEIV) += nop-usb-xceiv.o
obj-$(CONFIG_USB_ULPI) += ulpi.o
-obj-$(CONFIG_USB_MSM_OTG_72K) += msm72k_otg.o
+obj-$(CONFIG_USB_ULPI_VIEWPORT) += ulpi_viewport.o
+obj-$(CONFIG_USB_MSM_OTG) += msm_otg.o
obj-$(CONFIG_AB8500_USB) += ab8500-usb.o
diff --git a/drivers/usb/otg/ab8500-usb.c b/drivers/usb/otg/ab8500-usb.c
index d14736b3107b..07ccea9ada40 100644
--- a/drivers/usb/otg/ab8500-usb.c
+++ b/drivers/usb/otg/ab8500-usb.c
@@ -212,7 +212,7 @@ static int ab8500_usb_link_status_update(struct ab8500_usb *ab)
break;
}
- blocking_notifier_call_chain(&ab->otg.notifier, event, v);
+ atomic_notifier_call_chain(&ab->otg.notifier, event, v);
return 0;
}
@@ -281,7 +281,7 @@ static int ab8500_usb_set_power(struct otg_transceiver *otg, unsigned mA)
ab->vbus_draw = mA;
if (mA)
- blocking_notifier_call_chain(&ab->otg.notifier,
+ atomic_notifier_call_chain(&ab->otg.notifier,
USB_EVENT_ENUMERATED, ab->otg.gadget);
return 0;
}
@@ -500,7 +500,7 @@ static int __devinit ab8500_usb_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, ab);
- BLOCKING_INIT_NOTIFIER_HEAD(&ab->otg.notifier);
+ ATOMIC_INIT_NOTIFIER_HEAD(&ab->otg.notifier);
/* v1: Wait for link status to become stable.
* all: Updates form set_host and set_peripheral as they are atomic.
diff --git a/drivers/usb/otg/langwell_otg.c b/drivers/usb/otg/langwell_otg.c
index 9fea48264fa2..7f9b8cd4514b 100644
--- a/drivers/usb/otg/langwell_otg.c
+++ b/drivers/usb/otg/langwell_otg.c
@@ -174,50 +174,24 @@ static int langwell_otg_set_power(struct otg_transceiver *otg,
return 0;
}
-/* A-device drives vbus, controlled through PMIC CHRGCNTL register*/
+/* A-device drives vbus, controlled through IPC commands */
static int langwell_otg_set_vbus(struct otg_transceiver *otg, bool enabled)
{
struct langwell_otg *lnw = the_transceiver;
- u8 r;
+ u8 sub_id;
dev_dbg(lnw->dev, "%s <--- %s\n", __func__, enabled ? "on" : "off");
- /* FIXME: surely we should cache this on the first read. If not use
- readv to avoid two transactions */
- if (intel_scu_ipc_ioread8(0x00, &r) < 0) {
- dev_dbg(lnw->dev, "Failed to read PMIC register 0xD2");
- return -EBUSY;
- }
- if ((r & 0x03) != 0x02) {
- dev_dbg(lnw->dev, "not NEC PMIC attached\n");
- return -EBUSY;
- }
-
- if (intel_scu_ipc_ioread8(0x20, &r) < 0) {
- dev_dbg(lnw->dev, "Failed to read PMIC register 0xD2");
- return -EBUSY;
- }
-
- if ((r & 0x20) == 0) {
- dev_dbg(lnw->dev, "no battery attached\n");
- return -EBUSY;
- }
+ if (enabled)
+ sub_id = 0x8; /* Turn on the VBus */
+ else
+ sub_id = 0x9; /* Turn off the VBus */
- /* Workaround for battery attachment issue */
- if (r == 0x34) {
- dev_dbg(lnw->dev, "no battery attached on SH\n");
+ if (intel_scu_ipc_simple_command(0xef, sub_id)) {
+ dev_dbg(lnw->dev, "Failed to set Vbus via IPC commands\n");
return -EBUSY;
}
- dev_dbg(lnw->dev, "battery attached. 2 reg = %x\n", r);
-
- /* workaround: FW detect writing 0x20/0xc0 to d4 event.
- * this is only for NEC PMIC.
- */
-
- if (intel_scu_ipc_iowrite8(0xD4, enabled ? 0x20 : 0xC0))
- dev_dbg(lnw->dev, "Failed to write PMIC.\n");
-
dev_dbg(lnw->dev, "%s --->\n", __func__);
return 0;
@@ -394,14 +368,14 @@ static void langwell_otg_phy_low_power(int on)
dev_dbg(lnw->dev, "%s <--- done\n", __func__);
}
-/* After drv vbus, add 2 ms delay to set PHCD */
+/* After drv vbus, add 5 ms delay to set PHCD */
static void langwell_otg_phy_low_power_wait(int on)
{
struct langwell_otg *lnw = the_transceiver;
- dev_dbg(lnw->dev, "add 2ms delay before programing PHCD\n");
+ dev_dbg(lnw->dev, "add 5ms delay before programing PHCD\n");
- mdelay(2);
+ mdelay(5);
langwell_otg_phy_low_power(on);
}
diff --git a/drivers/usb/otg/msm72k_otg.c b/drivers/usb/otg/msm_otg.c
index 1cd52edcd0c2..296598628b85 100644
--- a/drivers/usb/otg/msm72k_otg.c
+++ b/drivers/usb/otg/msm_otg.c
@@ -253,6 +253,9 @@ static int msm_otg_reset(struct otg_transceiver *otg)
}
#define PHY_SUSPEND_TIMEOUT_USEC (500 * 1000)
+#define PHY_RESUME_TIMEOUT_USEC (100 * 1000)
+
+#ifdef CONFIG_PM_SLEEP
static int msm_otg_suspend(struct msm_otg *motg)
{
struct otg_transceiver *otg = &motg->otg;
@@ -334,7 +337,6 @@ static int msm_otg_suspend(struct msm_otg *motg)
return 0;
}
-#define PHY_RESUME_TIMEOUT_USEC (100 * 1000)
static int msm_otg_resume(struct msm_otg *motg)
{
struct otg_transceiver *otg = &motg->otg;
@@ -399,6 +401,7 @@ skip_phy_resume:
return 0;
}
+#endif
static void msm_otg_start_host(struct otg_transceiver *otg, int on)
{
@@ -720,7 +723,8 @@ static int msm_otg_mode_open(struct inode *inode, struct file *file)
static ssize_t msm_otg_mode_write(struct file *file, const char __user *ubuf,
size_t count, loff_t *ppos)
{
- struct msm_otg *motg = file->private_data;
+ struct seq_file *s = file->private_data;
+ struct msm_otg *motg = s->private;
char buf[16];
struct otg_transceiver *otg = &motg->otg;
int status = count;
@@ -972,7 +976,7 @@ static int __devexit msm_otg_remove(struct platform_device *pdev)
msm_otg_debugfs_cleanup();
cancel_work_sync(&motg->sm_work);
- msm_otg_resume(motg);
+ pm_runtime_resume(&pdev->dev);
device_init_wakeup(&pdev->dev, 0);
pm_runtime_disable(&pdev->dev);
@@ -1050,13 +1054,9 @@ static int msm_otg_runtime_resume(struct device *dev)
dev_dbg(dev, "OTG runtime resume\n");
return msm_otg_resume(motg);
}
-#else
-#define msm_otg_runtime_idle NULL
-#define msm_otg_runtime_suspend NULL
-#define msm_otg_runtime_resume NULL
#endif
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int msm_otg_pm_suspend(struct device *dev)
{
struct msm_otg *motg = dev_get_drvdata(dev);
@@ -1086,25 +1086,24 @@ static int msm_otg_pm_resume(struct device *dev)
return 0;
}
-#else
-#define msm_otg_pm_suspend NULL
-#define msm_otg_pm_resume NULL
#endif
+#ifdef CONFIG_PM
static const struct dev_pm_ops msm_otg_dev_pm_ops = {
- .runtime_suspend = msm_otg_runtime_suspend,
- .runtime_resume = msm_otg_runtime_resume,
- .runtime_idle = msm_otg_runtime_idle,
- .suspend = msm_otg_pm_suspend,
- .resume = msm_otg_pm_resume,
+ SET_SYSTEM_SLEEP_PM_OPS(msm_otg_pm_suspend, msm_otg_pm_resume)
+ SET_RUNTIME_PM_OPS(msm_otg_runtime_suspend, msm_otg_runtime_resume,
+ msm_otg_runtime_idle)
};
+#endif
static struct platform_driver msm_otg_driver = {
.remove = __devexit_p(msm_otg_remove),
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
+#ifdef CONFIG_PM
.pm = &msm_otg_dev_pm_ops,
+#endif
},
};
diff --git a/drivers/usb/otg/nop-usb-xceiv.c b/drivers/usb/otg/nop-usb-xceiv.c
index 8acf165fe13b..c1e360046435 100644
--- a/drivers/usb/otg/nop-usb-xceiv.c
+++ b/drivers/usb/otg/nop-usb-xceiv.c
@@ -132,7 +132,7 @@ static int __devinit nop_usb_xceiv_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, nop);
- BLOCKING_INIT_NOTIFIER_HEAD(&nop->otg.notifier);
+ ATOMIC_INIT_NOTIFIER_HEAD(&nop->otg.notifier);
return 0;
exit:
diff --git a/drivers/usb/otg/twl4030-usb.c b/drivers/usb/otg/twl4030-usb.c
index 6ca505f333e4..e01b073cc489 100644
--- a/drivers/usb/otg/twl4030-usb.c
+++ b/drivers/usb/otg/twl4030-usb.c
@@ -275,6 +275,8 @@ static enum usb_xceiv_events twl4030_usb_linkstat(struct twl4030_usb *twl)
dev_dbg(twl->dev, "HW_CONDITIONS 0x%02x/%d; link %d\n",
status, status, linkstat);
+ twl->otg.last_event = linkstat;
+
/* REVISIT this assumes host and peripheral controllers
* are registered, and that both are active...
*/
@@ -512,7 +514,7 @@ static irqreturn_t twl4030_usb_irq(int irq, void *_twl)
else
twl4030_phy_resume(twl);
- blocking_notifier_call_chain(&twl->otg.notifier, status,
+ atomic_notifier_call_chain(&twl->otg.notifier, status,
twl->otg.gadget);
}
sysfs_notify(&twl->dev->kobj, NULL, "vbus");
@@ -534,7 +536,7 @@ static void twl4030_usb_phy_init(struct twl4030_usb *twl)
twl->asleep = 0;
}
- blocking_notifier_call_chain(&twl->otg.notifier, status,
+ atomic_notifier_call_chain(&twl->otg.notifier, status,
twl->otg.gadget);
}
sysfs_notify(&twl->dev->kobj, NULL, "vbus");
@@ -623,7 +625,7 @@ static int __devinit twl4030_usb_probe(struct platform_device *pdev)
if (device_create_file(&pdev->dev, &dev_attr_vbus))
dev_warn(&pdev->dev, "could not create sysfs file\n");
- BLOCKING_INIT_NOTIFIER_HEAD(&twl->otg.notifier);
+ ATOMIC_INIT_NOTIFIER_HEAD(&twl->otg.notifier);
/* Our job is to use irqs and status from the power module
* to keep the transceiver disabled when nothing's connected.
diff --git a/drivers/usb/otg/twl6030-usb.c b/drivers/usb/otg/twl6030-usb.c
index 28f770103640..8a91b4b832a1 100644
--- a/drivers/usb/otg/twl6030-usb.c
+++ b/drivers/usb/otg/twl6030-usb.c
@@ -149,7 +149,6 @@ static int twl6030_set_phy_clk(struct otg_transceiver *x, int on)
static int twl6030_phy_init(struct otg_transceiver *x)
{
- u8 hw_state;
struct twl6030_usb *twl;
struct device *dev;
struct twl4030_usb_data *pdata;
@@ -158,11 +157,7 @@ static int twl6030_phy_init(struct otg_transceiver *x)
dev = twl->dev;
pdata = dev->platform_data;
- regulator_enable(twl->usb3v3);
-
- hw_state = twl6030_readb(twl, TWL6030_MODULE_ID0, STS_HW_CONDITIONS);
-
- if (hw_state & STS_USB_ID)
+ if (twl->linkstat == USB_EVENT_ID)
pdata->phy_power(twl->dev, 1, 1);
else
pdata->phy_power(twl->dev, 0, 1);
@@ -180,7 +175,17 @@ static void twl6030_phy_shutdown(struct otg_transceiver *x)
dev = twl->dev;
pdata = dev->platform_data;
pdata->phy_power(twl->dev, 0, 0);
- regulator_disable(twl->usb3v3);
+}
+
+static int twl6030_phy_suspend(struct otg_transceiver *x, int suspend)
+{
+ struct twl6030_usb *twl = xceiv_to_twl(x);
+ struct device *dev = twl->dev;
+ struct twl4030_usb_data *pdata = dev->platform_data;
+
+ pdata->phy_suspend(dev, suspend);
+
+ return 0;
}
static int twl6030_usb_ldo_init(struct twl6030_usb *twl)
@@ -199,16 +204,6 @@ static int twl6030_usb_ldo_init(struct twl6030_usb *twl)
if (IS_ERR(twl->usb3v3))
return -ENODEV;
- regulator_enable(twl->usb3v3);
-
- /* Program the VUSB_CFG_TRANS for ACTIVE state. */
- twl6030_writeb(twl, TWL_MODULE_PM_RECEIVER, 0x3F,
- VUSB_CFG_TRANS);
-
- /* Program the VUSB_CFG_STATE register to ON on all groups. */
- twl6030_writeb(twl, TWL_MODULE_PM_RECEIVER, 0xE1,
- VUSB_CFG_STATE);
-
/* Program the USB_VBUS_CTRL_SET and set VBUS_ACT_COMP bit */
twl6030_writeb(twl, TWL_MODULE_USB, 0x4, USB_VBUS_CTRL_SET);
@@ -261,16 +256,25 @@ static irqreturn_t twl6030_usb_irq(int irq, void *_twl)
CONTROLLER_STAT1);
if (!(hw_state & STS_USB_ID)) {
if (vbus_state & VBUS_DET) {
+ regulator_enable(twl->usb3v3);
+ twl->asleep = 1;
status = USB_EVENT_VBUS;
twl->otg.default_a = false;
twl->otg.state = OTG_STATE_B_IDLE;
+ twl->linkstat = status;
+ twl->otg.last_event = status;
+ atomic_notifier_call_chain(&twl->otg.notifier,
+ status, twl->otg.gadget);
} else {
status = USB_EVENT_NONE;
- }
- if (status >= 0) {
twl->linkstat = status;
- blocking_notifier_call_chain(&twl->otg.notifier,
+ twl->otg.last_event = status;
+ atomic_notifier_call_chain(&twl->otg.notifier,
status, twl->otg.gadget);
+ if (twl->asleep) {
+ regulator_disable(twl->usb3v3);
+ twl->asleep = 0;
+ }
}
}
sysfs_notify(&twl->dev->kobj, NULL, "vbus");
@@ -288,13 +292,17 @@ static irqreturn_t twl6030_usbotg_irq(int irq, void *_twl)
if (hw_state & STS_USB_ID) {
+ regulator_enable(twl->usb3v3);
+ twl->asleep = 1;
twl6030_writeb(twl, TWL_MODULE_USB, USB_ID_INT_EN_HI_CLR, 0x1);
twl6030_writeb(twl, TWL_MODULE_USB, USB_ID_INT_EN_HI_SET,
0x10);
status = USB_EVENT_ID;
twl->otg.default_a = true;
twl->otg.state = OTG_STATE_A_IDLE;
- blocking_notifier_call_chain(&twl->otg.notifier, status,
+ twl->linkstat = status;
+ twl->otg.last_event = status;
+ atomic_notifier_call_chain(&twl->otg.notifier, status,
twl->otg.gadget);
} else {
twl6030_writeb(twl, TWL_MODULE_USB, USB_ID_INT_EN_HI_CLR,
@@ -303,7 +311,6 @@ static irqreturn_t twl6030_usbotg_irq(int irq, void *_twl)
0x1);
}
twl6030_writeb(twl, TWL_MODULE_USB, USB_ID_INT_LATCH_CLR, status);
- twl->linkstat = status;
return IRQ_HANDLED;
}
@@ -395,6 +402,7 @@ static int __devinit twl6030_usb_probe(struct platform_device *pdev)
twl->otg.set_vbus = twl6030_set_vbus;
twl->otg.init = twl6030_phy_init;
twl->otg.shutdown = twl6030_phy_shutdown;
+ twl->otg.set_suspend = twl6030_phy_suspend;
/* init spinlock for workqueue */
spin_lock_init(&twl->lock);
@@ -411,7 +419,7 @@ static int __devinit twl6030_usb_probe(struct platform_device *pdev)
if (device_create_file(&pdev->dev, &dev_attr_vbus))
dev_warn(&pdev->dev, "could not create sysfs file\n");
- BLOCKING_INIT_NOTIFIER_HEAD(&twl->otg.notifier);
+ ATOMIC_INIT_NOTIFIER_HEAD(&twl->otg.notifier);
twl->irq_enabled = true;
status = request_threaded_irq(twl->irq1, NULL, twl6030_usbotg_irq,
@@ -437,7 +445,9 @@ static int __devinit twl6030_usb_probe(struct platform_device *pdev)
return status;
}
+ twl->asleep = 0;
pdata->phy_init(dev);
+ twl6030_phy_suspend(&twl->otg, 0);
twl6030_enable_irq(&twl->otg);
dev_info(&pdev->dev, "Initialized TWL6030 USB module\n");
diff --git a/drivers/usb/otg/ulpi_viewport.c b/drivers/usb/otg/ulpi_viewport.c
new file mode 100644
index 000000000000..e9a37f90994f
--- /dev/null
+++ b/drivers/usb/otg/ulpi_viewport.c
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/usb.h>
+#include <linux/io.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/ulpi.h>
+
+#define ULPI_VIEW_WAKEUP (1 << 31)
+#define ULPI_VIEW_RUN (1 << 30)
+#define ULPI_VIEW_WRITE (1 << 29)
+#define ULPI_VIEW_READ (0 << 29)
+#define ULPI_VIEW_ADDR(x) (((x) & 0xff) << 16)
+#define ULPI_VIEW_DATA_READ(x) (((x) >> 8) & 0xff)
+#define ULPI_VIEW_DATA_WRITE(x) ((x) & 0xff)
+
+static int ulpi_viewport_wait(void __iomem *view, u32 mask)
+{
+ unsigned long usec = 2000;
+
+ while (usec--) {
+ if (!(readl(view) & mask))
+ return 0;
+
+ udelay(1);
+ };
+
+ return -ETIMEDOUT;
+}
+
+static int ulpi_viewport_read(struct otg_transceiver *otg, u32 reg)
+{
+ int ret;
+ void __iomem *view = otg->io_priv;
+
+ writel(ULPI_VIEW_WAKEUP | ULPI_VIEW_WRITE, view);
+ ret = ulpi_viewport_wait(view, ULPI_VIEW_WAKEUP);
+ if (ret)
+ return ret;
+
+ writel(ULPI_VIEW_RUN | ULPI_VIEW_READ | ULPI_VIEW_ADDR(reg), view);
+ ret = ulpi_viewport_wait(view, ULPI_VIEW_RUN);
+ if (ret)
+ return ret;
+
+ return ULPI_VIEW_DATA_READ(readl(view));
+}
+
+static int ulpi_viewport_write(struct otg_transceiver *otg, u32 val, u32 reg)
+{
+ int ret;
+ void __iomem *view = otg->io_priv;
+
+ writel(ULPI_VIEW_WAKEUP | ULPI_VIEW_WRITE, view);
+ ret = ulpi_viewport_wait(view, ULPI_VIEW_WAKEUP);
+ if (ret)
+ return ret;
+
+ writel(ULPI_VIEW_RUN | ULPI_VIEW_WRITE | ULPI_VIEW_DATA_WRITE(val) |
+ ULPI_VIEW_ADDR(reg), view);
+
+ return ulpi_viewport_wait(view, ULPI_VIEW_RUN);
+}
+
+struct otg_io_access_ops ulpi_viewport_access_ops = {
+ .read = ulpi_viewport_read,
+ .write = ulpi_viewport_write,
+};
diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c
index 7b8815ddf368..14ac87ee9251 100644
--- a/drivers/usb/serial/ch341.c
+++ b/drivers/usb/serial/ch341.c
@@ -75,6 +75,7 @@ static int debug;
static const struct usb_device_id id_table[] = {
{ USB_DEVICE(0x4348, 0x5523) },
{ USB_DEVICE(0x1a86, 0x7523) },
+ { USB_DEVICE(0x1a86, 0x5523) },
{ },
};
MODULE_DEVICE_TABLE(usb, id_table);
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index f349a3629d00..a75f9298d88f 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -722,6 +722,8 @@ static struct usb_device_id id_table_combined [] = {
{ USB_DEVICE(FTDI_VID, FTDI_PROPOX_JTAGCABLEII_PID) },
{ USB_DEVICE(OLIMEX_VID, OLIMEX_ARM_USB_OCD_PID),
.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
+ { USB_DEVICE(OLIMEX_VID, OLIMEX_ARM_USB_OCD_H_PID),
+ .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
{ USB_DEVICE(FIC_VID, FIC_NEO1973_DEBUG_PID),
.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
{ USB_DEVICE(FTDI_VID, FTDI_OOCDLINK_PID),
@@ -973,7 +975,7 @@ static __u32 ftdi_2232h_baud_base_to_divisor(int baud, int base)
int divisor3;
/* hi-speed baud rate is 10-bit sampling instead of 16-bit */
- divisor3 = (base / 10 / baud) * 8;
+ divisor3 = base * 8 / (baud * 10);
divisor = divisor3 >> 3;
divisor |= (__u32)divfrac[divisor3 & 0x7] << 14;
diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h
index 117e8e6f93c6..c543e55bafba 100644
--- a/drivers/usb/serial/ftdi_sio_ids.h
+++ b/drivers/usb/serial/ftdi_sio_ids.h
@@ -730,6 +730,7 @@
/* Olimex */
#define OLIMEX_VID 0x15BA
#define OLIMEX_ARM_USB_OCD_PID 0x0003
+#define OLIMEX_ARM_USB_OCD_H_PID 0x002b
/*
* Telldus Technologies
diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c
index 3b246d93cf22..76e3e502c23d 100644
--- a/drivers/usb/serial/io_edgeport.c
+++ b/drivers/usb/serial/io_edgeport.c
@@ -2343,7 +2343,6 @@ static int write_cmd_usb(struct edgeport_port *edge_port,
usb_get_serial_data(edge_port->port->serial);
int status = 0;
struct urb *urb;
- int timeout;
usb_serial_debug_data(debug, &edge_port->port->dev,
__func__, length, buffer);
@@ -2376,8 +2375,6 @@ static int write_cmd_usb(struct edgeport_port *edge_port,
return status;
}
- /* wait for command to finish */
- timeout = COMMAND_TIMEOUT;
#if 0
wait_event(&edge_port->wait_command, !edge_port->commandPending);
diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c
index 0791778a66f3..67f41b526570 100644
--- a/drivers/usb/serial/keyspan.c
+++ b/drivers/usb/serial/keyspan.c
@@ -2121,16 +2121,16 @@ static int keyspan_usa49_send_setup(struct usb_serial *serial,
/* Work out which port within the device is being setup */
device_port = port->number - port->serial->minor;
- dbg("%s - endpoint %d port %d (%d)",
- __func__, usb_pipeendpoint(this_urb->pipe),
- port->number, device_port);
-
- /* Make sure we have an urb then send the message */
+ /* Make sure we have an urb then send the message */
if (this_urb == NULL) {
dbg("%s - oops no urb for port %d.", __func__, port->number);
return -1;
}
+ dbg("%s - endpoint %d port %d (%d)",
+ __func__, usb_pipeendpoint(this_urb->pipe),
+ port->number, device_port);
+
/* Save reset port val for resend.
Don't overwrite resend for open/close condition. */
if ((reset_port + 1) > p_priv->resend_cont)
diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c
index 554a8693a463..7b690f3282a2 100644
--- a/drivers/usb/serial/keyspan_pda.c
+++ b/drivers/usb/serial/keyspan_pda.c
@@ -173,7 +173,8 @@ static void keyspan_pda_wakeup_write(struct work_struct *work)
container_of(work, struct keyspan_pda_private, wakeup_work);
struct usb_serial_port *port = priv->port;
struct tty_struct *tty = tty_port_tty_get(&port->port);
- tty_wakeup(tty);
+ if (tty)
+ tty_wakeup(tty);
tty_kref_put(tty);
}
@@ -206,7 +207,7 @@ static void keyspan_pda_request_unthrottle(struct work_struct *work)
static void keyspan_pda_rx_interrupt(struct urb *urb)
{
struct usb_serial_port *port = urb->context;
- struct tty_struct *tty = tty_port_tty_get(&port->port);
+ struct tty_struct *tty;
unsigned char *data = urb->transfer_buffer;
int retval;
int status = urb->status;
@@ -223,7 +224,7 @@ static void keyspan_pda_rx_interrupt(struct urb *urb)
/* this urb is terminated, clean up */
dbg("%s - urb shutting down with status: %d",
__func__, status);
- goto out;
+ return;
default:
dbg("%s - nonzero urb status received: %d",
__func__, status);
@@ -233,12 +234,14 @@ static void keyspan_pda_rx_interrupt(struct urb *urb)
/* see if the message is data or a status interrupt */
switch (data[0]) {
case 0:
- /* rest of message is rx data */
- if (urb->actual_length) {
+ tty = tty_port_tty_get(&port->port);
+ /* rest of message is rx data */
+ if (tty && urb->actual_length) {
tty_insert_flip_string(tty, data + 1,
urb->actual_length - 1);
tty_flip_buffer_push(tty);
}
+ tty_kref_put(tty);
break;
case 1:
/* status interrupt */
@@ -265,8 +268,6 @@ exit:
dev_err(&port->dev,
"%s - usb_submit_urb failed with result %d",
__func__, retval);
-out:
- tty_kref_put(tty);
}
diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c
index bd5bd8589e04..b382d9a0274d 100644
--- a/drivers/usb/serial/kobil_sct.c
+++ b/drivers/usb/serial/kobil_sct.c
@@ -372,7 +372,7 @@ static void kobil_read_int_callback(struct urb *urb)
}
tty = tty_port_tty_get(&port->port);
- if (urb->actual_length) {
+ if (tty && urb->actual_length) {
/* BEGIN DEBUG */
/*
diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c
index 2849f8c32015..1e225aacf46e 100644
--- a/drivers/usb/serial/mct_u232.c
+++ b/drivers/usb/serial/mct_u232.c
@@ -78,6 +78,8 @@
#include <asm/unaligned.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>
+#include <linux/serial.h>
+#include <linux/ioctl.h>
#include "mct_u232.h"
/*
@@ -104,6 +106,10 @@ static void mct_u232_break_ctl(struct tty_struct *tty, int break_state);
static int mct_u232_tiocmget(struct tty_struct *tty, struct file *file);
static int mct_u232_tiocmset(struct tty_struct *tty, struct file *file,
unsigned int set, unsigned int clear);
+static int mct_u232_ioctl(struct tty_struct *tty, struct file *file,
+ unsigned int cmd, unsigned long arg);
+static int mct_u232_get_icount(struct tty_struct *tty,
+ struct serial_icounter_struct *icount);
static void mct_u232_throttle(struct tty_struct *tty);
static void mct_u232_unthrottle(struct tty_struct *tty);
@@ -150,9 +156,10 @@ static struct usb_serial_driver mct_u232_device = {
.tiocmset = mct_u232_tiocmset,
.attach = mct_u232_startup,
.release = mct_u232_release,
+ .ioctl = mct_u232_ioctl,
+ .get_icount = mct_u232_get_icount,
};
-
struct mct_u232_private {
spinlock_t lock;
unsigned int control_state; /* Modem Line Setting (TIOCM) */
@@ -160,6 +167,9 @@ struct mct_u232_private {
unsigned char last_lsr; /* Line Status Register */
unsigned char last_msr; /* Modem Status Register */
unsigned int rx_flags; /* Throttling flags */
+ struct async_icount icount;
+ wait_queue_head_t msr_wait; /* for handling sleeping while waiting
+ for msr change to happen */
};
#define THROTTLED 0x01
@@ -386,6 +396,20 @@ static int mct_u232_get_modem_stat(struct usb_serial *serial,
return rc;
} /* mct_u232_get_modem_stat */
+static void mct_u232_msr_to_icount(struct async_icount *icount,
+ unsigned char msr)
+{
+ /* Translate Control Line states */
+ if (msr & MCT_U232_MSR_DDSR)
+ icount->dsr++;
+ if (msr & MCT_U232_MSR_DCTS)
+ icount->cts++;
+ if (msr & MCT_U232_MSR_DRI)
+ icount->rng++;
+ if (msr & MCT_U232_MSR_DCD)
+ icount->dcd++;
+} /* mct_u232_msr_to_icount */
+
static void mct_u232_msr_to_state(unsigned int *control_state,
unsigned char msr)
{
@@ -422,6 +446,7 @@ static int mct_u232_startup(struct usb_serial *serial)
if (!priv)
return -ENOMEM;
spin_lock_init(&priv->lock);
+ init_waitqueue_head(&priv->msr_wait);
usb_set_serial_port_data(serial->port[0], priv);
init_waitqueue_head(&serial->port[0]->write_wait);
@@ -621,6 +646,8 @@ static void mct_u232_read_int_callback(struct urb *urb)
/* Record Control Line states */
mct_u232_msr_to_state(&priv->control_state, priv->last_msr);
+ mct_u232_msr_to_icount(&priv->icount, priv->last_msr);
+
#if 0
/* Not yet handled. See belkin_sa.c for further information */
/* Now to report any errors */
@@ -647,6 +674,7 @@ static void mct_u232_read_int_callback(struct urb *urb)
tty_kref_put(tty);
}
#endif
+ wake_up_interruptible(&priv->msr_wait);
spin_unlock_irqrestore(&priv->lock, flags);
exit:
retval = usb_submit_urb(urb, GFP_ATOMIC);
@@ -826,7 +854,6 @@ static void mct_u232_throttle(struct tty_struct *tty)
}
}
-
static void mct_u232_unthrottle(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
@@ -847,6 +874,82 @@ static void mct_u232_unthrottle(struct tty_struct *tty)
}
}
+static int mct_u232_ioctl(struct tty_struct *tty, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ DEFINE_WAIT(wait);
+ struct usb_serial_port *port = tty->driver_data;
+ struct mct_u232_private *mct_u232_port = usb_get_serial_port_data(port);
+ struct async_icount cnow, cprev;
+ unsigned long flags;
+
+ dbg("%s - port %d, cmd = 0x%x", __func__, port->number, cmd);
+
+ switch (cmd) {
+
+ case TIOCMIWAIT:
+
+ dbg("%s (%d) TIOCMIWAIT", __func__, port->number);
+
+ spin_lock_irqsave(&mct_u232_port->lock, flags);
+ cprev = mct_u232_port->icount;
+ spin_unlock_irqrestore(&mct_u232_port->lock, flags);
+ for ( ; ; ) {
+ prepare_to_wait(&mct_u232_port->msr_wait,
+ &wait, TASK_INTERRUPTIBLE);
+ schedule();
+ finish_wait(&mct_u232_port->msr_wait, &wait);
+ /* see if a signal did it */
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ spin_lock_irqsave(&mct_u232_port->lock, flags);
+ cnow = mct_u232_port->icount;
+ spin_unlock_irqrestore(&mct_u232_port->lock, flags);
+ if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
+ cnow.dcd == cprev.dcd && cnow.cts == cprev.cts)
+ return -EIO; /* no change => error */
+ if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
+ ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
+ ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) ||
+ ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts))) {
+ return 0;
+ }
+ cprev = cnow;
+ }
+
+ }
+ return -ENOIOCTLCMD;
+}
+
+static int mct_u232_get_icount(struct tty_struct *tty,
+ struct serial_icounter_struct *icount)
+{
+ struct usb_serial_port *port = tty->driver_data;
+ struct mct_u232_private *mct_u232_port = usb_get_serial_port_data(port);
+ struct async_icount *ic = &mct_u232_port->icount;
+ unsigned long flags;
+
+ spin_lock_irqsave(&mct_u232_port->lock, flags);
+
+ icount->cts = ic->cts;
+ icount->dsr = ic->dsr;
+ icount->rng = ic->rng;
+ icount->dcd = ic->dcd;
+ icount->rx = ic->rx;
+ icount->tx = ic->tx;
+ icount->frame = ic->frame;
+ icount->overrun = ic->overrun;
+ icount->parity = ic->parity;
+ icount->brk = ic->brk;
+ icount->buf_overrun = ic->buf_overrun;
+
+ spin_unlock_irqrestore(&mct_u232_port->lock, flags);
+
+ dbg("%s (%d) TIOCGICOUNT RX=%d, TX=%d",
+ __func__, port->number, icount->rx, icount->tx);
+ return 0;
+}
+
static int __init mct_u232_init(void)
{
int retval;
diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c
index 7d3bc9a3e2b6..ae506f4ee29d 100644
--- a/drivers/usb/serial/mos7720.c
+++ b/drivers/usb/serial/mos7720.c
@@ -2052,7 +2052,7 @@ static int mos7720_startup(struct usb_serial *serial)
struct usb_device *dev;
int i;
char data;
- u16 product = le16_to_cpu(serial->dev->descriptor.idProduct);
+ u16 product;
int ret_val;
dbg("%s: Entering ..........", __func__);
@@ -2062,6 +2062,7 @@ static int mos7720_startup(struct usb_serial *serial)
return -ENODEV;
}
+ product = le16_to_cpu(serial->dev->descriptor.idProduct);
dev = serial->dev;
/*
diff --git a/drivers/usb/serial/opticon.c b/drivers/usb/serial/opticon.c
index eda1f9266c4e..ce82396fc4e8 100644
--- a/drivers/usb/serial/opticon.c
+++ b/drivers/usb/serial/opticon.c
@@ -1,6 +1,7 @@
/*
* Opticon USB barcode to serial driver
*
+ * Copyright (C) 2011 Martin Jansen <martin.jansen@opticon.com>
* Copyright (C) 2008 - 2009 Greg Kroah-Hartman <gregkh@suse.de>
* Copyright (C) 2008 - 2009 Novell Inc.
*
@@ -21,6 +22,16 @@
#include <linux/usb/serial.h>
#include <linux/uaccess.h>
+#define CONTROL_RTS 0x02
+#define RESEND_CTS_STATE 0x03
+
+/* max number of write urbs in flight */
+#define URB_UPPER_LIMIT 8
+
+/* This driver works for the Opticon 1D barcode reader
+ * an examples of 1D barcode types are EAN, UPC, Code39, IATA etc.. */
+#define DRIVER_DESC "Opticon USB barcode to serial driver (1D)"
+
static int debug;
static const struct usb_device_id id_table[] = {
@@ -42,13 +53,13 @@ struct opticon_private {
bool throttled;
bool actually_throttled;
bool rts;
+ bool cts;
int outstanding_urbs;
};
-/* max number of write urbs in flight */
-#define URB_UPPER_LIMIT 4
-static void opticon_bulk_callback(struct urb *urb)
+
+static void opticon_read_bulk_callback(struct urb *urb)
{
struct opticon_private *priv = urb->context;
unsigned char *data = urb->transfer_buffer;
@@ -57,6 +68,7 @@ static void opticon_bulk_callback(struct urb *urb)
struct tty_struct *tty;
int result;
int data_length;
+ unsigned long flags;
dbg("%s - port %d", __func__, port->number);
@@ -87,10 +99,10 @@ static void opticon_bulk_callback(struct urb *urb)
* Data from the device comes with a 2 byte header:
*
* <0x00><0x00>data...
- * This is real data to be sent to the tty layer
+ * This is real data to be sent to the tty layer
* <0x00><0x01)level
- * This is a RTS level change, the third byte is the RTS
- * value (0 for low, 1 for high).
+ * This is a CTS level change, the third byte is the CTS
+ * value (0 for low, 1 for high).
*/
if ((data[0] == 0x00) && (data[1] == 0x00)) {
/* real data, send it to the tty layer */
@@ -103,10 +115,13 @@ static void opticon_bulk_callback(struct urb *urb)
}
} else {
if ((data[0] == 0x00) && (data[1] == 0x01)) {
+ spin_lock_irqsave(&priv->lock, flags);
+ /* CTS status infomation package */
if (data[2] == 0x00)
- priv->rts = false;
+ priv->cts = false;
else
- priv->rts = true;
+ priv->cts = true;
+ spin_unlock_irqrestore(&priv->lock, flags);
} else {
dev_dbg(&priv->udev->dev,
"Unknown data packet received from the device:"
@@ -129,7 +144,7 @@ exit:
usb_rcvbulkpipe(priv->udev,
priv->bulk_address),
priv->bulk_in_buffer, priv->buffer_size,
- opticon_bulk_callback, priv);
+ opticon_read_bulk_callback, priv);
result = usb_submit_urb(priv->bulk_read_urb, GFP_ATOMIC);
if (result)
dev_err(&port->dev,
@@ -140,6 +155,24 @@ exit:
spin_unlock(&priv->lock);
}
+static int send_control_msg(struct usb_serial_port *port, u8 requesttype,
+ u8 val)
+{
+ struct usb_serial *serial = port->serial;
+ int retval;
+ u8 buffer[2];
+
+ buffer[0] = val;
+ /* Send the message to the vendor control endpoint
+ * of the connected device */
+ retval = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+ requesttype,
+ USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE,
+ 0, 0, buffer, 1, 0);
+
+ return retval;
+}
+
static int opticon_open(struct tty_struct *tty, struct usb_serial_port *port)
{
struct opticon_private *priv = usb_get_serial_data(port->serial);
@@ -152,19 +185,30 @@ static int opticon_open(struct tty_struct *tty, struct usb_serial_port *port)
priv->throttled = false;
priv->actually_throttled = false;
priv->port = port;
+ priv->rts = false;
spin_unlock_irqrestore(&priv->lock, flags);
- /* Start reading from the device */
+ /* Clear RTS line */
+ send_control_msg(port, CONTROL_RTS, 0);
+
+ /* Setup the read URB and start reading from the device */
usb_fill_bulk_urb(priv->bulk_read_urb, priv->udev,
usb_rcvbulkpipe(priv->udev,
priv->bulk_address),
priv->bulk_in_buffer, priv->buffer_size,
- opticon_bulk_callback, priv);
+ opticon_read_bulk_callback, priv);
+
+ /* clear the halt status of the enpoint */
+ usb_clear_halt(priv->udev, priv->bulk_read_urb->pipe);
+
result = usb_submit_urb(priv->bulk_read_urb, GFP_KERNEL);
if (result)
dev_err(&port->dev,
"%s - failed resubmitting read urb, error %d\n",
__func__, result);
+ /* Request CTS line state, sometimes during opening the current
+ * CTS state can be missed. */
+ send_control_msg(port, RESEND_CTS_STATE, 1);
return result;
}
@@ -178,7 +222,7 @@ static void opticon_close(struct usb_serial_port *port)
usb_kill_urb(priv->bulk_read_urb);
}
-static void opticon_write_bulk_callback(struct urb *urb)
+static void opticon_write_control_callback(struct urb *urb)
{
struct opticon_private *priv = urb->context;
int status = urb->status;
@@ -210,6 +254,7 @@ static int opticon_write(struct tty_struct *tty, struct usb_serial_port *port,
unsigned char *buffer;
unsigned long flags;
int status;
+ struct usb_ctrlrequest *dr;
dbg("%s - port %d", __func__, port->number);
@@ -226,6 +271,7 @@ static int opticon_write(struct tty_struct *tty, struct usb_serial_port *port,
if (!buffer) {
dev_err(&port->dev, "out of memory\n");
count = -ENOMEM;
+
goto error_no_buffer;
}
@@ -240,35 +286,28 @@ static int opticon_write(struct tty_struct *tty, struct usb_serial_port *port,
usb_serial_debug_data(debug, &port->dev, __func__, count, buffer);
- if (port->bulk_out_endpointAddress) {
- usb_fill_bulk_urb(urb, serial->dev,
- usb_sndbulkpipe(serial->dev,
- port->bulk_out_endpointAddress),
- buffer, count, opticon_write_bulk_callback, priv);
- } else {
- struct usb_ctrlrequest *dr;
-
- dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_NOIO);
- if (!dr)
- return -ENOMEM;
-
- dr->bRequestType = USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT;
- dr->bRequest = 0x01;
- dr->wValue = 0;
- dr->wIndex = 0;
- dr->wLength = cpu_to_le16(count);
-
- usb_fill_control_urb(urb, serial->dev,
- usb_sndctrlpipe(serial->dev, 0),
- (unsigned char *)dr, buffer, count,
- opticon_write_bulk_callback, priv);
- }
+ /* The conncected devices do not have a bulk write endpoint,
+ * to transmit data to de barcode device the control endpoint is used */
+ dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_NOIO);
+ if (!dr)
+ return -ENOMEM;
+
+ dr->bRequestType = USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT;
+ dr->bRequest = 0x01;
+ dr->wValue = 0;
+ dr->wIndex = 0;
+ dr->wLength = cpu_to_le16(count);
+
+ usb_fill_control_urb(urb, serial->dev,
+ usb_sndctrlpipe(serial->dev, 0),
+ (unsigned char *)dr, buffer, count,
+ opticon_write_control_callback, priv);
/* send it down the pipe */
status = usb_submit_urb(urb, GFP_ATOMIC);
if (status) {
dev_err(&port->dev,
- "%s - usb_submit_urb(write bulk) failed with status = %d\n",
+ "%s - usb_submit_urb(write endpoint) failed status = %d\n",
__func__, status);
count = status;
goto error;
@@ -360,16 +399,49 @@ static int opticon_tiocmget(struct tty_struct *tty, struct file *file)
int result = 0;
dbg("%s - port %d", __func__, port->number);
+ if (!usb_get_intfdata(port->serial->interface))
+ return -ENODEV;
spin_lock_irqsave(&priv->lock, flags);
if (priv->rts)
- result = TIOCM_RTS;
+ result |= TIOCM_RTS;
+ if (priv->cts)
+ result |= TIOCM_CTS;
spin_unlock_irqrestore(&priv->lock, flags);
dbg("%s - %x", __func__, result);
return result;
}
+static int opticon_tiocmset(struct tty_struct *tty, struct file *file,
+ unsigned int set, unsigned int clear)
+{
+ struct usb_serial_port *port = tty->driver_data;
+ struct opticon_private *priv = usb_get_serial_data(port->serial);
+ unsigned long flags;
+ bool rts;
+ bool changed = false;
+
+ if (!usb_get_intfdata(port->serial->interface))
+ return -ENODEV;
+ /* We only support RTS so we only handle that */
+ spin_lock_irqsave(&priv->lock, flags);
+
+ rts = priv->rts;
+ if (set & TIOCM_RTS)
+ priv->rts = true;
+ if (clear & TIOCM_RTS)
+ priv->rts = false;
+ changed = rts ^ priv->rts;
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ if (!changed)
+ return 0;
+
+ /* Send the new RTS state to the connected device */
+ return send_control_msg(port, CONTROL_RTS, !rts);
+}
+
static int get_serial_info(struct opticon_private *priv,
struct serial_struct __user *serial)
{
@@ -431,6 +503,7 @@ static int opticon_startup(struct usb_serial *serial)
priv->serial = serial;
priv->port = serial->port[0];
priv->udev = serial->dev;
+ priv->outstanding_urbs = 0; /* Init the outstanding urbs */
/* find our bulk endpoint */
intf = serial->interface->altsetting;
@@ -456,13 +529,6 @@ static int opticon_startup(struct usb_serial *serial)
priv->bulk_address = endpoint->bEndpointAddress;
- /* set up our bulk urb */
- usb_fill_bulk_urb(priv->bulk_read_urb, priv->udev,
- usb_rcvbulkpipe(priv->udev,
- endpoint->bEndpointAddress),
- priv->bulk_in_buffer, priv->buffer_size,
- opticon_bulk_callback, priv);
-
bulk_in_found = true;
break;
}
@@ -558,6 +624,7 @@ static struct usb_serial_driver opticon_device = {
.unthrottle = opticon_unthrottle,
.ioctl = opticon_ioctl,
.tiocmget = opticon_tiocmget,
+ .tiocmset = opticon_tiocmset,
};
static int __init opticon_init(void)
@@ -581,6 +648,7 @@ static void __exit opticon_exit(void)
module_init(opticon_init);
module_exit(opticon_exit);
+MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
module_param(debug, bool, S_IRUGO | S_IWUSR);
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index 5f46838dfee5..75c7f456eed5 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -652,7 +652,8 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0028, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0029, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0030, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MF626, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MF626, 0xff,
+ 0xff, 0xff), .driver_info = (kernel_ulong_t)&four_g_w14_blacklist },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0032, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0033, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0034, 0xff, 0xff, 0xff) },
diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c
index 0457813eebee..5b88b6836a81 100644
--- a/drivers/usb/serial/sierra.c
+++ b/drivers/usb/serial/sierra.c
@@ -376,7 +376,10 @@ static int sierra_send_setup(struct usb_serial_port *port)
if (!do_send)
return 0;
- usb_autopm_get_interface(serial->interface);
+ retval = usb_autopm_get_interface(serial->interface);
+ if (retval < 0)
+ return retval;
+
retval = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
0x22, 0x21, val, interface, NULL, 0, USB_CTRL_SET_TIMEOUT);
usb_autopm_put_interface(serial->interface);
@@ -811,8 +814,12 @@ static void sierra_close(struct usb_serial_port *port)
mutex_lock(&serial->disc_mutex);
if (!serial->disconnected) {
serial->interface->needs_remote_wakeup = 0;
- usb_autopm_get_interface(serial->interface);
- sierra_send_setup(port);
+ /* odd error handling due to pm counters */
+ if (!usb_autopm_get_interface(serial->interface))
+ sierra_send_setup(port);
+ else
+ usb_autopm_get_interface_no_resume(serial->interface);
+
}
mutex_unlock(&serial->disc_mutex);
spin_lock_irq(&intfdata->susp_lock);
@@ -865,7 +872,8 @@ static int sierra_open(struct tty_struct *tty, struct usb_serial_port *port)
/* get rid of everything as in close */
sierra_close(port);
/* restore balance for autopm */
- usb_autopm_put_interface(serial->interface);
+ if (!serial->disconnected)
+ usb_autopm_put_interface(serial->interface);
return err;
}
sierra_send_setup(port);
diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c
index 546a52179bec..2ff90a9c8f47 100644
--- a/drivers/usb/serial/usb-serial.c
+++ b/drivers/usb/serial/usb-serial.c
@@ -911,9 +911,8 @@ int usb_serial_probe(struct usb_interface *interface,
dev_err(&interface->dev, "No free urbs available\n");
goto probe_error;
}
- buffer_size = serial->type->bulk_in_size;
- if (!buffer_size)
- buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
+ buffer_size = max_t(int, serial->type->bulk_in_size,
+ le16_to_cpu(endpoint->wMaxPacketSize));
port->bulk_in_size = buffer_size;
port->bulk_in_endpointAddress = endpoint->bEndpointAddress;
port->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL);
diff --git a/drivers/usb/serial/usb_wwan.c b/drivers/usb/serial/usb_wwan.c
index 9c014e2ecd68..eb95aecc0015 100644
--- a/drivers/usb/serial/usb_wwan.c
+++ b/drivers/usb/serial/usb_wwan.c
@@ -261,7 +261,8 @@ int usb_wwan_write(struct tty_struct *tty, struct usb_serial_port *port,
intfdata->in_flight--;
spin_unlock_irqrestore(&intfdata->susp_lock,
flags);
- continue;
+ usb_autopm_put_interface_async(port->serial->interface);
+ break;
}
}
@@ -308,11 +309,16 @@ static void usb_wwan_indat_callback(struct urb *urb)
/* Resubmit urb so we continue receiving */
if (status != -ESHUTDOWN) {
err = usb_submit_urb(urb, GFP_ATOMIC);
- if (err && err != -EPERM)
- printk(KERN_ERR "%s: resubmit read urb failed. "
- "(%d)", __func__, err);
- else
+ if (err) {
+ if (err != -EPERM) {
+ printk(KERN_ERR "%s: resubmit read urb failed. "
+ "(%d)", __func__, err);
+ /* busy also in error unless we are killed */
+ usb_mark_last_busy(port->serial->dev);
+ }
+ } else {
usb_mark_last_busy(port->serial->dev);
+ }
}
}
@@ -421,6 +427,7 @@ int usb_wwan_open(struct tty_struct *tty, struct usb_serial_port *port)
spin_lock_irq(&intfdata->susp_lock);
portdata->opened = 1;
spin_unlock_irq(&intfdata->susp_lock);
+ /* this balances a get in the generic USB serial code */
usb_autopm_put_interface(serial->interface);
return 0;
@@ -447,7 +454,8 @@ void usb_wwan_close(struct usb_serial_port *port)
usb_kill_urb(portdata->in_urbs[i]);
for (i = 0; i < N_OUT_URB; i++)
usb_kill_urb(portdata->out_urbs[i]);
- usb_autopm_get_interface(serial->interface);
+ /* balancing - important as an error cannot be handled*/
+ usb_autopm_get_interface_no_resume(serial->interface);
serial->interface->needs_remote_wakeup = 0;
}
}
@@ -661,6 +669,18 @@ int usb_wwan_suspend(struct usb_serial *serial, pm_message_t message)
}
EXPORT_SYMBOL(usb_wwan_suspend);
+static void unbusy_queued_urb(struct urb *urb, struct usb_wwan_port_private *portdata)
+{
+ int i;
+
+ for (i = 0; i < N_OUT_URB; i++) {
+ if (urb == portdata->out_urbs[i]) {
+ clear_bit(i, &portdata->out_busy);
+ break;
+ }
+ }
+}
+
static void play_delayed(struct usb_serial_port *port)
{
struct usb_wwan_intf_private *data;
@@ -672,8 +692,17 @@ static void play_delayed(struct usb_serial_port *port)
data = port->serial->private;
while ((urb = usb_get_from_anchor(&portdata->delayed))) {
err = usb_submit_urb(urb, GFP_ATOMIC);
- if (!err)
+ if (!err) {
data->in_flight++;
+ } else {
+ /* we have to throw away the rest */
+ do {
+ unbusy_queued_urb(urb, portdata);
+ //extremely dirty
+ atomic_dec(&port->serial->interface->dev.power.usage_count);
+ } while ((urb = usb_get_from_anchor(&portdata->delayed)));
+ break;
+ }
}
}
diff --git a/drivers/usb/storage/Kconfig b/drivers/usb/storage/Kconfig
index 49a489e03716..353aeb44da6a 100644
--- a/drivers/usb/storage/Kconfig
+++ b/drivers/usb/storage/Kconfig
@@ -31,6 +31,16 @@ config USB_STORAGE_DEBUG
Say Y here in order to have the USB Mass Storage code generate
verbose debugging messages.
+config USB_STORAGE_REALTEK
+ tristate "Realtek Card Reader support"
+ depends on USB_STORAGE
+ help
+ Say Y here to include additional code to support the power-saving function
+ for Realtek RTS51xx USB card readers.
+
+ If this driver is compiled as a module, it will be named ums-realtek.
+
+
config USB_STORAGE_DATAFAB
tristate "Datafab Compact Flash Reader support"
depends on USB_STORAGE
diff --git a/drivers/usb/storage/Makefile b/drivers/usb/storage/Makefile
index fcf14cdc4a04..0d2de971bd91 100644
--- a/drivers/usb/storage/Makefile
+++ b/drivers/usb/storage/Makefile
@@ -30,6 +30,7 @@ obj-$(CONFIG_USB_STORAGE_ISD200) += ums-isd200.o
obj-$(CONFIG_USB_STORAGE_JUMPSHOT) += ums-jumpshot.o
obj-$(CONFIG_USB_STORAGE_KARMA) += ums-karma.o
obj-$(CONFIG_USB_STORAGE_ONETOUCH) += ums-onetouch.o
+obj-$(CONFIG_USB_STORAGE_REALTEK) += ums-realtek.o
obj-$(CONFIG_USB_STORAGE_SDDR09) += ums-sddr09.o
obj-$(CONFIG_USB_STORAGE_SDDR55) += ums-sddr55.o
obj-$(CONFIG_USB_STORAGE_USBAT) += ums-usbat.o
@@ -42,6 +43,7 @@ ums-isd200-y := isd200.o
ums-jumpshot-y := jumpshot.o
ums-karma-y := karma.o
ums-onetouch-y := onetouch.o
+ums-realtek-y := realtek_cr.o
ums-sddr09-y := sddr09.o
ums-sddr55-y := sddr55.o
ums-usbat-y := shuttle_usbat.o
diff --git a/drivers/usb/storage/realtek_cr.c b/drivers/usb/storage/realtek_cr.c
new file mode 100644
index 000000000000..d509a4a7d74f
--- /dev/null
+++ b/drivers/usb/storage/realtek_cr.c
@@ -0,0 +1,675 @@
+/* Driver for Realtek RTS51xx USB card reader
+ *
+ * Copyright(c) 2009 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ * wwang (wei_wang@realsil.com.cn)
+ * No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China
+ */
+
+#include <linux/module.h>
+#include <linux/blkdev.h>
+#include <linux/kthread.h>
+#include <linux/sched.h>
+#include <linux/workqueue.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <linux/cdrom.h>
+
+#include <linux/usb.h>
+#include <linux/slab.h>
+#include <linux/usb_usual.h>
+
+#include "usb.h"
+#include "transport.h"
+#include "protocol.h"
+#include "debug.h"
+
+MODULE_DESCRIPTION("Driver for Realtek USB Card Reader");
+MODULE_AUTHOR("wwang <wei_wang@realsil.com.cn>");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1.03");
+
+static int auto_delink_en = 1;
+module_param(auto_delink_en, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(auto_delink_en, "enable auto delink");
+
+struct rts51x_status {
+ u16 vid;
+ u16 pid;
+ u8 cur_lun;
+ u8 card_type;
+ u8 total_lun;
+ u16 fw_ver;
+ u8 phy_exist;
+ u8 multi_flag;
+ u8 multi_card;
+ u8 log_exist;
+ union {
+ u8 detailed_type1;
+ u8 detailed_type2;
+ } detailed_type;
+ u8 function[2];
+};
+
+struct rts51x_chip {
+ u16 vendor_id;
+ u16 product_id;
+ char max_lun;
+
+ struct rts51x_status *status;
+ int status_len;
+
+ u32 flag;
+};
+
+/* flag definition */
+#define FLIDX_AUTO_DELINK 0x01
+
+#define SCSI_LUN(srb) ((srb)->device->lun)
+
+/* Bit Operation */
+#define SET_BIT(data, idx) ((data) |= 1 << (idx))
+#define CLR_BIT(data, idx) ((data) &= ~(1 << (idx)))
+#define CHK_BIT(data, idx) ((data) & (1 << (idx)))
+
+#define SET_AUTO_DELINK(chip) ((chip)->flag |= FLIDX_AUTO_DELINK)
+#define CLR_AUTO_DELINK(chip) ((chip)->flag &= ~FLIDX_AUTO_DELINK)
+#define CHK_AUTO_DELINK(chip) ((chip)->flag & FLIDX_AUTO_DELINK)
+
+#define RTS51X_GET_VID(chip) ((chip)->vendor_id)
+#define RTS51X_GET_PID(chip) ((chip)->product_id)
+
+#define FW_VERSION(chip) ((chip)->status[0].fw_ver)
+#define STATUS_LEN(chip) ((chip)->status_len)
+
+/* Check card reader function */
+#define SUPPORT_DETAILED_TYPE1(chip) \
+ CHK_BIT((chip)->status[0].function[0], 1)
+#define SUPPORT_OT(chip) \
+ CHK_BIT((chip)->status[0].function[0], 2)
+#define SUPPORT_OC(chip) \
+ CHK_BIT((chip)->status[0].function[0], 3)
+#define SUPPORT_AUTO_DELINK(chip) \
+ CHK_BIT((chip)->status[0].function[0], 4)
+#define SUPPORT_SDIO(chip) \
+ CHK_BIT((chip)->status[0].function[1], 0)
+#define SUPPORT_DETAILED_TYPE2(chip) \
+ CHK_BIT((chip)->status[0].function[1], 1)
+
+#define CHECK_PID(chip, pid) (RTS51X_GET_PID(chip) == (pid))
+#define CHECK_FW_VER(chip, fw_ver) (FW_VERSION(chip) == (fw_ver))
+#define CHECK_ID(chip, pid, fw_ver) \
+ (CHECK_PID((chip), (pid)) && CHECK_FW_VER((chip), (fw_ver)))
+
+#define wait_timeout_x(task_state, msecs) \
+do { \
+ set_current_state((task_state)); \
+ schedule_timeout((msecs) * HZ / 1000); \
+} while (0)
+
+#define wait_timeout(msecs) \
+ wait_timeout_x(TASK_INTERRUPTIBLE, (msecs))
+
+static int init_realtek_cr(struct us_data *us);
+
+/*
+ * The table of devices
+ */
+#define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \
+ vendorName, productName, useProtocol, useTransport, \
+ initFunction, flags) \
+{\
+ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \
+ .driver_info = (flags)|(USB_US_TYPE_STOR<<24)\
+}
+
+static const struct usb_device_id realtek_cr_ids[] = {
+# include "unusual_realtek.h"
+ { } /* Terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, realtek_cr_ids);
+
+#undef UNUSUAL_DEV
+
+/*
+ * The flags table
+ */
+#define UNUSUAL_DEV(idVendor, idProduct, bcdDeviceMin, bcdDeviceMax, \
+ vendor_name, product_name, use_protocol, use_transport, \
+ init_function, Flags) \
+{ \
+ .vendorName = vendor_name, \
+ .productName = product_name, \
+ .useProtocol = use_protocol, \
+ .useTransport = use_transport, \
+ .initFunction = init_function, \
+}
+
+static struct us_unusual_dev realtek_cr_unusual_dev_list[] = {
+# include "unusual_realtek.h"
+ { } /* Terminating entry */
+};
+
+#undef UNUSUAL_DEV
+
+static int rts51x_bulk_transport(struct us_data *us, u8 lun,
+ u8 *cmd, int cmd_len, u8 *buf, int buf_len,
+ enum dma_data_direction dir, int *act_len)
+{
+ struct bulk_cb_wrap *bcb = (struct bulk_cb_wrap *) us->iobuf;
+ struct bulk_cs_wrap *bcs = (struct bulk_cs_wrap *) us->iobuf;
+ int result;
+ unsigned int residue;
+ unsigned int cswlen;
+ unsigned int cbwlen = US_BULK_CB_WRAP_LEN;
+
+ /* set up the command wrapper */
+ bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN);
+ bcb->DataTransferLength = cpu_to_le32(buf_len);
+ bcb->Flags = (dir == DMA_FROM_DEVICE) ? 1 << 7 : 0;
+ bcb->Tag = ++us->tag;
+ bcb->Lun = lun;
+ bcb->Length = cmd_len;
+
+ /* copy the command payload */
+ memset(bcb->CDB, 0, sizeof(bcb->CDB));
+ memcpy(bcb->CDB, cmd, bcb->Length);
+
+ /* send it to out endpoint */
+ result = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe,
+ bcb, cbwlen, NULL);
+ if (result != USB_STOR_XFER_GOOD)
+ return USB_STOR_TRANSPORT_ERROR;
+
+ /* DATA STAGE */
+ /* send/receive data payload, if there is any */
+
+ if (buf && buf_len) {
+ unsigned int pipe = (dir == DMA_FROM_DEVICE) ?
+ us->recv_bulk_pipe : us->send_bulk_pipe;
+ result = usb_stor_bulk_transfer_buf(us, pipe,
+ buf, buf_len, NULL);
+ if (result == USB_STOR_XFER_ERROR)
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+
+ /* get CSW for device status */
+ result = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe,
+ bcs, US_BULK_CS_WRAP_LEN, &cswlen);
+ if (result != USB_STOR_XFER_GOOD)
+ return USB_STOR_TRANSPORT_ERROR;
+
+ /* check bulk status */
+ if (bcs->Signature != cpu_to_le32(US_BULK_CS_SIGN)) {
+ US_DEBUGP("Signature mismatch: got %08X, expecting %08X\n",
+ le32_to_cpu(bcs->Signature),
+ US_BULK_CS_SIGN);
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+
+ residue = bcs->Residue;
+ if (bcs->Tag != us->tag)
+ return USB_STOR_TRANSPORT_ERROR;
+
+ /* try to compute the actual residue, based on how much data
+ * was really transferred and what the device tells us */
+ if (residue)
+ residue = residue < buf_len ? residue : buf_len;
+
+ if (act_len)
+ *act_len = buf_len - residue;
+
+ /* based on the status code, we report good or bad */
+ switch (bcs->Status) {
+ case US_BULK_STAT_OK:
+ /* command good -- note that data could be short */
+ return USB_STOR_TRANSPORT_GOOD;
+
+ case US_BULK_STAT_FAIL:
+ /* command failed */
+ return USB_STOR_TRANSPORT_FAILED;
+
+ case US_BULK_STAT_PHASE:
+ /* phase error -- note that a transport reset will be
+ * invoked by the invoke_transport() function
+ */
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+
+ /* we should never get here, but if we do, we're in trouble */
+ return USB_STOR_TRANSPORT_ERROR;
+}
+
+/* Determine what the maximum LUN supported is */
+static int rts51x_get_max_lun(struct us_data *us)
+{
+ int result;
+
+ /* issue the command */
+ us->iobuf[0] = 0;
+ result = usb_stor_control_msg(us, us->recv_ctrl_pipe,
+ US_BULK_GET_MAX_LUN,
+ USB_DIR_IN | USB_TYPE_CLASS |
+ USB_RECIP_INTERFACE,
+ 0, us->ifnum, us->iobuf, 1, 10*HZ);
+
+ US_DEBUGP("GetMaxLUN command result is %d, data is %d\n",
+ result, us->iobuf[0]);
+
+ /* if we have a successful request, return the result */
+ if (result > 0)
+ return us->iobuf[0];
+
+ return 0;
+}
+
+static int rts51x_read_mem(struct us_data *us, u16 addr, u8 *data, u16 len)
+{
+ int retval;
+ u8 cmnd[12] = {0};
+
+ US_DEBUGP("%s, addr = 0x%x, len = %d\n", __func__, addr, len);
+
+ cmnd[0] = 0xF0;
+ cmnd[1] = 0x0D;
+ cmnd[2] = (u8)(addr >> 8);
+ cmnd[3] = (u8)addr;
+ cmnd[4] = (u8)(len >> 8);
+ cmnd[5] = (u8)len;
+
+ retval = rts51x_bulk_transport(us, 0, cmnd, 12,
+ data, len, DMA_FROM_DEVICE, NULL);
+ if (retval != USB_STOR_TRANSPORT_GOOD)
+ return -EIO;
+
+ return 0;
+}
+
+static int rts51x_write_mem(struct us_data *us, u16 addr, u8 *data, u16 len)
+{
+ int retval;
+ u8 cmnd[12] = {0};
+
+ US_DEBUGP("%s, addr = 0x%x, len = %d\n", __func__, addr, len);
+
+ cmnd[0] = 0xF0;
+ cmnd[1] = 0x0E;
+ cmnd[2] = (u8)(addr >> 8);
+ cmnd[3] = (u8)addr;
+ cmnd[4] = (u8)(len >> 8);
+ cmnd[5] = (u8)len;
+
+ retval = rts51x_bulk_transport(us, 0, cmnd, 12,
+ data, len, DMA_TO_DEVICE, NULL);
+ if (retval != USB_STOR_TRANSPORT_GOOD)
+ return -EIO;
+
+ return 0;
+}
+
+static int rts51x_read_status(struct us_data *us,
+ u8 lun, u8 *status, int len, int *actlen)
+{
+ int retval;
+ u8 cmnd[12] = {0};
+
+ US_DEBUGP("%s, lun = %d\n", __func__, lun);
+
+ cmnd[0] = 0xF0;
+ cmnd[1] = 0x09;
+
+ retval = rts51x_bulk_transport(us, lun, cmnd, 12,
+ status, len, DMA_FROM_DEVICE, actlen);
+ if (retval != USB_STOR_TRANSPORT_GOOD)
+ return -EIO;
+
+ return 0;
+}
+
+static int rts51x_check_status(struct us_data *us, u8 lun)
+{
+ struct rts51x_chip *chip = (struct rts51x_chip *)(us->extra);
+ int retval;
+ u8 buf[16];
+
+ retval = rts51x_read_status(us, lun, buf, 16, &(chip->status_len));
+ if (retval < 0)
+ return -EIO;
+
+ US_DEBUGP("chip->status_len = %d\n", chip->status_len);
+
+ chip->status[lun].vid = ((u16)buf[0] << 8) | buf[1];
+ chip->status[lun].pid = ((u16)buf[2] << 8) | buf[3];
+ chip->status[lun].cur_lun = buf[4];
+ chip->status[lun].card_type = buf[5];
+ chip->status[lun].total_lun = buf[6];
+ chip->status[lun].fw_ver = ((u16)buf[7] << 8) | buf[8];
+ chip->status[lun].phy_exist = buf[9];
+ chip->status[lun].multi_flag = buf[10];
+ chip->status[lun].multi_card = buf[11];
+ chip->status[lun].log_exist = buf[12];
+ if (chip->status_len == 16) {
+ chip->status[lun].detailed_type.detailed_type1 = buf[13];
+ chip->status[lun].function[0] = buf[14];
+ chip->status[lun].function[1] = buf[15];
+ }
+
+ return 0;
+}
+
+static int enable_oscillator(struct us_data *us)
+{
+ int retval;
+ u8 value;
+
+ retval = rts51x_read_mem(us, 0xFE77, &value, 1);
+ if (retval < 0)
+ return -EIO;
+
+ value |= 0x04;
+ retval = rts51x_write_mem(us, 0xFE77, &value, 1);
+ if (retval < 0)
+ return -EIO;
+
+ retval = rts51x_read_mem(us, 0xFE77, &value, 1);
+ if (retval < 0)
+ return -EIO;
+
+ if (!(value & 0x04))
+ return -EIO;
+
+ return 0;
+}
+
+static int do_config_autodelink(struct us_data *us, int enable, int force)
+{
+ int retval;
+ u8 value;
+
+ retval = rts51x_read_mem(us, 0xFE47, &value, 1);
+ if (retval < 0)
+ return -EIO;
+
+ if (enable) {
+ if (force)
+ value |= 0x03;
+ else
+ value |= 0x01;
+ } else {
+ value &= ~0x03;
+ }
+
+ US_DEBUGP("In %s,set 0xfe47 to 0x%x\n", __func__, value);
+
+ retval = rts51x_write_mem(us, 0xFE47, &value, 1);
+ if (retval < 0)
+ return -EIO;
+
+ return 0;
+}
+
+static int config_autodelink_after_power_on(struct us_data *us)
+{
+ struct rts51x_chip *chip = (struct rts51x_chip *)(us->extra);
+ int retval;
+ u8 value;
+
+ if (!CHK_AUTO_DELINK(chip))
+ return 0;
+
+ retval = rts51x_read_mem(us, 0xFE47, &value, 1);
+ if (retval < 0)
+ return -EIO;
+
+ if (auto_delink_en) {
+ CLR_BIT(value, 0);
+ CLR_BIT(value, 1);
+ SET_BIT(value, 2);
+
+ if (CHECK_ID(chip, 0x0138, 0x3882))
+ CLR_BIT(value, 2);
+
+ SET_BIT(value, 7);
+
+ retval = rts51x_write_mem(us, 0xFE47, &value, 1);
+ if (retval < 0)
+ return -EIO;
+
+ retval = enable_oscillator(us);
+ if (retval == 0)
+ (void)do_config_autodelink(us, 1, 0);
+ } else {
+ /* Autodelink controlled by firmware */
+
+ SET_BIT(value, 2);
+
+ if (CHECK_ID(chip, 0x0138, 0x3882))
+ CLR_BIT(value, 2);
+
+ if (CHECK_ID(chip, 0x0159, 0x5889) ||
+ CHECK_ID(chip, 0x0138, 0x3880)) {
+ CLR_BIT(value, 0);
+ CLR_BIT(value, 7);
+ }
+
+ retval = rts51x_write_mem(us, 0xFE47, &value, 1);
+ if (retval < 0)
+ return -EIO;
+
+ if (CHECK_ID(chip, 0x0159, 0x5888)) {
+ value = 0xFF;
+ retval = rts51x_write_mem(us, 0xFE79, &value, 1);
+ if (retval < 0)
+ return -EIO;
+
+ value = 0x01;
+ retval = rts51x_write_mem(us, 0x48, &value, 1);
+ if (retval < 0)
+ return -EIO;
+ }
+ }
+
+ return 0;
+}
+
+static int config_autodelink_before_power_down(struct us_data *us)
+{
+ struct rts51x_chip *chip = (struct rts51x_chip *)(us->extra);
+ int retval;
+ u8 value;
+
+ if (!CHK_AUTO_DELINK(chip))
+ return 0;
+
+ if (auto_delink_en) {
+ retval = rts51x_read_mem(us, 0xFE77, &value, 1);
+ if (retval < 0)
+ return -EIO;
+
+ SET_BIT(value, 2);
+ retval = rts51x_write_mem(us, 0xFE77, &value, 1);
+ if (retval < 0)
+ return -EIO;
+
+ if (CHECK_ID(chip, 0x0159, 0x5888)) {
+ value = 0x01;
+ retval = rts51x_write_mem(us, 0x48, &value, 1);
+ if (retval < 0)
+ return -EIO;
+ }
+
+ retval = rts51x_read_mem(us, 0xFE47, &value, 1);
+ if (retval < 0)
+ return -EIO;
+
+ SET_BIT(value, 0);
+ if (CHECK_ID(chip, 0x0138, 0x3882))
+ SET_BIT(value, 2);
+ retval = rts51x_write_mem(us, 0xFE77, &value, 1);
+ if (retval < 0)
+ return -EIO;
+ } else {
+ if (CHECK_ID(chip, 0x0159, 0x5889) ||
+ CHECK_ID(chip, 0x0138, 0x3880) ||
+ CHECK_ID(chip, 0x0138, 0x3882)) {
+ retval = rts51x_read_mem(us, 0xFE47, &value, 1);
+ if (retval < 0)
+ return -EIO;
+
+ if (CHECK_ID(chip, 0x0159, 0x5889) ||
+ CHECK_ID(chip, 0x0138, 0x3880)) {
+ SET_BIT(value, 0);
+ SET_BIT(value, 7);
+ }
+
+ if (CHECK_ID(chip, 0x0138, 0x3882))
+ SET_BIT(value, 2);
+
+ retval = rts51x_write_mem(us, 0xFE47, &value, 1);
+ if (retval < 0)
+ return -EIO;
+ }
+
+ if (CHECK_ID(chip, 0x0159, 0x5888)) {
+ value = 0x01;
+ retval = rts51x_write_mem(us, 0x48, &value, 1);
+ if (retval < 0)
+ return -EIO;
+ }
+ }
+
+ return 0;
+}
+
+static void realtek_cr_destructor(void *extra)
+{
+ struct rts51x_chip *chip = (struct rts51x_chip *)extra;
+
+ if (!chip)
+ return;
+
+ kfree(chip->status);
+}
+
+#ifdef CONFIG_PM
+static void realtek_pm_hook(struct us_data *us, int pm_state)
+{
+ if (pm_state == US_SUSPEND)
+ (void)config_autodelink_before_power_down(us);
+}
+#endif
+
+static int init_realtek_cr(struct us_data *us)
+{
+ struct rts51x_chip *chip;
+ int size, i, retval;
+
+ chip = kzalloc(sizeof(struct rts51x_chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ us->extra = chip;
+ us->extra_destructor = realtek_cr_destructor;
+#ifdef CONFIG_PM
+ us->suspend_resume_hook = realtek_pm_hook;
+#endif
+
+ us->max_lun = chip->max_lun = rts51x_get_max_lun(us);
+
+ US_DEBUGP("chip->max_lun = %d\n", chip->max_lun);
+
+ size = (chip->max_lun + 1) * sizeof(struct rts51x_status);
+ chip->status = kzalloc(size, GFP_KERNEL);
+ if (!chip->status)
+ goto INIT_FAIL;
+
+ for (i = 0; i <= (int)(chip->max_lun); i++) {
+ retval = rts51x_check_status(us, (u8)i);
+ if (retval < 0)
+ goto INIT_FAIL;
+ }
+
+ if (CHECK_FW_VER(chip, 0x5888) || CHECK_FW_VER(chip, 0x5889) ||
+ CHECK_FW_VER(chip, 0x5901))
+ SET_AUTO_DELINK(chip);
+ if (STATUS_LEN(chip) == 16) {
+ if (SUPPORT_AUTO_DELINK(chip))
+ SET_AUTO_DELINK(chip);
+ }
+
+ US_DEBUGP("chip->flag = 0x%x\n", chip->flag);
+
+ (void)config_autodelink_after_power_on(us);
+
+ return 0;
+
+INIT_FAIL:
+ if (us->extra) {
+ kfree(chip->status);
+ kfree(us->extra);
+ us->extra = NULL;
+ }
+
+ return -EIO;
+}
+
+static int realtek_cr_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct us_data *us;
+ int result;
+
+ US_DEBUGP("Probe Realtek Card Reader!\n");
+
+ result = usb_stor_probe1(&us, intf, id,
+ (id - realtek_cr_ids) + realtek_cr_unusual_dev_list);
+ if (result)
+ return result;
+
+ result = usb_stor_probe2(us);
+ return result;
+}
+
+static struct usb_driver realtek_cr_driver = {
+ .name = "ums-realtek",
+ .probe = realtek_cr_probe,
+ .disconnect = usb_stor_disconnect,
+ .suspend = usb_stor_suspend,
+ .resume = usb_stor_resume,
+ .reset_resume = usb_stor_reset_resume,
+ .pre_reset = usb_stor_pre_reset,
+ .post_reset = usb_stor_post_reset,
+ .id_table = realtek_cr_ids,
+ .soft_unbind = 1,
+};
+
+static int __init realtek_cr_init(void)
+{
+ return usb_register(&realtek_cr_driver);
+}
+
+static void __exit realtek_cr_exit(void)
+{
+ usb_deregister(&realtek_cr_driver);
+}
+
+module_init(realtek_cr_init);
+module_exit(realtek_cr_exit);
diff --git a/drivers/usb/storage/sierra_ms.c b/drivers/usb/storage/sierra_ms.c
index ceba512f84d0..1deca07c8265 100644
--- a/drivers/usb/storage/sierra_ms.c
+++ b/drivers/usb/storage/sierra_ms.c
@@ -126,13 +126,11 @@ static DEVICE_ATTR(truinst, S_IRUGO, show_truinst, NULL);
int sierra_ms_init(struct us_data *us)
{
int result, retries;
- signed long delay_t;
struct swoc_info *swocInfo;
struct usb_device *udev;
struct Scsi_Host *sh;
struct scsi_device *sd;
- delay_t = 2;
retries = 3;
result = 0;
udev = us->pusb_dev;
diff --git a/drivers/usb/storage/unusual_realtek.h b/drivers/usb/storage/unusual_realtek.h
new file mode 100644
index 000000000000..3236e0328516
--- /dev/null
+++ b/drivers/usb/storage/unusual_realtek.h
@@ -0,0 +1,41 @@
+/* Driver for Realtek RTS51xx USB card reader
+ *
+ * Copyright(c) 2009 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ * wwang (wei_wang@realsil.com.cn)
+ * No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China
+ */
+
+#if defined(CONFIG_USB_STORAGE_REALTEK) || \
+ defined(CONFIG_USB_STORAGE_REALTEK_MODULE)
+
+UNUSUAL_DEV(0x0bda, 0x0159, 0x0000, 0x9999,
+ "Realtek",
+ "USB Card Reader",
+ USB_SC_SCSI, USB_PR_BULK, init_realtek_cr, 0),
+
+UNUSUAL_DEV(0x0bda, 0x0158, 0x0000, 0x9999,
+ "Realtek",
+ "USB Card Reader",
+ USB_SC_SCSI, USB_PR_BULK, init_realtek_cr, 0),
+
+UNUSUAL_DEV(0x0bda, 0x0138, 0x0000, 0x9999,
+ "Realtek",
+ "USB Card Reader",
+ USB_SC_SCSI, USB_PR_BULK, init_realtek_cr, 0),
+
+#endif /* defined(CONFIG_USB_STORAGE_REALTEK) || ... */
diff --git a/drivers/usb/storage/usual-tables.c b/drivers/usb/storage/usual-tables.c
index 468bde7d1971..4293077c01aa 100644
--- a/drivers/usb/storage/usual-tables.c
+++ b/drivers/usb/storage/usual-tables.c
@@ -85,6 +85,7 @@ static struct ignore_entry ignore_ids[] = {
# include "unusual_jumpshot.h"
# include "unusual_karma.h"
# include "unusual_onetouch.h"
+# include "unusual_realtek.h"
# include "unusual_sddr09.h"
# include "unusual_sddr55.h"
# include "unusual_usbat.h"
diff --git a/drivers/usb/wusbcore/rh.c b/drivers/usb/wusbcore/rh.c
index a68ad7aa0b59..c175b7300c73 100644
--- a/drivers/usb/wusbcore/rh.c
+++ b/drivers/usb/wusbcore/rh.c
@@ -156,7 +156,7 @@ int wusbhc_rh_status_data(struct usb_hcd *usb_hcd, char *_buf)
EXPORT_SYMBOL_GPL(wusbhc_rh_status_data);
/*
- * Return the hub's desciptor
+ * Return the hub's descriptor
*
* NOTE: almost cut and paste from ehci-hub.c
*
@@ -184,8 +184,8 @@ static int wusbhc_rh_get_hub_descr(struct wusbhc *wusbhc, u16 wValue,
descr->bPwrOn2PwrGood = 0;
descr->bHubContrCurrent = 0;
/* two bitmaps: ports removable, and usb 1.0 legacy PortPwrCtrlMask */
- memset(&descr->bitmap[0], 0, temp);
- memset(&descr->bitmap[temp], 0xff, temp);
+ memset(&descr->u.hs.DeviceRemovable[0], 0, temp);
+ memset(&descr->u.hs.DeviceRemovable[temp], 0xff, temp);
return 0;
}
diff --git a/drivers/usb/wusbcore/wusbhc.c b/drivers/usb/wusbcore/wusbhc.c
index 2054d4ee9774..0faca16df765 100644
--- a/drivers/usb/wusbcore/wusbhc.c
+++ b/drivers/usb/wusbcore/wusbhc.c
@@ -320,7 +320,7 @@ u8 wusb_cluster_id_get(void)
u8 id;
spin_lock(&wusb_cluster_ids_lock);
id = find_first_zero_bit(wusb_cluster_id_table, CLUSTER_IDS);
- if (id > CLUSTER_IDS) {
+ if (id >= CLUSTER_IDS) {
id = 0;
goto out;
}
diff --git a/drivers/usb/wusbcore/wusbhc.h b/drivers/usb/wusbcore/wusbhc.h
index 3d94c4247f46..6bd426b7ec07 100644
--- a/drivers/usb/wusbcore/wusbhc.h
+++ b/drivers/usb/wusbcore/wusbhc.h
@@ -132,7 +132,7 @@ static inline void wusb_dev_put(struct wusb_dev *wusb_dev)
}
/**
- * Wireless USB Host Controlller root hub "fake" ports
+ * Wireless USB Host Controller root hub "fake" ports
* (state and device information)
*
* Wireless USB is wireless, so there are no ports; but we