diff options
Diffstat (limited to 'drivers/bluetooth/hci_qca.c')
-rw-r--r-- | drivers/bluetooth/hci_qca.c | 87 |
1 files changed, 48 insertions, 39 deletions
diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c index f036c8f98ea3..5e03504c4e0c 100644 --- a/drivers/bluetooth/hci_qca.c +++ b/drivers/bluetooth/hci_qca.c @@ -60,6 +60,7 @@ #define IBS_WAKE_RETRANS_TIMEOUT_MS 100 #define IBS_TX_IDLE_TIMEOUT_MS 2000 #define BAUDRATE_SETTLE_TIMEOUT_MS 300 +#define POWER_PULSE_TRANS_TIMEOUT_MS 100 /* susclk rate */ #define SUSCLK_RATE_32KHZ 32768 @@ -770,16 +771,17 @@ static int qca_enqueue(struct hci_uart *hu, struct sk_buff *skb) /* Prepend skb with frame type */ memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1); + spin_lock_irqsave(&qca->hci_ibs_lock, flags); + /* Don't go to sleep in middle of patch download or * Out-Of-Band(GPIOs control) sleep is selected. */ if (!test_bit(STATE_IN_BAND_SLEEP_ENABLED, &qca->flags)) { skb_queue_tail(&qca->txq, skb); + spin_unlock_irqrestore(&qca->hci_ibs_lock, flags); return 0; } - spin_lock_irqsave(&qca->hci_ibs_lock, flags); - /* Act according to current state */ switch (qca->tx_ibs_state) { case HCI_IBS_TX_AWAKE: @@ -963,7 +965,6 @@ static int qca_set_baudrate(struct hci_dev *hdev, uint8_t baudrate) struct hci_uart *hu = hci_get_drvdata(hdev); struct qca_data *qca = hu->priv; struct sk_buff *skb; - struct qca_serdev *qcadev; u8 cmd[] = { 0x01, 0x48, 0xFC, 0x01, 0x00 }; if (baudrate > QCA_BAUDRATE_3200000) @@ -977,13 +978,6 @@ static int qca_set_baudrate(struct hci_dev *hdev, uint8_t baudrate) return -ENOMEM; } - /* Disabling hardware flow control is mandatory while - * sending change baudrate request to wcn3990 SoC. - */ - qcadev = serdev_device_get_drvdata(hu->serdev); - if (qcadev->btsoc_type == QCA_WCN3990) - hci_uart_set_flow_control(hu, true); - /* Assign commands to change baudrate and packet type. */ skb_put_data(skb, cmd, sizeof(cmd)); hci_skb_pkt_type(skb) = HCI_COMMAND_PKT; @@ -999,9 +993,6 @@ static int qca_set_baudrate(struct hci_dev *hdev, uint8_t baudrate) schedule_timeout(msecs_to_jiffies(BAUDRATE_SETTLE_TIMEOUT_MS)); set_current_state(TASK_RUNNING); - if (qcadev->btsoc_type == QCA_WCN3990) - hci_uart_set_flow_control(hu, false); - return 0; } @@ -1013,11 +1004,10 @@ static inline void host_set_baudrate(struct hci_uart *hu, unsigned int speed) hci_uart_set_baudrate(hu, speed); } -static int qca_send_power_pulse(struct hci_dev *hdev, u8 cmd) +static int qca_send_power_pulse(struct hci_uart *hu, u8 cmd) { - struct hci_uart *hu = hci_get_drvdata(hdev); - struct qca_data *qca = hu->priv; - struct sk_buff *skb; + int ret; + int timeout = msecs_to_jiffies(POWER_PULSE_TRANS_TIMEOUT_MS); /* These power pulses are single byte command which are sent * at required baudrate to wcn3990. On wcn3990, we have an external @@ -1029,19 +1019,17 @@ static int qca_send_power_pulse(struct hci_dev *hdev, u8 cmd) * save power. Disabling hardware flow control is mandatory while * sending power pulses to SoC. */ - bt_dev_dbg(hdev, "sending power pulse %02x to SoC", cmd); - - skb = bt_skb_alloc(sizeof(cmd), GFP_KERNEL); - if (!skb) - return -ENOMEM; + bt_dev_dbg(hu->hdev, "sending power pulse %02x to controller", cmd); + serdev_device_write_flush(hu->serdev); hci_uart_set_flow_control(hu, true); + ret = serdev_device_write_buf(hu->serdev, &cmd, sizeof(cmd)); + if (ret < 0) { + bt_dev_err(hu->hdev, "failed to send power pulse %02x", cmd); + return ret; + } - skb_put_u8(skb, cmd); - hci_skb_pkt_type(skb) = HCI_COMMAND_PKT; - - skb_queue_tail(&qca->txq, skb); - hci_uart_tx_wakeup(hu); + serdev_device_wait_until_sent(hu->serdev, timeout); /* Wait for 100 uS for SoC to settle down */ usleep_range(100, 200); @@ -1091,7 +1079,8 @@ static int qca_check_speeds(struct hci_uart *hu) static int qca_set_speed(struct hci_uart *hu, enum qca_speed_type speed_type) { unsigned int speed, qca_baudrate; - int ret; + struct qca_serdev *qcadev; + int ret = 0; if (speed_type == QCA_INIT_SPEED) { speed = qca_get_speed(hu, QCA_INIT_SPEED); @@ -1102,21 +1091,31 @@ static int qca_set_speed(struct hci_uart *hu, enum qca_speed_type speed_type) if (!speed) return 0; + /* Disable flow control for wcn3990 to deassert RTS while + * changing the baudrate of chip and host. + */ + qcadev = serdev_device_get_drvdata(hu->serdev); + if (qcadev->btsoc_type == QCA_WCN3990) + hci_uart_set_flow_control(hu, true); + qca_baudrate = qca_get_baudrate_value(speed); bt_dev_dbg(hu->hdev, "Set UART speed to %d", speed); ret = qca_set_baudrate(hu->hdev, qca_baudrate); if (ret) - return ret; + goto error; host_set_baudrate(hu, speed); + +error: + if (qcadev->btsoc_type == QCA_WCN3990) + hci_uart_set_flow_control(hu, false); } - return 0; + return ret; } static int qca_wcn3990_init(struct hci_uart *hu) { - struct hci_dev *hdev = hu->hdev; struct qca_serdev *qcadev; int ret; @@ -1139,12 +1138,12 @@ static int qca_wcn3990_init(struct hci_uart *hu) /* Forcefully enable wcn3990 to enter in to boot mode. */ host_set_baudrate(hu, 2400); - ret = qca_send_power_pulse(hdev, QCA_WCN3990_POWEROFF_PULSE); + ret = qca_send_power_pulse(hu, QCA_WCN3990_POWEROFF_PULSE); if (ret) return ret; qca_set_speed(hu, QCA_INIT_SPEED); - ret = qca_send_power_pulse(hdev, QCA_WCN3990_POWERON_PULSE); + ret = qca_send_power_pulse(hu, QCA_WCN3990_POWERON_PULSE); if (ret) return ret; @@ -1241,7 +1240,10 @@ static int qca_setup(struct hci_uart *hu) } /* Setup bdaddr */ - hu->hdev->set_bdaddr = qca_set_bdaddr_rome; + if (qcadev->btsoc_type == QCA_WCN3990) + hu->hdev->set_bdaddr = qca_set_bdaddr; + else + hu->hdev->set_bdaddr = qca_set_bdaddr_rome; return ret; } @@ -1274,13 +1276,20 @@ static const struct qca_vreg_data qca_soc_data = { static void qca_power_shutdown(struct hci_uart *hu) { - struct serdev_device *serdev = hu->serdev; - unsigned char cmd = QCA_WCN3990_POWEROFF_PULSE; + struct qca_data *qca = hu->priv; + unsigned long flags; + + /* From this point we go into power off state. But serial port is + * still open, stop queueing the IBS data and flush all the buffered + * data in skb's. + */ + spin_lock_irqsave(&qca->hci_ibs_lock, flags); + clear_bit(STATE_IN_BAND_SLEEP_ENABLED, &qca->flags); + qca_flush(hu); + spin_unlock_irqrestore(&qca->hci_ibs_lock, flags); host_set_baudrate(hu, 2400); - hci_uart_set_flow_control(hu, true); - serdev_device_write_buf(serdev, &cmd, sizeof(cmd)); - hci_uart_set_flow_control(hu, false); + qca_send_power_pulse(hu, QCA_WCN3990_POWEROFF_PULSE); qca_power_setup(hu, false); } |