soc/intel/cmn/block/fast_spi: Introduce a DMA transfer queue
This commit implements a token-based queuing system to reduce delays
between consecutive DMA operations. The queuing mechanism aims to
improve overall throughput.
A Kconfig option FAST_SPI_DMA_QUEUE_SIZE defines the maximum number of
concurrent DMA transfers that can be queued and processed.
When DMA transfers are queued, a new thread is initiated to run
fast_spi_dma_main_thread(). This function consistently monitors for
available transfer tokens and performs DMA operations when tokens are in
the DMA_REQUESTED state. If there are no tokens in the desired state,
the thread_running flag is set to false, and the thread terminates
smoothly.
While it provides a barely perceptible boot time improvement with the
Panther Lake configuration, it makes the boot flow more consistent and
predictable.
TEST=On a Fatcat device with CBFS_PRELOAD enabled and set up, we observe
consecutive logs as follows, proving a new DMA transfer is issued
as soon as the previous one's completion is detected.
[DEBUG] Fast-SPI: transfer completed in 2574 us
[DEBUG] Fast-SPI: Reading 240 blocks via DMA
Change-Id: Ic5249362041cfa028885874b43893e1d7942ed94
Signed-off-by: Jeremy Compostella <jeremy.compostella@intel.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/88474
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Kapil Porwal <kapilporwal@google.com>
Reviewed-by: Subrata Banik <subratabanik@google.com>
Reviewed-by: Wonkyu Kim <wonkyu.kim@intel.com>
This commit is contained in:
parent
182ba52792
commit
eb1b5ee116
2 changed files with 122 additions and 8 deletions
|
|
@ -70,3 +70,11 @@ config FAST_SPI_DMA
|
||||||
callback. Although DMA transfers may not be significantly faster,
|
callback. Although DMA transfers may not be significantly faster,
|
||||||
they can free up CPU resources for other meaningful tasks in a
|
they can free up CPU resources for other meaningful tasks in a
|
||||||
multitasking environment (see COOP_MULTITASKING).
|
multitasking environment (see COOP_MULTITASKING).
|
||||||
|
|
||||||
|
config FAST_SPI_DMA_QUEUE_SIZE
|
||||||
|
int
|
||||||
|
depends on FAST_SPI_DMA
|
||||||
|
default 4
|
||||||
|
help
|
||||||
|
Defines the maximum number of concurrent DMA transfers that can
|
||||||
|
be queued and processed.
|
||||||
|
|
|
||||||
|
|
@ -55,11 +55,37 @@ union fast_spi_dma_status {
|
||||||
} fields;
|
} fields;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum transfer_state {
|
||||||
|
DMA_TOKEN_FREE,
|
||||||
|
DMA_REQUESTED,
|
||||||
|
DMA_IN_PROGRESS,
|
||||||
|
DMA_COMPLETE,
|
||||||
|
DMA_FAILED
|
||||||
|
};
|
||||||
|
|
||||||
|
struct fast_spi_dma_transfer {
|
||||||
|
enum transfer_state state;
|
||||||
|
uint32_t flash_offset;
|
||||||
|
uint32_t block_count;
|
||||||
|
void *buffer;
|
||||||
|
};
|
||||||
|
|
||||||
#define FAST_SPI_DMA_BLOCK_ALIGN KiB
|
#define FAST_SPI_DMA_BLOCK_ALIGN KiB
|
||||||
#define FAST_SPI_DMA_TIMEOUT_MSEC 1000
|
#define FAST_SPI_DMA_TIMEOUT_MSEC 1000
|
||||||
#define FAST_SPI_DMA_DELAY_STEP_USEC 200
|
#define FAST_SPI_DMA_DELAY_STEP_USEC 200
|
||||||
#define FAST_SPI_DEV DEV_PTR(fast_spi)
|
#define FAST_SPI_DEV DEV_PTR(fast_spi)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This array holds the transfer tokens used for managing DMA operations with the Fast
|
||||||
|
* SPI interface. Each token represents a transfer request. The number of tokens is
|
||||||
|
* determined by the FAST_SPI_DMA_QUEUE_SIZE kconfig, which defines the maximum number of
|
||||||
|
* concurrent DMA transfers that can be queued and processed.
|
||||||
|
*
|
||||||
|
* The tokens are utilized by the DMA transfer management functions to execute transfers
|
||||||
|
* in a sequential manner.
|
||||||
|
*/
|
||||||
|
static struct fast_spi_dma_transfer tokens[CONFIG_FAST_SPI_DMA_QUEUE_SIZE];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The bios_top variable stores the offset indicating the end of the BIOS region within
|
* The bios_top variable stores the offset indicating the end of the BIOS region within
|
||||||
* the SPI flash memory map. It is calculated based on the size and arrangement of memory
|
* the SPI flash memory map. It is calculated based on the size and arrangement of memory
|
||||||
|
|
@ -77,11 +103,12 @@ static uint32_t bios_top;
|
||||||
static const struct region_device_ops *mmap_ops;
|
static const struct region_device_ops *mmap_ops;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The fast_spi_dma_mutex is used to ensure thread-safe access to DMA operations. This
|
* This boolean variable is used to track whether the Fast SPI DMA thread is actively
|
||||||
* synchronization mechanism guarantees that only one thread can perform DMA operations
|
* running. It is set to true when the thread is executing and handling DMA transfer
|
||||||
* at a time.
|
* requests. When no transfer tokens are in the requested state, the thread sets this
|
||||||
|
* variable to false and exits gracefully.
|
||||||
*/
|
*/
|
||||||
static struct thread_mutex fast_spi_dma_mutex;
|
static bool thread_running;
|
||||||
|
|
||||||
static inline uint32_t fast_spi_read(enum fast_spi_dma_register reg)
|
static inline uint32_t fast_spi_read(enum fast_spi_dma_register reg)
|
||||||
{
|
{
|
||||||
|
|
@ -224,6 +251,75 @@ static enum cb_err fast_spi_dma_mmap_readat(const struct region_device *rd, void
|
||||||
return CB_SUCCESS;
|
return CB_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct fast_spi_dma_transfer *fast_spi_dma_find_token(enum transfer_state state)
|
||||||
|
{
|
||||||
|
struct fast_spi_dma_transfer *cur;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0, cur = tokens; i < ARRAY_SIZE(tokens); i++, cur++)
|
||||||
|
if (cur->state == state)
|
||||||
|
return cur;
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function operates as the core logic for handling Fast SPI DMA transfers. It
|
||||||
|
* continuously checks for transfer tokens in the DMA_REQUESTED state and executes the
|
||||||
|
* associated DMA transfer operations. If no tokens are found in the requested state, it
|
||||||
|
* sets the thread_running flag to false and exits the thread gracefully.
|
||||||
|
*/
|
||||||
|
static enum cb_err fast_spi_dma_main_thread(void *arg)
|
||||||
|
{
|
||||||
|
struct fast_spi_dma_transfer *token;
|
||||||
|
|
||||||
|
thread_running = true;
|
||||||
|
for (;;) {
|
||||||
|
token = fast_spi_dma_find_token(DMA_REQUESTED);
|
||||||
|
if (!token)
|
||||||
|
break;
|
||||||
|
|
||||||
|
token->state = DMA_IN_PROGRESS;
|
||||||
|
enum cb_err ret = fast_spi_do_dma_transfer(token->buffer,
|
||||||
|
token->block_count,
|
||||||
|
token->flash_offset);
|
||||||
|
token->state = ret == CB_SUCCESS ? DMA_COMPLETE : DMA_FAILED;
|
||||||
|
}
|
||||||
|
thread_running = false;
|
||||||
|
|
||||||
|
return CB_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function is responsible for allocating and preparing a new DMA transfer token to
|
||||||
|
* perform a data transfer from the SPI flash to a buffer. It also ensures that the DMA
|
||||||
|
* management thread is running. If the thread is not running, it attempts to start it.
|
||||||
|
*/
|
||||||
|
static struct fast_spi_dma_transfer *fast_spi_dma_queue_transfer(void *buffer,
|
||||||
|
uint32_t block_count,
|
||||||
|
uint32_t flash_offset)
|
||||||
|
{
|
||||||
|
struct fast_spi_dma_transfer *token = fast_spi_dma_find_token(DMA_TOKEN_FREE);
|
||||||
|
if (!token) {
|
||||||
|
printk(BIOS_ERR, "Fast-SPI: Could not find a new transfer token\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
token->buffer = buffer;
|
||||||
|
token->flash_offset = flash_offset;
|
||||||
|
token->block_count = block_count;
|
||||||
|
token->state = DMA_REQUESTED;
|
||||||
|
|
||||||
|
printk(BIOS_DEBUG, "Fast-SPI: %d blocks transfer token queued\n", block_count);
|
||||||
|
|
||||||
|
/* Ensure the DMA transfer thread is running. */
|
||||||
|
static struct thread_handle handle;
|
||||||
|
if (!thread_running && thread_run(&handle, fast_spi_dma_main_thread, NULL) != 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Perform DMA transfer from SPI flash to a buffer.
|
* Perform DMA transfer from SPI flash to a buffer.
|
||||||
*
|
*
|
||||||
|
|
@ -266,6 +362,8 @@ static enum cb_err fast_spi_dma_mmap_readat(const struct region_device *rd, void
|
||||||
static ssize_t fast_spi_dma_transfer(const struct region_device *rd, void *b,
|
static ssize_t fast_spi_dma_transfer(const struct region_device *rd, void *b,
|
||||||
size_t offset, size_t size)
|
size_t offset, size_t size)
|
||||||
{
|
{
|
||||||
|
enum cb_err ret;
|
||||||
|
|
||||||
/* Calculate aligned offset and flash offset for DMA transfer. */
|
/* Calculate aligned offset and flash offset for DMA transfer. */
|
||||||
uint32_t aligned_offset = ALIGN_UP(offset, FAST_SPI_DMA_BLOCK_ALIGN);
|
uint32_t aligned_offset = ALIGN_UP(offset, FAST_SPI_DMA_BLOCK_ALIGN);
|
||||||
uint32_t flash_offset = (bios_top - aligned_offset) / FAST_SPI_DMA_BLOCK_ALIGN;
|
uint32_t flash_offset = (bios_top - aligned_offset) / FAST_SPI_DMA_BLOCK_ALIGN;
|
||||||
|
|
@ -273,10 +371,14 @@ static ssize_t fast_spi_dma_transfer(const struct region_device *rd, void *b,
|
||||||
uint32_t block_count = (size - early_bytes) / FAST_SPI_DMA_BLOCK_ALIGN;
|
uint32_t block_count = (size - early_bytes) / FAST_SPI_DMA_BLOCK_ALIGN;
|
||||||
|
|
||||||
/* Perform DMA transfer. */
|
/* Perform DMA transfer. */
|
||||||
thread_mutex_lock(&fast_spi_dma_mutex);
|
struct fast_spi_dma_transfer *token = fast_spi_dma_queue_transfer(b, block_count,
|
||||||
enum cb_err ret = fast_spi_do_dma_transfer(b, block_count, flash_offset);
|
flash_offset);
|
||||||
thread_mutex_unlock(&fast_spi_dma_mutex);
|
if (!token)
|
||||||
if (ret != CB_SUCCESS) {
|
return -1;
|
||||||
|
/* Wait for completion */
|
||||||
|
while (token->state == DMA_REQUESTED || token->state == DMA_IN_PROGRESS)
|
||||||
|
udelay(FAST_SPI_DMA_DELAY_STEP_USEC);
|
||||||
|
if (token->state != DMA_COMPLETE) {
|
||||||
printk(BIOS_ERR, "Fast-SPI: DMA transfer failed, falling back to memory map operation\n");
|
printk(BIOS_ERR, "Fast-SPI: DMA transfer failed, falling back to memory map operation\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
@ -302,6 +404,10 @@ static ssize_t fast_spi_dma_transfer(const struct region_device *rd, void *b,
|
||||||
return transferred;
|
return transferred;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* We are done with the transfer token. */
|
||||||
|
token->state = DMA_TOKEN_FREE;
|
||||||
|
|
||||||
return transferred;
|
return transferred;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue