diff options
author | Duncan Laurie <dlaurie@chromium.org> | 2016-09-19 19:21:02 -0700 |
---|---|---|
committer | Duncan Laurie <dlaurie@chromium.org> | 2016-09-19 19:21:02 -0700 |
commit | 9b8ebfb96cf0937add6ad8478887d8932450e255 (patch) | |
tree | 1a59fff6307fe36dd239d8452818904385ea5496 /src/drivers/i2c | |
parent | 1de4f9549b8cb290451843dc9d2274887d4f3b22 (diff) | |
download | coreboot-9b8ebfb96cf0937add6ad8478887d8932450e255.tar.xz |
Revert "drivers/i2c/tpm: Split cr50 driver from main driver"
This reverts commit c565f9910707b91fcc7a27bab28806e558bb474d.
Diffstat (limited to 'src/drivers/i2c')
-rw-r--r-- | src/drivers/i2c/tpm/Kconfig | 13 | ||||
-rw-r--r-- | src/drivers/i2c/tpm/Makefile.inc | 19 | ||||
-rw-r--r-- | src/drivers/i2c/tpm/cr50.c | 466 | ||||
-rw-r--r-- | src/drivers/i2c/tpm/tpm.c | 242 |
4 files changed, 237 insertions, 503 deletions
diff --git a/src/drivers/i2c/tpm/Kconfig b/src/drivers/i2c/tpm/Kconfig index db77b956b9..903fc3bbe5 100644 --- a/src/drivers/i2c/tpm/Kconfig +++ b/src/drivers/i2c/tpm/Kconfig @@ -2,19 +2,6 @@ config I2C_TPM bool "I2C TPM" depends on TPM || TPM2 -choice - prompt "I2C TPM Driver" - default I2C_TPM_GENERIC - depends on I2C_TPM - -config I2C_TPM_GENERIC - bool "Generic I2C TPM Driver" - -config I2C_TPM_CR50 - bool "CR50 I2C TPM Driver" - -endchoice - config DRIVER_TPM_I2C_BUS hex "I2C TPM chip bus" default 9 # FIXME, workaround for Kconfig BS diff --git a/src/drivers/i2c/tpm/Makefile.inc b/src/drivers/i2c/tpm/Makefile.inc index 7fcfc78d56..3eb5de0518 100644 --- a/src/drivers/i2c/tpm/Makefile.inc +++ b/src/drivers/i2c/tpm/Makefile.inc @@ -1,17 +1,6 @@ - -ramstage-$(CONFIG_I2C_TPM) += tis.c -romstage-$(CONFIG_I2C_TPM) += tis.c -verstage-$(CONFIG_I2C_TPM) += tis.c -bootblock-$(CONFIG_I2C_TPM) += tis.c - -ramstage-$(CONFIG_I2C_TPM_GENERIC) += tpm.c -romstage-$(CONFIG_I2C_TPM_GENERIC) += tpm.c -verstage-$(CONFIG_I2C_TPM_GENERIC) += tpm.c -bootblock-$(CONFIG_I2C_TPM_GENERIC) += tpm.c - -ramstage-$(CONFIG_I2C_TPM_CR50) += cr50.c -romstage-$(CONFIG_I2C_TPM_CR50) += cr50.c -verstage-$(CONFIG_I2C_TPM_CR50) += cr50.c -bootblock-$(CONFIG_I2C_TPM_CR50) += cr50.c +ramstage-$(CONFIG_I2C_TPM) += tis.c tpm.c +romstage-$(CONFIG_I2C_TPM) += tis.c tpm.c +verstage-$(CONFIG_I2C_TPM) += tis.c tpm.c +bootblock-$(CONFIG_I2C_TPM) += tis.c tpm.c ramstage-$(CONFIG_DRIVER_I2C_TPM_ACPI) += chip.c diff --git a/src/drivers/i2c/tpm/cr50.c b/src/drivers/i2c/tpm/cr50.c deleted file mode 100644 index 419ac1e68f..0000000000 --- a/src/drivers/i2c/tpm/cr50.c +++ /dev/null @@ -1,466 +0,0 @@ -/* - * Copyright 2016 Google Inc. - * - * Based on Linux Kernel TPM driver by - * Peter Huewe <peter.huewe@infineon.com> - * Copyright (C) 2011 Infineon Technologies - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, version 2 of the - * License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -/* - * cr50 is a TPM 2.0 capable device that requries special - * handling for the I2C interface. - * - * - Use an interrupt for transaction status instead of hardcoded delays - * - Must use write+wait+read read protocol - * - All 4 bytes of status register must be read/written at once - * - Burst count max is 63 bytes, and burst count behaves - * slightly differently than other I2C TPMs - * - When reading from FIFO the full burstcnt must be read - * instead of just reading header and determining the remainder - */ - -#include <arch/early_variables.h> -#include <commonlib/endian.h> -#include <stdint.h> -#include <string.h> -#include <types.h> -#include <delay.h> -#include <console/console.h> -#include <device/i2c.h> -#include <endian.h> -#include <timer.h> -#include "tpm.h" - -#define CR50_MAX_BURSTCOUNT 63 - -#define SLEEP_DURATION 60 /* in usec */ -#define SLEEP_DURATION_LONG 210 /* in usec */ -#define SLEEP_DURATION_SAFE 750 /* in usec */ -#define SLEEP_DURATION_PROBE_MS 1000 /* in msec */ - -#define CR50_DID_VID 0x00281ae0L - -struct tpm_inf_dev { - int bus; - unsigned int addr; - uint8_t buf[TPM_BUFSIZE + sizeof(uint8_t)]; -}; - -static struct tpm_inf_dev g_tpm_dev CAR_GLOBAL; - -/* - * iic_tpm_read() - read from TPM register - * - * @addr: register address to read from - * @buffer: provided by caller - * @len: number of bytes to read - * - * 1) send register address byte 'addr' to the TPM - * 2) wait for TPM to indicate it is ready - * 3) read 'len' bytes of TPM response into the provided 'buffer' - * - * Return -1 on error, 0 on success. - */ -static int iic_tpm_read(uint8_t addr, uint8_t *buffer, size_t len) -{ - struct tpm_inf_dev *tpm_dev = car_get_var_ptr(&g_tpm_dev); - - if (tpm_dev->addr == 0) - return -1; - - /* Send the register address byte to the TPM */ - if (i2c_write_raw(tpm_dev->bus, tpm_dev->addr, &addr, 1)) { - printk(BIOS_ERR, "%s: Address write failed\n", __func__); - return -1; - } - - /* Wait for TPM to be ready with response data */ - udelay(SLEEP_DURATION_SAFE); - - /* Read response data from the TPM */ - if (i2c_read_raw(tpm_dev->bus, tpm_dev->addr, buffer, len)) { - printk(BIOS_ERR, "%s: Read response failed\n", __func__); - return -1; - } - - return 0; -} - -/* - * iic_tpm_write() - write to TPM register - * - * @addr: register address to write to - * @buffer: data to write - * @len: number of bytes to write - * - * 1) prepend the provided address to the provided data - * 2) send the address+data to the TPM - * 3) wait for TPM to indicate it is done writing - * - * Returns -1 on error, 0 on success. - */ -static int iic_tpm_write(uint8_t addr, uint8_t *buffer, size_t len) -{ - struct tpm_inf_dev *tpm_dev = car_get_var_ptr(&g_tpm_dev); - - if (tpm_dev->addr == 0) - return -1; - if (len > TPM_BUFSIZE) - return -1; - - /* Prepend the 'register address' to the buffer */ - tpm_dev->buf[0] = addr; - memcpy(tpm_dev->buf + 1, buffer, len); - - /* Send write request buffer with address */ - if (i2c_write_raw(tpm_dev->bus, tpm_dev->addr, tpm_dev->buf, len + 1)) { - printk(BIOS_ERR, "%s: Error writing to TPM\n", __func__); - return -1; - } - - /* Wait for TPM to be ready */ - udelay(SLEEP_DURATION_SAFE); - - return 0; -} - -static int check_locality(struct tpm_chip *chip, int loc) -{ - uint8_t buf; - - if (iic_tpm_read(TPM_ACCESS(loc), &buf, 1) < 0) - return -1; - - if ((buf & (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) == - (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) { - chip->vendor.locality = loc; - return loc; - } - - return -1; -} - -static void release_locality(struct tpm_chip *chip, int loc, int force) -{ - uint8_t buf; - if (iic_tpm_read(TPM_ACCESS(loc), &buf, 1) < 0) - return; - - if (force || (buf & (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) == - (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) { - buf = TPM_ACCESS_ACTIVE_LOCALITY; - iic_tpm_write(TPM_ACCESS(loc), &buf, 1); - } -} - -static int request_locality(struct tpm_chip *chip, int loc) -{ - uint8_t buf = TPM_ACCESS_REQUEST_USE; - - if (check_locality(chip, loc) >= 0) - return loc; /* we already have the locality */ - - iic_tpm_write(TPM_ACCESS(loc), &buf, 1); - - /* wait for burstcount */ - int timeout = 2 * 1000; /* 2s timeout */ - while (timeout) { - if (check_locality(chip, loc) >= 0) - return loc; - mdelay(TPM_TIMEOUT); - timeout--; - } - - return -1; -} - -/* cr50 requires all 4 bytes of status register to be read */ -static uint8_t cr50_tis_i2c_status(struct tpm_chip *chip) -{ - uint8_t buf[4]; - if (iic_tpm_read(TPM_STS(chip->vendor.locality), - buf, sizeof(buf)) < 0) { - printk(BIOS_ERR, "%s: Failed to read status\n", __func__); - return 0; - } - return buf[0]; -} - -/* cr50 requires all 4 bytes of status register to be written */ -static void cr50_tis_i2c_ready(struct tpm_chip *chip) -{ - uint8_t buf[4] = { TPM_STS_COMMAND_READY }; - iic_tpm_write(TPM_STS(chip->vendor.locality), buf, sizeof(buf)); -} - -/* cr50 uses bytes 3:2 of status register for burst count and - * all 4 bytes must be read */ -static int cr50_wait_burst_status(struct tpm_chip *chip, uint8_t mask, - size_t *burst, int *status) -{ - uint8_t buf[4]; - struct stopwatch sw; - - stopwatch_init_msecs_expire(&sw, 2000); - - while (!stopwatch_expired(&sw)) { - if (iic_tpm_read(TPM_STS(chip->vendor.locality), - buf, sizeof(buf)) != 0) { - printk(BIOS_WARNING, "%s: Read failed\n", __func__); - udelay(SLEEP_DURATION_SAFE); - continue; - } - - *status = buf[0]; - *burst = read_le16(&buf[1]); - - /* Check if mask matches and burst is valid */ - if ((*status & mask) == mask && - *burst > 0 && *burst <= CR50_MAX_BURSTCOUNT) - return 0; - - udelay(SLEEP_DURATION_SAFE); - } - - printk(BIOS_ERR, "%s: Timeout reading burst and status\n", __func__); - return -1; -} - -static int cr50_tis_i2c_recv(struct tpm_chip *chip, uint8_t *buf, - size_t buf_len) -{ - size_t burstcnt, current, len, expected; - uint8_t addr = TPM_DATA_FIFO(chip->vendor.locality); - int status; - int ret = -1; - - if (buf_len < TPM_HEADER_SIZE) - goto out; - - if (cr50_wait_burst_status(chip, TPM_STS_VALID, &burstcnt, &status) < 0) - goto out; - if (!(status & TPM_STS_DATA_AVAIL)) { - printk(BIOS_ERR, "%s: First chunk not available\n", __func__); - goto out; - } - - /* Read first chunk of burstcnt bytes */ - if (iic_tpm_read(addr, buf, burstcnt) != 0) { - printk(BIOS_ERR, "%s: Read failed\n", __func__); - goto out; - } - - /* Determine expected data in the return buffer */ - expected = read_be32(buf + TPM_RSP_SIZE_BYTE); - if (expected > buf_len) { - printk(BIOS_ERR, "%s: Too much data: %zu > %zu\n", - __func__, expected, buf_len); - goto out; - } - - /* Now read the rest of the data */ - current = burstcnt; - while (current < expected) { - /* Read updated burst count and check status */ - if (cr50_wait_burst_status(chip, TPM_STS_VALID, - &burstcnt, &status) < 0) - goto out; - if (!(status & TPM_STS_DATA_AVAIL)) { - printk(BIOS_ERR, "%s: Data not available\n", __func__); - goto out; - } - - len = min(burstcnt, expected - current); - if (iic_tpm_read(addr, buf + current, len) != 0) { - printk(BIOS_ERR, "%s: Read failed\n", __func__); - goto out; - } - - current += len; - } - - /* Ensure TPM is done reading data */ - if (cr50_wait_burst_status(chip, TPM_STS_VALID, &burstcnt, &status) < 0) - goto out; - if (status & TPM_STS_DATA_AVAIL) { - printk(BIOS_ERR, "%s: Data still available\n", __func__); - goto out; - } - - ret = current; - -out: - return ret; -} - -static int cr50_tis_i2c_send(struct tpm_chip *chip, uint8_t *buf, size_t len) -{ - int status; - size_t burstcnt, limit, sent = 0; - uint8_t tpm_go[4] = { TPM_STS_GO }; - struct stopwatch sw; - - if (len > TPM_BUFSIZE) - return -1; - - stopwatch_init_msecs_expire(&sw, 2000); - - /* Wait until TPM is ready for a command */ - while (!(cr50_tis_i2c_status(chip) & TPM_STS_COMMAND_READY)) { - if (stopwatch_expired(&sw)) { - printk(BIOS_ERR, "%s: Command ready timeout\n", - __func__); - return -1; - } - - cr50_tis_i2c_ready(chip); - udelay(SLEEP_DURATION_SAFE); - } - - while (len > 0) { - /* Read burst count and check status */ - if (cr50_wait_burst_status(chip, TPM_STS_VALID, - &burstcnt, &status) < 0) - goto out; - if (sent > 0 && !(status & TPM_STS_DATA_EXPECT)) { - printk(BIOS_ERR, "%s: Data not expected\n", __func__); - goto out; - } - - /* Use burstcnt - 1 to account for the address byte - * that is inserted by iic_tpm_write() */ - limit = min(burstcnt - 1, len); - if (iic_tpm_write(TPM_DATA_FIFO(chip->vendor.locality), - &buf[sent], limit) != 0) { - printk(BIOS_ERR, "%s: Write failed\n", __func__); - goto out; - } - - sent += limit; - len -= limit; - } - - /* Ensure TPM is not expecting more data */ - if (cr50_wait_burst_status(chip, TPM_STS_VALID, &burstcnt, &status) < 0) - goto out; - if (status & TPM_STS_DATA_EXPECT) { - printk(BIOS_ERR, "%s: Data still expected\n", __func__); - goto out; - } - - /* Start the TPM command */ - if (iic_tpm_write(TPM_STS(chip->vendor.locality), tpm_go, - sizeof(tpm_go)) < 0) { - printk(BIOS_ERR, "%s: Start command failed\n", __func__); - goto out; - } - return sent; - -out: - /* Abort current transaction if still pending */ - if (cr50_tis_i2c_status(chip) & TPM_STS_COMMAND_READY) - cr50_tis_i2c_ready(chip); - return -1; -} - -static void cr50_vendor_init(struct tpm_chip *chip) -{ - memset(&chip->vendor, 0, sizeof(struct tpm_vendor_specific)); - chip->vendor.req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID; - chip->vendor.req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID; - chip->vendor.req_canceled = TPM_STS_COMMAND_READY; - chip->vendor.status = &cr50_tis_i2c_status; - chip->vendor.recv = &cr50_tis_i2c_recv; - chip->vendor.send = &cr50_tis_i2c_send; - chip->vendor.cancel = &cr50_tis_i2c_ready; -} - -int tpm_vendor_probe(unsigned bus, uint32_t addr) -{ - struct tpm_inf_dev *tpm_dev = car_get_var_ptr(&g_tpm_dev); - struct stopwatch sw; - uint8_t buf = 0; - int ret; - long sw_run_duration = SLEEP_DURATION_PROBE_MS; - - tpm_dev->bus = bus; - tpm_dev->addr = addr; - - /* Wait for TPM_ACCESS register ValidSts bit to be set */ - stopwatch_init_msecs_expire(&sw, sw_run_duration); - do { - ret = iic_tpm_read(TPM_ACCESS(0), &buf, 1); - if (!ret && (buf & TPM_STS_VALID)) { - sw_run_duration = stopwatch_duration_msecs(&sw); - break; - } - udelay(SLEEP_DURATION_SAFE); - } while (!stopwatch_expired(&sw)); - - printk(BIOS_INFO, - "%s: ValidSts bit %s(%d) in TPM_ACCESS register after %ld ms\n", - __func__, (buf & TPM_STS_VALID) ? "set" : "clear", - (buf & TPM_STS_VALID) >> 7, sw_run_duration); - - /* Claim failure if the ValidSts (bit 7) is clear */ - if (!(buf & TPM_STS_VALID)) - return -1; - - return 0; -} - -int tpm_vendor_init(struct tpm_chip *chip, unsigned bus, uint32_t dev_addr) -{ - struct tpm_inf_dev *tpm_dev = car_get_var_ptr(&g_tpm_dev); - uint32_t vendor; - - if (dev_addr == 0) { - printk(BIOS_ERR, "%s: missing device address\n", __func__); - return -1; - } - - tpm_dev->bus = bus; - tpm_dev->addr = dev_addr; - - cr50_vendor_init(chip); - - /* Disable interrupts (not supported) */ - chip->vendor.irq = 0; - - if (request_locality(chip, 0) != 0) - return -1; - - /* Read four bytes from DID_VID register */ - if (iic_tpm_read(TPM_DID_VID(0), (uint8_t *)&vendor, 4) < 0) - goto out_err; - - if (vendor != CR50_DID_VID) { - printk(BIOS_DEBUG, "Vendor ID 0x%08x not recognized\n", vendor); - goto out_err; - } - - printk(BIOS_DEBUG, "cr50 TPM %u:%02x (device-id 0x%X)\n", - tpm_dev->bus, tpm_dev->addr, vendor >> 16); - - chip->is_open = 1; - return 0; - -out_err: - release_locality(chip, 0, 1); - return -1; -} - -void tpm_vendor_cleanup(struct tpm_chip *chip) -{ - release_locality(chip, chip->vendor.locality, 1); -} diff --git a/src/drivers/i2c/tpm/tpm.c b/src/drivers/i2c/tpm/tpm.c index 55c25fefa4..6b96774f60 100644 --- a/src/drivers/i2c/tpm/tpm.c +++ b/src/drivers/i2c/tpm/tpm.c @@ -45,6 +45,7 @@ #define SLEEP_DURATION 60 /* in usec */ #define SLEEP_DURATION_LONG 210 /* in usec */ +#define SLEEP_DURATION_SAFE 750 /* in usec */ #define SLEEP_DURATION_PROBE_MS 1000 /* in msec */ /* max. number of iterations after I2C NAK for 'long' commands @@ -57,16 +58,19 @@ /* expected value for DIDVID register */ #define TPM_TIS_I2C_DID_VID_9635 0x000b15d1L #define TPM_TIS_I2C_DID_VID_9645 0x001a15d1L +#define TPM_TIS_I2C_DID_VID_CR50 0x00281ae0L enum i2c_chip_type { SLB9635, SLB9645, + CR50, UNKNOWN, }; static const char * const chip_name[] = { [SLB9635] = "slb9635tt", [SLB9645] = "slb9645tt", + [CR50] = "cr50", [UNKNOWN] = "unknown/fallback to slb9635", }; @@ -107,6 +111,7 @@ static int iic_tpm_read(uint8_t addr, uint8_t *buffer, size_t len) switch (tpm_dev->chip_type) { case SLB9635: + case CR50: case UNKNOWN: /* slb9635 protocol should work in both cases */ for (count = 0; count < MAX_COUNT; count++) { @@ -469,6 +474,211 @@ out_err: return -1; } +/* + * cr50 is a TPM 2.0 capable device that requries special + * handling for the I2C interface. + * + * - Timeouts need to be longer + * - Must use the older SLB9635 style write+wait+read read protocol + * - All 4 bytes of status register must be read at once + * - Burst count max is 63 bytes, and burst count behaves + * slightly differently than other I2C TPMs + * - When reading from FIFO the full burstcnt must be read + * instead of just reading header and determining the remainder + */ + +/* cr50 max burst count */ +#define CR50_MAX_BURSTCOUNT 63 + +/* cr50 requires all 4 bytes of status register to be read */ +static uint8_t cr50_tis_i2c_status(struct tpm_chip *chip) +{ + uint8_t buf[4]; + if (iic_tpm_read(TPM_STS(chip->vendor.locality), + buf, sizeof(buf)) < 0) { + printk(BIOS_ERR, "%s: Failed to read status\n", __func__); + return 0; + } + return buf[0]; +} + +/* cr50 requires all 4 bytes of status register to be written */ +static void cr50_tis_i2c_ready(struct tpm_chip *chip) +{ + uint8_t buf[4] = { TPM_STS_COMMAND_READY }; + iic_tpm_write_long(TPM_STS(chip->vendor.locality), buf, sizeof(buf)); +} + +/* cr50 uses bytes 3:2 of status register for burst count and + * all 4 bytes must be read */ +static int cr50_wait_burst_status(struct tpm_chip *chip, uint8_t mask, + size_t *burst, int *status) +{ + uint8_t buf[4]; + struct stopwatch sw; + + stopwatch_init_msecs_expire(&sw, 2000); + + while (!stopwatch_expired(&sw)) { + if (iic_tpm_read(TPM_STS(chip->vendor.locality), + buf, sizeof(buf)) != 0) { + printk(BIOS_WARNING, "%s: Read failed\n", __func__); + udelay(SLEEP_DURATION_SAFE); + continue; + } + + *status = buf[0]; + *burst = read_le16(&buf[1]); + + /* Check if mask matches and burst is valid */ + if ((*status & mask) == mask && + *burst > 0 && *burst <= CR50_MAX_BURSTCOUNT) + return 0; + + udelay(SLEEP_DURATION_SAFE); + } + + printk(BIOS_ERR, "%s: Timeout reading burst and status\n", __func__); + return -1; +} + +static int cr50_tis_i2c_recv(struct tpm_chip *chip, uint8_t *buf, + size_t buf_len) +{ + size_t burstcnt, current, len, expected; + uint8_t addr = TPM_DATA_FIFO(chip->vendor.locality); + int status; + int ret = -1; + + if (buf_len < TPM_HEADER_SIZE) + goto out; + + if (cr50_wait_burst_status(chip, TPM_STS_VALID, &burstcnt, &status) < 0) + goto out; + if (!(status & TPM_STS_DATA_AVAIL)) { + printk(BIOS_ERR, "%s: First chunk not available\n", __func__); + goto out; + } + + /* Read first chunk of burstcnt bytes */ + if (iic_tpm_read(addr, buf, burstcnt) != 0) { + printk(BIOS_ERR, "%s: Read failed\n", __func__); + goto out; + } + + /* Determine expected data in the return buffer */ + expected = read_be32(buf + TPM_RSP_SIZE_BYTE); + if (expected > buf_len) { + printk(BIOS_ERR, "%s: Too much data: %zu > %zu\n", + __func__, expected, buf_len); + goto out; + } + + /* Now read the rest of the data */ + current = burstcnt; + while (current < expected) { + /* Read updated burst count and check status */ + if (cr50_wait_burst_status(chip, TPM_STS_VALID, + &burstcnt, &status) < 0) + goto out; + if (!(status & TPM_STS_DATA_AVAIL)) { + printk(BIOS_ERR, "%s: Data not available\n", __func__); + goto out; + } + + len = min(burstcnt, expected - current); + if (iic_tpm_read(addr, buf + current, len) != 0) { + printk(BIOS_ERR, "%s: Read failed\n", __func__); + goto out; + } + + current += len; + } + + /* Ensure TPM is done reading data */ + if (cr50_wait_burst_status(chip, TPM_STS_VALID, &burstcnt, &status) < 0) + goto out; + if (status & TPM_STS_DATA_AVAIL) { + printk(BIOS_ERR, "%s: Data still available\n", __func__); + goto out; + } + + ret = current; + +out: + return ret; +} + +static int cr50_tis_i2c_send(struct tpm_chip *chip, uint8_t *buf, size_t len) +{ + int status; + size_t burstcnt, limit, sent = 0; + uint8_t tpm_go[4] = { TPM_STS_GO }; + struct stopwatch sw; + + if (len > TPM_BUFSIZE) + return -1; + + stopwatch_init_msecs_expire(&sw, 2000); + + /* Wait until TPM is ready for a command */ + while (!(cr50_tis_i2c_status(chip) & TPM_STS_COMMAND_READY)) { + if (stopwatch_expired(&sw)) { + printk(BIOS_ERR, "%s: Command ready timeout\n", + __func__); + return -1; + } + + cr50_tis_i2c_ready(chip); + udelay(SLEEP_DURATION_SAFE); + } + + while (len > 0) { + /* Read burst count and check status */ + if (cr50_wait_burst_status(chip, TPM_STS_VALID, + &burstcnt, &status) < 0) + goto out; + if (sent > 0 && !(status & TPM_STS_DATA_EXPECT)) { + printk(BIOS_ERR, "%s: Data not expected\n", __func__); + goto out; + } + + /* Use burstcnt - 1 to account for the address byte + * that is inserted by iic_tpm_write() */ + limit = min(burstcnt - 1, len); + if (iic_tpm_write(TPM_DATA_FIFO(chip->vendor.locality), + &buf[sent], limit) != 0) { + printk(BIOS_ERR, "%s: Write failed\n", __func__); + goto out; + } + + sent += limit; + len -= limit; + } + + /* Ensure TPM is not expecting more data */ + if (cr50_wait_burst_status(chip, TPM_STS_VALID, &burstcnt, &status) < 0) + goto out; + if (status & TPM_STS_DATA_EXPECT) { + printk(BIOS_ERR, "%s: Data still expected\n", __func__); + goto out; + } + + /* Start the TPM command */ + if (iic_tpm_write(TPM_STS(chip->vendor.locality), tpm_go, + sizeof(tpm_go)) < 0) { + printk(BIOS_ERR, "%s: Start command failed\n", __func__); + goto out; + } + return sent; + +out: + /* Abort current transaction if still pending */ + if (cr50_tis_i2c_status(chip) & TPM_STS_COMMAND_READY) + cr50_tis_i2c_ready(chip); + return -1; +} + /* Initialization of I2C TPM */ int tpm_vendor_probe(unsigned bus, uint32_t addr) @@ -482,8 +692,8 @@ int tpm_vendor_probe(unsigned bus, uint32_t addr) tpm_dev->chip_type = UNKNOWN; tpm_dev->bus = bus; tpm_dev->addr = addr; - tpm_dev->sleep_short = SLEEP_DURATION; - tpm_dev->sleep_long = SLEEP_DURATION_LONG; + tpm_dev->sleep_short = SLEEP_DURATION_SAFE; + tpm_dev->sleep_long = SLEEP_DURATION_SAFE * 2; /* * Probe TPM. Check if the TPM_ACCESS register's ValidSts bit is set(1) @@ -496,7 +706,7 @@ int tpm_vendor_probe(unsigned bus, uint32_t addr) sw_run_duration = stopwatch_duration_msecs(&sw); break; } - udelay(SLEEP_DURATION); + udelay(SLEEP_DURATION_SAFE); } while (!stopwatch_expired(&sw)); printk(BIOS_INFO, @@ -526,8 +736,10 @@ int tpm_vendor_init(struct tpm_chip *chip, unsigned bus, uint32_t dev_addr) tpm_dev->chip_type = UNKNOWN; tpm_dev->bus = bus; tpm_dev->addr = dev_addr; - tpm_dev->sleep_short = SLEEP_DURATION; - tpm_dev->sleep_long = SLEEP_DURATION_LONG; + + /* Use conservative values to read chip id */ + tpm_dev->sleep_short = SLEEP_DURATION_SAFE; + tpm_dev->sleep_long = SLEEP_DURATION_SAFE * 2; memset(&chip->vendor, 0, sizeof(struct tpm_vendor_specific)); chip->is_open = 1; @@ -535,10 +747,6 @@ int tpm_vendor_init(struct tpm_chip *chip, unsigned bus, uint32_t dev_addr) chip->vendor.req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID; chip->vendor.req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID; chip->vendor.req_canceled = TPM_STS_COMMAND_READY; - chip->vendor.status = &tpm_tis_i2c_status; - chip->vendor.recv = &tpm_tis_i2c_recv; - chip->vendor.send = &tpm_tis_i2c_send; - chip->vendor.cancel = &tpm_tis_i2c_ready; /* Disable interrupts (not supported) */ chip->vendor.irq = 0; @@ -554,11 +762,27 @@ int tpm_vendor_init(struct tpm_chip *chip, unsigned bus, uint32_t dev_addr) tpm_dev->chip_type = SLB9645; } else if (be32_to_cpu(vendor) == TPM_TIS_I2C_DID_VID_9635) { tpm_dev->chip_type = SLB9635; + } else if (vendor == TPM_TIS_I2C_DID_VID_CR50) { + tpm_dev->chip_type = CR50; } else { printk(BIOS_DEBUG, "Vendor ID 0x%08x not recognized.\n", vendor); goto out_err; } + if (tpm_dev->chip_type == CR50) { + chip->vendor.status = &cr50_tis_i2c_status; + chip->vendor.recv = &cr50_tis_i2c_recv; + chip->vendor.send = &cr50_tis_i2c_send; + chip->vendor.cancel = &cr50_tis_i2c_ready; + } else { + tpm_dev->sleep_short = SLEEP_DURATION; + tpm_dev->sleep_long = SLEEP_DURATION_LONG; + chip->vendor.status = &tpm_tis_i2c_status; + chip->vendor.recv = &tpm_tis_i2c_recv; + chip->vendor.send = &tpm_tis_i2c_send; + chip->vendor.cancel = &tpm_tis_i2c_ready; + } + printk(BIOS_DEBUG, "I2C TPM %u:%02x (chip type %s device-id 0x%X)\n", tpm_dev->bus, tpm_dev->addr, chip_name[tpm_dev->chip_type], vendor >> 16); |