Bluetooth: Voting UART clocks in non atomic context.

As per the latest clock design, we should not vote for the clocks
in the atomic context.

CRs-fixed: 363013
Change-Id: I0f855f47bc3553501a5523a1997be34890696415
Signed-off-by: Ram Mohan Korukonda <rkorukon@codeaurora.org>
diff --git a/drivers/bluetooth/hci_ibs.c b/drivers/bluetooth/hci_ibs.c
index 2a6f3f8..6845020 100644
--- a/drivers/bluetooth/hci_ibs.c
+++ b/drivers/bluetooth/hci_ibs.c
@@ -5,7 +5,7 @@
  *  protocol extension to H4.
  *
  *  Copyright (C) 2007 Texas Instruments, Inc.
- *  Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *  Copyright (c) 2010, 2012 Code Aurora Forum. All rights reserved.
  *
  *  Acknowledgements:
  *  This file is based on hci_ll.c, which was...
@@ -37,7 +37,9 @@
 #include <linux/fcntl.h>
 #include <linux/interrupt.h>
 #include <linux/ptrace.h>
+#include <linux/ftrace.h>
 #include <linux/poll.h>
+#include <linux/workqueue.h>
 
 #include <linux/slab.h>
 #include <linux/tty.h>
@@ -92,6 +94,15 @@
 	HCI_IBS_RX_VOTE_CLOCK_OFF,
 };
 
+/* HCI_IBS state for the WorkQueue */
+enum hci_ibs_wq_state_e {
+	HCI_IBS_WQ_INIT_STATE = 0,
+	HCI_IBS_WQ_TX_VOTE_OFF,
+	HCI_IBS_WQ_RX_VOTE_OFF,
+	HCI_IBS_WQ_AWAKE_RX,
+	HCI_IBS_WQ_AWAKE_DEVICE,
+};
+
 static unsigned long wake_retrans = 1;
 static unsigned long tx_idle_delay = (HZ * 2);
 
@@ -112,6 +123,11 @@
 	unsigned long rx_vote;		/* clock must be on for RX */
 	struct	timer_list tx_idle_timer;
 	struct	timer_list wake_retrans_timer;
+	struct	workqueue_struct *workqueue;
+	struct	work_struct ws_ibs;
+	unsigned long ibs_wq_state;
+	void *ibs_hu; /* keeps the hci_uart pointer for reference */
+
 	/* debug */
 	unsigned long ibs_sent_wacks;
 	unsigned long ibs_sent_slps;
@@ -242,6 +258,56 @@
 	return err;
 }
 
+static void ibs_wq(struct work_struct *work)
+{
+	unsigned long flags = 0;
+	struct ibs_struct *ibs = container_of(work, struct ibs_struct,
+						ws_ibs);
+	struct hci_uart *hu = (struct hci_uart *)ibs->ibs_hu;
+
+	BT_DBG("hu %p, ibs_wq state: %lu\n", hu, ibs->ibs_wq_state);
+
+	/* lock hci_ibs state */
+	spin_lock_irqsave(&ibs->hci_ibs_lock, flags);
+
+	switch (ibs->ibs_wq_state) {
+	case HCI_IBS_WQ_AWAKE_DEVICE:
+		/* Vote for serial clock */
+		ibs_msm_serial_clock_vote(HCI_IBS_TX_VOTE_CLOCK_ON, hu);
+
+		/* send wake indication to device */
+		if (send_hci_ibs_cmd(HCI_IBS_WAKE_IND, hu) < 0)
+			BT_ERR("cannot send WAKE to device");
+
+		ibs->ibs_sent_wakes++; /* debug */
+
+		/* start retransmit timer */
+		mod_timer(&ibs->wake_retrans_timer, jiffies + wake_retrans);
+		break;
+	case HCI_IBS_WQ_AWAKE_RX:
+		ibs_msm_serial_clock_vote(HCI_IBS_RX_VOTE_CLOCK_ON, hu);
+		ibs->rx_ibs_state = HCI_IBS_RX_AWAKE;
+
+		if (send_hci_ibs_cmd(HCI_IBS_WAKE_ACK, hu) < 0)
+			BT_ERR("cannot acknowledge device wake up");
+
+		ibs->ibs_sent_wacks++; /* debug */
+		/* actually send the packets */
+		hci_uart_tx_wakeup(hu);
+		break;
+	case HCI_IBS_WQ_RX_VOTE_OFF:
+		ibs_msm_serial_clock_vote(HCI_IBS_RX_VOTE_CLOCK_OFF, hu);
+		break;
+	case HCI_IBS_WQ_TX_VOTE_OFF:
+		ibs_msm_serial_clock_vote(HCI_IBS_TX_VOTE_CLOCK_OFF, hu);
+		break;
+	default:
+		BT_DBG("Invalid state in ibs workqueue");
+		break;
+	}
+	spin_unlock_irqrestore(&ibs->hci_ibs_lock, flags);
+}
+
 static void hci_ibs_tx_idle_timeout(unsigned long arg)
 {
 	struct hci_uart *hu = (struct hci_uart *) arg;
@@ -282,7 +348,9 @@
 
 	spin_lock_irqsave_nested(&ibs->hci_ibs_lock,
 					flags, SINGLE_DEPTH_NESTING);
-	ibs_msm_serial_clock_vote(HCI_IBS_TX_VOTE_CLOCK_OFF, hu);
+	/* vote off tx clock */
+	ibs->ibs_wq_state = HCI_IBS_WQ_TX_VOTE_OFF;
+	queue_work(ibs->workqueue, &ibs->ws_ibs);
 out:
 	spin_unlock_irqrestore(&ibs->hci_ibs_lock, flags);
 }
@@ -336,6 +404,16 @@
 	skb_queue_head_init(&ibs->txq);
 	skb_queue_head_init(&ibs->tx_wait_q);
 	spin_lock_init(&ibs->hci_ibs_lock);
+	ibs->workqueue = create_singlethread_workqueue("ibs_wq");
+	if (!ibs->workqueue) {
+		BT_ERR("IBS Workqueue not initialized properly");
+		kfree(ibs);
+		return -ENOMEM;
+	}
+
+	INIT_WORK(&ibs->ws_ibs, ibs_wq);
+	ibs->ibs_hu = (void *)hu;
+	ibs->ibs_wq_state = HCI_IBS_WQ_INIT_STATE;
 
 	/* Assume we start with both sides asleep -- extra wakes OK */
 	ibs->tx_ibs_state = HCI_IBS_TX_ASLEEP;
@@ -432,6 +510,8 @@
 	skb_queue_purge(&ibs->txq);
 	del_timer(&ibs->tx_idle_timer);
 	del_timer(&ibs->wake_retrans_timer);
+	destroy_workqueue(ibs->workqueue);
+	ibs->ibs_hu = NULL;
 
 	kfree_skb(ibs->rx_skb);
 
@@ -463,9 +543,11 @@
 		/* Make sure clock is on - we may have turned clock off since
 		 * receiving the wake up indicator
 		 */
-		ibs_msm_serial_clock_vote(HCI_IBS_RX_VOTE_CLOCK_ON, hu);
-		ibs->rx_ibs_state = HCI_IBS_RX_AWAKE;
-		/* deliberate fall-through */
+		/* awake rx clock */
+		ibs->ibs_wq_state = HCI_IBS_WQ_AWAKE_RX;
+		queue_work(ibs->workqueue, &ibs->ws_ibs);
+		spin_unlock_irqrestore(&ibs->hci_ibs_lock, flags);
+		return;
 	case HCI_IBS_RX_AWAKE:
 		/* Always acknowledge device wake up,
 		 * sending IBS message doesn't count as TX ON.
@@ -510,7 +592,9 @@
 	case HCI_IBS_RX_AWAKE:
 		/* update state */
 		ibs->rx_ibs_state = HCI_IBS_RX_ASLEEP;
-		ibs_msm_serial_clock_vote(HCI_IBS_RX_VOTE_CLOCK_OFF, hu);
+		/* vote off rx clock under workqueue */
+		ibs->ibs_wq_state = HCI_IBS_WQ_RX_VOTE_OFF;
+		queue_work(ibs->workqueue, &ibs->ws_ibs);
 		break;
 	case HCI_IBS_RX_ASLEEP:
 		/* deliberate fall-through */
@@ -595,20 +679,12 @@
 
 	case HCI_IBS_TX_ASLEEP:
 		BT_DBG("device asleep, waking up and queueing packet");
-		ibs_msm_serial_clock_vote(HCI_IBS_TX_VOTE_CLOCK_ON, hu);
 		/* save packet for later */
 		skb_queue_tail(&ibs->tx_wait_q, skb);
-		/* awake device */
-		if (send_hci_ibs_cmd(HCI_IBS_WAKE_IND, hu) < 0) {
-			BT_ERR("cannot send WAKE to device");
-			break;
-		}
-		ibs->ibs_sent_wakes++; /* debug */
-
-		/* start retransmit timer */
-		mod_timer(&ibs->wake_retrans_timer, jiffies + wake_retrans);
-
 		ibs->tx_ibs_state = HCI_IBS_TX_WAKING;
+		/* schedule a work queue to wake up device */
+		ibs->ibs_wq_state = HCI_IBS_WQ_AWAKE_DEVICE;
+		queue_work(ibs->workqueue, &ibs->ws_ibs);
 		break;
 
 	case HCI_IBS_TX_WAKING: