TI Bluetooth: Adding TI Host Wakeup Driver changes
Anita Kar [Wed, 3 Apr 2013 09:23:55 +0000 (14:23 +0530)]
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>

drivers/misc/ti-st/Kconfig
drivers/misc/ti-st/Makefile
drivers/misc/ti-st/st_core.c
drivers/misc/ti-st/st_host_wake.c [new file with mode: 0755]
include/linux/ti_wilink_st.h

index 9417d76..432551f 100644 (file)
@@ -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
index 2449ead..779adc7 100644 (file)
@@ -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
index cc605cd..1491d1d 100644 (file)
@@ -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 (executable)
index 0000000..3246f15
--- /dev/null
@@ -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
index b768209..ff135a7 100644 (file)
@@ -453,4 +453,15 @@ struct ti_st_plat_data {
        int (*chip_awake) (struct kim_data_s *);
 };
 
+/*ST states used in st_host_wake driver*/
+#define ST_PROTO_UNREGISTERED  0
+#define ST_PROTO_REGISTERED    1
+
+void st_host_wake_notify(int, int);
+
+/*ST Voltage regulation state*/
+#define ST_VLTG_REG_DISABLE    0
+#define ST_VLTG_REG_ENABLE     1
+
+void st_vltg_regulation(int);
 #endif /* TI_WILINK_ST_H */