19ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor/*
29ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor * otg-wakelock.c
39ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor *
49ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor * Copyright (C) 2011 Google, Inc.
59ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor *
69ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor * This software is licensed under the terms of the GNU General Public
79ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor * License version 2, as published by the Free Software Foundation, and
89ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor * may be copied, distributed, and modified under those terms.
99ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor *
109ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor * This program is distributed in the hope that it will be useful,
119ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor * but WITHOUT ANY WARRANTY; without even the implied warranty of
129ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
139ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor * GNU General Public License for more details.
149ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor *
159ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor */
169ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor
179ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor#include <linux/kernel.h>
189ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor#include <linux/device.h>
19bdcd44a4fde20df00cc697036f724a2a25a523e9Arve Hjønnevåg#include <linux/err.h>
200386973f25737cbf29343dba9a08a5335f16c77bColin Cross#include <linux/module.h>
219ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor#include <linux/notifier.h>
229ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor#include <linux/wakelock.h>
239ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor#include <linux/spinlock.h>
249ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor#include <linux/usb/otg.h>
259ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor
2676abd55f4f733d64f078206f618bc3e35e0ac83dTodd Poynor#define TEMPORARY_HOLD_TIME	2000
2776abd55f4f733d64f078206f618bc3e35e0ac83dTodd Poynor
289ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynorstatic bool enabled = true;
298bbe0d9f5d5e09f4d496da42c3835b4102c98178Benoit Gobystatic struct usb_phy *otgwl_xceiv;
309ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynorstatic struct notifier_block otgwl_nb;
319ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor
329ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor/*
339ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor * otgwl_spinlock is held while the VBUS lock is grabbed or dropped and the
3476abd55f4f733d64f078206f618bc3e35e0ac83dTodd Poynor * held field is updated to match.
359ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor */
369ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor
379ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynorstatic DEFINE_SPINLOCK(otgwl_spinlock);
389ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor
399ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor/*
409ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor * Only one lock, but since these 3 fields are associated with each other...
419ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor */
429ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor
439ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynorstruct otgwl_lock {
449ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor	char name[40];
459ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor	struct wake_lock wakelock;
4676abd55f4f733d64f078206f618bc3e35e0ac83dTodd Poynor	bool held;
479ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor};
489ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor
499ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor/*
5076abd55f4f733d64f078206f618bc3e35e0ac83dTodd Poynor * VBUS present lock.  Also used as a timed lock on charger
5176abd55f4f733d64f078206f618bc3e35e0ac83dTodd Poynor * connect/disconnect and USB host disconnect, to allow the system
5276abd55f4f733d64f078206f618bc3e35e0ac83dTodd Poynor * to react to the change in power.
539ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor */
549ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor
559ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynorstatic struct otgwl_lock vbus_lock;
569ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor
5776abd55f4f733d64f078206f618bc3e35e0ac83dTodd Poynorstatic void otgwl_hold(struct otgwl_lock *lock)
589ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor{
5976abd55f4f733d64f078206f618bc3e35e0ac83dTodd Poynor	if (!lock->held) {
609ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor		wake_lock(&lock->wakelock);
6176abd55f4f733d64f078206f618bc3e35e0ac83dTodd Poynor		lock->held = true;
629ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor	}
639ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor}
649ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor
6576abd55f4f733d64f078206f618bc3e35e0ac83dTodd Poynorstatic void otgwl_temporary_hold(struct otgwl_lock *lock)
6676abd55f4f733d64f078206f618bc3e35e0ac83dTodd Poynor{
6776abd55f4f733d64f078206f618bc3e35e0ac83dTodd Poynor	wake_lock_timeout(&lock->wakelock,
6876abd55f4f733d64f078206f618bc3e35e0ac83dTodd Poynor			  msecs_to_jiffies(TEMPORARY_HOLD_TIME));
6976abd55f4f733d64f078206f618bc3e35e0ac83dTodd Poynor	lock->held = false;
7076abd55f4f733d64f078206f618bc3e35e0ac83dTodd Poynor}
7176abd55f4f733d64f078206f618bc3e35e0ac83dTodd Poynor
729ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynorstatic void otgwl_drop(struct otgwl_lock *lock)
739ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor{
7476abd55f4f733d64f078206f618bc3e35e0ac83dTodd Poynor	if (lock->held) {
759ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor		wake_unlock(&lock->wakelock);
7676abd55f4f733d64f078206f618bc3e35e0ac83dTodd Poynor		lock->held = false;
779ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor	}
789ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor}
799ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor
8076abd55f4f733d64f078206f618bc3e35e0ac83dTodd Poynorstatic void otgwl_handle_event(unsigned long event)
819ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor{
829ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor	unsigned long irqflags;
839ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor
849ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor	spin_lock_irqsave(&otgwl_spinlock, irqflags);
859ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor
8676abd55f4f733d64f078206f618bc3e35e0ac83dTodd Poynor	if (!enabled) {
8776abd55f4f733d64f078206f618bc3e35e0ac83dTodd Poynor		otgwl_drop(&vbus_lock);
8876abd55f4f733d64f078206f618bc3e35e0ac83dTodd Poynor		spin_unlock_irqrestore(&otgwl_spinlock, irqflags);
8976abd55f4f733d64f078206f618bc3e35e0ac83dTodd Poynor		return;
9076abd55f4f733d64f078206f618bc3e35e0ac83dTodd Poynor	}
9176abd55f4f733d64f078206f618bc3e35e0ac83dTodd Poynor
929ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor	switch (event) {
939ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor	case USB_EVENT_VBUS:
949ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor	case USB_EVENT_ENUMERATED:
9576abd55f4f733d64f078206f618bc3e35e0ac83dTodd Poynor		otgwl_hold(&vbus_lock);
969ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor		break;
979ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor
989ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor	case USB_EVENT_NONE:
999ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor	case USB_EVENT_ID:
1009ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor	case USB_EVENT_CHARGER:
10176abd55f4f733d64f078206f618bc3e35e0ac83dTodd Poynor		otgwl_temporary_hold(&vbus_lock);
1029ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor		break;
1039ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor
1049ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor	default:
1059ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor		break;
1069ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor	}
1079ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor
1089ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor	spin_unlock_irqrestore(&otgwl_spinlock, irqflags);
1099ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor}
1109ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor
11176abd55f4f733d64f078206f618bc3e35e0ac83dTodd Poynorstatic int otgwl_otg_notifications(struct notifier_block *nb,
11276abd55f4f733d64f078206f618bc3e35e0ac83dTodd Poynor				   unsigned long event, void *unused)
1139ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor{
11476abd55f4f733d64f078206f618bc3e35e0ac83dTodd Poynor	otgwl_handle_event(event);
11576abd55f4f733d64f078206f618bc3e35e0ac83dTodd Poynor	return NOTIFY_OK;
1169ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor}
1179ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor
1189ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynorstatic int set_enabled(const char *val, const struct kernel_param *kp)
1199ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor{
1209ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor	int rv = param_set_bool(val, kp);
1219ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor
1229ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor	if (rv)
1239ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor		return rv;
1249ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor
12576abd55f4f733d64f078206f618bc3e35e0ac83dTodd Poynor	if (otgwl_xceiv)
12676abd55f4f733d64f078206f618bc3e35e0ac83dTodd Poynor		otgwl_handle_event(otgwl_xceiv->last_event);
1279ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor
1289ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor	return 0;
1299ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor}
1309ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor
1319ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynorstatic struct kernel_param_ops enabled_param_ops = {
1329ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor	.set = set_enabled,
1339ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor	.get = param_get_bool,
1349ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor};
1359ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor
1369ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynormodule_param_cb(enabled, &enabled_param_ops, &enabled, 0644);
1379ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd PoynorMODULE_PARM_DESC(enabled, "enable wakelock when VBUS present");
1389ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor
1399ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynorstatic int __init otg_wakelock_init(void)
1409ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor{
14176abd55f4f733d64f078206f618bc3e35e0ac83dTodd Poynor	int ret;
142bdcd44a4fde20df00cc697036f724a2a25a523e9Arve Hjønnevåg	struct usb_phy *phy;
1439ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor
144bdcd44a4fde20df00cc697036f724a2a25a523e9Arve Hjønnevåg	phy = usb_get_phy(USB_PHY_TYPE_USB2);
1459ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor
146bdcd44a4fde20df00cc697036f724a2a25a523e9Arve Hjønnevåg	if (IS_ERR(phy)) {
1478bbe0d9f5d5e09f4d496da42c3835b4102c98178Benoit Goby		pr_err("%s: No USB transceiver found\n", __func__);
148bdcd44a4fde20df00cc697036f724a2a25a523e9Arve Hjønnevåg		return PTR_ERR(phy);
14976abd55f4f733d64f078206f618bc3e35e0ac83dTodd Poynor	}
150bdcd44a4fde20df00cc697036f724a2a25a523e9Arve Hjønnevåg	otgwl_xceiv = phy;
1519ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor
15276abd55f4f733d64f078206f618bc3e35e0ac83dTodd Poynor	snprintf(vbus_lock.name, sizeof(vbus_lock.name), "vbus-%s",
15376abd55f4f733d64f078206f618bc3e35e0ac83dTodd Poynor		 dev_name(otgwl_xceiv->dev));
15476abd55f4f733d64f078206f618bc3e35e0ac83dTodd Poynor	wake_lock_init(&vbus_lock.wakelock, WAKE_LOCK_SUSPEND,
15576abd55f4f733d64f078206f618bc3e35e0ac83dTodd Poynor		       vbus_lock.name);
1569ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor
15776abd55f4f733d64f078206f618bc3e35e0ac83dTodd Poynor	otgwl_nb.notifier_call = otgwl_otg_notifications;
1588bbe0d9f5d5e09f4d496da42c3835b4102c98178Benoit Goby	ret = usb_register_notifier(otgwl_xceiv, &otgwl_nb);
15976abd55f4f733d64f078206f618bc3e35e0ac83dTodd Poynor
16076abd55f4f733d64f078206f618bc3e35e0ac83dTodd Poynor	if (ret) {
1618bbe0d9f5d5e09f4d496da42c3835b4102c98178Benoit Goby		pr_err("%s: usb_register_notifier on transceiver %s"
16276abd55f4f733d64f078206f618bc3e35e0ac83dTodd Poynor		       " failed\n", __func__,
16376abd55f4f733d64f078206f618bc3e35e0ac83dTodd Poynor		       dev_name(otgwl_xceiv->dev));
16476abd55f4f733d64f078206f618bc3e35e0ac83dTodd Poynor		otgwl_xceiv = NULL;
16576abd55f4f733d64f078206f618bc3e35e0ac83dTodd Poynor		wake_lock_destroy(&vbus_lock.wakelock);
16676abd55f4f733d64f078206f618bc3e35e0ac83dTodd Poynor		return ret;
1679ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor	}
1689ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor
16976abd55f4f733d64f078206f618bc3e35e0ac83dTodd Poynor	otgwl_handle_event(otgwl_xceiv->last_event);
17076abd55f4f733d64f078206f618bc3e35e0ac83dTodd Poynor	return ret;
1719ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor}
1729ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynor
1739ed0e9588b17f2582200bdee7661981ca1cc9bd2Todd Poynorlate_initcall(otg_wakelock_init);
174