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