[ Upstream commit a2780889e247561744dd8efbd3478a1999b72ae3 ]

WCN399x chips are coex chips, it needs a VS pre shutdown
command while turning off the BT. So that chip can inform
BT is OFF to other active clients.

Signed-off-by: Harish Bandi <c-hba...@codeaurora.org>
Signed-off-by: Marcel Holtmann <mar...@holtmann.org>
Signed-off-by: Sasha Levin <sas...@kernel.org>
---
 drivers/bluetooth/btqca.c   | 21 +++++++++++++++++++++
 drivers/bluetooth/btqca.h   |  7 +++++++
 drivers/bluetooth/hci_qca.c |  3 +++
 3 files changed, 31 insertions(+)

diff --git a/drivers/bluetooth/btqca.c b/drivers/bluetooth/btqca.c
index 0ee5acb685a10..ee25e6ae1a098 100644
--- a/drivers/bluetooth/btqca.c
+++ b/drivers/bluetooth/btqca.c
@@ -99,6 +99,27 @@ static int qca_send_reset(struct hci_dev *hdev)
        return 0;
 }
 
+int qca_send_pre_shutdown_cmd(struct hci_dev *hdev)
+{
+       struct sk_buff *skb;
+       int err;
+
+       bt_dev_dbg(hdev, "QCA pre shutdown cmd");
+
+       skb = __hci_cmd_sync(hdev, QCA_PRE_SHUTDOWN_CMD, 0,
+                               NULL, HCI_INIT_TIMEOUT);
+       if (IS_ERR(skb)) {
+               err = PTR_ERR(skb);
+               bt_dev_err(hdev, "QCA preshutdown_cmd failed (%d)", err);
+               return err;
+       }
+
+       kfree_skb(skb);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(qca_send_pre_shutdown_cmd);
+
 static void qca_tlv_check_data(struct rome_config *config,
                                const struct firmware *fw)
 {
diff --git a/drivers/bluetooth/btqca.h b/drivers/bluetooth/btqca.h
index e9c9999596035..f2a9e576a86ce 100644
--- a/drivers/bluetooth/btqca.h
+++ b/drivers/bluetooth/btqca.h
@@ -13,6 +13,7 @@
 #define EDL_PATCH_TLV_REQ_CMD          (0x1E)
 #define EDL_NVM_ACCESS_SET_REQ_CMD     (0x01)
 #define MAX_SIZE_PER_TLV_SEGMENT       (243)
+#define QCA_PRE_SHUTDOWN_CMD           (0xFC08)
 
 #define EDL_CMD_REQ_RES_EVT            (0x00)
 #define EDL_PATCH_VER_RES_EVT          (0x19)
@@ -130,6 +131,7 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
                   enum qca_btsoc_type soc_type, u32 soc_ver);
 int qca_read_soc_version(struct hci_dev *hdev, u32 *soc_version);
 int qca_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr);
+int qca_send_pre_shutdown_cmd(struct hci_dev *hdev);
 static inline bool qca_is_wcn399x(enum qca_btsoc_type soc_type)
 {
        return soc_type == QCA_WCN3990 || soc_type == QCA_WCN3998;
@@ -161,4 +163,9 @@ static inline bool qca_is_wcn399x(enum qca_btsoc_type 
soc_type)
 {
        return false;
 }
+
+static inline int qca_send_pre_shutdown_cmd(struct hci_dev *hdev)
+{
+       return -EOPNOTSUPP;
+}
 #endif
diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c
index f41fb2c02e4fd..d88b024eaf566 100644
--- a/drivers/bluetooth/hci_qca.c
+++ b/drivers/bluetooth/hci_qca.c
@@ -1319,6 +1319,9 @@ static int qca_power_off(struct hci_dev *hdev)
 {
        struct hci_uart *hu = hci_get_drvdata(hdev);
 
+       /* Perform pre shutdown command */
+       qca_send_pre_shutdown_cmd(hdev);
+
        qca_power_shutdown(hu);
        return 0;
 }
-- 
2.20.1



Reply via email to