Android usb子系統的 電源管理 流程分析
阿新 • • 發佈:2019-02-19
對的處理器是高通MSM8260,主要是針對一些掛起喚醒流程進行分析,以便對整個usb框架流程更好的理解。
由於linux中的電源管理比較複雜,我就找了一個統一的介面,也就是 要想操縱usb的電源管理 必定要調的函式。順便說下,跟蹤程式碼最好的方法是用WARN_ON(1)打呼叫棧。先看電源管理子系統的一些初始化:
/*系統初始化所用的結構體*/
struct device {
.....
struct dev_pm_info power; //電源管理結構體
.....
};
/*該結構體定義如下*/ struct dev_pm_info {
pm_message_t power_state;
unsigned int can_wakeup:1; //can_wakeup標誌 表示裝置(或驅動)物理上 支援喚醒事件
unsigned int should_wakeup:1; //should_wakeup標誌 控制裝置是否應該嘗試啟用他的喚醒機制
unsigned async_suspend:1;
enum dpm_state status; /* Owned by the PM core */
#ifdef CONFIG_PM_SLEEP
struct list_head entry; //連結到dpm_list連結串列的節點
struct completion completion;
#endif
#ifdef CONFIG_PM_RUNTIME
struct timer_list suspend_timer; //處理自動掛起的timer
unsigned long timer_expires;
struct work_struct work; //請求處理用的work
wait_queue_head_t wait_queue; //等待佇列頭
spinlock_t lock;
atomic_t usage_count;
atomic_t child_count;
unsigned int disable_depth:3;
unsigned int ignore_children:1;
unsigned int idle_notification:1;
unsigned int request_pending:1;
unsigned int deferred_resume:1;
unsigned int run_wake:1;
unsigned int runtime_auto:1;
enum rpm_request request;
enum rpm_status runtime_status;
int runtime_error;
#endif
};
/*在裝置加入裝置驅動模型時,對裝置進行初始化*/
device_initialize(&dev->dev);
void device_initialize(struct device *dev)
{
............
device_pm_init(dev); //繼續進行初始化
...............
} void device_pm_init(struct device *dev)
{
dev->power.status = DPM_ON;
init_completion(&dev->power.completion);
complete_all(&dev->power.completion);
pm_runtime_init(dev); //呼叫該函式繼續進行初始化
}
void pm_runtime_init(struct device *dev)
{
spin_lock_init(&dev->power.lock); dev->power.runtime_status = RPM_SUSPENDED;
dev->power.idle_notification = false; dev->power.disable_depth = 1; //初始化時,disable該pm runtime功能
atomic_set(&dev->power.usage_count, 0); dev->power.runtime_error = 0; atomic_set(&dev->power.child_count, 0);
pm_suspend_ignore_children(dev, false);
dev->power.runtime_auto = true; dev->power.request_pending = false;
dev->power.request = RPM_REQ_NONE;
dev->power.deferred_resume = false;
INIT_WORK(&dev->power.work, pm_runtime_work); //處理autosuspend請求用的work /*處理work請求定時器*/
dev->power.timer_expires = 0;
setup_timer(&dev->power.suspend_timer, pm_suspend_timer_fn,(unsigned long)dev); init_waitqueue_head(&dev->power.wait_queue);//初始化等待佇列
} ***************************************************************
/*註冊的usb匯流排級別的操作*/
static int usb_runtime_suspend(struct device *dev)//usb的掛機函式
{
int status = 0; /* A USB device can be suspended if it passes the various autosuspend
* checks. Runtime suspend for a USB device means suspending all the
* interfaces and then the device itself.
*/
if (is_usb_device(dev)) {//針對usb裝置
struct usb_device *udev = to_usb_device(dev); if (autosuspend_check(udev) != 0)//是否支援自動掛起?
return -EAGAIN; status = usb_suspend_both(udev, PMSG_AUTO_SUSPEND);// 如果不支援,則繼續掛機流程 /* If an interface fails the suspend, adjust the last_busy
* time so that we don't get another suspend attempt right
* away.
*/
if (status) {
udev->last_busy = jiffies + (udev->autosuspend_delay == 0 ? HZ/2 : 0);
} /* Prevent the parent from suspending immediately after */
else if (udev->parent) {
udev->parent->last_busy = jiffies;
}
} /* Runtime suspend for a USB interface doesn't mean anything. */
return status;
} static int usb_runtime_resume(struct device *dev)//usb的喚醒函式
{
/* Runtime resume for a USB device means resuming both the device
* and all its interfaces.
*/
if (is_usb_device(dev)) {
struct usb_device *udev = to_usb_device(dev);
int status; status = usb_resume_both(udev, PMSG_AUTO_RESUME);
udev->last_busy = jiffies;
return status;
} /* Runtime resume for a USB interface doesn't mean anything. */
return 0;
} static int usb_runtime_idle(struct device *dev)//usb裝置的idle函式
{
/* An idle USB device can be suspended if it passes the various
* autosuspend checks. An idle interface can be suspended at
* any time.
*/
if (is_usb_device(dev)) {
struct usb_device *udev = to_usb_device(dev); if (autosuspend_check(udev) != 0)//支援自動掛起
return 0;
} pm_runtime_suspend(dev);
return 0;
} static struct dev_pm_ops usb_bus_pm_ops = {
.runtime_suspend = usb_runtime_suspend,
.runtime_resume = usb_runtime_resume,
.runtime_idle = usb_runtime_idle,
}; struct bus_type usb_bus_type = {
.name = "usb",
.match = usb_device_match,
.uevent = usb_uevent,
.pm = &usb_bus_pm_ops,
};
上面是系統和usb子系統的電源管理初始化部分。
/**************************************************************************/ usb子系統電源管理的呼叫過程,必須要呼叫下面兩個函式,就從這兩個函式開始跟起。 /*驅動呼叫的介面開始,傳入的是usb裝置結構體或者usb介面裝置結構體*/
static inline int pm_runtime_put(struct device *dev)
{
return __pm_runtime_put(dev, false);
} static inline int pm_runtime_put_sync(struct device *dev)
{
return __pm_runtime_put(dev, true);
} 上面兩個函式一個是同步的一個是不同步的。先跟同步的: /*當sync為true時,走的如下流程*/
int __pm_runtime_put(struct device *dev, bool sync)
{
int retval = 0;
if (atomic_dec_and_test(&dev->power.usage_count))
retval = sync ? pm_runtime_idle(dev) : pm_request_idle(dev); return retval;
} int pm_runtime_idle(struct device *dev)
{
int retval; spin_lock_irq(&dev->power.lock);
retval = __pm_runtime_idle(dev);
spin_unlock_irq(&dev->power.lock); return retval;
}
static int __pm_runtime_idle(struct device *dev)
__releases(&dev->power.lock) __acquires(&dev->power.lock)
{
dev->power.idle_notification = true; if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_idle) {
spin_unlock_irq(&dev->power.lock); dev->bus->pm->runtime_idle(dev);//呼叫usb匯流排的idle函式 spin_lock_irq(&dev->power.lock);
} else if (dev->type && dev->type->pm && dev->type->pm->runtime_idle) {
spin_unlock_irq(&dev->power.lock); dev->type->pm->runtime_idle(dev); spin_lock_irq(&dev->power.lock);
} else if (dev->class && dev->class->pm
&& dev->class->pm->runtime_idle) {
spin_unlock_irq(&dev->power.lock); dev->class->pm->runtime_idle(dev); spin_lock_irq(&dev->power.lock);
} dev->power.idle_notification = false;
wake_up_all(&dev->power.wait_queue); out:
return retval;
} /*usb匯流排的idle函式如下*/ static int usb_runtime_idle(struct device *dev)
{
/* An idle USB device can be suspended if it passes the various
* autosuspend checks. An idle interface can be suspended at
* any time.
*/
if (is_usb_device(dev)) {
struct usb_device *udev = to_usb_device(dev); if (autosuspend_check(udev) != 0)//檢測是否自動掛起,如果是自動掛起的話,走自動掛起流程
return 0;
} pm_runtime_suspend(dev);//如果不是自動掛起,則繼續suspend
return 0;
} int pm_runtime_suspend(struct device *dev)
{
int retval; spin_lock_irq(&dev->power.lock);
retval = __pm_runtime_suspend(dev, false);
spin_unlock_irq(&dev->power.lock); return retval;
} int __pm_runtime_suspend(struct device *dev, bool from_wq)
__releases(&dev->power.lock) __acquires(&dev->power.lock)
{
struct device *parent = NULL;
bool notify = false;
int retval = 0; dev_dbg(dev, "__pm_runtime_suspend()%s!\n",
from_wq ? " from workqueue" : ""); repeat: /* Other scheduled or pending requests need to be canceled. */
pm_runtime_cancel_pending(dev); if (dev->power.runtime_status == RPM_SUSPENDING) {
DEFINE_WAIT(wait); if (from_wq) {
retval = -EINPROGRESS;
goto out;
} /* Wait for the other suspend running in parallel with us. */
for (;;) {
prepare_to_wait(&dev->power.wait_queue, &wait, TASK_UNINTERRUPTIBLE);
if (dev->power.runtime_status != RPM_SUSPENDING)
break; spin_unlock_irq(&dev->power.lock); schedule(); spin_lock_irq(&dev->power.lock);
}
finish_wait(&dev->power.wait_queue, &wait);
goto repeat;
} dev->power.runtime_status = RPM_SUSPENDING;
dev->power.deferred_resume = false; if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_suspend) {
spin_unlock_irq(&dev->power.lock); retval = dev->bus->pm->runtime_suspend(dev);//呼叫匯流排的runtime suspend函式 spin_lock_irq(&dev->power.lock);
dev->power.runtime_error = retval;
} else if (dev->type && dev->type->pm && dev->type->pm->runtime_suspend) {
spin_unlock_irq(&dev->power.lock); retval = dev->type->pm->runtime_suspend(dev); spin_lock_irq(&dev->power.lock);
dev->power.runtime_error = retval;
} else if (dev->class && dev->class->pm
&& dev->class->pm->runtime_suspend) {
spin_unlock_irq(&dev->power.lock); retval = dev->class->pm->runtime_suspend(dev); spin_lock_irq(&dev->power.lock);
dev->power.runtime_error = retval;
} else {
retval = -ENOSYS;
} wake_up_all(&dev->power.wait_queue); if (dev->power.deferred_resume) {
__pm_runtime_resume(dev, false);
retval = -EAGAIN;
goto out;
} if (notify)
__pm_runtime_idle(dev); if (parent && !parent->power.ignore_children) {
spin_unlock_irq(&dev->power.lock); pm_request_idle(parent); spin_lock_irq(&dev->power.lock);
} out:
dev_dbg(dev, "__pm_runtime_suspend() returns %d!\n", retval); return retval;
} static int usb_runtime_suspend(struct device *dev)
{
int status = 0; /* A USB device can be suspended if it passes the various autosuspend
* checks. Runtime suspend for a USB device means suspending all the
* interfaces and then the device itself.
*/
if (is_usb_device(dev)) {
struct usb_device *udev = to_usb_device(dev); if (autosuspend_check(udev) != 0)//是否自動掛起,如果是走自動掛起流程
return -EAGAIN; status = usb_suspend_both(udev, PMSG_AUTO_SUSPEND);//否則繼續suspend /* If an interface fails the suspend, adjust the last_busy
* time so that we don't get another suspend attempt right
* away.
*/
if (status) {
udev->last_busy = jiffies +(udev->autosuspend_delay == 0 ? HZ/2 : 0);
} /* Prevent the parent from suspending immediately after */
else if (udev->parent) {
udev->parent->last_busy = jiffies;
}
} /* Runtime suspend for a USB interface doesn't mean anything. */
return status;
} static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
{
int status = 0;
int i = 0, n = 0;
struct usb_interface *intf; if (udev->state == USB_STATE_NOTATTACHED || udev->state == USB_STATE_SUSPENDED)
goto done; /* Suspend all the interfaces and then udev itself */
if (udev->actconfig) {//掛起所有的介面
n = udev->actconfig->desc.bNumInterfaces;
for (i = n - 1; i >= 0; --i) {
intf = udev->actconfig->interface[i];
status = usb_suspend_interface(udev, intf, msg);
if (status != 0)
break;
}
}
if (status == 0)
status = usb_suspend_device(udev, msg);//掛起該裝置本身 /* If the suspend failed, resume interfaces that did get suspended */
if (status != 0) {
msg.event ^= (PM_EVENT_SUSPEND | PM_EVENT_RESUME);
while (++i < n) {
intf = udev->actconfig->interface[i];
usb_resume_interface(udev, intf, msg, 0);
} /* If the suspend succeeded then prevent any more URB submissions
* and flush any outstanding URBs.
*/
} else {
udev->can_submit = 0;
for (i = 0; i < 16; ++i) {
usb_hcd_flush_endpoint(udev, udev->ep_out[i]);
usb_hcd_flush_endpoint(udev, udev->ep_in[i]);
}
} done:
dev_vdbg(&udev->dev, "%s: status %d\n", __func__, status);
return status;
} static int usb_suspend_device(struct usb_device *udev, pm_message_t msg)
{
struct usb_device_driver *udriver;
int status = 0; if (udev->state == USB_STATE_NOTATTACHED ||
udev->state == USB_STATE_SUSPENDED)
goto done; /* For devices that don't have a driver, we do a generic suspend. */
if (udev->dev.driver)
udriver = to_usb_device_driver(udev->dev.driver);
else {
udev->do_remote_wakeup = 0;
udriver = &usb_generic_driver;
}
status = udriver->suspend(udev, msg);//呼叫通用的裝置驅動掛起函式 done:
dev_vdbg(&udev->dev, "%s: status %d\n", __func__, status);
return status;
} static int generic_suspend(struct usb_device *udev, pm_message_t msg)
{
int rc; /* Normal USB devices suspend through their upstream port.
* Root hubs don't have upstream ports to suspend,
* so we have to shut down their downstream HC-to-USB
* interfaces manually by doing a bus (or "global") suspend.
*/
if (!udev->parent)//針對root hub的
rc = hcd_bus_suspend(udev, msg);//掛起root hub /* Non-root devices don't need to do anything for FREEZE or PRETHAW */
else if (msg.event == PM_EVENT_FREEZE || msg.event == PM_EVENT_PRETHAW)
rc = 0;
else
rc = usb_port_suspend(udev, msg);//掛起每個埠裝置 return rc;
} ********************************************************************* 下面是自動掛起的流程(上面的藍色字型標出呼叫的地方),最終也會走到generic_suspend,相當於兩條小溪最終匯成一起。 /* Internal routine to check whether we may autosuspend a device. */
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.
*/
w = 0;
if (udev->actconfig) {
for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
intf = udev->actconfig->interface[i]; /* We don't need to check interfaces that are
* disabled for runtime PM. Either they are unbound
* or else their drivers don't support autosuspend
* and so they are permanently active.
*/
if (intf->dev.power.disable_depth)
continue;
if (atomic_read(&intf->dev.power.usage_count) > 0)
return -EBUSY;
w |= intf->needs_remote_wakeup; /* Don't allow autosuspend if the device will need
* a reset-resume and any of its interface drivers
* doesn't include support or needs remote wakeup.
*/
if (udev->quirks & USB_QUIRK_RESET_RESUME) {
struct usb_driver *driver; driver = to_usb_driver(intf->dev.driver);
if (!driver->reset_resume ||
intf->needs_remote_wakeup)
return -EOPNOTSUPP;
}
}
}
if (w && !device_can_wakeup(&udev->dev)) {
dev_dbg(&udev->dev, "remote wakeup needed for autosuspend\n");
return -EOPNOTSUPP;
}
udev->do_remote_wakeup = w; /* If everything is okay but the device hasn't been idle for long
* enough, queue a delayed autosuspend request.
*/
j = ACCESS_ONCE(jiffies);
suspend_time = udev->last_busy + udev->autosuspend_delay;
if (time_before(j, suspend_time)) {
pm_schedule_suspend(&udev->dev, jiffies_to_msecs(round_jiffies_up_relative(suspend_time - j)));//排程
return -EAGAIN;
}
return 0;
} /**
* pm_schedule_suspend - Set up a timer to submit a suspend request in future.
* @dev: Device to suspend.
* @delay: Time to wait before submitting a suspend request, in milliseconds.
*/
int pm_schedule_suspend(struct device *dev, unsigned int delay)
{
unsigned long flags;
int retval = 0; spin_lock_irqsave(&dev->power.lock, flags); if (dev->power.runtime_error) {
retval = -EINVAL;
goto out;
} if (!delay) {
retval = __pm_request_suspend(dev);//如果沒有delay,則立即執行
goto out;
} pm_runtime_deactivate_timer(dev); if (dev->power.request_pending) {
/*
* Pending resume requests take precedence over us, but any
* other pending requests have to be canceled.
*/
if (dev->power.request == RPM_REQ_RESUME) {
retval = -EAGAIN;
goto out;
}
dev->power.request = RPM_REQ_NONE;
} if (dev->power.runtime_status == RPM_SUSPENDED)
retval = 1;
else if (atomic_read(&dev->power.usage_count) > 0
|| dev->power.disable_depth > 0)
retval = -EAGAIN;
else if (!pm_children_suspended(dev))
retval = -EBUSY;
if (retval)
goto out; dev->power.timer_expires = jiffies + msecs_to_jiffies(delay);
if (!dev->power.timer_expires)
dev->power.timer_expires = 1;
mod_timer(&dev->power.suspend_timer, dev->power.timer_expires);//如果有超時,等待定時器到期,執行處理函數pm_suspend_timer_fn out:
spin_unlock_irqrestore(&dev->power.lock, flags); return retval;
} /*定時器到期處理函式*/ static void pm_suspend_timer_fn(unsigned long data)
{
struct device *dev = (struct device *)data;
unsigned long flags;
unsigned long expires; spin_lock_irqsave(&dev->power.lock, flags); expires = dev->power.timer_expires;
/* If 'expire' is after 'jiffies' we've been called too early. */
if (expires > 0 && !time_after(expires, jiffies)) {
dev->power.timer_expires = 0;
__pm_request_suspend(dev);//定時器到時,執行該函式
} spin_unlock_irqrestore(&dev->power.lock, flags);
} /*無論是定時器超時,還是直接呼叫,都會呼叫下面該函式*/
static int __pm_request_suspend(struct device *dev)
{
int retval = 0; if (dev->power.runtime_error)
return -EINVAL; if (dev->power.runtime_status == RPM_SUSPENDED)
retval = 1;
else if (atomic_read(&dev->power.usage_count) > 0
|| dev->power.disable_depth > 0)
retval = -EAGAIN;
else if (dev->power.runtime_status == RPM_SUSPENDING)
retval = -EINPROGRESS;
else if (!pm_children_suspended(dev))
retval = -EBUSY;
if (retval < 0)
return retval; pm_runtime_deactivate_timer(dev); if (dev->power.request_pending) {
/*
* Pending resume requests take precedence over us, but we can
* overtake any other pending request.
*/
if (dev->power.request == RPM_REQ_RESUME)
retval = -EAGAIN;
else if (dev->power.request != RPM_REQ_SUSPEND)
dev->power.request = retval ?
RPM_REQ_NONE : RPM_REQ_SUSPEND;
return retval;
} else if (retval) {
return retval;
} dev->power.request = RPM_REQ_SUSPEND;//請求狀態為suspend
dev->power.request_pending = true;
queue_work(pm_wq, &dev->power.work);//排程work return 0;
}
static void pm_runtime_work(struct work_struct *work) {
struct device *dev = container_of(work, struct device, power.work);
enum rpm_request req; spin_lock_irq(&dev->power.lock); if (!dev->power.request_pending)
goto out; req = dev->power.request;
dev->power.request = RPM_REQ_NONE;
dev->power.request_pending = false; switch (req) {
case RPM_REQ_NONE:
break;
case RPM_REQ_IDLE:
__pm_runtime_idle(dev);
break;
case RPM_REQ_SUSPEND:
__pm_runtime_suspend(dev, true); //呼叫suspend函式
break;
case RPM_REQ_RESUME:
__pm_runtime_resume(dev, true);
break;
} out:
spin_unlock_irq(&dev->power.lock);
} int __pm_runtime_suspend(struct device *dev, bool from_wq)
__releases(&dev->power.lock) __acquires(&dev->power.lock)
{
struct device *parent = NULL;
bool notify = false;
int retval = 0; dev_dbg(dev, "__pm_runtime_suspend()%s!\n",from_wq ? " from workqueue" : ""); ................... if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_suspend) {
spin_unlock_irq(&dev->power.lock); retval = dev->bus->pm->runtime_suspend(dev);//呼叫usb匯流排的runtime_suspend函式 spin_lock_irq(&dev->power.lock);
dev->power.runtime_error = retval;
}
................... out:
dev_dbg(dev, "__pm_runtime_suspend() returns %d!\n", retval); return retval;
} static int usb_runtime_suspend(struct device *dev)
{
int status = 0; if (is_usb_device(dev)) {
struct usb_device *udev = to_usb_device(dev); if (autosuspend_check(udev) != 0)
return -EAGAIN; status = usb_suspend_both(udev, PMSG_AUTO_SUSPEND);//3 ...............
return status;
} static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
{
int status = 0;
int i = 0, n = 0;
struct usb_interface *intf; if (udev->state == USB_STATE_NOTATTACHED ||
udev->state == USB_STATE_SUSPENDED)
goto done; /* Suspend all the interfaces and then udev itself */
if (udev->actconfig) {//掛起所以埠
n = udev->actconfig->desc.bNumInterfaces;
for (i = n - 1; i >= 0; --i) {
intf = udev->actconfig->interface[i];
status = usb_suspend_interface(udev, intf, msg);
if (status != 0)
break;
}
}
if (status == 0)
status = usb_suspend_device(udev, msg);//掛起usb裝置本身
..............
} else { udev->can_submit = 0;
for (i = 0; i < 16; ++i) {
usb_hcd_flush_endpoint(udev, udev->ep_out[i]);
usb_hcd_flush_endpoint(udev, udev->ep_in[i]);
}
} done:
dev_vdbg(&udev->dev, "%s: status %d\n", __func__, status);
return status;
} static int usb_suspend_device(struct usb_device *udev, pm_message_t msg)
{
if (udev->dev.driver)
udriver = to_usb_device_driver(udev->dev.driver);
else {
udev->do_remote_wakeup = 0;
udriver = &usb_generic_driver;
}
status = udriver->suspend(udev, msg);//5 done:
dev_vdbg(&udev->dev, "%s: status %d\n", __func__, status);
return status;
} struct usb_device_driver usb_generic_driver = {
.name = "usb",
.probe = generic_probe,
.disconnect = generic_disconnect,
#ifdef CONFIG_PM
.suspend = generic_suspend,//6
.resume = generic_resume,
#endif
.supports_autosuspend = 1,
}; static int generic_suspend(struct usb_device *udev, pm_message_t msg)
{
int rc; if (!udev->parent)
rc = hcd_bus_suspend(udev, msg);//7 /* Non-root devices don't need to do anything for FREEZE or PRETHAW */
else if (msg.event == PM_EVENT_FREEZE || msg.event == PM_EVENT_PRETHAW)
rc = 0;
else
rc = usb_port_suspend(udev, msg); return rc;
} *************************************************** /*通用usb裝置的suspend函式,上面的自動掛起和正常掛起都會走*/
static int generic_suspend(struct usb_device *udev, pm_message_t msg)
{
int rc; /* Normal USB devices suspend through their upstream port.
* Root hubs don't have upstream ports to suspend,
* so we have to shut down their downstream HC-to-USB
* interfaces manually by doing a bus (or "global") suspend.
*/
if (!udev->parent)//針對root hub
rc = hcd_bus_suspend(udev, msg);//掛起root hub的下流口 /* Non-root devices don't need to do anything for FREEZE or PRETHAW */
else if (msg.event == PM_EVENT_FREEZE || msg.event == PM_EVENT_PRETHAW)
rc = 0;
else
rc = usb_port_suspend(udev, msg);//關閉usb裝置的上流口 return rc;
} int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
{
struct usb_hub *hub = hdev_to_hub(udev->parent);
int port1 = udev->portnum;
int status; // dev_dbg(hub->intfdev, "suspend port %d\n", port1); /* enable remote wakeup when appropriate; this lets the device
* wake up the upstream hub (including maybe the root hub).
*
* NOTE: OTG devices may issue remote wakeup (or SRP) even when
* we don't explicitly enable it here.
*/
if (udev->do_remote_wakeup) {
status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
USB_REQ_SET_FEATURE, USB_RECIP_DEVICE,
USB_DEVICE_REMOTE_WAKEUP, 0,
NULL, 0,
USB_CTRL_SET_TIMEOUT);
} /* see 7.1.7.6 */
status = set_port_feature(hub->hdev, port1, USB_PORT_FEAT_SUSPEND);
/* device has up to 10 msec to fully suspend */
dev_dbg(&udev->dev, "usb %ssuspend\n",(msg.event & PM_EVENT_AUTO ? "auto-" : ""));
usb_set_device_state(udev, USB_STATE_SUSPENDED);//設定usb裝置為掛起狀態
msleep(10);
}
return status;
} /*掛起root hub*/ int hcd_bus_suspend(struct usb_device *rhdev, pm_message_t msg)
{
struct usb_hcd *hcd = container_of(rhdev->bus, struct usb_hcd, self);
int status;
int old_state = hcd->state; dev_dbg(&rhdev->dev, "bus %s%s\n",
(msg.event & PM_EVENT_AUTO ? "auto-" : ""), "suspend");
if (!hcd->driver->bus_suspend) {
status = -ENOENT;
} else {
hcd->state = HC_STATE_QUIESCING;
status = hcd->driver->bus_suspend(hcd);//呼叫主機控制器driver的bus suspend
}
if (status == 0) {
usb_set_device_state(rhdev, USB_STATE_SUSPENDED);//設定root hub裝置的狀態為suspend狀態
hcd->state = HC_STATE_SUSPENDED;
} else {
hcd->state = old_state;
dev_dbg(&rhdev->dev, "bus %s fail, err %d\n","suspend", status);
}
return status;
} static struct hc_driver msm_hc_driver = {
.description = hcd_name,
.product_desc = "Qualcomm On-Chip EHCI Host Controller",
.hcd_priv_size = sizeof(struct msmusb_hcd), /*
* generic hardware linkage
*/
.irq = ehci_msm_irq,
.flags = HCD_USB2, .reset = ehci_msm_reset,
.start = ehci_msm_run, .stop = ehci_stop,
.shutdown = ehci_shutdown, /*
* managing i/o requests and associated device resources
*/
.urb_enqueue = ehci_urb_enqueue,
.urb_dequeue = ehci_urb_dequeue,
.endpoint_disable = ehci_endpoint_disable, /*
* scheduling support
*/
.get_frame_number = ehci_get_frame, /*
* root hub support
*/
.hub_status_data = ehci_hub_status_data,
.hub_control = ehci_hub_control,
.bus_suspend = ehci_msm_bus_suspend,//呼叫該suspend函式
.bus_resume = ehci_msm_bus_resume,
.relinquish_port = ehci_relinquish_port, .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
}; static int ehci_msm_bus_suspend(struct usb_hcd *hcd)
{
int ret;
struct msmusb_hcd *mhcd = hcd_to_mhcd(hcd);
struct device *dev = hcd->self.controller; ret = ehci_bus_suspend(hcd);//root hub掛起
if (ret) {
pr_err("ehci_bus suspend faield\n");
return ret;
} /*下面是掛起otg控制器*/
if (PHY_TYPE(mhcd->pdata->phy_info) == USB_PHY_INTEGRATED)
ret = otg_set_suspend(mhcd->xceiv, 1);//otg掛起,呼叫該函式
else
ret = usb_lpm_enter(hcd); pm_runtime_put_noidle(dev);
pm_runtime_suspend(dev);
wake_unlock(&mhcd->wlock);
return ret;
} static inline int otg_set_suspend(struct otg_transceiver *otg, int suspend)
{
if (otg->set_suspend != NULL)
return otg->set_suspend(otg, suspend);
else
return 0;
} dev->otg.set_suspend = msm_otg_set_suspend;
static int msm_otg_set_suspend(struct otg_transceiver *xceiv, int suspend)
{
struct msm_otg *dev = container_of(xceiv, struct msm_otg, otg);
enum usb_otg_state state;
unsigned long flags; if (!dev || (dev != the_msm_otg))
return -ENODEV; spin_lock_irqsave(&dev->lock, flags);
state = dev->otg.state;
spin_unlock_irqrestore(&dev->lock, flags); pr_debug("suspend request in state: %s\n",
state_string(state)); if (suspend) {
switch (state) {
#ifndef CONFIG_MSM_OTG_ENABLE_A_WAIT_BCON_TIMEOUT
case OTG_STATE_A_WAIT_BCON:
if (test_bit(ID_A, &dev->inputs))
msm_otg_set_power(xceiv, USB_IDCHG_MIN - 100);
msm_otg_put_suspend(dev);//如果沒有usb裝置連線,則進入該函式
break;
#endif
case OTG_STATE_A_HOST:
clear_bit(A_BUS_REQ, &dev->inputs);
wake_lock(&dev->wlock);
queue_work(dev->wq, &dev->sm_work);
break;
case OTG_STATE_B_PERIPHERAL:
if (xceiv->gadget->b_hnp_enable) {
set_bit(A_BUS_SUSPEND, &dev->inputs);
set_bit(B_BUS_REQ, &dev->inputs);
wake_lock(&dev->wlock);
queue_work(dev->wq, &dev->sm_work);
}
break;
case OTG_STATE_A_PERIPHERAL:
msm_otg_start_timer(dev, TA_BIDL_ADIS,
A_BIDL_ADIS);
break;
default:
break;
}
} else {
unsigned long timeout; switch (state) {
case OTG_STATE_A_PERIPHERAL:
/* A-peripheral observed activity on bus.
* clear A_BIDL_ADIS timer.
*/
msm_otg_del_timer(dev);
break;
case OTG_STATE_A_SUSPEND:
/* Remote wakeup or resume */
set_bit(A_BUS_REQ, &dev->inputs);
spin_lock_irqsave(&dev->lock, flags);
dev->otg.state = OTG_STATE_A_HOST;
spin_unlock_irqrestore(&dev->lock, flags);
if (test_bit(ID_A, &dev->inputs) &&
(get_aca_bmaxpower(dev) < USB_IDCHG_MIN))
msm_otg_set_power(xceiv,
USB_IDCHG_MIN - get_aca_bmaxpower(dev));
break;
default:
break;
} if (suspend == atomic_read(&dev->in_lpm))
return 0; disable_irq(dev->irq);
if (dev->pmic_vbus_notif_supp)
if (can_phy_power_collapse(dev) &&
dev->pdata->ldo_enable)
dev->pdata->ldo_enable(1); msm_otg_get_resume(dev); if (!is_phy_clk_disabled())
goto out; timeout = jiffies + usecs_to_jiffies(100);
enable_phy_clk();
while (is_phy_clk_disabled()) {
if (time_after(jiffies, timeout)) {
pr_err("%s: Unable to wakeup phy\n", __func__);
/* Reset both phy and link */
otg_reset(&dev->otg, 1);
break;
}
udelay(10);
}
if (dev->pmic_id_notif_supp) {
dev->pdata->pmic_id_notif_init(
&msm_otg_set_id_state, 0);
dev->pmic_id_notif_supp = 0;
enable_idgnd(dev);
}
out:
enable_idabc(dev);
enable_irq(dev->irq); } return 0;
} //上面是主機控制器呼叫到的該流程. //裝置控制器直接呼叫該函式是裝置進入suspend狀態
/***************************************************************/
static void msm_otg_put_suspend(struct msm_otg *dev)
{
#ifdef CONFIG_PM_RUNTIME
pm_runtime_put_sync(dev->otg.dev);//呼叫該函式,注意:傳入的是平臺裝置
if (!atomic_read(&dev->in_lpm))
pm_runtime_get_sync(dev->otg.dev);
#else
msm_otg_suspend(dev);
#endif
} static inline int pm_runtime_put_sync(struct device *dev)
{
return __pm_runtime_put(dev, true);//呼叫該函式
} int __pm_runtime_put(struct device *dev, bool sync)
{
int retval = 0; if (atomic_dec_and_test(&dev->power.usage_count))
retval = sync ? pm_runtime_idle(dev)/*呼叫該函式*/: pm_request_idle(dev); return retval;
} int pm_runtime_idle(struct device *dev)
{
int retval; spin_lock_irq(&dev->power.lock);
retval = __pm_runtime_idle(dev);//呼叫該函式
spin_unlock_irq(&dev->power.lock); return retval;
} static int __pm_runtime_idle(struct device *dev)
__releases(&dev->power.lock) __acquires(&dev->power.lock)
{
int retval = 0;
............... dev->power.idle_notification = true; if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_idle) {
spin_unlock_irq(&dev->power.lock); dev->bus->pm->runtime_idle(dev);//呼叫該函式 spin_lock_irq(&dev->power.lock);
} else if (dev->type && dev->type->pm && dev->type->pm->runtime_idle) {
spin_unlock_irq(&dev->power.lock); dev->type->pm->runtime_idle(dev); spin_lock_irq(&dev->power.lock);
} else if (dev->class && dev->class->pm
&& dev->class->pm->runtime_idle) {
spin_unlock_irq(&dev->power.lock); dev->class->pm->runtime_idle(dev); spin_lock_irq(&dev->power.lock);
} dev->power.idle_notification = false;
wake_up_all(&dev->power.wait_queue); out:
return retval;
} static const struct dev_pm_ops platform_dev_pm_ops = {
.prepare = platform_pm_prepare,
.complete = platform_pm_complete,
.suspend = platform_pm_suspend,
.resume = platform_pm_resume,
.freeze = platform_pm_freeze,
.thaw = platform_pm_thaw,
.poweroff = platform_pm_poweroff,
.restore = platform_pm_restore,
.suspend_noirq = platform_pm_suspend_noirq,
.resume_noirq = platform_pm_resume_noirq,
.freeze_noirq = platform_pm_freeze_noirq,
.thaw_noirq = platform_pm_thaw_noirq,
.poweroff_noirq = platform_pm_poweroff_noirq,
.restore_noirq = platform_pm_restore_noirq,
.runtime_suspend = platform_pm_runtime_suspend,
.runtime_resume = platform_pm_runtime_resume,
.runtime_idle = platform_pm_runtime_idle,//呼叫該處的函式
}; struct bus_type platform_bus_type = {
.name = "platform",
.dev_attrs = platform_dev_attrs,
.match = platform_match,
.uevent = platform_uevent,
.pm = &platform_dev_pm_ops,
}; int __weak platform_pm_runtime_idle(struct device *dev)
{
return pm_generic_runtime_idle(dev);//呼叫該函式
}; int pm_generic_runtime_idle(struct device *dev)
{
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; if (pm && pm->runtime_idle) {
int ret = pm->runtime_idle(dev);
if (ret)
return ret;
} pm_runtime_suspend(dev);//呼叫該函式
return 0;
} int pm_runtime_suspend(struct device *dev)
{
int retval; spin_lock_irq(&dev->power.lock);
retval = __pm_runtime_suspend(dev, false);//呼叫該函式
spin_unlock_irq(&dev->power.lock); return retval;
} int __pm_runtime_suspend(struct device *dev, bool from_wq)
__releases(&dev->power.lock) __acquires(&dev->power.lock)
{
struct device *parent = NULL;
bool notify = false;
int retval = 0; dev_dbg(dev, "__pm_runtime_suspend()%s!\n",from_wq ? " from workqueue" : ""); repeat:
dev->power.runtime_status = RPM_SUSPENDING;
dev->power.deferred_resume = false; if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_suspend) {
spin_unlock_irq(&dev->power.lock); retval = dev->bus->pm->runtime_suspend(dev); //呼叫平臺匯流排的suspend函式 spin_lock_irq(&dev->power.lock);
dev->power.runtime_error = retval;
} else if (dev->type && dev->type->pm
&& dev->type->pm->runtime_suspend) {
spin_unlock_irq(&dev->power.lock); retval = dev->type->pm->runtime_suspend(dev); spin_lock_irq(&dev->power.lock);
dev->power.runtime_error = retval;
} else if (dev->class && dev->class->pm
&& dev->class->pm->runtime_suspend) {
spin_unlock_irq(&dev->power.lock); retval = dev->class->pm->runtime_suspend(dev); spin_lock_irq(&dev->power.lock);
dev->power.runtime_error = retval;
} else {
retval = -ENOSYS;
} if (notify)
__pm_runtime_idle(dev); if (parent && !parent->power.ignore_children) {
spin_unlock_irq(&dev->power.lock); pm_request_idle(parent); spin_lock_irq(&dev->power.lock);
} out:
dev_dbg(dev, "__pm_runtime_suspend() returns %d!\n", retval); return retval;
} int __weak platform_pm_runtime_suspend(struct device *dev)
{
return pm_generic_runtime_suspend(dev);
}; int pm_generic_runtime_suspend(struct device *dev)
{
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
int ret; ret = pm && pm->runtime_suspend ? pm->runtime_suspend(dev) : -EINVAL;//呼叫平臺驅動的runtime_suspend函式 return ret;
} /*平臺驅動的註冊函式*/ /*******************************************************************/
static int msm_otg_runtime_suspend(struct device *dev)
{
struct msm_otg *otg = the_msm_otg; dev_dbg(dev, "pm_runtime: suspending...\n");
msm_otg_suspend(otg);
return 0;
} static int msm_otg_runtime_resume(struct device *dev)
{
struct msm_otg *otg = the_msm_otg; dev_dbg(dev, "pm_runtime: resuming...\n");
msm_otg_resume(otg);
return 0;
} static int msm_otg_runtime_idle(struct device *dev)
{
dev_dbg(dev, "pm_runtime: idling...\n");
return 0;
} static 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,
}; static struct platform_driver msm_otg_driver = {
.remove = __exit_p(msm_otg_remove),
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
.pm = &msm_otg_dev_pm_ops,
},
};
/*******************************************************************/
static int msm_otg_suspend(struct msm_otg *dev)
{
unsigned long timeout;
bool host_bus_suspend;
unsigned ret;
enum chg_type chg_type = atomic_read(&dev->chg_type);
unsigned long flags; disable_irq(dev->irq);
if (atomic_read(&dev->in_lpm))
goto out; atomic_set(&dev->in_lpm, 1);//設定low power mode為1,喚醒的時候要判斷該標記 ............... if (dev->pdata->config_vddcx)
dev->pdata->config_vddcx(0);
pr_info("%s: usb in low power mode\n", __func__); return 0;
}
到此整個睡眠流程分析完了。