diff options
Diffstat (limited to 'net/rfkill/wl127x-rfkill.c')
| -rw-r--r-- | net/rfkill/wl127x-rfkill.c | 288 | 
1 files changed, 288 insertions, 0 deletions
diff --git a/net/rfkill/wl127x-rfkill.c b/net/rfkill/wl127x-rfkill.c new file mode 100644 index 00000000000..9d15d3996a1 --- /dev/null +++ b/net/rfkill/wl127x-rfkill.c @@ -0,0 +1,288 @@ +/* + * Bluetooth TI wl127x rfkill power control via GPIO + * + * Copyright (C) 2009 Motorola, Inc. + * Copyright (C) 2008 Texas Instruments + * Initial code: Pavan Savoy <pavan.savoy@gmail.com> (wl127x_power.c) + * + *  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + * + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/gpio.h> +#include <linux/rfkill.h> +#include <linux/platform_device.h> +#include <linux/wl127x-rfkill.h> +#include <linux/delay.h> + +static int wl127x_bt_rfkill_set_power(void *data, bool blocked) +{ +	struct wl127x_rfkill_platform_data *pdata = +		(struct wl127x_rfkill_platform_data *) data; +	int nshutdown_gpio = pdata->bt_nshutdown_gpio; + +	if (blocked) { +		gpio_set_value(nshutdown_gpio, 0); +		if (pdata->bt_hw_disable) +			pdata->bt_hw_disable(); +	} else { +		if (pdata->bt_hw_enable) +			pdata->bt_hw_enable(); +		gpio_set_value(nshutdown_gpio, 1); +	} +	return 0; +} + +static int wl127x_fm_rfkill_set_power(void *data, bool blocked) +{ +	int nshutdown_gpio = (int) data; + +	if (blocked) +		gpio_set_value(nshutdown_gpio, 0); +	else +		gpio_set_value(nshutdown_gpio, 1); + +	return 0; +} + +static const struct rfkill_ops wl127x_bt_rfkill_ops = { +	.set_block = wl127x_bt_rfkill_set_power, +}; + +static const struct rfkill_ops wl127x_fm_rfkill_ops = { +	.set_block = wl127x_fm_rfkill_set_power, +}; + + + +/** +Added to reset the BT chip after Ram download +*/ +static ssize_t reset_wl18xx_chip(struct device *dev, +					   struct device_attribute +					   *attr, const char *buf, size_t size) +{ + +	int bt_enable_gpio; +	printk(KERN_DEBUG  "Calling reset_wl18xx_chip \n"); + +	/* TODO, rework once device tree is pulled in */ +	/* bt_enable_gpio = get_gpio_by_name("bt_reset_b"); */ +	bt_enable_gpio = 83; + +	printk(KERN_DEBUG "bt_enable_gpio = %d\n", bt_enable_gpio); + +	if (bt_enable_gpio < 0) { + +		printk(KERN_DEBUG "reset_wl18xx_chip: cannot retrieve bt_reset_b gpio from device tree\n"); +		bt_enable_gpio = -1; +		return -EINVAL; +	} + +	gpio_set_value(bt_enable_gpio, 0); +	msleep(5); +	gpio_set_value(bt_enable_gpio, 1); +	printk(KERN_DEBUG " successfully set the value\n"); + +	return 0; + +} + +static DEVICE_ATTR(reset_vio, 0644, NULL, reset_wl18xx_chip); + + +static int wl127x_rfkill_probe(struct platform_device *pdev) +{ +	int rc = 0; +	bool fm_deinit_required_flag = false; +	struct wl127x_rfkill_platform_data *pdata = pdev->dev.platform_data; + +	if (pdata->bt_nshutdown_gpio >= 0) { +		bool default_blocked = true;  /* power off */ +		printk(KERN_DEBUG "Entered the bt enable part \n"); +		rc = gpio_request(pdata->bt_nshutdown_gpio, +				  "wl127x_bt_nshutdown_gpio"); +		if (unlikely(rc)) +			return rc; + +		rc = gpio_direction_output(pdata->bt_nshutdown_gpio, 0); +		if (unlikely(rc)) { +			printk(KERN_ERR "wl127x_rfkill_probe failure could not set output direction for gpio bt_nshutdown_gpio \n"); +			goto bt_err_gpio_direction; +		} + +		if (pdata->bt_hw_init) +			rc = pdata->bt_hw_init(); +		if (unlikely(rc)) { +			printk(KERN_ERR "wl127x_rfkill_probe failure could not init hardware \n"); +			goto bt_err_gpio_direction; +		} + + +		wl127x_bt_rfkill_set_power((void *)pdata, default_blocked); + +		pdata->rfkill[WL127X_BLUETOOTH] = rfkill_alloc( +				"wl127x Bluetooth", &pdev->dev, +				RFKILL_TYPE_BLUETOOTH, &wl127x_bt_rfkill_ops, +				(void *)pdata); +		if (unlikely(!pdata->rfkill[WL127X_BLUETOOTH])) { +			printk(KERN_ERR "wl127x_rfkill_probe failure could not allocate memory \n"); +			rc = -ENOMEM; +			goto bt_err_rfkill_alloc; +		} + +		rfkill_set_states(pdata->rfkill[WL127X_BLUETOOTH], +				default_blocked, false); + +		rc = rfkill_register(pdata->rfkill[WL127X_BLUETOOTH]); +		if (unlikely(rc)) { +			printk(KERN_ERR "wl127x_rfkill_probe failure could to register BT \n"); +			goto bt_err_rfkill_register; +		} + +	} + +	if (pdata->fm_enable_gpio >= 0) { +		bool default_blocked = true;  /* power off */ +		printk(KERN_DEBUG "Entered the fm enable part \n"); +		rc = gpio_request(pdata->fm_enable_gpio, +				  "wl127x_fm_enable_gpio"); +		if (unlikely(rc)) +			return rc; + +		rc = gpio_direction_output(pdata->fm_enable_gpio, 0); +		if (unlikely(rc)) { +			printk(KERN_ERR "wl127x_rfkill_probe failure could not set output direction for gpio fm_enable_gpio \n"); +			goto fm_err_gpio_direction; +		} + +		wl127x_fm_rfkill_set_power((void *)pdata->fm_enable_gpio, +				default_blocked); + +		pdata->rfkill[WL127X_FM] = rfkill_alloc("wl127x FM Radio", +				&pdev->dev, RFKILL_TYPE_FM, +				&wl127x_fm_rfkill_ops, +				(void *)pdata->fm_enable_gpio); +		if (unlikely(!pdata->rfkill[WL127X_FM])) { +			printk(KERN_ERR "wl127x_rfkill_probe failure could not allocate memory for fm\n"); +			rc = -ENOMEM; +			goto fm_err_gpio_direction; + +		} + +		rfkill_set_states(pdata->rfkill[WL127X_FM], default_blocked, +				false); + +		rc = rfkill_register(pdata->rfkill[WL127X_FM]); +		if (unlikely(rc)) { +			printk(KERN_ERR "wl127x_rfkill_probe failure could to register FM \n"); +			goto fm_err_rfkill_register; +		} + +		fm_deinit_required_flag = true; +	} + +	/* Create device file to expose interface to user space to +	reset the vio of BT, this should be done independent of whether +	BT/FM is initialised as both run on the same w18xx chip +	*/ +	if (device_create_file(&pdev->dev, &dev_attr_reset_vio)) { +		printk(KERN_DEBUG  "Error creating sys entry for reset vio\n"); +		rc = -1; +		goto bt_err_rfkill_register; +	} + +	goto done; + +	/* Clean up for BT generic registration */ +bt_err_rfkill_register: +	rfkill_destroy(pdata->rfkill[WL127X_BLUETOOTH]); +bt_err_rfkill_alloc: +	if (pdata->bt_hw_release) +		pdata->bt_hw_release(); +bt_err_gpio_direction: +	if (pdata->bt_nshutdown_gpio >= 0) +		gpio_free(pdata->bt_nshutdown_gpio); + +	if (fm_deinit_required_flag == false) +		goto done; + +	fm_deinit_required_flag = false; + +	/* Clean up for FM generic registration +	do not clean up BT process as we still want to use BT +	*/ +fm_err_rfkill_register: +	rfkill_destroy(pdata->rfkill[WL127X_FM]); +fm_err_gpio_direction: +	gpio_free(pdata->fm_enable_gpio); + +done: +		return rc; +} + +static int wl127x_rfkill_remove(struct platform_device *pdev) +{ +	struct wl127x_rfkill_platform_data *pdata = pdev->dev.platform_data; + +	if (pdata->bt_nshutdown_gpio >= 0) { +		rfkill_unregister(pdata->rfkill[WL127X_BLUETOOTH]); +		rfkill_destroy(pdata->rfkill[WL127X_BLUETOOTH]); +		if (pdata->bt_hw_release) +			pdata->bt_hw_release(); +		gpio_free(pdata->bt_nshutdown_gpio); +	} + + +	if (pdata->fm_enable_gpio >= 0) { +		rfkill_unregister(pdata->rfkill[WL127X_FM]); +		rfkill_destroy(pdata->rfkill[WL127X_FM]); +		gpio_free(pdata->fm_enable_gpio); +	} + +	/* remove the sys fs file created as part of probe*/ +	device_remove_file(&pdev->dev, &dev_attr_reset_vio); + +	return 0; +} + +static struct platform_driver wl127x_rfkill_platform_driver = { +	.probe = wl127x_rfkill_probe, +	.remove = wl127x_rfkill_remove, +	.driver = { +		   .name = "wl127x-rfkill", +		   .owner = THIS_MODULE, +		   }, +}; + +static int __init wl127x_rfkill_init(void) +{ +	return platform_driver_register(&wl127x_rfkill_platform_driver); +} + +static void __exit wl127x_rfkill_exit(void) +{ +	platform_driver_unregister(&wl127x_rfkill_platform_driver); +} + +module_init(wl127x_rfkill_init); +module_exit(wl127x_rfkill_exit); + +MODULE_ALIAS("platform:wl127x"); +MODULE_DESCRIPTION("wl127x-rfkill"); +MODULE_AUTHOR("Motorola"); +MODULE_LICENSE("GPL");  |