From 0eadcc09203349b11ca477ec367079b23d32ab91 Mon Sep 17 00:00:00 2001 From: Tatyana Brokhman Date: Mon, 1 Nov 2010 18:18:24 +0200 Subject: usb: USB3.0 ch11 definitions Adding hub SuperSpeed usb definitions as defined by ch10 of the USB3.0 spec. Signed-off-by: Tatyana Brokhman Signed-off-by: Greg Kroah-Hartman --- include/linux/usb/ch11.h | 47 +++++++++++++++++++++++++++++++++++++++++++++++ include/linux/usb/hcd.h | 4 ++++ 2 files changed, 51 insertions(+) (limited to 'include/linux') diff --git a/include/linux/usb/ch11.h b/include/linux/usb/ch11.h index 119194c85d10..10ec0699bea4 100644 --- a/include/linux/usb/ch11.h +++ b/include/linux/usb/ch11.h @@ -27,6 +27,13 @@ #define HUB_GET_TT_STATE 10 #define HUB_STOP_TT 11 +/* + * Hub class additional requests defined by USB 3.0 spec + * See USB 3.0 spec Table 10-6 + */ +#define HUB_SET_DEPTH 12 +#define HUB_GET_PORT_ERR_COUNT 13 + /* * Hub Class feature numbers * See USB 2.0 spec Table 11-17 @@ -55,6 +62,20 @@ #define USB_PORT_FEAT_INDICATOR 22 #define USB_PORT_FEAT_C_PORT_L1 23 +/* + * Port feature selectors added by USB 3.0 spec. + * See USB 3.0 spec Table 10-7 + */ +#define USB_PORT_FEAT_LINK_STATE 5 +#define USB_PORT_FEAT_U1_TIMEOUT 23 +#define USB_PORT_FEAT_U2_TIMEOUT 24 +#define USB_PORT_FEAT_C_LINK_STATE 25 +#define USB_PORT_FEAT_C_CONFIG_ERR 26 +#define USB_PORT_FEAT_REMOTE_WAKE_MASK 27 +#define USB_PORT_FEAT_BH_PORT_RESET 28 +#define USB_PORT_FEAT_C_BH_PORT_RESET 29 +#define USB_PORT_FEAT_FORCE_LINKPM_ACCEPT 30 + /* * Hub Status and Hub Change results * See USB 2.0 spec Table 11-19 and Table 11-20 @@ -83,6 +104,32 @@ struct usb_port_status { /* bits 13 to 15 are reserved */ #define USB_PORT_STAT_SUPER_SPEED 0x8000 /* Linux-internal */ +/* + * Additions to wPortStatus bit field from USB 3.0 + * See USB 3.0 spec Table 10-10 + */ +#define USB_PORT_STAT_LINK_STATE 0x01e0 +#define USB_SS_PORT_STAT_POWER 0x0200 +#define USB_PORT_STAT_SPEED_5GBPS 0x0000 +/* Valid only if port is enabled */ + +/* + * Definitions for PORT_LINK_STATE values + * (bits 5-8) in wPortStatus + */ +#define USB_SS_PORT_LS_U0 0x0000 +#define USB_SS_PORT_LS_U1 0x0020 +#define USB_SS_PORT_LS_U2 0x0040 +#define USB_SS_PORT_LS_U3 0x0060 +#define USB_SS_PORT_LS_SS_DISABLED 0x0080 +#define USB_SS_PORT_LS_RX_DETECT 0x00a0 +#define USB_SS_PORT_LS_SS_INACTIVE 0x00c0 +#define USB_SS_PORT_LS_POLLING 0x00e0 +#define USB_SS_PORT_LS_RECOVERY 0x0100 +#define USB_SS_PORT_LS_HOT_RESET 0x0120 +#define USB_SS_PORT_LS_COMP_MOD 0x0140 +#define USB_SS_PORT_LS_LOOPBACK 0x0160 + /* * wPortChange bit field * See USB 2.0 spec Table 11-22 diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index 0b6e751ea0b1..dd6ee49a0844 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -471,6 +471,10 @@ extern void usb_ep0_reinit(struct usb_device *); /*-------------------------------------------------------------------------*/ +/* class requests from USB 3.0 hub spec, table 10-5 */ +#define SetHubDepth (0x3000 | HUB_SET_DEPTH) +#define GetPortErrorCount (0x8000 | HUB_GET_PORT_ERR_COUNT) + /* * Generic bandwidth allocation constants/support */ -- cgit v1.2.3 From 6ddf27cdbc218a412d7e993fdc08e30eec2042ce Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Mon, 15 Nov 2010 15:57:30 -0500 Subject: USB: make usb_mark_last_busy use pm_runtime_mark_last_busy Since the runtime-PM core already defines a .last_busy field in device.power, this patch uses it to replace the .last_busy field defined in usb_device and uses pm_runtime_mark_last_busy to implement usb_mark_last_busy. Signed-off-by: Ming Lei Reviewed-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/driver.c | 23 +++++++++++------------ drivers/usb/core/hcd-pci.c | 1 - drivers/usb/core/hcd.c | 1 - drivers/usb/core/hub.c | 1 - drivers/usb/core/message.c | 1 - include/linux/usb.h | 5 ++--- 6 files changed, 13 insertions(+), 19 deletions(-) (limited to 'include/linux') diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index eda2d2c25459..0a63e968c683 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -27,7 +27,6 @@ #include #include #include -#include #include "usb.h" @@ -1329,7 +1328,7 @@ 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; + usb_mark_last_busy(udev); do_unbind_rebind(udev, DO_REBIND); } } @@ -1397,7 +1396,7 @@ void usb_autosuspend_device(struct usb_device *udev) { int status; - udev->last_busy = jiffies; + usb_mark_last_busy(udev); status = pm_runtime_put_sync(&udev->dev); dev_vdbg(&udev->dev, "%s: cnt %d -> %d\n", __func__, atomic_read(&udev->dev.power.usage_count), @@ -1482,7 +1481,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", @@ -1512,8 +1511,8 @@ void usb_autopm_put_interface_async(struct usb_interface *intf) unsigned long last_busy; int status = 0; - last_busy = udev->last_busy; - udev->last_busy = jiffies; + last_busy = udev->dev.power.last_busy; + usb_mark_last_busy(udev); atomic_dec(&intf->pm_usage_cnt); pm_runtime_put_noidle(&intf->dev); @@ -1554,7 +1553,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); } @@ -1641,7 +1640,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); } @@ -1697,7 +1696,7 @@ static int autosuspend_check(struct usb_device *udev) * enough, queue a delayed autosuspend request. */ j = ACCESS_ONCE(jiffies); - suspend_time = udev->last_busy + udev->autosuspend_delay; + suspend_time = udev->dev.power.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))); @@ -1725,13 +1724,13 @@ static int usb_runtime_suspend(struct device *dev) * away. */ if (status) { - udev->last_busy = jiffies + + udev->dev.power.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; + usb_mark_last_busy(udev->parent); return status; } @@ -1745,7 +1744,7 @@ static int usb_runtime_resume(struct device *dev) * and all its interfaces. */ status = usb_resume_both(udev, PMSG_AUTO_RESUME); - udev->last_busy = jiffies; + usb_mark_last_busy(udev); return status; } diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index 3799573bd385..b55d46070a25 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 61800f77dac8..e70aeaf3dc1a 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -38,7 +38,6 @@ #include #include #include -#include #include #include diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 27115b45edc5..7c2405eccc4b 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index f377e49fcb30..832487423826 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include /* for usbcore internals */ diff --git a/include/linux/usb.h b/include/linux/usb.h index 35fe6ab222bb..7d22b3340a7f 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -20,6 +20,7 @@ #include /* for struct completion */ #include /* for current && schedule_timeout */ #include /* for struct mutex */ +#include /* for runtime PM */ struct usb_device; struct usb_driver; @@ -407,7 +408,6 @@ struct usb_tt; * @quirks: quirks of the whole device * @urbnum: number of URBs submitted for the whole device * @active_duration: total time device is not suspended - * @last_busy: time of last use * @autosuspend_delay: in jiffies * @connect_time: time device was first connected * @do_remote_wakeup: remote wakeup should be enabled @@ -481,7 +481,6 @@ struct usb_device { unsigned long active_duration; #ifdef CONFIG_PM - unsigned long last_busy; int autosuspend_delay; unsigned long connect_time; @@ -527,7 +526,7 @@ extern void usb_autopm_put_interface_no_suspend(struct usb_interface *intf); static inline void usb_mark_last_busy(struct usb_device *udev) { - udev->last_busy = jiffies; + pm_runtime_mark_last_busy(&udev->dev); } #else -- cgit v1.2.3 From fcc4a01eb8661226e80632327673f67bf6a5840b Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 15 Nov 2010 15:57:51 -0500 Subject: USB: use the runtime-PM autosuspend implementation This patch (as1428) converts USB over to the new runtime-PM core autosuspend framework. One slightly awkward aspect of the conversion is that USB devices will now have two suspend-delay attributes: the old power/autosuspend file and the new power/autosuspend_delay_ms file. One expresses the delay time in seconds and the other in milliseconds, but otherwise they do the same thing. The old attribute can be deprecated and then removed eventually. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- Documentation/usb/power-management.txt | 113 +++++++++++++++++---------------- drivers/usb/core/driver.c | 77 ++-------------------- drivers/usb/core/hub.c | 1 + drivers/usb/core/quirks.c | 6 -- drivers/usb/core/sysfs.c | 34 ++-------- drivers/usb/core/usb.c | 3 +- drivers/usb/core/usb.h | 2 - include/linux/usb.h | 2 - 8 files changed, 71 insertions(+), 167 deletions(-) (limited to 'include/linux') diff --git a/Documentation/usb/power-management.txt b/Documentation/usb/power-management.txt index b29d8e56cf28..c9ffa9ced7ee 100644 --- a/Documentation/usb/power-management.txt +++ b/Documentation/usb/power-management.txt @@ -2,7 +2,7 @@ Alan Stern - December 11, 2009 + October 28, 2010 @@ -107,9 +107,14 @@ allowed to issue dynamic suspends. The user interface for controlling dynamic PM is located in the power/ subdirectory of each USB device's sysfs directory, that is, in /sys/bus/usb/devices/.../power/ where "..." is the device's ID. The -relevant attribute files are: wakeup, control, and autosuspend. -(There may also be a file named "level"; this file was deprecated -as of the 2.6.35 kernel and replaced by the "control" file.) +relevant attribute files are: wakeup, control, and +autosuspend_delay_ms. (There may also be a file named "level"; this +file was deprecated as of the 2.6.35 kernel and replaced by the +"control" file. In 2.6.38 the "autosuspend" file will be deprecated +and replaced by the "autosuspend_delay_ms" file. The only difference +is that the newer file expresses the delay in milliseconds whereas the +older file uses seconds. Confusingly, both files are present in 2.6.37 +but only "autosuspend" works.) power/wakeup @@ -140,33 +145,36 @@ as of the 2.6.35 kernel and replaced by the "control" file.) suspended and autoresume was not allowed. This setting is no longer supported.) - power/autosuspend + power/autosuspend_delay_ms This file contains an integer value, which is the - number of seconds the device should remain idle before - the kernel will autosuspend it (the idle-delay time). - The default is 2. 0 means to autosuspend as soon as - the device becomes idle, and negative values mean - never to autosuspend. You can write a number to the - file to change the autosuspend idle-delay time. - -Writing "-1" to power/autosuspend and writing "on" to power/control do -essentially the same thing -- they both prevent the device from being -autosuspended. Yes, this is a redundancy in the API. + number of milliseconds the device should remain idle + before the kernel will autosuspend it (the idle-delay + time). The default is 2000. 0 means to autosuspend + as soon as the device becomes idle, and negative + values mean never to autosuspend. You can write a + number to the file to change the autosuspend + idle-delay time. + +Writing "-1" to power/autosuspend_delay_ms and writing "on" to +power/control do essentially the same thing -- they both prevent the +device from being autosuspended. Yes, this is a redundancy in the +API. (In 2.6.21 writing "0" to power/autosuspend would prevent the device from being autosuspended; the behavior was changed in 2.6.22. The power/autosuspend attribute did not exist prior to 2.6.21, and the power/level attribute did not exist prior to 2.6.22. power/control -was added in 2.6.34.) +was added in 2.6.34, and power/autosuspend_delay_ms was added in +2.6.37 but did not become functional until 2.6.38.) Changing the default idle-delay time ------------------------------------ -The default autosuspend idle-delay time is controlled by a module -parameter in usbcore. You can specify the value when usbcore is -loaded. For example, to set it to 5 seconds instead of 2 you would +The default autosuspend idle-delay time (in seconds) is controlled by +a module parameter in usbcore. You can specify the value when usbcore +is loaded. For example, to set it to 5 seconds instead of 2 you would do: modprobe usbcore autosuspend=5 @@ -234,25 +242,23 @@ every device. If a driver knows that its device has proper suspend/resume support, it can enable autosuspend all by itself. For example, the video -driver for a laptop's webcam might do this, since these devices are -rarely used and so should normally be autosuspended. +driver for a laptop's webcam might do this (in recent kernels they +do), since these devices are rarely used and so should normally be +autosuspended. Sometimes it turns out that even when a device does work okay with -autosuspend there are still problems. For example, there are -experimental patches adding autosuspend support to the usbhid driver, -which manages keyboards and mice, among other things. Tests with a -number of keyboards showed that typing on a suspended keyboard, while -causing the keyboard to do a remote wakeup all right, would -nonetheless frequently result in lost keystrokes. Tests with mice -showed that some of them would issue a remote-wakeup request in -response to button presses but not to motion, and some in response to -neither. +autosuspend there are still problems. For example, the usbhid driver, +which manages keyboards and mice, has autosuspend support. Tests with +a number of keyboards show that typing on a suspended keyboard, while +causing the keyboard to do a remote wakeup all right, will nonetheless +frequently result in lost keystrokes. Tests with mice show that some +of them will issue a remote-wakeup request in response to button +presses but not to motion, and some in response to neither. The kernel will not prevent you from enabling autosuspend on devices that can't handle it. It is even possible in theory to damage a -device by suspending it at the wrong time -- for example, suspending a -USB hard disk might cause it to spin down without parking the heads. -(Highly unlikely, but possible.) Take care. +device by suspending it at the wrong time. (Highly unlikely, but +possible.) Take care. The driver interface for Power Management @@ -336,10 +342,6 @@ autosuspend the interface's device. When the usage counter is = 0 then the interface is considered to be idle, and the kernel may autosuspend the device. -(There is a similar usage counter field in struct usb_device, -associated with the device itself rather than any of its interfaces. -This counter is used only by the USB core.) - Drivers need not be concerned about balancing changes to the usage counter; the USB core will undo any remaining "get"s when a driver is unbound from its interface. As a corollary, drivers must not call @@ -409,11 +411,11 @@ during autosuspend. For example, there's not much point autosuspending a keyboard if the user can't cause the keyboard to do a remote wakeup by typing on it. If the driver sets intf->needs_remote_wakeup to 1, the kernel won't autosuspend the -device if remote wakeup isn't available or has been disabled through -the power/wakeup attribute. (If the device is already autosuspended, -though, setting this flag won't cause the kernel to autoresume it. -Normally a driver would set this flag in its probe method, at which -time the device is guaranteed not to be autosuspended.) +device if remote wakeup isn't available. (If the device is already +autosuspended, though, setting this flag won't cause the kernel to +autoresume it. Normally a driver would set this flag in its probe +method, at which time the device is guaranteed not to be +autosuspended.) If a driver does its I/O asynchronously in interrupt context, it should call usb_autopm_get_interface_async() before starting output and @@ -422,20 +424,19 @@ it receives an input event, it should call usb_mark_last_busy(struct usb_device *udev); -in the event handler. This sets udev->last_busy to the current time. -udev->last_busy is the field used for idle-delay calculations; -updating it will cause any pending autosuspend to be moved back. Most -of the usb_autopm_* routines will also set the last_busy field to the -current time. +in the event handler. This tells the PM core that the device was just +busy and therefore the next autosuspend idle-delay expiration should +be pushed back. Many of the usb_autopm_* routines also make this call, +so drivers need to worry only when interrupt-driven input arrives. Asynchronous operation is always subject to races. For example, a -driver may call one of the usb_autopm_*_interface_async() routines at -a time when the core has just finished deciding the device has been -idle for long enough but not yet gotten around to calling the driver's -suspend method. The suspend method must be responsible for -synchronizing with the output request routine and the URB completion -handler; it should cause autosuspends to fail with -EBUSY if the -driver needs to use the device. +driver may call the usb_autopm_get_interface_async() routine at a time +when the core has just finished deciding the device has been idle for +long enough but not yet gotten around to calling the driver's suspend +method. The suspend method must be responsible for synchronizing with +the I/O request routine and the URB completion handler; it should +cause autosuspends to fail with -EBUSY if the driver needs to use the +device. External suspend calls should never be allowed to fail in this way, only autosuspend calls. The driver can tell them apart by checking @@ -472,7 +473,9 @@ Firstly, a device may already be autosuspended when a system suspend occurs. Since system suspends are supposed to be as transparent as possible, the device should remain suspended following the system resume. But this theory may not work out well in practice; over time -the kernel's behavior in this regard has changed. +the kernel's behavior in this regard has changed. As of 2.6.37 the +policy is to resume all devices during a system resume and let them +handle their own runtime suspends afterward. Secondly, a dynamic power-management event may occur as a system suspend is underway. The window for this is short, since system diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 0a63e968c683..43c25c29ac1f 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -1397,32 +1397,7 @@ void usb_autosuspend_device(struct usb_device *udev) int status; usb_mark_last_busy(udev); - 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); + 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); @@ -1508,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->dev.power.last_busy; 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); @@ -1651,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. @@ -1691,17 +1644,6 @@ 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->dev.power.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; } @@ -1719,17 +1661,8 @@ static int usb_runtime_suspend(struct device *dev) 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->dev.power.last_busy = jiffies + - (udev->autosuspend_delay == 0 ? HZ/2 : 0); - } - /* Prevent the parent from suspending immediately after */ - else if (udev->parent) + if (status == 0 && udev->parent) usb_mark_last_busy(udev->parent); return status; @@ -1756,7 +1689,7 @@ static int usb_runtime_idle(struct device *dev) * autosuspend checks. */ if (autosuspend_check(udev) == 0) - pm_runtime_suspend(dev); + pm_runtime_autosuspend(dev); return 0; } diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 7c2405eccc4b..fdb62ca10d86 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1803,6 +1803,7 @@ 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_use_autosuspend(&udev->dev); pm_runtime_enable(&udev->dev); err = usb_enumerate_device(udev); /* Read descriptors */ diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 25719da45e33..e3531da16137 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -124,12 +124,6 @@ void usb_detect_quirks(struct usb_device *udev) */ 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 */ diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index 9561e087907d..6781c369ce2d 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -334,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; } diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index fdd4130fbb7d..079cb57bab4f 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -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 diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index cd882203ad34..b975450f403e 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -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; diff --git a/include/linux/usb.h b/include/linux/usb.h index 7d22b3340a7f..5ee2223af08c 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -408,7 +408,6 @@ struct usb_tt; * @quirks: quirks of the whole device * @urbnum: number of URBs submitted for the whole device * @active_duration: total time device is not suspended - * @autosuspend_delay: in jiffies * @connect_time: time device was first connected * @do_remote_wakeup: remote wakeup should be enabled * @reset_resume: needs reset instead of resume @@ -481,7 +480,6 @@ struct usb_device { unsigned long active_duration; #ifdef CONFIG_PM - int autosuspend_delay; unsigned long connect_time; unsigned do_remote_wakeup:1; -- cgit v1.2.3 From 7fc56f0d9908fe140a01387d59954e3d0a2e7744 Mon Sep 17 00:00:00 2001 From: Luo Andy Date: Tue, 23 Nov 2010 10:41:21 +0800 Subject: usb: gadget: langwell_udc: add usb test mode support This patch adds test mode support for Langwell gadget driver. Signed-off-by: Henry Yuan Signed-off-by: Andy Luo Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/langwell_udc.c | 23 +++++++++++++++++++++++ include/linux/usb/ch9.h | 10 ++++++++++ 2 files changed, 33 insertions(+) (limited to 'include/linux') diff --git a/drivers/usb/gadget/langwell_udc.c b/drivers/usb/gadget/langwell_udc.c index b8ec954c0692..777972454e3e 100644 --- a/drivers/usb/gadget/langwell_udc.c +++ b/drivers/usb/gadget/langwell_udc.c @@ -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; diff --git a/include/linux/usb/ch9.h b/include/linux/usb/ch9.h index f917bbbc8901..ab461948b579 100644 --- a/include/linux/usb/ch9.h +++ b/include/linux/usb/ch9.h @@ -123,6 +123,16 @@ #define USB_DEVICE_A_ALT_HNP_SUPPORT 5 /* (otg) other RH port does */ #define USB_DEVICE_DEBUG_MODE 6 /* (special devices only) */ +/* + * Test Mode Selectors + * See USB 2.0 spec Table 9-7 + */ +#define TEST_J 1 +#define TEST_K 2 +#define TEST_SE0_NAK 3 +#define TEST_PACKET 4 +#define TEST_FORCE_EN 5 + /* * New Feature Selectors as added by USB 3.0 * See USB 3.0 spec Table 9-6 -- cgit v1.2.3 From 05ac10dd6862a3fcce33d2203fbb2ef285e3ca87 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 2 Dec 2010 08:49:26 +0200 Subject: usb: musb: trivial search and replace patch change all ocurrences of musb_hdrc to musb-hdrc. We will call glue layer drivers musb-, so in order to keep things somewhat standard, let's change the underscore into a dash. Signed-off-by: Felipe Balbi --- arch/arm/mach-davinci/usb.c | 2 +- arch/arm/mach-omap2/clock2420_data.c | 2 +- arch/arm/mach-omap2/clock2430_data.c | 2 +- arch/arm/mach-omap2/clock3xxx_data.c | 8 ++++---- arch/arm/mach-omap2/clock44xx_data.c | 2 +- arch/arm/mach-omap2/usb-musb.c | 2 +- arch/arm/mach-omap2/usb-tusb6010.c | 2 +- arch/blackfin/mach-bf527/boards/ad7160eval.c | 2 +- arch/blackfin/mach-bf527/boards/cm_bf527.c | 2 +- arch/blackfin/mach-bf527/boards/ezbrd.c | 2 +- arch/blackfin/mach-bf527/boards/ezkit.c | 2 +- arch/blackfin/mach-bf527/boards/tll6527m.c | 2 +- arch/blackfin/mach-bf548/boards/cm_bf548.c | 2 +- arch/blackfin/mach-bf548/boards/ezkit.c | 2 +- drivers/usb/gadget/gadget_chips.h | 2 +- drivers/usb/musb/Kconfig | 2 +- drivers/usb/musb/musb_core.c | 2 +- include/linux/usb/musb.h | 2 +- 18 files changed, 21 insertions(+), 21 deletions(-) (limited to 'include/linux') diff --git a/arch/arm/mach-davinci/usb.c b/arch/arm/mach-davinci/usb.c index b0d6b07431c0..0c7735bc0d19 100644 --- a/arch/arm/mach-davinci/usb.c +++ b/arch/arm/mach-davinci/usb.c @@ -76,7 +76,7 @@ static struct resource usb_resources[] = { static u64 usb_dmamask = DMA_BIT_MASK(32); static struct platform_device usb_dev = { - .name = "musb_hdrc", + .name = "musb-hdrc", .id = -1, .dev = { .platform_data = &usb_data, diff --git a/arch/arm/mach-omap2/clock2420_data.c b/arch/arm/mach-omap2/clock2420_data.c index 21f856252ad8..831e25c89d2d 100644 --- a/arch/arm/mach-omap2/clock2420_data.c +++ b/arch/arm/mach-omap2/clock2420_data.c @@ -1877,7 +1877,7 @@ static struct omap_clk omap2420_clks[] = { CLK("omap-aes", "ick", &aes_ick, CK_242X), CLK(NULL, "pka_ick", &pka_ick, CK_242X), CLK(NULL, "usb_fck", &usb_fck, CK_242X), - CLK("musb_hdrc", "fck", &osc_ck, CK_242X), + CLK("musb-hdrc", "fck", &osc_ck, CK_242X), }; /* diff --git a/arch/arm/mach-omap2/clock2430_data.c b/arch/arm/mach-omap2/clock2430_data.c index e32afcbdfb88..a6bccd7475a0 100644 --- a/arch/arm/mach-omap2/clock2430_data.c +++ b/arch/arm/mach-omap2/clock2430_data.c @@ -1983,7 +1983,7 @@ static struct omap_clk omap2430_clks[] = { CLK("omap-aes", "ick", &aes_ick, CK_243X), CLK(NULL, "pka_ick", &pka_ick, CK_243X), CLK(NULL, "usb_fck", &usb_fck, CK_243X), - CLK("musb_hdrc", "ick", &usbhs_ick, CK_243X), + CLK("musb-hdrc", "ick", &usbhs_ick, CK_243X), CLK("mmci-omap-hs.0", "ick", &mmchs1_ick, CK_243X), CLK("mmci-omap-hs.0", "fck", &mmchs1_fck, CK_243X), CLK("mmci-omap-hs.1", "ick", &mmchs2_ick, CK_243X), diff --git a/arch/arm/mach-omap2/clock3xxx_data.c b/arch/arm/mach-omap2/clock3xxx_data.c index a04cb03db51f..3e668edbf6fe 100644 --- a/arch/arm/mach-omap2/clock3xxx_data.c +++ b/arch/arm/mach-omap2/clock3xxx_data.c @@ -3306,8 +3306,8 @@ static struct omap_clk omap3xxx_clks[] = { CLK(NULL, "ssi_sst_fck", &ssi_sst_fck_3430es1, CK_3430ES1), CLK(NULL, "ssi_sst_fck", &ssi_sst_fck_3430es2, CK_3430ES2), CLK(NULL, "core_l3_ick", &core_l3_ick, CK_3XXX), - CLK("musb_hdrc", "ick", &hsotgusb_ick_3430es1, CK_3430ES1), - CLK("musb_hdrc", "ick", &hsotgusb_ick_3430es2, CK_3430ES2), + CLK("musb-hdrc", "ick", &hsotgusb_ick_3430es1, CK_3430ES1), + CLK("musb-hdrc", "ick", &hsotgusb_ick_3430es2, CK_3430ES2), CLK(NULL, "sdrc_ick", &sdrc_ick, CK_3XXX), CLK(NULL, "gpmc_fck", &gpmc_fck, CK_3XXX), CLK(NULL, "security_l3_ick", &security_l3_ick, CK_343X), @@ -3442,8 +3442,8 @@ static struct omap_clk omap3xxx_clks[] = { CLK("davinci_emac", "phy_clk", &emac_fck, CK_AM35XX), CLK("vpfe-capture", "master", &vpfe_ick, CK_AM35XX), CLK("vpfe-capture", "slave", &vpfe_fck, CK_AM35XX), - CLK("musb_hdrc", "ick", &hsotgusb_ick_am35xx, CK_AM35XX), - CLK("musb_hdrc", "fck", &hsotgusb_fck_am35xx, CK_AM35XX), + CLK("musb-hdrc", "ick", &hsotgusb_ick_am35xx, CK_AM35XX), + CLK("musb-hdrc", "fck", &hsotgusb_fck_am35xx, CK_AM35XX), CLK(NULL, "hecc_ck", &hecc_ck, CK_AM35XX), CLK(NULL, "uart4_ick", &uart4_ick_am35xx, CK_AM35XX), }; diff --git a/arch/arm/mach-omap2/clock44xx_data.c b/arch/arm/mach-omap2/clock44xx_data.c index f473e8922664..95dd34ce6ea1 100644 --- a/arch/arm/mach-omap2/clock44xx_data.c +++ b/arch/arm/mach-omap2/clock44xx_data.c @@ -2953,7 +2953,7 @@ static struct omap_clk omap44xx_clks[] = { CLK("ehci-omap.0", "usbhost_ick", &dummy_ck, CK_443X), CLK(NULL, "otg_60m_gfclk", &otg_60m_gfclk, CK_443X), CLK(NULL, "usb_otg_hs_xclk", &usb_otg_hs_xclk, CK_443X), - CLK("musb_hdrc", "ick", &usb_otg_hs_ick, CK_443X), + CLK("musb-hdrc", "ick", &usb_otg_hs_ick, CK_443X), CLK(NULL, "usb_phy_cm_clk32k", &usb_phy_cm_clk32k, CK_443X), CLK(NULL, "usb_tll_hs_usb_ch2_clk", &usb_tll_hs_usb_ch2_clk, CK_443X), CLK(NULL, "usb_tll_hs_usb_ch0_clk", &usb_tll_hs_usb_ch0_clk, CK_443X), diff --git a/arch/arm/mach-omap2/usb-musb.c b/arch/arm/mach-omap2/usb-musb.c index ed0e85c3764f..7d3ea53af3ab 100644 --- a/arch/arm/mach-omap2/usb-musb.c +++ b/arch/arm/mach-omap2/usb-musb.c @@ -77,7 +77,7 @@ static struct musb_hdrc_platform_data musb_plat = { static u64 musb_dmamask = DMA_BIT_MASK(32); static struct platform_device musb_device = { - .name = "musb_hdrc", + .name = "musb-hdrc", .id = -1, .dev = { .dma_mask = &musb_dmamask, diff --git a/arch/arm/mach-omap2/usb-tusb6010.c b/arch/arm/mach-omap2/usb-tusb6010.c index 64a0112b70a5..42389213b470 100644 --- a/arch/arm/mach-omap2/usb-tusb6010.c +++ b/arch/arm/mach-omap2/usb-tusb6010.c @@ -223,7 +223,7 @@ static struct resource tusb_resources[] = { static u64 tusb_dmamask = ~(u32)0; static struct platform_device tusb_device = { - .name = "musb_hdrc", + .name = "musb-hdrc", .id = -1, .dev = { .dma_mask = &tusb_dmamask, diff --git a/arch/blackfin/mach-bf527/boards/ad7160eval.c b/arch/blackfin/mach-bf527/boards/ad7160eval.c index fc767ac76381..ec9f2612be1d 100644 --- a/arch/blackfin/mach-bf527/boards/ad7160eval.c +++ b/arch/blackfin/mach-bf527/boards/ad7160eval.c @@ -83,7 +83,7 @@ static struct musb_hdrc_platform_data musb_plat = { static u64 musb_dmamask = ~(u32)0; static struct platform_device musb_device = { - .name = "musb_hdrc", + .name = "musb-hdrc", .id = 0, .dev = { .dma_mask = &musb_dmamask, diff --git a/arch/blackfin/mach-bf527/boards/cm_bf527.c b/arch/blackfin/mach-bf527/boards/cm_bf527.c index f714d7be35b9..a7627dee688e 100644 --- a/arch/blackfin/mach-bf527/boards/cm_bf527.c +++ b/arch/blackfin/mach-bf527/boards/cm_bf527.c @@ -120,7 +120,7 @@ static struct musb_hdrc_platform_data musb_plat = { static u64 musb_dmamask = ~(u32)0; static struct platform_device musb_device = { - .name = "musb_hdrc", + .name = "musb-hdrc", .id = 0, .dev = { .dma_mask = &musb_dmamask, diff --git a/arch/blackfin/mach-bf527/boards/ezbrd.c b/arch/blackfin/mach-bf527/boards/ezbrd.c index 315eec930604..d1df634e24c3 100644 --- a/arch/blackfin/mach-bf527/boards/ezbrd.c +++ b/arch/blackfin/mach-bf527/boards/ezbrd.c @@ -84,7 +84,7 @@ static struct musb_hdrc_platform_data musb_plat = { static u64 musb_dmamask = ~(u32)0; static struct platform_device musb_device = { - .name = "musb_hdrc", + .name = "musb-hdrc", .id = 0, .dev = { .dma_mask = &musb_dmamask, diff --git a/arch/blackfin/mach-bf527/boards/ezkit.c b/arch/blackfin/mach-bf527/boards/ezkit.c index 273731279740..5983abd8a7e3 100644 --- a/arch/blackfin/mach-bf527/boards/ezkit.c +++ b/arch/blackfin/mach-bf527/boards/ezkit.c @@ -124,7 +124,7 @@ static struct musb_hdrc_platform_data musb_plat = { static u64 musb_dmamask = ~(u32)0; static struct platform_device musb_device = { - .name = "musb_hdrc", + .name = "musb-hdrc", .id = 0, .dev = { .dma_mask = &musb_dmamask, diff --git a/arch/blackfin/mach-bf527/boards/tll6527m.c b/arch/blackfin/mach-bf527/boards/tll6527m.c index 9ec575729e2c..284ec1f44131 100644 --- a/arch/blackfin/mach-bf527/boards/tll6527m.c +++ b/arch/blackfin/mach-bf527/boards/tll6527m.c @@ -91,7 +91,7 @@ static struct musb_hdrc_platform_data musb_plat = { static u64 musb_dmamask = ~(u32)0; static struct platform_device musb_device = { - .name = "musb_hdrc", + .name = "musb-hdrc", .id = 0, .dev = { .dma_mask = &musb_dmamask, diff --git a/arch/blackfin/mach-bf548/boards/cm_bf548.c b/arch/blackfin/mach-bf548/boards/cm_bf548.c index 3e3dfb23f94e..f52334519a20 100644 --- a/arch/blackfin/mach-bf548/boards/cm_bf548.c +++ b/arch/blackfin/mach-bf548/boards/cm_bf548.c @@ -520,7 +520,7 @@ static struct musb_hdrc_platform_data musb_plat = { static u64 musb_dmamask = ~(u32)0; static struct platform_device musb_device = { - .name = "musb_hdrc", + .name = "musb-hdrc", .id = 0, .dev = { .dma_mask = &musb_dmamask, diff --git a/arch/blackfin/mach-bf548/boards/ezkit.c b/arch/blackfin/mach-bf548/boards/ezkit.c index 9ff166d6f00d..e2c851bef99a 100644 --- a/arch/blackfin/mach-bf548/boards/ezkit.c +++ b/arch/blackfin/mach-bf548/boards/ezkit.c @@ -625,7 +625,7 @@ static struct musb_hdrc_platform_data musb_plat = { static u64 musb_dmamask = ~(u32)0; static struct platform_device musb_device = { - .name = "musb_hdrc", + .name = "musb-hdrc", .id = 0, .dev = { .dma_mask = &musb_dmamask, diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h index d7b3bbe56808..aeb109bd317d 100644 --- a/drivers/usb/gadget/gadget_chips.h +++ b/drivers/usb/gadget/gadget_chips.h @@ -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 diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig index 7119d900a479..03a42901922c 100644 --- a/drivers/usb/musb/Kconfig +++ b/drivers/usb/musb/Kconfig @@ -30,7 +30,7 @@ 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". choice prompt "Platform Glue Layer" diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 8df1c583f19d..1ca14f90c994 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -126,7 +126,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); diff --git a/include/linux/usb/musb.h b/include/linux/usb/musb.h index ee2dd1d506ed..e2191d4db4dd 100644 --- a/include/linux/usb/musb.h +++ b/include/linux/usb/musb.h @@ -3,7 +3,7 @@ * Inventra (Multidrop) Highspeed Dual-Role Controllers: (M)HDRC. * * Board initialization should put one of these into dev->platform_data, - * probably on some platform_device named "musb_hdrc". It encapsulates + * probably on some platform_device named "musb-hdrc". It encapsulates * key configuration differences between boards. */ -- cgit v1.2.3 From f7ec94370f417fedad4db1054228ef958d48b926 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 2 Dec 2010 09:48:58 +0200 Subject: usb: musb: pass platform_ops via platform_data ... then we don't need to export any symbols from glue layer to musb_core. Signed-off-by: Felipe Balbi --- drivers/usb/musb/am35x.c | 4 +++- drivers/usb/musb/blackfin.c | 4 +++- drivers/usb/musb/da8xx.c | 4 +++- drivers/usb/musb/davinci.c | 4 +++- drivers/usb/musb/musb_core.c | 2 +- drivers/usb/musb/musb_core.h | 2 -- drivers/usb/musb/omap2430.c | 4 +++- drivers/usb/musb/tusb6010.c | 4 +++- include/linux/usb/musb.h | 3 +++ 9 files changed, 22 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/drivers/usb/musb/am35x.c b/drivers/usb/musb/am35x.c index 355883c5ffed..e372c87f37e2 100644 --- a/drivers/usb/musb/am35x.c +++ b/drivers/usb/musb/am35x.c @@ -530,7 +530,7 @@ void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst) } } -const struct musb_platform_ops musb_ops = { +static const struct musb_platform_ops am35x_ops = { .init = am35x_musb_init, .exit = am35x_musb_exit, @@ -572,6 +572,8 @@ static int __init am35x_probe(struct platform_device *pdev) glue->dev = &pdev->dev; glue->musb = musb; + pdata->platform_ops = &am35x_ops; + platform_set_drvdata(pdev, glue); ret = platform_device_add_resources(musb, pdev->resource, diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c index 02eded21d171..03cb001c0e19 100644 --- a/drivers/usb/musb/blackfin.c +++ b/drivers/usb/musb/blackfin.c @@ -436,7 +436,7 @@ static int bfin_musb_exit(struct musb *musb) return 0; } -const struct musb_platform_ops musb_ops = { +static const struct musb_platform_ops bfin_ops = { .init = bfin_musb_init, .exit = bfin_musb_exit, @@ -479,6 +479,8 @@ static int __init bfin_probe(struct platform_device *pdev) 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, diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c index 94ddfe01d673..45ccac3aad9d 100644 --- a/drivers/usb/musb/da8xx.c +++ b/drivers/usb/musb/da8xx.c @@ -475,7 +475,7 @@ static int da8xx_musb_exit(struct musb *musb) return 0; } -const struct musb_platform_ops musb_ops = { +static const struct musb_platform_ops da8xx_ops = { .init = da8xx_musb_init, .exit = da8xx_musb_exit, @@ -517,6 +517,8 @@ static int __init da8xx_probe(struct platform_device *pdev) glue->dev = &pdev->dev; glue->musb = musb; + pdata->platform_ops = &da8xx_ops; + platform_set_drvdata(pdev, glue); ret = platform_device_add_resources(musb, pdev->resource, diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c index 661870a1cd4d..831a04dd5a53 100644 --- a/drivers/usb/musb/davinci.c +++ b/drivers/usb/musb/davinci.c @@ -510,7 +510,7 @@ static int davinci_musb_exit(struct musb *musb) return 0; } -const struct musb_platform_ops musb_ops = { +static const struct musb_platform_ops davinci_ops = { .init = davinci_musb_init, .exit = davinci_musb_exit, @@ -551,6 +551,8 @@ static int __init davinci_probe(struct platform_device *pdev) glue->dev = &pdev->dev; glue->musb = musb; + pdata->platform_ops = &davinci_ops; + platform_set_drvdata(pdev, glue); ret = platform_device_add_resources(musb, pdev->resource, diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 1ca14f90c994..dcc77ef6cfff 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -1897,7 +1897,6 @@ allocate_instance(struct device *dev, } musb->controller = dev; - musb->ops = &musb_ops; return musb; } @@ -1997,6 +1996,7 @@ bad_config: musb->board_set_power = plat->set_power; musb->set_clock = plat->set_clock; musb->min_power = plat->min_power; + musb->ops = plat->platform_ops; /* Clock usage is chip-specific ... functional clock (DaVinci, * OMAP2430), or PHY ref (some TUSB6010 boards). All this core diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index 9594b9d1d27b..fac1eab3c59f 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -281,8 +281,6 @@ struct musb_platform_ops { void (*set_vbus)(struct musb *musb, int on); }; -extern const struct musb_platform_ops musb_ops; - /* * struct musb_hw_ep - endpoint hardware (bidirectional) * diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index bca9df7557a4..2eea1703e630 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -333,7 +333,7 @@ static int omap2430_musb_exit(struct musb *musb) return 0; } -const struct musb_platform_ops musb_ops = { +static const struct musb_platform_ops omap2430_ops = { .init = omap2430_musb_init, .exit = omap2430_musb_exit, @@ -378,6 +378,8 @@ static int __init omap2430_probe(struct platform_device *pdev) glue->dev = &pdev->dev; glue->musb = musb; + pdata->platform_ops = &omap2430_ops; + platform_set_drvdata(pdev, glue); ret = platform_device_add_resources(musb, pdev->resource, diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c index 2ff78d6b2eff..d6b832641c53 100644 --- a/drivers/usb/musb/tusb6010.c +++ b/drivers/usb/musb/tusb6010.c @@ -1178,7 +1178,7 @@ static int tusb_musb_exit(struct musb *musb) return 0; } -const struct musb_platform_ops musb_ops = { +static const struct musb_platform_ops tusb_ops = { .init = tusb_musb_init, .exit = tusb_musb_exit, @@ -1221,6 +1221,8 @@ static int __init tusb_probe(struct platform_device *pdev) 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, diff --git a/include/linux/usb/musb.h b/include/linux/usb/musb.h index e2191d4db4dd..6f4e5014cf5e 100644 --- a/include/linux/usb/musb.h +++ b/include/linux/usb/musb.h @@ -126,6 +126,9 @@ struct musb_hdrc_platform_data { /* Architecture specific board data */ void *board_data; + + /* Platform specific struct musb_ops pointer */ + const void *platform_ops; }; -- cgit v1.2.3 From 3b7029670d39d22f288ece95254e9ba5ceddd6ba Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 2 Dec 2010 09:51:00 +0200 Subject: usb: musb: mark ->set_clock deprecated ... we will completely drop that need by moving clock handling to platform glue layer. Marking as deprecated will allow us to catch all users easily. Signed-off-by: Felipe Balbi --- include/linux/usb/musb.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/usb/musb.h b/include/linux/usb/musb.h index 6f4e5014cf5e..0b72b5741645 100644 --- a/include/linux/usb/musb.h +++ b/include/linux/usb/musb.h @@ -119,7 +119,7 @@ struct musb_hdrc_platform_data { int (*set_power)(int state); /* Turn device clock on or off */ - int (*set_clock)(struct clk *clock, int is_on); + int (*set_clock)(struct clk *clock, int is_on) __deprecated; /* MUSB configuration-specific details */ struct musb_hdrc_config *config; -- cgit v1.2.3 From fa56df915d101770a495569473b4c13b1904087b Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 2 Dec 2010 10:55:29 +0200 Subject: usb: musb: drop the set_clock magic now that platform glue layer handles clock completely, that function is completely useless for us. Drop it. Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_core.h | 2 -- include/linux/usb/musb.h | 3 --- 2 files changed, 5 deletions(-) (limited to 'include/linux') diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index 6c8e9630fb19..1e538675ab86 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -455,8 +455,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; diff --git a/include/linux/usb/musb.h b/include/linux/usb/musb.h index 0b72b5741645..0b0e383035e5 100644 --- a/include/linux/usb/musb.h +++ b/include/linux/usb/musb.h @@ -118,9 +118,6 @@ struct musb_hdrc_platform_data { /* Power the device on or off */ int (*set_power)(int state); - /* Turn device clock on or off */ - int (*set_clock)(struct clk *clock, int is_on) __deprecated; - /* MUSB configuration-specific details */ struct musb_hdrc_config *config; -- cgit v1.2.3 From e70357e35c522776d9f56f6800af4ed7a5bdbaaf Mon Sep 17 00:00:00 2001 From: Hema HK Date: Fri, 10 Dec 2010 18:09:52 +0530 Subject: mfd: TWL6030: OMAP4: Registering the TWL6030-usb device Registering the twl6030-usb transceiver device as a child to twl6030 core. Removed the NOP transceiver init call from board file. Populated twl4030_usb_data platform data structure with the function pointers for OMAP4430 internal PHY operation to be used by twl630-usb driver. Signed-off-by: Hema HK Cc: Samuel Ortiz Cc: Tony Lindgren Signed-off-by: Felipe Balbi --- arch/arm/mach-omap2/board-4430sdp.c | 11 ++++++--- arch/arm/mach-omap2/board-omap4panda.c | 8 +++++++ drivers/mfd/twl-core.c | 44 ++++++++++++++++++++++++++++++---- include/linux/i2c/twl.h | 7 ++++++ 4 files changed, 62 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/arch/arm/mach-omap2/board-4430sdp.c b/arch/arm/mach-omap2/board-4430sdp.c index 288ff4415757..95bdea82ece6 100644 --- a/arch/arm/mach-omap2/board-4430sdp.c +++ b/arch/arm/mach-omap2/board-4430sdp.c @@ -242,6 +242,13 @@ static struct omap_musb_board_data musb_board_data = { .power = 100, }; +static struct twl4030_usb_data omap4_usbphy_data = { + .phy_init = omap4430_phy_init, + .phy_exit = omap4430_phy_exit, + .phy_power = omap4430_phy_power, + .phy_set_clock = omap4430_phy_set_clk, +}; + static struct omap2_hsmmc_info mmc[] = { { .mmc = 1, @@ -461,6 +468,7 @@ static struct twl4030_platform_data sdp4430_twldata = { .vaux1 = &sdp4430_vaux1, .vaux2 = &sdp4430_vaux2, .vaux3 = &sdp4430_vaux3, + .usb = &omap4_usbphy_data }; static struct i2c_board_info __initdata sdp4430_i2c_boardinfo[] = { @@ -533,9 +541,6 @@ static void __init omap_4430sdp_init(void) gpio_direction_output(OMAP4SDP_MDM_PWR_EN_GPIO, 1); } usb_ehci_init(&ehci_pdata); - - /* OMAP4 SDP uses internal transceiver so register nop transceiver */ - usb_nop_xceiv_register(); usb_musb_init(&musb_board_data); status = omap_ethernet_init(); diff --git a/arch/arm/mach-omap2/board-omap4panda.c b/arch/arm/mach-omap2/board-omap4panda.c index 3bf644deb8b8..585b2e387341 100644 --- a/arch/arm/mach-omap2/board-omap4panda.c +++ b/arch/arm/mach-omap2/board-omap4panda.c @@ -137,6 +137,13 @@ static struct omap_musb_board_data musb_board_data = { .power = 100, }; +static struct twl4030_usb_data omap4_usbphy_data = { + .phy_init = omap4430_phy_init, + .phy_exit = omap4430_phy_exit, + .phy_power = omap4430_phy_power, + .phy_set_clock = omap4430_phy_set_clk, +}; + static struct omap2_hsmmc_info mmc[] = { { .mmc = 1, @@ -345,6 +352,7 @@ static struct twl4030_platform_data omap4_panda_twldata = { .vaux1 = &omap4_panda_vaux1, .vaux2 = &omap4_panda_vaux2, .vaux3 = &omap4_panda_vaux3, + .usb = &omap4_usbphy_data, }; static struct i2c_board_info __initdata omap4_panda_i2c_boardinfo[] = { diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index 35275ba7096f..12abd5b924b3 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -95,7 +95,8 @@ #define twl_has_rtc() false #endif -#if defined(CONFIG_TWL4030_USB) || defined(CONFIG_TWL4030_USB_MODULE) +#if defined(CONFIG_TWL4030_USB) || defined(CONFIG_TWL4030_USB_MODULE) ||\ + defined(CONFIG_TWL6030_USB) || defined(CONFIG_TWL6030_USB_MODULE) #define twl_has_usb() true #else #define twl_has_usb() false @@ -682,6 +683,43 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features) usb3v1.dev = child; } } + if (twl_has_usb() && pdata->usb && twl_class_is_6030()) { + + static struct regulator_consumer_supply usb3v3 = { + .supply = "vusb", + }; + + if (twl_has_regulator()) { + /* this is a template that gets copied */ + struct regulator_init_data usb_fixed = { + .constraints.valid_modes_mask = + REGULATOR_MODE_NORMAL + | REGULATOR_MODE_STANDBY, + .constraints.valid_ops_mask = + REGULATOR_CHANGE_MODE + | REGULATOR_CHANGE_STATUS, + }; + + child = add_regulator_linked(TWL6030_REG_VUSB, + &usb_fixed, &usb3v3, 1); + if (IS_ERR(child)) + return PTR_ERR(child); + } + + child = add_child(0, "twl6030_usb", + pdata->usb, sizeof(*pdata->usb), + true, + /* irq1 = VBUS_PRES, irq0 = USB ID */ + pdata->irq_base + USBOTG_INTR_OFFSET, + pdata->irq_base + USB_PRES_INTR_OFFSET); + + if (IS_ERR(child)) + return PTR_ERR(child); + /* we need to connect regulators to this transceiver */ + if (twl_has_regulator() && child) + usb3v3.dev = child; + + } if (twl_has_watchdog()) { child = add_child(0, "twl4030_wdt", NULL, 0, false, 0, 0); @@ -815,10 +853,6 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features) if (IS_ERR(child)) return PTR_ERR(child); - child = add_regulator(TWL6030_REG_VUSB, pdata->vusb); - if (IS_ERR(child)) - return PTR_ERR(child); - child = add_regulator(TWL6030_REG_VAUX1_6030, pdata->vaux1); if (IS_ERR(child)) return PTR_ERR(child); diff --git a/include/linux/i2c/twl.h b/include/linux/i2c/twl.h index c760991b354a..61b9609e55f2 100644 --- a/include/linux/i2c/twl.h +++ b/include/linux/i2c/twl.h @@ -593,6 +593,13 @@ enum twl4030_usb_mode { struct twl4030_usb_data { enum twl4030_usb_mode usb_mode; + + int (*phy_init)(struct device *dev); + int (*phy_exit)(struct device *dev); + /* Power on/off the PHY */ + int (*phy_power)(struct device *dev, int iD, int on); + /* enable/disable phy clocks */ + int (*phy_set_clock)(struct device *dev, int on); }; struct twl4030_ins { -- cgit v1.2.3 From 352a337832774a6929c16b569abe9cedc3db01cc Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 9 Dec 2010 22:46:29 +0100 Subject: USB: otg: fix link breakage, when the NOP USB Xceiver is a module If the NOP USB OTG transceiver driver is built as a module, the otg.h header declares external functions, but if they are referenced from the kernel proper, as, e.g., in the OMAP3 case, where the omap3evm board is calling the usb_nop_xceiv_register() function, linkage breaks. This patch fixes this problem. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Greg Kroah-Hartman --- include/linux/usb/otg.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h index 0a5b3711e502..a1a1e7a73ec9 100644 --- a/include/linux/usb/otg.h +++ b/include/linux/usb/otg.h @@ -116,7 +116,7 @@ struct otg_transceiver { /* for board-specific init logic */ extern int otg_set_transceiver(struct otg_transceiver *); -#if defined(CONFIG_NOP_USB_XCEIV) || defined(CONFIG_NOP_USB_XCEIV_MODULE) +#if defined(CONFIG_NOP_USB_XCEIV) || (defined(CONFIG_NOP_USB_XCEIV_MODULE) && defined(MODULE)) /* sometimes transceivers are accessed only through e.g. ULPI */ extern void usb_nop_xceiv_register(void); extern void usb_nop_xceiv_unregister(void); -- cgit v1.2.3 From e0c201f339fe7fc38d1b0f6f4755ff627686c7e0 Mon Sep 17 00:00:00 2001 From: Pavankumar Kondeti Date: Tue, 7 Dec 2010 17:53:55 +0530 Subject: USB: Add MSM OTG Controller driver This driver implements PHY initialization, clock management, ULPI IO ops and simple OTG state machine to kick host/peripheral based on Id/VBUS line status. VBUS/Id lines are tied to a reference voltage on some boards. Hence provide debugfs interface to select host/peripheral mode. Signed-off-by: Pavankumar Kondeti Signed-off-by: Greg Kroah-Hartman --- drivers/usb/otg/Kconfig | 10 + drivers/usb/otg/Makefile | 1 + drivers/usb/otg/msm72k_otg.c | 850 +++++++++++++++++++++++++++++++++++++++ include/linux/usb/msm_hsusb.h | 108 +++++ include/linux/usb/msm_hsusb_hw.h | 56 +++ 5 files changed, 1025 insertions(+) create mode 100644 drivers/usb/otg/msm72k_otg.c create mode 100644 include/linux/usb/msm_hsusb.h create mode 100644 include/linux/usb/msm_hsusb_hw.h (limited to 'include/linux') diff --git a/drivers/usb/otg/Kconfig b/drivers/usb/otg/Kconfig index 5ce07528cd0c..915c729872f8 100644 --- a/drivers/usb/otg/Kconfig +++ b/drivers/usb/otg/Kconfig @@ -81,4 +81,14 @@ 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. This driver is required + even for peripheral only or host only mode configuration. + endif # USB || OTG diff --git a/drivers/usb/otg/Makefile b/drivers/usb/otg/Makefile index 66f1b83e4fa7..3b1b0960fb68 100644 --- a/drivers/usb/otg/Makefile +++ b/drivers/usb/otg/Makefile @@ -15,3 +15,4 @@ obj-$(CONFIG_TWL4030_USB) += twl4030-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 diff --git a/drivers/usb/otg/msm72k_otg.c b/drivers/usb/otg/msm72k_otg.c new file mode 100644 index 000000000000..46f468a912f4 --- /dev/null +++ b/drivers/usb/otg/msm72k_otg.c @@ -0,0 +1,850 @@ +/* Copyright (c) 2009-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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#define MSM_USB_BASE (motg->regs) +#define DRIVER_NAME "msm_otg" + +#define ULPI_IO_TIMEOUT_USEC (10 * 1000) +static int ulpi_read(struct otg_transceiver *otg, u32 reg) +{ + struct msm_otg *motg = container_of(otg, struct msm_otg, otg); + int cnt = 0; + + /* initiate read operation */ + writel(ULPI_RUN | ULPI_READ | ULPI_ADDR(reg), + USB_ULPI_VIEWPORT); + + /* wait for completion */ + while (cnt < ULPI_IO_TIMEOUT_USEC) { + if (!(readl(USB_ULPI_VIEWPORT) & ULPI_RUN)) + break; + udelay(1); + cnt++; + } + + if (cnt >= ULPI_IO_TIMEOUT_USEC) { + dev_err(otg->dev, "ulpi_read: timeout %08x\n", + readl(USB_ULPI_VIEWPORT)); + return -ETIMEDOUT; + } + return ULPI_DATA_READ(readl(USB_ULPI_VIEWPORT)); +} + +static int ulpi_write(struct otg_transceiver *otg, u32 val, u32 reg) +{ + struct msm_otg *motg = container_of(otg, struct msm_otg, otg); + int cnt = 0; + + /* initiate write operation */ + writel(ULPI_RUN | ULPI_WRITE | + ULPI_ADDR(reg) | ULPI_DATA(val), + USB_ULPI_VIEWPORT); + + /* wait for completion */ + while (cnt < ULPI_IO_TIMEOUT_USEC) { + if (!(readl(USB_ULPI_VIEWPORT) & ULPI_RUN)) + break; + udelay(1); + cnt++; + } + + if (cnt >= ULPI_IO_TIMEOUT_USEC) { + dev_err(otg->dev, "ulpi_write: timeout\n"); + return -ETIMEDOUT; + } + return 0; +} + +static struct otg_io_access_ops msm_otg_io_ops = { + .read = ulpi_read, + .write = ulpi_write, +}; + +static void ulpi_init(struct msm_otg *motg) +{ + struct msm_otg_platform_data *pdata = motg->pdata; + int *seq = pdata->phy_init_seq; + + if (!seq) + return; + + while (seq[0] >= 0) { + dev_vdbg(motg->otg.dev, "ulpi: write 0x%02x to 0x%02x\n", + seq[0], seq[1]); + ulpi_write(&motg->otg, seq[0], seq[1]); + seq += 2; + } +} + +static int msm_otg_link_clk_reset(struct msm_otg *motg, bool assert) +{ + int ret; + + if (assert) { + ret = clk_reset(motg->clk, CLK_RESET_ASSERT); + if (ret) + dev_err(motg->otg.dev, "usb hs_clk assert failed\n"); + } else { + ret = clk_reset(motg->clk, CLK_RESET_DEASSERT); + if (ret) + dev_err(motg->otg.dev, "usb hs_clk deassert failed\n"); + } + return ret; +} + +static int msm_otg_phy_clk_reset(struct msm_otg *motg) +{ + int ret; + + ret = clk_reset(motg->phy_reset_clk, CLK_RESET_ASSERT); + if (ret) { + dev_err(motg->otg.dev, "usb phy clk assert failed\n"); + return ret; + } + usleep_range(10000, 12000); + ret = clk_reset(motg->phy_reset_clk, CLK_RESET_DEASSERT); + if (ret) + dev_err(motg->otg.dev, "usb phy clk deassert failed\n"); + return ret; +} + +static int msm_otg_phy_reset(struct msm_otg *motg) +{ + u32 val; + int ret; + int retries; + + ret = msm_otg_link_clk_reset(motg, 1); + if (ret) + return ret; + ret = msm_otg_phy_clk_reset(motg); + if (ret) + return ret; + ret = msm_otg_link_clk_reset(motg, 0); + if (ret) + return ret; + + val = readl(USB_PORTSC) & ~PORTSC_PTS_MASK; + writel(val | PORTSC_PTS_ULPI, USB_PORTSC); + + for (retries = 3; retries > 0; retries--) { + ret = ulpi_write(&motg->otg, ULPI_FUNC_CTRL_SUSPENDM, + ULPI_CLR(ULPI_FUNC_CTRL)); + if (!ret) + break; + ret = msm_otg_phy_clk_reset(motg); + if (ret) + return ret; + } + if (!retries) + return -ETIMEDOUT; + + /* This reset calibrates the phy, if the above write succeeded */ + ret = msm_otg_phy_clk_reset(motg); + if (ret) + return ret; + + for (retries = 3; retries > 0; retries--) { + ret = ulpi_read(&motg->otg, ULPI_DEBUG); + if (ret != -ETIMEDOUT) + break; + ret = msm_otg_phy_clk_reset(motg); + if (ret) + return ret; + } + if (!retries) + return -ETIMEDOUT; + + dev_info(motg->otg.dev, "phy_reset: success\n"); + return 0; +} + +#define LINK_RESET_TIMEOUT_USEC (250 * 1000) +static int msm_otg_reset(struct otg_transceiver *otg) +{ + struct msm_otg *motg = container_of(otg, struct msm_otg, otg); + struct msm_otg_platform_data *pdata = motg->pdata; + int cnt = 0; + int ret; + u32 val = 0; + u32 ulpi_val = 0; + + ret = msm_otg_phy_reset(motg); + if (ret) { + dev_err(otg->dev, "phy_reset failed\n"); + return ret; + } + + ulpi_init(motg); + + writel(USBCMD_RESET, USB_USBCMD); + while (cnt < LINK_RESET_TIMEOUT_USEC) { + if (!(readl(USB_USBCMD) & USBCMD_RESET)) + break; + udelay(1); + cnt++; + } + if (cnt >= LINK_RESET_TIMEOUT_USEC) + return -ETIMEDOUT; + + /* select ULPI phy */ + writel(0x80000000, USB_PORTSC); + + msleep(100); + + writel(0x0, USB_AHBBURST); + writel(0x00, USB_AHBMODE); + + if (pdata->otg_control == OTG_PHY_CONTROL) { + val = readl(USB_OTGSC); + if (pdata->mode == USB_OTG) { + ulpi_val = ULPI_INT_IDGRD | ULPI_INT_SESS_VALID; + val |= OTGSC_IDIE | OTGSC_BSVIE; + } else if (pdata->mode == USB_PERIPHERAL) { + ulpi_val = ULPI_INT_SESS_VALID; + val |= OTGSC_BSVIE; + } + writel(val, USB_OTGSC); + ulpi_write(otg, ulpi_val, ULPI_USB_INT_EN_RISE); + ulpi_write(otg, ulpi_val, ULPI_USB_INT_EN_FALL); + } + + return 0; +} + +static void msm_otg_start_host(struct otg_transceiver *otg, int on) +{ + struct msm_otg *motg = container_of(otg, struct msm_otg, otg); + struct msm_otg_platform_data *pdata = motg->pdata; + struct usb_hcd *hcd; + + if (!otg->host) + return; + + hcd = bus_to_hcd(otg->host); + + if (on) { + dev_dbg(otg->dev, "host on\n"); + + if (pdata->vbus_power) + pdata->vbus_power(1); + /* + * Some boards have a switch cotrolled by gpio + * to enable/disable internal HUB. Enable internal + * HUB before kicking the host. + */ + if (pdata->setup_gpio) + pdata->setup_gpio(OTG_STATE_A_HOST); +#ifdef CONFIG_USB + usb_add_hcd(hcd, hcd->irq, IRQF_SHARED); +#endif + } else { + dev_dbg(otg->dev, "host off\n"); + +#ifdef CONFIG_USB + usb_remove_hcd(hcd); +#endif + if (pdata->setup_gpio) + pdata->setup_gpio(OTG_STATE_UNDEFINED); + if (pdata->vbus_power) + pdata->vbus_power(0); + } +} + +static int msm_otg_set_host(struct otg_transceiver *otg, struct usb_bus *host) +{ + struct msm_otg *motg = container_of(otg, struct msm_otg, otg); + struct usb_hcd *hcd; + + /* + * Fail host registration if this board can support + * only peripheral configuration. + */ + if (motg->pdata->mode == USB_PERIPHERAL) { + dev_info(otg->dev, "Host mode is not supported\n"); + return -ENODEV; + } + + if (!host) { + if (otg->state == OTG_STATE_A_HOST) { + msm_otg_start_host(otg, 0); + otg->host = NULL; + otg->state = OTG_STATE_UNDEFINED; + schedule_work(&motg->sm_work); + } else { + otg->host = NULL; + } + + return 0; + } + + hcd = bus_to_hcd(host); + hcd->power_budget = motg->pdata->power_budget; + + otg->host = host; + dev_dbg(otg->dev, "host driver registered w/ tranceiver\n"); + + /* + * Kick the state machine work, if peripheral is not supported + * or peripheral is already registered with us. + */ + if (motg->pdata->mode == USB_HOST || otg->gadget) + schedule_work(&motg->sm_work); + + return 0; +} + +static void msm_otg_start_peripheral(struct otg_transceiver *otg, int on) +{ + struct msm_otg *motg = container_of(otg, struct msm_otg, otg); + struct msm_otg_platform_data *pdata = motg->pdata; + + if (!otg->gadget) + return; + + if (on) { + dev_dbg(otg->dev, "gadget on\n"); + /* + * Some boards have a switch cotrolled by gpio + * to enable/disable internal HUB. Disable internal + * HUB before kicking the gadget. + */ + if (pdata->setup_gpio) + pdata->setup_gpio(OTG_STATE_B_PERIPHERAL); + usb_gadget_vbus_connect(otg->gadget); + } else { + dev_dbg(otg->dev, "gadget off\n"); + usb_gadget_vbus_disconnect(otg->gadget); + if (pdata->setup_gpio) + pdata->setup_gpio(OTG_STATE_UNDEFINED); + } + +} + +static int msm_otg_set_peripheral(struct otg_transceiver *otg, + struct usb_gadget *gadget) +{ + struct msm_otg *motg = container_of(otg, struct msm_otg, otg); + + /* + * Fail peripheral registration if this board can support + * only host configuration. + */ + if (motg->pdata->mode == USB_HOST) { + dev_info(otg->dev, "Peripheral mode is not supported\n"); + return -ENODEV; + } + + if (!gadget) { + if (otg->state == OTG_STATE_B_PERIPHERAL) { + msm_otg_start_peripheral(otg, 0); + otg->gadget = NULL; + otg->state = OTG_STATE_UNDEFINED; + schedule_work(&motg->sm_work); + } else { + otg->gadget = NULL; + } + + return 0; + } + otg->gadget = gadget; + dev_dbg(otg->dev, "peripheral driver registered w/ tranceiver\n"); + + /* + * Kick the state machine work, if host is not supported + * or host is already registered with us. + */ + if (motg->pdata->mode == USB_PERIPHERAL || otg->host) + schedule_work(&motg->sm_work); + + return 0; +} + +/* + * We support OTG, Peripheral only and Host only configurations. In case + * of OTG, mode switch (host-->peripheral/peripheral-->host) can happen + * via Id pin status or user request (debugfs). Id/BSV interrupts are not + * enabled when switch is controlled by user and default mode is supplied + * by board file, which can be changed by userspace later. + */ +static void msm_otg_init_sm(struct msm_otg *motg) +{ + struct msm_otg_platform_data *pdata = motg->pdata; + u32 otgsc = readl(USB_OTGSC); + + switch (pdata->mode) { + case USB_OTG: + if (pdata->otg_control == OTG_PHY_CONTROL) { + if (otgsc & OTGSC_ID) + set_bit(ID, &motg->inputs); + else + clear_bit(ID, &motg->inputs); + + if (otgsc & OTGSC_BSV) + set_bit(B_SESS_VLD, &motg->inputs); + else + clear_bit(B_SESS_VLD, &motg->inputs); + } else if (pdata->otg_control == OTG_USER_CONTROL) { + if (pdata->default_mode == USB_HOST) { + clear_bit(ID, &motg->inputs); + } else if (pdata->default_mode == USB_PERIPHERAL) { + set_bit(ID, &motg->inputs); + set_bit(B_SESS_VLD, &motg->inputs); + } else { + set_bit(ID, &motg->inputs); + clear_bit(B_SESS_VLD, &motg->inputs); + } + } + break; + case USB_HOST: + clear_bit(ID, &motg->inputs); + break; + case USB_PERIPHERAL: + set_bit(ID, &motg->inputs); + if (otgsc & OTGSC_BSV) + set_bit(B_SESS_VLD, &motg->inputs); + else + clear_bit(B_SESS_VLD, &motg->inputs); + break; + default: + break; + } +} + +static void msm_otg_sm_work(struct work_struct *w) +{ + struct msm_otg *motg = container_of(w, struct msm_otg, sm_work); + struct otg_transceiver *otg = &motg->otg; + + switch (otg->state) { + case OTG_STATE_UNDEFINED: + dev_dbg(otg->dev, "OTG_STATE_UNDEFINED state\n"); + msm_otg_reset(otg); + msm_otg_init_sm(motg); + otg->state = OTG_STATE_B_IDLE; + /* FALL THROUGH */ + case OTG_STATE_B_IDLE: + dev_dbg(otg->dev, "OTG_STATE_B_IDLE state\n"); + if (!test_bit(ID, &motg->inputs) && otg->host) { + /* disable BSV bit */ + writel(readl(USB_OTGSC) & ~OTGSC_BSVIE, USB_OTGSC); + msm_otg_start_host(otg, 1); + otg->state = OTG_STATE_A_HOST; + } else if (test_bit(B_SESS_VLD, &motg->inputs) && otg->gadget) { + msm_otg_start_peripheral(otg, 1); + otg->state = OTG_STATE_B_PERIPHERAL; + } + break; + case OTG_STATE_B_PERIPHERAL: + dev_dbg(otg->dev, "OTG_STATE_B_PERIPHERAL state\n"); + if (!test_bit(B_SESS_VLD, &motg->inputs) || + !test_bit(ID, &motg->inputs)) { + msm_otg_start_peripheral(otg, 0); + otg->state = OTG_STATE_B_IDLE; + msm_otg_reset(otg); + schedule_work(w); + } + break; + case OTG_STATE_A_HOST: + dev_dbg(otg->dev, "OTG_STATE_A_HOST state\n"); + if (test_bit(ID, &motg->inputs)) { + msm_otg_start_host(otg, 0); + otg->state = OTG_STATE_B_IDLE; + msm_otg_reset(otg); + schedule_work(w); + } + break; + default: + break; + } +} + +static irqreturn_t msm_otg_irq(int irq, void *data) +{ + struct msm_otg *motg = data; + struct otg_transceiver *otg = &motg->otg; + u32 otgsc = 0; + + otgsc = readl(USB_OTGSC); + if (!(otgsc & (OTGSC_IDIS | OTGSC_BSVIS))) + return IRQ_NONE; + + if ((otgsc & OTGSC_IDIS) && (otgsc & OTGSC_IDIE)) { + if (otgsc & OTGSC_ID) + set_bit(ID, &motg->inputs); + else + clear_bit(ID, &motg->inputs); + dev_dbg(otg->dev, "ID set/clear\n"); + } else if ((otgsc & OTGSC_BSVIS) && (otgsc & OTGSC_BSVIE)) { + if (otgsc & OTGSC_BSV) + set_bit(B_SESS_VLD, &motg->inputs); + else + clear_bit(B_SESS_VLD, &motg->inputs); + dev_dbg(otg->dev, "BSV set/clear\n"); + } + + writel(otgsc, USB_OTGSC); + schedule_work(&motg->sm_work); + return IRQ_HANDLED; +} + +static int msm_otg_mode_show(struct seq_file *s, void *unused) +{ + struct msm_otg *motg = s->private; + struct otg_transceiver *otg = &motg->otg; + + switch (otg->state) { + case OTG_STATE_A_HOST: + seq_printf(s, "host\n"); + break; + case OTG_STATE_B_PERIPHERAL: + seq_printf(s, "peripheral\n"); + break; + default: + seq_printf(s, "none\n"); + break; + } + + return 0; +} + +static int msm_otg_mode_open(struct inode *inode, struct file *file) +{ + return single_open(file, msm_otg_mode_show, inode->i_private); +} + +static ssize_t msm_otg_mode_write(struct file *file, const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct msm_otg *motg = file->private_data; + char buf[16]; + struct otg_transceiver *otg = &motg->otg; + int status = count; + enum usb_mode_type req_mode; + + memset(buf, 0x00, sizeof(buf)); + + if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) { + status = -EFAULT; + goto out; + } + + if (!strncmp(buf, "host", 4)) { + req_mode = USB_HOST; + } else if (!strncmp(buf, "peripheral", 10)) { + req_mode = USB_PERIPHERAL; + } else if (!strncmp(buf, "none", 4)) { + req_mode = USB_NONE; + } else { + status = -EINVAL; + goto out; + } + + switch (req_mode) { + case USB_NONE: + switch (otg->state) { + case OTG_STATE_A_HOST: + case OTG_STATE_B_PERIPHERAL: + set_bit(ID, &motg->inputs); + clear_bit(B_SESS_VLD, &motg->inputs); + break; + default: + goto out; + } + break; + case USB_PERIPHERAL: + switch (otg->state) { + case OTG_STATE_B_IDLE: + case OTG_STATE_A_HOST: + set_bit(ID, &motg->inputs); + set_bit(B_SESS_VLD, &motg->inputs); + break; + default: + goto out; + } + break; + case USB_HOST: + switch (otg->state) { + case OTG_STATE_B_IDLE: + case OTG_STATE_B_PERIPHERAL: + clear_bit(ID, &motg->inputs); + break; + default: + goto out; + } + break; + default: + goto out; + } + + schedule_work(&motg->sm_work); +out: + return status; +} + +const struct file_operations msm_otg_mode_fops = { + .open = msm_otg_mode_open, + .read = seq_read, + .write = msm_otg_mode_write, + .llseek = seq_lseek, + .release = single_release, +}; + +static struct dentry *msm_otg_dbg_root; +static struct dentry *msm_otg_dbg_mode; + +static int msm_otg_debugfs_init(struct msm_otg *motg) +{ + msm_otg_dbg_root = debugfs_create_dir("msm_otg", NULL); + + if (!msm_otg_dbg_root || IS_ERR(msm_otg_dbg_root)) + return -ENODEV; + + msm_otg_dbg_mode = debugfs_create_file("mode", S_IRUGO | S_IWUSR, + msm_otg_dbg_root, motg, &msm_otg_mode_fops); + if (!msm_otg_dbg_mode) { + debugfs_remove(msm_otg_dbg_root); + msm_otg_dbg_root = NULL; + return -ENODEV; + } + + return 0; +} + +static void msm_otg_debugfs_cleanup(void) +{ + debugfs_remove(msm_otg_dbg_mode); + debugfs_remove(msm_otg_dbg_root); +} + +static int __init msm_otg_probe(struct platform_device *pdev) +{ + int ret = 0; + struct resource *res; + struct msm_otg *motg; + struct otg_transceiver *otg; + + dev_info(&pdev->dev, "msm_otg probe\n"); + if (!pdev->dev.platform_data) { + dev_err(&pdev->dev, "No platform data given. Bailing out\n"); + return -ENODEV; + } + + motg = kzalloc(sizeof(struct msm_otg), GFP_KERNEL); + if (!motg) { + dev_err(&pdev->dev, "unable to allocate msm_otg\n"); + return -ENOMEM; + } + + motg->pdata = pdev->dev.platform_data; + otg = &motg->otg; + otg->dev = &pdev->dev; + + motg->phy_reset_clk = clk_get(&pdev->dev, "usb_phy_clk"); + if (IS_ERR(motg->phy_reset_clk)) { + dev_err(&pdev->dev, "failed to get usb_phy_clk\n"); + ret = PTR_ERR(motg->phy_reset_clk); + goto free_motg; + } + + motg->clk = clk_get(&pdev->dev, "usb_hs_clk"); + if (IS_ERR(motg->clk)) { + dev_err(&pdev->dev, "failed to get usb_hs_clk\n"); + ret = PTR_ERR(motg->clk); + goto put_phy_reset_clk; + } + + motg->pclk = clk_get(&pdev->dev, "usb_hs_pclk"); + if (IS_ERR(motg->pclk)) { + dev_err(&pdev->dev, "failed to get usb_hs_pclk\n"); + ret = PTR_ERR(motg->pclk); + goto put_clk; + } + + /* + * USB core clock is not present on all MSM chips. This + * clock is introduced to remove the dependency on AXI + * bus frequency. + */ + motg->core_clk = clk_get(&pdev->dev, "usb_hs_core_clk"); + if (IS_ERR(motg->core_clk)) + motg->core_clk = NULL; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "failed to get platform resource mem\n"); + ret = -ENODEV; + goto put_core_clk; + } + + motg->regs = ioremap(res->start, resource_size(res)); + if (!motg->regs) { + dev_err(&pdev->dev, "ioremap failed\n"); + ret = -ENOMEM; + goto put_core_clk; + } + dev_info(&pdev->dev, "OTG regs = %p\n", motg->regs); + + motg->irq = platform_get_irq(pdev, 0); + if (!motg->irq) { + dev_err(&pdev->dev, "platform_get_irq failed\n"); + ret = -ENODEV; + goto free_regs; + } + + clk_enable(motg->clk); + clk_enable(motg->pclk); + if (motg->core_clk) + clk_enable(motg->core_clk); + + writel(0, USB_USBINTR); + writel(0, USB_OTGSC); + + INIT_WORK(&motg->sm_work, msm_otg_sm_work); + ret = request_irq(motg->irq, msm_otg_irq, IRQF_SHARED, + "msm_otg", motg); + if (ret) { + dev_err(&pdev->dev, "request irq failed\n"); + goto disable_clks; + } + + otg->init = msm_otg_reset; + otg->set_host = msm_otg_set_host; + otg->set_peripheral = msm_otg_set_peripheral; + + otg->io_ops = &msm_otg_io_ops; + + ret = otg_set_transceiver(&motg->otg); + if (ret) { + dev_err(&pdev->dev, "otg_set_transceiver failed\n"); + goto free_irq; + } + + platform_set_drvdata(pdev, motg); + device_init_wakeup(&pdev->dev, 1); + + if (motg->pdata->mode == USB_OTG && + motg->pdata->otg_control == OTG_USER_CONTROL) { + ret = msm_otg_debugfs_init(motg); + if (ret) + dev_dbg(&pdev->dev, "mode debugfs file is" + "not available\n"); + } + + return 0; + +free_irq: + free_irq(motg->irq, motg); +disable_clks: + clk_disable(motg->pclk); + clk_disable(motg->clk); +free_regs: + iounmap(motg->regs); +put_core_clk: + if (motg->core_clk) + clk_put(motg->core_clk); + clk_put(motg->pclk); +put_clk: + clk_put(motg->clk); +put_phy_reset_clk: + clk_put(motg->phy_reset_clk); +free_motg: + kfree(motg); + return ret; +} + +static int __devexit msm_otg_remove(struct platform_device *pdev) +{ + struct msm_otg *motg = platform_get_drvdata(pdev); + struct otg_transceiver *otg = &motg->otg; + + if (otg->host || otg->gadget) + return -EBUSY; + + msm_otg_debugfs_cleanup(); + cancel_work_sync(&motg->sm_work); + device_init_wakeup(&pdev->dev, 0); + otg_set_transceiver(NULL); + + free_irq(motg->irq, motg); + + clk_disable(motg->pclk); + clk_disable(motg->clk); + if (motg->core_clk) + clk_disable(motg->core_clk); + + iounmap(motg->regs); + + clk_put(motg->phy_reset_clk); + clk_put(motg->pclk); + clk_put(motg->clk); + if (motg->core_clk) + clk_put(motg->core_clk); + + kfree(motg); + + return 0; +} + +static struct platform_driver msm_otg_driver = { + .remove = __devexit_p(msm_otg_remove), + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init msm_otg_init(void) +{ + return platform_driver_probe(&msm_otg_driver, msm_otg_probe); +} + +static void __exit msm_otg_exit(void) +{ + platform_driver_unregister(&msm_otg_driver); +} + +module_init(msm_otg_init); +module_exit(msm_otg_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("MSM USB transceiver driver"); diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h new file mode 100644 index 000000000000..b796df94ec72 --- /dev/null +++ b/include/linux/usb/msm_hsusb.h @@ -0,0 +1,108 @@ +/* linux/include/asm-arm/arch-msm/hsusb.h + * + * Copyright (C) 2008 Google, Inc. + * Author: Brian Swetland + * Copyright (c) 2009-2010, Code Aurora Forum. All rights reserved. + * + * 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. + * + */ + +#ifndef __ASM_ARCH_MSM_HSUSB_H +#define __ASM_ARCH_MSM_HSUSB_H + +#include +#include + +/** + * Supported USB modes + * + * USB_PERIPHERAL Only peripheral mode is supported. + * USB_HOST Only host mode is supported. + * USB_OTG OTG mode is supported. + * + */ +enum usb_mode_type { + USB_NONE = 0, + USB_PERIPHERAL, + USB_HOST, + USB_OTG, +}; + +/** + * OTG control + * + * OTG_NO_CONTROL Id/VBUS notifications not required. Useful in host + * only configuration. + * OTG_PHY_CONTROL Id/VBUS notifications comes form USB PHY. + * OTG_PMIC_CONTROL Id/VBUS notifications comes from PMIC hardware. + * OTG_USER_CONTROL Id/VBUS notifcations comes from User via sysfs. + * + */ +enum otg_control_type { + OTG_NO_CONTROL = 0, + OTG_PHY_CONTROL, + OTG_PMIC_CONTROL, + OTG_USER_CONTROL, +}; + +/** + * struct msm_otg_platform_data - platform device data + * for msm72k_otg driver. + * @phy_init_seq: PHY configuration sequence. val, reg pairs + * terminated by -1. + * @vbus_power: VBUS power on/off routine. + * @power_budget: VBUS power budget in mA (0 will be treated as 500mA). + * @mode: Supported mode (OTG/peripheral/host). + * @otg_control: OTG switch controlled by user/Id pin + * @default_mode: Default operational mode. Applicable only if + * OTG switch is controller by user. + * + */ +struct msm_otg_platform_data { + int *phy_init_seq; + void (*vbus_power)(bool on); + unsigned power_budget; + enum usb_mode_type mode; + enum otg_control_type otg_control; + enum usb_mode_type default_mode; + void (*setup_gpio)(enum usb_otg_state state); +}; + +/** + * struct msm_otg: OTG driver data. Shared by HCD and DCD. + * @otg: USB OTG Transceiver structure. + * @pdata: otg device platform data. + * @irq: IRQ number assigned for HSUSB controller. + * @clk: clock struct of usb_hs_clk. + * @pclk: clock struct of usb_hs_pclk. + * @phy_reset_clk: clock struct of usb_phy_clk. + * @core_clk: clock struct of usb_hs_core_clk. + * @regs: ioremapped register base address. + * @inputs: OTG state machine inputs(Id, SessValid etc). + * @sm_work: OTG state machine work. + * + */ +struct msm_otg { + struct otg_transceiver otg; + struct msm_otg_platform_data *pdata; + int irq; + struct clk *clk; + struct clk *pclk; + struct clk *phy_reset_clk; + struct clk *core_clk; + void __iomem *regs; +#define ID 0 +#define B_SESS_VLD 1 + unsigned long inputs; + struct work_struct sm_work; +}; + +#endif diff --git a/include/linux/usb/msm_hsusb_hw.h b/include/linux/usb/msm_hsusb_hw.h new file mode 100644 index 000000000000..b061cffcf048 --- /dev/null +++ b/include/linux/usb/msm_hsusb_hw.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2007 Google, Inc. + * Author: Brian Swetland + * + * 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. + * + */ + +#ifndef __LINUX_USB_GADGET_MSM72K_UDC_H__ +#define __LINUX_USB_GADGET_MSM72K_UDC_H__ + +#ifdef CONFIG_ARCH_MSM7X00A +#define USB_SBUSCFG (MSM_USB_BASE + 0x0090) +#else +#define USB_AHBBURST (MSM_USB_BASE + 0x0090) +#define USB_AHBMODE (MSM_USB_BASE + 0x0098) +#endif +#define USB_CAPLENGTH (MSM_USB_BASE + 0x0100) /* 8 bit */ + +#define USB_USBCMD (MSM_USB_BASE + 0x0140) +#define USB_PORTSC (MSM_USB_BASE + 0x0184) +#define USB_OTGSC (MSM_USB_BASE + 0x01A4) +#define USB_USBMODE (MSM_USB_BASE + 0x01A8) + +#define USBCMD_RESET 2 +#define USB_USBINTR (MSM_USB_BASE + 0x0148) + +#define PORTSC_PHCD (1 << 23) /* phy suspend mode */ +#define PORTSC_PTS_MASK (3 << 30) +#define PORTSC_PTS_ULPI (3 << 30) + +#define USB_ULPI_VIEWPORT (MSM_USB_BASE + 0x0170) +#define ULPI_RUN (1 << 30) +#define ULPI_WRITE (1 << 29) +#define ULPI_READ (0 << 29) +#define ULPI_ADDR(n) (((n) & 255) << 16) +#define ULPI_DATA(n) ((n) & 255) +#define ULPI_DATA_READ(n) (((n) >> 8) & 255) + +/* OTG definitions */ +#define OTGSC_INTSTS_MASK (0x7f << 16) +#define OTGSC_ID (1 << 8) +#define OTGSC_BSV (1 << 11) +#define OTGSC_IDIS (1 << 16) +#define OTGSC_BSVIS (1 << 19) +#define OTGSC_IDIE (1 << 24) +#define OTGSC_BSVIE (1 << 27) + +#endif /* __LINUX_USB_GADGET_MSM72K_UDC_H__ */ -- cgit v1.2.3 From 87c0104af742af2acfcbd685f2b9a40f33770dc0 Mon Sep 17 00:00:00 2001 From: Pavankumar Kondeti Date: Tue, 7 Dec 2010 17:53:58 +0530 Subject: USB: OTG: msm: Add support for power management Implement runtime and system pm ops to put hardware into low power mode (LPM). As part of LPM, USB clocks are turned off, PHY is put into suspend state and PHY comparators are turned off if VBUS/Id notifications are not required from PHY. Signed-off-by: Pavankumar Kondeti Signed-off-by: Greg Kroah-Hartman --- drivers/usb/otg/Kconfig | 5 +- drivers/usb/otg/msm72k_otg.c | 283 ++++++++++++++++++++++++++++++++++++++- include/linux/usb/msm_hsusb.h | 4 + include/linux/usb/msm_hsusb_hw.h | 3 + 4 files changed, 289 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/drivers/usb/otg/Kconfig b/drivers/usb/otg/Kconfig index 915c729872f8..2810c2af71b0 100644 --- a/drivers/usb/otg/Kconfig +++ b/drivers/usb/otg/Kconfig @@ -88,7 +88,8 @@ config USB_MSM_OTG_72K 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. This driver is required - even for peripheral only or host only mode configuration. + required after resetting the hardware and power management. + This driver is required even for peripheral only or host only + mode configurations. endif # USB || OTG diff --git a/drivers/usb/otg/msm72k_otg.c b/drivers/usb/otg/msm72k_otg.c index 46f468a912f4..1cd52edcd0c2 100644 --- a/drivers/usb/otg/msm72k_otg.c +++ b/drivers/usb/otg/msm72k_otg.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -251,6 +252,154 @@ static int msm_otg_reset(struct otg_transceiver *otg) return 0; } +#define PHY_SUSPEND_TIMEOUT_USEC (500 * 1000) +static int msm_otg_suspend(struct msm_otg *motg) +{ + struct otg_transceiver *otg = &motg->otg; + struct usb_bus *bus = otg->host; + struct msm_otg_platform_data *pdata = motg->pdata; + int cnt = 0; + + if (atomic_read(&motg->in_lpm)) + return 0; + + disable_irq(motg->irq); + /* + * Interrupt Latch Register auto-clear feature is not present + * in all PHY versions. Latch register is clear on read type. + * Clear latch register to avoid spurious wakeup from + * low power mode (LPM). + */ + ulpi_read(otg, 0x14); + + /* + * PHY comparators are disabled when PHY enters into low power + * mode (LPM). Keep PHY comparators ON in LPM only when we expect + * VBUS/Id notifications from USB PHY. Otherwise turn off USB + * PHY comparators. This save significant amount of power. + */ + if (pdata->otg_control == OTG_PHY_CONTROL) + ulpi_write(otg, 0x01, 0x30); + + /* + * PLL is not turned off when PHY enters into low power mode (LPM). + * Disable PLL for maximum power savings. + */ + ulpi_write(otg, 0x08, 0x09); + + /* + * PHY may take some time or even fail to enter into low power + * mode (LPM). Hence poll for 500 msec and reset the PHY and link + * in failure case. + */ + writel(readl(USB_PORTSC) | PORTSC_PHCD, USB_PORTSC); + while (cnt < PHY_SUSPEND_TIMEOUT_USEC) { + if (readl(USB_PORTSC) & PORTSC_PHCD) + break; + udelay(1); + cnt++; + } + + if (cnt >= PHY_SUSPEND_TIMEOUT_USEC) { + dev_err(otg->dev, "Unable to suspend PHY\n"); + msm_otg_reset(otg); + enable_irq(motg->irq); + return -ETIMEDOUT; + } + + /* + * PHY has capability to generate interrupt asynchronously in low + * power mode (LPM). This interrupt is level triggered. So USB IRQ + * line must be disabled till async interrupt enable bit is cleared + * in USBCMD register. Assert STP (ULPI interface STOP signal) to + * block data communication from PHY. + */ + writel(readl(USB_USBCMD) | ASYNC_INTR_CTRL | ULPI_STP_CTRL, USB_USBCMD); + + clk_disable(motg->pclk); + clk_disable(motg->clk); + if (motg->core_clk) + clk_disable(motg->core_clk); + + if (device_may_wakeup(otg->dev)) + enable_irq_wake(motg->irq); + if (bus) + clear_bit(HCD_FLAG_HW_ACCESSIBLE, &(bus_to_hcd(bus))->flags); + + atomic_set(&motg->in_lpm, 1); + enable_irq(motg->irq); + + dev_info(otg->dev, "USB in low power mode\n"); + + return 0; +} + +#define PHY_RESUME_TIMEOUT_USEC (100 * 1000) +static int msm_otg_resume(struct msm_otg *motg) +{ + struct otg_transceiver *otg = &motg->otg; + struct usb_bus *bus = otg->host; + int cnt = 0; + unsigned temp; + + if (!atomic_read(&motg->in_lpm)) + return 0; + + clk_enable(motg->pclk); + clk_enable(motg->clk); + if (motg->core_clk) + clk_enable(motg->core_clk); + + temp = readl(USB_USBCMD); + temp &= ~ASYNC_INTR_CTRL; + temp &= ~ULPI_STP_CTRL; + writel(temp, USB_USBCMD); + + /* + * PHY comes out of low power mode (LPM) in case of wakeup + * from asynchronous interrupt. + */ + if (!(readl(USB_PORTSC) & PORTSC_PHCD)) + goto skip_phy_resume; + + writel(readl(USB_PORTSC) & ~PORTSC_PHCD, USB_PORTSC); + while (cnt < PHY_RESUME_TIMEOUT_USEC) { + if (!(readl(USB_PORTSC) & PORTSC_PHCD)) + break; + udelay(1); + cnt++; + } + + if (cnt >= PHY_RESUME_TIMEOUT_USEC) { + /* + * This is a fatal error. Reset the link and + * PHY. USB state can not be restored. Re-insertion + * of USB cable is the only way to get USB working. + */ + dev_err(otg->dev, "Unable to resume USB." + "Re-plugin the cable\n"); + msm_otg_reset(otg); + } + +skip_phy_resume: + if (device_may_wakeup(otg->dev)) + disable_irq_wake(motg->irq); + if (bus) + set_bit(HCD_FLAG_HW_ACCESSIBLE, &(bus_to_hcd(bus))->flags); + + if (motg->async_int) { + motg->async_int = 0; + pm_runtime_put(otg->dev); + enable_irq(motg->irq); + } + + atomic_set(&motg->in_lpm, 0); + + dev_info(otg->dev, "USB exited from low power mode\n"); + + return 0; +} + static void msm_otg_start_host(struct otg_transceiver *otg, int on) { struct msm_otg *motg = container_of(otg, struct msm_otg, otg); @@ -306,6 +455,7 @@ static int msm_otg_set_host(struct otg_transceiver *otg, struct usb_bus *host) if (!host) { if (otg->state == OTG_STATE_A_HOST) { + pm_runtime_get_sync(otg->dev); msm_otg_start_host(otg, 0); otg->host = NULL; otg->state = OTG_STATE_UNDEFINED; @@ -327,8 +477,10 @@ static int msm_otg_set_host(struct otg_transceiver *otg, struct usb_bus *host) * Kick the state machine work, if peripheral is not supported * or peripheral is already registered with us. */ - if (motg->pdata->mode == USB_HOST || otg->gadget) + if (motg->pdata->mode == USB_HOST || otg->gadget) { + pm_runtime_get_sync(otg->dev); schedule_work(&motg->sm_work); + } return 0; } @@ -376,6 +528,7 @@ static int msm_otg_set_peripheral(struct otg_transceiver *otg, if (!gadget) { if (otg->state == OTG_STATE_B_PERIPHERAL) { + pm_runtime_get_sync(otg->dev); msm_otg_start_peripheral(otg, 0); otg->gadget = NULL; otg->state = OTG_STATE_UNDEFINED; @@ -393,8 +546,10 @@ static int msm_otg_set_peripheral(struct otg_transceiver *otg, * Kick the state machine work, if host is not supported * or host is already registered with us. */ - if (motg->pdata->mode == USB_PERIPHERAL || otg->host) + if (motg->pdata->mode == USB_PERIPHERAL || otg->host) { + pm_runtime_get_sync(otg->dev); schedule_work(&motg->sm_work); + } return 0; } @@ -473,6 +628,7 @@ static void msm_otg_sm_work(struct work_struct *w) msm_otg_start_peripheral(otg, 1); otg->state = OTG_STATE_B_PERIPHERAL; } + pm_runtime_put_sync(otg->dev); break; case OTG_STATE_B_PERIPHERAL: dev_dbg(otg->dev, "OTG_STATE_B_PERIPHERAL state\n"); @@ -504,6 +660,13 @@ static irqreturn_t msm_otg_irq(int irq, void *data) struct otg_transceiver *otg = &motg->otg; u32 otgsc = 0; + if (atomic_read(&motg->in_lpm)) { + disable_irq_nosync(irq); + motg->async_int = 1; + pm_runtime_get(otg->dev); + return IRQ_HANDLED; + } + otgsc = readl(USB_OTGSC); if (!(otgsc & (OTGSC_IDIS | OTGSC_BSVIS))) return IRQ_NONE; @@ -514,12 +677,14 @@ static irqreturn_t msm_otg_irq(int irq, void *data) else clear_bit(ID, &motg->inputs); dev_dbg(otg->dev, "ID set/clear\n"); + pm_runtime_get_noresume(otg->dev); } else if ((otgsc & OTGSC_BSVIS) && (otgsc & OTGSC_BSVIE)) { if (otgsc & OTGSC_BSV) set_bit(B_SESS_VLD, &motg->inputs); else clear_bit(B_SESS_VLD, &motg->inputs); dev_dbg(otg->dev, "BSV set/clear\n"); + pm_runtime_get_noresume(otg->dev); } writel(otgsc, USB_OTGSC); @@ -616,6 +781,7 @@ static ssize_t msm_otg_mode_write(struct file *file, const char __user *ubuf, goto out; } + pm_runtime_get_sync(otg->dev); schedule_work(&motg->sm_work); out: return status; @@ -770,8 +936,10 @@ static int __init msm_otg_probe(struct platform_device *pdev) "not available\n"); } - return 0; + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + return 0; free_irq: free_irq(motg->irq, motg); disable_clks: @@ -796,23 +964,45 @@ static int __devexit msm_otg_remove(struct platform_device *pdev) { struct msm_otg *motg = platform_get_drvdata(pdev); struct otg_transceiver *otg = &motg->otg; + int cnt = 0; if (otg->host || otg->gadget) return -EBUSY; msm_otg_debugfs_cleanup(); cancel_work_sync(&motg->sm_work); + + msm_otg_resume(motg); + device_init_wakeup(&pdev->dev, 0); - otg_set_transceiver(NULL); + pm_runtime_disable(&pdev->dev); + otg_set_transceiver(NULL); free_irq(motg->irq, motg); + /* + * Put PHY in low power mode. + */ + ulpi_read(otg, 0x14); + ulpi_write(otg, 0x08, 0x09); + + writel(readl(USB_PORTSC) | PORTSC_PHCD, USB_PORTSC); + while (cnt < PHY_SUSPEND_TIMEOUT_USEC) { + if (readl(USB_PORTSC) & PORTSC_PHCD) + break; + udelay(1); + cnt++; + } + if (cnt >= PHY_SUSPEND_TIMEOUT_USEC) + dev_err(otg->dev, "Unable to suspend PHY\n"); + clk_disable(motg->pclk); clk_disable(motg->clk); if (motg->core_clk) clk_disable(motg->core_clk); iounmap(motg->regs); + pm_runtime_set_suspended(&pdev->dev); clk_put(motg->phy_reset_clk); clk_put(motg->pclk); @@ -825,11 +1015,96 @@ static int __devexit msm_otg_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM_RUNTIME +static int msm_otg_runtime_idle(struct device *dev) +{ + struct msm_otg *motg = dev_get_drvdata(dev); + struct otg_transceiver *otg = &motg->otg; + + dev_dbg(dev, "OTG runtime idle\n"); + + /* + * It is observed some times that a spurious interrupt + * comes when PHY is put into LPM immediately after PHY reset. + * This 1 sec delay also prevents entering into LPM immediately + * after asynchronous interrupt. + */ + if (otg->state != OTG_STATE_UNDEFINED) + pm_schedule_suspend(dev, 1000); + + return -EAGAIN; +} + +static int msm_otg_runtime_suspend(struct device *dev) +{ + struct msm_otg *motg = dev_get_drvdata(dev); + + dev_dbg(dev, "OTG runtime suspend\n"); + return msm_otg_suspend(motg); +} + +static int msm_otg_runtime_resume(struct device *dev) +{ + struct msm_otg *motg = dev_get_drvdata(dev); + + dev_dbg(dev, "OTG runtime resume\n"); + return msm_otg_resume(motg); +} +#else +#define msm_otg_runtime_idle NULL +#define msm_otg_runtime_suspend NULL +#define msm_otg_runtime_resume NULL +#endif + +#ifdef CONFIG_PM +static int msm_otg_pm_suspend(struct device *dev) +{ + struct msm_otg *motg = dev_get_drvdata(dev); + + dev_dbg(dev, "OTG PM suspend\n"); + return msm_otg_suspend(motg); +} + +static int msm_otg_pm_resume(struct device *dev) +{ + struct msm_otg *motg = dev_get_drvdata(dev); + int ret; + + dev_dbg(dev, "OTG PM resume\n"); + + ret = msm_otg_resume(motg); + if (ret) + return ret; + + /* + * Runtime PM Documentation recommends bringing the + * device to full powered state upon resume. + */ + pm_runtime_disable(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + return 0; +} +#else +#define msm_otg_pm_suspend NULL +#define msm_otg_pm_resume NULL +#endif + +static const struct dev_pm_ops msm_otg_dev_pm_ops = { + .runtime_suspend = msm_otg_runtime_suspend, + .runtime_resume = msm_otg_runtime_resume, + .runtime_idle = msm_otg_runtime_idle, + .suspend = msm_otg_pm_suspend, + .resume = msm_otg_pm_resume, +}; + static struct platform_driver msm_otg_driver = { .remove = __devexit_p(msm_otg_remove), .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, + .pm = &msm_otg_dev_pm_ops, }, }; diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h index b796df94ec72..3675e03b1539 100644 --- a/include/linux/usb/msm_hsusb.h +++ b/include/linux/usb/msm_hsusb.h @@ -88,6 +88,8 @@ struct msm_otg_platform_data { * @regs: ioremapped register base address. * @inputs: OTG state machine inputs(Id, SessValid etc). * @sm_work: OTG state machine work. + * @in_lpm: indicates low power mode (LPM) state. + * @async_int: Async interrupt arrived. * */ struct msm_otg { @@ -103,6 +105,8 @@ struct msm_otg { #define B_SESS_VLD 1 unsigned long inputs; struct work_struct sm_work; + atomic_t in_lpm; + int async_int; }; #endif diff --git a/include/linux/usb/msm_hsusb_hw.h b/include/linux/usb/msm_hsusb_hw.h index b061cffcf048..b92e17349c7b 100644 --- a/include/linux/usb/msm_hsusb_hw.h +++ b/include/linux/usb/msm_hsusb_hw.h @@ -44,6 +44,9 @@ #define ULPI_DATA(n) ((n) & 255) #define ULPI_DATA_READ(n) (((n) >> 8) & 255) +#define ASYNC_INTR_CTRL (1 << 29) /* Enable async interrupt */ +#define ULPI_STP_CTRL (1 << 30) /* Block communication with PHY */ + /* OTG definitions */ #define OTGSC_INTSTS_MASK (0x7f << 16) #define OTGSC_ID (1 << 8) -- cgit v1.2.3