usb: otg: otg-wakelock: Fix build for 3.4
[linux-2.6.git] / drivers / usb / otg / otg-wakelock.c
1 /*
2  * otg-wakelock.c
3  *
4  * Copyright (C) 2011 Google, Inc.
5  *
6  * This software is licensed under the terms of the GNU General Public
7  * License version 2, as published by the Free Software Foundation, and
8  * may be copied, distributed, and modified under those terms.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  */
16
17 #include <linux/kernel.h>
18 #include <linux/device.h>
19 #include <linux/module.h>
20 #include <linux/notifier.h>
21 #include <linux/wakelock.h>
22 #include <linux/spinlock.h>
23 #include <linux/usb/otg.h>
24
25 #define TEMPORARY_HOLD_TIME     2000
26
27 static bool enabled = true;
28 static struct usb_phy *otgwl_xceiv;
29 static struct notifier_block otgwl_nb;
30
31 /*
32  * otgwl_spinlock is held while the VBUS lock is grabbed or dropped and the
33  * held field is updated to match.
34  */
35
36 static DEFINE_SPINLOCK(otgwl_spinlock);
37
38 /*
39  * Only one lock, but since these 3 fields are associated with each other...
40  */
41
42 struct otgwl_lock {
43         char name[40];
44         struct wake_lock wakelock;
45         bool held;
46 };
47
48 /*
49  * VBUS present lock.  Also used as a timed lock on charger
50  * connect/disconnect and USB host disconnect, to allow the system
51  * to react to the change in power.
52  */
53
54 static struct otgwl_lock vbus_lock;
55
56 static void otgwl_hold(struct otgwl_lock *lock)
57 {
58         if (!lock->held) {
59                 wake_lock(&lock->wakelock);
60                 lock->held = true;
61         }
62 }
63
64 static void otgwl_temporary_hold(struct otgwl_lock *lock)
65 {
66         wake_lock_timeout(&lock->wakelock,
67                           msecs_to_jiffies(TEMPORARY_HOLD_TIME));
68         lock->held = false;
69 }
70
71 static void otgwl_drop(struct otgwl_lock *lock)
72 {
73         if (lock->held) {
74                 wake_unlock(&lock->wakelock);
75                 lock->held = false;
76         }
77 }
78
79 static void otgwl_handle_event(unsigned long event)
80 {
81         unsigned long irqflags;
82
83         spin_lock_irqsave(&otgwl_spinlock, irqflags);
84
85         if (!enabled) {
86                 otgwl_drop(&vbus_lock);
87                 spin_unlock_irqrestore(&otgwl_spinlock, irqflags);
88                 return;
89         }
90
91         switch (event) {
92         case USB_EVENT_VBUS:
93         case USB_EVENT_ENUMERATED:
94                 otgwl_hold(&vbus_lock);
95                 break;
96
97         case USB_EVENT_NONE:
98         case USB_EVENT_ID:
99         case USB_EVENT_CHARGER:
100                 otgwl_temporary_hold(&vbus_lock);
101                 break;
102
103         default:
104                 break;
105         }
106
107         spin_unlock_irqrestore(&otgwl_spinlock, irqflags);
108 }
109
110 static int otgwl_otg_notifications(struct notifier_block *nb,
111                                    unsigned long event, void *unused)
112 {
113         otgwl_handle_event(event);
114         return NOTIFY_OK;
115 }
116
117 static int set_enabled(const char *val, const struct kernel_param *kp)
118 {
119         int rv = param_set_bool(val, kp);
120
121         if (rv)
122                 return rv;
123
124         if (otgwl_xceiv)
125                 otgwl_handle_event(otgwl_xceiv->last_event);
126
127         return 0;
128 }
129
130 static struct kernel_param_ops enabled_param_ops = {
131         .set = set_enabled,
132         .get = param_get_bool,
133 };
134
135 module_param_cb(enabled, &enabled_param_ops, &enabled, 0644);
136 MODULE_PARM_DESC(enabled, "enable wakelock when VBUS present");
137
138 static int __init otg_wakelock_init(void)
139 {
140         int ret;
141
142         otgwl_xceiv = usb_get_transceiver();
143
144         if (!otgwl_xceiv) {
145                 pr_err("%s: No USB transceiver found\n", __func__);
146                 return -ENODEV;
147         }
148
149         snprintf(vbus_lock.name, sizeof(vbus_lock.name), "vbus-%s",
150                  dev_name(otgwl_xceiv->dev));
151         wake_lock_init(&vbus_lock.wakelock, WAKE_LOCK_SUSPEND,
152                        vbus_lock.name);
153
154         otgwl_nb.notifier_call = otgwl_otg_notifications;
155         ret = usb_register_notifier(otgwl_xceiv, &otgwl_nb);
156
157         if (ret) {
158                 pr_err("%s: usb_register_notifier on transceiver %s"
159                        " failed\n", __func__,
160                        dev_name(otgwl_xceiv->dev));
161                 otgwl_xceiv = NULL;
162                 wake_lock_destroy(&vbus_lock.wakelock);
163                 return ret;
164         }
165
166         otgwl_handle_event(otgwl_xceiv->last_event);
167         return ret;
168 }
169
170 late_initcall(otg_wakelock_init);