TI Wl128x support of bluesleep & Wake on BT Driver changes
Anjan Rao [Mon, 5 Mar 2012 12:41:13 +0000 (17:41 +0530)]
Bug 933054
Bug 931931

Change-Id: Id84bcc1791114a50d26547de41daeb4774f6026b
Signed-off-by: Anjan Rao <anjan.rao@ti.com>
Reviewed-on: http://git-master/r/89136
Reviewed-by: Nagarjuna Kristam <nkristam@nvidia.com>
Tested-by: Nagarjuna Kristam <nkristam@nvidia.com>
Reviewed-by: Rakesh Goyal <rgoyal@nvidia.com>
Reviewed-by: Sachin Nikam <snikam@nvidia.com>

Rebase-Id: R8d732d2c1f761e6f1e92415d0ebfcd1f35fa0252

drivers/bluetooth/Kconfig
drivers/bluetooth/Makefile
drivers/bluetooth/ti_bluesleep.c [new file with mode: 0644]
drivers/misc/ti-st/st_core.c
drivers/misc/ti-st/st_kim.c
drivers/misc/ti-st/st_ll.c
include/linux/ti_wilink_st.h

index 83f4f31..e1dc750 100644 (file)
@@ -103,6 +103,16 @@ config BT_BLUESLEEP
           Say Y here to compile support for bluesleep support into the kernel
           or say M to compile it as module (bluesleep).
 
+config BT_TIBLUESLEEP
+        tristate "Bluesleep driver support for TI"
+        help
+          TI Bluetooth Bluesleep Driver.
+          This driver provides the dynamic active power saving mechanism for
+          bluetooth radio devices.
+
+          Say Y here to compile support for bluesleep support into the kernel
+          or say M to compile it as module (bluesleep).
+
 config BT_HCIBPA10X
        tristate "HCI BPA10x USB driver"
        depends on USB
index 4c2f9d0..1742f0a 100644 (file)
@@ -12,6 +12,7 @@ obj-$(CONFIG_BT_HCIDTL1)      += dtl1_cs.o
 obj-$(CONFIG_BT_HCIBT3C)       += bt3c_cs.o
 obj-$(CONFIG_BT_HCIBLUECARD)   += bluecard_cs.o
 obj-$(CONFIG_BT_HCIBTUART)     += btuart_cs.o
+obj-$(CONFIG_BT_TIBLUESLEEP)   += ti_bluesleep.o
 
 obj-$(CONFIG_BT_HCIBTUSB)      += btusb.o
 obj-$(CONFIG_BT_HCIBTSDIO)     += btsdio.o
diff --git a/drivers/bluetooth/ti_bluesleep.c b/drivers/bluetooth/ti_bluesleep.c
new file mode 100644 (file)
index 0000000..4a7c503
--- /dev/null
@@ -0,0 +1,392 @@
+/*
+ *  TI Bluesleep driver
+ *     Kernel module responsible for Wake up of Host
+ *  Copyright (C) 2009-2010 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.
+ *
+ * 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
+ *
+ * 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.
+ *
+ * Copyright (C) 2006-2007 - Motorola
+ * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved.
+ *
+ *  Date         Author           Comment
+ * -----------  --------------   --------------------------------
+ * 2006-Apr-28  Motorola         The kernel module for running the Bluetooth(R)
+ *                               Sleep-Mode Protocol from the Host side
+ * 2006-Sep-08  Motorola         Added workqueue for handling sleep work.
+ * 2007-Jan-24  Motorola         Added mbm_handle_ioi() call to ISR.
+ * 2009-Aug-10  Motorola         Changed "add_timer" to "mod_timer" to solve
+ *                               race when flurry of queued work comes in.
+*/
+
+#include <linux/module.h>       /* kernel module definitions */
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/notifier.h>
+#include <linux/proc_fs.h>
+#include <linux/spinlock.h>
+#include <linux/timer.h>
+#include <linux/uaccess.h>
+#include <linux/version.h>
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+
+#include <linux/irq.h>
+#include <linux/ioport.h>
+#include <linux/param.h>
+#include <linux/bitops.h>
+#include <linux/termios.h>
+#include <linux/wakelock.h>
+#include <mach/gpio.h>
+#include <linux/serial_core.h>
+#include <linux/tegra_uart.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h> /* event notifications */
+#include <linux/ti_wilink_st.h>
+#include "hci_uart.h"
+
+/*
+ * Defines
+ */
+
+#define VERSION         "1.1"
+
+#define POLARITY_LOW 0
+#define POLARITY_HIGH 1
+
+struct bluesleep_info {
+       unsigned host_wake_irq;
+       struct uart_port *uport;
+       struct wake_lock wake_lock;
+       int irq_polarity;
+       int has_ext_wake;
+};
+
+
+/* state variable names and bit positions */
+#define FLAG_RESET             0x00
+#define BT_ACTIVE              0x02
+#define BT_SUSPEND             0x04
+
+
+/* work function */
+static void hostwake_sleep_work(struct work_struct *work);
+
+/* work queue */
+DECLARE_DELAYED_WORK(ti_sleep_workqueue, hostwake_sleep_work);
+
+/* Macros for handling sleep work */
+#define hostwake_workqueue()     schedule_delayed_work(&ti_sleep_workqueue, 0)
+
+static struct bluesleep_info *bsi;
+
+/* module usage */
+static atomic_t open_count = ATOMIC_INIT(1);
+
+/*
+ * Global variables
+ */
+/** Global state flags */
+static unsigned long flags;
+
+/** Tasklet to respond to change in hostwake line */
+static struct tasklet_struct hostwake_task;
+
+/** Lock for state transitions */
+static spinlock_t rw_lock;
+
+/*
+ * Local functions
+ */
+static void hsuart_power(int on)
+{
+       pr_debug("%s", __func__);
+
+       if (on) {
+               tegra_uart_request_clock_on(bsi->uport);
+               tegra_uart_set_mctrl(bsi->uport, TIOCM_RTS);
+       } else {
+               tegra_uart_set_mctrl(bsi->uport, 0);
+               tegra_uart_request_clock_off(bsi->uport);
+       }
+}
+
+
+
+/**
+ * @brief@  main sleep work handling function which update the flags
+ * and activate and deactivate UART .
+ */
+
+static void hostwake_sleep_work(struct work_struct *work)
+{
+       int retval ;    
+       pr_debug("%s", __func__);
+       free_irq(bsi->host_wake_irq, "tibluesleep");
+       /*Activating UART */
+       if (test_bit(BT_SUSPEND, &flags)) {
+               BT_DBG("Activate UART");
+               hsuart_power(1);
+
+       }
+       bsi->has_ext_wake = 0;
+       clear_bit(BT_SUSPEND, &flags);
+       set_bit(BT_ACTIVE, &flags);
+
+}
+
+
+/**
+ * A tasklet function that runs in tasklet context 
+ * @param data Not used.
+ */
+static void bluesleep_hostwake_task(unsigned long data)
+{
+       pr_debug("%s", __func__);
+       disable_irq(bsi->host_wake_irq);
+       spin_lock(&rw_lock);
+       hostwake_workqueue();
+       spin_unlock(&rw_lock);
+
+}
+
+
+/**
+ * Schedules a tasklet to run when receiving an interrupt on the
+ * <code>HOST_WAKE</code> GPIO pin.
+ * @param irq Not used.
+ * @param dev_id Not used.
+ */
+static irqreturn_t bluesleep_hostwake_isr(int irq, void *dev_id)
+{
+
+       pr_debug("%s", __func__);
+       /* schedule a tasklet to handle the change in the host wake line */
+       bsi->has_ext_wake = 1;
+       tasklet_schedule(&hostwake_task);
+       return IRQ_HANDLED;
+}
+
+/**
+ * Starts the Sleep-Mode Protocol on the Host.
+ * @return On success, 0. On error, -1, and <code>errno</code> is set
+ * appropriately.
+ */
+
+  int bluesleep_start(struct uart_port *uport)
+{
+       int retval;
+
+       bsi->uport = uport;
+
+       pr_debug("%s", __func__);
+
+       if (test_bit(BT_SUSPEND, &flags)) {
+
+
+               BT_DBG("bluesleep_acquire irq");
+               if (bsi->irq_polarity == POLARITY_LOW) {
+                       retval = request_irq(bsi->host_wake_irq, bluesleep_hostwake_isr,
+                                       IRQF_DISABLED | IRQF_TRIGGER_FALLING,
+                                       "bluetooth hostwake", "tibluesleep");
+               } else  {
+                       retval = request_irq(bsi->host_wake_irq, bluesleep_hostwake_isr,
+                                       IRQF_DISABLED | IRQF_TRIGGER_RISING,
+                                       "bluetooth hostwake", "tibluesleep");
+               }
+               if (retval  < 0) {
+                       BT_ERR("Couldn't acquire BT_HOST_WAKE IRQ");
+                       goto fail;
+               }
+
+       retval = enable_irq_wake(bsi->host_wake_irq);
+       if (retval < 0) {
+               BT_ERR("Couldn't enable BT_HOST_WAKE as wakeup interrupt retval %d\n",retval);
+               free_irq(bsi->host_wake_irq, NULL);
+               goto fail;
+       }
+
+       }
+
+       return 0;
+fail:
+       atomic_inc(&open_count);
+
+       return retval;
+}
+
+/**
+ * Stops the Sleep-Mode Protocol on the Host.
+ */
+  void bluesleep_stop(void)
+{
+
+       pr_debug("%s", __func__);
+
+       if (disable_irq_wake(bsi->host_wake_irq))
+               BT_ERR("Couldn't disable hostwake IRQ wakeup mode\n");
+
+       free_irq(bsi->host_wake_irq, NULL);
+
+}
+
+static int bluesleep_probe(struct platform_device *pdev)
+{
+       int ret;
+       struct resource *res;
+
+       struct kim_data_s *kim_gdata;
+       struct st_data_s *core_data;
+       kim_gdata = dev_get_drvdata(&pdev->dev);
+       core_data = kim_gdata->core_data;
+       struct uart_state *state = (struct uart_state *)core_data->tty->driver_data;    
+
+
+       bsi = kzalloc(sizeof(struct bluesleep_info), GFP_KERNEL);
+       if (!bsi)
+               return -ENOMEM;
+
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+                                               "host_wake");
+       if (!res) {
+               BT_ERR("couldn't find host_wake irq\n");
+               ret = -ENODEV;
+               goto free_bsi;
+       }
+
+       bsi->host_wake_irq = res->start;        
+
+       if (bsi->host_wake_irq < 0) {
+               BT_ERR("couldn't find host_wake irq");
+               ret = -ENODEV;
+               goto free_bsi;
+       }
+       if (res->flags & IORESOURCE_IRQ_LOWEDGE)
+               bsi->irq_polarity = POLARITY_LOW;/*low edge (falling edge)*/
+       else
+               bsi->irq_polarity = POLARITY_HIGH;/*anything else*/
+
+       wake_lock_init(&bsi->wake_lock, WAKE_LOCK_SUSPEND, "bluesleep");
+       clear_bit(BT_SUSPEND, &flags);
+       set_bit(BT_ACTIVE, &flags);
+
+       return 0;
+
+
+free_bsi:
+       kfree(bsi);
+       return ret;
+}
+
+static int bluesleep_remove(struct platform_device *pdev)
+{
+       pr_debug("%s", __func__);
+       kfree(bsi);
+       return 0;
+}
+
+
+static int bluesleep_resume(struct platform_device *pdev)
+{
+
+       pr_debug("%s", __func__);
+       if (test_bit(BT_SUSPEND, &flags)) {
+       
+               if ((bsi->uport != NULL) && (bsi->has_ext_wake)) {
+                       tegra_uart_request_clock_on(bsi->uport);
+                       tegra_uart_set_mctrl(bsi->uport, TIOCM_RTS);
+                       
+
+               }
+               clear_bit(BT_SUSPEND, &flags);
+               set_bit(BT_ACTIVE, &flags);
+       }
+
+       return 0;
+}
+
+static int bluesleep_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       pr_debug("%s", __func__);
+       set_bit(BT_SUSPEND, &flags);
+       return 0;
+}
+
+static struct platform_driver bluesleep_driver = {
+       .probe = bluesleep_probe,
+       .remove = bluesleep_remove,
+       .suspend = bluesleep_suspend,
+       .resume = bluesleep_resume,
+       .driver = {
+               .name = "tibluesleep",
+               .owner = THIS_MODULE,
+       },
+};
+/**
+ * Initializes the module.
+ * @return On success, 0. On error, -1, and <code>errno</code> is set
+ * appropriately.
+ */
+static int __init bluesleep_init(void)
+{
+       int retval;
+       struct proc_dir_entry *ent;
+
+       BT_INFO("BlueSleep Mode Driver Ver %s", VERSION);
+
+       retval = platform_driver_register(&bluesleep_driver);
+       if (retval)
+               return retval;
+
+       if (bsi == NULL)
+               return 0;
+
+       flags = FLAG_RESET; /* clear all status bits */
+
+       /* Initialize spinlock. */
+       spin_lock_init(&rw_lock);
+
+       /* initialize host wake tasklet */
+       tasklet_init(&hostwake_task, bluesleep_hostwake_task, 0);
+
+       return 0;
+fail:
+       return retval;
+}
+
+/**
+ * Cleans up the module.
+ */
+
+static void __exit bluesleep_exit(void)
+{
+       if (bsi == NULL)
+               return;
+       /* assert bt wake */
+       free_irq(bsi->host_wake_irq, NULL);
+       platform_driver_unregister(&bluesleep_driver);
+
+}
+
+module_init(bluesleep_init);
+module_exit(bluesleep_exit);
+
+MODULE_DESCRIPTION("TI Bluetooth Sleep Mode Driver ver %s " VERSION);
+#ifdef MODULE_LICENSE
+MODULE_LICENSE("GPL");
+#endif
index 20097ee..582f0c7 100644 (file)
@@ -50,6 +50,7 @@ static void remove_channel_from_table(struct st_data_s *st_gdata,
                struct st_proto_s *proto)
 {
        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;
 }
 
index 46ebd50..c1c3144 100644 (file)
@@ -36,6 +36,7 @@
 #include <linux/skbuff.h>
 #include <linux/ti_wilink_st.h>
 #include <linux/module.h>
+#include <linux/serial_core.h>
 
 
 #define MAX_ST_DEVICES 3       /* Imagine 1 on each UART for now */
@@ -451,8 +452,8 @@ long st_kim_start(void *kim_data)
 
        do {
                /* platform specific enabling code here */
-               /*if (pdata->chip_enable)
-                       pdata->chip_enable();*/
+               if (pdata->chip_enable)
+                       pdata->chip_enable();
 
                /* Configure BT nShutdown to HIGH state */
                gpio_set_value(kim_gdata->nshutdown, GPIO_LOW);
@@ -508,6 +509,8 @@ long st_kim_stop(void *kim_data)
        long err = 0;
        struct kim_data_s       *kim_gdata = (struct kim_data_s *)kim_data;
        struct tty_struct       *tty = kim_gdata->core_data->tty;
+       struct ti_st_plat_data  *pdata =
+               kim_gdata->kim_pdev->dev.platform_data;
 
        INIT_COMPLETION(kim_gdata->ldisc_installed);
 
@@ -538,6 +541,9 @@ long st_kim_stop(void *kim_data)
        mdelay(1);
        gpio_set_value(kim_gdata->nshutdown, GPIO_LOW);
 
+       /* platform specific disable */
+       if (pdata->chip_disable)
+               pdata->chip_disable();
        return err;
 }
 
@@ -802,10 +808,31 @@ static int kim_remove(struct platform_device *pdev)
        return 0;
 }
 
+static unsigned long retry_suspend;
+
 int kim_suspend(struct platform_device *pdev, pm_message_t state)
 {
        struct ti_st_plat_data  *pdata = pdev->dev.platform_data;
        int ret;
+       struct kim_data_s *kim_gdata;
+        struct st_data_s *core_data;
+        kim_gdata = dev_get_drvdata(&pdev->dev);
+        core_data = kim_gdata->core_data;
+        struct uart_state *uart_state;
+        struct uart_port *uport;
+
+
+
+       if (st_ll_getstate(core_data) != ST_LL_INVALID) {
+            uart_state = core_data->tty->driver_data;
+            uport = uart_state->uart_port;          
+#ifdef CONFIG_BT_TIBLUESLEEP
+                       pr_info(" Bluesleep Start");
+                       bluesleep_start(uport);
+#endif
+
+
+    }
 
        if (pdata->suspend) {
                ret = pdata->suspend(pdev, state);
index 9e2c257..0025dde 100644 (file)
@@ -54,25 +54,31 @@ static void ll_device_want_to_sleep(struct st_data_s *st_data)
        /* communicate to platform about chip asleep */
        kim_data = st_data->kim_data;
        pdata = kim_data->kim_pdev->dev.platform_data;
+       if (pdata->chip_asleep)
+               pdata->chip_asleep();
 }
 
 static void ll_device_want_to_wakeup(struct st_data_s *st_data)
 {
+       struct kim_data_s       *kim_data = st_data->kim_data;
+       struct ti_st_plat_data  *pdata = kim_data->kim_pdev->dev.platform_data;
+
        /* diff actions in diff states */
        switch (st_data->ll_state) {
        case ST_LL_ASLEEP:
                /* communicate to platform about chip wakeup */
+               if (pdata->chip_awake)
+                       pdata->chip_awake();
+
                send_ll_cmd(st_data, LL_WAKE_UP_ACK);   /* send wake_ack */
                break;
        case ST_LL_ASLEEP_TO_AWAKE:
                /* duplicate wake_ind */
-               pr_debug("duplicate wake_ind while waiting for Wake ack");
-               send_ll_cmd(st_data, LL_WAKE_UP_ACK);   /* send wake_ack */
+               pr_err("duplicate wake_ind while waiting for Wake ack");
                break;
        case ST_LL_AWAKE:
                /* duplicate wake_ind */
-               pr_debug("duplicate wake_ind already AWAKE");
-               send_ll_cmd(st_data, LL_WAKE_UP_ACK);   /* send wake_ack */
+               pr_err("duplicate wake_ind already AWAKE");
                break;
        case ST_LL_AWAKE_TO_ASLEEP:
                /* duplicate wake_ind */
@@ -91,7 +97,12 @@ static void ll_device_want_to_wakeup(struct st_data_s *st_data)
  * enable ST LL */
 void st_ll_enable(struct st_data_s *ll)
 {
-       /* communicate to platform about chip enable */
+       struct kim_data_s      *kim_data = ll->kim_data;
+       struct ti_st_plat_data *pdata = kim_data->kim_pdev->dev.platform_data;
+        /* communicate to platform about chip enable */
+       if (pdata->chip_enable)
+               pdata->chip_enable();
+
        ll->ll_state = ST_LL_AWAKE;
 }
 
@@ -99,7 +110,12 @@ void st_ll_enable(struct st_data_s *ll)
  * disable ST LL */
 void st_ll_disable(struct st_data_s *ll)
 {
-       /* communicate to platform about chip disable */
+       struct kim_data_s      *kim_data = ll->kim_data;
+       struct ti_st_plat_data *pdata = kim_data->kim_pdev->dev.platform_data;
+        /* communicate to platform about chip disable */
+       if (pdata->chip_disable)
+               pdata->chip_disable();
+
        ll->ll_state = ST_LL_INVALID;
 }
 
@@ -111,6 +127,8 @@ void st_ll_wakeup(struct st_data_s *ll)
 
        if (likely(ll->ll_state != ST_LL_AWAKE)) {
                /* communicate to platform about chip wakeup */
+               if (pdata->chip_awake)
+                       pdata->chip_awake();
                send_ll_cmd(ll, LL_WAKE_UP_IND);        /* WAKE_IND */
                ll->ll_state = ST_LL_ASLEEP_TO_AWAKE;
        } else {
index 2e4edb1..4ebb5ad 100644 (file)
@@ -371,6 +371,8 @@ struct hci_command {
 #define LL_WAKE_UP_IND 0x32
 #define LL_WAKE_UP_ACK 0x33
 
+#define HCILL_SLEEP_MODE_OPCODE 0xFD0C
+
 /* initialize and de-init ST LL */
 long st_ll_init(struct st_data_s *);
 long st_ll_deinit(struct st_data_s *);
@@ -394,6 +396,9 @@ static inline unsigned long st_ll_getstate(struct st_data_s *ll){ return 0; }
 #endif
 unsigned long st_ll_sleep_state(struct st_data_s *, unsigned char);
 void st_ll_wakeup(struct st_data_s *);
+int bluesleep_start(struct uart_port *uport);
+void bluesleep_stop(void);
+
 
 /*
  * header information used by st_core.c for FM and GPS
@@ -444,10 +449,14 @@ struct ti_st_plat_data {
        unsigned long baud_rate;
        int (*suspend)(struct platform_device *, pm_message_t);
        int (*resume)(struct platform_device *);
-       int (*chip_enable) (struct kim_data_s *);
+/*     int (*chip_enable) (struct kim_data_s *);
        int (*chip_disable) (struct kim_data_s *);
        int (*chip_asleep) (struct kim_data_s *);
-       int (*chip_awake) (struct kim_data_s *);
+       int (*chip_awake) (struct kim_data_s *);*/
+       int (*chip_enable) (void);
+       int (*chip_disable) (void);
+       int (*chip_asleep) (void);
+       int (*chip_awake) (void);
 };
 
 #endif /* TI_WILINK_ST_H */