From cbc6a6ed0900aed789b5ca77192845f2f987af70 Mon Sep 17 00:00:00 2001 From: Antonio Ospite Date: Wed, 13 Apr 2011 21:40:45 +0200 Subject: [PATCH] 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 Signed-off-by: Antonio Ospite Reviewed-by: Johannes Berg Signed-off-by: John W. Linville --- include/linux/rfkill-regulator.h | 48 +++++++++ net/rfkill/Kconfig | 11 +++ net/rfkill/Makefile | 1 + net/rfkill/rfkill-regulator.c | 164 +++++++++++++++++++++++++++++++ 4 files changed, 224 insertions(+) create mode 100644 include/linux/rfkill-regulator.h create mode 100644 net/rfkill/rfkill-regulator.c diff --git a/include/linux/rfkill-regulator.h b/include/linux/rfkill-regulator.h new file mode 100644 index 000000000000..aca36bc83315 --- /dev/null +++ b/include/linux/rfkill-regulator.h @@ -0,0 +1,48 @@ +/* + * rfkill-regulator.c - Regulator consumer driver for rfkill + * + * Copyright (C) 2009 Guiming Zhuo + * Copyright (C) 2011 Antonio Ospite + * + * 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 + +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 */ diff --git a/net/rfkill/Kconfig b/net/rfkill/Kconfig index 7fce6dfd2180..48464ca13b24 100644 --- a/net/rfkill/Kconfig +++ b/net/rfkill/Kconfig @@ -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. diff --git a/net/rfkill/Makefile b/net/rfkill/Makefile index 662105352691..d9a5a58ffd8c 100644 --- a/net/rfkill/Makefile +++ b/net/rfkill/Makefile @@ -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 diff --git a/net/rfkill/rfkill-regulator.c b/net/rfkill/rfkill-regulator.c new file mode 100644 index 000000000000..18dc512a10f3 --- /dev/null +++ b/net/rfkill/rfkill-regulator.c @@ -0,0 +1,164 @@ +/* + * rfkill-regulator.c - Regulator consumer driver for rfkill + * + * Copyright (C) 2009 Guiming Zhuo + * Copyright (C) 2011 Antonio Ospite + * + * 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 +#include +#include +#include +#include +#include +#include + +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 "); +MODULE_AUTHOR("Antonio Ospite "); +MODULE_DESCRIPTION("Regulator consumer driver for rfkill"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:rfkill-regulator"); -- 2.20.1