Add low-level primitives for enabling SHM bridge support, creating SHM bridge pools and testing the availability of SHM bridges to qcom-scm. We don't yet provide a way to destroy the bridges as the first user will not require it. Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@xxxxxxxxxx> --- drivers/firmware/qcom_scm.c | 83 ++++++++++++++++++++++++++ drivers/firmware/qcom_scm.h | 3 + include/linux/firmware/qcom/qcom_scm.h | 8 +++ 3 files changed, 94 insertions(+) diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c index 422de70faff8..f45d5a424424 100644 --- a/drivers/firmware/qcom_scm.c +++ b/drivers/firmware/qcom_scm.c @@ -31,6 +31,8 @@ module_param(download_mode, bool, 0); #define SCM_HAS_IFACE_CLK BIT(1) #define SCM_HAS_BUS_CLK BIT(2) +#define SCM_SHM_BRIDGE_NOTSUPP 4 + struct qcom_scm { struct device *dev; struct clk *core_clk; @@ -45,6 +47,8 @@ struct qcom_scm { int scm_vote_count; u64 dload_mode_addr; + + bool shm_bridge_enabled; }; struct qcom_scm_current_perm_info { @@ -1248,6 +1252,85 @@ bool qcom_scm_lmh_dcvsh_available(void) } EXPORT_SYMBOL(qcom_scm_lmh_dcvsh_available); +bool qcom_scm_shm_bridge_available(void) +{ + if (!qcom_scm_is_available()) + return false; + + return READ_ONCE(__scm->shm_bridge_enabled); +} +EXPORT_SYMBOL_GPL(qcom_scm_shm_bridge_available); + +/* + * Must not be called unless qcom_scm_shm_bridge_available() returned true + * first. + */ +int qcom_scm_enable_shm_bridge(void) +{ + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_MP, + .cmd = QCOM_SCM_MP_SHM_BRIDGE_ENABLE, + .owner = ARM_SMCCC_OWNER_SIP + }; + + struct qcom_scm_res res; + int ret; + + ret = qcom_scm_call(__scm->dev, &desc, &res); + if (!ret && !res.result[0]) + WRITE_ONCE(__scm->shm_bridge_enabled, true); + + if (res.result[0] == SCM_SHM_BRIDGE_NOTSUPP) + ret = -EOPNOTSUPP; + + return ret ?: res.result[0]; +} +EXPORT_SYMBOL_GPL(qcom_scm_enable_shm_bridge); + +int qcom_scm_create_shm_bridge(struct device *dev, u64 pfn_and_ns_perm_flags, + u64 ipfn_and_s_perm_flags, u64 size_and_flags, + u64 ns_vmids, u64 *handle) +{ + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_MP, + .cmd = QCOM_SCM_MP_SHM_BRDIGE_CREATE, + .owner = ARM_SMCCC_OWNER_SIP + }; + + struct qcom_scm_res res; + int ret; + + desc.args[0] = pfn_and_ns_perm_flags; + desc.args[1] = ipfn_and_s_perm_flags; + desc.args[2] = size_and_flags; + desc.args[3] = ns_vmids; + + desc.arginfo = QCOM_SCM_ARGS(4, QCOM_SCM_VAL, QCOM_SCM_VAL, + QCOM_SCM_VAL, QCOM_SCM_VAL); + + ret = qcom_scm_call(dev ?: __scm->dev, &desc, &res); + + if (handle && !ret) + *handle = res.result[1]; + + return ret ?: res.result[0]; +} +EXPORT_SYMBOL_GPL(qcom_scm_create_shm_bridge); + +int qcom_scm_delete_shm_bridge(struct device *dev, u64 handle) +{ + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_MP, + .cmd = QCOM_SCM_MP_SHM_BRIDGE_DELETE, + .owner = ARM_SMCCC_OWNER_SIP, + .args[0] = handle, + .arginfo = QCOM_SCM_ARGS(1, QCOM_SCM_VAL), + }; + + return qcom_scm_call(dev ?: __scm->dev, &desc, NULL); +} +EXPORT_SYMBOL_GPL(qcom_scm_delete_shm_bridge); + int qcom_scm_lmh_profile_change(u32 profile_id) { struct qcom_scm_desc desc = { diff --git a/drivers/firmware/qcom_scm.h b/drivers/firmware/qcom_scm.h index e6e512bd57d1..44d60d06344b 100644 --- a/drivers/firmware/qcom_scm.h +++ b/drivers/firmware/qcom_scm.h @@ -111,6 +111,9 @@ extern int scm_legacy_call(struct device *dev, const struct qcom_scm_desc *desc, #define QCOM_SCM_MP_IOMMU_SET_CP_POOL_SIZE 0x05 #define QCOM_SCM_MP_VIDEO_VAR 0x08 #define QCOM_SCM_MP_ASSIGN 0x16 +#define QCOM_SCM_MP_SHM_BRIDGE_ENABLE 0x1c +#define QCOM_SCM_MP_SHM_BRIDGE_DELETE 0x1d +#define QCOM_SCM_MP_SHM_BRDIGE_CREATE 0x1e #define QCOM_SCM_SVC_OCMEM 0x0f #define QCOM_SCM_OCMEM_LOCK_CMD 0x01 diff --git a/include/linux/firmware/qcom/qcom_scm.h b/include/linux/firmware/qcom/qcom_scm.h index 0187fc54249e..100770380d97 100644 --- a/include/linux/firmware/qcom/qcom_scm.h +++ b/include/linux/firmware/qcom/qcom_scm.h @@ -5,6 +5,7 @@ #ifndef __QCOM_SCM_H #define __QCOM_SCM_H +#include <linux/device.h> #include <linux/err.h> #include <linux/types.h> #include <linux/cpumask.h> @@ -117,4 +118,11 @@ int qcom_scm_lmh_dcvsh(u32 payload_fn, u32 payload_reg, u32 payload_val, int qcom_scm_lmh_profile_change(u32 profile_id); bool qcom_scm_lmh_dcvsh_available(void); +bool qcom_scm_shm_bridge_available(void); +int qcom_scm_enable_shm_bridge(void); +int qcom_scm_create_shm_bridge(struct device *dev, u64 pfn_and_ns_perm_flags, + u64 ipfn_and_s_perm_flags, u64 size_and_flags, + u64 ns_vmids, u64 *handle); +int qcom_scm_delete_shm_bridge(struct device *dev, u64 handle); + #endif -- 2.39.2