drivers/spi/tpm: Make SPI TPM driver CAR-safe
1. Use proper CAR semantics for global/static variables. 2. Use spi_* functions directly instead of using a global structure to store pointers to those functions. BUG=b:36873582 Change-Id: I1fc52ab797ef0cbd3793a387d68198efc5dde58c Signed-off-by: Furquan Shaikh <furquan@chromium.org> Reviewed-on: https://review.coreboot.org/19114 Tested-by: build bot (Jenkins) Reviewed-by: Aaron Durbin <adurbin@chromium.org>
This commit is contained in:
committed by
Furquan Shaikh
parent
19e3d335bd
commit
bdf86a69ff
@ -15,6 +15,7 @@
|
|||||||
* Specification Revision 00.43".
|
* Specification Revision 00.43".
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <arch/early_variables.h>
|
||||||
#include <commonlib/endian.h>
|
#include <commonlib/endian.h>
|
||||||
#include <console/console.h>
|
#include <console/console.h>
|
||||||
#include <delay.h>
|
#include <delay.h>
|
||||||
@ -35,25 +36,11 @@
|
|||||||
#define TPM_RID_REG (TPM_LOCALITY_0_SPI_BASE + 0xf04)
|
#define TPM_RID_REG (TPM_LOCALITY_0_SPI_BASE + 0xf04)
|
||||||
#define TPM_FW_VER (TPM_LOCALITY_0_SPI_BASE + 0xf90)
|
#define TPM_FW_VER (TPM_LOCALITY_0_SPI_BASE + 0xf90)
|
||||||
|
|
||||||
/* SPI Interface descriptor used by the driver. */
|
/* SPI slave structure for TPM device. */
|
||||||
struct tpm_spi_if {
|
static struct spi_slave g_spi_slave CAR_GLOBAL;
|
||||||
struct spi_slave slave;
|
|
||||||
int (*cs_assert)(const struct spi_slave *slave);
|
|
||||||
void (*cs_deassert)(const struct spi_slave *slave);
|
|
||||||
int (*xfer)(const struct spi_slave *slave, const void *dout,
|
|
||||||
size_t bytesout, void *din,
|
|
||||||
size_t bytesin);
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Use the common SPI driver wrapper as the interface callbacks. */
|
|
||||||
static struct tpm_spi_if tpm_if = {
|
|
||||||
.cs_assert = spi_claim_bus,
|
|
||||||
.cs_deassert = spi_release_bus,
|
|
||||||
.xfer = spi_xfer
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Cached TPM device identification. */
|
/* Cached TPM device identification. */
|
||||||
static struct tpm2_info tpm_info;
|
static struct tpm2_info g_tpm_info CAR_GLOBAL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* TODO(vbendeb): make CONFIG_DEBUG_TPM an int to allow different level of
|
* TODO(vbendeb): make CONFIG_DEBUG_TPM an int to allow different level of
|
||||||
@ -100,16 +87,16 @@ typedef struct {
|
|||||||
|
|
||||||
void tpm2_get_info(struct tpm2_info *info)
|
void tpm2_get_info(struct tpm2_info *info)
|
||||||
{
|
{
|
||||||
*info = tpm_info;
|
*info = car_get_var(g_tpm_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
__attribute__((weak)) int tis_plat_irq_status(void)
|
__attribute__((weak)) int tis_plat_irq_status(void)
|
||||||
{
|
{
|
||||||
static int warning_displayed;
|
static int warning_displayed CAR_GLOBAL;
|
||||||
|
|
||||||
if (!warning_displayed) {
|
if (!car_get_var(warning_displayed)) {
|
||||||
printk(BIOS_WARNING, "WARNING: tis_plat_irq_status() not implemented, wasting 10ms to wait on Cr50!\n");
|
printk(BIOS_WARNING, "WARNING: tis_plat_irq_status() not implemented, wasting 10ms to wait on Cr50!\n");
|
||||||
warning_displayed = 1;
|
car_set_var(warning_displayed, 1);
|
||||||
}
|
}
|
||||||
mdelay(10);
|
mdelay(10);
|
||||||
|
|
||||||
@ -148,18 +135,19 @@ static int start_transaction(int read_write, size_t bytes, unsigned addr)
|
|||||||
uint8_t byte;
|
uint8_t byte;
|
||||||
int i;
|
int i;
|
||||||
struct stopwatch sw;
|
struct stopwatch sw;
|
||||||
static int tpm_sync_needed;
|
static int tpm_sync_needed CAR_GLOBAL;
|
||||||
|
struct spi_slave *spi_slave = car_get_var_ptr(&g_spi_slave);
|
||||||
|
|
||||||
/* Wait for tpm to finish previous transaction if needed */
|
/* Wait for tpm to finish previous transaction if needed */
|
||||||
if (tpm_sync_needed)
|
if (car_get_var(tpm_sync_needed))
|
||||||
tpm_sync();
|
tpm_sync();
|
||||||
else
|
else
|
||||||
tpm_sync_needed = 1;
|
car_set_var(tpm_sync_needed, 1);
|
||||||
|
|
||||||
/* Try to wake cr50 if it is asleep. */
|
/* Try to wake cr50 if it is asleep. */
|
||||||
tpm_if.cs_assert(&tpm_if.slave);
|
spi_claim_bus(spi_slave);
|
||||||
udelay(1);
|
udelay(1);
|
||||||
tpm_if.cs_deassert(&tpm_if.slave);
|
spi_release_bus(spi_slave);
|
||||||
udelay(100);
|
udelay(100);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -174,7 +162,7 @@ static int start_transaction(int read_write, size_t bytes, unsigned addr)
|
|||||||
header.body[i + 1] = (addr >> (8 * (2 - i))) & 0xff;
|
header.body[i + 1] = (addr >> (8 * (2 - i))) & 0xff;
|
||||||
|
|
||||||
/* CS assert wakes up the slave. */
|
/* CS assert wakes up the slave. */
|
||||||
tpm_if.cs_assert(&tpm_if.slave);
|
spi_claim_bus(spi_slave);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The TCG TPM over SPI specification introduces the notion of SPI
|
* The TCG TPM over SPI specification introduces the notion of SPI
|
||||||
@ -201,7 +189,7 @@ static int start_transaction(int read_write, size_t bytes, unsigned addr)
|
|||||||
* to require to stall the master, this would present an issue.
|
* to require to stall the master, this would present an issue.
|
||||||
* crosbug.com/p/52132 has been opened to track this.
|
* crosbug.com/p/52132 has been opened to track this.
|
||||||
*/
|
*/
|
||||||
tpm_if.xfer(&tpm_if.slave, header.body, sizeof(header.body), NULL, 0);
|
spi_xfer(spi_slave, header.body, sizeof(header.body), NULL, 0);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Now poll the bus until TPM removes the stall bit. Give it up to 100
|
* Now poll the bus until TPM removes the stall bit. Give it up to 100
|
||||||
@ -212,10 +200,10 @@ static int start_transaction(int read_write, size_t bytes, unsigned addr)
|
|||||||
do {
|
do {
|
||||||
if (stopwatch_expired(&sw)) {
|
if (stopwatch_expired(&sw)) {
|
||||||
printk(BIOS_ERR, "TPM flow control failure\n");
|
printk(BIOS_ERR, "TPM flow control failure\n");
|
||||||
tpm_if.cs_deassert(&tpm_if.slave);
|
spi_release_bus(spi_slave);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
tpm_if.xfer(&tpm_if.slave, NULL, 0, &byte, 1);
|
spi_xfer(spi_slave, NULL, 0, &byte, 1);
|
||||||
} while (!(byte & 1));
|
} while (!(byte & 1));
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -228,10 +216,11 @@ static void trace_dump(const char *prefix, uint32_t reg,
|
|||||||
size_t bytes, const uint8_t *buffer,
|
size_t bytes, const uint8_t *buffer,
|
||||||
int force)
|
int force)
|
||||||
{
|
{
|
||||||
static char prev_prefix;
|
static char prev_prefix CAR_GLOBAL;
|
||||||
static unsigned prev_reg;
|
static unsigned prev_reg CAR_GLOBAL;
|
||||||
static int current_char;
|
static int current_char CAR_GLOBAL;
|
||||||
const int BYTES_PER_LINE = 32;
|
const int BYTES_PER_LINE = 32;
|
||||||
|
int *current_char_ptr = car_get_var_ptr(¤t_char);
|
||||||
|
|
||||||
if (!force) {
|
if (!force) {
|
||||||
if (!debug_level_)
|
if (!debug_level_)
|
||||||
@ -245,11 +234,12 @@ static void trace_dump(const char *prefix, uint32_t reg,
|
|||||||
* Do not print register address again if the last dump print was for
|
* Do not print register address again if the last dump print was for
|
||||||
* that register.
|
* that register.
|
||||||
*/
|
*/
|
||||||
if ((prev_prefix != *prefix) || (prev_reg != reg)) {
|
if ((car_get_var(prev_prefix) != *prefix) ||
|
||||||
prev_prefix = *prefix;
|
(car_get_var(prev_reg) != reg)) {
|
||||||
prev_reg = reg;
|
car_set_var(prev_prefix, *prefix);
|
||||||
|
car_set_var(prev_reg, reg);
|
||||||
printk(BIOS_DEBUG, "\n%s %2.2x:", prefix, reg);
|
printk(BIOS_DEBUG, "\n%s %2.2x:", prefix, reg);
|
||||||
current_char = 0;
|
*current_char_ptr = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((reg != TPM_DATA_FIFO_REG) && (bytes == 4)) {
|
if ((reg != TPM_DATA_FIFO_REG) && (bytes == 4)) {
|
||||||
@ -266,11 +256,12 @@ static void trace_dump(const char *prefix, uint32_t reg,
|
|||||||
* quantiites is printed byte at a time.
|
* quantiites is printed byte at a time.
|
||||||
*/
|
*/
|
||||||
for (i = 0; i < bytes; i++) {
|
for (i = 0; i < bytes; i++) {
|
||||||
if (current_char && !(current_char % BYTES_PER_LINE)) {
|
if (*current_char_ptr &&
|
||||||
|
!(*current_char_ptr % BYTES_PER_LINE)) {
|
||||||
printk(BIOS_DEBUG, "\n ");
|
printk(BIOS_DEBUG, "\n ");
|
||||||
current_char = 0;
|
*current_char_ptr = 0;
|
||||||
}
|
}
|
||||||
current_char++;
|
(*current_char_ptr)++;
|
||||||
printk(BIOS_DEBUG, " %2.2x", buffer[i]);
|
printk(BIOS_DEBUG, " %2.2x", buffer[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -282,7 +273,8 @@ static void trace_dump(const char *prefix, uint32_t reg,
|
|||||||
*/
|
*/
|
||||||
static void write_bytes(const void *buffer, size_t bytes)
|
static void write_bytes(const void *buffer, size_t bytes)
|
||||||
{
|
{
|
||||||
tpm_if.xfer(&tpm_if.slave, buffer, bytes, NULL, 0);
|
struct spi_slave *spi_slave = car_get_var_ptr(&g_spi_slave);
|
||||||
|
spi_xfer(spi_slave, buffer, bytes, NULL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -291,7 +283,8 @@ static void write_bytes(const void *buffer, size_t bytes)
|
|||||||
*/
|
*/
|
||||||
static void read_bytes(void *buffer, size_t bytes)
|
static void read_bytes(void *buffer, size_t bytes)
|
||||||
{
|
{
|
||||||
tpm_if.xfer(&tpm_if.slave, NULL, 0, buffer, bytes);
|
struct spi_slave *spi_slave = car_get_var_ptr(&g_spi_slave);
|
||||||
|
spi_xfer(spi_slave, NULL, 0, buffer, bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -302,11 +295,12 @@ static void read_bytes(void *buffer, size_t bytes)
|
|||||||
*/
|
*/
|
||||||
static int tpm2_write_reg(unsigned reg_number, const void *buffer, size_t bytes)
|
static int tpm2_write_reg(unsigned reg_number, const void *buffer, size_t bytes)
|
||||||
{
|
{
|
||||||
|
struct spi_slave *spi_slave = car_get_var_ptr(&g_spi_slave);
|
||||||
trace_dump("W", reg_number, bytes, buffer, 0);
|
trace_dump("W", reg_number, bytes, buffer, 0);
|
||||||
if (!start_transaction(false, bytes, reg_number))
|
if (!start_transaction(false, bytes, reg_number))
|
||||||
return 0;
|
return 0;
|
||||||
write_bytes(buffer, bytes);
|
write_bytes(buffer, bytes);
|
||||||
tpm_if.cs_deassert(&tpm_if.slave);
|
spi_release_bus(spi_slave);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -318,12 +312,13 @@ static int tpm2_write_reg(unsigned reg_number, const void *buffer, size_t bytes)
|
|||||||
*/
|
*/
|
||||||
static int tpm2_read_reg(unsigned reg_number, void *buffer, size_t bytes)
|
static int tpm2_read_reg(unsigned reg_number, void *buffer, size_t bytes)
|
||||||
{
|
{
|
||||||
|
struct spi_slave *spi_slave = car_get_var_ptr(&g_spi_slave);
|
||||||
if (!start_transaction(true, bytes, reg_number)) {
|
if (!start_transaction(true, bytes, reg_number)) {
|
||||||
memset(buffer, 0, bytes);
|
memset(buffer, 0, bytes);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
read_bytes(buffer, bytes);
|
read_bytes(buffer, bytes);
|
||||||
tpm_if.cs_deassert(&tpm_if.slave);
|
spi_release_bus(spi_slave);
|
||||||
trace_dump("R", reg_number, bytes, buffer, 0);
|
trace_dump("R", reg_number, bytes, buffer, 0);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -359,8 +354,10 @@ int tpm2_init(struct spi_slave *spi_if)
|
|||||||
{
|
{
|
||||||
uint32_t did_vid, status;
|
uint32_t did_vid, status;
|
||||||
uint8_t cmd;
|
uint8_t cmd;
|
||||||
|
struct tpm2_info *tpm_info = car_get_var_ptr(&g_tpm_info);
|
||||||
|
struct spi_slave *spi_slave = car_get_var_ptr(&g_spi_slave);
|
||||||
|
|
||||||
memcpy(&tpm_if.slave, spi_if, sizeof(*spi_if));
|
memcpy(spi_slave, spi_if, sizeof(*spi_if));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* It is enough to check the first register read error status to bail
|
* It is enough to check the first register read error status to bail
|
||||||
@ -411,15 +408,15 @@ int tpm2_init(struct spi_slave *spi_if)
|
|||||||
* structure.
|
* structure.
|
||||||
*/
|
*/
|
||||||
tpm2_read_reg(TPM_RID_REG, &cmd, sizeof(cmd));
|
tpm2_read_reg(TPM_RID_REG, &cmd, sizeof(cmd));
|
||||||
tpm_info.vendor_id = did_vid & 0xffff;
|
tpm_info->vendor_id = did_vid & 0xffff;
|
||||||
tpm_info.device_id = did_vid >> 16;
|
tpm_info->device_id = did_vid >> 16;
|
||||||
tpm_info.revision = cmd;
|
tpm_info->revision = cmd;
|
||||||
|
|
||||||
printk(BIOS_INFO, "Connected to device vid:did:rid of %4.4x:%4.4x:%2.2x\n",
|
printk(BIOS_INFO, "Connected to device vid:did:rid of %4.4x:%4.4x:%2.2x\n",
|
||||||
tpm_info.vendor_id, tpm_info.device_id, tpm_info.revision);
|
tpm_info->vendor_id, tpm_info->device_id, tpm_info->revision);
|
||||||
|
|
||||||
/* Let's report device FW version if available. */
|
/* Let's report device FW version if available. */
|
||||||
if (tpm_info.vendor_id == 0x1ae0) {
|
if (tpm_info->vendor_id == 0x1ae0) {
|
||||||
int chunk_count = 0;
|
int chunk_count = 0;
|
||||||
size_t chunk_size;
|
size_t chunk_size;
|
||||||
/*
|
/*
|
||||||
@ -546,9 +543,10 @@ size_t tpm2_process_command(const void *tpm2_command, size_t command_size,
|
|||||||
uint8_t *rsp_body = tpm2_response;
|
uint8_t *rsp_body = tpm2_response;
|
||||||
union fifo_transfer_buffer fifo_buffer;
|
union fifo_transfer_buffer fifo_buffer;
|
||||||
const int HEADER_SIZE = 6;
|
const int HEADER_SIZE = 6;
|
||||||
|
struct tpm2_info *tpm_info = car_get_var_ptr(&g_tpm_info);
|
||||||
|
|
||||||
/* Do not try using an uninitialized TPM. */
|
/* Do not try using an uninitialized TPM. */
|
||||||
if (!tpm_info.vendor_id)
|
if (!tpm_info->vendor_id)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* Skip the two byte tag, read the size field. */
|
/* Skip the two byte tag, read the size field. */
|
||||||
|
Reference in New Issue
Block a user