Loading drivers/bluetooth/hci_ath.c +208 −28 Original line number Diff line number Diff line Loading @@ -5,7 +5,7 @@ * power management protocol extension to H4 to support AR300x Bluetooth Chip. * * Copyright (c) 2009-2010 Atheros Communications Inc. * Copyright (c) 2012-2013 The Linux Foundation. All rights reserved. * Copyright (c) 2012-2014 The Linux Foundation. All rights reserved. * * Acknowledgements: * This file is based on hci_h4.c, which was written Loading Loading @@ -39,13 +39,20 @@ #include <linux/platform_device.h> #include <linux/gpio.h> #include <linux/of_gpio.h> #include <linux/proc_fs.h> #include <net/bluetooth/bluetooth.h> #include <net/bluetooth/hci_core.h> #include "hci_uart.h" #ifdef CONFIG_SERIAL_MSM_HS #include <mach/msm_serial_hs.h> #endif static int enableuartsleep = 1; module_param(enableuartsleep, int, 0644); MODULE_PARM_DESC(enableuartsleep, "Enable Atheros Sleep Protocol"); unsigned int enableuartsleep = 1; module_param(enableuartsleep, uint, 0644); /* * Global variables */ Loading @@ -59,8 +66,11 @@ static struct of_device_id bluesleep_match_table[] = { /** Global state flags */ static unsigned long flags; /** Tasklet to respond to change in hostwake line */ static struct tasklet_struct hostwake_task; /** To Check LPM is enabled */ static bool is_lpm_enabled; /** Workqueue to respond to change in hostwake line */ static void wakeup_host_work(struct work_struct *work); /** Transmission timer */ static void bluesleep_tx_timer_expire(unsigned long data); Loading @@ -69,6 +79,8 @@ static DEFINE_TIMER(tx_timer, bluesleep_tx_timer_expire, 0, 0); /** Lock for state transitions */ static spinlock_t rw_lock; #define PROC_DIR "bluetooth/sleep" #define POLARITY_LOW 0 #define POLARITY_HIGH 1 Loading @@ -77,8 +89,11 @@ struct bluesleep_info { unsigned ext_wake; /* wake up device */ unsigned host_wake_irq; int irq_polarity; struct uart_port *uport; }; struct work_struct ws_sleep; /* 1 second timeout */ #define TX_TIMER_INTERVAL 1 Loading @@ -98,9 +113,22 @@ struct ath_struct { struct work_struct ctxtsw; }; static void hostwake_interrupt(unsigned long data) static void hsuart_serial_clock_on(struct uart_port *port) { BT_INFO(" wakeup host\n"); BT_DBG(""); if (port) msm_hs_request_clock_on(port); else BT_INFO("Uart has not voted for Clock ON"); } static void hsuart_serial_clock_off(struct uart_port *port) { BT_DBG(""); if (port) msm_hs_request_clock_off(port); else BT_INFO("Uart has not voted for Clock OFF"); } static void modify_timer_task(void) Loading @@ -112,34 +140,46 @@ static void modify_timer_task(void) } static int ath_wakeup_ar3k(struct tty_struct *tty) static int ath_wakeup_ar3k(void) { int status = 0; if (test_bit(BT_TXEXPIRED, &flags)) { BT_INFO("wakeup device\n"); hsuart_serial_clock_on(bsi->uport); BT_DBG("wakeup device\n"); gpio_set_value(bsi->ext_wake, 0); msleep(20); gpio_set_value(bsi->ext_wake, 1); } if (!is_lpm_enabled) modify_timer_task(); return status; } static void wakeup_host_work(struct work_struct *work) { BT_DBG("wake up host"); if (test_bit(BT_SLEEPENABLE, &flags)) { if (test_bit(BT_TXEXPIRED, &flags)) hsuart_serial_clock_on(bsi->uport); } if (!is_lpm_enabled) modify_timer_task(); } static void ath_hci_uart_work(struct work_struct *work) { int status; struct ath_struct *ath; struct hci_uart *hu; struct tty_struct *tty; ath = container_of(work, struct ath_struct, ctxtsw); hu = ath->hu; tty = hu->tty; /* verify and wake up controller */ if (test_bit(BT_SLEEPENABLE, &flags)) status = ath_wakeup_ar3k(tty); status = ath_wakeup_ar3k(); /* Ready to send Data */ clear_bit(HCI_UART_SENDING, &hu->tx_state); hci_uart_tx_wakeup(hu); Loading @@ -147,8 +187,11 @@ static void ath_hci_uart_work(struct work_struct *work) static irqreturn_t bluesleep_hostwake_isr(int irq, void *dev_id) { /* schedule a tasklet to handle the change in the host wake line */ tasklet_schedule(&hostwake_task); /* schedule a work to global shared workqueue to handle * the change in the host wake line */ schedule_work(&ws_sleep); return IRQ_HANDLED; } Loading Loading @@ -202,9 +245,6 @@ static int ath_bluesleep_gpio_config(int on) tx_timer.function = bluesleep_tx_timer_expire; tx_timer.data = 0; /* initialize host wake tasklet */ tasklet_init(&hostwake_task, hostwake_interrupt, 0); if (bsi->irq_polarity == POLARITY_LOW) { ret = request_irq(bsi->host_wake_irq, bluesleep_hostwake_isr, IRQF_DISABLED | IRQF_TRIGGER_FALLING, Loading Loading @@ -239,29 +279,81 @@ gpio_config_failed: return ret; } static int ath_lpm_start(void) { BT_DBG("Start LPM mode"); if (!bsi) { BT_ERR("HCIATH3K bluesleep info does not exist"); return -EIO; } bsi->uport = msm_hs_get_uart_port(0); if (!bsi->uport) { BT_ERR("UART Port is not available"); return -ENODEV; } INIT_WORK(&ws_sleep, wakeup_host_work); if (ath_bluesleep_gpio_config(1) < 0) { BT_ERR("HCIATH3K GPIO Config failed"); return -EIO; } return 0; } static int ath_lpm_stop(void) { BT_DBG("Stop LPM mode"); cancel_work_sync(&ws_sleep); if (bsi) { bsi->uport = NULL; ath_bluesleep_gpio_config(0); } return 0; } /* Initialize protocol */ static int ath_open(struct hci_uart *hu) { struct ath_struct *ath; struct uart_state *state; BT_DBG("hu %p, bsi %p", hu, bsi); if (!bsi) return -EIO; if (ath_bluesleep_gpio_config(1) < 0) { BT_ERR("HCIATH3K GPIO Config failed"); if (!bsi) { BT_ERR("HCIATH3K bluesleep info does not exist"); return -EIO; } ath = kzalloc(sizeof(*ath), GFP_KERNEL); if (!ath) ath = kzalloc(sizeof(*ath), GFP_ATOMIC); if (!ath) { BT_ERR("HCIATH3K Memory not enough to init driver"); return -ENOMEM; } skb_queue_head_init(&ath->txq); hu->priv = ath; ath->hu = hu; state = hu->tty->driver_data; if (!state) { BT_ERR("HCIATH3K tty driver data does not exist"); return -ENXIO; } bsi->uport = state->uart_port; if (ath_bluesleep_gpio_config(1) < 0) { BT_ERR("HCIATH3K GPIO Config failed"); hu->priv = NULL; kfree(ath); return -EIO; } ath->cur_sleep = enableuartsleep; if (ath->cur_sleep == 1) { Loading @@ -269,7 +361,7 @@ static int ath_open(struct hci_uart *hu) modify_timer_task(); } INIT_WORK(&ath->ctxtsw, ath_hci_uart_work); INIT_WORK(&ws_sleep, wakeup_host_work); return 0; } Loading @@ -296,12 +388,15 @@ static int ath_close(struct hci_uart *hu) cancel_work_sync(&ath->ctxtsw); hu->priv = NULL; kfree(ath); cancel_work_sync(&ws_sleep); if (bsi) ath_bluesleep_gpio_config(0); hu->priv = NULL; bsi->uport = NULL; kfree(ath); return 0; } Loading Loading @@ -390,11 +485,13 @@ static int ath_recv(struct hci_uart *hu, void *data, int count) static void bluesleep_tx_timer_expire(unsigned long data) { if (!test_bit(BT_SLEEPENABLE, &flags)) return; BT_INFO("Tx timer expired\n"); set_bit(BT_TXEXPIRED, &flags); hsuart_serial_clock_off(bsi->uport); } static struct hci_uart_proto athp = { Loading @@ -407,6 +504,88 @@ static struct hci_uart_proto athp = { .flush = ath_flush, }; static int lpm_enabled; static int bluesleep_lpm_set(const char *val, const struct kernel_param *kp) { int ret; ret = param_set_int(val, kp); if (ret) { BT_ERR("HCIATH3K: lpm enable parameter set failed"); return ret; } BT_DBG("lpm : %d", lpm_enabled); if ((lpm_enabled == 0) && is_lpm_enabled) { ath_lpm_stop(); clear_bit(BT_SLEEPENABLE, &flags); is_lpm_enabled = false; } else if ((lpm_enabled == 1) && !is_lpm_enabled) { if (ath_lpm_start() < 0) { BT_ERR("HCIATH3K LPM mode failed"); return -EIO; } set_bit(BT_SLEEPENABLE, &flags); is_lpm_enabled = true; } else { BT_ERR("HCIATH3K invalid lpm value"); return -EINVAL; } return 0; } static struct kernel_param_ops bluesleep_lpm_ops = { .set = bluesleep_lpm_set, .get = param_get_int, }; module_param_cb(ath_lpm, &bluesleep_lpm_ops, &lpm_enabled, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(ath_lpm, "Enable Atheros LPM sleep Protocol"); static int lpm_btwrite; static int bluesleep_lpm_btwrite(const char *val, const struct kernel_param *kp) { int ret; ret = param_set_int(val, kp); if (ret) { BT_ERR("HCIATH3K: lpm btwrite parameter set failed"); return ret; } BT_DBG("btwrite : %d", lpm_btwrite); if (is_lpm_enabled) { if (lpm_btwrite == 0) { /*Setting TXEXPIRED bit to make it compatible with current solution*/ set_bit(BT_TXEXPIRED, &flags); hsuart_serial_clock_off(bsi->uport); } else if (lpm_btwrite == 1) { ath_wakeup_ar3k(); clear_bit(BT_TXEXPIRED, &flags); } else { BT_ERR("HCIATH3K invalid btwrite value"); return -EINVAL; } } return 0; } static struct kernel_param_ops bluesleep_lpm_btwrite_ops = { .set = bluesleep_lpm_btwrite, .get = param_get_int, }; module_param_cb(ath_btwrite, &bluesleep_lpm_btwrite_ops, &lpm_btwrite, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(ath_lpm, "Assert/Deassert the sleep"); static int bluesleep_populate_dt_pinfo(struct platform_device *pdev) { Loading Loading @@ -545,5 +724,6 @@ int __init ath_init(void) int __exit ath_deinit(void) { platform_driver_unregister(&bluesleep_driver); return hci_uart_unregister_proto(&athp); } Loading
drivers/bluetooth/hci_ath.c +208 −28 Original line number Diff line number Diff line Loading @@ -5,7 +5,7 @@ * power management protocol extension to H4 to support AR300x Bluetooth Chip. * * Copyright (c) 2009-2010 Atheros Communications Inc. * Copyright (c) 2012-2013 The Linux Foundation. All rights reserved. * Copyright (c) 2012-2014 The Linux Foundation. All rights reserved. * * Acknowledgements: * This file is based on hci_h4.c, which was written Loading Loading @@ -39,13 +39,20 @@ #include <linux/platform_device.h> #include <linux/gpio.h> #include <linux/of_gpio.h> #include <linux/proc_fs.h> #include <net/bluetooth/bluetooth.h> #include <net/bluetooth/hci_core.h> #include "hci_uart.h" #ifdef CONFIG_SERIAL_MSM_HS #include <mach/msm_serial_hs.h> #endif static int enableuartsleep = 1; module_param(enableuartsleep, int, 0644); MODULE_PARM_DESC(enableuartsleep, "Enable Atheros Sleep Protocol"); unsigned int enableuartsleep = 1; module_param(enableuartsleep, uint, 0644); /* * Global variables */ Loading @@ -59,8 +66,11 @@ static struct of_device_id bluesleep_match_table[] = { /** Global state flags */ static unsigned long flags; /** Tasklet to respond to change in hostwake line */ static struct tasklet_struct hostwake_task; /** To Check LPM is enabled */ static bool is_lpm_enabled; /** Workqueue to respond to change in hostwake line */ static void wakeup_host_work(struct work_struct *work); /** Transmission timer */ static void bluesleep_tx_timer_expire(unsigned long data); Loading @@ -69,6 +79,8 @@ static DEFINE_TIMER(tx_timer, bluesleep_tx_timer_expire, 0, 0); /** Lock for state transitions */ static spinlock_t rw_lock; #define PROC_DIR "bluetooth/sleep" #define POLARITY_LOW 0 #define POLARITY_HIGH 1 Loading @@ -77,8 +89,11 @@ struct bluesleep_info { unsigned ext_wake; /* wake up device */ unsigned host_wake_irq; int irq_polarity; struct uart_port *uport; }; struct work_struct ws_sleep; /* 1 second timeout */ #define TX_TIMER_INTERVAL 1 Loading @@ -98,9 +113,22 @@ struct ath_struct { struct work_struct ctxtsw; }; static void hostwake_interrupt(unsigned long data) static void hsuart_serial_clock_on(struct uart_port *port) { BT_INFO(" wakeup host\n"); BT_DBG(""); if (port) msm_hs_request_clock_on(port); else BT_INFO("Uart has not voted for Clock ON"); } static void hsuart_serial_clock_off(struct uart_port *port) { BT_DBG(""); if (port) msm_hs_request_clock_off(port); else BT_INFO("Uart has not voted for Clock OFF"); } static void modify_timer_task(void) Loading @@ -112,34 +140,46 @@ static void modify_timer_task(void) } static int ath_wakeup_ar3k(struct tty_struct *tty) static int ath_wakeup_ar3k(void) { int status = 0; if (test_bit(BT_TXEXPIRED, &flags)) { BT_INFO("wakeup device\n"); hsuart_serial_clock_on(bsi->uport); BT_DBG("wakeup device\n"); gpio_set_value(bsi->ext_wake, 0); msleep(20); gpio_set_value(bsi->ext_wake, 1); } if (!is_lpm_enabled) modify_timer_task(); return status; } static void wakeup_host_work(struct work_struct *work) { BT_DBG("wake up host"); if (test_bit(BT_SLEEPENABLE, &flags)) { if (test_bit(BT_TXEXPIRED, &flags)) hsuart_serial_clock_on(bsi->uport); } if (!is_lpm_enabled) modify_timer_task(); } static void ath_hci_uart_work(struct work_struct *work) { int status; struct ath_struct *ath; struct hci_uart *hu; struct tty_struct *tty; ath = container_of(work, struct ath_struct, ctxtsw); hu = ath->hu; tty = hu->tty; /* verify and wake up controller */ if (test_bit(BT_SLEEPENABLE, &flags)) status = ath_wakeup_ar3k(tty); status = ath_wakeup_ar3k(); /* Ready to send Data */ clear_bit(HCI_UART_SENDING, &hu->tx_state); hci_uart_tx_wakeup(hu); Loading @@ -147,8 +187,11 @@ static void ath_hci_uart_work(struct work_struct *work) static irqreturn_t bluesleep_hostwake_isr(int irq, void *dev_id) { /* schedule a tasklet to handle the change in the host wake line */ tasklet_schedule(&hostwake_task); /* schedule a work to global shared workqueue to handle * the change in the host wake line */ schedule_work(&ws_sleep); return IRQ_HANDLED; } Loading Loading @@ -202,9 +245,6 @@ static int ath_bluesleep_gpio_config(int on) tx_timer.function = bluesleep_tx_timer_expire; tx_timer.data = 0; /* initialize host wake tasklet */ tasklet_init(&hostwake_task, hostwake_interrupt, 0); if (bsi->irq_polarity == POLARITY_LOW) { ret = request_irq(bsi->host_wake_irq, bluesleep_hostwake_isr, IRQF_DISABLED | IRQF_TRIGGER_FALLING, Loading Loading @@ -239,29 +279,81 @@ gpio_config_failed: return ret; } static int ath_lpm_start(void) { BT_DBG("Start LPM mode"); if (!bsi) { BT_ERR("HCIATH3K bluesleep info does not exist"); return -EIO; } bsi->uport = msm_hs_get_uart_port(0); if (!bsi->uport) { BT_ERR("UART Port is not available"); return -ENODEV; } INIT_WORK(&ws_sleep, wakeup_host_work); if (ath_bluesleep_gpio_config(1) < 0) { BT_ERR("HCIATH3K GPIO Config failed"); return -EIO; } return 0; } static int ath_lpm_stop(void) { BT_DBG("Stop LPM mode"); cancel_work_sync(&ws_sleep); if (bsi) { bsi->uport = NULL; ath_bluesleep_gpio_config(0); } return 0; } /* Initialize protocol */ static int ath_open(struct hci_uart *hu) { struct ath_struct *ath; struct uart_state *state; BT_DBG("hu %p, bsi %p", hu, bsi); if (!bsi) return -EIO; if (ath_bluesleep_gpio_config(1) < 0) { BT_ERR("HCIATH3K GPIO Config failed"); if (!bsi) { BT_ERR("HCIATH3K bluesleep info does not exist"); return -EIO; } ath = kzalloc(sizeof(*ath), GFP_KERNEL); if (!ath) ath = kzalloc(sizeof(*ath), GFP_ATOMIC); if (!ath) { BT_ERR("HCIATH3K Memory not enough to init driver"); return -ENOMEM; } skb_queue_head_init(&ath->txq); hu->priv = ath; ath->hu = hu; state = hu->tty->driver_data; if (!state) { BT_ERR("HCIATH3K tty driver data does not exist"); return -ENXIO; } bsi->uport = state->uart_port; if (ath_bluesleep_gpio_config(1) < 0) { BT_ERR("HCIATH3K GPIO Config failed"); hu->priv = NULL; kfree(ath); return -EIO; } ath->cur_sleep = enableuartsleep; if (ath->cur_sleep == 1) { Loading @@ -269,7 +361,7 @@ static int ath_open(struct hci_uart *hu) modify_timer_task(); } INIT_WORK(&ath->ctxtsw, ath_hci_uart_work); INIT_WORK(&ws_sleep, wakeup_host_work); return 0; } Loading @@ -296,12 +388,15 @@ static int ath_close(struct hci_uart *hu) cancel_work_sync(&ath->ctxtsw); hu->priv = NULL; kfree(ath); cancel_work_sync(&ws_sleep); if (bsi) ath_bluesleep_gpio_config(0); hu->priv = NULL; bsi->uport = NULL; kfree(ath); return 0; } Loading Loading @@ -390,11 +485,13 @@ static int ath_recv(struct hci_uart *hu, void *data, int count) static void bluesleep_tx_timer_expire(unsigned long data) { if (!test_bit(BT_SLEEPENABLE, &flags)) return; BT_INFO("Tx timer expired\n"); set_bit(BT_TXEXPIRED, &flags); hsuart_serial_clock_off(bsi->uport); } static struct hci_uart_proto athp = { Loading @@ -407,6 +504,88 @@ static struct hci_uart_proto athp = { .flush = ath_flush, }; static int lpm_enabled; static int bluesleep_lpm_set(const char *val, const struct kernel_param *kp) { int ret; ret = param_set_int(val, kp); if (ret) { BT_ERR("HCIATH3K: lpm enable parameter set failed"); return ret; } BT_DBG("lpm : %d", lpm_enabled); if ((lpm_enabled == 0) && is_lpm_enabled) { ath_lpm_stop(); clear_bit(BT_SLEEPENABLE, &flags); is_lpm_enabled = false; } else if ((lpm_enabled == 1) && !is_lpm_enabled) { if (ath_lpm_start() < 0) { BT_ERR("HCIATH3K LPM mode failed"); return -EIO; } set_bit(BT_SLEEPENABLE, &flags); is_lpm_enabled = true; } else { BT_ERR("HCIATH3K invalid lpm value"); return -EINVAL; } return 0; } static struct kernel_param_ops bluesleep_lpm_ops = { .set = bluesleep_lpm_set, .get = param_get_int, }; module_param_cb(ath_lpm, &bluesleep_lpm_ops, &lpm_enabled, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(ath_lpm, "Enable Atheros LPM sleep Protocol"); static int lpm_btwrite; static int bluesleep_lpm_btwrite(const char *val, const struct kernel_param *kp) { int ret; ret = param_set_int(val, kp); if (ret) { BT_ERR("HCIATH3K: lpm btwrite parameter set failed"); return ret; } BT_DBG("btwrite : %d", lpm_btwrite); if (is_lpm_enabled) { if (lpm_btwrite == 0) { /*Setting TXEXPIRED bit to make it compatible with current solution*/ set_bit(BT_TXEXPIRED, &flags); hsuart_serial_clock_off(bsi->uport); } else if (lpm_btwrite == 1) { ath_wakeup_ar3k(); clear_bit(BT_TXEXPIRED, &flags); } else { BT_ERR("HCIATH3K invalid btwrite value"); return -EINVAL; } } return 0; } static struct kernel_param_ops bluesleep_lpm_btwrite_ops = { .set = bluesleep_lpm_btwrite, .get = param_get_int, }; module_param_cb(ath_btwrite, &bluesleep_lpm_btwrite_ops, &lpm_btwrite, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(ath_lpm, "Assert/Deassert the sleep"); static int bluesleep_populate_dt_pinfo(struct platform_device *pdev) { Loading Loading @@ -545,5 +724,6 @@ int __init ath_init(void) int __exit ath_deinit(void) { platform_driver_unregister(&bluesleep_driver); return hci_uart_unregister_proto(&athp); }