ath10k: Fix HOST capability QMI incompatibility
authorBjorn Andersson <bjorn.andersson@linaro.org>
Thu, 25 Jul 2019 06:31:08 +0000 (23:31 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 29 Nov 2019 09:09:41 +0000 (10:09 +0100)
commit 7165ef890a4c44cf16db66b82fd78448f4bde6ba upstream.

The introduction of 768ec4c012ac ("ath10k: update HOST capability QMI
message") served the purpose of supporting the new and extended HOST
capability QMI message.

But while the new message adds a slew of optional members it changes the
data type of the "daemon_support" member, which means that older
versions of the firmware will fail to decode the incoming request
message.

There is no way to detect this breakage from Linux and there's no way to
recover from sending the wrong message (i.e. we can't just try one
format and then fallback to the other), so a quirk is introduced in
DeviceTree to indicate to the driver that the firmware requires the 8bit
version of this message.

Cc: stable@vger.kernel.org
Fixes: 768ec4c012ac ("ath10k: update HOST capability qmi message")
Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
Acked-by: Rob Herring <robh@kernel.org>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Documentation/devicetree/bindings/net/wireless/qcom,ath10k.txt
drivers/net/wireless/ath/ath10k/qmi.c
drivers/net/wireless/ath/ath10k/qmi_wlfw_v01.c
drivers/net/wireless/ath/ath10k/qmi_wlfw_v01.h
drivers/net/wireless/ath/ath10k/snoc.c
drivers/net/wireless/ath/ath10k/snoc.h

index ae661e6..f9499b2 100644 (file)
@@ -81,6 +81,12 @@ Optional properties:
        Definition: Name of external front end module used. Some valid FEM names
                    for example: "microsemi-lx5586", "sky85703-11"
                    and "sky85803" etc.
+- qcom,snoc-host-cap-8bit-quirk:
+       Usage: Optional
+       Value type: <empty>
+       Definition: Quirk specifying that the firmware expects the 8bit version
+                   of the host capability QMI request
+
 
 Example (to supply PCI based wifi block details):
 
index 3b63b62..545ac1f 100644 (file)
@@ -581,22 +581,29 @@ static int ath10k_qmi_host_cap_send_sync(struct ath10k_qmi *qmi)
 {
        struct wlfw_host_cap_resp_msg_v01 resp = {};
        struct wlfw_host_cap_req_msg_v01 req = {};
+       struct qmi_elem_info *req_ei;
        struct ath10k *ar = qmi->ar;
+       struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
        struct qmi_txn txn;
        int ret;
 
        req.daemon_support_valid = 1;
        req.daemon_support = 0;
 
-       ret = qmi_txn_init(&qmi->qmi_hdl, &txn,
-                          wlfw_host_cap_resp_msg_v01_ei, &resp);
+       ret = qmi_txn_init(&qmi->qmi_hdl, &txn, wlfw_host_cap_resp_msg_v01_ei,
+                          &resp);
        if (ret < 0)
                goto out;
 
+       if (test_bit(ATH10K_SNOC_FLAG_8BIT_HOST_CAP_QUIRK, &ar_snoc->flags))
+               req_ei = wlfw_host_cap_8bit_req_msg_v01_ei;
+       else
+               req_ei = wlfw_host_cap_req_msg_v01_ei;
+
        ret = qmi_send_request(&qmi->qmi_hdl, NULL, &txn,
                               QMI_WLFW_HOST_CAP_REQ_V01,
                               WLFW_HOST_CAP_REQ_MSG_V01_MAX_MSG_LEN,
-                              wlfw_host_cap_req_msg_v01_ei, &req);
+                              req_ei, &req);
        if (ret < 0) {
                qmi_txn_cancel(&txn);
                ath10k_err(ar, "failed to send host capability request: %d\n", ret);
index 1fe05c6..86fcf4e 100644 (file)
@@ -1988,6 +1988,28 @@ struct qmi_elem_info wlfw_host_cap_req_msg_v01_ei[] = {
        {}
 };
 
+struct qmi_elem_info wlfw_host_cap_8bit_req_msg_v01_ei[] = {
+       {
+               .data_type      = QMI_OPT_FLAG,
+               .elem_len       = 1,
+               .elem_size      = sizeof(u8),
+               .array_type     = NO_ARRAY,
+               .tlv_type       = 0x10,
+               .offset         = offsetof(struct wlfw_host_cap_req_msg_v01,
+                                          daemon_support_valid),
+       },
+       {
+               .data_type      = QMI_UNSIGNED_1_BYTE,
+               .elem_len       = 1,
+               .elem_size      = sizeof(u8),
+               .array_type     = NO_ARRAY,
+               .tlv_type       = 0x10,
+               .offset         = offsetof(struct wlfw_host_cap_req_msg_v01,
+                                          daemon_support),
+       },
+       {}
+};
+
 struct qmi_elem_info wlfw_host_cap_resp_msg_v01_ei[] = {
        {
                .data_type      = QMI_STRUCT,
index bca1186..4d107e1 100644 (file)
@@ -575,6 +575,7 @@ struct wlfw_host_cap_req_msg_v01 {
 
 #define WLFW_HOST_CAP_REQ_MSG_V01_MAX_MSG_LEN 189
 extern struct qmi_elem_info wlfw_host_cap_req_msg_v01_ei[];
+extern struct qmi_elem_info wlfw_host_cap_8bit_req_msg_v01_ei[];
 
 struct wlfw_host_cap_resp_msg_v01 {
        struct qmi_response_type_v01 resp;
index b491361..fc15a00 100644 (file)
@@ -1261,6 +1261,15 @@ out:
        return ret;
 }
 
+static void ath10k_snoc_quirks_init(struct ath10k *ar)
+{
+       struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
+       struct device *dev = &ar_snoc->dev->dev;
+
+       if (of_property_read_bool(dev->of_node, "qcom,snoc-host-cap-8bit-quirk"))
+               set_bit(ATH10K_SNOC_FLAG_8BIT_HOST_CAP_QUIRK, &ar_snoc->flags);
+}
+
 int ath10k_snoc_fw_indication(struct ath10k *ar, u64 type)
 {
        struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
@@ -1678,6 +1687,8 @@ static int ath10k_snoc_probe(struct platform_device *pdev)
        ar->ce_priv = &ar_snoc->ce;
        msa_size = drv_data->msa_size;
 
+       ath10k_snoc_quirks_init(ar);
+
        ret = ath10k_snoc_resource_init(ar);
        if (ret) {
                ath10k_warn(ar, "failed to initialize resource: %d\n", ret);
index d62f535..9db823e 100644 (file)
@@ -63,6 +63,7 @@ enum ath10k_snoc_flags {
        ATH10K_SNOC_FLAG_REGISTERED,
        ATH10K_SNOC_FLAG_UNREGISTERING,
        ATH10K_SNOC_FLAG_RECOVERY,
+       ATH10K_SNOC_FLAG_8BIT_HOST_CAP_QUIRK,
 };
 
 struct ath10k_snoc {