aboutsummaryrefslogtreecommitdiff
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.c123
1 files changed, 103 insertions, 20 deletions
diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c
index b3fd07a6f812..81c3c38baba1 100644
--- a/drivers/bluetooth/hci_qca.c
+++ b/drivers/bluetooth/hci_qca.c
@@ -75,6 +75,9 @@ enum qca_flags {
QCA_HW_ERROR_EVENT
};
+enum qca_capabilities {
+ QCA_CAP_WIDEBAND_SPEECH = BIT(0),
+};
/* HCI_IBS transmit side sleep protocol states */
enum tx_ibs_states {
@@ -111,6 +114,7 @@ struct qca_memdump_data {
char *memdump_buf_tail;
u32 current_seq_no;
u32 received_dump;
+ u32 ram_dump_size;
};
struct qca_memdump_event_hdr {
@@ -187,10 +191,11 @@ struct qca_vreg {
unsigned int load_uA;
};
-struct qca_vreg_data {
+struct qca_device_data {
enum qca_btsoc_type soc_type;
struct qca_vreg *vregs;
size_t num_vregs;
+ uint32_t capabilities;
};
/*
@@ -972,6 +977,8 @@ static void qca_controller_memdump(struct work_struct *work)
char nullBuff[QCA_DUMP_PACKET_SIZE] = { 0 };
u16 seq_no;
u32 dump_size;
+ u32 rx_size;
+ enum qca_btsoc_type soc_type = qca_soc_type(hu);
while ((skb = skb_dequeue(&qca->rx_memdump_q))) {
@@ -1021,10 +1028,12 @@ static void qca_controller_memdump(struct work_struct *work)
dump_size);
queue_delayed_work(qca->workqueue,
&qca->ctrl_memdump_timeout,
- msecs_to_jiffies(MEMDUMP_TIMEOUT_MS));
+ msecs_to_jiffies(MEMDUMP_TIMEOUT_MS)
+ );
skb_pull(skb, sizeof(dump_size));
memdump_buf = vmalloc(dump_size);
+ qca_memdump->ram_dump_size = dump_size;
qca_memdump->memdump_buf_head = memdump_buf;
qca_memdump->memdump_buf_tail = memdump_buf;
}
@@ -1047,26 +1056,57 @@ static void qca_controller_memdump(struct work_struct *work)
* the controller. In such cases let us store the dummy
* packets in the buffer.
*/
+ /* For QCA6390, controller does not lost packets but
+ * sequence number field of packat sometimes has error
+ * bits, so skip this checking for missing packet.
+ */
while ((seq_no > qca_memdump->current_seq_no + 1) &&
- seq_no != QCA_LAST_SEQUENCE_NUM) {
+ (soc_type != QCA_QCA6390) &&
+ seq_no != QCA_LAST_SEQUENCE_NUM) {
bt_dev_err(hu->hdev, "QCA controller missed packet:%d",
qca_memdump->current_seq_no);
+ rx_size = qca_memdump->received_dump;
+ rx_size += QCA_DUMP_PACKET_SIZE;
+ if (rx_size > qca_memdump->ram_dump_size) {
+ bt_dev_err(hu->hdev,
+ "QCA memdump received %d, no space for missed packet",
+ qca_memdump->received_dump);
+ break;
+ }
memcpy(memdump_buf, nullBuff, QCA_DUMP_PACKET_SIZE);
memdump_buf = memdump_buf + QCA_DUMP_PACKET_SIZE;
qca_memdump->received_dump += QCA_DUMP_PACKET_SIZE;
qca_memdump->current_seq_no++;
}
- memcpy(memdump_buf, (unsigned char *) skb->data, skb->len);
- memdump_buf = memdump_buf + skb->len;
- qca_memdump->memdump_buf_tail = memdump_buf;
- qca_memdump->current_seq_no = seq_no + 1;
- qca_memdump->received_dump += skb->len;
+ rx_size = qca_memdump->received_dump + skb->len;
+ if (rx_size <= qca_memdump->ram_dump_size) {
+ if ((seq_no != QCA_LAST_SEQUENCE_NUM) &&
+ (seq_no != qca_memdump->current_seq_no))
+ bt_dev_err(hu->hdev,
+ "QCA memdump unexpected packet %d",
+ seq_no);
+ bt_dev_dbg(hu->hdev,
+ "QCA memdump packet %d with length %d",
+ seq_no, skb->len);
+ memcpy(memdump_buf, (unsigned char *)skb->data,
+ skb->len);
+ memdump_buf = memdump_buf + skb->len;
+ qca_memdump->memdump_buf_tail = memdump_buf;
+ qca_memdump->current_seq_no = seq_no + 1;
+ qca_memdump->received_dump += skb->len;
+ } else {
+ bt_dev_err(hu->hdev,
+ "QCA memdump received %d, no space for packet %d",
+ qca_memdump->received_dump, seq_no);
+ }
qca->qca_memdump = qca_memdump;
kfree_skb(skb);
if (seq_no == QCA_LAST_SEQUENCE_NUM) {
- bt_dev_info(hu->hdev, "QCA writing crash dump of size %d bytes",
- qca_memdump->received_dump);
+ bt_dev_info(hu->hdev,
+ "QCA memdump Done, received %d, total %d",
+ qca_memdump->received_dump,
+ qca_memdump->ram_dump_size);
memdump_buf = qca_memdump->memdump_buf_head;
dev_coredumpv(&hu->serdev->dev, memdump_buf,
qca_memdump->received_dump, GFP_KERNEL);
@@ -1691,7 +1731,7 @@ static const struct hci_uart_proto qca_proto = {
.dequeue = qca_dequeue,
};
-static const struct qca_vreg_data qca_soc_data_wcn3990 = {
+static const struct qca_device_data qca_soc_data_wcn3990 = {
.soc_type = QCA_WCN3990,
.vregs = (struct qca_vreg []) {
{ "vddio", 15000 },
@@ -1702,7 +1742,7 @@ static const struct qca_vreg_data qca_soc_data_wcn3990 = {
.num_vregs = 4,
};
-static const struct qca_vreg_data qca_soc_data_wcn3991 = {
+static const struct qca_device_data qca_soc_data_wcn3991 = {
.soc_type = QCA_WCN3991,
.vregs = (struct qca_vreg []) {
{ "vddio", 15000 },
@@ -1711,9 +1751,10 @@ static const struct qca_vreg_data qca_soc_data_wcn3991 = {
{ "vddch0", 450000 },
},
.num_vregs = 4,
+ .capabilities = QCA_CAP_WIDEBAND_SPEECH,
};
-static const struct qca_vreg_data qca_soc_data_wcn3998 = {
+static const struct qca_device_data qca_soc_data_wcn3998 = {
.soc_type = QCA_WCN3998,
.vregs = (struct qca_vreg []) {
{ "vddio", 10000 },
@@ -1724,7 +1765,7 @@ static const struct qca_vreg_data qca_soc_data_wcn3998 = {
.num_vregs = 4,
};
-static const struct qca_vreg_data qca_soc_data_qca6390 = {
+static const struct qca_device_data qca_soc_data_qca6390 = {
.soc_type = QCA_QCA6390,
.num_vregs = 0,
};
@@ -1860,7 +1901,7 @@ static int qca_serdev_probe(struct serdev_device *serdev)
{
struct qca_serdev *qcadev;
struct hci_dev *hdev;
- const struct qca_vreg_data *data;
+ const struct qca_device_data *data;
int err;
bool power_ctrl_enabled = true;
@@ -1942,12 +1983,19 @@ static int qca_serdev_probe(struct serdev_device *serdev)
}
}
+ hdev = qcadev->serdev_hu.hdev;
+
if (power_ctrl_enabled) {
- hdev = qcadev->serdev_hu.hdev;
set_bit(HCI_QUIRK_NON_PERSISTENT_SETUP, &hdev->quirks);
hdev->shutdown = qca_power_off;
}
+ /* Wideband speech support must be set per driver since it can't be
+ * queried via hci.
+ */
+ if (data && (data->capabilities & QCA_CAP_WIDEBAND_SPEECH))
+ set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED, &hdev->quirks);
+
return 0;
}
@@ -1963,10 +2011,43 @@ static void qca_serdev_remove(struct serdev_device *serdev)
hci_uart_unregister_device(&qcadev->serdev_hu);
}
+static void qca_serdev_shutdown(struct device *dev)
+{
+ int ret;
+ int timeout = msecs_to_jiffies(CMD_TRANS_TIMEOUT_MS);
+ struct serdev_device *serdev = to_serdev_device(dev);
+ struct qca_serdev *qcadev = serdev_device_get_drvdata(serdev);
+ const u8 ibs_wake_cmd[] = { 0xFD };
+ const u8 edl_reset_soc_cmd[] = { 0x01, 0x00, 0xFC, 0x01, 0x05 };
+
+ if (qcadev->btsoc_type == QCA_QCA6390) {
+ serdev_device_write_flush(serdev);
+ ret = serdev_device_write_buf(serdev, ibs_wake_cmd,
+ sizeof(ibs_wake_cmd));
+ if (ret < 0) {
+ BT_ERR("QCA send IBS_WAKE_IND error: %d", ret);
+ return;
+ }
+ serdev_device_wait_until_sent(serdev, timeout);
+ usleep_range(8000, 10000);
+
+ serdev_device_write_flush(serdev);
+ ret = serdev_device_write_buf(serdev, edl_reset_soc_cmd,
+ sizeof(edl_reset_soc_cmd));
+ if (ret < 0) {
+ BT_ERR("QCA send EDL_RESET_REQ error: %d", ret);
+ return;
+ }
+ serdev_device_wait_until_sent(serdev, timeout);
+ usleep_range(8000, 10000);
+ }
+}
+
static int __maybe_unused qca_suspend(struct device *dev)
{
- struct hci_dev *hdev = container_of(dev, struct hci_dev, dev);
- struct hci_uart *hu = hci_get_drvdata(hdev);
+ 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;
int ret = 0;
@@ -2045,8 +2126,9 @@ error:
static int __maybe_unused qca_resume(struct device *dev)
{
- struct hci_dev *hdev = container_of(dev, struct hci_dev, dev);
- struct hci_uart *hu = hci_get_drvdata(hdev);
+ 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;
clear_bit(QCA_SUSPENDING, &qca->flags);
@@ -2088,6 +2170,7 @@ static struct serdev_device_driver qca_serdev_driver = {
.name = "hci_uart_qca",
.of_match_table = of_match_ptr(qca_bluetooth_of_match),
.acpi_match_table = ACPI_PTR(qca_bluetooth_acpi_match),
+ .shutdown = qca_serdev_shutdown,
.pm = &qca_pm_ops,
},
};