From: Li ZhiGang lizhigang@kylinos.cn
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");