diff options
-rw-r--r-- | src/drivers/spi/spi_flash_internal.h | 23 | ||||
-rw-r--r-- | src/drivers/spi/spi_flash_sfdp.c | 157 |
2 files changed, 176 insertions, 4 deletions
diff --git a/src/drivers/spi/spi_flash_internal.h b/src/drivers/spi/spi_flash_internal.h index d0f32c52ed..830a178e97 100644 --- a/src/drivers/spi/spi_flash_internal.h +++ b/src/drivers/spi/spi_flash_internal.h @@ -136,4 +136,27 @@ extern const struct spi_flash_ops_descriptor spi_flash_pp_0x20_sector_desc; /* Page Programming Command Set with 0xd8 Sector Erase command. */ extern const struct spi_flash_ops_descriptor spi_flash_pp_0xd8_sector_desc; +struct sfdp_rpmc_info { + bool flash_hardening; + enum { + SFDP_RPMC_COUNTER_BITS_32 = 0, + SFDP_RPMC_COUNTER_BITS_RESERVED = 1, + } monotonic_counter_size; + enum { + SFDP_RPMC_POLL_OP2_EXTENDED_STATUS = 0, + SFDP_RPMC_POLL_READ_STATUS = 1, + } busy_polling_method; + uint8_t number_of_counters; + uint8_t op1_write_command; + uint8_t op2_read_command; + uint64_t update_rate_s; + uint64_t read_counter_polling_delay_us; + uint64_t write_counter_polling_short_delay_us; + uint64_t write_counter_polling_long_delay_us; +}; + +/* Get RPMC information from the SPI flash's SFDP table */ +enum cb_err spi_flash_get_sfdp_rpmc(const struct spi_flash *flash, + struct sfdp_rpmc_info *rpmc_info); + #endif /* SPI_FLASH_INTERNAL_H */ diff --git a/src/drivers/spi/spi_flash_sfdp.c b/src/drivers/spi/spi_flash_sfdp.c index 807a33be58..46117bca38 100644 --- a/src/drivers/spi/spi_flash_sfdp.c +++ b/src/drivers/spi/spi_flash_sfdp.c @@ -164,10 +164,9 @@ void spi_flash_print_sfdp_headers(const struct spi_flash *flash) } } -static inline enum cb_err find_sfdp_parameter_header(const struct spi_flash *flash, - uint16_t table_id, uint16_t *revision, - uint8_t *length_dwords, - uint32_t *table_pointer) +static enum cb_err find_sfdp_parameter_header(const struct spi_flash *flash, uint16_t table_id, + uint16_t *revision, uint8_t *length_dwords, + uint32_t *table_pointer) { enum cb_err stat; uint16_t sfdp_rev; @@ -201,3 +200,153 @@ static inline enum cb_err find_sfdp_parameter_header(const struct spi_flash *fla return CB_ERR; } + +#define SFDP_PARAMETER_ID_RPMC 0xff03 + +#define SFDP_RPMC_TABLE_LENGTH_DWORDS 2 +#define SFDP_RPMC_TABLE_SUPPORTED_MAJOR_REV 1 + +/* RPMC parameter table byte offsets and fields */ +#define SFDP_RPMC_TABLE_CONFIG 0 +#define SFDP_RPMC_TABLE_CONFIG_FLASH_HARDENING_BIT BIT(0) +#define SFDP_RPMC_TABLE_CONFIG_MONOTONIC_COUNTER_SIZE_BIT BIT(1) +#define SFDP_RPMC_TABLE_CONFIG_BUSY_POLLING_METHOD BIT(2) +#define SFDP_RPMC_TABLE_CONFIG_RESERVED BIT(3) +#define SFDP_RPMC_TABLE_CONFIG_RESERVED_VALUE 0x08 +#define SFDP_RPMC_TABLE_CONFIG_NUM_COUNTERS_MASK 0xf0 +#define SFDP_RPMC_TABLE_CONFIG_NUM_COUNTERS_SHIFT 4 +#define SFDP_RPMC_TABLE_RPMC_OP1 1 +#define SFDP_RPMC_TABLE_RPMC_OP2 2 +#define SFDP_RPMC_TABLE_UPDATE_RATE 3 +#define SFDP_RPMC_TABLE_UPDATE_RATE_MASK 0x0f +#define SFDP_RPMC_TABLE_UPDATE_RATE_RESERVED_MASK 0xf0 +#define SFDP_RPMC_TABLE_UPDATE_RATE_RESERVED_VALUE 0xf0 +#define SFDP_RPMC_TABLE_READ_COUNTER_POLLING_DELAY 4 +#define SFDP_RPMC_TABLE_WRITE_COUNTER_POLLING_SHORT_DELAY 5 +#define SFDP_RPMC_TABLE_WRITE_COUNTER_POLLING_LONG_DELAY 6 +#define SFDP_RPMC_TABLE_RESERVED_BYTE 7 +#define SFDP_RPMC_TABLE_RESERVED_BYTE_VALUE 0xff + +static uint64_t calc_rpmc_update_rate_s(uint8_t val) +{ + /* val is at most 15, so this won't overflow */ + return 5 * 1 << (val & SFDP_RPMC_TABLE_UPDATE_RATE_MASK); +} + +#define SPDF_RPMC_DELAY_VALUE_MASK 0x1f +#define SPDF_RPMC_DELAY_UNIT_MASK 0x60 +#define SPDF_RPMC_DELAY_UNIT_SHIFT 5 +#define SPDF_RPMC_DELAY_SHORT_UNIT_0_US 1 /* 1us */ +#define SPDF_RPMC_DELAY_SHORT_UNIT_1_US 16 /* 16us */ +#define SPDF_RPMC_DELAY_SHORT_UNIT_2_US 128 /* 128us */ +#define SPDF_RPMC_DELAY_SHORT_UNIT_3_US 1000 /* 1ms */ +#define SPDF_RPMC_DELAY_LONG_UNIT_0_US 1000 /* 1ms */ +#define SPDF_RPMC_DELAY_LONG_UNIT_1_US 16000 /* 16ms */ +#define SPDF_RPMC_DELAY_LONG_UNIT_2_US 128000 /* 128ms */ +#define SPDF_RPMC_DELAY_LONG_UNIT_3_US 1000000 /* 1s */ + +static uint64_t calc_rpmc_short_delay_us(uint8_t val) +{ + const uint8_t value = val & SPDF_RPMC_DELAY_VALUE_MASK; + const uint8_t shift = (val & SPDF_RPMC_DELAY_UNIT_MASK) >> SPDF_RPMC_DELAY_UNIT_SHIFT; + uint64_t multiplier; + + switch (shift) { + case 0: + multiplier = SPDF_RPMC_DELAY_SHORT_UNIT_0_US; + break; + case 1: + multiplier = SPDF_RPMC_DELAY_SHORT_UNIT_1_US; + break; + case 2: + multiplier = SPDF_RPMC_DELAY_SHORT_UNIT_2_US; + break; + default: + multiplier = SPDF_RPMC_DELAY_SHORT_UNIT_3_US; + break; + } + + return value * multiplier; +} + +static uint64_t calc_rpmc_long_delay_us(uint8_t val) +{ + const uint8_t value = val & SPDF_RPMC_DELAY_VALUE_MASK; + const uint8_t shift = (val & SPDF_RPMC_DELAY_UNIT_MASK) >> SPDF_RPMC_DELAY_UNIT_SHIFT; + uint64_t multiplier; + + switch (shift) { + case 0: + multiplier = SPDF_RPMC_DELAY_LONG_UNIT_0_US; + break; + case 1: + multiplier = SPDF_RPMC_DELAY_LONG_UNIT_1_US; + break; + case 2: + multiplier = SPDF_RPMC_DELAY_LONG_UNIT_2_US; + break; + default: + multiplier = SPDF_RPMC_DELAY_LONG_UNIT_3_US; + break; + } + + return value * multiplier; +} + +enum cb_err spi_flash_get_sfdp_rpmc(const struct spi_flash *flash, + struct sfdp_rpmc_info *rpmc_info) +{ + uint16_t rev; + uint8_t length_dwords; + uint32_t table_pointer; + uint8_t buf[SFDP_RPMC_TABLE_LENGTH_DWORDS * sizeof(uint32_t)]; + + if (find_sfdp_parameter_header(flash, SFDP_PARAMETER_ID_RPMC, &rev, &length_dwords, + &table_pointer) != CB_SUCCESS) + return CB_ERR; + + if (length_dwords != SFDP_RPMC_TABLE_LENGTH_DWORDS) + return CB_ERR; + + if (rev >> 8 != SFDP_RPMC_TABLE_SUPPORTED_MAJOR_REV) { + printk(BIOS_ERR, "Unsupprted major RPMC table revision %#x\n", rev >> 8); + return CB_ERR; + } + + if (read_sfdp_data(flash, table_pointer, sizeof(buf), buf) != CB_SUCCESS) + return CB_ERR; + + if ((buf[SFDP_RPMC_TABLE_CONFIG] & SFDP_RPMC_TABLE_CONFIG_RESERVED) != + SFDP_RPMC_TABLE_CONFIG_RESERVED_VALUE || + (buf[SFDP_RPMC_TABLE_UPDATE_RATE] & SFDP_RPMC_TABLE_UPDATE_RATE_RESERVED_MASK) != + SFDP_RPMC_TABLE_UPDATE_RATE_RESERVED_VALUE || + buf[SFDP_RPMC_TABLE_RESERVED_BYTE] != SFDP_RPMC_TABLE_RESERVED_BYTE_VALUE) { + printk(BIOS_ERR, "Unexpected reserved values in RPMC table\n"); + return CB_ERR; + } + + rpmc_info->flash_hardening = !!(buf[SFDP_RPMC_TABLE_CONFIG] & + SFDP_RPMC_TABLE_CONFIG_FLASH_HARDENING_BIT); + rpmc_info->monotonic_counter_size = (buf[SFDP_RPMC_TABLE_CONFIG] & + SFDP_RPMC_TABLE_CONFIG_FLASH_HARDENING_BIT) ? + SFDP_RPMC_COUNTER_BITS_RESERVED : + SFDP_RPMC_COUNTER_BITS_32; + rpmc_info->busy_polling_method = (buf[SFDP_RPMC_TABLE_CONFIG] & + SFDP_RPMC_TABLE_CONFIG_BUSY_POLLING_METHOD) ? + SFDP_RPMC_POLL_READ_STATUS : + SFDP_RPMC_POLL_OP2_EXTENDED_STATUS; + rpmc_info->number_of_counters = ((buf[SFDP_RPMC_TABLE_CONFIG] & + SFDP_RPMC_TABLE_CONFIG_NUM_COUNTERS_MASK) >> + SFDP_RPMC_TABLE_CONFIG_NUM_COUNTERS_SHIFT) + 1; + rpmc_info->op1_write_command = buf[SFDP_RPMC_TABLE_RPMC_OP1]; + rpmc_info->op2_read_command = buf[SFDP_RPMC_TABLE_RPMC_OP2]; + rpmc_info->update_rate_s = calc_rpmc_update_rate_s(buf[SFDP_RPMC_TABLE_UPDATE_RATE] & + SFDP_RPMC_TABLE_UPDATE_RATE_MASK); + rpmc_info->read_counter_polling_delay_us = calc_rpmc_short_delay_us( + buf[SFDP_RPMC_TABLE_READ_COUNTER_POLLING_DELAY]); + rpmc_info->write_counter_polling_short_delay_us = calc_rpmc_short_delay_us( + buf[SFDP_RPMC_TABLE_WRITE_COUNTER_POLLING_SHORT_DELAY]); + rpmc_info->write_counter_polling_long_delay_us = calc_rpmc_long_delay_us( + buf[SFDP_RPMC_TABLE_WRITE_COUNTER_POLLING_LONG_DELAY]); + return CB_SUCCESS; +} |