aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/bluetooth/hci_qca.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/bluetooth/hci_qca.c')
-rw-r--r--drivers/bluetooth/hci_qca.c87
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);
}