summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFranck LENORMAND <franck.lenormand@nxp.com>2019-09-26 11:13:08 +0200
committerSilvano Di Ninno <silvano.dininno@nxp.com>2019-12-05 09:44:26 +0100
commit4f977a4fb86561f0d0bd870a97adb16163429c5c (patch)
treed476260731cf7a89a495b8778a67d3c48dceae05
parent4b87e5c8824f8108d3ca8c975977be7424ff9215 (diff)
SSI-87: soc: imx: secvio: Add support for SNVS secvio and tamper via SCFW
The driver register an IRQ handle to SCU for security violation interrupt. When an interruption is fired, the driver inform the user. Signed-off-by: Franck LENORMAND <franck.lenormand@nxp.com>
-rw-r--r--arch/arm64/boot/dts/freescale/fsl-imx8dx.dtsi4
-rw-r--r--arch/arm64/configs/defconfig1
-rw-r--r--drivers/soc/imx/Kconfig10
-rw-r--r--drivers/soc/imx/Makefile1
-rw-r--r--drivers/soc/imx/imx-secvio-sc.c1191
-rw-r--r--include/soc/imx8/imx-secvio-sc.h83
6 files changed, 1290 insertions, 0 deletions
diff --git a/arch/arm64/boot/dts/freescale/fsl-imx8dx.dtsi b/arch/arm64/boot/dts/freescale/fsl-imx8dx.dtsi
index d7f5229cdb31..af1aacf118ee 100644
--- a/arch/arm64/boot/dts/freescale/fsl-imx8dx.dtsi
+++ b/arch/arm64/boot/dts/freescale/fsl-imx8dx.dtsi
@@ -220,6 +220,10 @@
compatible = "fsl,imx-sc-rtc";
};
+ secvio: secvio {
+ compatible = "fsl,imx-sc-secvio";
+ };
+
timer {
compatible = "arm,armv8-timer";
interrupts = <GIC_PPI 13 (GIC_CPU_MASK_SIMPLE(6) | IRQ_TYPE_LEVEL_LOW)>, /* Physical Secure */
diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig
index a12ae35a7740..53f18b7a755c 100644
--- a/arch/arm64/configs/defconfig
+++ b/arch/arm64/configs/defconfig
@@ -713,6 +713,7 @@ CONFIG_ARM_SMMU_V3=y
CONFIG_RPMSG_QCOM_SMD=y
CONFIG_RASPBERRYPI_POWER=y
CONFIG_ARCH_MXC_ARM64=y
+CONFIG_SECVIO_SC=y
CONFIG_QCOM_SMEM=y
CONFIG_QCOM_SMD_RPM=y
CONFIG_QCOM_SMP2P=y
diff --git a/drivers/soc/imx/Kconfig b/drivers/soc/imx/Kconfig
index 3a1fa180f28f..b93a845fd170 100644
--- a/drivers/soc/imx/Kconfig
+++ b/drivers/soc/imx/Kconfig
@@ -52,4 +52,14 @@ config HAVE_IMX_RPMSG
select RPMSG_VIRTIO
select RPMSG
bool
+
+config SECVIO_SC
+ bool "NXP SC secvio support"
+ depends on ARCH_FSL_IMX8QXP || ARCH_FSL_IMX8QM
+ help
+ If you say yes here you get support for the NXP SNVS security violation module. It includes the possibility to read information
+ related to security violations and tampers. It also gives the
+ possibility to register user callbacks when a security violation
+ occurs.
+
endmenu
diff --git a/drivers/soc/imx/Makefile b/drivers/soc/imx/Makefile
index 2606ddfd945b..8a3116fd0d12 100644
--- a/drivers/soc/imx/Makefile
+++ b/drivers/soc/imx/Makefile
@@ -6,3 +6,4 @@ obj-$(CONFIG_ARCH_FSL_IMX8QM) += pm-domains.o
obj-$(CONFIG_ARCH_FSL_IMX8MQ) += busfreq-imx8mq.o gpc-psci.o
obj-$(CONFIG_ARCH_FSL_IMX8MM) += gpc-psci.o
obj-$(CONFIG_HAVE_IMX8_SOC) += soc-imx8.o
+obj-$(CONFIG_SECVIO_SC) += imx-secvio-sc.o
diff --git a/drivers/soc/imx/imx-secvio-sc.c b/drivers/soc/imx/imx-secvio-sc.c
new file mode 100644
index 000000000000..216d24c8c805
--- /dev/null
+++ b/drivers/soc/imx/imx-secvio-sc.c
@@ -0,0 +1,1191 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2019 NXP
+ *
+ */
+
+/*
+ * This module interact with the SCU which relay request to/from the SNVS block
+ * to detect if security violation occurred.
+ *
+ * The module exports an API to add processing when a SV is detected:
+ * - register_imx_secvio_sc_notifier
+ * - unregister_imx_secvio_sc_notifier
+ *
+ * The module exposes 2 files in debugfs:
+ * - secvio/info:
+ * * Read: It returns the value of the fuses and SNVS registers which are
+ * readable and related to secvio and tampers
+ * * Write: A write of the format "<hex id> [<hex value 0> <hex value 1>
+ * <hex value 2> <hex value 3> <hex value 4>](<nb values>)"
+ * will write the SNVS register having the provided id with the
+ * values provided (cf SECO ducumentation)
+ * - secvio/enable: State of the IRQ
+ */
+
+/* Includes */
+#include <linux/kernel.h>
+#include <linux/notifier.h>
+#include <linux/uaccess.h>
+#include <linux/debugfs.h>
+#include <linux/module.h>
+#include <linux/semaphore.h>
+#include <linux/fs.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <soc/imx8/sc/ipc.h>
+#include <soc/imx8/sc/sci.h>
+#include <soc/imx8/sc/svc/irq/api.h>
+#include <soc/imx8/sc/svc/seco/api.h>
+
+#include <soc/imx8/imx-secvio-sc.h>
+
+/* Definitions */
+
+/* Access for sc_seco_secvio_config API */
+#define SECVIO_CONFIG_READ 0
+#define SECVIO_CONFIG_WRITE 1
+
+/* Register IDs for sc_seco_secvio_config API */
+#define HPSVS_ID 0x18
+#define LPS_ID 0x4c
+#define LPTDS_ID 0xa4
+#define HPVIDR_ID 0xf8
+
+/* Forward declarations */
+static int imx_secvio_sc_process_state(struct device *dev);
+static int enable_irq(struct device *dev);
+static int disable_irq(struct device *dev);
+static void imx_secvio_sc_debugfs_remove(struct device *dev);
+static int imx_secvio_sc_teardown(struct device *dev);
+static int call_secvio_config(struct device *dev, u8 id, u8 access, u32 *data0,
+ u32 *data1, u32 *data2, u32 *data3, u32 *data4,
+ u8 size);
+
+/* Notifier list for new CB */
+static BLOCKING_NOTIFIER_HEAD(imx_secvio_sc_notifier_chain);
+
+/* Internal Structure */
+struct imx_secvio_sc_data {
+ struct device *dev;
+ sc_ipc_t ipcHandle;
+ struct notifier_block irq_nb;
+ struct notifier_block report_nb;
+
+ u8 enabled;
+
+#ifdef CONFIG_DEBUG_FS
+ u8 *io_buffer;
+ size_t io_buffer_size;
+
+ struct dentry *dfs;
+ struct semaphore sem;
+#endif /* CONFIG_DEBUG_FS */
+
+ u32 version;
+};
+
+/* Mapping of error code returned by SCU */
+struct mapping_sci_err {
+ const char *txt;
+ u8 sciErr;
+ u32 osErr;
+};
+
+const struct mapping_sci_err sci_err_list[] = {
+ {"None", SC_ERR_NONE, 0},
+ {"Wrong version", SC_ERR_VERSION, -EDOM},
+ {"Wrong config", SC_ERR_CONFIG, -ERANGE},
+ {"Bad parameter", SC_ERR_PARM, -EINVAL},
+ {"No access", SC_ERR_NOACCESS, -EACCES},
+ {"Locked", SC_ERR_LOCKED, -EROFS},
+ {"Unavailable", SC_ERR_UNAVAILABLE, -ENAVAIL},
+ {"Not found", SC_ERR_NOTFOUND, -ENOENT},
+ {"No power", SC_ERR_NOPOWER, -ECOMM},
+ {"Issue IPC", SC_ERR_IPC, -EPROTO},
+ {"Busy", SC_ERR_BUSY, -EBUSY},
+ {"Fail", SC_ERR_FAIL, -EFAULT},
+};
+
+/* Code */
+static int sci_err_to_errno(sc_err_t sciErr, const char **txt)
+{
+ int ret = -ENOMSG;
+ int idx;
+ const struct mapping_sci_err *mapping;
+ const char *ret_txt = "UNKNOWN";
+
+ for (idx = 0; idx < ARRAY_SIZE(sci_err_list); idx++) {
+ mapping = &sci_err_list[idx];
+
+ if (sciErr == mapping->sciErr) {
+ ret = mapping->osErr;
+ ret_txt = mapping->txt;
+ break;
+ }
+ }
+
+ if (txt)
+ *txt = ret_txt;
+
+ return ret;
+}
+
+int register_imx_secvio_sc_notifier(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_register(&imx_secvio_sc_notifier_chain,
+ nb);
+}
+EXPORT_SYMBOL(register_imx_secvio_sc_notifier);
+
+int unregister_imx_secvio_sc_notifier(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_unregister(&imx_secvio_sc_notifier_chain,
+ nb);
+}
+EXPORT_SYMBOL(unregister_imx_secvio_sc_notifier);
+
+static int imx_secvio_sc_notifier_call_chain(struct notifier_info *info)
+{
+ return blocking_notifier_call_chain(&imx_secvio_sc_notifier_chain,
+ 0, (void *)info);
+}
+
+static int imx_secvio_sc_notify(struct notifier_block *nb,
+ unsigned long event, void *group)
+{
+ int ret = 0;
+ struct imx_secvio_sc_data *data =
+ container_of(nb, struct imx_secvio_sc_data,
+ irq_nb);
+ struct device *dev = data->dev;
+
+ /* Filter event for us */
+ if (!((event & SC_IRQ_SECVIO) &&
+ (*(sc_irq_group_t *)group == SC_IRQ_GROUP_WAKE)))
+ goto exit;
+
+ dev_warn(dev, "secvio security violation detected\n");
+
+ ret = imx_secvio_sc_process_state(dev);
+
+exit:
+ return ret;
+}
+
+static int imx_secvio_sc_process_state(struct device *dev)
+{
+ int ret = 0;
+ struct notifier_info info = {0};
+
+ /* Read secvio status */
+ ret = call_secvio_config(dev, HPSVS_ID, SECVIO_CONFIG_READ,
+ &info.sv_status, NULL, NULL, NULL, NULL, 1);
+ if (ret)
+ dev_err(dev, "Cannot read secvio status: %d\n", ret);
+ info.sv_status &= HPSVS__ALL_SV__MASK;
+
+ /* Read tampers status */
+ ret = call_secvio_config(dev, LPS_ID, SECVIO_CONFIG_READ,
+ &info.tp_status_1, NULL, NULL, NULL, NULL, 1);
+ if (ret)
+ dev_err(dev, "Cannot read tamper 1 status: %d\n", ret);
+ info.tp_status_1 &= LPS__ALL_TP__MASK;
+
+ ret = call_secvio_config(dev, LPTDS_ID, SECVIO_CONFIG_READ,
+ &info.tp_status_2, NULL, NULL, NULL, NULL, 1);
+ if (ret)
+ dev_err(dev, "Cannot read tamper 2 status: %d\n", ret);
+ info.tp_status_2 &= LPTDS__ALL_TP__MASK;
+
+ dev_dbg(dev, "Status: %.8x, %.8x, %.8x\n", info.sv_status,
+ info.tp_status_1, info.tp_status_2);
+
+ /* Clear status */
+ ret = call_secvio_config(dev, HPSVS_ID, SECVIO_CONFIG_WRITE,
+ &info.sv_status, NULL, NULL, NULL, NULL, 1);
+ if (ret)
+ dev_err(dev, "Cannot clear secvio status: %d\n", ret);
+
+ ret = call_secvio_config(dev, LPS_ID, SECVIO_CONFIG_WRITE,
+ &info.tp_status_1, NULL, NULL, NULL, NULL, 1);
+ if (ret)
+ dev_err(dev, "Cannot clear temper 1 status: %d\n", ret);
+
+ ret = call_secvio_config(dev, LPTDS_ID, SECVIO_CONFIG_WRITE,
+ &info.tp_status_2, NULL, NULL, NULL, NULL, 1);
+ if (ret)
+ dev_err(dev, "Cannot clear tamper 2 status: %d\n", ret);
+
+ /* Re-enable interrupt */
+ ret = enable_irq(dev);
+ if (ret)
+ dev_err(dev, "Failed to enable IRQ\n");
+
+ /* Call chain of CB registered to this module */
+ if (imx_secvio_sc_notifier_call_chain(&info))
+ dev_warn(dev, "Issues when calling the notifier chain\n");
+
+ return ret;
+}
+
+static int report_to_user_notify(struct notifier_block *nb,
+ unsigned long status, void *notif_info)
+{
+ struct notifier_info *info = notif_info;
+ struct imx_secvio_sc_data *data =
+ container_of(nb, struct imx_secvio_sc_data,
+ report_nb);
+ struct device *dev = data->dev;
+
+ /* Inform what is the cause of the interrupt */
+ if (info->sv_status & HPSVS__LP_SEC_VIO__MASK) {
+ dev_info(dev, "SNVS secvio: LPSV\n");
+ } else if (info->sv_status & HPSVS__SW_LPSV__MASK) {
+ dev_info(dev, "SNVS secvio: SW LPSV\n");
+ } else if (info->sv_status & HPSVS__SW_FSV__MASK) {
+ dev_info(dev, "SNVS secvio: SW FSV\n");
+ } else if (info->sv_status & HPSVS__SW_SV__MASK) {
+ dev_info(dev, "SNVS secvio: SW SV\n");
+ } else if (info->sv_status & HPSVS__SV5__MASK) {
+ dev_info(dev, "SNVS secvio: SV 5\n");
+ } else if (info->sv_status & HPSVS__SV4__MASK) {
+ dev_info(dev, "SNVS secvio: SV 4\n");
+ } else if (info->sv_status & HPSVS__SV3__MASK) {
+ dev_info(dev, "SNVS secvio: SV 3\n");
+ } else if (info->sv_status & HPSVS__SV2__MASK) {
+ dev_info(dev, "SNVS secvio: SV 2\n");
+ } else if (info->sv_status & HPSVS__SV1__MASK) {
+ dev_info(dev, "SNVS secvio: SV 1\n");
+ } else if (info->sv_status & HPSVS__SV0__MASK) {
+ dev_info(dev, "SNVS secvio: SV 0\n");
+ }
+
+ /* Process in case of tamper */
+ if (info->tp_status_1 & LPS__ESVD__MASK)
+ dev_info(dev, "SNVS tamper: External SV\n");
+ else if (info->tp_status_1 & LPS__ET2D__MASK)
+ dev_info(dev, "SNVS tamper: Tamper 2\n");
+ else if (info->tp_status_1 & LPS__ET1D__MASK)
+ dev_info(dev, "SNVS tamper: Tamper 1\n");
+ else if (info->tp_status_1 & LPS__WMT2D__MASK)
+ dev_info(dev, "SNVS tamper: Wire Mesh 2\n");
+ else if (info->tp_status_1 & LPS__WMT1D__MASK)
+ dev_info(dev, "SNVS tamper: Wire Mesh 1\n");
+ else if (info->tp_status_1 & LPS__VTD__MASK)
+ dev_info(dev, "SNVS tamper: Voltage\n");
+ else if (info->tp_status_1 & LPS__TTD__MASK)
+ dev_info(dev, "SNVS tamper: Temperature\n");
+ else if (info->tp_status_1 & LPS__CTD__MASK)
+ dev_info(dev, "SNVS tamper: Clock\n");
+ else if (info->tp_status_1 & LPS__PGD__MASK)
+ dev_info(dev, "SNVS tamper: Power Glitch\n");
+ else if (info->tp_status_1 & LPS__MCR__MASK)
+ dev_info(dev, "SNVS tamper: Monotonic Counter rollover\n");
+ else if (info->tp_status_1 & LPS__SRTCR__MASK)
+ dev_info(dev, "SNVS tamper: Secure RTC rollover\n");
+ else if (info->tp_status_1 & LPS__LPTA__MASK)
+ dev_info(dev, "SNVS tamper: Time alarm\n");
+ else if (info->tp_status_2 & LPTDS__ET10D__MASK)
+ dev_info(dev, "SNVS tamper: Tamper 10\n");
+ else if (info->tp_status_2 & LPTDS__ET9D__MASK)
+ dev_info(dev, "SNVS tamper: Tamper 9\n");
+ else if (info->tp_status_2 & LPTDS__ET8D__MASK)
+ dev_info(dev, "SNVS tamper: Tamper 8\n");
+ else if (info->tp_status_2 & LPTDS__ET7D__MASK)
+ dev_info(dev, "SNVS tamper: Tamper 7\n");
+ else if (info->tp_status_2 & LPTDS__ET6D__MASK)
+ dev_info(dev, "SNVS tamper: Tamper 6\n");
+ else if (info->tp_status_2 & LPTDS__ET5D__MASK)
+ dev_info(dev, "SNVS tamper: Tamper 5\n");
+ else if (info->tp_status_2 & LPTDS__ET4D__MASK)
+ dev_info(dev, "SNVS tamper: Tamper 4\n");
+ else if (info->tp_status_2 & LPTDS__ET3D__MASK)
+ dev_info(dev, "SNVS tamper: Tamper 3\n");
+
+ return 0;
+}
+
+static int call_secvio_config(struct device *dev, u8 id, u8 access, u32 *data0,
+ u32 *data1, u32 *data2, u32 *data3, u32 *data4,
+ u8 size)
+{
+ int ret = 0;
+ sc_err_t sciErr;
+ struct imx_secvio_sc_data *data = dev_get_drvdata(dev);
+ u32 regs[5] = {0xDEADBEEF};
+ const char *details;
+
+ if (size > 5)
+ ret = -EINVAL;
+
+ switch (size) {
+ case 5:
+ if (!data4) {
+ ret = -EINVAL;
+ goto exit;
+ }
+ /* fallthrough */
+ case 4:
+ if (!data3) {
+ ret = -EINVAL;
+ goto exit;
+ }
+ /* fallthrough */
+ case 3:
+ if (!data2) {
+ ret = -EINVAL;
+ goto exit;
+ }
+ /* fallthrough */
+ case 2:
+ if (!data1) {
+ ret = -EINVAL;
+ goto exit;
+ }
+ /* fallthrough */
+ case 1:
+ if (!data0) {
+ ret = -EINVAL;
+ goto exit;
+ }
+ }
+
+ /* Pass parameters */
+ switch (size) {
+ case 5:
+ regs[4] = *data4;
+ /* fallthrough */
+ case 4:
+ regs[3] = *data3;
+ /* fallthrough */
+ case 3:
+ regs[2] = *data2;
+ /* fallthrough */
+ case 2:
+ regs[1] = *data1;
+ /* fallthrough */
+ case 1:
+ regs[0] = *data0;
+ }
+
+ dev_dbg(dev, "Send %s [%.8x %.8x %.8x %.8x %.8x] -> %.4x\n",
+ ((access) ? "W" : "R"), regs[0], regs[1], regs[2], regs[3],
+ regs[4], id);
+ sciErr = sc_seco_secvio_config(data->ipcHandle, id, access, &regs[0],
+ &regs[1], &regs[2], &regs[3],
+ &regs[4], size);
+ dev_dbg(dev, "Received [%.8x %.8x %.8x %.8x %.8x]\n",
+ regs[0], regs[1], regs[2], regs[3], regs[4]);
+
+ if (sciErr != SC_ERR_NONE) {
+ ret = sci_err_to_errno(sciErr, &details);
+ dev_err(dev, "Fail %s secvio config %s(%d)"
+ "[id: %.2x data:{%.8x %.8x %.8x %.8x %.8x}(%d)]",
+ ((access) ? "write" : "read"), details, sciErr,
+ id, regs[0], regs[1], regs[2], regs[3], regs[4], size);
+ }
+
+ if (!access) {
+ /* Read parameters */
+ switch (size) {
+ case 5:
+ *data4 = regs[4];
+ /* fallthrough */
+ case 4:
+ *data3 = regs[3];
+ /* fallthrough */
+ case 3:
+ *data2 = regs[2];
+ /* fallthrough */
+ case 2:
+ *data1 = regs[1];
+ /* fallthrough */
+ case 1:
+ *data0 = regs[0];
+ /* fallthrough */
+ }
+ }
+
+exit:
+ return ret;
+}
+
+#ifdef CONFIG_DEBUG_FS
+
+/* Buffer for copy to user */
+#define IMX_SECVIO_BUFFER_SIZE 4000
+
+/* Structure of values to read */
+struct snvs_sc_regs {
+ const char *name;
+ u32 id;
+ int size;
+};
+
+static struct snvs_sc_regs s_snvs_sc_reglist[] = {
+ /* Locks */
+ {"HPLR", 0x0, 1},
+ {"LPLR", 0x34, 1},
+ /* Security violation */
+ {"HPSICR", 0xc, 1},
+ {"HPSVCR", 0x10, 1},
+ {"HPSVS", 0x18, 1},
+ {"LPSVC", 0x40, 1},
+ /* Temper detectors */
+ {"LPTDC", 0x48, 2},
+ {"LPSR", 0x4c, 1},
+ {"LPTDS", 0xa4, 1},
+ /* */
+ {"LPTGFC", 0x44, 3},
+ {"LPATCTL", 0xe0, 1},
+ {"LPATCLK", 0xe4, 1},
+ {"LPATRC", 0xe8, 2},
+ /* Misc */
+ {"LPMKC", 0x3c, 1},
+ {"LPSMC", 0x5c, 2},
+ {"LPPGD", 0x64, 1},
+ {"HPVID", 0xf8, 2},
+};
+
+struct snvs_sc_dgo_regs {
+ const char *name;
+ u32 offset;
+};
+
+static struct snvs_sc_dgo_regs s_snvs_sc_dgo_reglist[] = {
+ {"Offset ctrl", 0x0},
+ {"Tamper PU/PD ctrl", 0x10},
+ {"Analog test ctrl", 0x20},
+ {"Temper sensor trim ctrl", 0x30},
+ {"Misc ctrl", 0x40},
+ {"Core voltage monitor ctrl", 0x50},
+};
+
+struct ocotp_fuse {
+ const char *name;
+ int word;
+};
+
+static struct ocotp_fuse ocotp_fuse_list[] = {
+ {"30", 30},
+ {"31", 31},
+ {"260", 260},
+ {"261", 261},
+ {"262", 262},
+ {"263", 263},
+ {"768", 768},
+};
+
+static int imx_secvio_sc_file_open(struct inode *node, struct file *filp)
+{
+ /* debugfs_create_file set inode->i_private to our private data */
+ filp->private_data = node->i_private;
+
+ return 0;
+}
+
+static int buffer_write(struct device *dev, void *buffer, size_t total_size,
+ size_t *offset, const char *fmt, ...)
+{
+ u8 *p = buffer;
+ va_list args;
+ int i, ret = 0;
+ size_t space_left = total_size - *offset;
+
+ va_start(args, fmt);
+ i = vsnprintf(p + *offset, space_left, fmt, args);
+ va_end(args);
+
+ if (i < 0) {
+ dev_err(dev, "Failed to write to buffer\n");
+ ret = i;
+ } else if (i >= space_left) {
+ dev_err(dev, "No space, need %d, left: %lu\n", i, space_left);
+ ret = -ENOSPC;
+ }
+
+ *offset += i;
+
+ return ret;
+}
+
+static ssize_t imx_secvio_sc_info_read(struct file *filp,
+ char __user *user_buffer,
+ size_t size, loff_t *offset)
+{
+ int idx;
+ int ret = 0;
+ struct device *dev = filp->private_data;
+ struct imx_secvio_sc_data *data = dev_get_drvdata(dev);
+ size_t buffer_pos = 0;
+
+ if (!access_ok(VERIFY_WRITE, user_buffer, size)) {
+ dev_err(dev, "Access check failed for %p(%ld)\n", user_buffer,
+ size);
+ return -EFAULT;
+ }
+
+ if (down_interruptible(&data->sem))
+ return -ERESTARTSYS;
+
+ /* Stop reading if an offset is passed*/
+ if (*offset) {
+ ret = 0;
+ goto exit;
+ }
+
+ ret = buffer_write(dev, data->io_buffer, data->io_buffer_size,
+ &buffer_pos, "Fuses:\n");
+ if (ret) {
+ dev_err(dev, "Failed to report fuse header\n");
+ goto exit;
+ }
+
+ /* Read fuses */
+ for (idx = 0; idx < ARRAY_SIZE(ocotp_fuse_list); idx++) {
+ u32 value = 0xDEADBEEF;
+ sc_err_t sciErr;
+ struct ocotp_fuse *fuse = &ocotp_fuse_list[idx];
+
+ sciErr = sc_misc_otp_fuse_read(data->ipcHandle, fuse->word,
+ &value);
+ if (sciErr != SC_ERR_NONE) {
+ ret = sci_err_to_errno(sciErr, NULL);
+ dev_err(dev, "Failed to access fuse %s: %d\n",
+ fuse->name, sciErr);
+ goto exit;
+ }
+
+ /* Write name of fuse */
+ ret = buffer_write(dev, data->io_buffer, data->io_buffer_size,
+ &buffer_pos, "(%.4x)%-12s: 0x%.8x\n",
+ fuse->word, fuse->name, value);
+ if (ret) {
+ dev_err(dev, "Failed to report fuse %s\n", fuse->name);
+ goto exit;
+ }
+ }
+
+ ret = buffer_write(dev, data->io_buffer, data->io_buffer_size,
+ &buffer_pos, "SNVS:\n");
+ if (ret) {
+ dev_err(dev, "Failed to report reg header\n");
+ goto exit;
+ }
+
+ /* Loop over all the registers to read */
+ for (idx = 0; idx < ARRAY_SIZE(s_snvs_sc_reglist); idx++) {
+ int idx_value;
+ u32 regs[5] = {0xDEADBEEF};
+ struct snvs_sc_regs *reg = &s_snvs_sc_reglist[idx];
+
+ ret = call_secvio_config(dev, reg->id, SECVIO_CONFIG_READ,
+ &regs[0], &regs[1], &regs[2], &regs[3],
+ &regs[4], reg->size);
+ if (ret) {
+ dev_err(dev, "Failed to read reg %s\n", reg->name);
+ goto exit;
+ }
+
+ /* Write name of register */
+
+ ret = buffer_write(dev, data->io_buffer, data->io_buffer_size,
+ &buffer_pos, "(0x%.2x) %-12s:", reg->id,
+ reg->name);
+ if (ret) {
+ dev_err(dev, "Failed to report name for reg %s\n",
+ reg->name);
+ goto exit;
+ }
+
+ /* loop over number of values to write */
+ for (idx_value = 0; idx_value < reg->size; idx_value++) {
+ ret = buffer_write(dev, data->io_buffer,
+ data->io_buffer_size, &buffer_pos,
+ " 0x%.8x", regs[idx_value]);
+ if (ret) {
+ dev_err(dev,
+ "Failed to report idx %d for reg %s\n",
+ idx_value, reg->name);
+ goto exit;
+ }
+ }
+
+ /* Add return line */
+ ret = buffer_write(dev, data->io_buffer, data->io_buffer_size,
+ &buffer_pos, "\n");
+ if (ret) {
+ dev_err(dev, "Failed to report newline for reg %s\n",
+ reg->name);
+ goto exit;
+ }
+ }
+
+ ret = buffer_write(dev, data->io_buffer, data->io_buffer_size,
+ &buffer_pos, "SNVS DGO:\n");
+ if (ret) {
+ dev_err(dev, "Failed to report digital reg header\n");
+ goto exit;
+ }
+
+ /* Loop over all the registers to read */
+ for (idx = 0; idx < ARRAY_SIZE(s_snvs_sc_dgo_reglist); idx++) {
+ u32 value;
+ struct snvs_sc_dgo_regs *reg = &s_snvs_sc_dgo_reglist[idx];
+
+ ret = sc_seco_secvio_dgo_config(data->ipcHandle, reg->offset,
+ SECVIO_CONFIG_READ, &value);
+ if (ret) {
+ dev_err(dev, "Failed to access reg %s: %d\n", reg->name,
+ ret);
+ goto exit;
+ }
+
+ /* Write name of register and value */
+ ret = buffer_write(dev, data->io_buffer, data->io_buffer_size,
+ &buffer_pos, "(0x%.2x) %-12s: 0x%.8x\n",
+ reg->offset, reg->name, value);
+ if (ret) {
+ dev_err(dev, "Failed to report reg %s\n", reg->name);
+ goto exit;
+ }
+ }
+
+ if (size < buffer_pos)
+ dev_info(dev, "Data is %ld bytes but buffer is only %ld\n",
+ buffer_pos, size);
+ else
+ size = buffer_pos;
+
+ ret = simple_read_from_buffer(user_buffer, size, offset,
+ data->io_buffer, buffer_pos);
+
+exit:
+ up(&data->sem);
+ return ret;
+}
+
+static ssize_t imx_secvio_sc_info_write(struct file *filp,
+ const char __user *user_buffer,
+ size_t size, loff_t *offset)
+{
+ int idx;
+ int ret = 0;
+ struct device *dev = filp->private_data;
+ struct imx_secvio_sc_data *data = dev_get_drvdata(dev);
+
+ u32 id;
+ int nb_vals;
+ u32 vals[5];
+
+ if (!access_ok(VERIFY_READ, user_buffer, size)) {
+ dev_err(dev, "Access check failed for %p(%ld)\n", user_buffer,
+ size);
+ return -EFAULT;
+ }
+
+ if (down_interruptible(&data->sem))
+ return -ERESTARTSYS;
+
+ /* Stop reading if an offset is passed*/
+ if (*offset) {
+ ret = size;
+ goto exit;
+ }
+
+ /* Reset buffer */
+ data->io_buffer[0] = '\0';
+
+ ret = simple_write_to_buffer(data->io_buffer, data->io_buffer_size,
+ offset, user_buffer, size);
+ if (ret < 0) {
+ dev_err(dev, "Failed to copy user buffer\n");
+ ret = -EIO;
+ goto exit;
+ }
+
+ ret = sscanf(data->io_buffer, "%x [%x %x %x %x %x](%d)", &id, &vals[0],
+ &vals[1], &vals[2], &vals[3], &vals[4], &nb_vals);
+ if (ret < 0) {
+ dev_err(dev, "Failed to process input\n");
+ ret = -EIO;
+ goto exit;
+ } else if (ret != 7) {
+ dev_err(dev, "Failed to process all parameters\n");
+ ret = -EIO;
+ goto exit;
+ }
+
+ dev_dbg(dev, "writing id %.2x [0x%.8x 0x%.8x 0x%.8x 0x%.8x 0x%.8x](%d)",
+ id, vals[0], vals[1], vals[2], vals[3], vals[4], nb_vals);
+
+ if (nb_vals > 5) {
+ dev_warn(dev, "Input nb values %d too big, reduced\n", nb_vals);
+ nb_vals = 5;
+ }
+
+ /* Check valid id */
+ ret = -EINVAL;
+ for (idx = 0; idx < ARRAY_SIZE(s_snvs_sc_reglist); idx++) {
+ struct snvs_sc_regs *reg = &s_snvs_sc_reglist[idx];
+
+ if (id == reg->id) {
+ ret = 0;
+ break;
+ }
+ }
+
+ if (ret) {
+ dev_err(dev, "Input id 0x%x is not valid\n", id);
+ goto exit;
+ }
+
+ ret = call_secvio_config(dev, id, SECVIO_CONFIG_WRITE, &vals[0],
+ &vals[1], &vals[2], &vals[3], &vals[4],
+ nb_vals);
+ if (ret)
+ dev_err(dev, "Failed to write reg %x\n", id);
+
+ ret = size;
+
+exit:
+ up(&data->sem);
+ return ret;
+}
+
+static const struct file_operations imx_secvio_sc_info_ops = {
+ .open = imx_secvio_sc_file_open,
+ .read = imx_secvio_sc_info_read,
+ .write = imx_secvio_sc_info_write,
+};
+
+static ssize_t imx_secvio_sc_enable_read(struct file *filp,
+ char __user *user_buffer,
+ size_t size, loff_t *offset)
+{
+ int ret = 0;
+ struct device *dev = filp->private_data;
+ struct imx_secvio_sc_data *data = dev_get_drvdata(dev);
+ size_t buffer_pos = 0;
+
+ if (!access_ok(VERIFY_WRITE, user_buffer, size)) {
+ dev_err(dev, "Access check failed for %p(%ld)\n", user_buffer,
+ size);
+ return -EFAULT;
+ }
+
+ if (down_interruptible(&data->sem))
+ return -ERESTARTSYS;
+
+ buffer_write(dev, data->io_buffer, data->io_buffer_size, &buffer_pos,
+ "%d\n", data->enabled);
+ if (ret) {
+ dev_err(dev, "Failed to write enabled state\n");
+ goto exit;
+ }
+
+ ret = simple_read_from_buffer(user_buffer, size, offset,
+ data->io_buffer, buffer_pos);
+
+exit:
+ up(&data->sem);
+ return ret;
+}
+
+static ssize_t imx_secvio_sc_enable_write(struct file *filp,
+ const char __user *user_buffer,
+ size_t size, loff_t *offset)
+{
+ int ret = 0;
+ struct device *dev = filp->private_data;
+ struct imx_secvio_sc_data *data = dev_get_drvdata(dev);
+ int state;
+
+ if (!access_ok(VERIFY_READ, user_buffer, size)) {
+ dev_err(dev, "Access check failed for %p(%ld)\n", user_buffer,
+ size);
+ return -EFAULT;
+ }
+
+ if (down_interruptible(&data->sem))
+ return -ERESTARTSYS;
+
+ /* Reset buffer */
+ data->io_buffer[0] = '\0';
+
+ ret = simple_write_to_buffer(data->io_buffer, data->io_buffer_size,
+ offset, user_buffer, size);
+ if (ret < 0) {
+ dev_err(dev, "Failed to copy user buffer\n");
+ ret = -EIO;
+ goto exit;
+ }
+
+ ret = sscanf(data->io_buffer, "%d", &state);
+ if (ret < 0) {
+ dev_err(dev, "Failed to process input\n");
+ ret = -EIO;
+ goto exit;
+ } else if (ret != 1) {
+ dev_err(dev, "Failed to process all parameters\n");
+ ret = -EIO;
+ goto exit;
+ }
+
+ if (state) {
+ ret = enable_irq(dev);
+ if (ret) {
+ dev_err(dev, "Fail to enable IRQ\n");
+ goto exit;
+ }
+ data->enabled = 1;
+ } else {
+ ret = disable_irq(dev);
+ if (ret) {
+ dev_err(dev, "Fail to disable IRQ\n");
+ goto exit;
+ }
+ data->enabled = 0;
+ }
+
+ ret = size;
+
+exit:
+ up(&data->sem);
+ return ret;
+}
+
+static const struct file_operations imx_secvio_sc_enable_ops = {
+ .open = imx_secvio_sc_file_open,
+ .read = imx_secvio_sc_enable_read,
+ .write = imx_secvio_sc_enable_write,
+};
+
+#endif /* CONFIG_DEBUG_FS */
+
+static int imx_secvio_sc_debugfs(struct device *dev)
+{
+ int ret = 0;
+#ifdef CONFIG_DEBUG_FS
+ struct imx_secvio_sc_data *data = dev_get_drvdata(dev);
+
+ data->io_buffer_size = IMX_SECVIO_BUFFER_SIZE;
+ data->io_buffer = devm_kzalloc(dev, data->io_buffer_size, GFP_KERNEL);
+ if (!data->io_buffer) {
+ ret = -ENOMEM;
+ dev_err(dev, "Failed to allocate mem for IO buffer\n");
+ goto error;
+ }
+
+ /* Create a folder */
+ data->dfs = debugfs_create_dir(dev_name(dev), NULL);
+ if (!data->dfs) {
+ dev_err(dev, "Failed to create dfs dir\n");
+ ret = -EIO;
+ goto error;
+ }
+
+ /* Create the semaphore for file operations */
+ sema_init(&data->sem, 1);
+
+ /* Create the file to read info and write to reg */
+ debugfs_create_file("info", 0x666,
+ data->dfs, dev,
+ &imx_secvio_sc_info_ops);
+
+ /* Create the file to enable/disable IRQs */
+ debugfs_create_file("enable", 0x666,
+ data->dfs, dev,
+ &imx_secvio_sc_enable_ops);
+
+ goto exit;
+
+error:
+ imx_secvio_sc_debugfs_remove(dev);
+
+exit:
+#endif /* CONFIG_DEBUG_FS */
+ return ret;
+}
+
+static int enable_irq(struct device *dev)
+{
+ int ret = 0, ret2;
+ sc_err_t sciErr;
+ struct imx_secvio_sc_data *data = dev_get_drvdata(dev);
+ const char *err_expl = NULL;
+ u32 irq_status;
+
+ /* Enable the IRQ */
+ sciErr = sc_irq_enable(data->ipcHandle, SC_R_MU_1A,
+ SC_IRQ_GROUP_WAKE,
+ SC_IRQ_SECVIO, true);
+ if (sciErr) {
+ ret = sci_err_to_errno(sciErr, &err_expl);
+ dev_err(dev, "Cannot enable SCU IRQ: %s(%d)\n", err_expl,
+ sciErr);
+ goto exit;
+ }
+
+ /* Enable interrupt */
+ sciErr = sc_seco_secvio_enable(data->ipcHandle);
+ if (sciErr != SC_ERR_NONE) {
+ ret = sci_err_to_errno(sciErr, &err_expl);
+ dev_err(dev, "Cannot enable SNVS irq: %s(%d)\n", err_expl,
+ sciErr);
+ goto exit;
+ };
+
+ /* Unmask interrupt */
+ sciErr = sc_irq_status(data->ipcHandle, SC_R_MU_1A, SC_IRQ_GROUP_WAKE,
+ &irq_status);
+ if (sciErr != SC_ERR_NONE) {
+ ret = sci_err_to_errno(sciErr, &err_expl);
+ dev_err(dev, "Cannot unmask irq: %s(%d)\n", err_expl,
+ sciErr);
+ goto exit;
+ };
+
+exit:
+ if (ret) {
+ ret2 = disable_irq(dev);
+ if (ret2)
+ dev_warn(dev, "Failed to disable the IRQ\n");
+ }
+
+ return ret;
+}
+
+static int disable_irq(struct device *dev)
+{
+ int ret = 0;
+ sc_err_t sciErr;
+ struct imx_secvio_sc_data *data = dev_get_drvdata(dev);
+ const char *err_expl = NULL;
+
+ /* Disable the IRQ */
+ sciErr = sc_irq_enable(data->ipcHandle, SC_R_MU_1A,
+ SC_IRQ_GROUP_WAKE,
+ SC_IRQ_SECVIO, false);
+ if (sciErr) {
+ ret = sci_err_to_errno(sciErr, &err_expl);
+ dev_err(dev, "Cannot enable SCU IRQ: %s(%d)\n", err_expl,
+ sciErr);
+ goto exit;
+ }
+
+exit:
+ return ret;
+}
+
+static void imx_secvio_sc_debugfs_remove(struct device *dev)
+{
+#ifdef CONFIG_DEBUG_FS
+ struct imx_secvio_sc_data *data = dev_get_drvdata(dev);
+
+ debugfs_remove(data->dfs);
+
+#endif /* CONFIG_DEBUG_FS */
+}
+
+static int imx_secvio_sc_setup(struct device *dev)
+{
+ int ret = 0, ret2;
+ u32 mu_id;
+ sc_err_t sciErr;
+ struct imx_secvio_sc_data *data;
+ u32 irq_status;
+ bool own_secvio;
+ const char *err_expl = NULL;
+
+ /* Allocate private data */
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data) {
+ ret = -ENOMEM;
+ dev_err(dev, "Failed to allocate mem for data\n");
+ goto error;
+ }
+
+ data->dev = dev;
+
+ dev_set_drvdata(dev, data);
+
+ /* Get Messaging Unit */
+ sciErr = sc_ipc_getMuID(&mu_id);
+ if (sciErr != SC_ERR_NONE) {
+ ret = sci_err_to_errno(sciErr, &err_expl);
+ dev_err(dev, "can not obtain mu id: %s(%d)\n", err_expl,
+ sciErr);
+ goto error;
+ }
+
+ /* Get a handle */
+ sciErr = sc_ipc_open(&data->ipcHandle, mu_id);
+ if (sciErr != SC_ERR_NONE) {
+ ret = sci_err_to_errno(sciErr, &err_expl);
+ dev_err(dev, "can not open mu channel to scu: %s(%d)\n",
+ err_expl,
+ sciErr);
+ goto error;
+ };
+
+ /* Check the API is accessible getting version */
+ ret = call_secvio_config(dev, HPVIDR_ID, SECVIO_CONFIG_READ,
+ &data->version, NULL, NULL, NULL, NULL, 1);
+ if (ret) {
+ dev_err(dev, "Cannot read snvs version: %d\n", ret);
+ goto error;
+ }
+
+ /* Init debug FS */
+ ret = imx_secvio_sc_debugfs(dev);
+ if (ret) {
+ dev_err(dev, "Failed to set debugfs\n");
+ goto error;
+ }
+
+ /* Check we own the SECVIO */
+ own_secvio = sc_rm_is_resource_owned(data->ipcHandle, SC_R_SECVIO);
+ if (!own_secvio) {
+ dev_err(dev, "Secvio resource is not owned\n");
+ ret = -EPERM;
+ goto error;
+ }
+
+ /* Check IRQ exists */
+ sciErr = sc_irq_status(data->ipcHandle, SC_R_MU_1A,
+ SC_IRQ_GROUP_WAKE,
+ &irq_status);
+ if (sciErr != SC_ERR_NONE) {
+ ret = sci_err_to_errno(sciErr, &err_expl);
+ dev_err(dev, "Cannot get IRQ state: %s(%d)\n", err_expl,
+ sciErr);
+ goto error;
+ }
+
+ ret = enable_irq(dev);
+ if (ret) {
+ dev_err(dev, "Failed to enable IRQ\n");
+ goto error;
+ }
+
+ /* Set the callback for notification */
+ data->irq_nb.notifier_call = imx_secvio_sc_notify;
+
+ /* Register the handle of IRQ */
+ ret = register_scu_notifier(&data->irq_nb);
+ if (ret) {
+ dev_err(dev, "Failed to register IRQ notification handler\n");
+ goto error;
+ }
+
+ /* Register the handle for reporting to user */
+ data->report_nb.notifier_call = report_to_user_notify;
+ ret = register_imx_secvio_sc_notifier(&data->report_nb);
+ if (ret) {
+ dev_err(dev, "Failed to register report notif handler\n");
+ goto error;
+ }
+
+ /* Process current state of the secvio and tampers */
+ imx_secvio_sc_process_state(dev);
+
+ data->enabled = 1;
+
+ goto exit;
+
+error:
+ ret2 = imx_secvio_sc_teardown(dev);
+ if (ret2)
+ dev_warn(dev, "Failed to teardown\n");
+
+exit:
+ return ret;
+}
+
+static int imx_secvio_sc_teardown(struct device *dev)
+{
+ int ret = 0;
+ struct imx_secvio_sc_data *data = dev_get_drvdata(dev);
+
+ if (!data)
+ goto exit;
+
+ if (data->dfs)
+ imx_secvio_sc_debugfs_remove(dev);
+
+ if (data->ipcHandle) {
+ ret = disable_irq(dev);
+ if (ret) {
+ dev_err(dev, "failed to disable IRQ\n");
+ goto exit;
+ }
+
+ sc_ipc_close(data->ipcHandle);
+ }
+
+ if (data->report_nb.notifier_call) {
+ ret = unregister_imx_secvio_sc_notifier(&data->report_nb);
+ if (ret) {
+ dev_err(dev, "Failed to unregister report to user\n");
+ goto exit;
+ }
+ }
+
+ if (data->irq_nb.notifier_call) {
+ ret = unregister_scu_notifier(&data->irq_nb);
+ if (ret) {
+ dev_err(dev, "Failed to unregister SCU IRQ\n");
+ goto exit;
+ }
+ }
+
+exit:
+ return ret;
+}
+
+static int imx_secvio_sc_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct device *dev = &pdev->dev;
+
+ ret = imx_secvio_sc_setup(dev);
+ if (ret)
+ dev_err(dev, "Failed to setup\n");
+
+ return ret;
+}
+
+static int imx_secvio_sc_remove(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct device *dev = &pdev->dev;
+
+ ret = imx_secvio_sc_teardown(dev);
+ if (ret)
+ dev_err(dev, "Failed to tear down\n");
+
+ return ret;
+}
+
+static const struct of_device_id imx_secvio_sc_dt_ids[] = {
+ { .compatible = "fsl,imx-sc-secvio", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx_sc_dt_ids);
+
+static struct platform_driver imx_secvio_sc_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "imx_secvio_sc",
+ .of_match_table = imx_secvio_sc_dt_ids,
+ },
+ .probe = imx_secvio_sc_probe,
+ .remove = imx_secvio_sc_remove,
+};
+module_platform_driver(imx_secvio_sc_driver);
+
+MODULE_AUTHOR("Franck LENORMAND <franck.lenormand@nxp.com>");
+MODULE_DESCRIPTION("NXP i.MX driver to handle SNVS secvio irq sent by SCFW");
+MODULE_LICENSE("GPL");
diff --git a/include/soc/imx8/imx-secvio-sc.h b/include/soc/imx8/imx-secvio-sc.h
new file mode 100644
index 000000000000..ff419ad512d1
--- /dev/null
+++ b/include/soc/imx8/imx-secvio-sc.h
@@ -0,0 +1,83 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* Copyright 2019 NXP */
+
+#ifndef _MISC_IMX_SECVIO_SC_H_
+#define _MISC_IMX_SECVIO_SC_H_
+
+#define HPSVS__LP_SEC_VIO__MASK BIT(31)
+#define HPSVS__SW_LPSV__MASK BIT(15)
+#define HPSVS__SW_FSV__MASK BIT(14)
+#define HPSVS__SW_SV__MASK BIT(13)
+#define HPSVS__SV5__MASK BIT(5)
+#define HPSVS__SV4__MASK BIT(4)
+#define HPSVS__SV3__MASK BIT(3)
+#define HPSVS__SV2__MASK BIT(2)
+#define HPSVS__SV1__MASK BIT(1)
+#define HPSVS__SV0__MASK BIT(0)
+
+#define HPSVS__ALL_SV__MASK (HPSVS__LP_SEC_VIO__MASK | \
+ HPSVS__SW_LPSV__MASK | \
+ HPSVS__SW_FSV__MASK | \
+ HPSVS__SW_SV__MASK | \
+ HPSVS__SV5__MASK | \
+ HPSVS__SV4__MASK | \
+ HPSVS__SV3__MASK | \
+ HPSVS__SV2__MASK | \
+ HPSVS__SV1__MASK | \
+ HPSVS__SV0__MASK)
+
+#define LPS__ESVD__MASK BIT(16)
+#define LPS__ET2D__MASK BIT(10)
+#define LPS__ET1D__MASK BIT(9)
+#define LPS__WMT2D__MASK BIT(8)
+#define LPS__WMT1D__MASK BIT(7)
+#define LPS__VTD__MASK BIT(6)
+#define LPS__TTD__MASK BIT(5)
+#define LPS__CTD__MASK BIT(4)
+#define LPS__PGD__MASK BIT(3)
+#define LPS__MCR__MASK BIT(2)
+#define LPS__SRTCR__MASK BIT(1)
+#define LPS__LPTA__MASK BIT(0)
+
+#define LPS__ALL_TP__MASK (LPS__ESVD__MASK | \
+ LPS__ET2D__MASK | \
+ LPS__ET1D__MASK | \
+ LPS__WMT2D__MASK | \
+ LPS__WMT1D__MASK | \
+ LPS__VTD__MASK | \
+ LPS__TTD__MASK | \
+ LPS__CTD__MASK | \
+ LPS__PGD__MASK | \
+ LPS__MCR__MASK | \
+ LPS__SRTCR__MASK | \
+ LPS__LPTA__MASK)
+
+#define LPTDS__ET10D__MASK BIT(7)
+#define LPTDS__ET9D__MASK BIT(6)
+#define LPTDS__ET8D__MASK BIT(5)
+#define LPTDS__ET7D__MASK BIT(4)
+#define LPTDS__ET6D__MASK BIT(3)
+#define LPTDS__ET5D__MASK BIT(2)
+#define LPTDS__ET4D__MASK BIT(1)
+#define LPTDS__ET3D__MASK BIT(0)
+
+#define LPTDS__ALL_TP__MASK (LPTDS__ET10D__MASK | \
+ LPTDS__ET9D__MASK | \
+ LPTDS__ET8D__MASK | \
+ LPTDS__ET7D__MASK | \
+ LPTDS__ET6D__MASK | \
+ LPTDS__ET5D__MASK | \
+ LPTDS__ET4D__MASK | \
+ LPTDS__ET3D__MASK)
+
+/* Struct for notification */
+struct notifier_info {
+ u32 sv_status; /* From HPSVS */
+ u32 tp_status_1; /* From LPS */
+ u32 tp_status_2; /* From LPTDS */
+};
+
+int register_imx_secvio_sc_notifier(struct notifier_block *nb);
+int unregister_imx_secvio_sc_notifier(struct notifier_block *nb);
+
+#endif /* _MISC_IMX_SECVIO_SC_H_ */