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.c178
1 files changed, 145 insertions, 33 deletions
diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c
index 439392b1c043..81c3c38baba1 100644
--- a/drivers/bluetooth/hci_qca.c
+++ b/drivers/bluetooth/hci_qca.c
@@ -26,6 +26,7 @@
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/of_device.h>
+#include <linux/acpi.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/serdev.h>
@@ -74,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 {
@@ -110,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 {
@@ -186,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;
};
/*
@@ -596,10 +602,12 @@ static int qca_open(struct hci_uart *hu)
if (hu->serdev) {
qcadev = serdev_device_get_drvdata(hu->serdev);
- if (qca_is_wcn399x(qcadev->btsoc_type)) {
+
+ if (qca_is_wcn399x(qcadev->btsoc_type))
hu->init_speed = qcadev->init_speed;
+
+ if (qcadev->oper_speed)
hu->oper_speed = qcadev->oper_speed;
- }
}
timer_setup(&qca->wake_retrans_timer, hci_ibs_wake_retrans_timeout, 0);
@@ -969,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))) {
@@ -1018,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;
}
@@ -1044,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);
@@ -1596,7 +1639,7 @@ static int qca_setup(struct hci_uart *hu)
set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks);
bt_dev_info(hdev, "setting up %s",
- qca_is_wcn399x(soc_type) ? "wcn399x" : "ROME");
+ qca_is_wcn399x(soc_type) ? "wcn399x" : "ROME/QCA6390");
retry:
ret = qca_power_on(hdev);
@@ -1665,10 +1708,10 @@ retry:
}
/* Setup bdaddr */
- if (qca_is_wcn399x(soc_type))
- hu->hdev->set_bdaddr = qca_set_bdaddr;
- else
+ if (soc_type == QCA_ROME)
hu->hdev->set_bdaddr = qca_set_bdaddr_rome;
+ else
+ hu->hdev->set_bdaddr = qca_set_bdaddr;
return ret;
}
@@ -1688,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 },
@@ -1699,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 },
@@ -1708,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 },
@@ -1721,6 +1765,11 @@ static const struct qca_vreg_data qca_soc_data_wcn3998 = {
.num_vregs = 4,
};
+static const struct qca_device_data qca_soc_data_qca6390 = {
+ .soc_type = QCA_QCA6390,
+ .num_vregs = 0,
+};
+
static void qca_power_shutdown(struct hci_uart *hu)
{
struct qca_serdev *qcadev;
@@ -1764,7 +1813,7 @@ static int qca_power_off(struct hci_dev *hdev)
enum qca_btsoc_type soc_type = qca_soc_type(hu);
/* Stop sending shutdown command if soc crashes. */
- if (qca_is_wcn399x(soc_type)
+ if (soc_type != QCA_ROME
&& qca->memdump_state == QCA_MEMDUMP_IDLE) {
qca_send_pre_shutdown_cmd(hdev);
usleep_range(8000, 10000);
@@ -1852,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;
@@ -1865,6 +1914,11 @@ static int qca_serdev_probe(struct serdev_device *serdev)
serdev_device_set_drvdata(serdev, qcadev);
device_property_read_string(&serdev->dev, "firmware-name",
&qcadev->firmware_name);
+ device_property_read_u32(&serdev->dev, "max-speed",
+ &qcadev->oper_speed);
+ if (!qcadev->oper_speed)
+ BT_DBG("UART will pick default operating speed");
+
if (data && qca_is_wcn399x(data->soc_type)) {
qcadev->btsoc_type = data->soc_type;
qcadev->bt_power = devm_kzalloc(&serdev->dev,
@@ -1889,18 +1943,17 @@ static int qca_serdev_probe(struct serdev_device *serdev)
return PTR_ERR(qcadev->susclk);
}
- device_property_read_u32(&serdev->dev, "max-speed",
- &qcadev->oper_speed);
- if (!qcadev->oper_speed)
- BT_DBG("UART will pick default operating speed");
-
err = hci_uart_register_device(&qcadev->serdev_hu, &qca_proto);
if (err) {
BT_ERR("wcn3990 serdev registration failed");
return err;
}
} else {
- qcadev->btsoc_type = QCA_ROME;
+ if (data)
+ qcadev->btsoc_type = data->soc_type;
+ else
+ qcadev->btsoc_type = QCA_ROME;
+
qcadev->bt_en = devm_gpiod_get_optional(&serdev->dev, "enable",
GPIOD_OUT_LOW);
if (!qcadev->bt_en) {
@@ -1930,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;
}
@@ -1951,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;
@@ -2033,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);
@@ -2044,21 +2138,39 @@ static int __maybe_unused qca_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(qca_pm_ops, qca_suspend, qca_resume);
+#ifdef CONFIG_OF
static const struct of_device_id qca_bluetooth_of_match[] = {
{ .compatible = "qcom,qca6174-bt" },
+ { .compatible = "qcom,qca6390-bt", .data = &qca_soc_data_qca6390},
+ { .compatible = "qcom,qca9377-bt" },
{ .compatible = "qcom,wcn3990-bt", .data = &qca_soc_data_wcn3990},
{ .compatible = "qcom,wcn3991-bt", .data = &qca_soc_data_wcn3991},
{ .compatible = "qcom,wcn3998-bt", .data = &qca_soc_data_wcn3998},
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, qca_bluetooth_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id qca_bluetooth_acpi_match[] = {
+ { "QCOM6390", (kernel_ulong_t)&qca_soc_data_qca6390 },
+ { "DLA16390", (kernel_ulong_t)&qca_soc_data_qca6390 },
+ { "DLB16390", (kernel_ulong_t)&qca_soc_data_qca6390 },
+ { "DLB26390", (kernel_ulong_t)&qca_soc_data_qca6390 },
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, qca_bluetooth_acpi_match);
+#endif
+
static struct serdev_device_driver qca_serdev_driver = {
.probe = qca_serdev_probe,
.remove = qca_serdev_remove,
.driver = {
.name = "hci_uart_qca",
- .of_match_table = qca_bluetooth_of_match,
+ .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,
},
};