2022-01-31 23:05:56

by Sean Wang

[permalink] [raw]
Subject: [PATCH 1/2] Bluetooth: mt7921s: support bluetooth reset mechanism

From: Chih-Ying Chiang <[email protected]>

Implement .cmd_timeout to reset the MT7921s device via a dedicated GPIO
pin when the firmware hang or the command has no response.

Co-developed-by: Mark Chen <[email protected]>
Signed-off-by: Mark Chen <[email protected]>
Co-developed-by: Sean Wang <[email protected]>
Signed-off-by: Sean Wang <[email protected]>
Signed-off-by: Chih-Ying Chiang <[email protected]>
---
drivers/bluetooth/btmtk.h | 6 ++
drivers/bluetooth/btmtksdio.c | 113 +++++++++++++++++++++++++++++++++-
2 files changed, 118 insertions(+), 1 deletion(-)

diff --git a/drivers/bluetooth/btmtk.h b/drivers/bluetooth/btmtk.h
index fb76d9765ce0..8960a5f89d48 100644
--- a/drivers/bluetooth/btmtk.h
+++ b/drivers/bluetooth/btmtk.h
@@ -10,9 +10,15 @@
#define BTMTK_WMT_REG_WRITE 0x1
#define BTMTK_WMT_REG_READ 0x2

+#define MT7921_BTSYS_RST 0x70002610
+#define MT7921_BTSYS_RST_WITH_GPIO BIT(7)
+
#define MT7921_PINMUX_0 0x70005050
#define MT7921_PINMUX_1 0x70005054

+#define MT7921_DLSTATUS 0x7c053c10
+#define BT_DL_STATE BIT(1)
+
enum {
BTMTK_WMT_PATCH_DWNLD = 0x1,
BTMTK_WMT_TEST = 0x2,
diff --git a/drivers/bluetooth/btmtksdio.c b/drivers/bluetooth/btmtksdio.c
index 8be763ab3bf4..8e200e80d2f6 100644
--- a/drivers/bluetooth/btmtksdio.c
+++ b/drivers/bluetooth/btmtksdio.c
@@ -12,10 +12,12 @@

#include <asm/unaligned.h>
#include <linux/atomic.h>
+#include <linux/gpio/consumer.h>
#include <linux/init.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/of.h>
#include <linux/pm_runtime.h>
#include <linux/skbuff.h>

@@ -83,6 +85,7 @@ MODULE_DEVICE_TABLE(sdio, btmtksdio_table);

#define MTK_REG_CHCR 0xc
#define C_INT_CLR_CTRL BIT(1)
+#define BT_RST_DONE BIT(8)

/* CHISR have the same bits field definition with CHIER */
#define MTK_REG_CHISR 0x10
@@ -114,6 +117,7 @@ MODULE_DEVICE_TABLE(sdio, btmtksdio_table);
#define BTMTKSDIO_HW_TX_READY 2
#define BTMTKSDIO_FUNC_ENABLED 3
#define BTMTKSDIO_PATCH_ENABLED 4
+#define BTMTKSDIO_HW_RESET_ACTIVE 5

struct mtkbtsdio_hdr {
__le16 len;
@@ -133,6 +137,8 @@ struct btmtksdio_dev {
struct sk_buff *evt_skb;

const struct btmtksdio_data *data;
+
+ struct gpio_desc *reset;
};

static int mtk_hci_wmt_sync(struct hci_dev *hdev,
@@ -297,6 +303,11 @@ static u32 btmtksdio_drv_own_query_79xx(struct btmtksdio_dev *bdev)
return sdio_readl(bdev->func, MTK_REG_PD2HRM0R, NULL);
}

+static u32 btmtksdio_chcr_query(struct btmtksdio_dev *bdev)
+{
+ return sdio_readl(bdev->func, MTK_REG_CHCR, NULL);
+}
+
static int btmtksdio_fw_pmctrl(struct btmtksdio_dev *bdev)
{
u32 status;
@@ -967,6 +978,28 @@ static int btmtksdio_sco_setting(struct hci_dev *hdev)
return btmtksdio_mtk_reg_write(hdev, MT7921_PINMUX_1, val, ~0);
}

+static int btmtksdio_reset_setting(struct hci_dev *hdev)
+{
+ int err;
+ u32 val;
+
+ err = btmtksdio_mtk_reg_read(hdev, MT7921_PINMUX_1, &val);
+ if (err < 0)
+ return err;
+
+ val |= 0x20; /* set the pin (bit field 11:8) work as GPIO mode */
+ err = btmtksdio_mtk_reg_write(hdev, MT7921_PINMUX_1, val, ~0);
+ if (err < 0)
+ return err;
+
+ err = btmtksdio_mtk_reg_read(hdev, MT7921_BTSYS_RST, &val);
+ if (err < 0)
+ return err;
+
+ val |= MT7921_BTSYS_RST_WITH_GPIO;
+ return btmtksdio_mtk_reg_write(hdev, MT7921_BTSYS_RST, val, ~0);
+}
+
static int btmtksdio_setup(struct hci_dev *hdev)
{
struct btmtksdio_dev *bdev = hci_get_drvdata(hdev);
@@ -974,13 +1007,32 @@ static int btmtksdio_setup(struct hci_dev *hdev)
unsigned long long duration;
char fwname[64];
int err, dev_id;
- u32 fw_version = 0;
+ u32 fw_version = 0, val;

calltime = ktime_get();
set_bit(BTMTKSDIO_HW_TX_READY, &bdev->tx_state);

switch (bdev->data->chipid) {
case 0x7921:
+ if (test_bit(BTMTKSDIO_HW_RESET_ACTIVE, &bdev->tx_state)) {
+ err = btmtksdio_mtk_reg_read(hdev, MT7921_DLSTATUS,
+ &val);
+ if (err < 0)
+ return err;
+
+ val &= ~BT_DL_STATE;
+ err = btmtksdio_mtk_reg_write(hdev, MT7921_DLSTATUS,
+ val, ~0);
+ if (err < 0)
+ return err;
+
+ btmtksdio_fw_pmctrl(bdev);
+ msleep(20);
+ btmtksdio_drv_pmctrl(bdev);
+
+ clear_bit(BTMTKSDIO_HW_RESET_ACTIVE, &bdev->tx_state);
+ }
+
err = btmtksdio_mtk_reg_read(hdev, 0x70010200, &dev_id);
if (err < 0) {
bt_dev_err(hdev, "Failed to get device id (%d)", err);
@@ -1015,6 +1067,16 @@ static int btmtksdio_setup(struct hci_dev *hdev)
return err;
}

+ /* Enable GPIO reset mechanism */
+ if (bdev->reset) {
+ err = btmtksdio_reset_setting(hdev);
+ if (err < 0) {
+ bt_dev_err(hdev, "Failed to enable Reset setting (%d)", err);
+ devm_gpiod_put(bdev->dev, bdev->reset);
+ bdev->reset = NULL;
+ }
+ }
+
break;
case 0x7663:
case 0x7668:
@@ -1111,6 +1173,47 @@ static int btmtksdio_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
return 0;
}

+static void btmtksdio_cmd_timeout(struct hci_dev *hdev)
+{
+ struct btmtksdio_dev *bdev = hci_get_drvdata(hdev);
+ u32 status;
+ int err;
+
+ if (!bdev->reset || bdev->data->chipid != 0x7921)
+ return;
+
+ pm_runtime_get_sync(bdev->dev);
+
+ if (test_and_set_bit(BTMTKSDIO_HW_RESET_ACTIVE, &bdev->tx_state))
+ return;
+
+ sdio_claim_host(bdev->func);
+
+ sdio_writel(bdev->func, C_INT_EN_CLR, MTK_REG_CHLPCR, NULL);
+ skb_queue_purge(&bdev->txq);
+ cancel_work_sync(&bdev->txrx_work);
+
+ gpiod_set_value_cansleep(bdev->reset, 1);
+ msleep(100);
+ gpiod_set_value_cansleep(bdev->reset, 0);
+
+ err = readx_poll_timeout(btmtksdio_chcr_query, bdev, status,
+ status & BT_RST_DONE, 100000, 2000000);
+ if (err < 0) {
+ bt_dev_err(hdev, "Failed to reset (%d)", err);
+ goto err;
+ }
+
+ clear_bit(BTMTKSDIO_PATCH_ENABLED, &bdev->tx_state);
+err:
+ sdio_release_host(bdev->func);
+
+ pm_runtime_put_noidle(bdev->dev);
+ pm_runtime_disable(bdev->dev);
+
+ hci_reset_dev(hdev);
+}
+
static bool btmtksdio_sdio_wakeup(struct hci_dev *hdev)
{
struct btmtksdio_dev *bdev = hci_get_drvdata(hdev);
@@ -1172,6 +1275,7 @@ static int btmtksdio_probe(struct sdio_func *func,

hdev->open = btmtksdio_open;
hdev->close = btmtksdio_close;
+ hdev->cmd_timeout = btmtksdio_cmd_timeout;
hdev->flush = btmtksdio_flush;
hdev->setup = btmtksdio_setup;
hdev->shutdown = btmtksdio_shutdown;
@@ -1216,6 +1320,13 @@ static int btmtksdio_probe(struct sdio_func *func,
if (err)
bt_dev_err(hdev, "failed to initialize device wakeup");

+ bdev->dev->of_node = of_find_compatible_node(NULL, NULL,
+ "mediatek,mt7921s-bluetooth");
+ bdev->reset = devm_gpiod_get_optional(bdev->dev, "reset",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(bdev->reset))
+ err = PTR_ERR(bdev->reset);
+
return err;
}

--
2.25.1


2022-01-31 23:12:04

by bluez.test.bot

[permalink] [raw]
Subject: RE: [1/2] Bluetooth: mt7921s: support bluetooth reset mechanism

This is automated email and please do not reply to this email!

Dear submitter,

Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=609706

---Test result---

Test Summary:
CheckPatch PASS 3.77 seconds
GitLint PASS 2.08 seconds
SubjectPrefix PASS 1.71 seconds
BuildKernel PASS 30.47 seconds
BuildKernel32 PASS 26.94 seconds
Incremental Build with patchesPASS 42.88 seconds
TestRunner: Setup PASS 472.91 seconds
TestRunner: l2cap-tester PASS 13.67 seconds
TestRunner: bnep-tester PASS 6.19 seconds
TestRunner: mgmt-tester PASS 105.33 seconds
TestRunner: rfcomm-tester PASS 7.59 seconds
TestRunner: sco-tester PASS 7.75 seconds
TestRunner: smp-tester PASS 7.58 seconds
TestRunner: userchan-tester PASS 6.45 seconds



---
Regards,
Linux Bluetooth