Skip to content

Commit 7176ba2

Browse files
rhkleinlinvjw
authored andcommitted
net: rfkill: add generic gpio rfkill driver
This adds a new generic gpio rfkill driver to support rfkill switches which are controlled by gpios. The driver also supports passing in data about the clock for the radio, so that when rfkill is blocking, it can disable the clock. This driver assumes platform data is passed from the board files to configure it for specific devices. Original-patch-by: Anantha Idapalapati <[email protected]> Signed-off-by: Rhyland Klein <[email protected]> Signed-off-by: John W. Linville <[email protected]>
1 parent c6820f1 commit 7176ba2

File tree

4 files changed

+280
-0
lines changed

4 files changed

+280
-0
lines changed

include/linux/rfkill-gpio.h

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Copyright (c) 2011, NVIDIA Corporation.
3+
*
4+
* This program is free software; you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation; either version 2 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12+
* more details.
13+
*
14+
* You should have received a copy of the GNU General Public License along
15+
* with this program; if not, write to the Free Software Foundation, Inc.,
16+
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17+
*/
18+
19+
20+
#ifndef __RFKILL_GPIO_H
21+
#define __RFKILL_GPIO_H
22+
23+
#include <linux/types.h>
24+
#include <linux/rfkill.h>
25+
26+
/**
27+
* struct rfkill_gpio_platform_data - platform data for rfkill gpio device.
28+
* for unused gpio's, the expected value is -1.
29+
* @name: name for the gpio rf kill instance
30+
* @reset_gpio: GPIO which is used for reseting rfkill switch
31+
* @shutdown_gpio: GPIO which is used for shutdown of rfkill switch
32+
* @power_clk_name: [optional] name of clk to turn off while blocked
33+
*/
34+
35+
struct rfkill_gpio_platform_data {
36+
char *name;
37+
int reset_gpio;
38+
int shutdown_gpio;
39+
const char *power_clk_name;
40+
enum rfkill_type type;
41+
};
42+
43+
#endif /* __RFKILL_GPIO_H */

net/rfkill/Kconfig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,12 @@ config RFKILL_REGULATOR
3333

3434
To compile this driver as a module, choose M here: the module will
3535
be called rfkill-regulator.
36+
37+
config RFKILL_GPIO
38+
tristate "GPIO RFKILL driver"
39+
depends on RFKILL && GPIOLIB && HAVE_CLK
40+
default n
41+
help
42+
If you say yes here you get support of a generic gpio RFKILL
43+
driver. The platform should fill in the appropriate fields in the
44+
rfkill_gpio_platform_data structure and pass that to the driver.

net/rfkill/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ rfkill-y += core.o
66
rfkill-$(CONFIG_RFKILL_INPUT) += input.o
77
obj-$(CONFIG_RFKILL) += rfkill.o
88
obj-$(CONFIG_RFKILL_REGULATOR) += rfkill-regulator.o
9+
obj-$(CONFIG_RFKILL_GPIO) += rfkill-gpio.o

net/rfkill/rfkill-gpio.c

Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
/*
2+
* Copyright (c) 2011, NVIDIA Corporation.
3+
*
4+
* This program is free software; you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation; either version 2 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12+
* more details.
13+
*
14+
* You should have received a copy of the GNU General Public License along
15+
* with this program; if not, write to the Free Software Foundation, Inc.,
16+
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17+
*/
18+
19+
#include <linux/gpio.h>
20+
#include <linux/init.h>
21+
#include <linux/kernel.h>
22+
#include <linux/module.h>
23+
#include <linux/rfkill.h>
24+
#include <linux/platform_device.h>
25+
#include <linux/clk.h>
26+
#include <linux/slab.h>
27+
28+
#include <linux/rfkill-gpio.h>
29+
30+
enum rfkill_gpio_clk_state {
31+
UNSPECIFIED = 0,
32+
PWR_ENABLED,
33+
PWR_DISABLED
34+
};
35+
36+
#define PWR_CLK_SET(_RF, _EN) \
37+
((_RF)->pwr_clk_enabled = (!(_EN) ? PWR_ENABLED : PWR_DISABLED))
38+
#define PWR_CLK_ENABLED(_RF) ((_RF)->pwr_clk_enabled == PWR_ENABLED)
39+
#define PWR_CLK_DISABLED(_RF) ((_RF)->pwr_clk_enabled != PWR_ENABLED)
40+
41+
struct rfkill_gpio_data {
42+
struct rfkill_gpio_platform_data *pdata;
43+
struct rfkill *rfkill_dev;
44+
char *reset_name;
45+
char *shutdown_name;
46+
enum rfkill_gpio_clk_state pwr_clk_enabled;
47+
struct clk *pwr_clk;
48+
};
49+
50+
static int rfkill_gpio_set_power(void *data, bool blocked)
51+
{
52+
struct rfkill_gpio_data *rfkill = data;
53+
54+
if (blocked) {
55+
if (gpio_is_valid(rfkill->pdata->shutdown_gpio))
56+
gpio_direction_output(rfkill->pdata->shutdown_gpio, 0);
57+
if (gpio_is_valid(rfkill->pdata->reset_gpio))
58+
gpio_direction_output(rfkill->pdata->reset_gpio, 0);
59+
if (rfkill->pwr_clk && PWR_CLK_ENABLED(rfkill))
60+
clk_disable(rfkill->pwr_clk);
61+
} else {
62+
if (rfkill->pwr_clk && PWR_CLK_DISABLED(rfkill))
63+
clk_enable(rfkill->pwr_clk);
64+
if (gpio_is_valid(rfkill->pdata->reset_gpio))
65+
gpio_direction_output(rfkill->pdata->reset_gpio, 1);
66+
if (gpio_is_valid(rfkill->pdata->shutdown_gpio))
67+
gpio_direction_output(rfkill->pdata->shutdown_gpio, 1);
68+
}
69+
70+
if (rfkill->pwr_clk)
71+
PWR_CLK_SET(rfkill, blocked);
72+
73+
return 0;
74+
}
75+
76+
static const struct rfkill_ops rfkill_gpio_ops = {
77+
.set_block = rfkill_gpio_set_power,
78+
};
79+
80+
static int rfkill_gpio_probe(struct platform_device *pdev)
81+
{
82+
struct rfkill_gpio_data *rfkill;
83+
struct rfkill_gpio_platform_data *pdata = pdev->dev.platform_data;
84+
int ret = 0;
85+
int len = 0;
86+
87+
if (!pdata) {
88+
pr_warn("%s: No platform data specified\n", __func__);
89+
return -EINVAL;
90+
}
91+
92+
/* make sure at-least one of the GPIO is defined and that
93+
* a name is specified for this instance */
94+
if (!pdata->name || (!gpio_is_valid(pdata->reset_gpio) &&
95+
!gpio_is_valid(pdata->shutdown_gpio))) {
96+
pr_warn("%s: invalid platform data\n", __func__);
97+
return -EINVAL;
98+
}
99+
100+
rfkill = kzalloc(sizeof(*rfkill), GFP_KERNEL);
101+
if (!rfkill)
102+
return -ENOMEM;
103+
104+
rfkill->pdata = pdata;
105+
106+
len = strlen(pdata->name);
107+
rfkill->reset_name = kzalloc(len + 7, GFP_KERNEL);
108+
if (!rfkill->reset_name) {
109+
ret = -ENOMEM;
110+
goto fail_alloc;
111+
}
112+
113+
rfkill->shutdown_name = kzalloc(len + 10, GFP_KERNEL);
114+
if (!rfkill->shutdown_name) {
115+
ret = -ENOMEM;
116+
goto fail_reset_name;
117+
}
118+
119+
snprintf(rfkill->reset_name, len + 6 , "%s_reset", pdata->name);
120+
snprintf(rfkill->shutdown_name, len + 9, "%s_shutdown", pdata->name);
121+
122+
if (pdata->power_clk_name) {
123+
rfkill->pwr_clk = clk_get(&pdev->dev, pdata->power_clk_name);
124+
if (IS_ERR(rfkill->pwr_clk)) {
125+
pr_warn("%s: can't find pwr_clk.\n", __func__);
126+
goto fail_shutdown_name;
127+
}
128+
}
129+
130+
if (gpio_is_valid(pdata->reset_gpio)) {
131+
ret = gpio_request(pdata->reset_gpio, rfkill->reset_name);
132+
if (ret) {
133+
pr_warn("%s: failed to get reset gpio.\n", __func__);
134+
goto fail_clock;
135+
}
136+
}
137+
138+
if (gpio_is_valid(pdata->shutdown_gpio)) {
139+
ret = gpio_request(pdata->shutdown_gpio, rfkill->shutdown_name);
140+
if (ret) {
141+
pr_warn("%s: failed to get shutdown gpio.\n", __func__);
142+
goto fail_reset;
143+
}
144+
}
145+
146+
rfkill->rfkill_dev = rfkill_alloc(pdata->name, &pdev->dev, pdata->type,
147+
&rfkill_gpio_ops, rfkill);
148+
if (!rfkill->rfkill_dev)
149+
goto fail_shutdown;
150+
151+
ret = rfkill_register(rfkill->rfkill_dev);
152+
if (ret < 0)
153+
goto fail_rfkill;
154+
155+
platform_set_drvdata(pdev, rfkill);
156+
157+
dev_info(&pdev->dev, "%s device registered.\n", pdata->name);
158+
159+
return 0;
160+
161+
fail_rfkill:
162+
rfkill_destroy(rfkill->rfkill_dev);
163+
fail_shutdown:
164+
if (gpio_is_valid(pdata->shutdown_gpio))
165+
gpio_free(pdata->shutdown_gpio);
166+
fail_reset:
167+
if (gpio_is_valid(pdata->reset_gpio))
168+
gpio_free(pdata->reset_gpio);
169+
fail_clock:
170+
if (rfkill->pwr_clk)
171+
clk_put(rfkill->pwr_clk);
172+
fail_shutdown_name:
173+
kfree(rfkill->shutdown_name);
174+
fail_reset_name:
175+
kfree(rfkill->reset_name);
176+
fail_alloc:
177+
kfree(rfkill);
178+
179+
return ret;
180+
}
181+
182+
static int rfkill_gpio_remove(struct platform_device *pdev)
183+
{
184+
struct rfkill_gpio_data *rfkill = platform_get_drvdata(pdev);
185+
186+
rfkill_unregister(rfkill->rfkill_dev);
187+
rfkill_destroy(rfkill->rfkill_dev);
188+
if (gpio_is_valid(rfkill->pdata->shutdown_gpio))
189+
gpio_free(rfkill->pdata->shutdown_gpio);
190+
if (gpio_is_valid(rfkill->pdata->reset_gpio))
191+
gpio_free(rfkill->pdata->reset_gpio);
192+
if (rfkill->pwr_clk && PWR_CLK_ENABLED(rfkill))
193+
clk_disable(rfkill->pwr_clk);
194+
if (rfkill->pwr_clk)
195+
clk_put(rfkill->pwr_clk);
196+
kfree(rfkill->shutdown_name);
197+
kfree(rfkill->reset_name);
198+
kfree(rfkill);
199+
200+
return 0;
201+
}
202+
203+
static struct platform_driver rfkill_gpio_driver = {
204+
.probe = rfkill_gpio_probe,
205+
.remove = __devexit_p(rfkill_gpio_remove),
206+
.driver = {
207+
.name = "rfkill_gpio",
208+
.owner = THIS_MODULE,
209+
},
210+
};
211+
212+
static int __init rfkill_gpio_init(void)
213+
{
214+
return platform_driver_register(&rfkill_gpio_driver);
215+
}
216+
217+
static void __exit rfkill_gpio_exit(void)
218+
{
219+
platform_driver_unregister(&rfkill_gpio_driver);
220+
}
221+
222+
module_init(rfkill_gpio_init);
223+
module_exit(rfkill_gpio_exit);
224+
225+
MODULE_DESCRIPTION("gpio rfkill");
226+
MODULE_AUTHOR("NVIDIA");
227+
MODULE_LICENSE("GPL");

0 commit comments

Comments
 (0)