diff options
Diffstat (limited to 'drivers/bluetooth/hci_qca.c')
-rw-r--r-- | drivers/bluetooth/hci_qca.c | 73 |
1 files changed, 71 insertions, 2 deletions
diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c index 9d273cdde563..9a5c9c1f9484 100644 --- a/drivers/bluetooth/hci_qca.c +++ b/drivers/bluetooth/hci_qca.c @@ -17,6 +17,7 @@ #include <linux/kernel.h> #include <linux/clk.h> +#include <linux/completion.h> #include <linux/debugfs.h> #include <linux/delay.h> #include <linux/device.h> @@ -53,6 +54,7 @@ enum qca_flags { QCA_IBS_ENABLED, + QCA_DROP_VENDOR_EVENT, }; /* HCI_IBS transmit side sleep protocol states */ @@ -97,6 +99,7 @@ struct qca_data { struct work_struct ws_rx_vote_off; struct work_struct ws_tx_vote_off; unsigned long flags; + struct completion drop_ev_comp; /* For debugging purpose */ u64 ibs_sent_wacks; @@ -156,6 +159,7 @@ struct qca_serdev { struct qca_power *bt_power; u32 init_speed; u32 oper_speed; + const char *firmware_name; }; static int qca_power_setup(struct hci_uart *hu, bool on); @@ -177,6 +181,17 @@ static enum qca_btsoc_type qca_soc_type(struct hci_uart *hu) return soc_type; } +static const char *qca_get_firmware_name(struct hci_uart *hu) +{ + if (hu->serdev) { + struct qca_serdev *qsd = serdev_device_get_drvdata(hu->serdev); + + return qsd->firmware_name; + } else { + return NULL; + } +} + static void __serial_clock_on(struct tty_struct *tty) { /* TODO: Some chipset requires to enable UART clock on client @@ -478,6 +493,7 @@ static int qca_open(struct hci_uart *hu) INIT_WORK(&qca->ws_tx_vote_off, qca_wq_serial_tx_clock_vote_off); qca->hu = hu; + init_completion(&qca->drop_ev_comp); /* Assume we start with both sides asleep -- extra wakes OK */ qca->tx_ibs_state = HCI_IBS_TX_ASLEEP; @@ -872,6 +888,35 @@ static int qca_recv_acl_data(struct hci_dev *hdev, struct sk_buff *skb) return hci_recv_frame(hdev, skb); } +static int qca_recv_event(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct hci_uart *hu = hci_get_drvdata(hdev); + struct qca_data *qca = hu->priv; + + if (test_bit(QCA_DROP_VENDOR_EVENT, &qca->flags)) { + struct hci_event_hdr *hdr = (void *)skb->data; + + /* For the WCN3990 the vendor command for a baudrate change + * isn't sent as synchronous HCI command, because the + * controller sends the corresponding vendor event with the + * new baudrate. The event is received and properly decoded + * after changing the baudrate of the host port. It needs to + * be dropped, otherwise it can be misinterpreted as + * response to a later firmware download command (also a + * vendor command). + */ + + if (hdr->evt == HCI_EV_VENDOR) + complete(&qca->drop_ev_comp); + + kfree(skb); + + return 0; + } + + return hci_recv_frame(hdev, skb); +} + #define QCA_IBS_SLEEP_IND_EVENT \ .type = HCI_IBS_SLEEP_IND, \ .hlen = 0, \ @@ -896,7 +941,7 @@ static int qca_recv_acl_data(struct hci_dev *hdev, struct sk_buff *skb) static const struct h4_recv_pkt qca_recv_pkts[] = { { H4_RECV_ACL, .recv = qca_recv_acl_data }, { H4_RECV_SCO, .recv = hci_recv_frame }, - { H4_RECV_EVENT, .recv = hci_recv_frame }, + { H4_RECV_EVENT, .recv = qca_recv_event }, { QCA_IBS_WAKE_IND_EVENT, .recv = qca_ibs_wake_ind }, { QCA_IBS_WAKE_ACK_EVENT, .recv = qca_ibs_wake_ack }, { QCA_IBS_SLEEP_IND_EVENT, .recv = qca_ibs_sleep_ind }, @@ -1091,6 +1136,7 @@ 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; + struct qca_data *qca = hu->priv; int ret = 0; if (speed_type == QCA_INIT_SPEED) { @@ -1110,6 +1156,11 @@ static int qca_set_speed(struct hci_uart *hu, enum qca_speed_type speed_type) if (qca_is_wcn399x(soc_type)) hci_uart_set_flow_control(hu, true); + if (soc_type == QCA_WCN3990) { + reinit_completion(&qca->drop_ev_comp); + set_bit(QCA_DROP_VENDOR_EVENT, &qca->flags); + } + 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); @@ -1121,6 +1172,20 @@ static int qca_set_speed(struct hci_uart *hu, enum qca_speed_type speed_type) error: if (qca_is_wcn399x(soc_type)) hci_uart_set_flow_control(hu, false); + + if (soc_type == QCA_WCN3990) { + /* Wait for the controller to send the vendor event + * for the baudrate change command. + */ + if (!wait_for_completion_timeout(&qca->drop_ev_comp, + msecs_to_jiffies(100))) { + bt_dev_err(hu->hdev, + "Failed to change controller baudrate\n"); + ret = -ETIMEDOUT; + } + + clear_bit(QCA_DROP_VENDOR_EVENT, &qca->flags); + } } return ret; @@ -1182,6 +1247,7 @@ static int qca_setup(struct hci_uart *hu) struct qca_data *qca = hu->priv; unsigned int speed, qca_baudrate = QCA_BAUDRATE_115200; enum qca_btsoc_type soc_type = qca_soc_type(hu); + const char *firmware_name = qca_get_firmware_name(hu); int ret; int soc_ver = 0; @@ -1232,7 +1298,8 @@ static int qca_setup(struct hci_uart *hu) bt_dev_info(hdev, "QCA controller version 0x%08x", soc_ver); /* Setup patch / NVM configurations */ - ret = qca_uart_setup(hdev, qca_baudrate, soc_type, soc_ver); + ret = qca_uart_setup(hdev, qca_baudrate, soc_type, soc_ver, + firmware_name); if (!ret) { set_bit(QCA_IBS_ENABLED, &qca->flags); qca_debugfs_init(hdev); @@ -1426,6 +1493,8 @@ static int qca_serdev_probe(struct serdev_device *serdev) qcadev->serdev_hu.serdev = serdev; data = of_device_get_match_data(&serdev->dev); serdev_device_set_drvdata(serdev, qcadev); + device_property_read_string(&serdev->dev, "firmware-name", + &qcadev->firmware_name); if (data && qca_is_wcn399x(data->soc_type)) { qcadev->btsoc_type = data->soc_type; qcadev->bt_power = devm_kzalloc(&serdev->dev, |