misc: ti-st: Adding IORESOURCE_IRQ_OPTIONAL case
[linux-2.6.git] / drivers / misc / bcm4329_rfkill.c
1 /*
2  * drivers/misc/bcm4329_rfkill.c
3  *
4  * Copyright (c) 2010, 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/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/rfkill.h>
31 #include <linux/platform_device.h>
32 #include <linux/clk.h>
33 #include <linux/slab.h>
34 #include <linux/delay.h>
35 #include <mach/gpio-tegra.h>
36
37 struct bcm4329_rfkill_data {
38         int gpio_reset;
39         int gpio_shutdown;
40         int delay;
41         struct clk *bt_32k_clk;
42 };
43
44 static struct bcm4329_rfkill_data *bcm4329_rfkill;
45
46 static int bcm4329_bt_rfkill_set_power(void *data, bool blocked)
47 {
48         /*
49          * check if BT gpio_shutdown line status and current request are same.
50          * If same, then return, else perform requested operation.
51          */
52         if (gpio_get_value(bcm4329_rfkill->gpio_shutdown) && !blocked)
53                 return 0;
54
55         if (blocked) {
56                 if (bcm4329_rfkill->gpio_shutdown)
57                         gpio_direction_output(bcm4329_rfkill->gpio_shutdown, 0);
58                 if (bcm4329_rfkill->gpio_reset)
59                         gpio_direction_output(bcm4329_rfkill->gpio_reset, 0);
60                 if (bcm4329_rfkill->bt_32k_clk)
61                         clk_disable_unprepare(bcm4329_rfkill->bt_32k_clk);
62         } else {
63                 if (bcm4329_rfkill->bt_32k_clk)
64                         clk_prepare_enable(bcm4329_rfkill->bt_32k_clk);
65                 if (bcm4329_rfkill->gpio_shutdown)
66                 {
67                         gpio_direction_output(bcm4329_rfkill->gpio_shutdown, 0);
68                         msleep(100);
69                         gpio_direction_output(bcm4329_rfkill->gpio_shutdown, 1);
70                         msleep(100);
71                 }
72                 if (bcm4329_rfkill->gpio_reset)
73                 {
74                         gpio_direction_output(bcm4329_rfkill->gpio_reset, 0);
75                         msleep(100);
76                         gpio_direction_output(bcm4329_rfkill->gpio_reset, 1);
77                         msleep(100);
78                 }
79         }
80
81         return 0;
82 }
83
84 static const struct rfkill_ops bcm4329_bt_rfkill_ops = {
85         .set_block = bcm4329_bt_rfkill_set_power,
86 };
87
88 static int bcm4329_rfkill_probe(struct platform_device *pdev)
89 {
90         struct rfkill *bt_rfkill;
91         struct resource *res;
92         int ret;
93         bool enable = false;  /* off */
94         bool default_sw_block_state;
95
96         bcm4329_rfkill = kzalloc(sizeof(*bcm4329_rfkill), GFP_KERNEL);
97         if (!bcm4329_rfkill)
98                 return -ENOMEM;
99
100         bcm4329_rfkill->bt_32k_clk = clk_get(&pdev->dev, "bcm4329_32k_clk");
101         if (IS_ERR(bcm4329_rfkill->bt_32k_clk)) {
102                 pr_warn("%s: can't find bcm4329_32k_clk.\
103                                 assuming 32k clock to chip\n", __func__);
104                 bcm4329_rfkill->bt_32k_clk = NULL;
105         }
106
107         res = platform_get_resource_byname(pdev, IORESOURCE_IO,
108                                                 "bcm4329_nreset_gpio");
109         if (res) {
110                 bcm4329_rfkill->gpio_reset = res->start;
111                 ret = gpio_request(bcm4329_rfkill->gpio_reset,
112                                                 "bcm4329_nreset_gpio");
113         } else {
114                 pr_warn("%s : can't find reset gpio. "
115                         "reset gpio may not be defined for "
116                         "this platform \n", __func__);
117                 bcm4329_rfkill->gpio_reset = 0;
118         }
119
120         res = platform_get_resource_byname(pdev, IORESOURCE_IO,
121                                                 "bcm4329_nshutdown_gpio");
122         if (res) {
123                 bcm4329_rfkill->gpio_shutdown = res->start;
124                 ret = gpio_request(bcm4329_rfkill->gpio_shutdown,
125                                                 "bcm4329_nshutdown_gpio");
126         } else {
127                 pr_warn("%s : can't find shutdown gpio "
128                         "shutdown gpio may not be defined for "
129                         "this platform \n", __func__);
130                 bcm4329_rfkill->gpio_shutdown = 0;
131         }
132
133         /* make sure at-least one of the GPIO is defined */
134         if (!bcm4329_rfkill->gpio_reset && !bcm4329_rfkill->gpio_shutdown)
135                 goto free_bcm_res;
136
137         if (bcm4329_rfkill->bt_32k_clk && enable)
138                 clk_prepare_enable(bcm4329_rfkill->bt_32k_clk);
139         if (bcm4329_rfkill->gpio_shutdown)
140                 gpio_direction_output(bcm4329_rfkill->gpio_shutdown, enable);
141         if (bcm4329_rfkill->gpio_reset)
142                 gpio_direction_output(bcm4329_rfkill->gpio_reset, enable);
143
144         bt_rfkill = rfkill_alloc("bcm4329 Bluetooth", &pdev->dev,
145                                 RFKILL_TYPE_BLUETOOTH, &bcm4329_bt_rfkill_ops,
146                                 NULL);
147
148         if (unlikely(!bt_rfkill))
149                 goto free_bcm_res;
150
151         default_sw_block_state = !enable;
152         rfkill_set_states(bt_rfkill, default_sw_block_state, false);
153
154         ret = rfkill_register(bt_rfkill);
155
156         if (unlikely(ret)) {
157                 rfkill_destroy(bt_rfkill);
158                 goto free_bcm_res;
159         }
160
161         return 0;
162
163 free_bcm_res:
164         if (bcm4329_rfkill->gpio_shutdown)
165                 gpio_free(bcm4329_rfkill->gpio_shutdown);
166         if (bcm4329_rfkill->gpio_reset)
167                 gpio_free(bcm4329_rfkill->gpio_reset);
168         if (bcm4329_rfkill->bt_32k_clk && enable)
169                 clk_disable_unprepare(bcm4329_rfkill->bt_32k_clk);
170         if (bcm4329_rfkill->bt_32k_clk)
171                 clk_put(bcm4329_rfkill->bt_32k_clk);
172         kfree(bcm4329_rfkill);
173         return -ENODEV;
174 }
175
176 static int bcm4329_rfkill_remove(struct platform_device *pdev)
177 {
178         struct rfkill *bt_rfkill = platform_get_drvdata(pdev);
179
180         if (bcm4329_rfkill->bt_32k_clk)
181                 clk_put(bcm4329_rfkill->bt_32k_clk);
182         rfkill_unregister(bt_rfkill);
183         rfkill_destroy(bt_rfkill);
184         if (bcm4329_rfkill->gpio_shutdown)
185                 gpio_free(bcm4329_rfkill->gpio_shutdown);
186         if (bcm4329_rfkill->gpio_reset)
187                 gpio_free(bcm4329_rfkill->gpio_reset);
188         kfree(bcm4329_rfkill);
189
190         return 0;
191 }
192
193 static struct platform_driver bcm4329_rfkill_driver = {
194         .probe = bcm4329_rfkill_probe,
195         .remove = bcm4329_rfkill_remove,
196         .driver = {
197                    .name = "bcm4329_rfkill",
198                    .owner = THIS_MODULE,
199         },
200 };
201
202 static int __init bcm4329_rfkill_init(void)
203 {
204         return platform_driver_register(&bcm4329_rfkill_driver);
205 }
206
207 static void __exit bcm4329_rfkill_exit(void)
208 {
209         platform_driver_unregister(&bcm4329_rfkill_driver);
210 }
211
212 module_init(bcm4329_rfkill_init);
213 module_exit(bcm4329_rfkill_exit);
214
215 MODULE_DESCRIPTION("BCM4329 rfkill");
216 MODULE_AUTHOR("NVIDIA");
217 MODULE_LICENSE("GPL");