From e6437c634f4e9d504598d69d93f34b82c3135edf Mon Sep 17 00:00:00 2001 From: Yue Ma Date: Tue, 24 Mar 2020 07:26:50 +0800 Subject: [PATCH] cnss2: Add support to send host SMMU IOVA range to firmware With the valid SMMU IOVA range WLAN firmware is able to detect invalid host DDR access before SMMU fault happens. Change-Id: I3a3aac4e1da7bb7747bfe3b352df33fc87221860 Signed-off-by: Yue Ma --- drivers/net/wireless/cnss2/bus.c | 33 ++++++++++++- drivers/net/wireless/cnss2/bus.h | 6 ++- drivers/net/wireless/cnss2/pci.c | 28 +++++++++++ drivers/net/wireless/cnss2/pci.h | 3 ++ drivers/net/wireless/cnss2/qmi.c | 12 +++++ .../cnss2/wlan_firmware_service_v01.c | 47 ++++++++++++++++++- .../cnss2/wlan_firmware_service_v01.h | 13 ++++- 7 files changed, 136 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/cnss2/bus.c b/drivers/net/wireless/cnss2/bus.c index f97c3f0d3d1d..c590f53af8f2 100644 --- a/drivers/net/wireless/cnss2/bus.c +++ b/drivers/net/wireless/cnss2/bus.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -/* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. */ +/* Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. */ #include "bus.h" #include "debug.h" @@ -451,3 +451,34 @@ int cnss_bus_debug_reg_write(struct cnss_plat_data *plat_priv, u32 offset, return 0; } } + +int cnss_bus_get_iova(struct cnss_plat_data *plat_priv, u64 *addr, u64 *size) +{ + if (!plat_priv) + return -ENODEV; + + switch (plat_priv->bus_type) { + case CNSS_BUS_PCI: + return cnss_pci_get_iova(plat_priv->bus_priv, addr, size); + default: + cnss_pr_err("Unsupported bus type: %d\n", + plat_priv->bus_type); + return -EINVAL; + } +} + +int cnss_bus_get_iova_ipa(struct cnss_plat_data *plat_priv, u64 *addr, + u64 *size) +{ + if (!plat_priv) + return -ENODEV; + + switch (plat_priv->bus_type) { + case CNSS_BUS_PCI: + return cnss_pci_get_iova_ipa(plat_priv->bus_priv, addr, size); + default: + cnss_pr_err("Unsupported bus type: %d\n", + plat_priv->bus_type); + return -EINVAL; + } +} diff --git a/drivers/net/wireless/cnss2/bus.h b/drivers/net/wireless/cnss2/bus.h index 7c719f5f263e..4b9e91f5e0e8 100644 --- a/drivers/net/wireless/cnss2/bus.h +++ b/drivers/net/wireless/cnss2/bus.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0-only */ -/* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. */ +/* Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. */ #ifndef _CNSS_BUS_H #define _CNSS_BUS_H @@ -52,5 +52,7 @@ int cnss_bus_debug_reg_read(struct cnss_plat_data *plat_priv, u32 offset, u32 *val); int cnss_bus_debug_reg_write(struct cnss_plat_data *plat_priv, u32 offset, u32 val); - +int cnss_bus_get_iova(struct cnss_plat_data *plat_priv, u64 *addr, u64 *size); +int cnss_bus_get_iova_ipa(struct cnss_plat_data *plat_priv, u64 *addr, + u64 *size); #endif /* _CNSS_BUS_H */ diff --git a/drivers/net/wireless/cnss2/pci.c b/drivers/net/wireless/cnss2/pci.c index fb7ffb264396..00193d50e849 100644 --- a/drivers/net/wireless/cnss2/pci.c +++ b/drivers/net/wireless/cnss2/pci.c @@ -3297,6 +3297,34 @@ static void cnss_pci_deinit_smmu(struct cnss_pci_data *pci_priv) pci_priv->iommu_domain = NULL; } +int cnss_pci_get_iova(struct cnss_pci_data *pci_priv, u64 *addr, u64 *size) +{ + if (!pci_priv) + return -ENODEV; + + if (!pci_priv->smmu_iova_len) + return -EINVAL; + + *addr = pci_priv->smmu_iova_start; + *size = pci_priv->smmu_iova_len; + + return 0; +} + +int cnss_pci_get_iova_ipa(struct cnss_pci_data *pci_priv, u64 *addr, u64 *size) +{ + if (!pci_priv) + return -ENODEV; + + if (!pci_priv->smmu_iova_ipa_len) + return -EINVAL; + + *addr = pci_priv->smmu_iova_ipa_start; + *size = pci_priv->smmu_iova_ipa_len; + + return 0; +} + struct iommu_domain *cnss_smmu_get_domain(struct device *dev) { struct cnss_pci_data *pci_priv = cnss_get_pci_priv(to_pci_dev(dev)); diff --git a/drivers/net/wireless/cnss2/pci.h b/drivers/net/wireless/cnss2/pci.h index 3dfa83499e6d..3e872d17c19d 100644 --- a/drivers/net/wireless/cnss2/pci.h +++ b/drivers/net/wireless/cnss2/pci.h @@ -205,5 +205,8 @@ int cnss_pci_debug_reg_read(struct cnss_pci_data *pci_priv, u32 offset, u32 *val); int cnss_pci_debug_reg_write(struct cnss_pci_data *pci_priv, u32 offset, u32 val); +int cnss_pci_get_iova(struct cnss_pci_data *pci_priv, u64 *addr, u64 *size); +int cnss_pci_get_iova_ipa(struct cnss_pci_data *pci_priv, u64 *addr, + u64 *size); #endif /* _CNSS_PCI_H */ diff --git a/drivers/net/wireless/cnss2/qmi.c b/drivers/net/wireless/cnss2/qmi.c index 3904b2c59213..ea73016bf466 100644 --- a/drivers/net/wireless/cnss2/qmi.c +++ b/drivers/net/wireless/cnss2/qmi.c @@ -157,6 +157,8 @@ static int cnss_wlfw_host_cap_send_sync(struct cnss_plat_data *plat_priv) struct wlfw_host_cap_resp_msg_v01 *resp; struct qmi_txn txn; int ret = 0; + u64 iova_start = 0, iova_size = 0, + iova_ipa_start = 0, iova_ipa_size = 0; cnss_pr_dbg("Sending host capability message, state: 0x%lx\n", plat_priv->driver_state); @@ -198,6 +200,16 @@ static int cnss_wlfw_host_cap_send_sync(struct cnss_plat_data *plat_priv) req->cal_done = plat_priv->cal_done; cnss_pr_dbg("Calibration done is %d\n", plat_priv->cal_done); + if (!cnss_bus_get_iova(plat_priv, &iova_start, &iova_size) && + !cnss_bus_get_iova_ipa(plat_priv, &iova_ipa_start, + &iova_ipa_size)) { + req->ddr_range_valid = 1; + req->ddr_range[0].start = iova_start; + req->ddr_range[0].size = iova_size + iova_ipa_size; + cnss_pr_dbg("Sending iova starting 0x%llx with size 0x%llx\n", + req->ddr_range[0].start, req->ddr_range[0].size); + } + ret = qmi_txn_init(&plat_priv->qmi_wlfw, &txn, wlfw_host_cap_resp_msg_v01_ei, resp); if (ret < 0) { diff --git a/drivers/net/wireless/cnss2/wlan_firmware_service_v01.c b/drivers/net/wireless/cnss2/wlan_firmware_service_v01.c index 682a20b88950..2a24cd25878b 100644 --- a/drivers/net/wireless/cnss2/wlan_firmware_service_v01.c +++ b/drivers/net/wireless/cnss2/wlan_firmware_service_v01.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -/* Copyright (c) 2015-2019, The Linux Foundation. All rights reserved. */ +/* Copyright (c) 2015-2020, The Linux Foundation. All rights reserved. */ #include "wlan_firmware_service_v01.h" @@ -431,6 +431,32 @@ static struct qmi_elem_info wlfw_fw_version_info_s_v01_ei[] = { }, }; +static struct qmi_elem_info wlfw_host_ddr_range_s_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_8_BYTE, + .elem_len = 1, + .elem_size = sizeof(u64), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct wlfw_host_ddr_range_s_v01, + start), + }, + { + .data_type = QMI_UNSIGNED_8_BYTE, + .elem_len = 1, + .elem_size = sizeof(u64), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct wlfw_host_ddr_range_s_v01, + size), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + struct qmi_elem_info wlfw_ind_register_req_msg_v01_ei[] = { { .data_type = QMI_OPT_FLAG, @@ -2559,6 +2585,25 @@ struct qmi_elem_info wlfw_host_cap_req_msg_v01_ei[] = { .offset = offsetof(struct wlfw_host_cap_req_msg_v01, cal_duration), }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x1E, + .offset = offsetof(struct wlfw_host_cap_req_msg_v01, + ddr_range_valid), + }, + { + .data_type = QMI_STRUCT, + .elem_len = QMI_WLFW_MAX_HOST_DDR_RANGE_SIZE_V01, + .elem_size = sizeof(struct wlfw_host_ddr_range_s_v01), + .array_type = STATIC_ARRAY, + .tlv_type = 0x1E, + .offset = offsetof(struct wlfw_host_cap_req_msg_v01, + ddr_range), + .ei_array = wlfw_host_ddr_range_s_v01_ei, + }, { .data_type = QMI_EOTI, .array_type = NO_ARRAY, diff --git a/drivers/net/wireless/cnss2/wlan_firmware_service_v01.h b/drivers/net/wireless/cnss2/wlan_firmware_service_v01.h index 0e18d8000c3f..5ca2866c4d2c 100644 --- a/drivers/net/wireless/cnss2/wlan_firmware_service_v01.h +++ b/drivers/net/wireless/cnss2/wlan_firmware_service_v01.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0-only */ -/* Copyright (c) 2015-2019, The Linux Foundation. All rights reserved. */ +/* Copyright (c) 2015-2020, The Linux Foundation. All rights reserved. */ #ifndef WLAN_FIRMWARE_SERVICE_V01_H #define WLAN_FIRMWARE_SERVICE_V01_H @@ -89,6 +89,7 @@ #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_HOST_DDR_RANGE_SIZE_V01 3 #define QMI_WLFW_MAX_TIMESTAMP_LEN_V01 32 #define QMI_WLFW_MAX_ATHDIAG_DATA_SIZE_V01 6144 #define QMI_WLFW_MAX_WFC_CALL_STATUS_DATA_SIZE_V01 256 @@ -244,6 +245,11 @@ struct wlfw_fw_version_info_s_v01 { char fw_build_timestamp[QMI_WLFW_MAX_TIMESTAMP_LEN_V01 + 1]; }; +struct wlfw_host_ddr_range_s_v01 { + u64 start; + u64 size; +}; + struct wlfw_ind_register_req_msg_v01 { u8 fw_ready_enable_valid; u8 fw_ready_enable; @@ -661,9 +667,12 @@ struct wlfw_host_cap_req_msg_v01 { u8 mem_cfg_mode; u8 cal_duration_valid; u16 cal_duration; + u8 ddr_range_valid; + struct wlfw_host_ddr_range_s_v01 + ddr_range[QMI_WLFW_MAX_HOST_DDR_RANGE_SIZE_V01]; }; -#define WLFW_HOST_CAP_REQ_MSG_V01_MAX_MSG_LEN 194 +#define WLFW_HOST_CAP_REQ_MSG_V01_MAX_MSG_LEN 245 extern struct qmi_elem_info wlfw_host_cap_req_msg_v01_ei[]; struct wlfw_host_cap_resp_msg_v01 {