diff --git a/sw/device/silicon_creator/lib/drivers/BUILD b/sw/device/silicon_creator/lib/drivers/BUILD index 679d588bf0168..808adeeadd323 100644 --- a/sw/device/silicon_creator/lib/drivers/BUILD +++ b/sw/device/silicon_creator/lib/drivers/BUILD @@ -583,8 +583,10 @@ cc_library( hdrs = ["spi_device.h"], deps = [ "//hw/ip/spi_device/data:spi_device_regs", + "//hw/ip/flash_ctrl/data:flash_ctrl_regs", "//hw/top_earlgrey/sw/autogen:top_earlgrey", "//sw/device/lib/base:abs_mmio", + "//sw/device/lib/base:memory", "//sw/device/silicon_creator/lib:error", ], ) diff --git a/sw/device/silicon_creator/lib/drivers/meson.build b/sw/device/silicon_creator/lib/drivers/meson.build index 63c47dafd2214..0decea93f2e7f 100644 --- a/sw/device/silicon_creator/lib/drivers/meson.build +++ b/sw/device/silicon_creator/lib/drivers/meson.build @@ -583,10 +583,12 @@ sw_silicon_creator_lib_driver_spi_device = declare_dependency( 'sw_silicon_creator_lib_driver_spi_device', sources: [ hw_ip_spi_device_reg_h, + hw_ip_flash_ctrl_reg_h, 'spi_device.c', ], dependencies: [ sw_lib_abs_mmio, + sw_lib_mem, ], ), ) @@ -596,10 +598,12 @@ test('sw_silicon_creator_lib_driver_spi_device_unittest', executable( sources: [ 'spi_device_unittest.cc', hw_ip_spi_device_reg_h, + hw_ip_flash_ctrl_reg_h, 'spi_device.c', ], dependencies: [ sw_vendor_gtest, + sw_lib_testing_memory, sw_lib_testing_mock_abs_mmio, ], native: true, diff --git a/sw/device/silicon_creator/lib/drivers/spi_device.c b/sw/device/silicon_creator/lib/drivers/spi_device.c index b7e6c950233e0..9ce7c4c78bfb6 100644 --- a/sw/device/silicon_creator/lib/drivers/spi_device.c +++ b/sw/device/silicon_creator/lib/drivers/spi_device.c @@ -6,18 +6,469 @@ #include "sw/device/lib/base/abs_mmio.h" #include "sw/device/lib/base/bitfield.h" +#include "sw/device/lib/base/memory.h" #include "sw/device/silicon_creator/lib/error.h" +#include "flash_ctrl_regs.h" #include "hw/top_earlgrey/sw/autogen/top_earlgrey.h" -#include "spi_device_regs.h" // Generated. +#include "spi_device_regs.h" enum { /** * Base address of the spi_device registers. */ kBase = TOP_EARLGREY_SPI_DEVICE_BASE_ADDR, + /** + * Start address of the SFDP space in spi_device buffer. + */ + kSfdpAreaStartAddr = + kBase + SPI_DEVICE_BUFFER_REG_OFFSET + kSpiDeviceSfdpAreaOffset, + /** + * End address (exclusive) of the SFDP space in spi_device buffer. + */ + kSfdpAreaEndAddr = kBase + SPI_DEVICE_BUFFER_REG_OFFSET + + kSpiDeviceSfdpAreaOffset + kSpiDeviceSfdpAreaNumBytes, + /** + * Flash data partition size in bits. + */ + kFlashBitCount = + FLASH_CTRL_PARAM_REG_NUM_BANKS * FLASH_CTRL_PARAM_BYTES_PER_BANK * 8, + /** + * 32-bit SFDP signature that indicates the presence of a SFDP table + * (JESD216F 6.2.1). + */ + kSfdpSignature = 0x50444653, + /** + * Number of parameter headers in the SFDP data structure (JESD216F 6.2.2). + * + * This number is zero-based. OpenTitan currently only has a single parameter + * header for the Basic Flash Parameters Table (BFPT). + */ + kSfdpParamCount = 0, + /** + * SFDP major revision number (JESD216F 6.2.2). + */ + kSfdpMajorRevision = 0x01, + /** + * SFDP minor revision number (JESD216F 6.2.2). + */ + kSfdpMinorRevision = 0x0a, + /** + * 3-byte addressing for SFDP_READ command, 8 wait states (JESD216F 6.2.3). + */ + kSfdpAccessProtocol = 0xff, + /** + * BFPT major revision number (JESD216F 6.4.1). + */ + kBfptMajorRevision = 0x01, + /** + * BFPT minor revision number (JESD216F 6.4.1). + */ + kBfptMinorRevision = 0x07, + /** + * LSB of BFPT's parameter ID (JESD216F 6.4.1). + */ + kBfptParamIdLsb = 0x00, + /** + * MSB of BFPT's parameter ID (JESD216F 6.4.2). + */ + kBfptParamIdMsb = 0xff, + /** + * Offset of the Basic Flash Parameter Table (BFPT) in the SFDP table. + */ + kBfptTablePointer = offsetof(spi_device_sfdp_table_t, bfpt), + /** + * Value used for BFPT fields that are not supported. + * + * Note: A handful of BFPT fields, e.g. Msb of the 14th word of BFPT, use 1 + * instead. Such fields should be defined according to JESD216F instead of + * using this value. + */ + kBfptNotSupported = 0, }; +static_assert(kBfptTablePointer % sizeof(uint32_t) == 0, + "BFPT must be word-aligned"); + +/** + * Computes the width of a field in a Basic Flash Parameters Table (BFPT) word. + * + * @param upper Upper (start) bit of the field (inclusive). + * @param lower Lower (end) bit of the field (inclusive). + */ +#define BFPT_FIELD_WIDTH(upper, lower) ((upper) - (lower) + 1) + +/** + * Computes the mask for a field in a BFPT word. + * + * @param upper Upper (start) bit of the field (inclusive). + * @param lower Lower (end) bit of the field (inclusive). + */ +#define BFPT_FIELD_MASK(upper, lower) \ + (((UINT64_C(1) << BFPT_FIELD_WIDTH(upper, lower)) - 1) << (lower)) + +/** + * Computes the value of a field in a BFPT word. + * + * Bits outside the field are left as 1s. This macro is intended for expanding a + * list of fields, e.g. `BFPT_WORD_1`, to compute the value of a BFPT word using + * bitwise AND. + * + * @param upper Upper (start) bit of the field (inclusive). + * @param lower Lower (end) bit of the field (inclusive). + * @param value Value of the field. + */ +#define BFPT_FIELD_VALUE(upper, lower, value) \ + ((uint32_t)~BFPT_FIELD_MASK(upper, lower) | \ + (BFPT_FIELD_MASK(upper, lower) & ((value) << (lower)))) + +// Note: Words below are numbered starting from 1 to match JESD216F. Some fields +// that are not supported by OpenTitan are merged for the sake of conciseness. +// Unused/reserved fields that should be set to all 1s are ommitted due to the +// definition of `BFPT_FIELD_VALUE()` above. See JESD216F for more details. + +// clang-format off +/** + * BFPT 1st Word + * ------------- + * [31:23]: Unused + * [22:19]: (1S-1S-4S) (1S-4S-4S) (1S-2S-2S) DTR Clock (not supported: 0x0) + * [18:17]: Address bytes (3-byte only addressing: 0x0) + * [16:16]: (1S-1S-2S) (not supported: 0x0) + * [15: 8]: 4 KiB erase instruction (0x20) + * [ 7: 5]: Unused + * [ 4: 4]: Write enable instruction (use 0x06 for WREN: 0x1) + * [ 3: 3]: Volatile block protect bits (solely volatile: 0x1) + * [ 2: 2]: Write granularity (buffer >= 64 B: 0x1) + * [ 1: 0]: Block/sector erase sizes (uniform 4 KiB erase: 0x1) + */ +#define BFPT_WORD_1(X) \ + X(22, 19, kBfptNotSupported) & \ + X(18, 17, 0x0) & \ + X(16, 16, kBfptNotSupported) & \ + X(15, 8, kSpiDeviceCmdSectorErase) & \ + X( 4, 4, 0x1) & \ + X( 3, 3, 0x1) & \ + X( 2, 2, 0x1) & \ + X( 1, 0, 0x1) + +/** + * BFPT 2nd Word + * ------------- + * [31:31]: Density greater than 2 Gib (0x0) + * [30: 0]: Flash memory density in bits, zero-based (0x7fffff) + */ +#define BFPT_WORD_2(X) \ + X(31, 31, 0x0) & \ + X(30, 0, kFlashBitCount - 1) + +/** + * BFPT 3rd Word + * ------------- + * [31: 0]: Fast read (1S-4S-4S) (1S-1S-4S) (not supported, 0x0) + */ +#define BFPT_WORD_3(X) \ + X(31, 0, kBfptNotSupported) + +/** + * BFPT 4th Word + * ------------- + * [31: 0]: Fast read (1S-1S-2S) (1S-2S-2S) (not supported, 0x0) + */ +#define BFPT_WORD_4(X) \ + X(31, 0, kBfptNotSupported) + +/** + * BFPT 5th Word + * ------------- + * [31: 5]: Reserved + * [ 4: 4]: Fast read (4S-4S-4S) support (not supported, 0x0) + * [ 3: 1]: Reserved + * [ 0: 0]: Fast read (2S-2S-2S) support (not supported, 0x0) + */ +#define BFPT_WORD_5(X) \ + X( 4, 4, kBfptNotSupported) & \ + X( 0, 0, kBfptNotSupported) + +/** + * BFPT 6th Word + * ------------- + * [31:16]: Fast read (2S-2S-2S) (not supported, 0x0) + * [15: 0]: Reserved + */ +#define BFPT_WORD_6(X) \ + X(31, 16, kBfptNotSupported) + +/** + * BFPT 7th Word + * ------------- + * [31:16]: Fast read (4S-4S-4S) (not supported, 0x0) + * [15: 0]: Reserved + */ +#define BFPT_WORD_7(X) \ + X(31, 16, kBfptNotSupported) + +/** + * BFPT 8th Word + * ------------- + * [31:16]: Erase type 2 instruction and size (not supported, 0x0) + * [15: 8]: Erase type 1 instruction (0x20) + * [ 7: 0]: Erase type 1 size (4 KiB, 2^N bytes, N = 0x0c) + */ +#define BFPT_WORD_8(X) \ + X(31, 16, kBfptNotSupported) & \ + X(15, 8, kSpiDeviceCmdSectorErase) & \ + X( 7, 0, 0x0c) + +/** + * BFPT 9th Word + * ------------- + * [31: 0]: Erase type 4 and 3 (not supported, 0x0) + */ +#define BFPT_WORD_9(X) \ + X(31, 0, kBfptNotSupported) + +/** + * BFPT 10th Word + * -------------- + * [31:11]: Erase 4,3,2 typical time (not supported, 0x0) + * [10: 9]: Erase type 1 time unit (ms, 0x0) + * [ 8: 4]: Erase type 1 time count, zero-based (0x1d) + * [ 3: 0]: Max erase time multiplier, zero-based (0x6) + * formula: 2 * (multiplier + 1) * erase_time + */ +#define BFPT_WORD_10(X) \ + X(31, 11, kBfptNotSupported) & \ + X(10, 9, 0x0) & \ + X( 8, 4, 0x1d) & \ + X( 3, 0, 0x6) + +/** + * BFPT 11th Word + * -------------- + * [31:31]: Reserved + * [30:29]: Chip erase time units (256 ms, 0x1) + * [28:24]: Chip erase time count, zero-based (0x3) + * [23:23]: Additional byte program time units (us, 0x0) + * [22:19]: Additional byte program time count, zero-based (0x0) + * [18:18]: First byte program time unit (8 us, 0x1) + * [17:14]: First byte program time count, zero-based (0x3) + * [13:13]: Page program time unit (8 us, 0x0) + * [12: 8]: Page program time count, zero-based (0x1f) + * [ 7: 4]: Page size, 2^N (0x8) + * [ 3: 0]: Max program time multiplier, zero-based (0x1) + */ +#define BFPT_WORD_11(X) \ + X(30, 29, 0x1) & \ + X(28, 24, 0x3) & \ + X(23, 23, 0x0) & \ + X(22, 19, 0x0) & \ + X(18, 18, 0x1) & \ + X(17, 14, 0x3) & \ + X(13, 13, 0x0) & \ + X(12, 8, 0x1f) & \ + X( 7, 4, 0x8) & \ + X( 3, 0, 0x1) + +/** + * BFPT 12th Word + * -------------- + * [31: 9]: Erase/program suspend/resume (not supported, 0x0) + * [ 8: 8]: Reserved + * [ 7: 0]: Prohibited ops during suspend (not supported, 0x0) + */ +#define BFPT_WORD_12(X) \ + X(31, 9, kBfptNotSupported) & \ + X( 7, 0, kBfptNotSupported) + +/** + * BFPT 13th Word + * -------------- + * [31: 0]: Erase/program suspend/resume instructions (not supported, 0x0) + */ +#define BFPT_WORD_13(X) \ + X(31, 0, kBfptNotSupported) + +/** + * BFPT 14th Word + * -------------- + * [31:31]: Deep powerdown support (not supported, 0x1) + * [30: 8]: Deep powerdown instructions and delay (not supported, 0x0) + * [ 7: 2]: Busy polling (bit 0 using 0x05 instruction, 0x1) + * [ 1: 0]: Reserved + */ +#define BFPT_WORD_14(X) \ + X(31, 31, 0x1) & \ + X(30, 8, kBfptNotSupported) & \ + X( 7, 2, 0x1) + +/** + * BFPT 15th Word + * -------------- + * [31:24]: Reserved + * [23: 0]: Hold, QE, (4S-4S-4S), 0-4-4 (not supported, 0x0) + */ +#define BFPT_WORD_15(X) \ + X(23, 0, kBfptNotSupported) + +/** + * BFPT 16th Word + * -------------- + * [31:14]: 4-Byte addressing (not supported, 0x0) + * [13: 8]: Soft-reset (instruction 0xf0, 0x80) + * [ 7: 7]: Reserved + * [ 6: 0]: Status register (read-only, 0x0) + */ +#define BFPT_WORD_16(X) \ + X(31, 14, kBfptNotSupported) & \ + X(13, 8, 0x80) & \ + X( 6, 0, 0x0) + +/** + * BFPT 17th Word + * -------------- + * [31: 0]: Fast read (1S-8S-8S) (1S-1S-8S) (not supported, 0x0) + */ +#define BFPT_WORD_17(X) \ + X(31, 0, kBfptNotSupported) + +/** + * BFPT 18th Word + * -------------- + * [31, 0]: Data strobe, SPI protocol reset, etc. (not supported, 0x0) + * + * Note: Reserved fields of this word should be 0 (JESD216F 6.4.21). + */ +#define BFPT_WORD_18(X) \ + X(31, 0, kBfptNotSupported) + +/** + * BFPT 19th Word + * -------------- + * [31, 0]: Octable enable, (8D-8D-8D), 0-8-8 mode (not suported, 0x0) + * + * Note: Reserved fields of this word should be 0 (JESD216F 6.4.22). + */ +#define BFPT_WORD_19(X) \ + X(31, 0, kBfptNotSupported) + +/** + * BFPT 20th Word + * -------------- + * [31, 0]: Max (8S-8S-8S) (4D-4D-4D) (4S-4S-4S) speed + * (not supported, 0xffffffff) + */ +#define BFPT_WORD_20(X) \ + X(31, 0, UINT32_MAX) + +/** + * BFPT 21st Word + * -------------- + * [31, 0]: Fast read support for various modes (not supported, 0x0) + * + * Note: Reserved fields of this word should be 0 (JESD216F 6.4.24). + */ +#define BFPT_WORD_21(X) \ + X(31, 0, kBfptNotSupported) + +/** + * BFPT 22nd Word + * -------------- + * [31, 0]: Fast read (1S-1D-1D) (1S-2D-2D) (not supported, 0x0) + */ +#define BFPT_WORD_22(X) \ + X(31, 0, kBfptNotSupported) + +/** + * BFPT 23rd Word + * -------------- + * [31, 0]: Fast read (1S-4D-4D) (4S-2D-2D) (not supported, 0x0) + */ +#define BFPT_WORD_23(X) \ + X(31, 0, kBfptNotSupported) +// clang-format on + +const spi_device_sfdp_table_t kSpiDeviceSfdpTable = { + .sfdp_header = + { + .signature = kSfdpSignature, + .minor_revision = kSfdpMinorRevision, + .major_revision = kSfdpMajorRevision, + .param_count = kSfdpParamCount, + .access_protocol = kSfdpAccessProtocol, + }, + .bfpt_header = + { + .param_id_lsb = kBfptParamIdLsb, + .minor_revision = kBfptMinorRevision, + .major_revision = kBfptMajorRevision, + .table_word_count = kSpiDeviceBfptNumWords, + .table_pointer = {kBfptTablePointer}, + .param_id_msb = kBfptParamIdMsb, + }, + .bfpt = {{ + BFPT_WORD_1(BFPT_FIELD_VALUE), BFPT_WORD_2(BFPT_FIELD_VALUE), + BFPT_WORD_3(BFPT_FIELD_VALUE), BFPT_WORD_4(BFPT_FIELD_VALUE), + BFPT_WORD_5(BFPT_FIELD_VALUE), BFPT_WORD_6(BFPT_FIELD_VALUE), + BFPT_WORD_7(BFPT_FIELD_VALUE), BFPT_WORD_8(BFPT_FIELD_VALUE), + BFPT_WORD_9(BFPT_FIELD_VALUE), BFPT_WORD_10(BFPT_FIELD_VALUE), + BFPT_WORD_11(BFPT_FIELD_VALUE), BFPT_WORD_12(BFPT_FIELD_VALUE), + BFPT_WORD_13(BFPT_FIELD_VALUE), BFPT_WORD_14(BFPT_FIELD_VALUE), + BFPT_WORD_15(BFPT_FIELD_VALUE), BFPT_WORD_16(BFPT_FIELD_VALUE), + BFPT_WORD_17(BFPT_FIELD_VALUE), BFPT_WORD_18(BFPT_FIELD_VALUE), + BFPT_WORD_19(BFPT_FIELD_VALUE), BFPT_WORD_20(BFPT_FIELD_VALUE), + BFPT_WORD_21(BFPT_FIELD_VALUE), BFPT_WORD_22(BFPT_FIELD_VALUE), + BFPT_WORD_23(BFPT_FIELD_VALUE), + }}}; + +/** + * Configuration options for a SPI Flash command. + */ +typedef struct cmd_info { + /** + * Offset of the CMD_INFO register to set. + */ + uint32_t reg_offset; + /** + * Instruction code. + */ + uint8_t op_code; + /** + * Address. + * + * 3 bytes if true, no address if false. + */ + bool address; + /** + * Number of dummy cycles. + */ + uint8_t dummy_cycles; +} cmd_info_t; + +/** + * Configures the spi_device to handle the given command. + * + * @param cmd_info Configuration options for a SPI Flash command. + */ +static void cmd_info_set(cmd_info_t cmd_info) { + // CMD_INFO registers share the same layout, the code below uses the macros of + // the CMD_INFO_0 register. + uint32_t reg = bitfield_field32_write(0, SPI_DEVICE_CMD_INFO_0_OPCODE_0_FIELD, + cmd_info.op_code); + reg = bitfield_field32_write( + reg, SPI_DEVICE_CMD_INFO_0_ADDR_MODE_0_FIELD, + cmd_info.address ? SPI_DEVICE_CMD_INFO_0_ADDR_MODE_0_VALUE_ADDR3B + : SPI_DEVICE_CMD_INFO_0_ADDR_MODE_0_VALUE_ADDRDISABLED); + if (cmd_info.dummy_cycles > 0) { + // `DUMMY_SIZE` field is zero-based. + reg = bitfield_field32_write(reg, SPI_DEVICE_CMD_INFO_0_DUMMY_SIZE_0_FIELD, + cmd_info.dummy_cycles - 1); + reg = bitfield_bit32_write(reg, SPI_DEVICE_CMD_INFO_0_DUMMY_EN_0_BIT, true); + } + reg = bitfield_bit32_write(reg, SPI_DEVICE_CMD_INFO_0_VALID_0_BIT, true); + abs_mmio_write32(kBase + cmd_info.reg_offset, reg); +} + void spi_device_init(void) { // CPOL = 0, CPHA = 0, MSb-first TX and RX, 3-byte addressing. uint32_t reg = bitfield_bit32_write(0, SPI_DEVICE_CFG_CPOL_BIT, false); @@ -48,10 +499,32 @@ void spi_device_init(void) { abs_mmio_write32(kBase + SPI_DEVICE_JEDEC_ID_REG_OFFSET, reg); // Configure READ_JEDEC_ID command (CMD_INFO_3). - reg = bitfield_field32_write(0, SPI_DEVICE_CMD_INFO_3_OPCODE_3_FIELD, - kSpiDeviceCmdReadJedecId); - reg = bitfield_bit32_write(reg, SPI_DEVICE_CMD_INFO_3_VALID_3_BIT, true); - abs_mmio_write32(kBase + SPI_DEVICE_CMD_INFO_3_REG_OFFSET, reg); + cmd_info_set((cmd_info_t){ + .reg_offset = SPI_DEVICE_CMD_INFO_3_REG_OFFSET, + .op_code = kSpiDeviceCmdReadJedecId, + .address = false, + .dummy_cycles = 0, + }); + // Configure READ_SFDP command (CMD_INFO_4). + cmd_info_set((cmd_info_t){ + .reg_offset = SPI_DEVICE_CMD_INFO_4_REG_OFFSET, + .op_code = kSpiDeviceCmdReadSfdp, + .address = true, + .dummy_cycles = 8, + }); + + // Write SFDP table to the reserved region in spi_device buffer. + uint32_t dest = kSfdpAreaStartAddr; + const char *table = (const char *)&kSpiDeviceSfdpTable; + for (size_t i = 0; i < kSpiDeviceSfdpTableNumWords; ++i) { + abs_mmio_write32(dest, read_32(table)); + dest += sizeof(uint32_t); + table += sizeof(uint32_t); + } + // Fill the remaining space with `0xff`s. + for (; dest < kSfdpAreaEndAddr; dest += sizeof(uint32_t)) { + abs_mmio_write32(dest, UINT32_MAX); + } // Reset status register abs_mmio_write32(kBase + SPI_DEVICE_FLASH_STATUS_REG_OFFSET, 0); diff --git a/sw/device/silicon_creator/lib/drivers/spi_device.h b/sw/device/silicon_creator/lib/drivers/spi_device.h index e5ef4187f66da..2fe5d02889444 100644 --- a/sw/device/silicon_creator/lib/drivers/spi_device.h +++ b/sw/device/silicon_creator/lib/drivers/spi_device.h @@ -37,8 +37,29 @@ enum { * LSB of the 2-byte device ID. */ kSpiDeviceJedecDensity = 0x0a, + // TODO(#11740): Auto-generated macros for SFDP offset and size. /** - * READ_JECEC_ID command. + * Size of the SFDP area in spi_device buffer in bytes. + * + * spi_device provides 256 bytes for the SFDP table. + */ + kSpiDeviceSfdpAreaNumBytes = 256, + /** + * Offset of the SFDP area in spi_device buffer. + */ + kSpiDeviceSfdpAreaOffset = 0xc00, + /** + * Size of the JEDEC Basic Flash Parameter Table (BFPT) in words. + * + * Note: JESD261F 6.4.1 states that this + */ + kSpiDeviceBfptNumWords = 23, + /** + * Size of the SFDP table in words. + */ + kSpiDeviceSfdpTableNumWords = 27, + /** + * READ_JEDEC_ID command. * * This command is handled by the spi_device. Upon receiving this instruction, * spi_device sends `kSpiDeviceJedecContCodeCount` repetitions of @@ -46,8 +67,137 @@ enum { * of device id. */ kSpiDeviceCmdReadJedecId = 0x9f, + /** + * READ_SFDP command. + * + * This commond is handled by the spi_device. Upon receiving this instruction, + * 3 bytes of address (all zeroes), and 8 dummy cycles; spi_device sends + * `kSpiDeviceSfdpTable` from its buffer. + */ + kSpiDeviceCmdReadSfdp = 0x5a, + /** + * SECTOR_ERASE command. + */ + kSpiDeviceCmdSectorErase = 0x20, }; +/** + * SFDP header (JESD216F 6.2). + */ +typedef struct spi_device_sfdp_header { + /** + * 32-bit SFDP signature that indicates the presence of a SFDP table + * (JESD216F 6.2.1). + */ + uint32_t signature; + /** + * SFDP major revision number (JESD216F 6.2.2). + */ + uint8_t minor_revision; + /** + * SFDP minor revision number (JESD216F 6.2.2). + */ + uint8_t major_revision; + /** + * Number of parameter headers, zero-based (JESD216F 6.2.2). + */ + uint8_t param_count; + /** + * SFDP access protocol (JESD216F 6.2.3). + */ + uint8_t access_protocol; +} spi_device_sfdp_header_t; +OT_ASSERT_MEMBER_OFFSET(spi_device_sfdp_header_t, signature, 0); +OT_ASSERT_MEMBER_OFFSET(spi_device_sfdp_header_t, minor_revision, 4); +OT_ASSERT_MEMBER_OFFSET(spi_device_sfdp_header_t, major_revision, 5); +OT_ASSERT_MEMBER_OFFSET(spi_device_sfdp_header_t, param_count, 6); +OT_ASSERT_MEMBER_OFFSET(spi_device_sfdp_header_t, access_protocol, 7); +OT_ASSERT_SIZE(spi_device_sfdp_header_t, 8); + +/** + * SFDP parameter header (JESD216F 6.3). + */ +typedef struct spi_device_param_header { + /** + * LSB of the parameter ID that indicates parameter table ownership and type + * (JESD216F 6.3.1, 6.3.3). + */ + uint8_t param_id_lsb; + /** + * Parameter table minor revision number (JESD216F 6.3.1). + */ + uint8_t minor_revision; + /** + * Parameter table major revision number (JESD216F 6.3.1). + */ + uint8_t major_revision; + /** + * Length of the parameter table in words, one-based (JESD216F 6.3.1). + */ + uint8_t table_word_count; + /** + * Word-aligned byte offset of the corresponding parameter table from the + * start of the SFDP table (JESD216F 6.3.2). + */ + uint8_t table_pointer[3]; + /** + * MSB of the parameter ID that indicates parameter table ownership and type + * (JESD216F 6.3.2, 6.3.3). + */ + uint8_t param_id_msb; +} spi_device_param_header_t; +OT_ASSERT_MEMBER_OFFSET(spi_device_param_header_t, param_id_lsb, 0); +OT_ASSERT_MEMBER_OFFSET(spi_device_param_header_t, minor_revision, 1); +OT_ASSERT_MEMBER_OFFSET(spi_device_param_header_t, major_revision, 2); +OT_ASSERT_MEMBER_OFFSET(spi_device_param_header_t, table_word_count, 3); +OT_ASSERT_MEMBER_OFFSET(spi_device_param_header_t, table_pointer, 4); +OT_ASSERT_MEMBER_OFFSET(spi_device_param_header_t, param_id_msb, 7); +OT_ASSERT_SIZE(spi_device_param_header_t, 8); + +/** + * Basic Flash Parameter Table (BFPT) (JESD216F 6.4). + * + * This is a mandatory table defined by JEDEC that identifies some of the basic + * features of a SPI protocol flash memory device. + */ +typedef struct spi_device_bfpt { + uint32_t data[kSpiDeviceBfptNumWords]; +} spi_device_bfpt_t; +OT_ASSERT_SIZE(spi_device_bfpt_t, 92); +static_assert(sizeof(spi_device_bfpt_t) == + kSpiDeviceBfptNumWords * sizeof(uint32_t), + "`kSpiDeviceBfptNumWords` is incorrect"); + +/** + * SFDP table. + */ +typedef struct spi_device_sfdp_table { + /** + * SFDP header. + */ + spi_device_sfdp_header_t sfdp_header; + /** + * Parameter header for the Basic Flash Parameters Table (BFPT). + */ + spi_device_param_header_t bfpt_header; + /** + * Basic Flash Parameters Table (BFPT). + */ + spi_device_bfpt_t bfpt; +} spi_device_sfdp_table_t; +OT_ASSERT_MEMBER_OFFSET(spi_device_sfdp_table_t, sfdp_header, 0); +OT_ASSERT_MEMBER_OFFSET(spi_device_sfdp_table_t, bfpt_header, 8); +OT_ASSERT_MEMBER_OFFSET(spi_device_sfdp_table_t, bfpt, 16); +OT_ASSERT_SIZE(spi_device_sfdp_table_t, 108); +static_assert(sizeof(spi_device_sfdp_table_t) <= kSpiDeviceSfdpAreaNumBytes, + "SFDP table must fit in the space provided by spi_device"); +static_assert(sizeof(spi_device_sfdp_table_t) == + kSpiDeviceSfdpTableNumWords * sizeof(uint32_t), + "`kSpiDeviceSfdpTableNumWords` is incorrect"); + +// Note: Declared here to be able to use in tests. +extern const spi_device_sfdp_table_t kSpiDeviceSfdpTable; + /** * Initializes the spi_device in flash mode for bootstrap in mask ROM. * @@ -55,8 +205,10 @@ enum { * - CPOL = 0, CPHA = 0 (clock low in idle, data sampled on rising clock edge). * - MSb-first bit order for RX and TX. * - Flash mode with 3-byte addressing. + * - `kSpiDeviceSfdpTable` written to the SFDP area in the buffer. * - Commands: * - READ_JEDEC_ID + * - READ_SFDP */ void spi_device_init(void); diff --git a/sw/device/silicon_creator/lib/drivers/spi_device_unittest.cc b/sw/device/silicon_creator/lib/drivers/spi_device_unittest.cc index ea81244013359..e4baacc478a64 100644 --- a/sw/device/silicon_creator/lib/drivers/spi_device_unittest.cc +++ b/sw/device/silicon_creator/lib/drivers/spi_device_unittest.cc @@ -4,6 +4,7 @@ #include "sw/device/silicon_creator/lib/drivers/spi_device.h" +#include #include #include "gtest/gtest.h" @@ -60,6 +61,29 @@ TEST_F(InitTest, Init) { {SPI_DEVICE_CMD_INFO_3_VALID_3_BIT, 1}, }); + EXPECT_ABS_WRITE32( + base_ + SPI_DEVICE_CMD_INFO_4_REG_OFFSET, + { + {SPI_DEVICE_CMD_INFO_4_OPCODE_4_OFFSET, kSpiDeviceCmdReadSfdp}, + {SPI_DEVICE_CMD_INFO_4_ADDR_MODE_4_OFFSET, + SPI_DEVICE_CMD_INFO_0_ADDR_MODE_0_VALUE_ADDR3B}, + {SPI_DEVICE_CMD_INFO_4_DUMMY_SIZE_4_OFFSET, 7}, + {SPI_DEVICE_CMD_INFO_4_DUMMY_EN_4_BIT, 1}, + {SPI_DEVICE_CMD_INFO_4_VALID_4_BIT, 1}, + }); + + std::array + sfdp_buffer; + sfdp_buffer.fill(std::numeric_limits::max()); + std::memcpy(sfdp_buffer.data(), &kSpiDeviceSfdpTable, + sizeof(kSpiDeviceSfdpTable)); + uint32_t offset = + base_ + SPI_DEVICE_BUFFER_REG_OFFSET + kSpiDeviceSfdpAreaOffset; + for (size_t i = 0; i < sfdp_buffer.size(); ++i) { + EXPECT_ABS_WRITE32(offset, sfdp_buffer[i]); + offset += sizeof(uint32_t); + } + EXPECT_ABS_WRITE32(base_ + SPI_DEVICE_FLASH_STATUS_REG_OFFSET, 0); uint32_t control_reg = std::numeric_limits::max();