]> nv-tegra.nvidia Code Review - linux-2.6.git/blob - drivers/misc/bluedroid_pm.c
drivers: misc: publish fps, cpu load, thread count
[linux-2.6.git] / drivers / misc / bluedroid_pm.c
1 /*
2  * drivers/misc/bluedroid_pm.c
3  *
4  * Copyright (c) 2012-2013, 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/pm_qos.h>
38
39 #define PROC_DIR        "bluetooth/sleep"
40
41 /* 5 seconds of Min CPU configurations during resume */
42 #define DEFAULT_RESUME_CPU_TIMEOUT      5000000
43
44 struct bluedroid_pm_data {
45         int gpio_reset;
46         int gpio_shutdown;
47         int host_wake;
48         int ext_wake;
49         int is_blocked;
50         int resume_min_frequency;
51         unsigned host_wake_irq;
52         struct regulator *vdd_3v3;
53         struct regulator *vdd_1v8;
54         struct rfkill *rfkill;
55         struct wake_lock wake_lock;
56         struct pm_qos_request resume_cpu_freq_req;
57 };
58
59 struct proc_dir_entry *proc_bt_dir, *bluetooth_sleep_dir;
60 static bool bluedroid_pm_blocked = 1;
61
62 static int create_bt_proc_interface(void *drv_data);
63 static void remove_bt_proc_interface(void);
64
65 static irqreturn_t bluedroid_pm_hostwake_isr(int irq, void *dev_id)
66 {
67         /* schedule a tasklet to handle the change in the host wake line */
68         return IRQ_HANDLED;
69 }
70
71 static int bluedroid_pm_rfkill_set_power(void *data, bool blocked)
72 {
73         struct bluedroid_pm_data *bluedroid_pm = data;
74         /*
75          * check if BT gpio_shutdown line status and current request are same.
76          * If same, then return, else perform requested operation.
77          */
78         if (gpio_get_value(bluedroid_pm->gpio_shutdown) == !blocked)
79                 return 0;
80
81         if (blocked) {
82                 if (bluedroid_pm->gpio_shutdown)
83                         gpio_set_value(bluedroid_pm->gpio_shutdown, 0);
84                 if (bluedroid_pm->gpio_reset)
85                         gpio_set_value(bluedroid_pm->gpio_reset, 0);
86                 if (bluedroid_pm->vdd_3v3)
87                         regulator_disable(bluedroid_pm->vdd_3v3);
88                 if (bluedroid_pm->vdd_1v8)
89                         regulator_disable(bluedroid_pm->vdd_1v8);
90                 if (bluedroid_pm->ext_wake)
91                         wake_unlock(&bluedroid_pm->wake_lock);
92                 pm_qos_remove_request(&bluedroid_pm->resume_cpu_freq_req);
93         } else {
94                 if (bluedroid_pm->vdd_3v3)
95                         regulator_enable(bluedroid_pm->vdd_3v3);
96                 if (bluedroid_pm->vdd_1v8)
97                         regulator_enable(bluedroid_pm->vdd_1v8);
98                 if (bluedroid_pm->gpio_shutdown)
99                         gpio_set_value(bluedroid_pm->gpio_shutdown, 1);
100                 if (bluedroid_pm->gpio_reset)
101                         gpio_set_value(bluedroid_pm->gpio_reset, 1);
102                 pm_qos_add_request(&bluedroid_pm->resume_cpu_freq_req,
103                                 PM_QOS_CPU_FREQ_MIN, PM_QOS_DEFAULT_VALUE);
104         }
105         bluedroid_pm->is_blocked = blocked;
106         return 0;
107 }
108
109 static const struct rfkill_ops bluedroid_pm_rfkill_ops = {
110         .set_block = bluedroid_pm_rfkill_set_power,
111 };
112
113 /*
114  * This API is added to set block state by ext driver,
115  * when bluedroid_pm rfkill is not used but host_wake functionality to be used.
116  * Eg: btwilink driver
117  */
118 void bluedroid_pm_set_ext_state(bool blocked)
119 {
120         bluedroid_pm_blocked = blocked;
121 }
122 EXPORT_SYMBOL(bluedroid_pm_set_ext_state);
123
124 static int bluedroid_pm_probe(struct platform_device *pdev)
125 {
126         static struct bluedroid_pm_data *bluedroid_pm;
127         struct rfkill *rfkill;
128         struct resource *res;
129         int ret;
130         bool enable = false;  /* off */
131
132         bluedroid_pm = kzalloc(sizeof(*bluedroid_pm), GFP_KERNEL);
133         if (!bluedroid_pm)
134                 return -ENOMEM;
135
136         bluedroid_pm->vdd_3v3 = regulator_get(&pdev->dev, "vdd_bt_3v3");
137         if (IS_ERR_OR_NULL(bluedroid_pm->vdd_3v3)) {
138                 pr_warn("%s: regulator vdd_bt_3v3 not available\n", __func__);
139                 bluedroid_pm->vdd_3v3 = NULL;
140         }
141         bluedroid_pm->vdd_1v8 = regulator_get(&pdev->dev, "vddio_bt_1v8");
142         if (IS_ERR_OR_NULL(bluedroid_pm->vdd_1v8)) {
143                 pr_warn("%s: regulator vddio_bt_1v8 not available\n", __func__);
144                 bluedroid_pm->vdd_1v8 = NULL;
145         }
146
147         res = platform_get_resource_byname(pdev, IORESOURCE_IO, "reset_gpio");
148         if (res) {
149                 bluedroid_pm->gpio_reset = res->start;
150                 ret = gpio_request(bluedroid_pm->gpio_reset, "reset_gpio");
151                 if (ret) {
152                         pr_err("%s: Failed to get reset gpio\n", __func__);
153                         goto free_res;
154                 }
155                 gpio_direction_output(bluedroid_pm->gpio_reset, enable);
156         } else {
157                 pr_debug("%s: Reset gpio not registered.\n", __func__);
158                 bluedroid_pm->gpio_reset = 0;
159         }
160
161         res = platform_get_resource_byname(pdev, IORESOURCE_IO,
162                                                 "shutdown_gpio");
163         if (res) {
164                 bluedroid_pm->gpio_shutdown = res->start;
165                 ret = gpio_request(bluedroid_pm->gpio_shutdown,
166                                                 "shutdown_gpio");
167                 if (ret) {
168                         pr_err("%s: Failed to get shutdown gpio\n", __func__);
169                         goto free_res;
170                 }
171                 gpio_direction_output(bluedroid_pm->gpio_shutdown, enable);
172         } else {
173                 pr_debug("%s: shutdown gpio not registered\n", __func__);
174                 bluedroid_pm->gpio_shutdown = 0;
175         }
176
177         /*
178          * make sure at-least one of the GPIO or regulators avaiable to
179          * register with rfkill is defined
180          */
181         if (bluedroid_pm->gpio_reset || bluedroid_pm->gpio_shutdown ||
182                 bluedroid_pm->vdd_1v8 || bluedroid_pm->vdd_3v3) {
183                 rfkill = rfkill_alloc(pdev->name, &pdev->dev,
184                                 RFKILL_TYPE_BLUETOOTH, &bluedroid_pm_rfkill_ops,
185                                 bluedroid_pm);
186
187                 if (unlikely(!rfkill))
188                         goto free_res;
189
190                 bluedroid_pm->is_blocked = !enable;
191                 rfkill_set_states(rfkill, bluedroid_pm->is_blocked, false);
192
193                 ret = rfkill_register(rfkill);
194
195                 if (unlikely(ret)) {
196                         rfkill_destroy(rfkill);
197                         kfree(rfkill);
198                         goto free_res;
199                 }
200                 bluedroid_pm->rfkill = rfkill;
201         }
202
203         res = platform_get_resource_byname(pdev, IORESOURCE_IO,
204                                 "gpio_host_wake");
205         if (res) {
206                 bluedroid_pm->host_wake = res->start;
207                 ret = gpio_request(bluedroid_pm->host_wake, "bt_host_wake");
208                 if (ret) {
209                         pr_err("%s: Failed to get host_wake gpio\n", __func__);
210                         goto free_res;
211                 }
212                 /* configure host_wake as input */
213                 gpio_direction_input(bluedroid_pm->host_wake);
214         } else {
215                 pr_debug("%s: gpio_host_wake not registered\n", __func__);
216                 bluedroid_pm->host_wake = 0;
217         }
218
219
220         res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "host_wake");
221         if (res) {
222                 pr_err("%s : found host_wake irq\n", __func__);
223                 bluedroid_pm->host_wake_irq = res->start;
224                 ret = request_irq(bluedroid_pm->host_wake_irq,
225                                         bluedroid_pm_hostwake_isr,
226                                         IRQF_DISABLED | IRQF_TRIGGER_RISING,
227                                         "bluetooth hostwake", bluedroid_pm);
228                 if (ret) {
229                         pr_err("%s: Failed to get host_wake irq\n", __func__);
230                         goto free_res;
231                 }
232         } else {
233                 pr_debug("%s: host_wake not registered\n", __func__);
234                 bluedroid_pm->host_wake_irq = 0;
235         }
236
237         res = platform_get_resource_byname(pdev, IORESOURCE_IO,
238                                 "gpio_ext_wake");
239         if (res) {
240                 bluedroid_pm->ext_wake = res->start;
241                 ret = gpio_request(bluedroid_pm->ext_wake, "bt_ext_wake");
242                 if (ret) {
243                         pr_err("%s: Failed to get ext_wake gpio\n", __func__);
244                         goto free_res;
245                 }
246                 /* configure ext_wake as output mode*/
247                 gpio_direction_output(bluedroid_pm->ext_wake, 1);
248                 if (create_bt_proc_interface(bluedroid_pm)) {
249                         pr_err("%s: Failed to create proc interface", __func__);
250                         goto free_res;
251                 }
252                 /* initialize wake lock */
253                 wake_lock_init(&bluedroid_pm->wake_lock, WAKE_LOCK_SUSPEND,
254                                                                 "bluedroid_pm");
255         } else {
256                 pr_debug("%s: gpio_ext_wake not registered\n", __func__);
257                 bluedroid_pm->ext_wake = 0;
258         }
259
260         res = platform_get_resource_byname(pdev, IORESOURCE_IO,
261                                 "min_cpu_freq");
262         if (res)
263                 bluedroid_pm->resume_min_frequency = res->start;
264
265         platform_set_drvdata(pdev, bluedroid_pm);
266         pr_debug("RFKILL BT driver successfully registered");
267         return 0;
268
269 free_res:
270         if (bluedroid_pm->vdd_3v3)
271                 regulator_put(bluedroid_pm->vdd_3v3);
272         if (bluedroid_pm->vdd_1v8)
273                 regulator_put(bluedroid_pm->vdd_1v8);
274         if (bluedroid_pm->gpio_shutdown)
275                 gpio_free(bluedroid_pm->gpio_shutdown);
276         if (bluedroid_pm->gpio_reset)
277                 gpio_free(bluedroid_pm->gpio_reset);
278         if (bluedroid_pm->ext_wake)
279                 gpio_free(bluedroid_pm->ext_wake);
280         if (bluedroid_pm->host_wake)
281                 gpio_free(bluedroid_pm->host_wake);
282         if (bluedroid_pm->rfkill) {
283                 rfkill_unregister(bluedroid_pm->rfkill);
284                 rfkill_destroy(bluedroid_pm->rfkill);
285                 kfree(bluedroid_pm->rfkill);
286         }
287         kfree(bluedroid_pm);
288         return -ENODEV;
289 }
290
291 static int bluedroid_pm_remove(struct platform_device *pdev)
292 {
293         struct bluedroid_pm_data *bluedroid_pm = platform_get_drvdata(pdev);
294
295         if (bluedroid_pm->host_wake)
296                 gpio_free(bluedroid_pm->host_wake);
297         if (bluedroid_pm->host_wake_irq)
298                 free_irq(bluedroid_pm->host_wake_irq, NULL);
299         if (bluedroid_pm->ext_wake) {
300                 wake_lock_destroy(&bluedroid_pm->wake_lock);
301                 gpio_free(bluedroid_pm->ext_wake);
302                 remove_bt_proc_interface();
303         }
304         if (bluedroid_pm->gpio_reset || bluedroid_pm->gpio_shutdown ||
305                 bluedroid_pm->vdd_1v8 || bluedroid_pm->vdd_3v3) {
306                 rfkill_unregister(bluedroid_pm->rfkill);
307                 rfkill_destroy(bluedroid_pm->rfkill);
308                 kfree(bluedroid_pm->rfkill);
309         }
310         if (bluedroid_pm->gpio_shutdown)
311                 gpio_free(bluedroid_pm->gpio_shutdown);
312         if (bluedroid_pm->gpio_reset)
313                 gpio_free(bluedroid_pm->gpio_reset);
314         if (bluedroid_pm->vdd_3v3)
315                 regulator_put(bluedroid_pm->vdd_3v3);
316         if (bluedroid_pm->vdd_1v8)
317                 regulator_put(bluedroid_pm->vdd_1v8);
318         kfree(bluedroid_pm);
319
320         return 0;
321 }
322
323 static int bluedroid_pm_suspend(struct platform_device *pdev,
324                                                 pm_message_t state)
325 {
326         struct bluedroid_pm_data *bluedroid_pm = platform_get_drvdata(pdev);
327         if (bluedroid_pm->host_wake)
328                 if (!bluedroid_pm->is_blocked || !bluedroid_pm_blocked)
329                         enable_irq_wake(bluedroid_pm->host_wake_irq);
330
331         return 0;
332 }
333
334 static int bluedroid_pm_resume(struct platform_device *pdev)
335 {
336         struct bluedroid_pm_data *bluedroid_pm = platform_get_drvdata(pdev);
337         if (bluedroid_pm->host_wake)
338                 if (!bluedroid_pm->is_blocked || !bluedroid_pm_blocked) {
339                         disable_irq_wake(bluedroid_pm->host_wake_irq);
340                         if (bluedroid_pm->resume_min_frequency)
341                                 pm_qos_update_request_timeout(
342                                         &bluedroid_pm->resume_cpu_freq_req,
343                                         bluedroid_pm->resume_min_frequency,
344                                         DEFAULT_RESUME_CPU_TIMEOUT);
345                 }
346
347         return 0;
348 }
349 static struct platform_driver bluedroid_pm_driver = {
350         .probe = bluedroid_pm_probe,
351         .remove = bluedroid_pm_remove,
352         .suspend = bluedroid_pm_suspend,
353         .resume = bluedroid_pm_resume,
354         .driver = {
355                    .name = "bluedroid_pm",
356                    .owner = THIS_MODULE,
357         },
358 };
359
360 static int lpm_read_proc(char *page, char **start, off_t offset,
361                                         int count, int *eof, void *data)
362 {
363         *eof = 1;
364         return sprintf(page, "lpm_read");
365 }
366
367 static int lpm_write_proc(struct file *file, const char *buffer,
368                                         unsigned long count, void *data)
369 {
370         char *buf;
371         struct bluedroid_pm_data *bluedroid_pm = data;
372
373         if (count < 1)
374                 return -EINVAL;
375
376         buf = kmalloc(count, GFP_KERNEL);
377         if (!buf)
378                 return -ENOMEM;
379
380         if (copy_from_user(buf, buffer, count)) {
381                 kfree(buf);
382                 return -EFAULT;
383         }
384
385         if (!bluedroid_pm->is_blocked) {
386                 if (buf[0] == '0') {
387                         gpio_set_value(bluedroid_pm->ext_wake, 0);
388                         wake_unlock(&bluedroid_pm->wake_lock);
389                 } else if (buf[0] == '1') {
390                         gpio_set_value(bluedroid_pm->ext_wake, 1);
391                         wake_lock(&bluedroid_pm->wake_lock);
392                 } else {
393                         kfree(buf);
394                         return -EINVAL;
395                 }
396         }
397
398         kfree(buf);
399         return count;
400 }
401
402 static void remove_bt_proc_interface(void)
403 {
404         remove_proc_entry("lpm", bluetooth_sleep_dir);
405         remove_proc_entry("sleep", proc_bt_dir);
406         remove_proc_entry("bluetooth", 0);
407 }
408
409 static int create_bt_proc_interface(void *drv_data)
410 {
411         int retval;
412         struct proc_dir_entry *ent;
413
414         proc_bt_dir = proc_mkdir("bluetooth", NULL);
415         if (proc_bt_dir == NULL) {
416                 pr_err("Unable to create /proc/bluetooth directory");
417                 return -ENOMEM;
418         }
419
420         bluetooth_sleep_dir = proc_mkdir("sleep", proc_bt_dir);
421         if (proc_bt_dir == NULL) {
422                 pr_err("Unable to create /proc/bluetooth directory");
423                 return -ENOMEM;
424         }
425
426         /* Creating read/write "btwake" entry */
427         ent = create_proc_entry("lpm", 0622, bluetooth_sleep_dir);
428         if (ent == NULL) {
429                 pr_err("Unable to create /proc/%s/btwake entry", PROC_DIR);
430                 retval = -ENOMEM;
431                 goto fail;
432         }
433         ent->read_proc  = lpm_read_proc;
434         ent->write_proc = lpm_write_proc;
435         ent->data       = drv_data;
436         return 0;
437 fail:
438         remove_proc_entry("lpm", bluetooth_sleep_dir);
439         remove_proc_entry("sleep", proc_bt_dir);
440         remove_proc_entry("bluetooth", 0);
441         return retval;
442 }
443
444 static int __init bluedroid_pm_init(void)
445 {
446         return platform_driver_register(&bluedroid_pm_driver);
447 }
448
449 static void __exit bluedroid_pm_exit(void)
450 {
451         platform_driver_unregister(&bluedroid_pm_driver);
452 }
453
454 module_init(bluedroid_pm_init);
455 module_exit(bluedroid_pm_exit);
456
457 MODULE_DESCRIPTION("bluedroid PM");
458 MODULE_AUTHOR("NVIDIA");
459 MODULE_LICENSE("GPL");