mail.coreboot.org
Sign In
Sign Up
Sign In
Sign Up
Manage this list
×
Keyboard Shortcuts
Thread View
j
: Next unread message
k
: Previous unread message
j a
: Jump to all threads
j l
: Jump to MailingList overview
2024
November
October
September
August
July
June
May
April
March
February
January
2023
December
November
October
September
August
July
June
May
April
March
February
January
2022
December
November
October
September
August
July
June
May
April
March
February
January
2021
December
November
October
September
August
July
June
May
April
March
February
January
2020
December
November
October
September
August
July
June
May
April
March
February
January
2019
December
November
October
September
August
July
June
May
April
March
February
January
2018
December
November
October
September
August
July
June
May
April
March
February
January
2017
December
November
October
September
August
July
June
May
April
March
February
January
2016
December
November
October
September
August
July
June
May
April
March
February
January
2015
December
November
October
September
August
July
June
May
April
March
February
January
2014
December
November
October
September
August
July
June
May
April
March
February
January
2013
December
November
October
September
August
July
June
May
April
March
List overview
Download
coreboot-gerrit
September 2016
----- 2024 -----
November 2024
October 2024
September 2024
August 2024
July 2024
June 2024
May 2024
April 2024
March 2024
February 2024
January 2024
----- 2023 -----
December 2023
November 2023
October 2023
September 2023
August 2023
July 2023
June 2023
May 2023
April 2023
March 2023
February 2023
January 2023
----- 2022 -----
December 2022
November 2022
October 2022
September 2022
August 2022
July 2022
June 2022
May 2022
April 2022
March 2022
February 2022
January 2022
----- 2021 -----
December 2021
November 2021
October 2021
September 2021
August 2021
July 2021
June 2021
May 2021
April 2021
March 2021
February 2021
January 2021
----- 2020 -----
December 2020
November 2020
October 2020
September 2020
August 2020
July 2020
June 2020
May 2020
April 2020
March 2020
February 2020
January 2020
----- 2019 -----
December 2019
November 2019
October 2019
September 2019
August 2019
July 2019
June 2019
May 2019
April 2019
March 2019
February 2019
January 2019
----- 2018 -----
December 2018
November 2018
October 2018
September 2018
August 2018
July 2018
June 2018
May 2018
April 2018
March 2018
February 2018
January 2018
----- 2017 -----
December 2017
November 2017
October 2017
September 2017
August 2017
July 2017
June 2017
May 2017
April 2017
March 2017
February 2017
January 2017
----- 2016 -----
December 2016
November 2016
October 2016
September 2016
August 2016
July 2016
June 2016
May 2016
April 2016
March 2016
February 2016
January 2016
----- 2015 -----
December 2015
November 2015
October 2015
September 2015
August 2015
July 2015
June 2015
May 2015
April 2015
March 2015
February 2015
January 2015
----- 2014 -----
December 2014
November 2014
October 2014
September 2014
August 2014
July 2014
June 2014
May 2014
April 2014
March 2014
February 2014
January 2014
----- 2013 -----
December 2013
November 2013
October 2013
September 2013
August 2013
July 2013
June 2013
May 2013
April 2013
March 2013
coreboot-gerrit@coreboot.org
1 participants
1340 discussions
Start a n
N
ew thread
Patch set updated for coreboot: drivers/i2c/tpm: Add support for cr50 TPM
by Duncan Laurie
02 Sep '16
02 Sep '16
Duncan Laurie (dlaurie(a)chromium.org) just uploaded a new patch set to gerrit, which you can find at
https://review.coreboot.org/16396
-gerrit commit 92be696950ab14b20e4ab5a174c5ea1ee362e938 Author: Duncan Laurie <dlaurie(a)chromium.org> Date: Thu Sep 1 15:50:22 2016 -0700 drivers/i2c/tpm: Add support for cr50 TPM Add support for the cr50 TPM used in apollolake chromebooks. This requires custom handling due to chip limitations, which may be revisited but are needed to get things working today. - timeouts need to be longer - must use the older style write+wait+read read protocol - all 4 bytes of status register must be read at once - same limitation applies when reading burst count from status reg - burst count max is 63 bytes, and burst count behaves slightly differently than other I2C TPMs - TPM expects the host to drain the full burst count (63 bytes) from the FIFO on a read Luckily the existing driver provides most abstraction needed to make this work seamlessly. To maximize code re-use the support for cr50 is added directly instead of as a separate driver and the style is kept similar to the rest of the driver code. This was tested with the cr50 TPM on a reef board with vboot use of TPM for secdata storage and factory initialization. Change-Id: I9b0bc282e41e779da8bf9184be0a11649735a101 Signed-off-by: Duncan Laurie <dlaurie(a)chromium.org> --- src/drivers/i2c/tpm/tpm.c | 202 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 191 insertions(+), 11 deletions(-) diff --git a/src/drivers/i2c/tpm/tpm.c b/src/drivers/i2c/tpm/tpm.c index c447001..0e0d531 100644 --- a/src/drivers/i2c/tpm/tpm.c +++ b/src/drivers/i2c/tpm/tpm.c @@ -36,6 +36,7 @@ #include <console/console.h> #include <device/i2c.h> #include <endian.h> +#include <timer.h> #include "tpm.h" /* Address of the TPM on the I2C bus */ @@ -44,6 +45,9 @@ /* max. number of iterations after I2C NAK */ #define MAX_COUNT 3 +/* cr50 max burst count */ +#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 */ @@ -58,16 +62,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", }; @@ -105,8 +112,11 @@ static int iic_tpm_read(uint8_t addr, uint8_t *buffer, size_t len) if (tpm_dev->bus < 0) return -1; - if ((tpm_dev->chip_type == SLB9635) || - (tpm_dev->chip_type == UNKNOWN)) { + + 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++) { rc = i2c_write_raw(tpm_dev->bus, tpm_dev->addr, @@ -132,7 +142,10 @@ static int iic_tpm_read(uint8_t addr, uint8_t *buffer, size_t len) break; /* success, break to skip sleep */ } - } else { + break; + + default: + { /* use a combined read for newer chips * unfortunately the smbus functions are not suitable due to * the 32 byte limit of the smbus. @@ -151,6 +164,7 @@ static int iic_tpm_read(uint8_t addr, uint8_t *buffer, size_t len) udelay(tpm_dev->sleep_short); } } + } /* take care of 'guard time' */ udelay(tpm_dev->sleep_short); @@ -486,6 +500,162 @@ out_err: return -1; } +/* + * cr50 is a TPM 2.0 capable device that requries special + * handling for the I2C interface. + */ + +/* 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) + 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 ssize_t cr50_get_burstcount(struct tpm_chip *chip) +{ + uint8_t buf[4]; + ssize_t burstcnt; + struct stopwatch sw; + + stopwatch_init_usecs_expire(&sw, 10 * SLEEP_DURATION_SAFE); + + while (!stopwatch_expired(&sw)) { + if (iic_tpm_read(TPM_STS(chip->vendor.locality), + buf, sizeof(buf)) != 0) + return -1; + + burstcnt = (buf[2] << 8) + buf[1]; + if (burstcnt > 0 && burstcnt <= CR50_MAX_BURSTCOUNT) + return burstcnt; + + udelay(SLEEP_DURATION_SAFE); + } + + return -1; +} + +static int cr50_tis_i2c_recv(struct tpm_chip *chip, uint8_t *buf, + size_t buf_len) +{ + uint32_t expected_buf; + ssize_t burstcnt; + size_t current, len, expected; + int status; + uint8_t addr = TPM_DATA_FIFO(chip->vendor.locality); + uint8_t extra[CR50_MAX_BURSTCOUNT]; + + if (buf_len < TPM_HEADER_SIZE) + goto out; + + burstcnt = cr50_get_burstcount(chip); + if (burstcnt <= 0 || burstcnt > buf_len) + goto out; + + /* Read first chunk of burstcnt bytes */ + if (iic_tpm_read(addr, buf, burstcnt) != 0) + goto out; + + memcpy(&expected_buf, buf + TPM_RSP_SIZE_BYTE, sizeof(expected_buf)); + expected = (size_t)be32_to_cpu(expected_buf); + if (expected > buf_len) + goto out; + + /* Now read the rest of the data */ + current = burstcnt; + while (current < expected) { + len = min(burstcnt, expected - current); + + if (iic_tpm_read(addr, buf + current, len) != 0) + goto out; + + current += len; + } + + /* cr50 expects the host to drain the FIFO */ + len = current % burstcnt; + if (len && iic_tpm_read(addr, extra, len) != 0) + goto out; + + wait_for_stat(chip, TPM_STS_VALID, &status); + if (status & TPM_STS_DATA_AVAIL) { + printk(BIOS_ERR, "TPM: Data Still Available\n"); + goto out; + } + + cr50_tis_i2c_ready(chip); + return current; + +out: + printk(BIOS_ERR, "%s failed\n", __func__); + cr50_tis_i2c_ready(chip); + return -1; +} + +static int cr50_tis_i2c_send(struct tpm_chip *chip, uint8_t *buf, size_t len) +{ + int status; + ssize_t burstcnt; + size_t sent = 0; + uint8_t tpm_go[4] = { TPM_STS_GO }; + + if (len > TPM_BUFSIZE) + return -1; + + /* Wait until TPM is ready for a command */ + status = chip->vendor.status(chip); + if ((status & TPM_STS_COMMAND_READY) == 0) { + cr50_tis_i2c_ready(chip); + if (wait_for_stat(chip, TPM_STS_COMMAND_READY, &status) < 0) { + printk(BIOS_ERR, "TPM: Command Ready Timeout\n"); + goto out; + } + } + + /* Get the burst count */ + burstcnt = cr50_get_burstcount(chip); + if (burstcnt < 0) + goto out; + + while (len > 0) { + ssize_t limit = min(burstcnt - 1, len); + + if (iic_tpm_write(TPM_DATA_FIFO(chip->vendor.locality), + &(buf[sent]), limit) != 0) + goto out; + + sent += limit; + len -= limit; + } + + /* Ensure TPM is not expecting more data */ + wait_for_stat(chip, TPM_STS_VALID, &status); + if ((status & TPM_STS_DATA_EXPECT) != 0) { + printk(BIOS_ERR, "TPM: Data Still Expected\n"); + goto out; + } + + /* Start the TPM command */ + iic_tpm_write(TPM_STS(chip->vendor.locality), tpm_go, sizeof(tpm_go)); + return sent; + +out: + printk(BIOS_ERR, "%s failed\n", __func__); + cr50_tis_i2c_ready(chip); + return -1; +} + /* Initialization of I2C TPM */ int tpm_vendor_init(struct tpm_chip *chip, unsigned bus, uint32_t dev_addr) @@ -506,10 +676,6 @@ int tpm_vendor_init(struct tpm_chip *chip, unsigned bus, uint32_t dev_addr) memset(&chip->vendor, 0, sizeof(struct tpm_vendor_specific)); chip->is_open = 1; - 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; 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; @@ -528,16 +694,30 @@ 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_release; } - tpm_dev->sleep_short = SLEEP_DURATION; - tpm_dev->sleep_long = SLEEP_DURATION_LONG; + 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, "1.2 TPM (chip type %s device-id 0x%X)\n", - chip_name[tpm_dev->chip_type], vendor >> 16); + 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); /* * A timeout query to TPM can be placed here.
1
0
0
0
Patch set updated for coreboot: drivers/i2c/tpm: Allow sleep durations to be set by the chip
by Duncan Laurie
02 Sep '16
02 Sep '16
Duncan Laurie (dlaurie(a)chromium.org) just uploaded a new patch set to gerrit, which you can find at
https://review.coreboot.org/16395
-gerrit commit 5255ce860837d97e383b1543d76839613d2d7344 Author: Duncan Laurie <dlaurie(a)chromium.org> Date: Wed Aug 31 14:48:12 2016 -0700 drivers/i2c/tpm: Allow sleep durations to be set by the chip Allow the sleep durations used by the driver to be set by the specific chip so they can be tuned appropriately. Since we need to read the chip id to know the values use very conservative defaults for the first command and then set it to the current values by default. Change-Id: Ic64159328b18a1471eb06fa8b52b589eec1e1ca2 Signed-off-by: Duncan Laurie <dlaurie(a)chromium.org> --- src/drivers/i2c/tpm/tpm.c | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/src/drivers/i2c/tpm/tpm.c b/src/drivers/i2c/tpm/tpm.c index 426bc7c..c447001 100644 --- a/src/drivers/i2c/tpm/tpm.c +++ b/src/drivers/i2c/tpm/tpm.c @@ -45,6 +45,8 @@ #define MAX_COUNT 3 #define SLEEP_DURATION 60 /* in usec */ +#define SLEEP_DURATION_LONG 210 /* in usec */ +#define SLEEP_DURATION_SAFE 750 /* in usec */ /* max. number of iterations after I2C NAK for 'long' commands * we need this especially for sending TPM_READY, since the cleanup after the @@ -53,8 +55,6 @@ */ #define MAX_COUNT_LONG 50 -#define SLEEP_DURATION_LONG 210 /* in usec */ - /* expected value for DIDVID register */ #define TPM_TIS_I2C_DID_VID_9635 0x000b15d1L #define TPM_TIS_I2C_DID_VID_9645 0x001a15d1L @@ -75,6 +75,8 @@ static const char * const chip_name[] = { struct tpm_inf_dev { int bus; unsigned int addr; + unsigned int sleep_short; /* Short sleep duration in usec */ + unsigned int sleep_long; /* Long sleep duration in usec */ uint8_t buf[TPM_BUFSIZE + sizeof(uint8_t)]; // max. buffer size + addr enum i2c_chip_type chip_type; }; @@ -112,7 +114,7 @@ static int iic_tpm_read(uint8_t addr, uint8_t *buffer, size_t len) if (rc == 0) break; /* success, break to skip sleep */ - udelay(SLEEP_DURATION); + udelay(tpm_dev->sleep_short); } if (rc) @@ -123,7 +125,7 @@ static int iic_tpm_read(uint8_t addr, uint8_t *buffer, size_t len) * retrieving the data */ for (count = 0; count < MAX_COUNT; count++) { - udelay(SLEEP_DURATION); + udelay(tpm_dev->sleep_short); rc = i2c_read_raw(tpm_dev->bus, tpm_dev->addr, buffer, len); if (rc == 0) @@ -146,12 +148,12 @@ static int iic_tpm_read(uint8_t addr, uint8_t *buffer, size_t len) i2c_transfer(tpm_dev->bus, &dseg, 1); if (rc == 0) break; /* break here to skip sleep */ - udelay(SLEEP_DURATION); + udelay(tpm_dev->sleep_short); } } /* take care of 'guard time' */ - udelay(SLEEP_DURATION); + udelay(tpm_dev->sleep_short); if (rc) return -1; @@ -187,7 +189,7 @@ static int iic_tpm_write_generic(uint8_t addr, uint8_t *buffer, size_t len, } /* take care of 'guard time' */ - udelay(SLEEP_DURATION); + udelay(tpm_dev->sleep_short); if (rc) return -1; @@ -212,7 +214,8 @@ static int iic_tpm_write_generic(uint8_t addr, uint8_t *buffer, size_t len, */ static int iic_tpm_write(uint8_t addr, uint8_t *buffer, size_t len) { - return iic_tpm_write_generic(addr, buffer, len, SLEEP_DURATION, + struct tpm_inf_dev *tpm_dev = car_get_var_ptr(&g_tpm_dev); + return iic_tpm_write_generic(addr, buffer, len, tpm_dev->sleep_short, MAX_COUNT); } @@ -222,7 +225,8 @@ static int iic_tpm_write(uint8_t addr, uint8_t *buffer, size_t len) * */ static int iic_tpm_write_long(uint8_t addr, uint8_t *buffer, size_t len) { - return iic_tpm_write_generic(addr, buffer, len, SLEEP_DURATION_LONG, + struct tpm_inf_dev *tpm_dev = car_get_var_ptr(&g_tpm_dev); + return iic_tpm_write_generic(addr, buffer, len, tpm_dev->sleep_long, MAX_COUNT_LONG); } @@ -495,6 +499,10 @@ int tpm_vendor_init(struct tpm_chip *chip, unsigned bus, uint32_t dev_addr) tpm_dev->addr = dev_addr; tpm_dev->bus = bus; + /* 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; @@ -525,6 +533,9 @@ int tpm_vendor_init(struct tpm_chip *chip, unsigned bus, uint32_t dev_addr) goto out_release; } + tpm_dev->sleep_short = SLEEP_DURATION; + tpm_dev->sleep_long = SLEEP_DURATION_LONG; + printk(BIOS_DEBUG, "1.2 TPM (chip type %s device-id 0x%X)\n", chip_name[tpm_dev->chip_type], vendor >> 16);
1
0
0
0
Patch set updated for coreboot: google/reef: Fix indent in devicetree.cb
by Duncan Laurie
02 Sep '16
02 Sep '16
Duncan Laurie (dlaurie(a)chromium.org) just uploaded a new patch set to gerrit, which you can find at
https://review.coreboot.org/16399
-gerrit commit f115d1a8ab7a260c9c5c39ad23db55c8f1b7d325 Author: Duncan Laurie <dlaurie(a)chromium.org> Date: Thu Sep 1 16:00:39 2016 -0700 google/reef: Fix indent in devicetree.cb Indent the I2C device for touchscreen with tabs so it aligns properly. Change-Id: Id9b2d26a4acdd6fe6c69055907258df3cc035b31 Signed-off-by: Duncan Laurie <dlaurie(a)chromium.org> --- src/mainboard/google/reef/devicetree.cb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/mainboard/google/reef/devicetree.cb b/src/mainboard/google/reef/devicetree.cb index ac69406..9a7c2d1 100644 --- a/src/mainboard/google/reef/devicetree.cb +++ b/src/mainboard/google/reef/devicetree.cb @@ -108,10 +108,10 @@ chip soc/intel/apollolake end # - I2C 2 device pci 16.3 on chip drivers/i2c/generic - register "hid" = ""ELAN0001"" - register "desc" = ""ELAN Touchscreen"" - register "irq" = "IRQ_EDGE_LOW(GPIO_21_IRQ)" - device i2c 10 on end + register "hid" = ""ELAN0001"" + register "desc" = ""ELAN Touchscreen"" + register "irq" = "IRQ_EDGE_LOW(GPIO_21_IRQ)" + device i2c 10 on end end end # - I2C 3 device pci 17.0 on
1
0
0
0
Patch set updated for coreboot: google/reef: Enable I2C TPM
by Duncan Laurie
02 Sep '16
02 Sep '16
Duncan Laurie (dlaurie(a)chromium.org) just uploaded a new patch set to gerrit, which you can find at
https://review.coreboot.org/16398
-gerrit commit 2adab19c75b74f8175bc9c470b05e3d420cd3f84 Author: Duncan Laurie <dlaurie(a)chromium.org> Date: Thu Sep 1 16:00:05 2016 -0700 google/reef: Enable I2C TPM Enable the I2C based TPM on the reef board at bus 2 and address 0x50. This makes vboot functional without needing MOCK_TPM and results in the following in the SSDT: Device (TPMI) { Name (_HID, "GOOG0005") // _HID: Hardware ID Name (_UID, Zero) // _UID: Unique ID Name (_DDN, "I2C TPM") // _DDN: DOS Device Name Method (_STA, 0, NotSerialized) // _STA: Status { Return (0x0F) } Name (_CRS, ResourceTemplate () // _CRS: Current Resource Settings { I2cSerialBus (0x0050, ControllerInitiated, 0x00061A80, AddressingMode7Bit, "\\_SB.PCI0.I2C2", 0x00, ResourceConsumer) Interrupt (ResourceConsumer, Edge, ActiveLow, Exclusive) { 0x00000039 } }) } Change-Id: Ia9775caabeac3e6a3bd72de38f9611b4cea7cea4 Signed-off-by: Duncan Laurie <dlaurie(a)chromium.org> --- src/mainboard/google/reef/Kconfig | 10 +++++++++- src/mainboard/google/reef/devicetree.cb | 8 +++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/mainboard/google/reef/Kconfig b/src/mainboard/google/reef/Kconfig index 0464e2d..08a5c6a 100644 --- a/src/mainboard/google/reef/Kconfig +++ b/src/mainboard/google/reef/Kconfig @@ -9,8 +9,16 @@ config BOARD_SPECIFIC_OPTIONS select HAVE_ACPI_RESUME select HAVE_ACPI_TABLES select MAINBOARD_HAS_CHROMEOS - select MAINBOARD_HAS_LPC_TPM + select MAINBOARD_HAS_TPM2 select SYSTEM_TYPE_LAPTOP + select TPM2 + select I2C_TPM + +config DRIVER_TPM_I2C_BUS + hex "0x2" + +config DRIVER_TPM_I2C_ADDR + hex "0x50" config CHROMEOS select LID_SWITCH diff --git a/src/mainboard/google/reef/devicetree.cb b/src/mainboard/google/reef/devicetree.cb index 4d08fae..ac69406 100644 --- a/src/mainboard/google/reef/devicetree.cb +++ b/src/mainboard/google/reef/devicetree.cb @@ -99,7 +99,13 @@ chip soc/intel/apollolake end end device pci 16.1 on end # - I2C 1 - device pci 16.2 on end # - I2C 2 + device pci 16.2 on + chip drivers/i2c/tpm + register "hid" = ""GOOG0005"" + register "irq" = "IRQ_EDGE_LOW(GPIO_28_IRQ)" + device i2c 50 on end + end + end # - I2C 2 device pci 16.3 on chip drivers/i2c/generic register "hid" = ""ELAN0001""
1
0
0
0
Patch set updated for coreboot: drivers/i2c/tpm: Add support for generating ACPI table
by Duncan Laurie
02 Sep '16
02 Sep '16
Duncan Laurie (dlaurie(a)chromium.org) just uploaded a new patch set to gerrit, which you can find at
https://review.coreboot.org/16397
-gerrit commit 5ecc29ff6defacec3af9ba5e251a9f57149c8bab Author: Duncan Laurie <dlaurie(a)chromium.org> Date: Thu Sep 1 15:56:44 2016 -0700 drivers/i2c/tpm: Add support for generating ACPI table Add code to generate an ACPI descriptor for an I2C TPM based on the device as described in devicetree.cb. This currently requires the devicetree to provide the HID, since we don't currently talk to the TPM in ramstage and I didn't want to add yet another init path for it here. This was tested on a reef board to ensure that the device is described properly in the SSDT. Change-Id: I43d7f6192f48e99a4074baa4e52f0a9ee554a250 Signed-off-by: Duncan Laurie <dlaurie(a)chromium.org> --- src/drivers/i2c/tpm/Makefile.inc | 2 + src/drivers/i2c/tpm/chip.c | 93 ++++++++++++++++++++++++++++++++++++++++ src/drivers/i2c/tpm/chip.h | 10 +++++ 3 files changed, 105 insertions(+) diff --git a/src/drivers/i2c/tpm/Makefile.inc b/src/drivers/i2c/tpm/Makefile.inc index 4f5913f..dc4593b 100644 --- a/src/drivers/i2c/tpm/Makefile.inc +++ b/src/drivers/i2c/tpm/Makefile.inc @@ -2,3 +2,5 @@ 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_I2C_TPM) += chip.c diff --git a/src/drivers/i2c/tpm/chip.c b/src/drivers/i2c/tpm/chip.c new file mode 100644 index 0000000..7e96149 --- /dev/null +++ b/src/drivers/i2c/tpm/chip.c @@ -0,0 +1,93 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2016 Google Inc. + * + * 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. + */ + +#include <arch/acpi_device.h> +#include <arch/acpigen.h> +#include <console/console.h> +#include <device/i2c.h> +#include <device/device.h> +#include <device/path.h> +#include <stdint.h> +#include <string.h> +#include "tpm.h" +#include "chip.h" + +#if IS_ENABLED(CONFIG_HAVE_ACPI_TABLES) +static void i2c_tpm_fill_ssdt(struct device *dev) +{ + struct drivers_i2c_tpm_config *config = dev->chip_info; + const char *scope = acpi_device_scope(dev); + struct acpi_i2c i2c = { + .address = dev->path.i2c.device, + .mode_10bit = dev->path.i2c.mode_10bit, + .speed = config->speed ? : I2C_SPEED_FAST, + .resource = scope, + }; + + if (!dev->enabled || !scope) + return; + + if (!config->hid) { + printk(BIOS_ERR, "%s: ERROR: HID required\n", dev_path(dev)); + return; + } + + /* Device */ + acpigen_write_scope(scope); + acpigen_write_device(acpi_device_name(dev)); + acpigen_write_name_string("_HID", config->hid); + acpigen_write_name_integer("_UID", config->uid); + acpigen_write_name_string("_DDN", dev->chip_ops->name); + acpigen_write_STA(ACPI_STATUS_DEVICE_ALL_ON); + + /* Resources */ + acpigen_write_name("_CRS"); + acpigen_write_resourcetemplate_header(); + acpi_device_write_i2c(&i2c); + acpi_device_write_interrupt(&config->irq); + acpigen_write_resourcetemplate_footer(); + + acpigen_pop_len(); /* Device */ + acpigen_pop_len(); /* Scope */ + + printk(BIOS_INFO, "%s: %s at %s\n", acpi_device_path(dev), + dev->chip_ops->name, dev_path(dev)); +} + +static const char *i2c_tpm_acpi_name(struct device *dev) +{ + return "TPMI"; +} +#endif + +static struct device_operations i2c_tpm_ops = { + .read_resources = DEVICE_NOOP, + .set_resources = DEVICE_NOOP, + .enable_resources = DEVICE_NOOP, +#if IS_ENABLED(CONFIG_HAVE_ACPI_TABLES) + .acpi_name = &i2c_tpm_acpi_name, + .acpi_fill_ssdt_generator = &i2c_tpm_fill_ssdt, +#endif +}; + +static void i2c_tpm_enable(struct device *dev) +{ + dev->ops = &i2c_tpm_ops; +} + +struct chip_operations drivers_i2c_tpm_ops = { + CHIP_NAME("I2C TPM") + .enable_dev = &i2c_tpm_enable +}; diff --git a/src/drivers/i2c/tpm/chip.h b/src/drivers/i2c/tpm/chip.h new file mode 100644 index 0000000..14103c7 --- /dev/null +++ b/src/drivers/i2c/tpm/chip.h @@ -0,0 +1,10 @@ +#include <arch/acpi_device.h> +#include <device/i2c.h> + +struct drivers_i2c_tpm_config { + const char *hid; /* ACPI _HID (required) */ + const char *desc; /* Device Description */ + unsigned uid; /* ACPI _UID */ + enum i2c_speed speed; /* Bus speed in Hz, default is I2C_SPEED_FAST */ + struct acpi_irq irq; /* Interrupt */ +};
1
0
0
0
Patch set updated for coreboot: drivers/i2c/tpm: Add support for cr50 TPM
by Duncan Laurie
02 Sep '16
02 Sep '16
Duncan Laurie (dlaurie(a)chromium.org) just uploaded a new patch set to gerrit, which you can find at
https://review.coreboot.org/16396
-gerrit commit f5aa5e73a71bda683dd94a275a5b5ffa6b819893 Author: Duncan Laurie <dlaurie(a)chromium.org> Date: Thu Sep 1 15:50:22 2016 -0700 drivers/i2c/tpm: Add support for cr50 TPM Add support for the cr50 TPM used in apollolake chromebooks. This requires custom handling due to chip limitations, which may be revisited but are needed to get things working today. - timeouts need to be longer - must use the older style write+wait+read read protocol - all 4 bytes of status register must be read at once - same limitation applies when reading burst count from status reg - burst count max is 63 bytes, and burst count behaves slightly differently than other I2C TPMs - TPM expects the host to drain the full burst count (63 bytes) from the FIFO on a read Luckily the existing driver provides most abstraction needed to make this work seamlessly. To maximize code re-use the support for cr50 is added directly instead of as a separate driver and the style is kept similar to the rest of the driver code. This was tested with the cr50 TPM on a reef board with vboot use of TPM for secdata storage and factory initialization. Change-Id: I9b0bc282e41e779da8bf9184be0a11649735a101 Signed-off-by: Duncan Laurie <dlaurie(a)chromium.org> --- src/drivers/i2c/tpm/tpm.c | 202 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 191 insertions(+), 11 deletions(-) diff --git a/src/drivers/i2c/tpm/tpm.c b/src/drivers/i2c/tpm/tpm.c index c447001..0e0d531 100644 --- a/src/drivers/i2c/tpm/tpm.c +++ b/src/drivers/i2c/tpm/tpm.c @@ -36,6 +36,7 @@ #include <console/console.h> #include <device/i2c.h> #include <endian.h> +#include <timer.h> #include "tpm.h" /* Address of the TPM on the I2C bus */ @@ -44,6 +45,9 @@ /* max. number of iterations after I2C NAK */ #define MAX_COUNT 3 +/* cr50 max burst count */ +#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 */ @@ -58,16 +62,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", }; @@ -105,8 +112,11 @@ static int iic_tpm_read(uint8_t addr, uint8_t *buffer, size_t len) if (tpm_dev->bus < 0) return -1; - if ((tpm_dev->chip_type == SLB9635) || - (tpm_dev->chip_type == UNKNOWN)) { + + 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++) { rc = i2c_write_raw(tpm_dev->bus, tpm_dev->addr, @@ -132,7 +142,10 @@ static int iic_tpm_read(uint8_t addr, uint8_t *buffer, size_t len) break; /* success, break to skip sleep */ } - } else { + break; + + default: + { /* use a combined read for newer chips * unfortunately the smbus functions are not suitable due to * the 32 byte limit of the smbus. @@ -151,6 +164,7 @@ static int iic_tpm_read(uint8_t addr, uint8_t *buffer, size_t len) udelay(tpm_dev->sleep_short); } } + } /* take care of 'guard time' */ udelay(tpm_dev->sleep_short); @@ -486,6 +500,162 @@ out_err: return -1; } +/* + * cr50 is a TPM 2.0 capable device that requries special + * handling for the I2C interface. + */ + +/* 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) + 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 ssize_t cr50_get_burstcount(struct tpm_chip *chip) +{ + uint8_t buf[4]; + ssize_t burstcnt; + struct stopwatch sw; + + stopwatch_init_usecs_expire(&sw, 10 * SLEEP_DURATION_SAFE); + + while (!stopwatch_expired(&sw)) { + if (iic_tpm_read(TPM_STS(chip->vendor.locality), + buf, sizeof(buf)) != 0) + return -1; + + burstcnt = (buf[2] << 8) + buf[1]; + if (burstcnt > 0 && burstcnt <= CR50_MAX_BURSTCOUNT) + return burstcnt; + + udelay(SLEEP_DURATION_SAFE); + } + + return -1; +} + +static int cr50_tis_i2c_recv(struct tpm_chip *chip, uint8_t *buf, + size_t buf_len) +{ + uint32_t expected_buf; + ssize_t burstcnt; + size_t current, len, expected; + int status; + uint8_t addr = TPM_DATA_FIFO(chip->vendor.locality); + uint8_t extra[CR50_MAX_BURSTCOUNT]; + + if (buf_len < TPM_HEADER_SIZE) + goto out; + + burstcnt = cr50_get_burstcount(chip); + if (burstcnt <= 0 || burstcnt > buf_len) + goto out; + + /* Read first chunk of burstcnt bytes */ + if (iic_tpm_read(addr, buf, burstcnt) != 0) + goto out; + + memcpy(&expected_buf, buf + TPM_RSP_SIZE_BYTE, sizeof(expected_buf)); + expected = (size_t)be32_to_cpu(expected_buf); + if (expected > buf_len) + goto out; + + /* Now read the rest of the data */ + current = burstcnt; + while (current < expected) { + len = min(burstcnt, expected - current); + + if (iic_tpm_read(addr, buf + current, len) != 0) + goto out; + + current += len; + } + + /* cr50 expects the host to drain the FIFO */ + len = current % burstcnt; + if (len && iic_tpm_read(addr, extra, len) != 0) + goto out; + + wait_for_stat(chip, TPM_STS_VALID, &status); + if (status & TPM_STS_DATA_AVAIL) { + printk(BIOS_ERR, "TPM: Data Still Available\n"); + goto out; + } + + cr50_tis_i2c_ready(chip); + return current; + +out: + printk(BIOS_ERR, "%s failed\n", __func__); + cr50_tis_i2c_ready(chip); + return -1; +} + +static int cr50_tis_i2c_send(struct tpm_chip *chip, uint8_t *buf, size_t len) +{ + int status; + ssize_t burstcnt; + size_t sent = 0; + uint8_t tpm_go[4] = { TPM_STS_GO }; + + if (len > TPM_BUFSIZE) + return -1; + + /* Wait until TPM is ready for a command */ + status = chip->vendor.status(chip); + if ((status & TPM_STS_COMMAND_READY) == 0) { + cr50_tis_i2c_ready(chip); + if (wait_for_stat(chip, TPM_STS_COMMAND_READY, &status) < 0) { + printk(BIOS_ERR, "TPM: Command Ready Timeout\n"); + goto out; + } + } + + /* Get the burst count */ + burstcnt = cr50_get_burstcount(chip); + if (burstcnt < 0) + goto out; + + while (len > 0) { + ssize_t limit = min(burstcnt - 1, len); + + if (iic_tpm_write(TPM_DATA_FIFO(chip->vendor.locality), + &(buf[sent]), limit) != 0) + goto out; + + sent += limit; + len -= limit; + } + + /* Ensure TPM is not expecting more data */ + wait_for_stat(chip, TPM_STS_VALID, &status); + if ((status & TPM_STS_DATA_EXPECT) != 0) { + printk(BIOS_ERR, "TPM: Data Still Expected\n"); + goto out; + } + + /* Start the TPM command */ + iic_tpm_write(TPM_STS(chip->vendor.locality), tpm_go, sizeof(tpm_go)); + return sent; + +out: + printk(BIOS_ERR, "%s failed\n", __func__); + cr50_tis_i2c_ready(chip); + return -1; +} + /* Initialization of I2C TPM */ int tpm_vendor_init(struct tpm_chip *chip, unsigned bus, uint32_t dev_addr) @@ -506,10 +676,6 @@ int tpm_vendor_init(struct tpm_chip *chip, unsigned bus, uint32_t dev_addr) memset(&chip->vendor, 0, sizeof(struct tpm_vendor_specific)); chip->is_open = 1; - 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; 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; @@ -528,16 +694,30 @@ 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_release; } - tpm_dev->sleep_short = SLEEP_DURATION; - tpm_dev->sleep_long = SLEEP_DURATION_LONG; + 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, "1.2 TPM (chip type %s device-id 0x%X)\n", - chip_name[tpm_dev->chip_type], vendor >> 16); + 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); /* * A timeout query to TPM can be placed here.
1
0
0
0
New patch to review for coreboot: google/reef: Fix indent in devicetree.cb
by Duncan Laurie
02 Sep '16
02 Sep '16
Duncan Laurie (dlaurie(a)chromium.org) just uploaded a new patch set to gerrit, which you can find at
https://review.coreboot.org/16399
-gerrit commit 4fa8c0d7f7451e374a0776dff60c38d25f8871b9 Author: Duncan Laurie <dlaurie(a)chromium.org> Date: Thu Sep 1 16:00:39 2016 -0700 google/reef: Fix indent in devicetree.cb Indent the I2C device for touchscreen with tabs so it aligns properly. Change-Id: Id9b2d26a4acdd6fe6c69055907258df3cc035b31 Signed-off-by: Duncan Laurie <dlaurie(a)chromium.org> --- src/mainboard/google/reef/devicetree.cb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/mainboard/google/reef/devicetree.cb b/src/mainboard/google/reef/devicetree.cb index ac69406..9a7c2d1 100644 --- a/src/mainboard/google/reef/devicetree.cb +++ b/src/mainboard/google/reef/devicetree.cb @@ -108,10 +108,10 @@ chip soc/intel/apollolake end # - I2C 2 device pci 16.3 on chip drivers/i2c/generic - register "hid" = ""ELAN0001"" - register "desc" = ""ELAN Touchscreen"" - register "irq" = "IRQ_EDGE_LOW(GPIO_21_IRQ)" - device i2c 10 on end + register "hid" = ""ELAN0001"" + register "desc" = ""ELAN Touchscreen"" + register "irq" = "IRQ_EDGE_LOW(GPIO_21_IRQ)" + device i2c 10 on end end end # - I2C 3 device pci 17.0 on
1
0
0
0
New patch to review for coreboot: google/reef: Enable I2C TPM
by Duncan Laurie
02 Sep '16
02 Sep '16
Duncan Laurie (dlaurie(a)chromium.org) just uploaded a new patch set to gerrit, which you can find at
https://review.coreboot.org/16398
-gerrit commit 126cc520d48b3bdfc3f8943e1c50ec3fbb8a1941 Author: Duncan Laurie <dlaurie(a)chromium.org> Date: Thu Sep 1 16:00:05 2016 -0700 google/reef: Enable I2C TPM Enable the I2C based TPM on the reef board at bus 2 and address 0x50. This makes vboot functional without needing MOCK_TPM and results in the following in the SSDT: Device (TPMI) { Name (_HID, "GOOG0005") // _HID: Hardware ID Name (_UID, Zero) // _UID: Unique ID Name (_DDN, "I2C TPM") // _DDN: DOS Device Name Method (_STA, 0, NotSerialized) // _STA: Status { Return (0x0F) } Name (_CRS, ResourceTemplate () // _CRS: Current Resource Settings { I2cSerialBus (0x0050, ControllerInitiated, 0x00061A80, AddressingMode7Bit, "\\_SB.PCI0.I2C2", 0x00, ResourceConsumer) Interrupt (ResourceConsumer, Edge, ActiveLow, Exclusive) { 0x00000039 } }) } Change-Id: Ia9775caabeac3e6a3bd72de38f9611b4cea7cea4 Signed-off-by: Duncan Laurie <dlaurie(a)chromium.org> --- src/mainboard/google/reef/Kconfig | 10 +++++++++- src/mainboard/google/reef/devicetree.cb | 8 +++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/mainboard/google/reef/Kconfig b/src/mainboard/google/reef/Kconfig index 0464e2d..08a5c6a 100644 --- a/src/mainboard/google/reef/Kconfig +++ b/src/mainboard/google/reef/Kconfig @@ -9,8 +9,16 @@ config BOARD_SPECIFIC_OPTIONS select HAVE_ACPI_RESUME select HAVE_ACPI_TABLES select MAINBOARD_HAS_CHROMEOS - select MAINBOARD_HAS_LPC_TPM + select MAINBOARD_HAS_TPM2 select SYSTEM_TYPE_LAPTOP + select TPM2 + select I2C_TPM + +config DRIVER_TPM_I2C_BUS + hex "0x2" + +config DRIVER_TPM_I2C_ADDR + hex "0x50" config CHROMEOS select LID_SWITCH diff --git a/src/mainboard/google/reef/devicetree.cb b/src/mainboard/google/reef/devicetree.cb index 4d08fae..ac69406 100644 --- a/src/mainboard/google/reef/devicetree.cb +++ b/src/mainboard/google/reef/devicetree.cb @@ -99,7 +99,13 @@ chip soc/intel/apollolake end end device pci 16.1 on end # - I2C 1 - device pci 16.2 on end # - I2C 2 + device pci 16.2 on + chip drivers/i2c/tpm + register "hid" = ""GOOG0005"" + register "irq" = "IRQ_EDGE_LOW(GPIO_28_IRQ)" + device i2c 50 on end + end + end # - I2C 2 device pci 16.3 on chip drivers/i2c/generic register "hid" = ""ELAN0001""
1
0
0
0
New patch to review for coreboot: drivers/i2c/tpm: Add support for generating ACPI table
by Duncan Laurie
02 Sep '16
02 Sep '16
Duncan Laurie (dlaurie(a)chromium.org) just uploaded a new patch set to gerrit, which you can find at
https://review.coreboot.org/16397
-gerrit commit 623184c95e286ae42dc84621d97ea1e692963530 Author: Duncan Laurie <dlaurie(a)chromium.org> Date: Thu Sep 1 15:56:44 2016 -0700 drivers/i2c/tpm: Add support for generating ACPI table Add code to generate an ACPI descriptor for an I2C TPM based on the device as described in devicetree.cb. This currently requires the devicetree to provide the HID, since we don't currently talk to the TPM in ramstage and I didn't want to add yet another init path for it here. This was tested on a reef board to ensure that the device is described properly in the SSDT. Change-Id: I43d7f6192f48e99a4074baa4e52f0a9ee554a250 Signed-off-by: Duncan Laurie <dlaurie(a)chromium.org> --- src/drivers/i2c/tpm/Makefile.inc | 2 + src/drivers/i2c/tpm/chip.c | 93 ++++++++++++++++++++++++++++++++++++++++ src/drivers/i2c/tpm/chip.h | 10 +++++ 3 files changed, 105 insertions(+) diff --git a/src/drivers/i2c/tpm/Makefile.inc b/src/drivers/i2c/tpm/Makefile.inc index 4f5913f..dc4593b 100644 --- a/src/drivers/i2c/tpm/Makefile.inc +++ b/src/drivers/i2c/tpm/Makefile.inc @@ -2,3 +2,5 @@ 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_I2C_TPM) += chip.c diff --git a/src/drivers/i2c/tpm/chip.c b/src/drivers/i2c/tpm/chip.c new file mode 100644 index 0000000..7e96149 --- /dev/null +++ b/src/drivers/i2c/tpm/chip.c @@ -0,0 +1,93 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2016 Google Inc. + * + * 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. + */ + +#include <arch/acpi_device.h> +#include <arch/acpigen.h> +#include <console/console.h> +#include <device/i2c.h> +#include <device/device.h> +#include <device/path.h> +#include <stdint.h> +#include <string.h> +#include "tpm.h" +#include "chip.h" + +#if IS_ENABLED(CONFIG_HAVE_ACPI_TABLES) +static void i2c_tpm_fill_ssdt(struct device *dev) +{ + struct drivers_i2c_tpm_config *config = dev->chip_info; + const char *scope = acpi_device_scope(dev); + struct acpi_i2c i2c = { + .address = dev->path.i2c.device, + .mode_10bit = dev->path.i2c.mode_10bit, + .speed = config->speed ? : I2C_SPEED_FAST, + .resource = scope, + }; + + if (!dev->enabled || !scope) + return; + + if (!config->hid) { + printk(BIOS_ERR, "%s: ERROR: HID required\n", dev_path(dev)); + return; + } + + /* Device */ + acpigen_write_scope(scope); + acpigen_write_device(acpi_device_name(dev)); + acpigen_write_name_string("_HID", config->hid); + acpigen_write_name_integer("_UID", config->uid); + acpigen_write_name_string("_DDN", dev->chip_ops->name); + acpigen_write_STA(ACPI_STATUS_DEVICE_ALL_ON); + + /* Resources */ + acpigen_write_name("_CRS"); + acpigen_write_resourcetemplate_header(); + acpi_device_write_i2c(&i2c); + acpi_device_write_interrupt(&config->irq); + acpigen_write_resourcetemplate_footer(); + + acpigen_pop_len(); /* Device */ + acpigen_pop_len(); /* Scope */ + + printk(BIOS_INFO, "%s: %s at %s\n", acpi_device_path(dev), + dev->chip_ops->name, dev_path(dev)); +} + +static const char *i2c_tpm_acpi_name(struct device *dev) +{ + return "TPMI"; +} +#endif + +static struct device_operations i2c_tpm_ops = { + .read_resources = DEVICE_NOOP, + .set_resources = DEVICE_NOOP, + .enable_resources = DEVICE_NOOP, +#if IS_ENABLED(CONFIG_HAVE_ACPI_TABLES) + .acpi_name = &i2c_tpm_acpi_name, + .acpi_fill_ssdt_generator = &i2c_tpm_fill_ssdt, +#endif +}; + +static void i2c_tpm_enable(struct device *dev) +{ + dev->ops = &i2c_tpm_ops; +} + +struct chip_operations drivers_i2c_tpm_ops = { + CHIP_NAME("I2C TPM") + .enable_dev = &i2c_tpm_enable +}; diff --git a/src/drivers/i2c/tpm/chip.h b/src/drivers/i2c/tpm/chip.h new file mode 100644 index 0000000..14103c7 --- /dev/null +++ b/src/drivers/i2c/tpm/chip.h @@ -0,0 +1,10 @@ +#include <arch/acpi_device.h> +#include <device/i2c.h> + +struct drivers_i2c_tpm_config { + const char *hid; /* ACPI _HID (required) */ + const char *desc; /* Device Description */ + unsigned uid; /* ACPI _UID */ + enum i2c_speed speed; /* Bus speed in Hz, default is I2C_SPEED_FAST */ + struct acpi_irq irq; /* Interrupt */ +};
1
0
0
0
New patch to review for coreboot: drivers/i2c/tpm: Add support for cr50 TPM
by Duncan Laurie
02 Sep '16
02 Sep '16
Duncan Laurie (dlaurie(a)chromium.org) just uploaded a new patch set to gerrit, which you can find at
https://review.coreboot.org/16396
-gerrit commit e86c026916e2d1e7b8c8d6b358a316df646e1200 Author: Duncan Laurie <dlaurie(a)chromium.org> Date: Thu Sep 1 15:50:22 2016 -0700 drivers/i2c/tpm: Add support for cr50 TPM Add support for the cr50 TPM used in apollolake chromebooks. This requires custom handling due to chip limitations, which may be revisited but are needed to get things working today. - timeouts need to be longer - must use the older style write+wait+read read protocol - all 4 bytes of status register must be read at once - same limitation applies when reading burst count from status reg - burst count max is 63 bytes, and burst count behaves slightly differently than other I2C TPMs - TPM expects the host to drain the full burst count (63 bytes) from the FIFO on a read Luckily the existing driver provides most abstraction needed to make this work seamlessly. To maximize code re-use the support for cr50 is added directly instead of as a separate driver and the style is kept similar to the rest of the driver code. This was tested with the cr50 TPM on a reef board with vboot use of TPM for secdata storage and factory initialization. Change-Id: I9b0bc282e41e779da8bf9184be0a11649735a101 Signed-off-by: Duncan Laurie <dlaurie(a)chromium.org> --- src/drivers/i2c/tpm/tpm.c | 204 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 192 insertions(+), 12 deletions(-) diff --git a/src/drivers/i2c/tpm/tpm.c b/src/drivers/i2c/tpm/tpm.c index 0f8a278..0e0d531 100644 --- a/src/drivers/i2c/tpm/tpm.c +++ b/src/drivers/i2c/tpm/tpm.c @@ -36,6 +36,7 @@ #include <console/console.h> #include <device/i2c.h> #include <endian.h> +#include <timer.h> #include "tpm.h" /* Address of the TPM on the I2C bus */ @@ -44,6 +45,9 @@ /* max. number of iterations after I2C NAK */ #define MAX_COUNT 3 +/* cr50 max burst count */ +#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 */ @@ -58,16 +62,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", }; @@ -105,8 +112,11 @@ static int iic_tpm_read(uint8_t addr, uint8_t *buffer, size_t len) if (tpm_dev->bus < 0) return -1; - if ((tpm_dev->chip_type == SLB9635) || - (tpm_dev->chip_type == UNKNOWN)) { + + 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++) { rc = i2c_write_raw(tpm_dev->bus, tpm_dev->addr, @@ -125,14 +135,17 @@ static int iic_tpm_read(uint8_t addr, uint8_t *buffer, size_t len) * retrieving the data */ for (count = 0; count < MAX_COUNT; count++) { - udelay(SLEEP_DURATION); + udelay(tpm_dev->sleep_short); rc = i2c_read_raw(tpm_dev->bus, tpm_dev->addr, buffer, len); if (rc == 0) break; /* success, break to skip sleep */ } - } else { + break; + + default: + { /* use a combined read for newer chips * unfortunately the smbus functions are not suitable due to * the 32 byte limit of the smbus. @@ -151,6 +164,7 @@ static int iic_tpm_read(uint8_t addr, uint8_t *buffer, size_t len) udelay(tpm_dev->sleep_short); } } + } /* take care of 'guard time' */ udelay(tpm_dev->sleep_short); @@ -486,6 +500,162 @@ out_err: return -1; } +/* + * cr50 is a TPM 2.0 capable device that requries special + * handling for the I2C interface. + */ + +/* 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) + 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 ssize_t cr50_get_burstcount(struct tpm_chip *chip) +{ + uint8_t buf[4]; + ssize_t burstcnt; + struct stopwatch sw; + + stopwatch_init_usecs_expire(&sw, 10 * SLEEP_DURATION_SAFE); + + while (!stopwatch_expired(&sw)) { + if (iic_tpm_read(TPM_STS(chip->vendor.locality), + buf, sizeof(buf)) != 0) + return -1; + + burstcnt = (buf[2] << 8) + buf[1]; + if (burstcnt > 0 && burstcnt <= CR50_MAX_BURSTCOUNT) + return burstcnt; + + udelay(SLEEP_DURATION_SAFE); + } + + return -1; +} + +static int cr50_tis_i2c_recv(struct tpm_chip *chip, uint8_t *buf, + size_t buf_len) +{ + uint32_t expected_buf; + ssize_t burstcnt; + size_t current, len, expected; + int status; + uint8_t addr = TPM_DATA_FIFO(chip->vendor.locality); + uint8_t extra[CR50_MAX_BURSTCOUNT]; + + if (buf_len < TPM_HEADER_SIZE) + goto out; + + burstcnt = cr50_get_burstcount(chip); + if (burstcnt <= 0 || burstcnt > buf_len) + goto out; + + /* Read first chunk of burstcnt bytes */ + if (iic_tpm_read(addr, buf, burstcnt) != 0) + goto out; + + memcpy(&expected_buf, buf + TPM_RSP_SIZE_BYTE, sizeof(expected_buf)); + expected = (size_t)be32_to_cpu(expected_buf); + if (expected > buf_len) + goto out; + + /* Now read the rest of the data */ + current = burstcnt; + while (current < expected) { + len = min(burstcnt, expected - current); + + if (iic_tpm_read(addr, buf + current, len) != 0) + goto out; + + current += len; + } + + /* cr50 expects the host to drain the FIFO */ + len = current % burstcnt; + if (len && iic_tpm_read(addr, extra, len) != 0) + goto out; + + wait_for_stat(chip, TPM_STS_VALID, &status); + if (status & TPM_STS_DATA_AVAIL) { + printk(BIOS_ERR, "TPM: Data Still Available\n"); + goto out; + } + + cr50_tis_i2c_ready(chip); + return current; + +out: + printk(BIOS_ERR, "%s failed\n", __func__); + cr50_tis_i2c_ready(chip); + return -1; +} + +static int cr50_tis_i2c_send(struct tpm_chip *chip, uint8_t *buf, size_t len) +{ + int status; + ssize_t burstcnt; + size_t sent = 0; + uint8_t tpm_go[4] = { TPM_STS_GO }; + + if (len > TPM_BUFSIZE) + return -1; + + /* Wait until TPM is ready for a command */ + status = chip->vendor.status(chip); + if ((status & TPM_STS_COMMAND_READY) == 0) { + cr50_tis_i2c_ready(chip); + if (wait_for_stat(chip, TPM_STS_COMMAND_READY, &status) < 0) { + printk(BIOS_ERR, "TPM: Command Ready Timeout\n"); + goto out; + } + } + + /* Get the burst count */ + burstcnt = cr50_get_burstcount(chip); + if (burstcnt < 0) + goto out; + + while (len > 0) { + ssize_t limit = min(burstcnt - 1, len); + + if (iic_tpm_write(TPM_DATA_FIFO(chip->vendor.locality), + &(buf[sent]), limit) != 0) + goto out; + + sent += limit; + len -= limit; + } + + /* Ensure TPM is not expecting more data */ + wait_for_stat(chip, TPM_STS_VALID, &status); + if ((status & TPM_STS_DATA_EXPECT) != 0) { + printk(BIOS_ERR, "TPM: Data Still Expected\n"); + goto out; + } + + /* Start the TPM command */ + iic_tpm_write(TPM_STS(chip->vendor.locality), tpm_go, sizeof(tpm_go)); + return sent; + +out: + printk(BIOS_ERR, "%s failed\n", __func__); + cr50_tis_i2c_ready(chip); + return -1; +} + /* Initialization of I2C TPM */ int tpm_vendor_init(struct tpm_chip *chip, unsigned bus, uint32_t dev_addr) @@ -506,10 +676,6 @@ int tpm_vendor_init(struct tpm_chip *chip, unsigned bus, uint32_t dev_addr) memset(&chip->vendor, 0, sizeof(struct tpm_vendor_specific)); chip->is_open = 1; - 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; 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; @@ -528,16 +694,30 @@ 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_release; } - tpm_dev->sleep_short = SLEEP_DURATION; - tpm_dev->sleep_long = SLEEP_DURATION_LONG; + 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, "1.2 TPM (chip type %s device-id 0x%X)\n", - chip_name[tpm_dev->chip_type], vendor >> 16); + 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); /* * A timeout query to TPM can be placed here.
1
0
0
0
← Newer
1
...
129
130
131
132
133
134
Older →
Jump to page:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
Results per page:
10
25
50
100
200