mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-06 19:08:57 +09:00
ANDROID: usb: otg: Temporarily grab wakelock on charger and disconnect events
Change-Id: If995d4af4adcb08e8369009483f2956ad9627267 Signed-off-by: Todd Poynor <toddpoynor@google.com>
This commit is contained in:
committed by
Dmitry Shmidt
parent
21b36ead66
commit
2c7e32437e
@@ -21,13 +21,15 @@
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/usb/otg.h>
|
||||
|
||||
#define TEMPORARY_HOLD_TIME 2000
|
||||
|
||||
static bool enabled = true;
|
||||
static struct otg_transceiver *otgwl_xceiv;
|
||||
static struct notifier_block otgwl_nb;
|
||||
|
||||
/*
|
||||
* otgwl_spinlock is held while the VBUS lock is grabbed or dropped and the
|
||||
* locked field is updated to match.
|
||||
* held field is updated to match.
|
||||
*/
|
||||
|
||||
static DEFINE_SPINLOCK(otgwl_spinlock);
|
||||
@@ -39,51 +41,62 @@ static DEFINE_SPINLOCK(otgwl_spinlock);
|
||||
struct otgwl_lock {
|
||||
char name[40];
|
||||
struct wake_lock wakelock;
|
||||
bool locked;
|
||||
bool held;
|
||||
};
|
||||
|
||||
/*
|
||||
* VBUS present lock.
|
||||
* VBUS present lock. Also used as a timed lock on charger
|
||||
* connect/disconnect and USB host disconnect, to allow the system
|
||||
* to react to the change in power.
|
||||
*/
|
||||
|
||||
static struct otgwl_lock vbus_lock;
|
||||
|
||||
static void otgwl_grab(struct otgwl_lock *lock)
|
||||
static void otgwl_hold(struct otgwl_lock *lock)
|
||||
{
|
||||
if (!lock->locked) {
|
||||
if (!lock->held) {
|
||||
wake_lock(&lock->wakelock);
|
||||
lock->locked = true;
|
||||
lock->held = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void otgwl_temporary_hold(struct otgwl_lock *lock)
|
||||
{
|
||||
wake_lock_timeout(&lock->wakelock,
|
||||
msecs_to_jiffies(TEMPORARY_HOLD_TIME));
|
||||
lock->held = false;
|
||||
}
|
||||
|
||||
static void otgwl_drop(struct otgwl_lock *lock)
|
||||
{
|
||||
if (lock->locked) {
|
||||
if (lock->held) {
|
||||
wake_unlock(&lock->wakelock);
|
||||
lock->locked = false;
|
||||
lock->held = false;
|
||||
}
|
||||
}
|
||||
|
||||
static int otgwl_otg_notifications(struct notifier_block *nb,
|
||||
unsigned long event, void *unused)
|
||||
static void otgwl_handle_event(unsigned long event)
|
||||
{
|
||||
unsigned long irqflags;
|
||||
|
||||
if (!enabled)
|
||||
return NOTIFY_OK;
|
||||
|
||||
spin_lock_irqsave(&otgwl_spinlock, irqflags);
|
||||
|
||||
if (!enabled) {
|
||||
otgwl_drop(&vbus_lock);
|
||||
spin_unlock_irqrestore(&otgwl_spinlock, irqflags);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (event) {
|
||||
case USB_EVENT_VBUS:
|
||||
case USB_EVENT_ENUMERATED:
|
||||
otgwl_grab(&vbus_lock);
|
||||
otgwl_hold(&vbus_lock);
|
||||
break;
|
||||
|
||||
case USB_EVENT_NONE:
|
||||
case USB_EVENT_ID:
|
||||
case USB_EVENT_CHARGER:
|
||||
otgwl_drop(&vbus_lock);
|
||||
otgwl_temporary_hold(&vbus_lock);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -91,71 +104,25 @@ static int otgwl_otg_notifications(struct notifier_block *nb,
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&otgwl_spinlock, irqflags);
|
||||
}
|
||||
|
||||
static int otgwl_otg_notifications(struct notifier_block *nb,
|
||||
unsigned long event, void *unused)
|
||||
{
|
||||
otgwl_handle_event(event);
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static void sync_with_xceiv_state(void)
|
||||
{
|
||||
if ((otgwl_xceiv->last_event == USB_EVENT_VBUS) ||
|
||||
(otgwl_xceiv->last_event == USB_EVENT_ENUMERATED))
|
||||
otgwl_grab(&vbus_lock);
|
||||
else
|
||||
otgwl_drop(&vbus_lock);
|
||||
}
|
||||
|
||||
static int init_for_xceiv(void)
|
||||
{
|
||||
int rv;
|
||||
|
||||
if (!otgwl_xceiv) {
|
||||
otgwl_xceiv = otg_get_transceiver();
|
||||
|
||||
if (!otgwl_xceiv) {
|
||||
pr_err("%s: No OTG transceiver found\n", __func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
snprintf(vbus_lock.name, sizeof(vbus_lock.name), "vbus-%s",
|
||||
dev_name(otgwl_xceiv->dev));
|
||||
wake_lock_init(&vbus_lock.wakelock, WAKE_LOCK_SUSPEND,
|
||||
vbus_lock.name);
|
||||
|
||||
rv = otg_register_notifier(otgwl_xceiv, &otgwl_nb);
|
||||
|
||||
if (rv) {
|
||||
pr_err("%s: otg_register_notifier on transceiver %s"
|
||||
" failed\n", __func__,
|
||||
dev_name(otgwl_xceiv->dev));
|
||||
otgwl_xceiv = NULL;
|
||||
wake_lock_destroy(&vbus_lock.wakelock);
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_enabled(const char *val, const struct kernel_param *kp)
|
||||
{
|
||||
unsigned long irqflags;
|
||||
int rv = param_set_bool(val, kp);
|
||||
|
||||
if (rv)
|
||||
return rv;
|
||||
|
||||
rv = init_for_xceiv();
|
||||
if (otgwl_xceiv)
|
||||
otgwl_handle_event(otgwl_xceiv->last_event);
|
||||
|
||||
if (rv)
|
||||
return rv;
|
||||
|
||||
spin_lock_irqsave(&otgwl_spinlock, irqflags);
|
||||
|
||||
if (enabled)
|
||||
sync_with_xceiv_state();
|
||||
else
|
||||
otgwl_drop(&vbus_lock);
|
||||
|
||||
spin_unlock_irqrestore(&otgwl_spinlock, irqflags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -169,22 +136,34 @@ MODULE_PARM_DESC(enabled, "enable wakelock when VBUS present");
|
||||
|
||||
static int __init otg_wakelock_init(void)
|
||||
{
|
||||
unsigned long irqflags;
|
||||
int ret;
|
||||
|
||||
otgwl_nb.notifier_call = otgwl_otg_notifications;
|
||||
otgwl_xceiv = otg_get_transceiver();
|
||||
|
||||
if (!init_for_xceiv()) {
|
||||
spin_lock_irqsave(&otgwl_spinlock, irqflags);
|
||||
|
||||
if (enabled)
|
||||
sync_with_xceiv_state();
|
||||
|
||||
spin_unlock_irqrestore(&otgwl_spinlock, irqflags);
|
||||
} else {
|
||||
enabled = false;
|
||||
if (!otgwl_xceiv) {
|
||||
pr_err("%s: No OTG transceiver found\n", __func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
snprintf(vbus_lock.name, sizeof(vbus_lock.name), "vbus-%s",
|
||||
dev_name(otgwl_xceiv->dev));
|
||||
wake_lock_init(&vbus_lock.wakelock, WAKE_LOCK_SUSPEND,
|
||||
vbus_lock.name);
|
||||
|
||||
otgwl_nb.notifier_call = otgwl_otg_notifications;
|
||||
ret = otg_register_notifier(otgwl_xceiv, &otgwl_nb);
|
||||
|
||||
if (ret) {
|
||||
pr_err("%s: otg_register_notifier on transceiver %s"
|
||||
" failed\n", __func__,
|
||||
dev_name(otgwl_xceiv->dev));
|
||||
otgwl_xceiv = NULL;
|
||||
wake_lock_destroy(&vbus_lock.wakelock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
otgwl_handle_event(otgwl_xceiv->last_event);
|
||||
return ret;
|
||||
}
|
||||
|
||||
late_initcall(otg_wakelock_init);
|
||||
|
||||
Reference in New Issue
Block a user