diff options
author | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
---|---|---|
committer | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
commit | fcc9d2e5a6c89d22b8b773a64fb4ad21ac318446 (patch) | |
tree | a57612d1888735a2ec7972891b68c1ac5ec8faea /drivers/misc/bcm4329_rfkill.c | |
parent | 8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff) |
Diffstat (limited to 'drivers/misc/bcm4329_rfkill.c')
-rw-r--r-- | drivers/misc/bcm4329_rfkill.c | 207 |
1 files changed, 207 insertions, 0 deletions
diff --git a/drivers/misc/bcm4329_rfkill.c b/drivers/misc/bcm4329_rfkill.c new file mode 100644 index 00000000000..a077326f255 --- /dev/null +++ b/drivers/misc/bcm4329_rfkill.c | |||
@@ -0,0 +1,207 @@ | |||
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 | |||
36 | struct bcm4329_rfkill_data { | ||
37 | int gpio_reset; | ||
38 | int gpio_shutdown; | ||
39 | int delay; | ||
40 | struct clk *bt_32k_clk; | ||
41 | }; | ||
42 | |||
43 | static struct bcm4329_rfkill_data *bcm4329_rfkill; | ||
44 | |||
45 | static int bcm4329_bt_rfkill_set_power(void *data, bool blocked) | ||
46 | { | ||
47 | if (blocked) { | ||
48 | if (bcm4329_rfkill->gpio_shutdown) | ||
49 | gpio_direction_output(bcm4329_rfkill->gpio_shutdown, 0); | ||
50 | if (bcm4329_rfkill->gpio_reset) | ||
51 | gpio_direction_output(bcm4329_rfkill->gpio_reset, 0); | ||
52 | if (bcm4329_rfkill->bt_32k_clk) | ||
53 | clk_disable(bcm4329_rfkill->bt_32k_clk); | ||
54 | } else { | ||
55 | if (bcm4329_rfkill->bt_32k_clk) | ||
56 | clk_enable(bcm4329_rfkill->bt_32k_clk); | ||
57 | if (bcm4329_rfkill->gpio_shutdown) | ||
58 | { | ||
59 | gpio_direction_output(bcm4329_rfkill->gpio_shutdown, 0); | ||
60 | msleep(100); | ||
61 | gpio_direction_output(bcm4329_rfkill->gpio_shutdown, 1); | ||
62 | msleep(100); | ||
63 | } | ||
64 | if (bcm4329_rfkill->gpio_reset) | ||
65 | { | ||
66 | gpio_direction_output(bcm4329_rfkill->gpio_reset, 0); | ||
67 | msleep(100); | ||
68 | gpio_direction_output(bcm4329_rfkill->gpio_reset, 1); | ||
69 | msleep(100); | ||
70 | } | ||
71 | } | ||
72 | |||
73 | return 0; | ||
74 | } | ||
75 | |||
76 | static const struct rfkill_ops bcm4329_bt_rfkill_ops = { | ||
77 | .set_block = bcm4329_bt_rfkill_set_power, | ||
78 | }; | ||
79 | |||
80 | static int bcm4329_rfkill_probe(struct platform_device *pdev) | ||
81 | { | ||
82 | struct rfkill *bt_rfkill; | ||
83 | struct resource *res; | ||
84 | int ret; | ||
85 | bool enable = false; /* off */ | ||
86 | bool default_sw_block_state; | ||
87 | |||
88 | bcm4329_rfkill = kzalloc(sizeof(*bcm4329_rfkill), GFP_KERNEL); | ||
89 | if (!bcm4329_rfkill) | ||
90 | return -ENOMEM; | ||
91 | |||
92 | bcm4329_rfkill->bt_32k_clk = clk_get(&pdev->dev, "bcm4329_32k_clk"); | ||
93 | if (IS_ERR(bcm4329_rfkill->bt_32k_clk)) { | ||
94 | pr_warn("%s: can't find bcm4329_32k_clk.\ | ||
95 | assuming 32k clock to chip\n", __func__); | ||
96 | bcm4329_rfkill->bt_32k_clk = NULL; | ||
97 | } | ||
98 | |||
99 | res = platform_get_resource_byname(pdev, IORESOURCE_IO, | ||
100 | "bcm4329_nreset_gpio"); | ||
101 | if (res) { | ||
102 | bcm4329_rfkill->gpio_reset = res->start; | ||
103 | tegra_gpio_enable(bcm4329_rfkill->gpio_reset); | ||
104 | ret = gpio_request(bcm4329_rfkill->gpio_reset, | ||
105 | "bcm4329_nreset_gpio"); | ||
106 | } else { | ||
107 | pr_warn("%s : can't find reset gpio.\n", __func__); | ||
108 | bcm4329_rfkill->gpio_reset = 0; | ||
109 | } | ||
110 | |||
111 | res = platform_get_resource_byname(pdev, IORESOURCE_IO, | ||
112 | "bcm4329_nshutdown_gpio"); | ||
113 | if (res) { | ||
114 | bcm4329_rfkill->gpio_shutdown = res->start; | ||
115 | tegra_gpio_enable(bcm4329_rfkill->gpio_shutdown); | ||
116 | ret = gpio_request(bcm4329_rfkill->gpio_shutdown, | ||
117 | "bcm4329_nshutdown_gpio"); | ||
118 | } else { | ||
119 | pr_warn("%s : can't find shutdown gpio.\n", __func__); | ||
120 | bcm4329_rfkill->gpio_shutdown = 0; | ||
121 | } | ||
122 | |||
123 | /* make sure at-least one of the GPIO is defined */ | ||
124 | if (!bcm4329_rfkill->gpio_reset && !bcm4329_rfkill->gpio_shutdown) | ||
125 | goto free_bcm_res; | ||
126 | |||
127 | if (bcm4329_rfkill->bt_32k_clk && enable) | ||
128 | clk_enable(bcm4329_rfkill->bt_32k_clk); | ||
129 | if (bcm4329_rfkill->gpio_shutdown) | ||
130 | gpio_direction_output(bcm4329_rfkill->gpio_shutdown, enable); | ||
131 | if (bcm4329_rfkill->gpio_reset) | ||
132 | gpio_direction_output(bcm4329_rfkill->gpio_reset, enable); | ||
133 | |||
134 | bt_rfkill = rfkill_alloc("bcm4329 Bluetooth", &pdev->dev, | ||
135 | RFKILL_TYPE_BLUETOOTH, &bcm4329_bt_rfkill_ops, | ||
136 | NULL); | ||
137 | |||
138 | if (unlikely(!bt_rfkill)) | ||
139 | goto free_bcm_res; | ||
140 | |||
141 | default_sw_block_state = !enable; | ||
142 | rfkill_set_states(bt_rfkill, default_sw_block_state, false); | ||
143 | |||
144 | ret = rfkill_register(bt_rfkill); | ||
145 | |||
146 | if (unlikely(ret)) { | ||
147 | rfkill_destroy(bt_rfkill); | ||
148 | goto free_bcm_res; | ||
149 | } | ||
150 | |||
151 | return 0; | ||
152 | |||
153 | free_bcm_res: | ||
154 | if (bcm4329_rfkill->gpio_shutdown) | ||
155 | gpio_free(bcm4329_rfkill->gpio_shutdown); | ||
156 | if (bcm4329_rfkill->gpio_reset) | ||
157 | gpio_free(bcm4329_rfkill->gpio_reset); | ||
158 | if (bcm4329_rfkill->bt_32k_clk && enable) | ||
159 | clk_disable(bcm4329_rfkill->bt_32k_clk); | ||
160 | if (bcm4329_rfkill->bt_32k_clk) | ||
161 | clk_put(bcm4329_rfkill->bt_32k_clk); | ||
162 | kfree(bcm4329_rfkill); | ||
163 | return -ENODEV; | ||
164 | } | ||
165 | |||
166 | static int bcm4329_rfkill_remove(struct platform_device *pdev) | ||
167 | { | ||
168 | struct rfkill *bt_rfkill = platform_get_drvdata(pdev); | ||
169 | |||
170 | if (bcm4329_rfkill->bt_32k_clk) | ||
171 | clk_put(bcm4329_rfkill->bt_32k_clk); | ||
172 | rfkill_unregister(bt_rfkill); | ||
173 | rfkill_destroy(bt_rfkill); | ||
174 | if (bcm4329_rfkill->gpio_shutdown) | ||
175 | gpio_free(bcm4329_rfkill->gpio_shutdown); | ||
176 | if (bcm4329_rfkill->gpio_reset) | ||
177 | gpio_free(bcm4329_rfkill->gpio_reset); | ||
178 | kfree(bcm4329_rfkill); | ||
179 | |||
180 | return 0; | ||
181 | } | ||
182 | |||
183 | static struct platform_driver bcm4329_rfkill_driver = { | ||
184 | .probe = bcm4329_rfkill_probe, | ||
185 | .remove = bcm4329_rfkill_remove, | ||
186 | .driver = { | ||
187 | .name = "bcm4329_rfkill", | ||
188 | .owner = THIS_MODULE, | ||
189 | }, | ||
190 | }; | ||
191 | |||
192 | static int __init bcm4329_rfkill_init(void) | ||
193 | { | ||
194 | return platform_driver_register(&bcm4329_rfkill_driver); | ||
195 | } | ||
196 | |||
197 | static void __exit bcm4329_rfkill_exit(void) | ||
198 | { | ||
199 | platform_driver_unregister(&bcm4329_rfkill_driver); | ||
200 | } | ||
201 | |||
202 | module_init(bcm4329_rfkill_init); | ||
203 | module_exit(bcm4329_rfkill_exit); | ||
204 | |||
205 | MODULE_DESCRIPTION("BCM4329 rfkill"); | ||
206 | MODULE_AUTHOR("NVIDIA"); | ||
207 | MODULE_LICENSE("GPL"); | ||