From db46fd5f031a3b9f826d72fc1d17284b3f55282e Mon Sep 17 00:00:00 2001 From: Anjan Rao Date: Mon, 5 Mar 2012 18:11:13 +0530 Subject: TI Wl128x support of bluesleep & Wake on BT Driver changes Bug 933054 Bug 931931 Change-Id: Id84bcc1791114a50d26547de41daeb4774f6026b Signed-off-by: Anjan Rao Reviewed-on: http://git-master/r/89136 Reviewed-by: Nagarjuna Kristam Tested-by: Nagarjuna Kristam Reviewed-by: Rakesh Goyal Reviewed-by: Sachin Nikam --- drivers/bluetooth/Kconfig | 10 + drivers/bluetooth/Makefile | 1 + drivers/bluetooth/ti_bluesleep.c | 392 +++++++++++++++++++++++++++++++++++++++ drivers/misc/ti-st/st_core.c | 1 + drivers/misc/ti-st/st_kim.c | 31 +++- drivers/misc/ti-st/st_ll.c | 30 ++- include/linux/ti_wilink_st.h | 13 +- 7 files changed, 468 insertions(+), 10 deletions(-) create mode 100644 drivers/bluetooth/ti_bluesleep.c 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 /* kernel module definitions */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include /* event notifications */ +#include +#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 + * HOST_WAKE 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 errno 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 errno 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 #include +#include #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 */ -- cgit v1.2.3