From 05a4d865806ce5e4ca8e08d718170b6794a0e78d Mon Sep 17 00:00:00 2001 From: Naman Padhiar Date: Thu, 1 Apr 2021 20:06:24 +0530 Subject: [PATCH] icnss2: Setup DMS QMI client for WLAN MAC address WLAN MAC address can be provisioned using NV memory based setup. DMS QMI server connection is required to retrieve this MAC address. Thus setup DMS QMI client in icnss platform driver and send this MAC address for WLAN FW before mission mode enable. 1. Move DMS QMI files to cnss_util from cnss2. 2. Read "use-nv-mac" property from DT node to decide whether reding MAC address from NV memory is mandatory or not. If mandatory wait for DMS QMI server to read MAC address. If not mandatory read MAC address if DMS server is available otherwise continue. Change-Id: Iddc5a7180c9eea2a8a9507f08b1b383bc095f2bc Signed-off-by: Naman Padhiar --- drivers/net/wireless/cnss2/Makefile | 2 +- drivers/net/wireless/cnss_utils/Makefile | 2 +- .../device_management_service_v01.c | 9 +- .../device_management_service_v01.h | 2 +- drivers/soc/qcom/icnss2/debug.c | 3 + drivers/soc/qcom/icnss2/main.c | 64 +++++- drivers/soc/qcom/icnss2/main.h | 10 + drivers/soc/qcom/icnss2/qmi.c | 197 ++++++++++++++++++ drivers/soc/qcom/icnss2/qmi.h | 30 ++- 9 files changed, 309 insertions(+), 10 deletions(-) rename drivers/net/wireless/{cnss2 => cnss_utils}/device_management_service_v01.c (87%) rename drivers/net/wireless/{cnss2 => cnss_utils}/device_management_service_v01.h (93%) diff --git a/drivers/net/wireless/cnss2/Makefile b/drivers/net/wireless/cnss2/Makefile index 225622e76a68..c031c5c90bd0 100644 --- a/drivers/net/wireless/cnss2/Makefile +++ b/drivers/net/wireless/cnss2/Makefile @@ -9,4 +9,4 @@ cnss2-y += debug.o cnss2-y += pci.o cnss2-y += power.o cnss2-y += genl.o -cnss2-$(CONFIG_CNSS2_QMI) += qmi.o coexistence_service_v01.o ip_multimedia_subsystem_private_service_v01.o device_management_service_v01.o +cnss2-$(CONFIG_CNSS2_QMI) += qmi.o coexistence_service_v01.o ip_multimedia_subsystem_private_service_v01.o diff --git a/drivers/net/wireless/cnss_utils/Makefile b/drivers/net/wireless/cnss_utils/Makefile index 1b7088904d2e..7ca7d0566359 100644 --- a/drivers/net/wireless/cnss_utils/Makefile +++ b/drivers/net/wireless/cnss_utils/Makefile @@ -1,4 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_CNSS_UTILS) += cnss_utils.o -obj-$(CONFIG_CNSS_QMI_SVC) += wlan_firmware_service_v01.o +obj-$(CONFIG_CNSS_QMI_SVC) += wlan_firmware_service_v01.o device_management_service_v01.o diff --git a/drivers/net/wireless/cnss2/device_management_service_v01.c b/drivers/net/wireless/cnss_utils/device_management_service_v01.c similarity index 87% rename from drivers/net/wireless/cnss2/device_management_service_v01.c rename to drivers/net/wireless/cnss_utils/device_management_service_v01.c index a714d64239a5..130a018a4c57 100644 --- a/drivers/net/wireless/cnss2/device_management_service_v01.c +++ b/drivers/net/wireless/cnss_utils/device_management_service_v01.c @@ -1,8 +1,8 @@ // SPDX-License-Identifier: GPL-2.0-only -/* Copyright (c) 2020, The Linux Foundation. All rights reserved. */ +/* Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. */ #include - +#include #include "device_management_service_v01.h" struct qmi_elem_info dms_get_mac_address_req_msg_v01_ei[] = { @@ -22,6 +22,7 @@ struct qmi_elem_info dms_get_mac_address_req_msg_v01_ei[] = { .tlv_type = QMI_COMMON_TLV_TYPE, }, }; +EXPORT_SYMBOL(dms_get_mac_address_req_msg_v01_ei); struct qmi_elem_info dms_get_mac_address_resp_msg_v01_ei[] = { { @@ -71,3 +72,7 @@ struct qmi_elem_info dms_get_mac_address_resp_msg_v01_ei[] = { .tlv_type = QMI_COMMON_TLV_TYPE, }, }; +EXPORT_SYMBOL(dms_get_mac_address_resp_msg_v01_ei); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("WLAN qmi service DMS"); diff --git a/drivers/net/wireless/cnss2/device_management_service_v01.h b/drivers/net/wireless/cnss_utils/device_management_service_v01.h similarity index 93% rename from drivers/net/wireless/cnss2/device_management_service_v01.h rename to drivers/net/wireless/cnss_utils/device_management_service_v01.h index ba0a40402f8c..8dbdd2e8ef4b 100644 --- a/drivers/net/wireless/cnss2/device_management_service_v01.h +++ b/drivers/net/wireless/cnss_utils/device_management_service_v01.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0-only - * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. */ #ifndef DEVICE_MANAGEMENT_SERVICE_V01_H diff --git a/drivers/soc/qcom/icnss2/debug.c b/drivers/soc/qcom/icnss2/debug.c index 588892f5be46..be0734253290 100644 --- a/drivers/soc/qcom/icnss2/debug.c +++ b/drivers/soc/qcom/icnss2/debug.c @@ -411,6 +411,9 @@ static int icnss_stats_show_state(struct seq_file *s, struct icnss_priv *priv) continue; case ICNSS_COLD_BOOT_CAL: seq_puts(s, "COLD BOOT CALIBRATION"); + continue; + case ICNSS_QMI_DMS_CONNECTED: + seq_puts(s, "DMS_CONNECTED"); } seq_printf(s, "UNKNOWN-%d", i); diff --git a/drivers/soc/qcom/icnss2/main.c b/drivers/soc/qcom/icnss2/main.c index 82701ea7e5e6..ad7a17c4d367 100644 --- a/drivers/soc/qcom/icnss2/main.c +++ b/drivers/soc/qcom/icnss2/main.c @@ -84,7 +84,8 @@ uint64_t dynamic_feature_mask = ICNSS_DEFAULT_FEATURE_MASK; #define ICNSS_EVENT_UNINTERRUPTIBLE BIT(1) #define ICNSS_EVENT_SYNC_UNINTERRUPTIBLE (ICNSS_EVENT_UNINTERRUPTIBLE | \ ICNSS_EVENT_SYNC) - +#define ICNSS_DMS_QMI_CONNECTION_WAIT_MS 50 +#define ICNSS_DMS_QMI_CONNECTION_WAIT_RETRY 200 enum icnss_pdr_cause_index { ICNSS_FW_CRASH, @@ -540,6 +541,42 @@ int icnss_call_driver_uevent(struct icnss_priv *priv, return priv->ops->uevent(&priv->pdev->dev, &uevent_data); } +static int icnss_setup_dms_mac(struct icnss_priv *priv) +{ + int i; + int ret = 0; + + ret = icnss_qmi_get_dms_mac(priv); + if (ret == 0 && priv->dms.mac_valid) + goto qmi_send; + + /* DTSI property use-nv-mac is used to force DMS MAC address for WLAN. + * Thus assert on failure to get MAC from DMS even after retries + */ + if (priv->use_nv_mac) { + for (i = 0; i < ICNSS_DMS_QMI_CONNECTION_WAIT_RETRY; i++) { + if (priv->dms.mac_valid) + break; + + ret = icnss_qmi_get_dms_mac(priv); + if (ret != -EAGAIN) + break; + msleep(ICNSS_DMS_QMI_CONNECTION_WAIT_MS); + } + if (!priv->dms.nv_mac_not_prov && !priv->dms.mac_valid) { + icnss_pr_err("Unable to get MAC from DMS after retries\n"); + ICNSS_ASSERT(0); + return -EINVAL; + } + } +qmi_send: + if (priv->dms.mac_valid) + ret = + icnss_wlfw_wlan_mac_req_send_sync(priv, priv->dms.mac, + ARRAY_SIZE(priv->dms.mac)); + return ret; +} + static int icnss_driver_event_server_arrive(struct icnss_priv *priv, void *data) { @@ -635,7 +672,6 @@ static int icnss_driver_event_server_arrive(struct icnss_priv *priv, ret = icnss_wlfw_bdf_dnld_send_sync(priv, priv->ctrl_params.bdf_type); - } if (priv->device_id == ADRASTEA_DEVICE_ID) { @@ -817,10 +853,13 @@ static int icnss_driver_event_fw_ready_ind(struct icnss_priv *priv, void *data) goto out; } - if (test_bit(ICNSS_PD_RESTART, &priv->state)) + if (test_bit(ICNSS_PD_RESTART, &priv->state)) { ret = icnss_pd_restart_complete(priv); - else + } else { + if (priv->device_id == WCN6750_DEVICE_ID) + icnss_setup_dms_mac(priv); ret = icnss_call_driver_probe(priv); + } icnss_vreg_unvote(priv); @@ -2786,6 +2825,10 @@ int icnss_wlan_enable(struct device *dev, struct icnss_wlan_enable_cfg *config, return -EINVAL; } + if (priv->device_id == WCN6750_DEVICE_ID && + !priv->dms.nv_mac_not_prov && !priv->dms.mac_valid) + icnss_setup_dms_mac(priv); + return icnss_send_wlan_enable_to_fw(priv, config, mode, host_version); } EXPORT_SYMBOL(icnss_wlan_enable); @@ -3715,6 +3758,12 @@ static inline void icnss_runtime_pm_deinit(struct icnss_priv *priv) pm_runtime_put_sync(&priv->pdev->dev); } +static inline bool icnss_use_nv_mac(struct icnss_priv *priv) +{ + return of_property_read_bool(priv->pdev->dev.of_node, + "use-nv-mac"); +} + static int icnss_probe(struct platform_device *pdev) { int ret = 0; @@ -3816,6 +3865,9 @@ static int icnss_probe(struct platform_device *pdev) init_completion(&priv->unblock_shutdown); if (priv->device_id == WCN6750_DEVICE_ID) { + ret = icnss_dms_init(priv); + if (ret) + icnss_pr_err("ICNSS DMS init failed %d\n", ret); ret = icnss_genl_init(); if (ret < 0) icnss_pr_err("ICNSS genl init failed %d\n", ret); @@ -3824,6 +3876,9 @@ static int icnss_probe(struct platform_device *pdev) icnss_get_cpr_info(priv); icnss_get_smp2p_info(priv); set_bit(ICNSS_COLD_BOOT_CAL, &priv->state); + priv->use_nv_mac = icnss_use_nv_mac(priv); + icnss_pr_dbg("NV MAC feature is %s\n", + priv->use_nv_mac ? "Mandatory":"Not Mandatory"); INIT_WORK(&wpss_loader, icnss_wpss_load); } @@ -3853,6 +3908,7 @@ static int icnss_remove(struct platform_device *pdev) icnss_pr_info("Removing driver: state: 0x%lx\n", priv->state); if (priv->device_id == WCN6750_DEVICE_ID) { + icnss_dms_deinit(priv); icnss_genl_exit(); icnss_runtime_pm_deinit(priv); } diff --git a/drivers/soc/qcom/icnss2/main.h b/drivers/soc/qcom/icnss2/main.h index b87b2ea55490..2b8db3d0d52b 100644 --- a/drivers/soc/qcom/icnss2/main.h +++ b/drivers/soc/qcom/icnss2/main.h @@ -118,6 +118,7 @@ enum icnss_driver_state { ICNSS_PDR, ICNSS_DEL_SERVER, ICNSS_COLD_BOOT_CAL, + ICNSS_QMI_DMS_CONNECTED, }; struct ce_irq_list { @@ -327,6 +328,12 @@ struct smp2p_out_info { struct qcom_smem_state *smem_state; }; +struct icnss_dms_data { + u8 mac_valid; + u8 nv_mac_not_prov; + u8 mac[QMI_WLFW_MAC_ADDR_SIZE_V01]; +}; + struct icnss_priv { uint32_t magic; struct platform_device *pdev; @@ -353,6 +360,7 @@ struct icnss_priv { dma_addr_t smmu_iova_ipa_current; size_t smmu_iova_ipa_len; struct qmi_handle qmi; + struct qmi_handle qmi_dms; struct list_head event_list; struct list_head soc_wake_msg_list; spinlock_t event_lock; @@ -438,6 +446,8 @@ struct icnss_priv { bool is_chain1_supported; bool chain_reg_info_updated; u32 hw_trc_override; + struct icnss_dms_data dms; + u8 use_nv_mac; }; struct icnss_reg_info { diff --git a/drivers/soc/qcom/icnss2/qmi.c b/drivers/soc/qcom/icnss2/qmi.c index 258de0233ede..1baf6f0ebcce 100644 --- a/drivers/soc/qcom/icnss2/qmi.c +++ b/drivers/soc/qcom/icnss2/qmi.c @@ -47,6 +47,8 @@ #define DEVICE_BAR_SIZE 0x200000 #define M3_SEGMENT_ADDR_MASK 0xFFFFFFFF +#define DMS_QMI_MAX_MSG_LEN SZ_256 +#define DMS_MAC_NOT_PROVISIONED 16 #ifdef CONFIG_ICNSS2_DEBUG bool ignore_fw_timeout; @@ -715,6 +717,201 @@ out: return ret; } +int icnss_qmi_get_dms_mac(struct icnss_priv *priv) +{ + struct dms_get_mac_address_req_msg_v01 req; + struct dms_get_mac_address_resp_msg_v01 resp; + struct qmi_txn txn; + int ret = 0; + + if (!test_bit(ICNSS_QMI_DMS_CONNECTED, &priv->state)) { + icnss_pr_err("DMS QMI connection not established\n"); + return -EAGAIN; + } + icnss_pr_dbg("Requesting DMS MAC address"); + + memset(&resp, 0, sizeof(resp)); + ret = qmi_txn_init(&priv->qmi_dms, &txn, + dms_get_mac_address_resp_msg_v01_ei, &resp); + if (ret < 0) { + icnss_pr_err("Failed to initialize txn for dms, err: %d\n", + ret); + goto out; + } + req.device = DMS_DEVICE_MAC_WLAN_V01; + ret = qmi_send_request(&priv->qmi_dms, NULL, &txn, + QMI_DMS_GET_MAC_ADDRESS_REQ_V01, + DMS_GET_MAC_ADDRESS_REQ_MSG_V01_MAX_MSG_LEN, + dms_get_mac_address_req_msg_v01_ei, &req); + if (ret < 0) { + qmi_txn_cancel(&txn); + icnss_pr_err("Failed to send QMI_DMS_GET_MAC_ADDRESS_REQ_V01, err: %d\n", + ret); + goto out; + } + ret = qmi_txn_wait(&txn, priv->ctrl_params.qmi_timeout); + if (ret < 0) { + icnss_pr_err("Failed to wait for QMI_DMS_GET_MAC_ADDRESS_RESP_V01, err: %d\n", + ret); + goto out; + } + + if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { + if (resp.resp.error == DMS_MAC_NOT_PROVISIONED) { + icnss_pr_err("NV MAC address is not provisioned"); + priv->dms.nv_mac_not_prov = 1; + } else { + icnss_pr_err("QMI_DMS_GET_MAC_ADDRESS_REQ_V01 failed, result: %d, err: %d\n", + resp.resp.result, resp.resp.error); + } + ret = -resp.resp.result; + goto out; + } + if (!resp.mac_address_valid || + resp.mac_address_len != QMI_WLFW_MAC_ADDR_SIZE_V01) { + icnss_pr_err("Invalid MAC address received from DMS\n"); + priv->dms.mac_valid = false; + goto out; + } + priv->dms.mac_valid = true; + memcpy(priv->dms.mac, resp.mac_address, QMI_WLFW_MAC_ADDR_SIZE_V01); + icnss_pr_info("Received DMS MAC: [%pM]\n", priv->dms.mac); +out: + return ret; +} + +int icnss_wlfw_wlan_mac_req_send_sync(struct icnss_priv *priv, + u8 *mac, u32 mac_len) +{ + struct wlfw_mac_addr_req_msg_v01 req; + struct wlfw_mac_addr_resp_msg_v01 resp = {0}; + struct qmi_txn txn; + int ret; + + if (!priv || !mac || mac_len != QMI_WLFW_MAC_ADDR_SIZE_V01) + return -EINVAL; + + ret = qmi_txn_init(&priv->qmi, &txn, + wlfw_mac_addr_resp_msg_v01_ei, &resp); + if (ret < 0) { + icnss_pr_err("Failed to initialize txn for mac req, err: %d\n", + ret); + ret = -EIO; + goto out; + } + + icnss_pr_dbg("Sending WLAN mac req [%pM], state: 0x%lx\n", + mac, priv->state); + memcpy(req.mac_addr, mac, mac_len); + req.mac_addr_valid = 1; + + ret = qmi_send_request(&priv->qmi, NULL, &txn, + QMI_WLFW_MAC_ADDR_REQ_V01, + WLFW_MAC_ADDR_REQ_MSG_V01_MAX_MSG_LEN, + wlfw_mac_addr_req_msg_v01_ei, &req); + if (ret < 0) { + qmi_txn_cancel(&txn); + icnss_pr_err("Failed to send mac req, err: %d\n", ret); + ret = -EIO; + goto out; + } + + ret = qmi_txn_wait(&txn, priv->ctrl_params.qmi_timeout); + if (ret < 0) { + icnss_pr_err("Failed to wait for resp of mac req, err: %d\n", + ret); + ret = -EIO; + goto out; + } + + if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { + icnss_pr_err("WLAN mac req failed, result: %d, err: %d\n", + resp.resp.result); + ret = -resp.resp.result; + } +out: + return ret; +} + +static int icnss_dms_connect_to_server(struct icnss_priv *priv, + unsigned int node, unsigned int port) +{ + struct qmi_handle *qmi_dms = &priv->qmi_dms; + struct sockaddr_qrtr sq = {0}; + int ret = 0; + + sq.sq_family = AF_QIPCRTR; + sq.sq_node = node; + sq.sq_port = port; + + ret = kernel_connect(qmi_dms->sock, (struct sockaddr *)&sq, + sizeof(sq), 0); + if (ret < 0) { + icnss_pr_err("Failed to connect to QMI DMS remote service Node: %d Port: %d\n", + node, port); + goto out; + } + + set_bit(ICNSS_QMI_DMS_CONNECTED, &priv->state); + icnss_pr_info("QMI DMS service connected, state: 0x%lx\n", + priv->state); +out: + return ret; +} + +static int dms_new_server(struct qmi_handle *qmi_dms, + struct qmi_service *service) +{ + struct icnss_priv *priv = + container_of(qmi_dms, struct icnss_priv, qmi_dms); + + if (!service) + return -EINVAL; + + return icnss_dms_connect_to_server(priv, service->node, + service->port); +} + +static void dms_del_server(struct qmi_handle *qmi_dms, + struct qmi_service *service) +{ + struct icnss_priv *priv = + container_of(qmi_dms, struct icnss_priv, qmi_dms); + + clear_bit(ICNSS_QMI_DMS_CONNECTED, &priv->state); + icnss_pr_info("QMI DMS service disconnected, state: 0x%lx\n", + priv->state); +} + +static struct qmi_ops qmi_dms_ops = { + .new_server = dms_new_server, + .del_server = dms_del_server, +}; + +int icnss_dms_init(struct icnss_priv *priv) +{ + int ret = 0; + + ret = qmi_handle_init(&priv->qmi_dms, DMS_QMI_MAX_MSG_LEN, + &qmi_dms_ops, NULL); + if (ret < 0) { + icnss_pr_err("Failed to initialize DMS handle, err: %d\n", ret); + goto out; + } + + ret = qmi_add_lookup(&priv->qmi_dms, DMS_SERVICE_ID_V01, + DMS_SERVICE_VERS_V01, 0); + if (ret < 0) + icnss_pr_err("Failed to add DMS lookup, err: %d\n", ret); +out: + return ret; +} + +void icnss_dms_deinit(struct icnss_priv *priv) +{ + qmi_handle_release(&priv->qmi_dms); +} + static int icnss_get_bdf_file_name(struct icnss_priv *priv, u32 bdf_type, char *filename, u32 filename_len) diff --git a/drivers/soc/qcom/icnss2/qmi.h b/drivers/soc/qcom/icnss2/qmi.h index fbd9849f57e6..186db10d188f 100644 --- a/drivers/soc/qcom/icnss2/qmi.h +++ b/drivers/soc/qcom/icnss2/qmi.h @@ -1,11 +1,13 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved. */ #ifndef __ICNSS_QMI_H__ #define __ICNSS_QMI_H__ +#include "device_management_service_v01.h" + #define QDSS_TRACE_SEG_LEN_MAX 32 #define QDSS_TRACE_FILE_NAME_MAX 16 #define M3_SEGMENTS_SIZE_MAX 10 @@ -172,6 +174,27 @@ int icnss_wlfw_m3_dump_upload_done_send_sync(struct icnss_priv *priv, { return 0; } + +int icnss_qmi_get_dms_mac(struct icnss_priv *priv) +{ + return 0; +} + +int icnss_wlfw_wlan_mac_req_send_sync(struct icnss_priv *priv, + u8 *mac, u32 mac_len) +{ + return 0; +} + +int icnss_dms_init(struct icns_priv *priv) +{ + return 0; +} + +void icnss_dms_deinit(struct icnss_priv *priv) +{ +} + #else int wlfw_ind_register_send_sync_msg(struct icnss_priv *priv); int icnss_connect_to_fw_server(struct icnss_priv *priv, void *data); @@ -220,6 +243,11 @@ int wlfw_send_soc_wake_msg(struct icnss_priv *priv, enum wlfw_soc_wake_enum_v01 type); int icnss_wlfw_m3_dump_upload_done_send_sync(struct icnss_priv *priv, u32 pdev_id, int status); +int icnss_qmi_get_dms_mac(struct icnss_priv *priv); +int icnss_wlfw_wlan_mac_req_send_sync(struct icnss_priv *priv, + u8 *mac, u32 mac_len); +int icnss_dms_init(struct icnss_priv *priv); +void icnss_dms_deinit(struct icnss_priv *priv); #endif #endif /* __ICNSS_QMI_H__*/