Skip to content
Snippets Groups Projects
Commit 5ebbfc8c authored by Li ZhiGang's avatar Li ZhiGang Committed by Cheng Jian
Browse files

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: default avatarLi ZhiGang <lizhigang@kylinos.cn>
Acked-by: default avatarXie XiuQi <xiexiuqi@huawei.com>
Signed-off-by: default avatarZhen Lei <thunder.leizhen@huawei.com>
Signed-off-by: default avatarYang Yingliang <yangyingliang@huawei.com>
Signed-off-by: default avatarCheng Jian <cj.chengjian@huawei.com>
parent d1479e45
No related branches found
No related tags found
No related merge requests found
......@@ -126,4 +126,6 @@ source "drivers/staging/axis-fifo/Kconfig"
source "drivers/staging/erofs/Kconfig"
source "drivers/staging/gmjstcm/Kconfig"
endif # STAGING
......@@ -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/
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
obj-$(CONFIG_GMJS_TCM_CORE) += tcm_core.o
tcm_core-objs := tcm.o
obj-$(CONFIG_GMJS_TCM_SPI) += tcm_tis_spi.o
// 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");
// 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 *);
// 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");
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment