mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-29 11:11:45 -04:00
Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6
This commit is contained in:
commit
7f46b1343f
2400 changed files with 559938 additions and 49536 deletions
|
|
@ -150,4 +150,6 @@ source "drivers/usb/atm/Kconfig"
|
|||
|
||||
source "drivers/usb/gadget/Kconfig"
|
||||
|
||||
source "drivers/usb/otg/Kconfig"
|
||||
|
||||
endif # USB_SUPPORT
|
||||
|
|
|
|||
|
|
@ -1275,7 +1275,7 @@ static int acm_suspend(struct usb_interface *intf, pm_message_t message)
|
|||
struct acm *acm = usb_get_intfdata(intf);
|
||||
int cnt;
|
||||
|
||||
if (acm->dev->auto_pm) {
|
||||
if (message.event & PM_EVENT_AUTO) {
|
||||
int b;
|
||||
|
||||
spin_lock_irq(&acm->read_lock);
|
||||
|
|
|
|||
|
|
@ -764,7 +764,8 @@ static int wdm_suspend(struct usb_interface *intf, pm_message_t message)
|
|||
|
||||
mutex_lock(&desc->plock);
|
||||
#ifdef CONFIG_PM
|
||||
if (interface_to_usbdev(desc->intf)->auto_pm && test_bit(WDM_IN_USE, &desc->flags)) {
|
||||
if ((message.event & PM_EVENT_AUTO) &&
|
||||
test_bit(WDM_IN_USE, &desc->flags)) {
|
||||
rv = -EBUSY;
|
||||
} else {
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/kref.h>
|
||||
|
|
@ -482,7 +483,6 @@ static ssize_t usbtmc_write(struct file *filp, const char __user *buf,
|
|||
int retval;
|
||||
int actual;
|
||||
unsigned long int n_bytes;
|
||||
int n;
|
||||
int remaining;
|
||||
int done;
|
||||
int this_part;
|
||||
|
|
@ -526,11 +526,8 @@ static ssize_t usbtmc_write(struct file *filp, const char __user *buf,
|
|||
goto exit;
|
||||
}
|
||||
|
||||
n_bytes = 12 + this_part;
|
||||
if (this_part % 4)
|
||||
n_bytes += 4 - this_part % 4;
|
||||
for (n = 12 + this_part; n < n_bytes; n++)
|
||||
buffer[n] = 0;
|
||||
n_bytes = roundup(12 + this_part, 4);
|
||||
memset(buffer + 12 + this_part, 0, n_bytes - (12 + this_part));
|
||||
|
||||
retval = usb_bulk_msg(data->usb_dev,
|
||||
usb_sndbulkpipe(data->usb_dev,
|
||||
|
|
|
|||
|
|
@ -981,9 +981,6 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
|
|||
return -EINVAL;
|
||||
if (!uurb->buffer)
|
||||
return -EINVAL;
|
||||
if (uurb->signr != 0 && (uurb->signr < SIGRTMIN ||
|
||||
uurb->signr > SIGRTMAX))
|
||||
return -EINVAL;
|
||||
if (!(uurb->type == USBDEVFS_URB_TYPE_CONTROL &&
|
||||
(uurb->endpoint & ~USB_ENDPOINT_DIR_MASK) == 0)) {
|
||||
ifnum = findintfep(ps->dev, uurb->endpoint);
|
||||
|
|
@ -1320,7 +1317,7 @@ static int get_urb32(struct usbdevfs_urb *kurb,
|
|||
if (__get_user(uptr, &uurb->buffer))
|
||||
return -EFAULT;
|
||||
kurb->buffer = compat_ptr(uptr);
|
||||
if (__get_user(uptr, &uurb->buffer))
|
||||
if (__get_user(uptr, &uurb->usercontext))
|
||||
return -EFAULT;
|
||||
kurb->usercontext = compat_ptr(uptr);
|
||||
|
||||
|
|
@ -1401,8 +1398,6 @@ static int proc_disconnectsignal(struct dev_state *ps, void __user *arg)
|
|||
|
||||
if (copy_from_user(&ds, arg, sizeof(ds)))
|
||||
return -EFAULT;
|
||||
if (ds.signr != 0 && (ds.signr < SIGRTMIN || ds.signr > SIGRTMAX))
|
||||
return -EINVAL;
|
||||
ps->discsignr = ds.signr;
|
||||
ps->disccontext = ds.context;
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -184,6 +184,20 @@ static int usb_unbind_device(struct device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Cancel any pending scheduled resets
|
||||
*
|
||||
* [see usb_queue_reset_device()]
|
||||
*
|
||||
* Called after unconfiguring / when releasing interfaces. See
|
||||
* comments in __usb_queue_reset_device() regarding
|
||||
* udev->reset_running.
|
||||
*/
|
||||
static void usb_cancel_queued_reset(struct usb_interface *iface)
|
||||
{
|
||||
if (iface->reset_running == 0)
|
||||
cancel_work_sync(&iface->reset_ws);
|
||||
}
|
||||
|
||||
/* called from driver core with dev locked */
|
||||
static int usb_probe_interface(struct device *dev)
|
||||
|
|
@ -242,6 +256,7 @@ static int usb_probe_interface(struct device *dev)
|
|||
mark_quiesced(intf);
|
||||
intf->needs_remote_wakeup = 0;
|
||||
intf->condition = USB_INTERFACE_UNBOUND;
|
||||
usb_cancel_queued_reset(intf);
|
||||
} else
|
||||
intf->condition = USB_INTERFACE_BOUND;
|
||||
|
||||
|
|
@ -272,6 +287,7 @@ static int usb_unbind_interface(struct device *dev)
|
|||
usb_disable_interface(udev, intf);
|
||||
|
||||
driver->disconnect(intf);
|
||||
usb_cancel_queued_reset(intf);
|
||||
|
||||
/* Reset other interface state.
|
||||
* We cannot do a Set-Interface if the device is suspended or
|
||||
|
|
@ -279,9 +295,12 @@ static int usb_unbind_interface(struct device *dev)
|
|||
* altsetting means creating new endpoint device entries).
|
||||
* When either of these happens, defer the Set-Interface.
|
||||
*/
|
||||
if (intf->cur_altsetting->desc.bAlternateSetting == 0)
|
||||
; /* Already in altsetting 0 so skip Set-Interface */
|
||||
else if (!error && intf->dev.power.status == DPM_ON)
|
||||
if (intf->cur_altsetting->desc.bAlternateSetting == 0) {
|
||||
/* Already in altsetting 0 so skip Set-Interface.
|
||||
* Just re-enable it without affecting the endpoint toggles.
|
||||
*/
|
||||
usb_enable_interface(udev, intf, false);
|
||||
} else if (!error && intf->dev.power.status == DPM_ON)
|
||||
usb_set_interface(udev, intf->altsetting[0].
|
||||
desc.bInterfaceNumber, 0);
|
||||
else
|
||||
|
|
@ -380,8 +399,10 @@ void usb_driver_release_interface(struct usb_driver *driver,
|
|||
if (device_is_registered(dev)) {
|
||||
iface->condition = USB_INTERFACE_UNBINDING;
|
||||
device_release_driver(dev);
|
||||
} else {
|
||||
iface->condition = USB_INTERFACE_UNBOUND;
|
||||
usb_cancel_queued_reset(iface);
|
||||
}
|
||||
|
||||
dev->driver = NULL;
|
||||
usb_set_intfdata(iface, NULL);
|
||||
|
||||
|
|
@ -904,7 +925,7 @@ static int usb_suspend_device(struct usb_device *udev, pm_message_t msg)
|
|||
}
|
||||
|
||||
/* Caller has locked udev's pm_mutex */
|
||||
static int usb_resume_device(struct usb_device *udev)
|
||||
static int usb_resume_device(struct usb_device *udev, pm_message_t msg)
|
||||
{
|
||||
struct usb_device_driver *udriver;
|
||||
int status = 0;
|
||||
|
|
@ -922,7 +943,7 @@ static int usb_resume_device(struct usb_device *udev)
|
|||
udev->reset_resume = 1;
|
||||
|
||||
udriver = to_usb_device_driver(udev->dev.driver);
|
||||
status = udriver->resume(udev);
|
||||
status = udriver->resume(udev, msg);
|
||||
|
||||
done:
|
||||
dev_vdbg(&udev->dev, "%s: status %d\n", __func__, status);
|
||||
|
|
@ -942,7 +963,8 @@ static int usb_suspend_interface(struct usb_device *udev,
|
|||
if (udev->state == USB_STATE_NOTATTACHED || !is_active(intf))
|
||||
goto done;
|
||||
|
||||
if (intf->condition == USB_INTERFACE_UNBOUND) /* This can't happen */
|
||||
/* This can happen; see usb_driver_release_interface() */
|
||||
if (intf->condition == USB_INTERFACE_UNBOUND)
|
||||
goto done;
|
||||
driver = to_usb_driver(intf->dev.driver);
|
||||
|
||||
|
|
@ -950,7 +972,7 @@ static int usb_suspend_interface(struct usb_device *udev,
|
|||
status = driver->suspend(intf, msg);
|
||||
if (status == 0)
|
||||
mark_quiesced(intf);
|
||||
else if (!udev->auto_pm)
|
||||
else if (!(msg.event & PM_EVENT_AUTO))
|
||||
dev_err(&intf->dev, "%s error %d\n",
|
||||
"suspend", status);
|
||||
} else {
|
||||
|
|
@ -968,7 +990,7 @@ static int usb_suspend_interface(struct usb_device *udev,
|
|||
|
||||
/* Caller has locked intf's usb_device's pm_mutex */
|
||||
static int usb_resume_interface(struct usb_device *udev,
|
||||
struct usb_interface *intf, int reset_resume)
|
||||
struct usb_interface *intf, pm_message_t msg, int reset_resume)
|
||||
{
|
||||
struct usb_driver *driver;
|
||||
int status = 0;
|
||||
|
|
@ -1092,7 +1114,7 @@ static int autosuspend_check(struct usb_device *udev, int reschedule)
|
|||
if (reschedule) {
|
||||
if (!timer_pending(&udev->autosuspend.timer)) {
|
||||
queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend,
|
||||
round_jiffies_relative(suspend_time - j));
|
||||
round_jiffies_up_relative(suspend_time - j));
|
||||
}
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
|
@ -1119,10 +1141,9 @@ static inline int autosuspend_check(struct usb_device *udev, int reschedule)
|
|||
* all the interfaces which were suspended are resumed so that they remain
|
||||
* in the same state as the device.
|
||||
*
|
||||
* If an autosuspend is in progress (@udev->auto_pm is set), the routine
|
||||
* checks first to make sure that neither the device itself or any of its
|
||||
* active interfaces is in use (pm_usage_cnt is greater than 0). If they
|
||||
* are, the autosuspend fails.
|
||||
* If an autosuspend is in progress the routine checks first to make sure
|
||||
* that neither the device itself or any of its active interfaces is in use
|
||||
* (pm_usage_cnt is greater than 0). If they are, the autosuspend fails.
|
||||
*
|
||||
* If the suspend succeeds, the routine recursively queues an autosuspend
|
||||
* request for @udev's parent device, thereby propagating the change up
|
||||
|
|
@ -1157,7 +1178,7 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
|
|||
|
||||
udev->do_remote_wakeup = device_may_wakeup(&udev->dev);
|
||||
|
||||
if (udev->auto_pm) {
|
||||
if (msg.event & PM_EVENT_AUTO) {
|
||||
status = autosuspend_check(udev, 0);
|
||||
if (status < 0)
|
||||
goto done;
|
||||
|
|
@ -1177,13 +1198,16 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
|
|||
|
||||
/* If the suspend failed, resume interfaces that did get suspended */
|
||||
if (status != 0) {
|
||||
pm_message_t msg2;
|
||||
|
||||
msg2.event = msg.event ^ (PM_EVENT_SUSPEND | PM_EVENT_RESUME);
|
||||
while (--i >= 0) {
|
||||
intf = udev->actconfig->interface[i];
|
||||
usb_resume_interface(udev, intf, 0);
|
||||
usb_resume_interface(udev, intf, msg2, 0);
|
||||
}
|
||||
|
||||
/* Try another autosuspend when the interfaces aren't busy */
|
||||
if (udev->auto_pm)
|
||||
if (msg.event & PM_EVENT_AUTO)
|
||||
autosuspend_check(udev, status == -EBUSY);
|
||||
|
||||
/* If the suspend succeeded then prevent any more URB submissions,
|
||||
|
|
@ -1213,6 +1237,7 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
|
|||
/**
|
||||
* usb_resume_both - resume a USB device and its interfaces
|
||||
* @udev: the usb_device to resume
|
||||
* @msg: Power Management message describing this state transition
|
||||
*
|
||||
* This is the central routine for resuming USB devices. It calls the
|
||||
* the resume method for @udev and then calls the resume methods for all
|
||||
|
|
@ -1238,7 +1263,7 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
|
|||
*
|
||||
* This routine can run only in process context.
|
||||
*/
|
||||
static int usb_resume_both(struct usb_device *udev)
|
||||
static int usb_resume_both(struct usb_device *udev, pm_message_t msg)
|
||||
{
|
||||
int status = 0;
|
||||
int i;
|
||||
|
|
@ -1254,14 +1279,15 @@ static int usb_resume_both(struct usb_device *udev)
|
|||
|
||||
/* Propagate the resume up the tree, if necessary */
|
||||
if (udev->state == USB_STATE_SUSPENDED) {
|
||||
if (udev->auto_pm && udev->autoresume_disabled) {
|
||||
if ((msg.event & PM_EVENT_AUTO) &&
|
||||
udev->autoresume_disabled) {
|
||||
status = -EPERM;
|
||||
goto done;
|
||||
}
|
||||
if (parent) {
|
||||
status = usb_autoresume_device(parent);
|
||||
if (status == 0) {
|
||||
status = usb_resume_device(udev);
|
||||
status = usb_resume_device(udev, msg);
|
||||
if (status || udev->state ==
|
||||
USB_STATE_NOTATTACHED) {
|
||||
usb_autosuspend_device(parent);
|
||||
|
|
@ -1284,15 +1310,16 @@ static int usb_resume_both(struct usb_device *udev)
|
|||
/* We can't progagate beyond the USB subsystem,
|
||||
* so if a root hub's controller is suspended
|
||||
* then we're stuck. */
|
||||
status = usb_resume_device(udev);
|
||||
status = usb_resume_device(udev, msg);
|
||||
}
|
||||
} else if (udev->reset_resume)
|
||||
status = usb_resume_device(udev);
|
||||
status = usb_resume_device(udev, msg);
|
||||
|
||||
if (status == 0 && udev->actconfig) {
|
||||
for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
|
||||
intf = udev->actconfig->interface[i];
|
||||
usb_resume_interface(udev, intf, udev->reset_resume);
|
||||
usb_resume_interface(udev, intf, msg,
|
||||
udev->reset_resume);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1320,13 +1347,13 @@ static int usb_autopm_do_device(struct usb_device *udev, int inc_usage_cnt)
|
|||
udev->last_busy = jiffies;
|
||||
if (inc_usage_cnt >= 0 && udev->pm_usage_cnt > 0) {
|
||||
if (udev->state == USB_STATE_SUSPENDED)
|
||||
status = usb_resume_both(udev);
|
||||
status = usb_resume_both(udev, PMSG_AUTO_RESUME);
|
||||
if (status != 0)
|
||||
udev->pm_usage_cnt -= inc_usage_cnt;
|
||||
else if (inc_usage_cnt)
|
||||
udev->last_busy = jiffies;
|
||||
} else if (inc_usage_cnt <= 0 && udev->pm_usage_cnt <= 0) {
|
||||
status = usb_suspend_both(udev, PMSG_SUSPEND);
|
||||
status = usb_suspend_both(udev, PMSG_AUTO_SUSPEND);
|
||||
}
|
||||
usb_pm_unlock(udev);
|
||||
return status;
|
||||
|
|
@ -1341,6 +1368,19 @@ void usb_autosuspend_work(struct work_struct *work)
|
|||
usb_autopm_do_device(udev, 0);
|
||||
}
|
||||
|
||||
/* usb_autoresume_work - callback routine to autoresume a USB device */
|
||||
void usb_autoresume_work(struct work_struct *work)
|
||||
{
|
||||
struct usb_device *udev =
|
||||
container_of(work, struct usb_device, autoresume);
|
||||
|
||||
/* Wake it up, let the drivers do their thing, and then put it
|
||||
* back to sleep.
|
||||
*/
|
||||
if (usb_autopm_do_device(udev, 1) == 0)
|
||||
usb_autopm_do_device(udev, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* usb_autosuspend_device - delayed autosuspend of a USB device and its interfaces
|
||||
* @udev: the usb_device to autosuspend
|
||||
|
|
@ -1437,13 +1477,14 @@ static int usb_autopm_do_interface(struct usb_interface *intf,
|
|||
udev->last_busy = jiffies;
|
||||
if (inc_usage_cnt >= 0 && intf->pm_usage_cnt > 0) {
|
||||
if (udev->state == USB_STATE_SUSPENDED)
|
||||
status = usb_resume_both(udev);
|
||||
status = usb_resume_both(udev,
|
||||
PMSG_AUTO_RESUME);
|
||||
if (status != 0)
|
||||
intf->pm_usage_cnt -= inc_usage_cnt;
|
||||
else
|
||||
udev->last_busy = jiffies;
|
||||
} else if (inc_usage_cnt <= 0 && intf->pm_usage_cnt <= 0) {
|
||||
status = usb_suspend_both(udev, PMSG_SUSPEND);
|
||||
status = usb_suspend_both(udev, PMSG_AUTO_SUSPEND);
|
||||
}
|
||||
}
|
||||
usb_pm_unlock(udev);
|
||||
|
|
@ -1491,6 +1532,45 @@ void usb_autopm_put_interface(struct usb_interface *intf)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(usb_autopm_put_interface);
|
||||
|
||||
/**
|
||||
* usb_autopm_put_interface_async - decrement a USB interface's PM-usage counter
|
||||
* @intf: the usb_interface whose counter should be decremented
|
||||
*
|
||||
* This routine does essentially the same thing as
|
||||
* usb_autopm_put_interface(): it decrements @intf's usage counter and
|
||||
* queues a delayed autosuspend request if the counter is <= 0. The
|
||||
* difference is that it does not acquire the device's pm_mutex;
|
||||
* callers must handle all synchronization issues themselves.
|
||||
*
|
||||
* Typically a driver would call this routine during an URB's completion
|
||||
* handler, if no more URBs were pending.
|
||||
*
|
||||
* This routine can run in atomic context.
|
||||
*/
|
||||
void usb_autopm_put_interface_async(struct usb_interface *intf)
|
||||
{
|
||||
struct usb_device *udev = interface_to_usbdev(intf);
|
||||
int status = 0;
|
||||
|
||||
if (intf->condition == USB_INTERFACE_UNBOUND) {
|
||||
status = -ENODEV;
|
||||
} else {
|
||||
udev->last_busy = jiffies;
|
||||
--intf->pm_usage_cnt;
|
||||
if (udev->autosuspend_disabled || udev->autosuspend_delay < 0)
|
||||
status = -EPERM;
|
||||
else if (intf->pm_usage_cnt <= 0 &&
|
||||
!timer_pending(&udev->autosuspend.timer)) {
|
||||
queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend,
|
||||
round_jiffies_up_relative(
|
||||
udev->autosuspend_delay));
|
||||
}
|
||||
}
|
||||
dev_vdbg(&intf->dev, "%s: status %d cnt %d\n",
|
||||
__func__, status, intf->pm_usage_cnt);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_autopm_put_interface_async);
|
||||
|
||||
/**
|
||||
* usb_autopm_get_interface - increment a USB interface's PM-usage counter
|
||||
* @intf: the usb_interface whose counter should be incremented
|
||||
|
|
@ -1536,6 +1616,37 @@ int usb_autopm_get_interface(struct usb_interface *intf)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(usb_autopm_get_interface);
|
||||
|
||||
/**
|
||||
* usb_autopm_get_interface_async - increment a USB interface's PM-usage counter
|
||||
* @intf: the usb_interface whose counter should be incremented
|
||||
*
|
||||
* This routine does much the same thing as
|
||||
* usb_autopm_get_interface(): it increments @intf's usage counter and
|
||||
* queues an autoresume request if the result is > 0. The differences
|
||||
* are that it does not acquire the device's pm_mutex (callers must
|
||||
* handle all synchronization issues themselves), and it does not
|
||||
* autoresume the device directly (it only queues a request). After a
|
||||
* successful call, the device will generally not yet be resumed.
|
||||
*
|
||||
* This routine can run in atomic context.
|
||||
*/
|
||||
int usb_autopm_get_interface_async(struct usb_interface *intf)
|
||||
{
|
||||
struct usb_device *udev = interface_to_usbdev(intf);
|
||||
int status = 0;
|
||||
|
||||
if (intf->condition == USB_INTERFACE_UNBOUND)
|
||||
status = -ENODEV;
|
||||
else if (udev->autoresume_disabled)
|
||||
status = -EPERM;
|
||||
else if (++intf->pm_usage_cnt > 0 && udev->state == USB_STATE_SUSPENDED)
|
||||
queue_work(ksuspend_usb_wq, &udev->autoresume);
|
||||
dev_vdbg(&intf->dev, "%s: status %d cnt %d\n",
|
||||
__func__, status, intf->pm_usage_cnt);
|
||||
return status;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_autopm_get_interface_async);
|
||||
|
||||
/**
|
||||
* usb_autopm_set_interface - set a USB interface's autosuspend state
|
||||
* @intf: the usb_interface whose state should be set
|
||||
|
|
@ -1563,6 +1674,9 @@ EXPORT_SYMBOL_GPL(usb_autopm_set_interface);
|
|||
void usb_autosuspend_work(struct work_struct *work)
|
||||
{}
|
||||
|
||||
void usb_autoresume_work(struct work_struct *work)
|
||||
{}
|
||||
|
||||
#endif /* CONFIG_USB_SUSPEND */
|
||||
|
||||
/**
|
||||
|
|
@ -1595,6 +1709,7 @@ int usb_external_suspend_device(struct usb_device *udev, pm_message_t msg)
|
|||
/**
|
||||
* usb_external_resume_device - external resume of a USB device and its interfaces
|
||||
* @udev: the usb_device to resume
|
||||
* @msg: Power Management message describing this state transition
|
||||
*
|
||||
* This routine handles external resume requests: ones not generated
|
||||
* internally by a USB driver (autoresume) but rather coming from the user
|
||||
|
|
@ -1603,13 +1718,13 @@ int usb_external_suspend_device(struct usb_device *udev, pm_message_t msg)
|
|||
*
|
||||
* The caller must hold @udev's device lock.
|
||||
*/
|
||||
int usb_external_resume_device(struct usb_device *udev)
|
||||
int usb_external_resume_device(struct usb_device *udev, pm_message_t msg)
|
||||
{
|
||||
int status;
|
||||
|
||||
usb_pm_lock(udev);
|
||||
udev->auto_pm = 0;
|
||||
status = usb_resume_both(udev);
|
||||
status = usb_resume_both(udev, msg);
|
||||
udev->last_busy = jiffies;
|
||||
usb_pm_unlock(udev);
|
||||
if (status == 0)
|
||||
|
|
@ -1622,7 +1737,7 @@ int usb_external_resume_device(struct usb_device *udev)
|
|||
return status;
|
||||
}
|
||||
|
||||
int usb_suspend(struct device *dev, pm_message_t message)
|
||||
int usb_suspend(struct device *dev, pm_message_t msg)
|
||||
{
|
||||
struct usb_device *udev;
|
||||
|
||||
|
|
@ -1641,10 +1756,10 @@ int usb_suspend(struct device *dev, pm_message_t message)
|
|||
}
|
||||
|
||||
udev->skip_sys_resume = 0;
|
||||
return usb_external_suspend_device(udev, message);
|
||||
return usb_external_suspend_device(udev, msg);
|
||||
}
|
||||
|
||||
int usb_resume(struct device *dev)
|
||||
int usb_resume(struct device *dev, pm_message_t msg)
|
||||
{
|
||||
struct usb_device *udev;
|
||||
|
||||
|
|
@ -1656,7 +1771,7 @@ int usb_resume(struct device *dev)
|
|||
*/
|
||||
if (udev->skip_sys_resume)
|
||||
return 0;
|
||||
return usb_external_resume_device(udev);
|
||||
return usb_external_resume_device(udev, msg);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_PM */
|
||||
|
|
|
|||
|
|
@ -276,7 +276,7 @@ static void ep_device_release(struct device *dev)
|
|||
kfree(ep_dev);
|
||||
}
|
||||
|
||||
int usb_create_ep_files(struct device *parent,
|
||||
int usb_create_ep_devs(struct device *parent,
|
||||
struct usb_host_endpoint *endpoint,
|
||||
struct usb_device *udev)
|
||||
{
|
||||
|
|
@ -340,7 +340,7 @@ exit:
|
|||
return retval;
|
||||
}
|
||||
|
||||
void usb_remove_ep_files(struct usb_host_endpoint *endpoint)
|
||||
void usb_remove_ep_devs(struct usb_host_endpoint *endpoint)
|
||||
{
|
||||
struct ep_device *ep_dev = endpoint->ep_dev;
|
||||
|
||||
|
|
|
|||
|
|
@ -200,18 +200,18 @@ static int generic_suspend(struct usb_device *udev, pm_message_t msg)
|
|||
* interfaces manually by doing a bus (or "global") suspend.
|
||||
*/
|
||||
if (!udev->parent)
|
||||
rc = hcd_bus_suspend(udev);
|
||||
rc = hcd_bus_suspend(udev, msg);
|
||||
|
||||
/* Non-root devices don't need to do anything for FREEZE or PRETHAW */
|
||||
else if (msg.event == PM_EVENT_FREEZE || msg.event == PM_EVENT_PRETHAW)
|
||||
rc = 0;
|
||||
else
|
||||
rc = usb_port_suspend(udev);
|
||||
rc = usb_port_suspend(udev, msg);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int generic_resume(struct usb_device *udev)
|
||||
static int generic_resume(struct usb_device *udev, pm_message_t msg)
|
||||
{
|
||||
int rc;
|
||||
|
||||
|
|
@ -221,9 +221,9 @@ static int generic_resume(struct usb_device *udev)
|
|||
* interfaces manually by doing a bus (or "global") resume.
|
||||
*/
|
||||
if (!udev->parent)
|
||||
rc = hcd_bus_resume(udev);
|
||||
rc = hcd_bus_resume(udev, msg);
|
||||
else
|
||||
rc = usb_port_resume(udev);
|
||||
rc = usb_port_resume(udev, msg);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -128,6 +128,7 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
|||
}
|
||||
|
||||
pci_set_master(dev);
|
||||
device_set_wakeup_enable(&dev->dev, 1);
|
||||
|
||||
retval = usb_add_hcd(hcd, dev->irq, IRQF_DISABLED | IRQF_SHARED);
|
||||
if (retval != 0)
|
||||
|
|
@ -191,17 +192,15 @@ EXPORT_SYMBOL_GPL(usb_hcd_pci_remove);
|
|||
/**
|
||||
* usb_hcd_pci_suspend - power management suspend of a PCI-based HCD
|
||||
* @dev: USB Host Controller being suspended
|
||||
* @message: semantics in flux
|
||||
* @message: Power Management message describing this state transition
|
||||
*
|
||||
* Store this function in the HCD's struct pci_driver as suspend().
|
||||
* Store this function in the HCD's struct pci_driver as .suspend.
|
||||
*/
|
||||
int usb_hcd_pci_suspend(struct pci_dev *dev, pm_message_t message)
|
||||
{
|
||||
struct usb_hcd *hcd;
|
||||
struct usb_hcd *hcd = pci_get_drvdata(dev);
|
||||
int retval = 0;
|
||||
int has_pci_pm;
|
||||
|
||||
hcd = pci_get_drvdata(dev);
|
||||
int wake, w;
|
||||
|
||||
/* Root hub suspend should have stopped all downstream traffic,
|
||||
* and all bus master traffic. And done so for both the interface
|
||||
|
|
@ -212,8 +211,15 @@ int usb_hcd_pci_suspend(struct pci_dev *dev, pm_message_t message)
|
|||
* otherwise the swsusp will save (and restore) garbage state.
|
||||
*/
|
||||
if (!(hcd->state == HC_STATE_SUSPENDED ||
|
||||
hcd->state == HC_STATE_HALT))
|
||||
return -EBUSY;
|
||||
hcd->state == HC_STATE_HALT)) {
|
||||
dev_warn(&dev->dev, "Root hub is not suspended\n");
|
||||
retval = -EBUSY;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* We might already be suspended (runtime PM -- not yet written) */
|
||||
if (dev->current_state != PCI_D0)
|
||||
goto done;
|
||||
|
||||
if (hcd->driver->pci_suspend) {
|
||||
retval = hcd->driver->pci_suspend(hcd, message);
|
||||
|
|
@ -221,49 +227,60 @@ int usb_hcd_pci_suspend(struct pci_dev *dev, pm_message_t message)
|
|||
if (retval)
|
||||
goto done;
|
||||
}
|
||||
|
||||
synchronize_irq(dev->irq);
|
||||
|
||||
/* FIXME until the generic PM interfaces change a lot more, this
|
||||
* can't use PCI D1 and D2 states. For example, the confusion
|
||||
* between messages and states will need to vanish, and messages
|
||||
* will need to provide a target system state again.
|
||||
*
|
||||
* It'll be important to learn characteristics of the target state,
|
||||
* especially on embedded hardware where the HCD will often be in
|
||||
* charge of an external VBUS power supply and one or more clocks.
|
||||
* Some target system states will leave them active; others won't.
|
||||
* (With PCI, that's often handled by platform BIOS code.)
|
||||
/* Don't fail on error to enable wakeup. We rely on pci code
|
||||
* to reject requests the hardware can't implement, rather
|
||||
* than coding the same thing.
|
||||
*/
|
||||
|
||||
/* even when the PCI layer rejects some of the PCI calls
|
||||
* below, HCs can try global suspend and reduce DMA traffic.
|
||||
* PM-sensitive HCDs may already have done this.
|
||||
*/
|
||||
has_pci_pm = pci_find_capability(dev, PCI_CAP_ID_PM);
|
||||
wake = (hcd->state == HC_STATE_SUSPENDED &&
|
||||
device_may_wakeup(&dev->dev));
|
||||
w = pci_wake_from_d3(dev, wake);
|
||||
if (w < 0)
|
||||
wake = w;
|
||||
dev_dbg(&dev->dev, "wakeup: %d\n", wake);
|
||||
|
||||
/* Downstream ports from this root hub should already be quiesced, so
|
||||
* there will be no DMA activity. Now we can shut down the upstream
|
||||
* link (except maybe for PME# resume signaling) and enter some PCI
|
||||
* low power state, if the hardware allows.
|
||||
*/
|
||||
if (hcd->state == HC_STATE_SUSPENDED) {
|
||||
pci_disable_device(dev);
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_hcd_pci_suspend);
|
||||
|
||||
/* no DMA or IRQs except when HC is active */
|
||||
if (dev->current_state == PCI_D0) {
|
||||
pci_save_state(dev);
|
||||
pci_disable_device(dev);
|
||||
}
|
||||
/**
|
||||
* usb_hcd_pci_suspend_late - suspend a PCI-based HCD after IRQs are disabled
|
||||
* @dev: USB Host Controller being suspended
|
||||
* @message: Power Management message describing this state transition
|
||||
*
|
||||
* Store this function in the HCD's struct pci_driver as .suspend_late.
|
||||
*/
|
||||
int usb_hcd_pci_suspend_late(struct pci_dev *dev, pm_message_t message)
|
||||
{
|
||||
int retval = 0;
|
||||
int has_pci_pm;
|
||||
|
||||
if (message.event == PM_EVENT_FREEZE ||
|
||||
message.event == PM_EVENT_PRETHAW) {
|
||||
dev_dbg(hcd->self.controller, "--> no state change\n");
|
||||
goto done;
|
||||
}
|
||||
/* We might already be suspended (runtime PM -- not yet written) */
|
||||
if (dev->current_state != PCI_D0)
|
||||
goto done;
|
||||
|
||||
if (!has_pci_pm) {
|
||||
dev_dbg(hcd->self.controller, "--> PCI D0/legacy\n");
|
||||
goto done;
|
||||
}
|
||||
pci_save_state(dev);
|
||||
|
||||
/* Don't change state if we don't need to */
|
||||
if (message.event == PM_EVENT_FREEZE ||
|
||||
message.event == PM_EVENT_PRETHAW) {
|
||||
dev_dbg(&dev->dev, "--> no state change\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
has_pci_pm = pci_find_capability(dev, PCI_CAP_ID_PM);
|
||||
if (!has_pci_pm) {
|
||||
dev_dbg(&dev->dev, "--> PCI D0 legacy\n");
|
||||
} else {
|
||||
|
||||
/* NOTE: dev->current_state becomes nonzero only here, and
|
||||
* only for devices that support PCI PM. Also, exiting
|
||||
|
|
@ -273,35 +290,16 @@ int usb_hcd_pci_suspend(struct pci_dev *dev, pm_message_t message)
|
|||
retval = pci_set_power_state(dev, PCI_D3hot);
|
||||
suspend_report_result(pci_set_power_state, retval);
|
||||
if (retval == 0) {
|
||||
int wake = device_can_wakeup(&hcd->self.root_hub->dev);
|
||||
|
||||
wake = wake && device_may_wakeup(hcd->self.controller);
|
||||
|
||||
dev_dbg(hcd->self.controller, "--> PCI D3%s\n",
|
||||
wake ? "/wakeup" : "");
|
||||
|
||||
/* Ignore these return values. We rely on pci code to
|
||||
* reject requests the hardware can't implement, rather
|
||||
* than coding the same thing.
|
||||
*/
|
||||
(void) pci_enable_wake(dev, PCI_D3hot, wake);
|
||||
(void) pci_enable_wake(dev, PCI_D3cold, wake);
|
||||
dev_dbg(&dev->dev, "--> PCI D3\n");
|
||||
} else {
|
||||
dev_dbg(&dev->dev, "PCI D3 suspend fail, %d\n",
|
||||
retval);
|
||||
(void) usb_hcd_pci_resume(dev);
|
||||
pci_restore_state(dev);
|
||||
}
|
||||
|
||||
} else if (hcd->state != HC_STATE_HALT) {
|
||||
dev_dbg(hcd->self.controller, "hcd state %d; not suspended\n",
|
||||
hcd->state);
|
||||
WARN_ON(1);
|
||||
retval = -EINVAL;
|
||||
}
|
||||
|
||||
done:
|
||||
if (retval == 0) {
|
||||
#ifdef CONFIG_PPC_PMAC
|
||||
if (retval == 0) {
|
||||
/* Disable ASIC clocks for USB */
|
||||
if (machine_is(powermac)) {
|
||||
struct device_node *of_node;
|
||||
|
|
@ -311,30 +309,24 @@ done:
|
|||
pmac_call_feature(PMAC_FTR_USB_ENABLE,
|
||||
of_node, 0, 0);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_hcd_pci_suspend);
|
||||
EXPORT_SYMBOL_GPL(usb_hcd_pci_suspend_late);
|
||||
|
||||
/**
|
||||
* usb_hcd_pci_resume - power management resume of a PCI-based HCD
|
||||
* usb_hcd_pci_resume_early - resume a PCI-based HCD before IRQs are enabled
|
||||
* @dev: USB Host Controller being resumed
|
||||
*
|
||||
* Store this function in the HCD's struct pci_driver as resume().
|
||||
* Store this function in the HCD's struct pci_driver as .resume_early.
|
||||
*/
|
||||
int usb_hcd_pci_resume(struct pci_dev *dev)
|
||||
int usb_hcd_pci_resume_early(struct pci_dev *dev)
|
||||
{
|
||||
struct usb_hcd *hcd;
|
||||
int retval;
|
||||
|
||||
hcd = pci_get_drvdata(dev);
|
||||
if (hcd->state != HC_STATE_SUSPENDED) {
|
||||
dev_dbg(hcd->self.controller,
|
||||
"can't resume, not suspended!\n");
|
||||
return 0;
|
||||
}
|
||||
int retval = 0;
|
||||
pci_power_t state = dev->current_state;
|
||||
|
||||
#ifdef CONFIG_PPC_PMAC
|
||||
/* Reenable ASIC clocks for USB */
|
||||
|
|
@ -352,7 +344,7 @@ int usb_hcd_pci_resume(struct pci_dev *dev)
|
|||
* calls "standby", "suspend to RAM", and so on). There are also
|
||||
* dirty cases when swsusp fakes a suspend in "shutdown" mode.
|
||||
*/
|
||||
if (dev->current_state != PCI_D0) {
|
||||
if (state != PCI_D0) {
|
||||
#ifdef DEBUG
|
||||
int pci_pm;
|
||||
u16 pmcr;
|
||||
|
|
@ -364,8 +356,7 @@ int usb_hcd_pci_resume(struct pci_dev *dev)
|
|||
/* Clean case: power to USB and to HC registers was
|
||||
* maintained; remote wakeup is easy.
|
||||
*/
|
||||
dev_dbg(hcd->self.controller, "resume from PCI D%d\n",
|
||||
pmcr);
|
||||
dev_dbg(&dev->dev, "resume from PCI D%d\n", pmcr);
|
||||
} else {
|
||||
/* Clean: HC lost Vcc power, D0 uninitialized
|
||||
* + Vaux may have preserved port and transceiver
|
||||
|
|
@ -376,32 +367,55 @@ int usb_hcd_pci_resume(struct pci_dev *dev)
|
|||
* + after BIOS init
|
||||
* + after Linux init (HCD statically linked)
|
||||
*/
|
||||
dev_dbg(hcd->self.controller,
|
||||
"PCI D0, from previous PCI D%d\n",
|
||||
dev->current_state);
|
||||
dev_dbg(&dev->dev, "resume from previous PCI D%d\n",
|
||||
state);
|
||||
}
|
||||
#endif
|
||||
/* yes, ignore these results too... */
|
||||
(void) pci_enable_wake(dev, dev->current_state, 0);
|
||||
(void) pci_enable_wake(dev, PCI_D3cold, 0);
|
||||
|
||||
retval = pci_set_power_state(dev, PCI_D0);
|
||||
} else {
|
||||
/* Same basic cases: clean (powered/not), dirty */
|
||||
dev_dbg(hcd->self.controller, "PCI legacy resume\n");
|
||||
dev_dbg(&dev->dev, "PCI legacy resume\n");
|
||||
}
|
||||
|
||||
if (retval < 0)
|
||||
dev_err(&dev->dev, "can't resume: %d\n", retval);
|
||||
else
|
||||
pci_restore_state(dev);
|
||||
|
||||
return retval;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_hcd_pci_resume_early);
|
||||
|
||||
/**
|
||||
* usb_hcd_pci_resume - power management resume of a PCI-based HCD
|
||||
* @dev: USB Host Controller being resumed
|
||||
*
|
||||
* Store this function in the HCD's struct pci_driver as .resume.
|
||||
*/
|
||||
int usb_hcd_pci_resume(struct pci_dev *dev)
|
||||
{
|
||||
struct usb_hcd *hcd;
|
||||
int retval;
|
||||
|
||||
hcd = pci_get_drvdata(dev);
|
||||
if (hcd->state != HC_STATE_SUSPENDED) {
|
||||
dev_dbg(hcd->self.controller,
|
||||
"can't resume, not suspended!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* NOTE: the PCI API itself is asymmetric here. We don't need to
|
||||
* pci_set_power_state(PCI_D0) since that's part of re-enabling;
|
||||
* but that won't re-enable bus mastering. Yet pci_disable_device()
|
||||
* explicitly disables bus mastering...
|
||||
*/
|
||||
retval = pci_enable_device(dev);
|
||||
if (retval < 0) {
|
||||
dev_err(hcd->self.controller,
|
||||
"can't re-enable after resume, %d!\n", retval);
|
||||
dev_err(&dev->dev, "can't re-enable after resume, %d!\n",
|
||||
retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
pci_set_master(dev);
|
||||
pci_restore_state(dev);
|
||||
|
||||
/* yes, ignore this result too... */
|
||||
(void) pci_wake_from_d3(dev, 0);
|
||||
|
||||
clear_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
|
||||
|
||||
|
|
@ -413,7 +427,6 @@ int usb_hcd_pci_resume(struct pci_dev *dev)
|
|||
usb_hc_died(hcd);
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_hcd_pci_resume);
|
||||
|
|
|
|||
|
|
@ -1010,7 +1010,7 @@ int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb)
|
|||
spin_lock(&hcd_urb_list_lock);
|
||||
|
||||
/* Check that the URB isn't being killed */
|
||||
if (unlikely(urb->reject)) {
|
||||
if (unlikely(atomic_read(&urb->reject))) {
|
||||
rc = -EPERM;
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -1340,7 +1340,7 @@ int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags)
|
|||
INIT_LIST_HEAD(&urb->urb_list);
|
||||
atomic_dec(&urb->use_count);
|
||||
atomic_dec(&urb->dev->urbnum);
|
||||
if (urb->reject)
|
||||
if (atomic_read(&urb->reject))
|
||||
wake_up(&usb_kill_urb_queue);
|
||||
usb_put_urb(urb);
|
||||
}
|
||||
|
|
@ -1444,7 +1444,7 @@ void usb_hcd_giveback_urb(struct usb_hcd *hcd, struct urb *urb, int status)
|
|||
urb->status = status;
|
||||
urb->complete (urb);
|
||||
atomic_dec (&urb->use_count);
|
||||
if (unlikely (urb->reject))
|
||||
if (unlikely(atomic_read(&urb->reject)))
|
||||
wake_up (&usb_kill_urb_queue);
|
||||
usb_put_urb (urb);
|
||||
}
|
||||
|
|
@ -1573,14 +1573,14 @@ int usb_hcd_get_frame_number (struct usb_device *udev)
|
|||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
int hcd_bus_suspend(struct usb_device *rhdev)
|
||||
int hcd_bus_suspend(struct usb_device *rhdev, pm_message_t msg)
|
||||
{
|
||||
struct usb_hcd *hcd = container_of(rhdev->bus, struct usb_hcd, self);
|
||||
int status;
|
||||
int old_state = hcd->state;
|
||||
|
||||
dev_dbg(&rhdev->dev, "bus %s%s\n",
|
||||
rhdev->auto_pm ? "auto-" : "", "suspend");
|
||||
(msg.event & PM_EVENT_AUTO ? "auto-" : ""), "suspend");
|
||||
if (!hcd->driver->bus_suspend) {
|
||||
status = -ENOENT;
|
||||
} else {
|
||||
|
|
@ -1598,14 +1598,14 @@ int hcd_bus_suspend(struct usb_device *rhdev)
|
|||
return status;
|
||||
}
|
||||
|
||||
int hcd_bus_resume(struct usb_device *rhdev)
|
||||
int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg)
|
||||
{
|
||||
struct usb_hcd *hcd = container_of(rhdev->bus, struct usb_hcd, self);
|
||||
int status;
|
||||
int old_state = hcd->state;
|
||||
|
||||
dev_dbg(&rhdev->dev, "usb %s%s\n",
|
||||
rhdev->auto_pm ? "auto-" : "", "resume");
|
||||
(msg.event & PM_EVENT_AUTO ? "auto-" : ""), "resume");
|
||||
if (!hcd->driver->bus_resume)
|
||||
return -ENOENT;
|
||||
if (hcd->state == HC_STATE_RUNNING)
|
||||
|
|
@ -1638,7 +1638,7 @@ static void hcd_resume_work(struct work_struct *work)
|
|||
|
||||
usb_lock_device(udev);
|
||||
usb_mark_last_busy(udev);
|
||||
usb_external_resume_device(udev);
|
||||
usb_external_resume_device(udev, PMSG_REMOTE_RESUME);
|
||||
usb_unlock_device(udev);
|
||||
}
|
||||
|
||||
|
|
@ -2028,7 +2028,7 @@ EXPORT_SYMBOL_GPL(usb_hcd_platform_shutdown);
|
|||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
#if defined(CONFIG_USB_MON)
|
||||
#if defined(CONFIG_USB_MON) || defined(CONFIG_USB_MON_MODULE)
|
||||
|
||||
struct usb_mon_operations *mon_ops;
|
||||
|
||||
|
|
@ -2064,4 +2064,4 @@ void usb_mon_deregister (void)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL (usb_mon_deregister);
|
||||
|
||||
#endif /* CONFIG_USB_MON */
|
||||
#endif /* CONFIG_USB_MON || CONFIG_USB_MON_MODULE */
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@
|
|||
* Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef __USB_CORE_HCD_H
|
||||
#define __USB_CORE_HCD_H
|
||||
|
||||
#ifdef __KERNEL__
|
||||
|
||||
|
|
@ -254,7 +256,9 @@ extern int usb_hcd_pci_probe(struct pci_dev *dev,
|
|||
extern void usb_hcd_pci_remove(struct pci_dev *dev);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
extern int usb_hcd_pci_suspend(struct pci_dev *dev, pm_message_t state);
|
||||
extern int usb_hcd_pci_suspend(struct pci_dev *dev, pm_message_t msg);
|
||||
extern int usb_hcd_pci_suspend_late(struct pci_dev *dev, pm_message_t msg);
|
||||
extern int usb_hcd_pci_resume_early(struct pci_dev *dev);
|
||||
extern int usb_hcd_pci_resume(struct pci_dev *dev);
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
|
|
@ -386,8 +390,8 @@ extern int usb_find_interface_driver(struct usb_device *dev,
|
|||
#ifdef CONFIG_PM
|
||||
extern void usb_hcd_resume_root_hub(struct usb_hcd *hcd);
|
||||
extern void usb_root_hub_lost_power(struct usb_device *rhdev);
|
||||
extern int hcd_bus_suspend(struct usb_device *rhdev);
|
||||
extern int hcd_bus_resume(struct usb_device *rhdev);
|
||||
extern int hcd_bus_suspend(struct usb_device *rhdev, pm_message_t msg);
|
||||
extern int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg);
|
||||
#else
|
||||
static inline void usb_hcd_resume_root_hub(struct usb_hcd *hcd)
|
||||
{
|
||||
|
|
@ -419,7 +423,7 @@ static inline void usbfs_cleanup(void) { }
|
|||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
#if defined(CONFIG_USB_MON)
|
||||
#if defined(CONFIG_USB_MON) || defined(CONFIG_USB_MON_MODULE)
|
||||
|
||||
struct usb_mon_operations {
|
||||
void (*urb_submit)(struct usb_bus *bus, struct urb *urb);
|
||||
|
|
@ -461,7 +465,7 @@ static inline void usbmon_urb_submit_error(struct usb_bus *bus, struct urb *urb,
|
|||
static inline void usbmon_urb_complete(struct usb_bus *bus, struct urb *urb,
|
||||
int status) {}
|
||||
|
||||
#endif /* CONFIG_USB_MON */
|
||||
#endif /* CONFIG_USB_MON || CONFIG_USB_MON_MODULE */
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
|
|
@ -490,3 +494,5 @@ extern struct rw_semaphore ehci_cf_port_reset_rwsem;
|
|||
extern unsigned long usb_hcds_loaded;
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
|
||||
#endif /* __USB_CORE_HCD_H */
|
||||
|
|
|
|||
|
|
@ -107,7 +107,9 @@ MODULE_PARM_DESC (blinkenlights, "true to cycle leds on hubs");
|
|||
/* define initial 64-byte descriptor request timeout in milliseconds */
|
||||
static int initial_descriptor_timeout = USB_CTRL_GET_TIMEOUT;
|
||||
module_param(initial_descriptor_timeout, int, S_IRUGO|S_IWUSR);
|
||||
MODULE_PARM_DESC(initial_descriptor_timeout, "initial 64-byte descriptor request timeout in milliseconds (default 5000 - 5.0 seconds)");
|
||||
MODULE_PARM_DESC(initial_descriptor_timeout,
|
||||
"initial 64-byte descriptor request timeout in milliseconds "
|
||||
"(default 5000 - 5.0 seconds)");
|
||||
|
||||
/*
|
||||
* As of 2.6.10 we introduce a new USB device initialization scheme which
|
||||
|
|
@ -1136,8 +1138,8 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
|||
hdev = interface_to_usbdev(intf);
|
||||
|
||||
if (hdev->level == MAX_TOPO_LEVEL) {
|
||||
dev_err(&intf->dev, "Unsupported bus topology: "
|
||||
"hub nested too deep\n");
|
||||
dev_err(&intf->dev,
|
||||
"Unsupported bus topology: hub nested too deep\n");
|
||||
return -E2BIG;
|
||||
}
|
||||
|
||||
|
|
@ -1374,8 +1376,9 @@ static void usb_stop_pm(struct usb_device *udev)
|
|||
usb_autosuspend_device(udev->parent);
|
||||
usb_pm_unlock(udev);
|
||||
|
||||
/* Stop any autosuspend requests already submitted */
|
||||
cancel_rearming_delayed_work(&udev->autosuspend);
|
||||
/* Stop any autosuspend or autoresume requests already submitted */
|
||||
cancel_delayed_work_sync(&udev->autosuspend);
|
||||
cancel_work_sync(&udev->autoresume);
|
||||
}
|
||||
|
||||
#else
|
||||
|
|
@ -1434,17 +1437,12 @@ void usb_disconnect(struct usb_device **pdev)
|
|||
usb_disable_device(udev, 0);
|
||||
usb_hcd_synchronize_unlinks(udev);
|
||||
|
||||
usb_remove_ep_devs(&udev->ep0);
|
||||
usb_unlock_device(udev);
|
||||
|
||||
/* Remove the device-specific files from sysfs. This must be
|
||||
* done with udev unlocked, because some of the attribute
|
||||
* routines try to acquire the device lock.
|
||||
*/
|
||||
usb_remove_sysfs_dev_files(udev);
|
||||
|
||||
/* Unregister the device. The device driver is responsible
|
||||
* for removing the device files from usbfs and sysfs and for
|
||||
* de-configuring the device.
|
||||
* for de-configuring the device and invoking the remove-device
|
||||
* notifier chain (used by usbfs and possibly others).
|
||||
*/
|
||||
device_del(&udev->dev);
|
||||
|
||||
|
|
@ -1476,8 +1474,8 @@ static void announce_device(struct usb_device *udev)
|
|||
dev_info(&udev->dev, "New USB device found, idVendor=%04x, idProduct=%04x\n",
|
||||
le16_to_cpu(udev->descriptor.idVendor),
|
||||
le16_to_cpu(udev->descriptor.idProduct));
|
||||
dev_info(&udev->dev, "New USB device strings: Mfr=%d, Product=%d, "
|
||||
"SerialNumber=%d\n",
|
||||
dev_info(&udev->dev,
|
||||
"New USB device strings: Mfr=%d, Product=%d, SerialNumber=%d\n",
|
||||
udev->descriptor.iManufacturer,
|
||||
udev->descriptor.iProduct,
|
||||
udev->descriptor.iSerialNumber);
|
||||
|
|
@ -1542,7 +1540,7 @@ static int usb_configure_device_otg(struct usb_device *udev)
|
|||
* customize to match your product.
|
||||
*/
|
||||
dev_info(&udev->dev,
|
||||
"can't set HNP mode; %d\n",
|
||||
"can't set HNP mode: %d\n",
|
||||
err);
|
||||
bus->b_hnp_enable = 0;
|
||||
}
|
||||
|
|
@ -1635,6 +1633,10 @@ int usb_new_device(struct usb_device *udev)
|
|||
{
|
||||
int err;
|
||||
|
||||
/* Increment the parent's count of unsuspended children */
|
||||
if (udev->parent)
|
||||
usb_autoresume_device(udev->parent);
|
||||
|
||||
usb_detect_quirks(udev); /* Determine quirks */
|
||||
err = usb_configure_device(udev); /* detect & probe dev/intfs */
|
||||
if (err < 0)
|
||||
|
|
@ -1643,13 +1645,12 @@ int usb_new_device(struct usb_device *udev)
|
|||
udev->dev.devt = MKDEV(USB_DEVICE_MAJOR,
|
||||
(((udev->bus->busnum-1) * 128) + (udev->devnum-1)));
|
||||
|
||||
/* Increment the parent's count of unsuspended children */
|
||||
if (udev->parent)
|
||||
usb_autoresume_device(udev->parent);
|
||||
/* Tell the world! */
|
||||
announce_device(udev);
|
||||
|
||||
/* Register the device. The device driver is responsible
|
||||
* for adding the device files to sysfs and for configuring
|
||||
* the device.
|
||||
* for configuring the device and invoking the add-device
|
||||
* notifier chain (used by usbfs and possibly others).
|
||||
*/
|
||||
err = device_add(&udev->dev);
|
||||
if (err) {
|
||||
|
|
@ -1657,15 +1658,12 @@ int usb_new_device(struct usb_device *udev)
|
|||
goto fail;
|
||||
}
|
||||
|
||||
/* put device-specific files into sysfs */
|
||||
usb_create_sysfs_dev_files(udev);
|
||||
|
||||
/* Tell the world! */
|
||||
announce_device(udev);
|
||||
(void) usb_create_ep_devs(&udev->dev, &udev->ep0, udev);
|
||||
return err;
|
||||
|
||||
fail:
|
||||
usb_set_device_state(udev, USB_STATE_NOTATTACHED);
|
||||
usb_stop_pm(udev);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
@ -1982,7 +1980,7 @@ static int check_port_resume_type(struct usb_device *udev,
|
|||
*
|
||||
* Returns 0 on success, else negative errno.
|
||||
*/
|
||||
int usb_port_suspend(struct usb_device *udev)
|
||||
int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
|
||||
{
|
||||
struct usb_hub *hub = hdev_to_hub(udev->parent);
|
||||
int port1 = udev->portnum;
|
||||
|
|
@ -2021,7 +2019,7 @@ int usb_port_suspend(struct usb_device *udev)
|
|||
} else {
|
||||
/* device has up to 10 msec to fully suspend */
|
||||
dev_dbg(&udev->dev, "usb %ssuspend\n",
|
||||
udev->auto_pm ? "auto-" : "");
|
||||
(msg.event & PM_EVENT_AUTO ? "auto-" : ""));
|
||||
usb_set_device_state(udev, USB_STATE_SUSPENDED);
|
||||
msleep(10);
|
||||
}
|
||||
|
|
@ -2045,8 +2043,8 @@ static int finish_port_resume(struct usb_device *udev)
|
|||
u16 devstatus;
|
||||
|
||||
/* caller owns the udev device lock */
|
||||
dev_dbg(&udev->dev, "finish %sresume\n",
|
||||
udev->reset_resume ? "reset-" : "");
|
||||
dev_dbg(&udev->dev, "%s\n",
|
||||
udev->reset_resume ? "finish reset-resume" : "finish resume");
|
||||
|
||||
/* usb ch9 identifies four variants of SUSPENDED, based on what
|
||||
* state the device resumes to. Linux currently won't see the
|
||||
|
|
@ -2098,8 +2096,9 @@ static int finish_port_resume(struct usb_device *udev)
|
|||
NULL, 0,
|
||||
USB_CTRL_SET_TIMEOUT);
|
||||
if (status)
|
||||
dev_dbg(&udev->dev, "disable remote "
|
||||
"wakeup, status %d\n", status);
|
||||
dev_dbg(&udev->dev,
|
||||
"disable remote wakeup, status %d\n",
|
||||
status);
|
||||
}
|
||||
status = 0;
|
||||
}
|
||||
|
|
@ -2140,7 +2139,7 @@ static int finish_port_resume(struct usb_device *udev)
|
|||
*
|
||||
* Returns 0 on success, else negative errno.
|
||||
*/
|
||||
int usb_port_resume(struct usb_device *udev)
|
||||
int usb_port_resume(struct usb_device *udev, pm_message_t msg)
|
||||
{
|
||||
struct usb_hub *hub = hdev_to_hub(udev->parent);
|
||||
int port1 = udev->portnum;
|
||||
|
|
@ -2165,7 +2164,7 @@ int usb_port_resume(struct usb_device *udev)
|
|||
} else {
|
||||
/* drive resume for at least 20 msec */
|
||||
dev_dbg(&udev->dev, "usb %sresume\n",
|
||||
udev->auto_pm ? "auto-" : "");
|
||||
(msg.event & PM_EVENT_AUTO ? "auto-" : ""));
|
||||
msleep(25);
|
||||
|
||||
/* Virtual root hubs can trigger on GET_PORT_STATUS to
|
||||
|
|
@ -2206,7 +2205,7 @@ static int remote_wakeup(struct usb_device *udev)
|
|||
if (udev->state == USB_STATE_SUSPENDED) {
|
||||
dev_dbg(&udev->dev, "usb %sresume\n", "wakeup-");
|
||||
usb_mark_last_busy(udev);
|
||||
status = usb_external_resume_device(udev);
|
||||
status = usb_external_resume_device(udev, PMSG_REMOTE_RESUME);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
|
@ -2215,14 +2214,14 @@ static int remote_wakeup(struct usb_device *udev)
|
|||
|
||||
/* When CONFIG_USB_SUSPEND isn't set, we never suspend or resume any ports. */
|
||||
|
||||
int usb_port_suspend(struct usb_device *udev)
|
||||
int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* However we may need to do a reset-resume */
|
||||
|
||||
int usb_port_resume(struct usb_device *udev)
|
||||
int usb_port_resume(struct usb_device *udev, pm_message_t msg)
|
||||
{
|
||||
struct usb_hub *hub = hdev_to_hub(udev->parent);
|
||||
int port1 = udev->portnum;
|
||||
|
|
@ -2262,7 +2261,7 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
|
|||
|
||||
udev = hdev->children [port1-1];
|
||||
if (udev && udev->can_submit) {
|
||||
if (!hdev->auto_pm)
|
||||
if (!(msg.event & PM_EVENT_AUTO))
|
||||
dev_dbg(&intf->dev, "port %d nyet suspended\n",
|
||||
port1);
|
||||
return -EBUSY;
|
||||
|
|
@ -2385,7 +2384,7 @@ void usb_ep0_reinit(struct usb_device *udev)
|
|||
{
|
||||
usb_disable_endpoint(udev, 0 + USB_DIR_IN);
|
||||
usb_disable_endpoint(udev, 0 + USB_DIR_OUT);
|
||||
usb_enable_endpoint(udev, &udev->ep0);
|
||||
usb_enable_endpoint(udev, &udev->ep0, true);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_ep0_reinit);
|
||||
|
||||
|
|
@ -2582,9 +2581,9 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
|
|||
goto fail;
|
||||
}
|
||||
if (r) {
|
||||
dev_err(&udev->dev, "device descriptor "
|
||||
"read/%s, error %d\n",
|
||||
"64", r);
|
||||
dev_err(&udev->dev,
|
||||
"device descriptor read/64, error %d\n",
|
||||
r);
|
||||
retval = -EMSGSIZE;
|
||||
continue;
|
||||
}
|
||||
|
|
@ -2621,9 +2620,9 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
|
|||
|
||||
retval = usb_get_device_descriptor(udev, 8);
|
||||
if (retval < 8) {
|
||||
dev_err(&udev->dev, "device descriptor "
|
||||
"read/%s, error %d\n",
|
||||
"8", retval);
|
||||
dev_err(&udev->dev,
|
||||
"device descriptor read/8, error %d\n",
|
||||
retval);
|
||||
if (retval >= 0)
|
||||
retval = -EMSGSIZE;
|
||||
} else {
|
||||
|
|
@ -2650,8 +2649,8 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
|
|||
|
||||
retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE);
|
||||
if (retval < (signed)sizeof(udev->descriptor)) {
|
||||
dev_err(&udev->dev, "device descriptor read/%s, error %d\n",
|
||||
"all", retval);
|
||||
dev_err(&udev->dev, "device descriptor read/all, error %d\n",
|
||||
retval);
|
||||
if (retval >= 0)
|
||||
retval = -ENOMSG;
|
||||
goto fail;
|
||||
|
|
@ -2719,9 +2718,9 @@ hub_power_remaining (struct usb_hub *hub)
|
|||
else
|
||||
delta = 8;
|
||||
if (delta > hub->mA_per_port)
|
||||
dev_warn(&udev->dev, "%dmA is over %umA budget "
|
||||
"for port %d!\n",
|
||||
delta, hub->mA_per_port, port1);
|
||||
dev_warn(&udev->dev,
|
||||
"%dmA is over %umA budget for port %d!\n",
|
||||
delta, hub->mA_per_port, port1);
|
||||
remaining -= delta;
|
||||
}
|
||||
if (remaining < 0) {
|
||||
|
|
@ -3517,3 +3516,46 @@ int usb_reset_device(struct usb_device *udev)
|
|||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_reset_device);
|
||||
|
||||
|
||||
/**
|
||||
* usb_queue_reset_device - Reset a USB device from an atomic context
|
||||
* @iface: USB interface belonging to the device to reset
|
||||
*
|
||||
* This function can be used to reset a USB device from an atomic
|
||||
* context, where usb_reset_device() won't work (as it blocks).
|
||||
*
|
||||
* Doing a reset via this method is functionally equivalent to calling
|
||||
* usb_reset_device(), except for the fact that it is delayed to a
|
||||
* workqueue. This means that any drivers bound to other interfaces
|
||||
* might be unbound, as well as users from usbfs in user space.
|
||||
*
|
||||
* Corner cases:
|
||||
*
|
||||
* - Scheduling two resets at the same time from two different drivers
|
||||
* attached to two different interfaces of the same device is
|
||||
* possible; depending on how the driver attached to each interface
|
||||
* handles ->pre_reset(), the second reset might happen or not.
|
||||
*
|
||||
* - If a driver is unbound and it had a pending reset, the reset will
|
||||
* be cancelled.
|
||||
*
|
||||
* - This function can be called during .probe() or .disconnect()
|
||||
* times. On return from .disconnect(), any pending resets will be
|
||||
* cancelled.
|
||||
*
|
||||
* There is no no need to lock/unlock the @reset_ws as schedule_work()
|
||||
* does its own.
|
||||
*
|
||||
* NOTE: We don't do any reference count tracking because it is not
|
||||
* needed. The lifecycle of the work_struct is tied to the
|
||||
* usb_interface. Before destroying the interface we cancel the
|
||||
* work_struct, so the fact that work_struct is queued and or
|
||||
* running means the interface (and thus, the device) exist and
|
||||
* are referenced.
|
||||
*/
|
||||
void usb_queue_reset_device(struct usb_interface *iface)
|
||||
{
|
||||
schedule_work(&iface->reset_ws);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_queue_reset_device);
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@
|
|||
#include "hcd.h" /* for usbcore internals */
|
||||
#include "usb.h"
|
||||
|
||||
static void cancel_async_set_config(struct usb_device *udev);
|
||||
|
||||
struct api_context {
|
||||
struct completion done;
|
||||
int status;
|
||||
|
|
@ -139,9 +141,9 @@ int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request,
|
|||
|
||||
dr->bRequestType = requesttype;
|
||||
dr->bRequest = request;
|
||||
dr->wValue = cpu_to_le16p(&value);
|
||||
dr->wIndex = cpu_to_le16p(&index);
|
||||
dr->wLength = cpu_to_le16p(&size);
|
||||
dr->wValue = cpu_to_le16(value);
|
||||
dr->wIndex = cpu_to_le16(index);
|
||||
dr->wLength = cpu_to_le16(size);
|
||||
|
||||
/* dbg("usb_control_msg"); */
|
||||
|
||||
|
|
@ -1004,6 +1006,34 @@ int usb_clear_halt(struct usb_device *dev, int pipe)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(usb_clear_halt);
|
||||
|
||||
static int create_intf_ep_devs(struct usb_interface *intf)
|
||||
{
|
||||
struct usb_device *udev = interface_to_usbdev(intf);
|
||||
struct usb_host_interface *alt = intf->cur_altsetting;
|
||||
int i;
|
||||
|
||||
if (intf->ep_devs_created || intf->unregistering)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < alt->desc.bNumEndpoints; ++i)
|
||||
(void) usb_create_ep_devs(&intf->dev, &alt->endpoint[i], udev);
|
||||
intf->ep_devs_created = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void remove_intf_ep_devs(struct usb_interface *intf)
|
||||
{
|
||||
struct usb_host_interface *alt = intf->cur_altsetting;
|
||||
int i;
|
||||
|
||||
if (!intf->ep_devs_created)
|
||||
return;
|
||||
|
||||
for (i = 0; i < alt->desc.bNumEndpoints; ++i)
|
||||
usb_remove_ep_devs(&alt->endpoint[i]);
|
||||
intf->ep_devs_created = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* usb_disable_endpoint -- Disable an endpoint by address
|
||||
* @dev: the device whose endpoint is being disabled
|
||||
|
|
@ -1092,7 +1122,7 @@ void usb_disable_device(struct usb_device *dev, int skip_ep0)
|
|||
dev_dbg(&dev->dev, "unregistering interface %s\n",
|
||||
dev_name(&interface->dev));
|
||||
interface->unregistering = 1;
|
||||
usb_remove_sysfs_intf_files(interface);
|
||||
remove_intf_ep_devs(interface);
|
||||
device_del(&interface->dev);
|
||||
}
|
||||
|
||||
|
|
@ -1113,22 +1143,26 @@ void usb_disable_device(struct usb_device *dev, int skip_ep0)
|
|||
* usb_enable_endpoint - Enable an endpoint for USB communications
|
||||
* @dev: the device whose interface is being enabled
|
||||
* @ep: the endpoint
|
||||
* @reset_toggle: flag to set the endpoint's toggle back to 0
|
||||
*
|
||||
* Resets the endpoint toggle, and sets dev->ep_{in,out} pointers.
|
||||
* Resets the endpoint toggle if asked, and sets dev->ep_{in,out} pointers.
|
||||
* For control endpoints, both the input and output sides are handled.
|
||||
*/
|
||||
void usb_enable_endpoint(struct usb_device *dev, struct usb_host_endpoint *ep)
|
||||
void usb_enable_endpoint(struct usb_device *dev, struct usb_host_endpoint *ep,
|
||||
bool reset_toggle)
|
||||
{
|
||||
int epnum = usb_endpoint_num(&ep->desc);
|
||||
int is_out = usb_endpoint_dir_out(&ep->desc);
|
||||
int is_control = usb_endpoint_xfer_control(&ep->desc);
|
||||
|
||||
if (is_out || is_control) {
|
||||
usb_settoggle(dev, epnum, 1, 0);
|
||||
if (reset_toggle)
|
||||
usb_settoggle(dev, epnum, 1, 0);
|
||||
dev->ep_out[epnum] = ep;
|
||||
}
|
||||
if (!is_out || is_control) {
|
||||
usb_settoggle(dev, epnum, 0, 0);
|
||||
if (reset_toggle)
|
||||
usb_settoggle(dev, epnum, 0, 0);
|
||||
dev->ep_in[epnum] = ep;
|
||||
}
|
||||
ep->enabled = 1;
|
||||
|
|
@ -1138,17 +1172,18 @@ void usb_enable_endpoint(struct usb_device *dev, struct usb_host_endpoint *ep)
|
|||
* usb_enable_interface - Enable all the endpoints for an interface
|
||||
* @dev: the device whose interface is being enabled
|
||||
* @intf: pointer to the interface descriptor
|
||||
* @reset_toggles: flag to set the endpoints' toggles back to 0
|
||||
*
|
||||
* Enables all the endpoints for the interface's current altsetting.
|
||||
*/
|
||||
static void usb_enable_interface(struct usb_device *dev,
|
||||
struct usb_interface *intf)
|
||||
void usb_enable_interface(struct usb_device *dev,
|
||||
struct usb_interface *intf, bool reset_toggles)
|
||||
{
|
||||
struct usb_host_interface *alt = intf->cur_altsetting;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < alt->desc.bNumEndpoints; ++i)
|
||||
usb_enable_endpoint(dev, &alt->endpoint[i]);
|
||||
usb_enable_endpoint(dev, &alt->endpoint[i], reset_toggles);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1235,8 +1270,10 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate)
|
|||
*/
|
||||
|
||||
/* prevent submissions using previous endpoint settings */
|
||||
if (iface->cur_altsetting != alt)
|
||||
if (iface->cur_altsetting != alt) {
|
||||
remove_intf_ep_devs(iface);
|
||||
usb_remove_sysfs_intf_files(iface);
|
||||
}
|
||||
usb_disable_interface(dev, iface);
|
||||
|
||||
iface->cur_altsetting = alt;
|
||||
|
|
@ -1271,10 +1308,11 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate)
|
|||
* during the SETUP stage - hence EP0 toggles are "don't care" here.
|
||||
* (Likewise, EP0 never "halts" on well designed devices.)
|
||||
*/
|
||||
usb_enable_interface(dev, iface);
|
||||
if (device_is_registered(&iface->dev))
|
||||
usb_enable_interface(dev, iface, true);
|
||||
if (device_is_registered(&iface->dev)) {
|
||||
usb_create_sysfs_intf_files(iface);
|
||||
|
||||
create_intf_ep_devs(iface);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_set_interface);
|
||||
|
|
@ -1334,7 +1372,6 @@ int usb_reset_configuration(struct usb_device *dev)
|
|||
struct usb_interface *intf = config->interface[i];
|
||||
struct usb_host_interface *alt;
|
||||
|
||||
usb_remove_sysfs_intf_files(intf);
|
||||
alt = usb_altnum_to_altsetting(intf, 0);
|
||||
|
||||
/* No altsetting 0? We'll assume the first altsetting.
|
||||
|
|
@ -1345,10 +1382,16 @@ int usb_reset_configuration(struct usb_device *dev)
|
|||
if (!alt)
|
||||
alt = &intf->altsetting[0];
|
||||
|
||||
if (alt != intf->cur_altsetting) {
|
||||
remove_intf_ep_devs(intf);
|
||||
usb_remove_sysfs_intf_files(intf);
|
||||
}
|
||||
intf->cur_altsetting = alt;
|
||||
usb_enable_interface(dev, intf);
|
||||
if (device_is_registered(&intf->dev))
|
||||
usb_enable_interface(dev, intf, true);
|
||||
if (device_is_registered(&intf->dev)) {
|
||||
usb_create_sysfs_intf_files(intf);
|
||||
create_intf_ep_devs(intf);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1441,6 +1484,46 @@ static struct usb_interface_assoc_descriptor *find_iad(struct usb_device *dev,
|
|||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Internal function to queue a device reset
|
||||
*
|
||||
* This is initialized into the workstruct in 'struct
|
||||
* usb_device->reset_ws' that is launched by
|
||||
* message.c:usb_set_configuration() when initializing each 'struct
|
||||
* usb_interface'.
|
||||
*
|
||||
* It is safe to get the USB device without reference counts because
|
||||
* the life cycle of @iface is bound to the life cycle of @udev. Then,
|
||||
* this function will be ran only if @iface is alive (and before
|
||||
* freeing it any scheduled instances of it will have been cancelled).
|
||||
*
|
||||
* We need to set a flag (usb_dev->reset_running) because when we call
|
||||
* the reset, the interfaces might be unbound. The current interface
|
||||
* cannot try to remove the queued work as it would cause a deadlock
|
||||
* (you cannot remove your work from within your executing
|
||||
* workqueue). This flag lets it know, so that
|
||||
* usb_cancel_queued_reset() doesn't try to do it.
|
||||
*
|
||||
* See usb_queue_reset_device() for more details
|
||||
*/
|
||||
void __usb_queue_reset_device(struct work_struct *ws)
|
||||
{
|
||||
int rc;
|
||||
struct usb_interface *iface =
|
||||
container_of(ws, struct usb_interface, reset_ws);
|
||||
struct usb_device *udev = interface_to_usbdev(iface);
|
||||
|
||||
rc = usb_lock_device_for_reset(udev, iface);
|
||||
if (rc >= 0) {
|
||||
iface->reset_running = 1;
|
||||
usb_reset_device(udev);
|
||||
iface->reset_running = 0;
|
||||
usb_unlock_device(udev);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* usb_set_configuration - Makes a particular device setting be current
|
||||
* @dev: the device whose configuration is being updated
|
||||
|
|
@ -1560,6 +1643,9 @@ free_interfaces:
|
|||
if (dev->state != USB_STATE_ADDRESS)
|
||||
usb_disable_device(dev, 1); /* Skip ep0 */
|
||||
|
||||
/* Get rid of pending async Set-Config requests for this device */
|
||||
cancel_async_set_config(dev);
|
||||
|
||||
ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
|
||||
USB_REQ_SET_CONFIGURATION, 0, configuration, 0,
|
||||
NULL, 0, USB_CTRL_SET_TIMEOUT);
|
||||
|
|
@ -1604,13 +1690,14 @@ free_interfaces:
|
|||
alt = &intf->altsetting[0];
|
||||
|
||||
intf->cur_altsetting = alt;
|
||||
usb_enable_interface(dev, intf);
|
||||
usb_enable_interface(dev, intf, true);
|
||||
intf->dev.parent = &dev->dev;
|
||||
intf->dev.driver = NULL;
|
||||
intf->dev.bus = &usb_bus_type;
|
||||
intf->dev.type = &usb_if_device_type;
|
||||
intf->dev.groups = usb_interface_groups;
|
||||
intf->dev.dma_mask = dev->dev.dma_mask;
|
||||
INIT_WORK(&intf->reset_ws, __usb_queue_reset_device);
|
||||
device_initialize(&intf->dev);
|
||||
mark_quiesced(intf);
|
||||
dev_set_name(&intf->dev, "%d-%s:%d.%d",
|
||||
|
|
@ -1641,17 +1728,21 @@ free_interfaces:
|
|||
dev_name(&intf->dev), ret);
|
||||
continue;
|
||||
}
|
||||
usb_create_sysfs_intf_files(intf);
|
||||
create_intf_ep_devs(intf);
|
||||
}
|
||||
|
||||
usb_autosuspend_device(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static LIST_HEAD(set_config_list);
|
||||
static DEFINE_SPINLOCK(set_config_lock);
|
||||
|
||||
struct set_config_request {
|
||||
struct usb_device *udev;
|
||||
int config;
|
||||
struct work_struct work;
|
||||
struct list_head node;
|
||||
};
|
||||
|
||||
/* Worker routine for usb_driver_set_configuration() */
|
||||
|
|
@ -1659,14 +1750,35 @@ static void driver_set_config_work(struct work_struct *work)
|
|||
{
|
||||
struct set_config_request *req =
|
||||
container_of(work, struct set_config_request, work);
|
||||
struct usb_device *udev = req->udev;
|
||||
|
||||
usb_lock_device(req->udev);
|
||||
usb_set_configuration(req->udev, req->config);
|
||||
usb_unlock_device(req->udev);
|
||||
usb_put_dev(req->udev);
|
||||
usb_lock_device(udev);
|
||||
spin_lock(&set_config_lock);
|
||||
list_del(&req->node);
|
||||
spin_unlock(&set_config_lock);
|
||||
|
||||
if (req->config >= -1) /* Is req still valid? */
|
||||
usb_set_configuration(udev, req->config);
|
||||
usb_unlock_device(udev);
|
||||
usb_put_dev(udev);
|
||||
kfree(req);
|
||||
}
|
||||
|
||||
/* Cancel pending Set-Config requests for a device whose configuration
|
||||
* was just changed
|
||||
*/
|
||||
static void cancel_async_set_config(struct usb_device *udev)
|
||||
{
|
||||
struct set_config_request *req;
|
||||
|
||||
spin_lock(&set_config_lock);
|
||||
list_for_each_entry(req, &set_config_list, node) {
|
||||
if (req->udev == udev)
|
||||
req->config = -999; /* Mark as cancelled */
|
||||
}
|
||||
spin_unlock(&set_config_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* usb_driver_set_configuration - Provide a way for drivers to change device configurations
|
||||
* @udev: the device whose configuration is being updated
|
||||
|
|
@ -1698,6 +1810,10 @@ int usb_driver_set_configuration(struct usb_device *udev, int config)
|
|||
req->config = config;
|
||||
INIT_WORK(&req->work, driver_set_config_work);
|
||||
|
||||
spin_lock(&set_config_lock);
|
||||
list_add(&req->node, &set_config_list);
|
||||
spin_unlock(&set_config_lock);
|
||||
|
||||
usb_get_dev(udev);
|
||||
schedule_work(&req->work);
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -359,19 +359,19 @@ set_level(struct device *dev, struct device_attribute *attr,
|
|||
strncmp(buf, on_string, len) == 0) {
|
||||
udev->autosuspend_disabled = 1;
|
||||
udev->autoresume_disabled = 0;
|
||||
rc = usb_external_resume_device(udev);
|
||||
rc = usb_external_resume_device(udev, PMSG_USER_RESUME);
|
||||
|
||||
} else if (len == sizeof auto_string - 1 &&
|
||||
strncmp(buf, auto_string, len) == 0) {
|
||||
udev->autosuspend_disabled = 0;
|
||||
udev->autoresume_disabled = 0;
|
||||
rc = usb_external_resume_device(udev);
|
||||
rc = usb_external_resume_device(udev, PMSG_USER_RESUME);
|
||||
|
||||
} else if (len == sizeof suspend_string - 1 &&
|
||||
strncmp(buf, suspend_string, len) == 0) {
|
||||
udev->autosuspend_disabled = 0;
|
||||
udev->autoresume_disabled = 1;
|
||||
rc = usb_external_suspend_device(udev, PMSG_SUSPEND);
|
||||
rc = usb_external_suspend_device(udev, PMSG_USER_SUSPEND);
|
||||
|
||||
} else
|
||||
rc = -EINVAL;
|
||||
|
|
@ -629,9 +629,6 @@ int usb_create_sysfs_dev_files(struct usb_device *udev)
|
|||
struct device *dev = &udev->dev;
|
||||
int retval;
|
||||
|
||||
/* Unforunately these attributes cannot be created before
|
||||
* the uevent is broadcast.
|
||||
*/
|
||||
retval = device_create_bin_file(dev, &dev_bin_attr_descriptors);
|
||||
if (retval)
|
||||
goto error;
|
||||
|
|
@ -643,11 +640,7 @@ int usb_create_sysfs_dev_files(struct usb_device *udev)
|
|||
retval = add_power_attributes(dev);
|
||||
if (retval)
|
||||
goto error;
|
||||
|
||||
retval = usb_create_ep_files(dev, &udev->ep0, udev);
|
||||
if (retval)
|
||||
goto error;
|
||||
return 0;
|
||||
return retval;
|
||||
error:
|
||||
usb_remove_sysfs_dev_files(udev);
|
||||
return retval;
|
||||
|
|
@ -657,7 +650,6 @@ void usb_remove_sysfs_dev_files(struct usb_device *udev)
|
|||
{
|
||||
struct device *dev = &udev->dev;
|
||||
|
||||
usb_remove_ep_files(&udev->ep0);
|
||||
remove_power_attributes(dev);
|
||||
remove_persist_attributes(dev);
|
||||
device_remove_bin_file(dev, &dev_bin_attr_descriptors);
|
||||
|
|
@ -812,28 +804,6 @@ struct attribute_group *usb_interface_groups[] = {
|
|||
NULL
|
||||
};
|
||||
|
||||
static inline void usb_create_intf_ep_files(struct usb_interface *intf,
|
||||
struct usb_device *udev)
|
||||
{
|
||||
struct usb_host_interface *iface_desc;
|
||||
int i;
|
||||
|
||||
iface_desc = intf->cur_altsetting;
|
||||
for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i)
|
||||
usb_create_ep_files(&intf->dev, &iface_desc->endpoint[i],
|
||||
udev);
|
||||
}
|
||||
|
||||
static inline void usb_remove_intf_ep_files(struct usb_interface *intf)
|
||||
{
|
||||
struct usb_host_interface *iface_desc;
|
||||
int i;
|
||||
|
||||
iface_desc = intf->cur_altsetting;
|
||||
for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i)
|
||||
usb_remove_ep_files(&iface_desc->endpoint[i]);
|
||||
}
|
||||
|
||||
int usb_create_sysfs_intf_files(struct usb_interface *intf)
|
||||
{
|
||||
struct usb_device *udev = interface_to_usbdev(intf);
|
||||
|
|
@ -843,26 +813,19 @@ int usb_create_sysfs_intf_files(struct usb_interface *intf)
|
|||
if (intf->sysfs_files_created || intf->unregistering)
|
||||
return 0;
|
||||
|
||||
/* The interface string may be present in some altsettings
|
||||
* and missing in others. Hence its attribute cannot be created
|
||||
* before the uevent is broadcast.
|
||||
*/
|
||||
if (alt->string == NULL)
|
||||
alt->string = usb_cache_string(udev, alt->desc.iInterface);
|
||||
if (alt->string)
|
||||
retval = device_create_file(&intf->dev, &dev_attr_interface);
|
||||
usb_create_intf_ep_files(intf, udev);
|
||||
intf->sysfs_files_created = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void usb_remove_sysfs_intf_files(struct usb_interface *intf)
|
||||
{
|
||||
struct device *dev = &intf->dev;
|
||||
|
||||
if (!intf->sysfs_files_created)
|
||||
return;
|
||||
usb_remove_intf_ep_files(intf);
|
||||
device_remove_file(dev, &dev_attr_interface);
|
||||
|
||||
device_remove_file(&intf->dev, &dev_attr_interface);
|
||||
intf->sysfs_files_created = 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@
|
|||
|
||||
#define to_urb(d) container_of(d, struct urb, kref)
|
||||
|
||||
static DEFINE_SPINLOCK(usb_reject_lock);
|
||||
|
||||
static void urb_destroy(struct kref *kref)
|
||||
{
|
||||
|
|
@ -131,9 +130,7 @@ void usb_anchor_urb(struct urb *urb, struct usb_anchor *anchor)
|
|||
urb->anchor = anchor;
|
||||
|
||||
if (unlikely(anchor->poisoned)) {
|
||||
spin_lock(&usb_reject_lock);
|
||||
urb->reject++;
|
||||
spin_unlock(&usb_reject_lock);
|
||||
atomic_inc(&urb->reject);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&anchor->lock, flags);
|
||||
|
|
@ -565,16 +562,12 @@ void usb_kill_urb(struct urb *urb)
|
|||
might_sleep();
|
||||
if (!(urb && urb->dev && urb->ep))
|
||||
return;
|
||||
spin_lock_irq(&usb_reject_lock);
|
||||
++urb->reject;
|
||||
spin_unlock_irq(&usb_reject_lock);
|
||||
atomic_inc(&urb->reject);
|
||||
|
||||
usb_hcd_unlink_urb(urb, -ENOENT);
|
||||
wait_event(usb_kill_urb_queue, atomic_read(&urb->use_count) == 0);
|
||||
|
||||
spin_lock_irq(&usb_reject_lock);
|
||||
--urb->reject;
|
||||
spin_unlock_irq(&usb_reject_lock);
|
||||
atomic_dec(&urb->reject);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_kill_urb);
|
||||
|
||||
|
|
@ -606,9 +599,7 @@ void usb_poison_urb(struct urb *urb)
|
|||
might_sleep();
|
||||
if (!(urb && urb->dev && urb->ep))
|
||||
return;
|
||||
spin_lock_irq(&usb_reject_lock);
|
||||
++urb->reject;
|
||||
spin_unlock_irq(&usb_reject_lock);
|
||||
atomic_inc(&urb->reject);
|
||||
|
||||
usb_hcd_unlink_urb(urb, -ENOENT);
|
||||
wait_event(usb_kill_urb_queue, atomic_read(&urb->use_count) == 0);
|
||||
|
|
@ -617,14 +608,10 @@ EXPORT_SYMBOL_GPL(usb_poison_urb);
|
|||
|
||||
void usb_unpoison_urb(struct urb *urb)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (!urb)
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&usb_reject_lock, flags);
|
||||
--urb->reject;
|
||||
spin_unlock_irqrestore(&usb_reject_lock, flags);
|
||||
atomic_dec(&urb->reject);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_unpoison_urb);
|
||||
|
||||
|
|
@ -691,6 +678,26 @@ void usb_poison_anchored_urbs(struct usb_anchor *anchor)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(usb_poison_anchored_urbs);
|
||||
|
||||
/**
|
||||
* usb_unpoison_anchored_urbs - let an anchor be used successfully again
|
||||
* @anchor: anchor the requests are bound to
|
||||
*
|
||||
* Reverses the effect of usb_poison_anchored_urbs
|
||||
* the anchor can be used normally after it returns
|
||||
*/
|
||||
void usb_unpoison_anchored_urbs(struct usb_anchor *anchor)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct urb *lazarus;
|
||||
|
||||
spin_lock_irqsave(&anchor->lock, flags);
|
||||
list_for_each_entry(lazarus, &anchor->urb_list, anchor_list) {
|
||||
usb_unpoison_urb(lazarus);
|
||||
}
|
||||
anchor->poisoned = 0;
|
||||
spin_unlock_irqrestore(&anchor->lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_unpoison_anchored_urbs);
|
||||
/**
|
||||
* usb_unlink_anchored_urbs - asynchronously cancel transfer requests en masse
|
||||
* @anchor: anchor the requests are bound to
|
||||
|
|
|
|||
|
|
@ -253,7 +253,7 @@ static int usb_dev_prepare(struct device *dev)
|
|||
static void usb_dev_complete(struct device *dev)
|
||||
{
|
||||
/* Currently used only for rebinding interfaces */
|
||||
usb_resume(dev); /* Implement eventually? */
|
||||
usb_resume(dev, PMSG_RESUME); /* Message event is meaningless */
|
||||
}
|
||||
|
||||
static int usb_dev_suspend(struct device *dev)
|
||||
|
|
@ -263,7 +263,7 @@ static int usb_dev_suspend(struct device *dev)
|
|||
|
||||
static int usb_dev_resume(struct device *dev)
|
||||
{
|
||||
return usb_resume(dev);
|
||||
return usb_resume(dev, PMSG_RESUME);
|
||||
}
|
||||
|
||||
static int usb_dev_freeze(struct device *dev)
|
||||
|
|
@ -273,7 +273,7 @@ static int usb_dev_freeze(struct device *dev)
|
|||
|
||||
static int usb_dev_thaw(struct device *dev)
|
||||
{
|
||||
return usb_resume(dev);
|
||||
return usb_resume(dev, PMSG_THAW);
|
||||
}
|
||||
|
||||
static int usb_dev_poweroff(struct device *dev)
|
||||
|
|
@ -283,10 +283,10 @@ static int usb_dev_poweroff(struct device *dev)
|
|||
|
||||
static int usb_dev_restore(struct device *dev)
|
||||
{
|
||||
return usb_resume(dev);
|
||||
return usb_resume(dev, PMSG_RESTORE);
|
||||
}
|
||||
|
||||
static struct pm_ops usb_device_pm_ops = {
|
||||
static struct dev_pm_ops usb_device_pm_ops = {
|
||||
.prepare = usb_dev_prepare,
|
||||
.complete = usb_dev_complete,
|
||||
.suspend = usb_dev_suspend,
|
||||
|
|
@ -301,7 +301,7 @@ static struct pm_ops usb_device_pm_ops = {
|
|||
|
||||
#define ksuspend_usb_init() 0
|
||||
#define ksuspend_usb_cleanup() do {} while (0)
|
||||
#define usb_device_pm_ops (*(struct pm_ops *)0)
|
||||
#define usb_device_pm_ops (*(struct dev_pm_ops *)0)
|
||||
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
|
|
@ -362,7 +362,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
|
|||
dev->ep0.desc.bLength = USB_DT_ENDPOINT_SIZE;
|
||||
dev->ep0.desc.bDescriptorType = USB_DT_ENDPOINT;
|
||||
/* ep0 maxpacket comes later, from device descriptor */
|
||||
usb_enable_endpoint(dev, &dev->ep0);
|
||||
usb_enable_endpoint(dev, &dev->ep0, true);
|
||||
dev->can_submit = 1;
|
||||
|
||||
/* Save readable and stable topology id, distinguishing devices
|
||||
|
|
@ -402,6 +402,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
|
|||
#ifdef CONFIG_PM
|
||||
mutex_init(&dev->pm_mutex);
|
||||
INIT_DELAYED_WORK(&dev->autosuspend, usb_autosuspend_work);
|
||||
INIT_WORK(&dev->autoresume, usb_autoresume_work);
|
||||
dev->autosuspend_delay = usb_autosuspend_delay * HZ;
|
||||
dev->connect_time = jiffies;
|
||||
dev->active_duration = -jiffies;
|
||||
|
|
@ -513,10 +514,7 @@ EXPORT_SYMBOL_GPL(usb_put_intf);
|
|||
* disconnect; in some drivers (such as usb-storage) the disconnect()
|
||||
* or suspend() method will block waiting for a device reset to complete.
|
||||
*
|
||||
* Returns a negative error code for failure, otherwise 1 or 0 to indicate
|
||||
* that the device will or will not have to be unlocked. (0 can be
|
||||
* returned when an interface is given and is BINDING, because in that
|
||||
* case the driver already owns the device lock.)
|
||||
* Returns a negative error code for failure, otherwise 0.
|
||||
*/
|
||||
int usb_lock_device_for_reset(struct usb_device *udev,
|
||||
const struct usb_interface *iface)
|
||||
|
|
@ -527,16 +525,9 @@ int usb_lock_device_for_reset(struct usb_device *udev,
|
|||
return -ENODEV;
|
||||
if (udev->state == USB_STATE_SUSPENDED)
|
||||
return -EHOSTUNREACH;
|
||||
if (iface) {
|
||||
switch (iface->condition) {
|
||||
case USB_INTERFACE_BINDING:
|
||||
return 0;
|
||||
case USB_INTERFACE_BOUND:
|
||||
break;
|
||||
default:
|
||||
return -EINTR;
|
||||
}
|
||||
}
|
||||
if (iface && (iface->condition == USB_INTERFACE_UNBINDING ||
|
||||
iface->condition == USB_INTERFACE_UNBOUND))
|
||||
return -EINTR;
|
||||
|
||||
while (usb_trylock_device(udev) != 0) {
|
||||
|
||||
|
|
@ -550,10 +541,11 @@ int usb_lock_device_for_reset(struct usb_device *udev,
|
|||
return -ENODEV;
|
||||
if (udev->state == USB_STATE_SUSPENDED)
|
||||
return -EHOSTUNREACH;
|
||||
if (iface && iface->condition != USB_INTERFACE_BOUND)
|
||||
if (iface && (iface->condition == USB_INTERFACE_UNBINDING ||
|
||||
iface->condition == USB_INTERFACE_UNBOUND))
|
||||
return -EINTR;
|
||||
}
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_lock_device_for_reset);
|
||||
|
||||
|
|
@ -962,8 +954,12 @@ void usb_buffer_unmap_sg(const struct usb_device *dev, int is_in,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(usb_buffer_unmap_sg);
|
||||
|
||||
/* format to disable USB on kernel command line is: nousb */
|
||||
__module_param_call("", nousb, param_set_bool, param_get_bool, &nousb, 0444);
|
||||
/* To disable USB, kernel command line is 'nousb' not 'usbcore.nousb' */
|
||||
#ifdef MODULE
|
||||
module_param(nousb, bool, 0444);
|
||||
#else
|
||||
core_param(nousb, nousb, bool, 0444);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* for external read access to <nousb>
|
||||
|
|
@ -974,6 +970,37 @@ int usb_disabled(void)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(usb_disabled);
|
||||
|
||||
/*
|
||||
* Notifications of device and interface registration
|
||||
*/
|
||||
static int usb_bus_notify(struct notifier_block *nb, unsigned long action,
|
||||
void *data)
|
||||
{
|
||||
struct device *dev = data;
|
||||
|
||||
switch (action) {
|
||||
case BUS_NOTIFY_ADD_DEVICE:
|
||||
if (dev->type == &usb_device_type)
|
||||
(void) usb_create_sysfs_dev_files(to_usb_device(dev));
|
||||
else if (dev->type == &usb_if_device_type)
|
||||
(void) usb_create_sysfs_intf_files(
|
||||
to_usb_interface(dev));
|
||||
break;
|
||||
|
||||
case BUS_NOTIFY_DEL_DEVICE:
|
||||
if (dev->type == &usb_device_type)
|
||||
usb_remove_sysfs_dev_files(to_usb_device(dev));
|
||||
else if (dev->type == &usb_if_device_type)
|
||||
usb_remove_sysfs_intf_files(to_usb_interface(dev));
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct notifier_block usb_bus_nb = {
|
||||
.notifier_call = usb_bus_notify,
|
||||
};
|
||||
|
||||
/*
|
||||
* Init
|
||||
*/
|
||||
|
|
@ -991,6 +1018,9 @@ static int __init usb_init(void)
|
|||
retval = bus_register(&usb_bus_type);
|
||||
if (retval)
|
||||
goto bus_register_failed;
|
||||
retval = bus_register_notifier(&usb_bus_type, &usb_bus_nb);
|
||||
if (retval)
|
||||
goto bus_notifier_failed;
|
||||
retval = usb_host_init();
|
||||
if (retval)
|
||||
goto host_init_failed;
|
||||
|
|
@ -1025,6 +1055,8 @@ driver_register_failed:
|
|||
major_init_failed:
|
||||
usb_host_cleanup();
|
||||
host_init_failed:
|
||||
bus_unregister_notifier(&usb_bus_type, &usb_bus_nb);
|
||||
bus_notifier_failed:
|
||||
bus_unregister(&usb_bus_type);
|
||||
bus_register_failed:
|
||||
ksuspend_usb_cleanup();
|
||||
|
|
@ -1048,6 +1080,7 @@ static void __exit usb_exit(void)
|
|||
usb_devio_cleanup();
|
||||
usb_hub_cleanup();
|
||||
usb_host_cleanup();
|
||||
bus_unregister_notifier(&usb_bus_type, &usb_bus_nb);
|
||||
bus_unregister(&usb_bus_type);
|
||||
ksuspend_usb_cleanup();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,20 @@
|
|||
#include <linux/pm.h>
|
||||
|
||||
/* Functions local to drivers/usb/core/ */
|
||||
|
||||
extern int usb_create_sysfs_dev_files(struct usb_device *dev);
|
||||
extern void usb_remove_sysfs_dev_files(struct usb_device *dev);
|
||||
extern int usb_create_sysfs_intf_files(struct usb_interface *intf);
|
||||
extern void usb_remove_sysfs_intf_files(struct usb_interface *intf);
|
||||
extern int usb_create_ep_files(struct device *parent,
|
||||
extern int usb_create_ep_devs(struct device *parent,
|
||||
struct usb_host_endpoint *endpoint,
|
||||
struct usb_device *udev);
|
||||
extern void usb_remove_ep_files(struct usb_host_endpoint *endpoint);
|
||||
extern void usb_remove_ep_devs(struct usb_host_endpoint *endpoint);
|
||||
|
||||
extern void usb_enable_endpoint(struct usb_device *dev,
|
||||
struct usb_host_endpoint *ep);
|
||||
struct usb_host_endpoint *ep, bool reset_toggle);
|
||||
extern void usb_enable_interface(struct usb_device *dev,
|
||||
struct usb_interface *intf, bool reset_toggles);
|
||||
extern void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr);
|
||||
extern void usb_disable_interface(struct usb_device *dev,
|
||||
struct usb_interface *intf);
|
||||
|
|
@ -42,14 +46,16 @@ extern void usb_host_cleanup(void);
|
|||
#ifdef CONFIG_PM
|
||||
|
||||
extern int usb_suspend(struct device *dev, pm_message_t msg);
|
||||
extern int usb_resume(struct device *dev);
|
||||
extern int usb_resume(struct device *dev, pm_message_t msg);
|
||||
|
||||
extern void usb_autosuspend_work(struct work_struct *work);
|
||||
extern int usb_port_suspend(struct usb_device *dev);
|
||||
extern int usb_port_resume(struct usb_device *dev);
|
||||
extern void usb_autoresume_work(struct work_struct *work);
|
||||
extern int usb_port_suspend(struct usb_device *dev, pm_message_t msg);
|
||||
extern int usb_port_resume(struct usb_device *dev, pm_message_t msg);
|
||||
extern int usb_external_suspend_device(struct usb_device *udev,
|
||||
pm_message_t msg);
|
||||
extern int usb_external_resume_device(struct usb_device *udev);
|
||||
extern int usb_external_resume_device(struct usb_device *udev,
|
||||
pm_message_t msg);
|
||||
|
||||
static inline void usb_pm_lock(struct usb_device *udev)
|
||||
{
|
||||
|
|
@ -63,12 +69,12 @@ static inline void usb_pm_unlock(struct usb_device *udev)
|
|||
|
||||
#else
|
||||
|
||||
static inline int usb_port_suspend(struct usb_device *udev)
|
||||
static inline int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int usb_port_resume(struct usb_device *udev)
|
||||
static inline int usb_port_resume(struct usb_device *udev, pm_message_t msg)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -297,13 +297,34 @@ config USB_S3C2410_DEBUG
|
|||
|
||||
# musb builds in ../musb along with host support
|
||||
config USB_GADGET_MUSB_HDRC
|
||||
boolean "Inventra HDRC USB Peripheral (TI, ...)"
|
||||
boolean "Inventra HDRC USB Peripheral (TI, ADI, ...)"
|
||||
depends on USB_MUSB_HDRC && (USB_MUSB_PERIPHERAL || USB_MUSB_OTG)
|
||||
select USB_GADGET_DUALSPEED
|
||||
select USB_GADGET_SELECTED
|
||||
help
|
||||
This OTG-capable silicon IP is used in dual designs including
|
||||
the TI DaVinci, OMAP 243x, OMAP 343x, and TUSB 6010.
|
||||
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"
|
||||
|
|
@ -377,6 +398,24 @@ config USB_FSL_QE
|
|||
default USB_GADGET
|
||||
select USB_GADGET_SELECTED
|
||||
|
||||
config USB_GADGET_CI13XXX
|
||||
boolean "MIPS USB CI13xxx"
|
||||
depends on PCI
|
||||
select USB_GADGET_DUALSPEED
|
||||
help
|
||||
MIPS USB IP core family device controller
|
||||
Currently it only supports IP part number CI13412
|
||||
|
||||
Say "y" to link the driver statically, or "m" to build a
|
||||
dynamically linked module called "ci13xxx_udc" and force all
|
||||
gadget drivers to also be dynamically linked.
|
||||
|
||||
config USB_CI13XXX
|
||||
tristate
|
||||
depends on USB_GADGET_CI13XXX
|
||||
default USB_GADGET
|
||||
select USB_GADGET_SELECTED
|
||||
|
||||
config USB_GADGET_NET2280
|
||||
boolean "NetChip 228x"
|
||||
depends on PCI
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ obj-$(CONFIG_USB_NET2280) += net2280.o
|
|||
obj-$(CONFIG_USB_AMD5536UDC) += amd5536udc.o
|
||||
obj-$(CONFIG_USB_PXA25X) += pxa25x_udc.o
|
||||
obj-$(CONFIG_USB_PXA27X) += pxa27x_udc.o
|
||||
obj-$(CONFIG_USB_IMX) += imx_udc.o
|
||||
obj-$(CONFIG_USB_GOKU) += goku_udc.o
|
||||
obj-$(CONFIG_USB_OMAP) += omap_udc.o
|
||||
obj-$(CONFIG_USB_LH7A40X) += lh7a40x_udc.o
|
||||
|
|
@ -19,6 +20,7 @@ obj-$(CONFIG_USB_ATMEL_USBA) += atmel_usba_udc.o
|
|||
obj-$(CONFIG_USB_FSL_USB2) += fsl_usb2_udc.o
|
||||
obj-$(CONFIG_USB_M66592) += m66592-udc.o
|
||||
obj-$(CONFIG_USB_FSL_QE) += fsl_qe_udc.o
|
||||
obj-$(CONFIG_USB_CI13XXX) += ci13xxx_udc.o
|
||||
|
||||
#
|
||||
# USB gadget drivers
|
||||
|
|
|
|||
|
|
@ -1474,7 +1474,7 @@ static struct at91_udc controller = {
|
|||
.ep0 = &controller.ep[0].ep,
|
||||
.name = driver_name,
|
||||
.dev = {
|
||||
.bus_id = "gadget",
|
||||
.init_name = "gadget",
|
||||
.release = nop_release,
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1034,7 +1034,7 @@ static struct usba_udc the_udc = {
|
|||
.is_dualspeed = 1,
|
||||
.name = "atmel_usba_udc",
|
||||
.dev = {
|
||||
.bus_id = "gadget",
|
||||
.init_name = "gadget",
|
||||
.release = nop_release,
|
||||
},
|
||||
},
|
||||
|
|
|
|||
2830
drivers/usb/gadget/ci13xxx_udc.c
Normal file
2830
drivers/usb/gadget/ci13xxx_udc.c
Normal file
File diff suppressed because it is too large
Load diff
195
drivers/usb/gadget/ci13xxx_udc.h
Normal file
195
drivers/usb/gadget/ci13xxx_udc.h
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
/*
|
||||
* ci13xxx_udc.h - structures, registers, and macros MIPS USB IP core
|
||||
*
|
||||
* Copyright (C) 2008 Chipidea - MIPS Technologies, Inc. All rights reserved.
|
||||
*
|
||||
* Author: David Lopo
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Description: MIPS USB IP core family device controller
|
||||
* Structures, registers and logging macros
|
||||
*/
|
||||
|
||||
#ifndef _CI13XXX_h_
|
||||
#define _CI13XXX_h_
|
||||
|
||||
/******************************************************************************
|
||||
* DEFINE
|
||||
*****************************************************************************/
|
||||
#define ENDPT_MAX (16)
|
||||
#define CTRL_PAYLOAD_MAX (64)
|
||||
#define RX (0) /* similar to USB_DIR_OUT but can be used as an index */
|
||||
#define TX (1) /* similar to USB_DIR_IN but can be used as an index */
|
||||
|
||||
/******************************************************************************
|
||||
* STRUCTURES
|
||||
*****************************************************************************/
|
||||
/* DMA layout of transfer descriptors */
|
||||
struct ci13xxx_td {
|
||||
/* 0 */
|
||||
u32 next;
|
||||
#define TD_TERMINATE BIT(0)
|
||||
/* 1 */
|
||||
u32 token;
|
||||
#define TD_STATUS (0x00FFUL << 0)
|
||||
#define TD_STATUS_TR_ERR BIT(3)
|
||||
#define TD_STATUS_DT_ERR BIT(5)
|
||||
#define TD_STATUS_HALTED BIT(6)
|
||||
#define TD_STATUS_ACTIVE BIT(7)
|
||||
#define TD_MULTO (0x0003UL << 10)
|
||||
#define TD_IOC BIT(15)
|
||||
#define TD_TOTAL_BYTES (0x7FFFUL << 16)
|
||||
/* 2 */
|
||||
u32 page[5];
|
||||
#define TD_CURR_OFFSET (0x0FFFUL << 0)
|
||||
#define TD_FRAME_NUM (0x07FFUL << 0)
|
||||
#define TD_RESERVED_MASK (0x0FFFUL << 0)
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* DMA layout of queue heads */
|
||||
struct ci13xxx_qh {
|
||||
/* 0 */
|
||||
u32 cap;
|
||||
#define QH_IOS BIT(15)
|
||||
#define QH_MAX_PKT (0x07FFUL << 16)
|
||||
#define QH_ZLT BIT(29)
|
||||
#define QH_MULT (0x0003UL << 30)
|
||||
/* 1 */
|
||||
u32 curr;
|
||||
/* 2 - 8 */
|
||||
struct ci13xxx_td td;
|
||||
/* 9 */
|
||||
u32 RESERVED;
|
||||
struct usb_ctrlrequest setup;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* Extension of usb_request */
|
||||
struct ci13xxx_req {
|
||||
struct usb_request req;
|
||||
unsigned map;
|
||||
struct list_head queue;
|
||||
struct ci13xxx_td *ptr;
|
||||
dma_addr_t dma;
|
||||
};
|
||||
|
||||
/* Extension of usb_ep */
|
||||
struct ci13xxx_ep {
|
||||
struct usb_ep ep;
|
||||
const struct usb_endpoint_descriptor *desc;
|
||||
u8 dir;
|
||||
u8 num;
|
||||
u8 type;
|
||||
char name[16];
|
||||
struct {
|
||||
struct list_head queue;
|
||||
struct ci13xxx_qh *ptr;
|
||||
dma_addr_t dma;
|
||||
} qh[2];
|
||||
struct usb_request *status;
|
||||
int wedge;
|
||||
|
||||
/* global resources */
|
||||
spinlock_t *lock;
|
||||
struct device *device;
|
||||
struct dma_pool *td_pool;
|
||||
};
|
||||
|
||||
/* CI13XXX UDC descriptor & global resources */
|
||||
struct ci13xxx {
|
||||
spinlock_t *lock; /* ctrl register bank access */
|
||||
|
||||
struct dma_pool *qh_pool; /* DMA pool for queue heads */
|
||||
struct dma_pool *td_pool; /* DMA pool for transfer descs */
|
||||
|
||||
struct usb_gadget gadget; /* USB slave device */
|
||||
struct ci13xxx_ep ci13xxx_ep[ENDPT_MAX]; /* extended endpts */
|
||||
|
||||
struct usb_gadget_driver *driver; /* 3rd party gadget driver */
|
||||
};
|
||||
|
||||
/******************************************************************************
|
||||
* REGISTERS
|
||||
*****************************************************************************/
|
||||
/* register size */
|
||||
#define REG_BITS (32)
|
||||
|
||||
/* HCCPARAMS */
|
||||
#define HCCPARAMS_LEN BIT(17)
|
||||
|
||||
/* DCCPARAMS */
|
||||
#define DCCPARAMS_DEN (0x1F << 0)
|
||||
#define DCCPARAMS_DC BIT(7)
|
||||
|
||||
/* TESTMODE */
|
||||
#define TESTMODE_FORCE BIT(0)
|
||||
|
||||
/* USBCMD */
|
||||
#define USBCMD_RS BIT(0)
|
||||
#define USBCMD_RST BIT(1)
|
||||
#define USBCMD_SUTW BIT(13)
|
||||
|
||||
/* USBSTS & USBINTR */
|
||||
#define USBi_UI BIT(0)
|
||||
#define USBi_UEI BIT(1)
|
||||
#define USBi_PCI BIT(2)
|
||||
#define USBi_URI BIT(6)
|
||||
#define USBi_SLI BIT(8)
|
||||
|
||||
/* DEVICEADDR */
|
||||
#define DEVICEADDR_USBADRA BIT(24)
|
||||
#define DEVICEADDR_USBADR (0x7FUL << 25)
|
||||
|
||||
/* PORTSC */
|
||||
#define PORTSC_SUSP BIT(7)
|
||||
#define PORTSC_HSP BIT(9)
|
||||
#define PORTSC_PTC (0x0FUL << 16)
|
||||
|
||||
/* DEVLC */
|
||||
#define DEVLC_PSPD (0x03UL << 25)
|
||||
#define DEVLC_PSPD_HS (0x02UL << 25)
|
||||
|
||||
/* USBMODE */
|
||||
#define USBMODE_CM (0x03UL << 0)
|
||||
#define USBMODE_CM_IDLE (0x00UL << 0)
|
||||
#define USBMODE_CM_DEVICE (0x02UL << 0)
|
||||
#define USBMODE_CM_HOST (0x03UL << 0)
|
||||
#define USBMODE_SLOM BIT(3)
|
||||
|
||||
/* ENDPTCTRL */
|
||||
#define ENDPTCTRL_RXS BIT(0)
|
||||
#define ENDPTCTRL_RXT (0x03UL << 2)
|
||||
#define ENDPTCTRL_RXR BIT(6) /* reserved for port 0 */
|
||||
#define ENDPTCTRL_RXE BIT(7)
|
||||
#define ENDPTCTRL_TXS BIT(16)
|
||||
#define ENDPTCTRL_TXT (0x03UL << 18)
|
||||
#define ENDPTCTRL_TXR BIT(22) /* reserved for port 0 */
|
||||
#define ENDPTCTRL_TXE BIT(23)
|
||||
|
||||
/******************************************************************************
|
||||
* LOGGING
|
||||
*****************************************************************************/
|
||||
#define ci13xxx_printk(level, format, args...) \
|
||||
do { \
|
||||
if (_udc == NULL) \
|
||||
printk(level "[%s] " format "\n", __func__, ## args); \
|
||||
else \
|
||||
dev_printk(level, _udc->gadget.dev.parent, \
|
||||
"[%s] " format "\n", __func__, ## args); \
|
||||
} while (0)
|
||||
|
||||
#define err(format, args...) ci13xxx_printk(KERN_ERR, format, ## args)
|
||||
#define warn(format, args...) ci13xxx_printk(KERN_WARNING, format, ## args)
|
||||
#define info(format, args...) ci13xxx_printk(KERN_INFO, format, ## args)
|
||||
|
||||
#ifdef TRACE
|
||||
#define trace(format, args...) ci13xxx_printk(KERN_DEBUG, format, ## args)
|
||||
#define dbg_trace(format, args...) dev_dbg(dev, format, ##args)
|
||||
#else
|
||||
#define trace(format, args...) do {} while (0)
|
||||
#define dbg_trace(format, args...) do {} while (0)
|
||||
#endif
|
||||
|
||||
#endif /* _CI13XXX_h_ */
|
||||
|
|
@ -161,7 +161,7 @@ ep_matches (
|
|||
/* report address */
|
||||
desc->bEndpointAddress &= USB_DIR_IN;
|
||||
if (isdigit (ep->name [2])) {
|
||||
u8 num = simple_strtol (&ep->name [2], NULL, 10);
|
||||
u8 num = simple_strtoul (&ep->name [2], NULL, 10);
|
||||
desc->bEndpointAddress |= num;
|
||||
#ifdef MANY_ENDPOINTS
|
||||
} else if (desc->bEndpointAddress & USB_DIR_IN) {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* file_storage.c -- File-backed USB Storage Gadget, for USB development
|
||||
*
|
||||
* Copyright (C) 2003-2007 Alan Stern
|
||||
* Copyright (C) 2003-2008 Alan Stern
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
|
|
@ -38,16 +38,17 @@
|
|||
|
||||
/*
|
||||
* The File-backed Storage Gadget acts as a USB Mass Storage device,
|
||||
* appearing to the host as a disk drive. In addition to providing an
|
||||
* example of a genuinely useful gadget driver for a USB device, it also
|
||||
* illustrates a technique of double-buffering for increased throughput.
|
||||
* Last but not least, it gives an easy way to probe the behavior of the
|
||||
* Mass Storage drivers in a USB host.
|
||||
* appearing to the host as a disk drive or as a CD-ROM drive. In addition
|
||||
* to providing an example of a genuinely useful gadget driver for a USB
|
||||
* device, it also illustrates a technique of double-buffering for increased
|
||||
* throughput. Last but not least, it gives an easy way to probe the
|
||||
* behavior of the Mass Storage drivers in a USB host.
|
||||
*
|
||||
* Backing storage is provided by a regular file or a block device, specified
|
||||
* by the "file" module parameter. Access can be limited to read-only by
|
||||
* setting the optional "ro" module parameter. The gadget will indicate that
|
||||
* it has removable media if the optional "removable" module parameter is set.
|
||||
* setting the optional "ro" module parameter. (For CD-ROM emulation,
|
||||
* access is always read-only.) The gadget will indicate that it has
|
||||
* removable media if the optional "removable" module parameter is set.
|
||||
*
|
||||
* The gadget supports the Control-Bulk (CB), Control-Bulk-Interrupt (CBI),
|
||||
* and Bulk-Only (also known as Bulk-Bulk-Bulk or BBB) transports, selected
|
||||
|
|
@ -64,7 +65,12 @@
|
|||
* The default number of LUNs is taken from the number of "file" elements;
|
||||
* it is 1 if "file" is not given. If "removable" is not set then a backing
|
||||
* file must be specified for each LUN. If it is set, then an unspecified
|
||||
* or empty backing filename means the LUN's medium is not loaded.
|
||||
* or empty backing filename means the LUN's medium is not loaded. Ideally
|
||||
* each LUN would be settable independently as a disk drive or a CD-ROM
|
||||
* drive, but currently all LUNs have to be the same type. The CD-ROM
|
||||
* emulation includes a single data track and no audio tracks; hence there
|
||||
* need be only one backing file per LUN. Note also that the CD-ROM block
|
||||
* length is set to 512 rather than the more common value 2048.
|
||||
*
|
||||
* Requirements are modest; only a bulk-in and a bulk-out endpoint are
|
||||
* needed (an interrupt-out endpoint is also needed for CBI). The memory
|
||||
|
|
@ -91,6 +97,8 @@
|
|||
* USB device controller (usually true),
|
||||
* boolean to permit the driver to halt
|
||||
* bulk endpoints
|
||||
* cdrom Default false, boolean for whether to emulate
|
||||
* a CD-ROM drive
|
||||
* transport=XXX Default BBB, transport name (CB, CBI, or BBB)
|
||||
* protocol=YYY Default SCSI, protocol name (RBC, 8020 or
|
||||
* ATAPI, QIC, UFI, 8070, or SCSI;
|
||||
|
|
@ -103,15 +111,16 @@
|
|||
* PAGE_CACHE_SIZE)
|
||||
*
|
||||
* If CONFIG_USB_FILE_STORAGE_TEST is not set, only the "file", "ro",
|
||||
* "removable", "luns", and "stall" options are available; default values
|
||||
* are used for everything else.
|
||||
* "removable", "luns", "stall", and "cdrom" options are available; default
|
||||
* values are used for everything else.
|
||||
*
|
||||
* The pathnames of the backing files and the ro settings are available in
|
||||
* the attribute files "file" and "ro" in the lun<n> subdirectory of the
|
||||
* gadget's sysfs directory. If the "removable" option is set, writing to
|
||||
* these files will simulate ejecting/loading the medium (writing an empty
|
||||
* line means eject) and adjusting a write-enable tab. Changes to the ro
|
||||
* setting are not allowed when the medium is loaded.
|
||||
* setting are not allowed when the medium is loaded or if CD-ROM emulation
|
||||
* is being used.
|
||||
*
|
||||
* This gadget driver is heavily based on "Gadget Zero" by David Brownell.
|
||||
* The driver's SCSI command interface was based on the "Information
|
||||
|
|
@ -261,7 +270,7 @@
|
|||
|
||||
#define DRIVER_DESC "File-backed Storage Gadget"
|
||||
#define DRIVER_NAME "g_file_storage"
|
||||
#define DRIVER_VERSION "7 August 2007"
|
||||
#define DRIVER_VERSION "20 November 2008"
|
||||
|
||||
static const char longname[] = DRIVER_DESC;
|
||||
static const char shortname[] = DRIVER_NAME;
|
||||
|
|
@ -341,6 +350,7 @@ static struct {
|
|||
|
||||
int removable;
|
||||
int can_stall;
|
||||
int cdrom;
|
||||
|
||||
char *transport_parm;
|
||||
char *protocol_parm;
|
||||
|
|
@ -359,6 +369,7 @@ static struct {
|
|||
.protocol_parm = "SCSI",
|
||||
.removable = 0,
|
||||
.can_stall = 1,
|
||||
.cdrom = 0,
|
||||
.vendor = DRIVER_VENDOR_ID,
|
||||
.product = DRIVER_PRODUCT_ID,
|
||||
.release = 0xffff, // Use controller chip type
|
||||
|
|
@ -382,6 +393,9 @@ MODULE_PARM_DESC(removable, "true to simulate removable media");
|
|||
module_param_named(stall, mod_data.can_stall, bool, S_IRUGO);
|
||||
MODULE_PARM_DESC(stall, "false to prevent bulk stalls");
|
||||
|
||||
module_param_named(cdrom, mod_data.cdrom, bool, S_IRUGO);
|
||||
MODULE_PARM_DESC(cdrom, "true to emulate cdrom instead of disk");
|
||||
|
||||
|
||||
/* In the non-TEST version, only the module parameters listed above
|
||||
* are available. */
|
||||
|
|
@ -411,6 +425,10 @@ MODULE_PARM_DESC(buflen, "I/O buffer size");
|
|||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* SCSI device types */
|
||||
#define TYPE_DISK 0x00
|
||||
#define TYPE_CDROM 0x05
|
||||
|
||||
/* USB protocol value = the transport method */
|
||||
#define USB_PR_CBI 0x00 // Control/Bulk/Interrupt
|
||||
#define USB_PR_CB 0x01 // Control/Bulk w/o interrupt
|
||||
|
|
@ -487,6 +505,8 @@ struct interrupt_data {
|
|||
#define SC_READ_12 0xa8
|
||||
#define SC_READ_CAPACITY 0x25
|
||||
#define SC_READ_FORMAT_CAPACITIES 0x23
|
||||
#define SC_READ_HEADER 0x44
|
||||
#define SC_READ_TOC 0x43
|
||||
#define SC_RELEASE 0x17
|
||||
#define SC_REQUEST_SENSE 0x03
|
||||
#define SC_RESERVE 0x16
|
||||
|
|
@ -2006,23 +2026,28 @@ static int do_inquiry(struct fsg_dev *fsg, struct fsg_buffhd *bh)
|
|||
u8 *buf = (u8 *) bh->buf;
|
||||
|
||||
static char vendor_id[] = "Linux ";
|
||||
static char product_id[] = "File-Stor Gadget";
|
||||
static char product_disk_id[] = "File-Stor Gadget";
|
||||
static char product_cdrom_id[] = "File-CD Gadget ";
|
||||
|
||||
if (!fsg->curlun) { // Unsupported LUNs are okay
|
||||
fsg->bad_lun_okay = 1;
|
||||
memset(buf, 0, 36);
|
||||
buf[0] = 0x7f; // Unsupported, no device-type
|
||||
buf[4] = 31; // Additional length
|
||||
return 36;
|
||||
}
|
||||
|
||||
memset(buf, 0, 8); // Non-removable, direct-access device
|
||||
memset(buf, 0, 8);
|
||||
buf[0] = (mod_data.cdrom ? TYPE_CDROM : TYPE_DISK);
|
||||
if (mod_data.removable)
|
||||
buf[1] = 0x80;
|
||||
buf[2] = 2; // ANSI SCSI level 2
|
||||
buf[3] = 2; // SCSI-2 INQUIRY data format
|
||||
buf[4] = 31; // Additional length
|
||||
// No special options
|
||||
sprintf(buf + 8, "%-8s%-16s%04x", vendor_id, product_id,
|
||||
sprintf(buf + 8, "%-8s%-16s%04x", vendor_id,
|
||||
(mod_data.cdrom ? product_cdrom_id :
|
||||
product_disk_id),
|
||||
mod_data.release);
|
||||
return 36;
|
||||
}
|
||||
|
|
@ -2101,6 +2126,75 @@ static int do_read_capacity(struct fsg_dev *fsg, struct fsg_buffhd *bh)
|
|||
}
|
||||
|
||||
|
||||
static void store_cdrom_address(u8 *dest, int msf, u32 addr)
|
||||
{
|
||||
if (msf) {
|
||||
/* Convert to Minutes-Seconds-Frames */
|
||||
addr >>= 2; /* Convert to 2048-byte frames */
|
||||
addr += 2*75; /* Lead-in occupies 2 seconds */
|
||||
dest[3] = addr % 75; /* Frames */
|
||||
addr /= 75;
|
||||
dest[2] = addr % 60; /* Seconds */
|
||||
addr /= 60;
|
||||
dest[1] = addr; /* Minutes */
|
||||
dest[0] = 0; /* Reserved */
|
||||
} else {
|
||||
/* Absolute sector */
|
||||
put_be32(dest, addr);
|
||||
}
|
||||
}
|
||||
|
||||
static int do_read_header(struct fsg_dev *fsg, struct fsg_buffhd *bh)
|
||||
{
|
||||
struct lun *curlun = fsg->curlun;
|
||||
int msf = fsg->cmnd[1] & 0x02;
|
||||
u32 lba = get_be32(&fsg->cmnd[2]);
|
||||
u8 *buf = (u8 *) bh->buf;
|
||||
|
||||
if ((fsg->cmnd[1] & ~0x02) != 0) { /* Mask away MSF */
|
||||
curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
|
||||
return -EINVAL;
|
||||
}
|
||||
if (lba >= curlun->num_sectors) {
|
||||
curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
memset(buf, 0, 8);
|
||||
buf[0] = 0x01; /* 2048 bytes of user data, rest is EC */
|
||||
store_cdrom_address(&buf[4], msf, lba);
|
||||
return 8;
|
||||
}
|
||||
|
||||
|
||||
static int do_read_toc(struct fsg_dev *fsg, struct fsg_buffhd *bh)
|
||||
{
|
||||
struct lun *curlun = fsg->curlun;
|
||||
int msf = fsg->cmnd[1] & 0x02;
|
||||
int start_track = fsg->cmnd[6];
|
||||
u8 *buf = (u8 *) bh->buf;
|
||||
|
||||
if ((fsg->cmnd[1] & ~0x02) != 0 || /* Mask away MSF */
|
||||
start_track > 1) {
|
||||
curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
memset(buf, 0, 20);
|
||||
buf[1] = (20-2); /* TOC data length */
|
||||
buf[2] = 1; /* First track number */
|
||||
buf[3] = 1; /* Last track number */
|
||||
buf[5] = 0x16; /* Data track, copying allowed */
|
||||
buf[6] = 0x01; /* Only track is number 1 */
|
||||
store_cdrom_address(&buf[8], msf, 0);
|
||||
|
||||
buf[13] = 0x16; /* Lead-out track is data */
|
||||
buf[14] = 0xAA; /* Lead-out track number */
|
||||
store_cdrom_address(&buf[16], msf, curlun->num_sectors);
|
||||
return 20;
|
||||
}
|
||||
|
||||
|
||||
static int do_mode_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh)
|
||||
{
|
||||
struct lun *curlun = fsg->curlun;
|
||||
|
|
@ -2848,6 +2942,26 @@ static int do_scsi_command(struct fsg_dev *fsg)
|
|||
reply = do_read_capacity(fsg, bh);
|
||||
break;
|
||||
|
||||
case SC_READ_HEADER:
|
||||
if (!mod_data.cdrom)
|
||||
goto unknown_cmnd;
|
||||
fsg->data_size_from_cmnd = get_be16(&fsg->cmnd[7]);
|
||||
if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST,
|
||||
(3<<7) | (0x1f<<1), 1,
|
||||
"READ HEADER")) == 0)
|
||||
reply = do_read_header(fsg, bh);
|
||||
break;
|
||||
|
||||
case SC_READ_TOC:
|
||||
if (!mod_data.cdrom)
|
||||
goto unknown_cmnd;
|
||||
fsg->data_size_from_cmnd = get_be16(&fsg->cmnd[7]);
|
||||
if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST,
|
||||
(7<<6) | (1<<1), 1,
|
||||
"READ TOC")) == 0)
|
||||
reply = do_read_toc(fsg, bh);
|
||||
break;
|
||||
|
||||
case SC_READ_FORMAT_CAPACITIES:
|
||||
fsg->data_size_from_cmnd = get_be16(&fsg->cmnd[7]);
|
||||
if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST,
|
||||
|
|
@ -2933,6 +3047,7 @@ static int do_scsi_command(struct fsg_dev *fsg)
|
|||
// Fall through
|
||||
|
||||
default:
|
||||
unknown_cmnd:
|
||||
fsg->data_size_from_cmnd = 0;
|
||||
sprintf(unknown, "Unknown x%02x", fsg->cmnd[0]);
|
||||
if ((reply = check_command(fsg, fsg->cmnd_size,
|
||||
|
|
@ -3498,6 +3613,7 @@ static int open_backing_file(struct lun *curlun, const char *filename)
|
|||
struct inode *inode = NULL;
|
||||
loff_t size;
|
||||
loff_t num_sectors;
|
||||
loff_t min_sectors;
|
||||
|
||||
/* R/W if we can, R/O if we must */
|
||||
ro = curlun->ro;
|
||||
|
|
@ -3541,8 +3657,19 @@ static int open_backing_file(struct lun *curlun, const char *filename)
|
|||
rc = (int) size;
|
||||
goto out;
|
||||
}
|
||||
num_sectors = size >> 9; // File size in 512-byte sectors
|
||||
if (num_sectors == 0) {
|
||||
num_sectors = size >> 9; // File size in 512-byte blocks
|
||||
min_sectors = 1;
|
||||
if (mod_data.cdrom) {
|
||||
num_sectors &= ~3; // Reduce to a multiple of 2048
|
||||
min_sectors = 300*4; // Smallest track is 300 frames
|
||||
if (num_sectors >= 256*60*75*4) {
|
||||
num_sectors = (256*60*75 - 1) * 4;
|
||||
LINFO(curlun, "file too big: %s\n", filename);
|
||||
LINFO(curlun, "using only first %d blocks\n",
|
||||
(int) num_sectors);
|
||||
}
|
||||
}
|
||||
if (num_sectors < min_sectors) {
|
||||
LINFO(curlun, "file too small: %s\n", filename);
|
||||
rc = -ETOOSMALL;
|
||||
goto out;
|
||||
|
|
@ -3845,9 +3972,12 @@ static int __init fsg_bind(struct usb_gadget *gadget)
|
|||
goto out;
|
||||
|
||||
if (mod_data.removable) { // Enable the store_xxx attributes
|
||||
dev_attr_ro.attr.mode = dev_attr_file.attr.mode = 0644;
|
||||
dev_attr_ro.store = store_ro;
|
||||
dev_attr_file.attr.mode = 0644;
|
||||
dev_attr_file.store = store_file;
|
||||
if (!mod_data.cdrom) {
|
||||
dev_attr_ro.attr.mode = 0644;
|
||||
dev_attr_ro.store = store_ro;
|
||||
}
|
||||
}
|
||||
|
||||
/* Find out how many LUNs there should be */
|
||||
|
|
@ -3872,6 +4002,8 @@ static int __init fsg_bind(struct usb_gadget *gadget)
|
|||
for (i = 0; i < fsg->nluns; ++i) {
|
||||
curlun = &fsg->luns[i];
|
||||
curlun->ro = mod_data.ro[i];
|
||||
if (mod_data.cdrom)
|
||||
curlun->ro = 1;
|
||||
curlun->dev.release = lun_release;
|
||||
curlun->dev.parent = &gadget->dev;
|
||||
curlun->dev.driver = &fsg_driver.driver;
|
||||
|
|
@ -4031,9 +4163,9 @@ static int __init fsg_bind(struct usb_gadget *gadget)
|
|||
mod_data.protocol_name, mod_data.protocol_type);
|
||||
DBG(fsg, "VendorID=x%04x, ProductID=x%04x, Release=x%04x\n",
|
||||
mod_data.vendor, mod_data.product, mod_data.release);
|
||||
DBG(fsg, "removable=%d, stall=%d, buflen=%u\n",
|
||||
DBG(fsg, "removable=%d, stall=%d, cdrom=%d, buflen=%u\n",
|
||||
mod_data.removable, mod_data.can_stall,
|
||||
mod_data.buflen);
|
||||
mod_data.cdrom, mod_data.buflen);
|
||||
DBG(fsg, "I/O thread pid: %d\n", task_pid_nr(fsg->thread_task));
|
||||
|
||||
set_bit(REGISTERED, &fsg->atomic_bitflags);
|
||||
|
|
@ -4050,6 +4182,7 @@ out:
|
|||
fsg->state = FSG_STATE_TERMINATED; // The thread is dead
|
||||
fsg_unbind(gadget);
|
||||
close_all_backing_files(fsg);
|
||||
complete(&fsg->thread_notifier);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
#include <linux/ioport.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
|
@ -370,6 +371,9 @@ static int qe_ep_bd_init(struct qe_udc *udc, unsigned char pipe_num)
|
|||
/* alloc multi-ram for BD rings and set the ep parameters */
|
||||
tmp_addr = cpm_muram_alloc(sizeof(struct qe_bd) * (bdring_len +
|
||||
USB_BDRING_LEN_TX), QE_ALIGNMENT_OF_BD);
|
||||
if (IS_ERR_VALUE(tmp_addr))
|
||||
return -ENOMEM;
|
||||
|
||||
out_be16(&epparam->rbase, (u16)tmp_addr);
|
||||
out_be16(&epparam->tbase, (u16)(tmp_addr +
|
||||
(sizeof(struct qe_bd) * bdring_len)));
|
||||
|
|
@ -689,7 +693,7 @@ en_done2:
|
|||
en_done1:
|
||||
spin_unlock_irqrestore(&udc->lock, flags);
|
||||
en_done:
|
||||
dev_dbg(udc->dev, "failed to initialize %s\n", ep->ep.name);
|
||||
dev_err(udc->dev, "failed to initialize %s\n", ep->ep.name);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
|
|
@ -2408,6 +2412,8 @@ static struct qe_udc __devinit *qe_udc_config(struct of_device *ofdev)
|
|||
tmp_addr = cpm_muram_alloc((USB_MAX_ENDPOINTS *
|
||||
sizeof(struct usb_ep_para)),
|
||||
USB_EP_PARA_ALIGNMENT);
|
||||
if (IS_ERR_VALUE(tmp_addr))
|
||||
goto cleanup;
|
||||
|
||||
for (i = 0; i < USB_MAX_ENDPOINTS; i++) {
|
||||
out_be16(&usbpram->epptr[i], (u16)tmp_addr);
|
||||
|
|
@ -2513,7 +2519,7 @@ static int __devinit qe_udc_probe(struct of_device *ofdev,
|
|||
/* Initialize the udc structure including QH member and other member */
|
||||
udc_controller = qe_udc_config(ofdev);
|
||||
if (!udc_controller) {
|
||||
dev_dbg(&ofdev->dev, "udc_controll is NULL\n");
|
||||
dev_err(&ofdev->dev, "failed to initialize\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
|
|
@ -2545,7 +2551,7 @@ static int __devinit qe_udc_probe(struct of_device *ofdev,
|
|||
|
||||
device_initialize(&udc_controller->gadget.dev);
|
||||
|
||||
strcpy(udc_controller->gadget.dev.bus_id, "gadget");
|
||||
dev_set_name(&udc_controller->gadget.dev, "gadget");
|
||||
|
||||
udc_controller->gadget.dev.release = qe_udc_release;
|
||||
udc_controller->gadget.dev.parent = &ofdev->dev;
|
||||
|
|
@ -2568,7 +2574,7 @@ static int __devinit qe_udc_probe(struct of_device *ofdev,
|
|||
/* create a buf for ZLP send, need to remain zeroed */
|
||||
udc_controller->nullbuf = kzalloc(256, GFP_KERNEL);
|
||||
if (udc_controller->nullbuf == NULL) {
|
||||
dev_dbg(udc_controller->dev, "cannot alloc nullbuf\n");
|
||||
dev_err(udc_controller->dev, "cannot alloc nullbuf\n");
|
||||
ret = -ENOMEM;
|
||||
goto err3;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -110,7 +110,6 @@
|
|||
#define gadget_is_at91(g) 0
|
||||
#endif
|
||||
|
||||
/* status unclear */
|
||||
#ifdef CONFIG_USB_GADGET_IMX
|
||||
#define gadget_is_imx(g) !strcmp("imx_udc", (g)->name)
|
||||
#else
|
||||
|
|
@ -158,6 +157,11 @@
|
|||
#define gadget_is_fsl_qe(g) 0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_GADGET_CI13XXX
|
||||
#define gadget_is_ci13xxx(g) (!strcmp("ci13xxx_udc", (g)->name))
|
||||
#else
|
||||
#define gadget_is_ci13xxx(g) 0
|
||||
#endif
|
||||
|
||||
// CONFIG_USB_GADGET_SX2
|
||||
// CONFIG_USB_GADGET_AU1X00
|
||||
|
|
@ -225,6 +229,8 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget)
|
|||
return 0x21;
|
||||
else if (gadget_is_fsl_qe(gadget))
|
||||
return 0x22;
|
||||
else if (gadget_is_ci13xxx(gadget))
|
||||
return 0x23;
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1349,7 +1349,7 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver)
|
|||
int retval;
|
||||
|
||||
if (!driver
|
||||
|| driver->speed != USB_SPEED_FULL
|
||||
|| driver->speed < USB_SPEED_FULL
|
||||
|| !driver->bind
|
||||
|| !driver->disconnect
|
||||
|| !driver->setup)
|
||||
|
|
|
|||
1516
drivers/usb/gadget/imx_udc.c
Normal file
1516
drivers/usb/gadget/imx_udc.c
Normal file
File diff suppressed because it is too large
Load diff
344
drivers/usb/gadget/imx_udc.h
Normal file
344
drivers/usb/gadget/imx_udc.h
Normal file
|
|
@ -0,0 +1,344 @@
|
|||
/*
|
||||
* Copyright (C) 2005 Mike Lee(eemike@gmail.com)
|
||||
*
|
||||
* This udc driver is now under testing and code is based on pxa2xx_udc.h
|
||||
* Please use it with your own risk!
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_USB_GADGET_IMX_H
|
||||
#define __LINUX_USB_GADGET_IMX_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
/* Helper macros */
|
||||
#define EP_NO(ep) ((ep->bEndpointAddress) & ~USB_DIR_IN) /* IN:1, OUT:0 */
|
||||
#define EP_DIR(ep) ((ep->bEndpointAddress) & USB_DIR_IN ? 1 : 0)
|
||||
#define irq_to_ep(irq) (((irq) >= USBD_INT0) || ((irq) <= USBD_INT6) ? ((irq) - USBD_INT0) : (USBD_INT6)) /*should not happen*/
|
||||
#define ep_to_irq(ep) (EP_NO((ep)) + USBD_INT0)
|
||||
#define IMX_USB_NB_EP 6
|
||||
|
||||
/* Driver structures */
|
||||
struct imx_request {
|
||||
struct usb_request req;
|
||||
struct list_head queue;
|
||||
unsigned int in_use;
|
||||
};
|
||||
|
||||
enum ep0_state {
|
||||
EP0_IDLE,
|
||||
EP0_IN_DATA_PHASE,
|
||||
EP0_OUT_DATA_PHASE,
|
||||
EP0_CONFIG,
|
||||
EP0_STALL,
|
||||
};
|
||||
|
||||
struct imx_ep_struct {
|
||||
struct usb_ep ep;
|
||||
struct imx_udc_struct *imx_usb;
|
||||
struct list_head queue;
|
||||
unsigned char stopped;
|
||||
unsigned char fifosize;
|
||||
unsigned char bEndpointAddress;
|
||||
unsigned char bmAttributes;
|
||||
};
|
||||
|
||||
struct imx_udc_struct {
|
||||
struct usb_gadget gadget;
|
||||
struct usb_gadget_driver *driver;
|
||||
struct device *dev;
|
||||
struct imx_ep_struct imx_ep[IMX_USB_NB_EP];
|
||||
struct clk *clk;
|
||||
enum ep0_state ep0state;
|
||||
struct resource *res;
|
||||
void __iomem *base;
|
||||
unsigned char set_config;
|
||||
int cfg,
|
||||
intf,
|
||||
alt,
|
||||
usbd_int[7];
|
||||
};
|
||||
|
||||
/* USB registers */
|
||||
#define USB_FRAME (0x00) /* USB frame */
|
||||
#define USB_SPEC (0x04) /* USB Spec */
|
||||
#define USB_STAT (0x08) /* USB Status */
|
||||
#define USB_CTRL (0x0C) /* USB Control */
|
||||
#define USB_DADR (0x10) /* USB Desc RAM addr */
|
||||
#define USB_DDAT (0x14) /* USB Desc RAM/EP buffer data */
|
||||
#define USB_INTR (0x18) /* USB interrupt */
|
||||
#define USB_MASK (0x1C) /* USB Mask */
|
||||
#define USB_ENAB (0x24) /* USB Enable */
|
||||
#define USB_EP_STAT(x) (0x30 + (x*0x30)) /* USB status/control */
|
||||
#define USB_EP_INTR(x) (0x34 + (x*0x30)) /* USB interrupt */
|
||||
#define USB_EP_MASK(x) (0x38 + (x*0x30)) /* USB mask */
|
||||
#define USB_EP_FDAT(x) (0x3C + (x*0x30)) /* USB FIFO data */
|
||||
#define USB_EP_FDAT0(x) (0x3C + (x*0x30)) /* USB FIFO data */
|
||||
#define USB_EP_FDAT1(x) (0x3D + (x*0x30)) /* USB FIFO data */
|
||||
#define USB_EP_FDAT2(x) (0x3E + (x*0x30)) /* USB FIFO data */
|
||||
#define USB_EP_FDAT3(x) (0x3F + (x*0x30)) /* USB FIFO data */
|
||||
#define USB_EP_FSTAT(x) (0x40 + (x*0x30)) /* USB FIFO status */
|
||||
#define USB_EP_FCTRL(x) (0x44 + (x*0x30)) /* USB FIFO control */
|
||||
#define USB_EP_LRFP(x) (0x48 + (x*0x30)) /* USB last read frame pointer */
|
||||
#define USB_EP_LWFP(x) (0x4C + (x*0x30)) /* USB last write frame pointer */
|
||||
#define USB_EP_FALRM(x) (0x50 + (x*0x30)) /* USB FIFO alarm */
|
||||
#define USB_EP_FRDP(x) (0x54 + (x*0x30)) /* USB FIFO read pointer */
|
||||
#define USB_EP_FWRP(x) (0x58 + (x*0x30)) /* USB FIFO write pointer */
|
||||
/* USB Control Register Bit Fields.*/
|
||||
#define CTRL_CMDOVER (1<<6) /* UDC status */
|
||||
#define CTRL_CMDERROR (1<<5) /* UDC status */
|
||||
#define CTRL_FE_ENA (1<<3) /* Enable Font End logic */
|
||||
#define CTRL_UDC_RST (1<<2) /* UDC reset */
|
||||
#define CTRL_AFE_ENA (1<<1) /* Analog Font end enable */
|
||||
#define CTRL_RESUME (1<<0) /* UDC resume */
|
||||
/* USB Status Register Bit Fields.*/
|
||||
#define STAT_RST (1<<8)
|
||||
#define STAT_SUSP (1<<7)
|
||||
#define STAT_CFG (3<<5)
|
||||
#define STAT_INTF (3<<3)
|
||||
#define STAT_ALTSET (7<<0)
|
||||
/* USB Interrupt Status/Mask Registers Bit fields */
|
||||
#define INTR_WAKEUP (1<<31) /* Wake up Interrupt */
|
||||
#define INTR_MSOF (1<<7) /* Missed Start of Frame */
|
||||
#define INTR_SOF (1<<6) /* Start of Frame */
|
||||
#define INTR_RESET_STOP (1<<5) /* Reset Signaling stop */
|
||||
#define INTR_RESET_START (1<<4) /* Reset Signaling start */
|
||||
#define INTR_RESUME (1<<3) /* Suspend to resume */
|
||||
#define INTR_SUSPEND (1<<2) /* Active to suspend */
|
||||
#define INTR_FRAME_MATCH (1<<1) /* Frame matched */
|
||||
#define INTR_CFG_CHG (1<<0) /* Configuration change occurred */
|
||||
/* USB Enable Register Bit Fields.*/
|
||||
#define ENAB_RST (1<<31) /* Reset USB modules */
|
||||
#define ENAB_ENAB (1<<30) /* Enable USB modules*/
|
||||
#define ENAB_SUSPEND (1<<29) /* Suspend USB modules */
|
||||
#define ENAB_ENDIAN (1<<28) /* Endian of USB modules */
|
||||
#define ENAB_PWRMD (1<<0) /* Power mode of USB modules */
|
||||
/* USB Descriptor Ram Address Register bit fields */
|
||||
#define DADR_CFG (1<<31) /* Configuration */
|
||||
#define DADR_BSY (1<<30) /* Busy status */
|
||||
#define DADR_DADR (0x1FF) /* Descriptor Ram Address */
|
||||
/* USB Descriptor RAM/Endpoint Buffer Data Register bit fields */
|
||||
#define DDAT_DDAT (0xFF) /* Descriptor Endpoint Buffer */
|
||||
/* USB Endpoint Status Register bit fields */
|
||||
#define EPSTAT_BCOUNT (0x7F<<16) /* Endpoint FIFO byte count */
|
||||
#define EPSTAT_SIP (1<<8) /* Endpoint setup in progress */
|
||||
#define EPSTAT_DIR (1<<7) /* Endpoint transfer direction */
|
||||
#define EPSTAT_MAX (3<<5) /* Endpoint Max packet size */
|
||||
#define EPSTAT_TYP (3<<3) /* Endpoint type */
|
||||
#define EPSTAT_ZLPS (1<<2) /* Send zero length packet */
|
||||
#define EPSTAT_FLUSH (1<<1) /* Endpoint FIFO Flush */
|
||||
#define EPSTAT_STALL (1<<0) /* Force stall */
|
||||
/* USB Endpoint FIFO Status Register bit fields */
|
||||
#define FSTAT_FRAME_STAT (0xF<<24) /* Frame status bit [0-3] */
|
||||
#define FSTAT_ERR (1<<22) /* FIFO error */
|
||||
#define FSTAT_UF (1<<21) /* FIFO underflow */
|
||||
#define FSTAT_OF (1<<20) /* FIFO overflow */
|
||||
#define FSTAT_FR (1<<19) /* FIFO frame ready */
|
||||
#define FSTAT_FULL (1<<18) /* FIFO full */
|
||||
#define FSTAT_ALRM (1<<17) /* FIFO alarm */
|
||||
#define FSTAT_EMPTY (1<<16) /* FIFO empty */
|
||||
/* USB Endpoint FIFO Control Register bit fields */
|
||||
#define FCTRL_WFR (1<<29) /* Write frame end */
|
||||
/* USB Endpoint Interrupt Status Regsiter bit fields */
|
||||
#define EPINTR_FIFO_FULL (1<<8) /* fifo full */
|
||||
#define EPINTR_FIFO_EMPTY (1<<7) /* fifo empty */
|
||||
#define EPINTR_FIFO_ERROR (1<<6) /* fifo error */
|
||||
#define EPINTR_FIFO_HIGH (1<<5) /* fifo high */
|
||||
#define EPINTR_FIFO_LOW (1<<4) /* fifo low */
|
||||
#define EPINTR_MDEVREQ (1<<3) /* multi Device request */
|
||||
#define EPINTR_EOT (1<<2) /* fifo end of transfer */
|
||||
#define EPINTR_DEVREQ (1<<1) /* Device request */
|
||||
#define EPINTR_EOF (1<<0) /* fifo end of frame */
|
||||
|
||||
/* Debug macros */
|
||||
#ifdef DEBUG
|
||||
|
||||
/* #define DEBUG_REQ */
|
||||
/* #define DEBUG_TRX */
|
||||
/* #define DEBUG_INIT */
|
||||
/* #define DEBUG_EP0 */
|
||||
/* #define DEBUG_EPX */
|
||||
/* #define DEBUG_IRQ */
|
||||
/* #define DEBUG_EPIRQ */
|
||||
/* #define DEBUG_DUMP */
|
||||
#define DEBUG_ERR
|
||||
|
||||
#ifdef DEBUG_REQ
|
||||
#define D_REQ(dev, args...) dev_dbg(dev, ## args)
|
||||
#else
|
||||
#define D_REQ(dev, args...) do {} while (0)
|
||||
#endif /* DEBUG_REQ */
|
||||
|
||||
#ifdef DEBUG_TRX
|
||||
#define D_TRX(dev, args...) dev_dbg(dev, ## args)
|
||||
#else
|
||||
#define D_TRX(dev, args...) do {} while (0)
|
||||
#endif /* DEBUG_TRX */
|
||||
|
||||
#ifdef DEBUG_INIT
|
||||
#define D_INI(dev, args...) dev_dbg(dev, ## args)
|
||||
#else
|
||||
#define D_INI(dev, args...) do {} while (0)
|
||||
#endif /* DEBUG_INIT */
|
||||
|
||||
#ifdef DEBUG_EP0
|
||||
static const char *state_name[] = {
|
||||
"EP0_IDLE",
|
||||
"EP0_IN_DATA_PHASE",
|
||||
"EP0_OUT_DATA_PHASE",
|
||||
"EP0_CONFIG",
|
||||
"EP0_STALL"
|
||||
};
|
||||
#define D_EP0(dev, args...) dev_dbg(dev, ## args)
|
||||
#else
|
||||
#define D_EP0(dev, args...) do {} while (0)
|
||||
#endif /* DEBUG_EP0 */
|
||||
|
||||
#ifdef DEBUG_EPX
|
||||
#define D_EPX(dev, args...) dev_dbg(dev, ## args)
|
||||
#else
|
||||
#define D_EPX(dev, args...) do {} while (0)
|
||||
#endif /* DEBUG_EP0 */
|
||||
|
||||
#ifdef DEBUG_IRQ
|
||||
static void dump_intr(const char *label, int irqreg, struct device *dev)
|
||||
{
|
||||
dev_dbg(dev, "<%s> USB_INTR=[%s%s%s%s%s%s%s%s%s]\n", label,
|
||||
(irqreg & INTR_WAKEUP) ? " wake" : "",
|
||||
(irqreg & INTR_MSOF) ? " msof" : "",
|
||||
(irqreg & INTR_SOF) ? " sof" : "",
|
||||
(irqreg & INTR_RESUME) ? " resume" : "",
|
||||
(irqreg & INTR_SUSPEND) ? " suspend" : "",
|
||||
(irqreg & INTR_RESET_STOP) ? " noreset" : "",
|
||||
(irqreg & INTR_RESET_START) ? " reset" : "",
|
||||
(irqreg & INTR_FRAME_MATCH) ? " fmatch" : "",
|
||||
(irqreg & INTR_CFG_CHG) ? " config" : "");
|
||||
}
|
||||
#else
|
||||
#define dump_intr(x, y, z) do {} while (0)
|
||||
#endif /* DEBUG_IRQ */
|
||||
|
||||
#ifdef DEBUG_EPIRQ
|
||||
static void dump_ep_intr(const char *label, int nr, int irqreg, struct device *dev)
|
||||
{
|
||||
dev_dbg(dev, "<%s> EP%d_INTR=[%s%s%s%s%s%s%s%s%s]\n", label, nr,
|
||||
(irqreg & EPINTR_FIFO_FULL) ? " full" : "",
|
||||
(irqreg & EPINTR_FIFO_EMPTY) ? " fempty" : "",
|
||||
(irqreg & EPINTR_FIFO_ERROR) ? " ferr" : "",
|
||||
(irqreg & EPINTR_FIFO_HIGH) ? " fhigh" : "",
|
||||
(irqreg & EPINTR_FIFO_LOW) ? " flow" : "",
|
||||
(irqreg & EPINTR_MDEVREQ) ? " mreq" : "",
|
||||
(irqreg & EPINTR_EOF) ? " eof" : "",
|
||||
(irqreg & EPINTR_DEVREQ) ? " devreq" : "",
|
||||
(irqreg & EPINTR_EOT) ? " eot" : "");
|
||||
}
|
||||
#else
|
||||
#define dump_ep_intr(x, y, z, i) do {} while (0)
|
||||
#endif /* DEBUG_IRQ */
|
||||
|
||||
#ifdef DEBUG_DUMP
|
||||
static void dump_usb_stat(const char *label, struct imx_udc_struct *imx_usb)
|
||||
{
|
||||
int temp = __raw_readl(imx_usb->base + USB_STAT);
|
||||
|
||||
dev_dbg(imx_usb->dev,
|
||||
"<%s> USB_STAT=[%s%s CFG=%d, INTF=%d, ALTR=%d]\n", label,
|
||||
(temp & STAT_RST) ? " reset" : "",
|
||||
(temp & STAT_SUSP) ? " suspend" : "",
|
||||
(temp & STAT_CFG) >> 5,
|
||||
(temp & STAT_INTF) >> 3,
|
||||
(temp & STAT_ALTSET));
|
||||
}
|
||||
|
||||
static void dump_ep_stat(const char *label, struct imx_ep_struct *imx_ep)
|
||||
{
|
||||
int temp = __raw_readl(imx_ep->imx_usb->base + USB_EP_INTR(EP_NO(imx_ep)));
|
||||
|
||||
dev_dbg(imx_ep->imx_usb->dev,
|
||||
"<%s> EP%d_INTR=[%s%s%s%s%s%s%s%s%s]\n", label, EP_NO(imx_ep),
|
||||
(temp & EPINTR_FIFO_FULL) ? " full" : "",
|
||||
(temp & EPINTR_FIFO_EMPTY) ? " fempty" : "",
|
||||
(temp & EPINTR_FIFO_ERROR) ? " ferr" : "",
|
||||
(temp & EPINTR_FIFO_HIGH) ? " fhigh" : "",
|
||||
(temp & EPINTR_FIFO_LOW) ? " flow" : "",
|
||||
(temp & EPINTR_MDEVREQ) ? " mreq" : "",
|
||||
(temp & EPINTR_EOF) ? " eof" : "",
|
||||
(temp & EPINTR_DEVREQ) ? " devreq" : "",
|
||||
(temp & EPINTR_EOT) ? " eot" : "");
|
||||
|
||||
temp = __raw_readl(imx_ep->imx_usb->base + USB_EP_STAT(EP_NO(imx_ep)));
|
||||
|
||||
dev_dbg(imx_ep->imx_usb->dev,
|
||||
"<%s> EP%d_STAT=[%s%s bcount=%d]\n", label, EP_NO(imx_ep),
|
||||
(temp & EPSTAT_SIP) ? " sip" : "",
|
||||
(temp & EPSTAT_STALL) ? " stall" : "",
|
||||
(temp & EPSTAT_BCOUNT) >> 16);
|
||||
|
||||
temp = __raw_readl(imx_ep->imx_usb->base + USB_EP_FSTAT(EP_NO(imx_ep)));
|
||||
|
||||
dev_dbg(imx_ep->imx_usb->dev,
|
||||
"<%s> EP%d_FSTAT=[%s%s%s%s%s%s%s]\n", label, EP_NO(imx_ep),
|
||||
(temp & FSTAT_ERR) ? " ferr" : "",
|
||||
(temp & FSTAT_UF) ? " funder" : "",
|
||||
(temp & FSTAT_OF) ? " fover" : "",
|
||||
(temp & FSTAT_FR) ? " fready" : "",
|
||||
(temp & FSTAT_FULL) ? " ffull" : "",
|
||||
(temp & FSTAT_ALRM) ? " falarm" : "",
|
||||
(temp & FSTAT_EMPTY) ? " fempty" : "");
|
||||
}
|
||||
|
||||
static void dump_req(const char *label, struct imx_ep_struct *imx_ep, struct usb_request *req)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!req || !req->buf) {
|
||||
dev_dbg(imx_ep->imx_usb->dev, "<%s> req or req buf is free\n", label);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((!EP_NO(imx_ep) && imx_ep->imx_usb->ep0state == EP0_IN_DATA_PHASE)
|
||||
|| (EP_NO(imx_ep) && EP_DIR(imx_ep))) {
|
||||
|
||||
dev_dbg(imx_ep->imx_usb->dev, "<%s> request dump <", label);
|
||||
for (i = 0; i < req->length; i++)
|
||||
printk("%02x-", *((u8 *)req->buf + i));
|
||||
printk(">\n");
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
#define dump_ep_stat(x, y) do {} while (0)
|
||||
#define dump_usb_stat(x, y) do {} while (0)
|
||||
#define dump_req(x, y, z) do {} while (0)
|
||||
#endif /* DEBUG_DUMP */
|
||||
|
||||
#ifdef DEBUG_ERR
|
||||
#define D_ERR(dev, args...) dev_dbg(dev, ## args)
|
||||
#else
|
||||
#define D_ERR(dev, args...) do {} while (0)
|
||||
#endif
|
||||
|
||||
#else
|
||||
#define D_REQ(dev, args...) do {} while (0)
|
||||
#define D_TRX(dev, args...) do {} while (0)
|
||||
#define D_INI(dev, args...) do {} while (0)
|
||||
#define D_EP0(dev, args...) do {} while (0)
|
||||
#define D_EPX(dev, args...) do {} while (0)
|
||||
#define dump_ep_intr(x, y, z, i) do {} while (0)
|
||||
#define dump_intr(x, y, z) do {} while (0)
|
||||
#define dump_ep_stat(x, y) do {} while (0)
|
||||
#define dump_usb_stat(x, y) do {} while (0)
|
||||
#define dump_req(x, y, z) do {} while (0)
|
||||
#define D_ERR(dev, args...) do {} while (0)
|
||||
#endif /* DEBUG */
|
||||
|
||||
#endif /* __LINUX_USB_GADGET_IMX_H */
|
||||
|
|
@ -1981,7 +1981,7 @@ static struct lh7a40x_udc memory = {
|
|||
.ep0 = &memory.ep[0].ep,
|
||||
.name = driver_name,
|
||||
.dev = {
|
||||
.bus_id = "gadget",
|
||||
.init_name = "gadget",
|
||||
.release = nop_release,
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1546,8 +1546,6 @@ static void nop_completion(struct usb_ep *ep, struct usb_request *r)
|
|||
{
|
||||
}
|
||||
|
||||
#define resource_len(r) (((r)->end - (r)->start) + 1)
|
||||
|
||||
static int __init m66592_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res;
|
||||
|
|
@ -1560,11 +1558,10 @@ static int __init m66592_probe(struct platform_device *pdev)
|
|||
int ret = 0;
|
||||
int i;
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
||||
(char *)udc_name);
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
ret = -ENODEV;
|
||||
pr_err("platform_get_resource_byname error.\n");
|
||||
pr_err("platform_get_resource error.\n");
|
||||
goto clean_up;
|
||||
}
|
||||
|
||||
|
|
@ -1575,7 +1572,7 @@ static int __init m66592_probe(struct platform_device *pdev)
|
|||
goto clean_up;
|
||||
}
|
||||
|
||||
reg = ioremap(res->start, resource_len(res));
|
||||
reg = ioremap(res->start, resource_size(res));
|
||||
if (reg == NULL) {
|
||||
ret = -ENOMEM;
|
||||
pr_err("ioremap error.\n");
|
||||
|
|
|
|||
|
|
@ -669,7 +669,7 @@ fill_dma_desc (struct net2280_ep *ep, struct net2280_request *req, int valid)
|
|||
|
||||
/* 2280 may be polling VALID_BIT through ep->dma->dmadesc */
|
||||
wmb ();
|
||||
td->dmacount = cpu_to_le32p (&dmacount);
|
||||
td->dmacount = cpu_to_le32(dmacount);
|
||||
}
|
||||
|
||||
static const u32 dmactl_default =
|
||||
|
|
|
|||
|
|
@ -3006,7 +3006,7 @@ cleanup1:
|
|||
|
||||
cleanup0:
|
||||
if (xceiv)
|
||||
put_device(xceiv->dev);
|
||||
otg_put_transceiver(xceiv);
|
||||
|
||||
if (cpu_is_omap16xx() || cpu_is_omap24xx()) {
|
||||
clk_disable(hhc_clk);
|
||||
|
|
@ -3034,7 +3034,7 @@ static int __exit omap_udc_remove(struct platform_device *pdev)
|
|||
|
||||
pullup_disable(udc);
|
||||
if (udc->transceiver) {
|
||||
put_device(udc->transceiver->dev);
|
||||
otg_put_transceiver(udc->transceiver);
|
||||
udc->transceiver = NULL;
|
||||
}
|
||||
omap_writew(0, UDC_SYSCON1);
|
||||
|
|
|
|||
|
|
@ -1833,7 +1833,7 @@ static struct pxa25x_udc memory = {
|
|||
.ep0 = &memory.ep[0].ep,
|
||||
.name = driver_name,
|
||||
.dev = {
|
||||
.bus_id = "gadget",
|
||||
.init_name = "gadget",
|
||||
.release = nop_release,
|
||||
},
|
||||
},
|
||||
|
|
@ -2198,7 +2198,7 @@ static int __init pxa25x_udc_probe(struct platform_device *pdev)
|
|||
udc_disable(dev);
|
||||
udc_reinit(dev);
|
||||
|
||||
dev->vbus = is_vbus_present();
|
||||
dev->vbus = !!is_vbus_present();
|
||||
|
||||
/* irq setup after old hardware state is cleaned up */
|
||||
retval = request_irq(irq, pxa25x_udc_irq,
|
||||
|
|
|
|||
|
|
@ -430,7 +430,6 @@ static void pio_irq_enable(struct pxa_ep *ep)
|
|||
/**
|
||||
* pio_irq_disable - Disables irq generation for one endpoint
|
||||
* @ep: udc endpoint
|
||||
* @index: endpoint number
|
||||
*/
|
||||
static void pio_irq_disable(struct pxa_ep *ep)
|
||||
{
|
||||
|
|
@ -586,7 +585,6 @@ static void inc_ep_stats_reqs(struct pxa_ep *ep, int is_in)
|
|||
* inc_ep_stats_bytes - Update ep stats counts
|
||||
* @ep: physical endpoint
|
||||
* @count: bytes transfered on endpoint
|
||||
* @req: usb request
|
||||
* @is_in: ep direction (USB_DIR_IN or 0)
|
||||
*/
|
||||
static void inc_ep_stats_bytes(struct pxa_ep *ep, int count, int is_in)
|
||||
|
|
@ -2162,7 +2160,7 @@ static struct pxa_udc memory = {
|
|||
.ep0 = &memory.udc_usb_ep[0].usb_ep,
|
||||
.name = driver_name,
|
||||
.dev = {
|
||||
.bus_id = "gadget",
|
||||
.init_name = "gadget",
|
||||
},
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@
|
|||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/gpio.h>
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/seq_file.h>
|
||||
|
|
@ -51,7 +52,6 @@
|
|||
#include <mach/irqs.h>
|
||||
|
||||
#include <mach/hardware.h>
|
||||
#include <mach/regs-gpio.h>
|
||||
|
||||
#include <plat/regs-udc.h>
|
||||
#include <plat/udc.h>
|
||||
|
|
@ -1510,11 +1510,7 @@ static irqreturn_t s3c2410_udc_vbus_irq(int irq, void *_dev)
|
|||
|
||||
dprintk(DEBUG_NORMAL, "%s()\n", __func__);
|
||||
|
||||
/* some cpus cannot read from an line configured to IRQ! */
|
||||
s3c2410_gpio_cfgpin(udc_info->vbus_pin, S3C2410_GPIO_INPUT);
|
||||
value = s3c2410_gpio_getpin(udc_info->vbus_pin);
|
||||
s3c2410_gpio_cfgpin(udc_info->vbus_pin, S3C2410_GPIO_SFN2);
|
||||
|
||||
value = gpio_get_value(udc_info->vbus_pin) ? 1 : 0;
|
||||
if (udc_info->vbus_pin_inverted)
|
||||
value = !value;
|
||||
|
||||
|
|
@ -1727,7 +1723,7 @@ static struct s3c2410_udc memory = {
|
|||
.ep0 = &memory.ep[0].ep,
|
||||
.name = gadget_name,
|
||||
.dev = {
|
||||
.bus_id = "gadget",
|
||||
.init_name = "gadget",
|
||||
},
|
||||
},
|
||||
|
||||
|
|
@ -1802,7 +1798,7 @@ static int s3c2410_udc_probe(struct platform_device *pdev)
|
|||
struct s3c2410_udc *udc = &memory;
|
||||
struct device *dev = &pdev->dev;
|
||||
int retval;
|
||||
unsigned int irq;
|
||||
int irq;
|
||||
|
||||
dev_dbg(dev, "%s()\n", __func__);
|
||||
|
||||
|
|
@ -1861,7 +1857,7 @@ static int s3c2410_udc_probe(struct platform_device *pdev)
|
|||
|
||||
/* irq setup after old hardware state is cleaned up */
|
||||
retval = request_irq(IRQ_USBD, s3c2410_udc_irq,
|
||||
IRQF_DISABLED, gadget_name, udc);
|
||||
IRQF_DISABLED, gadget_name, udc);
|
||||
|
||||
if (retval != 0) {
|
||||
dev_err(dev, "cannot get irq %i, err %d\n", IRQ_USBD, retval);
|
||||
|
|
@ -1872,17 +1868,28 @@ static int s3c2410_udc_probe(struct platform_device *pdev)
|
|||
dev_dbg(dev, "got irq %i\n", IRQ_USBD);
|
||||
|
||||
if (udc_info && udc_info->vbus_pin > 0) {
|
||||
irq = s3c2410_gpio_getirq(udc_info->vbus_pin);
|
||||
retval = gpio_request(udc_info->vbus_pin, "udc vbus");
|
||||
if (retval < 0) {
|
||||
dev_err(dev, "cannot claim vbus pin\n");
|
||||
goto err_int;
|
||||
}
|
||||
|
||||
irq = gpio_to_irq(udc_info->vbus_pin);
|
||||
if (irq < 0) {
|
||||
dev_err(dev, "no irq for gpio vbus pin\n");
|
||||
goto err_gpio_claim;
|
||||
}
|
||||
|
||||
retval = request_irq(irq, s3c2410_udc_vbus_irq,
|
||||
IRQF_DISABLED | IRQF_TRIGGER_RISING
|
||||
| IRQF_TRIGGER_FALLING | IRQF_SHARED,
|
||||
gadget_name, udc);
|
||||
|
||||
if (retval != 0) {
|
||||
dev_err(dev, "can't get vbus irq %i, err %d\n",
|
||||
dev_err(dev, "can't get vbus irq %d, err %d\n",
|
||||
irq, retval);
|
||||
retval = -EBUSY;
|
||||
goto err_int;
|
||||
goto err_gpio_claim;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "got irq %i\n", irq);
|
||||
|
|
@ -1902,6 +1909,9 @@ static int s3c2410_udc_probe(struct platform_device *pdev)
|
|||
|
||||
return 0;
|
||||
|
||||
err_gpio_claim:
|
||||
if (udc_info && udc_info->vbus_pin > 0)
|
||||
gpio_free(udc_info->vbus_pin);
|
||||
err_int:
|
||||
free_irq(IRQ_USBD, udc);
|
||||
err_map:
|
||||
|
|
@ -1927,7 +1937,7 @@ static int s3c2410_udc_remove(struct platform_device *pdev)
|
|||
debugfs_remove(udc->regs_info);
|
||||
|
||||
if (udc_info && udc_info->vbus_pin > 0) {
|
||||
irq = s3c2410_gpio_getirq(udc_info->vbus_pin);
|
||||
irq = gpio_to_irq(udc_info->vbus_pin);
|
||||
free_irq(irq, udc);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -96,6 +96,19 @@ config USB_EHCI_HCD_PPC_OF
|
|||
Enables support for the USB controller present on the PowerPC
|
||||
OpenFirmware platform bus.
|
||||
|
||||
config USB_OXU210HP_HCD
|
||||
tristate "OXU210HP HCD support"
|
||||
depends on USB
|
||||
---help---
|
||||
The OXU210HP is an USB host/OTG/device controller. Enable this
|
||||
option if your board has this chip. If unsure, say N.
|
||||
|
||||
This driver does not support isochronous transfers and doesn't
|
||||
implement OTG nor USB device controllers.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called oxu210hp-hcd.
|
||||
|
||||
config USB_ISP116X_HCD
|
||||
tristate "ISP116X HCD support"
|
||||
depends on USB
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ obj-$(CONFIG_USB_WHCI_HCD) += whci/
|
|||
obj-$(CONFIG_PCI) += pci-quirks.o
|
||||
|
||||
obj-$(CONFIG_USB_EHCI_HCD) += ehci-hcd.o
|
||||
obj-$(CONFIG_USB_OXU210HP_HCD) += oxu210hp-hcd.o
|
||||
obj-$(CONFIG_USB_ISP116X_HCD) += isp116x-hcd.o
|
||||
obj-$(CONFIG_USB_OHCI_HCD) += ohci-hcd.o
|
||||
obj-$(CONFIG_USB_UHCI_HCD) += uhci-hcd.o
|
||||
|
|
|
|||
|
|
@ -455,9 +455,7 @@ static void qh_lines (
|
|||
(scratch >> 16) & 0x7fff,
|
||||
scratch,
|
||||
td->urb);
|
||||
if (temp < 0)
|
||||
temp = 0;
|
||||
else if (size < temp)
|
||||
if (size < temp)
|
||||
temp = size;
|
||||
size -= temp;
|
||||
next += temp;
|
||||
|
|
@ -466,9 +464,7 @@ static void qh_lines (
|
|||
}
|
||||
|
||||
temp = snprintf (next, size, "\n");
|
||||
if (temp < 0)
|
||||
temp = 0;
|
||||
else if (size < temp)
|
||||
if (size < temp)
|
||||
temp = size;
|
||||
size -= temp;
|
||||
next += temp;
|
||||
|
|
|
|||
|
|
@ -194,6 +194,7 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
|
|||
u32 temp;
|
||||
u32 power_okay;
|
||||
int i;
|
||||
u8 resume_needed = 0;
|
||||
|
||||
if (time_before (jiffies, ehci->next_statechange))
|
||||
msleep(5);
|
||||
|
|
@ -228,7 +229,9 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
|
|||
|
||||
/* Some controller/firmware combinations need a delay during which
|
||||
* they set up the port statuses. See Bugzilla #8190. */
|
||||
mdelay(8);
|
||||
spin_unlock_irq(&ehci->lock);
|
||||
msleep(8);
|
||||
spin_lock_irq(&ehci->lock);
|
||||
|
||||
/* manually resume the ports we suspended during bus_suspend() */
|
||||
i = HCS_N_PORTS (ehci->hcs_params);
|
||||
|
|
@ -236,12 +239,21 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
|
|||
temp = ehci_readl(ehci, &ehci->regs->port_status [i]);
|
||||
temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS);
|
||||
if (test_bit(i, &ehci->bus_suspended) &&
|
||||
(temp & PORT_SUSPEND))
|
||||
(temp & PORT_SUSPEND)) {
|
||||
temp |= PORT_RESUME;
|
||||
resume_needed = 1;
|
||||
}
|
||||
ehci_writel(ehci, temp, &ehci->regs->port_status [i]);
|
||||
}
|
||||
|
||||
/* msleep for 20ms only if code is trying to resume port */
|
||||
if (resume_needed) {
|
||||
spin_unlock_irq(&ehci->lock);
|
||||
msleep(20);
|
||||
spin_lock_irq(&ehci->lock);
|
||||
}
|
||||
|
||||
i = HCS_N_PORTS (ehci->hcs_params);
|
||||
mdelay (20);
|
||||
while (i--) {
|
||||
temp = ehci_readl(ehci, &ehci->regs->port_status [i]);
|
||||
if (test_bit(i, &ehci->bus_suspended) &&
|
||||
|
|
@ -422,8 +434,15 @@ static int check_reset_complete (
|
|||
port_status &= ~PORT_RWC_BITS;
|
||||
ehci_writel(ehci, port_status, status_reg);
|
||||
|
||||
} else
|
||||
/* ensure 440EPX ohci controller state is operational */
|
||||
if (ehci->has_amcc_usb23)
|
||||
set_ohci_hcfs(ehci, 1);
|
||||
} else {
|
||||
ehci_dbg (ehci, "port %d high speed\n", index + 1);
|
||||
/* ensure 440EPx ohci controller state is suspended */
|
||||
if (ehci->has_amcc_usb23)
|
||||
set_ohci_hcfs(ehci, 0);
|
||||
}
|
||||
|
||||
return port_status;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -219,15 +219,19 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
|
|||
/* Serial Bus Release Number is at PCI 0x60 offset */
|
||||
pci_read_config_byte(pdev, 0x60, &ehci->sbrn);
|
||||
|
||||
/* Workaround current PCI init glitch: wakeup bits aren't
|
||||
* being set from PCI PM capability.
|
||||
/* Keep this around for a while just in case some EHCI
|
||||
* implementation uses legacy PCI PM support. This test
|
||||
* can be removed on 17 Dec 2009 if the dev_warn() hasn't
|
||||
* been triggered by then.
|
||||
*/
|
||||
if (!device_can_wakeup(&pdev->dev)) {
|
||||
u16 port_wake;
|
||||
|
||||
pci_read_config_word(pdev, 0x62, &port_wake);
|
||||
if (port_wake & 0x0001)
|
||||
if (port_wake & 0x0001) {
|
||||
dev_warn(&pdev->dev, "Enabling legacy PCI PM\n");
|
||||
device_init_wakeup(&pdev->dev, 1);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USB_SUSPEND
|
||||
|
|
@ -428,6 +432,8 @@ static struct pci_driver ehci_pci_driver = {
|
|||
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = usb_hcd_pci_suspend,
|
||||
.suspend_late = usb_hcd_pci_suspend_late,
|
||||
.resume_early = usb_hcd_pci_resume_early,
|
||||
.resume = usb_hcd_pci_resume,
|
||||
#endif
|
||||
.shutdown = usb_hcd_pci_shutdown,
|
||||
|
|
|
|||
|
|
@ -107,11 +107,13 @@ ehci_hcd_ppc_of_probe(struct of_device *op, const struct of_device_id *match)
|
|||
{
|
||||
struct device_node *dn = op->node;
|
||||
struct usb_hcd *hcd;
|
||||
struct ehci_hcd *ehci;
|
||||
struct ehci_hcd *ehci = NULL;
|
||||
struct resource res;
|
||||
int irq;
|
||||
int rv;
|
||||
|
||||
struct device_node *np;
|
||||
|
||||
if (usb_disabled())
|
||||
return -ENODEV;
|
||||
|
||||
|
|
@ -149,6 +151,20 @@ ehci_hcd_ppc_of_probe(struct of_device *op, const struct of_device_id *match)
|
|||
}
|
||||
|
||||
ehci = hcd_to_ehci(hcd);
|
||||
np = of_find_compatible_node(NULL, NULL, "ibm,usb-ohci-440epx");
|
||||
if (np != NULL) {
|
||||
/* claim we really affected by usb23 erratum */
|
||||
if (!of_address_to_resource(np, 0, &res))
|
||||
ehci->ohci_hcctrl_reg = ioremap(res.start +
|
||||
OHCI_HCCTRL_OFFSET, OHCI_HCCTRL_LEN);
|
||||
else
|
||||
pr_debug(__FILE__ ": no ohci offset in fdt\n");
|
||||
if (!ehci->ohci_hcctrl_reg) {
|
||||
pr_debug(__FILE__ ": ioremap for ohci hcctrl failed\n");
|
||||
} else {
|
||||
ehci->has_amcc_usb23 = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (of_get_property(dn, "big-endian", NULL)) {
|
||||
ehci->big_endian_mmio = 1;
|
||||
|
|
@ -181,6 +197,9 @@ err_ioremap:
|
|||
irq_dispose_mapping(irq);
|
||||
err_irq:
|
||||
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
|
||||
|
||||
if (ehci->has_amcc_usb23)
|
||||
iounmap(ehci->ohci_hcctrl_reg);
|
||||
err_rmr:
|
||||
usb_put_hcd(hcd);
|
||||
|
||||
|
|
@ -191,6 +210,11 @@ err_rmr:
|
|||
static int ehci_hcd_ppc_of_remove(struct of_device *op)
|
||||
{
|
||||
struct usb_hcd *hcd = dev_get_drvdata(&op->dev);
|
||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||
|
||||
struct device_node *np;
|
||||
struct resource res;
|
||||
|
||||
dev_set_drvdata(&op->dev, NULL);
|
||||
|
||||
dev_dbg(&op->dev, "stopping PPC-OF USB Controller\n");
|
||||
|
|
@ -201,6 +225,25 @@ static int ehci_hcd_ppc_of_remove(struct of_device *op)
|
|||
irq_dispose_mapping(hcd->irq);
|
||||
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
|
||||
|
||||
/* use request_mem_region to test if the ohci driver is loaded. if so
|
||||
* ensure the ohci core is operational.
|
||||
*/
|
||||
if (ehci->has_amcc_usb23) {
|
||||
np = of_find_compatible_node(NULL, NULL, "ibm,usb-ohci-440epx");
|
||||
if (np != NULL) {
|
||||
if (!of_address_to_resource(np, 0, &res))
|
||||
if (!request_mem_region(res.start,
|
||||
0x4, hcd_name))
|
||||
set_ohci_hcfs(ehci, 1);
|
||||
else
|
||||
release_mem_region(res.start, 0x4);
|
||||
else
|
||||
pr_debug(__FILE__ ": no ohci offset in fdt\n");
|
||||
of_node_put(np);
|
||||
}
|
||||
|
||||
iounmap(ehci->ohci_hcctrl_reg);
|
||||
}
|
||||
usb_put_hcd(hcd);
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -120,6 +120,16 @@ struct ehci_hcd { /* one per controller */
|
|||
unsigned has_fsl_port_bug:1; /* FreeScale */
|
||||
unsigned big_endian_mmio:1;
|
||||
unsigned big_endian_desc:1;
|
||||
unsigned has_amcc_usb23:1;
|
||||
|
||||
/* required for usb32 quirk */
|
||||
#define OHCI_CTRL_HCFS (3 << 6)
|
||||
#define OHCI_USB_OPER (2 << 6)
|
||||
#define OHCI_USB_SUSPEND (3 << 6)
|
||||
|
||||
#define OHCI_HCCTRL_OFFSET 0x4
|
||||
#define OHCI_HCCTRL_LEN 0x4
|
||||
__hc32 *ohci_hcctrl_reg;
|
||||
|
||||
u8 sbrn; /* packed release number */
|
||||
|
||||
|
|
@ -636,6 +646,30 @@ static inline void ehci_writel(const struct ehci_hcd *ehci,
|
|||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* On certain ppc-44x SoC there is a HW issue, that could only worked around with
|
||||
* explicit suspend/operate of OHCI. This function hereby makes sense only on that arch.
|
||||
* Other common bits are dependant on has_amcc_usb23 quirk flag.
|
||||
*/
|
||||
#ifdef CONFIG_44x
|
||||
static inline void set_ohci_hcfs(struct ehci_hcd *ehci, int operational)
|
||||
{
|
||||
u32 hc_control;
|
||||
|
||||
hc_control = (readl_be(ehci->ohci_hcctrl_reg) & ~OHCI_CTRL_HCFS);
|
||||
if (operational)
|
||||
hc_control |= OHCI_USB_OPER;
|
||||
else
|
||||
hc_control |= OHCI_USB_SUSPEND;
|
||||
|
||||
writel_be(hc_control, ehci->ohci_hcctrl_reg);
|
||||
(void) readl_be(ehci->ohci_hcctrl_reg);
|
||||
}
|
||||
#else
|
||||
static inline void set_ohci_hcfs(struct ehci_hcd *ehci, int operational)
|
||||
{ }
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -435,14 +435,13 @@ static int isp1760_hc_setup(struct usb_hcd *hcd)
|
|||
|
||||
/*
|
||||
* PORT 1 Control register of the ISP1760 is the OTG control
|
||||
* register on ISP1761.
|
||||
* register on ISP1761. Since there is no OTG or device controller
|
||||
* support in this driver, we use port 1 as a "normal" USB host port on
|
||||
* both chips.
|
||||
*/
|
||||
if (!(priv->devflags & ISP1760_FLAG_ISP1761) &&
|
||||
!(priv->devflags & ISP1760_FLAG_PORT1_DIS)) {
|
||||
isp1760_writel(PORT1_POWER | PORT1_INIT2,
|
||||
hcd->regs + HC_PORT1_CTRL);
|
||||
mdelay(10);
|
||||
}
|
||||
isp1760_writel(PORT1_POWER | PORT1_INIT2,
|
||||
hcd->regs + HC_PORT1_CTRL);
|
||||
mdelay(10);
|
||||
|
||||
priv->hcs_params = isp1760_readl(hcd->regs + HC_HCSPARAMS);
|
||||
|
||||
|
|
|
|||
|
|
@ -135,7 +135,6 @@ typedef void (packet_enqueue)(struct usb_hcd *hcd, struct isp1760_qh *qh,
|
|||
* indicate the most "atypical" case, so that a devflags of 0 is
|
||||
* a sane default configuration.
|
||||
*/
|
||||
#define ISP1760_FLAG_PORT1_DIS 0x00000001 /* Port 1 disabled */
|
||||
#define ISP1760_FLAG_BUS_WIDTH_16 0x00000002 /* 16-bit data bus width */
|
||||
#define ISP1760_FLAG_OTG_EN 0x00000004 /* Port 1 supports OTG */
|
||||
#define ISP1760_FLAG_ANALOG_OC 0x00000008 /* Analog overcurrent */
|
||||
|
|
|
|||
|
|
@ -60,9 +60,6 @@ static int of_isp1760_probe(struct of_device *dev,
|
|||
if (of_device_is_compatible(dp, "nxp,usb-isp1761"))
|
||||
devflags |= ISP1760_FLAG_ISP1761;
|
||||
|
||||
if (of_get_property(dp, "port1-disable", NULL) != NULL)
|
||||
devflags |= ISP1760_FLAG_PORT1_DIS;
|
||||
|
||||
/* Some systems wire up only 16 of the 32 data lines */
|
||||
prop = of_get_property(dp, "bus-width", NULL);
|
||||
if (prop && *prop == 16)
|
||||
|
|
@ -129,23 +126,23 @@ static struct of_platform_driver isp1760_of_driver = {
|
|||
#endif
|
||||
|
||||
#ifdef CONFIG_PCI
|
||||
static u32 nxp_pci_io_base;
|
||||
static u32 iolength;
|
||||
static u32 pci_mem_phy0;
|
||||
static u32 length;
|
||||
static u8 __iomem *chip_addr;
|
||||
static u8 __iomem *iobase;
|
||||
|
||||
static int __devinit isp1761_pci_probe(struct pci_dev *dev,
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
u8 latency, limit;
|
||||
__u32 reg_data;
|
||||
int retry_count;
|
||||
int length;
|
||||
int status = 1;
|
||||
struct usb_hcd *hcd;
|
||||
unsigned int devflags = 0;
|
||||
int ret_status = 0;
|
||||
|
||||
resource_size_t pci_mem_phy0;
|
||||
resource_size_t memlength;
|
||||
|
||||
u8 __iomem *chip_addr;
|
||||
u8 __iomem *iobase;
|
||||
resource_size_t nxp_pci_io_base;
|
||||
resource_size_t iolength;
|
||||
|
||||
if (usb_disabled())
|
||||
return -ENODEV;
|
||||
|
|
@ -168,26 +165,30 @@ static int __devinit isp1761_pci_probe(struct pci_dev *dev,
|
|||
iobase = ioremap_nocache(nxp_pci_io_base, iolength);
|
||||
if (!iobase) {
|
||||
printk(KERN_ERR "ioremap #1\n");
|
||||
release_mem_region(nxp_pci_io_base, iolength);
|
||||
return -ENOMEM;
|
||||
ret_status = -ENOMEM;
|
||||
goto cleanup1;
|
||||
}
|
||||
/* Grab the PLX PCI shared memory of the ISP 1761 we need */
|
||||
pci_mem_phy0 = pci_resource_start(dev, 3);
|
||||
length = pci_resource_len(dev, 3);
|
||||
|
||||
if (length < 0xffff) {
|
||||
printk(KERN_ERR "memory length for this resource is less than "
|
||||
"required\n");
|
||||
release_mem_region(nxp_pci_io_base, iolength);
|
||||
iounmap(iobase);
|
||||
return -ENOMEM;
|
||||
memlength = pci_resource_len(dev, 3);
|
||||
if (memlength < 0xffff) {
|
||||
printk(KERN_ERR "memory length for this resource is wrong\n");
|
||||
ret_status = -ENOMEM;
|
||||
goto cleanup2;
|
||||
}
|
||||
|
||||
if (!request_mem_region(pci_mem_phy0, length, "ISP-PCI")) {
|
||||
if (!request_mem_region(pci_mem_phy0, memlength, "ISP-PCI")) {
|
||||
printk(KERN_ERR "host controller already in use\n");
|
||||
release_mem_region(nxp_pci_io_base, iolength);
|
||||
iounmap(iobase);
|
||||
return -EBUSY;
|
||||
ret_status = -EBUSY;
|
||||
goto cleanup2;
|
||||
}
|
||||
|
||||
/* map available memory */
|
||||
chip_addr = ioremap_nocache(pci_mem_phy0,memlength);
|
||||
if (!chip_addr) {
|
||||
printk(KERN_ERR "Error ioremap failed\n");
|
||||
ret_status = -ENOMEM;
|
||||
goto cleanup3;
|
||||
}
|
||||
|
||||
/* bad pci latencies can contribute to overruns */
|
||||
|
|
@ -210,39 +211,54 @@ static int __devinit isp1761_pci_probe(struct pci_dev *dev,
|
|||
* */
|
||||
writel(0xface, chip_addr + HC_SCRATCH_REG);
|
||||
udelay(100);
|
||||
reg_data = readl(chip_addr + HC_SCRATCH_REG);
|
||||
reg_data = readl(chip_addr + HC_SCRATCH_REG) & 0x0000ffff;
|
||||
retry_count--;
|
||||
}
|
||||
|
||||
iounmap(chip_addr);
|
||||
|
||||
/* Host Controller presence is detected by writing to scratch register
|
||||
* and reading back and checking the contents are same or not
|
||||
*/
|
||||
if (reg_data != 0xFACE) {
|
||||
dev_err(&dev->dev, "scratch register mismatch %x\n", reg_data);
|
||||
goto clean;
|
||||
ret_status = -ENOMEM;
|
||||
goto cleanup3;
|
||||
}
|
||||
|
||||
pci_set_master(dev);
|
||||
|
||||
status = readl(iobase + 0x68);
|
||||
status |= 0x900;
|
||||
writel(status, iobase + 0x68);
|
||||
/* configure PLX PCI chip to pass interrupts */
|
||||
#define PLX_INT_CSR_REG 0x68
|
||||
reg_data = readl(iobase + PLX_INT_CSR_REG);
|
||||
reg_data |= 0x900;
|
||||
writel(reg_data, iobase + PLX_INT_CSR_REG);
|
||||
|
||||
dev->dev.dma_mask = NULL;
|
||||
hcd = isp1760_register(pci_mem_phy0, length, dev->irq,
|
||||
hcd = isp1760_register(pci_mem_phy0, memlength, dev->irq,
|
||||
IRQF_SHARED | IRQF_DISABLED, &dev->dev, dev_name(&dev->dev),
|
||||
devflags);
|
||||
if (!IS_ERR(hcd)) {
|
||||
pci_set_drvdata(dev, hcd);
|
||||
return 0;
|
||||
if (IS_ERR(hcd)) {
|
||||
ret_status = -ENODEV;
|
||||
goto cleanup3;
|
||||
}
|
||||
clean:
|
||||
status = -ENODEV;
|
||||
|
||||
/* done with PLX IO access */
|
||||
iounmap(iobase);
|
||||
release_mem_region(pci_mem_phy0, length);
|
||||
release_mem_region(nxp_pci_io_base, iolength);
|
||||
return status;
|
||||
|
||||
pci_set_drvdata(dev, hcd);
|
||||
return 0;
|
||||
|
||||
cleanup3:
|
||||
release_mem_region(pci_mem_phy0, memlength);
|
||||
cleanup2:
|
||||
iounmap(iobase);
|
||||
cleanup1:
|
||||
release_mem_region(nxp_pci_io_base, iolength);
|
||||
return ret_status;
|
||||
}
|
||||
|
||||
static void isp1761_pci_remove(struct pci_dev *dev)
|
||||
{
|
||||
struct usb_hcd *hcd;
|
||||
|
|
@ -255,12 +271,6 @@ static void isp1761_pci_remove(struct pci_dev *dev)
|
|||
usb_put_hcd(hcd);
|
||||
|
||||
pci_disable_device(dev);
|
||||
|
||||
iounmap(iobase);
|
||||
iounmap(chip_addr);
|
||||
|
||||
release_mem_region(nxp_pci_io_base, iolength);
|
||||
release_mem_region(pci_mem_phy0, length);
|
||||
}
|
||||
|
||||
static void isp1761_pci_shutdown(struct pci_dev *dev)
|
||||
|
|
@ -268,12 +278,16 @@ static void isp1761_pci_shutdown(struct pci_dev *dev)
|
|||
printk(KERN_ERR "ips1761_pci_shutdown\n");
|
||||
}
|
||||
|
||||
static const struct pci_device_id isp1760_plx [] = { {
|
||||
/* handle any USB 2.0 EHCI controller */
|
||||
PCI_DEVICE_CLASS(((PCI_CLASS_BRIDGE_OTHER << 8) | (0x06 << 16)), ~0),
|
||||
.driver_data = 0,
|
||||
},
|
||||
{ /* end: all zeroes */ }
|
||||
static const struct pci_device_id isp1760_plx [] = {
|
||||
{
|
||||
.class = PCI_CLASS_BRIDGE_OTHER << 8,
|
||||
.class_mask = ~0,
|
||||
.vendor = PCI_VENDOR_ID_PLX,
|
||||
.device = 0x5406,
|
||||
.subvendor = PCI_VENDOR_ID_PLX,
|
||||
.subdevice = 0x9054,
|
||||
},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, isp1760_plx);
|
||||
|
||||
|
|
|
|||
|
|
@ -589,13 +589,15 @@ static int ohci_run (struct ohci_hcd *ohci)
|
|||
/* also: power/overcurrent flags in roothub.a */
|
||||
}
|
||||
|
||||
/* Reset USB nearly "by the book". RemoteWakeupConnected was
|
||||
* saved if boot firmware (BIOS/SMM/...) told us it's connected,
|
||||
* or if bus glue did the same (e.g. for PCI add-in cards with
|
||||
* PCI PM support).
|
||||
/* Reset USB nearly "by the book". RemoteWakeupConnected has
|
||||
* to be checked in case boot firmware (BIOS/SMM/...) has set up
|
||||
* wakeup in a way the bus isn't aware of (e.g., legacy PCI PM).
|
||||
* If the bus glue detected wakeup capability then it should
|
||||
* already be enabled. Either way, if wakeup should be enabled
|
||||
* but isn't, we'll enable it now.
|
||||
*/
|
||||
if ((ohci->hc_control & OHCI_CTRL_RWC) != 0
|
||||
&& !device_may_wakeup(hcd->self.controller))
|
||||
&& !device_can_wakeup(hcd->self.controller))
|
||||
device_init_wakeup(hcd->self.controller, 1);
|
||||
|
||||
switch (ohci->hc_control & OHCI_CTRL_HCFS) {
|
||||
|
|
|
|||
|
|
@ -355,9 +355,9 @@ static int __devinit ohci_pci_start (struct usb_hcd *hcd)
|
|||
|
||||
/* RWC may not be set for add-in PCI cards, since boot
|
||||
* firmware probably ignored them. This transfers PCI
|
||||
* PM wakeup capabilities (once the PCI layer is fixed).
|
||||
* PM wakeup capabilities.
|
||||
*/
|
||||
if (device_may_wakeup(&pdev->dev))
|
||||
if (device_can_wakeup(&pdev->dev))
|
||||
ohci->hc_control |= OHCI_CTRL_RWC;
|
||||
}
|
||||
#endif /* CONFIG_PM */
|
||||
|
|
@ -487,6 +487,8 @@ static struct pci_driver ohci_pci_driver = {
|
|||
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = usb_hcd_pci_suspend,
|
||||
.suspend_late = usb_hcd_pci_suspend_late,
|
||||
.resume_early = usb_hcd_pci_resume_early,
|
||||
.resume = usb_hcd_pci_resume,
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -106,65 +106,34 @@ extern int ocpi_enable(void);
|
|||
|
||||
static struct clk *usb_clk;
|
||||
|
||||
static int isp1301_probe(struct i2c_adapter *adap);
|
||||
static int isp1301_detach(struct i2c_client *client);
|
||||
|
||||
static const unsigned short normal_i2c[] =
|
||||
{ ISP1301_I2C_ADDR, ISP1301_I2C_ADDR + 1, I2C_CLIENT_END };
|
||||
static const unsigned short dummy_i2c_addrlist[] = { I2C_CLIENT_END };
|
||||
|
||||
static struct i2c_client_address_data addr_data = {
|
||||
.normal_i2c = normal_i2c,
|
||||
.probe = dummy_i2c_addrlist,
|
||||
.ignore = dummy_i2c_addrlist,
|
||||
static int isp1301_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int isp1301_remove(struct i2c_client *client)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct i2c_device_id isp1301_id[] = {
|
||||
{ "isp1301_pnx", 0 },
|
||||
{ }
|
||||
};
|
||||
|
||||
struct i2c_driver isp1301_driver = {
|
||||
.driver = {
|
||||
.name = "isp1301_pnx",
|
||||
},
|
||||
.attach_adapter = isp1301_probe,
|
||||
.detach_client = isp1301_detach,
|
||||
.probe = isp1301_probe,
|
||||
.remove = isp1301_remove,
|
||||
.id_table = isp1301_id,
|
||||
};
|
||||
|
||||
static int isp1301_attach(struct i2c_adapter *adap, int addr, int kind)
|
||||
{
|
||||
struct i2c_client *c;
|
||||
int err;
|
||||
|
||||
c = kzalloc(sizeof(*c), GFP_KERNEL);
|
||||
if (!c)
|
||||
return -ENOMEM;
|
||||
|
||||
strlcpy(c->name, "isp1301_pnx", I2C_NAME_SIZE);
|
||||
c->flags = 0;
|
||||
c->addr = addr;
|
||||
c->adapter = adap;
|
||||
c->driver = &isp1301_driver;
|
||||
|
||||
err = i2c_attach_client(c);
|
||||
if (err) {
|
||||
kfree(c);
|
||||
return err;
|
||||
}
|
||||
|
||||
isp1301_i2c_client = c;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int isp1301_probe(struct i2c_adapter *adap)
|
||||
{
|
||||
return i2c_probe(adap, &addr_data, isp1301_attach);
|
||||
}
|
||||
|
||||
static int isp1301_detach(struct i2c_client *client)
|
||||
{
|
||||
i2c_detach_client(client);
|
||||
kfree(isp1301_i2c_client);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void i2c_write(u8 buf, u8 subaddr)
|
||||
{
|
||||
char tmpbuf[2];
|
||||
|
|
@ -328,6 +297,8 @@ static int __devinit usb_hcd_pnx4008_probe(struct platform_device *pdev)
|
|||
struct usb_hcd *hcd = 0;
|
||||
struct ohci_hcd *ohci;
|
||||
const struct hc_driver *driver = &ohci_pnx4008_hc_driver;
|
||||
struct i2c_adapter *i2c_adap;
|
||||
struct i2c_board_info i2c_info;
|
||||
|
||||
int ret = 0, irq;
|
||||
|
||||
|
|
@ -351,9 +322,20 @@ static int __devinit usb_hcd_pnx4008_probe(struct platform_device *pdev)
|
|||
|
||||
ret = i2c_add_driver(&isp1301_driver);
|
||||
if (ret < 0) {
|
||||
err("failed to connect I2C to ISP1301 USB Transceiver");
|
||||
err("failed to add ISP1301 driver");
|
||||
goto out;
|
||||
}
|
||||
i2c_adap = i2c_get_adapter(2);
|
||||
memset(&i2c_info, 0, sizeof(struct i2c_board_info));
|
||||
strlcpy(i2c_info.name, "isp1301_pnx", I2C_NAME_SIZE);
|
||||
isp1301_i2c_client = i2c_new_probed_device(i2c_adap, &i2c_info,
|
||||
normal_i2c);
|
||||
i2c_put_adapter(i2c_adap);
|
||||
if (!isp1301_i2c_client) {
|
||||
err("failed to connect I2C to ISP1301 USB Transceiver");
|
||||
ret = -ENODEV;
|
||||
goto out_i2c_driver;
|
||||
}
|
||||
|
||||
isp1301_configure();
|
||||
|
||||
|
|
@ -429,6 +411,9 @@ out3:
|
|||
out2:
|
||||
clk_put(usb_clk);
|
||||
out1:
|
||||
i2c_unregister_client(isp1301_i2c_client);
|
||||
isp1301_i2c_client = NULL;
|
||||
out_i2c_driver:
|
||||
i2c_del_driver(&isp1301_driver);
|
||||
out:
|
||||
return ret;
|
||||
|
|
@ -445,6 +430,8 @@ static int usb_hcd_pnx4008_remove(struct platform_device *pdev)
|
|||
pnx4008_unset_usb_bits();
|
||||
clk_disable(usb_clk);
|
||||
clk_put(usb_clk);
|
||||
i2c_unregister_client(isp1301_i2c_client);
|
||||
isp1301_i2c_client = NULL;
|
||||
i2c_del_driver(&isp1301_driver);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
|
|
|||
|
|
@ -91,6 +91,7 @@ ohci_hcd_ppc_of_probe(struct of_device *op, const struct of_device_id *match)
|
|||
|
||||
int rv;
|
||||
int is_bigendian;
|
||||
struct device_node *np;
|
||||
|
||||
if (usb_disabled())
|
||||
return -ENODEV;
|
||||
|
|
@ -147,6 +148,30 @@ ohci_hcd_ppc_of_probe(struct of_device *op, const struct of_device_id *match)
|
|||
if (rv == 0)
|
||||
return 0;
|
||||
|
||||
/* by now, 440epx is known to show usb_23 erratum */
|
||||
np = of_find_compatible_node(NULL, NULL, "ibm,usb-ehci-440epx");
|
||||
|
||||
/* Work around - At this point ohci_run has executed, the
|
||||
* controller is running, everything, the root ports, etc., is
|
||||
* set up. If the ehci driver is loaded, put the ohci core in
|
||||
* the suspended state. The ehci driver will bring it out of
|
||||
* suspended state when / if a non-high speed USB device is
|
||||
* attached to the USB Host port. If the ehci driver is not
|
||||
* loaded, do nothing. request_mem_region is used to test if
|
||||
* the ehci driver is loaded.
|
||||
*/
|
||||
if (np != NULL) {
|
||||
if (!of_address_to_resource(np, 0, &res)) {
|
||||
if (!request_mem_region(res.start, 0x4, hcd_name)) {
|
||||
writel_be((readl_be(&ohci->regs->control) |
|
||||
OHCI_USB_SUSPEND), &ohci->regs->control);
|
||||
(void) readl_be(&ohci->regs->control);
|
||||
} else
|
||||
release_mem_region(res.start, 0x4);
|
||||
} else
|
||||
pr_debug(__FILE__ ": cannot get ehci offset from fdt\n");
|
||||
}
|
||||
|
||||
iounmap(hcd->regs);
|
||||
err_ioremap:
|
||||
irq_dispose_mapping(irq);
|
||||
|
|
|
|||
|
|
@ -201,7 +201,7 @@ static int __devinit ohci_hcd_tmio_drv_probe(struct platform_device *dev)
|
|||
if (!cell)
|
||||
return -EINVAL;
|
||||
|
||||
hcd = usb_create_hcd(&ohci_tmio_hc_driver, &dev->dev, dev->dev.bus_id);
|
||||
hcd = usb_create_hcd(&ohci_tmio_hc_driver, &dev->dev, dev_name(&dev->dev));
|
||||
if (!hcd) {
|
||||
ret = -ENOMEM;
|
||||
goto err_usb_create_hcd;
|
||||
|
|
|
|||
3985
drivers/usb/host/oxu210hp-hcd.c
Normal file
3985
drivers/usb/host/oxu210hp-hcd.c
Normal file
File diff suppressed because it is too large
Load diff
447
drivers/usb/host/oxu210hp.h
Normal file
447
drivers/usb/host/oxu210hp.h
Normal file
|
|
@ -0,0 +1,447 @@
|
|||
/*
|
||||
* Host interface registers
|
||||
*/
|
||||
|
||||
#define OXU_DEVICEID 0x00
|
||||
#define OXU_REV_MASK 0xffff0000
|
||||
#define OXU_REV_SHIFT 16
|
||||
#define OXU_REV_2100 0x2100
|
||||
#define OXU_BO_SHIFT 8
|
||||
#define OXU_BO_MASK (0x3 << OXU_BO_SHIFT)
|
||||
#define OXU_MAJ_REV_SHIFT 4
|
||||
#define OXU_MAJ_REV_MASK (0xf << OXU_MAJ_REV_SHIFT)
|
||||
#define OXU_MIN_REV_SHIFT 0
|
||||
#define OXU_MIN_REV_MASK (0xf << OXU_MIN_REV_SHIFT)
|
||||
#define OXU_HOSTIFCONFIG 0x04
|
||||
#define OXU_SOFTRESET 0x08
|
||||
#define OXU_SRESET (1 << 0)
|
||||
|
||||
#define OXU_PIOBURSTREADCTRL 0x0C
|
||||
|
||||
#define OXU_CHIPIRQSTATUS 0x10
|
||||
#define OXU_CHIPIRQEN_SET 0x14
|
||||
#define OXU_CHIPIRQEN_CLR 0x18
|
||||
#define OXU_USBSPHLPWUI 0x00000080
|
||||
#define OXU_USBOTGLPWUI 0x00000040
|
||||
#define OXU_USBSPHI 0x00000002
|
||||
#define OXU_USBOTGI 0x00000001
|
||||
|
||||
#define OXU_CLKCTRL_SET 0x1C
|
||||
#define OXU_SYSCLKEN 0x00000008
|
||||
#define OXU_USBSPHCLKEN 0x00000002
|
||||
#define OXU_USBOTGCLKEN 0x00000001
|
||||
|
||||
#define OXU_ASO 0x68
|
||||
#define OXU_SPHPOEN 0x00000100
|
||||
#define OXU_OVRCCURPUPDEN 0x00000800
|
||||
#define OXU_ASO_OP (1 << 10)
|
||||
#define OXU_COMPARATOR 0x000004000
|
||||
|
||||
#define OXU_USBMODE 0x1A8
|
||||
#define OXU_VBPS 0x00000020
|
||||
#define OXU_ES_LITTLE 0x00000000
|
||||
#define OXU_CM_HOST_ONLY 0x00000003
|
||||
|
||||
/*
|
||||
* Proper EHCI structs & defines
|
||||
*/
|
||||
|
||||
/* Magic numbers that can affect system performance */
|
||||
#define EHCI_TUNE_CERR 3 /* 0-3 qtd retries; 0 == don't stop */
|
||||
#define EHCI_TUNE_RL_HS 4 /* nak throttle; see 4.9 */
|
||||
#define EHCI_TUNE_RL_TT 0
|
||||
#define EHCI_TUNE_MULT_HS 1 /* 1-3 transactions/uframe; 4.10.3 */
|
||||
#define EHCI_TUNE_MULT_TT 1
|
||||
#define EHCI_TUNE_FLS 2 /* (small) 256 frame schedule */
|
||||
|
||||
struct oxu_hcd;
|
||||
|
||||
/* EHCI register interface, corresponds to EHCI Revision 0.95 specification */
|
||||
|
||||
/* Section 2.2 Host Controller Capability Registers */
|
||||
struct ehci_caps {
|
||||
/* these fields are specified as 8 and 16 bit registers,
|
||||
* but some hosts can't perform 8 or 16 bit PCI accesses.
|
||||
*/
|
||||
u32 hc_capbase;
|
||||
#define HC_LENGTH(p) (((p)>>00)&0x00ff) /* bits 7:0 */
|
||||
#define HC_VERSION(p) (((p)>>16)&0xffff) /* bits 31:16 */
|
||||
u32 hcs_params; /* HCSPARAMS - offset 0x4 */
|
||||
#define HCS_DEBUG_PORT(p) (((p)>>20)&0xf) /* bits 23:20, debug port? */
|
||||
#define HCS_INDICATOR(p) ((p)&(1 << 16)) /* true: has port indicators */
|
||||
#define HCS_N_CC(p) (((p)>>12)&0xf) /* bits 15:12, #companion HCs */
|
||||
#define HCS_N_PCC(p) (((p)>>8)&0xf) /* bits 11:8, ports per CC */
|
||||
#define HCS_PORTROUTED(p) ((p)&(1 << 7)) /* true: port routing */
|
||||
#define HCS_PPC(p) ((p)&(1 << 4)) /* true: port power control */
|
||||
#define HCS_N_PORTS(p) (((p)>>0)&0xf) /* bits 3:0, ports on HC */
|
||||
|
||||
u32 hcc_params; /* HCCPARAMS - offset 0x8 */
|
||||
#define HCC_EXT_CAPS(p) (((p)>>8)&0xff) /* for pci extended caps */
|
||||
#define HCC_ISOC_CACHE(p) ((p)&(1 << 7)) /* true: can cache isoc frame */
|
||||
#define HCC_ISOC_THRES(p) (((p)>>4)&0x7) /* bits 6:4, uframes cached */
|
||||
#define HCC_CANPARK(p) ((p)&(1 << 2)) /* true: can park on async qh */
|
||||
#define HCC_PGM_FRAMELISTLEN(p) ((p)&(1 << 1)) /* true: periodic_size changes*/
|
||||
#define HCC_64BIT_ADDR(p) ((p)&(1)) /* true: can use 64-bit addr */
|
||||
u8 portroute[8]; /* nibbles for routing - offset 0xC */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
|
||||
/* Section 2.3 Host Controller Operational Registers */
|
||||
struct ehci_regs {
|
||||
/* USBCMD: offset 0x00 */
|
||||
u32 command;
|
||||
/* 23:16 is r/w intr rate, in microframes; default "8" == 1/msec */
|
||||
#define CMD_PARK (1<<11) /* enable "park" on async qh */
|
||||
#define CMD_PARK_CNT(c) (((c)>>8)&3) /* how many transfers to park for */
|
||||
#define CMD_LRESET (1<<7) /* partial reset (no ports, etc) */
|
||||
#define CMD_IAAD (1<<6) /* "doorbell" interrupt async advance */
|
||||
#define CMD_ASE (1<<5) /* async schedule enable */
|
||||
#define CMD_PSE (1<<4) /* periodic schedule enable */
|
||||
/* 3:2 is periodic frame list size */
|
||||
#define CMD_RESET (1<<1) /* reset HC not bus */
|
||||
#define CMD_RUN (1<<0) /* start/stop HC */
|
||||
|
||||
/* USBSTS: offset 0x04 */
|
||||
u32 status;
|
||||
#define STS_ASS (1<<15) /* Async Schedule Status */
|
||||
#define STS_PSS (1<<14) /* Periodic Schedule Status */
|
||||
#define STS_RECL (1<<13) /* Reclamation */
|
||||
#define STS_HALT (1<<12) /* Not running (any reason) */
|
||||
/* some bits reserved */
|
||||
/* these STS_* flags are also intr_enable bits (USBINTR) */
|
||||
#define STS_IAA (1<<5) /* Interrupted on async advance */
|
||||
#define STS_FATAL (1<<4) /* such as some PCI access errors */
|
||||
#define STS_FLR (1<<3) /* frame list rolled over */
|
||||
#define STS_PCD (1<<2) /* port change detect */
|
||||
#define STS_ERR (1<<1) /* "error" completion (overflow, ...) */
|
||||
#define STS_INT (1<<0) /* "normal" completion (short, ...) */
|
||||
|
||||
#define INTR_MASK (STS_IAA | STS_FATAL | STS_PCD | STS_ERR | STS_INT)
|
||||
|
||||
/* USBINTR: offset 0x08 */
|
||||
u32 intr_enable;
|
||||
|
||||
/* FRINDEX: offset 0x0C */
|
||||
u32 frame_index; /* current microframe number */
|
||||
/* CTRLDSSEGMENT: offset 0x10 */
|
||||
u32 segment; /* address bits 63:32 if needed */
|
||||
/* PERIODICLISTBASE: offset 0x14 */
|
||||
u32 frame_list; /* points to periodic list */
|
||||
/* ASYNCLISTADDR: offset 0x18 */
|
||||
u32 async_next; /* address of next async queue head */
|
||||
|
||||
u32 reserved[9];
|
||||
|
||||
/* CONFIGFLAG: offset 0x40 */
|
||||
u32 configured_flag;
|
||||
#define FLAG_CF (1<<0) /* true: we'll support "high speed" */
|
||||
|
||||
/* PORTSC: offset 0x44 */
|
||||
u32 port_status[0]; /* up to N_PORTS */
|
||||
/* 31:23 reserved */
|
||||
#define PORT_WKOC_E (1<<22) /* wake on overcurrent (enable) */
|
||||
#define PORT_WKDISC_E (1<<21) /* wake on disconnect (enable) */
|
||||
#define PORT_WKCONN_E (1<<20) /* wake on connect (enable) */
|
||||
/* 19:16 for port testing */
|
||||
#define PORT_LED_OFF (0<<14)
|
||||
#define PORT_LED_AMBER (1<<14)
|
||||
#define PORT_LED_GREEN (2<<14)
|
||||
#define PORT_LED_MASK (3<<14)
|
||||
#define PORT_OWNER (1<<13) /* true: companion hc owns this port */
|
||||
#define PORT_POWER (1<<12) /* true: has power (see PPC) */
|
||||
#define PORT_USB11(x) (((x)&(3<<10)) == (1<<10)) /* USB 1.1 device */
|
||||
/* 11:10 for detecting lowspeed devices (reset vs release ownership) */
|
||||
/* 9 reserved */
|
||||
#define PORT_RESET (1<<8) /* reset port */
|
||||
#define PORT_SUSPEND (1<<7) /* suspend port */
|
||||
#define PORT_RESUME (1<<6) /* resume it */
|
||||
#define PORT_OCC (1<<5) /* over current change */
|
||||
#define PORT_OC (1<<4) /* over current active */
|
||||
#define PORT_PEC (1<<3) /* port enable change */
|
||||
#define PORT_PE (1<<2) /* port enable */
|
||||
#define PORT_CSC (1<<1) /* connect status change */
|
||||
#define PORT_CONNECT (1<<0) /* device connected */
|
||||
#define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC)
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* Appendix C, Debug port ... intended for use with special "debug devices"
|
||||
* that can help if there's no serial console. (nonstandard enumeration.)
|
||||
*/
|
||||
struct ehci_dbg_port {
|
||||
u32 control;
|
||||
#define DBGP_OWNER (1<<30)
|
||||
#define DBGP_ENABLED (1<<28)
|
||||
#define DBGP_DONE (1<<16)
|
||||
#define DBGP_INUSE (1<<10)
|
||||
#define DBGP_ERRCODE(x) (((x)>>7)&0x07)
|
||||
# define DBGP_ERR_BAD 1
|
||||
# define DBGP_ERR_SIGNAL 2
|
||||
#define DBGP_ERROR (1<<6)
|
||||
#define DBGP_GO (1<<5)
|
||||
#define DBGP_OUT (1<<4)
|
||||
#define DBGP_LEN(x) (((x)>>0)&0x0f)
|
||||
u32 pids;
|
||||
#define DBGP_PID_GET(x) (((x)>>16)&0xff)
|
||||
#define DBGP_PID_SET(data, tok) (((data)<<8)|(tok))
|
||||
u32 data03;
|
||||
u32 data47;
|
||||
u32 address;
|
||||
#define DBGP_EPADDR(dev, ep) (((dev)<<8)|(ep))
|
||||
} __attribute__ ((packed));
|
||||
|
||||
|
||||
#define QTD_NEXT(dma) cpu_to_le32((u32)dma)
|
||||
|
||||
/*
|
||||
* EHCI Specification 0.95 Section 3.5
|
||||
* QTD: describe data transfer components (buffer, direction, ...)
|
||||
* See Fig 3-6 "Queue Element Transfer Descriptor Block Diagram".
|
||||
*
|
||||
* These are associated only with "QH" (Queue Head) structures,
|
||||
* used with control, bulk, and interrupt transfers.
|
||||
*/
|
||||
struct ehci_qtd {
|
||||
/* first part defined by EHCI spec */
|
||||
__le32 hw_next; /* see EHCI 3.5.1 */
|
||||
__le32 hw_alt_next; /* see EHCI 3.5.2 */
|
||||
__le32 hw_token; /* see EHCI 3.5.3 */
|
||||
#define QTD_TOGGLE (1 << 31) /* data toggle */
|
||||
#define QTD_LENGTH(tok) (((tok)>>16) & 0x7fff)
|
||||
#define QTD_IOC (1 << 15) /* interrupt on complete */
|
||||
#define QTD_CERR(tok) (((tok)>>10) & 0x3)
|
||||
#define QTD_PID(tok) (((tok)>>8) & 0x3)
|
||||
#define QTD_STS_ACTIVE (1 << 7) /* HC may execute this */
|
||||
#define QTD_STS_HALT (1 << 6) /* halted on error */
|
||||
#define QTD_STS_DBE (1 << 5) /* data buffer error (in HC) */
|
||||
#define QTD_STS_BABBLE (1 << 4) /* device was babbling (qtd halted) */
|
||||
#define QTD_STS_XACT (1 << 3) /* device gave illegal response */
|
||||
#define QTD_STS_MMF (1 << 2) /* incomplete split transaction */
|
||||
#define QTD_STS_STS (1 << 1) /* split transaction state */
|
||||
#define QTD_STS_PING (1 << 0) /* issue PING? */
|
||||
__le32 hw_buf[5]; /* see EHCI 3.5.4 */
|
||||
__le32 hw_buf_hi[5]; /* Appendix B */
|
||||
|
||||
/* the rest is HCD-private */
|
||||
dma_addr_t qtd_dma; /* qtd address */
|
||||
struct list_head qtd_list; /* sw qtd list */
|
||||
struct urb *urb; /* qtd's urb */
|
||||
size_t length; /* length of buffer */
|
||||
|
||||
u32 qtd_buffer_len;
|
||||
void *buffer;
|
||||
dma_addr_t buffer_dma;
|
||||
void *transfer_buffer;
|
||||
void *transfer_dma;
|
||||
} __attribute__ ((aligned(32)));
|
||||
|
||||
/* mask NakCnt+T in qh->hw_alt_next */
|
||||
#define QTD_MASK __constant_cpu_to_le32 (~0x1f)
|
||||
|
||||
#define IS_SHORT_READ(token) (QTD_LENGTH(token) != 0 && QTD_PID(token) == 1)
|
||||
|
||||
/* Type tag from {qh, itd, sitd, fstn}->hw_next */
|
||||
#define Q_NEXT_TYPE(dma) ((dma) & __constant_cpu_to_le32 (3 << 1))
|
||||
|
||||
/* values for that type tag */
|
||||
#define Q_TYPE_QH __constant_cpu_to_le32 (1 << 1)
|
||||
|
||||
/* next async queue entry, or pointer to interrupt/periodic QH */
|
||||
#define QH_NEXT(dma) (cpu_to_le32(((u32)dma)&~0x01f)|Q_TYPE_QH)
|
||||
|
||||
/* for periodic/async schedules and qtd lists, mark end of list */
|
||||
#define EHCI_LIST_END __constant_cpu_to_le32(1) /* "null pointer" to hw */
|
||||
|
||||
/*
|
||||
* Entries in periodic shadow table are pointers to one of four kinds
|
||||
* of data structure. That's dictated by the hardware; a type tag is
|
||||
* encoded in the low bits of the hardware's periodic schedule. Use
|
||||
* Q_NEXT_TYPE to get the tag.
|
||||
*
|
||||
* For entries in the async schedule, the type tag always says "qh".
|
||||
*/
|
||||
union ehci_shadow {
|
||||
struct ehci_qh *qh; /* Q_TYPE_QH */
|
||||
__le32 *hw_next; /* (all types) */
|
||||
void *ptr;
|
||||
};
|
||||
|
||||
/*
|
||||
* EHCI Specification 0.95 Section 3.6
|
||||
* QH: describes control/bulk/interrupt endpoints
|
||||
* See Fig 3-7 "Queue Head Structure Layout".
|
||||
*
|
||||
* These appear in both the async and (for interrupt) periodic schedules.
|
||||
*/
|
||||
|
||||
struct ehci_qh {
|
||||
/* first part defined by EHCI spec */
|
||||
__le32 hw_next; /* see EHCI 3.6.1 */
|
||||
__le32 hw_info1; /* see EHCI 3.6.2 */
|
||||
#define QH_HEAD 0x00008000
|
||||
__le32 hw_info2; /* see EHCI 3.6.2 */
|
||||
#define QH_SMASK 0x000000ff
|
||||
#define QH_CMASK 0x0000ff00
|
||||
#define QH_HUBADDR 0x007f0000
|
||||
#define QH_HUBPORT 0x3f800000
|
||||
#define QH_MULT 0xc0000000
|
||||
__le32 hw_current; /* qtd list - see EHCI 3.6.4 */
|
||||
|
||||
/* qtd overlay (hardware parts of a struct ehci_qtd) */
|
||||
__le32 hw_qtd_next;
|
||||
__le32 hw_alt_next;
|
||||
__le32 hw_token;
|
||||
__le32 hw_buf[5];
|
||||
__le32 hw_buf_hi[5];
|
||||
|
||||
/* the rest is HCD-private */
|
||||
dma_addr_t qh_dma; /* address of qh */
|
||||
union ehci_shadow qh_next; /* ptr to qh; or periodic */
|
||||
struct list_head qtd_list; /* sw qtd list */
|
||||
struct ehci_qtd *dummy;
|
||||
struct ehci_qh *reclaim; /* next to reclaim */
|
||||
|
||||
struct oxu_hcd *oxu;
|
||||
struct kref kref;
|
||||
unsigned stamp;
|
||||
|
||||
u8 qh_state;
|
||||
#define QH_STATE_LINKED 1 /* HC sees this */
|
||||
#define QH_STATE_UNLINK 2 /* HC may still see this */
|
||||
#define QH_STATE_IDLE 3 /* HC doesn't see this */
|
||||
#define QH_STATE_UNLINK_WAIT 4 /* LINKED and on reclaim q */
|
||||
#define QH_STATE_COMPLETING 5 /* don't touch token.HALT */
|
||||
|
||||
/* periodic schedule info */
|
||||
u8 usecs; /* intr bandwidth */
|
||||
u8 gap_uf; /* uframes split/csplit gap */
|
||||
u8 c_usecs; /* ... split completion bw */
|
||||
u16 tt_usecs; /* tt downstream bandwidth */
|
||||
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 */
|
||||
} __attribute__ ((aligned(32)));
|
||||
|
||||
/*
|
||||
* Proper OXU210HP structs
|
||||
*/
|
||||
|
||||
#define OXU_OTG_CORE_OFFSET 0x00400
|
||||
#define OXU_OTG_CAP_OFFSET (OXU_OTG_CORE_OFFSET + 0x100)
|
||||
#define OXU_SPH_CORE_OFFSET 0x00800
|
||||
#define OXU_SPH_CAP_OFFSET (OXU_SPH_CORE_OFFSET + 0x100)
|
||||
|
||||
#define OXU_OTG_MEM 0xE000
|
||||
#define OXU_SPH_MEM 0x16000
|
||||
|
||||
/* Only how many elements & element structure are specifies here. */
|
||||
/* 2 host controllers are enabled - total size <= 28 kbytes */
|
||||
#define DEFAULT_I_TDPS 1024
|
||||
#define QHEAD_NUM 16
|
||||
#define QTD_NUM 32
|
||||
#define SITD_NUM 8
|
||||
#define MURB_NUM 8
|
||||
|
||||
#define BUFFER_NUM 8
|
||||
#define BUFFER_SIZE 512
|
||||
|
||||
struct oxu_info {
|
||||
struct usb_hcd *hcd[2];
|
||||
};
|
||||
|
||||
struct oxu_buf {
|
||||
u8 buffer[BUFFER_SIZE];
|
||||
} __attribute__ ((aligned(BUFFER_SIZE)));
|
||||
|
||||
struct oxu_onchip_mem {
|
||||
struct oxu_buf db_pool[BUFFER_NUM];
|
||||
|
||||
u32 frame_list[DEFAULT_I_TDPS];
|
||||
struct ehci_qh qh_pool[QHEAD_NUM];
|
||||
struct ehci_qtd qtd_pool[QTD_NUM];
|
||||
} __attribute__ ((aligned(4 << 10)));
|
||||
|
||||
#define EHCI_MAX_ROOT_PORTS 15 /* see HCS_N_PORTS */
|
||||
|
||||
struct oxu_murb {
|
||||
struct urb urb;
|
||||
struct urb *main;
|
||||
u8 last;
|
||||
};
|
||||
|
||||
struct oxu_hcd { /* one per controller */
|
||||
unsigned int is_otg:1;
|
||||
|
||||
u8 qh_used[QHEAD_NUM];
|
||||
u8 qtd_used[QTD_NUM];
|
||||
u8 db_used[BUFFER_NUM];
|
||||
u8 murb_used[MURB_NUM];
|
||||
|
||||
struct oxu_onchip_mem __iomem *mem;
|
||||
spinlock_t mem_lock;
|
||||
|
||||
struct timer_list urb_timer;
|
||||
|
||||
struct ehci_caps __iomem *caps;
|
||||
struct ehci_regs __iomem *regs;
|
||||
|
||||
__u32 hcs_params; /* cached register copy */
|
||||
spinlock_t lock;
|
||||
|
||||
/* async schedule support */
|
||||
struct ehci_qh *async;
|
||||
struct ehci_qh *reclaim;
|
||||
unsigned reclaim_ready:1;
|
||||
unsigned scanning:1;
|
||||
|
||||
/* periodic schedule support */
|
||||
unsigned periodic_size;
|
||||
__le32 *periodic; /* hw periodic table */
|
||||
dma_addr_t periodic_dma;
|
||||
unsigned i_thresh; /* uframes HC might cache */
|
||||
|
||||
union ehci_shadow *pshadow; /* mirror hw periodic table */
|
||||
int next_uframe; /* scan periodic, start here */
|
||||
unsigned periodic_sched; /* periodic activity count */
|
||||
|
||||
/* per root hub port */
|
||||
unsigned long reset_done[EHCI_MAX_ROOT_PORTS];
|
||||
/* bit vectors (one bit per port) */
|
||||
unsigned long bus_suspended; /* which ports were
|
||||
* already suspended at the
|
||||
* start of a bus suspend
|
||||
*/
|
||||
unsigned long companion_ports;/* which ports are dedicated
|
||||
* to the companion controller
|
||||
*/
|
||||
|
||||
struct timer_list watchdog;
|
||||
unsigned long actions;
|
||||
unsigned stamp;
|
||||
unsigned long next_statechange;
|
||||
u32 command;
|
||||
|
||||
/* SILICON QUIRKS */
|
||||
struct list_head urb_list; /* this is the head to urb
|
||||
* queue that didn't get enough
|
||||
* resources
|
||||
*/
|
||||
struct oxu_murb *murb_pool; /* murb per split big urb */
|
||||
unsigned urb_len;
|
||||
|
||||
u8 sbrn; /* packed release number */
|
||||
};
|
||||
|
||||
#define EHCI_IAA_JIFFIES (HZ/100) /* arbitrary; ~10 msec */
|
||||
#define EHCI_IO_JIFFIES (HZ/10) /* io watchdog > irq_thresh */
|
||||
#define EHCI_ASYNC_JIFFIES (HZ/20) /* async idle timeout */
|
||||
#define EHCI_SHRINK_JIFFIES (HZ/200) /* async qh unlink delay */
|
||||
|
||||
enum ehci_timer_action {
|
||||
TIMER_IO_WATCHDOG,
|
||||
TIMER_IAA_WATCHDOG,
|
||||
TIMER_ASYNC_SHRINK,
|
||||
TIMER_ASYNC_OFF,
|
||||
};
|
||||
|
||||
#include <linux/oxu210hp.h>
|
||||
|
|
@ -172,9 +172,9 @@ static void __devinit quirk_usb_handoff_ohci(struct pci_dev *pdev)
|
|||
if (!mmio_resource_enabled(pdev, 0))
|
||||
return;
|
||||
|
||||
base = ioremap_nocache(pci_resource_start(pdev, 0),
|
||||
pci_resource_len(pdev, 0));
|
||||
if (base == NULL) return;
|
||||
base = pci_ioremap_bar(pdev, 0);
|
||||
if (base == NULL)
|
||||
return;
|
||||
|
||||
/* On PA-RISC, PDC can leave IR set incorrectly; ignore it there. */
|
||||
#ifndef __hppa__
|
||||
|
|
@ -221,9 +221,9 @@ static void __devinit quirk_usb_disable_ehci(struct pci_dev *pdev)
|
|||
if (!mmio_resource_enabled(pdev, 0))
|
||||
return;
|
||||
|
||||
base = ioremap_nocache(pci_resource_start(pdev, 0),
|
||||
pci_resource_len(pdev, 0));
|
||||
if (base == NULL) return;
|
||||
base = pci_ioremap_bar(pdev, 0);
|
||||
if (base == NULL)
|
||||
return;
|
||||
|
||||
cap_length = readb(base);
|
||||
op_reg_base = base + cap_length;
|
||||
|
|
@ -271,7 +271,7 @@ static void __devinit quirk_usb_disable_ehci(struct pci_dev *pdev)
|
|||
/* if boot firmware now owns EHCI, spin till
|
||||
* it hands it over.
|
||||
*/
|
||||
msec = 5000;
|
||||
msec = 1000;
|
||||
while ((cap & EHCI_USBLEGSUP_BIOS) && (msec > 0)) {
|
||||
tried_handoff = 1;
|
||||
msleep(10);
|
||||
|
|
|
|||
|
|
@ -2275,7 +2275,6 @@ static int __init_or_module r8a66597_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#define resource_len(r) (((r)->end - (r)->start) + 1)
|
||||
static int __init r8a66597_probe(struct platform_device *pdev)
|
||||
{
|
||||
#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) && defined(CONFIG_HAVE_CLK)
|
||||
|
|
@ -2296,11 +2295,10 @@ static int __init r8a66597_probe(struct platform_device *pdev)
|
|||
goto clean_up;
|
||||
}
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
||||
(char *)hcd_name);
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
ret = -ENODEV;
|
||||
dev_err(&pdev->dev, "platform_get_resource_byname error.\n");
|
||||
dev_err(&pdev->dev, "platform_get_resource error.\n");
|
||||
goto clean_up;
|
||||
}
|
||||
|
||||
|
|
@ -2315,7 +2313,7 @@ static int __init r8a66597_probe(struct platform_device *pdev)
|
|||
irq = ires->start;
|
||||
irq_trigger = ires->flags & IRQF_TRIGGER_MASK;
|
||||
|
||||
reg = ioremap(res->start, resource_len(res));
|
||||
reg = ioremap(res->start, resource_size(res));
|
||||
if (reg == NULL) {
|
||||
ret = -ENOMEM;
|
||||
dev_err(&pdev->dev, "ioremap error.\n");
|
||||
|
|
|
|||
|
|
@ -942,6 +942,8 @@ static struct pci_driver uhci_pci_driver = {
|
|||
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = usb_hcd_pci_suspend,
|
||||
.suspend_late = usb_hcd_pci_suspend_late,
|
||||
.resume_early = usb_hcd_pci_resume_early,
|
||||
.resume = usb_hcd_pci_resume,
|
||||
#endif /* PM */
|
||||
};
|
||||
|
|
|
|||
|
|
@ -350,17 +350,16 @@ static int mts_scsi_abort(struct scsi_cmnd *srb)
|
|||
static int mts_scsi_host_reset(struct scsi_cmnd *srb)
|
||||
{
|
||||
struct mts_desc* desc = (struct mts_desc*)(srb->device->host->hostdata[0]);
|
||||
int result, rc;
|
||||
int result;
|
||||
|
||||
MTS_DEBUG_GOT_HERE();
|
||||
mts_debug_dump(desc);
|
||||
|
||||
rc = usb_lock_device_for_reset(desc->usb_dev, desc->usb_intf);
|
||||
if (rc < 0)
|
||||
return FAILED;
|
||||
result = usb_reset_device(desc->usb_dev);
|
||||
if (rc)
|
||||
result = usb_lock_device_for_reset(desc->usb_dev, desc->usb_intf);
|
||||
if (result == 0) {
|
||||
result = usb_reset_device(desc->usb_dev);
|
||||
usb_unlock_device(desc->usb_dev);
|
||||
}
|
||||
return result ? FAILED : SUCCESS;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -123,6 +123,11 @@ static int berry_probe(struct usb_interface *intf,
|
|||
{
|
||||
struct usb_device *udev = interface_to_usbdev(intf);
|
||||
|
||||
if (udev->bus_mA < 500) {
|
||||
dbg(&udev->dev, "Not enough power to charge available\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
dbg(&udev->dev, "Power is set to %dmA\n",
|
||||
udev->actconfig->desc.bMaxPower * 2);
|
||||
|
||||
|
|
|
|||
|
|
@ -160,7 +160,7 @@ static int emi26_load_firmware (struct usb_device *dev)
|
|||
err("%s - error loading firmware: error = %d", __func__, err);
|
||||
goto wraperr;
|
||||
}
|
||||
} while (i > 0);
|
||||
} while (rec);
|
||||
|
||||
/* Assert reset (stop the CPU in the EMI) */
|
||||
err = emi26_set_reset(dev,1);
|
||||
|
|
|
|||
|
|
@ -192,8 +192,6 @@ static struct urb *simple_alloc_urb (
|
|||
{
|
||||
struct urb *urb;
|
||||
|
||||
if (bytes < 0)
|
||||
return NULL;
|
||||
urb = usb_alloc_urb (0, GFP_KERNEL);
|
||||
if (!urb)
|
||||
return urb;
|
||||
|
|
|
|||
|
|
@ -3,14 +3,13 @@
|
|||
#
|
||||
|
||||
config USB_MON
|
||||
bool "USB Monitor"
|
||||
depends on USB!=n
|
||||
default y
|
||||
tristate "USB Monitor"
|
||||
depends on USB
|
||||
default y if USB=y
|
||||
default m if USB=m
|
||||
help
|
||||
If you say Y here, a component which captures the USB traffic
|
||||
If you select this option, a component which captures the USB traffic
|
||||
between peripheral-specific drivers and HC drivers will be built.
|
||||
For more information, see <file:Documentation/usb/usbmon.txt>.
|
||||
|
||||
This is somewhat experimental at this time, but it should be safe.
|
||||
|
||||
If unsure, say Y.
|
||||
If unsure, say Y (if allowed), otherwise M.
|
||||
|
|
|
|||
|
|
@ -4,5 +4,4 @@
|
|||
|
||||
usbmon-objs := mon_main.o mon_stat.o mon_text.o mon_bin.o mon_dma.o
|
||||
|
||||
# This does not use CONFIG_USB_MON because we want this to use a tristate.
|
||||
obj-$(CONFIG_USB) += usbmon.o
|
||||
obj-$(CONFIG_USB_MON) += usbmon.o
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ config USB_MUSB_HDRC
|
|||
depends on (USB || USB_GADGET) && HAVE_CLK
|
||||
depends on !SUPERH
|
||||
select TWL4030_USB if MACH_OMAP_3430SDP
|
||||
tristate 'Inventra Highspeed Dual Role Controller (TI, ...)'
|
||||
tristate 'Inventra Highspeed Dual Role Controller (TI, ADI, ...)'
|
||||
help
|
||||
Say Y here if your system has a dual role high speed USB
|
||||
controller based on the Mentor Graphics silicon IP. Then
|
||||
|
|
@ -22,6 +22,9 @@ config USB_MUSB_HDRC
|
|||
Texas Instruments parts using this IP include DaVinci 644x,
|
||||
OMAP 243x, OMAP 343x, and TUSB 6010.
|
||||
|
||||
Analog Devices parts using this IP include Blackfin BF54x,
|
||||
BF525 and BF527.
|
||||
|
||||
If you do not know what this is, please say N.
|
||||
|
||||
To compile this driver as a module, choose M here; the
|
||||
|
|
@ -33,6 +36,8 @@ config USB_MUSB_SOC
|
|||
default y if ARCH_DAVINCI
|
||||
default y if ARCH_OMAP2430
|
||||
default y if ARCH_OMAP34XX
|
||||
default y if (BF54x && !BF544)
|
||||
default y if (BF52x && !BF522 && !BF523)
|
||||
|
||||
comment "DaVinci 644x USB support"
|
||||
depends on USB_MUSB_HDRC && ARCH_DAVINCI
|
||||
|
|
@ -43,6 +48,9 @@ comment "OMAP 243x high speed USB support"
|
|||
comment "OMAP 343x high speed USB support"
|
||||
depends on USB_MUSB_HDRC && ARCH_OMAP34XX
|
||||
|
||||
comment "Blackfin high speed USB Support"
|
||||
depends on USB_MUSB_HDRC && (BF54x && !BF544) || (BF52x && !BF522 && !BF523)
|
||||
|
||||
config USB_TUSB6010
|
||||
boolean "TUSB 6010 support"
|
||||
depends on USB_MUSB_HDRC && !USB_MUSB_SOC
|
||||
|
|
@ -142,7 +150,7 @@ config MUSB_PIO_ONLY
|
|||
config USB_INVENTRA_DMA
|
||||
bool
|
||||
depends on USB_MUSB_HDRC && !MUSB_PIO_ONLY
|
||||
default ARCH_OMAP2430 || ARCH_OMAP34XX
|
||||
default ARCH_OMAP2430 || ARCH_OMAP34XX || BLACKFIN
|
||||
help
|
||||
Enable DMA transfers using Mentor's engine.
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,14 @@ ifeq ($(CONFIG_ARCH_OMAP3430),y)
|
|||
musb_hdrc-objs += omap2430.o
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_BF54x),y)
|
||||
musb_hdrc-objs += blackfin.o
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_BF52x),y)
|
||||
musb_hdrc-objs += blackfin.o
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_USB_GADGET_MUSB_HDRC),y)
|
||||
musb_hdrc-objs += musb_gadget_ep0.o musb_gadget.o
|
||||
endif
|
||||
|
|
|
|||
320
drivers/usb/musb/blackfin.c
Normal file
320
drivers/usb/musb/blackfin.c
Normal file
|
|
@ -0,0 +1,320 @@
|
|||
/*
|
||||
* MUSB OTG controller driver for Blackfin Processors
|
||||
*
|
||||
* Copyright 2006-2008 Analog Devices Inc.
|
||||
*
|
||||
* Enter bugs at http://blackfin.uclinux.org/
|
||||
*
|
||||
* Licensed under the GPL-2 or later.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include <asm/cacheflush.h>
|
||||
|
||||
#include "musb_core.h"
|
||||
#include "blackfin.h"
|
||||
|
||||
/*
|
||||
* Load an endpoint's FIFO
|
||||
*/
|
||||
void musb_write_fifo(struct musb_hw_ep *hw_ep, u16 len, const u8 *src)
|
||||
{
|
||||
void __iomem *fifo = hw_ep->fifo;
|
||||
void __iomem *epio = hw_ep->regs;
|
||||
|
||||
prefetch((u8 *)src);
|
||||
|
||||
musb_writew(epio, MUSB_TXCOUNT, len);
|
||||
|
||||
DBG(4, "TX ep%d fifo %p count %d buf %p, epio %p\n",
|
||||
hw_ep->epnum, fifo, len, src, epio);
|
||||
|
||||
dump_fifo_data(src, len);
|
||||
|
||||
if (unlikely((unsigned long)src & 0x01))
|
||||
outsw_8((unsigned long)fifo, src,
|
||||
len & 0x01 ? (len >> 1) + 1 : len >> 1);
|
||||
else
|
||||
outsw((unsigned long)fifo, src,
|
||||
len & 0x01 ? (len >> 1) + 1 : len >> 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Unload an endpoint's FIFO
|
||||
*/
|
||||
void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst)
|
||||
{
|
||||
void __iomem *fifo = hw_ep->fifo;
|
||||
u8 epnum = hw_ep->epnum;
|
||||
u16 dma_reg = 0;
|
||||
|
||||
DBG(4, "%cX ep%d fifo %p count %d buf %p\n",
|
||||
'R', hw_ep->epnum, fifo, len, dst);
|
||||
|
||||
#ifdef CONFIG_BF52x
|
||||
invalidate_dcache_range((unsigned int)dst,
|
||||
(unsigned int)(dst + len));
|
||||
|
||||
/* Setup DMA address register */
|
||||
dma_reg = (u16) ((u32) dst & 0xFFFF);
|
||||
bfin_write16(USB_DMA_REG(epnum, USB_DMAx_ADDR_LOW), dma_reg);
|
||||
SSYNC();
|
||||
|
||||
dma_reg = (u16) (((u32) dst >> 16) & 0xFFFF);
|
||||
bfin_write16(USB_DMA_REG(epnum, USB_DMAx_ADDR_HIGH), dma_reg);
|
||||
SSYNC();
|
||||
|
||||
/* Setup DMA count register */
|
||||
bfin_write16(USB_DMA_REG(epnum, USB_DMAx_COUNT_LOW), len);
|
||||
bfin_write16(USB_DMA_REG(epnum, USB_DMAx_COUNT_HIGH), 0);
|
||||
SSYNC();
|
||||
|
||||
/* Enable the DMA */
|
||||
dma_reg = (epnum << 4) | DMA_ENA | INT_ENA;
|
||||
bfin_write16(USB_DMA_REG(epnum, USB_DMAx_CTRL), dma_reg);
|
||||
SSYNC();
|
||||
|
||||
/* Wait for compelete */
|
||||
while (!(bfin_read_USB_DMA_INTERRUPT() & (1 << epnum)))
|
||||
cpu_relax();
|
||||
|
||||
/* acknowledge dma interrupt */
|
||||
bfin_write_USB_DMA_INTERRUPT(1 << epnum);
|
||||
SSYNC();
|
||||
|
||||
/* Reset DMA */
|
||||
bfin_write16(USB_DMA_REG(epnum, USB_DMAx_CTRL), 0);
|
||||
SSYNC();
|
||||
#else
|
||||
if (unlikely((unsigned long)dst & 0x01))
|
||||
insw_8((unsigned long)fifo, dst,
|
||||
len & 0x01 ? (len >> 1) + 1 : len >> 1);
|
||||
else
|
||||
insw((unsigned long)fifo, dst,
|
||||
len & 0x01 ? (len >> 1) + 1 : len >> 1);
|
||||
#endif
|
||||
|
||||
dump_fifo_data(dst, len);
|
||||
}
|
||||
|
||||
static irqreturn_t blackfin_interrupt(int irq, void *__hci)
|
||||
{
|
||||
unsigned long flags;
|
||||
irqreturn_t retval = IRQ_NONE;
|
||||
struct musb *musb = __hci;
|
||||
|
||||
spin_lock_irqsave(&musb->lock, flags);
|
||||
|
||||
musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB);
|
||||
musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX);
|
||||
musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX);
|
||||
|
||||
if (musb->int_usb || musb->int_tx || musb->int_rx) {
|
||||
musb_writeb(musb->mregs, MUSB_INTRUSB, musb->int_usb);
|
||||
musb_writew(musb->mregs, MUSB_INTRTX, musb->int_tx);
|
||||
musb_writew(musb->mregs, MUSB_INTRRX, musb->int_rx);
|
||||
retval = musb_interrupt(musb);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&musb->lock, flags);
|
||||
|
||||
/* REVISIT we sometimes get spurious IRQs on g_ep0
|
||||
* not clear why... fall in BF54x too.
|
||||
*/
|
||||
if (retval != IRQ_HANDLED)
|
||||
DBG(5, "spurious?\n");
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void musb_conn_timer_handler(unsigned long _musb)
|
||||
{
|
||||
struct musb *musb = (void *)_musb;
|
||||
unsigned long flags;
|
||||
u16 val;
|
||||
|
||||
spin_lock_irqsave(&musb->lock, flags);
|
||||
switch (musb->xceiv.state) {
|
||||
case OTG_STATE_A_IDLE:
|
||||
case OTG_STATE_A_WAIT_BCON:
|
||||
/* Start a new session */
|
||||
val = musb_readw(musb->mregs, MUSB_DEVCTL);
|
||||
val |= MUSB_DEVCTL_SESSION;
|
||||
musb_writew(musb->mregs, MUSB_DEVCTL, val);
|
||||
|
||||
val = musb_readw(musb->mregs, MUSB_DEVCTL);
|
||||
if (!(val & MUSB_DEVCTL_BDEVICE)) {
|
||||
gpio_set_value(musb->config->gpio_vrsel, 1);
|
||||
musb->xceiv.state = OTG_STATE_A_WAIT_BCON;
|
||||
} else {
|
||||
gpio_set_value(musb->config->gpio_vrsel, 0);
|
||||
|
||||
/* Ignore VBUSERROR and SUSPEND IRQ */
|
||||
val = musb_readb(musb->mregs, MUSB_INTRUSBE);
|
||||
val &= ~MUSB_INTR_VBUSERROR;
|
||||
musb_writeb(musb->mregs, MUSB_INTRUSBE, val);
|
||||
|
||||
val = MUSB_INTR_SUSPEND | MUSB_INTR_VBUSERROR;
|
||||
musb_writeb(musb->mregs, MUSB_INTRUSB, val);
|
||||
|
||||
val = MUSB_POWER_HSENAB;
|
||||
musb_writeb(musb->mregs, MUSB_POWER, val);
|
||||
}
|
||||
mod_timer(&musb_conn_timer, jiffies + TIMER_DELAY);
|
||||
break;
|
||||
|
||||
default:
|
||||
DBG(1, "%s state not handled\n", otg_state_string(musb));
|
||||
break;
|
||||
}
|
||||
spin_unlock_irqrestore(&musb->lock, flags);
|
||||
|
||||
DBG(4, "state is %s\n", otg_state_string(musb));
|
||||
}
|
||||
|
||||
void musb_platform_enable(struct musb *musb)
|
||||
{
|
||||
if (is_host_enabled(musb)) {
|
||||
mod_timer(&musb_conn_timer, jiffies + TIMER_DELAY);
|
||||
musb->a_wait_bcon = TIMER_DELAY;
|
||||
}
|
||||
}
|
||||
|
||||
void musb_platform_disable(struct musb *musb)
|
||||
{
|
||||
}
|
||||
|
||||
static void bfin_vbus_power(struct musb *musb, int is_on, int sleeping)
|
||||
{
|
||||
}
|
||||
|
||||
static void bfin_set_vbus(struct musb *musb, int is_on)
|
||||
{
|
||||
if (is_on)
|
||||
gpio_set_value(musb->config->gpio_vrsel, 1);
|
||||
else
|
||||
gpio_set_value(musb->config->gpio_vrsel, 0);
|
||||
|
||||
DBG(1, "VBUS %s, devctl %02x "
|
||||
/* otg %3x conf %08x prcm %08x */ "\n",
|
||||
otg_state_string(musb),
|
||||
musb_readb(musb->mregs, MUSB_DEVCTL));
|
||||
}
|
||||
|
||||
static int bfin_set_power(struct otg_transceiver *x, unsigned mA)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void musb_platform_try_idle(struct musb *musb, unsigned long timeout)
|
||||
{
|
||||
if (is_host_enabled(musb))
|
||||
mod_timer(&musb_conn_timer, jiffies + TIMER_DELAY);
|
||||
}
|
||||
|
||||
int musb_platform_get_vbus_status(struct musb *musb)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void musb_platform_set_mode(struct musb *musb, u8 musb_mode)
|
||||
{
|
||||
}
|
||||
|
||||
int __init musb_platform_init(struct musb *musb)
|
||||
{
|
||||
|
||||
/*
|
||||
* Rev 1.0 BF549 EZ-KITs require PE7 to be high for both DEVICE
|
||||
* and OTG HOST modes, while rev 1.1 and greater require PE7 to
|
||||
* be low for DEVICE mode and high for HOST mode. We set it high
|
||||
* here because we are in host mode
|
||||
*/
|
||||
|
||||
if (gpio_request(musb->config->gpio_vrsel, "USB_VRSEL")) {
|
||||
printk(KERN_ERR "Failed ro request USB_VRSEL GPIO_%d \n",
|
||||
musb->config->gpio_vrsel);
|
||||
return -ENODEV;
|
||||
}
|
||||
gpio_direction_output(musb->config->gpio_vrsel, 0);
|
||||
|
||||
if (ANOMALY_05000346) {
|
||||
bfin_write_USB_APHY_CALIB(ANOMALY_05000346_value);
|
||||
SSYNC();
|
||||
}
|
||||
|
||||
if (ANOMALY_05000347) {
|
||||
bfin_write_USB_APHY_CNTRL(0x0);
|
||||
SSYNC();
|
||||
}
|
||||
|
||||
/* TODO
|
||||
* Set SIC-IVG register
|
||||
*/
|
||||
|
||||
/* Configure PLL oscillator register */
|
||||
bfin_write_USB_PLLOSC_CTRL(0x30a8);
|
||||
SSYNC();
|
||||
|
||||
bfin_write_USB_SRP_CLKDIV((get_sclk()/1000) / 32 - 1);
|
||||
SSYNC();
|
||||
|
||||
bfin_write_USB_EP_NI0_RXMAXP(64);
|
||||
SSYNC();
|
||||
|
||||
bfin_write_USB_EP_NI0_TXMAXP(64);
|
||||
SSYNC();
|
||||
|
||||
/* Route INTRUSB/INTR_RX/INTR_TX to USB_INT0*/
|
||||
bfin_write_USB_GLOBINTR(0x7);
|
||||
SSYNC();
|
||||
|
||||
bfin_write_USB_GLOBAL_CTL(GLOBAL_ENA | EP1_TX_ENA | EP2_TX_ENA |
|
||||
EP3_TX_ENA | EP4_TX_ENA | EP5_TX_ENA |
|
||||
EP6_TX_ENA | EP7_TX_ENA | EP1_RX_ENA |
|
||||
EP2_RX_ENA | EP3_RX_ENA | EP4_RX_ENA |
|
||||
EP5_RX_ENA | EP6_RX_ENA | EP7_RX_ENA);
|
||||
SSYNC();
|
||||
|
||||
if (is_host_enabled(musb)) {
|
||||
musb->board_set_vbus = bfin_set_vbus;
|
||||
setup_timer(&musb_conn_timer,
|
||||
musb_conn_timer_handler, (unsigned long) musb);
|
||||
}
|
||||
if (is_peripheral_enabled(musb))
|
||||
musb->xceiv.set_power = bfin_set_power;
|
||||
|
||||
musb->isr = blackfin_interrupt;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int musb_platform_suspend(struct musb *musb)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int musb_platform_resume(struct musb *musb)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int musb_platform_exit(struct musb *musb)
|
||||
{
|
||||
|
||||
bfin_vbus_power(musb, 0 /*off*/, 1);
|
||||
gpio_free(musb->config->gpio_vrsel);
|
||||
musb_platform_suspend(musb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
52
drivers/usb/musb/blackfin.h
Normal file
52
drivers/usb/musb/blackfin.h
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright (C) 2007 by Analog Devices, Inc.
|
||||
*
|
||||
* The Inventra Controller Driver for Linux is free software; you
|
||||
* can redistribute it and/or modify it under the terms of the GNU
|
||||
* General Public License version 2 as published by the Free Software
|
||||
* Foundation.
|
||||
*/
|
||||
|
||||
#ifndef __MUSB_BLACKFIN_H__
|
||||
#define __MUSB_BLACKFIN_H__
|
||||
|
||||
/*
|
||||
* Blackfin specific definitions
|
||||
*/
|
||||
|
||||
#undef DUMP_FIFO_DATA
|
||||
#ifdef DUMP_FIFO_DATA
|
||||
static void dump_fifo_data(u8 *buf, u16 len)
|
||||
{
|
||||
u8 *tmp = buf;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if (!(i % 16) && i)
|
||||
pr_debug("\n");
|
||||
pr_debug("%02x ", *tmp++);
|
||||
}
|
||||
pr_debug("\n");
|
||||
}
|
||||
#else
|
||||
#define dump_fifo_data(buf, len) do {} while (0)
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_BF52x
|
||||
|
||||
#define USB_DMA_BASE USB_DMA_INTERRUPT
|
||||
#define USB_DMAx_CTRL 0x04
|
||||
#define USB_DMAx_ADDR_LOW 0x08
|
||||
#define USB_DMAx_ADDR_HIGH 0x0C
|
||||
#define USB_DMAx_COUNT_LOW 0x10
|
||||
#define USB_DMAx_COUNT_HIGH 0x14
|
||||
|
||||
#define USB_DMA_REG(ep, reg) (USB_DMA_BASE + 0x20 * ep + reg)
|
||||
#endif
|
||||
|
||||
/* Almost 1 second */
|
||||
#define TIMER_DELAY (1 * HZ)
|
||||
|
||||
static struct timer_list musb_conn_timer;
|
||||
|
||||
#endif /* __MUSB_BLACKFIN_H__ */
|
||||
|
|
@ -32,9 +32,9 @@
|
|||
#include <linux/io.h>
|
||||
#include <linux/gpio.h>
|
||||
|
||||
#include <asm/arch/hardware.h>
|
||||
#include <asm/arch/memory.h>
|
||||
#include <asm/arch/gpio.h>
|
||||
#include <mach/arch/hardware.h>
|
||||
#include <mach/arch/memory.h>
|
||||
#include <mach/arch/gpio.h>
|
||||
#include <asm/mach-types.h>
|
||||
|
||||
#include "musb_core.h"
|
||||
|
|
@ -364,6 +364,18 @@ static irqreturn_t davinci_interrupt(int irq, void *__hci)
|
|||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
int musb_platform_set_mode(struct musb *musb, u8 mode)
|
||||
{
|
||||
/* EVM can't do this (right?) */
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
int musb_platform_set_mode(struct musb *musb, u8 mode)
|
||||
{
|
||||
/* EVM can't do this (right?) */
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
int __init musb_platform_init(struct musb *musb)
|
||||
{
|
||||
void __iomem *tibase = musb->ctrl_base;
|
||||
|
|
|
|||
|
|
@ -148,7 +148,8 @@ static inline struct musb *dev_to_musb(struct device *dev)
|
|||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef CONFIG_USB_TUSB6010
|
||||
#if !defined(CONFIG_USB_TUSB6010) && !defined(CONFIG_BLACKFIN)
|
||||
|
||||
/*
|
||||
* Load an endpoint's FIFO
|
||||
*/
|
||||
|
|
@ -1124,25 +1125,25 @@ fifo_setup(struct musb *musb, struct musb_hw_ep *hw_ep,
|
|||
#endif
|
||||
switch (cfg->style) {
|
||||
case FIFO_TX:
|
||||
musb_writeb(mbase, MUSB_TXFIFOSZ, c_size);
|
||||
musb_writew(mbase, MUSB_TXFIFOADD, c_off);
|
||||
musb_write_txfifosz(mbase, c_size);
|
||||
musb_write_txfifoadd(mbase, c_off);
|
||||
hw_ep->tx_double_buffered = !!(c_size & MUSB_FIFOSZ_DPB);
|
||||
hw_ep->max_packet_sz_tx = maxpacket;
|
||||
break;
|
||||
case FIFO_RX:
|
||||
musb_writeb(mbase, MUSB_RXFIFOSZ, c_size);
|
||||
musb_writew(mbase, MUSB_RXFIFOADD, c_off);
|
||||
musb_write_rxfifosz(mbase, c_size);
|
||||
musb_write_rxfifoadd(mbase, c_off);
|
||||
hw_ep->rx_double_buffered = !!(c_size & MUSB_FIFOSZ_DPB);
|
||||
hw_ep->max_packet_sz_rx = maxpacket;
|
||||
break;
|
||||
case FIFO_RXTX:
|
||||
musb_writeb(mbase, MUSB_TXFIFOSZ, c_size);
|
||||
musb_writew(mbase, MUSB_TXFIFOADD, c_off);
|
||||
musb_write_txfifosz(mbase, c_size);
|
||||
musb_write_txfifoadd(mbase, c_off);
|
||||
hw_ep->rx_double_buffered = !!(c_size & MUSB_FIFOSZ_DPB);
|
||||
hw_ep->max_packet_sz_rx = maxpacket;
|
||||
|
||||
musb_writeb(mbase, MUSB_RXFIFOSZ, c_size);
|
||||
musb_writew(mbase, MUSB_RXFIFOADD, c_off);
|
||||
musb_write_rxfifosz(mbase, c_size);
|
||||
musb_write_rxfifoadd(mbase, c_off);
|
||||
hw_ep->tx_double_buffered = hw_ep->rx_double_buffered;
|
||||
hw_ep->max_packet_sz_tx = maxpacket;
|
||||
|
||||
|
|
@ -1212,7 +1213,7 @@ static int __init ep_config_from_table(struct musb *musb)
|
|||
if (epn >= musb->config->num_eps) {
|
||||
pr_debug("%s: invalid ep %d\n",
|
||||
musb_driver_name, epn);
|
||||
continue;
|
||||
return -EINVAL;
|
||||
}
|
||||
offset = fifo_setup(musb, hw_ep + epn, cfg++, offset);
|
||||
if (offset < 0) {
|
||||
|
|
@ -1246,9 +1247,10 @@ static int __init ep_config_from_table(struct musb *musb)
|
|||
*/
|
||||
static int __init ep_config_from_hw(struct musb *musb)
|
||||
{
|
||||
u8 epnum = 0, reg;
|
||||
u8 epnum = 0;
|
||||
struct musb_hw_ep *hw_ep;
|
||||
void *mbase = musb->mregs;
|
||||
int ret = 0;
|
||||
|
||||
DBG(2, "<== static silicon ep config\n");
|
||||
|
||||
|
|
@ -1258,26 +1260,9 @@ static int __init ep_config_from_hw(struct musb *musb)
|
|||
musb_ep_select(mbase, epnum);
|
||||
hw_ep = musb->endpoints + epnum;
|
||||
|
||||
/* read from core using indexed model */
|
||||
reg = musb_readb(hw_ep->regs, 0x10 + MUSB_FIFOSIZE);
|
||||
if (!reg) {
|
||||
/* 0's returned when no more endpoints */
|
||||
ret = musb_read_fifosize(musb, hw_ep, epnum);
|
||||
if (ret < 0)
|
||||
break;
|
||||
}
|
||||
musb->nr_endpoints++;
|
||||
musb->epmask |= (1 << epnum);
|
||||
|
||||
hw_ep->max_packet_sz_tx = 1 << (reg & 0x0f);
|
||||
|
||||
/* shared TX/RX FIFO? */
|
||||
if ((reg & 0xf0) == 0xf0) {
|
||||
hw_ep->max_packet_sz_rx = hw_ep->max_packet_sz_tx;
|
||||
hw_ep->is_shared_fifo = true;
|
||||
continue;
|
||||
} else {
|
||||
hw_ep->max_packet_sz_rx = 1 << ((reg & 0xf0) >> 4);
|
||||
hw_ep->is_shared_fifo = false;
|
||||
}
|
||||
|
||||
/* FIXME set up hw_ep->{rx,tx}_double_buffered */
|
||||
|
||||
|
|
@ -1326,7 +1311,7 @@ static int __init musb_core_init(u16 musb_type, struct musb *musb)
|
|||
|
||||
/* log core options (read using indexed model) */
|
||||
musb_ep_select(mbase, 0);
|
||||
reg = musb_readb(mbase, 0x10 + MUSB_CONFIGDATA);
|
||||
reg = musb_read_configdata(mbase);
|
||||
|
||||
strcpy(aInfo, (reg & MUSB_CONFIGDATA_UTMIDW) ? "UTMI-16" : "UTMI-8");
|
||||
if (reg & MUSB_CONFIGDATA_DYNFIFO)
|
||||
|
|
@ -1391,7 +1376,7 @@ static int __init musb_core_init(u16 musb_type, struct musb *musb)
|
|||
}
|
||||
|
||||
/* log release info */
|
||||
hwvers = musb_readw(mbase, MUSB_HWVERS);
|
||||
hwvers = musb_read_hwvers(mbase);
|
||||
rev_major = (hwvers >> 10) & 0x1f;
|
||||
rev_minor = hwvers & 0x3ff;
|
||||
snprintf(aRevision, 32, "%d.%d%s", rev_major,
|
||||
|
|
@ -1400,8 +1385,7 @@ static int __init musb_core_init(u16 musb_type, struct musb *musb)
|
|||
musb_driver_name, type, aRevision, aDate);
|
||||
|
||||
/* configure ep0 */
|
||||
musb->endpoints[0].max_packet_sz_tx = MUSB_EP0_FIFOSIZE;
|
||||
musb->endpoints[0].max_packet_sz_rx = MUSB_EP0_FIFOSIZE;
|
||||
musb_configure_ep0(musb);
|
||||
|
||||
/* discover endpoint configuration */
|
||||
musb->nr_endpoints = 1;
|
||||
|
|
@ -1445,7 +1429,7 @@ static int __init musb_core_init(u16 musb_type, struct musb *musb)
|
|||
|
||||
hw_ep->regs = MUSB_EP_OFFSET(i, 0) + mbase;
|
||||
#ifdef CONFIG_USB_MUSB_HDRC_HCD
|
||||
hw_ep->target_regs = MUSB_BUSCTL_OFFSET(i, 0) + mbase;
|
||||
hw_ep->target_regs = musb_read_target_reg_base(i, mbase);
|
||||
hw_ep->rx_reinit = 1;
|
||||
hw_ep->tx_reinit = 1;
|
||||
#endif
|
||||
|
|
@ -1671,17 +1655,20 @@ musb_mode_store(struct device *dev, struct device_attribute *attr,
|
|||
{
|
||||
struct musb *musb = dev_to_musb(dev);
|
||||
unsigned long flags;
|
||||
int status;
|
||||
|
||||
spin_lock_irqsave(&musb->lock, flags);
|
||||
if (!strncmp(buf, "host", 4))
|
||||
musb_platform_set_mode(musb, MUSB_HOST);
|
||||
if (!strncmp(buf, "peripheral", 10))
|
||||
musb_platform_set_mode(musb, MUSB_PERIPHERAL);
|
||||
if (!strncmp(buf, "otg", 3))
|
||||
musb_platform_set_mode(musb, MUSB_OTG);
|
||||
if (sysfs_streq(buf, "host"))
|
||||
status = musb_platform_set_mode(musb, MUSB_HOST);
|
||||
else if (sysfs_streq(buf, "peripheral"))
|
||||
status = musb_platform_set_mode(musb, MUSB_PERIPHERAL);
|
||||
else if (sysfs_streq(buf, "otg"))
|
||||
status = musb_platform_set_mode(musb, MUSB_OTG);
|
||||
else
|
||||
status = -EINVAL;
|
||||
spin_unlock_irqrestore(&musb->lock, flags);
|
||||
|
||||
return n;
|
||||
return (status == 0) ? n : status;
|
||||
}
|
||||
static DEVICE_ATTR(mode, 0644, musb_mode_show, musb_mode_store);
|
||||
|
||||
|
|
@ -1781,7 +1768,7 @@ allocate_instance(struct device *dev,
|
|||
#ifdef CONFIG_USB_MUSB_HDRC_HCD
|
||||
struct usb_hcd *hcd;
|
||||
|
||||
hcd = usb_create_hcd(&musb_hc_driver, dev, dev->bus_id);
|
||||
hcd = usb_create_hcd(&musb_hc_driver, dev, dev_name(dev));
|
||||
if (!hcd)
|
||||
return NULL;
|
||||
/* usbcore sets dev->driver_data to hcd, and sometimes uses that... */
|
||||
|
|
@ -1810,7 +1797,6 @@ allocate_instance(struct device *dev,
|
|||
for (epnum = 0, ep = musb->endpoints;
|
||||
epnum < musb->config->num_eps;
|
||||
epnum++, ep++) {
|
||||
|
||||
ep->musb = musb;
|
||||
ep->epnum = epnum;
|
||||
}
|
||||
|
|
@ -1838,7 +1824,7 @@ static void musb_free(struct musb *musb)
|
|||
musb_gadget_cleanup(musb);
|
||||
#endif
|
||||
|
||||
if (musb->nIrq >= 0) {
|
||||
if (musb->nIrq >= 0 && musb->irq_wake) {
|
||||
disable_irq_wake(musb->nIrq);
|
||||
free_irq(musb->nIrq, musb);
|
||||
}
|
||||
|
|
@ -1984,15 +1970,19 @@ bad_config:
|
|||
INIT_WORK(&musb->irq_work, musb_irq_work);
|
||||
|
||||
/* attach to the IRQ */
|
||||
if (request_irq(nIrq, musb->isr, 0, dev->bus_id, musb)) {
|
||||
if (request_irq(nIrq, musb->isr, 0, dev_name(dev), musb)) {
|
||||
dev_err(dev, "request_irq %d failed!\n", nIrq);
|
||||
status = -ENODEV;
|
||||
goto fail2;
|
||||
}
|
||||
musb->nIrq = nIrq;
|
||||
/* FIXME this handles wakeup irqs wrong */
|
||||
if (enable_irq_wake(nIrq) == 0)
|
||||
if (enable_irq_wake(nIrq) == 0) {
|
||||
musb->irq_wake = 1;
|
||||
device_init_wakeup(dev, 1);
|
||||
} else {
|
||||
musb->irq_wake = 0;
|
||||
}
|
||||
|
||||
pr_info("%s: USB %s mode controller at %p using %s, IRQ %d\n",
|
||||
musb_driver_name,
|
||||
|
|
|
|||
|
|
@ -191,7 +191,7 @@ enum musb_g_ep0_state {
|
|||
*/
|
||||
|
||||
#if defined(CONFIG_ARCH_DAVINCI) || defined(CONFIG_ARCH_OMAP2430) \
|
||||
|| defined(CONFIG_ARCH_OMAP3430)
|
||||
|| defined(CONFIG_ARCH_OMAP3430) || defined(CONFIG_BLACKFIN)
|
||||
/* REVISIT indexed access seemed to
|
||||
* misbehave (on DaVinci) for at least peripheral IN ...
|
||||
*/
|
||||
|
|
@ -359,6 +359,7 @@ struct musb {
|
|||
struct otg_transceiver xceiv;
|
||||
|
||||
int nIrq;
|
||||
unsigned irq_wake:1;
|
||||
|
||||
struct musb_hw_ep endpoints[MUSB_C_NUM_EPS];
|
||||
#define control_ep endpoints
|
||||
|
|
@ -447,6 +448,70 @@ static inline struct musb *gadget_to_musb(struct usb_gadget *g)
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_BLACKFIN
|
||||
static inline int musb_read_fifosize(struct musb *musb,
|
||||
struct musb_hw_ep *hw_ep, u8 epnum)
|
||||
{
|
||||
musb->nr_endpoints++;
|
||||
musb->epmask |= (1 << epnum);
|
||||
|
||||
if (epnum < 5) {
|
||||
hw_ep->max_packet_sz_tx = 128;
|
||||
hw_ep->max_packet_sz_rx = 128;
|
||||
} else {
|
||||
hw_ep->max_packet_sz_tx = 1024;
|
||||
hw_ep->max_packet_sz_rx = 1024;
|
||||
}
|
||||
hw_ep->is_shared_fifo = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void musb_configure_ep0(struct musb *musb)
|
||||
{
|
||||
musb->endpoints[0].max_packet_sz_tx = MUSB_EP0_FIFOSIZE;
|
||||
musb->endpoints[0].max_packet_sz_rx = MUSB_EP0_FIFOSIZE;
|
||||
musb->endpoints[0].is_shared_fifo = true;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline int musb_read_fifosize(struct musb *musb,
|
||||
struct musb_hw_ep *hw_ep, u8 epnum)
|
||||
{
|
||||
u8 reg = 0;
|
||||
|
||||
/* read from core using indexed model */
|
||||
reg = musb_readb(hw_ep->regs, 0x10 + MUSB_FIFOSIZE);
|
||||
/* 0's returned when no more endpoints */
|
||||
if (!reg)
|
||||
return -ENODEV;
|
||||
|
||||
musb->nr_endpoints++;
|
||||
musb->epmask |= (1 << epnum);
|
||||
|
||||
hw_ep->max_packet_sz_tx = 1 << (reg & 0x0f);
|
||||
|
||||
/* shared TX/RX FIFO? */
|
||||
if ((reg & 0xf0) == 0xf0) {
|
||||
hw_ep->max_packet_sz_rx = hw_ep->max_packet_sz_tx;
|
||||
hw_ep->is_shared_fifo = true;
|
||||
return 0;
|
||||
} else {
|
||||
hw_ep->max_packet_sz_rx = 1 << ((reg & 0xf0) >> 4);
|
||||
hw_ep->is_shared_fifo = false;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void musb_configure_ep0(struct musb *musb)
|
||||
{
|
||||
musb->endpoints[0].max_packet_sz_tx = MUSB_EP0_FIFOSIZE;
|
||||
musb->endpoints[0].max_packet_sz_rx = MUSB_EP0_FIFOSIZE;
|
||||
}
|
||||
#endif /* CONFIG_BLACKFIN */
|
||||
|
||||
|
||||
/***************************** Glue it together *****************************/
|
||||
|
||||
|
|
@ -467,16 +532,16 @@ extern void musb_platform_disable(struct musb *musb);
|
|||
|
||||
extern void musb_hnp_stop(struct musb *musb);
|
||||
|
||||
extern void musb_platform_set_mode(struct musb *musb, u8 musb_mode);
|
||||
extern int musb_platform_set_mode(struct musb *musb, u8 musb_mode);
|
||||
|
||||
#if defined(CONFIG_USB_TUSB6010) || \
|
||||
#if defined(CONFIG_USB_TUSB6010) || defined(CONFIG_BLACKFIN) || \
|
||||
defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP34XX)
|
||||
extern void musb_platform_try_idle(struct musb *musb, unsigned long timeout);
|
||||
#else
|
||||
#define musb_platform_try_idle(x, y) do {} while (0)
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_TUSB6010
|
||||
#if defined(CONFIG_USB_TUSB6010) || defined(CONFIG_BLACKFIN)
|
||||
extern int musb_platform_get_vbus_status(struct musb *musb);
|
||||
#else
|
||||
#define musb_platform_get_vbus_status(x) 0
|
||||
|
|
|
|||
|
|
@ -1633,7 +1633,7 @@ int __init musb_gadget_setup(struct musb *musb)
|
|||
musb->g.speed = USB_SPEED_UNKNOWN;
|
||||
|
||||
/* this "gadget" abstracts/virtualizes the controller */
|
||||
strcpy(musb->g.dev.bus_id, "gadget");
|
||||
dev_set_name(&musb->g.dev, "gadget");
|
||||
musb->g.dev.parent = musb->controller;
|
||||
musb->g.dev.dma_mask = musb->controller->dma_mask;
|
||||
musb->g.dev.release = musb_gadget_release;
|
||||
|
|
|
|||
|
|
@ -112,18 +112,21 @@ static void musb_h_tx_flush_fifo(struct musb_hw_ep *ep)
|
|||
{
|
||||
void __iomem *epio = ep->regs;
|
||||
u16 csr;
|
||||
u16 lastcsr = 0;
|
||||
int retries = 1000;
|
||||
|
||||
csr = musb_readw(epio, MUSB_TXCSR);
|
||||
while (csr & MUSB_TXCSR_FIFONOTEMPTY) {
|
||||
DBG(5, "Host TX FIFONOTEMPTY csr: %02x\n", csr);
|
||||
if (csr != lastcsr)
|
||||
DBG(3, "Host TX FIFONOTEMPTY csr: %02x\n", csr);
|
||||
lastcsr = csr;
|
||||
csr |= MUSB_TXCSR_FLUSHFIFO;
|
||||
musb_writew(epio, MUSB_TXCSR, csr);
|
||||
csr = musb_readw(epio, MUSB_TXCSR);
|
||||
if (retries-- < 1) {
|
||||
ERR("Could not flush host TX fifo: csr: %04x\n", csr);
|
||||
if (WARN(retries-- < 1,
|
||||
"Could not flush host TX%d fifo: csr: %04x\n",
|
||||
ep->epnum, csr))
|
||||
return;
|
||||
}
|
||||
mdelay(1);
|
||||
}
|
||||
}
|
||||
|
|
@ -268,7 +271,7 @@ __musb_giveback(struct musb *musb, struct urb *urb, int status)
|
|||
__releases(musb->lock)
|
||||
__acquires(musb->lock)
|
||||
{
|
||||
DBG(({ int level; switch (urb->status) {
|
||||
DBG(({ int level; switch (status) {
|
||||
case 0:
|
||||
level = 4;
|
||||
break;
|
||||
|
|
@ -283,8 +286,8 @@ __acquires(musb->lock)
|
|||
level = 2;
|
||||
break;
|
||||
}; level; }),
|
||||
"complete %p (%d), dev%d ep%d%s, %d/%d\n",
|
||||
urb, urb->status,
|
||||
"complete %p %pF (%d), dev%d ep%d%s, %d/%d\n",
|
||||
urb, urb->complete, status,
|
||||
usb_pipedevice(urb->pipe),
|
||||
usb_pipeendpoint(urb->pipe),
|
||||
usb_pipein(urb->pipe) ? "in" : "out",
|
||||
|
|
@ -593,12 +596,10 @@ musb_rx_reinit(struct musb *musb, struct musb_qh *qh, struct musb_hw_ep *ep)
|
|||
|
||||
/* target addr and (for multipoint) hub addr/port */
|
||||
if (musb->is_multipoint) {
|
||||
musb_writeb(ep->target_regs, MUSB_RXFUNCADDR,
|
||||
qh->addr_reg);
|
||||
musb_writeb(ep->target_regs, MUSB_RXHUBADDR,
|
||||
qh->h_addr_reg);
|
||||
musb_writeb(ep->target_regs, MUSB_RXHUBPORT,
|
||||
qh->h_port_reg);
|
||||
musb_write_rxfunaddr(ep->target_regs, qh->addr_reg);
|
||||
musb_write_rxhubaddr(ep->target_regs, qh->h_addr_reg);
|
||||
musb_write_rxhubport(ep->target_regs, qh->h_port_reg);
|
||||
|
||||
} else
|
||||
musb_writeb(musb->mregs, MUSB_FADDR, qh->addr_reg);
|
||||
|
||||
|
|
@ -712,15 +713,9 @@ static void musb_ep_program(struct musb *musb, u8 epnum,
|
|||
|
||||
/* target addr and (for multipoint) hub addr/port */
|
||||
if (musb->is_multipoint) {
|
||||
musb_writeb(mbase,
|
||||
MUSB_BUSCTL_OFFSET(epnum, MUSB_TXFUNCADDR),
|
||||
qh->addr_reg);
|
||||
musb_writeb(mbase,
|
||||
MUSB_BUSCTL_OFFSET(epnum, MUSB_TXHUBADDR),
|
||||
qh->h_addr_reg);
|
||||
musb_writeb(mbase,
|
||||
MUSB_BUSCTL_OFFSET(epnum, MUSB_TXHUBPORT),
|
||||
qh->h_port_reg);
|
||||
musb_write_txfunaddr(mbase, epnum, qh->addr_reg);
|
||||
musb_write_txhubaddr(mbase, epnum, qh->h_addr_reg);
|
||||
musb_write_txhubport(mbase, epnum, qh->h_port_reg);
|
||||
/* FIXME if !epnum, do the same for RX ... */
|
||||
} else
|
||||
musb_writeb(mbase, MUSB_FADDR, qh->addr_reg);
|
||||
|
|
@ -988,8 +983,10 @@ static bool musb_h_ep0_continue(struct musb *musb, u16 len, struct urb *urb)
|
|||
if (fifo_count) {
|
||||
fifo_dest = (u8 *) (urb->transfer_buffer
|
||||
+ urb->actual_length);
|
||||
DBG(3, "Sending %d bytes to %p\n",
|
||||
fifo_count, fifo_dest);
|
||||
DBG(3, "Sending %d byte%s to ep0 fifo %p\n",
|
||||
fifo_count,
|
||||
(fifo_count == 1) ? "" : "s",
|
||||
fifo_dest);
|
||||
musb_write_fifo(hw_ep, fifo_count, fifo_dest);
|
||||
|
||||
urb->actual_length += fifo_count;
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@
|
|||
|
||||
#if !defined(CONFIG_ARM) && !defined(CONFIG_SUPERH) \
|
||||
&& !defined(CONFIG_AVR32) && !defined(CONFIG_PPC32) \
|
||||
&& !defined(CONFIG_PPC64)
|
||||
&& !defined(CONFIG_PPC64) && !defined(CONFIG_BLACKFIN)
|
||||
static inline void readsl(const void __iomem *addr, void *buf, int len)
|
||||
{ insl((unsigned long)addr, buf, len); }
|
||||
static inline void readsw(const void __iomem *addr, void *buf, int len)
|
||||
|
|
@ -56,6 +56,8 @@ static inline void writesb(const void __iomem *addr, const void *buf, int len)
|
|||
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_BLACKFIN
|
||||
|
||||
/* NOTE: these offsets are all in bytes */
|
||||
|
||||
static inline u16 musb_readw(const void __iomem *addr, unsigned offset)
|
||||
|
|
@ -114,4 +116,26 @@ static inline void musb_writeb(void __iomem *addr, unsigned offset, u8 data)
|
|||
|
||||
#endif /* CONFIG_USB_TUSB6010 */
|
||||
|
||||
#else
|
||||
|
||||
static inline u8 musb_readb(const void __iomem *addr, unsigned offset)
|
||||
{ return (u8) (bfin_read16(addr + offset)); }
|
||||
|
||||
static inline u16 musb_readw(const void __iomem *addr, unsigned offset)
|
||||
{ return bfin_read16(addr + offset); }
|
||||
|
||||
static inline u32 musb_readl(const void __iomem *addr, unsigned offset)
|
||||
{ return (u32) (bfin_read16(addr + offset)); }
|
||||
|
||||
static inline void musb_writeb(void __iomem *addr, unsigned offset, u8 data)
|
||||
{ bfin_write16(addr + offset, (u16) data); }
|
||||
|
||||
static inline void musb_writew(void __iomem *addr, unsigned offset, u16 data)
|
||||
{ bfin_write16(addr + offset, data); }
|
||||
|
||||
static inline void musb_writel(void __iomem *addr, unsigned offset, u32 data)
|
||||
{ bfin_write16(addr + offset, (u16) data); }
|
||||
|
||||
#endif /* CONFIG_BLACKFIN */
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -37,97 +37,6 @@
|
|||
|
||||
#define MUSB_EP0_FIFOSIZE 64 /* This is non-configurable */
|
||||
|
||||
/*
|
||||
* Common USB registers
|
||||
*/
|
||||
|
||||
#define MUSB_FADDR 0x00 /* 8-bit */
|
||||
#define MUSB_POWER 0x01 /* 8-bit */
|
||||
|
||||
#define MUSB_INTRTX 0x02 /* 16-bit */
|
||||
#define MUSB_INTRRX 0x04
|
||||
#define MUSB_INTRTXE 0x06
|
||||
#define MUSB_INTRRXE 0x08
|
||||
#define MUSB_INTRUSB 0x0A /* 8 bit */
|
||||
#define MUSB_INTRUSBE 0x0B /* 8 bit */
|
||||
#define MUSB_FRAME 0x0C
|
||||
#define MUSB_INDEX 0x0E /* 8 bit */
|
||||
#define MUSB_TESTMODE 0x0F /* 8 bit */
|
||||
|
||||
/* Get offset for a given FIFO from musb->mregs */
|
||||
#ifdef CONFIG_USB_TUSB6010
|
||||
#define MUSB_FIFO_OFFSET(epnum) (0x200 + ((epnum) * 0x20))
|
||||
#else
|
||||
#define MUSB_FIFO_OFFSET(epnum) (0x20 + ((epnum) * 4))
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Additional Control Registers
|
||||
*/
|
||||
|
||||
#define MUSB_DEVCTL 0x60 /* 8 bit */
|
||||
|
||||
/* These are always controlled through the INDEX register */
|
||||
#define MUSB_TXFIFOSZ 0x62 /* 8-bit (see masks) */
|
||||
#define MUSB_RXFIFOSZ 0x63 /* 8-bit (see masks) */
|
||||
#define MUSB_TXFIFOADD 0x64 /* 16-bit offset shifted right 3 */
|
||||
#define MUSB_RXFIFOADD 0x66 /* 16-bit offset shifted right 3 */
|
||||
|
||||
/* REVISIT: vctrl/vstatus: optional vendor utmi+phy register at 0x68 */
|
||||
#define MUSB_HWVERS 0x6C /* 8 bit */
|
||||
|
||||
#define MUSB_EPINFO 0x78 /* 8 bit */
|
||||
#define MUSB_RAMINFO 0x79 /* 8 bit */
|
||||
#define MUSB_LINKINFO 0x7a /* 8 bit */
|
||||
#define MUSB_VPLEN 0x7b /* 8 bit */
|
||||
#define MUSB_HS_EOF1 0x7c /* 8 bit */
|
||||
#define MUSB_FS_EOF1 0x7d /* 8 bit */
|
||||
#define MUSB_LS_EOF1 0x7e /* 8 bit */
|
||||
|
||||
/* Offsets to endpoint registers */
|
||||
#define MUSB_TXMAXP 0x00
|
||||
#define MUSB_TXCSR 0x02
|
||||
#define MUSB_CSR0 MUSB_TXCSR /* Re-used for EP0 */
|
||||
#define MUSB_RXMAXP 0x04
|
||||
#define MUSB_RXCSR 0x06
|
||||
#define MUSB_RXCOUNT 0x08
|
||||
#define MUSB_COUNT0 MUSB_RXCOUNT /* Re-used for EP0 */
|
||||
#define MUSB_TXTYPE 0x0A
|
||||
#define MUSB_TYPE0 MUSB_TXTYPE /* Re-used for EP0 */
|
||||
#define MUSB_TXINTERVAL 0x0B
|
||||
#define MUSB_NAKLIMIT0 MUSB_TXINTERVAL /* Re-used for EP0 */
|
||||
#define MUSB_RXTYPE 0x0C
|
||||
#define MUSB_RXINTERVAL 0x0D
|
||||
#define MUSB_FIFOSIZE 0x0F
|
||||
#define MUSB_CONFIGDATA MUSB_FIFOSIZE /* Re-used for EP0 */
|
||||
|
||||
/* Offsets to endpoint registers in indexed model (using INDEX register) */
|
||||
#define MUSB_INDEXED_OFFSET(_epnum, _offset) \
|
||||
(0x10 + (_offset))
|
||||
|
||||
/* Offsets to endpoint registers in flat models */
|
||||
#define MUSB_FLAT_OFFSET(_epnum, _offset) \
|
||||
(0x100 + (0x10*(_epnum)) + (_offset))
|
||||
|
||||
#ifdef CONFIG_USB_TUSB6010
|
||||
/* TUSB6010 EP0 configuration register is special */
|
||||
#define MUSB_TUSB_OFFSET(_epnum, _offset) \
|
||||
(0x10 + _offset)
|
||||
#include "tusb6010.h" /* Needed "only" for TUSB_EP0_CONF */
|
||||
#endif
|
||||
|
||||
/* "bus control"/target registers, for host side multipoint (external hubs) */
|
||||
#define MUSB_TXFUNCADDR 0x00
|
||||
#define MUSB_TXHUBADDR 0x02
|
||||
#define MUSB_TXHUBPORT 0x03
|
||||
|
||||
#define MUSB_RXFUNCADDR 0x04
|
||||
#define MUSB_RXHUBADDR 0x06
|
||||
#define MUSB_RXHUBPORT 0x07
|
||||
|
||||
#define MUSB_BUSCTL_OFFSET(_epnum, _offset) \
|
||||
(0x80 + (8*(_epnum)) + (_offset))
|
||||
|
||||
/*
|
||||
* MUSB Register bits
|
||||
*/
|
||||
|
|
@ -228,7 +137,6 @@
|
|||
|
||||
/* TXCSR in Peripheral and Host mode */
|
||||
#define MUSB_TXCSR_AUTOSET 0x8000
|
||||
#define MUSB_TXCSR_MODE 0x2000
|
||||
#define MUSB_TXCSR_DMAENAB 0x1000
|
||||
#define MUSB_TXCSR_FRCDATATOG 0x0800
|
||||
#define MUSB_TXCSR_DMAMODE 0x0400
|
||||
|
|
@ -297,4 +205,309 @@
|
|||
/* HUBADDR */
|
||||
#define MUSB_HUBADDR_MULTI_TT 0x80
|
||||
|
||||
|
||||
#ifndef CONFIG_BLACKFIN
|
||||
|
||||
/*
|
||||
* Common USB registers
|
||||
*/
|
||||
|
||||
#define MUSB_FADDR 0x00 /* 8-bit */
|
||||
#define MUSB_POWER 0x01 /* 8-bit */
|
||||
|
||||
#define MUSB_INTRTX 0x02 /* 16-bit */
|
||||
#define MUSB_INTRRX 0x04
|
||||
#define MUSB_INTRTXE 0x06
|
||||
#define MUSB_INTRRXE 0x08
|
||||
#define MUSB_INTRUSB 0x0A /* 8 bit */
|
||||
#define MUSB_INTRUSBE 0x0B /* 8 bit */
|
||||
#define MUSB_FRAME 0x0C
|
||||
#define MUSB_INDEX 0x0E /* 8 bit */
|
||||
#define MUSB_TESTMODE 0x0F /* 8 bit */
|
||||
|
||||
/* Get offset for a given FIFO from musb->mregs */
|
||||
#ifdef CONFIG_USB_TUSB6010
|
||||
#define MUSB_FIFO_OFFSET(epnum) (0x200 + ((epnum) * 0x20))
|
||||
#else
|
||||
#define MUSB_FIFO_OFFSET(epnum) (0x20 + ((epnum) * 4))
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Additional Control Registers
|
||||
*/
|
||||
|
||||
#define MUSB_DEVCTL 0x60 /* 8 bit */
|
||||
|
||||
/* These are always controlled through the INDEX register */
|
||||
#define MUSB_TXFIFOSZ 0x62 /* 8-bit (see masks) */
|
||||
#define MUSB_RXFIFOSZ 0x63 /* 8-bit (see masks) */
|
||||
#define MUSB_TXFIFOADD 0x64 /* 16-bit offset shifted right 3 */
|
||||
#define MUSB_RXFIFOADD 0x66 /* 16-bit offset shifted right 3 */
|
||||
|
||||
/* REVISIT: vctrl/vstatus: optional vendor utmi+phy register at 0x68 */
|
||||
#define MUSB_HWVERS 0x6C /* 8 bit */
|
||||
|
||||
#define MUSB_EPINFO 0x78 /* 8 bit */
|
||||
#define MUSB_RAMINFO 0x79 /* 8 bit */
|
||||
#define MUSB_LINKINFO 0x7a /* 8 bit */
|
||||
#define MUSB_VPLEN 0x7b /* 8 bit */
|
||||
#define MUSB_HS_EOF1 0x7c /* 8 bit */
|
||||
#define MUSB_FS_EOF1 0x7d /* 8 bit */
|
||||
#define MUSB_LS_EOF1 0x7e /* 8 bit */
|
||||
|
||||
/* Offsets to endpoint registers */
|
||||
#define MUSB_TXMAXP 0x00
|
||||
#define MUSB_TXCSR 0x02
|
||||
#define MUSB_CSR0 MUSB_TXCSR /* Re-used for EP0 */
|
||||
#define MUSB_RXMAXP 0x04
|
||||
#define MUSB_RXCSR 0x06
|
||||
#define MUSB_RXCOUNT 0x08
|
||||
#define MUSB_COUNT0 MUSB_RXCOUNT /* Re-used for EP0 */
|
||||
#define MUSB_TXTYPE 0x0A
|
||||
#define MUSB_TYPE0 MUSB_TXTYPE /* Re-used for EP0 */
|
||||
#define MUSB_TXINTERVAL 0x0B
|
||||
#define MUSB_NAKLIMIT0 MUSB_TXINTERVAL /* Re-used for EP0 */
|
||||
#define MUSB_RXTYPE 0x0C
|
||||
#define MUSB_RXINTERVAL 0x0D
|
||||
#define MUSB_FIFOSIZE 0x0F
|
||||
#define MUSB_CONFIGDATA MUSB_FIFOSIZE /* Re-used for EP0 */
|
||||
|
||||
/* Offsets to endpoint registers in indexed model (using INDEX register) */
|
||||
#define MUSB_INDEXED_OFFSET(_epnum, _offset) \
|
||||
(0x10 + (_offset))
|
||||
|
||||
/* Offsets to endpoint registers in flat models */
|
||||
#define MUSB_FLAT_OFFSET(_epnum, _offset) \
|
||||
(0x100 + (0x10*(_epnum)) + (_offset))
|
||||
|
||||
#ifdef CONFIG_USB_TUSB6010
|
||||
/* TUSB6010 EP0 configuration register is special */
|
||||
#define MUSB_TUSB_OFFSET(_epnum, _offset) \
|
||||
(0x10 + _offset)
|
||||
#include "tusb6010.h" /* Needed "only" for TUSB_EP0_CONF */
|
||||
#endif
|
||||
|
||||
#define MUSB_TXCSR_MODE 0x2000
|
||||
|
||||
/* "bus control"/target registers, for host side multipoint (external hubs) */
|
||||
#define MUSB_TXFUNCADDR 0x00
|
||||
#define MUSB_TXHUBADDR 0x02
|
||||
#define MUSB_TXHUBPORT 0x03
|
||||
|
||||
#define MUSB_RXFUNCADDR 0x04
|
||||
#define MUSB_RXHUBADDR 0x06
|
||||
#define MUSB_RXHUBPORT 0x07
|
||||
|
||||
#define MUSB_BUSCTL_OFFSET(_epnum, _offset) \
|
||||
(0x80 + (8*(_epnum)) + (_offset))
|
||||
|
||||
static inline void musb_write_txfifosz(void __iomem *mbase, u8 c_size)
|
||||
{
|
||||
musb_writeb(mbase, MUSB_TXFIFOSZ, c_size);
|
||||
}
|
||||
|
||||
static inline void musb_write_txfifoadd(void __iomem *mbase, u16 c_off)
|
||||
{
|
||||
musb_writew(mbase, MUSB_TXFIFOADD, c_off);
|
||||
}
|
||||
|
||||
static inline void musb_write_rxfifosz(void __iomem *mbase, u8 c_size)
|
||||
{
|
||||
musb_writeb(mbase, MUSB_RXFIFOSZ, c_size);
|
||||
}
|
||||
|
||||
static inline void musb_write_rxfifoadd(void __iomem *mbase, u16 c_off)
|
||||
{
|
||||
musb_writew(mbase, MUSB_RXFIFOADD, c_off);
|
||||
}
|
||||
|
||||
static inline u8 musb_read_configdata(void __iomem *mbase)
|
||||
{
|
||||
return musb_readb(mbase, 0x10 + MUSB_CONFIGDATA);
|
||||
}
|
||||
|
||||
static inline u16 musb_read_hwvers(void __iomem *mbase)
|
||||
{
|
||||
return musb_readw(mbase, MUSB_HWVERS);
|
||||
}
|
||||
|
||||
static inline void __iomem *musb_read_target_reg_base(u8 i, void __iomem *mbase)
|
||||
{
|
||||
return (MUSB_BUSCTL_OFFSET(i, 0) + mbase);
|
||||
}
|
||||
|
||||
static inline void musb_write_rxfunaddr(void __iomem *ep_target_regs,
|
||||
u8 qh_addr_reg)
|
||||
{
|
||||
musb_writeb(ep_target_regs, MUSB_RXFUNCADDR, qh_addr_reg);
|
||||
}
|
||||
|
||||
static inline void musb_write_rxhubaddr(void __iomem *ep_target_regs,
|
||||
u8 qh_h_addr_reg)
|
||||
{
|
||||
musb_writeb(ep_target_regs, MUSB_RXHUBADDR, qh_h_addr_reg);
|
||||
}
|
||||
|
||||
static inline void musb_write_rxhubport(void __iomem *ep_target_regs,
|
||||
u8 qh_h_port_reg)
|
||||
{
|
||||
musb_writeb(ep_target_regs, MUSB_RXHUBPORT, qh_h_port_reg);
|
||||
}
|
||||
|
||||
static inline void musb_write_txfunaddr(void __iomem *mbase, u8 epnum,
|
||||
u8 qh_addr_reg)
|
||||
{
|
||||
musb_writeb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_TXFUNCADDR),
|
||||
qh_addr_reg);
|
||||
}
|
||||
|
||||
static inline void musb_write_txhubaddr(void __iomem *mbase, u8 epnum,
|
||||
u8 qh_addr_reg)
|
||||
{
|
||||
musb_writeb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_TXHUBADDR),
|
||||
qh_addr_reg);
|
||||
}
|
||||
|
||||
static inline void musb_write_txhubport(void __iomem *mbase, u8 epnum,
|
||||
u8 qh_h_port_reg)
|
||||
{
|
||||
musb_writeb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_TXHUBPORT),
|
||||
qh_h_port_reg);
|
||||
}
|
||||
|
||||
#else /* CONFIG_BLACKFIN */
|
||||
|
||||
#define USB_BASE USB_FADDR
|
||||
#define USB_OFFSET(reg) (reg - USB_BASE)
|
||||
|
||||
/*
|
||||
* Common USB registers
|
||||
*/
|
||||
#define MUSB_FADDR USB_OFFSET(USB_FADDR) /* 8-bit */
|
||||
#define MUSB_POWER USB_OFFSET(USB_POWER) /* 8-bit */
|
||||
#define MUSB_INTRTX USB_OFFSET(USB_INTRTX) /* 16-bit */
|
||||
#define MUSB_INTRRX USB_OFFSET(USB_INTRRX)
|
||||
#define MUSB_INTRTXE USB_OFFSET(USB_INTRTXE)
|
||||
#define MUSB_INTRRXE USB_OFFSET(USB_INTRRXE)
|
||||
#define MUSB_INTRUSB USB_OFFSET(USB_INTRUSB) /* 8 bit */
|
||||
#define MUSB_INTRUSBE USB_OFFSET(USB_INTRUSBE)/* 8 bit */
|
||||
#define MUSB_FRAME USB_OFFSET(USB_FRAME)
|
||||
#define MUSB_INDEX USB_OFFSET(USB_INDEX) /* 8 bit */
|
||||
#define MUSB_TESTMODE USB_OFFSET(USB_TESTMODE)/* 8 bit */
|
||||
|
||||
/* Get offset for a given FIFO from musb->mregs */
|
||||
#define MUSB_FIFO_OFFSET(epnum) \
|
||||
(USB_OFFSET(USB_EP0_FIFO) + ((epnum) * 8))
|
||||
|
||||
/*
|
||||
* Additional Control Registers
|
||||
*/
|
||||
|
||||
#define MUSB_DEVCTL USB_OFFSET(USB_OTG_DEV_CTL) /* 8 bit */
|
||||
|
||||
#define MUSB_LINKINFO USB_OFFSET(USB_LINKINFO)/* 8 bit */
|
||||
#define MUSB_VPLEN USB_OFFSET(USB_VPLEN) /* 8 bit */
|
||||
#define MUSB_HS_EOF1 USB_OFFSET(USB_HS_EOF1) /* 8 bit */
|
||||
#define MUSB_FS_EOF1 USB_OFFSET(USB_FS_EOF1) /* 8 bit */
|
||||
#define MUSB_LS_EOF1 USB_OFFSET(USB_LS_EOF1) /* 8 bit */
|
||||
|
||||
/* Offsets to endpoint registers */
|
||||
#define MUSB_TXMAXP 0x00
|
||||
#define MUSB_TXCSR 0x04
|
||||
#define MUSB_CSR0 MUSB_TXCSR /* Re-used for EP0 */
|
||||
#define MUSB_RXMAXP 0x08
|
||||
#define MUSB_RXCSR 0x0C
|
||||
#define MUSB_RXCOUNT 0x10
|
||||
#define MUSB_COUNT0 MUSB_RXCOUNT /* Re-used for EP0 */
|
||||
#define MUSB_TXTYPE 0x14
|
||||
#define MUSB_TYPE0 MUSB_TXTYPE /* Re-used for EP0 */
|
||||
#define MUSB_TXINTERVAL 0x18
|
||||
#define MUSB_NAKLIMIT0 MUSB_TXINTERVAL /* Re-used for EP0 */
|
||||
#define MUSB_RXTYPE 0x1C
|
||||
#define MUSB_RXINTERVAL 0x20
|
||||
#define MUSB_TXCOUNT 0x28
|
||||
|
||||
/* Offsets to endpoint registers in indexed model (using INDEX register) */
|
||||
#define MUSB_INDEXED_OFFSET(_epnum, _offset) \
|
||||
(0x40 + (_offset))
|
||||
|
||||
/* Offsets to endpoint registers in flat models */
|
||||
#define MUSB_FLAT_OFFSET(_epnum, _offset) \
|
||||
(USB_OFFSET(USB_EP_NI0_TXMAXP) + (0x40 * (_epnum)) + (_offset))
|
||||
|
||||
/* Not implemented - HW has seperate Tx/Rx FIFO */
|
||||
#define MUSB_TXCSR_MODE 0x0000
|
||||
|
||||
/*
|
||||
* Dummy stub for clk framework, it will be removed
|
||||
* until Blackfin supports clk framework
|
||||
*/
|
||||
#define clk_get(dev, id) NULL
|
||||
#define clk_put(clock) do {} while (0)
|
||||
#define clk_enable(clock) do {} while (0)
|
||||
#define clk_disable(clock) do {} while (0)
|
||||
|
||||
static inline void musb_write_txfifosz(void __iomem *mbase, u8 c_size)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void musb_write_txfifoadd(void __iomem *mbase, u16 c_off)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void musb_write_rxfifosz(void __iomem *mbase, u8 c_size)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void musb_write_rxfifoadd(void __iomem *mbase, u16 c_off)
|
||||
{
|
||||
}
|
||||
|
||||
static inline u8 musb_read_configdata(void __iomem *mbase)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline u16 musb_read_hwvers(void __iomem *mbase)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline u16 musb_read_target_reg_base(u8 i, void __iomem *mbase)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void musb_write_rxfunaddr(void __iomem *ep_target_regs,
|
||||
u8 qh_addr_req)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void musb_write_rxhubaddr(void __iomem *ep_target_regs,
|
||||
u8 qh_h_addr_reg)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void musb_write_rxhubport(void __iomem *ep_target_regs,
|
||||
u8 qh_h_port_reg)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void musb_write_txfunaddr(void __iomem *mbase, u8 epnum,
|
||||
u8 qh_addr_reg)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void musb_write_txhubaddr(void __iomem *mbase, u8 epnum,
|
||||
u8 qh_addr_reg)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void musb_write_txhubport(void __iomem *mbase, u8 epnum,
|
||||
u8 qh_h_port_reg)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* CONFIG_BLACKFIN */
|
||||
|
||||
#endif /* __MUSB_REGS_H__ */
|
||||
|
|
|
|||
|
|
@ -34,58 +34,7 @@
|
|||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include "musb_core.h"
|
||||
|
||||
#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3430)
|
||||
#include "omap2430.h"
|
||||
#endif
|
||||
|
||||
#define MUSB_HSDMA_BASE 0x200
|
||||
#define MUSB_HSDMA_INTR (MUSB_HSDMA_BASE + 0)
|
||||
#define MUSB_HSDMA_CONTROL 0x4
|
||||
#define MUSB_HSDMA_ADDRESS 0x8
|
||||
#define MUSB_HSDMA_COUNT 0xc
|
||||
|
||||
#define MUSB_HSDMA_CHANNEL_OFFSET(_bchannel, _offset) \
|
||||
(MUSB_HSDMA_BASE + (_bchannel << 4) + _offset)
|
||||
|
||||
/* control register (16-bit): */
|
||||
#define MUSB_HSDMA_ENABLE_SHIFT 0
|
||||
#define MUSB_HSDMA_TRANSMIT_SHIFT 1
|
||||
#define MUSB_HSDMA_MODE1_SHIFT 2
|
||||
#define MUSB_HSDMA_IRQENABLE_SHIFT 3
|
||||
#define MUSB_HSDMA_ENDPOINT_SHIFT 4
|
||||
#define MUSB_HSDMA_BUSERROR_SHIFT 8
|
||||
#define MUSB_HSDMA_BURSTMODE_SHIFT 9
|
||||
#define MUSB_HSDMA_BURSTMODE (3 << MUSB_HSDMA_BURSTMODE_SHIFT)
|
||||
#define MUSB_HSDMA_BURSTMODE_UNSPEC 0
|
||||
#define MUSB_HSDMA_BURSTMODE_INCR4 1
|
||||
#define MUSB_HSDMA_BURSTMODE_INCR8 2
|
||||
#define MUSB_HSDMA_BURSTMODE_INCR16 3
|
||||
|
||||
#define MUSB_HSDMA_CHANNELS 8
|
||||
|
||||
struct musb_dma_controller;
|
||||
|
||||
struct musb_dma_channel {
|
||||
struct dma_channel channel;
|
||||
struct musb_dma_controller *controller;
|
||||
u32 start_addr;
|
||||
u32 len;
|
||||
u16 max_packet_sz;
|
||||
u8 idx;
|
||||
u8 epnum;
|
||||
u8 transmit;
|
||||
};
|
||||
|
||||
struct musb_dma_controller {
|
||||
struct dma_controller controller;
|
||||
struct musb_dma_channel channel[MUSB_HSDMA_CHANNELS];
|
||||
void *private_data;
|
||||
void __iomem *base;
|
||||
u8 channel_count;
|
||||
u8 used_channels;
|
||||
u8 irq;
|
||||
};
|
||||
#include "musbhsdma.h"
|
||||
|
||||
static int dma_controller_start(struct dma_controller *c)
|
||||
{
|
||||
|
|
@ -203,12 +152,8 @@ static void configure_channel(struct dma_channel *channel,
|
|||
: 0);
|
||||
|
||||
/* address/count */
|
||||
musb_writel(mbase,
|
||||
MUSB_HSDMA_CHANNEL_OFFSET(bchannel, MUSB_HSDMA_ADDRESS),
|
||||
dma_addr);
|
||||
musb_writel(mbase,
|
||||
MUSB_HSDMA_CHANNEL_OFFSET(bchannel, MUSB_HSDMA_COUNT),
|
||||
len);
|
||||
musb_write_hsdma_addr(mbase, bchannel, dma_addr);
|
||||
musb_write_hsdma_count(mbase, bchannel, len);
|
||||
|
||||
/* control (this should start things) */
|
||||
musb_writew(mbase,
|
||||
|
|
@ -279,13 +224,8 @@ static int dma_channel_abort(struct dma_channel *channel)
|
|||
musb_writew(mbase,
|
||||
MUSB_HSDMA_CHANNEL_OFFSET(bchannel, MUSB_HSDMA_CONTROL),
|
||||
0);
|
||||
musb_writel(mbase,
|
||||
MUSB_HSDMA_CHANNEL_OFFSET(bchannel, MUSB_HSDMA_ADDRESS),
|
||||
0);
|
||||
musb_writel(mbase,
|
||||
MUSB_HSDMA_CHANNEL_OFFSET(bchannel, MUSB_HSDMA_COUNT),
|
||||
0);
|
||||
|
||||
musb_write_hsdma_addr(mbase, bchannel, 0);
|
||||
musb_write_hsdma_count(mbase, bchannel, 0);
|
||||
channel->status = MUSB_DMA_STATUS_FREE;
|
||||
}
|
||||
|
||||
|
|
@ -333,10 +273,8 @@ static irqreturn_t dma_controller_irq(int irq, void *private_data)
|
|||
} else {
|
||||
u8 devctl;
|
||||
|
||||
addr = musb_readl(mbase,
|
||||
MUSB_HSDMA_CHANNEL_OFFSET(
|
||||
bchannel,
|
||||
MUSB_HSDMA_ADDRESS));
|
||||
addr = musb_read_hsdma_addr(mbase,
|
||||
bchannel);
|
||||
channel->actual_len = addr
|
||||
- musb_channel->start_addr;
|
||||
|
||||
|
|
@ -375,6 +313,12 @@ static irqreturn_t dma_controller_irq(int irq, void *private_data)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BLACKFIN
|
||||
/* Clear DMA interrup flags */
|
||||
musb_writeb(mbase, MUSB_HSDMA_INTR, int_hsdma);
|
||||
#endif
|
||||
|
||||
retval = IRQ_HANDLED;
|
||||
done:
|
||||
spin_unlock_irqrestore(&musb->lock, flags);
|
||||
|
|
@ -424,7 +368,7 @@ dma_controller_create(struct musb *musb, void __iomem *base)
|
|||
controller->controller.channel_abort = dma_channel_abort;
|
||||
|
||||
if (request_irq(irq, dma_controller_irq, IRQF_DISABLED,
|
||||
musb->controller->bus_id, &controller->controller)) {
|
||||
dev_name(musb->controller), &controller->controller)) {
|
||||
dev_err(dev, "request_irq %d failed!\n", irq);
|
||||
dma_controller_destroy(&controller->controller);
|
||||
|
||||
|
|
|
|||
149
drivers/usb/musb/musbhsdma.h
Normal file
149
drivers/usb/musb/musbhsdma.h
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
/*
|
||||
* MUSB OTG driver - support for Mentor's DMA controller
|
||||
*
|
||||
* Copyright 2005 Mentor Graphics Corporation
|
||||
* Copyright (C) 2005-2007 by Texas Instruments
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
|
||||
* NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
|
||||
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3430)
|
||||
#include "omap2430.h"
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_BLACKFIN
|
||||
|
||||
#define MUSB_HSDMA_BASE 0x200
|
||||
#define MUSB_HSDMA_INTR (MUSB_HSDMA_BASE + 0)
|
||||
#define MUSB_HSDMA_CONTROL 0x4
|
||||
#define MUSB_HSDMA_ADDRESS 0x8
|
||||
#define MUSB_HSDMA_COUNT 0xc
|
||||
|
||||
#define MUSB_HSDMA_CHANNEL_OFFSET(_bchannel, _offset) \
|
||||
(MUSB_HSDMA_BASE + (_bchannel << 4) + _offset)
|
||||
|
||||
#define musb_read_hsdma_addr(mbase, bchannel) \
|
||||
musb_readl(mbase, \
|
||||
MUSB_HSDMA_CHANNEL_OFFSET(bchannel, MUSB_HSDMA_ADDRESS))
|
||||
|
||||
#define musb_write_hsdma_addr(mbase, bchannel, addr) \
|
||||
musb_writel(mbase, \
|
||||
MUSB_HSDMA_CHANNEL_OFFSET(bchannel, MUSB_HSDMA_ADDRESS), \
|
||||
addr)
|
||||
|
||||
#define musb_write_hsdma_count(mbase, bchannel, len) \
|
||||
musb_writel(mbase, \
|
||||
MUSB_HSDMA_CHANNEL_OFFSET(bchannel, MUSB_HSDMA_COUNT), \
|
||||
len)
|
||||
#else
|
||||
|
||||
#define MUSB_HSDMA_BASE 0x400
|
||||
#define MUSB_HSDMA_INTR (MUSB_HSDMA_BASE + 0)
|
||||
#define MUSB_HSDMA_CONTROL 0x04
|
||||
#define MUSB_HSDMA_ADDR_LOW 0x08
|
||||
#define MUSB_HSDMA_ADDR_HIGH 0x0C
|
||||
#define MUSB_HSDMA_COUNT_LOW 0x10
|
||||
#define MUSB_HSDMA_COUNT_HIGH 0x14
|
||||
|
||||
#define MUSB_HSDMA_CHANNEL_OFFSET(_bchannel, _offset) \
|
||||
(MUSB_HSDMA_BASE + (_bchannel * 0x20) + _offset)
|
||||
|
||||
static inline u32 musb_read_hsdma_addr(void __iomem *mbase, u8 bchannel)
|
||||
{
|
||||
u32 addr = musb_readw(mbase,
|
||||
MUSB_HSDMA_CHANNEL_OFFSET(bchannel, MUSB_HSDMA_ADDR_HIGH));
|
||||
|
||||
addr = addr << 16;
|
||||
|
||||
addr |= musb_readw(mbase,
|
||||
MUSB_HSDMA_CHANNEL_OFFSET(bchannel, MUSB_HSDMA_ADDR_LOW));
|
||||
|
||||
return addr;
|
||||
}
|
||||
|
||||
static inline void musb_write_hsdma_addr(void __iomem *mbase,
|
||||
u8 bchannel, dma_addr_t dma_addr)
|
||||
{
|
||||
musb_writew(mbase,
|
||||
MUSB_HSDMA_CHANNEL_OFFSET(bchannel, MUSB_HSDMA_ADDR_LOW),
|
||||
((u16)((u32) dma_addr & 0xFFFF)));
|
||||
musb_writew(mbase,
|
||||
MUSB_HSDMA_CHANNEL_OFFSET(bchannel, MUSB_HSDMA_ADDR_HIGH),
|
||||
((u16)(((u32) dma_addr >> 16) & 0xFFFF)));
|
||||
}
|
||||
|
||||
static inline void musb_write_hsdma_count(void __iomem *mbase,
|
||||
u8 bchannel, u32 len)
|
||||
{
|
||||
musb_writew(mbase,
|
||||
MUSB_HSDMA_CHANNEL_OFFSET(bchannel, MUSB_HSDMA_COUNT_LOW),
|
||||
((u16)((u32) len & 0xFFFF)));
|
||||
musb_writew(mbase,
|
||||
MUSB_HSDMA_CHANNEL_OFFSET(bchannel, MUSB_HSDMA_COUNT_HIGH),
|
||||
((u16)(((u32) len >> 16) & 0xFFFF)));
|
||||
}
|
||||
|
||||
#endif /* CONFIG_BLACKFIN */
|
||||
|
||||
/* control register (16-bit): */
|
||||
#define MUSB_HSDMA_ENABLE_SHIFT 0
|
||||
#define MUSB_HSDMA_TRANSMIT_SHIFT 1
|
||||
#define MUSB_HSDMA_MODE1_SHIFT 2
|
||||
#define MUSB_HSDMA_IRQENABLE_SHIFT 3
|
||||
#define MUSB_HSDMA_ENDPOINT_SHIFT 4
|
||||
#define MUSB_HSDMA_BUSERROR_SHIFT 8
|
||||
#define MUSB_HSDMA_BURSTMODE_SHIFT 9
|
||||
#define MUSB_HSDMA_BURSTMODE (3 << MUSB_HSDMA_BURSTMODE_SHIFT)
|
||||
#define MUSB_HSDMA_BURSTMODE_UNSPEC 0
|
||||
#define MUSB_HSDMA_BURSTMODE_INCR4 1
|
||||
#define MUSB_HSDMA_BURSTMODE_INCR8 2
|
||||
#define MUSB_HSDMA_BURSTMODE_INCR16 3
|
||||
|
||||
#define MUSB_HSDMA_CHANNELS 8
|
||||
|
||||
struct musb_dma_controller;
|
||||
|
||||
struct musb_dma_channel {
|
||||
struct dma_channel channel;
|
||||
struct musb_dma_controller *controller;
|
||||
u32 start_addr;
|
||||
u32 len;
|
||||
u16 max_packet_sz;
|
||||
u8 idx;
|
||||
u8 epnum;
|
||||
u8 transmit;
|
||||
};
|
||||
|
||||
struct musb_dma_controller {
|
||||
struct dma_controller controller;
|
||||
struct musb_dma_channel channel[MUSB_HSDMA_CHANNELS];
|
||||
void *private_data;
|
||||
void __iomem *base;
|
||||
u8 channel_count;
|
||||
u8 used_channels;
|
||||
u8 irq;
|
||||
};
|
||||
|
|
@ -58,10 +58,10 @@ static void musb_do_idle(unsigned long _musb)
|
|||
#endif
|
||||
u8 devctl;
|
||||
|
||||
devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
|
||||
|
||||
spin_lock_irqsave(&musb->lock, flags);
|
||||
|
||||
devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
|
||||
|
||||
switch (musb->xceiv.state) {
|
||||
case OTG_STATE_A_WAIT_BCON:
|
||||
devctl &= ~MUSB_DEVCTL_SESSION;
|
||||
|
|
@ -196,7 +196,7 @@ static int omap_set_power(struct otg_transceiver *x, unsigned mA)
|
|||
|
||||
static int musb_platform_resume(struct musb *musb);
|
||||
|
||||
void musb_platform_set_mode(struct musb *musb, u8 musb_mode)
|
||||
int musb_platform_set_mode(struct musb *musb, u8 musb_mode)
|
||||
{
|
||||
u8 devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
|
||||
|
||||
|
|
@ -204,15 +204,24 @@ void musb_platform_set_mode(struct musb *musb, u8 musb_mode)
|
|||
musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
|
||||
|
||||
switch (musb_mode) {
|
||||
#ifdef CONFIG_USB_MUSB_HDRC_HCD
|
||||
case MUSB_HOST:
|
||||
otg_set_host(&musb->xceiv, musb->xceiv.host);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_USB_GADGET_MUSB_HDRC
|
||||
case MUSB_PERIPHERAL:
|
||||
otg_set_peripheral(&musb->xceiv, musb->xceiv.gadget);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_USB_MUSB_OTG
|
||||
case MUSB_OTG:
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __init musb_platform_init(struct musb *musb)
|
||||
|
|
|
|||
|
|
@ -598,7 +598,7 @@ static void tusb_source_power(struct musb *musb, int is_on)
|
|||
* and peripheral modes in non-OTG configurations by reconfiguring hardware
|
||||
* and then setting musb->board_mode. For now, only support OTG mode.
|
||||
*/
|
||||
void musb_platform_set_mode(struct musb *musb, u8 musb_mode)
|
||||
int musb_platform_set_mode(struct musb *musb, u8 musb_mode)
|
||||
{
|
||||
void __iomem *tbase = musb->ctrl_base;
|
||||
u32 otg_stat, phy_otg_ctrl, phy_otg_ena, dev_conf;
|
||||
|
|
@ -641,7 +641,8 @@ void musb_platform_set_mode(struct musb *musb, u8 musb_mode)
|
|||
#endif
|
||||
|
||||
default:
|
||||
DBG(2, "Trying to set unknown mode %i\n", musb_mode);
|
||||
DBG(2, "Trying to set mode %i\n", musb_mode);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
musb_writel(tbase, TUSB_PHY_OTG_CTRL,
|
||||
|
|
@ -655,6 +656,8 @@ void musb_platform_set_mode(struct musb *musb, u8 musb_mode)
|
|||
!(otg_stat & TUSB_DEV_OTG_STAT_ID_STATUS))
|
||||
INFO("Cannot be peripheral with mini-A cable "
|
||||
"otg_stat: %08x\n", otg_stat);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline unsigned long
|
||||
|
|
|
|||
54
drivers/usb/otg/Kconfig
Normal file
54
drivers/usb/otg/Kconfig
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
#
|
||||
# USB OTG infrastructure may be needed for peripheral-only, host-only,
|
||||
# or OTG-capable configurations when OTG transceivers or controllers
|
||||
# are used.
|
||||
#
|
||||
|
||||
comment "OTG and related infrastructure"
|
||||
|
||||
if USB || USB_GADGET
|
||||
|
||||
config USB_OTG_UTILS
|
||||
bool
|
||||
help
|
||||
Select this to make sure the build includes objects from
|
||||
the OTG infrastructure directory.
|
||||
|
||||
#
|
||||
# USB Transceiver Drivers
|
||||
#
|
||||
config USB_GPIO_VBUS
|
||||
tristate "GPIO based peripheral-only VBUS sensing 'transceiver'"
|
||||
depends on GENERIC_GPIO
|
||||
select USB_OTG_UTILS
|
||||
help
|
||||
Provides simple GPIO VBUS sensing for controllers with an
|
||||
internal transceiver via the otg_transceiver interface, and
|
||||
optionally control of a D+ pullup GPIO as well as a VBUS
|
||||
current limit regulator.
|
||||
|
||||
config ISP1301_OMAP
|
||||
tristate "Philips ISP1301 with OMAP OTG"
|
||||
depends on I2C && ARCH_OMAP_OTG
|
||||
select USB_OTG_UTILS
|
||||
help
|
||||
If you say yes here you get support for the Philips ISP1301
|
||||
USB-On-The-Go transceiver working with the OMAP OTG controller.
|
||||
The ISP1301 is a full speed USB transceiver which is used in
|
||||
products including H2, H3, and H4 development boards for Texas
|
||||
Instruments OMAP processors.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called isp1301_omap.
|
||||
|
||||
config TWL4030_USB
|
||||
tristate "TWL4030 USB Transceiver Driver"
|
||||
depends on TWL4030_CORE
|
||||
select USB_OTG_UTILS
|
||||
help
|
||||
Enable this to support the USB OTG transceiver on TWL4030
|
||||
family chips (including the TWL5030 and TPS659x0 devices).
|
||||
This transceiver supports high and full speed devices plus,
|
||||
in host mode, low speed.
|
||||
|
||||
endif # USB || OTG
|
||||
15
drivers/usb/otg/Makefile
Normal file
15
drivers/usb/otg/Makefile
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
#
|
||||
# OTG infrastructure and transceiver drivers
|
||||
#
|
||||
|
||||
# infrastructure
|
||||
obj-$(CONFIG_USB_OTG_UTILS) += otg.o
|
||||
|
||||
# transceiver drivers
|
||||
obj-$(CONFIG_USB_GPIO_VBUS) += gpio_vbus.o
|
||||
obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o
|
||||
obj-$(CONFIG_TWL4030_USB) += twl4030-usb.o
|
||||
|
||||
ccflags-$(CONFIG_USB_DEBUG) += -DDEBUG
|
||||
ccflags-$(CONFIG_USB_GADGET_DEBUG) += -DDEBUG
|
||||
|
||||
335
drivers/usb/otg/gpio_vbus.c
Normal file
335
drivers/usb/otg/gpio_vbus.c
Normal file
|
|
@ -0,0 +1,335 @@
|
|||
/*
|
||||
* gpio-vbus.c - simple GPIO VBUS sensing driver for B peripheral devices
|
||||
*
|
||||
* Copyright (c) 2008 Philipp Zabel <philipp.zabel@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/usb.h>
|
||||
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#include <linux/usb/gadget.h>
|
||||
#include <linux/usb/gpio_vbus.h>
|
||||
#include <linux/usb/otg.h>
|
||||
|
||||
|
||||
/*
|
||||
* A simple GPIO VBUS sensing driver for B peripheral only devices
|
||||
* with internal transceivers. It can control a D+ pullup GPIO and
|
||||
* a regulator to limit the current drawn from VBUS.
|
||||
*
|
||||
* Needs to be loaded before the UDC driver that will use it.
|
||||
*/
|
||||
struct gpio_vbus_data {
|
||||
struct otg_transceiver otg;
|
||||
struct device *dev;
|
||||
struct regulator *vbus_draw;
|
||||
int vbus_draw_enabled;
|
||||
unsigned mA;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* This driver relies on "both edges" triggering. VBUS has 100 msec to
|
||||
* stabilize, so the peripheral controller driver may need to cope with
|
||||
* some bouncing due to current surges (e.g. charging local capacitance)
|
||||
* and contact chatter.
|
||||
*
|
||||
* REVISIT in desperate straits, toggling between rising and falling
|
||||
* edges might be workable.
|
||||
*/
|
||||
#define VBUS_IRQ_FLAGS \
|
||||
( IRQF_SAMPLE_RANDOM | IRQF_SHARED \
|
||||
| IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING )
|
||||
|
||||
|
||||
/* interface to regulator framework */
|
||||
static void set_vbus_draw(struct gpio_vbus_data *gpio_vbus, unsigned mA)
|
||||
{
|
||||
struct regulator *vbus_draw = gpio_vbus->vbus_draw;
|
||||
int enabled;
|
||||
|
||||
if (!vbus_draw)
|
||||
return;
|
||||
|
||||
enabled = gpio_vbus->vbus_draw_enabled;
|
||||
if (mA) {
|
||||
regulator_set_current_limit(vbus_draw, 0, 1000 * mA);
|
||||
if (!enabled) {
|
||||
regulator_enable(vbus_draw);
|
||||
gpio_vbus->vbus_draw_enabled = 1;
|
||||
}
|
||||
} else {
|
||||
if (enabled) {
|
||||
regulator_disable(vbus_draw);
|
||||
gpio_vbus->vbus_draw_enabled = 0;
|
||||
}
|
||||
}
|
||||
gpio_vbus->mA = mA;
|
||||
}
|
||||
|
||||
/* VBUS change IRQ handler */
|
||||
static irqreturn_t gpio_vbus_irq(int irq, void *data)
|
||||
{
|
||||
struct platform_device *pdev = data;
|
||||
struct gpio_vbus_mach_info *pdata = pdev->dev.platform_data;
|
||||
struct gpio_vbus_data *gpio_vbus = platform_get_drvdata(pdev);
|
||||
int gpio, vbus;
|
||||
|
||||
vbus = gpio_get_value(pdata->gpio_vbus);
|
||||
if (pdata->gpio_vbus_inverted)
|
||||
vbus = !vbus;
|
||||
|
||||
dev_dbg(&pdev->dev, "VBUS %s (gadget: %s)\n",
|
||||
vbus ? "supplied" : "inactive",
|
||||
gpio_vbus->otg.gadget ? gpio_vbus->otg.gadget->name : "none");
|
||||
|
||||
if (!gpio_vbus->otg.gadget)
|
||||
return IRQ_HANDLED;
|
||||
|
||||
/* Peripheral controllers which manage the pullup themselves won't have
|
||||
* gpio_pullup configured here. If it's configured here, we'll do what
|
||||
* isp1301_omap::b_peripheral() does and enable the pullup here... although
|
||||
* that may complicate usb_gadget_{,dis}connect() support.
|
||||
*/
|
||||
gpio = pdata->gpio_pullup;
|
||||
if (vbus) {
|
||||
gpio_vbus->otg.state = OTG_STATE_B_PERIPHERAL;
|
||||
usb_gadget_vbus_connect(gpio_vbus->otg.gadget);
|
||||
|
||||
/* drawing a "unit load" is *always* OK, except for OTG */
|
||||
set_vbus_draw(gpio_vbus, 100);
|
||||
|
||||
/* optionally enable D+ pullup */
|
||||
if (gpio_is_valid(gpio))
|
||||
gpio_set_value(gpio, !pdata->gpio_pullup_inverted);
|
||||
} else {
|
||||
/* optionally disable D+ pullup */
|
||||
if (gpio_is_valid(gpio))
|
||||
gpio_set_value(gpio, pdata->gpio_pullup_inverted);
|
||||
|
||||
set_vbus_draw(gpio_vbus, 0);
|
||||
|
||||
usb_gadget_vbus_disconnect(gpio_vbus->otg.gadget);
|
||||
gpio_vbus->otg.state = OTG_STATE_B_IDLE;
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/* OTG transceiver interface */
|
||||
|
||||
/* bind/unbind the peripheral controller */
|
||||
static int gpio_vbus_set_peripheral(struct otg_transceiver *otg,
|
||||
struct usb_gadget *gadget)
|
||||
{
|
||||
struct gpio_vbus_data *gpio_vbus;
|
||||
struct gpio_vbus_mach_info *pdata;
|
||||
struct platform_device *pdev;
|
||||
int gpio, irq;
|
||||
|
||||
gpio_vbus = container_of(otg, struct gpio_vbus_data, otg);
|
||||
pdev = to_platform_device(gpio_vbus->dev);
|
||||
pdata = gpio_vbus->dev->platform_data;
|
||||
irq = gpio_to_irq(pdata->gpio_vbus);
|
||||
gpio = pdata->gpio_pullup;
|
||||
|
||||
if (!gadget) {
|
||||
dev_dbg(&pdev->dev, "unregistering gadget '%s'\n",
|
||||
otg->gadget->name);
|
||||
|
||||
/* optionally disable D+ pullup */
|
||||
if (gpio_is_valid(gpio))
|
||||
gpio_set_value(gpio, pdata->gpio_pullup_inverted);
|
||||
|
||||
set_vbus_draw(gpio_vbus, 0);
|
||||
|
||||
usb_gadget_vbus_disconnect(otg->gadget);
|
||||
otg->state = OTG_STATE_UNDEFINED;
|
||||
|
||||
otg->gadget = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
otg->gadget = gadget;
|
||||
dev_dbg(&pdev->dev, "registered gadget '%s'\n", gadget->name);
|
||||
|
||||
/* initialize connection state */
|
||||
gpio_vbus_irq(irq, pdev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* effective for B devices, ignored for A-peripheral */
|
||||
static int gpio_vbus_set_power(struct otg_transceiver *otg, unsigned mA)
|
||||
{
|
||||
struct gpio_vbus_data *gpio_vbus;
|
||||
|
||||
gpio_vbus = container_of(otg, struct gpio_vbus_data, otg);
|
||||
|
||||
if (otg->state == OTG_STATE_B_PERIPHERAL)
|
||||
set_vbus_draw(gpio_vbus, mA);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* for non-OTG B devices: set/clear transceiver suspend mode */
|
||||
static int gpio_vbus_set_suspend(struct otg_transceiver *otg, int suspend)
|
||||
{
|
||||
struct gpio_vbus_data *gpio_vbus;
|
||||
|
||||
gpio_vbus = container_of(otg, struct gpio_vbus_data, otg);
|
||||
|
||||
/* draw max 0 mA from vbus in suspend mode; or the previously
|
||||
* recorded amount of current if not suspended
|
||||
*
|
||||
* NOTE: high powered configs (mA > 100) may draw up to 2.5 mA
|
||||
* if they're wake-enabled ... we don't handle that yet.
|
||||
*/
|
||||
return gpio_vbus_set_power(otg, suspend ? 0 : gpio_vbus->mA);
|
||||
}
|
||||
|
||||
/* platform driver interface */
|
||||
|
||||
static int __init gpio_vbus_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct gpio_vbus_mach_info *pdata = pdev->dev.platform_data;
|
||||
struct gpio_vbus_data *gpio_vbus;
|
||||
struct resource *res;
|
||||
int err, gpio, irq;
|
||||
|
||||
if (!pdata || !gpio_is_valid(pdata->gpio_vbus))
|
||||
return -EINVAL;
|
||||
gpio = pdata->gpio_vbus;
|
||||
|
||||
gpio_vbus = kzalloc(sizeof(struct gpio_vbus_data), GFP_KERNEL);
|
||||
if (!gpio_vbus)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, gpio_vbus);
|
||||
gpio_vbus->dev = &pdev->dev;
|
||||
gpio_vbus->otg.label = "gpio-vbus";
|
||||
gpio_vbus->otg.state = OTG_STATE_UNDEFINED;
|
||||
gpio_vbus->otg.set_peripheral = gpio_vbus_set_peripheral;
|
||||
gpio_vbus->otg.set_power = gpio_vbus_set_power;
|
||||
gpio_vbus->otg.set_suspend = gpio_vbus_set_suspend;
|
||||
|
||||
err = gpio_request(gpio, "vbus_detect");
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "can't request vbus gpio %d, err: %d\n",
|
||||
gpio, err);
|
||||
goto err_gpio;
|
||||
}
|
||||
gpio_direction_input(gpio);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
||||
if (res) {
|
||||
irq = res->start;
|
||||
res->flags &= IRQF_TRIGGER_MASK;
|
||||
res->flags |= IRQF_SAMPLE_RANDOM | IRQF_SHARED;
|
||||
} else
|
||||
irq = gpio_to_irq(gpio);
|
||||
|
||||
/* if data line pullup is in use, initialize it to "not pulling up" */
|
||||
gpio = pdata->gpio_pullup;
|
||||
if (gpio_is_valid(gpio)) {
|
||||
err = gpio_request(gpio, "udc_pullup");
|
||||
if (err) {
|
||||
dev_err(&pdev->dev,
|
||||
"can't request pullup gpio %d, err: %d\n",
|
||||
gpio, err);
|
||||
gpio_free(pdata->gpio_vbus);
|
||||
goto err_gpio;
|
||||
}
|
||||
gpio_direction_output(gpio, pdata->gpio_pullup_inverted);
|
||||
}
|
||||
|
||||
err = request_irq(irq, gpio_vbus_irq, VBUS_IRQ_FLAGS,
|
||||
"vbus_detect", pdev);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "can't request irq %i, err: %d\n",
|
||||
irq, err);
|
||||
goto err_irq;
|
||||
}
|
||||
|
||||
/* only active when a gadget is registered */
|
||||
err = otg_set_transceiver(&gpio_vbus->otg);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "can't register transceiver, err: %d\n",
|
||||
err);
|
||||
goto err_otg;
|
||||
}
|
||||
|
||||
gpio_vbus->vbus_draw = regulator_get(&pdev->dev, "vbus_draw");
|
||||
if (IS_ERR(gpio_vbus->vbus_draw)) {
|
||||
dev_dbg(&pdev->dev, "can't get vbus_draw regulator, err: %ld\n",
|
||||
PTR_ERR(gpio_vbus->vbus_draw));
|
||||
gpio_vbus->vbus_draw = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
err_otg:
|
||||
free_irq(irq, &pdev->dev);
|
||||
err_irq:
|
||||
if (gpio_is_valid(pdata->gpio_pullup))
|
||||
gpio_free(pdata->gpio_pullup);
|
||||
gpio_free(pdata->gpio_vbus);
|
||||
err_gpio:
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
kfree(gpio_vbus);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __exit gpio_vbus_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct gpio_vbus_data *gpio_vbus = platform_get_drvdata(pdev);
|
||||
struct gpio_vbus_mach_info *pdata = pdev->dev.platform_data;
|
||||
int gpio = pdata->gpio_vbus;
|
||||
|
||||
regulator_put(gpio_vbus->vbus_draw);
|
||||
|
||||
otg_set_transceiver(NULL);
|
||||
|
||||
free_irq(gpio_to_irq(gpio), &pdev->dev);
|
||||
if (gpio_is_valid(pdata->gpio_pullup))
|
||||
gpio_free(pdata->gpio_pullup);
|
||||
gpio_free(gpio);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
kfree(gpio_vbus);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* NOTE: the gpio-vbus device may *NOT* be hotplugged */
|
||||
|
||||
MODULE_ALIAS("platform:gpio-vbus");
|
||||
|
||||
static struct platform_driver gpio_vbus_driver = {
|
||||
.driver = {
|
||||
.name = "gpio-vbus",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.remove = __exit_p(gpio_vbus_remove),
|
||||
};
|
||||
|
||||
static int __init gpio_vbus_init(void)
|
||||
{
|
||||
return platform_driver_probe(&gpio_vbus_driver, gpio_vbus_probe);
|
||||
}
|
||||
module_init(gpio_vbus_init);
|
||||
|
||||
static void __exit gpio_vbus_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&gpio_vbus_driver);
|
||||
}
|
||||
module_exit(gpio_vbus_exit);
|
||||
|
||||
MODULE_DESCRIPTION("simple GPIO controlled OTG transceiver driver");
|
||||
MODULE_AUTHOR("Philipp Zabel");
|
||||
MODULE_LICENSE("GPL");
|
||||
1683
drivers/usb/otg/isp1301_omap.c
Normal file
1683
drivers/usb/otg/isp1301_omap.c
Normal file
File diff suppressed because it is too large
Load diff
65
drivers/usb/otg/otg.c
Normal file
65
drivers/usb/otg/otg.c
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* otg.c -- USB OTG utility code
|
||||
*
|
||||
* Copyright (C) 2004 Texas Instruments
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/device.h>
|
||||
|
||||
#include <linux/usb/otg.h>
|
||||
|
||||
static struct otg_transceiver *xceiv;
|
||||
|
||||
/**
|
||||
* otg_get_transceiver - find the (single) OTG transceiver
|
||||
*
|
||||
* Returns the transceiver driver, after getting a refcount to it; or
|
||||
* null if there is no such transceiver. The caller is responsible for
|
||||
* calling otg_put_transceiver() to release that count.
|
||||
*
|
||||
* For use by USB host and peripheral drivers.
|
||||
*/
|
||||
struct otg_transceiver *otg_get_transceiver(void)
|
||||
{
|
||||
if (xceiv)
|
||||
get_device(xceiv->dev);
|
||||
return xceiv;
|
||||
}
|
||||
EXPORT_SYMBOL(otg_get_transceiver);
|
||||
|
||||
/**
|
||||
* otg_put_transceiver - release the (single) OTG transceiver
|
||||
* @x: the transceiver returned by otg_get_transceiver()
|
||||
*
|
||||
* Releases a refcount the caller received from otg_get_transceiver().
|
||||
*
|
||||
* For use by USB host and peripheral drivers.
|
||||
*/
|
||||
void otg_put_transceiver(struct otg_transceiver *x)
|
||||
{
|
||||
put_device(x->dev);
|
||||
}
|
||||
EXPORT_SYMBOL(otg_put_transceiver);
|
||||
|
||||
/**
|
||||
* otg_set_transceiver - declare the (single) OTG transceiver
|
||||
* @x: the USB OTG transceiver to be used; or NULL
|
||||
*
|
||||
* This call is exclusively for use by transceiver drivers, which
|
||||
* coordinate the activities of drivers for host and peripheral
|
||||
* controllers, and in some cases for VBUS current regulation.
|
||||
*/
|
||||
int otg_set_transceiver(struct otg_transceiver *x)
|
||||
{
|
||||
if (xceiv && x)
|
||||
return -EBUSY;
|
||||
xceiv = x;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(otg_set_transceiver);
|
||||
721
drivers/usb/otg/twl4030-usb.c
Normal file
721
drivers/usb/otg/twl4030-usb.c
Normal file
|
|
@ -0,0 +1,721 @@
|
|||
/*
|
||||
* twl4030_usb - TWL4030 USB transceiver, talking to OMAP OTG controller
|
||||
*
|
||||
* Copyright (C) 2004-2007 Texas Instruments
|
||||
* Copyright (C) 2008 Nokia Corporation
|
||||
* Contact: Felipe Balbi <felipe.balbi@nokia.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* Current status:
|
||||
* - HS USB ULPI mode works.
|
||||
* - 3-pin mode support may be added in future.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/usb/otg.h>
|
||||
#include <linux/i2c/twl4030.h>
|
||||
|
||||
|
||||
/* Register defines */
|
||||
|
||||
#define VENDOR_ID_LO 0x00
|
||||
#define VENDOR_ID_HI 0x01
|
||||
#define PRODUCT_ID_LO 0x02
|
||||
#define PRODUCT_ID_HI 0x03
|
||||
|
||||
#define FUNC_CTRL 0x04
|
||||
#define FUNC_CTRL_SET 0x05
|
||||
#define FUNC_CTRL_CLR 0x06
|
||||
#define FUNC_CTRL_SUSPENDM (1 << 6)
|
||||
#define FUNC_CTRL_RESET (1 << 5)
|
||||
#define FUNC_CTRL_OPMODE_MASK (3 << 3) /* bits 3 and 4 */
|
||||
#define FUNC_CTRL_OPMODE_NORMAL (0 << 3)
|
||||
#define FUNC_CTRL_OPMODE_NONDRIVING (1 << 3)
|
||||
#define FUNC_CTRL_OPMODE_DISABLE_BIT_NRZI (2 << 3)
|
||||
#define FUNC_CTRL_TERMSELECT (1 << 2)
|
||||
#define FUNC_CTRL_XCVRSELECT_MASK (3 << 0) /* bits 0 and 1 */
|
||||
#define FUNC_CTRL_XCVRSELECT_HS (0 << 0)
|
||||
#define FUNC_CTRL_XCVRSELECT_FS (1 << 0)
|
||||
#define FUNC_CTRL_XCVRSELECT_LS (2 << 0)
|
||||
#define FUNC_CTRL_XCVRSELECT_FS4LS (3 << 0)
|
||||
|
||||
#define IFC_CTRL 0x07
|
||||
#define IFC_CTRL_SET 0x08
|
||||
#define IFC_CTRL_CLR 0x09
|
||||
#define IFC_CTRL_INTERFACE_PROTECT_DISABLE (1 << 7)
|
||||
#define IFC_CTRL_AUTORESUME (1 << 4)
|
||||
#define IFC_CTRL_CLOCKSUSPENDM (1 << 3)
|
||||
#define IFC_CTRL_CARKITMODE (1 << 2)
|
||||
#define IFC_CTRL_FSLSSERIALMODE_3PIN (1 << 1)
|
||||
|
||||
#define TWL4030_OTG_CTRL 0x0A
|
||||
#define TWL4030_OTG_CTRL_SET 0x0B
|
||||
#define TWL4030_OTG_CTRL_CLR 0x0C
|
||||
#define TWL4030_OTG_CTRL_DRVVBUS (1 << 5)
|
||||
#define TWL4030_OTG_CTRL_CHRGVBUS (1 << 4)
|
||||
#define TWL4030_OTG_CTRL_DISCHRGVBUS (1 << 3)
|
||||
#define TWL4030_OTG_CTRL_DMPULLDOWN (1 << 2)
|
||||
#define TWL4030_OTG_CTRL_DPPULLDOWN (1 << 1)
|
||||
#define TWL4030_OTG_CTRL_IDPULLUP (1 << 0)
|
||||
|
||||
#define USB_INT_EN_RISE 0x0D
|
||||
#define USB_INT_EN_RISE_SET 0x0E
|
||||
#define USB_INT_EN_RISE_CLR 0x0F
|
||||
#define USB_INT_EN_FALL 0x10
|
||||
#define USB_INT_EN_FALL_SET 0x11
|
||||
#define USB_INT_EN_FALL_CLR 0x12
|
||||
#define USB_INT_STS 0x13
|
||||
#define USB_INT_LATCH 0x14
|
||||
#define USB_INT_IDGND (1 << 4)
|
||||
#define USB_INT_SESSEND (1 << 3)
|
||||
#define USB_INT_SESSVALID (1 << 2)
|
||||
#define USB_INT_VBUSVALID (1 << 1)
|
||||
#define USB_INT_HOSTDISCONNECT (1 << 0)
|
||||
|
||||
#define CARKIT_CTRL 0x19
|
||||
#define CARKIT_CTRL_SET 0x1A
|
||||
#define CARKIT_CTRL_CLR 0x1B
|
||||
#define CARKIT_CTRL_MICEN (1 << 6)
|
||||
#define CARKIT_CTRL_SPKRIGHTEN (1 << 5)
|
||||
#define CARKIT_CTRL_SPKLEFTEN (1 << 4)
|
||||
#define CARKIT_CTRL_RXDEN (1 << 3)
|
||||
#define CARKIT_CTRL_TXDEN (1 << 2)
|
||||
#define CARKIT_CTRL_IDGNDDRV (1 << 1)
|
||||
#define CARKIT_CTRL_CARKITPWR (1 << 0)
|
||||
#define CARKIT_PLS_CTRL 0x22
|
||||
#define CARKIT_PLS_CTRL_SET 0x23
|
||||
#define CARKIT_PLS_CTRL_CLR 0x24
|
||||
#define CARKIT_PLS_CTRL_SPKRRIGHT_BIASEN (1 << 3)
|
||||
#define CARKIT_PLS_CTRL_SPKRLEFT_BIASEN (1 << 2)
|
||||
#define CARKIT_PLS_CTRL_RXPLSEN (1 << 1)
|
||||
#define CARKIT_PLS_CTRL_TXPLSEN (1 << 0)
|
||||
|
||||
#define MCPC_CTRL 0x30
|
||||
#define MCPC_CTRL_SET 0x31
|
||||
#define MCPC_CTRL_CLR 0x32
|
||||
#define MCPC_CTRL_RTSOL (1 << 7)
|
||||
#define MCPC_CTRL_EXTSWR (1 << 6)
|
||||
#define MCPC_CTRL_EXTSWC (1 << 5)
|
||||
#define MCPC_CTRL_VOICESW (1 << 4)
|
||||
#define MCPC_CTRL_OUT64K (1 << 3)
|
||||
#define MCPC_CTRL_RTSCTSSW (1 << 2)
|
||||
#define MCPC_CTRL_HS_UART (1 << 0)
|
||||
|
||||
#define MCPC_IO_CTRL 0x33
|
||||
#define MCPC_IO_CTRL_SET 0x34
|
||||
#define MCPC_IO_CTRL_CLR 0x35
|
||||
#define MCPC_IO_CTRL_MICBIASEN (1 << 5)
|
||||
#define MCPC_IO_CTRL_CTS_NPU (1 << 4)
|
||||
#define MCPC_IO_CTRL_RXD_PU (1 << 3)
|
||||
#define MCPC_IO_CTRL_TXDTYP (1 << 2)
|
||||
#define MCPC_IO_CTRL_CTSTYP (1 << 1)
|
||||
#define MCPC_IO_CTRL_RTSTYP (1 << 0)
|
||||
|
||||
#define MCPC_CTRL2 0x36
|
||||
#define MCPC_CTRL2_SET 0x37
|
||||
#define MCPC_CTRL2_CLR 0x38
|
||||
#define MCPC_CTRL2_MCPC_CK_EN (1 << 0)
|
||||
|
||||
#define OTHER_FUNC_CTRL 0x80
|
||||
#define OTHER_FUNC_CTRL_SET 0x81
|
||||
#define OTHER_FUNC_CTRL_CLR 0x82
|
||||
#define OTHER_FUNC_CTRL_BDIS_ACON_EN (1 << 4)
|
||||
#define OTHER_FUNC_CTRL_FIVEWIRE_MODE (1 << 2)
|
||||
|
||||
#define OTHER_IFC_CTRL 0x83
|
||||
#define OTHER_IFC_CTRL_SET 0x84
|
||||
#define OTHER_IFC_CTRL_CLR 0x85
|
||||
#define OTHER_IFC_CTRL_OE_INT_EN (1 << 6)
|
||||
#define OTHER_IFC_CTRL_CEA2011_MODE (1 << 5)
|
||||
#define OTHER_IFC_CTRL_FSLSSERIALMODE_4PIN (1 << 4)
|
||||
#define OTHER_IFC_CTRL_HIZ_ULPI_60MHZ_OUT (1 << 3)
|
||||
#define OTHER_IFC_CTRL_HIZ_ULPI (1 << 2)
|
||||
#define OTHER_IFC_CTRL_ALT_INT_REROUTE (1 << 0)
|
||||
|
||||
#define OTHER_INT_EN_RISE 0x86
|
||||
#define OTHER_INT_EN_RISE_SET 0x87
|
||||
#define OTHER_INT_EN_RISE_CLR 0x88
|
||||
#define OTHER_INT_EN_FALL 0x89
|
||||
#define OTHER_INT_EN_FALL_SET 0x8A
|
||||
#define OTHER_INT_EN_FALL_CLR 0x8B
|
||||
#define OTHER_INT_STS 0x8C
|
||||
#define OTHER_INT_LATCH 0x8D
|
||||
#define OTHER_INT_VB_SESS_VLD (1 << 7)
|
||||
#define OTHER_INT_DM_HI (1 << 6) /* not valid for "latch" reg */
|
||||
#define OTHER_INT_DP_HI (1 << 5) /* not valid for "latch" reg */
|
||||
#define OTHER_INT_BDIS_ACON (1 << 3) /* not valid for "fall" regs */
|
||||
#define OTHER_INT_MANU (1 << 1)
|
||||
#define OTHER_INT_ABNORMAL_STRESS (1 << 0)
|
||||
|
||||
#define ID_STATUS 0x96
|
||||
#define ID_RES_FLOAT (1 << 4)
|
||||
#define ID_RES_440K (1 << 3)
|
||||
#define ID_RES_200K (1 << 2)
|
||||
#define ID_RES_102K (1 << 1)
|
||||
#define ID_RES_GND (1 << 0)
|
||||
|
||||
#define POWER_CTRL 0xAC
|
||||
#define POWER_CTRL_SET 0xAD
|
||||
#define POWER_CTRL_CLR 0xAE
|
||||
#define POWER_CTRL_OTG_ENAB (1 << 5)
|
||||
|
||||
#define OTHER_IFC_CTRL2 0xAF
|
||||
#define OTHER_IFC_CTRL2_SET 0xB0
|
||||
#define OTHER_IFC_CTRL2_CLR 0xB1
|
||||
#define OTHER_IFC_CTRL2_ULPI_STP_LOW (1 << 4)
|
||||
#define OTHER_IFC_CTRL2_ULPI_TXEN_POL (1 << 3)
|
||||
#define OTHER_IFC_CTRL2_ULPI_4PIN_2430 (1 << 2)
|
||||
#define OTHER_IFC_CTRL2_USB_INT_OUTSEL_MASK (3 << 0) /* bits 0 and 1 */
|
||||
#define OTHER_IFC_CTRL2_USB_INT_OUTSEL_INT1N (0 << 0)
|
||||
#define OTHER_IFC_CTRL2_USB_INT_OUTSEL_INT2N (1 << 0)
|
||||
|
||||
#define REG_CTRL_EN 0xB2
|
||||
#define REG_CTRL_EN_SET 0xB3
|
||||
#define REG_CTRL_EN_CLR 0xB4
|
||||
#define REG_CTRL_ERROR 0xB5
|
||||
#define ULPI_I2C_CONFLICT_INTEN (1 << 0)
|
||||
|
||||
#define OTHER_FUNC_CTRL2 0xB8
|
||||
#define OTHER_FUNC_CTRL2_SET 0xB9
|
||||
#define OTHER_FUNC_CTRL2_CLR 0xBA
|
||||
#define OTHER_FUNC_CTRL2_VBAT_TIMER_EN (1 << 0)
|
||||
|
||||
/* following registers do not have separate _clr and _set registers */
|
||||
#define VBUS_DEBOUNCE 0xC0
|
||||
#define ID_DEBOUNCE 0xC1
|
||||
#define VBAT_TIMER 0xD3
|
||||
#define PHY_PWR_CTRL 0xFD
|
||||
#define PHY_PWR_PHYPWD (1 << 0)
|
||||
#define PHY_CLK_CTRL 0xFE
|
||||
#define PHY_CLK_CTRL_CLOCKGATING_EN (1 << 2)
|
||||
#define PHY_CLK_CTRL_CLK32K_EN (1 << 1)
|
||||
#define REQ_PHY_DPLL_CLK (1 << 0)
|
||||
#define PHY_CLK_CTRL_STS 0xFF
|
||||
#define PHY_DPLL_CLK (1 << 0)
|
||||
|
||||
/* In module TWL4030_MODULE_PM_MASTER */
|
||||
#define PROTECT_KEY 0x0E
|
||||
|
||||
/* In module TWL4030_MODULE_PM_RECEIVER */
|
||||
#define VUSB_DEDICATED1 0x7D
|
||||
#define VUSB_DEDICATED2 0x7E
|
||||
#define VUSB1V5_DEV_GRP 0x71
|
||||
#define VUSB1V5_TYPE 0x72
|
||||
#define VUSB1V5_REMAP 0x73
|
||||
#define VUSB1V8_DEV_GRP 0x74
|
||||
#define VUSB1V8_TYPE 0x75
|
||||
#define VUSB1V8_REMAP 0x76
|
||||
#define VUSB3V1_DEV_GRP 0x77
|
||||
#define VUSB3V1_TYPE 0x78
|
||||
#define VUSB3V1_REMAP 0x79
|
||||
|
||||
/* In module TWL4030_MODULE_INTBR */
|
||||
#define PMBR1 0x0D
|
||||
#define GPIO_USB_4PIN_ULPI_2430C (3 << 0)
|
||||
|
||||
|
||||
|
||||
enum linkstat {
|
||||
USB_LINK_UNKNOWN = 0,
|
||||
USB_LINK_NONE,
|
||||
USB_LINK_VBUS,
|
||||
USB_LINK_ID,
|
||||
};
|
||||
|
||||
struct twl4030_usb {
|
||||
struct otg_transceiver otg;
|
||||
struct device *dev;
|
||||
|
||||
/* for vbus reporting with irqs disabled */
|
||||
spinlock_t lock;
|
||||
|
||||
/* pin configuration */
|
||||
enum twl4030_usb_mode usb_mode;
|
||||
|
||||
int irq;
|
||||
u8 linkstat;
|
||||
u8 asleep;
|
||||
bool irq_enabled;
|
||||
};
|
||||
|
||||
/* internal define on top of container_of */
|
||||
#define xceiv_to_twl(x) container_of((x), struct twl4030_usb, otg);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static int twl4030_i2c_write_u8_verify(struct twl4030_usb *twl,
|
||||
u8 module, u8 data, u8 address)
|
||||
{
|
||||
u8 check;
|
||||
|
||||
if ((twl4030_i2c_write_u8(module, data, address) >= 0) &&
|
||||
(twl4030_i2c_read_u8(module, &check, address) >= 0) &&
|
||||
(check == data))
|
||||
return 0;
|
||||
dev_dbg(twl->dev, "Write%d[%d,0x%x] wrote %02x but read %02x\n",
|
||||
1, module, address, check, data);
|
||||
|
||||
/* Failed once: Try again */
|
||||
if ((twl4030_i2c_write_u8(module, data, address) >= 0) &&
|
||||
(twl4030_i2c_read_u8(module, &check, address) >= 0) &&
|
||||
(check == data))
|
||||
return 0;
|
||||
dev_dbg(twl->dev, "Write%d[%d,0x%x] wrote %02x but read %02x\n",
|
||||
2, module, address, check, data);
|
||||
|
||||
/* Failed again: Return error */
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
#define twl4030_usb_write_verify(twl, address, data) \
|
||||
twl4030_i2c_write_u8_verify(twl, TWL4030_MODULE_USB, (data), (address))
|
||||
|
||||
static inline int twl4030_usb_write(struct twl4030_usb *twl,
|
||||
u8 address, u8 data)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
ret = twl4030_i2c_write_u8(TWL4030_MODULE_USB, data, address);
|
||||
if (ret < 0)
|
||||
dev_dbg(twl->dev,
|
||||
"TWL4030:USB:Write[0x%x] Error %d\n", address, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int twl4030_readb(struct twl4030_usb *twl, u8 module, u8 address)
|
||||
{
|
||||
u8 data;
|
||||
int ret = 0;
|
||||
|
||||
ret = twl4030_i2c_read_u8(module, &data, address);
|
||||
if (ret >= 0)
|
||||
ret = data;
|
||||
else
|
||||
dev_dbg(twl->dev,
|
||||
"TWL4030:readb[0x%x,0x%x] Error %d\n",
|
||||
module, address, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int twl4030_usb_read(struct twl4030_usb *twl, u8 address)
|
||||
{
|
||||
return twl4030_readb(twl, TWL4030_MODULE_USB, address);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static inline int
|
||||
twl4030_usb_set_bits(struct twl4030_usb *twl, u8 reg, u8 bits)
|
||||
{
|
||||
return twl4030_usb_write(twl, reg + 1, bits);
|
||||
}
|
||||
|
||||
static inline int
|
||||
twl4030_usb_clear_bits(struct twl4030_usb *twl, u8 reg, u8 bits)
|
||||
{
|
||||
return twl4030_usb_write(twl, reg + 2, bits);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static enum linkstat twl4030_usb_linkstat(struct twl4030_usb *twl)
|
||||
{
|
||||
int status;
|
||||
int linkstat = USB_LINK_UNKNOWN;
|
||||
|
||||
/* STS_HW_CONDITIONS */
|
||||
status = twl4030_readb(twl, TWL4030_MODULE_PM_MASTER, 0x0f);
|
||||
if (status < 0)
|
||||
dev_err(twl->dev, "USB link status err %d\n", status);
|
||||
else if (status & BIT(7))
|
||||
linkstat = USB_LINK_VBUS;
|
||||
else if (status & BIT(2))
|
||||
linkstat = USB_LINK_ID;
|
||||
else
|
||||
linkstat = USB_LINK_NONE;
|
||||
|
||||
dev_dbg(twl->dev, "HW_CONDITIONS 0x%02x/%d; link %d\n",
|
||||
status, status, linkstat);
|
||||
|
||||
/* REVISIT this assumes host and peripheral controllers
|
||||
* are registered, and that both are active...
|
||||
*/
|
||||
|
||||
spin_lock_irq(&twl->lock);
|
||||
twl->linkstat = linkstat;
|
||||
if (linkstat == USB_LINK_ID) {
|
||||
twl->otg.default_a = true;
|
||||
twl->otg.state = OTG_STATE_A_IDLE;
|
||||
} else {
|
||||
twl->otg.default_a = false;
|
||||
twl->otg.state = OTG_STATE_B_IDLE;
|
||||
}
|
||||
spin_unlock_irq(&twl->lock);
|
||||
|
||||
return linkstat;
|
||||
}
|
||||
|
||||
static void twl4030_usb_set_mode(struct twl4030_usb *twl, int mode)
|
||||
{
|
||||
twl->usb_mode = mode;
|
||||
|
||||
switch (mode) {
|
||||
case T2_USB_MODE_ULPI:
|
||||
twl4030_usb_clear_bits(twl, IFC_CTRL, IFC_CTRL_CARKITMODE);
|
||||
twl4030_usb_set_bits(twl, POWER_CTRL, POWER_CTRL_OTG_ENAB);
|
||||
twl4030_usb_clear_bits(twl, FUNC_CTRL,
|
||||
FUNC_CTRL_XCVRSELECT_MASK |
|
||||
FUNC_CTRL_OPMODE_MASK);
|
||||
break;
|
||||
case -1:
|
||||
/* FIXME: power on defaults */
|
||||
break;
|
||||
default:
|
||||
dev_err(twl->dev, "unsupported T2 transceiver mode %d\n",
|
||||
mode);
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
static void twl4030_i2c_access(struct twl4030_usb *twl, int on)
|
||||
{
|
||||
unsigned long timeout;
|
||||
int val = twl4030_usb_read(twl, PHY_CLK_CTRL);
|
||||
|
||||
if (val >= 0) {
|
||||
if (on) {
|
||||
/* enable DPLL to access PHY registers over I2C */
|
||||
val |= REQ_PHY_DPLL_CLK;
|
||||
WARN_ON(twl4030_usb_write_verify(twl, PHY_CLK_CTRL,
|
||||
(u8)val) < 0);
|
||||
|
||||
timeout = jiffies + HZ;
|
||||
while (!(twl4030_usb_read(twl, PHY_CLK_CTRL_STS) &
|
||||
PHY_DPLL_CLK)
|
||||
&& time_before(jiffies, timeout))
|
||||
udelay(10);
|
||||
if (!(twl4030_usb_read(twl, PHY_CLK_CTRL_STS) &
|
||||
PHY_DPLL_CLK))
|
||||
dev_err(twl->dev, "Timeout setting T2 HSUSB "
|
||||
"PHY DPLL clock\n");
|
||||
} else {
|
||||
/* let ULPI control the DPLL clock */
|
||||
val &= ~REQ_PHY_DPLL_CLK;
|
||||
WARN_ON(twl4030_usb_write_verify(twl, PHY_CLK_CTRL,
|
||||
(u8)val) < 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void twl4030_phy_power(struct twl4030_usb *twl, int on)
|
||||
{
|
||||
u8 pwr;
|
||||
|
||||
pwr = twl4030_usb_read(twl, PHY_PWR_CTRL);
|
||||
if (on) {
|
||||
pwr &= ~PHY_PWR_PHYPWD;
|
||||
WARN_ON(twl4030_usb_write_verify(twl, PHY_PWR_CTRL, pwr) < 0);
|
||||
twl4030_usb_write(twl, PHY_CLK_CTRL,
|
||||
twl4030_usb_read(twl, PHY_CLK_CTRL) |
|
||||
(PHY_CLK_CTRL_CLOCKGATING_EN |
|
||||
PHY_CLK_CTRL_CLK32K_EN));
|
||||
} else {
|
||||
pwr |= PHY_PWR_PHYPWD;
|
||||
WARN_ON(twl4030_usb_write_verify(twl, PHY_PWR_CTRL, pwr) < 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void twl4030_phy_suspend(struct twl4030_usb *twl, int controller_off)
|
||||
{
|
||||
if (twl->asleep)
|
||||
return;
|
||||
|
||||
twl4030_phy_power(twl, 0);
|
||||
twl->asleep = 1;
|
||||
}
|
||||
|
||||
static void twl4030_phy_resume(struct twl4030_usb *twl)
|
||||
{
|
||||
if (!twl->asleep)
|
||||
return;
|
||||
|
||||
twl4030_phy_power(twl, 1);
|
||||
twl4030_i2c_access(twl, 1);
|
||||
twl4030_usb_set_mode(twl, twl->usb_mode);
|
||||
if (twl->usb_mode == T2_USB_MODE_ULPI)
|
||||
twl4030_i2c_access(twl, 0);
|
||||
twl->asleep = 0;
|
||||
}
|
||||
|
||||
static void twl4030_usb_ldo_init(struct twl4030_usb *twl)
|
||||
{
|
||||
/* Enable writing to power configuration registers */
|
||||
twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0xC0, PROTECT_KEY);
|
||||
twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0x0C, PROTECT_KEY);
|
||||
|
||||
/* put VUSB3V1 LDO in active state */
|
||||
twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB_DEDICATED2);
|
||||
|
||||
/* input to VUSB3V1 LDO is from VBAT, not VBUS */
|
||||
twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0x14, VUSB_DEDICATED1);
|
||||
|
||||
/* turn on 3.1V regulator */
|
||||
twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0x20, VUSB3V1_DEV_GRP);
|
||||
twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB3V1_TYPE);
|
||||
|
||||
/* turn on 1.5V regulator */
|
||||
twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0x20, VUSB1V5_DEV_GRP);
|
||||
twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB1V5_TYPE);
|
||||
|
||||
/* turn on 1.8V regulator */
|
||||
twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0x20, VUSB1V8_DEV_GRP);
|
||||
twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB1V8_TYPE);
|
||||
|
||||
/* disable access to power configuration registers */
|
||||
twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0, PROTECT_KEY);
|
||||
}
|
||||
|
||||
static ssize_t twl4030_usb_vbus_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct twl4030_usb *twl = dev_get_drvdata(dev);
|
||||
unsigned long flags;
|
||||
int ret = -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&twl->lock, flags);
|
||||
ret = sprintf(buf, "%s\n",
|
||||
(twl->linkstat == USB_LINK_VBUS) ? "on" : "off");
|
||||
spin_unlock_irqrestore(&twl->lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
static DEVICE_ATTR(vbus, 0444, twl4030_usb_vbus_show, NULL);
|
||||
|
||||
static irqreturn_t twl4030_usb_irq(int irq, void *_twl)
|
||||
{
|
||||
struct twl4030_usb *twl = _twl;
|
||||
int status;
|
||||
|
||||
#ifdef CONFIG_LOCKDEP
|
||||
/* WORKAROUND for lockdep forcing IRQF_DISABLED on us, which
|
||||
* we don't want and can't tolerate. Although it might be
|
||||
* friendlier not to borrow this thread context...
|
||||
*/
|
||||
local_irq_enable();
|
||||
#endif
|
||||
|
||||
status = twl4030_usb_linkstat(twl);
|
||||
if (status != USB_LINK_UNKNOWN) {
|
||||
|
||||
/* FIXME add a set_power() method so that B-devices can
|
||||
* configure the charger appropriately. It's not always
|
||||
* correct to consume VBUS power, and how much current to
|
||||
* consume is a function of the USB configuration chosen
|
||||
* by the host.
|
||||
*
|
||||
* REVISIT usb_gadget_vbus_connect(...) as needed, ditto
|
||||
* its disconnect() sibling, when changing to/from the
|
||||
* USB_LINK_VBUS state. musb_hdrc won't care until it
|
||||
* starts to handle softconnect right.
|
||||
*/
|
||||
twl4030charger_usb_en(status == USB_LINK_VBUS);
|
||||
|
||||
if (status == USB_LINK_NONE)
|
||||
twl4030_phy_suspend(twl, 0);
|
||||
else
|
||||
twl4030_phy_resume(twl);
|
||||
}
|
||||
sysfs_notify(&twl->dev->kobj, NULL, "vbus");
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int twl4030_set_suspend(struct otg_transceiver *x, int suspend)
|
||||
{
|
||||
struct twl4030_usb *twl = xceiv_to_twl(x);
|
||||
|
||||
if (suspend)
|
||||
twl4030_phy_suspend(twl, 1);
|
||||
else
|
||||
twl4030_phy_resume(twl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int twl4030_set_peripheral(struct otg_transceiver *x,
|
||||
struct usb_gadget *gadget)
|
||||
{
|
||||
struct twl4030_usb *twl;
|
||||
|
||||
if (!x)
|
||||
return -ENODEV;
|
||||
|
||||
twl = xceiv_to_twl(x);
|
||||
twl->otg.gadget = gadget;
|
||||
if (!gadget)
|
||||
twl->otg.state = OTG_STATE_UNDEFINED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int twl4030_set_host(struct otg_transceiver *x, struct usb_bus *host)
|
||||
{
|
||||
struct twl4030_usb *twl;
|
||||
|
||||
if (!x)
|
||||
return -ENODEV;
|
||||
|
||||
twl = xceiv_to_twl(x);
|
||||
twl->otg.host = host;
|
||||
if (!host)
|
||||
twl->otg.state = OTG_STATE_UNDEFINED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init twl4030_usb_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct twl4030_usb_data *pdata = pdev->dev.platform_data;
|
||||
struct twl4030_usb *twl;
|
||||
int status;
|
||||
|
||||
if (!pdata) {
|
||||
dev_dbg(&pdev->dev, "platform_data not available\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
twl = kzalloc(sizeof *twl, GFP_KERNEL);
|
||||
if (!twl)
|
||||
return -ENOMEM;
|
||||
|
||||
twl->dev = &pdev->dev;
|
||||
twl->irq = platform_get_irq(pdev, 0);
|
||||
twl->otg.dev = twl->dev;
|
||||
twl->otg.label = "twl4030";
|
||||
twl->otg.set_host = twl4030_set_host;
|
||||
twl->otg.set_peripheral = twl4030_set_peripheral;
|
||||
twl->otg.set_suspend = twl4030_set_suspend;
|
||||
twl->usb_mode = pdata->usb_mode;
|
||||
twl->asleep = 1;
|
||||
|
||||
/* init spinlock for workqueue */
|
||||
spin_lock_init(&twl->lock);
|
||||
|
||||
twl4030_usb_ldo_init(twl);
|
||||
otg_set_transceiver(&twl->otg);
|
||||
|
||||
platform_set_drvdata(pdev, twl);
|
||||
if (device_create_file(&pdev->dev, &dev_attr_vbus))
|
||||
dev_warn(&pdev->dev, "could not create sysfs file\n");
|
||||
|
||||
/* Our job is to use irqs and status from the power module
|
||||
* to keep the transceiver disabled when nothing's connected.
|
||||
*
|
||||
* FIXME we actually shouldn't start enabling it until the
|
||||
* USB controller drivers have said they're ready, by calling
|
||||
* set_host() and/or set_peripheral() ... OTG_capable boards
|
||||
* need both handles, otherwise just one suffices.
|
||||
*/
|
||||
twl->irq_enabled = true;
|
||||
status = request_irq(twl->irq, twl4030_usb_irq,
|
||||
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
|
||||
"twl4030_usb", twl);
|
||||
if (status < 0) {
|
||||
dev_dbg(&pdev->dev, "can't get IRQ %d, err %d\n",
|
||||
twl->irq, status);
|
||||
kfree(twl);
|
||||
return status;
|
||||
}
|
||||
|
||||
/* The IRQ handler just handles changes from the previous states
|
||||
* of the ID and VBUS pins ... in probe() we must initialize that
|
||||
* previous state. The easy way: fake an IRQ.
|
||||
*
|
||||
* REVISIT: a real IRQ might have happened already, if PREEMPT is
|
||||
* enabled. Else the IRQ may not yet be configured or enabled,
|
||||
* because of scheduling delays.
|
||||
*/
|
||||
twl4030_usb_irq(twl->irq, twl);
|
||||
|
||||
dev_info(&pdev->dev, "Initialized TWL4030 USB module\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __exit twl4030_usb_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct twl4030_usb *twl = platform_get_drvdata(pdev);
|
||||
int val;
|
||||
|
||||
free_irq(twl->irq, twl);
|
||||
device_remove_file(twl->dev, &dev_attr_vbus);
|
||||
|
||||
/* set transceiver mode to power on defaults */
|
||||
twl4030_usb_set_mode(twl, -1);
|
||||
|
||||
/* autogate 60MHz ULPI clock,
|
||||
* clear dpll clock request for i2c access,
|
||||
* disable 32KHz
|
||||
*/
|
||||
val = twl4030_usb_read(twl, PHY_CLK_CTRL);
|
||||
if (val >= 0) {
|
||||
val |= PHY_CLK_CTRL_CLOCKGATING_EN;
|
||||
val &= ~(PHY_CLK_CTRL_CLK32K_EN | REQ_PHY_DPLL_CLK);
|
||||
twl4030_usb_write(twl, PHY_CLK_CTRL, (u8)val);
|
||||
}
|
||||
|
||||
/* disable complete OTG block */
|
||||
twl4030_usb_clear_bits(twl, POWER_CTRL, POWER_CTRL_OTG_ENAB);
|
||||
|
||||
twl4030_phy_power(twl, 0);
|
||||
|
||||
kfree(twl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver twl4030_usb_driver = {
|
||||
.probe = twl4030_usb_probe,
|
||||
.remove = __exit_p(twl4030_usb_remove),
|
||||
.driver = {
|
||||
.name = "twl4030_usb",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init twl4030_usb_init(void)
|
||||
{
|
||||
return platform_driver_register(&twl4030_usb_driver);
|
||||
}
|
||||
subsys_initcall(twl4030_usb_init);
|
||||
|
||||
static void __exit twl4030_usb_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&twl4030_usb_driver);
|
||||
}
|
||||
module_exit(twl4030_usb_exit);
|
||||
|
||||
MODULE_ALIAS("platform:twl4030_usb");
|
||||
MODULE_AUTHOR("Texas Instruments, Inc, Nokia Corporation");
|
||||
MODULE_DESCRIPTION("TWL4030 USB transceiver driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
@ -496,6 +496,14 @@ config USB_SERIAL_SAFE_PADDED
|
|||
bool "USB Secure Encapsulated Driver - Padded"
|
||||
depends on USB_SERIAL_SAFE
|
||||
|
||||
config USB_SERIAL_SIEMENS_MPI
|
||||
tristate "USB Siemens MPI driver"
|
||||
help
|
||||
Say M here if you want to use a Siemens USB/MPI adapter.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called siemens_mpi.
|
||||
|
||||
config USB_SERIAL_SIERRAWIRELESS
|
||||
tristate "USB Sierra Wireless Driver"
|
||||
help
|
||||
|
|
@ -565,6 +573,15 @@ config USB_SERIAL_OMNINET
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called omninet.
|
||||
|
||||
config USB_SERIAL_OPTICON
|
||||
tristate "USB Opticon Barcode driver (serial mode)"
|
||||
help
|
||||
Say Y here if you want to use a Opticon USB Barcode device
|
||||
in serial emulation mode.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called opticon.
|
||||
|
||||
config USB_SERIAL_DEBUG
|
||||
tristate "USB Debugging Device"
|
||||
help
|
||||
|
|
|
|||
|
|
@ -41,10 +41,12 @@ obj-$(CONFIG_USB_SERIAL_MOS7840) += mos7840.o
|
|||
obj-$(CONFIG_USB_SERIAL_MOTOROLA) += moto_modem.o
|
||||
obj-$(CONFIG_USB_SERIAL_NAVMAN) += navman.o
|
||||
obj-$(CONFIG_USB_SERIAL_OMNINET) += omninet.o
|
||||
obj-$(CONFIG_USB_SERIAL_OPTICON) += opticon.o
|
||||
obj-$(CONFIG_USB_SERIAL_OPTION) += option.o
|
||||
obj-$(CONFIG_USB_SERIAL_OTI6858) += oti6858.o
|
||||
obj-$(CONFIG_USB_SERIAL_PL2303) += pl2303.o
|
||||
obj-$(CONFIG_USB_SERIAL_SAFE) += safe_serial.o
|
||||
obj-$(CONFIG_USB_SERIAL_SIEMENS_MPI) += siemens_mpi.o
|
||||
obj-$(CONFIG_USB_SERIAL_SIERRAWIRELESS) += sierra.o
|
||||
obj-$(CONFIG_USB_SERIAL_SPCP8X5) += spcp8x5.o
|
||||
obj-$(CONFIG_USB_SERIAL_TI) += ti_usb_3410_5052.o
|
||||
|
|
|
|||
|
|
@ -635,8 +635,7 @@ static int digi_write_oob_command(struct usb_serial_port *port,
|
|||
|
||||
spin_lock_irqsave(&oob_priv->dp_port_lock, flags);
|
||||
while (count > 0) {
|
||||
while (oob_port->write_urb->status == -EINPROGRESS
|
||||
|| oob_priv->dp_write_urb_in_use) {
|
||||
while (oob_priv->dp_write_urb_in_use) {
|
||||
cond_wait_interruptible_timeout_irqrestore(
|
||||
&oob_port->write_wait, DIGI_RETRY_TIMEOUT,
|
||||
&oob_priv->dp_port_lock, flags);
|
||||
|
|
@ -699,9 +698,8 @@ static int digi_write_inb_command(struct usb_serial_port *port,
|
|||
|
||||
spin_lock_irqsave(&priv->dp_port_lock, flags);
|
||||
while (count > 0 && ret == 0) {
|
||||
while ((port->write_urb->status == -EINPROGRESS
|
||||
|| priv->dp_write_urb_in_use)
|
||||
&& time_before(jiffies, timeout)) {
|
||||
while (priv->dp_write_urb_in_use &&
|
||||
time_before(jiffies, timeout)) {
|
||||
cond_wait_interruptible_timeout_irqrestore(
|
||||
&port->write_wait, DIGI_RETRY_TIMEOUT,
|
||||
&priv->dp_port_lock, flags);
|
||||
|
|
@ -779,8 +777,7 @@ static int digi_set_modem_signals(struct usb_serial_port *port,
|
|||
spin_lock_irqsave(&oob_priv->dp_port_lock, flags);
|
||||
spin_lock(&port_priv->dp_port_lock);
|
||||
|
||||
while (oob_port->write_urb->status == -EINPROGRESS ||
|
||||
oob_priv->dp_write_urb_in_use) {
|
||||
while (oob_priv->dp_write_urb_in_use) {
|
||||
spin_unlock(&port_priv->dp_port_lock);
|
||||
cond_wait_interruptible_timeout_irqrestore(
|
||||
&oob_port->write_wait, DIGI_RETRY_TIMEOUT,
|
||||
|
|
@ -1168,12 +1165,10 @@ static int digi_write(struct tty_struct *tty, struct usb_serial_port *port,
|
|||
|
||||
/* be sure only one write proceeds at a time */
|
||||
/* there are races on the port private buffer */
|
||||
/* and races to check write_urb->status */
|
||||
spin_lock_irqsave(&priv->dp_port_lock, flags);
|
||||
|
||||
/* wait for urb status clear to submit another urb */
|
||||
if (port->write_urb->status == -EINPROGRESS ||
|
||||
priv->dp_write_urb_in_use) {
|
||||
if (priv->dp_write_urb_in_use) {
|
||||
/* buffer data if count is 1 (probably put_char) if possible */
|
||||
if (count == 1 && priv->dp_out_buf_len < DIGI_OUT_BUF_SIZE) {
|
||||
priv->dp_out_buf[priv->dp_out_buf_len++] = *buf;
|
||||
|
|
@ -1236,7 +1231,7 @@ static void digi_write_bulk_callback(struct urb *urb)
|
|||
int ret = 0;
|
||||
int status = urb->status;
|
||||
|
||||
dbg("digi_write_bulk_callback: TOP, urb->status=%d", status);
|
||||
dbg("digi_write_bulk_callback: TOP, status=%d", status);
|
||||
|
||||
/* port and serial sanity check */
|
||||
if (port == NULL || (priv = usb_get_serial_port_data(port)) == NULL) {
|
||||
|
|
@ -1266,8 +1261,7 @@ static void digi_write_bulk_callback(struct urb *urb)
|
|||
/* try to send any buffered data on this port, if it is open */
|
||||
spin_lock(&priv->dp_port_lock);
|
||||
priv->dp_write_urb_in_use = 0;
|
||||
if (port->port.count && port->write_urb->status != -EINPROGRESS
|
||||
&& priv->dp_out_buf_len > 0) {
|
||||
if (port->port.count && priv->dp_out_buf_len > 0) {
|
||||
*((unsigned char *)(port->write_urb->transfer_buffer))
|
||||
= (unsigned char)DIGI_CMD_SEND_DATA;
|
||||
*((unsigned char *)(port->write_urb->transfer_buffer) + 1)
|
||||
|
|
@ -1305,8 +1299,7 @@ static int digi_write_room(struct tty_struct *tty)
|
|||
|
||||
spin_lock_irqsave(&priv->dp_port_lock, flags);
|
||||
|
||||
if (port->write_urb->status == -EINPROGRESS ||
|
||||
priv->dp_write_urb_in_use)
|
||||
if (priv->dp_write_urb_in_use)
|
||||
room = 0;
|
||||
else
|
||||
room = port->bulk_out_size - 2 - priv->dp_out_buf_len;
|
||||
|
|
@ -1322,8 +1315,7 @@ static int digi_chars_in_buffer(struct tty_struct *tty)
|
|||
struct usb_serial_port *port = tty->driver_data;
|
||||
struct digi_port *priv = usb_get_serial_port_data(port);
|
||||
|
||||
if (port->write_urb->status == -EINPROGRESS
|
||||
|| priv->dp_write_urb_in_use) {
|
||||
if (priv->dp_write_urb_in_use) {
|
||||
dbg("digi_chars_in_buffer: port=%d, chars=%d",
|
||||
priv->dp_port_num, port->bulk_out_size - 2);
|
||||
/* return(port->bulk_out_size - 2); */
|
||||
|
|
@ -1702,7 +1694,7 @@ static int digi_read_inb_callback(struct urb *urb)
|
|||
/* short/multiple packet check */
|
||||
if (urb->actual_length != len + 2) {
|
||||
dev_err(&port->dev, "%s: INCOMPLETE OR MULTIPLE PACKET, "
|
||||
"urb->status=%d, port=%d, opcode=%d, len=%d, "
|
||||
"status=%d, port=%d, opcode=%d, len=%d, "
|
||||
"actual_length=%d, status=%d\n", __func__, status,
|
||||
priv->dp_port_num, opcode, len, urb->actual_length,
|
||||
port_status);
|
||||
|
|
|
|||
|
|
@ -1056,7 +1056,7 @@ static void garmin_write_bulk_callback(struct urb *urb)
|
|||
|
||||
if (status) {
|
||||
dbg("%s - nonzero write bulk status received: %d",
|
||||
__func__, urb->status);
|
||||
__func__, status);
|
||||
spin_lock_irqsave(&garmin_data_p->lock, flags);
|
||||
garmin_data_p->flags |= CLEAR_HALT_REQUIRED;
|
||||
spin_unlock_irqrestore(&garmin_data_p->lock, flags);
|
||||
|
|
|
|||
|
|
@ -473,7 +473,7 @@ static struct usb_serial_driver ipw_device = {
|
|||
|
||||
|
||||
|
||||
static int usb_ipw_init(void)
|
||||
static int __init usb_ipw_init(void)
|
||||
{
|
||||
int retval;
|
||||
|
||||
|
|
@ -490,7 +490,7 @@ static int usb_ipw_init(void)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void usb_ipw_exit(void)
|
||||
static void __exit usb_ipw_exit(void)
|
||||
{
|
||||
usb_deregister(&usb_ipw_driver);
|
||||
usb_serial_deregister(&ipw_device);
|
||||
|
|
|
|||
|
|
@ -190,10 +190,12 @@ static void iuu_rxcmd(struct urb *urb)
|
|||
{
|
||||
struct usb_serial_port *port = urb->context;
|
||||
int result;
|
||||
int status = urb->status;
|
||||
|
||||
dbg("%s - enter", __func__);
|
||||
|
||||
if (urb->status) {
|
||||
dbg("%s - urb->status = %d", __func__, urb->status);
|
||||
if (status) {
|
||||
dbg("%s - status = %d", __func__, status);
|
||||
/* error stop all */
|
||||
return;
|
||||
}
|
||||
|
|
@ -245,10 +247,12 @@ static void iuu_update_status_callback(struct urb *urb)
|
|||
struct usb_serial_port *port = urb->context;
|
||||
struct iuu_private *priv = usb_get_serial_port_data(port);
|
||||
u8 *st;
|
||||
int status = urb->status;
|
||||
|
||||
dbg("%s - enter", __func__);
|
||||
|
||||
if (urb->status) {
|
||||
dbg("%s - urb->status = %d", __func__, urb->status);
|
||||
if (status) {
|
||||
dbg("%s - status = %d", __func__, status);
|
||||
/* error stop all */
|
||||
return;
|
||||
}
|
||||
|
|
@ -274,9 +278,9 @@ static void iuu_status_callback(struct urb *urb)
|
|||
{
|
||||
struct usb_serial_port *port = urb->context;
|
||||
int result;
|
||||
dbg("%s - enter", __func__);
|
||||
int status = urb->status;
|
||||
|
||||
dbg("%s - urb->status = %d", __func__, urb->status);
|
||||
dbg("%s - status = %d", __func__, status);
|
||||
usb_fill_bulk_urb(port->read_urb, port->serial->dev,
|
||||
usb_rcvbulkpipe(port->serial->dev,
|
||||
port->bulk_in_endpointAddress),
|
||||
|
|
@ -618,11 +622,12 @@ static void read_buf_callback(struct urb *urb)
|
|||
struct usb_serial_port *port = urb->context;
|
||||
unsigned char *data = urb->transfer_buffer;
|
||||
struct tty_struct *tty;
|
||||
dbg("%s - urb->status = %d", __func__, urb->status);
|
||||
int status = urb->status;
|
||||
|
||||
if (urb->status) {
|
||||
dbg("%s - urb->status = %d", __func__, urb->status);
|
||||
if (urb->status == -EPROTO) {
|
||||
dbg("%s - status = %d", __func__, status);
|
||||
|
||||
if (status) {
|
||||
if (status == -EPROTO) {
|
||||
/* reschedule needed */
|
||||
}
|
||||
return;
|
||||
|
|
@ -695,7 +700,7 @@ static void iuu_uart_read_callback(struct urb *urb)
|
|||
struct usb_serial_port *port = urb->context;
|
||||
struct iuu_private *priv = usb_get_serial_port_data(port);
|
||||
unsigned long flags;
|
||||
int status;
|
||||
int status = urb->status;
|
||||
int error = 0;
|
||||
int len = 0;
|
||||
unsigned char *data = urb->transfer_buffer;
|
||||
|
|
@ -703,8 +708,8 @@ static void iuu_uart_read_callback(struct urb *urb)
|
|||
|
||||
dbg("%s - enter", __func__);
|
||||
|
||||
if (urb->status) {
|
||||
dbg("%s - urb->status = %d", __func__, urb->status);
|
||||
if (status) {
|
||||
dbg("%s - status = %d", __func__, status);
|
||||
/* error stop all */
|
||||
return;
|
||||
}
|
||||
|
|
@ -782,12 +787,11 @@ static void read_rxcmd_callback(struct urb *urb)
|
|||
{
|
||||
struct usb_serial_port *port = urb->context;
|
||||
int result;
|
||||
dbg("%s - enter", __func__);
|
||||
int status = urb->status;
|
||||
|
||||
dbg("%s - urb->status = %d", __func__, urb->status);
|
||||
dbg("%s - status = %d", __func__, status);
|
||||
|
||||
if (urb->status) {
|
||||
dbg("%s - urb->status = %d", __func__, urb->status);
|
||||
if (status) {
|
||||
/* error stop all */
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -214,6 +214,7 @@ struct moschip_port {
|
|||
spinlock_t pool_lock;
|
||||
struct urb *write_urb_pool[NUM_URBS];
|
||||
char busy[NUM_URBS];
|
||||
bool read_urb_busy;
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -679,26 +680,30 @@ static void mos7840_bulk_in_callback(struct urb *urb)
|
|||
struct tty_struct *tty;
|
||||
int status = urb->status;
|
||||
|
||||
if (status) {
|
||||
dbg("nonzero read bulk status received: %d", status);
|
||||
return;
|
||||
}
|
||||
|
||||
mos7840_port = urb->context;
|
||||
if (!mos7840_port) {
|
||||
dbg("%s", "NULL mos7840_port pointer \n");
|
||||
mos7840_port->read_urb_busy = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (status) {
|
||||
dbg("nonzero read bulk status received: %d", status);
|
||||
mos7840_port->read_urb_busy = false;
|
||||
return;
|
||||
}
|
||||
|
||||
port = (struct usb_serial_port *)mos7840_port->port;
|
||||
if (mos7840_port_paranoia_check(port, __func__)) {
|
||||
dbg("%s", "Port Paranoia failed \n");
|
||||
mos7840_port->read_urb_busy = false;
|
||||
return;
|
||||
}
|
||||
|
||||
serial = mos7840_get_usb_serial(port, __func__);
|
||||
if (!serial) {
|
||||
dbg("%s\n", "Bad serial pointer ");
|
||||
mos7840_port->read_urb_busy = false;
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -725,17 +730,19 @@ static void mos7840_bulk_in_callback(struct urb *urb)
|
|||
|
||||
if (!mos7840_port->read_urb) {
|
||||
dbg("%s", "URB KILLED !!!\n");
|
||||
mos7840_port->read_urb_busy = false;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
mos7840_port->read_urb->dev = serial->dev;
|
||||
|
||||
mos7840_port->read_urb_busy = true;
|
||||
retval = usb_submit_urb(mos7840_port->read_urb, GFP_ATOMIC);
|
||||
|
||||
if (retval) {
|
||||
dbg(" usb_submit_urb(read bulk) failed, retval = %d",
|
||||
retval);
|
||||
dbg("usb_submit_urb(read bulk) failed, retval = %d", retval);
|
||||
mos7840_port->read_urb_busy = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1055,10 +1062,12 @@ static int mos7840_open(struct tty_struct *tty,
|
|||
|
||||
dbg("mos7840_open: bulkin endpoint is %d\n",
|
||||
port->bulk_in_endpointAddress);
|
||||
mos7840_port->read_urb_busy = true;
|
||||
response = usb_submit_urb(mos7840_port->read_urb, GFP_KERNEL);
|
||||
if (response) {
|
||||
dev_err(&port->dev, "%s - Error %d submitting control urb\n",
|
||||
__func__, response);
|
||||
mos7840_port->read_urb_busy = false;
|
||||
}
|
||||
|
||||
/* initialize our wait queues */
|
||||
|
|
@ -1227,6 +1236,7 @@ static void mos7840_close(struct tty_struct *tty,
|
|||
if (mos7840_port->read_urb) {
|
||||
dbg("%s", "Shutdown bulk read\n");
|
||||
usb_kill_urb(mos7840_port->read_urb);
|
||||
mos7840_port->read_urb_busy = false;
|
||||
}
|
||||
if ((&mos7840_port->control_urb)) {
|
||||
dbg("%s", "Shutdown control read\n");
|
||||
|
|
@ -2043,14 +2053,14 @@ static void mos7840_change_port_settings(struct tty_struct *tty,
|
|||
Data = 0x0c;
|
||||
mos7840_set_uart_reg(port, INTERRUPT_ENABLE_REGISTER, Data);
|
||||
|
||||
if (mos7840_port->read_urb->status != -EINPROGRESS) {
|
||||
if (mos7840_port->read_urb_busy == false) {
|
||||
mos7840_port->read_urb->dev = serial->dev;
|
||||
|
||||
mos7840_port->read_urb_busy = true;
|
||||
status = usb_submit_urb(mos7840_port->read_urb, GFP_ATOMIC);
|
||||
|
||||
if (status) {
|
||||
dbg(" usb_submit_urb(read bulk) failed, status = %d",
|
||||
dbg("usb_submit_urb(read bulk) failed, status = %d",
|
||||
status);
|
||||
mos7840_port->read_urb_busy = false;
|
||||
}
|
||||
}
|
||||
wake_up(&mos7840_port->delta_msr_wait);
|
||||
|
|
@ -2117,12 +2127,14 @@ static void mos7840_set_termios(struct tty_struct *tty,
|
|||
return;
|
||||
}
|
||||
|
||||
if (mos7840_port->read_urb->status != -EINPROGRESS) {
|
||||
if (mos7840_port->read_urb_busy == false) {
|
||||
mos7840_port->read_urb->dev = serial->dev;
|
||||
mos7840_port->read_urb_busy = true;
|
||||
status = usb_submit_urb(mos7840_port->read_urb, GFP_ATOMIC);
|
||||
if (status) {
|
||||
dbg(" usb_submit_urb(read bulk) failed, status = %d",
|
||||
dbg("usb_submit_urb(read bulk) failed, status = %d",
|
||||
status);
|
||||
mos7840_port->read_urb_busy = false;
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
|
|
|||
358
drivers/usb/serial/opticon.c
Normal file
358
drivers/usb/serial/opticon.c
Normal file
|
|
@ -0,0 +1,358 @@
|
|||
/*
|
||||
* Opticon USB barcode to serial driver
|
||||
*
|
||||
* Copyright (C) 2008 Greg Kroah-Hartman <gregkh@suse.de>
|
||||
* Copyright (C) 2008 Novell Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version
|
||||
* 2 as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/tty_driver.h>
|
||||
#include <linux/tty_flip.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/serial.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
static int debug;
|
||||
|
||||
static struct usb_device_id id_table[] = {
|
||||
{ USB_DEVICE(0x065a, 0x0009) },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(usb, id_table);
|
||||
|
||||
/* This structure holds all of the individual device information */
|
||||
struct opticon_private {
|
||||
struct usb_device *udev;
|
||||
struct usb_serial *serial;
|
||||
struct usb_serial_port *port;
|
||||
unsigned char *bulk_in_buffer;
|
||||
struct urb *bulk_read_urb;
|
||||
int buffer_size;
|
||||
u8 bulk_address;
|
||||
spinlock_t lock; /* protects the following flags */
|
||||
bool throttled;
|
||||
bool actually_throttled;
|
||||
bool rts;
|
||||
};
|
||||
|
||||
static void opticon_bulk_callback(struct urb *urb)
|
||||
{
|
||||
struct opticon_private *priv = urb->context;
|
||||
unsigned char *data = urb->transfer_buffer;
|
||||
struct usb_serial_port *port = priv->port;
|
||||
int status = urb->status;
|
||||
struct tty_struct *tty;
|
||||
int result;
|
||||
int available_room = 0;
|
||||
int data_length;
|
||||
|
||||
dbg("%s - port %d", __func__, port->number);
|
||||
|
||||
switch (status) {
|
||||
case 0:
|
||||
/* success */
|
||||
break;
|
||||
case -ECONNRESET:
|
||||
case -ENOENT:
|
||||
case -ESHUTDOWN:
|
||||
/* this urb is terminated, clean up */
|
||||
dbg("%s - urb shutting down with status: %d",
|
||||
__func__, status);
|
||||
return;
|
||||
default:
|
||||
dbg("%s - nonzero urb status received: %d",
|
||||
__func__, status);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
usb_serial_debug_data(debug, &port->dev, __func__, urb->actual_length,
|
||||
data);
|
||||
|
||||
if (urb->actual_length > 2) {
|
||||
data_length = urb->actual_length - 2;
|
||||
|
||||
/*
|
||||
* Data from the device comes with a 2 byte header:
|
||||
*
|
||||
* <0x00><0x00>data...
|
||||
* This is real data to be sent to the tty layer
|
||||
* <0x00><0x01)level
|
||||
* This is a RTS level change, the third byte is the RTS
|
||||
* value (0 for low, 1 for high).
|
||||
*/
|
||||
if ((data[0] == 0x00) && (data[1] == 0x00)) {
|
||||
/* real data, send it to the tty layer */
|
||||
tty = tty_port_tty_get(&port->port);
|
||||
if (tty) {
|
||||
available_room = tty_buffer_request_room(tty,
|
||||
data_length);
|
||||
if (available_room) {
|
||||
tty_insert_flip_string(tty, data,
|
||||
available_room);
|
||||
tty_flip_buffer_push(tty);
|
||||
}
|
||||
tty_kref_put(tty);
|
||||
}
|
||||
} else {
|
||||
if ((data[0] == 0x00) && (data[1] == 0x01)) {
|
||||
if (data[2] == 0x00)
|
||||
priv->rts = false;
|
||||
else
|
||||
priv->rts = true;
|
||||
/* FIXME change the RTS level */
|
||||
} else {
|
||||
dev_dbg(&priv->udev->dev,
|
||||
"Unknown data packet received from the device:"
|
||||
" %2x %2x\n",
|
||||
data[0], data[1]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
dev_dbg(&priv->udev->dev,
|
||||
"Improper ammount of data received from the device, "
|
||||
"%d bytes", urb->actual_length);
|
||||
}
|
||||
|
||||
exit:
|
||||
spin_lock(&priv->lock);
|
||||
|
||||
/* Continue trying to always read if we should */
|
||||
if (!priv->throttled) {
|
||||
usb_fill_bulk_urb(priv->bulk_read_urb, priv->udev,
|
||||
usb_rcvbulkpipe(priv->udev,
|
||||
priv->bulk_address),
|
||||
priv->bulk_in_buffer, priv->buffer_size,
|
||||
opticon_bulk_callback, priv);
|
||||
result = usb_submit_urb(port->read_urb, GFP_ATOMIC);
|
||||
if (result)
|
||||
dev_err(&port->dev,
|
||||
"%s - failed resubmitting read urb, error %d\n",
|
||||
__func__, result);
|
||||
} else
|
||||
priv->actually_throttled = true;
|
||||
spin_unlock(&priv->lock);
|
||||
}
|
||||
|
||||
static int opticon_open(struct tty_struct *tty, struct usb_serial_port *port,
|
||||
struct file *filp)
|
||||
{
|
||||
struct opticon_private *priv = usb_get_serial_data(port->serial);
|
||||
unsigned long flags;
|
||||
int result = 0;
|
||||
|
||||
dbg("%s - port %d", __func__, port->number);
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
priv->throttled = false;
|
||||
priv->actually_throttled = false;
|
||||
priv->port = port;
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
/*
|
||||
* Force low_latency on so that our tty_push actually forces the data
|
||||
* through, otherwise it is scheduled, and with high data rates (like
|
||||
* with OHCI) data can get lost.
|
||||
*/
|
||||
if (tty)
|
||||
tty->low_latency = 1;
|
||||
|
||||
/* Start reading from the device */
|
||||
usb_fill_bulk_urb(priv->bulk_read_urb, priv->udev,
|
||||
usb_rcvbulkpipe(priv->udev,
|
||||
priv->bulk_address),
|
||||
priv->bulk_in_buffer, priv->buffer_size,
|
||||
opticon_bulk_callback, priv);
|
||||
result = usb_submit_urb(priv->bulk_read_urb, GFP_KERNEL);
|
||||
if (result)
|
||||
dev_err(&port->dev,
|
||||
"%s - failed resubmitting read urb, error %d\n",
|
||||
__func__, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void opticon_close(struct tty_struct *tty, struct usb_serial_port *port,
|
||||
struct file *filp)
|
||||
{
|
||||
struct opticon_private *priv = usb_get_serial_data(port->serial);
|
||||
|
||||
dbg("%s - port %d", __func__, port->number);
|
||||
|
||||
/* shutdown our urbs */
|
||||
usb_kill_urb(priv->bulk_read_urb);
|
||||
}
|
||||
|
||||
static void opticon_throttle(struct tty_struct *tty)
|
||||
{
|
||||
struct usb_serial_port *port = tty->driver_data;
|
||||
struct opticon_private *priv = usb_get_serial_data(port->serial);
|
||||
unsigned long flags;
|
||||
|
||||
dbg("%s - port %d", __func__, port->number);
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
priv->throttled = true;
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
}
|
||||
|
||||
|
||||
static void opticon_unthrottle(struct tty_struct *tty)
|
||||
{
|
||||
struct usb_serial_port *port = tty->driver_data;
|
||||
struct opticon_private *priv = usb_get_serial_data(port->serial);
|
||||
unsigned long flags;
|
||||
int result;
|
||||
|
||||
dbg("%s - port %d", __func__, port->number);
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
priv->throttled = false;
|
||||
priv->actually_throttled = false;
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
priv->bulk_read_urb->dev = port->serial->dev;
|
||||
result = usb_submit_urb(priv->bulk_read_urb, GFP_ATOMIC);
|
||||
if (result)
|
||||
dev_err(&port->dev,
|
||||
"%s - failed submitting read urb, error %d\n",
|
||||
__func__, result);
|
||||
}
|
||||
|
||||
static int opticon_startup(struct usb_serial *serial)
|
||||
{
|
||||
struct opticon_private *priv;
|
||||
struct usb_host_interface *intf;
|
||||
int i;
|
||||
int retval = -ENOMEM;
|
||||
bool bulk_in_found = false;
|
||||
|
||||
/* create our private serial structure */
|
||||
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
|
||||
if (priv == NULL) {
|
||||
dev_err(&serial->dev->dev, "%s - Out of memory\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
spin_lock_init(&priv->lock);
|
||||
priv->serial = serial;
|
||||
priv->port = serial->port[0];
|
||||
priv->udev = serial->dev;
|
||||
|
||||
/* find our bulk endpoint */
|
||||
intf = serial->interface->altsetting;
|
||||
for (i = 0; i < intf->desc.bNumEndpoints; ++i) {
|
||||
struct usb_endpoint_descriptor *endpoint;
|
||||
|
||||
endpoint = &intf->endpoint[i].desc;
|
||||
if (!usb_endpoint_is_bulk_in(endpoint))
|
||||
continue;
|
||||
|
||||
priv->bulk_read_urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!priv->bulk_read_urb) {
|
||||
dev_err(&priv->udev->dev, "out of memory\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
priv->buffer_size = le16_to_cpu(endpoint->wMaxPacketSize) * 2;
|
||||
priv->bulk_in_buffer = kmalloc(priv->buffer_size, GFP_KERNEL);
|
||||
if (!priv->bulk_in_buffer) {
|
||||
dev_err(&priv->udev->dev, "out of memory\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
priv->bulk_address = endpoint->bEndpointAddress;
|
||||
|
||||
/* set up our bulk urb */
|
||||
usb_fill_bulk_urb(priv->bulk_read_urb, priv->udev,
|
||||
usb_rcvbulkpipe(priv->udev,
|
||||
endpoint->bEndpointAddress),
|
||||
priv->bulk_in_buffer, priv->buffer_size,
|
||||
opticon_bulk_callback, priv);
|
||||
|
||||
bulk_in_found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!bulk_in_found) {
|
||||
dev_err(&priv->udev->dev,
|
||||
"Error - the proper endpoints were not found!\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
usb_set_serial_data(serial, priv);
|
||||
return 0;
|
||||
|
||||
error:
|
||||
usb_free_urb(priv->bulk_read_urb);
|
||||
kfree(priv->bulk_in_buffer);
|
||||
kfree(priv);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void opticon_shutdown(struct usb_serial *serial)
|
||||
{
|
||||
struct opticon_private *priv = usb_get_serial_data(serial);
|
||||
|
||||
dbg("%s", __func__);
|
||||
|
||||
usb_kill_urb(priv->bulk_read_urb);
|
||||
usb_free_urb(priv->bulk_read_urb);
|
||||
kfree(priv->bulk_in_buffer);
|
||||
kfree(priv);
|
||||
usb_set_serial_data(serial, NULL);
|
||||
}
|
||||
|
||||
|
||||
static struct usb_driver opticon_driver = {
|
||||
.name = "opticon",
|
||||
.probe = usb_serial_probe,
|
||||
.disconnect = usb_serial_disconnect,
|
||||
.id_table = id_table,
|
||||
.no_dynamic_id = 1,
|
||||
};
|
||||
|
||||
static struct usb_serial_driver opticon_device = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "opticon",
|
||||
},
|
||||
.id_table = id_table,
|
||||
.usb_driver = &opticon_driver,
|
||||
.num_ports = 1,
|
||||
.attach = opticon_startup,
|
||||
.open = opticon_open,
|
||||
.close = opticon_close,
|
||||
.shutdown = opticon_shutdown,
|
||||
.throttle = opticon_throttle,
|
||||
.unthrottle = opticon_unthrottle,
|
||||
};
|
||||
|
||||
static int __init opticon_init(void)
|
||||
{
|
||||
int retval;
|
||||
|
||||
retval = usb_serial_register(&opticon_device);
|
||||
if (retval)
|
||||
return retval;
|
||||
retval = usb_register(&opticon_driver);
|
||||
if (retval)
|
||||
usb_serial_deregister(&opticon_device);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void __exit opticon_exit(void)
|
||||
{
|
||||
usb_deregister(&opticon_driver);
|
||||
usb_serial_deregister(&opticon_device);
|
||||
}
|
||||
|
||||
module_init(opticon_init);
|
||||
module_exit(opticon_exit);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
module_param(debug, bool, S_IRUGO | S_IWUSR);
|
||||
MODULE_PARM_DESC(debug, "Debug enabled or not");
|
||||
|
|
@ -522,9 +522,9 @@ static int debug;
|
|||
/* per port private data */
|
||||
|
||||
#define N_IN_URB 4
|
||||
#define N_OUT_URB 1
|
||||
#define N_OUT_URB 4
|
||||
#define IN_BUFLEN 4096
|
||||
#define OUT_BUFLEN 128
|
||||
#define OUT_BUFLEN 4096
|
||||
|
||||
struct option_port_private {
|
||||
/* Input endpoints and buffer for this port */
|
||||
|
|
@ -654,10 +654,6 @@ static int option_write(struct tty_struct *tty, struct usb_serial_port *port,
|
|||
usb_unlink_urb(this_urb);
|
||||
continue;
|
||||
}
|
||||
if (this_urb->status != 0)
|
||||
dbg("usb_write %p failed (err=%d)",
|
||||
this_urb, this_urb->status);
|
||||
|
||||
dbg("%s: endpoint %d buf %d", __func__,
|
||||
usb_pipeendpoint(this_urb->pipe), i);
|
||||
|
||||
|
|
@ -669,8 +665,7 @@ static int option_write(struct tty_struct *tty, struct usb_serial_port *port,
|
|||
err = usb_submit_urb(this_urb, GFP_ATOMIC);
|
||||
if (err) {
|
||||
dbg("usb_submit_urb %p (write bulk) failed "
|
||||
"(%d, has %d)", this_urb,
|
||||
err, this_urb->status);
|
||||
"(%d)", this_urb, err);
|
||||
clear_bit(i, &portdata->out_busy);
|
||||
continue;
|
||||
}
|
||||
|
|
|
|||
77
drivers/usb/serial/siemens_mpi.c
Normal file
77
drivers/usb/serial/siemens_mpi.c
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Siemens USB-MPI Serial USB driver
|
||||
*
|
||||
* Copyright (C) 2005 Thomas Hergenhahn <thomas.hergenhahn@suse.de>
|
||||
* Copyright (C) 2005,2008 Greg Kroah-Hartman <gregkh@suse.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version
|
||||
* 2 as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/serial.h>
|
||||
|
||||
/* Version Information */
|
||||
#define DRIVER_VERSION "Version 0.1 09/26/2005"
|
||||
#define DRIVER_AUTHOR "Thomas [email protected] http://libnodave.sf.net"
|
||||
#define DRIVER_DESC "Driver for Siemens USB/MPI adapter"
|
||||
|
||||
|
||||
static struct usb_device_id id_table[] = {
|
||||
/* Vendor and product id for 6ES7-972-0CB20-0XA0 */
|
||||
{ USB_DEVICE(0x908, 0x0004) },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(usb, id_table);
|
||||
|
||||
static struct usb_driver siemens_usb_mpi_driver = {
|
||||
.name = "siemens_mpi",
|
||||
.probe = usb_serial_probe,
|
||||
.disconnect = usb_serial_disconnect,
|
||||
.id_table = id_table,
|
||||
};
|
||||
|
||||
static struct usb_serial_driver siemens_usb_mpi_device = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "siemens_mpi",
|
||||
},
|
||||
.id_table = id_table,
|
||||
.num_ports = 1,
|
||||
};
|
||||
|
||||
static int __init siemens_usb_mpi_init(void)
|
||||
{
|
||||
int retval;
|
||||
|
||||
retval = usb_serial_register(&siemens_usb_mpi_device);
|
||||
if (retval)
|
||||
goto failed_usb_serial_register;
|
||||
retval = usb_register(&siemens_usb_mpi_driver);
|
||||
if (retval)
|
||||
goto failed_usb_register;
|
||||
printk(KERN_INFO DRIVER_DESC "\n");
|
||||
printk(KERN_INFO DRIVER_VERSION " " DRIVER_AUTHOR "\n");
|
||||
return retval;
|
||||
failed_usb_register:
|
||||
usb_serial_deregister(&siemens_usb_mpi_device);
|
||||
failed_usb_serial_register:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void __exit siemens_usb_mpi_exit(void)
|
||||
{
|
||||
usb_deregister(&siemens_usb_mpi_driver);
|
||||
usb_serial_deregister(&siemens_usb_mpi_device);
|
||||
}
|
||||
|
||||
module_init(siemens_usb_mpi_init);
|
||||
module_exit(siemens_usb_mpi_exit);
|
||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
@ -709,21 +709,20 @@ static void spcp8x5_read_bulk_callback(struct urb *urb)
|
|||
unsigned char *data = urb->transfer_buffer;
|
||||
unsigned long flags;
|
||||
int i;
|
||||
int result;
|
||||
u8 status = 0;
|
||||
int result = urb->status;
|
||||
u8 status;
|
||||
char tty_flag;
|
||||
|
||||
dev_dbg(&port->dev, "start, urb->status = %d, "
|
||||
"urb->actual_length = %d\n,", urb->status, urb->actual_length);
|
||||
dev_dbg(&port->dev, "start, result = %d, urb->actual_length = %d\n,",
|
||||
result, urb->actual_length);
|
||||
|
||||
/* check the urb status */
|
||||
if (urb->status) {
|
||||
if (result) {
|
||||
if (!port->port.count)
|
||||
return;
|
||||
if (urb->status == -EPROTO) {
|
||||
if (result == -EPROTO) {
|
||||
/* spcp8x5 mysteriously fails with -EPROTO */
|
||||
/* reschedule the read */
|
||||
urb->status = 0;
|
||||
urb->dev = port->serial->dev;
|
||||
result = usb_submit_urb(urb , GFP_ATOMIC);
|
||||
if (result)
|
||||
|
|
@ -833,8 +832,9 @@ static void spcp8x5_write_bulk_callback(struct urb *urb)
|
|||
struct usb_serial_port *port = urb->context;
|
||||
struct spcp8x5_private *priv = usb_get_serial_port_data(port);
|
||||
int result;
|
||||
int status = urb->status;
|
||||
|
||||
switch (urb->status) {
|
||||
switch (status) {
|
||||
case 0:
|
||||
/* success */
|
||||
break;
|
||||
|
|
@ -843,14 +843,14 @@ static void spcp8x5_write_bulk_callback(struct urb *urb)
|
|||
case -ESHUTDOWN:
|
||||
/* this urb is terminated, clean up */
|
||||
dev_dbg(&port->dev, "urb shutting down with status: %d\n",
|
||||
urb->status);
|
||||
status);
|
||||
priv->write_urb_in_use = 0;
|
||||
return;
|
||||
default:
|
||||
/* error in the urb, so we have to resubmit it */
|
||||
dbg("%s - Overflow in write", __func__);
|
||||
dbg("%s - nonzero write bulk status received: %d",
|
||||
__func__, urb->status);
|
||||
__func__, status);
|
||||
port->write_urb->transfer_buffer_length = 1;
|
||||
port->write_urb->dev = port->serial->dev;
|
||||
result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ static struct usb_driver debug_driver = {
|
|||
.no_dynamic_id = 1,
|
||||
};
|
||||
|
||||
int usb_debug_open(struct tty_struct *tty, struct usb_serial_port *port,
|
||||
static int usb_debug_open(struct tty_struct *tty, struct usb_serial_port *port,
|
||||
struct file *filp)
|
||||
{
|
||||
port->bulk_out_size = USB_DEBUG_MAX_PACKET_SIZE;
|
||||
|
|
|
|||
|
|
@ -61,13 +61,6 @@ config USB_STORAGE_ISD200
|
|||
- CyQ've CQ8060A CDRW drive
|
||||
- Planex eXtreme Drive RX-25HU USB-IDE cable (not model RX-25U)
|
||||
|
||||
config USB_STORAGE_DPCM
|
||||
bool "Microtech/ZiO! CompactFlash/SmartMedia support"
|
||||
depends on USB_STORAGE
|
||||
help
|
||||
Say Y here to support the Microtech/ZiO! CompactFlash reader.
|
||||
There is a web page at <http://www.ziocorp.com/products/>.
|
||||
|
||||
config USB_STORAGE_USBAT
|
||||
bool "USBAT/USBAT02-based storage support"
|
||||
depends on USB_STORAGE
|
||||
|
|
@ -90,12 +83,12 @@ config USB_STORAGE_USBAT
|
|||
- Sandisk ImageMate SDDR-05b
|
||||
|
||||
config USB_STORAGE_SDDR09
|
||||
bool "SanDisk SDDR-09 (and other SmartMedia) support"
|
||||
bool "SanDisk SDDR-09 (and other SmartMedia, including DPCM) support"
|
||||
depends on USB_STORAGE
|
||||
help
|
||||
Say Y here to include additional code to support the Sandisk SDDR-09
|
||||
SmartMedia reader in the USB Mass Storage driver.
|
||||
Also works for the Microtech Zio! SmartMedia reader.
|
||||
Also works for the Microtech Zio! CompactFlash/SmartMedia reader.
|
||||
|
||||
config USB_STORAGE_SDDR55
|
||||
bool "SanDisk SDDR-55 SmartMedia support"
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ usb-storage-obj-$(CONFIG_USB_STORAGE_USBAT) += shuttle_usbat.o
|
|||
usb-storage-obj-$(CONFIG_USB_STORAGE_SDDR09) += sddr09.o
|
||||
usb-storage-obj-$(CONFIG_USB_STORAGE_SDDR55) += sddr55.o
|
||||
usb-storage-obj-$(CONFIG_USB_STORAGE_FREECOM) += freecom.o
|
||||
usb-storage-obj-$(CONFIG_USB_STORAGE_DPCM) += dpcm.o
|
||||
usb-storage-obj-$(CONFIG_USB_STORAGE_ISD200) += isd200.o
|
||||
usb-storage-obj-$(CONFIG_USB_STORAGE_DATAFAB) += datafab.o
|
||||
usb-storage-obj-$(CONFIG_USB_STORAGE_JUMPSHOT) += jumpshot.o
|
||||
|
|
@ -24,7 +23,7 @@ usb-storage-obj-$(CONFIG_USB_STORAGE_KARMA) += karma.o
|
|||
usb-storage-obj-$(CONFIG_USB_STORAGE_CYPRESS_ATACB) += cypress_atacb.o
|
||||
|
||||
usb-storage-objs := scsiglue.o protocol.o transport.o usb.o \
|
||||
initializers.o sierra_ms.o $(usb-storage-obj-y)
|
||||
initializers.o sierra_ms.o option_ms.o $(usb-storage-obj-y)
|
||||
|
||||
ifneq ($(CONFIG_USB_LIBUSUAL),)
|
||||
obj-$(CONFIG_USB) += libusual.o
|
||||
|
|
|
|||
|
|
@ -1,86 +0,0 @@
|
|||
/* Driver for Microtech DPCM-USB CompactFlash/SmartMedia reader
|
||||
*
|
||||
* DPCM driver v0.1:
|
||||
*
|
||||
* First release
|
||||
*
|
||||
* Current development and maintenance by:
|
||||
* (c) 2000 Brian Webb (webbb@earthlink.net)
|
||||
*
|
||||
* This device contains both a CompactFlash card reader, which
|
||||
* uses the Control/Bulk w/o Interrupt protocol and
|
||||
* a SmartMedia card reader that uses the same protocol
|
||||
* as the SDDR09.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <scsi/scsi.h>
|
||||
#include <scsi/scsi_cmnd.h>
|
||||
#include <scsi/scsi_device.h>
|
||||
|
||||
#include "usb.h"
|
||||
#include "transport.h"
|
||||
#include "protocol.h"
|
||||
#include "debug.h"
|
||||
#include "dpcm.h"
|
||||
#include "sddr09.h"
|
||||
|
||||
/*
|
||||
* Transport for the Microtech DPCM-USB
|
||||
*
|
||||
*/
|
||||
int dpcm_transport(struct scsi_cmnd *srb, struct us_data *us)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (srb == NULL)
|
||||
return USB_STOR_TRANSPORT_ERROR;
|
||||
|
||||
US_DEBUGP("dpcm_transport: LUN=%d\n", srb->device->lun);
|
||||
|
||||
switch (srb->device->lun) {
|
||||
case 0:
|
||||
|
||||
/*
|
||||
* LUN 0 corresponds to the CompactFlash card reader.
|
||||
*/
|
||||
ret = usb_stor_CB_transport(srb, us);
|
||||
break;
|
||||
|
||||
#ifdef CONFIG_USB_STORAGE_SDDR09
|
||||
case 1:
|
||||
|
||||
/*
|
||||
* LUN 1 corresponds to the SmartMedia card reader.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Set the LUN to 0 (just in case).
|
||||
*/
|
||||
srb->device->lun = 0; us->srb->device->lun = 0;
|
||||
ret = sddr09_transport(srb, us);
|
||||
srb->device->lun = 1; us->srb->device->lun = 1;
|
||||
break;
|
||||
|
||||
#endif
|
||||
|
||||
default:
|
||||
US_DEBUGP("dpcm_transport: Invalid LUN %d\n", srb->device->lun);
|
||||
ret = USB_STOR_TRANSPORT_ERROR;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
/* Driver for Microtech DPCM-USB CompactFlash/SmartMedia reader
|
||||
*
|
||||
* DPCM driver v0.1:
|
||||
*
|
||||
* First release
|
||||
*
|
||||
* Current development and maintenance by:
|
||||
* (c) 2000 Brian Webb (webbb@earthlink.net)
|
||||
*
|
||||
* See dpcm.c for more explanation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef _MICROTECH_DPCM_USB_H
|
||||
#define _MICROTECH_DPCM_USB_H
|
||||
|
||||
extern int dpcm_transport(struct scsi_cmnd *srb, struct us_data *us);
|
||||
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue