wifi: ath12k: add support for collecting firmware log

Currently there is no way to collect firmware log because firmware
does not send it to host. Also host does not handle WMI_DIAG_EVENTID
which is used by firmware to upload firmware log.

So add support for it by firstly enabling firmware log upload via a
QMI message, and secondly processing WMI DIAG event to expose it to
userspace via trace event.

This change applies to both WCN7850 and QCN9274.

Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0-03427-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.15378.4

Signed-off-by: Baochen Qiang <quic_bqiang@quicinc.com>
Acked-by: Jeff Johnson <quic_jjohnson@quicinc.com>
Signed-off-by: Kalle Valo <quic_kvalo@quicinc.com>
Link: https://msgid.link/20240115023726.2866-1-quic_bqiang@quicinc.com
This commit is contained in:
Baochen Qiang 2024-01-18 07:53:48 +02:00 committed by Kalle Valo
parent 6db6e70a17
commit 9f9df1a253
4 changed files with 146 additions and 2 deletions

View File

@ -1954,6 +1954,50 @@ static const struct qmi_elem_info qmi_wlanfw_fw_ready_ind_msg_v01_ei[] = {
},
};
static const struct qmi_elem_info qmi_wlanfw_wlan_ini_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 qmi_wlanfw_wlan_ini_req_msg_v01,
enable_fwlog_valid),
},
{
.data_type = QMI_UNSIGNED_1_BYTE,
.elem_len = 1,
.elem_size = sizeof(u8),
.array_type = NO_ARRAY,
.tlv_type = 0x10,
.offset = offsetof(struct qmi_wlanfw_wlan_ini_req_msg_v01,
enable_fwlog),
},
{
.data_type = QMI_EOTI,
.array_type = NO_ARRAY,
.tlv_type = QMI_COMMON_TLV_TYPE,
},
};
static const struct qmi_elem_info qmi_wlanfw_wlan_ini_resp_msg_v01_ei[] = {
{
.data_type = QMI_STRUCT,
.elem_len = 1,
.elem_size = sizeof(struct qmi_response_type_v01),
.array_type = NO_ARRAY,
.tlv_type = 0x02,
.offset = offsetof(struct qmi_wlanfw_wlan_ini_resp_msg_v01,
resp),
.ei_array = qmi_response_type_v01_ei,
},
{
.data_type = QMI_EOTI,
.array_type = NO_ARRAY,
.tlv_type = QMI_COMMON_TLV_TYPE,
},
};
static void ath12k_host_cap_parse_mlo(struct ath12k_base *ab,
struct qmi_wlanfw_host_cap_req_msg_v01 *req)
{
@ -2853,6 +2897,49 @@ static int ath12k_qmi_wlanfw_wlan_cfg_send(struct ath12k_base *ab)
return ret;
}
static int ath12k_qmi_wlanfw_wlan_ini_send(struct ath12k_base *ab)
{
struct qmi_wlanfw_wlan_ini_resp_msg_v01 resp = {};
struct qmi_wlanfw_wlan_ini_req_msg_v01 req = {};
struct qmi_txn txn;
int ret;
req.enable_fwlog_valid = true;
req.enable_fwlog = 1;
ret = qmi_txn_init(&ab->qmi.handle, &txn,
qmi_wlanfw_wlan_ini_resp_msg_v01_ei, &resp);
if (ret < 0)
goto out;
ret = qmi_send_request(&ab->qmi.handle, NULL, &txn,
ATH12K_QMI_WLANFW_WLAN_INI_REQ_V01,
QMI_WLANFW_WLAN_INI_REQ_MSG_V01_MAX_LEN,
qmi_wlanfw_wlan_ini_req_msg_v01_ei, &req);
if (ret < 0) {
qmi_txn_cancel(&txn);
ath12k_warn(ab, "failed to send QMI wlan ini request: %d\n",
ret);
goto out;
}
ret = qmi_txn_wait(&txn, msecs_to_jiffies(ATH12K_QMI_WLANFW_TIMEOUT_MS));
if (ret < 0) {
ath12k_warn(ab, "failed to receive QMI wlan ini request: %d\n", ret);
goto out;
}
if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
ath12k_warn(ab, "QMI wlan ini response failure: %d %d\n",
resp.resp.result, resp.resp.error);
ret = -EINVAL;
goto out;
}
out:
return ret;
}
void ath12k_qmi_firmware_stop(struct ath12k_base *ab)
{
int ret;
@ -2869,6 +2956,12 @@ int ath12k_qmi_firmware_start(struct ath12k_base *ab,
{
int ret;
ret = ath12k_qmi_wlanfw_wlan_ini_send(ab);
if (ret < 0) {
ath12k_warn(ab, "qmi failed to send wlan fw ini: %d\n", ret);
return ret;
}
ret = ath12k_qmi_wlanfw_wlan_cfg_send(ab);
if (ret < 0) {
ath12k_warn(ab, "qmi failed to send wlan cfg:%d\n", ret);

View File

@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
* Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
* Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef ATH12K_QMI_H
@ -576,6 +576,21 @@ struct qmi_wlanfw_wlan_cfg_resp_msg_v01 {
struct qmi_response_type_v01 resp;
};
#define ATH12K_QMI_WLANFW_WLAN_INI_REQ_V01 0x002F
#define ATH12K_QMI_WLANFW_WLAN_INI_RESP_V01 0x002F
#define QMI_WLANFW_WLAN_INI_REQ_MSG_V01_MAX_LEN 7
#define QMI_WLANFW_WLAN_INI_RESP_MSG_V01_MAX_LEN 7
struct qmi_wlanfw_wlan_ini_req_msg_v01 {
/* Must be set to true if enable_fwlog is being passed */
u8 enable_fwlog_valid;
u8 enable_fwlog;
};
struct qmi_wlanfw_wlan_ini_resp_msg_v01 {
struct qmi_response_type_v01 resp;
};
int ath12k_qmi_firmware_start(struct ath12k_base *ab,
u32 mode);
void ath12k_qmi_firmware_stop(struct ath12k_base *ab);

View File

@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2019-2021 The Linux Foundation. All rights reserved.
* Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
* Copyright (c) 2021-2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#if !defined(_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ)
@ -140,6 +140,33 @@ TRACE_EVENT(ath12k_htt_rxdesc,
)
);
TRACE_EVENT(ath12k_wmi_diag,
TP_PROTO(struct ath12k_base *ab, const void *data, size_t len),
TP_ARGS(ab, data, len),
TP_STRUCT__entry(
__string(device, dev_name(ab->dev))
__string(driver, dev_driver_string(ab->dev))
__field(u16, len)
__dynamic_array(u8, data, len)
),
TP_fast_assign(
__assign_str(device, dev_name(ab->dev));
__assign_str(driver, dev_driver_string(ab->dev));
__entry->len = len;
memcpy(__get_dynamic_array(data), data, len);
),
TP_printk(
"%s %s tlv diag len %d",
__get_str(driver),
__get_str(device),
__entry->len
)
);
#endif /* _TRACE_H_ || TRACE_HEADER_MULTI_READ*/
/* we don't want to use include/trace/events */

View File

@ -6664,6 +6664,12 @@ static void ath12k_rfkill_state_change_event(struct ath12k_base *ab,
kfree(tb);
}
static void
ath12k_wmi_diag_event(struct ath12k_base *ab, struct sk_buff *skb)
{
trace_ath12k_wmi_diag(ab, skb->data, skb->len);
}
static void ath12k_wmi_op_rx(struct ath12k_base *ab, struct sk_buff *skb)
{
struct wmi_cmd_hdr *cmd_hdr;
@ -6774,6 +6780,9 @@ static void ath12k_wmi_op_rx(struct ath12k_base *ab, struct sk_buff *skb)
case WMI_VDEV_DELETE_RESP_EVENTID:
ath12k_vdev_delete_resp_event(ab, skb);
break;
case WMI_DIAG_EVENTID:
ath12k_wmi_diag_event(ab, skb);
break;
/* TODO: Add remaining events */
default:
ath12k_dbg(ab, ATH12K_DBG_WMI, "Unknown eventid: 0x%x\n", id);