From d19122a52dcd88dd93da334ce13f26f4099417d0 Mon Sep 17 00:00:00 2001 From: Yuanyuan Liu Date: Tue, 3 Jan 2017 12:41:14 -0800 Subject: [PATCH 1/4] icnss: Update WLFW service with FW rejuvenate related messages Update WLFW service with FW rejuvenate related messages. These messages are used to trigger WLAN FW rejuvenation and update FW dynamic feature mask to FW. CRs-Fixed: 1107443 Change-Id: If91c78b5325b177195bdcb8159542d50bd0dd01d Signed-off-by: Yuanyuan Liu --- drivers/soc/qcom/wlan_firmware_service_v01.c | 435 ++++++++++++++++++- drivers/soc/qcom/wlan_firmware_service_v01.h | 128 +++++- 2 files changed, 558 insertions(+), 5 deletions(-) diff --git a/drivers/soc/qcom/wlan_firmware_service_v01.c b/drivers/soc/qcom/wlan_firmware_service_v01.c index 3e00d6c9d153..e3ebea31c019 100644 --- a/drivers/soc/qcom/wlan_firmware_service_v01.c +++ b/drivers/soc/qcom/wlan_firmware_service_v01.c @@ -1,4 +1,4 @@ - /* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. + /* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -130,6 +130,23 @@ static struct elem_info wlfw_shadow_reg_cfg_s_v01_ei[] = { }, }; +static struct elem_info wlfw_shadow_reg_v2_cfg_s_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct wlfw_shadow_reg_v2_cfg_s_v01, + addr), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + static struct elem_info wlfw_memory_region_info_s_v01_ei[] = { { .data_type = QMI_UNSIGNED_8_BYTE, @@ -360,6 +377,78 @@ struct elem_info wlfw_ind_register_req_msg_v01_ei[] = { .offset = offsetof(struct wlfw_ind_register_req_msg_v01, client_id), }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x16, + .offset = offsetof(struct wlfw_ind_register_req_msg_v01, + request_mem_enable_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x16, + .offset = offsetof(struct wlfw_ind_register_req_msg_v01, + request_mem_enable), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x17, + .offset = offsetof(struct wlfw_ind_register_req_msg_v01, + fw_mem_ready_enable_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x17, + .offset = offsetof(struct wlfw_ind_register_req_msg_v01, + fw_mem_ready_enable), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x18, + .offset = offsetof(struct wlfw_ind_register_req_msg_v01, + cold_boot_cal_done_enable_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x18, + .offset = offsetof(struct wlfw_ind_register_req_msg_v01, + cold_boot_cal_done_enable), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x19, + .offset = offsetof(struct wlfw_ind_register_req_msg_v01, + rejuvenate_enable_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .is_array = NO_ARRAY, + .tlv_type = 0x19, + .offset = offsetof(struct wlfw_ind_register_req_msg_v01, + rejuvenate_enable), + }, { .data_type = QMI_EOTI, .is_array = NO_ARRAY, @@ -645,6 +734,34 @@ struct elem_info wlfw_wlan_cfg_req_msg_v01_ei[] = { shadow_reg), .ei_array = wlfw_shadow_reg_cfg_s_v01_ei, }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x14, + .offset = offsetof(struct wlfw_wlan_cfg_req_msg_v01, + shadow_reg_v2_valid), + }, + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x14, + .offset = offsetof(struct wlfw_wlan_cfg_req_msg_v01, + shadow_reg_v2_len), + }, + { + .data_type = QMI_STRUCT, + .elem_len = QMI_WLFW_MAX_NUM_SHADOW_REG_V2_V01, + .elem_size = sizeof(struct wlfw_shadow_reg_v2_cfg_s_v01), + .is_array = VAR_LEN_ARRAY, + .tlv_type = 0x14, + .offset = offsetof(struct wlfw_wlan_cfg_req_msg_v01, + shadow_reg_v2), + .ei_array = wlfw_shadow_reg_v2_cfg_s_v01_ei, + }, { .data_type = QMI_EOTI, .is_array = NO_ARRAY, @@ -1650,3 +1767,319 @@ struct elem_info wlfw_mac_addr_resp_msg_v01_ei[] = { .is_array = QMI_COMMON_TLV_TYPE, }, }; + +struct elem_info wlfw_host_cap_req_msg_v01_ei[] = { + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = 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(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct wlfw_host_cap_req_msg_v01, + daemon_support), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_host_cap_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .is_array = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct wlfw_host_cap_resp_msg_v01, + resp), + .ei_array = get_qmi_response_type_v01_ei(), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_request_mem_ind_msg_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .is_array = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct wlfw_request_mem_ind_msg_v01, + size), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_respond_mem_req_msg_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_8_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint64_t), + .is_array = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct wlfw_respond_mem_req_msg_v01, + addr), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .is_array = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct wlfw_respond_mem_req_msg_v01, + size), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_respond_mem_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .is_array = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct wlfw_respond_mem_resp_msg_v01, + resp), + .ei_array = get_qmi_response_type_v01_ei(), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_fw_mem_ready_ind_msg_v01_ei[] = { + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_cold_boot_cal_done_ind_msg_v01_ei[] = { + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_rejuvenate_ind_msg_v01_ei[] = { + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct wlfw_rejuvenate_ind_msg_v01, + cause_for_rejuvenation_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct wlfw_rejuvenate_ind_msg_v01, + cause_for_rejuvenation), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct wlfw_rejuvenate_ind_msg_v01, + requesting_sub_system_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct wlfw_rejuvenate_ind_msg_v01, + requesting_sub_system), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct wlfw_rejuvenate_ind_msg_v01, + line_number_valid), + }, + { + .data_type = QMI_UNSIGNED_2_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint16_t), + .is_array = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct wlfw_rejuvenate_ind_msg_v01, + line_number), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct wlfw_rejuvenate_ind_msg_v01, + function_name_valid), + }, + { + .data_type = QMI_STRING, + .elem_len = QMI_WLFW_FUNCTION_NAME_LEN_V01 + 1, + .elem_size = sizeof(char), + .is_array = NO_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct wlfw_rejuvenate_ind_msg_v01, + function_name), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_rejuvenate_ack_req_msg_v01_ei[] = { + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_rejuvenate_ack_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .is_array = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof( + struct wlfw_rejuvenate_ack_resp_msg_v01, + resp), + .ei_array = get_qmi_response_type_v01_ei(), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_dynamic_feature_mask_req_msg_v01_ei[] = { + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct + wlfw_dynamic_feature_mask_req_msg_v01, + mask_valid), + }, + { + .data_type = QMI_UNSIGNED_8_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint64_t), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof( + struct wlfw_dynamic_feature_mask_req_msg_v01, + mask), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_dynamic_feature_mask_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .is_array = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof( + struct wlfw_dynamic_feature_mask_resp_msg_v01, + resp), + .ei_array = get_qmi_response_type_v01_ei(), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof( + struct wlfw_dynamic_feature_mask_resp_msg_v01, + prev_mask_valid), + }, + { + .data_type = QMI_UNSIGNED_8_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint64_t), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof( + struct wlfw_dynamic_feature_mask_resp_msg_v01, + prev_mask), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof( + struct wlfw_dynamic_feature_mask_resp_msg_v01, + curr_mask_valid), + }, + { + .data_type = QMI_UNSIGNED_8_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint64_t), + .is_array = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof( + struct wlfw_dynamic_feature_mask_resp_msg_v01, + curr_mask), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .is_array = QMI_COMMON_TLV_TYPE, + }, +}; + diff --git a/drivers/soc/qcom/wlan_firmware_service_v01.h b/drivers/soc/qcom/wlan_firmware_service_v01.h index 47b315fce94c..751e92338a0f 100644 --- a/drivers/soc/qcom/wlan_firmware_service_v01.h +++ b/drivers/soc/qcom/wlan_firmware_service_v01.h @@ -1,4 +1,4 @@ - /* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. + /* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -17,7 +17,10 @@ #define WLFW_SERVICE_VERS_V01 0x01 #define QMI_WLFW_BDF_DOWNLOAD_REQ_V01 0x0025 +#define QMI_WLFW_FW_MEM_READY_IND_V01 0x0037 #define QMI_WLFW_INITIATE_CAL_UPDATE_IND_V01 0x002A +#define QMI_WLFW_HOST_CAP_REQ_V01 0x0034 +#define QMI_WLFW_DYNAMIC_FEATURE_MASK_RESP_V01 0x003B #define QMI_WLFW_CAP_REQ_V01 0x0024 #define QMI_WLFW_CAL_REPORT_REQ_V01 0x0026 #define QMI_WLFW_CAL_UPDATE_RESP_V01 0x0029 @@ -26,25 +29,34 @@ #define QMI_WLFW_CAL_REPORT_RESP_V01 0x0026 #define QMI_WLFW_MAC_ADDR_RESP_V01 0x0033 #define QMI_WLFW_INITIATE_CAL_DOWNLOAD_IND_V01 0x0028 +#define QMI_WLFW_HOST_CAP_RESP_V01 0x0034 #define QMI_WLFW_MSA_READY_IND_V01 0x002B #define QMI_WLFW_ATHDIAG_WRITE_RESP_V01 0x0031 #define QMI_WLFW_WLAN_MODE_REQ_V01 0x0022 #define QMI_WLFW_IND_REGISTER_REQ_V01 0x0020 #define QMI_WLFW_WLAN_CFG_RESP_V01 0x0023 +#define QMI_WLFW_COLD_BOOT_CAL_DONE_IND_V01 0x0038 +#define QMI_WLFW_REQUEST_MEM_IND_V01 0x0035 +#define QMI_WLFW_REJUVENATE_IND_V01 0x0039 +#define QMI_WLFW_DYNAMIC_FEATURE_MASK_REQ_V01 0x003B #define QMI_WLFW_ATHDIAG_WRITE_REQ_V01 0x0031 #define QMI_WLFW_WLAN_MODE_RESP_V01 0x0022 +#define QMI_WLFW_RESPOND_MEM_REQ_V01 0x0036 #define QMI_WLFW_PIN_CONNECT_RESULT_IND_V01 0x002C #define QMI_WLFW_FW_READY_IND_V01 0x0021 #define QMI_WLFW_MSA_READY_RESP_V01 0x002E #define QMI_WLFW_CAL_UPDATE_REQ_V01 0x0029 #define QMI_WLFW_INI_REQ_V01 0x002F #define QMI_WLFW_BDF_DOWNLOAD_RESP_V01 0x0025 +#define QMI_WLFW_REJUVENATE_ACK_RESP_V01 0x003A #define QMI_WLFW_MSA_INFO_RESP_V01 0x002D #define QMI_WLFW_MSA_READY_REQ_V01 0x002E #define QMI_WLFW_CAP_RESP_V01 0x0024 +#define QMI_WLFW_REJUVENATE_ACK_REQ_V01 0x003A #define QMI_WLFW_ATHDIAG_READ_RESP_V01 0x0030 #define QMI_WLFW_VBATT_REQ_V01 0x0032 #define QMI_WLFW_MAC_ADDR_REQ_V01 0x0033 +#define QMI_WLFW_RESPOND_MEM_RESP_V01 0x0036 #define QMI_WLFW_VBATT_RESP_V01 0x0032 #define QMI_WLFW_MSA_INFO_REQ_V01 0x002D #define QMI_WLFW_CAL_DOWNLOAD_REQ_V01 0x0027 @@ -55,12 +67,14 @@ #define QMI_WLFW_MAX_NUM_MEMORY_REGIONS_V01 2 #define QMI_WLFW_MAX_NUM_CAL_V01 5 #define QMI_WLFW_MAX_DATA_SIZE_V01 6144 +#define QMI_WLFW_FUNCTION_NAME_LEN_V01 128 #define QMI_WLFW_MAX_NUM_CE_V01 12 #define QMI_WLFW_MAX_TIMESTAMP_LEN_V01 32 +#define QMI_WLFW_MAX_BUILD_ID_LEN_V01 128 #define QMI_WLFW_MAX_STR_LEN_V01 16 #define QMI_WLFW_MAX_NUM_SHADOW_REG_V01 24 #define QMI_WLFW_MAC_ADDR_SIZE_V01 6 -#define QMI_WLFW_MAX_BUILD_ID_LEN_V01 128 +#define QMI_WLFW_MAX_NUM_SHADOW_REG_V2_V01 36 #define QMI_WLFW_MAX_NUM_SVC_V01 24 enum wlfw_driver_mode_enum_v01 { @@ -72,6 +86,7 @@ enum wlfw_driver_mode_enum_v01 { QMI_WLFW_OFF_V01 = 4, QMI_WLFW_CCPM_V01 = 5, QMI_WLFW_QVIT_V01 = 6, + QMI_WLFW_CALIBRATION_V01 = 7, WLFW_DRIVER_MODE_ENUM_MAX_VAL_V01 = INT_MAX, }; @@ -104,6 +119,9 @@ enum wlfw_pipedir_enum_v01 { #define QMI_WLFW_ALREADY_REGISTERED_V01 ((uint64_t)0x01ULL) #define QMI_WLFW_FW_READY_V01 ((uint64_t)0x02ULL) #define QMI_WLFW_MSA_READY_V01 ((uint64_t)0x04ULL) +#define QMI_WLFW_FW_MEM_READY_V01 ((uint64_t)0x08ULL) + +#define QMI_WLFW_FW_REJUVENATE_V01 ((uint64_t)0x01ULL) struct wlfw_ce_tgt_pipe_cfg_s_v01 { uint32_t pipe_num; @@ -124,6 +142,10 @@ struct wlfw_shadow_reg_cfg_s_v01 { uint16_t offset; }; +struct wlfw_shadow_reg_v2_cfg_s_v01 { + uint32_t addr; +}; + struct wlfw_memory_region_info_s_v01 { uint64_t region_addr; uint32_t size; @@ -161,8 +183,16 @@ struct wlfw_ind_register_req_msg_v01 { uint8_t pin_connect_result_enable; uint8_t client_id_valid; uint32_t client_id; + uint8_t request_mem_enable_valid; + uint8_t request_mem_enable; + uint8_t fw_mem_ready_enable_valid; + uint8_t fw_mem_ready_enable; + uint8_t cold_boot_cal_done_enable_valid; + uint8_t cold_boot_cal_done_enable; + uint8_t rejuvenate_enable_valid; + uint32_t rejuvenate_enable; }; -#define WLFW_IND_REGISTER_REQ_MSG_V01_MAX_MSG_LEN 27 +#define WLFW_IND_REGISTER_REQ_MSG_V01_MAX_MSG_LEN 46 extern struct elem_info wlfw_ind_register_req_msg_v01_ei[]; struct wlfw_ind_register_resp_msg_v01 { @@ -222,8 +252,12 @@ struct wlfw_wlan_cfg_req_msg_v01 { uint8_t shadow_reg_valid; uint32_t shadow_reg_len; struct wlfw_shadow_reg_cfg_s_v01 shadow_reg[QMI_WLFW_MAX_NUM_SHADOW_REG_V01]; + uint8_t shadow_reg_v2_valid; + uint32_t shadow_reg_v2_len; + struct wlfw_shadow_reg_v2_cfg_s_v01 + shadow_reg_v2[QMI_WLFW_MAX_NUM_SHADOW_REG_V2_V01]; }; -#define WLFW_WLAN_CFG_REQ_MSG_V01_MAX_MSG_LEN 655 +#define WLFW_WLAN_CFG_REQ_MSG_V01_MAX_MSG_LEN 803 extern struct elem_info wlfw_wlan_cfg_req_msg_v01_ei[]; struct wlfw_wlan_cfg_resp_msg_v01 { @@ -448,4 +482,90 @@ struct wlfw_mac_addr_resp_msg_v01 { #define WLFW_MAC_ADDR_RESP_MSG_V01_MAX_MSG_LEN 7 extern struct elem_info wlfw_mac_addr_resp_msg_v01_ei[]; +struct wlfw_host_cap_req_msg_v01 { + uint8_t daemon_support_valid; + uint8_t daemon_support; +}; +#define WLFW_HOST_CAP_REQ_MSG_V01_MAX_MSG_LEN 4 +extern struct elem_info wlfw_host_cap_req_msg_v01_ei[]; + +struct wlfw_host_cap_resp_msg_v01 { + struct qmi_response_type_v01 resp; +}; +#define WLFW_HOST_CAP_RESP_MSG_V01_MAX_MSG_LEN 7 +extern struct elem_info wlfw_host_cap_resp_msg_v01_ei[]; + +struct wlfw_request_mem_ind_msg_v01 { + uint32_t size; +}; +#define WLFW_REQUEST_MEM_IND_MSG_V01_MAX_MSG_LEN 7 +extern struct elem_info wlfw_request_mem_ind_msg_v01_ei[]; + +struct wlfw_respond_mem_req_msg_v01 { + uint64_t addr; + uint32_t size; +}; +#define WLFW_RESPOND_MEM_REQ_MSG_V01_MAX_MSG_LEN 18 +extern struct elem_info wlfw_respond_mem_req_msg_v01_ei[]; + +struct wlfw_respond_mem_resp_msg_v01 { + struct qmi_response_type_v01 resp; +}; +#define WLFW_RESPOND_MEM_RESP_MSG_V01_MAX_MSG_LEN 7 +extern struct elem_info wlfw_respond_mem_resp_msg_v01_ei[]; + +struct wlfw_fw_mem_ready_ind_msg_v01 { + char placeholder; +}; +#define WLFW_FW_MEM_READY_IND_MSG_V01_MAX_MSG_LEN 0 +extern struct elem_info wlfw_fw_mem_ready_ind_msg_v01_ei[]; + +struct wlfw_cold_boot_cal_done_ind_msg_v01 { + char placeholder; +}; +#define WLFW_COLD_BOOT_CAL_DONE_IND_MSG_V01_MAX_MSG_LEN 0 +extern struct elem_info wlfw_cold_boot_cal_done_ind_msg_v01_ei[]; + +struct wlfw_rejuvenate_ind_msg_v01 { + uint8_t cause_for_rejuvenation_valid; + uint8_t cause_for_rejuvenation; + uint8_t requesting_sub_system_valid; + uint8_t requesting_sub_system; + uint8_t line_number_valid; + uint16_t line_number; + uint8_t function_name_valid; + char function_name[QMI_WLFW_FUNCTION_NAME_LEN_V01 + 1]; +}; +#define WLFW_REJUVENATE_IND_MSG_V01_MAX_MSG_LEN 144 +extern struct elem_info wlfw_rejuvenate_ind_msg_v01_ei[]; + +struct wlfw_rejuvenate_ack_req_msg_v01 { + char placeholder; +}; +#define WLFW_REJUVENATE_ACK_REQ_MSG_V01_MAX_MSG_LEN 0 +extern struct elem_info wlfw_rejuvenate_ack_req_msg_v01_ei[]; + +struct wlfw_rejuvenate_ack_resp_msg_v01 { + struct qmi_response_type_v01 resp; +}; +#define WLFW_REJUVENATE_ACK_RESP_MSG_V01_MAX_MSG_LEN 7 +extern struct elem_info wlfw_rejuvenate_ack_resp_msg_v01_ei[]; + +struct wlfw_dynamic_feature_mask_req_msg_v01 { + uint8_t mask_valid; + uint64_t mask; +}; +#define WLFW_DYNAMIC_FEATURE_MASK_REQ_MSG_V01_MAX_MSG_LEN 11 +extern struct elem_info wlfw_dynamic_feature_mask_req_msg_v01_ei[]; + +struct wlfw_dynamic_feature_mask_resp_msg_v01 { + struct qmi_response_type_v01 resp; + uint8_t prev_mask_valid; + uint64_t prev_mask; + uint8_t curr_mask_valid; + uint64_t curr_mask; +}; +#define WLFW_DYNAMIC_FEATURE_MASK_RESP_MSG_V01_MAX_MSG_LEN 29 +extern struct elem_info wlfw_dynamic_feature_mask_resp_msg_v01_ei[]; + #endif From 28520e0a3827e8588913672ca348ec381ebef3da Mon Sep 17 00:00:00 2001 From: Yuanyuan Liu Date: Wed, 14 Dec 2016 10:45:34 -0800 Subject: [PATCH 2/4] icnss: Add support of FW rejuvenation Add support of WLAN FW rejuvenation. After receiving FW rejuvenate indication, ICNSS will start WLAN FW rejuvenation and ack back to FW. CRs-Fixed: 1107443 Change-Id: I127e86d8467f64534f096d58424e687d4231d7a5 Signed-off-by: Yuanyuan Liu --- drivers/soc/qcom/icnss.c | 70 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c index 561a0d38e502..0e534625ef1e 100644 --- a/drivers/soc/qcom/icnss.c +++ b/drivers/soc/qcom/icnss.c @@ -175,6 +175,7 @@ enum icnss_driver_event_type { struct icnss_event_pd_service_down_data { bool crashed; + bool fw_rejuvenate; }; struct icnss_driver_event { @@ -256,6 +257,9 @@ struct icnss_stats { uint32_t vbatt_req; uint32_t vbatt_resp; uint32_t vbatt_req_err; + uint32_t rejuvenate_ack_req; + uint32_t rejuvenate_ack_resp; + uint32_t rejuvenate_ack_err; }; #define MAX_NO_OF_MAC_ADDR 4 @@ -1001,6 +1005,8 @@ static int wlfw_ind_register_send_sync_msg(void) req.msa_ready_enable = 1; req.pin_connect_result_enable_valid = 1; req.pin_connect_result_enable = 1; + req.rejuvenate_enable_valid = 1; + req.rejuvenate_enable = 1; req_desc.max_msg_len = WLFW_IND_REGISTER_REQ_MSG_V01_MAX_MSG_LEN; req_desc.msg_id = QMI_WLFW_IND_REGISTER_REQ_V01; @@ -1390,6 +1396,51 @@ out: return ret; } +static int wlfw_rejuvenate_ack_send_sync_msg(struct icnss_priv *priv) +{ + int ret; + struct wlfw_rejuvenate_ack_req_msg_v01 req; + struct wlfw_rejuvenate_ack_resp_msg_v01 resp; + struct msg_desc req_desc, resp_desc; + + icnss_pr_dbg("Sending rejuvenate ack request, state: 0x%lx\n", + priv->state); + + memset(&req, 0, sizeof(req)); + memset(&resp, 0, sizeof(resp)); + + req_desc.max_msg_len = WLFW_REJUVENATE_ACK_REQ_MSG_V01_MAX_MSG_LEN; + req_desc.msg_id = QMI_WLFW_REJUVENATE_ACK_REQ_V01; + req_desc.ei_array = wlfw_rejuvenate_ack_req_msg_v01_ei; + + resp_desc.max_msg_len = WLFW_REJUVENATE_ACK_RESP_MSG_V01_MAX_MSG_LEN; + resp_desc.msg_id = QMI_WLFW_REJUVENATE_ACK_RESP_V01; + resp_desc.ei_array = wlfw_rejuvenate_ack_resp_msg_v01_ei; + + priv->stats.rejuvenate_ack_req++; + ret = qmi_send_req_wait(priv->wlfw_clnt, &req_desc, &req, sizeof(req), + &resp_desc, &resp, sizeof(resp), + WLFW_TIMEOUT_MS); + if (ret < 0) { + icnss_pr_err("Send rejuvenate ack req failed %d\n", ret); + goto out; + } + + if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { + icnss_pr_err("QMI rejuvenate ack request rejected, result:%d error %d\n", + resp.resp.result, resp.resp.error); + ret = resp.resp.result; + goto out; + } + priv->stats.rejuvenate_ack_resp++; + return 0; + +out: + priv->stats.rejuvenate_ack_err++; + ICNSS_ASSERT(false); + return ret; +} + static void icnss_qmi_wlfw_clnt_notify_work(struct work_struct *work) { int ret; @@ -1430,6 +1481,8 @@ static void icnss_qmi_wlfw_clnt_ind(struct qmi_handle *handle, unsigned int msg_id, void *msg, unsigned int msg_len, void *ind_cb_priv) { + struct icnss_event_pd_service_down_data *event_data; + if (!penv) return; @@ -1450,6 +1503,17 @@ static void icnss_qmi_wlfw_clnt_ind(struct qmi_handle *handle, msg_id); icnss_qmi_pin_connect_result_ind(msg, msg_len); break; + case QMI_WLFW_REJUVENATE_IND_V01: + icnss_pr_dbg("Received Rejuvenate Indication msg_id 0x%x, state: 0x%lx\n", + msg_id, penv->state); + event_data = kzalloc(sizeof(*event_data), GFP_KERNEL); + if (event_data == NULL) + return; + event_data->crashed = true; + event_data->fw_rejuvenate = true; + icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN, + 0, event_data); + break; default: icnss_pr_err("Invalid msg_id 0x%x\n", msg_id); break; @@ -1773,6 +1837,9 @@ static int icnss_driver_event_pd_service_down(struct icnss_priv *priv, else icnss_call_driver_remove(priv); + if (event_data->fw_rejuvenate) + wlfw_rejuvenate_ack_send_sync_msg(priv); + out: ret = icnss_hw_power_off(priv); @@ -3103,6 +3170,9 @@ static int icnss_stats_show(struct seq_file *s, void *data) ICNSS_STATS_DUMP(s, priv, vbatt_req); ICNSS_STATS_DUMP(s, priv, vbatt_resp); ICNSS_STATS_DUMP(s, priv, vbatt_req_err); + ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_req); + ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_resp); + ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_err); seq_puts(s, "\n<------------------ PM stats ------------------->\n"); ICNSS_STATS_DUMP(s, priv, pm_suspend); From 089656e381b3cf9259a9d0b408b2b7c7e150d72f Mon Sep 17 00:00:00 2001 From: Yuanyuan Liu Date: Tue, 27 Dec 2016 11:18:12 -0800 Subject: [PATCH 3/4] icnss: Add debugfs support of configuring fw features Add debugfs support of configuring fw features dynamically. CRs-Fixed: 1107443 Change-Id: I0021ac61467174c794fa8f6ef130479b166c1425 Signed-off-by: Yuanyuan Liu --- drivers/soc/qcom/icnss.c | 179 ++++++++++++++++++++++++++++++--------- 1 file changed, 141 insertions(+), 38 deletions(-) diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c index 0e534625ef1e..c6db3daaf812 100644 --- a/drivers/soc/qcom/icnss.c +++ b/drivers/soc/qcom/icnss.c @@ -143,6 +143,7 @@ enum icnss_debug_quirks { SSR_ONLY, PDR_ONLY, VBATT_DISABLE, + FW_REJUVENATE_ENABLE, }; #define ICNSS_QUIRKS_DEFAULT BIT(VBATT_DISABLE) @@ -150,6 +151,9 @@ enum icnss_debug_quirks { unsigned long quirks = ICNSS_QUIRKS_DEFAULT; module_param(quirks, ulong, 0600); +uint64_t dynamic_feature_mask = QMI_WLFW_FW_REJUVENATE_V01; +module_param(dynamic_feature_mask, ullong, 0600); + void *icnss_ipc_log_context; #ifdef CONFIG_ICNSS_DEBUG @@ -1005,8 +1009,10 @@ static int wlfw_ind_register_send_sync_msg(void) req.msa_ready_enable = 1; req.pin_connect_result_enable_valid = 1; req.pin_connect_result_enable = 1; - req.rejuvenate_enable_valid = 1; - req.rejuvenate_enable = 1; + if (test_bit(FW_REJUVENATE_ENABLE, &quirks)) { + req.rejuvenate_enable_valid = 1; + req.rejuvenate_enable = 1; + } req_desc.max_msg_len = WLFW_IND_REGISTER_REQ_MSG_V01_MAX_MSG_LEN; req_desc.msg_id = QMI_WLFW_IND_REGISTER_REQ_V01; @@ -1441,6 +1447,69 @@ out: return ret; } +static int wlfw_dynamic_feature_mask_send_sync_msg(struct icnss_priv *priv, + uint64_t dynamic_feature_mask) +{ + int ret; + struct wlfw_dynamic_feature_mask_req_msg_v01 req; + struct wlfw_dynamic_feature_mask_resp_msg_v01 resp; + struct msg_desc req_desc, resp_desc; + + if (!test_bit(ICNSS_WLFW_QMI_CONNECTED, &priv->state)) { + icnss_pr_err("Invalid state for dynamic feature: 0x%lx\n", + priv->state); + return -EINVAL; + } + + if (!test_bit(FW_REJUVENATE_ENABLE, &quirks)) { + icnss_pr_dbg("FW rejuvenate is disabled from quirks\n"); + dynamic_feature_mask &= ~QMI_WLFW_FW_REJUVENATE_V01; + } + + icnss_pr_dbg("Sending dynamic feature mask request, val 0x%llx, state: 0x%lx\n", + dynamic_feature_mask, priv->state); + + memset(&req, 0, sizeof(req)); + memset(&resp, 0, sizeof(resp)); + + req.mask_valid = 1; + req.mask = dynamic_feature_mask; + + req_desc.max_msg_len = + WLFW_DYNAMIC_FEATURE_MASK_REQ_MSG_V01_MAX_MSG_LEN; + req_desc.msg_id = QMI_WLFW_DYNAMIC_FEATURE_MASK_REQ_V01; + req_desc.ei_array = wlfw_dynamic_feature_mask_req_msg_v01_ei; + + resp_desc.max_msg_len = + WLFW_DYNAMIC_FEATURE_MASK_RESP_MSG_V01_MAX_MSG_LEN; + resp_desc.msg_id = QMI_WLFW_DYNAMIC_FEATURE_MASK_RESP_V01; + resp_desc.ei_array = wlfw_dynamic_feature_mask_resp_msg_v01_ei; + + ret = qmi_send_req_wait(priv->wlfw_clnt, &req_desc, &req, sizeof(req), + &resp_desc, &resp, sizeof(resp), + WLFW_TIMEOUT_MS); + if (ret < 0) { + icnss_pr_err("Send dynamic feature mask req failed %d\n", ret); + goto out; + } + + if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { + icnss_pr_err("QMI dynamic feature mask request rejected, result:%d error %d\n", + resp.resp.result, resp.resp.error); + ret = resp.resp.result; + goto out; + } + + icnss_pr_dbg("prev_mask_valid %u, prev_mask 0x%llx, curr_maks_valid %u, curr_mask 0x%llx\n", + resp.prev_mask_valid, resp.prev_mask, + resp.curr_mask_valid, resp.curr_mask); + + return 0; + +out: + return ret; +} + static void icnss_qmi_wlfw_clnt_notify_work(struct work_struct *work) { int ret; @@ -1586,6 +1655,11 @@ static int icnss_driver_event_server_arrive(void *data) if (ret < 0) goto err_setup_msa; + ret = wlfw_dynamic_feature_mask_send_sync_msg(penv, + dynamic_feature_mask); + if (ret < 0) + goto err_setup_msa; + icnss_init_vph_monitor(penv); return ret; @@ -2843,33 +2917,35 @@ static void icnss_smmu_deinit(struct icnss_priv *priv) priv->smmu_mapping = NULL; } -static int icnss_test_mode_show(struct seq_file *s, void *data) +static int icnss_fw_debug_show(struct seq_file *s, void *data) { struct icnss_priv *priv = s->private; - seq_puts(s, "0 : Test mode disable\n"); - seq_puts(s, "1 : WLAN Firmware test\n"); - seq_puts(s, "2 : CCPM test\n"); + seq_puts(s, "\nUsage: echo > /icnss/fw_debug\n"); - seq_puts(s, "\n"); + seq_puts(s, "\nCMD: test_mode\n"); + seq_puts(s, " VAL: 0 (Test mode disable)\n"); + seq_puts(s, " VAL: 1 (WLAN FW test)\n"); + seq_puts(s, " VAL: 2 (CCPM test)\n"); + + seq_puts(s, "\nCMD: dynamic_feature_mask\n"); + seq_puts(s, " VAL: (64 bit feature mask)\n"); if (!test_bit(ICNSS_FW_READY, &priv->state)) { - seq_puts(s, "Firmware is not ready yet!, wait for FW READY\n"); + seq_puts(s, "Firmware is not ready yet, can't run test_mode!\n"); goto out; } if (test_bit(ICNSS_DRIVER_PROBED, &priv->state)) { - seq_puts(s, "Machine mode is running, can't run test mode!\n"); + seq_puts(s, "Machine mode is running, can't run test_mode!\n"); goto out; } if (test_bit(ICNSS_FW_TEST_MODE, &priv->state)) { - seq_puts(s, "Test mode is running!\n"); + seq_puts(s, "test_mode is running, can't run test_mode!\n"); goto out; } - seq_puts(s, "Test can be run, Have fun!\n"); - out: seq_puts(s, "\n"); return 0; @@ -2955,31 +3031,58 @@ out: return ret; } -static ssize_t icnss_test_mode_write(struct file *fp, const char __user *buf, +static ssize_t icnss_fw_debug_write(struct file *fp, + const char __user *user_buf, size_t count, loff_t *off) { struct icnss_priv *priv = ((struct seq_file *)fp->private_data)->private; - int ret; - u32 val; + char buf[64]; + char *sptr, *token; + unsigned int len = 0; + char *cmd; + uint64_t val; + const char *delim = " "; + int ret = 0; - ret = kstrtou32_from_user(buf, count, 0, &val); - if (ret) - return ret; + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EINVAL; - switch (val) { - case 0: - ret = icnss_test_mode_fw_test_off(priv); - break; - case 1: - ret = icnss_test_mode_fw_test(priv, ICNSS_WALTEST); - break; - case 2: - ret = icnss_test_mode_fw_test(priv, ICNSS_CCPM); - break; - default: - ret = -EINVAL; - break; + buf[len] = '\0'; + sptr = buf; + + token = strsep(&sptr, delim); + if (!token) + return -EINVAL; + if (!sptr) + return -EINVAL; + cmd = token; + + token = strsep(&sptr, delim); + if (!token) + return -EINVAL; + if (kstrtou64(token, 0, &val)) + return -EINVAL; + + if (strcmp(cmd, "test_mode") == 0) { + switch (val) { + case 0: + ret = icnss_test_mode_fw_test_off(priv); + break; + case 1: + ret = icnss_test_mode_fw_test(priv, ICNSS_WALTEST); + break; + case 2: + ret = icnss_test_mode_fw_test(priv, ICNSS_CCPM); + break; + default: + return -EINVAL; + } + } else if (strcmp(cmd, "dynamic_feature_mask") == 0) { + ret = wlfw_dynamic_feature_mask_send_sync_msg(priv, val); + } else { + return -EINVAL; } if (ret) @@ -2991,16 +3094,16 @@ static ssize_t icnss_test_mode_write(struct file *fp, const char __user *buf, return count; } -static int icnss_test_mode_open(struct inode *inode, struct file *file) +static int icnss_fw_debug_open(struct inode *inode, struct file *file) { - return single_open(file, icnss_test_mode_show, inode->i_private); + return single_open(file, icnss_fw_debug_show, inode->i_private); } -static const struct file_operations icnss_test_mode_fops = { +static const struct file_operations icnss_fw_debug_fops = { .read = seq_read, - .write = icnss_test_mode_write, + .write = icnss_fw_debug_write, .release = single_release, - .open = icnss_test_mode_open, + .open = icnss_fw_debug_open, .owner = THIS_MODULE, .llseek = seq_lseek, }; @@ -3428,8 +3531,8 @@ static int icnss_debugfs_create(struct icnss_priv *priv) priv->root_dentry = root_dentry; - debugfs_create_file("test_mode", 0644, root_dentry, priv, - &icnss_test_mode_fops); + debugfs_create_file("fw_debug", 0644, root_dentry, priv, + &icnss_fw_debug_fops); debugfs_create_file("stats", 0644, root_dentry, priv, &icnss_stats_fops); From 17375362311acc7a92491add6f6558ecbafbd809 Mon Sep 17 00:00:00 2001 From: Sameer Thalappil Date: Mon, 9 Jan 2017 12:18:08 -0800 Subject: [PATCH 4/4] icnss: Add support for host initiated recovery Add an API that can trigger WLAN PDR. WLAN host driver can use this API to trigger PDR in the cases where it fails to communicate with FW over it's native copy engine interface. CRs-Fixed: 1110381 Change-Id: I7a076ba023b3e48c9e5473ec4895f50c71816831 Signed-off-by: Sameer Thalappil --- drivers/soc/qcom/icnss.c | 78 ++++++++++++++++++++++++++++++++-------- include/soc/qcom/icnss.h | 1 + 2 files changed, 65 insertions(+), 14 deletions(-) diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c index c6db3daaf812..548d558c80d4 100644 --- a/drivers/soc/qcom/icnss.c +++ b/drivers/soc/qcom/icnss.c @@ -272,6 +272,12 @@ struct icnss_wlan_mac_addr { uint32_t no_of_mac_addr_set; }; +struct service_notifier_context { + void *handle; + uint32_t instance_id; + char name[QMI_SERVREG_LOC_NAME_LENGTH_V01 + 1]; +}; + static struct icnss_priv { uint32_t magic; struct platform_device *pdev; @@ -309,7 +315,7 @@ static struct icnss_priv { spinlock_t on_off_lock; struct icnss_stats stats; struct work_struct service_notifier_work; - void **service_notifier; + struct service_notifier_context *service_notifier; struct notifier_block service_notifier_nb; int total_domains; struct notifier_block get_service_nb; @@ -2114,8 +2120,9 @@ static int icnss_pdr_unregister_notifier(struct icnss_priv *priv) return 0; for (i = 0; i < priv->total_domains; i++) - service_notif_unregister_notifier(priv->service_notifier[i], - &priv->service_notifier_nb); + service_notif_unregister_notifier( + priv->service_notifier[i].handle, + &priv->service_notifier_nb); kfree(priv->service_notifier); @@ -2168,7 +2175,7 @@ static int icnss_get_service_location_notify(struct notifier_block *nb, int curr_state; int ret; int i; - void **handle; + struct service_notifier_context *notifier; icnss_pr_dbg("Get service notify opcode: %lu, state: 0x%lx\n", opcode, priv->state); @@ -2182,9 +2189,10 @@ static int icnss_get_service_location_notify(struct notifier_block *nb, goto out; } - handle = kcalloc(pd->total_domains, sizeof(void *), GFP_KERNEL); - - if (!handle) { + notifier = kcalloc(pd->total_domains, + sizeof(struct service_notifier_context), + GFP_KERNEL); + if (!notifier) { ret = -ENOMEM; goto out; } @@ -2196,21 +2204,24 @@ static int icnss_get_service_location_notify(struct notifier_block *nb, pd->domain_list[i].name, pd->domain_list[i].instance_id); - handle[i] = + notifier[i].handle = service_notif_register_notifier(pd->domain_list[i].name, pd->domain_list[i].instance_id, &priv->service_notifier_nb, &curr_state); + notifier[i].instance_id = pd->domain_list[i].instance_id; + strlcpy(notifier[i].name, pd->domain_list[i].name, + QMI_SERVREG_LOC_NAME_LENGTH_V01 + 1); - if (IS_ERR(handle[i])) { + if (IS_ERR(notifier[i].handle)) { icnss_pr_err("%d: Unable to register notifier for %s(0x%x)\n", i, pd->domain_list->name, pd->domain_list->instance_id); - ret = PTR_ERR(handle[i]); + ret = PTR_ERR(notifier[i].handle); goto free_handle; } } - priv->service_notifier = handle; + priv->service_notifier = notifier; priv->total_domains = pd->total_domains; set_bit(ICNSS_PDR_ENABLED, &priv->state); @@ -2221,11 +2232,11 @@ static int icnss_get_service_location_notify(struct notifier_block *nb, free_handle: for (i = 0; i < pd->total_domains; i++) { - if (handle[i]) - service_notif_unregister_notifier(handle[i], + if (notifier[i].handle) + service_notif_unregister_notifier(notifier[i].handle, &priv->service_notifier_nb); } - kfree(handle); + kfree(notifier); out: icnss_pr_err("PD restart not enabled: %d, state: 0x%lx\n", ret, @@ -2854,6 +2865,42 @@ out: } EXPORT_SYMBOL(icnss_get_wlan_mac_address); +int icnss_trigger_recovery(struct device *dev) +{ + int ret = 0; + struct icnss_priv *priv = dev_get_drvdata(dev); + + if (priv->magic != ICNSS_MAGIC) { + icnss_pr_err("Invalid drvdata: magic 0x%x\n", priv->magic); + ret = -EINVAL; + goto out; + } + + if (test_bit(ICNSS_PD_RESTART, &priv->state)) { + icnss_pr_err("PD recovery already in progress: state: 0x%lx\n", + priv->state); + ret = -EPERM; + goto out; + } + + if (!priv->service_notifier[0].handle) { + icnss_pr_err("Invalid handle during recovery\n"); + ret = -EINVAL; + goto out; + } + + /* + * Initiate PDR, required only for the first instance + */ + ret = service_notif_pd_restart(priv->service_notifier[0].name, + priv->service_notifier[0].instance_id); + +out: + return ret; +} +EXPORT_SYMBOL(icnss_trigger_recovery); + + static int icnss_smmu_init(struct icnss_priv *priv) { struct dma_iommu_mapping *mapping; @@ -3076,6 +3123,9 @@ static ssize_t icnss_fw_debug_write(struct file *fp, case 2: ret = icnss_test_mode_fw_test(priv, ICNSS_CCPM); break; + case 3: + ret = icnss_trigger_recovery(&priv->pdev->dev); + break; default: return -EINVAL; } diff --git a/include/soc/qcom/icnss.h b/include/soc/qcom/icnss.h index 14892a05bd19..7770f06b5e08 100644 --- a/include/soc/qcom/icnss.h +++ b/include/soc/qcom/icnss.h @@ -127,5 +127,6 @@ extern bool icnss_is_qmi_disable(void); extern bool icnss_is_fw_ready(void); extern int icnss_set_wlan_mac_address(const u8 *in, const uint32_t len); extern u8 *icnss_get_wlan_mac_address(struct device *dev, uint32_t *num); +extern int icnss_trigger_recovery(struct device *dev); #endif /* _ICNSS_WLAN_H_ */