Skip to content

Commit

Permalink
Bluetooth: hci_qca: Add support for Qualcomm Bluetooth SoC WCN3991
Browse files Browse the repository at this point in the history
This patch add support for WCN3991 i.e. current values and fw download
support.

Signed-off-by: Balakrishna Godavarthi <bgodavar@codeaurora.org>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
  • Loading branch information
Balakrishna Godavarthi authored and Marcel Holtmann committed Nov 9, 2019
1 parent e303d12 commit 7d250a0
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 20 deletions.
68 changes: 53 additions & 15 deletions drivers/bluetooth/btqca.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,27 +14,41 @@

#define VERSION "0.1"

int qca_read_soc_version(struct hci_dev *hdev, u32 *soc_version)
int qca_read_soc_version(struct hci_dev *hdev, u32 *soc_version,
enum qca_btsoc_type soc_type)
{
struct sk_buff *skb;
struct edl_event_hdr *edl;
struct qca_btsoc_version *ver;
char cmd;
int err = 0;
u8 event_type = HCI_EV_VENDOR;
u8 rlen = sizeof(*edl) + sizeof(*ver);
u8 rtype = EDL_APP_VER_RES_EVT;

bt_dev_dbg(hdev, "QCA Version Request");

/* Unlike other SoC's sending version command response as payload to
* VSE event. WCN3991 sends version command response as a payload to
* command complete event.
*/
if (soc_type == QCA_WCN3991) {
event_type = 0;
rlen += 1;
rtype = EDL_PATCH_VER_REQ_CMD;
}

cmd = EDL_PATCH_VER_REQ_CMD;
skb = __hci_cmd_sync_ev(hdev, EDL_PATCH_CMD_OPCODE, EDL_PATCH_CMD_LEN,
&cmd, HCI_EV_VENDOR, HCI_INIT_TIMEOUT);
&cmd, event_type, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
err = PTR_ERR(skb);
bt_dev_err(hdev, "Reading QCA version information failed (%d)",
err);
return err;
}

if (skb->len != sizeof(*edl) + sizeof(*ver)) {
if (skb->len != rlen) {
bt_dev_err(hdev, "QCA Version size mismatch len %d", skb->len);
err = -EILSEQ;
goto out;
Expand All @@ -48,13 +62,16 @@ int qca_read_soc_version(struct hci_dev *hdev, u32 *soc_version)
}

if (edl->cresp != EDL_CMD_REQ_RES_EVT ||
edl->rtype != EDL_APP_VER_RES_EVT) {
edl->rtype != rtype) {
bt_dev_err(hdev, "QCA Wrong packet received %d %d", edl->cresp,
edl->rtype);
err = -EIO;
goto out;
}

if (soc_type == QCA_WCN3991)
memmove(&edl->data, &edl->data[1], sizeof(*ver));

ver = (struct qca_btsoc_version *)(edl->data);

BT_DBG("%s: Product:0x%08x", hdev->name, le32_to_cpu(ver->product_id));
Expand Down Expand Up @@ -223,13 +240,17 @@ static void qca_tlv_check_data(struct qca_fw_config *config,
}

static int qca_tlv_send_segment(struct hci_dev *hdev, int seg_size,
const u8 *data, enum qca_tlv_dnld_mode mode)
const u8 *data, enum qca_tlv_dnld_mode mode,
enum qca_btsoc_type soc_type)
{
struct sk_buff *skb;
struct edl_event_hdr *edl;
struct tlv_seg_resp *tlv_resp;
u8 cmd[MAX_SIZE_PER_TLV_SEGMENT + 2];
int err = 0;
u8 event_type = HCI_EV_VENDOR;
u8 rlen = (sizeof(*edl) + sizeof(*tlv_resp));
u8 rtype = EDL_TVL_DNLD_RES_EVT;

cmd[0] = EDL_PATCH_TLV_REQ_CMD;
cmd[1] = seg_size;
Expand All @@ -239,15 +260,25 @@ static int qca_tlv_send_segment(struct hci_dev *hdev, int seg_size,
return __hci_cmd_send(hdev, EDL_PATCH_CMD_OPCODE, seg_size + 2,
cmd);

/* Unlike other SoC's sending version command response as payload to
* VSE event. WCN3991 sends version command response as a payload to
* command complete event.
*/
if (soc_type == QCA_WCN3991) {
event_type = 0;
rlen = sizeof(*edl);
rtype = EDL_PATCH_TLV_REQ_CMD;
}

skb = __hci_cmd_sync_ev(hdev, EDL_PATCH_CMD_OPCODE, seg_size + 2, cmd,
HCI_EV_VENDOR, HCI_INIT_TIMEOUT);
event_type, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
err = PTR_ERR(skb);
bt_dev_err(hdev, "QCA Failed to send TLV segment (%d)", err);
return err;
}

if (skb->len != sizeof(*edl) + sizeof(*tlv_resp)) {
if (skb->len != rlen) {
bt_dev_err(hdev, "QCA TLV response size mismatch");
err = -EILSEQ;
goto out;
Expand All @@ -260,13 +291,19 @@ static int qca_tlv_send_segment(struct hci_dev *hdev, int seg_size,
goto out;
}

tlv_resp = (struct tlv_seg_resp *)(edl->data);
if (edl->cresp != EDL_CMD_REQ_RES_EVT || edl->rtype != rtype) {
bt_dev_err(hdev, "QCA TLV with error stat 0x%x rtype 0x%x",
edl->cresp, edl->rtype);
err = -EIO;
}

if (edl->cresp != EDL_CMD_REQ_RES_EVT ||
edl->rtype != EDL_TVL_DNLD_RES_EVT || tlv_resp->result != 0x00) {
if (soc_type == QCA_WCN3991)
goto out;

tlv_resp = (struct tlv_seg_resp *)(edl->data);
if (tlv_resp->result) {
bt_dev_err(hdev, "QCA TLV with error stat 0x%x rtype 0x%x (0x%x)",
edl->cresp, edl->rtype, tlv_resp->result);
err = -EIO;
}

out:
Expand Down Expand Up @@ -301,7 +338,8 @@ static int qca_inject_cmd_complete_event(struct hci_dev *hdev)
}

static int qca_download_firmware(struct hci_dev *hdev,
struct qca_fw_config *config)
struct qca_fw_config *config,
enum qca_btsoc_type soc_type)
{
const struct firmware *fw;
const u8 *segment;
Expand Down Expand Up @@ -331,7 +369,7 @@ static int qca_download_firmware(struct hci_dev *hdev,
config->dnld_mode = QCA_SKIP_EVT_NONE;

ret = qca_tlv_send_segment(hdev, segsize, segment,
config->dnld_mode);
config->dnld_mode, soc_type);
if (ret)
goto out;

Expand Down Expand Up @@ -405,7 +443,7 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
"qca/rampatch_%08x.bin", soc_ver);
}

err = qca_download_firmware(hdev, &config);
err = qca_download_firmware(hdev, &config, soc_type);
if (err < 0) {
bt_dev_err(hdev, "QCA Failed to download patch (%d)", err);
return err;
Expand All @@ -426,7 +464,7 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
snprintf(config.fwname, sizeof(config.fwname),
"qca/nvm_%08x.bin", soc_ver);

err = qca_download_firmware(hdev, &config);
err = qca_download_firmware(hdev, &config, soc_type);
if (err < 0) {
bt_dev_err(hdev, "QCA Failed to download NVM (%d)", err);
return err;
Expand Down
10 changes: 7 additions & 3 deletions drivers/bluetooth/btqca.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ enum qca_btsoc_type {
QCA_AR3002,
QCA_ROME,
QCA_WCN3990,
QCA_WCN3991,
QCA_WCN3998,
};

Expand All @@ -134,12 +135,14 @@ int qca_set_bdaddr_rome(struct hci_dev *hdev, const bdaddr_t *bdaddr);
int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
enum qca_btsoc_type soc_type, u32 soc_ver,
const char *firmware_name);
int qca_read_soc_version(struct hci_dev *hdev, u32 *soc_version);
int qca_read_soc_version(struct hci_dev *hdev, u32 *soc_version,
enum qca_btsoc_type);
int qca_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr);
int qca_send_pre_shutdown_cmd(struct hci_dev *hdev);
static inline bool qca_is_wcn399x(enum qca_btsoc_type soc_type)
{
return soc_type == QCA_WCN3990 || soc_type == QCA_WCN3998;
return soc_type == QCA_WCN3990 || soc_type == QCA_WCN3991 ||
soc_type == QCA_WCN3998;
}
#else

Expand All @@ -155,7 +158,8 @@ static inline int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
return -EOPNOTSUPP;
}

static inline int qca_read_soc_version(struct hci_dev *hdev, u32 *soc_version)
static inline int qca_read_soc_version(struct hci_dev *hdev, u32 *soc_version,
enum qca_btsoc_type)
{
return -EOPNOTSUPP;
}
Expand Down
16 changes: 14 additions & 2 deletions drivers/bluetooth/hci_qca.c
Original file line number Diff line number Diff line change
Expand Up @@ -1288,7 +1288,7 @@ static int qca_setup(struct hci_uart *hu)
if (ret)
return ret;

ret = qca_read_soc_version(hdev, &soc_ver);
ret = qca_read_soc_version(hdev, &soc_ver, soc_type);
if (ret)
return ret;
} else {
Expand All @@ -1308,7 +1308,7 @@ static int qca_setup(struct hci_uart *hu)

if (!qca_is_wcn399x(soc_type)) {
/* Get QCA version information */
ret = qca_read_soc_version(hdev, &soc_ver);
ret = qca_read_soc_version(hdev, &soc_ver, soc_type);
if (ret)
return ret;
}
Expand Down Expand Up @@ -1366,6 +1366,17 @@ static const struct qca_vreg_data qca_soc_data_wcn3990 = {
.num_vregs = 4,
};

static const struct qca_vreg_data qca_soc_data_wcn3991 = {
.soc_type = QCA_WCN3991,
.vregs = (struct qca_vreg []) {
{ "vddio", 15000 },
{ "vddxo", 80000 },
{ "vddrf", 300000 },
{ "vddch0", 450000 },
},
.num_vregs = 4,
};

static const struct qca_vreg_data qca_soc_data_wcn3998 = {
.soc_type = QCA_WCN3998,
.vregs = (struct qca_vreg []) {
Expand Down Expand Up @@ -1662,6 +1673,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,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 */ }
};
Expand Down

0 comments on commit 7d250a0

Please sign in to comment.