Merge "icnss2: Setup DMS QMI client for WLAN MAC address"

This commit is contained in:
qctecmdr 2021-04-12 10:06:12 -07:00 • committed by Gerrit - the friendly Code Review server
commit 3890e7c19d
9 changed files with 309 additions and 10 deletions

View file

@ -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

View file

@ -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

View file

@ -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 <linux/soc/qcom/qmi.h>
#include <linux/module.h>
#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");

View file

@ -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

View file

@ -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);

View file

@ -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);
}

View file

@ -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 {

View file

@ -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)

View file

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