summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/arm/freescale/fsl,s400-api.yaml48
-rw-r--r--Documentation/devicetree/bindings/arm/freescale/fsl,seco_mu.yaml78
-rw-r--r--drivers/firmware/imx/Kconfig22
-rw-r--r--drivers/firmware/imx/Makefile4
-rw-r--r--drivers/firmware/imx/imx-scu-irq.c116
-rw-r--r--drivers/firmware/imx/imx-scu-soc.c9
-rw-r--r--drivers/firmware/imx/imx-scu.c76
-rw-r--r--drivers/firmware/imx/misc.c25
-rw-r--r--drivers/firmware/imx/rm.c162
-rw-r--r--drivers/firmware/imx/scu-pd.c19
-rw-r--r--drivers/firmware/imx/seco.c249
-rw-r--r--drivers/firmware/imx/seco_mu.c1210
-rw-r--r--drivers/firmware/imx/senclave_base_msg.c141
-rw-r--r--drivers/firmware/imx/sentnl_mu.c918
-rw-r--r--drivers/firmware/imx/sentnl_mu.h139
-rw-r--r--include/linux/firmware/imx/ipc.h4
-rw-r--r--include/linux/firmware/imx/sci.h28
-rw-r--r--include/linux/firmware/imx/seco_mu_ioctl.h50
-rw-r--r--include/linux/firmware/imx/senclave_base_msg.h36
-rw-r--r--include/linux/firmware/imx/sentnl_mu_ioctl.h51
-rw-r--r--include/linux/firmware/imx/svc/misc.h9
-rw-r--r--include/linux/firmware/imx/svc/rm.h39
-rw-r--r--include/linux/firmware/imx/svc/seco.h77
23 files changed, 3453 insertions, 57 deletions
diff --git a/Documentation/devicetree/bindings/arm/freescale/fsl,s400-api.yaml b/Documentation/devicetree/bindings/arm/freescale/fsl,s400-api.yaml
new file mode 100644
index 000000000000..fe2c2b69b63c
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/freescale/fsl,s400-api.yaml
@@ -0,0 +1,48 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/arm/freescale/fsl,s400-api.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: NXP S400 Baseline API module
+
+maintainers:
+ - Alice Guo <alice.guo@nxp.com>
+
+description: |
+ In the Sentinel application, the security subsystem uses S4 MU-AP to
+ communicate and coordinate with the SoC host processor. The s400-api firmware
+ driver provides the services to transmit data to and receive data from the
+ S4 MU-AP.
+
+properties:
+ compatible:
+ items:
+ - const: fsl,imx8ulp-s400
+
+ mboxes:
+ description: |
+ Use the mailbox provided by S4 MU-AP device to communicate with the S400.
+ It should contain 2 mailboxes, one for transmitting messages and another
+ for receiving.
+ maxItems: 1
+
+ mbox-names:
+ items:
+ - const: tx
+ - const: rx
+
+required:
+ - compatible
+ - mboxes
+ - mbox-names
+
+additionalProperties: false
+
+examples:
+ - |
+ s400-api {
+ compatible = "fsl,imx8ulp-s400";
+ mboxes = <&s4muap 0 0 &s4muap 1 0>;
+ mbox-names = "tx", "rx";
+ };
diff --git a/Documentation/devicetree/bindings/arm/freescale/fsl,seco_mu.yaml b/Documentation/devicetree/bindings/arm/freescale/fsl,seco_mu.yaml
new file mode 100644
index 000000000000..c36bea2079c2
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/freescale/fsl,seco_mu.yaml
@@ -0,0 +1,78 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/arm/freescale/fsl,seco_mu.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: NXP i.MX8 SECO MU driver
+
+maintainers:
+ - Aisheng Dong <aisheng.dong@nxp.com>
+
+description: |
+ Create char devices in /dev as channels of the form /dev/seco_muXchY with X
+ the id of the driver and Y for each users. It allows to send and receive
+ messages to the SECO.
+
+properties:
+ compatible:
+ enum:
+ - fsl,imx-seco-mu
+
+ mboxes:
+ description:
+ List of <&phandle type channel> - 4 channels for TX, 4 channels for RX,
+ 1 channel for TXDB (see mailbox/fsl,mu.txt)
+ maxItems: 9
+
+ mbox-names:
+ items:
+ - const: txdb
+ - const: rxdb
+
+ fsl,seco_mu_id:
+ description:
+ Identify the driver instance, used to create the channels, default to 1
+ allOf:
+ - $ref: /schemas/types.yaml#/definitions/uint32
+ - enum: [0,1,2,3]
+
+ fsl,seco_max_users:
+ description:
+ Number of channels to create, default to 4
+ allOf:
+ - $ref: /schemas/types.yaml#/definitions/uint32
+ - enum: [0,1,2,3,4,5,6,7,8,9]
+
+ fsl,cmd_tag:
+ description:
+ Tag in message header for commands on this MU, default to 0x17
+ allOf:
+ - $ref: /schemas/types.yaml#/definitions/uint8
+ - enum: [0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e]
+
+ fsl,rsp_tag:
+ description:
+ Tag in message header for responses on this MU, default to 0xe1
+ allOf:
+ - $ref: /schemas/types.yaml#/definitions/uint8
+ - enum: [0xe1,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8]
+
+required:
+ - compatible
+ - mboxes
+ - mbox-names
+
+examples:
+ - |
+ seco_mu: seco_mu {
+ compatible = "fsl,imx-seco-mu";
+ mbox-names = "txdb", "rxdb";
+ mboxes = <&mu 2 0
+ &mu 3 0>;
+
+ fsl,seco_mu_id = <1>;
+ fsl,seco_max_users = <4>;
+ fsl,cmd_tag = /bits/ 8 <0x17>;
+ fsl,rsp_tag = /bits/ 8 <0xe1>;
+ };
diff --git a/drivers/firmware/imx/Kconfig b/drivers/firmware/imx/Kconfig
index c027d99f2a59..f7a2ff0b4373 100644
--- a/drivers/firmware/imx/Kconfig
+++ b/drivers/firmware/imx/Kconfig
@@ -28,3 +28,25 @@ config IMX_SCU_PD
depends on IMX_SCU
help
The System Controller Firmware (SCFW) based power domain driver.
+
+config IMX_SECO_MU
+ tristate "i.MX Security Controller (SECO) support"
+ depends on IMX_MBOX
+ default y if IMX_SCU
+
+ help
+ It is possible to use APIs exposed by the SECO like HSM and SHE using the
+ SAB protocol via the shared Messaging Unit. This driver exposes these
+ interfaces via a set of file descriptors allowing to configure shared
+ memory, send and receive messages.
+
+config IMX_SENCLAVE_MU
+ tristate "i.MX Embedded Secure Enclave support."
+ depends on IMX_MBOX
+ default y if ARM64
+
+ help
+ It is possible to use APIs exposed by the iMX Secure Enclave like base, HSM &
+ SHE using the SAB protocol via the shared Messaging Unit. This driver exposes
+ these interfaces via a set of file descriptors allowing to configure shared
+ memory, send and receive messages.
diff --git a/drivers/firmware/imx/Makefile b/drivers/firmware/imx/Makefile
index b76acbade2a0..87ef85208350 100644
--- a/drivers/firmware/imx/Makefile
+++ b/drivers/firmware/imx/Makefile
@@ -1,4 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_IMX_DSP) += imx-dsp.o
-obj-$(CONFIG_IMX_SCU) += imx-scu.o misc.o imx-scu-irq.o rm.o imx-scu-soc.o
+obj-$(CONFIG_IMX_SCU) += imx-scu.o misc.o imx-scu-irq.o rm.o imx-scu-soc.o seco.o
obj-$(CONFIG_IMX_SCU_PD) += scu-pd.o
+obj-${CONFIG_IMX_SECO_MU} += seco_mu.o
+obj-${CONFIG_IMX_SENCLAVE_MU} += sentnl_mu.o senclave_base_msg.o
diff --git a/drivers/firmware/imx/imx-scu-irq.c b/drivers/firmware/imx/imx-scu-irq.c
index d9dcc20945c6..afa1f94f3600 100644
--- a/drivers/firmware/imx/imx-scu-irq.c
+++ b/drivers/firmware/imx/imx-scu-irq.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0+
/*
- * Copyright 2019 NXP
+ * Copyright 2019-2020 NXP
*
* Implementation of the SCU IRQ functions using MU.
*
@@ -11,10 +11,11 @@
#include <linux/firmware/imx/sci.h>
#include <linux/mailbox_client.h>
#include <linux/suspend.h>
+#include <linux/sysfs.h>
+#include <linux/kobject.h>
#define IMX_SC_IRQ_FUNC_ENABLE 1
#define IMX_SC_IRQ_FUNC_STATUS 2
-#define IMX_SC_IRQ_NUM_GROUP 4
static u32 mu_resource_id;
@@ -40,63 +41,102 @@ struct imx_sc_msg_irq_enable {
u8 enable;
} __packed;
+struct scu_wakeup {
+ u32 mask;
+ u32 wakeup_src;
+ bool valid;
+};
+
+/* Sysfs functions */
+struct kobject *wakeup_obj;
+static ssize_t wakeup_source_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf);
+static ssize_t num_wakeup_groups_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf);
+static struct kobj_attribute wakeup_source_attr = __ATTR(wakeup_src, 0660, wakeup_source_show, NULL);
+static struct kobj_attribute num_wakeup_groups = __ATTR(num_wakeup_grps, 0660, num_wakeup_groups_show, NULL);
+
+static struct scu_wakeup scu_irq_wakeup[IMX_SC_IRQ_NUM_GROUP];
+
+
static struct imx_sc_ipc *imx_sc_irq_ipc_handle;
static struct work_struct imx_sc_irq_work;
-static ATOMIC_NOTIFIER_HEAD(imx_scu_irq_notifier_chain);
+static BLOCKING_NOTIFIER_HEAD(imx_scu_irq_notifier_chain);
int imx_scu_irq_register_notifier(struct notifier_block *nb)
{
- return atomic_notifier_chain_register(
+ return blocking_notifier_chain_register(
&imx_scu_irq_notifier_chain, nb);
}
EXPORT_SYMBOL(imx_scu_irq_register_notifier);
int imx_scu_irq_unregister_notifier(struct notifier_block *nb)
{
- return atomic_notifier_chain_unregister(
+ return blocking_notifier_chain_unregister(
&imx_scu_irq_notifier_chain, nb);
}
EXPORT_SYMBOL(imx_scu_irq_unregister_notifier);
static int imx_scu_irq_notifier_call_chain(unsigned long status, u8 *group)
{
- return atomic_notifier_call_chain(&imx_scu_irq_notifier_chain,
+ return blocking_notifier_call_chain(&imx_scu_irq_notifier_chain,
status, (void *)group);
}
static void imx_scu_irq_work_handler(struct work_struct *work)
{
- struct imx_sc_msg_irq_get_status msg;
- struct imx_sc_rpc_msg *hdr = &msg.hdr;
u32 irq_status;
int ret;
u8 i;
for (i = 0; i < IMX_SC_IRQ_NUM_GROUP; i++) {
- hdr->ver = IMX_SC_RPC_VERSION;
- hdr->svc = IMX_SC_RPC_SVC_IRQ;
- hdr->func = IMX_SC_IRQ_FUNC_STATUS;
- hdr->size = 2;
-
- msg.data.req.resource = mu_resource_id;
- msg.data.req.group = i;
-
- ret = imx_scu_call_rpc(imx_sc_irq_ipc_handle, &msg, true);
+ if (scu_irq_wakeup[i].mask) {
+ scu_irq_wakeup[i].valid = false;
+ scu_irq_wakeup[i].wakeup_src = 0;
+ }
+ ret = imx_scu_irq_get_status(i, &irq_status);
if (ret) {
pr_err("get irq group %d status failed, ret %d\n",
i, ret);
return;
}
- irq_status = msg.data.resp.status;
if (!irq_status)
continue;
-
+ if (scu_irq_wakeup[i].mask & irq_status) {
+ scu_irq_wakeup[i].valid = true;
+ scu_irq_wakeup[i].wakeup_src = irq_status & scu_irq_wakeup[i].mask;
+ } else {
+ scu_irq_wakeup[i].wakeup_src = irq_status;
+ }
pm_system_wakeup();
imx_scu_irq_notifier_call_chain(irq_status, &i);
}
}
+int imx_scu_irq_get_status(u8 group, u32 *irq_status)
+{
+ struct imx_sc_msg_irq_get_status msg;
+ struct imx_sc_rpc_msg *hdr = &msg.hdr;
+ int ret;
+
+ hdr->ver = IMX_SC_RPC_VERSION;
+ hdr->svc = IMX_SC_RPC_SVC_IRQ;
+ hdr->func = IMX_SC_IRQ_FUNC_STATUS;
+ hdr->size = 2;
+
+ msg.data.req.resource = mu_resource_id;
+ msg.data.req.group = group;
+
+ ret = imx_scu_call_rpc(imx_sc_irq_ipc_handle, &msg, true);
+ if (ret)
+ return ret;
+
+ if (irq_status)
+ *irq_status = msg.data.resp.status;
+
+ return 0;
+}
+EXPORT_SYMBOL(imx_scu_irq_get_status);
+
int imx_scu_irq_group_enable(u8 group, u32 mask, u8 enable)
{
struct imx_sc_msg_irq_enable msg;
@@ -121,6 +161,11 @@ int imx_scu_irq_group_enable(u8 group, u32 mask, u8 enable)
pr_err("enable irq failed, group %d, mask %d, ret %d\n",
group, mask, ret);
+ if (enable)
+ scu_irq_wakeup[group].mask |= mask;
+ else
+ scu_irq_wakeup[group].mask &= ~mask;
+
return ret;
}
EXPORT_SYMBOL(imx_scu_irq_group_enable);
@@ -130,6 +175,30 @@ static void imx_scu_irq_callback(struct mbox_client *c, void *msg)
schedule_work(&imx_sc_irq_work);
}
+static ssize_t wakeup_source_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ u8 i = 0, size = 0;
+
+ for (i = 0; i < IMX_SC_IRQ_NUM_GROUP; i++) {
+ if (scu_irq_wakeup[i].wakeup_src != 0) {
+ if (scu_irq_wakeup[i].valid)
+ size += sprintf(buf + size, "Wakeup source group = %d, irq = 0x%x\n",
+ i, scu_irq_wakeup[i].wakeup_src);
+ else
+ size += sprintf(buf + size, "Spurious SCU wakeup, group = %d, irq = 0x%x\n",
+ i, scu_irq_wakeup[i].wakeup_src);
+ }
+ }
+ return strlen(buf);
+}
+
+static ssize_t num_wakeup_groups_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", IMX_SC_IRQ_NUM_GROUP);
+}
+
int imx_scu_enable_general_irq_channel(struct device *dev)
{
struct of_phandle_args spec;
@@ -169,6 +238,15 @@ int imx_scu_enable_general_irq_channel(struct device *dev)
mu_resource_id = IMX_SC_R_MU_0A + i;
+ /* Create directory under /sysfs/firmware */
+ wakeup_obj = kobject_create_and_add("scu_wakeup_source", firmware_kobj);
+
+ if (sysfs_create_file(wakeup_obj, &wakeup_source_attr.attr)) {
+ pr_err("Cannot create sysfs file......\n");
+ kobject_put(wakeup_obj);
+ sysfs_remove_file(firmware_kobj, &wakeup_source_attr.attr);
+ }
+
return ret;
}
EXPORT_SYMBOL(imx_scu_enable_general_irq_channel);
diff --git a/drivers/firmware/imx/imx-scu-soc.c b/drivers/firmware/imx/imx-scu-soc.c
index 418a2ac29937..c8d14315d463 100644
--- a/drivers/firmware/imx/imx-scu-soc.c
+++ b/drivers/firmware/imx/imx-scu-soc.c
@@ -37,18 +37,15 @@ static int imx_scu_soc_uid(u64 *soc_uid)
{
struct imx_sc_msg_misc_get_soc_uid msg;
struct imx_sc_rpc_msg *hdr = &msg.hdr;
- int ret;
+
+ memset(&msg, 0, sizeof(msg));
hdr->ver = IMX_SC_RPC_VERSION;
hdr->svc = IMX_SC_RPC_SVC_MISC;
hdr->func = IMX_SC_MISC_FUNC_UNIQUE_ID;
hdr->size = 1;
- ret = imx_scu_call_rpc(imx_sc_soc_ipc_handle, &msg, true);
- if (ret) {
- pr_err("%s: get soc uid failed, ret %d\n", __func__, ret);
- return ret;
- }
+ imx_scu_call_rpc(imx_sc_soc_ipc_handle, &msg, true);
*soc_uid = msg.uid_high;
*soc_uid <<= 32;
diff --git a/drivers/firmware/imx/imx-scu.c b/drivers/firmware/imx/imx-scu.c
index dca79caccd01..fd6de5771841 100644
--- a/drivers/firmware/imx/imx-scu.c
+++ b/drivers/firmware/imx/imx-scu.c
@@ -7,6 +7,7 @@
*
*/
+#include <linux/arm-smccc.h>
#include <linux/err.h>
#include <linux/firmware/imx/ipc.h>
#include <linux/firmware/imx/sci.h>
@@ -19,8 +20,11 @@
#include <linux/of_platform.h>
#include <linux/platform_device.h>
+#include <xen/xen.h>
+
+#define FSL_HVC_SC 0xC6000000
#define SCU_MU_CHAN_NUM 8
-#define MAX_RX_TIMEOUT (msecs_to_jiffies(30))
+#define MAX_RX_TIMEOUT (msecs_to_jiffies(3000))
struct imx_sc_chan {
struct imx_sc_ipc *sc_ipc;
@@ -204,6 +208,7 @@ int imx_scu_call_rpc(struct imx_sc_ipc *sc_ipc, void *msg, bool have_resp)
{
uint8_t saved_svc, saved_func;
struct imx_sc_rpc_msg *hdr;
+ struct arm_smccc_res res;
int ret;
if (WARN_ON(!sc_ipc || !msg))
@@ -218,33 +223,45 @@ int imx_scu_call_rpc(struct imx_sc_ipc *sc_ipc, void *msg, bool have_resp)
saved_func = ((struct imx_sc_rpc_msg *)msg)->func;
}
sc_ipc->count = 0;
- ret = imx_scu_ipc_write(sc_ipc, msg);
- if (ret < 0) {
- dev_err(sc_ipc->dev, "RPC send msg failed: %d\n", ret);
- goto out;
- }
-
- if (have_resp) {
- if (!wait_for_completion_timeout(&sc_ipc->done,
- MAX_RX_TIMEOUT)) {
- dev_err(sc_ipc->dev, "RPC send msg timeout\n");
- mutex_unlock(&sc_ipc->lock);
- return -ETIMEDOUT;
+ sc_ipc->rx_size = 0;
+ if (xen_initial_domain()) {
+ arm_smccc_hvc(FSL_HVC_SC, (uint64_t)msg, !have_resp, 0, 0, 0,
+ 0, 0, &res);
+ if (res.a0)
+ printk("Error FSL_HVC_SC %ld\n", res.a0);
+
+ ret = res.a0;
+
+ } else {
+ ret = imx_scu_ipc_write(sc_ipc, msg);
+ if (ret < 0) {
+ dev_err(sc_ipc->dev, "RPC send msg failed: %d\n", ret);
+ goto out;
}
- /* response status is stored in hdr->func field */
- hdr = msg;
- ret = hdr->func;
- /*
- * Some special SCU firmware APIs do NOT have return value
- * in hdr->func, but they do have response data, those special
- * APIs are defined as void function in SCU firmware, so they
- * should be treated as return success always.
- */
- if ((saved_svc == IMX_SC_RPC_SVC_MISC) &&
- (saved_func == IMX_SC_MISC_FUNC_UNIQUE_ID ||
- saved_func == IMX_SC_MISC_FUNC_GET_BUTTON_STATUS))
- ret = 0;
+ if (have_resp) {
+ if (!wait_for_completion_timeout(&sc_ipc->done,
+ MAX_RX_TIMEOUT)) {
+ dev_err(sc_ipc->dev, "RPC send msg timeout\n");
+ mutex_unlock(&sc_ipc->lock);
+ return -ETIMEDOUT;
+ }
+
+ /* response status is stored in hdr->func field */
+ hdr = msg;
+ ret = hdr->func;
+
+ /*
+ * Some special SCU firmware APIs do NOT have return value
+ * in hdr->func, but they do have response data, those special
+ * APIs are defined as void function in SCU firmware, so they
+ * should be treated as return success always.
+ */
+ if ((saved_svc == IMX_SC_RPC_SVC_MISC) &&
+ (saved_func == IMX_SC_MISC_FUNC_UNIQUE_ID ||
+ saved_func == IMX_SC_MISC_FUNC_GET_BUTTON_STATUS))
+ ret = 0;
+ }
}
out:
@@ -354,7 +371,12 @@ static struct platform_driver imx_scu_driver = {
},
.probe = imx_scu_probe,
};
-builtin_platform_driver(imx_scu_driver);
+
+static int __init imx_scu_driver_init(void)
+{
+ return platform_driver_register(&imx_scu_driver);
+}
+subsys_initcall_sync(imx_scu_driver_init);
MODULE_AUTHOR("Dong Aisheng <aisheng.dong@nxp.com>");
MODULE_DESCRIPTION("IMX SCU firmware protocol driver");
diff --git a/drivers/firmware/imx/misc.c b/drivers/firmware/imx/misc.c
index d073cb3ce699..01878451d4ed 100644
--- a/drivers/firmware/imx/misc.c
+++ b/drivers/firmware/imx/misc.c
@@ -18,6 +18,13 @@ struct imx_sc_msg_req_misc_set_ctrl {
u16 resource;
} __packed __aligned(4);
+
+struct imx_sc_msg_req_misc_set_dma_group {
+ struct imx_sc_rpc_msg hdr;
+ u16 resource;
+ u8 val;
+} __packed __aligned(4);
+
struct imx_sc_msg_req_cpu_start {
struct imx_sc_rpc_msg hdr;
u32 address_hi;
@@ -67,6 +74,24 @@ int imx_sc_misc_set_control(struct imx_sc_ipc *ipc, u32 resource,
}
EXPORT_SYMBOL(imx_sc_misc_set_control);
+int imx_sc_misc_set_dma_group(struct imx_sc_ipc *ipc, u32 resource,
+ u32 val)
+{
+ struct imx_sc_msg_req_misc_set_dma_group msg;
+ struct imx_sc_rpc_msg *hdr = &msg.hdr;
+
+ hdr->ver = IMX_SC_RPC_VERSION;
+ hdr->svc = (uint8_t)IMX_SC_RPC_SVC_MISC;
+ hdr->func = (uint8_t)IMX_SC_MISC_FUNC_SET_DMA_GROUP;
+ hdr->size = 2;
+
+ msg.val = val;
+ msg.resource = resource;
+
+ return imx_scu_call_rpc(ipc, &msg, true);
+}
+EXPORT_SYMBOL(imx_sc_misc_set_dma_group);
+
/*
* This function gets a miscellaneous control value.
*
diff --git a/drivers/firmware/imx/rm.c b/drivers/firmware/imx/rm.c
index a12db6ff323b..6dd4db3861d7 100644
--- a/drivers/firmware/imx/rm.c
+++ b/drivers/firmware/imx/rm.c
@@ -13,6 +13,11 @@ struct imx_sc_msg_rm_rsrc_owned {
u16 resource;
} __packed __aligned(4);
+struct imx_sc_msg_rm_pt {
+ struct imx_sc_rpc_msg hdr;
+ u8 val;
+} __packed __aligned(4);
+
/*
* This function check @resource is owned by current partition or not
*
@@ -43,3 +48,160 @@ bool imx_sc_rm_is_resource_owned(struct imx_sc_ipc *ipc, u16 resource)
return hdr->func;
}
EXPORT_SYMBOL(imx_sc_rm_is_resource_owned);
+
+/*
+ * This function returns the current partition number
+ *
+ * @param[in] ipc IPC handle
+ * @param[out] pt holding the partition number
+ *
+ * @return Returns 0 for success and < 0 for errors.
+ */
+int imx_sc_rm_get_partition(struct imx_sc_ipc *ipc, u8 *pt)
+{
+ struct imx_sc_msg_rm_pt msg;
+ struct imx_sc_rpc_msg *hdr = &msg.hdr;
+ int ret;
+
+ hdr->ver = IMX_SC_RPC_VERSION;
+ hdr->svc = IMX_SC_RPC_SVC_RM;
+ hdr->func = IMX_SC_RM_FUNC_GET_PARTITION;
+ hdr->size = 1;
+
+ ret = imx_scu_call_rpc(ipc, &msg, true);
+ if (ret)
+ return ret;
+
+ if (pt != NULL)
+ *pt = msg.val;
+
+ return 0;
+}
+EXPORT_SYMBOL(imx_sc_rm_get_partition);
+
+struct imx_sc_msg_rm_find_memreg {
+ struct imx_sc_rpc_msg hdr;
+ union {
+ struct {
+ u32 add_start_hi;
+ u32 add_start_lo;
+ u32 add_end_hi;
+ u32 add_end_lo;
+ } req;
+ struct {
+ u8 val;
+ } resp;
+ } data;
+} __packed __aligned(4);
+
+int imx_sc_rm_find_memreg(struct imx_sc_ipc *ipc, u8 *mr, u64 addr_start,
+ u64 addr_end)
+{
+ struct imx_sc_msg_rm_find_memreg msg;
+ struct imx_sc_rpc_msg *hdr = &msg.hdr;
+ int ret;
+
+ hdr->ver = IMX_SC_RPC_VERSION;
+ hdr->svc = IMX_SC_RPC_SVC_RM;
+ hdr->func = IMX_SC_RM_FUNC_FIND_MEMREG;
+ hdr->size = 5;
+
+ msg.data.req.add_start_hi = addr_start >> 32;
+ msg.data.req.add_start_lo = addr_start;
+ msg.data.req.add_end_hi = addr_end >> 32;
+ msg.data.req.add_end_lo = addr_end;
+
+ ret = imx_scu_call_rpc(ipc, &msg, true);
+ if (ret)
+ return ret;
+
+ if (mr)
+ *mr = msg.data.resp.val;
+
+ return 0;
+}
+EXPORT_SYMBOL(imx_sc_rm_find_memreg);
+
+struct imx_sc_msg_rm_get_resource_owner {
+ struct imx_sc_rpc_msg hdr;
+ union {
+ struct {
+ u16 resource;
+ } req;
+ struct {
+ u8 val;
+ } resp;
+ } data;
+} __packed __aligned(4);
+
+int imx_sc_rm_get_resource_owner(struct imx_sc_ipc *ipc, u16 resource, u8 *pt)
+{
+ struct imx_sc_msg_rm_get_resource_owner msg;
+ struct imx_sc_rpc_msg *hdr = &msg.hdr;
+ int ret;
+
+ hdr->ver = IMX_SC_RPC_VERSION;
+ hdr->svc = IMX_SC_RPC_SVC_RM;
+ hdr->func = IMX_SC_RM_FUNC_GET_RESOURCE_OWNER;
+ hdr->size = 2;
+
+ msg.data.req.resource = resource;
+
+ ret = imx_scu_call_rpc(ipc, &msg, true);
+ if (ret)
+ return ret;
+
+ if (pt)
+ *pt = msg.data.resp.val;
+
+ return 0;
+}
+EXPORT_SYMBOL(imx_sc_rm_get_resource_owner);
+
+struct imx_sc_msg_set_memreg_permissions {
+ struct imx_sc_rpc_msg hdr;
+ u8 mr;
+ u8 pt;
+ u8 perm;
+} __packed __aligned(4);
+
+int imx_sc_rm_set_memreg_permissions(struct imx_sc_ipc *ipc, u8 mr,
+ u8 pt, u8 perm)
+{
+ struct imx_sc_msg_set_memreg_permissions msg;
+ struct imx_sc_rpc_msg *hdr = &msg.hdr;
+
+ hdr->ver = IMX_SC_RPC_VERSION;
+ hdr->svc = IMX_SC_RPC_SVC_RM;
+ hdr->func = IMX_SC_RM_FUNC_SET_MEMREG_PERMISSIONS;
+ hdr->size = 2;
+
+ msg.mr = mr;
+ msg.pt = pt;
+ msg.perm = perm;
+
+ return imx_scu_call_rpc(ipc, &msg, true);
+}
+EXPORT_SYMBOL(imx_sc_rm_set_memreg_permissions);
+
+int imx_sc_rm_get_did(struct imx_sc_ipc *ipc, u8 *did)
+{
+ struct imx_sc_rpc_msg msg;
+ struct imx_sc_rpc_msg *hdr = &msg;
+ int ret;
+
+ hdr->ver = IMX_SC_RPC_VERSION;
+ hdr->svc = IMX_SC_RPC_SVC_RM;
+ hdr->func = IMX_SC_RM_FUNC_GET_DID;
+ hdr->size = 1;
+
+ ret = imx_scu_call_rpc(ipc, &msg, true);
+ if (ret < 0)
+ return ret;
+
+ if (did)
+ *did = msg.func;
+
+ return 0;
+}
+EXPORT_SYMBOL(imx_sc_rm_get_did);
diff --git a/drivers/firmware/imx/scu-pd.c b/drivers/firmware/imx/scu-pd.c
index 9dfec68219e3..75778c6eb9bf 100644
--- a/drivers/firmware/imx/scu-pd.c
+++ b/drivers/firmware/imx/scu-pd.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2016 Freescale Semiconductor, Inc.
- * Copyright 2017-2018 NXP
+ * Copyright 2017-2018,2020 NXP
* Dong Aisheng <aisheng.dong@nxp.com>
*
* Implementation of the SCU based Power Domains
@@ -108,6 +108,7 @@ static const struct imx_sc_pd_range imx8qxp_scu_pd_ranges[] = {
/* CONN SS */
{ "usb", IMX_SC_R_USB_0, 2, true, 0 },
{ "usb0phy", IMX_SC_R_USB_0_PHY, 1, false, 0 },
+ { "usb1phy", IMX_SC_R_USB_1_PHY, 1, false, 0},
{ "usb2", IMX_SC_R_USB_2, 1, false, 0 },
{ "usb2phy", IMX_SC_R_USB_2_PHY, 1, false, 0 },
{ "sdhc", IMX_SC_R_SDHC_0, 3, true, 0 },
@@ -147,7 +148,7 @@ static const struct imx_sc_pd_range imx8qxp_scu_pd_ranges[] = {
{ "lcd", IMX_SC_R_LCD_0, 1, true, 0 },
{ "lcd-pll", IMX_SC_R_ELCDIF_PLL, 1, true, 0 },
{ "lcd0-pwm", IMX_SC_R_LCD_0_PWM_0, 1, true, 0 },
- { "lpuart", IMX_SC_R_UART_0, 4, true, 0 },
+ { "lpuart", IMX_SC_R_UART_0, 5, true, 0 },
{ "lpspi", IMX_SC_R_SPI_0, 4, true, 0 },
{ "irqstr_dsp", IMX_SC_R_IRQSTR_DSP, 1, false, 0 },
@@ -202,6 +203,14 @@ static const struct imx_sc_pd_range imx8qxp_scu_pd_ranges[] = {
{ "img-jpegdec-s0", IMX_SC_R_MJPEG_DEC_S0, 4, true, 0 },
{ "img-jpegenc-mp", IMX_SC_R_MJPEG_ENC_MP, 1, false, 0 },
{ "img-jpegenc-s0", IMX_SC_R_MJPEG_ENC_S0, 4, true, 0 },
+
+ /* SECO SS */
+ { "seco_mu", IMX_SC_R_SECO_MU_2, 3, true, 2},
+
+ /* V2X SS */
+ { "v2x_mu", IMX_SC_R_V2X_MU_0, 2, true, 0},
+ { "v2x_mu", IMX_SC_R_V2X_MU_2, 1, true, 2},
+ { "v2x_mu", IMX_SC_R_V2X_MU_3, 2, true, 3},
};
static const struct imx_sc_pd_soc imx8qxp_scu_pd = {
@@ -252,6 +261,12 @@ static int imx_sc_pd_power(struct generic_pm_domain *domain, bool power_on)
msg.mode = power_on ? IMX_SC_PM_PW_MODE_ON : IMX_SC_PM_PW_MODE_LP;
ret = imx_scu_call_rpc(pm_ipc_handle, &msg, true);
+ if (ret == -EACCES)
+ {
+ pr_warn("Resource %d not owned by partition, power state unchanged\n",
+ pd->rsrc);
+ return 0;
+ }
if (ret)
dev_err(&domain->dev, "failed to power %s resource %d ret %d\n",
power_on ? "up" : "off", pd->rsrc, ret);
diff --git a/drivers/firmware/imx/seco.c b/drivers/firmware/imx/seco.c
new file mode 100644
index 000000000000..18232c70053b
--- /dev/null
+++ b/drivers/firmware/imx/seco.c
@@ -0,0 +1,249 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2020 NXP
+ *
+ * File containing client-side RPC functions for the SECO service. These
+ * function are ported to clients that communicate to the SC.
+ */
+
+#include <linux/firmware/imx/sci.h>
+
+struct imx_sc_msg_seco_get_build_id {
+ struct imx_sc_rpc_msg hdr;
+ u32 version;
+ u32 commit;
+} __packed __aligned(4);
+
+int imx_sc_seco_build_info(struct imx_sc_ipc *ipc, uint32_t *version,
+ uint32_t *commit)
+{
+ struct imx_sc_msg_seco_get_build_id msg = {0};
+ struct imx_sc_rpc_msg *hdr = &msg.hdr;
+
+ hdr->ver = IMX_SC_RPC_VERSION;
+ hdr->svc = IMX_SC_RPC_SVC_SECO;
+ hdr->func = IMX_SC_SECO_FUNC_BUILD_INFO;
+ hdr->size = 1;
+
+ imx_scu_call_rpc(ipc, &msg, true);
+
+ if (version)
+ *version = msg.version;
+ if (commit)
+ *commit = msg.commit;
+
+ return 0;
+}
+EXPORT_SYMBOL(imx_sc_seco_build_info);
+
+struct imx_sc_msg_seco_sab_msg {
+ struct imx_sc_rpc_msg hdr;
+ u32 smsg_addr_hi;
+ u32 smsg_addr_lo;
+} __packed __aligned(4);
+
+int imx_sc_seco_sab_msg(struct imx_sc_ipc *ipc, u64 smsg_addr)
+{
+ struct imx_sc_msg_seco_sab_msg msg;
+ struct imx_sc_rpc_msg *hdr = &msg.hdr;
+ int ret;
+
+ hdr->ver = IMX_SC_RPC_VERSION;
+ hdr->svc = IMX_SC_RPC_SVC_SECO;
+ hdr->func = IMX_SC_SECO_FUNC_SAB_MSG;
+ hdr->size = 3;
+
+ msg.smsg_addr_hi = smsg_addr >> 32;
+ msg.smsg_addr_lo = smsg_addr;
+
+ ret = imx_scu_call_rpc(ipc, &msg, true);
+ return ret;
+}
+EXPORT_SYMBOL(imx_sc_seco_sab_msg);
+
+int imx_sc_seco_secvio_enable(struct imx_sc_ipc *ipc)
+{
+ struct imx_sc_rpc_msg msg;
+ struct imx_sc_rpc_msg *hdr = &msg;
+ int ret;
+
+ hdr->ver = IMX_SC_RPC_VERSION;
+ hdr->svc = (uint8_t)IMX_SC_RPC_SVC_SECO;
+ hdr->func = (uint8_t)IMX_SC_SECO_FUNC_SECVIO_ENABLE;
+ hdr->size = 1;
+
+ ret = imx_scu_call_rpc(ipc, &msg, true);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+EXPORT_SYMBOL(imx_sc_seco_secvio_enable);
+
+struct imx_sc_msg_req_seco_config {
+ struct imx_sc_rpc_msg hdr;
+ u32 data0;
+ u32 data1;
+ u32 data2;
+ u32 data3;
+ u32 data4;
+ u8 id;
+ u8 access;
+ u8 size;
+} __packed __aligned(4);
+
+struct imx_sc_msg_resp_seco_config {
+ struct imx_sc_rpc_msg hdr;
+ u32 data0;
+ u32 data1;
+ u32 data2;
+ u32 data3;
+ u32 data4;
+} __packed __aligned(4);
+
+int imx_sc_seco_secvio_config(struct imx_sc_ipc *ipc, u8 id, u8 access,
+ u32 *data0, u32 *data1, u32 *data2, u32 *data3,
+ u32 *data4, u8 size)
+{
+ struct imx_sc_msg_req_seco_config msg;
+ struct imx_sc_msg_resp_seco_config *resp;
+ struct imx_sc_rpc_msg *hdr = &msg.hdr;
+ int ret;
+
+ if (!ipc)
+ return -EINVAL;
+
+ hdr->ver = IMX_SC_RPC_VERSION;
+ hdr->svc = (uint8_t)IMX_SC_RPC_SVC_SECO;
+ hdr->func = (uint8_t)IMX_SC_SECO_FUNC_SECVIO_CONFIG;
+ hdr->size = 7;
+
+ /* Check the pointers on data are valid and set it if doing a write */
+ switch (size) {
+ case 5:
+ if (data4) {
+ if (access)
+ msg.data4 = *data4;
+ } else {
+ return -EINVAL;
+ }
+ fallthrough;
+ case 4:
+ if (data3) {
+ if (access)
+ msg.data3 = *data3;
+ } else {
+ return -EINVAL;
+ }
+ fallthrough;
+ case 3:
+ if (data2) {
+ if (access)
+ msg.data2 = *data2;
+ } else {
+ return -EINVAL;
+ }
+ fallthrough;
+ case 2:
+ if (data1) {
+ if (access)
+ msg.data1 = *data1;
+ } else {
+ return -EINVAL;
+ }
+ fallthrough;
+ case 1:
+ if (data0) {
+ if (access)
+ msg.data0 = *data0;
+ } else {
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ msg.id = id;
+ msg.access = access;
+ msg.size = size;
+
+ ret = imx_scu_call_rpc(ipc, &msg, true);
+ if (ret)
+ return ret;
+
+ resp = (struct imx_sc_msg_resp_seco_config *)&msg;
+
+ /* Pointers already checked so we just copy the data if reading */
+ if (!access)
+ switch (size) {
+ case 5:
+ *data4 = resp->data4;
+ fallthrough;
+ case 4:
+ *data3 = resp->data3;
+ fallthrough;
+ case 3:
+ *data2 = resp->data2;
+ fallthrough;
+ case 2:
+ *data1 = resp->data1;
+ fallthrough;
+ case 1:
+ *data0 = resp->data0;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(imx_sc_seco_secvio_config);
+
+struct imx_sc_msg_req_seco_dgo_config {
+ struct imx_sc_rpc_msg hdr;
+ u32 data;
+ u8 id;
+ u8 access;
+} __packed __aligned(4);
+
+struct imx_sc_msg_resp_seco_dgo_config {
+ struct imx_sc_rpc_msg hdr;
+ u32 data;
+} __packed __aligned(4);
+
+int imx_sc_seco_secvio_dgo_config(struct imx_sc_ipc *ipc, u8 id, u8 access,
+ u32 *data)
+{
+ struct imx_sc_msg_req_seco_dgo_config msg;
+ struct imx_sc_msg_resp_seco_dgo_config *resp;
+ struct imx_sc_rpc_msg *hdr = &msg.hdr;
+ int ret;
+
+ if (!ipc)
+ return -EINVAL;
+
+ hdr->ver = IMX_SC_RPC_VERSION;
+ hdr->svc = (uint8_t)IMX_SC_RPC_SVC_SECO;
+ hdr->func = (uint8_t)IMX_SC_SECO_FUNC_SECVIO_DGO_CONFIG;
+ hdr->size = 3;
+
+ if (access) {
+ if (data)
+ msg.data = *data;
+ else
+ return -EINVAL;
+ }
+
+ msg.access = access;
+ msg.id = id;
+
+ ret = imx_scu_call_rpc(ipc, &msg, true);
+ if (ret)
+ return ret;
+
+ resp = (struct imx_sc_msg_resp_seco_dgo_config *)&msg;
+
+ if (!access && data)
+ *data = resp->data;
+
+ return 0;
+}
+EXPORT_SYMBOL(imx_sc_seco_secvio_dgo_config);
diff --git a/drivers/firmware/imx/seco_mu.c b/drivers/firmware/imx/seco_mu.c
new file mode 100644
index 000000000000..02f4295f8099
--- /dev/null
+++ b/drivers/firmware/imx/seco_mu.c
@@ -0,0 +1,1210 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * Copyright 2019-2020 NXP
+ */
+
+/*
+ * This driver allows to send messages to the SECO using a shared mailbox. The
+ * messages must follow the protocol defined.
+ */
+
+/*
+ * Architecture of the driver:
+ *
+ * Non-Secure + Secure
+ * |
+ * |
+ * +---------+ +-------------+ |
+ * |seco_mu.c+<---->+imx-mailbox.c| |
+ * | | | mailbox.c +<-->+------+ +------+
+ * +---+-----+ +-------------+ | MU X +<-->+ SECO |
+ * | +------+ +------+
+ * +----------------+ |
+ * | | |
+ * v v |
+ * logical logical |
+ * receiver waiter |
+ * + + |
+ * | | |
+ * | | |
+ * | +----+------+ |
+ * | | | |
+ * | | | |
+ * device_ctx device_ctx device_ctx |
+ * |
+ * User 0 User 1 User Y |
+ * +------+ +------+ +------+ |
+ * |misc.c| |misc.c| |misc.c| |
+ * kernel space +------+ +------+ +------+ |
+ * |
+ * +------------------------------------------------------ |
+ * | | | |
+ * userspace /dev/seco_muXch0 | | |
+ * /dev/seco_muXch1 | |
+ * /dev/seco_muXchY |
+ * |
+ *
+ * When a user sends a command to the seco, it registers its device_ctx as
+ * waiter of a response from SECO
+ *
+ * A user can be registered as receiver of command by the SECO.
+ *
+ * When a message is received, the driver select the device_ctx receiving the
+ * message depending on the tag in the message. It selects the device_ctx
+ * accordingly.
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/miscdevice.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/uaccess.h>
+#include <linux/firmware/imx/sci.h>
+#include <dt-bindings/firmware/imx/rsrc.h>
+#include <linux/firmware/imx/seco_mu_ioctl.h>
+#include <linux/mailbox_client.h>
+
+#define MAX_RECV_SIZE 31
+#define MAX_RECV_SIZE_BYTES (MAX_RECV_SIZE * sizeof(u32))
+#define MAX_MESSAGE_SIZE 31
+#define MAX_MESSAGE_SIZE_BYTES (MAX_MESSAGE_SIZE * sizeof(u32))
+#define MESSAGE_SIZE(hdr) (((struct she_mu_hdr *)(&(hdr)))->size)
+#define MESSAGE_TAG(hdr) (((struct she_mu_hdr *)(&(hdr)))->tag)
+
+#define DEFAULT_MESSAGING_TAG_COMMAND (0x17u)
+#define DEFAULT_MESSAGING_TAG_RESPONSE (0xe1u)
+
+#define SECURE_RAM_BASE_ADDRESS (0x31800000ULL)
+#define SECURE_RAM_BASE_ADDRESS_SCU (0x20800000u)
+#define SECURE_RAM_SIZE (0x10000ULL)
+
+#define SECO_MU_DEFAULT_MAX_USERS 4
+
+#define SECO_MU_INTERRUPT_INDEX (0u)
+#define SECO_DEFAULT_MU_INDEX (1u)
+#define SECO_DEFAULT_TZ (0u)
+#define DEFAULT_DID (0u)
+
+#define MAX_DATA_SIZE_PER_USER (65 * 1024)
+
+/* Header of the messages exchange with the SECO */
+struct she_mu_hdr {
+ u8 ver;
+ u8 size;
+ u8 command;
+ u8 tag;
+} __packed;
+
+/* Status of a char device */
+enum mu_device_status_t {
+ MU_FREE,
+ MU_OPENED
+};
+
+struct seco_shared_mem {
+ dma_addr_t dma_addr;
+ u32 size;
+ u32 pos;
+ u8 *ptr;
+};
+
+struct seco_out_buffer_desc {
+ u8 *out_ptr;
+ u8 *out_usr_ptr;
+ u32 out_size;
+ struct list_head link;
+};
+
+/* Private struct for each char device instance. */
+struct seco_mu_device_ctx {
+ struct device *dev;
+ struct seco_mu_priv *mu_priv;
+ struct miscdevice miscdev;
+
+ enum mu_device_status_t status;
+ wait_queue_head_t wq;
+ struct semaphore fops_lock;
+
+ u32 pending_hdr;
+ struct list_head pending_out;
+
+ struct seco_shared_mem secure_mem;
+ struct seco_shared_mem non_secure_mem;
+
+ u32 temp_cmd[MAX_MESSAGE_SIZE];
+ u32 temp_resp[MAX_RECV_SIZE];
+ u32 temp_resp_size;
+ struct notifier_block scu_notify;
+ bool v2x_reset;
+};
+
+/* Private struct for seco MU driver. */
+struct seco_mu_priv {
+ struct seco_mu_device_ctx *cmd_receiver_dev;
+ struct seco_mu_device_ctx *waiting_rsp_dev;
+ /*
+ * prevent parallel access to the MU registers
+ * e.g. a user trying to send a command while the other one is
+ * sending a response.
+ */
+ struct mutex mu_lock;
+ /*
+ * prevent a command to be sent on the MU while another one is still
+ * processing. (response to a command is allowed)
+ */
+ struct mutex mu_cmd_lock;
+ struct device *dev;
+ u32 seco_mu_id;
+ u8 cmd_tag;
+ u8 rsp_tag;
+
+ struct mbox_client cl;
+ struct mbox_chan *tx_chan;
+ struct mbox_chan *rx_chan;
+
+ struct imx_sc_ipc *ipc_scu;
+ u8 seco_part_owner;
+};
+
+/* macro to log operation of a misc device */
+#define miscdev_dbg(p_miscdev, fmt, va_args...) \
+ ({ \
+ struct miscdevice *_p_miscdev = p_miscdev; \
+ dev_dbg((_p_miscdev)->parent, "%s: " fmt, (_p_miscdev)->name, \
+ ##va_args); \
+ })
+
+#define miscdev_info(p_miscdev, fmt, va_args...) \
+ ({ \
+ struct miscdevice *_p_miscdev = p_miscdev; \
+ dev_info((_p_miscdev)->parent, "%s: " fmt, (_p_miscdev)->name, \
+ ##va_args); \
+ })
+
+#define miscdev_err(p_miscdev, fmt, va_args...) \
+ ({ \
+ struct miscdevice *_p_miscdev = p_miscdev; \
+ dev_err((_p_miscdev)->parent, "%s: " fmt, (_p_miscdev)->name, \
+ ##va_args); \
+ })
+
+/* macro to log operation of a device context */
+#define devctx_dbg(p_devctx, fmt, va_args...) \
+ miscdev_dbg(&((p_devctx)->miscdev), fmt, ##va_args)
+#define devctx_info(p_devctx, fmt, va_args...) \
+ miscdev_info(&((p_devctx)->miscdev), fmt, ##va_args)
+#define devctx_err(p_devctx, fmt, va_args...) \
+ miscdev_err((&(p_devctx)->miscdev), fmt, ##va_args)
+
+#define IMX_SC_RM_PERM_FULL 7U /* Full access */
+
+/* Give access to SECU to the memory we want to share */
+static int seco_mu_setup_seco_memory_access(struct seco_mu_device_ctx *dev_ctx,
+ u64 addr, u32 len)
+{
+ struct seco_mu_priv *priv = dev_get_drvdata(dev_ctx->dev);
+ int ret;
+ u8 mr;
+
+ ret = imx_sc_rm_find_memreg(priv->ipc_scu, &mr, addr, addr + len);
+ if (ret) {
+ devctx_err(dev_ctx, "Fail find memreg\n");
+ goto exit;
+ }
+
+ ret = imx_sc_rm_set_memreg_permissions(priv->ipc_scu, mr,
+ priv->seco_part_owner,
+ IMX_SC_RM_PERM_FULL);
+ if (ret) {
+ devctx_err(dev_ctx, "Fail set permission for resource\n");
+ goto exit;
+ }
+
+exit:
+ return ret;
+}
+
+/*
+ * File operations for user-space
+ */
+/* Open a char device. */
+static int seco_mu_fops_open(struct inode *nd, struct file *fp)
+{
+ struct seco_mu_device_ctx *dev_ctx = container_of(fp->private_data,
+ struct seco_mu_device_ctx, miscdev);
+ int err;
+
+ /* Avoid race if opened at the same time */
+ if (down_trylock(&dev_ctx->fops_lock))
+ return -EBUSY;
+
+ /* Authorize only 1 instance. */
+ if (dev_ctx->status != MU_FREE) {
+ err = -EBUSY;
+ goto exit;
+ }
+
+ /*
+ * Allocate some memory for data exchanges with SECO.
+ * This will be used for data not requiring secure memory.
+ */
+ dev_ctx->non_secure_mem.ptr = dmam_alloc_coherent(dev_ctx->dev,
+ MAX_DATA_SIZE_PER_USER,
+ &dev_ctx->non_secure_mem.dma_addr,
+ GFP_KERNEL);
+ if (!dev_ctx->non_secure_mem.ptr) {
+ err = -ENOMEM;
+ devctx_err(dev_ctx, "Failed to map shared memory with SECO\n");
+ goto exit;
+ }
+
+ err = seco_mu_setup_seco_memory_access(dev_ctx,
+ dev_ctx->non_secure_mem.dma_addr,
+ MAX_DATA_SIZE_PER_USER);
+ if (err) {
+ err = -EPERM;
+ devctx_err(dev_ctx,
+ "Failed to share access to shared memory\n");
+ goto free_coherent;
+ }
+
+ dev_ctx->non_secure_mem.size = MAX_DATA_SIZE_PER_USER;
+ dev_ctx->non_secure_mem.pos = 0;
+ dev_ctx->status = MU_OPENED;
+
+ dev_ctx->pending_hdr = 0;
+ dev_ctx->v2x_reset = 0;
+
+ goto exit;
+
+free_coherent:
+ dmam_free_coherent(dev_ctx->mu_priv->dev, MAX_DATA_SIZE_PER_USER,
+ dev_ctx->non_secure_mem.ptr,
+ dev_ctx->non_secure_mem.dma_addr);
+
+exit:
+ up(&dev_ctx->fops_lock);
+ return err;
+}
+
+/* Close a char device. */
+static int seco_mu_fops_close(struct inode *nd, struct file *fp)
+{
+ struct seco_mu_device_ctx *dev_ctx = container_of(fp->private_data,
+ struct seco_mu_device_ctx, miscdev);
+ struct seco_mu_priv *mu_priv = dev_ctx->mu_priv;
+ struct seco_out_buffer_desc *out_buf_desc;
+
+ /* Avoid race if closed at the same time */
+ if (down_trylock(&dev_ctx->fops_lock))
+ return -EBUSY;
+
+ /* The device context has not been opened */
+ if (dev_ctx->status != MU_OPENED)
+ goto exit;
+
+ /* check if this device was registered as command receiver. */
+ if (mu_priv->cmd_receiver_dev == dev_ctx)
+ mu_priv->cmd_receiver_dev = NULL;
+
+ /* check if this device was registered as waiting response. */
+ if (mu_priv->waiting_rsp_dev == dev_ctx) {
+ mu_priv->waiting_rsp_dev = NULL;
+ mutex_unlock(&mu_priv->mu_cmd_lock);
+ }
+
+ /* Unmap secure memory shared buffer. */
+ if (dev_ctx->secure_mem.ptr)
+ devm_iounmap(dev_ctx->dev, dev_ctx->secure_mem.ptr);
+
+ dev_ctx->secure_mem.ptr = NULL;
+ dev_ctx->secure_mem.dma_addr = 0;
+ dev_ctx->secure_mem.size = 0;
+ dev_ctx->secure_mem.pos = 0;
+
+ /* Free non-secure shared buffer. */
+ dmam_free_coherent(dev_ctx->mu_priv->dev, MAX_DATA_SIZE_PER_USER,
+ dev_ctx->non_secure_mem.ptr,
+ dev_ctx->non_secure_mem.dma_addr);
+
+ dev_ctx->non_secure_mem.ptr = NULL;
+ dev_ctx->non_secure_mem.dma_addr = 0;
+ dev_ctx->non_secure_mem.size = 0;
+ dev_ctx->non_secure_mem.pos = 0;
+
+ while (!list_empty(&dev_ctx->pending_out)) {
+ out_buf_desc = list_first_entry_or_null(&dev_ctx->pending_out,
+ struct seco_out_buffer_desc,
+ link);
+ __list_del_entry(&out_buf_desc->link);
+ devm_kfree(dev_ctx->dev, out_buf_desc);
+ }
+
+ dev_ctx->status = MU_FREE;
+
+exit:
+ up(&dev_ctx->fops_lock);
+ return 0;
+}
+
+/* Write a message to the MU. */
+static ssize_t seco_mu_fops_write(struct file *fp, const char __user *buf,
+ size_t size, loff_t *ppos)
+{
+ struct seco_mu_device_ctx *dev_ctx = container_of(fp->private_data,
+ struct seco_mu_device_ctx, miscdev);
+ struct seco_mu_priv *mu_priv = dev_ctx->mu_priv;
+ u32 nb_words = 0, header;
+ int err;
+
+ devctx_dbg(dev_ctx, "write from buf (%p)%ld, ppos=%lld\n", buf, size,
+ ((ppos) ? *ppos : 0));
+
+ if (down_interruptible(&dev_ctx->fops_lock))
+ return -EBUSY;
+
+ if (dev_ctx->status != MU_OPENED) {
+ err = -EINVAL;
+ goto exit;
+ }
+
+ if (size < sizeof(struct she_mu_hdr)) {
+ devctx_err(dev_ctx, "User buffer too small(%ld < %lu)\n", size,
+ sizeof(struct she_mu_hdr));
+ err = -ENOSPC;
+ goto exit;
+ }
+
+ if (size > MAX_MESSAGE_SIZE_BYTES) {
+ devctx_err(dev_ctx, "User buffer too big(%ld > %lu)\n", size,
+ MAX_MESSAGE_SIZE_BYTES);
+ err = -ENOSPC;
+ goto exit;
+ }
+
+ /* Copy data to buffer */
+ err = (int)copy_from_user(dev_ctx->temp_cmd, buf, size);
+ if (err) {
+ err = -EFAULT;
+ devctx_err(dev_ctx, "Fail copy message from user\n");
+ goto exit;
+ }
+
+ print_hex_dump_debug("from user ", DUMP_PREFIX_OFFSET, 4, 4,
+ dev_ctx->temp_cmd, size, false);
+
+ header = dev_ctx->temp_cmd[0];
+
+ /* Check the message is valid according to tags */
+ if (MESSAGE_TAG(header) == mu_priv->cmd_tag) {
+ /*
+ * unlocked in seco_mu_receive_work_handler when the
+ * response to this command is received.
+ */
+ mutex_lock(&mu_priv->mu_cmd_lock);
+ mu_priv->waiting_rsp_dev = dev_ctx;
+ } else if (MESSAGE_TAG(header) == mu_priv->rsp_tag) {
+ /* Check the device context can send the command */
+ if (dev_ctx != mu_priv->cmd_receiver_dev) {
+ devctx_err(dev_ctx,
+ "This channel is not configured to send response to SECO\n");
+ err = -EPERM;
+ goto exit;
+ }
+ } else {
+ devctx_err(dev_ctx, "The message does not have a valid TAG\n");
+ err = -EINVAL;
+ goto exit;
+ }
+
+ /*
+ * Check that the size passed as argument matches the size
+ * carried in the message.
+ */
+ nb_words = MESSAGE_SIZE(header);
+ if (nb_words * sizeof(u32) != size) {
+ devctx_err(dev_ctx, "User buffer too small\n");
+ goto exit;
+ }
+
+ mutex_lock(&mu_priv->mu_lock);
+
+ /* Send message */
+ devctx_dbg(dev_ctx, "sending message\n");
+ err = mbox_send_message(mu_priv->tx_chan, dev_ctx->temp_cmd);
+ if (err < 0) {
+ devctx_err(dev_ctx, "Failed to send message\n");
+ goto unlock;
+ }
+
+ err = nb_words * (u32)sizeof(u32);
+
+unlock:
+ mutex_unlock(&mu_priv->mu_lock);
+
+exit:
+ up(&dev_ctx->fops_lock);
+ return err;
+}
+
+/*
+ * Read a message from the MU.
+ * Blocking until a message is available.
+ */
+static ssize_t seco_mu_fops_read(struct file *fp, char __user *buf,
+ size_t size, loff_t *ppos)
+{
+ struct seco_mu_device_ctx *dev_ctx = container_of(fp->private_data,
+ struct seco_mu_device_ctx, miscdev);
+ u32 data_size = 0, size_to_copy = 0;
+ struct seco_out_buffer_desc *b_desc;
+ int err;
+
+ devctx_dbg(dev_ctx, "read to buf %p(%ld), ppos=%lld\n", buf, size,
+ ((ppos) ? *ppos : 0));
+
+ if (down_interruptible(&dev_ctx->fops_lock))
+ return -EBUSY;
+
+ if (dev_ctx->status != MU_OPENED) {
+ err = -EINVAL;
+ goto exit;
+ }
+
+ if (dev_ctx->v2x_reset) {
+ err = -EINVAL;
+ goto exit;
+ }
+
+ /* Wait until the complete message is received on the MU. */
+ err = wait_event_interruptible(dev_ctx->wq, dev_ctx->pending_hdr != 0);
+ if (err) {
+ devctx_err(dev_ctx, "Interrupted by signal\n");
+ goto exit;
+ }
+
+ if (dev_ctx->v2x_reset) {
+ err = -EINVAL;
+ dev_ctx->v2x_reset = 0;
+ goto exit;
+ }
+
+ devctx_dbg(dev_ctx, "%s %s\n", __func__,
+ "message received, start transmit to user");
+
+ /* Check that the size passed as argument is larger than
+ * the one carried in the message.
+ */
+ data_size = dev_ctx->temp_resp_size * sizeof(u32);
+ size_to_copy = data_size;
+ if (size_to_copy > size) {
+ devctx_dbg(dev_ctx, "User buffer too small (%ld < %d)\n",
+ size, size_to_copy);
+ size_to_copy = size;
+ }
+
+ /* We may need to copy the output data to user before
+ * delivering the completion message.
+ */
+ while (!list_empty(&dev_ctx->pending_out)) {
+ b_desc = list_first_entry_or_null(&dev_ctx->pending_out,
+ struct seco_out_buffer_desc,
+ link);
+ if (b_desc->out_usr_ptr && b_desc->out_ptr) {
+ devctx_dbg(dev_ctx, "Copy output data to user\n");
+ err = (int)copy_to_user(b_desc->out_usr_ptr,
+ b_desc->out_ptr,
+ b_desc->out_size);
+ if (err) {
+ devctx_err(dev_ctx,
+ "Failed to copy output data to user\n");
+ err = -EFAULT;
+ goto exit;
+ }
+ }
+ __list_del_entry(&b_desc->link);
+ devm_kfree(dev_ctx->dev, b_desc);
+ }
+
+ /* Copy data from the buffer */
+ print_hex_dump_debug("to user ", DUMP_PREFIX_OFFSET, 4, 4,
+ dev_ctx->temp_resp, size_to_copy, false);
+ err = (int)copy_to_user(buf, dev_ctx->temp_resp, size_to_copy);
+ if (err) {
+ devctx_err(dev_ctx, "Failed to copy to user\n");
+ err = -EFAULT;
+ goto exit;
+ }
+
+ err = size_to_copy;
+
+ /* free memory allocated on the shared buffers. */
+ dev_ctx->secure_mem.pos = 0;
+ dev_ctx->non_secure_mem.pos = 0;
+
+ dev_ctx->pending_hdr = 0;
+
+exit:
+ up(&dev_ctx->fops_lock);
+ return err;
+}
+
+/* Configure the shared memory according to user config */
+static int
+seco_mu_ioctl_shared_mem_cfg_handler(struct seco_mu_device_ctx *dev_ctx,
+ unsigned long arg)
+{
+ struct seco_mu_ioctl_shared_mem_cfg cfg;
+ int err = -EINVAL;
+ u64 high_boundary;
+
+ /* Check if not already configured. */
+ if (dev_ctx->secure_mem.dma_addr != 0u) {
+ devctx_err(dev_ctx, "Shared memory not configured\n");
+ goto exit;
+ }
+
+ err = (int)copy_from_user(&cfg, (u8 *)arg,
+ sizeof(cfg));
+ if (err) {
+ devctx_err(dev_ctx, "Fail copy shared memory config to user\n");
+ err = -EFAULT;
+ goto exit;
+ }
+
+ devctx_dbg(dev_ctx, "cfg offset: %u(%d)\n", cfg.base_offset, cfg.size);
+
+ high_boundary = cfg.base_offset;
+ if (high_boundary > SECURE_RAM_SIZE) {
+ devctx_err(dev_ctx, "base offset is over secure memory\n");
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ high_boundary += cfg.size;
+ if (high_boundary > SECURE_RAM_SIZE) {
+ devctx_err(dev_ctx, "total memory is over secure memory\n");
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ dev_ctx->secure_mem.dma_addr = (dma_addr_t)cfg.base_offset;
+ dev_ctx->secure_mem.size = cfg.size;
+ dev_ctx->secure_mem.pos = 0;
+ dev_ctx->secure_mem.ptr = devm_ioremap(dev_ctx->dev,
+ (phys_addr_t)(SECURE_RAM_BASE_ADDRESS +
+ (u64)dev_ctx->secure_mem.dma_addr),
+ dev_ctx->secure_mem.size);
+ if (!dev_ctx->secure_mem.ptr) {
+ devctx_err(dev_ctx, "Failed to map secure memory\n");
+ err = -ENOMEM;
+ goto exit;
+ }
+
+exit:
+ return err;
+}
+
+/*
+ * Copy a buffer of daa to/from the user and return the address to use in
+ * messages
+ */
+static int seco_mu_ioctl_setup_iobuf_handler(struct seco_mu_device_ctx *dev_ctx,
+ unsigned long arg)
+{
+ struct seco_out_buffer_desc *out_buf_desc;
+ struct seco_mu_ioctl_setup_iobuf io;
+ struct seco_shared_mem *shared_mem;
+ int err = -EINVAL;
+ u32 pos;
+
+ err = (int)copy_from_user(&io,
+ (u8 *)arg,
+ sizeof(io));
+ if (err) {
+ devctx_err(dev_ctx, "Failed copy iobuf config from user\n");
+ err = -EFAULT;
+ goto exit;
+ }
+
+ devctx_dbg(dev_ctx, "io [buf: %p(%d) flag: %x]\n",
+ io.user_buf, io.length, io.flags);
+
+ if (io.length == 0 || !io.user_buf) {
+ /*
+ * Accept NULL pointers since some buffers are optional
+ * in SECO commands. In this case we should return 0 as
+ * pointer to be embedded into the message.
+ * Skip all data copy part of code below.
+ */
+ io.seco_addr = 0;
+ goto copy;
+ }
+
+ /* Select the shared memory to be used for this buffer. */
+ if (io.flags & SECO_MU_IO_FLAGS_USE_SEC_MEM) {
+ /* App requires to use secure memory for this buffer.*/
+ shared_mem = &dev_ctx->secure_mem;
+ } else {
+ /* No specific requirement for this buffer. */
+ shared_mem = &dev_ctx->non_secure_mem;
+ }
+
+ /* Check there is enough space in the shared memory. */
+ if (io.length >= shared_mem->size - shared_mem->pos) {
+ devctx_err(dev_ctx, "Not enough space in shared memory\n");
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ /* Allocate space in shared memory. 8 bytes aligned. */
+ pos = shared_mem->pos;
+ shared_mem->pos += round_up(io.length, 8u);
+ io.seco_addr = (u64)shared_mem->dma_addr + pos;
+
+ if ((io.flags & SECO_MU_IO_FLAGS_USE_SEC_MEM) &&
+ !(io.flags & SECO_MU_IO_FLAGS_USE_SHORT_ADDR))
+ /*Add base address to get full address.*/
+ io.seco_addr += SECURE_RAM_BASE_ADDRESS_SCU;
+
+ if (io.flags & SECO_MU_IO_FLAGS_IS_INPUT) {
+ /*
+ * buffer is input:
+ * copy data from user space to this allocated buffer.
+ */
+ err = (int)copy_from_user(shared_mem->ptr + pos, io.user_buf,
+ io.length);
+ if (err) {
+ devctx_err(dev_ctx,
+ "Failed copy data to shared memory\n");
+ err = -EFAULT;
+ goto exit;
+ }
+ } else {
+ /*
+ * buffer is output:
+ * add an entry in the "pending buffers" list so data
+ * can be copied to user space when receiving SECO
+ * response.
+ */
+ out_buf_desc = devm_kmalloc(dev_ctx->dev, sizeof(*out_buf_desc),
+ GFP_KERNEL);
+ if (!out_buf_desc) {
+ err = -ENOMEM;
+ devctx_err(dev_ctx,
+ "Failed allocating mem for pending buffer\n"
+ );
+ goto exit;
+ }
+
+ out_buf_desc->out_ptr = shared_mem->ptr + pos;
+ out_buf_desc->out_usr_ptr = io.user_buf;
+ out_buf_desc->out_size = io.length;
+ list_add_tail(&out_buf_desc->link, &dev_ctx->pending_out);
+ }
+
+copy:
+ /* Provide the seco address to user space only if success. */
+ err = (int)copy_to_user((u8 *)arg, &io,
+ sizeof(io));
+ if (err) {
+ devctx_err(dev_ctx, "Failed to copy iobuff setup to user\n");
+ err = -EFAULT;
+ goto exit;
+ }
+
+exit:
+ return err;
+}
+
+/* Retrieve info about the MU */
+static int seco_mu_ioctl_get_mu_info_handler(struct seco_mu_device_ctx *dev_ctx,
+ unsigned long arg)
+{
+ struct seco_mu_priv *priv = dev_get_drvdata(dev_ctx->dev);
+ struct seco_mu_ioctl_get_mu_info info;
+ int err = -EINVAL;
+
+ info.seco_mu_idx = (u8)priv->seco_mu_id;
+ info.interrupt_idx = SECO_MU_INTERRUPT_INDEX;
+ info.tz = SECO_DEFAULT_TZ;
+
+ err = imx_sc_rm_get_did(priv->ipc_scu, &info.did);
+ if (err) {
+ devctx_err(dev_ctx, "Get did failed\n");
+ goto exit;
+ }
+
+ devctx_dbg(dev_ctx,
+ "info [mu_idx: %d, irq_idx: %d, tz: 0x%x, did: 0x%x]\n",
+ info.seco_mu_idx, info.interrupt_idx, info.tz, info.did);
+
+ err = (int)copy_to_user((u8 *)arg, &info,
+ sizeof(info));
+ if (err) {
+ devctx_err(dev_ctx, "Failed to copy mu info to user\n");
+ err = -EFAULT;
+ goto exit;
+ }
+
+exit:
+ return err;
+}
+
+static int seco_mu_ioctl_signed_msg_handler(struct seco_mu_device_ctx *dev_ctx,
+ unsigned long arg)
+{
+ struct seco_shared_mem *shared_mem = &dev_ctx->non_secure_mem;
+ struct seco_mu_priv *priv = dev_get_drvdata(dev_ctx->dev);
+ struct seco_mu_ioctl_signed_message msg;
+ int err = -EINVAL;
+ u64 addr;
+ u32 pos;
+
+ err = (int)copy_from_user(&msg,
+ (u8 *)arg,
+ sizeof(msg));
+ if (err) {
+ devctx_err(dev_ctx, "Failed to copy from user: %d\n", err);
+ err = -EFAULT;
+ goto exit;
+ }
+
+ /* Check there is enough space in the shared memory. */
+ if (msg.msg_size >= shared_mem->size - shared_mem->pos) {
+ devctx_err(dev_ctx, "Not enough mem: %d left, %d required\n",
+ shared_mem->size - shared_mem->pos, msg.msg_size);
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ /* Allocate space in shared memory. 8 bytes aligned. */
+ pos = shared_mem->pos;
+
+ /* get physical address from the pos */
+ addr = (u64)shared_mem->dma_addr + pos;
+
+ /* copy signed message from user space to this allocated buffer */
+ err = (int)copy_from_user(shared_mem->ptr + pos, msg.message,
+ msg.msg_size);
+ if (err) {
+ devctx_err(dev_ctx, "Failed to signed message from user: %d\n",
+ err);
+ err = -EFAULT;
+ goto exit;
+ }
+
+ /* Send the message to SECO through SCU */
+ msg.error_code = imx_sc_seco_sab_msg(priv->ipc_scu, addr);
+
+ err = (int)copy_to_user((u8 *)arg, &msg,
+ sizeof(msg));
+ if (err) {
+ devctx_err(dev_ctx, "Failed to copy to user: %d\n", err);
+ err = -EFAULT;
+ goto exit;
+ }
+
+exit:
+ return err;
+}
+
+/* IOCTL entry point of a char device */
+static long seco_mu_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
+{
+ struct seco_mu_device_ctx *dev_ctx = container_of(fp->private_data,
+ struct seco_mu_device_ctx, miscdev);
+ struct seco_mu_priv *mu_priv = dev_ctx->mu_priv;
+ int err = -EINVAL;
+
+ /* Prevent race during change of device context */
+ if (down_interruptible(&dev_ctx->fops_lock))
+ return -EBUSY;
+
+ switch (cmd) {
+ case SECO_MU_IOCTL_ENABLE_CMD_RCV:
+ if (!mu_priv->cmd_receiver_dev) {
+ devctx_dbg(dev_ctx, "setting as receiver\n");
+ mu_priv->cmd_receiver_dev = dev_ctx;
+ err = 0;
+ };
+ break;
+ case SECO_MU_IOCTL_SHARED_BUF_CFG:
+ err = seco_mu_ioctl_shared_mem_cfg_handler(dev_ctx, arg);
+ break;
+ case SECO_MU_IOCTL_SETUP_IOBUF:
+ err = seco_mu_ioctl_setup_iobuf_handler(dev_ctx, arg);
+ break;
+ case SECO_MU_IOCTL_GET_MU_INFO:
+ err = seco_mu_ioctl_get_mu_info_handler(dev_ctx, arg);
+ break;
+ case SECO_MU_IOCTL_SIGNED_MESSAGE:
+ err = seco_mu_ioctl_signed_msg_handler(dev_ctx, arg);
+ break;
+ default:
+ err = -EINVAL;
+ devctx_dbg(dev_ctx, "IOCTL %.8x not supported\n", cmd);
+ }
+
+ up(&dev_ctx->fops_lock);
+ return (long)err;
+}
+
+/*
+ * Callback called by mailbox FW when data are received
+ */
+static void seco_mu_rx_callback(struct mbox_client *c, void *msg)
+{
+ struct device *dev = c->dev;
+ struct seco_mu_priv *priv = dev_get_drvdata(dev);
+ struct seco_mu_device_ctx *dev_ctx;
+ bool is_response = false;
+ int msg_size;
+ u32 header;
+
+ dev_dbg(dev, "Message received on mailbox\n");
+
+ /* The function can be called with NULL msg */
+ if (!msg) {
+ dev_err(dev, "Message is invalid\n");
+ return;
+ }
+
+ if (IS_ERR(msg)) {
+ dev_err(dev, "Error during reception of message: %ld\n",
+ PTR_ERR(msg));
+ return;
+ }
+
+ header = *(u32 *)msg;
+
+ dev_dbg(dev, "Selecting device\n");
+
+ /* Incoming command: wake up the receiver if any. */
+ if (MESSAGE_TAG(header) == priv->cmd_tag) {
+ dev_dbg(dev, "Selecting cmd receiver\n");
+ dev_ctx = priv->cmd_receiver_dev;
+ } else if (MESSAGE_TAG(header) == priv->rsp_tag) {
+ dev_dbg(dev, "Selecting rsp waiter\n");
+ dev_ctx = priv->waiting_rsp_dev;
+ is_response = true;
+ } else {
+ dev_err(dev, "Failed to select a device for message: %.8x\n",
+ header);
+ return;
+ }
+
+ if (!dev_ctx) {
+ dev_err(dev, "No device context selected for message: %.8x\n",
+ header);
+ return;
+ }
+
+ /* Init reception */
+ msg_size = MESSAGE_SIZE(header);
+ if (msg_size > MAX_RECV_SIZE) {
+ devctx_err(dev_ctx, "Message is too big (%d > %d)", msg_size,
+ MAX_RECV_SIZE);
+ return;
+ }
+
+ memcpy(dev_ctx->temp_resp, msg, msg_size * sizeof(u32));
+ dev_ctx->temp_resp_size = msg_size;
+
+ /* Allow user to read */
+ dev_ctx->pending_hdr = dev_ctx->temp_resp[0];
+ wake_up_interruptible(&dev_ctx->wq);
+
+ if (is_response) {
+ /* Allow user to send new command */
+ mutex_unlock(&priv->mu_cmd_lock);
+ }
+}
+
+#define SECO_FW_VER_FEAT_MASK (0x0000FFF0u)
+#define SECO_FW_VER_FEAT_SHIFT (0x04u)
+#define SECO_FW_VER_FEAT_MIN_ALL_MU (0x04u)
+
+/*
+ * Get SECO FW version and check if it supports receiving commands on all MUs
+ * The version is retrieved through SCU since this is the only communication
+ * channel to SECO always present.
+ */
+static int seco_mu_check_all_mu_supported(struct device *dev)
+{
+ struct seco_mu_priv *priv = dev_get_drvdata(dev);
+ u32 seco_ver;
+ int ret;
+
+ ret = imx_sc_seco_build_info(priv->ipc_scu, &seco_ver, NULL);
+ if (ret) {
+ dev_err(dev, "failed to retrieve SECO build info\n");
+ goto exit;
+ }
+
+ if (((seco_ver & SECO_FW_VER_FEAT_MASK) >> SECO_FW_VER_FEAT_SHIFT)
+ < SECO_FW_VER_FEAT_MIN_ALL_MU) {
+ dev_err(dev, "current SECO FW do not support MU with Linux\n");
+ ret = -ENOTSUPP;
+ goto exit;
+ }
+
+exit:
+ return ret;
+}
+
+/* Char driver setup */
+static const struct file_operations seco_mu_fops = {
+ .open = seco_mu_fops_open,
+ .owner = THIS_MODULE,
+ .read = seco_mu_fops_read,
+ .release = seco_mu_fops_close,
+ .write = seco_mu_fops_write,
+ .unlocked_ioctl = seco_mu_ioctl,
+};
+
+/* interface for managed res to free a mailbox channel */
+static void if_mbox_free_channel(void *mbox_chan)
+{
+ mbox_free_channel(mbox_chan);
+}
+
+/* interface for managed res to unregister a char device */
+static void if_misc_deregister(void *miscdevice)
+{
+ misc_deregister(miscdevice);
+}
+
+static int seco_mu_request_channel(struct device *dev,
+ struct mbox_chan **chan,
+ const char *name)
+{
+ struct seco_mu_priv *priv = dev_get_drvdata(dev);
+ struct mbox_chan *t_chan;
+ int ret = 0;
+
+ t_chan = mbox_request_channel_byname(&priv->cl, name);
+ if (IS_ERR(t_chan)) {
+ ret = PTR_ERR(t_chan);
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev,
+ "Failed to request chan %s ret %d\n", name,
+ ret);
+ goto exit;
+ }
+
+ ret = devm_add_action(dev, if_mbox_free_channel, t_chan);
+ if (ret) {
+ dev_err(dev, "failed to add devm removal of mbox %s\n", name);
+ goto exit;
+ }
+
+ *chan = t_chan;
+
+exit:
+ return ret;
+}
+
+static int imx_sc_v2x_reset_notify(struct notifier_block *nb,
+ unsigned long event, void *group)
+{
+ struct seco_mu_device_ctx *dev_ctx = container_of(nb,
+ struct seco_mu_device_ctx, scu_notify);
+
+ if (!(event & IMX_SC_IRQ_V2X_RESET))
+ return 0;
+
+ dev_ctx->v2x_reset = true;
+
+ wake_up_interruptible(&dev_ctx->wq);
+ return 0;
+}
+/* Driver probe.*/
+static int seco_mu_probe(struct platform_device *pdev)
+{
+ struct seco_mu_device_ctx *dev_ctx;
+ struct device *dev = &pdev->dev;
+ struct seco_mu_priv *priv;
+ struct device_node *np;
+ int max_nb_users = 0;
+ char *devname;
+ int ret;
+ int i;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ ret = -ENOMEM;
+ dev_err(dev, "Fail allocate mem for private data\n");
+ goto exit;
+ }
+ priv->dev = dev;
+ dev_set_drvdata(dev, priv);
+
+ /*
+ * Get the address of MU to be used for communication with the SCU
+ */
+ np = pdev->dev.of_node;
+ if (!np) {
+ dev_err(dev, "Cannot find MU User entry in device tree\n");
+ ret = -ENOTSUPP;
+ goto exit;
+ }
+
+ ret = imx_scu_get_handle(&priv->ipc_scu);
+ if (ret) {
+ dev_err(dev, "Fail to retrieve IPC handle\n");
+ goto exit;
+ }
+
+ ret = imx_sc_rm_get_resource_owner(priv->ipc_scu, IMX_SC_R_SECO,
+ &priv->seco_part_owner);
+ if (ret) {
+ dev_err(dev, "Fail get owner of SECO resource\n");
+ goto exit;
+ }
+
+ ret = seco_mu_check_all_mu_supported(dev);
+ if (ret) {
+ dev_err(dev, "Fail seco_mu_check_all_mu_supported\n");
+ goto exit;
+ }
+
+ /* Initialize the mutex. */
+ mutex_init(&priv->mu_cmd_lock);
+ mutex_init(&priv->mu_lock);
+
+ priv->cmd_receiver_dev = NULL;
+ priv->waiting_rsp_dev = NULL;
+
+ ret = of_property_read_u32(np, "fsl,seco_mu_id", &priv->seco_mu_id);
+ if (ret) {
+ dev_warn(dev, "%s: Not able to read mu_id", __func__);
+ priv->seco_mu_id = SECO_DEFAULT_MU_INDEX;
+ }
+
+ ret = of_property_read_u32(np, "fsl,seco_max_users", &max_nb_users);
+ if (ret) {
+ dev_warn(dev, "%s: Not able to read mu_max_user", __func__);
+ max_nb_users = SECO_MU_DEFAULT_MAX_USERS;
+ }
+
+ ret = of_property_read_u8(np, "fsl,cmd_tag", &priv->cmd_tag);
+ if (ret)
+ priv->cmd_tag = DEFAULT_MESSAGING_TAG_COMMAND;
+
+ ret = of_property_read_u8(np, "fsl,rsp_tag", &priv->rsp_tag);
+ if (ret)
+ priv->rsp_tag = DEFAULT_MESSAGING_TAG_RESPONSE;
+
+ /* Mailbox client configuration */
+ priv->cl.dev = dev;
+ priv->cl.knows_txdone = true;
+ priv->cl.rx_callback = seco_mu_rx_callback;
+
+ ret = seco_mu_request_channel(dev, &priv->tx_chan, "txdb");
+ if (ret) {
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "Failed to request txdb channel\n");
+
+ goto exit;
+ }
+
+ ret = seco_mu_request_channel(dev, &priv->rx_chan, "rxdb");
+ if (ret) {
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "Failed to request rxdb channel\n");
+
+ goto exit;
+ }
+
+ /* Create users */
+ for (i = 0; i < max_nb_users; i++) {
+ dev_ctx = devm_kzalloc(dev, sizeof(*dev_ctx), GFP_KERNEL);
+ if (!dev_ctx) {
+ ret = -ENOMEM;
+ dev_err(dev,
+ "Fail to allocate memory for device context\n");
+ goto exit;
+ }
+
+ dev_ctx->dev = dev;
+ dev_ctx->status = MU_FREE;
+ dev_ctx->mu_priv = priv;
+ /* Default value invalid for an header. */
+ init_waitqueue_head(&dev_ctx->wq);
+
+ INIT_LIST_HEAD(&dev_ctx->pending_out);
+ sema_init(&dev_ctx->fops_lock, 1);
+
+ devname = devm_kasprintf(dev, GFP_KERNEL, "seco_mu%d_ch%d",
+ priv->seco_mu_id, i);
+ if (!devname) {
+ ret = -ENOMEM;
+ dev_err(dev,
+ "Fail to allocate memory for misc dev name\n");
+ goto exit;
+ }
+
+ dev_ctx->miscdev.name = devname;
+ dev_ctx->miscdev.minor = MISC_DYNAMIC_MINOR;
+ dev_ctx->miscdev.fops = &seco_mu_fops;
+ dev_ctx->miscdev.parent = dev;
+ ret = misc_register(&dev_ctx->miscdev);
+ if (ret) {
+ dev_err(dev, "failed to register misc device %d\n",
+ ret);
+ goto exit;
+ }
+
+ ret = devm_add_action(dev, if_misc_deregister,
+ &dev_ctx->miscdev);
+
+ dev_ctx->scu_notify.notifier_call = imx_sc_v2x_reset_notify;
+
+ ret = imx_scu_irq_register_notifier(&dev_ctx->scu_notify);
+ if (ret) {
+ dev_err(&pdev->dev, "v2x reqister scu notifier failed.\n");
+ return ret;
+ }
+
+ if (ret)
+ dev_warn(dev,
+ "failed to add managed removal of miscdev\n");
+ }
+
+ ret = imx_scu_irq_group_enable(IMX_SC_IRQ_GROUP_WAKE,
+ IMX_SC_IRQ_V2X_RESET, true);
+ if (ret) {
+ dev_warn(&pdev->dev, "v2x Enable irq failed.\n");
+ return ret;
+ }
+
+exit:
+ return ret;
+}
+
+static const struct of_device_id seco_mu_match[] = {
+ {
+ .compatible = "fsl,imx-seco-mu",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, seco_mu_match);
+
+static struct platform_driver seco_mu_driver = {
+ .driver = {
+ .name = "seco_mu",
+ .of_match_table = seco_mu_match,
+ },
+ .probe = seco_mu_probe,
+};
+
+module_platform_driver(seco_mu_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("IMX Seco MU");
+MODULE_AUTHOR("NXP");
diff --git a/drivers/firmware/imx/senclave_base_msg.c b/drivers/firmware/imx/senclave_base_msg.c
new file mode 100644
index 000000000000..ad8b88648968
--- /dev/null
+++ b/drivers/firmware/imx/senclave_base_msg.c
@@ -0,0 +1,141 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2021 NXP
+ * Author: Pankaj <pankaj.gupta@nxp.com>
+ Alice Guo <alice.guo@nxp.com>
+ */
+
+#include <linux/types.h>
+#include <linux/completion.h>
+#include <linux/mailbox_client.h>
+
+#include <linux/firmware/imx/senclave_base_msg.h>
+#include <linux/firmware/imx/sentnl_mu_ioctl.h>
+
+#include "sentnl_mu.h"
+
+/* Fill a command message header with a given command ID and length in bytes. */
+static int plat_fill_cmd_msg_hdr(struct mu_hdr *hdr, uint8_t cmd, uint32_t len)
+{
+ struct sentnl_mu_priv *priv = NULL;
+ int err = 0;
+
+ err = get_sentnl_mu_priv(&priv);
+ if (err) {
+ pr_err("Error: iMX Sentinel MU is not probed successfully.\n");
+ return err;
+ }
+ hdr->tag = priv->cmd_tag;
+ hdr->ver = MESSAGING_VERSION_6;
+ hdr->command = cmd;
+ hdr->size = (uint8_t)(len / sizeof(uint32_t));
+
+ return err;
+}
+
+static int imx_sentnl_msg_send_rcv(struct sentnl_mu_priv *priv)
+{
+ unsigned int wait;
+ int err = 0;
+
+ mutex_lock(&priv->mu_cmd_lock);
+ mutex_lock(&priv->mu_lock);
+
+ err = mbox_send_message(priv->tx_chan, &priv->tx_msg);
+ if (err < 0) {
+ pr_err("Error: mbox_send_message failure.\n");
+ mutex_unlock(&priv->mu_lock);
+ return err;
+ }
+ mutex_unlock(&priv->mu_lock);
+
+ wait = msecs_to_jiffies(1000);
+ if (!wait_for_completion_timeout(&priv->done, wait)) {
+ mutex_unlock(&priv->mu_cmd_lock);
+ pr_err("Error: wait_for_completion timed out.\n");
+ return -ETIMEDOUT;
+ }
+
+ /* As part of func sentnl_mu_rx_callback() execution,
+ * response will copied to sentnl_msg->rsp_msg.
+ *
+ * Lock: (mutex_unlock(&sentnl_mu_priv->mu_cmd_lock),
+ * will be unlocked if it is a response.
+ */
+ return err;
+}
+
+static int read_otp_uniq_id(struct sentnl_mu_priv *priv, u32 *value)
+{
+ unsigned int tag, command, size, ver, status;
+
+ tag = MSG_TAG(priv->rx_msg.header);
+ command = MSG_COMMAND(priv->rx_msg.header);
+ size = MSG_SIZE(priv->rx_msg.header);
+ ver = MSG_VER(priv->rx_msg.header);
+ status = RES_STATUS(priv->rx_msg.data[0]);
+
+ if (tag == 0xe1 && command == SENTNL_READ_FUSE_REQ &&
+ size == 0x07 && ver == SENTNL_VERSION && status == SENTNL_SUCCESS_IND) {
+ value[0] = priv->rx_msg.data[1];
+ value[1] = priv->rx_msg.data[2];
+ value[2] = priv->rx_msg.data[3];
+ value[3] = priv->rx_msg.data[4];
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int read_fuse_word(struct sentnl_mu_priv *priv, u32 *value)
+{
+ unsigned int tag, command, size, ver, status;
+
+ tag = MSG_TAG(priv->rx_msg.header);
+ command = MSG_COMMAND(priv->rx_msg.header);
+ size = MSG_SIZE(priv->rx_msg.header);
+ ver = MSG_VER(priv->rx_msg.header);
+ status = RES_STATUS(priv->rx_msg.data[0]);
+
+ if (tag == 0xe1 && command == SENTNL_READ_FUSE_REQ &&
+ size == 0x03 && ver == 0x06 && status == SENTNL_SUCCESS_IND) {
+ value[0] = priv->rx_msg.data[1];
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+int read_common_fuse(uint16_t fuse_id, u32 *value)
+{
+ struct sentnl_mu_priv *priv = NULL;
+ int err = 0;
+
+ err = get_sentnl_mu_priv(&priv);
+ if (err) {
+ pr_err("Error: iMX Sentinel MU is not probed successfully.\n");
+ return err;
+ }
+ err = plat_fill_cmd_msg_hdr((struct mu_hdr *)&priv->tx_msg.header, SENTNL_READ_FUSE_REQ, 8);
+ if (err) {
+ pr_err("Error: plat_fill_cmd_msg_hdr failed.\n");
+ return err;
+ }
+
+ priv->tx_msg.data[0] = fuse_id;
+ err = imx_sentnl_msg_send_rcv(priv);
+ if (err < 0)
+ return err;
+
+ switch (fuse_id) {
+ case OTP_UNIQ_ID:
+ err = read_otp_uniq_id(priv, value);
+ break;
+ default:
+ err = read_fuse_word(priv, value);
+ break;
+ }
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(read_common_fuse);
diff --git a/drivers/firmware/imx/sentnl_mu.c b/drivers/firmware/imx/sentnl_mu.c
new file mode 100644
index 000000000000..2913c72f296a
--- /dev/null
+++ b/drivers/firmware/imx/sentnl_mu.c
@@ -0,0 +1,918 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2021 NXP
+ * Author: Alice Guo <alice.guo@nxp.com>
+ * Author: Pankaj Gupta <pankaj.gupta@nxp.com>
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/completion.h>
+#include <linux/dev_printk.h>
+#include <linux/errno.h>
+#include <linux/export.h>
+#include <linux/firmware/imx/senclave_base_msg.h>
+#include <linux/firmware/imx/sentnl_mu_ioctl.h>
+#include <linux/io.h>
+#include <linux/init.h>
+#include <linux/mailbox_client.h>
+#include <linux/miscdevice.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/sys_soc.h>
+
+#include "sentnl_mu.h"
+
+struct sentnl_mu_priv *sentnl_priv_export;
+
+int get_sentnl_mu_priv(struct sentnl_mu_priv **export)
+{
+ if (!sentnl_priv_export)
+ return -EPROBE_DEFER;
+
+ *export = sentnl_priv_export;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(get_sentnl_mu_priv);
+
+
+/*
+ * Callback called by mailbox FW when data are received
+ */
+static void sentnl_mu_rx_callback(struct mbox_client *c, void *msg)
+{
+ struct device *dev = c->dev;
+ struct sentnl_mu_priv *priv = dev_get_drvdata(dev);
+ struct sentnl_mu_device_ctx *dev_ctx;
+ bool is_response = false;
+ int msg_size;
+ struct mu_hdr header;
+
+ dev_dbg(dev, "Message received on mailbox\n");
+
+ /* The function can be called with NULL msg */
+ if (!msg) {
+ dev_err(dev, "Message is invalid\n");
+ return;
+ }
+
+ if (IS_ERR(msg)) {
+ dev_err(dev, "Error during reception of message: %ld\n",
+ PTR_ERR(msg));
+ return;
+ }
+
+ header.tag = ((u8 *)msg)[3];
+ header.command = ((u8 *)msg)[2];
+ header.size = ((u8 *)msg)[1];
+ header.ver = ((u8 *)msg)[0];
+
+ dev_dbg(dev, "Selecting device\n");
+
+ /* Incoming command: wake up the receiver if any. */
+ if (header.tag == priv->cmd_tag) {
+ dev_dbg(dev, "Selecting cmd receiver\n");
+ dev_ctx = priv->cmd_receiver_dev;
+ } else if (header.tag == priv->rsp_tag) {
+ if (priv->waiting_rsp_dev) {
+ dev_dbg(dev, "Selecting rsp waiter\n");
+ dev_ctx = priv->waiting_rsp_dev;
+ is_response = true;
+ } else {
+ /* Reading the Sentinel response
+ * to the command sent by other
+ * linux kernel services.
+ */
+ spin_lock(&priv->lock);
+ priv->rx_msg = *(struct sentnl_api_msg *)msg;
+ complete(&priv->done);
+ spin_unlock(&priv->lock);
+ mutex_unlock(&priv->mu_cmd_lock);
+ return;
+ }
+ } else {
+ dev_err(dev, "Failed to select a device for message: %.8x\n",
+ *((u32 *) &header));
+ return;
+ }
+
+ if (!dev_ctx) {
+ dev_err(dev, "No device context selected for message: %.8x\n",
+ *((u32 *)&header));
+ return;
+ }
+ /* Init reception */
+ msg_size = header.size;
+ if (msg_size > MAX_RECV_SIZE) {
+ devctx_err(dev_ctx, "Message is too big (%d > %d)", msg_size,
+ MAX_RECV_SIZE);
+ return;
+ }
+
+ memcpy(dev_ctx->temp_resp, msg, msg_size * sizeof(u32));
+ dev_ctx->temp_resp_size = msg_size;
+
+ /* Allow user to read */
+ dev_ctx->pending_hdr = dev_ctx->temp_resp[0];
+ wake_up_interruptible(&dev_ctx->wq);
+
+ if (is_response) {
+ /* Allow user to send new command */
+ mutex_unlock(&priv->mu_cmd_lock);
+ }
+}
+
+struct device *imx_soc_device_register(void)
+{
+ struct soc_device_attribute *attr;
+ struct soc_device *dev;
+ u32 v[4];
+ int err;
+
+ err = read_common_fuse(OTP_UNIQ_ID, v);
+ if (err)
+ return NULL;
+
+ attr = kzalloc(sizeof(*attr), GFP_KERNEL);
+ if (!attr)
+ return NULL;
+
+ err = of_property_read_string(of_root, "model", &attr->machine);
+ if (err) {
+ kfree(attr);
+ return NULL;
+ }
+ attr->family = kasprintf(GFP_KERNEL, "Freescale i.MX");
+ attr->revision = kasprintf(GFP_KERNEL, "1.0");
+ attr->serial_number = kasprintf(GFP_KERNEL, "%016llX", (u64)v[3] << 32 | v[0]);
+ attr->soc_id = kasprintf(GFP_KERNEL, "i.MX8ULP");
+
+ dev = soc_device_register(attr);
+ if (IS_ERR(dev)) {
+ kfree(attr->soc_id);
+ kfree(attr->serial_number);
+ kfree(attr->revision);
+ kfree(attr->family);
+ kfree(attr->machine);
+ kfree(attr);
+ return ERR_CAST(dev);
+ }
+
+ return soc_device_to_device(dev);
+}
+
+/*
+ * File operations for user-space
+ */
+
+/* Write a message to the MU. */
+static ssize_t sentnl_mu_fops_write(struct file *fp, const char __user *buf,
+ size_t size, loff_t *ppos)
+{
+ struct sentnl_mu_device_ctx *dev_ctx = container_of(fp->private_data,
+ struct sentnl_mu_device_ctx, miscdev);
+ struct sentnl_mu_priv *sentnl_mu_priv = dev_ctx->priv;
+ u32 nb_words = 0;
+ struct mu_hdr header;
+ int err;
+
+ devctx_dbg(dev_ctx, "write from buf (%p)%ld, ppos=%lld\n", buf, size,
+ ((ppos) ? *ppos : 0));
+
+ if (down_interruptible(&dev_ctx->fops_lock))
+ return -EBUSY;
+
+ if (dev_ctx->status != MU_OPENED) {
+ err = -EINVAL;
+ goto exit;
+ }
+
+ if (size < 4) {//sizeof(struct she_mu_hdr)) {
+ devctx_err(dev_ctx, "User buffer too small(%ld < %x)\n", size, 0x4);
+ //devctx_err(dev_ctx, "User buffer too small(%ld < %lu)\n", size, ()0x4);
+ // sizeof(struct she_mu_hdr));
+ err = -ENOSPC;
+ goto exit;
+ }
+
+ if (size > MAX_MESSAGE_SIZE_BYTES) {
+ devctx_err(dev_ctx, "User buffer too big(%ld > %lu)\n", size,
+ MAX_MESSAGE_SIZE_BYTES);
+ err = -ENOSPC;
+ goto exit;
+ }
+
+ /* Copy data to buffer */
+ err = (int)copy_from_user(dev_ctx->temp_cmd, buf, size);
+ if (err) {
+ err = -EFAULT;
+ devctx_err(dev_ctx, "Fail copy message from user\n");
+ goto exit;
+ }
+
+ print_hex_dump_debug("from user ", DUMP_PREFIX_OFFSET, 4, 4,
+ dev_ctx->temp_cmd, size, false);
+
+ header = *((struct mu_hdr *) (&dev_ctx->temp_cmd[0]));
+
+ /* Check the message is valid according to tags */
+ if (header.tag == sentnl_mu_priv->cmd_tag) {
+ /*
+ * unlocked in sentnl_mu_receive_work_handler when the
+ * response to this command is received.
+ */
+ mutex_lock(&sentnl_mu_priv->mu_cmd_lock);
+ sentnl_mu_priv->waiting_rsp_dev = dev_ctx;
+ } else if (header.tag == sentnl_mu_priv->rsp_tag) {
+ /* Check the device context can send the command */
+ if (dev_ctx != sentnl_mu_priv->cmd_receiver_dev) {
+ devctx_err(dev_ctx,
+ "This channel is not configured to send response to SECO\n");
+ err = -EPERM;
+ goto exit;
+ }
+ } else {
+ devctx_err(dev_ctx, "The message does not have a valid TAG\n");
+ err = -EINVAL;
+ goto exit;
+ }
+
+ /*
+ * Check that the size passed as argument matches the size
+ * carried in the message.
+ */
+ nb_words = header.size;
+ if (nb_words * sizeof(u32) != size) {
+ devctx_err(dev_ctx, "User buffer too small\n");
+ goto exit;
+ }
+
+ mutex_lock(&sentnl_mu_priv->mu_lock);
+
+ /* Send message */
+ devctx_dbg(dev_ctx, "sending message\n");
+ err = mbox_send_message(sentnl_mu_priv->tx_chan, dev_ctx->temp_cmd);
+ if (err < 0) {
+ devctx_err(dev_ctx, "Failed to send message\n");
+ goto unlock;
+ }
+
+ err = nb_words * (u32)sizeof(u32);
+
+unlock:
+ mutex_unlock(&sentnl_mu_priv->mu_lock);
+
+exit:
+ up(&dev_ctx->fops_lock);
+ return err;
+}
+
+/*
+ * Read a message from the MU.
+ * Blocking until a message is available.
+ */
+static ssize_t sentnl_mu_fops_read(struct file *fp, char __user *buf,
+ size_t size, loff_t *ppos)
+{
+ struct sentnl_mu_device_ctx *dev_ctx = container_of(fp->private_data,
+ struct sentnl_mu_device_ctx, miscdev);
+ u32 data_size = 0, size_to_copy = 0;
+ struct sentnl_obuf_desc *b_desc;
+ int err;
+
+ devctx_dbg(dev_ctx, "read to buf %p(%ld), ppos=%lld\n", buf, size,
+ ((ppos) ? *ppos : 0));
+
+ if (down_interruptible(&dev_ctx->fops_lock))
+ return -EBUSY;
+
+ if (dev_ctx->status != MU_OPENED) {
+ err = -EINVAL;
+ goto exit;
+ }
+
+ /* Wait until the complete message is received on the MU. */
+ err = wait_event_interruptible(dev_ctx->wq, dev_ctx->pending_hdr != 0);
+ if (err) {
+ devctx_err(dev_ctx, "Interrupted by signal\n");
+ goto exit;
+ }
+
+ devctx_dbg(dev_ctx, "%s %s\n", __func__,
+ "message received, start transmit to user");
+
+ /* Check that the size passed as argument is larger than
+ * the one carried in the message.
+ */
+ data_size = dev_ctx->temp_resp_size * sizeof(u32);
+ size_to_copy = data_size;
+ if (size_to_copy > size) {
+ devctx_dbg(dev_ctx, "User buffer too small (%ld < %d)\n",
+ size, size_to_copy);
+ size_to_copy = size;
+ }
+
+ /* We may need to copy the output data to user before
+ * delivering the completion message.
+ */
+ while (!list_empty(&dev_ctx->pending_out)) {
+ b_desc = list_first_entry_or_null(&dev_ctx->pending_out,
+ struct sentnl_obuf_desc,
+ link);
+ if (b_desc->out_usr_ptr && b_desc->out_ptr) {
+ devctx_dbg(dev_ctx, "Copy output data to user\n");
+ err = (int)copy_to_user(b_desc->out_usr_ptr,
+ b_desc->out_ptr,
+ b_desc->out_size);
+ if (err) {
+ devctx_err(dev_ctx,
+ "Failed to copy output data to user\n");
+ err = -EFAULT;
+ goto exit;
+ }
+ }
+ __list_del_entry(&b_desc->link);
+ devm_kfree(dev_ctx->dev, b_desc);
+ }
+
+ /* Copy data from the buffer */
+ print_hex_dump_debug("to user ", DUMP_PREFIX_OFFSET, 4, 4,
+ dev_ctx->temp_resp, size_to_copy, false);
+ err = (int)copy_to_user(buf, dev_ctx->temp_resp, size_to_copy);
+ if (err) {
+ devctx_err(dev_ctx, "Failed to copy to user\n");
+ err = -EFAULT;
+ goto exit;
+ }
+
+ err = size_to_copy;
+
+ /* free memory allocated on the shared buffers. */
+ dev_ctx->secure_mem.pos = 0;
+ dev_ctx->non_secure_mem.pos = 0;
+
+ dev_ctx->pending_hdr = 0;
+
+exit:
+ up(&dev_ctx->fops_lock);
+ return err;
+}
+
+/* Give access to Sentinel, to the memory we want to share */
+static int sentnl_mu_setup_sentnl_mem_access(struct sentnl_mu_device_ctx *dev_ctx,
+ u64 addr, u32 len)
+{
+ /* Assuming Sentinel has access to all the memory regions */
+ int ret = 0;
+
+ if (ret) {
+ devctx_err(dev_ctx, "Fail find memreg\n");
+ goto exit;
+ }
+
+ if (ret) {
+ devctx_err(dev_ctx, "Fail set permission for resource\n");
+ goto exit;
+ }
+
+exit:
+ return ret;
+}
+
+static int sentnl_mu_ioctl_get_mu_info(struct sentnl_mu_device_ctx *dev_ctx,
+ unsigned long arg)
+{
+ struct sentnl_mu_priv *priv = dev_get_drvdata(dev_ctx->dev);
+ struct sentnl_mu_ioctl_get_mu_info info;
+ int err = -EINVAL;
+
+ info.sentnl_mu_id = (u8)priv->sentnl_mu_id;
+ info.interrupt_idx = 0;
+ info.tz = 0;
+ info.did = 0x7;
+
+ devctx_dbg(dev_ctx,
+ "info [mu_idx: %d, irq_idx: %d, tz: 0x%x, did: 0x%x]\n",
+ info.sentnl_mu_id, info.interrupt_idx, info.tz, info.did);
+
+ err = (int)copy_to_user((u8 *)arg, &info,
+ sizeof(info));
+ if (err) {
+ devctx_err(dev_ctx, "Failed to copy mu info to user\n");
+ err = -EFAULT;
+ goto exit;
+ }
+
+exit:
+ return err;
+}
+
+/*
+ * Copy a buffer of daa to/from the user and return the address to use in
+ * messages
+ */
+static int sentnl_mu_ioctl_setup_iobuf_handler(struct sentnl_mu_device_ctx *dev_ctx,
+ unsigned long arg)
+{
+ struct sentnl_obuf_desc *out_buf_desc;
+ struct sentnl_mu_ioctl_setup_iobuf io = {0};
+ struct sentnl_shared_mem *shared_mem;
+ int err = -EINVAL;
+ u32 pos;
+
+ err = (int)copy_from_user(&io,
+ (u8 *)arg,
+ sizeof(io));
+ if (err) {
+ devctx_err(dev_ctx, "Failed copy iobuf config from user\n");
+ err = -EFAULT;
+ goto exit;
+ }
+
+ devctx_dbg(dev_ctx, "io [buf: %p(%d) flag: %x]\n",
+ io.user_buf, io.length, io.flags);
+
+ if (io.length == 0 || !io.user_buf) {
+ /*
+ * Accept NULL pointers since some buffers are optional
+ * in SECO commands. In this case we should return 0 as
+ * pointer to be embedded into the message.
+ * Skip all data copy part of code below.
+ */
+ io.sentnl_addr = 0;
+ goto copy;
+ }
+
+ /* Select the shared memory to be used for this buffer. */
+ if (io.flags & SECO_MU_IO_FLAGS_USE_SEC_MEM) {
+ /* App requires to use secure memory for this buffer.*/
+ devctx_err(dev_ctx, "Failed allocate SEC MEM memory\n");
+ err = -EFAULT;
+ goto exit;
+ } else {
+ /* No specific requirement for this buffer. */
+ shared_mem = &dev_ctx->non_secure_mem;
+ }
+
+ /* Check there is enough space in the shared memory. */
+ if (io.length >= shared_mem->size - shared_mem->pos) {
+ devctx_err(dev_ctx, "Not enough space in shared memory\n");
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ /* Allocate space in shared memory. 8 bytes aligned. */
+ pos = shared_mem->pos;
+ shared_mem->pos += round_up(io.length, 8u);
+ io.sentnl_addr = (u64)shared_mem->dma_addr + pos;
+
+ if ((io.flags & SECO_MU_IO_FLAGS_USE_SEC_MEM) &&
+ !(io.flags & SECO_MU_IO_FLAGS_USE_SHORT_ADDR)) {
+ /*Add base address to get full address.*/
+ devctx_err(dev_ctx, "Failed allocate SEC MEM memory\n");
+ err = -EFAULT;
+ goto exit;
+ }
+
+ if (io.flags & SECO_MU_IO_FLAGS_IS_INPUT) {
+ /*
+ * buffer is input:
+ * copy data from user space to this allocated buffer.
+ */
+ err = (int)copy_from_user(shared_mem->ptr + pos, io.user_buf,
+ io.length);
+ if (err) {
+ devctx_err(dev_ctx,
+ "Failed copy data to shared memory\n");
+ err = -EFAULT;
+ goto exit;
+ }
+ } else {
+ /*
+ * buffer is output:
+ * add an entry in the "pending buffers" list so data
+ * can be copied to user space when receiving SECO
+ * response.
+ */
+ out_buf_desc = devm_kmalloc(dev_ctx->dev, sizeof(*out_buf_desc),
+ GFP_KERNEL);
+ if (!out_buf_desc) {
+ err = -ENOMEM;
+ devctx_err(dev_ctx,
+ "Failed allocating mem for pending buffer\n"
+ );
+ goto exit;
+ }
+
+ out_buf_desc->out_ptr = shared_mem->ptr + pos;
+ out_buf_desc->out_usr_ptr = io.user_buf;
+ out_buf_desc->out_size = io.length;
+ list_add_tail(&out_buf_desc->link, &dev_ctx->pending_out);
+ }
+
+copy:
+ /* Provide the sentinel address to user space only if success. */
+ err = (int)copy_to_user((u8 *)arg, &io,
+ sizeof(io));
+ if (err) {
+ devctx_err(dev_ctx, "Failed to copy iobuff setup to user\n");
+ err = -EFAULT;
+ goto exit;
+ }
+exit:
+ return err;
+}
+
+
+
+/* Open a char device. */
+static int sentnl_mu_fops_open(struct inode *nd, struct file *fp)
+{
+ struct sentnl_mu_device_ctx *dev_ctx = container_of(fp->private_data,
+ struct sentnl_mu_device_ctx,
+ miscdev);
+ int err;
+
+ /* Avoid race if opened at the same time */
+ if (down_trylock(&dev_ctx->fops_lock))
+ return -EBUSY;
+
+ /* Authorize only 1 instance. */
+ if (dev_ctx->status != MU_FREE) {
+ err = -EBUSY;
+ goto exit;
+ }
+
+ /*
+ * Allocate some memory for data exchanges with S40x.
+ * This will be used for data not requiring secure memory.
+ */
+ dev_ctx->non_secure_mem.ptr = dmam_alloc_coherent(dev_ctx->dev,
+ MAX_DATA_SIZE_PER_USER,
+ &dev_ctx->non_secure_mem.dma_addr,
+ GFP_KERNEL);
+ if (!dev_ctx->non_secure_mem.ptr) {
+ err = -ENOMEM;
+ devctx_err(dev_ctx, "Failed to map shared memory with S40x\n");
+ goto exit;
+ }
+
+ err = sentnl_mu_setup_sentnl_mem_access(dev_ctx,
+ dev_ctx->non_secure_mem.dma_addr,
+ MAX_DATA_SIZE_PER_USER);
+ if (err) {
+ err = -EPERM;
+ devctx_err(dev_ctx,
+ "Failed to share access to shared memory\n");
+ goto free_coherent;
+ }
+
+ dev_ctx->non_secure_mem.size = MAX_DATA_SIZE_PER_USER;
+ dev_ctx->non_secure_mem.pos = 0;
+ dev_ctx->status = MU_OPENED;
+
+ dev_ctx->pending_hdr = 0;
+
+ goto exit;
+
+free_coherent:
+ dmam_free_coherent(dev_ctx->priv->dev, MAX_DATA_SIZE_PER_USER,
+ dev_ctx->non_secure_mem.ptr,
+ dev_ctx->non_secure_mem.dma_addr);
+
+exit:
+ up(&dev_ctx->fops_lock);
+ return err;
+}
+
+/* Close a char device. */
+static int sentnl_mu_fops_close(struct inode *nd, struct file *fp)
+{
+ struct sentnl_mu_device_ctx *dev_ctx = container_of(fp->private_data,
+ struct sentnl_mu_device_ctx, miscdev);
+ struct sentnl_mu_priv *priv = dev_ctx->priv;
+ struct sentnl_obuf_desc *out_buf_desc;
+
+ /* Avoid race if closed at the same time */
+ if (down_trylock(&dev_ctx->fops_lock))
+ return -EBUSY;
+
+ /* The device context has not been opened */
+ if (dev_ctx->status != MU_OPENED)
+ goto exit;
+
+ /* check if this device was registered as command receiver. */
+ if (priv->cmd_receiver_dev == dev_ctx)
+ priv->cmd_receiver_dev = NULL;
+
+ /* check if this device was registered as waiting response. */
+ if (priv->waiting_rsp_dev == dev_ctx) {
+ priv->waiting_rsp_dev = NULL;
+ mutex_unlock(&priv->mu_cmd_lock);
+ }
+
+ /* Unmap secure memory shared buffer. */
+ if (dev_ctx->secure_mem.ptr)
+ devm_iounmap(dev_ctx->dev, dev_ctx->secure_mem.ptr);
+
+ dev_ctx->secure_mem.ptr = NULL;
+ dev_ctx->secure_mem.dma_addr = 0;
+ dev_ctx->secure_mem.size = 0;
+ dev_ctx->secure_mem.pos = 0;
+
+ /* Free non-secure shared buffer. */
+ dmam_free_coherent(dev_ctx->priv->dev, MAX_DATA_SIZE_PER_USER,
+ dev_ctx->non_secure_mem.ptr,
+ dev_ctx->non_secure_mem.dma_addr);
+
+ dev_ctx->non_secure_mem.ptr = NULL;
+ dev_ctx->non_secure_mem.dma_addr = 0;
+ dev_ctx->non_secure_mem.size = 0;
+ dev_ctx->non_secure_mem.pos = 0;
+
+ while (!list_empty(&dev_ctx->pending_out)) {
+ out_buf_desc = list_first_entry_or_null(&dev_ctx->pending_out,
+ struct sentnl_obuf_desc,
+ link);
+ __list_del_entry(&out_buf_desc->link);
+ devm_kfree(dev_ctx->dev, out_buf_desc);
+ }
+
+ dev_ctx->status = MU_FREE;
+
+exit:
+ up(&dev_ctx->fops_lock);
+ return 0;
+}
+
+/* IOCTL entry point of a char device */
+static long sentnl_mu_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
+{
+ struct sentnl_mu_device_ctx *dev_ctx = container_of(fp->private_data,
+ struct sentnl_mu_device_ctx,
+ miscdev);
+ struct sentnl_mu_priv *sentnl_mu_priv = dev_ctx->priv;
+ int err = -EINVAL;
+
+ /* Prevent race during change of device context */
+ if (down_interruptible(&dev_ctx->fops_lock))
+ return -EBUSY;
+
+ switch (cmd) {
+ case SENTNL_MU_IOCTL_ENABLE_CMD_RCV:
+ if (!sentnl_mu_priv->cmd_receiver_dev) {
+ sentnl_mu_priv->cmd_receiver_dev = dev_ctx;
+ err = 0;
+ };
+ break;
+ case SENTNL_MU_IOCTL_GET_MU_INFO:
+ err = sentnl_mu_ioctl_get_mu_info(dev_ctx, arg);
+ break;
+ case SENTNL_MU_IOCTL_SHARED_BUF_CFG:
+ devctx_err(dev_ctx, "SENTNL_MU_IOCTL_SHARED_BUF_CFG not supported [0x%x].\n", err);
+ break;
+ case SENTNL_MU_IOCTL_SETUP_IOBUF:
+ err = sentnl_mu_ioctl_setup_iobuf_handler(dev_ctx, arg);
+ break;
+ case SENTNL_MU_IOCTL_SIGNED_MESSAGE:
+ devctx_err(dev_ctx, "SENTNL_MU_IOCTL_SIGNED_MESSAGE not supported [0x%x].\n", err);
+ break;
+ default:
+ err = -EINVAL;
+ devctx_dbg(dev_ctx, "IOCTL %.8x not supported\n", cmd);
+ }
+
+ up(&dev_ctx->fops_lock);
+ return (long)err;
+}
+
+/* Char driver setup */
+static const struct file_operations sentnl_mu_fops = {
+ .open = sentnl_mu_fops_open,
+ .owner = THIS_MODULE,
+ .release = sentnl_mu_fops_close,
+ .unlocked_ioctl = sentnl_mu_ioctl,
+ .read = sentnl_mu_fops_read,
+ .write = sentnl_mu_fops_write,
+};
+
+/* interface for managed res to free a mailbox channel */
+static void if_mbox_free_channel(void *mbox_chan)
+{
+ mbox_free_channel(mbox_chan);
+}
+
+/* interface for managed res to unregister a char device */
+static void if_misc_deregister(void *miscdevice)
+{
+ misc_deregister(miscdevice);
+}
+
+static int sentnl_mu_request_channel(struct device *dev,
+ struct mbox_chan **chan,
+ struct mbox_client *cl,
+ const char *name)
+{
+ struct mbox_chan *t_chan;
+ int ret = 0;
+
+ t_chan = mbox_request_channel_byname(cl, name);
+ if (IS_ERR(t_chan)) {
+ ret = PTR_ERR(t_chan);
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev,
+ "Failed to request chan %s ret %d\n", name,
+ ret);
+ goto exit;
+ }
+
+ ret = devm_add_action(dev, if_mbox_free_channel, t_chan);
+ if (ret) {
+ dev_err(dev, "failed to add devm removal of mbox %s\n", name);
+ goto exit;
+ }
+
+ *chan = t_chan;
+
+exit:
+ return ret;
+}
+
+static int sentnl_mu_probe(struct platform_device *pdev)
+{
+ struct sentnl_mu_device_ctx *dev_ctx;
+ struct device *dev = &pdev->dev;
+ struct sentnl_mu_priv *priv;
+ struct device_node *np;
+ int max_nb_users = 0;
+ char *devname;
+ struct device *soc;
+ int ret;
+ int i;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ ret = -ENOMEM;
+ dev_err(dev, "Fail allocate mem for private data\n");
+ goto exit;
+ }
+ priv->dev = dev;
+ dev_set_drvdata(dev, priv);
+
+ /*
+ * Get the address of MU to be used for communication with the SCU
+ */
+ np = pdev->dev.of_node;
+ if (!np) {
+ dev_err(dev, "Cannot find MU User entry in device tree\n");
+ ret = -ENOTSUPP;
+ goto exit;
+ }
+
+ /* Initialize the mutex. */
+ mutex_init(&priv->mu_cmd_lock);
+ mutex_init(&priv->mu_lock);
+
+ /* TBD */
+ priv->cmd_receiver_dev = NULL;
+ priv->waiting_rsp_dev = NULL;
+
+ ret = of_property_read_u32(np, "fsl,sentnl_mu_id", &priv->sentnl_mu_id);
+ if (ret) {
+ dev_warn(dev, "%s: Not able to read mu_id", __func__);
+ priv->sentnl_mu_id = S4_DEFAULT_MUAP_INDEX;
+ }
+
+ ret = of_property_read_u32(np, "fsl,sentnl_mu_max_users", &max_nb_users);
+ if (ret) {
+ dev_warn(dev, "%s: Not able to read mu_max_user", __func__);
+ max_nb_users = S4_MUAP_DEFAULT_MAX_USERS;
+ }
+
+ ret = of_property_read_u8(np, "fsl,cmd_tag", &priv->cmd_tag);
+ if (ret) {
+ dev_warn(dev, "%s: Not able to read cmd_tag", __func__);
+ priv->cmd_tag = DEFAULT_MESSAGING_TAG_COMMAND;
+ }
+
+ ret = of_property_read_u8(np, "fsl,rsp_tag", &priv->rsp_tag);
+ if (ret) {
+ dev_warn(dev, "%s: Not able to read rsp_tag", __func__);
+ priv->rsp_tag = DEFAULT_MESSAGING_TAG_RESPONSE;
+ }
+
+ /* Mailbox client configuration */
+ priv->sentnl_mb_cl.dev = dev;
+ priv->sentnl_mb_cl.tx_block = false;
+ priv->sentnl_mb_cl.knows_txdone = true;
+ priv->sentnl_mb_cl.rx_callback = sentnl_mu_rx_callback;
+
+ ret = sentnl_mu_request_channel(dev, &priv->tx_chan, &priv->sentnl_mb_cl, "tx");
+ if (ret) {
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "Failed to request tx channel\n");
+
+ goto exit;
+ }
+
+ ret = sentnl_mu_request_channel(dev, &priv->rx_chan, &priv->sentnl_mb_cl, "rx");
+ if (ret) {
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "Failed to request rx channel\n");
+
+ goto exit;
+ }
+
+ /* Create users */
+ for (i = 0; i < max_nb_users; i++) {
+ dev_ctx = devm_kzalloc(dev, sizeof(*dev_ctx), GFP_KERNEL);
+ if (!dev_ctx) {
+ ret = -ENOMEM;
+ dev_err(dev,
+ "Fail to allocate memory for device context\n");
+ goto exit;
+ }
+
+ dev_ctx->dev = dev;
+ dev_ctx->status = MU_FREE;
+ dev_ctx->priv = priv;
+ /* Default value invalid for an header. */
+ init_waitqueue_head(&dev_ctx->wq);
+
+ INIT_LIST_HEAD(&dev_ctx->pending_out);
+ sema_init(&dev_ctx->fops_lock, 1);
+
+ devname = devm_kasprintf(dev, GFP_KERNEL, "sentnl_mu%d_ch%d",
+ priv->sentnl_mu_id, i);
+ if (!devname) {
+ ret = -ENOMEM;
+ dev_err(dev,
+ "Fail to allocate memory for misc dev name\n");
+ goto exit;
+ }
+
+ dev_ctx->miscdev.name = devname;
+ dev_ctx->miscdev.minor = MISC_DYNAMIC_MINOR;
+ dev_ctx->miscdev.fops = &sentnl_mu_fops;
+ dev_ctx->miscdev.parent = dev;
+ ret = misc_register(&dev_ctx->miscdev);
+ if (ret) {
+ dev_err(dev, "failed to register misc device %d\n",
+ ret);
+ goto exit;
+ }
+
+ ret = devm_add_action(dev, if_misc_deregister,
+ &dev_ctx->miscdev);
+
+ }
+
+ init_completion(&priv->done);
+ spin_lock_init(&priv->lock);
+
+ sentnl_priv_export = priv;
+
+ soc = imx_soc_device_register();
+ if (IS_ERR(soc)) {
+ pr_err("failed to register SoC device: %ld\n", PTR_ERR(soc));
+ return PTR_ERR(soc);
+ }
+
+ dev_set_drvdata(dev, priv);
+ return devm_of_platform_populate(dev);
+
+exit:
+ return ret;
+}
+
+static int sentnl_mu_remove(struct platform_device *pdev)
+{
+ struct sentnl_mu_priv *priv;
+
+ priv = dev_get_drvdata(&pdev->dev);
+ mbox_free_channel(priv->tx_chan);
+ mbox_free_channel(priv->rx_chan);
+
+ return 0;
+}
+
+static const struct of_device_id sentnl_mu_match[] = {
+ { .compatible = "fsl,imx-sentnl", },
+ {},
+};
+
+static struct platform_driver sentnl_mu_driver = {
+ .driver = {
+ .name = "fsl-sentnl-mu",
+ .of_match_table = sentnl_mu_match,
+ },
+ .probe = sentnl_mu_probe,
+ .remove = sentnl_mu_remove,
+};
+module_platform_driver(sentnl_mu_driver);
+
+MODULE_AUTHOR("Pankaj Gupta <pankaj.gupta@nxp.com>");
+MODULE_DESCRIPTION("iMX Secure Enclave MU Driver.");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/firmware/imx/sentnl_mu.h b/drivers/firmware/imx/sentnl_mu.h
new file mode 100644
index 000000000000..3f5a4488effa
--- /dev/null
+++ b/drivers/firmware/imx/sentnl_mu.h
@@ -0,0 +1,139 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright 2021 NXP
+ */
+
+#ifndef SENTNL_MU_H
+#define SENTNL_MU_H
+
+#include <linux/miscdevice.h>
+#include <linux/semaphore.h>
+
+/* macro to log operation of a misc device */
+#define miscdev_dbg(p_miscdev, fmt, va_args...) \
+ ({ \
+ struct miscdevice *_p_miscdev = p_miscdev; \
+ dev_dbg((_p_miscdev)->parent, "%s: " fmt, (_p_miscdev)->name, \
+ ##va_args); \
+ })
+
+#define miscdev_info(p_miscdev, fmt, va_args...) \
+ ({ \
+ struct miscdevice *_p_miscdev = p_miscdev; \
+ dev_info((_p_miscdev)->parent, "%s: " fmt, (_p_miscdev)->name, \
+ ##va_args); \
+ })
+
+#define miscdev_err(p_miscdev, fmt, va_args...) \
+ ({ \
+ struct miscdevice *_p_miscdev = p_miscdev; \
+ dev_err((_p_miscdev)->parent, "%s: " fmt, (_p_miscdev)->name, \
+ ##va_args); \
+ })
+/* macro to log operation of a device context */
+#define devctx_dbg(p_devctx, fmt, va_args...) \
+ miscdev_dbg(&((p_devctx)->miscdev), fmt, ##va_args)
+#define devctx_info(p_devctx, fmt, va_args...) \
+ miscdev_info(&((p_devctx)->miscdev), fmt, ##va_args)
+#define devctx_err(p_devctx, fmt, va_args...) \
+ miscdev_err((&(p_devctx)->miscdev), fmt, ##va_args)
+
+#define MSG_TAG(x) (((x) & 0xff000000) >> 24)
+#define MSG_COMMAND(x) (((x) & 0x00ff0000) >> 16)
+#define MSG_SIZE(x) (((x) & 0x0000ff00) >> 8)
+#define MSG_VER(x) ((x) & 0x000000ff)
+#define RES_STATUS(x) ((x) & 0x000000ff)
+#define MAX_DATA_SIZE_PER_USER (65 * 1024)
+#define S4_DEFAULT_MUAP_INDEX (2)
+#define S4_MUAP_DEFAULT_MAX_USERS (4)
+
+#define DEFAULT_MESSAGING_TAG_COMMAND (0x17u)
+#define DEFAULT_MESSAGING_TAG_RESPONSE (0xe1u)
+
+#define SECO_MU_IO_FLAGS_IS_INPUT (0x01u)
+#define SECO_MU_IO_FLAGS_USE_SEC_MEM (0x02u)
+#define SECO_MU_IO_FLAGS_USE_SHORT_ADDR (0x04u)
+
+struct sentnl_obuf_desc {
+ u8 *out_ptr;
+ u8 *out_usr_ptr;
+ u32 out_size;
+ struct list_head link;
+};
+
+/* Status of a char device */
+enum mu_device_status_t {
+ MU_FREE,
+ MU_OPENED
+};
+
+struct sentnl_shared_mem {
+ dma_addr_t dma_addr;
+ u32 size;
+ u32 pos;
+ u8 *ptr;
+};
+
+/* Private struct for each char device instance. */
+struct sentnl_mu_device_ctx {
+ struct device *dev;
+ struct sentnl_mu_priv *priv;
+ struct miscdevice miscdev;
+
+ enum mu_device_status_t status;
+ wait_queue_head_t wq;
+ struct semaphore fops_lock;
+
+ u32 pending_hdr;
+ struct list_head pending_out;
+
+ struct sentnl_shared_mem secure_mem;
+ struct sentnl_shared_mem non_secure_mem;
+
+ u32 temp_cmd[MAX_MESSAGE_SIZE];
+ u32 temp_resp[MAX_RECV_SIZE];
+ u32 temp_resp_size;
+ struct notifier_block sentnl_notify;
+};
+
+/* Header of the messages exchange with the SENTINEL */
+struct mu_hdr {
+ u8 ver;
+ u8 size;
+ u8 command;
+ u8 tag;
+} __packed;
+
+struct sentnl_api_msg {
+ u32 header; /* u8 Tag; u8 Command; u8 Size; u8 Ver; */
+ u32 data[SENTNL_MSG_DATA_NUM];
+};
+
+struct sentnl_mu_priv {
+ struct sentnl_mu_device_ctx *cmd_receiver_dev;
+ struct sentnl_mu_device_ctx *waiting_rsp_dev;
+ /*
+ * prevent parallel access to the MU registers
+ * e.g. a user trying to send a command while the other one is
+ * sending a response.
+ */
+ struct mutex mu_lock;
+ /*
+ * prevent a command to be sent on the MU while another one is still
+ * processing. (response to a command is allowed)
+ */
+ struct mutex mu_cmd_lock;
+ struct device *dev;
+ u32 sentnl_mu_id;
+ u8 cmd_tag;
+ u8 rsp_tag;
+
+ struct mbox_client sentnl_mb_cl;
+ struct mbox_chan *tx_chan, *rx_chan;
+ struct sentnl_api_msg tx_msg, rx_msg;
+ struct completion done;
+ spinlock_t lock;
+};
+
+int get_sentnl_mu_priv(struct sentnl_mu_priv **export);
+#endif
diff --git a/include/linux/firmware/imx/ipc.h b/include/linux/firmware/imx/ipc.h
index 0b4643571625..6e60322bcef9 100644
--- a/include/linux/firmware/imx/ipc.h
+++ b/include/linux/firmware/imx/ipc.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
- * Copyright 2018 NXP
+ * Copyright 2018,2020 NXP
*
* Header file for the IPC implementation.
*/
@@ -25,6 +25,8 @@ enum imx_sc_rpc_svc {
IMX_SC_RPC_SVC_PAD = 6,
IMX_SC_RPC_SVC_MISC = 7,
IMX_SC_RPC_SVC_IRQ = 8,
+ IMX_SC_RPC_SVC_SECO = 9,
+ IMX_SC_RPC_SVC_ABORT = 10,
};
struct imx_sc_rpc_msg {
diff --git a/include/linux/firmware/imx/sci.h b/include/linux/firmware/imx/sci.h
index 5cc63fe7e84d..d45dbbe8f67d 100644
--- a/include/linux/firmware/imx/sci.h
+++ b/include/linux/firmware/imx/sci.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright (C) 2016 Freescale Semiconductor, Inc.
- * Copyright 2017~2018 NXP
+ * Copyright 2017~2018,2020 NXP
*
* Header file containing the public System Controller Interface (SCI)
* definitions.
@@ -15,12 +15,38 @@
#include <linux/firmware/imx/svc/misc.h>
#include <linux/firmware/imx/svc/pm.h>
#include <linux/firmware/imx/svc/rm.h>
+#include <linux/firmware/imx/svc/seco.h>
+
+#define IMX_SC_IRQ_NUM_GROUP 9
+
+#define IMX_SC_IRQ_GROUP_TEMP 0 /* Temp interrupts */
+#define IMX_SC_IRQ_GROUP_WDOG 1 /* Watchdog interrupts */
+#define IMX_SC_IRQ_GROUP_RTC 2 /* RTC interrupts */
+#define IMX_SC_IRQ_GROUP_WAKE 3 /* Wakeup interrupts */
+#define IMX_SC_IRQ_GROUP_SYSCTR 4 /* System counter interrupts */
+#define IMX_SC_IRQ_GROUP_REBOOTED 5 /* Partition reboot complete */
+#define IMX_SC_IRQ_GROUP_REBOOT 6 /* Partition reboot starting */
+#define IMX_SC_IRQ_GROUP_OFFED 7 /* Partition off complete */
+#define IMX_SC_IRQ_GROUP_OFF 8 /* Partition off starting */
+
+#define IMX_SC_IRQ_RTC BIT(0) /* RTC interrupt */
+#define IMX_SC_IRQ_WDOG BIT(0) /* Watch Dog interrupt */
+#define IMX_SC_IRQ_SYSCTR BIT(0) /* System Counter interrupt */
+#define IMX_SC_IRQ_BUTTON BIT(0) /* Button interrupt */
+#define IMX_SC_IRQ_PAD BIT(1) /* Pad wakeup */
+#define IMX_SC_IRQ_USR1 BIT(2) /* User defined 1 */
+#define IMX_SC_IRQ_USR2 BIT(3) /* User defined 2 */
+#define IMX_SC_IRQ_BC_PAD BIT(4) /* Pad wakeup (broadcast to all partitions) */
+#define IMX_SC_IRQ_SW_WAKE BIT(5) /* Software requested wake */
+#define IMX_SC_IRQ_SECVIO BIT(6) /* Security violation */
+#define IMX_SC_IRQ_V2X_RESET BIT(7) /* V2X reset */
#if IS_ENABLED(CONFIG_IMX_SCU)
int imx_scu_enable_general_irq_channel(struct device *dev);
int imx_scu_irq_register_notifier(struct notifier_block *nb);
int imx_scu_irq_unregister_notifier(struct notifier_block *nb);
int imx_scu_irq_group_enable(u8 group, u32 mask, u8 enable);
+int imx_scu_irq_get_status(u8 group, u32 *irq_status);
int imx_scu_soc_init(struct device *dev);
#else
static inline int imx_scu_soc_init(struct device *dev)
diff --git a/include/linux/firmware/imx/seco_mu_ioctl.h b/include/linux/firmware/imx/seco_mu_ioctl.h
new file mode 100644
index 000000000000..bd8402b473a4
--- /dev/null
+++ b/include/linux/firmware/imx/seco_mu_ioctl.h
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: (GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause*/
+/*
+ * Copyright 2019-2020 NXP
+ */
+
+#ifndef SECO_MU_IOCTL_H
+#define SECO_MU_IOCTL_H
+
+/* IOCTL definitions. */
+struct seco_mu_ioctl_setup_iobuf {
+ u8 *user_buf;
+ u32 length;
+ u32 flags;
+ u64 seco_addr;
+};
+
+struct seco_mu_ioctl_shared_mem_cfg {
+ u32 base_offset;
+ u32 size;
+};
+
+struct seco_mu_ioctl_get_mu_info {
+ u8 seco_mu_idx;
+ u8 interrupt_idx;
+ u8 tz;
+ u8 did;
+};
+
+struct seco_mu_ioctl_signed_message {
+ u8 *message;
+ u32 msg_size;
+ u32 error_code;
+};
+
+#define SECO_MU_IO_FLAGS_IS_INPUT (0x01u)
+#define SECO_MU_IO_FLAGS_USE_SEC_MEM (0x02u)
+#define SECO_MU_IO_FLAGS_USE_SHORT_ADDR (0x04u)
+
+#define SECO_MU_IOCTL 0x0A /* like MISC_MAJOR. */
+#define SECO_MU_IOCTL_ENABLE_CMD_RCV _IO(SECO_MU_IOCTL, 0x01)
+#define SECO_MU_IOCTL_SHARED_BUF_CFG _IOW(SECO_MU_IOCTL, 0x02, \
+ struct seco_mu_ioctl_shared_mem_cfg)
+#define SECO_MU_IOCTL_SETUP_IOBUF _IOWR(SECO_MU_IOCTL, 0x03, \
+ struct seco_mu_ioctl_setup_iobuf)
+#define SECO_MU_IOCTL_GET_MU_INFO _IOR(SECO_MU_IOCTL, 0x04, \
+ struct seco_mu_ioctl_get_mu_info)
+#define SECO_MU_IOCTL_SIGNED_MESSAGE _IOWR(SECO_MU_IOCTL, 0x05, \
+ struct seco_mu_ioctl_signed_message)
+
+#endif
diff --git a/include/linux/firmware/imx/senclave_base_msg.h b/include/linux/firmware/imx/senclave_base_msg.h
new file mode 100644
index 000000000000..0592fbcf8fd3
--- /dev/null
+++ b/include/linux/firmware/imx/senclave_base_msg.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright 2021 NXP
+ */
+
+#ifndef SENTNL_BASE_MSG_H
+#define SENTNL_BASE_MSG_H
+
+#define MAX_RECV_SIZE 31
+#define MAX_RECV_SIZE_BYTES (MAX_RECV_SIZE * sizeof(u32))
+#define MAX_MESSAGE_SIZE 31
+#define MAX_MESSAGE_SIZE_BYTES (MAX_MESSAGE_SIZE * sizeof(u32))
+
+#define MESSAGING_VERSION_6 0x6
+
+#define SENTNL_OEM_CNTN_AUTH_REQ 0x87
+#define SENTNL_VERIFY_IMAGE_REQ 0x88
+#define SENTNL_RELEASE_CONTAINER_REQ 0x89
+#define SENTNL_READ_FUSE_REQ 0x97
+#define OTP_UNIQ_ID 0x01
+#define OTFAD_CONFIG 0x2
+
+#define SENTNL_VERSION 0x6
+#define SENTNL_SUCCESS_IND 0xD6
+#define SENTNL_FAILURE_IND 0x29
+
+#define SENTNL_MSG_DATA_NUM 10
+
+#define SENTNL_OEM_CNTN_AUTH_REQ_SIZE 3
+#define SENTNL_VERIFY_IMAGE_REQ_SIZE 2
+#define SENTNL_REL_CONTAINER_REQ_SIZE 1
+
+
+int read_common_fuse(uint16_t fuse_index, u32 *value);
+
+#endif
diff --git a/include/linux/firmware/imx/sentnl_mu_ioctl.h b/include/linux/firmware/imx/sentnl_mu_ioctl.h
new file mode 100644
index 000000000000..eda727aa81fc
--- /dev/null
+++ b/include/linux/firmware/imx/sentnl_mu_ioctl.h
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: (GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause*/
+/*
+ * Copyright 2019-2021 NXP
+ */
+
+#ifndef SENTNL_MU_IOCTL_H
+#define SENTNL_MU_IOCTL_H
+
+/* IOCTL definitions. */
+
+struct sentnl_mu_ioctl_setup_iobuf {
+ u8 *user_buf;
+ u32 length;
+ u32 flags;
+ u64 sentnl_addr;
+};
+
+struct sentnl_mu_ioctl_shared_mem_cfg {
+ u32 base_offset;
+ u32 size;
+};
+
+struct sentnl_mu_ioctl_get_mu_info {
+ u8 sentnl_mu_id;
+ u8 interrupt_idx;
+ u8 tz;
+ u8 did;
+};
+
+struct sentnl_mu_ioctl_signed_message {
+ u8 *message;
+ u32 msg_size;
+ u32 error_code;
+};
+
+#define SENTNL_MU_IO_FLAGS_IS_INTPUT (0x01u)
+#define SENTNL_MU_IO_FLAGS_USE_SEC_MEM (0x02u)
+#define SENTNL_MU_IO_FLAGS_USE_SHORT_ADDR (0x04u)
+
+#define SENTNL_MU_IOCTL 0x0A /* like MISC_MAJOR. */
+#define SENTNL_MU_IOCTL_ENABLE_CMD_RCV _IO(SENTNL_MU_IOCTL, 0x01)
+#define SENTNL_MU_IOCTL_SHARED_BUF_CFG _IOW(SENTNL_MU_IOCTL, 0x02, \
+ struct sentnl_mu_ioctl_shared_mem_cfg)
+#define SENTNL_MU_IOCTL_SETUP_IOBUF _IOWR(SENTNL_MU_IOCTL, 0x03, \
+ struct sentnl_mu_ioctl_setup_iobuf)
+#define SENTNL_MU_IOCTL_GET_MU_INFO _IOR(SENTNL_MU_IOCTL, 0x04, \
+ struct sentnl_mu_ioctl_get_mu_info)
+#define SENTNL_MU_IOCTL_SIGNED_MESSAGE _IOWR(SENTNL_MU_IOCTL, 0x05, \
+ struct sentnl_mu_ioctl_signed_message)
+
+#endif
diff --git a/include/linux/firmware/imx/svc/misc.h b/include/linux/firmware/imx/svc/misc.h
index 760db08a67fc..8fb0fe2dfc92 100644
--- a/include/linux/firmware/imx/svc/misc.h
+++ b/include/linux/firmware/imx/svc/misc.h
@@ -50,6 +50,9 @@ enum imx_misc_func {
int imx_sc_misc_set_control(struct imx_sc_ipc *ipc, u32 resource,
u8 ctrl, u32 val);
+int imx_sc_misc_set_dma_group(struct imx_sc_ipc *ipc, u32 resource,
+ u32 val);
+
int imx_sc_misc_get_control(struct imx_sc_ipc *ipc, u32 resource,
u8 ctrl, u32 *val);
@@ -67,6 +70,12 @@ static inline int imx_sc_misc_get_control(struct imx_sc_ipc *ipc,
{
return -ENOTSUPP;
}
+static inline int
+imx_sc_misc_set_dma_group(struct imx_sc_ipc *ipc, u32 resource,
+ u32 val)
+{
+ return -EIO;
+}
static inline int imx_sc_pm_cpu_start(struct imx_sc_ipc *ipc, u32 resource,
bool enable, u64 phys_addr)
diff --git a/include/linux/firmware/imx/svc/rm.h b/include/linux/firmware/imx/svc/rm.h
index 456b6a59d29b..64aabcba8768 100644
--- a/include/linux/firmware/imx/svc/rm.h
+++ b/include/linux/firmware/imx/svc/rm.h
@@ -59,11 +59,50 @@ enum imx_sc_rm_func {
#if IS_ENABLED(CONFIG_IMX_SCU)
bool imx_sc_rm_is_resource_owned(struct imx_sc_ipc *ipc, u16 resource);
+int imx_sc_rm_get_partition(struct imx_sc_ipc *ipc, u8 *pt);
+int imx_sc_rm_find_memreg(struct imx_sc_ipc *ipc, u8 *mr, u64 addr_start,
+ u64 addr_end);
+int imx_sc_rm_get_resource_owner(struct imx_sc_ipc *ipc, u16 resource, u8 *pt);
+int imx_sc_rm_set_memreg_permissions(struct imx_sc_ipc *ipc, u8 mr,
+ u8 pt, u8 perm);
+int imx_sc_rm_get_did(struct imx_sc_ipc *ipc, u8 *did);
#else
static inline bool
imx_sc_rm_is_resource_owned(struct imx_sc_ipc *ipc, u16 resource)
{
return true;
}
+
+static inline int imx_sc_rm_get_partition(struct imx_sc_ipc *ipc, u8 *pt)
+{
+ return -ENOENT;
+}
+
+static inline
+int imx_sc_rm_find_memreg(struct imx_sc_ipc *ipc, u8 *mr, u64 addr_start,
+ u64 addr_end)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline
+int imx_sc_rm_get_resource_owner(struct imx_sc_ipc *ipc, u16 resource, u8 *pt)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline
+int imx_sc_rm_set_memreg_permissions(struct imx_sc_ipc *ipc, u8 mr,
+ u8 pt, u8 perm)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline
+int imx_sc_rm_get_did(struct imx_sc_ipc *ipc, u8 *did)
+{
+ return -EOPNOTSUPP;
+}
#endif
+
#endif
diff --git a/include/linux/firmware/imx/svc/seco.h b/include/linux/firmware/imx/svc/seco.h
new file mode 100644
index 000000000000..d0dd803a1a52
--- /dev/null
+++ b/include/linux/firmware/imx/svc/seco.h
@@ -0,0 +1,77 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright 2020 NXP
+ *
+ * Header file containing the public API for the System Controller (SC)
+ * Security Controller (SECO) function.
+ *
+ * SECO_SVC (SVC) Security Controller Service
+ *
+ * Module for the Security Controller (SECO) service.
+ */
+
+#ifndef _SC_SECO_API_H
+#define _SC_SECO_API_H
+
+#include <linux/errno.h>
+#include <linux/firmware/imx/sci.h>
+
+/*
+ * This type is used to indicate RPC RM function calls.
+ */
+enum imx_sc_seco_func {
+ IMX_SC_SECO_FUNC_UNKNOWN = 0,
+ IMX_SC_SECO_FUNC_BUILD_INFO = 16,
+ IMX_SC_SECO_FUNC_SAB_MSG = 23,
+ IMX_SC_SECO_FUNC_SECVIO_ENABLE = 25,
+ IMX_SC_SECO_FUNC_SECVIO_CONFIG = 26,
+ IMX_SC_SECO_FUNC_SECVIO_DGO_CONFIG = 27,
+};
+
+#if IS_ENABLED(CONFIG_IMX_SCU)
+int imx_sc_seco_build_info(struct imx_sc_ipc *ipc, uint32_t *version,
+ uint32_t *commit);
+int imx_sc_seco_sab_msg(struct imx_sc_ipc *ipc, u64 smsg_addr);
+int imx_sc_seco_secvio_enable(struct imx_sc_ipc *ipc);
+int imx_sc_seco_secvio_config(struct imx_sc_ipc *ipc, u8 id, u8 access,
+ u32 *data0, u32 *data1, u32 *data2, u32 *data3,
+ u32 *data4, u8 size);
+int imx_sc_seco_secvio_dgo_config(struct imx_sc_ipc *ipc, u8 id, u8 access,
+ u32 *data);
+#else /* IS_ENABLED(CONFIG_IMX_SCU) */
+static inline
+int imx_sc_seco_build_info(struct imx_sc_ipc *ipc, uint32_t *version,
+ uint32_t *commit)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline
+int imx_sc_seco_sab_msg(struct imx_sc_ipc *ipc, u64 smsg_addr)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline
+int imx_sc_seco_secvio_enable(struct imx_sc_ipc *ipc)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline
+int imx_sc_seco_secvio_config(struct imx_sc_ipc *ipc, u8 id, u8 access,
+ u32 *data0, u32 *data1, u32 *data2, u32 *data3,
+ u32 *data4, u8 size)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline
+int imx_sc_seco_secvio_dgo_config(struct imx_sc_ipc *ipc, u8 id, u8 access,
+ u32 *data)
+{
+ return -EOPNOTSUPP;
+}
+#endif /* IS_ENABLED(CONFIG_IMX_SCU) */
+
+#endif /* _SC_SECO_API_H */