in hci_qca.c [2186:2298]
static int __maybe_unused qca_suspend(struct device *dev)
{
struct serdev_device *serdev = to_serdev_device(dev);
struct qca_serdev *qcadev = serdev_device_get_drvdata(serdev);
struct hci_uart *hu = &qcadev->serdev_hu;
struct qca_data *qca = hu->priv;
unsigned long flags;
bool tx_pending = false;
int ret = 0;
u8 cmd;
u32 wait_timeout = 0;
set_bit(QCA_SUSPENDING, &qca->flags);
/* if BT SoC is running with default firmware then it does not
* support in-band sleep
*/
if (test_bit(QCA_ROM_FW, &qca->flags))
return 0;
/* During SSR after memory dump collection, controller will be
* powered off and then powered on.If controller is powered off
* during SSR then we should wait until SSR is completed.
*/
if (test_bit(QCA_BT_OFF, &qca->flags) &&
!test_bit(QCA_SSR_TRIGGERED, &qca->flags))
return 0;
if (test_bit(QCA_IBS_DISABLED, &qca->flags) ||
test_bit(QCA_SSR_TRIGGERED, &qca->flags)) {
wait_timeout = test_bit(QCA_SSR_TRIGGERED, &qca->flags) ?
IBS_DISABLE_SSR_TIMEOUT_MS :
FW_DOWNLOAD_TIMEOUT_MS;
/* QCA_IBS_DISABLED flag is set to true, During FW download
* and during memory dump collection. It is reset to false,
* After FW download complete.
*/
wait_on_bit_timeout(&qca->flags, QCA_IBS_DISABLED,
TASK_UNINTERRUPTIBLE, msecs_to_jiffies(wait_timeout));
if (test_bit(QCA_IBS_DISABLED, &qca->flags)) {
bt_dev_err(hu->hdev, "SSR or FW download time out");
ret = -ETIMEDOUT;
goto error;
}
}
cancel_work_sync(&qca->ws_awake_device);
cancel_work_sync(&qca->ws_awake_rx);
spin_lock_irqsave_nested(&qca->hci_ibs_lock,
flags, SINGLE_DEPTH_NESTING);
switch (qca->tx_ibs_state) {
case HCI_IBS_TX_WAKING:
del_timer(&qca->wake_retrans_timer);
fallthrough;
case HCI_IBS_TX_AWAKE:
del_timer(&qca->tx_idle_timer);
serdev_device_write_flush(hu->serdev);
cmd = HCI_IBS_SLEEP_IND;
ret = serdev_device_write_buf(hu->serdev, &cmd, sizeof(cmd));
if (ret < 0) {
BT_ERR("Failed to send SLEEP to device");
break;
}
qca->tx_ibs_state = HCI_IBS_TX_ASLEEP;
qca->ibs_sent_slps++;
tx_pending = true;
break;
case HCI_IBS_TX_ASLEEP:
break;
default:
BT_ERR("Spurious tx state %d", qca->tx_ibs_state);
ret = -EINVAL;
break;
}
spin_unlock_irqrestore(&qca->hci_ibs_lock, flags);
if (ret < 0)
goto error;
if (tx_pending) {
serdev_device_wait_until_sent(hu->serdev,
msecs_to_jiffies(CMD_TRANS_TIMEOUT_MS));
serial_clock_vote(HCI_IBS_TX_VOTE_CLOCK_OFF, hu);
}
/* Wait for HCI_IBS_SLEEP_IND sent by device to indicate its Tx is going
* to sleep, so that the packet does not wake the system later.
*/
ret = wait_event_interruptible_timeout(qca->suspend_wait_q,
qca->rx_ibs_state == HCI_IBS_RX_ASLEEP,
msecs_to_jiffies(IBS_BTSOC_TX_IDLE_TIMEOUT_MS));
if (ret == 0) {
ret = -ETIMEDOUT;
goto error;
}
return 0;
error:
clear_bit(QCA_SUSPENDING, &qca->flags);
return ret;
}