summaryrefslogtreecommitdiff
path: root/drivers/misc/bluedroid_pm.c
diff options
context:
space:
mode:
authorNagarjuna Kristam <nkristam@nvidia.com>2012-12-27 11:58:04 +0530
committerBitan Biswas <bbiswas@nvidia.com>2013-12-02 01:05:07 -0800
commitf23cf4bd753b5769cf73aba48d54867073d436b6 (patch)
tree62ea8a34d2648f060282f142c96b93c950e5c824 /drivers/misc/bluedroid_pm.c
parent73ed42bd6cee234042d3971120aeaec2b540ae73 (diff)
misc: bluedroid_pm: check BT TX and RX status before wake_lock release
bluedroid stack, updates only TX busy status through proc interface. When BT HID/PAN-U connection are active, high probability of data is RX only and no activity on Tx path. So, dis-allow suspend when either of BT TX or BT RX is active. Bug 1381466 Change-Id: Ib174b7d95d173c34e41ce393f93fda5fa204f217 Signed-off-by: Nagarjuna Kristam <nkristam@nvidia.com> Reviewed-on: http://git-master/r/299894 (cherry picked from commit 912b388337c2b6703ddaabd95250afac95fee303) Reviewed-on: http://git-master/r/336465 GVS: Gerrit_Virtual_Submit Reviewed-by: Bitan Biswas <bbiswas@nvidia.com>
Diffstat (limited to 'drivers/misc/bluedroid_pm.c')
-rw-r--r--drivers/misc/bluedroid_pm.c131
1 files changed, 105 insertions, 26 deletions
diff --git a/drivers/misc/bluedroid_pm.c b/drivers/misc/bluedroid_pm.c
index c6f33bd7646e..534e7197bc1b 100644
--- a/drivers/misc/bluedroid_pm.c
+++ b/drivers/misc/bluedroid_pm.c
@@ -38,8 +38,29 @@
#include <linux/pm_qos.h>
#include <linux/bluedroid_pm.h>
#include <linux/delay.h>
+#include <linux/timer.h>
#define PROC_DIR "bluetooth/sleep"
+
+/* 5 seconds of Min CPU configurations during resume */
+#define DEFAULT_RESUME_CPU_TIMEOUT 5000000
+
+#define TX_TIMER_INTERVAL 5
+
+/* Macro to enable or disable debug logging */
+/* #define BLUEDROID_PM_DBG */
+#ifndef BLUEDROID_PM_DBG
+#define BDP_DBG(fmt, ...) pr_debug("%s: " fmt, __func__, ##__VA_ARGS__)
+#else
+#define BDP_DBG(fmt, ...) pr_warn("%s: " fmt, __func__, ##__VA_ARGS__)
+#endif
+
+#define BDP_WARN(fmt, ...) pr_warn("%s: " fmt, __func__, ##__VA_ARGS__)
+#define BDP_ERR(fmt, ...) pr_err("%s: " fmt, __func__, ##__VA_ARGS__)
+
+/* status flags for bluedroid_pm_driver */
+#define BT_WAKE 0x01
+
struct bluedroid_pm_data {
int gpio_reset;
int gpio_shutdown;
@@ -47,6 +68,7 @@ struct bluedroid_pm_data {
int ext_wake;
int is_blocked;
int resume_min_frequency;
+ unsigned long flags;
unsigned host_wake_irq;
struct regulator *vdd_3v3;
struct regulator *vdd_1v8;
@@ -61,12 +83,59 @@ static bool bluedroid_pm_blocked = 1;
static int create_bt_proc_interface(void *drv_data);
static void remove_bt_proc_interface(void);
+static DEFINE_MUTEX(bt_wlan_sync);
+
+void bt_wlan_lock(void)
+{
+ mutex_lock(&bt_wlan_sync);
+}
+EXPORT_SYMBOL(bt_wlan_lock);
+
+void bt_wlan_unlock(void)
+{
+ mutex_unlock(&bt_wlan_sync);
+}
+EXPORT_SYMBOL(bt_wlan_unlock);
+
+/** bluedroid_m busy timer */
+static void bluedroid_pm_timer_expire(unsigned long data);
+static DEFINE_TIMER(bluedroid_pm_timer, bluedroid_pm_timer_expire, 0, 0);
+
static irqreturn_t bluedroid_pm_hostwake_isr(int irq, void *dev_id)
{
/* schedule a tasklet to handle the change in the host wake line */
return IRQ_HANDLED;
}
+/**
+ * Handles bluedroid_pm busy timer expiration.
+ * @param data: bluedroid_pm strcuture.
+ */
+static void bluedroid_pm_timer_expire(unsigned long data)
+{
+ struct bluedroid_pm_data *bluedroid_pm =
+ (struct bluedroid_pm_data *)data;
+
+ /*
+ * if bluedroid_pm data is NULL or timer is deleted with TX busy.
+ * return from the function.
+ */
+ if (!bluedroid_pm || test_bit(BT_WAKE, &bluedroid_pm->flags))
+ return;
+
+ if (!gpio_get_value(bluedroid_pm->host_wake)) {
+ /* BT can sleep */
+ BDP_DBG("Tx and Rx are idle, BT sleeping");
+ gpio_set_value(bluedroid_pm->ext_wake, 0);
+ wake_unlock(&bluedroid_pm->wake_lock);
+ } else {
+ /* BT Rx is busy, Reset Timer */
+ BDP_DBG("Rx is busy, restarting the timer");
+ mod_timer(&bluedroid_pm_timer,
+ jiffies + (TX_TIMER_INTERVAL * HZ));
+ }
+}
+
static int bluedroid_pm_rfkill_set_power(void *data, bool blocked)
{
struct bluedroid_pm_data *bluedroid_pm = data;
@@ -157,12 +226,12 @@ static int bluedroid_pm_probe(struct platform_device *pdev)
bluedroid_pm->gpio_reset = res->start;
ret = gpio_request(bluedroid_pm->gpio_reset, "reset_gpio");
if (ret) {
- pr_err("%s: Failed to get reset gpio\n", __func__);
+ BDP_ERR("Failed to get reset gpio\n");
goto free_res;
}
gpio_direction_output(bluedroid_pm->gpio_reset, enable);
} else {
- pr_debug("%s: Reset gpio not registered.\n", __func__);
+ BDP_DBG("Reset gpio not registered.\n");
bluedroid_pm->gpio_reset = 0;
}
@@ -173,12 +242,12 @@ static int bluedroid_pm_probe(struct platform_device *pdev)
ret = gpio_request(bluedroid_pm->gpio_shutdown,
"shutdown_gpio");
if (ret) {
- pr_err("%s: Failed to get shutdown gpio\n", __func__);
+ BDP_ERR("Failed to get shutdown gpio\n");
goto free_res;
}
gpio_direction_output(bluedroid_pm->gpio_shutdown, enable);
} else {
- pr_debug("%s: shutdown gpio not registered\n", __func__);
+ BDP_DBG("shutdown gpio not registered\n");
bluedroid_pm->gpio_shutdown = 0;
}
@@ -214,31 +283,31 @@ static int bluedroid_pm_probe(struct platform_device *pdev)
bluedroid_pm->host_wake = res->start;
ret = gpio_request(bluedroid_pm->host_wake, "bt_host_wake");
if (ret) {
- pr_err("%s: Failed to get host_wake gpio\n", __func__);
+ BDP_ERR("Failed to get host_wake gpio\n");
goto free_res;
}
/* configure host_wake as input */
gpio_direction_input(bluedroid_pm->host_wake);
} else {
- pr_debug("%s: gpio_host_wake not registered\n", __func__);
+ BDP_DBG("gpio_host_wake not registered\n");
bluedroid_pm->host_wake = 0;
}
res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "host_wake");
if (res) {
- pr_err("%s : found host_wake irq\n", __func__);
+ BDP_DBG("found host_wake irq\n");
bluedroid_pm->host_wake_irq = res->start;
ret = request_irq(bluedroid_pm->host_wake_irq,
bluedroid_pm_hostwake_isr,
IRQF_DISABLED | IRQF_TRIGGER_RISING,
"bluetooth hostwake", bluedroid_pm);
if (ret) {
- pr_err("%s: Failed to get host_wake irq\n", __func__);
+ BDP_ERR("Failed to get host_wake irq\n");
goto free_res;
}
} else {
- pr_debug("%s: host_wake not registered\n", __func__);
+ BDP_DBG("host_wake not registered\n");
bluedroid_pm->host_wake_irq = 0;
}
@@ -248,20 +317,24 @@ static int bluedroid_pm_probe(struct platform_device *pdev)
bluedroid_pm->ext_wake = res->start;
ret = gpio_request(bluedroid_pm->ext_wake, "bt_ext_wake");
if (ret) {
- pr_err("%s: Failed to get ext_wake gpio\n", __func__);
+ BDP_ERR("Failed to get ext_wake gpio\n");
goto free_res;
}
/* configure ext_wake as output mode*/
gpio_direction_output(bluedroid_pm->ext_wake, 1);
if (create_bt_proc_interface(bluedroid_pm)) {
- pr_err("%s: Failed to create proc interface", __func__);
+ BDP_ERR("Failed to create proc interface");
goto free_res;
}
/* initialize wake lock */
wake_lock_init(&bluedroid_pm->wake_lock, WAKE_LOCK_SUSPEND,
"bluedroid_pm");
+ /* Initialize timer */
+ init_timer(&bluedroid_pm_timer);
+ bluedroid_pm_timer.function = bluedroid_pm_timer_expire;
+ bluedroid_pm_timer.data = bluedroid_pm;
} else {
- pr_debug("%s: gpio_ext_wake not registered\n", __func__);
+ BDP_DBG("gpio_ext_wake not registered\n");
bluedroid_pm->ext_wake = 0;
}
@@ -271,7 +344,7 @@ static int bluedroid_pm_probe(struct platform_device *pdev)
pdata->resume_min_frequency;
platform_set_drvdata(pdev, bluedroid_pm);
- pr_debug("RFKILL BT driver successfully registered");
+ BDP_DBG("driver successfully registered");
return 0;
free_res:
@@ -308,6 +381,7 @@ static int bluedroid_pm_remove(struct platform_device *pdev)
wake_lock_destroy(&bluedroid_pm->wake_lock);
gpio_free(bluedroid_pm->ext_wake);
remove_bt_proc_interface();
+ del_timer(&bluedroid_pm_timer);
}
if (bluedroid_pm->gpio_reset || bluedroid_pm->gpio_shutdown ||
bluedroid_pm->vdd_1v8 || bluedroid_pm->vdd_3v3) {
@@ -387,19 +461,24 @@ static ssize_t lpm_write_proc(struct file *file, const char __user *buffer,
if (!bluedroid_pm->is_blocked) {
if (buf[0] == '0') {
- if (bluedroid_pm->resume_min_frequency)
- pm_qos_update_request(&bluedroid_pm->
- resume_cpu_freq_req,
- PM_QOS_DEFAULT_VALUE);
- gpio_set_value(bluedroid_pm->ext_wake, 0);
- wake_unlock(&bluedroid_pm->wake_lock);
+ if (!gpio_get_value(bluedroid_pm->host_wake)) {
+ /* BT can sleep */
+ BDP_DBG("Tx and Rx are idle, BT sleeping");
+ gpio_set_value(bluedroid_pm->ext_wake, 0);
+ wake_unlock(&bluedroid_pm->wake_lock);
+ } else {
+ /* Reset Timer */
+ BDP_DBG("Rx is busy, restarting the timer");
+ mod_timer(&bluedroid_pm_timer,
+ jiffies + (TX_TIMER_INTERVAL * HZ));
+ }
+ clear_bit(BT_WAKE, &bluedroid_pm->flags);
} else if (buf[0] == '1') {
- if (bluedroid_pm->resume_min_frequency)
- pm_qos_update_request(&bluedroid_pm->
- resume_cpu_freq_req,
- bluedroid_pm->resume_min_frequency);
+ BDP_DBG("Tx is busy, wake_lock taken, delete timer");
gpio_set_value(bluedroid_pm->ext_wake, 1);
wake_lock(&bluedroid_pm->wake_lock);
+ del_timer(&bluedroid_pm_timer);
+ set_bit(BT_WAKE, &bluedroid_pm->flags);
} else {
kfree(buf);
return -EINVAL;
@@ -430,20 +509,20 @@ static int create_bt_proc_interface(void *drv_data)
proc_bt_dir = proc_mkdir("bluetooth", NULL);
if (proc_bt_dir == NULL) {
- pr_err("Unable to create /proc/bluetooth directory");
+ BDP_ERR("Unable to create /proc/bluetooth directory");
return -ENOMEM;
}
bluetooth_sleep_dir = proc_mkdir("sleep", proc_bt_dir);
if (proc_bt_dir == NULL) {
- pr_err("Unable to create /proc/bluetooth directory");
+ BDP_ERR("Unable to create /proc/bluetooth directory");
return -ENOMEM;
}
/* Creating read/write "btwake" entry */
ent = proc_create_data("lpm", 0622, bluetooth_sleep_dir, &lpm_fops, drv_data);
if (ent == NULL) {
- pr_err("Unable to create /proc/%s/btwake entry", PROC_DIR);
+ BDP_ERR("Unable to create /proc/%s/btwake entry", PROC_DIR);
retval = -ENOMEM;
goto fail;
}