From 5ebbfc8c29eb5eb6dfdab853bdb2a622e0533d7e Mon Sep 17 00:00:00 2001
From: Li ZhiGang <lizhigang@kylinos.cn>
Date: Mon, 29 Mar 2021 10:07:19 +0800
Subject: [PATCH] staging: TCM: add GMJS(Nationz Tech) TCM driver.

driver inclusion
category: feature
bugzilla: 50797
CVE: NA

-------------------------------------------------------------------------

Nationz Tech TCM are used for trusted computing, the chip attached via SPI or LPC.
We have a brief verify/test with this driver on KunPeng920 + openEuler system, with externally compiled module.

Signed-off-by: Li ZhiGang <lizhigang@kylinos.cn>
Acked-by: Xie XiuQi <xiexiuqi@huawei.com>
Signed-off-by: Zhen Lei <thunder.leizhen@huawei.com>
Signed-off-by: Yang Yingliang <yangyingliang@huawei.com>
Signed-off-by: Cheng Jian <cj.chengjian@huawei.com>
---
 drivers/staging/Kconfig               |   2 +
 drivers/staging/Makefile              |   1 +
 drivers/staging/gmjstcm/Kconfig       |  21 +
 drivers/staging/gmjstcm/Makefile      |   3 +
 drivers/staging/gmjstcm/tcm.c         | 949 ++++++++++++++++++++++++++
 drivers/staging/gmjstcm/tcm.h         | 122 ++++
 drivers/staging/gmjstcm/tcm_tis_spi.c | 847 +++++++++++++++++++++++
 7 files changed, 1945 insertions(+)
 create mode 100644 drivers/staging/gmjstcm/Kconfig
 create mode 100644 drivers/staging/gmjstcm/Makefile
 create mode 100644 drivers/staging/gmjstcm/tcm.c
 create mode 100644 drivers/staging/gmjstcm/tcm.h
 create mode 100644 drivers/staging/gmjstcm/tcm_tis_spi.c

diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index 1abf76be2aa8..d51fa4f4e7ca 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -126,4 +126,6 @@ source "drivers/staging/axis-fifo/Kconfig"
 
 source "drivers/staging/erofs/Kconfig"
 
+source "drivers/staging/gmjstcm/Kconfig"
+
 endif # STAGING
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index ab0cbe8815b1..1562b51985d0 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -53,3 +53,4 @@ obj-$(CONFIG_SOC_MT7621)	+= mt7621-dts/
 obj-$(CONFIG_STAGING_GASKET_FRAMEWORK)	+= gasket/
 obj-$(CONFIG_XIL_AXIS_FIFO)	+= axis-fifo/
 obj-$(CONFIG_EROFS_FS)		+= erofs/
+obj-$(CONFIG_GMJS_TCM)      += gmjstcm/
diff --git a/drivers/staging/gmjstcm/Kconfig b/drivers/staging/gmjstcm/Kconfig
new file mode 100644
index 000000000000..5b5397ae1832
--- /dev/null
+++ b/drivers/staging/gmjstcm/Kconfig
@@ -0,0 +1,21 @@
+menu "GMJS TCM support"
+
+config GMJS_TCM
+	bool
+
+config GMJS_TCM_CORE
+	tristate "GMJS TCM core support"
+	depends on ARM64 || MIPS
+	default m
+	select GMJS_TCM
+	help
+		GMJS TCM core support.
+
+config GMJS_TCM_SPI
+	tristate "GMJS TCM support on SPI interface"
+	depends on GMJS_TCM_CORE && SPI_MASTER
+	default m
+	help
+		GMJS TCM support on SPI interface.
+
+endmenu
diff --git a/drivers/staging/gmjstcm/Makefile b/drivers/staging/gmjstcm/Makefile
new file mode 100644
index 000000000000..369f01119372
--- /dev/null
+++ b/drivers/staging/gmjstcm/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_GMJS_TCM_CORE) += tcm_core.o
+tcm_core-objs := tcm.o
+obj-$(CONFIG_GMJS_TCM_SPI) += tcm_tis_spi.o
diff --git a/drivers/staging/gmjstcm/tcm.c b/drivers/staging/gmjstcm/tcm.c
new file mode 100644
index 000000000000..5c41bfa8b423
--- /dev/null
+++ b/drivers/staging/gmjstcm/tcm.c
@@ -0,0 +1,949 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2009 Nationz Technologies Inc.
+ *
+ * Description: Exprot symbol for tcm_tis module
+ *
+ * Major Function: public write read register function etc.
+ *
+ */
+
+#include <linux/sched.h>
+#include <linux/poll.h>
+#include <linux/spinlock.h>
+#include <linux/timer.h>
+#include "tcm.h"
+
+/*
+ * const var
+ */
+enum tcm_const {
+	TCM_MINOR = 224,	/* officially assigned */
+	TCM_BUFSIZE = 2048,	/* Buffer Size */
+	TCM_NUM_DEVICES = 256,	/* Max supporting tcm device number */
+};
+
+/*
+ * CMD duration
+ */
+enum tcm_duration {
+	TCM_SHORT = 0,
+	TCM_MEDIUM = 1,
+	TCM_LONG = 2,
+	TCM_UNDEFINED,
+};
+
+/* Max Total of Command Number */
+#define TCM_MAX_ORDINAL 88 /*243*/
+
+static LIST_HEAD(tcm_chip_list);
+static DEFINE_SPINLOCK(driver_lock); /* spin lock */
+static DECLARE_BITMAP(dev_mask, TCM_NUM_DEVICES);
+
+typedef struct tagTCM_Command {
+	u8 ordinal;
+	u8 DURATION;
+} TCM_Command;
+
+static const TCM_Command TCM_Command_List[TCM_MAX_ORDINAL + 1] = {
+	{/*TCM_ORD_ActivateIdentity,		*/122,	1},
+	{/*TCM_ORD_CertifyKey,			*/50,	1},
+	{/*TCM_ORD_CertifyKeyM,			*/51,	1},
+	{/*TCM_ORD_ChangeAuth,			*/12,	1},
+	{/*TCM_ORD_ChangeAuthOwner,		*/16,	0},
+	{/*TCM_ORD_ContinueSelfTeSt,		*/83,	2},
+	{/*TCM_ORD_CreateCounter,		*/220,	0},
+	{/*TCM_ORD_CreateWrapKey,		*/31,	2},
+	{/*TCM_ORD_DiSableForceClear,		*/94,	0},
+	{/*TCM_ORD_DiSableOwnerClear,		*/92,	0},
+	{/*TCM_ORD_EStabliShTranSport,		*/230,	0},
+	{/*TCM_ORD_ExecuteTranSport,		*/231,	2},
+	{/*TCM_ORD_Extend,			*/20,	0},
+	{/*TCM_ORD_FieldUpgrade,		*/170,	2},
+	{/*TCM_ORD_FluShSpecific,		*/186,	0},
+	{/*TCM_ORD_ForceClear,			*/93,	0},
+	{/*TCM_ORD_GetAuditDigeSt,		*/133,	0},
+	{/*TCM_ORD_GetAuditDigeStSigned,	*/134,	1},
+	{/*TCM_ORD_GetCapability,		*/101,	0},
+	{/*TCM_ORD_GetPubKey,			*/33,	0},
+	{/*TCM_ORD_GetRandoM,			*/70,	0},
+	{/*TCM_ORD_GetTeStReSult,		*/84,	0},
+	{/*TCM_ORD_GetTickS,			*/241,	0},
+	{/*TCM_ORD_IncreMentCounter,		*/221,	0},
+	{/*TCM_ORD_LoadContext,			*/185,	1},
+	{/*TCM_ORD_MakeIdentity,		*/121,	2},
+	{/*TCM_ORD_NV_DefineSpace,		*/204,	0},
+	{/*TCM_ORD_NV_ReadValue,		*/207,	0},
+	{/*TCM_ORD_NV_ReadValueAuth,		*/208,	0},
+	{/*TCM_ORD_NV_WriteValue,		*/205,	0},
+	{/*TCM_ORD_NV_WriteValueAuth,		*/206,	0},
+	{/*TCM_ORD_OwnerClear,			*/91,	0},
+	{/*TCM_ORD_OwnerReadInternalPub,	*/129,	0},
+	{/*TCM_ORD_OwnerSetDiSable,		*/110,	0},
+	{/*TCM_ORD_PCR_ReSet,			*/200,	0},
+	{/*TCM_ORD_PcrRead,			*/21,	0},
+	{/*TCM_ORD_PhySicalDiSable,		*/112,	0},
+	{/*TCM_ORD_PhySicalEnable,		*/111,	0},
+	{/*TCM_ORD_PhySicalSetDeactivated,	*/114,	0},
+	{/*TCM_ORD_Quote,			*/22,	1},
+	{/*TCM_ORD_QuoteM,			*/62,	1},
+	{/*TCM_ORD_ReadCounter,			*/222,	0},
+	{/*TCM_ORD_ReadPubek,			*/124,	0},
+	{/*TCM_ORD_ReleaSeCounter,		*/223,	0},
+	{/*TCM_ORD_ReleaSeCounterOwner,		*/224,	0},
+	{/*TCM_ORD_ReleaSeTranSportSigned,	*/232,	1},
+	{/*TCM_ORD_ReSetLockValue,		*/64,	0},
+	{/*TCM_ORD_RevokeTruSt,			*/128,	0},
+	{/*TCM_ORD_SaveContext,			*/184,	1},
+	{/*TCM_ORD_SaveState,			*/152,	1},
+	{/*TCM_ORD_Seal,			*/23,	1},
+	{/*TCM_ORD_Sealx,			*/61,	1},
+	{/*TCM_ORD_SelfTeStFull,		*/80,	2},
+	{/*TCM_ORD_SetCapability,		*/63,	0},
+	{/*TCM_ORD_SetOperatorAuth,		*/116,	0},
+	{/*TCM_ORD_SetOrdinalAuditStatuS,	*/141,	0},
+	{/*TCM_ORD_SetOwnerInStall,		*/113,	0},
+	{/*TCM_ORD_SetTeMpDeactivated,		*/115,	0},
+	{/*TCM_ORD_Sign,			*/60,	1},
+	{/*TCM_ORD_Startup,			*/153,	0},
+	{/*TCM_ORD_TakeOwnerShip,		*/13,	1},
+	{/*TCM_ORD_TickStaMpBlob,		*/242,	1},
+	{/*TCM_ORD_UnSeal,			*/24,	1},
+	{/*TSC_ORD_PhySicalPreSence,		*/10,	0},
+	{/*TSC_ORD_ReSetEStabliShMentBit,	*/11,	0},
+	{/*TCM_ORD_WrapKey,			*/189,	2},
+	{/*TCM_ORD_APcreate,			*/191,	0},
+	{/*TCM_ORD_APTerMinate,			*/192,	0},
+	{/*TCM_ORD_CreateMigratedBlob,		*/193,	1},
+	{/*TCM_ORD_ConvertMigratedBlob,		*/194,	1},
+	{/*TCM_ORD_AuthorizeMigrationKey,	*/195,	0},
+	{/*TCM_ORD_SMS4Encrypt,			*/197,	1},
+	{/*TCM_ORD_SMS4Decrypt,			*/198,	1},
+	{/*TCM_ORD_ReadEKCert,			*/199,	1},
+	{/*TCM_ORD_WriteEKCert,			*/233,	1},
+	{/*TCM_ORD_SCHStart,			*/234,	0},
+	{/*TCM_ORD_SCHUpdata,			*/235,	0},
+	{/*TCM_ORD_SCHCoMplete,			*/236,	0},
+	{/*TCM_ORD_SCHCoMpleteExtend,		*/237,	0},
+	{/*TCM_ORD_ECCDecrypt,			*/238,	1},
+	{/*TCM_ORD_LoadKey,			*/239,	1},
+	{/*TCM_ORD_CreateEndorSeMentKeyPair,	*/120,	2},
+	{/*TCM_ORD_CreateRevocableEK,		*/127,	2},
+	{/*TCM_ORD_ReleaSeECCExchangeSeSSion,	*/174,	1},
+	{/*TCM_ORD_CreateECCExchangeSeSSion,	*/175,	1},
+	{/*TCM_ORD_GetKeyECCExchangeSeSSion,	*/176,	1},
+	{/*TCM_ORD_ActivatePEK,			*/217,	1},
+	{/*TCM_ORD_ActivatePEKCert,		*/218,	1},
+	{0,	0}
+};
+
+static void user_reader_timeout(struct timer_list *t)
+{
+	struct tcm_chip *chip = from_timer(chip, t, user_read_timer);
+
+	schedule_work(&chip->work);
+}
+
+static void timeout_work(struct work_struct *work)
+{
+	struct tcm_chip *chip = container_of(work, struct tcm_chip, work);
+
+	mutex_lock(&chip->buffer_mutex);
+	atomic_set(&chip->data_pending, 0);
+	memset(chip->data_buffer, 0, TCM_BUFSIZE);
+	mutex_unlock(&chip->buffer_mutex);
+}
+
+unsigned long tcm_calc_ordinal_duration(struct tcm_chip *chip,
+					   u32 ordinal)
+{
+	int duration_idx = TCM_UNDEFINED;
+	int duration = 0;
+	int i = 0;
+
+	for (i = 0; i < TCM_MAX_ORDINAL; i++) {
+		if (ordinal == TCM_Command_List[i].ordinal) {
+			duration_idx = TCM_Command_List[i].DURATION;
+			break;
+		}
+	}
+
+	if (duration_idx != TCM_UNDEFINED)
+		duration = chip->vendor.duration[duration_idx];
+	if (duration <= 0)
+		return 2 * 60 * HZ;
+	else
+		return duration;
+}
+EXPORT_SYMBOL_GPL(tcm_calc_ordinal_duration);
+
+/*
+ * Internal kernel interface to transmit TCM commands
+ * buff format: TAG(2 bytes) + Total Size(4 bytes ) +
+ * Command Ordinal(4 bytes ) + ......
+ */
+static ssize_t tcm_transmit(struct tcm_chip *chip, const char *buf,
+			    size_t bufsiz)
+{
+	ssize_t rc = 0;
+	u32 count = 0, ordinal = 0;
+	unsigned long stop = 0;
+
+	count = be32_to_cpu(*((__be32 *)(buf + 2))); /* buff size */
+	ordinal = be32_to_cpu(*((__be32 *)(buf + 6))); /* command ordinal */
+
+	if (count == 0)
+		return -ENODATA;
+	if (count > bufsiz) { /* buff size err ,invalid buff stream */
+		dev_err(chip->dev, "invalid count value %x, %zx\n",
+				count, bufsiz);
+		return -E2BIG;
+	}
+
+	mutex_lock(&chip->tcm_mutex); /* enter mutex */
+
+	rc = chip->vendor.send(chip, (u8 *)buf, count);
+	if (rc < 0) {
+		dev_err(chip->dev, "%s: tcm_send: error %zd\n",
+				__func__, rc);
+		goto out;
+	}
+
+	if (chip->vendor.irq)
+		goto out_recv;
+
+	stop = jiffies + tcm_calc_ordinal_duration(chip,
+			ordinal); /* cmd duration */
+	do {
+		u8 status = chip->vendor.status(chip);
+
+		if ((status & chip->vendor.req_complete_mask) ==
+				chip->vendor.req_complete_val)
+			goto out_recv;
+
+		if ((status == chip->vendor.req_canceled)) {
+			dev_err(chip->dev, "Operation Canceled\n");
+			rc = -ECANCELED;
+			goto out;
+		}
+
+		msleep(TCM_TIMEOUT); /* CHECK */
+		rmb();
+	} while (time_before(jiffies, stop));
+	/* time out */
+	chip->vendor.cancel(chip);
+	dev_err(chip->dev, "Operation Timed out\n");
+	rc = -ETIME;
+	goto out;
+
+out_recv:
+	rc = chip->vendor.recv(chip, (u8 *)buf, bufsiz);
+	if (rc < 0)
+		dev_err(chip->dev, "%s: tcm_recv: error %zd\n",
+				__func__, rc);
+out:
+	mutex_unlock(&chip->tcm_mutex);
+	return rc;
+}
+
+#define TCM_DIGEST_SIZE 32
+#define TCM_ERROR_SIZE 10
+#define TCM_RET_CODE_IDX 6
+#define TCM_GET_CAP_RET_SIZE_IDX 10
+#define TCM_GET_CAP_RET_UINT32_1_IDX 14
+#define TCM_GET_CAP_RET_UINT32_2_IDX 18
+#define TCM_GET_CAP_RET_UINT32_3_IDX 22
+#define TCM_GET_CAP_RET_UINT32_4_IDX 26
+#define TCM_GET_CAP_PERM_DISABLE_IDX 16
+#define TCM_GET_CAP_PERM_INACTIVE_IDX 18
+#define TCM_GET_CAP_RET_BOOL_1_IDX 14
+#define TCM_GET_CAP_TEMP_INACTIVE_IDX 16
+
+#define TCM_CAP_IDX 13
+#define TCM_CAP_SUBCAP_IDX 21
+
+enum tcm_capabilities {
+	TCM_CAP_FLAG = 4,
+	TCM_CAP_PROP = 5,
+};
+
+enum tcm_sub_capabilities {
+	TCM_CAP_PROP_PCR = 0x1,			/* tcm 0x101 */
+	TCM_CAP_PROP_MANUFACTURER = 0x3,	/* tcm 0x103 */
+	TCM_CAP_FLAG_PERM = 0x8,		/* tcm 0x108 */
+	TCM_CAP_FLAG_VOL = 0x9,			/* tcm 0x109 */
+	TCM_CAP_PROP_OWNER = 0x11,		/* tcm 0x101 */
+	TCM_CAP_PROP_TIS_TIMEOUT = 0x15,	/* tcm 0x115 */
+	TCM_CAP_PROP_TIS_DURATION = 0x20,	/* tcm 0x120 */
+};
+
+/*
+ * This is a semi generic GetCapability command for use
+ * with the capability type TCM_CAP_PROP or TCM_CAP_FLAG
+ * and their associated sub_capabilities.
+ */
+
+static const u8 tcm_cap[] = {
+	0, 193,			/* TCM_TAG_RQU_COMMAND 0xc1*/
+	0, 0, 0, 22,		/* length */
+	0, 0, 128, 101,		/* TCM_ORD_GetCapability */
+	0, 0, 0, 0,		/* TCM_CAP_<TYPE> */
+	0, 0, 0, 4,		/* TCM_CAP_SUB_<TYPE> size */
+	0, 0, 1, 0		/* TCM_CAP_SUB_<TYPE> */
+};
+
+static ssize_t transmit_cmd(struct tcm_chip *chip, u8 *data, int len,
+		char *desc)
+{
+	int err = 0;
+
+	len = tcm_transmit(chip, data, len);
+	if (len <  0)
+		return len;
+	if (len == TCM_ERROR_SIZE) {
+		err = be32_to_cpu(*((__be32 *)(data + TCM_RET_CODE_IDX)));
+		dev_dbg(chip->dev, "A TCM error (%d) occurred %s\n", err, desc);
+		return err;
+	}
+	return 0;
+}
+
+/*
+ * Get default timeouts value form tcm by GetCapability with TCM_CAP_PROP_TIS_TIMEOUT prop
+ */
+void tcm_get_timeouts(struct tcm_chip *chip)
+{
+	u8 data[max_t(int, ARRAY_SIZE(tcm_cap), 30)];
+	ssize_t rc = 0;
+	u32 timeout = 0;
+
+	memcpy(data, tcm_cap, sizeof(tcm_cap));
+	data[TCM_CAP_IDX] = TCM_CAP_PROP;
+	data[TCM_CAP_SUBCAP_IDX] = TCM_CAP_PROP_TIS_TIMEOUT;
+
+	rc = transmit_cmd(chip, data, sizeof(data),
+			"attempting to determine the timeouts");
+	if (rc)
+		goto duration;
+
+	if (be32_to_cpu(*((__be32 *)(data + TCM_GET_CAP_RET_SIZE_IDX))) !=
+			4 * sizeof(u32))
+		goto duration;
+
+	/* Don't overwrite default if value is 0 */
+	timeout = be32_to_cpu(*((__be32 *)(data + TCM_GET_CAP_RET_UINT32_1_IDX)));
+	if (timeout)
+		chip->vendor.timeout_a = msecs_to_jiffies(timeout);
+	timeout = be32_to_cpu(*((__be32 *)(data + TCM_GET_CAP_RET_UINT32_2_IDX)));
+	if (timeout)
+		chip->vendor.timeout_b = msecs_to_jiffies(timeout);
+	timeout = be32_to_cpu(*((__be32 *)(data + TCM_GET_CAP_RET_UINT32_3_IDX)));
+	if (timeout)
+		chip->vendor.timeout_c = msecs_to_jiffies(timeout);
+	timeout = be32_to_cpu(*((__be32 *)(data + TCM_GET_CAP_RET_UINT32_4_IDX)));
+	if (timeout)
+		chip->vendor.timeout_d = msecs_to_jiffies(timeout);
+
+duration:
+	memcpy(data, tcm_cap, sizeof(tcm_cap));
+	data[TCM_CAP_IDX] = TCM_CAP_PROP;
+	data[TCM_CAP_SUBCAP_IDX] = TCM_CAP_PROP_TIS_DURATION;
+
+	rc = transmit_cmd(chip, data, sizeof(data),
+			"attempting to determine the durations");
+	if (rc)
+		return;
+
+	if (be32_to_cpu(*((__be32 *)(data + TCM_GET_CAP_RET_SIZE_IDX))) !=
+			3 * sizeof(u32))
+		return;
+
+	chip->vendor.duration[TCM_SHORT] =
+			msecs_to_jiffies(be32_to_cpu(*((__be32 *)(data +
+				TCM_GET_CAP_RET_UINT32_1_IDX))));
+	chip->vendor.duration[TCM_MEDIUM] =
+			msecs_to_jiffies(be32_to_cpu(*((__be32 *)(data +
+				TCM_GET_CAP_RET_UINT32_2_IDX))));
+	chip->vendor.duration[TCM_LONG] =
+			msecs_to_jiffies(be32_to_cpu(*((__be32 *)(data +
+				TCM_GET_CAP_RET_UINT32_3_IDX))));
+}
+EXPORT_SYMBOL_GPL(tcm_get_timeouts);
+
+ssize_t tcm_show_enabled(struct device *dev, struct device_attribute *attr,
+		char *buf)
+{
+	u8 data[max_t(int, ARRAY_SIZE(tcm_cap), 35)];
+	ssize_t rc = 0;
+	struct tcm_chip *chip = dev_get_drvdata(dev);
+
+	if (chip == NULL)
+		return -ENODEV;
+
+	memcpy(data, tcm_cap, sizeof(tcm_cap));
+	data[TCM_CAP_IDX] = TCM_CAP_FLAG;
+	data[TCM_CAP_SUBCAP_IDX] = TCM_CAP_FLAG_PERM;
+
+	rc = transmit_cmd(chip, data, sizeof(data),
+			"attemtping to determine the permanent state");
+	if (rc)
+		return 0;
+	if (data[TCM_GET_CAP_PERM_DISABLE_IDX])
+		return sprintf(buf, "disable\n");
+	else
+		return sprintf(buf, "enable\n");
+}
+EXPORT_SYMBOL_GPL(tcm_show_enabled);
+
+ssize_t tcm_show_active(struct device *dev, struct device_attribute *attr,
+		char *buf)
+{
+	u8 data[max_t(int, ARRAY_SIZE(tcm_cap), 35)];
+	ssize_t rc = 0;
+	struct tcm_chip *chip = dev_get_drvdata(dev);
+
+	if (chip == NULL)
+		return -ENODEV;
+
+	memcpy(data, tcm_cap, sizeof(tcm_cap));
+	data[TCM_CAP_IDX] = TCM_CAP_FLAG;
+	data[TCM_CAP_SUBCAP_IDX] = TCM_CAP_FLAG_PERM;
+
+	rc = transmit_cmd(chip, data, sizeof(data),
+			"attemtping to determine the permanent state");
+	if (rc)
+		return 0;
+	if (data[TCM_GET_CAP_PERM_INACTIVE_IDX])
+		return sprintf(buf, "deactivated\n");
+	else
+		return sprintf(buf, "activated\n");
+}
+EXPORT_SYMBOL_GPL(tcm_show_active);
+
+ssize_t tcm_show_owned(struct device *dev, struct device_attribute *attr,
+		char *buf)
+{
+	u8 data[sizeof(tcm_cap)];
+	ssize_t rc = 0;
+	struct tcm_chip *chip = dev_get_drvdata(dev);
+
+	if (chip == NULL)
+		return -ENODEV;
+
+	memcpy(data, tcm_cap, sizeof(tcm_cap));
+	data[TCM_CAP_IDX] = TCM_CAP_PROP;
+	data[TCM_CAP_SUBCAP_IDX] = TCM_CAP_PROP_OWNER;
+
+	rc = transmit_cmd(chip, data, sizeof(data),
+			"attempting to determine the owner state");
+	if (rc)
+		return 0;
+	if (data[TCM_GET_CAP_RET_BOOL_1_IDX])
+		return sprintf(buf, "Owner installed\n");
+	else
+		return sprintf(buf, "Owner have not installed\n");
+}
+EXPORT_SYMBOL_GPL(tcm_show_owned);
+
+ssize_t tcm_show_temp_deactivated(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	u8 data[sizeof(tcm_cap)];
+	ssize_t rc = 0;
+	struct tcm_chip *chip = dev_get_drvdata(dev);
+
+	if (chip == NULL)
+		return -ENODEV;
+
+	memcpy(data, tcm_cap, sizeof(tcm_cap));
+	data[TCM_CAP_IDX] = TCM_CAP_FLAG;
+	data[TCM_CAP_SUBCAP_IDX] = TCM_CAP_FLAG_VOL;
+
+	rc = transmit_cmd(chip, data, sizeof(data),
+			"attempting to determine the temporary state");
+	if (rc)
+		return 0;
+	if (data[TCM_GET_CAP_TEMP_INACTIVE_IDX])
+		return sprintf(buf, "Temp deactivated\n");
+	else
+		return sprintf(buf, "activated\n");
+}
+EXPORT_SYMBOL_GPL(tcm_show_temp_deactivated);
+
+static const u8 pcrread[] = {
+	0, 193,			/* TCM_TAG_RQU_COMMAND */
+	0, 0, 0, 14,		/* length */
+	0, 0, 128, 21,		/* TCM_ORD_PcrRead */
+	0, 0, 0, 0		/* PCR index */
+};
+
+ssize_t tcm_show_pcrs(struct device *dev, struct device_attribute *attr,
+		      char *buf)
+{
+	u8 data[1024];
+	ssize_t rc = 0;
+	int i = 0, j = 0, num_pcrs = 0;
+	__be32 index = 0;
+	char *str = buf;
+	struct tcm_chip *chip = dev_get_drvdata(dev);
+
+	if (chip == NULL)
+		return -ENODEV;
+
+	memcpy(data, tcm_cap, sizeof(tcm_cap));
+	data[TCM_CAP_IDX] = TCM_CAP_PROP;
+	data[TCM_CAP_SUBCAP_IDX] = TCM_CAP_PROP_PCR;
+
+	rc = transmit_cmd(chip, data, sizeof(data),
+			"attempting to determine the number of PCRS");
+	if (rc)
+		return 0;
+
+	num_pcrs = be32_to_cpu(*((__be32 *)(data + 14)));
+	for (i = 0; i < num_pcrs; i++) {
+		memcpy(data, pcrread, sizeof(pcrread));
+		index = cpu_to_be32(i);
+		memcpy(data + 10, &index, 4);
+		rc = transmit_cmd(chip, data, sizeof(data),
+				"attempting to read a PCR");
+		if (rc)
+			goto out;
+		str += sprintf(str, "PCR-%02d: ", i);
+		for (j = 0; j < TCM_DIGEST_SIZE; j++)
+			str += sprintf(str, "%02X ", *(data + 10 + j));
+		str += sprintf(str, "\n");
+		memset(data, 0, 1024);
+	}
+out:
+	return str - buf;
+}
+EXPORT_SYMBOL_GPL(tcm_show_pcrs);
+
+#define  READ_PUBEK_RESULT_SIZE 128
+static const u8 readpubek[] = {
+	0, 193,			/* TCM_TAG_RQU_COMMAND */
+	0, 0, 0, 42,		/* length */
+	0, 0, 128, 124,		/* TCM_ORD_ReadPubek */
+	0, 0, 0, 0, 0, 0, 0, 0,	/* NONCE */
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0
+};
+
+ssize_t tcm_show_pubek(struct device *dev, struct device_attribute *attr,
+		char *buf)
+{
+	u8 data[READ_PUBEK_RESULT_SIZE] = {0};
+	ssize_t err = 0;
+	int i = 0, rc = 0;
+	char *str = buf;
+	struct tcm_chip *chip = dev_get_drvdata(dev);
+
+	if (chip == NULL)
+		return -ENODEV;
+
+	memcpy(data, readpubek, sizeof(readpubek));
+
+	err = transmit_cmd(chip, data, sizeof(data),
+			"attempting to read the PUBEK");
+	if (err)
+		goto out;
+
+	str += sprintf(str, "PUBEK:");
+	for (i = 0 ; i < 65 ; i++) {
+		if ((i) % 16 == 0)
+			str += sprintf(str, "\n");
+		str += sprintf(str, "%02X  ", data[i+10]);
+	}
+
+	str += sprintf(str, "\n");
+out:
+	rc = str - buf;
+	return rc;
+}
+EXPORT_SYMBOL_GPL(tcm_show_pubek);
+
+#define CAP_VERSION_1_1 6
+#define CAP_VERSION_1_2 0x1A
+#define CAP_VERSION_IDX 13
+static const u8 cap_version[] = {
+	0, 193,			/* TCM_TAG_RQU_COMMAND */
+	0, 0, 0, 18,		/* length */
+	0, 0, 128, 101,		/* TCM_ORD_GetCapability */
+	0, 0, 0, 0,
+	0, 0, 0, 0
+};
+
+ssize_t tcm_show_caps(struct device *dev, struct device_attribute *attr,
+		      char *buf)
+{
+	u8 data[max_t(int, max(ARRAY_SIZE(tcm_cap), ARRAY_SIZE(cap_version)), 30)];
+	ssize_t rc = 0;
+	char *str = buf;
+	struct tcm_chip *chip = dev_get_drvdata(dev);
+
+	if (chip == NULL)
+		return -ENODEV;
+
+	memcpy(data, tcm_cap, sizeof(tcm_cap));
+	data[TCM_CAP_IDX] = TCM_CAP_PROP;
+	data[TCM_CAP_SUBCAP_IDX] = TCM_CAP_PROP_MANUFACTURER;
+
+	rc = transmit_cmd(chip, data, sizeof(data),
+			"attempting to determine the manufacturer");
+	if (rc)
+		return 0;
+
+	str += sprintf(str, "Manufacturer: 0x%x\n",
+			be32_to_cpu(*((__be32 *)(data + TCM_GET_CAP_RET_UINT32_1_IDX))));
+
+	memcpy(data, cap_version, sizeof(cap_version));
+	data[CAP_VERSION_IDX] = CAP_VERSION_1_1;
+	rc = transmit_cmd(chip, data, sizeof(data),
+			"attempting to determine the 1.1 version");
+	if (rc)
+		goto out;
+
+	str += sprintf(str, "Firmware version: %02X.%02X.%02X.%02X\n",
+		       (int)data[14], (int)data[15], (int)data[16],
+		       (int)data[17]);
+
+out:
+	return str - buf;
+}
+EXPORT_SYMBOL_GPL(tcm_show_caps);
+
+ssize_t tcm_store_cancel(struct device *dev, struct device_attribute *attr,
+		const char *buf, size_t count)
+{
+	struct tcm_chip *chip = dev_get_drvdata(dev);
+
+	if (chip == NULL)
+		return 0;
+
+	chip->vendor.cancel(chip);
+	return count;
+}
+EXPORT_SYMBOL_GPL(tcm_store_cancel);
+
+/*
+ * Device file system interface to the TCM
+ * when App call file open in usr space ,this func will respone
+ */
+int tcm_open(struct inode *inode, struct file *file)
+{
+	int rc = 0, minor = iminor(inode);
+	struct tcm_chip *chip = NULL, *pos = NULL;
+
+	spin_lock(&driver_lock);
+
+	list_for_each_entry(pos, &tcm_chip_list, list) {
+		if (pos->vendor.miscdev.minor == minor) {
+			chip = pos;
+			break;
+		}
+	}
+
+	if (chip == NULL) {
+		rc = -ENODEV;
+		goto err_out;
+	}
+
+	if (chip->num_opens) {
+		dev_dbg(chip->dev, "Another process owns this TCM\n");
+		rc = -EBUSY;
+		goto err_out;
+	}
+
+	chip->num_opens++;
+	get_device(chip->dev);
+
+	spin_unlock(&driver_lock);
+
+	chip->data_buffer = kmalloc(TCM_BUFSIZE * sizeof(u8), GFP_KERNEL);
+	if (chip->data_buffer == NULL) {
+		chip->num_opens--;
+		put_device(chip->dev);
+		return -ENOMEM;
+	}
+
+	atomic_set(&chip->data_pending, 0);
+
+	file->private_data = chip;
+	return 0;
+
+err_out:
+	spin_unlock(&driver_lock);
+	return rc;
+}
+EXPORT_SYMBOL_GPL(tcm_open);
+
+int tcm_release(struct inode *inode, struct file *file)
+{
+	struct tcm_chip *chip = file->private_data;
+
+	spin_lock(&driver_lock);
+	file->private_data = NULL;
+	chip->num_opens--;
+	del_singleshot_timer_sync(&chip->user_read_timer);
+	flush_work(&chip->work);
+	atomic_set(&chip->data_pending, 0);
+	put_device(chip->dev);
+	kfree(chip->data_buffer);
+	spin_unlock(&driver_lock);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(tcm_release);
+
+ssize_t tcm_write(struct file *file, const char __user *buf,
+		  size_t size, loff_t *off)
+{
+	struct tcm_chip *chip = file->private_data;
+	int in_size = size, out_size;
+
+	/*
+	 * cannot perform a write until the read has cleared
+	 * either via tcm_read or a user_read_timer timeout
+	 */
+	while (atomic_read(&chip->data_pending) != 0)
+		msleep(TCM_TIMEOUT);
+
+	mutex_lock(&chip->buffer_mutex);
+
+	if (in_size > TCM_BUFSIZE)
+		in_size = TCM_BUFSIZE;
+
+	if (copy_from_user(chip->data_buffer, (void __user *)buf, in_size)) {
+		mutex_unlock(&chip->buffer_mutex);
+		return -EFAULT;
+	}
+
+	/* atomic tcm command send and result receive */
+	out_size = tcm_transmit(chip, chip->data_buffer, TCM_BUFSIZE);
+
+	if (out_size >= 0) {
+		atomic_set(&chip->data_pending, out_size);
+		mutex_unlock(&chip->buffer_mutex);
+
+		/* Set a timeout by which the reader must come claim the result */
+		mod_timer(&chip->user_read_timer, jiffies + (60 * HZ));
+	} else
+		mutex_unlock(&chip->buffer_mutex);
+
+	return in_size;
+}
+EXPORT_SYMBOL_GPL(tcm_write);
+
+ssize_t tcm_read(struct file *file, char __user *buf,
+		 size_t size, loff_t *off)
+{
+	struct tcm_chip *chip = file->private_data;
+	int ret_size = 0;
+
+	del_singleshot_timer_sync(&chip->user_read_timer);
+	flush_work(&chip->work);
+	ret_size = atomic_read(&chip->data_pending);
+	atomic_set(&chip->data_pending, 0);
+	if (ret_size > 0) {	/* relay data */
+		if (size < ret_size)
+			ret_size = size;
+
+		mutex_lock(&chip->buffer_mutex);
+		if (copy_to_user(buf, chip->data_buffer, ret_size))
+			ret_size = -EFAULT;
+		mutex_unlock(&chip->buffer_mutex);
+	}
+
+	return ret_size;
+}
+EXPORT_SYMBOL_GPL(tcm_read);
+
+void tcm_remove_hardware(struct device *dev)
+{
+	struct tcm_chip *chip = dev_get_drvdata(dev);
+
+	if (chip == NULL) {
+		dev_err(dev, "No device data found\n");
+		return;
+	}
+
+	spin_lock(&driver_lock);
+	list_del(&chip->list);
+	spin_unlock(&driver_lock);
+
+	dev_set_drvdata(dev, NULL);
+	misc_deregister(&chip->vendor.miscdev);
+	kfree(chip->vendor.miscdev.name);
+
+	sysfs_remove_group(&dev->kobj, chip->vendor.attr_group);
+	/* tcm_bios_log_teardown(chip->bios_dir); */
+
+	clear_bit(chip->dev_num, dev_mask);
+	kfree(chip);
+	put_device(dev);
+}
+EXPORT_SYMBOL_GPL(tcm_remove_hardware);
+
+static u8 savestate[] = {
+	0, 193,			/* TCM_TAG_RQU_COMMAND */
+	0, 0, 0, 10,		/* blob length (in bytes) */
+	0, 0, 128, 152		/* TCM_ORD_SaveState */
+};
+
+/*
+ * We are about to suspend. Save the TCM state
+ * so that it can be restored.
+ */
+int tcm_pm_suspend(struct device *dev, pm_message_t pm_state)
+{
+	struct tcm_chip *chip = dev_get_drvdata(dev);
+
+	if (chip == NULL)
+		return -ENODEV;
+
+	tcm_transmit(chip, savestate, sizeof(savestate));
+	return 0;
+}
+EXPORT_SYMBOL_GPL(tcm_pm_suspend);
+
+int tcm_pm_suspend_p(struct device *dev)
+{
+	struct tcm_chip *chip = dev_get_drvdata(dev);
+
+	if (chip == NULL)
+		return -ENODEV;
+
+	tcm_transmit(chip, savestate, sizeof(savestate));
+	return 0;
+}
+EXPORT_SYMBOL_GPL(tcm_pm_suspend_p);
+
+void tcm_startup(struct tcm_chip *chip)
+{
+	u8 start_up[] = {
+		0, 193,			/* TCM_TAG_RQU_COMMAND */
+		0, 0, 0, 12,		/* blob length (in bytes) */
+		0, 0, 128, 153,		/* TCM_ORD_SaveState */
+		0, 1
+	};
+	if (chip == NULL)
+		return;
+	tcm_transmit(chip, start_up, sizeof(start_up));
+}
+EXPORT_SYMBOL_GPL(tcm_startup);
+
+/*
+ * Resume from a power safe. The BIOS already restored
+ * the TCM state.
+ */
+int tcm_pm_resume(struct device *dev)
+{
+	u8 start_up[] = {
+		0, 193,			/* TCM_TAG_RQU_COMMAND */
+		0, 0, 0, 12,		/* blob length (in bytes) */
+		0, 0, 128, 153,		/* TCM_ORD_SaveState */
+		0, 1
+	};
+	struct tcm_chip *chip = dev_get_drvdata(dev);
+	/* dev_info(chip->dev ,"--call tcm_pm_resume\n"); */
+	if (chip == NULL)
+		return -ENODEV;
+
+	tcm_transmit(chip, start_up, sizeof(start_up));
+	return 0;
+}
+EXPORT_SYMBOL_GPL(tcm_pm_resume);
+
+/*
+ * Called from tcm_<specific>.c probe function only for devices
+ * the driver has determined it should claim.  Prior to calling
+ * this function the specific probe function has called pci_enable_device
+ * upon errant exit from this function specific probe function should call
+ * pci_disable_device
+ */
+struct tcm_chip *tcm_register_hardware(struct device *dev,
+		const struct tcm_vendor_specific *entry)
+{
+	int rc;
+#define DEVNAME_SIZE 7
+
+	char *devname = NULL;
+	struct tcm_chip *chip = NULL;
+
+	/* Driver specific per-device data */
+	chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+	if (chip == NULL) {
+		dev_err(dev, "chip kzalloc err\n");
+		return NULL;
+	}
+
+	mutex_init(&chip->buffer_mutex);
+	mutex_init(&chip->tcm_mutex);
+	INIT_LIST_HEAD(&chip->list);
+
+	INIT_WORK(&chip->work, timeout_work);
+	timer_setup(&chip->user_read_timer, user_reader_timeout, 0);
+
+	memcpy(&chip->vendor, entry, sizeof(struct tcm_vendor_specific));
+
+	chip->dev_num = find_first_zero_bit(dev_mask, TCM_NUM_DEVICES);
+
+	if (chip->dev_num >= TCM_NUM_DEVICES) {
+		dev_err(dev, "No available tcm device numbers\n");
+		kfree(chip);
+		return NULL;
+	} else if (chip->dev_num == 0)
+		chip->vendor.miscdev.minor = TCM_MINOR;
+	else
+		chip->vendor.miscdev.minor = MISC_DYNAMIC_MINOR;
+
+	set_bit(chip->dev_num, dev_mask);
+
+	devname = kmalloc(DEVNAME_SIZE, GFP_KERNEL);
+	scnprintf(devname, DEVNAME_SIZE, "%s%d", "tcm", chip->dev_num);
+	chip->vendor.miscdev.name = devname;
+
+	/* chip->vendor.miscdev.dev = dev; */
+
+	chip->dev = get_device(dev);
+
+	if (misc_register(&chip->vendor.miscdev)) {
+		dev_err(chip->dev,
+			"unable to misc_register %s, minor %d\n",
+			chip->vendor.miscdev.name,
+			chip->vendor.miscdev.minor);
+		put_device(dev);
+		clear_bit(chip->dev_num, dev_mask);
+		kfree(chip);
+		kfree(devname);
+		return NULL;
+	}
+
+	spin_lock(&driver_lock);
+	dev_set_drvdata(dev, chip);
+	list_add(&chip->list, &tcm_chip_list);
+	spin_unlock(&driver_lock);
+
+	rc = sysfs_create_group(&dev->kobj, chip->vendor.attr_group);
+	/* chip->bios_dir = tcm_bios_log_setup(devname); */
+
+	return chip;
+}
+EXPORT_SYMBOL_GPL(tcm_register_hardware);
+
+static int __init tcm_init_module(void)
+{
+	return 0;
+}
+
+static void __exit tcm_exit_module(void)
+{
+}
+
+module_init(tcm_init_module);
+module_exit(tcm_exit_module);
+
+MODULE_AUTHOR("Nationz Technologies Inc.");
+MODULE_DESCRIPTION("TCM Driver");
+MODULE_VERSION("1.1.1.0");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/gmjstcm/tcm.h b/drivers/staging/gmjstcm/tcm.h
new file mode 100644
index 000000000000..40cd0a879c3a
--- /dev/null
+++ b/drivers/staging/gmjstcm/tcm.h
@@ -0,0 +1,122 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2009 Nationz Technologies Inc.
+ *
+ */
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+
+struct device;
+struct tcm_chip;
+
+enum tcm_timeout {
+	TCM_TIMEOUT = 5,
+};
+
+/* TCM addresses */
+enum tcm_addr {
+	TCM_SUPERIO_ADDR = 0x2E,
+	TCM_ADDR = 0x4E,
+};
+
+extern ssize_t tcm_show_pubek(struct device *, struct device_attribute *attr,
+				char *);
+extern ssize_t tcm_show_pcrs(struct device *, struct device_attribute *attr,
+				char *);
+extern ssize_t tcm_show_caps(struct device *, struct device_attribute *attr,
+				char *);
+extern ssize_t tcm_store_cancel(struct device *, struct device_attribute *attr,
+				const char *, size_t);
+extern ssize_t tcm_show_enabled(struct device *, struct device_attribute *attr,
+				char *);
+extern ssize_t tcm_show_active(struct device *, struct device_attribute *attr,
+				char *);
+extern ssize_t tcm_show_owned(struct device *, struct device_attribute *attr,
+				char *);
+extern ssize_t tcm_show_temp_deactivated(struct device *,
+					 struct device_attribute *attr, char *);
+
+struct tcm_vendor_specific {
+	const u8 req_complete_mask;
+	const u8 req_complete_val;
+	const u8 req_canceled;
+	void __iomem *iobase;		/* ioremapped address */
+	void __iomem *iolbc;
+	unsigned long base;		/* TCM base address */
+
+	int irq;
+
+	int region_size;
+	int have_region;
+
+	int (*recv)(struct tcm_chip *, u8 *, size_t);
+	int (*send)(struct tcm_chip *, u8 *, size_t);
+	void (*cancel)(struct tcm_chip *);
+	u8 (*status)(struct tcm_chip *);
+	struct miscdevice miscdev;
+	struct attribute_group *attr_group;
+	struct list_head list;
+	int locality;
+	unsigned long timeout_a, timeout_b, timeout_c, timeout_d; /* jiffies */
+	unsigned long duration[3]; /* jiffies */
+
+	wait_queue_head_t read_queue;
+	wait_queue_head_t int_queue;
+};
+
+struct tcm_chip {
+	struct device *dev;	/* Device stuff */
+
+	int dev_num;		/* /dev/tcm# */
+	int num_opens;		/* only one allowed */
+	int time_expired;
+
+	/* Data passed to and from the tcm via the read/write calls */
+	u8 *data_buffer;
+	atomic_t data_pending;
+	struct mutex buffer_mutex;
+
+	struct timer_list user_read_timer;	/* user needs to claim result */
+	struct work_struct work;
+	struct mutex tcm_mutex;	       /* tcm is processing */
+
+	struct tcm_vendor_specific vendor;
+
+	struct dentry **bios_dir;
+
+	struct list_head list;
+};
+
+#define to_tcm_chip(n) container_of(n, struct tcm_chip, vendor)
+
+static inline int tcm_read_index(int base, int index)
+{
+	outb(index, base);
+	return inb(base+1) & 0xFF;
+}
+
+static inline void tcm_write_index(int base, int index, int value)
+{
+	outb(index, base);
+	outb(value & 0xFF, base+1);
+}
+extern void tcm_startup(struct tcm_chip *);
+extern void tcm_get_timeouts(struct tcm_chip *);
+extern unsigned long tcm_calc_ordinal_duration(struct tcm_chip *, u32);
+extern struct tcm_chip *tcm_register_hardware(struct device *,
+				 const struct tcm_vendor_specific *);
+extern int tcm_open(struct inode *, struct file *);
+extern int tcm_release(struct inode *, struct file *);
+extern ssize_t tcm_write(struct file *, const char __user *, size_t,
+			 loff_t *);
+extern ssize_t tcm_read(struct file *, char __user *, size_t, loff_t *);
+extern void tcm_remove_hardware(struct device *);
+extern int tcm_pm_suspend(struct device *, pm_message_t);
+extern int tcm_pm_suspend_p(struct device *);
+extern int tcm_pm_resume(struct device *);
diff --git a/drivers/staging/gmjstcm/tcm_tis_spi.c b/drivers/staging/gmjstcm/tcm_tis_spi.c
new file mode 100644
index 000000000000..e29c0c1d54c6
--- /dev/null
+++ b/drivers/staging/gmjstcm/tcm_tis_spi.c
@@ -0,0 +1,847 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Kylin Tech. Co., Ltd.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/interrupt.h>
+#include <linux/wait.h>
+#include <linux/acpi.h>
+#include <linux/spi/spi.h>
+
+#include "tcm.h"
+
+static int is_ft_all(void) {
+	return 0;
+}
+
+#define TCM_HEADER_SIZE 10
+
+static bool tcm_debug;
+module_param_named(debug, tcm_debug, bool, 0600);
+MODULE_PARM_DESC(debug, "Turn TCM debugging mode on and off");
+
+#define tcm_dbg(fmt, args...)	\
+{					\
+	if (tcm_debug)		\
+		pr_err(fmt, ## args);	\
+}
+
+enum tis_access {
+	TCM_ACCESS_VALID = 0x80,
+	TCM_ACCESS_ACTIVE_LOCALITY = 0x20,
+	TCM_ACCESS_REQUEST_PENDING = 0x04,
+	TCM_ACCESS_REQUEST_USE = 0x02,
+};
+
+enum tis_status {
+	TCM_STS_VALID = 0x80,
+	TCM_STS_COMMAND_READY = 0x40,
+	TCM_STS_GO = 0x20,
+	TCM_STS_DATA_AVAIL = 0x10,
+	TCM_STS_DATA_EXPECT = 0x08,
+};
+
+enum tis_int_flags {
+	TCM_GLOBAL_INT_ENABLE = 0x80000000,
+	TCM_INTF_BURST_COUNT_STATIC = 0x100,
+	TCM_INTF_CMD_READY_INT = 0x080,
+	TCM_INTF_INT_EDGE_FALLING = 0x040,
+	TCM_INTF_INT_EDGE_RISING = 0x020,
+	TCM_INTF_INT_LEVEL_LOW = 0x010,
+	TCM_INTF_INT_LEVEL_HIGH = 0x008,
+	TCM_INTF_LOCALITY_CHANGE_INT = 0x004,
+	TCM_INTF_STS_VALID_INT = 0x002,
+	TCM_INTF_DATA_AVAIL_INT = 0x001,
+};
+
+enum tis_defaults {
+	TIS_SHORT_TIMEOUT = 750,	/* ms */
+	TIS_LONG_TIMEOUT = 2000,	/* 2 sec */
+};
+
+#define	TCM_ACCESS(l)			(0x0000 | ((l) << 12))
+#define	TCM_INT_ENABLE(l)		(0x0008 | ((l) << 12)) /* interperet */
+#define	TCM_INT_VECTOR(l)		(0x000C | ((l) << 12))
+#define	TCM_INT_STATUS(l)		(0x0010 | ((l) << 12))
+#define	TCM_INTF_CAPS(l)		(0x0014 | ((l) << 12))
+#define	TCM_STS(l)				(0x0018 | ((l) << 12))
+#define	TCM_DATA_FIFO(l)		(0x0024 | ((l) << 12))
+
+#define	TCM_DID_VID(l)			(0x0F00 | ((l) << 12))
+#define	TCM_RID(l)				(0x0F04 | ((l) << 12))
+
+#define TIS_MEM_BASE_huawei     0x3fed40000LL
+
+#define MAX_SPI_FRAMESIZE 64
+
+//
+#define _CPU_FT2000A4
+#define REUSE_CONF_REG_BASE		0x28180208
+#define REUSE_GPIO1_A5_BASE		0x28005000
+
+static void *__iomem reuse_conf_reg;
+static void *__iomem gpio1_a5;
+
+//
+static LIST_HEAD(tis_chips);
+static DEFINE_SPINLOCK(tis_lock);
+
+struct chip_data {
+	u8 cs;
+	u8 tmode;
+	u8 type;
+	u8 poll_mode;
+	u16 clk_div;
+	u32 speed_hz;
+	void (*cs_control)(u32 command);
+};
+
+struct tcm_tis_spi_phy {
+	struct spi_device *spi_device;
+	struct completion ready;
+	u8 *iobuf;
+};
+
+int tcm_tis_spi_transfer(struct device *dev, u32 addr, u16 len,
+		u8 *in, const u8 *out)
+{
+	struct tcm_tis_spi_phy *phy = dev_get_drvdata(dev);
+	int ret = 0;
+	struct spi_message m;
+	struct spi_transfer spi_xfer;
+	u8 transfer_len;
+
+	tcm_dbg("TCM-dbg: %s, addr: 0x%x, len: %x, %s\n",
+			__func__, addr, len, (in) ? "in" : "out");
+
+	spi_bus_lock(phy->spi_device->master);
+
+	/* set gpio1_a5 to LOW */
+	if (is_ft_all() && (phy->spi_device->chip_select == 0)) {
+		iowrite32(0x0, gpio1_a5);
+	}
+
+	while (len) {
+		transfer_len = min_t(u16, len, MAX_SPI_FRAMESIZE);
+
+		phy->iobuf[0] = (in ? 0x80 : 0) | (transfer_len - 1);
+		phy->iobuf[1] = 0xd4;
+		phy->iobuf[2] = addr >> 8;
+		phy->iobuf[3] = addr;
+
+		memset(&spi_xfer, 0, sizeof(spi_xfer));
+		spi_xfer.tx_buf = phy->iobuf;
+		spi_xfer.rx_buf = phy->iobuf;
+		spi_xfer.len = 4;
+		spi_xfer.cs_change = 1;
+
+		spi_message_init(&m);
+		spi_message_add_tail(&spi_xfer, &m);
+		ret = spi_sync_locked(phy->spi_device, &m);
+		if (ret < 0)
+			goto exit;
+
+		spi_xfer.cs_change = 0;
+		spi_xfer.len = transfer_len;
+		spi_xfer.delay_usecs = 5;
+
+		if (in) {
+			spi_xfer.tx_buf = NULL;
+		} else if (out) {
+			spi_xfer.rx_buf = NULL;
+			memcpy(phy->iobuf, out, transfer_len);
+			out += transfer_len;
+		}
+
+		spi_message_init(&m);
+		spi_message_add_tail(&spi_xfer, &m);
+		reinit_completion(&phy->ready);
+		ret = spi_sync_locked(phy->spi_device, &m);
+		if (ret < 0)
+			goto exit;
+
+		if (in) {
+			memcpy(in, phy->iobuf, transfer_len);
+			in += transfer_len;
+		}
+
+		len -= transfer_len;
+	}
+
+exit:
+	/* set gpio1_a5 to HIGH */
+	if (is_ft_all() && (phy->spi_device->chip_select == 0)) {
+		iowrite32(0x20, gpio1_a5);
+	}
+
+	spi_bus_unlock(phy->spi_device->master);
+	tcm_dbg("TCM-dbg: ret: %d\n", ret);
+	return ret;
+}
+
+static int tcm_tis_read8(struct device *dev,
+		u32 addr, u16 len, u8 *result)
+{
+	return tcm_tis_spi_transfer(dev, addr, len, result, NULL);
+}
+
+static int tcm_tis_write8(struct device *dev,
+		u32 addr, u16 len, u8 *value)
+{
+	return tcm_tis_spi_transfer(dev, addr, len, NULL, value);
+}
+
+static int tcm_tis_readb(struct device *dev, u32 addr, u8 *value)
+{
+	return tcm_tis_read8(dev, addr, sizeof(u8), value);
+}
+
+static int tcm_tis_writeb(struct device *dev, u32 addr, u8 value)
+{
+	return tcm_tis_write8(dev, addr, sizeof(u8), &value);
+}
+
+static int tcm_tis_readl(struct device *dev, u32 addr, u32 *result)
+{
+	int rc;
+	__le32 result_le;
+
+	rc = tcm_tis_read8(dev, addr, sizeof(u32), (u8 *)&result_le);
+	tcm_dbg("TCM-dbg: result_le: 0x%x\n", result_le);
+	if (!rc)
+		*result = le32_to_cpu(result_le);
+
+	return rc;
+}
+
+static int tcm_tis_writel(struct device *dev, u32 addr, u32 value)
+{
+	int rc;
+	__le32 value_le;
+
+	value_le = cpu_to_le32(value);
+	rc = tcm_tis_write8(dev, addr, sizeof(u32), (u8 *)&value_le);
+
+	return rc;
+}
+
+static int request_locality(struct tcm_chip *chip, int l);
+static void release_locality(struct tcm_chip *chip, int l, int force);
+static void cleanup_tis(void)
+{
+	int ret;
+	u32 inten;
+	struct tcm_vendor_specific *i, *j;
+	struct tcm_chip *chip;
+
+	spin_lock(&tis_lock);
+	list_for_each_entry_safe(i, j, &tis_chips, list) {
+		chip = to_tcm_chip(i);
+		ret = tcm_tis_readl(chip->dev,
+				TCM_INT_ENABLE(chip->vendor.locality), &inten);
+		if (ret < 0)
+			return;
+
+		tcm_tis_writel(chip->dev, TCM_INT_ENABLE(chip->vendor.locality),
+				~TCM_GLOBAL_INT_ENABLE & inten);
+		release_locality(chip, chip->vendor.locality, 1);
+	}
+	spin_unlock(&tis_lock);
+}
+
+static void tcm_tis_init(struct tcm_chip *chip)
+{
+	int ret;
+	u8 rid;
+	u32 vendor, intfcaps;
+
+	ret = tcm_tis_readl(chip->dev, TCM_DID_VID(0), &vendor);
+
+	if ((vendor & 0xffff) != 0x19f5 && (vendor & 0xffff) != 0x1B4E)
+		pr_info("there is no Nationz TCM on you computer\n");
+
+	ret = tcm_tis_readb(chip->dev, TCM_RID(0), &rid);
+	if (ret < 0)
+		return;
+	pr_info("kylin: 2019-09-21 1.2 TCM (device-id 0x%X, rev-id %d)\n",
+		vendor >> 16, rid);
+
+	/* Figure out the capabilities */
+	ret = tcm_tis_readl(chip->dev,
+			TCM_INTF_CAPS(chip->vendor.locality), &intfcaps);
+	if (ret < 0)
+		return;
+
+	if (request_locality(chip, 0) != 0)
+		pr_err("tcm request_locality err\n");
+
+	atomic_set(&chip->data_pending, 0);
+}
+
+static void tcm_handle_err(struct tcm_chip *chip)
+{
+	cleanup_tis();
+	tcm_tis_init(chip);
+}
+
+static bool check_locality(struct tcm_chip *chip, int l)
+{
+	int ret;
+	u8 access;
+
+	ret = tcm_tis_readb(chip->dev, TCM_ACCESS(l), &access);
+	tcm_dbg("TCM-dbg: access: 0x%x\n", access);
+	if (ret < 0)
+		return false;
+
+	if ((access & (TCM_ACCESS_ACTIVE_LOCALITY | TCM_ACCESS_VALID)) ==
+					(TCM_ACCESS_ACTIVE_LOCALITY | TCM_ACCESS_VALID)) {
+		chip->vendor.locality = l;
+		return true;
+	}
+
+	return false;
+}
+
+static int request_locality(struct tcm_chip *chip, int l)
+{
+	unsigned long stop;
+
+	if (check_locality(chip, l))
+		return l;
+
+	tcm_tis_writeb(chip->dev, TCM_ACCESS(l), TCM_ACCESS_REQUEST_USE);
+
+	/* wait for burstcount */
+	stop = jiffies + chip->vendor.timeout_a;
+	do {
+		if (check_locality(chip, l))
+			return l;
+		msleep(TCM_TIMEOUT);
+	} while (time_before(jiffies, stop));
+
+	return -1;
+}
+
+static void release_locality(struct tcm_chip *chip, int l, int force)
+{
+	int ret;
+	u8 access;
+
+	ret = tcm_tis_readb(chip->dev, TCM_ACCESS(l), &access);
+	if (ret < 0)
+		return;
+	if (force || (access & (TCM_ACCESS_REQUEST_PENDING | TCM_ACCESS_VALID)) ==
+			(TCM_ACCESS_REQUEST_PENDING | TCM_ACCESS_VALID))
+		tcm_tis_writeb(chip->dev,
+				TCM_ACCESS(l), TCM_ACCESS_ACTIVE_LOCALITY);
+}
+
+static u8 tcm_tis_status(struct tcm_chip *chip)
+{
+	int ret;
+	u8 status;
+
+	ret = tcm_tis_readb(chip->dev,
+			TCM_STS(chip->vendor.locality), &status);
+	tcm_dbg("TCM-dbg: status: 0x%x\n", status);
+	if (ret < 0)
+		return 0;
+
+	return status;
+}
+
+static void tcm_tis_ready(struct tcm_chip *chip)
+{
+	/* this causes the current command to be aboreted */
+	tcm_tis_writeb(chip->dev, TCM_STS(chip->vendor.locality),
+			TCM_STS_COMMAND_READY);
+}
+
+static int get_burstcount(struct tcm_chip *chip)
+{
+	int ret;
+	unsigned long stop;
+	u8 tmp, tmp1;
+	int burstcnt = 0;
+
+	/* wait for burstcount */
+	/* which timeout value, spec has 2 answers (c & d) */
+	stop = jiffies + chip->vendor.timeout_d;
+	do {
+		ret = tcm_tis_readb(chip->dev,
+				TCM_STS(chip->vendor.locality) + 1,
+				&tmp);
+		tcm_dbg("TCM-dbg: burstcnt: 0x%x\n", burstcnt);
+		if (ret < 0)
+			return -EINVAL;
+		ret = tcm_tis_readb(chip->dev,
+				(TCM_STS(chip->vendor.locality) + 2),
+				&tmp1);
+		tcm_dbg("TCM-dbg: burstcnt: 0x%x\n", burstcnt);
+		if (ret < 0)
+			return -EINVAL;
+
+		burstcnt = tmp | (tmp1 << 8);
+		if (burstcnt)
+			return burstcnt;
+		msleep(TCM_TIMEOUT);
+	} while (time_before(jiffies, stop));
+
+	return -EBUSY;
+}
+
+static int wait_for_stat(struct tcm_chip *chip, u8 mask,
+		unsigned long timeout,
+		wait_queue_head_t *queue)
+{
+	unsigned long stop;
+	u8 status;
+
+	/* check current status */
+	status = tcm_tis_status(chip);
+	if ((status & mask) == mask)
+		return 0;
+
+	stop = jiffies + timeout;
+	do {
+		msleep(TCM_TIMEOUT);
+		status = tcm_tis_status(chip);
+		if ((status & mask) == mask)
+			return 0;
+	} while (time_before(jiffies, stop));
+
+	return -ETIME;
+}
+
+static int recv_data(struct tcm_chip *chip, u8 *buf, size_t count)
+{
+	int ret;
+	int size = 0, burstcnt;
+
+	while (size < count && wait_for_stat(chip,
+				TCM_STS_DATA_AVAIL | TCM_STS_VALID,
+				chip->vendor.timeout_c,
+				&chip->vendor.read_queue) == 0) {
+		burstcnt = get_burstcount(chip);
+
+		if (burstcnt < 0) {
+			dev_err(chip->dev, "Unable to read burstcount\n");
+			return burstcnt;
+		}
+
+		for (; burstcnt > 0 && size < count; burstcnt--) {
+			ret = tcm_tis_readb(chip->dev,
+					TCM_DATA_FIFO(chip->vendor.locality),
+					&buf[size]);
+			tcm_dbg("TCM-dbg: buf[%d]: 0x%x\n", size, buf[size]);
+			size++;
+		}
+	}
+
+	return size;
+}
+
+static int tcm_tis_recv(struct tcm_chip *chip, u8 *buf, size_t count)
+{
+	int size = 0;
+	int expected, status;
+	unsigned long stop;
+
+	if (count < TCM_HEADER_SIZE) {
+		dev_err(chip->dev, "read size is to small: %d\n", (u32)(count));
+		size = -EIO;
+		goto out;
+	}
+
+	/* read first 10 bytes, including tag, paramsize, and result */
+	size = recv_data(chip, buf, TCM_HEADER_SIZE);
+	if (size < TCM_HEADER_SIZE) {
+		dev_err(chip->dev, "Unable to read header\n");
+		goto out;
+	}
+
+	expected = be32_to_cpu(*(__be32 *)(buf + 2));
+	if (expected > count) {
+		dev_err(chip->dev, "Expected data count\n");
+		size = -EIO;
+		goto out;
+	}
+
+	size += recv_data(chip, &buf[TCM_HEADER_SIZE],
+				expected - TCM_HEADER_SIZE);
+	if (size < expected) {
+		dev_err(chip->dev, "Unable to read remainder of result\n");
+		size = -ETIME;
+		goto out;
+	}
+
+	wait_for_stat(chip, TCM_STS_VALID, chip->vendor.timeout_c,
+		      &chip->vendor.int_queue);
+
+	stop = jiffies + chip->vendor.timeout_c;
+	do {
+		msleep(TCM_TIMEOUT);
+		status = tcm_tis_status(chip);
+		if ((status & TCM_STS_DATA_AVAIL) == 0)
+			break;
+
+	} while (time_before(jiffies, stop));
+
+	status = tcm_tis_status(chip);
+	if (status & TCM_STS_DATA_AVAIL) {	/* retry? */
+		dev_err(chip->dev, "Error left over data\n");
+		size = -EIO;
+		goto out;
+	}
+
+out:
+	tcm_tis_ready(chip);
+	release_locality(chip, chip->vendor.locality, 0);
+	if (size < 0)
+		tcm_handle_err(chip);
+	return size;
+}
+
+/*
+ * If interrupts are used (signaled by an irq set in the vendor structure)
+ * tcm.c can skip polling for the data to be available as the interrupt is
+ * waited for here
+ */
+static int tcm_tis_send(struct tcm_chip *chip, u8 *buf, size_t len)
+{
+	int rc, status, burstcnt;
+	size_t count = 0;
+	u32 ordinal;
+	unsigned long stop;
+	int send_again = 0;
+
+tcm_tis_send_again:
+	count = 0;
+	if (request_locality(chip, 0) < 0) {
+		dev_err(chip->dev, "send, tcm is busy\n");
+		return -EBUSY;
+	}
+	status = tcm_tis_status(chip);
+
+	if ((status & TCM_STS_COMMAND_READY) == 0) {
+		tcm_tis_ready(chip);
+		if (wait_for_stat(chip, TCM_STS_COMMAND_READY,
+					chip->vendor.timeout_b, &chip->vendor.int_queue) < 0) {
+			dev_err(chip->dev, "send, tcm wait time out1\n");
+			rc = -ETIME;
+			goto out_err;
+		}
+	}
+
+	while (count < len - 1) {
+		burstcnt = get_burstcount(chip);
+		if (burstcnt < 0) {
+			dev_err(chip->dev, "Unable to read burstcount\n");
+			rc = burstcnt;
+			goto out_err;
+		}
+		for (; burstcnt > 0 && count < len - 1; burstcnt--) {
+			tcm_tis_writeb(chip->dev,
+					TCM_DATA_FIFO(chip->vendor.locality), buf[count]);
+			count++;
+		}
+
+		wait_for_stat(chip, TCM_STS_VALID, chip->vendor.timeout_c,
+				&chip->vendor.int_queue);
+	}
+
+	/* write last byte */
+	tcm_tis_writeb(chip->dev,
+			TCM_DATA_FIFO(chip->vendor.locality), buf[count]);
+
+	wait_for_stat(chip, TCM_STS_VALID,
+			chip->vendor.timeout_c, &chip->vendor.int_queue);
+	stop = jiffies + chip->vendor.timeout_c;
+	do {
+		msleep(TCM_TIMEOUT);
+		status = tcm_tis_status(chip);
+		if ((status & TCM_STS_DATA_EXPECT) == 0)
+			break;
+
+	} while (time_before(jiffies, stop));
+
+	if ((status & TCM_STS_DATA_EXPECT) != 0) {
+		dev_err(chip->dev, "send, tcm expect data\n");
+		rc = -EIO;
+		goto out_err;
+	}
+
+	/* go and do it */
+	tcm_tis_writeb(chip->dev, TCM_STS(chip->vendor.locality), TCM_STS_GO);
+
+	ordinal = be32_to_cpu(*((__be32 *)(buf + 6)));
+	if (wait_for_stat(chip, TCM_STS_DATA_AVAIL | TCM_STS_VALID,
+				tcm_calc_ordinal_duration(chip, ordinal),
+				&chip->vendor.read_queue) < 0) {
+		dev_err(chip->dev, "send, tcm wait time out2\n");
+		rc = -ETIME;
+		goto out_err;
+	}
+
+	return len;
+
+out_err:
+	tcm_tis_ready(chip);
+	release_locality(chip, chip->vendor.locality, 0);
+	tcm_handle_err(chip);
+	if (send_again++ < 3) {
+		goto tcm_tis_send_again;
+	}
+
+	dev_err(chip->dev, "kylin send, err: %d\n", rc);
+	return rc;
+}
+
+static struct file_operations tis_ops = {
+	.owner = THIS_MODULE,
+	.llseek = no_llseek,
+	.open = tcm_open,
+	.read = tcm_read,
+	.write = tcm_write,
+	.release = tcm_release,
+};
+
+static DEVICE_ATTR(pubek, S_IRUGO, tcm_show_pubek, NULL);
+static DEVICE_ATTR(pcrs, S_IRUGO, tcm_show_pcrs, NULL);
+static DEVICE_ATTR(enabled, S_IRUGO, tcm_show_enabled, NULL);
+static DEVICE_ATTR(active, S_IRUGO, tcm_show_active, NULL);
+static DEVICE_ATTR(owned, S_IRUGO, tcm_show_owned, NULL);
+static DEVICE_ATTR(temp_deactivated, S_IRUGO, tcm_show_temp_deactivated,
+			NULL);
+static DEVICE_ATTR(caps, S_IRUGO, tcm_show_caps, NULL);
+static DEVICE_ATTR(cancel, S_IWUSR | S_IWGRP, NULL, tcm_store_cancel);
+
+static struct attribute *tis_attrs[] = {
+	&dev_attr_pubek.attr,
+	&dev_attr_pcrs.attr,
+	&dev_attr_enabled.attr,
+	&dev_attr_active.attr,
+	&dev_attr_owned.attr,
+	&dev_attr_temp_deactivated.attr,
+	&dev_attr_caps.attr,
+	&dev_attr_cancel.attr, NULL,
+};
+
+static struct attribute_group tis_attr_grp = {
+	.attrs = tis_attrs
+};
+
+static struct tcm_vendor_specific tcm_tis = {
+	.status = tcm_tis_status,
+	.recv = tcm_tis_recv,
+	.send = tcm_tis_send,
+	.cancel = tcm_tis_ready,
+	.req_complete_mask = TCM_STS_DATA_AVAIL | TCM_STS_VALID,
+	.req_complete_val = TCM_STS_DATA_AVAIL | TCM_STS_VALID,
+	.req_canceled = TCM_STS_COMMAND_READY,
+	.attr_group = &tis_attr_grp,
+	.miscdev = {
+		.fops = &tis_ops,
+	},
+};
+
+static struct tcm_chip *chip;
+static int tcm_tis_spi_probe(struct spi_device *spi)
+{
+	int ret;
+	u8 revid;
+	u32 vendor, intfcaps;
+	struct tcm_tis_spi_phy *phy;
+	struct chip_data *spi_chip;
+
+	pr_info("TCM(ky): __func__(v=%d) ..\n",
+				10);
+
+	tcm_dbg("TCM-dbg: %s/%d, enter\n", __func__, __LINE__);
+	phy = devm_kzalloc(&spi->dev, sizeof(struct tcm_tis_spi_phy),
+			GFP_KERNEL);
+	if (!phy)
+		return -ENOMEM;
+
+	phy->iobuf = devm_kmalloc(&spi->dev, MAX_SPI_FRAMESIZE, GFP_KERNEL);
+	if (!phy->iobuf)
+		return -ENOMEM;
+
+	phy->spi_device = spi;
+	init_completion(&phy->ready);
+
+	tcm_dbg("TCM-dbg: %s/%d\n", __func__, __LINE__);
+	/* init spi dev */
+	spi->chip_select = 0;		/* cs0 */
+	spi->mode = SPI_MODE_0;
+	spi->bits_per_word = 8;
+	spi->max_speed_hz = spi->max_speed_hz ? : 24000000;
+	spi_setup(spi);
+
+	spi_chip = spi_get_ctldata(spi);
+	if (!spi_chip) {
+		pr_err("There was wrong in spi master\n");
+		return -ENODEV;
+	}
+	/* tcm does not support interrupt mode, we use poll mode instead. */
+	spi_chip->poll_mode = 1;
+
+	tcm_dbg("TCM-dbg: %s/%d\n", __func__, __LINE__);
+	/* regiter tcm hw */
+	chip = tcm_register_hardware(&spi->dev, &tcm_tis);
+	if (!chip) {
+		dev_err(chip->dev, "tcm register hardware err\n");
+		return -ENODEV;
+	}
+
+	dev_set_drvdata(chip->dev, phy);
+
+	/**
+	 * phytium2000a4 spi controller's clk clk level is unstable,
+	 * so it is solved by using the low level of gpio output.
+	 **/
+	if (is_ft_all() && (spi->chip_select == 0)) {
+		/* reuse conf reg base */
+		reuse_conf_reg = ioremap(REUSE_CONF_REG_BASE, 0x10);
+		if (!reuse_conf_reg) {
+			dev_err(&spi->dev, "Failed to ioremap reuse conf reg\n");
+			ret = -ENOMEM;
+			goto out_err;
+		}
+
+		/* gpio1 a5 base addr */
+		gpio1_a5 = ioremap(REUSE_GPIO1_A5_BASE, 0x10);
+		if (!gpio1_a5) {
+			dev_err(&spi->dev, "Failed to ioremap gpio1 a5\n");
+			ret = -ENOMEM;
+			goto out_err;
+		}
+
+		/* reuse cs0 to gpio1_a5 */
+		iowrite32((ioread32(reuse_conf_reg) | 0xFFFF0) & 0xFFF9004F,
+				reuse_conf_reg);
+		/* set gpio1 a5 to output */
+		iowrite32(0x20, gpio1_a5 + 0x4);
+	}
+
+	tcm_dbg("TCM-dbg: %s/%d\n",
+			__func__, __LINE__);
+	ret = tcm_tis_readl(chip->dev, TCM_DID_VID(0), &vendor);
+	if (ret < 0)
+		goto out_err;
+
+	tcm_dbg("TCM-dbg: %s/%d, vendor: 0x%x\n",
+			__func__, __LINE__, vendor);
+	if ((vendor & 0xffff) != 0x19f5 && (vendor & 0xffff) != 0x1B4E) {
+		dev_err(chip->dev, "there is no Nationz TCM on you computer\n");
+		goto out_err;
+	}
+
+	ret = tcm_tis_readb(chip->dev, TCM_RID(0), &revid);
+	tcm_dbg("TCM-dbg: %s/%d, revid: 0x%x\n",
+			__func__, __LINE__, revid);
+	if (ret < 0)
+		goto out_err;
+	dev_info(chip->dev, "kylin: 2019-09-21 1.2 TCM "
+				"(device-id 0x%X, rev-id %d)\n",
+			vendor >> 16, revid);
+
+	/* Default timeouts */
+	chip->vendor.timeout_a = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
+	chip->vendor.timeout_b = msecs_to_jiffies(TIS_LONG_TIMEOUT);
+	chip->vendor.timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
+	chip->vendor.timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
+
+	tcm_dbg("TCM-dbg: %s/%d\n",
+			__func__, __LINE__);
+	/* Figure out the capabilities */
+	ret = tcm_tis_readl(chip->dev,
+			TCM_INTF_CAPS(chip->vendor.locality), &intfcaps);
+	if (ret < 0)
+		goto out_err;
+
+	tcm_dbg("TCM-dbg: %s/%d, intfcaps: 0x%x\n",
+			__func__, __LINE__, intfcaps);
+	if (request_locality(chip, 0) != 0) {
+		dev_err(chip->dev, "tcm request_locality err\n");
+		ret = -ENODEV;
+		goto out_err;
+	}
+
+	INIT_LIST_HEAD(&chip->vendor.list);
+	spin_lock(&tis_lock);
+	list_add(&chip->vendor.list, &tis_chips);
+	spin_unlock(&tis_lock);
+
+	tcm_get_timeouts(chip);
+	tcm_startup(chip);
+
+	tcm_dbg("TCM-dbg: %s/%d, exit\n", __func__, __LINE__);
+	return 0;
+
+out_err:
+	if (is_ft_all()) {
+		if (reuse_conf_reg)
+			iounmap(reuse_conf_reg);
+		if (gpio1_a5)
+			iounmap(gpio1_a5);
+	}
+	tcm_dbg("TCM-dbg: %s/%d, error\n", __func__, __LINE__);
+	dev_set_drvdata(chip->dev, chip);
+	tcm_remove_hardware(chip->dev);
+
+	return ret;
+}
+
+static int tcm_tis_spi_remove(struct spi_device *dev)
+{
+	if (is_ft_all()) {
+		if (reuse_conf_reg)
+			iounmap(reuse_conf_reg);
+		if (gpio1_a5)
+			iounmap(gpio1_a5);
+	}
+
+	dev_info(&dev->dev, "%s\n", __func__);
+	dev_set_drvdata(chip->dev, chip);
+	tcm_remove_hardware(&dev->dev);
+
+	return 0;
+}
+
+static const struct acpi_device_id tcm_tis_spi_acpi_match[] = {
+	{"TCMS0001", 0},
+	{"SMO0768", 0},
+	{"ZIC0601", 0},
+	{}
+};
+MODULE_DEVICE_TABLE(acpi, tcm_tis_spi_acpi_match);
+
+static const struct spi_device_id tcm_tis_spi_id_table[] = {
+	{"SMO0768", 0},
+	{"ZIC0601", 0},
+	{}
+};
+MODULE_DEVICE_TABLE(spi, tcm_tis_spi_id_table);
+
+static struct spi_driver tcm_tis_spi_drv = {
+	.driver = {
+		.name = "tcm_tis_spi",
+		.acpi_match_table = ACPI_PTR(tcm_tis_spi_acpi_match),
+	},
+	.id_table = tcm_tis_spi_id_table,
+	.probe = tcm_tis_spi_probe,
+	.remove = tcm_tis_spi_remove,
+};
+
+module_spi_driver(tcm_tis_spi_drv);
+
+MODULE_AUTHOR("xiongxin<xiongxin(a)tj.kylinos.cn>");
+MODULE_DESCRIPTION("TCM Driver Base Spi");
+MODULE_VERSION("6.1.0.2");
+MODULE_LICENSE("GPL");
-- 
GitLab