summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorStephen Rothwell <sfr@canb.auug.org.au>2011-03-01 17:13:24 +1100
committerStephen Rothwell <sfr@canb.auug.org.au>2011-03-01 17:13:24 +1100
commit1e45cefa5c9cbba40b97bc1eae873992ebf114f9 (patch)
tree17509cdab69603c905e853114010b3456a93f7ba /drivers
parente768a07c7cd30cb478a5d5e9d7f46afc5644657b (diff)
parent601e72a067f0230cb815ae0c206081c3ea19c095 (diff)
Merge remote-tracking branch 'usb/usb-next'
Conflicts: drivers/usb/gadget/Kconfig
Diffstat (limited to 'drivers')
-rw-r--r--drivers/usb/atm/ueagle-atm.c19
-rw-r--r--drivers/usb/core/buffer.c26
-rw-r--r--drivers/usb/core/hcd.c30
-rw-r--r--drivers/usb/core/hub.c48
-rw-r--r--drivers/usb/core/usb.h13
-rw-r--r--drivers/usb/early/ehci-dbgp.c2
-rw-r--r--drivers/usb/gadget/Kconfig12
-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/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/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.c16
-rw-r--r--drivers/usb/host/Kconfig15
-rw-r--r--drivers/usb/host/ehci-dbg.c4
-rw-r--r--drivers/usb/host/ehci-hcd.c12
-rw-r--r--drivers/usb/host/ehci-hub.c28
-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-orion.c5
-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.c3
-rw-r--r--drivers/usb/host/imx21-hcd.c5
-rw-r--r--drivers/usb/host/isp1362-hcd.c7
-rw-r--r--drivers/usb/host/isp1760-hcd.c22
-rw-r--r--drivers/usb/host/isp1760-hcd.h1
-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.c25
-rw-r--r--drivers/usb/musb/musb_core.h6
-rw-r--r--drivers/usb/musb/musb_gadget.c199
-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/omap2430.c134
-rw-r--r--drivers/usb/otg/ab8500-usb.c6
-rw-r--r--drivers/usb/otg/msm72k_otg.c31
-rw-r--r--drivers/usb/otg/nop-usb-xceiv.c2
-rw-r--r--drivers/usb/otg/twl4030-usb.c6
-rw-r--r--drivers/usb/otg/twl6030-usb.c56
-rw-r--r--drivers/usb/serial/ftdi_sio.c2
-rw-r--r--drivers/usb/serial/keyspan.c10
-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/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/unusual_realtek.h41
-rw-r--r--drivers/usb/storage/usual-tables.c1
-rw-r--r--drivers/usb/wusbcore/wusbhc.h2
-rw-r--r--drivers/uwb/scan.c2
63 files changed, 4884 insertions, 719 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/hcd.c b/drivers/usb/core/hcd.c
index e935f71d7a34..24765fd6cf12 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -1262,7 +1262,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 +1279,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 +1322,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 +1427,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);
/*-------------------------------------------------------------------------*/
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 19d3435e6140..9b4b365e2648 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -1501,6 +1501,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 +1529,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 +1554,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 +1562,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 +1609,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 +1640,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);
@@ -2073,7 +2081,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 +2644,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 +2749,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 +2781,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 +2863,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 +2928,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;
@@ -3133,15 +3137,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
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 +3229,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))
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 e8cbbdac7a15..5df8a8dcf516 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_OMAP
boolean "OMAP USB Device Controller"
depends on ARCH_OMAP
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index a2f7f9a4f4a8..1ea15ee74fd3 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -27,6 +27,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/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/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..2b025200a69a 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 */
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 0e6afa260ed8..923e5a079b59 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
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 c4de2d75a3d7..b07e3e31795e 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -679,7 +679,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;
}
@@ -1254,6 +1259,11 @@ 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
+
#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..ae0140dd9b9b 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)
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-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-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..30fbdbe1cf1e 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;
diff --git a/drivers/usb/host/imx21-hcd.c b/drivers/usb/host/imx21-hcd.c
index f90d003f2302..b7dfda8a1d51 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;
diff --git a/drivers/usb/host/isp1362-hcd.c b/drivers/usb/host/isp1362-hcd.c
index 43a39eb56cc6..02b742df332a 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--;
diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c
index bdba8c5d844a..c470cc83dbb0 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
@@ -850,6 +851,11 @@ static void enqueue_an_ATL_packet(struct usb_hcd *hcd, struct isp1760_qh *qh,
skip_map &= ~queue_entry;
isp1760_writel(skip_map, hcd->regs + HC_ATL_PTD_SKIPMAP_REG);
+ priv->atl_queued++;
+ if (priv->atl_queued == 2)
+ isp1760_writel(INTERRUPT_ENABLE_SOT_MASK,
+ hcd->regs + HC_INTERRUPT_ENABLE);
+
buffstatus = isp1760_readl(hcd->regs + HC_BUFFER_STATUS_REG);
buffstatus |= ATL_BUFFER;
isp1760_writel(buffstatus, hcd->regs + HC_BUFFER_STATUS_REG);
@@ -992,6 +998,7 @@ static void do_atl_int(struct usb_hcd *usb_hcd)
u32 dw3;
status = 0;
+ priv->atl_queued--;
queue_entry = __ffs(done_map);
done_map &= ~(1 << queue_entry);
@@ -1054,11 +1061,6 @@ 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);
@@ -1086,6 +1088,11 @@ static void do_atl_int(struct usb_hcd *usb_hcd)
priv_write_copy(priv, (u32 *)&ptd, usb_hcd->regs +
atl_regs, sizeof(ptd));
+ priv->atl_queued++;
+ if (priv->atl_queued == 2)
+ isp1760_writel(INTERRUPT_ENABLE_SOT_MASK,
+ usb_hcd->regs + HC_INTERRUPT_ENABLE);
+
buffstatus = isp1760_readl(usb_hcd->regs +
HC_BUFFER_STATUS_REG);
buffstatus |= ATL_BUFFER;
@@ -1191,6 +1198,9 @@ static void do_atl_int(struct usb_hcd *usb_hcd)
skip_map = isp1760_readl(usb_hcd->regs +
HC_ATL_PTD_SKIPMAP_REG);
}
+ if (priv->atl_queued <= 1)
+ isp1760_writel(INTERRUPT_ENABLE_MASK,
+ usb_hcd->regs + HC_INTERRUPT_ENABLE);
}
static void do_intl_int(struct usb_hcd *usb_hcd)
@@ -1770,7 +1780,7 @@ static irqreturn_t isp1760_irq(struct usb_hcd *usb_hcd)
goto leave;
isp1760_writel(imask, usb_hcd->regs + HC_INTERRUPT_REG);
- if (imask & HC_ATL_INT)
+ if (imask & (HC_ATL_INT | HC_SOT_INT))
do_atl_int(usb_hcd);
if (imask & HC_INTL_INT)
diff --git a/drivers/usb/host/isp1760-hcd.h b/drivers/usb/host/isp1760-hcd.h
index 6931ef5c9650..612bce5dce03 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)
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 e550f35eff0f..c0fe53928a1f 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) {
diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h
index 3fb617ef9e72..763256ab1c7f 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);
@@ -360,7 +360,7 @@ struct musb_context_registers {
#if defined(CONFIG_SOC_OMAP2430) || defined(CONFIG_ARCH_OMAP3) || \
defined(CONFIG_ARCH_OMAP4)
- u32 otg_sysconfig, otg_forcestandby;
+ u32 otg_forcestandby;
#endif
u8 power;
u16 intrtxe, intrrxe;
diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c
index 2fe304611dcf..da8c93bfb6fb 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:
@@ -1349,7 +1352,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 +1804,99 @@ 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;
}
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;
}
+ 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 +1951,17 @@ 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;
+
+ /*
+ * REVISIT always use otg_set_peripheral() here too;
* this needs to shut down the OTG engine.
*/
@@ -1956,29 +1971,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 +1998,10 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
*/
}
- return retval;
+ if (!is_otg_enabled(musb))
+ musb_stop(musb);
+
+ 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/omap2430.c b/drivers/usb/musb/omap2430.c
index bc8badd16897..3eb752f79b8b 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);
@@ -309,21 +302,6 @@ static int omap2430_musb_init(struct musb *musb)
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);
-
l = musb_readl(musb->mregs, OTG_INTERFSEL);
if (data->interface_type == MUSB_INTERFACE_UTMI) {
@@ -350,16 +328,56 @@ 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;
}
+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)
{
del_timer_sync(&musb_idle_timer);
@@ -378,6 +396,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,7 +408,7 @@ 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 status = 0;
int ret = -ENOMEM;
@@ -403,26 +424,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,29 +439,32 @@ 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;
+ pm_runtime_enable(&pdev->dev);
+ status = pm_runtime_get_sync(&pdev->dev);
+ if (status < 0) {
+ dev_err(&pdev->dev, "pm_runtime_get_sync FAILED");
+ goto err3;
+ }
-err4:
- clk_disable(clk);
+ return 0;
err3:
- clk_put(clk);
-
+ pm_runtime_disable(&pdev->dev);
err2:
platform_device_put(musb);
@@ -471,8 +481,8 @@ 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;
@@ -481,13 +491,11 @@ static int __exit omap2430_remove(struct platform_device *pdev)
#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);
}
@@ -499,7 +507,10 @@ static int omap2430_suspend(struct device *dev)
omap2430_low_level_exit(musb);
otg_set_suspend(musb->xceiv, 1);
omap2430_save_context(musb);
- clk_disable(glue->clk);
+
+ if (!pm_runtime_suspended(dev) && dev->bus && dev->bus->pm &&
+ dev->bus->pm->runtime_suspend)
+ dev->bus->pm->runtime_suspend(dev);
return 0;
}
@@ -508,13 +519,10 @@ static int omap2430_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;
- }
+ if (!pm_runtime_suspended(dev) && dev->bus && dev->bus->pm &&
+ dev->bus->pm->runtime_resume)
+ dev->bus->pm->runtime_resume(dev);
omap2430_low_level_init(musb);
omap2430_restore_context(musb);
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/msm72k_otg.c b/drivers/usb/otg/msm72k_otg.c
index 1cd52edcd0c2..296598628b85 100644
--- a/drivers/usb/otg/msm72k_otg.c
+++ b/drivers/usb/otg/msm72k_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..2362d8352bc8 100644
--- a/drivers/usb/otg/twl4030-usb.c
+++ b/drivers/usb/otg/twl4030-usb.c
@@ -512,7 +512,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 +534,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 +623,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/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index 997326ea15bd..c0ccb4a5aabe 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -973,7 +973,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/keyspan.c b/drivers/usb/serial/keyspan.c
index c6e968f24e04..a442352d7b61 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/mct_u232.c b/drivers/usb/serial/mct_u232.c
index ef49902c5a51..d2c019637e45 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);
static int mct_u232_tiocmset(struct tty_struct *tty,
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 d8b3e8fa14e9..40abedbc5943 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 8d603a1ca2eb..201f6096844b 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)
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/sierra.c b/drivers/usb/serial/sierra.c
index 2c1e812b189d..d5d136a53b61 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 a72575349744..1c031309ab25 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 aac2ff030332..a65ddd543869 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/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/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
diff --git a/drivers/uwb/scan.c b/drivers/uwb/scan.c
index 76a1a1ed7d3e..367aa12786b9 100644
--- a/drivers/uwb/scan.c
+++ b/drivers/uwb/scan.c
@@ -42,7 +42,7 @@
/**
* Start/stop scanning in a radio controller
*
- * @rc: UWB Radio Controlller
+ * @rc: UWB Radio Controller
* @channel: Channel to scan; encodings in WUSB1.0[Table 5.12]
* @type: Type of scanning to do.
* @bpst_offset: value at which to start scanning (if type ==