[Skiboot] [PATCH v2 14/31] libstb/drivers/tpm_i2c_nuvoton.c: write FIFO
Claudio Carvalho
cclaudio at linux.vnet.ibm.com
Wed Sep 28 18:01:13 AEST 2016
This adds the 2/5 step performed by the TPM I2C Nuvoton driver to
transmit a command to the TPM device. In this step the driver
writes a given command to master I2C FIFO.
Signed-off-by: Claudio Carvalho <cclaudio at linux.vnet.ibm.com>
---
libstb/drivers/tpm_i2c_nuvoton.c | 178 +++++++++++++++++++++++++++++++++++++++
libstb/status_codes.h | 1 +
2 files changed, 179 insertions(+)
diff --git a/libstb/drivers/tpm_i2c_nuvoton.c b/libstb/drivers/tpm_i2c_nuvoton.c
index ead0f15..e418d7b 100644
--- a/libstb/drivers/tpm_i2c_nuvoton.c
+++ b/libstb/drivers/tpm_i2c_nuvoton.c
@@ -35,14 +35,20 @@
* 00.43.
*/
#define TCG_PTP_TIMEOUT_B 2000
+#define TCG_PTP_TIMEOUT_D 30
/* I2C interface offsets */
#define NUVOTON_TPM_STS 0x00
+#define NUVOTON_TPM_BURST_COUNT 0x01
+#define NUVOTON_TPM_DATA_FIFO_W 0x20
/* Bit masks for the TPM STATUS register */
+#define TCG_PTP_STS_VALID 0x80
#define TCG_PTP_STS_COMMAND_READY 0x40
+#define TCG_PTP_STS_EXPECT 0x08
/* TPM Driver values */
+#define MAX_STSVALID_POLLS 5 /* Max poll of 50ms (5*10ms) */
#define TPM_TIMEOUT_INTERVAL 10
static struct tpm_dev *tpm_device = NULL;
@@ -55,6 +61,33 @@ static int tpm_status_write_byte(uint8_t byte)
sizeof(value));
}
+static int tpm_read_sts_reg_valid(uint8_t* value)
+{
+ int polls, rc;
+
+ for(polls=0; polls<=MAX_STSVALID_POLLS; polls++) {
+ rc = tpm_i2c_request_send(tpm_device->bus_id,
+ tpm_device->xscom_base, SMBUS_READ,
+ NUVOTON_TPM_STS, 1, value, sizeof(uint8_t));
+ if (rc < 0)
+ return rc;
+ if (rc == 0 &&
+ ((*value & TCG_PTP_STS_VALID) == TCG_PTP_STS_VALID))
+ return 0;
+ /* Wait TPM STS register be settled */
+ time_wait_ms(5);
+ }
+ value = 0;
+ /**
+ * @fwts-label TPMValidBitTimeout
+ * @fwts-advice The valid bit of the tpm status register is taking
+ * longer to be settled. Either the wait time needs to be increased
+ * or the TPM device is not functional.
+ */
+ prlog(PR_ERR, "TPM: valid bit not settled. Timeout.\n");
+ return STB_TPM_TIMEOUT;
+}
+
static bool tpm_is_command_ready(int* rc)
{
uint8_t value = 0;
@@ -100,6 +133,145 @@ static int tpm_poll_for_command_ready(void)
return STB_TPM_TIMEOUT;
}
+static bool tpm_is_expecting(int* rc)
+{
+ uint8_t value = 0;
+ *rc = tpm_read_sts_reg_valid(&value);
+ if (*rc == 0 &&
+ (( value & TCG_PTP_STS_EXPECT) == TCG_PTP_STS_EXPECT))
+ return true;
+ return false;
+}
+
+static int tpm_read_burst_count(uint8_t* burst_count)
+{
+ int rc = 0;
+ /* In i2C, burstCount is 1 byte */
+ rc = tpm_i2c_request_send(tpm_device->bus_id, tpm_device->xscom_base,
+ SMBUS_READ, NUVOTON_TPM_BURST_COUNT, 1,
+ burst_count, sizeof(uint8_t));
+ DBG("---- burst_count=%d rc=%d\n", *burst_count, rc);
+ if (rc < 0)
+ *burst_count = 0;
+ return rc;
+}
+
+static int tpm_write_fifo(uint8_t* buf, size_t buflen)
+{
+ uint8_t burst_count = 0;
+ int delay = 0;
+ int rc;
+ size_t curByte = 0;
+ uint8_t* bytePtr = buf;
+ uint8_t* curBytePtr = NULL;
+ /*
+ * We will transfer the command except for the last byte
+ * that will be transfered separately to allow for
+ * overflow checking
+ */
+ size_t length = buflen - 1;
+ size_t tx_len = 0;
+
+ do {
+ rc = tpm_read_burst_count(&burst_count);
+ if (rc < 0) {
+ return rc;
+ } else if (burst_count == 0) {
+ /* Need to delay to allow the TPM time */
+ time_wait_ms(TPM_TIMEOUT_INTERVAL);
+ delay += TPM_TIMEOUT_INTERVAL;
+ continue;
+ }
+ /*
+ * Send in some data
+ */
+ curBytePtr = &(bytePtr[curByte]);
+ tx_len = (curByte + burst_count > length ?
+ (length - curByte) : burst_count);
+ rc = tpm_i2c_request_send(tpm_device->bus_id,
+ tpm_device->xscom_base,
+ SMBUS_WRITE, NUVOTON_TPM_DATA_FIFO_W,
+ 1, curBytePtr, tx_len);
+ curByte += tx_len;
+ DBG("%s write FIFO sent %zd bytes."
+ " burstcount polling delay=%d/%d, rc=%d\n",
+ (rc) ? "!!!!" : "----", curByte, delay,
+ TCG_PTP_TIMEOUT_D, rc);
+ delay = 0;
+ if (rc < 0)
+ return rc;
+
+ if (!tpm_is_expecting(&rc)) {
+ /**
+ * @fwts-label TPMWriteFifoOverflow1
+ * @fwts-advice The write to the TPM FIFO overflowed,
+ * the TPM is not expecting more data. This indicates a bug
+ * in the TPM device driver.
+ */
+ prlog(PR_ERR, "TPM: write FIFO overflow1\n");
+ return STB_TPM_OVERFLOW;
+ }
+ /* Everything but the last byte sent? */
+ if (curByte >= length)
+ break;
+ } while (delay < TCG_PTP_TIMEOUT_D);
+
+ if (delay < TCG_PTP_TIMEOUT_D) {
+ /*
+ * Send the final byte
+ */
+ delay = 0;
+ do {
+ rc = tpm_read_burst_count(&burst_count);
+ if (rc < 0) {
+ return rc;
+ } else if (burst_count == 0) {
+ /* Need to delay to allow the TPM time */
+ time_wait_ms(TPM_TIMEOUT_INTERVAL);
+ delay += TPM_TIMEOUT_INTERVAL;
+ continue;
+ }
+ curBytePtr = &(bytePtr[curByte]);
+ rc = tpm_i2c_request_send(tpm_device->bus_id,
+ tpm_device->xscom_base,
+ SMBUS_WRITE,
+ NUVOTON_TPM_DATA_FIFO_W, 1,
+ curBytePtr, 1);
+ DBG("%s write FIFO sent last byte, delay=%d/%d,"
+ " rc=%d\n",
+ (rc) ? "!!!!" : "----", delay,
+ TCG_PTP_TIMEOUT_D, rc);
+ break;
+ } while (delay < TCG_PTP_TIMEOUT_D);
+ }
+
+ if (delay >= TCG_PTP_TIMEOUT_D) {
+ /**
+ * @fwts-label TPMWriteBurstcountBitTimeout
+ * @fwts-advice The burstcount bit of the tpm status register is
+ * taking longer to be settled. Either the wait time need to be
+ * increased or the TPM device is not functional.
+ */
+ prlog(PR_ERR, "TPM: write FIFO, burstcount polling timeout."
+ " delay=%d/%d\n", delay, TCG_PTP_TIMEOUT_D);
+ return STB_TPM_TIMEOUT;
+ }
+ if (rc == 0) {
+ if (tpm_is_expecting(&rc)) {
+ /**
+ * @fwts-label TPMWriteFifoOverflow2
+ * @fwts-advice The write to the TPM FIFO overflowed.
+ * It is expecting more data even though we think we
+ * are done. This indicates a bug in the TPM device
+ * driver.
+ */
+ prlog(PR_ERR, "TPM: write FIFO overflow2\n");
+ return STB_TPM_OVERFLOW;
+ }
+ }
+ return rc;
+}
+
static int tpm_transmit(struct tpm_dev *dev, uint8_t* buf, size_t cmdlen,
size_t* buflen)
{
@@ -127,6 +299,12 @@ static int tpm_transmit(struct tpm_dev *dev, uint8_t* buf, size_t cmdlen,
if (rc < 0)
goto out;
}
+
+ DBG("step 2/5: write FIFO\n");
+ rc = tpm_write_fifo(buf, cmdlen);
+ if (rc < 0)
+ goto out;
+
out:
DBG("**** tpm_transmit %s, rc=%d ****\n",
(rc) ? "ERROR" : "SUCCESS", rc);
diff --git a/libstb/status_codes.h b/libstb/status_codes.h
index 88443b9..1637e9f 100644
--- a/libstb/status_codes.h
+++ b/libstb/status_codes.h
@@ -26,6 +26,7 @@
#define STB_VERIFY_FAILED -100
/* TPM */
+#define STB_TPM_OVERFLOW -300
#define STB_TPM_TIMEOUT -301
#endif /* __STB_STATUS_CODES_H */
--
1.9.1
More information about the Skiboot
mailing list