summaryrefslogtreecommitdiffstats
path: root/drivers/bluetooth
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/bluetooth')
-rw-r--r--drivers/bluetooth/hci_qca.c48
1 files changed, 39 insertions, 9 deletions
diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c
index a5c2611b8768..5cc7b163020c 100644
--- a/drivers/bluetooth/hci_qca.c
+++ b/drivers/bluetooth/hci_qca.c
@@ -50,6 +50,8 @@
#define IBS_HOST_TX_IDLE_TIMEOUT_MS 2000
#define CMD_TRANS_TIMEOUT_MS 100
#define MEMDUMP_TIMEOUT_MS 8000
+#define IBS_DISABLE_SSR_TIMEOUT_MS (MEMDUMP_TIMEOUT_MS + 1000)
+#define FW_DOWNLOAD_TIMEOUT_MS 3000
/* susclk rate */
#define SUSCLK_RATE_32KHZ 32768
@@ -68,12 +70,13 @@
#define QCA_MEMDUMP_BYTE 0xFB
enum qca_flags {
- QCA_IBS_ENABLED,
+ QCA_IBS_DISABLED,
QCA_DROP_VENDOR_EVENT,
QCA_SUSPENDING,
QCA_MEMDUMP_COLLECTION,
QCA_HW_ERROR_EVENT,
- QCA_SSR_TRIGGERED
+ QCA_SSR_TRIGGERED,
+ QCA_BT_OFF
};
enum qca_capabilities {
@@ -870,7 +873,7 @@ static int qca_enqueue(struct hci_uart *hu, struct sk_buff *skb)
* Out-Of-Band(GPIOs control) sleep is selected.
* Don't wake the device up when suspending.
*/
- if (!test_bit(QCA_IBS_ENABLED, &qca->flags) ||
+ if (test_bit(QCA_IBS_DISABLED, &qca->flags) ||
test_bit(QCA_SUSPENDING, &qca->flags)) {
skb_queue_tail(&qca->txq, skb);
spin_unlock_irqrestore(&qca->hci_ibs_lock, flags);
@@ -1015,7 +1018,7 @@ static void qca_controller_memdump(struct work_struct *work)
* the controller to send the dump is 8 seconds. let us
* start timer to handle this asynchronous activity.
*/
- clear_bit(QCA_IBS_ENABLED, &qca->flags);
+ set_bit(QCA_IBS_DISABLED, &qca->flags);
set_bit(QCA_MEMDUMP_COLLECTION, &qca->flags);
dump = (void *) skb->data;
dump_size = __le32_to_cpu(dump->dump_size);
@@ -1619,6 +1622,7 @@ static int qca_power_on(struct hci_dev *hdev)
struct hci_uart *hu = hci_get_drvdata(hdev);
enum qca_btsoc_type soc_type = qca_soc_type(hu);
struct qca_serdev *qcadev;
+ struct qca_data *qca = hu->priv;
int ret = 0;
/* Non-serdev device usually is powered by external power
@@ -1638,6 +1642,7 @@ static int qca_power_on(struct hci_dev *hdev)
}
}
+ clear_bit(QCA_BT_OFF, &qca->flags);
return ret;
}
@@ -1657,7 +1662,7 @@ static int qca_setup(struct hci_uart *hu)
return ret;
/* Patch downloading has to be done without IBS mode */
- clear_bit(QCA_IBS_ENABLED, &qca->flags);
+ set_bit(QCA_IBS_DISABLED, &qca->flags);
/* Enable controller to do both LE scan and BR/EDR inquiry
* simultaneously.
@@ -1708,7 +1713,7 @@ retry:
ret = qca_uart_setup(hdev, qca_baudrate, soc_type, soc_ver,
firmware_name);
if (!ret) {
- set_bit(QCA_IBS_ENABLED, &qca->flags);
+ clear_bit(QCA_IBS_DISABLED, &qca->flags);
qca_debugfs_init(hdev);
hu->hdev->hw_error = qca_hw_error;
hu->hdev->cmd_timeout = qca_cmd_timeout;
@@ -1816,7 +1821,7 @@ static void qca_power_shutdown(struct hci_uart *hu)
* data in skb's.
*/
spin_lock_irqsave(&qca->hci_ibs_lock, flags);
- clear_bit(QCA_IBS_ENABLED, &qca->flags);
+ set_bit(QCA_IBS_DISABLED, &qca->flags);
qca_flush(hu);
spin_unlock_irqrestore(&qca->hci_ibs_lock, flags);
@@ -1833,6 +1838,8 @@ static void qca_power_shutdown(struct hci_uart *hu)
} else if (qcadev->bt_en) {
gpiod_set_value_cansleep(qcadev->bt_en, 0);
}
+
+ set_bit(QCA_BT_OFF, &qca->flags);
}
static int qca_power_off(struct hci_dev *hdev)
@@ -2090,11 +2097,34 @@ static int __maybe_unused qca_suspend(struct device *dev)
bool tx_pending = false;
int ret = 0;
u8 cmd;
+ u32 wait_timeout = 0;
set_bit(QCA_SUSPENDING, &qca->flags);
- /* Device is downloading patch or doesn't support in-band sleep. */
- if (!test_bit(QCA_IBS_ENABLED, &qca->flags))
+ if (test_bit(QCA_BT_OFF, &qca->flags))
+ return 0;
+
+ if (test_bit(QCA_IBS_DISABLED, &qca->flags)) {
+ wait_timeout = test_bit(QCA_SSR_TRIGGERED, &qca->flags) ?
+ IBS_DISABLE_SSR_TIMEOUT_MS :
+ FW_DOWNLOAD_TIMEOUT_MS;
+
+ /* QCA_IBS_DISABLED flag is set to true, During FW download
+ * and during memory dump collection. It is reset to false,
+ * After FW download complete and after memory dump collections.
+ */
+ wait_on_bit_timeout(&qca->flags, QCA_IBS_DISABLED,
+ TASK_UNINTERRUPTIBLE, msecs_to_jiffies(wait_timeout));
+
+ if (test_bit(QCA_IBS_DISABLED, &qca->flags)) {
+ bt_dev_err(hu->hdev, "SSR or FW download time out");
+ ret = -ETIMEDOUT;
+ goto error;
+ }
+ }
+
+ /* After memory dump collection, Controller is powered off.*/
+ if (test_bit(QCA_BT_OFF, &qca->flags))
return 0;
cancel_work_sync(&qca->ws_awake_device);