The patch is workaround for hardware issue on WCN6750. On WCN6750 sometimes observed AON power source takes 100ms time to fully discharge voltage during OFF. As WCN6750 is combo chip for WLAN and BT. If any of the tech area ON is triggered during discharge phase, it fails to turn ON. To overcome this hardware issue, During BT ON, driver check for WLAN_EN pin status. If it high, it will pull BT_EN to high immediately else it will wait for 100ms assuming WLAN was just powered OFF and then BT_EN will be pulled to high. Fixes: d8f97da1b92d2 ("Bluetooth: hci_qca: Add support for QTI Bluetooth chip wcn6750") Reviewed-by: Miao-chen Chou <mcchou@xxxxxxxxxxxx> Signed-off-by: Sai Teja Aluvala <quic_saluvala@xxxxxxxxxxx> Signed-off-by: Balakrishna Godavarthi <quic_bgodavar@xxxxxxxxxxx> --- drivers/bluetooth/hci_qca.c | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c index eab34e2..c3862d1 100644 --- a/drivers/bluetooth/hci_qca.c +++ b/drivers/bluetooth/hci_qca.c @@ -219,6 +219,7 @@ struct qca_serdev { struct hci_uart serdev_hu; struct gpio_desc *bt_en; struct gpio_desc *sw_ctrl; + struct gpio_desc *wlan_en; struct clk *susclk; enum qca_btsoc_type btsoc_type; struct qca_power *bt_power; @@ -1627,12 +1628,25 @@ static int qca_regulator_init(struct hci_uart *hu) if (qcadev->bt_en) { gpiod_set_value_cansleep(qcadev->bt_en, 0); msleep(50); + } + + if (!qcadev->wlan_en || (qcadev->wlan_en && gpiod_get_value_cansleep(qcadev->wlan_en))) + gpiod_set_value_cansleep(qcadev->bt_en, 1); + + if (qcadev->wlan_en && !gpiod_get_value_cansleep(qcadev->wlan_en)) { + gpiod_set_value_cansleep(qcadev->bt_en, 0); + msleep(100); gpiod_set_value_cansleep(qcadev->bt_en, 1); - msleep(50); - if (qcadev->sw_ctrl) { - sw_ctrl_state = gpiod_get_value_cansleep(qcadev->sw_ctrl); - bt_dev_dbg(hu->hdev, "SW_CTRL is %d", sw_ctrl_state); - } + } + + if (!gpiod_get_value_cansleep(qcadev->bt_en)) + gpiod_set_value_cansleep(qcadev->bt_en, 1); + + msleep(50); + + if (qcadev->sw_ctrl) { + sw_ctrl_state = gpiod_get_value_cansleep(qcadev->sw_ctrl); + bt_dev_dbg(hu->hdev, "SW_CTRL is %d", sw_ctrl_state); } qca_set_speed(hu, QCA_INIT_SPEED); @@ -1906,8 +1920,8 @@ static void qca_power_shutdown(struct hci_uart *hu) qca_regulator_disable(qcadev); } else if (soc_type == QCA_WCN6750) { gpiod_set_value_cansleep(qcadev->bt_en, 0); - msleep(100); qca_regulator_disable(qcadev); + msleep(100); if (qcadev->sw_ctrl) { sw_ctrl_state = gpiod_get_value_cansleep(qcadev->sw_ctrl); bt_dev_dbg(hu->hdev, "SW_CTRL is %d", sw_ctrl_state); @@ -2057,6 +2071,10 @@ static int qca_serdev_probe(struct serdev_device *serdev) qcadev->bt_power->vregs_on = false; + qcadev->wlan_en = devm_gpiod_get_optional(&serdev->dev, "wlan", GPIOD_ASIS); + if (!qcadev->wlan_en && data->soc_type == QCA_WCN6750) + dev_err(&serdev->dev, "failed to acquire WL_EN gpio"); + qcadev->bt_en = devm_gpiod_get_optional(&serdev->dev, "enable", GPIOD_OUT_LOW); if (IS_ERR_OR_NULL(qcadev->bt_en) && data->soc_type == QCA_WCN6750) { -- The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project