arm: tegra: baseband: Discard modem remote wakeup in L3 transition
[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/pm_runtime.h>
30 #include <linux/suspend.h>
31 #include <linux/slab.h>
32 #include <linux/wakelock.h>
33 #include <mach/tegra_usb_modem_power.h>
34
35 struct tegra_usb_modem {
36         unsigned int wake_gpio; /* remote wakeup gpio */
37         unsigned int wake_cnt;  /* remote wakeup counter */
38         int irq;                /* remote wakeup irq */
39         struct mutex lock;
40         struct wake_lock wake_lock;     /* modem wake lock */
41         unsigned int vid;       /* modem vendor id */
42         unsigned int pid;       /* modem product id */
43         struct usb_device *udev;        /* modem usb device */
44         struct usb_interface *intf;     /* first modem usb interface */
45         struct workqueue_struct *wq;    /* modem workqueue */
46         struct delayed_work recovery_work;      /* modem recovery work */
47         const struct tegra_modem_operations *ops;       /* modem operations */
48         unsigned int capability;        /* modem capability */
49         int system_suspend;     /* system suspend flag */
50         struct notifier_block pm_notifier;      /* pm event notifier */
51         struct notifier_block usb_notifier;     /* usb event notifier */
52 };
53
54 /* supported modems */
55 static const struct usb_device_id modem_list[] = {
56         {USB_DEVICE(0x1983, 0x0310),    /* Icera 450 rev1 */
57          .driver_info = TEGRA_MODEM_AUTOSUSPEND,
58          },
59         {USB_DEVICE(0x1983, 0x0321),    /* Icera 450 rev2 */
60          .driver_info = TEGRA_MODEM_AUTOSUSPEND,
61          },
62         {USB_DEVICE(0x1983, 0x0327),    /* Icera 450 5AE */
63          .driver_info = TEGRA_MODEM_AUTOSUSPEND,
64          },
65         {}
66 };
67
68 static irqreturn_t tegra_usb_modem_wake_thread(int irq, void *data)
69 {
70         struct tegra_usb_modem *modem = (struct tegra_usb_modem *)data;
71
72         wake_lock_timeout(&modem->wake_lock, HZ);
73         mutex_lock(&modem->lock);
74         if (modem->udev) {
75                 pr_info("modem wake (%u)\n", ++(modem->wake_cnt));
76
77                 if (!modem->system_suspend) {
78                         usb_lock_device(modem->udev);
79                         if (usb_autopm_get_interface(modem->intf) == 0)
80                                 usb_autopm_put_interface_async(modem->intf);
81                         usb_unlock_device(modem->udev);
82                 }
83         }
84         mutex_unlock(&modem->lock);
85
86         return IRQ_HANDLED;
87 }
88
89 static void tegra_usb_modem_recovery(struct work_struct *ws)
90 {
91         struct tegra_usb_modem *modem = container_of(ws, struct tegra_usb_modem,
92                                                      recovery_work.work);
93
94         mutex_lock(&modem->lock);
95         if (!modem->udev) {     /* assume modem crashed */
96                 if (modem->ops && modem->ops->reset)
97                         modem->ops->reset();
98         }
99         mutex_unlock(&modem->lock);
100 }
101
102 static void device_add_handler(struct tegra_usb_modem *modem,
103                                struct usb_device *udev)
104 {
105         const struct usb_device_descriptor *desc = &udev->descriptor;
106         struct usb_interface *intf = usb_ifnum_to_if(udev, 0);
107         const struct usb_device_id *id = usb_match_id(intf, modem_list);
108
109         if (id) {
110                 /* hold wakelock to ensure ril has enough time to restart */
111                 wake_lock_timeout(&modem->wake_lock, HZ * 10);
112
113                 pr_info("Add device %d <%s %s>\n", udev->devnum,
114                         udev->manufacturer, udev->product);
115
116                 mutex_lock(&modem->lock);
117                 modem->udev = udev;
118                 modem->intf = intf;
119                 modem->vid = desc->idVendor;
120                 modem->pid = desc->idProduct;
121                 modem->wake_cnt = 0;
122                 modem->capability = id->driver_info;
123                 mutex_unlock(&modem->lock);
124
125                 pr_info("persist_enabled: %u\n", udev->persist_enabled);
126
127 #ifdef CONFIG_PM
128                 if (modem->capability & TEGRA_MODEM_AUTOSUSPEND) {
129                         pm_runtime_set_autosuspend_delay(&udev->dev, 2000);
130                         usb_enable_autosuspend(udev);
131                         pr_info("enable autosuspend for %s %s\n",
132                                 udev->manufacturer, udev->product);
133                 }
134 #endif
135         }
136 }
137
138 static void device_remove_handler(struct tegra_usb_modem *modem,
139                                   struct usb_device *udev)
140 {
141         const struct usb_device_descriptor *desc = &udev->descriptor;
142
143         if (desc->idVendor == modem->vid &&
144             desc->idProduct == modem->pid) {
145                 pr_info("Remove device %d <%s %s>\n", udev->devnum,
146                         udev->manufacturer, udev->product);
147
148                 mutex_lock(&modem->lock);
149                 modem->udev = NULL;
150                 modem->intf = NULL;
151                 modem->vid = 0;
152                 mutex_unlock(&modem->lock);
153
154                 if (modem->capability & TEGRA_MODEM_RECOVERY)
155                         queue_delayed_work(modem->wq,
156                                            &modem->recovery_work, HZ * 10);
157         }
158 }
159
160 static int mdm_usb_notifier(struct notifier_block *notifier,
161                             unsigned long usb_event,
162                             void *udev)
163 {
164         struct tegra_usb_modem *modem =
165                 container_of(notifier, struct tegra_usb_modem, usb_notifier);
166
167         switch (usb_event) {
168         case USB_DEVICE_ADD:
169                 device_add_handler(modem, udev);
170                 break;
171         case USB_DEVICE_REMOVE:
172                 device_remove_handler(modem, udev);
173                 break;
174         }
175         return NOTIFY_OK;
176 }
177
178 static int mdm_pm_notifier(struct notifier_block *notifier,
179                            unsigned long pm_event,
180                            void *unused)
181 {
182         struct tegra_usb_modem *modem =
183                 container_of(notifier, struct tegra_usb_modem, pm_notifier);
184
185         mutex_lock(&modem->lock);
186         if (!modem->udev) {
187                 mutex_unlock(&modem->lock);
188                 return NOTIFY_DONE;
189         }
190
191         pr_info("%s: event %ld\n", __func__, pm_event);
192         switch (pm_event) {
193         case PM_SUSPEND_PREPARE:
194                 if (wake_lock_active(&modem->wake_lock)) {
195                         pr_warn("%s: wakelock was active, aborting suspend\n",
196                                 __func__);
197                         return NOTIFY_STOP;
198                 }
199
200                 modem->system_suspend = 1;
201                 mutex_unlock(&modem->lock);
202                 return NOTIFY_OK;
203         case PM_POST_SUSPEND:
204                 modem->system_suspend = 0;
205                 mutex_unlock(&modem->lock);
206                 return NOTIFY_OK;
207         }
208
209         mutex_unlock(&modem->lock);
210         return NOTIFY_DONE;
211 }
212
213 static int mdm_init(struct tegra_usb_modem *modem, struct platform_device *pdev)
214 {
215         struct tegra_usb_modem_power_platform_data *pdata =
216             pdev->dev.platform_data;
217         int ret = 0;
218
219         /* get modem operations from platform data */
220         modem->ops = (const struct tegra_modem_operations *)pdata->ops;
221
222         if (modem->ops) {
223                 /* modem init */
224                 if (modem->ops->init) {
225                         ret = modem->ops->init();
226                         if (ret)
227                                 return ret;
228                 }
229
230                 /* start modem */
231                 if (modem->ops->start)
232                         modem->ops->start();
233         }
234
235         mutex_init(&(modem->lock));
236         wake_lock_init(&modem->wake_lock, WAKE_LOCK_SUSPEND,
237                        "tegra_usb_mdm_lock");
238
239         /* create work queue platform_driver_registe*/
240         modem->wq = create_workqueue("tegra_usb_mdm_queue");
241         INIT_DELAYED_WORK(&(modem->recovery_work), tegra_usb_modem_recovery);
242
243         /* create threaded irq for remote wakeup */
244         if (gpio_is_valid(pdata->wake_gpio)) {
245                 /* get remote wakeup gpio from platform data */
246                 modem->wake_gpio = pdata->wake_gpio;
247
248                 ret = gpio_request(modem->wake_gpio, "usb_mdm_wake");
249                 if (ret)
250                         return ret;
251
252                 tegra_gpio_enable(modem->wake_gpio);
253
254                 /* enable IRQ for remote wakeup */
255                 modem->irq = gpio_to_irq(modem->wake_gpio);
256
257                 ret =
258                     request_threaded_irq(modem->irq, NULL,
259                                          tegra_usb_modem_wake_thread,
260                                          pdata->flags, "tegra_usb_mdm_wake",
261                                          modem);
262                 if (ret < 0) {
263                         dev_err(&pdev->dev, "%s: request_threaded_irq error\n",
264                                 __func__);
265                         return ret;
266                 }
267
268                 ret = enable_irq_wake(modem->irq);
269                 if (ret) {
270                         dev_err(&pdev->dev, "%s: enable_irq_wake error\n",
271                                 __func__);
272                         free_irq(modem->irq, modem);
273                         return ret;
274                 }
275         }
276
277         modem->pm_notifier.notifier_call = mdm_pm_notifier;
278         modem->usb_notifier.notifier_call = mdm_usb_notifier;
279
280         usb_register_notify(&modem->usb_notifier);
281         register_pm_notifier(&modem->pm_notifier);
282
283         return ret;
284 }
285
286 static int tegra_usb_modem_probe(struct platform_device *pdev)
287 {
288         struct tegra_usb_modem_power_platform_data *pdata =
289             pdev->dev.platform_data;
290         struct tegra_usb_modem *modem;
291         int ret = 0;
292
293         if (!pdata) {
294                 dev_dbg(&pdev->dev, "platform_data not available\n");
295                 return -EINVAL;
296         }
297
298         modem = kzalloc(sizeof(struct tegra_usb_modem), GFP_KERNEL);
299         if (!modem) {
300                 dev_dbg(&pdev->dev, "failed to allocate memory\n");
301                 return -ENOMEM;
302         }
303
304         ret = mdm_init(modem, pdev);
305         if (ret) {
306                 kfree(modem);
307                 return ret;
308         }
309
310         dev_set_drvdata(&pdev->dev, modem);
311
312         return ret;
313 }
314
315 static int __exit tegra_usb_modem_remove(struct platform_device *pdev)
316 {
317         struct tegra_usb_modem *modem = platform_get_drvdata(pdev);
318
319         unregister_pm_notifier(&modem->pm_notifier);
320         usb_unregister_notify(&modem->usb_notifier);
321
322         if (modem->irq) {
323                 disable_irq_wake(modem->irq);
324                 free_irq(modem->irq, modem);
325         }
326         kfree(modem);
327         return 0;
328 }
329
330 #ifdef CONFIG_PM
331 static int tegra_usb_modem_suspend(struct platform_device *pdev,
332                                    pm_message_t state)
333 {
334         struct tegra_usb_modem *modem = platform_get_drvdata(pdev);
335
336         /* send L3 hint to modem */
337         if (modem->ops && modem->ops->suspend)
338                 modem->ops->suspend();
339         return 0;
340 }
341
342 static int tegra_usb_modem_resume(struct platform_device *pdev)
343 {
344         struct tegra_usb_modem *modem = platform_get_drvdata(pdev);
345
346         /* send L3->L0 hint to modem */
347         if (modem->ops && modem->ops->resume)
348                 modem->ops->resume();
349         return 0;
350 }
351 #endif
352
353 static struct platform_driver tegra_usb_modem_power_driver = {
354         .driver = {
355                    .name = "tegra_usb_modem_power",
356                    .owner = THIS_MODULE,
357                    },
358         .probe = tegra_usb_modem_probe,
359         .remove = __exit_p(tegra_usb_modem_remove),
360 #ifdef CONFIG_PM
361         .suspend = tegra_usb_modem_suspend,
362         .resume = tegra_usb_modem_resume,
363 #endif
364 };
365
366 static int __init tegra_usb_modem_power_init(void)
367 {
368         return platform_driver_register(&tegra_usb_modem_power_driver);
369 }
370
371 subsys_initcall(tegra_usb_modem_power_init);
372
373 static void __exit tegra_usb_modem_power_exit(void)
374 {
375         platform_driver_unregister(&tegra_usb_modem_power_driver);
376 }
377
378 module_exit(tegra_usb_modem_power_exit);
379
380 MODULE_DESCRIPTION("Tegra usb modem power management driver");
381 MODULE_LICENSE("GPL");