Skip to content

Commit

Permalink
flash/nor: add DesignWare SPI controller driver
Browse files Browse the repository at this point in the history
Driver for DesignWare SPI controller, found on many SoCs (see compatible
list in Linux device tree bindings
Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml). This
implementation only supports MIPS as it was the only one available for the
tests, however, adding support for other architectures should require only
few adjustments. Driver relies on flash/nor/spi.h to find Flash chip info.
Driver internal functions support 24bit addressing mode, but due to
limitations of flash/nor/spi.h, it is not used. The repoted writing speed
is about 60kb/s.
Lint, sanitizer and valgraind repoted warnings were not related to the
driver.

Change-Id: Id3df5626ab88055f034f74f274823051dedefeb1
Signed-off-by: Sergey Matsievskiy <[email protected]>
  • Loading branch information
Sergey Matsievskiy committed Sep 18, 2024
1 parent fd7b66c commit 8d55de7
Show file tree
Hide file tree
Showing 14 changed files with 2,562 additions and 2 deletions.
33 changes: 33 additions & 0 deletions contrib/loaders/flash/dw-spi/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
TOOLCHAIN:=mipsel-linux-gnu-
CC:=$(TOOLCHAIN)gcc
OBJCOPY:=$(TOOLCHAIN)objcopy
CFLAGS:=-O2 -Wall -Wextra -fpic
SRC=dw-spi.c
OBJ=$(patsubst %.c, %.o,$(SRC))

# sparx-iv
ifeq ($(TOOLCHAIN),mipsel-linux-gnu-)
CFLAGS+= -march=24kec
endif

all: \
$(TOOLCHAIN)transaction.inc \
$(TOOLCHAIN)erase.inc \
$(TOOLCHAIN)check_fill.inc \
$(TOOLCHAIN)program.inc \
$(TOOLCHAIN)read.inc

$(TOOLCHAIN)%.bin: $(OBJ)
$(OBJCOPY) --dump-section .$*=$@ $<

%.inc: %.bin
xxd -i > $@ < $<

.PHONY: clean
clean:
rm -rf .ccls-cache
find . \( \
-iname "*.o" \
-o -iname "*.bin" \
-o -iname "*.inc" \
\) -delete
260 changes: 260 additions & 0 deletions contrib/loaders/flash/dw-spi/dw-spi.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,260 @@
// SPDX-License-Identifier: GPL-2.0-or-later

#include "dw-spi.h"

#include "../../../../src/flash/nor/dw-spi-helper.h"

/**
* @brief Generic flash transaction.
*
* nCS is only asserted when TX FIFO is not empty.
* Must supply TX FIFO with data for the whole duration of transaction.
*
* @param[in] arg Function arguments.
* @retval 0 Success. Returned in argument register.
* @retval 1 Failure. Returned in argument register.
*/
__attribute__((section(".transaction"))) void
transaction(struct dw_spi_transaction *arg)
{
register uint8_t *buffer_tx = (uint8_t *)arg->buffer;
register uint8_t *buffer_rx = buffer_tx;
register uint32_t size = arg->size;
register volatile uint8_t *status = (uint8_t *)arg->status_reg;
register volatile uint8_t *data = (uint8_t *)arg->data_reg;

wait_tx_finish(status);
flush_rx(status, data);

for (; size > 0; size--) {
wait_tx_available(status);
push_byte(data, *buffer_tx++);
if (!tx_in_progress(status)) {
RET_ERROR;
BKPT;
return;
}
if (arg->read_flag && rx_available(status))
*buffer_rx++ = rcv_byte(data);
}

if (arg->read_flag) {
while (buffer_rx < buffer_tx) {
wait_rx_available(status);
*buffer_rx++ = rcv_byte(data);
}
}

RET_OK;
BKPT;
}

/**
* @brief Check flash sectors are filled with pattern. Primary use for
* checking sector erase state.
*
* nCS is only asserted when TX FIFO is not empty.
* Must supply TX FIFO with data for the whole duration of transaction.
*
* @param[in] arg Function arguments.
* @retval 0 Success. Returned in argument register.
* @retval 1 Failure. Returned in argument register.
*/
__attribute__((section(".check_fill"))) void
check_fill(struct dw_spi_check_fill *arg)
{
register uint32_t tx_size;
register uint32_t rx_size;
register uint32_t dummy_count;
register uint8_t filled;
register uint8_t *fill_status_array = (uint8_t *)arg->fill_status_array;
register volatile uint8_t *status = (uint8_t *)arg->status_reg;
register volatile uint8_t *data = (uint8_t *)arg->data_reg;

for (; arg->sector_count > 0; arg->sector_count--,
arg->address += arg->sector_size,
fill_status_array++) {
wait_tx_finish(status);
flush_rx(status, data);

push_byte(data, arg->read_cmd);
if (arg->four_byte_mode) {
dummy_count = 5;
push_u32(status, data, arg->address);
} else {
dummy_count = 4;
push_u24(status, data, arg->address);
}

for (tx_size = arg->sector_size, rx_size = arg->sector_size, filled = 1;
tx_size > 0; tx_size--) {
wait_tx_available(status);
if (!tx_in_progress(status)) {
RET_ERROR;
BKPT;
return;
}
push_byte(data, 0); // dummy write
if (rx_available(status)) {
if (dummy_count > 0) {
rcv_byte(data);
dummy_count--;
} else {
if (rcv_byte(data) != arg->pattern) {
filled = 0;
break;
}
rx_size--;
}
}
}
if (filled) {
for (; rx_size > 0; rx_size--) {
wait_rx_available(status);
if (rcv_byte(data) != arg->pattern) {
filled = 0;
break;
}
}
}
*fill_status_array = filled;
}

RET_OK;
BKPT;
}

/**
* @brief Erase flash sectors.
*
* @param[in] arg Function arguments.
* @retval 0 Success. Returned in argument register.
* @retval 1 Failure. Returned in argument register.
*/
__attribute__((section(".erase"))) void
erase(struct dw_spi_erase *arg)
{
register uint32_t address = arg->address;
register uint32_t count = arg->sector_count;
register volatile uint8_t *status = (uint8_t *)arg->status_reg;
register volatile uint8_t *data = (uint8_t *)arg->data_reg;

for (; count > 0; count--, address += arg->sector_size) {
write_enable(status, data, arg->write_enable_cmd);
wait_write_enable(status, data, arg->read_status_cmd,
arg->write_enable_mask);

erase_sector(status, data, arg->erase_sector_cmd, address,
arg->four_byte_mode);
wait_busy(status, data, arg->read_status_cmd, arg->busy_mask);
}

RET_OK;
BKPT;
}

/**
* @brief Flash program.
*
* @param[in] arg Function arguments.
* @retval 0 Success. Returned in argument register.
* @retval 1 Failure. Returned in argument register.
*/
__attribute__((section(".program"))) void
program(struct dw_spi_program *arg)
{
register uint8_t *buffer = (uint8_t *)arg->buffer;
register uint32_t buffer_size = arg->buffer_size;
register volatile uint8_t *status = (uint8_t *)arg->status_reg;
register volatile uint8_t *data = (uint8_t *)arg->data_reg;
register uint32_t page_size;

while (buffer_size > 0) {
write_enable(status, data, arg->write_enable_cmd);
wait_write_enable(status, data, arg->read_status_cmd,
arg->write_enable_mask);

wait_tx_finish(status);

push_byte(data, arg->program_cmd);
if (arg->four_byte_mode)
push_u32(status, data, arg->address);
else
push_u24(status, data, arg->address);
for (page_size = MIN(arg->page_size, buffer_size); page_size > 0;
page_size--, buffer_size--) {
wait_tx_available(status);
push_byte(data, *buffer++);
}
arg->address += arg->page_size;
wait_busy(status, data, arg->read_status_cmd, arg->busy_mask);
}

RET_OK;
BKPT;
}

/**
* @brief Read data from flash.
*
* nCS is only asserted when TX FIFO is not empty.
* Must supply TX FIFO with data for the whole duration of transaction.
*
* @param[in] arg Function arguments.
* @retval 0 Success. Returned in argument register.
* @retval 1 Failure. Returned in argument register.
*/
__attribute__((section(".read"))) void
read(struct dw_spi_read *arg)
{
register uint32_t tx_size = arg->buffer_size;
register uint32_t rx_size = arg->buffer_size;
register uint32_t dummy_count;
register uint8_t *buffer = (uint8_t *)arg->buffer;
register volatile uint8_t *status = (uint8_t *)arg->status_reg;
register volatile uint8_t *data = (uint8_t *)arg->data_reg;

wait_tx_finish(status);
flush_rx(status, data);

push_byte(data, arg->read_cmd);
if (arg->four_byte_mode) {
dummy_count = 5;
push_u32(status, data, arg->address);
} else {
dummy_count = 4;
push_u24(status, data, arg->address);
}

for (; tx_size > 0; tx_size--) {
wait_tx_available(status);
if (!tx_in_progress(status)) {
RET_ERROR;
BKPT;
return;
}
push_byte(data, 0); // dummy write
if (rx_available(status)) {
if (dummy_count > 0) {
rcv_byte(data);
dummy_count--;
} else {
*buffer++ = rcv_byte(data);
rx_size--;
}
}
}
while (rx_size > 0) {
wait_rx_available(status);
if (dummy_count > 0) {
rcv_byte(data);
dummy_count--;
} else {
*buffer++ = rcv_byte(data);
rx_size--;
}
}

RET_OK;
BKPT;
}
Loading

0 comments on commit 8d55de7

Please sign in to comment.