From f255d31f9063e50b56208fff439b63039cfd7ac6 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 22 Aug 2015 18:31:31 -0600 Subject: dm: tpm: Add a uclass for Trusted Platform Modules Add a new uclass for TPMs which uses almost the same TIS (TPM Interface Specification) as is currently implemented. Since init() is handled by the normal driver model probe() method, we don't need to implement that. Also rename the transfer method to xfer() which is a less clumbsy name. Once all drivers and users are converted to driver model we can remove the old code. Signed-off-by: Simon Glass Acked-by: Christophe Ricard Reviewed-by: Heiko Schocher --- drivers/tpm/tpm-uclass.c | 133 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 drivers/tpm/tpm-uclass.c (limited to 'drivers/tpm/tpm-uclass.c') diff --git a/drivers/tpm/tpm-uclass.c b/drivers/tpm/tpm-uclass.c new file mode 100644 index 0000000000..b6e1fc5e62 --- /dev/null +++ b/drivers/tpm/tpm-uclass.c @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2015 Google, Inc + * Written by Simon Glass + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include "tpm_internal.h" + +int tpm_open(struct udevice *dev) +{ + struct tpm_ops *ops = tpm_get_ops(dev); + + if (!ops->open) + return -ENOSYS; + + return ops->open(dev); +} + +int tpm_close(struct udevice *dev) +{ + struct tpm_ops *ops = tpm_get_ops(dev); + + if (!ops->close) + return -ENOSYS; + + return ops->close(dev); +} + +int tpm_get_desc(struct udevice *dev, char *buf, int size) +{ + struct tpm_ops *ops = tpm_get_ops(dev); + + if (!ops->get_desc) + return -ENOSYS; + + return ops->get_desc(dev, buf, size); +} + +/* Returns max number of milliseconds to wait */ +static ulong tpm_tis_i2c_calc_ordinal_duration(struct tpm_chip_priv *priv, + u32 ordinal) +{ + int duration_idx = TPM_UNDEFINED; + int duration = 0; + + if (ordinal < TPM_MAX_ORDINAL) { + duration_idx = tpm_ordinal_duration[ordinal]; + } else if ((ordinal & TPM_PROTECTED_ORDINAL_MASK) < + TPM_MAX_PROTECTED_ORDINAL) { + duration_idx = tpm_protected_ordinal_duration[ + ordinal & TPM_PROTECTED_ORDINAL_MASK]; + } + + if (duration_idx != TPM_UNDEFINED) + duration = priv->duration_ms[duration_idx]; + + if (duration <= 0) + return 2 * 60 * 1000; /* Two minutes timeout */ + else + return duration; +} + +int tpm_xfer(struct udevice *dev, const uint8_t *sendbuf, size_t send_size, + uint8_t *recvbuf, size_t *recv_size) +{ + struct tpm_chip_priv *priv = dev_get_uclass_priv(dev); + struct tpm_ops *ops = tpm_get_ops(dev); + ulong start, stop; + uint count, ordinal; + int ret, ret2; + + if (ops->xfer) + return ops->xfer(dev, sendbuf, send_size, recvbuf, recv_size); + + if (!ops->send || !ops->recv) + return -ENOSYS; + + /* switch endianess: big->little */ + count = get_unaligned_be32(sendbuf + TPM_CMD_COUNT_BYTE); + ordinal = get_unaligned_be32(sendbuf + TPM_CMD_ORDINAL_BYTE); + + if (count == 0) { + debug("no data\n"); + return -ENODATA; + } + if (count > send_size) { + debug("invalid count value %x %zx\n", count, send_size); + return -E2BIG; + } + + debug("%s: Calling send\n", __func__); + ret = ops->send(dev, sendbuf, send_size); + if (ret < 0) + return ret; + + start = get_timer(0); + stop = tpm_tis_i2c_calc_ordinal_duration(priv, ordinal); + do { + ret = ops->recv(dev, priv->buf, sizeof(priv->buf)); + if (ret >= 0) { + if (ret > *recv_size) + return -ENOSPC; + memcpy(recvbuf, priv->buf, ret); + *recv_size = ret; + ret = 0; + break; + } else if (ret != -EAGAIN) { + return ret; + } + + mdelay(priv->retry_time_ms); + if (get_timer(start) > stop) { + ret = -ETIMEDOUT; + break; + } + } while (ret); + + ret2 = ops->cleanup ? ops->cleanup(dev) : 0; + + return ret2 ? ret2 : ret; +} + +UCLASS_DRIVER(tpm) = { + .id = UCLASS_TPM, + .name = "tpm", + .flags = DM_UC_FLAG_SEQ_ALIAS, + .per_device_auto_alloc_size = sizeof(struct tpm_chip_priv), +}; -- cgit v1.2.1