From 8650e54e96a183114a8bae06522a5467f66afbde Mon Sep 17 00:00:00 2001 From: Manoj Prabhu B Date: Tue, 18 Feb 2020 14:19:24 +0530 Subject: [PATCH] memshare: Add snapshot of memshare driver The patch is a snapshot of memshare driver taken from msm-4.19 commit (Merge "defconfig: Remove unnecessary config for bengal 32bit"). Change-Id: I90d59259e887e2cd0c7e16e81407668c807d0530 Signed-off-by: Manoj Prabhu B --- drivers/soc/qcom/Kconfig | 2 + drivers/soc/qcom/Makefile | 1 + drivers/soc/qcom/memshare/Kconfig | 13 + drivers/soc/qcom/memshare/Makefile | 2 + drivers/soc/qcom/memshare/heap_mem_ext_v01.c | 340 +++++++ drivers/soc/qcom/memshare/heap_mem_ext_v01.h | 263 +++++ drivers/soc/qcom/memshare/msm_memshare.c | 955 +++++++++++++++++++ drivers/soc/qcom/memshare/msm_memshare.h | 64 ++ 8 files changed, 1640 insertions(+) create mode 100644 drivers/soc/qcom/memshare/Kconfig create mode 100644 drivers/soc/qcom/memshare/Makefile create mode 100644 drivers/soc/qcom/memshare/heap_mem_ext_v01.c create mode 100644 drivers/soc/qcom/memshare/heap_mem_ext_v01.h create mode 100644 drivers/soc/qcom/memshare/msm_memshare.c create mode 100644 drivers/soc/qcom/memshare/msm_memshare.h diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index b8f591527aff..186337db6e09 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -284,6 +284,8 @@ config QCOM_QMI_HELPERS depends on ARCH_QCOM || COMPILE_TEST depends on NET +source "drivers/soc/qcom/memshare/Kconfig" + config QCOM_RMTFS_MEM tristate "Qualcomm Remote Filesystem memory driver" depends on ARCH_QCOM diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 62bac04e9c6d..ed6e76325325 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_MSM_SERVICE_NOTIFIER) += service-notifier.o obj-$(CONFIG_MSM_SERVICE_LOCATOR) += service-locator.o obj-$(CONFIG_QCOM_QMI_HELPERS) += qmi_helpers.o qmi_helpers-y += qmi_encdec.o qmi_interface.o +obj-$(CONFIG_MEM_SHARE_QMI_SERVICE) = memshare/ obj-$(CONFIG_QCOM_RMTFS_MEM) += rmtfs_mem.o obj-$(CONFIG_QCOM_RPMH) += qcom_rpmh.o qcom_rpmh-y += rpmh-rsc.o diff --git a/drivers/soc/qcom/memshare/Kconfig b/drivers/soc/qcom/memshare/Kconfig new file mode 100644 index 000000000000..57343ecf5447 --- /dev/null +++ b/drivers/soc/qcom/memshare/Kconfig @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Shared Heap for external processors +# +config MEM_SHARE_QMI_SERVICE + depends on QCOM_QMI_HELPERS + bool "Shared Heap for external processors" + help + Memory Share Kernel QTI Messaging Interface Service + receives requests from Modem Processor Sub System + for heap alloc/free from Application Processor + Sub System and send a response back to client with + proper handle/address. diff --git a/drivers/soc/qcom/memshare/Makefile b/drivers/soc/qcom/memshare/Makefile new file mode 100644 index 000000000000..71caf9896b0b --- /dev/null +++ b/drivers/soc/qcom/memshare/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_MEM_SHARE_QMI_SERVICE) := heap_mem_ext_v01.o msm_memshare.o diff --git a/drivers/soc/qcom/memshare/heap_mem_ext_v01.c b/drivers/soc/qcom/memshare/heap_mem_ext_v01.c new file mode 100644 index 000000000000..137cdd3ccd1b --- /dev/null +++ b/drivers/soc/qcom/memshare/heap_mem_ext_v01.c @@ -0,0 +1,340 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2013-2015, 2017-2020, The Linux Foundation. All rights reserved. + */ + +#include +#include "heap_mem_ext_v01.h" + +struct qmi_elem_info dhms_mem_alloc_addr_info_type_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_8_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint64_t), + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + .offset = offsetof(struct + dhms_mem_alloc_addr_info_type_v01, + phy_addr), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + .offset = offsetof(struct + dhms_mem_alloc_addr_info_type_v01, + num_bytes), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +struct qmi_elem_info mem_alloc_generic_req_msg_data_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .array_type = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct mem_alloc_generic_req_msg_v01, + num_bytes), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct mem_alloc_generic_req_msg_v01, + client_id), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .array_type = NO_ARRAY, + .tlv_type = 0x03, + .offset = offsetof(struct mem_alloc_generic_req_msg_v01, + proc_id), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .array_type = NO_ARRAY, + .tlv_type = 0x04, + .offset = offsetof(struct mem_alloc_generic_req_msg_v01, + sequence_id), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct mem_alloc_generic_req_msg_v01, + alloc_contiguous_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct mem_alloc_generic_req_msg_v01, + alloc_contiguous), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct mem_alloc_generic_req_msg_v01, + block_alignment_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .array_type = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct mem_alloc_generic_req_msg_v01, + block_alignment), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +struct qmi_elem_info mem_alloc_generic_resp_msg_data_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct + mem_alloc_generic_resp_msg_v01, + resp), + .ei_array = qmi_response_type_v01_ei, + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct + mem_alloc_generic_resp_msg_v01, + sequence_id_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct + mem_alloc_generic_resp_msg_v01, + sequence_id), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct + mem_alloc_generic_resp_msg_v01, + dhms_mem_alloc_addr_info_valid), + }, + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct + mem_alloc_generic_resp_msg_v01, + dhms_mem_alloc_addr_info_len), + }, + { + .data_type = QMI_STRUCT, + .elem_len = MAX_ARR_CNT_V01, + .elem_size = sizeof(struct + dhms_mem_alloc_addr_info_type_v01), + .array_type = VAR_LEN_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct + mem_alloc_generic_resp_msg_v01, + dhms_mem_alloc_addr_info), + .ei_array = dhms_mem_alloc_addr_info_type_v01_ei, + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +struct qmi_elem_info mem_free_generic_req_msg_data_v01_ei[] = { + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct mem_free_generic_req_msg_v01, + dhms_mem_alloc_addr_info_len), + }, + { + .data_type = QMI_STRUCT, + .elem_len = MAX_ARR_CNT_V01, + .elem_size = sizeof(struct + dhms_mem_alloc_addr_info_type_v01), + .array_type = VAR_LEN_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct mem_free_generic_req_msg_v01, + dhms_mem_alloc_addr_info), + .ei_array = dhms_mem_alloc_addr_info_type_v01_ei, + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct mem_free_generic_req_msg_v01, + client_id_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct mem_free_generic_req_msg_v01, + client_id), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct mem_free_generic_req_msg_v01, + proc_id_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .array_type = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct mem_free_generic_req_msg_v01, + proc_id), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +struct qmi_elem_info mem_free_generic_resp_msg_data_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct + mem_free_generic_resp_msg_v01, + resp), + .ei_array = qmi_response_type_v01_ei, + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +struct qmi_elem_info mem_query_size_req_msg_data_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .array_type = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct mem_query_size_req_msg_v01, + client_id), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct mem_query_size_req_msg_v01, + proc_id_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct mem_query_size_req_msg_v01, + proc_id), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +struct qmi_elem_info mem_query_size_resp_msg_data_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct + mem_query_size_rsp_msg_v01, + resp), + .ei_array = qmi_response_type_v01_ei, + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct mem_query_size_rsp_msg_v01, + size_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct mem_query_size_rsp_msg_v01, + size), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; diff --git a/drivers/soc/qcom/memshare/heap_mem_ext_v01.h b/drivers/soc/qcom/memshare/heap_mem_ext_v01.h new file mode 100644 index 000000000000..754571c9d1f6 --- /dev/null +++ b/drivers/soc/qcom/memshare/heap_mem_ext_v01.h @@ -0,0 +1,263 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (c) 2013-2015, 2017-2020, The Linux Foundation. All rights reserved. + */ + +#ifndef HEAP_MEM_EXT_SERVICE_01_H +#define HEAP_MEM_EXT_SERVICE_01_H + +#include + +#define MEM_ALLOC_REQ_MAX_MSG_LEN_V01 255 +#define MEM_FREE_REQ_MAX_MSG_LEN_V01 255 +#define MEM_QUERY_MAX_MSG_LEN_V01 255 +#define MAX_ARR_CNT_V01 64 + +struct dhms_mem_alloc_addr_info_type_v01 { + uint64_t phy_addr; + uint32_t num_bytes; +}; + +enum dhms_mem_proc_id_v01 { + /* To force a 32 bit signed enum. Do not change or use */ + DHMS_MEM_PROC_ID_MIN_ENUM_VAL_V01 = -2147483647, + /* Request from MPSS processor */ + DHMS_MEM_PROC_MPSS_V01 = 0, + /* Request from ADSP processor */ + DHMS_MEM_PROC_ADSP_V01 = 1, + /* Request from WCNSS processor */ + DHMS_MEM_PROC_WCNSS_V01 = 2, + /* To force a 32 bit signed enum. Do not change or use */ + DHMS_MEM_PROC_ID_MAX_ENUM_VAL_V01 = 2147483647 +}; + +enum dhms_mem_client_id_v01 { + /*To force a 32 bit signed enum. Do not change or use*/ + DHMS_MEM_CLIENT_ID_MIN_ENUM_VAL_V01 = -2147483647, + /* Request from GPS Client */ + DHMS_MEM_CLIENT_GPS_V01 = 0, + /* Invalid Client */ + DHMS_MEM_CLIENT_INVALID = 1000, + /* To force a 32 bit signed enum. Do not change or use */ + DHMS_MEM_CLIENT_ID_MAX_ENUM_VAL_V01 = 2147483647 +}; + +enum dhms_mem_block_align_enum_v01 { + /* To force a 32 bit signed enum. Do not change or use + */ + DHMS_MEM_BLOCK_ALIGN_ENUM_MIN_ENUM_VAL_V01 = -2147483647, + /* Align allocated memory by 2 bytes */ + DHMS_MEM_BLOCK_ALIGN_2_V01 = 0, + /* Align allocated memory by 4 bytes */ + DHMS_MEM_BLOCK_ALIGN_4_V01 = 1, + /**< Align allocated memory by 8 bytes */ + DHMS_MEM_BLOCK_ALIGN_8_V01 = 2, + /**< Align allocated memory by 16 bytes */ + DHMS_MEM_BLOCK_ALIGN_16_V01 = 3, + /**< Align allocated memory by 32 bytes */ + DHMS_MEM_BLOCK_ALIGN_32_V01 = 4, + /**< Align allocated memory by 64 bytes */ + DHMS_MEM_BLOCK_ALIGN_64_V01 = 5, + /**< Align allocated memory by 128 bytes */ + DHMS_MEM_BLOCK_ALIGN_128_V01 = 6, + /**< Align allocated memory by 256 bytes */ + DHMS_MEM_BLOCK_ALIGN_256_V01 = 7, + /**< Align allocated memory by 512 bytes */ + DHMS_MEM_BLOCK_ALIGN_512_V01 = 8, + /**< Align allocated memory by 1024 bytes */ + DHMS_MEM_BLOCK_ALIGN_1K_V01 = 9, + /**< Align allocated memory by 2048 bytes */ + DHMS_MEM_BLOCK_ALIGN_2K_V01 = 10, + /**< Align allocated memory by 4096 bytes */ + DHMS_MEM_BLOCK_ALIGN_4K_V01 = 11, + DHMS_MEM_BLOCK_ALIGN_ENUM_MAX_ENUM_VAL_V01 = 2147483647 + /* To force a 32 bit signed enum. Do not change or use + */ +}; + +/* Request Message; This command is used for getting + * the multiple physically contiguous + * memory blocks from the server memory subsystem + */ +struct mem_alloc_generic_req_msg_v01 { + + /* Mandatory */ + /*requested size*/ + uint32_t num_bytes; + + /* Mandatory */ + /* client id */ + enum dhms_mem_client_id_v01 client_id; + + /* Mandatory */ + /* Peripheral Id*/ + enum dhms_mem_proc_id_v01 proc_id; + + /* Mandatory */ + /* Sequence id */ + uint32_t sequence_id; + + /* Optional */ + /* alloc_contiguous */ + /* Must be set to true if alloc_contiguous is being passed */ + uint8_t alloc_contiguous_valid; + + /* Alloc_contiguous is used to identify that clients are requesting + * for contiguous or non contiguous memory, default is contiguous + * 0 = non contiguous else contiguous + */ + uint8_t alloc_contiguous; + + /* Optional */ + /* Must be set to true if block_alignment + * is being passed + */ + uint8_t block_alignment_valid; + + /* The block alignment for the memory block to be allocated + */ + enum dhms_mem_block_align_enum_v01 block_alignment; + +}; /* Message */ + +/* Response Message; This command is used for getting + * the multiple physically contiguous memory blocks + * from the server memory subsystem + */ +struct mem_alloc_generic_resp_msg_v01 { + + /* Mandatory */ + /* Result Code */ + /* The result of the requested memory operation + */ + struct qmi_response_type_v01 resp; + + /* Optional */ + /* Sequence ID */ + /* Must be set to true if sequence_id is being passed */ + uint8_t sequence_id_valid; + + + /* Mandatory */ + /* Sequence id */ + uint32_t sequence_id; + + /* Optional */ + /* Memory Block Handle + */ + /* Must be set to true if handle is being passed + */ + uint8_t dhms_mem_alloc_addr_info_valid; + + /* Optional */ + /* Handle Size */ + uint32_t dhms_mem_alloc_addr_info_len; + + /* Optional */ + /* The physical address of the memory allocated on the HLOS + */ + struct dhms_mem_alloc_addr_info_type_v01 + dhms_mem_alloc_addr_info[MAX_ARR_CNT_V01]; + +}; /* Message */ + +/* Request Message; This command is used for releasing + * the multiple physically contiguous + * memory blocks to the server memory subsystem + */ +struct mem_free_generic_req_msg_v01 { + + /* Mandatory */ + /* Must be set to # of elments in array*/ + uint32_t dhms_mem_alloc_addr_info_len; + + /* Mandatory */ + /* Physical address and size of the memory allocated + * on the HLOS to be freed. + */ + struct dhms_mem_alloc_addr_info_type_v01 + dhms_mem_alloc_addr_info[MAX_ARR_CNT_V01]; + + /* Optional */ + /* Client ID */ + /* Must be set to true if client_id is being passed */ + uint8_t client_id_valid; + + /* Optional */ + /* Client Id */ + enum dhms_mem_client_id_v01 client_id; + + /* Optional */ + /* Proc ID */ + /* Must be set to true if proc_id is being passed */ + uint8_t proc_id_valid; + + /* Optional */ + /* Peripheral */ + enum dhms_mem_proc_id_v01 proc_id; + +}; /* Message */ + +/* Response Message; This command is used for releasing + * the multiple physically contiguous + * memory blocks to the server memory subsystem + */ +struct mem_free_generic_resp_msg_v01 { + + /* + * Mandatory + * Result of the requested memory operation, todo, + * need to check the async operation for free + */ + struct qmi_response_type_v01 resp; + +}; /* Message */ + +struct mem_query_size_req_msg_v01 { + + /* Mandatory */ + enum dhms_mem_client_id_v01 client_id; + + /* + * Optional + * Proc ID + * proc_id_valid must be set to true if proc_id is being passed + */ + uint8_t proc_id_valid; + + enum dhms_mem_proc_id_v01 proc_id; +}; /* Message */ + +struct mem_query_size_rsp_msg_v01 { + + /* + * Mandatory + * Result Code + */ + struct qmi_response_type_v01 resp; + + /* + * Optional + * size_valid must be set to true if size is being passed + */ + uint8_t size_valid; + + uint32_t size; +}; /* Message */ + + +extern struct qmi_elem_info mem_alloc_generic_req_msg_data_v01_ei[]; +extern struct qmi_elem_info mem_alloc_generic_resp_msg_data_v01_ei[]; +extern struct qmi_elem_info mem_free_generic_req_msg_data_v01_ei[]; +extern struct qmi_elem_info mem_free_generic_resp_msg_data_v01_ei[]; +extern struct qmi_elem_info mem_query_size_req_msg_data_v01_ei[]; +extern struct qmi_elem_info mem_query_size_resp_msg_data_v01_ei[]; + +/*Service Message Definition*/ +#define MEM_ALLOC_GENERIC_REQ_MSG_V01 0x0022 +#define MEM_ALLOC_GENERIC_RESP_MSG_V01 0x0022 +#define MEM_FREE_GENERIC_REQ_MSG_V01 0x0023 +#define MEM_FREE_GENERIC_RESP_MSG_V01 0x0023 +#define MEM_QUERY_SIZE_REQ_MSG_V01 0x0024 +#define MEM_QUERY_SIZE_RESP_MSG_V01 0x0024 + +#endif diff --git a/drivers/soc/qcom/memshare/msm_memshare.c b/drivers/soc/qcom/memshare/msm_memshare.c new file mode 100644 index 000000000000..2bc5df679bd4 --- /dev/null +++ b/drivers/soc/qcom/memshare/msm_memshare.c @@ -0,0 +1,955 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2013-2020, The Linux Foundation. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "msm_memshare.h" +#include "heap_mem_ext_v01.h" + +#include +#include + +/* Macros */ +#define MEMSHARE_DEV_NAME "memshare" +#define MEMSHARE_CHILD_DEV_NAME "memshare_child" +static unsigned long(attrs); + +static struct qmi_handle *mem_share_svc_handle; +static struct workqueue_struct *mem_share_svc_workqueue; +static uint64_t bootup_request; +static bool ramdump_event; +static void *memshare_ramdump_dev[MAX_CLIENTS]; +static struct device *memshare_dev[MAX_CLIENTS]; + +/* Memshare Driver Structure */ +struct memshare_driver { + struct device *dev; + struct mutex mem_share; + struct mutex mem_free; + struct work_struct memshare_init_work; +}; + +struct memshare_child { + struct device *dev; +}; + +static struct memshare_driver *memsh_drv; +static struct memshare_child *memsh_child; +static struct mem_blocks memblock[MAX_CLIENTS]; +static uint32_t num_clients; + +/* + * This API creates ramdump dev handlers + * for each of the memshare clients. + * These dev handlers will be used for + * extracting the ramdump for loaned memory + * segments. + */ + +static int mem_share_configure_ramdump(int client) +{ + char client_name[18]; + const char *clnt = NULL; + + switch (client) { + case 0: + clnt = "GPS"; + break; + case 1: + clnt = "FTM"; + break; + case 2: + clnt = "DIAG"; + break; + default: + dev_err(memsh_child->dev, "memshare: no memshare clients registered\n"); + return -EINVAL; + } + + snprintf(client_name, sizeof(client_name), + "memshare_%s", clnt); + if (memshare_dev[client]) { + memshare_ramdump_dev[client] = + create_ramdump_device(client_name, + memshare_dev[client]); + } else { + dev_err(memsh_child->dev, + "memshare: invalid memshare device for creating ramdump device\n"); + return -ENODEV; + } + if (IS_ERR_OR_NULL(memshare_ramdump_dev[client])) { + dev_err(memsh_child->dev, + "memshare: unable to create memshare ramdump device\n"); + memshare_ramdump_dev[client] = NULL; + return -ENOMEM; + } + + return 0; +} + +static int check_client(int client_id, int proc, int request) +{ + int i = 0, rc; + int found = DHMS_MEM_CLIENT_INVALID; + + for (i = 0; i < MAX_CLIENTS; i++) { + if (memblock[i].client_id == client_id && + memblock[i].peripheral == proc) { + found = i; + break; + } + } + if ((found == DHMS_MEM_CLIENT_INVALID) && !request) { + dev_dbg(memsh_child->dev, + "memshare: No registered client for the client_id: %d, adding a new client\n", + client_id); + /* Add a new client */ + for (i = 0; i < MAX_CLIENTS; i++) { + if (memblock[i].client_id == DHMS_MEM_CLIENT_INVALID) { + memblock[i].client_id = client_id; + memblock[i].allotted = 0; + memblock[i].guarantee = 0; + memblock[i].peripheral = proc; + found = i; + + if (!memblock[i].file_created) { + rc = mem_share_configure_ramdump(i); + if (rc) + dev_err(memsh_child->dev, + "memshare_check_client: cannot create ramdump for client with id: %d\n", + client_id); + else + memblock[i].file_created = 1; + } + + break; + } + } + } + + return found; +} + +static void free_client(int id) +{ + memblock[id].phy_addr = 0; + memblock[id].virtual_addr = 0; + memblock[id].allotted = 0; + memblock[id].guarantee = 0; + memblock[id].sequence_id = -1; + memblock[id].memory_type = MEMORY_CMA; + +} + +static void fill_alloc_response(struct mem_alloc_generic_resp_msg_v01 *resp, + int id, int *flag) +{ + resp->sequence_id_valid = 1; + resp->sequence_id = memblock[id].sequence_id; + resp->dhms_mem_alloc_addr_info_valid = 1; + resp->dhms_mem_alloc_addr_info_len = 1; + resp->dhms_mem_alloc_addr_info[0].phy_addr = memblock[id].phy_addr; + resp->dhms_mem_alloc_addr_info[0].num_bytes = memblock[id].size; + if (!*flag) { + resp->resp.result = QMI_RESULT_SUCCESS_V01; + resp->resp.error = QMI_ERR_NONE_V01; + } else { + resp->resp.result = QMI_RESULT_FAILURE_V01; + resp->resp.error = QMI_ERR_NO_MEMORY_V01; + } + +} + +static void initialize_client(void) +{ + int i; + + for (i = 0; i < MAX_CLIENTS; i++) { + memblock[i].allotted = 0; + memblock[i].size = 0; + memblock[i].guarantee = 0; + memblock[i].phy_addr = 0; + memblock[i].virtual_addr = 0; + memblock[i].client_id = DHMS_MEM_CLIENT_INVALID; + memblock[i].peripheral = -1; + memblock[i].sequence_id = -1; + memblock[i].memory_type = MEMORY_CMA; + memblock[i].free_memory = 0; + memblock[i].hyp_mapping = 0; + memblock[i].file_created = 0; + } +} + +/* + * mem_share_do_ramdump() function initializes the + * ramdump segments with the physical address and + * size of the memshared clients. Extraction of ramdump + * is skipped if memshare client is not allotted + * This calls the ramdump api in extracting the + * ramdump in elf format. + */ + +static int mem_share_do_ramdump(void) +{ + int i = 0, ret; + char *client_name = NULL; + u32 source_vmlist[1] = {VMID_MSS_MSA}; + int dest_vmids[1] = {VMID_HLOS}; + int dest_perms[1] = {PERM_READ|PERM_WRITE|PERM_EXEC}; + + for (i = 0; i < num_clients; i++) { + + struct ramdump_segment *ramdump_segments_tmp = NULL; + + switch (i) { + case 0: + client_name = "GPS"; + break; + case 1: + client_name = "FTM"; + break; + case 2: + client_name = "DIAG"; + break; + default: + dev_err(memsh_child->dev, + "memshare: no memshare clients registered for client_id: %d\n", + i); + return -EINVAL; + } + + if (!memblock[i].allotted) { + dev_err(memsh_child->dev, "memshare: %s: memblock is not allotted\n", + client_name); + continue; + } + + if (memblock[i].hyp_mapping && + memblock[i].peripheral == + DHMS_MEM_PROC_MPSS_V01) { + dev_dbg(memsh_child->dev, + "memshare: %s: hypervisor unmapping for client before elf dump\n", + client_name); + if (memblock[i].alloc_request) + continue; + ret = hyp_assign_phys( + memblock[i].phy_addr, + memblock[i].size, + source_vmlist, + 1, dest_vmids, + dest_perms, 1); + if (ret) { + /* + * This is an error case as hyp + * mapping was successful + * earlier but during unmap + * it lead to failure. + */ + dev_err(memsh_child->dev, + "memshare: %s: failed to map the memory region to APPS\n", + client_name); + continue; + } else { + memblock[i].hyp_mapping = 0; + } + } + + if (!memblock[i].hyp_mapping) { + ramdump_segments_tmp = kcalloc(1, + sizeof(struct ramdump_segment), + GFP_KERNEL); + if (!ramdump_segments_tmp) + return -ENOMEM; + + ramdump_segments_tmp[0].size = memblock[i].size; + ramdump_segments_tmp[0].v_address = + memblock[i].virtual_addr; + + dev_dbg(memsh_child->dev, "memshare: %s: Begin elf dump for size = %d\n", + client_name, memblock[i].size); + + ret = do_elf_ramdump(memshare_ramdump_dev[i], + ramdump_segments_tmp, 1); + kfree(ramdump_segments_tmp); + if (ret < 0) { + dev_err(memsh_child->dev, + "memshare: %s: Unable to elf dump with failure: %d\n", + client_name, ret); + return ret; + } + } + } + return 0; +} + +static int modem_notifier_cb(struct notifier_block *this, unsigned long code, + void *_cmd) +{ + int i, ret, size = 0; + u32 source_vmlist[1] = {VMID_MSS_MSA}; + int dest_vmids[1] = {VMID_HLOS}; + int dest_perms[1] = {PERM_READ|PERM_WRITE|PERM_EXEC}; + struct notif_data *notifdata = NULL; + + mutex_lock(&memsh_drv->mem_share); + + switch (code) { + + case SUBSYS_BEFORE_SHUTDOWN: + bootup_request++; + for (i = 0; i < MAX_CLIENTS; i++) + memblock[i].alloc_request = 0; + break; + + case SUBSYS_RAMDUMP_NOTIFICATION: + ramdump_event = true; + break; + + case SUBSYS_BEFORE_POWERUP: + if (_cmd) { + notifdata = (struct notif_data *) _cmd; + } else { + ramdump_event = false; + break; + } + + if (notifdata->enable_ramdump && ramdump_event) { + dev_info(memsh_child->dev, "memshare: Ramdump collection is enabled\n"); + ret = mem_share_do_ramdump(); + if (ret) + dev_err(memsh_child->dev, "memshare: Ramdump collection failed\n"); + ramdump_event = false; + } + break; + + case SUBSYS_AFTER_POWERUP: + dev_dbg(memsh_child->dev, "memshare: Modem has booted up\n"); + for (i = 0; i < MAX_CLIENTS; i++) { + size = memblock[i].size; + if (memblock[i].free_memory > 0 && + bootup_request >= 2) { + memblock[i].free_memory -= 1; + dev_dbg(memsh_child->dev, "memshare: free_memory count: %d for client id: %d\n", + memblock[i].free_memory, + memblock[i].client_id); + } + + if (memblock[i].free_memory == 0 && + memblock[i].peripheral == + DHMS_MEM_PROC_MPSS_V01 && + !memblock[i].guarantee && + !memblock[i].client_request && + memblock[i].allotted && + !memblock[i].alloc_request) { + dev_info(memsh_child->dev, + "memshare: hypervisor unmapping for allocated memory with client id: %d\n", + memblock[i].client_id); + if (memblock[i].hyp_mapping) { + ret = hyp_assign_phys( + memblock[i].phy_addr, + memblock[i].size, + source_vmlist, + 1, dest_vmids, + dest_perms, 1); + if (ret && + memblock[i].hyp_mapping == 1) { + /* + * This is an error case as hyp + * mapping was successful + * earlier but during unmap + * it lead to failure. + */ + dev_err(memsh_child->dev, + "memshare: failed to hypervisor unmap the memory region for client id: %d\n", + memblock[i].client_id); + } else { + memblock[i].hyp_mapping = 0; + } + } + if (memblock[i].guard_band) { + /* + * Check if the client required guard band + * support so the memory region of client's + * size + guard bytes of 4K can be freed. + */ + size += MEMSHARE_GUARD_BYTES; + } + dma_free_attrs(memsh_drv->dev, + size, memblock[i].virtual_addr, + memblock[i].phy_addr, + attrs); + free_client(i); + } + } + bootup_request++; + break; + + default: + break; + } + + mutex_unlock(&memsh_drv->mem_share); + return NOTIFY_DONE; +} + +static struct notifier_block nb = { + .notifier_call = modem_notifier_cb, +}; + +static void shared_hyp_mapping(int client_id) +{ + int ret; + u32 source_vmlist[1] = {VMID_HLOS}; + int dest_vmids[1] = {VMID_MSS_MSA}; + int dest_perms[1] = {PERM_READ|PERM_WRITE}; + + if (client_id == DHMS_MEM_CLIENT_INVALID) { + dev_err(memsh_child->dev, + "memshare: hypervisor mapping failure for invalid client\n"); + return; + } + + ret = hyp_assign_phys(memblock[client_id].phy_addr, + memblock[client_id].size, + source_vmlist, 1, dest_vmids, + dest_perms, 1); + + if (ret != 0) { + dev_err(memsh_child->dev, "memshare: hyp_assign_phys failed size=%u err=%d\n", + memblock[client_id].size, ret); + return; + } + memblock[client_id].hyp_mapping = 1; +} + +static void handle_alloc_generic_req(struct qmi_handle *handle, + struct sockaddr_qrtr *sq, struct qmi_txn *txn, const void *decoded_msg) +{ + struct mem_alloc_generic_req_msg_v01 *alloc_req; + struct mem_alloc_generic_resp_msg_v01 *alloc_resp; + int rc, resp = 0; + int client_id; + uint32_t size = 0; + + mutex_lock(&memsh_drv->mem_share); + alloc_req = (struct mem_alloc_generic_req_msg_v01 *)decoded_msg; + dev_info(memsh_child->dev, + "memshare_alloc: memory alloc request received for client id: %d, proc_id: %d, request size: %d\n", + alloc_req->client_id, alloc_req->proc_id, alloc_req->num_bytes); + alloc_resp = kzalloc(sizeof(*alloc_resp), + GFP_KERNEL); + if (!alloc_resp) { + mutex_unlock(&memsh_drv->mem_share); + return; + } + alloc_resp->resp.result = QMI_RESULT_FAILURE_V01; + alloc_resp->resp.error = QMI_ERR_NO_MEMORY_V01; + client_id = check_client(alloc_req->client_id, alloc_req->proc_id, + CHECK); + + if (client_id >= MAX_CLIENTS) { + dev_err(memsh_child->dev, + "memshare_alloc: client not found, requested client: %d, proc_id: %d\n", + alloc_req->client_id, alloc_req->proc_id); + kfree(alloc_resp); + alloc_resp = NULL; + mutex_unlock(&memsh_drv->mem_share); + return; + } + + if (!memblock[client_id].allotted) { + if (memblock[client_id].guard_band && alloc_req->num_bytes > 0) + size = alloc_req->num_bytes + MEMSHARE_GUARD_BYTES; + else + size = alloc_req->num_bytes; + rc = memshare_alloc(memsh_drv->dev, size, + &memblock[client_id]); + if (rc) { + dev_err(memsh_child->dev, + "memshare_alloc: unable to allocate memory of size: %d for requested client\n", + size); + resp = 1; + } + if (!resp) { + memblock[client_id].free_memory += 1; + memblock[client_id].allotted = 1; + memblock[client_id].size = alloc_req->num_bytes; + memblock[client_id].peripheral = alloc_req->proc_id; + } + } + dev_dbg(memsh_child->dev, + "memshare_alloc: free memory count for client id: %d = %d\n", + memblock[client_id].client_id, memblock[client_id].free_memory); + + memblock[client_id].sequence_id = alloc_req->sequence_id; + memblock[client_id].alloc_request = 1; + + fill_alloc_response(alloc_resp, client_id, &resp); + /* + * Perform the Hypervisor mapping in order to avoid XPU viloation + * to the allocated region for Modem Clients + */ + if (!memblock[client_id].hyp_mapping && + memblock[client_id].allotted) + shared_hyp_mapping(client_id); + mutex_unlock(&memsh_drv->mem_share); + dev_info(memsh_child->dev, + "memshare_alloc: client_id: %d, alloc_resp.num_bytes: %d, alloc_resp.resp.result: %lx\n", + alloc_req->client_id, + alloc_resp->dhms_mem_alloc_addr_info[0].num_bytes, + (unsigned long)alloc_resp->resp.result); + + rc = qmi_send_response(mem_share_svc_handle, sq, txn, + MEM_ALLOC_GENERIC_RESP_MSG_V01, + sizeof(struct mem_alloc_generic_resp_msg_v01), + mem_alloc_generic_resp_msg_data_v01_ei, alloc_resp); + if (rc < 0) + dev_err(memsh_child->dev, + "memshare_alloc: Error sending the alloc response: %d\n", + rc); + + kfree(alloc_resp); + alloc_resp = NULL; +} + +static void handle_free_generic_req(struct qmi_handle *handle, + struct sockaddr_qrtr *sq, struct qmi_txn *txn, const void *decoded_msg) +{ + struct mem_free_generic_req_msg_v01 *free_req; + struct mem_free_generic_resp_msg_v01 free_resp; + int rc, flag = 0, ret = 0, size = 0; + uint32_t client_id; + u32 source_vmlist[1] = {VMID_MSS_MSA}; + int dest_vmids[1] = {VMID_HLOS}; + int dest_perms[1] = {PERM_READ|PERM_WRITE|PERM_EXEC}; + + mutex_lock(&memsh_drv->mem_free); + free_req = (struct mem_free_generic_req_msg_v01 *)decoded_msg; + memset(&free_resp, 0, sizeof(free_resp)); + free_resp.resp.error = QMI_ERR_INTERNAL_V01; + free_resp.resp.result = QMI_RESULT_FAILURE_V01; + dev_info(memsh_child->dev, + "memshare_free: handling memory free request with client id: %d, proc_id: %d\n", + free_req->client_id, free_req->proc_id); + client_id = check_client(free_req->client_id, free_req->proc_id, FREE); + if (client_id == DHMS_MEM_CLIENT_INVALID) { + dev_err(memsh_child->dev, "memshare_free: invalid client request to free memory\n"); + flag = 1; + } else if (!memblock[client_id].guarantee && + !memblock[client_id].client_request && + memblock[client_id].allotted) { + dev_dbg(memsh_child->dev, + "memshare_free: hypervisor unmapping for client_id:%d - size: %d\n", + client_id, memblock[client_id].size); + ret = hyp_assign_phys(memblock[client_id].phy_addr, + memblock[client_id].size, source_vmlist, 1, + dest_vmids, dest_perms, 1); + if (ret && memblock[client_id].hyp_mapping == 1) { + /* + * This is an error case as hyp mapping was successful + * earlier but during unmap it lead to failure. + */ + dev_err(memsh_child->dev, + "memshare_free: failed to unmap the region for client id:%d\n", + client_id); + } + size = memblock[client_id].size; + if (memblock[client_id].guard_band) { + /* + * Check if the client required guard band support so + * the memory region of client's size + guard + * bytes of 4K can be freed + */ + size += MEMSHARE_GUARD_BYTES; + } + dma_free_attrs(memsh_drv->dev, size, + memblock[client_id].virtual_addr, + memblock[client_id].phy_addr, + attrs); + free_client(client_id); + } else { + dev_err(memsh_child->dev, + "memshare_free: cannot free the memory for a guaranteed client (client_id: %d)\n", + client_id); + } + + if (flag) { + free_resp.resp.result = QMI_RESULT_FAILURE_V01; + free_resp.resp.error = QMI_ERR_INVALID_ID_V01; + } else { + free_resp.resp.result = QMI_RESULT_SUCCESS_V01; + free_resp.resp.error = QMI_ERR_NONE_V01; + } + + mutex_unlock(&memsh_drv->mem_free); + rc = qmi_send_response(mem_share_svc_handle, sq, txn, + MEM_FREE_GENERIC_RESP_MSG_V01, + MEM_FREE_REQ_MAX_MSG_LEN_V01, + mem_free_generic_resp_msg_data_v01_ei, &free_resp); + if (rc < 0) + dev_err(memsh_child->dev, + "memshare_free: error sending the free response: %d\n", rc); + +} + +static void handle_query_size_req(struct qmi_handle *handle, + struct sockaddr_qrtr *sq, struct qmi_txn *txn, const void *decoded_msg) +{ + int rc, client_id; + struct mem_query_size_req_msg_v01 *query_req; + struct mem_query_size_rsp_msg_v01 *query_resp; + + mutex_lock(&memsh_drv->mem_share); + query_req = (struct mem_query_size_req_msg_v01 *)decoded_msg; + query_resp = kzalloc(sizeof(*query_resp), + GFP_KERNEL); + if (!query_resp) { + mutex_unlock(&memsh_drv->mem_share); + return; + } + dev_dbg(memsh_child->dev, + "memshare_query: query on availalbe memory size for client id: %d, proc_id: %d\n", + query_req->client_id, query_req->proc_id); + client_id = check_client(query_req->client_id, query_req->proc_id, + CHECK); + + if (client_id >= MAX_CLIENTS) { + dev_err(memsh_child->dev, + "memshare_query: client not found, requested client: %d, proc_id: %d\n", + query_req->client_id, query_req->proc_id); + kfree(query_resp); + query_resp = NULL; + mutex_unlock(&memsh_drv->mem_share); + return; + } + + if (memblock[client_id].init_size) { + query_resp->size_valid = 1; + query_resp->size = memblock[client_id].init_size; + } else { + query_resp->size_valid = 1; + query_resp->size = 0; + } + query_resp->resp.result = QMI_RESULT_SUCCESS_V01; + query_resp->resp.error = QMI_ERR_NONE_V01; + mutex_unlock(&memsh_drv->mem_share); + + dev_info(memsh_child->dev, + "memshare_query: client_id : %d, query_resp.size :%d, query_resp.resp.result :%lx\n", + query_req->client_id, query_resp->size, + (unsigned long)query_resp->resp.result); + rc = qmi_send_response(mem_share_svc_handle, sq, txn, + MEM_QUERY_SIZE_RESP_MSG_V01, + MEM_QUERY_MAX_MSG_LEN_V01, + mem_query_size_resp_msg_data_v01_ei, query_resp); + if (rc < 0) + dev_err(memsh_child->dev, + "memshare_query: Error sending the query response: %d\n", rc); + + kfree(query_resp); + query_resp = NULL; +} + +static void mem_share_svc_disconnect_cb(struct qmi_handle *qmi, + unsigned int node, unsigned int port) +{ +} + +static struct qmi_ops server_ops = { + .del_client = mem_share_svc_disconnect_cb, +}; + +static struct qmi_msg_handler qmi_memshare_handlers[] = { + { + .type = QMI_REQUEST, + .msg_id = MEM_ALLOC_GENERIC_REQ_MSG_V01, + .ei = mem_alloc_generic_req_msg_data_v01_ei, + .decoded_size = MEM_ALLOC_REQ_MAX_MSG_LEN_V01, + .fn = handle_alloc_generic_req, + }, + { + .type = QMI_REQUEST, + .msg_id = MEM_FREE_GENERIC_REQ_MSG_V01, + .ei = mem_free_generic_req_msg_data_v01_ei, + .decoded_size = MEM_FREE_REQ_MAX_MSG_LEN_V01, + .fn = handle_free_generic_req, + }, + { + .type = QMI_REQUEST, + .msg_id = MEM_QUERY_SIZE_REQ_MSG_V01, + .ei = mem_query_size_req_msg_data_v01_ei, + .decoded_size = MEM_QUERY_MAX_MSG_LEN_V01, + .fn = handle_query_size_req, + }, +}; + +int memshare_alloc(struct device *dev, + unsigned int block_size, + struct mem_blocks *pblk) +{ + dev_dbg(memsh_child->dev, + "memshare: allocation request for size: %d\n", block_size); + + if (!pblk) { + dev_err(memsh_child->dev, + "memshare: Failed memory block allocation\n"); + return -ENOMEM; + } + + pblk->virtual_addr = dma_alloc_attrs(dev, block_size, + &pblk->phy_addr, GFP_KERNEL, + attrs); + if (pblk->virtual_addr == NULL) + return -ENOMEM; + + return 0; +} + +static void memshare_init_worker(struct work_struct *work) +{ + int rc; + + mem_share_svc_workqueue = + create_singlethread_workqueue("mem_share_svc"); + if (!mem_share_svc_workqueue) + return; + + mem_share_svc_handle = kzalloc(sizeof(struct qmi_handle), + GFP_KERNEL); + if (!mem_share_svc_handle) { + destroy_workqueue(mem_share_svc_workqueue); + return; + } + + rc = qmi_handle_init(mem_share_svc_handle, + sizeof(struct qmi_elem_info), + &server_ops, qmi_memshare_handlers); + if (rc < 0) { + dev_err(memsh_child->dev, + "memshare: Creating mem_share_svc qmi handle failed\n"); + kfree(mem_share_svc_handle); + destroy_workqueue(mem_share_svc_workqueue); + return; + } + rc = qmi_add_server(mem_share_svc_handle, MEM_SHARE_SERVICE_SVC_ID, + MEM_SHARE_SERVICE_VERS, MEM_SHARE_SERVICE_INS_ID); + if (rc < 0) { + dev_err(memsh_child->dev, + "memshare: Registering mem share svc failed %d\n", rc); + qmi_handle_release(mem_share_svc_handle); + kfree(mem_share_svc_handle); + destroy_workqueue(mem_share_svc_workqueue); + return; + } + dev_dbg(memsh_child->dev, "memshare: memshare_init successful\n"); +} + +static int memshare_child_probe(struct platform_device *pdev) +{ + int rc; + uint32_t size, client_id; + const char *name; + struct memshare_child *drv; + + drv = devm_kzalloc(&pdev->dev, sizeof(struct memshare_child), + GFP_KERNEL); + + if (!drv) + return -ENOMEM; + + drv->dev = &pdev->dev; + memsh_child = drv; + platform_set_drvdata(pdev, memsh_child); + + rc = of_property_read_u32(pdev->dev.of_node, "qcom,peripheral-size", + &size); + if (rc) { + dev_err(memsh_child->dev, "memshare: Error reading size of clients, rc: %d\n", + rc); + return rc; + } + + rc = of_property_read_u32(pdev->dev.of_node, "qcom,client-id", + &client_id); + if (rc) { + dev_err(memsh_child->dev, "memshare: Error reading client id, rc: %d\n", + rc); + return rc; + } + + memblock[num_clients].guarantee = of_property_read_bool( + pdev->dev.of_node, + "qcom,allocate-boot-time"); + + memblock[num_clients].client_request = of_property_read_bool( + pdev->dev.of_node, + "qcom,allocate-on-request"); + + memblock[num_clients].guard_band = of_property_read_bool( + pdev->dev.of_node, + "qcom,guard-band"); + + rc = of_property_read_string(pdev->dev.of_node, "label", + &name); + if (rc) { + dev_err(memsh_child->dev, "memshare: Error reading peripheral info for client, rc: %d\n", + rc); + return rc; + } + + if (strcmp(name, "modem") == 0) + memblock[num_clients].peripheral = DHMS_MEM_PROC_MPSS_V01; + else if (strcmp(name, "adsp") == 0) + memblock[num_clients].peripheral = DHMS_MEM_PROC_ADSP_V01; + else if (strcmp(name, "wcnss") == 0) + memblock[num_clients].peripheral = DHMS_MEM_PROC_WCNSS_V01; + + memblock[num_clients].init_size = size; + memblock[num_clients].client_id = client_id; + + /* + * Memshare allocation for guaranteed clients + */ + if (memblock[num_clients].guarantee && size > 0) { + if (memblock[num_clients].guard_band) + size += MEMSHARE_GUARD_BYTES; + rc = memshare_alloc(memsh_child->dev, + size, + &memblock[num_clients]); + if (rc) { + dev_err(memsh_child->dev, + "memshare_child: Unable to allocate memory for guaranteed clients, rc: %d\n", + rc); + return rc; + } + memblock[num_clients].size = size; + memblock[num_clients].allotted = 1; + shared_hyp_mapping(num_clients); + } + + /* + * call for creating ramdump dev handlers for + * memshare clients + */ + + memshare_dev[num_clients] = &pdev->dev; + + if (!memblock[num_clients].file_created) { + rc = mem_share_configure_ramdump(num_clients); + if (rc) + dev_err(memsh_child->dev, + "memshare_child: cannot create ramdump for client with id: %d\n", + memblock[num_clients].client_id); + else + memblock[num_clients].file_created = 1; + } + + num_clients++; + + return 0; +} + +static int memshare_probe(struct platform_device *pdev) +{ + int rc; + struct memshare_driver *drv; + + drv = devm_kzalloc(&pdev->dev, sizeof(struct memshare_driver), + GFP_KERNEL); + + if (!drv) + return -ENOMEM; + + /* Memory allocation has been done successfully */ + mutex_init(&drv->mem_free); + mutex_init(&drv->mem_share); + + INIT_WORK(&drv->memshare_init_work, memshare_init_worker); + schedule_work(&drv->memshare_init_work); + + drv->dev = &pdev->dev; + memsh_drv = drv; + platform_set_drvdata(pdev, memsh_drv); + initialize_client(); + num_clients = 0; + + rc = of_platform_populate(pdev->dev.of_node, NULL, NULL, + &pdev->dev); + + if (rc) { + dev_err(memsh_child->dev, + "memshare: error populating the devices\n"); + return rc; + } + + subsys_notif_register_notifier("modem", &nb); + dev_dbg(memsh_child->dev, "memshare: Memshare inited\n"); + + return 0; +} + +static int memshare_remove(struct platform_device *pdev) +{ + if (!memsh_drv) + return 0; + + flush_workqueue(mem_share_svc_workqueue); + qmi_handle_release(mem_share_svc_handle); + kfree(mem_share_svc_handle); + destroy_workqueue(mem_share_svc_workqueue); + return 0; +} + +static int memshare_child_remove(struct platform_device *pdev) +{ + return 0; +} + +static const struct of_device_id memshare_match_table[] = { + { + .compatible = "qcom,memshare", + }, + {} +}; + +static const struct of_device_id memshare_match_table1[] = { + { + .compatible = "qcom,memshare-peripheral", + }, + {} +}; + + +static struct platform_driver memshare_pdriver = { + .probe = memshare_probe, + .remove = memshare_remove, + .driver = { + .name = MEMSHARE_DEV_NAME, + .of_match_table = memshare_match_table, + }, +}; + +static struct platform_driver memshare_pchild = { + .probe = memshare_child_probe, + .remove = memshare_child_remove, + .driver = { + .name = MEMSHARE_CHILD_DEV_NAME, + .of_match_table = memshare_match_table1, + }, +}; + +module_platform_driver(memshare_pdriver); +module_platform_driver(memshare_pchild); + +MODULE_DESCRIPTION("Mem Share QMI Service Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/soc/qcom/memshare/msm_memshare.h b/drivers/soc/qcom/memshare/msm_memshare.h new file mode 100644 index 000000000000..642e968eb2e0 --- /dev/null +++ b/drivers/soc/qcom/memshare/msm_memshare.h @@ -0,0 +1,64 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (c) 2013-2020, The Linux Foundation. All rights reserved. + */ + +#ifndef _LINUX_MEM_SHARE_H +#define _LINUX_MEM_SHARE_H + +#define MEM_SHARE_SERVICE_SVC_ID 0x00000034 +#define MEM_SHARE_SERVICE_INS_ID 1 +#define MEM_SHARE_SERVICE_VERS 1 + +#define MEMORY_CMA 1 +#define MEMORY_NON_CMA 0 +#define MAX_CLIENTS 10 +#define GPS 0 +#define CHECK 0 +#define FREE 1 +#define MEMSHARE_GUARD_BYTES (4*1024) + +struct mem_blocks { + /* Client Id information */ + uint32_t client_id; + /* Peripheral associated with client */ + uint32_t peripheral; + /* Sequence Id */ + uint32_t sequence_id; + /* CMA or Non-CMA region */ + uint32_t memory_type; + /* Guaranteed Memory */ + uint32_t guarantee; + /* Memory alloted or not */ + uint32_t allotted; + /* Memory allocation request received or not */ + uint32_t alloc_request; + /* Allocation on request from a client*/ + uint32_t client_request; + /* Guard band around the allotted region*/ + uint32_t guard_band; + /* Size required for client */ + uint32_t size; + /* Available memory size for client */ + uint32_t init_size; + /* + * start address of the memory block reserved by server memory + * subsystem to client + */ + phys_addr_t phy_addr; + /* Virtual address for the physical address allocated */ + void *virtual_addr; + /* Release memory only when XPU is released*/ + uint8_t free_memory; + /* Need Hypervisor mapping*/ + uint8_t hyp_mapping; + /* Status flag which checks if ramdump file is created*/ + int file_created; + +}; + +int memshare_alloc(struct device *dev, + unsigned int block_size, + struct mem_blocks *pblk); +void memshare_free(unsigned int block_size, + struct mem_blocks *pblk); +#endif /* _LINUX_MEM_SHARE_H */