diff options
author | Rakesh Kumar <krakesh@nvidia.com> | 2011-02-11 00:31:25 -0800 |
---|---|---|
committer | Dan Willemsen <dwillemsen@nvidia.com> | 2011-11-30 21:43:03 -0800 |
commit | f760ebbe3917a9a84ce27dee7e7ba6c6e637191f (patch) | |
tree | 7f1a765fbcabeccaa3336d7d6f97c65c3d5297d2 /drivers/misc | |
parent | 0e5eec3307ce47cb03f9405b1c227b400611b24b (diff) |
[arm/tegra/bt/rfkill]: bluetooth rfkill driver
A new "rfkill" driver added to control the BT radio.
Bug 724106
Original-Change-Id: I60ce0d188884d17f4f61d378b43368b7b5f9d7a9
(cherry picked from commit 78dbd0a022280964d3e9dcd3f00753ce8d66fa16)
Reviewed-on: http://git-master/r/9955
Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com>
Tested-by: Bharat Nihalani <bnihalani@nvidia.com>
Rebase-Id: Rac61d26ff1845a0a835c8f93c2bc2f6cefd04ac3
Diffstat (limited to 'drivers/misc')
-rw-r--r-- | drivers/misc/Kconfig | 6 | ||||
-rw-r--r-- | drivers/misc/Makefile | 1 | ||||
-rw-r--r-- | drivers/misc/bcm4329_rfkill.c | 186 |
3 files changed, 193 insertions, 0 deletions
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index eedf35a22f5f..40b75d076ae1 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -508,6 +508,12 @@ config USB_SWITCH_FSA9480 stereo and mono audio, video, microphone and UART data to use a common connector port. +config BCM4329_RFKILL + bool "Enable BCM4329 RFKILL driver" + default n + ---help--- + Adds BCM4329 RFKILL driver for Broadcom BCM4329 chipset + source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 00a7b2e015d4..fe2e911a0621 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -48,3 +48,4 @@ obj-y += lis3lv02d/ obj-y += carma/ obj-$(CONFIG_USB_SWITCH_FSA9480) += fsa9480.o obj-$(CONFIG_SENSORS_NCT1008) += nct1008.o +obj-$(CONFIG_BCM4329_RFKILL) += bcm4329_rfkill.o diff --git a/drivers/misc/bcm4329_rfkill.c b/drivers/misc/bcm4329_rfkill.c new file mode 100644 index 000000000000..eeb4047631f3 --- /dev/null +++ b/drivers/misc/bcm4329_rfkill.c @@ -0,0 +1,186 @@ +/* + * drivers/misc/bcm4329_rfkill.c + * + * Copyright (c) 2010, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <linux/err.h> +#include <linux/types.h> +#include <linux/uaccess.h> +#include <linux/fs.h> +#include <linux/gpio.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/miscdevice.h> +#include <linux/module.h> +#include <linux/rfkill.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/slab.h> + +struct bcm4329_rfkill_data { + int gpio_reset; + int gpio_shutdown; + int delay; + struct clk *bt_32k_clk; +}; + +static struct bcm4329_rfkill_data *bcm4329_rfkill; + +static int bcm4329_bt_rfkill_set_power(void *data, bool blocked) +{ + if (blocked) { + gpio_direction_output(bcm4329_rfkill->gpio_shutdown, 0); + gpio_direction_output(bcm4329_rfkill->gpio_reset, 0); + if (bcm4329_rfkill->bt_32k_clk) + clk_disable(bcm4329_rfkill->bt_32k_clk); + } else { + if (bcm4329_rfkill->bt_32k_clk) + clk_enable(bcm4329_rfkill->bt_32k_clk); + gpio_direction_output(bcm4329_rfkill->gpio_shutdown, 1); + gpio_direction_output(bcm4329_rfkill->gpio_reset, 1); + } + + return 0; +} + +static const struct rfkill_ops bcm4329_bt_rfkill_ops = { + .set_block = bcm4329_bt_rfkill_set_power, +}; + +static int bcm4329_rfkill_probe(struct platform_device *pdev) +{ + struct rfkill *bt_rfkill; + struct resource *res; + int ret; + bool enable = false; /* off */ + bool default_sw_block_state; + + bcm4329_rfkill = kzalloc(sizeof(*bcm4329_rfkill), GFP_KERNEL); + if (!bcm4329_rfkill) + return -ENOMEM; + + bcm4329_rfkill->bt_32k_clk = clk_get(&pdev->dev, "bcm4329_32k_clk"); + if (IS_ERR(bcm4329_rfkill->bt_32k_clk)) { + pr_warn("can't find bcm4329_32k_clk. assuming clock to chip\n"); + bcm4329_rfkill->bt_32k_clk = NULL; + } + + res = platform_get_resource_byname(pdev, IORESOURCE_IO, + "bcm4329_nreset_gpio"); + if (!res) { + pr_err("couldn't find reset gpio\n"); + goto free_bcm_32k_clk; + } + bcm4329_rfkill->gpio_reset = res->start; + tegra_gpio_enable(bcm4329_rfkill->gpio_reset); + ret = gpio_request(bcm4329_rfkill->gpio_reset, + "bcm4329_nreset_gpio"); + if (unlikely(ret)) + goto free_bcm_32k_clk; + + res = platform_get_resource_byname(pdev, IORESOURCE_IO, + "bcm4329_nshutdown_gpio"); + if (!res) { + pr_err("couldn't find shutdown gpio\n"); + gpio_free(bcm4329_rfkill->gpio_reset); + goto free_bcm_32k_clk; + } + tegra_gpio_enable(bcm4329_rfkill->gpio_shutdown); + ret = gpio_request(bcm4329_rfkill->gpio_shutdown, + "bcm4329_nshutdown_gpio"); + if (unlikely(ret)) { + gpio_free(bcm4329_rfkill->gpio_reset); + goto free_bcm_32k_clk; + } + + if (bcm4329_rfkill->bt_32k_clk && enable) + clk_enable(bcm4329_rfkill->bt_32k_clk); + gpio_direction_output(bcm4329_rfkill->gpio_shutdown, enable); + gpio_direction_output(bcm4329_rfkill->gpio_reset, enable); + + bt_rfkill = rfkill_alloc("bcm4329 Bluetooth", &pdev->dev, + RFKILL_TYPE_BLUETOOTH, &bcm4329_bt_rfkill_ops, + NULL); + + if (unlikely(!bt_rfkill)) + goto free_bcm_res; + + default_sw_block_state = !enable; + rfkill_set_states(bt_rfkill, default_sw_block_state, false); + + ret = rfkill_register(bt_rfkill); + + if (unlikely(ret)) { + rfkill_destroy(bt_rfkill); + goto free_bcm_res; + } + + return 0; + +free_bcm_res: + gpio_free(bcm4329_rfkill->gpio_shutdown); + gpio_free(bcm4329_rfkill->gpio_reset); +free_bcm_32k_clk: + if (bcm4329_rfkill->bt_32k_clk && enable) + clk_disable(bcm4329_rfkill->bt_32k_clk); + if (bcm4329_rfkill->bt_32k_clk) + clk_put(bcm4329_rfkill->bt_32k_clk); + kfree(bcm4329_rfkill); + return -ENODEV; +} + +static int bcm4329_rfkill_remove(struct platform_device *pdev) +{ + struct rfkill *bt_rfkill = platform_get_drvdata(pdev); + + if (bcm4329_rfkill->bt_32k_clk) + clk_put(bcm4329_rfkill->bt_32k_clk); + rfkill_unregister(bt_rfkill); + rfkill_destroy(bt_rfkill); + gpio_free(bcm4329_rfkill->gpio_shutdown); + gpio_free(bcm4329_rfkill->gpio_reset); + kfree(bcm4329_rfkill); + + return 0; +} + +static struct platform_driver bcm4329_rfkill_driver = { + .probe = bcm4329_rfkill_probe, + .remove = bcm4329_rfkill_remove, + .driver = { + .name = "bcm4329_rfkill", + .owner = THIS_MODULE, + }, +}; + +static int __init bcm4329_rfkill_init(void) +{ + return platform_driver_register(&bcm4329_rfkill_driver); +} + +static void __exit bcm4329_rfkill_exit(void) +{ + platform_driver_unregister(&bcm4329_rfkill_driver); +} + +module_init(bcm4329_rfkill_init); +module_exit(bcm4329_rfkill_exit); + +MODULE_DESCRIPTION("BCM4329 rfkill"); +MODULE_AUTHOR("NVIDIA"); +MODULE_LICENSE("GPL"); |