]> git.ipfire.org Git - people/pmueller/ipfire-2.x.git/blob - src/patches/kernel/wandboard/imx/0004-ARM-i.MX6-Wandboard-add-wifi-bt-rfkill-driver.patch
OpenVPN: Added 'valid til (days)' field for N2N.
[people/pmueller/ipfire-2.x.git] / src / patches / kernel / wandboard / imx / 0004-ARM-i.MX6-Wandboard-add-wifi-bt-rfkill-driver.patch
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