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