Bluetooth: hci_ibs: Vote on/off 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: 377905
Change-Id: I70f269d40f007326de7a6279bfd11c4e576d251b
Signed-off-by: Ram Mohan Korukonda <rkorukon@codeaurora.org>
(cherry picked from commit d0063b6620e90266599cec711d6c5160878d47ca)
diff --git a/drivers/bluetooth/hci_ibs.c b/drivers/bluetooth/hci_ibs.c
index fb084f5..6253605 100644
--- a/drivers/bluetooth/hci_ibs.c
+++ b/drivers/bluetooth/hci_ibs.c
@@ -112,6 +112,13 @@
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_awake_rx;
+ struct work_struct ws_awake_device;
+ struct work_struct ws_rx_vote_off;
+ struct work_struct ws_tx_vote_off;
+ void *ibs_hu; /* keeps the hci_uart pointer for reference */
+
/* debug */
unsigned long ibs_sent_wacks;
unsigned long ibs_sent_slps;
@@ -242,12 +249,90 @@
return err;
}
+static void ibs_wq_awake_device(struct work_struct *work)
+{
+ struct ibs_struct *ibs = container_of(work, struct ibs_struct,
+ ws_awake_device);
+ struct hci_uart *hu = (struct hci_uart *)ibs->ibs_hu;
+
+ BT_DBG(" %p ", hu);
+
+ /* Vote for serial clock */
+ ibs_msm_serial_clock_vote(HCI_IBS_TX_VOTE_CLOCK_ON, hu);
+
+ spin_lock(&ibs->hci_ibs_lock);
+
+ /* 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);
+
+ spin_unlock(&ibs->hci_ibs_lock);
+}
+
+static void ibs_wq_awake_rx(struct work_struct *work)
+{
+ struct ibs_struct *ibs = container_of(work, struct ibs_struct,
+ ws_awake_rx);
+ struct hci_uart *hu = (struct hci_uart *)ibs->ibs_hu;
+
+ BT_DBG(" %p ", hu);
+
+ ibs_msm_serial_clock_vote(HCI_IBS_RX_VOTE_CLOCK_ON, hu);
+
+ spin_lock(&ibs->hci_ibs_lock);
+ ibs->rx_ibs_state = HCI_IBS_RX_AWAKE;
+ /* Always acknowledge device wake up,
+ * sending IBS message doesn't count as TX ON
+ */
+ if (send_hci_ibs_cmd(HCI_IBS_WAKE_ACK, hu) < 0)
+ BT_ERR("cannot acknowledge device wake up");
+
+ ibs->ibs_sent_wacks++; /* debug */
+
+ spin_unlock(&ibs->hci_ibs_lock);
+ /* actually send the packets */
+ hci_uart_tx_wakeup(hu);
+
+}
+
+static void ibs_wq_serial_rx_clock_vote_off(struct work_struct *work)
+{
+ struct ibs_struct *ibs = container_of(work, struct ibs_struct,
+ ws_rx_vote_off);
+ struct hci_uart *hu = (struct hci_uart *)ibs->ibs_hu;
+
+ BT_DBG(" %p ", hu);
+
+ ibs_msm_serial_clock_vote(HCI_IBS_RX_VOTE_CLOCK_OFF, hu);
+
+}
+
+static void ibs_wq_serial_tx_clock_vote_off(struct work_struct *work)
+{
+ struct ibs_struct *ibs = container_of(work, struct ibs_struct,
+ ws_tx_vote_off);
+ struct hci_uart *hu = (struct hci_uart *)ibs->ibs_hu;
+
+ BT_DBG(" %p ", hu);
+
+ hci_uart_tx_wakeup(hu); /* run HCI tx handling unlocked */
+
+ /* now that message queued to tty driver, vote for tty clocks off */
+ /* It is up to the tty driver to pend the clocks off until tx done. */
+ ibs_msm_serial_clock_vote(HCI_IBS_TX_VOTE_CLOCK_OFF, hu);
+
+}
+
static void hci_ibs_tx_idle_timeout(unsigned long arg)
{
struct hci_uart *hu = (struct hci_uart *) arg;
struct ibs_struct *ibs = hu->priv;
unsigned long flags;
- unsigned long vote_tx_sleep = 0;
BT_DBG("hu %p idle timeout in %lu state", hu, ibs->tx_ibs_state);
@@ -267,22 +352,11 @@
}
ibs->tx_ibs_state = HCI_IBS_TX_ASLEEP;
ibs->ibs_sent_slps++; /* debug */
- vote_tx_sleep = 1;
break;
}
- spin_unlock_irqrestore(&ibs->hci_ibs_lock, flags);
+ queue_work(ibs->workqueue, &ibs->ws_tx_vote_off);
- hci_uart_tx_wakeup(hu); /* run HCI tx handling unlocked */
-
- if (!vote_tx_sleep)
- return;
- /* now that message queued to tty driver, vote for tty clocks off */
- /* It is up to the tty driver to pend the clocks off until tx done. */
-
- spin_lock_irqsave_nested(&ibs->hci_ibs_lock,
- flags, SINGLE_DEPTH_NESTING);
- ibs_msm_serial_clock_vote(HCI_IBS_TX_VOTE_CLOCK_OFF, hu);
out:
spin_unlock_irqrestore(&ibs->hci_ibs_lock, flags);
}
@@ -336,6 +410,19 @@
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_awake_rx, ibs_wq_awake_rx);
+ INIT_WORK(&ibs->ws_awake_device, ibs_wq_awake_device);
+ INIT_WORK(&ibs->ws_rx_vote_off, ibs_wq_serial_rx_clock_vote_off);
+ INIT_WORK(&ibs->ws_tx_vote_off, ibs_wq_serial_tx_clock_vote_off);
+
+ ibs->ibs_hu = (void *)hu;
/* Assume we start with both sides asleep -- extra wakes OK */
ibs->tx_ibs_state = HCI_IBS_TX_ASLEEP;
@@ -432,6 +519,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 +552,10 @@
/* 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 */
+ queue_work(ibs->workqueue, &ibs->ws_awake_rx);
+ 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 +600,8 @@
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 */
+ queue_work(ibs->workqueue, &ibs->ws_rx_vote_off);
break;
case HCI_IBS_RX_ASLEEP:
/* deliberate fall-through */
@@ -595,20 +686,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 */
+ queue_work(ibs->workqueue, &ibs->ws_awake_device);
break;
case HCI_IBS_TX_WAKING: