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__*/