Commit 51feb98d authored by Linus Torvalds's avatar Linus Torvalds

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: (48 commits)
  USB: otg: fix module reinsert issue
  USB: handle zero-length usbfs submissions correctly
  USB: EHCI: report actual_length for iso transfers
  USB: option: remove unnecessary and erroneous code
  USB: cypress_m8: remove invalid Clear-Halt
  USB: musb_host: undo incorrect change in musb_advance_schedule()
  USB: fix LANGID=0 regression
  USB: serial: sierra driver id_table additions
  USB serial: Add ID for Turtelizer, an FT2232L-based JTAG/RS-232 adapter.
  USB: fix race leading to a write after kfree in usbfs
  USB: Sierra: fix oops upon device close
  USB: option.c: add A-Link 3GU device id
  USB: Serial: Add support for Arkham Technology adapters
  USB: Fix option_ms regression in 2.6.31-rc2
  USB: gadget audio: select SND_PCM
  USB: ftdi: support NDI devices
  Revert USB: usbfs: deprecate and hide option for !embedded
  USB: usb.h: fix kernel-doc notation
  USB: RNDIS gadget, fix issues talking from PXA
  USB: serial: FTDI with product code FB80 and vendor id 0403
  ...
parents 8f8f0134 dc7520c1
......@@ -387,6 +387,7 @@ static void acm_rx_tasklet(unsigned long _acm)
struct acm_ru *rcv;
unsigned long flags;
unsigned char throttled;
struct usb_host_endpoint *ep;
dbg("Entering acm_rx_tasklet");
......@@ -462,11 +463,20 @@ static void acm_rx_tasklet(unsigned long _acm)
rcv->buffer = buf;
usb_fill_bulk_urb(rcv->urb, acm->dev,
acm->rx_endpoint,
buf->base,
acm->readsize,
acm_read_bulk, rcv);
ep = (usb_pipein(acm->rx_endpoint) ? acm->dev->ep_in : acm->dev->ep_out)
[usb_pipeendpoint(acm->rx_endpoint)];
if (usb_endpoint_xfer_int(&ep->desc))
usb_fill_int_urb(rcv->urb, acm->dev,
acm->rx_endpoint,
buf->base,
acm->readsize,
acm_read_bulk, rcv, ep->desc.bInterval);
else
usb_fill_bulk_urb(rcv->urb, acm->dev,
acm->rx_endpoint,
buf->base,
acm->readsize,
acm_read_bulk, rcv);
rcv->urb->transfer_dma = buf->dma;
rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
......@@ -1227,9 +1237,14 @@ static int acm_probe(struct usb_interface *intf,
goto alloc_fail7;
}
usb_fill_bulk_urb(snd->urb, usb_dev,
usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
NULL, acm->writesize, acm_write_bulk, snd);
if (usb_endpoint_xfer_int(epwrite))
usb_fill_int_urb(snd->urb, usb_dev,
usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
NULL, acm->writesize, acm_write_bulk, snd, epwrite->bInterval);
else
usb_fill_bulk_urb(snd->urb, usb_dev,
usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
NULL, acm->writesize, acm_write_bulk, snd);
snd->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
snd->instance = acm;
}
......
......@@ -751,7 +751,7 @@ static int get_capabilities(struct usbtmc_device_data *data)
{
struct device *dev = &data->usb_dev->dev;
char *buffer;
int rv;
int rv = 0;
buffer = kmalloc(0x18, GFP_KERNEL);
if (!buffer)
......@@ -763,7 +763,7 @@ static int get_capabilities(struct usbtmc_device_data *data)
0, 0, buffer, 0x18, USBTMC_TIMEOUT);
if (rv < 0) {
dev_err(dev, "usb_control_msg returned %d\n", rv);
return rv;
goto err_out;
}
dev_dbg(dev, "GET_CAPABILITIES returned %x\n", buffer[0]);
......@@ -773,7 +773,8 @@ static int get_capabilities(struct usbtmc_device_data *data)
dev_dbg(dev, "USB488 device capabilities are %x\n", buffer[15]);
if (buffer[0] != USBTMC_STATUS_SUCCESS) {
dev_err(dev, "GET_CAPABILITIES returned %x\n", buffer[0]);
return -EPERM;
rv = -EPERM;
goto err_out;
}
data->capabilities.interface_capabilities = buffer[4];
......@@ -781,8 +782,9 @@ static int get_capabilities(struct usbtmc_device_data *data)
data->capabilities.usb488_interface_capabilities = buffer[14];
data->capabilities.usb488_device_capabilities = buffer[15];
err_out:
kfree(buffer);
return 0;
return rv;
}
#define capability_attribute(name) \
......
......@@ -28,7 +28,7 @@ comment "Miscellaneous USB options"
depends on USB
config USB_DEVICEFS
bool "USB device filesystem (DEPRECATED)" if EMBEDDED
bool "USB device filesystem (DEPRECATED)"
depends on USB
---help---
If you say Y here (and to "/proc file system support" in the "File
......
......@@ -136,17 +136,19 @@ static const struct class_info clas_info[] =
{USB_CLASS_AUDIO, "audio"},
{USB_CLASS_COMM, "comm."},
{USB_CLASS_HID, "HID"},
{USB_CLASS_HUB, "hub"},
{USB_CLASS_PHYSICAL, "PID"},
{USB_CLASS_STILL_IMAGE, "still"},
{USB_CLASS_PRINTER, "print"},
{USB_CLASS_MASS_STORAGE, "stor."},
{USB_CLASS_HUB, "hub"},
{USB_CLASS_CDC_DATA, "data"},
{USB_CLASS_APP_SPEC, "app."},
{USB_CLASS_VENDOR_SPEC, "vend."},
{USB_CLASS_STILL_IMAGE, "still"},
{USB_CLASS_CSCID, "scard"},
{USB_CLASS_CONTENT_SEC, "c-sec"},
{USB_CLASS_VIDEO, "video"},
{USB_CLASS_WIRELESS_CONTROLLER, "wlcon"},
{USB_CLASS_MISC, "misc"},
{USB_CLASS_APP_SPEC, "app."},
{USB_CLASS_VENDOR_SPEC, "vend."},
{-1, "unk."} /* leave as last */
};
......
......@@ -325,21 +325,34 @@ static void async_completed(struct urb *urb)
struct async *as = urb->context;
struct dev_state *ps = as->ps;
struct siginfo sinfo;
struct pid *pid = NULL;
uid_t uid = 0;
uid_t euid = 0;
u32 secid = 0;
int signr;
spin_lock(&ps->lock);
list_move_tail(&as->asynclist, &ps->async_completed);
spin_unlock(&ps->lock);
as->status = urb->status;
if (as->signr) {
signr = as->signr;
if (signr) {
sinfo.si_signo = as->signr;
sinfo.si_errno = as->status;
sinfo.si_code = SI_ASYNCIO;
sinfo.si_addr = as->userurb;
kill_pid_info_as_uid(as->signr, &sinfo, as->pid, as->uid,
as->euid, as->secid);
pid = as->pid;
uid = as->uid;
euid = as->euid;
secid = as->secid;
}
snoop(&urb->dev->dev, "urb complete\n");
snoop_urb(urb, as->userurb);
spin_unlock(&ps->lock);
if (signr)
kill_pid_info_as_uid(sinfo.si_signo, &sinfo, pid, uid,
euid, secid);
wake_up(&ps->wait);
}
......@@ -982,7 +995,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
USBDEVFS_URB_ZERO_PACKET |
USBDEVFS_URB_NO_INTERRUPT))
return -EINVAL;
if (!uurb->buffer)
if (uurb->buffer_length > 0 && !uurb->buffer)
return -EINVAL;
if (!(uurb->type == USBDEVFS_URB_TYPE_CONTROL &&
(uurb->endpoint & ~USB_ENDPOINT_DIR_MASK) == 0)) {
......@@ -1038,11 +1051,6 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
is_in = 0;
uurb->endpoint &= ~USB_DIR_IN;
}
if (!access_ok(is_in ? VERIFY_WRITE : VERIFY_READ,
uurb->buffer, uurb->buffer_length)) {
kfree(dr);
return -EFAULT;
}
snoop(&ps->dev->dev, "control urb: bRequest=%02x "
"bRrequestType=%02x wValue=%04x "
"wIndex=%04x wLength=%04x\n",
......@@ -1062,9 +1070,6 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
uurb->number_of_packets = 0;
if (uurb->buffer_length > MAX_USBFS_BUFFER_SIZE)
return -EINVAL;
if (!access_ok(is_in ? VERIFY_WRITE : VERIFY_READ,
uurb->buffer, uurb->buffer_length))
return -EFAULT;
snoop(&ps->dev->dev, "bulk urb\n");
break;
......@@ -1106,28 +1111,35 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
return -EINVAL;
if (uurb->buffer_length > MAX_USBFS_BUFFER_SIZE)
return -EINVAL;
if (!access_ok(is_in ? VERIFY_WRITE : VERIFY_READ,
uurb->buffer, uurb->buffer_length))
return -EFAULT;
snoop(&ps->dev->dev, "interrupt urb\n");
break;
default:
return -EINVAL;
}
as = alloc_async(uurb->number_of_packets);
if (!as) {
if (uurb->buffer_length > 0 &&
!access_ok(is_in ? VERIFY_WRITE : VERIFY_READ,
uurb->buffer, uurb->buffer_length)) {
kfree(isopkt);
kfree(dr);
return -ENOMEM;
return -EFAULT;
}
as->urb->transfer_buffer = kmalloc(uurb->buffer_length, GFP_KERNEL);
if (!as->urb->transfer_buffer) {
as = alloc_async(uurb->number_of_packets);
if (!as) {
kfree(isopkt);
kfree(dr);
free_async(as);
return -ENOMEM;
}
if (uurb->buffer_length > 0) {
as->urb->transfer_buffer = kmalloc(uurb->buffer_length,
GFP_KERNEL);
if (!as->urb->transfer_buffer) {
kfree(isopkt);
kfree(dr);
free_async(as);
return -ENOMEM;
}
}
as->urb->dev = ps->dev;
as->urb->pipe = (uurb->type << 30) |
__create_pipe(ps->dev, uurb->endpoint & 0xf) |
......@@ -1169,7 +1181,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
kfree(isopkt);
as->ps = ps;
as->userurb = arg;
if (uurb->endpoint & USB_DIR_IN)
if (is_in && uurb->buffer_length > 0)
as->userbuffer = uurb->buffer;
else
as->userbuffer = NULL;
......@@ -1179,9 +1191,9 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
as->uid = cred->uid;
as->euid = cred->euid;
security_task_getsecid(current, &as->secid);
if (!is_in) {
if (!is_in && uurb->buffer_length > 0) {
if (copy_from_user(as->urb->transfer_buffer, uurb->buffer,
as->urb->transfer_buffer_length)) {
uurb->buffer_length)) {
free_async(as);
return -EFAULT;
}
......@@ -1231,22 +1243,22 @@ static int processcompl(struct async *as, void __user * __user *arg)
if (as->userbuffer)
if (copy_to_user(as->userbuffer, urb->transfer_buffer,
urb->transfer_buffer_length))
return -EFAULT;
goto err_out;
if (put_user(as->status, &userurb->status))
return -EFAULT;
goto err_out;
if (put_user(urb->actual_length, &userurb->actual_length))
return -EFAULT;
goto err_out;
if (put_user(urb->error_count, &userurb->error_count))
return -EFAULT;
goto err_out;
if (usb_endpoint_xfer_isoc(&urb->ep->desc)) {
for (i = 0; i < urb->number_of_packets; i++) {
if (put_user(urb->iso_frame_desc[i].actual_length,
&userurb->iso_frame_desc[i].actual_length))
return -EFAULT;
goto err_out;
if (put_user(urb->iso_frame_desc[i].status,
&userurb->iso_frame_desc[i].status))
return -EFAULT;
goto err_out;
}
}
......@@ -1255,6 +1267,10 @@ static int processcompl(struct async *as, void __user * __user *arg)
if (put_user(addr, (void __user * __user *)arg))
return -EFAULT;
return 0;
err_out:
free_async(as);
return -EFAULT;
}
static struct async *reap_as(struct dev_state *ps)
......
......@@ -227,6 +227,10 @@ struct hc_driver {
/* has a port been handed over to a companion? */
int (*port_handed_over)(struct usb_hcd *, int);
/* CLEAR_TT_BUFFER completion callback */
void (*clear_tt_buffer_complete)(struct usb_hcd *,
struct usb_host_endpoint *);
/* xHCI specific functions */
/* Called by usb_alloc_dev to alloc HC device structures */
int (*alloc_dev)(struct usb_hcd *, struct usb_device *);
......
......@@ -450,10 +450,10 @@ hub_clear_tt_buffer (struct usb_device *hdev, u16 devinfo, u16 tt)
* talking to TTs must queue control transfers (not just bulk and iso), so
* both can talk to the same hub concurrently.
*/
static void hub_tt_kevent (struct work_struct *work)
static void hub_tt_work(struct work_struct *work)
{
struct usb_hub *hub =
container_of(work, struct usb_hub, tt.kevent);
container_of(work, struct usb_hub, tt.clear_work);
unsigned long flags;
int limit = 100;
......@@ -462,6 +462,7 @@ static void hub_tt_kevent (struct work_struct *work)
struct list_head *next;
struct usb_tt_clear *clear;
struct usb_device *hdev = hub->hdev;
const struct hc_driver *drv;
int status;
next = hub->tt.clear_list.next;
......@@ -471,21 +472,25 @@ static void hub_tt_kevent (struct work_struct *work)
/* drop lock so HCD can concurrently report other TT errors */
spin_unlock_irqrestore (&hub->tt.lock, flags);
status = hub_clear_tt_buffer (hdev, clear->devinfo, clear->tt);
spin_lock_irqsave (&hub->tt.lock, flags);
if (status)
dev_err (&hdev->dev,
"clear tt %d (%04x) error %d\n",
clear->tt, clear->devinfo, status);
/* Tell the HCD, even if the operation failed */
drv = clear->hcd->driver;
if (drv->clear_tt_buffer_complete)
(drv->clear_tt_buffer_complete)(clear->hcd, clear->ep);
kfree(clear);
spin_lock_irqsave(&hub->tt.lock, flags);
}
spin_unlock_irqrestore (&hub->tt.lock, flags);
}
/**
* usb_hub_tt_clear_buffer - clear control/bulk TT state in high speed hub
* @udev: the device whose split transaction failed
* @pipe: identifies the endpoint of the failed transaction
* usb_hub_clear_tt_buffer - clear control/bulk TT state in high speed hub
* @urb: an URB associated with the failed or incomplete split transaction
*
* High speed HCDs use this to tell the hub driver that some split control or
* bulk transaction failed in a way that requires clearing internal state of
......@@ -495,8 +500,10 @@ static void hub_tt_kevent (struct work_struct *work)
* It may not be possible for that hub to handle additional full (or low)
* speed transactions until that state is fully cleared out.
*/
void usb_hub_tt_clear_buffer (struct usb_device *udev, int pipe)
int usb_hub_clear_tt_buffer(struct urb *urb)
{
struct usb_device *udev = urb->dev;
int pipe = urb->pipe;
struct usb_tt *tt = udev->tt;
unsigned long flags;
struct usb_tt_clear *clear;
......@@ -508,7 +515,7 @@ void usb_hub_tt_clear_buffer (struct usb_device *udev, int pipe)
if ((clear = kmalloc (sizeof *clear, GFP_ATOMIC)) == NULL) {
dev_err (&udev->dev, "can't save CLEAR_TT_BUFFER state\n");
/* FIXME recover somehow ... RESET_TT? */
return;
return -ENOMEM;
}
/* info that CLEAR_TT_BUFFER needs */
......@@ -520,14 +527,19 @@ void usb_hub_tt_clear_buffer (struct usb_device *udev, int pipe)
: (USB_ENDPOINT_XFER_BULK << 11);
if (usb_pipein (pipe))
clear->devinfo |= 1 << 15;
/* info for completion callback */
clear->hcd = bus_to_hcd(udev->bus);
clear->ep = urb->ep;
/* tell keventd to clear state for this TT */
spin_lock_irqsave (&tt->lock, flags);
list_add_tail (&clear->clear_list, &tt->clear_list);
schedule_work (&tt->kevent);
schedule_work(&tt->clear_work);
spin_unlock_irqrestore (&tt->lock, flags);
return 0;
}
EXPORT_SYMBOL_GPL(usb_hub_tt_clear_buffer);
EXPORT_SYMBOL_GPL(usb_hub_clear_tt_buffer);
/* If do_delay is false, return the number of milliseconds the caller
* needs to delay.
......@@ -818,7 +830,7 @@ static void hub_quiesce(struct usb_hub *hub, enum hub_quiescing_type type)
if (hub->has_indicators)
cancel_delayed_work_sync(&hub->leds);
if (hub->tt.hub)
cancel_work_sync(&hub->tt.kevent);
cancel_work_sync(&hub->tt.clear_work);
}
/* caller has locked the hub device */
......@@ -935,7 +947,7 @@ static int hub_configure(struct usb_hub *hub,
spin_lock_init (&hub->tt.lock);
INIT_LIST_HEAD (&hub->tt.clear_list);
INIT_WORK (&hub->tt.kevent, hub_tt_kevent);
INIT_WORK(&hub->tt.clear_work, hub_tt_work);
switch (hdev->descriptor.bDeviceProtocol) {
case 0:
break;
......
......@@ -188,16 +188,18 @@ struct usb_tt {
/* for control/bulk error recovery (CLEAR_TT_BUFFER) */
spinlock_t lock;
struct list_head clear_list; /* of usb_tt_clear */
struct work_struct kevent;
struct work_struct clear_work;
};
struct usb_tt_clear {
struct list_head clear_list;
unsigned tt;
u16 devinfo;
struct usb_hcd *hcd;
struct usb_host_endpoint *ep;
};
extern void usb_hub_tt_clear_buffer(struct usb_device *dev, int pipe);
extern int usb_hub_clear_tt_buffer(struct urb *urb);
extern void usb_ep0_reinit(struct usb_device *);
#endif /* __LINUX_HUB_H */
......@@ -806,6 +806,48 @@ static int usb_string_sub(struct usb_device *dev, unsigned int langid,
return rc;
}
static int usb_get_langid(struct usb_device *dev, unsigned char *tbuf)
{
int err;
if (dev->have_langid)
return 0;
if (dev->string_langid < 0)
return -EPIPE;
err = usb_string_sub(dev, 0, 0, tbuf);
/* If the string was reported but is malformed, default to english
* (0x0409) */
if (err == -ENODATA || (err > 0 && err < 4)) {
dev->string_langid = 0x0409;
dev->have_langid = 1;
dev_err(&dev->dev,
"string descriptor 0 malformed (err = %d), "
"defaulting to 0x%04x\n",
err, dev->string_langid);
return 0;
}
/* In case of all other errors, we assume the device is not able to
* deal with strings at all. Set string_langid to -1 in order to
* prevent any string to be retrieved from the device */
if (err < 0) {
dev_err(&dev->dev, "string descriptor 0 read error: %d\n",
err);
dev->string_langid = -1;
return -EPIPE;
}
/* always use the first langid listed */
dev->string_langid = tbuf[2] | (tbuf[3] << 8);
dev->have_langid = 1;
dev_dbg(&dev->dev, "default language 0x%04x\n",
dev->string_langid);
return 0;
}
/**
* usb_string - returns UTF-8 version of a string descriptor
* @dev: the device whose string descriptor is being retrieved
......@@ -837,24 +879,9 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size)
if (!tbuf)
return -ENOMEM;
/* get langid for strings if it's not yet known */
if (!dev->have_langid) {
err = usb_string_sub(dev, 0, 0, tbuf);
if (err < 0) {
dev_err(&dev->dev,
"string descriptor 0 read error: %d\n",
err);
} else if (err < 4) {
dev_err(&dev->dev, "string descriptor 0 too short\n");
} else {
dev->string_langid = tbuf[2] | (tbuf[3] << 8);
/* always use the first langid listed */
dev_dbg(&dev->dev, "default language 0x%04x\n",
dev->string_langid);
}
dev->have_langid = 1;
}
err = usb_get_langid(dev, tbuf);
if (err < 0)
goto errout;
err = usb_string_sub(dev, dev->string_langid, index, tbuf);
if (err < 0)
......
......@@ -286,6 +286,27 @@ config USB_S3C_HSOTG
default USB_GADGET
select USB_GADGET_SELECTED
config USB_GADGET_IMX
boolean "Freescale IMX USB Peripheral Controller"
depends on ARCH_MX1
help
Freescale's IMX series include an integrated full speed
USB 1.1 device controller. The controller in the IMX series
is register-compatible.
It has Six fixed-function endpoints, as well as endpoint
zero (for control transfers).
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "imx_udc" and force all
gadget drivers to also be dynamically linked.
config USB_IMX
tristate
depends on USB_GADGET_IMX
default USB_GADGET
select USB_GADGET_SELECTED
config USB_GADGET_S3C2410
boolean "S3C2410 USB Device Controller"
depends on ARCH_S3C2410
......@@ -321,27 +342,6 @@ config USB_GADGET_MUSB_HDRC
This OTG-capable silicon IP is used in dual designs including
the TI DaVinci, OMAP 243x, OMAP 343x, TUSB 6010, and ADI Blackfin
config USB_GADGET_IMX
boolean "Freescale IMX USB Peripheral Controller"
depends on ARCH_MX1
help
Freescale's IMX series include an integrated full speed
USB 1.1 device controller. The controller in the IMX series
is register-compatible.
It has Six fixed-function endpoints, as well as endpoint
zero (for control transfers).
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "imx_udc" and force all
gadget drivers to also be dynamically linked.
config USB_IMX
tristate
depends on USB_GADGET_IMX
default USB_GADGET
select USB_GADGET_SELECTED
config USB_GADGET_M66592
boolean "Renesas M66592 USB Peripheral Controller"
select USB_GADGET_DUALSPEED
......@@ -604,6 +604,7 @@ config USB_ZERO_HNPTEST
config USB_AUDIO
tristate "Audio Gadget (EXPERIMENTAL)"
depends on SND
select SND_PCM
help
Gadget Audio is compatible with USB Audio Class specification 1.0.
It will include at least one AudioControl interface, zero or more
......
......@@ -42,9 +42,9 @@
* Instead: allocate your own, using normal USB-IF procedures.
*/
/* Thanks to NetChip Technologies for donating this product ID. */
#define AUDIO_VENDOR_NUM 0x0525 /* NetChip */
#define AUDIO_PRODUCT_NUM 0xa4a1 /* Linux-USB Audio Gadget */
/* Thanks to Linux Foundation for donating this product ID. */
#define AUDIO_VENDOR_NUM 0x1d6b /* Linux Foundation */
#define AUDIO_PRODUCT_NUM 0x0101 /* Linux-USB Audio Gadget */
/*-------------------------------------------------------------------------*/
......
......@@ -293,15 +293,16 @@ static int __init eth_bind(struct usb_composite_dev *cdev)
/* CDC Subset */
eth_config_driver.label = "CDC Subset/SAFE";
device_desc.idVendor = cpu_to_le16(SIMPLE_VENDOR_NUM),
device_desc.idProduct = cpu_to_le16(SIMPLE_PRODUCT_NUM),
device_desc.bDeviceClass = USB_CLASS_VENDOR_SPEC;
device_desc.idVendor = cpu_to_le16(SIMPLE_VENDOR_NUM);
device_desc.idProduct = cpu_to_le16(SIMPLE_PRODUCT_NUM);
if (!has_rndis())
device_desc.bDeviceClass = USB_CLASS_VENDOR_SPEC;
}
if (has_rndis()) {
/* RNDIS plus ECM-or-Subset */
device_desc.idVendor = cpu_to_le16(RNDIS_VENDOR_NUM),
device_desc.idProduct = cpu_to_le16(RNDIS_PRODUCT_NUM),
device_desc.idVendor = cpu_to_le16(RNDIS_VENDOR_NUM);
device_desc.idProduct = cpu_to_le16(RNDIS_PRODUCT_NUM);
device_desc.bNumConfigurations = 2;
}
......
......@@ -139,7 +139,7 @@ static int is_vbus_present(void)
{
struct pxa2xx_udc_mach_info *mach = the_controller->mach;
if (mach->gpio_vbus) {
if (gpio_is_valid(mach->gpio_vbus)) {
int value = gpio_get_value(mach->gpio_vbus);
if (mach->gpio_vbus_inverted)
......@@ -158,7 +158,7 @@ static void pullup_off(void)
struct pxa2xx_udc_mach_info *mach = the_controller->mach;
int off_level = mach->gpio_pullup_inverted;
if (mach->gpio_pullup)
if (gpio_is_valid(mach->gpio_pullup))
gpio_set_value(mach->gpio_pullup, off_level);
else if (mach->udc_command)
mach->udc_command(PXA2XX_UDC_CMD_DISCONNECT);
......@@ -169,7 +169,7 @@ static void pullup_on(void)
struct pxa2xx_udc_mach_info *mach = the_controller->mach;
int on_level = !mach->gpio_pullup_inverted;
if (mach->gpio_pullup)
if (gpio_is_valid(mach->gpio_pullup))
gpio_set_value(mach->gpio_pullup, on_level);
else if (mach->udc_command)
mach->udc_command(PXA2XX_UDC_CMD_CONNECT);
......@@ -1000,7 +1000,7 @@ static int pxa25x_udc_pullup(struct usb_gadget *_gadget, int is_active)
udc = container_of(_gadget, struct pxa25x_udc, gadget);
/* not all boards support pullup control */
if (!udc->mach->gpio_pullup && !udc->mach->udc_command)
if (!gpio_is_valid(udc->mach->gpio_pullup) && !udc->mach->udc_command)
return -EOPNOTSUPP;
udc->pullup = (is_active != 0);
......@@ -1802,11 +1802,13 @@ pxa25x_udc_irq(int irq, void *_dev)
USIR0 |= tmp;
handled = 1;
}
#ifndef CONFIG_USB_PXA25X_SMALL
if (usir1 & tmp) {
handle_ep(&dev->ep[i+8]);
USIR1 |= tmp;
handled = 1;
}
#endif
}
}
......@@ -2160,7 +2162,7 @@ static int __init pxa25x_udc_probe(struct platform_device *pdev)
dev->dev = &pdev->dev;
dev->mach = pdev->dev.platform_data;
if (dev->mach->gpio_vbus) {
if (gpio_is_valid(dev->mach->gpio_vbus)) {
if ((retval = gpio_request(dev->mach->gpio_vbus,
"pxa25x_udc GPIO VBUS"))) {
dev_dbg(&pdev->dev,
......@@ -2173,7 +2175,7 @@ static int __init pxa25x_udc_probe(struct platform_device *pdev)
} else
vbus_irq = 0;
if (dev->mach->gpio_pullup) {
if (gpio_is_valid(dev->mach->gpio_pullup)) {
if ((retval = gpio_request(dev->mach->gpio_pullup,
"pca25x_udc GPIO PULLUP"))) {
dev_dbg(&pdev->dev,
......@@ -2256,10 +2258,10 @@ static int __init pxa25x_udc_probe(struct platform_device *pdev)
#endif
free_irq(irq, dev);
err_irq1:
if (dev->mach->gpio_pullup)
if (gpio_is_valid(dev->mach->gpio_pullup))
gpio_free(dev->mach->gpio_pullup);
err_gpio_pullup:
if (dev->mach->gpio_vbus)
if (gpio_is_valid(dev->mach->gpio_vbus))
gpio_free(dev->mach->gpio_vbus);
err_gpio_vbus:
clk_put(dev->clk);
......@@ -2294,11 +2296,11 @@ static int __exit pxa25x_udc_remove(struct platform_device *pdev)
free_irq(LUBBOCK_USB_IRQ, dev);
}
#endif
if (dev->mach->gpio_vbus) {
if (gpio_is_valid(dev->mach->gpio_vbus)) {
free_irq(gpio_to_irq(dev->mach->gpio_vbus), dev);
gpio_free(dev->mach->gpio_vbus);
}
if (dev->mach->gpio_pullup)
if (gpio_is_valid(dev->mach->gpio_pullup))
gpio_free(dev->mach->gpio_pullup);
clk_put(dev->clk);
......@@ -2329,7 +2331,7 @@ static int pxa25x_udc_suspend(struct platform_device *dev, pm_message_t state)
struct pxa25x_udc *udc = platform_get_drvdata(dev);
unsigned long flags;
if (!udc->mach->gpio_pullup && !udc->mach->udc_command)
if (!gpio_is_valid(udc->mach->gpio_pullup) && !udc->mach->udc_command)
WARNING("USB host won't detect disconnect!\n");
udc->suspended = 1;
......
......@@ -442,6 +442,8 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
case OID_802_3_MAC_OPTIONS:
pr_debug("%s: OID_802_3_MAC_OPTIONS\n", __func__);
*outbuf = cpu_to_le32(0);
retval = 0;
break;
/* ieee802.3 statistics OIDs (table 4-4) */
......
......@@ -181,26 +181,27 @@ config USB_OHCI_HCD_PPC_SOC
Enables support for the USB controller on the MPC52xx or
STB03xxx processor chip. If unsure, say Y.
config USB_OHCI_HCD_PPC_OF
bool "OHCI support for PPC USB controller on OF platform bus"
depends on USB_OHCI_HCD && PPC_OF
default y
---help---
Enables support for the USB controller PowerPC present on the
OpenFirmware platform bus.
config USB_OHCI_HCD_PPC_OF_BE
bool "Support big endian HC"
depends on USB_OHCI_HCD_PPC_OF
default y
bool "OHCI support for OF platform bus (big endian)"
depends on USB_OHCI_HCD && PPC_OF
select USB_OHCI_BIG_ENDIAN_DESC
select USB_OHCI_BIG_ENDIAN_MMIO
---help---
Enables support for big-endian USB controllers present on the
OpenFirmware platform bus.
config USB_OHCI_HCD_PPC_OF_LE
bool "Support little endian HC"
depends on USB_OHCI_HCD_PPC_OF
default n
bool "OHCI support for OF platform bus (little endian)"
depends on USB_OHCI_HCD && PPC_OF
select USB_OHCI_LITTLE_ENDIAN
---help---
Enables support for little-endian USB controllers present on the
OpenFirmware platform bus.
config USB_OHCI_HCD_PPC_OF
bool
depends on USB_OHCI_HCD && PPC_OF
default USB_OHCI_HCD_PPC_OF_BE || USB_OHCI_HCD_PPC_OF_LE
config USB_OHCI_HCD_PCI
bool "OHCI support for PCI-bus USB controllers"
......
......@@ -113,6 +113,8 @@ static const struct hc_driver ehci_au1xxx_hc_driver = {
.bus_resume = ehci_bus_resume,
.relinquish_port = ehci_relinquish_port,
.port_handed_over = ehci_port_handed_over,
.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
};
static int ehci_hcd_au1xxx_drv_probe(struct platform_device *pdev)
......
......@@ -325,6 +325,8 @@ static const struct hc_driver ehci_fsl_hc_driver = {
.bus_resume = ehci_bus_resume,
.relinquish_port = ehci_relinquish_port,
.port_handed_over = ehci_port_handed_over,
.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
};
static int ehci_fsl_drv_probe(struct platform_device *pdev)
......
......@@ -1003,6 +1003,8 @@ ehci_endpoint_disable (struct usb_hcd *hcd, struct usb_host_endpoint *ep)
schedule_timeout_uninterruptible(1);
goto rescan;
case QH_STATE_IDLE: /* fully unlinked */
if (qh->clearing_tt)
goto idle_timeout;
if (list_empty (&qh->qtd_list)) {
qh_put (qh);
break;
......@@ -1030,12 +1032,14 @@ ehci_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep)
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
struct ehci_qh *qh;
int eptype = usb_endpoint_type(&ep->desc);
int epnum = usb_endpoint_num(&ep->desc);
int is_out = usb_endpoint_dir_out(&ep->desc);
unsigned long flags;
if (eptype != USB_ENDPOINT_XFER_BULK && eptype != USB_ENDPOINT_XFER_INT)
return;
rescan:
spin_lock_irq(&ehci->lock);
spin_lock_irqsave(&ehci->lock, flags);
qh = ep->hcpriv;
/* For Bulk and Interrupt endpoints we maintain the toggle state
......@@ -1044,29 +1048,24 @@ ehci_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep)
* the toggle bit in the QH.
*/
if (qh) {
usb_settoggle(qh->dev, epnum, is_out, 0);
if (!list_empty(&qh->qtd_list)) {
WARN_ONCE(1, "clear_halt for a busy endpoint\n");
} else if (qh->qh_state == QH_STATE_IDLE) {
qh->hw_token &= ~cpu_to_hc32(ehci, QTD_TOGGLE);
} else {
/* It's not safe to write into the overlay area
* while the QH is active. Unlink it first and
* wait for the unlink to complete.
} else if (qh->qh_state == QH_STATE_LINKED) {
/* The toggle value in the QH can't be updated
* while the QH is active. Unlink it now;
* re-linking will call qh_refresh().
*/
if (qh->qh_state == QH_STATE_LINKED) {
if (eptype == USB_ENDPOINT_XFER_BULK) {
unlink_async(ehci, qh);
} else {
intr_deschedule(ehci, qh);
(void) qh_schedule(ehci, qh);
}
if (eptype == USB_ENDPOINT_XFER_BULK) {
unlink_async(ehci, qh);
} else {
intr_deschedule(ehci, qh);
(void) qh_schedule(ehci, qh);
}
spin_unlock_irq(&ehci->lock);
schedule_timeout_uninterruptible(1);
goto rescan;
}
}
spin_unlock_irq(&ehci->lock);
spin_unlock_irqrestore(&ehci->lock, flags);
}
static int ehci_get_frame (struct usb_hcd *hcd)
......
......@@ -61,6 +61,8 @@ static const struct hc_driver ixp4xx_ehci_hc_driver = {
#endif
.relinquish_port = ehci_relinquish_port,
.port_handed_over = ehci_port_handed_over,
.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
};
static int ixp4xx_ehci_probe(struct platform_device *pdev)
......
......@@ -165,6 +165,8 @@ static const struct hc_driver ehci_orion_hc_driver = {
.bus_resume = ehci_bus_resume,
.relinquish_port = ehci_relinquish_port,
.port_handed_over = ehci_port_handed_over,
.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
};
static void __init
......
......@@ -404,6 +404,8 @@ static const struct hc_driver ehci_pci_hc_driver = {
.bus_resume = ehci_bus_resume,
.relinquish_port = ehci_relinquish_port,
.port_handed_over = ehci_port_handed_over,
.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
};
/*-------------------------------------------------------------------------*/
......
......@@ -79,6 +79,8 @@ static const struct hc_driver ehci_ppc_of_hc_driver = {
#endif
.relinquish_port = ehci_relinquish_port,
.port_handed_over = ehci_port_handed_over,
.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
};
......
......@@ -75,6 +75,8 @@ static const struct hc_driver ps3_ehci_hc_driver = {
#endif
.relinquish_port = ehci_relinquish_port,
.port_handed_over = ehci_port_handed_over,
.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
};
static int __devinit ps3_ehci_probe(struct ps3_system_bus_device *dev)
......
......@@ -93,6 +93,22 @@ qh_update (struct ehci_hcd *ehci, struct ehci_qh *qh, struct ehci_qtd *qtd)
qh->hw_qtd_next = QTD_NEXT(ehci, qtd->qtd_dma);
qh->hw_alt_next = EHCI_LIST_END(ehci);
/* Except for control endpoints, we make hardware maintain data
* toggle (like OHCI) ... here (re)initialize the toggle in the QH,
* and set the pseudo-toggle in udev. Only usb_clear_halt() will
* ever clear it.
*/
if (!(qh->hw_info1 & cpu_to_hc32(ehci, 1 << 14))) {
unsigned is_out, epnum;
is_out = !(qtd->hw_token & cpu_to_hc32(ehci, 1 << 8));
epnum = (hc32_to_cpup(ehci, &qh->hw_info1) >> 8) & 0x0f;
if (unlikely (!usb_gettoggle (qh->dev, epnum, is_out))) {
qh->hw_token &= ~cpu_to_hc32(ehci, QTD_TOGGLE);
usb_settoggle (qh->dev, epnum, is_out, 1);
}
}
/* HC must see latest qtd and qh data before we clear ACTIVE+HALT */
wmb ();
qh->hw_token &= cpu_to_hc32(ehci, QTD_TOGGLE | QTD_STS_PING);
......@@ -123,6 +139,55 @@ qh_refresh (struct ehci_hcd *ehci, struct ehci_qh *qh)
/*-------------------------------------------------------------------------*/
static void qh_link_async(struct ehci_hcd *ehci, struct ehci_qh *qh);
static void ehci_clear_tt_buffer_complete(struct usb_hcd *hcd,
struct usb_host_endpoint *ep)
{
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
struct ehci_qh *qh = ep->hcpriv;
unsigned long flags;
spin_lock_irqsave(&ehci->lock, flags);
qh->clearing_tt = 0;
if (qh->qh_state == QH_STATE_IDLE && !list_empty(&qh->qtd_list)
&& HC_IS_RUNNING(hcd->state))
qh_link_async(ehci, qh);
spin_unlock_irqrestore(&ehci->lock, flags);
}
static void ehci_clear_tt_buffer(struct ehci_hcd *ehci, struct ehci_qh *qh,
struct urb *urb, u32 token)
{
/* If an async split transaction gets an error or is unlinked,
* the TT buffer may be left in an indeterminate state. We
* have to clear the TT buffer.
*
* Note: this routine is never called for Isochronous transfers.
*/
if (urb->dev->tt && !usb_pipeint(urb->pipe) && !qh->clearing_tt) {
#ifdef DEBUG
struct usb_device *tt = urb->dev->tt->hub;
dev_dbg(&tt->dev,
"clear tt buffer port %d, a%d ep%d t%08x\n",
urb->dev->ttport, urb->dev->devnum,
usb_pipeendpoint(urb->pipe), token);
#endif /* DEBUG */
if (!ehci_is_TDI(ehci)
|| urb->dev->tt->hub !=
ehci_to_hcd(ehci)->self.root_hub) {
if (usb_hub_clear_tt_buffer(urb) == 0)
qh->clearing_tt = 1;
} else {
/* REVISIT ARC-derived cores don't clear the root
* hub TT buffer in this way...
*/
}
}
}
static int qtd_copy_status (
struct ehci_hcd *ehci,
struct urb *urb,
......@@ -149,6 +214,14 @@ static int qtd_copy_status (
if (token & QTD_STS_BABBLE) {
/* FIXME "must" disable babbling device's port too */
status = -EOVERFLOW;
/* CERR nonzero + halt --> stall */
} else if (QTD_CERR(token)) {
status = -EPIPE;
/* In theory, more than one of the following bits can be set
* since they are sticky and the transaction is retried.
* Which to test first is rather arbitrary.
*/
} else if (token & QTD_STS_MMF) {
/* fs/ls interrupt xfer missed the complete-split */
status = -EPROTO;
......@@ -157,21 +230,15 @@ static int qtd_copy_status (
? -ENOSR /* hc couldn't read data */
: -ECOMM; /* hc couldn't write data */
} else if (token & QTD_STS_XACT) {
/* timeout, bad crc, wrong PID, etc; retried */
if (QTD_CERR (token))
status = -EPIPE;
else {
ehci_dbg (ehci, "devpath %s ep%d%s 3strikes\n",
urb->dev->devpath,
usb_pipeendpoint (urb->pipe),
usb_pipein (urb->pipe) ? "in" : "out");
status = -EPROTO;
}
/* CERR nonzero + no errors + halt --> stall */
} else if (QTD_CERR (token))
status = -EPIPE;
else /* unknown */
/* timeout, bad CRC, wrong PID, etc */
ehci_dbg(ehci, "devpath %s ep%d%s 3strikes\n",
urb->dev->devpath,
usb_pipeendpoint(urb->pipe),
usb_pipein(urb->pipe) ? "in" : "out");
status = -EPROTO;
} else { /* unknown */
status = -EPROTO;
}
ehci_vdbg (ehci,
"dev%d ep%d%s qtd token %08x --> status %d\n",
......@@ -179,28 +246,6 @@ static int qtd_copy_status (
usb_pipeendpoint (urb->pipe),
usb_pipein (urb->pipe) ? "in" : "out",
token, status);
/* if async CSPLIT failed, try cleaning out the TT buffer */
if (status != -EPIPE
&& urb->dev->tt
&& !usb_pipeint(urb->pipe)
&& ((token & QTD_STS_MMF) != 0
|| QTD_CERR(token) == 0)
&& (!ehci_is_TDI(ehci)
|| urb->dev->tt->hub !=
ehci_to_hcd(ehci)->self.root_hub)) {
#ifdef DEBUG
struct usb_device *tt = urb->dev->tt->hub;
dev_dbg (&tt->dev,
"clear tt buffer port %d, a%d ep%d t%08x\n",
urb->dev->ttport, urb->dev->devnum,
usb_pipeendpoint (urb->pipe), token);
#endif /* DEBUG */
/* REVISIT ARC-derived cores don't clear the root
* hub TT buffer in this way...
*/
usb_hub_tt_clear_buffer (urb->dev, urb->pipe);
}
}
return status;
......@@ -391,9 +436,16 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
/* qh unlinked; token in overlay may be most current */
if (state == QH_STATE_IDLE
&& cpu_to_hc32(ehci, qtd->qtd_dma)
== qh->hw_current)
== qh->hw_current) {
token = hc32_to_cpu(ehci, qh->hw_token);
/* An unlink may leave an incomplete
* async transaction in the TT buffer.
* We have to clear it.
*/
ehci_clear_tt_buffer(ehci, qh, urb, token);
}
/* force halt for unlinked or blocked qh, so we'll
* patch the qh later and so that completions can't
* activate it while we "know" it's stopped.
......@@ -419,6 +471,13 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
&& (qtd->hw_alt_next
& EHCI_LIST_END(ehci)))
last_status = -EINPROGRESS;
/* As part of low/full-speed endpoint-halt processing
* we must clear the TT buffer (11.17.5).
*/
if (unlikely(last_status != -EINPROGRESS &&
last_status != -EREMOTEIO))
ehci_clear_tt_buffer(ehci, qh, urb, token);
}
/* if we're removing something not at the queue head,
......@@ -834,6 +893,7 @@ qh_make (
qh->qh_state = QH_STATE_IDLE;
qh->hw_info1 = cpu_to_hc32(ehci, info1);
qh->hw_info2 = cpu_to_hc32(ehci, info2);
usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), !is_input, 1);
qh_refresh (ehci, qh);
return qh;
}
......@@ -847,6 +907,10 @@ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
__hc32 dma = QH_NEXT(ehci, qh->qh_dma);
struct ehci_qh *head;
/* Don't link a QH if there's a Clear-TT-Buffer pending */
if (unlikely(qh->clearing_tt))
return;
/* (re)start the async schedule? */
head = ehci->async;
timer_action_done (ehci, TIMER_ASYNC_OFF);
......@@ -864,7 +928,7 @@ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
}
}
/* clear halt and maybe recover from silicon quirk */
/* clear halt and/or toggle; and maybe recover from silicon quirk */
if (qh->qh_state == QH_STATE_IDLE)
qh_refresh (ehci, qh);
......
......@@ -1619,11 +1619,14 @@ itd_complete (
desc->status = -EPROTO;
/* HC need not update length with this error */
if (!(t & EHCI_ISOC_BABBLE))
desc->actual_length = EHCI_ITD_LENGTH (t);
if (!(t & EHCI_ISOC_BABBLE)) {
desc->actual_length = EHCI_ITD_LENGTH(t);
urb->actual_length += desc->actual_length;
}
} else if (likely ((t & EHCI_ISOC_ACTIVE) == 0)) {
desc->status = 0;
desc->actual_length = EHCI_ITD_LENGTH (t);
desc->actual_length = EHCI_ITD_LENGTH(t);
urb->actual_length += desc->actual_length;
} else {
/* URB was too late */
desc->status = -EXDEV;
......@@ -2014,7 +2017,8 @@ sitd_complete (
desc->status = -EPROTO;
} else {
desc->status = 0;
desc->actual_length = desc->length - SITD_LENGTH (t);
desc->actual_length = desc->length - SITD_LENGTH(t);
urb->actual_length += desc->actual_length;
}
stream->depth -= stream->interval << 3;
......
......@@ -354,7 +354,9 @@ struct ehci_qh {
unsigned short period; /* polling interval */
unsigned short start; /* where polling starts */
#define NO_FRAME ((unsigned short)~0) /* pick new start */
struct usb_device *dev; /* access to TT */
unsigned clearing_tt:1; /* Clear-TT-Buf in progress */
} __attribute__ ((aligned (32)));
/*-------------------------------------------------------------------------*/
......
......@@ -576,9 +576,7 @@ irqreturn_t fhci_irq(struct usb_hcd *hcd)
out_be16(&usb->fhci->regs->usb_event,
usb->saved_msk);
} else if (usb->port_status == FHCI_PORT_DISABLED) {
if (fhci_ioports_check_bus_state(fhci) == 1 &&
usb->port_status != FHCI_PORT_LOW &&
usb->port_status != FHCI_PORT_FULL)
if (fhci_ioports_check_bus_state(fhci) == 1)
fhci_device_connected_interrupt(fhci);
}
usb_er &= ~USB_E_RESET_MASK;
......@@ -605,9 +603,7 @@ irqreturn_t fhci_irq(struct usb_hcd *hcd)
}
if (usb_er & USB_E_IDLE_MASK) {
if (usb->port_status == FHCI_PORT_DISABLED &&
usb->port_status != FHCI_PORT_LOW &&
usb->port_status != FHCI_PORT_FULL) {
if (usb->port_status == FHCI_PORT_DISABLED) {
usb_er &= ~USB_E_RESET_MASK;
fhci_device_connected_interrupt(fhci);
} else if (usb->port_status ==
......
......@@ -361,7 +361,7 @@ static int __devexit isp1760_plat_remove(struct platform_device *pdev)
static struct platform_driver isp1760_plat_driver = {
.probe = isp1760_plat_probe,
.remove = isp1760_plat_remove,
.remove = __devexit_p(isp1760_plat_remove),
.driver = {
.name = "isp1760",
},
......
......@@ -35,13 +35,14 @@
#include <mach/hardware.h>
#include <mach/memory.h>
#include <mach/gpio.h>
#include <mach/cputype.h>
#include <asm/mach-types.h>
#include "musb_core.h"
#ifdef CONFIG_MACH_DAVINCI_EVM
#define GPIO_nVBUS_DRV 87
#define GPIO_nVBUS_DRV 144
#endif
#include "davinci.h"
......@@ -329,7 +330,6 @@ static irqreturn_t davinci_interrupt(int irq, void *__hci)
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;
......@@ -343,7 +343,9 @@ static irqreturn_t davinci_interrupt(int irq, void *__hci)
portstate(musb->port1_status &= ~USB_PORT_STAT_POWER);
}
/* NOTE: this must complete poweron within 100 msec */
/* NOTE: this must complete poweron within 100 msec
* (OTG_TIME_A_WAIT_VRISE) but we don't check for that.
*/
davinci_source_power(musb, drvvbus, 0);
DBG(2, "VBUS %s (%s)%s, devctl %02x\n",
drvvbus ? "on" : "off",
......@@ -411,6 +413,21 @@ int __init musb_platform_init(struct musb *musb)
__raw_writel(phy_ctrl, USB_PHY_CTRL);
}
/* On dm355, the default-A state machine needs DRVVBUS control.
* If we won't be a host, there's no need to turn it on.
*/
if (cpu_is_davinci_dm355()) {
u32 deepsleep = __raw_readl(DM355_DEEPSLEEP);
if (is_host_enabled(musb)) {
deepsleep &= ~DRVVBUS_OVERRIDE;
} else {
deepsleep &= ~DRVVBUS_FORCE;
deepsleep |= DRVVBUS_OVERRIDE;
}
__raw_writel(deepsleep, DM355_DEEPSLEEP);
}
/* reset the controller */
musb_writel(tibase, DAVINCI_USB_CTRL_REG, 0x1);
......@@ -437,6 +454,15 @@ int musb_platform_exit(struct musb *musb)
if (is_host_enabled(musb))
del_timer_sync(&otg_workaround);
/* force VBUS off */
if (cpu_is_davinci_dm355()) {
u32 deepsleep = __raw_readl(DM355_DEEPSLEEP);
deepsleep &= ~DRVVBUS_FORCE;
deepsleep |= DRVVBUS_OVERRIDE;
__raw_writel(deepsleep, DM355_DEEPSLEEP);
}
davinci_source_power(musb, 0 /*off*/, 1);
/* delay, to avoid problems with module reload */
......
......@@ -373,7 +373,7 @@ static void musb_advance_schedule(struct musb *musb, struct urb *urb,
musb_save_toggle(qh, is_in, urb);
break;
case USB_ENDPOINT_XFER_ISOC:
if (urb->error_count)
if (status == 0 && urb->error_count)
status = -EXDEV;
break;
}
......@@ -2235,13 +2235,30 @@ static void musb_h_stop(struct usb_hcd *hcd)
static int musb_bus_suspend(struct usb_hcd *hcd)
{
struct musb *musb = hcd_to_musb(hcd);
u8 devctl;
if (musb->xceiv->state == OTG_STATE_A_SUSPEND)
if (!is_host_active(musb))
return 0;
if (is_host_active(musb) && musb->is_active) {
WARNING("trying to suspend as %s is_active=%i\n",
otg_state_string(musb), musb->is_active);
switch (musb->xceiv->state) {
case OTG_STATE_A_SUSPEND:
return 0;
case OTG_STATE_A_WAIT_VRISE:
/* ID could be grounded even if there's no device
* on the other end of the cable. NOTE that the
* A_WAIT_VRISE timers are messy with MUSB...
*/
devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
if ((devctl & MUSB_DEVCTL_VBUS) == MUSB_DEVCTL_VBUS)
musb->xceiv->state = OTG_STATE_A_WAIT_BCON;
break;
default:
break;
}
if (musb->is_active) {
WARNING("trying to suspend as %s while active\n",
otg_state_string(musb));
return -EBUSY;
} else
return 0;
......
......@@ -59,18 +59,4 @@ config NOP_USB_XCEIV
built-in with usb ip or which are autonomous and doesn't require any
phy programming such as ISP1x04 etc.
config USB_LANGWELL_OTG
tristate "Intel Langwell USB OTG dual-role support"
depends on USB && MRST
select USB_OTG
select USB_OTG_UTILS
help
Say Y here if you want to build Intel Langwell USB OTG
transciever driver in kernel. This driver implements role
switch between EHCI host driver and Langwell USB OTG
client driver.
To compile this driver as a module, choose M here: the
module will be called langwell_otg.
endif # USB || OTG
......@@ -9,7 +9,6 @@ obj-$(CONFIG_USB_OTG_UTILS) += otg.o
obj-$(CONFIG_USB_GPIO_VBUS) += gpio_vbus.o
obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o
obj-$(CONFIG_TWL4030_USB) += twl4030-usb.o
obj-$(CONFIG_USB_LANGWELL_OTG) += langwell_otg.o
obj-$(CONFIG_NOP_USB_XCEIV) += nop-usb-xceiv.o
ccflags-$(CONFIG_USB_DEBUG) += -DDEBUG
......
/*
* Intel Langwell USB OTG transceiver driver
* Copyright (C) 2008 - 2009, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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 driver helps to switch Langwell OTG controller function between host
* and peripheral. It works with EHCI driver and Langwell client controller
* driver together.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/moduleparam.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <linux/usb.h>
#include <linux/usb/otg.h>
#include <linux/notifier.h>
#include <asm/ipc_defs.h>
#include <linux/delay.h>
#include "../core/hcd.h"
#include <linux/usb/langwell_otg.h>
#define DRIVER_DESC "Intel Langwell USB OTG transceiver driver"
#define DRIVER_VERSION "3.0.0.32L.0002"
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_AUTHOR("Henry Yuan <hang.yuan@intel.com>, Hao Wu <hao.wu@intel.com>");
MODULE_VERSION(DRIVER_VERSION);
MODULE_LICENSE("GPL");
static const char driver_name[] = "langwell_otg";
static int langwell_otg_probe(struct pci_dev *pdev,
const struct pci_device_id *id);
static void langwell_otg_remove(struct pci_dev *pdev);
static int langwell_otg_suspend(struct pci_dev *pdev, pm_message_t message);
static int langwell_otg_resume(struct pci_dev *pdev);
static int langwell_otg_set_host(struct otg_transceiver *otg,
struct usb_bus *host);
static int langwell_otg_set_peripheral(struct otg_transceiver *otg,
struct usb_gadget *gadget);
static int langwell_otg_start_srp(struct otg_transceiver *otg);
static const struct pci_device_id pci_ids[] = {{
.class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe),
.class_mask = ~0,
.vendor = 0x8086,
.device = 0x0811,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
}, { /* end: all zeroes */ }
};
static struct pci_driver otg_pci_driver = {
.name = (char *) driver_name,
.id_table = pci_ids,
.probe = langwell_otg_probe,
.remove = langwell_otg_remove,
.suspend = langwell_otg_suspend,
.resume = langwell_otg_resume,
};
static const char *state_string(enum usb_otg_state state)
{
switch (state) {
case OTG_STATE_A_IDLE:
return "a_idle";
case OTG_STATE_A_WAIT_VRISE:
return "a_wait_vrise";
case OTG_STATE_A_WAIT_BCON:
return "a_wait_bcon";
case OTG_STATE_A_HOST:
return "a_host";
case OTG_STATE_A_SUSPEND:
return "a_suspend";
case OTG_STATE_A_PERIPHERAL:
return "a_peripheral";
case OTG_STATE_A_WAIT_VFALL:
return "a_wait_vfall";
case OTG_STATE_A_VBUS_ERR:
return "a_vbus_err";
case OTG_STATE_B_IDLE:
return "b_idle";
case OTG_STATE_B_SRP_INIT:
return "b_srp_init";
case OTG_STATE_B_PERIPHERAL:
return "b_peripheral";
case OTG_STATE_B_WAIT_ACON:
return "b_wait_acon";
case OTG_STATE_B_HOST:
return "b_host";
default:
return "UNDEFINED";
}
}
/* HSM timers */
static inline struct langwell_otg_timer *otg_timer_initializer
(void (*function)(unsigned long), unsigned long expires, unsigned long data)
{
struct langwell_otg_timer *timer;
timer = kmalloc(sizeof(struct langwell_otg_timer), GFP_KERNEL);
timer->function = function;
timer->expires = expires;
timer->data = data;
return timer;
}
static struct langwell_otg_timer *a_wait_vrise_tmr, *a_wait_bcon_tmr,
*a_aidl_bdis_tmr, *b_ase0_brst_tmr, *b_se0_srp_tmr, *b_srp_res_tmr,
*b_bus_suspend_tmr;
static struct list_head active_timers;
static struct langwell_otg *the_transceiver;
/* host/client notify transceiver when event affects HNP state */
void langwell_update_transceiver()
{
otg_dbg("transceiver driver is notified\n");
queue_work(the_transceiver->qwork, &the_transceiver->work);
}
EXPORT_SYMBOL(langwell_update_transceiver);
static int langwell_otg_set_host(struct otg_transceiver *otg,
struct usb_bus *host)
{
otg->host = host;
return 0;
}
static int langwell_otg_set_peripheral(struct otg_transceiver *otg,
struct usb_gadget *gadget)
{
otg->gadget = gadget;
return 0;
}
static int langwell_otg_set_power(struct otg_transceiver *otg,
unsigned mA)
{
return 0;
}
/* A-device drives vbus, controlled through PMIC CHRGCNTL register*/
static void langwell_otg_drv_vbus(int on)
{
struct ipc_pmic_reg_data pmic_data = {0};
struct ipc_pmic_reg_data battery_data;
/* Check if battery is attached or not */
battery_data.pmic_reg_data[0].register_address = 0xd2;
battery_data.ioc = 0;
battery_data.num_entries = 1;
if (ipc_pmic_register_read(&battery_data)) {
otg_dbg("Failed to read PMIC register 0xd2.\n");
return;
}
if ((battery_data.pmic_reg_data[0].value & 0x20) == 0) {
otg_dbg("no battery attached\n");
return;
}
/* Workaround for battery attachment issue */
if (battery_data.pmic_reg_data[0].value == 0x34) {
otg_dbg("battery \n");
return;
}
otg_dbg("battery attached\n");
pmic_data.ioc = 0;
pmic_data.pmic_reg_data[0].register_address = 0xD4;
pmic_data.num_entries = 1;
if (on)
pmic_data.pmic_reg_data[0].value = 0x20;
else
pmic_data.pmic_reg_data[0].value = 0xc0;
if (ipc_pmic_register_write(&pmic_data, TRUE))
otg_dbg("Failed to write PMIC.\n");
}
/* charge vbus or discharge vbus through a resistor to ground */
static void langwell_otg_chrg_vbus(int on)
{
u32 val;
val = readl(the_transceiver->regs + CI_OTGSC);
if (on)
writel((val & ~OTGSC_INTSTS_MASK) | OTGSC_VC,
the_transceiver->regs + CI_OTGSC);
else
writel((val & ~OTGSC_INTSTS_MASK) | OTGSC_VD,
the_transceiver->regs + CI_OTGSC);
}
/* Start SRP */
static int langwell_otg_start_srp(struct otg_transceiver *otg)
{
u32 val;
otg_dbg("Start SRP ->\n");
val = readl(the_transceiver->regs + CI_OTGSC);
writel((val & ~OTGSC_INTSTS_MASK) | OTGSC_HADP,
the_transceiver->regs + CI_OTGSC);
/* Check if the data plus is finished or not */
msleep(8);
val = readl(the_transceiver->regs + CI_OTGSC);
if (val & (OTGSC_HADP | OTGSC_DP))
otg_dbg("DataLine SRP Error\n");
/* FIXME: VBus SRP */
return 0;
}
/* stop SOF via bus_suspend */
static void langwell_otg_loc_sof(int on)
{
struct usb_hcd *hcd;
int err;
otg_dbg("loc_sof -> %d\n", on);
hcd = bus_to_hcd(the_transceiver->otg.host);
if (on)
err = hcd->driver->bus_resume(hcd);
else
err = hcd->driver->bus_suspend(hcd);
if (err)
otg_dbg("Failed to resume/suspend bus - %d\n", err);
}
static void langwell_otg_phy_low_power(int on)
{
u32 val;
otg_dbg("phy low power mode-> %d\n", on);
val = readl(the_transceiver->regs + CI_HOSTPC1);
if (on)
writel(val | HOSTPC1_PHCD, the_transceiver->regs + CI_HOSTPC1);
else
writel(val & ~HOSTPC1_PHCD, the_transceiver->regs + CI_HOSTPC1);
}
/* Enable/Disable OTG interrupt */
static void langwell_otg_intr(int on)
{
u32 val;
otg_dbg("interrupt -> %d\n", on);
val = readl(the_transceiver->regs + CI_OTGSC);
if (on) {
val = val | (OTGSC_INTEN_MASK | OTGSC_IDPU);
writel(val, the_transceiver->regs + CI_OTGSC);
} else {
val = val & ~(OTGSC_INTEN_MASK | OTGSC_IDPU);
writel(val, the_transceiver->regs + CI_OTGSC);
}
}
/* set HAAR: Hardware Assist Auto-Reset */
static void langwell_otg_HAAR(int on)
{
u32 val;
otg_dbg("HAAR -> %d\n", on);
val = readl(the_transceiver->regs + CI_OTGSC);
if (on)
writel((val & ~OTGSC_INTSTS_MASK) | OTGSC_HAAR,
the_transceiver->regs + CI_OTGSC);
else
writel((val & ~OTGSC_INTSTS_MASK) & ~OTGSC_HAAR,
the_transceiver->regs + CI_OTGSC);
}
/* set HABA: Hardware Assist B-Disconnect to A-Connect */
static void langwell_otg_HABA(int on)
{
u32 val;
otg_dbg("HABA -> %d\n", on);
val = readl(the_transceiver->regs + CI_OTGSC);
if (on)
writel((val & ~OTGSC_INTSTS_MASK) | OTGSC_HABA,
the_transceiver->regs + CI_OTGSC);
else
writel((val & ~OTGSC_INTSTS_MASK) & ~OTGSC_HABA,
the_transceiver->regs + CI_OTGSC);
}
static int langwell_otg_check_se0_srp(int on)
{
u32 val;
int delay_time = TB_SE0_SRP * 10; /* step is 100us */
otg_dbg("check_se0_srp -> \n");
do {
udelay(100);
if (!delay_time--)
break;
val = readl(the_transceiver->regs + CI_PORTSC1);
val &= PORTSC_LS;
} while (!val);
otg_dbg("check_se0_srp <- \n");
return val;
}
/* The timeout callback function to set time out bit */
static void set_tmout(unsigned long indicator)
{
*(int *)indicator = 1;
}
void langwell_otg_nsf_msg(unsigned long indicator)
{
switch (indicator) {
case 2:
case 4:
case 6:
case 7:
printk(KERN_ERR "OTG:NSF-%lu - deivce not responding\n",
indicator);
break;
case 3:
printk(KERN_ERR "OTG:NSF-%lu - deivce not supported\n",
indicator);
break;
default:
printk(KERN_ERR "Do not have this kind of NSF\n");
break;
}
}
/* Initialize timers */
static void langwell_otg_init_timers(struct otg_hsm *hsm)
{
/* HSM used timers */
a_wait_vrise_tmr = otg_timer_initializer(&set_tmout, TA_WAIT_VRISE,
(unsigned long)&hsm->a_wait_vrise_tmout);
a_wait_bcon_tmr = otg_timer_initializer(&set_tmout, TA_WAIT_BCON,
(unsigned long)&hsm->a_wait_bcon_tmout);
a_aidl_bdis_tmr = otg_timer_initializer(&set_tmout, TA_AIDL_BDIS,
(unsigned long)&hsm->a_aidl_bdis_tmout);
b_ase0_brst_tmr = otg_timer_initializer(&set_tmout, TB_ASE0_BRST,
(unsigned long)&hsm->b_ase0_brst_tmout);
b_se0_srp_tmr = otg_timer_initializer(&set_tmout, TB_SE0_SRP,
(unsigned long)&hsm->b_se0_srp);
b_srp_res_tmr = otg_timer_initializer(&set_tmout, TB_SRP_RES,
(unsigned long)&hsm->b_srp_res_tmout);
b_bus_suspend_tmr = otg_timer_initializer(&set_tmout, TB_BUS_SUSPEND,
(unsigned long)&hsm->b_bus_suspend_tmout);
}
/* Free timers */
static void langwell_otg_free_timers(void)
{
kfree(a_wait_vrise_tmr);
kfree(a_wait_bcon_tmr);
kfree(a_aidl_bdis_tmr);
kfree(b_ase0_brst_tmr);
kfree(b_se0_srp_tmr);
kfree(b_srp_res_tmr);
kfree(b_bus_suspend_tmr);
}
/* Add timer to timer list */
static void langwell_otg_add_timer(void *gtimer)
{
struct langwell_otg_timer *timer = (struct langwell_otg_timer *)gtimer;
struct langwell_otg_timer *tmp_timer;
u32 val32;
/* Check if the timer is already in the active list,
* if so update timer count
*/
list_for_each_entry(tmp_timer, &active_timers, list)
if (tmp_timer == timer) {
timer->count = timer->expires;
return;
}
timer->count = timer->expires;
if (list_empty(&active_timers)) {
val32 = readl(the_transceiver->regs + CI_OTGSC);
writel(val32 | OTGSC_1MSE, the_transceiver->regs + CI_OTGSC);
}
list_add_tail(&timer->list, &active_timers);
}
/* Remove timer from the timer list; clear timeout status */
static void langwell_otg_del_timer(void *gtimer)
{
struct langwell_otg_timer *timer = (struct langwell_otg_timer *)gtimer;
struct langwell_otg_timer *tmp_timer, *del_tmp;
u32 val32;
list_for_each_entry_safe(tmp_timer, del_tmp, &active_timers, list)
if (tmp_timer == timer)
list_del(&timer->list);
if (list_empty(&active_timers)) {
val32 = readl(the_transceiver->regs + CI_OTGSC);
writel(val32 & ~OTGSC_1MSE, the_transceiver->regs + CI_OTGSC);
}
}
/* Reduce timer count by 1, and find timeout conditions.*/
static int langwell_otg_tick_timer(u32 *int_sts)
{
struct langwell_otg_timer *tmp_timer, *del_tmp;
int expired = 0;
list_for_each_entry_safe(tmp_timer, del_tmp, &active_timers, list) {
tmp_timer->count--;
/* check if timer expires */
if (!tmp_timer->count) {
list_del(&tmp_timer->list);
tmp_timer->function(tmp_timer->data);
expired = 1;
}
}
if (list_empty(&active_timers)) {
otg_dbg("tick timer: disable 1ms int\n");
*int_sts = *int_sts & ~OTGSC_1MSE;
}
return expired;
}
static void reset_otg(void)
{
u32 val;
int delay_time = 1000;
otg_dbg("reseting OTG controller ...\n");
val = readl(the_transceiver->regs + CI_USBCMD);
writel(val | USBCMD_RST, the_transceiver->regs + CI_USBCMD);
do {
udelay(100);
if (!delay_time--)
otg_dbg("reset timeout\n");
val = readl(the_transceiver->regs + CI_USBCMD);
val &= USBCMD_RST;
} while (val != 0);
otg_dbg("reset done.\n");
}
static void set_host_mode(void)
{
u32 val;
reset_otg();
val = readl(the_transceiver->regs + CI_USBMODE);
val = (val & (~USBMODE_CM)) | USBMODE_HOST;
writel(val, the_transceiver->regs + CI_USBMODE);
}
static void set_client_mode(void)
{
u32 val;
reset_otg();
val = readl(the_transceiver->regs + CI_USBMODE);
val = (val & (~USBMODE_CM)) | USBMODE_DEVICE;
writel(val, the_transceiver->regs + CI_USBMODE);
}
static void init_hsm(void)
{
struct langwell_otg *langwell = the_transceiver;
u32 val32;
/* read OTGSC after reset */
val32 = readl(langwell->regs + CI_OTGSC);
otg_dbg("%s: OTGSC init value = 0x%x\n", __func__, val32);
/* set init state */
if (val32 & OTGSC_ID) {
langwell->hsm.id = 1;
langwell->otg.default_a = 0;
set_client_mode();
langwell->otg.state = OTG_STATE_B_IDLE;
langwell_otg_drv_vbus(0);
} else {
langwell->hsm.id = 0;
langwell->otg.default_a = 1;
set_host_mode();
langwell->otg.state = OTG_STATE_A_IDLE;
}
/* set session indicator */
if (val32 & OTGSC_BSE)
langwell->hsm.b_sess_end = 1;
if (val32 & OTGSC_BSV)
langwell->hsm.b_sess_vld = 1;
if (val32 & OTGSC_ASV)
langwell->hsm.a_sess_vld = 1;
if (val32 & OTGSC_AVV)
langwell->hsm.a_vbus_vld = 1;
/* defautly power the bus */
langwell->hsm.a_bus_req = 1;
langwell->hsm.a_bus_drop = 0;
/* defautly don't request bus as B device */
langwell->hsm.b_bus_req = 0;
/* no system error */
langwell->hsm.a_clr_err = 0;
}
static irqreturn_t otg_dummy_irq(int irq, void *_dev)
{
void __iomem *reg_base = _dev;
u32 val;
u32 int_mask = 0;
val = readl(reg_base + CI_USBMODE);
if ((val & USBMODE_CM) != USBMODE_DEVICE)
return IRQ_NONE;
val = readl(reg_base + CI_USBSTS);
int_mask = val & INTR_DUMMY_MASK;
if (int_mask == 0)
return IRQ_NONE;
/* clear hsm.b_conn here since host driver can't detect it
* otg_dummy_irq called means B-disconnect happened.
*/
if (the_transceiver->hsm.b_conn) {
the_transceiver->hsm.b_conn = 0;
if (spin_trylock(&the_transceiver->wq_lock)) {
queue_work(the_transceiver->qwork,
&the_transceiver->work);
spin_unlock(&the_transceiver->wq_lock);
}
}
/* Clear interrupts */
writel(int_mask, reg_base + CI_USBSTS);
return IRQ_HANDLED;
}
static irqreturn_t otg_irq(int irq, void *_dev)
{
struct langwell_otg *langwell = _dev;
u32 int_sts, int_en;
u32 int_mask = 0;
int flag = 0;
int_sts = readl(langwell->regs + CI_OTGSC);
int_en = (int_sts & OTGSC_INTEN_MASK) >> 8;
int_mask = int_sts & int_en;
if (int_mask == 0)
return IRQ_NONE;
if (int_mask & OTGSC_IDIS) {
otg_dbg("%s: id change int\n", __func__);
langwell->hsm.id = (int_sts & OTGSC_ID) ? 1 : 0;
flag = 1;
}
if (int_mask & OTGSC_DPIS) {
otg_dbg("%s: data pulse int\n", __func__);
langwell->hsm.a_srp_det = (int_sts & OTGSC_DPS) ? 1 : 0;
flag = 1;
}
if (int_mask & OTGSC_BSEIS) {
otg_dbg("%s: b session end int\n", __func__);
langwell->hsm.b_sess_end = (int_sts & OTGSC_BSE) ? 1 : 0;
flag = 1;
}
if (int_mask & OTGSC_BSVIS) {
otg_dbg("%s: b session valid int\n", __func__);
langwell->hsm.b_sess_vld = (int_sts & OTGSC_BSV) ? 1 : 0;
flag = 1;
}
if (int_mask & OTGSC_ASVIS) {
otg_dbg("%s: a session valid int\n", __func__);
langwell->hsm.a_sess_vld = (int_sts & OTGSC_ASV) ? 1 : 0;
flag = 1;
}
if (int_mask & OTGSC_AVVIS) {
otg_dbg("%s: a vbus valid int\n", __func__);
langwell->hsm.a_vbus_vld = (int_sts & OTGSC_AVV) ? 1 : 0;
flag = 1;
}
if (int_mask & OTGSC_1MSS) {
/* need to schedule otg_work if any timer is expired */
if (langwell_otg_tick_timer(&int_sts))
flag = 1;
}
writel((int_sts & ~OTGSC_INTSTS_MASK) | int_mask,
langwell->regs + CI_OTGSC);
if (flag)
queue_work(langwell->qwork, &langwell->work);
return IRQ_HANDLED;
}
static void langwell_otg_work(struct work_struct *work)
{
struct langwell_otg *langwell = container_of(work,
struct langwell_otg, work);
int retval;
otg_dbg("%s: old state = %s\n", __func__,
state_string(langwell->otg.state));
switch (langwell->otg.state) {
case OTG_STATE_UNDEFINED:
case OTG_STATE_B_IDLE:
if (!langwell->hsm.id) {
langwell_otg_del_timer(b_srp_res_tmr);
langwell->otg.default_a = 1;
langwell->hsm.a_srp_det = 0;
langwell_otg_chrg_vbus(0);
langwell_otg_drv_vbus(0);
set_host_mode();
langwell->otg.state = OTG_STATE_A_IDLE;
queue_work(langwell->qwork, &langwell->work);
} else if (langwell->hsm.b_srp_res_tmout) {
langwell->hsm.b_srp_res_tmout = 0;
langwell->hsm.b_bus_req = 0;
langwell_otg_nsf_msg(6);
} else if (langwell->hsm.b_sess_vld) {
langwell_otg_del_timer(b_srp_res_tmr);
langwell->hsm.b_sess_end = 0;
langwell->hsm.a_bus_suspend = 0;
langwell_otg_chrg_vbus(0);
if (langwell->client_ops) {
langwell->client_ops->resume(langwell->pdev);
langwell->otg.state = OTG_STATE_B_PERIPHERAL;
} else
otg_dbg("client driver not loaded.\n");
} else if (langwell->hsm.b_bus_req &&
(langwell->hsm.b_sess_end)) {
/* workaround for b_se0_srp detection */
retval = langwell_otg_check_se0_srp(0);
if (retval) {
langwell->hsm.b_bus_req = 0;
otg_dbg("LS is not SE0, try again later\n");
} else {
/* Start SRP */
langwell_otg_start_srp(&langwell->otg);
langwell_otg_add_timer(b_srp_res_tmr);
}
}
break;
case OTG_STATE_B_SRP_INIT:
if (!langwell->hsm.id) {
langwell->otg.default_a = 1;
langwell->hsm.a_srp_det = 0;
langwell_otg_drv_vbus(0);
langwell_otg_chrg_vbus(0);
langwell->otg.state = OTG_STATE_A_IDLE;
queue_work(langwell->qwork, &langwell->work);
} else if (langwell->hsm.b_sess_vld) {
langwell_otg_chrg_vbus(0);
if (langwell->client_ops) {
langwell->client_ops->resume(langwell->pdev);
langwell->otg.state = OTG_STATE_B_PERIPHERAL;
} else
otg_dbg("client driver not loaded.\n");
}
break;
case OTG_STATE_B_PERIPHERAL:
if (!langwell->hsm.id) {
langwell->otg.default_a = 1;
langwell->hsm.a_srp_det = 0;
langwell_otg_drv_vbus(0);
langwell_otg_chrg_vbus(0);
set_host_mode();
if (langwell->client_ops) {
langwell->client_ops->suspend(langwell->pdev,
PMSG_FREEZE);
} else
otg_dbg("client driver has been removed.\n");
langwell->otg.state = OTG_STATE_A_IDLE;
queue_work(langwell->qwork, &langwell->work);
} else if (!langwell->hsm.b_sess_vld) {
langwell->hsm.b_hnp_enable = 0;
if (langwell->client_ops) {
langwell->client_ops->suspend(langwell->pdev,
PMSG_FREEZE);
} else
otg_dbg("client driver has been removed.\n");
langwell->otg.state = OTG_STATE_B_IDLE;
} else if (langwell->hsm.b_bus_req && langwell->hsm.b_hnp_enable
&& langwell->hsm.a_bus_suspend) {
if (langwell->client_ops) {
langwell->client_ops->suspend(langwell->pdev,
PMSG_FREEZE);
} else
otg_dbg("client driver has been removed.\n");
langwell_otg_HAAR(1);
langwell->hsm.a_conn = 0;
if (langwell->host_ops) {
langwell->host_ops->probe(langwell->pdev,
langwell->host_ops->id_table);
langwell->otg.state = OTG_STATE_B_WAIT_ACON;
} else
otg_dbg("host driver not loaded.\n");
langwell->hsm.a_bus_resume = 0;
langwell->hsm.b_ase0_brst_tmout = 0;
langwell_otg_add_timer(b_ase0_brst_tmr);
}
break;
case OTG_STATE_B_WAIT_ACON:
if (!langwell->hsm.id) {
langwell_otg_del_timer(b_ase0_brst_tmr);
langwell->otg.default_a = 1;
langwell->hsm.a_srp_det = 0;
langwell_otg_drv_vbus(0);
langwell_otg_chrg_vbus(0);
set_host_mode();
langwell_otg_HAAR(0);
if (langwell->host_ops)
langwell->host_ops->remove(langwell->pdev);
else
otg_dbg("host driver has been removed.\n");
langwell->otg.state = OTG_STATE_A_IDLE;
queue_work(langwell->qwork, &langwell->work);
} else if (!langwell->hsm.b_sess_vld) {
langwell_otg_del_timer(b_ase0_brst_tmr);
langwell->hsm.b_hnp_enable = 0;
langwell->hsm.b_bus_req = 0;
langwell_otg_chrg_vbus(0);
langwell_otg_HAAR(0);
if (langwell->host_ops)
langwell->host_ops->remove(langwell->pdev);
else
otg_dbg("host driver has been removed.\n");
langwell->otg.state = OTG_STATE_B_IDLE;
} else if (langwell->hsm.a_conn) {
langwell_otg_del_timer(b_ase0_brst_tmr);
langwell_otg_HAAR(0);
langwell->otg.state = OTG_STATE_B_HOST;
queue_work(langwell->qwork, &langwell->work);
} else if (langwell->hsm.a_bus_resume ||
langwell->hsm.b_ase0_brst_tmout) {
langwell_otg_del_timer(b_ase0_brst_tmr);
langwell_otg_HAAR(0);
langwell_otg_nsf_msg(7);
if (langwell->host_ops)
langwell->host_ops->remove(langwell->pdev);
else
otg_dbg("host driver has been removed.\n");
langwell->hsm.a_bus_suspend = 0;
langwell->hsm.b_bus_req = 0;
if (langwell->client_ops)
langwell->client_ops->resume(langwell->pdev);
else
otg_dbg("client driver not loaded.\n");
langwell->otg.state = OTG_STATE_B_PERIPHERAL;
}
break;
case OTG_STATE_B_HOST:
if (!langwell->hsm.id) {
langwell->otg.default_a = 1;
langwell->hsm.a_srp_det = 0;
langwell_otg_drv_vbus(0);
langwell_otg_chrg_vbus(0);
set_host_mode();
if (langwell->host_ops)
langwell->host_ops->remove(langwell->pdev);
else
otg_dbg("host driver has been removed.\n");
langwell->otg.state = OTG_STATE_A_IDLE;
queue_work(langwell->qwork, &langwell->work);
} else if (!langwell->hsm.b_sess_vld) {
langwell->hsm.b_hnp_enable = 0;
langwell->hsm.b_bus_req = 0;
langwell_otg_chrg_vbus(0);
if (langwell->host_ops)
langwell->host_ops->remove(langwell->pdev);
else
otg_dbg("host driver has been removed.\n");
langwell->otg.state = OTG_STATE_B_IDLE;
} else if ((!langwell->hsm.b_bus_req) ||
(!langwell->hsm.a_conn)) {
langwell->hsm.b_bus_req = 0;
langwell_otg_loc_sof(0);
if (langwell->host_ops)
langwell->host_ops->remove(langwell->pdev);
else
otg_dbg("host driver has been removed.\n");
langwell->hsm.a_bus_suspend = 0;
if (langwell->client_ops)
langwell->client_ops->resume(langwell->pdev);
else
otg_dbg("client driver not loaded.\n");
langwell->otg.state = OTG_STATE_B_PERIPHERAL;
}
break;
case OTG_STATE_A_IDLE:
langwell->otg.default_a = 1;
if (langwell->hsm.id) {
langwell->otg.default_a = 0;
langwell->hsm.b_bus_req = 0;
langwell_otg_drv_vbus(0);
langwell_otg_chrg_vbus(0);
langwell->otg.state = OTG_STATE_B_IDLE;
queue_work(langwell->qwork, &langwell->work);
} else if (langwell->hsm.a_sess_vld) {
langwell_otg_drv_vbus(1);
langwell->hsm.a_srp_det = 1;
langwell->hsm.a_wait_vrise_tmout = 0;
langwell_otg_add_timer(a_wait_vrise_tmr);
langwell->otg.state = OTG_STATE_A_WAIT_VRISE;
queue_work(langwell->qwork, &langwell->work);
} else if (!langwell->hsm.a_bus_drop &&
(langwell->hsm.a_srp_det || langwell->hsm.a_bus_req)) {
langwell_otg_drv_vbus(1);
langwell->hsm.a_wait_vrise_tmout = 0;
langwell_otg_add_timer(a_wait_vrise_tmr);
langwell->otg.state = OTG_STATE_A_WAIT_VRISE;
queue_work(langwell->qwork, &langwell->work);
}
break;
case OTG_STATE_A_WAIT_VRISE:
if (langwell->hsm.id) {
langwell_otg_del_timer(a_wait_vrise_tmr);
langwell->hsm.b_bus_req = 0;
langwell->otg.default_a = 0;
langwell_otg_drv_vbus(0);
langwell->otg.state = OTG_STATE_B_IDLE;
} else if (langwell->hsm.a_vbus_vld) {
langwell_otg_del_timer(a_wait_vrise_tmr);
if (langwell->host_ops)
langwell->host_ops->probe(langwell->pdev,
langwell->host_ops->id_table);
else
otg_dbg("host driver not loaded.\n");
langwell->hsm.b_conn = 0;
langwell->hsm.a_set_b_hnp_en = 0;
langwell->hsm.a_wait_bcon_tmout = 0;
langwell_otg_add_timer(a_wait_bcon_tmr);
langwell->otg.state = OTG_STATE_A_WAIT_BCON;
} else if (langwell->hsm.a_wait_vrise_tmout) {
if (langwell->hsm.a_vbus_vld) {
if (langwell->host_ops)
langwell->host_ops->probe(
langwell->pdev,
langwell->host_ops->id_table);
else
otg_dbg("host driver not loaded.\n");
langwell->hsm.b_conn = 0;
langwell->hsm.a_set_b_hnp_en = 0;
langwell->hsm.a_wait_bcon_tmout = 0;
langwell_otg_add_timer(a_wait_bcon_tmr);
langwell->otg.state = OTG_STATE_A_WAIT_BCON;
} else {
langwell_otg_drv_vbus(0);
langwell->otg.state = OTG_STATE_A_VBUS_ERR;
}
}
break;
case OTG_STATE_A_WAIT_BCON:
if (langwell->hsm.id) {
langwell_otg_del_timer(a_wait_bcon_tmr);
langwell->otg.default_a = 0;
langwell->hsm.b_bus_req = 0;
if (langwell->host_ops)
langwell->host_ops->remove(langwell->pdev);
else
otg_dbg("host driver has been removed.\n");
langwell_otg_drv_vbus(0);
langwell->otg.state = OTG_STATE_B_IDLE;
queue_work(langwell->qwork, &langwell->work);
} else if (!langwell->hsm.a_vbus_vld) {
langwell_otg_del_timer(a_wait_bcon_tmr);
if (langwell->host_ops)
langwell->host_ops->remove(langwell->pdev);
else
otg_dbg("host driver has been removed.\n");
langwell_otg_drv_vbus(0);
langwell->otg.state = OTG_STATE_A_VBUS_ERR;
} else if (langwell->hsm.a_bus_drop ||
(langwell->hsm.a_wait_bcon_tmout &&
!langwell->hsm.a_bus_req)) {
langwell_otg_del_timer(a_wait_bcon_tmr);
if (langwell->host_ops)
langwell->host_ops->remove(langwell->pdev);
else
otg_dbg("host driver has been removed.\n");
langwell_otg_drv_vbus(0);
langwell->otg.state = OTG_STATE_A_WAIT_VFALL;
} else if (langwell->hsm.b_conn) {
langwell_otg_del_timer(a_wait_bcon_tmr);
langwell->hsm.a_suspend_req = 0;
langwell->otg.state = OTG_STATE_A_HOST;
if (!langwell->hsm.a_bus_req &&
langwell->hsm.a_set_b_hnp_en) {
/* It is not safe enough to do a fast
* transistion from A_WAIT_BCON to
* A_SUSPEND */
msleep(10000);
if (langwell->hsm.a_bus_req)
break;
if (request_irq(langwell->pdev->irq,
otg_dummy_irq, IRQF_SHARED,
driver_name, langwell->regs) != 0) {
otg_dbg("request interrupt %d fail\n",
langwell->pdev->irq);
}
langwell_otg_HABA(1);
langwell->hsm.b_bus_resume = 0;
langwell->hsm.a_aidl_bdis_tmout = 0;
langwell_otg_add_timer(a_aidl_bdis_tmr);
langwell_otg_loc_sof(0);
langwell->otg.state = OTG_STATE_A_SUSPEND;
} else if (!langwell->hsm.a_bus_req &&
!langwell->hsm.a_set_b_hnp_en) {
struct pci_dev *pdev = langwell->pdev;
if (langwell->host_ops)
langwell->host_ops->remove(pdev);
else
otg_dbg("host driver removed.\n");
langwell_otg_drv_vbus(0);
langwell->otg.state = OTG_STATE_A_WAIT_VFALL;
}
}
break;
case OTG_STATE_A_HOST:
if (langwell->hsm.id) {
langwell->otg.default_a = 0;
langwell->hsm.b_bus_req = 0;
if (langwell->host_ops)
langwell->host_ops->remove(langwell->pdev);
else
otg_dbg("host driver has been removed.\n");
langwell_otg_drv_vbus(0);
langwell->otg.state = OTG_STATE_B_IDLE;
queue_work(langwell->qwork, &langwell->work);
} else if (langwell->hsm.a_bus_drop ||
(!langwell->hsm.a_set_b_hnp_en && !langwell->hsm.a_bus_req)) {
if (langwell->host_ops)
langwell->host_ops->remove(langwell->pdev);
else
otg_dbg("host driver has been removed.\n");
langwell_otg_drv_vbus(0);
langwell->otg.state = OTG_STATE_A_WAIT_VFALL;
} else if (!langwell->hsm.a_vbus_vld) {
if (langwell->host_ops)
langwell->host_ops->remove(langwell->pdev);
else
otg_dbg("host driver has been removed.\n");
langwell_otg_drv_vbus(0);
langwell->otg.state = OTG_STATE_A_VBUS_ERR;
} else if (langwell->hsm.a_set_b_hnp_en
&& !langwell->hsm.a_bus_req) {
/* Set HABA to enable hardware assistance to signal
* A-connect after receiver B-disconnect. Hardware
* will then set client mode and enable URE, SLE and
* PCE after the assistance. otg_dummy_irq is used to
* clean these ints when client driver is not resumed.
*/
if (request_irq(langwell->pdev->irq,
otg_dummy_irq, IRQF_SHARED, driver_name,
langwell->regs) != 0) {
otg_dbg("request interrupt %d failed\n",
langwell->pdev->irq);
}
/* set HABA */
langwell_otg_HABA(1);
langwell->hsm.b_bus_resume = 0;
langwell->hsm.a_aidl_bdis_tmout = 0;
langwell_otg_add_timer(a_aidl_bdis_tmr);
langwell_otg_loc_sof(0);
langwell->otg.state = OTG_STATE_A_SUSPEND;
} else if (!langwell->hsm.b_conn || !langwell->hsm.a_bus_req) {
langwell->hsm.a_wait_bcon_tmout = 0;
langwell->hsm.a_set_b_hnp_en = 0;
langwell_otg_add_timer(a_wait_bcon_tmr);
langwell->otg.state = OTG_STATE_A_WAIT_BCON;
}
break;
case OTG_STATE_A_SUSPEND:
if (langwell->hsm.id) {
langwell_otg_del_timer(a_aidl_bdis_tmr);
langwell_otg_HABA(0);
free_irq(langwell->pdev->irq, langwell->regs);
langwell->otg.default_a = 0;
langwell->hsm.b_bus_req = 0;
if (langwell->host_ops)
langwell->host_ops->remove(langwell->pdev);
else
otg_dbg("host driver has been removed.\n");
langwell_otg_drv_vbus(0);
langwell->otg.state = OTG_STATE_B_IDLE;
queue_work(langwell->qwork, &langwell->work);
} else if (langwell->hsm.a_bus_req ||
langwell->hsm.b_bus_resume) {
langwell_otg_del_timer(a_aidl_bdis_tmr);
langwell_otg_HABA(0);
free_irq(langwell->pdev->irq, langwell->regs);
langwell->hsm.a_suspend_req = 0;
langwell_otg_loc_sof(1);
langwell->otg.state = OTG_STATE_A_HOST;
} else if (langwell->hsm.a_aidl_bdis_tmout ||
langwell->hsm.a_bus_drop) {
langwell_otg_del_timer(a_aidl_bdis_tmr);
langwell_otg_HABA(0);
free_irq(langwell->pdev->irq, langwell->regs);
if (langwell->host_ops)
langwell->host_ops->remove(langwell->pdev);
else
otg_dbg("host driver has been removed.\n");
langwell_otg_drv_vbus(0);
langwell->otg.state = OTG_STATE_A_WAIT_VFALL;
} else if (!langwell->hsm.b_conn &&
langwell->hsm.a_set_b_hnp_en) {
langwell_otg_del_timer(a_aidl_bdis_tmr);
langwell_otg_HABA(0);
free_irq(langwell->pdev->irq, langwell->regs);
if (langwell->host_ops)
langwell->host_ops->remove(langwell->pdev);
else
otg_dbg("host driver has been removed.\n");
langwell->hsm.b_bus_suspend = 0;
langwell->hsm.b_bus_suspend_vld = 0;
langwell->hsm.b_bus_suspend_tmout = 0;
/* msleep(200); */
if (langwell->client_ops)
langwell->client_ops->resume(langwell->pdev);
else
otg_dbg("client driver not loaded.\n");
langwell_otg_add_timer(b_bus_suspend_tmr);
langwell->otg.state = OTG_STATE_A_PERIPHERAL;
break;
} else if (!langwell->hsm.a_vbus_vld) {
langwell_otg_del_timer(a_aidl_bdis_tmr);
langwell_otg_HABA(0);
free_irq(langwell->pdev->irq, langwell->regs);
if (langwell->host_ops)
langwell->host_ops->remove(langwell->pdev);
else
otg_dbg("host driver has been removed.\n");
langwell_otg_drv_vbus(0);
langwell->otg.state = OTG_STATE_A_VBUS_ERR;
}
break;
case OTG_STATE_A_PERIPHERAL:
if (langwell->hsm.id) {
langwell_otg_del_timer(b_bus_suspend_tmr);
langwell->otg.default_a = 0;
langwell->hsm.b_bus_req = 0;
if (langwell->client_ops)
langwell->client_ops->suspend(langwell->pdev,
PMSG_FREEZE);
else
otg_dbg("client driver has been removed.\n");
langwell_otg_drv_vbus(0);
langwell->otg.state = OTG_STATE_B_IDLE;
queue_work(langwell->qwork, &langwell->work);
} else if (!langwell->hsm.a_vbus_vld) {
langwell_otg_del_timer(b_bus_suspend_tmr);
if (langwell->client_ops)
langwell->client_ops->suspend(langwell->pdev,
PMSG_FREEZE);
else
otg_dbg("client driver has been removed.\n");
langwell_otg_drv_vbus(0);
langwell->otg.state = OTG_STATE_A_VBUS_ERR;
} else if (langwell->hsm.a_bus_drop) {
langwell_otg_del_timer(b_bus_suspend_tmr);
if (langwell->client_ops)
langwell->client_ops->suspend(langwell->pdev,
PMSG_FREEZE);
else
otg_dbg("client driver has been removed.\n");
langwell_otg_drv_vbus(0);
langwell->otg.state = OTG_STATE_A_WAIT_VFALL;
} else if (langwell->hsm.b_bus_suspend) {
langwell_otg_del_timer(b_bus_suspend_tmr);
if (langwell->client_ops)
langwell->client_ops->suspend(langwell->pdev,
PMSG_FREEZE);
else
otg_dbg("client driver has been removed.\n");
if (langwell->host_ops)
langwell->host_ops->probe(langwell->pdev,
langwell->host_ops->id_table);
else
otg_dbg("host driver not loaded.\n");
langwell->hsm.a_set_b_hnp_en = 0;
langwell->hsm.a_wait_bcon_tmout = 0;
langwell_otg_add_timer(a_wait_bcon_tmr);
langwell->otg.state = OTG_STATE_A_WAIT_BCON;
} else if (langwell->hsm.b_bus_suspend_tmout) {
u32 val;
val = readl(langwell->regs + CI_PORTSC1);
if (!(val & PORTSC_SUSP))
break;
if (langwell->client_ops)
langwell->client_ops->suspend(langwell->pdev,
PMSG_FREEZE);
else
otg_dbg("client driver has been removed.\n");
if (langwell->host_ops)
langwell->host_ops->probe(langwell->pdev,
langwell->host_ops->id_table);
else
otg_dbg("host driver not loaded.\n");
langwell->hsm.a_set_b_hnp_en = 0;
langwell->hsm.a_wait_bcon_tmout = 0;
langwell_otg_add_timer(a_wait_bcon_tmr);
langwell->otg.state = OTG_STATE_A_WAIT_BCON;
}
break;
case OTG_STATE_A_VBUS_ERR:
if (langwell->hsm.id) {
langwell->otg.default_a = 0;
langwell->hsm.a_clr_err = 0;
langwell->hsm.a_srp_det = 0;
langwell->otg.state = OTG_STATE_B_IDLE;
queue_work(langwell->qwork, &langwell->work);
} else if (langwell->hsm.a_clr_err) {
langwell->hsm.a_clr_err = 0;
langwell->hsm.a_srp_det = 0;
reset_otg();
init_hsm();
if (langwell->otg.state == OTG_STATE_A_IDLE)
queue_work(langwell->qwork, &langwell->work);
}
break;
case OTG_STATE_A_WAIT_VFALL:
if (langwell->hsm.id) {
langwell->otg.default_a = 0;
langwell->otg.state = OTG_STATE_B_IDLE;
queue_work(langwell->qwork, &langwell->work);
} else if (langwell->hsm.a_bus_req) {
langwell_otg_drv_vbus(1);
langwell->hsm.a_wait_vrise_tmout = 0;
langwell_otg_add_timer(a_wait_vrise_tmr);
langwell->otg.state = OTG_STATE_A_WAIT_VRISE;
} else if (!langwell->hsm.a_sess_vld) {
langwell->hsm.a_srp_det = 0;
langwell_otg_drv_vbus(0);
set_host_mode();
langwell->otg.state = OTG_STATE_A_IDLE;
}
break;
default:
;
}
otg_dbg("%s: new state = %s\n", __func__,
state_string(langwell->otg.state));
}
static ssize_t
show_registers(struct device *_dev, struct device_attribute *attr, char *buf)
{
struct langwell_otg *langwell;
char *next;
unsigned size;
unsigned t;
langwell = the_transceiver;
next = buf;
size = PAGE_SIZE;
t = scnprintf(next, size,
"\n"
"USBCMD = 0x%08x \n"
"USBSTS = 0x%08x \n"
"USBINTR = 0x%08x \n"
"ASYNCLISTADDR = 0x%08x \n"
"PORTSC1 = 0x%08x \n"
"HOSTPC1 = 0x%08x \n"
"OTGSC = 0x%08x \n"
"USBMODE = 0x%08x \n",
readl(langwell->regs + 0x30),
readl(langwell->regs + 0x34),
readl(langwell->regs + 0x38),
readl(langwell->regs + 0x48),
readl(langwell->regs + 0x74),
readl(langwell->regs + 0xb4),
readl(langwell->regs + 0xf4),
readl(langwell->regs + 0xf8)
);
size -= t;
next += t;
return PAGE_SIZE - size;
}
static DEVICE_ATTR(registers, S_IRUGO, show_registers, NULL);
static ssize_t
show_hsm(struct device *_dev, struct device_attribute *attr, char *buf)
{
struct langwell_otg *langwell;
char *next;
unsigned size;
unsigned t;
langwell = the_transceiver;
next = buf;
size = PAGE_SIZE;
t = scnprintf(next, size,
"\n"
"current state = %s\n"
"a_bus_resume = \t%d\n"
"a_bus_suspend = \t%d\n"
"a_conn = \t%d\n"
"a_sess_vld = \t%d\n"
"a_srp_det = \t%d\n"
"a_vbus_vld = \t%d\n"
"b_bus_resume = \t%d\n"
"b_bus_suspend = \t%d\n"
"b_conn = \t%d\n"
"b_se0_srp = \t%d\n"
"b_sess_end = \t%d\n"
"b_sess_vld = \t%d\n"
"id = \t%d\n"
"a_set_b_hnp_en = \t%d\n"
"b_srp_done = \t%d\n"
"b_hnp_enable = \t%d\n"
"a_wait_vrise_tmout = \t%d\n"
"a_wait_bcon_tmout = \t%d\n"
"a_aidl_bdis_tmout = \t%d\n"
"b_ase0_brst_tmout = \t%d\n"
"a_bus_drop = \t%d\n"
"a_bus_req = \t%d\n"
"a_clr_err = \t%d\n"
"a_suspend_req = \t%d\n"
"b_bus_req = \t%d\n"
"b_bus_suspend_tmout = \t%d\n"
"b_bus_suspend_vld = \t%d\n",
state_string(langwell->otg.state),
langwell->hsm.a_bus_resume,
langwell->hsm.a_bus_suspend,
langwell->hsm.a_conn,
langwell->hsm.a_sess_vld,
langwell->hsm.a_srp_det,
langwell->hsm.a_vbus_vld,
langwell->hsm.b_bus_resume,
langwell->hsm.b_bus_suspend,
langwell->hsm.b_conn,
langwell->hsm.b_se0_srp,
langwell->hsm.b_sess_end,
langwell->hsm.b_sess_vld,
langwell->hsm.id,
langwell->hsm.a_set_b_hnp_en,
langwell->hsm.b_srp_done,
langwell->hsm.b_hnp_enable,
langwell->hsm.a_wait_vrise_tmout,
langwell->hsm.a_wait_bcon_tmout,
langwell->hsm.a_aidl_bdis_tmout,
langwell->hsm.b_ase0_brst_tmout,
langwell->hsm.a_bus_drop,
langwell->hsm.a_bus_req,
langwell->hsm.a_clr_err,
langwell->hsm.a_suspend_req,
langwell->hsm.b_bus_req,
langwell->hsm.b_bus_suspend_tmout,
langwell->hsm.b_bus_suspend_vld
);
size -= t;
next += t;
return PAGE_SIZE - size;
}
static DEVICE_ATTR(hsm, S_IRUGO, show_hsm, NULL);
static ssize_t
get_a_bus_req(struct device *dev, struct device_attribute *attr, char *buf)
{
struct langwell_otg *langwell;
char *next;
unsigned size;
unsigned t;
langwell = the_transceiver;
next = buf;
size = PAGE_SIZE;
t = scnprintf(next, size, "%d", langwell->hsm.a_bus_req);
size -= t;
next += t;
return PAGE_SIZE - size;
}
static ssize_t
set_a_bus_req(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct langwell_otg *langwell;
langwell = the_transceiver;
if (!langwell->otg.default_a)
return -1;
if (count > 2)
return -1;
if (buf[0] == '0') {
langwell->hsm.a_bus_req = 0;
otg_dbg("a_bus_req = 0\n");
} else if (buf[0] == '1') {
/* If a_bus_drop is TRUE, a_bus_req can't be set */
if (langwell->hsm.a_bus_drop)
return -1;
langwell->hsm.a_bus_req = 1;
otg_dbg("a_bus_req = 1\n");
}
if (spin_trylock(&langwell->wq_lock)) {
queue_work(langwell->qwork, &langwell->work);
spin_unlock(&langwell->wq_lock);
}
return count;
}
static DEVICE_ATTR(a_bus_req, S_IRUGO | S_IWUGO, get_a_bus_req, set_a_bus_req);
static ssize_t
get_a_bus_drop(struct device *dev, struct device_attribute *attr, char *buf)
{
struct langwell_otg *langwell;
char *next;
unsigned size;
unsigned t;
langwell = the_transceiver;
next = buf;
size = PAGE_SIZE;
t = scnprintf(next, size, "%d", langwell->hsm.a_bus_drop);
size -= t;
next += t;
return PAGE_SIZE - size;
}
static ssize_t
set_a_bus_drop(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct langwell_otg *langwell;
langwell = the_transceiver;
if (!langwell->otg.default_a)
return -1;
if (count > 2)
return -1;
if (buf[0] == '0') {
langwell->hsm.a_bus_drop = 0;
otg_dbg("a_bus_drop = 0\n");
} else if (buf[0] == '1') {
langwell->hsm.a_bus_drop = 1;
langwell->hsm.a_bus_req = 0;
otg_dbg("a_bus_drop = 1, then a_bus_req = 0\n");
}
if (spin_trylock(&langwell->wq_lock)) {
queue_work(langwell->qwork, &langwell->work);
spin_unlock(&langwell->wq_lock);
}
return count;
}
static DEVICE_ATTR(a_bus_drop, S_IRUGO | S_IWUGO,
get_a_bus_drop, set_a_bus_drop);
static ssize_t
get_b_bus_req(struct device *dev, struct device_attribute *attr, char *buf)
{
struct langwell_otg *langwell;
char *next;
unsigned size;
unsigned t;
langwell = the_transceiver;
next = buf;
size = PAGE_SIZE;
t = scnprintf(next, size, "%d", langwell->hsm.b_bus_req);
size -= t;
next += t;
return PAGE_SIZE - size;
}
static ssize_t
set_b_bus_req(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct langwell_otg *langwell;
langwell = the_transceiver;
if (langwell->otg.default_a)
return -1;
if (count > 2)
return -1;
if (buf[0] == '0') {
langwell->hsm.b_bus_req = 0;
otg_dbg("b_bus_req = 0\n");
} else if (buf[0] == '1') {
langwell->hsm.b_bus_req = 1;
otg_dbg("b_bus_req = 1\n");
}
if (spin_trylock(&langwell->wq_lock)) {
queue_work(langwell->qwork, &langwell->work);
spin_unlock(&langwell->wq_lock);
}
return count;
}
static DEVICE_ATTR(b_bus_req, S_IRUGO | S_IWUGO, get_b_bus_req, set_b_bus_req);
static ssize_t
set_a_clr_err(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct langwell_otg *langwell;
langwell = the_transceiver;
if (!langwell->otg.default_a)
return -1;
if (count > 2)
return -1;
if (buf[0] == '1') {
langwell->hsm.a_clr_err = 1;
otg_dbg("a_clr_err = 1\n");
}
if (spin_trylock(&langwell->wq_lock)) {
queue_work(langwell->qwork, &langwell->work);
spin_unlock(&langwell->wq_lock);
}
return count;
}
static DEVICE_ATTR(a_clr_err, S_IWUGO, NULL, set_a_clr_err);
static struct attribute *inputs_attrs[] = {
&dev_attr_a_bus_req.attr,
&dev_attr_a_bus_drop.attr,
&dev_attr_b_bus_req.attr,
&dev_attr_a_clr_err.attr,
NULL,
};
static struct attribute_group debug_dev_attr_group = {
.name = "inputs",
.attrs = inputs_attrs,
};
int langwell_register_host(struct pci_driver *host_driver)
{
int ret = 0;
the_transceiver->host_ops = host_driver;
queue_work(the_transceiver->qwork, &the_transceiver->work);
otg_dbg("host controller driver is registered\n");
return ret;
}
EXPORT_SYMBOL(langwell_register_host);
void langwell_unregister_host(struct pci_driver *host_driver)
{
if (the_transceiver->host_ops)
the_transceiver->host_ops->remove(the_transceiver->pdev);
the_transceiver->host_ops = NULL;
the_transceiver->hsm.a_bus_drop = 1;
queue_work(the_transceiver->qwork, &the_transceiver->work);
otg_dbg("host controller driver is unregistered\n");
}
EXPORT_SYMBOL(langwell_unregister_host);
int langwell_register_peripheral(struct pci_driver *client_driver)
{
int ret = 0;
if (client_driver)
ret = client_driver->probe(the_transceiver->pdev,
client_driver->id_table);
if (!ret) {
the_transceiver->client_ops = client_driver;
queue_work(the_transceiver->qwork, &the_transceiver->work);
otg_dbg("client controller driver is registered\n");
}
return ret;
}
EXPORT_SYMBOL(langwell_register_peripheral);
void langwell_unregister_peripheral(struct pci_driver *client_driver)
{
if (the_transceiver->client_ops)
the_transceiver->client_ops->remove(the_transceiver->pdev);
the_transceiver->client_ops = NULL;
the_transceiver->hsm.b_bus_req = 0;
queue_work(the_transceiver->qwork, &the_transceiver->work);
otg_dbg("client controller driver is unregistered\n");
}
EXPORT_SYMBOL(langwell_unregister_peripheral);
static int langwell_otg_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
unsigned long resource, len;
void __iomem *base = NULL;
int retval;
u32 val32;
struct langwell_otg *langwell;
char qname[] = "langwell_otg_queue";
retval = 0;
otg_dbg("\notg controller is detected.\n");
if (pci_enable_device(pdev) < 0) {
retval = -ENODEV;
goto done;
}
langwell = kzalloc(sizeof *langwell, GFP_KERNEL);
if (langwell == NULL) {
retval = -ENOMEM;
goto done;
}
the_transceiver = langwell;
/* control register: BAR 0 */
resource = pci_resource_start(pdev, 0);
len = pci_resource_len(pdev, 0);
if (!request_mem_region(resource, len, driver_name)) {
retval = -EBUSY;
goto err;
}
langwell->region = 1;
base = ioremap_nocache(resource, len);
if (base == NULL) {
retval = -EFAULT;
goto err;
}
langwell->regs = base;
if (!pdev->irq) {
otg_dbg("No IRQ.\n");
retval = -ENODEV;
goto err;
}
langwell->qwork = create_workqueue(qname);
if (!langwell->qwork) {
otg_dbg("cannot create workqueue %s\n", qname);
retval = -ENOMEM;
goto err;
}
INIT_WORK(&langwell->work, langwell_otg_work);
/* OTG common part */
langwell->pdev = pdev;
langwell->otg.dev = &pdev->dev;
langwell->otg.label = driver_name;
langwell->otg.set_host = langwell_otg_set_host;
langwell->otg.set_peripheral = langwell_otg_set_peripheral;
langwell->otg.set_power = langwell_otg_set_power;
langwell->otg.start_srp = langwell_otg_start_srp;
langwell->otg.state = OTG_STATE_UNDEFINED;
if (otg_set_transceiver(&langwell->otg)) {
otg_dbg("can't set transceiver\n");
retval = -EBUSY;
goto err;
}
reset_otg();
init_hsm();
spin_lock_init(&langwell->lock);
spin_lock_init(&langwell->wq_lock);
INIT_LIST_HEAD(&active_timers);
langwell_otg_init_timers(&langwell->hsm);
if (request_irq(pdev->irq, otg_irq, IRQF_SHARED,
driver_name, langwell) != 0) {
otg_dbg("request interrupt %d failed\n", pdev->irq);
retval = -EBUSY;
goto err;
}
/* enable OTGSC int */
val32 = OTGSC_DPIE | OTGSC_BSEIE | OTGSC_BSVIE |
OTGSC_ASVIE | OTGSC_AVVIE | OTGSC_IDIE | OTGSC_IDPU;
writel(val32, langwell->regs + CI_OTGSC);
retval = device_create_file(&pdev->dev, &dev_attr_registers);
if (retval < 0) {
otg_dbg("Can't register sysfs attribute: %d\n", retval);
goto err;
}
retval = device_create_file(&pdev->dev, &dev_attr_hsm);
if (retval < 0) {
otg_dbg("Can't hsm sysfs attribute: %d\n", retval);
goto err;
}
retval = sysfs_create_group(&pdev->dev.kobj, &debug_dev_attr_group);
if (retval < 0) {
otg_dbg("Can't register sysfs attr group: %d\n", retval);
goto err;
}
if (langwell->otg.state == OTG_STATE_A_IDLE)
queue_work(langwell->qwork, &langwell->work);
return 0;
err:
if (the_transceiver)
langwell_otg_remove(pdev);
done:
return retval;
}
static void langwell_otg_remove(struct pci_dev *pdev)
{
struct langwell_otg *langwell;
langwell = the_transceiver;
if (langwell->qwork) {
flush_workqueue(langwell->qwork);
destroy_workqueue(langwell->qwork);
}
langwell_otg_free_timers();
/* disable OTGSC interrupt as OTGSC doesn't change in reset */
writel(0, langwell->regs + CI_OTGSC);
if (pdev->irq)
free_irq(pdev->irq, langwell);
if (langwell->regs)
iounmap(langwell->regs);
if (langwell->region)
release_mem_region(pci_resource_start(pdev, 0),
pci_resource_len(pdev, 0));
otg_set_transceiver(NULL);
pci_disable_device(pdev);
sysfs_remove_group(&pdev->dev.kobj, &debug_dev_attr_group);
device_remove_file(&pdev->dev, &dev_attr_hsm);
device_remove_file(&pdev->dev, &dev_attr_registers);
kfree(langwell);
langwell = NULL;
}
static void transceiver_suspend(struct pci_dev *pdev)
{
pci_save_state(pdev);
pci_set_power_state(pdev, PCI_D3hot);
langwell_otg_phy_low_power(1);
}
static int langwell_otg_suspend(struct pci_dev *pdev, pm_message_t message)
{
int ret = 0;
struct langwell_otg *langwell;
langwell = the_transceiver;
/* Disbale OTG interrupts */
langwell_otg_intr(0);
if (pdev->irq)
free_irq(pdev->irq, langwell);
/* Prevent more otg_work */
flush_workqueue(langwell->qwork);
spin_lock(&langwell->wq_lock);
/* start actions */
switch (langwell->otg.state) {
case OTG_STATE_A_IDLE:
case OTG_STATE_B_IDLE:
case OTG_STATE_A_WAIT_VFALL:
case OTG_STATE_A_VBUS_ERR:
transceiver_suspend(pdev);
break;
case OTG_STATE_A_WAIT_VRISE:
langwell_otg_del_timer(a_wait_vrise_tmr);
langwell->hsm.a_srp_det = 0;
langwell_otg_drv_vbus(0);
langwell->otg.state = OTG_STATE_A_IDLE;
transceiver_suspend(pdev);
break;
case OTG_STATE_A_WAIT_BCON:
langwell_otg_del_timer(a_wait_bcon_tmr);
if (langwell->host_ops)
ret = langwell->host_ops->suspend(pdev, message);
langwell_otg_drv_vbus(0);
break;
case OTG_STATE_A_HOST:
if (langwell->host_ops)
ret = langwell->host_ops->suspend(pdev, message);
langwell_otg_drv_vbus(0);
langwell_otg_phy_low_power(1);
break;
case OTG_STATE_A_SUSPEND:
langwell_otg_del_timer(a_aidl_bdis_tmr);
langwell_otg_HABA(0);
if (langwell->host_ops)
langwell->host_ops->remove(pdev);
else
otg_dbg("host driver has been removed.\n");
langwell_otg_drv_vbus(0);
transceiver_suspend(pdev);
langwell->otg.state = OTG_STATE_A_WAIT_VFALL;
break;
case OTG_STATE_A_PERIPHERAL:
if (langwell->client_ops)
ret = langwell->client_ops->suspend(pdev, message);
else
otg_dbg("client driver has been removed.\n");
langwell_otg_drv_vbus(0);
transceiver_suspend(pdev);
langwell->otg.state = OTG_STATE_A_WAIT_VFALL;
break;
case OTG_STATE_B_HOST:
if (langwell->host_ops)
langwell->host_ops->remove(pdev);
else
otg_dbg("host driver has been removed.\n");
langwell->hsm.b_bus_req = 0;
transceiver_suspend(pdev);
langwell->otg.state = OTG_STATE_B_IDLE;
break;
case OTG_STATE_B_PERIPHERAL:
if (langwell->client_ops)
ret = langwell->client_ops->suspend(pdev, message);
else
otg_dbg("client driver has been removed.\n");
break;
case OTG_STATE_B_WAIT_ACON:
langwell_otg_del_timer(b_ase0_brst_tmr);
langwell_otg_HAAR(0);
if (langwell->host_ops)
langwell->host_ops->remove(pdev);
else
otg_dbg("host driver has been removed.\n");
langwell->hsm.b_bus_req = 0;
langwell->otg.state = OTG_STATE_B_IDLE;
transceiver_suspend(pdev);
break;
default:
otg_dbg("error state before suspend\n ");
break;
}
spin_unlock(&langwell->wq_lock);
return ret;
}
static void transceiver_resume(struct pci_dev *pdev)
{
pci_restore_state(pdev);
pci_set_power_state(pdev, PCI_D0);
langwell_otg_phy_low_power(0);
}
static int langwell_otg_resume(struct pci_dev *pdev)
{
int ret = 0;
struct langwell_otg *langwell;
langwell = the_transceiver;
spin_lock(&langwell->wq_lock);
switch (langwell->otg.state) {
case OTG_STATE_A_IDLE:
case OTG_STATE_B_IDLE:
case OTG_STATE_A_WAIT_VFALL:
case OTG_STATE_A_VBUS_ERR:
transceiver_resume(pdev);
break;
case OTG_STATE_A_WAIT_BCON:
langwell_otg_add_timer(a_wait_bcon_tmr);
langwell_otg_drv_vbus(1);
if (langwell->host_ops)
ret = langwell->host_ops->resume(pdev);
break;
case OTG_STATE_A_HOST:
langwell_otg_drv_vbus(1);
langwell_otg_phy_low_power(0);
if (langwell->host_ops)
ret = langwell->host_ops->resume(pdev);
break;
case OTG_STATE_B_PERIPHERAL:
if (langwell->client_ops)
ret = langwell->client_ops->resume(pdev);
else
otg_dbg("client driver not loaded.\n");
break;
default:
otg_dbg("error state before suspend\n ");
break;
}
if (request_irq(pdev->irq, otg_irq, IRQF_SHARED,
driver_name, the_transceiver) != 0) {
otg_dbg("request interrupt %d failed\n", pdev->irq);
ret = -EBUSY;
}
/* enable OTG interrupts */
langwell_otg_intr(1);
spin_unlock(&langwell->wq_lock);
queue_work(langwell->qwork, &langwell->work);
return ret;
}
static int __init langwell_otg_init(void)
{
return pci_register_driver(&otg_pci_driver);
}
module_init(langwell_otg_init);
static void __exit langwell_otg_cleanup(void)
{
pci_unregister_driver(&otg_pci_driver);
}
module_exit(langwell_otg_cleanup);
......@@ -53,6 +53,7 @@ EXPORT_SYMBOL(usb_nop_xceiv_register);
void usb_nop_xceiv_unregister(void)
{
platform_device_unregister(pd);
pd = NULL;
}
EXPORT_SYMBOL(usb_nop_xceiv_unregister);
......
......@@ -169,9 +169,11 @@ static int usb_console_setup(struct console *co, char *options)
kfree(tty);
}
}
/* So we know not to kill the hardware on a hangup on this
port. We have also bumped the use count by one so it won't go
idle */
/* Now that any required fake tty operations are completed restore
* the tty port count */
--port->port.count;
/* The console is special in terms of closing the device so
* indicate this port is now acting as a system console. */
port->console = 1;
retval = 0;
......@@ -204,7 +206,7 @@ static void usb_console_write(struct console *co,
dbg("%s - port %d, %d byte(s)", __func__, port->number, count);
if (!port->port.count) {
if (!port->console) {
dbg("%s - port not opened", __func__);
return;
}
......@@ -300,8 +302,7 @@ void usb_serial_console_exit(void)
{
if (usbcons_info.port) {
unregister_console(&usbcons);
if (usbcons_info.port->port.count)
usbcons_info.port->port.count--;
usbcons_info.port->console = 0;
usbcons_info.port = NULL;
}
}
......
......@@ -67,6 +67,8 @@ static struct usb_device_id id_table [] = {
{ USB_DEVICE(0x10AB, 0x10C5) }, /* Siemens MC60 Cable */
{ USB_DEVICE(0x10B5, 0xAC70) }, /* Nokia CA-42 USB */
{ USB_DEVICE(0x10C4, 0x0F91) }, /* Vstabi */
{ USB_DEVICE(0x10C4, 0x1101) }, /* Arkham Technology DS101 Bus Monitor */
{ USB_DEVICE(0x10C4, 0x1601) }, /* Arkham Technology DS101 Adapter */
{ USB_DEVICE(0x10C4, 0x800A) }, /* SPORTident BSM7-D-USB main station */
{ USB_DEVICE(0x10C4, 0x803B) }, /* Pololu USB-serial converter */
{ USB_DEVICE(0x10C4, 0x8053) }, /* Enfora EDG1228 */
......
......@@ -1228,8 +1228,8 @@ static void cypress_read_int_callback(struct urb *urb)
/* precursor to disconnect so just go away */
return;
case -EPIPE:
usb_clear_halt(port->serial->dev, 0x81);
break;
/* Can't call usb_clear_halt while in_interrupt */
/* FALLS THROUGH */
default:
/* something ugly is going on... */
dev_err(&urb->dev->dev,
......
......@@ -108,6 +108,7 @@ struct ftdi_sio_quirk {
static int ftdi_jtag_probe(struct usb_serial *serial);
static int ftdi_mtxorb_hack_setup(struct usb_serial *serial);
static int ftdi_NDI_device_setup(struct usb_serial *serial);
static void ftdi_USB_UIRT_setup(struct ftdi_private *priv);
static void ftdi_HE_TIRA1_setup(struct ftdi_private *priv);
......@@ -119,6 +120,10 @@ static struct ftdi_sio_quirk ftdi_mtxorb_hack_quirk = {
.probe = ftdi_mtxorb_hack_setup,
};
static struct ftdi_sio_quirk ftdi_NDI_device_quirk = {
.probe = ftdi_NDI_device_setup,
};
static struct ftdi_sio_quirk ftdi_USB_UIRT_quirk = {
.port_probe = ftdi_USB_UIRT_setup,
};
......@@ -192,6 +197,7 @@ static struct usb_device_id id_table_combined [] = {
{ USB_DEVICE(FTDI_VID, FTDI_MTXORB_4_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_MTXORB_5_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_MTXORB_6_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_R2000KU_TRUE_RNG) },
{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0100_PID) },
{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0101_PID) },
{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0102_PID) },
......@@ -580,6 +586,9 @@ static struct usb_device_id id_table_combined [] = {
{ USB_DEVICE(FTDI_VID, FTDI_CCSICDU20_0_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_CCSICDU40_1_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_CCSMACHX_2_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_CCSLOAD_N_GO_3_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_CCSICDU64_4_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_CCSPRIME8_5_PID) },
{ USB_DEVICE(FTDI_VID, INSIDE_ACCESSO) },
{ USB_DEVICE(INTREPID_VID, INTREPID_VALUECAN_PID) },
{ USB_DEVICE(INTREPID_VID, INTREPID_NEOVI_PID) },
......@@ -645,6 +654,16 @@ static struct usb_device_id id_table_combined [] = {
{ USB_DEVICE(FTDI_VID, FTDI_TACTRIX_OPENPORT_13S_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_TACTRIX_OPENPORT_13U_PID) },
{ USB_DEVICE(ELEKTOR_VID, ELEKTOR_FT323R_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_NDI_HUC_PID),
.driver_info = (kernel_ulong_t)&ftdi_NDI_device_quirk },
{ USB_DEVICE(FTDI_VID, FTDI_NDI_SPECTRA_SCU_PID),
.driver_info = (kernel_ulong_t)&ftdi_NDI_device_quirk },
{ USB_DEVICE(FTDI_VID, FTDI_NDI_FUTURE_2_PID),
.driver_info = (kernel_ulong_t)&ftdi_NDI_device_quirk },
{ USB_DEVICE(FTDI_VID, FTDI_NDI_FUTURE_3_PID),
.driver_info = (kernel_ulong_t)&ftdi_NDI_device_quirk },
{ USB_DEVICE(FTDI_VID, FTDI_NDI_AURORA_SCU_PID),
.driver_info = (kernel_ulong_t)&ftdi_NDI_device_quirk },
{ USB_DEVICE(TELLDUS_VID, TELLDUS_TELLSTICK_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_MAXSTREAM_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_PHI_FISCO_PID) },
......@@ -661,6 +680,8 @@ static struct usb_device_id id_table_combined [] = {
.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(FTDI_VID, FTDI_TURTELIZER_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) },
{ USB_DEVICE(PAPOUCH_VID, PAPOUCH_QUIDO4x4_PID) },
......@@ -668,7 +689,6 @@ static struct usb_device_id id_table_combined [] = {
{ USB_DEVICE(FTDI_VID, FTDI_DOMINTELL_DUSB_PID) },
{ USB_DEVICE(ALTI2_VID, ALTI2_N3_PID) },
{ USB_DEVICE(FTDI_VID, DIEBOLD_BCS_SE923_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_NDI_HUC_PID) },
{ USB_DEVICE(ATMEL_VID, STK541_PID) },
{ USB_DEVICE(DE_VID, STB_PID) },
{ USB_DEVICE(DE_VID, WHT_PID) },
......@@ -1024,6 +1044,16 @@ static __u32 get_ftdi_divisor(struct tty_struct *tty,
case FT2232C: /* FT2232C chip */
case FT232RL:
if (baud <= 3000000) {
__u16 product_id = le16_to_cpu(
port->serial->dev->descriptor.idProduct);
if (((FTDI_NDI_HUC_PID == product_id) ||
(FTDI_NDI_SPECTRA_SCU_PID == product_id) ||
(FTDI_NDI_FUTURE_2_PID == product_id) ||
(FTDI_NDI_FUTURE_3_PID == product_id) ||
(FTDI_NDI_AURORA_SCU_PID == product_id)) &&
(baud == 19200)) {
baud = 1200000;
}
div_value = ftdi_232bm_baud_to_divisor(baud);
} else {
dbg("%s - Baud rate too high!", __func__);
......@@ -1554,6 +1584,39 @@ static void ftdi_HE_TIRA1_setup(struct ftdi_private *priv)
priv->force_rtscts = 1;
} /* ftdi_HE_TIRA1_setup */
/*
* Module parameter to control latency timer for NDI FTDI-based USB devices.
* If this value is not set in modprobe.conf.local its value will be set to 1ms.
*/
static int ndi_latency_timer = 1;
/* Setup for the NDI FTDI-based USB devices, which requires hardwired
* baudrate (19200 gets mapped to 1200000).
*
* Called from usbserial:serial_probe.
*/
static int ftdi_NDI_device_setup(struct usb_serial *serial)
{
struct usb_device *udev = serial->dev;
int latency = ndi_latency_timer;
int rv = 0;
char buf[1];
if (latency == 0)
latency = 1;
if (latency > 99)
latency = 99;
dbg("%s setting NDI device latency to %d", __func__, latency);
dev_info(&udev->dev, "NDI device with a latency value of %d", latency);
rv = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
FTDI_SIO_SET_LATENCY_TIMER_REQUEST,
FTDI_SIO_SET_LATENCY_TIMER_REQUEST_TYPE,
latency, 0, buf, 0, WDR_TIMEOUT);
return 0;
}
/*
* First port on JTAG adaptors such as Olimex arm-usb-ocd or the FIC/OpenMoko
* Neo1973 Debug Board is reserved for JTAG interface and can be accessed from
......@@ -2623,3 +2686,5 @@ MODULE_PARM_DESC(vendor, "User specified vendor ID (default="
module_param(product, ushort, 0);
MODULE_PARM_DESC(product, "User specified product ID");
module_param(ndi_latency_timer, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(ndi_latency_timer, "NDI device latency timer override");
......@@ -506,6 +506,7 @@
*
* Armin Laeuger originally sent the PID for the UM 100 module.
*/
#define FTDI_R2000KU_TRUE_RNG 0xFB80 /* R2000KU TRUE RNG */
#define FTDI_ELV_UR100_PID 0xFB58 /* USB-RS232-Umsetzer (UR 100) */
#define FTDI_ELV_UM100_PID 0xFB5A /* USB-Modul UM 100 */
#define FTDI_ELV_UO100_PID 0xFB5B /* USB-Modul UO 100 */
......@@ -614,6 +615,9 @@
#define FTDI_CCSICDU20_0_PID 0xF9D0
#define FTDI_CCSICDU40_1_PID 0xF9D1
#define FTDI_CCSMACHX_2_PID 0xF9D2
#define FTDI_CCSLOAD_N_GO_3_PID 0xF9D3
#define FTDI_CCSICDU64_4_PID 0xF9D4
#define FTDI_CCSPRIME8_5_PID 0xF9D5
/* Inside Accesso contactless reader (http://www.insidefr.com) */
#define INSIDE_ACCESSO 0xFAD0
......@@ -735,6 +739,15 @@
/* Pyramid Computer GmbH */
#define FTDI_PYRAMID_PID 0xE6C8 /* Pyramid Appliance Display */
/*
* NDI (www.ndigital.com) product ids
*/
#define FTDI_NDI_HUC_PID 0xDA70 /* NDI Host USB Converter */
#define FTDI_NDI_SPECTRA_SCU_PID 0xDA71 /* NDI Spectra SCU */
#define FTDI_NDI_FUTURE_2_PID 0xDA72 /* NDI future device #2 */
#define FTDI_NDI_FUTURE_3_PID 0xDA73 /* NDI future device #3 */
#define FTDI_NDI_AURORA_SCU_PID 0xDA74 /* NDI Aurora SCU */
/*
* Posiflex inc retail equipment (http://www.posiflex.com.tw)
*/
......@@ -848,9 +861,6 @@
#define TML_VID 0x1B91 /* Vendor ID */
#define TML_USB_SERIAL_PID 0x0064 /* USB - Serial Converter */
/* NDI Polaris System */
#define FTDI_NDI_HUC_PID 0xDA70
/* Propox devices */
#define FTDI_PROPOX_JTAGCABLEII_PID 0xD738
......@@ -934,6 +944,8 @@
#define MARVELL_VID 0x9e88
#define MARVELL_SHEEVAPLUG_PID 0x9e8f
#define FTDI_TURTELIZER_PID 0xBDC8 /* JTAG/RS-232 adapter by egnite GmBH */
/*
* BmRequestType: 1100 0000b
* bRequest: FTDI_E2_READ
......
......@@ -206,6 +206,7 @@ static int option_resume(struct usb_serial *serial);
#define NOVATELWIRELESS_PRODUCT_MC950D 0x4400
#define NOVATELWIRELESS_PRODUCT_U727 0x5010
#define NOVATELWIRELESS_PRODUCT_MC760 0x6000
#define NOVATELWIRELESS_PRODUCT_OVMC760 0x6002
/* FUTURE NOVATEL PRODUCTS */
#define NOVATELWIRELESS_PRODUCT_EVDO_HIGHSPEED 0X6001
......@@ -307,11 +308,20 @@ static int option_resume(struct usb_serial *serial);
#define DLINK_VENDOR_ID 0x1186
#define DLINK_PRODUCT_DWM_652 0x3e04
#define QISDA_VENDOR_ID 0x1da5
#define QISDA_PRODUCT_H21_4512 0x4512
#define QISDA_PRODUCT_H21_4523 0x4523
#define QISDA_PRODUCT_H20_4515 0x4515
#define QISDA_PRODUCT_H20_4519 0x4519
/* TOSHIBA PRODUCTS */
#define TOSHIBA_VENDOR_ID 0x0930
#define TOSHIBA_PRODUCT_HSDPA_MINICARD 0x1302
#define ALINK_VENDOR_ID 0x1e0e
#define ALINK_PRODUCT_3GU 0x9200
static struct usb_device_id option_ids[] = {
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COLT) },
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA) },
......@@ -430,6 +440,7 @@ static struct usb_device_id option_ids[] = {
{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_MC727) }, /* Novatel MC727/U727/USB727 */
{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_U727) }, /* Novatel MC727/U727/USB727 */
{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_MC760) }, /* Novatel MC760/U760/USB760 */
{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_OVMC760) }, /* Novatel Ovation MC760 */
{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_HSPA_FULLSPEED) }, /* Novatel HSPA product */
{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_EVDO_EMBEDDED_FULLSPEED) }, /* Novatel EVDO Embedded product */
{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_HSPA_EMBEDDED_FULLSPEED) }, /* Novatel HSPA Embedded product */
......@@ -529,8 +540,13 @@ static struct usb_device_id option_ids[] = {
{ USB_DEVICE(ZTE_VENDOR_ID, ZTE_PRODUCT_CDMA_TECH) },
{ USB_DEVICE(BENQ_VENDOR_ID, BENQ_PRODUCT_H10) },
{ USB_DEVICE(DLINK_VENDOR_ID, DLINK_PRODUCT_DWM_652) },
{ USB_DEVICE(0x1da5, 0x4515) }, /* BenQ H20 */
{ USB_DEVICE(QISDA_VENDOR_ID, QISDA_PRODUCT_H21_4512) },
{ USB_DEVICE(QISDA_VENDOR_ID, QISDA_PRODUCT_H21_4523) },
{ USB_DEVICE(QISDA_VENDOR_ID, QISDA_PRODUCT_H20_4515) },
{ USB_DEVICE(QISDA_VENDOR_ID, QISDA_PRODUCT_H20_4519) },
{ USB_DEVICE(TOSHIBA_VENDOR_ID, TOSHIBA_PRODUCT_HSDPA_MINICARD ) }, /* Toshiba 3G HSDPA == Novatel Expedite EU870D MiniCard */
{ USB_DEVICE(ALINK_VENDOR_ID, 0x9000) },
{ USB_DEVICE_AND_INTERFACE_INFO(ALINK_VENDOR_ID, ALINK_PRODUCT_3GU, 0xff, 0xff, 0xff) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, option_ids);
......@@ -732,7 +748,6 @@ static int option_write(struct tty_struct *tty, struct usb_serial_port *port,
memcpy(this_urb->transfer_buffer, buf, todo);
this_urb->transfer_buffer_length = todo;
this_urb->dev = port->serial->dev;
err = usb_submit_urb(this_urb, GFP_ATOMIC);
if (err) {
dbg("usb_submit_urb %p (write bulk) failed "
......@@ -860,7 +875,6 @@ static void option_instat_callback(struct urb *urb)
/* Resubmit urb so we continue receiving IRQ data */
if (status != -ESHUTDOWN && status != -ENOENT) {
urb->dev = serial->dev;
err = usb_submit_urb(urb, GFP_ATOMIC);
if (err)
dbg("%s: resubmit intr urb failed. (%d)",
......@@ -921,23 +935,11 @@ static int option_open(struct tty_struct *tty,
dbg("%s", __func__);
/* Reset low level data toggle and start reading from endpoints */
/* Start reading from the IN endpoint */
for (i = 0; i < N_IN_URB; i++) {
urb = portdata->in_urbs[i];
if (!urb)
continue;
if (urb->dev != serial->dev) {
dbg("%s: dev %p != %p", __func__,
urb->dev, serial->dev);
continue;
}
/*
* make sure endpoint data toggle is synchronized with the
* device
*/
usb_clear_halt(urb->dev, urb->pipe);
err = usb_submit_urb(urb, GFP_KERNEL);
if (err) {
dbg("%s: submit urb %d failed (%d) %d",
......@@ -946,16 +948,6 @@ static int option_open(struct tty_struct *tty,
}
}
/* Reset low level data toggle on out endpoints */
for (i = 0; i < N_OUT_URB; i++) {
urb = portdata->out_urbs[i];
if (!urb)
continue;
urb->dev = serial->dev;
/* usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
usb_pipeout(urb->pipe), 0); */
}
option_send_setup(port);
return 0;
......@@ -1218,7 +1210,6 @@ static int option_resume(struct usb_serial *serial)
dbg("%s: No interrupt URB for port %d\n", __func__, i);
continue;
}
port->interrupt_in_urb->dev = serial->dev;
err = usb_submit_urb(port->interrupt_in_urb, GFP_NOIO);
dbg("Submitted interrupt URB for port %d (result %d)", i, err);
if (err < 0) {
......
......@@ -94,6 +94,7 @@ static struct usb_device_id id_table [] = {
{ USB_DEVICE(YCCABLE_VENDOR_ID, YCCABLE_PRODUCT_ID) },
{ USB_DEVICE(SUPERIAL_VENDOR_ID, SUPERIAL_PRODUCT_ID) },
{ USB_DEVICE(HP_VENDOR_ID, HP_LD220_PRODUCT_ID) },
{ USB_DEVICE(CRESSI_VENDOR_ID, CRESSI_EDY_PRODUCT_ID) },
{ } /* Terminating entry */
};
......
......@@ -122,3 +122,7 @@
/* Hewlett-Packard LD220-HP POS Pole Display */
#define HP_VENDOR_ID 0x03f0
#define HP_LD220_PRODUCT_ID 0x3524
/* Cressi Edy (diving computer) PC interface */
#define CRESSI_VENDOR_ID 0x04b8
#define CRESSI_EDY_PRODUCT_ID 0x0521
......@@ -181,35 +181,50 @@ static const struct sierra_iface_info direct_ip_interface_blacklist = {
};
static struct usb_device_id id_table [] = {
{ USB_DEVICE(0x0F3D, 0x0112) }, /* Airprime/Sierra PC 5220 */
{ USB_DEVICE(0x03F0, 0x1B1D) }, /* HP ev2200 a.k.a MC5720 */
{ USB_DEVICE(0x03F0, 0x1E1D) }, /* HP hs2300 a.k.a MC8775 */
{ USB_DEVICE(0x1199, 0x0017) }, /* Sierra Wireless EM5625 */
{ USB_DEVICE(0x1199, 0x0018) }, /* Sierra Wireless MC5720 */
{ USB_DEVICE(0x1199, 0x0218) }, /* Sierra Wireless MC5720 */
{ USB_DEVICE(0x03f0, 0x1b1d) }, /* HP ev2200 a.k.a 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, 0x0022) }, /* Sierra Wireless EM5725 */
{ USB_DEVICE(0x1199, 0x0024) }, /* Sierra Wireless MC5727 */
{ USB_DEVICE(0x1199, 0x0224) }, /* Sierra Wireless MC5727 */
{ USB_DEVICE(0x1199, 0x0019) }, /* Sierra Wireless AirCard 595 */
{ USB_DEVICE(0x1199, 0x0021) }, /* Sierra Wireless AirCard 597E */
{ USB_DEVICE(0x1199, 0x0112) }, /* Sierra Wireless AirCard 580 */
{ USB_DEVICE(0x1199, 0x0120) }, /* Sierra Wireless USB Dongle 595U */
/* Sierra Wireless C597 */
/* Sierra Wireless C597 */
{ USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x0023, 0xFF, 0xFF, 0xFF) },
/* Sierra Wireless Device */
/* Sierra Wireless T598 */
{ USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x0025, 0xFF, 0xFF, 0xFF) },
{ USB_DEVICE(0x1199, 0x0026) }, /* Sierra Wireless Device */
{ USB_DEVICE(0x1199, 0x0027) }, /* Sierra Wireless Device */
{ USB_DEVICE(0x1199, 0x0028) }, /* Sierra Wireless Device */
{ USB_DEVICE(0x1199, 0x0026) }, /* Sierra Wireless T11 */
{ USB_DEVICE(0x1199, 0x0027) }, /* Sierra Wireless AC402 */
{ USB_DEVICE(0x1199, 0x0028) }, /* Sierra Wireless MC5728 */
{ USB_DEVICE(0x1199, 0x0029) }, /* 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, 0x6804) }, /* Sierra Wireless MC8755 */
{ USB_DEVICE(0x1199, 0x6805) }, /* Sierra Wireless MC8765 */
{ USB_DEVICE(0x1199, 0x6808) }, /* Sierra Wireless MC8755 */
{ USB_DEVICE(0x1199, 0x6809) }, /* Sierra Wireless MC8765 */
{ USB_DEVICE(0x1199, 0x6812) }, /* Sierra Wireless MC8775 & AC 875U */
{ USB_DEVICE(0x1199, 0x6813) }, /* Sierra Wireless MC8775 (Lenovo) */
{ USB_DEVICE(0x1199, 0x6813) }, /* Sierra Wireless MC8775 */
{ USB_DEVICE(0x1199, 0x6815) }, /* Sierra Wireless MC8775 */
{ USB_DEVICE(0x03f0, 0x1e1d) }, /* HP hs2300 a.k.a MC8775 */
{ USB_DEVICE(0x1199, 0x6816) }, /* Sierra Wireless MC8775 */
{ USB_DEVICE(0x1199, 0x6820) }, /* Sierra Wireless AirCard 875 */
{ USB_DEVICE(0x1199, 0x6821) }, /* Sierra Wireless AirCard 875U */
{ USB_DEVICE(0x1199, 0x6822) }, /* Sierra Wireless AirCard 875E */
{ USB_DEVICE(0x1199, 0x6832) }, /* Sierra Wireless MC8780 */
{ USB_DEVICE(0x1199, 0x6833) }, /* Sierra Wireless MC8781 */
{ USB_DEVICE(0x1199, 0x6834) }, /* Sierra Wireless MC8780 */
{ USB_DEVICE(0x1199, 0x6835) }, /* Sierra Wireless MC8781 */
{ USB_DEVICE(0x1199, 0x6838) }, /* Sierra Wireless MC8780 */
{ USB_DEVICE(0x1199, 0x6839) }, /* Sierra Wireless MC8781 */
{ USB_DEVICE(0x1199, 0x683A) }, /* Sierra Wireless MC8785 */
{ USB_DEVICE(0x1199, 0x683B) }, /* Sierra Wireless MC8785 Composite */
/* Sierra Wireless MC8790, MC8791, MC8792 Composite */
......@@ -227,16 +242,13 @@ static struct usb_device_id id_table [] = {
{ 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 */
/* Sierra Wireless C888, Air Card 501, USB 303, USB 304 */
{ USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x6890, 0xFF, 0xFF, 0xFF)},
/* Sierra Wireless Device */
/* Sierra Wireless C22/C33 */
{ USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x6891, 0xFF, 0xFF, 0xFF)},
/* Sierra Wireless Device */
/* Sierra Wireless HSPA Non-Composite Device */
{ USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x6892, 0xFF, 0xFF, 0xFF)},
{ USB_DEVICE(0x1199, 0x0112) }, /* Sierra Wireless AirCard 580 */
{ USB_DEVICE(0x0F3D, 0x0112) }, /* Airprime/Sierra PC 5220 */
{ USB_DEVICE(0x1199, 0x6893) }, /* Sierra Wireless Device */
{ USB_DEVICE(0x1199, 0x68A3), /* Sierra Wireless Direct IP modems */
.driver_info = (kernel_ulong_t)&direct_ip_interface_blacklist
},
......@@ -814,7 +826,7 @@ static int sierra_startup(struct usb_serial *serial)
return 0;
}
static void sierra_disconnect(struct usb_serial *serial)
static void sierra_release(struct usb_serial *serial)
{
int i;
struct usb_serial_port *port;
......@@ -830,7 +842,6 @@ static void sierra_disconnect(struct usb_serial *serial)
if (!portdata)
continue;
kfree(portdata);
usb_set_serial_port_data(port, NULL);
}
}
......@@ -853,7 +864,7 @@ static struct usb_serial_driver sierra_device = {
.tiocmget = sierra_tiocmget,
.tiocmset = sierra_tiocmset,
.attach = sierra_startup,
.disconnect = sierra_disconnect,
.release = sierra_release,
.read_int_callback = sierra_instat_callback,
};
......
......@@ -191,7 +191,6 @@ static struct usb_device_id ti_id_table_5052[5+TI_EXTRA_VID_PID_COUNT+1] = {
{ USB_DEVICE(TI_VENDOR_ID, TI_5152_BOOT_PRODUCT_ID) },
{ USB_DEVICE(TI_VENDOR_ID, TI_5052_EEPROM_PRODUCT_ID) },
{ USB_DEVICE(TI_VENDOR_ID, TI_5052_FIRMWARE_PRODUCT_ID) },
{ USB_DEVICE(IBM_VENDOR_ID, IBM_4543_PRODUCT_ID) },
};
static struct usb_device_id ti_id_table_combined[14+2*TI_EXTRA_VID_PID_COUNT+1] = {
......@@ -1658,7 +1657,7 @@ static int ti_do_download(struct usb_device *dev, int pipe,
u8 cs = 0;
int done;
struct ti_firmware_header *header;
int status;
int status = 0;
int len;
for (pos = sizeof(struct ti_firmware_header); pos < size; pos++)
......
......@@ -221,7 +221,8 @@ static int serial_open (struct tty_struct *tty, struct file *filp)
tty->driver_data = port;
tty_port_tty_set(&port->port, tty);
if (port->port.count == 1) {
/* If the console is attached, the device is already open */
if (port->port.count == 1 && !port->console) {
/* lock this module before we call it
* this may fail, which means we must bail out,
......
......@@ -118,6 +118,9 @@ static int option_inquiry(struct us_data *us)
result = memcmp(buffer+8, "Option", 6);
if (result != 0)
result = memcmp(buffer+8, "ZCOPTION", 8);
/* Read the CSW */
usb_stor_bulk_transfer_buf(us,
us->recv_bulk_pipe,
......
......@@ -888,8 +888,6 @@ struct usb_driver {
* struct usb_device_driver - identifies USB device driver to usbcore
* @name: The driver name should be unique among USB drivers,
* and should normally be the same as the module name.
* @nodename: Callback to provide a naming hint for a possible
* device node to create.
* @probe: Called to see if the driver is willing to manage a particular
* device. If it is, probe returns zero and uses dev_set_drvdata()
* to associate driver-specific data with the device. If unwilling
......@@ -924,6 +922,8 @@ extern struct bus_type usb_bus_type;
/**
* struct usb_class_driver - identifies a USB driver that wants to use the USB major number
* @name: the usb class device name for this driver. Will show up in sysfs.
* @nodename: Callback to provide a naming hint for a possible
* device node to create.
* @fops: pointer to the struct file_operations of this driver.
* @minor_base: the start of the minor range for this driver.
*
......@@ -1046,6 +1046,8 @@ typedef void (*usb_complete_t)(struct urb *);
* the device driver is saying that it provided this DMA address,
* which the host controller driver should use in preference to the
* transfer_buffer.
* @sg: scatter gather buffer list
* @num_sgs: number of entries in the sg list
* @transfer_buffer_length: How big is transfer_buffer. The transfer may
* be broken up into chunks according to the current maximum packet
* size for the endpoint, which is a function of the configuration
......
/*
* Intel Langwell USB OTG transceiver driver
* Copyright (C) 2008, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#ifndef __LANGWELL_OTG_H__
#define __LANGWELL_OTG_H__
/* notify transceiver driver about OTG events */
extern void langwell_update_transceiver(void);
/* HCD register bus driver */
extern int langwell_register_host(struct pci_driver *host_driver);
/* HCD unregister bus driver */
extern void langwell_unregister_host(struct pci_driver *host_driver);
/* DCD register bus driver */
extern int langwell_register_peripheral(struct pci_driver *client_driver);
/* DCD unregister bus driver */
extern void langwell_unregister_peripheral(struct pci_driver *client_driver);
/* No silent failure, output warning message */
extern void langwell_otg_nsf_msg(unsigned long message);
#define CI_USBCMD 0x30
# define USBCMD_RST BIT(1)
# define USBCMD_RS BIT(0)
#define CI_USBSTS 0x34
# define USBSTS_SLI BIT(8)
# define USBSTS_URI BIT(6)
# define USBSTS_PCI BIT(2)
#define CI_PORTSC1 0x74
# define PORTSC_PP BIT(12)
# define PORTSC_LS (BIT(11) | BIT(10))
# define PORTSC_SUSP BIT(7)
# define PORTSC_CCS BIT(0)
#define CI_HOSTPC1 0xb4
# define HOSTPC1_PHCD BIT(22)
#define CI_OTGSC 0xf4
# define OTGSC_DPIE BIT(30)
# define OTGSC_1MSE BIT(29)
# define OTGSC_BSEIE BIT(28)
# define OTGSC_BSVIE BIT(27)
# define OTGSC_ASVIE BIT(26)
# define OTGSC_AVVIE BIT(25)
# define OTGSC_IDIE BIT(24)
# define OTGSC_DPIS BIT(22)
# define OTGSC_1MSS BIT(21)
# define OTGSC_BSEIS BIT(20)
# define OTGSC_BSVIS BIT(19)
# define OTGSC_ASVIS BIT(18)
# define OTGSC_AVVIS BIT(17)
# define OTGSC_IDIS BIT(16)
# define OTGSC_DPS BIT(14)
# define OTGSC_1MST BIT(13)
# define OTGSC_BSE BIT(12)
# define OTGSC_BSV BIT(11)
# define OTGSC_ASV BIT(10)
# define OTGSC_AVV BIT(9)
# define OTGSC_ID BIT(8)
# define OTGSC_HABA BIT(7)
# define OTGSC_HADP BIT(6)
# define OTGSC_IDPU BIT(5)
# define OTGSC_DP BIT(4)
# define OTGSC_OT BIT(3)
# define OTGSC_HAAR BIT(2)
# define OTGSC_VC BIT(1)
# define OTGSC_VD BIT(0)
# define OTGSC_INTEN_MASK (0x7f << 24)
# define OTGSC_INTSTS_MASK (0x7f << 16)
#define CI_USBMODE 0xf8
# define USBMODE_CM (BIT(1) | BIT(0))
# define USBMODE_IDLE 0
# define USBMODE_DEVICE 0x2
# define USBMODE_HOST 0x3
#define INTR_DUMMY_MASK (USBSTS_SLI | USBSTS_URI | USBSTS_PCI)
struct otg_hsm {
/* Input */
int a_bus_resume;
int a_bus_suspend;
int a_conn;
int a_sess_vld;
int a_srp_det;
int a_vbus_vld;
int b_bus_resume;
int b_bus_suspend;
int b_conn;
int b_se0_srp;
int b_sess_end;
int b_sess_vld;
int id;
/* Internal variables */
int a_set_b_hnp_en;
int b_srp_done;
int b_hnp_enable;
/* Timeout indicator for timers */
int a_wait_vrise_tmout;
int a_wait_bcon_tmout;
int a_aidl_bdis_tmout;
int b_ase0_brst_tmout;
int b_bus_suspend_tmout;
int b_srp_res_tmout;
/* Informative variables */
int a_bus_drop;
int a_bus_req;
int a_clr_err;
int a_suspend_req;
int b_bus_req;
/* Output */
int drv_vbus;
int loc_conn;
int loc_sof;
/* Others */
int b_bus_suspend_vld;
};
#define TA_WAIT_VRISE 100
#define TA_WAIT_BCON 30000
#define TA_AIDL_BDIS 15000
#define TB_ASE0_BRST 5000
#define TB_SE0_SRP 2
#define TB_SRP_RES 100
#define TB_BUS_SUSPEND 500
struct langwell_otg_timer {
unsigned long expires; /* Number of count increase to timeout */
unsigned long count; /* Tick counter */
void (*function)(unsigned long); /* Timeout function */
unsigned long data; /* Data passed to function */
struct list_head list;
};
struct langwell_otg {
struct otg_transceiver otg;
struct otg_hsm hsm;
void __iomem *regs;
unsigned region;
struct pci_driver *host_ops;
struct pci_driver *client_ops;
struct pci_dev *pdev;
struct work_struct work;
struct workqueue_struct *qwork;
spinlock_t lock;
spinlock_t wq_lock;
};
static inline struct langwell_otg *otg_to_langwell(struct otg_transceiver *otg)
{
return container_of(otg, struct langwell_otg, otg);
}
#ifdef DEBUG
#define otg_dbg(fmt, args...) \
printk(KERN_DEBUG fmt , ## args)
#else
#define otg_dbg(fmt, args...) \
do { } while (0)
#endif /* DEBUG */
#endif /* __LANGWELL_OTG_H__ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment