Merge branch 'usb-next' into musb-merge
* usb-next: (132 commits) USB: uas: Use GFP_NOIO instead of GFP_KERNEL in I/O submission path USB: uas: Ensure we only bind to a UAS interface USB: uas: Rename sense pipe and sense urb to status pipe and status urb USB: uas: Use kzalloc instead of kmalloc USB: uas: Fix up the Sense IU usb: musb: core: kill unneeded #include's DA8xx: assign name to MUSB IRQ resource usb: gadget: g_ncm added usb: gadget: f_ncm.c added usb: gadget: u_ether: prepare for NCM usb: pch_udc: Fix setup transfers with data out usb: pch_udc: Fix compile error, warnings and checkpatch warnings usb: add ab8500 usb transceiver driver USB: gadget: Implement runtime PM for MSM bus glue driver USB: gadget: Implement runtime PM for ci13xxx gadget USB: gadget: Add USB controller driver for MSM SoC USB: gadget: Introduce ci13xxx_udc_driver struct USB: gadget: Initialize ci13xxx gadget device's coherent DMA mask USB: gadget: Fix "scheduling while atomic" bugs in ci13xxx_udc USB: gadget: Separate out PCI bus code from ci13xxx_udc ...
This commit is contained in:
@@ -41,17 +41,13 @@ config USB_ARCH_HAS_OHCI
|
||||
default y if MFD_TC6393XB
|
||||
default y if ARCH_W90X900
|
||||
default y if ARCH_DAVINCI_DA8XX
|
||||
default y if PLAT_SPEAR
|
||||
# PPC:
|
||||
default y if STB03xxx
|
||||
default y if PPC_MPC52xx
|
||||
# MIPS:
|
||||
default y if MIPS_ALCHEMY
|
||||
default y if MACH_JZ4740
|
||||
# SH:
|
||||
default y if CPU_SUBTYPE_SH7720
|
||||
default y if CPU_SUBTYPE_SH7721
|
||||
default y if CPU_SUBTYPE_SH7763
|
||||
default y if CPU_SUBTYPE_SH7786
|
||||
# more:
|
||||
default PCI
|
||||
|
||||
@@ -66,6 +62,9 @@ config USB_ARCH_HAS_EHCI
|
||||
default y if ARCH_AT91SAM9G45
|
||||
default y if ARCH_MXC
|
||||
default y if ARCH_OMAP3
|
||||
default y if ARCH_VT8500
|
||||
default y if PLAT_SPEAR
|
||||
default y if ARCH_MSM
|
||||
default PCI
|
||||
|
||||
# ARM SA1111 chips have a non-PCI based "OHCI-compatible" USB host interface.
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/quirks.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include "usb.h"
|
||||
|
||||
@@ -1262,6 +1261,7 @@ static int usb_resume_both(struct usb_device *udev, pm_message_t msg)
|
||||
udev->reset_resume);
|
||||
}
|
||||
}
|
||||
usb_mark_last_busy(udev);
|
||||
|
||||
done:
|
||||
dev_vdbg(&udev->dev, "%s: status %d\n", __func__, status);
|
||||
@@ -1329,7 +1329,6 @@ int usb_resume(struct device *dev, pm_message_t msg)
|
||||
pm_runtime_disable(dev);
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_enable(dev);
|
||||
udev->last_busy = jiffies;
|
||||
do_unbind_rebind(udev, DO_REBIND);
|
||||
}
|
||||
}
|
||||
@@ -1397,33 +1396,8 @@ void usb_autosuspend_device(struct usb_device *udev)
|
||||
{
|
||||
int status;
|
||||
|
||||
udev->last_busy = jiffies;
|
||||
status = pm_runtime_put_sync(&udev->dev);
|
||||
dev_vdbg(&udev->dev, "%s: cnt %d -> %d\n",
|
||||
__func__, atomic_read(&udev->dev.power.usage_count),
|
||||
status);
|
||||
}
|
||||
|
||||
/**
|
||||
* usb_try_autosuspend_device - attempt an autosuspend of a USB device and its interfaces
|
||||
* @udev: the usb_device to autosuspend
|
||||
*
|
||||
* This routine should be called when a core subsystem thinks @udev may
|
||||
* be ready to autosuspend.
|
||||
*
|
||||
* @udev's usage counter left unchanged. If it is 0 and all the interfaces
|
||||
* are inactive then an autosuspend will be attempted. The attempt may
|
||||
* fail or be delayed.
|
||||
*
|
||||
* The caller must hold @udev's device lock.
|
||||
*
|
||||
* This routine can run only in process context.
|
||||
*/
|
||||
void usb_try_autosuspend_device(struct usb_device *udev)
|
||||
{
|
||||
int status;
|
||||
|
||||
status = pm_runtime_idle(&udev->dev);
|
||||
usb_mark_last_busy(udev);
|
||||
status = pm_runtime_put_sync_autosuspend(&udev->dev);
|
||||
dev_vdbg(&udev->dev, "%s: cnt %d -> %d\n",
|
||||
__func__, atomic_read(&udev->dev.power.usage_count),
|
||||
status);
|
||||
@@ -1482,7 +1456,7 @@ void usb_autopm_put_interface(struct usb_interface *intf)
|
||||
struct usb_device *udev = interface_to_usbdev(intf);
|
||||
int status;
|
||||
|
||||
udev->last_busy = jiffies;
|
||||
usb_mark_last_busy(udev);
|
||||
atomic_dec(&intf->pm_usage_cnt);
|
||||
status = pm_runtime_put_sync(&intf->dev);
|
||||
dev_vdbg(&intf->dev, "%s: cnt %d -> %d\n",
|
||||
@@ -1509,32 +1483,11 @@ EXPORT_SYMBOL_GPL(usb_autopm_put_interface);
|
||||
void usb_autopm_put_interface_async(struct usb_interface *intf)
|
||||
{
|
||||
struct usb_device *udev = interface_to_usbdev(intf);
|
||||
unsigned long last_busy;
|
||||
int status = 0;
|
||||
int status;
|
||||
|
||||
last_busy = udev->last_busy;
|
||||
udev->last_busy = jiffies;
|
||||
usb_mark_last_busy(udev);
|
||||
atomic_dec(&intf->pm_usage_cnt);
|
||||
pm_runtime_put_noidle(&intf->dev);
|
||||
|
||||
if (udev->dev.power.runtime_auto) {
|
||||
/* Optimization: Don't schedule a delayed autosuspend if
|
||||
* the timer is already running and the expiration time
|
||||
* wouldn't change.
|
||||
*
|
||||
* We have to use the interface's timer. Attempts to
|
||||
* schedule a suspend for the device would fail because
|
||||
* the interface is still active.
|
||||
*/
|
||||
if (intf->dev.power.timer_expires == 0 ||
|
||||
round_jiffies_up(last_busy) !=
|
||||
round_jiffies_up(jiffies)) {
|
||||
status = pm_schedule_suspend(&intf->dev,
|
||||
jiffies_to_msecs(
|
||||
round_jiffies_up_relative(
|
||||
udev->autosuspend_delay)));
|
||||
}
|
||||
}
|
||||
status = pm_runtime_put(&intf->dev);
|
||||
dev_vdbg(&intf->dev, "%s: cnt %d -> %d\n",
|
||||
__func__, atomic_read(&intf->dev.power.usage_count),
|
||||
status);
|
||||
@@ -1554,7 +1507,7 @@ void usb_autopm_put_interface_no_suspend(struct usb_interface *intf)
|
||||
{
|
||||
struct usb_device *udev = interface_to_usbdev(intf);
|
||||
|
||||
udev->last_busy = jiffies;
|
||||
usb_mark_last_busy(udev);
|
||||
atomic_dec(&intf->pm_usage_cnt);
|
||||
pm_runtime_put_noidle(&intf->dev);
|
||||
}
|
||||
@@ -1612,18 +1565,9 @@ EXPORT_SYMBOL_GPL(usb_autopm_get_interface);
|
||||
*/
|
||||
int usb_autopm_get_interface_async(struct usb_interface *intf)
|
||||
{
|
||||
int status = 0;
|
||||
enum rpm_status s;
|
||||
|
||||
/* Don't request a resume unless the interface is already suspending
|
||||
* or suspended. Doing so would force a running suspend timer to be
|
||||
* cancelled.
|
||||
*/
|
||||
pm_runtime_get_noresume(&intf->dev);
|
||||
s = ACCESS_ONCE(intf->dev.power.runtime_status);
|
||||
if (s == RPM_SUSPENDING || s == RPM_SUSPENDED)
|
||||
status = pm_request_resume(&intf->dev);
|
||||
int status;
|
||||
|
||||
status = pm_runtime_get(&intf->dev);
|
||||
if (status < 0 && status != -EINPROGRESS)
|
||||
pm_runtime_put_noidle(&intf->dev);
|
||||
else
|
||||
@@ -1650,7 +1594,7 @@ void usb_autopm_get_interface_no_resume(struct usb_interface *intf)
|
||||
{
|
||||
struct usb_device *udev = interface_to_usbdev(intf);
|
||||
|
||||
udev->last_busy = jiffies;
|
||||
usb_mark_last_busy(udev);
|
||||
atomic_inc(&intf->pm_usage_cnt);
|
||||
pm_runtime_get_noresume(&intf->dev);
|
||||
}
|
||||
@@ -1661,7 +1605,6 @@ static int autosuspend_check(struct usb_device *udev)
|
||||
{
|
||||
int w, i;
|
||||
struct usb_interface *intf;
|
||||
unsigned long suspend_time, j;
|
||||
|
||||
/* Fail if autosuspend is disabled, or any interfaces are in use, or
|
||||
* any interface drivers require remote wakeup but it isn't available.
|
||||
@@ -1701,87 +1644,46 @@ static int autosuspend_check(struct usb_device *udev)
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
udev->do_remote_wakeup = w;
|
||||
|
||||
/* If everything is okay but the device hasn't been idle for long
|
||||
* enough, queue a delayed autosuspend request.
|
||||
*/
|
||||
j = ACCESS_ONCE(jiffies);
|
||||
suspend_time = udev->last_busy + udev->autosuspend_delay;
|
||||
if (time_before(j, suspend_time)) {
|
||||
pm_schedule_suspend(&udev->dev, jiffies_to_msecs(
|
||||
round_jiffies_up_relative(suspend_time - j)));
|
||||
return -EAGAIN;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usb_runtime_suspend(struct device *dev)
|
||||
{
|
||||
int status = 0;
|
||||
struct usb_device *udev = to_usb_device(dev);
|
||||
int status;
|
||||
|
||||
/* A USB device can be suspended if it passes the various autosuspend
|
||||
* checks. Runtime suspend for a USB device means suspending all the
|
||||
* interfaces and then the device itself.
|
||||
*/
|
||||
if (is_usb_device(dev)) {
|
||||
struct usb_device *udev = to_usb_device(dev);
|
||||
if (autosuspend_check(udev) != 0)
|
||||
return -EAGAIN;
|
||||
|
||||
if (autosuspend_check(udev) != 0)
|
||||
return -EAGAIN;
|
||||
|
||||
status = usb_suspend_both(udev, PMSG_AUTO_SUSPEND);
|
||||
|
||||
/* If an interface fails the suspend, adjust the last_busy
|
||||
* time so that we don't get another suspend attempt right
|
||||
* away.
|
||||
*/
|
||||
if (status) {
|
||||
udev->last_busy = jiffies +
|
||||
(udev->autosuspend_delay == 0 ?
|
||||
HZ/2 : 0);
|
||||
}
|
||||
|
||||
/* Prevent the parent from suspending immediately after */
|
||||
else if (udev->parent)
|
||||
udev->parent->last_busy = jiffies;
|
||||
}
|
||||
|
||||
/* Runtime suspend for a USB interface doesn't mean anything. */
|
||||
status = usb_suspend_both(udev, PMSG_AUTO_SUSPEND);
|
||||
return status;
|
||||
}
|
||||
|
||||
static int usb_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct usb_device *udev = to_usb_device(dev);
|
||||
int status;
|
||||
|
||||
/* Runtime resume for a USB device means resuming both the device
|
||||
* and all its interfaces.
|
||||
*/
|
||||
if (is_usb_device(dev)) {
|
||||
struct usb_device *udev = to_usb_device(dev);
|
||||
int status;
|
||||
|
||||
status = usb_resume_both(udev, PMSG_AUTO_RESUME);
|
||||
udev->last_busy = jiffies;
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Runtime resume for a USB interface doesn't mean anything. */
|
||||
return 0;
|
||||
status = usb_resume_both(udev, PMSG_AUTO_RESUME);
|
||||
return status;
|
||||
}
|
||||
|
||||
static int usb_runtime_idle(struct device *dev)
|
||||
{
|
||||
struct usb_device *udev = to_usb_device(dev);
|
||||
|
||||
/* An idle USB device can be suspended if it passes the various
|
||||
* autosuspend checks. An idle interface can be suspended at
|
||||
* any time.
|
||||
* autosuspend checks.
|
||||
*/
|
||||
if (is_usb_device(dev)) {
|
||||
struct usb_device *udev = to_usb_device(dev);
|
||||
|
||||
if (autosuspend_check(udev) != 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
pm_runtime_suspend(dev);
|
||||
if (autosuspend_check(udev) == 0)
|
||||
pm_runtime_autosuspend(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
|
||||
|
||||
@@ -38,7 +38,6 @@
|
||||
#include <asm/unaligned.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/freezer.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/byteorder.h>
|
||||
@@ -1804,8 +1803,15 @@ int usb_new_device(struct usb_device *udev)
|
||||
|
||||
/* Tell the runtime-PM framework the device is active */
|
||||
pm_runtime_set_active(&udev->dev);
|
||||
pm_runtime_get_noresume(&udev->dev);
|
||||
pm_runtime_use_autosuspend(&udev->dev);
|
||||
pm_runtime_enable(&udev->dev);
|
||||
|
||||
/* By default, forbid autosuspend for all devices. It will be
|
||||
* allowed for hubs during binding.
|
||||
*/
|
||||
usb_disable_autosuspend(udev);
|
||||
|
||||
err = usb_enumerate_device(udev); /* Read descriptors */
|
||||
if (err < 0)
|
||||
goto fail;
|
||||
@@ -1831,6 +1837,8 @@ int usb_new_device(struct usb_device *udev)
|
||||
}
|
||||
|
||||
(void) usb_create_ep_devs(&udev->dev, &udev->ep0, udev);
|
||||
usb_mark_last_busy(udev);
|
||||
pm_runtime_put_sync_autosuspend(&udev->dev);
|
||||
return err;
|
||||
|
||||
fail:
|
||||
@@ -2221,6 +2229,7 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
|
||||
usb_set_device_state(udev, USB_STATE_SUSPENDED);
|
||||
msleep(10);
|
||||
}
|
||||
usb_mark_last_busy(hub->hdev);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
@@ -1804,6 +1804,7 @@ free_interfaces:
|
||||
INIT_WORK(&intf->reset_ws, __usb_queue_reset_device);
|
||||
intf->minor = -1;
|
||||
device_initialize(&intf->dev);
|
||||
pm_runtime_no_callbacks(&intf->dev);
|
||||
dev_set_name(&intf->dev, "%d-%s:%d.%d",
|
||||
dev->bus->busnum, dev->devpath,
|
||||
configuration, alt->desc.bInterfaceNumber);
|
||||
|
||||
@@ -117,21 +117,6 @@ void usb_detect_quirks(struct usb_device *udev)
|
||||
dev_dbg(&udev->dev, "USB quirks for this device: %x\n",
|
||||
udev->quirks);
|
||||
|
||||
#ifdef CONFIG_USB_SUSPEND
|
||||
|
||||
/* By default, disable autosuspend for all devices. The hub driver
|
||||
* will enable it for hubs.
|
||||
*/
|
||||
usb_disable_autosuspend(udev);
|
||||
|
||||
/* Autosuspend can also be disabled if the initial autosuspend_delay
|
||||
* is negative.
|
||||
*/
|
||||
if (udev->autosuspend_delay < 0)
|
||||
usb_autoresume_device(udev);
|
||||
|
||||
#endif
|
||||
|
||||
/* For the present, all devices default to USB-PERSIST enabled */
|
||||
#if 0 /* was: #ifdef CONFIG_PM */
|
||||
/* Hubs are automatically enabled for USB-PERSIST */
|
||||
|
||||
@@ -233,8 +233,6 @@ static DEVICE_ATTR(urbnum, S_IRUGO, show_urbnum, NULL);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
static const char power_group[] = "power";
|
||||
|
||||
static ssize_t
|
||||
show_persist(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
@@ -278,7 +276,7 @@ static int add_persist_attributes(struct device *dev)
|
||||
if (udev->descriptor.bDeviceClass != USB_CLASS_HUB)
|
||||
rc = sysfs_add_file_to_group(&dev->kobj,
|
||||
&dev_attr_persist.attr,
|
||||
power_group);
|
||||
power_group_name);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@@ -287,7 +285,7 @@ static void remove_persist_attributes(struct device *dev)
|
||||
{
|
||||
sysfs_remove_file_from_group(&dev->kobj,
|
||||
&dev_attr_persist.attr,
|
||||
power_group);
|
||||
power_group_name);
|
||||
}
|
||||
#else
|
||||
|
||||
@@ -336,44 +334,20 @@ static DEVICE_ATTR(active_duration, S_IRUGO, show_active_duration, NULL);
|
||||
static ssize_t
|
||||
show_autosuspend(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct usb_device *udev = to_usb_device(dev);
|
||||
|
||||
return sprintf(buf, "%d\n", udev->autosuspend_delay / HZ);
|
||||
return sprintf(buf, "%d\n", dev->power.autosuspend_delay / 1000);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
set_autosuspend(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct usb_device *udev = to_usb_device(dev);
|
||||
int value, old_delay;
|
||||
int rc;
|
||||
int value;
|
||||
|
||||
if (sscanf(buf, "%d", &value) != 1 || value >= INT_MAX/HZ ||
|
||||
value <= - INT_MAX/HZ)
|
||||
if (sscanf(buf, "%d", &value) != 1 || value >= INT_MAX/1000 ||
|
||||
value <= -INT_MAX/1000)
|
||||
return -EINVAL;
|
||||
value *= HZ;
|
||||
|
||||
usb_lock_device(udev);
|
||||
old_delay = udev->autosuspend_delay;
|
||||
udev->autosuspend_delay = value;
|
||||
|
||||
if (old_delay < 0) { /* Autosuspend wasn't allowed */
|
||||
if (value >= 0)
|
||||
usb_autosuspend_device(udev);
|
||||
} else { /* Autosuspend was allowed */
|
||||
if (value < 0) {
|
||||
rc = usb_autoresume_device(udev);
|
||||
if (rc < 0) {
|
||||
count = rc;
|
||||
udev->autosuspend_delay = old_delay;
|
||||
}
|
||||
} else {
|
||||
usb_try_autosuspend_device(udev);
|
||||
}
|
||||
}
|
||||
|
||||
usb_unlock_device(udev);
|
||||
pm_runtime_set_autosuspend_delay(dev, value * 1000);
|
||||
return count;
|
||||
}
|
||||
|
||||
@@ -438,44 +412,30 @@ set_level(struct device *dev, struct device_attribute *attr,
|
||||
|
||||
static DEVICE_ATTR(level, S_IRUGO | S_IWUSR, show_level, set_level);
|
||||
|
||||
static struct attribute *power_attrs[] = {
|
||||
&dev_attr_autosuspend.attr,
|
||||
&dev_attr_level.attr,
|
||||
&dev_attr_connected_duration.attr,
|
||||
&dev_attr_active_duration.attr,
|
||||
NULL,
|
||||
};
|
||||
static struct attribute_group power_attr_group = {
|
||||
.name = power_group_name,
|
||||
.attrs = power_attrs,
|
||||
};
|
||||
|
||||
static int add_power_attributes(struct device *dev)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if (is_usb_device(dev)) {
|
||||
rc = sysfs_add_file_to_group(&dev->kobj,
|
||||
&dev_attr_autosuspend.attr,
|
||||
power_group);
|
||||
if (rc == 0)
|
||||
rc = sysfs_add_file_to_group(&dev->kobj,
|
||||
&dev_attr_level.attr,
|
||||
power_group);
|
||||
if (rc == 0)
|
||||
rc = sysfs_add_file_to_group(&dev->kobj,
|
||||
&dev_attr_connected_duration.attr,
|
||||
power_group);
|
||||
if (rc == 0)
|
||||
rc = sysfs_add_file_to_group(&dev->kobj,
|
||||
&dev_attr_active_duration.attr,
|
||||
power_group);
|
||||
}
|
||||
if (is_usb_device(dev))
|
||||
rc = sysfs_merge_group(&dev->kobj, &power_attr_group);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void remove_power_attributes(struct device *dev)
|
||||
{
|
||||
sysfs_remove_file_from_group(&dev->kobj,
|
||||
&dev_attr_active_duration.attr,
|
||||
power_group);
|
||||
sysfs_remove_file_from_group(&dev->kobj,
|
||||
&dev_attr_connected_duration.attr,
|
||||
power_group);
|
||||
sysfs_remove_file_from_group(&dev->kobj,
|
||||
&dev_attr_level.attr,
|
||||
power_group);
|
||||
sysfs_remove_file_from_group(&dev->kobj,
|
||||
&dev_attr_autosuspend.attr,
|
||||
power_group);
|
||||
sysfs_unmerge_group(&dev->kobj, &power_attr_group);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
@@ -445,7 +445,8 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
|
||||
INIT_LIST_HEAD(&dev->filelist);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
dev->autosuspend_delay = usb_autosuspend_delay * HZ;
|
||||
pm_runtime_set_autosuspend_delay(&dev->dev,
|
||||
usb_autosuspend_delay * 1000);
|
||||
dev->connect_time = jiffies;
|
||||
dev->active_duration = -jiffies;
|
||||
#endif
|
||||
|
||||
@@ -75,14 +75,12 @@ static inline int usb_port_resume(struct usb_device *udev, pm_message_t msg)
|
||||
#ifdef CONFIG_USB_SUSPEND
|
||||
|
||||
extern void usb_autosuspend_device(struct usb_device *udev);
|
||||
extern void usb_try_autosuspend_device(struct usb_device *udev);
|
||||
extern int usb_autoresume_device(struct usb_device *udev);
|
||||
extern int usb_remote_wakeup(struct usb_device *dev);
|
||||
|
||||
#else
|
||||
|
||||
#define usb_autosuspend_device(udev) do {} while (0)
|
||||
#define usb_try_autosuspend_device(udev) do {} while (0)
|
||||
static inline int usb_autoresume_device(struct usb_device *udev)
|
||||
{
|
||||
return 0;
|
||||
|
||||
@@ -338,6 +338,19 @@ config USB_S3C2410_DEBUG
|
||||
boolean "S3C2410 udc debug messages"
|
||||
depends on USB_GADGET_S3C2410
|
||||
|
||||
config USB_GADGET_PXA_U2O
|
||||
boolean "PXA9xx Processor USB2.0 controller"
|
||||
select USB_GADGET_DUALSPEED
|
||||
help
|
||||
PXA9xx Processor series include a high speed USB2.0 device
|
||||
controller, which support high speed and full speed USB peripheral.
|
||||
|
||||
config USB_PXA_U2O
|
||||
tristate
|
||||
depends on USB_GADGET_PXA_U2O
|
||||
default USB_GADGET
|
||||
select USB_GADGET_SELECTED
|
||||
|
||||
#
|
||||
# Controllers available in both integrated and discrete versions
|
||||
#
|
||||
@@ -414,8 +427,8 @@ config USB_FSL_QE
|
||||
default USB_GADGET
|
||||
select USB_GADGET_SELECTED
|
||||
|
||||
config USB_GADGET_CI13XXX
|
||||
boolean "MIPS USB CI13xxx"
|
||||
config USB_GADGET_CI13XXX_PCI
|
||||
boolean "MIPS USB CI13xxx PCI UDC"
|
||||
depends on PCI
|
||||
select USB_GADGET_DUALSPEED
|
||||
help
|
||||
@@ -426,9 +439,9 @@ config USB_GADGET_CI13XXX
|
||||
dynamically linked module called "ci13xxx_udc" and force all
|
||||
gadget drivers to also be dynamically linked.
|
||||
|
||||
config USB_CI13XXX
|
||||
config USB_CI13XXX_PCI
|
||||
tristate
|
||||
depends on USB_GADGET_CI13XXX
|
||||
depends on USB_GADGET_CI13XXX_PCI
|
||||
default USB_GADGET
|
||||
select USB_GADGET_SELECTED
|
||||
|
||||
@@ -495,6 +508,49 @@ config USB_LANGWELL
|
||||
default USB_GADGET
|
||||
select USB_GADGET_SELECTED
|
||||
|
||||
config USB_GADGET_EG20T
|
||||
boolean "Intel EG20T(Topcliff) USB Device controller"
|
||||
depends on PCI
|
||||
select USB_GADGET_DUALSPEED
|
||||
help
|
||||
This is a USB device driver for EG20T PCH.
|
||||
EG20T PCH is the platform controller hub that is used in Intel's
|
||||
general embedded platform. EG20T PCH has USB device interface.
|
||||
Using this interface, it is able to access system devices connected
|
||||
to USB device.
|
||||
This driver enables USB device function.
|
||||
USB device is a USB peripheral controller which
|
||||
supports both full and high speed USB 2.0 data transfers.
|
||||
This driver supports both control transfer and bulk transfer modes.
|
||||
This driver dose not support interrupt transfer or isochronous
|
||||
transfer modes.
|
||||
|
||||
config USB_EG20T
|
||||
tristate
|
||||
depends on USB_GADGET_EG20T
|
||||
default USB_GADGET
|
||||
select USB_GADGET_SELECTED
|
||||
|
||||
config USB_GADGET_CI13XXX_MSM
|
||||
boolean "MIPS USB CI13xxx for MSM"
|
||||
depends on ARCH_MSM
|
||||
select USB_GADGET_DUALSPEED
|
||||
select USB_MSM_OTG_72K
|
||||
help
|
||||
MSM SoC has chipidea USB controller. This driver uses
|
||||
ci13xxx_udc core.
|
||||
This driver depends on OTG driver for PHY initialization,
|
||||
clock management, powering up VBUS, and power management.
|
||||
|
||||
Say "y" to link the driver statically, or "m" to build a
|
||||
dynamically linked module called "ci13xxx_msm" and force all
|
||||
gadget drivers to also be dynamically linked.
|
||||
|
||||
config USB_CI13XXX_MSM
|
||||
tristate
|
||||
depends on USB_GADGET_CI13XXX_MSM
|
||||
default USB_GADGET
|
||||
select USB_GADGET_SELECTED
|
||||
|
||||
#
|
||||
# LAST -- dummy/emulated controller
|
||||
@@ -685,6 +741,19 @@ config USB_ETH_EEM
|
||||
If you say "y" here, the Ethernet gadget driver will use the EEM
|
||||
protocol rather than ECM. If unsure, say "n".
|
||||
|
||||
config USB_G_NCM
|
||||
tristate "Network Control Model (NCM) support"
|
||||
depends on NET
|
||||
select CRC32
|
||||
help
|
||||
This driver implements USB CDC NCM subclass standard. NCM is
|
||||
an advanced protocol for Ethernet encapsulation, allows grouping
|
||||
of several ethernet frames into one USB transfer and diffferent
|
||||
alignment possibilities.
|
||||
|
||||
Say "y" to link the driver statically, or "m" to build a
|
||||
dynamically linked module called "g_ncm".
|
||||
|
||||
config USB_GADGETFS
|
||||
tristate "Gadget Filesystem (EXPERIMENTAL)"
|
||||
depends on EXPERIMENTAL
|
||||
|
||||
@@ -21,9 +21,13 @@ fsl_usb2_udc-$(CONFIG_ARCH_MXC) += fsl_mxc_udc.o
|
||||
obj-$(CONFIG_USB_M66592) += m66592-udc.o
|
||||
obj-$(CONFIG_USB_R8A66597) += r8a66597-udc.o
|
||||
obj-$(CONFIG_USB_FSL_QE) += fsl_qe_udc.o
|
||||
obj-$(CONFIG_USB_CI13XXX) += ci13xxx_udc.o
|
||||
obj-$(CONFIG_USB_CI13XXX_PCI) += ci13xxx_pci.o
|
||||
obj-$(CONFIG_USB_S3C_HSOTG) += s3c-hsotg.o
|
||||
obj-$(CONFIG_USB_LANGWELL) += langwell_udc.o
|
||||
obj-$(CONFIG_USB_EG20T) += pch_udc.o
|
||||
obj-$(CONFIG_USB_PXA_U2O) += mv_udc.o
|
||||
mv_udc-y := mv_udc_core.o mv_udc_phy.o
|
||||
obj-$(CONFIG_USB_CI13XXX_MSM) += ci13xxx_msm.o
|
||||
|
||||
#
|
||||
# USB gadget drivers
|
||||
@@ -43,6 +47,7 @@ g_hid-y := hid.o
|
||||
g_dbgp-y := dbgp.o
|
||||
g_nokia-y := nokia.o
|
||||
g_webcam-y := webcam.o
|
||||
g_ncm-y := ncm.o
|
||||
|
||||
obj-$(CONFIG_USB_ZERO) += g_zero.o
|
||||
obj-$(CONFIG_USB_AUDIO) += g_audio.o
|
||||
@@ -60,3 +65,4 @@ obj-$(CONFIG_USB_G_DBGP) += g_dbgp.o
|
||||
obj-$(CONFIG_USB_G_MULTI) += g_multi.o
|
||||
obj-$(CONFIG_USB_G_NOKIA) += g_nokia.o
|
||||
obj-$(CONFIG_USB_G_WEBCAM) += g_webcam.o
|
||||
obj-$(CONFIG_USB_G_NCM) += g_ncm.o
|
||||
|
||||
@@ -3359,7 +3359,6 @@ static int udc_probe(struct udc *dev)
|
||||
dev_set_name(&dev->gadget.dev, "gadget");
|
||||
dev->gadget.dev.release = gadget_release;
|
||||
dev->gadget.name = name;
|
||||
dev->gadget.name = name;
|
||||
dev->gadget.is_dualspeed = 1;
|
||||
|
||||
/* init registers, interrupts, ... */
|
||||
|
||||
134
drivers/usb/gadget/ci13xxx_msm.c
Normal file
134
drivers/usb/gadget/ci13xxx_msm.c
Normal file
@@ -0,0 +1,134 @@
|
||||
/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only 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 Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/usb/msm_hsusb_hw.h>
|
||||
#include <linux/usb/ulpi.h>
|
||||
|
||||
#include "ci13xxx_udc.c"
|
||||
|
||||
#define MSM_USB_BASE (udc->regs)
|
||||
|
||||
static irqreturn_t msm_udc_irq(int irq, void *data)
|
||||
{
|
||||
return udc_irq();
|
||||
}
|
||||
|
||||
static void ci13xxx_msm_notify_event(struct ci13xxx *udc, unsigned event)
|
||||
{
|
||||
struct device *dev = udc->gadget.dev.parent;
|
||||
int val;
|
||||
|
||||
switch (event) {
|
||||
case CI13XXX_CONTROLLER_RESET_EVENT:
|
||||
dev_dbg(dev, "CI13XXX_CONTROLLER_RESET_EVENT received\n");
|
||||
writel(0, USB_AHBBURST);
|
||||
writel(0, USB_AHBMODE);
|
||||
break;
|
||||
case CI13XXX_CONTROLLER_STOPPED_EVENT:
|
||||
dev_dbg(dev, "CI13XXX_CONTROLLER_STOPPED_EVENT received\n");
|
||||
/*
|
||||
* Put the transceiver in non-driving mode. Otherwise host
|
||||
* may not detect soft-disconnection.
|
||||
*/
|
||||
val = otg_io_read(udc->transceiver, ULPI_FUNC_CTRL);
|
||||
val &= ~ULPI_FUNC_CTRL_OPMODE_MASK;
|
||||
val |= ULPI_FUNC_CTRL_OPMODE_NONDRIVING;
|
||||
otg_io_write(udc->transceiver, val, ULPI_FUNC_CTRL);
|
||||
break;
|
||||
default:
|
||||
dev_dbg(dev, "unknown ci13xxx_udc event\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static struct ci13xxx_udc_driver ci13xxx_msm_udc_driver = {
|
||||
.name = "ci13xxx_msm",
|
||||
.flags = CI13XXX_REGS_SHARED |
|
||||
CI13XXX_REQUIRE_TRANSCEIVER |
|
||||
CI13XXX_PULLUP_ON_VBUS |
|
||||
CI13XXX_DISABLE_STREAMING,
|
||||
|
||||
.notify_event = ci13xxx_msm_notify_event,
|
||||
};
|
||||
|
||||
static int ci13xxx_msm_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res;
|
||||
void __iomem *regs;
|
||||
int irq;
|
||||
int ret;
|
||||
|
||||
dev_dbg(&pdev->dev, "ci13xxx_msm_probe\n");
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "failed to get platform resource mem\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
regs = ioremap(res->start, resource_size(res));
|
||||
if (!regs) {
|
||||
dev_err(&pdev->dev, "ioremap failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = udc_probe(&ci13xxx_msm_udc_driver, &pdev->dev, regs);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "udc_probe failed\n");
|
||||
goto iounmap;
|
||||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "IRQ not found\n");
|
||||
ret = -ENXIO;
|
||||
goto udc_remove;
|
||||
}
|
||||
|
||||
ret = request_irq(irq, msm_udc_irq, IRQF_SHARED, pdev->name, pdev);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "request_irq failed\n");
|
||||
goto udc_remove;
|
||||
}
|
||||
|
||||
pm_runtime_no_callbacks(&pdev->dev);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
|
||||
udc_remove:
|
||||
udc_remove();
|
||||
iounmap:
|
||||
iounmap(regs);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct platform_driver ci13xxx_msm_driver = {
|
||||
.probe = ci13xxx_msm_probe,
|
||||
.driver = { .name = "msm_hsusb", },
|
||||
};
|
||||
|
||||
static int __init ci13xxx_msm_init(void)
|
||||
{
|
||||
return platform_driver_register(&ci13xxx_msm_driver);
|
||||
}
|
||||
module_init(ci13xxx_msm_init);
|
||||
176
drivers/usb/gadget/ci13xxx_pci.c
Normal file
176
drivers/usb/gadget/ci13xxx_pci.c
Normal file
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* ci13xxx_pci.c - MIPS USB IP core family device controller
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include "ci13xxx_udc.c"
|
||||
|
||||
/* driver name */
|
||||
#define UDC_DRIVER_NAME "ci13xxx_pci"
|
||||
|
||||
/******************************************************************************
|
||||
* PCI block
|
||||
*****************************************************************************/
|
||||
/**
|
||||
* ci13xxx_pci_irq: interrut handler
|
||||
* @irq: irq number
|
||||
* @pdev: USB Device Controller interrupt source
|
||||
*
|
||||
* This function returns IRQ_HANDLED if the IRQ has been handled
|
||||
* This is an ISR don't trace, use attribute interface instead
|
||||
*/
|
||||
static irqreturn_t ci13xxx_pci_irq(int irq, void *pdev)
|
||||
{
|
||||
if (irq == 0) {
|
||||
dev_err(&((struct pci_dev *)pdev)->dev, "Invalid IRQ0 usage!");
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
return udc_irq();
|
||||
}
|
||||
|
||||
static struct ci13xxx_udc_driver ci13xxx_pci_udc_driver = {
|
||||
.name = UDC_DRIVER_NAME,
|
||||
};
|
||||
|
||||
/**
|
||||
* ci13xxx_pci_probe: PCI probe
|
||||
* @pdev: USB device controller being probed
|
||||
* @id: PCI hotplug ID connecting controller to UDC framework
|
||||
*
|
||||
* This function returns an error code
|
||||
* Allocates basic PCI resources for this USB device controller, and then
|
||||
* invokes the udc_probe() method to start the UDC associated with it
|
||||
*/
|
||||
static int __devinit ci13xxx_pci_probe(struct pci_dev *pdev,
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
void __iomem *regs = NULL;
|
||||
int retval = 0;
|
||||
|
||||
if (id == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
retval = pci_enable_device(pdev);
|
||||
if (retval)
|
||||
goto done;
|
||||
|
||||
if (!pdev->irq) {
|
||||
dev_err(&pdev->dev, "No IRQ, check BIOS/PCI setup!");
|
||||
retval = -ENODEV;
|
||||
goto disable_device;
|
||||
}
|
||||
|
||||
retval = pci_request_regions(pdev, UDC_DRIVER_NAME);
|
||||
if (retval)
|
||||
goto disable_device;
|
||||
|
||||
/* BAR 0 holds all the registers */
|
||||
regs = pci_iomap(pdev, 0, 0);
|
||||
if (!regs) {
|
||||
dev_err(&pdev->dev, "Error mapping memory!");
|
||||
retval = -EFAULT;
|
||||
goto release_regions;
|
||||
}
|
||||
pci_set_drvdata(pdev, (__force void *)regs);
|
||||
|
||||
pci_set_master(pdev);
|
||||
pci_try_set_mwi(pdev);
|
||||
|
||||
retval = udc_probe(&ci13xxx_pci_udc_driver, &pdev->dev, regs);
|
||||
if (retval)
|
||||
goto iounmap;
|
||||
|
||||
/* our device does not have MSI capability */
|
||||
|
||||
retval = request_irq(pdev->irq, ci13xxx_pci_irq, IRQF_SHARED,
|
||||
UDC_DRIVER_NAME, pdev);
|
||||
if (retval)
|
||||
goto gadget_remove;
|
||||
|
||||
return 0;
|
||||
|
||||
gadget_remove:
|
||||
udc_remove();
|
||||
iounmap:
|
||||
pci_iounmap(pdev, regs);
|
||||
release_regions:
|
||||
pci_release_regions(pdev);
|
||||
disable_device:
|
||||
pci_disable_device(pdev);
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* ci13xxx_pci_remove: PCI remove
|
||||
* @pdev: USB Device Controller being removed
|
||||
*
|
||||
* Reverses the effect of ci13xxx_pci_probe(),
|
||||
* first invoking the udc_remove() and then releases
|
||||
* all PCI resources allocated for this USB device controller
|
||||
*/
|
||||
static void __devexit ci13xxx_pci_remove(struct pci_dev *pdev)
|
||||
{
|
||||
free_irq(pdev->irq, pdev);
|
||||
udc_remove();
|
||||
pci_iounmap(pdev, (__force void __iomem *)pci_get_drvdata(pdev));
|
||||
pci_release_regions(pdev);
|
||||
pci_disable_device(pdev);
|
||||
}
|
||||
|
||||
/**
|
||||
* PCI device table
|
||||
* PCI device structure
|
||||
*
|
||||
* Check "pci.h" for details
|
||||
*/
|
||||
static DEFINE_PCI_DEVICE_TABLE(ci13xxx_pci_id_table) = {
|
||||
{ PCI_DEVICE(0x153F, 0x1004) },
|
||||
{ PCI_DEVICE(0x153F, 0x1006) },
|
||||
{ 0, 0, 0, 0, 0, 0, 0 /* end: all zeroes */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, ci13xxx_pci_id_table);
|
||||
|
||||
static struct pci_driver ci13xxx_pci_driver = {
|
||||
.name = UDC_DRIVER_NAME,
|
||||
.id_table = ci13xxx_pci_id_table,
|
||||
.probe = ci13xxx_pci_probe,
|
||||
.remove = __devexit_p(ci13xxx_pci_remove),
|
||||
};
|
||||
|
||||
/**
|
||||
* ci13xxx_pci_init: module init
|
||||
*
|
||||
* Driver load
|
||||
*/
|
||||
static int __init ci13xxx_pci_init(void)
|
||||
{
|
||||
return pci_register_driver(&ci13xxx_pci_driver);
|
||||
}
|
||||
module_init(ci13xxx_pci_init);
|
||||
|
||||
/**
|
||||
* ci13xxx_pci_exit: module exit
|
||||
*
|
||||
* Driver unload
|
||||
*/
|
||||
static void __exit ci13xxx_pci_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&ci13xxx_pci_driver);
|
||||
}
|
||||
module_exit(ci13xxx_pci_exit);
|
||||
|
||||
MODULE_AUTHOR("MIPS - David Lopo <dlopo@chipidea.mips.com>");
|
||||
MODULE_DESCRIPTION("MIPS CI13XXX USB Peripheral Controller");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_VERSION("June 2008");
|
||||
@@ -22,7 +22,6 @@
|
||||
* - ENDPT: endpoint operations (Gadget API)
|
||||
* - GADGET: gadget operations (Gadget API)
|
||||
* - BUS: bus glue code, bus abstraction layer
|
||||
* - PCI: PCI core interface and PCI resources (interrupts, memory...)
|
||||
*
|
||||
* Compile Options
|
||||
* - CONFIG_USB_GADGET_DEBUG_FILES: enable debug facilities
|
||||
@@ -60,11 +59,11 @@
|
||||
#include <linux/io.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
#include <linux/usb/otg.h>
|
||||
|
||||
#include "ci13xxx_udc.h"
|
||||
|
||||
@@ -75,9 +74,6 @@
|
||||
/* ctrl register bank access */
|
||||
static DEFINE_SPINLOCK(udc_lock);
|
||||
|
||||
/* driver name */
|
||||
#define UDC_DRIVER_NAME "ci13xxx_udc"
|
||||
|
||||
/* control endpoint description */
|
||||
static const struct usb_endpoint_descriptor
|
||||
ctrl_endpt_desc = {
|
||||
@@ -132,6 +128,9 @@ static struct {
|
||||
size_t size; /* bank size */
|
||||
} hw_bank;
|
||||
|
||||
/* MSM specific */
|
||||
#define ABS_AHBBURST (0x0090UL)
|
||||
#define ABS_AHBMODE (0x0098UL)
|
||||
/* UDC register map */
|
||||
#define ABS_CAPLENGTH (0x100UL)
|
||||
#define ABS_HCCPARAMS (0x108UL)
|
||||
@@ -248,13 +247,7 @@ static u32 hw_ctest_and_write(u32 addr, u32 mask, u32 data)
|
||||
return (reg & mask) >> ffs_nr(mask);
|
||||
}
|
||||
|
||||
/**
|
||||
* hw_device_reset: resets chip (execute without interruption)
|
||||
* @base: register base address
|
||||
*
|
||||
* This function returns an error code
|
||||
*/
|
||||
static int hw_device_reset(void __iomem *base)
|
||||
static int hw_device_init(void __iomem *base)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
@@ -271,25 +264,6 @@ static int hw_device_reset(void __iomem *base)
|
||||
hw_bank.size += CAP_LAST;
|
||||
hw_bank.size /= sizeof(u32);
|
||||
|
||||
/* should flush & stop before reset */
|
||||
hw_cwrite(CAP_ENDPTFLUSH, ~0, ~0);
|
||||
hw_cwrite(CAP_USBCMD, USBCMD_RS, 0);
|
||||
|
||||
hw_cwrite(CAP_USBCMD, USBCMD_RST, USBCMD_RST);
|
||||
while (hw_cread(CAP_USBCMD, USBCMD_RST))
|
||||
udelay(10); /* not RTOS friendly */
|
||||
|
||||
/* USBMODE should be configured step by step */
|
||||
hw_cwrite(CAP_USBMODE, USBMODE_CM, USBMODE_CM_IDLE);
|
||||
hw_cwrite(CAP_USBMODE, USBMODE_CM, USBMODE_CM_DEVICE);
|
||||
hw_cwrite(CAP_USBMODE, USBMODE_SLOM, USBMODE_SLOM); /* HW >= 2.3 */
|
||||
|
||||
if (hw_cread(CAP_USBMODE, USBMODE_CM) != USBMODE_CM_DEVICE) {
|
||||
pr_err("cannot enter in device mode");
|
||||
pr_err("lpm = %i", hw_bank.lpm);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
reg = hw_aread(ABS_DCCPARAMS, DCCPARAMS_DEN) >> ffs_nr(DCCPARAMS_DEN);
|
||||
if (reg == 0 || reg > ENDPT_MAX)
|
||||
return -ENODEV;
|
||||
@@ -304,6 +278,43 @@ static int hw_device_reset(void __iomem *base)
|
||||
|
||||
return 0;
|
||||
}
|
||||
/**
|
||||
* hw_device_reset: resets chip (execute without interruption)
|
||||
* @base: register base address
|
||||
*
|
||||
* This function returns an error code
|
||||
*/
|
||||
static int hw_device_reset(struct ci13xxx *udc)
|
||||
{
|
||||
/* should flush & stop before reset */
|
||||
hw_cwrite(CAP_ENDPTFLUSH, ~0, ~0);
|
||||
hw_cwrite(CAP_USBCMD, USBCMD_RS, 0);
|
||||
|
||||
hw_cwrite(CAP_USBCMD, USBCMD_RST, USBCMD_RST);
|
||||
while (hw_cread(CAP_USBCMD, USBCMD_RST))
|
||||
udelay(10); /* not RTOS friendly */
|
||||
|
||||
|
||||
if (udc->udc_driver->notify_event)
|
||||
udc->udc_driver->notify_event(udc,
|
||||
CI13XXX_CONTROLLER_RESET_EVENT);
|
||||
|
||||
if (udc->udc_driver->flags && CI13XXX_DISABLE_STREAMING)
|
||||
hw_cwrite(CAP_USBMODE, USBMODE_SDIS, USBMODE_SDIS);
|
||||
|
||||
/* USBMODE should be configured step by step */
|
||||
hw_cwrite(CAP_USBMODE, USBMODE_CM, USBMODE_CM_IDLE);
|
||||
hw_cwrite(CAP_USBMODE, USBMODE_CM, USBMODE_CM_DEVICE);
|
||||
hw_cwrite(CAP_USBMODE, USBMODE_SLOM, USBMODE_SLOM); /* HW >= 2.3 */
|
||||
|
||||
if (hw_cread(CAP_USBMODE, USBMODE_CM) != USBMODE_CM_DEVICE) {
|
||||
pr_err("cannot enter in device mode");
|
||||
pr_err("lpm = %i", hw_bank.lpm);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* hw_device_state: enables/disables interrupts & starts/stops device (execute
|
||||
@@ -1557,8 +1568,6 @@ __acquires(mEp->lock)
|
||||
* Caller must hold lock
|
||||
*/
|
||||
static int _gadget_stop_activity(struct usb_gadget *gadget)
|
||||
__releases(udc->lock)
|
||||
__acquires(udc->lock)
|
||||
{
|
||||
struct usb_ep *ep;
|
||||
struct ci13xxx *udc = container_of(gadget, struct ci13xxx, gadget);
|
||||
@@ -1570,8 +1579,6 @@ __acquires(udc->lock)
|
||||
if (gadget == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
spin_unlock(udc->lock);
|
||||
|
||||
/* flush all endpoints */
|
||||
gadget_for_each_ep(ep, gadget) {
|
||||
usb_ep_fifo_flush(ep);
|
||||
@@ -1591,8 +1598,6 @@ __acquires(udc->lock)
|
||||
mEp->status = NULL;
|
||||
}
|
||||
|
||||
spin_lock(udc->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1621,6 +1626,7 @@ __acquires(udc->lock)
|
||||
|
||||
dbg_event(0xFF, "BUS RST", 0);
|
||||
|
||||
spin_unlock(udc->lock);
|
||||
retval = _gadget_stop_activity(&udc->gadget);
|
||||
if (retval)
|
||||
goto done;
|
||||
@@ -1629,10 +1635,9 @@ __acquires(udc->lock)
|
||||
if (retval)
|
||||
goto done;
|
||||
|
||||
spin_unlock(udc->lock);
|
||||
retval = usb_ep_enable(&mEp->ep, &ctrl_endpt_desc);
|
||||
if (!retval) {
|
||||
mEp->status = usb_ep_alloc_request(&mEp->ep, GFP_KERNEL);
|
||||
mEp->status = usb_ep_alloc_request(&mEp->ep, GFP_ATOMIC);
|
||||
if (mEp->status == NULL) {
|
||||
usb_ep_disable(&mEp->ep);
|
||||
retval = -ENOMEM;
|
||||
@@ -2061,7 +2066,6 @@ static struct usb_request *ep_alloc_request(struct usb_ep *ep, gfp_t gfp_flags)
|
||||
{
|
||||
struct ci13xxx_ep *mEp = container_of(ep, struct ci13xxx_ep, ep);
|
||||
struct ci13xxx_req *mReq = NULL;
|
||||
unsigned long flags;
|
||||
|
||||
trace("%p, %i", ep, gfp_flags);
|
||||
|
||||
@@ -2070,8 +2074,6 @@ static struct usb_request *ep_alloc_request(struct usb_ep *ep, gfp_t gfp_flags)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(mEp->lock, flags);
|
||||
|
||||
mReq = kzalloc(sizeof(struct ci13xxx_req), gfp_flags);
|
||||
if (mReq != NULL) {
|
||||
INIT_LIST_HEAD(&mReq->queue);
|
||||
@@ -2086,8 +2088,6 @@ static struct usb_request *ep_alloc_request(struct usb_ep *ep, gfp_t gfp_flags)
|
||||
|
||||
dbg_event(_usb_addr(mEp), "ALLOC", mReq == NULL);
|
||||
|
||||
spin_unlock_irqrestore(mEp->lock, flags);
|
||||
|
||||
return (mReq == NULL) ? NULL : &mReq->req;
|
||||
}
|
||||
|
||||
@@ -2332,12 +2332,47 @@ static const struct usb_ep_ops usb_ep_ops = {
|
||||
/******************************************************************************
|
||||
* GADGET block
|
||||
*****************************************************************************/
|
||||
static int ci13xxx_vbus_session(struct usb_gadget *_gadget, int is_active)
|
||||
{
|
||||
struct ci13xxx *udc = container_of(_gadget, struct ci13xxx, gadget);
|
||||
unsigned long flags;
|
||||
int gadget_ready = 0;
|
||||
|
||||
if (!(udc->udc_driver->flags & CI13XXX_PULLUP_ON_VBUS))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
spin_lock_irqsave(udc->lock, flags);
|
||||
udc->vbus_active = is_active;
|
||||
if (udc->driver)
|
||||
gadget_ready = 1;
|
||||
spin_unlock_irqrestore(udc->lock, flags);
|
||||
|
||||
if (gadget_ready) {
|
||||
if (is_active) {
|
||||
pm_runtime_get_sync(&_gadget->dev);
|
||||
hw_device_reset(udc);
|
||||
hw_device_state(udc->ci13xxx_ep[0].qh[RX].dma);
|
||||
} else {
|
||||
hw_device_state(0);
|
||||
if (udc->udc_driver->notify_event)
|
||||
udc->udc_driver->notify_event(udc,
|
||||
CI13XXX_CONTROLLER_STOPPED_EVENT);
|
||||
_gadget_stop_activity(&udc->gadget);
|
||||
pm_runtime_put_sync(&_gadget->dev);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Device operations part of the API to the USB controller hardware,
|
||||
* which don't involve endpoints (or i/o)
|
||||
* Check "usb_gadget.h" for details
|
||||
*/
|
||||
static const struct usb_gadget_ops usb_gadget_ops;
|
||||
static const struct usb_gadget_ops usb_gadget_ops = {
|
||||
.vbus_session = ci13xxx_vbus_session,
|
||||
};
|
||||
|
||||
/**
|
||||
* usb_gadget_probe_driver: register a gadget driver
|
||||
@@ -2390,7 +2425,6 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
|
||||
info("hw_ep_max = %d", hw_ep_max);
|
||||
|
||||
udc->driver = driver;
|
||||
udc->gadget.ops = NULL;
|
||||
udc->gadget.dev.driver = NULL;
|
||||
|
||||
retval = 0;
|
||||
@@ -2410,9 +2444,11 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
|
||||
/* this allocation cannot be random */
|
||||
for (k = RX; k <= TX; k++) {
|
||||
INIT_LIST_HEAD(&mEp->qh[k].queue);
|
||||
spin_unlock_irqrestore(udc->lock, flags);
|
||||
mEp->qh[k].ptr = dma_pool_alloc(udc->qh_pool,
|
||||
GFP_KERNEL,
|
||||
&mEp->qh[k].dma);
|
||||
spin_lock_irqsave(udc->lock, flags);
|
||||
if (mEp->qh[k].ptr == NULL)
|
||||
retval = -ENOMEM;
|
||||
else
|
||||
@@ -2429,7 +2465,6 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
|
||||
|
||||
/* bind gadget */
|
||||
driver->driver.bus = NULL;
|
||||
udc->gadget.ops = &usb_gadget_ops;
|
||||
udc->gadget.dev.driver = &driver->driver;
|
||||
|
||||
spin_unlock_irqrestore(udc->lock, flags);
|
||||
@@ -2437,12 +2472,24 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
|
||||
spin_lock_irqsave(udc->lock, flags);
|
||||
|
||||
if (retval) {
|
||||
udc->gadget.ops = NULL;
|
||||
udc->gadget.dev.driver = NULL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
pm_runtime_get_sync(&udc->gadget.dev);
|
||||
if (udc->udc_driver->flags & CI13XXX_PULLUP_ON_VBUS) {
|
||||
if (udc->vbus_active) {
|
||||
if (udc->udc_driver->flags & CI13XXX_REGS_SHARED)
|
||||
hw_device_reset(udc);
|
||||
} else {
|
||||
pm_runtime_put_sync(&udc->gadget.dev);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
retval = hw_device_state(udc->ci13xxx_ep[0].qh[RX].dma);
|
||||
if (retval)
|
||||
pm_runtime_put_sync(&udc->gadget.dev);
|
||||
|
||||
done:
|
||||
spin_unlock_irqrestore(udc->lock, flags);
|
||||
@@ -2475,19 +2522,22 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
|
||||
|
||||
spin_lock_irqsave(udc->lock, flags);
|
||||
|
||||
hw_device_state(0);
|
||||
if (!(udc->udc_driver->flags & CI13XXX_PULLUP_ON_VBUS) ||
|
||||
udc->vbus_active) {
|
||||
hw_device_state(0);
|
||||
if (udc->udc_driver->notify_event)
|
||||
udc->udc_driver->notify_event(udc,
|
||||
CI13XXX_CONTROLLER_STOPPED_EVENT);
|
||||
_gadget_stop_activity(&udc->gadget);
|
||||
pm_runtime_put(&udc->gadget.dev);
|
||||
}
|
||||
|
||||
/* unbind gadget */
|
||||
if (udc->gadget.ops != NULL) {
|
||||
_gadget_stop_activity(&udc->gadget);
|
||||
spin_unlock_irqrestore(udc->lock, flags);
|
||||
driver->unbind(&udc->gadget); /* MAY SLEEP */
|
||||
spin_lock_irqsave(udc->lock, flags);
|
||||
|
||||
spin_unlock_irqrestore(udc->lock, flags);
|
||||
driver->unbind(&udc->gadget); /* MAY SLEEP */
|
||||
spin_lock_irqsave(udc->lock, flags);
|
||||
|
||||
udc->gadget.ops = NULL;
|
||||
udc->gadget.dev.driver = NULL;
|
||||
}
|
||||
udc->gadget.dev.driver = NULL;
|
||||
|
||||
/* free resources */
|
||||
for (i = 0; i < hw_ep_max; i++) {
|
||||
@@ -2544,6 +2594,14 @@ static irqreturn_t udc_irq(void)
|
||||
}
|
||||
|
||||
spin_lock(udc->lock);
|
||||
|
||||
if (udc->udc_driver->flags & CI13XXX_REGS_SHARED) {
|
||||
if (hw_cread(CAP_USBMODE, USBMODE_CM) !=
|
||||
USBMODE_CM_DEVICE) {
|
||||
spin_unlock(udc->lock);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
}
|
||||
intr = hw_test_and_clear_intr_active();
|
||||
if (intr) {
|
||||
isr_statistics.hndl.buf[isr_statistics.hndl.idx++] = intr;
|
||||
@@ -2602,14 +2660,16 @@ static void udc_release(struct device *dev)
|
||||
* No interrupts active, the IRQ has not been requested yet
|
||||
* Kernel assumes 32-bit DMA operations by default, no need to dma_set_mask
|
||||
*/
|
||||
static int udc_probe(struct device *dev, void __iomem *regs, const char *name)
|
||||
static int udc_probe(struct ci13xxx_udc_driver *driver, struct device *dev,
|
||||
void __iomem *regs)
|
||||
{
|
||||
struct ci13xxx *udc;
|
||||
int retval = 0;
|
||||
|
||||
trace("%p, %p, %p", dev, regs, name);
|
||||
|
||||
if (dev == NULL || regs == NULL || name == NULL)
|
||||
if (dev == NULL || regs == NULL || driver == NULL ||
|
||||
driver->name == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
udc = kzalloc(sizeof(struct ci13xxx), GFP_KERNEL);
|
||||
@@ -2617,42 +2677,77 @@ static int udc_probe(struct device *dev, void __iomem *regs, const char *name)
|
||||
return -ENOMEM;
|
||||
|
||||
udc->lock = &udc_lock;
|
||||
udc->regs = regs;
|
||||
udc->udc_driver = driver;
|
||||
|
||||
retval = hw_device_reset(regs);
|
||||
if (retval)
|
||||
goto done;
|
||||
|
||||
udc->gadget.ops = NULL;
|
||||
udc->gadget.ops = &usb_gadget_ops;
|
||||
udc->gadget.speed = USB_SPEED_UNKNOWN;
|
||||
udc->gadget.is_dualspeed = 1;
|
||||
udc->gadget.is_otg = 0;
|
||||
udc->gadget.name = name;
|
||||
udc->gadget.name = driver->name;
|
||||
|
||||
INIT_LIST_HEAD(&udc->gadget.ep_list);
|
||||
udc->gadget.ep0 = NULL;
|
||||
|
||||
dev_set_name(&udc->gadget.dev, "gadget");
|
||||
udc->gadget.dev.dma_mask = dev->dma_mask;
|
||||
udc->gadget.dev.coherent_dma_mask = dev->coherent_dma_mask;
|
||||
udc->gadget.dev.parent = dev;
|
||||
udc->gadget.dev.release = udc_release;
|
||||
|
||||
retval = hw_device_init(regs);
|
||||
if (retval < 0)
|
||||
goto free_udc;
|
||||
|
||||
udc->transceiver = otg_get_transceiver();
|
||||
|
||||
if (udc->udc_driver->flags & CI13XXX_REQUIRE_TRANSCEIVER) {
|
||||
if (udc->transceiver == NULL) {
|
||||
retval = -ENODEV;
|
||||
goto free_udc;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(udc->udc_driver->flags & CI13XXX_REGS_SHARED)) {
|
||||
retval = hw_device_reset(udc);
|
||||
if (retval)
|
||||
goto put_transceiver;
|
||||
}
|
||||
|
||||
retval = device_register(&udc->gadget.dev);
|
||||
if (retval)
|
||||
goto done;
|
||||
if (retval) {
|
||||
put_device(&udc->gadget.dev);
|
||||
goto put_transceiver;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USB_GADGET_DEBUG_FILES
|
||||
retval = dbg_create_files(&udc->gadget.dev);
|
||||
#endif
|
||||
if (retval) {
|
||||
device_unregister(&udc->gadget.dev);
|
||||
goto done;
|
||||
if (retval)
|
||||
goto unreg_device;
|
||||
|
||||
if (udc->transceiver) {
|
||||
retval = otg_set_peripheral(udc->transceiver, &udc->gadget);
|
||||
if (retval)
|
||||
goto remove_dbg;
|
||||
}
|
||||
pm_runtime_no_callbacks(&udc->gadget.dev);
|
||||
pm_runtime_enable(&udc->gadget.dev);
|
||||
|
||||
_udc = udc;
|
||||
return retval;
|
||||
|
||||
done:
|
||||
err("error = %i", retval);
|
||||
remove_dbg:
|
||||
#ifdef CONFIG_USB_GADGET_DEBUG_FILES
|
||||
dbg_remove_files(&udc->gadget.dev);
|
||||
#endif
|
||||
unreg_device:
|
||||
device_unregister(&udc->gadget.dev);
|
||||
put_transceiver:
|
||||
if (udc->transceiver)
|
||||
otg_put_transceiver(udc->transceiver);
|
||||
free_udc:
|
||||
kfree(udc);
|
||||
_udc = NULL;
|
||||
return retval;
|
||||
@@ -2672,6 +2767,10 @@ static void udc_remove(void)
|
||||
return;
|
||||
}
|
||||
|
||||
if (udc->transceiver) {
|
||||
otg_set_peripheral(udc->transceiver, &udc->gadget);
|
||||
otg_put_transceiver(udc->transceiver);
|
||||
}
|
||||
#ifdef CONFIG_USB_GADGET_DEBUG_FILES
|
||||
dbg_remove_files(&udc->gadget.dev);
|
||||
#endif
|
||||
@@ -2680,156 +2779,3 @@ static void udc_remove(void)
|
||||
kfree(udc);
|
||||
_udc = NULL;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* PCI block
|
||||
*****************************************************************************/
|
||||
/**
|
||||
* ci13xxx_pci_irq: interrut handler
|
||||
* @irq: irq number
|
||||
* @pdev: USB Device Controller interrupt source
|
||||
*
|
||||
* This function returns IRQ_HANDLED if the IRQ has been handled
|
||||
* This is an ISR don't trace, use attribute interface instead
|
||||
*/
|
||||
static irqreturn_t ci13xxx_pci_irq(int irq, void *pdev)
|
||||
{
|
||||
if (irq == 0) {
|
||||
dev_err(&((struct pci_dev *)pdev)->dev, "Invalid IRQ0 usage!");
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
return udc_irq();
|
||||
}
|
||||
|
||||
/**
|
||||
* ci13xxx_pci_probe: PCI probe
|
||||
* @pdev: USB device controller being probed
|
||||
* @id: PCI hotplug ID connecting controller to UDC framework
|
||||
*
|
||||
* This function returns an error code
|
||||
* Allocates basic PCI resources for this USB device controller, and then
|
||||
* invokes the udc_probe() method to start the UDC associated with it
|
||||
*/
|
||||
static int __devinit ci13xxx_pci_probe(struct pci_dev *pdev,
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
void __iomem *regs = NULL;
|
||||
int retval = 0;
|
||||
|
||||
if (id == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
retval = pci_enable_device(pdev);
|
||||
if (retval)
|
||||
goto done;
|
||||
|
||||
if (!pdev->irq) {
|
||||
dev_err(&pdev->dev, "No IRQ, check BIOS/PCI setup!");
|
||||
retval = -ENODEV;
|
||||
goto disable_device;
|
||||
}
|
||||
|
||||
retval = pci_request_regions(pdev, UDC_DRIVER_NAME);
|
||||
if (retval)
|
||||
goto disable_device;
|
||||
|
||||
/* BAR 0 holds all the registers */
|
||||
regs = pci_iomap(pdev, 0, 0);
|
||||
if (!regs) {
|
||||
dev_err(&pdev->dev, "Error mapping memory!");
|
||||
retval = -EFAULT;
|
||||
goto release_regions;
|
||||
}
|
||||
pci_set_drvdata(pdev, (__force void *)regs);
|
||||
|
||||
pci_set_master(pdev);
|
||||
pci_try_set_mwi(pdev);
|
||||
|
||||
retval = udc_probe(&pdev->dev, regs, UDC_DRIVER_NAME);
|
||||
if (retval)
|
||||
goto iounmap;
|
||||
|
||||
/* our device does not have MSI capability */
|
||||
|
||||
retval = request_irq(pdev->irq, ci13xxx_pci_irq, IRQF_SHARED,
|
||||
UDC_DRIVER_NAME, pdev);
|
||||
if (retval)
|
||||
goto gadget_remove;
|
||||
|
||||
return 0;
|
||||
|
||||
gadget_remove:
|
||||
udc_remove();
|
||||
iounmap:
|
||||
pci_iounmap(pdev, regs);
|
||||
release_regions:
|
||||
pci_release_regions(pdev);
|
||||
disable_device:
|
||||
pci_disable_device(pdev);
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* ci13xxx_pci_remove: PCI remove
|
||||
* @pdev: USB Device Controller being removed
|
||||
*
|
||||
* Reverses the effect of ci13xxx_pci_probe(),
|
||||
* first invoking the udc_remove() and then releases
|
||||
* all PCI resources allocated for this USB device controller
|
||||
*/
|
||||
static void __devexit ci13xxx_pci_remove(struct pci_dev *pdev)
|
||||
{
|
||||
free_irq(pdev->irq, pdev);
|
||||
udc_remove();
|
||||
pci_iounmap(pdev, (__force void __iomem *)pci_get_drvdata(pdev));
|
||||
pci_release_regions(pdev);
|
||||
pci_disable_device(pdev);
|
||||
}
|
||||
|
||||
/**
|
||||
* PCI device table
|
||||
* PCI device structure
|
||||
*
|
||||
* Check "pci.h" for details
|
||||
*/
|
||||
static DEFINE_PCI_DEVICE_TABLE(ci13xxx_pci_id_table) = {
|
||||
{ PCI_DEVICE(0x153F, 0x1004) },
|
||||
{ PCI_DEVICE(0x153F, 0x1006) },
|
||||
{ 0, 0, 0, 0, 0, 0, 0 /* end: all zeroes */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, ci13xxx_pci_id_table);
|
||||
|
||||
static struct pci_driver ci13xxx_pci_driver = {
|
||||
.name = UDC_DRIVER_NAME,
|
||||
.id_table = ci13xxx_pci_id_table,
|
||||
.probe = ci13xxx_pci_probe,
|
||||
.remove = __devexit_p(ci13xxx_pci_remove),
|
||||
};
|
||||
|
||||
/**
|
||||
* ci13xxx_pci_init: module init
|
||||
*
|
||||
* Driver load
|
||||
*/
|
||||
static int __init ci13xxx_pci_init(void)
|
||||
{
|
||||
return pci_register_driver(&ci13xxx_pci_driver);
|
||||
}
|
||||
module_init(ci13xxx_pci_init);
|
||||
|
||||
/**
|
||||
* ci13xxx_pci_exit: module exit
|
||||
*
|
||||
* Driver unload
|
||||
*/
|
||||
static void __exit ci13xxx_pci_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&ci13xxx_pci_driver);
|
||||
}
|
||||
module_exit(ci13xxx_pci_exit);
|
||||
|
||||
MODULE_AUTHOR("MIPS - David Lopo <dlopo@chipidea.mips.com>");
|
||||
MODULE_DESCRIPTION("MIPS CI13XXX USB Peripheral Controller");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_VERSION("June 2008");
|
||||
|
||||
@@ -97,9 +97,24 @@ struct ci13xxx_ep {
|
||||
struct dma_pool *td_pool;
|
||||
};
|
||||
|
||||
struct ci13xxx;
|
||||
struct ci13xxx_udc_driver {
|
||||
const char *name;
|
||||
unsigned long flags;
|
||||
#define CI13XXX_REGS_SHARED BIT(0)
|
||||
#define CI13XXX_REQUIRE_TRANSCEIVER BIT(1)
|
||||
#define CI13XXX_PULLUP_ON_VBUS BIT(2)
|
||||
#define CI13XXX_DISABLE_STREAMING BIT(3)
|
||||
|
||||
#define CI13XXX_CONTROLLER_RESET_EVENT 0
|
||||
#define CI13XXX_CONTROLLER_STOPPED_EVENT 1
|
||||
void (*notify_event) (struct ci13xxx *udc, unsigned event);
|
||||
};
|
||||
|
||||
/* CI13XXX UDC descriptor & global resources */
|
||||
struct ci13xxx {
|
||||
spinlock_t *lock; /* ctrl register bank access */
|
||||
void __iomem *regs; /* registers address space */
|
||||
|
||||
struct dma_pool *qh_pool; /* DMA pool for queue heads */
|
||||
struct dma_pool *td_pool; /* DMA pool for transfer descs */
|
||||
@@ -108,6 +123,9 @@ struct ci13xxx {
|
||||
struct ci13xxx_ep ci13xxx_ep[ENDPT_MAX]; /* extended endpts */
|
||||
|
||||
struct usb_gadget_driver *driver; /* 3rd party gadget driver */
|
||||
struct ci13xxx_udc_driver *udc_driver; /* device controller driver */
|
||||
int vbus_active; /* is VBUS active */
|
||||
struct otg_transceiver *transceiver; /* Transceiver struct */
|
||||
};
|
||||
|
||||
/******************************************************************************
|
||||
@@ -157,6 +175,7 @@ struct ci13xxx {
|
||||
#define USBMODE_CM_DEVICE (0x02UL << 0)
|
||||
#define USBMODE_CM_HOST (0x03UL << 0)
|
||||
#define USBMODE_SLOM BIT(3)
|
||||
#define USBMODE_SDIS BIT(4)
|
||||
|
||||
/* ENDPTCTRL */
|
||||
#define ENDPTCTRL_RXS BIT(0)
|
||||
|
||||
@@ -1126,7 +1126,7 @@ static int composite_bind(struct usb_gadget *gadget)
|
||||
cdev->desc = *composite->dev;
|
||||
cdev->desc.bMaxPacketSize0 = gadget->ep0->maxpacket;
|
||||
|
||||
/* stirng overrides */
|
||||
/* string overrides */
|
||||
if (iManufacturer || !cdev->desc.iManufacturer) {
|
||||
if (!iManufacturer && !composite->iManufacturer &&
|
||||
!*composite_manufacturer)
|
||||
@@ -1188,6 +1188,8 @@ composite_suspend(struct usb_gadget *gadget)
|
||||
composite->suspend(cdev);
|
||||
|
||||
cdev->suspended = 1;
|
||||
|
||||
usb_gadget_vbus_draw(gadget, 2);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -1195,6 +1197,7 @@ composite_resume(struct usb_gadget *gadget)
|
||||
{
|
||||
struct usb_composite_dev *cdev = get_gadget_data(gadget);
|
||||
struct usb_function *f;
|
||||
u8 maxpower;
|
||||
|
||||
/* REVISIT: should we have config level
|
||||
* suspend/resume callbacks?
|
||||
@@ -1207,6 +1210,11 @@ composite_resume(struct usb_gadget *gadget)
|
||||
if (f->resume)
|
||||
f->resume(f);
|
||||
}
|
||||
|
||||
maxpower = cdev->config->bMaxPower;
|
||||
|
||||
usb_gadget_vbus_draw(gadget, maxpower ?
|
||||
(2 * maxpower) : CONFIG_USB_GADGET_VBUS_DRAW);
|
||||
}
|
||||
|
||||
cdev->suspended = 0;
|
||||
|
||||
@@ -1197,6 +1197,139 @@ static struct dummy_ep *find_endpoint (struct dummy *dum, u8 address)
|
||||
#define Ep_Request (USB_TYPE_STANDARD | USB_RECIP_ENDPOINT)
|
||||
#define Ep_InRequest (Ep_Request | USB_DIR_IN)
|
||||
|
||||
|
||||
/**
|
||||
* handle_control_request() - handles all control transfers
|
||||
* @dum: pointer to dummy (the_controller)
|
||||
* @urb: the urb request to handle
|
||||
* @setup: pointer to the setup data for a USB device control
|
||||
* request
|
||||
* @status: pointer to request handling status
|
||||
*
|
||||
* Return 0 - if the request was handled
|
||||
* 1 - if the request wasn't handles
|
||||
* error code on error
|
||||
*/
|
||||
static int handle_control_request(struct dummy *dum, struct urb *urb,
|
||||
struct usb_ctrlrequest *setup,
|
||||
int *status)
|
||||
{
|
||||
struct dummy_ep *ep2;
|
||||
int ret_val = 1;
|
||||
unsigned w_index;
|
||||
unsigned w_value;
|
||||
|
||||
w_index = le16_to_cpu(setup->wIndex);
|
||||
w_value = le16_to_cpu(setup->wValue);
|
||||
switch (setup->bRequest) {
|
||||
case USB_REQ_SET_ADDRESS:
|
||||
if (setup->bRequestType != Dev_Request)
|
||||
break;
|
||||
dum->address = w_value;
|
||||
*status = 0;
|
||||
dev_dbg(udc_dev(dum), "set_address = %d\n",
|
||||
w_value);
|
||||
ret_val = 0;
|
||||
break;
|
||||
case USB_REQ_SET_FEATURE:
|
||||
if (setup->bRequestType == Dev_Request) {
|
||||
ret_val = 0;
|
||||
switch (w_value) {
|
||||
case USB_DEVICE_REMOTE_WAKEUP:
|
||||
break;
|
||||
case USB_DEVICE_B_HNP_ENABLE:
|
||||
dum->gadget.b_hnp_enable = 1;
|
||||
break;
|
||||
case USB_DEVICE_A_HNP_SUPPORT:
|
||||
dum->gadget.a_hnp_support = 1;
|
||||
break;
|
||||
case USB_DEVICE_A_ALT_HNP_SUPPORT:
|
||||
dum->gadget.a_alt_hnp_support = 1;
|
||||
break;
|
||||
default:
|
||||
ret_val = -EOPNOTSUPP;
|
||||
}
|
||||
if (ret_val == 0) {
|
||||
dum->devstatus |= (1 << w_value);
|
||||
*status = 0;
|
||||
}
|
||||
} else if (setup->bRequestType == Ep_Request) {
|
||||
/* endpoint halt */
|
||||
ep2 = find_endpoint(dum, w_index);
|
||||
if (!ep2 || ep2->ep.name == ep0name) {
|
||||
ret_val = -EOPNOTSUPP;
|
||||
break;
|
||||
}
|
||||
ep2->halted = 1;
|
||||
ret_val = 0;
|
||||
*status = 0;
|
||||
}
|
||||
break;
|
||||
case USB_REQ_CLEAR_FEATURE:
|
||||
if (setup->bRequestType == Dev_Request) {
|
||||
ret_val = 0;
|
||||
switch (w_value) {
|
||||
case USB_DEVICE_REMOTE_WAKEUP:
|
||||
w_value = USB_DEVICE_REMOTE_WAKEUP;
|
||||
break;
|
||||
default:
|
||||
ret_val = -EOPNOTSUPP;
|
||||
break;
|
||||
}
|
||||
if (ret_val == 0) {
|
||||
dum->devstatus &= ~(1 << w_value);
|
||||
*status = 0;
|
||||
}
|
||||
} else if (setup->bRequestType == Ep_Request) {
|
||||
/* endpoint halt */
|
||||
ep2 = find_endpoint(dum, w_index);
|
||||
if (!ep2) {
|
||||
ret_val = -EOPNOTSUPP;
|
||||
break;
|
||||
}
|
||||
if (!ep2->wedged)
|
||||
ep2->halted = 0;
|
||||
ret_val = 0;
|
||||
*status = 0;
|
||||
}
|
||||
break;
|
||||
case USB_REQ_GET_STATUS:
|
||||
if (setup->bRequestType == Dev_InRequest
|
||||
|| setup->bRequestType == Intf_InRequest
|
||||
|| setup->bRequestType == Ep_InRequest) {
|
||||
char *buf;
|
||||
/*
|
||||
* device: remote wakeup, selfpowered
|
||||
* interface: nothing
|
||||
* endpoint: halt
|
||||
*/
|
||||
buf = (char *)urb->transfer_buffer;
|
||||
if (urb->transfer_buffer_length > 0) {
|
||||
if (setup->bRequestType == Ep_InRequest) {
|
||||
ep2 = find_endpoint(dum, w_index);
|
||||
if (!ep2) {
|
||||
ret_val = -EOPNOTSUPP;
|
||||
break;
|
||||
}
|
||||
buf[0] = ep2->halted;
|
||||
} else if (setup->bRequestType ==
|
||||
Dev_InRequest) {
|
||||
buf[0] = (u8)dum->devstatus;
|
||||
} else
|
||||
buf[0] = 0;
|
||||
}
|
||||
if (urb->transfer_buffer_length > 1)
|
||||
buf[1] = 0;
|
||||
urb->actual_length = min_t(u32, 2,
|
||||
urb->transfer_buffer_length);
|
||||
ret_val = 0;
|
||||
*status = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
/* drive both sides of the transfers; looks like irq handlers to
|
||||
* both drivers except the callbacks aren't in_irq().
|
||||
*/
|
||||
@@ -1299,14 +1432,8 @@ restart:
|
||||
if (ep == &dum->ep [0] && ep->setup_stage) {
|
||||
struct usb_ctrlrequest setup;
|
||||
int value = 1;
|
||||
struct dummy_ep *ep2;
|
||||
unsigned w_index;
|
||||
unsigned w_value;
|
||||
|
||||
setup = *(struct usb_ctrlrequest*) urb->setup_packet;
|
||||
w_index = le16_to_cpu(setup.wIndex);
|
||||
w_value = le16_to_cpu(setup.wValue);
|
||||
|
||||
/* paranoia, in case of stale queued data */
|
||||
list_for_each_entry (req, &ep->queue, queue) {
|
||||
list_del_init (&req->queue);
|
||||
@@ -1328,117 +1455,9 @@ restart:
|
||||
ep->last_io = jiffies;
|
||||
ep->setup_stage = 0;
|
||||
ep->halted = 0;
|
||||
switch (setup.bRequest) {
|
||||
case USB_REQ_SET_ADDRESS:
|
||||
if (setup.bRequestType != Dev_Request)
|
||||
break;
|
||||
dum->address = w_value;
|
||||
status = 0;
|
||||
dev_dbg (udc_dev(dum), "set_address = %d\n",
|
||||
w_value);
|
||||
value = 0;
|
||||
break;
|
||||
case USB_REQ_SET_FEATURE:
|
||||
if (setup.bRequestType == Dev_Request) {
|
||||
value = 0;
|
||||
switch (w_value) {
|
||||
case USB_DEVICE_REMOTE_WAKEUP:
|
||||
break;
|
||||
case USB_DEVICE_B_HNP_ENABLE:
|
||||
dum->gadget.b_hnp_enable = 1;
|
||||
break;
|
||||
case USB_DEVICE_A_HNP_SUPPORT:
|
||||
dum->gadget.a_hnp_support = 1;
|
||||
break;
|
||||
case USB_DEVICE_A_ALT_HNP_SUPPORT:
|
||||
dum->gadget.a_alt_hnp_support
|
||||
= 1;
|
||||
break;
|
||||
default:
|
||||
value = -EOPNOTSUPP;
|
||||
}
|
||||
if (value == 0) {
|
||||
dum->devstatus |=
|
||||
(1 << w_value);
|
||||
status = 0;
|
||||
}
|
||||
|
||||
} else if (setup.bRequestType == Ep_Request) {
|
||||
// endpoint halt
|
||||
ep2 = find_endpoint (dum, w_index);
|
||||
if (!ep2 || ep2->ep.name == ep0name) {
|
||||
value = -EOPNOTSUPP;
|
||||
break;
|
||||
}
|
||||
ep2->halted = 1;
|
||||
value = 0;
|
||||
status = 0;
|
||||
}
|
||||
break;
|
||||
case USB_REQ_CLEAR_FEATURE:
|
||||
if (setup.bRequestType == Dev_Request) {
|
||||
switch (w_value) {
|
||||
case USB_DEVICE_REMOTE_WAKEUP:
|
||||
dum->devstatus &= ~(1 <<
|
||||
USB_DEVICE_REMOTE_WAKEUP);
|
||||
value = 0;
|
||||
status = 0;
|
||||
break;
|
||||
default:
|
||||
value = -EOPNOTSUPP;
|
||||
break;
|
||||
}
|
||||
} else if (setup.bRequestType == Ep_Request) {
|
||||
// endpoint halt
|
||||
ep2 = find_endpoint (dum, w_index);
|
||||
if (!ep2) {
|
||||
value = -EOPNOTSUPP;
|
||||
break;
|
||||
}
|
||||
if (!ep2->wedged)
|
||||
ep2->halted = 0;
|
||||
value = 0;
|
||||
status = 0;
|
||||
}
|
||||
break;
|
||||
case USB_REQ_GET_STATUS:
|
||||
if (setup.bRequestType == Dev_InRequest
|
||||
|| setup.bRequestType
|
||||
== Intf_InRequest
|
||||
|| setup.bRequestType
|
||||
== Ep_InRequest
|
||||
) {
|
||||
char *buf;
|
||||
|
||||
// device: remote wakeup, selfpowered
|
||||
// interface: nothing
|
||||
// endpoint: halt
|
||||
buf = (char *)urb->transfer_buffer;
|
||||
if (urb->transfer_buffer_length > 0) {
|
||||
if (setup.bRequestType ==
|
||||
Ep_InRequest) {
|
||||
ep2 = find_endpoint (dum, w_index);
|
||||
if (!ep2) {
|
||||
value = -EOPNOTSUPP;
|
||||
break;
|
||||
}
|
||||
buf [0] = ep2->halted;
|
||||
} else if (setup.bRequestType ==
|
||||
Dev_InRequest) {
|
||||
buf [0] = (u8)
|
||||
dum->devstatus;
|
||||
} else
|
||||
buf [0] = 0;
|
||||
}
|
||||
if (urb->transfer_buffer_length > 1)
|
||||
buf [1] = 0;
|
||||
urb->actual_length = min_t(u32, 2,
|
||||
urb->transfer_buffer_length);
|
||||
value = 0;
|
||||
status = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
value = handle_control_request(dum, urb, &setup,
|
||||
&status);
|
||||
|
||||
/* gadget driver handles all other requests. block
|
||||
* until setup() returns; no reentrancy issues etc.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
1407
drivers/usb/gadget/f_ncm.c
Normal file
1407
drivers/usb/gadget/f_ncm.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -3392,25 +3392,28 @@ static int __init fsg_bind(struct usb_gadget *gadget)
|
||||
dev_set_name(&curlun->dev,"%s-lun%d",
|
||||
dev_name(&gadget->dev), i);
|
||||
|
||||
if ((rc = device_register(&curlun->dev)) != 0) {
|
||||
kref_get(&fsg->ref);
|
||||
rc = device_register(&curlun->dev);
|
||||
if (rc) {
|
||||
INFO(fsg, "failed to register LUN%d: %d\n", i, rc);
|
||||
goto out;
|
||||
}
|
||||
if ((rc = device_create_file(&curlun->dev,
|
||||
&dev_attr_ro)) != 0 ||
|
||||
(rc = device_create_file(&curlun->dev,
|
||||
&dev_attr_nofua)) != 0 ||
|
||||
(rc = device_create_file(&curlun->dev,
|
||||
&dev_attr_file)) != 0) {
|
||||
device_unregister(&curlun->dev);
|
||||
put_device(&curlun->dev);
|
||||
goto out;
|
||||
}
|
||||
curlun->registered = 1;
|
||||
kref_get(&fsg->ref);
|
||||
|
||||
rc = device_create_file(&curlun->dev, &dev_attr_ro);
|
||||
if (rc)
|
||||
goto out;
|
||||
rc = device_create_file(&curlun->dev, &dev_attr_nofua);
|
||||
if (rc)
|
||||
goto out;
|
||||
rc = device_create_file(&curlun->dev, &dev_attr_file);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
if (mod_data.file[i] && *mod_data.file[i]) {
|
||||
if ((rc = fsg_lun_open(curlun,
|
||||
mod_data.file[i])) != 0)
|
||||
rc = fsg_lun_open(curlun, mod_data.file[i]);
|
||||
if (rc)
|
||||
goto out;
|
||||
} else if (!mod_data.removable) {
|
||||
ERROR(fsg, "no file given for LUN%d\n", i);
|
||||
|
||||
@@ -1,7 +1,29 @@
|
||||
/*
|
||||
* g_ffs.c -- user mode file system API for USB composite function controllers
|
||||
*
|
||||
* Copyright (C) 2010 Samsung Electronics
|
||||
* Author: Michal Nazarewicz <m.nazarewicz@samsung.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "g_ffs: " fmt
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/utsname.h>
|
||||
|
||||
|
||||
/*
|
||||
* kbuild is not very cooperative with respect to linking separately
|
||||
* compiled library objects into one module. So for now we won't use
|
||||
@@ -43,7 +65,6 @@ static int eth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]);
|
||||
|
||||
#include "f_fs.c"
|
||||
|
||||
|
||||
#define DRIVER_NAME "g_ffs"
|
||||
#define DRIVER_DESC "USB Function Filesystem"
|
||||
#define DRIVER_VERSION "24 Aug 2004"
|
||||
@@ -73,8 +94,6 @@ MODULE_PARM_DESC(bDeviceSubClass, "USB Device subclass");
|
||||
module_param_named(bDeviceProtocol, gfs_dev_desc.bDeviceProtocol, byte, 0644);
|
||||
MODULE_PARM_DESC(bDeviceProtocol, "USB Device protocol");
|
||||
|
||||
|
||||
|
||||
static const struct usb_descriptor_header *gfs_otg_desc[] = {
|
||||
(const struct usb_descriptor_header *)
|
||||
&(const struct usb_otg_descriptor) {
|
||||
@@ -91,8 +110,7 @@ static const struct usb_descriptor_header *gfs_otg_desc[] = {
|
||||
NULL
|
||||
};
|
||||
|
||||
/* string IDs are assigned dynamically */
|
||||
|
||||
/* String IDs are assigned dynamically */
|
||||
static struct usb_string gfs_strings[] = {
|
||||
#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
|
||||
{ .s = "FunctionFS + RNDIS" },
|
||||
@@ -114,8 +132,6 @@ static struct usb_gadget_strings *gfs_dev_strings[] = {
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct gfs_configuration {
|
||||
struct usb_configuration c;
|
||||
int (*eth)(struct usb_configuration *c, u8 *ethaddr);
|
||||
@@ -138,7 +154,6 @@ struct gfs_configuration {
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
static int gfs_bind(struct usb_composite_dev *cdev);
|
||||
static int gfs_unbind(struct usb_composite_dev *cdev);
|
||||
static int gfs_do_config(struct usb_configuration *c);
|
||||
@@ -151,11 +166,9 @@ static struct usb_composite_driver gfs_driver = {
|
||||
.iProduct = DRIVER_DESC,
|
||||
};
|
||||
|
||||
|
||||
static struct ffs_data *gfs_ffs_data;
|
||||
static unsigned long gfs_registered;
|
||||
|
||||
|
||||
static int gfs_init(void)
|
||||
{
|
||||
ENTER();
|
||||
@@ -175,7 +188,6 @@ static void gfs_exit(void)
|
||||
}
|
||||
module_exit(gfs_exit);
|
||||
|
||||
|
||||
static int functionfs_ready_callback(struct ffs_data *ffs)
|
||||
{
|
||||
int ret;
|
||||
@@ -200,14 +212,11 @@ static void functionfs_closed_callback(struct ffs_data *ffs)
|
||||
usb_composite_unregister(&gfs_driver);
|
||||
}
|
||||
|
||||
|
||||
static int functionfs_check_dev_callback(const char *dev_name)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int gfs_bind(struct usb_composite_dev *cdev)
|
||||
{
|
||||
int ret, i;
|
||||
@@ -274,7 +283,6 @@ static int gfs_unbind(struct usb_composite_dev *cdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int gfs_do_config(struct usb_configuration *c)
|
||||
{
|
||||
struct gfs_configuration *gc =
|
||||
@@ -315,7 +323,6 @@ static int gfs_do_config(struct usb_configuration *c)
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_USB_FUNCTIONFS_ETH
|
||||
|
||||
static int eth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN])
|
||||
|
||||
@@ -96,7 +96,7 @@
|
||||
|
||||
/* Mentor high speed "dual role" controller, in peripheral role */
|
||||
#ifdef CONFIG_USB_GADGET_MUSB_HDRC
|
||||
#define gadget_is_musbhdrc(g) !strcmp("musb_hdrc", (g)->name)
|
||||
#define gadget_is_musbhdrc(g) !strcmp("musb-hdrc", (g)->name)
|
||||
#else
|
||||
#define gadget_is_musbhdrc(g) 0
|
||||
#endif
|
||||
@@ -120,10 +120,10 @@
|
||||
#define gadget_is_fsl_qe(g) 0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_GADGET_CI13XXX
|
||||
#define gadget_is_ci13xxx(g) (!strcmp("ci13xxx_udc", (g)->name))
|
||||
#ifdef CONFIG_USB_GADGET_CI13XXX_PCI
|
||||
#define gadget_is_ci13xxx_pci(g) (!strcmp("ci13xxx_pci", (g)->name))
|
||||
#else
|
||||
#define gadget_is_ci13xxx(g) 0
|
||||
#define gadget_is_ci13xxx_pci(g) 0
|
||||
#endif
|
||||
|
||||
// CONFIG_USB_GADGET_SX2
|
||||
@@ -142,6 +142,17 @@
|
||||
#define gadget_is_s3c_hsotg(g) 0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_GADGET_EG20T
|
||||
#define gadget_is_pch(g) (!strcmp("pch_udc", (g)->name))
|
||||
#else
|
||||
#define gadget_is_pch(g) 0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_GADGET_CI13XXX_MSM
|
||||
#define gadget_is_ci13xxx_msm(g) (!strcmp("ci13xxx_msm", (g)->name))
|
||||
#else
|
||||
#define gadget_is_ci13xxx_msm(g) 0
|
||||
#endif
|
||||
|
||||
/**
|
||||
* usb_gadget_controller_number - support bcdDevice id convention
|
||||
@@ -192,7 +203,7 @@ 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))
|
||||
else if (gadget_is_ci13xxx_pci(gadget))
|
||||
return 0x23;
|
||||
else if (gadget_is_langwell(gadget))
|
||||
return 0x24;
|
||||
@@ -200,6 +211,10 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget)
|
||||
return 0x25;
|
||||
else if (gadget_is_s3c_hsotg(gadget))
|
||||
return 0x26;
|
||||
else if (gadget_is_pch(gadget))
|
||||
return 0x27;
|
||||
else if (gadget_is_ci13xxx_msm(gadget))
|
||||
return 0x28;
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
|
||||
@@ -1191,13 +1191,17 @@ static irqreturn_t imx_udc_ctrl_irq(int irq, void *dev)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
#ifndef MX1_INT_USBD0
|
||||
#define MX1_INT_USBD0 MX1_USBD_INT0
|
||||
#endif
|
||||
|
||||
static irqreturn_t imx_udc_bulk_irq(int irq, void *dev)
|
||||
{
|
||||
struct imx_udc_struct *imx_usb = dev;
|
||||
struct imx_ep_struct *imx_ep = &imx_usb->imx_ep[irq - USBD_INT0];
|
||||
struct imx_ep_struct *imx_ep = &imx_usb->imx_ep[irq - MX1_INT_USBD0];
|
||||
int intr = __raw_readl(imx_usb->base + USB_EP_INTR(EP_NO(imx_ep)));
|
||||
|
||||
dump_ep_intr(__func__, irq - USBD_INT0, intr, imx_usb->dev);
|
||||
dump_ep_intr(__func__, irq - MX1_INT_USBD0, intr, imx_usb->dev);
|
||||
|
||||
if (!imx_usb->driver) {
|
||||
__raw_writel(intr, imx_usb->base + USB_EP_INTR(EP_NO(imx_ep)));
|
||||
|
||||
@@ -23,9 +23,6 @@
|
||||
/* 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 */
|
||||
|
||||
@@ -2225,6 +2225,7 @@ static void handle_setup_packet(struct langwell_udc *dev,
|
||||
u16 wValue = le16_to_cpu(setup->wValue);
|
||||
u16 wIndex = le16_to_cpu(setup->wIndex);
|
||||
u16 wLength = le16_to_cpu(setup->wLength);
|
||||
u32 portsc1;
|
||||
|
||||
dev_vdbg(&dev->pdev->dev, "---> %s()\n", __func__);
|
||||
|
||||
@@ -2313,6 +2314,28 @@ static void handle_setup_packet(struct langwell_udc *dev,
|
||||
dev->dev_status &= ~(1 << wValue);
|
||||
}
|
||||
break;
|
||||
case USB_DEVICE_TEST_MODE:
|
||||
dev_dbg(&dev->pdev->dev, "SETUP: TEST MODE\n");
|
||||
if ((wIndex & 0xff) ||
|
||||
(dev->gadget.speed != USB_SPEED_HIGH))
|
||||
ep0_stall(dev);
|
||||
|
||||
switch (wIndex >> 8) {
|
||||
case TEST_J:
|
||||
case TEST_K:
|
||||
case TEST_SE0_NAK:
|
||||
case TEST_PACKET:
|
||||
case TEST_FORCE_EN:
|
||||
if (prime_status_phase(dev, EP_DIR_IN))
|
||||
ep0_stall(dev);
|
||||
portsc1 = readl(&dev->op_regs->portsc1);
|
||||
portsc1 |= (wIndex & 0xf00) << 8;
|
||||
writel(portsc1, &dev->op_regs->portsc1);
|
||||
goto end;
|
||||
default:
|
||||
rc = -EOPNOTSUPP;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
rc = -EOPNOTSUPP;
|
||||
break;
|
||||
|
||||
@@ -102,7 +102,7 @@ static struct fsg_module_parameters mod_data = {
|
||||
};
|
||||
FSG_MODULE_PARAMETERS(/* no prefix */, mod_data);
|
||||
|
||||
static unsigned long msg_registered = 0;
|
||||
static unsigned long msg_registered;
|
||||
static void msg_cleanup(void);
|
||||
|
||||
static int msg_thread_exits(struct fsg_common *common)
|
||||
|
||||
294
drivers/usb/gadget/mv_udc.h
Normal file
294
drivers/usb/gadget/mv_udc.h
Normal file
@@ -0,0 +1,294 @@
|
||||
|
||||
#ifndef __MV_UDC_H
|
||||
#define __MV_UDC_H
|
||||
|
||||
#define VUSBHS_MAX_PORTS 8
|
||||
|
||||
#define DQH_ALIGNMENT 2048
|
||||
#define DTD_ALIGNMENT 64
|
||||
#define DMA_BOUNDARY 4096
|
||||
|
||||
#define EP_DIR_IN 1
|
||||
#define EP_DIR_OUT 0
|
||||
|
||||
#define DMA_ADDR_INVALID (~(dma_addr_t)0)
|
||||
|
||||
#define EP0_MAX_PKT_SIZE 64
|
||||
/* ep0 transfer state */
|
||||
#define WAIT_FOR_SETUP 0
|
||||
#define DATA_STATE_XMIT 1
|
||||
#define DATA_STATE_NEED_ZLP 2
|
||||
#define WAIT_FOR_OUT_STATUS 3
|
||||
#define DATA_STATE_RECV 4
|
||||
|
||||
#define CAPLENGTH_MASK (0xff)
|
||||
#define DCCPARAMS_DEN_MASK (0x1f)
|
||||
|
||||
#define HCSPARAMS_PPC (0x10)
|
||||
|
||||
/* Frame Index Register Bit Masks */
|
||||
#define USB_FRINDEX_MASKS 0x3fff
|
||||
|
||||
/* Command Register Bit Masks */
|
||||
#define USBCMD_RUN_STOP (0x00000001)
|
||||
#define USBCMD_CTRL_RESET (0x00000002)
|
||||
#define USBCMD_SETUP_TRIPWIRE_SET (0x00002000)
|
||||
#define USBCMD_SETUP_TRIPWIRE_CLEAR (~USBCMD_SETUP_TRIPWIRE_SET)
|
||||
|
||||
#define USBCMD_ATDTW_TRIPWIRE_SET (0x00004000)
|
||||
#define USBCMD_ATDTW_TRIPWIRE_CLEAR (~USBCMD_ATDTW_TRIPWIRE_SET)
|
||||
|
||||
/* bit 15,3,2 are for frame list size */
|
||||
#define USBCMD_FRAME_SIZE_1024 (0x00000000) /* 000 */
|
||||
#define USBCMD_FRAME_SIZE_512 (0x00000004) /* 001 */
|
||||
#define USBCMD_FRAME_SIZE_256 (0x00000008) /* 010 */
|
||||
#define USBCMD_FRAME_SIZE_128 (0x0000000C) /* 011 */
|
||||
#define USBCMD_FRAME_SIZE_64 (0x00008000) /* 100 */
|
||||
#define USBCMD_FRAME_SIZE_32 (0x00008004) /* 101 */
|
||||
#define USBCMD_FRAME_SIZE_16 (0x00008008) /* 110 */
|
||||
#define USBCMD_FRAME_SIZE_8 (0x0000800C) /* 111 */
|
||||
|
||||
#define EPCTRL_TX_ALL_MASK (0xFFFF0000)
|
||||
#define EPCTRL_RX_ALL_MASK (0x0000FFFF)
|
||||
|
||||
#define EPCTRL_TX_DATA_TOGGLE_RST (0x00400000)
|
||||
#define EPCTRL_TX_EP_STALL (0x00010000)
|
||||
#define EPCTRL_RX_EP_STALL (0x00000001)
|
||||
#define EPCTRL_RX_DATA_TOGGLE_RST (0x00000040)
|
||||
#define EPCTRL_RX_ENABLE (0x00000080)
|
||||
#define EPCTRL_TX_ENABLE (0x00800000)
|
||||
#define EPCTRL_CONTROL (0x00000000)
|
||||
#define EPCTRL_ISOCHRONOUS (0x00040000)
|
||||
#define EPCTRL_BULK (0x00080000)
|
||||
#define EPCTRL_INT (0x000C0000)
|
||||
#define EPCTRL_TX_TYPE (0x000C0000)
|
||||
#define EPCTRL_RX_TYPE (0x0000000C)
|
||||
#define EPCTRL_DATA_TOGGLE_INHIBIT (0x00000020)
|
||||
#define EPCTRL_TX_EP_TYPE_SHIFT (18)
|
||||
#define EPCTRL_RX_EP_TYPE_SHIFT (2)
|
||||
|
||||
#define EPCOMPLETE_MAX_ENDPOINTS (16)
|
||||
|
||||
/* endpoint list address bit masks */
|
||||
#define USB_EP_LIST_ADDRESS_MASK 0xfffff800
|
||||
|
||||
#define PORTSCX_W1C_BITS 0x2a
|
||||
#define PORTSCX_PORT_RESET 0x00000100
|
||||
#define PORTSCX_PORT_POWER 0x00001000
|
||||
#define PORTSCX_FORCE_FULL_SPEED_CONNECT 0x01000000
|
||||
#define PORTSCX_PAR_XCVR_SELECT 0xC0000000
|
||||
#define PORTSCX_PORT_FORCE_RESUME 0x00000040
|
||||
#define PORTSCX_PORT_SUSPEND 0x00000080
|
||||
#define PORTSCX_PORT_SPEED_FULL 0x00000000
|
||||
#define PORTSCX_PORT_SPEED_LOW 0x04000000
|
||||
#define PORTSCX_PORT_SPEED_HIGH 0x08000000
|
||||
#define PORTSCX_PORT_SPEED_MASK 0x0C000000
|
||||
|
||||
/* USB MODE Register Bit Masks */
|
||||
#define USBMODE_CTRL_MODE_IDLE 0x00000000
|
||||
#define USBMODE_CTRL_MODE_DEVICE 0x00000002
|
||||
#define USBMODE_CTRL_MODE_HOST 0x00000003
|
||||
#define USBMODE_CTRL_MODE_RSV 0x00000001
|
||||
#define USBMODE_SETUP_LOCK_OFF 0x00000008
|
||||
#define USBMODE_STREAM_DISABLE 0x00000010
|
||||
|
||||
/* USB STS Register Bit Masks */
|
||||
#define USBSTS_INT 0x00000001
|
||||
#define USBSTS_ERR 0x00000002
|
||||
#define USBSTS_PORT_CHANGE 0x00000004
|
||||
#define USBSTS_FRM_LST_ROLL 0x00000008
|
||||
#define USBSTS_SYS_ERR 0x00000010
|
||||
#define USBSTS_IAA 0x00000020
|
||||
#define USBSTS_RESET 0x00000040
|
||||
#define USBSTS_SOF 0x00000080
|
||||
#define USBSTS_SUSPEND 0x00000100
|
||||
#define USBSTS_HC_HALTED 0x00001000
|
||||
#define USBSTS_RCL 0x00002000
|
||||
#define USBSTS_PERIODIC_SCHEDULE 0x00004000
|
||||
#define USBSTS_ASYNC_SCHEDULE 0x00008000
|
||||
|
||||
|
||||
/* Interrupt Enable Register Bit Masks */
|
||||
#define USBINTR_INT_EN (0x00000001)
|
||||
#define USBINTR_ERR_INT_EN (0x00000002)
|
||||
#define USBINTR_PORT_CHANGE_DETECT_EN (0x00000004)
|
||||
|
||||
#define USBINTR_ASYNC_ADV_AAE (0x00000020)
|
||||
#define USBINTR_ASYNC_ADV_AAE_ENABLE (0x00000020)
|
||||
#define USBINTR_ASYNC_ADV_AAE_DISABLE (0xFFFFFFDF)
|
||||
|
||||
#define USBINTR_RESET_EN (0x00000040)
|
||||
#define USBINTR_SOF_UFRAME_EN (0x00000080)
|
||||
#define USBINTR_DEVICE_SUSPEND (0x00000100)
|
||||
|
||||
#define USB_DEVICE_ADDRESS_MASK (0xfe000000)
|
||||
#define USB_DEVICE_ADDRESS_BIT_SHIFT (25)
|
||||
|
||||
struct mv_cap_regs {
|
||||
u32 caplength_hciversion;
|
||||
u32 hcsparams; /* HC structural parameters */
|
||||
u32 hccparams; /* HC Capability Parameters*/
|
||||
u32 reserved[5];
|
||||
u32 dciversion; /* DC version number and reserved 16 bits */
|
||||
u32 dccparams; /* DC Capability Parameters */
|
||||
};
|
||||
|
||||
struct mv_op_regs {
|
||||
u32 usbcmd; /* Command register */
|
||||
u32 usbsts; /* Status register */
|
||||
u32 usbintr; /* Interrupt enable */
|
||||
u32 frindex; /* Frame index */
|
||||
u32 reserved1[1];
|
||||
u32 deviceaddr; /* Device Address */
|
||||
u32 eplistaddr; /* Endpoint List Address */
|
||||
u32 ttctrl; /* HOST TT status and control */
|
||||
u32 burstsize; /* Programmable Burst Size */
|
||||
u32 txfilltuning; /* Host Transmit Pre-Buffer Packet Tuning */
|
||||
u32 reserved[4];
|
||||
u32 epnak; /* Endpoint NAK */
|
||||
u32 epnaken; /* Endpoint NAK Enable */
|
||||
u32 configflag; /* Configured Flag register */
|
||||
u32 portsc[VUSBHS_MAX_PORTS]; /* Port Status/Control x, x = 1..8 */
|
||||
u32 otgsc;
|
||||
u32 usbmode; /* USB Host/Device mode */
|
||||
u32 epsetupstat; /* Endpoint Setup Status */
|
||||
u32 epprime; /* Endpoint Initialize */
|
||||
u32 epflush; /* Endpoint De-initialize */
|
||||
u32 epstatus; /* Endpoint Status */
|
||||
u32 epcomplete; /* Endpoint Interrupt On Complete */
|
||||
u32 epctrlx[16]; /* Endpoint Control, where x = 0.. 15 */
|
||||
u32 mcr; /* Mux Control */
|
||||
u32 isr; /* Interrupt Status */
|
||||
u32 ier; /* Interrupt Enable */
|
||||
};
|
||||
|
||||
struct mv_udc {
|
||||
struct usb_gadget gadget;
|
||||
struct usb_gadget_driver *driver;
|
||||
spinlock_t lock;
|
||||
struct completion *done;
|
||||
struct platform_device *dev;
|
||||
int irq;
|
||||
|
||||
struct mv_cap_regs __iomem *cap_regs;
|
||||
struct mv_op_regs __iomem *op_regs;
|
||||
unsigned int phy_regs;
|
||||
unsigned int max_eps;
|
||||
struct mv_dqh *ep_dqh;
|
||||
size_t ep_dqh_size;
|
||||
dma_addr_t ep_dqh_dma;
|
||||
|
||||
struct dma_pool *dtd_pool;
|
||||
struct mv_ep *eps;
|
||||
|
||||
struct mv_dtd *dtd_head;
|
||||
struct mv_dtd *dtd_tail;
|
||||
unsigned int dtd_entries;
|
||||
|
||||
struct mv_req *status_req;
|
||||
struct usb_ctrlrequest local_setup_buff;
|
||||
|
||||
unsigned int resume_state; /* USB state to resume */
|
||||
unsigned int usb_state; /* USB current state */
|
||||
unsigned int ep0_state; /* Endpoint zero state */
|
||||
unsigned int ep0_dir;
|
||||
|
||||
unsigned int dev_addr;
|
||||
|
||||
int errors;
|
||||
unsigned softconnect:1,
|
||||
vbus_active:1,
|
||||
remote_wakeup:1,
|
||||
softconnected:1,
|
||||
force_fs:1;
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
/* endpoint data structure */
|
||||
struct mv_ep {
|
||||
struct usb_ep ep;
|
||||
struct mv_udc *udc;
|
||||
struct list_head queue;
|
||||
struct mv_dqh *dqh;
|
||||
const struct usb_endpoint_descriptor *desc;
|
||||
u32 direction;
|
||||
char name[14];
|
||||
unsigned stopped:1,
|
||||
wedge:1,
|
||||
ep_type:2,
|
||||
ep_num:8;
|
||||
};
|
||||
|
||||
/* request data structure */
|
||||
struct mv_req {
|
||||
struct usb_request req;
|
||||
struct mv_dtd *dtd, *head, *tail;
|
||||
struct mv_ep *ep;
|
||||
struct list_head queue;
|
||||
unsigned dtd_count;
|
||||
unsigned mapped:1;
|
||||
};
|
||||
|
||||
#define EP_QUEUE_HEAD_MULT_POS 30
|
||||
#define EP_QUEUE_HEAD_ZLT_SEL 0x20000000
|
||||
#define EP_QUEUE_HEAD_MAX_PKT_LEN_POS 16
|
||||
#define EP_QUEUE_HEAD_MAX_PKT_LEN(ep_info) (((ep_info)>>16)&0x07ff)
|
||||
#define EP_QUEUE_HEAD_IOS 0x00008000
|
||||
#define EP_QUEUE_HEAD_NEXT_TERMINATE 0x00000001
|
||||
#define EP_QUEUE_HEAD_IOC 0x00008000
|
||||
#define EP_QUEUE_HEAD_MULTO 0x00000C00
|
||||
#define EP_QUEUE_HEAD_STATUS_HALT 0x00000040
|
||||
#define EP_QUEUE_HEAD_STATUS_ACTIVE 0x00000080
|
||||
#define EP_QUEUE_CURRENT_OFFSET_MASK 0x00000FFF
|
||||
#define EP_QUEUE_HEAD_NEXT_POINTER_MASK 0xFFFFFFE0
|
||||
#define EP_QUEUE_FRINDEX_MASK 0x000007FF
|
||||
#define EP_MAX_LENGTH_TRANSFER 0x4000
|
||||
|
||||
struct mv_dqh {
|
||||
/* Bits 16..26 Bit 15 is Interrupt On Setup */
|
||||
u32 max_packet_length;
|
||||
u32 curr_dtd_ptr; /* Current dTD Pointer */
|
||||
u32 next_dtd_ptr; /* Next dTD Pointer */
|
||||
/* Total bytes (16..30), IOC (15), INT (8), STS (0-7) */
|
||||
u32 size_ioc_int_sts;
|
||||
u32 buff_ptr0; /* Buffer pointer Page 0 (12-31) */
|
||||
u32 buff_ptr1; /* Buffer pointer Page 1 (12-31) */
|
||||
u32 buff_ptr2; /* Buffer pointer Page 2 (12-31) */
|
||||
u32 buff_ptr3; /* Buffer pointer Page 3 (12-31) */
|
||||
u32 buff_ptr4; /* Buffer pointer Page 4 (12-31) */
|
||||
u32 reserved1;
|
||||
/* 8 bytes of setup data that follows the Setup PID */
|
||||
u8 setup_buffer[8];
|
||||
u32 reserved2[4];
|
||||
};
|
||||
|
||||
|
||||
#define DTD_NEXT_TERMINATE (0x00000001)
|
||||
#define DTD_IOC (0x00008000)
|
||||
#define DTD_STATUS_ACTIVE (0x00000080)
|
||||
#define DTD_STATUS_HALTED (0x00000040)
|
||||
#define DTD_STATUS_DATA_BUFF_ERR (0x00000020)
|
||||
#define DTD_STATUS_TRANSACTION_ERR (0x00000008)
|
||||
#define DTD_RESERVED_FIELDS (0x00007F00)
|
||||
#define DTD_ERROR_MASK (0x68)
|
||||
#define DTD_ADDR_MASK (0xFFFFFFE0)
|
||||
#define DTD_PACKET_SIZE 0x7FFF0000
|
||||
#define DTD_LENGTH_BIT_POS (16)
|
||||
|
||||
struct mv_dtd {
|
||||
u32 dtd_next;
|
||||
u32 size_ioc_sts;
|
||||
u32 buff_ptr0; /* Buffer pointer Page 0 */
|
||||
u32 buff_ptr1; /* Buffer pointer Page 1 */
|
||||
u32 buff_ptr2; /* Buffer pointer Page 2 */
|
||||
u32 buff_ptr3; /* Buffer pointer Page 3 */
|
||||
u32 buff_ptr4; /* Buffer pointer Page 4 */
|
||||
u32 scratch_ptr;
|
||||
/* 32 bytes */
|
||||
dma_addr_t td_dma; /* dma address for this td */
|
||||
struct mv_dtd *next_dtd_virt;
|
||||
};
|
||||
|
||||
extern int mv_udc_phy_init(unsigned int base);
|
||||
|
||||
#endif
|
||||
2149
drivers/usb/gadget/mv_udc_core.c
Normal file
2149
drivers/usb/gadget/mv_udc_core.c
Normal file
File diff suppressed because it is too large
Load Diff
214
drivers/usb/gadget/mv_udc_phy.c
Normal file
214
drivers/usb/gadget/mv_udc_phy.c
Normal file
@@ -0,0 +1,214 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/errno.h>
|
||||
|
||||
#include <mach/cputype.h>
|
||||
|
||||
#ifdef CONFIG_ARCH_MMP
|
||||
|
||||
#define UTMI_REVISION 0x0
|
||||
#define UTMI_CTRL 0x4
|
||||
#define UTMI_PLL 0x8
|
||||
#define UTMI_TX 0xc
|
||||
#define UTMI_RX 0x10
|
||||
#define UTMI_IVREF 0x14
|
||||
#define UTMI_T0 0x18
|
||||
#define UTMI_T1 0x1c
|
||||
#define UTMI_T2 0x20
|
||||
#define UTMI_T3 0x24
|
||||
#define UTMI_T4 0x28
|
||||
#define UTMI_T5 0x2c
|
||||
#define UTMI_RESERVE 0x30
|
||||
#define UTMI_USB_INT 0x34
|
||||
#define UTMI_DBG_CTL 0x38
|
||||
#define UTMI_OTG_ADDON 0x3c
|
||||
|
||||
/* For UTMICTRL Register */
|
||||
#define UTMI_CTRL_USB_CLK_EN (1 << 31)
|
||||
/* pxa168 */
|
||||
#define UTMI_CTRL_SUSPEND_SET1 (1 << 30)
|
||||
#define UTMI_CTRL_SUSPEND_SET2 (1 << 29)
|
||||
#define UTMI_CTRL_RXBUF_PDWN (1 << 24)
|
||||
#define UTMI_CTRL_TXBUF_PDWN (1 << 11)
|
||||
|
||||
#define UTMI_CTRL_INPKT_DELAY_SHIFT 30
|
||||
#define UTMI_CTRL_INPKT_DELAY_SOF_SHIFT 28
|
||||
#define UTMI_CTRL_PU_REF_SHIFT 20
|
||||
#define UTMI_CTRL_ARC_PULLDN_SHIFT 12
|
||||
#define UTMI_CTRL_PLL_PWR_UP_SHIFT 1
|
||||
#define UTMI_CTRL_PWR_UP_SHIFT 0
|
||||
/* For UTMI_PLL Register */
|
||||
#define UTMI_PLL_CLK_BLK_EN_SHIFT 24
|
||||
#define UTMI_PLL_FBDIV_SHIFT 4
|
||||
#define UTMI_PLL_REFDIV_SHIFT 0
|
||||
#define UTMI_PLL_FBDIV_MASK 0x00000FF0
|
||||
#define UTMI_PLL_REFDIV_MASK 0x0000000F
|
||||
#define UTMI_PLL_ICP_MASK 0x00007000
|
||||
#define UTMI_PLL_KVCO_MASK 0x00031000
|
||||
#define UTMI_PLL_PLLCALI12_SHIFT 29
|
||||
#define UTMI_PLL_PLLCALI12_MASK (0x3 << 29)
|
||||
#define UTMI_PLL_PLLVDD18_SHIFT 27
|
||||
#define UTMI_PLL_PLLVDD18_MASK (0x3 << 27)
|
||||
#define UTMI_PLL_PLLVDD12_SHIFT 25
|
||||
#define UTMI_PLL_PLLVDD12_MASK (0x3 << 25)
|
||||
#define UTMI_PLL_KVCO_SHIFT 15
|
||||
#define UTMI_PLL_ICP_SHIFT 12
|
||||
/* For UTMI_TX Register */
|
||||
#define UTMI_TX_REG_EXT_FS_RCAL_SHIFT 27
|
||||
#define UTMI_TX_REG_EXT_FS_RCAL_MASK (0xf << 27)
|
||||
#define UTMI_TX_REG_EXT_FS_RCAL_EN_MASK 26
|
||||
#define UTMI_TX_REG_EXT_FS_RCAL_EN (0x1 << 26)
|
||||
#define UTMI_TX_LOW_VDD_EN_SHIFT 11
|
||||
#define UTMI_TX_IMPCAL_VTH_SHIFT 14
|
||||
#define UTMI_TX_IMPCAL_VTH_MASK (0x7 << 14)
|
||||
#define UTMI_TX_CK60_PHSEL_SHIFT 17
|
||||
#define UTMI_TX_CK60_PHSEL_MASK (0xf << 17)
|
||||
#define UTMI_TX_TXVDD12_SHIFT 22
|
||||
#define UTMI_TX_TXVDD12_MASK (0x3 << 22)
|
||||
#define UTMI_TX_AMP_SHIFT 0
|
||||
#define UTMI_TX_AMP_MASK (0x7 << 0)
|
||||
/* For UTMI_RX Register */
|
||||
#define UTMI_RX_SQ_THRESH_SHIFT 4
|
||||
#define UTMI_RX_SQ_THRESH_MASK (0xf << 4)
|
||||
#define UTMI_REG_SQ_LENGTH_SHIFT 15
|
||||
#define UTMI_REG_SQ_LENGTH_MASK (0x3 << 15)
|
||||
|
||||
#define REG_RCAL_START 0x00001000
|
||||
#define VCOCAL_START 0x00200000
|
||||
#define KVCO_EXT 0x00400000
|
||||
#define PLL_READY 0x00800000
|
||||
#define CLK_BLK_EN 0x01000000
|
||||
#endif
|
||||
|
||||
static unsigned int u2o_read(unsigned int base, unsigned int offset)
|
||||
{
|
||||
return readl(base + offset);
|
||||
}
|
||||
|
||||
static void u2o_set(unsigned int base, unsigned int offset, unsigned int value)
|
||||
{
|
||||
unsigned int reg;
|
||||
|
||||
reg = readl(base + offset);
|
||||
reg |= value;
|
||||
writel(reg, base + offset);
|
||||
readl(base + offset);
|
||||
}
|
||||
|
||||
static void u2o_clear(unsigned int base, unsigned int offset,
|
||||
unsigned int value)
|
||||
{
|
||||
unsigned int reg;
|
||||
|
||||
reg = readl(base + offset);
|
||||
reg &= ~value;
|
||||
writel(reg, base + offset);
|
||||
readl(base + offset);
|
||||
}
|
||||
|
||||
static void u2o_write(unsigned int base, unsigned int offset,
|
||||
unsigned int value)
|
||||
{
|
||||
writel(value, base + offset);
|
||||
readl(base + offset);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ARCH_MMP
|
||||
int mv_udc_phy_init(unsigned int base)
|
||||
{
|
||||
unsigned long timeout;
|
||||
|
||||
/* Initialize the USB PHY power */
|
||||
if (cpu_is_pxa910()) {
|
||||
u2o_set(base, UTMI_CTRL, (1 << UTMI_CTRL_INPKT_DELAY_SOF_SHIFT)
|
||||
| (1 << UTMI_CTRL_PU_REF_SHIFT));
|
||||
}
|
||||
|
||||
u2o_set(base, UTMI_CTRL, 1 << UTMI_CTRL_PLL_PWR_UP_SHIFT);
|
||||
u2o_set(base, UTMI_CTRL, 1 << UTMI_CTRL_PWR_UP_SHIFT);
|
||||
|
||||
/* UTMI_PLL settings */
|
||||
u2o_clear(base, UTMI_PLL, UTMI_PLL_PLLVDD18_MASK
|
||||
| UTMI_PLL_PLLVDD12_MASK | UTMI_PLL_PLLCALI12_MASK
|
||||
| UTMI_PLL_FBDIV_MASK | UTMI_PLL_REFDIV_MASK
|
||||
| UTMI_PLL_ICP_MASK | UTMI_PLL_KVCO_MASK);
|
||||
|
||||
u2o_set(base, UTMI_PLL, (0xee << UTMI_PLL_FBDIV_SHIFT)
|
||||
| (0xb << UTMI_PLL_REFDIV_SHIFT)
|
||||
| (3 << UTMI_PLL_PLLVDD18_SHIFT)
|
||||
| (3 << UTMI_PLL_PLLVDD12_SHIFT)
|
||||
| (3 << UTMI_PLL_PLLCALI12_SHIFT)
|
||||
| (1 << UTMI_PLL_ICP_SHIFT) | (3 << UTMI_PLL_KVCO_SHIFT));
|
||||
|
||||
/* UTMI_TX */
|
||||
u2o_clear(base, UTMI_TX, UTMI_TX_REG_EXT_FS_RCAL_EN_MASK
|
||||
| UTMI_TX_TXVDD12_MASK
|
||||
| UTMI_TX_CK60_PHSEL_MASK | UTMI_TX_IMPCAL_VTH_MASK
|
||||
| UTMI_TX_REG_EXT_FS_RCAL_MASK | UTMI_TX_AMP_MASK);
|
||||
u2o_set(base, UTMI_TX, (3 << UTMI_TX_TXVDD12_SHIFT)
|
||||
| (4 << UTMI_TX_CK60_PHSEL_SHIFT)
|
||||
| (4 << UTMI_TX_IMPCAL_VTH_SHIFT)
|
||||
| (8 << UTMI_TX_REG_EXT_FS_RCAL_SHIFT)
|
||||
| (3 << UTMI_TX_AMP_SHIFT));
|
||||
|
||||
/* UTMI_RX */
|
||||
u2o_clear(base, UTMI_RX, UTMI_RX_SQ_THRESH_MASK
|
||||
| UTMI_REG_SQ_LENGTH_MASK);
|
||||
if (cpu_is_pxa168())
|
||||
u2o_set(base, UTMI_RX, (7 << UTMI_RX_SQ_THRESH_SHIFT)
|
||||
| (2 << UTMI_REG_SQ_LENGTH_SHIFT));
|
||||
else
|
||||
u2o_set(base, UTMI_RX, (0x7 << UTMI_RX_SQ_THRESH_SHIFT)
|
||||
| (2 << UTMI_REG_SQ_LENGTH_SHIFT));
|
||||
|
||||
/* UTMI_IVREF */
|
||||
if (cpu_is_pxa168())
|
||||
/*
|
||||
* fixing Microsoft Altair board interface with NEC hub issue -
|
||||
* Set UTMI_IVREF from 0x4a3 to 0x4bf
|
||||
*/
|
||||
u2o_write(base, UTMI_IVREF, 0x4bf);
|
||||
|
||||
/* calibrate */
|
||||
timeout = jiffies + 100;
|
||||
while ((u2o_read(base, UTMI_PLL) & PLL_READY) == 0) {
|
||||
if (time_after(jiffies, timeout))
|
||||
return -ETIME;
|
||||
cpu_relax();
|
||||
}
|
||||
|
||||
/* toggle VCOCAL_START bit of UTMI_PLL */
|
||||
udelay(200);
|
||||
u2o_set(base, UTMI_PLL, VCOCAL_START);
|
||||
udelay(40);
|
||||
u2o_clear(base, UTMI_PLL, VCOCAL_START);
|
||||
|
||||
/* toggle REG_RCAL_START bit of UTMI_TX */
|
||||
udelay(200);
|
||||
u2o_set(base, UTMI_TX, REG_RCAL_START);
|
||||
udelay(40);
|
||||
u2o_clear(base, UTMI_TX, REG_RCAL_START);
|
||||
udelay(200);
|
||||
|
||||
/* make sure phy is ready */
|
||||
timeout = jiffies + 100;
|
||||
while ((u2o_read(base, UTMI_PLL) & PLL_READY) == 0) {
|
||||
if (time_after(jiffies, timeout))
|
||||
return -ETIME;
|
||||
cpu_relax();
|
||||
}
|
||||
|
||||
if (cpu_is_pxa168()) {
|
||||
u2o_set(base, UTMI_RESERVE, 1 << 5);
|
||||
/* Turn on UTMI PHY OTG extension */
|
||||
u2o_write(base, UTMI_OTG_ADDON, 1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
int mv_udc_phy_init(unsigned int base)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
248
drivers/usb/gadget/ncm.c
Normal file
248
drivers/usb/gadget/ncm.c
Normal file
@@ -0,0 +1,248 @@
|
||||
/*
|
||||
* ncm.c -- NCM gadget driver
|
||||
*
|
||||
* Copyright (C) 2010 Nokia Corporation
|
||||
* Contact: Yauheni Kaliuta <yauheni.kaliuta@nokia.com>
|
||||
*
|
||||
* The driver borrows from ether.c which is:
|
||||
*
|
||||
* Copyright (C) 2003-2005,2008 David Brownell
|
||||
* Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger
|
||||
* Copyright (C) 2008 Nokia Corporation
|
||||
*
|
||||
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/* #define DEBUG */
|
||||
/* #define VERBOSE_DEBUG */
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/utsname.h>
|
||||
|
||||
|
||||
#include "u_ether.h"
|
||||
|
||||
#define DRIVER_DESC "NCM Gadget"
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* Kbuild is not very cooperative with respect to linking separately
|
||||
* compiled library objects into one module. So for now we won't use
|
||||
* separate compilation ... ensuring init/exit sections work to shrink
|
||||
* the runtime footprint, and giving us at least some parts of what
|
||||
* a "gcc --combine ... part1.c part2.c part3.c ... " build would.
|
||||
*/
|
||||
#include "composite.c"
|
||||
#include "usbstring.c"
|
||||
#include "config.c"
|
||||
#include "epautoconf.c"
|
||||
|
||||
#include "f_ncm.c"
|
||||
#include "u_ether.c"
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!!
|
||||
* Instead: allocate your own, using normal USB-IF procedures.
|
||||
*/
|
||||
|
||||
/* Thanks to NetChip Technologies for donating this product ID.
|
||||
* It's for devices with only CDC Ethernet configurations.
|
||||
*/
|
||||
#define CDC_VENDOR_NUM 0x0525 /* NetChip */
|
||||
#define CDC_PRODUCT_NUM 0xa4a1 /* Linux-USB Ethernet Gadget */
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static struct usb_device_descriptor device_desc = {
|
||||
.bLength = sizeof device_desc,
|
||||
.bDescriptorType = USB_DT_DEVICE,
|
||||
|
||||
.bcdUSB = cpu_to_le16 (0x0200),
|
||||
|
||||
.bDeviceClass = USB_CLASS_COMM,
|
||||
.bDeviceSubClass = 0,
|
||||
.bDeviceProtocol = 0,
|
||||
/* .bMaxPacketSize0 = f(hardware) */
|
||||
|
||||
/* Vendor and product id defaults change according to what configs
|
||||
* we support. (As does bNumConfigurations.) These values can
|
||||
* also be overridden by module parameters.
|
||||
*/
|
||||
.idVendor = cpu_to_le16 (CDC_VENDOR_NUM),
|
||||
.idProduct = cpu_to_le16 (CDC_PRODUCT_NUM),
|
||||
/* .bcdDevice = f(hardware) */
|
||||
/* .iManufacturer = DYNAMIC */
|
||||
/* .iProduct = DYNAMIC */
|
||||
/* NO SERIAL NUMBER */
|
||||
.bNumConfigurations = 1,
|
||||
};
|
||||
|
||||
static struct usb_otg_descriptor otg_descriptor = {
|
||||
.bLength = sizeof otg_descriptor,
|
||||
.bDescriptorType = USB_DT_OTG,
|
||||
|
||||
/* REVISIT SRP-only hardware is possible, although
|
||||
* it would not be called "OTG" ...
|
||||
*/
|
||||
.bmAttributes = USB_OTG_SRP | USB_OTG_HNP,
|
||||
};
|
||||
|
||||
static const struct usb_descriptor_header *otg_desc[] = {
|
||||
(struct usb_descriptor_header *) &otg_descriptor,
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
||||
/* string IDs are assigned dynamically */
|
||||
|
||||
#define STRING_MANUFACTURER_IDX 0
|
||||
#define STRING_PRODUCT_IDX 1
|
||||
|
||||
static char manufacturer[50];
|
||||
|
||||
static struct usb_string strings_dev[] = {
|
||||
[STRING_MANUFACTURER_IDX].s = manufacturer,
|
||||
[STRING_PRODUCT_IDX].s = DRIVER_DESC,
|
||||
{ } /* end of list */
|
||||
};
|
||||
|
||||
static struct usb_gadget_strings stringtab_dev = {
|
||||
.language = 0x0409, /* en-us */
|
||||
.strings = strings_dev,
|
||||
};
|
||||
|
||||
static struct usb_gadget_strings *dev_strings[] = {
|
||||
&stringtab_dev,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static u8 hostaddr[ETH_ALEN];
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static int __init ncm_do_config(struct usb_configuration *c)
|
||||
{
|
||||
/* FIXME alloc iConfiguration string, set it in c->strings */
|
||||
|
||||
if (gadget_is_otg(c->cdev->gadget)) {
|
||||
c->descriptors = otg_desc;
|
||||
c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
|
||||
}
|
||||
|
||||
return ncm_bind_config(c, hostaddr);
|
||||
}
|
||||
|
||||
static struct usb_configuration ncm_config_driver = {
|
||||
/* .label = f(hardware) */
|
||||
.label = "CDC Ethernet (NCM)",
|
||||
.bConfigurationValue = 1,
|
||||
/* .iConfiguration = DYNAMIC */
|
||||
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static int __init gncm_bind(struct usb_composite_dev *cdev)
|
||||
{
|
||||
int gcnum;
|
||||
struct usb_gadget *gadget = cdev->gadget;
|
||||
int status;
|
||||
|
||||
/* set up network link layer */
|
||||
status = gether_setup(cdev->gadget, hostaddr);
|
||||
if (status < 0)
|
||||
return status;
|
||||
|
||||
gcnum = usb_gadget_controller_number(gadget);
|
||||
if (gcnum >= 0)
|
||||
device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum);
|
||||
else {
|
||||
/* We assume that can_support_ecm() tells the truth;
|
||||
* but if the controller isn't recognized at all then
|
||||
* that assumption is a bit more likely to be wrong.
|
||||
*/
|
||||
dev_warn(&gadget->dev,
|
||||
"controller '%s' not recognized; trying %s\n",
|
||||
gadget->name,
|
||||
ncm_config_driver.label);
|
||||
device_desc.bcdDevice =
|
||||
cpu_to_le16(0x0300 | 0x0099);
|
||||
}
|
||||
|
||||
|
||||
/* Allocate string descriptor numbers ... note that string
|
||||
* contents can be overridden by the composite_dev glue.
|
||||
*/
|
||||
|
||||
/* device descriptor strings: manufacturer, product */
|
||||
snprintf(manufacturer, sizeof manufacturer, "%s %s with %s",
|
||||
init_utsname()->sysname, init_utsname()->release,
|
||||
gadget->name);
|
||||
status = usb_string_id(cdev);
|
||||
if (status < 0)
|
||||
goto fail;
|
||||
strings_dev[STRING_MANUFACTURER_IDX].id = status;
|
||||
device_desc.iManufacturer = status;
|
||||
|
||||
status = usb_string_id(cdev);
|
||||
if (status < 0)
|
||||
goto fail;
|
||||
strings_dev[STRING_PRODUCT_IDX].id = status;
|
||||
device_desc.iProduct = status;
|
||||
|
||||
status = usb_add_config(cdev, &ncm_config_driver,
|
||||
ncm_do_config);
|
||||
if (status < 0)
|
||||
goto fail;
|
||||
|
||||
dev_info(&gadget->dev, "%s\n", DRIVER_DESC);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
gether_cleanup();
|
||||
return status;
|
||||
}
|
||||
|
||||
static int __exit gncm_unbind(struct usb_composite_dev *cdev)
|
||||
{
|
||||
gether_cleanup();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct usb_composite_driver ncm_driver = {
|
||||
.name = "g_ncm",
|
||||
.dev = &device_desc,
|
||||
.strings = dev_strings,
|
||||
.unbind = __exit_p(gncm_unbind),
|
||||
};
|
||||
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_AUTHOR("Yauheni Kaliuta");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
static int __init init(void)
|
||||
{
|
||||
return usb_composite_probe(&ncm_driver, gncm_bind);
|
||||
}
|
||||
module_init(init);
|
||||
|
||||
static void __exit cleanup(void)
|
||||
{
|
||||
usb_composite_unregister(&ncm_driver);
|
||||
}
|
||||
module_exit(cleanup);
|
||||
2947
drivers/usb/gadget/pch_udc.c
Normal file
2947
drivers/usb/gadget/pch_udc.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -255,6 +255,7 @@ static int gaudio_open_snd_dev(struct gaudio *card)
|
||||
ERROR(card, "No such PCM capture device: %s\n", fn_cap);
|
||||
snd->substream = NULL;
|
||||
snd->card = NULL;
|
||||
snd->filp = NULL;
|
||||
} else {
|
||||
pcm_file = snd->filp->private_data;
|
||||
snd->substream = pcm_file->substream;
|
||||
@@ -273,17 +274,17 @@ static int gaudio_close_snd_dev(struct gaudio *gau)
|
||||
|
||||
/* Close control device */
|
||||
snd = &gau->control;
|
||||
if (!IS_ERR(snd->filp))
|
||||
if (snd->filp)
|
||||
filp_close(snd->filp, current->files);
|
||||
|
||||
/* Close PCM playback device and setup substream */
|
||||
snd = &gau->playback;
|
||||
if (!IS_ERR(snd->filp))
|
||||
if (snd->filp)
|
||||
filp_close(snd->filp, current->files);
|
||||
|
||||
/* Close PCM capture device and setup substream */
|
||||
snd = &gau->capture;
|
||||
if (!IS_ERR(snd->filp))
|
||||
if (snd->filp)
|
||||
filp_close(snd->filp, current->files);
|
||||
|
||||
return 0;
|
||||
@@ -304,8 +305,7 @@ int __init gaudio_setup(struct gaudio *card)
|
||||
ret = gaudio_open_snd_dev(card);
|
||||
if (ret)
|
||||
ERROR(card, "we need at least one control device\n");
|
||||
|
||||
if (!the_card)
|
||||
else if (!the_card)
|
||||
the_card = card;
|
||||
|
||||
return ret;
|
||||
|
||||
@@ -240,6 +240,9 @@ rx_submit(struct eth_dev *dev, struct usb_request *req, gfp_t gfp_flags)
|
||||
size += out->maxpacket - 1;
|
||||
size -= size % out->maxpacket;
|
||||
|
||||
if (dev->port_usb->is_fixed)
|
||||
size = max(size, dev->port_usb->fixed_out_len);
|
||||
|
||||
skb = alloc_skb(size + NET_IP_ALIGN, gfp_flags);
|
||||
if (skb == NULL) {
|
||||
DBG(dev, "no rx skb\n");
|
||||
@@ -578,12 +581,19 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
|
||||
req->context = skb;
|
||||
req->complete = tx_complete;
|
||||
|
||||
/* NCM requires no zlp if transfer is dwNtbInMaxSize */
|
||||
if (dev->port_usb->is_fixed &&
|
||||
length == dev->port_usb->fixed_in_len &&
|
||||
(length % in->maxpacket) == 0)
|
||||
req->zero = 0;
|
||||
else
|
||||
req->zero = 1;
|
||||
|
||||
/* use zlp framing on tx for strict CDC-Ether conformance,
|
||||
* though any robust network rx path ignores extra padding.
|
||||
* and some hardware doesn't like to write zlps.
|
||||
*/
|
||||
req->zero = 1;
|
||||
if (!dev->zlp && (length % in->maxpacket) == 0)
|
||||
if (req->zero && !dev->zlp && (length % in->maxpacket) == 0)
|
||||
length++;
|
||||
|
||||
req->length = length;
|
||||
|
||||
@@ -62,6 +62,10 @@ struct gether {
|
||||
|
||||
/* hooks for added framing, as needed for RNDIS and EEM. */
|
||||
u32 header_len;
|
||||
/* NCM requires fixed size bundles */
|
||||
bool is_fixed;
|
||||
u32 fixed_out_len;
|
||||
u32 fixed_in_len;
|
||||
struct sk_buff *(*wrap)(struct gether *port,
|
||||
struct sk_buff *skb);
|
||||
int (*unwrap)(struct gether *port,
|
||||
@@ -103,6 +107,7 @@ static inline bool can_support_ecm(struct usb_gadget *gadget)
|
||||
/* each configuration may bind one instance of an ethernet link */
|
||||
int geth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]);
|
||||
int ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]);
|
||||
int ncm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]);
|
||||
int eem_bind_config(struct usb_configuration *c);
|
||||
|
||||
#ifdef USB_ETH_RNDIS
|
||||
|
||||
@@ -133,6 +133,25 @@ config USB_EHCI_MXC
|
||||
---help---
|
||||
Variation of ARC USB block used in some Freescale chips.
|
||||
|
||||
config USB_EHCI_HCD_OMAP
|
||||
bool "EHCI support for OMAP3 and later chips"
|
||||
depends on USB_EHCI_HCD && ARCH_OMAP
|
||||
default y
|
||||
--- help ---
|
||||
Enables support for the on-chip EHCI controller on
|
||||
OMAP3 and later chips.
|
||||
|
||||
config USB_EHCI_MSM
|
||||
bool "Support for MSM on-chip EHCI USB controller"
|
||||
depends on USB_EHCI_HCD && ARCH_MSM
|
||||
select USB_EHCI_ROOT_HUB_TT
|
||||
select USB_MSM_OTG_72K
|
||||
---help---
|
||||
Enables support for the USB Host controller present on the
|
||||
Qualcomm chipsets. Root Hub has inbuilt TT.
|
||||
This driver depends on OTG driver for PHY initialization,
|
||||
clock management, powering up VBUS, and power management.
|
||||
|
||||
config USB_EHCI_HCD_PPC_OF
|
||||
bool "EHCI support for PPC USB controller on OF platform bus"
|
||||
depends on USB_EHCI_HCD && PPC_OF
|
||||
|
||||
@@ -99,6 +99,7 @@ static const struct hc_driver ehci_atmel_hc_driver = {
|
||||
.urb_enqueue = ehci_urb_enqueue,
|
||||
.urb_dequeue = ehci_urb_dequeue,
|
||||
.endpoint_disable = ehci_endpoint_disable,
|
||||
.endpoint_reset = ehci_endpoint_reset,
|
||||
|
||||
/* scheduling support */
|
||||
.get_frame_number = ehci_get_frame,
|
||||
@@ -110,6 +111,8 @@ static const struct hc_driver ehci_atmel_hc_driver = {
|
||||
.bus_resume = ehci_bus_resume,
|
||||
.relinquish_port = ehci_relinquish_port,
|
||||
.port_handed_over = ehci_port_handed_over,
|
||||
|
||||
.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
|
||||
};
|
||||
|
||||
static int __init ehci_atmel_drv_probe(struct platform_device *pdev)
|
||||
|
||||
@@ -879,7 +879,7 @@ static int fill_buffer(struct debug_buffer *buf)
|
||||
int ret = 0;
|
||||
|
||||
if (!buf->output_buf)
|
||||
buf->output_buf = (char *)vmalloc(buf->alloc_size);
|
||||
buf->output_buf = vmalloc(buf->alloc_size);
|
||||
|
||||
if (!buf->output_buf) {
|
||||
ret = -ENOMEM;
|
||||
|
||||
@@ -114,6 +114,9 @@ MODULE_PARM_DESC(hird, "host initiated resume duration, +1 for each 75us\n");
|
||||
|
||||
#define INTR_MASK (STS_IAA | STS_FATAL | STS_PCD | STS_ERR | STS_INT)
|
||||
|
||||
/* for ASPM quirk of ISOC on AMD SB800 */
|
||||
static struct pci_dev *amd_nb_dev;
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
#include "ehci.h"
|
||||
@@ -529,6 +532,11 @@ static void ehci_stop (struct usb_hcd *hcd)
|
||||
spin_unlock_irq (&ehci->lock);
|
||||
ehci_mem_cleanup (ehci);
|
||||
|
||||
if (amd_nb_dev) {
|
||||
pci_dev_put(amd_nb_dev);
|
||||
amd_nb_dev = NULL;
|
||||
}
|
||||
|
||||
#ifdef EHCI_STATS
|
||||
ehci_dbg (ehci, "irq normal %ld err %ld reclaim %ld (lost %ld)\n",
|
||||
ehci->stats.normal, ehci->stats.error, ehci->stats.reclaim,
|
||||
@@ -1166,12 +1174,17 @@ MODULE_LICENSE ("GPL");
|
||||
#define PLATFORM_DRIVER ehci_mxc_driver
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_CPU_SUBTYPE_SH7786
|
||||
#include "ehci-sh.c"
|
||||
#define PLATFORM_DRIVER ehci_hcd_sh_driver
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SOC_AU1200
|
||||
#include "ehci-au1xxx.c"
|
||||
#define PLATFORM_DRIVER ehci_hcd_au1xxx_driver
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ARCH_OMAP3
|
||||
#ifdef CONFIG_USB_EHCI_HCD_OMAP
|
||||
#include "ehci-omap.c"
|
||||
#define PLATFORM_DRIVER ehci_hcd_omap_driver
|
||||
#endif
|
||||
@@ -1216,6 +1229,21 @@ MODULE_LICENSE ("GPL");
|
||||
#define PLATFORM_DRIVER ehci_octeon_driver
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ARCH_VT8500
|
||||
#include "ehci-vt8500.c"
|
||||
#define PLATFORM_DRIVER vt8500_ehci_driver
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PLAT_SPEAR
|
||||
#include "ehci-spear.c"
|
||||
#define PLATFORM_DRIVER spear_ehci_hcd_driver
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_EHCI_MSM
|
||||
#include "ehci-msm.c"
|
||||
#define PLATFORM_DRIVER ehci_msm_driver
|
||||
#endif
|
||||
|
||||
#if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \
|
||||
!defined(PS3_SYSTEM_BUS_DRIVER) && !defined(OF_PLATFORM_DRIVER) && \
|
||||
!defined(XILINX_OF_PLATFORM_DRIVER)
|
||||
|
||||
345
drivers/usb/host/ehci-msm.c
Normal file
345
drivers/usb/host/ehci-msm.c
Normal file
@@ -0,0 +1,345 @@
|
||||
/* ehci-msm.c - HSUSB Host Controller Driver Implementation
|
||||
*
|
||||
* Copyright (c) 2008-2010, Code Aurora Forum. All rights reserved.
|
||||
*
|
||||
* Partly derived from ehci-fsl.c and ehci-hcd.c
|
||||
* Copyright (c) 2000-2004 by David Brownell
|
||||
* Copyright (c) 2005 MontaVista Software
|
||||
*
|
||||
* All source code in this file is licensed under the following license except
|
||||
* where indicated.
|
||||
*
|
||||
* 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, you can find it at http://www.fsf.org
|
||||
*/
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include <linux/usb/otg.h>
|
||||
#include <linux/usb/msm_hsusb_hw.h>
|
||||
|
||||
#define MSM_USB_BASE (hcd->regs)
|
||||
|
||||
static struct otg_transceiver *otg;
|
||||
|
||||
/*
|
||||
* ehci_run defined in drivers/usb/host/ehci-hcd.c reset the controller and
|
||||
* the configuration settings in ehci_msm_reset vanish after controller is
|
||||
* reset. Resetting the controler in ehci_run seems to be un-necessary
|
||||
* provided HCD reset the controller before calling ehci_run. Most of the HCD
|
||||
* do but some are not. So this function is same as ehci_run but we don't
|
||||
* reset the controller here.
|
||||
*/
|
||||
static int ehci_msm_run(struct usb_hcd *hcd)
|
||||
{
|
||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||
u32 temp;
|
||||
u32 hcc_params;
|
||||
|
||||
hcd->uses_new_polling = 1;
|
||||
|
||||
ehci_writel(ehci, ehci->periodic_dma, &ehci->regs->frame_list);
|
||||
ehci_writel(ehci, (u32)ehci->async->qh_dma, &ehci->regs->async_next);
|
||||
|
||||
/*
|
||||
* hcc_params controls whether ehci->regs->segment must (!!!)
|
||||
* be used; it constrains QH/ITD/SITD and QTD locations.
|
||||
* pci_pool consistent memory always uses segment zero.
|
||||
* streaming mappings for I/O buffers, like pci_map_single(),
|
||||
* can return segments above 4GB, if the device allows.
|
||||
*
|
||||
* NOTE: the dma mask is visible through dma_supported(), so
|
||||
* drivers can pass this info along ... like NETIF_F_HIGHDMA,
|
||||
* Scsi_Host.highmem_io, and so forth. It's readonly to all
|
||||
* host side drivers though.
|
||||
*/
|
||||
hcc_params = ehci_readl(ehci, &ehci->caps->hcc_params);
|
||||
if (HCC_64BIT_ADDR(hcc_params))
|
||||
ehci_writel(ehci, 0, &ehci->regs->segment);
|
||||
|
||||
/*
|
||||
* Philips, Intel, and maybe others need CMD_RUN before the
|
||||
* root hub will detect new devices (why?); NEC doesn't
|
||||
*/
|
||||
ehci->command &= ~(CMD_LRESET|CMD_IAAD|CMD_PSE|CMD_ASE|CMD_RESET);
|
||||
ehci->command |= CMD_RUN;
|
||||
ehci_writel(ehci, ehci->command, &ehci->regs->command);
|
||||
dbg_cmd(ehci, "init", ehci->command);
|
||||
|
||||
/*
|
||||
* Start, enabling full USB 2.0 functionality ... usb 1.1 devices
|
||||
* are explicitly handed to companion controller(s), so no TT is
|
||||
* involved with the root hub. (Except where one is integrated,
|
||||
* and there's no companion controller unless maybe for USB OTG.)
|
||||
*
|
||||
* Turning on the CF flag will transfer ownership of all ports
|
||||
* from the companions to the EHCI controller. If any of the
|
||||
* companions are in the middle of a port reset at the time, it
|
||||
* could cause trouble. Write-locking ehci_cf_port_reset_rwsem
|
||||
* guarantees that no resets are in progress. After we set CF,
|
||||
* a short delay lets the hardware catch up; new resets shouldn't
|
||||
* be started before the port switching actions could complete.
|
||||
*/
|
||||
down_write(&ehci_cf_port_reset_rwsem);
|
||||
hcd->state = HC_STATE_RUNNING;
|
||||
ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag);
|
||||
ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */
|
||||
usleep_range(5000, 5500);
|
||||
up_write(&ehci_cf_port_reset_rwsem);
|
||||
ehci->last_periodic_enable = ktime_get_real();
|
||||
|
||||
temp = HC_VERSION(ehci_readl(ehci, &ehci->caps->hc_capbase));
|
||||
ehci_info(ehci,
|
||||
"USB %x.%x started, EHCI %x.%02x%s\n",
|
||||
((ehci->sbrn & 0xf0)>>4), (ehci->sbrn & 0x0f),
|
||||
temp >> 8, temp & 0xff,
|
||||
ignore_oc ? ", overcurrent ignored" : "");
|
||||
|
||||
ehci_writel(ehci, INTR_MASK,
|
||||
&ehci->regs->intr_enable); /* Turn On Interrupts */
|
||||
|
||||
/* GRR this is run-once init(), being done every time the HC starts.
|
||||
* So long as they're part of class devices, we can't do it init()
|
||||
* since the class device isn't created that early.
|
||||
*/
|
||||
create_debug_files(ehci);
|
||||
create_companion_file(ehci);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ehci_msm_reset(struct usb_hcd *hcd)
|
||||
{
|
||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||
int retval;
|
||||
|
||||
ehci->caps = USB_CAPLENGTH;
|
||||
ehci->regs = USB_CAPLENGTH +
|
||||
HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase));
|
||||
|
||||
/* cache the data to minimize the chip reads*/
|
||||
ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
|
||||
|
||||
hcd->has_tt = 1;
|
||||
ehci->sbrn = HCD_USB2;
|
||||
|
||||
/* data structure init */
|
||||
retval = ehci_init(hcd);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
retval = ehci_reset(ehci);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
/* bursts of unspecified length. */
|
||||
writel(0, USB_AHBBURST);
|
||||
/* Use the AHB transactor */
|
||||
writel(0, USB_AHBMODE);
|
||||
/* Disable streaming mode and select host mode */
|
||||
writel(0x13, USB_USBMODE);
|
||||
|
||||
ehci_port_power(ehci, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct hc_driver msm_hc_driver = {
|
||||
.description = hcd_name,
|
||||
.product_desc = "Qualcomm On-Chip EHCI Host Controller",
|
||||
.hcd_priv_size = sizeof(struct ehci_hcd),
|
||||
|
||||
/*
|
||||
* generic hardware linkage
|
||||
*/
|
||||
.irq = ehci_irq,
|
||||
.flags = HCD_USB2 | HCD_MEMORY,
|
||||
|
||||
.reset = ehci_msm_reset,
|
||||
.start = ehci_msm_run,
|
||||
|
||||
.stop = ehci_stop,
|
||||
.shutdown = ehci_shutdown,
|
||||
|
||||
/*
|
||||
* managing i/o requests and associated device resources
|
||||
*/
|
||||
.urb_enqueue = ehci_urb_enqueue,
|
||||
.urb_dequeue = ehci_urb_dequeue,
|
||||
.endpoint_disable = ehci_endpoint_disable,
|
||||
.endpoint_reset = ehci_endpoint_reset,
|
||||
.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
|
||||
|
||||
/*
|
||||
* scheduling support
|
||||
*/
|
||||
.get_frame_number = ehci_get_frame,
|
||||
|
||||
/*
|
||||
* root hub support
|
||||
*/
|
||||
.hub_status_data = ehci_hub_status_data,
|
||||
.hub_control = ehci_hub_control,
|
||||
.relinquish_port = ehci_relinquish_port,
|
||||
.port_handed_over = ehci_port_handed_over,
|
||||
|
||||
/*
|
||||
* PM support
|
||||
*/
|
||||
.bus_suspend = ehci_bus_suspend,
|
||||
.bus_resume = ehci_bus_resume,
|
||||
};
|
||||
|
||||
static int ehci_msm_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct usb_hcd *hcd;
|
||||
struct resource *res;
|
||||
int ret;
|
||||
|
||||
dev_dbg(&pdev->dev, "ehci_msm proble\n");
|
||||
|
||||
hcd = usb_create_hcd(&msm_hc_driver, &pdev->dev, dev_name(&pdev->dev));
|
||||
if (!hcd) {
|
||||
dev_err(&pdev->dev, "Unable to create HCD\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
hcd->irq = platform_get_irq(pdev, 0);
|
||||
if (hcd->irq < 0) {
|
||||
dev_err(&pdev->dev, "Unable to get IRQ resource\n");
|
||||
ret = hcd->irq;
|
||||
goto put_hcd;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "Unable to get memory resource\n");
|
||||
ret = -ENODEV;
|
||||
goto put_hcd;
|
||||
}
|
||||
|
||||
hcd->rsrc_start = res->start;
|
||||
hcd->rsrc_len = resource_size(res);
|
||||
hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
|
||||
if (!hcd->regs) {
|
||||
dev_err(&pdev->dev, "ioremap failed\n");
|
||||
ret = -ENOMEM;
|
||||
goto put_hcd;
|
||||
}
|
||||
|
||||
/*
|
||||
* OTG driver takes care of PHY initialization, clock management,
|
||||
* powering up VBUS, mapping of registers address space and power
|
||||
* management.
|
||||
*/
|
||||
otg = otg_get_transceiver();
|
||||
if (!otg) {
|
||||
dev_err(&pdev->dev, "unable to find transceiver\n");
|
||||
ret = -ENODEV;
|
||||
goto unmap;
|
||||
}
|
||||
|
||||
ret = otg_set_host(otg, &hcd->self);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "unable to register with transceiver\n");
|
||||
goto put_transceiver;
|
||||
}
|
||||
|
||||
device_init_wakeup(&pdev->dev, 1);
|
||||
/*
|
||||
* OTG device parent of HCD takes care of putting
|
||||
* hardware into low power mode.
|
||||
*/
|
||||
pm_runtime_no_callbacks(&pdev->dev);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
|
||||
put_transceiver:
|
||||
otg_put_transceiver(otg);
|
||||
unmap:
|
||||
iounmap(hcd->regs);
|
||||
put_hcd:
|
||||
usb_put_hcd(hcd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit ehci_msm_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct usb_hcd *hcd = platform_get_drvdata(pdev);
|
||||
|
||||
device_init_wakeup(&pdev->dev, 0);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
pm_runtime_set_suspended(&pdev->dev);
|
||||
|
||||
otg_set_host(otg, NULL);
|
||||
otg_put_transceiver(otg);
|
||||
|
||||
usb_put_hcd(hcd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int ehci_msm_pm_suspend(struct device *dev)
|
||||
{
|
||||
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
||||
bool wakeup = device_may_wakeup(dev);
|
||||
|
||||
dev_dbg(dev, "ehci-msm PM suspend\n");
|
||||
|
||||
/*
|
||||
* EHCI helper function has also the same check before manipulating
|
||||
* port wakeup flags. We do check here the same condition before
|
||||
* calling the same helper function to avoid bringing hardware
|
||||
* from Low power mode when there is no need for adjusting port
|
||||
* wakeup flags.
|
||||
*/
|
||||
if (hcd->self.root_hub->do_remote_wakeup && !wakeup) {
|
||||
pm_runtime_resume(dev);
|
||||
ehci_prepare_ports_for_controller_suspend(hcd_to_ehci(hcd),
|
||||
wakeup);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ehci_msm_pm_resume(struct device *dev)
|
||||
{
|
||||
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
||||
|
||||
dev_dbg(dev, "ehci-msm PM resume\n");
|
||||
ehci_prepare_ports_for_controller_resume(hcd_to_ehci(hcd));
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define ehci_msm_pm_suspend NULL
|
||||
#define ehci_msm_pm_resume NULL
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops ehci_msm_dev_pm_ops = {
|
||||
.suspend = ehci_msm_pm_suspend,
|
||||
.resume = ehci_msm_pm_resume,
|
||||
};
|
||||
|
||||
static struct platform_driver ehci_msm_driver = {
|
||||
.probe = ehci_msm_probe,
|
||||
.remove = __devexit_p(ehci_msm_remove),
|
||||
.driver = {
|
||||
.name = "msm_hsusb_host",
|
||||
.pm = &ehci_msm_dev_pm_ops,
|
||||
},
|
||||
};
|
||||
@@ -100,6 +100,7 @@ static const struct hc_driver ehci_mxc_hc_driver = {
|
||||
.urb_enqueue = ehci_urb_enqueue,
|
||||
.urb_dequeue = ehci_urb_dequeue,
|
||||
.endpoint_disable = ehci_endpoint_disable,
|
||||
.endpoint_reset = ehci_endpoint_reset,
|
||||
|
||||
/*
|
||||
* scheduling support
|
||||
@@ -115,6 +116,8 @@ static const struct hc_driver ehci_mxc_hc_driver = {
|
||||
.bus_resume = ehci_bus_resume,
|
||||
.relinquish_port = ehci_relinquish_port,
|
||||
.port_handed_over = ehci_port_handed_over,
|
||||
|
||||
.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
|
||||
};
|
||||
|
||||
static int ehci_mxc_drv_probe(struct platform_device *pdev)
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
/*
|
||||
* ehci-omap.c - driver for USBHOST on OMAP 34xx processor
|
||||
* ehci-omap.c - driver for USBHOST on OMAP3/4 processors
|
||||
*
|
||||
* Bus Glue for OMAP34xx USBHOST 3 port EHCI controller
|
||||
* Tested on OMAP3430 ES2.0 SDP
|
||||
* Bus Glue for the EHCI controllers in OMAP3/4
|
||||
* Tested on several OMAP3 boards, and OMAP4 Pandaboard
|
||||
*
|
||||
* Copyright (C) 2007-2008 Texas Instruments, Inc.
|
||||
* Copyright (C) 2007-2010 Texas Instruments, Inc.
|
||||
* Author: Vikram Pandita <vikram.pandita@ti.com>
|
||||
* Author: Anand Gadiyar <gadiyar@ti.com>
|
||||
*
|
||||
* Copyright (C) 2009 Nokia Corporation
|
||||
* Contact: Felipe Balbi <felipe.balbi@nokia.com>
|
||||
@@ -26,11 +27,14 @@
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* TODO (last updated Feb 12, 2010):
|
||||
* TODO (last updated Nov 21, 2010):
|
||||
* - add kernel-doc
|
||||
* - enable AUTOIDLE
|
||||
* - add suspend/resume
|
||||
* - move workarounds to board-files
|
||||
* - factor out code common to OHCI
|
||||
* - add HSIC and TLL support
|
||||
* - convert to use hwmod and runtime PM
|
||||
*/
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
@@ -114,6 +118,23 @@
|
||||
#define OMAP_UHH_HOSTCONFIG_P2_CONNECT_STATUS (1 << 9)
|
||||
#define OMAP_UHH_HOSTCONFIG_P3_CONNECT_STATUS (1 << 10)
|
||||
|
||||
/* OMAP4-specific defines */
|
||||
#define OMAP4_UHH_SYSCONFIG_IDLEMODE_CLEAR (3 << 2)
|
||||
#define OMAP4_UHH_SYSCONFIG_NOIDLE (1 << 2)
|
||||
|
||||
#define OMAP4_UHH_SYSCONFIG_STDBYMODE_CLEAR (3 << 4)
|
||||
#define OMAP4_UHH_SYSCONFIG_NOSTDBY (1 << 4)
|
||||
#define OMAP4_UHH_SYSCONFIG_SOFTRESET (1 << 0)
|
||||
|
||||
#define OMAP4_P1_MODE_CLEAR (3 << 16)
|
||||
#define OMAP4_P1_MODE_TLL (1 << 16)
|
||||
#define OMAP4_P1_MODE_HSIC (3 << 16)
|
||||
#define OMAP4_P2_MODE_CLEAR (3 << 18)
|
||||
#define OMAP4_P2_MODE_TLL (1 << 18)
|
||||
#define OMAP4_P2_MODE_HSIC (3 << 18)
|
||||
|
||||
#define OMAP_REV2_TLL_CHANNEL_COUNT 2
|
||||
|
||||
#define OMAP_UHH_DEBUG_CSR (0x44)
|
||||
|
||||
/* EHCI Register Set */
|
||||
@@ -127,6 +148,17 @@
|
||||
#define EHCI_INSNREG05_ULPI_EXTREGADD_SHIFT 8
|
||||
#define EHCI_INSNREG05_ULPI_WRDATA_SHIFT 0
|
||||
|
||||
/* Values of UHH_REVISION - Note: these are not given in the TRM */
|
||||
#define OMAP_EHCI_REV1 0x00000010 /* OMAP3 */
|
||||
#define OMAP_EHCI_REV2 0x50700100 /* OMAP4 */
|
||||
|
||||
#define is_omap_ehci_rev1(x) (x->omap_ehci_rev == OMAP_EHCI_REV1)
|
||||
#define is_omap_ehci_rev2(x) (x->omap_ehci_rev == OMAP_EHCI_REV2)
|
||||
|
||||
#define is_ehci_phy_mode(x) (x == EHCI_HCD_OMAP_MODE_PHY)
|
||||
#define is_ehci_tll_mode(x) (x == EHCI_HCD_OMAP_MODE_TLL)
|
||||
#define is_ehci_hsic_mode(x) (x == EHCI_HCD_OMAP_MODE_HSIC)
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static inline void ehci_omap_writel(void __iomem *base, u32 reg, u32 val)
|
||||
@@ -156,10 +188,14 @@ struct ehci_hcd_omap {
|
||||
struct device *dev;
|
||||
|
||||
struct clk *usbhost_ick;
|
||||
struct clk *usbhost2_120m_fck;
|
||||
struct clk *usbhost1_48m_fck;
|
||||
struct clk *usbhost_hs_fck;
|
||||
struct clk *usbhost_fs_fck;
|
||||
struct clk *usbtll_fck;
|
||||
struct clk *usbtll_ick;
|
||||
struct clk *xclk60mhsp1_ck;
|
||||
struct clk *xclk60mhsp2_ck;
|
||||
struct clk *utmi_p1_fck;
|
||||
struct clk *utmi_p2_fck;
|
||||
|
||||
/* FIXME the following two workarounds are
|
||||
* board specific not silicon-specific so these
|
||||
@@ -176,6 +212,9 @@ struct ehci_hcd_omap {
|
||||
/* phy reset workaround */
|
||||
int phy_reset;
|
||||
|
||||
/* IP revision */
|
||||
u32 omap_ehci_rev;
|
||||
|
||||
/* desired phy_mode: TLL, PHY */
|
||||
enum ehci_hcd_omap_mode port_mode[OMAP3_HS_USB_PORTS];
|
||||
|
||||
@@ -191,13 +230,14 @@ struct ehci_hcd_omap {
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static void omap_usb_utmi_init(struct ehci_hcd_omap *omap, u8 tll_channel_mask)
|
||||
static void omap_usb_utmi_init(struct ehci_hcd_omap *omap, u8 tll_channel_mask,
|
||||
u8 tll_channel_count)
|
||||
{
|
||||
unsigned reg;
|
||||
int i;
|
||||
|
||||
/* Program the 3 TLL channels upfront */
|
||||
for (i = 0; i < OMAP_TLL_CHANNEL_COUNT; i++) {
|
||||
for (i = 0; i < tll_channel_count; i++) {
|
||||
reg = ehci_omap_readl(omap->tll_base, OMAP_TLL_CHANNEL_CONF(i));
|
||||
|
||||
/* Disable AutoIdle, BitStuffing and use SDR Mode */
|
||||
@@ -217,7 +257,7 @@ static void omap_usb_utmi_init(struct ehci_hcd_omap *omap, u8 tll_channel_mask)
|
||||
ehci_omap_writel(omap->tll_base, OMAP_TLL_SHARED_CONF, reg);
|
||||
|
||||
/* Enable channels now */
|
||||
for (i = 0; i < OMAP_TLL_CHANNEL_COUNT; i++) {
|
||||
for (i = 0; i < tll_channel_count; i++) {
|
||||
reg = ehci_omap_readl(omap->tll_base, OMAP_TLL_CHANNEL_CONF(i));
|
||||
|
||||
/* Enable only the reg that is needed */
|
||||
@@ -286,19 +326,19 @@ static int omap_start_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd)
|
||||
}
|
||||
clk_enable(omap->usbhost_ick);
|
||||
|
||||
omap->usbhost2_120m_fck = clk_get(omap->dev, "usbhost_120m_fck");
|
||||
if (IS_ERR(omap->usbhost2_120m_fck)) {
|
||||
ret = PTR_ERR(omap->usbhost2_120m_fck);
|
||||
omap->usbhost_hs_fck = clk_get(omap->dev, "hs_fck");
|
||||
if (IS_ERR(omap->usbhost_hs_fck)) {
|
||||
ret = PTR_ERR(omap->usbhost_hs_fck);
|
||||
goto err_host_120m_fck;
|
||||
}
|
||||
clk_enable(omap->usbhost2_120m_fck);
|
||||
clk_enable(omap->usbhost_hs_fck);
|
||||
|
||||
omap->usbhost1_48m_fck = clk_get(omap->dev, "usbhost_48m_fck");
|
||||
if (IS_ERR(omap->usbhost1_48m_fck)) {
|
||||
ret = PTR_ERR(omap->usbhost1_48m_fck);
|
||||
omap->usbhost_fs_fck = clk_get(omap->dev, "fs_fck");
|
||||
if (IS_ERR(omap->usbhost_fs_fck)) {
|
||||
ret = PTR_ERR(omap->usbhost_fs_fck);
|
||||
goto err_host_48m_fck;
|
||||
}
|
||||
clk_enable(omap->usbhost1_48m_fck);
|
||||
clk_enable(omap->usbhost_fs_fck);
|
||||
|
||||
if (omap->phy_reset) {
|
||||
/* Refer: ISSUE1 */
|
||||
@@ -333,6 +373,80 @@ static int omap_start_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd)
|
||||
}
|
||||
clk_enable(omap->usbtll_ick);
|
||||
|
||||
omap->omap_ehci_rev = ehci_omap_readl(omap->uhh_base,
|
||||
OMAP_UHH_REVISION);
|
||||
dev_dbg(omap->dev, "OMAP UHH_REVISION 0x%x\n",
|
||||
omap->omap_ehci_rev);
|
||||
|
||||
/*
|
||||
* Enable per-port clocks as needed (newer controllers only).
|
||||
* - External ULPI clock for PHY mode
|
||||
* - Internal clocks for TLL and HSIC modes (TODO)
|
||||
*/
|
||||
if (is_omap_ehci_rev2(omap)) {
|
||||
switch (omap->port_mode[0]) {
|
||||
case EHCI_HCD_OMAP_MODE_PHY:
|
||||
omap->xclk60mhsp1_ck = clk_get(omap->dev,
|
||||
"xclk60mhsp1_ck");
|
||||
if (IS_ERR(omap->xclk60mhsp1_ck)) {
|
||||
ret = PTR_ERR(omap->xclk60mhsp1_ck);
|
||||
dev_err(omap->dev,
|
||||
"Unable to get Port1 ULPI clock\n");
|
||||
}
|
||||
|
||||
omap->utmi_p1_fck = clk_get(omap->dev,
|
||||
"utmi_p1_gfclk");
|
||||
if (IS_ERR(omap->utmi_p1_fck)) {
|
||||
ret = PTR_ERR(omap->utmi_p1_fck);
|
||||
dev_err(omap->dev,
|
||||
"Unable to get utmi_p1_fck\n");
|
||||
}
|
||||
|
||||
ret = clk_set_parent(omap->utmi_p1_fck,
|
||||
omap->xclk60mhsp1_ck);
|
||||
if (ret != 0) {
|
||||
dev_err(omap->dev,
|
||||
"Unable to set P1 f-clock\n");
|
||||
}
|
||||
break;
|
||||
case EHCI_HCD_OMAP_MODE_TLL:
|
||||
/* TODO */
|
||||
default:
|
||||
break;
|
||||
}
|
||||
switch (omap->port_mode[1]) {
|
||||
case EHCI_HCD_OMAP_MODE_PHY:
|
||||
omap->xclk60mhsp2_ck = clk_get(omap->dev,
|
||||
"xclk60mhsp2_ck");
|
||||
if (IS_ERR(omap->xclk60mhsp2_ck)) {
|
||||
ret = PTR_ERR(omap->xclk60mhsp2_ck);
|
||||
dev_err(omap->dev,
|
||||
"Unable to get Port2 ULPI clock\n");
|
||||
}
|
||||
|
||||
omap->utmi_p2_fck = clk_get(omap->dev,
|
||||
"utmi_p2_gfclk");
|
||||
if (IS_ERR(omap->utmi_p2_fck)) {
|
||||
ret = PTR_ERR(omap->utmi_p2_fck);
|
||||
dev_err(omap->dev,
|
||||
"Unable to get utmi_p2_fck\n");
|
||||
}
|
||||
|
||||
ret = clk_set_parent(omap->utmi_p2_fck,
|
||||
omap->xclk60mhsp2_ck);
|
||||
if (ret != 0) {
|
||||
dev_err(omap->dev,
|
||||
"Unable to set P2 f-clock\n");
|
||||
}
|
||||
break;
|
||||
case EHCI_HCD_OMAP_MODE_TLL:
|
||||
/* TODO */
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* perform TLL soft reset, and wait until reset is complete */
|
||||
ehci_omap_writel(omap->tll_base, OMAP_USBTLL_SYSCONFIG,
|
||||
OMAP_USBTLL_SYSCONFIG_SOFTRESET);
|
||||
@@ -360,12 +474,20 @@ static int omap_start_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd)
|
||||
|
||||
/* Put UHH in NoIdle/NoStandby mode */
|
||||
reg = ehci_omap_readl(omap->uhh_base, OMAP_UHH_SYSCONFIG);
|
||||
reg |= (OMAP_UHH_SYSCONFIG_ENAWAKEUP
|
||||
| OMAP_UHH_SYSCONFIG_SIDLEMODE
|
||||
| OMAP_UHH_SYSCONFIG_CACTIVITY
|
||||
| OMAP_UHH_SYSCONFIG_MIDLEMODE);
|
||||
reg &= ~OMAP_UHH_SYSCONFIG_AUTOIDLE;
|
||||
if (is_omap_ehci_rev1(omap)) {
|
||||
reg |= (OMAP_UHH_SYSCONFIG_ENAWAKEUP
|
||||
| OMAP_UHH_SYSCONFIG_SIDLEMODE
|
||||
| OMAP_UHH_SYSCONFIG_CACTIVITY
|
||||
| OMAP_UHH_SYSCONFIG_MIDLEMODE);
|
||||
reg &= ~OMAP_UHH_SYSCONFIG_AUTOIDLE;
|
||||
|
||||
|
||||
} else if (is_omap_ehci_rev2(omap)) {
|
||||
reg &= ~OMAP4_UHH_SYSCONFIG_IDLEMODE_CLEAR;
|
||||
reg |= OMAP4_UHH_SYSCONFIG_NOIDLE;
|
||||
reg &= ~OMAP4_UHH_SYSCONFIG_STDBYMODE_CLEAR;
|
||||
reg |= OMAP4_UHH_SYSCONFIG_NOSTDBY;
|
||||
}
|
||||
ehci_omap_writel(omap->uhh_base, OMAP_UHH_SYSCONFIG, reg);
|
||||
|
||||
reg = ehci_omap_readl(omap->uhh_base, OMAP_UHH_HOSTCONFIG);
|
||||
@@ -376,40 +498,56 @@ static int omap_start_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd)
|
||||
| OMAP_UHH_HOSTCONFIG_INCR16_BURST_EN);
|
||||
reg &= ~OMAP_UHH_HOSTCONFIG_INCRX_ALIGN_EN;
|
||||
|
||||
if (omap->port_mode[0] == EHCI_HCD_OMAP_MODE_UNKNOWN)
|
||||
reg &= ~OMAP_UHH_HOSTCONFIG_P1_CONNECT_STATUS;
|
||||
if (omap->port_mode[1] == EHCI_HCD_OMAP_MODE_UNKNOWN)
|
||||
reg &= ~OMAP_UHH_HOSTCONFIG_P2_CONNECT_STATUS;
|
||||
if (omap->port_mode[2] == EHCI_HCD_OMAP_MODE_UNKNOWN)
|
||||
reg &= ~OMAP_UHH_HOSTCONFIG_P3_CONNECT_STATUS;
|
||||
if (is_omap_ehci_rev1(omap)) {
|
||||
if (omap->port_mode[0] == EHCI_HCD_OMAP_MODE_UNKNOWN)
|
||||
reg &= ~OMAP_UHH_HOSTCONFIG_P1_CONNECT_STATUS;
|
||||
if (omap->port_mode[1] == EHCI_HCD_OMAP_MODE_UNKNOWN)
|
||||
reg &= ~OMAP_UHH_HOSTCONFIG_P2_CONNECT_STATUS;
|
||||
if (omap->port_mode[2] == EHCI_HCD_OMAP_MODE_UNKNOWN)
|
||||
reg &= ~OMAP_UHH_HOSTCONFIG_P3_CONNECT_STATUS;
|
||||
|
||||
/* Bypass the TLL module for PHY mode operation */
|
||||
if (cpu_is_omap3430() && (omap_rev() <= OMAP3430_REV_ES2_1)) {
|
||||
dev_dbg(omap->dev, "OMAP3 ES version <= ES2.1\n");
|
||||
if ((omap->port_mode[0] == EHCI_HCD_OMAP_MODE_PHY) ||
|
||||
(omap->port_mode[1] == EHCI_HCD_OMAP_MODE_PHY) ||
|
||||
(omap->port_mode[2] == EHCI_HCD_OMAP_MODE_PHY))
|
||||
reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_BYPASS;
|
||||
else
|
||||
reg |= OMAP_UHH_HOSTCONFIG_ULPI_BYPASS;
|
||||
} else {
|
||||
dev_dbg(omap->dev, "OMAP3 ES version > ES2.1\n");
|
||||
if (omap->port_mode[0] == EHCI_HCD_OMAP_MODE_PHY)
|
||||
reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS;
|
||||
else if (omap->port_mode[0] == EHCI_HCD_OMAP_MODE_TLL)
|
||||
reg |= OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS;
|
||||
/* Bypass the TLL module for PHY mode operation */
|
||||
if (cpu_is_omap3430() && (omap_rev() <= OMAP3430_REV_ES2_1)) {
|
||||
dev_dbg(omap->dev, "OMAP3 ES version <= ES2.1\n");
|
||||
if (is_ehci_phy_mode(omap->port_mode[0]) ||
|
||||
is_ehci_phy_mode(omap->port_mode[1]) ||
|
||||
is_ehci_phy_mode(omap->port_mode[2]))
|
||||
reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_BYPASS;
|
||||
else
|
||||
reg |= OMAP_UHH_HOSTCONFIG_ULPI_BYPASS;
|
||||
} else {
|
||||
dev_dbg(omap->dev, "OMAP3 ES version > ES2.1\n");
|
||||
if (is_ehci_phy_mode(omap->port_mode[0]))
|
||||
reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS;
|
||||
else if (is_ehci_tll_mode(omap->port_mode[0]))
|
||||
reg |= OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS;
|
||||
|
||||
if (omap->port_mode[1] == EHCI_HCD_OMAP_MODE_PHY)
|
||||
reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS;
|
||||
else if (omap->port_mode[1] == EHCI_HCD_OMAP_MODE_TLL)
|
||||
reg |= OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS;
|
||||
if (is_ehci_phy_mode(omap->port_mode[1]))
|
||||
reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS;
|
||||
else if (is_ehci_tll_mode(omap->port_mode[1]))
|
||||
reg |= OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS;
|
||||
|
||||
if (omap->port_mode[2] == EHCI_HCD_OMAP_MODE_PHY)
|
||||
reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS;
|
||||
else if (omap->port_mode[2] == EHCI_HCD_OMAP_MODE_TLL)
|
||||
reg |= OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS;
|
||||
if (is_ehci_phy_mode(omap->port_mode[2]))
|
||||
reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS;
|
||||
else if (is_ehci_tll_mode(omap->port_mode[2]))
|
||||
reg |= OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS;
|
||||
}
|
||||
} else if (is_omap_ehci_rev2(omap)) {
|
||||
/* Clear port mode fields for PHY mode*/
|
||||
reg &= ~OMAP4_P1_MODE_CLEAR;
|
||||
reg &= ~OMAP4_P2_MODE_CLEAR;
|
||||
|
||||
if (is_ehci_tll_mode(omap->port_mode[0]))
|
||||
reg |= OMAP4_P1_MODE_TLL;
|
||||
else if (is_ehci_hsic_mode(omap->port_mode[0]))
|
||||
reg |= OMAP4_P1_MODE_HSIC;
|
||||
|
||||
if (is_ehci_tll_mode(omap->port_mode[1]))
|
||||
reg |= OMAP4_P2_MODE_TLL;
|
||||
else if (is_ehci_hsic_mode(omap->port_mode[1]))
|
||||
reg |= OMAP4_P2_MODE_HSIC;
|
||||
}
|
||||
|
||||
ehci_omap_writel(omap->uhh_base, OMAP_UHH_HOSTCONFIG, reg);
|
||||
dev_dbg(omap->dev, "UHH setup done, uhh_hostconfig=%x\n", reg);
|
||||
|
||||
@@ -438,7 +576,7 @@ static int omap_start_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd)
|
||||
tll_ch_mask |= OMAP_TLL_CHANNEL_3_EN_MASK;
|
||||
|
||||
/* Enable UTMI mode for required TLL channels */
|
||||
omap_usb_utmi_init(omap, tll_ch_mask);
|
||||
omap_usb_utmi_init(omap, tll_ch_mask, OMAP_TLL_CHANNEL_COUNT);
|
||||
}
|
||||
|
||||
if (omap->phy_reset) {
|
||||
@@ -464,6 +602,14 @@ static int omap_start_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd)
|
||||
return 0;
|
||||
|
||||
err_sys_status:
|
||||
clk_disable(omap->utmi_p2_fck);
|
||||
clk_put(omap->utmi_p2_fck);
|
||||
clk_disable(omap->xclk60mhsp2_ck);
|
||||
clk_put(omap->xclk60mhsp2_ck);
|
||||
clk_disable(omap->utmi_p1_fck);
|
||||
clk_put(omap->utmi_p1_fck);
|
||||
clk_disable(omap->xclk60mhsp1_ck);
|
||||
clk_put(omap->xclk60mhsp1_ck);
|
||||
clk_disable(omap->usbtll_ick);
|
||||
clk_put(omap->usbtll_ick);
|
||||
|
||||
@@ -472,8 +618,8 @@ err_tll_ick:
|
||||
clk_put(omap->usbtll_fck);
|
||||
|
||||
err_tll_fck:
|
||||
clk_disable(omap->usbhost1_48m_fck);
|
||||
clk_put(omap->usbhost1_48m_fck);
|
||||
clk_disable(omap->usbhost_fs_fck);
|
||||
clk_put(omap->usbhost_fs_fck);
|
||||
|
||||
if (omap->phy_reset) {
|
||||
if (gpio_is_valid(omap->reset_gpio_port[0]))
|
||||
@@ -484,8 +630,8 @@ err_tll_fck:
|
||||
}
|
||||
|
||||
err_host_48m_fck:
|
||||
clk_disable(omap->usbhost2_120m_fck);
|
||||
clk_put(omap->usbhost2_120m_fck);
|
||||
clk_disable(omap->usbhost_hs_fck);
|
||||
clk_put(omap->usbhost_hs_fck);
|
||||
|
||||
err_host_120m_fck:
|
||||
clk_disable(omap->usbhost_ick);
|
||||
@@ -503,6 +649,8 @@ static void omap_stop_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd)
|
||||
|
||||
/* Reset OMAP modules for insmod/rmmod to work */
|
||||
ehci_omap_writel(omap->uhh_base, OMAP_UHH_SYSCONFIG,
|
||||
is_omap_ehci_rev2(omap) ?
|
||||
OMAP4_UHH_SYSCONFIG_SOFTRESET :
|
||||
OMAP_UHH_SYSCONFIG_SOFTRESET);
|
||||
while (!(ehci_omap_readl(omap->uhh_base, OMAP_UHH_SYSSTATUS)
|
||||
& (1 << 0))) {
|
||||
@@ -550,16 +698,16 @@ static void omap_stop_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd)
|
||||
omap->usbhost_ick = NULL;
|
||||
}
|
||||
|
||||
if (omap->usbhost1_48m_fck != NULL) {
|
||||
clk_disable(omap->usbhost1_48m_fck);
|
||||
clk_put(omap->usbhost1_48m_fck);
|
||||
omap->usbhost1_48m_fck = NULL;
|
||||
if (omap->usbhost_fs_fck != NULL) {
|
||||
clk_disable(omap->usbhost_fs_fck);
|
||||
clk_put(omap->usbhost_fs_fck);
|
||||
omap->usbhost_fs_fck = NULL;
|
||||
}
|
||||
|
||||
if (omap->usbhost2_120m_fck != NULL) {
|
||||
clk_disable(omap->usbhost2_120m_fck);
|
||||
clk_put(omap->usbhost2_120m_fck);
|
||||
omap->usbhost2_120m_fck = NULL;
|
||||
if (omap->usbhost_hs_fck != NULL) {
|
||||
clk_disable(omap->usbhost_hs_fck);
|
||||
clk_put(omap->usbhost_hs_fck);
|
||||
omap->usbhost_hs_fck = NULL;
|
||||
}
|
||||
|
||||
if (omap->usbtll_ick != NULL) {
|
||||
@@ -568,6 +716,32 @@ static void omap_stop_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd)
|
||||
omap->usbtll_ick = NULL;
|
||||
}
|
||||
|
||||
if (is_omap_ehci_rev2(omap)) {
|
||||
if (omap->xclk60mhsp1_ck != NULL) {
|
||||
clk_disable(omap->xclk60mhsp1_ck);
|
||||
clk_put(omap->xclk60mhsp1_ck);
|
||||
omap->xclk60mhsp1_ck = NULL;
|
||||
}
|
||||
|
||||
if (omap->utmi_p1_fck != NULL) {
|
||||
clk_disable(omap->utmi_p1_fck);
|
||||
clk_put(omap->utmi_p1_fck);
|
||||
omap->utmi_p1_fck = NULL;
|
||||
}
|
||||
|
||||
if (omap->xclk60mhsp2_ck != NULL) {
|
||||
clk_disable(omap->xclk60mhsp2_ck);
|
||||
clk_put(omap->xclk60mhsp2_ck);
|
||||
omap->xclk60mhsp2_ck = NULL;
|
||||
}
|
||||
|
||||
if (omap->utmi_p2_fck != NULL) {
|
||||
clk_disable(omap->utmi_p2_fck);
|
||||
clk_put(omap->utmi_p2_fck);
|
||||
omap->utmi_p2_fck = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (omap->phy_reset) {
|
||||
if (gpio_is_valid(omap->reset_gpio_port[0]))
|
||||
gpio_free(omap->reset_gpio_port[0]);
|
||||
|
||||
@@ -22,6 +22,9 @@
|
||||
#error "This file is PCI bus glue. CONFIG_PCI must be defined."
|
||||
#endif
|
||||
|
||||
/* defined here to avoid adding to pci_ids.h for single instance use */
|
||||
#define PCI_DEVICE_ID_INTEL_CE4100_USB 0x2e70
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* called after powerup, by probe or system-pm "wakeup" */
|
||||
@@ -41,6 +44,35 @@ static int ehci_pci_reinit(struct ehci_hcd *ehci, struct pci_dev *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ehci_quirk_amd_SB800(struct ehci_hcd *ehci)
|
||||
{
|
||||
struct pci_dev *amd_smbus_dev;
|
||||
u8 rev = 0;
|
||||
|
||||
amd_smbus_dev = pci_get_device(PCI_VENDOR_ID_ATI, 0x4385, NULL);
|
||||
if (!amd_smbus_dev)
|
||||
return 0;
|
||||
|
||||
pci_read_config_byte(amd_smbus_dev, PCI_REVISION_ID, &rev);
|
||||
if (rev < 0x40) {
|
||||
pci_dev_put(amd_smbus_dev);
|
||||
amd_smbus_dev = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!amd_nb_dev)
|
||||
amd_nb_dev = pci_get_device(PCI_VENDOR_ID_AMD, 0x1510, NULL);
|
||||
if (!amd_nb_dev)
|
||||
ehci_err(ehci, "QUIRK: unable to get AMD NB device\n");
|
||||
|
||||
ehci_info(ehci, "QUIRK: Enable AMD SB800 L1 fix\n");
|
||||
|
||||
pci_dev_put(amd_smbus_dev);
|
||||
amd_smbus_dev = NULL;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* called during probe() after chip reset completes */
|
||||
static int ehci_pci_setup(struct usb_hcd *hcd)
|
||||
{
|
||||
@@ -99,6 +131,9 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
|
||||
/* cache this readonly data; minimize chip reads */
|
||||
ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
|
||||
|
||||
if (ehci_quirk_amd_SB800(ehci))
|
||||
ehci->amd_l1_fix = 1;
|
||||
|
||||
retval = ehci_halt(ehci);
|
||||
if (retval)
|
||||
return retval;
|
||||
@@ -137,6 +172,10 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
|
||||
ehci_info(ehci, "disable lpm for langwell/penwell\n");
|
||||
ehci->has_lpm = 0;
|
||||
}
|
||||
if (pdev->device == PCI_DEVICE_ID_INTEL_CE4100_USB) {
|
||||
hcd->has_tt = 1;
|
||||
tdi_reset(ehci);
|
||||
}
|
||||
break;
|
||||
case PCI_VENDOR_ID_TDI:
|
||||
if (pdev->device == PCI_DEVICE_ID_TDI_EHCI) {
|
||||
|
||||
@@ -1590,6 +1590,63 @@ itd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_itd *itd)
|
||||
*hw_p = cpu_to_hc32(ehci, itd->itd_dma | Q_TYPE_ITD);
|
||||
}
|
||||
|
||||
#define AB_REG_BAR_LOW 0xe0
|
||||
#define AB_REG_BAR_HIGH 0xe1
|
||||
#define AB_INDX(addr) ((addr) + 0x00)
|
||||
#define AB_DATA(addr) ((addr) + 0x04)
|
||||
#define NB_PCIE_INDX_ADDR 0xe0
|
||||
#define NB_PCIE_INDX_DATA 0xe4
|
||||
#define NB_PIF0_PWRDOWN_0 0x01100012
|
||||
#define NB_PIF0_PWRDOWN_1 0x01100013
|
||||
|
||||
static void ehci_quirk_amd_L1(struct ehci_hcd *ehci, int disable)
|
||||
{
|
||||
u32 addr, addr_low, addr_high, val;
|
||||
|
||||
outb_p(AB_REG_BAR_LOW, 0xcd6);
|
||||
addr_low = inb_p(0xcd7);
|
||||
outb_p(AB_REG_BAR_HIGH, 0xcd6);
|
||||
addr_high = inb_p(0xcd7);
|
||||
addr = addr_high << 8 | addr_low;
|
||||
outl_p(0x30, AB_INDX(addr));
|
||||
outl_p(0x40, AB_DATA(addr));
|
||||
outl_p(0x34, AB_INDX(addr));
|
||||
val = inl_p(AB_DATA(addr));
|
||||
|
||||
if (disable) {
|
||||
val &= ~0x8;
|
||||
val |= (1 << 4) | (1 << 9);
|
||||
} else {
|
||||
val |= 0x8;
|
||||
val &= ~((1 << 4) | (1 << 9));
|
||||
}
|
||||
outl_p(val, AB_DATA(addr));
|
||||
|
||||
if (amd_nb_dev) {
|
||||
addr = NB_PIF0_PWRDOWN_0;
|
||||
pci_write_config_dword(amd_nb_dev, NB_PCIE_INDX_ADDR, addr);
|
||||
pci_read_config_dword(amd_nb_dev, NB_PCIE_INDX_DATA, &val);
|
||||
if (disable)
|
||||
val &= ~(0x3f << 7);
|
||||
else
|
||||
val |= 0x3f << 7;
|
||||
|
||||
pci_write_config_dword(amd_nb_dev, NB_PCIE_INDX_DATA, val);
|
||||
|
||||
addr = NB_PIF0_PWRDOWN_1;
|
||||
pci_write_config_dword(amd_nb_dev, NB_PCIE_INDX_ADDR, addr);
|
||||
pci_read_config_dword(amd_nb_dev, NB_PCIE_INDX_DATA, &val);
|
||||
if (disable)
|
||||
val &= ~(0x3f << 7);
|
||||
else
|
||||
val |= 0x3f << 7;
|
||||
|
||||
pci_write_config_dword(amd_nb_dev, NB_PCIE_INDX_DATA, val);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* fit urb's itds into the selected schedule slot; activate as needed */
|
||||
static int
|
||||
itd_link_urb (
|
||||
@@ -1616,6 +1673,12 @@ itd_link_urb (
|
||||
urb->interval,
|
||||
next_uframe >> 3, next_uframe & 0x7);
|
||||
}
|
||||
|
||||
if (ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs == 0) {
|
||||
if (ehci->amd_l1_fix == 1)
|
||||
ehci_quirk_amd_L1(ehci, 1);
|
||||
}
|
||||
|
||||
ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs++;
|
||||
|
||||
/* fill iTDs uframe by uframe */
|
||||
@@ -1740,6 +1803,11 @@ itd_complete (
|
||||
(void) disable_periodic(ehci);
|
||||
ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--;
|
||||
|
||||
if (ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs == 0) {
|
||||
if (ehci->amd_l1_fix == 1)
|
||||
ehci_quirk_amd_L1(ehci, 0);
|
||||
}
|
||||
|
||||
if (unlikely(list_is_singular(&stream->td_list))) {
|
||||
ehci_to_hcd(ehci)->self.bandwidth_allocated
|
||||
-= stream->bandwidth;
|
||||
@@ -2025,6 +2093,12 @@ sitd_link_urb (
|
||||
(next_uframe >> 3) & (ehci->periodic_size - 1),
|
||||
stream->interval, hc32_to_cpu(ehci, stream->splits));
|
||||
}
|
||||
|
||||
if (ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs == 0) {
|
||||
if (ehci->amd_l1_fix == 1)
|
||||
ehci_quirk_amd_L1(ehci, 1);
|
||||
}
|
||||
|
||||
ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs++;
|
||||
|
||||
/* fill sITDs frame by frame */
|
||||
@@ -2125,6 +2199,11 @@ sitd_complete (
|
||||
(void) disable_periodic(ehci);
|
||||
ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--;
|
||||
|
||||
if (ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs == 0) {
|
||||
if (ehci->amd_l1_fix == 1)
|
||||
ehci_quirk_amd_L1(ehci, 0);
|
||||
}
|
||||
|
||||
if (list_is_singular(&stream->td_list)) {
|
||||
ehci_to_hcd(ehci)->self.bandwidth_allocated
|
||||
-= stream->bandwidth;
|
||||
|
||||
243
drivers/usb/host/ehci-sh.c
Normal file
243
drivers/usb/host/ehci-sh.c
Normal file
@@ -0,0 +1,243 @@
|
||||
/*
|
||||
* SuperH EHCI host controller driver
|
||||
*
|
||||
* Copyright (C) 2010 Paul Mundt
|
||||
*
|
||||
* Based on ohci-sh.c and ehci-atmel.c.
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU General Public
|
||||
* License. See the file "COPYING" in the main directory of this archive
|
||||
* for more details.
|
||||
*/
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/clk.h>
|
||||
|
||||
struct ehci_sh_priv {
|
||||
struct clk *iclk, *fclk;
|
||||
struct usb_hcd *hcd;
|
||||
};
|
||||
|
||||
static int ehci_sh_reset(struct usb_hcd *hcd)
|
||||
{
|
||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||
int ret;
|
||||
|
||||
ehci->caps = hcd->regs;
|
||||
ehci->regs = hcd->regs + HC_LENGTH(ehci_readl(ehci,
|
||||
&ehci->caps->hc_capbase));
|
||||
|
||||
dbg_hcs_params(ehci, "reset");
|
||||
dbg_hcc_params(ehci, "reset");
|
||||
|
||||
ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
|
||||
|
||||
ret = ehci_halt(ehci);
|
||||
if (unlikely(ret))
|
||||
return ret;
|
||||
|
||||
ret = ehci_init(hcd);
|
||||
if (unlikely(ret))
|
||||
return ret;
|
||||
|
||||
ehci->sbrn = 0x20;
|
||||
|
||||
ehci_reset(ehci);
|
||||
ehci_port_power(ehci, 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct hc_driver ehci_sh_hc_driver = {
|
||||
.description = hcd_name,
|
||||
.product_desc = "SuperH EHCI",
|
||||
.hcd_priv_size = sizeof(struct ehci_hcd),
|
||||
|
||||
/*
|
||||
* generic hardware linkage
|
||||
*/
|
||||
.irq = ehci_irq,
|
||||
.flags = HCD_USB2 | HCD_MEMORY,
|
||||
|
||||
/*
|
||||
* basic lifecycle operations
|
||||
*/
|
||||
.reset = ehci_sh_reset,
|
||||
.start = ehci_run,
|
||||
.stop = ehci_stop,
|
||||
.shutdown = ehci_shutdown,
|
||||
|
||||
/*
|
||||
* managing i/o requests and associated device resources
|
||||
*/
|
||||
.urb_enqueue = ehci_urb_enqueue,
|
||||
.urb_dequeue = ehci_urb_dequeue,
|
||||
.endpoint_disable = ehci_endpoint_disable,
|
||||
.endpoint_reset = ehci_endpoint_reset,
|
||||
|
||||
/*
|
||||
* scheduling support
|
||||
*/
|
||||
.get_frame_number = ehci_get_frame,
|
||||
|
||||
/*
|
||||
* root hub support
|
||||
*/
|
||||
.hub_status_data = ehci_hub_status_data,
|
||||
.hub_control = ehci_hub_control,
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
.bus_suspend = ehci_bus_suspend,
|
||||
.bus_resume = ehci_bus_resume,
|
||||
#endif
|
||||
|
||||
.relinquish_port = ehci_relinquish_port,
|
||||
.port_handed_over = ehci_port_handed_over,
|
||||
.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
|
||||
};
|
||||
|
||||
static int ehci_hcd_sh_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct hc_driver *driver = &ehci_sh_hc_driver;
|
||||
struct resource *res;
|
||||
struct ehci_sh_priv *priv;
|
||||
struct usb_hcd *hcd;
|
||||
int irq, ret;
|
||||
|
||||
if (usb_disabled())
|
||||
return -ENODEV;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev,
|
||||
"Found HC with no register addr. Check %s setup!\n",
|
||||
dev_name(&pdev->dev));
|
||||
ret = -ENODEV;
|
||||
goto fail_create_hcd;
|
||||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq <= 0) {
|
||||
dev_err(&pdev->dev,
|
||||
"Found HC with no IRQ. Check %s setup!\n",
|
||||
dev_name(&pdev->dev));
|
||||
ret = -ENODEV;
|
||||
goto fail_create_hcd;
|
||||
}
|
||||
|
||||
/* initialize hcd */
|
||||
hcd = usb_create_hcd(&ehci_sh_hc_driver, &pdev->dev,
|
||||
dev_name(&pdev->dev));
|
||||
if (!hcd) {
|
||||
ret = -ENOMEM;
|
||||
goto fail_create_hcd;
|
||||
}
|
||||
|
||||
hcd->rsrc_start = res->start;
|
||||
hcd->rsrc_len = resource_size(res);
|
||||
|
||||
if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len,
|
||||
driver->description)) {
|
||||
dev_dbg(&pdev->dev, "controller already in use\n");
|
||||
ret = -EBUSY;
|
||||
goto fail_request_resource;
|
||||
}
|
||||
|
||||
hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len);
|
||||
if (hcd->regs == NULL) {
|
||||
dev_dbg(&pdev->dev, "error mapping memory\n");
|
||||
ret = -ENXIO;
|
||||
goto fail_ioremap;
|
||||
}
|
||||
|
||||
priv = kmalloc(sizeof(struct ehci_sh_priv), GFP_KERNEL);
|
||||
if (!priv) {
|
||||
dev_dbg(&pdev->dev, "error allocating priv data\n");
|
||||
ret = -ENOMEM;
|
||||
goto fail_alloc;
|
||||
}
|
||||
|
||||
/* These are optional, we don't care if they fail */
|
||||
priv->fclk = clk_get(&pdev->dev, "usb_fck");
|
||||
if (IS_ERR(priv->fclk))
|
||||
priv->fclk = NULL;
|
||||
|
||||
priv->iclk = clk_get(&pdev->dev, "usb_ick");
|
||||
if (IS_ERR(priv->iclk))
|
||||
priv->iclk = NULL;
|
||||
|
||||
clk_enable(priv->fclk);
|
||||
clk_enable(priv->iclk);
|
||||
|
||||
ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED);
|
||||
if (ret != 0) {
|
||||
dev_err(&pdev->dev, "Failed to add hcd");
|
||||
goto fail_add_hcd;
|
||||
}
|
||||
|
||||
priv->hcd = hcd;
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
return ret;
|
||||
|
||||
fail_add_hcd:
|
||||
clk_disable(priv->iclk);
|
||||
clk_disable(priv->fclk);
|
||||
|
||||
clk_put(priv->iclk);
|
||||
clk_put(priv->fclk);
|
||||
|
||||
kfree(priv);
|
||||
fail_alloc:
|
||||
iounmap(hcd->regs);
|
||||
fail_ioremap:
|
||||
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
|
||||
fail_request_resource:
|
||||
usb_put_hcd(hcd);
|
||||
fail_create_hcd:
|
||||
dev_err(&pdev->dev, "init %s fail, %d\n", dev_name(&pdev->dev), ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __exit ehci_hcd_sh_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ehci_sh_priv *priv = platform_get_drvdata(pdev);
|
||||
struct usb_hcd *hcd = priv->hcd;
|
||||
|
||||
usb_remove_hcd(hcd);
|
||||
iounmap(hcd->regs);
|
||||
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
|
||||
usb_put_hcd(hcd);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
clk_disable(priv->fclk);
|
||||
clk_disable(priv->iclk);
|
||||
|
||||
clk_put(priv->fclk);
|
||||
clk_put(priv->iclk);
|
||||
|
||||
kfree(priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ehci_hcd_sh_shutdown(struct platform_device *pdev)
|
||||
{
|
||||
struct ehci_sh_priv *priv = platform_get_drvdata(pdev);
|
||||
struct usb_hcd *hcd = priv->hcd;
|
||||
|
||||
if (hcd->driver->shutdown)
|
||||
hcd->driver->shutdown(hcd);
|
||||
}
|
||||
|
||||
static struct platform_driver ehci_hcd_sh_driver = {
|
||||
.probe = ehci_hcd_sh_probe,
|
||||
.remove = __exit_p(ehci_hcd_sh_remove),
|
||||
.shutdown = ehci_hcd_sh_shutdown,
|
||||
.driver = {
|
||||
.name = "sh_ehci",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
MODULE_ALIAS("platform:sh_ehci");
|
||||
212
drivers/usb/host/ehci-spear.c
Normal file
212
drivers/usb/host/ehci-spear.c
Normal file
@@ -0,0 +1,212 @@
|
||||
/*
|
||||
* Driver for EHCI HCD on SPEAR SOC
|
||||
*
|
||||
* Copyright (C) 2010 ST Micro Electronics,
|
||||
* Deepak Sikri <deepak.sikri@st.com>
|
||||
*
|
||||
* Based on various ehci-*.c drivers
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU General Public
|
||||
* License. See the file COPYING in the main directory of this archive for
|
||||
* more details.
|
||||
*/
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/clk.h>
|
||||
|
||||
struct spear_ehci {
|
||||
struct ehci_hcd ehci;
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
#define to_spear_ehci(hcd) (struct spear_ehci *)hcd_to_ehci(hcd)
|
||||
|
||||
static void spear_start_ehci(struct spear_ehci *ehci)
|
||||
{
|
||||
clk_enable(ehci->clk);
|
||||
}
|
||||
|
||||
static void spear_stop_ehci(struct spear_ehci *ehci)
|
||||
{
|
||||
clk_disable(ehci->clk);
|
||||
}
|
||||
|
||||
static int ehci_spear_setup(struct usb_hcd *hcd)
|
||||
{
|
||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||
int retval = 0;
|
||||
|
||||
/* registers start at offset 0x0 */
|
||||
ehci->caps = hcd->regs;
|
||||
ehci->regs = hcd->regs + HC_LENGTH(ehci_readl(ehci,
|
||||
&ehci->caps->hc_capbase));
|
||||
/* cache this readonly data; minimize chip reads */
|
||||
ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
|
||||
retval = ehci_halt(ehci);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
retval = ehci_init(hcd);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
ehci_reset(ehci);
|
||||
ehci_port_power(ehci, 0);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static const struct hc_driver ehci_spear_hc_driver = {
|
||||
.description = hcd_name,
|
||||
.product_desc = "SPEAr EHCI",
|
||||
.hcd_priv_size = sizeof(struct spear_ehci),
|
||||
|
||||
/* generic hardware linkage */
|
||||
.irq = ehci_irq,
|
||||
.flags = HCD_MEMORY | HCD_USB2,
|
||||
|
||||
/* basic lifecycle operations */
|
||||
.reset = ehci_spear_setup,
|
||||
.start = ehci_run,
|
||||
.stop = ehci_stop,
|
||||
.shutdown = ehci_shutdown,
|
||||
|
||||
/* managing i/o requests and associated device resources */
|
||||
.urb_enqueue = ehci_urb_enqueue,
|
||||
.urb_dequeue = ehci_urb_dequeue,
|
||||
.endpoint_disable = ehci_endpoint_disable,
|
||||
.endpoint_reset = ehci_endpoint_reset,
|
||||
|
||||
/* scheduling support */
|
||||
.get_frame_number = ehci_get_frame,
|
||||
|
||||
/* root hub support */
|
||||
.hub_status_data = ehci_hub_status_data,
|
||||
.hub_control = ehci_hub_control,
|
||||
.bus_suspend = ehci_bus_suspend,
|
||||
.bus_resume = ehci_bus_resume,
|
||||
.relinquish_port = ehci_relinquish_port,
|
||||
.port_handed_over = ehci_port_handed_over,
|
||||
.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
|
||||
};
|
||||
|
||||
static int spear_ehci_hcd_drv_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct usb_hcd *hcd ;
|
||||
struct spear_ehci *ehci;
|
||||
struct resource *res;
|
||||
struct clk *usbh_clk;
|
||||
const struct hc_driver *driver = &ehci_spear_hc_driver;
|
||||
int *pdata = pdev->dev.platform_data;
|
||||
int irq, retval;
|
||||
char clk_name[20] = "usbh_clk";
|
||||
|
||||
if (pdata == NULL)
|
||||
return -EFAULT;
|
||||
|
||||
if (usb_disabled())
|
||||
return -ENODEV;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
retval = irq;
|
||||
goto fail_irq_get;
|
||||
}
|
||||
|
||||
if (*pdata >= 0)
|
||||
sprintf(clk_name, "usbh.%01d_clk", *pdata);
|
||||
|
||||
usbh_clk = clk_get(NULL, clk_name);
|
||||
if (IS_ERR(usbh_clk)) {
|
||||
dev_err(&pdev->dev, "Error getting interface clock\n");
|
||||
retval = PTR_ERR(usbh_clk);
|
||||
goto fail_get_usbh_clk;
|
||||
}
|
||||
|
||||
hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev));
|
||||
if (!hcd) {
|
||||
retval = -ENOMEM;
|
||||
goto fail_create_hcd;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
retval = -ENODEV;
|
||||
goto fail_request_resource;
|
||||
}
|
||||
|
||||
hcd->rsrc_start = res->start;
|
||||
hcd->rsrc_len = resource_size(res);
|
||||
if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len,
|
||||
driver->description)) {
|
||||
retval = -EBUSY;
|
||||
goto fail_request_resource;
|
||||
}
|
||||
|
||||
hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
|
||||
if (hcd->regs == NULL) {
|
||||
dev_dbg(&pdev->dev, "error mapping memory\n");
|
||||
retval = -ENOMEM;
|
||||
goto fail_ioremap;
|
||||
}
|
||||
|
||||
ehci = (struct spear_ehci *)hcd_to_ehci(hcd);
|
||||
ehci->clk = usbh_clk;
|
||||
|
||||
spear_start_ehci(ehci);
|
||||
retval = usb_add_hcd(hcd, irq, IRQF_SHARED | IRQF_DISABLED);
|
||||
if (retval)
|
||||
goto fail_add_hcd;
|
||||
|
||||
return retval;
|
||||
|
||||
fail_add_hcd:
|
||||
spear_stop_ehci(ehci);
|
||||
iounmap(hcd->regs);
|
||||
fail_ioremap:
|
||||
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
|
||||
fail_request_resource:
|
||||
usb_put_hcd(hcd);
|
||||
fail_create_hcd:
|
||||
clk_put(usbh_clk);
|
||||
fail_get_usbh_clk:
|
||||
fail_irq_get:
|
||||
dev_err(&pdev->dev, "init fail, %d\n", retval);
|
||||
|
||||
return retval ;
|
||||
}
|
||||
|
||||
static int spear_ehci_hcd_drv_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct usb_hcd *hcd = platform_get_drvdata(pdev);
|
||||
struct spear_ehci *ehci_p = to_spear_ehci(hcd);
|
||||
|
||||
if (!hcd)
|
||||
return 0;
|
||||
if (in_interrupt())
|
||||
BUG();
|
||||
usb_remove_hcd(hcd);
|
||||
|
||||
if (ehci_p->clk)
|
||||
spear_stop_ehci(ehci_p);
|
||||
iounmap(hcd->regs);
|
||||
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
|
||||
usb_put_hcd(hcd);
|
||||
|
||||
if (ehci_p->clk)
|
||||
clk_put(ehci_p->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver spear_ehci_hcd_driver = {
|
||||
.probe = spear_ehci_hcd_drv_probe,
|
||||
.remove = spear_ehci_hcd_drv_remove,
|
||||
.shutdown = usb_hcd_platform_shutdown,
|
||||
.driver = {
|
||||
.name = "spear-ehci",
|
||||
.bus = &platform_bus_type
|
||||
}
|
||||
};
|
||||
|
||||
MODULE_ALIAS("platform:spear-ehci");
|
||||
172
drivers/usb/host/ehci-vt8500.c
Normal file
172
drivers/usb/host/ehci-vt8500.c
Normal file
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* drivers/usb/host/ehci-vt8500.c
|
||||
*
|
||||
* Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
|
||||
*
|
||||
* Based on ehci-au1xxx.c
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
static int ehci_update_device(struct usb_hcd *hcd, struct usb_device *udev)
|
||||
{
|
||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||
int rc = 0;
|
||||
|
||||
if (!udev->parent) /* udev is root hub itself, impossible */
|
||||
rc = -1;
|
||||
/* we only support lpm device connected to root hub yet */
|
||||
if (ehci->has_lpm && !udev->parent->parent) {
|
||||
rc = ehci_lpm_set_da(ehci, udev->devnum, udev->portnum);
|
||||
if (!rc)
|
||||
rc = ehci_lpm_check(ehci, udev->portnum);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static const struct hc_driver vt8500_ehci_hc_driver = {
|
||||
.description = hcd_name,
|
||||
.product_desc = "VT8500 EHCI",
|
||||
.hcd_priv_size = sizeof(struct ehci_hcd),
|
||||
|
||||
/*
|
||||
* generic hardware linkage
|
||||
*/
|
||||
.irq = ehci_irq,
|
||||
.flags = HCD_MEMORY | HCD_USB2,
|
||||
|
||||
/*
|
||||
* basic lifecycle operations
|
||||
*/
|
||||
.reset = ehci_init,
|
||||
.start = ehci_run,
|
||||
.stop = ehci_stop,
|
||||
.shutdown = ehci_shutdown,
|
||||
|
||||
/*
|
||||
* managing i/o requests and associated device resources
|
||||
*/
|
||||
.urb_enqueue = ehci_urb_enqueue,
|
||||
.urb_dequeue = ehci_urb_dequeue,
|
||||
.endpoint_disable = ehci_endpoint_disable,
|
||||
.endpoint_reset = ehci_endpoint_reset,
|
||||
|
||||
/*
|
||||
* scheduling support
|
||||
*/
|
||||
.get_frame_number = ehci_get_frame,
|
||||
|
||||
/*
|
||||
* root hub support
|
||||
*/
|
||||
.hub_status_data = ehci_hub_status_data,
|
||||
.hub_control = ehci_hub_control,
|
||||
.bus_suspend = ehci_bus_suspend,
|
||||
.bus_resume = ehci_bus_resume,
|
||||
.relinquish_port = ehci_relinquish_port,
|
||||
.port_handed_over = ehci_port_handed_over,
|
||||
|
||||
/*
|
||||
* call back when device connected and addressed
|
||||
*/
|
||||
.update_device = ehci_update_device,
|
||||
|
||||
.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
|
||||
};
|
||||
|
||||
static int vt8500_ehci_drv_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct usb_hcd *hcd;
|
||||
struct ehci_hcd *ehci;
|
||||
struct resource *res;
|
||||
int ret;
|
||||
|
||||
if (usb_disabled())
|
||||
return -ENODEV;
|
||||
|
||||
if (pdev->resource[1].flags != IORESOURCE_IRQ) {
|
||||
pr_debug("resource[1] is not IORESOURCE_IRQ");
|
||||
return -ENOMEM;
|
||||
}
|
||||
hcd = usb_create_hcd(&vt8500_ehci_hc_driver, &pdev->dev, "VT8500");
|
||||
if (!hcd)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
hcd->rsrc_start = res->start;
|
||||
hcd->rsrc_len = resource_size(res);
|
||||
|
||||
if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
|
||||
pr_debug("request_mem_region failed");
|
||||
ret = -EBUSY;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
|
||||
if (!hcd->regs) {
|
||||
pr_debug("ioremap failed");
|
||||
ret = -ENOMEM;
|
||||
goto err2;
|
||||
}
|
||||
|
||||
ehci = hcd_to_ehci(hcd);
|
||||
ehci->caps = hcd->regs;
|
||||
ehci->regs = hcd->regs + HC_LENGTH(readl(&ehci->caps->hc_capbase));
|
||||
|
||||
dbg_hcs_params(ehci, "reset");
|
||||
dbg_hcc_params(ehci, "reset");
|
||||
|
||||
/* cache this readonly data; minimize chip reads */
|
||||
ehci->hcs_params = readl(&ehci->caps->hcs_params);
|
||||
|
||||
ehci_port_power(ehci, 1);
|
||||
|
||||
ret = usb_add_hcd(hcd, pdev->resource[1].start,
|
||||
IRQF_DISABLED | IRQF_SHARED);
|
||||
if (ret == 0) {
|
||||
platform_set_drvdata(pdev, hcd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
iounmap(hcd->regs);
|
||||
err2:
|
||||
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
|
||||
err1:
|
||||
usb_put_hcd(hcd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int vt8500_ehci_drv_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct usb_hcd *hcd = platform_get_drvdata(pdev);
|
||||
|
||||
usb_remove_hcd(hcd);
|
||||
iounmap(hcd->regs);
|
||||
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
|
||||
usb_put_hcd(hcd);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver vt8500_ehci_driver = {
|
||||
.probe = vt8500_ehci_drv_probe,
|
||||
.remove = vt8500_ehci_drv_remove,
|
||||
.shutdown = usb_hcd_platform_shutdown,
|
||||
.driver = {
|
||||
.name = "vt8500-ehci",
|
||||
.owner = THIS_MODULE,
|
||||
}
|
||||
};
|
||||
|
||||
MODULE_ALIAS("platform:vt8500-ehci");
|
||||
@@ -130,6 +130,7 @@ static const struct hc_driver ehci_w90x900_hc_driver = {
|
||||
.urb_enqueue = ehci_urb_enqueue,
|
||||
.urb_dequeue = ehci_urb_dequeue,
|
||||
.endpoint_disable = ehci_endpoint_disable,
|
||||
.endpoint_reset = ehci_endpoint_reset,
|
||||
|
||||
/*
|
||||
* scheduling support
|
||||
@@ -147,6 +148,8 @@ static const struct hc_driver ehci_w90x900_hc_driver = {
|
||||
#endif
|
||||
.relinquish_port = ehci_relinquish_port,
|
||||
.port_handed_over = ehci_port_handed_over,
|
||||
|
||||
.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
|
||||
};
|
||||
|
||||
static int __devinit ehci_w90x900_probe(struct platform_device *pdev)
|
||||
|
||||
@@ -117,6 +117,7 @@ static const struct hc_driver ehci_xilinx_of_hc_driver = {
|
||||
.urb_enqueue = ehci_urb_enqueue,
|
||||
.urb_dequeue = ehci_urb_dequeue,
|
||||
.endpoint_disable = ehci_endpoint_disable,
|
||||
.endpoint_reset = ehci_endpoint_reset,
|
||||
|
||||
/*
|
||||
* scheduling support
|
||||
|
||||
@@ -131,6 +131,7 @@ struct ehci_hcd { /* one per controller */
|
||||
unsigned has_amcc_usb23:1;
|
||||
unsigned need_io_watchdog:1;
|
||||
unsigned broken_periodic:1;
|
||||
unsigned amd_l1_fix:1;
|
||||
unsigned fs_i_thresh:1; /* Intel iso scheduling */
|
||||
unsigned use_dummy_qh:1; /* AMD Frame List table quirk*/
|
||||
|
||||
|
||||
@@ -1081,6 +1081,11 @@ MODULE_LICENSE ("GPL");
|
||||
#define OF_PLATFORM_DRIVER ohci_hcd_ppc_of_driver
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PLAT_SPEAR
|
||||
#include "ohci-spear.c"
|
||||
#define PLATFORM_DRIVER spear_ohci_hcd_driver
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PPC_PS3
|
||||
#include "ohci-ps3.c"
|
||||
#define PS3_SYSTEM_BUS_DRIVER ps3_ohci_driver
|
||||
|
||||
@@ -109,7 +109,7 @@ static int ohci_hcd_sh_probe(struct platform_device *pdev)
|
||||
hcd->regs = (void __iomem *)res->start;
|
||||
hcd->rsrc_start = res->start;
|
||||
hcd->rsrc_len = resource_size(res);
|
||||
ret = usb_add_hcd(hcd, irq, IRQF_DISABLED);
|
||||
ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED);
|
||||
if (ret != 0) {
|
||||
err("Failed to add hcd");
|
||||
usb_put_hcd(hcd);
|
||||
|
||||
240
drivers/usb/host/ohci-spear.c
Normal file
240
drivers/usb/host/ohci-spear.c
Normal file
@@ -0,0 +1,240 @@
|
||||
/*
|
||||
* OHCI HCD (Host Controller Driver) for USB.
|
||||
*
|
||||
* Copyright (C) 2010 ST Microelectronics.
|
||||
* Deepak Sikri<deepak.sikri@st.com>
|
||||
*
|
||||
* Based on various ohci-*.c drivers
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public
|
||||
* License version 2. This program is licensed "as is" without any
|
||||
* warranty of any kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#include <linux/signal.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/clk.h>
|
||||
|
||||
struct spear_ohci {
|
||||
struct ohci_hcd ohci;
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
#define to_spear_ohci(hcd) (struct spear_ohci *)hcd_to_ohci(hcd)
|
||||
|
||||
static void spear_start_ohci(struct spear_ohci *ohci)
|
||||
{
|
||||
clk_enable(ohci->clk);
|
||||
}
|
||||
|
||||
static void spear_stop_ohci(struct spear_ohci *ohci)
|
||||
{
|
||||
clk_disable(ohci->clk);
|
||||
}
|
||||
|
||||
static int __devinit ohci_spear_start(struct usb_hcd *hcd)
|
||||
{
|
||||
struct ohci_hcd *ohci = hcd_to_ohci(hcd);
|
||||
int ret;
|
||||
|
||||
ret = ohci_init(ohci);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ohci->regs = hcd->regs;
|
||||
|
||||
ret = ohci_run(ohci);
|
||||
if (ret < 0) {
|
||||
dev_err(hcd->self.controller, "can't start\n");
|
||||
ohci_stop(hcd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
create_debug_files(ohci);
|
||||
|
||||
#ifdef DEBUG
|
||||
ohci_dump(ohci, 1);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct hc_driver ohci_spear_hc_driver = {
|
||||
.description = hcd_name,
|
||||
.product_desc = "SPEAr OHCI",
|
||||
.hcd_priv_size = sizeof(struct spear_ohci),
|
||||
|
||||
/* generic hardware linkage */
|
||||
.irq = ohci_irq,
|
||||
.flags = HCD_USB11 | HCD_MEMORY,
|
||||
|
||||
/* basic lifecycle operations */
|
||||
.start = ohci_spear_start,
|
||||
.stop = ohci_stop,
|
||||
.shutdown = ohci_shutdown,
|
||||
#ifdef CONFIG_PM
|
||||
.bus_suspend = ohci_bus_suspend,
|
||||
.bus_resume = ohci_bus_resume,
|
||||
#endif
|
||||
|
||||
/* managing i/o requests and associated device resources */
|
||||
.urb_enqueue = ohci_urb_enqueue,
|
||||
.urb_dequeue = ohci_urb_dequeue,
|
||||
.endpoint_disable = ohci_endpoint_disable,
|
||||
|
||||
/* scheduling support */
|
||||
.get_frame_number = ohci_get_frame,
|
||||
|
||||
/* root hub support */
|
||||
.hub_status_data = ohci_hub_status_data,
|
||||
.hub_control = ohci_hub_control,
|
||||
|
||||
.start_port_reset = ohci_start_port_reset,
|
||||
};
|
||||
|
||||
static int spear_ohci_hcd_drv_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct hc_driver *driver = &ohci_spear_hc_driver;
|
||||
struct usb_hcd *hcd = NULL;
|
||||
struct clk *usbh_clk;
|
||||
struct spear_ohci *ohci_p;
|
||||
struct resource *res;
|
||||
int retval, irq;
|
||||
int *pdata = pdev->dev.platform_data;
|
||||
char clk_name[20] = "usbh_clk";
|
||||
|
||||
if (pdata == NULL)
|
||||
return -EFAULT;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
retval = irq;
|
||||
goto fail_irq_get;
|
||||
}
|
||||
|
||||
if (*pdata >= 0)
|
||||
sprintf(clk_name, "usbh.%01d_clk", *pdata);
|
||||
|
||||
usbh_clk = clk_get(NULL, clk_name);
|
||||
if (IS_ERR(usbh_clk)) {
|
||||
dev_err(&pdev->dev, "Error getting interface clock\n");
|
||||
retval = PTR_ERR(usbh_clk);
|
||||
goto fail_get_usbh_clk;
|
||||
}
|
||||
|
||||
hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev));
|
||||
if (!hcd) {
|
||||
retval = -ENOMEM;
|
||||
goto fail_create_hcd;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
retval = -ENODEV;
|
||||
goto fail_request_resource;
|
||||
}
|
||||
|
||||
hcd->rsrc_start = pdev->resource[0].start;
|
||||
hcd->rsrc_len = resource_size(res);
|
||||
if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
|
||||
dev_dbg(&pdev->dev, "request_mem_region failed\n");
|
||||
retval = -EBUSY;
|
||||
goto fail_request_resource;
|
||||
}
|
||||
|
||||
hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
|
||||
if (!hcd->regs) {
|
||||
dev_dbg(&pdev->dev, "ioremap failed\n");
|
||||
retval = -ENOMEM;
|
||||
goto fail_ioremap;
|
||||
}
|
||||
|
||||
ohci_p = (struct spear_ohci *)hcd_to_ohci(hcd);
|
||||
ohci_p->clk = usbh_clk;
|
||||
spear_start_ohci(ohci_p);
|
||||
ohci_hcd_init(hcd_to_ohci(hcd));
|
||||
|
||||
retval = usb_add_hcd(hcd, platform_get_irq(pdev, 0), IRQF_DISABLED);
|
||||
if (retval == 0)
|
||||
return retval;
|
||||
|
||||
spear_stop_ohci(ohci_p);
|
||||
iounmap(hcd->regs);
|
||||
fail_ioremap:
|
||||
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
|
||||
fail_request_resource:
|
||||
usb_put_hcd(hcd);
|
||||
fail_create_hcd:
|
||||
clk_put(usbh_clk);
|
||||
fail_get_usbh_clk:
|
||||
fail_irq_get:
|
||||
dev_err(&pdev->dev, "init fail, %d\n", retval);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int spear_ohci_hcd_drv_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct usb_hcd *hcd = platform_get_drvdata(pdev);
|
||||
struct spear_ohci *ohci_p = to_spear_ohci(hcd);
|
||||
|
||||
usb_remove_hcd(hcd);
|
||||
if (ohci_p->clk)
|
||||
spear_stop_ohci(ohci_p);
|
||||
|
||||
iounmap(hcd->regs);
|
||||
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
|
||||
usb_put_hcd(hcd);
|
||||
|
||||
if (ohci_p->clk)
|
||||
clk_put(ohci_p->clk);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_PM)
|
||||
static int spear_ohci_hcd_drv_suspend(struct platform_device *dev,
|
||||
pm_message_t message)
|
||||
{
|
||||
struct usb_hcd *hcd = platform_get_drvdata(dev);
|
||||
struct ohci_hcd *ohci = hcd_to_ohci(hcd);
|
||||
struct spear_ohci *ohci_p = to_spear_ohci(hcd);
|
||||
|
||||
if (time_before(jiffies, ohci->next_statechange))
|
||||
msleep(5);
|
||||
ohci->next_statechange = jiffies;
|
||||
|
||||
spear_stop_ohci(ohci_p);
|
||||
ohci_to_hcd(ohci)->state = HC_STATE_SUSPENDED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spear_ohci_hcd_drv_resume(struct platform_device *dev)
|
||||
{
|
||||
struct usb_hcd *hcd = platform_get_drvdata(dev);
|
||||
struct ohci_hcd *ohci = hcd_to_ohci(hcd);
|
||||
struct spear_ohci *ohci_p = to_spear_ohci(hcd);
|
||||
|
||||
if (time_before(jiffies, ohci->next_statechange))
|
||||
msleep(5);
|
||||
ohci->next_statechange = jiffies;
|
||||
|
||||
spear_start_ohci(ohci_p);
|
||||
ohci_finish_controller_resume(hcd);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Driver definition to register with the platform bus */
|
||||
static struct platform_driver spear_ohci_hcd_driver = {
|
||||
.probe = spear_ohci_hcd_drv_probe,
|
||||
.remove = spear_ohci_hcd_drv_remove,
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = spear_ohci_hcd_drv_suspend,
|
||||
.resume = spear_ohci_hcd_drv_resume,
|
||||
#endif
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "spear-ohci",
|
||||
},
|
||||
};
|
||||
|
||||
MODULE_ALIAS("platform:spear-ohci");
|
||||
@@ -569,7 +569,7 @@ static int uhci_init(struct usb_hcd *hcd)
|
||||
*/
|
||||
static void uhci_shutdown(struct pci_dev *pdev)
|
||||
{
|
||||
struct usb_hcd *hcd = (struct usb_hcd *) pci_get_drvdata(pdev);
|
||||
struct usb_hcd *hcd = pci_get_drvdata(pdev);
|
||||
|
||||
uhci_hc_died(hcd_to_uhci(hcd));
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ static void uhci_set_next_interrupt(struct uhci_hcd *uhci)
|
||||
{
|
||||
if (uhci->is_stopped)
|
||||
mod_timer(&uhci_to_hcd(uhci)->rh_timer, jiffies);
|
||||
uhci->term_td->status |= cpu_to_le32(TD_CTRL_IOC);
|
||||
uhci->term_td->status |= cpu_to_le32(TD_CTRL_IOC);
|
||||
}
|
||||
|
||||
static inline void uhci_clear_next_interrupt(struct uhci_hcd *uhci)
|
||||
@@ -195,7 +195,9 @@ static inline void uhci_remove_td_from_frame_list(struct uhci_hcd *uhci,
|
||||
} else {
|
||||
struct uhci_td *ntd;
|
||||
|
||||
ntd = list_entry(td->fl_list.next, struct uhci_td, fl_list);
|
||||
ntd = list_entry(td->fl_list.next,
|
||||
struct uhci_td,
|
||||
fl_list);
|
||||
uhci->frame[td->frame] = LINK_TO_TD(ntd);
|
||||
uhci->frame_cpu[td->frame] = ntd;
|
||||
}
|
||||
@@ -728,7 +730,7 @@ static inline struct urb_priv *uhci_alloc_urb_priv(struct uhci_hcd *uhci,
|
||||
|
||||
urbp->urb = urb;
|
||||
urb->hcpriv = urbp;
|
||||
|
||||
|
||||
INIT_LIST_HEAD(&urbp->node);
|
||||
INIT_LIST_HEAD(&urbp->td_list);
|
||||
|
||||
@@ -846,7 +848,7 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb,
|
||||
|
||||
/* Alternate Data0/1 (start with Data1) */
|
||||
destination ^= TD_TOKEN_TOGGLE;
|
||||
|
||||
|
||||
uhci_add_td_to_urbp(td, urbp);
|
||||
uhci_fill_td(td, status, destination | uhci_explen(pktsze),
|
||||
data);
|
||||
@@ -857,7 +859,7 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb,
|
||||
}
|
||||
|
||||
/*
|
||||
* Build the final TD for control status
|
||||
* Build the final TD for control status
|
||||
*/
|
||||
td = uhci_alloc_td(uhci);
|
||||
if (!td)
|
||||
|
||||
@@ -356,7 +356,7 @@ static void __exit whci_hc_driver_exit(void)
|
||||
module_exit(whci_hc_driver_exit);
|
||||
|
||||
/* PCI device ID's that we handle (so it gets loaded) */
|
||||
static struct pci_device_id whci_hcd_id_table[] = {
|
||||
static struct pci_device_id __used whci_hcd_id_table[] = {
|
||||
{ PCI_DEVICE_CLASS(PCI_CLASS_WIRELESS_WHCI, ~0) },
|
||||
{ /* empty last entry */ }
|
||||
};
|
||||
|
||||
@@ -436,6 +436,28 @@ static unsigned int mon_bin_get_data(const struct mon_reader_bin *rp,
|
||||
return length;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is the look-ahead pass in case of 'C Zi', when actual_length cannot
|
||||
* be used to determine the length of the whole contiguous buffer.
|
||||
*/
|
||||
static unsigned int mon_bin_collate_isodesc(const struct mon_reader_bin *rp,
|
||||
struct urb *urb, unsigned int ndesc)
|
||||
{
|
||||
struct usb_iso_packet_descriptor *fp;
|
||||
unsigned int length;
|
||||
|
||||
length = 0;
|
||||
fp = urb->iso_frame_desc;
|
||||
while (ndesc-- != 0) {
|
||||
if (fp->actual_length != 0) {
|
||||
if (fp->offset + fp->actual_length > length)
|
||||
length = fp->offset + fp->actual_length;
|
||||
}
|
||||
fp++;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
static void mon_bin_get_isodesc(const struct mon_reader_bin *rp,
|
||||
unsigned int offset, struct urb *urb, char ev_type, unsigned int ndesc)
|
||||
{
|
||||
@@ -478,6 +500,10 @@ static void mon_bin_event(struct mon_reader_bin *rp, struct urb *urb,
|
||||
/*
|
||||
* Find the maximum allowable length, then allocate space.
|
||||
*/
|
||||
urb_length = (ev_type == 'S') ?
|
||||
urb->transfer_buffer_length : urb->actual_length;
|
||||
length = urb_length;
|
||||
|
||||
if (usb_endpoint_xfer_isoc(epd)) {
|
||||
if (urb->number_of_packets < 0) {
|
||||
ndesc = 0;
|
||||
@@ -486,14 +512,16 @@ static void mon_bin_event(struct mon_reader_bin *rp, struct urb *urb,
|
||||
} else {
|
||||
ndesc = urb->number_of_packets;
|
||||
}
|
||||
if (ev_type == 'C' && usb_urb_dir_in(urb))
|
||||
length = mon_bin_collate_isodesc(rp, urb, ndesc);
|
||||
} else {
|
||||
ndesc = 0;
|
||||
}
|
||||
lendesc = ndesc*sizeof(struct mon_bin_isodesc);
|
||||
|
||||
urb_length = (ev_type == 'S') ?
|
||||
urb->transfer_buffer_length : urb->actual_length;
|
||||
length = urb_length;
|
||||
/* not an issue unless there's a subtle bug in a HCD somewhere */
|
||||
if (length >= urb->transfer_buffer_length)
|
||||
length = urb->transfer_buffer_length;
|
||||
|
||||
if (length >= rp->b_size/5)
|
||||
length = rp->b_size/5;
|
||||
|
||||
@@ -12,6 +12,7 @@ config USB_MUSB_HDRC
|
||||
depends on (ARM || (BF54x && !BF544) || (BF52x && !BF522 && !BF523))
|
||||
select NOP_USB_XCEIV if (ARCH_DAVINCI || MACH_OMAP3EVM || BLACKFIN)
|
||||
select TWL4030_USB if MACH_OMAP_3430SDP
|
||||
select TWL6030_USB if MACH_OMAP_4430SDP || MACH_OMAP4_PANDA
|
||||
select USB_OTG_UTILS
|
||||
tristate 'Inventra Highspeed Dual Role Controller (TI, ADI, ...)'
|
||||
help
|
||||
@@ -30,57 +31,41 @@ config USB_MUSB_HDRC
|
||||
If you do not know what this is, please say N.
|
||||
|
||||
To compile this driver as a module, choose M here; the
|
||||
module will be called "musb_hdrc".
|
||||
module will be called "musb-hdrc".
|
||||
|
||||
config USB_MUSB_SOC
|
||||
boolean
|
||||
choice
|
||||
prompt "Platform Glue Layer"
|
||||
depends on USB_MUSB_HDRC
|
||||
default y if ARCH_DAVINCI
|
||||
default y if ARCH_OMAP2430
|
||||
default y if ARCH_OMAP3
|
||||
default y if ARCH_OMAP4
|
||||
default y if (BF54x && !BF544)
|
||||
default y if (BF52x && !BF522 && !BF523)
|
||||
|
||||
comment "DaVinci 35x and 644x USB support"
|
||||
depends on USB_MUSB_HDRC && ARCH_DAVINCI_DMx
|
||||
config USB_MUSB_DAVINCI
|
||||
bool "DaVinci"
|
||||
depends on ARCH_DAVINCI_DMx
|
||||
|
||||
comment "DA8xx/OMAP-L1x USB support"
|
||||
depends on USB_MUSB_HDRC && ARCH_DAVINCI_DA8XX
|
||||
config USB_MUSB_DA8XX
|
||||
bool "DA8xx/OMAP-L1x"
|
||||
depends on ARCH_DAVINCI_DA8XX
|
||||
|
||||
comment "OMAP 243x high speed USB support"
|
||||
depends on USB_MUSB_HDRC && ARCH_OMAP2430
|
||||
config USB_MUSB_TUSB6010
|
||||
bool "TUSB6010"
|
||||
depends on ARCH_OMAP
|
||||
|
||||
comment "OMAP 343x high speed USB support"
|
||||
depends on USB_MUSB_HDRC && ARCH_OMAP3
|
||||
|
||||
comment "OMAP 44xx high speed USB support"
|
||||
depends on USB_MUSB_HDRC && ARCH_OMAP4
|
||||
|
||||
comment "Blackfin high speed USB Support"
|
||||
depends on USB_MUSB_HDRC && ((BF54x && !BF544) || (BF52x && !BF522 && !BF523))
|
||||
config USB_MUSB_OMAP2PLUS
|
||||
bool "OMAP2430 and onwards"
|
||||
depends on ARCH_OMAP2PLUS
|
||||
|
||||
config USB_MUSB_AM35X
|
||||
bool
|
||||
depends on USB_MUSB_HDRC && !ARCH_OMAP2430 && !ARCH_OMAP4
|
||||
select NOP_USB_XCEIV
|
||||
default MACH_OMAP3517EVM
|
||||
help
|
||||
Select this option if your platform is based on AM35x. As
|
||||
AM35x has an updated MUSB with CPPI4.1 DMA so this config
|
||||
is introduced to differentiate musb ip between OMAP3x and
|
||||
AM35x platforms.
|
||||
bool "AM35x"
|
||||
depends on ARCH_OMAP
|
||||
|
||||
config USB_TUSB6010
|
||||
boolean "TUSB 6010 support"
|
||||
depends on USB_MUSB_HDRC && !USB_MUSB_SOC
|
||||
select NOP_USB_XCEIV
|
||||
default y
|
||||
help
|
||||
The TUSB 6010 chip, from Texas Instruments, connects a discrete
|
||||
HDRC core using a 16-bit parallel bus (NOR flash style) or VLYNQ
|
||||
(a high speed serial link). It can use system-specific external
|
||||
DMA controllers.
|
||||
config USB_MUSB_BLACKFIN
|
||||
bool "Blackfin"
|
||||
depends on (BF54x && !BF544) || (BF52x && ! BF522 && !BF523)
|
||||
|
||||
config USB_MUSB_UX500
|
||||
bool "U8500 and U5500"
|
||||
depends on (ARCH_U8500 && AB8500_USB) || (ARCH_U5500)
|
||||
|
||||
endchoice
|
||||
|
||||
choice
|
||||
prompt "Driver Mode"
|
||||
@@ -158,7 +143,7 @@ config USB_MUSB_HDRC_HCD
|
||||
config MUSB_PIO_ONLY
|
||||
bool 'Disable DMA (always use PIO)'
|
||||
depends on USB_MUSB_HDRC
|
||||
default USB_TUSB6010 || ARCH_DAVINCI_DA8XX || USB_MUSB_AM35X
|
||||
default USB_MUSB_TUSB6010 || USB_MUSB_DA8XX || USB_MUSB_AM35X
|
||||
help
|
||||
All data is copied between memory and FIFO by the CPU.
|
||||
DMA controllers are ignored.
|
||||
@@ -171,21 +156,21 @@ config MUSB_PIO_ONLY
|
||||
config USB_INVENTRA_DMA
|
||||
bool
|
||||
depends on USB_MUSB_HDRC && !MUSB_PIO_ONLY
|
||||
default ARCH_OMAP2430 || ARCH_OMAP3 || BLACKFIN || ARCH_OMAP4
|
||||
default USB_MUSB_OMAP2PLUS || USB_MUSB_BLACKFIN
|
||||
help
|
||||
Enable DMA transfers using Mentor's engine.
|
||||
|
||||
config USB_TI_CPPI_DMA
|
||||
bool
|
||||
depends on USB_MUSB_HDRC && !MUSB_PIO_ONLY
|
||||
default ARCH_DAVINCI
|
||||
default USB_MUSB_DAVINCI
|
||||
help
|
||||
Enable DMA transfers when TI CPPI DMA is available.
|
||||
|
||||
config USB_TUSB_OMAP_DMA
|
||||
bool
|
||||
depends on USB_MUSB_HDRC && !MUSB_PIO_ONLY
|
||||
depends on USB_TUSB6010
|
||||
depends on USB_MUSB_TUSB6010
|
||||
depends on ARCH_OMAP
|
||||
default y
|
||||
help
|
||||
|
||||
@@ -8,22 +8,19 @@ obj-$(CONFIG_USB_MUSB_HDRC) += musb_hdrc.o
|
||||
|
||||
musb_hdrc-y := musb_core.o
|
||||
|
||||
musb_hdrc-$(CONFIG_ARCH_DAVINCI_DMx) += davinci.o
|
||||
musb_hdrc-$(CONFIG_ARCH_DAVINCI_DA8XX) += da8xx.o
|
||||
musb_hdrc-$(CONFIG_USB_TUSB6010) += tusb6010.o
|
||||
musb_hdrc-$(CONFIG_ARCH_OMAP2430) += omap2430.o
|
||||
ifeq ($(CONFIG_USB_MUSB_AM35X),y)
|
||||
musb_hdrc-$(CONFIG_ARCH_OMAP3430) += am35x.o
|
||||
else
|
||||
musb_hdrc-$(CONFIG_ARCH_OMAP3430) += omap2430.o
|
||||
endif
|
||||
musb_hdrc-$(CONFIG_ARCH_OMAP4) += omap2430.o
|
||||
musb_hdrc-$(CONFIG_BF54x) += blackfin.o
|
||||
musb_hdrc-$(CONFIG_BF52x) += blackfin.o
|
||||
musb_hdrc-$(CONFIG_USB_GADGET_MUSB_HDRC) += musb_gadget_ep0.o musb_gadget.o
|
||||
musb_hdrc-$(CONFIG_USB_MUSB_HDRC_HCD) += musb_virthub.o musb_host.o
|
||||
musb_hdrc-$(CONFIG_DEBUG_FS) += musb_debugfs.o
|
||||
|
||||
# Hardware Glue Layer
|
||||
obj-$(CONFIG_USB_MUSB_OMAP2PLUS) += omap2430.o
|
||||
obj-$(CONFIG_USB_MUSB_AM35X) += am35x.o
|
||||
obj-$(CONFIG_USB_MUSB_TUSB6010) += tusb6010.o
|
||||
obj-$(CONFIG_USB_MUSB_DAVINCI) += davinci.o
|
||||
obj-$(CONFIG_USB_MUSB_DA8XX) += da8xx.o
|
||||
obj-$(CONFIG_USB_MUSB_BLACKFIN) += blackfin.o
|
||||
obj-$(CONFIG_USB_MUSB_UX500) += ux500.o
|
||||
|
||||
# the kconfig must guarantee that only one of the
|
||||
# possible I/O schemes will be enabled at a time ...
|
||||
# PIO only, or DMA (several potential schemes).
|
||||
|
||||
@@ -29,8 +29,9 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
|
||||
#include <plat/control.h>
|
||||
#include <plat/usb.h>
|
||||
|
||||
#include "musb_core.h"
|
||||
@@ -80,51 +81,18 @@
|
||||
|
||||
#define USB_MENTOR_CORE_OFFSET 0x400
|
||||
|
||||
static inline void phy_on(void)
|
||||
{
|
||||
unsigned long timeout = jiffies + msecs_to_jiffies(100);
|
||||
u32 devconf2;
|
||||
|
||||
/*
|
||||
* Start the on-chip PHY and its PLL.
|
||||
*/
|
||||
devconf2 = omap_ctrl_readl(AM35XX_CONTROL_DEVCONF2);
|
||||
|
||||
devconf2 &= ~(CONF2_RESET | CONF2_PHYPWRDN | CONF2_OTGPWRDN);
|
||||
devconf2 |= CONF2_PHY_PLLON;
|
||||
|
||||
omap_ctrl_writel(devconf2, AM35XX_CONTROL_DEVCONF2);
|
||||
|
||||
DBG(1, "Waiting for PHY clock good...\n");
|
||||
while (!(omap_ctrl_readl(AM35XX_CONTROL_DEVCONF2)
|
||||
& CONF2_PHYCLKGD)) {
|
||||
cpu_relax();
|
||||
|
||||
if (time_after(jiffies, timeout)) {
|
||||
DBG(1, "musb PHY clock good timed out\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void phy_off(void)
|
||||
{
|
||||
u32 devconf2;
|
||||
|
||||
/*
|
||||
* Power down the on-chip PHY.
|
||||
*/
|
||||
devconf2 = omap_ctrl_readl(AM35XX_CONTROL_DEVCONF2);
|
||||
|
||||
devconf2 &= ~CONF2_PHY_PLLON;
|
||||
devconf2 |= CONF2_PHYPWRDN | CONF2_OTGPWRDN;
|
||||
omap_ctrl_writel(devconf2, AM35XX_CONTROL_DEVCONF2);
|
||||
}
|
||||
struct am35x_glue {
|
||||
struct device *dev;
|
||||
struct platform_device *musb;
|
||||
struct clk *phy_clk;
|
||||
struct clk *clk;
|
||||
};
|
||||
#define glue_to_musb(g) platform_get_drvdata(g->musb)
|
||||
|
||||
/*
|
||||
* musb_platform_enable - enable interrupts
|
||||
* am35x_musb_enable - enable interrupts
|
||||
*/
|
||||
void musb_platform_enable(struct musb *musb)
|
||||
static void am35x_musb_enable(struct musb *musb)
|
||||
{
|
||||
void __iomem *reg_base = musb->ctrl_base;
|
||||
u32 epmask;
|
||||
@@ -143,9 +111,9 @@ void musb_platform_enable(struct musb *musb)
|
||||
}
|
||||
|
||||
/*
|
||||
* musb_platform_disable - disable HDRC and flush interrupts
|
||||
* am35x_musb_disable - disable HDRC and flush interrupts
|
||||
*/
|
||||
void musb_platform_disable(struct musb *musb)
|
||||
static void am35x_musb_disable(struct musb *musb)
|
||||
{
|
||||
void __iomem *reg_base = musb->ctrl_base;
|
||||
|
||||
@@ -162,7 +130,7 @@ void musb_platform_disable(struct musb *musb)
|
||||
#define portstate(stmt)
|
||||
#endif
|
||||
|
||||
static void am35x_set_vbus(struct musb *musb, int is_on)
|
||||
static void am35x_musb_set_vbus(struct musb *musb, int is_on)
|
||||
{
|
||||
WARN_ON(is_on && is_peripheral_active(musb));
|
||||
}
|
||||
@@ -221,7 +189,7 @@ static void otg_timer(unsigned long _musb)
|
||||
spin_unlock_irqrestore(&musb->lock, flags);
|
||||
}
|
||||
|
||||
void musb_platform_try_idle(struct musb *musb, unsigned long timeout)
|
||||
static void am35x_musb_try_idle(struct musb *musb, unsigned long timeout)
|
||||
{
|
||||
static unsigned long last_timer;
|
||||
|
||||
@@ -251,13 +219,16 @@ void musb_platform_try_idle(struct musb *musb, unsigned long timeout)
|
||||
mod_timer(&otg_workaround, timeout);
|
||||
}
|
||||
|
||||
static irqreturn_t am35x_interrupt(int irq, void *hci)
|
||||
static irqreturn_t am35x_musb_interrupt(int irq, void *hci)
|
||||
{
|
||||
struct musb *musb = hci;
|
||||
void __iomem *reg_base = musb->ctrl_base;
|
||||
struct device *dev = musb->controller;
|
||||
struct musb_hdrc_platform_data *plat = dev->platform_data;
|
||||
struct omap_musb_board_data *data = plat->board_data;
|
||||
unsigned long flags;
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
u32 epintr, usbintr, lvl_intr;
|
||||
u32 epintr, usbintr;
|
||||
|
||||
spin_lock_irqsave(&musb->lock, flags);
|
||||
|
||||
@@ -346,9 +317,8 @@ eoi:
|
||||
/* EOI needs to be written for the IRQ to be re-asserted. */
|
||||
if (ret == IRQ_HANDLED || epintr || usbintr) {
|
||||
/* clear level interrupt */
|
||||
lvl_intr = omap_ctrl_readl(AM35XX_CONTROL_LVL_INTR_CLEAR);
|
||||
lvl_intr |= AM35XX_USBOTGSS_INT_CLR;
|
||||
omap_ctrl_writel(lvl_intr, AM35XX_CONTROL_LVL_INTR_CLEAR);
|
||||
if (data->clear_irq)
|
||||
data->clear_irq();
|
||||
/* write EOI */
|
||||
musb_writel(reg_base, USB_END_OF_INTR_REG, 0);
|
||||
}
|
||||
@@ -362,137 +332,85 @@ eoi:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int musb_platform_set_mode(struct musb *musb, u8 musb_mode)
|
||||
static int am35x_musb_set_mode(struct musb *musb, u8 musb_mode)
|
||||
{
|
||||
u32 devconf2 = omap_ctrl_readl(AM35XX_CONTROL_DEVCONF2);
|
||||
struct device *dev = musb->controller;
|
||||
struct musb_hdrc_platform_data *plat = dev->platform_data;
|
||||
struct omap_musb_board_data *data = plat->board_data;
|
||||
int retval = 0;
|
||||
|
||||
devconf2 &= ~CONF2_OTGMODE;
|
||||
switch (musb_mode) {
|
||||
#ifdef CONFIG_USB_MUSB_HDRC_HCD
|
||||
case MUSB_HOST: /* Force VBUS valid, ID = 0 */
|
||||
devconf2 |= CONF2_FORCE_HOST;
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_USB_GADGET_MUSB_HDRC
|
||||
case MUSB_PERIPHERAL: /* Force VBUS valid, ID = 1 */
|
||||
devconf2 |= CONF2_FORCE_DEVICE;
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_USB_MUSB_OTG
|
||||
case MUSB_OTG: /* Don't override the VBUS/ID comparators */
|
||||
devconf2 |= CONF2_NO_OVERRIDE;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
DBG(2, "Trying to set unsupported mode %u\n", musb_mode);
|
||||
}
|
||||
if (data->set_mode)
|
||||
data->set_mode(musb_mode);
|
||||
else
|
||||
retval = -EIO;
|
||||
|
||||
omap_ctrl_writel(devconf2, AM35XX_CONTROL_DEVCONF2);
|
||||
return 0;
|
||||
return retval;
|
||||
}
|
||||
|
||||
int __init musb_platform_init(struct musb *musb, void *board_data)
|
||||
static int am35x_musb_init(struct musb *musb)
|
||||
{
|
||||
struct device *dev = musb->controller;
|
||||
struct musb_hdrc_platform_data *plat = dev->platform_data;
|
||||
struct omap_musb_board_data *data = plat->board_data;
|
||||
void __iomem *reg_base = musb->ctrl_base;
|
||||
u32 rev, lvl_intr, sw_reset;
|
||||
int status;
|
||||
u32 rev;
|
||||
|
||||
musb->mregs += USB_MENTOR_CORE_OFFSET;
|
||||
|
||||
clk_enable(musb->clock);
|
||||
DBG(2, "musb->clock=%lud\n", clk_get_rate(musb->clock));
|
||||
|
||||
musb->phy_clock = clk_get(musb->controller, "fck");
|
||||
if (IS_ERR(musb->phy_clock)) {
|
||||
status = PTR_ERR(musb->phy_clock);
|
||||
goto exit0;
|
||||
}
|
||||
clk_enable(musb->phy_clock);
|
||||
DBG(2, "musb->phy_clock=%lud\n", clk_get_rate(musb->phy_clock));
|
||||
|
||||
/* Returns zero if e.g. not clocked */
|
||||
rev = musb_readl(reg_base, USB_REVISION_REG);
|
||||
if (!rev) {
|
||||
status = -ENODEV;
|
||||
goto exit1;
|
||||
}
|
||||
if (!rev)
|
||||
return -ENODEV;
|
||||
|
||||
usb_nop_xceiv_register();
|
||||
musb->xceiv = otg_get_transceiver();
|
||||
if (!musb->xceiv) {
|
||||
status = -ENODEV;
|
||||
goto exit1;
|
||||
}
|
||||
if (!musb->xceiv)
|
||||
return -ENODEV;
|
||||
|
||||
if (is_host_enabled(musb))
|
||||
setup_timer(&otg_workaround, otg_timer, (unsigned long) musb);
|
||||
|
||||
musb->board_set_vbus = am35x_set_vbus;
|
||||
|
||||
/* Global reset */
|
||||
sw_reset = omap_ctrl_readl(AM35XX_CONTROL_IP_SW_RESET);
|
||||
|
||||
sw_reset |= AM35XX_USBOTGSS_SW_RST;
|
||||
omap_ctrl_writel(sw_reset, AM35XX_CONTROL_IP_SW_RESET);
|
||||
|
||||
sw_reset &= ~AM35XX_USBOTGSS_SW_RST;
|
||||
omap_ctrl_writel(sw_reset, AM35XX_CONTROL_IP_SW_RESET);
|
||||
/* Reset the musb */
|
||||
if (data->reset)
|
||||
data->reset();
|
||||
|
||||
/* Reset the controller */
|
||||
musb_writel(reg_base, USB_CTRL_REG, AM35X_SOFT_RESET_MASK);
|
||||
|
||||
/* Start the on-chip PHY and its PLL. */
|
||||
phy_on();
|
||||
if (data->set_phy_power)
|
||||
data->set_phy_power(1);
|
||||
|
||||
msleep(5);
|
||||
|
||||
musb->isr = am35x_interrupt;
|
||||
musb->isr = am35x_musb_interrupt;
|
||||
|
||||
/* clear level interrupt */
|
||||
lvl_intr = omap_ctrl_readl(AM35XX_CONTROL_LVL_INTR_CLEAR);
|
||||
lvl_intr |= AM35XX_USBOTGSS_INT_CLR;
|
||||
omap_ctrl_writel(lvl_intr, AM35XX_CONTROL_LVL_INTR_CLEAR);
|
||||
if (data->clear_irq)
|
||||
data->clear_irq();
|
||||
|
||||
return 0;
|
||||
exit1:
|
||||
clk_disable(musb->phy_clock);
|
||||
clk_put(musb->phy_clock);
|
||||
exit0:
|
||||
clk_disable(musb->clock);
|
||||
return status;
|
||||
}
|
||||
|
||||
int musb_platform_exit(struct musb *musb)
|
||||
static int am35x_musb_exit(struct musb *musb)
|
||||
{
|
||||
struct device *dev = musb->controller;
|
||||
struct musb_hdrc_platform_data *plat = dev->platform_data;
|
||||
struct omap_musb_board_data *data = plat->board_data;
|
||||
|
||||
if (is_host_enabled(musb))
|
||||
del_timer_sync(&otg_workaround);
|
||||
|
||||
phy_off();
|
||||
/* Shutdown the on-chip PHY and its PLL. */
|
||||
if (data->set_phy_power)
|
||||
data->set_phy_power(0);
|
||||
|
||||
otg_put_transceiver(musb->xceiv);
|
||||
usb_nop_xceiv_unregister();
|
||||
|
||||
clk_disable(musb->clock);
|
||||
|
||||
clk_disable(musb->phy_clock);
|
||||
clk_put(musb->phy_clock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
void musb_platform_save_context(struct musb *musb,
|
||||
struct musb_context_registers *musb_context)
|
||||
{
|
||||
phy_off();
|
||||
}
|
||||
|
||||
void musb_platform_restore_context(struct musb *musb,
|
||||
struct musb_context_registers *musb_context)
|
||||
{
|
||||
phy_on();
|
||||
}
|
||||
#endif
|
||||
|
||||
/* AM35x supports only 32bit read operation */
|
||||
void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst)
|
||||
{
|
||||
@@ -522,3 +440,215 @@ void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst)
|
||||
memcpy(dst, &val, len);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct musb_platform_ops am35x_ops = {
|
||||
.init = am35x_musb_init,
|
||||
.exit = am35x_musb_exit,
|
||||
|
||||
.enable = am35x_musb_enable,
|
||||
.disable = am35x_musb_disable,
|
||||
|
||||
.set_mode = am35x_musb_set_mode,
|
||||
.try_idle = am35x_musb_try_idle,
|
||||
|
||||
.set_vbus = am35x_musb_set_vbus,
|
||||
};
|
||||
|
||||
static u64 am35x_dmamask = DMA_BIT_MASK(32);
|
||||
|
||||
static int __init am35x_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct musb_hdrc_platform_data *pdata = pdev->dev.platform_data;
|
||||
struct platform_device *musb;
|
||||
struct am35x_glue *glue;
|
||||
|
||||
struct clk *phy_clk;
|
||||
struct clk *clk;
|
||||
|
||||
int ret = -ENOMEM;
|
||||
|
||||
glue = kzalloc(sizeof(*glue), GFP_KERNEL);
|
||||
if (!glue) {
|
||||
dev_err(&pdev->dev, "failed to allocate glue context\n");
|
||||
goto err0;
|
||||
}
|
||||
|
||||
musb = platform_device_alloc("musb-hdrc", -1);
|
||||
if (!musb) {
|
||||
dev_err(&pdev->dev, "failed to allocate musb device\n");
|
||||
goto err1;
|
||||
}
|
||||
|
||||
phy_clk = clk_get(&pdev->dev, "fck");
|
||||
if (IS_ERR(phy_clk)) {
|
||||
dev_err(&pdev->dev, "failed to get PHY clock\n");
|
||||
ret = PTR_ERR(phy_clk);
|
||||
goto err2;
|
||||
}
|
||||
|
||||
clk = clk_get(&pdev->dev, "ick");
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(&pdev->dev, "failed to get clock\n");
|
||||
ret = PTR_ERR(clk);
|
||||
goto err3;
|
||||
}
|
||||
|
||||
ret = clk_enable(phy_clk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to enable PHY clock\n");
|
||||
goto err4;
|
||||
}
|
||||
|
||||
ret = clk_enable(clk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to enable clock\n");
|
||||
goto err5;
|
||||
}
|
||||
|
||||
musb->dev.parent = &pdev->dev;
|
||||
musb->dev.dma_mask = &am35x_dmamask;
|
||||
musb->dev.coherent_dma_mask = am35x_dmamask;
|
||||
|
||||
glue->dev = &pdev->dev;
|
||||
glue->musb = musb;
|
||||
glue->phy_clk = phy_clk;
|
||||
glue->clk = clk;
|
||||
|
||||
pdata->platform_ops = &am35x_ops;
|
||||
|
||||
platform_set_drvdata(pdev, glue);
|
||||
|
||||
ret = platform_device_add_resources(musb, pdev->resource,
|
||||
pdev->num_resources);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to add resources\n");
|
||||
goto err6;
|
||||
}
|
||||
|
||||
ret = platform_device_add_data(musb, pdata, sizeof(*pdata));
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to add platform_data\n");
|
||||
goto err6;
|
||||
}
|
||||
|
||||
ret = platform_device_add(musb);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to register musb device\n");
|
||||
goto err6;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err6:
|
||||
clk_disable(clk);
|
||||
|
||||
err5:
|
||||
clk_disable(phy_clk);
|
||||
|
||||
err4:
|
||||
clk_put(clk);
|
||||
|
||||
err3:
|
||||
clk_put(phy_clk);
|
||||
|
||||
err2:
|
||||
platform_device_put(musb);
|
||||
|
||||
err1:
|
||||
kfree(glue);
|
||||
|
||||
err0:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __exit am35x_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct am35x_glue *glue = platform_get_drvdata(pdev);
|
||||
|
||||
platform_device_del(glue->musb);
|
||||
platform_device_put(glue->musb);
|
||||
clk_disable(glue->clk);
|
||||
clk_disable(glue->phy_clk);
|
||||
clk_put(glue->clk);
|
||||
clk_put(glue->phy_clk);
|
||||
kfree(glue);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int am35x_suspend(struct device *dev)
|
||||
{
|
||||
struct am35x_glue *glue = dev_get_drvdata(dev);
|
||||
struct musb_hdrc_platform_data *plat = dev->platform_data;
|
||||
struct omap_musb_board_data *data = plat->board_data;
|
||||
|
||||
/* Shutdown the on-chip PHY and its PLL. */
|
||||
if (data->set_phy_power)
|
||||
data->set_phy_power(0);
|
||||
|
||||
clk_disable(glue->phy_clk);
|
||||
clk_disable(glue->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int am35x_resume(struct device *dev)
|
||||
{
|
||||
struct am35x_glue *glue = dev_get_drvdata(dev);
|
||||
struct musb_hdrc_platform_data *plat = dev->platform_data;
|
||||
struct omap_musb_board_data *data = plat->board_data;
|
||||
int ret;
|
||||
|
||||
/* Start the on-chip PHY and its PLL. */
|
||||
if (data->set_phy_power)
|
||||
data->set_phy_power(1);
|
||||
|
||||
ret = clk_enable(glue->phy_clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to enable PHY clock\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_enable(glue->clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to enable clock\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct dev_pm_ops am35x_pm_ops = {
|
||||
.suspend = am35x_suspend,
|
||||
.resume = am35x_resume,
|
||||
};
|
||||
|
||||
#define DEV_PM_OPS &am35x_pm_ops
|
||||
#else
|
||||
#define DEV_PM_OPS NULL
|
||||
#endif
|
||||
|
||||
static struct platform_driver am35x_driver = {
|
||||
.remove = __exit_p(am35x_remove),
|
||||
.driver = {
|
||||
.name = "musb-am35x",
|
||||
.pm = DEV_PM_OPS,
|
||||
},
|
||||
};
|
||||
|
||||
MODULE_DESCRIPTION("AM35x MUSB Glue Layer");
|
||||
MODULE_AUTHOR("Ajay Kumar Gupta <ajay.gupta@ti.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
||||
static int __init am35x_init(void)
|
||||
{
|
||||
return platform_driver_probe(&am35x_driver, am35x_probe);
|
||||
}
|
||||
subsys_initcall(am35x_init);
|
||||
|
||||
static void __exit am35x_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&am35x_driver);
|
||||
}
|
||||
module_exit(am35x_exit);
|
||||
|
||||
@@ -15,12 +15,20 @@
|
||||
#include <linux/list.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
|
||||
#include <asm/cacheflush.h>
|
||||
|
||||
#include "musb_core.h"
|
||||
#include "blackfin.h"
|
||||
|
||||
struct bfin_glue {
|
||||
struct device *dev;
|
||||
struct platform_device *musb;
|
||||
};
|
||||
#define glue_to_musb(g) platform_get_drvdata(g->musb)
|
||||
|
||||
/*
|
||||
* Load an endpoint's FIFO
|
||||
*/
|
||||
@@ -278,7 +286,7 @@ static void musb_conn_timer_handler(unsigned long _musb)
|
||||
DBG(4, "state is %s\n", otg_state_string(musb));
|
||||
}
|
||||
|
||||
void musb_platform_enable(struct musb *musb)
|
||||
static void bfin_musb_enable(struct musb *musb)
|
||||
{
|
||||
if (!is_otg_enabled(musb) && is_host_enabled(musb)) {
|
||||
mod_timer(&musb_conn_timer, jiffies + TIMER_DELAY);
|
||||
@@ -286,11 +294,11 @@ void musb_platform_enable(struct musb *musb)
|
||||
}
|
||||
}
|
||||
|
||||
void musb_platform_disable(struct musb *musb)
|
||||
static void bfin_musb_disable(struct musb *musb)
|
||||
{
|
||||
}
|
||||
|
||||
static void bfin_set_vbus(struct musb *musb, int is_on)
|
||||
static void bfin_musb_set_vbus(struct musb *musb, int is_on)
|
||||
{
|
||||
int value = musb->config->gpio_vrsel_active;
|
||||
if (!is_on)
|
||||
@@ -303,51 +311,29 @@ static void bfin_set_vbus(struct musb *musb, int is_on)
|
||||
musb_readb(musb->mregs, MUSB_DEVCTL));
|
||||
}
|
||||
|
||||
static int bfin_set_power(struct otg_transceiver *x, unsigned mA)
|
||||
static int bfin_musb_set_power(struct otg_transceiver *x, unsigned mA)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void musb_platform_try_idle(struct musb *musb, unsigned long timeout)
|
||||
static void bfin_musb_try_idle(struct musb *musb, unsigned long timeout)
|
||||
{
|
||||
if (!is_otg_enabled(musb) && is_host_enabled(musb))
|
||||
mod_timer(&musb_conn_timer, jiffies + TIMER_DELAY);
|
||||
}
|
||||
|
||||
int musb_platform_get_vbus_status(struct musb *musb)
|
||||
static int bfin_musb_get_vbus_status(struct musb *musb)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int musb_platform_set_mode(struct musb *musb, u8 musb_mode)
|
||||
static int bfin_musb_set_mode(struct musb *musb, u8 musb_mode)
|
||||
{
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
int __init musb_platform_init(struct musb *musb, void *board_data)
|
||||
static void bfin_musb_reg_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);
|
||||
|
||||
usb_nop_xceiv_register();
|
||||
musb->xceiv = otg_get_transceiver();
|
||||
if (!musb->xceiv) {
|
||||
gpio_free(musb->config->gpio_vrsel);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (ANOMALY_05000346) {
|
||||
bfin_write_USB_APHY_CALIB(ANOMALY_05000346_value);
|
||||
SSYNC();
|
||||
@@ -382,21 +368,47 @@ int __init musb_platform_init(struct musb *musb, void *board_data)
|
||||
EP2_RX_ENA | EP3_RX_ENA | EP4_RX_ENA |
|
||||
EP5_RX_ENA | EP6_RX_ENA | EP7_RX_ENA);
|
||||
SSYNC();
|
||||
}
|
||||
|
||||
static int bfin_musb_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);
|
||||
|
||||
usb_nop_xceiv_register();
|
||||
musb->xceiv = otg_get_transceiver();
|
||||
if (!musb->xceiv) {
|
||||
gpio_free(musb->config->gpio_vrsel);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
bfin_musb_reg_init(musb);
|
||||
|
||||
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->xceiv->set_power = bfin_musb_set_power;
|
||||
|
||||
musb->isr = blackfin_interrupt;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int musb_platform_exit(struct musb *musb)
|
||||
static int bfin_musb_exit(struct musb *musb)
|
||||
{
|
||||
gpio_free(musb->config->gpio_vrsel);
|
||||
|
||||
@@ -404,3 +416,154 @@ int musb_platform_exit(struct musb *musb)
|
||||
usb_nop_xceiv_unregister();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct musb_platform_ops bfin_ops = {
|
||||
.init = bfin_musb_init,
|
||||
.exit = bfin_musb_exit,
|
||||
|
||||
.enable = bfin_musb_enable,
|
||||
.disable = bfin_musb_disable,
|
||||
|
||||
.set_mode = bfin_musb_set_mode,
|
||||
.try_idle = bfin_musb_try_idle,
|
||||
|
||||
.vbus_status = bfin_musb_vbus_status,
|
||||
.set_vbus = bfin_musb_set_vbus,
|
||||
};
|
||||
|
||||
static u64 bfin_dmamask = DMA_BIT_MASK(32);
|
||||
|
||||
static int __init bfin_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct musb_hdrc_platform_data *pdata = pdev->dev.platform_data;
|
||||
struct platform_device *musb;
|
||||
struct bfin_glue *glue;
|
||||
|
||||
int ret = -ENOMEM;
|
||||
|
||||
glue = kzalloc(sizeof(*glue), GFP_KERNEL);
|
||||
if (!glue) {
|
||||
dev_err(&pdev->dev, "failed to allocate glue context\n");
|
||||
goto err0;
|
||||
}
|
||||
|
||||
musb = platform_device_alloc("musb-hdrc", -1);
|
||||
if (!musb) {
|
||||
dev_err(&pdev->dev, "failed to allocate musb device\n");
|
||||
goto err1;
|
||||
}
|
||||
|
||||
musb->dev.parent = &pdev->dev;
|
||||
musb->dev.dma_mask = &bfin_dmamask;
|
||||
musb->dev.coherent_dma_mask = bfin_dmamask;
|
||||
|
||||
glue->dev = &pdev->dev;
|
||||
glue->musb = musb;
|
||||
|
||||
pdata->platform_ops = &bfin_ops;
|
||||
|
||||
platform_set_drvdata(pdev, glue);
|
||||
|
||||
ret = platform_device_add_resources(musb, pdev->resource,
|
||||
pdev->num_resources);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to add resources\n");
|
||||
goto err2;
|
||||
}
|
||||
|
||||
ret = platform_device_add_data(musb, pdata, sizeof(*pdata));
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to add platform_data\n");
|
||||
goto err2;
|
||||
}
|
||||
|
||||
ret = platform_device_add(musb);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to register musb device\n");
|
||||
goto err2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err2:
|
||||
platform_device_put(musb);
|
||||
|
||||
err1:
|
||||
kfree(glue);
|
||||
|
||||
err0:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __exit bfin_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct bfin_glue *glue = platform_get_drvdata(pdev);
|
||||
|
||||
platform_device_del(glue->musb);
|
||||
platform_device_put(glue->musb);
|
||||
kfree(glue);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int bfin_suspend(struct device *dev)
|
||||
{
|
||||
struct bfin_glue *glue = dev_get_drvdata(dev);
|
||||
struct musb *musb = glue_to_musb(glue);
|
||||
|
||||
if (is_host_active(musb))
|
||||
/*
|
||||
* During hibernate gpio_vrsel will change from high to low
|
||||
* low which will generate wakeup event resume the system
|
||||
* immediately. Set it to 0 before hibernate to avoid this
|
||||
* wakeup event.
|
||||
*/
|
||||
gpio_set_value(musb->config->gpio_vrsel, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bfin_resume(struct device *dev)
|
||||
{
|
||||
struct bfin_glue *glue = dev_get_drvdata(dev);
|
||||
struct musb *musb = glue_to_musb(glue);
|
||||
|
||||
bfin_musb_reg_init(musb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct dev_pm_ops bfin_pm_ops = {
|
||||
.suspend = bfin_suspend,
|
||||
.resume = bfin_resume,
|
||||
};
|
||||
|
||||
#define DEV_PM_OPS &bfin_pm_op,
|
||||
#else
|
||||
#define DEV_PM_OPS NULL
|
||||
#endif
|
||||
|
||||
static struct platform_driver bfin_driver = {
|
||||
.remove = __exit_p(bfin_remove),
|
||||
.driver = {
|
||||
.name = "musb-bfin",
|
||||
.pm = DEV_PM_OPS,
|
||||
},
|
||||
};
|
||||
|
||||
MODULE_DESCRIPTION("Blackfin MUSB Glue Layer");
|
||||
MODULE_AUTHOR("Bryan Wy <cooloney@kernel.org>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
||||
static int __init bfin_init(void)
|
||||
{
|
||||
return platform_driver_probe(&bfin_driver, bfin_probe);
|
||||
}
|
||||
subsys_initcall(bfin_init);
|
||||
|
||||
static void __exit bfin_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&bfin_driver);
|
||||
}
|
||||
module_exit(bfin_exit);
|
||||
|
||||
@@ -1308,7 +1308,7 @@ dma_controller_create(struct musb *musb, void __iomem *mregs)
|
||||
struct cppi *controller;
|
||||
struct device *dev = musb->controller;
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
int irq = platform_get_irq(pdev, 1);
|
||||
int irq = platform_get_irq_byname(pdev, "dma");
|
||||
|
||||
controller = kzalloc(sizeof *controller, GFP_KERNEL);
|
||||
if (!controller)
|
||||
|
||||
@@ -29,6 +29,8 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
|
||||
#include <mach/da8xx.h>
|
||||
#include <mach/usb.h>
|
||||
@@ -78,6 +80,12 @@
|
||||
|
||||
#define CFGCHIP2 IO_ADDRESS(DA8XX_SYSCFG0_BASE + DA8XX_CFGCHIP2_REG)
|
||||
|
||||
struct da8xx_glue {
|
||||
struct device *dev;
|
||||
struct platform_device *musb;
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
/*
|
||||
* REVISIT (PM): we should be able to keep the PHY in low power mode most
|
||||
* of the time (24 MHz oscillator and PLL off, etc.) by setting POWER.D0
|
||||
@@ -131,9 +139,9 @@ static inline void phy_off(void)
|
||||
*/
|
||||
|
||||
/**
|
||||
* musb_platform_enable - enable interrupts
|
||||
* da8xx_musb_enable - enable interrupts
|
||||
*/
|
||||
void musb_platform_enable(struct musb *musb)
|
||||
static void da8xx_musb_enable(struct musb *musb)
|
||||
{
|
||||
void __iomem *reg_base = musb->ctrl_base;
|
||||
u32 mask;
|
||||
@@ -151,9 +159,9 @@ void musb_platform_enable(struct musb *musb)
|
||||
}
|
||||
|
||||
/**
|
||||
* musb_platform_disable - disable HDRC and flush interrupts
|
||||
* da8xx_musb_disable - disable HDRC and flush interrupts
|
||||
*/
|
||||
void musb_platform_disable(struct musb *musb)
|
||||
static void da8xx_musb_disable(struct musb *musb)
|
||||
{
|
||||
void __iomem *reg_base = musb->ctrl_base;
|
||||
|
||||
@@ -170,7 +178,7 @@ void musb_platform_disable(struct musb *musb)
|
||||
#define portstate(stmt)
|
||||
#endif
|
||||
|
||||
static void da8xx_set_vbus(struct musb *musb, int is_on)
|
||||
static void da8xx_musb_set_vbus(struct musb *musb, int is_on)
|
||||
{
|
||||
WARN_ON(is_on && is_peripheral_active(musb));
|
||||
}
|
||||
@@ -252,7 +260,7 @@ static void otg_timer(unsigned long _musb)
|
||||
spin_unlock_irqrestore(&musb->lock, flags);
|
||||
}
|
||||
|
||||
void musb_platform_try_idle(struct musb *musb, unsigned long timeout)
|
||||
static void da8xx_musb_try_idle(struct musb *musb, unsigned long timeout)
|
||||
{
|
||||
static unsigned long last_timer;
|
||||
|
||||
@@ -282,7 +290,7 @@ void musb_platform_try_idle(struct musb *musb, unsigned long timeout)
|
||||
mod_timer(&otg_workaround, timeout);
|
||||
}
|
||||
|
||||
static irqreturn_t da8xx_interrupt(int irq, void *hci)
|
||||
static irqreturn_t da8xx_musb_interrupt(int irq, void *hci)
|
||||
{
|
||||
struct musb *musb = hci;
|
||||
void __iomem *reg_base = musb->ctrl_base;
|
||||
@@ -380,7 +388,7 @@ static irqreturn_t da8xx_interrupt(int irq, void *hci)
|
||||
return ret;
|
||||
}
|
||||
|
||||
int musb_platform_set_mode(struct musb *musb, u8 musb_mode)
|
||||
static int da8xx_musb_set_mode(struct musb *musb, u8 musb_mode)
|
||||
{
|
||||
u32 cfgchip2 = __raw_readl(CFGCHIP2);
|
||||
|
||||
@@ -409,15 +417,13 @@ int musb_platform_set_mode(struct musb *musb, u8 musb_mode)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __init musb_platform_init(struct musb *musb, void *board_data)
|
||||
static int da8xx_musb_init(struct musb *musb)
|
||||
{
|
||||
void __iomem *reg_base = musb->ctrl_base;
|
||||
u32 rev;
|
||||
|
||||
musb->mregs += DA8XX_MENTOR_CORE_OFFSET;
|
||||
|
||||
clk_enable(musb->clock);
|
||||
|
||||
/* Returns zero if e.g. not clocked */
|
||||
rev = musb_readl(reg_base, DA8XX_USB_REVISION_REG);
|
||||
if (!rev)
|
||||
@@ -431,8 +437,6 @@ int __init musb_platform_init(struct musb *musb, void *board_data)
|
||||
if (is_host_enabled(musb))
|
||||
setup_timer(&otg_workaround, otg_timer, (unsigned long)musb);
|
||||
|
||||
musb->board_set_vbus = da8xx_set_vbus;
|
||||
|
||||
/* Reset the controller */
|
||||
musb_writel(reg_base, DA8XX_USB_CTRL_REG, DA8XX_SOFT_RESET_MASK);
|
||||
|
||||
@@ -446,14 +450,13 @@ int __init musb_platform_init(struct musb *musb, void *board_data)
|
||||
rev, __raw_readl(CFGCHIP2),
|
||||
musb_readb(reg_base, DA8XX_USB_CTRL_REG));
|
||||
|
||||
musb->isr = da8xx_interrupt;
|
||||
musb->isr = da8xx_musb_interrupt;
|
||||
return 0;
|
||||
fail:
|
||||
clk_disable(musb->clock);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
int musb_platform_exit(struct musb *musb)
|
||||
static int da8xx_musb_exit(struct musb *musb)
|
||||
{
|
||||
if (is_host_enabled(musb))
|
||||
del_timer_sync(&otg_workaround);
|
||||
@@ -463,7 +466,140 @@ int musb_platform_exit(struct musb *musb)
|
||||
otg_put_transceiver(musb->xceiv);
|
||||
usb_nop_xceiv_unregister();
|
||||
|
||||
clk_disable(musb->clock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct musb_platform_ops da8xx_ops = {
|
||||
.init = da8xx_musb_init,
|
||||
.exit = da8xx_musb_exit,
|
||||
|
||||
.enable = da8xx_musb_enable,
|
||||
.disable = da8xx_musb_disable,
|
||||
|
||||
.set_mode = da8xx_musb_set_mode,
|
||||
.try_idle = da8xx_musb_try_idle,
|
||||
|
||||
.set_vbus = da8xx_musb_set_vbus,
|
||||
};
|
||||
|
||||
static u64 da8xx_dmamask = DMA_BIT_MASK(32);
|
||||
|
||||
static int __init da8xx_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct musb_hdrc_platform_data *pdata = pdev->dev.platform_data;
|
||||
struct platform_device *musb;
|
||||
struct da8xx_glue *glue;
|
||||
|
||||
struct clk *clk;
|
||||
|
||||
int ret = -ENOMEM;
|
||||
|
||||
glue = kzalloc(sizeof(*glue), GFP_KERNEL);
|
||||
if (!glue) {
|
||||
dev_err(&pdev->dev, "failed to allocate glue context\n");
|
||||
goto err0;
|
||||
}
|
||||
|
||||
musb = platform_device_alloc("musb-hdrc", -1);
|
||||
if (!musb) {
|
||||
dev_err(&pdev->dev, "failed to allocate musb device\n");
|
||||
goto err1;
|
||||
}
|
||||
|
||||
clk = clk_get(&pdev->dev, "usb20");
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(&pdev->dev, "failed to get clock\n");
|
||||
ret = PTR_ERR(clk);
|
||||
goto err2;
|
||||
}
|
||||
|
||||
ret = clk_enable(clk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to enable clock\n");
|
||||
goto err3;
|
||||
}
|
||||
|
||||
musb->dev.parent = &pdev->dev;
|
||||
musb->dev.dma_mask = &da8xx_dmamask;
|
||||
musb->dev.coherent_dma_mask = da8xx_dmamask;
|
||||
|
||||
glue->dev = &pdev->dev;
|
||||
glue->musb = musb;
|
||||
glue->clk = clk;
|
||||
|
||||
pdata->platform_ops = &da8xx_ops;
|
||||
|
||||
platform_set_drvdata(pdev, glue);
|
||||
|
||||
ret = platform_device_add_resources(musb, pdev->resource,
|
||||
pdev->num_resources);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to add resources\n");
|
||||
goto err4;
|
||||
}
|
||||
|
||||
ret = platform_device_add_data(musb, pdata, sizeof(*pdata));
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to add platform_data\n");
|
||||
goto err4;
|
||||
}
|
||||
|
||||
ret = platform_device_add(musb);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to register musb device\n");
|
||||
goto err4;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err4:
|
||||
clk_disable(clk);
|
||||
|
||||
err3:
|
||||
clk_put(clk);
|
||||
|
||||
err2:
|
||||
platform_device_put(musb);
|
||||
|
||||
err1:
|
||||
kfree(glue);
|
||||
|
||||
err0:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __exit da8xx_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct da8xx_glue *glue = platform_get_drvdata(pdev);
|
||||
|
||||
platform_device_del(glue->musb);
|
||||
platform_device_put(glue->musb);
|
||||
clk_disable(glue->clk);
|
||||
clk_put(glue->clk);
|
||||
kfree(glue);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver da8xx_driver = {
|
||||
.remove = __exit_p(da8xx_remove),
|
||||
.driver = {
|
||||
.name = "musb-da8xx",
|
||||
},
|
||||
};
|
||||
|
||||
MODULE_DESCRIPTION("DA8xx/OMAP-L1x MUSB Glue Layer");
|
||||
MODULE_AUTHOR("Sergei Shtylyov <sshtylyov@ru.mvista.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
||||
static int __init da8xx_init(void)
|
||||
{
|
||||
return platform_driver_probe(&da8xx_driver, da8xx_probe);
|
||||
}
|
||||
subsys_initcall(da8xx_init);
|
||||
|
||||
static void __exit da8xx_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&da8xx_driver);
|
||||
}
|
||||
module_exit(da8xx_exit);
|
||||
|
||||
@@ -30,6 +30,8 @@
|
||||
#include <linux/clk.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
|
||||
#include <mach/hardware.h>
|
||||
#include <mach/memory.h>
|
||||
@@ -51,6 +53,12 @@
|
||||
#define USB_PHY_CTRL IO_ADDRESS(USBPHY_CTL_PADDR)
|
||||
#define DM355_DEEPSLEEP IO_ADDRESS(DM355_DEEPSLEEP_PADDR)
|
||||
|
||||
struct davinci_glue {
|
||||
struct device *dev;
|
||||
struct platform_device *musb;
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
/* REVISIT (PM) we should be able to keep the PHY in low power mode most
|
||||
* of the time (24 MHZ oscillator and PLL off, etc) by setting POWER.D0
|
||||
* and, when in host mode, autosuspending idle root ports... PHYPLLON
|
||||
@@ -83,7 +91,7 @@ static inline void phy_off(void)
|
||||
|
||||
static int dma_off = 1;
|
||||
|
||||
void musb_platform_enable(struct musb *musb)
|
||||
static void davinci_musb_enable(struct musb *musb)
|
||||
{
|
||||
u32 tmp, old, val;
|
||||
|
||||
@@ -116,7 +124,7 @@ void musb_platform_enable(struct musb *musb)
|
||||
/*
|
||||
* Disable the HDRC and flush interrupts
|
||||
*/
|
||||
void musb_platform_disable(struct musb *musb)
|
||||
static void davinci_musb_disable(struct musb *musb)
|
||||
{
|
||||
/* because we don't set CTRLR.UINT, "important" to:
|
||||
* - not read/write INTRUSB/INTRUSBE
|
||||
@@ -167,7 +175,7 @@ static void evm_deferred_drvvbus(struct work_struct *ignored)
|
||||
|
||||
#endif /* EVM */
|
||||
|
||||
static void davinci_source_power(struct musb *musb, int is_on, int immediate)
|
||||
static void davinci_musb_source_power(struct musb *musb, int is_on, int immediate)
|
||||
{
|
||||
#ifdef CONFIG_MACH_DAVINCI_EVM
|
||||
if (is_on)
|
||||
@@ -190,10 +198,10 @@ static void davinci_source_power(struct musb *musb, int is_on, int immediate)
|
||||
#endif
|
||||
}
|
||||
|
||||
static void davinci_set_vbus(struct musb *musb, int is_on)
|
||||
static void davinci_musb_set_vbus(struct musb *musb, int is_on)
|
||||
{
|
||||
WARN_ON(is_on && is_peripheral_active(musb));
|
||||
davinci_source_power(musb, is_on, 0);
|
||||
davinci_musb_source_power(musb, is_on, 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -259,7 +267,7 @@ static void otg_timer(unsigned long _musb)
|
||||
spin_unlock_irqrestore(&musb->lock, flags);
|
||||
}
|
||||
|
||||
static irqreturn_t davinci_interrupt(int irq, void *__hci)
|
||||
static irqreturn_t davinci_musb_interrupt(int irq, void *__hci)
|
||||
{
|
||||
unsigned long flags;
|
||||
irqreturn_t retval = IRQ_NONE;
|
||||
@@ -345,7 +353,7 @@ static irqreturn_t davinci_interrupt(int irq, void *__hci)
|
||||
/* NOTE: this must complete poweron within 100 msec
|
||||
* (OTG_TIME_A_WAIT_VRISE) but we don't check for that.
|
||||
*/
|
||||
davinci_source_power(musb, drvvbus, 0);
|
||||
davinci_musb_source_power(musb, drvvbus, 0);
|
||||
DBG(2, "VBUS %s (%s)%s, devctl %02x\n",
|
||||
drvvbus ? "on" : "off",
|
||||
otg_state_string(musb),
|
||||
@@ -370,13 +378,13 @@ static irqreturn_t davinci_interrupt(int irq, void *__hci)
|
||||
return retval;
|
||||
}
|
||||
|
||||
int musb_platform_set_mode(struct musb *musb, u8 mode)
|
||||
static int davinci_musb_set_mode(struct musb *musb, u8 mode)
|
||||
{
|
||||
/* EVM can't do this (right?) */
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
int __init musb_platform_init(struct musb *musb, void *board_data)
|
||||
static int davinci_musb_init(struct musb *musb)
|
||||
{
|
||||
void __iomem *tibase = musb->ctrl_base;
|
||||
u32 revision;
|
||||
@@ -388,8 +396,6 @@ int __init musb_platform_init(struct musb *musb, void *board_data)
|
||||
|
||||
musb->mregs += DAVINCI_BASE_OFFSET;
|
||||
|
||||
clk_enable(musb->clock);
|
||||
|
||||
/* returns zero if e.g. not clocked */
|
||||
revision = musb_readl(tibase, DAVINCI_USB_VERSION_REG);
|
||||
if (revision == 0)
|
||||
@@ -398,8 +404,7 @@ int __init musb_platform_init(struct musb *musb, void *board_data)
|
||||
if (is_host_enabled(musb))
|
||||
setup_timer(&otg_workaround, otg_timer, (unsigned long) musb);
|
||||
|
||||
musb->board_set_vbus = davinci_set_vbus;
|
||||
davinci_source_power(musb, 0, 1);
|
||||
davinci_musb_source_power(musb, 0, 1);
|
||||
|
||||
/* dm355 EVM swaps D+/D- for signal integrity, and
|
||||
* is clocked from the main 24 MHz crystal.
|
||||
@@ -440,18 +445,16 @@ int __init musb_platform_init(struct musb *musb, void *board_data)
|
||||
revision, __raw_readl(USB_PHY_CTRL),
|
||||
musb_readb(tibase, DAVINCI_USB_CTRL_REG));
|
||||
|
||||
musb->isr = davinci_interrupt;
|
||||
musb->isr = davinci_musb_interrupt;
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
clk_disable(musb->clock);
|
||||
|
||||
otg_put_transceiver(musb->xceiv);
|
||||
usb_nop_xceiv_unregister();
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
int musb_platform_exit(struct musb *musb)
|
||||
static int davinci_musb_exit(struct musb *musb)
|
||||
{
|
||||
if (is_host_enabled(musb))
|
||||
del_timer_sync(&otg_workaround);
|
||||
@@ -465,7 +468,7 @@ int musb_platform_exit(struct musb *musb)
|
||||
__raw_writel(deepsleep, DM355_DEEPSLEEP);
|
||||
}
|
||||
|
||||
davinci_source_power(musb, 0 /*off*/, 1);
|
||||
davinci_musb_source_power(musb, 0 /*off*/, 1);
|
||||
|
||||
/* delay, to avoid problems with module reload */
|
||||
if (is_host_enabled(musb) && musb->xceiv->default_a) {
|
||||
@@ -495,10 +498,141 @@ int musb_platform_exit(struct musb *musb)
|
||||
|
||||
phy_off();
|
||||
|
||||
clk_disable(musb->clock);
|
||||
|
||||
otg_put_transceiver(musb->xceiv);
|
||||
usb_nop_xceiv_unregister();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct musb_platform_ops davinci_ops = {
|
||||
.init = davinci_musb_init,
|
||||
.exit = davinci_musb_exit,
|
||||
|
||||
.enable = davinci_musb_enable,
|
||||
.disable = davinci_musb_disable,
|
||||
|
||||
.set_mode = davinci_musb_set_mode,
|
||||
|
||||
.set_vbus = davinci_musb_set_vbus,
|
||||
};
|
||||
|
||||
static u64 davinci_dmamask = DMA_BIT_MASK(32);
|
||||
|
||||
static int __init davinci_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct musb_hdrc_platform_data *pdata = pdev->dev.platform_data;
|
||||
struct platform_device *musb;
|
||||
struct davinci_glue *glue;
|
||||
struct clk *clk;
|
||||
|
||||
int ret = -ENOMEM;
|
||||
|
||||
glue = kzalloc(sizeof(*glue), GFP_KERNEL);
|
||||
if (!glue) {
|
||||
dev_err(&pdev->dev, "failed to allocate glue context\n");
|
||||
goto err0;
|
||||
}
|
||||
|
||||
musb = platform_device_alloc("musb-hdrc", -1);
|
||||
if (!musb) {
|
||||
dev_err(&pdev->dev, "failed to allocate musb device\n");
|
||||
goto err1;
|
||||
}
|
||||
|
||||
clk = clk_get(&pdev->dev, "usb");
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(&pdev->dev, "failed to get clock\n");
|
||||
ret = PTR_ERR(clk);
|
||||
goto err2;
|
||||
}
|
||||
|
||||
ret = clk_enable(clk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to enable clock\n");
|
||||
goto err3;
|
||||
}
|
||||
|
||||
musb->dev.parent = &pdev->dev;
|
||||
musb->dev.dma_mask = &davinci_dmamask;
|
||||
musb->dev.coherent_dma_mask = davinci_dmamask;
|
||||
|
||||
glue->dev = &pdev->dev;
|
||||
glue->musb = musb;
|
||||
glue->clk = clk;
|
||||
|
||||
pdata->platform_ops = &davinci_ops;
|
||||
|
||||
platform_set_drvdata(pdev, glue);
|
||||
|
||||
ret = platform_device_add_resources(musb, pdev->resource,
|
||||
pdev->num_resources);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to add resources\n");
|
||||
goto err4;
|
||||
}
|
||||
|
||||
ret = platform_device_add_data(musb, pdata, sizeof(*pdata));
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to add platform_data\n");
|
||||
goto err4;
|
||||
}
|
||||
|
||||
ret = platform_device_add(musb);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to register musb device\n");
|
||||
goto err4;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err4:
|
||||
clk_disable(clk);
|
||||
|
||||
err3:
|
||||
clk_put(clk);
|
||||
|
||||
err2:
|
||||
platform_device_put(musb);
|
||||
|
||||
err1:
|
||||
kfree(glue);
|
||||
|
||||
err0:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __exit davinci_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct davinci_glue *glue = platform_get_drvdata(pdev);
|
||||
|
||||
platform_device_del(glue->musb);
|
||||
platform_device_put(glue->musb);
|
||||
clk_disable(glue->clk);
|
||||
clk_put(glue->clk);
|
||||
kfree(glue);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver davinci_driver = {
|
||||
.remove = __exit_p(davinci_remove),
|
||||
.driver = {
|
||||
.name = "musb-davinci",
|
||||
},
|
||||
};
|
||||
|
||||
MODULE_DESCRIPTION("DaVinci MUSB Glue Layer");
|
||||
MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
||||
static int __init davinci_init(void)
|
||||
{
|
||||
return platform_driver_probe(&davinci_driver, davinci_probe);
|
||||
}
|
||||
subsys_initcall(davinci_init);
|
||||
|
||||
static void __exit davinci_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&davinci_driver);
|
||||
}
|
||||
module_exit(davinci_exit);
|
||||
|
||||
@@ -99,19 +99,8 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#ifdef CONFIG_ARM
|
||||
#include <mach/hardware.h>
|
||||
#include <mach/memory.h>
|
||||
#include <asm/mach-types.h>
|
||||
#endif
|
||||
|
||||
#include "musb_core.h"
|
||||
|
||||
|
||||
#ifdef CONFIG_ARCH_DAVINCI
|
||||
#include "davinci.h"
|
||||
#endif
|
||||
|
||||
#define TA_WAIT_BCON(m) max_t(int, (m)->a_wait_bcon, OTG_TIME_A_WAIT_BCON)
|
||||
|
||||
|
||||
@@ -126,7 +115,7 @@ MODULE_PARM_DESC(debug, "Debug message level. Default = 0");
|
||||
|
||||
#define DRIVER_INFO DRIVER_DESC ", v" MUSB_VERSION
|
||||
|
||||
#define MUSB_DRIVER_NAME "musb_hdrc"
|
||||
#define MUSB_DRIVER_NAME "musb-hdrc"
|
||||
const char musb_driver_name[] = MUSB_DRIVER_NAME;
|
||||
|
||||
MODULE_DESCRIPTION(DRIVER_INFO);
|
||||
@@ -230,7 +219,7 @@ static struct otg_io_access_ops musb_ulpi_access = {
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
#if !defined(CONFIG_USB_TUSB6010) && !defined(CONFIG_BLACKFIN)
|
||||
#if !defined(CONFIG_USB_MUSB_TUSB6010) && !defined(CONFIG_USB_MUSB_BLACKFIN)
|
||||
|
||||
/*
|
||||
* Load an endpoint's FIFO
|
||||
@@ -390,7 +379,7 @@ void musb_otg_timer_func(unsigned long data)
|
||||
case OTG_STATE_A_SUSPEND:
|
||||
case OTG_STATE_A_WAIT_BCON:
|
||||
DBG(1, "HNP: %s timeout\n", otg_state_string(musb));
|
||||
musb_set_vbus(musb, 0);
|
||||
musb_platform_set_vbus(musb, 0);
|
||||
musb->xceiv->state = OTG_STATE_A_WAIT_VFALL;
|
||||
break;
|
||||
default:
|
||||
@@ -571,7 +560,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
|
||||
musb->ep0_stage = MUSB_EP0_START;
|
||||
musb->xceiv->state = OTG_STATE_A_IDLE;
|
||||
MUSB_HST_MODE(musb);
|
||||
musb_set_vbus(musb, 1);
|
||||
musb_platform_set_vbus(musb, 1);
|
||||
|
||||
handled = IRQ_HANDLED;
|
||||
}
|
||||
@@ -642,7 +631,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
|
||||
|
||||
/* go through A_WAIT_VFALL then start a new session */
|
||||
if (!ignore)
|
||||
musb_set_vbus(musb, 0);
|
||||
musb_platform_set_vbus(musb, 0);
|
||||
handled = IRQ_HANDLED;
|
||||
}
|
||||
|
||||
@@ -1049,8 +1038,6 @@ static void musb_shutdown(struct platform_device *pdev)
|
||||
spin_lock_irqsave(&musb->lock, flags);
|
||||
musb_platform_disable(musb);
|
||||
musb_generic_disable(musb);
|
||||
if (musb->clock)
|
||||
clk_put(musb->clock);
|
||||
spin_unlock_irqrestore(&musb->lock, flags);
|
||||
|
||||
if (!is_otg_enabled(musb) && is_host_enabled(musb))
|
||||
@@ -1074,10 +1061,11 @@ static void musb_shutdown(struct platform_device *pdev)
|
||||
* We don't currently use dynamic fifo setup capability to do anything
|
||||
* more than selecting one of a bunch of predefined configurations.
|
||||
*/
|
||||
#if defined(CONFIG_USB_TUSB6010) || \
|
||||
defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3) \
|
||||
|| defined(CONFIG_ARCH_OMAP4)
|
||||
#if defined(CONFIG_USB_MUSB_TUSB6010) || defined(CONFIG_USB_MUSB_OMAP2PLUS) \
|
||||
|| defined(CONFIG_USB_MUSB_AM35X)
|
||||
static ushort __initdata fifo_mode = 4;
|
||||
#elif defined(CONFIG_USB_MUSB_UX500)
|
||||
static ushort __initdata fifo_mode = 5;
|
||||
#else
|
||||
static ushort __initdata fifo_mode = 2;
|
||||
#endif
|
||||
@@ -1501,7 +1489,7 @@ static int __init musb_core_init(u16 musb_type, struct musb *musb)
|
||||
struct musb_hw_ep *hw_ep = musb->endpoints + i;
|
||||
|
||||
hw_ep->fifo = MUSB_FIFO_OFFSET(i) + mbase;
|
||||
#ifdef CONFIG_USB_TUSB6010
|
||||
#ifdef CONFIG_USB_MUSB_TUSB6010
|
||||
hw_ep->fifo_async = musb->async + 0x400 + MUSB_FIFO_OFFSET(i);
|
||||
hw_ep->fifo_sync = musb->sync + 0x400 + MUSB_FIFO_OFFSET(i);
|
||||
hw_ep->fifo_sync_va =
|
||||
@@ -1548,7 +1536,8 @@ static int __init musb_core_init(u16 musb_type, struct musb *musb)
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3430) || \
|
||||
defined(CONFIG_ARCH_OMAP4)
|
||||
defined(CONFIG_ARCH_OMAP4) || defined(CONFIG_ARCH_U8500) || \
|
||||
defined(CONFIG_ARCH_U5500)
|
||||
|
||||
static irqreturn_t generic_interrupt(int irq, void *__hci)
|
||||
{
|
||||
@@ -1904,6 +1893,7 @@ allocate_instance(struct device *dev,
|
||||
}
|
||||
|
||||
musb->controller = dev;
|
||||
|
||||
return musb;
|
||||
}
|
||||
|
||||
@@ -2000,30 +1990,14 @@ bad_config:
|
||||
spin_lock_init(&musb->lock);
|
||||
musb->board_mode = plat->mode;
|
||||
musb->board_set_power = plat->set_power;
|
||||
musb->set_clock = plat->set_clock;
|
||||
musb->min_power = plat->min_power;
|
||||
|
||||
/* Clock usage is chip-specific ... functional clock (DaVinci,
|
||||
* OMAP2430), or PHY ref (some TUSB6010 boards). All this core
|
||||
* code does is make sure a clock handle is available; platform
|
||||
* code manages it during start/stop and suspend/resume.
|
||||
*/
|
||||
if (plat->clock) {
|
||||
musb->clock = clk_get(dev, plat->clock);
|
||||
if (IS_ERR(musb->clock)) {
|
||||
status = PTR_ERR(musb->clock);
|
||||
musb->clock = NULL;
|
||||
goto fail1;
|
||||
}
|
||||
}
|
||||
musb->ops = plat->platform_ops;
|
||||
|
||||
/* The musb_platform_init() call:
|
||||
* - adjusts musb->mregs and musb->isr if needed,
|
||||
* - may initialize an integrated tranceiver
|
||||
* - initializes musb->xceiv, usually by otg_get_transceiver()
|
||||
* - activates clocks.
|
||||
* - stops powering VBUS
|
||||
* - assigns musb->board_set_vbus if host mode is enabled
|
||||
*
|
||||
* There are various transciever configurations. Blackfin,
|
||||
* DaVinci, TUSB60x0, and others integrate them. OMAP3 uses
|
||||
@@ -2031,9 +2005,9 @@ bad_config:
|
||||
* isp1504, non-OTG, etc) mostly hooking up through ULPI.
|
||||
*/
|
||||
musb->isr = generic_interrupt;
|
||||
status = musb_platform_init(musb, plat->board_data);
|
||||
status = musb_platform_init(musb);
|
||||
if (status < 0)
|
||||
goto fail2;
|
||||
goto fail1;
|
||||
|
||||
if (!musb->isr) {
|
||||
status = -ENODEV;
|
||||
@@ -2186,10 +2160,6 @@ fail3:
|
||||
device_init_wakeup(dev, 0);
|
||||
musb_platform_exit(musb);
|
||||
|
||||
fail2:
|
||||
if (musb->clock)
|
||||
clk_put(musb->clock);
|
||||
|
||||
fail1:
|
||||
dev_err(musb->controller,
|
||||
"musb_init_controller failed with status %d\n", status);
|
||||
@@ -2215,7 +2185,7 @@ static u64 *orig_dma_mask;
|
||||
static int __init musb_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
int irq = platform_get_irq(pdev, 0);
|
||||
int irq = platform_get_irq_byname(pdev, "mc");
|
||||
int status;
|
||||
struct resource *iomem;
|
||||
void __iomem *base;
|
||||
@@ -2265,144 +2235,138 @@ static int __exit musb_remove(struct platform_device *pdev)
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
static struct musb_context_registers musb_context;
|
||||
|
||||
void musb_save_context(struct musb *musb)
|
||||
static void musb_save_context(struct musb *musb)
|
||||
{
|
||||
int i;
|
||||
void __iomem *musb_base = musb->mregs;
|
||||
void __iomem *epio;
|
||||
|
||||
if (is_host_enabled(musb)) {
|
||||
musb_context.frame = musb_readw(musb_base, MUSB_FRAME);
|
||||
musb_context.testmode = musb_readb(musb_base, MUSB_TESTMODE);
|
||||
musb_context.busctl = musb_read_ulpi_buscontrol(musb->mregs);
|
||||
musb->context.frame = musb_readw(musb_base, MUSB_FRAME);
|
||||
musb->context.testmode = musb_readb(musb_base, MUSB_TESTMODE);
|
||||
musb->context.busctl = musb_read_ulpi_buscontrol(musb->mregs);
|
||||
}
|
||||
musb_context.power = musb_readb(musb_base, MUSB_POWER);
|
||||
musb_context.intrtxe = musb_readw(musb_base, MUSB_INTRTXE);
|
||||
musb_context.intrrxe = musb_readw(musb_base, MUSB_INTRRXE);
|
||||
musb_context.intrusbe = musb_readb(musb_base, MUSB_INTRUSBE);
|
||||
musb_context.index = musb_readb(musb_base, MUSB_INDEX);
|
||||
musb_context.devctl = musb_readb(musb_base, MUSB_DEVCTL);
|
||||
musb->context.power = musb_readb(musb_base, MUSB_POWER);
|
||||
musb->context.intrtxe = musb_readw(musb_base, MUSB_INTRTXE);
|
||||
musb->context.intrrxe = musb_readw(musb_base, MUSB_INTRRXE);
|
||||
musb->context.intrusbe = musb_readb(musb_base, MUSB_INTRUSBE);
|
||||
musb->context.index = musb_readb(musb_base, MUSB_INDEX);
|
||||
musb->context.devctl = musb_readb(musb_base, MUSB_DEVCTL);
|
||||
|
||||
for (i = 0; i < musb->config->num_eps; ++i) {
|
||||
epio = musb->endpoints[i].regs;
|
||||
musb_context.index_regs[i].txmaxp =
|
||||
musb->context.index_regs[i].txmaxp =
|
||||
musb_readw(epio, MUSB_TXMAXP);
|
||||
musb_context.index_regs[i].txcsr =
|
||||
musb->context.index_regs[i].txcsr =
|
||||
musb_readw(epio, MUSB_TXCSR);
|
||||
musb_context.index_regs[i].rxmaxp =
|
||||
musb->context.index_regs[i].rxmaxp =
|
||||
musb_readw(epio, MUSB_RXMAXP);
|
||||
musb_context.index_regs[i].rxcsr =
|
||||
musb->context.index_regs[i].rxcsr =
|
||||
musb_readw(epio, MUSB_RXCSR);
|
||||
|
||||
if (musb->dyn_fifo) {
|
||||
musb_context.index_regs[i].txfifoadd =
|
||||
musb->context.index_regs[i].txfifoadd =
|
||||
musb_read_txfifoadd(musb_base);
|
||||
musb_context.index_regs[i].rxfifoadd =
|
||||
musb->context.index_regs[i].rxfifoadd =
|
||||
musb_read_rxfifoadd(musb_base);
|
||||
musb_context.index_regs[i].txfifosz =
|
||||
musb->context.index_regs[i].txfifosz =
|
||||
musb_read_txfifosz(musb_base);
|
||||
musb_context.index_regs[i].rxfifosz =
|
||||
musb->context.index_regs[i].rxfifosz =
|
||||
musb_read_rxfifosz(musb_base);
|
||||
}
|
||||
if (is_host_enabled(musb)) {
|
||||
musb_context.index_regs[i].txtype =
|
||||
musb->context.index_regs[i].txtype =
|
||||
musb_readb(epio, MUSB_TXTYPE);
|
||||
musb_context.index_regs[i].txinterval =
|
||||
musb->context.index_regs[i].txinterval =
|
||||
musb_readb(epio, MUSB_TXINTERVAL);
|
||||
musb_context.index_regs[i].rxtype =
|
||||
musb->context.index_regs[i].rxtype =
|
||||
musb_readb(epio, MUSB_RXTYPE);
|
||||
musb_context.index_regs[i].rxinterval =
|
||||
musb->context.index_regs[i].rxinterval =
|
||||
musb_readb(epio, MUSB_RXINTERVAL);
|
||||
|
||||
musb_context.index_regs[i].txfunaddr =
|
||||
musb->context.index_regs[i].txfunaddr =
|
||||
musb_read_txfunaddr(musb_base, i);
|
||||
musb_context.index_regs[i].txhubaddr =
|
||||
musb->context.index_regs[i].txhubaddr =
|
||||
musb_read_txhubaddr(musb_base, i);
|
||||
musb_context.index_regs[i].txhubport =
|
||||
musb->context.index_regs[i].txhubport =
|
||||
musb_read_txhubport(musb_base, i);
|
||||
|
||||
musb_context.index_regs[i].rxfunaddr =
|
||||
musb->context.index_regs[i].rxfunaddr =
|
||||
musb_read_rxfunaddr(musb_base, i);
|
||||
musb_context.index_regs[i].rxhubaddr =
|
||||
musb->context.index_regs[i].rxhubaddr =
|
||||
musb_read_rxhubaddr(musb_base, i);
|
||||
musb_context.index_regs[i].rxhubport =
|
||||
musb->context.index_regs[i].rxhubport =
|
||||
musb_read_rxhubport(musb_base, i);
|
||||
}
|
||||
}
|
||||
|
||||
musb_platform_save_context(musb, &musb_context);
|
||||
}
|
||||
|
||||
void musb_restore_context(struct musb *musb)
|
||||
static void musb_restore_context(struct musb *musb)
|
||||
{
|
||||
int i;
|
||||
void __iomem *musb_base = musb->mregs;
|
||||
void __iomem *ep_target_regs;
|
||||
void __iomem *epio;
|
||||
|
||||
musb_platform_restore_context(musb, &musb_context);
|
||||
|
||||
if (is_host_enabled(musb)) {
|
||||
musb_writew(musb_base, MUSB_FRAME, musb_context.frame);
|
||||
musb_writeb(musb_base, MUSB_TESTMODE, musb_context.testmode);
|
||||
musb_write_ulpi_buscontrol(musb->mregs, musb_context.busctl);
|
||||
musb_writew(musb_base, MUSB_FRAME, musb->context.frame);
|
||||
musb_writeb(musb_base, MUSB_TESTMODE, musb->context.testmode);
|
||||
musb_write_ulpi_buscontrol(musb->mregs, musb->context.busctl);
|
||||
}
|
||||
musb_writeb(musb_base, MUSB_POWER, musb_context.power);
|
||||
musb_writew(musb_base, MUSB_INTRTXE, musb_context.intrtxe);
|
||||
musb_writew(musb_base, MUSB_INTRRXE, musb_context.intrrxe);
|
||||
musb_writeb(musb_base, MUSB_INTRUSBE, musb_context.intrusbe);
|
||||
musb_writeb(musb_base, MUSB_DEVCTL, musb_context.devctl);
|
||||
musb_writeb(musb_base, MUSB_POWER, musb->context.power);
|
||||
musb_writew(musb_base, MUSB_INTRTXE, musb->context.intrtxe);
|
||||
musb_writew(musb_base, MUSB_INTRRXE, musb->context.intrrxe);
|
||||
musb_writeb(musb_base, MUSB_INTRUSBE, musb->context.intrusbe);
|
||||
musb_writeb(musb_base, MUSB_DEVCTL, musb->context.devctl);
|
||||
|
||||
for (i = 0; i < musb->config->num_eps; ++i) {
|
||||
epio = musb->endpoints[i].regs;
|
||||
musb_writew(epio, MUSB_TXMAXP,
|
||||
musb_context.index_regs[i].txmaxp);
|
||||
musb->context.index_regs[i].txmaxp);
|
||||
musb_writew(epio, MUSB_TXCSR,
|
||||
musb_context.index_regs[i].txcsr);
|
||||
musb->context.index_regs[i].txcsr);
|
||||
musb_writew(epio, MUSB_RXMAXP,
|
||||
musb_context.index_regs[i].rxmaxp);
|
||||
musb->context.index_regs[i].rxmaxp);
|
||||
musb_writew(epio, MUSB_RXCSR,
|
||||
musb_context.index_regs[i].rxcsr);
|
||||
musb->context.index_regs[i].rxcsr);
|
||||
|
||||
if (musb->dyn_fifo) {
|
||||
musb_write_txfifosz(musb_base,
|
||||
musb_context.index_regs[i].txfifosz);
|
||||
musb->context.index_regs[i].txfifosz);
|
||||
musb_write_rxfifosz(musb_base,
|
||||
musb_context.index_regs[i].rxfifosz);
|
||||
musb->context.index_regs[i].rxfifosz);
|
||||
musb_write_txfifoadd(musb_base,
|
||||
musb_context.index_regs[i].txfifoadd);
|
||||
musb->context.index_regs[i].txfifoadd);
|
||||
musb_write_rxfifoadd(musb_base,
|
||||
musb_context.index_regs[i].rxfifoadd);
|
||||
musb->context.index_regs[i].rxfifoadd);
|
||||
}
|
||||
|
||||
if (is_host_enabled(musb)) {
|
||||
musb_writeb(epio, MUSB_TXTYPE,
|
||||
musb_context.index_regs[i].txtype);
|
||||
musb->context.index_regs[i].txtype);
|
||||
musb_writeb(epio, MUSB_TXINTERVAL,
|
||||
musb_context.index_regs[i].txinterval);
|
||||
musb->context.index_regs[i].txinterval);
|
||||
musb_writeb(epio, MUSB_RXTYPE,
|
||||
musb_context.index_regs[i].rxtype);
|
||||
musb->context.index_regs[i].rxtype);
|
||||
musb_writeb(epio, MUSB_RXINTERVAL,
|
||||
|
||||
musb_context.index_regs[i].rxinterval);
|
||||
musb->context.index_regs[i].rxinterval);
|
||||
musb_write_txfunaddr(musb_base, i,
|
||||
musb_context.index_regs[i].txfunaddr);
|
||||
musb->context.index_regs[i].txfunaddr);
|
||||
musb_write_txhubaddr(musb_base, i,
|
||||
musb_context.index_regs[i].txhubaddr);
|
||||
musb->context.index_regs[i].txhubaddr);
|
||||
musb_write_txhubport(musb_base, i,
|
||||
musb_context.index_regs[i].txhubport);
|
||||
musb->context.index_regs[i].txhubport);
|
||||
|
||||
ep_target_regs =
|
||||
musb_read_target_reg_base(i, musb_base);
|
||||
|
||||
musb_write_rxfunaddr(ep_target_regs,
|
||||
musb_context.index_regs[i].rxfunaddr);
|
||||
musb->context.index_regs[i].rxfunaddr);
|
||||
musb_write_rxhubaddr(ep_target_regs,
|
||||
musb_context.index_regs[i].rxhubaddr);
|
||||
musb->context.index_regs[i].rxhubaddr);
|
||||
musb_write_rxhubport(ep_target_regs,
|
||||
musb_context.index_regs[i].rxhubport);
|
||||
musb->context.index_regs[i].rxhubport);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2413,9 +2377,6 @@ static int musb_suspend(struct device *dev)
|
||||
unsigned long flags;
|
||||
struct musb *musb = dev_to_musb(&pdev->dev);
|
||||
|
||||
if (!musb->clock)
|
||||
return 0;
|
||||
|
||||
spin_lock_irqsave(&musb->lock, flags);
|
||||
|
||||
if (is_peripheral_active(musb)) {
|
||||
@@ -2430,10 +2391,6 @@ static int musb_suspend(struct device *dev)
|
||||
|
||||
musb_save_context(musb);
|
||||
|
||||
if (musb->set_clock)
|
||||
musb->set_clock(musb->clock, 0);
|
||||
else
|
||||
clk_disable(musb->clock);
|
||||
spin_unlock_irqrestore(&musb->lock, flags);
|
||||
return 0;
|
||||
}
|
||||
@@ -2443,14 +2400,6 @@ static int musb_resume_noirq(struct device *dev)
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct musb *musb = dev_to_musb(&pdev->dev);
|
||||
|
||||
if (!musb->clock)
|
||||
return 0;
|
||||
|
||||
if (musb->set_clock)
|
||||
musb->set_clock(musb->clock, 1);
|
||||
else
|
||||
clk_enable(musb->clock);
|
||||
|
||||
musb_restore_context(musb);
|
||||
|
||||
/* for static cmos like DaVinci, register values were preserved
|
||||
|
||||
@@ -222,7 +222,7 @@ enum musb_g_ep0_state {
|
||||
#endif
|
||||
|
||||
/* TUSB mapping: "flat" plus ep0 special cases */
|
||||
#if defined(CONFIG_USB_TUSB6010)
|
||||
#if defined(CONFIG_USB_MUSB_TUSB6010)
|
||||
#define musb_ep_select(_mbase, _epnum) \
|
||||
musb_writeb((_mbase), MUSB_INDEX, (_epnum))
|
||||
#define MUSB_EP_OFFSET MUSB_TUSB_OFFSET
|
||||
@@ -253,6 +253,29 @@ enum musb_g_ep0_state {
|
||||
|
||||
/******************************** TYPES *************************************/
|
||||
|
||||
/**
|
||||
* struct musb_platform_ops - Operations passed to musb_core by HW glue layer
|
||||
* @init: turns on clocks, sets up platform-specific registers, etc
|
||||
* @exit: undoes @init
|
||||
* @set_mode: forcefully changes operating mode
|
||||
* @try_ilde: tries to idle the IP
|
||||
* @vbus_status: returns vbus status if possible
|
||||
* @set_vbus: forces vbus status
|
||||
*/
|
||||
struct musb_platform_ops {
|
||||
int (*init)(struct musb *musb);
|
||||
int (*exit)(struct musb *musb);
|
||||
|
||||
void (*enable)(struct musb *musb);
|
||||
void (*disable)(struct musb *musb);
|
||||
|
||||
int (*set_mode)(struct musb *musb, u8 mode);
|
||||
void (*try_idle)(struct musb *musb, unsigned long timeout);
|
||||
|
||||
int (*vbus_status)(struct musb *musb);
|
||||
void (*set_vbus)(struct musb *musb, int on);
|
||||
};
|
||||
|
||||
/*
|
||||
* struct musb_hw_ep - endpoint hardware (bidirectional)
|
||||
*
|
||||
@@ -263,7 +286,7 @@ struct musb_hw_ep {
|
||||
void __iomem *fifo;
|
||||
void __iomem *regs;
|
||||
|
||||
#ifdef CONFIG_USB_TUSB6010
|
||||
#ifdef CONFIG_USB_MUSB_TUSB6010
|
||||
void __iomem *conf;
|
||||
#endif
|
||||
|
||||
@@ -280,7 +303,7 @@ struct musb_hw_ep {
|
||||
struct dma_channel *tx_channel;
|
||||
struct dma_channel *rx_channel;
|
||||
|
||||
#ifdef CONFIG_USB_TUSB6010
|
||||
#ifdef CONFIG_USB_MUSB_TUSB6010
|
||||
/* TUSB has "asynchronous" and "synchronous" dma modes */
|
||||
dma_addr_t fifo_async;
|
||||
dma_addr_t fifo_sync;
|
||||
@@ -323,14 +346,43 @@ static inline struct usb_request *next_out_request(struct musb_hw_ep *hw_ep)
|
||||
#endif
|
||||
}
|
||||
|
||||
struct musb_csr_regs {
|
||||
/* FIFO registers */
|
||||
u16 txmaxp, txcsr, rxmaxp, rxcsr;
|
||||
u16 rxfifoadd, txfifoadd;
|
||||
u8 txtype, txinterval, rxtype, rxinterval;
|
||||
u8 rxfifosz, txfifosz;
|
||||
u8 txfunaddr, txhubaddr, txhubport;
|
||||
u8 rxfunaddr, rxhubaddr, rxhubport;
|
||||
};
|
||||
|
||||
struct musb_context_registers {
|
||||
|
||||
#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3) || \
|
||||
defined(CONFIG_ARCH_OMAP4)
|
||||
u32 otg_sysconfig, otg_forcestandby;
|
||||
#endif
|
||||
u8 power;
|
||||
u16 intrtxe, intrrxe;
|
||||
u8 intrusbe;
|
||||
u16 frame;
|
||||
u8 index, testmode;
|
||||
|
||||
u8 devctl, busctl, misc;
|
||||
|
||||
struct musb_csr_regs index_regs[MUSB_C_NUM_EPS];
|
||||
};
|
||||
|
||||
/*
|
||||
* struct musb - Driver instance data.
|
||||
*/
|
||||
struct musb {
|
||||
/* device lock */
|
||||
spinlock_t lock;
|
||||
struct clk *clock;
|
||||
struct clk *phy_clock;
|
||||
|
||||
const struct musb_platform_ops *ops;
|
||||
struct musb_context_registers context;
|
||||
|
||||
irqreturn_t (*isr)(int, void *);
|
||||
struct work_struct irq_work;
|
||||
u16 hwvers;
|
||||
@@ -359,11 +411,7 @@ struct musb {
|
||||
|
||||
struct timer_list otg_timer;
|
||||
#endif
|
||||
|
||||
/* called with IRQs blocked; ON/nonzero implies starting a session,
|
||||
* and waiting at least a_wait_vrise_tmout.
|
||||
*/
|
||||
void (*board_set_vbus)(struct musb *, int is_on);
|
||||
struct notifier_block nb;
|
||||
|
||||
struct dma_controller *dma_controller;
|
||||
|
||||
@@ -371,7 +419,7 @@ struct musb {
|
||||
void __iomem *ctrl_base;
|
||||
void __iomem *mregs;
|
||||
|
||||
#ifdef CONFIG_USB_TUSB6010
|
||||
#ifdef CONFIG_USB_MUSB_TUSB6010
|
||||
dma_addr_t async;
|
||||
dma_addr_t sync;
|
||||
void __iomem *sync_va;
|
||||
@@ -398,8 +446,6 @@ struct musb {
|
||||
u8 board_mode; /* enum musb_mode */
|
||||
int (*board_set_power)(int state);
|
||||
|
||||
int (*set_clock)(struct clk *clk, int is_active);
|
||||
|
||||
u8 min_power; /* vbus for periph, in mA/2 */
|
||||
|
||||
bool is_host;
|
||||
@@ -458,52 +504,6 @@ struct musb {
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
struct musb_csr_regs {
|
||||
/* FIFO registers */
|
||||
u16 txmaxp, txcsr, rxmaxp, rxcsr;
|
||||
u16 rxfifoadd, txfifoadd;
|
||||
u8 txtype, txinterval, rxtype, rxinterval;
|
||||
u8 rxfifosz, txfifosz;
|
||||
u8 txfunaddr, txhubaddr, txhubport;
|
||||
u8 rxfunaddr, rxhubaddr, rxhubport;
|
||||
};
|
||||
|
||||
struct musb_context_registers {
|
||||
|
||||
#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3) || \
|
||||
defined(CONFIG_ARCH_OMAP4)
|
||||
u32 otg_sysconfig, otg_forcestandby;
|
||||
#endif
|
||||
u8 power;
|
||||
u16 intrtxe, intrrxe;
|
||||
u8 intrusbe;
|
||||
u16 frame;
|
||||
u8 index, testmode;
|
||||
|
||||
u8 devctl, busctl, misc;
|
||||
|
||||
struct musb_csr_regs index_regs[MUSB_C_NUM_EPS];
|
||||
};
|
||||
|
||||
#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3) || \
|
||||
defined(CONFIG_ARCH_OMAP4)
|
||||
extern void musb_platform_save_context(struct musb *musb,
|
||||
struct musb_context_registers *musb_context);
|
||||
extern void musb_platform_restore_context(struct musb *musb,
|
||||
struct musb_context_registers *musb_context);
|
||||
#else
|
||||
#define musb_platform_save_context(m, x) do {} while (0)
|
||||
#define musb_platform_restore_context(m, x) do {} while (0)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
static inline void musb_set_vbus(struct musb *musb, int is_on)
|
||||
{
|
||||
musb->board_set_vbus(musb, is_on);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USB_GADGET_MUSB_HDRC
|
||||
static inline struct musb *gadget_to_musb(struct usb_gadget *g)
|
||||
{
|
||||
@@ -592,29 +592,63 @@ extern void musb_load_testpacket(struct musb *);
|
||||
|
||||
extern irqreturn_t musb_interrupt(struct musb *);
|
||||
|
||||
extern void musb_platform_enable(struct musb *musb);
|
||||
extern void musb_platform_disable(struct musb *musb);
|
||||
|
||||
extern void musb_hnp_stop(struct musb *musb);
|
||||
|
||||
extern int musb_platform_set_mode(struct musb *musb, u8 musb_mode);
|
||||
static inline void musb_platform_set_vbus(struct musb *musb, int is_on)
|
||||
{
|
||||
if (musb->ops->set_vbus)
|
||||
musb->ops->set_vbus(musb, is_on);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_USB_TUSB6010) || defined(CONFIG_BLACKFIN) || \
|
||||
defined(CONFIG_ARCH_DAVINCI_DA8XX) || \
|
||||
defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3) || \
|
||||
defined(CONFIG_ARCH_OMAP4)
|
||||
extern void musb_platform_try_idle(struct musb *musb, unsigned long timeout);
|
||||
#else
|
||||
#define musb_platform_try_idle(x, y) do {} while (0)
|
||||
#endif
|
||||
static inline void musb_platform_enable(struct musb *musb)
|
||||
{
|
||||
if (musb->ops->enable)
|
||||
musb->ops->enable(musb);
|
||||
}
|
||||
|
||||
#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
|
||||
#endif
|
||||
static inline void musb_platform_disable(struct musb *musb)
|
||||
{
|
||||
if (musb->ops->disable)
|
||||
musb->ops->disable(musb);
|
||||
}
|
||||
|
||||
extern int __init musb_platform_init(struct musb *musb, void *board_data);
|
||||
extern int musb_platform_exit(struct musb *musb);
|
||||
static inline int musb_platform_set_mode(struct musb *musb, u8 mode)
|
||||
{
|
||||
if (!musb->ops->set_mode)
|
||||
return 0;
|
||||
|
||||
return musb->ops->set_mode(musb, mode);
|
||||
}
|
||||
|
||||
static inline void musb_platform_try_idle(struct musb *musb,
|
||||
unsigned long timeout)
|
||||
{
|
||||
if (musb->ops->try_idle)
|
||||
musb->ops->try_idle(musb, timeout);
|
||||
}
|
||||
|
||||
static inline int musb_platform_get_vbus_status(struct musb *musb)
|
||||
{
|
||||
if (!musb->ops->vbus_status)
|
||||
return 0;
|
||||
|
||||
return musb->ops->vbus_status(musb);
|
||||
}
|
||||
|
||||
static inline int musb_platform_init(struct musb *musb)
|
||||
{
|
||||
if (!musb->ops->init)
|
||||
return -EINVAL;
|
||||
|
||||
return musb->ops->init(musb);
|
||||
}
|
||||
|
||||
static inline int musb_platform_exit(struct musb *musb)
|
||||
{
|
||||
if (!musb->ops->exit)
|
||||
return -EINVAL;
|
||||
|
||||
return musb->ops->exit(musb);
|
||||
}
|
||||
|
||||
#endif /* __MUSB_CORE_H__ */
|
||||
|
||||
@@ -1136,13 +1136,16 @@ struct usb_request *musb_alloc_request(struct usb_ep *ep, gfp_t gfp_flags)
|
||||
struct musb_request *request = NULL;
|
||||
|
||||
request = kzalloc(sizeof *request, gfp_flags);
|
||||
if (request) {
|
||||
INIT_LIST_HEAD(&request->request.list);
|
||||
request->request.dma = DMA_ADDR_INVALID;
|
||||
request->epnum = musb_ep->current_epnum;
|
||||
request->ep = musb_ep;
|
||||
if (!request) {
|
||||
DBG(4, "not enough memory\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&request->request.list);
|
||||
request->request.dma = DMA_ADDR_INVALID;
|
||||
request->epnum = musb_ep->current_epnum;
|
||||
request->ep = musb_ep;
|
||||
|
||||
return &request->request;
|
||||
}
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ static inline void musb_writel(void __iomem *addr, unsigned offset, u32 data)
|
||||
{ __raw_writel(data, addr + offset); }
|
||||
|
||||
|
||||
#ifdef CONFIG_USB_TUSB6010
|
||||
#ifdef CONFIG_USB_MUSB_TUSB6010
|
||||
|
||||
/*
|
||||
* TUSB6010 doesn't allow 8-bit access; 16-bit access is the minimum.
|
||||
@@ -114,7 +114,7 @@ static inline u8 musb_readb(const void __iomem *addr, unsigned offset)
|
||||
static inline void musb_writeb(void __iomem *addr, unsigned offset, u8 data)
|
||||
{ __raw_writeb(data, addr + offset); }
|
||||
|
||||
#endif /* CONFIG_USB_TUSB6010 */
|
||||
#endif /* CONFIG_USB_MUSB_TUSB6010 */
|
||||
|
||||
#else
|
||||
|
||||
|
||||
@@ -234,7 +234,7 @@
|
||||
#define MUSB_TESTMODE 0x0F /* 8 bit */
|
||||
|
||||
/* Get offset for a given FIFO from musb->mregs */
|
||||
#ifdef CONFIG_USB_TUSB6010
|
||||
#ifdef CONFIG_USB_MUSB_TUSB6010
|
||||
#define MUSB_FIFO_OFFSET(epnum) (0x200 + ((epnum) * 0x20))
|
||||
#else
|
||||
#define MUSB_FIFO_OFFSET(epnum) (0x20 + ((epnum) * 4))
|
||||
@@ -295,7 +295,7 @@
|
||||
#define MUSB_FLAT_OFFSET(_epnum, _offset) \
|
||||
(0x100 + (0x10*(_epnum)) + (_offset))
|
||||
|
||||
#ifdef CONFIG_USB_TUSB6010
|
||||
#ifdef CONFIG_USB_MUSB_TUSB6010
|
||||
/* TUSB6010 EP0 configuration register is special */
|
||||
#define MUSB_TUSB_OFFSET(_epnum, _offset) \
|
||||
(0x10 + _offset)
|
||||
|
||||
@@ -276,7 +276,7 @@ int musb_hub_control(
|
||||
break;
|
||||
case USB_PORT_FEAT_POWER:
|
||||
if (!(is_otg_enabled(musb) && hcd->self.is_b_host))
|
||||
musb_set_vbus(musb, 0);
|
||||
musb_platform_set_vbus(musb, 0);
|
||||
break;
|
||||
case USB_PORT_FEAT_C_CONNECTION:
|
||||
case USB_PORT_FEAT_C_ENABLE:
|
||||
|
||||
@@ -377,7 +377,7 @@ dma_controller_create(struct musb *musb, void __iomem *base)
|
||||
struct musb_dma_controller *controller;
|
||||
struct device *dev = musb->controller;
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
int irq = platform_get_irq(pdev, 1);
|
||||
int irq = platform_get_irq_byname(pdev, "dma");
|
||||
|
||||
if (irq == 0) {
|
||||
dev_err(dev, "No DMA interrupt line!\n");
|
||||
|
||||
@@ -31,10 +31,18 @@
|
||||
#include <linux/list.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
|
||||
#include "musb_core.h"
|
||||
#include "omap2430.h"
|
||||
|
||||
struct omap2430_glue {
|
||||
struct device *dev;
|
||||
struct platform_device *musb;
|
||||
struct clk *clk;
|
||||
};
|
||||
#define glue_to_musb(g) platform_get_drvdata(g->musb)
|
||||
|
||||
static struct timer_list musb_idle_timer;
|
||||
|
||||
@@ -49,12 +57,8 @@ static void musb_do_idle(unsigned long _musb)
|
||||
|
||||
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;
|
||||
musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
|
||||
|
||||
devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
|
||||
if (devctl & MUSB_DEVCTL_BDEVICE) {
|
||||
@@ -98,7 +102,7 @@ static void musb_do_idle(unsigned long _musb)
|
||||
}
|
||||
|
||||
|
||||
void musb_platform_try_idle(struct musb *musb, unsigned long timeout)
|
||||
static void omap2430_musb_try_idle(struct musb *musb, unsigned long timeout)
|
||||
{
|
||||
unsigned long default_timeout = jiffies + msecs_to_jiffies(3);
|
||||
static unsigned long last_timer;
|
||||
@@ -131,15 +135,11 @@ void musb_platform_try_idle(struct musb *musb, unsigned long timeout)
|
||||
mod_timer(&musb_idle_timer, timeout);
|
||||
}
|
||||
|
||||
void musb_platform_enable(struct musb *musb)
|
||||
{
|
||||
}
|
||||
void musb_platform_disable(struct musb *musb)
|
||||
{
|
||||
}
|
||||
static void omap_set_vbus(struct musb *musb, int is_on)
|
||||
static void omap2430_musb_set_vbus(struct musb *musb, int is_on)
|
||||
{
|
||||
u8 devctl;
|
||||
unsigned long timeout = jiffies + msecs_to_jiffies(1000);
|
||||
int ret = 1;
|
||||
/* HDRC controls CPEN, but beware current surges during device
|
||||
* connect. They can trigger transient overcurrent conditions
|
||||
* that must be ignored.
|
||||
@@ -148,12 +148,35 @@ static void omap_set_vbus(struct musb *musb, int is_on)
|
||||
devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
|
||||
|
||||
if (is_on) {
|
||||
musb->is_active = 1;
|
||||
musb->xceiv->default_a = 1;
|
||||
musb->xceiv->state = OTG_STATE_A_WAIT_VRISE;
|
||||
devctl |= MUSB_DEVCTL_SESSION;
|
||||
if (musb->xceiv->state == OTG_STATE_A_IDLE) {
|
||||
/* start the session */
|
||||
devctl |= MUSB_DEVCTL_SESSION;
|
||||
musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
|
||||
/*
|
||||
* Wait for the musb to set as A device to enable the
|
||||
* VBUS
|
||||
*/
|
||||
while (musb_readb(musb->mregs, MUSB_DEVCTL) & 0x80) {
|
||||
|
||||
MUSB_HST_MODE(musb);
|
||||
cpu_relax();
|
||||
|
||||
if (time_after(jiffies, timeout)) {
|
||||
dev_err(musb->controller,
|
||||
"configured as A device timeout");
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret && musb->xceiv->set_vbus)
|
||||
otg_set_vbus(musb->xceiv, 1);
|
||||
} else {
|
||||
musb->is_active = 1;
|
||||
musb->xceiv->default_a = 1;
|
||||
musb->xceiv->state = OTG_STATE_A_WAIT_VRISE;
|
||||
devctl |= MUSB_DEVCTL_SESSION;
|
||||
MUSB_HST_MODE(musb);
|
||||
}
|
||||
} else {
|
||||
musb->is_active = 0;
|
||||
|
||||
@@ -175,9 +198,7 @@ static void omap_set_vbus(struct musb *musb, int is_on)
|
||||
musb_readb(musb->mregs, MUSB_DEVCTL));
|
||||
}
|
||||
|
||||
static int musb_platform_resume(struct musb *musb);
|
||||
|
||||
int musb_platform_set_mode(struct musb *musb, u8 musb_mode)
|
||||
static int omap2430_musb_set_mode(struct musb *musb, u8 musb_mode)
|
||||
{
|
||||
u8 devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
|
||||
|
||||
@@ -187,10 +208,94 @@ int musb_platform_set_mode(struct musb *musb, u8 musb_mode)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __init musb_platform_init(struct musb *musb, void *board_data)
|
||||
static inline void omap2430_low_level_exit(struct musb *musb)
|
||||
{
|
||||
u32 l;
|
||||
struct omap_musb_board_data *data = board_data;
|
||||
|
||||
/* in any role */
|
||||
l = musb_readl(musb->mregs, OTG_FORCESTDBY);
|
||||
l |= ENABLEFORCE; /* enable MSTANDBY */
|
||||
musb_writel(musb->mregs, OTG_FORCESTDBY, l);
|
||||
|
||||
l = musb_readl(musb->mregs, OTG_SYSCONFIG);
|
||||
l |= ENABLEWAKEUP; /* enable wakeup */
|
||||
musb_writel(musb->mregs, OTG_SYSCONFIG, l);
|
||||
}
|
||||
|
||||
static inline void omap2430_low_level_init(struct musb *musb)
|
||||
{
|
||||
u32 l;
|
||||
|
||||
l = musb_readl(musb->mregs, OTG_SYSCONFIG);
|
||||
l &= ~ENABLEWAKEUP; /* disable wakeup */
|
||||
musb_writel(musb->mregs, OTG_SYSCONFIG, l);
|
||||
|
||||
l = musb_readl(musb->mregs, OTG_FORCESTDBY);
|
||||
l &= ~ENABLEFORCE; /* disable MSTANDBY */
|
||||
musb_writel(musb->mregs, OTG_FORCESTDBY, l);
|
||||
}
|
||||
|
||||
/* blocking notifier support */
|
||||
static int musb_otg_notifications(struct notifier_block *nb,
|
||||
unsigned long event, void *unused)
|
||||
{
|
||||
struct musb *musb = container_of(nb, struct musb, nb);
|
||||
struct device *dev = musb->controller;
|
||||
struct musb_hdrc_platform_data *pdata = dev->platform_data;
|
||||
struct omap_musb_board_data *data = pdata->board_data;
|
||||
|
||||
switch (event) {
|
||||
case USB_EVENT_ID:
|
||||
DBG(4, "ID GND\n");
|
||||
|
||||
if (is_otg_enabled(musb)) {
|
||||
#ifdef CONFIG_USB_GADGET_MUSB_HDRC
|
||||
if (musb->gadget_driver) {
|
||||
otg_init(musb->xceiv);
|
||||
|
||||
if (data->interface_type ==
|
||||
MUSB_INTERFACE_UTMI)
|
||||
omap2430_musb_set_vbus(musb, 1);
|
||||
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
otg_init(musb->xceiv);
|
||||
if (data->interface_type ==
|
||||
MUSB_INTERFACE_UTMI)
|
||||
omap2430_musb_set_vbus(musb, 1);
|
||||
}
|
||||
break;
|
||||
|
||||
case USB_EVENT_VBUS:
|
||||
DBG(4, "VBUS Connect\n");
|
||||
|
||||
otg_init(musb->xceiv);
|
||||
break;
|
||||
|
||||
case USB_EVENT_NONE:
|
||||
DBG(4, "VBUS Disconnect\n");
|
||||
|
||||
if (data->interface_type == MUSB_INTERFACE_UTMI) {
|
||||
if (musb->xceiv->set_vbus)
|
||||
otg_set_vbus(musb->xceiv, 0);
|
||||
}
|
||||
otg_shutdown(musb->xceiv);
|
||||
break;
|
||||
default:
|
||||
DBG(4, "ID float\n");
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static int omap2430_musb_init(struct musb *musb)
|
||||
{
|
||||
u32 l, status = 0;
|
||||
struct device *dev = musb->controller;
|
||||
struct musb_hdrc_platform_data *plat = dev->platform_data;
|
||||
struct omap_musb_board_data *data = plat->board_data;
|
||||
|
||||
/* We require some kind of external transceiver, hooked
|
||||
* up through ULPI. TWL4030-family PMICs include one,
|
||||
@@ -202,7 +307,7 @@ int __init musb_platform_init(struct musb *musb, void *board_data)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
musb_platform_resume(musb);
|
||||
omap2430_low_level_init(musb);
|
||||
|
||||
l = musb_readl(musb->mregs, OTG_SYSCONFIG);
|
||||
l &= ~ENABLEWAKEUP; /* disable wakeup */
|
||||
@@ -239,87 +344,214 @@ int __init musb_platform_init(struct musb *musb, void *board_data)
|
||||
musb_readl(musb->mregs, OTG_INTERFSEL),
|
||||
musb_readl(musb->mregs, OTG_SIMENABLE));
|
||||
|
||||
if (is_host_enabled(musb))
|
||||
musb->board_set_vbus = omap_set_vbus;
|
||||
musb->nb.notifier_call = musb_otg_notifications;
|
||||
status = otg_register_notifier(musb->xceiv, &musb->nb);
|
||||
|
||||
if (status)
|
||||
DBG(1, "notification register failed\n");
|
||||
|
||||
/* check whether cable is already connected */
|
||||
if (musb->xceiv->state ==OTG_STATE_B_IDLE)
|
||||
musb_otg_notifications(&musb->nb, 1,
|
||||
musb->xceiv->gadget);
|
||||
|
||||
setup_timer(&musb_idle_timer, musb_do_idle, (unsigned long) musb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
void musb_platform_save_context(struct musb *musb,
|
||||
struct musb_context_registers *musb_context)
|
||||
static int omap2430_musb_exit(struct musb *musb)
|
||||
{
|
||||
musb_context->otg_sysconfig = musb_readl(musb->mregs, OTG_SYSCONFIG);
|
||||
musb_context->otg_forcestandby = musb_readl(musb->mregs, OTG_FORCESTDBY);
|
||||
}
|
||||
|
||||
void musb_platform_restore_context(struct musb *musb,
|
||||
struct musb_context_registers *musb_context)
|
||||
{
|
||||
musb_writel(musb->mregs, OTG_SYSCONFIG, musb_context->otg_sysconfig);
|
||||
musb_writel(musb->mregs, OTG_FORCESTDBY, musb_context->otg_forcestandby);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int musb_platform_suspend(struct musb *musb)
|
||||
{
|
||||
u32 l;
|
||||
|
||||
if (!musb->clock)
|
||||
return 0;
|
||||
|
||||
/* in any role */
|
||||
l = musb_readl(musb->mregs, OTG_FORCESTDBY);
|
||||
l |= ENABLEFORCE; /* enable MSTANDBY */
|
||||
musb_writel(musb->mregs, OTG_FORCESTDBY, l);
|
||||
|
||||
l = musb_readl(musb->mregs, OTG_SYSCONFIG);
|
||||
l |= ENABLEWAKEUP; /* enable wakeup */
|
||||
musb_writel(musb->mregs, OTG_SYSCONFIG, l);
|
||||
|
||||
otg_set_suspend(musb->xceiv, 1);
|
||||
|
||||
if (musb->set_clock)
|
||||
musb->set_clock(musb->clock, 0);
|
||||
else
|
||||
clk_disable(musb->clock);
|
||||
omap2430_low_level_exit(musb);
|
||||
otg_put_transceiver(musb->xceiv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int musb_platform_resume(struct musb *musb)
|
||||
static const struct musb_platform_ops omap2430_ops = {
|
||||
.init = omap2430_musb_init,
|
||||
.exit = omap2430_musb_exit,
|
||||
|
||||
.set_mode = omap2430_musb_set_mode,
|
||||
.try_idle = omap2430_musb_try_idle,
|
||||
|
||||
.set_vbus = omap2430_musb_set_vbus,
|
||||
};
|
||||
|
||||
static u64 omap2430_dmamask = DMA_BIT_MASK(32);
|
||||
|
||||
static int __init omap2430_probe(struct platform_device *pdev)
|
||||
{
|
||||
u32 l;
|
||||
struct musb_hdrc_platform_data *pdata = pdev->dev.platform_data;
|
||||
struct platform_device *musb;
|
||||
struct omap2430_glue *glue;
|
||||
struct clk *clk;
|
||||
|
||||
if (!musb->clock)
|
||||
return 0;
|
||||
int ret = -ENOMEM;
|
||||
|
||||
glue = kzalloc(sizeof(*glue), GFP_KERNEL);
|
||||
if (!glue) {
|
||||
dev_err(&pdev->dev, "failed to allocate glue context\n");
|
||||
goto err0;
|
||||
}
|
||||
|
||||
musb = platform_device_alloc("musb-hdrc", -1);
|
||||
if (!musb) {
|
||||
dev_err(&pdev->dev, "failed to allocate musb device\n");
|
||||
goto err1;
|
||||
}
|
||||
|
||||
clk = clk_get(&pdev->dev, "ick");
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(&pdev->dev, "failed to get clock\n");
|
||||
ret = PTR_ERR(clk);
|
||||
goto err2;
|
||||
}
|
||||
|
||||
ret = clk_enable(clk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to enable clock\n");
|
||||
goto err3;
|
||||
}
|
||||
|
||||
musb->dev.parent = &pdev->dev;
|
||||
musb->dev.dma_mask = &omap2430_dmamask;
|
||||
musb->dev.coherent_dma_mask = omap2430_dmamask;
|
||||
|
||||
glue->dev = &pdev->dev;
|
||||
glue->musb = musb;
|
||||
glue->clk = clk;
|
||||
|
||||
pdata->platform_ops = &omap2430_ops;
|
||||
|
||||
platform_set_drvdata(pdev, glue);
|
||||
|
||||
ret = platform_device_add_resources(musb, pdev->resource,
|
||||
pdev->num_resources);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to add resources\n");
|
||||
goto err4;
|
||||
}
|
||||
|
||||
ret = platform_device_add_data(musb, pdata, sizeof(*pdata));
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to add platform_data\n");
|
||||
goto err4;
|
||||
}
|
||||
|
||||
ret = platform_device_add(musb);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to register musb device\n");
|
||||
goto err4;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err4:
|
||||
clk_disable(clk);
|
||||
|
||||
err3:
|
||||
clk_put(clk);
|
||||
|
||||
err2:
|
||||
platform_device_put(musb);
|
||||
|
||||
err1:
|
||||
kfree(glue);
|
||||
|
||||
err0:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __exit omap2430_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct omap2430_glue *glue = platform_get_drvdata(pdev);
|
||||
|
||||
platform_device_del(glue->musb);
|
||||
platform_device_put(glue->musb);
|
||||
clk_disable(glue->clk);
|
||||
clk_put(glue->clk);
|
||||
kfree(glue);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static void omap2430_save_context(struct musb *musb)
|
||||
{
|
||||
musb->context.otg_sysconfig = musb_readl(musb->mregs, OTG_SYSCONFIG);
|
||||
musb->context.otg_forcestandby = musb_readl(musb->mregs, OTG_FORCESTDBY);
|
||||
}
|
||||
|
||||
static void omap2430_restore_context(struct musb *musb)
|
||||
{
|
||||
musb_writel(musb->mregs, OTG_SYSCONFIG, musb->context.otg_sysconfig);
|
||||
musb_writel(musb->mregs, OTG_FORCESTDBY, musb->context.otg_forcestandby);
|
||||
}
|
||||
|
||||
static int omap2430_suspend(struct device *dev)
|
||||
{
|
||||
struct omap2430_glue *glue = dev_get_drvdata(dev);
|
||||
struct musb *musb = glue_to_musb(glue);
|
||||
|
||||
omap2430_low_level_exit(musb);
|
||||
otg_set_suspend(musb->xceiv, 1);
|
||||
omap2430_save_context(musb);
|
||||
clk_disable(glue->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int omap2430_resume(struct device *dev)
|
||||
{
|
||||
struct omap2430_glue *glue = dev_get_drvdata(dev);
|
||||
struct musb *musb = glue_to_musb(glue);
|
||||
int ret;
|
||||
|
||||
ret = clk_enable(glue->clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "faled to enable clock\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
omap2430_low_level_init(musb);
|
||||
omap2430_restore_context(musb);
|
||||
otg_set_suspend(musb->xceiv, 0);
|
||||
|
||||
if (musb->set_clock)
|
||||
musb->set_clock(musb->clock, 1);
|
||||
else
|
||||
clk_enable(musb->clock);
|
||||
|
||||
l = musb_readl(musb->mregs, OTG_SYSCONFIG);
|
||||
l &= ~ENABLEWAKEUP; /* disable wakeup */
|
||||
musb_writel(musb->mregs, OTG_SYSCONFIG, l);
|
||||
|
||||
l = musb_readl(musb->mregs, OTG_FORCESTDBY);
|
||||
l &= ~ENABLEFORCE; /* disable MSTANDBY */
|
||||
musb_writel(musb->mregs, OTG_FORCESTDBY, l);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct dev_pm_ops omap2430_pm_ops = {
|
||||
.suspend = omap2430_suspend,
|
||||
.resume = omap2430_resume,
|
||||
};
|
||||
|
||||
int musb_platform_exit(struct musb *musb)
|
||||
#define DEV_PM_OPS (&omap2430_pm_ops)
|
||||
#else
|
||||
#define DEV_PM_OPS NULL
|
||||
#endif
|
||||
|
||||
static struct platform_driver omap2430_driver = {
|
||||
.remove = __exit_p(omap2430_remove),
|
||||
.driver = {
|
||||
.name = "musb-omap2430",
|
||||
.pm = DEV_PM_OPS,
|
||||
},
|
||||
};
|
||||
|
||||
MODULE_DESCRIPTION("OMAP2PLUS MUSB Glue Layer");
|
||||
MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
||||
static int __init omap2430_init(void)
|
||||
{
|
||||
|
||||
musb_platform_suspend(musb);
|
||||
|
||||
otg_put_transceiver(musb->xceiv);
|
||||
return 0;
|
||||
return platform_driver_probe(&omap2430_driver, omap2430_probe);
|
||||
}
|
||||
subsys_initcall(omap2430_init);
|
||||
|
||||
static void __exit omap2430_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&omap2430_driver);
|
||||
}
|
||||
module_exit(omap2430_exit);
|
||||
|
||||
@@ -21,10 +21,16 @@
|
||||
#include <linux/usb.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
|
||||
#include "musb_core.h"
|
||||
|
||||
static void tusb_source_power(struct musb *musb, int is_on);
|
||||
struct tusb6010_glue {
|
||||
struct device *dev;
|
||||
struct platform_device *musb;
|
||||
};
|
||||
|
||||
static void tusb_musb_set_vbus(struct musb *musb, int is_on);
|
||||
|
||||
#define TUSB_REV_MAJOR(reg_val) ((reg_val >> 4) & 0xf)
|
||||
#define TUSB_REV_MINOR(reg_val) (reg_val & 0xf)
|
||||
@@ -50,7 +56,7 @@ u8 tusb_get_revision(struct musb *musb)
|
||||
return rev;
|
||||
}
|
||||
|
||||
static int __init tusb_print_revision(struct musb *musb)
|
||||
static int tusb_print_revision(struct musb *musb)
|
||||
{
|
||||
void __iomem *tbase = musb->ctrl_base;
|
||||
u8 rev;
|
||||
@@ -275,17 +281,6 @@ static int tusb_draw_power(struct otg_transceiver *x, unsigned mA)
|
||||
void __iomem *tbase = musb->ctrl_base;
|
||||
u32 reg;
|
||||
|
||||
/*
|
||||
* Keep clock active when enabled. Note that this is not tied to
|
||||
* drawing VBUS, as with OTG mA can be less than musb->min_power.
|
||||
*/
|
||||
if (musb->set_clock) {
|
||||
if (mA)
|
||||
musb->set_clock(musb->clock, 1);
|
||||
else
|
||||
musb->set_clock(musb->clock, 0);
|
||||
}
|
||||
|
||||
/* tps65030 seems to consume max 100mA, with maybe 60mA available
|
||||
* (measured on one board) for things other than tps and tusb.
|
||||
*
|
||||
@@ -348,7 +343,7 @@ static void tusb_set_clock_source(struct musb *musb, unsigned mode)
|
||||
* USB link is not suspended ... and tells us the relevant wakeup
|
||||
* events. SW_EN for voltage is handled separately.
|
||||
*/
|
||||
void tusb_allow_idle(struct musb *musb, u32 wakeup_enables)
|
||||
static void tusb_allow_idle(struct musb *musb, u32 wakeup_enables)
|
||||
{
|
||||
void __iomem *tbase = musb->ctrl_base;
|
||||
u32 reg;
|
||||
@@ -385,7 +380,7 @@ void tusb_allow_idle(struct musb *musb, u32 wakeup_enables)
|
||||
/*
|
||||
* Updates cable VBUS status. Caller must take care of locking.
|
||||
*/
|
||||
int musb_platform_get_vbus_status(struct musb *musb)
|
||||
static int tusb_musb_vbus_status(struct musb *musb)
|
||||
{
|
||||
void __iomem *tbase = musb->ctrl_base;
|
||||
u32 otg_stat, prcm_mngmt;
|
||||
@@ -431,7 +426,7 @@ static void musb_do_idle(unsigned long _musb)
|
||||
}
|
||||
/* FALLTHROUGH */
|
||||
case OTG_STATE_A_IDLE:
|
||||
tusb_source_power(musb, 0);
|
||||
tusb_musb_set_vbus(musb, 0);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -475,7 +470,7 @@ done:
|
||||
* we don't want to treat that full speed J as a wakeup event.
|
||||
* ... peripherals must draw only suspend current after 10 msec.
|
||||
*/
|
||||
void musb_platform_try_idle(struct musb *musb, unsigned long timeout)
|
||||
static void tusb_musb_try_idle(struct musb *musb, unsigned long timeout)
|
||||
{
|
||||
unsigned long default_timeout = jiffies + msecs_to_jiffies(3);
|
||||
static unsigned long last_timer;
|
||||
@@ -515,7 +510,7 @@ void musb_platform_try_idle(struct musb *musb, unsigned long timeout)
|
||||
| TUSB_DEV_OTG_TIMER_ENABLE) \
|
||||
: 0)
|
||||
|
||||
static void tusb_source_power(struct musb *musb, int is_on)
|
||||
static void tusb_musb_set_vbus(struct musb *musb, int is_on)
|
||||
{
|
||||
void __iomem *tbase = musb->ctrl_base;
|
||||
u32 conf, prcm, timer;
|
||||
@@ -531,8 +526,6 @@ static void tusb_source_power(struct musb *musb, int is_on)
|
||||
devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
|
||||
|
||||
if (is_on) {
|
||||
if (musb->set_clock)
|
||||
musb->set_clock(musb->clock, 1);
|
||||
timer = OTG_TIMER_MS(OTG_TIME_A_WAIT_VRISE);
|
||||
musb->xceiv->default_a = 1;
|
||||
musb->xceiv->state = OTG_STATE_A_WAIT_VRISE;
|
||||
@@ -571,8 +564,6 @@ static void tusb_source_power(struct musb *musb, int is_on)
|
||||
|
||||
devctl &= ~MUSB_DEVCTL_SESSION;
|
||||
conf &= ~TUSB_DEV_CONF_USB_HOST_MODE;
|
||||
if (musb->set_clock)
|
||||
musb->set_clock(musb->clock, 0);
|
||||
}
|
||||
prcm &= ~(TUSB_PRCM_MNGMT_15_SW_EN | TUSB_PRCM_MNGMT_33_SW_EN);
|
||||
|
||||
@@ -599,7 +590,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.
|
||||
*/
|
||||
int musb_platform_set_mode(struct musb *musb, u8 musb_mode)
|
||||
static int tusb_musb_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;
|
||||
@@ -677,7 +668,7 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase)
|
||||
default_a = is_host_enabled(musb);
|
||||
DBG(2, "Default-%c\n", default_a ? 'A' : 'B');
|
||||
musb->xceiv->default_a = default_a;
|
||||
tusb_source_power(musb, default_a);
|
||||
tusb_musb_set_vbus(musb, default_a);
|
||||
|
||||
/* Don't allow idling immediately */
|
||||
if (default_a)
|
||||
@@ -722,7 +713,7 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase)
|
||||
switch (musb->xceiv->state) {
|
||||
case OTG_STATE_A_IDLE:
|
||||
DBG(2, "Got SRP, turning on VBUS\n");
|
||||
musb_set_vbus(musb, 1);
|
||||
musb_platform_set_vbus(musb, 1);
|
||||
|
||||
/* CONNECT can wake if a_wait_bcon is set */
|
||||
if (musb->a_wait_bcon != 0)
|
||||
@@ -748,11 +739,11 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase)
|
||||
*/
|
||||
if (musb->vbuserr_retry) {
|
||||
musb->vbuserr_retry--;
|
||||
tusb_source_power(musb, 1);
|
||||
tusb_musb_set_vbus(musb, 1);
|
||||
} else {
|
||||
musb->vbuserr_retry
|
||||
= VBUSERR_RETRY_COUNT;
|
||||
tusb_source_power(musb, 0);
|
||||
tusb_musb_set_vbus(musb, 0);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -786,7 +777,7 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase)
|
||||
} else {
|
||||
/* REVISIT report overcurrent to hub? */
|
||||
ERR("vbus too slow, devctl %02x\n", devctl);
|
||||
tusb_source_power(musb, 0);
|
||||
tusb_musb_set_vbus(musb, 0);
|
||||
}
|
||||
break;
|
||||
case OTG_STATE_A_WAIT_BCON:
|
||||
@@ -807,7 +798,7 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase)
|
||||
return idle_timeout;
|
||||
}
|
||||
|
||||
static irqreturn_t tusb_interrupt(int irq, void *__hci)
|
||||
static irqreturn_t tusb_musb_interrupt(int irq, void *__hci)
|
||||
{
|
||||
struct musb *musb = __hci;
|
||||
void __iomem *tbase = musb->ctrl_base;
|
||||
@@ -911,7 +902,7 @@ static irqreturn_t tusb_interrupt(int irq, void *__hci)
|
||||
musb_writel(tbase, TUSB_INT_SRC_CLEAR,
|
||||
int_src & ~TUSB_INT_MASK_RESERVED_BITS);
|
||||
|
||||
musb_platform_try_idle(musb, idle_timeout);
|
||||
tusb_musb_try_idle(musb, idle_timeout);
|
||||
|
||||
musb_writel(tbase, TUSB_INT_MASK, int_mask);
|
||||
spin_unlock_irqrestore(&musb->lock, flags);
|
||||
@@ -926,7 +917,7 @@ static int dma_off;
|
||||
* REVISIT:
|
||||
* - Check what is unnecessary in MGC_HdrcStart()
|
||||
*/
|
||||
void musb_platform_enable(struct musb *musb)
|
||||
static void tusb_musb_enable(struct musb *musb)
|
||||
{
|
||||
void __iomem *tbase = musb->ctrl_base;
|
||||
|
||||
@@ -970,7 +961,7 @@ void musb_platform_enable(struct musb *musb)
|
||||
/*
|
||||
* Disables TUSB6010. Caller must take care of locking.
|
||||
*/
|
||||
void musb_platform_disable(struct musb *musb)
|
||||
static void tusb_musb_disable(struct musb *musb)
|
||||
{
|
||||
void __iomem *tbase = musb->ctrl_base;
|
||||
|
||||
@@ -995,7 +986,7 @@ void musb_platform_disable(struct musb *musb)
|
||||
* Sets up TUSB6010 CPU interface specific signals and registers
|
||||
* Note: Settings optimized for OMAP24xx
|
||||
*/
|
||||
static void __init tusb_setup_cpu_interface(struct musb *musb)
|
||||
static void tusb_setup_cpu_interface(struct musb *musb)
|
||||
{
|
||||
void __iomem *tbase = musb->ctrl_base;
|
||||
|
||||
@@ -1022,7 +1013,7 @@ static void __init tusb_setup_cpu_interface(struct musb *musb)
|
||||
musb_writel(tbase, TUSB_WAIT_COUNT, 1);
|
||||
}
|
||||
|
||||
static int __init tusb_start(struct musb *musb)
|
||||
static int tusb_musb_start(struct musb *musb)
|
||||
{
|
||||
void __iomem *tbase = musb->ctrl_base;
|
||||
int ret = 0;
|
||||
@@ -1091,7 +1082,7 @@ err:
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
int __init musb_platform_init(struct musb *musb, void *board_data)
|
||||
static int tusb_musb_init(struct musb *musb)
|
||||
{
|
||||
struct platform_device *pdev;
|
||||
struct resource *mem;
|
||||
@@ -1131,16 +1122,14 @@ int __init musb_platform_init(struct musb *musb, void *board_data)
|
||||
*/
|
||||
musb->mregs += TUSB_BASE_OFFSET;
|
||||
|
||||
ret = tusb_start(musb);
|
||||
ret = tusb_musb_start(musb);
|
||||
if (ret) {
|
||||
printk(KERN_ERR "Could not start tusb6010 (%d)\n",
|
||||
ret);
|
||||
goto done;
|
||||
}
|
||||
musb->isr = tusb_interrupt;
|
||||
musb->isr = tusb_musb_interrupt;
|
||||
|
||||
if (is_host_enabled(musb))
|
||||
musb->board_set_vbus = tusb_source_power;
|
||||
if (is_peripheral_enabled(musb)) {
|
||||
musb->xceiv->set_power = tusb_draw_power;
|
||||
the_musb = musb;
|
||||
@@ -1159,7 +1148,7 @@ done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int musb_platform_exit(struct musb *musb)
|
||||
static int tusb_musb_exit(struct musb *musb)
|
||||
{
|
||||
del_timer_sync(&musb_idle_timer);
|
||||
the_musb = NULL;
|
||||
@@ -1173,3 +1162,115 @@ int musb_platform_exit(struct musb *musb)
|
||||
usb_nop_xceiv_unregister();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct musb_platform_ops tusb_ops = {
|
||||
.init = tusb_musb_init,
|
||||
.exit = tusb_musb_exit,
|
||||
|
||||
.enable = tusb_musb_enable,
|
||||
.disable = tusb_musb_disable,
|
||||
|
||||
.set_mode = tusb_musb_set_mode,
|
||||
.try_idle = tusb_musb_try_idle,
|
||||
|
||||
.vbus_status = tusb_musb_vbus_status,
|
||||
.set_vbus = tusb_musb_set_vbus,
|
||||
};
|
||||
|
||||
static u64 tusb_dmamask = DMA_BIT_MASK(32);
|
||||
|
||||
static int __init tusb_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct musb_hdrc_platform_data *pdata = pdev->dev.platform_data;
|
||||
struct platform_device *musb;
|
||||
struct tusb6010_glue *glue;
|
||||
|
||||
int ret = -ENOMEM;
|
||||
|
||||
glue = kzalloc(sizeof(*glue), GFP_KERNEL);
|
||||
if (!glue) {
|
||||
dev_err(&pdev->dev, "failed to allocate glue context\n");
|
||||
goto err0;
|
||||
}
|
||||
|
||||
musb = platform_device_alloc("musb-hdrc", -1);
|
||||
if (!musb) {
|
||||
dev_err(&pdev->dev, "failed to allocate musb device\n");
|
||||
goto err1;
|
||||
}
|
||||
|
||||
musb->dev.parent = &pdev->dev;
|
||||
musb->dev.dma_mask = &tusb_dmamask;
|
||||
musb->dev.coherent_dma_mask = tusb_dmamask;
|
||||
|
||||
glue->dev = &pdev->dev;
|
||||
glue->musb = musb;
|
||||
|
||||
pdata->platform_ops = &tusb_ops;
|
||||
|
||||
platform_set_drvdata(pdev, glue);
|
||||
|
||||
ret = platform_device_add_resources(musb, pdev->resource,
|
||||
pdev->num_resources);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to add resources\n");
|
||||
goto err2;
|
||||
}
|
||||
|
||||
ret = platform_device_add_data(musb, pdata, sizeof(*pdata));
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to add platform_data\n");
|
||||
goto err2;
|
||||
}
|
||||
|
||||
ret = platform_device_add(musb);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to register musb device\n");
|
||||
goto err1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err2:
|
||||
platform_device_put(musb);
|
||||
|
||||
err1:
|
||||
kfree(glue);
|
||||
|
||||
err0:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __exit tusb_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct tusb6010_glue *glue = platform_get_drvdata(pdev);
|
||||
|
||||
platform_device_del(glue->musb);
|
||||
platform_device_put(glue->musb);
|
||||
kfree(glue);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver tusb_driver = {
|
||||
.remove = __exit_p(tusb_remove),
|
||||
.driver = {
|
||||
.name = "musb-tusb",
|
||||
},
|
||||
};
|
||||
|
||||
MODULE_DESCRIPTION("TUSB6010 MUSB Glue Layer");
|
||||
MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
||||
static int __init tusb_init(void)
|
||||
{
|
||||
return platform_driver_probe(&tusb_driver, tusb_probe);
|
||||
}
|
||||
subsys_initcall(tusb_init);
|
||||
|
||||
static void __exit tusb_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&tusb_driver);
|
||||
}
|
||||
module_exit(tusb_exit);
|
||||
|
||||
216
drivers/usb/musb/ux500.c
Normal file
216
drivers/usb/musb/ux500.c
Normal file
@@ -0,0 +1,216 @@
|
||||
/*
|
||||
* Copyright (C) 2010 ST-Ericsson AB
|
||||
* Mian Yousaf Kaukab <mian.yousaf.kaukab@stericsson.com>
|
||||
*
|
||||
* Based on omap2430.c
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include "musb_core.h"
|
||||
|
||||
struct ux500_glue {
|
||||
struct device *dev;
|
||||
struct platform_device *musb;
|
||||
struct clk *clk;
|
||||
};
|
||||
#define glue_to_musb(g) platform_get_drvdata(g->musb)
|
||||
|
||||
static int ux500_musb_init(struct musb *musb)
|
||||
{
|
||||
musb->xceiv = otg_get_transceiver();
|
||||
if (!musb->xceiv) {
|
||||
pr_err("HS USB OTG: no transceiver configured\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ux500_musb_exit(struct musb *musb)
|
||||
{
|
||||
otg_put_transceiver(musb->xceiv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct musb_platform_ops ux500_ops = {
|
||||
.init = ux500_musb_init,
|
||||
.exit = ux500_musb_exit,
|
||||
};
|
||||
|
||||
static int __init ux500_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct musb_hdrc_platform_data *pdata = pdev->dev.platform_data;
|
||||
struct platform_device *musb;
|
||||
struct ux500_glue *glue;
|
||||
struct clk *clk;
|
||||
|
||||
int ret = -ENOMEM;
|
||||
|
||||
glue = kzalloc(sizeof(*glue), GFP_KERNEL);
|
||||
if (!glue) {
|
||||
dev_err(&pdev->dev, "failed to allocate glue context\n");
|
||||
goto err0;
|
||||
}
|
||||
|
||||
musb = platform_device_alloc("musb-hdrc", -1);
|
||||
if (!musb) {
|
||||
dev_err(&pdev->dev, "failed to allocate musb device\n");
|
||||
goto err1;
|
||||
}
|
||||
|
||||
clk = clk_get(&pdev->dev, "usb");
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(&pdev->dev, "failed to get clock\n");
|
||||
ret = PTR_ERR(clk);
|
||||
goto err2;
|
||||
}
|
||||
|
||||
ret = clk_enable(clk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to enable clock\n");
|
||||
goto err3;
|
||||
}
|
||||
|
||||
musb->dev.parent = &pdev->dev;
|
||||
|
||||
glue->dev = &pdev->dev;
|
||||
glue->musb = musb;
|
||||
glue->clk = clk;
|
||||
|
||||
pdata->platform_ops = &ux500_ops;
|
||||
|
||||
platform_set_drvdata(pdev, glue);
|
||||
|
||||
ret = platform_device_add_resources(musb, pdev->resource,
|
||||
pdev->num_resources);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to add resources\n");
|
||||
goto err4;
|
||||
}
|
||||
|
||||
ret = platform_device_add_data(musb, pdata, sizeof(*pdata));
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to add platform_data\n");
|
||||
goto err4;
|
||||
}
|
||||
|
||||
ret = platform_device_add(musb);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to register musb device\n");
|
||||
goto err4;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err4:
|
||||
clk_disable(clk);
|
||||
|
||||
err3:
|
||||
clk_put(clk);
|
||||
|
||||
err2:
|
||||
platform_device_put(musb);
|
||||
|
||||
err1:
|
||||
kfree(glue);
|
||||
|
||||
err0:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __exit ux500_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ux500_glue *glue = platform_get_drvdata(pdev);
|
||||
|
||||
platform_device_del(glue->musb);
|
||||
platform_device_put(glue->musb);
|
||||
clk_disable(glue->clk);
|
||||
clk_put(glue->clk);
|
||||
kfree(glue);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int ux500_suspend(struct device *dev)
|
||||
{
|
||||
struct ux500_glue *glue = dev_get_drvdata(dev);
|
||||
struct musb *musb = glue_to_musb(glue);
|
||||
|
||||
otg_set_suspend(musb->xceiv, 1);
|
||||
clk_disable(glue->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ux500_resume(struct device *dev)
|
||||
{
|
||||
struct ux500_glue *glue = dev_get_drvdata(dev);
|
||||
struct musb *musb = glue_to_musb(glue);
|
||||
int ret;
|
||||
|
||||
ret = clk_enable(glue->clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to enable clock\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
otg_set_suspend(musb->xceiv, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops ux500_pm_ops = {
|
||||
.suspend = ux500_suspend,
|
||||
.resume = ux500_resume,
|
||||
};
|
||||
|
||||
#define DEV_PM_OPS (&ux500_pm_ops)
|
||||
#else
|
||||
#define DEV_PM_OPS NULL
|
||||
#endif
|
||||
|
||||
static struct platform_driver ux500_driver = {
|
||||
.remove = __exit_p(ux500_remove),
|
||||
.driver = {
|
||||
.name = "musb-ux500",
|
||||
.pm = DEV_PM_OPS,
|
||||
},
|
||||
};
|
||||
|
||||
MODULE_DESCRIPTION("UX500 MUSB Glue Layer");
|
||||
MODULE_AUTHOR("Mian Yousaf Kaukab <mian.yousaf.kaukab@stericsson.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
||||
static int __init ux500_init(void)
|
||||
{
|
||||
return platform_driver_probe(&ux500_driver, ux500_probe);
|
||||
}
|
||||
subsys_initcall(ux500_init);
|
||||
|
||||
static void __exit ux500_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&ux500_driver);
|
||||
}
|
||||
module_exit(ux500_exit);
|
||||
@@ -59,6 +59,18 @@ config TWL4030_USB
|
||||
This transceiver supports high and full speed devices plus,
|
||||
in host mode, low speed.
|
||||
|
||||
config TWL6030_USB
|
||||
tristate "TWL6030 USB Transceiver Driver"
|
||||
depends on TWL4030_CORE
|
||||
select USB_OTG_UTILS
|
||||
help
|
||||
Enable this to support the USB OTG transceiver on TWL6030
|
||||
family chips. This TWL6030 transceiver has the VBUS and ID GND
|
||||
and OTG SRP events capabilities. For all other transceiver functionality
|
||||
UTMI PHY is embedded in OMAP4430. The internal PHY configurations APIs
|
||||
are hooked to this driver through platform_data structure.
|
||||
The definition of internal PHY APIs are in the mach-omap2 layer.
|
||||
|
||||
config NOP_USB_XCEIV
|
||||
tristate "NOP USB Transceiver Driver"
|
||||
select USB_OTG_UTILS
|
||||
@@ -81,4 +93,24 @@ config USB_LANGWELL_OTG
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called langwell_otg.
|
||||
|
||||
config USB_MSM_OTG_72K
|
||||
tristate "OTG support for Qualcomm on-chip USB controller"
|
||||
depends on (USB || USB_GADGET) && ARCH_MSM
|
||||
select USB_OTG_UTILS
|
||||
help
|
||||
Enable this to support the USB OTG transceiver on MSM chips. It
|
||||
handles PHY initialization, clock management, and workarounds
|
||||
required after resetting the hardware and power management.
|
||||
This driver is required even for peripheral only or host only
|
||||
mode configurations.
|
||||
|
||||
config AB8500_USB
|
||||
tristate "AB8500 USB Transceiver Driver"
|
||||
depends on AB8500_CORE
|
||||
select USB_OTG_UTILS
|
||||
help
|
||||
Enable this to support the USB OTG transceiver in AB8500 chip.
|
||||
This transceiver supports high and full speed devices plus,
|
||||
in host mode, low speed.
|
||||
|
||||
endif # USB || OTG
|
||||
|
||||
@@ -12,6 +12,9 @@ obj-$(CONFIG_USB_OTG_UTILS) += otg.o
|
||||
obj-$(CONFIG_USB_GPIO_VBUS) += gpio_vbus.o
|
||||
obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o
|
||||
obj-$(CONFIG_TWL4030_USB) += twl4030-usb.o
|
||||
obj-$(CONFIG_TWL6030_USB) += twl6030-usb.o
|
||||
obj-$(CONFIG_USB_LANGWELL_OTG) += langwell_otg.o
|
||||
obj-$(CONFIG_NOP_USB_XCEIV) += nop-usb-xceiv.o
|
||||
obj-$(CONFIG_USB_ULPI) += ulpi.o
|
||||
obj-$(CONFIG_USB_MSM_OTG_72K) += msm72k_otg.o
|
||||
obj-$(CONFIG_AB8500_USB) += ab8500-usb.o
|
||||
|
||||
585
drivers/usb/otg/ab8500-usb.c
Normal file
585
drivers/usb/otg/ab8500-usb.c
Normal file
@@ -0,0 +1,585 @@
|
||||
/*
|
||||
* drivers/usb/otg/ab8500_usb.c
|
||||
*
|
||||
* USB transceiver driver for AB8500 chip
|
||||
*
|
||||
* Copyright (C) 2010 ST-Ericsson AB
|
||||
* Mian Yousaf Kaukab <mian.yousaf.kaukab@stericsson.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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/usb/otg.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/mfd/abx500.h>
|
||||
#include <linux/mfd/ab8500.h>
|
||||
|
||||
#define AB8500_MAIN_WD_CTRL_REG 0x01
|
||||
#define AB8500_USB_LINE_STAT_REG 0x80
|
||||
#define AB8500_USB_PHY_CTRL_REG 0x8A
|
||||
|
||||
#define AB8500_BIT_OTG_STAT_ID (1 << 0)
|
||||
#define AB8500_BIT_PHY_CTRL_HOST_EN (1 << 0)
|
||||
#define AB8500_BIT_PHY_CTRL_DEVICE_EN (1 << 1)
|
||||
#define AB8500_BIT_WD_CTRL_ENABLE (1 << 0)
|
||||
#define AB8500_BIT_WD_CTRL_KICK (1 << 1)
|
||||
|
||||
#define AB8500_V1x_LINK_STAT_WAIT (HZ/10)
|
||||
#define AB8500_WD_KICK_DELAY_US 100 /* usec */
|
||||
#define AB8500_WD_V11_DISABLE_DELAY_US 100 /* usec */
|
||||
#define AB8500_WD_V10_DISABLE_DELAY_MS 100 /* ms */
|
||||
|
||||
/* Usb line status register */
|
||||
enum ab8500_usb_link_status {
|
||||
USB_LINK_NOT_CONFIGURED = 0,
|
||||
USB_LINK_STD_HOST_NC,
|
||||
USB_LINK_STD_HOST_C_NS,
|
||||
USB_LINK_STD_HOST_C_S,
|
||||
USB_LINK_HOST_CHG_NM,
|
||||
USB_LINK_HOST_CHG_HS,
|
||||
USB_LINK_HOST_CHG_HS_CHIRP,
|
||||
USB_LINK_DEDICATED_CHG,
|
||||
USB_LINK_ACA_RID_A,
|
||||
USB_LINK_ACA_RID_B,
|
||||
USB_LINK_ACA_RID_C_NM,
|
||||
USB_LINK_ACA_RID_C_HS,
|
||||
USB_LINK_ACA_RID_C_HS_CHIRP,
|
||||
USB_LINK_HM_IDGND,
|
||||
USB_LINK_RESERVED,
|
||||
USB_LINK_NOT_VALID_LINK
|
||||
};
|
||||
|
||||
struct ab8500_usb {
|
||||
struct otg_transceiver otg;
|
||||
struct device *dev;
|
||||
int irq_num_id_rise;
|
||||
int irq_num_id_fall;
|
||||
int irq_num_vbus_rise;
|
||||
int irq_num_vbus_fall;
|
||||
int irq_num_link_status;
|
||||
unsigned vbus_draw;
|
||||
struct delayed_work dwork;
|
||||
struct work_struct phy_dis_work;
|
||||
unsigned long link_status_wait;
|
||||
int rev;
|
||||
};
|
||||
|
||||
static inline struct ab8500_usb *xceiv_to_ab(struct otg_transceiver *x)
|
||||
{
|
||||
return container_of(x, struct ab8500_usb, otg);
|
||||
}
|
||||
|
||||
static void ab8500_usb_wd_workaround(struct ab8500_usb *ab)
|
||||
{
|
||||
abx500_set_register_interruptible(ab->dev,
|
||||
AB8500_SYS_CTRL2_BLOCK,
|
||||
AB8500_MAIN_WD_CTRL_REG,
|
||||
AB8500_BIT_WD_CTRL_ENABLE);
|
||||
|
||||
udelay(AB8500_WD_KICK_DELAY_US);
|
||||
|
||||
abx500_set_register_interruptible(ab->dev,
|
||||
AB8500_SYS_CTRL2_BLOCK,
|
||||
AB8500_MAIN_WD_CTRL_REG,
|
||||
(AB8500_BIT_WD_CTRL_ENABLE
|
||||
| AB8500_BIT_WD_CTRL_KICK));
|
||||
|
||||
if (ab->rev > 0x10) /* v1.1 v2.0 */
|
||||
udelay(AB8500_WD_V11_DISABLE_DELAY_US);
|
||||
else /* v1.0 */
|
||||
msleep(AB8500_WD_V10_DISABLE_DELAY_MS);
|
||||
|
||||
abx500_set_register_interruptible(ab->dev,
|
||||
AB8500_SYS_CTRL2_BLOCK,
|
||||
AB8500_MAIN_WD_CTRL_REG,
|
||||
0);
|
||||
}
|
||||
|
||||
static void ab8500_usb_phy_ctrl(struct ab8500_usb *ab, bool sel_host,
|
||||
bool enable)
|
||||
{
|
||||
u8 ctrl_reg;
|
||||
abx500_get_register_interruptible(ab->dev,
|
||||
AB8500_USB,
|
||||
AB8500_USB_PHY_CTRL_REG,
|
||||
&ctrl_reg);
|
||||
if (sel_host) {
|
||||
if (enable)
|
||||
ctrl_reg |= AB8500_BIT_PHY_CTRL_HOST_EN;
|
||||
else
|
||||
ctrl_reg &= ~AB8500_BIT_PHY_CTRL_HOST_EN;
|
||||
} else {
|
||||
if (enable)
|
||||
ctrl_reg |= AB8500_BIT_PHY_CTRL_DEVICE_EN;
|
||||
else
|
||||
ctrl_reg &= ~AB8500_BIT_PHY_CTRL_DEVICE_EN;
|
||||
}
|
||||
|
||||
abx500_set_register_interruptible(ab->dev,
|
||||
AB8500_USB,
|
||||
AB8500_USB_PHY_CTRL_REG,
|
||||
ctrl_reg);
|
||||
|
||||
/* Needed to enable the phy.*/
|
||||
if (enable)
|
||||
ab8500_usb_wd_workaround(ab);
|
||||
}
|
||||
|
||||
#define ab8500_usb_host_phy_en(ab) ab8500_usb_phy_ctrl(ab, true, true)
|
||||
#define ab8500_usb_host_phy_dis(ab) ab8500_usb_phy_ctrl(ab, true, false)
|
||||
#define ab8500_usb_peri_phy_en(ab) ab8500_usb_phy_ctrl(ab, false, true)
|
||||
#define ab8500_usb_peri_phy_dis(ab) ab8500_usb_phy_ctrl(ab, false, false)
|
||||
|
||||
static int ab8500_usb_link_status_update(struct ab8500_usb *ab)
|
||||
{
|
||||
u8 reg;
|
||||
enum ab8500_usb_link_status lsts;
|
||||
void *v = NULL;
|
||||
enum usb_xceiv_events event;
|
||||
|
||||
abx500_get_register_interruptible(ab->dev,
|
||||
AB8500_USB,
|
||||
AB8500_USB_LINE_STAT_REG,
|
||||
®);
|
||||
|
||||
lsts = (reg >> 3) & 0x0F;
|
||||
|
||||
switch (lsts) {
|
||||
case USB_LINK_NOT_CONFIGURED:
|
||||
case USB_LINK_RESERVED:
|
||||
case USB_LINK_NOT_VALID_LINK:
|
||||
/* TODO: Disable regulators. */
|
||||
ab8500_usb_host_phy_dis(ab);
|
||||
ab8500_usb_peri_phy_dis(ab);
|
||||
ab->otg.state = OTG_STATE_B_IDLE;
|
||||
ab->otg.default_a = false;
|
||||
ab->vbus_draw = 0;
|
||||
event = USB_EVENT_NONE;
|
||||
break;
|
||||
|
||||
case USB_LINK_STD_HOST_NC:
|
||||
case USB_LINK_STD_HOST_C_NS:
|
||||
case USB_LINK_STD_HOST_C_S:
|
||||
case USB_LINK_HOST_CHG_NM:
|
||||
case USB_LINK_HOST_CHG_HS:
|
||||
case USB_LINK_HOST_CHG_HS_CHIRP:
|
||||
if (ab->otg.gadget) {
|
||||
/* TODO: Enable regulators. */
|
||||
ab8500_usb_peri_phy_en(ab);
|
||||
v = ab->otg.gadget;
|
||||
}
|
||||
event = USB_EVENT_VBUS;
|
||||
break;
|
||||
|
||||
case USB_LINK_HM_IDGND:
|
||||
if (ab->otg.host) {
|
||||
/* TODO: Enable regulators. */
|
||||
ab8500_usb_host_phy_en(ab);
|
||||
v = ab->otg.host;
|
||||
}
|
||||
ab->otg.state = OTG_STATE_A_IDLE;
|
||||
ab->otg.default_a = true;
|
||||
event = USB_EVENT_ID;
|
||||
break;
|
||||
|
||||
case USB_LINK_ACA_RID_A:
|
||||
case USB_LINK_ACA_RID_B:
|
||||
/* TODO */
|
||||
case USB_LINK_ACA_RID_C_NM:
|
||||
case USB_LINK_ACA_RID_C_HS:
|
||||
case USB_LINK_ACA_RID_C_HS_CHIRP:
|
||||
case USB_LINK_DEDICATED_CHG:
|
||||
/* TODO: vbus_draw */
|
||||
event = USB_EVENT_CHARGER;
|
||||
break;
|
||||
}
|
||||
|
||||
blocking_notifier_call_chain(&ab->otg.notifier, event, v);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ab8500_usb_delayed_work(struct work_struct *work)
|
||||
{
|
||||
struct ab8500_usb *ab = container_of(work, struct ab8500_usb,
|
||||
dwork.work);
|
||||
|
||||
ab8500_usb_link_status_update(ab);
|
||||
}
|
||||
|
||||
static irqreturn_t ab8500_usb_v1x_common_irq(int irq, void *data)
|
||||
{
|
||||
struct ab8500_usb *ab = (struct ab8500_usb *) data;
|
||||
|
||||
/* Wait for link status to become stable. */
|
||||
schedule_delayed_work(&ab->dwork, ab->link_status_wait);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t ab8500_usb_v1x_vbus_fall_irq(int irq, void *data)
|
||||
{
|
||||
struct ab8500_usb *ab = (struct ab8500_usb *) data;
|
||||
|
||||
/* Link status will not be updated till phy is disabled. */
|
||||
ab8500_usb_peri_phy_dis(ab);
|
||||
|
||||
/* Wait for link status to become stable. */
|
||||
schedule_delayed_work(&ab->dwork, ab->link_status_wait);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t ab8500_usb_v20_irq(int irq, void *data)
|
||||
{
|
||||
struct ab8500_usb *ab = (struct ab8500_usb *) data;
|
||||
|
||||
ab8500_usb_link_status_update(ab);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void ab8500_usb_phy_disable_work(struct work_struct *work)
|
||||
{
|
||||
struct ab8500_usb *ab = container_of(work, struct ab8500_usb,
|
||||
phy_dis_work);
|
||||
|
||||
if (!ab->otg.host)
|
||||
ab8500_usb_host_phy_dis(ab);
|
||||
|
||||
if (!ab->otg.gadget)
|
||||
ab8500_usb_peri_phy_dis(ab);
|
||||
}
|
||||
|
||||
static int ab8500_usb_set_power(struct otg_transceiver *otg, unsigned mA)
|
||||
{
|
||||
struct ab8500_usb *ab;
|
||||
|
||||
if (!otg)
|
||||
return -ENODEV;
|
||||
|
||||
ab = xceiv_to_ab(otg);
|
||||
|
||||
ab->vbus_draw = mA;
|
||||
|
||||
if (mA)
|
||||
blocking_notifier_call_chain(&ab->otg.notifier,
|
||||
USB_EVENT_ENUMERATED, ab->otg.gadget);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* TODO: Implement some way for charging or other drivers to read
|
||||
* ab->vbus_draw.
|
||||
*/
|
||||
|
||||
static int ab8500_usb_set_suspend(struct otg_transceiver *x, int suspend)
|
||||
{
|
||||
/* TODO */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ab8500_usb_set_peripheral(struct otg_transceiver *otg,
|
||||
struct usb_gadget *gadget)
|
||||
{
|
||||
struct ab8500_usb *ab;
|
||||
|
||||
if (!otg)
|
||||
return -ENODEV;
|
||||
|
||||
ab = xceiv_to_ab(otg);
|
||||
|
||||
/* Some drivers call this function in atomic context.
|
||||
* Do not update ab8500 registers directly till this
|
||||
* is fixed.
|
||||
*/
|
||||
|
||||
if (!gadget) {
|
||||
/* TODO: Disable regulators. */
|
||||
ab->otg.gadget = NULL;
|
||||
schedule_work(&ab->phy_dis_work);
|
||||
} else {
|
||||
ab->otg.gadget = gadget;
|
||||
ab->otg.state = OTG_STATE_B_IDLE;
|
||||
|
||||
/* Phy will not be enabled if cable is already
|
||||
* plugged-in. Schedule to enable phy.
|
||||
* Use same delay to avoid any race condition.
|
||||
*/
|
||||
schedule_delayed_work(&ab->dwork, ab->link_status_wait);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ab8500_usb_set_host(struct otg_transceiver *otg,
|
||||
struct usb_bus *host)
|
||||
{
|
||||
struct ab8500_usb *ab;
|
||||
|
||||
if (!otg)
|
||||
return -ENODEV;
|
||||
|
||||
ab = xceiv_to_ab(otg);
|
||||
|
||||
/* Some drivers call this function in atomic context.
|
||||
* Do not update ab8500 registers directly till this
|
||||
* is fixed.
|
||||
*/
|
||||
|
||||
if (!host) {
|
||||
/* TODO: Disable regulators. */
|
||||
ab->otg.host = NULL;
|
||||
schedule_work(&ab->phy_dis_work);
|
||||
} else {
|
||||
ab->otg.host = host;
|
||||
/* Phy will not be enabled if cable is already
|
||||
* plugged-in. Schedule to enable phy.
|
||||
* Use same delay to avoid any race condition.
|
||||
*/
|
||||
schedule_delayed_work(&ab->dwork, ab->link_status_wait);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ab8500_usb_irq_free(struct ab8500_usb *ab)
|
||||
{
|
||||
if (ab->rev < 0x20) {
|
||||
free_irq(ab->irq_num_id_rise, ab);
|
||||
free_irq(ab->irq_num_id_fall, ab);
|
||||
free_irq(ab->irq_num_vbus_rise, ab);
|
||||
free_irq(ab->irq_num_vbus_fall, ab);
|
||||
} else {
|
||||
free_irq(ab->irq_num_link_status, ab);
|
||||
}
|
||||
}
|
||||
|
||||
static int ab8500_usb_v1x_res_setup(struct platform_device *pdev,
|
||||
struct ab8500_usb *ab)
|
||||
{
|
||||
int err;
|
||||
|
||||
ab->irq_num_id_rise = platform_get_irq_byname(pdev, "ID_WAKEUP_R");
|
||||
if (ab->irq_num_id_rise < 0) {
|
||||
dev_err(&pdev->dev, "ID rise irq not found\n");
|
||||
return ab->irq_num_id_rise;
|
||||
}
|
||||
err = request_threaded_irq(ab->irq_num_id_rise, NULL,
|
||||
ab8500_usb_v1x_common_irq,
|
||||
IRQF_NO_SUSPEND | IRQF_SHARED,
|
||||
"usb-id-rise", ab);
|
||||
if (err < 0) {
|
||||
dev_err(ab->dev, "request_irq failed for ID rise irq\n");
|
||||
goto fail0;
|
||||
}
|
||||
|
||||
ab->irq_num_id_fall = platform_get_irq_byname(pdev, "ID_WAKEUP_F");
|
||||
if (ab->irq_num_id_fall < 0) {
|
||||
dev_err(&pdev->dev, "ID fall irq not found\n");
|
||||
return ab->irq_num_id_fall;
|
||||
}
|
||||
err = request_threaded_irq(ab->irq_num_id_fall, NULL,
|
||||
ab8500_usb_v1x_common_irq,
|
||||
IRQF_NO_SUSPEND | IRQF_SHARED,
|
||||
"usb-id-fall", ab);
|
||||
if (err < 0) {
|
||||
dev_err(ab->dev, "request_irq failed for ID fall irq\n");
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
ab->irq_num_vbus_rise = platform_get_irq_byname(pdev, "VBUS_DET_R");
|
||||
if (ab->irq_num_vbus_rise < 0) {
|
||||
dev_err(&pdev->dev, "VBUS rise irq not found\n");
|
||||
return ab->irq_num_vbus_rise;
|
||||
}
|
||||
err = request_threaded_irq(ab->irq_num_vbus_rise, NULL,
|
||||
ab8500_usb_v1x_common_irq,
|
||||
IRQF_NO_SUSPEND | IRQF_SHARED,
|
||||
"usb-vbus-rise", ab);
|
||||
if (err < 0) {
|
||||
dev_err(ab->dev, "request_irq failed for Vbus rise irq\n");
|
||||
goto fail2;
|
||||
}
|
||||
|
||||
ab->irq_num_vbus_fall = platform_get_irq_byname(pdev, "VBUS_DET_F");
|
||||
if (ab->irq_num_vbus_fall < 0) {
|
||||
dev_err(&pdev->dev, "VBUS fall irq not found\n");
|
||||
return ab->irq_num_vbus_fall;
|
||||
}
|
||||
err = request_threaded_irq(ab->irq_num_vbus_fall, NULL,
|
||||
ab8500_usb_v1x_vbus_fall_irq,
|
||||
IRQF_NO_SUSPEND | IRQF_SHARED,
|
||||
"usb-vbus-fall", ab);
|
||||
if (err < 0) {
|
||||
dev_err(ab->dev, "request_irq failed for Vbus fall irq\n");
|
||||
goto fail3;
|
||||
}
|
||||
|
||||
return 0;
|
||||
fail3:
|
||||
free_irq(ab->irq_num_vbus_rise, ab);
|
||||
fail2:
|
||||
free_irq(ab->irq_num_id_fall, ab);
|
||||
fail1:
|
||||
free_irq(ab->irq_num_id_rise, ab);
|
||||
fail0:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int ab8500_usb_v2_res_setup(struct platform_device *pdev,
|
||||
struct ab8500_usb *ab)
|
||||
{
|
||||
int err;
|
||||
|
||||
ab->irq_num_link_status = platform_get_irq_byname(pdev,
|
||||
"USB_LINK_STATUS");
|
||||
if (ab->irq_num_link_status < 0) {
|
||||
dev_err(&pdev->dev, "Link status irq not found\n");
|
||||
return ab->irq_num_link_status;
|
||||
}
|
||||
|
||||
err = request_threaded_irq(ab->irq_num_link_status, NULL,
|
||||
ab8500_usb_v20_irq,
|
||||
IRQF_NO_SUSPEND | IRQF_SHARED,
|
||||
"usb-link-status", ab);
|
||||
if (err < 0) {
|
||||
dev_err(ab->dev,
|
||||
"request_irq failed for link status irq\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devinit ab8500_usb_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct ab8500_usb *ab;
|
||||
int err;
|
||||
int rev;
|
||||
|
||||
rev = abx500_get_chip_id(&pdev->dev);
|
||||
if (rev < 0) {
|
||||
dev_err(&pdev->dev, "Chip id read failed\n");
|
||||
return rev;
|
||||
} else if (rev < 0x10) {
|
||||
dev_err(&pdev->dev, "Unsupported AB8500 chip\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ab = kzalloc(sizeof *ab, GFP_KERNEL);
|
||||
if (!ab)
|
||||
return -ENOMEM;
|
||||
|
||||
ab->dev = &pdev->dev;
|
||||
ab->rev = rev;
|
||||
ab->otg.dev = ab->dev;
|
||||
ab->otg.label = "ab8500";
|
||||
ab->otg.state = OTG_STATE_UNDEFINED;
|
||||
ab->otg.set_host = ab8500_usb_set_host;
|
||||
ab->otg.set_peripheral = ab8500_usb_set_peripheral;
|
||||
ab->otg.set_suspend = ab8500_usb_set_suspend;
|
||||
ab->otg.set_power = ab8500_usb_set_power;
|
||||
|
||||
platform_set_drvdata(pdev, ab);
|
||||
|
||||
BLOCKING_INIT_NOTIFIER_HEAD(&ab->otg.notifier);
|
||||
|
||||
/* v1: Wait for link status to become stable.
|
||||
* all: Updates form set_host and set_peripheral as they are atomic.
|
||||
*/
|
||||
INIT_DELAYED_WORK(&ab->dwork, ab8500_usb_delayed_work);
|
||||
|
||||
/* all: Disable phy when called from set_host and set_peripheral */
|
||||
INIT_WORK(&ab->phy_dis_work, ab8500_usb_phy_disable_work);
|
||||
|
||||
if (ab->rev < 0x20) {
|
||||
err = ab8500_usb_v1x_res_setup(pdev, ab);
|
||||
ab->link_status_wait = AB8500_V1x_LINK_STAT_WAIT;
|
||||
} else {
|
||||
err = ab8500_usb_v2_res_setup(pdev, ab);
|
||||
}
|
||||
|
||||
if (err < 0)
|
||||
goto fail0;
|
||||
|
||||
err = otg_set_transceiver(&ab->otg);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "Can't register transceiver\n");
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
dev_info(&pdev->dev, "AB8500 usb driver initialized\n");
|
||||
|
||||
return 0;
|
||||
fail1:
|
||||
ab8500_usb_irq_free(ab);
|
||||
fail0:
|
||||
kfree(ab);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __devexit ab8500_usb_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ab8500_usb *ab = platform_get_drvdata(pdev);
|
||||
|
||||
ab8500_usb_irq_free(ab);
|
||||
|
||||
cancel_delayed_work_sync(&ab->dwork);
|
||||
|
||||
cancel_work_sync(&ab->phy_dis_work);
|
||||
|
||||
otg_set_transceiver(NULL);
|
||||
|
||||
ab8500_usb_host_phy_dis(ab);
|
||||
ab8500_usb_peri_phy_dis(ab);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
kfree(ab);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver ab8500_usb_driver = {
|
||||
.probe = ab8500_usb_probe,
|
||||
.remove = __devexit_p(ab8500_usb_remove),
|
||||
.driver = {
|
||||
.name = "ab8500-usb",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init ab8500_usb_init(void)
|
||||
{
|
||||
return platform_driver_register(&ab8500_usb_driver);
|
||||
}
|
||||
subsys_initcall(ab8500_usb_init);
|
||||
|
||||
static void __exit ab8500_usb_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&ab8500_usb_driver);
|
||||
}
|
||||
module_exit(ab8500_usb_exit);
|
||||
|
||||
MODULE_ALIAS("platform:ab8500_usb");
|
||||
MODULE_AUTHOR("ST-Ericsson AB");
|
||||
MODULE_DESCRIPTION("AB8500 usb transceiver driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
1125
drivers/usb/otg/msm72k_otg.c
Normal file
1125
drivers/usb/otg/msm72k_otg.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -678,7 +678,8 @@ static int __exit twl4030_usb_remove(struct platform_device *pdev)
|
||||
/* disable complete OTG block */
|
||||
twl4030_usb_clear_bits(twl, POWER_CTRL, POWER_CTRL_OTG_ENAB);
|
||||
|
||||
twl4030_phy_power(twl, 0);
|
||||
if (!twl->asleep)
|
||||
twl4030_phy_power(twl, 0);
|
||||
regulator_put(twl->usb1v5);
|
||||
regulator_put(twl->usb1v8);
|
||||
regulator_put(twl->usb3v1);
|
||||
|
||||
493
drivers/usb/otg/twl6030-usb.c
Normal file
493
drivers/usb/otg/twl6030-usb.c
Normal file
@@ -0,0 +1,493 @@
|
||||
/*
|
||||
* twl6030_usb - TWL6030 USB transceiver, talking to OMAP OTG driver.
|
||||
*
|
||||
* Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.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.
|
||||
*
|
||||
* Author: Hema HK <hemahk@ti.com>
|
||||
*
|
||||
* 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 <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/usb/otg.h>
|
||||
#include <linux/i2c/twl.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
/* usb register definitions */
|
||||
#define USB_VENDOR_ID_LSB 0x00
|
||||
#define USB_VENDOR_ID_MSB 0x01
|
||||
#define USB_PRODUCT_ID_LSB 0x02
|
||||
#define USB_PRODUCT_ID_MSB 0x03
|
||||
#define USB_VBUS_CTRL_SET 0x04
|
||||
#define USB_VBUS_CTRL_CLR 0x05
|
||||
#define USB_ID_CTRL_SET 0x06
|
||||
#define USB_ID_CTRL_CLR 0x07
|
||||
#define USB_VBUS_INT_SRC 0x08
|
||||
#define USB_VBUS_INT_LATCH_SET 0x09
|
||||
#define USB_VBUS_INT_LATCH_CLR 0x0A
|
||||
#define USB_VBUS_INT_EN_LO_SET 0x0B
|
||||
#define USB_VBUS_INT_EN_LO_CLR 0x0C
|
||||
#define USB_VBUS_INT_EN_HI_SET 0x0D
|
||||
#define USB_VBUS_INT_EN_HI_CLR 0x0E
|
||||
#define USB_ID_INT_SRC 0x0F
|
||||
#define USB_ID_INT_LATCH_SET 0x10
|
||||
#define USB_ID_INT_LATCH_CLR 0x11
|
||||
|
||||
#define USB_ID_INT_EN_LO_SET 0x12
|
||||
#define USB_ID_INT_EN_LO_CLR 0x13
|
||||
#define USB_ID_INT_EN_HI_SET 0x14
|
||||
#define USB_ID_INT_EN_HI_CLR 0x15
|
||||
#define USB_OTG_ADP_CTRL 0x16
|
||||
#define USB_OTG_ADP_HIGH 0x17
|
||||
#define USB_OTG_ADP_LOW 0x18
|
||||
#define USB_OTG_ADP_RISE 0x19
|
||||
#define USB_OTG_REVISION 0x1A
|
||||
|
||||
/* to be moved to LDO */
|
||||
#define TWL6030_MISC2 0xE5
|
||||
#define TWL6030_CFG_LDO_PD2 0xF5
|
||||
#define TWL6030_BACKUP_REG 0xFA
|
||||
|
||||
#define STS_HW_CONDITIONS 0x21
|
||||
|
||||
/* In module TWL6030_MODULE_PM_MASTER */
|
||||
#define STS_HW_CONDITIONS 0x21
|
||||
#define STS_USB_ID BIT(2)
|
||||
|
||||
/* In module TWL6030_MODULE_PM_RECEIVER */
|
||||
#define VUSB_CFG_TRANS 0x71
|
||||
#define VUSB_CFG_STATE 0x72
|
||||
#define VUSB_CFG_VOLTAGE 0x73
|
||||
|
||||
/* in module TWL6030_MODULE_MAIN_CHARGE */
|
||||
|
||||
#define CHARGERUSB_CTRL1 0x8
|
||||
|
||||
#define CONTROLLER_STAT1 0x03
|
||||
#define VBUS_DET BIT(2)
|
||||
|
||||
struct twl6030_usb {
|
||||
struct otg_transceiver otg;
|
||||
struct device *dev;
|
||||
|
||||
/* for vbus reporting with irqs disabled */
|
||||
spinlock_t lock;
|
||||
|
||||
struct regulator *usb3v3;
|
||||
|
||||
int irq1;
|
||||
int irq2;
|
||||
u8 linkstat;
|
||||
u8 asleep;
|
||||
bool irq_enabled;
|
||||
};
|
||||
|
||||
#define xceiv_to_twl(x) container_of((x), struct twl6030_usb, otg);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static inline int twl6030_writeb(struct twl6030_usb *twl, u8 module,
|
||||
u8 data, u8 address)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
ret = twl_i2c_write_u8(module, data, address);
|
||||
if (ret < 0)
|
||||
dev_err(twl->dev,
|
||||
"Write[0x%x] Error %d\n", address, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline u8 twl6030_readb(struct twl6030_usb *twl, u8 module, u8 address)
|
||||
{
|
||||
u8 data, ret = 0;
|
||||
|
||||
ret = twl_i2c_read_u8(module, &data, address);
|
||||
if (ret >= 0)
|
||||
ret = data;
|
||||
else
|
||||
dev_err(twl->dev,
|
||||
"readb[0x%x,0x%x] Error %d\n",
|
||||
module, address, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
static int twl6030_set_phy_clk(struct otg_transceiver *x, int on)
|
||||
{
|
||||
struct twl6030_usb *twl;
|
||||
struct device *dev;
|
||||
struct twl4030_usb_data *pdata;
|
||||
|
||||
twl = xceiv_to_twl(x);
|
||||
dev = twl->dev;
|
||||
pdata = dev->platform_data;
|
||||
|
||||
pdata->phy_set_clock(twl->dev, on);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int twl6030_phy_init(struct otg_transceiver *x)
|
||||
{
|
||||
u8 hw_state;
|
||||
struct twl6030_usb *twl;
|
||||
struct device *dev;
|
||||
struct twl4030_usb_data *pdata;
|
||||
|
||||
twl = xceiv_to_twl(x);
|
||||
dev = twl->dev;
|
||||
pdata = dev->platform_data;
|
||||
|
||||
regulator_enable(twl->usb3v3);
|
||||
|
||||
hw_state = twl6030_readb(twl, TWL6030_MODULE_ID0, STS_HW_CONDITIONS);
|
||||
|
||||
if (hw_state & STS_USB_ID)
|
||||
pdata->phy_power(twl->dev, 1, 1);
|
||||
else
|
||||
pdata->phy_power(twl->dev, 0, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void twl6030_phy_shutdown(struct otg_transceiver *x)
|
||||
{
|
||||
struct twl6030_usb *twl;
|
||||
struct device *dev;
|
||||
struct twl4030_usb_data *pdata;
|
||||
|
||||
twl = xceiv_to_twl(x);
|
||||
dev = twl->dev;
|
||||
pdata = dev->platform_data;
|
||||
pdata->phy_power(twl->dev, 0, 0);
|
||||
regulator_disable(twl->usb3v3);
|
||||
}
|
||||
|
||||
static int twl6030_usb_ldo_init(struct twl6030_usb *twl)
|
||||
{
|
||||
|
||||
/* Set to OTG_REV 1.3 and turn on the ID_WAKEUP_COMP */
|
||||
twl6030_writeb(twl, TWL6030_MODULE_ID0 , 0x1, TWL6030_BACKUP_REG);
|
||||
|
||||
/* Program CFG_LDO_PD2 register and set VUSB bit */
|
||||
twl6030_writeb(twl, TWL6030_MODULE_ID0 , 0x1, TWL6030_CFG_LDO_PD2);
|
||||
|
||||
/* Program MISC2 register and set bit VUSB_IN_VBAT */
|
||||
twl6030_writeb(twl, TWL6030_MODULE_ID0 , 0x10, TWL6030_MISC2);
|
||||
|
||||
twl->usb3v3 = regulator_get(twl->dev, "vusb");
|
||||
if (IS_ERR(twl->usb3v3))
|
||||
return -ENODEV;
|
||||
|
||||
regulator_enable(twl->usb3v3);
|
||||
|
||||
/* Program the VUSB_CFG_TRANS for ACTIVE state. */
|
||||
twl6030_writeb(twl, TWL_MODULE_PM_RECEIVER, 0x3F,
|
||||
VUSB_CFG_TRANS);
|
||||
|
||||
/* Program the VUSB_CFG_STATE register to ON on all groups. */
|
||||
twl6030_writeb(twl, TWL_MODULE_PM_RECEIVER, 0xE1,
|
||||
VUSB_CFG_STATE);
|
||||
|
||||
/* Program the USB_VBUS_CTRL_SET and set VBUS_ACT_COMP bit */
|
||||
twl6030_writeb(twl, TWL_MODULE_USB, 0x4, USB_VBUS_CTRL_SET);
|
||||
|
||||
/*
|
||||
* Program the USB_ID_CTRL_SET register to enable GND drive
|
||||
* and the ID comparators
|
||||
*/
|
||||
twl6030_writeb(twl, TWL_MODULE_USB, 0x14, USB_ID_CTRL_SET);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t twl6030_usb_vbus_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct twl6030_usb *twl = dev_get_drvdata(dev);
|
||||
unsigned long flags;
|
||||
int ret = -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&twl->lock, flags);
|
||||
|
||||
switch (twl->linkstat) {
|
||||
case USB_EVENT_VBUS:
|
||||
ret = snprintf(buf, PAGE_SIZE, "vbus\n");
|
||||
break;
|
||||
case USB_EVENT_ID:
|
||||
ret = snprintf(buf, PAGE_SIZE, "id\n");
|
||||
break;
|
||||
case USB_EVENT_NONE:
|
||||
ret = snprintf(buf, PAGE_SIZE, "none\n");
|
||||
break;
|
||||
default:
|
||||
ret = snprintf(buf, PAGE_SIZE, "UNKNOWN\n");
|
||||
}
|
||||
spin_unlock_irqrestore(&twl->lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
static DEVICE_ATTR(vbus, 0444, twl6030_usb_vbus_show, NULL);
|
||||
|
||||
static irqreturn_t twl6030_usb_irq(int irq, void *_twl)
|
||||
{
|
||||
struct twl6030_usb *twl = _twl;
|
||||
int status;
|
||||
u8 vbus_state, hw_state;
|
||||
|
||||
hw_state = twl6030_readb(twl, TWL6030_MODULE_ID0, STS_HW_CONDITIONS);
|
||||
|
||||
vbus_state = twl6030_readb(twl, TWL_MODULE_MAIN_CHARGE,
|
||||
CONTROLLER_STAT1);
|
||||
if (!(hw_state & STS_USB_ID)) {
|
||||
if (vbus_state & VBUS_DET) {
|
||||
status = USB_EVENT_VBUS;
|
||||
twl->otg.default_a = false;
|
||||
twl->otg.state = OTG_STATE_B_IDLE;
|
||||
} else {
|
||||
status = USB_EVENT_NONE;
|
||||
}
|
||||
if (status >= 0) {
|
||||
twl->linkstat = status;
|
||||
blocking_notifier_call_chain(&twl->otg.notifier,
|
||||
status, twl->otg.gadget);
|
||||
}
|
||||
}
|
||||
sysfs_notify(&twl->dev->kobj, NULL, "vbus");
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t twl6030_usbotg_irq(int irq, void *_twl)
|
||||
{
|
||||
struct twl6030_usb *twl = _twl;
|
||||
int status = USB_EVENT_NONE;
|
||||
u8 hw_state;
|
||||
|
||||
hw_state = twl6030_readb(twl, TWL6030_MODULE_ID0, STS_HW_CONDITIONS);
|
||||
|
||||
if (hw_state & STS_USB_ID) {
|
||||
|
||||
twl6030_writeb(twl, TWL_MODULE_USB, USB_ID_INT_EN_HI_CLR, 0x1);
|
||||
twl6030_writeb(twl, TWL_MODULE_USB, USB_ID_INT_EN_HI_SET,
|
||||
0x10);
|
||||
status = USB_EVENT_ID;
|
||||
twl->otg.default_a = true;
|
||||
twl->otg.state = OTG_STATE_A_IDLE;
|
||||
blocking_notifier_call_chain(&twl->otg.notifier, status,
|
||||
twl->otg.gadget);
|
||||
} else {
|
||||
twl6030_writeb(twl, TWL_MODULE_USB, USB_ID_INT_EN_HI_CLR,
|
||||
0x10);
|
||||
twl6030_writeb(twl, TWL_MODULE_USB, USB_ID_INT_EN_HI_SET,
|
||||
0x1);
|
||||
}
|
||||
twl6030_writeb(twl, TWL_MODULE_USB, USB_ID_INT_LATCH_CLR, status);
|
||||
twl->linkstat = status;
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int twl6030_set_peripheral(struct otg_transceiver *x,
|
||||
struct usb_gadget *gadget)
|
||||
{
|
||||
struct twl6030_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 twl6030_enable_irq(struct otg_transceiver *x)
|
||||
{
|
||||
struct twl6030_usb *twl = xceiv_to_twl(x);
|
||||
|
||||
twl6030_writeb(twl, TWL_MODULE_USB, USB_ID_INT_EN_HI_SET, 0x1);
|
||||
twl6030_interrupt_unmask(0x05, REG_INT_MSK_LINE_C);
|
||||
twl6030_interrupt_unmask(0x05, REG_INT_MSK_STS_C);
|
||||
|
||||
twl6030_interrupt_unmask(TWL6030_CHARGER_CTRL_INT_MASK,
|
||||
REG_INT_MSK_LINE_C);
|
||||
twl6030_interrupt_unmask(TWL6030_CHARGER_CTRL_INT_MASK,
|
||||
REG_INT_MSK_STS_C);
|
||||
twl6030_usb_irq(twl->irq2, twl);
|
||||
twl6030_usbotg_irq(twl->irq1, twl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int twl6030_set_vbus(struct otg_transceiver *x, bool enabled)
|
||||
{
|
||||
struct twl6030_usb *twl = xceiv_to_twl(x);
|
||||
|
||||
/*
|
||||
* Start driving VBUS. Set OPA_MODE bit in CHARGERUSB_CTRL1
|
||||
* register. This enables boost mode.
|
||||
*/
|
||||
if (enabled)
|
||||
twl6030_writeb(twl, TWL_MODULE_MAIN_CHARGE , 0x40,
|
||||
CHARGERUSB_CTRL1);
|
||||
else
|
||||
twl6030_writeb(twl, TWL_MODULE_MAIN_CHARGE , 0x00,
|
||||
CHARGERUSB_CTRL1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int twl6030_set_host(struct otg_transceiver *x, struct usb_bus *host)
|
||||
{
|
||||
struct twl6030_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 __devinit twl6030_usb_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct twl6030_usb *twl;
|
||||
int status, err;
|
||||
struct twl4030_usb_data *pdata;
|
||||
struct device *dev = &pdev->dev;
|
||||
pdata = dev->platform_data;
|
||||
|
||||
twl = kzalloc(sizeof *twl, GFP_KERNEL);
|
||||
if (!twl)
|
||||
return -ENOMEM;
|
||||
|
||||
twl->dev = &pdev->dev;
|
||||
twl->irq1 = platform_get_irq(pdev, 0);
|
||||
twl->irq2 = platform_get_irq(pdev, 1);
|
||||
twl->otg.dev = twl->dev;
|
||||
twl->otg.label = "twl6030";
|
||||
twl->otg.set_host = twl6030_set_host;
|
||||
twl->otg.set_peripheral = twl6030_set_peripheral;
|
||||
twl->otg.set_vbus = twl6030_set_vbus;
|
||||
twl->otg.init = twl6030_phy_init;
|
||||
twl->otg.shutdown = twl6030_phy_shutdown;
|
||||
|
||||
/* init spinlock for workqueue */
|
||||
spin_lock_init(&twl->lock);
|
||||
|
||||
err = twl6030_usb_ldo_init(twl);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "ldo init failed\n");
|
||||
kfree(twl);
|
||||
return err;
|
||||
}
|
||||
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");
|
||||
|
||||
BLOCKING_INIT_NOTIFIER_HEAD(&twl->otg.notifier);
|
||||
|
||||
twl->irq_enabled = true;
|
||||
status = request_threaded_irq(twl->irq1, NULL, twl6030_usbotg_irq,
|
||||
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
|
||||
"twl6030_usb", twl);
|
||||
if (status < 0) {
|
||||
dev_err(&pdev->dev, "can't get IRQ %d, err %d\n",
|
||||
twl->irq1, status);
|
||||
device_remove_file(twl->dev, &dev_attr_vbus);
|
||||
kfree(twl);
|
||||
return status;
|
||||
}
|
||||
|
||||
status = request_threaded_irq(twl->irq2, NULL, twl6030_usb_irq,
|
||||
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
|
||||
"twl6030_usb", twl);
|
||||
if (status < 0) {
|
||||
dev_err(&pdev->dev, "can't get IRQ %d, err %d\n",
|
||||
twl->irq2, status);
|
||||
free_irq(twl->irq1, twl);
|
||||
device_remove_file(twl->dev, &dev_attr_vbus);
|
||||
kfree(twl);
|
||||
return status;
|
||||
}
|
||||
|
||||
pdata->phy_init(dev);
|
||||
twl6030_enable_irq(&twl->otg);
|
||||
dev_info(&pdev->dev, "Initialized TWL6030 USB module\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __exit twl6030_usb_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct twl6030_usb *twl = platform_get_drvdata(pdev);
|
||||
|
||||
struct twl4030_usb_data *pdata;
|
||||
struct device *dev = &pdev->dev;
|
||||
pdata = dev->platform_data;
|
||||
|
||||
twl6030_interrupt_mask(TWL6030_USBOTG_INT_MASK,
|
||||
REG_INT_MSK_LINE_C);
|
||||
twl6030_interrupt_mask(TWL6030_USBOTG_INT_MASK,
|
||||
REG_INT_MSK_STS_C);
|
||||
free_irq(twl->irq1, twl);
|
||||
free_irq(twl->irq2, twl);
|
||||
regulator_put(twl->usb3v3);
|
||||
pdata->phy_exit(twl->dev);
|
||||
device_remove_file(twl->dev, &dev_attr_vbus);
|
||||
kfree(twl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver twl6030_usb_driver = {
|
||||
.probe = twl6030_usb_probe,
|
||||
.remove = __exit_p(twl6030_usb_remove),
|
||||
.driver = {
|
||||
.name = "twl6030_usb",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init twl6030_usb_init(void)
|
||||
{
|
||||
return platform_driver_register(&twl6030_usb_driver);
|
||||
}
|
||||
subsys_initcall(twl6030_usb_init);
|
||||
|
||||
static void __exit twl6030_usb_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&twl6030_usb_driver);
|
||||
}
|
||||
module_exit(twl6030_usb_exit);
|
||||
|
||||
MODULE_ALIAS("platform:twl6030_usb");
|
||||
MODULE_AUTHOR("Hema HK <hemahk@ti.com>");
|
||||
MODULE_DESCRIPTION("TWL6030 USB transceiver driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -989,6 +989,7 @@ static struct usb_serial_driver option_1port_device = {
|
||||
.set_termios = usb_wwan_set_termios,
|
||||
.tiocmget = usb_wwan_tiocmget,
|
||||
.tiocmset = usb_wwan_tiocmset,
|
||||
.ioctl = usb_wwan_ioctl,
|
||||
.attach = usb_wwan_startup,
|
||||
.disconnect = usb_wwan_disconnect,
|
||||
.release = usb_wwan_release,
|
||||
|
||||
@@ -79,7 +79,6 @@ struct ssu100_port_private {
|
||||
u8 shadowLSR;
|
||||
u8 shadowMSR;
|
||||
wait_queue_head_t delta_msr_wait; /* Used for TIOCMIWAIT */
|
||||
unsigned short max_packet_size;
|
||||
struct async_icount icount;
|
||||
};
|
||||
|
||||
@@ -464,36 +463,6 @@ static int ssu100_ioctl(struct tty_struct *tty, struct file *file,
|
||||
return -ENOIOCTLCMD;
|
||||
}
|
||||
|
||||
static void ssu100_set_max_packet_size(struct usb_serial_port *port)
|
||||
{
|
||||
struct ssu100_port_private *priv = usb_get_serial_port_data(port);
|
||||
struct usb_serial *serial = port->serial;
|
||||
struct usb_device *udev = serial->dev;
|
||||
|
||||
struct usb_interface *interface = serial->interface;
|
||||
struct usb_endpoint_descriptor *ep_desc = &interface->cur_altsetting->endpoint[1].desc;
|
||||
|
||||
unsigned num_endpoints;
|
||||
int i;
|
||||
unsigned long flags;
|
||||
|
||||
num_endpoints = interface->cur_altsetting->desc.bNumEndpoints;
|
||||
dev_info(&udev->dev, "Number of endpoints %d\n", num_endpoints);
|
||||
|
||||
for (i = 0; i < num_endpoints; i++) {
|
||||
dev_info(&udev->dev, "Endpoint %d MaxPacketSize %d\n", i+1,
|
||||
interface->cur_altsetting->endpoint[i].desc.wMaxPacketSize);
|
||||
ep_desc = &interface->cur_altsetting->endpoint[i].desc;
|
||||
}
|
||||
|
||||
/* set max packet size based on descriptor */
|
||||
spin_lock_irqsave(&priv->status_lock, flags);
|
||||
priv->max_packet_size = ep_desc->wMaxPacketSize;
|
||||
spin_unlock_irqrestore(&priv->status_lock, flags);
|
||||
|
||||
dev_info(&udev->dev, "Setting MaxPacketSize %d\n", priv->max_packet_size);
|
||||
}
|
||||
|
||||
static int ssu100_attach(struct usb_serial *serial)
|
||||
{
|
||||
struct ssu100_port_private *priv;
|
||||
@@ -511,7 +480,6 @@ static int ssu100_attach(struct usb_serial *serial)
|
||||
spin_lock_init(&priv->status_lock);
|
||||
init_waitqueue_head(&priv->delta_msr_wait);
|
||||
usb_set_serial_port_data(port, priv);
|
||||
ssu100_set_max_packet_size(port);
|
||||
|
||||
return ssu100_initdevice(serial->dev);
|
||||
}
|
||||
@@ -641,13 +609,14 @@ static void ssu100_update_lsr(struct usb_serial_port *port, u8 lsr,
|
||||
|
||||
}
|
||||
|
||||
static int ssu100_process_packet(struct tty_struct *tty,
|
||||
struct usb_serial_port *port,
|
||||
struct ssu100_port_private *priv,
|
||||
char *packet, int len)
|
||||
static int ssu100_process_packet(struct urb *urb,
|
||||
struct tty_struct *tty)
|
||||
{
|
||||
int i;
|
||||
struct usb_serial_port *port = urb->context;
|
||||
char *packet = (char *)urb->transfer_buffer;
|
||||
char flag = TTY_NORMAL;
|
||||
u32 len = urb->actual_length;
|
||||
int i;
|
||||
char *ch;
|
||||
|
||||
dbg("%s - port %d", __func__, port->number);
|
||||
@@ -685,12 +654,8 @@ static int ssu100_process_packet(struct tty_struct *tty,
|
||||
static void ssu100_process_read_urb(struct urb *urb)
|
||||
{
|
||||
struct usb_serial_port *port = urb->context;
|
||||
struct ssu100_port_private *priv = usb_get_serial_port_data(port);
|
||||
char *data = (char *)urb->transfer_buffer;
|
||||
struct tty_struct *tty;
|
||||
int count = 0;
|
||||
int i;
|
||||
int len;
|
||||
int count;
|
||||
|
||||
dbg("%s", __func__);
|
||||
|
||||
@@ -698,10 +663,7 @@ static void ssu100_process_read_urb(struct urb *urb)
|
||||
if (!tty)
|
||||
return;
|
||||
|
||||
for (i = 0; i < urb->actual_length; i += priv->max_packet_size) {
|
||||
len = min_t(int, urb->actual_length - i, priv->max_packet_size);
|
||||
count += ssu100_process_packet(tty, port, priv, &data[i], len);
|
||||
}
|
||||
count = ssu100_process_packet(urb, tty);
|
||||
|
||||
if (count)
|
||||
tty_flip_buffer_push(tty);
|
||||
@@ -717,8 +679,6 @@ static struct usb_serial_driver ssu100_device = {
|
||||
.id_table = id_table,
|
||||
.usb_driver = &ssu100_driver,
|
||||
.num_ports = 1,
|
||||
.bulk_in_size = 256,
|
||||
.bulk_out_size = 256,
|
||||
.open = ssu100_open,
|
||||
.close = ssu100_close,
|
||||
.attach = ssu100_attach,
|
||||
|
||||
@@ -18,6 +18,8 @@ extern void usb_wwan_set_termios(struct tty_struct *tty,
|
||||
extern int usb_wwan_tiocmget(struct tty_struct *tty, struct file *file);
|
||||
extern int usb_wwan_tiocmset(struct tty_struct *tty, struct file *file,
|
||||
unsigned int set, unsigned int clear);
|
||||
extern int usb_wwan_ioctl(struct tty_struct *tty, struct file *file,
|
||||
unsigned int cmd, unsigned long arg);
|
||||
extern int usb_wwan_send_setup(struct usb_serial_port *port);
|
||||
extern int usb_wwan_write(struct tty_struct *tty, struct usb_serial_port *port,
|
||||
const unsigned char *buf, int count);
|
||||
|
||||
@@ -31,8 +31,10 @@
|
||||
#include <linux/tty_flip.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/serial.h>
|
||||
#include <linux/serial.h>
|
||||
#include "usb-wwan.h"
|
||||
|
||||
static int debug;
|
||||
@@ -123,6 +125,83 @@ int usb_wwan_tiocmset(struct tty_struct *tty, struct file *file,
|
||||
}
|
||||
EXPORT_SYMBOL(usb_wwan_tiocmset);
|
||||
|
||||
static int get_serial_info(struct usb_serial_port *port,
|
||||
struct serial_struct __user *retinfo)
|
||||
{
|
||||
struct serial_struct tmp;
|
||||
|
||||
if (!retinfo)
|
||||
return -EFAULT;
|
||||
|
||||
memset(&tmp, 0, sizeof(tmp));
|
||||
tmp.line = port->serial->minor;
|
||||
tmp.port = port->number;
|
||||
tmp.baud_base = tty_get_baud_rate(port->port.tty);
|
||||
tmp.close_delay = port->port.close_delay / 10;
|
||||
tmp.closing_wait = port->port.closing_wait == ASYNC_CLOSING_WAIT_NONE ?
|
||||
ASYNC_CLOSING_WAIT_NONE :
|
||||
port->port.closing_wait / 10;
|
||||
|
||||
if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_serial_info(struct usb_serial_port *port,
|
||||
struct serial_struct __user *newinfo)
|
||||
{
|
||||
struct serial_struct new_serial;
|
||||
unsigned int closing_wait, close_delay;
|
||||
int retval = 0;
|
||||
|
||||
if (copy_from_user(&new_serial, newinfo, sizeof(new_serial)))
|
||||
return -EFAULT;
|
||||
|
||||
close_delay = new_serial.close_delay * 10;
|
||||
closing_wait = new_serial.closing_wait == ASYNC_CLOSING_WAIT_NONE ?
|
||||
ASYNC_CLOSING_WAIT_NONE : new_serial.closing_wait * 10;
|
||||
|
||||
mutex_lock(&port->port.mutex);
|
||||
|
||||
if (!capable(CAP_SYS_ADMIN)) {
|
||||
if ((close_delay != port->port.close_delay) ||
|
||||
(closing_wait != port->port.closing_wait))
|
||||
retval = -EPERM;
|
||||
else
|
||||
retval = -EOPNOTSUPP;
|
||||
} else {
|
||||
port->port.close_delay = close_delay;
|
||||
port->port.closing_wait = closing_wait;
|
||||
}
|
||||
|
||||
mutex_unlock(&port->port.mutex);
|
||||
return retval;
|
||||
}
|
||||
|
||||
int usb_wwan_ioctl(struct tty_struct *tty, struct file *file,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct usb_serial_port *port = tty->driver_data;
|
||||
|
||||
dbg("%s cmd 0x%04x", __func__, cmd);
|
||||
|
||||
switch (cmd) {
|
||||
case TIOCGSERIAL:
|
||||
return get_serial_info(port,
|
||||
(struct serial_struct __user *) arg);
|
||||
case TIOCSSERIAL:
|
||||
return set_serial_info(port,
|
||||
(struct serial_struct __user *) arg);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
dbg("%s arg not supported", __func__);
|
||||
|
||||
return -ENOIOCTLCMD;
|
||||
}
|
||||
EXPORT_SYMBOL(usb_wwan_ioctl);
|
||||
|
||||
/* Write */
|
||||
int usb_wwan_write(struct tty_struct *tty, struct usb_serial_port *port,
|
||||
const unsigned char *buf, int count)
|
||||
|
||||
@@ -49,14 +49,17 @@ struct command_iu {
|
||||
__u8 cdb[16]; /* XXX: Overflow-checking tools may misunderstand */
|
||||
};
|
||||
|
||||
/*
|
||||
* Also used for the Read Ready and Write Ready IUs since they have the
|
||||
* same first four bytes
|
||||
*/
|
||||
struct sense_iu {
|
||||
__u8 iu_id;
|
||||
__u8 rsvd1;
|
||||
__be16 tag;
|
||||
__be16 status_qual;
|
||||
__u8 status;
|
||||
__u8 service_response;
|
||||
__u8 rsvd8[6];
|
||||
__u8 rsvd7[7];
|
||||
__be16 len;
|
||||
__u8 sense[SCSI_SENSE_BUFFERSIZE];
|
||||
};
|
||||
@@ -97,8 +100,8 @@ struct uas_dev_info {
|
||||
};
|
||||
|
||||
enum {
|
||||
ALLOC_SENSE_URB = (1 << 0),
|
||||
SUBMIT_SENSE_URB = (1 << 1),
|
||||
ALLOC_STATUS_URB = (1 << 0),
|
||||
SUBMIT_STATUS_URB = (1 << 1),
|
||||
ALLOC_DATA_IN_URB = (1 << 2),
|
||||
SUBMIT_DATA_IN_URB = (1 << 3),
|
||||
ALLOC_DATA_OUT_URB = (1 << 4),
|
||||
@@ -112,7 +115,7 @@ struct uas_cmd_info {
|
||||
unsigned int state;
|
||||
unsigned int stream;
|
||||
struct urb *cmd_urb;
|
||||
struct urb *sense_urb;
|
||||
struct urb *status_urb;
|
||||
struct urb *data_in_urb;
|
||||
struct urb *data_out_urb;
|
||||
struct list_head list;
|
||||
@@ -138,7 +141,7 @@ static void uas_do_work(struct work_struct *work)
|
||||
struct scsi_pointer *scp = (void *)cmdinfo;
|
||||
struct scsi_cmnd *cmnd = container_of(scp,
|
||||
struct scsi_cmnd, SCp);
|
||||
uas_submit_urbs(cmnd, cmnd->device->hostdata, GFP_KERNEL);
|
||||
uas_submit_urbs(cmnd, cmnd->device->hostdata, GFP_NOIO);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,7 +207,7 @@ static void uas_xfer_data(struct urb *urb, struct scsi_cmnd *cmnd,
|
||||
struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
|
||||
int err;
|
||||
|
||||
cmdinfo->state = direction | SUBMIT_SENSE_URB;
|
||||
cmdinfo->state = direction | SUBMIT_STATUS_URB;
|
||||
err = uas_submit_urbs(cmnd, cmnd->device->hostdata, GFP_ATOMIC);
|
||||
if (err) {
|
||||
spin_lock(&uas_work_lock);
|
||||
@@ -294,7 +297,7 @@ static struct urb *uas_alloc_sense_urb(struct uas_dev_info *devinfo, gfp_t gfp,
|
||||
if (!urb)
|
||||
goto out;
|
||||
|
||||
iu = kmalloc(sizeof(*iu), gfp);
|
||||
iu = kzalloc(sizeof(*iu), gfp);
|
||||
if (!iu)
|
||||
goto free;
|
||||
|
||||
@@ -325,7 +328,7 @@ static struct urb *uas_alloc_cmd_urb(struct uas_dev_info *devinfo, gfp_t gfp,
|
||||
if (len < 0)
|
||||
len = 0;
|
||||
len = ALIGN(len, 4);
|
||||
iu = kmalloc(sizeof(*iu) + len, gfp);
|
||||
iu = kzalloc(sizeof(*iu) + len, gfp);
|
||||
if (!iu)
|
||||
goto free;
|
||||
|
||||
@@ -357,21 +360,21 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd,
|
||||
{
|
||||
struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
|
||||
|
||||
if (cmdinfo->state & ALLOC_SENSE_URB) {
|
||||
cmdinfo->sense_urb = uas_alloc_sense_urb(devinfo, gfp, cmnd,
|
||||
cmdinfo->stream);
|
||||
if (!cmdinfo->sense_urb)
|
||||
if (cmdinfo->state & ALLOC_STATUS_URB) {
|
||||
cmdinfo->status_urb = uas_alloc_sense_urb(devinfo, gfp, cmnd,
|
||||
cmdinfo->stream);
|
||||
if (!cmdinfo->status_urb)
|
||||
return SCSI_MLQUEUE_DEVICE_BUSY;
|
||||
cmdinfo->state &= ~ALLOC_SENSE_URB;
|
||||
cmdinfo->state &= ~ALLOC_STATUS_URB;
|
||||
}
|
||||
|
||||
if (cmdinfo->state & SUBMIT_SENSE_URB) {
|
||||
if (usb_submit_urb(cmdinfo->sense_urb, gfp)) {
|
||||
if (cmdinfo->state & SUBMIT_STATUS_URB) {
|
||||
if (usb_submit_urb(cmdinfo->status_urb, gfp)) {
|
||||
scmd_printk(KERN_INFO, cmnd,
|
||||
"sense urb submission failure\n");
|
||||
return SCSI_MLQUEUE_DEVICE_BUSY;
|
||||
}
|
||||
cmdinfo->state &= ~SUBMIT_SENSE_URB;
|
||||
cmdinfo->state &= ~SUBMIT_STATUS_URB;
|
||||
}
|
||||
|
||||
if (cmdinfo->state & ALLOC_DATA_IN_URB) {
|
||||
@@ -440,7 +443,7 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd,
|
||||
|
||||
BUILD_BUG_ON(sizeof(struct uas_cmd_info) > sizeof(struct scsi_pointer));
|
||||
|
||||
if (!cmdinfo->sense_urb && sdev->current_cmnd)
|
||||
if (!cmdinfo->status_urb && sdev->current_cmnd)
|
||||
return SCSI_MLQUEUE_DEVICE_BUSY;
|
||||
|
||||
if (blk_rq_tagged(cmnd->request)) {
|
||||
@@ -452,7 +455,7 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd,
|
||||
|
||||
cmnd->scsi_done = done;
|
||||
|
||||
cmdinfo->state = ALLOC_SENSE_URB | SUBMIT_SENSE_URB |
|
||||
cmdinfo->state = ALLOC_STATUS_URB | SUBMIT_STATUS_URB |
|
||||
ALLOC_CMD_URB | SUBMIT_CMD_URB;
|
||||
|
||||
switch (cmnd->sc_data_direction) {
|
||||
@@ -475,8 +478,8 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd,
|
||||
err = uas_submit_urbs(cmnd, devinfo, GFP_ATOMIC);
|
||||
if (err) {
|
||||
/* If we did nothing, give up now */
|
||||
if (cmdinfo->state & SUBMIT_SENSE_URB) {
|
||||
usb_free_urb(cmdinfo->sense_urb);
|
||||
if (cmdinfo->state & SUBMIT_STATUS_URB) {
|
||||
usb_free_urb(cmdinfo->status_urb);
|
||||
return SCSI_MLQUEUE_DEVICE_BUSY;
|
||||
}
|
||||
spin_lock(&uas_work_lock);
|
||||
@@ -578,6 +581,34 @@ static struct usb_device_id uas_usb_ids[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(usb, uas_usb_ids);
|
||||
|
||||
static int uas_is_interface(struct usb_host_interface *intf)
|
||||
{
|
||||
return (intf->desc.bInterfaceClass == USB_CLASS_MASS_STORAGE &&
|
||||
intf->desc.bInterfaceSubClass == USB_SC_SCSI &&
|
||||
intf->desc.bInterfaceProtocol == USB_PR_UAS);
|
||||
}
|
||||
|
||||
static int uas_switch_interface(struct usb_device *udev,
|
||||
struct usb_interface *intf)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (uas_is_interface(intf->cur_altsetting))
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < intf->num_altsetting; i++) {
|
||||
struct usb_host_interface *alt = &intf->altsetting[i];
|
||||
if (alt == intf->cur_altsetting)
|
||||
continue;
|
||||
if (uas_is_interface(alt))
|
||||
return usb_set_interface(udev,
|
||||
alt->desc.bInterfaceNumber,
|
||||
alt->desc.bAlternateSetting);
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static void uas_configure_endpoints(struct uas_dev_info *devinfo)
|
||||
{
|
||||
struct usb_host_endpoint *eps[4] = { };
|
||||
@@ -651,13 +682,8 @@ static int uas_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
||||
struct uas_dev_info *devinfo;
|
||||
struct usb_device *udev = interface_to_usbdev(intf);
|
||||
|
||||
if (id->bInterfaceProtocol == 0x50) {
|
||||
int ifnum = intf->cur_altsetting->desc.bInterfaceNumber;
|
||||
/* XXX: Shouldn't assume that 1 is the alternative we want */
|
||||
int ret = usb_set_interface(udev, ifnum, 1);
|
||||
if (ret)
|
||||
return -ENODEV;
|
||||
}
|
||||
if (uas_switch_interface(udev, intf))
|
||||
return -ENODEV;
|
||||
|
||||
devinfo = kmalloc(sizeof(struct uas_dev_info), GFP_KERNEL);
|
||||
if (!devinfo)
|
||||
|
||||
Reference in New Issue
Block a user