drivers/i2c/tpm/cr50: Use tis_plat_irq_status for Cr50 IRQ status
The Cr50 TPM uses an IRQ to provide a "status" signal used for hand-shaking the reception of commands. Real IRQs are not supported in firmware, however firmware can still poll interrupt status registers for the same effect. Commit94cc485338
("drivers/i2c/tpm/cr50: Support interrupts for status") added support for the Cr50 driver on X86 platforms to use a KConfig file to supply an IRQ which it would poll using acpi_get_gpe. If the IRQ is not supplied, the Cr50 driver inserts a 20 ms wait. Unfortunately this doesn't work so well when using the i2c connected Cr50 on ARM platforms. Luckily, a more generic implementation to allow a mainboard to supply a Cr50 IRQ status polling function was solved for SPI connected Cr50s by commit19e3d335bd
("drivers/spi/tpm: using tpm irq to sync tpm transaction"). Let's refactor the i2c c50 driver to use this same approach, and change eve and reef boards to make use of DRIVER_TPM_TIS_ACPI_INTERRUPT for specifying the TPM flow control interrupt. This essentially reverts these two commits:48f708d199
drivers/i2c/tpm/cr50: Initialize IRQ status handler before probe94cc485338
drivers/i2c/tpm/cr50: Support interrupts for status And ports this commit to i2c/tpm/cr50:19e3d335bd
drivers/spi/tpm: using tpm irq to sync tpm transaction As a side effect the tpm_vendor_specific IRQ field goes back to its original usage as the "TPM 1.2 command complete" interrupt, instead of being repurposed to hold the flow control IRQ. BRANCH=none BUG=b:36786804 TEST=Boot reef w/ serial enabled firmware, verify verstage sees "cr50 TPM" and does not complain about lack of tis_plat_irq_status(). TEST=Boot eve w/ serial enabled firmware, verify verstage sees "cr50 TPM" and does not complain about lack of tis_plat_irq_status(). Change-Id: I004329eae1d8aabda51c46b8504bf210484782b4 Signed-off-by: Daniel Kurtz <djkurtz@chromium.org> Reviewed-on: https://review.coreboot.org/19363 Reviewed-by: Julius Werner <jwerner@chromium.org> Reviewed-by: Aaron Durbin <adurbin@chromium.org> Tested-by: build bot (Jenkins)
This commit is contained in:
parent
ed644b1803
commit
c4852e7157
|
@ -44,11 +44,6 @@ config DRIVER_TPM_I2C_ADDR
|
||||||
default 0x2 # FIXME, workaround for Kconfig BS
|
default 0x2 # FIXME, workaround for Kconfig BS
|
||||||
depends on I2C_TPM
|
depends on I2C_TPM
|
||||||
|
|
||||||
config DRIVER_TPM_I2C_IRQ
|
|
||||||
int "IRQ or GPE to use for TPM interrupt"
|
|
||||||
default -1
|
|
||||||
depends on I2C_TPM
|
|
||||||
|
|
||||||
config DRIVER_I2C_TPM_ACPI
|
config DRIVER_I2C_TPM_ACPI
|
||||||
depends on I2C_TPM
|
depends on I2C_TPM
|
||||||
bool "Generate I2C TPM ACPI device"
|
bool "Generate I2C TPM ACPI device"
|
||||||
|
|
|
@ -42,10 +42,6 @@
|
||||||
#include <tpm.h>
|
#include <tpm.h>
|
||||||
#include "tpm.h"
|
#include "tpm.h"
|
||||||
|
|
||||||
#if IS_ENABLED(CONFIG_ARCH_X86)
|
|
||||||
#include <arch/acpi.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define CR50_MAX_BUFSIZE 63
|
#define CR50_MAX_BUFSIZE 63
|
||||||
#define CR50_TIMEOUT_LONG_MS 2000 /* Long timeout while waiting for TPM */
|
#define CR50_TIMEOUT_LONG_MS 2000 /* Long timeout while waiting for TPM */
|
||||||
#define CR50_TIMEOUT_SHORT_MS 2 /* Short timeout during transactions */
|
#define CR50_TIMEOUT_SHORT_MS 2 /* Short timeout during transactions */
|
||||||
|
@ -61,33 +57,33 @@ struct tpm_inf_dev {
|
||||||
|
|
||||||
static struct tpm_inf_dev g_tpm_dev CAR_GLOBAL;
|
static struct tpm_inf_dev g_tpm_dev CAR_GLOBAL;
|
||||||
|
|
||||||
|
__attribute__ ((weak)) int tis_plat_irq_status(void)
|
||||||
|
{
|
||||||
|
static int warning_displayed CAR_GLOBAL;
|
||||||
|
|
||||||
|
if (!car_get_var(warning_displayed)) {
|
||||||
|
printk(BIOS_WARNING, "WARNING: tis_plat_irq_status() not implemented, wasting 20ms to wait on Cr50!\n");
|
||||||
|
car_set_var(warning_displayed, 1);
|
||||||
|
}
|
||||||
|
mdelay(CR50_TIMEOUT_NOIRQ_MS);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
/* Wait for interrupt to indicate the TPM is ready */
|
/* Wait for interrupt to indicate the TPM is ready */
|
||||||
static int cr50_i2c_wait_tpm_ready(struct tpm_chip *chip)
|
static int cr50_i2c_wait_tpm_ready(struct tpm_chip *chip)
|
||||||
{
|
{
|
||||||
struct stopwatch sw;
|
struct stopwatch sw;
|
||||||
|
|
||||||
if (!chip->vendor.irq_status) {
|
|
||||||
/* Fixed delay if interrupt not supported */
|
|
||||||
mdelay(CR50_TIMEOUT_NOIRQ_MS);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
stopwatch_init_msecs_expire(&sw, CR50_TIMEOUT_IRQ_MS);
|
stopwatch_init_msecs_expire(&sw, CR50_TIMEOUT_IRQ_MS);
|
||||||
|
|
||||||
while (!chip->vendor.irq_status(chip->vendor.irq))
|
while (!tis_plat_irq_status())
|
||||||
if (stopwatch_expired(&sw))
|
if (stopwatch_expired(&sw))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Clear pending interrupts */
|
|
||||||
static void cr50_i2c_clear_tpm_irq(struct tpm_chip *chip)
|
|
||||||
{
|
|
||||||
if (chip->vendor.irq_status)
|
|
||||||
chip->vendor.irq_status(chip->vendor.irq);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* cr50_i2c_read() - read from TPM register
|
* cr50_i2c_read() - read from TPM register
|
||||||
*
|
*
|
||||||
|
@ -111,7 +107,7 @@ static int cr50_i2c_read(struct tpm_chip *chip, uint8_t addr,
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
/* Clear interrupt before starting transaction */
|
/* Clear interrupt before starting transaction */
|
||||||
cr50_i2c_clear_tpm_irq(chip);
|
tis_plat_irq_status();
|
||||||
|
|
||||||
/* Send the register address byte to the TPM */
|
/* Send the register address byte to the TPM */
|
||||||
if (i2c_write_raw(tpm_dev->bus, tpm_dev->addr, &addr, 1)) {
|
if (i2c_write_raw(tpm_dev->bus, tpm_dev->addr, &addr, 1)) {
|
||||||
|
@ -161,7 +157,7 @@ static int cr50_i2c_write(struct tpm_chip *chip,
|
||||||
memcpy(tpm_dev->buf + 1, buffer, len);
|
memcpy(tpm_dev->buf + 1, buffer, len);
|
||||||
|
|
||||||
/* Clear interrupt before starting transaction */
|
/* Clear interrupt before starting transaction */
|
||||||
cr50_i2c_clear_tpm_irq(chip);
|
tis_plat_irq_status();
|
||||||
|
|
||||||
/* Send write request buffer with address */
|
/* Send write request buffer with address */
|
||||||
if (i2c_write_raw(tpm_dev->bus, tpm_dev->addr, tpm_dev->buf, len + 1)) {
|
if (i2c_write_raw(tpm_dev->bus, tpm_dev->addr, tpm_dev->buf, len + 1)) {
|
||||||
|
@ -418,25 +414,6 @@ static void cr50_vendor_init(struct tpm_chip *chip)
|
||||||
chip->vendor.recv = &cr50_i2c_tis_recv;
|
chip->vendor.recv = &cr50_i2c_tis_recv;
|
||||||
chip->vendor.send = &cr50_i2c_tis_send;
|
chip->vendor.send = &cr50_i2c_tis_send;
|
||||||
chip->vendor.cancel = &cr50_i2c_tis_ready;
|
chip->vendor.cancel = &cr50_i2c_tis_ready;
|
||||||
chip->vendor.irq = CONFIG_DRIVER_TPM_I2C_IRQ;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Interrupts are not supported this early in firmware,
|
|
||||||
* use use an arch-specific method to query for interrupt status.
|
|
||||||
*/
|
|
||||||
if (chip->vendor.irq > 0) {
|
|
||||||
#if IS_ENABLED(CONFIG_ARCH_X86)
|
|
||||||
/* Query GPE status for interrupt */
|
|
||||||
chip->vendor.irq_status = &acpi_get_gpe;
|
|
||||||
#else
|
|
||||||
chip->vendor.irq = -1;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
if (chip->vendor.irq <= 0)
|
|
||||||
printk(BIOS_WARNING,
|
|
||||||
"%s: No IRQ, will use %ums delay for TPM ready\n",
|
|
||||||
__func__, CR50_TIMEOUT_NOIRQ_MS);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int tpm_vendor_probe(unsigned int bus, uint32_t addr)
|
int tpm_vendor_probe(unsigned int bus, uint32_t addr)
|
||||||
|
@ -503,8 +480,8 @@ int tpm_vendor_init(struct tpm_chip *chip, unsigned int bus, uint32_t dev_addr)
|
||||||
goto out_err;
|
goto out_err;
|
||||||
}
|
}
|
||||||
|
|
||||||
printk(BIOS_DEBUG, "cr50 TPM 2.0 (i2c %u:0x%02x irq %d id 0x%x)\n",
|
printk(BIOS_DEBUG, "cr50 TPM 2.0 (i2c %u:0x%02x id 0x%x)\n",
|
||||||
bus, dev_addr, chip->vendor.irq, vendor >> 16);
|
bus, dev_addr, vendor >> 16);
|
||||||
|
|
||||||
chip->is_open = 1;
|
chip->is_open = 1;
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -59,7 +59,6 @@ struct tpm_vendor_specific {
|
||||||
uint8_t req_complete_val;
|
uint8_t req_complete_val;
|
||||||
uint8_t req_canceled;
|
uint8_t req_canceled;
|
||||||
int irq;
|
int irq;
|
||||||
int (*irq_status)(int irq);
|
|
||||||
int (*recv)(struct tpm_chip *, uint8_t *, size_t);
|
int (*recv)(struct tpm_chip *, uint8_t *, size_t);
|
||||||
int (*send)(struct tpm_chip *, uint8_t *, size_t);
|
int (*send)(struct tpm_chip *, uint8_t *, size_t);
|
||||||
void (*cancel)(struct tpm_chip *);
|
void (*cancel)(struct tpm_chip *);
|
||||||
|
|
Loading…
Reference in New Issue