drivers/spi/spi_flash_sfdp: add SFDP support to get RPMC parameters
JESD216F.02 and JESD260 were used as a reference. Signed-off-by: Felix Held <felix-coreboot@felixheld.de> Change-Id: I3a1f7a5d16dd3ca6c8263b617ae9c21184b6a5b9 Reviewed-on: https://review.coreboot.org/c/coreboot/+/85008 Reviewed-by: Matt DeVillier <matt.devillier@amd.corp-partner.google.com> Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
This commit is contained in:
parent
3040e99679
commit
8c9e6a1f1d
2 changed files with 176 additions and 4 deletions
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue