summaryrefslogtreecommitdiff
path: root/src/drivers
diff options
context:
space:
mode:
authorFelix Held <felix-coreboot@felixheld.de>2024-11-06 00:40:33 +0100
committerFelix Held <felix-coreboot@felixheld.de>2024-11-12 15:40:38 +0000
commit8c9e6a1f1daead0a0a0d7b9ca2f7af2d49b8d0b3 (patch)
treef484da4e2a7318c0b3874e50c39c79f9afb15322 /src/drivers
parent3040e9967951c014d98a6d9c0b45a12777af6372 (diff)
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>
Diffstat (limited to 'src/drivers')
-rw-r--r--src/drivers/spi/spi_flash_internal.h23
-rw-r--r--src/drivers/spi/spi_flash_sfdp.c157
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;
+}