Hi Ilias, On Wed, 3 Nov 2021 at 09:09, Ilias Apalodimas <ilias.apalodi...@linaro.org> wrote: > > There's a lot of code duplication in U-Boot right now. All the TPM TIS > compatible drivers we have at the moment have their own copy of a TIS > implementation. > > So let's create a common layer which implements the core TIS functions. > Any driver added from now own, which is compatible with the TIS spec, will > only have to provide the underlying bus communication mechanisms. > > Signed-off-by: Ilias Apalodimas <ilias.apalodi...@linaro.org> > --- > drivers/tpm/tpm2_tis_core.c | 523 ++++++++++++++++++++++++++++++++++++ > drivers/tpm/tpm_tis.h | 39 +++ > include/tpm-v2.h | 1 + > 3 files changed, 563 insertions(+) > create mode 100644 drivers/tpm/tpm2_tis_core.c > > diff --git a/drivers/tpm/tpm2_tis_core.c b/drivers/tpm/tpm2_tis_core.c > new file mode 100644 > index 000000000000..7d7050151622 > --- /dev/null > +++ b/drivers/tpm/tpm2_tis_core.c > @@ -0,0 +1,523 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Copyright (c) 2020, Linaro Limited > + * > + * Based on the Linux TIS core interface and U-Boot original SPI TPM driver > + */ > + > +#include <common.h> > +#include <dm.h> > +#include <tpm-v2.h> > +#include <linux/delay.h> > +#include <linux/unaligned/be_byteshift.h> > +#include "tpm_tis.h" > + > +/** > + * tpm_tis_get_desc - Get the TPM description > + * > + * @udev: udevice
TPM device > + * @buf: buffer to fill data > + * @size: buffer size > + * > + * @Return: Number of characters written (or would have been written) in > buffer > + */ > +int tpm_tis_get_desc(struct udevice *udev, char *buf, int size) Please use dev throughout, to be consistent with how driver model is used > +{ > + struct tpm_chip *chip = dev_get_priv(udev); > + > + if (size < 80) > + return -ENOSPC; > + > + return snprintf(buf, size, > + "%s v2.0: VendorID 0x%04x, DeviceID 0x%04x, > RevisionID 0x%02x [%s]", > + udev->name, chip->vend_dev & 0xFFFF, > + chip->vend_dev >> 16, chip->rid, > + (chip->is_open ? "open" : "closed")); > +} > + > +/** > + * tpm_tis_check_locality - Check the current TPM locality > + * > + * @udev: udevice > + * @loc: locality > + * > + * Return: True if the tested locality matches > + */ > +static bool tpm_tis_check_locality(struct udevice *udev, int loc) > +{ > + struct tpm_chip *chip = dev_get_priv(udev); > + struct tpm_tis_phy_ops *phy_ops = chip->phy_ops; > + u8 locality; > + > + phy_ops->read_bytes(udev, TPM_ACCESS(loc), 1, &locality); > + if ((locality & (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID | > + TPM_ACCESS_REQUEST_USE)) == > + (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) { > + chip->locality = loc; > + return true; > + } > + > + return false; > +} > + > +/** > + * tpm_tis_request_locality - Request a locality from the TPM > + * > + * @udev: udev > + * @loc: requested locality > + * > + * Return: 0 on success -1 on failure > + */ > +int tpm_tis_request_locality(struct udevice *udev, int loc) > +{ > + struct tpm_chip *chip = dev_get_priv(udev); > + struct tpm_tis_phy_ops *phy_ops = chip->phy_ops; > + u8 buf = TPM_ACCESS_REQUEST_USE; > + unsigned long start, stop; > + > + if (tpm_tis_check_locality(udev, loc)) > + return 0; > + > + phy_ops->write_bytes(udev, TPM_ACCESS(loc), 1, &buf); > + start = get_timer(0); > + stop = chip->timeout_a; > + do { > + if (tpm_tis_check_locality(udev, loc)) > + return 0; > + mdelay(TPM_TIMEOUT_MS); > + } while (get_timer(start) < stop); > + > + return -1; > +} > + > +/** > + * tpm_tis_status - Check the current device status > + * > + * @udev: udevice > + * @status: return value of status > + * > + * Return: 0 on success, negative on failure > + */ > +static int tpm_tis_status(struct udevice *udev, u8 *status) > +{ > + struct tpm_chip *chip = dev_get_priv(udev); > + struct tpm_tis_phy_ops *phy_ops = chip->phy_ops; > + > + if (chip->locality < 0) > + return -EINVAL; > + > + phy_ops->read_bytes(udev, TPM_STS(chip->locality), 1, status); > + > + if ((*status & TPM_STS_READ_ZERO)) { > + log_err("TPM returned invalid status\n"); > + return -EINVAL; > + } > + > + return 0; > +} > + > +/** > + * tpm_tis_release_locality - Release the requested locality > + * > + * @udev: udevice > + * @loc: requested locality > + * > + * Return: 0 on success, negative on failure > + */ > +int tpm_tis_release_locality(struct udevice *udev, int loc) > +{ > + struct tpm_chip *chip = dev_get_priv(udev); > + struct tpm_tis_phy_ops *phy_ops = chip->phy_ops; > + u8 buf = TPM_ACCESS_ACTIVE_LOCALITY; > + int ret; > + > + if (chip->locality < 0) > + return 0; > + > + ret = phy_ops->write_bytes(udev, TPM_ACCESS(loc), 1, &buf); > + chip->locality = -1; > + > + return ret; > +} > + > +/** > + * tpm_tis_wait_for_stat - Wait for TPM to become ready > + * > + * @udev: udev > + * @mask: mask to match > + * @timeout: timeout for retries > + * @status: current status > + * > + * Return: 0 on success, negative on failure > + */ > +static int tpm_tis_wait_for_stat(struct udevice *udev, u8 mask, > + unsigned long timeout, u8 *status) > +{ > + unsigned long start = get_timer(0); > + unsigned long stop = timeout; > + int ret; > + > + do { > + mdelay(TPM_TIMEOUT_MS); > + ret = tpm_tis_status(udev, status); > + if (ret) > + return ret; > + > + if ((*status & mask) == mask) > + return 0; > + } while (get_timer(start) < stop); > + > + return -ETIMEDOUT; > +} > + > +/** > + * tpm_tis_get_burstcount - Get the burstcount for the data FIFO > + * > + * @udev: udevice > + * @burstcount: current burstcount > + * > + * Return: 0 on success, negative on failure > + */ > +static int tpm_tis_get_burstcount(struct udevice *udev, size_t *burstcount) > +{ > + struct tpm_chip *chip = dev_get_priv(udev); > + struct tpm_tis_phy_ops *phy_ops = chip->phy_ops; > + unsigned long start, stop; > + u32 burst; > + > + if (chip->locality < 0) > + return -EINVAL; > + > + /* wait for burstcount */ > + start = get_timer(0); > + /* > + * This is the TPMv2 defined timeout. Change this in case you want to > + * make the driver compatile to TPMv1 > + */ > + stop = chip->timeout_a; > + do { > + phy_ops->read32(udev, TPM_STS(chip->locality), &burst); > + *burstcount = (burst >> 8) & 0xFFFF; > + if (*burstcount) > + return 0; > + > + mdelay(TPM_TIMEOUT_MS); > + } while (get_timer(start) < stop); > + > + return -ETIMEDOUT; > +} > + > +/** > + * tpm_tis_ready - Cancel pending comands and get the device on a ready state > + * > + * @udev: udevcie > + * > + * Return: 0 on success, negative on failure > + */ > +static int tpm_tis_ready(struct udevice *udev) > +{ > + struct tpm_chip *chip = dev_get_priv(udev); > + struct tpm_tis_phy_ops *phy_ops = chip->phy_ops; > + u8 data = TPM_STS_COMMAND_READY; > + > + /* This will cancel any pending commands */ > + return phy_ops->write_bytes(udev, TPM_STS(chip->locality), 1, &data); > +} > + > +/** > + * tpm_tis_send - send data to the device > + * > + * @udev: udevice > + * @buf: buffer to send > + * @len: size of the buffer > + * > + * Return: number of bytes sent or negative on failure > + */ > +int tpm_tis_send(struct udevice *udev, const u8 *buf, size_t len) > +{ > + struct tpm_chip *chip = dev_get_priv(udev); > + struct tpm_tis_phy_ops *phy_ops = chip->phy_ops; > + size_t burstcnt, wr_size, sent = 0; > + u8 data = TPM_STS_GO; > + u8 status; > + int ret; > + > + if (!chip) > + return -ENODEV; > + > + ret = tpm_tis_request_locality(udev, 0); > + if (ret < 0) > + return -EBUSY; > + > + ret = tpm_tis_status(udev, &status); > + if (ret) > + goto release_locality; > + > + if (!(status & TPM_STS_COMMAND_READY)) { > + ret = tpm_tis_ready(udev); > + if (ret) { > + log_err("Can't cancel previous TPM operation\n"); > + goto release_locality; > + } > + ret = tpm_tis_wait_for_stat(udev, TPM_STS_COMMAND_READY, > + chip->timeout_b, &status); > + if (ret) { > + log_err("TPM not ready\n"); > + goto release_locality; > + } > + } > + > + while (len > 0) { > + ret = tpm_tis_get_burstcount(udev, &burstcnt); > + if (ret) > + goto release_locality; > + > + wr_size = min(len, burstcnt); > + ret = phy_ops->write_bytes(udev, > TPM_DATA_FIFO(chip->locality), > + wr_size, buf + sent); > + if (ret < 0) > + goto release_locality; > + > + ret = tpm_tis_wait_for_stat(udev, TPM_STS_VALID, > + chip->timeout_c, &status); > + if (ret) > + goto release_locality; > + > + sent += wr_size; > + len -= wr_size; > + /* make sure the TPM expects more data */ > + if (len && !(status & TPM_STS_DATA_EXPECT)) { > + ret = -EIO; > + goto release_locality; > + } > + } > + > + /* > + * Make a final check ensuring everything is ok and the TPM expects no > + * more data > + */ > + ret = tpm_tis_wait_for_stat(udev, TPM_STS_VALID, chip->timeout_c, > + &status); > + if (ret) > + goto release_locality; > + > + if (status & TPM_STS_DATA_EXPECT) { > + ret = -EIO; > + goto release_locality; > + } > + > + ret = phy_ops->write_bytes(udev, TPM_STS(chip->locality), 1, &data); > + if (ret) > + goto release_locality; > + > + return sent; > + > +release_locality: > + tpm_tis_ready(udev); > + tpm_tis_release_locality(udev, chip->locality); > + > + return ret; > +} > + > +/** > + * tpm_tis_recv_data - Receive data from a device. Wrapper for tpm_tis_recv > + * > + * @udev: udevice > + * @buf: buffer to copy data > + * @size: buffer size > + * > + * Return: bytes read or negative on failure > + */ > +static int tpm_tis_recv_data(struct udevice *udev, u8 *buf, size_t count) > +{ > + struct tpm_chip *chip = dev_get_priv(udev); > + struct tpm_tis_phy_ops *phy_ops = chip->phy_ops; > + int size = 0, len, ret; > + size_t burstcnt; > + u8 status; > + > + while (size < count && > + tpm_tis_wait_for_stat(udev, TPM_STS_DATA_AVAIL | TPM_STS_VALID, > + chip->timeout_c, &status) == 0) { > + ret = tpm_tis_get_burstcount(udev, &burstcnt); > + if (ret) > + return ret; > + > + len = min_t(int, burstcnt, count - size); > + ret = phy_ops->read_bytes(udev, TPM_DATA_FIFO(chip->locality), > + len, buf + size); > + if (ret < 0) > + return ret; > + > + size += len; > + } > + > + return size; > +} > + > +/** > + * tpm_tis_recv - Receive data from a device > + * > + * @udev: udevice > + * @buf: buffer to copy data > + * @size: buffer size > + * > + * Return: bytes read or negative on failure > + */ > +int tpm_tis_recv(struct udevice *udev, u8 *buf, size_t count) > +{ > + struct tpm_chip *chip = dev_get_priv(udev); > + int size, expected; > + > + if (count < TPM_HEADER_SIZE) > + return -E2BIG; > + > + size = tpm_tis_recv_data(udev, buf, TPM_HEADER_SIZE); > + if (size < TPM_HEADER_SIZE) { > + log_err("TPM error, unable to read header\n"); > + goto out; > + } > + > + expected = get_unaligned_be32(buf + TPM_CMD_COUNT_OFFSET); > + if (expected > count) { > + size = -EIO; > + log_warning("Too much data: %d > %zu\n", expected, count); > + goto out; > + } > + > + size += tpm_tis_recv_data(udev, &buf[TPM_HEADER_SIZE], > + expected - TPM_HEADER_SIZE); > + if (size < expected) { > + log(LOGC_NONE, LOGL_ERR, > + "TPM error, unable to read remaining bytes of result\n"); > + size = -EIO; > + goto out; > + } > + > +out: > + tpm_tis_ready(udev); > + /* acquired in tpm_tis_send */ > + tpm_tis_release_locality(udev, chip->locality); > + > + return size; > +} > + > +/** tpm_tis_cleanup - Get the device in ready state and release locality > + * > + * @udev: udevice > + * > + * Return: always 0 > + */ > +int tpm_tis_cleanup(struct udevice *udev) > +{ > + struct tpm_chip *chip = dev_get_priv(udev); > + > + tpm_tis_ready(udev); > + tpm_tis_release_locality(udev, chip->locality); > + > + return 0; > +} > + > +/** > + * tpm_tis_open - Open the device and request locality 0 > + * > + * @udev: udevice > + * > + * Return: 0 on success, negative on failure > + */ > +int tpm_tis_open(struct udevice *udev) > +{ > + struct tpm_chip *chip = dev_get_priv(udev); > + int ret; > + > + if (chip->is_open) > + return -EBUSY; > + > + ret = tpm_tis_request_locality(udev, 0); > + if (!ret) > + chip->is_open = 1; > + > + return ret; > +} > + > +/** > + * tpm_tis_ops_register - register the PHY ops for the device > + * > + * @udev: udevice > + * @ops: bus ops for the device > + */ > +void tpm_tis_ops_register(struct udevice *udev, struct tpm_tis_phy_ops *ops) > +{ > + struct tpm_chip *chip = dev_get_priv(udev); > + > + chip->phy_ops = ops; > +} > + > +static bool tis_check_ops(struct tpm_tis_phy_ops *phy_ops) > +{ > + if (!phy_ops || !phy_ops->read_bytes || !phy_ops->write_bytes || > + !phy_ops->read32 || !phy_ops->write32) > + return false; > + > + return true; > +} > + > +/** > + * tpm_tis_init - inititalize the device > + * > + * @udev: udevice > + * > + * Return: 0 on success, negative on failure > + */ > +int tpm_tis_init(struct udevice *udev) > +{ > + struct tpm_chip *chip = dev_get_priv(udev); > + struct tpm_tis_phy_ops *phy_ops = chip->phy_ops; > + int ret; > + u32 tmp; > + > + if (!tis_check_ops(phy_ops)) { > + log_err("Driver bug. No bus ops defined\n"); > + return -1; > + } > + ret = tpm_tis_request_locality(udev, 0); > + if (ret) > + return ret; > + > + chip->timeout_a = TIS_SHORT_TIMEOUT_MS; > + chip->timeout_b = TIS_LONG_TIMEOUT_MS; > + chip->timeout_c = TIS_SHORT_TIMEOUT_MS; > + chip->timeout_d = TIS_SHORT_TIMEOUT_MS; > + > + /* Disable interrupts */ > + phy_ops->read32(udev, TPM_INT_ENABLE(chip->locality), &tmp); > + tmp |= TPM_INTF_CMD_READY_INT | TPM_INTF_LOCALITY_CHANGE_INT | > + TPM_INTF_DATA_AVAIL_INT | TPM_INTF_STS_VALID_INT; > + tmp &= ~TPM_GLOBAL_INT_ENABLE; > + phy_ops->write32(udev, TPM_INT_ENABLE(chip->locality), tmp); > + > + phy_ops->read_bytes(udev, TPM_RID(chip->locality), 1, &chip->rid); > + phy_ops->read32(udev, TPM_DID_VID(chip->locality), &chip->vend_dev); > + > + return tpm_tis_release_locality(udev, chip->locality); > +} > + > +/** > + * tpm_tis_close - Close the device and release locality > + * > + * @udev: udevice > + * > + * Return: 0 on success, negative on failure > + */ > +int tpm_tis_close(struct udevice *udev) > +{ > + struct tpm_chip *chip = dev_get_priv(udev); > + int ret = 0; > + > + if (chip->is_open) { > + ret = tpm_tis_release_locality(udev, chip->locality); > + chip->is_open = 0; > + } > + > + return ret; > +} > diff --git a/drivers/tpm/tpm_tis.h b/drivers/tpm/tpm_tis.h > index 2a160fe05c9a..28dd7f200329 100644 > --- a/drivers/tpm/tpm_tis.h > +++ b/drivers/tpm/tpm_tis.h > @@ -21,6 +21,36 @@ > #include <linux/compiler.h> > #include <linux/types.h> > > +struct tpm_tis_phy_ops { > + int (*read_bytes)(struct udevice *udev, u32 addr, u16 len, > + u8 *result); > + int (*write_bytes)(struct udevice *udev, u32 addr, u16 len, > + const u8 *value); > + int (*read32)(struct udevice *udev, u32 addr, u32 *result); > + int (*write32)(struct udevice *udev, u32 addr, u32 src); > +}; > + > +enum tis_int_flags { > + TPM_GLOBAL_INT_ENABLE = 0x80000000, > + TPM_INTF_BURST_COUNT_STATIC = 0x100, > + TPM_INTF_CMD_READY_INT = 0x080, > + TPM_INTF_INT_EDGE_FALLING = 0x040, > + TPM_INTF_INT_EDGE_RISING = 0x020, > + TPM_INTF_INT_LEVEL_LOW = 0x010, > + TPM_INTF_INT_LEVEL_HIGH = 0x008, > + TPM_INTF_LOCALITY_CHANGE_INT = 0x004, > + TPM_INTF_STS_VALID_INT = 0x002, > + TPM_INTF_DATA_AVAIL_INT = 0x001, > +}; > + > +#define TPM_ACCESS(l) (0x0000 | ((l) << 12)) > +#define TPM_INT_ENABLE(l) (0x0008 | ((l) << 12)) > +#define TPM_STS(l) (0x0018 | ((l) << 12)) > +#define TPM_DATA_FIFO(l) (0x0024 | ((l) << 12)) > +#define TPM_DID_VID(l) (0x0F00 | ((l) << 12)) > +#define TPM_RID(l) (0x0F04 | ((l) << 12)) > +#define TPM_INTF_CAPS(l) (0x0014 | ((l) << 12)) lower-case hex > + > enum tpm_timeout { > TPM_TIMEOUT_MS = 5, > TIS_SHORT_TIMEOUT_MS = 750, > @@ -43,6 +73,7 @@ struct tpm_chip { > u8 rid; > unsigned long timeout_a, timeout_b, timeout_c, timeout_d; /* msec */ > ulong chip_type; > + struct tpm_tis_phy_ops *phy_ops; comment? > }; > > struct tpm_input_header { > @@ -130,4 +161,12 @@ enum tis_status { > }; > #endif > > +int tpm_tis_open(struct udevice *udev); Please add comments > +int tpm_tis_close(struct udevice *udev); > +int tpm_tis_cleanup(struct udevice *udev); > +int tpm_tis_send(struct udevice *udev, const u8 *buf, size_t len); > +int tpm_tis_recv(struct udevice *udev, u8 *buf, size_t count); > +int tpm_tis_get_desc(struct udevice *udev, char *buf, int size); > +int tpm_tis_init(struct udevice *udev); > +void tpm_tis_ops_register(struct udevice *udev, struct tpm_tis_phy_ops *ops); > #endif > diff --git a/include/tpm-v2.h b/include/tpm-v2.h > index 13b3db67c60f..e6b68769f3ff 100644 > --- a/include/tpm-v2.h > +++ b/include/tpm-v2.h > @@ -396,6 +396,7 @@ enum { > TPM_STS_DATA_EXPECT = 1 << 3, > TPM_STS_SELF_TEST_DONE = 1 << 2, > TPM_STS_RESPONSE_RETRY = 1 << 1, > + TPM_STS_READ_ZERO = 0x23 > }; > > enum { > -- > 2.33.1 > Regards, Simon