[PATCH v4 2/2] Bluetooth: btusb: Support Bluetooth Reset for Mediatek Chip(MT7921)
mark-yw.chen at mediatek.com
mark-yw.chen at mediatek.com
Thu Jul 1 12:42:43 PDT 2021
From: "mark-yw.chen" <mark-yw.chen at mediatek.com>
When the firmware hang or command no response, driver can reset the
bluetooth mcu via USB to recovery it. The reset steps as follows.
1. Cancel USB transfer requests before reset.
2. It use speicific USB HW Register to reset Bluetooth MCU, at the
same time, the USB Endpoint0 still keep alive.
3. Poll the USB HW register until reset is completed by Endpoint0.
4. To recovery unexpected USB state and behavior during resetting the
Bluetooth MCU, the driver need to reset the USB device for MT7921.
5. After the reset is completed, the Bluetooth MCU need to re-setup,
such as download patch, power-on sequence and etc.
Signed-off-by: mark-yw.chen <mark-yw.chen at mediatek.com>
---
drivers/bluetooth/btusb.c | 127 ++++++++++++++++++++++++++++++++++++++
1 file changed, 127 insertions(+)
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index b1249b664981..122a928ebcb5 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -3076,6 +3076,17 @@ static int btusb_shutdown_intel_new(struct hci_dev *hdev)
return 0;
}
+/* UHW CR mapping */
+#define BT_MISC 0x70002510
+#define BT_SUBSYS_RST 0x70002610
+#define UDMA_INT_STA_BT 0x74000024
+#define UDMA_INT_STA_BT1 0x74000308
+#define BT_WDT_STATUS 0x740003A0
+#define EP_RST_OPT 0x74011890
+#define EP_RST_IN_OUT_OPT 0x00010001
+#define BT_RST_DONE 0x00000100
+#define BT_RESET_WAIT_MS 100
+#define BT_RESET_NUM_TRIES 10
#define FIRMWARE_MT7663 "mediatek/mt7663pr2h.bin"
#define FIRMWARE_MT7668 "mediatek/mt7668pr2h.bin"
@@ -3650,6 +3661,63 @@ static int btusb_mtk_func_query(struct hci_dev *hdev)
return status;
}
+static int btusb_mtk_uhw_reg_write(struct btusb_data *data, u32 reg, u32 val)
+{
+ struct hci_dev *hdev = data->hdev;
+ int pipe, err;
+ void *buf;
+
+ buf = kzalloc(4, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ put_unaligned_le32(val, buf);
+
+ pipe = usb_sndctrlpipe(data->udev, 0);
+ err = usb_control_msg(data->udev, pipe, 0x02,
+ 0x5E,
+ reg >> 16, reg & 0xffff,
+ buf, 4, USB_CTRL_SET_TIMEOUT);
+ if (err < 0) {
+ bt_dev_err(hdev, "Failed to write uhw reg(%d)", err);
+ goto err_free_buf;
+ }
+
+err_free_buf:
+ kfree(buf);
+
+ return err;
+}
+
+static int btusb_mtk_uhw_reg_read(struct btusb_data *data, u32 reg, u32 *val)
+{
+ struct hci_dev *hdev = data->hdev;
+ int pipe, err;
+ void *buf;
+
+ buf = kzalloc(4, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ pipe = usb_rcvctrlpipe(data->udev, 0);
+ err = usb_control_msg(data->udev, pipe, 0x01,
+ 0xDE,
+ reg >> 16, reg & 0xffff,
+ buf, 4, USB_CTRL_SET_TIMEOUT);
+ if (err < 0) {
+ bt_dev_err(hdev, "Failed to read uhw reg(%d)", err);
+ goto err_free_buf;
+ }
+
+ *val = get_unaligned_le32(buf);
+ bt_dev_dbg(hdev, "reg=%x, value=0x%08x", reg, *val);
+
+err_free_buf:
+ kfree(buf);
+
+ return err;
+}
+
static int btusb_mtk_reg_read(struct btusb_data *data, u32 reg, u32 *val)
{
int pipe, err, size = sizeof(u32);
@@ -3729,6 +3797,9 @@ static int btusb_mtk_setup(struct hci_dev *hdev)
dev_id & 0xffff, (fw_version & 0xff) + 1);
err = btusb_mtk_setup_firmware_79xx(hdev, fw_bin_name);
+ /* It's Device EndPoint Reset Option Register */
+ btusb_mtk_uhw_reg_write(data, EP_RST_OPT, EP_RST_IN_OUT_OPT);
+
/* Enable Bluetooth protocol */
param = 1;
wmt_params.op = BTMTK_WMT_FUNC_CTRL;
@@ -3852,6 +3923,61 @@ static int btusb_mtk_shutdown(struct hci_dev *hdev)
return 0;
}
+static void btusb_mtk_cmd_timeout(struct hci_dev *hdev)
+{
+ struct btusb_data *data = hci_get_drvdata(hdev);
+ u32 val;
+ int err, retry = 0;
+
+ /* It's MediaTek specific bluetooth reset mechanism via USB */
+ if (test_and_set_bit(BTUSB_HW_RESET_ACTIVE, &data->flags)) {
+ bt_dev_err(hdev, "last reset failed? Not resetting again");
+ return;
+ }
+
+ err = usb_autopm_get_interface(data->intf);
+ if (err < 0)
+ return;
+
+ btusb_stop_traffic(data);
+ usb_kill_anchored_urbs(&data->tx_anchor);
+
+ /* It's Device EndPoint Reset Option Register */
+ bt_dev_dbg(hdev, "Initiating reset mechanism via uhw");
+ btusb_mtk_uhw_reg_write(data, EP_RST_OPT, EP_RST_IN_OUT_OPT);
+ btusb_mtk_uhw_reg_read(data, BT_WDT_STATUS, &val);
+
+ /* Reset the bluetooth chip via USB interface. */
+ btusb_mtk_uhw_reg_write(data, BT_SUBSYS_RST, 1);
+ btusb_mtk_uhw_reg_write(data, UDMA_INT_STA_BT, 0x000000FF);
+ btusb_mtk_uhw_reg_read(data, UDMA_INT_STA_BT, &val);
+ btusb_mtk_uhw_reg_write(data, UDMA_INT_STA_BT1, 0x000000FF);
+ btusb_mtk_uhw_reg_read(data, UDMA_INT_STA_BT1, &val);
+ msleep(20);
+ btusb_mtk_uhw_reg_write(data, BT_SUBSYS_RST, 0);
+ btusb_mtk_uhw_reg_read(data, BT_SUBSYS_RST, &val);
+
+ /* Poll the register until reset is completed */
+ do {
+ btusb_mtk_uhw_reg_read(data, BT_MISC, &val);
+ if ((val & 0x00000100) == BT_RST_DONE) {
+ bt_dev_dbg(hdev, "Bluetooth Reset Successfully");
+ break;
+ }
+
+ bt_dev_dbg(hdev, "Polling Bluetooth Reset CR");
+ retry++;
+ msleep(BT_RESET_WAIT_MS);
+ } while (retry < BT_RESET_NUM_TRIES);
+
+ btusb_mtk_id_get(data, 0x70010200, &val);
+ bt_dev_info(hdev, "device id (%x)", val);
+
+ usb_queue_reset_device(data->intf);
+
+ clear_bit(BTUSB_HW_RESET_ACTIVE, &data->flags);
+}
+
static int btusb_recv_acl_mtk(struct hci_dev *hdev, struct sk_buff *skb)
{
struct btusb_data *data = hci_get_drvdata(hdev);
@@ -4691,6 +4817,7 @@ static int btusb_probe(struct usb_interface *intf,
hdev->setup = btusb_mtk_setup;
hdev->shutdown = btusb_mtk_shutdown;
hdev->manufacturer = 70;
+ hdev->cmd_timeout = btusb_mtk_cmd_timeout;
set_bit(HCI_QUIRK_NON_PERSISTENT_SETUP, &hdev->quirks);
data->recv_acl = btusb_recv_acl_mtk;
}
--
2.18.0
More information about the Linux-mediatek
mailing list