Merge branch 'usb-next' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
* 'usb-next' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (232 commits) USB: Add USB-ID for Multiplex RC serial adapter to cp210x.c xhci: Clean up 32-bit build warnings. USB: update documentation for usbmon usb: usb-storage doesn't support dynamic id currently, the patch disables the feature to fix an oops drivers/usb/class/cdc-acm.c: clear dangling pointer drivers/usb/dwc3/dwc3-pci.c: introduce missing kfree drivers/usb/host/isp1760-if.c: introduce missing kfree usb: option: add ZD Incorporated HSPA modem usb: ch9: fix up MaxStreams helper USB: usb-skeleton.c: cleanup open_count USB: usb-skeleton.c: fix open/disconnect race xhci: Properly handle COMP_2ND_BW_ERR USB: remove dead code from suspend/resume path USB: add quirk for another camera drivers: usb: wusbcore: Fix dependency for USB_WUSB xhci: Better debugging for critical host errors. xhci: Be less verbose during URB cancellation. xhci: Remove debugging about ring structure allocation. xhci: Remove debugging about toggling cycle bits. xhci: Remove debugging for individual transfers. ...
This commit is contained in:
Коммит
55b81e6f27
|
@ -119,6 +119,31 @@ Description:
|
|||
Write a 1 to force the device to disconnect
|
||||
(equivalent to unplugging a wired USB device).
|
||||
|
||||
What: /sys/bus/usb/drivers/.../new_id
|
||||
Date: October 2011
|
||||
Contact: linux-usb@vger.kernel.org
|
||||
Description:
|
||||
Writing a device ID to this file will attempt to
|
||||
dynamically add a new device ID to a USB device driver.
|
||||
This may allow the driver to support more hardware than
|
||||
was included in the driver's static device ID support
|
||||
table at compile time. The format for the device ID is:
|
||||
idVendor idProduct bInterfaceClass.
|
||||
The vendor ID and device ID fields are required, the
|
||||
interface class is optional.
|
||||
Upon successfully adding an ID, the driver will probe
|
||||
for the device and attempt to bind to it. For example:
|
||||
# echo "8086 10f5" > /sys/bus/usb/drivers/foo/new_id
|
||||
|
||||
What: /sys/bus/usb-serial/drivers/.../new_id
|
||||
Date: October 2011
|
||||
Contact: linux-usb@vger.kernel.org
|
||||
Description:
|
||||
For serial USB drivers, this attribute appears under the
|
||||
extra bus folder "usb-serial" in sysfs; apart from that
|
||||
difference, all descriptions from the entry
|
||||
"/sys/bus/usb/drivers/.../new_id" apply.
|
||||
|
||||
What: /sys/bus/usb/drivers/.../remove_id
|
||||
Date: November 2009
|
||||
Contact: CHENG Renquan <rqcheng@smu.edu.sg>
|
||||
|
|
|
@ -523,6 +523,20 @@ Why: In 3.0, we can now autodetect internal 3G device and already have
|
|||
information log when acer-wmi initial.
|
||||
Who: Lee, Chun-Yi <jlee@novell.com>
|
||||
|
||||
---------------------------
|
||||
|
||||
What: /sys/devices/platform/_UDC_/udc/_UDC_/is_dualspeed file and
|
||||
is_dualspeed line in /sys/devices/platform/ci13xxx_*/udc/device file.
|
||||
When: 3.8
|
||||
Why: The is_dualspeed file is superseded by maximum_speed in the same
|
||||
directory and is_dualspeed line in device file is superseded by
|
||||
max_speed line in the same file.
|
||||
|
||||
The maximum_speed/max_speed specifies maximum speed supported by UDC.
|
||||
To check if dualspeeed is supported, check if the value is >= 3.
|
||||
Various possible speeds are defined in <linux/usb/ch9.h>.
|
||||
Who: Michal Nazarewicz <mina86@mina86.com>
|
||||
|
||||
----------------------------
|
||||
|
||||
What: The XFS nodelaylog mount option
|
||||
|
|
|
@ -2637,6 +2637,10 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
|
|||
[USB] Start with the old device initialization
|
||||
scheme (default 0 = off).
|
||||
|
||||
usbcore.usbfs_memory_mb=
|
||||
[USB] Memory limit (in MB) for buffers allocated by
|
||||
usbfs (default = 16, 0 = max = 2047).
|
||||
|
||||
usbcore.use_both_schemes=
|
||||
[USB] Try the other device initialization scheme
|
||||
if the first one fails (default 1 = enabled).
|
||||
|
|
|
@ -47,10 +47,11 @@ This allows to filter away annoying devices that talk continuously.
|
|||
|
||||
2. Find which bus connects to the desired device
|
||||
|
||||
Run "cat /proc/bus/usb/devices", and find the T-line which corresponds to
|
||||
the device. Usually you do it by looking for the vendor string. If you have
|
||||
many similar devices, unplug one and compare two /proc/bus/usb/devices outputs.
|
||||
The T-line will have a bus number. Example:
|
||||
Run "cat /sys/kernel/debug/usb/devices", and find the T-line which corresponds
|
||||
to the device. Usually you do it by looking for the vendor string. If you have
|
||||
many similar devices, unplug one and compare the two
|
||||
/sys/kernel/debug/usb/devices outputs. The T-line will have a bus number.
|
||||
Example:
|
||||
|
||||
T: Bus=03 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 2 Spd=12 MxCh= 0
|
||||
D: Ver= 1.10 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 8 #Cfgs= 1
|
||||
|
@ -58,7 +59,10 @@ P: Vendor=0557 ProdID=2004 Rev= 1.00
|
|||
S: Manufacturer=ATEN
|
||||
S: Product=UC100KM V2.00
|
||||
|
||||
Bus=03 means it's bus 3.
|
||||
"Bus=03" means it's bus 3. Alternatively, you can look at the output from
|
||||
"lsusb" and get the bus number from the appropriate line. Example:
|
||||
|
||||
Bus 003 Device 002: ID 0557:2004 ATEN UC100KM V2.00
|
||||
|
||||
3. Start 'cat'
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ static struct device *mmc_device;
|
|||
#define TUSB6010_GPIO_ENABLE 0
|
||||
#define TUSB6010_DMACHAN 0x3f
|
||||
|
||||
#ifdef CONFIG_USB_MUSB_TUSB6010
|
||||
#if defined(CONFIG_USB_MUSB_TUSB6010) || defined(CONFIG_USB_MUSB_TUSB6010_MODULE)
|
||||
/*
|
||||
* Enable or disable power to TUSB6010. When enabling, turn on 3.3 V and
|
||||
* 1.5 V voltage regulators of PM companion chip. Companion chip will then
|
||||
|
|
|
@ -50,6 +50,7 @@
|
|||
#include <plat/nand.h>
|
||||
#include <plat/sdhci.h>
|
||||
#include <plat/udc.h>
|
||||
#include <linux/platform_data/s3c-hsudc.h>
|
||||
|
||||
#include <plat/regs-fb-v4.h>
|
||||
#include <plat/fb.h>
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/platform_data/s3c-hsudc.h>
|
||||
|
||||
#include <asm/irq.h>
|
||||
#include <asm/pmu.h>
|
||||
|
|
|
@ -37,20 +37,7 @@ struct s3c2410_udc_mach_info {
|
|||
|
||||
extern void __init s3c24xx_udc_set_platdata(struct s3c2410_udc_mach_info *);
|
||||
|
||||
/**
|
||||
* s3c24xx_hsudc_platdata - Platform data for USB High-Speed gadget controller.
|
||||
* @epnum: Number of endpoints to be instantiated by the controller driver.
|
||||
* @gpio_init: Platform specific USB related GPIO initialization.
|
||||
* @gpio_uninit: Platform specific USB releted GPIO uninitialzation.
|
||||
*
|
||||
* Representation of platform data for the S3C24XX USB 2.0 High Speed gadget
|
||||
* controllers.
|
||||
*/
|
||||
struct s3c24xx_hsudc_platdata {
|
||||
unsigned int epnum;
|
||||
void (*gpio_init)(void);
|
||||
void (*gpio_uninit)(void);
|
||||
};
|
||||
struct s3c24xx_hsudc_platdata;
|
||||
|
||||
extern void __init s3c24xx_hsudc_set_platdata(struct s3c24xx_hsudc_platdata *pd);
|
||||
|
||||
|
|
|
@ -212,11 +212,13 @@ kvp_respond_to_host(char *key, char *value, int error)
|
|||
* The windows host expects the key/value pair to be encoded
|
||||
* in utf16.
|
||||
*/
|
||||
keylen = utf8s_to_utf16s(key_name, strlen(key_name),
|
||||
(wchar_t *)kvp_data->data.key);
|
||||
keylen = utf8s_to_utf16s(key_name, strlen(key_name), UTF16_HOST_ENDIAN,
|
||||
(wchar_t *) kvp_data->data.key,
|
||||
HV_KVP_EXCHANGE_MAX_KEY_SIZE / 2);
|
||||
kvp_data->data.key_size = 2*(keylen + 1); /* utf16 encoding */
|
||||
valuelen = utf8s_to_utf16s(value, strlen(value),
|
||||
(wchar_t *)kvp_data->data.value);
|
||||
valuelen = utf8s_to_utf16s(value, strlen(value), UTF16_HOST_ENDIAN,
|
||||
(wchar_t *) kvp_data->data.value,
|
||||
HV_KVP_EXCHANGE_MAX_VALUE_SIZE / 2);
|
||||
kvp_data->data.value_size = 2*(valuelen + 1); /* utf16 encoding */
|
||||
|
||||
kvp_data->data.value_type = REG_SZ; /* all our values are strings */
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
obj-$(CONFIG_USB) += core/
|
||||
|
||||
obj-$(CONFIG_USB_OTG_UTILS) += otg/
|
||||
|
||||
obj-$(CONFIG_USB_DWC3) += dwc3/
|
||||
|
||||
obj-$(CONFIG_USB_MON) += mon/
|
||||
|
@ -51,7 +53,6 @@ obj-$(CONFIG_USB_SPEEDTOUCH) += atm/
|
|||
|
||||
obj-$(CONFIG_USB_MUSB_HDRC) += musb/
|
||||
obj-$(CONFIG_USB_RENESAS_USBHS) += renesas_usbhs/
|
||||
obj-$(CONFIG_USB_OTG_UTILS) += otg/
|
||||
obj-$(CONFIG_USB_GADGET) += gadget/
|
||||
|
||||
obj-$(CONFIG_USB_COMMON) += usb-common.o
|
||||
|
|
|
@ -225,21 +225,10 @@ static struct platform_driver c67x00_driver = {
|
|||
.name = "c67x00",
|
||||
},
|
||||
};
|
||||
MODULE_ALIAS("platform:c67x00");
|
||||
|
||||
static int __init c67x00_init(void)
|
||||
{
|
||||
return platform_driver_register(&c67x00_driver);
|
||||
}
|
||||
|
||||
static void __exit c67x00_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&c67x00_driver);
|
||||
}
|
||||
|
||||
module_init(c67x00_init);
|
||||
module_exit(c67x00_exit);
|
||||
module_platform_driver(c67x00_driver);
|
||||
|
||||
MODULE_AUTHOR("Peter Korsgaard, Jan Veldeman, Grant Likely");
|
||||
MODULE_DESCRIPTION("Cypress C67X00 USB Controller Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:c67x00");
|
||||
|
|
|
@ -271,7 +271,6 @@ static void c67x00_hcd_irq(struct c67x00_sie *sie, u16 int_status, u16 msg)
|
|||
if (int_status & SOFEOP_FLG(sie->sie_num)) {
|
||||
c67x00_ll_usb_clear_status(sie, SOF_EOP_IRQ_FLG);
|
||||
c67x00_sched_kick(c67x00);
|
||||
set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -58,12 +58,62 @@ static struct usb_driver acm_driver;
|
|||
static struct tty_driver *acm_tty_driver;
|
||||
static struct acm *acm_table[ACM_TTY_MINORS];
|
||||
|
||||
static DEFINE_MUTEX(open_mutex);
|
||||
static DEFINE_MUTEX(acm_table_lock);
|
||||
|
||||
#define ACM_READY(acm) (acm && acm->dev && acm->port.count)
|
||||
/*
|
||||
* acm_table accessors
|
||||
*/
|
||||
|
||||
static const struct tty_port_operations acm_port_ops = {
|
||||
};
|
||||
/*
|
||||
* Look up an ACM structure by index. If found and not disconnected, increment
|
||||
* its refcount and return it with its mutex held.
|
||||
*/
|
||||
static struct acm *acm_get_by_index(unsigned index)
|
||||
{
|
||||
struct acm *acm;
|
||||
|
||||
mutex_lock(&acm_table_lock);
|
||||
acm = acm_table[index];
|
||||
if (acm) {
|
||||
mutex_lock(&acm->mutex);
|
||||
if (acm->disconnected) {
|
||||
mutex_unlock(&acm->mutex);
|
||||
acm = NULL;
|
||||
} else {
|
||||
tty_port_get(&acm->port);
|
||||
mutex_unlock(&acm->mutex);
|
||||
}
|
||||
}
|
||||
mutex_unlock(&acm_table_lock);
|
||||
return acm;
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to find an available minor number and if found, associate it with 'acm'.
|
||||
*/
|
||||
static int acm_alloc_minor(struct acm *acm)
|
||||
{
|
||||
int minor;
|
||||
|
||||
mutex_lock(&acm_table_lock);
|
||||
for (minor = 0; minor < ACM_TTY_MINORS; minor++) {
|
||||
if (!acm_table[minor]) {
|
||||
acm_table[minor] = acm;
|
||||
break;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&acm_table_lock);
|
||||
|
||||
return minor;
|
||||
}
|
||||
|
||||
/* Release the minor number associated with 'acm'. */
|
||||
static void acm_release_minor(struct acm *acm)
|
||||
{
|
||||
mutex_lock(&acm_table_lock);
|
||||
acm_table[acm->minor] = NULL;
|
||||
mutex_unlock(&acm_table_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Functions for ACM control messages.
|
||||
|
@ -267,9 +317,6 @@ static void acm_ctrl_irq(struct urb *urb)
|
|||
goto exit;
|
||||
}
|
||||
|
||||
if (!ACM_READY(acm))
|
||||
goto exit;
|
||||
|
||||
usb_mark_last_busy(acm->dev);
|
||||
|
||||
data = (unsigned char *)(dr + 1);
|
||||
|
@ -429,8 +476,7 @@ static void acm_write_bulk(struct urb *urb)
|
|||
spin_lock_irqsave(&acm->write_lock, flags);
|
||||
acm_write_done(acm, wb);
|
||||
spin_unlock_irqrestore(&acm->write_lock, flags);
|
||||
if (ACM_READY(acm))
|
||||
schedule_work(&acm->work);
|
||||
schedule_work(&acm->work);
|
||||
}
|
||||
|
||||
static void acm_softint(struct work_struct *work)
|
||||
|
@ -440,8 +486,6 @@ static void acm_softint(struct work_struct *work)
|
|||
|
||||
dev_vdbg(&acm->data->dev, "%s\n", __func__);
|
||||
|
||||
if (!ACM_READY(acm))
|
||||
return;
|
||||
tty = tty_port_tty_get(&acm->port);
|
||||
if (!tty)
|
||||
return;
|
||||
|
@ -453,93 +497,122 @@ static void acm_softint(struct work_struct *work)
|
|||
* TTY handlers
|
||||
*/
|
||||
|
||||
static int acm_tty_open(struct tty_struct *tty, struct file *filp)
|
||||
static int acm_tty_install(struct tty_driver *driver, struct tty_struct *tty)
|
||||
{
|
||||
struct acm *acm;
|
||||
int rv = -ENODEV;
|
||||
int retval;
|
||||
|
||||
mutex_lock(&open_mutex);
|
||||
dev_dbg(tty->dev, "%s\n", __func__);
|
||||
|
||||
acm = acm_table[tty->index];
|
||||
if (!acm || !acm->dev)
|
||||
goto out;
|
||||
else
|
||||
rv = 0;
|
||||
acm = acm_get_by_index(tty->index);
|
||||
if (!acm)
|
||||
return -ENODEV;
|
||||
|
||||
retval = tty_init_termios(tty);
|
||||
if (retval)
|
||||
goto error_init_termios;
|
||||
|
||||
tty->driver_data = acm;
|
||||
|
||||
/* Final install (we use the default method) */
|
||||
tty_driver_kref_get(driver);
|
||||
tty->count++;
|
||||
driver->ttys[tty->index] = tty;
|
||||
|
||||
return 0;
|
||||
|
||||
error_init_termios:
|
||||
tty_port_put(&acm->port);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int acm_tty_open(struct tty_struct *tty, struct file *filp)
|
||||
{
|
||||
struct acm *acm = tty->driver_data;
|
||||
|
||||
dev_dbg(tty->dev, "%s\n", __func__);
|
||||
|
||||
return tty_port_open(&acm->port, tty, filp);
|
||||
}
|
||||
|
||||
static int acm_port_activate(struct tty_port *port, struct tty_struct *tty)
|
||||
{
|
||||
struct acm *acm = container_of(port, struct acm, port);
|
||||
int retval = -ENODEV;
|
||||
|
||||
dev_dbg(&acm->control->dev, "%s\n", __func__);
|
||||
|
||||
set_bit(TTY_NO_WRITE_SPLIT, &tty->flags);
|
||||
|
||||
tty->driver_data = acm;
|
||||
tty_port_tty_set(&acm->port, tty);
|
||||
|
||||
if (usb_autopm_get_interface(acm->control) < 0)
|
||||
goto early_bail;
|
||||
else
|
||||
acm->control->needs_remote_wakeup = 1;
|
||||
|
||||
mutex_lock(&acm->mutex);
|
||||
if (acm->port.count++) {
|
||||
mutex_unlock(&acm->mutex);
|
||||
usb_autopm_put_interface(acm->control);
|
||||
goto out;
|
||||
}
|
||||
if (acm->disconnected)
|
||||
goto disconnected;
|
||||
|
||||
retval = usb_autopm_get_interface(acm->control);
|
||||
if (retval)
|
||||
goto error_get_interface;
|
||||
|
||||
/*
|
||||
* FIXME: Why do we need this? Allocating 64K of physically contiguous
|
||||
* memory is really nasty...
|
||||
*/
|
||||
set_bit(TTY_NO_WRITE_SPLIT, &tty->flags);
|
||||
acm->control->needs_remote_wakeup = 1;
|
||||
|
||||
acm->ctrlurb->dev = acm->dev;
|
||||
if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) {
|
||||
dev_err(&acm->control->dev,
|
||||
"%s - usb_submit_urb(ctrl irq) failed\n", __func__);
|
||||
goto bail_out;
|
||||
goto error_submit_urb;
|
||||
}
|
||||
|
||||
if (0 > acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS) &&
|
||||
acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS;
|
||||
if (acm_set_control(acm, acm->ctrlout) < 0 &&
|
||||
(acm->ctrl_caps & USB_CDC_CAP_LINE))
|
||||
goto bail_out;
|
||||
goto error_set_control;
|
||||
|
||||
usb_autopm_put_interface(acm->control);
|
||||
|
||||
if (acm_submit_read_urbs(acm, GFP_KERNEL))
|
||||
goto bail_out;
|
||||
|
||||
set_bit(ASYNCB_INITIALIZED, &acm->port.flags);
|
||||
rv = tty_port_block_til_ready(&acm->port, tty, filp);
|
||||
goto error_submit_read_urbs;
|
||||
|
||||
mutex_unlock(&acm->mutex);
|
||||
out:
|
||||
mutex_unlock(&open_mutex);
|
||||
return rv;
|
||||
|
||||
bail_out:
|
||||
acm->port.count--;
|
||||
mutex_unlock(&acm->mutex);
|
||||
return 0;
|
||||
|
||||
error_submit_read_urbs:
|
||||
acm->ctrlout = 0;
|
||||
acm_set_control(acm, acm->ctrlout);
|
||||
error_set_control:
|
||||
usb_kill_urb(acm->ctrlurb);
|
||||
error_submit_urb:
|
||||
usb_autopm_put_interface(acm->control);
|
||||
early_bail:
|
||||
mutex_unlock(&open_mutex);
|
||||
tty_port_tty_set(&acm->port, NULL);
|
||||
return -EIO;
|
||||
error_get_interface:
|
||||
disconnected:
|
||||
mutex_unlock(&acm->mutex);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void acm_tty_unregister(struct acm *acm)
|
||||
static void acm_port_destruct(struct tty_port *port)
|
||||
{
|
||||
int i;
|
||||
struct acm *acm = container_of(port, struct acm, port);
|
||||
|
||||
dev_dbg(&acm->control->dev, "%s\n", __func__);
|
||||
|
||||
tty_unregister_device(acm_tty_driver, acm->minor);
|
||||
acm_release_minor(acm);
|
||||
usb_put_intf(acm->control);
|
||||
acm_table[acm->minor] = NULL;
|
||||
usb_free_urb(acm->ctrlurb);
|
||||
for (i = 0; i < ACM_NW; i++)
|
||||
usb_free_urb(acm->wb[i].urb);
|
||||
for (i = 0; i < acm->rx_buflimit; i++)
|
||||
usb_free_urb(acm->read_urbs[i]);
|
||||
kfree(acm->country_codes);
|
||||
kfree(acm);
|
||||
}
|
||||
|
||||
static void acm_port_down(struct acm *acm)
|
||||
static void acm_port_shutdown(struct tty_port *port)
|
||||
{
|
||||
struct acm *acm = container_of(port, struct acm, port);
|
||||
int i;
|
||||
|
||||
if (acm->dev) {
|
||||
dev_dbg(&acm->control->dev, "%s\n", __func__);
|
||||
|
||||
mutex_lock(&acm->mutex);
|
||||
if (!acm->disconnected) {
|
||||
usb_autopm_get_interface(acm->control);
|
||||
acm_set_control(acm, acm->ctrlout = 0);
|
||||
usb_kill_urb(acm->ctrlurb);
|
||||
|
@ -550,40 +623,28 @@ static void acm_port_down(struct acm *acm)
|
|||
acm->control->needs_remote_wakeup = 0;
|
||||
usb_autopm_put_interface(acm->control);
|
||||
}
|
||||
mutex_unlock(&acm->mutex);
|
||||
}
|
||||
|
||||
static void acm_tty_cleanup(struct tty_struct *tty)
|
||||
{
|
||||
struct acm *acm = tty->driver_data;
|
||||
dev_dbg(&acm->control->dev, "%s\n", __func__);
|
||||
tty_port_put(&acm->port);
|
||||
}
|
||||
|
||||
static void acm_tty_hangup(struct tty_struct *tty)
|
||||
{
|
||||
struct acm *acm = tty->driver_data;
|
||||
dev_dbg(&acm->control->dev, "%s\n", __func__);
|
||||
tty_port_hangup(&acm->port);
|
||||
mutex_lock(&open_mutex);
|
||||
acm_port_down(acm);
|
||||
mutex_unlock(&open_mutex);
|
||||
}
|
||||
|
||||
static void acm_tty_close(struct tty_struct *tty, struct file *filp)
|
||||
{
|
||||
struct acm *acm = tty->driver_data;
|
||||
|
||||
/* Perform the closing process and see if we need to do the hardware
|
||||
shutdown */
|
||||
if (!acm)
|
||||
return;
|
||||
|
||||
mutex_lock(&open_mutex);
|
||||
if (tty_port_close_start(&acm->port, tty, filp) == 0) {
|
||||
if (!acm->dev) {
|
||||
tty_port_tty_set(&acm->port, NULL);
|
||||
acm_tty_unregister(acm);
|
||||
tty->driver_data = NULL;
|
||||
}
|
||||
mutex_unlock(&open_mutex);
|
||||
return;
|
||||
}
|
||||
acm_port_down(acm);
|
||||
tty_port_close_end(&acm->port, tty);
|
||||
tty_port_tty_set(&acm->port, NULL);
|
||||
mutex_unlock(&open_mutex);
|
||||
dev_dbg(&acm->control->dev, "%s\n", __func__);
|
||||
tty_port_close(&acm->port, tty, filp);
|
||||
}
|
||||
|
||||
static int acm_tty_write(struct tty_struct *tty,
|
||||
|
@ -595,8 +656,6 @@ static int acm_tty_write(struct tty_struct *tty,
|
|||
int wbn;
|
||||
struct acm_wb *wb;
|
||||
|
||||
if (!ACM_READY(acm))
|
||||
return -EINVAL;
|
||||
if (!count)
|
||||
return 0;
|
||||
|
||||
|
@ -625,8 +684,6 @@ static int acm_tty_write(struct tty_struct *tty,
|
|||
static int acm_tty_write_room(struct tty_struct *tty)
|
||||
{
|
||||
struct acm *acm = tty->driver_data;
|
||||
if (!ACM_READY(acm))
|
||||
return -EINVAL;
|
||||
/*
|
||||
* Do not let the line discipline to know that we have a reserve,
|
||||
* or it might get too enthusiastic.
|
||||
|
@ -637,7 +694,11 @@ static int acm_tty_write_room(struct tty_struct *tty)
|
|||
static int acm_tty_chars_in_buffer(struct tty_struct *tty)
|
||||
{
|
||||
struct acm *acm = tty->driver_data;
|
||||
if (!ACM_READY(acm))
|
||||
/*
|
||||
* if the device was unplugged then any remaining characters fell out
|
||||
* of the connector ;)
|
||||
*/
|
||||
if (acm->disconnected)
|
||||
return 0;
|
||||
/*
|
||||
* This is inaccurate (overcounts), but it works.
|
||||
|
@ -649,9 +710,6 @@ static void acm_tty_throttle(struct tty_struct *tty)
|
|||
{
|
||||
struct acm *acm = tty->driver_data;
|
||||
|
||||
if (!ACM_READY(acm))
|
||||
return;
|
||||
|
||||
spin_lock_irq(&acm->read_lock);
|
||||
acm->throttle_req = 1;
|
||||
spin_unlock_irq(&acm->read_lock);
|
||||
|
@ -662,9 +720,6 @@ static void acm_tty_unthrottle(struct tty_struct *tty)
|
|||
struct acm *acm = tty->driver_data;
|
||||
unsigned int was_throttled;
|
||||
|
||||
if (!ACM_READY(acm))
|
||||
return;
|
||||
|
||||
spin_lock_irq(&acm->read_lock);
|
||||
was_throttled = acm->throttled;
|
||||
acm->throttled = 0;
|
||||
|
@ -679,8 +734,7 @@ static int acm_tty_break_ctl(struct tty_struct *tty, int state)
|
|||
{
|
||||
struct acm *acm = tty->driver_data;
|
||||
int retval;
|
||||
if (!ACM_READY(acm))
|
||||
return -EINVAL;
|
||||
|
||||
retval = acm_send_break(acm, state ? 0xffff : 0);
|
||||
if (retval < 0)
|
||||
dev_dbg(&acm->control->dev, "%s - send break failed\n",
|
||||
|
@ -692,9 +746,6 @@ static int acm_tty_tiocmget(struct tty_struct *tty)
|
|||
{
|
||||
struct acm *acm = tty->driver_data;
|
||||
|
||||
if (!ACM_READY(acm))
|
||||
return -EINVAL;
|
||||
|
||||
return (acm->ctrlout & ACM_CTRL_DTR ? TIOCM_DTR : 0) |
|
||||
(acm->ctrlout & ACM_CTRL_RTS ? TIOCM_RTS : 0) |
|
||||
(acm->ctrlin & ACM_CTRL_DSR ? TIOCM_DSR : 0) |
|
||||
|
@ -709,9 +760,6 @@ static int acm_tty_tiocmset(struct tty_struct *tty,
|
|||
struct acm *acm = tty->driver_data;
|
||||
unsigned int newctrl;
|
||||
|
||||
if (!ACM_READY(acm))
|
||||
return -EINVAL;
|
||||
|
||||
newctrl = acm->ctrlout;
|
||||
set = (set & TIOCM_DTR ? ACM_CTRL_DTR : 0) |
|
||||
(set & TIOCM_RTS ? ACM_CTRL_RTS : 0);
|
||||
|
@ -728,11 +776,6 @@ static int acm_tty_tiocmset(struct tty_struct *tty,
|
|||
static int acm_tty_ioctl(struct tty_struct *tty,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct acm *acm = tty->driver_data;
|
||||
|
||||
if (!ACM_READY(acm))
|
||||
return -EINVAL;
|
||||
|
||||
return -ENOIOCTLCMD;
|
||||
}
|
||||
|
||||
|
@ -756,9 +799,6 @@ static void acm_tty_set_termios(struct tty_struct *tty,
|
|||
struct usb_cdc_line_coding newline;
|
||||
int newctrl = acm->ctrlout;
|
||||
|
||||
if (!ACM_READY(acm))
|
||||
return;
|
||||
|
||||
newline.dwDTERate = cpu_to_le32(tty_get_baud_rate(tty));
|
||||
newline.bCharFormat = termios->c_cflag & CSTOPB ? 2 : 0;
|
||||
newline.bParityType = termios->c_cflag & PARENB ?
|
||||
|
@ -788,6 +828,12 @@ static void acm_tty_set_termios(struct tty_struct *tty,
|
|||
}
|
||||
}
|
||||
|
||||
static const struct tty_port_operations acm_port_ops = {
|
||||
.shutdown = acm_port_shutdown,
|
||||
.activate = acm_port_activate,
|
||||
.destruct = acm_port_destruct,
|
||||
};
|
||||
|
||||
/*
|
||||
* USB probe and disconnect routines.
|
||||
*/
|
||||
|
@ -1047,12 +1093,6 @@ skip_normal_probe:
|
|||
}
|
||||
made_compressed_probe:
|
||||
dev_dbg(&intf->dev, "interfaces are valid\n");
|
||||
for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++);
|
||||
|
||||
if (minor == ACM_TTY_MINORS) {
|
||||
dev_err(&intf->dev, "no more free acm devices\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
acm = kzalloc(sizeof(struct acm), GFP_KERNEL);
|
||||
if (acm == NULL) {
|
||||
|
@ -1060,6 +1100,13 @@ made_compressed_probe:
|
|||
goto alloc_fail;
|
||||
}
|
||||
|
||||
minor = acm_alloc_minor(acm);
|
||||
if (minor == ACM_TTY_MINORS) {
|
||||
dev_err(&intf->dev, "no more free acm devices\n");
|
||||
kfree(acm);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ctrlsize = usb_endpoint_maxp(epctrl);
|
||||
readsize = usb_endpoint_maxp(epread) *
|
||||
(quirks == SINGLE_RX_URB ? 1 : 2);
|
||||
|
@ -1183,6 +1230,8 @@ made_compressed_probe:
|
|||
i = device_create_file(&intf->dev, &dev_attr_wCountryCodes);
|
||||
if (i < 0) {
|
||||
kfree(acm->country_codes);
|
||||
acm->country_codes = NULL;
|
||||
acm->country_code_size = 0;
|
||||
goto skip_countries;
|
||||
}
|
||||
|
||||
|
@ -1191,6 +1240,8 @@ made_compressed_probe:
|
|||
if (i < 0) {
|
||||
device_remove_file(&intf->dev, &dev_attr_wCountryCodes);
|
||||
kfree(acm->country_codes);
|
||||
acm->country_codes = NULL;
|
||||
acm->country_code_size = 0;
|
||||
goto skip_countries;
|
||||
}
|
||||
}
|
||||
|
@ -1218,8 +1269,6 @@ skip_countries:
|
|||
usb_get_intf(control_interface);
|
||||
tty_register_device(acm_tty_driver, minor, &control_interface->dev);
|
||||
|
||||
acm_table[minor] = acm;
|
||||
|
||||
return 0;
|
||||
alloc_fail7:
|
||||
for (i = 0; i < ACM_NW; i++)
|
||||
|
@ -1234,6 +1283,7 @@ alloc_fail5:
|
|||
alloc_fail4:
|
||||
usb_free_coherent(usb_dev, ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
|
||||
alloc_fail2:
|
||||
acm_release_minor(acm);
|
||||
kfree(acm);
|
||||
alloc_fail:
|
||||
return -ENOMEM;
|
||||
|
@ -1259,12 +1309,16 @@ static void acm_disconnect(struct usb_interface *intf)
|
|||
struct acm *acm = usb_get_intfdata(intf);
|
||||
struct usb_device *usb_dev = interface_to_usbdev(intf);
|
||||
struct tty_struct *tty;
|
||||
int i;
|
||||
|
||||
dev_dbg(&intf->dev, "%s\n", __func__);
|
||||
|
||||
/* sibling interface is already cleaning up */
|
||||
if (!acm)
|
||||
return;
|
||||
|
||||
mutex_lock(&open_mutex);
|
||||
mutex_lock(&acm->mutex);
|
||||
acm->disconnected = true;
|
||||
if (acm->country_codes) {
|
||||
device_remove_file(&acm->control->dev,
|
||||
&dev_attr_wCountryCodes);
|
||||
|
@ -1272,33 +1326,32 @@ static void acm_disconnect(struct usb_interface *intf)
|
|||
&dev_attr_iCountryCodeRelDate);
|
||||
}
|
||||
device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities);
|
||||
acm->dev = NULL;
|
||||
usb_set_intfdata(acm->control, NULL);
|
||||
usb_set_intfdata(acm->data, NULL);
|
||||
mutex_unlock(&acm->mutex);
|
||||
|
||||
tty = tty_port_tty_get(&acm->port);
|
||||
if (tty) {
|
||||
tty_vhangup(tty);
|
||||
tty_kref_put(tty);
|
||||
}
|
||||
|
||||
stop_data_traffic(acm);
|
||||
|
||||
usb_free_urb(acm->ctrlurb);
|
||||
for (i = 0; i < ACM_NW; i++)
|
||||
usb_free_urb(acm->wb[i].urb);
|
||||
for (i = 0; i < acm->rx_buflimit; i++)
|
||||
usb_free_urb(acm->read_urbs[i]);
|
||||
acm_write_buffers_free(acm);
|
||||
usb_free_coherent(usb_dev, acm->ctrlsize, acm->ctrl_buffer,
|
||||
acm->ctrl_dma);
|
||||
usb_free_coherent(usb_dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
|
||||
acm_read_buffers_free(acm);
|
||||
|
||||
if (!acm->combined_interfaces)
|
||||
usb_driver_release_interface(&acm_driver, intf == acm->control ?
|
||||
acm->data : acm->control);
|
||||
|
||||
if (acm->port.count == 0) {
|
||||
acm_tty_unregister(acm);
|
||||
mutex_unlock(&open_mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
mutex_unlock(&open_mutex);
|
||||
tty = tty_port_tty_get(&acm->port);
|
||||
if (tty) {
|
||||
tty_hangup(tty);
|
||||
tty_kref_put(tty);
|
||||
}
|
||||
tty_port_put(&acm->port);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
@ -1325,16 +1378,10 @@ static int acm_suspend(struct usb_interface *intf, pm_message_t message)
|
|||
|
||||
if (cnt)
|
||||
return 0;
|
||||
/*
|
||||
we treat opened interfaces differently,
|
||||
we must guard against open
|
||||
*/
|
||||
mutex_lock(&acm->mutex);
|
||||
|
||||
if (acm->port.count)
|
||||
if (test_bit(ASYNCB_INITIALIZED, &acm->port.flags))
|
||||
stop_data_traffic(acm);
|
||||
|
||||
mutex_unlock(&acm->mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1353,8 +1400,7 @@ static int acm_resume(struct usb_interface *intf)
|
|||
if (cnt)
|
||||
return 0;
|
||||
|
||||
mutex_lock(&acm->mutex);
|
||||
if (acm->port.count) {
|
||||
if (test_bit(ASYNCB_INITIALIZED, &acm->port.flags)) {
|
||||
rv = usb_submit_urb(acm->ctrlurb, GFP_NOIO);
|
||||
|
||||
spin_lock_irq(&acm->write_lock);
|
||||
|
@ -1378,7 +1424,6 @@ static int acm_resume(struct usb_interface *intf)
|
|||
}
|
||||
|
||||
err_out:
|
||||
mutex_unlock(&acm->mutex);
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
@ -1387,15 +1432,14 @@ static int acm_reset_resume(struct usb_interface *intf)
|
|||
struct acm *acm = usb_get_intfdata(intf);
|
||||
struct tty_struct *tty;
|
||||
|
||||
mutex_lock(&acm->mutex);
|
||||
if (acm->port.count) {
|
||||
if (test_bit(ASYNCB_INITIALIZED, &acm->port.flags)) {
|
||||
tty = tty_port_tty_get(&acm->port);
|
||||
if (tty) {
|
||||
tty_hangup(tty);
|
||||
tty_kref_put(tty);
|
||||
}
|
||||
}
|
||||
mutex_unlock(&acm->mutex);
|
||||
|
||||
return acm_resume(intf);
|
||||
}
|
||||
|
||||
|
@ -1604,8 +1648,10 @@ static struct usb_driver acm_driver = {
|
|||
*/
|
||||
|
||||
static const struct tty_operations acm_ops = {
|
||||
.install = acm_tty_install,
|
||||
.open = acm_tty_open,
|
||||
.close = acm_tty_close,
|
||||
.cleanup = acm_tty_cleanup,
|
||||
.hangup = acm_tty_hangup,
|
||||
.write = acm_tty_write,
|
||||
.write_room = acm_tty_write_room,
|
||||
|
|
|
@ -101,6 +101,7 @@ struct acm {
|
|||
int transmitting;
|
||||
spinlock_t write_lock;
|
||||
struct mutex mutex;
|
||||
bool disconnected;
|
||||
struct usb_cdc_line_coding line; /* bits, stop, parity */
|
||||
struct work_struct work; /* work queue entry for line discipline waking up */
|
||||
unsigned int ctrlin; /* input control lines (DCD, DSR, RI, break, overruns) */
|
||||
|
|
|
@ -86,6 +86,7 @@ struct async {
|
|||
void __user *userbuffer;
|
||||
void __user *userurb;
|
||||
struct urb *urb;
|
||||
unsigned int mem_usage;
|
||||
int status;
|
||||
u32 secid;
|
||||
u8 bulk_addr;
|
||||
|
@ -108,8 +109,44 @@ enum snoop_when {
|
|||
|
||||
#define USB_DEVICE_DEV MKDEV(USB_DEVICE_MAJOR, 0)
|
||||
|
||||
#define MAX_USBFS_BUFFER_SIZE 16384
|
||||
/* Limit on the total amount of memory we can allocate for transfers */
|
||||
static unsigned usbfs_memory_mb = 16;
|
||||
module_param(usbfs_memory_mb, uint, 0644);
|
||||
MODULE_PARM_DESC(usbfs_memory_mb,
|
||||
"maximum MB allowed for usbfs buffers (0 = no limit)");
|
||||
|
||||
/* Hard limit, necessary to avoid aithmetic overflow */
|
||||
#define USBFS_XFER_MAX (UINT_MAX / 2 - 1000000)
|
||||
|
||||
static atomic_t usbfs_memory_usage; /* Total memory currently allocated */
|
||||
|
||||
/* Check whether it's okay to allocate more memory for a transfer */
|
||||
static int usbfs_increase_memory_usage(unsigned amount)
|
||||
{
|
||||
unsigned lim;
|
||||
|
||||
/*
|
||||
* Convert usbfs_memory_mb to bytes, avoiding overflows.
|
||||
* 0 means use the hard limit (effectively unlimited).
|
||||
*/
|
||||
lim = ACCESS_ONCE(usbfs_memory_mb);
|
||||
if (lim == 0 || lim > (USBFS_XFER_MAX >> 20))
|
||||
lim = USBFS_XFER_MAX;
|
||||
else
|
||||
lim <<= 20;
|
||||
|
||||
atomic_add(amount, &usbfs_memory_usage);
|
||||
if (atomic_read(&usbfs_memory_usage) <= lim)
|
||||
return 0;
|
||||
atomic_sub(amount, &usbfs_memory_usage);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Memory for a transfer is being deallocated */
|
||||
static void usbfs_decrease_memory_usage(unsigned amount)
|
||||
{
|
||||
atomic_sub(amount, &usbfs_memory_usage);
|
||||
}
|
||||
|
||||
static int connected(struct dev_state *ps)
|
||||
{
|
||||
|
@ -249,10 +286,12 @@ static struct async *alloc_async(unsigned int numisoframes)
|
|||
static void free_async(struct async *as)
|
||||
{
|
||||
put_pid(as->pid);
|
||||
put_cred(as->cred);
|
||||
if (as->cred)
|
||||
put_cred(as->cred);
|
||||
kfree(as->urb->transfer_buffer);
|
||||
kfree(as->urb->setup_packet);
|
||||
usb_free_urb(as->urb);
|
||||
usbfs_decrease_memory_usage(as->mem_usage);
|
||||
kfree(as);
|
||||
}
|
||||
|
||||
|
@ -792,9 +831,15 @@ static int proc_control(struct dev_state *ps, void __user *arg)
|
|||
wLength = ctrl.wLength; /* To suppress 64k PAGE_SIZE warning */
|
||||
if (wLength > PAGE_SIZE)
|
||||
return -EINVAL;
|
||||
ret = usbfs_increase_memory_usage(PAGE_SIZE + sizeof(struct urb) +
|
||||
sizeof(struct usb_ctrlrequest));
|
||||
if (ret)
|
||||
return ret;
|
||||
tbuf = (unsigned char *)__get_free_page(GFP_KERNEL);
|
||||
if (!tbuf)
|
||||
return -ENOMEM;
|
||||
if (!tbuf) {
|
||||
ret = -ENOMEM;
|
||||
goto done;
|
||||
}
|
||||
tmo = ctrl.timeout;
|
||||
snoop(&dev->dev, "control urb: bRequestType=%02x "
|
||||
"bRequest=%02x wValue=%04x "
|
||||
|
@ -806,8 +851,8 @@ static int proc_control(struct dev_state *ps, void __user *arg)
|
|||
if (ctrl.bRequestType & 0x80) {
|
||||
if (ctrl.wLength && !access_ok(VERIFY_WRITE, ctrl.data,
|
||||
ctrl.wLength)) {
|
||||
free_page((unsigned long)tbuf);
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
pipe = usb_rcvctrlpipe(dev, 0);
|
||||
snoop_urb(dev, NULL, pipe, ctrl.wLength, tmo, SUBMIT, NULL, 0);
|
||||
|
@ -821,15 +866,15 @@ static int proc_control(struct dev_state *ps, void __user *arg)
|
|||
tbuf, max(i, 0));
|
||||
if ((i > 0) && ctrl.wLength) {
|
||||
if (copy_to_user(ctrl.data, tbuf, i)) {
|
||||
free_page((unsigned long)tbuf);
|
||||
return -EFAULT;
|
||||
ret = -EFAULT;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (ctrl.wLength) {
|
||||
if (copy_from_user(tbuf, ctrl.data, ctrl.wLength)) {
|
||||
free_page((unsigned long)tbuf);
|
||||
return -EFAULT;
|
||||
ret = -EFAULT;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
pipe = usb_sndctrlpipe(dev, 0);
|
||||
|
@ -843,14 +888,18 @@ static int proc_control(struct dev_state *ps, void __user *arg)
|
|||
usb_lock_device(dev);
|
||||
snoop_urb(dev, NULL, pipe, max(i, 0), min(i, 0), COMPLETE, NULL, 0);
|
||||
}
|
||||
free_page((unsigned long)tbuf);
|
||||
if (i < 0 && i != -EPIPE) {
|
||||
dev_printk(KERN_DEBUG, &dev->dev, "usbfs: USBDEVFS_CONTROL "
|
||||
"failed cmd %s rqt %u rq %u len %u ret %d\n",
|
||||
current->comm, ctrl.bRequestType, ctrl.bRequest,
|
||||
ctrl.wLength, i);
|
||||
}
|
||||
return i;
|
||||
ret = i;
|
||||
done:
|
||||
free_page((unsigned long) tbuf);
|
||||
usbfs_decrease_memory_usage(PAGE_SIZE + sizeof(struct urb) +
|
||||
sizeof(struct usb_ctrlrequest));
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int proc_bulk(struct dev_state *ps, void __user *arg)
|
||||
|
@ -877,15 +926,20 @@ static int proc_bulk(struct dev_state *ps, void __user *arg)
|
|||
if (!usb_maxpacket(dev, pipe, !(bulk.ep & USB_DIR_IN)))
|
||||
return -EINVAL;
|
||||
len1 = bulk.len;
|
||||
if (len1 > MAX_USBFS_BUFFER_SIZE)
|
||||
if (len1 >= USBFS_XFER_MAX)
|
||||
return -EINVAL;
|
||||
if (!(tbuf = kmalloc(len1, GFP_KERNEL)))
|
||||
return -ENOMEM;
|
||||
ret = usbfs_increase_memory_usage(len1 + sizeof(struct urb));
|
||||
if (ret)
|
||||
return ret;
|
||||
if (!(tbuf = kmalloc(len1, GFP_KERNEL))) {
|
||||
ret = -ENOMEM;
|
||||
goto done;
|
||||
}
|
||||
tmo = bulk.timeout;
|
||||
if (bulk.ep & 0x80) {
|
||||
if (len1 && !access_ok(VERIFY_WRITE, bulk.data, len1)) {
|
||||
kfree(tbuf);
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
snoop_urb(dev, NULL, pipe, len1, tmo, SUBMIT, NULL, 0);
|
||||
|
||||
|
@ -896,15 +950,15 @@ static int proc_bulk(struct dev_state *ps, void __user *arg)
|
|||
|
||||
if (!i && len2) {
|
||||
if (copy_to_user(bulk.data, tbuf, len2)) {
|
||||
kfree(tbuf);
|
||||
return -EFAULT;
|
||||
ret = -EFAULT;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (len1) {
|
||||
if (copy_from_user(tbuf, bulk.data, len1)) {
|
||||
kfree(tbuf);
|
||||
return -EFAULT;
|
||||
ret = -EFAULT;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
snoop_urb(dev, NULL, pipe, len1, tmo, SUBMIT, tbuf, len1);
|
||||
|
@ -914,10 +968,11 @@ static int proc_bulk(struct dev_state *ps, void __user *arg)
|
|||
usb_lock_device(dev);
|
||||
snoop_urb(dev, NULL, pipe, len2, i, COMPLETE, NULL, 0);
|
||||
}
|
||||
ret = (i < 0 ? i : len2);
|
||||
done:
|
||||
kfree(tbuf);
|
||||
if (i < 0)
|
||||
return i;
|
||||
return len2;
|
||||
usbfs_decrease_memory_usage(len1 + sizeof(struct urb));
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int proc_resetep(struct dev_state *ps, void __user *arg)
|
||||
|
@ -1062,7 +1117,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
|
|||
{
|
||||
struct usbdevfs_iso_packet_desc *isopkt = NULL;
|
||||
struct usb_host_endpoint *ep;
|
||||
struct async *as;
|
||||
struct async *as = NULL;
|
||||
struct usb_ctrlrequest *dr = NULL;
|
||||
unsigned int u, totlen, isofrmlen;
|
||||
int ret, ifnum = -1;
|
||||
|
@ -1095,32 +1150,30 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
|
|||
}
|
||||
if (!ep)
|
||||
return -ENOENT;
|
||||
|
||||
u = 0;
|
||||
switch(uurb->type) {
|
||||
case USBDEVFS_URB_TYPE_CONTROL:
|
||||
if (!usb_endpoint_xfer_control(&ep->desc))
|
||||
return -EINVAL;
|
||||
/* min 8 byte setup packet,
|
||||
* max 8 byte setup plus an arbitrary data stage */
|
||||
if (uurb->buffer_length < 8 ||
|
||||
uurb->buffer_length > (8 + MAX_USBFS_BUFFER_SIZE))
|
||||
/* min 8 byte setup packet */
|
||||
if (uurb->buffer_length < 8)
|
||||
return -EINVAL;
|
||||
dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
|
||||
if (!dr)
|
||||
return -ENOMEM;
|
||||
if (copy_from_user(dr, uurb->buffer, 8)) {
|
||||
kfree(dr);
|
||||
return -EFAULT;
|
||||
ret = -EFAULT;
|
||||
goto error;
|
||||
}
|
||||
if (uurb->buffer_length < (le16_to_cpup(&dr->wLength) + 8)) {
|
||||
kfree(dr);
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
ret = check_ctrlrecip(ps, dr->bRequestType, dr->bRequest,
|
||||
le16_to_cpup(&dr->wIndex));
|
||||
if (ret) {
|
||||
kfree(dr);
|
||||
return ret;
|
||||
}
|
||||
if (ret)
|
||||
goto error;
|
||||
uurb->number_of_packets = 0;
|
||||
uurb->buffer_length = le16_to_cpup(&dr->wLength);
|
||||
uurb->buffer += 8;
|
||||
|
@ -1138,6 +1191,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
|
|||
__le16_to_cpup(&dr->wValue),
|
||||
__le16_to_cpup(&dr->wIndex),
|
||||
__le16_to_cpup(&dr->wLength));
|
||||
u = sizeof(struct usb_ctrlrequest);
|
||||
break;
|
||||
|
||||
case USBDEVFS_URB_TYPE_BULK:
|
||||
|
@ -1151,8 +1205,6 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
|
|||
goto interrupt_urb;
|
||||
}
|
||||
uurb->number_of_packets = 0;
|
||||
if (uurb->buffer_length > MAX_USBFS_BUFFER_SIZE)
|
||||
return -EINVAL;
|
||||
break;
|
||||
|
||||
case USBDEVFS_URB_TYPE_INTERRUPT:
|
||||
|
@ -1160,8 +1212,6 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
|
|||
return -EINVAL;
|
||||
interrupt_urb:
|
||||
uurb->number_of_packets = 0;
|
||||
if (uurb->buffer_length > MAX_USBFS_BUFFER_SIZE)
|
||||
return -EINVAL;
|
||||
break;
|
||||
|
||||
case USBDEVFS_URB_TYPE_ISO:
|
||||
|
@ -1176,50 +1226,53 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
|
|||
if (!(isopkt = kmalloc(isofrmlen, GFP_KERNEL)))
|
||||
return -ENOMEM;
|
||||
if (copy_from_user(isopkt, iso_frame_desc, isofrmlen)) {
|
||||
kfree(isopkt);
|
||||
return -EFAULT;
|
||||
ret = -EFAULT;
|
||||
goto error;
|
||||
}
|
||||
for (totlen = u = 0; u < uurb->number_of_packets; u++) {
|
||||
/* arbitrary limit,
|
||||
* sufficient for USB 2.0 high-bandwidth iso */
|
||||
if (isopkt[u].length > 8192) {
|
||||
kfree(isopkt);
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
totlen += isopkt[u].length;
|
||||
}
|
||||
/* 3072 * 64 microframes */
|
||||
if (totlen > 196608) {
|
||||
kfree(isopkt);
|
||||
return -EINVAL;
|
||||
}
|
||||
u *= sizeof(struct usb_iso_packet_descriptor);
|
||||
uurb->buffer_length = totlen;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (uurb->buffer_length >= USBFS_XFER_MAX) {
|
||||
ret = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
if (uurb->buffer_length > 0 &&
|
||||
!access_ok(is_in ? VERIFY_WRITE : VERIFY_READ,
|
||||
uurb->buffer, uurb->buffer_length)) {
|
||||
kfree(isopkt);
|
||||
kfree(dr);
|
||||
return -EFAULT;
|
||||
ret = -EFAULT;
|
||||
goto error;
|
||||
}
|
||||
as = alloc_async(uurb->number_of_packets);
|
||||
if (!as) {
|
||||
kfree(isopkt);
|
||||
kfree(dr);
|
||||
return -ENOMEM;
|
||||
ret = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
u += sizeof(struct async) + sizeof(struct urb) + uurb->buffer_length;
|
||||
ret = usbfs_increase_memory_usage(u);
|
||||
if (ret)
|
||||
goto error;
|
||||
as->mem_usage = u;
|
||||
|
||||
if (uurb->buffer_length > 0) {
|
||||
as->urb->transfer_buffer = kmalloc(uurb->buffer_length,
|
||||
GFP_KERNEL);
|
||||
if (!as->urb->transfer_buffer) {
|
||||
kfree(isopkt);
|
||||
kfree(dr);
|
||||
free_async(as);
|
||||
return -ENOMEM;
|
||||
ret = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
/* Isochronous input data may end up being discontiguous
|
||||
* if some of the packets are short. Clear the buffer so
|
||||
|
@ -1253,6 +1306,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
|
|||
|
||||
as->urb->transfer_buffer_length = uurb->buffer_length;
|
||||
as->urb->setup_packet = (unsigned char *)dr;
|
||||
dr = NULL;
|
||||
as->urb->start_frame = uurb->start_frame;
|
||||
as->urb->number_of_packets = uurb->number_of_packets;
|
||||
if (uurb->type == USBDEVFS_URB_TYPE_ISO ||
|
||||
|
@ -1268,6 +1322,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
|
|||
totlen += isopkt[u].length;
|
||||
}
|
||||
kfree(isopkt);
|
||||
isopkt = NULL;
|
||||
as->ps = ps;
|
||||
as->userurb = arg;
|
||||
if (is_in && uurb->buffer_length > 0)
|
||||
|
@ -1282,8 +1337,8 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
|
|||
if (!is_in && uurb->buffer_length > 0) {
|
||||
if (copy_from_user(as->urb->transfer_buffer, uurb->buffer,
|
||||
uurb->buffer_length)) {
|
||||
free_async(as);
|
||||
return -EFAULT;
|
||||
ret = -EFAULT;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
snoop_urb(ps->dev, as->userurb, as->urb->pipe,
|
||||
|
@ -1329,10 +1384,16 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
|
|||
snoop_urb(ps->dev, as->userurb, as->urb->pipe,
|
||||
0, ret, COMPLETE, NULL, 0);
|
||||
async_removepending(as);
|
||||
free_async(as);
|
||||
return ret;
|
||||
goto error;
|
||||
}
|
||||
return 0;
|
||||
|
||||
error:
|
||||
kfree(isopkt);
|
||||
kfree(dr);
|
||||
if (as)
|
||||
free_async(as);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int proc_submiturb(struct dev_state *ps, void __user *arg)
|
||||
|
|
|
@ -45,10 +45,12 @@ ssize_t usb_store_new_id(struct usb_dynids *dynids,
|
|||
struct usb_dynid *dynid;
|
||||
u32 idVendor = 0;
|
||||
u32 idProduct = 0;
|
||||
unsigned int bInterfaceClass = 0;
|
||||
int fields = 0;
|
||||
int retval = 0;
|
||||
|
||||
fields = sscanf(buf, "%x %x", &idVendor, &idProduct);
|
||||
fields = sscanf(buf, "%x %x %x", &idVendor, &idProduct,
|
||||
&bInterfaceClass);
|
||||
if (fields < 2)
|
||||
return -EINVAL;
|
||||
|
||||
|
@ -60,6 +62,10 @@ ssize_t usb_store_new_id(struct usb_dynids *dynids,
|
|||
dynid->id.idVendor = idVendor;
|
||||
dynid->id.idProduct = idProduct;
|
||||
dynid->id.match_flags = USB_DEVICE_ID_MATCH_DEVICE;
|
||||
if (fields == 3) {
|
||||
dynid->id.bInterfaceClass = (u8)bInterfaceClass;
|
||||
dynid->id.match_flags |= USB_DEVICE_ID_MATCH_INT_CLASS;
|
||||
}
|
||||
|
||||
spin_lock(&dynids->lock);
|
||||
list_add_tail(&dynid->node, &dynids->list);
|
||||
|
@ -1073,17 +1079,10 @@ static int usb_suspend_interface(struct usb_device *udev,
|
|||
goto done;
|
||||
driver = to_usb_driver(intf->dev.driver);
|
||||
|
||||
if (driver->suspend) {
|
||||
status = driver->suspend(intf, msg);
|
||||
if (status && !PMSG_IS_AUTO(msg))
|
||||
dev_err(&intf->dev, "%s error %d\n",
|
||||
"suspend", status);
|
||||
} else {
|
||||
/* Later we will unbind the driver and reprobe */
|
||||
intf->needs_binding = 1;
|
||||
dev_warn(&intf->dev, "no %s for driver %s?\n",
|
||||
"suspend", driver->name);
|
||||
}
|
||||
/* at this time we know the driver supports suspend */
|
||||
status = driver->suspend(intf, msg);
|
||||
if (status && !PMSG_IS_AUTO(msg))
|
||||
dev_err(&intf->dev, "suspend error %d\n", status);
|
||||
|
||||
done:
|
||||
dev_vdbg(&intf->dev, "%s: status %d\n", __func__, status);
|
||||
|
@ -1132,16 +1131,9 @@ static int usb_resume_interface(struct usb_device *udev,
|
|||
"reset_resume", driver->name);
|
||||
}
|
||||
} else {
|
||||
if (driver->resume) {
|
||||
status = driver->resume(intf);
|
||||
if (status)
|
||||
dev_err(&intf->dev, "%s error %d\n",
|
||||
"resume", status);
|
||||
} else {
|
||||
intf->needs_binding = 1;
|
||||
dev_warn(&intf->dev, "no %s for driver %s?\n",
|
||||
"resume", driver->name);
|
||||
}
|
||||
status = driver->resume(intf);
|
||||
if (status)
|
||||
dev_err(&intf->dev, "resume error %d\n", status);
|
||||
}
|
||||
|
||||
done:
|
||||
|
|
|
@ -453,10 +453,6 @@ static int resume_common(struct device *dev, int event)
|
|||
|
||||
pci_set_master(pci_dev);
|
||||
|
||||
clear_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
|
||||
if (hcd->shared_hcd)
|
||||
clear_bit(HCD_FLAG_SAW_IRQ, &hcd->shared_hcd->flags);
|
||||
|
||||
if (hcd->driver->pci_resume && !HCD_DEAD(hcd)) {
|
||||
if (event != PM_EVENT_AUTO_RESUME)
|
||||
wait_for_companions(pci_dev, hcd);
|
||||
|
|
|
@ -658,7 +658,7 @@ error:
|
|||
len > offsetof(struct usb_device_descriptor,
|
||||
bDeviceProtocol))
|
||||
((struct usb_device_descriptor *) ubuf)->
|
||||
bDeviceProtocol = 1;
|
||||
bDeviceProtocol = USB_HUB_PR_HS_SINGLE_TT;
|
||||
}
|
||||
|
||||
/* any errors get returned through the urb completion */
|
||||
|
@ -1168,20 +1168,6 @@ int usb_hcd_check_unlink_urb(struct usb_hcd *hcd, struct urb *urb,
|
|||
if (urb->unlinked)
|
||||
return -EBUSY;
|
||||
urb->unlinked = status;
|
||||
|
||||
/* IRQ setup can easily be broken so that USB controllers
|
||||
* never get completion IRQs ... maybe even the ones we need to
|
||||
* finish unlinking the initial failed usb_set_address()
|
||||
* or device descriptor fetch.
|
||||
*/
|
||||
if (!HCD_SAW_IRQ(hcd) && !is_root_hub(urb->dev)) {
|
||||
dev_warn(hcd->self.controller, "Unlink after no-IRQ? "
|
||||
"Controller is probably using the wrong IRQ.\n");
|
||||
set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
|
||||
if (hcd->shared_hcd)
|
||||
set_bit(HCD_FLAG_SAW_IRQ, &hcd->shared_hcd->flags);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_hcd_check_unlink_urb);
|
||||
|
@ -1412,11 +1398,10 @@ int usb_hcd_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
|
|||
ret = -EAGAIN;
|
||||
else
|
||||
urb->transfer_flags |= URB_DMA_MAP_SG;
|
||||
if (n != urb->num_sgs) {
|
||||
urb->num_sgs = n;
|
||||
urb->num_mapped_sgs = n;
|
||||
if (n != urb->num_sgs)
|
||||
urb->transfer_flags |=
|
||||
URB_DMA_SG_COMBINED;
|
||||
}
|
||||
} else if (urb->sg) {
|
||||
struct scatterlist *sg = urb->sg;
|
||||
urb->transfer_dma = dma_map_page(
|
||||
|
@ -2148,16 +2133,12 @@ irqreturn_t usb_hcd_irq (int irq, void *__hcd)
|
|||
*/
|
||||
local_irq_save(flags);
|
||||
|
||||
if (unlikely(HCD_DEAD(hcd) || !HCD_HW_ACCESSIBLE(hcd))) {
|
||||
if (unlikely(HCD_DEAD(hcd) || !HCD_HW_ACCESSIBLE(hcd)))
|
||||
rc = IRQ_NONE;
|
||||
} else if (hcd->driver->irq(hcd) == IRQ_NONE) {
|
||||
else if (hcd->driver->irq(hcd) == IRQ_NONE)
|
||||
rc = IRQ_NONE;
|
||||
} else {
|
||||
set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
|
||||
if (hcd->shared_hcd)
|
||||
set_bit(HCD_FLAG_SAW_IRQ, &hcd->shared_hcd->flags);
|
||||
else
|
||||
rc = IRQ_HANDLED;
|
||||
}
|
||||
|
||||
local_irq_restore(flags);
|
||||
return rc;
|
||||
|
|
|
@ -84,7 +84,7 @@ struct usb_hub {
|
|||
|
||||
static inline int hub_is_superspeed(struct usb_device *hdev)
|
||||
{
|
||||
return (hdev->descriptor.bDeviceProtocol == 3);
|
||||
return (hdev->descriptor.bDeviceProtocol == USB_HUB_PR_SS);
|
||||
}
|
||||
|
||||
/* Protect struct usb_device->state and ->children members
|
||||
|
@ -1041,58 +1041,58 @@ static int hub_configure(struct usb_hub *hub,
|
|||
dev_dbg(hub_dev, "standalone hub\n");
|
||||
|
||||
switch (wHubCharacteristics & HUB_CHAR_LPSM) {
|
||||
case 0x00:
|
||||
dev_dbg(hub_dev, "ganged power switching\n");
|
||||
break;
|
||||
case 0x01:
|
||||
dev_dbg(hub_dev, "individual port power switching\n");
|
||||
break;
|
||||
case 0x02:
|
||||
case 0x03:
|
||||
dev_dbg(hub_dev, "no power switching (usb 1.0)\n");
|
||||
break;
|
||||
case HUB_CHAR_COMMON_LPSM:
|
||||
dev_dbg(hub_dev, "ganged power switching\n");
|
||||
break;
|
||||
case HUB_CHAR_INDV_PORT_LPSM:
|
||||
dev_dbg(hub_dev, "individual port power switching\n");
|
||||
break;
|
||||
case HUB_CHAR_NO_LPSM:
|
||||
case HUB_CHAR_LPSM:
|
||||
dev_dbg(hub_dev, "no power switching (usb 1.0)\n");
|
||||
break;
|
||||
}
|
||||
|
||||
switch (wHubCharacteristics & HUB_CHAR_OCPM) {
|
||||
case 0x00:
|
||||
dev_dbg(hub_dev, "global over-current protection\n");
|
||||
break;
|
||||
case 0x08:
|
||||
dev_dbg(hub_dev, "individual port over-current protection\n");
|
||||
break;
|
||||
case 0x10:
|
||||
case 0x18:
|
||||
dev_dbg(hub_dev, "no over-current protection\n");
|
||||
break;
|
||||
case HUB_CHAR_COMMON_OCPM:
|
||||
dev_dbg(hub_dev, "global over-current protection\n");
|
||||
break;
|
||||
case HUB_CHAR_INDV_PORT_OCPM:
|
||||
dev_dbg(hub_dev, "individual port over-current protection\n");
|
||||
break;
|
||||
case HUB_CHAR_NO_OCPM:
|
||||
case HUB_CHAR_OCPM:
|
||||
dev_dbg(hub_dev, "no over-current protection\n");
|
||||
break;
|
||||
}
|
||||
|
||||
spin_lock_init (&hub->tt.lock);
|
||||
INIT_LIST_HEAD (&hub->tt.clear_list);
|
||||
INIT_WORK(&hub->tt.clear_work, hub_tt_work);
|
||||
switch (hdev->descriptor.bDeviceProtocol) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
dev_dbg(hub_dev, "Single TT\n");
|
||||
hub->tt.hub = hdev;
|
||||
break;
|
||||
case 2:
|
||||
ret = usb_set_interface(hdev, 0, 1);
|
||||
if (ret == 0) {
|
||||
dev_dbg(hub_dev, "TT per port\n");
|
||||
hub->tt.multi = 1;
|
||||
} else
|
||||
dev_err(hub_dev, "Using single TT (err %d)\n",
|
||||
ret);
|
||||
hub->tt.hub = hdev;
|
||||
break;
|
||||
case 3:
|
||||
/* USB 3.0 hubs don't have a TT */
|
||||
break;
|
||||
default:
|
||||
dev_dbg(hub_dev, "Unrecognized hub protocol %d\n",
|
||||
hdev->descriptor.bDeviceProtocol);
|
||||
break;
|
||||
case USB_HUB_PR_FS:
|
||||
break;
|
||||
case USB_HUB_PR_HS_SINGLE_TT:
|
||||
dev_dbg(hub_dev, "Single TT\n");
|
||||
hub->tt.hub = hdev;
|
||||
break;
|
||||
case USB_HUB_PR_HS_MULTI_TT:
|
||||
ret = usb_set_interface(hdev, 0, 1);
|
||||
if (ret == 0) {
|
||||
dev_dbg(hub_dev, "TT per port\n");
|
||||
hub->tt.multi = 1;
|
||||
} else
|
||||
dev_err(hub_dev, "Using single TT (err %d)\n",
|
||||
ret);
|
||||
hub->tt.hub = hdev;
|
||||
break;
|
||||
case USB_HUB_PR_SS:
|
||||
/* USB 3.0 hubs don't have a TT */
|
||||
break;
|
||||
default:
|
||||
dev_dbg(hub_dev, "Unrecognized hub protocol %d\n",
|
||||
hdev->descriptor.bDeviceProtocol);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Note 8 FS bit times == (8 bits / 12000000 bps) ~= 666ns */
|
||||
|
@ -1360,7 +1360,6 @@ descriptor_error:
|
|||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* No BKL needed */
|
||||
static int
|
||||
hub_ioctl(struct usb_interface *intf, unsigned int code, void *user_data)
|
||||
{
|
||||
|
|
|
@ -117,9 +117,12 @@ static const struct usb_device_id usb_quirk_list[] = {
|
|||
{ USB_DEVICE(0x06a3, 0x0006), .driver_info =
|
||||
USB_QUIRK_CONFIG_INTF_STRINGS },
|
||||
|
||||
/* Guillemot Webcam Hercules Dualpix Exchange*/
|
||||
/* Guillemot Webcam Hercules Dualpix Exchange (2nd ID) */
|
||||
{ USB_DEVICE(0x06f8, 0x0804), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||
|
||||
/* Guillemot Webcam Hercules Dualpix Exchange*/
|
||||
{ USB_DEVICE(0x06f8, 0x3005), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||
|
||||
/* M-Systems Flash Disk Pioneers */
|
||||
{ USB_DEVICE(0x08ec, 0x1000), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||
|
||||
|
|
|
@ -132,20 +132,6 @@ 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;
|
||||
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
config USB_DWC3
|
||||
tristate "DesignWare USB3 DRD Core Support"
|
||||
depends on (USB || USB_GADGET)
|
||||
depends on (USB && USB_GADGET)
|
||||
select USB_OTG_UTILS
|
||||
select USB_GADGET_DUALSPEED
|
||||
select USB_GADGET_SUPERSPEED
|
||||
select USB_XHCI_PLATFORM
|
||||
help
|
||||
Say Y or M here if your system has a Dual Role SuperSpeed
|
||||
USB controller based on the DesignWare USB3 IP Core.
|
||||
|
|
|
@ -4,10 +4,8 @@ ccflags-$(CONFIG_USB_DWC3_VERBOSE) += -DVERBOSE_DEBUG
|
|||
obj-$(CONFIG_USB_DWC3) += dwc3.o
|
||||
|
||||
dwc3-y := core.o
|
||||
|
||||
ifneq ($(CONFIG_USB_GADGET_DWC3),)
|
||||
dwc3-y += gadget.o ep0.o
|
||||
endif
|
||||
dwc3-y += host.o
|
||||
dwc3-y += gadget.o ep0.o
|
||||
|
||||
ifneq ($(CONFIG_DEBUG_FS),)
|
||||
dwc3-y += debugfs.o
|
||||
|
|
|
@ -59,6 +59,60 @@
|
|||
|
||||
#include "debug.h"
|
||||
|
||||
static char *maximum_speed = "super";
|
||||
module_param(maximum_speed, charp, 0);
|
||||
MODULE_PARM_DESC(maximum_speed, "Maximum supported speed.");
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
#define DWC3_DEVS_POSSIBLE 32
|
||||
|
||||
static DECLARE_BITMAP(dwc3_devs, DWC3_DEVS_POSSIBLE);
|
||||
|
||||
int dwc3_get_device_id(void)
|
||||
{
|
||||
int id;
|
||||
|
||||
again:
|
||||
id = find_first_zero_bit(dwc3_devs, DWC3_DEVS_POSSIBLE);
|
||||
if (id < DWC3_DEVS_POSSIBLE) {
|
||||
int old;
|
||||
|
||||
old = test_and_set_bit(id, dwc3_devs);
|
||||
if (old)
|
||||
goto again;
|
||||
} else {
|
||||
pr_err("dwc3: no space for new device\n");
|
||||
id = -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dwc3_get_device_id);
|
||||
|
||||
void dwc3_put_device_id(int id)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (id < 0)
|
||||
return;
|
||||
|
||||
ret = test_bit(id, dwc3_devs);
|
||||
WARN(!ret, "dwc3: ID %d not in use\n", id);
|
||||
clear_bit(id, dwc3_devs);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dwc3_put_device_id);
|
||||
|
||||
void dwc3_set_mode(struct dwc3 *dwc, u32 mode)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GCTL);
|
||||
reg &= ~(DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_OTG));
|
||||
reg |= DWC3_GCTL_PRTCAPDIR(mode);
|
||||
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc3_core_soft_reset - Issues core soft reset and PHY reset
|
||||
* @dwc: pointer to our context structure
|
||||
|
@ -150,7 +204,7 @@ static void dwc3_free_event_buffers(struct dwc3 *dwc)
|
|||
struct dwc3_event_buffer *evt;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < DWC3_EVENT_BUFFERS_NUM; i++) {
|
||||
for (i = 0; i < dwc->num_event_buffers; i++) {
|
||||
evt = dwc->ev_buffs[i];
|
||||
if (evt) {
|
||||
dwc3_free_one_event_buffer(dwc, evt);
|
||||
|
@ -162,17 +216,25 @@ static void dwc3_free_event_buffers(struct dwc3 *dwc)
|
|||
/**
|
||||
* dwc3_alloc_event_buffers - Allocates @num event buffers of size @length
|
||||
* @dwc: Pointer to out controller context structure
|
||||
* @num: number of event buffers to allocate
|
||||
* @length: size of event buffer
|
||||
*
|
||||
* Returns 0 on success otherwise negative errno. In error the case, dwc
|
||||
* may contain some buffers allocated but not all which were requested.
|
||||
*/
|
||||
static int __devinit dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned num,
|
||||
unsigned length)
|
||||
static int __devinit dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned length)
|
||||
{
|
||||
int num;
|
||||
int i;
|
||||
|
||||
num = DWC3_NUM_INT(dwc->hwparams.hwparams1);
|
||||
dwc->num_event_buffers = num;
|
||||
|
||||
dwc->ev_buffs = kzalloc(sizeof(*dwc->ev_buffs) * num, GFP_KERNEL);
|
||||
if (!dwc->ev_buffs) {
|
||||
dev_err(dwc->dev, "can't allocate event buffers array\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
struct dwc3_event_buffer *evt;
|
||||
|
||||
|
@ -198,7 +260,7 @@ static int __devinit dwc3_event_buffers_setup(struct dwc3 *dwc)
|
|||
struct dwc3_event_buffer *evt;
|
||||
int n;
|
||||
|
||||
for (n = 0; n < DWC3_EVENT_BUFFERS_NUM; n++) {
|
||||
for (n = 0; n < dwc->num_event_buffers; n++) {
|
||||
evt = dwc->ev_buffs[n];
|
||||
dev_dbg(dwc->dev, "Event buf %p dma %08llx length %d\n",
|
||||
evt->buf, (unsigned long long) evt->dma,
|
||||
|
@ -221,7 +283,7 @@ static void dwc3_event_buffers_cleanup(struct dwc3 *dwc)
|
|||
struct dwc3_event_buffer *evt;
|
||||
int n;
|
||||
|
||||
for (n = 0; n < DWC3_EVENT_BUFFERS_NUM; n++) {
|
||||
for (n = 0; n < dwc->num_event_buffers; n++) {
|
||||
evt = dwc->ev_buffs[n];
|
||||
dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(n), 0);
|
||||
dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(n), 0);
|
||||
|
@ -285,8 +347,32 @@ static int __devinit dwc3_core_init(struct dwc3 *dwc)
|
|||
cpu_relax();
|
||||
} while (true);
|
||||
|
||||
ret = dwc3_alloc_event_buffers(dwc, DWC3_EVENT_BUFFERS_NUM,
|
||||
DWC3_EVENT_BUFFERS_SIZE);
|
||||
dwc3_cache_hwparams(dwc);
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GCTL);
|
||||
reg &= ~DWC3_GCTL_SCALEDOWN(3);
|
||||
reg &= ~DWC3_GCTL_DISSCRAMBLE;
|
||||
|
||||
switch (DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams1)) {
|
||||
case DWC3_GHWPARAMS1_EN_PWROPT_CLK:
|
||||
reg &= ~DWC3_GCTL_DSBLCLKGTNG;
|
||||
break;
|
||||
default:
|
||||
dev_dbg(dwc->dev, "No power optimization available\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* WORKAROUND: DWC3 revisions <1.90a have a bug
|
||||
* when The device fails to connect at SuperSpeed
|
||||
* and falls back to high-speed mode which causes
|
||||
* the device to enter in a Connect/Disconnect loop
|
||||
*/
|
||||
if (dwc->revision < DWC3_REVISION_190A)
|
||||
reg |= DWC3_GCTL_U2RSTECN;
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
|
||||
|
||||
ret = dwc3_alloc_event_buffers(dwc, DWC3_EVENT_BUFFERS_SIZE);
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "failed to allocate event buffers\n");
|
||||
ret = -ENOMEM;
|
||||
|
@ -299,8 +385,6 @@ static int __devinit dwc3_core_init(struct dwc3 *dwc)
|
|||
goto err1;
|
||||
}
|
||||
|
||||
dwc3_cache_hwparams(dwc);
|
||||
|
||||
return 0;
|
||||
|
||||
err1:
|
||||
|
@ -320,15 +404,17 @@ static void dwc3_core_exit(struct dwc3 *dwc)
|
|||
|
||||
static int __devinit dwc3_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct platform_device_id *id = platform_get_device_id(pdev);
|
||||
struct resource *res;
|
||||
struct dwc3 *dwc;
|
||||
void __iomem *regs;
|
||||
unsigned int features = id->driver_data;
|
||||
|
||||
int ret = -ENOMEM;
|
||||
int irq;
|
||||
|
||||
void __iomem *regs;
|
||||
void *mem;
|
||||
|
||||
u8 mode;
|
||||
|
||||
mem = kzalloc(sizeof(*dwc) + DWC3_ALIGN_MASK, GFP_KERNEL);
|
||||
if (!mem) {
|
||||
dev_err(&pdev->dev, "not enough memory\n");
|
||||
|
@ -343,6 +429,8 @@ static int __devinit dwc3_probe(struct platform_device *pdev)
|
|||
goto err1;
|
||||
}
|
||||
|
||||
dwc->res = res;
|
||||
|
||||
res = request_mem_region(res->start, resource_size(res),
|
||||
dev_name(&pdev->dev));
|
||||
if (!res) {
|
||||
|
@ -370,6 +458,17 @@ static int __devinit dwc3_probe(struct platform_device *pdev)
|
|||
dwc->dev = &pdev->dev;
|
||||
dwc->irq = irq;
|
||||
|
||||
if (!strncmp("super", maximum_speed, 5))
|
||||
dwc->maximum_speed = DWC3_DCFG_SUPERSPEED;
|
||||
else if (!strncmp("high", maximum_speed, 4))
|
||||
dwc->maximum_speed = DWC3_DCFG_HIGHSPEED;
|
||||
else if (!strncmp("full", maximum_speed, 4))
|
||||
dwc->maximum_speed = DWC3_DCFG_FULLSPEED1;
|
||||
else if (!strncmp("low", maximum_speed, 3))
|
||||
dwc->maximum_speed = DWC3_DCFG_LOWSPEED;
|
||||
else
|
||||
dwc->maximum_speed = DWC3_DCFG_SUPERSPEED;
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
pm_runtime_forbid(&pdev->dev);
|
||||
|
@ -380,13 +479,44 @@ static int __devinit dwc3_probe(struct platform_device *pdev)
|
|||
goto err3;
|
||||
}
|
||||
|
||||
if (features & DWC3_HAS_PERIPHERAL) {
|
||||
mode = DWC3_MODE(dwc->hwparams.hwparams0);
|
||||
|
||||
switch (mode) {
|
||||
case DWC3_MODE_DEVICE:
|
||||
dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE);
|
||||
ret = dwc3_gadget_init(dwc);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to initialized gadget\n");
|
||||
dev_err(&pdev->dev, "failed to initialize gadget\n");
|
||||
goto err4;
|
||||
}
|
||||
break;
|
||||
case DWC3_MODE_HOST:
|
||||
dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST);
|
||||
ret = dwc3_host_init(dwc);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to initialize host\n");
|
||||
goto err4;
|
||||
}
|
||||
break;
|
||||
case DWC3_MODE_DRD:
|
||||
dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG);
|
||||
ret = dwc3_host_init(dwc);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to initialize host\n");
|
||||
goto err4;
|
||||
}
|
||||
|
||||
ret = dwc3_gadget_init(dwc);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to initialize gadget\n");
|
||||
goto err4;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
dev_err(&pdev->dev, "Unsupported mode of operation %d\n", mode);
|
||||
goto err4;
|
||||
}
|
||||
dwc->mode = mode;
|
||||
|
||||
ret = dwc3_debugfs_init(dwc);
|
||||
if (ret) {
|
||||
|
@ -399,8 +529,21 @@ static int __devinit dwc3_probe(struct platform_device *pdev)
|
|||
return 0;
|
||||
|
||||
err5:
|
||||
if (features & DWC3_HAS_PERIPHERAL)
|
||||
switch (mode) {
|
||||
case DWC3_MODE_DEVICE:
|
||||
dwc3_gadget_exit(dwc);
|
||||
break;
|
||||
case DWC3_MODE_HOST:
|
||||
dwc3_host_exit(dwc);
|
||||
break;
|
||||
case DWC3_MODE_DRD:
|
||||
dwc3_host_exit(dwc);
|
||||
dwc3_gadget_exit(dwc);
|
||||
break;
|
||||
default:
|
||||
/* do nothing */
|
||||
break;
|
||||
}
|
||||
|
||||
err4:
|
||||
dwc3_core_exit(dwc);
|
||||
|
@ -420,10 +563,8 @@ err0:
|
|||
|
||||
static int __devexit dwc3_remove(struct platform_device *pdev)
|
||||
{
|
||||
const struct platform_device_id *id = platform_get_device_id(pdev);
|
||||
struct dwc3 *dwc = platform_get_drvdata(pdev);
|
||||
struct resource *res;
|
||||
unsigned int features = id->driver_data;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
|
||||
|
@ -432,8 +573,21 @@ static int __devexit dwc3_remove(struct platform_device *pdev)
|
|||
|
||||
dwc3_debugfs_exit(dwc);
|
||||
|
||||
if (features & DWC3_HAS_PERIPHERAL)
|
||||
switch (dwc->mode) {
|
||||
case DWC3_MODE_DEVICE:
|
||||
dwc3_gadget_exit(dwc);
|
||||
break;
|
||||
case DWC3_MODE_HOST:
|
||||
dwc3_host_exit(dwc);
|
||||
break;
|
||||
case DWC3_MODE_DRD:
|
||||
dwc3_host_exit(dwc);
|
||||
dwc3_gadget_exit(dwc);
|
||||
break;
|
||||
default:
|
||||
/* do nothing */
|
||||
break;
|
||||
}
|
||||
|
||||
dwc3_core_exit(dwc);
|
||||
release_mem_region(res->start, resource_size(res));
|
||||
|
@ -443,30 +597,15 @@ static int __devexit dwc3_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct platform_device_id dwc3_id_table[] __devinitconst = {
|
||||
{
|
||||
.name = "dwc3-omap",
|
||||
.driver_data = (DWC3_HAS_PERIPHERAL
|
||||
| DWC3_HAS_XHCI
|
||||
| DWC3_HAS_OTG),
|
||||
},
|
||||
{
|
||||
.name = "dwc3-pci",
|
||||
.driver_data = DWC3_HAS_PERIPHERAL,
|
||||
},
|
||||
{ }, /* Terminating Entry */
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, dwc3_id_table);
|
||||
|
||||
static struct platform_driver dwc3_driver = {
|
||||
.probe = dwc3_probe,
|
||||
.remove = __devexit_p(dwc3_remove),
|
||||
.driver = {
|
||||
.name = "dwc3",
|
||||
},
|
||||
.id_table = dwc3_id_table,
|
||||
};
|
||||
|
||||
MODULE_ALIAS("platform:dwc3");
|
||||
MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
||||
MODULE_DESCRIPTION("DesignWare USB3 DRD Controller Driver");
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
|
||||
#include <linux/device.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/mm.h>
|
||||
|
@ -52,7 +53,6 @@
|
|||
/* Global constants */
|
||||
#define DWC3_ENDPOINTS_NUM 32
|
||||
|
||||
#define DWC3_EVENT_BUFFERS_NUM 2
|
||||
#define DWC3_EVENT_BUFFERS_SIZE PAGE_SIZE
|
||||
#define DWC3_EVENT_TYPE_MASK 0xfe
|
||||
|
||||
|
@ -153,6 +153,7 @@
|
|||
#define DWC3_GCTL_CLK_PIPEHALF (2)
|
||||
#define DWC3_GCTL_CLK_MASK (3)
|
||||
|
||||
#define DWC3_GCTL_PRTCAP(n) (((n) & (3 << 12)) >> 12)
|
||||
#define DWC3_GCTL_PRTCAPDIR(n) (n << 12)
|
||||
#define DWC3_GCTL_PRTCAP_HOST 1
|
||||
#define DWC3_GCTL_PRTCAP_DEVICE 2
|
||||
|
@ -347,6 +348,7 @@ struct dwc3_ep {
|
|||
u32 free_slot;
|
||||
u32 busy_slot;
|
||||
const struct usb_endpoint_descriptor *desc;
|
||||
const struct usb_ss_ep_comp_descriptor *comp_desc;
|
||||
struct dwc3 *dwc;
|
||||
|
||||
unsigned flags;
|
||||
|
@ -536,6 +538,31 @@ struct dwc3_hwparams {
|
|||
u32 hwparams8;
|
||||
};
|
||||
|
||||
/* HWPARAMS0 */
|
||||
#define DWC3_MODE(n) ((n) & 0x7)
|
||||
|
||||
#define DWC3_MODE_DEVICE 0
|
||||
#define DWC3_MODE_HOST 1
|
||||
#define DWC3_MODE_DRD 2
|
||||
#define DWC3_MODE_HUB 3
|
||||
|
||||
/* HWPARAMS1 */
|
||||
#define DWC3_NUM_INT(n) (((n) & (0x3f << 15)) >> 15)
|
||||
|
||||
struct dwc3_request {
|
||||
struct usb_request request;
|
||||
struct list_head list;
|
||||
struct dwc3_ep *dep;
|
||||
|
||||
u8 epnum;
|
||||
struct dwc3_trb_hw *trb;
|
||||
dma_addr_t trb_dma;
|
||||
|
||||
unsigned direction:1;
|
||||
unsigned mapped:1;
|
||||
unsigned queued:1;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dwc3 - representation of our controller
|
||||
* @ctrl_req: usb control request which is used for ep0
|
||||
|
@ -549,19 +576,24 @@ struct dwc3_hwparams {
|
|||
* @ep0_bounce_addr: dma address of ep0_bounce
|
||||
* @lock: for synchronizing
|
||||
* @dev: pointer to our struct device
|
||||
* @xhci: pointer to our xHCI child
|
||||
* @event_buffer_list: a list of event buffers
|
||||
* @gadget: device side representation of the peripheral controller
|
||||
* @gadget_driver: pointer to the gadget driver
|
||||
* @regs: base address for our registers
|
||||
* @regs_size: address space size
|
||||
* @irq: IRQ number
|
||||
* @num_event_buffers: calculated number of event buffers
|
||||
* @u1u2: only used on revisions <1.83a for workaround
|
||||
* @maximum_speed: maximum speed requested (mainly for testing purposes)
|
||||
* @revision: revision register contents
|
||||
* @mode: mode of operation
|
||||
* @is_selfpowered: true when we are selfpowered
|
||||
* @three_stage_setup: set if we perform a three phase setup
|
||||
* @ep0_status_pending: ep0 status response without a req is pending
|
||||
* @ep0_bounced: true when we used bounce buffer
|
||||
* @ep0_expect_in: true when we expect a DATA IN transfer
|
||||
* @start_config_issued: true when StartConfig command has been issued
|
||||
* @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround
|
||||
* @ep0_next_event: hold the next expected event
|
||||
* @ep0state: state of endpoint zero
|
||||
* @link_state: link state
|
||||
|
@ -579,12 +611,15 @@ struct dwc3 {
|
|||
dma_addr_t ep0_trb_addr;
|
||||
dma_addr_t setup_buf_addr;
|
||||
dma_addr_t ep0_bounce_addr;
|
||||
struct usb_request ep0_usb_req;
|
||||
struct dwc3_request ep0_usb_req;
|
||||
/* device lock */
|
||||
spinlock_t lock;
|
||||
struct device *dev;
|
||||
|
||||
struct dwc3_event_buffer *ev_buffs[DWC3_EVENT_BUFFERS_NUM];
|
||||
struct platform_device *xhci;
|
||||
struct resource *res;
|
||||
|
||||
struct dwc3_event_buffer **ev_buffs;
|
||||
struct dwc3_ep *eps[DWC3_ENDPOINTS_NUM];
|
||||
|
||||
struct usb_gadget gadget;
|
||||
|
@ -595,7 +630,11 @@ struct dwc3 {
|
|||
|
||||
int irq;
|
||||
|
||||
u32 num_event_buffers;
|
||||
u32 u1u2;
|
||||
u32 maximum_speed;
|
||||
u32 revision;
|
||||
u32 mode;
|
||||
|
||||
#define DWC3_REVISION_173A 0x5533173a
|
||||
#define DWC3_REVISION_175A 0x5533175a
|
||||
|
@ -607,10 +646,11 @@ struct dwc3 {
|
|||
|
||||
unsigned is_selfpowered:1;
|
||||
unsigned three_stage_setup:1;
|
||||
unsigned ep0_status_pending:1;
|
||||
unsigned ep0_bounced:1;
|
||||
unsigned ep0_expect_in:1;
|
||||
unsigned start_config_issued:1;
|
||||
unsigned setup_packet_pending:1;
|
||||
unsigned delayed_status:1;
|
||||
|
||||
enum dwc3_ep0_next ep0_next_event;
|
||||
enum dwc3_ep0_state ep0state;
|
||||
|
@ -765,4 +805,16 @@ union dwc3_event {
|
|||
#define DWC3_HAS_XHCI BIT(1)
|
||||
#define DWC3_HAS_OTG BIT(3)
|
||||
|
||||
/* prototypes */
|
||||
void dwc3_set_mode(struct dwc3 *dwc, u32 mode);
|
||||
|
||||
int dwc3_host_init(struct dwc3 *dwc);
|
||||
void dwc3_host_exit(struct dwc3 *dwc);
|
||||
|
||||
int dwc3_gadget_init(struct dwc3 *dwc);
|
||||
void dwc3_gadget_exit(struct dwc3 *dwc);
|
||||
|
||||
extern int dwc3_get_device_id(void);
|
||||
extern void dwc3_put_device_id(int id);
|
||||
|
||||
#endif /* __DRIVERS_USB_DWC3_CORE_H */
|
||||
|
|
|
@ -44,12 +44,12 @@
|
|||
#include <linux/debugfs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include "core.h"
|
||||
#include "gadget.h"
|
||||
#include "io.h"
|
||||
#include "debug.h"
|
||||
|
||||
#define dump_register(nm) \
|
||||
{ \
|
||||
|
@ -395,6 +395,75 @@ static const struct file_operations dwc3_regdump_fops = {
|
|||
.release = single_release,
|
||||
};
|
||||
|
||||
static int dwc3_mode_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct dwc3 *dwc = s->private;
|
||||
unsigned long flags;
|
||||
u32 reg;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GCTL);
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
switch (DWC3_GCTL_PRTCAP(reg)) {
|
||||
case DWC3_GCTL_PRTCAP_HOST:
|
||||
seq_printf(s, "host\n");
|
||||
break;
|
||||
case DWC3_GCTL_PRTCAP_DEVICE:
|
||||
seq_printf(s, "device\n");
|
||||
break;
|
||||
case DWC3_GCTL_PRTCAP_OTG:
|
||||
seq_printf(s, "OTG\n");
|
||||
break;
|
||||
default:
|
||||
seq_printf(s, "UNKNOWN %08x\n", DWC3_GCTL_PRTCAP(reg));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_mode_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, dwc3_mode_show, inode->i_private);
|
||||
}
|
||||
|
||||
static ssize_t dwc3_mode_write(struct file *file,
|
||||
const char __user *ubuf, size_t count, loff_t *ppos)
|
||||
{
|
||||
struct seq_file *s = file->private_data;
|
||||
struct dwc3 *dwc = s->private;
|
||||
unsigned long flags;
|
||||
u32 mode = 0;
|
||||
char buf[32];
|
||||
|
||||
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
|
||||
return -EFAULT;
|
||||
|
||||
if (!strncmp(buf, "host", 4))
|
||||
mode |= DWC3_GCTL_PRTCAP_HOST;
|
||||
|
||||
if (!strncmp(buf, "device", 6))
|
||||
mode |= DWC3_GCTL_PRTCAP_DEVICE;
|
||||
|
||||
if (!strncmp(buf, "otg", 3))
|
||||
mode |= DWC3_GCTL_PRTCAP_OTG;
|
||||
|
||||
if (mode) {
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
dwc3_set_mode(dwc, mode);
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static const struct file_operations dwc3_mode_fops = {
|
||||
.open = dwc3_mode_open,
|
||||
.write = dwc3_mode_write,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
int __devinit dwc3_debugfs_init(struct dwc3 *dwc)
|
||||
{
|
||||
struct dentry *root;
|
||||
|
@ -402,7 +471,7 @@ int __devinit dwc3_debugfs_init(struct dwc3 *dwc)
|
|||
int ret;
|
||||
|
||||
root = debugfs_create_dir(dev_name(dwc->dev), NULL);
|
||||
if (IS_ERR(root)){
|
||||
if (IS_ERR(root)) {
|
||||
ret = PTR_ERR(root);
|
||||
goto err0;
|
||||
}
|
||||
|
@ -415,6 +484,14 @@ int __devinit dwc3_debugfs_init(struct dwc3 *dwc)
|
|||
ret = PTR_ERR(file);
|
||||
goto err1;
|
||||
}
|
||||
|
||||
file = debugfs_create_file("mode", S_IRUGO | S_IWUSR, root,
|
||||
dwc, &dwc3_mode_fops);
|
||||
if (IS_ERR(file)) {
|
||||
ret = PTR_ERR(file);
|
||||
goto err1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err1:
|
||||
|
|
|
@ -48,6 +48,7 @@
|
|||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include "core.h"
|
||||
#include "io.h"
|
||||
|
||||
/*
|
||||
|
@ -200,6 +201,7 @@ static int __devinit dwc3_omap_probe(struct platform_device *pdev)
|
|||
struct dwc3_omap *omap;
|
||||
struct resource *res;
|
||||
|
||||
int devid;
|
||||
int ret = -ENOMEM;
|
||||
int irq;
|
||||
|
||||
|
@ -236,16 +238,20 @@ static int __devinit dwc3_omap_probe(struct platform_device *pdev)
|
|||
goto err1;
|
||||
}
|
||||
|
||||
dwc3 = platform_device_alloc("dwc3-omap", -1);
|
||||
devid = dwc3_get_device_id();
|
||||
if (devid < 0)
|
||||
goto err2;
|
||||
|
||||
dwc3 = platform_device_alloc("dwc3", devid);
|
||||
if (!dwc3) {
|
||||
dev_err(&pdev->dev, "couldn't allocate dwc3 device\n");
|
||||
goto err2;
|
||||
goto err3;
|
||||
}
|
||||
|
||||
context = kzalloc(resource_size(res), GFP_KERNEL);
|
||||
if (!context) {
|
||||
dev_err(&pdev->dev, "couldn't allocate dwc3 context memory\n");
|
||||
goto err3;
|
||||
goto err4;
|
||||
}
|
||||
|
||||
spin_lock_init(&omap->lock);
|
||||
|
@ -299,7 +305,7 @@ static int __devinit dwc3_omap_probe(struct platform_device *pdev)
|
|||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to request IRQ #%d --> %d\n",
|
||||
omap->irq, ret);
|
||||
goto err4;
|
||||
goto err5;
|
||||
}
|
||||
|
||||
/* enable all IRQs */
|
||||
|
@ -322,26 +328,29 @@ static int __devinit dwc3_omap_probe(struct platform_device *pdev)
|
|||
pdev->num_resources);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "couldn't add resources to dwc3 device\n");
|
||||
goto err5;
|
||||
goto err6;
|
||||
}
|
||||
|
||||
ret = platform_device_add(dwc3);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to register dwc3 device\n");
|
||||
goto err5;
|
||||
goto err6;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err5:
|
||||
err6:
|
||||
free_irq(omap->irq, omap);
|
||||
|
||||
err4:
|
||||
err5:
|
||||
kfree(omap->context);
|
||||
|
||||
err3:
|
||||
err4:
|
||||
platform_device_put(dwc3);
|
||||
|
||||
err3:
|
||||
dwc3_put_device_id(devid);
|
||||
|
||||
err2:
|
||||
iounmap(base);
|
||||
|
||||
|
@ -358,6 +367,7 @@ static int __devexit dwc3_omap_remove(struct platform_device *pdev)
|
|||
|
||||
platform_device_unregister(omap->dwc3);
|
||||
|
||||
dwc3_put_device_id(omap->dwc3->id);
|
||||
free_irq(omap->irq, omap);
|
||||
iounmap(omap->base);
|
||||
|
||||
|
@ -384,18 +394,9 @@ static struct platform_driver dwc3_omap_driver = {
|
|||
},
|
||||
};
|
||||
|
||||
module_platform_driver(dwc3_omap_driver);
|
||||
|
||||
MODULE_ALIAS("platform:omap-dwc3");
|
||||
MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
||||
MODULE_DESCRIPTION("DesignWare USB3 OMAP Glue Layer");
|
||||
|
||||
static int __devinit dwc3_omap_init(void)
|
||||
{
|
||||
return platform_driver_register(&dwc3_omap_driver);
|
||||
}
|
||||
module_init(dwc3_omap_init);
|
||||
|
||||
static void __exit dwc3_omap_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&dwc3_omap_driver);
|
||||
}
|
||||
module_exit(dwc3_omap_exit);
|
||||
|
|
|
@ -42,52 +42,17 @@
|
|||
#include <linux/pci.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include "core.h"
|
||||
|
||||
/* FIXME define these in <linux/pci_ids.h> */
|
||||
#define PCI_VENDOR_ID_SYNOPSYS 0x16c3
|
||||
#define PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3 0xabcd
|
||||
|
||||
#define DWC3_PCI_DEVS_POSSIBLE 32
|
||||
|
||||
struct dwc3_pci {
|
||||
struct device *dev;
|
||||
struct platform_device *dwc3;
|
||||
};
|
||||
|
||||
static DECLARE_BITMAP(dwc3_pci_devs, DWC3_PCI_DEVS_POSSIBLE);
|
||||
|
||||
static int dwc3_pci_get_device_id(struct dwc3_pci *glue)
|
||||
{
|
||||
int id;
|
||||
|
||||
again:
|
||||
id = find_first_zero_bit(dwc3_pci_devs, DWC3_PCI_DEVS_POSSIBLE);
|
||||
if (id < DWC3_PCI_DEVS_POSSIBLE) {
|
||||
int old;
|
||||
|
||||
old = test_and_set_bit(id, dwc3_pci_devs);
|
||||
if (old)
|
||||
goto again;
|
||||
} else {
|
||||
dev_err(glue->dev, "no space for new device\n");
|
||||
id = -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dwc3_pci_put_device_id(struct dwc3_pci *glue, int id)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (id < 0)
|
||||
return;
|
||||
|
||||
ret = test_bit(id, dwc3_pci_devs);
|
||||
WARN(!ret, "Device: %s\nID %d not in use\n",
|
||||
dev_driver_string(glue->dev), id);
|
||||
clear_bit(id, dwc3_pci_devs);
|
||||
}
|
||||
|
||||
static int __devinit dwc3_pci_probe(struct pci_dev *pci,
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
|
@ -114,11 +79,11 @@ static int __devinit dwc3_pci_probe(struct pci_dev *pci,
|
|||
pci_set_power_state(pci, PCI_D0);
|
||||
pci_set_master(pci);
|
||||
|
||||
devid = dwc3_pci_get_device_id(glue);
|
||||
devid = dwc3_get_device_id();
|
||||
if (devid < 0)
|
||||
goto err2;
|
||||
|
||||
dwc3 = platform_device_alloc("dwc3-pci", devid);
|
||||
dwc3 = platform_device_alloc("dwc3", devid);
|
||||
if (!dwc3) {
|
||||
dev_err(&pci->dev, "couldn't allocate dwc3 device\n");
|
||||
goto err3;
|
||||
|
@ -163,13 +128,13 @@ err4:
|
|||
platform_device_put(dwc3);
|
||||
|
||||
err3:
|
||||
dwc3_pci_put_device_id(glue, devid);
|
||||
dwc3_put_device_id(devid);
|
||||
|
||||
err2:
|
||||
pci_disable_device(pci);
|
||||
|
||||
err1:
|
||||
kfree(pci);
|
||||
kfree(glue);
|
||||
|
||||
err0:
|
||||
return ret;
|
||||
|
@ -179,7 +144,7 @@ static void __devexit dwc3_pci_remove(struct pci_dev *pci)
|
|||
{
|
||||
struct dwc3_pci *glue = pci_get_drvdata(pci);
|
||||
|
||||
dwc3_pci_put_device_id(glue, glue->dwc3->id);
|
||||
dwc3_put_device_id(glue->dwc3->id);
|
||||
platform_device_unregister(glue->dwc3);
|
||||
pci_set_drvdata(pci, NULL);
|
||||
pci_disable_device(pci);
|
||||
|
@ -196,7 +161,7 @@ static DEFINE_PCI_DEVICE_TABLE(dwc3_pci_id_table) = {
|
|||
MODULE_DEVICE_TABLE(pci, dwc3_pci_id_table);
|
||||
|
||||
static struct pci_driver dwc3_pci_driver = {
|
||||
.name = "pci-dwc3",
|
||||
.name = "dwc3-pci",
|
||||
.id_table = dwc3_pci_id_table,
|
||||
.probe = dwc3_pci_probe,
|
||||
.remove = __devexit_p(dwc3_pci_remove),
|
||||
|
|
|
@ -48,13 +48,13 @@
|
|||
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
#include <linux/usb/composite.h>
|
||||
|
||||
#include "core.h"
|
||||
#include "gadget.h"
|
||||
#include "io.h"
|
||||
|
||||
static void dwc3_ep0_inspect_setup(struct dwc3 *dwc,
|
||||
const struct dwc3_event_depevt *event);
|
||||
static void dwc3_ep0_do_control_status(struct dwc3 *dwc, u32 epnum);
|
||||
|
||||
static const char *dwc3_ep0_state_string(enum dwc3_ep0_state state)
|
||||
{
|
||||
|
@ -125,6 +125,8 @@ static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma,
|
|||
static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep,
|
||||
struct dwc3_request *req)
|
||||
{
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
u32 type;
|
||||
int ret = 0;
|
||||
|
||||
req->request.actual = 0;
|
||||
|
@ -143,9 +145,7 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep,
|
|||
* IRQ we were waiting for is long gone.
|
||||
*/
|
||||
if (dep->flags & DWC3_EP_PENDING_REQUEST) {
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
unsigned direction;
|
||||
u32 type;
|
||||
|
||||
direction = !!(dep->flags & DWC3_EP0_DIR_IN);
|
||||
|
||||
|
@ -165,6 +165,13 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep,
|
|||
req->request.dma, req->request.length, type);
|
||||
dep->flags &= ~(DWC3_EP_PENDING_REQUEST |
|
||||
DWC3_EP0_DIR_IN);
|
||||
} else if (dwc->delayed_status) {
|
||||
dwc->delayed_status = false;
|
||||
|
||||
if (dwc->ep0state == EP0_STATUS_PHASE)
|
||||
dwc3_ep0_do_control_status(dwc, 1);
|
||||
else
|
||||
dev_dbg(dwc->dev, "too early for delayed status\n");
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
@ -190,9 +197,7 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
|
|||
}
|
||||
|
||||
/* we share one TRB for ep0/1 */
|
||||
if (!list_empty(&dwc->eps[0]->request_list) ||
|
||||
!list_empty(&dwc->eps[1]->request_list) ||
|
||||
dwc->ep0_status_pending) {
|
||||
if (!list_empty(&dep->request_list)) {
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
@ -214,8 +219,9 @@ static void dwc3_ep0_stall_and_restart(struct dwc3 *dwc)
|
|||
struct dwc3_ep *dep = dwc->eps[0];
|
||||
|
||||
/* stall is always issued on EP0 */
|
||||
__dwc3_gadget_ep_set_halt(dwc->eps[0], 1);
|
||||
dwc->eps[0]->flags = DWC3_EP_ENABLED;
|
||||
__dwc3_gadget_ep_set_halt(dep, 1);
|
||||
dep->flags = DWC3_EP_ENABLED;
|
||||
dwc->delayed_status = false;
|
||||
|
||||
if (!list_empty(&dep->request_list)) {
|
||||
struct dwc3_request *req;
|
||||
|
@ -254,17 +260,14 @@ static struct dwc3_ep *dwc3_wIndex_to_dep(struct dwc3 *dwc, __le16 wIndex_le)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static void dwc3_ep0_send_status_response(struct dwc3 *dwc)
|
||||
static void dwc3_ep0_status_cmpl(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
dwc3_ep0_start_trans(dwc, 1, dwc->setup_buf_addr,
|
||||
dwc->ep0_usb_req.length,
|
||||
DWC3_TRBCTL_CONTROL_DATA);
|
||||
}
|
||||
|
||||
/*
|
||||
* ch 9.4.5
|
||||
*/
|
||||
static int dwc3_ep0_handle_status(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
|
||||
static int dwc3_ep0_handle_status(struct dwc3 *dwc,
|
||||
struct usb_ctrlrequest *ctrl)
|
||||
{
|
||||
struct dwc3_ep *dep;
|
||||
u32 recip;
|
||||
|
@ -291,7 +294,7 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl
|
|||
case USB_RECIP_ENDPOINT:
|
||||
dep = dwc3_wIndex_to_dep(dwc, ctrl->wIndex);
|
||||
if (!dep)
|
||||
return -EINVAL;
|
||||
return -EINVAL;
|
||||
|
||||
if (dep->flags & DWC3_EP_STALL)
|
||||
usb_status = 1 << USB_ENDPOINT_HALT;
|
||||
|
@ -302,10 +305,14 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl
|
|||
|
||||
response_pkt = (__le16 *) dwc->setup_buf;
|
||||
*response_pkt = cpu_to_le16(usb_status);
|
||||
dwc->ep0_usb_req.length = sizeof(*response_pkt);
|
||||
dwc->ep0_status_pending = 1;
|
||||
|
||||
return 0;
|
||||
dep = dwc->eps[0];
|
||||
dwc->ep0_usb_req.dep = dep;
|
||||
dwc->ep0_usb_req.request.length = sizeof(*response_pkt);
|
||||
dwc->ep0_usb_req.request.dma = dwc->setup_buf_addr;
|
||||
dwc->ep0_usb_req.request.complete = dwc3_ep0_status_cmpl;
|
||||
|
||||
return __dwc3_gadget_ep0_queue(dep, &dwc->ep0_usb_req);
|
||||
}
|
||||
|
||||
static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
|
||||
|
@ -396,8 +403,7 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
|
|||
case USB_RECIP_ENDPOINT:
|
||||
switch (wValue) {
|
||||
case USB_ENDPOINT_HALT:
|
||||
|
||||
dep = dwc3_wIndex_to_dep(dwc, ctrl->wIndex);
|
||||
dep = dwc3_wIndex_to_dep(dwc, wIndex);
|
||||
if (!dep)
|
||||
return -EINVAL;
|
||||
ret = __dwc3_gadget_ep_set_halt(dep, set);
|
||||
|
@ -422,8 +428,15 @@ static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
|
|||
u32 reg;
|
||||
|
||||
addr = le16_to_cpu(ctrl->wValue);
|
||||
if (addr > 127)
|
||||
if (addr > 127) {
|
||||
dev_dbg(dwc->dev, "invalid device address %d\n", addr);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (dwc->dev_state == DWC3_CONFIGURED_STATE) {
|
||||
dev_dbg(dwc->dev, "trying to set address when configured\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCFG);
|
||||
reg &= ~(DWC3_DCFG_DEVADDR_MASK);
|
||||
|
@ -473,8 +486,10 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
|
|||
if (!cfg)
|
||||
dwc->dev_state = DWC3_ADDRESS_STATE;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dwc3_ep0_std_request(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
|
||||
|
@ -537,6 +552,9 @@ static void dwc3_ep0_inspect_setup(struct dwc3 *dwc,
|
|||
else
|
||||
ret = dwc3_ep0_delegate_req(dwc, ctrl);
|
||||
|
||||
if (ret == USB_GADGET_DELAYED_STATUS)
|
||||
dwc->delayed_status = true;
|
||||
|
||||
if (ret >= 0)
|
||||
return;
|
||||
|
||||
|
@ -550,27 +568,21 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
|
|||
struct dwc3_request *r = NULL;
|
||||
struct usb_request *ur;
|
||||
struct dwc3_trb trb;
|
||||
struct dwc3_ep *dep;
|
||||
struct dwc3_ep *ep0;
|
||||
u32 transferred;
|
||||
u8 epnum;
|
||||
|
||||
epnum = event->endpoint_number;
|
||||
dep = dwc->eps[epnum];
|
||||
ep0 = dwc->eps[0];
|
||||
|
||||
dwc->ep0_next_event = DWC3_EP0_NRDY_STATUS;
|
||||
|
||||
if (!dwc->ep0_status_pending) {
|
||||
r = next_request(&dwc->eps[0]->request_list);
|
||||
ur = &r->request;
|
||||
} else {
|
||||
ur = &dwc->ep0_usb_req;
|
||||
dwc->ep0_status_pending = 0;
|
||||
}
|
||||
r = next_request(&ep0->request_list);
|
||||
ur = &r->request;
|
||||
|
||||
dwc3_trb_to_nat(dwc->ep0_trb, &trb);
|
||||
|
||||
if (dwc->ep0_bounced) {
|
||||
struct dwc3_ep *ep0 = dwc->eps[0];
|
||||
|
||||
transferred = min_t(u32, ur->length,
|
||||
ep0->endpoint.maxpacket - trb.length);
|
||||
|
@ -591,7 +603,7 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
|
|||
* seems to be case when req.length > maxpacket. Could it be?
|
||||
*/
|
||||
if (r)
|
||||
dwc3_gadget_giveback(dep, r, 0);
|
||||
dwc3_gadget_giveback(ep0, r, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -619,6 +631,7 @@ static void dwc3_ep0_xfer_complete(struct dwc3 *dwc,
|
|||
struct dwc3_ep *dep = dwc->eps[event->endpoint_number];
|
||||
|
||||
dep->flags &= ~DWC3_EP_BUSY;
|
||||
dwc->setup_packet_pending = false;
|
||||
|
||||
switch (dwc->ep0state) {
|
||||
case EP0_SETUP_PHASE:
|
||||
|
@ -643,7 +656,6 @@ static void dwc3_ep0_xfer_complete(struct dwc3 *dwc,
|
|||
static void dwc3_ep0_do_control_setup(struct dwc3 *dwc,
|
||||
const struct dwc3_event_depevt *event)
|
||||
{
|
||||
dwc->ep0state = EP0_SETUP_PHASE;
|
||||
dwc3_ep0_out_start(dwc);
|
||||
}
|
||||
|
||||
|
@ -655,12 +667,6 @@ static void dwc3_ep0_do_control_data(struct dwc3 *dwc,
|
|||
int ret;
|
||||
|
||||
dep = dwc->eps[0];
|
||||
dwc->ep0state = EP0_DATA_PHASE;
|
||||
|
||||
if (dwc->ep0_status_pending) {
|
||||
dwc3_ep0_send_status_response(dwc);
|
||||
return;
|
||||
}
|
||||
|
||||
if (list_empty(&dep->request_list)) {
|
||||
dev_vdbg(dwc->dev, "pending request for EP0 Data phase\n");
|
||||
|
@ -674,7 +680,6 @@ static void dwc3_ep0_do_control_data(struct dwc3 *dwc,
|
|||
req = next_request(&dep->request_list);
|
||||
req->direction = !!event->endpoint_number;
|
||||
|
||||
dwc->ep0state = EP0_DATA_PHASE;
|
||||
if (req->request.length == 0) {
|
||||
ret = dwc3_ep0_start_trans(dwc, event->endpoint_number,
|
||||
dwc->ctrl_req_addr, 0,
|
||||
|
@ -706,35 +711,79 @@ static void dwc3_ep0_do_control_data(struct dwc3 *dwc,
|
|||
WARN_ON(ret < 0);
|
||||
}
|
||||
|
||||
static void dwc3_ep0_do_control_status(struct dwc3 *dwc,
|
||||
const struct dwc3_event_depevt *event)
|
||||
static int dwc3_ep0_start_control_status(struct dwc3_ep *dep)
|
||||
{
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
u32 type;
|
||||
int ret;
|
||||
|
||||
dwc->ep0state = EP0_STATUS_PHASE;
|
||||
|
||||
type = dwc->three_stage_setup ? DWC3_TRBCTL_CONTROL_STATUS3
|
||||
: DWC3_TRBCTL_CONTROL_STATUS2;
|
||||
|
||||
ret = dwc3_ep0_start_trans(dwc, event->endpoint_number,
|
||||
return dwc3_ep0_start_trans(dwc, dep->number,
|
||||
dwc->ctrl_req_addr, 0, type);
|
||||
}
|
||||
|
||||
WARN_ON(ret < 0);
|
||||
static void dwc3_ep0_do_control_status(struct dwc3 *dwc, u32 epnum)
|
||||
{
|
||||
struct dwc3_ep *dep = dwc->eps[epnum];
|
||||
|
||||
WARN_ON(dwc3_ep0_start_control_status(dep));
|
||||
}
|
||||
|
||||
static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
|
||||
const struct dwc3_event_depevt *event)
|
||||
{
|
||||
dwc->setup_packet_pending = true;
|
||||
|
||||
/*
|
||||
* This part is very tricky: If we has just handled
|
||||
* XferNotReady(Setup) and we're now expecting a
|
||||
* XferComplete but, instead, we receive another
|
||||
* XferNotReady(Setup), we should STALL and restart
|
||||
* the state machine.
|
||||
*
|
||||
* In all other cases, we just continue waiting
|
||||
* for the XferComplete event.
|
||||
*
|
||||
* We are a little bit unsafe here because we're
|
||||
* not trying to ensure that last event was, indeed,
|
||||
* XferNotReady(Setup).
|
||||
*
|
||||
* Still, we don't expect any condition where that
|
||||
* should happen and, even if it does, it would be
|
||||
* another error condition.
|
||||
*/
|
||||
if (dwc->ep0_next_event == DWC3_EP0_COMPLETE) {
|
||||
switch (event->status) {
|
||||
case DEPEVT_STATUS_CONTROL_SETUP:
|
||||
dev_vdbg(dwc->dev, "Unexpected XferNotReady(Setup)\n");
|
||||
dwc3_ep0_stall_and_restart(dwc);
|
||||
break;
|
||||
case DEPEVT_STATUS_CONTROL_DATA:
|
||||
/* FALLTHROUGH */
|
||||
case DEPEVT_STATUS_CONTROL_STATUS:
|
||||
/* FALLTHROUGH */
|
||||
default:
|
||||
dev_vdbg(dwc->dev, "waiting for XferComplete\n");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
switch (event->status) {
|
||||
case DEPEVT_STATUS_CONTROL_SETUP:
|
||||
dev_vdbg(dwc->dev, "Control Setup\n");
|
||||
|
||||
dwc->ep0state = EP0_SETUP_PHASE;
|
||||
|
||||
dwc3_ep0_do_control_setup(dwc, event);
|
||||
break;
|
||||
|
||||
case DEPEVT_STATUS_CONTROL_DATA:
|
||||
dev_vdbg(dwc->dev, "Control Data\n");
|
||||
|
||||
dwc->ep0state = EP0_DATA_PHASE;
|
||||
|
||||
if (dwc->ep0_next_event != DWC3_EP0_NRDY_DATA) {
|
||||
dev_vdbg(dwc->dev, "Expected %d got %d\n",
|
||||
dwc->ep0_next_event,
|
||||
|
@ -764,6 +813,8 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
|
|||
case DEPEVT_STATUS_CONTROL_STATUS:
|
||||
dev_vdbg(dwc->dev, "Control Status\n");
|
||||
|
||||
dwc->ep0state = EP0_STATUS_PHASE;
|
||||
|
||||
if (dwc->ep0_next_event != DWC3_EP0_NRDY_STATUS) {
|
||||
dev_vdbg(dwc->dev, "Expected %d got %d\n",
|
||||
dwc->ep0_next_event,
|
||||
|
@ -772,12 +823,19 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
|
|||
dwc3_ep0_stall_and_restart(dwc);
|
||||
return;
|
||||
}
|
||||
dwc3_ep0_do_control_status(dwc, event);
|
||||
|
||||
if (dwc->delayed_status) {
|
||||
WARN_ON_ONCE(event->endpoint_number != 1);
|
||||
dev_vdbg(dwc->dev, "Mass Storage delayed status\n");
|
||||
return;
|
||||
}
|
||||
|
||||
dwc3_ep0_do_control_status(dwc, event->endpoint_number);
|
||||
}
|
||||
}
|
||||
|
||||
void dwc3_ep0_interrupt(struct dwc3 *dwc,
|
||||
const const struct dwc3_event_depevt *event)
|
||||
const struct dwc3_event_depevt *event)
|
||||
{
|
||||
u8 epnum = event->endpoint_number;
|
||||
|
||||
|
|
|
@ -65,6 +65,22 @@ void dwc3_map_buffer_to_dma(struct dwc3_request *req)
|
|||
return;
|
||||
}
|
||||
|
||||
if (req->request.num_sgs) {
|
||||
int mapped;
|
||||
|
||||
mapped = dma_map_sg(dwc->dev, req->request.sg,
|
||||
req->request.num_sgs,
|
||||
req->direction ? DMA_TO_DEVICE
|
||||
: DMA_FROM_DEVICE);
|
||||
if (mapped < 0) {
|
||||
dev_err(dwc->dev, "failed to map SGs\n");
|
||||
return;
|
||||
}
|
||||
|
||||
req->request.num_mapped_sgs = mapped;
|
||||
return;
|
||||
}
|
||||
|
||||
if (req->request.dma == DMA_ADDR_INVALID) {
|
||||
req->request.dma = dma_map_single(dwc->dev, req->request.buf,
|
||||
req->request.length, req->direction
|
||||
|
@ -82,6 +98,17 @@ void dwc3_unmap_buffer_from_dma(struct dwc3_request *req)
|
|||
return;
|
||||
}
|
||||
|
||||
if (req->request.num_mapped_sgs) {
|
||||
req->request.dma = DMA_ADDR_INVALID;
|
||||
dma_unmap_sg(dwc->dev, req->request.sg,
|
||||
req->request.num_sgs,
|
||||
req->direction ? DMA_TO_DEVICE
|
||||
: DMA_FROM_DEVICE);
|
||||
|
||||
req->request.num_mapped_sgs = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (req->mapped) {
|
||||
dma_unmap_single(dwc->dev, req->request.dma,
|
||||
req->request.length, req->direction
|
||||
|
@ -97,7 +124,11 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
|
|||
struct dwc3 *dwc = dep->dwc;
|
||||
|
||||
if (req->queued) {
|
||||
dep->busy_slot++;
|
||||
if (req->request.num_mapped_sgs)
|
||||
dep->busy_slot += req->request.num_mapped_sgs;
|
||||
else
|
||||
dep->busy_slot++;
|
||||
|
||||
/*
|
||||
* Skip LINK TRB. We can't use req->trb and check for
|
||||
* DWC3_TRBCTL_LINK_TRB because it points the TRB we just
|
||||
|
@ -108,6 +139,7 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
|
|||
dep->busy_slot++;
|
||||
}
|
||||
list_del(&req->list);
|
||||
req->trb = NULL;
|
||||
|
||||
if (req->request.status == -EINPROGRESS)
|
||||
req->request.status = status;
|
||||
|
@ -251,7 +283,8 @@ static int dwc3_gadget_start_config(struct dwc3 *dwc, struct dwc3_ep *dep)
|
|||
}
|
||||
|
||||
static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep,
|
||||
const struct usb_endpoint_descriptor *desc)
|
||||
const struct usb_endpoint_descriptor *desc,
|
||||
const struct usb_ss_ep_comp_descriptor *comp_desc)
|
||||
{
|
||||
struct dwc3_gadget_ep_cmd_params params;
|
||||
|
||||
|
@ -264,7 +297,7 @@ static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep,
|
|||
params.param1 = DWC3_DEPCFG_XFER_COMPLETE_EN
|
||||
| DWC3_DEPCFG_XFER_NOT_READY_EN;
|
||||
|
||||
if (usb_endpoint_xfer_bulk(desc) && dep->endpoint.max_streams) {
|
||||
if (usb_ss_max_streams(comp_desc) && usb_endpoint_xfer_bulk(desc)) {
|
||||
params.param1 |= DWC3_DEPCFG_STREAM_CAPABLE
|
||||
| DWC3_DEPCFG_STREAM_EVENT_EN;
|
||||
dep->stream_capable = true;
|
||||
|
@ -317,7 +350,8 @@ static int dwc3_gadget_set_xfer_resource(struct dwc3 *dwc, struct dwc3_ep *dep)
|
|||
* Caller should take care of locking
|
||||
*/
|
||||
static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
|
||||
const struct usb_endpoint_descriptor *desc)
|
||||
const struct usb_endpoint_descriptor *desc,
|
||||
const struct usb_ss_ep_comp_descriptor *comp_desc)
|
||||
{
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
u32 reg;
|
||||
|
@ -329,7 +363,7 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
|
|||
return ret;
|
||||
}
|
||||
|
||||
ret = dwc3_gadget_set_ep_config(dwc, dep, desc);
|
||||
ret = dwc3_gadget_set_ep_config(dwc, dep, desc, comp_desc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -343,6 +377,7 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
|
|||
return ret;
|
||||
|
||||
dep->desc = desc;
|
||||
dep->comp_desc = comp_desc;
|
||||
dep->type = usb_endpoint_type(desc);
|
||||
dep->flags |= DWC3_EP_ENABLED;
|
||||
|
||||
|
@ -405,6 +440,7 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep)
|
|||
|
||||
dep->stream_capable = false;
|
||||
dep->desc = NULL;
|
||||
dep->comp_desc = NULL;
|
||||
dep->type = 0;
|
||||
dep->flags = 0;
|
||||
|
||||
|
@ -473,7 +509,7 @@ static int dwc3_gadget_ep_enable(struct usb_ep *ep,
|
|||
dev_vdbg(dwc->dev, "Enabling %s\n", dep->name);
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
ret = __dwc3_gadget_ep_enable(dep, desc);
|
||||
ret = __dwc3_gadget_ep_enable(dep, desc, ep->comp_desc);
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
return ret;
|
||||
|
@ -539,6 +575,85 @@ static void dwc3_gadget_ep_free_request(struct usb_ep *ep,
|
|||
kfree(req);
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc3_prepare_one_trb - setup one TRB from one request
|
||||
* @dep: endpoint for which this request is prepared
|
||||
* @req: dwc3_request pointer
|
||||
*/
|
||||
static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
|
||||
struct dwc3_request *req, dma_addr_t dma,
|
||||
unsigned length, unsigned last, unsigned chain)
|
||||
{
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
struct dwc3_trb_hw *trb_hw;
|
||||
struct dwc3_trb trb;
|
||||
|
||||
unsigned int cur_slot;
|
||||
|
||||
dev_vdbg(dwc->dev, "%s: req %p dma %08llx length %d%s%s\n",
|
||||
dep->name, req, (unsigned long long) dma,
|
||||
length, last ? " last" : "",
|
||||
chain ? " chain" : "");
|
||||
|
||||
trb_hw = &dep->trb_pool[dep->free_slot & DWC3_TRB_MASK];
|
||||
cur_slot = dep->free_slot;
|
||||
dep->free_slot++;
|
||||
|
||||
/* Skip the LINK-TRB on ISOC */
|
||||
if (((cur_slot & DWC3_TRB_MASK) == DWC3_TRB_NUM - 1) &&
|
||||
usb_endpoint_xfer_isoc(dep->desc))
|
||||
return;
|
||||
|
||||
memset(&trb, 0, sizeof(trb));
|
||||
if (!req->trb) {
|
||||
dwc3_gadget_move_request_queued(req);
|
||||
req->trb = trb_hw;
|
||||
req->trb_dma = dwc3_trb_dma_offset(dep, trb_hw);
|
||||
}
|
||||
|
||||
if (usb_endpoint_xfer_isoc(dep->desc)) {
|
||||
trb.isp_imi = true;
|
||||
trb.csp = true;
|
||||
} else {
|
||||
trb.chn = chain;
|
||||
trb.lst = last;
|
||||
}
|
||||
|
||||
if (usb_endpoint_xfer_bulk(dep->desc) && dep->stream_capable)
|
||||
trb.sid_sofn = req->request.stream_id;
|
||||
|
||||
switch (usb_endpoint_type(dep->desc)) {
|
||||
case USB_ENDPOINT_XFER_CONTROL:
|
||||
trb.trbctl = DWC3_TRBCTL_CONTROL_SETUP;
|
||||
break;
|
||||
|
||||
case USB_ENDPOINT_XFER_ISOC:
|
||||
trb.trbctl = DWC3_TRBCTL_ISOCHRONOUS_FIRST;
|
||||
|
||||
/* IOC every DWC3_TRB_NUM / 4 so we can refill */
|
||||
if (!(cur_slot % (DWC3_TRB_NUM / 4)))
|
||||
trb.ioc = last;
|
||||
break;
|
||||
|
||||
case USB_ENDPOINT_XFER_BULK:
|
||||
case USB_ENDPOINT_XFER_INT:
|
||||
trb.trbctl = DWC3_TRBCTL_NORMAL;
|
||||
break;
|
||||
default:
|
||||
/*
|
||||
* This is only possible with faulty memory because we
|
||||
* checked it already :)
|
||||
*/
|
||||
BUG();
|
||||
}
|
||||
|
||||
trb.length = length;
|
||||
trb.bplh = dma;
|
||||
trb.hwo = true;
|
||||
|
||||
dwc3_trb_to_hw(&trb, trb_hw);
|
||||
}
|
||||
|
||||
/*
|
||||
* dwc3_prepare_trbs - setup TRBs from requests
|
||||
* @dep: endpoint for which requests are being prepared
|
||||
|
@ -548,18 +663,17 @@ static void dwc3_gadget_ep_free_request(struct usb_ep *ep,
|
|||
* transfers. The functions returns once there are not more TRBs available or
|
||||
* it run out of requests.
|
||||
*/
|
||||
static struct dwc3_request *dwc3_prepare_trbs(struct dwc3_ep *dep,
|
||||
bool starting)
|
||||
static void dwc3_prepare_trbs(struct dwc3_ep *dep, bool starting)
|
||||
{
|
||||
struct dwc3_request *req, *n, *ret = NULL;
|
||||
struct dwc3_trb_hw *trb_hw;
|
||||
struct dwc3_trb trb;
|
||||
struct dwc3_request *req, *n;
|
||||
u32 trbs_left;
|
||||
unsigned int last_one = 0;
|
||||
|
||||
BUILD_BUG_ON_NOT_POWER_OF_2(DWC3_TRB_NUM);
|
||||
|
||||
/* the first request must not be queued */
|
||||
trbs_left = (dep->busy_slot - dep->free_slot) & DWC3_TRB_MASK;
|
||||
|
||||
/*
|
||||
* if busy & slot are equal than it is either full or empty. If we are
|
||||
* starting to proceed requests then we are empty. Otherwise we ar
|
||||
|
@ -567,7 +681,7 @@ static struct dwc3_request *dwc3_prepare_trbs(struct dwc3_ep *dep,
|
|||
*/
|
||||
if (!trbs_left) {
|
||||
if (!starting)
|
||||
return NULL;
|
||||
return;
|
||||
trbs_left = DWC3_TRB_NUM;
|
||||
/*
|
||||
* In case we start from scratch, we queue the ISOC requests
|
||||
|
@ -591,94 +705,62 @@ static struct dwc3_request *dwc3_prepare_trbs(struct dwc3_ep *dep,
|
|||
|
||||
/* The last TRB is a link TRB, not used for xfer */
|
||||
if ((trbs_left <= 1) && usb_endpoint_xfer_isoc(dep->desc))
|
||||
return NULL;
|
||||
return;
|
||||
|
||||
list_for_each_entry_safe(req, n, &dep->request_list, list) {
|
||||
unsigned int last_one = 0;
|
||||
unsigned int cur_slot;
|
||||
unsigned length;
|
||||
dma_addr_t dma;
|
||||
|
||||
trb_hw = &dep->trb_pool[dep->free_slot & DWC3_TRB_MASK];
|
||||
cur_slot = dep->free_slot;
|
||||
dep->free_slot++;
|
||||
if (req->request.num_mapped_sgs > 0) {
|
||||
struct usb_request *request = &req->request;
|
||||
struct scatterlist *sg = request->sg;
|
||||
struct scatterlist *s;
|
||||
int i;
|
||||
|
||||
/* Skip the LINK-TRB on ISOC */
|
||||
if (((cur_slot & DWC3_TRB_MASK) == DWC3_TRB_NUM - 1) &&
|
||||
usb_endpoint_xfer_isoc(dep->desc))
|
||||
continue;
|
||||
for_each_sg(sg, s, request->num_mapped_sgs, i) {
|
||||
unsigned chain = true;
|
||||
|
||||
dwc3_gadget_move_request_queued(req);
|
||||
memset(&trb, 0, sizeof(trb));
|
||||
trbs_left--;
|
||||
length = sg_dma_len(s);
|
||||
dma = sg_dma_address(s);
|
||||
|
||||
/* Is our TRB pool empty? */
|
||||
if (!trbs_left)
|
||||
last_one = 1;
|
||||
/* Is this the last request? */
|
||||
if (list_empty(&dep->request_list))
|
||||
last_one = 1;
|
||||
if (i == (request->num_mapped_sgs - 1)
|
||||
|| sg_is_last(s)) {
|
||||
last_one = true;
|
||||
chain = false;
|
||||
}
|
||||
|
||||
/*
|
||||
* FIXME we shouldn't need to set LST bit always but we are
|
||||
* facing some weird problem with the Hardware where it doesn't
|
||||
* complete even though it has been previously started.
|
||||
*
|
||||
* While we're debugging the problem, as a workaround to
|
||||
* multiple TRBs handling, use only one TRB at a time.
|
||||
*/
|
||||
last_one = 1;
|
||||
trbs_left--;
|
||||
if (!trbs_left)
|
||||
last_one = true;
|
||||
|
||||
req->trb = trb_hw;
|
||||
if (!ret)
|
||||
ret = req;
|
||||
if (last_one)
|
||||
chain = false;
|
||||
|
||||
trb.bplh = req->request.dma;
|
||||
dwc3_prepare_one_trb(dep, req, dma, length,
|
||||
last_one, chain);
|
||||
|
||||
if (usb_endpoint_xfer_isoc(dep->desc)) {
|
||||
trb.isp_imi = true;
|
||||
trb.csp = true;
|
||||
if (last_one)
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
trb.lst = last_one;
|
||||
dma = req->request.dma;
|
||||
length = req->request.length;
|
||||
trbs_left--;
|
||||
|
||||
if (!trbs_left)
|
||||
last_one = 1;
|
||||
|
||||
/* Is this the last request? */
|
||||
if (list_is_last(&req->list, &dep->request_list))
|
||||
last_one = 1;
|
||||
|
||||
dwc3_prepare_one_trb(dep, req, dma, length,
|
||||
last_one, false);
|
||||
|
||||
if (last_one)
|
||||
break;
|
||||
}
|
||||
|
||||
if (usb_endpoint_xfer_bulk(dep->desc) && dep->stream_capable)
|
||||
trb.sid_sofn = req->request.stream_id;
|
||||
|
||||
switch (usb_endpoint_type(dep->desc)) {
|
||||
case USB_ENDPOINT_XFER_CONTROL:
|
||||
trb.trbctl = DWC3_TRBCTL_CONTROL_SETUP;
|
||||
break;
|
||||
|
||||
case USB_ENDPOINT_XFER_ISOC:
|
||||
trb.trbctl = DWC3_TRBCTL_ISOCHRONOUS_FIRST;
|
||||
|
||||
/* IOC every DWC3_TRB_NUM / 4 so we can refill */
|
||||
if (!(cur_slot % (DWC3_TRB_NUM / 4)))
|
||||
trb.ioc = last_one;
|
||||
break;
|
||||
|
||||
case USB_ENDPOINT_XFER_BULK:
|
||||
case USB_ENDPOINT_XFER_INT:
|
||||
trb.trbctl = DWC3_TRBCTL_NORMAL;
|
||||
break;
|
||||
default:
|
||||
/*
|
||||
* This is only possible with faulty memory because we
|
||||
* checked it already :)
|
||||
*/
|
||||
BUG();
|
||||
}
|
||||
|
||||
trb.length = req->request.length;
|
||||
trb.hwo = true;
|
||||
|
||||
dwc3_trb_to_hw(&trb, trb_hw);
|
||||
req->trb_dma = dwc3_trb_dma_offset(dep, trb_hw);
|
||||
|
||||
if (last_one)
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param,
|
||||
|
@ -707,11 +789,13 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param,
|
|||
/* req points to the first request which will be sent */
|
||||
req = next_request(&dep->req_queued);
|
||||
} else {
|
||||
dwc3_prepare_trbs(dep, start_new);
|
||||
|
||||
/*
|
||||
* req points to the first request where HWO changed
|
||||
* from 0 to 1
|
||||
*/
|
||||
req = dwc3_prepare_trbs(dep, start_new);
|
||||
req = next_request(&dep->req_queued);
|
||||
}
|
||||
if (!req) {
|
||||
dep->flags |= DWC3_EP_PENDING_REQUEST;
|
||||
|
@ -745,8 +829,9 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param,
|
|||
dep->flags |= DWC3_EP_BUSY;
|
||||
dep->res_trans_idx = dwc3_gadget_ep_get_transfer_index(dwc,
|
||||
dep->number);
|
||||
if (!dep->res_trans_idx)
|
||||
printk_once(KERN_ERR "%s() res_trans_idx is invalid\n", __func__);
|
||||
|
||||
WARN_ON_ONCE(!dep->res_trans_idx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1155,35 +1240,9 @@ static int dwc3_gadget_start(struct usb_gadget *g,
|
|||
dwc->gadget_driver = driver;
|
||||
dwc->gadget.dev.driver = &driver->driver;
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GCTL);
|
||||
|
||||
reg &= ~DWC3_GCTL_SCALEDOWN(3);
|
||||
reg &= ~DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_OTG);
|
||||
reg &= ~DWC3_GCTL_DISSCRAMBLE;
|
||||
reg |= DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_DEVICE);
|
||||
|
||||
switch (DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams0)) {
|
||||
case DWC3_GHWPARAMS1_EN_PWROPT_CLK:
|
||||
reg &= ~DWC3_GCTL_DSBLCLKGTNG;
|
||||
break;
|
||||
default:
|
||||
dev_dbg(dwc->dev, "No power optimization available\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* WORKAROUND: DWC3 revisions <1.90a have a bug
|
||||
* when The device fails to connect at SuperSpeed
|
||||
* and falls back to high-speed mode which causes
|
||||
* the device to enter in a Connect/Disconnect loop
|
||||
*/
|
||||
if (dwc->revision < DWC3_REVISION_190A)
|
||||
reg |= DWC3_GCTL_U2RSTECN;
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCFG);
|
||||
reg &= ~(DWC3_DCFG_SPEED_MASK);
|
||||
reg |= DWC3_DCFG_SUPERSPEED;
|
||||
reg |= dwc->maximum_speed;
|
||||
dwc3_writel(dwc->regs, DWC3_DCFG, reg);
|
||||
|
||||
dwc->start_config_issued = false;
|
||||
|
@ -1192,14 +1251,14 @@ static int dwc3_gadget_start(struct usb_gadget *g,
|
|||
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
|
||||
|
||||
dep = dwc->eps[0];
|
||||
ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc);
|
||||
ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL);
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "failed to enable %s\n", dep->name);
|
||||
goto err0;
|
||||
}
|
||||
|
||||
dep = dwc->eps[1];
|
||||
ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc);
|
||||
ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL);
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "failed to enable %s\n", dep->name);
|
||||
goto err1;
|
||||
|
@ -1290,11 +1349,10 @@ static int __devinit dwc3_gadget_init_endpoints(struct dwc3 *dwc)
|
|||
&dwc->gadget.ep_list);
|
||||
|
||||
ret = dwc3_alloc_trb_pool(dep);
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "%s: failed to allocate TRB pool\n", dep->name);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&dep->request_list);
|
||||
INIT_LIST_HEAD(&dep->req_queued);
|
||||
}
|
||||
|
@ -1334,8 +1392,10 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
|
|||
|
||||
do {
|
||||
req = next_request(&dep->req_queued);
|
||||
if (!req)
|
||||
break;
|
||||
if (!req) {
|
||||
WARN_ON_ONCE(1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
dwc3_trb_to_nat(req->trb, &trb);
|
||||
|
||||
|
@ -1400,6 +1460,31 @@ static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc,
|
|||
dep->flags &= ~DWC3_EP_BUSY;
|
||||
dep->res_trans_idx = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* WORKAROUND: This is the 2nd half of U1/U2 -> U0 workaround.
|
||||
* See dwc3_gadget_linksts_change_interrupt() for 1st half.
|
||||
*/
|
||||
if (dwc->revision < DWC3_REVISION_183A) {
|
||||
u32 reg;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < DWC3_ENDPOINTS_NUM; i++) {
|
||||
struct dwc3_ep *dep = dwc->eps[i];
|
||||
|
||||
if (!(dep->flags & DWC3_EP_ENABLED))
|
||||
continue;
|
||||
|
||||
if (!list_empty(&dep->req_queued))
|
||||
return;
|
||||
}
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
reg |= dwc->u1u2;
|
||||
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
|
||||
|
||||
dwc->u1u2 = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void dwc3_gadget_start_isoc(struct dwc3 *dwc,
|
||||
|
@ -1639,6 +1724,7 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)
|
|||
dwc->start_config_issued = false;
|
||||
|
||||
dwc->gadget.speed = USB_SPEED_UNKNOWN;
|
||||
dwc->setup_packet_pending = false;
|
||||
}
|
||||
|
||||
static void dwc3_gadget_usb3_phy_power(struct dwc3 *dwc, int on)
|
||||
|
@ -1675,6 +1761,40 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
|
|||
|
||||
dev_vdbg(dwc->dev, "%s\n", __func__);
|
||||
|
||||
/*
|
||||
* WORKAROUND: DWC3 revisions <1.88a have an issue which
|
||||
* would cause a missing Disconnect Event if there's a
|
||||
* pending Setup Packet in the FIFO.
|
||||
*
|
||||
* There's no suggested workaround on the official Bug
|
||||
* report, which states that "unless the driver/application
|
||||
* is doing any special handling of a disconnect event,
|
||||
* there is no functional issue".
|
||||
*
|
||||
* Unfortunately, it turns out that we _do_ some special
|
||||
* handling of a disconnect event, namely complete all
|
||||
* pending transfers, notify gadget driver of the
|
||||
* disconnection, and so on.
|
||||
*
|
||||
* Our suggested workaround is to follow the Disconnect
|
||||
* Event steps here, instead, based on a setup_packet_pending
|
||||
* flag. Such flag gets set whenever we have a XferNotReady
|
||||
* event on EP0 and gets cleared on XferComplete for the
|
||||
* same endpoint.
|
||||
*
|
||||
* Refers to:
|
||||
*
|
||||
* STAR#9000466709: RTL: Device : Disconnect event not
|
||||
* generated if setup packet pending in FIFO
|
||||
*/
|
||||
if (dwc->revision < DWC3_REVISION_188A) {
|
||||
if (dwc->setup_packet_pending)
|
||||
dwc3_gadget_disconnect_interrupt(dwc);
|
||||
}
|
||||
|
||||
/* after reset -> Default State */
|
||||
dwc->dev_state = DWC3_DEFAULT_STATE;
|
||||
|
||||
/* Enable PHYs */
|
||||
dwc3_gadget_usb2_phy_power(dwc, true);
|
||||
dwc3_gadget_usb3_phy_power(dwc, true);
|
||||
|
@ -1755,6 +1875,22 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
|
|||
|
||||
switch (speed) {
|
||||
case DWC3_DCFG_SUPERSPEED:
|
||||
/*
|
||||
* WORKAROUND: DWC3 revisions <1.90a have an issue which
|
||||
* would cause a missing USB3 Reset event.
|
||||
*
|
||||
* In such situations, we should force a USB3 Reset
|
||||
* event by calling our dwc3_gadget_reset_interrupt()
|
||||
* routine.
|
||||
*
|
||||
* Refers to:
|
||||
*
|
||||
* STAR#9000483510: RTL: SS : USB3 reset event may
|
||||
* not be generated always when the link enters poll
|
||||
*/
|
||||
if (dwc->revision < DWC3_REVISION_190A)
|
||||
dwc3_gadget_reset_interrupt(dwc);
|
||||
|
||||
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
|
||||
dwc->gadget.ep0->maxpacket = 512;
|
||||
dwc->gadget.speed = USB_SPEED_SUPER;
|
||||
|
@ -1781,14 +1917,14 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
|
|||
dwc3_gadget_disable_phy(dwc, dwc->gadget.speed);
|
||||
|
||||
dep = dwc->eps[0];
|
||||
ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc);
|
||||
ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL);
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "failed to enable %s\n", dep->name);
|
||||
return;
|
||||
}
|
||||
|
||||
dep = dwc->eps[1];
|
||||
ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc);
|
||||
ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL);
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "failed to enable %s\n", dep->name);
|
||||
return;
|
||||
|
@ -1818,8 +1954,55 @@ static void dwc3_gadget_wakeup_interrupt(struct dwc3 *dwc)
|
|||
static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
|
||||
unsigned int evtinfo)
|
||||
{
|
||||
/* The fith bit says SuperSpeed yes or no. */
|
||||
dwc->link_state = evtinfo & DWC3_LINK_STATE_MASK;
|
||||
enum dwc3_link_state next = evtinfo & DWC3_LINK_STATE_MASK;
|
||||
|
||||
/*
|
||||
* WORKAROUND: DWC3 Revisions <1.83a have an issue which, depending
|
||||
* on the link partner, the USB session might do multiple entry/exit
|
||||
* of low power states before a transfer takes place.
|
||||
*
|
||||
* Due to this problem, we might experience lower throughput. The
|
||||
* suggested workaround is to disable DCTL[12:9] bits if we're
|
||||
* transitioning from U1/U2 to U0 and enable those bits again
|
||||
* after a transfer completes and there are no pending transfers
|
||||
* on any of the enabled endpoints.
|
||||
*
|
||||
* This is the first half of that workaround.
|
||||
*
|
||||
* Refers to:
|
||||
*
|
||||
* STAR#9000446952: RTL: Device SS : if U1/U2 ->U0 takes >128us
|
||||
* core send LGO_Ux entering U0
|
||||
*/
|
||||
if (dwc->revision < DWC3_REVISION_183A) {
|
||||
if (next == DWC3_LINK_STATE_U0) {
|
||||
u32 u1u2;
|
||||
u32 reg;
|
||||
|
||||
switch (dwc->link_state) {
|
||||
case DWC3_LINK_STATE_U1:
|
||||
case DWC3_LINK_STATE_U2:
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
u1u2 = reg & (DWC3_DCTL_INITU2ENA
|
||||
| DWC3_DCTL_ACCEPTU2ENA
|
||||
| DWC3_DCTL_INITU1ENA
|
||||
| DWC3_DCTL_ACCEPTU1ENA);
|
||||
|
||||
if (!dwc->u1u2)
|
||||
dwc->u1u2 = reg & u1u2;
|
||||
|
||||
reg &= ~u1u2;
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
|
||||
break;
|
||||
default:
|
||||
/* do nothing */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dwc->link_state = next;
|
||||
|
||||
dev_vdbg(dwc->dev, "%s link %d\n", __func__, dwc->link_state);
|
||||
}
|
||||
|
@ -1925,7 +2108,7 @@ static irqreturn_t dwc3_interrupt(int irq, void *_dwc)
|
|||
|
||||
spin_lock(&dwc->lock);
|
||||
|
||||
for (i = 0; i < DWC3_EVENT_BUFFERS_NUM; i++) {
|
||||
for (i = 0; i < dwc->num_event_buffers; i++) {
|
||||
irqreturn_t status;
|
||||
|
||||
status = dwc3_process_event_buf(dwc, i);
|
||||
|
@ -1986,9 +2169,10 @@ int __devinit dwc3_gadget_init(struct dwc3 *dwc)
|
|||
dev_set_name(&dwc->gadget.dev, "gadget");
|
||||
|
||||
dwc->gadget.ops = &dwc3_gadget_ops;
|
||||
dwc->gadget.is_dualspeed = true;
|
||||
dwc->gadget.max_speed = USB_SPEED_SUPER;
|
||||
dwc->gadget.speed = USB_SPEED_UNKNOWN;
|
||||
dwc->gadget.dev.parent = dwc->dev;
|
||||
dwc->gadget.sg_supported = true;
|
||||
|
||||
dma_set_coherent_mask(&dwc->gadget.dev, dwc->dev->coherent_dma_mask);
|
||||
|
||||
|
@ -2076,7 +2260,6 @@ err0:
|
|||
void dwc3_gadget_exit(struct dwc3 *dwc)
|
||||
{
|
||||
int irq;
|
||||
int i;
|
||||
|
||||
usb_del_gadget_udc(&dwc->gadget);
|
||||
irq = platform_get_irq(to_platform_device(dwc->dev), 0);
|
||||
|
@ -2084,9 +2267,6 @@ void dwc3_gadget_exit(struct dwc3 *dwc)
|
|||
dwc3_writel(dwc->regs, DWC3_DEVTEN, 0x00);
|
||||
free_irq(irq, dwc);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(dwc->eps); i++)
|
||||
__dwc3_gadget_ep_disable(dwc->eps[i]);
|
||||
|
||||
dwc3_gadget_free_endpoints(dwc);
|
||||
|
||||
dma_free_coherent(dwc->dev, 512, dwc->ep0_bounce,
|
||||
|
|
|
@ -79,19 +79,6 @@ struct dwc3_gadget_ep_cmd_params {
|
|||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
struct dwc3_request {
|
||||
struct usb_request request;
|
||||
struct list_head list;
|
||||
struct dwc3_ep *dep;
|
||||
|
||||
u8 epnum;
|
||||
struct dwc3_trb_hw *trb;
|
||||
dma_addr_t trb_dma;
|
||||
|
||||
unsigned direction:1;
|
||||
unsigned mapped:1;
|
||||
unsigned queued:1;
|
||||
};
|
||||
#define to_dwc3_request(r) (container_of(r, struct dwc3_request, request))
|
||||
|
||||
static inline struct dwc3_request *next_request(struct list_head *list)
|
||||
|
@ -110,23 +97,11 @@ static inline void dwc3_gadget_move_request_queued(struct dwc3_request *req)
|
|||
list_move_tail(&req->list, &dep->req_queued);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_USB_GADGET_DWC3) || defined(CONFIG_USB_GADGET_DWC3_MODULE)
|
||||
int dwc3_gadget_init(struct dwc3 *dwc);
|
||||
void dwc3_gadget_exit(struct dwc3 *dwc);
|
||||
#else
|
||||
static inline int dwc3_gadget_init(struct dwc3 *dwc) { return 0; }
|
||||
static inline void dwc3_gadget_exit(struct dwc3 *dwc) { }
|
||||
static inline int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
|
||||
unsigned cmd, struct dwc3_gadget_ep_cmd_params *params)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
|
||||
int status);
|
||||
|
||||
void dwc3_ep0_interrupt(struct dwc3 *dwc, const struct dwc3_event_depevt *event);
|
||||
void dwc3_ep0_interrupt(struct dwc3 *dwc,
|
||||
const struct dwc3_event_depevt *event);
|
||||
void dwc3_ep0_out_start(struct dwc3 *dwc);
|
||||
int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
|
||||
gfp_t gfp_flags);
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
/**
|
||||
* host.c - DesignWare USB3 DRD Controller Host Glue
|
||||
*
|
||||
* Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com
|
||||
*
|
||||
* Authors: Felipe Balbi <balbi@ti.com>,
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions, and the following disclaimer,
|
||||
* without modification.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The names of the above-listed copyright holders may not be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* ALTERNATIVELY, this software may be distributed under the terms of the
|
||||
* GNU General Public License ("GPL") version 2, as published by the Free
|
||||
* Software Foundation.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include "core.h"
|
||||
|
||||
static struct resource generic_resources[] = {
|
||||
{
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
{
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
};
|
||||
|
||||
int dwc3_host_init(struct dwc3 *dwc)
|
||||
{
|
||||
struct platform_device *xhci;
|
||||
int ret;
|
||||
|
||||
xhci = platform_device_alloc("xhci", -1);
|
||||
if (!xhci) {
|
||||
dev_err(dwc->dev, "couldn't allocate xHCI device\n");
|
||||
ret = -ENOMEM;
|
||||
goto err0;
|
||||
}
|
||||
|
||||
dma_set_coherent_mask(&xhci->dev, dwc->dev->coherent_dma_mask);
|
||||
|
||||
xhci->dev.parent = dwc->dev;
|
||||
xhci->dev.dma_mask = dwc->dev->dma_mask;
|
||||
xhci->dev.dma_parms = dwc->dev->dma_parms;
|
||||
|
||||
dwc->xhci = xhci;
|
||||
|
||||
/* setup resources */
|
||||
generic_resources[0].start = dwc->irq;
|
||||
|
||||
generic_resources[1].start = dwc->res->start;
|
||||
generic_resources[1].end = dwc->res->start + 0x7fff;
|
||||
|
||||
ret = platform_device_add_resources(xhci, generic_resources,
|
||||
ARRAY_SIZE(generic_resources));
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "couldn't add resources to xHCI device\n");
|
||||
goto err1;
|
||||
}
|
||||
|
||||
ret = platform_device_add(xhci);
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "failed to register xHCI device\n");
|
||||
goto err1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err1:
|
||||
platform_device_put(xhci);
|
||||
|
||||
err0:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void dwc3_host_exit(struct dwc3 *dwc)
|
||||
{
|
||||
platform_device_unregister(dwc->xhci);
|
||||
}
|
|
@ -39,7 +39,7 @@
|
|||
#ifndef __DRIVERS_USB_DWC3_IO_H
|
||||
#define __DRIVERS_USB_DWC3_IO_H
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
static inline u32 dwc3_readl(void __iomem *base, u32 offset)
|
||||
{
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
menuconfig USB_GADGET
|
||||
tristate "USB Gadget Support"
|
||||
select NLS
|
||||
help
|
||||
USB is a master/slave protocol, organized with one master
|
||||
host (such as a PC) controlling up to 127 peripheral devices.
|
||||
|
@ -124,7 +125,6 @@ config USB_GADGET_STORAGE_NUM_BUFFERS
|
|||
#
|
||||
choice
|
||||
prompt "USB Peripheral Controller"
|
||||
depends on USB_GADGET
|
||||
help
|
||||
A USB device uses a controller to talk to its host.
|
||||
Systems should have only one such upstream link.
|
||||
|
@ -234,7 +234,6 @@ config USB_R8A66597
|
|||
|
||||
config USB_RENESAS_USBHS_UDC
|
||||
tristate 'Renesas USBHS controller'
|
||||
depends on SUPERH || ARCH_SHMOBILE
|
||||
depends on USB_RENESAS_USBHS
|
||||
select USB_GADGET_DUALSPEED
|
||||
help
|
||||
|
@ -309,25 +308,13 @@ config USB_S3C_HSUDC
|
|||
|
||||
This driver has been tested on S3C2416 and S3C2450 processors.
|
||||
|
||||
config USB_PXA_U2O
|
||||
tristate "PXA9xx Processor USB2.0 controller"
|
||||
depends on ARCH_MMP
|
||||
config USB_MV_UDC
|
||||
tristate "Marvell USB2.0 Device Controller"
|
||||
select USB_GADGET_DUALSPEED
|
||||
help
|
||||
PXA9xx Processor series include a high speed USB2.0 device
|
||||
controller, which support high speed and full speed USB peripheral.
|
||||
|
||||
config USB_GADGET_DWC3
|
||||
tristate "DesignWare USB3.0 (DRD) Controller"
|
||||
depends on USB_DWC3
|
||||
select USB_GADGET_DUALSPEED
|
||||
select USB_GADGET_SUPERSPEED
|
||||
help
|
||||
DesignWare USB3.0 controller is a SuperSpeed USB3.0 Controller
|
||||
which can be configured for peripheral-only, host-only, hub-only
|
||||
and Dual-Role operation. This Controller was first integrated into
|
||||
the OMAP5 series of processors. More information about the OMAP5
|
||||
version of this controller, refer to http://www.ti.com/omap5.
|
||||
Marvell Socs (including PXA and MMP series) include a high speed
|
||||
USB2.0 OTG controller, which can be configured as high speed or
|
||||
full speed USB peripheral.
|
||||
|
||||
#
|
||||
# Controllers available in both integrated and discrete versions
|
||||
|
@ -543,12 +530,10 @@ endchoice
|
|||
# Selected by UDC drivers that support high-speed operation.
|
||||
config USB_GADGET_DUALSPEED
|
||||
bool
|
||||
depends on USB_GADGET
|
||||
|
||||
# Selected by UDC drivers that support super-speed opperation
|
||||
config USB_GADGET_SUPERSPEED
|
||||
bool
|
||||
depends on USB_GADGET
|
||||
depends on USB_GADGET_DUALSPEED
|
||||
|
||||
#
|
||||
|
@ -556,7 +541,6 @@ config USB_GADGET_SUPERSPEED
|
|||
#
|
||||
choice
|
||||
tristate "USB Gadget Drivers"
|
||||
depends on USB_GADGET
|
||||
default USB_ETH
|
||||
help
|
||||
A Linux "Gadget Driver" talks to the USB Peripheral Controller
|
||||
|
|
|
@ -27,7 +27,7 @@ obj-$(CONFIG_USB_S3C_HSOTG) += s3c-hsotg.o
|
|||
obj-$(CONFIG_USB_S3C_HSUDC) += s3c-hsudc.o
|
||||
obj-$(CONFIG_USB_LANGWELL) += langwell_udc.o
|
||||
obj-$(CONFIG_USB_EG20T) += pch_udc.o
|
||||
obj-$(CONFIG_USB_PXA_U2O) += mv_udc.o
|
||||
obj-$(CONFIG_USB_MV_UDC) += mv_udc.o
|
||||
mv_udc-y := mv_udc_core.o
|
||||
obj-$(CONFIG_USB_CI13XXX_MSM) += ci13xxx_msm.o
|
||||
obj-$(CONFIG_USB_FUSB300) += fusb300_udc.o
|
||||
|
|
|
@ -1959,7 +1959,7 @@ static int amd5536_start(struct usb_gadget_driver *driver,
|
|||
u32 tmp;
|
||||
|
||||
if (!driver || !bind || !driver->setup
|
||||
|| driver->speed < USB_SPEED_HIGH)
|
||||
|| driver->max_speed < USB_SPEED_HIGH)
|
||||
return -EINVAL;
|
||||
if (!dev)
|
||||
return -ENODEV;
|
||||
|
@ -3349,7 +3349,7 @@ static int udc_probe(struct udc *dev)
|
|||
dev_set_name(&dev->gadget.dev, "gadget");
|
||||
dev->gadget.dev.release = gadget_release;
|
||||
dev->gadget.name = name;
|
||||
dev->gadget.is_dualspeed = 1;
|
||||
dev->gadget.max_speed = USB_SPEED_HIGH;
|
||||
|
||||
/* init registers, interrupts, ... */
|
||||
startup_registers(dev);
|
||||
|
|
|
@ -1633,7 +1633,7 @@ static int at91_start(struct usb_gadget_driver *driver,
|
|||
unsigned long flags;
|
||||
|
||||
if (!driver
|
||||
|| driver->speed < USB_SPEED_FULL
|
||||
|| driver->max_speed < USB_SPEED_FULL
|
||||
|| !bind
|
||||
|| !driver->setup) {
|
||||
DBG("bad parameter.\n");
|
||||
|
|
|
@ -1038,7 +1038,7 @@ static struct usba_udc the_udc = {
|
|||
.gadget = {
|
||||
.ops = &usba_udc_ops,
|
||||
.ep_list = LIST_HEAD_INIT(the_udc.gadget.ep_list),
|
||||
.is_dualspeed = 1,
|
||||
.max_speed = USB_SPEED_HIGH,
|
||||
.name = "atmel_usba_udc",
|
||||
.dev = {
|
||||
.init_name = "gadget",
|
||||
|
|
|
@ -182,6 +182,16 @@ static inline int hw_ep_bit(int num, int dir)
|
|||
return num + (dir ? 16 : 0);
|
||||
}
|
||||
|
||||
static int ep_to_bit(int n)
|
||||
{
|
||||
int fill = 16 - hw_ep_max / 2;
|
||||
|
||||
if (n >= hw_ep_max / 2)
|
||||
n += fill;
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* hw_aread: reads from register bitfield
|
||||
* @addr: address relative to bus map
|
||||
|
@ -440,12 +450,13 @@ static int hw_ep_get_halt(int num, int dir)
|
|||
/**
|
||||
* hw_test_and_clear_setup_status: test & clear setup status (execute without
|
||||
* interruption)
|
||||
* @n: bit number (endpoint)
|
||||
* @n: endpoint number
|
||||
*
|
||||
* This function returns setup status
|
||||
*/
|
||||
static int hw_test_and_clear_setup_status(int n)
|
||||
{
|
||||
n = ep_to_bit(n);
|
||||
return hw_ctest_and_clear(CAP_ENDPTSETUPSTAT, BIT(n));
|
||||
}
|
||||
|
||||
|
@ -641,12 +652,13 @@ static int hw_register_write(u16 addr, u32 data)
|
|||
/**
|
||||
* hw_test_and_clear_complete: test & clear complete status (execute without
|
||||
* interruption)
|
||||
* @n: bit number (endpoint)
|
||||
* @n: endpoint number
|
||||
*
|
||||
* This function returns complete status
|
||||
*/
|
||||
static int hw_test_and_clear_complete(int n)
|
||||
{
|
||||
n = ep_to_bit(n);
|
||||
return hw_ctest_and_clear(CAP_ENDPTCOMPLETE, BIT(n));
|
||||
}
|
||||
|
||||
|
@ -754,8 +766,11 @@ static ssize_t show_device(struct device *dev, struct device_attribute *attr,
|
|||
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n, "speed = %d\n",
|
||||
gadget->speed);
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n, "max_speed = %d\n",
|
||||
gadget->max_speed);
|
||||
/* TODO: Scheduled for removal in 3.8. */
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n, "is_dualspeed = %d\n",
|
||||
gadget->is_dualspeed);
|
||||
gadget_is_dualspeed(gadget));
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n, "is_otg = %d\n",
|
||||
gadget->is_otg);
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n, "is_a_peripheral = %d\n",
|
||||
|
@ -798,7 +813,7 @@ static ssize_t show_driver(struct device *dev, struct device_attribute *attr,
|
|||
n += scnprintf(buf + n, PAGE_SIZE - n, "function = %s\n",
|
||||
(driver->function ? driver->function : ""));
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n, "max speed = %d\n",
|
||||
driver->speed);
|
||||
driver->max_speed);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
@ -2563,9 +2578,7 @@ static int ci13xxx_start(struct usb_gadget_driver *driver,
|
|||
if (driver == NULL ||
|
||||
bind == NULL ||
|
||||
driver->setup == NULL ||
|
||||
driver->disconnect == NULL ||
|
||||
driver->suspend == NULL ||
|
||||
driver->resume == NULL)
|
||||
driver->disconnect == NULL)
|
||||
return -EINVAL;
|
||||
else if (udc == NULL)
|
||||
return -ENODEV;
|
||||
|
@ -2693,8 +2706,6 @@ static int ci13xxx_stop(struct usb_gadget_driver *driver)
|
|||
driver->unbind == NULL ||
|
||||
driver->setup == NULL ||
|
||||
driver->disconnect == NULL ||
|
||||
driver->suspend == NULL ||
|
||||
driver->resume == NULL ||
|
||||
driver != udc->driver)
|
||||
return -EINVAL;
|
||||
|
||||
|
@ -2793,7 +2804,7 @@ 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) {
|
||||
if (udc->suspended && udc->driver->resume) {
|
||||
spin_unlock(udc->lock);
|
||||
udc->driver->resume(&udc->gadget);
|
||||
spin_lock(udc->lock);
|
||||
|
@ -2807,7 +2818,8 @@ static irqreturn_t udc_irq(void)
|
|||
isr_tr_complete_handler(udc);
|
||||
}
|
||||
if (USBi_SLI & intr) {
|
||||
if (udc->gadget.speed != USB_SPEED_UNKNOWN) {
|
||||
if (udc->gadget.speed != USB_SPEED_UNKNOWN &&
|
||||
udc->driver->suspend) {
|
||||
udc->suspended = 1;
|
||||
spin_unlock(udc->lock);
|
||||
udc->driver->suspend(&udc->gadget);
|
||||
|
@ -2871,7 +2883,7 @@ static int udc_probe(struct ci13xxx_udc_driver *driver, struct device *dev,
|
|||
|
||||
udc->gadget.ops = &usb_gadget_ops;
|
||||
udc->gadget.speed = USB_SPEED_UNKNOWN;
|
||||
udc->gadget.is_dualspeed = 1;
|
||||
udc->gadget.max_speed = USB_SPEED_HIGH;
|
||||
udc->gadget.is_otg = 0;
|
||||
udc->gadget.name = driver->name;
|
||||
|
||||
|
|
|
@ -127,7 +127,7 @@ struct ci13xxx {
|
|||
struct ci13xxx_ep ci13xxx_ep[ENDPT_MAX]; /* extended endpts */
|
||||
u32 ep0_dir; /* ep0 direction */
|
||||
#define ep0out ci13xxx_ep[0]
|
||||
#define ep0in ci13xxx_ep[16]
|
||||
#define ep0in ci13xxx_ep[hw_ep_max / 2]
|
||||
u8 remote_wakeup; /* Is remote wakeup feature
|
||||
enabled by the host? */
|
||||
u8 suspended; /* suspended by the host */
|
||||
|
|
|
@ -1535,9 +1535,9 @@ composite_resume(struct usb_gadget *gadget)
|
|||
|
||||
static struct usb_gadget_driver composite_driver = {
|
||||
#ifdef CONFIG_USB_GADGET_SUPERSPEED
|
||||
.speed = USB_SPEED_SUPER,
|
||||
.max_speed = USB_SPEED_SUPER,
|
||||
#else
|
||||
.speed = USB_SPEED_HIGH,
|
||||
.max_speed = USB_SPEED_HIGH,
|
||||
#endif
|
||||
|
||||
.unbind = composite_unbind,
|
||||
|
@ -1584,8 +1584,8 @@ int usb_composite_probe(struct usb_composite_driver *driver,
|
|||
driver->iProduct = driver->name;
|
||||
composite_driver.function = (char *) driver->name;
|
||||
composite_driver.driver.name = driver->name;
|
||||
composite_driver.speed = min((u8)composite_driver.speed,
|
||||
(u8)driver->max_speed);
|
||||
composite_driver.max_speed =
|
||||
min_t(u8, composite_driver.max_speed, driver->max_speed);
|
||||
composite = driver;
|
||||
composite_gadget_bind = bind;
|
||||
|
||||
|
|
|
@ -404,7 +404,7 @@ fail:
|
|||
|
||||
static struct usb_gadget_driver dbgp_driver = {
|
||||
.function = "dbgp",
|
||||
.speed = USB_SPEED_HIGH,
|
||||
.max_speed = USB_SPEED_HIGH,
|
||||
.unbind = dbgp_unbind,
|
||||
.setup = dbgp_setup,
|
||||
.disconnect = dbgp_disconnect,
|
||||
|
|
|
@ -823,19 +823,18 @@ static int dummy_pullup (struct usb_gadget *_gadget, int value)
|
|||
|
||||
if (value && dum->driver) {
|
||||
if (mod_data.is_super_speed)
|
||||
dum->gadget.speed = dum->driver->speed;
|
||||
dum->gadget.speed = dum->driver->max_speed;
|
||||
else if (mod_data.is_high_speed)
|
||||
dum->gadget.speed = min_t(u8, USB_SPEED_HIGH,
|
||||
dum->driver->speed);
|
||||
dum->driver->max_speed);
|
||||
else
|
||||
dum->gadget.speed = USB_SPEED_FULL;
|
||||
dummy_udc_udpate_ep0(dum);
|
||||
|
||||
if (dum->gadget.speed < dum->driver->speed)
|
||||
if (dum->gadget.speed < dum->driver->max_speed)
|
||||
dev_dbg(udc_dev(dum), "This device can perform faster"
|
||||
" if you connect it to a %s port...\n",
|
||||
(dum->driver->speed == USB_SPEED_SUPER ?
|
||||
"SuperSpeed" : "HighSpeed"));
|
||||
" if you connect it to a %s port...\n",
|
||||
usb_speed_string(dum->driver->max_speed));
|
||||
}
|
||||
dum_hcd = gadget_to_dummy_hcd(_gadget);
|
||||
|
||||
|
@ -898,7 +897,7 @@ static int dummy_udc_start(struct usb_gadget *g,
|
|||
struct dummy_hcd *dum_hcd = gadget_to_dummy_hcd(g);
|
||||
struct dummy *dum = dum_hcd->dum;
|
||||
|
||||
if (driver->speed == USB_SPEED_UNKNOWN)
|
||||
if (driver->max_speed == USB_SPEED_UNKNOWN)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
|
@ -977,7 +976,7 @@ static int dummy_udc_probe (struct platform_device *pdev)
|
|||
|
||||
dum->gadget.name = gadget_name;
|
||||
dum->gadget.ops = &dummy_ops;
|
||||
dum->gadget.is_dualspeed = 1;
|
||||
dum->gadget.max_speed = USB_SPEED_SUPER;
|
||||
|
||||
dev_set_name(&dum->gadget.dev, "gadget");
|
||||
dum->gadget.dev.parent = &pdev->dev;
|
||||
|
|
|
@ -149,7 +149,7 @@ ep_matches (
|
|||
switch (type) {
|
||||
case USB_ENDPOINT_XFER_INT:
|
||||
/* INT: limit 64 bytes full speed, 1024 high/super speed */
|
||||
if (!gadget->is_dualspeed && max > 64)
|
||||
if (!gadget_is_dualspeed(gadget) && max > 64)
|
||||
return 0;
|
||||
/* FALLTHROUGH */
|
||||
|
||||
|
@ -157,12 +157,12 @@ ep_matches (
|
|||
/* ISO: limit 1023 bytes full speed, 1024 high/super speed */
|
||||
if (ep->maxpacket < max)
|
||||
return 0;
|
||||
if (!gadget->is_dualspeed && max > 1023)
|
||||
if (!gadget_is_dualspeed(gadget) && max > 1023)
|
||||
return 0;
|
||||
|
||||
/* BOTH: "high bandwidth" works only at high speed */
|
||||
if ((desc->wMaxPacketSize & cpu_to_le16(3<<11))) {
|
||||
if (!gadget->is_dualspeed)
|
||||
if (!gadget_is_dualspeed(gadget))
|
||||
return 0;
|
||||
/* configure your hardware with enough buffering!! */
|
||||
}
|
||||
|
|
|
@ -1399,7 +1399,7 @@ static int ffs_epfiles_create(struct ffs_data *ffs)
|
|||
ENTER();
|
||||
|
||||
count = ffs->eps_count;
|
||||
epfiles = kzalloc(count * sizeof *epfiles, GFP_KERNEL);
|
||||
epfiles = kcalloc(count, sizeof(*epfiles), GFP_KERNEL);
|
||||
if (!epfiles)
|
||||
return -ENOMEM;
|
||||
|
||||
|
|
|
@ -1873,17 +1873,14 @@ static int check_command(struct fsg_common *common, int cmnd_size,
|
|||
common->lun, lun);
|
||||
|
||||
/* Check the LUN */
|
||||
if (common->lun < common->nluns) {
|
||||
curlun = &common->luns[common->lun];
|
||||
common->curlun = curlun;
|
||||
curlun = common->curlun;
|
||||
if (curlun) {
|
||||
if (common->cmnd[0] != REQUEST_SENSE) {
|
||||
curlun->sense_data = SS_NO_SENSE;
|
||||
curlun->sense_data_info = 0;
|
||||
curlun->info_valid = 0;
|
||||
}
|
||||
} else {
|
||||
common->curlun = NULL;
|
||||
curlun = NULL;
|
||||
common->bad_lun_okay = 0;
|
||||
|
||||
/*
|
||||
|
@ -1929,6 +1926,17 @@ static int check_command(struct fsg_common *common, int cmnd_size,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* wrapper of check_command for data size in blocks handling */
|
||||
static int check_command_size_in_blocks(struct fsg_common *common,
|
||||
int cmnd_size, enum data_direction data_dir,
|
||||
unsigned int mask, int needs_medium, const char *name)
|
||||
{
|
||||
if (common->curlun)
|
||||
common->data_size_from_cmnd <<= common->curlun->blkbits;
|
||||
return check_command(common, cmnd_size, data_dir,
|
||||
mask, needs_medium, name);
|
||||
}
|
||||
|
||||
static int do_scsi_command(struct fsg_common *common)
|
||||
{
|
||||
struct fsg_buffhd *bh;
|
||||
|
@ -2011,9 +2019,9 @@ static int do_scsi_command(struct fsg_common *common)
|
|||
|
||||
case READ_6:
|
||||
i = common->cmnd[4];
|
||||
common->data_size_from_cmnd = (i == 0 ? 256 : i) <<
|
||||
common->curlun->blkbits;
|
||||
reply = check_command(common, 6, DATA_DIR_TO_HOST,
|
||||
common->data_size_from_cmnd = (i == 0) ? 256 : i;
|
||||
reply = check_command_size_in_blocks(common, 6,
|
||||
DATA_DIR_TO_HOST,
|
||||
(7<<1) | (1<<4), 1,
|
||||
"READ(6)");
|
||||
if (reply == 0)
|
||||
|
@ -2022,9 +2030,9 @@ static int do_scsi_command(struct fsg_common *common)
|
|||
|
||||
case READ_10:
|
||||
common->data_size_from_cmnd =
|
||||
get_unaligned_be16(&common->cmnd[7]) <<
|
||||
common->curlun->blkbits;
|
||||
reply = check_command(common, 10, DATA_DIR_TO_HOST,
|
||||
get_unaligned_be16(&common->cmnd[7]);
|
||||
reply = check_command_size_in_blocks(common, 10,
|
||||
DATA_DIR_TO_HOST,
|
||||
(1<<1) | (0xf<<2) | (3<<7), 1,
|
||||
"READ(10)");
|
||||
if (reply == 0)
|
||||
|
@ -2033,9 +2041,9 @@ static int do_scsi_command(struct fsg_common *common)
|
|||
|
||||
case READ_12:
|
||||
common->data_size_from_cmnd =
|
||||
get_unaligned_be32(&common->cmnd[6]) <<
|
||||
common->curlun->blkbits;
|
||||
reply = check_command(common, 12, DATA_DIR_TO_HOST,
|
||||
get_unaligned_be32(&common->cmnd[6]);
|
||||
reply = check_command_size_in_blocks(common, 12,
|
||||
DATA_DIR_TO_HOST,
|
||||
(1<<1) | (0xf<<2) | (0xf<<6), 1,
|
||||
"READ(12)");
|
||||
if (reply == 0)
|
||||
|
@ -2134,9 +2142,9 @@ static int do_scsi_command(struct fsg_common *common)
|
|||
|
||||
case WRITE_6:
|
||||
i = common->cmnd[4];
|
||||
common->data_size_from_cmnd = (i == 0 ? 256 : i) <<
|
||||
common->curlun->blkbits;
|
||||
reply = check_command(common, 6, DATA_DIR_FROM_HOST,
|
||||
common->data_size_from_cmnd = (i == 0) ? 256 : i;
|
||||
reply = check_command_size_in_blocks(common, 6,
|
||||
DATA_DIR_FROM_HOST,
|
||||
(7<<1) | (1<<4), 1,
|
||||
"WRITE(6)");
|
||||
if (reply == 0)
|
||||
|
@ -2145,9 +2153,9 @@ static int do_scsi_command(struct fsg_common *common)
|
|||
|
||||
case WRITE_10:
|
||||
common->data_size_from_cmnd =
|
||||
get_unaligned_be16(&common->cmnd[7]) <<
|
||||
common->curlun->blkbits;
|
||||
reply = check_command(common, 10, DATA_DIR_FROM_HOST,
|
||||
get_unaligned_be16(&common->cmnd[7]);
|
||||
reply = check_command_size_in_blocks(common, 10,
|
||||
DATA_DIR_FROM_HOST,
|
||||
(1<<1) | (0xf<<2) | (3<<7), 1,
|
||||
"WRITE(10)");
|
||||
if (reply == 0)
|
||||
|
@ -2156,9 +2164,9 @@ static int do_scsi_command(struct fsg_common *common)
|
|||
|
||||
case WRITE_12:
|
||||
common->data_size_from_cmnd =
|
||||
get_unaligned_be32(&common->cmnd[6]) <<
|
||||
common->curlun->blkbits;
|
||||
reply = check_command(common, 12, DATA_DIR_FROM_HOST,
|
||||
get_unaligned_be32(&common->cmnd[6]);
|
||||
reply = check_command_size_in_blocks(common, 12,
|
||||
DATA_DIR_FROM_HOST,
|
||||
(1<<1) | (0xf<<2) | (0xf<<6), 1,
|
||||
"WRITE(12)");
|
||||
if (reply == 0)
|
||||
|
@ -2273,6 +2281,10 @@ static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh)
|
|||
if (common->data_size == 0)
|
||||
common->data_dir = DATA_DIR_NONE;
|
||||
common->lun = cbw->Lun;
|
||||
if (common->lun >= 0 && common->lun < common->nluns)
|
||||
common->curlun = &common->luns[common->lun];
|
||||
else
|
||||
common->curlun = NULL;
|
||||
common->tag = cbw->Tag;
|
||||
return 0;
|
||||
}
|
||||
|
@ -2763,7 +2775,7 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common,
|
|||
* Create the LUNs, open their backing files, and register the
|
||||
* LUN devices in sysfs.
|
||||
*/
|
||||
curlun = kzalloc(nluns * sizeof *curlun, GFP_KERNEL);
|
||||
curlun = kcalloc(nluns, sizeof(*curlun), GFP_KERNEL);
|
||||
if (unlikely(!curlun)) {
|
||||
rc = -ENOMEM;
|
||||
goto error_release;
|
||||
|
|
|
@ -2297,19 +2297,17 @@ static int check_command(struct fsg_dev *fsg, int cmnd_size,
|
|||
DBG(fsg, "using LUN %d from CBW, "
|
||||
"not LUN %d from CDB\n",
|
||||
fsg->lun, lun);
|
||||
} else
|
||||
fsg->lun = lun; // Use LUN from the command
|
||||
}
|
||||
|
||||
/* Check the LUN */
|
||||
if (fsg->lun < fsg->nluns) {
|
||||
fsg->curlun = curlun = &fsg->luns[fsg->lun];
|
||||
curlun = fsg->curlun;
|
||||
if (curlun) {
|
||||
if (fsg->cmnd[0] != REQUEST_SENSE) {
|
||||
curlun->sense_data = SS_NO_SENSE;
|
||||
curlun->sense_data_info = 0;
|
||||
curlun->info_valid = 0;
|
||||
}
|
||||
} else {
|
||||
fsg->curlun = curlun = NULL;
|
||||
fsg->bad_lun_okay = 0;
|
||||
|
||||
/* INQUIRY and REQUEST SENSE commands are explicitly allowed
|
||||
|
@ -2351,6 +2349,16 @@ static int check_command(struct fsg_dev *fsg, int cmnd_size,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* wrapper of check_command for data size in blocks handling */
|
||||
static int check_command_size_in_blocks(struct fsg_dev *fsg, int cmnd_size,
|
||||
enum data_direction data_dir, unsigned int mask,
|
||||
int needs_medium, const char *name)
|
||||
{
|
||||
if (fsg->curlun)
|
||||
fsg->data_size_from_cmnd <<= fsg->curlun->blkbits;
|
||||
return check_command(fsg, cmnd_size, data_dir,
|
||||
mask, needs_medium, name);
|
||||
}
|
||||
|
||||
static int do_scsi_command(struct fsg_dev *fsg)
|
||||
{
|
||||
|
@ -2425,26 +2433,27 @@ static int do_scsi_command(struct fsg_dev *fsg)
|
|||
|
||||
case READ_6:
|
||||
i = fsg->cmnd[4];
|
||||
fsg->data_size_from_cmnd = (i == 0 ? 256 : i) << fsg->curlun->blkbits;
|
||||
if ((reply = check_command(fsg, 6, DATA_DIR_TO_HOST,
|
||||
fsg->data_size_from_cmnd = (i == 0) ? 256 : i;
|
||||
if ((reply = check_command_size_in_blocks(fsg, 6,
|
||||
DATA_DIR_TO_HOST,
|
||||
(7<<1) | (1<<4), 1,
|
||||
"READ(6)")) == 0)
|
||||
reply = do_read(fsg);
|
||||
break;
|
||||
|
||||
case READ_10:
|
||||
fsg->data_size_from_cmnd =
|
||||
get_unaligned_be16(&fsg->cmnd[7]) << fsg->curlun->blkbits;
|
||||
if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST,
|
||||
fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->cmnd[7]);
|
||||
if ((reply = check_command_size_in_blocks(fsg, 10,
|
||||
DATA_DIR_TO_HOST,
|
||||
(1<<1) | (0xf<<2) | (3<<7), 1,
|
||||
"READ(10)")) == 0)
|
||||
reply = do_read(fsg);
|
||||
break;
|
||||
|
||||
case READ_12:
|
||||
fsg->data_size_from_cmnd =
|
||||
get_unaligned_be32(&fsg->cmnd[6]) << fsg->curlun->blkbits;
|
||||
if ((reply = check_command(fsg, 12, DATA_DIR_TO_HOST,
|
||||
fsg->data_size_from_cmnd = get_unaligned_be32(&fsg->cmnd[6]);
|
||||
if ((reply = check_command_size_in_blocks(fsg, 12,
|
||||
DATA_DIR_TO_HOST,
|
||||
(1<<1) | (0xf<<2) | (0xf<<6), 1,
|
||||
"READ(12)")) == 0)
|
||||
reply = do_read(fsg);
|
||||
|
@ -2529,26 +2538,27 @@ static int do_scsi_command(struct fsg_dev *fsg)
|
|||
|
||||
case WRITE_6:
|
||||
i = fsg->cmnd[4];
|
||||
fsg->data_size_from_cmnd = (i == 0 ? 256 : i) << fsg->curlun->blkbits;
|
||||
if ((reply = check_command(fsg, 6, DATA_DIR_FROM_HOST,
|
||||
fsg->data_size_from_cmnd = (i == 0) ? 256 : i;
|
||||
if ((reply = check_command_size_in_blocks(fsg, 6,
|
||||
DATA_DIR_FROM_HOST,
|
||||
(7<<1) | (1<<4), 1,
|
||||
"WRITE(6)")) == 0)
|
||||
reply = do_write(fsg);
|
||||
break;
|
||||
|
||||
case WRITE_10:
|
||||
fsg->data_size_from_cmnd =
|
||||
get_unaligned_be16(&fsg->cmnd[7]) << fsg->curlun->blkbits;
|
||||
if ((reply = check_command(fsg, 10, DATA_DIR_FROM_HOST,
|
||||
fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->cmnd[7]);
|
||||
if ((reply = check_command_size_in_blocks(fsg, 10,
|
||||
DATA_DIR_FROM_HOST,
|
||||
(1<<1) | (0xf<<2) | (3<<7), 1,
|
||||
"WRITE(10)")) == 0)
|
||||
reply = do_write(fsg);
|
||||
break;
|
||||
|
||||
case WRITE_12:
|
||||
fsg->data_size_from_cmnd =
|
||||
get_unaligned_be32(&fsg->cmnd[6]) << fsg->curlun->blkbits;
|
||||
if ((reply = check_command(fsg, 12, DATA_DIR_FROM_HOST,
|
||||
fsg->data_size_from_cmnd = get_unaligned_be32(&fsg->cmnd[6]);
|
||||
if ((reply = check_command_size_in_blocks(fsg, 12,
|
||||
DATA_DIR_FROM_HOST,
|
||||
(1<<1) | (0xf<<2) | (0xf<<6), 1,
|
||||
"WRITE(12)")) == 0)
|
||||
reply = do_write(fsg);
|
||||
|
@ -2715,7 +2725,17 @@ static int get_next_command(struct fsg_dev *fsg)
|
|||
memcpy(fsg->cmnd, fsg->cbbuf_cmnd, fsg->cmnd_size);
|
||||
fsg->cbbuf_cmnd_size = 0;
|
||||
spin_unlock_irq(&fsg->lock);
|
||||
|
||||
/* Use LUN from the command */
|
||||
fsg->lun = fsg->cmnd[1] >> 5;
|
||||
}
|
||||
|
||||
/* Update current lun */
|
||||
if (fsg->lun >= 0 && fsg->lun < fsg->nluns)
|
||||
fsg->curlun = &fsg->luns[fsg->lun];
|
||||
else
|
||||
fsg->curlun = NULL;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -3584,7 +3604,7 @@ static void fsg_resume(struct usb_gadget *gadget)
|
|||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static struct usb_gadget_driver fsg_driver = {
|
||||
.speed = USB_SPEED_SUPER,
|
||||
.max_speed = USB_SPEED_SUPER,
|
||||
.function = (char *) fsg_string_product,
|
||||
.unbind = fsg_unbind,
|
||||
.disconnect = fsg_disconnect,
|
||||
|
|
|
@ -2336,7 +2336,7 @@ static int fsl_qe_start(struct usb_gadget_driver *driver,
|
|||
if (!udc_controller)
|
||||
return -ENODEV;
|
||||
|
||||
if (!driver || driver->speed < USB_SPEED_FULL
|
||||
if (!driver || driver->max_speed < USB_SPEED_FULL
|
||||
|| !bind || !driver->disconnect || !driver->setup)
|
||||
return -EINVAL;
|
||||
|
||||
|
@ -2350,7 +2350,7 @@ static int fsl_qe_start(struct usb_gadget_driver *driver,
|
|||
/* hook up the driver */
|
||||
udc_controller->driver = driver;
|
||||
udc_controller->gadget.dev.driver = &driver->driver;
|
||||
udc_controller->gadget.speed = (enum usb_device_speed)(driver->speed);
|
||||
udc_controller->gadget.speed = driver->max_speed;
|
||||
spin_unlock_irqrestore(&udc_controller->lock, flags);
|
||||
|
||||
retval = bind(&udc_controller->gadget);
|
||||
|
@ -2814,20 +2814,7 @@ static struct platform_driver udc_driver = {
|
|||
#endif
|
||||
};
|
||||
|
||||
static int __init qe_udc_init(void)
|
||||
{
|
||||
printk(KERN_INFO "%s: %s, %s\n", driver_name, driver_desc,
|
||||
DRIVER_VERSION);
|
||||
return platform_driver_register(&udc_driver);
|
||||
}
|
||||
|
||||
static void __exit qe_udc_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&udc_driver);
|
||||
}
|
||||
|
||||
module_init(qe_udc_init);
|
||||
module_exit(qe_udc_exit);
|
||||
module_platform_driver(udc_driver);
|
||||
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
|
|
|
@ -1934,7 +1934,7 @@ static int fsl_start(struct usb_gadget_driver *driver,
|
|||
if (!udc_controller)
|
||||
return -ENODEV;
|
||||
|
||||
if (!driver || driver->speed < USB_SPEED_FULL
|
||||
if (!driver || driver->max_speed < USB_SPEED_FULL
|
||||
|| !bind || !driver->disconnect || !driver->setup)
|
||||
return -EINVAL;
|
||||
|
||||
|
@ -2525,7 +2525,7 @@ static int __init fsl_udc_probe(struct platform_device *pdev)
|
|||
|
||||
/* Setup gadget structure */
|
||||
udc_controller->gadget.ops = &fsl_gadget_ops;
|
||||
udc_controller->gadget.is_dualspeed = 1;
|
||||
udc_controller->gadget.max_speed = USB_SPEED_HIGH;
|
||||
udc_controller->gadget.ep0 = &udc_controller->eps[0].ep;
|
||||
INIT_LIST_HEAD(&udc_controller->gadget.ep_list);
|
||||
udc_controller->gadget.speed = USB_SPEED_UNKNOWN;
|
||||
|
|
|
@ -1317,7 +1317,7 @@ static int fusb300_udc_start(struct usb_gadget_driver *driver,
|
|||
int retval;
|
||||
|
||||
if (!driver
|
||||
|| driver->speed < USB_SPEED_FULL
|
||||
|| driver->max_speed < USB_SPEED_FULL
|
||||
|| !bind
|
||||
|| !driver->setup)
|
||||
return -EINVAL;
|
||||
|
@ -1463,7 +1463,7 @@ static int __init fusb300_probe(struct platform_device *pdev)
|
|||
|
||||
dev_set_name(&fusb300->gadget.dev, "gadget");
|
||||
|
||||
fusb300->gadget.is_dualspeed = 1;
|
||||
fusb300->gadget.max_speed = USB_SPEED_HIGH;
|
||||
fusb300->gadget.dev.parent = &pdev->dev;
|
||||
fusb300->gadget.dev.dma_mask = pdev->dev.dma_mask;
|
||||
fusb300->gadget.dev.release = pdev->dev.release;
|
||||
|
|
|
@ -1357,7 +1357,7 @@ static int goku_start(struct usb_gadget_driver *driver,
|
|||
int retval;
|
||||
|
||||
if (!driver
|
||||
|| driver->speed < USB_SPEED_FULL
|
||||
|| driver->max_speed < USB_SPEED_FULL
|
||||
|| !bind
|
||||
|| !driver->disconnect
|
||||
|| !driver->setup)
|
||||
|
@ -1796,6 +1796,7 @@ static int goku_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
|||
spin_lock_init(&dev->lock);
|
||||
dev->pdev = pdev;
|
||||
dev->gadget.ops = &goku_ops;
|
||||
dev->gadget.max_speed = USB_SPEED_FULL;
|
||||
|
||||
/* the "gadget" abstracts/virtualizes the controller */
|
||||
dev_set_name(&dev->gadget.dev, "gadget");
|
||||
|
|
|
@ -1336,7 +1336,7 @@ static int imx_udc_start(struct usb_gadget_driver *driver,
|
|||
int retval;
|
||||
|
||||
if (!driver
|
||||
|| driver->speed < USB_SPEED_FULL
|
||||
|| driver->max_speed < USB_SPEED_FULL
|
||||
|| !bind
|
||||
|| !driver->disconnect
|
||||
|| !driver->setup)
|
||||
|
|
|
@ -1766,9 +1766,9 @@ gadgetfs_suspend (struct usb_gadget *gadget)
|
|||
|
||||
static struct usb_gadget_driver gadgetfs_driver = {
|
||||
#ifdef CONFIG_USB_GADGET_DUALSPEED
|
||||
.speed = USB_SPEED_HIGH,
|
||||
.max_speed = USB_SPEED_HIGH,
|
||||
#else
|
||||
.speed = USB_SPEED_FULL,
|
||||
.max_speed = USB_SPEED_FULL,
|
||||
#endif
|
||||
.function = (char *) driver_desc,
|
||||
.unbind = gadgetfs_unbind,
|
||||
|
@ -1792,7 +1792,7 @@ static int gadgetfs_probe (struct usb_gadget *gadget)
|
|||
}
|
||||
|
||||
static struct usb_gadget_driver probe_driver = {
|
||||
.speed = USB_SPEED_HIGH,
|
||||
.max_speed = USB_SPEED_HIGH,
|
||||
.unbind = gadgetfs_nop,
|
||||
.setup = (void *)gadgetfs_nop,
|
||||
.disconnect = gadgetfs_nop,
|
||||
|
|
|
@ -3267,7 +3267,7 @@ static int langwell_udc_probe(struct pci_dev *pdev,
|
|||
dev->gadget.ep0 = &dev->ep[0].ep; /* gadget ep0 */
|
||||
INIT_LIST_HEAD(&dev->gadget.ep_list); /* ep_list */
|
||||
dev->gadget.speed = USB_SPEED_UNKNOWN; /* speed */
|
||||
dev->gadget.is_dualspeed = 1; /* support dual speed */
|
||||
dev->gadget.max_speed = USB_SPEED_HIGH; /* support dual speed */
|
||||
#ifdef OTG_TRANSCEIVER
|
||||
dev->gadget.is_otg = 1; /* support otg mode */
|
||||
#endif
|
||||
|
|
|
@ -1472,7 +1472,7 @@ static int m66592_start(struct usb_gadget_driver *driver,
|
|||
int retval;
|
||||
|
||||
if (!driver
|
||||
|| driver->speed < USB_SPEED_HIGH
|
||||
|| driver->max_speed < USB_SPEED_HIGH
|
||||
|| !bind
|
||||
|| !driver->setup)
|
||||
return -EINVAL;
|
||||
|
@ -1653,7 +1653,7 @@ static int __init m66592_probe(struct platform_device *pdev)
|
|||
m66592->gadget.ops = &m66592_gadget_ops;
|
||||
device_initialize(&m66592->gadget.dev);
|
||||
dev_set_name(&m66592->gadget.dev, "gadget");
|
||||
m66592->gadget.is_dualspeed = 1;
|
||||
m66592->gadget.max_speed = USB_SPEED_HIGH;
|
||||
m66592->gadget.dev.parent = &pdev->dev;
|
||||
m66592->gadget.dev.dma_mask = pdev->dev.dma_mask;
|
||||
m66592->gadget.dev.release = pdev->dev.release;
|
||||
|
|
|
@ -180,7 +180,7 @@ struct mv_udc {
|
|||
|
||||
struct mv_cap_regs __iomem *cap_regs;
|
||||
struct mv_op_regs __iomem *op_regs;
|
||||
unsigned int phy_regs;
|
||||
void __iomem *phy_regs;
|
||||
unsigned int max_eps;
|
||||
struct mv_dqh *ep_dqh;
|
||||
size_t ep_dqh_size;
|
||||
|
@ -211,11 +211,14 @@ struct mv_udc {
|
|||
softconnected:1,
|
||||
force_fs:1,
|
||||
clock_gating:1,
|
||||
active:1;
|
||||
active:1,
|
||||
stopped:1; /* stop bit is setted */
|
||||
|
||||
struct work_struct vbus_work;
|
||||
struct workqueue_struct *qwork;
|
||||
|
||||
struct otg_transceiver *transceiver;
|
||||
|
||||
struct mv_usb_platform_data *pdata;
|
||||
|
||||
/* some SOC has mutiple clock sources for USB*/
|
||||
|
|
|
@ -276,11 +276,12 @@ static void done(struct mv_ep *ep, struct mv_req *req, int status)
|
|||
|
||||
static int queue_dtd(struct mv_ep *ep, struct mv_req *req)
|
||||
{
|
||||
u32 tmp, epstatus, bit_pos, direction;
|
||||
struct mv_udc *udc;
|
||||
struct mv_dqh *dqh;
|
||||
u32 bit_pos, direction;
|
||||
u32 usbcmd, epstatus;
|
||||
unsigned int loops;
|
||||
int readsafe, retval = 0;
|
||||
int retval = 0;
|
||||
|
||||
udc = ep->udc;
|
||||
direction = ep_dir(ep);
|
||||
|
@ -293,30 +294,18 @@ static int queue_dtd(struct mv_ep *ep, struct mv_req *req)
|
|||
lastreq = list_entry(ep->queue.prev, struct mv_req, queue);
|
||||
lastreq->tail->dtd_next =
|
||||
req->head->td_dma & EP_QUEUE_HEAD_NEXT_POINTER_MASK;
|
||||
if (readl(&udc->op_regs->epprime) & bit_pos) {
|
||||
loops = LOOPS(PRIME_TIMEOUT);
|
||||
while (readl(&udc->op_regs->epprime) & bit_pos) {
|
||||
if (loops == 0) {
|
||||
retval = -ETIME;
|
||||
goto done;
|
||||
}
|
||||
udelay(LOOPS_USEC);
|
||||
loops--;
|
||||
}
|
||||
if (readl(&udc->op_regs->epstatus) & bit_pos)
|
||||
goto done;
|
||||
}
|
||||
readsafe = 0;
|
||||
|
||||
wmb();
|
||||
|
||||
if (readl(&udc->op_regs->epprime) & bit_pos)
|
||||
goto done;
|
||||
|
||||
loops = LOOPS(READSAFE_TIMEOUT);
|
||||
while (readsafe == 0) {
|
||||
if (loops == 0) {
|
||||
retval = -ETIME;
|
||||
goto done;
|
||||
}
|
||||
while (1) {
|
||||
/* start with setting the semaphores */
|
||||
tmp = readl(&udc->op_regs->usbcmd);
|
||||
tmp |= USBCMD_ATDTW_TRIPWIRE_SET;
|
||||
writel(tmp, &udc->op_regs->usbcmd);
|
||||
usbcmd = readl(&udc->op_regs->usbcmd);
|
||||
usbcmd |= USBCMD_ATDTW_TRIPWIRE_SET;
|
||||
writel(usbcmd, &udc->op_regs->usbcmd);
|
||||
|
||||
/* read the endpoint status */
|
||||
epstatus = readl(&udc->op_regs->epstatus) & bit_pos;
|
||||
|
@ -329,98 +318,46 @@ static int queue_dtd(struct mv_ep *ep, struct mv_req *req)
|
|||
* primed.
|
||||
*/
|
||||
if (readl(&udc->op_regs->usbcmd)
|
||||
& USBCMD_ATDTW_TRIPWIRE_SET) {
|
||||
readsafe = 1;
|
||||
}
|
||||
& USBCMD_ATDTW_TRIPWIRE_SET)
|
||||
break;
|
||||
|
||||
loops--;
|
||||
if (loops == 0) {
|
||||
dev_err(&udc->dev->dev,
|
||||
"Timeout for ATDTW_TRIPWIRE...\n");
|
||||
retval = -ETIME;
|
||||
goto done;
|
||||
}
|
||||
udelay(LOOPS_USEC);
|
||||
}
|
||||
|
||||
/* Clear the semaphore */
|
||||
tmp = readl(&udc->op_regs->usbcmd);
|
||||
tmp &= USBCMD_ATDTW_TRIPWIRE_CLEAR;
|
||||
writel(tmp, &udc->op_regs->usbcmd);
|
||||
usbcmd = readl(&udc->op_regs->usbcmd);
|
||||
usbcmd &= USBCMD_ATDTW_TRIPWIRE_CLEAR;
|
||||
writel(usbcmd, &udc->op_regs->usbcmd);
|
||||
|
||||
/* If endpoint is not active, we activate it now. */
|
||||
if (!epstatus) {
|
||||
if (direction == EP_DIR_IN) {
|
||||
struct mv_dtd *curr_dtd = dma_to_virt(
|
||||
&udc->dev->dev, dqh->curr_dtd_ptr);
|
||||
|
||||
loops = LOOPS(DTD_TIMEOUT);
|
||||
while (curr_dtd->size_ioc_sts
|
||||
& DTD_STATUS_ACTIVE) {
|
||||
if (loops == 0) {
|
||||
retval = -ETIME;
|
||||
goto done;
|
||||
}
|
||||
loops--;
|
||||
udelay(LOOPS_USEC);
|
||||
}
|
||||
}
|
||||
/* No other transfers on the queue */
|
||||
|
||||
/* Write dQH next pointer and terminate bit to 0 */
|
||||
dqh->next_dtd_ptr = req->head->td_dma
|
||||
& EP_QUEUE_HEAD_NEXT_POINTER_MASK;
|
||||
dqh->size_ioc_int_sts = 0;
|
||||
|
||||
/*
|
||||
* Ensure that updates to the QH will
|
||||
* occur before priming.
|
||||
*/
|
||||
wmb();
|
||||
|
||||
/* Prime the Endpoint */
|
||||
writel(bit_pos, &udc->op_regs->epprime);
|
||||
}
|
||||
} else {
|
||||
/* Write dQH next pointer and terminate bit to 0 */
|
||||
dqh->next_dtd_ptr = req->head->td_dma
|
||||
& EP_QUEUE_HEAD_NEXT_POINTER_MASK;
|
||||
dqh->size_ioc_int_sts = 0;
|
||||
|
||||
/* Ensure that updates to the QH will occur before priming. */
|
||||
wmb();
|
||||
|
||||
/* Prime the Endpoint */
|
||||
writel(bit_pos, &udc->op_regs->epprime);
|
||||
|
||||
if (direction == EP_DIR_IN) {
|
||||
/* FIXME add status check after prime the IN ep */
|
||||
int prime_again;
|
||||
u32 curr_dtd_ptr = dqh->curr_dtd_ptr;
|
||||
|
||||
loops = LOOPS(DTD_TIMEOUT);
|
||||
prime_again = 0;
|
||||
while ((curr_dtd_ptr != req->head->td_dma)) {
|
||||
curr_dtd_ptr = dqh->curr_dtd_ptr;
|
||||
if (loops == 0) {
|
||||
dev_err(&udc->dev->dev,
|
||||
"failed to prime %s\n",
|
||||
ep->name);
|
||||
retval = -ETIME;
|
||||
goto done;
|
||||
}
|
||||
loops--;
|
||||
udelay(LOOPS_USEC);
|
||||
|
||||
if (loops == (LOOPS(DTD_TIMEOUT) >> 2)) {
|
||||
if (prime_again)
|
||||
goto done;
|
||||
dev_info(&udc->dev->dev,
|
||||
"prime again\n");
|
||||
writel(bit_pos,
|
||||
&udc->op_regs->epprime);
|
||||
prime_again = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (epstatus)
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Write dQH next pointer and terminate bit to 0 */
|
||||
dqh->next_dtd_ptr = req->head->td_dma
|
||||
& EP_QUEUE_HEAD_NEXT_POINTER_MASK;
|
||||
|
||||
/* clear active and halt bit, in case set from a previous error */
|
||||
dqh->size_ioc_int_sts &= ~(DTD_STATUS_ACTIVE | DTD_STATUS_HALTED);
|
||||
|
||||
/* Ensure that updates to the QH will occure before priming. */
|
||||
wmb();
|
||||
|
||||
/* Prime the Endpoint */
|
||||
writel(bit_pos, &udc->op_regs->epprime);
|
||||
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
static struct mv_dtd *build_dtd(struct mv_req *req, unsigned *length,
|
||||
dma_addr_t *dma, int *is_last)
|
||||
{
|
||||
|
@ -841,6 +778,27 @@ mv_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void mv_prime_ep(struct mv_ep *ep, struct mv_req *req)
|
||||
{
|
||||
struct mv_dqh *dqh = ep->dqh;
|
||||
u32 bit_pos;
|
||||
|
||||
/* Write dQH next pointer and terminate bit to 0 */
|
||||
dqh->next_dtd_ptr = req->head->td_dma
|
||||
& EP_QUEUE_HEAD_NEXT_POINTER_MASK;
|
||||
|
||||
/* clear active and halt bit, in case set from a previous error */
|
||||
dqh->size_ioc_int_sts &= ~(DTD_STATUS_ACTIVE | DTD_STATUS_HALTED);
|
||||
|
||||
/* Ensure that updates to the QH will occure before priming. */
|
||||
wmb();
|
||||
|
||||
bit_pos = 1 << (((ep_dir(ep) == EP_DIR_OUT) ? 0 : 16) + ep->ep_num);
|
||||
|
||||
/* Prime the Endpoint */
|
||||
writel(bit_pos, &ep->udc->op_regs->epprime);
|
||||
}
|
||||
|
||||
/* dequeues (cancels, unlinks) an I/O request from an endpoint */
|
||||
static int mv_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
|
||||
{
|
||||
|
@ -883,15 +841,13 @@ static int mv_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
|
|||
|
||||
/* The request isn't the last request in this ep queue */
|
||||
if (req->queue.next != &ep->queue) {
|
||||
struct mv_dqh *qh;
|
||||
struct mv_req *next_req;
|
||||
|
||||
qh = ep->dqh;
|
||||
next_req = list_entry(req->queue.next, struct mv_req,
|
||||
queue);
|
||||
next_req = list_entry(req->queue.next,
|
||||
struct mv_req, queue);
|
||||
|
||||
/* Point the QH to the first TD of next request */
|
||||
writel((u32) next_req->head, &qh->curr_dtd_ptr);
|
||||
mv_prime_ep(ep, next_req);
|
||||
} else {
|
||||
struct mv_dqh *qh;
|
||||
|
||||
|
@ -1056,6 +1012,8 @@ static void udc_stop(struct mv_udc *udc)
|
|||
USBINTR_PORT_CHANGE_DETECT_EN | USBINTR_RESET_EN);
|
||||
writel(tmp, &udc->op_regs->usbintr);
|
||||
|
||||
udc->stopped = 1;
|
||||
|
||||
/* Reset the Run the bit in the command register to stop VUSB */
|
||||
tmp = readl(&udc->op_regs->usbcmd);
|
||||
tmp &= ~USBCMD_RUN_STOP;
|
||||
|
@ -1072,6 +1030,8 @@ static void udc_start(struct mv_udc *udc)
|
|||
/* Enable interrupts */
|
||||
writel(usbintr, &udc->op_regs->usbintr);
|
||||
|
||||
udc->stopped = 0;
|
||||
|
||||
/* Set the Run bit in the command register */
|
||||
writel(USBCMD_RUN_STOP, &udc->op_regs->usbcmd);
|
||||
}
|
||||
|
@ -1134,11 +1094,11 @@ static int udc_reset(struct mv_udc *udc)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int mv_udc_enable(struct mv_udc *udc)
|
||||
static int mv_udc_enable_internal(struct mv_udc *udc)
|
||||
{
|
||||
int retval;
|
||||
|
||||
if (udc->clock_gating == 0 || udc->active)
|
||||
if (udc->active)
|
||||
return 0;
|
||||
|
||||
dev_dbg(&udc->dev->dev, "enable udc\n");
|
||||
|
@ -1157,9 +1117,17 @@ static int mv_udc_enable(struct mv_udc *udc)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void mv_udc_disable(struct mv_udc *udc)
|
||||
static int mv_udc_enable(struct mv_udc *udc)
|
||||
{
|
||||
if (udc->clock_gating && udc->active) {
|
||||
if (udc->clock_gating)
|
||||
return mv_udc_enable_internal(udc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mv_udc_disable_internal(struct mv_udc *udc)
|
||||
{
|
||||
if (udc->active) {
|
||||
dev_dbg(&udc->dev->dev, "disable udc\n");
|
||||
if (udc->pdata->phy_deinit)
|
||||
udc->pdata->phy_deinit(udc->phy_regs);
|
||||
|
@ -1168,6 +1136,12 @@ static void mv_udc_disable(struct mv_udc *udc)
|
|||
}
|
||||
}
|
||||
|
||||
static void mv_udc_disable(struct mv_udc *udc)
|
||||
{
|
||||
if (udc->clock_gating)
|
||||
mv_udc_disable_internal(udc);
|
||||
}
|
||||
|
||||
static int mv_udc_get_frame(struct usb_gadget *gadget)
|
||||
{
|
||||
struct mv_udc *udc;
|
||||
|
@ -1178,7 +1152,7 @@ static int mv_udc_get_frame(struct usb_gadget *gadget)
|
|||
|
||||
udc = container_of(gadget, struct mv_udc, gadget);
|
||||
|
||||
retval = readl(udc->op_regs->frindex) & USB_FRINDEX_MASKS;
|
||||
retval = readl(&udc->op_regs->frindex) & USB_FRINDEX_MASKS;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
@ -1212,10 +1186,11 @@ static int mv_udc_vbus_session(struct usb_gadget *gadget, int is_active)
|
|||
udc = container_of(gadget, struct mv_udc, gadget);
|
||||
spin_lock_irqsave(&udc->lock, flags);
|
||||
|
||||
udc->vbus_active = (is_active != 0);
|
||||
|
||||
dev_dbg(&udc->dev->dev, "%s: softconnect %d, vbus_active %d\n",
|
||||
__func__, udc->softconnect, udc->vbus_active);
|
||||
|
||||
udc->vbus_active = (is_active != 0);
|
||||
if (udc->driver && udc->softconnect && udc->vbus_active) {
|
||||
retval = mv_udc_enable(udc);
|
||||
if (retval == 0) {
|
||||
|
@ -1244,10 +1219,11 @@ static int mv_udc_pullup(struct usb_gadget *gadget, int is_on)
|
|||
udc = container_of(gadget, struct mv_udc, gadget);
|
||||
spin_lock_irqsave(&udc->lock, flags);
|
||||
|
||||
udc->softconnect = (is_on != 0);
|
||||
|
||||
dev_dbg(&udc->dev->dev, "%s: softconnect %d, vbus_active %d\n",
|
||||
__func__, udc->softconnect, udc->vbus_active);
|
||||
|
||||
udc->softconnect = (is_on != 0);
|
||||
if (udc->driver && udc->softconnect && udc->vbus_active) {
|
||||
retval = mv_udc_enable(udc);
|
||||
if (retval == 0) {
|
||||
|
@ -1407,6 +1383,20 @@ static int mv_udc_start(struct usb_gadget_driver *driver,
|
|||
return retval;
|
||||
}
|
||||
|
||||
if (udc->transceiver) {
|
||||
retval = otg_set_peripheral(udc->transceiver, &udc->gadget);
|
||||
if (retval) {
|
||||
dev_err(&udc->dev->dev,
|
||||
"unable to register peripheral to otg\n");
|
||||
if (driver->unbind) {
|
||||
driver->unbind(&udc->gadget);
|
||||
udc->gadget.dev.driver = NULL;
|
||||
udc->driver = NULL;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
/* pullup is always on */
|
||||
mv_udc_pullup(&udc->gadget, 1);
|
||||
|
||||
|
@ -2026,6 +2016,10 @@ static irqreturn_t mv_udc_irq(int irq, void *dev)
|
|||
struct mv_udc *udc = (struct mv_udc *)dev;
|
||||
u32 status, intr;
|
||||
|
||||
/* Disable ISR when stopped bit is set */
|
||||
if (udc->stopped)
|
||||
return IRQ_NONE;
|
||||
|
||||
spin_lock(&udc->lock);
|
||||
|
||||
status = readl(&udc->op_regs->usbsts);
|
||||
|
@ -2109,7 +2103,12 @@ static int __devexit mv_udc_remove(struct platform_device *dev)
|
|||
destroy_workqueue(udc->qwork);
|
||||
}
|
||||
|
||||
if (udc->pdata && udc->pdata->vbus && udc->clock_gating)
|
||||
/*
|
||||
* If we have transceiver inited,
|
||||
* then vbus irq will not be requested in udc driver.
|
||||
*/
|
||||
if (udc->pdata && udc->pdata->vbus
|
||||
&& udc->clock_gating && udc->transceiver == NULL)
|
||||
free_irq(udc->pdata->vbus->irq, &dev->dev);
|
||||
|
||||
/* free memory allocated in probe */
|
||||
|
@ -2129,11 +2128,9 @@ static int __devexit mv_udc_remove(struct platform_device *dev)
|
|||
|
||||
if (udc->cap_regs)
|
||||
iounmap(udc->cap_regs);
|
||||
udc->cap_regs = NULL;
|
||||
|
||||
if (udc->phy_regs)
|
||||
iounmap((void *)udc->phy_regs);
|
||||
udc->phy_regs = 0;
|
||||
iounmap(udc->phy_regs);
|
||||
|
||||
if (udc->status_req) {
|
||||
kfree(udc->status_req->req.buf);
|
||||
|
@ -2182,6 +2179,11 @@ static int __devinit mv_udc_probe(struct platform_device *dev)
|
|||
|
||||
udc->dev = dev;
|
||||
|
||||
#ifdef CONFIG_USB_OTG_UTILS
|
||||
if (pdata->mode == MV_USB_MODE_OTG)
|
||||
udc->transceiver = otg_get_transceiver();
|
||||
#endif
|
||||
|
||||
udc->clknum = pdata->clknum;
|
||||
for (clk_i = 0; clk_i < udc->clknum; clk_i++) {
|
||||
udc->clk[clk_i] = clk_get(&dev->dev, pdata->clkname[clk_i]);
|
||||
|
@ -2213,24 +2215,20 @@ static int __devinit mv_udc_probe(struct platform_device *dev)
|
|||
goto err_iounmap_capreg;
|
||||
}
|
||||
|
||||
udc->phy_regs = (unsigned int)ioremap(r->start, resource_size(r));
|
||||
if (udc->phy_regs == 0) {
|
||||
udc->phy_regs = ioremap(r->start, resource_size(r));
|
||||
if (udc->phy_regs == NULL) {
|
||||
dev_err(&dev->dev, "failed to map phy I/O memory\n");
|
||||
retval = -EBUSY;
|
||||
goto err_iounmap_capreg;
|
||||
}
|
||||
|
||||
/* we will acces controller register, so enable the clk */
|
||||
udc_clock_enable(udc);
|
||||
if (pdata->phy_init) {
|
||||
retval = pdata->phy_init(udc->phy_regs);
|
||||
if (retval) {
|
||||
dev_err(&dev->dev, "phy init error %d\n", retval);
|
||||
goto err_iounmap_phyreg;
|
||||
}
|
||||
}
|
||||
retval = mv_udc_enable_internal(udc);
|
||||
if (retval)
|
||||
goto err_iounmap_phyreg;
|
||||
|
||||
udc->op_regs = (struct mv_op_regs __iomem *)((u32)udc->cap_regs
|
||||
udc->op_regs =
|
||||
(struct mv_op_regs __iomem *)((unsigned long)udc->cap_regs
|
||||
+ (readl(&udc->cap_regs->caplength_hciversion)
|
||||
& CAPLENGTH_MASK));
|
||||
udc->max_eps = readl(&udc->cap_regs->dccparams) & DCCPARAMS_DEN_MASK;
|
||||
|
@ -2312,7 +2310,7 @@ static int __devinit mv_udc_probe(struct platform_device *dev)
|
|||
udc->gadget.ep0 = &udc->eps[0].ep; /* gadget ep0 */
|
||||
INIT_LIST_HEAD(&udc->gadget.ep_list); /* ep_list */
|
||||
udc->gadget.speed = USB_SPEED_UNKNOWN; /* speed */
|
||||
udc->gadget.is_dualspeed = 1; /* support dual speed */
|
||||
udc->gadget.max_speed = USB_SPEED_HIGH; /* support dual speed */
|
||||
|
||||
/* the "gadget" abstracts/virtualizes the controller */
|
||||
dev_set_name(&udc->gadget.dev, "gadget");
|
||||
|
@ -2328,7 +2326,9 @@ static int __devinit mv_udc_probe(struct platform_device *dev)
|
|||
eps_init(udc);
|
||||
|
||||
/* VBUS detect: we can disable/enable clock on demand.*/
|
||||
if (pdata->vbus) {
|
||||
if (udc->transceiver)
|
||||
udc->clock_gating = 1;
|
||||
else if (pdata->vbus) {
|
||||
udc->clock_gating = 1;
|
||||
retval = request_threaded_irq(pdata->vbus->irq, NULL,
|
||||
mv_udc_vbus_irq, IRQF_ONESHOT, "vbus", udc);
|
||||
|
@ -2354,11 +2354,9 @@ static int __devinit mv_udc_probe(struct platform_device *dev)
|
|||
* If not, it means that VBUS detection is not supported, we
|
||||
* have to enable vbus active all the time to let controller work.
|
||||
*/
|
||||
if (udc->clock_gating) {
|
||||
if (udc->pdata->phy_deinit)
|
||||
udc->pdata->phy_deinit(udc->phy_regs);
|
||||
udc_clock_disable(udc);
|
||||
} else
|
||||
if (udc->clock_gating)
|
||||
mv_udc_disable_internal(udc);
|
||||
else
|
||||
udc->vbus_active = 1;
|
||||
|
||||
retval = usb_add_gadget_udc(&dev->dev, &udc->gadget);
|
||||
|
@ -2371,7 +2369,8 @@ static int __devinit mv_udc_probe(struct platform_device *dev)
|
|||
return 0;
|
||||
|
||||
err_unregister:
|
||||
if (udc->pdata && udc->pdata->vbus && udc->clock_gating)
|
||||
if (udc->pdata && udc->pdata->vbus
|
||||
&& udc->clock_gating && udc->transceiver == NULL)
|
||||
free_irq(pdata->vbus->irq, &dev->dev);
|
||||
device_unregister(&udc->gadget.dev);
|
||||
err_free_irq:
|
||||
|
@ -2387,11 +2386,9 @@ err_free_dma:
|
|||
dma_free_coherent(&dev->dev, udc->ep_dqh_size,
|
||||
udc->ep_dqh, udc->ep_dqh_dma);
|
||||
err_disable_clock:
|
||||
if (udc->pdata->phy_deinit)
|
||||
udc->pdata->phy_deinit(udc->phy_regs);
|
||||
udc_clock_disable(udc);
|
||||
mv_udc_disable_internal(udc);
|
||||
err_iounmap_phyreg:
|
||||
iounmap((void *)udc->phy_regs);
|
||||
iounmap(udc->phy_regs);
|
||||
err_iounmap_capreg:
|
||||
iounmap(udc->cap_regs);
|
||||
err_put_clk:
|
||||
|
@ -2407,7 +2404,30 @@ static int mv_udc_suspend(struct device *_dev)
|
|||
{
|
||||
struct mv_udc *udc = the_controller;
|
||||
|
||||
udc_stop(udc);
|
||||
/* if OTG is enabled, the following will be done in OTG driver*/
|
||||
if (udc->transceiver)
|
||||
return 0;
|
||||
|
||||
if (udc->pdata->vbus && udc->pdata->vbus->poll)
|
||||
if (udc->pdata->vbus->poll() == VBUS_HIGH) {
|
||||
dev_info(&udc->dev->dev, "USB cable is connected!\n");
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
/*
|
||||
* only cable is unplugged, udc can suspend.
|
||||
* So do not care about clock_gating == 1.
|
||||
*/
|
||||
if (!udc->clock_gating) {
|
||||
udc_stop(udc);
|
||||
|
||||
spin_lock_irq(&udc->lock);
|
||||
/* stop all usb activities */
|
||||
stop_activity(udc, udc->driver);
|
||||
spin_unlock_irq(&udc->lock);
|
||||
|
||||
mv_udc_disable_internal(udc);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -2417,20 +2437,22 @@ static int mv_udc_resume(struct device *_dev)
|
|||
struct mv_udc *udc = the_controller;
|
||||
int retval;
|
||||
|
||||
if (udc->pdata->phy_init) {
|
||||
retval = udc->pdata->phy_init(udc->phy_regs);
|
||||
if (retval) {
|
||||
dev_err(&udc->dev->dev,
|
||||
"init phy error %d when resume back\n",
|
||||
retval);
|
||||
/* if OTG is enabled, the following will be done in OTG driver*/
|
||||
if (udc->transceiver)
|
||||
return 0;
|
||||
|
||||
if (!udc->clock_gating) {
|
||||
retval = mv_udc_enable_internal(udc);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
if (udc->driver && udc->softconnect) {
|
||||
udc_reset(udc);
|
||||
ep0_reset(udc);
|
||||
udc_start(udc);
|
||||
}
|
||||
}
|
||||
|
||||
udc_reset(udc);
|
||||
ep0_reset(udc);
|
||||
udc_start(udc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -2457,30 +2479,16 @@ static struct platform_driver udc_driver = {
|
|||
.shutdown = mv_udc_shutdown,
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "pxa-u2o",
|
||||
.name = "mv-udc",
|
||||
#ifdef CONFIG_PM
|
||||
.pm = &mv_udc_pm_ops,
|
||||
#endif
|
||||
},
|
||||
};
|
||||
MODULE_ALIAS("platform:pxa-u2o");
|
||||
|
||||
module_platform_driver(udc_driver);
|
||||
MODULE_ALIAS("platform:mv-udc");
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_AUTHOR("Chao Xie <chao.xie@marvell.com>");
|
||||
MODULE_VERSION(DRIVER_VERSION);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
|
||||
static int __init init(void)
|
||||
{
|
||||
return platform_driver_register(&udc_driver);
|
||||
}
|
||||
module_init(init);
|
||||
|
||||
|
||||
static void __exit cleanup(void)
|
||||
{
|
||||
platform_driver_unregister(&udc_driver);
|
||||
}
|
||||
module_exit(cleanup);
|
||||
|
||||
|
|
|
@ -1459,7 +1459,7 @@ static int net2272_start(struct usb_gadget *_gadget,
|
|||
unsigned i;
|
||||
|
||||
if (!driver || !driver->unbind || !driver->setup ||
|
||||
driver->speed != USB_SPEED_HIGH)
|
||||
driver->max_speed != USB_SPEED_HIGH)
|
||||
return -EINVAL;
|
||||
|
||||
dev = container_of(_gadget, struct net2272, gadget);
|
||||
|
@ -2235,7 +2235,7 @@ net2272_probe_init(struct device *dev, unsigned int irq)
|
|||
ret->irq = irq;
|
||||
ret->dev = dev;
|
||||
ret->gadget.ops = &net2272_ops;
|
||||
ret->gadget.is_dualspeed = 1;
|
||||
ret->gadget.max_speed = USB_SPEED_HIGH;
|
||||
|
||||
/* the "gadget" abstracts/virtualizes the controller */
|
||||
dev_set_name(&ret->gadget.dev, "gadget");
|
||||
|
|
|
@ -1881,7 +1881,7 @@ static int net2280_start(struct usb_gadget *_gadget,
|
|||
* (dev->usb->xcvrdiag & FORCE_FULL_SPEED_MODE)
|
||||
* "must not be used in normal operation"
|
||||
*/
|
||||
if (!driver || driver->speed < USB_SPEED_HIGH
|
||||
if (!driver || driver->max_speed < USB_SPEED_HIGH
|
||||
|| !driver->setup)
|
||||
return -EINVAL;
|
||||
|
||||
|
@ -2698,7 +2698,7 @@ static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id)
|
|||
spin_lock_init (&dev->lock);
|
||||
dev->pdev = pdev;
|
||||
dev->gadget.ops = &net2280_ops;
|
||||
dev->gadget.is_dualspeed = 1;
|
||||
dev->gadget.max_speed = USB_SPEED_HIGH;
|
||||
|
||||
/* the "gadget" abstracts/virtualizes the controller */
|
||||
dev_set_name(&dev->gadget.dev, "gadget");
|
||||
|
|
|
@ -2110,7 +2110,7 @@ static int omap_udc_start(struct usb_gadget_driver *driver,
|
|||
return -ENODEV;
|
||||
if (!driver
|
||||
// FIXME if otg, check: driver->is_otg
|
||||
|| driver->speed < USB_SPEED_FULL
|
||||
|| driver->max_speed < USB_SPEED_FULL
|
||||
|| !bind || !driver->setup)
|
||||
return -EINVAL;
|
||||
|
||||
|
@ -2676,6 +2676,7 @@ omap_udc_setup(struct platform_device *odev, struct otg_transceiver *xceiv)
|
|||
INIT_LIST_HEAD(&udc->gadget.ep_list);
|
||||
INIT_LIST_HEAD(&udc->iso);
|
||||
udc->gadget.speed = USB_SPEED_UNKNOWN;
|
||||
udc->gadget.max_speed = USB_SPEED_FULL;
|
||||
udc->gadget.name = driver_name;
|
||||
|
||||
device_initialize(&udc->gadget.dev);
|
||||
|
|
|
@ -2693,7 +2693,7 @@ static int pch_udc_start(struct usb_gadget_driver *driver,
|
|||
struct pch_udc_dev *dev = pch_udc;
|
||||
int retval;
|
||||
|
||||
if (!driver || (driver->speed == USB_SPEED_UNKNOWN) || !bind ||
|
||||
if (!driver || (driver->max_speed == USB_SPEED_UNKNOWN) || !bind ||
|
||||
!driver->setup || !driver->unbind || !driver->disconnect) {
|
||||
dev_err(&dev->pdev->dev,
|
||||
"%s: invalid driver parameter\n", __func__);
|
||||
|
@ -2941,7 +2941,7 @@ static int pch_udc_probe(struct pci_dev *pdev,
|
|||
dev->gadget.dev.dma_mask = pdev->dev.dma_mask;
|
||||
dev->gadget.dev.release = gadget_release;
|
||||
dev->gadget.name = KBUILD_MODNAME;
|
||||
dev->gadget.is_dualspeed = 1;
|
||||
dev->gadget.max_speed = USB_SPEED_HIGH;
|
||||
|
||||
retval = device_register(&dev->gadget.dev);
|
||||
if (retval)
|
||||
|
|
|
@ -1141,7 +1141,7 @@ printer_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
|
|||
break;
|
||||
#ifdef CONFIG_USB_GADGET_DUALSPEED
|
||||
case USB_DT_DEVICE_QUALIFIER:
|
||||
if (!gadget->is_dualspeed)
|
||||
if (!gadget_is_dualspeed(gadget))
|
||||
break;
|
||||
/*
|
||||
* assumes ep0 uses the same value for both
|
||||
|
@ -1155,7 +1155,7 @@ printer_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
|
|||
break;
|
||||
|
||||
case USB_DT_OTHER_SPEED_CONFIG:
|
||||
if (!gadget->is_dualspeed)
|
||||
if (!gadget_is_dualspeed(gadget))
|
||||
break;
|
||||
/* FALLTHROUGH */
|
||||
#endif /* CONFIG_USB_GADGET_DUALSPEED */
|
||||
|
@ -1535,7 +1535,7 @@ fail:
|
|||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static struct usb_gadget_driver printer_driver = {
|
||||
.speed = DEVSPEED,
|
||||
.max_speed = DEVSPEED,
|
||||
|
||||
.function = (char *) driver_desc,
|
||||
.unbind = printer_unbind,
|
||||
|
|
|
@ -1264,7 +1264,7 @@ static int pxa25x_start(struct usb_gadget_driver *driver,
|
|||
int retval;
|
||||
|
||||
if (!driver
|
||||
|| driver->speed < USB_SPEED_FULL
|
||||
|| driver->max_speed < USB_SPEED_FULL
|
||||
|| !bind
|
||||
|| !driver->disconnect
|
||||
|| !driver->setup)
|
||||
|
|
|
@ -1807,7 +1807,7 @@ static int pxa27x_udc_start(struct usb_gadget_driver *driver,
|
|||
struct pxa_udc *udc = the_controller;
|
||||
int retval;
|
||||
|
||||
if (!driver || driver->speed < USB_SPEED_FULL || !bind
|
||||
if (!driver || driver->max_speed < USB_SPEED_FULL || !bind
|
||||
|| !driver->disconnect || !driver->setup)
|
||||
return -EINVAL;
|
||||
if (!udc)
|
||||
|
|
|
@ -1746,7 +1746,7 @@ static int r8a66597_start(struct usb_gadget *gadget,
|
|||
struct r8a66597 *r8a66597 = gadget_to_r8a66597(gadget);
|
||||
|
||||
if (!driver
|
||||
|| driver->speed < USB_SPEED_HIGH
|
||||
|| driver->max_speed < USB_SPEED_HIGH
|
||||
|| !driver->setup)
|
||||
return -EINVAL;
|
||||
if (!r8a66597)
|
||||
|
@ -1911,7 +1911,7 @@ static int __init r8a66597_probe(struct platform_device *pdev)
|
|||
|
||||
r8a66597->gadget.ops = &r8a66597_gadget_ops;
|
||||
dev_set_name(&r8a66597->gadget.dev, "gadget");
|
||||
r8a66597->gadget.is_dualspeed = 1;
|
||||
r8a66597->gadget.max_speed = USB_SPEED_HIGH;
|
||||
r8a66597->gadget.dev.parent = &pdev->dev;
|
||||
r8a66597->gadget.dev.dma_mask = pdev->dev.dma_mask;
|
||||
r8a66597->gadget.dev.release = pdev->dev.release;
|
||||
|
|
|
@ -2586,7 +2586,7 @@ static int s3c_hsotg_start(struct usb_gadget_driver *driver,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (driver->speed < USB_SPEED_FULL)
|
||||
if (driver->max_speed < USB_SPEED_FULL)
|
||||
dev_err(hsotg->dev, "%s: bad speed\n", __func__);
|
||||
|
||||
if (!bind || !driver->setup) {
|
||||
|
@ -3362,7 +3362,7 @@ static int __devinit s3c_hsotg_probe(struct platform_device *pdev)
|
|||
|
||||
dev_set_name(&hsotg->gadget.dev, "gadget");
|
||||
|
||||
hsotg->gadget.is_dualspeed = 1;
|
||||
hsotg->gadget.max_speed = USB_SPEED_HIGH;
|
||||
hsotg->gadget.ops = &s3c_hsotg_gadget_ops;
|
||||
hsotg->gadget.name = dev_name(dev);
|
||||
|
||||
|
@ -3467,18 +3467,7 @@ static struct platform_driver s3c_hsotg_driver = {
|
|||
.resume = s3c_hsotg_resume,
|
||||
};
|
||||
|
||||
static int __init s3c_hsotg_modinit(void)
|
||||
{
|
||||
return platform_driver_register(&s3c_hsotg_driver);
|
||||
}
|
||||
|
||||
static void __exit s3c_hsotg_modexit(void)
|
||||
{
|
||||
platform_driver_unregister(&s3c_hsotg_driver);
|
||||
}
|
||||
|
||||
module_init(s3c_hsotg_modinit);
|
||||
module_exit(s3c_hsotg_modexit);
|
||||
module_platform_driver(s3c_hsotg_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Samsung S3C USB High-speed/OtG device");
|
||||
MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
|
||||
|
|
|
@ -28,9 +28,10 @@
|
|||
#include <linux/usb/gadget.h>
|
||||
#include <linux/usb/otg.h>
|
||||
#include <linux/prefetch.h>
|
||||
#include <linux/platform_data/s3c-hsudc.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#include <mach/regs-s3c2443-clock.h>
|
||||
#include <plat/udc.h>
|
||||
|
||||
#define S3C_HSUDC_REG(x) (x)
|
||||
|
||||
|
@ -87,6 +88,12 @@
|
|||
#define DATA_STATE_XMIT (1)
|
||||
#define DATA_STATE_RECV (2)
|
||||
|
||||
static const char * const s3c_hsudc_supply_names[] = {
|
||||
"vdda", /* analog phy supply, 3.3V */
|
||||
"vddi", /* digital phy supply, 1.2V */
|
||||
"vddosc", /* oscillator supply, 1.8V - 3.3V */
|
||||
};
|
||||
|
||||
/**
|
||||
* struct s3c_hsudc_ep - Endpoint representation used by driver.
|
||||
* @ep: USB gadget layer representation of device endpoint.
|
||||
|
@ -139,6 +146,7 @@ struct s3c_hsudc {
|
|||
struct device *dev;
|
||||
struct s3c24xx_hsudc_platdata *pd;
|
||||
struct otg_transceiver *transceiver;
|
||||
struct regulator_bulk_data supplies[ARRAY_SIZE(s3c_hsudc_supply_names)];
|
||||
spinlock_t lock;
|
||||
void __iomem *regs;
|
||||
struct resource *mem_rsrc;
|
||||
|
@ -153,7 +161,6 @@ struct s3c_hsudc {
|
|||
#define ep_index(_ep) ((_ep)->bEndpointAddress & \
|
||||
USB_ENDPOINT_NUMBER_MASK)
|
||||
|
||||
static struct s3c_hsudc *the_controller;
|
||||
static const char driver_name[] = "s3c-udc";
|
||||
static const char ep0name[] = "ep0-control";
|
||||
|
||||
|
@ -282,8 +289,7 @@ static void s3c_hsudc_nuke_ep(struct s3c_hsudc_ep *hsep, int status)
|
|||
* All the endpoints are stopped and any pending transfer requests if any on
|
||||
* the endpoint are terminated.
|
||||
*/
|
||||
static void s3c_hsudc_stop_activity(struct s3c_hsudc *hsudc,
|
||||
struct usb_gadget_driver *driver)
|
||||
static void s3c_hsudc_stop_activity(struct s3c_hsudc *hsudc)
|
||||
{
|
||||
struct s3c_hsudc_ep *hsep;
|
||||
int epnum;
|
||||
|
@ -295,10 +301,6 @@ static void s3c_hsudc_stop_activity(struct s3c_hsudc *hsudc,
|
|||
hsep->stopped = 1;
|
||||
s3c_hsudc_nuke_ep(hsep, -ESHUTDOWN);
|
||||
}
|
||||
|
||||
spin_unlock(&hsudc->lock);
|
||||
driver->disconnect(&hsudc->gadget);
|
||||
spin_lock(&hsudc->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1135,16 +1137,15 @@ static irqreturn_t s3c_hsudc_irq(int irq, void *_dev)
|
|||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int s3c_hsudc_start(struct usb_gadget_driver *driver,
|
||||
int (*bind)(struct usb_gadget *))
|
||||
static int s3c_hsudc_start(struct usb_gadget *gadget,
|
||||
struct usb_gadget_driver *driver)
|
||||
{
|
||||
struct s3c_hsudc *hsudc = the_controller;
|
||||
struct s3c_hsudc *hsudc = to_hsudc(gadget);
|
||||
int ret;
|
||||
|
||||
if (!driver
|
||||
|| driver->speed < USB_SPEED_FULL
|
||||
|| !bind
|
||||
|| !driver->unbind || !driver->disconnect || !driver->setup)
|
||||
|| driver->max_speed < USB_SPEED_FULL
|
||||
|| !driver->setup)
|
||||
return -EINVAL;
|
||||
|
||||
if (!hsudc)
|
||||
|
@ -1155,21 +1156,12 @@ static int s3c_hsudc_start(struct usb_gadget_driver *driver,
|
|||
|
||||
hsudc->driver = driver;
|
||||
hsudc->gadget.dev.driver = &driver->driver;
|
||||
hsudc->gadget.speed = USB_SPEED_UNKNOWN;
|
||||
ret = device_add(&hsudc->gadget.dev);
|
||||
if (ret) {
|
||||
dev_err(hsudc->dev, "failed to probe gadget device");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = bind(&hsudc->gadget);
|
||||
if (ret) {
|
||||
dev_err(hsudc->dev, "%s: bind failed\n", hsudc->gadget.name);
|
||||
device_del(&hsudc->gadget.dev);
|
||||
|
||||
hsudc->driver = NULL;
|
||||
hsudc->gadget.dev.driver = NULL;
|
||||
return ret;
|
||||
ret = regulator_bulk_enable(ARRAY_SIZE(hsudc->supplies),
|
||||
hsudc->supplies);
|
||||
if (ret != 0) {
|
||||
dev_err(hsudc->dev, "failed to enable supplies: %d\n", ret);
|
||||
goto err_supplies;
|
||||
}
|
||||
|
||||
/* connect to bus through transceiver */
|
||||
|
@ -1178,13 +1170,7 @@ static int s3c_hsudc_start(struct usb_gadget_driver *driver,
|
|||
if (ret) {
|
||||
dev_err(hsudc->dev, "%s: can't bind to transceiver\n",
|
||||
hsudc->gadget.name);
|
||||
driver->unbind(&hsudc->gadget);
|
||||
|
||||
device_del(&hsudc->gadget.dev);
|
||||
|
||||
hsudc->driver = NULL;
|
||||
hsudc->gadget.dev.driver = NULL;
|
||||
return ret;
|
||||
goto err_otg;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1197,34 +1183,43 @@ static int s3c_hsudc_start(struct usb_gadget_driver *driver,
|
|||
hsudc->pd->gpio_init();
|
||||
|
||||
return 0;
|
||||
err_otg:
|
||||
regulator_bulk_disable(ARRAY_SIZE(hsudc->supplies), hsudc->supplies);
|
||||
err_supplies:
|
||||
hsudc->driver = NULL;
|
||||
hsudc->gadget.dev.driver = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int s3c_hsudc_stop(struct usb_gadget_driver *driver)
|
||||
static int s3c_hsudc_stop(struct usb_gadget *gadget,
|
||||
struct usb_gadget_driver *driver)
|
||||
{
|
||||
struct s3c_hsudc *hsudc = the_controller;
|
||||
struct s3c_hsudc *hsudc = to_hsudc(gadget);
|
||||
unsigned long flags;
|
||||
|
||||
if (!hsudc)
|
||||
return -ENODEV;
|
||||
|
||||
if (!driver || driver != hsudc->driver || !driver->unbind)
|
||||
if (!driver || driver != hsudc->driver)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&hsudc->lock, flags);
|
||||
hsudc->driver = 0;
|
||||
hsudc->driver = NULL;
|
||||
hsudc->gadget.dev.driver = NULL;
|
||||
hsudc->gadget.speed = USB_SPEED_UNKNOWN;
|
||||
s3c_hsudc_uninit_phy();
|
||||
if (hsudc->pd->gpio_uninit)
|
||||
hsudc->pd->gpio_uninit();
|
||||
s3c_hsudc_stop_activity(hsudc, driver);
|
||||
s3c_hsudc_stop_activity(hsudc);
|
||||
spin_unlock_irqrestore(&hsudc->lock, flags);
|
||||
|
||||
if (hsudc->transceiver)
|
||||
(void) otg_set_peripheral(hsudc->transceiver, NULL);
|
||||
|
||||
driver->unbind(&hsudc->gadget);
|
||||
device_del(&hsudc->gadget.dev);
|
||||
disable_irq(hsudc->irq);
|
||||
|
||||
regulator_bulk_disable(ARRAY_SIZE(hsudc->supplies), hsudc->supplies);
|
||||
|
||||
dev_info(hsudc->dev, "unregistered gadget driver '%s'\n",
|
||||
driver->driver.name);
|
||||
return 0;
|
||||
|
@ -1242,7 +1237,7 @@ static int s3c_hsudc_gadget_getframe(struct usb_gadget *gadget)
|
|||
|
||||
static int s3c_hsudc_vbus_draw(struct usb_gadget *gadget, unsigned mA)
|
||||
{
|
||||
struct s3c_hsudc *hsudc = the_controller;
|
||||
struct s3c_hsudc *hsudc = to_hsudc(gadget);
|
||||
|
||||
if (!hsudc)
|
||||
return -ENODEV;
|
||||
|
@ -1255,18 +1250,18 @@ static int s3c_hsudc_vbus_draw(struct usb_gadget *gadget, unsigned mA)
|
|||
|
||||
static struct usb_gadget_ops s3c_hsudc_gadget_ops = {
|
||||
.get_frame = s3c_hsudc_gadget_getframe,
|
||||
.start = s3c_hsudc_start,
|
||||
.stop = s3c_hsudc_stop,
|
||||
.udc_start = s3c_hsudc_start,
|
||||
.udc_stop = s3c_hsudc_stop,
|
||||
.vbus_draw = s3c_hsudc_vbus_draw,
|
||||
};
|
||||
|
||||
static int s3c_hsudc_probe(struct platform_device *pdev)
|
||||
static int __devinit s3c_hsudc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct resource *res;
|
||||
struct s3c_hsudc *hsudc;
|
||||
struct s3c24xx_hsudc_platdata *pd = pdev->dev.platform_data;
|
||||
int ret;
|
||||
int ret, i;
|
||||
|
||||
hsudc = kzalloc(sizeof(struct s3c_hsudc) +
|
||||
sizeof(struct s3c_hsudc_ep) * pd->epnum,
|
||||
|
@ -1276,13 +1271,22 @@ static int s3c_hsudc_probe(struct platform_device *pdev)
|
|||
return -ENOMEM;
|
||||
}
|
||||
|
||||
the_controller = hsudc;
|
||||
platform_set_drvdata(pdev, dev);
|
||||
hsudc->dev = dev;
|
||||
hsudc->pd = pdev->dev.platform_data;
|
||||
|
||||
hsudc->transceiver = otg_get_transceiver();
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(hsudc->supplies); i++)
|
||||
hsudc->supplies[i].supply = s3c_hsudc_supply_names[i];
|
||||
|
||||
ret = regulator_bulk_get(dev, ARRAY_SIZE(hsudc->supplies),
|
||||
hsudc->supplies);
|
||||
if (ret != 0) {
|
||||
dev_err(dev, "failed to request supplies: %d\n", ret);
|
||||
goto err_supplies;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(dev, "unable to obtain driver resource data\n");
|
||||
|
@ -1307,10 +1311,9 @@ static int s3c_hsudc_probe(struct platform_device *pdev)
|
|||
|
||||
spin_lock_init(&hsudc->lock);
|
||||
|
||||
device_initialize(&hsudc->gadget.dev);
|
||||
dev_set_name(&hsudc->gadget.dev, "gadget");
|
||||
|
||||
hsudc->gadget.is_dualspeed = 1;
|
||||
hsudc->gadget.max_speed = USB_SPEED_HIGH;
|
||||
hsudc->gadget.ops = &s3c_hsudc_gadget_ops;
|
||||
hsudc->gadget.name = dev_name(dev);
|
||||
hsudc->gadget.dev.parent = dev;
|
||||
|
@ -1319,6 +1322,7 @@ static int s3c_hsudc_probe(struct platform_device *pdev)
|
|||
|
||||
hsudc->gadget.is_otg = 0;
|
||||
hsudc->gadget.is_a_peripheral = 0;
|
||||
hsudc->gadget.speed = USB_SPEED_UNKNOWN;
|
||||
|
||||
s3c_hsudc_setup_ep(hsudc);
|
||||
|
||||
|
@ -1348,12 +1352,20 @@ static int s3c_hsudc_probe(struct platform_device *pdev)
|
|||
disable_irq(hsudc->irq);
|
||||
local_irq_enable();
|
||||
|
||||
ret = device_register(&hsudc->gadget.dev);
|
||||
if (ret) {
|
||||
put_device(&hsudc->gadget.dev);
|
||||
goto err_add_device;
|
||||
}
|
||||
|
||||
ret = usb_add_gadget_udc(&pdev->dev, &hsudc->gadget);
|
||||
if (ret)
|
||||
goto err_add_udc;
|
||||
|
||||
return 0;
|
||||
err_add_udc:
|
||||
device_unregister(&hsudc->gadget.dev);
|
||||
err_add_device:
|
||||
clk_disable(hsudc->uclk);
|
||||
clk_put(hsudc->uclk);
|
||||
err_clk:
|
||||
|
@ -1362,10 +1374,13 @@ err_irq:
|
|||
iounmap(hsudc->regs);
|
||||
|
||||
err_remap:
|
||||
release_resource(hsudc->mem_rsrc);
|
||||
kfree(hsudc->mem_rsrc);
|
||||
|
||||
release_mem_region(res->start, resource_size(res));
|
||||
err_res:
|
||||
if (hsudc->transceiver)
|
||||
otg_put_transceiver(hsudc->transceiver);
|
||||
|
||||
regulator_bulk_free(ARRAY_SIZE(hsudc->supplies), hsudc->supplies);
|
||||
err_supplies:
|
||||
kfree(hsudc);
|
||||
return ret;
|
||||
}
|
||||
|
@ -1377,21 +1392,10 @@ static struct platform_driver s3c_hsudc_driver = {
|
|||
},
|
||||
.probe = s3c_hsudc_probe,
|
||||
};
|
||||
MODULE_ALIAS("platform:s3c-hsudc");
|
||||
|
||||
static int __init s3c_hsudc_modinit(void)
|
||||
{
|
||||
return platform_driver_register(&s3c_hsudc_driver);
|
||||
}
|
||||
|
||||
static void __exit s3c_hsudc_modexit(void)
|
||||
{
|
||||
platform_driver_unregister(&s3c_hsudc_driver);
|
||||
}
|
||||
|
||||
module_init(s3c_hsudc_modinit);
|
||||
module_exit(s3c_hsudc_modexit);
|
||||
module_platform_driver(s3c_hsudc_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Samsung S3C24XX USB high-speed controller driver");
|
||||
MODULE_AUTHOR("Thomas Abraham <thomas.ab@samsung.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:s3c-hsudc");
|
||||
|
|
|
@ -1683,9 +1683,9 @@ static int s3c2410_udc_start(struct usb_gadget_driver *driver,
|
|||
if (udc->driver)
|
||||
return -EBUSY;
|
||||
|
||||
if (!bind || !driver->setup || driver->speed < USB_SPEED_FULL) {
|
||||
if (!bind || !driver->setup || driver->max_speed < USB_SPEED_FULL) {
|
||||
printk(KERN_ERR "Invalid driver: bind %p setup %p speed %d\n",
|
||||
bind, driver->setup, driver->speed);
|
||||
bind, driver->setup, driver->max_speed);
|
||||
return -EINVAL;
|
||||
}
|
||||
#if defined(MODULE)
|
||||
|
|
|
@ -371,14 +371,28 @@ static ssize_t usb_udc_softconn_store(struct device *dev,
|
|||
}
|
||||
static DEVICE_ATTR(soft_connect, S_IWUSR, NULL, usb_udc_softconn_store);
|
||||
|
||||
static ssize_t usb_udc_speed_show(struct device *dev,
|
||||
#define USB_UDC_SPEED_ATTR(name, param) \
|
||||
ssize_t usb_udc_##param##_show(struct device *dev, \
|
||||
struct device_attribute *attr, char *buf) \
|
||||
{ \
|
||||
struct usb_udc *udc = container_of(dev, struct usb_udc, dev); \
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n", \
|
||||
usb_speed_string(udc->gadget->param)); \
|
||||
} \
|
||||
static DEVICE_ATTR(name, S_IRUSR, usb_udc_##param##_show, NULL)
|
||||
|
||||
static USB_UDC_SPEED_ATTR(current_speed, speed);
|
||||
static USB_UDC_SPEED_ATTR(maximum_speed, max_speed);
|
||||
|
||||
/* TODO: Scheduled for removal in 3.8. */
|
||||
static ssize_t usb_udc_is_dualspeed_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct usb_udc *udc = container_of(dev, struct usb_udc, dev);
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n",
|
||||
usb_speed_string(udc->gadget->speed));
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n",
|
||||
gadget_is_dualspeed(udc->gadget));
|
||||
}
|
||||
static DEVICE_ATTR(speed, S_IRUGO, usb_udc_speed_show, NULL);
|
||||
static DEVICE_ATTR(is_dualspeed, S_IRUSR, usb_udc_is_dualspeed_show, NULL);
|
||||
|
||||
#define USB_UDC_ATTR(name) \
|
||||
ssize_t usb_udc_##name##_show(struct device *dev, \
|
||||
|
@ -391,7 +405,6 @@ ssize_t usb_udc_##name##_show(struct device *dev, \
|
|||
} \
|
||||
static DEVICE_ATTR(name, S_IRUGO, usb_udc_##name##_show, NULL)
|
||||
|
||||
static USB_UDC_ATTR(is_dualspeed);
|
||||
static USB_UDC_ATTR(is_otg);
|
||||
static USB_UDC_ATTR(is_a_peripheral);
|
||||
static USB_UDC_ATTR(b_hnp_enable);
|
||||
|
@ -401,7 +414,8 @@ static USB_UDC_ATTR(a_alt_hnp_support);
|
|||
static struct attribute *usb_udc_attrs[] = {
|
||||
&dev_attr_srp.attr,
|
||||
&dev_attr_soft_connect.attr,
|
||||
&dev_attr_speed.attr,
|
||||
&dev_attr_current_speed.attr,
|
||||
&dev_attr_maximum_speed.attr,
|
||||
|
||||
&dev_attr_is_dualspeed.attr,
|
||||
&dev_attr_is_otg.attr,
|
||||
|
|
|
@ -13,82 +13,17 @@
|
|||
#include <linux/string.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/nls.h>
|
||||
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
|
||||
static int utf8_to_utf16le(const char *s, __le16 *cp, unsigned len)
|
||||
{
|
||||
int count = 0;
|
||||
u8 c;
|
||||
u16 uchar;
|
||||
|
||||
/* this insists on correct encodings, though not minimal ones.
|
||||
* BUT it currently rejects legit 4-byte UTF-8 code points,
|
||||
* which need surrogate pairs. (Unicode 3.1 can use them.)
|
||||
*/
|
||||
while (len != 0 && (c = (u8) *s++) != 0) {
|
||||
if (unlikely(c & 0x80)) {
|
||||
// 2-byte sequence:
|
||||
// 00000yyyyyxxxxxx = 110yyyyy 10xxxxxx
|
||||
if ((c & 0xe0) == 0xc0) {
|
||||
uchar = (c & 0x1f) << 6;
|
||||
|
||||
c = (u8) *s++;
|
||||
if ((c & 0xc0) != 0x80)
|
||||
goto fail;
|
||||
c &= 0x3f;
|
||||
uchar |= c;
|
||||
|
||||
// 3-byte sequence (most CJKV characters):
|
||||
// zzzzyyyyyyxxxxxx = 1110zzzz 10yyyyyy 10xxxxxx
|
||||
} else if ((c & 0xf0) == 0xe0) {
|
||||
uchar = (c & 0x0f) << 12;
|
||||
|
||||
c = (u8) *s++;
|
||||
if ((c & 0xc0) != 0x80)
|
||||
goto fail;
|
||||
c &= 0x3f;
|
||||
uchar |= c << 6;
|
||||
|
||||
c = (u8) *s++;
|
||||
if ((c & 0xc0) != 0x80)
|
||||
goto fail;
|
||||
c &= 0x3f;
|
||||
uchar |= c;
|
||||
|
||||
/* no bogus surrogates */
|
||||
if (0xd800 <= uchar && uchar <= 0xdfff)
|
||||
goto fail;
|
||||
|
||||
// 4-byte sequence (surrogate pairs, currently rare):
|
||||
// 11101110wwwwzzzzyy + 110111yyyyxxxxxx
|
||||
// = 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx
|
||||
// (uuuuu = wwww + 1)
|
||||
// FIXME accept the surrogate code points (only)
|
||||
|
||||
} else
|
||||
goto fail;
|
||||
} else
|
||||
uchar = c;
|
||||
put_unaligned_le16(uchar, cp++);
|
||||
count++;
|
||||
len--;
|
||||
}
|
||||
return count;
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* usb_gadget_get_string - fill out a string descriptor
|
||||
* @table: of c strings encoded using UTF-8
|
||||
* @id: string id, from low byte of wValue in get string descriptor
|
||||
* @buf: at least 256 bytes
|
||||
* @buf: at least 256 bytes, must be 16-bit aligned
|
||||
*
|
||||
* Finds the UTF-8 string matching the ID, and converts it into a
|
||||
* string descriptor in utf16-le.
|
||||
|
@ -125,8 +60,8 @@ usb_gadget_get_string (struct usb_gadget_strings *table, int id, u8 *buf)
|
|||
|
||||
/* string descriptors have length, tag, then UTF16-LE text */
|
||||
len = min ((size_t) 126, strlen (s->s));
|
||||
memset (buf + 2, 0, 2 * len); /* zero all the bytes */
|
||||
len = utf8_to_utf16le(s->s, (__le16 *)&buf[2], len);
|
||||
len = utf8s_to_utf16s(s->s, len, UTF16_LITTLE_ENDIAN,
|
||||
(wchar_t *) &buf[2], 126);
|
||||
if (len < 0)
|
||||
return -EINVAL;
|
||||
buf [0] = (len + 1) * 2;
|
||||
|
|
|
@ -194,6 +194,15 @@ config USB_EHCI_S5P
|
|||
help
|
||||
Enable support for the S5P SOC's on-chip EHCI controller.
|
||||
|
||||
config USB_EHCI_MV
|
||||
bool "EHCI support for Marvell on-chip controller"
|
||||
depends on USB_EHCI_HCD
|
||||
select USB_EHCI_ROOT_HUB_TT
|
||||
---help---
|
||||
Enables support for Marvell (including PXA and MMP series) on-chip
|
||||
USB SPH and OTG controller. SPH is a single port host, and it can
|
||||
only be EHCI host. OTG is controller that can switch to host mode.
|
||||
|
||||
config USB_W90X900_EHCI
|
||||
bool "W90X900(W90P910) EHCI support"
|
||||
depends on USB_EHCI_HCD && ARCH_W90X900
|
||||
|
|
|
@ -23,6 +23,7 @@ static int au1xxx_ehci_setup(struct usb_hcd *hcd)
|
|||
int ret = ehci_init(hcd);
|
||||
|
||||
ehci->need_io_watchdog = 0;
|
||||
ehci_reset(ehci);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -48,6 +48,10 @@
|
|||
#include <asm/system.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#if defined(CONFIG_PPC_PS3)
|
||||
#include <asm/firmware.h>
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
|
@ -230,12 +234,58 @@ static int ehci_halt (struct ehci_hcd *ehci)
|
|||
STS_HALT, STS_HALT, 16 * 125);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_USB_SUSPEND) && defined(CONFIG_PPC_PS3)
|
||||
|
||||
/*
|
||||
* The EHCI controller of the Cell Super Companion Chip used in the
|
||||
* PS3 will stop the root hub after all root hub ports are suspended.
|
||||
* When in this condition handshake will return -ETIMEDOUT. The
|
||||
* STS_HLT bit will not be set, so inspection of the frame index is
|
||||
* used here to test for the condition. If the condition is found
|
||||
* return success to allow the USB suspend to complete.
|
||||
*/
|
||||
|
||||
static int handshake_for_broken_root_hub(struct ehci_hcd *ehci,
|
||||
void __iomem *ptr, u32 mask, u32 done,
|
||||
int usec)
|
||||
{
|
||||
unsigned int old_index;
|
||||
int error;
|
||||
|
||||
if (!firmware_has_feature(FW_FEATURE_PS3_LV1))
|
||||
return -ETIMEDOUT;
|
||||
|
||||
old_index = ehci_read_frame_index(ehci);
|
||||
|
||||
error = handshake(ehci, ptr, mask, done, usec);
|
||||
|
||||
if (error == -ETIMEDOUT && ehci_read_frame_index(ehci) == old_index)
|
||||
return 0;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static int handshake_for_broken_root_hub(struct ehci_hcd *ehci,
|
||||
void __iomem *ptr, u32 mask, u32 done,
|
||||
int usec)
|
||||
{
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static int handshake_on_error_set_halt(struct ehci_hcd *ehci, void __iomem *ptr,
|
||||
u32 mask, u32 done, int usec)
|
||||
{
|
||||
int error;
|
||||
|
||||
error = handshake(ehci, ptr, mask, done, usec);
|
||||
if (error == -ETIMEDOUT)
|
||||
error = handshake_for_broken_root_hub(ehci, ptr, mask, done,
|
||||
usec);
|
||||
|
||||
if (error) {
|
||||
ehci_halt(ehci);
|
||||
ehci->rh_state = EHCI_RH_HALTED;
|
||||
|
@ -620,6 +670,7 @@ static int ehci_init(struct usb_hcd *hcd)
|
|||
hw = ehci->async->hw;
|
||||
hw->hw_next = QH_NEXT(ehci, ehci->async->qh_dma);
|
||||
hw->hw_info1 = cpu_to_hc32(ehci, QH_HEAD);
|
||||
hw->hw_info1 |= cpu_to_hc32(ehci, (1 << 7)); /* I = 1 */
|
||||
hw->hw_token = cpu_to_hc32(ehci, QTD_STS_HALT);
|
||||
hw->hw_qtd_next = EHCI_LIST_END(ehci);
|
||||
ehci->async->qh_state = QH_STATE_LINKED;
|
||||
|
@ -677,22 +728,13 @@ static int ehci_init(struct usb_hcd *hcd)
|
|||
static int ehci_run (struct usb_hcd *hcd)
|
||||
{
|
||||
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
|
||||
int retval;
|
||||
u32 temp;
|
||||
u32 hcc_params;
|
||||
|
||||
hcd->uses_new_polling = 1;
|
||||
|
||||
/* EHCI spec section 4.1 */
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
ehci_writel(ehci, ehci->periodic_dma, &ehci->regs->frame_list);
|
||||
ehci_writel(ehci, (u32)ehci->async->qh_dma, &ehci->regs->async_next);
|
||||
|
||||
|
@ -1324,11 +1366,16 @@ MODULE_LICENSE ("GPL");
|
|||
#define PLATFORM_DRIVER ehci_pxa168_driver
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_NLM_XLR
|
||||
#ifdef CONFIG_CPU_XLR
|
||||
#include "ehci-xls.c"
|
||||
#define PLATFORM_DRIVER ehci_xls_driver
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_EHCI_MV
|
||||
#include "ehci-mv.c"
|
||||
#define PLATFORM_DRIVER ehci_mv_driver
|
||||
#endif
|
||||
|
||||
#if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \
|
||||
!defined(PS3_SYSTEM_BUS_DRIVER) && !defined(OF_PLATFORM_DRIVER) && \
|
||||
!defined(XILINX_OF_PLATFORM_DRIVER)
|
||||
|
|
|
@ -0,0 +1,391 @@
|
|||
/*
|
||||
* Copyright (C) 2011 Marvell International Ltd. All rights reserved.
|
||||
* Author: Chao Xie <chao.xie@marvell.com>
|
||||
* Neil Zhang <zhangwm@marvell.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; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/usb/otg.h>
|
||||
#include <linux/platform_data/mv_usb.h>
|
||||
|
||||
#define CAPLENGTH_MASK (0xff)
|
||||
|
||||
struct ehci_hcd_mv {
|
||||
struct usb_hcd *hcd;
|
||||
|
||||
/* Which mode does this ehci running OTG/Host ? */
|
||||
int mode;
|
||||
|
||||
void __iomem *phy_regs;
|
||||
void __iomem *cap_regs;
|
||||
void __iomem *op_regs;
|
||||
|
||||
struct otg_transceiver *otg;
|
||||
|
||||
struct mv_usb_platform_data *pdata;
|
||||
|
||||
/* clock source and total clock number */
|
||||
unsigned int clknum;
|
||||
struct clk *clk[0];
|
||||
};
|
||||
|
||||
static void ehci_clock_enable(struct ehci_hcd_mv *ehci_mv)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ehci_mv->clknum; i++)
|
||||
clk_enable(ehci_mv->clk[i]);
|
||||
}
|
||||
|
||||
static void ehci_clock_disable(struct ehci_hcd_mv *ehci_mv)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ehci_mv->clknum; i++)
|
||||
clk_disable(ehci_mv->clk[i]);
|
||||
}
|
||||
|
||||
static int mv_ehci_enable(struct ehci_hcd_mv *ehci_mv)
|
||||
{
|
||||
int retval;
|
||||
|
||||
ehci_clock_enable(ehci_mv);
|
||||
if (ehci_mv->pdata->phy_init) {
|
||||
retval = ehci_mv->pdata->phy_init(ehci_mv->phy_regs);
|
||||
if (retval)
|
||||
return retval;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mv_ehci_disable(struct ehci_hcd_mv *ehci_mv)
|
||||
{
|
||||
if (ehci_mv->pdata->phy_deinit)
|
||||
ehci_mv->pdata->phy_deinit(ehci_mv->phy_regs);
|
||||
ehci_clock_disable(ehci_mv);
|
||||
}
|
||||
|
||||
static int mv_ehci_reset(struct usb_hcd *hcd)
|
||||
{
|
||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||
struct device *dev = hcd->self.controller;
|
||||
struct ehci_hcd_mv *ehci_mv = dev_get_drvdata(dev);
|
||||
int retval;
|
||||
|
||||
if (ehci_mv == NULL) {
|
||||
dev_err(dev, "Can not find private ehci data\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/*
|
||||
* data structure init
|
||||
*/
|
||||
retval = ehci_init(hcd);
|
||||
if (retval) {
|
||||
dev_err(dev, "ehci_init failed %d\n", retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
hcd->has_tt = 1;
|
||||
ehci->sbrn = 0x20;
|
||||
|
||||
retval = ehci_reset(ehci);
|
||||
if (retval) {
|
||||
dev_err(dev, "ehci_reset failed %d\n", retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct hc_driver mv_ehci_hc_driver = {
|
||||
.description = hcd_name,
|
||||
.product_desc = "Marvell EHCI",
|
||||
.hcd_priv_size = sizeof(struct ehci_hcd),
|
||||
|
||||
/*
|
||||
* generic hardware linkage
|
||||
*/
|
||||
.irq = ehci_irq,
|
||||
.flags = HCD_MEMORY | HCD_USB2,
|
||||
|
||||
/*
|
||||
* basic lifecycle operations
|
||||
*/
|
||||
.reset = mv_ehci_reset,
|
||||
.start = ehci_run,
|
||||
.stop = ehci_stop,
|
||||
.shutdown = ehci_shutdown,
|
||||
|
||||
/*
|
||||
* managing i/o requests and associated device resources
|
||||
*/
|
||||
.urb_enqueue = ehci_urb_enqueue,
|
||||
.urb_dequeue = ehci_urb_dequeue,
|
||||
.endpoint_disable = ehci_endpoint_disable,
|
||||
.endpoint_reset = ehci_endpoint_reset,
|
||||
.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
|
||||
|
||||
/*
|
||||
* scheduling support
|
||||
*/
|
||||
.get_frame_number = ehci_get_frame,
|
||||
|
||||
/*
|
||||
* root hub support
|
||||
*/
|
||||
.hub_status_data = ehci_hub_status_data,
|
||||
.hub_control = ehci_hub_control,
|
||||
.bus_suspend = ehci_bus_suspend,
|
||||
.bus_resume = ehci_bus_resume,
|
||||
};
|
||||
|
||||
static int mv_ehci_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct mv_usb_platform_data *pdata = pdev->dev.platform_data;
|
||||
struct usb_hcd *hcd;
|
||||
struct ehci_hcd *ehci;
|
||||
struct ehci_hcd_mv *ehci_mv;
|
||||
struct resource *r;
|
||||
int clk_i, retval = -ENODEV;
|
||||
u32 offset;
|
||||
size_t size;
|
||||
|
||||
if (!pdata) {
|
||||
dev_err(&pdev->dev, "missing platform_data\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (usb_disabled())
|
||||
return -ENODEV;
|
||||
|
||||
hcd = usb_create_hcd(&mv_ehci_hc_driver, &pdev->dev, "mv ehci");
|
||||
if (!hcd)
|
||||
return -ENOMEM;
|
||||
|
||||
size = sizeof(*ehci_mv) + sizeof(struct clk *) * pdata->clknum;
|
||||
ehci_mv = kzalloc(size, GFP_KERNEL);
|
||||
if (ehci_mv == NULL) {
|
||||
dev_err(&pdev->dev, "cannot allocate ehci_hcd_mv\n");
|
||||
retval = -ENOMEM;
|
||||
goto err_put_hcd;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, ehci_mv);
|
||||
ehci_mv->pdata = pdata;
|
||||
ehci_mv->hcd = hcd;
|
||||
|
||||
ehci_mv->clknum = pdata->clknum;
|
||||
for (clk_i = 0; clk_i < ehci_mv->clknum; clk_i++) {
|
||||
ehci_mv->clk[clk_i] =
|
||||
clk_get(&pdev->dev, pdata->clkname[clk_i]);
|
||||
if (IS_ERR(ehci_mv->clk[clk_i])) {
|
||||
dev_err(&pdev->dev, "error get clck \"%s\"\n",
|
||||
pdata->clkname[clk_i]);
|
||||
retval = PTR_ERR(ehci_mv->clk[clk_i]);
|
||||
goto err_put_clk;
|
||||
}
|
||||
}
|
||||
|
||||
r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phyregs");
|
||||
if (r == NULL) {
|
||||
dev_err(&pdev->dev, "no phy I/O memory resource defined\n");
|
||||
retval = -ENODEV;
|
||||
goto err_put_clk;
|
||||
}
|
||||
|
||||
ehci_mv->phy_regs = ioremap(r->start, resource_size(r));
|
||||
if (ehci_mv->phy_regs == 0) {
|
||||
dev_err(&pdev->dev, "failed to map phy I/O memory\n");
|
||||
retval = -EFAULT;
|
||||
goto err_put_clk;
|
||||
}
|
||||
|
||||
r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "capregs");
|
||||
if (!r) {
|
||||
dev_err(&pdev->dev, "no I/O memory resource defined\n");
|
||||
retval = -ENODEV;
|
||||
goto err_iounmap_phyreg;
|
||||
}
|
||||
|
||||
ehci_mv->cap_regs = ioremap(r->start, resource_size(r));
|
||||
if (ehci_mv->cap_regs == NULL) {
|
||||
dev_err(&pdev->dev, "failed to map I/O memory\n");
|
||||
retval = -EFAULT;
|
||||
goto err_iounmap_phyreg;
|
||||
}
|
||||
|
||||
retval = mv_ehci_enable(ehci_mv);
|
||||
if (retval) {
|
||||
dev_err(&pdev->dev, "init phy error %d\n", retval);
|
||||
goto err_iounmap_capreg;
|
||||
}
|
||||
|
||||
offset = readl(ehci_mv->cap_regs) & CAPLENGTH_MASK;
|
||||
ehci_mv->op_regs =
|
||||
(void __iomem *) ((unsigned long) ehci_mv->cap_regs + offset);
|
||||
|
||||
hcd->rsrc_start = r->start;
|
||||
hcd->rsrc_len = r->end - r->start + 1;
|
||||
hcd->regs = ehci_mv->op_regs;
|
||||
|
||||
hcd->irq = platform_get_irq(pdev, 0);
|
||||
if (!hcd->irq) {
|
||||
dev_err(&pdev->dev, "Cannot get irq.");
|
||||
retval = -ENODEV;
|
||||
goto err_disable_clk;
|
||||
}
|
||||
|
||||
ehci = hcd_to_ehci(hcd);
|
||||
ehci->caps = (struct ehci_caps *) ehci_mv->cap_regs;
|
||||
ehci->regs = (struct ehci_regs *) ehci_mv->op_regs;
|
||||
ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
|
||||
|
||||
ehci_mv->mode = pdata->mode;
|
||||
if (ehci_mv->mode == MV_USB_MODE_OTG) {
|
||||
#ifdef CONFIG_USB_OTG_UTILS
|
||||
ehci_mv->otg = otg_get_transceiver();
|
||||
if (!ehci_mv->otg) {
|
||||
dev_err(&pdev->dev,
|
||||
"unable to find transceiver\n");
|
||||
retval = -ENODEV;
|
||||
goto err_disable_clk;
|
||||
}
|
||||
|
||||
retval = otg_set_host(ehci_mv->otg, &hcd->self);
|
||||
if (retval < 0) {
|
||||
dev_err(&pdev->dev,
|
||||
"unable to register with transceiver\n");
|
||||
retval = -ENODEV;
|
||||
goto err_put_transceiver;
|
||||
}
|
||||
/* otg will enable clock before use as host */
|
||||
mv_ehci_disable(ehci_mv);
|
||||
#else
|
||||
dev_info(&pdev->dev, "MV_USB_MODE_OTG "
|
||||
"must have CONFIG_USB_OTG_UTILS enabled\n");
|
||||
goto err_disable_clk;
|
||||
#endif
|
||||
} else {
|
||||
if (pdata->set_vbus)
|
||||
pdata->set_vbus(1);
|
||||
|
||||
retval = usb_add_hcd(hcd, hcd->irq, IRQF_SHARED);
|
||||
if (retval) {
|
||||
dev_err(&pdev->dev,
|
||||
"failed to add hcd with err %d\n", retval);
|
||||
goto err_set_vbus;
|
||||
}
|
||||
}
|
||||
|
||||
if (pdata->private_init)
|
||||
pdata->private_init(ehci_mv->op_regs, ehci_mv->phy_regs);
|
||||
|
||||
dev_info(&pdev->dev,
|
||||
"successful find EHCI device with regs 0x%p irq %d"
|
||||
" working in %s mode\n", hcd->regs, hcd->irq,
|
||||
ehci_mv->mode == MV_USB_MODE_OTG ? "OTG" : "Host");
|
||||
|
||||
return 0;
|
||||
|
||||
err_set_vbus:
|
||||
if (pdata->set_vbus)
|
||||
pdata->set_vbus(0);
|
||||
#ifdef CONFIG_USB_OTG_UTILS
|
||||
err_put_transceiver:
|
||||
if (ehci_mv->otg)
|
||||
otg_put_transceiver(ehci_mv->otg);
|
||||
#endif
|
||||
err_disable_clk:
|
||||
mv_ehci_disable(ehci_mv);
|
||||
err_iounmap_capreg:
|
||||
iounmap(ehci_mv->cap_regs);
|
||||
err_iounmap_phyreg:
|
||||
iounmap(ehci_mv->phy_regs);
|
||||
err_put_clk:
|
||||
for (clk_i--; clk_i >= 0; clk_i--)
|
||||
clk_put(ehci_mv->clk[clk_i]);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
kfree(ehci_mv);
|
||||
err_put_hcd:
|
||||
usb_put_hcd(hcd);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int mv_ehci_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ehci_hcd_mv *ehci_mv = platform_get_drvdata(pdev);
|
||||
struct usb_hcd *hcd = ehci_mv->hcd;
|
||||
int clk_i;
|
||||
|
||||
if (hcd->rh_registered)
|
||||
usb_remove_hcd(hcd);
|
||||
|
||||
if (ehci_mv->otg) {
|
||||
otg_set_host(ehci_mv->otg, NULL);
|
||||
otg_put_transceiver(ehci_mv->otg);
|
||||
}
|
||||
|
||||
if (ehci_mv->mode == MV_USB_MODE_HOST) {
|
||||
if (ehci_mv->pdata->set_vbus)
|
||||
ehci_mv->pdata->set_vbus(0);
|
||||
|
||||
mv_ehci_disable(ehci_mv);
|
||||
}
|
||||
|
||||
iounmap(ehci_mv->cap_regs);
|
||||
iounmap(ehci_mv->phy_regs);
|
||||
|
||||
for (clk_i = 0; clk_i < ehci_mv->clknum; clk_i++)
|
||||
clk_put(ehci_mv->clk[clk_i]);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
kfree(ehci_mv);
|
||||
usb_put_hcd(hcd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
MODULE_ALIAS("mv-ehci");
|
||||
|
||||
static const struct platform_device_id ehci_id_table[] = {
|
||||
{"pxa-u2oehci", PXA_U2OEHCI},
|
||||
{"pxa-sph", PXA_SPH},
|
||||
{"mmp3-hsic", MMP3_HSIC},
|
||||
{"mmp3-fsic", MMP3_FSIC},
|
||||
{},
|
||||
};
|
||||
|
||||
static void mv_ehci_shutdown(struct platform_device *pdev)
|
||||
{
|
||||
struct ehci_hcd_mv *ehci_mv = platform_get_drvdata(pdev);
|
||||
struct usb_hcd *hcd = ehci_mv->hcd;
|
||||
|
||||
if (!hcd->rh_registered)
|
||||
return;
|
||||
|
||||
if (hcd->driver->shutdown)
|
||||
hcd->driver->shutdown(hcd);
|
||||
}
|
||||
|
||||
static struct platform_driver ehci_mv_driver = {
|
||||
.probe = mv_ehci_probe,
|
||||
.remove = mv_ehci_remove,
|
||||
.shutdown = mv_ehci_shutdown,
|
||||
.driver = {
|
||||
.name = "mv-ehci",
|
||||
.bus = &platform_bus_type,
|
||||
},
|
||||
.id_table = ehci_id_table,
|
||||
};
|
|
@ -155,6 +155,8 @@ static int ehci_octeon_drv_probe(struct platform_device *pdev)
|
|||
/* cache this readonly data; minimize chip reads */
|
||||
ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
|
||||
|
||||
ehci_reset(ehci);
|
||||
|
||||
ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
|
||||
if (ret) {
|
||||
dev_dbg(&pdev->dev, "failed to add hcd with err %d\n", ret);
|
||||
|
|
|
@ -228,6 +228,8 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev)
|
|||
/* cache this readonly data; minimize chip reads */
|
||||
omap_ehci->hcs_params = readl(&omap_ehci->caps->hcs_params);
|
||||
|
||||
ehci_reset(omap_ehci);
|
||||
|
||||
ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to add hcd with err %d\n", ret);
|
||||
|
|
|
@ -21,6 +21,34 @@
|
|||
#include <asm/firmware.h>
|
||||
#include <asm/ps3.h>
|
||||
|
||||
static void ps3_ehci_setup_insnreg(struct ehci_hcd *ehci)
|
||||
{
|
||||
/* PS3 HC internal setup register offsets. */
|
||||
|
||||
enum ps3_ehci_hc_insnreg {
|
||||
ps3_ehci_hc_insnreg01 = 0x084,
|
||||
ps3_ehci_hc_insnreg02 = 0x088,
|
||||
ps3_ehci_hc_insnreg03 = 0x08c,
|
||||
};
|
||||
|
||||
/* PS3 EHCI HC errata fix 316 - The PS3 EHCI HC will reset its
|
||||
* internal INSNREGXX setup regs back to the chip default values
|
||||
* on Host Controller Reset (CMD_RESET) or Light Host Controller
|
||||
* Reset (CMD_LRESET). The work-around for this is for the HC
|
||||
* driver to re-initialise these regs when ever the HC is reset.
|
||||
*/
|
||||
|
||||
/* Set burst transfer counts to 256 out, 32 in. */
|
||||
|
||||
writel_be(0x01000020, (void __iomem *)ehci->regs +
|
||||
ps3_ehci_hc_insnreg01);
|
||||
|
||||
/* Enable burst transfer counts. */
|
||||
|
||||
writel_be(0x00000001, (void __iomem *)ehci->regs +
|
||||
ps3_ehci_hc_insnreg03);
|
||||
}
|
||||
|
||||
static int ps3_ehci_hc_reset(struct usb_hcd *hcd)
|
||||
{
|
||||
int result;
|
||||
|
@ -49,6 +77,8 @@ static int ps3_ehci_hc_reset(struct usb_hcd *hcd)
|
|||
|
||||
ehci_reset(ehci);
|
||||
|
||||
ps3_ehci_setup_insnreg(ehci);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -299,7 +299,7 @@ static int __devinit ehci_pxa168_drv_probe(struct platform_device *pdev)
|
|||
ehci = hcd_to_ehci(hcd);
|
||||
ehci->caps = hcd->regs + 0x100;
|
||||
ehci->regs = hcd->regs + 0x100 +
|
||||
HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase));
|
||||
HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase));
|
||||
ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
|
||||
hcd->has_tt = 1;
|
||||
ehci->sbrn = 0x20;
|
||||
|
|
|
@ -373,6 +373,17 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
|
|||
retry_xacterr:
|
||||
if ((token & QTD_STS_ACTIVE) == 0) {
|
||||
|
||||
/* Report Data Buffer Error: non-fatal but useful */
|
||||
if (token & QTD_STS_DBE)
|
||||
ehci_dbg(ehci,
|
||||
"detected DataBufferErr for urb %p ep%d%s len %d, qtd %p [qh %p]\n",
|
||||
urb,
|
||||
usb_endpoint_num(&urb->ep->desc),
|
||||
usb_endpoint_dir_in(&urb->ep->desc) ? "in" : "out",
|
||||
urb->transfer_buffer_length,
|
||||
qtd,
|
||||
qh);
|
||||
|
||||
/* on STALL, error, and short reads this urb must
|
||||
* complete and all its qtds must be recycled.
|
||||
*/
|
||||
|
@ -647,7 +658,7 @@ qh_urb_transaction (
|
|||
/*
|
||||
* data transfer stage: buffer setup
|
||||
*/
|
||||
i = urb->num_sgs;
|
||||
i = urb->num_mapped_sgs;
|
||||
if (len > 0 && i > 0) {
|
||||
sg = urb->sg;
|
||||
buf = sg_dma_address(sg);
|
||||
|
|
|
@ -14,8 +14,6 @@
|
|||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <mach/regs-pmu.h>
|
||||
#include <plat/cpu.h>
|
||||
#include <plat/ehci.h>
|
||||
#include <plat/usb-phy.h>
|
||||
|
||||
|
@ -136,6 +134,8 @@ static int __devinit s5p_ehci_probe(struct platform_device *pdev)
|
|||
/* cache this readonly data; minimize chip reads */
|
||||
ehci->hcs_params = readl(&ehci->caps->hcs_params);
|
||||
|
||||
ehci_reset(ehci);
|
||||
|
||||
err = usb_add_hcd(hcd, irq, IRQF_SHARED);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "Failed to add USB HCD\n");
|
||||
|
|
|
@ -132,6 +132,8 @@ static int vt8500_ehci_drv_probe(struct platform_device *pdev)
|
|||
|
||||
ehci_port_power(ehci, 1);
|
||||
|
||||
ehci_reset(ehci);
|
||||
|
||||
ret = usb_add_hcd(hcd, pdev->resource[1].start,
|
||||
IRQF_SHARED);
|
||||
if (ret == 0) {
|
||||
|
|
|
@ -78,6 +78,8 @@ static int __devinit usb_w90x900_probe(const struct hc_driver *driver,
|
|||
if (irq < 0)
|
||||
goto err4;
|
||||
|
||||
ehci_reset(ehci);
|
||||
|
||||
retval = usb_add_hcd(hcd, irq, IRQF_SHARED);
|
||||
if (retval != 0)
|
||||
goto err4;
|
||||
|
|
|
@ -69,7 +69,7 @@ int ehci_xls_probe_internal(const struct hc_driver *driver,
|
|||
}
|
||||
|
||||
hcd->rsrc_start = res->start;
|
||||
hcd->rsrc_len = res->end - res->start + 1;
|
||||
hcd->rsrc_len = resource_size(res);
|
||||
|
||||
if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len,
|
||||
driver->description)) {
|
||||
|
|
|
@ -824,17 +824,7 @@ static struct platform_driver of_fhci_driver = {
|
|||
.remove = __devexit_p(of_fhci_remove),
|
||||
};
|
||||
|
||||
static int __init fhci_module_init(void)
|
||||
{
|
||||
return platform_driver_register(&of_fhci_driver);
|
||||
}
|
||||
module_init(fhci_module_init);
|
||||
|
||||
static void __exit fhci_module_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&of_fhci_driver);
|
||||
}
|
||||
module_exit(fhci_module_exit);
|
||||
module_platform_driver(of_fhci_driver);
|
||||
|
||||
MODULE_DESCRIPTION("USB Freescale Host Controller Interface Driver");
|
||||
MODULE_AUTHOR("Shlomi Gridish <gridish@freescale.com>, "
|
||||
|
|
|
@ -297,17 +297,7 @@ static struct platform_driver fsl_usb2_mph_dr_driver = {
|
|||
.remove = __devexit_p(fsl_usb2_mph_dr_of_remove),
|
||||
};
|
||||
|
||||
static int __init fsl_usb2_mph_dr_init(void)
|
||||
{
|
||||
return platform_driver_register(&fsl_usb2_mph_dr_driver);
|
||||
}
|
||||
module_init(fsl_usb2_mph_dr_init);
|
||||
|
||||
static void __exit fsl_usb2_mph_dr_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&fsl_usb2_mph_dr_driver);
|
||||
}
|
||||
module_exit(fsl_usb2_mph_dr_exit);
|
||||
module_platform_driver(fsl_usb2_mph_dr_driver);
|
||||
|
||||
MODULE_DESCRIPTION("FSL MPH DR OF devices driver");
|
||||
MODULE_AUTHOR("Anatolij Gustschin <agust@denx.de>");
|
||||
|
|
|
@ -776,7 +776,6 @@ static int hwahc_probe(struct usb_interface *usb_iface,
|
|||
goto error_alloc;
|
||||
}
|
||||
usb_hcd->wireless = 1;
|
||||
set_bit(HCD_FLAG_SAW_IRQ, &usb_hcd->flags);
|
||||
wusbhc = usb_hcd_to_wusbhc(usb_hcd);
|
||||
hwahc = container_of(wusbhc, struct hwahc, wusbhc);
|
||||
hwahc_init(hwahc);
|
||||
|
|
|
@ -1924,18 +1924,7 @@ static struct platform_driver imx21_hcd_driver = {
|
|||
.resume = NULL,
|
||||
};
|
||||
|
||||
static int __init imx21_hcd_init(void)
|
||||
{
|
||||
return platform_driver_register(&imx21_hcd_driver);
|
||||
}
|
||||
|
||||
static void __exit imx21_hcd_cleanup(void)
|
||||
{
|
||||
platform_driver_unregister(&imx21_hcd_driver);
|
||||
}
|
||||
|
||||
module_init(imx21_hcd_init);
|
||||
module_exit(imx21_hcd_cleanup);
|
||||
module_platform_driver(imx21_hcd_driver);
|
||||
|
||||
MODULE_DESCRIPTION("i.MX21 USB Host controller");
|
||||
MODULE_AUTHOR("Martin Fuzzey");
|
||||
|
|
|
@ -32,6 +32,13 @@ static struct kmem_cache *qtd_cachep;
|
|||
static struct kmem_cache *qh_cachep;
|
||||
static struct kmem_cache *urb_listitem_cachep;
|
||||
|
||||
enum queue_head_types {
|
||||
QH_CONTROL,
|
||||
QH_BULK,
|
||||
QH_INTERRUPT,
|
||||
QH_END
|
||||
};
|
||||
|
||||
struct isp1760_hcd {
|
||||
u32 hcs_params;
|
||||
spinlock_t lock;
|
||||
|
@ -40,7 +47,7 @@ struct isp1760_hcd {
|
|||
struct slotinfo int_slots[32];
|
||||
int int_done_map;
|
||||
struct memory_chunk memory_pool[BLOCKS];
|
||||
struct list_head controlqhs, bulkqhs, interruptqhs;
|
||||
struct list_head qh_list[QH_END];
|
||||
|
||||
/* periodic schedule support */
|
||||
#define DEFAULT_I_TDPS 1024
|
||||
|
@ -406,12 +413,12 @@ static int priv_init(struct usb_hcd *hcd)
|
|||
{
|
||||
struct isp1760_hcd *priv = hcd_to_priv(hcd);
|
||||
u32 hcc_params;
|
||||
int i;
|
||||
|
||||
spin_lock_init(&priv->lock);
|
||||
|
||||
INIT_LIST_HEAD(&priv->interruptqhs);
|
||||
INIT_LIST_HEAD(&priv->controlqhs);
|
||||
INIT_LIST_HEAD(&priv->bulkqhs);
|
||||
for (i = 0; i < QH_END; i++)
|
||||
INIT_LIST_HEAD(&priv->qh_list[i]);
|
||||
|
||||
/*
|
||||
* hw default: 1K periodic list heads, one per frame.
|
||||
|
@ -930,9 +937,9 @@ void schedule_ptds(struct usb_hcd *hcd)
|
|||
struct isp1760_hcd *priv;
|
||||
struct isp1760_qh *qh, *qh_next;
|
||||
struct list_head *ep_queue;
|
||||
struct usb_host_endpoint *ep;
|
||||
LIST_HEAD(urb_list);
|
||||
struct urb_listitem *urb_listitem, *urb_listitem_next;
|
||||
int i;
|
||||
|
||||
if (!hcd) {
|
||||
WARN_ON(1);
|
||||
|
@ -944,28 +951,13 @@ void schedule_ptds(struct usb_hcd *hcd)
|
|||
/*
|
||||
* check finished/retired xfers, transfer payloads, call urb_done()
|
||||
*/
|
||||
ep_queue = &priv->interruptqhs;
|
||||
while (ep_queue) {
|
||||
for (i = 0; i < QH_END; i++) {
|
||||
ep_queue = &priv->qh_list[i];
|
||||
list_for_each_entry_safe(qh, qh_next, ep_queue, qh_list) {
|
||||
ep = list_entry(qh->qtd_list.next, struct isp1760_qtd,
|
||||
qtd_list)->urb->ep;
|
||||
collect_qtds(hcd, qh, &urb_list);
|
||||
if (list_empty(&qh->qtd_list)) {
|
||||
if (list_empty(&qh->qtd_list))
|
||||
list_del(&qh->qh_list);
|
||||
if (ep->hcpriv == NULL) {
|
||||
/* Endpoint has been disabled, so we
|
||||
can free the associated queue head. */
|
||||
qh_free(qh);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ep_queue == &priv->interruptqhs)
|
||||
ep_queue = &priv->controlqhs;
|
||||
else if (ep_queue == &priv->controlqhs)
|
||||
ep_queue = &priv->bulkqhs;
|
||||
else
|
||||
ep_queue = NULL;
|
||||
}
|
||||
|
||||
list_for_each_entry_safe(urb_listitem, urb_listitem_next, &urb_list,
|
||||
|
@ -998,17 +990,10 @@ void schedule_ptds(struct usb_hcd *hcd)
|
|||
*
|
||||
* I'm sure this scheme could be improved upon!
|
||||
*/
|
||||
ep_queue = &priv->controlqhs;
|
||||
while (ep_queue) {
|
||||
for (i = 0; i < QH_END; i++) {
|
||||
ep_queue = &priv->qh_list[i];
|
||||
list_for_each_entry_safe(qh, qh_next, ep_queue, qh_list)
|
||||
enqueue_qtds(hcd, qh);
|
||||
|
||||
if (ep_queue == &priv->controlqhs)
|
||||
ep_queue = &priv->interruptqhs;
|
||||
else if (ep_queue == &priv->interruptqhs)
|
||||
ep_queue = &priv->bulkqhs;
|
||||
else
|
||||
ep_queue = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1543,16 +1528,16 @@ static int isp1760_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
|
|||
|
||||
switch (usb_pipetype(urb->pipe)) {
|
||||
case PIPE_CONTROL:
|
||||
ep_queue = &priv->controlqhs;
|
||||
ep_queue = &priv->qh_list[QH_CONTROL];
|
||||
break;
|
||||
case PIPE_BULK:
|
||||
ep_queue = &priv->bulkqhs;
|
||||
ep_queue = &priv->qh_list[QH_BULK];
|
||||
break;
|
||||
case PIPE_INTERRUPT:
|
||||
if (urb->interval < 0)
|
||||
return -EINVAL;
|
||||
/* FIXME: Check bandwidth */
|
||||
ep_queue = &priv->interruptqhs;
|
||||
ep_queue = &priv->qh_list[QH_INTERRUPT];
|
||||
break;
|
||||
case PIPE_ISOCHRONOUS:
|
||||
dev_err(hcd->self.controller, "%s: isochronous USB packets "
|
||||
|
@ -1714,8 +1699,8 @@ static void isp1760_endpoint_disable(struct usb_hcd *hcd,
|
|||
{
|
||||
struct isp1760_hcd *priv = hcd_to_priv(hcd);
|
||||
unsigned long spinflags;
|
||||
struct isp1760_qh *qh;
|
||||
struct isp1760_qtd *qtd;
|
||||
struct isp1760_qh *qh, *qh_iter;
|
||||
int i;
|
||||
|
||||
spin_lock_irqsave(&priv->lock, spinflags);
|
||||
|
||||
|
@ -1723,14 +1708,17 @@ static void isp1760_endpoint_disable(struct usb_hcd *hcd,
|
|||
if (!qh)
|
||||
goto out;
|
||||
|
||||
list_for_each_entry(qtd, &qh->qtd_list, qtd_list)
|
||||
if (qtd->status != QTD_RETIRE) {
|
||||
dequeue_urb_from_qtd(hcd, qh, qtd);
|
||||
qtd->urb->status = -ECONNRESET;
|
||||
}
|
||||
WARN_ON(!list_empty(&qh->qtd_list));
|
||||
|
||||
for (i = 0; i < QH_END; i++)
|
||||
list_for_each_entry(qh_iter, &priv->qh_list[i], qh_list)
|
||||
if (qh_iter == qh) {
|
||||
list_del(&qh_iter->qh_list);
|
||||
i = QH_END;
|
||||
break;
|
||||
}
|
||||
qh_free(qh);
|
||||
ep->hcpriv = NULL;
|
||||
/* Cannot free qh here since it will be parsed by schedule_ptds() */
|
||||
|
||||
schedule_ptds(hcd);
|
||||
|
||||
|
|
|
@ -47,23 +47,27 @@ static int of_isp1760_probe(struct platform_device *dev)
|
|||
int virq;
|
||||
resource_size_t res_len;
|
||||
int ret;
|
||||
const unsigned int *prop;
|
||||
unsigned int devflags = 0;
|
||||
enum of_gpio_flags gpio_flags;
|
||||
u32 bus_width = 0;
|
||||
|
||||
drvdata = kzalloc(sizeof(*drvdata), GFP_KERNEL);
|
||||
if (!drvdata)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = of_address_to_resource(dp, 0, &memory);
|
||||
if (ret)
|
||||
return -ENXIO;
|
||||
if (ret) {
|
||||
ret = -ENXIO;
|
||||
goto free_data;
|
||||
}
|
||||
|
||||
res_len = resource_size(&memory);
|
||||
|
||||
res = request_mem_region(memory.start, res_len, dev_name(&dev->dev));
|
||||
if (!res)
|
||||
return -EBUSY;
|
||||
if (!res) {
|
||||
ret = -EBUSY;
|
||||
goto free_data;
|
||||
}
|
||||
|
||||
if (of_irq_map_one(dp, 0, &oirq)) {
|
||||
ret = -ENODEV;
|
||||
|
@ -77,8 +81,8 @@ static int of_isp1760_probe(struct platform_device *dev)
|
|||
devflags |= ISP1760_FLAG_ISP1761;
|
||||
|
||||
/* Some systems wire up only 16 of the 32 data lines */
|
||||
prop = of_get_property(dp, "bus-width", NULL);
|
||||
if (prop && *prop == 16)
|
||||
of_property_read_u32(dp, "bus-width", &bus_width);
|
||||
if (bus_width == 16)
|
||||
devflags |= ISP1760_FLAG_BUS_WIDTH_16;
|
||||
|
||||
if (of_get_property(dp, "port1-otg", NULL) != NULL)
|
||||
|
@ -125,6 +129,7 @@ free_gpio:
|
|||
gpio_free(drvdata->rst_gpio);
|
||||
release_reg:
|
||||
release_mem_region(memory.start, res_len);
|
||||
free_data:
|
||||
kfree(drvdata);
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -173,12 +173,9 @@ static int ohci_hcd_au1xxx_drv_suspend(struct device *dev)
|
|||
* mark HW unaccessible, bail out if RH has been resumed. Use
|
||||
* the spinlock to properly synchronize with possible pending
|
||||
* RH suspend or resume activity.
|
||||
*
|
||||
* This is still racy as hcd->state is manipulated outside of
|
||||
* any locks =P But that will be a different fix.
|
||||
*/
|
||||
spin_lock_irqsave(&ohci->lock, flags);
|
||||
if (hcd->state != HC_STATE_SUSPENDED) {
|
||||
if (ohci->rh_state != OHCI_RH_SUSPENDED) {
|
||||
rc = -EINVAL;
|
||||
goto bail;
|
||||
}
|
||||
|
|
|
@ -127,6 +127,19 @@ static char *hcfs2string (int state)
|
|||
return "?";
|
||||
}
|
||||
|
||||
static const char *rh_state_string(struct ohci_hcd *ohci)
|
||||
{
|
||||
switch (ohci->rh_state) {
|
||||
case OHCI_RH_HALTED:
|
||||
return "halted";
|
||||
case OHCI_RH_SUSPENDED:
|
||||
return "suspended";
|
||||
case OHCI_RH_RUNNING:
|
||||
return "running";
|
||||
}
|
||||
return "?";
|
||||
}
|
||||
|
||||
// dump control and status registers
|
||||
static void
|
||||
ohci_dump_status (struct ohci_hcd *controller, char **next, unsigned *size)
|
||||
|
@ -136,9 +149,10 @@ ohci_dump_status (struct ohci_hcd *controller, char **next, unsigned *size)
|
|||
|
||||
temp = ohci_readl (controller, ®s->revision) & 0xff;
|
||||
ohci_dbg_sw (controller, next, size,
|
||||
"OHCI %d.%d, %s legacy support registers\n",
|
||||
"OHCI %d.%d, %s legacy support registers, rh state %s\n",
|
||||
0x03 & (temp >> 4), (temp & 0x0f),
|
||||
(temp & 0x0100) ? "with" : "NO");
|
||||
(temp & 0x0100) ? "with" : "NO",
|
||||
rh_state_string(controller));
|
||||
|
||||
temp = ohci_readl (controller, ®s->control);
|
||||
ohci_dbg_sw (controller, next, size,
|
||||
|
|
|
@ -179,8 +179,6 @@ static int ohci_hcd_ep93xx_drv_suspend(struct platform_device *pdev, pm_message_
|
|||
ohci->next_statechange = jiffies;
|
||||
|
||||
ep93xx_stop_hc(&pdev->dev);
|
||||
hcd->state = HC_STATE_SUSPENDED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -209,7 +209,7 @@ static int ohci_urb_enqueue (
|
|||
retval = -ENODEV;
|
||||
goto fail;
|
||||
}
|
||||
if (!HC_IS_RUNNING(hcd->state)) {
|
||||
if (ohci->rh_state != OHCI_RH_RUNNING) {
|
||||
retval = -ENODEV;
|
||||
goto fail;
|
||||
}
|
||||
|
@ -274,7 +274,7 @@ static int ohci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
|
|||
rc = usb_hcd_check_unlink_urb(hcd, urb, status);
|
||||
if (rc) {
|
||||
; /* Do nothing */
|
||||
} else if (HC_IS_RUNNING(hcd->state)) {
|
||||
} else if (ohci->rh_state == OHCI_RH_RUNNING) {
|
||||
urb_priv_t *urb_priv;
|
||||
|
||||
/* Unless an IRQ completed the unlink while it was being
|
||||
|
@ -321,7 +321,7 @@ ohci_endpoint_disable (struct usb_hcd *hcd, struct usb_host_endpoint *ep)
|
|||
rescan:
|
||||
spin_lock_irqsave (&ohci->lock, flags);
|
||||
|
||||
if (!HC_IS_RUNNING (hcd->state)) {
|
||||
if (ohci->rh_state != OHCI_RH_RUNNING) {
|
||||
sanitize:
|
||||
ed->state = ED_IDLE;
|
||||
if (quirk_zfmicro(ohci) && ed->type == PIPE_INTERRUPT)
|
||||
|
@ -377,6 +377,7 @@ static void ohci_usb_reset (struct ohci_hcd *ohci)
|
|||
ohci->hc_control = ohci_readl (ohci, &ohci->regs->control);
|
||||
ohci->hc_control &= OHCI_CTRL_RWC;
|
||||
ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
|
||||
ohci->rh_state = OHCI_RH_HALTED;
|
||||
}
|
||||
|
||||
/* ohci_shutdown forcibly disables IRQs and DMA, helping kexec and
|
||||
|
@ -500,7 +501,7 @@ static int ohci_init (struct ohci_hcd *ohci)
|
|||
if (distrust_firmware)
|
||||
ohci->flags |= OHCI_QUIRK_HUB_POWER;
|
||||
|
||||
disable (ohci);
|
||||
ohci->rh_state = OHCI_RH_HALTED;
|
||||
ohci->regs = hcd->regs;
|
||||
|
||||
/* REVISIT this BIOS handshake is now moved into PCI "quirks", and
|
||||
|
@ -575,7 +576,7 @@ static int ohci_run (struct ohci_hcd *ohci)
|
|||
int first = ohci->fminterval == 0;
|
||||
struct usb_hcd *hcd = ohci_to_hcd(ohci);
|
||||
|
||||
disable (ohci);
|
||||
ohci->rh_state = OHCI_RH_HALTED;
|
||||
|
||||
/* boot firmware should have set this up (5.1.1.3.1) */
|
||||
if (first) {
|
||||
|
@ -688,7 +689,7 @@ retry:
|
|||
ohci->hc_control &= OHCI_CTRL_RWC;
|
||||
ohci->hc_control |= OHCI_CONTROL_INIT | OHCI_USB_OPER;
|
||||
ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
|
||||
hcd->state = HC_STATE_RUNNING;
|
||||
ohci->rh_state = OHCI_RH_RUNNING;
|
||||
|
||||
/* wake on ConnectStatusChange, matching external hubs */
|
||||
ohci_writel (ohci, RH_HS_DRWE, &ohci->regs->roothub.status);
|
||||
|
@ -725,7 +726,6 @@ retry:
|
|||
|
||||
// POTPGT delay is bits 24-31, in 2 ms units.
|
||||
mdelay ((val >> 23) & 0x1fe);
|
||||
hcd->state = HC_STATE_RUNNING;
|
||||
|
||||
if (quirk_zfmicro(ohci)) {
|
||||
/* Create timer to watch for bad queue state on ZF Micro */
|
||||
|
@ -761,7 +761,7 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd)
|
|||
* of dead, unclocked, or unplugged (CardBus...) devices
|
||||
*/
|
||||
if (ints == ~(u32)0) {
|
||||
disable (ohci);
|
||||
ohci->rh_state = OHCI_RH_HALTED;
|
||||
ohci_dbg (ohci, "device removed!\n");
|
||||
usb_hc_died(hcd);
|
||||
return IRQ_HANDLED;
|
||||
|
@ -771,7 +771,7 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd)
|
|||
ints &= ohci_readl(ohci, ®s->intrenable);
|
||||
|
||||
/* interrupt for some other device? */
|
||||
if (ints == 0 || unlikely(hcd->state == HC_STATE_HALT))
|
||||
if (ints == 0 || unlikely(ohci->rh_state == OHCI_RH_HALTED))
|
||||
return IRQ_NOTMINE;
|
||||
|
||||
if (ints & OHCI_INTR_UE) {
|
||||
|
@ -786,8 +786,8 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd)
|
|||
|
||||
schedule_work (&ohci->nec_work);
|
||||
} else {
|
||||
disable (ohci);
|
||||
ohci_err (ohci, "OHCI Unrecoverable Error, disabled\n");
|
||||
ohci->rh_state = OHCI_RH_HALTED;
|
||||
usb_hc_died(hcd);
|
||||
}
|
||||
|
||||
|
@ -871,11 +871,11 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd)
|
|||
if ((ints & OHCI_INTR_SF) != 0
|
||||
&& !ohci->ed_rm_list
|
||||
&& !ohci->ed_to_check
|
||||
&& HC_IS_RUNNING(hcd->state))
|
||||
&& ohci->rh_state == OHCI_RH_RUNNING)
|
||||
ohci_writel (ohci, OHCI_INTR_SF, ®s->intrdisable);
|
||||
spin_unlock (&ohci->lock);
|
||||
|
||||
if (HC_IS_RUNNING(hcd->state)) {
|
||||
if (ohci->rh_state == OHCI_RH_RUNNING) {
|
||||
ohci_writel (ohci, ints, ®s->intrstatus);
|
||||
ohci_writel (ohci, OHCI_INTR_MIE, ®s->intrenable);
|
||||
// flush those writes
|
||||
|
@ -929,7 +929,7 @@ static int ohci_restart (struct ohci_hcd *ohci)
|
|||
struct urb_priv *priv;
|
||||
|
||||
spin_lock_irq(&ohci->lock);
|
||||
disable (ohci);
|
||||
ohci->rh_state = OHCI_RH_HALTED;
|
||||
|
||||
/* Recycle any "live" eds/tds (and urbs). */
|
||||
if (!list_empty (&ohci->pending))
|
||||
|
@ -1111,7 +1111,7 @@ MODULE_LICENSE ("GPL");
|
|||
#define PLATFORM_DRIVER ohci_hcd_ath79_driver
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_NLM_XLR
|
||||
#ifdef CONFIG_CPU_XLR
|
||||
#include "ohci-xls.c"
|
||||
#define PLATFORM_DRIVER ohci_xls_driver
|
||||
#endif
|
||||
|
|
|
@ -111,6 +111,7 @@ __acquires(ohci->lock)
|
|||
if (!autostop) {
|
||||
ohci->next_statechange = jiffies + msecs_to_jiffies (5);
|
||||
ohci->autostop = 0;
|
||||
ohci->rh_state = OHCI_RH_SUSPENDED;
|
||||
}
|
||||
|
||||
done:
|
||||
|
@ -140,7 +141,7 @@ __acquires(ohci->lock)
|
|||
|
||||
if (ohci->hc_control & (OHCI_CTRL_IR | OHCI_SCHED_ENABLES)) {
|
||||
/* this can happen after resuming a swsusp snapshot */
|
||||
if (hcd->state == HC_STATE_RESUMING) {
|
||||
if (ohci->rh_state != OHCI_RH_RUNNING) {
|
||||
ohci_dbg (ohci, "BIOS/SMM active, control %03x\n",
|
||||
ohci->hc_control);
|
||||
status = -EBUSY;
|
||||
|
@ -274,6 +275,7 @@ skip_resume:
|
|||
(void) ohci_readl (ohci, &ohci->regs->control);
|
||||
}
|
||||
|
||||
ohci->rh_state = OHCI_RH_RUNNING;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -336,11 +338,8 @@ static void ohci_finish_controller_resume(struct usb_hcd *hcd)
|
|||
/* If needed, reinitialize and suspend the root hub */
|
||||
if (need_reinit) {
|
||||
spin_lock_irq(&ohci->lock);
|
||||
hcd->state = HC_STATE_RESUMING;
|
||||
ohci_rh_resume(ohci);
|
||||
hcd->state = HC_STATE_QUIESCING;
|
||||
ohci_rh_suspend(ohci, 0);
|
||||
hcd->state = HC_STATE_SUSPENDED;
|
||||
spin_unlock_irq(&ohci->lock);
|
||||
}
|
||||
|
||||
|
|
|
@ -516,7 +516,6 @@ static int ohci_omap_suspend(struct platform_device *dev, pm_message_t message)
|
|||
ohci->next_statechange = jiffies;
|
||||
|
||||
omap_ohci_clock_power(0);
|
||||
ohci_to_hcd(ohci)->state = HC_STATE_SUSPENDED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -308,12 +308,9 @@ static int ohci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup)
|
|||
* mark HW unaccessible, bail out if RH has been resumed. Use
|
||||
* the spinlock to properly synchronize with possible pending
|
||||
* RH suspend or resume activity.
|
||||
*
|
||||
* This is still racy as hcd->state is manipulated outside of
|
||||
* any locks =P But that will be a different fix.
|
||||
*/
|
||||
spin_lock_irqsave (&ohci->lock, flags);
|
||||
if (hcd->state != HC_STATE_SUSPENDED) {
|
||||
if (ohci->rh_state != OHCI_RH_SUSPENDED) {
|
||||
rc = -EINVAL;
|
||||
goto bail;
|
||||
}
|
||||
|
|
|
@ -502,8 +502,6 @@ static int ohci_hcd_pxa27x_drv_suspend(struct device *dev)
|
|||
ohci->ohci.next_statechange = jiffies;
|
||||
|
||||
pxa27x_stop_hc(ohci, dev);
|
||||
hcd->state = HC_STATE_SUSPENDED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -912,7 +912,7 @@ rescan_all:
|
|||
/* only take off EDs that the HC isn't using, accounting for
|
||||
* frame counter wraps and EDs with partially retired TDs
|
||||
*/
|
||||
if (likely (HC_IS_RUNNING(ohci_to_hcd(ohci)->state))) {
|
||||
if (likely(ohci->rh_state == OHCI_RH_RUNNING)) {
|
||||
if (tick_before (tick, ed->tick)) {
|
||||
skip_ed:
|
||||
last = &ed->ed_next;
|
||||
|
@ -1012,7 +1012,7 @@ rescan_this:
|
|||
|
||||
/* but if there's work queued, reschedule */
|
||||
if (!list_empty (&ed->td_list)) {
|
||||
if (HC_IS_RUNNING(ohci_to_hcd(ohci)->state))
|
||||
if (ohci->rh_state == OHCI_RH_RUNNING)
|
||||
ed_schedule (ohci, ed);
|
||||
}
|
||||
|
||||
|
@ -1021,9 +1021,7 @@ rescan_this:
|
|||
}
|
||||
|
||||
/* maybe reenable control and bulk lists */
|
||||
if (HC_IS_RUNNING(ohci_to_hcd(ohci)->state)
|
||||
&& ohci_to_hcd(ohci)->state != HC_STATE_QUIESCING
|
||||
&& !ohci->ed_rm_list) {
|
||||
if (ohci->rh_state == OHCI_RH_RUNNING && !ohci->ed_rm_list) {
|
||||
u32 command = 0, control = 0;
|
||||
|
||||
if (ohci->ed_controltail) {
|
||||
|
|
|
@ -486,15 +486,66 @@ static int __devexit ohci_hcd_s3c2410_drv_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int ohci_hcd_s3c2410_drv_suspend(struct device *dev)
|
||||
{
|
||||
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
||||
struct ohci_hcd *ohci = hcd_to_ohci(hcd);
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
unsigned long flags;
|
||||
int rc = 0;
|
||||
|
||||
/*
|
||||
* Root hub was already suspended. Disable irq emission and
|
||||
* mark HW unaccessible, bail out if RH has been resumed. Use
|
||||
* the spinlock to properly synchronize with possible pending
|
||||
* RH suspend or resume activity.
|
||||
*/
|
||||
spin_lock_irqsave(&ohci->lock, flags);
|
||||
if (ohci->rh_state != OHCI_RH_SUSPENDED) {
|
||||
rc = -EINVAL;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
|
||||
|
||||
s3c2410_stop_hc(pdev);
|
||||
bail:
|
||||
spin_unlock_irqrestore(&ohci->lock, flags);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int ohci_hcd_s3c2410_drv_resume(struct device *dev)
|
||||
{
|
||||
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
|
||||
s3c2410_start_hc(pdev, hcd);
|
||||
|
||||
set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
|
||||
ohci_finish_controller_resume(hcd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define ohci_hcd_s3c2410_drv_suspend NULL
|
||||
#define ohci_hcd_s3c2410_drv_resume NULL
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops ohci_hcd_s3c2410_pm_ops = {
|
||||
.suspend = ohci_hcd_s3c2410_drv_suspend,
|
||||
.resume = ohci_hcd_s3c2410_drv_resume,
|
||||
};
|
||||
|
||||
static struct platform_driver ohci_hcd_s3c2410_driver = {
|
||||
.probe = ohci_hcd_s3c2410_drv_probe,
|
||||
.remove = __devexit_p(ohci_hcd_s3c2410_drv_remove),
|
||||
.shutdown = usb_hcd_platform_shutdown,
|
||||
/*.suspend = ohci_hcd_s3c2410_drv_suspend, */
|
||||
/*.resume = ohci_hcd_s3c2410_drv_resume, */
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "s3c2410-ohci",
|
||||
.pm = &ohci_hcd_s3c2410_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -29,7 +29,6 @@ static int ohci_sh_start(struct usb_hcd *hcd)
|
|||
ohci_hcd_init(ohci);
|
||||
ohci_init(ohci);
|
||||
ohci_run(ohci);
|
||||
hcd->state = HC_STATE_RUNNING;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче