aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/bluetooth/hci_qca.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/bluetooth/hci_qca.c')
-rw-r--r--drivers/bluetooth/hci_qca.c134
1 files changed, 83 insertions, 51 deletions
diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c
index 81c3c38baba1..20e1dedbc58c 100644
--- a/drivers/bluetooth/hci_qca.c
+++ b/drivers/bluetooth/hci_qca.c
@@ -46,7 +46,7 @@
#define HCI_MAX_IBS_SIZE 10
#define IBS_WAKE_RETRANS_TIMEOUT_MS 100
-#define IBS_BTSOC_TX_IDLE_TIMEOUT_MS 40
+#define IBS_BTSOC_TX_IDLE_TIMEOUT_MS 200
#define IBS_HOST_TX_IDLE_TIMEOUT_MS 2000
#define CMD_TRANS_TIMEOUT_MS 100
#define MEMDUMP_TIMEOUT_MS 8000
@@ -72,7 +72,8 @@ enum qca_flags {
QCA_DROP_VENDOR_EVENT,
QCA_SUSPENDING,
QCA_MEMDUMP_COLLECTION,
- QCA_HW_ERROR_EVENT
+ QCA_HW_ERROR_EVENT,
+ QCA_SSR_TRIGGERED
};
enum qca_capabilities {
@@ -289,25 +290,21 @@ static void serial_clock_vote(unsigned long vote, struct hci_uart *hu)
case HCI_IBS_TX_VOTE_CLOCK_ON:
qca->tx_vote = true;
qca->tx_votes_on++;
- new_vote = true;
break;
case HCI_IBS_RX_VOTE_CLOCK_ON:
qca->rx_vote = true;
qca->rx_votes_on++;
- new_vote = true;
break;
case HCI_IBS_TX_VOTE_CLOCK_OFF:
qca->tx_vote = false;
qca->tx_votes_off++;
- new_vote = qca->rx_vote | qca->tx_vote;
break;
case HCI_IBS_RX_VOTE_CLOCK_OFF:
qca->rx_vote = false;
qca->rx_votes_off++;
- new_vote = qca->rx_vote | qca->tx_vote;
break;
default:
@@ -315,6 +312,8 @@ static void serial_clock_vote(unsigned long vote, struct hci_uart *hu)
return;
}
+ new_vote = qca->rx_vote | qca->tx_vote;
+
if (new_vote != old_vote) {
if (new_vote)
__serial_clock_on(hu->tty);
@@ -474,8 +473,6 @@ static void hci_ibs_tx_idle_timeout(struct timer_list *t)
case HCI_IBS_TX_ASLEEP:
case HCI_IBS_TX_WAKING:
- /* Fall through */
-
default:
BT_ERR("Spurious timeout tx state %d", qca->tx_ibs_state);
break;
@@ -518,8 +515,6 @@ static void hci_ibs_wake_retrans_timeout(struct timer_list *t)
case HCI_IBS_TX_ASLEEP:
case HCI_IBS_TX_AWAKE:
- /* Fall through */
-
default:
BT_ERR("Spurious timeout tx state %d", qca->tx_ibs_state);
break;
@@ -837,8 +832,6 @@ static void device_woke_up(struct hci_uart *hu)
break;
case HCI_IBS_TX_ASLEEP:
- /* Fall through */
-
default:
BT_ERR("Received HCI_IBS_WAKE_ACK in tx state %d",
qca->tx_ibs_state);
@@ -862,6 +855,13 @@ static int qca_enqueue(struct hci_uart *hu, struct sk_buff *skb)
BT_DBG("hu %p qca enq skb %p tx_ibs_state %d", hu, skb,
qca->tx_ibs_state);
+ if (test_bit(QCA_SSR_TRIGGERED, &qca->flags)) {
+ /* As SSR is in progress, ignore the packets */
+ bt_dev_dbg(hu->hdev, "SSR is in progress");
+ kfree_skb(skb);
+ return 0;
+ }
+
/* Prepend skb with frame type */
memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1);
@@ -983,8 +983,11 @@ static void qca_controller_memdump(struct work_struct *work)
while ((skb = skb_dequeue(&qca->rx_memdump_q))) {
mutex_lock(&qca->hci_memdump_lock);
- /* Skip processing the received packets if timeout detected. */
- if (qca->memdump_state == QCA_MEMDUMP_TIMEOUT) {
+ /* Skip processing the received packets if timeout detected
+ * or memdump collection completed.
+ */
+ if (qca->memdump_state == QCA_MEMDUMP_TIMEOUT ||
+ qca->memdump_state == QCA_MEMDUMP_COLLECTED) {
mutex_unlock(&qca->hci_memdump_lock);
return;
}
@@ -1128,6 +1131,7 @@ static int qca_controller_memdump_event(struct hci_dev *hdev,
struct hci_uart *hu = hci_get_drvdata(hdev);
struct qca_data *qca = hu->priv;
+ set_bit(QCA_SSR_TRIGGERED, &qca->flags);
skb_queue_tail(&qca->rx_memdump_q, skb);
queue_work(qca->workqueue, &qca->ctrl_memdump_evt);
@@ -1485,9 +1489,8 @@ static void qca_hw_error(struct hci_dev *hdev, u8 code)
{
struct hci_uart *hu = hci_get_drvdata(hdev);
struct qca_data *qca = hu->priv;
- struct qca_memdump_data *qca_memdump = qca->qca_memdump;
- char *memdump_buf = NULL;
+ set_bit(QCA_SSR_TRIGGERED, &qca->flags);
set_bit(QCA_HW_ERROR_EVENT, &qca->flags);
bt_dev_info(hdev, "mem_dump_status: %d", qca->memdump_state);
@@ -1509,19 +1512,23 @@ static void qca_hw_error(struct hci_dev *hdev, u8 code)
qca_wait_for_dump_collection(hdev);
}
+ mutex_lock(&qca->hci_memdump_lock);
if (qca->memdump_state != QCA_MEMDUMP_COLLECTED) {
bt_dev_err(hu->hdev, "clearing allocated memory due to memdump timeout");
- mutex_lock(&qca->hci_memdump_lock);
- if (qca_memdump)
- memdump_buf = qca_memdump->memdump_buf_head;
- vfree(memdump_buf);
- kfree(qca_memdump);
- qca->qca_memdump = NULL;
+ if (qca->qca_memdump) {
+ vfree(qca->qca_memdump->memdump_buf_head);
+ kfree(qca->qca_memdump);
+ qca->qca_memdump = NULL;
+ }
qca->memdump_state = QCA_MEMDUMP_TIMEOUT;
cancel_delayed_work(&qca->ctrl_memdump_timeout);
- skb_queue_purge(&qca->rx_memdump_q);
- mutex_unlock(&qca->hci_memdump_lock);
+ }
+ mutex_unlock(&qca->hci_memdump_lock);
+
+ if (qca->memdump_state == QCA_MEMDUMP_TIMEOUT ||
+ qca->memdump_state == QCA_MEMDUMP_COLLECTED) {
cancel_work_sync(&qca->ctrl_memdump_evt);
+ skb_queue_purge(&qca->rx_memdump_q);
}
clear_bit(QCA_HW_ERROR_EVENT, &qca->flags);
@@ -1532,10 +1539,30 @@ static void qca_cmd_timeout(struct hci_dev *hdev)
struct hci_uart *hu = hci_get_drvdata(hdev);
struct qca_data *qca = hu->priv;
- if (qca->memdump_state == QCA_MEMDUMP_IDLE)
+ set_bit(QCA_SSR_TRIGGERED, &qca->flags);
+ if (qca->memdump_state == QCA_MEMDUMP_IDLE) {
+ set_bit(QCA_MEMDUMP_COLLECTION, &qca->flags);
qca_send_crashbuffer(hu);
- else
- bt_dev_info(hdev, "Dump collection is in process");
+ qca_wait_for_dump_collection(hdev);
+ } else if (qca->memdump_state == QCA_MEMDUMP_COLLECTING) {
+ /* Let us wait here until memory dump collected or
+ * memory dump timer expired.
+ */
+ bt_dev_info(hdev, "waiting for dump to complete");
+ qca_wait_for_dump_collection(hdev);
+ }
+
+ mutex_lock(&qca->hci_memdump_lock);
+ if (qca->memdump_state != QCA_MEMDUMP_COLLECTED) {
+ qca->memdump_state = QCA_MEMDUMP_TIMEOUT;
+ if (!test_bit(QCA_HW_ERROR_EVENT, &qca->flags)) {
+ /* Inject hw error event to reset the device
+ * and driver.
+ */
+ hci_reset_dev(hu->hdev);
+ }
+ }
+ mutex_unlock(&qca->hci_memdump_lock);
}
static int qca_wcn3990_init(struct hci_uart *hu)
@@ -1641,11 +1668,15 @@ static int qca_setup(struct hci_uart *hu)
bt_dev_info(hdev, "setting up %s",
qca_is_wcn399x(soc_type) ? "wcn399x" : "ROME/QCA6390");
+ qca->memdump_state = QCA_MEMDUMP_IDLE;
+
retry:
ret = qca_power_on(hdev);
if (ret)
return ret;
+ clear_bit(QCA_SSR_TRIGGERED, &qca->flags);
+
if (qca_is_wcn399x(soc_type)) {
set_bit(HCI_QUIRK_USE_BDADDR_PROPERTY, &hdev->quirks);
@@ -1788,9 +1819,6 @@ static void qca_power_shutdown(struct hci_uart *hu)
qca_flush(hu);
spin_unlock_irqrestore(&qca->hci_ibs_lock, flags);
- hu->hdev->hw_error = NULL;
- hu->hdev->cmd_timeout = NULL;
-
/* Non-serdev device usually is powered by external power
* and don't need additional action in driver for power down
*/
@@ -1812,6 +1840,9 @@ static int qca_power_off(struct hci_dev *hdev)
struct qca_data *qca = hu->priv;
enum qca_btsoc_type soc_type = qca_soc_type(hu);
+ hu->hdev->hw_error = NULL;
+ hu->hdev->cmd_timeout = NULL;
+
/* Stop sending shutdown command if soc crashes. */
if (soc_type != QCA_ROME
&& qca->memdump_state == QCA_MEMDUMP_IDLE) {
@@ -1819,7 +1850,6 @@ static int qca_power_off(struct hci_dev *hdev)
usleep_range(8000, 10000);
}
- qca->memdump_state = QCA_MEMDUMP_IDLE;
qca_power_shutdown(hu);
return 0;
}
@@ -1962,17 +1992,17 @@ static int qca_serdev_probe(struct serdev_device *serdev)
}
qcadev->susclk = devm_clk_get_optional(&serdev->dev, NULL);
- if (!qcadev->susclk) {
+ if (IS_ERR(qcadev->susclk)) {
dev_warn(&serdev->dev, "failed to acquire clk\n");
- } else {
- err = clk_set_rate(qcadev->susclk, SUSCLK_RATE_32KHZ);
- if (err)
- return err;
-
- err = clk_prepare_enable(qcadev->susclk);
- if (err)
- return err;
+ return PTR_ERR(qcadev->susclk);
}
+ err = clk_set_rate(qcadev->susclk, SUSCLK_RATE_32KHZ);
+ if (err)
+ return err;
+
+ err = clk_prepare_enable(qcadev->susclk);
+ if (err)
+ return err;
err = hci_uart_register_device(&qcadev->serdev_hu, &qca_proto);
if (err) {
@@ -2050,6 +2080,7 @@ static int __maybe_unused qca_suspend(struct device *dev)
struct hci_uart *hu = &qcadev->serdev_hu;
struct qca_data *qca = hu->priv;
unsigned long flags;
+ bool tx_pending = false;
int ret = 0;
u8 cmd;
@@ -2068,7 +2099,7 @@ static int __maybe_unused qca_suspend(struct device *dev)
switch (qca->tx_ibs_state) {
case HCI_IBS_TX_WAKING:
del_timer(&qca->wake_retrans_timer);
- /* Fall through */
+ fallthrough;
case HCI_IBS_TX_AWAKE:
del_timer(&qca->tx_idle_timer);
@@ -2083,8 +2114,7 @@ static int __maybe_unused qca_suspend(struct device *dev)
qca->tx_ibs_state = HCI_IBS_TX_ASLEEP;
qca->ibs_sent_slps++;
-
- qca_wq_serial_tx_clock_vote_off(&qca->ws_tx_vote_off);
+ tx_pending = true;
break;
case HCI_IBS_TX_ASLEEP:
@@ -2101,22 +2131,24 @@ static int __maybe_unused qca_suspend(struct device *dev)
if (ret < 0)
goto error;
- serdev_device_wait_until_sent(hu->serdev,
- msecs_to_jiffies(CMD_TRANS_TIMEOUT_MS));
+ if (tx_pending) {
+ serdev_device_wait_until_sent(hu->serdev,
+ msecs_to_jiffies(CMD_TRANS_TIMEOUT_MS));
+ serial_clock_vote(HCI_IBS_TX_VOTE_CLOCK_OFF, hu);
+ }
/* Wait for HCI_IBS_SLEEP_IND sent by device to indicate its Tx is going
* to sleep, so that the packet does not wake the system later.
*/
-
ret = wait_event_interruptible_timeout(qca->suspend_wait_q,
qca->rx_ibs_state == HCI_IBS_RX_ASLEEP,
msecs_to_jiffies(IBS_BTSOC_TX_IDLE_TIMEOUT_MS));
-
- if (ret > 0)
- return 0;
-
- if (ret == 0)
+ if (ret == 0) {
ret = -ETIMEDOUT;
+ goto error;
+ }
+
+ return 0;
error:
clear_bit(QCA_SUSPENDING, &qca->flags);