usb: cdc_ether: Add new product id for the 5AE profile
[linux-2.6.git] / arch / arm / mach-tegra / tegra_usb_modem_power.c
1 /*
2  * arch/arm/mach-tegra/tegra_usb_modem_power.c
3  *
4  * Copyright (c) 2011, NVIDIA Corporation.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
14  * more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program; if not, write to the Free Software Foundation, Inc.,
18  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19  */
20
21 #include <linux/module.h>
22 #include <linux/init.h>
23 #include <linux/interrupt.h>
24 #include <linux/platform_device.h>
25 #include <linux/workqueue.h>
26 #include <linux/gpio.h>
27 #include <linux/usb.h>
28 #include <linux/err.h>
29 #include <linux/wakelock.h>
30 #include <mach/tegra_usb_modem_power.h>
31
32 struct tegra_usb_modem {
33         unsigned int wake_gpio; /* remote wakeup gpio */
34         unsigned int wake_cnt;  /* remote wakeup counter */
35         int irq;                /* remote wakeup irq */
36         struct mutex lock;
37         struct wake_lock wake_lock;     /* modem wake lock */
38         unsigned int vid;       /* modem vendor id */
39         unsigned int pid;       /* modem product id */
40         struct usb_device *udev;        /* modem usb device */
41         struct usb_interface *intf;     /* first modem usb interface */
42         struct workqueue_struct *wq;    /* modem workqueue */
43         struct delayed_work recovery_work;      /* modem recovery work */
44         const struct tegra_modem_operations *ops;       /* modem operations */
45         unsigned int capability;        /* modem capability */
46 };
47
48 static struct tegra_usb_modem tegra_mdm;
49
50 /* supported modems */
51 static const struct usb_device_id modem_list[] = {
52         {USB_DEVICE(0x1983, 0x0310),    /* Icera 450 rev1 */
53          .driver_info = TEGRA_MODEM_AUTOSUSPEND,
54          },
55         {USB_DEVICE(0x1983, 0x0321),    /* Icera 450 rev2 */
56          .driver_info = TEGRA_MODEM_AUTOSUSPEND,
57          },
58         {USB_DEVICE(0x1983, 0x0327),    /* Icera 450 5AE */
59          .driver_info = TEGRA_MODEM_AUTOSUSPEND,
60          },
61         {}
62 };
63
64 static irqreturn_t tegra_usb_modem_wake_thread(int irq, void *data)
65 {
66         struct tegra_usb_modem *modem = (struct tegra_usb_modem *)data;
67
68         wake_lock_timeout(&modem->wake_lock, HZ);
69         mutex_lock(&modem->lock);
70         if (modem->udev) {
71                 usb_lock_device(modem->udev);
72                 pr_info("modem wake (%u)\n", ++(modem->wake_cnt));
73                 if (usb_autopm_get_interface(modem->intf) == 0)
74                         usb_autopm_put_interface_async(modem->intf);
75                 usb_unlock_device(modem->udev);
76         }
77         mutex_unlock(&modem->lock);
78
79         return IRQ_HANDLED;
80 }
81
82 static void tegra_usb_modem_recovery(struct work_struct *ws)
83 {
84         struct tegra_usb_modem *modem = container_of(ws, struct tegra_usb_modem,
85                                                      recovery_work.work);
86
87         mutex_lock(&modem->lock);
88         if (!modem->udev) {     /* assume modem crashed */
89                 if (modem->ops && modem->ops->reset)
90                         modem->ops->reset();
91         }
92         mutex_unlock(&modem->lock);
93 }
94
95 static void device_add_handler(struct usb_device *udev)
96 {
97         const struct usb_device_descriptor *desc = &udev->descriptor;
98         struct usb_interface *intf = usb_ifnum_to_if(udev, 0);
99         const struct usb_device_id *id = usb_match_id(intf, modem_list);
100
101         if (id) {
102                 /* hold wakelock to ensure ril has enough time to restart */
103                 wake_lock_timeout(&tegra_mdm.wake_lock, HZ*10);
104
105                 pr_info("Add device %d <%s %s>\n", udev->devnum,
106                         udev->manufacturer, udev->product);
107
108                 mutex_lock(&tegra_mdm.lock);
109                 tegra_mdm.udev = udev;
110                 tegra_mdm.intf = intf;
111                 tegra_mdm.vid = desc->idVendor;
112                 tegra_mdm.pid = desc->idProduct;
113                 tegra_mdm.wake_cnt = 0;
114                 tegra_mdm.capability = id->driver_info;
115                 mutex_unlock(&tegra_mdm.lock);
116
117                 pr_info("persist_enabled: %u\n", udev->persist_enabled);
118
119                 if (tegra_mdm.capability & TEGRA_MODEM_AUTOSUSPEND) {
120                         usb_enable_autosuspend(udev);
121                         pr_info("enable autosuspend for %s %s\n",
122                                 udev->manufacturer, udev->product);
123                 }
124         }
125 }
126
127 static void device_remove_handler(struct usb_device *udev)
128 {
129         const struct usb_device_descriptor *desc = &udev->descriptor;
130
131         if (desc->idVendor == tegra_mdm.vid &&
132             desc->idProduct == tegra_mdm.pid) {
133                 pr_info("Remove device %d <%s %s>\n", udev->devnum,
134                         udev->manufacturer, udev->product);
135
136                 mutex_lock(&tegra_mdm.lock);
137                 tegra_mdm.udev = NULL;
138                 tegra_mdm.intf = NULL;
139                 tegra_mdm.vid = 0;
140                 mutex_unlock(&tegra_mdm.lock);
141
142                 if (tegra_mdm.capability & TEGRA_MODEM_RECOVERY)
143                         queue_delayed_work(tegra_mdm.wq,
144                                            &tegra_mdm.recovery_work, HZ * 10);
145         }
146 }
147
148 static int usb_notify(struct notifier_block *self, unsigned long action,
149                       void *blob)
150 {
151         switch (action) {
152         case USB_DEVICE_ADD:
153                 device_add_handler(blob);
154                 break;
155         case USB_DEVICE_REMOVE:
156                 device_remove_handler(blob);
157                 break;
158         }
159
160         return NOTIFY_OK;
161 }
162
163 static struct notifier_block usb_nb = {
164         .notifier_call = usb_notify,
165 };
166
167 static int tegra_usb_modem_probe(struct platform_device *pdev)
168 {
169         struct tegra_usb_modem_power_platform_data *pdata =
170             pdev->dev.platform_data;
171         int ret;
172
173         if (!pdata) {
174                 dev_dbg(&pdev->dev, "platform_data not available\n");
175                 return -EINVAL;
176         }
177
178         /* get modem operations from platform data */
179         tegra_mdm.ops = (const struct tegra_modem_operations *)pdata->ops;
180
181         if (tegra_mdm.ops) {
182                 /* modem init */
183                 if (tegra_mdm.ops->init) {
184                         ret = tegra_mdm.ops->init();
185                         if (ret)
186                                 return ret;
187                 }
188
189                 /* start modem */
190                 if (tegra_mdm.ops->start)
191                         tegra_mdm.ops->start();
192         }
193
194         mutex_init(&(tegra_mdm.lock));
195         wake_lock_init(&(tegra_mdm.wake_lock), WAKE_LOCK_SUSPEND,
196                        "tegra_usb_mdm_lock");
197
198         /* create work queue */
199         tegra_mdm.wq = create_workqueue("tegra_usb_mdm_queue");
200         INIT_DELAYED_WORK(&(tegra_mdm.recovery_work), tegra_usb_modem_recovery);
201
202         /* create threaded irq for remote wakeup */
203         if (gpio_is_valid(pdata->wake_gpio)) {
204                 /* get remote wakeup gpio from platform data */
205                 tegra_mdm.wake_gpio = pdata->wake_gpio;
206
207                 ret = gpio_request(tegra_mdm.wake_gpio, "usb_mdm_wake");
208                 if (ret)
209                         return ret;
210
211                 tegra_gpio_enable(tegra_mdm.wake_gpio);
212
213                 /* enable IRQ for remote wakeup */
214                 tegra_mdm.irq = gpio_to_irq(tegra_mdm.wake_gpio);
215
216                 ret =
217                     request_threaded_irq(tegra_mdm.irq, NULL,
218                                          tegra_usb_modem_wake_thread,
219                                          pdata->flags, "tegra_usb_mdm_wake",
220                                          &tegra_mdm);
221                 if (ret < 0) {
222                         dev_err(&pdev->dev, "%s: request_threaded_irq error\n",
223                                 __func__);
224                         return ret;
225                 }
226
227                 ret = enable_irq_wake(tegra_mdm.irq);
228                 if (ret) {
229                         dev_err(&pdev->dev, "%s: enable_irq_wake error\n",
230                                 __func__);
231                         free_irq(tegra_mdm.irq, &tegra_mdm);
232                         return ret;
233                 }
234         }
235
236         usb_register_notify(&usb_nb);
237         dev_info(&pdev->dev, "Initialized tegra_usb_modem_power\n");
238
239         return 0;
240 }
241
242 static int __exit tegra_usb_modem_remove(struct platform_device *pdev)
243 {
244         usb_unregister_notify(&usb_nb);
245
246         if (tegra_mdm.irq) {
247                 disable_irq_wake(tegra_mdm.irq);
248                 free_irq(tegra_mdm.irq, &tegra_mdm);
249         }
250         return 0;
251 }
252
253 #ifdef CONFIG_PM
254 static int tegra_usb_modem_suspend(struct platform_device *pdev,
255                                    pm_message_t state)
256 {
257         /* send L3 hint to modem */
258         if (tegra_mdm.ops && tegra_mdm.ops->suspend)
259                 tegra_mdm.ops->suspend();
260         return 0;
261 }
262
263 static int tegra_usb_modem_resume(struct platform_device *pdev)
264 {
265         /* send L3->L0 hint to modem */
266         if (tegra_mdm.ops && tegra_mdm.ops->resume)
267                 tegra_mdm.ops->resume();
268         return 0;
269 }
270 #endif
271
272 static struct platform_driver tegra_usb_modem_power_driver = {
273         .driver = {
274                    .name = "tegra_usb_modem_power",
275                    .owner = THIS_MODULE,
276                    },
277         .probe = tegra_usb_modem_probe,
278         .remove = __exit_p(tegra_usb_modem_remove),
279 #ifdef CONFIG_PM
280         .suspend = tegra_usb_modem_suspend,
281         .resume = tegra_usb_modem_resume,
282 #endif
283 };
284
285 static int __init tegra_usb_modem_power_init(void)
286 {
287         return platform_driver_register(&tegra_usb_modem_power_driver);
288 }
289
290 subsys_initcall(tegra_usb_modem_power_init);
291
292 static void __exit tegra_usb_modem_power_exit(void)
293 {
294         platform_driver_unregister(&tegra_usb_modem_power_driver);
295 }
296
297 module_exit(tegra_usb_modem_power_exit);
298
299 MODULE_DESCRIPTION("Tegra usb modem power management driver");
300 MODULE_LICENSE("GPL");