[PATCH v1 3/3] Bluetooth: hci_qca: Add support for QTI bluetooth MAPLE

From: Zijun Hu
Date: Tue Nov 02 2021 - 03:13:15 EST


From: Zijun Hu <quic_zijuhu@xxxxxxxxxxx>

Add support for MAPLE integrated within SOC, it is mounted on
a virtual tty port and powered on/off via relevant IOCTL, neither
IBS nor RAMPATCH downloading is not required.

Signed-off-by: Zijun Hu <quic_zijuhu@xxxxxxxxxxx>
---
drivers/bluetooth/btqca.c | 13 ++++++++++++-
drivers/bluetooth/btqca.h | 13 +++++++++++++
drivers/bluetooth/hci_qca.c | 47 ++++++++++++++++++++++++++++++++++++++++++++-
3 files changed, 71 insertions(+), 2 deletions(-)

diff --git a/drivers/bluetooth/btqca.c b/drivers/bluetooth/btqca.c
index be04d74037d2..b83d2ecefe5d 100644
--- a/drivers/bluetooth/btqca.c
+++ b/drivers/bluetooth/btqca.c
@@ -255,6 +255,8 @@ static void qca_tlv_check_data(struct hci_dev *hdev,
BT_DBG("TLV Type\t\t : 0x%x", type_len & 0x000000ff);
BT_DBG("Length\t\t : %d bytes", length);

+ if (qca_is_maple(soc_type))
+ break;
idx = 0;
data = tlv->data;
while (idx < length) {
@@ -552,6 +554,9 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
rom_ver = ((soc_ver & 0x00000f00) >> 0x04) | (soc_ver & 0x0000000f);

/* Download rampatch file */
+ if (qca_is_maple(soc_type))
+ goto download_nvm;
+
config.type = TLV_TYPE_PATCH;
if (qca_is_wcn399x(soc_type)) {
snprintf(config.fwname, sizeof(config.fwname),
@@ -580,6 +585,7 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
/* Give the controller some time to get ready to receive the NVM */
msleep(10);

+download_nvm:
/* Download NVM configuration */
config.type = TLV_TYPE_NVM;
if (firmware_name)
@@ -597,6 +603,9 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
else if (soc_type == QCA_QCA6390)
snprintf(config.fwname, sizeof(config.fwname),
"qca/htnv%02x.bin", rom_ver);
+ else if (qca_is_maple(soc_type))
+ snprintf(config.fwname, sizeof(config.fwname),
+ "qca/mpnv%02x.bin", rom_ver);
else if (soc_type == QCA_WCN6750)
snprintf(config.fwname, sizeof(config.fwname),
"qca/msnv%02x.bin", rom_ver);
@@ -609,6 +618,8 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
bt_dev_err(hdev, "QCA Failed to download NVM (%d)", err);
return err;
}
+ if (qca_is_maple(soc_type))
+ msleep(MAPLE_NVM_READY_DELAY_MS);

if (soc_type >= QCA_WCN3991) {
err = qca_disable_soc_logging(hdev);
@@ -637,7 +648,7 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
return err;
}

- if (soc_type == QCA_WCN3991 || soc_type == QCA_WCN6750) {
+ if (soc_type == QCA_WCN3991 || soc_type == QCA_WCN6750 || qca_is_maple(soc_type)) {
/* get fw build info */
err = qca_read_fw_build_info(hdev);
if (err < 0)
diff --git a/drivers/bluetooth/btqca.h b/drivers/bluetooth/btqca.h
index 30afa7703afd..0a5a7d1daa71 100644
--- a/drivers/bluetooth/btqca.h
+++ b/drivers/bluetooth/btqca.h
@@ -46,6 +46,8 @@

#define QCA_FW_BUILD_VER_LEN 255

+#define MAPLE_NVM_READY_DELAY_MS 1500
+#define MAPLE_POWER_CONTROL_DELAY_MS 50

enum qca_baudrate {
QCA_BAUDRATE_115200 = 0,
@@ -145,6 +147,7 @@ enum qca_btsoc_type {
QCA_WCN3991,
QCA_QCA6390,
QCA_WCN6750,
+ QCA_MAPLE,
};

#if IS_ENABLED(CONFIG_BT_QCA)
@@ -167,6 +170,11 @@ static inline bool qca_is_wcn6750(enum qca_btsoc_type soc_type)
return soc_type == QCA_WCN6750;
}

+static inline bool qca_is_maple(enum qca_btsoc_type soc_type)
+{
+ return soc_type == QCA_MAPLE;
+}
+
#else

static inline int qca_set_bdaddr_rome(struct hci_dev *hdev, const bdaddr_t *bdaddr)
@@ -204,6 +212,11 @@ static inline bool qca_is_wcn6750(enum qca_btsoc_type soc_type)
return false;
}

+static inline bool qca_is_maple(enum qca_btsoc_type soc_type)
+{
+ return false;
+}
+
static inline int qca_send_pre_shutdown_cmd(struct hci_dev *hdev)
{
return -EOPNOTSUPP;
diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c
index dd768a8ed7cb..f1d9670719c4 100644
--- a/drivers/bluetooth/hci_qca.c
+++ b/drivers/bluetooth/hci_qca.c
@@ -70,6 +70,10 @@
#define QCA_CRASHBYTE_PACKET_LEN 1096
#define QCA_MEMDUMP_BYTE 0xFB

+#ifndef IOCTL_IPC_BOOT
+#define IOCTL_IPC_BOOT 0xBE
+#endif
+
enum qca_flags {
QCA_IBS_DISABLED,
QCA_DROP_VENDOR_EVENT,
@@ -1370,6 +1374,9 @@ static unsigned int qca_get_speed(struct hci_uart *hu,
{
unsigned int speed = 0;

+ if (qca_is_maple(qca_soc_type(hu)))
+ return 0;
+
if (speed_type == QCA_INIT_SPEED) {
if (hu->init_speed)
speed = hu->init_speed;
@@ -1387,6 +1394,9 @@ static unsigned int qca_get_speed(struct hci_uart *hu,

static int qca_check_speeds(struct hci_uart *hu)
{
+ if (qca_is_maple(qca_soc_type(hu)))
+ return 0;
+
if (qca_is_wcn399x(qca_soc_type(hu)) ||
qca_is_wcn6750(qca_soc_type(hu))) {
if (!qca_get_speed(hu, QCA_INIT_SPEED) &&
@@ -1660,6 +1670,21 @@ static int qca_regulator_init(struct hci_uart *hu)
return 0;
}

+static int qca_maple_power_control(struct hci_uart *hu, bool on)
+{
+ int ret;
+ int power_arg = on ? 1 : 0;
+
+ ret = serdev_device_ioctl(hu->serdev, IOCTL_IPC_BOOT, power_arg);
+ if (ret)
+ bt_dev_err(hu->hdev, "%s: power %s failure: %d\n", __func__,
+ on ? "ON" : "OFF", ret);
+ else
+ msleep(MAPLE_POWER_CONTROL_DELAY_MS);
+
+ return ret;
+}
+
static int qca_power_on(struct hci_dev *hdev)
{
struct hci_uart *hu = hci_get_drvdata(hdev);
@@ -1677,6 +1702,8 @@ static int qca_power_on(struct hci_dev *hdev)
if (qca_is_wcn399x(soc_type) ||
qca_is_wcn6750(soc_type)) {
ret = qca_regulator_init(hu);
+ } else if (qca_is_maple(soc_type)) {
+ ret = qca_maple_power_control(hu, true);
} else {
qcadev = serdev_device_get_drvdata(hu->serdev);
if (qcadev->bt_en) {
@@ -1715,6 +1742,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_maple(soc_type) ? "maple" :
qca_is_wcn399x(soc_type) ? "wcn399x" :
(soc_type == QCA_WCN6750) ? "wcn6750" : "ROME/QCA6390");

@@ -1761,7 +1789,10 @@ static int qca_setup(struct hci_uart *hu)
ret = qca_uart_setup(hdev, qca_baudrate, soc_type, ver,
firmware_name);
if (!ret) {
- clear_bit(QCA_IBS_DISABLED, &qca->flags);
+ if (qca_is_maple(soc_type))
+ set_bit(QCA_ROM_FW, &qca->flags);
+ else
+ clear_bit(QCA_IBS_DISABLED, &qca->flags);
qca_debugfs_init(hdev);
hu->hdev->hw_error = qca_hw_error;
hu->hdev->cmd_timeout = qca_cmd_timeout;
@@ -1858,6 +1889,11 @@ static const struct qca_device_data qca_soc_data_qca6390 = {
.num_vregs = 0,
};

+static const struct qca_device_data qca_soc_data_maple = {
+ .soc_type = QCA_MAPLE,
+ .num_vregs = 0,
+};
+
static const struct qca_device_data qca_soc_data_wcn6750 = {
.soc_type = QCA_WCN6750,
.vregs = (struct qca_vreg []) {
@@ -1912,6 +1948,8 @@ static void qca_power_shutdown(struct hci_uart *hu)
sw_ctrl_state = gpiod_get_value_cansleep(qcadev->sw_ctrl);
bt_dev_dbg(hu->hdev, "SW_CTRL is %d", sw_ctrl_state);
}
+ } else if (qca_is_maple(soc_type)) {
+ qca_maple_power_control(hu, false);
} else if (qcadev->bt_en) {
gpiod_set_value_cansleep(qcadev->bt_en, 0);
}
@@ -2089,6 +2127,10 @@ static int qca_serdev_probe(struct serdev_device *serdev)
dev_warn(&serdev->dev, "failed to acquire enable gpio\n");
power_ctrl_enabled = false;
}
+ if (qca_is_maple(qcadev->btsoc_type)) {
+ dev_info(&serdev->dev, "Maple: power ctrl enabled\n");
+ power_ctrl_enabled = true;
+ }

qcadev->susclk = devm_clk_get_optional(&serdev->dev, NULL);
if (IS_ERR(qcadev->susclk)) {
@@ -2142,6 +2184,8 @@ static void qca_serdev_remove(struct serdev_device *serdev)
qca_is_wcn6750(qcadev->btsoc_type)) &&
power->vregs_on)
qca_power_shutdown(&qcadev->serdev_hu);
+ else if (qca_is_maple(qcadev->btsoc_type))
+ qca_power_shutdown(&qcadev->serdev_hu);
else if (qcadev->susclk)
clk_disable_unprepare(qcadev->susclk);

@@ -2312,6 +2356,7 @@ static SIMPLE_DEV_PM_OPS(qca_pm_ops, qca_suspend, qca_resume);
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,maple-bt", .data = &qca_soc_data_maple},
{ .compatible = "qcom,qca9377-bt" },
{ .compatible = "qcom,wcn3990-bt", .data = &qca_soc_data_wcn3990},
{ .compatible = "qcom,wcn3991-bt", .data = &qca_soc_data_wcn3991},
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project