]>
Commit | Line | Data |
---|---|---|
0c41633e SS |
1 | From adf0f7b7d7c0083dd936fe46423b89e974f8df12 Mon Sep 17 00:00:00 2001 |
2 | From: Vladimir Ermakov <vooon341@gmail.com> | |
3 | Date: Wed, 10 Jul 2013 03:06:54 +0400 | |
4 | Subject: [PATCH] ARM i.MX6 Wandboard add wifi+bt rfkill driver | |
5 | ||
6 | BRCM WiFi module requires initialization for control gpio; | |
7 | Additional provides rfkill funcs. | |
8 | ||
9 | v2: fix wrong probe func in driver struct | |
10 | v3: add imx6qdl compatible | |
11 | ||
12 | Signed-off-by: Vladimir Ermakov <vooon341@gmail.com> | |
13 | --- | |
14 | arch/arm/mach-imx/devices/Kconfig | 6 + | |
15 | arch/arm/mach-imx/devices/Makefile | 1 + | |
16 | arch/arm/mach-imx/devices/wand-rfkill.c | 290 ++++++++++++++++++++++++++++++++ | |
17 | 3 files changed, 297 insertions(+) | |
18 | create mode 100644 arch/arm/mach-imx/devices/wand-rfkill.c | |
19 | ||
20 | diff --git a/arch/arm/mach-imx/devices/Kconfig b/arch/arm/mach-imx/devices/Kconfig | |
21 | index 68c74fb..a0adf75 100644 | |
22 | --- a/arch/arm/mach-imx/devices/Kconfig | |
23 | +++ b/arch/arm/mach-imx/devices/Kconfig | |
24 | @@ -85,3 +85,9 @@ config IMX_HAVE_PLATFORM_SDHCI_ESDHC_IMX | |
25 | ||
26 | config IMX_HAVE_PLATFORM_SPI_IMX | |
27 | bool | |
28 | + | |
29 | +config WAND_RFKILL | |
30 | + tristate "Wandboard RF Kill support" | |
31 | + depends on SOC_IMX6Q | |
32 | + default m | |
33 | + select RFKILL | |
34 | diff --git a/arch/arm/mach-imx/devices/Makefile b/arch/arm/mach-imx/devices/Makefile | |
35 | index 67416fb..b2aded5 100644 | |
36 | --- a/arch/arm/mach-imx/devices/Makefile | |
37 | +++ b/arch/arm/mach-imx/devices/Makefile | |
38 | @@ -30,3 +30,4 @@ obj-$(CONFIG_IMX_HAVE_PLATFORM_MXC_W1) += platform-mxc_w1.o | |
39 | obj-$(CONFIG_IMX_HAVE_PLATFORM_SDHCI_ESDHC_IMX) += platform-sdhci-esdhc-imx.o | |
40 | obj-$(CONFIG_IMX_HAVE_PLATFORM_SPI_IMX) += platform-spi_imx.o | |
41 | obj-$(CONFIG_IMX_HAVE_PLATFORM_MX2_EMMA) += platform-mx2-emma.o | |
42 | +obj-$(CONFIG_WAND_RFKILL) += wand-rfkill.o | |
43 | diff --git a/arch/arm/mach-imx/devices/wand-rfkill.c b/arch/arm/mach-imx/devices/wand-rfkill.c | |
44 | new file mode 100644 | |
45 | index 0000000..da7ef9f | |
46 | --- /dev/null | |
47 | +++ b/arch/arm/mach-imx/devices/wand-rfkill.c | |
48 | @@ -0,0 +1,290 @@ | |
49 | +/* | |
50 | + * arch/arm/mach-imx/devices/wand-rfkill.c | |
51 | + * | |
52 | + * Copyright (C) 2013 Vladimir Ermakov <vooon341@gmail.com> | |
53 | + * | |
54 | + * based on net/rfkill/rfkill-gpio.c | |
55 | + * | |
56 | + * This software is licensed under the terms of the GNU General Public | |
57 | + * License version 2, as published by the Free Software Foundation, and | |
58 | + * may be copied, distributed, and modified under those terms. | |
59 | + * | |
60 | + * This program is distributed in the hope that it will be useful, | |
61 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
62 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
63 | + * GNU General Public License for more details. | |
64 | + * | |
65 | + */ | |
66 | + | |
67 | +#include <linux/of.h> | |
68 | +#include <linux/of_gpio.h> | |
69 | +#include <linux/of_device.h> | |
70 | +#include <linux/pinctrl/consumer.h> | |
71 | +#include <linux/platform_device.h> | |
72 | +#include <linux/rfkill.h> | |
73 | +#include <linux/delay.h> | |
74 | +#include <linux/kernel.h> | |
75 | +#include <linux/module.h> | |
76 | +#include <linux/slab.h> | |
77 | + | |
78 | + | |
79 | +struct wand_rfkill_data { | |
80 | + struct rfkill *rfkill_dev; | |
81 | + int shutdown_gpio; | |
82 | + const char *shutdown_name; | |
83 | +}; | |
84 | + | |
85 | +static int wand_rfkill_set_block(void *data, bool blocked) | |
86 | +{ | |
87 | + struct wand_rfkill_data *rfkill = data; | |
88 | + | |
89 | + pr_debug("wandboard-rfkill: set block %d\n", blocked); | |
90 | + | |
91 | + if (blocked) { | |
92 | + if (gpio_is_valid(rfkill->shutdown_gpio)) | |
93 | + gpio_direction_output(rfkill->shutdown_gpio, 0); | |
94 | + } else { | |
95 | + if (gpio_is_valid(rfkill->shutdown_gpio)) | |
96 | + gpio_direction_output(rfkill->shutdown_gpio, 1); | |
97 | + } | |
98 | + | |
99 | + return 0; | |
100 | +} | |
101 | + | |
102 | +static const struct rfkill_ops wand_rfkill_ops = { | |
103 | + .set_block = wand_rfkill_set_block, | |
104 | +}; | |
105 | + | |
106 | +static int wand_rfkill_wifi_probe(struct device *dev, | |
107 | + struct device_node *np, | |
108 | + struct wand_rfkill_data *rfkill) | |
109 | +{ | |
110 | + int ret; | |
111 | + int wl_ref_on, wl_rst_n, wl_reg_on, wl_wake, wl_host_wake; | |
112 | + | |
113 | + wl_ref_on = of_get_named_gpio(np, "wifi-ref-on", 0); | |
114 | + wl_rst_n = of_get_named_gpio(np, "wifi-rst-n", 0); | |
115 | + wl_reg_on = of_get_named_gpio(np, "wifi-reg-on", 0); | |
116 | + wl_wake = of_get_named_gpio(np, "wifi-wake", 0); | |
117 | + wl_host_wake = of_get_named_gpio(np, "wifi-host-wake", 0); | |
118 | + | |
119 | + if (!gpio_is_valid(wl_rst_n) || !gpio_is_valid(wl_ref_on) || | |
120 | + !gpio_is_valid(wl_reg_on) || !gpio_is_valid(wl_wake) || | |
121 | + !gpio_is_valid(wl_host_wake)) { | |
122 | + | |
123 | + dev_err(dev, "incorrect wifi gpios (%d %d %d %d %d)\n", | |
124 | + wl_rst_n, wl_ref_on, wl_reg_on, wl_wake, wl_host_wake); | |
125 | + return -EINVAL; | |
126 | + } | |
127 | + | |
128 | + dev_info(dev, "initialize wifi chip\n"); | |
129 | + | |
130 | + gpio_request(wl_rst_n, "wl_rst_n"); | |
131 | + gpio_direction_output(wl_rst_n, 0); | |
132 | + msleep(11); | |
133 | + gpio_set_value(wl_rst_n, 1); | |
134 | + | |
135 | + gpio_request(wl_ref_on, "wl_ref_on"); | |
136 | + gpio_direction_output(wl_ref_on, 1); | |
137 | + | |
138 | + gpio_request(wl_reg_on, "wl_reg_on"); | |
139 | + gpio_direction_output(wl_reg_on, 1); | |
140 | + | |
141 | + gpio_request(wl_wake, "wl_wake"); | |
142 | + gpio_direction_output(wl_wake, 1); | |
143 | + | |
144 | + gpio_request(wl_host_wake, "wl_host_wake"); | |
145 | + gpio_direction_input(wl_host_wake); | |
146 | + | |
147 | + rfkill->shutdown_name = "wifi_shutdown"; | |
148 | + rfkill->shutdown_gpio = wl_wake; | |
149 | + | |
150 | + rfkill->rfkill_dev = rfkill_alloc("wifi-rfkill", dev, RFKILL_TYPE_WLAN, | |
151 | + &wand_rfkill_ops, rfkill); | |
152 | + if (!rfkill->rfkill_dev) { | |
153 | + ret = -ENOMEM; | |
154 | + goto wifi_fail_free_gpio; | |
155 | + } | |
156 | + | |
157 | + ret = rfkill_register(rfkill->rfkill_dev); | |
158 | + if (ret < 0) | |
159 | + goto wifi_fail_unregister; | |
160 | + | |
161 | + dev_info(dev, "wifi-rfkill registered.\n"); | |
162 | + | |
163 | + return 0; | |
164 | + | |
165 | +wifi_fail_unregister: | |
166 | + rfkill_destroy(rfkill->rfkill_dev); | |
167 | +wifi_fail_free_gpio: | |
168 | + if (gpio_is_valid(wl_rst_n)) gpio_free(wl_rst_n); | |
169 | + if (gpio_is_valid(wl_ref_on)) gpio_free(wl_ref_on); | |
170 | + if (gpio_is_valid(wl_reg_on)) gpio_free(wl_reg_on); | |
171 | + if (gpio_is_valid(wl_wake)) gpio_free(wl_wake); | |
172 | + if (gpio_is_valid(wl_host_wake)) gpio_free(wl_host_wake); | |
173 | + | |
174 | + return ret; | |
175 | +} | |
176 | + | |
177 | +static int wand_rfkill_bt_probe(struct device *dev, | |
178 | + struct device_node *np, | |
179 | + struct wand_rfkill_data *rfkill) | |
180 | +{ | |
181 | + int ret; | |
182 | + int bt_on, bt_wake, bt_host_wake; | |
183 | + | |
184 | + bt_on = of_get_named_gpio(np, "bluetooth-on", 0); | |
185 | + bt_wake = of_get_named_gpio(np, "bluetooth-wake", 0); | |
186 | + bt_host_wake = of_get_named_gpio(np, "bluetooth-host-wake", 0); | |
187 | + | |
188 | + if (!gpio_is_valid(bt_on) || !gpio_is_valid(bt_wake) || | |
189 | + !gpio_is_valid(bt_host_wake)) { | |
190 | + | |
191 | + dev_err(dev, "incorrect bt gpios (%d %d %d)\n", | |
192 | + bt_on, bt_wake, bt_host_wake); | |
193 | + return -EINVAL; | |
194 | + } | |
195 | + | |
196 | + dev_info(dev, "initialize bluetooth chip\n"); | |
197 | + | |
198 | + gpio_request(bt_on, "bt_on"); | |
199 | + gpio_direction_output(bt_on, 0); | |
200 | + msleep(11); | |
201 | + gpio_set_value(bt_on, 1); | |
202 | + | |
203 | + gpio_request(bt_wake, "bt_wake"); | |
204 | + gpio_direction_output(bt_wake, 1); | |
205 | + | |
206 | + gpio_request(bt_host_wake, "bt_host_wake"); | |
207 | + gpio_direction_input(bt_host_wake); | |
208 | + | |
209 | + rfkill->shutdown_name = "bluetooth_shutdown"; | |
210 | + rfkill->shutdown_gpio = bt_wake; | |
211 | + | |
212 | + rfkill->rfkill_dev = rfkill_alloc("bluetooth-rfkill", dev, RFKILL_TYPE_BLUETOOTH, | |
213 | + &wand_rfkill_ops, rfkill); | |
214 | + if (!rfkill->rfkill_dev) { | |
215 | + ret = -ENOMEM; | |
216 | + goto bt_fail_free_gpio; | |
217 | + } | |
218 | + | |
219 | + ret = rfkill_register(rfkill->rfkill_dev); | |
220 | + if (ret < 0) | |
221 | + goto bt_fail_unregister; | |
222 | + | |
223 | + dev_info(dev, "bluetooth-rfkill registered.\n"); | |
224 | + | |
225 | + return 0; | |
226 | + | |
227 | +bt_fail_unregister: | |
228 | + rfkill_destroy(rfkill->rfkill_dev); | |
229 | +bt_fail_free_gpio: | |
230 | + if (gpio_is_valid(bt_on)) gpio_free(bt_on); | |
231 | + if (gpio_is_valid(bt_wake)) gpio_free(bt_wake); | |
232 | + if (gpio_is_valid(bt_host_wake)) gpio_free(bt_host_wake); | |
233 | + | |
234 | + return ret; | |
235 | +} | |
236 | + | |
237 | +static int wand_rfkill_probe(struct platform_device *pdev) | |
238 | +{ | |
239 | + struct wand_rfkill_data *rfkill; | |
240 | + struct pinctrl *pinctrl; | |
241 | + int ret; | |
242 | + | |
243 | + dev_info(&pdev->dev, "Wandboard rfkill initialization\n"); | |
244 | + | |
245 | + if (!pdev->dev.of_node) { | |
246 | + dev_err(&pdev->dev, "no device tree node\n"); | |
247 | + return -ENODEV; | |
248 | + } | |
249 | + | |
250 | + rfkill = kzalloc(sizeof(*rfkill) * 2, GFP_KERNEL); | |
251 | + if (!rfkill) | |
252 | + return -ENOMEM; | |
253 | + | |
254 | + pinctrl = devm_pinctrl_get_select_default(&pdev->dev); | |
255 | + if (IS_ERR(pinctrl)) { | |
256 | + int ret = PTR_ERR(pinctrl); | |
257 | + dev_err(&pdev->dev, "failed to get default pinctrl: %d\n", ret); | |
258 | + return ret; | |
259 | + } | |
260 | + | |
261 | + /* setup WiFi */ | |
262 | + ret = wand_rfkill_wifi_probe(&pdev->dev, pdev->dev.of_node, &rfkill[0]); | |
263 | + if (ret < 0) | |
264 | + goto fail_free_rfkill; | |
265 | + | |
266 | + /* setup bluetooth */ | |
267 | + ret = wand_rfkill_bt_probe(&pdev->dev, pdev->dev.of_node, &rfkill[1]); | |
268 | + if (ret < 0) | |
269 | + goto fail_unregister_wifi; | |
270 | + | |
271 | + platform_set_drvdata(pdev, rfkill); | |
272 | + | |
273 | + return 0; | |
274 | + | |
275 | +fail_unregister_wifi: | |
276 | + if (rfkill[1].rfkill_dev) { | |
277 | + rfkill_unregister(rfkill[1].rfkill_dev); | |
278 | + rfkill_destroy(rfkill[1].rfkill_dev); | |
279 | + } | |
280 | + | |
281 | + /* TODO free gpio */ | |
282 | + | |
283 | +fail_free_rfkill: | |
284 | + kfree(rfkill); | |
285 | + | |
286 | + return ret; | |
287 | +} | |
288 | + | |
289 | +static int wand_rfkill_remove(struct platform_device *pdev) | |
290 | +{ | |
291 | + struct wand_rfkill_data *rfkill = platform_get_drvdata(pdev); | |
292 | + | |
293 | + dev_info(&pdev->dev, "Module unloading\n"); | |
294 | + | |
295 | + if (!rfkill) | |
296 | + return 0; | |
297 | + | |
298 | + /* WiFi */ | |
299 | + if (gpio_is_valid(rfkill[0].shutdown_gpio)) | |
300 | + gpio_free(rfkill[0].shutdown_gpio); | |
301 | + | |
302 | + rfkill_unregister(rfkill[0].rfkill_dev); | |
303 | + rfkill_destroy(rfkill[0].rfkill_dev); | |
304 | + | |
305 | + /* Bt */ | |
306 | + if (gpio_is_valid(rfkill[1].shutdown_gpio)) | |
307 | + gpio_free(rfkill[1].shutdown_gpio); | |
308 | + | |
309 | + rfkill_unregister(rfkill[1].rfkill_dev); | |
310 | + rfkill_destroy(rfkill[1].rfkill_dev); | |
311 | + | |
312 | + kfree(rfkill); | |
313 | + | |
314 | + return 0; | |
315 | +} | |
316 | + | |
317 | +static struct of_device_id wand_rfkill_match[] = { | |
318 | + { .compatible = "wand,imx6q-wandboard-rfkill", }, | |
319 | + { .compatible = "wand,imx6dl-wandboard-rfkill", }, | |
320 | + { .compatible = "wand,imx6qdl-wandboard-rfkill", }, | |
321 | + {} | |
322 | +}; | |
323 | + | |
324 | +static struct platform_driver wand_rfkill_driver = { | |
325 | + .driver = { | |
326 | + .name = "wandboard-rfkill", | |
327 | + .owner = THIS_MODULE, | |
328 | + .of_match_table = of_match_ptr(wand_rfkill_match), | |
329 | + }, | |
330 | + .probe = wand_rfkill_probe, | |
331 | + .remove = wand_rfkill_remove | |
332 | +}; | |
333 | + | |
334 | +module_platform_driver(wand_rfkill_driver); | |
335 | + | |
336 | +MODULE_AUTHOR("Vladimir Ermakov <vooon341@gmail.com>"); | |
337 | +MODULE_DESCRIPTION("Wandboard rfkill driver"); | |
338 | +MODULE_LICENSE("GPL v2"); | |
339 | -- | |
340 | 1.8.4.rc3 | |
341 |