diff options
author | Anita Kar <akar@nvidia.com> | 2013-04-03 14:53:55 +0530 |
---|---|---|
committer | Dan Willemsen <dwillemsen@nvidia.com> | 2013-09-14 13:07:04 -0700 |
commit | a94d329f51de027b78e0eaf9c93f98ec6fa06739 (patch) | |
tree | f24f91728f19f5d4fa3c8b39c8ddc2f77e6eb51b /drivers/misc/ti-st | |
parent | ce0f36537811cb14409457ae9e8229f569f3c062 (diff) |
TI Bluetooth: Adding TI Host Wakeup Driver changes
Signed-off-by: Raghavendra Shenoy Mathav <raghavendra.shenoy@ti.com>
Bug 1179655
Change-Id: I904ed2d392b6ff8fbfb00e949f470542387aace4
Signed-off-by: Nagarjuna Kristam <nkristam@nvidia.com>
Reviewed-on: http://git-master/r/197395
(cherry picked from commit 4ee03e6f06df0581272a5899267dd295279d2a4c)
Signed-off-by: Anita Kar <akar@nvidia.com>
Reviewed-on: http://git-master/r/215507
Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com>
Diffstat (limited to 'drivers/misc/ti-st')
-rw-r--r-- | drivers/misc/ti-st/Kconfig | 28 | ||||
-rw-r--r-- | drivers/misc/ti-st/Makefile | 1 | ||||
-rw-r--r-- | drivers/misc/ti-st/st_core.c | 22 | ||||
-rwxr-xr-x | drivers/misc/ti-st/st_host_wake.c | 295 |
4 files changed, 344 insertions, 2 deletions
diff --git a/drivers/misc/ti-st/Kconfig b/drivers/misc/ti-st/Kconfig index 9417d76284c4..432551f04280 100644 --- a/drivers/misc/ti-st/Kconfig +++ b/drivers/misc/ti-st/Kconfig @@ -30,4 +30,32 @@ config ST_HCI user-space Bluetooth stacks. It will provide a character device for user space Bluetooth stack to send/receive data over shared transport. + +config ST_HOST_WAKE + tristate "Host wake up driver for protocols registered with ST" + depends on TI_ST + help + This enables the host to wake up whenever a transaction is initiated + by the remote side. + +config ST_HOST_WAKE_GPS + bool "Enable Host wake up driver when GPS registers with ST" + depends on ST_HOST_WAKE + help + This enables the host to wake up whenever GPS transaction is initiated + by the remote side. + +config ST_HOST_WAKE_FM + bool "Enable Host wake up driver when FM registers with ST" + depends on ST_HOST_WAKE + help + This enables the host to wake up whenever FM transaction is initiated + by the remote side. + +config ST_HOST_WAKE_NFC + bool "Enable Host wake up driver when NFC registers with ST" + depends on ST_HOST_WAKE + help + This enables the host to wake up whenever NFC transaction is initiated + by the remote side. endmenu diff --git a/drivers/misc/ti-st/Makefile b/drivers/misc/ti-st/Makefile index 2449eade3203..779adc7007d7 100644 --- a/drivers/misc/ti-st/Makefile +++ b/drivers/misc/ti-st/Makefile @@ -6,3 +6,4 @@ obj-$(CONFIG_TI_ST) += st_drv.o st_drv-objs := st_core.o st_kim.o st_ll.o obj-$(CONFIG_ST_GPS) += gps_drv.o obj-$(CONFIG_ST_HCI) += tty_hci.o +obj-$(CONFIG_ST_HOST_WAKE) += st_host_wake.o diff --git a/drivers/misc/ti-st/st_core.c b/drivers/misc/ti-st/st_core.c index cc605cd96e3c..1491d1d5d01c 100644 --- a/drivers/misc/ti-st/st_core.c +++ b/drivers/misc/ti-st/st_core.c @@ -46,6 +46,9 @@ static void add_channel_to_table(struct st_data_s *st_gdata, /* list now has the channel id as index itself */ st_gdata->list[new_proto->chnl_id] = new_proto; st_gdata->is_registered[new_proto->chnl_id] = true; +#ifdef CONFIG_ST_HOST_WAKE + st_host_wake_notify(new_proto->chnl_id, ST_PROTO_REGISTERED); +#endif } static void remove_channel_from_table(struct st_data_s *st_gdata, @@ -54,6 +57,9 @@ static void remove_channel_from_table(struct st_data_s *st_gdata, pr_info("%s: id %d\n", __func__, proto->chnl_id); /* st_gdata->list[proto->chnl_id] = NULL; */ st_gdata->is_registered[proto->chnl_id] = false; +#ifdef CONFIG_ST_HOST_WAKE + st_host_wake_notify(proto->chnl_id, ST_PROTO_UNREGISTERED); +#endif } /* @@ -552,6 +558,10 @@ long st_register(struct st_proto_s *new_proto) /* release lock previously held - re-locked below */ spin_unlock_irqrestore(&st_gdata->lock, flags); +#ifdef CONFIG_ST_HOST_WAKE + /*Enable Voltage regulation*/ + st_vltg_regulation(ST_VLTG_REG_ENABLE); +#endif /* this may take a while to complete * since it involves BT fw download */ @@ -564,6 +574,11 @@ long st_register(struct st_proto_s *new_proto) st_reg_complete(st_gdata, err); clear_bit(ST_REG_PENDING, &st_gdata->st_state); } + +#ifdef CONFIG_ST_HOST_WAKE + /*Disable Voltage regulation*/ + st_vltg_regulation(ST_VLTG_REG_DISABLE); +#endif return -EINVAL; } @@ -603,7 +618,6 @@ long st_register(struct st_proto_s *new_proto) add_channel_to_table(st_gdata, new_proto); st_gdata->protos_registered++; new_proto->write = st_write; - /* lock already held before entering else */ spin_unlock_irqrestore(&st_gdata->lock, flags); return err; @@ -648,7 +662,6 @@ long st_unregister(struct st_proto_s *proto) if ((st_gdata->protos_registered == ST_EMPTY) && (!test_bit(ST_REG_PENDING, &st_gdata->st_state))) { pr_info(" all chnl_ids unregistered "); - /* stop traffic on tty */ if (st_gdata->tty) { tty_ldisc_flush(st_gdata->tty); @@ -657,6 +670,11 @@ long st_unregister(struct st_proto_s *proto) /* all chnl_ids now unregistered */ st_kim_stop(st_gdata->kim_data); + +#ifdef CONFIG_ST_HOST_WAKE + /*Disable Voltage regulation*/ + st_vltg_regulation(ST_VLTG_REG_DISABLE); +#endif /* disable ST LL */ st_ll_disable(st_gdata); } diff --git a/drivers/misc/ti-st/st_host_wake.c b/drivers/misc/ti-st/st_host_wake.c new file mode 100755 index 000000000000..3246f1563fe2 --- /dev/null +++ b/drivers/misc/ti-st/st_host_wake.c @@ -0,0 +1,295 @@ +/* + * Shared Transport Host wake up driver + * For protocols registered over Shared Transport + * Copyright (C) 2011-2012 Texas Instruments + * + * 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. + * + * 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/interrupt.h> +#include <linux/platform_device.h> + +#include <linux/ti_wilink_st.h> + +#include <linux/regulator/consumer.h> + +#define VERSION "1.2" +#define FLAG_RESET 0x00 +#define HOST_WAKE 0x01 +#define IRQ_WAKE 0x02 + +#define CHANNEL_ACL 0x02 +#define CHANNEL_EVT 0x04 +#define CHANNEL_FM 0x08 +#define CHANNEL_GPS 0x09 +#define CHANNEL_NFC 0x0C + +struct st_host_wake_info { + unsigned host_wake_irq; + struct regulator *vdd_3v3; + struct regulator *vdd_1v8; + unsigned int supp_proto_reg; +}; + +static unsigned long flags; +static struct st_host_wake_info *bsi; +static char dev_id[12] ="st_host_wake"; + +void st_host_wake_notify(int chan_id, int reg_state) +{ + /* HOST_WAKE to be set after all BT channels including CHANNEL_SCO + * is registered + */ + if(chan_id == CHANNEL_ACL || chan_id == CHANNEL_EVT) + return; + +#ifndef CONFIG_ST_HOST_WAKE_GPS + if(chan_id == CHANNEL_GPS) { + pr_info("CONFIG_ST_HOST_WAKE_GPS not set hence reject"); + return; + } +#endif + +#ifndef CONFIG_ST_HOST_WAKE_FM + if(chan_id == CHANNEL_FM) { + pr_info("CONFIG_ST_HOST_WAKE_FM not set hence reject"); + return; + } +#endif + +#ifndef CONFIG_ST_HOST_WAKE_NFC + if(chan_id == CHANNEL_NFC) { + pr_info("CONFIG_ST_HOST_WAKE_NFC not set hence reject"); + return; + } +#endif + switch(reg_state) { + case ST_PROTO_REGISTERED: + pr_info("Channel %d registered", chan_id); + bsi->supp_proto_reg++; + set_bit(HOST_WAKE, &flags); + pr_info("HOST_WAKE set"); + break; + + case ST_PROTO_UNREGISTERED: + pr_info("Channel %d un-registered", chan_id); + bsi->supp_proto_reg--; + + if(!bsi->supp_proto_reg) { + pr_info("All supported protocols un-registered"); + if(bsi && test_bit(IRQ_WAKE, &flags)) { + pr_info("disabling wake_irq after unregister"); + disable_irq_wake(bsi->host_wake_irq); + clear_bit(IRQ_WAKE, &flags); + } + + clear_bit(HOST_WAKE, &flags); + pr_info("HOST_WAKE cleared"); + } + break; + + default: + break; + } +} +EXPORT_SYMBOL(st_host_wake_notify); + +void st_vltg_regulation(int state) +{ + pr_info("%s with state %d", __func__, state); + + if(ST_VLTG_REG_ENABLE == state) { + if (bsi->vdd_3v3) + regulator_enable(bsi->vdd_3v3); + if (bsi->vdd_1v8) + regulator_enable(bsi->vdd_1v8); + + } else if(ST_VLTG_REG_DISABLE == state) { + if (bsi->vdd_3v3) + regulator_disable(bsi->vdd_3v3); + if (bsi->vdd_1v8) + regulator_disable(bsi->vdd_1v8); + + } else { + pr_warn("Unknown voltage regulation state"); + } +} +EXPORT_SYMBOL(st_vltg_regulation); + +static irqreturn_t st_host_wake_isr(int irq, void *dev_id) +{ + pr_debug("%s", __func__); + + return IRQ_HANDLED; +} + +static int st_host_wake_probe(struct platform_device *pdev) +{ + int ret = 0; + struct resource *res = NULL; + + pr_info("TI Host Wakeup Driver [Ver %s]", VERSION); + + bsi = kzalloc(sizeof(struct st_host_wake_info), GFP_KERNEL); + if (!bsi) + return -ENOMEM; + + bsi->vdd_3v3 = regulator_get(&pdev->dev, "vdd_st_3v3"); + if (IS_ERR_OR_NULL(bsi->vdd_3v3)) { + pr_warn("%s: regulator vdd_st_3v3 not available\n", __func__); + bsi->vdd_3v3 = NULL; + } + bsi->vdd_1v8 = regulator_get(&pdev->dev, "vddio_st_1v8"); + if (IS_ERR_OR_NULL(bsi->vdd_1v8)) { + pr_warn("%s: regulator vddio_st_1v8 not available\n", __func__); + bsi->vdd_1v8 = NULL; + } + + res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, + "host_wake"); + if (!res) { + pr_err("couldn't find host_wake irq\n"); + ret = -ENODEV; + goto free_bsi; + } + + bsi->host_wake_irq = res->start; + clear_bit(IRQ_WAKE, &flags); + + if (bsi->host_wake_irq < 0) { + pr_err("couldn't find host_wake irq"); + ret = -ENODEV; + goto free_bsi; + } + + if (res->flags & IORESOURCE_IRQ_LOWEDGE) + ret = request_irq(bsi->host_wake_irq, st_host_wake_isr, + IRQF_DISABLED | IRQF_TRIGGER_FALLING, + "bluetooth hostwake", dev_id); + else + ret = request_irq(bsi->host_wake_irq, st_host_wake_isr, + IRQF_DISABLED | IRQF_TRIGGER_RISING, + "bluetooth hostwake", dev_id); + + if (ret < 0) { + pr_err("Couldn't acquire HOST_WAKE IRQ"); + goto free_bsi; + } + + clear_bit(HOST_WAKE, &flags); + bsi->supp_proto_reg = 0; + + goto finish; + +free_bsi: + kfree(bsi); +finish: + return ret; +} + +static int st_host_wake_remove(struct platform_device *pdev) +{ + pr_debug("%s", __func__); + + free_irq(bsi->host_wake_irq, dev_id); + + if (bsi->vdd_3v3) + regulator_put(bsi->vdd_3v3); + if (bsi->vdd_1v8) + regulator_put(bsi->vdd_1v8); + + kfree(bsi); + + return 0; +} + +static int st_host_wake_resume(struct platform_device *pdev) +{ + pr_info("%s", __func__); + + if (test_bit(HOST_WAKE, &flags) && test_bit(IRQ_WAKE, &flags)) { + pr_info("disable the host_wake irq"); + disable_irq_wake(bsi->host_wake_irq); + clear_bit(IRQ_WAKE, &flags); + } + + return 0; +} + +static int st_host_wake_suspend(struct platform_device *pdev, + pm_message_t state) +{ + int retval = 0; + + pr_info("%s", __func__); + + if (test_bit(HOST_WAKE, &flags) && (!test_bit(IRQ_WAKE, &flags))) { + retval = enable_irq_wake(bsi->host_wake_irq); + if (retval < 0) { + pr_err("Couldn't enable HOST_WAKE as wakeup" + "interrupt retval %d\n", retval); + goto fail; + } + set_bit(IRQ_WAKE, &flags); + pr_info("enabled the host_wake irq"); + } +fail: + return retval; +} + + +static struct platform_driver st_host_wake_driver = { + .probe = st_host_wake_probe, + .remove = st_host_wake_remove, + .suspend = st_host_wake_suspend, + .resume = st_host_wake_resume, + .driver = { + .name = "st_host_wake", + .owner = THIS_MODULE, + }, +}; + +static int __init st_host_wake_init(void) +{ + int retval = 0; + + pr_debug("%s", __func__); + + retval = platform_driver_register(&st_host_wake_driver); + if(retval) + pr_err("st_host_wake_init failed"); + + return retval; +} + +static void __exit st_host_wake_exit(void) +{ + pr_debug("%s", __func__); + + if (bsi == NULL) + return; + + platform_driver_unregister(&st_host_wake_driver); +} + +module_init(st_host_wake_init); +module_exit(st_host_wake_exit); + +MODULE_DESCRIPTION("TI Host Wakeup Driver [Ver %s]" VERSION); +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif |