dcf63302007713bf7ea73ad743036bd0f8171bc9
[linux-3.10.git] / drivers / misc / bluedroid_pm.c
1 /*
2  * drivers/misc/bluedroid_pm.c
3  *
4  * Copyright (c) 2014, NVIDIA Corporation. All rights reserved.
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/err.h>
22 #include <linux/types.h>
23 #include <linux/uaccess.h>
24 #include <linux/fs.h>
25 #include <linux/gpio.h>
26 #include <linux/init.h>
27 #include <linux/kernel.h>
28 #include <linux/miscdevice.h>
29 #include <linux/module.h>
30 #include <linux/proc_fs.h>
31 #include <linux/regulator/consumer.h>
32 #include <linux/rfkill.h>
33 #include <linux/platform_device.h>
34 #include <linux/clk.h>
35 #include <linux/interrupt.h>
36 #include <linux/wakelock.h>
37 #include <linux/slab.h>
38 #include <linux/pm_qos.h>
39 #include <linux/bluedroid_pm.h>
40 #include <linux/delay.h>
41 #include <linux/timer.h>
42 #include <linux/of.h>
43 #include <linux/of_gpio.h>
44
45 #define PROC_DIR        "bluetooth/sleep"
46
47 /* 5 seconds of Min CPU configurations during resume */
48 #define DEFAULT_RESUME_CPU_TIMEOUT      5000000
49
50 #define TX_TIMER_INTERVAL 5
51
52 /* Macro to enable or disable debug logging */
53 /* #define BLUEDROID_PM_DBG */
54 #ifndef BLUEDROID_PM_DBG
55 #define BDP_DBG(fmt, ...)       pr_debug("%s: " fmt, __func__, ##__VA_ARGS__)
56 #else
57 #define BDP_DBG(fmt, ...)       pr_warn("%s: " fmt, __func__, ##__VA_ARGS__)
58 #endif
59
60 #define BDP_WARN(fmt, ...)      pr_warn("%s: " fmt, __func__, ##__VA_ARGS__)
61 #define BDP_ERR(fmt, ...)       pr_err("%s: " fmt, __func__, ##__VA_ARGS__)
62
63 /* status flags for bluedroid_pm_driver */
64 #define BT_WAKE 0x01
65
66 struct bluedroid_pm_data {
67         int gpio_reset;
68         int gpio_shutdown;
69         int host_wake;
70         int ext_wake;
71         int is_blocked;
72         int resume_min_frequency;
73         unsigned long flags;
74         int host_wake_irq;
75         struct regulator *vdd_3v3;
76         struct regulator *vdd_1v8;
77         struct rfkill *rfkill;
78         struct wake_lock wake_lock;
79         struct pm_qos_request resume_cpu_freq_req;
80 };
81
82 struct proc_dir_entry *proc_bt_dir, *bluetooth_sleep_dir;
83 static bool bluedroid_pm_blocked = 1;
84
85 static int create_bt_proc_interface(void *drv_data);
86 static void remove_bt_proc_interface(void);
87
88 static DEFINE_MUTEX(bt_wlan_sync);
89
90 void bt_wlan_lock(void)
91 {
92         mutex_lock(&bt_wlan_sync);
93 }
94 EXPORT_SYMBOL(bt_wlan_lock);
95
96 void bt_wlan_unlock(void)
97 {
98         mutex_unlock(&bt_wlan_sync);
99 }
100 EXPORT_SYMBOL(bt_wlan_unlock);
101
102 /** bluedroid_m busy timer */
103 static void bluedroid_pm_timer_expire(unsigned long data);
104 static DEFINE_TIMER(bluedroid_pm_timer, bluedroid_pm_timer_expire, 0, 0);
105 static int bluedroid_pm_gpio_get_value(unsigned int gpio);
106 static void bluedroid_pm_gpio_set_value(unsigned int gpio, int value);
107
108 static irqreturn_t bluedroid_pm_hostwake_isr(int irq, void *dev_id)
109 {
110         /* schedule a tasklet to handle the change in the host wake line */
111         return IRQ_HANDLED;
112 }
113
114 static int bluedroid_pm_gpio_get_value(unsigned int gpio)
115 {
116         if (gpio_cansleep(gpio))
117                 return gpio_get_value_cansleep(gpio);
118         else
119                 return gpio_get_value(gpio);
120 }
121
122 static void bluedroid_pm_gpio_set_value(unsigned int gpio, int value)
123 {
124         if (gpio_cansleep(gpio))
125                 gpio_set_value_cansleep(gpio, value);
126         else
127                 gpio_set_value(gpio, value);
128 }
129
130 /**
131  * Handles bluedroid_pm busy timer expiration.
132  * @param data: bluedroid_pm strcuture.
133  */
134 static void bluedroid_pm_timer_expire(unsigned long data)
135 {
136         struct bluedroid_pm_data *bluedroid_pm =
137                                 (struct bluedroid_pm_data *)data;
138
139         /*
140          * if bluedroid_pm data is NULL or timer is deleted with TX busy.
141          * return from the function.
142          */
143         if (!bluedroid_pm || test_bit(BT_WAKE, &bluedroid_pm->flags))
144                 return;
145
146         if (!bluedroid_pm_gpio_get_value(bluedroid_pm->host_wake)) {
147                 /* BT can sleep */
148                 BDP_DBG("Tx and Rx are idle, BT sleeping");
149                 bluedroid_pm_gpio_set_value(bluedroid_pm->ext_wake, 0);
150                 wake_unlock(&bluedroid_pm->wake_lock);
151         } else {
152                 /* BT Rx is busy, Reset Timer */
153                 BDP_DBG("Rx is busy, restarting the timer");
154                 mod_timer(&bluedroid_pm_timer,
155                                         jiffies + (TX_TIMER_INTERVAL * HZ));
156         }
157 }
158
159 static int bluedroid_pm_rfkill_set_power(void *data, bool blocked)
160 {
161         struct bluedroid_pm_data *bluedroid_pm = data;
162         int ret = 0;
163
164         mdelay(100);
165         if (blocked) {
166                 if (gpio_is_valid(bluedroid_pm->gpio_shutdown))
167                         bluedroid_pm_gpio_set_value(
168                                 bluedroid_pm->gpio_shutdown, 0);
169                 if (gpio_is_valid(bluedroid_pm->gpio_reset))
170                         bluedroid_pm_gpio_set_value(
171                                 bluedroid_pm->gpio_reset, 0);
172                 if (bluedroid_pm->vdd_3v3)
173                         ret |= regulator_disable(bluedroid_pm->vdd_3v3);
174                 if (bluedroid_pm->vdd_1v8)
175                         ret |= regulator_disable(bluedroid_pm->vdd_1v8);
176                 if (gpio_is_valid(bluedroid_pm->ext_wake))
177                         wake_unlock(&bluedroid_pm->wake_lock);
178                 if (bluedroid_pm->resume_min_frequency)
179                         pm_qos_remove_request(&bluedroid_pm->
180                                                 resume_cpu_freq_req);
181         } else {
182                 if (bluedroid_pm->vdd_3v3)
183                         ret |= regulator_enable(bluedroid_pm->vdd_3v3);
184                 if (bluedroid_pm->vdd_1v8)
185                         ret |= regulator_enable(bluedroid_pm->vdd_1v8);
186                 if (gpio_is_valid(bluedroid_pm->gpio_shutdown))
187                         bluedroid_pm_gpio_set_value(
188                                 bluedroid_pm->gpio_shutdown, 1);
189                 if (gpio_is_valid(bluedroid_pm->gpio_reset))
190                         bluedroid_pm_gpio_set_value(
191                                 bluedroid_pm->gpio_reset, 1);
192                 if (bluedroid_pm->resume_min_frequency)
193                         pm_qos_add_request(&bluedroid_pm->
194                                                 resume_cpu_freq_req,
195                                                 PM_QOS_CPU_FREQ_MIN,
196                                                 PM_QOS_DEFAULT_VALUE);
197         }
198         bluedroid_pm->is_blocked = blocked;
199         mdelay(100);
200
201         return ret;
202 }
203
204 static const struct rfkill_ops bluedroid_pm_rfkill_ops = {
205         .set_block = bluedroid_pm_rfkill_set_power,
206 };
207
208 /*
209  * This API is added to set block state by ext driver,
210  * when bluedroid_pm rfkill is not used but host_wake functionality to be used.
211  * Eg: btwilink driver
212  */
213 void bluedroid_pm_set_ext_state(bool blocked)
214 {
215         bluedroid_pm_blocked = blocked;
216 }
217 EXPORT_SYMBOL(bluedroid_pm_set_ext_state);
218
219 static int bluedroid_pm_probe(struct platform_device *pdev)
220 {
221         static struct bluedroid_pm_data *bluedroid_pm;
222         struct bluedroid_pm_platform_data *pdata = pdev->dev.platform_data;
223         struct rfkill *rfkill;
224         struct resource *res;
225         int ret;
226         bool enable = false;  /* off */
227         struct device_node *node;
228
229         bluedroid_pm = kzalloc(sizeof(*bluedroid_pm), GFP_KERNEL);
230         if (!bluedroid_pm)
231                 return -ENOMEM;
232
233         bluedroid_pm->vdd_3v3 = regulator_get(&pdev->dev, "avdd");
234         if (IS_ERR(bluedroid_pm->vdd_3v3)) {
235                 pr_warn("%s: regulator avdd not available\n", __func__);
236                 bluedroid_pm->vdd_3v3 = NULL;
237         }
238         bluedroid_pm->vdd_1v8 = regulator_get(&pdev->dev, "dvdd");
239         if (IS_ERR(bluedroid_pm->vdd_1v8)) {
240                 pr_warn("%s: regulator dvdd not available\n", __func__);
241                 bluedroid_pm->vdd_1v8 = NULL;
242         }
243
244         if (pdev->dev.of_node) {
245                 node = pdev->dev.of_node;
246
247                 bluedroid_pm->gpio_reset =
248                                 of_get_named_gpio(node, "bluedroid_pm,reset-gpio", 0);
249                 bluedroid_pm->gpio_shutdown =
250                                 of_get_named_gpio(node, "bluedroid_pm,shutdown-gpio", 0);
251                 bluedroid_pm->host_wake =
252                                 of_get_named_gpio(node, "bluedroid_pm,host-wake-gpio", 0);
253                 bluedroid_pm->host_wake_irq = platform_get_irq(pdev, 0);
254                 bluedroid_pm->ext_wake =
255                                 of_get_named_gpio(node, "bluedroid_pm,ext-wake-gpio", 0);
256         } else {
257                 res = platform_get_resource_byname(pdev, IORESOURCE_IO,
258                                                         "reset_gpio");
259                 if (res)
260                         bluedroid_pm->gpio_reset = res->start;
261                 else
262                         bluedroid_pm->gpio_reset = -1;
263
264                 res = platform_get_resource_byname(pdev, IORESOURCE_IO,
265                                                         "shutdown_gpio");
266                 if (res)
267                         bluedroid_pm->gpio_shutdown = res->start;
268                 else
269                         bluedroid_pm->gpio_shutdown = -1;
270
271                 res = platform_get_resource_byname(pdev, IORESOURCE_IO,
272                                                 "gpio_host_wake");
273                 if (res)
274                         bluedroid_pm->host_wake = res->start;
275                 else
276                         bluedroid_pm->host_wake = -1;
277
278                 res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
279                                                         "host_wake");
280                 if (res)
281                         bluedroid_pm->host_wake_irq = res->start;
282                 else
283                         bluedroid_pm->host_wake_irq = -1;
284
285                 res = platform_get_resource_byname(pdev, IORESOURCE_IO,
286                                                 "gpio_ext_wake");
287                 if (res)
288                         bluedroid_pm->ext_wake = res->start;
289                 else
290                         bluedroid_pm->ext_wake = -1;
291         }
292
293         if (gpio_is_valid(bluedroid_pm->gpio_reset)) {
294                 ret = gpio_request(bluedroid_pm->gpio_reset, "reset_gpio");
295                 if (ret) {
296                         BDP_ERR("Failed to get reset gpio\n");
297                         goto free_res;
298                 }
299                 gpio_direction_output(bluedroid_pm->gpio_reset, enable);
300         } else
301                 BDP_DBG("Reset gpio not registered.\n");
302
303         if (gpio_is_valid(bluedroid_pm->gpio_shutdown)) {
304                 ret = gpio_request(bluedroid_pm->gpio_shutdown,
305                                                 "shutdown_gpio");
306                 if (ret) {
307                         BDP_ERR("Failed to get shutdown gpio\n");
308                         goto free_res;
309                 }
310                 gpio_direction_output(bluedroid_pm->gpio_shutdown, enable);
311         } else
312                 BDP_DBG("shutdown gpio not registered\n");
313
314         /*
315          * make sure at-least one of the GPIO or regulators avaiable to
316          * register with rfkill is defined
317          */
318         if ((gpio_is_valid(bluedroid_pm->gpio_reset)) ||
319                 (gpio_is_valid(bluedroid_pm->gpio_shutdown)) ||
320                 bluedroid_pm->vdd_1v8 || bluedroid_pm->vdd_3v3) {
321                 rfkill = rfkill_alloc(pdev->name, &pdev->dev,
322                                 RFKILL_TYPE_BLUETOOTH, &bluedroid_pm_rfkill_ops,
323                                 bluedroid_pm);
324
325                 if (unlikely(!rfkill))
326                         goto free_res;
327
328                 bluedroid_pm->is_blocked = !enable;
329                 rfkill_set_states(rfkill, bluedroid_pm->is_blocked, false);
330
331                 ret = rfkill_register(rfkill);
332
333                 if (unlikely(ret)) {
334                         rfkill_destroy(rfkill);
335                         kfree(rfkill);
336                         goto free_res;
337                 }
338                 bluedroid_pm->rfkill = rfkill;
339         }
340
341         if (gpio_is_valid(bluedroid_pm->host_wake)) {
342                 ret = gpio_request(bluedroid_pm->host_wake, "bt_host_wake");
343                 if (ret) {
344                         BDP_ERR("Failed to get host_wake gpio\n");
345                         goto free_res;
346                 }
347                 /* configure host_wake as input */
348                 gpio_direction_input(bluedroid_pm->host_wake);
349         } else
350                 BDP_DBG("gpio_host_wake not registered\n");
351
352         if (bluedroid_pm->host_wake_irq > -1) {
353                 BDP_DBG("found host_wake irq\n");
354                 ret = request_irq(bluedroid_pm->host_wake_irq,
355                                         bluedroid_pm_hostwake_isr,
356                                         IRQF_DISABLED | IRQF_TRIGGER_RISING,
357                                         "bluetooth hostwake", bluedroid_pm);
358                 if (ret) {
359                         BDP_ERR("Failed to get host_wake irq\n");
360                         goto free_res;
361                 }
362         } else
363                 BDP_DBG("host_wake not registered\n");
364
365         if (gpio_is_valid(bluedroid_pm->ext_wake)) {
366                 ret = gpio_request(bluedroid_pm->ext_wake, "bt_ext_wake");
367                 if (ret) {
368                         BDP_ERR("Failed to get ext_wake gpio\n");
369                         goto free_res;
370                 }
371                 /* configure ext_wake as output mode*/
372                 gpio_direction_output(bluedroid_pm->ext_wake, 1);
373                 if (create_bt_proc_interface(bluedroid_pm)) {
374                         BDP_ERR("Failed to create proc interface");
375                         goto free_res;
376                 }
377                 /* initialize wake lock */
378                 wake_lock_init(&bluedroid_pm->wake_lock, WAKE_LOCK_SUSPEND,
379                                                                 "bluedroid_pm");
380                 /* Initialize timer */
381                 init_timer(&bluedroid_pm_timer);
382                 bluedroid_pm_timer.function = bluedroid_pm_timer_expire;
383                 bluedroid_pm_timer.data = (unsigned long)bluedroid_pm;
384         } else
385                 BDP_DBG("gpio_ext_wake not registered\n");
386
387         /* Update resume_min_frequency, if pdata is passed from board files */
388         if (pdata)
389                 bluedroid_pm->resume_min_frequency =
390                                                 pdata->resume_min_frequency;
391
392         platform_set_drvdata(pdev, bluedroid_pm);
393         BDP_DBG("driver successfully registered");
394         return 0;
395
396 free_res:
397         if (bluedroid_pm->vdd_3v3)
398                 regulator_put(bluedroid_pm->vdd_3v3);
399         if (bluedroid_pm->vdd_1v8)
400                 regulator_put(bluedroid_pm->vdd_1v8);
401         if (gpio_is_valid(bluedroid_pm->gpio_shutdown))
402                 gpio_free(bluedroid_pm->gpio_shutdown);
403         if (gpio_is_valid(bluedroid_pm->gpio_reset))
404                 gpio_free(bluedroid_pm->gpio_reset);
405         if (gpio_is_valid(bluedroid_pm->ext_wake))
406                 gpio_free(bluedroid_pm->ext_wake);
407         if (gpio_is_valid(bluedroid_pm->host_wake))
408                 gpio_free(bluedroid_pm->host_wake);
409         if (bluedroid_pm->rfkill) {
410                 rfkill_unregister(bluedroid_pm->rfkill);
411                 rfkill_destroy(bluedroid_pm->rfkill);
412                 kfree(bluedroid_pm->rfkill);
413         }
414         kfree(bluedroid_pm);
415         return -ENODEV;
416 }
417
418 static int bluedroid_pm_remove(struct platform_device *pdev)
419 {
420         struct bluedroid_pm_data *bluedroid_pm = platform_get_drvdata(pdev);
421
422         if (bluedroid_pm->host_wake)
423                 gpio_free(bluedroid_pm->host_wake);
424         if (bluedroid_pm->host_wake_irq)
425                 free_irq(bluedroid_pm->host_wake_irq, NULL);
426         if (bluedroid_pm->ext_wake) {
427                 wake_lock_destroy(&bluedroid_pm->wake_lock);
428                 gpio_free(bluedroid_pm->ext_wake);
429                 remove_bt_proc_interface();
430                 del_timer(&bluedroid_pm_timer);
431         }
432         if (bluedroid_pm->gpio_reset || bluedroid_pm->gpio_shutdown ||
433                 bluedroid_pm->vdd_1v8 || bluedroid_pm->vdd_3v3) {
434                 rfkill_unregister(bluedroid_pm->rfkill);
435                 rfkill_destroy(bluedroid_pm->rfkill);
436                 kfree(bluedroid_pm->rfkill);
437         }
438         if (bluedroid_pm->gpio_shutdown)
439                 gpio_free(bluedroid_pm->gpio_shutdown);
440         if (bluedroid_pm->gpio_reset)
441                 gpio_free(bluedroid_pm->gpio_reset);
442         if (bluedroid_pm->vdd_3v3)
443                 regulator_put(bluedroid_pm->vdd_3v3);
444         if (bluedroid_pm->vdd_1v8)
445                 regulator_put(bluedroid_pm->vdd_1v8);
446         kfree(bluedroid_pm);
447
448         return 0;
449 }
450
451 static int bluedroid_pm_suspend(struct platform_device *pdev,
452                                                 pm_message_t state)
453 {
454         struct bluedroid_pm_data *bluedroid_pm = platform_get_drvdata(pdev);
455         if (bluedroid_pm->host_wake)
456                 if (!bluedroid_pm->is_blocked || !bluedroid_pm_blocked)
457                         enable_irq_wake(bluedroid_pm->host_wake_irq);
458
459         return 0;
460 }
461
462 static int bluedroid_pm_resume(struct platform_device *pdev)
463 {
464         struct bluedroid_pm_data *bluedroid_pm = platform_get_drvdata(pdev);
465         if (bluedroid_pm->host_wake)
466                 if (!bluedroid_pm->is_blocked || !bluedroid_pm_blocked)
467                         disable_irq_wake(bluedroid_pm->host_wake_irq);
468
469         return 0;
470 }
471
472 static struct of_device_id bdroid_of_match[] = {
473         { .compatible = "nvidia,tegra-bluedroid_pm", },
474         { },
475 };
476 MODULE_DEVICE_TABLE(of, bdroid_of_match);
477
478 static struct platform_driver bluedroid_pm_driver = {
479         .probe = bluedroid_pm_probe,
480         .remove = bluedroid_pm_remove,
481         .suspend = bluedroid_pm_suspend,
482         .resume = bluedroid_pm_resume,
483         .driver = {
484                 .name = "bluedroid_pm",
485                 .of_match_table = of_match_ptr(bdroid_of_match),
486                 .owner = THIS_MODULE,
487         },
488 };
489
490 static ssize_t lpm_read_proc(struct file *file, char __user *buf, size_t size,
491                                         loff_t *ppos)
492 {
493         char *msg = "lpm_read";
494
495         return simple_read_from_buffer(buf, size, ppos, msg, strlen(msg));
496 }
497
498 static ssize_t lpm_write_proc(struct file *file, const char __user *buffer,
499                                         size_t count, loff_t *ppos)
500 {
501         char *buf;
502         struct bluedroid_pm_data *bluedroid_pm = PDE_DATA(file_inode(file));
503
504         if (count < 1)
505                 return -EINVAL;
506
507         buf = kmalloc(count, GFP_KERNEL);
508         if (!buf)
509                 return -ENOMEM;
510
511         if (copy_from_user(buf, buffer, count)) {
512                 kfree(buf);
513                 return -EFAULT;
514         }
515
516         if (!bluedroid_pm->is_blocked) {
517                 if (buf[0] == '0') {
518                         if (!bluedroid_pm_gpio_get_value(
519                                 bluedroid_pm->host_wake)) {
520                                 /* BT can sleep */
521                                 BDP_DBG("Tx and Rx are idle, BT sleeping");
522                                         bluedroid_pm_gpio_set_value(
523                                                 bluedroid_pm->ext_wake, 0);
524                                         wake_unlock(&bluedroid_pm->wake_lock);
525                                 } else {
526                                         /* Reset Timer */
527                                         BDP_DBG("Rx is busy, restarting the timer");
528                                         mod_timer(&bluedroid_pm_timer,
529                                                 jiffies + (TX_TIMER_INTERVAL * HZ));
530                                 }
531                         clear_bit(BT_WAKE, &bluedroid_pm->flags);
532                 } else if (buf[0] == '1') {
533                         BDP_DBG("Tx is busy, wake_lock taken, delete timer");
534                         bluedroid_pm_gpio_set_value(
535                                 bluedroid_pm->ext_wake, 1);
536                         wake_lock(&bluedroid_pm->wake_lock);
537                         del_timer(&bluedroid_pm_timer);
538                         set_bit(BT_WAKE, &bluedroid_pm->flags);
539                 } else {
540                         kfree(buf);
541                         return -EINVAL;
542                 }
543         }
544
545         kfree(buf);
546         return count;
547 }
548
549 static const struct file_operations lpm_fops = {
550         .read           = lpm_read_proc,
551         .write          = lpm_write_proc,
552         .llseek         = default_llseek,
553 };
554
555 static void remove_bt_proc_interface(void)
556 {
557         remove_proc_entry("lpm", bluetooth_sleep_dir);
558         remove_proc_entry("sleep", proc_bt_dir);
559         remove_proc_entry("bluetooth", 0);
560 }
561
562 static int create_bt_proc_interface(void *drv_data)
563 {
564         int retval;
565         struct proc_dir_entry *ent;
566
567         proc_bt_dir = proc_mkdir("bluetooth", NULL);
568         if (proc_bt_dir == NULL) {
569                 BDP_ERR("Unable to create /proc/bluetooth directory");
570                 return -ENOMEM;
571         }
572
573         bluetooth_sleep_dir = proc_mkdir("sleep", proc_bt_dir);
574         if (proc_bt_dir == NULL) {
575                 BDP_ERR("Unable to create /proc/bluetooth directory");
576                 return -ENOMEM;
577         }
578
579         /* Creating read/write "btwake" entry */
580         ent = proc_create_data("lpm", 0622, bluetooth_sleep_dir, &lpm_fops, drv_data);
581         if (ent == NULL) {
582                 BDP_ERR("Unable to create /proc/%s/btwake entry", PROC_DIR);
583                 retval = -ENOMEM;
584                 goto fail;
585         }
586         return 0;
587 fail:
588         remove_proc_entry("lpm", bluetooth_sleep_dir);
589         remove_proc_entry("sleep", proc_bt_dir);
590         remove_proc_entry("bluetooth", 0);
591         return retval;
592 }
593
594 static int __init bluedroid_pm_init(void)
595 {
596         return platform_driver_register(&bluedroid_pm_driver);
597 }
598
599 static void __exit bluedroid_pm_exit(void)
600 {
601         platform_driver_unregister(&bluedroid_pm_driver);
602 }
603
604 module_init(bluedroid_pm_init);
605 module_exit(bluedroid_pm_exit);
606
607 MODULE_DESCRIPTION("bluedroid PM");
608 MODULE_AUTHOR("NVIDIA");
609 MODULE_LICENSE("GPL");