Add debugfs interface reg_addr/reg_value and use these two interfaces to read register value. For example, execute the following commands to read WCN6855 HW register of “memtype=0xa offset=0x3a00d0”: $ echo 0xa 0x3a00d0>reg_addr $ cat reg_value 0x10200000 Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1 Signed-off-by: Cheng Wang <quic_chengwan@xxxxxxxxxxx> --- v2: change per comments from Kalle fixes new warnings. drivers/net/wireless/ath/ath11k/core.h | 2 + drivers/net/wireless/ath/ath11k/debugfs.c | 169 ++++++++++++++++++++++ drivers/net/wireless/ath/ath11k/qmi.c | 137 ++++++++++++++++++ drivers/net/wireless/ath/ath11k/qmi.h | 39 +++++ 4 files changed, 347 insertions(+) diff --git a/drivers/net/wireless/ath/ath11k/core.h b/drivers/net/wireless/ath/ath11k/core.h index 4eda15c56b04..910479427f55 100644 --- a/drivers/net/wireless/ath/ath11k/core.h +++ b/drivers/net/wireless/ath/ath11k/core.h @@ -454,6 +454,8 @@ struct ath11k_debug { u32 pktlog_peer_valid; u8 pktlog_peer_addr[ETH_ALEN]; u32 rx_filter; + enum qmi_allocram_arena mem_type; + u32 mem_offset; }; struct ath11k_per_peer_tx_stats { diff --git a/drivers/net/wireless/ath/ath11k/debugfs.c b/drivers/net/wireless/ath/ath11k/debugfs.c index 215b6014c0ef..ec2f8c91063a 100644 --- a/drivers/net/wireless/ath/ath11k/debugfs.c +++ b/drivers/net/wireless/ath/ath11k/debugfs.c @@ -1113,6 +1113,160 @@ static const struct file_operations fops_simulate_radar = { .open = simple_open }; +static const char *ath11k_memtype2string(u32 mem_type) +{ + const char * const mem_type_str[] = { + [QMI_ALLOCRAM_HRAM_ARENA] = "hram", + [QMI_ALLOCRAM_HCRAM_ARENA] = "hcram", + [QMI_ALLOCRAM_HREMOTE_ARENA] = "hremote", + [QMI_ALLOCRAM_HCREMOTE_ARENA] = "hcremote", + [QMI_ALLOCRAM_REMOTE_ARENA] = "remote", + [QMI_ALLOCRAM_SRAM_ARENA] = "sram", + [QMI_ALLOCRAM_SRAM_AUX_ARENA] = "sram_aux", + [QMI_ALLOCRAM_PAGEABLE_ARENA] = "pageable", + [QMI_ALLOCRAM_CMEM_ARENA] = "cmem", + [QMI_ALLOCRAM_TRAM_ARENA] = "tram", + [QMI_ALLOCRAM_HWIO_ARENA] = "hwio", + [QMI_ALLOCRAM_CALDB_ARENA] = "caldb", + [QMI_ALLOCRAM_M3_ARENA] = "m3_arena", + [QMI_ALLOCRAM_ETMREMOTE_ARENA] = "etm_remote", + [QMI_ALLOCRAM_EMUPHY_ARENA] = "emu_phy", + }; + + if (mem_type < ARRAY_SIZE(mem_type_str)) + return mem_type_str[mem_type]; + + return NULL; +} + +static ssize_t ath11k_read_reg_addr(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath11k *ar = file->private_data; + u8 buf[64]; + size_t len = 0; + u32 mem_type, mem_offset; + + mutex_lock(&ar->conf_mutex); + mem_type = ar->debug.mem_type; + mem_offset = ar->debug.mem_offset; + mutex_unlock(&ar->conf_mutex); + + len += scnprintf(buf + len, sizeof(buf) - len, "0x%x 0x%x\n", + mem_type, mem_offset); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t ath11k_write_reg_addr(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath11k *ar = file->private_data; + struct ath11k_base *ab = ar->ab; + char buf[64] = {0}; + ssize_t ret; + int rc; + u32 mem_type, mem_offset; + char sep[] = " "; + char *token, *cur; + const char *mem_type_str; + + if (*ppos != 0 || count >= sizeof(buf) || count == 0) { + ret = -EINVAL; + goto out; + } + + ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count); + if (ret < 0) + goto out; + + /* drop the possible '\n' from the end */ + if (buf[*ppos - 1] == '\n') + buf[*ppos - 1] = '\0'; + + ath11k_dbg(ab, ATH11K_DBG_QMI, "%s: %s\n", __func__, buf); + ret = count; + cur = buf; + + token = strsep(&cur, sep); + rc = kstrtou32(token, 0, &mem_type); + if (rc) { + ath11k_warn(ab, "%s convert error: mem_type %s\n", __func__, token); + ret = -EFAULT; + goto out; + } + + mem_type_str = ath11k_memtype2string(mem_type); + if (!mem_type_str) { + ath11k_warn(ab, "%s invalid mem type value %d\n", __func__, mem_type); + ret = -EINVAL; + goto out; + } + ath11k_dbg(ab, ATH11K_DBG_QMI, "%s mem_type %d %s\n", + __func__, mem_type, mem_type_str); + + token = strim(cur); + rc = kstrtou32(token, 0, &mem_offset); + if (rc) { + ath11k_warn(ab, "%s convert error: mem_offset %s\n", __func__, token); + ret = -EFAULT; + goto out; + } + + mutex_lock(&ar->conf_mutex); + ar->debug.mem_type = mem_type; + ar->debug.mem_offset = mem_offset; + mutex_unlock(&ar->conf_mutex); + +out: + return ret; +} + +static const struct file_operations fops_reg_addr = { + .read = ath11k_read_reg_addr, + .write = ath11k_write_reg_addr, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t ath11k_read_reg_value(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath11k *ar = file->private_data; + struct ath11k_base *ab = ar->ab; + u8 buf[64]; + size_t len = 0; + int ret_read; + u32 reg_value; + + if (ar->debug.mem_type >= QMI_ALLOCRAM_ARENA_MAX) { + ath11k_warn(ab, "%s mem type error %d\n", __func__, ar->debug.mem_type); + return -EINVAL; + } + + ret_read = ath11k_qmi_wlanfw_athdiag_read_send(ab, ar->debug.mem_type, + ar->debug.mem_offset, ®_value); + if (ret_read < 0) + len += scnprintf(buf + len, sizeof(buf) - len, + "failed to read reg, err=%d\n", ret_read); + else + len += scnprintf(buf + len, sizeof(buf) - len, "0x%x\n", reg_value); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static const struct file_operations fops_reg_value = { + .read = ath11k_read_reg_value, + .write = NULL, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + int ath11k_debugfs_register(struct ath11k *ar) { struct ath11k_base *ab = ar->ab; @@ -1152,6 +1306,21 @@ int ath11k_debugfs_register(struct ath11k *ar) &ar->dfs_block_radar_events); } + switch (ab->hw_rev) { + case ATH11K_HW_WCN6855_HW20: + debugfs_create_file("reg_addr", 0600, + ar->debug.debugfs_pdev, ar, + &fops_reg_addr); + debugfs_create_file("reg_value", 0400, + ar->debug.debugfs_pdev, ar, + &fops_reg_value); + break; + default: + ath11k_warn(ab, "reg_addr debugfs not support hardware: %d\n", + ab->hw_rev); + break; + } + return 0; } diff --git a/drivers/net/wireless/ath/ath11k/qmi.c b/drivers/net/wireless/ath/ath11k/qmi.c index d0701e8eca9c..fc2fe84fc646 100644 --- a/drivers/net/wireless/ath/ath11k/qmi.c +++ b/drivers/net/wireless/ath/ath11k/qmi.c @@ -1628,6 +1628,86 @@ static struct qmi_elem_info qmi_wlanfw_wlan_ini_resp_msg_v01_ei[] = { }, }; +static struct qmi_elem_info qmi_wlanfw_athdiag_read_req_msg_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct qmi_wlanfw_athdiag_read_req_msg_v01, + mem_offset), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct qmi_wlanfw_athdiag_read_req_msg_v01, + mem_type), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0x03, + .offset = offsetof(struct qmi_wlanfw_athdiag_read_req_msg_v01, + data_len), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_athdiag_read_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_athdiag_read_resp_msg_v01, + resp), + .ei_array = qmi_response_type_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_athdiag_read_resp_msg_v01, + data_valid), + }, + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct qmi_wlanfw_athdiag_read_resp_msg_v01, + data_len), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = QMI_WLANFW_MAX_ATHDIAG_DATA_SIZE_V01, + .elem_size = sizeof(u8), + .array_type = VAR_LEN_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct qmi_wlanfw_athdiag_read_resp_msg_v01, + data), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + static int ath11k_qmi_host_cap_send(struct ath11k_base *ab) { struct qmi_wlanfw_host_cap_req_msg_v01 req; @@ -3025,3 +3105,60 @@ void ath11k_qmi_deinit_service(struct ath11k_base *ab) } EXPORT_SYMBOL(ath11k_qmi_deinit_service); +int ath11k_qmi_wlanfw_athdiag_read_send(struct ath11k_base *ab, u32 mem_type, + u32 mem_offset, u32 *out) +{ + int ret; + struct qmi_wlanfw_athdiag_read_req_msg_v01 req; + struct qmi_wlanfw_athdiag_read_resp_msg_v01 *resp; + struct qmi_txn txn; + + resp = kzalloc(sizeof(*resp), GFP_KERNEL); + if (!resp) + return -ENOMEM; + + req.mem_type = mem_type; + req.mem_offset = mem_offset; + req.data_len = sizeof(u32); + + ath11k_info(ab, "%s start, mem_type %d offset 0x%x\n", + __func__, mem_type, mem_offset); + ret = qmi_txn_init(&ab->qmi.handle, &txn, + qmi_wlanfw_athdiag_read_resp_msg_v01_ei, resp); + if (ret < 0) + goto out; + + ret = qmi_send_request(&ab->qmi.handle, NULL, &txn, + QMI_WLANFW_ATHDIAG_READ_REQ_V01, + QMI_WLANFW_ATHDIAG_READ_REQ_MSG_V01_MAX_LEN, + qmi_wlanfw_athdiag_read_req_msg_v01_ei, &req); + if (ret < 0) { + qmi_txn_cancel(&txn); + ath11k_warn(ab, "qmi failed to send wlan diag read request, err = %d\n", + ret); + goto out; + } + + ret = qmi_txn_wait(&txn, msecs_to_jiffies(ATH11K_QMI_WLANFW_TIMEOUT_MS)); + if (ret < 0) { + ath11k_warn(ab, "qmi failed wlan diag read request, err = %d\n", ret); + goto out; + } + + if (resp->resp.result != QMI_RESULT_SUCCESS_V01 || + !resp->data_valid || + resp->data_len != sizeof(u32)) { + ath11k_warn(ab, "QMI request failed result=%d, error=%d, valid=%d, len=%d\n", + resp->resp.result, resp->resp.error, + resp->data_valid, resp->data_len); + ret = -EINVAL; + goto out; + } + *out = *(u32 *)resp->data; + ath11k_dbg(ab, ATH11K_DBG_QMI, "qmi read reg pass, resp.data = 0x%08x\n", + *(u32 *)resp->data); + +out: + kfree(resp); + return ret; +} diff --git a/drivers/net/wireless/ath/ath11k/qmi.h b/drivers/net/wireless/ath/ath11k/qmi.h index ba2eff4d59cb..2cd6a3043d59 100644 --- a/drivers/net/wireless/ath/ath11k/qmi.h +++ b/drivers/net/wireless/ath/ath11k/qmi.h @@ -181,6 +181,43 @@ struct qmi_wlanfw_host_cap_resp_msg_v01 { struct qmi_response_type_v01 resp; }; +#define QMI_WLANFW_MAX_ATHDIAG_DATA_SIZE_V01 512 +#define QMI_WLANFW_ATHDIAG_READ_REQ_V01 0x0030 +#define QMI_WLANFW_ATHDIAG_READ_RESP_V01 0x0030 +#define QMI_WLANFW_ATHDIAG_READ_REQ_MSG_V01_MAX_LEN 21 + +enum qmi_allocram_arena { + QMI_ALLOCRAM_HRAM_ARENA, + QMI_ALLOCRAM_HCRAM_ARENA, + QMI_ALLOCRAM_HREMOTE_ARENA, + QMI_ALLOCRAM_HCREMOTE_ARENA, + QMI_ALLOCRAM_REMOTE_ARENA, + QMI_ALLOCRAM_SRAM_ARENA, + QMI_ALLOCRAM_SRAM_AUX_ARENA, + QMI_ALLOCRAM_PAGEABLE_ARENA, + QMI_ALLOCRAM_CMEM_ARENA, + QMI_ALLOCRAM_TRAM_ARENA, + QMI_ALLOCRAM_HWIO_ARENA, + QMI_ALLOCRAM_CALDB_ARENA, + QMI_ALLOCRAM_M3_ARENA, + QMI_ALLOCRAM_ETMREMOTE_ARENA, + QMI_ALLOCRAM_EMUPHY_ARENA, + QMI_ALLOCRAM_ARENA_MAX, +}; + +struct qmi_wlanfw_athdiag_read_req_msg_v01 { + u32 mem_offset; + u32 mem_type; + u32 data_len; +}; + +struct qmi_wlanfw_athdiag_read_resp_msg_v01 { + struct qmi_response_type_v01 resp; + u8 data_valid; + u32 data_len; + u8 data[QMI_WLANFW_MAX_ATHDIAG_DATA_SIZE_V01]; +}; + #define QMI_WLANFW_IND_REGISTER_REQ_MSG_V01_MAX_LEN 54 #define QMI_WLANFW_IND_REGISTER_REQ_V01 0x0020 #define QMI_WLANFW_IND_REGISTER_RESP_MSG_V01_MAX_LEN 18 @@ -492,5 +529,7 @@ void ath11k_qmi_event_work(struct work_struct *work); void ath11k_qmi_msg_recv_work(struct work_struct *work); void ath11k_qmi_deinit_service(struct ath11k_base *ab); int ath11k_qmi_init_service(struct ath11k_base *ab); +int ath11k_qmi_wlanfw_athdiag_read_send(struct ath11k_base *ab, u32 mem_type, + u32 offset, u32 *out); #endif -- 2.25.1