forked from Minki/linux
Merge git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb-2.6
* git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb-2.6: (47 commits) usb: musb: pass configuration specifics via pdata usb: musb: fix hanging when rmmod gadget driver USB: Add MUSB and TUSB support USB: serial: remove CONFIG_USB_DEBUG from sierra and option drivers USB: Add vendor/product id of ZTE MF628 to option USB: quirk PLL power down mode USB: omap_udc: fix compilation with debug enabled usb: cdc-acm: drain writes on close usb: cdc-acm: stop dropping tx buffers usb: cdc-acm: bugfix release() usb gadget: issue notifications from ACM function usb gadget: remove needless struct members USB: sh: r8a66597-hcd: fix disconnect regression USB: isp1301: fix compilation USB: fix compiler warning fix usb-storage: unusual_devs entry for Nokia 5300 USB: cdc-acm.c: Fix compile warnings USB: BandRich BandLuxe C150/C250 HSPA Data Card Driver USB: ftdi_sio: add support for PHI Fisco data cable (FT232BM based, VID/PID 0403:e40b) usb: isp1760: don't be noisy about short packets. ...
This commit is contained in:
commit
b635acec48
@ -2560,9 +2560,6 @@ Your cooperation is appreciated.
|
||||
96 = /dev/usb/hiddev0 1st USB HID device
|
||||
...
|
||||
111 = /dev/usb/hiddev15 16th USB HID device
|
||||
112 = /dev/usb/auer0 1st auerswald ISDN device
|
||||
...
|
||||
127 = /dev/usb/auer15 16th auerswald ISDN device
|
||||
128 = /dev/usb/brlvgr0 First Braille Voyager device
|
||||
...
|
||||
131 = /dev/usb/brlvgr3 Fourth Braille Voyager device
|
||||
|
@ -105,7 +105,6 @@ Code Seq# Include File Comments
|
||||
'T' all linux/soundcard.h conflict!
|
||||
'T' all asm-i386/ioctls.h conflict!
|
||||
'U' 00-EF linux/drivers/usb/usb.h
|
||||
'U' F0-FF drivers/usb/auerswald.c
|
||||
'V' all linux/vt.h
|
||||
'W' 00-1F linux/watchdog.h conflict!
|
||||
'W' 00-1F linux/wanrouter.h conflict!
|
||||
|
@ -1,30 +0,0 @@
|
||||
Auerswald USB kernel driver
|
||||
===========================
|
||||
|
||||
What is it? What can I do with it?
|
||||
==================================
|
||||
The auerswald USB kernel driver connects your linux 2.4.x
|
||||
system to the auerswald usb-enabled devices.
|
||||
|
||||
There are two types of auerswald usb devices:
|
||||
a) small PBX systems (ISDN)
|
||||
b) COMfort system telephones (ISDN)
|
||||
|
||||
The driver installation creates the devices
|
||||
/dev/usb/auer0..15. These devices carry a vendor-
|
||||
specific protocol. You may run all auerswald java
|
||||
software on it. The java software needs a native
|
||||
library "libAuerUsbJNINative.so" installed on
|
||||
your system. This library is available from
|
||||
auerswald and shipped as part of the java software.
|
||||
|
||||
You may create the devices with:
|
||||
mknod -m 666 /dev/usb/auer0 c 180 112
|
||||
...
|
||||
mknod -m 666 /dev/usb/auer15 c 180 127
|
||||
|
||||
Future plans
|
||||
============
|
||||
- Connection to ISDN4LINUX (the hisax interface)
|
||||
|
||||
The maintainer of this driver is wolfgang@iksw-muees.de
|
@ -436,7 +436,12 @@ post_reset; the USB core guarantees that this is true of internal
|
||||
suspend/resume events as well.
|
||||
|
||||
If a driver wants to block all suspend/resume calls during some
|
||||
critical section, it can simply acquire udev->pm_mutex.
|
||||
critical section, it can simply acquire udev->pm_mutex. Note that
|
||||
calls to resume may be triggered indirectly. Block IO due to memory
|
||||
allocations can make the vm subsystem resume a device. Thus while
|
||||
holding this lock you must not allocate memory with GFP_KERNEL or
|
||||
GFP_NOFS.
|
||||
|
||||
Alternatively, if the critical section might call some of the
|
||||
usb_autopm_* routines, the driver can avoid deadlock by doing:
|
||||
|
||||
|
12
MAINTAINERS
12
MAINTAINERS
@ -2928,6 +2928,12 @@ M: jirislaby@gmail.com
|
||||
L: linux-kernel@vger.kernel.org
|
||||
S: Maintained
|
||||
|
||||
MUSB MULTIPOINT HIGH SPEED DUAL-ROLE CONTROLLER
|
||||
P: Felipe Balbi
|
||||
M: felipe.balbi@nokia.com
|
||||
L: linux-usb@vger.kernel.org
|
||||
S: Maintained
|
||||
|
||||
MYRICOM MYRI-10G 10GbE DRIVER (MYRI10GE)
|
||||
P: Andrew Gallatin
|
||||
M: gallatin@myri.com
|
||||
@ -4196,12 +4202,6 @@ M: oliver@neukum.name
|
||||
L: linux-usb@vger.kernel.org
|
||||
S: Maintained
|
||||
|
||||
USB AUERSWALD DRIVER
|
||||
P: Wolfgang Muees
|
||||
M: wolfgang@iksw-muees.de
|
||||
L: linux-usb@vger.kernel.org
|
||||
S: Maintained
|
||||
|
||||
USB BLOCK DRIVER (UB ub)
|
||||
P: Pete Zaitcev
|
||||
M: zaitcev@redhat.com
|
||||
|
@ -317,7 +317,6 @@ tusb6010_setup_interface(struct musb_hdrc_platform_data *data,
|
||||
printk(error, 6, status);
|
||||
return -ENODEV;
|
||||
}
|
||||
data->multipoint = 1;
|
||||
tusb_device.dev.platform_data = data;
|
||||
|
||||
/* REVISIT let the driver know what DMA channels work */
|
||||
|
@ -57,6 +57,7 @@ obj-$(CONFIG_ATA_OVER_ETH) += block/aoe/
|
||||
obj-$(CONFIG_PARIDE) += block/paride/
|
||||
obj-$(CONFIG_TC) += tc/
|
||||
obj-$(CONFIG_USB) += usb/
|
||||
obj-$(CONFIG_USB_MUSB_HDRC) += usb/musb/
|
||||
obj-$(CONFIG_PCI) += usb/
|
||||
obj-$(CONFIG_USB_GADGET) += usb/gadget/
|
||||
obj-$(CONFIG_SERIO) += input/serio/
|
||||
|
@ -1593,7 +1593,7 @@ fail1:
|
||||
if (machine_is_omap_h2()) {
|
||||
/* full speed signaling by default */
|
||||
isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1,
|
||||
MC1_SPEED_REG);
|
||||
MC1_SPEED);
|
||||
isp1301_set_bits(isp, ISP1301_MODE_CONTROL_2,
|
||||
MC2_SPD_SUSP_CTRL);
|
||||
|
||||
|
@ -95,16 +95,18 @@ config USB
|
||||
|
||||
source "drivers/usb/core/Kconfig"
|
||||
|
||||
source "drivers/usb/mon/Kconfig"
|
||||
|
||||
source "drivers/usb/host/Kconfig"
|
||||
|
||||
source "drivers/usb/musb/Kconfig"
|
||||
|
||||
source "drivers/usb/class/Kconfig"
|
||||
|
||||
source "drivers/usb/storage/Kconfig"
|
||||
|
||||
source "drivers/usb/image/Kconfig"
|
||||
|
||||
source "drivers/usb/mon/Kconfig"
|
||||
|
||||
comment "USB port drivers"
|
||||
depends on USB
|
||||
|
||||
|
@ -602,7 +602,7 @@ static int cxacru_cm_get_array(struct cxacru_data *instance, enum cxacru_cm_requ
|
||||
offd = le32_to_cpu(buf[offb++]);
|
||||
if (offd >= size) {
|
||||
if (printk_ratelimit())
|
||||
usb_err(instance->usbatm, "wrong index #%x in response to cm #%x\n",
|
||||
usb_err(instance->usbatm, "wrong index %#x in response to cm %#x\n",
|
||||
offd, cm);
|
||||
ret = -EIO;
|
||||
goto cleanup;
|
||||
|
@ -51,6 +51,7 @@
|
||||
*/
|
||||
|
||||
#undef DEBUG
|
||||
#undef VERBOSE_DEBUG
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
@ -70,6 +71,9 @@
|
||||
|
||||
#include "cdc-acm.h"
|
||||
|
||||
|
||||
#define ACM_CLOSE_TIMEOUT 15 /* seconds to let writes drain */
|
||||
|
||||
/*
|
||||
* Version Information
|
||||
*/
|
||||
@ -85,6 +89,12 @@ static DEFINE_MUTEX(open_mutex);
|
||||
|
||||
#define ACM_READY(acm) (acm && acm->dev && acm->used)
|
||||
|
||||
#ifdef VERBOSE_DEBUG
|
||||
#define verbose 1
|
||||
#else
|
||||
#define verbose 0
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Functions for ACM control messages.
|
||||
*/
|
||||
@ -136,19 +146,17 @@ static int acm_wb_alloc(struct acm *acm)
|
||||
static int acm_wb_is_avail(struct acm *acm)
|
||||
{
|
||||
int i, n;
|
||||
unsigned long flags;
|
||||
|
||||
n = ACM_NW;
|
||||
spin_lock_irqsave(&acm->write_lock, flags);
|
||||
for (i = 0; i < ACM_NW; i++) {
|
||||
n -= acm->wb[i].use;
|
||||
}
|
||||
spin_unlock_irqrestore(&acm->write_lock, flags);
|
||||
return n;
|
||||
}
|
||||
|
||||
static inline int acm_wb_is_used(struct acm *acm, int wbn)
|
||||
{
|
||||
return acm->wb[wbn].use;
|
||||
}
|
||||
|
||||
/*
|
||||
* Finish write.
|
||||
*/
|
||||
@ -157,7 +165,6 @@ static void acm_write_done(struct acm *acm, struct acm_wb *wb)
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&acm->write_lock, flags);
|
||||
acm->write_ready = 1;
|
||||
wb->use = 0;
|
||||
acm->transmitting--;
|
||||
spin_unlock_irqrestore(&acm->write_lock, flags);
|
||||
@ -190,40 +197,25 @@ static int acm_start_wb(struct acm *acm, struct acm_wb *wb)
|
||||
static int acm_write_start(struct acm *acm, int wbn)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct acm_wb *wb;
|
||||
struct acm_wb *wb = &acm->wb[wbn];
|
||||
int rc;
|
||||
|
||||
spin_lock_irqsave(&acm->write_lock, flags);
|
||||
if (!acm->dev) {
|
||||
wb->use = 0;
|
||||
spin_unlock_irqrestore(&acm->write_lock, flags);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!acm->write_ready) {
|
||||
spin_unlock_irqrestore(&acm->write_lock, flags);
|
||||
return 0; /* A white lie */
|
||||
}
|
||||
|
||||
wb = &acm->wb[wbn];
|
||||
if(acm_wb_is_avail(acm) <= 1)
|
||||
acm->write_ready = 0;
|
||||
|
||||
dbg("%s susp_count: %d", __func__, acm->susp_count);
|
||||
if (acm->susp_count) {
|
||||
acm->old_ready = acm->write_ready;
|
||||
acm->delayed_wb = wb;
|
||||
acm->write_ready = 0;
|
||||
schedule_work(&acm->waker);
|
||||
spin_unlock_irqrestore(&acm->write_lock, flags);
|
||||
return 0; /* A white lie */
|
||||
}
|
||||
usb_mark_last_busy(acm->dev);
|
||||
|
||||
if (!acm_wb_is_used(acm, wbn)) {
|
||||
spin_unlock_irqrestore(&acm->write_lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
rc = acm_start_wb(acm, wb);
|
||||
spin_unlock_irqrestore(&acm->write_lock, flags);
|
||||
|
||||
@ -488,22 +480,28 @@ urbs:
|
||||
/* data interface wrote those outgoing bytes */
|
||||
static void acm_write_bulk(struct urb *urb)
|
||||
{
|
||||
struct acm *acm;
|
||||
struct acm_wb *wb = urb->context;
|
||||
struct acm *acm = wb->instance;
|
||||
|
||||
dbg("Entering acm_write_bulk with status %d", urb->status);
|
||||
if (verbose || urb->status
|
||||
|| (urb->actual_length != urb->transfer_buffer_length))
|
||||
dev_dbg(&acm->data->dev, "tx %d/%d bytes -- > %d\n",
|
||||
urb->actual_length,
|
||||
urb->transfer_buffer_length,
|
||||
urb->status);
|
||||
|
||||
acm = wb->instance;
|
||||
acm_write_done(acm, wb);
|
||||
if (ACM_READY(acm))
|
||||
schedule_work(&acm->work);
|
||||
else
|
||||
wake_up_interruptible(&acm->drain_wait);
|
||||
}
|
||||
|
||||
static void acm_softint(struct work_struct *work)
|
||||
{
|
||||
struct acm *acm = container_of(work, struct acm, work);
|
||||
dbg("Entering acm_softint.");
|
||||
|
||||
|
||||
dev_vdbg(&acm->data->dev, "tx work\n");
|
||||
if (!ACM_READY(acm))
|
||||
return;
|
||||
tty_wakeup(acm->tty);
|
||||
@ -512,7 +510,6 @@ static void acm_softint(struct work_struct *work)
|
||||
static void acm_waker(struct work_struct *waker)
|
||||
{
|
||||
struct acm *acm = container_of(waker, struct acm, waker);
|
||||
long flags;
|
||||
int rv;
|
||||
|
||||
rv = usb_autopm_get_interface(acm->control);
|
||||
@ -524,9 +521,6 @@ static void acm_waker(struct work_struct *waker)
|
||||
acm_start_wb(acm, acm->delayed_wb);
|
||||
acm->delayed_wb = NULL;
|
||||
}
|
||||
spin_lock_irqsave(&acm->write_lock, flags);
|
||||
acm->write_ready = acm->old_ready;
|
||||
spin_unlock_irqrestore(&acm->write_lock, flags);
|
||||
usb_autopm_put_interface(acm->control);
|
||||
}
|
||||
|
||||
@ -628,6 +622,8 @@ static void acm_tty_unregister(struct acm *acm)
|
||||
kfree(acm);
|
||||
}
|
||||
|
||||
static int acm_tty_chars_in_buffer(struct tty_struct *tty);
|
||||
|
||||
static void acm_tty_close(struct tty_struct *tty, struct file *filp)
|
||||
{
|
||||
struct acm *acm = tty->driver_data;
|
||||
@ -642,6 +638,13 @@ static void acm_tty_close(struct tty_struct *tty, struct file *filp)
|
||||
if (acm->dev) {
|
||||
usb_autopm_get_interface(acm->control);
|
||||
acm_set_control(acm, acm->ctrlout = 0);
|
||||
|
||||
/* try letting the last writes drain naturally */
|
||||
wait_event_interruptible_timeout(acm->drain_wait,
|
||||
(ACM_NW == acm_wb_is_avail(acm))
|
||||
|| !acm->dev,
|
||||
ACM_CLOSE_TIMEOUT * HZ);
|
||||
|
||||
usb_kill_urb(acm->ctrlurb);
|
||||
for (i = 0; i < ACM_NW; i++)
|
||||
usb_kill_urb(acm->wb[i].urb);
|
||||
@ -697,7 +700,7 @@ static int acm_tty_write_room(struct tty_struct *tty)
|
||||
* Do not let the line discipline to know that we have a reserve,
|
||||
* or it might get too enthusiastic.
|
||||
*/
|
||||
return (acm->write_ready && acm_wb_is_avail(acm)) ? acm->writesize : 0;
|
||||
return acm_wb_is_avail(acm) ? acm->writesize : 0;
|
||||
}
|
||||
|
||||
static int acm_tty_chars_in_buffer(struct tty_struct *tty)
|
||||
@ -1072,11 +1075,11 @@ skip_normal_probe:
|
||||
acm->urb_task.data = (unsigned long) acm;
|
||||
INIT_WORK(&acm->work, acm_softint);
|
||||
INIT_WORK(&acm->waker, acm_waker);
|
||||
init_waitqueue_head(&acm->drain_wait);
|
||||
spin_lock_init(&acm->throttle_lock);
|
||||
spin_lock_init(&acm->write_lock);
|
||||
spin_lock_init(&acm->read_lock);
|
||||
mutex_init(&acm->mutex);
|
||||
acm->write_ready = 1;
|
||||
acm->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress);
|
||||
|
||||
buf = usb_buffer_alloc(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma);
|
||||
@ -1108,9 +1111,11 @@ skip_normal_probe:
|
||||
rcv->instance = acm;
|
||||
}
|
||||
for (i = 0; i < num_rx_buf; i++) {
|
||||
struct acm_rb *buf = &(acm->rb[i]);
|
||||
struct acm_rb *rb = &(acm->rb[i]);
|
||||
|
||||
if (!(buf->base = usb_buffer_alloc(acm->dev, readsize, GFP_KERNEL, &buf->dma))) {
|
||||
rb->base = usb_buffer_alloc(acm->dev, readsize,
|
||||
GFP_KERNEL, &rb->dma);
|
||||
if (!rb->base) {
|
||||
dev_dbg(&intf->dev, "out of memory (read bufs usb_buffer_alloc)\n");
|
||||
goto alloc_fail7;
|
||||
}
|
||||
@ -1172,6 +1177,7 @@ skip_countries:
|
||||
acm_set_line(acm, &acm->line);
|
||||
|
||||
usb_driver_claim_interface(&acm_driver, data_interface, acm);
|
||||
usb_set_intfdata(data_interface, acm);
|
||||
|
||||
usb_get_intf(control_interface);
|
||||
tty_register_device(acm_tty_driver, minor, &control_interface->dev);
|
||||
@ -1221,11 +1227,11 @@ static void acm_disconnect(struct usb_interface *intf)
|
||||
struct acm *acm = usb_get_intfdata(intf);
|
||||
struct usb_device *usb_dev = interface_to_usbdev(intf);
|
||||
|
||||
mutex_lock(&open_mutex);
|
||||
if (!acm || !acm->dev) {
|
||||
mutex_unlock(&open_mutex);
|
||||
/* sibling interface is already cleaning up */
|
||||
if (!acm)
|
||||
return;
|
||||
}
|
||||
|
||||
mutex_lock(&open_mutex);
|
||||
if (acm->country_codes){
|
||||
device_remove_file(&acm->control->dev,
|
||||
&dev_attr_wCountryCodes);
|
||||
|
@ -106,8 +106,6 @@ struct acm {
|
||||
struct list_head spare_read_bufs;
|
||||
struct list_head filled_read_bufs;
|
||||
int write_used; /* number of non-empty write buffers */
|
||||
int write_ready; /* write urb is not running */
|
||||
int old_ready;
|
||||
int processing;
|
||||
int transmitting;
|
||||
spinlock_t write_lock;
|
||||
@ -115,6 +113,7 @@ struct acm {
|
||||
struct usb_cdc_line_coding line; /* bits, stop, parity */
|
||||
struct work_struct work; /* work queue entry for line discipline waking up */
|
||||
struct work_struct waker;
|
||||
wait_queue_head_t drain_wait; /* close processing */
|
||||
struct tasklet_struct urb_task; /* rx processing */
|
||||
spinlock_t throttle_lock; /* synchronize throtteling and read callback */
|
||||
unsigned int ctrlin; /* input control lines (DCD, DSR, RI, break, overruns) */
|
||||
|
@ -774,7 +774,6 @@ void usb_deregister(struct usb_driver *driver)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_deregister);
|
||||
|
||||
|
||||
/* Forced unbinding of a USB interface driver, either because
|
||||
* it doesn't support pre_reset/post_reset/reset_resume or
|
||||
* because it doesn't support suspend/resume.
|
||||
@ -821,6 +820,8 @@ void usb_rebind_intf(struct usb_interface *intf)
|
||||
dev_warn(&intf->dev, "rebind failed: %d\n", rc);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
#define DO_UNBIND 0
|
||||
#define DO_REBIND 1
|
||||
|
||||
@ -872,8 +873,6 @@ static void do_unbind_rebind(struct usb_device *udev, int action)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
/* Caller has locked udev's pm_mutex */
|
||||
static int usb_suspend_device(struct usb_device *udev, pm_message_t msg)
|
||||
{
|
||||
|
@ -1091,8 +1091,8 @@ void usb_disable_device(struct usb_device *dev, int skip_ep0)
|
||||
continue;
|
||||
dev_dbg(&dev->dev, "unregistering interface %s\n",
|
||||
dev_name(&interface->dev));
|
||||
device_del(&interface->dev);
|
||||
usb_remove_sysfs_intf_files(interface);
|
||||
device_del(&interface->dev);
|
||||
}
|
||||
|
||||
/* Now that the interfaces are unbound, nobody should
|
||||
|
@ -284,6 +284,16 @@ config USB_LH7A40X
|
||||
default USB_GADGET
|
||||
select USB_GADGET_SELECTED
|
||||
|
||||
# built in ../musb along with host support
|
||||
config USB_GADGET_MUSB_HDRC
|
||||
boolean "Inventra HDRC USB Peripheral (TI, ...)"
|
||||
depends on USB_MUSB_HDRC && (USB_MUSB_PERIPHERAL || USB_MUSB_OTG)
|
||||
select USB_GADGET_DUALSPEED
|
||||
select USB_GADGET_SELECTED
|
||||
help
|
||||
This OTG-capable silicon IP is used in dual designs including
|
||||
the TI DaVinci, OMAP 243x, OMAP 343x, and TUSB 6010.
|
||||
|
||||
config USB_GADGET_OMAP
|
||||
boolean "OMAP USB Device Controller"
|
||||
depends on ARCH_OMAP
|
||||
|
@ -542,13 +542,14 @@ dummy_queue (struct usb_ep *_ep, struct usb_request *_req,
|
||||
req->req.context = dum;
|
||||
req->req.complete = fifo_complete;
|
||||
|
||||
list_add_tail(&req->queue, &ep->queue);
|
||||
spin_unlock (&dum->lock);
|
||||
_req->actual = _req->length;
|
||||
_req->status = 0;
|
||||
_req->complete (_ep, _req);
|
||||
spin_lock (&dum->lock);
|
||||
}
|
||||
list_add_tail (&req->queue, &ep->queue);
|
||||
} else
|
||||
list_add_tail(&req->queue, &ep->queue);
|
||||
spin_unlock_irqrestore (&dum->lock, flags);
|
||||
|
||||
/* real hardware would likely enable transfers here, in case
|
||||
|
@ -47,18 +47,37 @@ struct f_acm {
|
||||
u8 ctrl_id, data_id;
|
||||
u8 port_num;
|
||||
|
||||
struct usb_descriptor_header **fs_function;
|
||||
u8 pending;
|
||||
|
||||
/* lock is mostly for pending and notify_req ... they get accessed
|
||||
* by callbacks both from tty (open/close/break) under its spinlock,
|
||||
* and notify_req.complete() which can't use that lock.
|
||||
*/
|
||||
spinlock_t lock;
|
||||
|
||||
struct acm_ep_descs fs;
|
||||
struct usb_descriptor_header **hs_function;
|
||||
struct acm_ep_descs hs;
|
||||
|
||||
struct usb_ep *notify;
|
||||
struct usb_endpoint_descriptor *notify_desc;
|
||||
struct usb_request *notify_req;
|
||||
|
||||
struct usb_cdc_line_coding port_line_coding; /* 8-N-1 etc */
|
||||
|
||||
/* SetControlLineState request -- CDC 1.1 section 6.2.14 (INPUT) */
|
||||
u16 port_handshake_bits;
|
||||
#define RS232_RTS (1 << 1) /* unused with full duplex */
|
||||
#define RS232_DTR (1 << 0) /* host is ready for data r/w */
|
||||
#define ACM_CTRL_RTS (1 << 1) /* unused with full duplex */
|
||||
#define ACM_CTRL_DTR (1 << 0) /* host is ready for data r/w */
|
||||
|
||||
/* SerialState notification -- CDC 1.1 section 6.3.5 (OUTPUT) */
|
||||
u16 serial_state;
|
||||
#define ACM_CTRL_OVERRUN (1 << 6)
|
||||
#define ACM_CTRL_PARITY (1 << 5)
|
||||
#define ACM_CTRL_FRAMING (1 << 4)
|
||||
#define ACM_CTRL_RI (1 << 3)
|
||||
#define ACM_CTRL_BRK (1 << 2)
|
||||
#define ACM_CTRL_DSR (1 << 1)
|
||||
#define ACM_CTRL_DCD (1 << 0)
|
||||
};
|
||||
|
||||
static inline struct f_acm *func_to_acm(struct usb_function *f)
|
||||
@ -66,12 +85,17 @@ static inline struct f_acm *func_to_acm(struct usb_function *f)
|
||||
return container_of(f, struct f_acm, port.func);
|
||||
}
|
||||
|
||||
static inline struct f_acm *port_to_acm(struct gserial *p)
|
||||
{
|
||||
return container_of(p, struct f_acm, port);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* notification endpoint uses smallish and infrequent fixed-size messages */
|
||||
|
||||
#define GS_LOG2_NOTIFY_INTERVAL 5 /* 1 << 5 == 32 msec */
|
||||
#define GS_NOTIFY_MAXPACKET 8
|
||||
#define GS_NOTIFY_MAXPACKET 10 /* notification + 2 bytes */
|
||||
|
||||
/* interface and class descriptors: */
|
||||
|
||||
@ -117,7 +141,7 @@ static struct usb_cdc_acm_descriptor acm_descriptor __initdata = {
|
||||
.bLength = sizeof(acm_descriptor),
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
.bDescriptorSubType = USB_CDC_ACM_TYPE,
|
||||
.bmCapabilities = (1 << 1),
|
||||
.bmCapabilities = USB_CDC_CAP_LINE,
|
||||
};
|
||||
|
||||
static struct usb_cdc_union_desc acm_union_desc __initdata = {
|
||||
@ -277,6 +301,11 @@ static int acm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
|
||||
|
||||
/* composite driver infrastructure handles everything except
|
||||
* CDC class messages; interface activation uses set_alt().
|
||||
*
|
||||
* Note CDC spec table 4 lists the ACM request profile. It requires
|
||||
* encapsulated command support ... we don't handle any, and respond
|
||||
* to them by stalling. Options include get/set/clear comm features
|
||||
* (not that useful) and SEND_BREAK.
|
||||
*/
|
||||
switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
|
||||
|
||||
@ -312,7 +341,7 @@ static int acm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
|
||||
value = 0;
|
||||
|
||||
/* FIXME we should not allow data to flow until the
|
||||
* host sets the RS232_DTR bit; and when it clears
|
||||
* host sets the ACM_CTRL_DTR bit; and when it clears
|
||||
* that bit, we should return to that no-flow state.
|
||||
*/
|
||||
acm->port_handshake_bits = w_value;
|
||||
@ -350,9 +379,6 @@ static int acm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
|
||||
/* we know alt == 0, so this is an activation or a reset */
|
||||
|
||||
if (intf == acm->ctrl_id) {
|
||||
/* REVISIT this may need more work when we start to
|
||||
* send notifications ...
|
||||
*/
|
||||
if (acm->notify->driver_data) {
|
||||
VDBG(cdev, "reset acm control interface %d\n", intf);
|
||||
usb_ep_disable(acm->notify);
|
||||
@ -397,6 +423,128 @@ static void acm_disable(struct usb_function *f)
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* acm_cdc_notify - issue CDC notification to host
|
||||
* @acm: wraps host to be notified
|
||||
* @type: notification type
|
||||
* @value: Refer to cdc specs, wValue field.
|
||||
* @data: data to be sent
|
||||
* @length: size of data
|
||||
* Context: irqs blocked, acm->lock held, acm_notify_req non-null
|
||||
*
|
||||
* Returns zero on sucess or a negative errno.
|
||||
*
|
||||
* See section 6.3.5 of the CDC 1.1 specification for information
|
||||
* about the only notification we issue: SerialState change.
|
||||
*/
|
||||
static int acm_cdc_notify(struct f_acm *acm, u8 type, u16 value,
|
||||
void *data, unsigned length)
|
||||
{
|
||||
struct usb_ep *ep = acm->notify;
|
||||
struct usb_request *req;
|
||||
struct usb_cdc_notification *notify;
|
||||
const unsigned len = sizeof(*notify) + length;
|
||||
void *buf;
|
||||
int status;
|
||||
|
||||
req = acm->notify_req;
|
||||
acm->notify_req = NULL;
|
||||
acm->pending = false;
|
||||
|
||||
req->length = len;
|
||||
notify = req->buf;
|
||||
buf = notify + 1;
|
||||
|
||||
notify->bmRequestType = USB_DIR_IN | USB_TYPE_CLASS
|
||||
| USB_RECIP_INTERFACE;
|
||||
notify->bNotificationType = type;
|
||||
notify->wValue = cpu_to_le16(value);
|
||||
notify->wIndex = cpu_to_le16(acm->ctrl_id);
|
||||
notify->wLength = cpu_to_le16(length);
|
||||
memcpy(buf, data, length);
|
||||
|
||||
status = usb_ep_queue(ep, req, GFP_ATOMIC);
|
||||
if (status < 0) {
|
||||
ERROR(acm->port.func.config->cdev,
|
||||
"acm ttyGS%d can't notify serial state, %d\n",
|
||||
acm->port_num, status);
|
||||
acm->notify_req = req;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int acm_notify_serial_state(struct f_acm *acm)
|
||||
{
|
||||
struct usb_composite_dev *cdev = acm->port.func.config->cdev;
|
||||
int status;
|
||||
|
||||
spin_lock(&acm->lock);
|
||||
if (acm->notify_req) {
|
||||
DBG(cdev, "acm ttyGS%d serial state %04x\n",
|
||||
acm->port_num, acm->serial_state);
|
||||
status = acm_cdc_notify(acm, USB_CDC_NOTIFY_SERIAL_STATE,
|
||||
0, &acm->serial_state, sizeof(acm->serial_state));
|
||||
} else {
|
||||
acm->pending = true;
|
||||
status = 0;
|
||||
}
|
||||
spin_unlock(&acm->lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
static void acm_cdc_notify_complete(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
struct f_acm *acm = req->context;
|
||||
u8 doit = false;
|
||||
|
||||
/* on this call path we do NOT hold the port spinlock,
|
||||
* which is why ACM needs its own spinlock
|
||||
*/
|
||||
spin_lock(&acm->lock);
|
||||
if (req->status != -ESHUTDOWN)
|
||||
doit = acm->pending;
|
||||
acm->notify_req = req;
|
||||
spin_unlock(&acm->lock);
|
||||
|
||||
if (doit)
|
||||
acm_notify_serial_state(acm);
|
||||
}
|
||||
|
||||
/* connect == the TTY link is open */
|
||||
|
||||
static void acm_connect(struct gserial *port)
|
||||
{
|
||||
struct f_acm *acm = port_to_acm(port);
|
||||
|
||||
acm->serial_state |= ACM_CTRL_DSR | ACM_CTRL_DCD;
|
||||
acm_notify_serial_state(acm);
|
||||
}
|
||||
|
||||
static void acm_disconnect(struct gserial *port)
|
||||
{
|
||||
struct f_acm *acm = port_to_acm(port);
|
||||
|
||||
acm->serial_state &= ~(ACM_CTRL_DSR | ACM_CTRL_DCD);
|
||||
acm_notify_serial_state(acm);
|
||||
}
|
||||
|
||||
static int acm_send_break(struct gserial *port, int duration)
|
||||
{
|
||||
struct f_acm *acm = port_to_acm(port);
|
||||
u16 state;
|
||||
|
||||
state = acm->serial_state;
|
||||
state &= ~ACM_CTRL_BRK;
|
||||
if (duration)
|
||||
state |= ACM_CTRL_BRK;
|
||||
|
||||
acm->serial_state = state;
|
||||
return acm_notify_serial_state(acm);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* ACM function driver setup/binding */
|
||||
static int __init
|
||||
acm_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
@ -445,8 +593,20 @@ acm_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
acm->notify = ep;
|
||||
ep->driver_data = cdev; /* claim */
|
||||
|
||||
/* allocate notification */
|
||||
acm->notify_req = gs_alloc_req(ep,
|
||||
sizeof(struct usb_cdc_notification) + 2,
|
||||
GFP_KERNEL);
|
||||
if (!acm->notify_req)
|
||||
goto fail;
|
||||
|
||||
acm->notify_req->complete = acm_cdc_notify_complete;
|
||||
acm->notify_req->context = acm;
|
||||
|
||||
/* copy descriptors, and track endpoint copies */
|
||||
f->descriptors = usb_copy_descriptors(acm_fs_function);
|
||||
if (!f->descriptors)
|
||||
goto fail;
|
||||
|
||||
acm->fs.in = usb_find_endpoint(acm_fs_function,
|
||||
f->descriptors, &acm_fs_in_desc);
|
||||
@ -478,8 +638,6 @@ acm_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
f->hs_descriptors, &acm_hs_notify_desc);
|
||||
}
|
||||
|
||||
/* FIXME provide a callback for triggering notifications */
|
||||
|
||||
DBG(cdev, "acm ttyGS%d: %s speed IN/%s OUT/%s NOTIFY/%s\n",
|
||||
acm->port_num,
|
||||
gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
|
||||
@ -488,6 +646,9 @@ acm_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
if (acm->notify_req)
|
||||
gs_free_req(acm->notify, acm->notify_req);
|
||||
|
||||
/* we might as well release our claims on endpoints */
|
||||
if (acm->notify)
|
||||
acm->notify->driver_data = NULL;
|
||||
@ -504,10 +665,13 @@ fail:
|
||||
static void
|
||||
acm_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
struct f_acm *acm = func_to_acm(f);
|
||||
|
||||
if (gadget_is_dualspeed(c->cdev->gadget))
|
||||
usb_free_descriptors(f->hs_descriptors);
|
||||
usb_free_descriptors(f->descriptors);
|
||||
kfree(func_to_acm(f));
|
||||
gs_free_req(acm->notify, acm->notify_req);
|
||||
kfree(acm);
|
||||
}
|
||||
|
||||
/* Some controllers can't support CDC ACM ... */
|
||||
@ -571,8 +735,14 @@ int __init acm_bind_config(struct usb_configuration *c, u8 port_num)
|
||||
if (!acm)
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock_init(&acm->lock);
|
||||
|
||||
acm->port_num = port_num;
|
||||
|
||||
acm->port.connect = acm_connect;
|
||||
acm->port.disconnect = acm_disconnect;
|
||||
acm->port.send_break = acm_send_break;
|
||||
|
||||
acm->port.func.name = "acm";
|
||||
acm->port.func.strings = acm_strings;
|
||||
/* descriptors are per-instance copies */
|
||||
|
@ -63,9 +63,7 @@ struct f_ecm {
|
||||
|
||||
char ethaddr[14];
|
||||
|
||||
struct usb_descriptor_header **fs_function;
|
||||
struct ecm_ep_descs fs;
|
||||
struct usb_descriptor_header **hs_function;
|
||||
struct ecm_ep_descs hs;
|
||||
|
||||
struct usb_ep *notify;
|
||||
|
@ -85,9 +85,7 @@ struct f_rndis {
|
||||
u8 ethaddr[ETH_ALEN];
|
||||
int config;
|
||||
|
||||
struct usb_descriptor_header **fs_function;
|
||||
struct rndis_ep_descs fs;
|
||||
struct usb_descriptor_header **hs_function;
|
||||
struct rndis_ep_descs hs;
|
||||
|
||||
struct usb_ep *notify;
|
||||
|
@ -36,9 +36,7 @@ struct f_gser {
|
||||
u8 data_id;
|
||||
u8 port_num;
|
||||
|
||||
struct usb_descriptor_header **fs_function;
|
||||
struct gser_descs fs;
|
||||
struct usb_descriptor_header **hs_function;
|
||||
struct gser_descs hs;
|
||||
};
|
||||
|
||||
|
@ -66,9 +66,7 @@ struct f_gether {
|
||||
|
||||
char ethaddr[14];
|
||||
|
||||
struct usb_descriptor_header **fs_function;
|
||||
struct geth_descs fs;
|
||||
struct usb_descriptor_header **hs_function;
|
||||
struct geth_descs hs;
|
||||
};
|
||||
|
||||
|
@ -11,6 +11,10 @@
|
||||
* Some are available on 2.4 kernels; several are available, but not
|
||||
* yet pushed in the 2.6 mainline tree.
|
||||
*/
|
||||
|
||||
#ifndef __GADGET_CHIPS_H
|
||||
#define __GADGET_CHIPS_H
|
||||
|
||||
#ifdef CONFIG_USB_GADGET_NET2280
|
||||
#define gadget_is_net2280(g) !strcmp("net2280", (g)->name)
|
||||
#else
|
||||
@ -237,3 +241,5 @@ static inline bool gadget_supports_altsettings(struct usb_gadget *gadget)
|
||||
/* Everything else is *presumably* fine ... */
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif /* __GADGET_CHIPS_H */
|
||||
|
@ -54,6 +54,7 @@
|
||||
|
||||
#include <mach/dma.h>
|
||||
#include <mach/usb.h>
|
||||
#include <mach/control.h>
|
||||
|
||||
#include "omap_udc.h"
|
||||
|
||||
@ -2310,10 +2311,10 @@ static int proc_otg_show(struct seq_file *s)
|
||||
u32 trans;
|
||||
char *ctrl_name;
|
||||
|
||||
tmp = OTG_REV_REG;
|
||||
tmp = omap_readl(OTG_REV);
|
||||
if (cpu_is_omap24xx()) {
|
||||
ctrl_name = "control_devconf";
|
||||
trans = CONTROL_DEVCONF_REG;
|
||||
trans = omap_ctrl_readl(OMAP2_CONTROL_DEVCONF0);
|
||||
} else {
|
||||
ctrl_name = "tranceiver_ctrl";
|
||||
trans = omap_readw(USB_TRANSCEIVER_CTRL);
|
||||
|
@ -52,13 +52,16 @@
|
||||
* is managed in userspace ... OBEX, PTP, and MTP have been mentioned.
|
||||
*/
|
||||
|
||||
#define PREFIX "ttyGS"
|
||||
|
||||
/*
|
||||
* gserial is the lifecycle interface, used by USB functions
|
||||
* gs_port is the I/O nexus, used by the tty driver
|
||||
* tty_struct links to the tty/filesystem framework
|
||||
*
|
||||
* gserial <---> gs_port ... links will be null when the USB link is
|
||||
* inactive; managed by gserial_{connect,disconnect}().
|
||||
* inactive; managed by gserial_{connect,disconnect}(). each gserial
|
||||
* instance can wrap its own USB control protocol.
|
||||
* gserial->ioport == usb_ep->driver_data ... gs_port
|
||||
* gs_port->port_usb ... gserial
|
||||
*
|
||||
@ -100,6 +103,8 @@ struct gs_port {
|
||||
wait_queue_head_t close_wait; /* wait for last close */
|
||||
|
||||
struct list_head read_pool;
|
||||
struct list_head read_queue;
|
||||
unsigned n_read;
|
||||
struct tasklet_struct push;
|
||||
|
||||
struct list_head write_pool;
|
||||
@ -177,7 +182,7 @@ static void gs_buf_clear(struct gs_buf *gb)
|
||||
/*
|
||||
* gs_buf_data_avail
|
||||
*
|
||||
* Return the number of bytes of data available in the circular
|
||||
* Return the number of bytes of data written into the circular
|
||||
* buffer.
|
||||
*/
|
||||
static unsigned gs_buf_data_avail(struct gs_buf *gb)
|
||||
@ -278,7 +283,7 @@ gs_buf_get(struct gs_buf *gb, char *buf, unsigned count)
|
||||
* Allocate a usb_request and its buffer. Returns a pointer to the
|
||||
* usb_request or NULL if there is an error.
|
||||
*/
|
||||
static struct usb_request *
|
||||
struct usb_request *
|
||||
gs_alloc_req(struct usb_ep *ep, unsigned len, gfp_t kmalloc_flags)
|
||||
{
|
||||
struct usb_request *req;
|
||||
@ -302,7 +307,7 @@ gs_alloc_req(struct usb_ep *ep, unsigned len, gfp_t kmalloc_flags)
|
||||
*
|
||||
* Free a usb_request and its buffer.
|
||||
*/
|
||||
static void gs_free_req(struct usb_ep *ep, struct usb_request *req)
|
||||
void gs_free_req(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
kfree(req->buf);
|
||||
usb_ep_free_request(ep, req);
|
||||
@ -367,11 +372,9 @@ __acquires(&port->port_lock)
|
||||
req->length = len;
|
||||
list_del(&req->list);
|
||||
|
||||
#ifdef VERBOSE_DEBUG
|
||||
pr_debug("%s: %s, len=%d, 0x%02x 0x%02x 0x%02x ...\n",
|
||||
__func__, in->name, len, *((u8 *)req->buf),
|
||||
pr_vdebug(PREFIX "%d: tx len=%d, 0x%02x 0x%02x 0x%02x ...\n",
|
||||
port->port_num, len, *((u8 *)req->buf),
|
||||
*((u8 *)req->buf+1), *((u8 *)req->buf+2));
|
||||
#endif
|
||||
|
||||
/* Drop lock while we call out of driver; completions
|
||||
* could be issued while we do so. Disconnection may
|
||||
@ -401,56 +404,6 @@ __acquires(&port->port_lock)
|
||||
return status;
|
||||
}
|
||||
|
||||
static void gs_rx_push(unsigned long _port)
|
||||
{
|
||||
struct gs_port *port = (void *)_port;
|
||||
struct tty_struct *tty = port->port_tty;
|
||||
|
||||
/* With low_latency, tty_flip_buffer_push() doesn't put its
|
||||
* real work through a workqueue, so the ldisc has a better
|
||||
* chance to keep up with peak USB data rates.
|
||||
*/
|
||||
if (tty) {
|
||||
tty_flip_buffer_push(tty);
|
||||
wake_up_interruptible(&tty->read_wait);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* gs_recv_packet
|
||||
*
|
||||
* Called for each USB packet received. Reads the packet
|
||||
* header and stuffs the data in the appropriate tty buffer.
|
||||
* Returns 0 if successful, or a negative error number.
|
||||
*
|
||||
* Called during USB completion routine, on interrupt time.
|
||||
* With port_lock.
|
||||
*/
|
||||
static int gs_recv_packet(struct gs_port *port, char *packet, unsigned size)
|
||||
{
|
||||
unsigned len;
|
||||
struct tty_struct *tty;
|
||||
|
||||
/* I/O completions can continue for a while after close(), until the
|
||||
* request queue empties. Just discard any data we receive, until
|
||||
* something reopens this TTY ... as if there were no HW flow control.
|
||||
*/
|
||||
tty = port->port_tty;
|
||||
if (tty == NULL) {
|
||||
pr_vdebug("%s: ttyGS%d, after close\n",
|
||||
__func__, port->port_num);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
len = tty_insert_flip_string(tty, packet, size);
|
||||
if (len > 0)
|
||||
tasklet_schedule(&port->push);
|
||||
if (len < size)
|
||||
pr_debug("%s: ttyGS%d, drop %d bytes\n",
|
||||
__func__, port->port_num, size - len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: caller owns port_lock, and port_usb is set
|
||||
*/
|
||||
@ -469,9 +422,9 @@ __acquires(&port->port_lock)
|
||||
int status;
|
||||
struct tty_struct *tty;
|
||||
|
||||
/* no more rx if closed or throttled */
|
||||
/* no more rx if closed */
|
||||
tty = port->port_tty;
|
||||
if (!tty || test_bit(TTY_THROTTLED, &tty->flags))
|
||||
if (!tty)
|
||||
break;
|
||||
|
||||
req = list_entry(pool->next, struct usb_request, list);
|
||||
@ -500,36 +453,134 @@ __acquires(&port->port_lock)
|
||||
return started;
|
||||
}
|
||||
|
||||
/*
|
||||
* RX tasklet takes data out of the RX queue and hands it up to the TTY
|
||||
* layer until it refuses to take any more data (or is throttled back).
|
||||
* Then it issues reads for any further data.
|
||||
*
|
||||
* If the RX queue becomes full enough that no usb_request is queued,
|
||||
* the OUT endpoint may begin NAKing as soon as its FIFO fills up.
|
||||
* So QUEUE_SIZE packets plus however many the FIFO holds (usually two)
|
||||
* can be buffered before the TTY layer's buffers (currently 64 KB).
|
||||
*/
|
||||
static void gs_rx_push(unsigned long _port)
|
||||
{
|
||||
struct gs_port *port = (void *)_port;
|
||||
struct tty_struct *tty;
|
||||
struct list_head *queue = &port->read_queue;
|
||||
bool disconnect = false;
|
||||
bool do_push = false;
|
||||
|
||||
/* hand any queued data to the tty */
|
||||
spin_lock_irq(&port->port_lock);
|
||||
tty = port->port_tty;
|
||||
while (!list_empty(queue)) {
|
||||
struct usb_request *req;
|
||||
|
||||
req = list_first_entry(queue, struct usb_request, list);
|
||||
|
||||
/* discard data if tty was closed */
|
||||
if (!tty)
|
||||
goto recycle;
|
||||
|
||||
/* leave data queued if tty was rx throttled */
|
||||
if (test_bit(TTY_THROTTLED, &tty->flags))
|
||||
break;
|
||||
|
||||
switch (req->status) {
|
||||
case -ESHUTDOWN:
|
||||
disconnect = true;
|
||||
pr_vdebug(PREFIX "%d: shutdown\n", port->port_num);
|
||||
break;
|
||||
|
||||
default:
|
||||
/* presumably a transient fault */
|
||||
pr_warning(PREFIX "%d: unexpected RX status %d\n",
|
||||
port->port_num, req->status);
|
||||
/* FALLTHROUGH */
|
||||
case 0:
|
||||
/* normal completion */
|
||||
break;
|
||||
}
|
||||
|
||||
/* push data to (open) tty */
|
||||
if (req->actual) {
|
||||
char *packet = req->buf;
|
||||
unsigned size = req->actual;
|
||||
unsigned n;
|
||||
int count;
|
||||
|
||||
/* we may have pushed part of this packet already... */
|
||||
n = port->n_read;
|
||||
if (n) {
|
||||
packet += n;
|
||||
size -= n;
|
||||
}
|
||||
|
||||
count = tty_insert_flip_string(tty, packet, size);
|
||||
if (count)
|
||||
do_push = true;
|
||||
if (count != size) {
|
||||
/* stop pushing; TTY layer can't handle more */
|
||||
port->n_read += count;
|
||||
pr_vdebug(PREFIX "%d: rx block %d/%d\n",
|
||||
port->port_num,
|
||||
count, req->actual);
|
||||
break;
|
||||
}
|
||||
port->n_read = 0;
|
||||
}
|
||||
recycle:
|
||||
list_move(&req->list, &port->read_pool);
|
||||
}
|
||||
|
||||
/* Push from tty to ldisc; this is immediate with low_latency, and
|
||||
* may trigger callbacks to this driver ... so drop the spinlock.
|
||||
*/
|
||||
if (tty && do_push) {
|
||||
spin_unlock_irq(&port->port_lock);
|
||||
tty_flip_buffer_push(tty);
|
||||
wake_up_interruptible(&tty->read_wait);
|
||||
spin_lock_irq(&port->port_lock);
|
||||
|
||||
/* tty may have been closed */
|
||||
tty = port->port_tty;
|
||||
}
|
||||
|
||||
|
||||
/* We want our data queue to become empty ASAP, keeping data
|
||||
* in the tty and ldisc (not here). If we couldn't push any
|
||||
* this time around, there may be trouble unless there's an
|
||||
* implicit tty_unthrottle() call on its way...
|
||||
*
|
||||
* REVISIT we should probably add a timer to keep the tasklet
|
||||
* from starving ... but it's not clear that case ever happens.
|
||||
*/
|
||||
if (!list_empty(queue) && tty) {
|
||||
if (!test_bit(TTY_THROTTLED, &tty->flags)) {
|
||||
if (do_push)
|
||||
tasklet_schedule(&port->push);
|
||||
else
|
||||
pr_warning(PREFIX "%d: RX not scheduled?\n",
|
||||
port->port_num);
|
||||
}
|
||||
}
|
||||
|
||||
/* If we're still connected, refill the USB RX queue. */
|
||||
if (!disconnect && port->port_usb)
|
||||
gs_start_rx(port);
|
||||
|
||||
spin_unlock_irq(&port->port_lock);
|
||||
}
|
||||
|
||||
static void gs_read_complete(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
int status;
|
||||
struct gs_port *port = ep->driver_data;
|
||||
|
||||
/* Queue all received data until the tty layer is ready for it. */
|
||||
spin_lock(&port->port_lock);
|
||||
list_add(&req->list, &port->read_pool);
|
||||
|
||||
switch (req->status) {
|
||||
case 0:
|
||||
/* normal completion */
|
||||
status = gs_recv_packet(port, req->buf, req->actual);
|
||||
if (status && status != -EIO)
|
||||
pr_debug("%s: %s %s err %d\n",
|
||||
__func__, "recv", ep->name, status);
|
||||
gs_start_rx(port);
|
||||
break;
|
||||
|
||||
case -ESHUTDOWN:
|
||||
/* disconnect */
|
||||
pr_vdebug("%s: %s shutdown\n", __func__, ep->name);
|
||||
break;
|
||||
|
||||
default:
|
||||
/* presumably a transient fault */
|
||||
pr_warning("%s: unexpected %s status %d\n",
|
||||
__func__, ep->name, req->status);
|
||||
gs_start_rx(port);
|
||||
break;
|
||||
}
|
||||
list_add_tail(&req->list, &port->read_queue);
|
||||
tasklet_schedule(&port->push);
|
||||
spin_unlock(&port->port_lock);
|
||||
}
|
||||
|
||||
@ -625,6 +676,7 @@ static int gs_start_io(struct gs_port *port)
|
||||
}
|
||||
|
||||
/* queue read requests */
|
||||
port->n_read = 0;
|
||||
started = gs_start_rx(port);
|
||||
|
||||
/* unblock any pending writes into our circular buffer */
|
||||
@ -633,9 +685,10 @@ static int gs_start_io(struct gs_port *port)
|
||||
} else {
|
||||
gs_free_requests(ep, head);
|
||||
gs_free_requests(port->port_usb->in, &port->write_pool);
|
||||
status = -EIO;
|
||||
}
|
||||
|
||||
return started ? 0 : status;
|
||||
return status;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
@ -736,10 +789,13 @@ static int gs_open(struct tty_struct *tty, struct file *file)
|
||||
|
||||
/* if connected, start the I/O stream */
|
||||
if (port->port_usb) {
|
||||
struct gserial *gser = port->port_usb;
|
||||
|
||||
pr_debug("gs_open: start ttyGS%d\n", port->port_num);
|
||||
gs_start_io(port);
|
||||
|
||||
/* REVISIT for ACM, issue "network connected" event */
|
||||
if (gser->connect)
|
||||
gser->connect(gser);
|
||||
}
|
||||
|
||||
pr_debug("gs_open: ttyGS%d (%p,%p)\n", port->port_num, tty, file);
|
||||
@ -766,6 +822,7 @@ static int gs_writes_finished(struct gs_port *p)
|
||||
static void gs_close(struct tty_struct *tty, struct file *file)
|
||||
{
|
||||
struct gs_port *port = tty->driver_data;
|
||||
struct gserial *gser;
|
||||
|
||||
spin_lock_irq(&port->port_lock);
|
||||
|
||||
@ -785,32 +842,31 @@ static void gs_close(struct tty_struct *tty, struct file *file)
|
||||
port->openclose = true;
|
||||
port->open_count = 0;
|
||||
|
||||
if (port->port_usb)
|
||||
/* REVISIT for ACM, issue "network disconnected" event */;
|
||||
gser = port->port_usb;
|
||||
if (gser && gser->disconnect)
|
||||
gser->disconnect(gser);
|
||||
|
||||
/* wait for circular write buffer to drain, disconnect, or at
|
||||
* most GS_CLOSE_TIMEOUT seconds; then discard the rest
|
||||
*/
|
||||
if (gs_buf_data_avail(&port->port_write_buf) > 0
|
||||
&& port->port_usb) {
|
||||
if (gs_buf_data_avail(&port->port_write_buf) > 0 && gser) {
|
||||
spin_unlock_irq(&port->port_lock);
|
||||
wait_event_interruptible_timeout(port->drain_wait,
|
||||
gs_writes_finished(port),
|
||||
GS_CLOSE_TIMEOUT * HZ);
|
||||
spin_lock_irq(&port->port_lock);
|
||||
gser = port->port_usb;
|
||||
}
|
||||
|
||||
/* Iff we're disconnected, there can be no I/O in flight so it's
|
||||
* ok to free the circular buffer; else just scrub it. And don't
|
||||
* let the push tasklet fire again until we're re-opened.
|
||||
*/
|
||||
if (port->port_usb == NULL)
|
||||
if (gser == NULL)
|
||||
gs_buf_free(&port->port_write_buf);
|
||||
else
|
||||
gs_buf_clear(&port->port_write_buf);
|
||||
|
||||
tasklet_kill(&port->push);
|
||||
|
||||
tty->driver_data = NULL;
|
||||
port->port_tty = NULL;
|
||||
|
||||
@ -911,15 +967,35 @@ static void gs_unthrottle(struct tty_struct *tty)
|
||||
{
|
||||
struct gs_port *port = tty->driver_data;
|
||||
unsigned long flags;
|
||||
unsigned started = 0;
|
||||
|
||||
spin_lock_irqsave(&port->port_lock, flags);
|
||||
if (port->port_usb)
|
||||
started = gs_start_rx(port);
|
||||
if (port->port_usb) {
|
||||
/* Kickstart read queue processing. We don't do xon/xoff,
|
||||
* rts/cts, or other handshaking with the host, but if the
|
||||
* read queue backs up enough we'll be NAKing OUT packets.
|
||||
*/
|
||||
tasklet_schedule(&port->push);
|
||||
pr_vdebug(PREFIX "%d: unthrottle\n", port->port_num);
|
||||
}
|
||||
spin_unlock_irqrestore(&port->port_lock, flags);
|
||||
}
|
||||
|
||||
pr_vdebug("gs_unthrottle: ttyGS%d, %d packets\n",
|
||||
port->port_num, started);
|
||||
static int gs_break_ctl(struct tty_struct *tty, int duration)
|
||||
{
|
||||
struct gs_port *port = tty->driver_data;
|
||||
int status = 0;
|
||||
struct gserial *gser;
|
||||
|
||||
pr_vdebug("gs_break_ctl: ttyGS%d, send break (%d) \n",
|
||||
port->port_num, duration);
|
||||
|
||||
spin_lock_irq(&port->port_lock);
|
||||
gser = port->port_usb;
|
||||
if (gser && gser->send_break)
|
||||
status = gser->send_break(gser, duration);
|
||||
spin_unlock_irq(&port->port_lock);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static const struct tty_operations gs_tty_ops = {
|
||||
@ -931,6 +1007,7 @@ static const struct tty_operations gs_tty_ops = {
|
||||
.write_room = gs_write_room,
|
||||
.chars_in_buffer = gs_chars_in_buffer,
|
||||
.unthrottle = gs_unthrottle,
|
||||
.break_ctl = gs_break_ctl,
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
@ -953,6 +1030,7 @@ gs_port_alloc(unsigned port_num, struct usb_cdc_line_coding *coding)
|
||||
tasklet_init(&port->push, gs_rx_push, (unsigned long) port);
|
||||
|
||||
INIT_LIST_HEAD(&port->read_pool);
|
||||
INIT_LIST_HEAD(&port->read_queue);
|
||||
INIT_LIST_HEAD(&port->write_pool);
|
||||
|
||||
port->port_num = port_num;
|
||||
@ -997,7 +1075,7 @@ int __init gserial_setup(struct usb_gadget *g, unsigned count)
|
||||
|
||||
gs_tty_driver->owner = THIS_MODULE;
|
||||
gs_tty_driver->driver_name = "g_serial";
|
||||
gs_tty_driver->name = "ttyGS";
|
||||
gs_tty_driver->name = PREFIX;
|
||||
/* uses dynamically assigned dev_t values */
|
||||
|
||||
gs_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
|
||||
@ -1104,6 +1182,8 @@ void gserial_cleanup(void)
|
||||
ports[i].port = NULL;
|
||||
mutex_unlock(&ports[i].lock);
|
||||
|
||||
tasklet_kill(&port->push);
|
||||
|
||||
/* wait for old opens to finish */
|
||||
wait_event(port->close_wait, gs_closed(port));
|
||||
|
||||
@ -1175,14 +1255,17 @@ int gserial_connect(struct gserial *gser, u8 port_num)
|
||||
|
||||
/* REVISIT if waiting on "carrier detect", signal. */
|
||||
|
||||
/* REVISIT for ACM, issue "network connection" status notification:
|
||||
* connected if open_count, else disconnected.
|
||||
/* if it's already open, start I/O ... and notify the serial
|
||||
* protocol about open/close status (connect/disconnect).
|
||||
*/
|
||||
|
||||
/* if it's already open, start I/O */
|
||||
if (port->open_count) {
|
||||
pr_debug("gserial_connect: start ttyGS%d\n", port->port_num);
|
||||
gs_start_io(port);
|
||||
if (gser->connect)
|
||||
gser->connect(gser);
|
||||
} else {
|
||||
if (gser->disconnect)
|
||||
gser->disconnect(gser);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&port->port_lock, flags);
|
||||
@ -1241,6 +1324,7 @@ void gserial_disconnect(struct gserial *gser)
|
||||
if (port->open_count == 0 && !port->openclose)
|
||||
gs_buf_free(&port->port_write_buf);
|
||||
gs_free_requests(gser->out, &port->read_pool);
|
||||
gs_free_requests(gser->out, &port->read_queue);
|
||||
gs_free_requests(gser->in, &port->write_pool);
|
||||
spin_unlock_irqrestore(&port->port_lock, flags);
|
||||
}
|
||||
|
@ -23,8 +23,7 @@
|
||||
* style I/O using the USB peripheral endpoints listed here, including
|
||||
* hookups to sysfs and /dev for each logical "tty" device.
|
||||
*
|
||||
* REVISIT need TTY --> USB event flow too, so ACM can report open/close
|
||||
* as carrier detect events. Model after ECM. There's more ACM state too.
|
||||
* REVISIT at least ACM could support tiocmget() if needed.
|
||||
*
|
||||
* REVISIT someday, allow multiplexing several TTYs over these endpoints.
|
||||
*/
|
||||
@ -41,8 +40,17 @@ struct gserial {
|
||||
|
||||
/* REVISIT avoid this CDC-ACM support harder ... */
|
||||
struct usb_cdc_line_coding port_line_coding; /* 9600-8-N-1 etc */
|
||||
|
||||
/* notification callbacks */
|
||||
void (*connect)(struct gserial *p);
|
||||
void (*disconnect)(struct gserial *p);
|
||||
int (*send_break)(struct gserial *p, int duration);
|
||||
};
|
||||
|
||||
/* utilities to allocate/free request and buffer */
|
||||
struct usb_request *gs_alloc_req(struct usb_ep *ep, unsigned len, gfp_t flags);
|
||||
void gs_free_req(struct usb_ep *, struct usb_request *req);
|
||||
|
||||
/* port setup/teardown is handled by gadget driver */
|
||||
int gserial_setup(struct usb_gadget *g, unsigned n_ports);
|
||||
void gserial_cleanup(void);
|
||||
|
@ -126,9 +126,8 @@ static void isp1760_writel(const unsigned int val, __u32 __iomem *regs)
|
||||
* doesn't quite work because some people have to enforce 32-bit access
|
||||
*/
|
||||
static void priv_read_copy(struct isp1760_hcd *priv, u32 *src,
|
||||
__u32 __iomem *dst, u32 offset, u32 len)
|
||||
__u32 __iomem *dst, u32 len)
|
||||
{
|
||||
struct usb_hcd *hcd = priv_to_hcd(priv);
|
||||
u32 val;
|
||||
u8 *buff8;
|
||||
|
||||
@ -136,11 +135,6 @@ static void priv_read_copy(struct isp1760_hcd *priv, u32 *src,
|
||||
printk(KERN_ERR "ERROR: buffer: %p len: %d\n", src, len);
|
||||
return;
|
||||
}
|
||||
isp1760_writel(offset, hcd->regs + HC_MEMORY_REG);
|
||||
/* XXX
|
||||
* 90nsec delay, the spec says something how this could be avoided.
|
||||
*/
|
||||
mdelay(1);
|
||||
|
||||
while (len >= 4) {
|
||||
*src = __raw_readl(dst);
|
||||
@ -987,8 +981,20 @@ static void do_atl_int(struct usb_hcd *usb_hcd)
|
||||
printk(KERN_ERR "qh is 0\n");
|
||||
continue;
|
||||
}
|
||||
priv_read_copy(priv, (u32 *)&ptd, usb_hcd->regs + atl_regs,
|
||||
atl_regs, sizeof(ptd));
|
||||
isp1760_writel(atl_regs + ISP_BANK(0), usb_hcd->regs +
|
||||
HC_MEMORY_REG);
|
||||
isp1760_writel(payload + ISP_BANK(1), usb_hcd->regs +
|
||||
HC_MEMORY_REG);
|
||||
/*
|
||||
* write bank1 address twice to ensure the 90ns delay (time
|
||||
* between BANK0 write and the priv_read_copy() call is at
|
||||
* least 3*t_WHWL + 2*t_w11 = 3*25ns + 2*17ns = 92ns)
|
||||
*/
|
||||
isp1760_writel(payload + ISP_BANK(1), usb_hcd->regs +
|
||||
HC_MEMORY_REG);
|
||||
|
||||
priv_read_copy(priv, (u32 *)&ptd, usb_hcd->regs + atl_regs +
|
||||
ISP_BANK(0), sizeof(ptd));
|
||||
|
||||
dw1 = le32_to_cpu(ptd.dw1);
|
||||
dw2 = le32_to_cpu(ptd.dw2);
|
||||
@ -1091,7 +1097,7 @@ static void do_atl_int(struct usb_hcd *usb_hcd)
|
||||
case IN_PID:
|
||||
priv_read_copy(priv,
|
||||
priv->atl_ints[queue_entry].data_buffer,
|
||||
usb_hcd->regs + payload, payload,
|
||||
usb_hcd->regs + payload + ISP_BANK(1),
|
||||
length);
|
||||
|
||||
case OUT_PID:
|
||||
@ -1122,11 +1128,11 @@ static void do_atl_int(struct usb_hcd *usb_hcd)
|
||||
} else if (usb_pipebulk(urb->pipe) && (length < qtd->length)) {
|
||||
/* short BULK received */
|
||||
|
||||
printk(KERN_ERR "short bulk, %d instead %zu\n", length,
|
||||
qtd->length);
|
||||
if (urb->transfer_flags & URB_SHORT_NOT_OK) {
|
||||
urb->status = -EREMOTEIO;
|
||||
printk(KERN_ERR "not okey\n");
|
||||
isp1760_dbg(priv, "short bulk, %d instead %zu "
|
||||
"with URB_SHORT_NOT_OK flag.\n",
|
||||
length, qtd->length);
|
||||
}
|
||||
|
||||
if (urb->status == -EINPROGRESS)
|
||||
@ -1206,8 +1212,20 @@ static void do_intl_int(struct usb_hcd *usb_hcd)
|
||||
continue;
|
||||
}
|
||||
|
||||
priv_read_copy(priv, (u32 *)&ptd, usb_hcd->regs + int_regs,
|
||||
int_regs, sizeof(ptd));
|
||||
isp1760_writel(int_regs + ISP_BANK(0), usb_hcd->regs +
|
||||
HC_MEMORY_REG);
|
||||
isp1760_writel(payload + ISP_BANK(1), usb_hcd->regs +
|
||||
HC_MEMORY_REG);
|
||||
/*
|
||||
* write bank1 address twice to ensure the 90ns delay (time
|
||||
* between BANK0 write and the priv_read_copy() call is at
|
||||
* least 3*t_WHWL + 2*t_w11 = 3*25ns + 2*17ns = 92ns)
|
||||
*/
|
||||
isp1760_writel(payload + ISP_BANK(1), usb_hcd->regs +
|
||||
HC_MEMORY_REG);
|
||||
|
||||
priv_read_copy(priv, (u32 *)&ptd, usb_hcd->regs + int_regs +
|
||||
ISP_BANK(0), sizeof(ptd));
|
||||
dw1 = le32_to_cpu(ptd.dw1);
|
||||
dw3 = le32_to_cpu(ptd.dw3);
|
||||
check_int_err_status(le32_to_cpu(ptd.dw4));
|
||||
@ -1242,7 +1260,7 @@ static void do_intl_int(struct usb_hcd *usb_hcd)
|
||||
case IN_PID:
|
||||
priv_read_copy(priv,
|
||||
priv->int_ints[queue_entry].data_buffer,
|
||||
usb_hcd->regs + payload , payload,
|
||||
usb_hcd->regs + payload + ISP_BANK(1),
|
||||
length);
|
||||
case OUT_PID:
|
||||
|
||||
@ -1615,8 +1633,7 @@ static int isp1760_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
|
||||
return -EPIPE;
|
||||
}
|
||||
|
||||
isp1760_prepare_enqueue(priv, urb, &qtd_list, mem_flags, pe);
|
||||
return 0;
|
||||
return isp1760_prepare_enqueue(priv, urb, &qtd_list, mem_flags, pe);
|
||||
}
|
||||
|
||||
static int isp1760_urb_dequeue(struct usb_hcd *hcd, struct urb *urb,
|
||||
|
@ -54,6 +54,8 @@ void deinit_kmem_cache(void);
|
||||
#define BUFFER_MAP 0x7
|
||||
|
||||
#define HC_MEMORY_REG 0x33c
|
||||
#define ISP_BANK(x) ((x) << 16)
|
||||
|
||||
#define HC_PORT1_CTRL 0x374
|
||||
#define PORT1_POWER (3 << 3)
|
||||
#define PORT1_INIT1 (1 << 7)
|
||||
@ -119,6 +121,9 @@ struct inter_packet_info {
|
||||
typedef void (packet_enqueue)(struct usb_hcd *hcd, struct isp1760_qh *qh,
|
||||
struct isp1760_qtd *qtd);
|
||||
|
||||
#define isp1760_dbg(priv, fmt, args...) \
|
||||
dev_dbg(priv_to_hcd(priv)->self.controller, fmt, ##args)
|
||||
|
||||
#define isp1760_info(priv, fmt, args...) \
|
||||
dev_info(priv_to_hcd(priv)->self.controller, fmt, ##args)
|
||||
|
||||
|
@ -86,6 +86,21 @@ static void ohci_stop (struct usb_hcd *hcd);
|
||||
static int ohci_restart (struct ohci_hcd *ohci);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PCI
|
||||
static void quirk_amd_pll(int state);
|
||||
static void amd_iso_dev_put(void);
|
||||
#else
|
||||
static inline void quirk_amd_pll(int state)
|
||||
{
|
||||
return;
|
||||
}
|
||||
static inline void amd_iso_dev_put(void)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#include "ohci-hub.c"
|
||||
#include "ohci-dbg.c"
|
||||
#include "ohci-mem.c"
|
||||
@ -483,6 +498,9 @@ static int ohci_init (struct ohci_hcd *ohci)
|
||||
int ret;
|
||||
struct usb_hcd *hcd = ohci_to_hcd(ohci);
|
||||
|
||||
if (distrust_firmware)
|
||||
ohci->flags |= OHCI_QUIRK_HUB_POWER;
|
||||
|
||||
disable (ohci);
|
||||
ohci->regs = hcd->regs;
|
||||
|
||||
@ -689,7 +707,8 @@ retry:
|
||||
temp |= RH_A_NOCP;
|
||||
temp &= ~(RH_A_POTPGT | RH_A_NPS);
|
||||
ohci_writel (ohci, temp, &ohci->regs->roothub.a);
|
||||
} else if ((ohci->flags & OHCI_QUIRK_AMD756) || distrust_firmware) {
|
||||
} else if ((ohci->flags & OHCI_QUIRK_AMD756) ||
|
||||
(ohci->flags & OHCI_QUIRK_HUB_POWER)) {
|
||||
/* hub power always on; required for AMD-756 and some
|
||||
* Mac platforms. ganged overcurrent reporting, if any.
|
||||
*/
|
||||
@ -882,6 +901,8 @@ static void ohci_stop (struct usb_hcd *hcd)
|
||||
|
||||
if (quirk_zfmicro(ohci))
|
||||
del_timer(&ohci->unlink_watchdog);
|
||||
if (quirk_amdiso(ohci))
|
||||
amd_iso_dev_put();
|
||||
|
||||
remove_debug_files (ohci);
|
||||
ohci_mem_cleanup (ohci);
|
||||
|
@ -483,6 +483,13 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf)
|
||||
length++;
|
||||
}
|
||||
|
||||
/* Some broken controllers never turn off RHCS in the interrupt
|
||||
* status register. For their sake we won't re-enable RHSC
|
||||
* interrupts if the flag is already set.
|
||||
*/
|
||||
if (ohci_readl(ohci, &ohci->regs->intrstatus) & OHCI_INTR_RHSC)
|
||||
changed = 1;
|
||||
|
||||
/* look at each port */
|
||||
for (i = 0; i < ohci->num_ports; i++) {
|
||||
u32 status = roothub_portstatus (ohci, i);
|
||||
@ -572,8 +579,6 @@ static int ohci_start_port_reset (struct usb_hcd *hcd, unsigned port)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void start_hnp(struct ohci_hcd *ohci);
|
||||
|
||||
#else
|
||||
|
||||
#define ohci_start_port_reset NULL
|
||||
@ -760,7 +765,7 @@ static int ohci_hub_control (
|
||||
#ifdef CONFIG_USB_OTG
|
||||
if (hcd->self.otg_port == (wIndex + 1)
|
||||
&& hcd->self.b_hnp_enable)
|
||||
start_hnp(ohci);
|
||||
ohci->start_hnp(ohci);
|
||||
else
|
||||
#endif
|
||||
ohci_writel (ohci, RH_PS_PSS,
|
||||
|
@ -225,6 +225,7 @@ static int ohci_omap_init(struct usb_hcd *hcd)
|
||||
dev_err(hcd->self.controller, "can't find transceiver\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
ohci->start_hnp = start_hnp;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -260,7 +261,7 @@ static int ohci_omap_init(struct usb_hcd *hcd)
|
||||
omap_cfg_reg(W4_USB_HIGHZ);
|
||||
}
|
||||
ohci_writel(ohci, rh, &ohci->regs->roothub.a);
|
||||
distrust_firmware = 0;
|
||||
ohci->flags &= ~OHCI_QUIRK_HUB_POWER;
|
||||
} else if (machine_is_nokia770()) {
|
||||
/* We require a self-powered hub, which should have
|
||||
* plenty of power. */
|
||||
|
@ -18,6 +18,28 @@
|
||||
#error "This file is PCI bus glue. CONFIG_PCI must be defined."
|
||||
#endif
|
||||
|
||||
#include <linux/pci.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
|
||||
/* constants used to work around PM-related transfer
|
||||
* glitches in some AMD 700 series southbridges
|
||||
*/
|
||||
#define AB_REG_BAR 0xf0
|
||||
#define AB_INDX(addr) ((addr) + 0x00)
|
||||
#define AB_DATA(addr) ((addr) + 0x04)
|
||||
#define AX_INDXC 0X30
|
||||
#define AX_DATAC 0x34
|
||||
|
||||
#define NB_PCIE_INDX_ADDR 0xe0
|
||||
#define NB_PCIE_INDX_DATA 0xe4
|
||||
#define PCIE_P_CNTL 0x10040
|
||||
#define BIF_NB 0x10002
|
||||
|
||||
static struct pci_dev *amd_smbus_dev;
|
||||
static struct pci_dev *amd_hb_dev;
|
||||
static int amd_ohci_iso_count;
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static int broken_suspend(struct usb_hcd *hcd)
|
||||
@ -143,6 +165,103 @@ static int ohci_quirk_nec(struct usb_hcd *hcd)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ohci_quirk_amd700(struct usb_hcd *hcd)
|
||||
{
|
||||
struct ohci_hcd *ohci = hcd_to_ohci(hcd);
|
||||
u8 rev = 0;
|
||||
|
||||
if (!amd_smbus_dev)
|
||||
amd_smbus_dev = pci_get_device(PCI_VENDOR_ID_ATI,
|
||||
PCI_DEVICE_ID_ATI_SBX00_SMBUS, NULL);
|
||||
if (!amd_smbus_dev)
|
||||
return 0;
|
||||
|
||||
pci_read_config_byte(amd_smbus_dev, PCI_REVISION_ID, &rev);
|
||||
if ((rev > 0x3b) || (rev < 0x30)) {
|
||||
pci_dev_put(amd_smbus_dev);
|
||||
amd_smbus_dev = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
amd_ohci_iso_count++;
|
||||
|
||||
if (!amd_hb_dev)
|
||||
amd_hb_dev = pci_get_device(PCI_VENDOR_ID_AMD, 0x9600, NULL);
|
||||
|
||||
ohci->flags |= OHCI_QUIRK_AMD_ISO;
|
||||
ohci_dbg(ohci, "enabled AMD ISO transfers quirk\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The hardware normally enables the A-link power management feature, which
|
||||
* lets the system lower the power consumption in idle states.
|
||||
*
|
||||
* Assume the system is configured to have USB 1.1 ISO transfers going
|
||||
* to or from a USB device. Without this quirk, that stream may stutter
|
||||
* or have breaks occasionally. For transfers going to speakers, this
|
||||
* makes a very audible mess...
|
||||
*
|
||||
* That audio playback corruption is due to the audio stream getting
|
||||
* interrupted occasionally when the link goes in lower power state
|
||||
* This USB quirk prevents the link going into that lower power state
|
||||
* during audio playback or other ISO operations.
|
||||
*/
|
||||
static void quirk_amd_pll(int on)
|
||||
{
|
||||
u32 addr;
|
||||
u32 val;
|
||||
u32 bit = (on > 0) ? 1 : 0;
|
||||
|
||||
pci_read_config_dword(amd_smbus_dev, AB_REG_BAR, &addr);
|
||||
|
||||
/* BIT names/meanings are NDA-protected, sorry ... */
|
||||
|
||||
outl(AX_INDXC, AB_INDX(addr));
|
||||
outl(0x40, AB_DATA(addr));
|
||||
outl(AX_DATAC, AB_INDX(addr));
|
||||
val = inl(AB_DATA(addr));
|
||||
val &= ~((1 << 3) | (1 << 4) | (1 << 9));
|
||||
val |= (bit << 3) | ((!bit) << 4) | ((!bit) << 9);
|
||||
outl(val, AB_DATA(addr));
|
||||
|
||||
if (amd_hb_dev) {
|
||||
addr = PCIE_P_CNTL;
|
||||
pci_write_config_dword(amd_hb_dev, NB_PCIE_INDX_ADDR, addr);
|
||||
|
||||
pci_read_config_dword(amd_hb_dev, NB_PCIE_INDX_DATA, &val);
|
||||
val &= ~(1 | (1 << 3) | (1 << 4) | (1 << 9) | (1 << 12));
|
||||
val |= bit | (bit << 3) | (bit << 12);
|
||||
val |= ((!bit) << 4) | ((!bit) << 9);
|
||||
pci_write_config_dword(amd_hb_dev, NB_PCIE_INDX_DATA, val);
|
||||
|
||||
addr = BIF_NB;
|
||||
pci_write_config_dword(amd_hb_dev, NB_PCIE_INDX_ADDR, addr);
|
||||
|
||||
pci_read_config_dword(amd_hb_dev, NB_PCIE_INDX_DATA, &val);
|
||||
val &= ~(1 << 8);
|
||||
val |= bit << 8;
|
||||
pci_write_config_dword(amd_hb_dev, NB_PCIE_INDX_DATA, val);
|
||||
}
|
||||
}
|
||||
|
||||
static void amd_iso_dev_put(void)
|
||||
{
|
||||
amd_ohci_iso_count--;
|
||||
if (amd_ohci_iso_count == 0) {
|
||||
if (amd_smbus_dev) {
|
||||
pci_dev_put(amd_smbus_dev);
|
||||
amd_smbus_dev = NULL;
|
||||
}
|
||||
if (amd_hb_dev) {
|
||||
pci_dev_put(amd_hb_dev);
|
||||
amd_hb_dev = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* List of quirks for OHCI */
|
||||
static const struct pci_device_id ohci_pci_quirks[] = {
|
||||
{
|
||||
@ -181,6 +300,19 @@ static const struct pci_device_id ohci_pci_quirks[] = {
|
||||
PCI_DEVICE(PCI_VENDOR_ID_ITE, 0x8152),
|
||||
.driver_data = (unsigned long) broken_suspend,
|
||||
},
|
||||
{
|
||||
PCI_DEVICE(PCI_VENDOR_ID_ATI, 0x4397),
|
||||
.driver_data = (unsigned long)ohci_quirk_amd700,
|
||||
},
|
||||
{
|
||||
PCI_DEVICE(PCI_VENDOR_ID_ATI, 0x4398),
|
||||
.driver_data = (unsigned long)ohci_quirk_amd700,
|
||||
},
|
||||
{
|
||||
PCI_DEVICE(PCI_VENDOR_ID_ATI, 0x4399),
|
||||
.driver_data = (unsigned long)ohci_quirk_amd700,
|
||||
},
|
||||
|
||||
/* FIXME for some of the early AMD 760 southbridges, OHCI
|
||||
* won't work at all. blacklist them.
|
||||
*/
|
||||
|
@ -49,6 +49,9 @@ __acquires(ohci->lock)
|
||||
switch (usb_pipetype (urb->pipe)) {
|
||||
case PIPE_ISOCHRONOUS:
|
||||
ohci_to_hcd(ohci)->self.bandwidth_isoc_reqs--;
|
||||
if (ohci_to_hcd(ohci)->self.bandwidth_isoc_reqs == 0
|
||||
&& quirk_amdiso(ohci))
|
||||
quirk_amd_pll(1);
|
||||
break;
|
||||
case PIPE_INTERRUPT:
|
||||
ohci_to_hcd(ohci)->self.bandwidth_int_reqs--;
|
||||
@ -677,6 +680,9 @@ static void td_submit_urb (
|
||||
data + urb->iso_frame_desc [cnt].offset,
|
||||
urb->iso_frame_desc [cnt].length, urb, cnt);
|
||||
}
|
||||
if (ohci_to_hcd(ohci)->self.bandwidth_isoc_reqs == 0
|
||||
&& quirk_amdiso(ohci))
|
||||
quirk_amd_pll(0);
|
||||
periodic = ohci_to_hcd(ohci)->self.bandwidth_isoc_reqs++ == 0
|
||||
&& ohci_to_hcd(ohci)->self.bandwidth_int_reqs == 0;
|
||||
break;
|
||||
|
@ -371,6 +371,7 @@ struct ohci_hcd {
|
||||
* other external transceivers should be software-transparent
|
||||
*/
|
||||
struct otg_transceiver *transceiver;
|
||||
void (*start_hnp)(struct ohci_hcd *ohci);
|
||||
|
||||
/*
|
||||
* memory management for queue data structures
|
||||
@ -399,6 +400,8 @@ struct ohci_hcd {
|
||||
#define OHCI_QUIRK_ZFMICRO 0x20 /* Compaq ZFMicro chipset*/
|
||||
#define OHCI_QUIRK_NEC 0x40 /* lost interrupts */
|
||||
#define OHCI_QUIRK_FRAME_NO 0x80 /* no big endian frame_no shift */
|
||||
#define OHCI_QUIRK_HUB_POWER 0x100 /* distrust firmware power/oc setup */
|
||||
#define OHCI_QUIRK_AMD_ISO 0x200 /* ISO transfers*/
|
||||
// there are also chip quirks/bugs in init logic
|
||||
|
||||
struct work_struct nec_work; /* Worker for NEC quirk */
|
||||
@ -426,6 +429,10 @@ static inline int quirk_zfmicro(struct ohci_hcd *ohci)
|
||||
{
|
||||
return ohci->flags & OHCI_QUIRK_ZFMICRO;
|
||||
}
|
||||
static inline int quirk_amdiso(struct ohci_hcd *ohci)
|
||||
{
|
||||
return ohci->flags & OHCI_QUIRK_AMD_ISO;
|
||||
}
|
||||
#else
|
||||
static inline int quirk_nec(struct ohci_hcd *ohci)
|
||||
{
|
||||
@ -435,6 +442,10 @@ static inline int quirk_zfmicro(struct ohci_hcd *ohci)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int quirk_amdiso(struct ohci_hcd *ohci)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* convert between an hcd pointer and the corresponding ohci_hcd */
|
||||
|
@ -964,11 +964,34 @@ static void pipe_irq_disable(struct r8a66597 *r8a66597, u16 pipenum)
|
||||
disable_irq_nrdy(r8a66597, pipenum);
|
||||
}
|
||||
|
||||
static void r8a66597_root_hub_start_polling(struct r8a66597 *r8a66597)
|
||||
{
|
||||
mod_timer(&r8a66597->rh_timer,
|
||||
jiffies + msecs_to_jiffies(R8A66597_RH_POLL_TIME));
|
||||
}
|
||||
|
||||
static void start_root_hub_sampling(struct r8a66597 *r8a66597, int port,
|
||||
int connect)
|
||||
{
|
||||
struct r8a66597_root_hub *rh = &r8a66597->root_hub[port];
|
||||
|
||||
rh->old_syssts = r8a66597_read(r8a66597, get_syssts_reg(port)) & LNST;
|
||||
rh->scount = R8A66597_MAX_SAMPLING;
|
||||
if (connect)
|
||||
rh->port |= 1 << USB_PORT_FEAT_CONNECTION;
|
||||
else
|
||||
rh->port &= ~(1 << USB_PORT_FEAT_CONNECTION);
|
||||
rh->port |= 1 << USB_PORT_FEAT_C_CONNECTION;
|
||||
|
||||
r8a66597_root_hub_start_polling(r8a66597);
|
||||
}
|
||||
|
||||
/* this function must be called with interrupt disabled */
|
||||
static void r8a66597_check_syssts(struct r8a66597 *r8a66597, int port,
|
||||
u16 syssts)
|
||||
{
|
||||
if (syssts == SE0) {
|
||||
r8a66597_write(r8a66597, ~ATTCH, get_intsts_reg(port));
|
||||
r8a66597_bset(r8a66597, ATTCHE, get_intenb_reg(port));
|
||||
return;
|
||||
}
|
||||
@ -1002,13 +1025,10 @@ static void r8a66597_usb_disconnect(struct r8a66597 *r8a66597, int port)
|
||||
{
|
||||
struct r8a66597_device *dev = r8a66597->root_hub[port].dev;
|
||||
|
||||
r8a66597->root_hub[port].port &= ~(1 << USB_PORT_FEAT_CONNECTION);
|
||||
r8a66597->root_hub[port].port |= (1 << USB_PORT_FEAT_C_CONNECTION);
|
||||
|
||||
disable_r8a66597_pipe_all(r8a66597, dev);
|
||||
free_usb_address(r8a66597, dev);
|
||||
|
||||
r8a66597_bset(r8a66597, ATTCHE, get_intenb_reg(port));
|
||||
start_root_hub_sampling(r8a66597, port, 0);
|
||||
}
|
||||
|
||||
/* this function must be called with interrupt disabled */
|
||||
@ -1551,23 +1571,6 @@ static void irq_pipe_nrdy(struct r8a66597 *r8a66597)
|
||||
}
|
||||
}
|
||||
|
||||
static void r8a66597_root_hub_start_polling(struct r8a66597 *r8a66597)
|
||||
{
|
||||
mod_timer(&r8a66597->rh_timer,
|
||||
jiffies + msecs_to_jiffies(R8A66597_RH_POLL_TIME));
|
||||
}
|
||||
|
||||
static void start_root_hub_sampling(struct r8a66597 *r8a66597, int port)
|
||||
{
|
||||
struct r8a66597_root_hub *rh = &r8a66597->root_hub[port];
|
||||
|
||||
rh->old_syssts = r8a66597_read(r8a66597, get_syssts_reg(port)) & LNST;
|
||||
rh->scount = R8A66597_MAX_SAMPLING;
|
||||
r8a66597->root_hub[port].port |= (1 << USB_PORT_FEAT_CONNECTION)
|
||||
| (1 << USB_PORT_FEAT_C_CONNECTION);
|
||||
r8a66597_root_hub_start_polling(r8a66597);
|
||||
}
|
||||
|
||||
static irqreturn_t r8a66597_irq(struct usb_hcd *hcd)
|
||||
{
|
||||
struct r8a66597 *r8a66597 = hcd_to_r8a66597(hcd);
|
||||
@ -1594,7 +1597,7 @@ static irqreturn_t r8a66597_irq(struct usb_hcd *hcd)
|
||||
r8a66597_bclr(r8a66597, ATTCHE, INTENB2);
|
||||
|
||||
/* start usb bus sampling */
|
||||
start_root_hub_sampling(r8a66597, 1);
|
||||
start_root_hub_sampling(r8a66597, 1, 1);
|
||||
}
|
||||
if (mask2 & DTCH) {
|
||||
r8a66597_write(r8a66597, ~DTCH, INTSTS2);
|
||||
@ -1609,7 +1612,7 @@ static irqreturn_t r8a66597_irq(struct usb_hcd *hcd)
|
||||
r8a66597_bclr(r8a66597, ATTCHE, INTENB1);
|
||||
|
||||
/* start usb bus sampling */
|
||||
start_root_hub_sampling(r8a66597, 0);
|
||||
start_root_hub_sampling(r8a66597, 0, 1);
|
||||
}
|
||||
if (mask1 & DTCH) {
|
||||
r8a66597_write(r8a66597, ~DTCH, INTSTS1);
|
||||
|
@ -42,16 +42,6 @@ config USB_ADUTUX
|
||||
To compile this driver as a module, choose M here. The module
|
||||
will be called adutux.
|
||||
|
||||
config USB_AUERSWALD
|
||||
tristate "USB Auerswald ISDN support"
|
||||
depends on USB
|
||||
help
|
||||
Say Y here if you want to connect an Auerswald USB ISDN Device
|
||||
to your computer's USB port.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called auerswald.
|
||||
|
||||
config USB_RIO500
|
||||
tristate "USB Diamond Rio500 support"
|
||||
depends on USB
|
||||
|
@ -5,7 +5,6 @@
|
||||
|
||||
obj-$(CONFIG_USB_ADUTUX) += adutux.o
|
||||
obj-$(CONFIG_USB_APPLEDISPLAY) += appledisplay.o
|
||||
obj-$(CONFIG_USB_AUERSWALD) += auerswald.o
|
||||
obj-$(CONFIG_USB_BERRY_CHARGE) += berry_charge.o
|
||||
obj-$(CONFIG_USB_CYPRESS_CY7C63)+= cypress_cy7c63.o
|
||||
obj-$(CONFIG_USB_CYTHERM) += cytherm.o
|
||||
|
File diff suppressed because it is too large
Load Diff
176
drivers/usb/musb/Kconfig
Normal file
176
drivers/usb/musb/Kconfig
Normal file
@ -0,0 +1,176 @@
|
||||
#
|
||||
# USB Dual Role (OTG-ready) Controller Drivers
|
||||
# for silicon based on Mentor Graphics INVENTRA designs
|
||||
#
|
||||
|
||||
comment "Enable Host or Gadget support to see Inventra options"
|
||||
depends on !USB && USB_GADGET=n
|
||||
|
||||
# (M)HDRC = (Multipoint) Highspeed Dual-Role Controller
|
||||
config USB_MUSB_HDRC
|
||||
depends on (USB || USB_GADGET) && HAVE_CLK
|
||||
select TWL4030_USB if MACH_OMAP_3430SDP
|
||||
tristate 'Inventra Highspeed Dual Role Controller (TI, ...)'
|
||||
help
|
||||
Say Y here if your system has a dual role high speed USB
|
||||
controller based on the Mentor Graphics silicon IP. Then
|
||||
configure options to match your silicon and the board
|
||||
it's being used with, including the USB peripheral role,
|
||||
or the USB host role, or both.
|
||||
|
||||
Texas Instruments parts using this IP include DaVinci 644x,
|
||||
OMAP 243x, OMAP 343x, and TUSB 6010.
|
||||
|
||||
If you do not know what this is, please say N.
|
||||
|
||||
To compile this driver as a module, choose M here; the
|
||||
module will be called "musb_hdrc".
|
||||
|
||||
config USB_MUSB_SOC
|
||||
boolean
|
||||
depends on USB_MUSB_HDRC
|
||||
default y if ARCH_DAVINCI
|
||||
default y if ARCH_OMAP2430
|
||||
default y if ARCH_OMAP34XX
|
||||
help
|
||||
Use a static <asm/arch/hdrc_cnf.h> file to describe how the
|
||||
controller is configured (endpoints, mechanisms, etc) on the
|
||||
current iteration of a given system-on-chip.
|
||||
|
||||
comment "DaVinci 644x USB support"
|
||||
depends on USB_MUSB_HDRC && ARCH_DAVINCI
|
||||
|
||||
comment "OMAP 243x high speed USB support"
|
||||
depends on USB_MUSB_HDRC && ARCH_OMAP2430
|
||||
|
||||
comment "OMAP 343x high speed USB support"
|
||||
depends on USB_MUSB_HDRC && ARCH_OMAP34XX
|
||||
|
||||
config USB_TUSB6010
|
||||
boolean "TUSB 6010 support"
|
||||
depends on USB_MUSB_HDRC && !USB_MUSB_SOC
|
||||
default y
|
||||
help
|
||||
The TUSB 6010 chip, from Texas Instruments, connects a discrete
|
||||
HDRC core using a 16-bit parallel bus (NOR flash style) or VLYNQ
|
||||
(a high speed serial link). It can use system-specific external
|
||||
DMA controllers.
|
||||
|
||||
choice
|
||||
prompt "Driver Mode"
|
||||
depends on USB_MUSB_HDRC
|
||||
help
|
||||
Dual-Role devices can support both host and peripheral roles,
|
||||
as well as a the special "OTG Device" role which can switch
|
||||
between both roles as needed.
|
||||
|
||||
# use USB_MUSB_HDRC_HCD not USB_MUSB_HOST to #ifdef host side support;
|
||||
# OTG needs both roles, not just USB_MUSB_HOST.
|
||||
config USB_MUSB_HOST
|
||||
depends on USB
|
||||
bool "USB Host"
|
||||
help
|
||||
Say Y here if your system supports the USB host role.
|
||||
If it has a USB "A" (rectangular), "Mini-A" (uncommon),
|
||||
or "Mini-AB" connector, it supports the host role.
|
||||
(With a "Mini-AB" connector, you should enable USB OTG.)
|
||||
|
||||
# use USB_GADGET_MUSB_HDRC not USB_MUSB_PERIPHERAL to #ifdef peripheral
|
||||
# side support ... OTG needs both roles
|
||||
config USB_MUSB_PERIPHERAL
|
||||
depends on USB_GADGET
|
||||
bool "USB Peripheral (gadget stack)"
|
||||
select USB_GADGET_MUSB_HDRC
|
||||
help
|
||||
Say Y here if your system supports the USB peripheral role.
|
||||
If it has a USB "B" (squarish), "Mini-B", or "Mini-AB"
|
||||
connector, it supports the peripheral role.
|
||||
(With a "Mini-AB" connector, you should enable USB OTG.)
|
||||
|
||||
config USB_MUSB_OTG
|
||||
depends on USB && USB_GADGET && PM && EXPERIMENTAL
|
||||
bool "Both host and peripheral: USB OTG (On The Go) Device"
|
||||
select USB_GADGET_MUSB_HDRC
|
||||
select USB_OTG
|
||||
help
|
||||
The most notable feature of USB OTG is support for a
|
||||
"Dual-Role" device, which can act as either a device
|
||||
or a host. The initial role choice can be changed
|
||||
later, when two dual-role devices talk to each other.
|
||||
|
||||
At this writing, the OTG support in this driver is incomplete,
|
||||
omitting the mandatory HNP or SRP protocols. However, some
|
||||
of the cable based role switching works. (That is, grounding
|
||||
the ID pin switches the controller to host mode, while leaving
|
||||
it floating leaves it in peripheral mode.)
|
||||
|
||||
Select this if your system has a Mini-AB connector, or
|
||||
to simplify certain kinds of configuration.
|
||||
|
||||
To implement your OTG Targeted Peripherals List (TPL), enable
|
||||
USB_OTG_WHITELIST and update "drivers/usb/core/otg_whitelist.h"
|
||||
to match your requirements.
|
||||
|
||||
endchoice
|
||||
|
||||
# enable peripheral support (including with OTG)
|
||||
config USB_GADGET_MUSB_HDRC
|
||||
bool
|
||||
depends on USB_MUSB_HDRC && (USB_MUSB_PERIPHERAL || USB_MUSB_OTG)
|
||||
# default y
|
||||
# select USB_GADGET_DUALSPEED
|
||||
# select USB_GADGET_SELECTED
|
||||
|
||||
# enables host support (including with OTG)
|
||||
config USB_MUSB_HDRC_HCD
|
||||
bool
|
||||
depends on USB_MUSB_HDRC && (USB_MUSB_HOST || USB_MUSB_OTG)
|
||||
select USB_OTG if USB_GADGET_MUSB_HDRC
|
||||
default y
|
||||
|
||||
|
||||
config MUSB_PIO_ONLY
|
||||
bool 'Disable DMA (always use PIO)'
|
||||
depends on USB_MUSB_HDRC
|
||||
default y if USB_TUSB6010
|
||||
help
|
||||
All data is copied between memory and FIFO by the CPU.
|
||||
DMA controllers are ignored.
|
||||
|
||||
Do not select 'n' here unless DMA support for your SOC or board
|
||||
is unavailable (or unstable). When DMA is enabled at compile time,
|
||||
you can still disable it at run time using the "use_dma=n" module
|
||||
parameter.
|
||||
|
||||
config USB_INVENTRA_DMA
|
||||
bool
|
||||
depends on USB_MUSB_HDRC && !MUSB_PIO_ONLY
|
||||
default ARCH_OMAP2430 || ARCH_OMAP34XX
|
||||
help
|
||||
Enable DMA transfers using Mentor's engine.
|
||||
|
||||
config USB_TI_CPPI_DMA
|
||||
bool
|
||||
depends on USB_MUSB_HDRC && !MUSB_PIO_ONLY
|
||||
default ARCH_DAVINCI
|
||||
help
|
||||
Enable DMA transfers when TI CPPI DMA is available.
|
||||
|
||||
config USB_TUSB_OMAP_DMA
|
||||
bool
|
||||
depends on USB_MUSB_HDRC && !MUSB_PIO_ONLY
|
||||
depends on USB_TUSB6010
|
||||
depends on ARCH_OMAP
|
||||
default y
|
||||
help
|
||||
Enable DMA transfers on TUSB 6010 when OMAP DMA is available.
|
||||
|
||||
config USB_MUSB_LOGLEVEL
|
||||
depends on USB_MUSB_HDRC
|
||||
int 'Logging Level (0 - none / 3 - annoying / ... )'
|
||||
default 0
|
||||
help
|
||||
Set the logging level. 0 disables the debugging altogether,
|
||||
although when USB_DEBUG is set the value is at least 1.
|
||||
Starting at level 3, per-transfer (urb, usb_request, packet,
|
||||
or dma transfer) tracing may kick in.
|
86
drivers/usb/musb/Makefile
Normal file
86
drivers/usb/musb/Makefile
Normal file
@ -0,0 +1,86 @@
|
||||
#
|
||||
# for USB OTG silicon based on Mentor Graphics INVENTRA designs
|
||||
#
|
||||
|
||||
musb_hdrc-objs := musb_core.o
|
||||
|
||||
obj-$(CONFIG_USB_MUSB_HDRC) += musb_hdrc.o
|
||||
|
||||
ifeq ($(CONFIG_ARCH_DAVINCI),y)
|
||||
musb_hdrc-objs += davinci.o
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_USB_TUSB6010),y)
|
||||
musb_hdrc-objs += tusb6010.o
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_ARCH_OMAP2430),y)
|
||||
musb_hdrc-objs += omap2430.o
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_ARCH_OMAP3430),y)
|
||||
musb_hdrc-objs += omap2430.o
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_USB_GADGET_MUSB_HDRC),y)
|
||||
musb_hdrc-objs += musb_gadget_ep0.o musb_gadget.o
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_USB_MUSB_HDRC_HCD),y)
|
||||
musb_hdrc-objs += musb_virthub.o musb_host.o
|
||||
endif
|
||||
|
||||
# the kconfig must guarantee that only one of the
|
||||
# possible I/O schemes will be enabled at a time ...
|
||||
# PIO only, or DMA (several potential schemes).
|
||||
# though PIO is always there to back up DMA, and for ep0
|
||||
|
||||
ifneq ($(CONFIG_MUSB_PIO_ONLY),y)
|
||||
|
||||
ifeq ($(CONFIG_USB_INVENTRA_DMA),y)
|
||||
musb_hdrc-objs += musbhsdma.o
|
||||
|
||||
else
|
||||
ifeq ($(CONFIG_USB_TI_CPPI_DMA),y)
|
||||
musb_hdrc-objs += cppi_dma.o
|
||||
|
||||
else
|
||||
ifeq ($(CONFIG_USB_TUSB_OMAP_DMA),y)
|
||||
musb_hdrc-objs += tusb6010_omap.o
|
||||
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
|
||||
################################################################################
|
||||
|
||||
# FIXME remove all these extra "-DMUSB_* things, stick to CONFIG_*
|
||||
|
||||
ifeq ($(CONFIG_USB_INVENTRA_MUSB_HAS_AHB_ID),y)
|
||||
EXTRA_CFLAGS += -DMUSB_AHB_ID
|
||||
endif
|
||||
|
||||
# Debugging
|
||||
|
||||
MUSB_DEBUG:=$(CONFIG_USB_MUSB_LOGLEVEL)
|
||||
|
||||
ifeq ("$(strip $(MUSB_DEBUG))","")
|
||||
ifdef CONFIG_USB_DEBUG
|
||||
MUSB_DEBUG:=1
|
||||
else
|
||||
MUSB_DEBUG:=0
|
||||
endif
|
||||
endif
|
||||
|
||||
ifneq ($(MUSB_DEBUG),0)
|
||||
EXTRA_CFLAGS += -DDEBUG
|
||||
|
||||
ifeq ($(CONFIG_PROC_FS),y)
|
||||
musb_hdrc-objs += musb_procfs.o
|
||||
endif
|
||||
|
||||
endif
|
||||
|
||||
EXTRA_CFLAGS += -DMUSB_DEBUG=$(MUSB_DEBUG)
|
1540
drivers/usb/musb/cppi_dma.c
Normal file
1540
drivers/usb/musb/cppi_dma.c
Normal file
File diff suppressed because it is too large
Load Diff
133
drivers/usb/musb/cppi_dma.h
Normal file
133
drivers/usb/musb/cppi_dma.h
Normal file
@ -0,0 +1,133 @@
|
||||
/* Copyright (C) 2005-2006 by Texas Instruments */
|
||||
|
||||
#ifndef _CPPI_DMA_H_
|
||||
#define _CPPI_DMA_H_
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/dmapool.h>
|
||||
|
||||
#include "musb_dma.h"
|
||||
#include "musb_core.h"
|
||||
|
||||
|
||||
/* FIXME fully isolate CPPI from DaVinci ... the "CPPI generic" registers
|
||||
* would seem to be shared with the TUSB6020 (over VLYNQ).
|
||||
*/
|
||||
|
||||
#include "davinci.h"
|
||||
|
||||
|
||||
/* CPPI RX/TX state RAM */
|
||||
|
||||
struct cppi_tx_stateram {
|
||||
u32 tx_head; /* "DMA packet" head descriptor */
|
||||
u32 tx_buf;
|
||||
u32 tx_current; /* current descriptor */
|
||||
u32 tx_buf_current;
|
||||
u32 tx_info; /* flags, remaining buflen */
|
||||
u32 tx_rem_len;
|
||||
u32 tx_dummy; /* unused */
|
||||
u32 tx_complete;
|
||||
};
|
||||
|
||||
struct cppi_rx_stateram {
|
||||
u32 rx_skipbytes;
|
||||
u32 rx_head;
|
||||
u32 rx_sop; /* "DMA packet" head descriptor */
|
||||
u32 rx_current; /* current descriptor */
|
||||
u32 rx_buf_current;
|
||||
u32 rx_len_len;
|
||||
u32 rx_cnt_cnt;
|
||||
u32 rx_complete;
|
||||
};
|
||||
|
||||
/* hw_options bits in CPPI buffer descriptors */
|
||||
#define CPPI_SOP_SET ((u32)(1 << 31))
|
||||
#define CPPI_EOP_SET ((u32)(1 << 30))
|
||||
#define CPPI_OWN_SET ((u32)(1 << 29)) /* owned by cppi */
|
||||
#define CPPI_EOQ_MASK ((u32)(1 << 28))
|
||||
#define CPPI_ZERO_SET ((u32)(1 << 23)) /* rx saw zlp; tx issues one */
|
||||
#define CPPI_RXABT_MASK ((u32)(1 << 19)) /* need more rx buffers */
|
||||
|
||||
#define CPPI_RECV_PKTLEN_MASK 0xFFFF
|
||||
#define CPPI_BUFFER_LEN_MASK 0xFFFF
|
||||
|
||||
#define CPPI_TEAR_READY ((u32)(1 << 31))
|
||||
|
||||
/* CPPI data structure definitions */
|
||||
|
||||
#define CPPI_DESCRIPTOR_ALIGN 16 /* bytes; 5-dec docs say 4-byte align */
|
||||
|
||||
struct cppi_descriptor {
|
||||
/* hardware overlay */
|
||||
u32 hw_next; /* next buffer descriptor Pointer */
|
||||
u32 hw_bufp; /* i/o buffer pointer */
|
||||
u32 hw_off_len; /* buffer_offset16, buffer_length16 */
|
||||
u32 hw_options; /* flags: SOP, EOP etc*/
|
||||
|
||||
struct cppi_descriptor *next;
|
||||
dma_addr_t dma; /* address of this descriptor */
|
||||
u32 buflen; /* for RX: original buffer length */
|
||||
} __attribute__ ((aligned(CPPI_DESCRIPTOR_ALIGN)));
|
||||
|
||||
|
||||
struct cppi;
|
||||
|
||||
/* CPPI Channel Control structure */
|
||||
struct cppi_channel {
|
||||
struct dma_channel channel;
|
||||
|
||||
/* back pointer to the DMA controller structure */
|
||||
struct cppi *controller;
|
||||
|
||||
/* which direction of which endpoint? */
|
||||
struct musb_hw_ep *hw_ep;
|
||||
bool transmit;
|
||||
u8 index;
|
||||
|
||||
/* DMA modes: RNDIS or "transparent" */
|
||||
u8 is_rndis;
|
||||
|
||||
/* book keeping for current transfer request */
|
||||
dma_addr_t buf_dma;
|
||||
u32 buf_len;
|
||||
u32 maxpacket;
|
||||
u32 offset; /* dma requested */
|
||||
|
||||
void __iomem *state_ram; /* CPPI state */
|
||||
|
||||
struct cppi_descriptor *freelist;
|
||||
|
||||
/* BD management fields */
|
||||
struct cppi_descriptor *head;
|
||||
struct cppi_descriptor *tail;
|
||||
struct cppi_descriptor *last_processed;
|
||||
|
||||
/* use tx_complete in host role to track endpoints waiting for
|
||||
* FIFONOTEMPTY to clear.
|
||||
*/
|
||||
struct list_head tx_complete;
|
||||
};
|
||||
|
||||
/* CPPI DMA controller object */
|
||||
struct cppi {
|
||||
struct dma_controller controller;
|
||||
struct musb *musb;
|
||||
void __iomem *mregs; /* Mentor regs */
|
||||
void __iomem *tibase; /* TI/CPPI regs */
|
||||
|
||||
struct cppi_channel tx[MUSB_C_NUM_EPT - 1];
|
||||
struct cppi_channel rx[MUSB_C_NUM_EPR - 1];
|
||||
|
||||
struct dma_pool *pool;
|
||||
|
||||
struct list_head tx_complete;
|
||||
};
|
||||
|
||||
/* irq handling hook */
|
||||
extern void cppi_completion(struct musb *, u32 rx, u32 tx);
|
||||
|
||||
#endif /* end of ifndef _CPPI_DMA_H_ */
|
462
drivers/usb/musb/davinci.c
Normal file
462
drivers/usb/musb/davinci.c
Normal file
@ -0,0 +1,462 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2006 by Texas Instruments
|
||||
*
|
||||
* This file is part of the Inventra Controller Driver for Linux.
|
||||
*
|
||||
* The Inventra Controller Driver for Linux is free software; you
|
||||
* can redistribute it and/or modify it under the terms of the GNU
|
||||
* General Public License version 2 as published by the Free Software
|
||||
* Foundation.
|
||||
*
|
||||
* The Inventra Controller Driver for Linux is distributed in
|
||||
* the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with The Inventra Controller Driver for Linux ; if not,
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place,
|
||||
* Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include <asm/arch/hardware.h>
|
||||
#include <asm/arch/memory.h>
|
||||
#include <asm/arch/gpio.h>
|
||||
#include <asm/mach-types.h>
|
||||
|
||||
#include "musb_core.h"
|
||||
|
||||
#ifdef CONFIG_MACH_DAVINCI_EVM
|
||||
#include <asm/arch/i2c-client.h>
|
||||
#endif
|
||||
|
||||
#include "davinci.h"
|
||||
#include "cppi_dma.h"
|
||||
|
||||
|
||||
/* REVISIT (PM) we should be able to keep the PHY in low power mode most
|
||||
* of the time (24 MHZ oscillator and PLL off, etc) by setting POWER.D0
|
||||
* and, when in host mode, autosuspending idle root ports... PHYPLLON
|
||||
* (overriding SUSPENDM?) then likely needs to stay off.
|
||||
*/
|
||||
|
||||
static inline void phy_on(void)
|
||||
{
|
||||
/* start the on-chip PHY and its PLL */
|
||||
__raw_writel(USBPHY_SESNDEN | USBPHY_VBDTCTEN | USBPHY_PHYPLLON,
|
||||
(void __force __iomem *) IO_ADDRESS(USBPHY_CTL_PADDR));
|
||||
while ((__raw_readl((void __force __iomem *)
|
||||
IO_ADDRESS(USBPHY_CTL_PADDR))
|
||||
& USBPHY_PHYCLKGD) == 0)
|
||||
cpu_relax();
|
||||
}
|
||||
|
||||
static inline void phy_off(void)
|
||||
{
|
||||
/* powerdown the on-chip PHY and its oscillator */
|
||||
__raw_writel(USBPHY_OSCPDWN | USBPHY_PHYPDWN, (void __force __iomem *)
|
||||
IO_ADDRESS(USBPHY_CTL_PADDR));
|
||||
}
|
||||
|
||||
static int dma_off = 1;
|
||||
|
||||
void musb_platform_enable(struct musb *musb)
|
||||
{
|
||||
u32 tmp, old, val;
|
||||
|
||||
/* workaround: setup irqs through both register sets */
|
||||
tmp = (musb->epmask & DAVINCI_USB_TX_ENDPTS_MASK)
|
||||
<< DAVINCI_USB_TXINT_SHIFT;
|
||||
musb_writel(musb->ctrl_base, DAVINCI_USB_INT_MASK_SET_REG, tmp);
|
||||
old = tmp;
|
||||
tmp = (musb->epmask & (0xfffe & DAVINCI_USB_RX_ENDPTS_MASK))
|
||||
<< DAVINCI_USB_RXINT_SHIFT;
|
||||
musb_writel(musb->ctrl_base, DAVINCI_USB_INT_MASK_SET_REG, tmp);
|
||||
tmp |= old;
|
||||
|
||||
val = ~MUSB_INTR_SOF;
|
||||
tmp |= ((val & 0x01ff) << DAVINCI_USB_USBINT_SHIFT);
|
||||
musb_writel(musb->ctrl_base, DAVINCI_USB_INT_MASK_SET_REG, tmp);
|
||||
|
||||
if (is_dma_capable() && !dma_off)
|
||||
printk(KERN_WARNING "%s %s: dma not reactivated\n",
|
||||
__FILE__, __func__);
|
||||
else
|
||||
dma_off = 0;
|
||||
|
||||
/* force a DRVVBUS irq so we can start polling for ID change */
|
||||
if (is_otg_enabled(musb))
|
||||
musb_writel(musb->ctrl_base, DAVINCI_USB_INT_SET_REG,
|
||||
DAVINCI_INTR_DRVVBUS << DAVINCI_USB_USBINT_SHIFT);
|
||||
}
|
||||
|
||||
/*
|
||||
* Disable the HDRC and flush interrupts
|
||||
*/
|
||||
void musb_platform_disable(struct musb *musb)
|
||||
{
|
||||
/* because we don't set CTRLR.UINT, "important" to:
|
||||
* - not read/write INTRUSB/INTRUSBE
|
||||
* - (except during initial setup, as workaround)
|
||||
* - use INTSETR/INTCLRR instead
|
||||
*/
|
||||
musb_writel(musb->ctrl_base, DAVINCI_USB_INT_MASK_CLR_REG,
|
||||
DAVINCI_USB_USBINT_MASK
|
||||
| DAVINCI_USB_TXINT_MASK
|
||||
| DAVINCI_USB_RXINT_MASK);
|
||||
musb_writeb(musb->mregs, MUSB_DEVCTL, 0);
|
||||
musb_writel(musb->ctrl_base, DAVINCI_USB_EOI_REG, 0);
|
||||
|
||||
if (is_dma_capable() && !dma_off)
|
||||
WARNING("dma still active\n");
|
||||
}
|
||||
|
||||
|
||||
/* REVISIT it's not clear whether DaVinci can support full OTG. */
|
||||
|
||||
static int vbus_state = -1;
|
||||
|
||||
#ifdef CONFIG_USB_MUSB_HDRC_HCD
|
||||
#define portstate(stmt) stmt
|
||||
#else
|
||||
#define portstate(stmt)
|
||||
#endif
|
||||
|
||||
|
||||
/* VBUS SWITCHING IS BOARD-SPECIFIC */
|
||||
|
||||
#ifdef CONFIG_MACH_DAVINCI_EVM
|
||||
#ifndef CONFIG_MACH_DAVINCI_EVM_OTG
|
||||
|
||||
/* I2C operations are always synchronous, and require a task context.
|
||||
* With unloaded systems, using the shared workqueue seems to suffice
|
||||
* to satisfy the 100msec A_WAIT_VRISE timeout...
|
||||
*/
|
||||
static void evm_deferred_drvvbus(struct work_struct *ignored)
|
||||
{
|
||||
davinci_i2c_expander_op(0x3a, USB_DRVVBUS, vbus_state);
|
||||
vbus_state = !vbus_state;
|
||||
}
|
||||
static DECLARE_WORK(evm_vbus_work, evm_deferred_drvvbus);
|
||||
|
||||
#endif /* modified board */
|
||||
#endif /* EVM */
|
||||
|
||||
static void davinci_source_power(struct musb *musb, int is_on, int immediate)
|
||||
{
|
||||
if (is_on)
|
||||
is_on = 1;
|
||||
|
||||
if (vbus_state == is_on)
|
||||
return;
|
||||
vbus_state = !is_on; /* 0/1 vs "-1 == unknown/init" */
|
||||
|
||||
#ifdef CONFIG_MACH_DAVINCI_EVM
|
||||
if (machine_is_davinci_evm()) {
|
||||
#ifdef CONFIG_MACH_DAVINCI_EVM_OTG
|
||||
/* modified EVM board switching VBUS with GPIO(6) not I2C
|
||||
* NOTE: PINMUX0.RGB888 (bit23) must be clear
|
||||
*/
|
||||
if (is_on)
|
||||
gpio_set(GPIO(6));
|
||||
else
|
||||
gpio_clear(GPIO(6));
|
||||
immediate = 1;
|
||||
#else
|
||||
if (immediate)
|
||||
davinci_i2c_expander_op(0x3a, USB_DRVVBUS, !is_on);
|
||||
else
|
||||
schedule_work(&evm_vbus_work);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
if (immediate)
|
||||
vbus_state = is_on;
|
||||
}
|
||||
|
||||
static void davinci_set_vbus(struct musb *musb, int is_on)
|
||||
{
|
||||
WARN_ON(is_on && is_peripheral_active(musb));
|
||||
davinci_source_power(musb, is_on, 0);
|
||||
}
|
||||
|
||||
|
||||
#define POLL_SECONDS 2
|
||||
|
||||
static struct timer_list otg_workaround;
|
||||
|
||||
static void otg_timer(unsigned long _musb)
|
||||
{
|
||||
struct musb *musb = (void *)_musb;
|
||||
void __iomem *mregs = musb->mregs;
|
||||
u8 devctl;
|
||||
unsigned long flags;
|
||||
|
||||
/* We poll because DaVinci's won't expose several OTG-critical
|
||||
* status change events (from the transceiver) otherwise.
|
||||
*/
|
||||
devctl = musb_readb(mregs, MUSB_DEVCTL);
|
||||
DBG(7, "poll devctl %02x (%s)\n", devctl, otg_state_string(musb));
|
||||
|
||||
spin_lock_irqsave(&musb->lock, flags);
|
||||
switch (musb->xceiv.state) {
|
||||
case OTG_STATE_A_WAIT_VFALL:
|
||||
/* Wait till VBUS falls below SessionEnd (~0.2V); the 1.3 RTL
|
||||
* seems to mis-handle session "start" otherwise (or in our
|
||||
* case "recover"), in routine "VBUS was valid by the time
|
||||
* VBUSERR got reported during enumeration" cases.
|
||||
*/
|
||||
if (devctl & MUSB_DEVCTL_VBUS) {
|
||||
mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ);
|
||||
break;
|
||||
}
|
||||
musb->xceiv.state = OTG_STATE_A_WAIT_VRISE;
|
||||
musb_writel(musb->ctrl_base, DAVINCI_USB_INT_SET_REG,
|
||||
MUSB_INTR_VBUSERROR << DAVINCI_USB_USBINT_SHIFT);
|
||||
break;
|
||||
case OTG_STATE_B_IDLE:
|
||||
if (!is_peripheral_enabled(musb))
|
||||
break;
|
||||
|
||||
/* There's no ID-changed IRQ, so we have no good way to tell
|
||||
* when to switch to the A-Default state machine (by setting
|
||||
* the DEVCTL.SESSION flag).
|
||||
*
|
||||
* Workaround: whenever we're in B_IDLE, try setting the
|
||||
* session flag every few seconds. If it works, ID was
|
||||
* grounded and we're now in the A-Default state machine.
|
||||
*
|
||||
* NOTE setting the session flag is _supposed_ to trigger
|
||||
* SRP, but clearly it doesn't.
|
||||
*/
|
||||
musb_writeb(mregs, MUSB_DEVCTL,
|
||||
devctl | MUSB_DEVCTL_SESSION);
|
||||
devctl = musb_readb(mregs, MUSB_DEVCTL);
|
||||
if (devctl & MUSB_DEVCTL_BDEVICE)
|
||||
mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ);
|
||||
else
|
||||
musb->xceiv.state = OTG_STATE_A_IDLE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
spin_unlock_irqrestore(&musb->lock, flags);
|
||||
}
|
||||
|
||||
static irqreturn_t davinci_interrupt(int irq, void *__hci)
|
||||
{
|
||||
unsigned long flags;
|
||||
irqreturn_t retval = IRQ_NONE;
|
||||
struct musb *musb = __hci;
|
||||
void __iomem *tibase = musb->ctrl_base;
|
||||
u32 tmp;
|
||||
|
||||
spin_lock_irqsave(&musb->lock, flags);
|
||||
|
||||
/* NOTE: DaVinci shadows the Mentor IRQs. Don't manage them through
|
||||
* the Mentor registers (except for setup), use the TI ones and EOI.
|
||||
*
|
||||
* Docs describe irq "vector" registers asociated with the CPPI and
|
||||
* USB EOI registers. These hold a bitmask corresponding to the
|
||||
* current IRQ, not an irq handler address. Would using those bits
|
||||
* resolve some of the races observed in this dispatch code??
|
||||
*/
|
||||
|
||||
/* CPPI interrupts share the same IRQ line, but have their own
|
||||
* mask, state, "vector", and EOI registers.
|
||||
*/
|
||||
if (is_cppi_enabled()) {
|
||||
u32 cppi_tx = musb_readl(tibase, DAVINCI_TXCPPI_MASKED_REG);
|
||||
u32 cppi_rx = musb_readl(tibase, DAVINCI_RXCPPI_MASKED_REG);
|
||||
|
||||
if (cppi_tx || cppi_rx) {
|
||||
DBG(4, "CPPI IRQ t%x r%x\n", cppi_tx, cppi_rx);
|
||||
cppi_completion(musb, cppi_rx, cppi_tx);
|
||||
retval = IRQ_HANDLED;
|
||||
}
|
||||
}
|
||||
|
||||
/* ack and handle non-CPPI interrupts */
|
||||
tmp = musb_readl(tibase, DAVINCI_USB_INT_SRC_MASKED_REG);
|
||||
musb_writel(tibase, DAVINCI_USB_INT_SRC_CLR_REG, tmp);
|
||||
DBG(4, "IRQ %08x\n", tmp);
|
||||
|
||||
musb->int_rx = (tmp & DAVINCI_USB_RXINT_MASK)
|
||||
>> DAVINCI_USB_RXINT_SHIFT;
|
||||
musb->int_tx = (tmp & DAVINCI_USB_TXINT_MASK)
|
||||
>> DAVINCI_USB_TXINT_SHIFT;
|
||||
musb->int_usb = (tmp & DAVINCI_USB_USBINT_MASK)
|
||||
>> DAVINCI_USB_USBINT_SHIFT;
|
||||
|
||||
/* DRVVBUS irqs are the only proxy we have (a very poor one!) for
|
||||
* DaVinci's missing ID change IRQ. We need an ID change IRQ to
|
||||
* switch appropriately between halves of the OTG state machine.
|
||||
* Managing DEVCTL.SESSION per Mentor docs requires we know its
|
||||
* value, but DEVCTL.BDEVICE is invalid without DEVCTL.SESSION set.
|
||||
* Also, DRVVBUS pulses for SRP (but not at 5V) ...
|
||||
*/
|
||||
if (tmp & (DAVINCI_INTR_DRVVBUS << DAVINCI_USB_USBINT_SHIFT)) {
|
||||
int drvvbus = musb_readl(tibase, DAVINCI_USB_STAT_REG);
|
||||
void __iomem *mregs = musb->mregs;
|
||||
u8 devctl = musb_readb(mregs, MUSB_DEVCTL);
|
||||
int err = musb->int_usb & MUSB_INTR_VBUSERROR;
|
||||
|
||||
err = is_host_enabled(musb)
|
||||
&& (musb->int_usb & MUSB_INTR_VBUSERROR);
|
||||
if (err) {
|
||||
/* The Mentor core doesn't debounce VBUS as needed
|
||||
* to cope with device connect current spikes. This
|
||||
* means it's not uncommon for bus-powered devices
|
||||
* to get VBUS errors during enumeration.
|
||||
*
|
||||
* This is a workaround, but newer RTL from Mentor
|
||||
* seems to allow a better one: "re"starting sessions
|
||||
* without waiting (on EVM, a **long** time) for VBUS
|
||||
* to stop registering in devctl.
|
||||
*/
|
||||
musb->int_usb &= ~MUSB_INTR_VBUSERROR;
|
||||
musb->xceiv.state = OTG_STATE_A_WAIT_VFALL;
|
||||
mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ);
|
||||
WARNING("VBUS error workaround (delay coming)\n");
|
||||
} else if (is_host_enabled(musb) && drvvbus) {
|
||||
musb->is_active = 1;
|
||||
MUSB_HST_MODE(musb);
|
||||
musb->xceiv.default_a = 1;
|
||||
musb->xceiv.state = OTG_STATE_A_WAIT_VRISE;
|
||||
portstate(musb->port1_status |= USB_PORT_STAT_POWER);
|
||||
del_timer(&otg_workaround);
|
||||
} else {
|
||||
musb->is_active = 0;
|
||||
MUSB_DEV_MODE(musb);
|
||||
musb->xceiv.default_a = 0;
|
||||
musb->xceiv.state = OTG_STATE_B_IDLE;
|
||||
portstate(musb->port1_status &= ~USB_PORT_STAT_POWER);
|
||||
}
|
||||
|
||||
/* NOTE: this must complete poweron within 100 msec */
|
||||
davinci_source_power(musb, drvvbus, 0);
|
||||
DBG(2, "VBUS %s (%s)%s, devctl %02x\n",
|
||||
drvvbus ? "on" : "off",
|
||||
otg_state_string(musb),
|
||||
err ? " ERROR" : "",
|
||||
devctl);
|
||||
retval = IRQ_HANDLED;
|
||||
}
|
||||
|
||||
if (musb->int_tx || musb->int_rx || musb->int_usb)
|
||||
retval |= musb_interrupt(musb);
|
||||
|
||||
/* irq stays asserted until EOI is written */
|
||||
musb_writel(tibase, DAVINCI_USB_EOI_REG, 0);
|
||||
|
||||
/* poll for ID change */
|
||||
if (is_otg_enabled(musb)
|
||||
&& musb->xceiv.state == OTG_STATE_B_IDLE)
|
||||
mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ);
|
||||
|
||||
spin_unlock_irqrestore(&musb->lock, flags);
|
||||
|
||||
/* REVISIT we sometimes get unhandled IRQs
|
||||
* (e.g. ep0). not clear why...
|
||||
*/
|
||||
if (retval != IRQ_HANDLED)
|
||||
DBG(5, "unhandled? %08x\n", tmp);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
int __init musb_platform_init(struct musb *musb)
|
||||
{
|
||||
void __iomem *tibase = musb->ctrl_base;
|
||||
u32 revision;
|
||||
|
||||
musb->mregs += DAVINCI_BASE_OFFSET;
|
||||
#if 0
|
||||
/* REVISIT there's something odd about clocking, this
|
||||
* didn't appear do the job ...
|
||||
*/
|
||||
musb->clock = clk_get(pDevice, "usb");
|
||||
if (IS_ERR(musb->clock))
|
||||
return PTR_ERR(musb->clock);
|
||||
|
||||
status = clk_enable(musb->clock);
|
||||
if (status < 0)
|
||||
return -ENODEV;
|
||||
#endif
|
||||
|
||||
/* returns zero if e.g. not clocked */
|
||||
revision = musb_readl(tibase, DAVINCI_USB_VERSION_REG);
|
||||
if (revision == 0)
|
||||
return -ENODEV;
|
||||
|
||||
if (is_host_enabled(musb))
|
||||
setup_timer(&otg_workaround, otg_timer, (unsigned long) musb);
|
||||
|
||||
musb->board_set_vbus = davinci_set_vbus;
|
||||
davinci_source_power(musb, 0, 1);
|
||||
|
||||
/* reset the controller */
|
||||
musb_writel(tibase, DAVINCI_USB_CTRL_REG, 0x1);
|
||||
|
||||
/* start the on-chip PHY and its PLL */
|
||||
phy_on();
|
||||
|
||||
msleep(5);
|
||||
|
||||
/* NOTE: irqs are in mixed mode, not bypass to pure-musb */
|
||||
pr_debug("DaVinci OTG revision %08x phy %03x control %02x\n",
|
||||
revision, __raw_readl((void __force __iomem *)
|
||||
IO_ADDRESS(USBPHY_CTL_PADDR)),
|
||||
musb_readb(tibase, DAVINCI_USB_CTRL_REG));
|
||||
|
||||
musb->isr = davinci_interrupt;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int musb_platform_exit(struct musb *musb)
|
||||
{
|
||||
if (is_host_enabled(musb))
|
||||
del_timer_sync(&otg_workaround);
|
||||
|
||||
davinci_source_power(musb, 0 /*off*/, 1);
|
||||
|
||||
/* delay, to avoid problems with module reload */
|
||||
if (is_host_enabled(musb) && musb->xceiv.default_a) {
|
||||
int maxdelay = 30;
|
||||
u8 devctl, warn = 0;
|
||||
|
||||
/* if there's no peripheral connected, this can take a
|
||||
* long time to fall, especially on EVM with huge C133.
|
||||
*/
|
||||
do {
|
||||
devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
|
||||
if (!(devctl & MUSB_DEVCTL_VBUS))
|
||||
break;
|
||||
if ((devctl & MUSB_DEVCTL_VBUS) != warn) {
|
||||
warn = devctl & MUSB_DEVCTL_VBUS;
|
||||
DBG(1, "VBUS %d\n",
|
||||
warn >> MUSB_DEVCTL_VBUS_SHIFT);
|
||||
}
|
||||
msleep(1000);
|
||||
maxdelay--;
|
||||
} while (maxdelay > 0);
|
||||
|
||||
/* in OTG mode, another host might be connected */
|
||||
if (devctl & MUSB_DEVCTL_VBUS)
|
||||
DBG(1, "VBUS off timeout (devctl %02x)\n", devctl);
|
||||
}
|
||||
|
||||
phy_off();
|
||||
return 0;
|
||||
}
|
100
drivers/usb/musb/davinci.h
Normal file
100
drivers/usb/musb/davinci.h
Normal file
@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2006 by Texas Instruments
|
||||
*
|
||||
* The Inventra Controller Driver for Linux is free software; you
|
||||
* can redistribute it and/or modify it under the terms of the GNU
|
||||
* General Public License version 2 as published by the Free Software
|
||||
* Foundation.
|
||||
*/
|
||||
|
||||
#ifndef __MUSB_HDRDF_H__
|
||||
#define __MUSB_HDRDF_H__
|
||||
|
||||
/*
|
||||
* DaVinci-specific definitions
|
||||
*/
|
||||
|
||||
/* Integrated highspeed/otg PHY */
|
||||
#define USBPHY_CTL_PADDR (DAVINCI_SYSTEM_MODULE_BASE + 0x34)
|
||||
#define USBPHY_PHYCLKGD (1 << 8)
|
||||
#define USBPHY_SESNDEN (1 << 7) /* v(sess_end) comparator */
|
||||
#define USBPHY_VBDTCTEN (1 << 6) /* v(bus) comparator */
|
||||
#define USBPHY_PHYPLLON (1 << 4) /* override pll suspend */
|
||||
#define USBPHY_CLKO1SEL (1 << 3)
|
||||
#define USBPHY_OSCPDWN (1 << 2)
|
||||
#define USBPHY_PHYPDWN (1 << 0)
|
||||
|
||||
/* For now include usb OTG module registers here */
|
||||
#define DAVINCI_USB_VERSION_REG 0x00
|
||||
#define DAVINCI_USB_CTRL_REG 0x04
|
||||
#define DAVINCI_USB_STAT_REG 0x08
|
||||
#define DAVINCI_RNDIS_REG 0x10
|
||||
#define DAVINCI_AUTOREQ_REG 0x14
|
||||
#define DAVINCI_USB_INT_SOURCE_REG 0x20
|
||||
#define DAVINCI_USB_INT_SET_REG 0x24
|
||||
#define DAVINCI_USB_INT_SRC_CLR_REG 0x28
|
||||
#define DAVINCI_USB_INT_MASK_REG 0x2c
|
||||
#define DAVINCI_USB_INT_MASK_SET_REG 0x30
|
||||
#define DAVINCI_USB_INT_MASK_CLR_REG 0x34
|
||||
#define DAVINCI_USB_INT_SRC_MASKED_REG 0x38
|
||||
#define DAVINCI_USB_EOI_REG 0x3c
|
||||
#define DAVINCI_USB_EOI_INTVEC 0x40
|
||||
|
||||
/* BEGIN CPPI-generic (?) */
|
||||
|
||||
/* CPPI related registers */
|
||||
#define DAVINCI_TXCPPI_CTRL_REG 0x80
|
||||
#define DAVINCI_TXCPPI_TEAR_REG 0x84
|
||||
#define DAVINCI_CPPI_EOI_REG 0x88
|
||||
#define DAVINCI_CPPI_INTVEC_REG 0x8c
|
||||
#define DAVINCI_TXCPPI_MASKED_REG 0x90
|
||||
#define DAVINCI_TXCPPI_RAW_REG 0x94
|
||||
#define DAVINCI_TXCPPI_INTENAB_REG 0x98
|
||||
#define DAVINCI_TXCPPI_INTCLR_REG 0x9c
|
||||
|
||||
#define DAVINCI_RXCPPI_CTRL_REG 0xC0
|
||||
#define DAVINCI_RXCPPI_MASKED_REG 0xD0
|
||||
#define DAVINCI_RXCPPI_RAW_REG 0xD4
|
||||
#define DAVINCI_RXCPPI_INTENAB_REG 0xD8
|
||||
#define DAVINCI_RXCPPI_INTCLR_REG 0xDC
|
||||
|
||||
#define DAVINCI_RXCPPI_BUFCNT0_REG 0xE0
|
||||
#define DAVINCI_RXCPPI_BUFCNT1_REG 0xE4
|
||||
#define DAVINCI_RXCPPI_BUFCNT2_REG 0xE8
|
||||
#define DAVINCI_RXCPPI_BUFCNT3_REG 0xEC
|
||||
|
||||
/* CPPI state RAM entries */
|
||||
#define DAVINCI_CPPI_STATERAM_BASE_OFFSET 0x100
|
||||
|
||||
#define DAVINCI_TXCPPI_STATERAM_OFFSET(chnum) \
|
||||
(DAVINCI_CPPI_STATERAM_BASE_OFFSET + ((chnum) * 0x40))
|
||||
#define DAVINCI_RXCPPI_STATERAM_OFFSET(chnum) \
|
||||
(DAVINCI_CPPI_STATERAM_BASE_OFFSET + 0x20 + ((chnum) * 0x40))
|
||||
|
||||
/* CPPI masks */
|
||||
#define DAVINCI_DMA_CTRL_ENABLE 1
|
||||
#define DAVINCI_DMA_CTRL_DISABLE 0
|
||||
|
||||
#define DAVINCI_DMA_ALL_CHANNELS_ENABLE 0xF
|
||||
#define DAVINCI_DMA_ALL_CHANNELS_DISABLE 0xF
|
||||
|
||||
/* END CPPI-generic (?) */
|
||||
|
||||
#define DAVINCI_USB_TX_ENDPTS_MASK 0x1f /* ep0 + 4 tx */
|
||||
#define DAVINCI_USB_RX_ENDPTS_MASK 0x1e /* 4 rx */
|
||||
|
||||
#define DAVINCI_USB_USBINT_SHIFT 16
|
||||
#define DAVINCI_USB_TXINT_SHIFT 0
|
||||
#define DAVINCI_USB_RXINT_SHIFT 8
|
||||
|
||||
#define DAVINCI_INTR_DRVVBUS 0x0100
|
||||
|
||||
#define DAVINCI_USB_USBINT_MASK 0x01ff0000 /* 8 Mentor, DRVVBUS */
|
||||
#define DAVINCI_USB_TXINT_MASK \
|
||||
(DAVINCI_USB_TX_ENDPTS_MASK << DAVINCI_USB_TXINT_SHIFT)
|
||||
#define DAVINCI_USB_RXINT_MASK \
|
||||
(DAVINCI_USB_RX_ENDPTS_MASK << DAVINCI_USB_RXINT_SHIFT)
|
||||
|
||||
#define DAVINCI_BASE_OFFSET 0x400
|
||||
|
||||
#endif /* __MUSB_HDRDF_H__ */
|
2261
drivers/usb/musb/musb_core.c
Normal file
2261
drivers/usb/musb/musb_core.c
Normal file
File diff suppressed because it is too large
Load Diff
507
drivers/usb/musb/musb_core.h
Normal file
507
drivers/usb/musb/musb_core.h
Normal file
@ -0,0 +1,507 @@
|
||||
/*
|
||||
* MUSB OTG driver defines
|
||||
*
|
||||
* Copyright 2005 Mentor Graphics Corporation
|
||||
* Copyright (C) 2005-2006 by Texas Instruments
|
||||
* Copyright (C) 2006-2007 Nokia Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED "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 AUTHORS 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __MUSB_CORE_H__
|
||||
#define __MUSB_CORE_H__
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/otg.h>
|
||||
#include <linux/usb/musb.h>
|
||||
|
||||
struct musb;
|
||||
struct musb_hw_ep;
|
||||
struct musb_ep;
|
||||
|
||||
|
||||
#include "musb_debug.h"
|
||||
#include "musb_dma.h"
|
||||
|
||||
#include "musb_io.h"
|
||||
#include "musb_regs.h"
|
||||
|
||||
#include "musb_gadget.h"
|
||||
#include "../core/hcd.h"
|
||||
#include "musb_host.h"
|
||||
|
||||
|
||||
|
||||
#ifdef CONFIG_USB_MUSB_OTG
|
||||
|
||||
#define is_peripheral_enabled(musb) ((musb)->board_mode != MUSB_HOST)
|
||||
#define is_host_enabled(musb) ((musb)->board_mode != MUSB_PERIPHERAL)
|
||||
#define is_otg_enabled(musb) ((musb)->board_mode == MUSB_OTG)
|
||||
|
||||
/* NOTE: otg and peripheral-only state machines start at B_IDLE.
|
||||
* OTG or host-only go to A_IDLE when ID is sensed.
|
||||
*/
|
||||
#define is_peripheral_active(m) (!(m)->is_host)
|
||||
#define is_host_active(m) ((m)->is_host)
|
||||
|
||||
#else
|
||||
#define is_peripheral_enabled(musb) is_peripheral_capable()
|
||||
#define is_host_enabled(musb) is_host_capable()
|
||||
#define is_otg_enabled(musb) 0
|
||||
|
||||
#define is_peripheral_active(musb) is_peripheral_capable()
|
||||
#define is_host_active(musb) is_host_capable()
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_USB_MUSB_OTG) || defined(CONFIG_USB_MUSB_PERIPHERAL)
|
||||
/* for some reason, the "select USB_GADGET_MUSB_HDRC" doesn't always
|
||||
* override that choice selection (often USB_GADGET_DUMMY_HCD).
|
||||
*/
|
||||
#ifndef CONFIG_USB_GADGET_MUSB_HDRC
|
||||
#error bogus Kconfig output ... select CONFIG_USB_GADGET_MUSB_HDRC
|
||||
#endif
|
||||
#endif /* need MUSB gadget selection */
|
||||
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
#include <linux/fs.h>
|
||||
#define MUSB_CONFIG_PROC_FS
|
||||
#endif
|
||||
|
||||
/****************************** PERIPHERAL ROLE *****************************/
|
||||
|
||||
#ifdef CONFIG_USB_GADGET_MUSB_HDRC
|
||||
|
||||
#define is_peripheral_capable() (1)
|
||||
|
||||
extern irqreturn_t musb_g_ep0_irq(struct musb *);
|
||||
extern void musb_g_tx(struct musb *, u8);
|
||||
extern void musb_g_rx(struct musb *, u8);
|
||||
extern void musb_g_reset(struct musb *);
|
||||
extern void musb_g_suspend(struct musb *);
|
||||
extern void musb_g_resume(struct musb *);
|
||||
extern void musb_g_wakeup(struct musb *);
|
||||
extern void musb_g_disconnect(struct musb *);
|
||||
|
||||
#else
|
||||
|
||||
#define is_peripheral_capable() (0)
|
||||
|
||||
static inline irqreturn_t musb_g_ep0_irq(struct musb *m) { return IRQ_NONE; }
|
||||
static inline void musb_g_reset(struct musb *m) {}
|
||||
static inline void musb_g_suspend(struct musb *m) {}
|
||||
static inline void musb_g_resume(struct musb *m) {}
|
||||
static inline void musb_g_wakeup(struct musb *m) {}
|
||||
static inline void musb_g_disconnect(struct musb *m) {}
|
||||
|
||||
#endif
|
||||
|
||||
/****************************** HOST ROLE ***********************************/
|
||||
|
||||
#ifdef CONFIG_USB_MUSB_HDRC_HCD
|
||||
|
||||
#define is_host_capable() (1)
|
||||
|
||||
extern irqreturn_t musb_h_ep0_irq(struct musb *);
|
||||
extern void musb_host_tx(struct musb *, u8);
|
||||
extern void musb_host_rx(struct musb *, u8);
|
||||
|
||||
#else
|
||||
|
||||
#define is_host_capable() (0)
|
||||
|
||||
static inline irqreturn_t musb_h_ep0_irq(struct musb *m) { return IRQ_NONE; }
|
||||
static inline void musb_host_tx(struct musb *m, u8 e) {}
|
||||
static inline void musb_host_rx(struct musb *m, u8 e) {}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/****************************** CONSTANTS ********************************/
|
||||
|
||||
#ifndef MUSB_C_NUM_EPS
|
||||
#define MUSB_C_NUM_EPS ((u8)16)
|
||||
#endif
|
||||
|
||||
#ifndef MUSB_MAX_END0_PACKET
|
||||
#define MUSB_MAX_END0_PACKET ((u16)MUSB_EP0_FIFOSIZE)
|
||||
#endif
|
||||
|
||||
/* host side ep0 states */
|
||||
enum musb_h_ep0_state {
|
||||
MUSB_EP0_IDLE,
|
||||
MUSB_EP0_START, /* expect ack of setup */
|
||||
MUSB_EP0_IN, /* expect IN DATA */
|
||||
MUSB_EP0_OUT, /* expect ack of OUT DATA */
|
||||
MUSB_EP0_STATUS, /* expect ack of STATUS */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* peripheral side ep0 states */
|
||||
enum musb_g_ep0_state {
|
||||
MUSB_EP0_STAGE_SETUP, /* idle, waiting for setup */
|
||||
MUSB_EP0_STAGE_TX, /* IN data */
|
||||
MUSB_EP0_STAGE_RX, /* OUT data */
|
||||
MUSB_EP0_STAGE_STATUSIN, /* (after OUT data) */
|
||||
MUSB_EP0_STAGE_STATUSOUT, /* (after IN data) */
|
||||
MUSB_EP0_STAGE_ACKWAIT, /* after zlp, before statusin */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* OTG protocol constants */
|
||||
#define OTG_TIME_A_WAIT_VRISE 100 /* msec (max) */
|
||||
#define OTG_TIME_A_WAIT_BCON 0 /* 0=infinite; min 1000 msec */
|
||||
#define OTG_TIME_A_IDLE_BDIS 200 /* msec (min) */
|
||||
|
||||
/*************************** REGISTER ACCESS ********************************/
|
||||
|
||||
/* Endpoint registers (other than dynfifo setup) can be accessed either
|
||||
* directly with the "flat" model, or after setting up an index register.
|
||||
*/
|
||||
|
||||
#if defined(CONFIG_ARCH_DAVINCI) || defined(CONFIG_ARCH_OMAP2430) \
|
||||
|| defined(CONFIG_ARCH_OMAP3430)
|
||||
/* REVISIT indexed access seemed to
|
||||
* misbehave (on DaVinci) for at least peripheral IN ...
|
||||
*/
|
||||
#define MUSB_FLAT_REG
|
||||
#endif
|
||||
|
||||
/* TUSB mapping: "flat" plus ep0 special cases */
|
||||
#if defined(CONFIG_USB_TUSB6010)
|
||||
#define musb_ep_select(_mbase, _epnum) \
|
||||
musb_writeb((_mbase), MUSB_INDEX, (_epnum))
|
||||
#define MUSB_EP_OFFSET MUSB_TUSB_OFFSET
|
||||
|
||||
/* "flat" mapping: each endpoint has its own i/o address */
|
||||
#elif defined(MUSB_FLAT_REG)
|
||||
#define musb_ep_select(_mbase, _epnum) (((void)(_mbase)), ((void)(_epnum)))
|
||||
#define MUSB_EP_OFFSET MUSB_FLAT_OFFSET
|
||||
|
||||
/* "indexed" mapping: INDEX register controls register bank select */
|
||||
#else
|
||||
#define musb_ep_select(_mbase, _epnum) \
|
||||
musb_writeb((_mbase), MUSB_INDEX, (_epnum))
|
||||
#define MUSB_EP_OFFSET MUSB_INDEXED_OFFSET
|
||||
#endif
|
||||
|
||||
/****************************** FUNCTIONS ********************************/
|
||||
|
||||
#define MUSB_HST_MODE(_musb)\
|
||||
{ (_musb)->is_host = true; }
|
||||
#define MUSB_DEV_MODE(_musb) \
|
||||
{ (_musb)->is_host = false; }
|
||||
|
||||
#define test_devctl_hst_mode(_x) \
|
||||
(musb_readb((_x)->mregs, MUSB_DEVCTL)&MUSB_DEVCTL_HM)
|
||||
|
||||
#define MUSB_MODE(musb) ((musb)->is_host ? "Host" : "Peripheral")
|
||||
|
||||
/******************************** TYPES *************************************/
|
||||
|
||||
/*
|
||||
* struct musb_hw_ep - endpoint hardware (bidirectional)
|
||||
*
|
||||
* Ordered slightly for better cacheline locality.
|
||||
*/
|
||||
struct musb_hw_ep {
|
||||
struct musb *musb;
|
||||
void __iomem *fifo;
|
||||
void __iomem *regs;
|
||||
|
||||
#ifdef CONFIG_USB_TUSB6010
|
||||
void __iomem *conf;
|
||||
#endif
|
||||
|
||||
/* index in musb->endpoints[] */
|
||||
u8 epnum;
|
||||
|
||||
/* hardware configuration, possibly dynamic */
|
||||
bool is_shared_fifo;
|
||||
bool tx_double_buffered;
|
||||
bool rx_double_buffered;
|
||||
u16 max_packet_sz_tx;
|
||||
u16 max_packet_sz_rx;
|
||||
|
||||
struct dma_channel *tx_channel;
|
||||
struct dma_channel *rx_channel;
|
||||
|
||||
#ifdef CONFIG_USB_TUSB6010
|
||||
/* TUSB has "asynchronous" and "synchronous" dma modes */
|
||||
dma_addr_t fifo_async;
|
||||
dma_addr_t fifo_sync;
|
||||
void __iomem *fifo_sync_va;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_MUSB_HDRC_HCD
|
||||
void __iomem *target_regs;
|
||||
|
||||
/* currently scheduled peripheral endpoint */
|
||||
struct musb_qh *in_qh;
|
||||
struct musb_qh *out_qh;
|
||||
|
||||
u8 rx_reinit;
|
||||
u8 tx_reinit;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_GADGET_MUSB_HDRC
|
||||
/* peripheral side */
|
||||
struct musb_ep ep_in; /* TX */
|
||||
struct musb_ep ep_out; /* RX */
|
||||
#endif
|
||||
};
|
||||
|
||||
static inline struct usb_request *next_in_request(struct musb_hw_ep *hw_ep)
|
||||
{
|
||||
#ifdef CONFIG_USB_GADGET_MUSB_HDRC
|
||||
return next_request(&hw_ep->ep_in);
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline struct usb_request *next_out_request(struct musb_hw_ep *hw_ep)
|
||||
{
|
||||
#ifdef CONFIG_USB_GADGET_MUSB_HDRC
|
||||
return next_request(&hw_ep->ep_out);
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* struct musb - Driver instance data.
|
||||
*/
|
||||
struct musb {
|
||||
/* device lock */
|
||||
spinlock_t lock;
|
||||
struct clk *clock;
|
||||
irqreturn_t (*isr)(int, void *);
|
||||
struct work_struct irq_work;
|
||||
|
||||
/* this hub status bit is reserved by USB 2.0 and not seen by usbcore */
|
||||
#define MUSB_PORT_STAT_RESUME (1 << 31)
|
||||
|
||||
u32 port1_status;
|
||||
|
||||
#ifdef CONFIG_USB_MUSB_HDRC_HCD
|
||||
unsigned long rh_timer;
|
||||
|
||||
enum musb_h_ep0_state ep0_stage;
|
||||
|
||||
/* bulk traffic normally dedicates endpoint hardware, and each
|
||||
* direction has its own ring of host side endpoints.
|
||||
* we try to progress the transfer at the head of each endpoint's
|
||||
* queue until it completes or NAKs too much; then we try the next
|
||||
* endpoint.
|
||||
*/
|
||||
struct musb_hw_ep *bulk_ep;
|
||||
|
||||
struct list_head control; /* of musb_qh */
|
||||
struct list_head in_bulk; /* of musb_qh */
|
||||
struct list_head out_bulk; /* of musb_qh */
|
||||
struct musb_qh *periodic[32]; /* tree of interrupt+iso */
|
||||
#endif
|
||||
|
||||
/* called with IRQs blocked; ON/nonzero implies starting a session,
|
||||
* and waiting at least a_wait_vrise_tmout.
|
||||
*/
|
||||
void (*board_set_vbus)(struct musb *, int is_on);
|
||||
|
||||
struct dma_controller *dma_controller;
|
||||
|
||||
struct device *controller;
|
||||
void __iomem *ctrl_base;
|
||||
void __iomem *mregs;
|
||||
|
||||
#ifdef CONFIG_USB_TUSB6010
|
||||
dma_addr_t async;
|
||||
dma_addr_t sync;
|
||||
void __iomem *sync_va;
|
||||
#endif
|
||||
|
||||
/* passed down from chip/board specific irq handlers */
|
||||
u8 int_usb;
|
||||
u16 int_rx;
|
||||
u16 int_tx;
|
||||
|
||||
struct otg_transceiver xceiv;
|
||||
|
||||
int nIrq;
|
||||
|
||||
struct musb_hw_ep endpoints[MUSB_C_NUM_EPS];
|
||||
#define control_ep endpoints
|
||||
|
||||
#define VBUSERR_RETRY_COUNT 3
|
||||
u16 vbuserr_retry;
|
||||
u16 epmask;
|
||||
u8 nr_endpoints;
|
||||
|
||||
u8 board_mode; /* enum musb_mode */
|
||||
int (*board_set_power)(int state);
|
||||
|
||||
int (*set_clock)(struct clk *clk, int is_active);
|
||||
|
||||
u8 min_power; /* vbus for periph, in mA/2 */
|
||||
|
||||
bool is_host;
|
||||
|
||||
int a_wait_bcon; /* VBUS timeout in msecs */
|
||||
unsigned long idle_timeout; /* Next timeout in jiffies */
|
||||
|
||||
/* active means connected and not suspended */
|
||||
unsigned is_active:1;
|
||||
|
||||
unsigned is_multipoint:1;
|
||||
unsigned ignore_disconnect:1; /* during bus resets */
|
||||
|
||||
#ifdef C_MP_TX
|
||||
unsigned bulk_split:1;
|
||||
#define can_bulk_split(musb,type) \
|
||||
(((type) == USB_ENDPOINT_XFER_BULK) && (musb)->bulk_split)
|
||||
#else
|
||||
#define can_bulk_split(musb, type) 0
|
||||
#endif
|
||||
|
||||
#ifdef C_MP_RX
|
||||
unsigned bulk_combine:1;
|
||||
#define can_bulk_combine(musb,type) \
|
||||
(((type) == USB_ENDPOINT_XFER_BULK) && (musb)->bulk_combine)
|
||||
#else
|
||||
#define can_bulk_combine(musb, type) 0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_GADGET_MUSB_HDRC
|
||||
/* is_suspended means USB B_PERIPHERAL suspend */
|
||||
unsigned is_suspended:1;
|
||||
|
||||
/* may_wakeup means remote wakeup is enabled */
|
||||
unsigned may_wakeup:1;
|
||||
|
||||
/* is_self_powered is reported in device status and the
|
||||
* config descriptor. is_bus_powered means B_PERIPHERAL
|
||||
* draws some VBUS current; both can be true.
|
||||
*/
|
||||
unsigned is_self_powered:1;
|
||||
unsigned is_bus_powered:1;
|
||||
|
||||
unsigned set_address:1;
|
||||
unsigned test_mode:1;
|
||||
unsigned softconnect:1;
|
||||
|
||||
u8 address;
|
||||
u8 test_mode_nr;
|
||||
u16 ackpend; /* ep0 */
|
||||
enum musb_g_ep0_state ep0_state;
|
||||
struct usb_gadget g; /* the gadget */
|
||||
struct usb_gadget_driver *gadget_driver; /* its driver */
|
||||
#endif
|
||||
|
||||
struct musb_hdrc_config *config;
|
||||
|
||||
#ifdef MUSB_CONFIG_PROC_FS
|
||||
struct proc_dir_entry *proc_entry;
|
||||
#endif
|
||||
};
|
||||
|
||||
static inline void musb_set_vbus(struct musb *musb, int is_on)
|
||||
{
|
||||
musb->board_set_vbus(musb, is_on);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USB_GADGET_MUSB_HDRC
|
||||
static inline struct musb *gadget_to_musb(struct usb_gadget *g)
|
||||
{
|
||||
return container_of(g, struct musb, g);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/***************************** Glue it together *****************************/
|
||||
|
||||
extern const char musb_driver_name[];
|
||||
|
||||
extern void musb_start(struct musb *musb);
|
||||
extern void musb_stop(struct musb *musb);
|
||||
|
||||
extern void musb_write_fifo(struct musb_hw_ep *ep, u16 len, const u8 *src);
|
||||
extern void musb_read_fifo(struct musb_hw_ep *ep, u16 len, u8 *dst);
|
||||
|
||||
extern void musb_load_testpacket(struct musb *);
|
||||
|
||||
extern irqreturn_t musb_interrupt(struct musb *);
|
||||
|
||||
extern void musb_platform_enable(struct musb *musb);
|
||||
extern void musb_platform_disable(struct musb *musb);
|
||||
|
||||
extern void musb_hnp_stop(struct musb *musb);
|
||||
|
||||
extern void musb_platform_set_mode(struct musb *musb, u8 musb_mode);
|
||||
|
||||
#if defined(CONFIG_USB_TUSB6010) || \
|
||||
defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP34XX)
|
||||
extern void musb_platform_try_idle(struct musb *musb, unsigned long timeout);
|
||||
#else
|
||||
#define musb_platform_try_idle(x, y) do {} while (0)
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_TUSB6010
|
||||
extern int musb_platform_get_vbus_status(struct musb *musb);
|
||||
#else
|
||||
#define musb_platform_get_vbus_status(x) 0
|
||||
#endif
|
||||
|
||||
extern int __init musb_platform_init(struct musb *musb);
|
||||
extern int musb_platform_exit(struct musb *musb);
|
||||
|
||||
/*-------------------------- ProcFS definitions ---------------------*/
|
||||
|
||||
struct proc_dir_entry;
|
||||
|
||||
#if (MUSB_DEBUG > 0) && defined(MUSB_CONFIG_PROC_FS)
|
||||
extern struct proc_dir_entry *musb_debug_create(char *name, struct musb *data);
|
||||
extern void musb_debug_delete(char *name, struct musb *data);
|
||||
|
||||
#else
|
||||
static inline struct proc_dir_entry *
|
||||
musb_debug_create(char *name, struct musb *data)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
static inline void musb_debug_delete(char *name, struct musb *data)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __MUSB_CORE_H__ */
|
66
drivers/usb/musb/musb_debug.h
Normal file
66
drivers/usb/musb/musb_debug.h
Normal file
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* MUSB OTG driver debug defines
|
||||
*
|
||||
* Copyright 2005 Mentor Graphics Corporation
|
||||
* Copyright (C) 2005-2006 by Texas Instruments
|
||||
* Copyright (C) 2006-2007 Nokia Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED "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 AUTHORS 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __MUSB_LINUX_DEBUG_H__
|
||||
#define __MUSB_LINUX_DEBUG_H__
|
||||
|
||||
#define yprintk(facility, format, args...) \
|
||||
do { printk(facility "%s %d: " format , \
|
||||
__func__, __LINE__ , ## args); } while (0)
|
||||
#define WARNING(fmt, args...) yprintk(KERN_WARNING, fmt, ## args)
|
||||
#define INFO(fmt, args...) yprintk(KERN_INFO, fmt, ## args)
|
||||
#define ERR(fmt, args...) yprintk(KERN_ERR, fmt, ## args)
|
||||
|
||||
#define xprintk(level, facility, format, args...) do { \
|
||||
if (_dbg_level(level)) { \
|
||||
printk(facility "%s %d: " format , \
|
||||
__func__, __LINE__ , ## args); \
|
||||
} } while (0)
|
||||
|
||||
#if MUSB_DEBUG > 0
|
||||
extern unsigned debug;
|
||||
#else
|
||||
#define debug 0
|
||||
#endif
|
||||
|
||||
static inline int _dbg_level(unsigned l)
|
||||
{
|
||||
return debug >= l;
|
||||
}
|
||||
|
||||
#define DBG(level, fmt, args...) xprintk(level, KERN_DEBUG, fmt, ## args)
|
||||
|
||||
extern const char *otg_state_string(struct musb *);
|
||||
|
||||
#endif /* __MUSB_LINUX_DEBUG_H__ */
|
172
drivers/usb/musb/musb_dma.h
Normal file
172
drivers/usb/musb/musb_dma.h
Normal file
@ -0,0 +1,172 @@
|
||||
/*
|
||||
* MUSB OTG driver DMA controller abstraction
|
||||
*
|
||||
* Copyright 2005 Mentor Graphics Corporation
|
||||
* Copyright (C) 2005-2006 by Texas Instruments
|
||||
* Copyright (C) 2006-2007 Nokia Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED "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 AUTHORS 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __MUSB_DMA_H__
|
||||
#define __MUSB_DMA_H__
|
||||
|
||||
struct musb_hw_ep;
|
||||
|
||||
/*
|
||||
* DMA Controller Abstraction
|
||||
*
|
||||
* DMA Controllers are abstracted to allow use of a variety of different
|
||||
* implementations of DMA, as allowed by the Inventra USB cores. On the
|
||||
* host side, usbcore sets up the DMA mappings and flushes caches; on the
|
||||
* peripheral side, the gadget controller driver does. Responsibilities
|
||||
* of a DMA controller driver include:
|
||||
*
|
||||
* - Handling the details of moving multiple USB packets
|
||||
* in cooperation with the Inventra USB core, including especially
|
||||
* the correct RX side treatment of short packets and buffer-full
|
||||
* states (both of which terminate transfers).
|
||||
*
|
||||
* - Knowing the correlation between dma channels and the
|
||||
* Inventra core's local endpoint resources and data direction.
|
||||
*
|
||||
* - Maintaining a list of allocated/available channels.
|
||||
*
|
||||
* - Updating channel status on interrupts,
|
||||
* whether shared with the Inventra core or separate.
|
||||
*/
|
||||
|
||||
#define DMA_ADDR_INVALID (~(dma_addr_t)0)
|
||||
|
||||
#ifndef CONFIG_MUSB_PIO_ONLY
|
||||
#define is_dma_capable() (1)
|
||||
#else
|
||||
#define is_dma_capable() (0)
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_TI_CPPI_DMA
|
||||
#define is_cppi_enabled() 1
|
||||
#else
|
||||
#define is_cppi_enabled() 0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_TUSB_OMAP_DMA
|
||||
#define tusb_dma_omap() 1
|
||||
#else
|
||||
#define tusb_dma_omap() 0
|
||||
#endif
|
||||
|
||||
/*
|
||||
* DMA channel status ... updated by the dma controller driver whenever that
|
||||
* status changes, and protected by the overall controller spinlock.
|
||||
*/
|
||||
enum dma_channel_status {
|
||||
/* unallocated */
|
||||
MUSB_DMA_STATUS_UNKNOWN,
|
||||
/* allocated ... but not busy, no errors */
|
||||
MUSB_DMA_STATUS_FREE,
|
||||
/* busy ... transactions are active */
|
||||
MUSB_DMA_STATUS_BUSY,
|
||||
/* transaction(s) aborted due to ... dma or memory bus error */
|
||||
MUSB_DMA_STATUS_BUS_ABORT,
|
||||
/* transaction(s) aborted due to ... core error or USB fault */
|
||||
MUSB_DMA_STATUS_CORE_ABORT
|
||||
};
|
||||
|
||||
struct dma_controller;
|
||||
|
||||
/**
|
||||
* struct dma_channel - A DMA channel.
|
||||
* @private_data: channel-private data
|
||||
* @max_len: the maximum number of bytes the channel can move in one
|
||||
* transaction (typically representing many USB maximum-sized packets)
|
||||
* @actual_len: how many bytes have been transferred
|
||||
* @status: current channel status (updated e.g. on interrupt)
|
||||
* @desired_mode: true if mode 1 is desired; false if mode 0 is desired
|
||||
*
|
||||
* channels are associated with an endpoint for the duration of at least
|
||||
* one usb transfer.
|
||||
*/
|
||||
struct dma_channel {
|
||||
void *private_data;
|
||||
/* FIXME not void* private_data, but a dma_controller * */
|
||||
size_t max_len;
|
||||
size_t actual_len;
|
||||
enum dma_channel_status status;
|
||||
bool desired_mode;
|
||||
};
|
||||
|
||||
/*
|
||||
* dma_channel_status - return status of dma channel
|
||||
* @c: the channel
|
||||
*
|
||||
* Returns the software's view of the channel status. If that status is BUSY
|
||||
* then it's possible that the hardware has completed (or aborted) a transfer,
|
||||
* so the driver needs to update that status.
|
||||
*/
|
||||
static inline enum dma_channel_status
|
||||
dma_channel_status(struct dma_channel *c)
|
||||
{
|
||||
return (is_dma_capable() && c) ? c->status : MUSB_DMA_STATUS_UNKNOWN;
|
||||
}
|
||||
|
||||
/**
|
||||
* struct dma_controller - A DMA Controller.
|
||||
* @start: call this to start a DMA controller;
|
||||
* return 0 on success, else negative errno
|
||||
* @stop: call this to stop a DMA controller
|
||||
* return 0 on success, else negative errno
|
||||
* @channel_alloc: call this to allocate a DMA channel
|
||||
* @channel_release: call this to release a DMA channel
|
||||
* @channel_abort: call this to abort a pending DMA transaction,
|
||||
* returning it to FREE (but allocated) state
|
||||
*
|
||||
* Controllers manage dma channels.
|
||||
*/
|
||||
struct dma_controller {
|
||||
int (*start)(struct dma_controller *);
|
||||
int (*stop)(struct dma_controller *);
|
||||
struct dma_channel *(*channel_alloc)(struct dma_controller *,
|
||||
struct musb_hw_ep *, u8 is_tx);
|
||||
void (*channel_release)(struct dma_channel *);
|
||||
int (*channel_program)(struct dma_channel *channel,
|
||||
u16 maxpacket, u8 mode,
|
||||
dma_addr_t dma_addr,
|
||||
u32 length);
|
||||
int (*channel_abort)(struct dma_channel *);
|
||||
};
|
||||
|
||||
/* called after channel_program(), may indicate a fault */
|
||||
extern void musb_dma_completion(struct musb *musb, u8 epnum, u8 transmit);
|
||||
|
||||
|
||||
extern struct dma_controller *__init
|
||||
dma_controller_create(struct musb *, void __iomem *);
|
||||
|
||||
extern void dma_controller_destroy(struct dma_controller *);
|
||||
|
||||
#endif /* __MUSB_DMA_H__ */
|
2031
drivers/usb/musb/musb_gadget.c
Normal file
2031
drivers/usb/musb/musb_gadget.c
Normal file
File diff suppressed because it is too large
Load Diff
108
drivers/usb/musb/musb_gadget.h
Normal file
108
drivers/usb/musb/musb_gadget.h
Normal file
@ -0,0 +1,108 @@
|
||||
/*
|
||||
* MUSB OTG driver peripheral defines
|
||||
*
|
||||
* Copyright 2005 Mentor Graphics Corporation
|
||||
* Copyright (C) 2005-2006 by Texas Instruments
|
||||
* Copyright (C) 2006-2007 Nokia Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED "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 AUTHORS 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __MUSB_GADGET_H
|
||||
#define __MUSB_GADGET_H
|
||||
|
||||
struct musb_request {
|
||||
struct usb_request request;
|
||||
struct musb_ep *ep;
|
||||
struct musb *musb;
|
||||
u8 tx; /* endpoint direction */
|
||||
u8 epnum;
|
||||
u8 mapped;
|
||||
};
|
||||
|
||||
static inline struct musb_request *to_musb_request(struct usb_request *req)
|
||||
{
|
||||
return req ? container_of(req, struct musb_request, request) : NULL;
|
||||
}
|
||||
|
||||
extern struct usb_request *
|
||||
musb_alloc_request(struct usb_ep *ep, gfp_t gfp_flags);
|
||||
extern void musb_free_request(struct usb_ep *ep, struct usb_request *req);
|
||||
|
||||
|
||||
/*
|
||||
* struct musb_ep - peripheral side view of endpoint rx or tx side
|
||||
*/
|
||||
struct musb_ep {
|
||||
/* stuff towards the head is basically write-once. */
|
||||
struct usb_ep end_point;
|
||||
char name[12];
|
||||
struct musb_hw_ep *hw_ep;
|
||||
struct musb *musb;
|
||||
u8 current_epnum;
|
||||
|
||||
/* ... when enabled/disabled ... */
|
||||
u8 type;
|
||||
u8 is_in;
|
||||
u16 packet_sz;
|
||||
const struct usb_endpoint_descriptor *desc;
|
||||
struct dma_channel *dma;
|
||||
|
||||
/* later things are modified based on usage */
|
||||
struct list_head req_list;
|
||||
|
||||
/* true if lock must be dropped but req_list may not be advanced */
|
||||
u8 busy;
|
||||
};
|
||||
|
||||
static inline struct musb_ep *to_musb_ep(struct usb_ep *ep)
|
||||
{
|
||||
return ep ? container_of(ep, struct musb_ep, end_point) : NULL;
|
||||
}
|
||||
|
||||
static inline struct usb_request *next_request(struct musb_ep *ep)
|
||||
{
|
||||
struct list_head *queue = &ep->req_list;
|
||||
|
||||
if (list_empty(queue))
|
||||
return NULL;
|
||||
return container_of(queue->next, struct usb_request, list);
|
||||
}
|
||||
|
||||
extern void musb_g_tx(struct musb *musb, u8 epnum);
|
||||
extern void musb_g_rx(struct musb *musb, u8 epnum);
|
||||
|
||||
extern const struct usb_ep_ops musb_g_ep0_ops;
|
||||
|
||||
extern int musb_gadget_setup(struct musb *);
|
||||
extern void musb_gadget_cleanup(struct musb *);
|
||||
|
||||
extern void musb_g_giveback(struct musb_ep *, struct usb_request *, int);
|
||||
|
||||
extern int musb_gadget_set_halt(struct usb_ep *ep, int value);
|
||||
|
||||
#endif /* __MUSB_GADGET_H */
|
981
drivers/usb/musb/musb_gadget_ep0.c
Normal file
981
drivers/usb/musb/musb_gadget_ep0.c
Normal file
@ -0,0 +1,981 @@
|
||||
/*
|
||||
* MUSB OTG peripheral driver ep0 handling
|
||||
*
|
||||
* Copyright 2005 Mentor Graphics Corporation
|
||||
* Copyright (C) 2005-2006 by Texas Instruments
|
||||
* Copyright (C) 2006-2007 Nokia Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED "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 AUTHORS 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/kernel.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#include "musb_core.h"
|
||||
|
||||
/* ep0 is always musb->endpoints[0].ep_in */
|
||||
#define next_ep0_request(musb) next_in_request(&(musb)->endpoints[0])
|
||||
|
||||
/*
|
||||
* locking note: we use only the controller lock, for simpler correctness.
|
||||
* It's always held with IRQs blocked.
|
||||
*
|
||||
* It protects the ep0 request queue as well as ep0_state, not just the
|
||||
* controller and indexed registers. And that lock stays held unless it
|
||||
* needs to be dropped to allow reentering this driver ... like upcalls to
|
||||
* the gadget driver, or adjusting endpoint halt status.
|
||||
*/
|
||||
|
||||
static char *decode_ep0stage(u8 stage)
|
||||
{
|
||||
switch (stage) {
|
||||
case MUSB_EP0_STAGE_SETUP: return "idle";
|
||||
case MUSB_EP0_STAGE_TX: return "in";
|
||||
case MUSB_EP0_STAGE_RX: return "out";
|
||||
case MUSB_EP0_STAGE_ACKWAIT: return "wait";
|
||||
case MUSB_EP0_STAGE_STATUSIN: return "in/status";
|
||||
case MUSB_EP0_STAGE_STATUSOUT: return "out/status";
|
||||
default: return "?";
|
||||
}
|
||||
}
|
||||
|
||||
/* handle a standard GET_STATUS request
|
||||
* Context: caller holds controller lock
|
||||
*/
|
||||
static int service_tx_status_request(
|
||||
struct musb *musb,
|
||||
const struct usb_ctrlrequest *ctrlrequest)
|
||||
{
|
||||
void __iomem *mbase = musb->mregs;
|
||||
int handled = 1;
|
||||
u8 result[2], epnum = 0;
|
||||
const u8 recip = ctrlrequest->bRequestType & USB_RECIP_MASK;
|
||||
|
||||
result[1] = 0;
|
||||
|
||||
switch (recip) {
|
||||
case USB_RECIP_DEVICE:
|
||||
result[0] = musb->is_self_powered << USB_DEVICE_SELF_POWERED;
|
||||
result[0] |= musb->may_wakeup << USB_DEVICE_REMOTE_WAKEUP;
|
||||
#ifdef CONFIG_USB_MUSB_OTG
|
||||
if (musb->g.is_otg) {
|
||||
result[0] |= musb->g.b_hnp_enable
|
||||
<< USB_DEVICE_B_HNP_ENABLE;
|
||||
result[0] |= musb->g.a_alt_hnp_support
|
||||
<< USB_DEVICE_A_ALT_HNP_SUPPORT;
|
||||
result[0] |= musb->g.a_hnp_support
|
||||
<< USB_DEVICE_A_HNP_SUPPORT;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
|
||||
case USB_RECIP_INTERFACE:
|
||||
result[0] = 0;
|
||||
break;
|
||||
|
||||
case USB_RECIP_ENDPOINT: {
|
||||
int is_in;
|
||||
struct musb_ep *ep;
|
||||
u16 tmp;
|
||||
void __iomem *regs;
|
||||
|
||||
epnum = (u8) ctrlrequest->wIndex;
|
||||
if (!epnum) {
|
||||
result[0] = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
is_in = epnum & USB_DIR_IN;
|
||||
if (is_in) {
|
||||
epnum &= 0x0f;
|
||||
ep = &musb->endpoints[epnum].ep_in;
|
||||
} else {
|
||||
ep = &musb->endpoints[epnum].ep_out;
|
||||
}
|
||||
regs = musb->endpoints[epnum].regs;
|
||||
|
||||
if (epnum >= MUSB_C_NUM_EPS || !ep->desc) {
|
||||
handled = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
musb_ep_select(mbase, epnum);
|
||||
if (is_in)
|
||||
tmp = musb_readw(regs, MUSB_TXCSR)
|
||||
& MUSB_TXCSR_P_SENDSTALL;
|
||||
else
|
||||
tmp = musb_readw(regs, MUSB_RXCSR)
|
||||
& MUSB_RXCSR_P_SENDSTALL;
|
||||
musb_ep_select(mbase, 0);
|
||||
|
||||
result[0] = tmp ? 1 : 0;
|
||||
} break;
|
||||
|
||||
default:
|
||||
/* class, vendor, etc ... delegate */
|
||||
handled = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
/* fill up the fifo; caller updates csr0 */
|
||||
if (handled > 0) {
|
||||
u16 len = le16_to_cpu(ctrlrequest->wLength);
|
||||
|
||||
if (len > 2)
|
||||
len = 2;
|
||||
musb_write_fifo(&musb->endpoints[0], len, result);
|
||||
}
|
||||
|
||||
return handled;
|
||||
}
|
||||
|
||||
/*
|
||||
* handle a control-IN request, the end0 buffer contains the current request
|
||||
* that is supposed to be a standard control request. Assumes the fifo to
|
||||
* be at least 2 bytes long.
|
||||
*
|
||||
* @return 0 if the request was NOT HANDLED,
|
||||
* < 0 when error
|
||||
* > 0 when the request is processed
|
||||
*
|
||||
* Context: caller holds controller lock
|
||||
*/
|
||||
static int
|
||||
service_in_request(struct musb *musb, const struct usb_ctrlrequest *ctrlrequest)
|
||||
{
|
||||
int handled = 0; /* not handled */
|
||||
|
||||
if ((ctrlrequest->bRequestType & USB_TYPE_MASK)
|
||||
== USB_TYPE_STANDARD) {
|
||||
switch (ctrlrequest->bRequest) {
|
||||
case USB_REQ_GET_STATUS:
|
||||
handled = service_tx_status_request(musb,
|
||||
ctrlrequest);
|
||||
break;
|
||||
|
||||
/* case USB_REQ_SYNC_FRAME: */
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return handled;
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: caller holds controller lock
|
||||
*/
|
||||
static void musb_g_ep0_giveback(struct musb *musb, struct usb_request *req)
|
||||
{
|
||||
musb_g_giveback(&musb->endpoints[0].ep_in, req, 0);
|
||||
musb->ep0_state = MUSB_EP0_STAGE_SETUP;
|
||||
}
|
||||
|
||||
/*
|
||||
* Tries to start B-device HNP negotiation if enabled via sysfs
|
||||
*/
|
||||
static inline void musb_try_b_hnp_enable(struct musb *musb)
|
||||
{
|
||||
void __iomem *mbase = musb->mregs;
|
||||
u8 devctl;
|
||||
|
||||
DBG(1, "HNP: Setting HR\n");
|
||||
devctl = musb_readb(mbase, MUSB_DEVCTL);
|
||||
musb_writeb(mbase, MUSB_DEVCTL, devctl | MUSB_DEVCTL_HR);
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle all control requests with no DATA stage, including standard
|
||||
* requests such as:
|
||||
* USB_REQ_SET_CONFIGURATION, USB_REQ_SET_INTERFACE, unrecognized
|
||||
* always delegated to the gadget driver
|
||||
* USB_REQ_SET_ADDRESS, USB_REQ_CLEAR_FEATURE, USB_REQ_SET_FEATURE
|
||||
* always handled here, except for class/vendor/... features
|
||||
*
|
||||
* Context: caller holds controller lock
|
||||
*/
|
||||
static int
|
||||
service_zero_data_request(struct musb *musb,
|
||||
struct usb_ctrlrequest *ctrlrequest)
|
||||
__releases(musb->lock)
|
||||
__acquires(musb->lock)
|
||||
{
|
||||
int handled = -EINVAL;
|
||||
void __iomem *mbase = musb->mregs;
|
||||
const u8 recip = ctrlrequest->bRequestType & USB_RECIP_MASK;
|
||||
|
||||
/* the gadget driver handles everything except what we MUST handle */
|
||||
if ((ctrlrequest->bRequestType & USB_TYPE_MASK)
|
||||
== USB_TYPE_STANDARD) {
|
||||
switch (ctrlrequest->bRequest) {
|
||||
case USB_REQ_SET_ADDRESS:
|
||||
/* change it after the status stage */
|
||||
musb->set_address = true;
|
||||
musb->address = (u8) (ctrlrequest->wValue & 0x7f);
|
||||
handled = 1;
|
||||
break;
|
||||
|
||||
case USB_REQ_CLEAR_FEATURE:
|
||||
switch (recip) {
|
||||
case USB_RECIP_DEVICE:
|
||||
if (ctrlrequest->wValue
|
||||
!= USB_DEVICE_REMOTE_WAKEUP)
|
||||
break;
|
||||
musb->may_wakeup = 0;
|
||||
handled = 1;
|
||||
break;
|
||||
case USB_RECIP_INTERFACE:
|
||||
break;
|
||||
case USB_RECIP_ENDPOINT:{
|
||||
const u8 num = ctrlrequest->wIndex & 0x0f;
|
||||
struct musb_ep *musb_ep;
|
||||
|
||||
if (num == 0
|
||||
|| num >= MUSB_C_NUM_EPS
|
||||
|| ctrlrequest->wValue
|
||||
!= USB_ENDPOINT_HALT)
|
||||
break;
|
||||
|
||||
if (ctrlrequest->wIndex & USB_DIR_IN)
|
||||
musb_ep = &musb->endpoints[num].ep_in;
|
||||
else
|
||||
musb_ep = &musb->endpoints[num].ep_out;
|
||||
if (!musb_ep->desc)
|
||||
break;
|
||||
|
||||
/* REVISIT do it directly, no locking games */
|
||||
spin_unlock(&musb->lock);
|
||||
musb_gadget_set_halt(&musb_ep->end_point, 0);
|
||||
spin_lock(&musb->lock);
|
||||
|
||||
/* select ep0 again */
|
||||
musb_ep_select(mbase, 0);
|
||||
handled = 1;
|
||||
} break;
|
||||
default:
|
||||
/* class, vendor, etc ... delegate */
|
||||
handled = 0;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case USB_REQ_SET_FEATURE:
|
||||
switch (recip) {
|
||||
case USB_RECIP_DEVICE:
|
||||
handled = 1;
|
||||
switch (ctrlrequest->wValue) {
|
||||
case USB_DEVICE_REMOTE_WAKEUP:
|
||||
musb->may_wakeup = 1;
|
||||
break;
|
||||
case USB_DEVICE_TEST_MODE:
|
||||
if (musb->g.speed != USB_SPEED_HIGH)
|
||||
goto stall;
|
||||
if (ctrlrequest->wIndex & 0xff)
|
||||
goto stall;
|
||||
|
||||
switch (ctrlrequest->wIndex >> 8) {
|
||||
case 1:
|
||||
pr_debug("TEST_J\n");
|
||||
/* TEST_J */
|
||||
musb->test_mode_nr =
|
||||
MUSB_TEST_J;
|
||||
break;
|
||||
case 2:
|
||||
/* TEST_K */
|
||||
pr_debug("TEST_K\n");
|
||||
musb->test_mode_nr =
|
||||
MUSB_TEST_K;
|
||||
break;
|
||||
case 3:
|
||||
/* TEST_SE0_NAK */
|
||||
pr_debug("TEST_SE0_NAK\n");
|
||||
musb->test_mode_nr =
|
||||
MUSB_TEST_SE0_NAK;
|
||||
break;
|
||||
case 4:
|
||||
/* TEST_PACKET */
|
||||
pr_debug("TEST_PACKET\n");
|
||||
musb->test_mode_nr =
|
||||
MUSB_TEST_PACKET;
|
||||
break;
|
||||
default:
|
||||
goto stall;
|
||||
}
|
||||
|
||||
/* enter test mode after irq */
|
||||
if (handled > 0)
|
||||
musb->test_mode = true;
|
||||
break;
|
||||
#ifdef CONFIG_USB_MUSB_OTG
|
||||
case USB_DEVICE_B_HNP_ENABLE:
|
||||
if (!musb->g.is_otg)
|
||||
goto stall;
|
||||
musb->g.b_hnp_enable = 1;
|
||||
musb_try_b_hnp_enable(musb);
|
||||
break;
|
||||
case USB_DEVICE_A_HNP_SUPPORT:
|
||||
if (!musb->g.is_otg)
|
||||
goto stall;
|
||||
musb->g.a_hnp_support = 1;
|
||||
break;
|
||||
case USB_DEVICE_A_ALT_HNP_SUPPORT:
|
||||
if (!musb->g.is_otg)
|
||||
goto stall;
|
||||
musb->g.a_alt_hnp_support = 1;
|
||||
break;
|
||||
#endif
|
||||
stall:
|
||||
default:
|
||||
handled = -EINVAL;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case USB_RECIP_INTERFACE:
|
||||
break;
|
||||
|
||||
case USB_RECIP_ENDPOINT:{
|
||||
const u8 epnum =
|
||||
ctrlrequest->wIndex & 0x0f;
|
||||
struct musb_ep *musb_ep;
|
||||
struct musb_hw_ep *ep;
|
||||
void __iomem *regs;
|
||||
int is_in;
|
||||
u16 csr;
|
||||
|
||||
if (epnum == 0
|
||||
|| epnum >= MUSB_C_NUM_EPS
|
||||
|| ctrlrequest->wValue
|
||||
!= USB_ENDPOINT_HALT)
|
||||
break;
|
||||
|
||||
ep = musb->endpoints + epnum;
|
||||
regs = ep->regs;
|
||||
is_in = ctrlrequest->wIndex & USB_DIR_IN;
|
||||
if (is_in)
|
||||
musb_ep = &ep->ep_in;
|
||||
else
|
||||
musb_ep = &ep->ep_out;
|
||||
if (!musb_ep->desc)
|
||||
break;
|
||||
|
||||
musb_ep_select(mbase, epnum);
|
||||
if (is_in) {
|
||||
csr = musb_readw(regs,
|
||||
MUSB_TXCSR);
|
||||
if (csr & MUSB_TXCSR_FIFONOTEMPTY)
|
||||
csr |= MUSB_TXCSR_FLUSHFIFO;
|
||||
csr |= MUSB_TXCSR_P_SENDSTALL
|
||||
| MUSB_TXCSR_CLRDATATOG
|
||||
| MUSB_TXCSR_P_WZC_BITS;
|
||||
musb_writew(regs, MUSB_TXCSR,
|
||||
csr);
|
||||
} else {
|
||||
csr = musb_readw(regs,
|
||||
MUSB_RXCSR);
|
||||
csr |= MUSB_RXCSR_P_SENDSTALL
|
||||
| MUSB_RXCSR_FLUSHFIFO
|
||||
| MUSB_RXCSR_CLRDATATOG
|
||||
| MUSB_TXCSR_P_WZC_BITS;
|
||||
musb_writew(regs, MUSB_RXCSR,
|
||||
csr);
|
||||
}
|
||||
|
||||
/* select ep0 again */
|
||||
musb_ep_select(mbase, 0);
|
||||
handled = 1;
|
||||
} break;
|
||||
|
||||
default:
|
||||
/* class, vendor, etc ... delegate */
|
||||
handled = 0;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* delegate SET_CONFIGURATION, etc */
|
||||
handled = 0;
|
||||
}
|
||||
} else
|
||||
handled = 0;
|
||||
return handled;
|
||||
}
|
||||
|
||||
/* we have an ep0out data packet
|
||||
* Context: caller holds controller lock
|
||||
*/
|
||||
static void ep0_rxstate(struct musb *musb)
|
||||
{
|
||||
void __iomem *regs = musb->control_ep->regs;
|
||||
struct usb_request *req;
|
||||
u16 tmp;
|
||||
|
||||
req = next_ep0_request(musb);
|
||||
|
||||
/* read packet and ack; or stall because of gadget driver bug:
|
||||
* should have provided the rx buffer before setup() returned.
|
||||
*/
|
||||
if (req) {
|
||||
void *buf = req->buf + req->actual;
|
||||
unsigned len = req->length - req->actual;
|
||||
|
||||
/* read the buffer */
|
||||
tmp = musb_readb(regs, MUSB_COUNT0);
|
||||
if (tmp > len) {
|
||||
req->status = -EOVERFLOW;
|
||||
tmp = len;
|
||||
}
|
||||
musb_read_fifo(&musb->endpoints[0], tmp, buf);
|
||||
req->actual += tmp;
|
||||
tmp = MUSB_CSR0_P_SVDRXPKTRDY;
|
||||
if (tmp < 64 || req->actual == req->length) {
|
||||
musb->ep0_state = MUSB_EP0_STAGE_STATUSIN;
|
||||
tmp |= MUSB_CSR0_P_DATAEND;
|
||||
} else
|
||||
req = NULL;
|
||||
} else
|
||||
tmp = MUSB_CSR0_P_SVDRXPKTRDY | MUSB_CSR0_P_SENDSTALL;
|
||||
|
||||
|
||||
/* Completion handler may choose to stall, e.g. because the
|
||||
* message just received holds invalid data.
|
||||
*/
|
||||
if (req) {
|
||||
musb->ackpend = tmp;
|
||||
musb_g_ep0_giveback(musb, req);
|
||||
if (!musb->ackpend)
|
||||
return;
|
||||
musb->ackpend = 0;
|
||||
}
|
||||
musb_writew(regs, MUSB_CSR0, tmp);
|
||||
}
|
||||
|
||||
/*
|
||||
* transmitting to the host (IN), this code might be called from IRQ
|
||||
* and from kernel thread.
|
||||
*
|
||||
* Context: caller holds controller lock
|
||||
*/
|
||||
static void ep0_txstate(struct musb *musb)
|
||||
{
|
||||
void __iomem *regs = musb->control_ep->regs;
|
||||
struct usb_request *request = next_ep0_request(musb);
|
||||
u16 csr = MUSB_CSR0_TXPKTRDY;
|
||||
u8 *fifo_src;
|
||||
u8 fifo_count;
|
||||
|
||||
if (!request) {
|
||||
/* WARN_ON(1); */
|
||||
DBG(2, "odd; csr0 %04x\n", musb_readw(regs, MUSB_CSR0));
|
||||
return;
|
||||
}
|
||||
|
||||
/* load the data */
|
||||
fifo_src = (u8 *) request->buf + request->actual;
|
||||
fifo_count = min((unsigned) MUSB_EP0_FIFOSIZE,
|
||||
request->length - request->actual);
|
||||
musb_write_fifo(&musb->endpoints[0], fifo_count, fifo_src);
|
||||
request->actual += fifo_count;
|
||||
|
||||
/* update the flags */
|
||||
if (fifo_count < MUSB_MAX_END0_PACKET
|
||||
|| request->actual == request->length) {
|
||||
musb->ep0_state = MUSB_EP0_STAGE_STATUSOUT;
|
||||
csr |= MUSB_CSR0_P_DATAEND;
|
||||
} else
|
||||
request = NULL;
|
||||
|
||||
/* report completions as soon as the fifo's loaded; there's no
|
||||
* win in waiting till this last packet gets acked. (other than
|
||||
* very precise fault reporting, needed by USB TMC; possible with
|
||||
* this hardware, but not usable from portable gadget drivers.)
|
||||
*/
|
||||
if (request) {
|
||||
musb->ackpend = csr;
|
||||
musb_g_ep0_giveback(musb, request);
|
||||
if (!musb->ackpend)
|
||||
return;
|
||||
musb->ackpend = 0;
|
||||
}
|
||||
|
||||
/* send it out, triggering a "txpktrdy cleared" irq */
|
||||
musb_writew(regs, MUSB_CSR0, csr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read a SETUP packet (struct usb_ctrlrequest) from the hardware.
|
||||
* Fields are left in USB byte-order.
|
||||
*
|
||||
* Context: caller holds controller lock.
|
||||
*/
|
||||
static void
|
||||
musb_read_setup(struct musb *musb, struct usb_ctrlrequest *req)
|
||||
{
|
||||
struct usb_request *r;
|
||||
void __iomem *regs = musb->control_ep->regs;
|
||||
|
||||
musb_read_fifo(&musb->endpoints[0], sizeof *req, (u8 *)req);
|
||||
|
||||
/* NOTE: earlier 2.6 versions changed setup packets to host
|
||||
* order, but now USB packets always stay in USB byte order.
|
||||
*/
|
||||
DBG(3, "SETUP req%02x.%02x v%04x i%04x l%d\n",
|
||||
req->bRequestType,
|
||||
req->bRequest,
|
||||
le16_to_cpu(req->wValue),
|
||||
le16_to_cpu(req->wIndex),
|
||||
le16_to_cpu(req->wLength));
|
||||
|
||||
/* clean up any leftover transfers */
|
||||
r = next_ep0_request(musb);
|
||||
if (r)
|
||||
musb_g_ep0_giveback(musb, r);
|
||||
|
||||
/* For zero-data requests we want to delay the STATUS stage to
|
||||
* avoid SETUPEND errors. If we read data (OUT), delay accepting
|
||||
* packets until there's a buffer to store them in.
|
||||
*
|
||||
* If we write data, the controller acts happier if we enable
|
||||
* the TX FIFO right away, and give the controller a moment
|
||||
* to switch modes...
|
||||
*/
|
||||
musb->set_address = false;
|
||||
musb->ackpend = MUSB_CSR0_P_SVDRXPKTRDY;
|
||||
if (req->wLength == 0) {
|
||||
if (req->bRequestType & USB_DIR_IN)
|
||||
musb->ackpend |= MUSB_CSR0_TXPKTRDY;
|
||||
musb->ep0_state = MUSB_EP0_STAGE_ACKWAIT;
|
||||
} else if (req->bRequestType & USB_DIR_IN) {
|
||||
musb->ep0_state = MUSB_EP0_STAGE_TX;
|
||||
musb_writew(regs, MUSB_CSR0, MUSB_CSR0_P_SVDRXPKTRDY);
|
||||
while ((musb_readw(regs, MUSB_CSR0)
|
||||
& MUSB_CSR0_RXPKTRDY) != 0)
|
||||
cpu_relax();
|
||||
musb->ackpend = 0;
|
||||
} else
|
||||
musb->ep0_state = MUSB_EP0_STAGE_RX;
|
||||
}
|
||||
|
||||
static int
|
||||
forward_to_driver(struct musb *musb, const struct usb_ctrlrequest *ctrlrequest)
|
||||
__releases(musb->lock)
|
||||
__acquires(musb->lock)
|
||||
{
|
||||
int retval;
|
||||
if (!musb->gadget_driver)
|
||||
return -EOPNOTSUPP;
|
||||
spin_unlock(&musb->lock);
|
||||
retval = musb->gadget_driver->setup(&musb->g, ctrlrequest);
|
||||
spin_lock(&musb->lock);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle peripheral ep0 interrupt
|
||||
*
|
||||
* Context: irq handler; we won't re-enter the driver that way.
|
||||
*/
|
||||
irqreturn_t musb_g_ep0_irq(struct musb *musb)
|
||||
{
|
||||
u16 csr;
|
||||
u16 len;
|
||||
void __iomem *mbase = musb->mregs;
|
||||
void __iomem *regs = musb->endpoints[0].regs;
|
||||
irqreturn_t retval = IRQ_NONE;
|
||||
|
||||
musb_ep_select(mbase, 0); /* select ep0 */
|
||||
csr = musb_readw(regs, MUSB_CSR0);
|
||||
len = musb_readb(regs, MUSB_COUNT0);
|
||||
|
||||
DBG(4, "csr %04x, count %d, myaddr %d, ep0stage %s\n",
|
||||
csr, len,
|
||||
musb_readb(mbase, MUSB_FADDR),
|
||||
decode_ep0stage(musb->ep0_state));
|
||||
|
||||
/* I sent a stall.. need to acknowledge it now.. */
|
||||
if (csr & MUSB_CSR0_P_SENTSTALL) {
|
||||
musb_writew(regs, MUSB_CSR0,
|
||||
csr & ~MUSB_CSR0_P_SENTSTALL);
|
||||
retval = IRQ_HANDLED;
|
||||
musb->ep0_state = MUSB_EP0_STAGE_SETUP;
|
||||
csr = musb_readw(regs, MUSB_CSR0);
|
||||
}
|
||||
|
||||
/* request ended "early" */
|
||||
if (csr & MUSB_CSR0_P_SETUPEND) {
|
||||
musb_writew(regs, MUSB_CSR0, MUSB_CSR0_P_SVDSETUPEND);
|
||||
retval = IRQ_HANDLED;
|
||||
musb->ep0_state = MUSB_EP0_STAGE_SETUP;
|
||||
csr = musb_readw(regs, MUSB_CSR0);
|
||||
/* NOTE: request may need completion */
|
||||
}
|
||||
|
||||
/* docs from Mentor only describe tx, rx, and idle/setup states.
|
||||
* we need to handle nuances around status stages, and also the
|
||||
* case where status and setup stages come back-to-back ...
|
||||
*/
|
||||
switch (musb->ep0_state) {
|
||||
|
||||
case MUSB_EP0_STAGE_TX:
|
||||
/* irq on clearing txpktrdy */
|
||||
if ((csr & MUSB_CSR0_TXPKTRDY) == 0) {
|
||||
ep0_txstate(musb);
|
||||
retval = IRQ_HANDLED;
|
||||
}
|
||||
break;
|
||||
|
||||
case MUSB_EP0_STAGE_RX:
|
||||
/* irq on set rxpktrdy */
|
||||
if (csr & MUSB_CSR0_RXPKTRDY) {
|
||||
ep0_rxstate(musb);
|
||||
retval = IRQ_HANDLED;
|
||||
}
|
||||
break;
|
||||
|
||||
case MUSB_EP0_STAGE_STATUSIN:
|
||||
/* end of sequence #2 (OUT/RX state) or #3 (no data) */
|
||||
|
||||
/* update address (if needed) only @ the end of the
|
||||
* status phase per usb spec, which also guarantees
|
||||
* we get 10 msec to receive this irq... until this
|
||||
* is done we won't see the next packet.
|
||||
*/
|
||||
if (musb->set_address) {
|
||||
musb->set_address = false;
|
||||
musb_writeb(mbase, MUSB_FADDR, musb->address);
|
||||
}
|
||||
|
||||
/* enter test mode if needed (exit by reset) */
|
||||
else if (musb->test_mode) {
|
||||
DBG(1, "entering TESTMODE\n");
|
||||
|
||||
if (MUSB_TEST_PACKET == musb->test_mode_nr)
|
||||
musb_load_testpacket(musb);
|
||||
|
||||
musb_writeb(mbase, MUSB_TESTMODE,
|
||||
musb->test_mode_nr);
|
||||
}
|
||||
/* FALLTHROUGH */
|
||||
|
||||
case MUSB_EP0_STAGE_STATUSOUT:
|
||||
/* end of sequence #1: write to host (TX state) */
|
||||
{
|
||||
struct usb_request *req;
|
||||
|
||||
req = next_ep0_request(musb);
|
||||
if (req)
|
||||
musb_g_ep0_giveback(musb, req);
|
||||
}
|
||||
retval = IRQ_HANDLED;
|
||||
musb->ep0_state = MUSB_EP0_STAGE_SETUP;
|
||||
/* FALLTHROUGH */
|
||||
|
||||
case MUSB_EP0_STAGE_SETUP:
|
||||
if (csr & MUSB_CSR0_RXPKTRDY) {
|
||||
struct usb_ctrlrequest setup;
|
||||
int handled = 0;
|
||||
|
||||
if (len != 8) {
|
||||
ERR("SETUP packet len %d != 8 ?\n", len);
|
||||
break;
|
||||
}
|
||||
musb_read_setup(musb, &setup);
|
||||
retval = IRQ_HANDLED;
|
||||
|
||||
/* sometimes the RESET won't be reported */
|
||||
if (unlikely(musb->g.speed == USB_SPEED_UNKNOWN)) {
|
||||
u8 power;
|
||||
|
||||
printk(KERN_NOTICE "%s: peripheral reset "
|
||||
"irq lost!\n",
|
||||
musb_driver_name);
|
||||
power = musb_readb(mbase, MUSB_POWER);
|
||||
musb->g.speed = (power & MUSB_POWER_HSMODE)
|
||||
? USB_SPEED_HIGH : USB_SPEED_FULL;
|
||||
|
||||
}
|
||||
|
||||
switch (musb->ep0_state) {
|
||||
|
||||
/* sequence #3 (no data stage), includes requests
|
||||
* we can't forward (notably SET_ADDRESS and the
|
||||
* device/endpoint feature set/clear operations)
|
||||
* plus SET_CONFIGURATION and others we must
|
||||
*/
|
||||
case MUSB_EP0_STAGE_ACKWAIT:
|
||||
handled = service_zero_data_request(
|
||||
musb, &setup);
|
||||
|
||||
/* status stage might be immediate */
|
||||
if (handled > 0) {
|
||||
musb->ackpend |= MUSB_CSR0_P_DATAEND;
|
||||
musb->ep0_state =
|
||||
MUSB_EP0_STAGE_STATUSIN;
|
||||
}
|
||||
break;
|
||||
|
||||
/* sequence #1 (IN to host), includes GET_STATUS
|
||||
* requests that we can't forward, GET_DESCRIPTOR
|
||||
* and others that we must
|
||||
*/
|
||||
case MUSB_EP0_STAGE_TX:
|
||||
handled = service_in_request(musb, &setup);
|
||||
if (handled > 0) {
|
||||
musb->ackpend = MUSB_CSR0_TXPKTRDY
|
||||
| MUSB_CSR0_P_DATAEND;
|
||||
musb->ep0_state =
|
||||
MUSB_EP0_STAGE_STATUSOUT;
|
||||
}
|
||||
break;
|
||||
|
||||
/* sequence #2 (OUT from host), always forward */
|
||||
default: /* MUSB_EP0_STAGE_RX */
|
||||
break;
|
||||
}
|
||||
|
||||
DBG(3, "handled %d, csr %04x, ep0stage %s\n",
|
||||
handled, csr,
|
||||
decode_ep0stage(musb->ep0_state));
|
||||
|
||||
/* unless we need to delegate this to the gadget
|
||||
* driver, we know how to wrap this up: csr0 has
|
||||
* not yet been written.
|
||||
*/
|
||||
if (handled < 0)
|
||||
goto stall;
|
||||
else if (handled > 0)
|
||||
goto finish;
|
||||
|
||||
handled = forward_to_driver(musb, &setup);
|
||||
if (handled < 0) {
|
||||
musb_ep_select(mbase, 0);
|
||||
stall:
|
||||
DBG(3, "stall (%d)\n", handled);
|
||||
musb->ackpend |= MUSB_CSR0_P_SENDSTALL;
|
||||
musb->ep0_state = MUSB_EP0_STAGE_SETUP;
|
||||
finish:
|
||||
musb_writew(regs, MUSB_CSR0,
|
||||
musb->ackpend);
|
||||
musb->ackpend = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case MUSB_EP0_STAGE_ACKWAIT:
|
||||
/* This should not happen. But happens with tusb6010 with
|
||||
* g_file_storage and high speed. Do nothing.
|
||||
*/
|
||||
retval = IRQ_HANDLED;
|
||||
break;
|
||||
|
||||
default:
|
||||
/* "can't happen" */
|
||||
WARN_ON(1);
|
||||
musb_writew(regs, MUSB_CSR0, MUSB_CSR0_P_SENDSTALL);
|
||||
musb->ep0_state = MUSB_EP0_STAGE_SETUP;
|
||||
break;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
musb_g_ep0_enable(struct usb_ep *ep, const struct usb_endpoint_descriptor *desc)
|
||||
{
|
||||
/* always enabled */
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int musb_g_ep0_disable(struct usb_ep *e)
|
||||
{
|
||||
/* always enabled */
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int
|
||||
musb_g_ep0_queue(struct usb_ep *e, struct usb_request *r, gfp_t gfp_flags)
|
||||
{
|
||||
struct musb_ep *ep;
|
||||
struct musb_request *req;
|
||||
struct musb *musb;
|
||||
int status;
|
||||
unsigned long lockflags;
|
||||
void __iomem *regs;
|
||||
|
||||
if (!e || !r)
|
||||
return -EINVAL;
|
||||
|
||||
ep = to_musb_ep(e);
|
||||
musb = ep->musb;
|
||||
regs = musb->control_ep->regs;
|
||||
|
||||
req = to_musb_request(r);
|
||||
req->musb = musb;
|
||||
req->request.actual = 0;
|
||||
req->request.status = -EINPROGRESS;
|
||||
req->tx = ep->is_in;
|
||||
|
||||
spin_lock_irqsave(&musb->lock, lockflags);
|
||||
|
||||
if (!list_empty(&ep->req_list)) {
|
||||
status = -EBUSY;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
switch (musb->ep0_state) {
|
||||
case MUSB_EP0_STAGE_RX: /* control-OUT data */
|
||||
case MUSB_EP0_STAGE_TX: /* control-IN data */
|
||||
case MUSB_EP0_STAGE_ACKWAIT: /* zero-length data */
|
||||
status = 0;
|
||||
break;
|
||||
default:
|
||||
DBG(1, "ep0 request queued in state %d\n",
|
||||
musb->ep0_state);
|
||||
status = -EINVAL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* add request to the list */
|
||||
list_add_tail(&(req->request.list), &(ep->req_list));
|
||||
|
||||
DBG(3, "queue to %s (%s), length=%d\n",
|
||||
ep->name, ep->is_in ? "IN/TX" : "OUT/RX",
|
||||
req->request.length);
|
||||
|
||||
musb_ep_select(musb->mregs, 0);
|
||||
|
||||
/* sequence #1, IN ... start writing the data */
|
||||
if (musb->ep0_state == MUSB_EP0_STAGE_TX)
|
||||
ep0_txstate(musb);
|
||||
|
||||
/* sequence #3, no-data ... issue IN status */
|
||||
else if (musb->ep0_state == MUSB_EP0_STAGE_ACKWAIT) {
|
||||
if (req->request.length)
|
||||
status = -EINVAL;
|
||||
else {
|
||||
musb->ep0_state = MUSB_EP0_STAGE_STATUSIN;
|
||||
musb_writew(regs, MUSB_CSR0,
|
||||
musb->ackpend | MUSB_CSR0_P_DATAEND);
|
||||
musb->ackpend = 0;
|
||||
musb_g_ep0_giveback(ep->musb, r);
|
||||
}
|
||||
|
||||
/* else for sequence #2 (OUT), caller provides a buffer
|
||||
* before the next packet arrives. deferred responses
|
||||
* (after SETUP is acked) are racey.
|
||||
*/
|
||||
} else if (musb->ackpend) {
|
||||
musb_writew(regs, MUSB_CSR0, musb->ackpend);
|
||||
musb->ackpend = 0;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
spin_unlock_irqrestore(&musb->lock, lockflags);
|
||||
return status;
|
||||
}
|
||||
|
||||
static int musb_g_ep0_dequeue(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
/* we just won't support this */
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int musb_g_ep0_halt(struct usb_ep *e, int value)
|
||||
{
|
||||
struct musb_ep *ep;
|
||||
struct musb *musb;
|
||||
void __iomem *base, *regs;
|
||||
unsigned long flags;
|
||||
int status;
|
||||
u16 csr;
|
||||
|
||||
if (!e || !value)
|
||||
return -EINVAL;
|
||||
|
||||
ep = to_musb_ep(e);
|
||||
musb = ep->musb;
|
||||
base = musb->mregs;
|
||||
regs = musb->control_ep->regs;
|
||||
status = 0;
|
||||
|
||||
spin_lock_irqsave(&musb->lock, flags);
|
||||
|
||||
if (!list_empty(&ep->req_list)) {
|
||||
status = -EBUSY;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
musb_ep_select(base, 0);
|
||||
csr = musb->ackpend;
|
||||
|
||||
switch (musb->ep0_state) {
|
||||
|
||||
/* Stalls are usually issued after parsing SETUP packet, either
|
||||
* directly in irq context from setup() or else later.
|
||||
*/
|
||||
case MUSB_EP0_STAGE_TX: /* control-IN data */
|
||||
case MUSB_EP0_STAGE_ACKWAIT: /* STALL for zero-length data */
|
||||
case MUSB_EP0_STAGE_RX: /* control-OUT data */
|
||||
csr = musb_readw(regs, MUSB_CSR0);
|
||||
/* FALLTHROUGH */
|
||||
|
||||
/* It's also OK to issue stalls during callbacks when a non-empty
|
||||
* DATA stage buffer has been read (or even written).
|
||||
*/
|
||||
case MUSB_EP0_STAGE_STATUSIN: /* control-OUT status */
|
||||
case MUSB_EP0_STAGE_STATUSOUT: /* control-IN status */
|
||||
|
||||
csr |= MUSB_CSR0_P_SENDSTALL;
|
||||
musb_writew(regs, MUSB_CSR0, csr);
|
||||
musb->ep0_state = MUSB_EP0_STAGE_SETUP;
|
||||
musb->ackpend = 0;
|
||||
break;
|
||||
default:
|
||||
DBG(1, "ep0 can't halt in state %d\n", musb->ep0_state);
|
||||
status = -EINVAL;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
spin_unlock_irqrestore(&musb->lock, flags);
|
||||
return status;
|
||||
}
|
||||
|
||||
const struct usb_ep_ops musb_g_ep0_ops = {
|
||||
.enable = musb_g_ep0_enable,
|
||||
.disable = musb_g_ep0_disable,
|
||||
.alloc_request = musb_alloc_request,
|
||||
.free_request = musb_free_request,
|
||||
.queue = musb_g_ep0_queue,
|
||||
.dequeue = musb_g_ep0_dequeue,
|
||||
.set_halt = musb_g_ep0_halt,
|
||||
};
|
2170
drivers/usb/musb/musb_host.c
Normal file
2170
drivers/usb/musb/musb_host.c
Normal file
File diff suppressed because it is too large
Load Diff
110
drivers/usb/musb/musb_host.h
Normal file
110
drivers/usb/musb/musb_host.h
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* MUSB OTG driver host defines
|
||||
*
|
||||
* Copyright 2005 Mentor Graphics Corporation
|
||||
* Copyright (C) 2005-2006 by Texas Instruments
|
||||
* Copyright (C) 2006-2007 Nokia Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED "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 AUTHORS 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _MUSB_HOST_H
|
||||
#define _MUSB_HOST_H
|
||||
|
||||
static inline struct usb_hcd *musb_to_hcd(struct musb *musb)
|
||||
{
|
||||
return container_of((void *) musb, struct usb_hcd, hcd_priv);
|
||||
}
|
||||
|
||||
static inline struct musb *hcd_to_musb(struct usb_hcd *hcd)
|
||||
{
|
||||
return (struct musb *) (hcd->hcd_priv);
|
||||
}
|
||||
|
||||
/* stored in "usb_host_endpoint.hcpriv" for scheduled endpoints */
|
||||
struct musb_qh {
|
||||
struct usb_host_endpoint *hep; /* usbcore info */
|
||||
struct usb_device *dev;
|
||||
struct musb_hw_ep *hw_ep; /* current binding */
|
||||
|
||||
struct list_head ring; /* of musb_qh */
|
||||
/* struct musb_qh *next; */ /* for periodic tree */
|
||||
|
||||
unsigned offset; /* in urb->transfer_buffer */
|
||||
unsigned segsize; /* current xfer fragment */
|
||||
|
||||
u8 type_reg; /* {rx,tx} type register */
|
||||
u8 intv_reg; /* {rx,tx} interval register */
|
||||
u8 addr_reg; /* device address register */
|
||||
u8 h_addr_reg; /* hub address register */
|
||||
u8 h_port_reg; /* hub port register */
|
||||
|
||||
u8 is_ready; /* safe to modify hw_ep */
|
||||
u8 type; /* XFERTYPE_* */
|
||||
u8 epnum;
|
||||
u16 maxpacket;
|
||||
u16 frame; /* for periodic schedule */
|
||||
unsigned iso_idx; /* in urb->iso_frame_desc[] */
|
||||
};
|
||||
|
||||
/* map from control or bulk queue head to the first qh on that ring */
|
||||
static inline struct musb_qh *first_qh(struct list_head *q)
|
||||
{
|
||||
if (list_empty(q))
|
||||
return NULL;
|
||||
return list_entry(q->next, struct musb_qh, ring);
|
||||
}
|
||||
|
||||
|
||||
extern void musb_root_disconnect(struct musb *musb);
|
||||
|
||||
struct usb_hcd;
|
||||
|
||||
extern int musb_hub_status_data(struct usb_hcd *hcd, char *buf);
|
||||
extern int musb_hub_control(struct usb_hcd *hcd,
|
||||
u16 typeReq, u16 wValue, u16 wIndex,
|
||||
char *buf, u16 wLength);
|
||||
|
||||
extern const struct hc_driver musb_hc_driver;
|
||||
|
||||
static inline struct urb *next_urb(struct musb_qh *qh)
|
||||
{
|
||||
#ifdef CONFIG_USB_MUSB_HDRC_HCD
|
||||
struct list_head *queue;
|
||||
|
||||
if (!qh)
|
||||
return NULL;
|
||||
queue = &qh->hep->urb_list;
|
||||
if (list_empty(queue))
|
||||
return NULL;
|
||||
return list_entry(queue->next, struct urb, urb_list);
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* _MUSB_HOST_H */
|
115
drivers/usb/musb/musb_io.h
Normal file
115
drivers/usb/musb/musb_io.h
Normal file
@ -0,0 +1,115 @@
|
||||
/*
|
||||
* MUSB OTG driver register I/O
|
||||
*
|
||||
* Copyright 2005 Mentor Graphics Corporation
|
||||
* Copyright (C) 2005-2006 by Texas Instruments
|
||||
* Copyright (C) 2006-2007 Nokia Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED "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 AUTHORS 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __MUSB_LINUX_PLATFORM_ARCH_H__
|
||||
#define __MUSB_LINUX_PLATFORM_ARCH_H__
|
||||
|
||||
#include <linux/io.h>
|
||||
|
||||
#ifndef CONFIG_ARM
|
||||
static inline void readsl(const void __iomem *addr, void *buf, int len)
|
||||
{ insl((unsigned long)addr, buf, len); }
|
||||
static inline void readsw(const void __iomem *addr, void *buf, int len)
|
||||
{ insw((unsigned long)addr, buf, len); }
|
||||
static inline void readsb(const void __iomem *addr, void *buf, int len)
|
||||
{ insb((unsigned long)addr, buf, len); }
|
||||
|
||||
static inline void writesl(const void __iomem *addr, const void *buf, int len)
|
||||
{ outsl((unsigned long)addr, buf, len); }
|
||||
static inline void writesw(const void __iomem *addr, const void *buf, int len)
|
||||
{ outsw((unsigned long)addr, buf, len); }
|
||||
static inline void writesb(const void __iomem *addr, const void *buf, int len)
|
||||
{ outsb((unsigned long)addr, buf, len); }
|
||||
|
||||
#endif
|
||||
|
||||
/* NOTE: these offsets are all in bytes */
|
||||
|
||||
static inline u16 musb_readw(const void __iomem *addr, unsigned offset)
|
||||
{ return __raw_readw(addr + offset); }
|
||||
|
||||
static inline u32 musb_readl(const void __iomem *addr, unsigned offset)
|
||||
{ return __raw_readl(addr + offset); }
|
||||
|
||||
|
||||
static inline void musb_writew(void __iomem *addr, unsigned offset, u16 data)
|
||||
{ __raw_writew(data, addr + offset); }
|
||||
|
||||
static inline void musb_writel(void __iomem *addr, unsigned offset, u32 data)
|
||||
{ __raw_writel(data, addr + offset); }
|
||||
|
||||
|
||||
#ifdef CONFIG_USB_TUSB6010
|
||||
|
||||
/*
|
||||
* TUSB6010 doesn't allow 8-bit access; 16-bit access is the minimum.
|
||||
*/
|
||||
static inline u8 musb_readb(const void __iomem *addr, unsigned offset)
|
||||
{
|
||||
u16 tmp;
|
||||
u8 val;
|
||||
|
||||
tmp = __raw_readw(addr + (offset & ~1));
|
||||
if (offset & 1)
|
||||
val = (tmp >> 8);
|
||||
else
|
||||
val = tmp & 0xff;
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline void musb_writeb(void __iomem *addr, unsigned offset, u8 data)
|
||||
{
|
||||
u16 tmp;
|
||||
|
||||
tmp = __raw_readw(addr + (offset & ~1));
|
||||
if (offset & 1)
|
||||
tmp = (data << 8) | (tmp & 0xff);
|
||||
else
|
||||
tmp = (tmp & 0xff00) | data;
|
||||
|
||||
__raw_writew(tmp, addr + (offset & ~1));
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline u8 musb_readb(const void __iomem *addr, unsigned offset)
|
||||
{ return __raw_readb(addr + offset); }
|
||||
|
||||
static inline void musb_writeb(void __iomem *addr, unsigned offset, u8 data)
|
||||
{ __raw_writeb(data, addr + offset); }
|
||||
|
||||
#endif /* CONFIG_USB_TUSB6010 */
|
||||
|
||||
#endif
|
830
drivers/usb/musb/musb_procfs.c
Normal file
830
drivers/usb/musb/musb_procfs.c
Normal file
@ -0,0 +1,830 @@
|
||||
/*
|
||||
* MUSB OTG driver debug support
|
||||
*
|
||||
* Copyright 2005 Mentor Graphics Corporation
|
||||
* Copyright (C) 2005-2006 by Texas Instruments
|
||||
* Copyright (C) 2006-2007 Nokia Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED "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 AUTHORS 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/kernel.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/uaccess.h> /* FIXME remove procfs writes */
|
||||
#include <asm/arch/hardware.h>
|
||||
|
||||
#include "musb_core.h"
|
||||
|
||||
#include "davinci.h"
|
||||
|
||||
#ifdef CONFIG_USB_MUSB_HDRC_HCD
|
||||
|
||||
static int dump_qh(struct musb_qh *qh, char *buf, unsigned max)
|
||||
{
|
||||
int count;
|
||||
int tmp;
|
||||
struct usb_host_endpoint *hep = qh->hep;
|
||||
struct urb *urb;
|
||||
|
||||
count = snprintf(buf, max, " qh %p dev%d ep%d%s max%d\n",
|
||||
qh, qh->dev->devnum, qh->epnum,
|
||||
({ char *s; switch (qh->type) {
|
||||
case USB_ENDPOINT_XFER_BULK:
|
||||
s = "-bulk"; break;
|
||||
case USB_ENDPOINT_XFER_INT:
|
||||
s = "-int"; break;
|
||||
case USB_ENDPOINT_XFER_CONTROL:
|
||||
s = ""; break;
|
||||
default:
|
||||
s = "iso"; break;
|
||||
}; s; }),
|
||||
qh->maxpacket);
|
||||
if (count <= 0)
|
||||
return 0;
|
||||
buf += count;
|
||||
max -= count;
|
||||
|
||||
list_for_each_entry(urb, &hep->urb_list, urb_list) {
|
||||
tmp = snprintf(buf, max, "\t%s urb %p %d/%d\n",
|
||||
usb_pipein(urb->pipe) ? "in" : "out",
|
||||
urb, urb->actual_length,
|
||||
urb->transfer_buffer_length);
|
||||
if (tmp <= 0)
|
||||
break;
|
||||
tmp = min(tmp, (int)max);
|
||||
count += tmp;
|
||||
buf += tmp;
|
||||
max -= tmp;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static int
|
||||
dump_queue(struct list_head *q, char *buf, unsigned max)
|
||||
{
|
||||
int count = 0;
|
||||
struct musb_qh *qh;
|
||||
|
||||
list_for_each_entry(qh, q, ring) {
|
||||
int tmp;
|
||||
|
||||
tmp = dump_qh(qh, buf, max);
|
||||
if (tmp <= 0)
|
||||
break;
|
||||
tmp = min(tmp, (int)max);
|
||||
count += tmp;
|
||||
buf += tmp;
|
||||
max -= tmp;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
#endif /* HCD */
|
||||
|
||||
#ifdef CONFIG_USB_GADGET_MUSB_HDRC
|
||||
static int dump_ep(struct musb_ep *ep, char *buffer, unsigned max)
|
||||
{
|
||||
char *buf = buffer;
|
||||
int code = 0;
|
||||
void __iomem *regs = ep->hw_ep->regs;
|
||||
char *mode = "1buf";
|
||||
|
||||
if (ep->is_in) {
|
||||
if (ep->hw_ep->tx_double_buffered)
|
||||
mode = "2buf";
|
||||
} else {
|
||||
if (ep->hw_ep->rx_double_buffered)
|
||||
mode = "2buf";
|
||||
}
|
||||
|
||||
do {
|
||||
struct usb_request *req;
|
||||
|
||||
code = snprintf(buf, max,
|
||||
"\n%s (hw%d): %s%s, csr %04x maxp %04x\n",
|
||||
ep->name, ep->current_epnum,
|
||||
mode, ep->dma ? " dma" : "",
|
||||
musb_readw(regs,
|
||||
(ep->is_in || !ep->current_epnum)
|
||||
? MUSB_TXCSR
|
||||
: MUSB_RXCSR),
|
||||
musb_readw(regs, ep->is_in
|
||||
? MUSB_TXMAXP
|
||||
: MUSB_RXMAXP)
|
||||
);
|
||||
if (code <= 0)
|
||||
break;
|
||||
code = min(code, (int) max);
|
||||
buf += code;
|
||||
max -= code;
|
||||
|
||||
if (is_cppi_enabled() && ep->current_epnum) {
|
||||
unsigned cppi = ep->current_epnum - 1;
|
||||
void __iomem *base = ep->musb->ctrl_base;
|
||||
unsigned off1 = cppi << 2;
|
||||
void __iomem *ram = base;
|
||||
char tmp[16];
|
||||
|
||||
if (ep->is_in) {
|
||||
ram += DAVINCI_TXCPPI_STATERAM_OFFSET(cppi);
|
||||
tmp[0] = 0;
|
||||
} else {
|
||||
ram += DAVINCI_RXCPPI_STATERAM_OFFSET(cppi);
|
||||
snprintf(tmp, sizeof tmp, "%d left, ",
|
||||
musb_readl(base,
|
||||
DAVINCI_RXCPPI_BUFCNT0_REG + off1));
|
||||
}
|
||||
|
||||
code = snprintf(buf, max, "%cX DMA%d: %s"
|
||||
"%08x %08x, %08x %08x; "
|
||||
"%08x %08x %08x .. %08x\n",
|
||||
ep->is_in ? 'T' : 'R',
|
||||
ep->current_epnum - 1, tmp,
|
||||
musb_readl(ram, 0 * 4),
|
||||
musb_readl(ram, 1 * 4),
|
||||
musb_readl(ram, 2 * 4),
|
||||
musb_readl(ram, 3 * 4),
|
||||
musb_readl(ram, 4 * 4),
|
||||
musb_readl(ram, 5 * 4),
|
||||
musb_readl(ram, 6 * 4),
|
||||
musb_readl(ram, 7 * 4));
|
||||
if (code <= 0)
|
||||
break;
|
||||
code = min(code, (int) max);
|
||||
buf += code;
|
||||
max -= code;
|
||||
}
|
||||
|
||||
if (list_empty(&ep->req_list)) {
|
||||
code = snprintf(buf, max, "\t(queue empty)\n");
|
||||
if (code <= 0)
|
||||
break;
|
||||
code = min(code, (int) max);
|
||||
buf += code;
|
||||
max -= code;
|
||||
break;
|
||||
}
|
||||
list_for_each_entry(req, &ep->req_list, list) {
|
||||
code = snprintf(buf, max, "\treq %p, %s%s%d/%d\n",
|
||||
req,
|
||||
req->zero ? "zero, " : "",
|
||||
req->short_not_ok ? "!short, " : "",
|
||||
req->actual, req->length);
|
||||
if (code <= 0)
|
||||
break;
|
||||
code = min(code, (int) max);
|
||||
buf += code;
|
||||
max -= code;
|
||||
}
|
||||
} while (0);
|
||||
return buf - buffer;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int
|
||||
dump_end_info(struct musb *musb, u8 epnum, char *aBuffer, unsigned max)
|
||||
{
|
||||
int code = 0;
|
||||
char *buf = aBuffer;
|
||||
struct musb_hw_ep *hw_ep = &musb->endpoints[epnum];
|
||||
|
||||
do {
|
||||
musb_ep_select(musb->mregs, epnum);
|
||||
#ifdef CONFIG_USB_MUSB_HDRC_HCD
|
||||
if (is_host_active(musb)) {
|
||||
int dump_rx, dump_tx;
|
||||
void __iomem *regs = hw_ep->regs;
|
||||
|
||||
/* TEMPORARY (!) until we have a real periodic
|
||||
* schedule tree ...
|
||||
*/
|
||||
if (!epnum) {
|
||||
/* control is shared, uses RX queue
|
||||
* but (mostly) shadowed tx registers
|
||||
*/
|
||||
dump_tx = !list_empty(&musb->control);
|
||||
dump_rx = 0;
|
||||
} else if (hw_ep == musb->bulk_ep) {
|
||||
dump_tx = !list_empty(&musb->out_bulk);
|
||||
dump_rx = !list_empty(&musb->in_bulk);
|
||||
} else if (musb->periodic[epnum]) {
|
||||
struct usb_host_endpoint *hep;
|
||||
|
||||
hep = musb->periodic[epnum]->hep;
|
||||
dump_rx = hep->desc.bEndpointAddress
|
||||
& USB_ENDPOINT_DIR_MASK;
|
||||
dump_tx = !dump_rx;
|
||||
} else
|
||||
break;
|
||||
/* END TEMPORARY */
|
||||
|
||||
|
||||
if (dump_rx) {
|
||||
code = snprintf(buf, max,
|
||||
"\nRX%d: %s rxcsr %04x interval %02x "
|
||||
"max %04x type %02x; "
|
||||
"dev %d hub %d port %d"
|
||||
"\n",
|
||||
epnum,
|
||||
hw_ep->rx_double_buffered
|
||||
? "2buf" : "1buf",
|
||||
musb_readw(regs, MUSB_RXCSR),
|
||||
musb_readb(regs, MUSB_RXINTERVAL),
|
||||
musb_readw(regs, MUSB_RXMAXP),
|
||||
musb_readb(regs, MUSB_RXTYPE),
|
||||
/* FIXME: assumes multipoint */
|
||||
musb_readb(musb->mregs,
|
||||
MUSB_BUSCTL_OFFSET(epnum,
|
||||
MUSB_RXFUNCADDR)),
|
||||
musb_readb(musb->mregs,
|
||||
MUSB_BUSCTL_OFFSET(epnum,
|
||||
MUSB_RXHUBADDR)),
|
||||
musb_readb(musb->mregs,
|
||||
MUSB_BUSCTL_OFFSET(epnum,
|
||||
MUSB_RXHUBPORT))
|
||||
);
|
||||
if (code <= 0)
|
||||
break;
|
||||
code = min(code, (int) max);
|
||||
buf += code;
|
||||
max -= code;
|
||||
|
||||
if (is_cppi_enabled()
|
||||
&& epnum
|
||||
&& hw_ep->rx_channel) {
|
||||
unsigned cppi = epnum - 1;
|
||||
unsigned off1 = cppi << 2;
|
||||
void __iomem *base;
|
||||
void __iomem *ram;
|
||||
char tmp[16];
|
||||
|
||||
base = musb->ctrl_base;
|
||||
ram = DAVINCI_RXCPPI_STATERAM_OFFSET(
|
||||
cppi) + base;
|
||||
snprintf(tmp, sizeof tmp, "%d left, ",
|
||||
musb_readl(base,
|
||||
DAVINCI_RXCPPI_BUFCNT0_REG
|
||||
+ off1));
|
||||
|
||||
code = snprintf(buf, max,
|
||||
" rx dma%d: %s"
|
||||
"%08x %08x, %08x %08x; "
|
||||
"%08x %08x %08x .. %08x\n",
|
||||
cppi, tmp,
|
||||
musb_readl(ram, 0 * 4),
|
||||
musb_readl(ram, 1 * 4),
|
||||
musb_readl(ram, 2 * 4),
|
||||
musb_readl(ram, 3 * 4),
|
||||
musb_readl(ram, 4 * 4),
|
||||
musb_readl(ram, 5 * 4),
|
||||
musb_readl(ram, 6 * 4),
|
||||
musb_readl(ram, 7 * 4));
|
||||
if (code <= 0)
|
||||
break;
|
||||
code = min(code, (int) max);
|
||||
buf += code;
|
||||
max -= code;
|
||||
}
|
||||
|
||||
if (hw_ep == musb->bulk_ep
|
||||
&& !list_empty(
|
||||
&musb->in_bulk)) {
|
||||
code = dump_queue(&musb->in_bulk,
|
||||
buf, max);
|
||||
if (code <= 0)
|
||||
break;
|
||||
code = min(code, (int) max);
|
||||
buf += code;
|
||||
max -= code;
|
||||
} else if (musb->periodic[epnum]) {
|
||||
code = dump_qh(musb->periodic[epnum],
|
||||
buf, max);
|
||||
if (code <= 0)
|
||||
break;
|
||||
code = min(code, (int) max);
|
||||
buf += code;
|
||||
max -= code;
|
||||
}
|
||||
}
|
||||
|
||||
if (dump_tx) {
|
||||
code = snprintf(buf, max,
|
||||
"\nTX%d: %s txcsr %04x interval %02x "
|
||||
"max %04x type %02x; "
|
||||
"dev %d hub %d port %d"
|
||||
"\n",
|
||||
epnum,
|
||||
hw_ep->tx_double_buffered
|
||||
? "2buf" : "1buf",
|
||||
musb_readw(regs, MUSB_TXCSR),
|
||||
musb_readb(regs, MUSB_TXINTERVAL),
|
||||
musb_readw(regs, MUSB_TXMAXP),
|
||||
musb_readb(regs, MUSB_TXTYPE),
|
||||
/* FIXME: assumes multipoint */
|
||||
musb_readb(musb->mregs,
|
||||
MUSB_BUSCTL_OFFSET(epnum,
|
||||
MUSB_TXFUNCADDR)),
|
||||
musb_readb(musb->mregs,
|
||||
MUSB_BUSCTL_OFFSET(epnum,
|
||||
MUSB_TXHUBADDR)),
|
||||
musb_readb(musb->mregs,
|
||||
MUSB_BUSCTL_OFFSET(epnum,
|
||||
MUSB_TXHUBPORT))
|
||||
);
|
||||
if (code <= 0)
|
||||
break;
|
||||
code = min(code, (int) max);
|
||||
buf += code;
|
||||
max -= code;
|
||||
|
||||
if (is_cppi_enabled()
|
||||
&& epnum
|
||||
&& hw_ep->tx_channel) {
|
||||
unsigned cppi = epnum - 1;
|
||||
void __iomem *base;
|
||||
void __iomem *ram;
|
||||
|
||||
base = musb->ctrl_base;
|
||||
ram = DAVINCI_RXCPPI_STATERAM_OFFSET(
|
||||
cppi) + base;
|
||||
code = snprintf(buf, max,
|
||||
" tx dma%d: "
|
||||
"%08x %08x, %08x %08x; "
|
||||
"%08x %08x %08x .. %08x\n",
|
||||
cppi,
|
||||
musb_readl(ram, 0 * 4),
|
||||
musb_readl(ram, 1 * 4),
|
||||
musb_readl(ram, 2 * 4),
|
||||
musb_readl(ram, 3 * 4),
|
||||
musb_readl(ram, 4 * 4),
|
||||
musb_readl(ram, 5 * 4),
|
||||
musb_readl(ram, 6 * 4),
|
||||
musb_readl(ram, 7 * 4));
|
||||
if (code <= 0)
|
||||
break;
|
||||
code = min(code, (int) max);
|
||||
buf += code;
|
||||
max -= code;
|
||||
}
|
||||
|
||||
if (hw_ep == musb->control_ep
|
||||
&& !list_empty(
|
||||
&musb->control)) {
|
||||
code = dump_queue(&musb->control,
|
||||
buf, max);
|
||||
if (code <= 0)
|
||||
break;
|
||||
code = min(code, (int) max);
|
||||
buf += code;
|
||||
max -= code;
|
||||
} else if (hw_ep == musb->bulk_ep
|
||||
&& !list_empty(
|
||||
&musb->out_bulk)) {
|
||||
code = dump_queue(&musb->out_bulk,
|
||||
buf, max);
|
||||
if (code <= 0)
|
||||
break;
|
||||
code = min(code, (int) max);
|
||||
buf += code;
|
||||
max -= code;
|
||||
} else if (musb->periodic[epnum]) {
|
||||
code = dump_qh(musb->periodic[epnum],
|
||||
buf, max);
|
||||
if (code <= 0)
|
||||
break;
|
||||
code = min(code, (int) max);
|
||||
buf += code;
|
||||
max -= code;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef CONFIG_USB_GADGET_MUSB_HDRC
|
||||
if (is_peripheral_active(musb)) {
|
||||
code = 0;
|
||||
|
||||
if (hw_ep->ep_in.desc || !epnum) {
|
||||
code = dump_ep(&hw_ep->ep_in, buf, max);
|
||||
if (code <= 0)
|
||||
break;
|
||||
code = min(code, (int) max);
|
||||
buf += code;
|
||||
max -= code;
|
||||
}
|
||||
if (hw_ep->ep_out.desc) {
|
||||
code = dump_ep(&hw_ep->ep_out, buf, max);
|
||||
if (code <= 0)
|
||||
break;
|
||||
code = min(code, (int) max);
|
||||
buf += code;
|
||||
max -= code;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
} while (0);
|
||||
|
||||
return buf - aBuffer;
|
||||
}
|
||||
|
||||
/* Dump the current status and compile options.
|
||||
* @param musb the device driver instance
|
||||
* @param buffer where to dump the status; it must be big enough to hold the
|
||||
* result otherwise "BAD THINGS HAPPENS(TM)".
|
||||
*/
|
||||
static int dump_header_stats(struct musb *musb, char *buffer)
|
||||
{
|
||||
int code, count = 0;
|
||||
const void __iomem *mbase = musb->mregs;
|
||||
|
||||
*buffer = 0;
|
||||
count = sprintf(buffer, "Status: %sHDRC, Mode=%s "
|
||||
"(Power=%02x, DevCtl=%02x)\n",
|
||||
(musb->is_multipoint ? "M" : ""), MUSB_MODE(musb),
|
||||
musb_readb(mbase, MUSB_POWER),
|
||||
musb_readb(mbase, MUSB_DEVCTL));
|
||||
if (count <= 0)
|
||||
return 0;
|
||||
buffer += count;
|
||||
|
||||
code = sprintf(buffer, "OTG state: %s; %sactive\n",
|
||||
otg_state_string(musb),
|
||||
musb->is_active ? "" : "in");
|
||||
if (code <= 0)
|
||||
goto done;
|
||||
buffer += code;
|
||||
count += code;
|
||||
|
||||
code = sprintf(buffer,
|
||||
"Options: "
|
||||
#ifdef CONFIG_MUSB_PIO_ONLY
|
||||
"pio"
|
||||
#elif defined(CONFIG_USB_TI_CPPI_DMA)
|
||||
"cppi-dma"
|
||||
#elif defined(CONFIG_USB_INVENTRA_DMA)
|
||||
"musb-dma"
|
||||
#elif defined(CONFIG_USB_TUSB_OMAP_DMA)
|
||||
"tusb-omap-dma"
|
||||
#else
|
||||
"?dma?"
|
||||
#endif
|
||||
", "
|
||||
#ifdef CONFIG_USB_MUSB_OTG
|
||||
"otg (peripheral+host)"
|
||||
#elif defined(CONFIG_USB_GADGET_MUSB_HDRC)
|
||||
"peripheral"
|
||||
#elif defined(CONFIG_USB_MUSB_HDRC_HCD)
|
||||
"host"
|
||||
#endif
|
||||
", debug=%d [eps=%d]\n",
|
||||
debug,
|
||||
musb->nr_endpoints);
|
||||
if (code <= 0)
|
||||
goto done;
|
||||
count += code;
|
||||
buffer += code;
|
||||
|
||||
#ifdef CONFIG_USB_GADGET_MUSB_HDRC
|
||||
code = sprintf(buffer, "Peripheral address: %02x\n",
|
||||
musb_readb(musb->ctrl_base, MUSB_FADDR));
|
||||
if (code <= 0)
|
||||
goto done;
|
||||
buffer += code;
|
||||
count += code;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_MUSB_HDRC_HCD
|
||||
code = sprintf(buffer, "Root port status: %08x\n",
|
||||
musb->port1_status);
|
||||
if (code <= 0)
|
||||
goto done;
|
||||
buffer += code;
|
||||
count += code;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ARCH_DAVINCI
|
||||
code = sprintf(buffer,
|
||||
"DaVinci: ctrl=%02x stat=%1x phy=%03x\n"
|
||||
"\trndis=%05x auto=%04x intsrc=%08x intmsk=%08x"
|
||||
"\n",
|
||||
musb_readl(musb->ctrl_base, DAVINCI_USB_CTRL_REG),
|
||||
musb_readl(musb->ctrl_base, DAVINCI_USB_STAT_REG),
|
||||
__raw_readl((void __force __iomem *)
|
||||
IO_ADDRESS(USBPHY_CTL_PADDR)),
|
||||
musb_readl(musb->ctrl_base, DAVINCI_RNDIS_REG),
|
||||
musb_readl(musb->ctrl_base, DAVINCI_AUTOREQ_REG),
|
||||
musb_readl(musb->ctrl_base,
|
||||
DAVINCI_USB_INT_SOURCE_REG),
|
||||
musb_readl(musb->ctrl_base,
|
||||
DAVINCI_USB_INT_MASK_REG));
|
||||
if (code <= 0)
|
||||
goto done;
|
||||
count += code;
|
||||
buffer += code;
|
||||
#endif /* DAVINCI */
|
||||
|
||||
#ifdef CONFIG_USB_TUSB6010
|
||||
code = sprintf(buffer,
|
||||
"TUSB6010: devconf %08x, phy enable %08x drive %08x"
|
||||
"\n\totg %03x timer %08x"
|
||||
"\n\tprcm conf %08x mgmt %08x; int src %08x mask %08x"
|
||||
"\n",
|
||||
musb_readl(musb->ctrl_base, TUSB_DEV_CONF),
|
||||
musb_readl(musb->ctrl_base, TUSB_PHY_OTG_CTRL_ENABLE),
|
||||
musb_readl(musb->ctrl_base, TUSB_PHY_OTG_CTRL),
|
||||
musb_readl(musb->ctrl_base, TUSB_DEV_OTG_STAT),
|
||||
musb_readl(musb->ctrl_base, TUSB_DEV_OTG_TIMER),
|
||||
musb_readl(musb->ctrl_base, TUSB_PRCM_CONF),
|
||||
musb_readl(musb->ctrl_base, TUSB_PRCM_MNGMT),
|
||||
musb_readl(musb->ctrl_base, TUSB_INT_SRC),
|
||||
musb_readl(musb->ctrl_base, TUSB_INT_MASK));
|
||||
if (code <= 0)
|
||||
goto done;
|
||||
count += code;
|
||||
buffer += code;
|
||||
#endif /* DAVINCI */
|
||||
|
||||
if (is_cppi_enabled() && musb->dma_controller) {
|
||||
code = sprintf(buffer,
|
||||
"CPPI: txcr=%d txsrc=%01x txena=%01x; "
|
||||
"rxcr=%d rxsrc=%01x rxena=%01x "
|
||||
"\n",
|
||||
musb_readl(musb->ctrl_base,
|
||||
DAVINCI_TXCPPI_CTRL_REG),
|
||||
musb_readl(musb->ctrl_base,
|
||||
DAVINCI_TXCPPI_RAW_REG),
|
||||
musb_readl(musb->ctrl_base,
|
||||
DAVINCI_TXCPPI_INTENAB_REG),
|
||||
musb_readl(musb->ctrl_base,
|
||||
DAVINCI_RXCPPI_CTRL_REG),
|
||||
musb_readl(musb->ctrl_base,
|
||||
DAVINCI_RXCPPI_RAW_REG),
|
||||
musb_readl(musb->ctrl_base,
|
||||
DAVINCI_RXCPPI_INTENAB_REG));
|
||||
if (code <= 0)
|
||||
goto done;
|
||||
count += code;
|
||||
buffer += code;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USB_GADGET_MUSB_HDRC
|
||||
if (is_peripheral_enabled(musb)) {
|
||||
code = sprintf(buffer, "Gadget driver: %s\n",
|
||||
musb->gadget_driver
|
||||
? musb->gadget_driver->driver.name
|
||||
: "(none)");
|
||||
if (code <= 0)
|
||||
goto done;
|
||||
count += code;
|
||||
buffer += code;
|
||||
}
|
||||
#endif
|
||||
|
||||
done:
|
||||
return count;
|
||||
}
|
||||
|
||||
/* Write to ProcFS
|
||||
*
|
||||
* C soft-connect
|
||||
* c soft-disconnect
|
||||
* I enable HS
|
||||
* i disable HS
|
||||
* s stop session
|
||||
* F force session (OTG-unfriendly)
|
||||
* E rElinquish bus (OTG)
|
||||
* H request host mode
|
||||
* h cancel host request
|
||||
* T start sending TEST_PACKET
|
||||
* D<num> set/query the debug level
|
||||
*/
|
||||
static int musb_proc_write(struct file *file, const char __user *buffer,
|
||||
unsigned long count, void *data)
|
||||
{
|
||||
char cmd;
|
||||
u8 reg;
|
||||
struct musb *musb = (struct musb *)data;
|
||||
void __iomem *mbase = musb->mregs;
|
||||
|
||||
/* MOD_INC_USE_COUNT; */
|
||||
|
||||
if (unlikely(copy_from_user(&cmd, buffer, 1)))
|
||||
return -EFAULT;
|
||||
|
||||
switch (cmd) {
|
||||
case 'C':
|
||||
if (mbase) {
|
||||
reg = musb_readb(mbase, MUSB_POWER)
|
||||
| MUSB_POWER_SOFTCONN;
|
||||
musb_writeb(mbase, MUSB_POWER, reg);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
if (mbase) {
|
||||
reg = musb_readb(mbase, MUSB_POWER)
|
||||
& ~MUSB_POWER_SOFTCONN;
|
||||
musb_writeb(mbase, MUSB_POWER, reg);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'I':
|
||||
if (mbase) {
|
||||
reg = musb_readb(mbase, MUSB_POWER)
|
||||
| MUSB_POWER_HSENAB;
|
||||
musb_writeb(mbase, MUSB_POWER, reg);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'i':
|
||||
if (mbase) {
|
||||
reg = musb_readb(mbase, MUSB_POWER)
|
||||
& ~MUSB_POWER_HSENAB;
|
||||
musb_writeb(mbase, MUSB_POWER, reg);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'F':
|
||||
reg = musb_readb(mbase, MUSB_DEVCTL);
|
||||
reg |= MUSB_DEVCTL_SESSION;
|
||||
musb_writeb(mbase, MUSB_DEVCTL, reg);
|
||||
break;
|
||||
|
||||
case 'H':
|
||||
if (mbase) {
|
||||
reg = musb_readb(mbase, MUSB_DEVCTL);
|
||||
reg |= MUSB_DEVCTL_HR;
|
||||
musb_writeb(mbase, MUSB_DEVCTL, reg);
|
||||
/* MUSB_HST_MODE( ((struct musb*)data) ); */
|
||||
/* WARNING("Host Mode\n"); */
|
||||
}
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
if (mbase) {
|
||||
reg = musb_readb(mbase, MUSB_DEVCTL);
|
||||
reg &= ~MUSB_DEVCTL_HR;
|
||||
musb_writeb(mbase, MUSB_DEVCTL, reg);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'T':
|
||||
if (mbase) {
|
||||
musb_load_testpacket(musb);
|
||||
musb_writeb(mbase, MUSB_TESTMODE,
|
||||
MUSB_TEST_PACKET);
|
||||
}
|
||||
break;
|
||||
|
||||
#if (MUSB_DEBUG > 0)
|
||||
/* set/read debug level */
|
||||
case 'D':{
|
||||
if (count > 1) {
|
||||
char digits[8], *p = digits;
|
||||
int i = 0, level = 0, sign = 1;
|
||||
int len = min(count - 1, (unsigned long)8);
|
||||
|
||||
if (copy_from_user(&digits, &buffer[1], len))
|
||||
return -EFAULT;
|
||||
|
||||
/* optional sign */
|
||||
if (*p == '-') {
|
||||
len -= 1;
|
||||
sign = -sign;
|
||||
p++;
|
||||
}
|
||||
|
||||
/* read it */
|
||||
while (i++ < len && *p > '0' && *p < '9') {
|
||||
level = level * 10 + (*p - '0');
|
||||
p++;
|
||||
}
|
||||
|
||||
level *= sign;
|
||||
DBG(1, "debug level %d\n", level);
|
||||
debug = level;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case '?':
|
||||
INFO("?: you are seeing it\n");
|
||||
INFO("C/c: soft connect enable/disable\n");
|
||||
INFO("I/i: hispeed enable/disable\n");
|
||||
INFO("F: force session start\n");
|
||||
INFO("H: host mode\n");
|
||||
INFO("T: start sending TEST_PACKET\n");
|
||||
INFO("D: set/read dbug level\n");
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
ERR("Command %c not implemented\n", cmd);
|
||||
break;
|
||||
}
|
||||
|
||||
musb_platform_try_idle(musb, 0);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static int musb_proc_read(char *page, char **start,
|
||||
off_t off, int count, int *eof, void *data)
|
||||
{
|
||||
char *buffer = page;
|
||||
int code = 0;
|
||||
unsigned long flags;
|
||||
struct musb *musb = data;
|
||||
unsigned epnum;
|
||||
|
||||
count -= off;
|
||||
count -= 1; /* for NUL at end */
|
||||
if (count <= 0)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&musb->lock, flags);
|
||||
|
||||
code = dump_header_stats(musb, buffer);
|
||||
if (code > 0) {
|
||||
buffer += code;
|
||||
count -= code;
|
||||
}
|
||||
|
||||
/* generate the report for the end points */
|
||||
/* REVISIT ... not unless something's connected! */
|
||||
for (epnum = 0; count >= 0 && epnum < musb->nr_endpoints;
|
||||
epnum++) {
|
||||
code = dump_end_info(musb, epnum, buffer, count);
|
||||
if (code > 0) {
|
||||
buffer += code;
|
||||
count -= code;
|
||||
}
|
||||
}
|
||||
|
||||
musb_platform_try_idle(musb, 0);
|
||||
|
||||
spin_unlock_irqrestore(&musb->lock, flags);
|
||||
*eof = 1;
|
||||
|
||||
return buffer - page;
|
||||
}
|
||||
|
||||
void __devexit musb_debug_delete(char *name, struct musb *musb)
|
||||
{
|
||||
if (musb->proc_entry)
|
||||
remove_proc_entry(name, NULL);
|
||||
}
|
||||
|
||||
struct proc_dir_entry *__init
|
||||
musb_debug_create(char *name, struct musb *data)
|
||||
{
|
||||
struct proc_dir_entry *pde;
|
||||
|
||||
/* FIXME convert everything to seq_file; then later, debugfs */
|
||||
|
||||
if (!name)
|
||||
return NULL;
|
||||
|
||||
pde = create_proc_entry(name, S_IFREG | S_IRUGO | S_IWUSR, NULL);
|
||||
data->proc_entry = pde;
|
||||
if (pde) {
|
||||
pde->data = data;
|
||||
/* pde->owner = THIS_MODULE; */
|
||||
|
||||
pde->read_proc = musb_proc_read;
|
||||
pde->write_proc = musb_proc_write;
|
||||
|
||||
pde->size = 0;
|
||||
|
||||
pr_debug("Registered /proc/%s\n", name);
|
||||
} else {
|
||||
pr_debug("Cannot create a valid proc file entry");
|
||||
}
|
||||
|
||||
return pde;
|
||||
}
|
300
drivers/usb/musb/musb_regs.h
Normal file
300
drivers/usb/musb/musb_regs.h
Normal file
@ -0,0 +1,300 @@
|
||||
/*
|
||||
* MUSB OTG driver register defines
|
||||
*
|
||||
* Copyright 2005 Mentor Graphics Corporation
|
||||
* Copyright (C) 2005-2006 by Texas Instruments
|
||||
* Copyright (C) 2006-2007 Nokia Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED "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 AUTHORS 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __MUSB_REGS_H__
|
||||
#define __MUSB_REGS_H__
|
||||
|
||||
#define MUSB_EP0_FIFOSIZE 64 /* This is non-configurable */
|
||||
|
||||
/*
|
||||
* Common USB registers
|
||||
*/
|
||||
|
||||
#define MUSB_FADDR 0x00 /* 8-bit */
|
||||
#define MUSB_POWER 0x01 /* 8-bit */
|
||||
|
||||
#define MUSB_INTRTX 0x02 /* 16-bit */
|
||||
#define MUSB_INTRRX 0x04
|
||||
#define MUSB_INTRTXE 0x06
|
||||
#define MUSB_INTRRXE 0x08
|
||||
#define MUSB_INTRUSB 0x0A /* 8 bit */
|
||||
#define MUSB_INTRUSBE 0x0B /* 8 bit */
|
||||
#define MUSB_FRAME 0x0C
|
||||
#define MUSB_INDEX 0x0E /* 8 bit */
|
||||
#define MUSB_TESTMODE 0x0F /* 8 bit */
|
||||
|
||||
/* Get offset for a given FIFO from musb->mregs */
|
||||
#ifdef CONFIG_USB_TUSB6010
|
||||
#define MUSB_FIFO_OFFSET(epnum) (0x200 + ((epnum) * 0x20))
|
||||
#else
|
||||
#define MUSB_FIFO_OFFSET(epnum) (0x20 + ((epnum) * 4))
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Additional Control Registers
|
||||
*/
|
||||
|
||||
#define MUSB_DEVCTL 0x60 /* 8 bit */
|
||||
|
||||
/* These are always controlled through the INDEX register */
|
||||
#define MUSB_TXFIFOSZ 0x62 /* 8-bit (see masks) */
|
||||
#define MUSB_RXFIFOSZ 0x63 /* 8-bit (see masks) */
|
||||
#define MUSB_TXFIFOADD 0x64 /* 16-bit offset shifted right 3 */
|
||||
#define MUSB_RXFIFOADD 0x66 /* 16-bit offset shifted right 3 */
|
||||
|
||||
/* REVISIT: vctrl/vstatus: optional vendor utmi+phy register at 0x68 */
|
||||
#define MUSB_HWVERS 0x6C /* 8 bit */
|
||||
|
||||
#define MUSB_EPINFO 0x78 /* 8 bit */
|
||||
#define MUSB_RAMINFO 0x79 /* 8 bit */
|
||||
#define MUSB_LINKINFO 0x7a /* 8 bit */
|
||||
#define MUSB_VPLEN 0x7b /* 8 bit */
|
||||
#define MUSB_HS_EOF1 0x7c /* 8 bit */
|
||||
#define MUSB_FS_EOF1 0x7d /* 8 bit */
|
||||
#define MUSB_LS_EOF1 0x7e /* 8 bit */
|
||||
|
||||
/* Offsets to endpoint registers */
|
||||
#define MUSB_TXMAXP 0x00
|
||||
#define MUSB_TXCSR 0x02
|
||||
#define MUSB_CSR0 MUSB_TXCSR /* Re-used for EP0 */
|
||||
#define MUSB_RXMAXP 0x04
|
||||
#define MUSB_RXCSR 0x06
|
||||
#define MUSB_RXCOUNT 0x08
|
||||
#define MUSB_COUNT0 MUSB_RXCOUNT /* Re-used for EP0 */
|
||||
#define MUSB_TXTYPE 0x0A
|
||||
#define MUSB_TYPE0 MUSB_TXTYPE /* Re-used for EP0 */
|
||||
#define MUSB_TXINTERVAL 0x0B
|
||||
#define MUSB_NAKLIMIT0 MUSB_TXINTERVAL /* Re-used for EP0 */
|
||||
#define MUSB_RXTYPE 0x0C
|
||||
#define MUSB_RXINTERVAL 0x0D
|
||||
#define MUSB_FIFOSIZE 0x0F
|
||||
#define MUSB_CONFIGDATA MUSB_FIFOSIZE /* Re-used for EP0 */
|
||||
|
||||
/* Offsets to endpoint registers in indexed model (using INDEX register) */
|
||||
#define MUSB_INDEXED_OFFSET(_epnum, _offset) \
|
||||
(0x10 + (_offset))
|
||||
|
||||
/* Offsets to endpoint registers in flat models */
|
||||
#define MUSB_FLAT_OFFSET(_epnum, _offset) \
|
||||
(0x100 + (0x10*(_epnum)) + (_offset))
|
||||
|
||||
#ifdef CONFIG_USB_TUSB6010
|
||||
/* TUSB6010 EP0 configuration register is special */
|
||||
#define MUSB_TUSB_OFFSET(_epnum, _offset) \
|
||||
(0x10 + _offset)
|
||||
#include "tusb6010.h" /* Needed "only" for TUSB_EP0_CONF */
|
||||
#endif
|
||||
|
||||
/* "bus control"/target registers, for host side multipoint (external hubs) */
|
||||
#define MUSB_TXFUNCADDR 0x00
|
||||
#define MUSB_TXHUBADDR 0x02
|
||||
#define MUSB_TXHUBPORT 0x03
|
||||
|
||||
#define MUSB_RXFUNCADDR 0x04
|
||||
#define MUSB_RXHUBADDR 0x06
|
||||
#define MUSB_RXHUBPORT 0x07
|
||||
|
||||
#define MUSB_BUSCTL_OFFSET(_epnum, _offset) \
|
||||
(0x80 + (8*(_epnum)) + (_offset))
|
||||
|
||||
/*
|
||||
* MUSB Register bits
|
||||
*/
|
||||
|
||||
/* POWER */
|
||||
#define MUSB_POWER_ISOUPDATE 0x80
|
||||
#define MUSB_POWER_SOFTCONN 0x40
|
||||
#define MUSB_POWER_HSENAB 0x20
|
||||
#define MUSB_POWER_HSMODE 0x10
|
||||
#define MUSB_POWER_RESET 0x08
|
||||
#define MUSB_POWER_RESUME 0x04
|
||||
#define MUSB_POWER_SUSPENDM 0x02
|
||||
#define MUSB_POWER_ENSUSPEND 0x01
|
||||
|
||||
/* INTRUSB */
|
||||
#define MUSB_INTR_SUSPEND 0x01
|
||||
#define MUSB_INTR_RESUME 0x02
|
||||
#define MUSB_INTR_RESET 0x04
|
||||
#define MUSB_INTR_BABBLE 0x04
|
||||
#define MUSB_INTR_SOF 0x08
|
||||
#define MUSB_INTR_CONNECT 0x10
|
||||
#define MUSB_INTR_DISCONNECT 0x20
|
||||
#define MUSB_INTR_SESSREQ 0x40
|
||||
#define MUSB_INTR_VBUSERROR 0x80 /* For SESSION end */
|
||||
|
||||
/* DEVCTL */
|
||||
#define MUSB_DEVCTL_BDEVICE 0x80
|
||||
#define MUSB_DEVCTL_FSDEV 0x40
|
||||
#define MUSB_DEVCTL_LSDEV 0x20
|
||||
#define MUSB_DEVCTL_VBUS 0x18
|
||||
#define MUSB_DEVCTL_VBUS_SHIFT 3
|
||||
#define MUSB_DEVCTL_HM 0x04
|
||||
#define MUSB_DEVCTL_HR 0x02
|
||||
#define MUSB_DEVCTL_SESSION 0x01
|
||||
|
||||
/* TESTMODE */
|
||||
#define MUSB_TEST_FORCE_HOST 0x80
|
||||
#define MUSB_TEST_FIFO_ACCESS 0x40
|
||||
#define MUSB_TEST_FORCE_FS 0x20
|
||||
#define MUSB_TEST_FORCE_HS 0x10
|
||||
#define MUSB_TEST_PACKET 0x08
|
||||
#define MUSB_TEST_K 0x04
|
||||
#define MUSB_TEST_J 0x02
|
||||
#define MUSB_TEST_SE0_NAK 0x01
|
||||
|
||||
/* Allocate for double-packet buffering (effectively doubles assigned _SIZE) */
|
||||
#define MUSB_FIFOSZ_DPB 0x10
|
||||
/* Allocation size (8, 16, 32, ... 4096) */
|
||||
#define MUSB_FIFOSZ_SIZE 0x0f
|
||||
|
||||
/* CSR0 */
|
||||
#define MUSB_CSR0_FLUSHFIFO 0x0100
|
||||
#define MUSB_CSR0_TXPKTRDY 0x0002
|
||||
#define MUSB_CSR0_RXPKTRDY 0x0001
|
||||
|
||||
/* CSR0 in Peripheral mode */
|
||||
#define MUSB_CSR0_P_SVDSETUPEND 0x0080
|
||||
#define MUSB_CSR0_P_SVDRXPKTRDY 0x0040
|
||||
#define MUSB_CSR0_P_SENDSTALL 0x0020
|
||||
#define MUSB_CSR0_P_SETUPEND 0x0010
|
||||
#define MUSB_CSR0_P_DATAEND 0x0008
|
||||
#define MUSB_CSR0_P_SENTSTALL 0x0004
|
||||
|
||||
/* CSR0 in Host mode */
|
||||
#define MUSB_CSR0_H_DIS_PING 0x0800
|
||||
#define MUSB_CSR0_H_WR_DATATOGGLE 0x0400 /* Set to allow setting: */
|
||||
#define MUSB_CSR0_H_DATATOGGLE 0x0200 /* Data toggle control */
|
||||
#define MUSB_CSR0_H_NAKTIMEOUT 0x0080
|
||||
#define MUSB_CSR0_H_STATUSPKT 0x0040
|
||||
#define MUSB_CSR0_H_REQPKT 0x0020
|
||||
#define MUSB_CSR0_H_ERROR 0x0010
|
||||
#define MUSB_CSR0_H_SETUPPKT 0x0008
|
||||
#define MUSB_CSR0_H_RXSTALL 0x0004
|
||||
|
||||
/* CSR0 bits to avoid zeroing (write zero clears, write 1 ignored) */
|
||||
#define MUSB_CSR0_P_WZC_BITS \
|
||||
(MUSB_CSR0_P_SENTSTALL)
|
||||
#define MUSB_CSR0_H_WZC_BITS \
|
||||
(MUSB_CSR0_H_NAKTIMEOUT | MUSB_CSR0_H_RXSTALL \
|
||||
| MUSB_CSR0_RXPKTRDY)
|
||||
|
||||
/* TxType/RxType */
|
||||
#define MUSB_TYPE_SPEED 0xc0
|
||||
#define MUSB_TYPE_SPEED_SHIFT 6
|
||||
#define MUSB_TYPE_PROTO 0x30 /* Implicitly zero for ep0 */
|
||||
#define MUSB_TYPE_PROTO_SHIFT 4
|
||||
#define MUSB_TYPE_REMOTE_END 0xf /* Implicitly zero for ep0 */
|
||||
|
||||
/* CONFIGDATA */
|
||||
#define MUSB_CONFIGDATA_MPRXE 0x80 /* Auto bulk pkt combining */
|
||||
#define MUSB_CONFIGDATA_MPTXE 0x40 /* Auto bulk pkt splitting */
|
||||
#define MUSB_CONFIGDATA_BIGENDIAN 0x20
|
||||
#define MUSB_CONFIGDATA_HBRXE 0x10 /* HB-ISO for RX */
|
||||
#define MUSB_CONFIGDATA_HBTXE 0x08 /* HB-ISO for TX */
|
||||
#define MUSB_CONFIGDATA_DYNFIFO 0x04 /* Dynamic FIFO sizing */
|
||||
#define MUSB_CONFIGDATA_SOFTCONE 0x02 /* SoftConnect */
|
||||
#define MUSB_CONFIGDATA_UTMIDW 0x01 /* Data width 0/1 => 8/16bits */
|
||||
|
||||
/* TXCSR in Peripheral and Host mode */
|
||||
#define MUSB_TXCSR_AUTOSET 0x8000
|
||||
#define MUSB_TXCSR_MODE 0x2000
|
||||
#define MUSB_TXCSR_DMAENAB 0x1000
|
||||
#define MUSB_TXCSR_FRCDATATOG 0x0800
|
||||
#define MUSB_TXCSR_DMAMODE 0x0400
|
||||
#define MUSB_TXCSR_CLRDATATOG 0x0040
|
||||
#define MUSB_TXCSR_FLUSHFIFO 0x0008
|
||||
#define MUSB_TXCSR_FIFONOTEMPTY 0x0002
|
||||
#define MUSB_TXCSR_TXPKTRDY 0x0001
|
||||
|
||||
/* TXCSR in Peripheral mode */
|
||||
#define MUSB_TXCSR_P_ISO 0x4000
|
||||
#define MUSB_TXCSR_P_INCOMPTX 0x0080
|
||||
#define MUSB_TXCSR_P_SENTSTALL 0x0020
|
||||
#define MUSB_TXCSR_P_SENDSTALL 0x0010
|
||||
#define MUSB_TXCSR_P_UNDERRUN 0x0004
|
||||
|
||||
/* TXCSR in Host mode */
|
||||
#define MUSB_TXCSR_H_WR_DATATOGGLE 0x0200
|
||||
#define MUSB_TXCSR_H_DATATOGGLE 0x0100
|
||||
#define MUSB_TXCSR_H_NAKTIMEOUT 0x0080
|
||||
#define MUSB_TXCSR_H_RXSTALL 0x0020
|
||||
#define MUSB_TXCSR_H_ERROR 0x0004
|
||||
|
||||
/* TXCSR bits to avoid zeroing (write zero clears, write 1 ignored) */
|
||||
#define MUSB_TXCSR_P_WZC_BITS \
|
||||
(MUSB_TXCSR_P_INCOMPTX | MUSB_TXCSR_P_SENTSTALL \
|
||||
| MUSB_TXCSR_P_UNDERRUN | MUSB_TXCSR_FIFONOTEMPTY)
|
||||
#define MUSB_TXCSR_H_WZC_BITS \
|
||||
(MUSB_TXCSR_H_NAKTIMEOUT | MUSB_TXCSR_H_RXSTALL \
|
||||
| MUSB_TXCSR_H_ERROR | MUSB_TXCSR_FIFONOTEMPTY)
|
||||
|
||||
/* RXCSR in Peripheral and Host mode */
|
||||
#define MUSB_RXCSR_AUTOCLEAR 0x8000
|
||||
#define MUSB_RXCSR_DMAENAB 0x2000
|
||||
#define MUSB_RXCSR_DISNYET 0x1000
|
||||
#define MUSB_RXCSR_PID_ERR 0x1000
|
||||
#define MUSB_RXCSR_DMAMODE 0x0800
|
||||
#define MUSB_RXCSR_INCOMPRX 0x0100
|
||||
#define MUSB_RXCSR_CLRDATATOG 0x0080
|
||||
#define MUSB_RXCSR_FLUSHFIFO 0x0010
|
||||
#define MUSB_RXCSR_DATAERROR 0x0008
|
||||
#define MUSB_RXCSR_FIFOFULL 0x0002
|
||||
#define MUSB_RXCSR_RXPKTRDY 0x0001
|
||||
|
||||
/* RXCSR in Peripheral mode */
|
||||
#define MUSB_RXCSR_P_ISO 0x4000
|
||||
#define MUSB_RXCSR_P_SENTSTALL 0x0040
|
||||
#define MUSB_RXCSR_P_SENDSTALL 0x0020
|
||||
#define MUSB_RXCSR_P_OVERRUN 0x0004
|
||||
|
||||
/* RXCSR in Host mode */
|
||||
#define MUSB_RXCSR_H_AUTOREQ 0x4000
|
||||
#define MUSB_RXCSR_H_WR_DATATOGGLE 0x0400
|
||||
#define MUSB_RXCSR_H_DATATOGGLE 0x0200
|
||||
#define MUSB_RXCSR_H_RXSTALL 0x0040
|
||||
#define MUSB_RXCSR_H_REQPKT 0x0020
|
||||
#define MUSB_RXCSR_H_ERROR 0x0004
|
||||
|
||||
/* RXCSR bits to avoid zeroing (write zero clears, write 1 ignored) */
|
||||
#define MUSB_RXCSR_P_WZC_BITS \
|
||||
(MUSB_RXCSR_P_SENTSTALL | MUSB_RXCSR_P_OVERRUN \
|
||||
| MUSB_RXCSR_RXPKTRDY)
|
||||
#define MUSB_RXCSR_H_WZC_BITS \
|
||||
(MUSB_RXCSR_H_RXSTALL | MUSB_RXCSR_H_ERROR \
|
||||
| MUSB_RXCSR_DATAERROR | MUSB_RXCSR_RXPKTRDY)
|
||||
|
||||
/* HUBADDR */
|
||||
#define MUSB_HUBADDR_MULTI_TT 0x80
|
||||
|
||||
#endif /* __MUSB_REGS_H__ */
|
425
drivers/usb/musb/musb_virthub.c
Normal file
425
drivers/usb/musb/musb_virthub.c
Normal file
@ -0,0 +1,425 @@
|
||||
/*
|
||||
* MUSB OTG driver virtual root hub support
|
||||
*
|
||||
* Copyright 2005 Mentor Graphics Corporation
|
||||
* Copyright (C) 2005-2006 by Texas Instruments
|
||||
* Copyright (C) 2006-2007 Nokia Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED "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 AUTHORS 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/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/timer.h>
|
||||
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#include "musb_core.h"
|
||||
|
||||
|
||||
static void musb_port_suspend(struct musb *musb, bool do_suspend)
|
||||
{
|
||||
u8 power;
|
||||
void __iomem *mbase = musb->mregs;
|
||||
|
||||
if (!is_host_active(musb))
|
||||
return;
|
||||
|
||||
/* NOTE: this doesn't necessarily put PHY into low power mode,
|
||||
* turning off its clock; that's a function of PHY integration and
|
||||
* MUSB_POWER_ENSUSPEND. PHY may need a clock (sigh) to detect
|
||||
* SE0 changing to connect (J) or wakeup (K) states.
|
||||
*/
|
||||
power = musb_readb(mbase, MUSB_POWER);
|
||||
if (do_suspend) {
|
||||
int retries = 10000;
|
||||
|
||||
power &= ~MUSB_POWER_RESUME;
|
||||
power |= MUSB_POWER_SUSPENDM;
|
||||
musb_writeb(mbase, MUSB_POWER, power);
|
||||
|
||||
/* Needed for OPT A tests */
|
||||
power = musb_readb(mbase, MUSB_POWER);
|
||||
while (power & MUSB_POWER_SUSPENDM) {
|
||||
power = musb_readb(mbase, MUSB_POWER);
|
||||
if (retries-- < 1)
|
||||
break;
|
||||
}
|
||||
|
||||
DBG(3, "Root port suspended, power %02x\n", power);
|
||||
|
||||
musb->port1_status |= USB_PORT_STAT_SUSPEND;
|
||||
switch (musb->xceiv.state) {
|
||||
case OTG_STATE_A_HOST:
|
||||
musb->xceiv.state = OTG_STATE_A_SUSPEND;
|
||||
musb->is_active = is_otg_enabled(musb)
|
||||
&& musb->xceiv.host->b_hnp_enable;
|
||||
musb_platform_try_idle(musb, 0);
|
||||
break;
|
||||
#ifdef CONFIG_USB_MUSB_OTG
|
||||
case OTG_STATE_B_HOST:
|
||||
musb->xceiv.state = OTG_STATE_B_WAIT_ACON;
|
||||
musb->is_active = is_otg_enabled(musb)
|
||||
&& musb->xceiv.host->b_hnp_enable;
|
||||
musb_platform_try_idle(musb, 0);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
DBG(1, "bogus rh suspend? %s\n",
|
||||
otg_state_string(musb));
|
||||
}
|
||||
} else if (power & MUSB_POWER_SUSPENDM) {
|
||||
power &= ~MUSB_POWER_SUSPENDM;
|
||||
power |= MUSB_POWER_RESUME;
|
||||
musb_writeb(mbase, MUSB_POWER, power);
|
||||
|
||||
DBG(3, "Root port resuming, power %02x\n", power);
|
||||
|
||||
/* later, GetPortStatus will stop RESUME signaling */
|
||||
musb->port1_status |= MUSB_PORT_STAT_RESUME;
|
||||
musb->rh_timer = jiffies + msecs_to_jiffies(20);
|
||||
}
|
||||
}
|
||||
|
||||
static void musb_port_reset(struct musb *musb, bool do_reset)
|
||||
{
|
||||
u8 power;
|
||||
void __iomem *mbase = musb->mregs;
|
||||
|
||||
#ifdef CONFIG_USB_MUSB_OTG
|
||||
if (musb->xceiv.state == OTG_STATE_B_IDLE) {
|
||||
DBG(2, "HNP: Returning from HNP; no hub reset from b_idle\n");
|
||||
musb->port1_status &= ~USB_PORT_STAT_RESET;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!is_host_active(musb))
|
||||
return;
|
||||
|
||||
/* NOTE: caller guarantees it will turn off the reset when
|
||||
* the appropriate amount of time has passed
|
||||
*/
|
||||
power = musb_readb(mbase, MUSB_POWER);
|
||||
if (do_reset) {
|
||||
|
||||
/*
|
||||
* If RESUME is set, we must make sure it stays minimum 20 ms.
|
||||
* Then we must clear RESUME and wait a bit to let musb start
|
||||
* generating SOFs. If we don't do this, OPT HS A 6.8 tests
|
||||
* fail with "Error! Did not receive an SOF before suspend
|
||||
* detected".
|
||||
*/
|
||||
if (power & MUSB_POWER_RESUME) {
|
||||
while (time_before(jiffies, musb->rh_timer))
|
||||
msleep(1);
|
||||
musb_writeb(mbase, MUSB_POWER,
|
||||
power & ~MUSB_POWER_RESUME);
|
||||
msleep(1);
|
||||
}
|
||||
|
||||
musb->ignore_disconnect = true;
|
||||
power &= 0xf0;
|
||||
musb_writeb(mbase, MUSB_POWER,
|
||||
power | MUSB_POWER_RESET);
|
||||
|
||||
musb->port1_status |= USB_PORT_STAT_RESET;
|
||||
musb->port1_status &= ~USB_PORT_STAT_ENABLE;
|
||||
musb->rh_timer = jiffies + msecs_to_jiffies(50);
|
||||
} else {
|
||||
DBG(4, "root port reset stopped\n");
|
||||
musb_writeb(mbase, MUSB_POWER,
|
||||
power & ~MUSB_POWER_RESET);
|
||||
|
||||
musb->ignore_disconnect = false;
|
||||
|
||||
power = musb_readb(mbase, MUSB_POWER);
|
||||
if (power & MUSB_POWER_HSMODE) {
|
||||
DBG(4, "high-speed device connected\n");
|
||||
musb->port1_status |= USB_PORT_STAT_HIGH_SPEED;
|
||||
}
|
||||
|
||||
musb->port1_status &= ~USB_PORT_STAT_RESET;
|
||||
musb->port1_status |= USB_PORT_STAT_ENABLE
|
||||
| (USB_PORT_STAT_C_RESET << 16)
|
||||
| (USB_PORT_STAT_C_ENABLE << 16);
|
||||
usb_hcd_poll_rh_status(musb_to_hcd(musb));
|
||||
|
||||
musb->vbuserr_retry = VBUSERR_RETRY_COUNT;
|
||||
}
|
||||
}
|
||||
|
||||
void musb_root_disconnect(struct musb *musb)
|
||||
{
|
||||
musb->port1_status = (1 << USB_PORT_FEAT_POWER)
|
||||
| (1 << USB_PORT_FEAT_C_CONNECTION);
|
||||
|
||||
usb_hcd_poll_rh_status(musb_to_hcd(musb));
|
||||
musb->is_active = 0;
|
||||
|
||||
switch (musb->xceiv.state) {
|
||||
case OTG_STATE_A_HOST:
|
||||
case OTG_STATE_A_SUSPEND:
|
||||
musb->xceiv.state = OTG_STATE_A_WAIT_BCON;
|
||||
musb->is_active = 0;
|
||||
break;
|
||||
case OTG_STATE_A_WAIT_VFALL:
|
||||
musb->xceiv.state = OTG_STATE_B_IDLE;
|
||||
break;
|
||||
default:
|
||||
DBG(1, "host disconnect (%s)\n", otg_state_string(musb));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------*/
|
||||
|
||||
/* Caller may or may not hold musb->lock */
|
||||
int musb_hub_status_data(struct usb_hcd *hcd, char *buf)
|
||||
{
|
||||
struct musb *musb = hcd_to_musb(hcd);
|
||||
int retval = 0;
|
||||
|
||||
/* called in_irq() via usb_hcd_poll_rh_status() */
|
||||
if (musb->port1_status & 0xffff0000) {
|
||||
*buf = 0x02;
|
||||
retval = 1;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
int musb_hub_control(
|
||||
struct usb_hcd *hcd,
|
||||
u16 typeReq,
|
||||
u16 wValue,
|
||||
u16 wIndex,
|
||||
char *buf,
|
||||
u16 wLength)
|
||||
{
|
||||
struct musb *musb = hcd_to_musb(hcd);
|
||||
u32 temp;
|
||||
int retval = 0;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&musb->lock, flags);
|
||||
|
||||
if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))) {
|
||||
spin_unlock_irqrestore(&musb->lock, flags);
|
||||
return -ESHUTDOWN;
|
||||
}
|
||||
|
||||
/* hub features: always zero, setting is a NOP
|
||||
* port features: reported, sometimes updated when host is active
|
||||
* no indicators
|
||||
*/
|
||||
switch (typeReq) {
|
||||
case ClearHubFeature:
|
||||
case SetHubFeature:
|
||||
switch (wValue) {
|
||||
case C_HUB_OVER_CURRENT:
|
||||
case C_HUB_LOCAL_POWER:
|
||||
break;
|
||||
default:
|
||||
goto error;
|
||||
}
|
||||
break;
|
||||
case ClearPortFeature:
|
||||
if ((wIndex & 0xff) != 1)
|
||||
goto error;
|
||||
|
||||
switch (wValue) {
|
||||
case USB_PORT_FEAT_ENABLE:
|
||||
break;
|
||||
case USB_PORT_FEAT_SUSPEND:
|
||||
musb_port_suspend(musb, false);
|
||||
break;
|
||||
case USB_PORT_FEAT_POWER:
|
||||
if (!(is_otg_enabled(musb) && hcd->self.is_b_host))
|
||||
musb_set_vbus(musb, 0);
|
||||
break;
|
||||
case USB_PORT_FEAT_C_CONNECTION:
|
||||
case USB_PORT_FEAT_C_ENABLE:
|
||||
case USB_PORT_FEAT_C_OVER_CURRENT:
|
||||
case USB_PORT_FEAT_C_RESET:
|
||||
case USB_PORT_FEAT_C_SUSPEND:
|
||||
break;
|
||||
default:
|
||||
goto error;
|
||||
}
|
||||
DBG(5, "clear feature %d\n", wValue);
|
||||
musb->port1_status &= ~(1 << wValue);
|
||||
break;
|
||||
case GetHubDescriptor:
|
||||
{
|
||||
struct usb_hub_descriptor *desc = (void *)buf;
|
||||
|
||||
desc->bDescLength = 9;
|
||||
desc->bDescriptorType = 0x29;
|
||||
desc->bNbrPorts = 1;
|
||||
desc->wHubCharacteristics = __constant_cpu_to_le16(
|
||||
0x0001 /* per-port power switching */
|
||||
| 0x0010 /* no overcurrent reporting */
|
||||
);
|
||||
desc->bPwrOn2PwrGood = 5; /* msec/2 */
|
||||
desc->bHubContrCurrent = 0;
|
||||
|
||||
/* workaround bogus struct definition */
|
||||
desc->DeviceRemovable[0] = 0x02; /* port 1 */
|
||||
desc->DeviceRemovable[1] = 0xff;
|
||||
}
|
||||
break;
|
||||
case GetHubStatus:
|
||||
temp = 0;
|
||||
*(__le32 *) buf = cpu_to_le32(temp);
|
||||
break;
|
||||
case GetPortStatus:
|
||||
if (wIndex != 1)
|
||||
goto error;
|
||||
|
||||
/* finish RESET signaling? */
|
||||
if ((musb->port1_status & USB_PORT_STAT_RESET)
|
||||
&& time_after_eq(jiffies, musb->rh_timer))
|
||||
musb_port_reset(musb, false);
|
||||
|
||||
/* finish RESUME signaling? */
|
||||
if ((musb->port1_status & MUSB_PORT_STAT_RESUME)
|
||||
&& time_after_eq(jiffies, musb->rh_timer)) {
|
||||
u8 power;
|
||||
|
||||
power = musb_readb(musb->mregs, MUSB_POWER);
|
||||
power &= ~MUSB_POWER_RESUME;
|
||||
DBG(4, "root port resume stopped, power %02x\n",
|
||||
power);
|
||||
musb_writeb(musb->mregs, MUSB_POWER, power);
|
||||
|
||||
/* ISSUE: DaVinci (RTL 1.300) disconnects after
|
||||
* resume of high speed peripherals (but not full
|
||||
* speed ones).
|
||||
*/
|
||||
|
||||
musb->is_active = 1;
|
||||
musb->port1_status &= ~(USB_PORT_STAT_SUSPEND
|
||||
| MUSB_PORT_STAT_RESUME);
|
||||
musb->port1_status |= USB_PORT_STAT_C_SUSPEND << 16;
|
||||
usb_hcd_poll_rh_status(musb_to_hcd(musb));
|
||||
/* NOTE: it might really be A_WAIT_BCON ... */
|
||||
musb->xceiv.state = OTG_STATE_A_HOST;
|
||||
}
|
||||
|
||||
put_unaligned(cpu_to_le32(musb->port1_status
|
||||
& ~MUSB_PORT_STAT_RESUME),
|
||||
(__le32 *) buf);
|
||||
|
||||
/* port change status is more interesting */
|
||||
DBG(get_unaligned((u16 *)(buf+2)) ? 2 : 5, "port status %08x\n",
|
||||
musb->port1_status);
|
||||
break;
|
||||
case SetPortFeature:
|
||||
if ((wIndex & 0xff) != 1)
|
||||
goto error;
|
||||
|
||||
switch (wValue) {
|
||||
case USB_PORT_FEAT_POWER:
|
||||
/* NOTE: this controller has a strange state machine
|
||||
* that involves "requesting sessions" according to
|
||||
* magic side effects from incompletely-described
|
||||
* rules about startup...
|
||||
*
|
||||
* This call is what really starts the host mode; be
|
||||
* very careful about side effects if you reorder any
|
||||
* initialization logic, e.g. for OTG, or change any
|
||||
* logic relating to VBUS power-up.
|
||||
*/
|
||||
if (!(is_otg_enabled(musb) && hcd->self.is_b_host))
|
||||
musb_start(musb);
|
||||
break;
|
||||
case USB_PORT_FEAT_RESET:
|
||||
musb_port_reset(musb, true);
|
||||
break;
|
||||
case USB_PORT_FEAT_SUSPEND:
|
||||
musb_port_suspend(musb, true);
|
||||
break;
|
||||
case USB_PORT_FEAT_TEST:
|
||||
if (unlikely(is_host_active(musb)))
|
||||
goto error;
|
||||
|
||||
wIndex >>= 8;
|
||||
switch (wIndex) {
|
||||
case 1:
|
||||
pr_debug("TEST_J\n");
|
||||
temp = MUSB_TEST_J;
|
||||
break;
|
||||
case 2:
|
||||
pr_debug("TEST_K\n");
|
||||
temp = MUSB_TEST_K;
|
||||
break;
|
||||
case 3:
|
||||
pr_debug("TEST_SE0_NAK\n");
|
||||
temp = MUSB_TEST_SE0_NAK;
|
||||
break;
|
||||
case 4:
|
||||
pr_debug("TEST_PACKET\n");
|
||||
temp = MUSB_TEST_PACKET;
|
||||
musb_load_testpacket(musb);
|
||||
break;
|
||||
case 5:
|
||||
pr_debug("TEST_FORCE_ENABLE\n");
|
||||
temp = MUSB_TEST_FORCE_HOST
|
||||
| MUSB_TEST_FORCE_HS;
|
||||
|
||||
musb_writeb(musb->mregs, MUSB_DEVCTL,
|
||||
MUSB_DEVCTL_SESSION);
|
||||
break;
|
||||
case 6:
|
||||
pr_debug("TEST_FIFO_ACCESS\n");
|
||||
temp = MUSB_TEST_FIFO_ACCESS;
|
||||
break;
|
||||
default:
|
||||
goto error;
|
||||
}
|
||||
musb_writeb(musb->mregs, MUSB_TESTMODE, temp);
|
||||
break;
|
||||
default:
|
||||
goto error;
|
||||
}
|
||||
DBG(5, "set feature %d\n", wValue);
|
||||
musb->port1_status |= 1 << wValue;
|
||||
break;
|
||||
|
||||
default:
|
||||
error:
|
||||
/* "protocol stall" on error */
|
||||
retval = -EPIPE;
|
||||
}
|
||||
spin_unlock_irqrestore(&musb->lock, flags);
|
||||
return retval;
|
||||
}
|
433
drivers/usb/musb/musbhsdma.c
Normal file
433
drivers/usb/musb/musbhsdma.c
Normal file
@ -0,0 +1,433 @@
|
||||
/*
|
||||
* MUSB OTG driver - support for Mentor's DMA controller
|
||||
*
|
||||
* Copyright 2005 Mentor Graphics Corporation
|
||||
* Copyright (C) 2005-2007 by Texas Instruments
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED "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 AUTHORS 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/device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include "musb_core.h"
|
||||
|
||||
#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3430)
|
||||
#include "omap2430.h"
|
||||
#endif
|
||||
|
||||
#define MUSB_HSDMA_BASE 0x200
|
||||
#define MUSB_HSDMA_INTR (MUSB_HSDMA_BASE + 0)
|
||||
#define MUSB_HSDMA_CONTROL 0x4
|
||||
#define MUSB_HSDMA_ADDRESS 0x8
|
||||
#define MUSB_HSDMA_COUNT 0xc
|
||||
|
||||
#define MUSB_HSDMA_CHANNEL_OFFSET(_bChannel, _offset) \
|
||||
(MUSB_HSDMA_BASE + (_bChannel << 4) + _offset)
|
||||
|
||||
/* control register (16-bit): */
|
||||
#define MUSB_HSDMA_ENABLE_SHIFT 0
|
||||
#define MUSB_HSDMA_TRANSMIT_SHIFT 1
|
||||
#define MUSB_HSDMA_MODE1_SHIFT 2
|
||||
#define MUSB_HSDMA_IRQENABLE_SHIFT 3
|
||||
#define MUSB_HSDMA_ENDPOINT_SHIFT 4
|
||||
#define MUSB_HSDMA_BUSERROR_SHIFT 8
|
||||
#define MUSB_HSDMA_BURSTMODE_SHIFT 9
|
||||
#define MUSB_HSDMA_BURSTMODE (3 << MUSB_HSDMA_BURSTMODE_SHIFT)
|
||||
#define MUSB_HSDMA_BURSTMODE_UNSPEC 0
|
||||
#define MUSB_HSDMA_BURSTMODE_INCR4 1
|
||||
#define MUSB_HSDMA_BURSTMODE_INCR8 2
|
||||
#define MUSB_HSDMA_BURSTMODE_INCR16 3
|
||||
|
||||
#define MUSB_HSDMA_CHANNELS 8
|
||||
|
||||
struct musb_dma_controller;
|
||||
|
||||
struct musb_dma_channel {
|
||||
struct dma_channel Channel;
|
||||
struct musb_dma_controller *controller;
|
||||
u32 dwStartAddress;
|
||||
u32 len;
|
||||
u16 wMaxPacketSize;
|
||||
u8 bIndex;
|
||||
u8 epnum;
|
||||
u8 transmit;
|
||||
};
|
||||
|
||||
struct musb_dma_controller {
|
||||
struct dma_controller Controller;
|
||||
struct musb_dma_channel aChannel[MUSB_HSDMA_CHANNELS];
|
||||
void *pDmaPrivate;
|
||||
void __iomem *pCoreBase;
|
||||
u8 bChannelCount;
|
||||
u8 bmUsedChannels;
|
||||
u8 irq;
|
||||
};
|
||||
|
||||
static int dma_controller_start(struct dma_controller *c)
|
||||
{
|
||||
/* nothing to do */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dma_channel_release(struct dma_channel *pChannel);
|
||||
|
||||
static int dma_controller_stop(struct dma_controller *c)
|
||||
{
|
||||
struct musb_dma_controller *controller =
|
||||
container_of(c, struct musb_dma_controller, Controller);
|
||||
struct musb *musb = (struct musb *) controller->pDmaPrivate;
|
||||
struct dma_channel *pChannel;
|
||||
u8 bBit;
|
||||
|
||||
if (controller->bmUsedChannels != 0) {
|
||||
dev_err(musb->controller,
|
||||
"Stopping DMA controller while channel active\n");
|
||||
|
||||
for (bBit = 0; bBit < MUSB_HSDMA_CHANNELS; bBit++) {
|
||||
if (controller->bmUsedChannels & (1 << bBit)) {
|
||||
pChannel = &controller->aChannel[bBit].Channel;
|
||||
dma_channel_release(pChannel);
|
||||
|
||||
if (!controller->bmUsedChannels)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct dma_channel *dma_channel_allocate(struct dma_controller *c,
|
||||
struct musb_hw_ep *hw_ep, u8 transmit)
|
||||
{
|
||||
u8 bBit;
|
||||
struct dma_channel *pChannel = NULL;
|
||||
struct musb_dma_channel *pImplChannel = NULL;
|
||||
struct musb_dma_controller *controller =
|
||||
container_of(c, struct musb_dma_controller, Controller);
|
||||
|
||||
for (bBit = 0; bBit < MUSB_HSDMA_CHANNELS; bBit++) {
|
||||
if (!(controller->bmUsedChannels & (1 << bBit))) {
|
||||
controller->bmUsedChannels |= (1 << bBit);
|
||||
pImplChannel = &(controller->aChannel[bBit]);
|
||||
pImplChannel->controller = controller;
|
||||
pImplChannel->bIndex = bBit;
|
||||
pImplChannel->epnum = hw_ep->epnum;
|
||||
pImplChannel->transmit = transmit;
|
||||
pChannel = &(pImplChannel->Channel);
|
||||
pChannel->private_data = pImplChannel;
|
||||
pChannel->status = MUSB_DMA_STATUS_FREE;
|
||||
pChannel->max_len = 0x10000;
|
||||
/* Tx => mode 1; Rx => mode 0 */
|
||||
pChannel->desired_mode = transmit;
|
||||
pChannel->actual_len = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return pChannel;
|
||||
}
|
||||
|
||||
static void dma_channel_release(struct dma_channel *pChannel)
|
||||
{
|
||||
struct musb_dma_channel *pImplChannel =
|
||||
(struct musb_dma_channel *) pChannel->private_data;
|
||||
|
||||
pChannel->actual_len = 0;
|
||||
pImplChannel->dwStartAddress = 0;
|
||||
pImplChannel->len = 0;
|
||||
|
||||
pImplChannel->controller->bmUsedChannels &=
|
||||
~(1 << pImplChannel->bIndex);
|
||||
|
||||
pChannel->status = MUSB_DMA_STATUS_UNKNOWN;
|
||||
}
|
||||
|
||||
static void configure_channel(struct dma_channel *pChannel,
|
||||
u16 packet_sz, u8 mode,
|
||||
dma_addr_t dma_addr, u32 len)
|
||||
{
|
||||
struct musb_dma_channel *pImplChannel =
|
||||
(struct musb_dma_channel *) pChannel->private_data;
|
||||
struct musb_dma_controller *controller = pImplChannel->controller;
|
||||
void __iomem *mbase = controller->pCoreBase;
|
||||
u8 bChannel = pImplChannel->bIndex;
|
||||
u16 csr = 0;
|
||||
|
||||
DBG(4, "%p, pkt_sz %d, addr 0x%x, len %d, mode %d\n",
|
||||
pChannel, packet_sz, dma_addr, len, mode);
|
||||
|
||||
if (mode) {
|
||||
csr |= 1 << MUSB_HSDMA_MODE1_SHIFT;
|
||||
BUG_ON(len < packet_sz);
|
||||
|
||||
if (packet_sz >= 64) {
|
||||
csr |= MUSB_HSDMA_BURSTMODE_INCR16
|
||||
<< MUSB_HSDMA_BURSTMODE_SHIFT;
|
||||
} else if (packet_sz >= 32) {
|
||||
csr |= MUSB_HSDMA_BURSTMODE_INCR8
|
||||
<< MUSB_HSDMA_BURSTMODE_SHIFT;
|
||||
} else if (packet_sz >= 16) {
|
||||
csr |= MUSB_HSDMA_BURSTMODE_INCR4
|
||||
<< MUSB_HSDMA_BURSTMODE_SHIFT;
|
||||
}
|
||||
}
|
||||
|
||||
csr |= (pImplChannel->epnum << MUSB_HSDMA_ENDPOINT_SHIFT)
|
||||
| (1 << MUSB_HSDMA_ENABLE_SHIFT)
|
||||
| (1 << MUSB_HSDMA_IRQENABLE_SHIFT)
|
||||
| (pImplChannel->transmit
|
||||
? (1 << MUSB_HSDMA_TRANSMIT_SHIFT)
|
||||
: 0);
|
||||
|
||||
/* address/count */
|
||||
musb_writel(mbase,
|
||||
MUSB_HSDMA_CHANNEL_OFFSET(bChannel, MUSB_HSDMA_ADDRESS),
|
||||
dma_addr);
|
||||
musb_writel(mbase,
|
||||
MUSB_HSDMA_CHANNEL_OFFSET(bChannel, MUSB_HSDMA_COUNT),
|
||||
len);
|
||||
|
||||
/* control (this should start things) */
|
||||
musb_writew(mbase,
|
||||
MUSB_HSDMA_CHANNEL_OFFSET(bChannel, MUSB_HSDMA_CONTROL),
|
||||
csr);
|
||||
}
|
||||
|
||||
static int dma_channel_program(struct dma_channel *pChannel,
|
||||
u16 packet_sz, u8 mode,
|
||||
dma_addr_t dma_addr, u32 len)
|
||||
{
|
||||
struct musb_dma_channel *pImplChannel =
|
||||
(struct musb_dma_channel *) pChannel->private_data;
|
||||
|
||||
DBG(2, "ep%d-%s pkt_sz %d, dma_addr 0x%x length %d, mode %d\n",
|
||||
pImplChannel->epnum,
|
||||
pImplChannel->transmit ? "Tx" : "Rx",
|
||||
packet_sz, dma_addr, len, mode);
|
||||
|
||||
BUG_ON(pChannel->status == MUSB_DMA_STATUS_UNKNOWN ||
|
||||
pChannel->status == MUSB_DMA_STATUS_BUSY);
|
||||
|
||||
pChannel->actual_len = 0;
|
||||
pImplChannel->dwStartAddress = dma_addr;
|
||||
pImplChannel->len = len;
|
||||
pImplChannel->wMaxPacketSize = packet_sz;
|
||||
pChannel->status = MUSB_DMA_STATUS_BUSY;
|
||||
|
||||
if ((mode == 1) && (len >= packet_sz))
|
||||
configure_channel(pChannel, packet_sz, 1, dma_addr, len);
|
||||
else
|
||||
configure_channel(pChannel, packet_sz, 0, dma_addr, len);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int dma_channel_abort(struct dma_channel *pChannel)
|
||||
{
|
||||
struct musb_dma_channel *pImplChannel =
|
||||
(struct musb_dma_channel *) pChannel->private_data;
|
||||
u8 bChannel = pImplChannel->bIndex;
|
||||
void __iomem *mbase = pImplChannel->controller->pCoreBase;
|
||||
u16 csr;
|
||||
|
||||
if (pChannel->status == MUSB_DMA_STATUS_BUSY) {
|
||||
if (pImplChannel->transmit) {
|
||||
|
||||
csr = musb_readw(mbase,
|
||||
MUSB_EP_OFFSET(pImplChannel->epnum,
|
||||
MUSB_TXCSR));
|
||||
csr &= ~(MUSB_TXCSR_AUTOSET |
|
||||
MUSB_TXCSR_DMAENAB |
|
||||
MUSB_TXCSR_DMAMODE);
|
||||
musb_writew(mbase,
|
||||
MUSB_EP_OFFSET(pImplChannel->epnum,
|
||||
MUSB_TXCSR),
|
||||
csr);
|
||||
} else {
|
||||
csr = musb_readw(mbase,
|
||||
MUSB_EP_OFFSET(pImplChannel->epnum,
|
||||
MUSB_RXCSR));
|
||||
csr &= ~(MUSB_RXCSR_AUTOCLEAR |
|
||||
MUSB_RXCSR_DMAENAB |
|
||||
MUSB_RXCSR_DMAMODE);
|
||||
musb_writew(mbase,
|
||||
MUSB_EP_OFFSET(pImplChannel->epnum,
|
||||
MUSB_RXCSR),
|
||||
csr);
|
||||
}
|
||||
|
||||
musb_writew(mbase,
|
||||
MUSB_HSDMA_CHANNEL_OFFSET(bChannel, MUSB_HSDMA_CONTROL),
|
||||
0);
|
||||
musb_writel(mbase,
|
||||
MUSB_HSDMA_CHANNEL_OFFSET(bChannel, MUSB_HSDMA_ADDRESS),
|
||||
0);
|
||||
musb_writel(mbase,
|
||||
MUSB_HSDMA_CHANNEL_OFFSET(bChannel, MUSB_HSDMA_COUNT),
|
||||
0);
|
||||
|
||||
pChannel->status = MUSB_DMA_STATUS_FREE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t dma_controller_irq(int irq, void *private_data)
|
||||
{
|
||||
struct musb_dma_controller *controller =
|
||||
(struct musb_dma_controller *)private_data;
|
||||
struct musb_dma_channel *pImplChannel;
|
||||
struct musb *musb = controller->pDmaPrivate;
|
||||
void __iomem *mbase = controller->pCoreBase;
|
||||
struct dma_channel *pChannel;
|
||||
u8 bChannel;
|
||||
u16 csr;
|
||||
u32 dwAddress;
|
||||
u8 int_hsdma;
|
||||
irqreturn_t retval = IRQ_NONE;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&musb->lock, flags);
|
||||
|
||||
int_hsdma = musb_readb(mbase, MUSB_HSDMA_INTR);
|
||||
if (!int_hsdma)
|
||||
goto done;
|
||||
|
||||
for (bChannel = 0; bChannel < MUSB_HSDMA_CHANNELS; bChannel++) {
|
||||
if (int_hsdma & (1 << bChannel)) {
|
||||
pImplChannel = (struct musb_dma_channel *)
|
||||
&(controller->aChannel[bChannel]);
|
||||
pChannel = &pImplChannel->Channel;
|
||||
|
||||
csr = musb_readw(mbase,
|
||||
MUSB_HSDMA_CHANNEL_OFFSET(bChannel,
|
||||
MUSB_HSDMA_CONTROL));
|
||||
|
||||
if (csr & (1 << MUSB_HSDMA_BUSERROR_SHIFT))
|
||||
pImplChannel->Channel.status =
|
||||
MUSB_DMA_STATUS_BUS_ABORT;
|
||||
else {
|
||||
u8 devctl;
|
||||
|
||||
dwAddress = musb_readl(mbase,
|
||||
MUSB_HSDMA_CHANNEL_OFFSET(
|
||||
bChannel,
|
||||
MUSB_HSDMA_ADDRESS));
|
||||
pChannel->actual_len = dwAddress
|
||||
- pImplChannel->dwStartAddress;
|
||||
|
||||
DBG(2, "ch %p, 0x%x -> 0x%x (%d / %d) %s\n",
|
||||
pChannel, pImplChannel->dwStartAddress,
|
||||
dwAddress, pChannel->actual_len,
|
||||
pImplChannel->len,
|
||||
(pChannel->actual_len
|
||||
< pImplChannel->len) ?
|
||||
"=> reconfig 0" : "=> complete");
|
||||
|
||||
devctl = musb_readb(mbase, MUSB_DEVCTL);
|
||||
|
||||
pChannel->status = MUSB_DMA_STATUS_FREE;
|
||||
|
||||
/* completed */
|
||||
if ((devctl & MUSB_DEVCTL_HM)
|
||||
&& (pImplChannel->transmit)
|
||||
&& ((pChannel->desired_mode == 0)
|
||||
|| (pChannel->actual_len &
|
||||
(pImplChannel->wMaxPacketSize - 1)))
|
||||
) {
|
||||
/* Send out the packet */
|
||||
musb_ep_select(mbase,
|
||||
pImplChannel->epnum);
|
||||
musb_writew(mbase, MUSB_EP_OFFSET(
|
||||
pImplChannel->epnum,
|
||||
MUSB_TXCSR),
|
||||
MUSB_TXCSR_TXPKTRDY);
|
||||
} else
|
||||
musb_dma_completion(
|
||||
musb,
|
||||
pImplChannel->epnum,
|
||||
pImplChannel->transmit);
|
||||
}
|
||||
}
|
||||
}
|
||||
retval = IRQ_HANDLED;
|
||||
done:
|
||||
spin_unlock_irqrestore(&musb->lock, flags);
|
||||
return retval;
|
||||
}
|
||||
|
||||
void dma_controller_destroy(struct dma_controller *c)
|
||||
{
|
||||
struct musb_dma_controller *controller;
|
||||
|
||||
controller = container_of(c, struct musb_dma_controller, Controller);
|
||||
if (!controller)
|
||||
return;
|
||||
|
||||
if (controller->irq)
|
||||
free_irq(controller->irq, c);
|
||||
|
||||
kfree(controller);
|
||||
}
|
||||
|
||||
struct dma_controller *__init
|
||||
dma_controller_create(struct musb *musb, void __iomem *pCoreBase)
|
||||
{
|
||||
struct musb_dma_controller *controller;
|
||||
struct device *dev = musb->controller;
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
int irq = platform_get_irq(pdev, 1);
|
||||
|
||||
if (irq == 0) {
|
||||
dev_err(dev, "No DMA interrupt line!\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
controller = kzalloc(sizeof(struct musb_dma_controller), GFP_KERNEL);
|
||||
if (!controller)
|
||||
return NULL;
|
||||
|
||||
controller->bChannelCount = MUSB_HSDMA_CHANNELS;
|
||||
controller->pDmaPrivate = musb;
|
||||
controller->pCoreBase = pCoreBase;
|
||||
|
||||
controller->Controller.start = dma_controller_start;
|
||||
controller->Controller.stop = dma_controller_stop;
|
||||
controller->Controller.channel_alloc = dma_channel_allocate;
|
||||
controller->Controller.channel_release = dma_channel_release;
|
||||
controller->Controller.channel_program = dma_channel_program;
|
||||
controller->Controller.channel_abort = dma_channel_abort;
|
||||
|
||||
if (request_irq(irq, dma_controller_irq, IRQF_DISABLED,
|
||||
musb->controller->bus_id, &controller->Controller)) {
|
||||
dev_err(dev, "request_irq %d failed!\n", irq);
|
||||
dma_controller_destroy(&controller->Controller);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
controller->irq = irq;
|
||||
|
||||
return &controller->Controller;
|
||||
}
|
324
drivers/usb/musb/omap2430.c
Normal file
324
drivers/usb/musb/omap2430.c
Normal file
@ -0,0 +1,324 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2007 by Texas Instruments
|
||||
* Some code has been taken from tusb6010.c
|
||||
* Copyrights for that are attributable to:
|
||||
* Copyright (C) 2006 Nokia Corporation
|
||||
* Jarkko Nikula <jarkko.nikula@nokia.com>
|
||||
* Tony Lindgren <tony@atomide.com>
|
||||
*
|
||||
* This file is part of the Inventra Controller Driver for Linux.
|
||||
*
|
||||
* The Inventra Controller Driver for Linux is free software; you
|
||||
* can redistribute it and/or modify it under the terms of the GNU
|
||||
* General Public License version 2 as published by the Free Software
|
||||
* Foundation.
|
||||
*
|
||||
* The Inventra Controller Driver for Linux is distributed in
|
||||
* the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with The Inventra Controller Driver for Linux ; if not,
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place,
|
||||
* Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include <asm/mach-types.h>
|
||||
#include <asm/arch/hardware.h>
|
||||
#include <asm/arch/mux.h>
|
||||
|
||||
#include "musb_core.h"
|
||||
#include "omap2430.h"
|
||||
|
||||
#ifdef CONFIG_ARCH_OMAP3430
|
||||
#define get_cpu_rev() 2
|
||||
#endif
|
||||
|
||||
#define MUSB_TIMEOUT_A_WAIT_BCON 1100
|
||||
|
||||
static struct timer_list musb_idle_timer;
|
||||
|
||||
static void musb_do_idle(unsigned long _musb)
|
||||
{
|
||||
struct musb *musb = (void *)_musb;
|
||||
unsigned long flags;
|
||||
u8 power;
|
||||
u8 devctl;
|
||||
|
||||
devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
|
||||
|
||||
spin_lock_irqsave(&musb->lock, flags);
|
||||
|
||||
switch (musb->xceiv.state) {
|
||||
case OTG_STATE_A_WAIT_BCON:
|
||||
devctl &= ~MUSB_DEVCTL_SESSION;
|
||||
musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
|
||||
|
||||
devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
|
||||
if (devctl & MUSB_DEVCTL_BDEVICE) {
|
||||
musb->xceiv.state = OTG_STATE_B_IDLE;
|
||||
MUSB_DEV_MODE(musb);
|
||||
} else {
|
||||
musb->xceiv.state = OTG_STATE_A_IDLE;
|
||||
MUSB_HST_MODE(musb);
|
||||
}
|
||||
break;
|
||||
#ifdef CONFIG_USB_MUSB_HDRC_HCD
|
||||
case OTG_STATE_A_SUSPEND:
|
||||
/* finish RESUME signaling? */
|
||||
if (musb->port1_status & MUSB_PORT_STAT_RESUME) {
|
||||
power = musb_readb(musb->mregs, MUSB_POWER);
|
||||
power &= ~MUSB_POWER_RESUME;
|
||||
DBG(1, "root port resume stopped, power %02x\n", power);
|
||||
musb_writeb(musb->mregs, MUSB_POWER, power);
|
||||
musb->is_active = 1;
|
||||
musb->port1_status &= ~(USB_PORT_STAT_SUSPEND
|
||||
| MUSB_PORT_STAT_RESUME);
|
||||
musb->port1_status |= USB_PORT_STAT_C_SUSPEND << 16;
|
||||
usb_hcd_poll_rh_status(musb_to_hcd(musb));
|
||||
/* NOTE: it might really be A_WAIT_BCON ... */
|
||||
musb->xceiv.state = OTG_STATE_A_HOST;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_USB_MUSB_HDRC_HCD
|
||||
case OTG_STATE_A_HOST:
|
||||
devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
|
||||
if (devctl & MUSB_DEVCTL_BDEVICE)
|
||||
musb->xceiv.state = OTG_STATE_B_IDLE;
|
||||
else
|
||||
musb->xceiv.state = OTG_STATE_A_WAIT_BCON;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
spin_unlock_irqrestore(&musb->lock, flags);
|
||||
}
|
||||
|
||||
|
||||
void musb_platform_try_idle(struct musb *musb, unsigned long timeout)
|
||||
{
|
||||
unsigned long default_timeout = jiffies + msecs_to_jiffies(3);
|
||||
static unsigned long last_timer;
|
||||
|
||||
if (timeout == 0)
|
||||
timeout = default_timeout;
|
||||
|
||||
/* Never idle if active, or when VBUS timeout is not set as host */
|
||||
if (musb->is_active || ((musb->a_wait_bcon == 0)
|
||||
&& (musb->xceiv.state == OTG_STATE_A_WAIT_BCON))) {
|
||||
DBG(4, "%s active, deleting timer\n", otg_state_string(musb));
|
||||
del_timer(&musb_idle_timer);
|
||||
last_timer = jiffies;
|
||||
return;
|
||||
}
|
||||
|
||||
if (time_after(last_timer, timeout)) {
|
||||
if (!timer_pending(&musb_idle_timer))
|
||||
last_timer = timeout;
|
||||
else {
|
||||
DBG(4, "Longer idle timer already pending, ignoring\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
last_timer = timeout;
|
||||
|
||||
DBG(4, "%s inactive, for idle timer for %lu ms\n",
|
||||
otg_state_string(musb),
|
||||
(unsigned long)jiffies_to_msecs(timeout - jiffies));
|
||||
mod_timer(&musb_idle_timer, timeout);
|
||||
}
|
||||
|
||||
void musb_platform_enable(struct musb *musb)
|
||||
{
|
||||
}
|
||||
void musb_platform_disable(struct musb *musb)
|
||||
{
|
||||
}
|
||||
static void omap_vbus_power(struct musb *musb, int is_on, int sleeping)
|
||||
{
|
||||
}
|
||||
|
||||
static void omap_set_vbus(struct musb *musb, int is_on)
|
||||
{
|
||||
u8 devctl;
|
||||
/* HDRC controls CPEN, but beware current surges during device
|
||||
* connect. They can trigger transient overcurrent conditions
|
||||
* that must be ignored.
|
||||
*/
|
||||
|
||||
devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
|
||||
|
||||
if (is_on) {
|
||||
musb->is_active = 1;
|
||||
musb->xceiv.default_a = 1;
|
||||
musb->xceiv.state = OTG_STATE_A_WAIT_VRISE;
|
||||
devctl |= MUSB_DEVCTL_SESSION;
|
||||
|
||||
MUSB_HST_MODE(musb);
|
||||
} else {
|
||||
musb->is_active = 0;
|
||||
|
||||
/* NOTE: we're skipping A_WAIT_VFALL -> A_IDLE and
|
||||
* jumping right to B_IDLE...
|
||||
*/
|
||||
|
||||
musb->xceiv.default_a = 0;
|
||||
musb->xceiv.state = OTG_STATE_B_IDLE;
|
||||
devctl &= ~MUSB_DEVCTL_SESSION;
|
||||
|
||||
MUSB_DEV_MODE(musb);
|
||||
}
|
||||
musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
|
||||
|
||||
DBG(1, "VBUS %s, devctl %02x "
|
||||
/* otg %3x conf %08x prcm %08x */ "\n",
|
||||
otg_state_string(musb),
|
||||
musb_readb(musb->mregs, MUSB_DEVCTL));
|
||||
}
|
||||
static int omap_set_power(struct otg_transceiver *x, unsigned mA)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int musb_platform_resume(struct musb *musb);
|
||||
|
||||
void musb_platform_set_mode(struct musb *musb, u8 musb_mode)
|
||||
{
|
||||
u8 devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
|
||||
|
||||
devctl |= MUSB_DEVCTL_SESSION;
|
||||
musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
|
||||
|
||||
switch (musb_mode) {
|
||||
case MUSB_HOST:
|
||||
otg_set_host(&musb->xceiv, musb->xceiv.host);
|
||||
break;
|
||||
case MUSB_PERIPHERAL:
|
||||
otg_set_peripheral(&musb->xceiv, musb->xceiv.gadget);
|
||||
break;
|
||||
case MUSB_OTG:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int __init musb_platform_init(struct musb *musb)
|
||||
{
|
||||
u32 l;
|
||||
|
||||
#if defined(CONFIG_ARCH_OMAP2430)
|
||||
omap_cfg_reg(AE5_2430_USB0HS_STP);
|
||||
#endif
|
||||
|
||||
musb_platform_resume(musb);
|
||||
|
||||
l = omap_readl(OTG_SYSCONFIG);
|
||||
l &= ~ENABLEWAKEUP; /* disable wakeup */
|
||||
l &= ~NOSTDBY; /* remove possible nostdby */
|
||||
l |= SMARTSTDBY; /* enable smart standby */
|
||||
l &= ~AUTOIDLE; /* disable auto idle */
|
||||
l &= ~NOIDLE; /* remove possible noidle */
|
||||
l |= SMARTIDLE; /* enable smart idle */
|
||||
l |= AUTOIDLE; /* enable auto idle */
|
||||
omap_writel(l, OTG_SYSCONFIG);
|
||||
|
||||
l = omap_readl(OTG_INTERFSEL);
|
||||
l |= ULPI_12PIN;
|
||||
omap_writel(l, OTG_INTERFSEL);
|
||||
|
||||
pr_debug("HS USB OTG: revision 0x%x, sysconfig 0x%02x, "
|
||||
"sysstatus 0x%x, intrfsel 0x%x, simenable 0x%x\n",
|
||||
omap_readl(OTG_REVISION), omap_readl(OTG_SYSCONFIG),
|
||||
omap_readl(OTG_SYSSTATUS), omap_readl(OTG_INTERFSEL),
|
||||
omap_readl(OTG_SIMENABLE));
|
||||
|
||||
omap_vbus_power(musb, musb->board_mode == MUSB_HOST, 1);
|
||||
|
||||
if (is_host_enabled(musb))
|
||||
musb->board_set_vbus = omap_set_vbus;
|
||||
if (is_peripheral_enabled(musb))
|
||||
musb->xceiv.set_power = omap_set_power;
|
||||
musb->a_wait_bcon = MUSB_TIMEOUT_A_WAIT_BCON;
|
||||
|
||||
setup_timer(&musb_idle_timer, musb_do_idle, (unsigned long) musb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int musb_platform_suspend(struct musb *musb)
|
||||
{
|
||||
u32 l;
|
||||
|
||||
if (!musb->clock)
|
||||
return 0;
|
||||
|
||||
/* in any role */
|
||||
l = omap_readl(OTG_FORCESTDBY);
|
||||
l |= ENABLEFORCE; /* enable MSTANDBY */
|
||||
omap_writel(l, OTG_FORCESTDBY);
|
||||
|
||||
l = omap_readl(OTG_SYSCONFIG);
|
||||
l |= ENABLEWAKEUP; /* enable wakeup */
|
||||
omap_writel(l, OTG_SYSCONFIG);
|
||||
|
||||
if (musb->xceiv.set_suspend)
|
||||
musb->xceiv.set_suspend(&musb->xceiv, 1);
|
||||
|
||||
if (musb->set_clock)
|
||||
musb->set_clock(musb->clock, 0);
|
||||
else
|
||||
clk_disable(musb->clock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int musb_platform_resume(struct musb *musb)
|
||||
{
|
||||
u32 l;
|
||||
|
||||
if (!musb->clock)
|
||||
return 0;
|
||||
|
||||
if (musb->xceiv.set_suspend)
|
||||
musb->xceiv.set_suspend(&musb->xceiv, 0);
|
||||
|
||||
if (musb->set_clock)
|
||||
musb->set_clock(musb->clock, 1);
|
||||
else
|
||||
clk_enable(musb->clock);
|
||||
|
||||
l = omap_readl(OTG_SYSCONFIG);
|
||||
l &= ~ENABLEWAKEUP; /* disable wakeup */
|
||||
omap_writel(l, OTG_SYSCONFIG);
|
||||
|
||||
l = omap_readl(OTG_FORCESTDBY);
|
||||
l &= ~ENABLEFORCE; /* disable MSTANDBY */
|
||||
omap_writel(l, OTG_FORCESTDBY);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int musb_platform_exit(struct musb *musb)
|
||||
{
|
||||
|
||||
omap_vbus_power(musb, 0 /*off*/, 1);
|
||||
|
||||
musb_platform_suspend(musb);
|
||||
|
||||
clk_put(musb->clock);
|
||||
musb->clock = 0;
|
||||
|
||||
return 0;
|
||||
}
|
56
drivers/usb/musb/omap2430.h
Normal file
56
drivers/usb/musb/omap2430.h
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2006 by Texas Instruments
|
||||
*
|
||||
* The Inventra Controller Driver for Linux is free software; you
|
||||
* can redistribute it and/or modify it under the terms of the GNU
|
||||
* General Public License version 2 as published by the Free Software
|
||||
* Foundation.
|
||||
*/
|
||||
|
||||
#ifndef __MUSB_OMAP243X_H__
|
||||
#define __MUSB_OMAP243X_H__
|
||||
|
||||
#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3430)
|
||||
#include <asm/arch/hardware.h>
|
||||
#include <asm/arch/usb.h>
|
||||
|
||||
/*
|
||||
* OMAP2430-specific definitions
|
||||
*/
|
||||
|
||||
#define MENTOR_BASE_OFFSET 0
|
||||
#if defined(CONFIG_ARCH_OMAP2430)
|
||||
#define OMAP_HSOTG_BASE (OMAP243X_HS_BASE)
|
||||
#elif defined(CONFIG_ARCH_OMAP3430)
|
||||
#define OMAP_HSOTG_BASE (OMAP34XX_HSUSB_OTG_BASE)
|
||||
#endif
|
||||
#define OMAP_HSOTG(offset) (OMAP_HSOTG_BASE + 0x400 + (offset))
|
||||
#define OTG_REVISION OMAP_HSOTG(0x0)
|
||||
#define OTG_SYSCONFIG OMAP_HSOTG(0x4)
|
||||
# define MIDLEMODE 12 /* bit position */
|
||||
# define FORCESTDBY (0 << MIDLEMODE)
|
||||
# define NOSTDBY (1 << MIDLEMODE)
|
||||
# define SMARTSTDBY (2 << MIDLEMODE)
|
||||
# define SIDLEMODE 3 /* bit position */
|
||||
# define FORCEIDLE (0 << SIDLEMODE)
|
||||
# define NOIDLE (1 << SIDLEMODE)
|
||||
# define SMARTIDLE (2 << SIDLEMODE)
|
||||
# define ENABLEWAKEUP (1 << 2)
|
||||
# define SOFTRST (1 << 1)
|
||||
# define AUTOIDLE (1 << 0)
|
||||
#define OTG_SYSSTATUS OMAP_HSOTG(0x8)
|
||||
# define RESETDONE (1 << 0)
|
||||
#define OTG_INTERFSEL OMAP_HSOTG(0xc)
|
||||
# define EXTCP (1 << 2)
|
||||
# define PHYSEL 0 /* bit position */
|
||||
# define UTMI_8BIT (0 << PHYSEL)
|
||||
# define ULPI_12PIN (1 << PHYSEL)
|
||||
# define ULPI_8PIN (2 << PHYSEL)
|
||||
#define OTG_SIMENABLE OMAP_HSOTG(0x10)
|
||||
# define TM1 (1 << 0)
|
||||
#define OTG_FORCESTDBY OMAP_HSOTG(0x14)
|
||||
# define ENABLEFORCE (1 << 0)
|
||||
|
||||
#endif /* CONFIG_ARCH_OMAP2430 */
|
||||
|
||||
#endif /* __MUSB_OMAP243X_H__ */
|
1151
drivers/usb/musb/tusb6010.c
Normal file
1151
drivers/usb/musb/tusb6010.c
Normal file
File diff suppressed because it is too large
Load Diff
233
drivers/usb/musb/tusb6010.h
Normal file
233
drivers/usb/musb/tusb6010.h
Normal file
@ -0,0 +1,233 @@
|
||||
/*
|
||||
* Definitions for TUSB6010 USB 2.0 OTG Dual Role controller
|
||||
*
|
||||
* Copyright (C) 2006 Nokia Corporation
|
||||
* Jarkko Nikula <jarkko.nikula@nokia.com>
|
||||
* Tony Lindgren <tony@atomide.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef __TUSB6010_H__
|
||||
#define __TUSB6010_H__
|
||||
|
||||
extern u8 tusb_get_revision(struct musb *musb);
|
||||
|
||||
#ifdef CONFIG_USB_TUSB6010
|
||||
#define musb_in_tusb() 1
|
||||
#else
|
||||
#define musb_in_tusb() 0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_TUSB_OMAP_DMA
|
||||
#define tusb_dma_omap() 1
|
||||
#else
|
||||
#define tusb_dma_omap() 0
|
||||
#endif
|
||||
|
||||
/* VLYNQ control register. 32-bit at offset 0x000 */
|
||||
#define TUSB_VLYNQ_CTRL 0x004
|
||||
|
||||
/* Mentor Graphics OTG core registers. 8,- 16- and 32-bit at offset 0x400 */
|
||||
#define TUSB_BASE_OFFSET 0x400
|
||||
|
||||
/* FIFO registers 32-bit at offset 0x600 */
|
||||
#define TUSB_FIFO_BASE 0x600
|
||||
|
||||
/* Device System & Control registers. 32-bit at offset 0x800 */
|
||||
#define TUSB_SYS_REG_BASE 0x800
|
||||
|
||||
#define TUSB_DEV_CONF (TUSB_SYS_REG_BASE + 0x000)
|
||||
#define TUSB_DEV_CONF_USB_HOST_MODE (1 << 16)
|
||||
#define TUSB_DEV_CONF_PROD_TEST_MODE (1 << 15)
|
||||
#define TUSB_DEV_CONF_SOFT_ID (1 << 1)
|
||||
#define TUSB_DEV_CONF_ID_SEL (1 << 0)
|
||||
|
||||
#define TUSB_PHY_OTG_CTRL_ENABLE (TUSB_SYS_REG_BASE + 0x004)
|
||||
#define TUSB_PHY_OTG_CTRL (TUSB_SYS_REG_BASE + 0x008)
|
||||
#define TUSB_PHY_OTG_CTRL_WRPROTECT (0xa5 << 24)
|
||||
#define TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP (1 << 23)
|
||||
#define TUSB_PHY_OTG_CTRL_OTG_VBUS_DET_EN (1 << 19)
|
||||
#define TUSB_PHY_OTG_CTRL_OTG_SESS_END_EN (1 << 18)
|
||||
#define TUSB_PHY_OTG_CTRL_TESTM2 (1 << 17)
|
||||
#define TUSB_PHY_OTG_CTRL_TESTM1 (1 << 16)
|
||||
#define TUSB_PHY_OTG_CTRL_TESTM0 (1 << 15)
|
||||
#define TUSB_PHY_OTG_CTRL_TX_DATA2 (1 << 14)
|
||||
#define TUSB_PHY_OTG_CTRL_TX_GZ2 (1 << 13)
|
||||
#define TUSB_PHY_OTG_CTRL_TX_ENABLE2 (1 << 12)
|
||||
#define TUSB_PHY_OTG_CTRL_DM_PULLDOWN (1 << 11)
|
||||
#define TUSB_PHY_OTG_CTRL_DP_PULLDOWN (1 << 10)
|
||||
#define TUSB_PHY_OTG_CTRL_OSC_EN (1 << 9)
|
||||
#define TUSB_PHY_OTG_CTRL_PHYREF_CLKSEL(v) (((v) & 3) << 7)
|
||||
#define TUSB_PHY_OTG_CTRL_PD (1 << 6)
|
||||
#define TUSB_PHY_OTG_CTRL_PLL_ON (1 << 5)
|
||||
#define TUSB_PHY_OTG_CTRL_EXT_RPU (1 << 4)
|
||||
#define TUSB_PHY_OTG_CTRL_PWR_GOOD (1 << 3)
|
||||
#define TUSB_PHY_OTG_CTRL_RESET (1 << 2)
|
||||
#define TUSB_PHY_OTG_CTRL_SUSPENDM (1 << 1)
|
||||
#define TUSB_PHY_OTG_CTRL_CLK_MODE (1 << 0)
|
||||
|
||||
/*OTG status register */
|
||||
#define TUSB_DEV_OTG_STAT (TUSB_SYS_REG_BASE + 0x00c)
|
||||
#define TUSB_DEV_OTG_STAT_PWR_CLK_GOOD (1 << 8)
|
||||
#define TUSB_DEV_OTG_STAT_SESS_END (1 << 7)
|
||||
#define TUSB_DEV_OTG_STAT_SESS_VALID (1 << 6)
|
||||
#define TUSB_DEV_OTG_STAT_VBUS_VALID (1 << 5)
|
||||
#define TUSB_DEV_OTG_STAT_VBUS_SENSE (1 << 4)
|
||||
#define TUSB_DEV_OTG_STAT_ID_STATUS (1 << 3)
|
||||
#define TUSB_DEV_OTG_STAT_HOST_DISCON (1 << 2)
|
||||
#define TUSB_DEV_OTG_STAT_LINE_STATE (3 << 0)
|
||||
#define TUSB_DEV_OTG_STAT_DP_ENABLE (1 << 1)
|
||||
#define TUSB_DEV_OTG_STAT_DM_ENABLE (1 << 0)
|
||||
|
||||
#define TUSB_DEV_OTG_TIMER (TUSB_SYS_REG_BASE + 0x010)
|
||||
# define TUSB_DEV_OTG_TIMER_ENABLE (1 << 31)
|
||||
# define TUSB_DEV_OTG_TIMER_VAL(v) ((v) & 0x07ffffff)
|
||||
#define TUSB_PRCM_REV (TUSB_SYS_REG_BASE + 0x014)
|
||||
|
||||
/* PRCM configuration register */
|
||||
#define TUSB_PRCM_CONF (TUSB_SYS_REG_BASE + 0x018)
|
||||
#define TUSB_PRCM_CONF_SFW_CPEN (1 << 24)
|
||||
#define TUSB_PRCM_CONF_SYS_CLKSEL(v) (((v) & 3) << 16)
|
||||
|
||||
/* PRCM management register */
|
||||
#define TUSB_PRCM_MNGMT (TUSB_SYS_REG_BASE + 0x01c)
|
||||
#define TUSB_PRCM_MNGMT_SRP_FIX_TIMER(v) (((v) & 0xf) << 25)
|
||||
#define TUSB_PRCM_MNGMT_SRP_FIX_EN (1 << 24)
|
||||
#define TUSB_PRCM_MNGMT_VBUS_VALID_TIMER(v) (((v) & 0xf) << 20)
|
||||
#define TUSB_PRCM_MNGMT_VBUS_VALID_FLT_EN (1 << 19)
|
||||
#define TUSB_PRCM_MNGMT_DFT_CLK_DIS (1 << 18)
|
||||
#define TUSB_PRCM_MNGMT_VLYNQ_CLK_DIS (1 << 17)
|
||||
#define TUSB_PRCM_MNGMT_OTG_SESS_END_EN (1 << 10)
|
||||
#define TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN (1 << 9)
|
||||
#define TUSB_PRCM_MNGMT_OTG_ID_PULLUP (1 << 8)
|
||||
#define TUSB_PRCM_MNGMT_15_SW_EN (1 << 4)
|
||||
#define TUSB_PRCM_MNGMT_33_SW_EN (1 << 3)
|
||||
#define TUSB_PRCM_MNGMT_5V_CPEN (1 << 2)
|
||||
#define TUSB_PRCM_MNGMT_PM_IDLE (1 << 1)
|
||||
#define TUSB_PRCM_MNGMT_DEV_IDLE (1 << 0)
|
||||
|
||||
/* Wake-up source clear and mask registers */
|
||||
#define TUSB_PRCM_WAKEUP_SOURCE (TUSB_SYS_REG_BASE + 0x020)
|
||||
#define TUSB_PRCM_WAKEUP_CLEAR (TUSB_SYS_REG_BASE + 0x028)
|
||||
#define TUSB_PRCM_WAKEUP_MASK (TUSB_SYS_REG_BASE + 0x02c)
|
||||
#define TUSB_PRCM_WAKEUP_RESERVED_BITS (0xffffe << 13)
|
||||
#define TUSB_PRCM_WGPIO_7 (1 << 12)
|
||||
#define TUSB_PRCM_WGPIO_6 (1 << 11)
|
||||
#define TUSB_PRCM_WGPIO_5 (1 << 10)
|
||||
#define TUSB_PRCM_WGPIO_4 (1 << 9)
|
||||
#define TUSB_PRCM_WGPIO_3 (1 << 8)
|
||||
#define TUSB_PRCM_WGPIO_2 (1 << 7)
|
||||
#define TUSB_PRCM_WGPIO_1 (1 << 6)
|
||||
#define TUSB_PRCM_WGPIO_0 (1 << 5)
|
||||
#define TUSB_PRCM_WHOSTDISCON (1 << 4) /* Host disconnect */
|
||||
#define TUSB_PRCM_WBUS (1 << 3) /* USB bus resume */
|
||||
#define TUSB_PRCM_WNORCS (1 << 2) /* NOR chip select */
|
||||
#define TUSB_PRCM_WVBUS (1 << 1) /* OTG PHY VBUS */
|
||||
#define TUSB_PRCM_WID (1 << 0) /* OTG PHY ID detect */
|
||||
|
||||
#define TUSB_PULLUP_1_CTRL (TUSB_SYS_REG_BASE + 0x030)
|
||||
#define TUSB_PULLUP_2_CTRL (TUSB_SYS_REG_BASE + 0x034)
|
||||
#define TUSB_INT_CTRL_REV (TUSB_SYS_REG_BASE + 0x038)
|
||||
#define TUSB_INT_CTRL_CONF (TUSB_SYS_REG_BASE + 0x03c)
|
||||
#define TUSB_USBIP_INT_SRC (TUSB_SYS_REG_BASE + 0x040)
|
||||
#define TUSB_USBIP_INT_SET (TUSB_SYS_REG_BASE + 0x044)
|
||||
#define TUSB_USBIP_INT_CLEAR (TUSB_SYS_REG_BASE + 0x048)
|
||||
#define TUSB_USBIP_INT_MASK (TUSB_SYS_REG_BASE + 0x04c)
|
||||
#define TUSB_DMA_INT_SRC (TUSB_SYS_REG_BASE + 0x050)
|
||||
#define TUSB_DMA_INT_SET (TUSB_SYS_REG_BASE + 0x054)
|
||||
#define TUSB_DMA_INT_CLEAR (TUSB_SYS_REG_BASE + 0x058)
|
||||
#define TUSB_DMA_INT_MASK (TUSB_SYS_REG_BASE + 0x05c)
|
||||
#define TUSB_GPIO_INT_SRC (TUSB_SYS_REG_BASE + 0x060)
|
||||
#define TUSB_GPIO_INT_SET (TUSB_SYS_REG_BASE + 0x064)
|
||||
#define TUSB_GPIO_INT_CLEAR (TUSB_SYS_REG_BASE + 0x068)
|
||||
#define TUSB_GPIO_INT_MASK (TUSB_SYS_REG_BASE + 0x06c)
|
||||
|
||||
/* NOR flash interrupt source registers */
|
||||
#define TUSB_INT_SRC (TUSB_SYS_REG_BASE + 0x070)
|
||||
#define TUSB_INT_SRC_SET (TUSB_SYS_REG_BASE + 0x074)
|
||||
#define TUSB_INT_SRC_CLEAR (TUSB_SYS_REG_BASE + 0x078)
|
||||
#define TUSB_INT_MASK (TUSB_SYS_REG_BASE + 0x07c)
|
||||
#define TUSB_INT_SRC_TXRX_DMA_DONE (1 << 24)
|
||||
#define TUSB_INT_SRC_USB_IP_CORE (1 << 17)
|
||||
#define TUSB_INT_SRC_OTG_TIMEOUT (1 << 16)
|
||||
#define TUSB_INT_SRC_VBUS_SENSE_CHNG (1 << 15)
|
||||
#define TUSB_INT_SRC_ID_STATUS_CHNG (1 << 14)
|
||||
#define TUSB_INT_SRC_DEV_WAKEUP (1 << 13)
|
||||
#define TUSB_INT_SRC_DEV_READY (1 << 12)
|
||||
#define TUSB_INT_SRC_USB_IP_TX (1 << 9)
|
||||
#define TUSB_INT_SRC_USB_IP_RX (1 << 8)
|
||||
#define TUSB_INT_SRC_USB_IP_VBUS_ERR (1 << 7)
|
||||
#define TUSB_INT_SRC_USB_IP_VBUS_REQ (1 << 6)
|
||||
#define TUSB_INT_SRC_USB_IP_DISCON (1 << 5)
|
||||
#define TUSB_INT_SRC_USB_IP_CONN (1 << 4)
|
||||
#define TUSB_INT_SRC_USB_IP_SOF (1 << 3)
|
||||
#define TUSB_INT_SRC_USB_IP_RST_BABBLE (1 << 2)
|
||||
#define TUSB_INT_SRC_USB_IP_RESUME (1 << 1)
|
||||
#define TUSB_INT_SRC_USB_IP_SUSPEND (1 << 0)
|
||||
|
||||
/* NOR flash interrupt registers reserved bits. Must be written as 0 */
|
||||
#define TUSB_INT_MASK_RESERVED_17 (0x3fff << 17)
|
||||
#define TUSB_INT_MASK_RESERVED_13 (1 << 13)
|
||||
#define TUSB_INT_MASK_RESERVED_8 (0xf << 8)
|
||||
#define TUSB_INT_SRC_RESERVED_26 (0x1f << 26)
|
||||
#define TUSB_INT_SRC_RESERVED_18 (0x3f << 18)
|
||||
#define TUSB_INT_SRC_RESERVED_10 (0x03 << 10)
|
||||
|
||||
/* Reserved bits for NOR flash interrupt mask and clear register */
|
||||
#define TUSB_INT_MASK_RESERVED_BITS (TUSB_INT_MASK_RESERVED_17 | \
|
||||
TUSB_INT_MASK_RESERVED_13 | \
|
||||
TUSB_INT_MASK_RESERVED_8)
|
||||
|
||||
/* Reserved bits for NOR flash interrupt status register */
|
||||
#define TUSB_INT_SRC_RESERVED_BITS (TUSB_INT_SRC_RESERVED_26 | \
|
||||
TUSB_INT_SRC_RESERVED_18 | \
|
||||
TUSB_INT_SRC_RESERVED_10)
|
||||
|
||||
#define TUSB_GPIO_REV (TUSB_SYS_REG_BASE + 0x080)
|
||||
#define TUSB_GPIO_CONF (TUSB_SYS_REG_BASE + 0x084)
|
||||
#define TUSB_DMA_CTRL_REV (TUSB_SYS_REG_BASE + 0x100)
|
||||
#define TUSB_DMA_REQ_CONF (TUSB_SYS_REG_BASE + 0x104)
|
||||
#define TUSB_EP0_CONF (TUSB_SYS_REG_BASE + 0x108)
|
||||
#define TUSB_DMA_EP_MAP (TUSB_SYS_REG_BASE + 0x148)
|
||||
|
||||
/* Offsets from each ep base register */
|
||||
#define TUSB_EP_TX_OFFSET 0x10c /* EP_IN in docs */
|
||||
#define TUSB_EP_RX_OFFSET 0x14c /* EP_OUT in docs */
|
||||
#define TUSB_EP_MAX_PACKET_SIZE_OFFSET 0x188
|
||||
|
||||
#define TUSB_WAIT_COUNT (TUSB_SYS_REG_BASE + 0x1c8)
|
||||
#define TUSB_SCRATCH_PAD (TUSB_SYS_REG_BASE + 0x1c4)
|
||||
#define TUSB_PROD_TEST_RESET (TUSB_SYS_REG_BASE + 0x1d8)
|
||||
|
||||
/* Device System & Control register bitfields */
|
||||
#define TUSB_INT_CTRL_CONF_INT_RELCYC(v) (((v) & 0x7) << 18)
|
||||
#define TUSB_INT_CTRL_CONF_INT_POLARITY (1 << 17)
|
||||
#define TUSB_INT_CTRL_CONF_INT_MODE (1 << 16)
|
||||
#define TUSB_GPIO_CONF_DMAREQ(v) (((v) & 0x3f) << 24)
|
||||
#define TUSB_DMA_REQ_CONF_BURST_SIZE(v) (((v) & 3) << 26)
|
||||
#define TUSB_DMA_REQ_CONF_DMA_REQ_EN(v) (((v) & 0x3f) << 20)
|
||||
#define TUSB_DMA_REQ_CONF_DMA_REQ_ASSER(v) (((v) & 0xf) << 16)
|
||||
#define TUSB_EP0_CONFIG_SW_EN (1 << 8)
|
||||
#define TUSB_EP0_CONFIG_DIR_TX (1 << 7)
|
||||
#define TUSB_EP0_CONFIG_XFR_SIZE(v) ((v) & 0x7f)
|
||||
#define TUSB_EP_CONFIG_SW_EN (1 << 31)
|
||||
#define TUSB_EP_CONFIG_XFR_SIZE(v) ((v) & 0x7fffffff)
|
||||
#define TUSB_PROD_TEST_RESET_VAL 0xa596
|
||||
#define TUSB_EP_FIFO(ep) (TUSB_FIFO_BASE + (ep) * 0x20)
|
||||
|
||||
#define TUSB_DIDR1_LO (TUSB_SYS_REG_BASE + 0x1f8)
|
||||
#define TUSB_DIDR1_HI (TUSB_SYS_REG_BASE + 0x1fc)
|
||||
#define TUSB_DIDR1_HI_CHIP_REV(v) (((v) >> 17) & 0xf)
|
||||
#define TUSB_DIDR1_HI_REV_20 0
|
||||
#define TUSB_DIDR1_HI_REV_30 1
|
||||
#define TUSB_DIDR1_HI_REV_31 2
|
||||
|
||||
#define TUSB_REV_10 0x10
|
||||
#define TUSB_REV_20 0x20
|
||||
#define TUSB_REV_30 0x30
|
||||
#define TUSB_REV_31 0x31
|
||||
|
||||
#endif /* __TUSB6010_H__ */
|
719
drivers/usb/musb/tusb6010_omap.c
Normal file
719
drivers/usb/musb/tusb6010_omap.c
Normal file
@ -0,0 +1,719 @@
|
||||
/*
|
||||
* TUSB6010 USB 2.0 OTG Dual Role controller OMAP DMA interface
|
||||
*
|
||||
* Copyright (C) 2006 Nokia Corporation
|
||||
* Tony Lindgren <tony@atomide.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <asm/arch/dma.h>
|
||||
#include <asm/arch/mux.h>
|
||||
|
||||
#include "musb_core.h"
|
||||
|
||||
#define to_chdat(c) ((struct tusb_omap_dma_ch *)(c)->private_data)
|
||||
|
||||
#define MAX_DMAREQ 5 /* REVISIT: Really 6, but req5 not OK */
|
||||
|
||||
struct tusb_omap_dma_ch {
|
||||
struct musb *musb;
|
||||
void __iomem *tbase;
|
||||
unsigned long phys_offset;
|
||||
int epnum;
|
||||
u8 tx;
|
||||
struct musb_hw_ep *hw_ep;
|
||||
|
||||
int ch;
|
||||
s8 dmareq;
|
||||
s8 sync_dev;
|
||||
|
||||
struct tusb_omap_dma *tusb_dma;
|
||||
|
||||
void __iomem *dma_addr;
|
||||
|
||||
u32 len;
|
||||
u16 packet_sz;
|
||||
u16 transfer_packet_sz;
|
||||
u32 transfer_len;
|
||||
u32 completed_len;
|
||||
};
|
||||
|
||||
struct tusb_omap_dma {
|
||||
struct dma_controller controller;
|
||||
struct musb *musb;
|
||||
void __iomem *tbase;
|
||||
|
||||
int ch;
|
||||
s8 dmareq;
|
||||
s8 sync_dev;
|
||||
unsigned multichannel:1;
|
||||
};
|
||||
|
||||
static int tusb_omap_dma_start(struct dma_controller *c)
|
||||
{
|
||||
struct tusb_omap_dma *tusb_dma;
|
||||
|
||||
tusb_dma = container_of(c, struct tusb_omap_dma, controller);
|
||||
|
||||
/* DBG(3, "ep%i ch: %i\n", chdat->epnum, chdat->ch); */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tusb_omap_dma_stop(struct dma_controller *c)
|
||||
{
|
||||
struct tusb_omap_dma *tusb_dma;
|
||||
|
||||
tusb_dma = container_of(c, struct tusb_omap_dma, controller);
|
||||
|
||||
/* DBG(3, "ep%i ch: %i\n", chdat->epnum, chdat->ch); */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate dmareq0 to the current channel unless it's already taken
|
||||
*/
|
||||
static inline int tusb_omap_use_shared_dmareq(struct tusb_omap_dma_ch *chdat)
|
||||
{
|
||||
u32 reg = musb_readl(chdat->tbase, TUSB_DMA_EP_MAP);
|
||||
|
||||
if (reg != 0) {
|
||||
DBG(3, "ep%i dmareq0 is busy for ep%i\n",
|
||||
chdat->epnum, reg & 0xf);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
if (chdat->tx)
|
||||
reg = (1 << 4) | chdat->epnum;
|
||||
else
|
||||
reg = chdat->epnum;
|
||||
|
||||
musb_writel(chdat->tbase, TUSB_DMA_EP_MAP, reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void tusb_omap_free_shared_dmareq(struct tusb_omap_dma_ch *chdat)
|
||||
{
|
||||
u32 reg = musb_readl(chdat->tbase, TUSB_DMA_EP_MAP);
|
||||
|
||||
if ((reg & 0xf) != chdat->epnum) {
|
||||
printk(KERN_ERR "ep%i trying to release dmareq0 for ep%i\n",
|
||||
chdat->epnum, reg & 0xf);
|
||||
return;
|
||||
}
|
||||
musb_writel(chdat->tbase, TUSB_DMA_EP_MAP, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* See also musb_dma_completion in plat_uds.c and musb_g_[tx|rx]() in
|
||||
* musb_gadget.c.
|
||||
*/
|
||||
static void tusb_omap_dma_cb(int lch, u16 ch_status, void *data)
|
||||
{
|
||||
struct dma_channel *channel = (struct dma_channel *)data;
|
||||
struct tusb_omap_dma_ch *chdat = to_chdat(channel);
|
||||
struct tusb_omap_dma *tusb_dma = chdat->tusb_dma;
|
||||
struct musb *musb = chdat->musb;
|
||||
struct musb_hw_ep *hw_ep = chdat->hw_ep;
|
||||
void __iomem *ep_conf = hw_ep->conf;
|
||||
void __iomem *mbase = musb->mregs;
|
||||
unsigned long remaining, flags, pio;
|
||||
int ch;
|
||||
|
||||
spin_lock_irqsave(&musb->lock, flags);
|
||||
|
||||
if (tusb_dma->multichannel)
|
||||
ch = chdat->ch;
|
||||
else
|
||||
ch = tusb_dma->ch;
|
||||
|
||||
if (ch_status != OMAP_DMA_BLOCK_IRQ)
|
||||
printk(KERN_ERR "TUSB DMA error status: %i\n", ch_status);
|
||||
|
||||
DBG(3, "ep%i %s dma callback ch: %i status: %x\n",
|
||||
chdat->epnum, chdat->tx ? "tx" : "rx",
|
||||
ch, ch_status);
|
||||
|
||||
if (chdat->tx)
|
||||
remaining = musb_readl(ep_conf, TUSB_EP_TX_OFFSET);
|
||||
else
|
||||
remaining = musb_readl(ep_conf, TUSB_EP_RX_OFFSET);
|
||||
|
||||
remaining = TUSB_EP_CONFIG_XFR_SIZE(remaining);
|
||||
|
||||
/* HW issue #10: XFR_SIZE may get corrupt on DMA (both async & sync) */
|
||||
if (unlikely(remaining > chdat->transfer_len)) {
|
||||
DBG(2, "Corrupt %s dma ch%i XFR_SIZE: 0x%08lx\n",
|
||||
chdat->tx ? "tx" : "rx", chdat->ch,
|
||||
remaining);
|
||||
remaining = 0;
|
||||
}
|
||||
|
||||
channel->actual_len = chdat->transfer_len - remaining;
|
||||
pio = chdat->len - channel->actual_len;
|
||||
|
||||
DBG(3, "DMA remaining %lu/%u\n", remaining, chdat->transfer_len);
|
||||
|
||||
/* Transfer remaining 1 - 31 bytes */
|
||||
if (pio > 0 && pio < 32) {
|
||||
u8 *buf;
|
||||
|
||||
DBG(3, "Using PIO for remaining %lu bytes\n", pio);
|
||||
buf = phys_to_virt((u32)chdat->dma_addr) + chdat->transfer_len;
|
||||
if (chdat->tx) {
|
||||
dma_cache_maint(phys_to_virt((u32)chdat->dma_addr),
|
||||
chdat->transfer_len, DMA_TO_DEVICE);
|
||||
musb_write_fifo(hw_ep, pio, buf);
|
||||
} else {
|
||||
musb_read_fifo(hw_ep, pio, buf);
|
||||
dma_cache_maint(phys_to_virt((u32)chdat->dma_addr),
|
||||
chdat->transfer_len, DMA_FROM_DEVICE);
|
||||
}
|
||||
channel->actual_len += pio;
|
||||
}
|
||||
|
||||
if (!tusb_dma->multichannel)
|
||||
tusb_omap_free_shared_dmareq(chdat);
|
||||
|
||||
channel->status = MUSB_DMA_STATUS_FREE;
|
||||
|
||||
/* Handle only RX callbacks here. TX callbacks must be handled based
|
||||
* on the TUSB DMA status interrupt.
|
||||
* REVISIT: Use both TUSB DMA status interrupt and OMAP DMA callback
|
||||
* interrupt for RX and TX.
|
||||
*/
|
||||
if (!chdat->tx)
|
||||
musb_dma_completion(musb, chdat->epnum, chdat->tx);
|
||||
|
||||
/* We must terminate short tx transfers manually by setting TXPKTRDY.
|
||||
* REVISIT: This same problem may occur with other MUSB dma as well.
|
||||
* Easy to test with g_ether by pinging the MUSB board with ping -s54.
|
||||
*/
|
||||
if ((chdat->transfer_len < chdat->packet_sz)
|
||||
|| (chdat->transfer_len % chdat->packet_sz != 0)) {
|
||||
u16 csr;
|
||||
|
||||
if (chdat->tx) {
|
||||
DBG(3, "terminating short tx packet\n");
|
||||
musb_ep_select(mbase, chdat->epnum);
|
||||
csr = musb_readw(hw_ep->regs, MUSB_TXCSR);
|
||||
csr |= MUSB_TXCSR_MODE | MUSB_TXCSR_TXPKTRDY
|
||||
| MUSB_TXCSR_P_WZC_BITS;
|
||||
musb_writew(hw_ep->regs, MUSB_TXCSR, csr);
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&musb->lock, flags);
|
||||
}
|
||||
|
||||
static int tusb_omap_dma_program(struct dma_channel *channel, u16 packet_sz,
|
||||
u8 rndis_mode, dma_addr_t dma_addr, u32 len)
|
||||
{
|
||||
struct tusb_omap_dma_ch *chdat = to_chdat(channel);
|
||||
struct tusb_omap_dma *tusb_dma = chdat->tusb_dma;
|
||||
struct musb *musb = chdat->musb;
|
||||
struct musb_hw_ep *hw_ep = chdat->hw_ep;
|
||||
void __iomem *mbase = musb->mregs;
|
||||
void __iomem *ep_conf = hw_ep->conf;
|
||||
dma_addr_t fifo = hw_ep->fifo_sync;
|
||||
struct omap_dma_channel_params dma_params;
|
||||
u32 dma_remaining;
|
||||
int src_burst, dst_burst;
|
||||
u16 csr;
|
||||
int ch;
|
||||
s8 dmareq;
|
||||
s8 sync_dev;
|
||||
|
||||
if (unlikely(dma_addr & 0x1) || (len < 32) || (len > packet_sz))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* HW issue #10: Async dma will eventually corrupt the XFR_SIZE
|
||||
* register which will cause missed DMA interrupt. We could try to
|
||||
* use a timer for the callback, but it is unsafe as the XFR_SIZE
|
||||
* register is corrupt, and we won't know if the DMA worked.
|
||||
*/
|
||||
if (dma_addr & 0x2)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Because of HW issue #10, it seems like mixing sync DMA and async
|
||||
* PIO access can confuse the DMA. Make sure XFR_SIZE is reset before
|
||||
* using the channel for DMA.
|
||||
*/
|
||||
if (chdat->tx)
|
||||
dma_remaining = musb_readl(ep_conf, TUSB_EP_TX_OFFSET);
|
||||
else
|
||||
dma_remaining = musb_readl(ep_conf, TUSB_EP_RX_OFFSET);
|
||||
|
||||
dma_remaining = TUSB_EP_CONFIG_XFR_SIZE(dma_remaining);
|
||||
if (dma_remaining) {
|
||||
DBG(2, "Busy %s dma ch%i, not using: %08x\n",
|
||||
chdat->tx ? "tx" : "rx", chdat->ch,
|
||||
dma_remaining);
|
||||
return false;
|
||||
}
|
||||
|
||||
chdat->transfer_len = len & ~0x1f;
|
||||
|
||||
if (len < packet_sz)
|
||||
chdat->transfer_packet_sz = chdat->transfer_len;
|
||||
else
|
||||
chdat->transfer_packet_sz = packet_sz;
|
||||
|
||||
if (tusb_dma->multichannel) {
|
||||
ch = chdat->ch;
|
||||
dmareq = chdat->dmareq;
|
||||
sync_dev = chdat->sync_dev;
|
||||
} else {
|
||||
if (tusb_omap_use_shared_dmareq(chdat) != 0) {
|
||||
DBG(3, "could not get dma for ep%i\n", chdat->epnum);
|
||||
return false;
|
||||
}
|
||||
if (tusb_dma->ch < 0) {
|
||||
/* REVISIT: This should get blocked earlier, happens
|
||||
* with MSC ErrorRecoveryTest
|
||||
*/
|
||||
WARN_ON(1);
|
||||
return false;
|
||||
}
|
||||
|
||||
ch = tusb_dma->ch;
|
||||
dmareq = tusb_dma->dmareq;
|
||||
sync_dev = tusb_dma->sync_dev;
|
||||
omap_set_dma_callback(ch, tusb_omap_dma_cb, channel);
|
||||
}
|
||||
|
||||
chdat->packet_sz = packet_sz;
|
||||
chdat->len = len;
|
||||
channel->actual_len = 0;
|
||||
chdat->dma_addr = (void __iomem *)dma_addr;
|
||||
channel->status = MUSB_DMA_STATUS_BUSY;
|
||||
|
||||
/* Since we're recycling dma areas, we need to clean or invalidate */
|
||||
if (chdat->tx)
|
||||
dma_cache_maint(phys_to_virt(dma_addr), len, DMA_TO_DEVICE);
|
||||
else
|
||||
dma_cache_maint(phys_to_virt(dma_addr), len, DMA_FROM_DEVICE);
|
||||
|
||||
/* Use 16-bit transfer if dma_addr is not 32-bit aligned */
|
||||
if ((dma_addr & 0x3) == 0) {
|
||||
dma_params.data_type = OMAP_DMA_DATA_TYPE_S32;
|
||||
dma_params.elem_count = 8; /* Elements in frame */
|
||||
} else {
|
||||
dma_params.data_type = OMAP_DMA_DATA_TYPE_S16;
|
||||
dma_params.elem_count = 16; /* Elements in frame */
|
||||
fifo = hw_ep->fifo_async;
|
||||
}
|
||||
|
||||
dma_params.frame_count = chdat->transfer_len / 32; /* Burst sz frame */
|
||||
|
||||
DBG(3, "ep%i %s dma ch%i dma: %08x len: %u(%u) packet_sz: %i(%i)\n",
|
||||
chdat->epnum, chdat->tx ? "tx" : "rx",
|
||||
ch, dma_addr, chdat->transfer_len, len,
|
||||
chdat->transfer_packet_sz, packet_sz);
|
||||
|
||||
/*
|
||||
* Prepare omap DMA for transfer
|
||||
*/
|
||||
if (chdat->tx) {
|
||||
dma_params.src_amode = OMAP_DMA_AMODE_POST_INC;
|
||||
dma_params.src_start = (unsigned long)dma_addr;
|
||||
dma_params.src_ei = 0;
|
||||
dma_params.src_fi = 0;
|
||||
|
||||
dma_params.dst_amode = OMAP_DMA_AMODE_DOUBLE_IDX;
|
||||
dma_params.dst_start = (unsigned long)fifo;
|
||||
dma_params.dst_ei = 1;
|
||||
dma_params.dst_fi = -31; /* Loop 32 byte window */
|
||||
|
||||
dma_params.trigger = sync_dev;
|
||||
dma_params.sync_mode = OMAP_DMA_SYNC_FRAME;
|
||||
dma_params.src_or_dst_synch = 0; /* Dest sync */
|
||||
|
||||
src_burst = OMAP_DMA_DATA_BURST_16; /* 16x32 read */
|
||||
dst_burst = OMAP_DMA_DATA_BURST_8; /* 8x32 write */
|
||||
} else {
|
||||
dma_params.src_amode = OMAP_DMA_AMODE_DOUBLE_IDX;
|
||||
dma_params.src_start = (unsigned long)fifo;
|
||||
dma_params.src_ei = 1;
|
||||
dma_params.src_fi = -31; /* Loop 32 byte window */
|
||||
|
||||
dma_params.dst_amode = OMAP_DMA_AMODE_POST_INC;
|
||||
dma_params.dst_start = (unsigned long)dma_addr;
|
||||
dma_params.dst_ei = 0;
|
||||
dma_params.dst_fi = 0;
|
||||
|
||||
dma_params.trigger = sync_dev;
|
||||
dma_params.sync_mode = OMAP_DMA_SYNC_FRAME;
|
||||
dma_params.src_or_dst_synch = 1; /* Source sync */
|
||||
|
||||
src_burst = OMAP_DMA_DATA_BURST_8; /* 8x32 read */
|
||||
dst_burst = OMAP_DMA_DATA_BURST_16; /* 16x32 write */
|
||||
}
|
||||
|
||||
DBG(3, "ep%i %s using %i-bit %s dma from 0x%08lx to 0x%08lx\n",
|
||||
chdat->epnum, chdat->tx ? "tx" : "rx",
|
||||
(dma_params.data_type == OMAP_DMA_DATA_TYPE_S32) ? 32 : 16,
|
||||
((dma_addr & 0x3) == 0) ? "sync" : "async",
|
||||
dma_params.src_start, dma_params.dst_start);
|
||||
|
||||
omap_set_dma_params(ch, &dma_params);
|
||||
omap_set_dma_src_burst_mode(ch, src_burst);
|
||||
omap_set_dma_dest_burst_mode(ch, dst_burst);
|
||||
omap_set_dma_write_mode(ch, OMAP_DMA_WRITE_LAST_NON_POSTED);
|
||||
|
||||
/*
|
||||
* Prepare MUSB for DMA transfer
|
||||
*/
|
||||
if (chdat->tx) {
|
||||
musb_ep_select(mbase, chdat->epnum);
|
||||
csr = musb_readw(hw_ep->regs, MUSB_TXCSR);
|
||||
csr |= (MUSB_TXCSR_AUTOSET | MUSB_TXCSR_DMAENAB
|
||||
| MUSB_TXCSR_DMAMODE | MUSB_TXCSR_MODE);
|
||||
csr &= ~MUSB_TXCSR_P_UNDERRUN;
|
||||
musb_writew(hw_ep->regs, MUSB_TXCSR, csr);
|
||||
} else {
|
||||
musb_ep_select(mbase, chdat->epnum);
|
||||
csr = musb_readw(hw_ep->regs, MUSB_RXCSR);
|
||||
csr |= MUSB_RXCSR_DMAENAB;
|
||||
csr &= ~(MUSB_RXCSR_AUTOCLEAR | MUSB_RXCSR_DMAMODE);
|
||||
musb_writew(hw_ep->regs, MUSB_RXCSR,
|
||||
csr | MUSB_RXCSR_P_WZC_BITS);
|
||||
}
|
||||
|
||||
/*
|
||||
* Start DMA transfer
|
||||
*/
|
||||
omap_start_dma(ch);
|
||||
|
||||
if (chdat->tx) {
|
||||
/* Send transfer_packet_sz packets at a time */
|
||||
musb_writel(ep_conf, TUSB_EP_MAX_PACKET_SIZE_OFFSET,
|
||||
chdat->transfer_packet_sz);
|
||||
|
||||
musb_writel(ep_conf, TUSB_EP_TX_OFFSET,
|
||||
TUSB_EP_CONFIG_XFR_SIZE(chdat->transfer_len));
|
||||
} else {
|
||||
/* Receive transfer_packet_sz packets at a time */
|
||||
musb_writel(ep_conf, TUSB_EP_MAX_PACKET_SIZE_OFFSET,
|
||||
chdat->transfer_packet_sz << 16);
|
||||
|
||||
musb_writel(ep_conf, TUSB_EP_RX_OFFSET,
|
||||
TUSB_EP_CONFIG_XFR_SIZE(chdat->transfer_len));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int tusb_omap_dma_abort(struct dma_channel *channel)
|
||||
{
|
||||
struct tusb_omap_dma_ch *chdat = to_chdat(channel);
|
||||
struct tusb_omap_dma *tusb_dma = chdat->tusb_dma;
|
||||
|
||||
if (!tusb_dma->multichannel) {
|
||||
if (tusb_dma->ch >= 0) {
|
||||
omap_stop_dma(tusb_dma->ch);
|
||||
omap_free_dma(tusb_dma->ch);
|
||||
tusb_dma->ch = -1;
|
||||
}
|
||||
|
||||
tusb_dma->dmareq = -1;
|
||||
tusb_dma->sync_dev = -1;
|
||||
}
|
||||
|
||||
channel->status = MUSB_DMA_STATUS_FREE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int tusb_omap_dma_allocate_dmareq(struct tusb_omap_dma_ch *chdat)
|
||||
{
|
||||
u32 reg = musb_readl(chdat->tbase, TUSB_DMA_EP_MAP);
|
||||
int i, dmareq_nr = -1;
|
||||
|
||||
const int sync_dev[6] = {
|
||||
OMAP24XX_DMA_EXT_DMAREQ0,
|
||||
OMAP24XX_DMA_EXT_DMAREQ1,
|
||||
OMAP242X_DMA_EXT_DMAREQ2,
|
||||
OMAP242X_DMA_EXT_DMAREQ3,
|
||||
OMAP242X_DMA_EXT_DMAREQ4,
|
||||
OMAP242X_DMA_EXT_DMAREQ5,
|
||||
};
|
||||
|
||||
for (i = 0; i < MAX_DMAREQ; i++) {
|
||||
int cur = (reg & (0xf << (i * 5))) >> (i * 5);
|
||||
if (cur == 0) {
|
||||
dmareq_nr = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (dmareq_nr == -1)
|
||||
return -EAGAIN;
|
||||
|
||||
reg |= (chdat->epnum << (dmareq_nr * 5));
|
||||
if (chdat->tx)
|
||||
reg |= ((1 << 4) << (dmareq_nr * 5));
|
||||
musb_writel(chdat->tbase, TUSB_DMA_EP_MAP, reg);
|
||||
|
||||
chdat->dmareq = dmareq_nr;
|
||||
chdat->sync_dev = sync_dev[chdat->dmareq];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void tusb_omap_dma_free_dmareq(struct tusb_omap_dma_ch *chdat)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
if (!chdat || chdat->dmareq < 0)
|
||||
return;
|
||||
|
||||
reg = musb_readl(chdat->tbase, TUSB_DMA_EP_MAP);
|
||||
reg &= ~(0x1f << (chdat->dmareq * 5));
|
||||
musb_writel(chdat->tbase, TUSB_DMA_EP_MAP, reg);
|
||||
|
||||
chdat->dmareq = -1;
|
||||
chdat->sync_dev = -1;
|
||||
}
|
||||
|
||||
static struct dma_channel *dma_channel_pool[MAX_DMAREQ];
|
||||
|
||||
static struct dma_channel *
|
||||
tusb_omap_dma_allocate(struct dma_controller *c,
|
||||
struct musb_hw_ep *hw_ep,
|
||||
u8 tx)
|
||||
{
|
||||
int ret, i;
|
||||
const char *dev_name;
|
||||
struct tusb_omap_dma *tusb_dma;
|
||||
struct musb *musb;
|
||||
void __iomem *tbase;
|
||||
struct dma_channel *channel = NULL;
|
||||
struct tusb_omap_dma_ch *chdat = NULL;
|
||||
u32 reg;
|
||||
|
||||
tusb_dma = container_of(c, struct tusb_omap_dma, controller);
|
||||
musb = tusb_dma->musb;
|
||||
tbase = musb->ctrl_base;
|
||||
|
||||
reg = musb_readl(tbase, TUSB_DMA_INT_MASK);
|
||||
if (tx)
|
||||
reg &= ~(1 << hw_ep->epnum);
|
||||
else
|
||||
reg &= ~(1 << (hw_ep->epnum + 15));
|
||||
musb_writel(tbase, TUSB_DMA_INT_MASK, reg);
|
||||
|
||||
/* REVISIT: Why does dmareq5 not work? */
|
||||
if (hw_ep->epnum == 0) {
|
||||
DBG(3, "Not allowing DMA for ep0 %s\n", tx ? "tx" : "rx");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < MAX_DMAREQ; i++) {
|
||||
struct dma_channel *ch = dma_channel_pool[i];
|
||||
if (ch->status == MUSB_DMA_STATUS_UNKNOWN) {
|
||||
ch->status = MUSB_DMA_STATUS_FREE;
|
||||
channel = ch;
|
||||
chdat = ch->private_data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!channel)
|
||||
return NULL;
|
||||
|
||||
if (tx) {
|
||||
chdat->tx = 1;
|
||||
dev_name = "TUSB transmit";
|
||||
} else {
|
||||
chdat->tx = 0;
|
||||
dev_name = "TUSB receive";
|
||||
}
|
||||
|
||||
chdat->musb = tusb_dma->musb;
|
||||
chdat->tbase = tusb_dma->tbase;
|
||||
chdat->hw_ep = hw_ep;
|
||||
chdat->epnum = hw_ep->epnum;
|
||||
chdat->dmareq = -1;
|
||||
chdat->completed_len = 0;
|
||||
chdat->tusb_dma = tusb_dma;
|
||||
|
||||
channel->max_len = 0x7fffffff;
|
||||
channel->desired_mode = 0;
|
||||
channel->actual_len = 0;
|
||||
|
||||
if (tusb_dma->multichannel) {
|
||||
ret = tusb_omap_dma_allocate_dmareq(chdat);
|
||||
if (ret != 0)
|
||||
goto free_dmareq;
|
||||
|
||||
ret = omap_request_dma(chdat->sync_dev, dev_name,
|
||||
tusb_omap_dma_cb, channel, &chdat->ch);
|
||||
if (ret != 0)
|
||||
goto free_dmareq;
|
||||
} else if (tusb_dma->ch == -1) {
|
||||
tusb_dma->dmareq = 0;
|
||||
tusb_dma->sync_dev = OMAP24XX_DMA_EXT_DMAREQ0;
|
||||
|
||||
/* Callback data gets set later in the shared dmareq case */
|
||||
ret = omap_request_dma(tusb_dma->sync_dev, "TUSB shared",
|
||||
tusb_omap_dma_cb, NULL, &tusb_dma->ch);
|
||||
if (ret != 0)
|
||||
goto free_dmareq;
|
||||
|
||||
chdat->dmareq = -1;
|
||||
chdat->ch = -1;
|
||||
}
|
||||
|
||||
DBG(3, "ep%i %s dma: %s dma%i dmareq%i sync%i\n",
|
||||
chdat->epnum,
|
||||
chdat->tx ? "tx" : "rx",
|
||||
chdat->ch >= 0 ? "dedicated" : "shared",
|
||||
chdat->ch >= 0 ? chdat->ch : tusb_dma->ch,
|
||||
chdat->dmareq >= 0 ? chdat->dmareq : tusb_dma->dmareq,
|
||||
chdat->sync_dev >= 0 ? chdat->sync_dev : tusb_dma->sync_dev);
|
||||
|
||||
return channel;
|
||||
|
||||
free_dmareq:
|
||||
tusb_omap_dma_free_dmareq(chdat);
|
||||
|
||||
DBG(3, "ep%i: Could not get a DMA channel\n", chdat->epnum);
|
||||
channel->status = MUSB_DMA_STATUS_UNKNOWN;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void tusb_omap_dma_release(struct dma_channel *channel)
|
||||
{
|
||||
struct tusb_omap_dma_ch *chdat = to_chdat(channel);
|
||||
struct musb *musb = chdat->musb;
|
||||
void __iomem *tbase = musb->ctrl_base;
|
||||
u32 reg;
|
||||
|
||||
DBG(3, "ep%i ch%i\n", chdat->epnum, chdat->ch);
|
||||
|
||||
reg = musb_readl(tbase, TUSB_DMA_INT_MASK);
|
||||
if (chdat->tx)
|
||||
reg |= (1 << chdat->epnum);
|
||||
else
|
||||
reg |= (1 << (chdat->epnum + 15));
|
||||
musb_writel(tbase, TUSB_DMA_INT_MASK, reg);
|
||||
|
||||
reg = musb_readl(tbase, TUSB_DMA_INT_CLEAR);
|
||||
if (chdat->tx)
|
||||
reg |= (1 << chdat->epnum);
|
||||
else
|
||||
reg |= (1 << (chdat->epnum + 15));
|
||||
musb_writel(tbase, TUSB_DMA_INT_CLEAR, reg);
|
||||
|
||||
channel->status = MUSB_DMA_STATUS_UNKNOWN;
|
||||
|
||||
if (chdat->ch >= 0) {
|
||||
omap_stop_dma(chdat->ch);
|
||||
omap_free_dma(chdat->ch);
|
||||
chdat->ch = -1;
|
||||
}
|
||||
|
||||
if (chdat->dmareq >= 0)
|
||||
tusb_omap_dma_free_dmareq(chdat);
|
||||
|
||||
channel = NULL;
|
||||
}
|
||||
|
||||
void dma_controller_destroy(struct dma_controller *c)
|
||||
{
|
||||
struct tusb_omap_dma *tusb_dma;
|
||||
int i;
|
||||
|
||||
tusb_dma = container_of(c, struct tusb_omap_dma, controller);
|
||||
for (i = 0; i < MAX_DMAREQ; i++) {
|
||||
struct dma_channel *ch = dma_channel_pool[i];
|
||||
if (ch) {
|
||||
kfree(ch->private_data);
|
||||
kfree(ch);
|
||||
}
|
||||
}
|
||||
|
||||
if (!tusb_dma->multichannel && tusb_dma && tusb_dma->ch >= 0)
|
||||
omap_free_dma(tusb_dma->ch);
|
||||
|
||||
kfree(tusb_dma);
|
||||
}
|
||||
|
||||
struct dma_controller *__init
|
||||
dma_controller_create(struct musb *musb, void __iomem *base)
|
||||
{
|
||||
void __iomem *tbase = musb->ctrl_base;
|
||||
struct tusb_omap_dma *tusb_dma;
|
||||
int i;
|
||||
|
||||
/* REVISIT: Get dmareq lines used from board-*.c */
|
||||
|
||||
musb_writel(musb->ctrl_base, TUSB_DMA_INT_MASK, 0x7fffffff);
|
||||
musb_writel(musb->ctrl_base, TUSB_DMA_EP_MAP, 0);
|
||||
|
||||
musb_writel(tbase, TUSB_DMA_REQ_CONF,
|
||||
TUSB_DMA_REQ_CONF_BURST_SIZE(2)
|
||||
| TUSB_DMA_REQ_CONF_DMA_REQ_EN(0x3f)
|
||||
| TUSB_DMA_REQ_CONF_DMA_REQ_ASSER(2));
|
||||
|
||||
tusb_dma = kzalloc(sizeof(struct tusb_omap_dma), GFP_KERNEL);
|
||||
if (!tusb_dma)
|
||||
goto cleanup;
|
||||
|
||||
tusb_dma->musb = musb;
|
||||
tusb_dma->tbase = musb->ctrl_base;
|
||||
|
||||
tusb_dma->ch = -1;
|
||||
tusb_dma->dmareq = -1;
|
||||
tusb_dma->sync_dev = -1;
|
||||
|
||||
tusb_dma->controller.start = tusb_omap_dma_start;
|
||||
tusb_dma->controller.stop = tusb_omap_dma_stop;
|
||||
tusb_dma->controller.channel_alloc = tusb_omap_dma_allocate;
|
||||
tusb_dma->controller.channel_release = tusb_omap_dma_release;
|
||||
tusb_dma->controller.channel_program = tusb_omap_dma_program;
|
||||
tusb_dma->controller.channel_abort = tusb_omap_dma_abort;
|
||||
|
||||
if (tusb_get_revision(musb) >= TUSB_REV_30)
|
||||
tusb_dma->multichannel = 1;
|
||||
|
||||
for (i = 0; i < MAX_DMAREQ; i++) {
|
||||
struct dma_channel *ch;
|
||||
struct tusb_omap_dma_ch *chdat;
|
||||
|
||||
ch = kzalloc(sizeof(struct dma_channel), GFP_KERNEL);
|
||||
if (!ch)
|
||||
goto cleanup;
|
||||
|
||||
dma_channel_pool[i] = ch;
|
||||
|
||||
chdat = kzalloc(sizeof(struct tusb_omap_dma_ch), GFP_KERNEL);
|
||||
if (!chdat)
|
||||
goto cleanup;
|
||||
|
||||
ch->status = MUSB_DMA_STATUS_UNKNOWN;
|
||||
ch->private_data = chdat;
|
||||
}
|
||||
|
||||
return &tusb_dma->controller;
|
||||
|
||||
cleanup:
|
||||
dma_controller_destroy(&tusb_dma->controller);
|
||||
|
||||
return NULL;
|
||||
}
|
@ -499,9 +499,10 @@ config USB_SERIAL_SAFE_PADDED
|
||||
config USB_SERIAL_SIERRAWIRELESS
|
||||
tristate "USB Sierra Wireless Driver"
|
||||
help
|
||||
Say M here if you want to use a Sierra Wireless device (if
|
||||
using an PC 5220 or AC580 please use the Airprime driver
|
||||
instead).
|
||||
Say M here if you want to use Sierra Wireless devices.
|
||||
|
||||
Many deviecs have a feature known as TRU-Install, for those devices
|
||||
to work properly the USB Storage Sierra feature must be enabled.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called sierra.
|
||||
|
@ -563,6 +563,7 @@ static struct usb_device_id id_table_combined [] = {
|
||||
{ USB_DEVICE(FTDI_VID, FTDI_ELV_FHZ1300PC_PID) },
|
||||
{ USB_DEVICE(FTDI_VID, FTDI_ELV_EM1010PC_PID) },
|
||||
{ USB_DEVICE(FTDI_VID, FTDI_ELV_WS500_PID) },
|
||||
{ USB_DEVICE(FTDI_VID, FTDI_ELV_HS485_PID) },
|
||||
{ USB_DEVICE(FTDI_VID, LINX_SDMUSBQSS_PID) },
|
||||
{ USB_DEVICE(FTDI_VID, LINX_MASTERDEVEL2_PID) },
|
||||
{ USB_DEVICE(FTDI_VID, LINX_FUTURE_0_PID) },
|
||||
@ -637,6 +638,7 @@ static struct usb_device_id id_table_combined [] = {
|
||||
{ USB_DEVICE(ELEKTOR_VID, ELEKTOR_FT323R_PID) },
|
||||
{ USB_DEVICE(TELLDUS_VID, TELLDUS_TELLSTICK_PID) },
|
||||
{ USB_DEVICE(FTDI_VID, FTDI_MAXSTREAM_PID) },
|
||||
{ USB_DEVICE(FTDI_VID, FTDI_PHI_FISCO_PID) },
|
||||
{ USB_DEVICE(TML_VID, TML_USB_SERIAL_PID) },
|
||||
{ USB_DEVICE(FTDI_VID, FTDI_ELSTER_UNICOM_PID) },
|
||||
{ USB_DEVICE(FTDI_VID, FTDI_PROPOX_JTAGCABLEII_PID) },
|
||||
@ -646,6 +648,10 @@ static struct usb_device_id id_table_combined [] = {
|
||||
.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
|
||||
{ USB_DEVICE(FTDI_VID, FTDI_OOCDLINK_PID),
|
||||
.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
|
||||
{ USB_DEVICE(FTDI_VID, LMI_LM3S_DEVEL_BOARD_PID),
|
||||
.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
|
||||
{ USB_DEVICE(FTDI_VID, LMI_LM3S_EVAL_BOARD_PID),
|
||||
.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
|
||||
{ USB_DEVICE(RATOC_VENDOR_ID, RATOC_PRODUCT_ID_USB60F) },
|
||||
{ USB_DEVICE(FTDI_VID, FTDI_REU_TINY_PID) },
|
||||
{ }, /* Optional parameter entry */
|
||||
|
@ -524,7 +524,9 @@
|
||||
#define FTDI_ELV_WS300PC_PID 0xE0F6 /* PC-Wetterstation (WS 300 PC) */
|
||||
#define FTDI_ELV_FHZ1300PC_PID 0xE0E8 /* FHZ 1300 PC */
|
||||
#define FTDI_ELV_WS500_PID 0xE0E9 /* PC-Wetterstation (WS 500) */
|
||||
#define FTDI_ELV_HS485_PID 0xE0EA /* USB to RS-485 adapter */
|
||||
#define FTDI_ELV_EM1010PC_PID 0xE0EF /* Engery monitor EM 1010 PC */
|
||||
#define FTDI_PHI_FISCO_PID 0xE40B /* PHI Fisco USB to Serial cable */
|
||||
|
||||
/*
|
||||
* Definitions for ID TECH (www.idt-net.com) devices
|
||||
@ -815,6 +817,11 @@
|
||||
#define OLIMEX_VID 0x15BA
|
||||
#define OLIMEX_ARM_USB_OCD_PID 0x0003
|
||||
|
||||
/* Luminary Micro Stellaris Boards, VID = FTDI_VID */
|
||||
/* FTDI 2332C Dual channel device, side A=245 FIFO (JTAG), Side B=RS232 UART */
|
||||
#define LMI_LM3S_DEVEL_BOARD_PID 0xbcd8
|
||||
#define LMI_LM3S_EVAL_BOARD_PID 0xbcd9
|
||||
|
||||
/* www.elsterelectricity.com Elster Unicom III Optical Probe */
|
||||
#define FTDI_ELSTER_UNICOM_PID 0xE700 /* Product Id */
|
||||
|
||||
|
@ -186,6 +186,23 @@ static int option_send_setup(struct tty_struct *tty, struct usb_serial_port *po
|
||||
#define BANDRICH_VENDOR_ID 0x1A8D
|
||||
#define BANDRICH_PRODUCT_C100_1 0x1002
|
||||
#define BANDRICH_PRODUCT_C100_2 0x1003
|
||||
#define BANDRICH_PRODUCT_1004 0x1004
|
||||
#define BANDRICH_PRODUCT_1005 0x1005
|
||||
#define BANDRICH_PRODUCT_1006 0x1006
|
||||
#define BANDRICH_PRODUCT_1007 0x1007
|
||||
#define BANDRICH_PRODUCT_1008 0x1008
|
||||
#define BANDRICH_PRODUCT_1009 0x1009
|
||||
#define BANDRICH_PRODUCT_100A 0x100a
|
||||
|
||||
#define BANDRICH_PRODUCT_100B 0x100b
|
||||
#define BANDRICH_PRODUCT_100C 0x100c
|
||||
#define BANDRICH_PRODUCT_100D 0x100d
|
||||
#define BANDRICH_PRODUCT_100E 0x100e
|
||||
|
||||
#define BANDRICH_PRODUCT_100F 0x100f
|
||||
#define BANDRICH_PRODUCT_1010 0x1010
|
||||
#define BANDRICH_PRODUCT_1011 0x1011
|
||||
#define BANDRICH_PRODUCT_1012 0x1012
|
||||
|
||||
#define AMOI_VENDOR_ID 0x1614
|
||||
#define AMOI_PRODUCT_9508 0x0800
|
||||
@ -197,6 +214,10 @@ static int option_send_setup(struct tty_struct *tty, struct usb_serial_port *po
|
||||
#define TELIT_VENDOR_ID 0x1bc7
|
||||
#define TELIT_PRODUCT_UC864E 0x1003
|
||||
|
||||
/* ZTE PRODUCTS */
|
||||
#define ZTE_VENDOR_ID 0x19d2
|
||||
#define ZTE_PRODUCT_MF628 0x0015
|
||||
|
||||
static struct usb_device_id option_ids[] = {
|
||||
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COLT) },
|
||||
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA) },
|
||||
@ -302,12 +323,28 @@ static struct usb_device_id option_ids[] = {
|
||||
{ USB_DEVICE(ONDA_VENDOR_ID, ONDA_PRODUCT_ET502HS) },
|
||||
{ USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_C100_1) },
|
||||
{ USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_C100_2) },
|
||||
{ USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_1004) },
|
||||
{ USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_1005) },
|
||||
{ USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_1006) },
|
||||
{ USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_1007) },
|
||||
{ USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_1008) },
|
||||
{ USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_1009) },
|
||||
{ USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_100A) },
|
||||
{ USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_100B) },
|
||||
{ USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_100C) },
|
||||
{ USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_100D) },
|
||||
{ USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_100E) },
|
||||
{ USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_100F) },
|
||||
{ USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_1010) },
|
||||
{ USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_1011) },
|
||||
{ USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_1012) },
|
||||
{ USB_DEVICE(KYOCERA_VENDOR_ID, KYOCERA_PRODUCT_KPC650) },
|
||||
{ USB_DEVICE(KYOCERA_VENDOR_ID, KYOCERA_PRODUCT_KPC680) },
|
||||
{ USB_DEVICE(QUALCOMM_VENDOR_ID, 0x6000)}, /* ZTE AC8700 */
|
||||
{ USB_DEVICE(QUALCOMM_VENDOR_ID, 0x6613)}, /* Onda H600/ZTE MF330 */
|
||||
{ USB_DEVICE(MAXON_VENDOR_ID, 0x6280) }, /* BP3-USB & BP3-EXT HSDPA */
|
||||
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_UC864E) },
|
||||
{ USB_DEVICE(ZTE_VENDOR_ID, ZTE_PRODUCT_MF628) },
|
||||
{ } /* Terminating entry */
|
||||
};
|
||||
MODULE_DEVICE_TABLE(usb, option_ids);
|
||||
@ -346,11 +383,7 @@ static struct usb_serial_driver option_1port_device = {
|
||||
.read_int_callback = option_instat_callback,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_USB_DEBUG
|
||||
static int debug;
|
||||
#else
|
||||
#define debug 0
|
||||
#endif
|
||||
|
||||
/* per port private data */
|
||||
|
||||
@ -954,8 +987,5 @@ MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_VERSION(DRIVER_VERSION);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
#ifdef CONFIG_USB_DEBUG
|
||||
module_param(debug, bool, S_IRUGO | S_IWUSR);
|
||||
MODULE_PARM_DESC(debug, "Debug messages");
|
||||
#endif
|
||||
|
||||
|
@ -90,7 +90,6 @@ static struct usb_device_id id_table [] = {
|
||||
{ USB_DEVICE(ALCOR_VENDOR_ID, ALCOR_PRODUCT_ID) },
|
||||
{ USB_DEVICE(WS002IN_VENDOR_ID, WS002IN_PRODUCT_ID) },
|
||||
{ USB_DEVICE(COREGA_VENDOR_ID, COREGA_PRODUCT_ID) },
|
||||
{ USB_DEVICE(HL340_VENDOR_ID, HL340_PRODUCT_ID) },
|
||||
{ USB_DEVICE(YCCABLE_VENDOR_ID, YCCABLE_PRODUCT_ID) },
|
||||
{ } /* Terminating entry */
|
||||
};
|
||||
|
@ -107,10 +107,6 @@
|
||||
#define COREGA_VENDOR_ID 0x07aa
|
||||
#define COREGA_PRODUCT_ID 0x002a
|
||||
|
||||
/* HL HL-340 (ID: 4348:5523) */
|
||||
#define HL340_VENDOR_ID 0x4348
|
||||
#define HL340_PRODUCT_ID 0x5523
|
||||
|
||||
/* Y.C. Cable U.S.A., Inc - USB to RS-232 */
|
||||
#define YCCABLE_VENDOR_ID 0x05ad
|
||||
#define YCCABLE_PRODUCT_ID 0x0fba
|
||||
|
@ -14,7 +14,7 @@
|
||||
Whom based his on the Keyspan driver by Hugh Blemings <hugh@blemings.org>
|
||||
*/
|
||||
|
||||
#define DRIVER_VERSION "v.1.2.9c"
|
||||
#define DRIVER_VERSION "v.1.2.13a"
|
||||
#define DRIVER_AUTHOR "Kevin Lloyd <klloyd@sierrawireless.com>"
|
||||
#define DRIVER_DESC "USB Driver for Sierra Wireless USB modems"
|
||||
|
||||
@ -31,6 +31,7 @@
|
||||
#define SWIMS_USB_REQUEST_SetPower 0x00
|
||||
#define SWIMS_USB_REQUEST_SetNmea 0x07
|
||||
#define SWIMS_USB_REQUEST_SetMode 0x0B
|
||||
#define SWIMS_USB_REQUEST_GetSwocInfo 0x0A
|
||||
#define SWIMS_SET_MODE_Modem 0x0001
|
||||
|
||||
/* per port private data */
|
||||
@ -40,18 +41,11 @@
|
||||
|
||||
static int debug;
|
||||
static int nmea;
|
||||
static int truinstall = 1;
|
||||
|
||||
enum devicetype {
|
||||
DEVICE_3_PORT = 0,
|
||||
DEVICE_1_PORT = 1,
|
||||
DEVICE_INSTALLER = 2,
|
||||
};
|
||||
|
||||
static int sierra_set_power_state(struct usb_device *udev, __u16 swiState)
|
||||
{
|
||||
int result;
|
||||
dev_dbg(&udev->dev, "%s", "SET POWER STATE\n");
|
||||
dev_dbg(&udev->dev, "%s", __func__);
|
||||
result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
|
||||
SWIMS_USB_REQUEST_SetPower, /* __u8 request */
|
||||
USB_TYPE_VENDOR, /* __u8 request type */
|
||||
@ -63,25 +57,10 @@ static int sierra_set_power_state(struct usb_device *udev, __u16 swiState)
|
||||
return result;
|
||||
}
|
||||
|
||||
static int sierra_set_ms_mode(struct usb_device *udev, __u16 eSWocMode)
|
||||
{
|
||||
int result;
|
||||
dev_dbg(&udev->dev, "%s", "DEVICE MODE SWITCH\n");
|
||||
result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
|
||||
SWIMS_USB_REQUEST_SetMode, /* __u8 request */
|
||||
USB_TYPE_VENDOR, /* __u8 request type */
|
||||
eSWocMode, /* __u16 value */
|
||||
0x0000, /* __u16 index */
|
||||
NULL, /* void *data */
|
||||
0, /* __u16 size */
|
||||
USB_CTRL_SET_TIMEOUT); /* int timeout */
|
||||
return result;
|
||||
}
|
||||
|
||||
static int sierra_vsc_set_nmea(struct usb_device *udev, __u16 enable)
|
||||
{
|
||||
int result;
|
||||
dev_dbg(&udev->dev, "%s", "NMEA Enable sent\n");
|
||||
dev_dbg(&udev->dev, "%s", __func__);
|
||||
result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
|
||||
SWIMS_USB_REQUEST_SetNmea, /* __u8 request */
|
||||
USB_TYPE_VENDOR, /* __u8 request type */
|
||||
@ -97,6 +76,7 @@ static int sierra_calc_num_ports(struct usb_serial *serial)
|
||||
{
|
||||
int result;
|
||||
int *num_ports = usb_get_serial_data(serial);
|
||||
dev_dbg(&serial->dev->dev, "%s", __func__);
|
||||
|
||||
result = *num_ports;
|
||||
|
||||
@ -110,22 +90,23 @@ static int sierra_calc_num_ports(struct usb_serial *serial)
|
||||
|
||||
static int sierra_calc_interface(struct usb_serial *serial)
|
||||
{
|
||||
int interface;
|
||||
struct usb_interface *p_interface;
|
||||
struct usb_host_interface *p_host_interface;
|
||||
int interface;
|
||||
struct usb_interface *p_interface;
|
||||
struct usb_host_interface *p_host_interface;
|
||||
dev_dbg(&serial->dev->dev, "%s", __func__);
|
||||
|
||||
/* Get the interface structure pointer from the serial struct */
|
||||
p_interface = serial->interface;
|
||||
/* Get the interface structure pointer from the serial struct */
|
||||
p_interface = serial->interface;
|
||||
|
||||
/* Get a pointer to the host interface structure */
|
||||
p_host_interface = p_interface->cur_altsetting;
|
||||
/* Get a pointer to the host interface structure */
|
||||
p_host_interface = p_interface->cur_altsetting;
|
||||
|
||||
/* read the interface descriptor for this active altsetting
|
||||
* to find out the interface number we are on
|
||||
*/
|
||||
interface = p_host_interface->desc.bInterfaceNumber;
|
||||
/* read the interface descriptor for this active altsetting
|
||||
* to find out the interface number we are on
|
||||
*/
|
||||
interface = p_host_interface->desc.bInterfaceNumber;
|
||||
|
||||
return interface;
|
||||
return interface;
|
||||
}
|
||||
|
||||
static int sierra_probe(struct usb_serial *serial,
|
||||
@ -135,43 +116,40 @@ static int sierra_probe(struct usb_serial *serial,
|
||||
struct usb_device *udev;
|
||||
int *num_ports;
|
||||
u8 ifnum;
|
||||
u8 numendpoints;
|
||||
|
||||
dev_dbg(&serial->dev->dev, "%s", __func__);
|
||||
|
||||
num_ports = kmalloc(sizeof(*num_ports), GFP_KERNEL);
|
||||
if (!num_ports)
|
||||
return -ENOMEM;
|
||||
|
||||
ifnum = serial->interface->cur_altsetting->desc.bInterfaceNumber;
|
||||
numendpoints = serial->interface->cur_altsetting->desc.bNumEndpoints;
|
||||
udev = serial->dev;
|
||||
|
||||
/* Figure out the interface number from the serial structure */
|
||||
ifnum = sierra_calc_interface(serial);
|
||||
/* Figure out the interface number from the serial structure */
|
||||
ifnum = sierra_calc_interface(serial);
|
||||
|
||||
/*
|
||||
* If this interface supports more than 1 alternate
|
||||
* select the 2nd one
|
||||
*/
|
||||
if (serial->interface->num_altsetting == 2) {
|
||||
dev_dbg(&udev->dev,
|
||||
"Selecting alt setting for interface %d\n",
|
||||
ifnum);
|
||||
/*
|
||||
* If this interface supports more than 1 alternate
|
||||
* select the 2nd one
|
||||
*/
|
||||
if (serial->interface->num_altsetting == 2) {
|
||||
dev_dbg(&udev->dev, "Selecting alt setting for interface %d\n",
|
||||
ifnum);
|
||||
/* We know the alternate setting is 1 for the MC8785 */
|
||||
usb_set_interface(udev, ifnum, 1);
|
||||
}
|
||||
|
||||
/* We know the alternate setting is 1 for the MC8785 */
|
||||
usb_set_interface(udev, ifnum, 1);
|
||||
}
|
||||
|
||||
/* Check if in installer mode */
|
||||
if (truinstall && id->driver_info == DEVICE_INSTALLER) {
|
||||
dev_dbg(&udev->dev, "%s", "FOUND TRU-INSTALL DEVICE(SW)\n");
|
||||
result = sierra_set_ms_mode(udev, SWIMS_SET_MODE_Modem);
|
||||
/* Don't bind to the device when in installer mode */
|
||||
kfree(num_ports);
|
||||
return -EIO;
|
||||
} else if (id->driver_info == DEVICE_1_PORT)
|
||||
*num_ports = 1;
|
||||
else if (ifnum == 0x99)
|
||||
/* Dummy interface present on some SKUs should be ignored */
|
||||
if (ifnum == 0x99)
|
||||
*num_ports = 0;
|
||||
else if (numendpoints <= 3)
|
||||
*num_ports = 1;
|
||||
else
|
||||
*num_ports = 3;
|
||||
*num_ports = (numendpoints-1)/2;
|
||||
|
||||
/*
|
||||
* save off our num_ports info so that we can use it in the
|
||||
* calc_num_ports callback
|
||||
@ -187,40 +165,50 @@ static struct usb_device_id id_table [] = {
|
||||
{ USB_DEVICE(0x1199, 0x0218) }, /* Sierra Wireless MC5720 */
|
||||
{ USB_DEVICE(0x0f30, 0x1b1d) }, /* Sierra Wireless MC5720 */
|
||||
{ USB_DEVICE(0x1199, 0x0020) }, /* Sierra Wireless MC5725 */
|
||||
{ USB_DEVICE(0x1199, 0x0024) }, /* Sierra Wireless MC5727 */
|
||||
{ USB_DEVICE(0x1199, 0x0220) }, /* Sierra Wireless MC5725 */
|
||||
{ USB_DEVICE(0x1199, 0x0019) }, /* Sierra Wireless AirCard 595 */
|
||||
{ USB_DEVICE(0x1199, 0x0021) }, /* Sierra Wireless AirCard 597E */
|
||||
{ USB_DEVICE(0x1199, 0x0120) }, /* Sierra Wireless USB Dongle 595U */
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x0023, 0xFF, 0xFF, 0xFF) }, /* Sierra Wireless C597 */
|
||||
/* Sierra Wireless C597 */
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x0023, 0xFF, 0xFF, 0xFF) },
|
||||
/* Sierra Wireless Device */
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x0025, 0xFF, 0xFF, 0xFF) },
|
||||
{ USB_DEVICE(0x1199, 0x0026) }, /* Sierra Wireless Device */
|
||||
|
||||
{ USB_DEVICE(0x1199, 0x6802) }, /* Sierra Wireless MC8755 */
|
||||
{ USB_DEVICE(0x1199, 0x6804) }, /* Sierra Wireless MC8755 */
|
||||
{ USB_DEVICE(0x1199, 0x6803) }, /* Sierra Wireless MC8765 */
|
||||
{ USB_DEVICE(0x1199, 0x6812) }, /* Sierra Wireless MC8775 & AC 875U */
|
||||
{ USB_DEVICE(0x1199, 0x6813) }, /* Sierra Wireless MC8775 (Thinkpad internal) */
|
||||
{ USB_DEVICE(0x1199, 0x6813) }, /* Sierra Wireless MC8775 (Lenovo) */
|
||||
{ USB_DEVICE(0x1199, 0x6815) }, /* Sierra Wireless MC8775 */
|
||||
{ USB_DEVICE(0x03f0, 0x1e1d) }, /* HP hs2300 a.k.a MC8775 */
|
||||
{ USB_DEVICE(0x1199, 0x6820) }, /* Sierra Wireless AirCard 875 */
|
||||
{ USB_DEVICE(0x1199, 0x6821) }, /* Sierra Wireless AirCard 875U */
|
||||
{ USB_DEVICE(0x1199, 0x6832) }, /* Sierra Wireless MC8780*/
|
||||
{ USB_DEVICE(0x1199, 0x6833) }, /* Sierra Wireless MC8781*/
|
||||
{ USB_DEVICE(0x1199, 0x683B), .driver_info = DEVICE_1_PORT }, /* Sierra Wireless MC8785 Composite*/
|
||||
{ USB_DEVICE(0x1199, 0x6832) }, /* Sierra Wireless MC8780 */
|
||||
{ USB_DEVICE(0x1199, 0x6833) }, /* Sierra Wireless MC8781 */
|
||||
{ USB_DEVICE(0x1199, 0x683B) }, /* Sierra Wireless MC8785 Composite */
|
||||
{ USB_DEVICE(0x1199, 0x683C) }, /* Sierra Wireless MC8790 */
|
||||
{ USB_DEVICE(0x1199, 0x683D) }, /* Sierra Wireless MC8790 */
|
||||
{ USB_DEVICE(0x1199, 0x683E) }, /* Sierra Wireless MC8790 */
|
||||
{ USB_DEVICE(0x1199, 0x6850) }, /* Sierra Wireless AirCard 880 */
|
||||
{ USB_DEVICE(0x1199, 0x6851) }, /* Sierra Wireless AirCard 881 */
|
||||
{ USB_DEVICE(0x1199, 0x6852) }, /* Sierra Wireless AirCard 880 E */
|
||||
{ USB_DEVICE(0x1199, 0x6853) }, /* Sierra Wireless AirCard 881 E */
|
||||
{ USB_DEVICE(0x1199, 0x6855) }, /* Sierra Wireless AirCard 880 U */
|
||||
{ USB_DEVICE(0x1199, 0x6856) }, /* Sierra Wireless AirCard 881 U */
|
||||
{ USB_DEVICE(0x1199, 0x6859), .driver_info = DEVICE_1_PORT }, /* Sierra Wireless AirCard 885 E */
|
||||
{ USB_DEVICE(0x1199, 0x685A), .driver_info = DEVICE_1_PORT }, /* Sierra Wireless AirCard 885 E */
|
||||
{ USB_DEVICE(0x1199, 0x6859) }, /* Sierra Wireless AirCard 885 E */
|
||||
{ USB_DEVICE(0x1199, 0x685A) }, /* Sierra Wireless AirCard 885 E */
|
||||
/* Sierra Wireless C885 */
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x6880, 0xFF, 0xFF, 0xFF)},
|
||||
/* Sierra Wireless Device */
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x6890, 0xFF, 0xFF, 0xFF)},
|
||||
/* Sierra Wireless Device */
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x6892, 0xFF, 0xFF, 0xFF)},
|
||||
|
||||
{ USB_DEVICE(0x1199, 0x6468) }, /* Sierra Wireless MP3G - EVDO */
|
||||
{ USB_DEVICE(0x1199, 0x6469) }, /* Sierra Wireless MP3G - UMTS/HSPA */
|
||||
{ USB_DEVICE(0x1199, 0x0112) }, /* Sierra Wireless AirCard 580 */
|
||||
{ USB_DEVICE(0x0F3D, 0x0112) }, /* Airprime/Sierra PC 5220 */
|
||||
|
||||
{ USB_DEVICE(0x1199, 0x0112), .driver_info = DEVICE_1_PORT }, /* Sierra Wireless AirCard 580 */
|
||||
{ USB_DEVICE(0x0F3D, 0x0112), .driver_info = DEVICE_1_PORT }, /* Airprime/Sierra PC 5220 */
|
||||
|
||||
{ USB_DEVICE(0x1199, 0x0FFF), .driver_info = DEVICE_INSTALLER},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(usb, id_table);
|
||||
@ -268,13 +256,19 @@ static int sierra_send_setup(struct tty_struct *tty,
|
||||
if (portdata->rts_state)
|
||||
val |= 0x02;
|
||||
|
||||
/* Determine which port is targeted */
|
||||
if (port->bulk_out_endpointAddress == 2)
|
||||
interface = 0;
|
||||
else if (port->bulk_out_endpointAddress == 4)
|
||||
interface = 1;
|
||||
else if (port->bulk_out_endpointAddress == 5)
|
||||
interface = 2;
|
||||
/* If composite device then properly report interface */
|
||||
if (serial->num_ports == 1)
|
||||
interface = sierra_calc_interface(serial);
|
||||
|
||||
/* Otherwise the need to do non-composite mapping */
|
||||
else {
|
||||
if (port->bulk_out_endpointAddress == 2)
|
||||
interface = 0;
|
||||
else if (port->bulk_out_endpointAddress == 4)
|
||||
interface = 1;
|
||||
else if (port->bulk_out_endpointAddress == 5)
|
||||
interface = 2;
|
||||
}
|
||||
|
||||
return usb_control_msg(serial->dev,
|
||||
usb_rcvctrlpipe(serial->dev, 0),
|
||||
@ -713,7 +707,7 @@ static void sierra_shutdown(struct usb_serial *serial)
|
||||
static struct usb_serial_driver sierra_device = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "sierra1",
|
||||
.name = "sierra",
|
||||
},
|
||||
.description = "Sierra USB modem",
|
||||
.id_table = id_table,
|
||||
@ -769,14 +763,8 @@ MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_VERSION(DRIVER_VERSION);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
module_param(truinstall, bool, 0);
|
||||
MODULE_PARM_DESC(truinstall, "TRU-Install support");
|
||||
|
||||
module_param(nmea, bool, 0);
|
||||
module_param(nmea, bool, S_IRUGO | S_IWUSR);
|
||||
MODULE_PARM_DESC(nmea, "NMEA streaming");
|
||||
|
||||
#ifdef CONFIG_USB_DEBUG
|
||||
module_param(debug, bool, S_IRUGO | S_IWUSR);
|
||||
MODULE_PARM_DESC(debug, "Debug messages");
|
||||
#endif
|
||||
|
||||
|
@ -122,9 +122,6 @@ static void return_serial(struct usb_serial *serial)
|
||||
|
||||
dbg("%s", __func__);
|
||||
|
||||
if (serial == NULL)
|
||||
return;
|
||||
|
||||
for (i = 0; i < serial->num_ports; ++i)
|
||||
serial_table[serial->minor + i] = NULL;
|
||||
}
|
||||
@ -142,7 +139,8 @@ static void destroy_serial(struct kref *kref)
|
||||
serial->type->shutdown(serial);
|
||||
|
||||
/* return the minor range that this device had */
|
||||
return_serial(serial);
|
||||
if (serial->minor != SERIAL_TTY_NO_MINOR)
|
||||
return_serial(serial);
|
||||
|
||||
for (i = 0; i < serial->num_ports; ++i)
|
||||
serial->port[i]->port.count = 0;
|
||||
@ -575,6 +573,7 @@ static struct usb_serial *create_serial(struct usb_device *dev,
|
||||
serial->interface = interface;
|
||||
kref_init(&serial->kref);
|
||||
mutex_init(&serial->disc_mutex);
|
||||
serial->minor = SERIAL_TTY_NO_MINOR;
|
||||
|
||||
return serial;
|
||||
}
|
||||
|
@ -146,6 +146,18 @@ config USB_STORAGE_KARMA
|
||||
on the resulting scsi device node returns the Karma to normal
|
||||
operation.
|
||||
|
||||
config USB_STORAGE_SIERRA
|
||||
bool "Sierra Wireless TRU-Install Feature Support"
|
||||
depends on USB_STORAGE
|
||||
help
|
||||
Say Y here to include additional code to support Sierra Wireless
|
||||
products with the TRU-Install feature (e.g., AC597E, AC881U).
|
||||
|
||||
This code switches the Sierra Wireless device from being in
|
||||
Mass Storage mode to Modem mode. It also has the ability to
|
||||
support host software upgrades should full Linux support be added
|
||||
to TRU-Install.
|
||||
|
||||
config USB_STORAGE_CYPRESS_ATACB
|
||||
bool "SAT emulation on Cypress USB/ATA Bridge with ATACB"
|
||||
depends on USB_STORAGE
|
||||
|
@ -21,6 +21,7 @@ usb-storage-obj-$(CONFIG_USB_STORAGE_JUMPSHOT) += jumpshot.o
|
||||
usb-storage-obj-$(CONFIG_USB_STORAGE_ALAUDA) += alauda.o
|
||||
usb-storage-obj-$(CONFIG_USB_STORAGE_ONETOUCH) += onetouch.o
|
||||
usb-storage-obj-$(CONFIG_USB_STORAGE_KARMA) += karma.o
|
||||
usb-storage-obj-$(CONFIG_USB_STORAGE_SIERRA) += sierra_ms.o
|
||||
usb-storage-obj-$(CONFIG_USB_STORAGE_CYPRESS_ATACB) += cypress_atacb.o
|
||||
|
||||
usb-storage-objs := scsiglue.o protocol.o transport.o usb.o \
|
||||
|
207
drivers/usb/storage/sierra_ms.c
Normal file
207
drivers/usb/storage/sierra_ms.c
Normal file
@ -0,0 +1,207 @@
|
||||
#include <scsi/scsi.h>
|
||||
#include <scsi/scsi_host.h>
|
||||
#include <scsi/scsi_cmnd.h>
|
||||
#include <scsi/scsi_device.h>
|
||||
#include <linux/usb.h>
|
||||
|
||||
#include "usb.h"
|
||||
#include "transport.h"
|
||||
#include "protocol.h"
|
||||
#include "scsiglue.h"
|
||||
#include "sierra_ms.h"
|
||||
#include "debug.h"
|
||||
|
||||
#define SWIMS_USB_REQUEST_SetSwocMode 0x0B
|
||||
#define SWIMS_USB_REQUEST_GetSwocInfo 0x0A
|
||||
#define SWIMS_USB_INDEX_SetMode 0x0000
|
||||
#define SWIMS_SET_MODE_Modem 0x0001
|
||||
|
||||
#define TRU_NORMAL 0x01
|
||||
#define TRU_FORCE_MS 0x02
|
||||
#define TRU_FORCE_MODEM 0x03
|
||||
|
||||
static unsigned int swi_tru_install = 1;
|
||||
module_param(swi_tru_install, uint, S_IRUGO | S_IWUSR);
|
||||
MODULE_PARM_DESC(swi_tru_install, "TRU-Install mode (1=Full Logic (def),"
|
||||
" 2=Force CD-Rom, 3=Force Modem)");
|
||||
|
||||
struct swoc_info {
|
||||
__u8 rev;
|
||||
__u8 reserved[8];
|
||||
__u16 LinuxSKU;
|
||||
__u16 LinuxVer;
|
||||
__u8 reserved2[47];
|
||||
} __attribute__((__packed__));
|
||||
|
||||
static bool containsFullLinuxPackage(struct swoc_info *swocInfo)
|
||||
{
|
||||
if ((swocInfo->LinuxSKU >= 0x2100 && swocInfo->LinuxSKU <= 0x2FFF) ||
|
||||
(swocInfo->LinuxSKU >= 0x7100 && swocInfo->LinuxSKU <= 0x7FFF))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
static int sierra_set_ms_mode(struct usb_device *udev, __u16 eSWocMode)
|
||||
{
|
||||
int result;
|
||||
US_DEBUGP("SWIMS: %s", "DEVICE MODE SWITCH\n");
|
||||
result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
|
||||
SWIMS_USB_REQUEST_SetSwocMode, /* __u8 request */
|
||||
USB_TYPE_VENDOR | USB_DIR_OUT, /* __u8 request type */
|
||||
eSWocMode, /* __u16 value */
|
||||
0x0000, /* __u16 index */
|
||||
NULL, /* void *data */
|
||||
0, /* __u16 size */
|
||||
USB_CTRL_SET_TIMEOUT); /* int timeout */
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
static int sierra_get_swoc_info(struct usb_device *udev,
|
||||
struct swoc_info *swocInfo)
|
||||
{
|
||||
int result;
|
||||
|
||||
US_DEBUGP("SWIMS: Attempting to get TRU-Install info.\n");
|
||||
|
||||
result = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
|
||||
SWIMS_USB_REQUEST_GetSwocInfo, /* __u8 request */
|
||||
USB_TYPE_VENDOR | USB_DIR_IN, /* __u8 request type */
|
||||
0, /* __u16 value */
|
||||
0, /* __u16 index */
|
||||
(void *) swocInfo, /* void *data */
|
||||
sizeof(struct swoc_info), /* __u16 size */
|
||||
USB_CTRL_SET_TIMEOUT); /* int timeout */
|
||||
|
||||
swocInfo->LinuxSKU = le16_to_cpu(swocInfo->LinuxSKU);
|
||||
swocInfo->LinuxVer = le16_to_cpu(swocInfo->LinuxVer);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void debug_swoc(struct swoc_info *swocInfo)
|
||||
{
|
||||
US_DEBUGP("SWIMS: SWoC Rev: %02d \n", swocInfo->rev);
|
||||
US_DEBUGP("SWIMS: Linux SKU: %04X \n", swocInfo->LinuxSKU);
|
||||
US_DEBUGP("SWIMS: Linux Version: %04X \n", swocInfo->LinuxVer);
|
||||
}
|
||||
|
||||
|
||||
static ssize_t show_truinst(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct swoc_info *swocInfo;
|
||||
struct usb_interface *intf = to_usb_interface(dev);
|
||||
struct usb_device *udev = interface_to_usbdev(intf);
|
||||
int result;
|
||||
if (swi_tru_install == TRU_FORCE_MS) {
|
||||
result = snprintf(buf, PAGE_SIZE, "Forced Mass Storage\n");
|
||||
} else {
|
||||
swocInfo = kmalloc(sizeof(struct swoc_info), GFP_KERNEL);
|
||||
if (!swocInfo) {
|
||||
US_DEBUGP("SWIMS: Allocation failure\n");
|
||||
snprintf(buf, PAGE_SIZE, "Error\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
result = sierra_get_swoc_info(udev, swocInfo);
|
||||
if (result < 0) {
|
||||
US_DEBUGP("SWIMS: failed SWoC query\n");
|
||||
kfree(swocInfo);
|
||||
snprintf(buf, PAGE_SIZE, "Error\n");
|
||||
return -EIO;
|
||||
}
|
||||
debug_swoc(swocInfo);
|
||||
result = snprintf(buf, PAGE_SIZE,
|
||||
"REV=%02d SKU=%04X VER=%04X\n",
|
||||
swocInfo->rev,
|
||||
swocInfo->LinuxSKU,
|
||||
swocInfo->LinuxVer);
|
||||
kfree(swocInfo);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
static DEVICE_ATTR(truinst, S_IWUGO | S_IRUGO, show_truinst, NULL);
|
||||
|
||||
int sierra_ms_init(struct us_data *us)
|
||||
{
|
||||
int result, retries;
|
||||
signed long delay_t;
|
||||
struct swoc_info *swocInfo;
|
||||
struct usb_device *udev;
|
||||
struct Scsi_Host *sh;
|
||||
struct scsi_device *sd;
|
||||
|
||||
delay_t = 2;
|
||||
retries = 3;
|
||||
result = 0;
|
||||
udev = us->pusb_dev;
|
||||
|
||||
sh = us_to_host(us);
|
||||
sd = scsi_get_host_dev(sh);
|
||||
|
||||
US_DEBUGP("SWIMS: sierra_ms_init called\n");
|
||||
|
||||
/* Force Modem mode */
|
||||
if (swi_tru_install == TRU_FORCE_MODEM) {
|
||||
US_DEBUGP("SWIMS: %s", "Forcing Modem Mode\n");
|
||||
result = sierra_set_ms_mode(udev, SWIMS_SET_MODE_Modem);
|
||||
if (result < 0)
|
||||
US_DEBUGP("SWIMS: Failed to switch to modem mode.\n");
|
||||
return -EIO;
|
||||
}
|
||||
/* Force Mass Storage mode (keep CD-Rom) */
|
||||
else if (swi_tru_install == TRU_FORCE_MS) {
|
||||
US_DEBUGP("SWIMS: %s", "Forcing Mass Storage Mode\n");
|
||||
goto complete;
|
||||
}
|
||||
/* Normal TRU-Install Logic */
|
||||
else {
|
||||
US_DEBUGP("SWIMS: %s", "Normal SWoC Logic\n");
|
||||
|
||||
swocInfo = kmalloc(sizeof(struct swoc_info),
|
||||
GFP_KERNEL);
|
||||
if (!swocInfo) {
|
||||
US_DEBUGP("SWIMS: %s", "Allocation failure\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
retries = 3;
|
||||
do {
|
||||
retries--;
|
||||
result = sierra_get_swoc_info(udev, swocInfo);
|
||||
if (result < 0) {
|
||||
US_DEBUGP("SWIMS: %s", "Failed SWoC query\n");
|
||||
schedule_timeout_uninterruptible(2*HZ);
|
||||
}
|
||||
} while (retries && result < 0);
|
||||
|
||||
if (result < 0) {
|
||||
US_DEBUGP("SWIMS: %s",
|
||||
"Completely failed SWoC query\n");
|
||||
kfree(swocInfo);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
debug_swoc(swocInfo);
|
||||
|
||||
/* If there is not Linux software on the TRU-Install device
|
||||
* then switch to modem mode
|
||||
*/
|
||||
if (!containsFullLinuxPackage(swocInfo)) {
|
||||
US_DEBUGP("SWIMS: %s",
|
||||
"Switching to Modem Mode\n");
|
||||
result = sierra_set_ms_mode(udev,
|
||||
SWIMS_SET_MODE_Modem);
|
||||
if (result < 0)
|
||||
US_DEBUGP("SWIMS: Failed to switch modem\n");
|
||||
kfree(swocInfo);
|
||||
return -EIO;
|
||||
}
|
||||
kfree(swocInfo);
|
||||
}
|
||||
complete:
|
||||
result = device_create_file(&us->pusb_intf->dev, &dev_attr_truinst);
|
||||
|
||||
return USB_STOR_TRANSPORT_GOOD;
|
||||
}
|
||||
|
4
drivers/usb/storage/sierra_ms.h
Normal file
4
drivers/usb/storage/sierra_ms.h
Normal file
@ -0,0 +1,4 @@
|
||||
#ifndef _SIERRA_MS_H_
|
||||
#define _SIERRA_MS_H_
|
||||
extern int sierra_ms_init(struct us_data *us);
|
||||
#endif
|
@ -1032,8 +1032,21 @@ int usb_stor_Bulk_transport(struct scsi_cmnd *srb, struct us_data *us)
|
||||
|
||||
/* try to compute the actual residue, based on how much data
|
||||
* was really transferred and what the device tells us */
|
||||
if (residue) {
|
||||
if (!(us->fflags & US_FL_IGNORE_RESIDUE)) {
|
||||
if (residue && !(us->fflags & US_FL_IGNORE_RESIDUE)) {
|
||||
|
||||
/* Heuristically detect devices that generate bogus residues
|
||||
* by seeing what happens with INQUIRY and READ CAPACITY
|
||||
* commands.
|
||||
*/
|
||||
if (bcs->Status == US_BULK_STAT_OK &&
|
||||
scsi_get_resid(srb) == 0 &&
|
||||
((srb->cmnd[0] == INQUIRY &&
|
||||
transfer_length == 36) ||
|
||||
(srb->cmnd[0] == READ_CAPACITY &&
|
||||
transfer_length == 8))) {
|
||||
us->fflags |= US_FL_IGNORE_RESIDUE;
|
||||
|
||||
} else {
|
||||
residue = min(residue, transfer_length);
|
||||
scsi_set_resid(srb, max(scsi_get_resid(srb),
|
||||
(int) residue));
|
||||
|
@ -225,6 +225,13 @@ UNUSUAL_DEV( 0x0421, 0x0495, 0x0370, 0x0370,
|
||||
US_SC_DEVICE, US_PR_DEVICE, NULL,
|
||||
US_FL_MAX_SECTORS_64 ),
|
||||
|
||||
/* Reported by Cedric Godin <cedric@belbone.be> */
|
||||
UNUSUAL_DEV( 0x0421, 0x04b9, 0x0551, 0x0551,
|
||||
"Nokia",
|
||||
"5300",
|
||||
US_SC_DEVICE, US_PR_DEVICE, NULL,
|
||||
US_FL_FIX_CAPACITY ),
|
||||
|
||||
/* Reported by Olaf Hering <olh@suse.de> from novell bug #105878 */
|
||||
UNUSUAL_DEV( 0x0424, 0x0fdc, 0x0210, 0x0210,
|
||||
"SMSC",
|
||||
@ -356,14 +363,14 @@ UNUSUAL_DEV( 0x04b0, 0x040f, 0x0100, 0x0200,
|
||||
US_FL_FIX_CAPACITY),
|
||||
|
||||
/* Reported by Emil Larsson <emil@swip.net> */
|
||||
UNUSUAL_DEV( 0x04b0, 0x0411, 0x0100, 0x0110,
|
||||
UNUSUAL_DEV( 0x04b0, 0x0411, 0x0100, 0x0111,
|
||||
"NIKON",
|
||||
"NIKON DSC D80",
|
||||
US_SC_DEVICE, US_PR_DEVICE, NULL,
|
||||
US_FL_FIX_CAPACITY),
|
||||
|
||||
/* Reported by Ortwin Glueck <odi@odi.ch> */
|
||||
UNUSUAL_DEV( 0x04b0, 0x0413, 0x0110, 0x0110,
|
||||
UNUSUAL_DEV( 0x04b0, 0x0413, 0x0110, 0x0111,
|
||||
"NIKON",
|
||||
"NIKON DSC D40",
|
||||
US_SC_DEVICE, US_PR_DEVICE, NULL,
|
||||
@ -1185,6 +1192,13 @@ UNUSUAL_DEV( 0x07c4, 0xa400, 0x0000, 0xffff,
|
||||
US_SC_DEVICE, US_PR_DEVICE, NULL,
|
||||
US_FL_FIX_INQUIRY ),
|
||||
|
||||
/* Reported by Rauch Wolke <rauchwolke@gmx.net> */
|
||||
UNUSUAL_DEV( 0x07c4, 0xa4a5, 0x0000, 0xffff,
|
||||
"Simple Tech/Datafab",
|
||||
"CF+SM Reader",
|
||||
US_SC_DEVICE, US_PR_DEVICE, NULL,
|
||||
US_FL_IGNORE_RESIDUE ),
|
||||
|
||||
/* Casio QV 2x00/3x00/4000/8000 digital still cameras are not conformant
|
||||
* to the USB storage specification in two ways:
|
||||
* - They tell us they are using transport protocol CBI. In reality they
|
||||
@ -1562,6 +1576,7 @@ UNUSUAL_DEV( 0x10d6, 0x2200, 0x0100, 0x0100,
|
||||
US_SC_DEVICE, US_PR_DEVICE, NULL,
|
||||
0),
|
||||
|
||||
#ifdef CONFIG_USB_STORAGE_SIERRA
|
||||
/* Reported by Kevin Lloyd <linux@sierrawireless.com>
|
||||
* Entry is needed for the initializer function override,
|
||||
* which instructs the device to load as a modem
|
||||
@ -1570,8 +1585,9 @@ UNUSUAL_DEV( 0x10d6, 0x2200, 0x0100, 0x0100,
|
||||
UNUSUAL_DEV( 0x1199, 0x0fff, 0x0000, 0x9999,
|
||||
"Sierra Wireless",
|
||||
"USB MMC Storage",
|
||||
US_SC_DEVICE, US_PR_DEVICE, NULL,
|
||||
US_FL_IGNORE_DEVICE),
|
||||
US_SC_DEVICE, US_PR_DEVICE, sierra_ms_init,
|
||||
0),
|
||||
#endif
|
||||
|
||||
/* Reported by Jaco Kroon <jaco@kroon.co.za>
|
||||
* The usb-storage module found on the Digitech GNX4 (and supposedly other
|
||||
@ -1742,6 +1758,15 @@ UNUSUAL_DEV( 0x22b8, 0x4810, 0x0001, 0x0002,
|
||||
US_SC_DEVICE, US_PR_DEVICE, NULL,
|
||||
US_FL_FIX_CAPACITY),
|
||||
|
||||
/*
|
||||
* Patch by Jost Diederichs <jost@qdusa.com>
|
||||
*/
|
||||
UNUSUAL_DEV(0x22b8, 0x6410, 0x0001, 0x9999,
|
||||
"Motorola Inc.",
|
||||
"Motorola Phone (RAZRV3xx)",
|
||||
US_SC_DEVICE, US_PR_DEVICE, NULL,
|
||||
US_FL_FIX_CAPACITY),
|
||||
|
||||
/*
|
||||
* Patch by Constantin Baranov <const@tltsu.ru>
|
||||
* Report by Andreas Koenecke.
|
||||
@ -1767,6 +1792,13 @@ UNUSUAL_DEV( 0x2770, 0x915d, 0x0010, 0x0010,
|
||||
US_SC_DEVICE, US_PR_DEVICE, NULL,
|
||||
US_FL_FIX_CAPACITY ),
|
||||
|
||||
/* Reported by Andrey Rahmatullin <wrar@altlinux.org> */
|
||||
UNUSUAL_DEV( 0x4102, 0x1020, 0x0100, 0x0100,
|
||||
"iRiver",
|
||||
"MP3 T10",
|
||||
US_SC_DEVICE, US_PR_DEVICE, NULL,
|
||||
US_FL_IGNORE_RESIDUE ),
|
||||
|
||||
/*
|
||||
* David Härdeman <david@2gen.com>
|
||||
* The key makes the SCSI stack print confusing (but harmless) messages
|
||||
|
@ -102,6 +102,9 @@
|
||||
#ifdef CONFIG_USB_STORAGE_CYPRESS_ATACB
|
||||
#include "cypress_atacb.h"
|
||||
#endif
|
||||
#ifdef CONFIG_USB_STORAGE_SIERRA
|
||||
#include "sierra_ms.h"
|
||||
#endif
|
||||
|
||||
/* Some informational data */
|
||||
MODULE_AUTHOR("Matthew Dharm <mdharm-usb@one-eyed-alien.net>");
|
||||
|
@ -110,6 +110,8 @@ enum usb_interface_condition {
|
||||
* @sysfs_files_created: sysfs attributes exist
|
||||
* @needs_remote_wakeup: flag set when the driver requires remote-wakeup
|
||||
* capability during autosuspend.
|
||||
* @needs_binding: flag set when the driver should be re-probed or unbound
|
||||
* following a reset or suspend operation it doesn't support.
|
||||
* @dev: driver model's view of this device
|
||||
* @usb_dev: if an interface is bound to the USB major, this will point
|
||||
* to the sysfs representation for that device.
|
||||
|
98
include/linux/usb/musb.h
Normal file
98
include/linux/usb/musb.h
Normal file
@ -0,0 +1,98 @@
|
||||
/*
|
||||
* This is used to for host and peripheral modes of the driver for
|
||||
* Inventra (Multidrop) Highspeed Dual-Role Controllers: (M)HDRC.
|
||||
*
|
||||
* Board initialization should put one of these into dev->platform_data,
|
||||
* probably on some platform_device named "musb_hdrc". It encapsulates
|
||||
* key configuration differences between boards.
|
||||
*/
|
||||
|
||||
/* The USB role is defined by the connector used on the board, so long as
|
||||
* standards are being followed. (Developer boards sometimes won't.)
|
||||
*/
|
||||
enum musb_mode {
|
||||
MUSB_UNDEFINED = 0,
|
||||
MUSB_HOST, /* A or Mini-A connector */
|
||||
MUSB_PERIPHERAL, /* B or Mini-B connector */
|
||||
MUSB_OTG /* Mini-AB connector */
|
||||
};
|
||||
|
||||
struct clk;
|
||||
|
||||
struct musb_hdrc_eps_bits {
|
||||
const char name[16];
|
||||
u8 bits;
|
||||
};
|
||||
|
||||
struct musb_hdrc_config {
|
||||
/* MUSB configuration-specific details */
|
||||
unsigned multipoint:1; /* multipoint device */
|
||||
unsigned dyn_fifo:1; /* supports dynamic fifo sizing */
|
||||
unsigned soft_con:1; /* soft connect required */
|
||||
unsigned utm_16:1; /* utm data witdh is 16 bits */
|
||||
unsigned big_endian:1; /* true if CPU uses big-endian */
|
||||
unsigned mult_bulk_tx:1; /* Tx ep required for multbulk pkts */
|
||||
unsigned mult_bulk_rx:1; /* Rx ep required for multbulk pkts */
|
||||
unsigned high_iso_tx:1; /* Tx ep required for HB iso */
|
||||
unsigned high_iso_rx:1; /* Rx ep required for HD iso */
|
||||
unsigned dma:1; /* supports DMA */
|
||||
unsigned vendor_req:1; /* vendor registers required */
|
||||
|
||||
u8 num_eps; /* number of endpoints _with_ ep0 */
|
||||
u8 dma_channels; /* number of dma channels */
|
||||
u8 dyn_fifo_size; /* dynamic size in bytes */
|
||||
u8 vendor_ctrl; /* vendor control reg width */
|
||||
u8 vendor_stat; /* vendor status reg witdh */
|
||||
u8 dma_req_chan; /* bitmask for required dma channels */
|
||||
u8 ram_bits; /* ram address size */
|
||||
|
||||
struct musb_hdrc_eps_bits *eps_bits;
|
||||
};
|
||||
|
||||
struct musb_hdrc_platform_data {
|
||||
/* MUSB_HOST, MUSB_PERIPHERAL, or MUSB_OTG */
|
||||
u8 mode;
|
||||
|
||||
/* for clk_get() */
|
||||
const char *clock;
|
||||
|
||||
/* (HOST or OTG) switch VBUS on/off */
|
||||
int (*set_vbus)(struct device *dev, int is_on);
|
||||
|
||||
/* (HOST or OTG) mA/2 power supplied on (default = 8mA) */
|
||||
u8 power;
|
||||
|
||||
/* (PERIPHERAL) mA/2 max power consumed (default = 100mA) */
|
||||
u8 min_power;
|
||||
|
||||
/* (HOST or OTG) msec/2 after VBUS on till power good */
|
||||
u8 potpgt;
|
||||
|
||||
/* Power the device on or off */
|
||||
int (*set_power)(int state);
|
||||
|
||||
/* Turn device clock on or off */
|
||||
int (*set_clock)(struct clk *clock, int is_on);
|
||||
|
||||
/* MUSB configuration-specific details */
|
||||
struct musb_hdrc_config *config;
|
||||
};
|
||||
|
||||
|
||||
/* TUSB 6010 support */
|
||||
|
||||
#define TUSB6010_OSCCLK_60 16667 /* psec/clk @ 60.0 MHz */
|
||||
#define TUSB6010_REFCLK_24 41667 /* psec/clk @ 24.0 MHz XI */
|
||||
#define TUSB6010_REFCLK_19 52083 /* psec/clk @ 19.2 MHz CLKIN */
|
||||
|
||||
#ifdef CONFIG_ARCH_OMAP2
|
||||
|
||||
extern int __init tusb6010_setup_interface(
|
||||
struct musb_hdrc_platform_data *data,
|
||||
unsigned ps_refclk, unsigned waitpin,
|
||||
unsigned async_cs, unsigned sync_cs,
|
||||
unsigned irq, unsigned dmachan);
|
||||
|
||||
extern int tusb6010_platform_retime(unsigned is_refclk);
|
||||
|
||||
#endif /* OMAP2 */
|
@ -17,7 +17,8 @@
|
||||
#include <linux/mutex.h>
|
||||
|
||||
#define SERIAL_TTY_MAJOR 188 /* Nice legal number now */
|
||||
#define SERIAL_TTY_MINORS 255 /* loads of devices :) */
|
||||
#define SERIAL_TTY_MINORS 254 /* loads of devices :) */
|
||||
#define SERIAL_TTY_NO_MINOR 255 /* No minor was assigned */
|
||||
|
||||
/* The maximum number of ports one device can grab at once */
|
||||
#define MAX_NUM_PORTS 8
|
||||
|
Loading…
Reference in New Issue
Block a user