diff options
Diffstat (limited to 'arch/arm/mach-mx5/imx_bt_rfkill.c')
-rwxr-xr-x | arch/arm/mach-mx5/imx_bt_rfkill.c | 169 |
1 files changed, 169 insertions, 0 deletions
diff --git a/arch/arm/mach-mx5/imx_bt_rfkill.c b/arch/arm/mach-mx5/imx_bt_rfkill.c new file mode 100755 index 00000000..372ba0fe --- /dev/null +++ b/arch/arm/mach-mx5/imx_bt_rfkill.c @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * 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. + */ + +/*! + * @file imx_bt_rfkill.c + * + * @brief This driver is implement a rfkill control interface of bluetooth + * chip on i.MX serial boards. Register the power regulator function and + * reset function in platform data. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/suspend.h> +#include <linux/init.h> +#include <linux/rfkill.h> +#include <mach/hardware.h> +#include <mach/imx_rfkill.h> + +static int system_in_suspend; + +static int imx_bt_set_block(void *rfkdata, bool blocked) +{ + struct imx_bt_rfkill_platform_data *data = rfkdata; + int ret; + + /* Bluetooth stack will reset the bluetooth chip during + * resume, since we keep bluetooth's power during suspend, + * don't let rfkill to actually reset the chip. */ + if (system_in_suspend) + return 0; + + pr_info("rfkill: Bluetooth RF turn %s\n", blocked ? "off" : "on"); + if (!blocked) + ret = data->power_change(1); + else + ret = data->power_change(0); + + return ret; +} + +static const struct rfkill_ops imx_bt_rfkill_ops = { + .set_block = imx_bt_set_block, +}; + +static int imx_bt_power_event(struct notifier_block *this, + unsigned long event, void *dummy) +{ + switch (event) { + case PM_SUSPEND_PREPARE: + system_in_suspend = 1; + /* going to suspend, don't reset chip */ + break; + case PM_POST_SUSPEND: + system_in_suspend = 0; + /* System is resume, can reset chip */ + break; + default: + break; + } + return NOTIFY_DONE; +} + +static struct notifier_block imx_bt_power_notifier = { + .notifier_call = imx_bt_power_event, +}; + +static int imx_bt_rfkill_probe(struct platform_device *dev) +{ + int rc; + struct rfkill *rfk; + + struct imx_bt_rfkill_platform_data *data = dev->dev.platform_data; + + if (data->power_change == NULL) { + rc = -EINVAL; + dev_err(&dev->dev, "no power_change function\n"); + goto error_check_func; + } + + rc = register_pm_notifier(&imx_bt_power_notifier); + if (rc) + goto error_check_func; + + rfk = rfkill_alloc("imx-bt", &dev->dev, RFKILL_TYPE_BLUETOOTH, + &imx_bt_rfkill_ops, data); + + if (!rfk) { + rc = -ENOMEM; + goto error_rfk_alloc; + } + + rc = rfkill_register(rfk); + if (rc) + goto error_rfkill; + + platform_set_drvdata(dev, rfk); + printk(KERN_INFO "imx_bt_rfkill driver success loaded\n"); + return 0; + +error_rfkill: + rfkill_destroy(rfk); +error_rfk_alloc: +error_check_func: + return rc; +} + +static int __devexit imx_bt_rfkill_remove(struct platform_device *dev) +{ + struct imx_bt_rfkill_platform_data *data = dev->dev.platform_data; + struct rfkill *rfk = platform_get_drvdata(dev); + + platform_set_drvdata(dev, NULL); + + if (rfk) { + rfkill_unregister(rfk); + rfkill_destroy(rfk); + } + + data->power_change(0); + + return 0; +} + +static struct platform_driver imx_bt_rfkill_drv = { + .driver = { + .name = "imx_bt_rfkill", + }, + + .probe = imx_bt_rfkill_probe, + .remove = __devexit_p(imx_bt_rfkill_remove), +}; + +static int __init imx_bt_rfkill_init(void) +{ + return platform_driver_register(&imx_bt_rfkill_drv); +} + +module_init(imx_bt_rfkill_init); + +static void __exit imx_bt_rfkill_exit(void) +{ + platform_driver_unregister(&imx_bt_rfkill_drv); +} + +module_exit(imx_bt_rfkill_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("RFKill control interface of BT on MX53 SMD"); |