rfkill: Regulator consumer driver for rfkill
Add a regulator consumer driver for rfkill to enable controlling radio transmitters connected to voltage regulators using the regulator framework. A new "vrfkill" virtual supply is provided to use in platform code. Signed-off-by: Guiming Zhuo <gmzhuo@gmail.com> Signed-off-by: Antonio Ospite <ospite@studenti.unina.it> Reviewed-by: Johannes Berg <johannes@sipsolutions.net> Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
Родитель
bb411b4db2
Коммит
cbc6a6ed09
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* rfkill-regulator.c - Regulator consumer driver for rfkill
|
||||
*
|
||||
* Copyright (C) 2009 Guiming Zhuo <gmzhuo@gmail.com>
|
||||
* Copyright (C) 2011 Antonio Ospite <ospite@studenti.unina.it>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_RFKILL_REGULATOR_H
|
||||
#define __LINUX_RFKILL_REGULATOR_H
|
||||
|
||||
/*
|
||||
* Use "vrfkill" as supply id when declaring the regulator consumer:
|
||||
*
|
||||
* static struct regulator_consumer_supply pcap_regulator_V6_consumers [] = {
|
||||
* { .dev_name = "rfkill-regulator.0", .supply = "vrfkill" },
|
||||
* };
|
||||
*
|
||||
* If you have several regulator driven rfkill, you can append a numerical id to
|
||||
* .dev_name as done above, and use the same id when declaring the platform
|
||||
* device:
|
||||
*
|
||||
* static struct rfkill_regulator_platform_data ezx_rfkill_bt_data = {
|
||||
* .name = "ezx-bluetooth",
|
||||
* .type = RFKILL_TYPE_BLUETOOTH,
|
||||
* };
|
||||
*
|
||||
* static struct platform_device a910_rfkill = {
|
||||
* .name = "rfkill-regulator",
|
||||
* .id = 0,
|
||||
* .dev = {
|
||||
* .platform_data = &ezx_rfkill_bt_data,
|
||||
* },
|
||||
* };
|
||||
*/
|
||||
|
||||
#include <linux/rfkill.h>
|
||||
|
||||
struct rfkill_regulator_platform_data {
|
||||
char *name; /* the name for the rfkill switch */
|
||||
enum rfkill_type type; /* the type as specified in rfkill.h */
|
||||
};
|
||||
|
||||
#endif /* __LINUX_RFKILL_REGULATOR_H */
|
|
@ -22,3 +22,14 @@ config RFKILL_INPUT
|
|||
depends on RFKILL
|
||||
depends on INPUT = y || RFKILL = INPUT
|
||||
default y if !EXPERT
|
||||
|
||||
config RFKILL_REGULATOR
|
||||
tristate "Generic rfkill regulator driver"
|
||||
depends on RFKILL || !RFKILL
|
||||
depends on REGULATOR
|
||||
help
|
||||
This options enable controlling radio transmitters connected to
|
||||
voltage regulator using the regulator framework.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will
|
||||
be called rfkill-regulator.
|
||||
|
|
|
@ -5,3 +5,4 @@
|
|||
rfkill-y += core.o
|
||||
rfkill-$(CONFIG_RFKILL_INPUT) += input.o
|
||||
obj-$(CONFIG_RFKILL) += rfkill.o
|
||||
obj-$(CONFIG_RFKILL_REGULATOR) += rfkill-regulator.o
|
||||
|
|
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
* rfkill-regulator.c - Regulator consumer driver for rfkill
|
||||
*
|
||||
* Copyright (C) 2009 Guiming Zhuo <gmzhuo@gmail.com>
|
||||
* Copyright (C) 2011 Antonio Ospite <ospite@studenti.unina.it>
|
||||
*
|
||||
* Implementation inspired by leds-regulator driver.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/rfkill.h>
|
||||
#include <linux/rfkill-regulator.h>
|
||||
|
||||
struct rfkill_regulator_data {
|
||||
struct rfkill *rf_kill;
|
||||
bool reg_enabled;
|
||||
|
||||
struct regulator *vcc;
|
||||
};
|
||||
|
||||
static int rfkill_regulator_set_block(void *data, bool blocked)
|
||||
{
|
||||
struct rfkill_regulator_data *rfkill_data = data;
|
||||
|
||||
pr_debug("%s: blocked: %d\n", __func__, blocked);
|
||||
|
||||
if (blocked) {
|
||||
if (rfkill_data->reg_enabled) {
|
||||
regulator_disable(rfkill_data->vcc);
|
||||
rfkill_data->reg_enabled = 0;
|
||||
}
|
||||
} else {
|
||||
if (!rfkill_data->reg_enabled) {
|
||||
regulator_enable(rfkill_data->vcc);
|
||||
rfkill_data->reg_enabled = 1;
|
||||
}
|
||||
}
|
||||
|
||||
pr_debug("%s: regulator_is_enabled after set_block: %d\n", __func__,
|
||||
regulator_is_enabled(rfkill_data->vcc));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct rfkill_ops rfkill_regulator_ops = {
|
||||
.set_block = rfkill_regulator_set_block,
|
||||
};
|
||||
|
||||
static int __devinit rfkill_regulator_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct rfkill_regulator_platform_data *pdata = pdev->dev.platform_data;
|
||||
struct rfkill_regulator_data *rfkill_data;
|
||||
struct regulator *vcc;
|
||||
struct rfkill *rf_kill;
|
||||
int ret = 0;
|
||||
|
||||
if (pdata == NULL) {
|
||||
dev_err(&pdev->dev, "no platform data\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (pdata->name == NULL || pdata->type == 0) {
|
||||
dev_err(&pdev->dev, "invalid name or type in platform data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
vcc = regulator_get_exclusive(&pdev->dev, "vrfkill");
|
||||
if (IS_ERR(vcc)) {
|
||||
dev_err(&pdev->dev, "Cannot get vcc for %s\n", pdata->name);
|
||||
ret = PTR_ERR(vcc);
|
||||
goto out;
|
||||
}
|
||||
|
||||
rfkill_data = kzalloc(sizeof(*rfkill_data), GFP_KERNEL);
|
||||
if (rfkill_data == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto err_data_alloc;
|
||||
}
|
||||
|
||||
rf_kill = rfkill_alloc(pdata->name, &pdev->dev,
|
||||
pdata->type,
|
||||
&rfkill_regulator_ops, rfkill_data);
|
||||
if (rf_kill == NULL) {
|
||||
dev_err(&pdev->dev, "Cannot alloc rfkill device\n");
|
||||
ret = -ENOMEM;
|
||||
goto err_rfkill_alloc;
|
||||
}
|
||||
|
||||
if (regulator_is_enabled(vcc)) {
|
||||
dev_dbg(&pdev->dev, "Regulator already enabled\n");
|
||||
rfkill_data->reg_enabled = 1;
|
||||
}
|
||||
rfkill_data->vcc = vcc;
|
||||
rfkill_data->rf_kill = rf_kill;
|
||||
|
||||
ret = rfkill_register(rf_kill);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Cannot register rfkill device\n");
|
||||
goto err_rfkill_register;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, rfkill_data);
|
||||
dev_info(&pdev->dev, "%s initialized\n", pdata->name);
|
||||
|
||||
return 0;
|
||||
|
||||
err_rfkill_register:
|
||||
rfkill_destroy(rf_kill);
|
||||
err_rfkill_alloc:
|
||||
kfree(rfkill_data);
|
||||
err_data_alloc:
|
||||
regulator_put(vcc);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit rfkill_regulator_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct rfkill_regulator_data *rfkill_data = platform_get_drvdata(pdev);
|
||||
struct rfkill *rf_kill = rfkill_data->rf_kill;
|
||||
|
||||
rfkill_unregister(rf_kill);
|
||||
rfkill_destroy(rf_kill);
|
||||
regulator_put(rfkill_data->vcc);
|
||||
kfree(rfkill_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver rfkill_regulator_driver = {
|
||||
.probe = rfkill_regulator_probe,
|
||||
.remove = __devexit_p(rfkill_regulator_remove),
|
||||
.driver = {
|
||||
.name = "rfkill-regulator",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init rfkill_regulator_init(void)
|
||||
{
|
||||
return platform_driver_register(&rfkill_regulator_driver);
|
||||
}
|
||||
module_init(rfkill_regulator_init);
|
||||
|
||||
static void __exit rfkill_regulator_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&rfkill_regulator_driver);
|
||||
}
|
||||
module_exit(rfkill_regulator_exit);
|
||||
|
||||
MODULE_AUTHOR("Guiming Zhuo <gmzhuo@gmail.com>");
|
||||
MODULE_AUTHOR("Antonio Ospite <ospite@studenti.unina.it>");
|
||||
MODULE_DESCRIPTION("Regulator consumer driver for rfkill");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:rfkill-regulator");
|
Загрузка…
Ссылка в новой задаче