summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnjan Rao <anjan.rao@ti.com>2012-03-05 18:11:13 +0530
committerRohan Somvanshi <rsomvanshi@nvidia.com>2012-03-15 08:43:42 -0700
commitdb46fd5f031a3b9f826d72fc1d17284b3f55282e (patch)
treecd937ce99b7f01160c2665fccb67973d72d59a96
parentd748841ab04d0cb829e426cd0301cd4de48a3983 (diff)
TI Wl128x support of bluesleep & Wake on BT Driver changes
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>
-rw-r--r--drivers/bluetooth/Kconfig10
-rw-r--r--drivers/bluetooth/Makefile1
-rw-r--r--drivers/bluetooth/ti_bluesleep.c392
-rw-r--r--drivers/misc/ti-st/st_core.c1
-rw-r--r--drivers/misc/ti-st/st_kim.c31
-rw-r--r--drivers/misc/ti-st/st_ll.c30
-rw-r--r--include/linux/ti_wilink_st.h13
7 files changed, 468 insertions, 10 deletions
diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig
index adf975635488..4411cb035784 100644
--- a/drivers/bluetooth/Kconfig
+++ b/drivers/bluetooth/Kconfig
@@ -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
diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile
index 4c2f9d0051be..1742f0acd2f1 100644
--- a/drivers/bluetooth/Makefile
+++ b/drivers/bluetooth/Makefile
@@ -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
index 000000000000..4a7c503f2149
--- /dev/null
+++ b/drivers/bluetooth/ti_bluesleep.c
@@ -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
diff --git a/drivers/misc/ti-st/st_core.c b/drivers/misc/ti-st/st_core.c
index dde6d57dea55..67026f87dcd9 100644
--- a/drivers/misc/ti-st/st_core.c
+++ b/drivers/misc/ti-st/st_core.c
@@ -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;
}
diff --git a/drivers/misc/ti-st/st_kim.c b/drivers/misc/ti-st/st_kim.c
index 154cee0e5afc..5996b934abf0 100644
--- a/drivers/misc/ti-st/st_kim.c
+++ b/drivers/misc/ti-st/st_kim.c
@@ -35,6 +35,7 @@
#include <linux/skbuff.h>
#include <linux/ti_wilink_st.h>
+#include <linux/serial_core.h>
#define MAX_ST_DEVICES 3 /* Imagine 1 on each UART for now */
@@ -450,8 +451,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);
@@ -516,6 +517,8 @@ long st_kim_stop(void *kim_data)
{
long err = 0;
struct kim_data_s *kim_gdata = (struct kim_data_s *)kim_data;
+ struct ti_st_plat_data *pdata =
+ kim_gdata->kim_pdev->dev.platform_data;
INIT_COMPLETION(kim_gdata->ldisc_installed);
@@ -543,6 +546,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;
}
@@ -777,10 +783,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);
diff --git a/drivers/misc/ti-st/st_ll.c b/drivers/misc/ti-st/st_ll.c
index 9e2c257f65fe..0025dde6a6d9 100644
--- a/drivers/misc/ti-st/st_ll.c
+++ b/drivers/misc/ti-st/st_ll.c
@@ -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 {
diff --git a/include/linux/ti_wilink_st.h b/include/linux/ti_wilink_st.h
index 2e4edb1b1b7f..4ebb5adf494d 100644
--- a/include/linux/ti_wilink_st.h
+++ b/include/linux/ti_wilink_st.h
@@ -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 */