Bluetooth: btrtl: Ask 8821C to drop old firmware
[platform/kernel/linux-starfive.git] / drivers / bluetooth / btrtl.c
index 1ab9f27..0ac0f88 100644 (file)
@@ -56,6 +56,7 @@ struct btrtl_device_info {
        int fw_len;
        u8 *cfg_data;
        int cfg_len;
+       bool drop_fw;
 };
 
 static const struct id_table ic_id_table[] = {
@@ -537,6 +538,8 @@ struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev,
        u16 hci_rev, lmp_subver;
        u8 hci_ver;
        int ret;
+       u16 opcode;
+       u8 cmd[2];
 
        btrtl_dev = kzalloc(sizeof(*btrtl_dev), GFP_KERNEL);
        if (!btrtl_dev) {
@@ -558,6 +561,49 @@ struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev,
        hci_ver = resp->hci_ver;
        hci_rev = le16_to_cpu(resp->hci_rev);
        lmp_subver = le16_to_cpu(resp->lmp_subver);
+
+       if (resp->hci_ver == 0x8 && le16_to_cpu(resp->hci_rev) == 0x826c &&
+           resp->lmp_ver == 0x8 && le16_to_cpu(resp->lmp_subver) == 0xa99e)
+               btrtl_dev->drop_fw = true;
+
+       if (btrtl_dev->drop_fw) {
+               opcode = hci_opcode_pack(0x3f, 0x66);
+               cmd[0] = opcode & 0xff;
+               cmd[1] = opcode >> 8;
+
+               skb = bt_skb_alloc(sizeof(cmd), GFP_KERNEL);
+               if (IS_ERR(skb))
+                       goto out_free;
+
+               skb_put_data(skb, cmd, sizeof(cmd));
+               hci_skb_pkt_type(skb) = HCI_COMMAND_PKT;
+
+               hdev->send(hdev, skb);
+
+               /* Ensure the above vendor command is sent to controller and
+                * process has done.
+                */
+               msleep(200);
+
+               /* Read the local version again. Expect to have the vanilla
+                * version as cold boot.
+                */
+               skb = btrtl_read_local_version(hdev);
+               if (IS_ERR(skb)) {
+                       ret = PTR_ERR(skb);
+                       goto err_free;
+               }
+
+               resp = (struct hci_rp_read_local_version *)skb->data;
+               rtl_dev_info(hdev, "examining hci_ver=%02x hci_rev=%04x lmp_ver=%02x lmp_subver=%04x",
+                            resp->hci_ver, resp->hci_rev,
+                            resp->lmp_ver, resp->lmp_subver);
+
+               hci_ver = resp->hci_ver;
+               hci_rev = le16_to_cpu(resp->hci_rev);
+               lmp_subver = le16_to_cpu(resp->lmp_subver);
+       }
+out_free:
        kfree_skb(skb);
 
        btrtl_dev->ic_info = btrtl_match_ic(lmp_subver, hci_rev, hci_ver,