From: openocd-gerrit <ope...@us...> - 2025-01-31 03:26:29
|
This is an automated email from the git hooks/post-receive script. It was generated because a ref change was pushed to the repository containing the project "Main OpenOCD repository". The branch, master has been updated via eb6f2745b7d9924d0dddeab91c1743867c4e812c (commit) from ce38758e3df929d5b27947ab7b987cc319a628aa (commit) Those revisions listed above that are new to this repository have not appeared on any other notification email; so we list those revisions in full, below. - Log ----------------------------------------------------------------- commit eb6f2745b7d9924d0dddeab91c1743867c4e812c Author: Sergey Matsievskiy <mat...@gm...> Date: Wed Sep 18 20:12:48 2024 +0300 flash/nor: add DesignWare SPI controller driver 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 reported writing speed is about 60kb/s. Lint, sanitizer and valgrind reported warnings were not related to the driver. Change-Id: Id3df5626ab88055f034f74f274823051dedefeb1 Signed-off-by: Sergey Matsievskiy <mat...@gm...> Reviewed-on: https://review.openocd.org/c/openocd/+/8400 Tested-by: jenkins Reviewed-by: Tomas Vanek <va...@fb...> diff --git a/contrib/loaders/flash/dw-spi/Makefile b/contrib/loaders/flash/dw-spi/Makefile new file mode 100644 index 000000000..e86827882 --- /dev/null +++ b/contrib/loaders/flash/dw-spi/Makefile @@ -0,0 +1,35 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +TOOLCHAIN:=mipsel-linux-gnu- +CC:=$(TOOLCHAIN)gcc +OBJCOPY:=$(TOOLCHAIN)objcopy +CFLAGS:=-O2 -Wall -Wextra -fpic -Wno-int-to-pointer-cast +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 diff --git a/contrib/loaders/flash/dw-spi/dw-spi.c b/contrib/loaders/flash/dw-spi/dw-spi.c new file mode 100644 index 000000000..66b743913 --- /dev/null +++ b/contrib/loaders/flash/dw-spi/dw-spi.c @@ -0,0 +1,246 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** + * @file + * Helper functions for DesignWare SPI Core driver. + * These helpers are loaded into CPU and execute Flash manipulation algorithms + * at full CPU speed. Due to inability to control nCS pin, this is the only way + * to communicate with Flash chips connected via DW SPI serial interface. + * + * In order to avoid using stack, all functions used in helpers are inlined. + * Software breakpoints are used to terminate helpers. + * + * Pushing byte to TX FIFO does not make byte immediately available in RX FIFO + * and nCS is only asserted when TX FIFO is not empty. General approach is to + * fill TX FIFO with as many bytes as possible, at the same time reading + * available bytes from RX FIFO. + * + * This file contains helper functions. + */ + +#include "dw-spi.h" + +#include "../../../../src/flash/nor/dw-spi-helper.h" + +/** + * @brief Generic flash transaction. + * + * @param[in] arg: Function arguments. + */ +__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--) { + send_u8(status, data, *buffer_tx++); + if (arg->read_flag && rx_available(status)) + *buffer_rx++ = rcv_byte(data); + } + + // Pushed all data to TX FIFO. Read bytes left in RX FIFO. + if (arg->read_flag) { + while (buffer_rx < buffer_tx) { + wait_rx_available(status); + *buffer_rx++ = rcv_byte(data); + } + } + + RETURN; +} + +/** + * @brief Check flash sectors are filled with pattern. Primary use for + * checking sector erase state. + * + * @param[in] arg: Function arguments. + */ +__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); + + /* + * Command byte and address bytes make up for dummy_count number of + * bytes, that must be skipped in RX FIFO before actual data arrives. + */ + send_u8(status, data, arg->read_cmd); + if (arg->four_byte_mode) { + dummy_count = 1 + 4; // Command byte + 4 address bytes + send_u32(status, data, arg->address); + } else { + dummy_count = 1 + 3; // Command byte + 3 address bytes + send_u24(status, data, arg->address); + } + + for (tx_size = arg->sector_size, rx_size = arg->sector_size, filled = 1; + tx_size > 0; tx_size--) { + send_u8(status, data, 0); // Dummy write to push out read data. + if (rx_available(status)) { + if (dummy_count > 0) { + // Read data not arrived yet. + 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; + } + + RETURN; +} + +/** + * @brief Erase flash sectors. + * + * @param[in] arg: Function arguments. + */ +__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); + } + + RETURN; +} + +/** + * @brief Flash program. + * + * @param[in] arg: Function arguments. + */ +__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); + + send_u8(status, data, arg->program_cmd); + if (arg->four_byte_mode) + send_u32(status, data, arg->address); + else + send_u24(status, data, arg->address); + + for (page_size = MIN(arg->page_size, buffer_size); page_size > 0; + page_size--, buffer_size--) { + send_u8(status, data, *buffer++); + } + arg->address += arg->page_size; + wait_busy(status, data, arg->read_status_cmd, arg->busy_mask); + } + + RETURN; +} + +/** + * @brief Read data from flash. + * + * @param[in] arg: Function arguments. + */ +__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); + + /* + * Command byte and address bytes make up for dummy_count number of + * bytes, that must be skipped in RX FIFO before actual data arrives. + */ + send_u8(status, data, arg->read_cmd); + if (arg->four_byte_mode) { + dummy_count = 1 + 4; // Command byte + 4 address bytes + send_u32(status, data, arg->address); + } else { + dummy_count = 1 + 3; // Command byte + 3 address bytes + send_u24(status, data, arg->address); + } + + for (; tx_size > 0; tx_size--) { + send_u8(status, data, 0); // Dummy write to push out read data. + 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) { + // Read data not arrived yet. + rcv_byte(data); + dummy_count--; + } else { + *buffer++ = rcv_byte(data); + rx_size--; + } + } + + RETURN; +} diff --git a/contrib/loaders/flash/dw-spi/dw-spi.h b/contrib/loaders/flash/dw-spi/dw-spi.h new file mode 100644 index 000000000..9efa768e6 --- /dev/null +++ b/contrib/loaders/flash/dw-spi/dw-spi.h @@ -0,0 +1,313 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/** + * @file + * Helper functions for DesignWare SPI Core driver. + * These helpers are loaded into CPU and execute Flash manipulation algorithms + * at full CPU speed. Due to inability to control nCS pin, this is the only way + * to communicate with Flash chips connected via DW SPI serial interface. + * + * In order to avoid using stack, all functions used in helpers are inlined. + * Software breakpoints are used to terminate helpers. + * + * This file contains functions, common to helpers. + */ + +#ifndef _DW_SPI_H_ +#define _DW_SPI_H_ + +#include <stdint.h> +#include <sys/param.h> + +#include "../../../../src/helper/types.h" + +/** + * @brief SI busy status bit. + * + * Set when serial transfer is in progress, cleared when master is idle or + * disabled. + */ +#define DW_SPI_STATUS_BUSY 0x01 + +/** + * @brief SI TX FIFO not full status bit. + * + * Set when TX FIFO has room for one or more data-word. + */ +#define DW_SPI_STATUS_TFNF 0x02 + +/** + * @brief SI TX FIFO empty status bit. + */ +#define DW_SPI_STATUS_TFE 0x04 + +/** + * @brief SI RX FIFO not empty status bit. + */ +#define DW_SPI_STATUS_RFNE 0x08 + +/** + * @brief Return from helper function. + */ +#define RETURN \ + do { \ + asm("sdbbp\n\t"); \ + return; \ + } while (0) + +/** + * @brief Append byte to TX FIFO. + * + * For each transferred byte, DW SPI controller receives a byte into RX FIFO. + * Slave data are read by pushing dummy bytes to TX FIFO. + * + * @param[in] dr: Pointer to DR register. + * @param[in] byte: Data to push. + */ +__attribute__((always_inline)) static inline void +_send_byte(volatile uint8_t *dr, uint8_t byte) +{ + *dr = byte; +} + +/** + * @brief Get byte from RX FIFO. + * + * Reading RX byte removes it from RX FIFO. + * + * @param[in] dr: Pointer to DR register. + * @return RX FIFO byte. + */ +__attribute__((always_inline)) static inline uint8_t +rcv_byte(volatile uint8_t *dr) +{ + return *dr; +} + +/** + * @brief Check transmission is currently in progress. + * + * @param[in] sr: Pointer to SR register. + * @retval 1: Transmission is in progress. + * @retval 0: Controller is idle or off. + */ +__attribute__((always_inline)) static inline int +tx_in_progress(volatile uint8_t *sr) +{ + return (*sr ^ DW_SPI_STATUS_TFE) & (DW_SPI_STATUS_BUSY | DW_SPI_STATUS_TFE); +} + +/** + * @brief Wait for controller to finish previous transaction. + * + * @param[in] sr: Pointer to SR register. + */ +__attribute__((always_inline)) static inline void +wait_tx_finish(volatile uint8_t *sr) +{ + while (tx_in_progress(sr)) + ; +} + +/** + * @brief Wait for room in TX FIFO. + * + * @param[in] sr: Pointer to SR register. + */ +__attribute__((always_inline)) static inline void +wait_tx_available(volatile uint8_t *sr) +{ + while (!(*sr & DW_SPI_STATUS_TFNF)) + ; +} + +/** + * @brief Check for data available in RX FIFO. + * + * @param[in] sr: Pointer to SR register. + * @retval 1: Data available. + * @retval 0: No data available. + */ +__attribute__((always_inline)) static inline int +rx_available(volatile uint8_t *sr) +{ + return *sr & DW_SPI_STATUS_RFNE; +} + +/** + * @brief Wait for data in RX FIFO. + * + * @param[in] sr: Pointer to SR register. + */ +__attribute__((always_inline)) static inline void +wait_rx_available(volatile uint8_t *sr) +{ + while (!rx_available(sr)) + ; +} + +/** + * @brief Flush RX FIFO. + * + * @param[in] sr: Pointer to SR register. + * @param[in] dr: Pointer to DR register. + */ +__attribute__((always_inline)) static inline void +flush_rx(volatile uint8_t *sr, volatile uint8_t *dr) +{ + while (*sr & DW_SPI_STATUS_RFNE) + *dr; +} + +/** + * @brief Append variable number of bytes to TX FIFO. + * + * @param[in] sr: Pointer to SR register. + * @param[in] dr: Pointer to DR register. + * @param[in] word: Data to append. + * @param[in] bytes: Number of bytes to append. + */ +__attribute__((always_inline)) static inline void +_send_bytes(volatile uint8_t *sr, volatile uint8_t *dr, uint32_t word, + int bytes) +{ + for (register int i = bytes - 1; i >= 0; i--) { + wait_tx_available(sr); + _send_byte(dr, (word >> (i * 8)) & 0xff); + } +} + +/** + * @brief Append 8 bit value to TX FIFO. + * + * @param[in] sr: Pointer to SR register. + * @param[in] dr: Pointer to DR register. + * @param[in] word: Data to push. + */ +__attribute__((always_inline)) static inline void +send_u8(volatile uint8_t *sr, volatile uint8_t *dr, uint8_t byte) +{ + wait_tx_available(sr); + _send_byte(dr, byte); +} + +/** + * @brief Append 24 bit value to TX FIFO. + * + * Used to send Flash addresses in 24 bit mode. + * + * @param[in] sr: Pointer to SR register. + * @param[in] dr: Pointer to DR register. + * @param[in] word: Data to push. + */ +__attribute__((always_inline)) static inline void +send_u24(volatile uint8_t *sr, volatile uint8_t *dr, uint32_t word) +{ + _send_bytes(sr, dr, word, 3); +} + +/** + * @brief Append 32 bit value to TX FIFO. + * + * @param[in] sr: Pointer to SR register. + * @param[in] dr: Pointer to DR register. + * @param[in] word: Data to push. + */ +__attribute__((always_inline)) static inline void +send_u32(volatile uint8_t *sr, volatile uint8_t *dr, uint32_t word) +{ + _send_bytes(sr, dr, word, 4); +} + +/** + * @brief Read chip status register. + * + * @param[in] sr: Pointer to SR register. + * @param[in] dr: Pointer to DR register. + * @param[in] stat_cmd: Read status command. + * @return Chip status. + */ +__attribute__((always_inline)) static inline uint8_t +read_status(volatile uint8_t *sr, volatile uint8_t *dr, uint8_t stat_cmd) +{ + wait_tx_finish(sr); + flush_rx(sr, dr); + /* + * Don't bother with wait_tx_available() as TX FIFO is empty + * and we only send two bytes. + */ + _send_byte(dr, stat_cmd); + _send_byte(dr, 0); // Dummy write to push out read data. + wait_rx_available(sr); + rcv_byte(dr); // Dummy read to skip command byte. + wait_rx_available(sr); + return rcv_byte(dr); +} + +/** + * @brief Enable Flash chip write. + * + * @param[in] sr: Pointer to SR register. + * @param[in] dr: Pointer to DR register. + * @param[in] we_cmd: Write enable command. + */ +__attribute__((always_inline)) static inline void +write_enable(volatile uint8_t *sr, volatile uint8_t *dr, uint8_t we_cmd) +{ + wait_tx_finish(sr); + _send_byte(dr, we_cmd); +} + +/** + * @brief Erase Flash sector. + * + * @param[in] sr: Pointer to SR register. + * @param[in] dr: Pointer to DR register. + * @param[in] erase_cmd: Erase sector cmd. + * @param[in] address: Sector address. + * @param[in] four_byte_mode: Device is in 32 bit mode flag. + */ +__attribute__((always_inline)) static inline void +erase_sector(volatile uint8_t *sr, volatile uint8_t *dr, uint8_t erase_cmd, + uint32_t address, uint8_t four_byte_mode) +{ + wait_tx_finish(sr); + _send_byte(dr, erase_cmd); + if (four_byte_mode) + send_u32(sr, dr, address); + else + send_u24(sr, dr, address); +} + +/** + * @brief Wait for write enable flag. + * + * @param[in] sr: Pointer to SR register. + * @param[in] dr: Pointer to DR register. + * @param[in] stat_cmd: Read status command. + * @param[in] we_mask: Write enable status mask. + */ +__attribute__((always_inline)) static inline void +wait_write_enable(volatile uint8_t *sr, volatile uint8_t *dr, uint8_t stat_cmd, + uint8_t we_mask) +{ + while (!(read_status(sr, dr, stat_cmd) & we_mask)) + ; +} + +/** + * @brief Wait while flash is busy. + * + * @param[in] sr: Pointer to SR register. + * @param[in] dr: Pointer to DR register. + * @param[in] stat_cmd: Read status command. + * @param[in] busy_mask: Flash busy mask. + */ +__attribute__((always_inline)) static inline void +wait_busy(volatile uint8_t *sr, volatile uint8_t *dr, uint8_t stat_cmd, + uint8_t busy_mask) +{ + while (read_status(sr, dr, stat_cmd) & busy_mask) + ; +} + +#endif // _DW_SPI_H_ diff --git a/contrib/loaders/flash/dw-spi/mipsel-linux-gnu-check_fill.inc b/contrib/loaders/flash/dw-spi/mipsel-linux-gnu-check_fill.inc new file mode 100644 index 000000000..94c732fe5 --- /dev/null +++ b/contrib/loaders/flash/dw-spi/mipsel-linux-gnu-check_fill.inc @@ -0,0 +1,39 @@ + 0x0b, 0x00, 0x82, 0x88, 0x1f, 0x00, 0x8a, 0x88, 0x0f, 0x00, 0x83, 0x88, + 0x17, 0x00, 0x86, 0x88, 0x08, 0x00, 0x82, 0x98, 0x1c, 0x00, 0x8a, 0x98, + 0x0c, 0x00, 0x83, 0x98, 0x14, 0x00, 0x86, 0x98, 0x25, 0x38, 0x80, 0x00, + 0x54, 0x00, 0x40, 0x10, 0xf8, 0xff, 0x09, 0x24, 0x00, 0x00, 0x64, 0x90, + 0x05, 0x00, 0x84, 0x30, 0x04, 0x00, 0x84, 0x38, 0xfc, 0xff, 0x80, 0x14, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x90, 0x08, 0x00, 0x42, 0x30, + 0x07, 0x00, 0x40, 0x50, 0x25, 0x00, 0xe5, 0x90, 0x00, 0x00, 0xc2, 0x90, + 0x00, 0x00, 0x62, 0x90, 0x08, 0x00, 0x42, 0x30, 0xfc, 0xff, 0x40, 0x14, + 0x00, 0x00, 0x00, 0x00, 0x25, 0x00, 0xe5, 0x90, 0x00, 0x00, 0x62, 0x90, + 0x02, 0x00, 0x42, 0x30, 0xfd, 0xff, 0x40, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc5, 0xa0, 0x26, 0x00, 0xe2, 0x90, 0x45, 0x00, 0x40, 0x10, + 0x03, 0x00, 0xe8, 0x88, 0x00, 0x00, 0xe8, 0x98, 0x18, 0x00, 0x05, 0x24, + 0x00, 0x00, 0x62, 0x90, 0x02, 0x00, 0x42, 0x30, 0xfd, 0xff, 0x40, 0x10, + 0x06, 0x10, 0xa8, 0x00, 0xff, 0x00, 0x42, 0x30, 0xf8, 0xff, 0xa5, 0x24, + 0x00, 0x00, 0xc2, 0xa0, 0xf8, 0xff, 0xa9, 0x14, 0x05, 0x00, 0x0b, 0x24, + 0x07, 0x00, 0xe8, 0x88, 0x04, 0x00, 0xe8, 0x98, 0x1e, 0x00, 0x00, 0x11, + 0x25, 0x28, 0x00, 0x01, 0x00, 0x00, 0x62, 0x90, 0x02, 0x00, 0x42, 0x30, + 0xfd, 0xff, 0x40, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xa0, + 0x00, 0x00, 0x62, 0x90, 0x08, 0x00, 0x42, 0x30, 0x06, 0x00, 0x40, 0x50, + 0xff, 0xff, 0xa5, 0x24, 0x00, 0x00, 0xc2, 0x90, 0x25, 0x00, 0x60, 0x51, + 0x24, 0x00, 0xec, 0x90, 0xff, 0xff, 0x6b, 0x25, 0xff, 0xff, 0xa5, 0x24, + 0xf1, 0xff, 0xa0, 0x14, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x51, + 0x01, 0x00, 0x04, 0x24, 0x24, 0x00, 0xe5, 0x90, 0x00, 0x00, 0x62, 0x90, + 0x08, 0x00, 0x42, 0x30, 0xfd, 0xff, 0x40, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc2, 0x90, 0xff, 0x00, 0x42, 0x30, 0x04, 0x00, 0xa2, 0x14, + 0xff, 0xff, 0x08, 0x25, 0xf7, 0xff, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x04, 0x24, 0x00, 0x00, 0x44, 0xa1, 0x0b, 0x00, 0xe2, 0x88, + 0x01, 0x00, 0x4a, 0x25, 0x08, 0x00, 0xe2, 0x98, 0xff, 0xff, 0x42, 0x24, + 0x0b, 0x00, 0xe2, 0xa8, 0x08, 0x00, 0xe2, 0xb8, 0x03, 0x00, 0xe4, 0x88, + 0x07, 0x00, 0xe5, 0x88, 0x00, 0x00, 0xe4, 0x98, 0x04, 0x00, 0xe5, 0x98, + 0x21, 0x20, 0x85, 0x00, 0x03, 0x00, 0xe4, 0xa8, 0xae, 0xff, 0x40, 0x14, + 0x00, 0x00, 0xe4, 0xb8, 0x3f, 0x00, 0x00, 0x70, 0x08, 0x00, 0xe0, 0x03, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x42, 0x30, 0xed, 0xff, 0x82, 0x55, + 0x00, 0x00, 0x44, 0xa1, 0xd9, 0xff, 0x00, 0x10, 0xff, 0xff, 0x08, 0x25, + 0x00, 0x00, 0xe8, 0x98, 0x10, 0x00, 0x05, 0x24, 0x00, 0x00, 0x62, 0x90, + 0x02, 0x00, 0x42, 0x30, 0xfd, 0xff, 0x40, 0x10, 0x06, 0x10, 0xa8, 0x00, + 0xff, 0x00, 0x42, 0x30, 0xf8, 0xff, 0xa5, 0x24, 0x00, 0x00, 0xc2, 0xa0, + 0xf8, 0xff, 0xa9, 0x14, 0x04, 0x00, 0x0b, 0x24, 0xbc, 0xff, 0x00, 0x10, + 0x07, 0x00, 0xe8, 0x88 diff --git a/contrib/loaders/flash/dw-spi/mipsel-linux-gnu-erase.inc b/contrib/loaders/flash/dw-spi/mipsel-linux-gnu-erase.inc new file mode 100644 index 000000000..b72c7ea44 --- /dev/null +++ b/contrib/loaders/flash/dw-spi/mipsel-linux-gnu-erase.inc @@ -0,0 +1,39 @@ + 0x0b, 0x00, 0x88, 0x88, 0x25, 0x28, 0x80, 0x00, 0x03, 0x00, 0x86, 0x88, + 0x0f, 0x00, 0x82, 0x88, 0x17, 0x00, 0x84, 0x88, 0x08, 0x00, 0xa8, 0x98, + 0x00, 0x00, 0xa6, 0x98, 0x0c, 0x00, 0xa2, 0x98, 0x5f, 0x00, 0x00, 0x11, + 0x14, 0x00, 0xa4, 0x98, 0xf8, 0xff, 0x07, 0x24, 0x1d, 0x00, 0xa9, 0x90, + 0x00, 0x00, 0x43, 0x90, 0x05, 0x00, 0x63, 0x30, 0x04, 0x00, 0x63, 0x38, + 0xfc, 0xff, 0x60, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x89, 0xa0, + 0x1c, 0x00, 0xaa, 0x90, 0x1f, 0x00, 0xa9, 0x90, 0x00, 0x00, 0x43, 0x90, + 0x05, 0x00, 0x63, 0x30, 0x04, 0x00, 0x63, 0x38, 0xfc, 0xff, 0x60, 0x14, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x90, 0x08, 0x00, 0x63, 0x30, + 0x06, 0x00, 0x60, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x83, 0x90, + 0x00, 0x00, 0x43, 0x90, 0x08, 0x00, 0x63, 0x30, 0xfc, 0xff, 0x60, 0x14, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8a, 0xa0, 0x00, 0x00, 0x80, 0xa0, + 0x00, 0x00, 0x43, 0x90, 0x08, 0x00, 0x63, 0x30, 0xfd, 0xff, 0x60, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x83, 0x90, 0x00, 0x00, 0x43, 0x90, + 0x08, 0x00, 0x63, 0x30, 0xfd, 0xff, 0x60, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x83, 0x90, 0x24, 0x18, 0x23, 0x01, 0xe4, 0xff, 0x60, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0xaa, 0x90, 0x21, 0x00, 0xa9, 0x90, + 0x00, 0x00, 0x43, 0x90, 0x05, 0x00, 0x63, 0x30, 0x04, 0x00, 0x63, 0x38, + 0xfc, 0xff, 0x60, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8a, 0xa0, + 0x31, 0x00, 0x20, 0x11, 0x10, 0x00, 0x09, 0x24, 0x18, 0x00, 0x09, 0x24, + 0x00, 0x00, 0x43, 0x90, 0x02, 0x00, 0x63, 0x30, 0xfd, 0xff, 0x60, 0x10, + 0x06, 0x18, 0x26, 0x01, 0xff, 0x00, 0x63, 0x30, 0xf8, 0xff, 0x29, 0x25, + 0xf9, 0xff, 0x27, 0x15, 0x00, 0x00, 0x83, 0xa0, 0x1c, 0x00, 0xaa, 0x90, + 0x20, 0x00, 0xa9, 0x90, 0x00, 0x00, 0x43, 0x90, 0x05, 0x00, 0x63, 0x30, + 0x04, 0x00, 0x63, 0x38, 0xfc, 0xff, 0x60, 0x14, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x43, 0x90, 0x08, 0x00, 0x63, 0x30, 0x06, 0x00, 0x60, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x83, 0x90, 0x00, 0x00, 0x43, 0x90, + 0x08, 0x00, 0x63, 0x30, 0xfc, 0xff, 0x60, 0x14, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x8a, 0xa0, 0x00, 0x00, 0x80, 0xa0, 0x00, 0x00, 0x43, 0x90, + 0x08, 0x00, 0x63, 0x30, 0xfd, 0xff, 0x60, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x83, 0x90, 0x00, 0x00, 0x43, 0x90, 0x08, 0x00, 0x63, 0x30, + 0xfd, 0xff, 0x60, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x83, 0x90, + 0x24, 0x18, 0x23, 0x01, 0xe4, 0xff, 0x60, 0x14, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x00, 0xa3, 0x88, 0xff, 0xff, 0x08, 0x25, 0x04, 0x00, 0xa3, 0x98, + 0xa4, 0xff, 0x00, 0x15, 0x21, 0x30, 0xc3, 0x00, 0x3f, 0x00, 0x00, 0x70, + 0x08, 0x00, 0xe0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x90, + 0x02, 0x00, 0x63, 0x30, 0xfd, 0xff, 0x60, 0x10, 0x06, 0x18, 0x26, 0x01, + 0xff, 0x00, 0x63, 0x30, 0xf8, 0xff, 0x29, 0x25, 0xf9, 0xff, 0x27, 0x15, + 0x00, 0x00, 0x83, 0xa0, 0xd1, 0xff, 0x00, 0x10, 0x1c, 0x00, 0xaa, 0x90 diff --git a/contrib/loaders/flash/dw-spi/mipsel-linux-gnu-program.inc b/contrib/loaders/flash/dw-spi/mipsel-linux-gnu-program.inc new file mode 100644 index 000000000..31500e01d --- /dev/null +++ b/contrib/loaders/flash/dw-spi/mipsel-linux-gnu-program.inc @@ -0,0 +1,51 @@ + 0x13, 0x00, 0x88, 0x88, 0x25, 0x30, 0x80, 0x00, 0x0b, 0x00, 0x85, 0x88, + 0x17, 0x00, 0x82, 0x88, 0x1f, 0x00, 0x84, 0x88, 0x10, 0x00, 0xc8, 0x98, + 0x08, 0x00, 0xc5, 0x98, 0x14, 0x00, 0xc2, 0x98, 0x1c, 0x00, 0xc4, 0x98, + 0x78, 0x00, 0x00, 0x11, 0xf8, 0xff, 0x07, 0x24, 0x25, 0x00, 0xc9, 0x90, + 0x00, 0x00, 0x43, 0x90, 0x05, 0x00, 0x63, 0x30, 0x04, 0x00, 0x63, 0x38, + 0xfc, 0xff, 0x60, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x89, 0xa0, + 0x24, 0x00, 0xca, 0x90, 0x27, 0x00, 0xc9, 0x90, 0x00, 0x00, 0x43, 0x90, + 0x05, 0x00, 0x63, 0x30, 0x04, 0x00, 0x63, 0x38, 0xfc, 0xff, 0x60, 0x14, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x90, 0x08, 0x00, 0x63, 0x30, + 0x06, 0x00, 0x60, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x83, 0x90, + 0x00, 0x00, 0x43, 0x90, 0x08, 0x00, 0x63, 0x30, 0xfc, 0xff, 0x60, 0x14, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8a, 0xa0, 0x00, 0x00, 0x80, 0xa0, + 0x00, 0x00, 0x43, 0x90, 0x08, 0x00, 0x63, 0x30, 0xfd, 0xff, 0x60, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x83, 0x90, 0x00, 0x00, 0x43, 0x90, + 0x08, 0x00, 0x63, 0x30, 0xfd, 0xff, 0x60, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x83, 0x90, 0x24, 0x18, 0x23, 0x01, 0xe4, 0xff, 0x60, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x90, 0x05, 0x00, 0x63, 0x30, + 0x04, 0x00, 0x63, 0x38, 0xfc, 0xff, 0x60, 0x14, 0x00, 0x00, 0x00, 0x00, + 0x26, 0x00, 0xc9, 0x90, 0x00, 0x00, 0x43, 0x90, 0x02, 0x00, 0x63, 0x30, + 0xfd, 0xff, 0x60, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x89, 0xa0, + 0x29, 0x00, 0xc3, 0x90, 0x47, 0x00, 0x60, 0x10, 0x03, 0x00, 0xca, 0x88, + 0x00, 0x00, 0xca, 0x98, 0x18, 0x00, 0x09, 0x24, 0x00, 0x00, 0x43, 0x90, + 0x02, 0x00, 0x63, 0x30, 0xfd, 0xff, 0x60, 0x10, 0x06, 0x18, 0x2a, 0x01, + 0xff, 0x00, 0x63, 0x30, 0xf8, 0xff, 0x29, 0x25, 0x00, 0x00, 0x83, 0xa0, + 0xf8, 0xff, 0x27, 0x15, 0x25, 0x58, 0x00, 0x01, 0x07, 0x00, 0xc3, 0x88, + 0x04, 0x00, 0xc3, 0x98, 0x2b, 0x48, 0x03, 0x01, 0x0a, 0x58, 0x69, 0x00, + 0x0d, 0x00, 0x60, 0x11, 0x21, 0x50, 0xab, 0x00, 0x00, 0x00, 0xa9, 0x90, + 0x01, 0x00, 0xa5, 0x24, 0x00, 0x00, 0x43, 0x90, 0x02, 0x00, 0x63, 0x30, + 0xfd, 0xff, 0x60, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x89, 0xa0, + 0xf9, 0xff, 0xaa, 0x54, 0x00, 0x00, 0xa9, 0x90, 0x07, 0x00, 0xc3, 0x88, + 0x23, 0x40, 0x0b, 0x01, 0x04, 0x00, 0xc3, 0x98, 0x03, 0x00, 0xc9, 0x88, + 0x00, 0x00, 0xc9, 0x98, 0x21, 0x18, 0x23, 0x01, 0x03, 0x00, 0xc3, 0xa8, + 0x00, 0x00, 0xc3, 0xb8, 0x24, 0x00, 0xca, 0x90, 0x28, 0x00, 0xc9, 0x90, + 0x00, 0x00, 0x43, 0x90, 0x05, 0x00, 0x63, 0x30, 0x04, 0x00, 0x63, 0x38, + 0xfc, 0xff, 0x60, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x90, + 0x08, 0x00, 0x63, 0x30, 0x06, 0x00, 0x60, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x83, 0x90, 0x00, 0x00, 0x43, 0x90, 0x08, 0x00, 0x63, 0x30, + 0xfc, 0xff, 0x60, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8a, 0xa0, + 0x00, 0x00, 0x80, 0xa0, 0x00, 0x00, 0x43, 0x90, 0x08, 0x00, 0x63, 0x30, + 0xfd, 0xff, 0x60, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x83, 0x90, + 0x00, 0x00, 0x43, 0x90, 0x08, 0x00, 0x63, 0x30, 0xfd, 0xff, 0x60, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x83, 0x90, 0x24, 0x18, 0x23, 0x01, + 0xe4, 0xff, 0x60, 0x14, 0x00, 0x00, 0x00, 0x00, 0x8b, 0xff, 0x00, 0x55, + 0x25, 0x00, 0xc9, 0x90, 0x3f, 0x00, 0x00, 0x70, 0x08, 0x00, 0xe0, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xca, 0x98, 0x10, 0x00, 0x09, 0x24, + 0x00, 0x00, 0x43, 0x90, 0x02, 0x00, 0x63, 0x30, 0xfd, 0xff, 0x60, 0x10, + 0x06, 0x18, 0x2a, 0x01, 0xff, 0x00, 0x63, 0x30, 0xf8, 0xff, 0x29, 0x25, + 0x00, 0x00, 0x83, 0xa0, 0xf8, 0xff, 0x27, 0x15, 0x25, 0x58, 0x00, 0x01, + 0x07, 0x00, 0xc3, 0x88, 0x04, 0x00, 0xc3, 0x98, 0x2b, 0x48, 0x03, 0x01, + 0x0a, 0x58, 0x69, 0x00, 0xbb, 0xff, 0x60, 0x15, 0x21, 0x50, 0xab, 0x00, + 0xc6, 0xff, 0x00, 0x10, 0x03, 0x00, 0xc9, 0x88 diff --git a/contrib/loaders/flash/dw-spi/mipsel-linux-gnu-read.inc b/contrib/loaders/flash/dw-spi/mipsel-linux-gnu-read.inc new file mode 100644 index 000000000..3f18b7c2e --- /dev/null +++ b/contrib/loaders/flash/dw-spi/mipsel-linux-gnu-read.inc @@ -0,0 +1,33 @@ + 0x0f, 0x00, 0x87, 0x88, 0x07, 0x00, 0x88, 0x88, 0x13, 0x00, 0x83, 0x88, + 0x1b, 0x00, 0x85, 0x88, 0x0c, 0x00, 0x87, 0x98, 0x04, 0x00, 0x88, 0x98, + 0x10, 0x00, 0x83, 0x98, 0x18, 0x00, 0x85, 0x98, 0x00, 0x00, 0x62, 0x90, + 0x05, 0x00, 0x42, 0x30, 0x04, 0x00, 0x42, 0x38, 0xfc, 0xff, 0x40, 0x14, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x90, 0x08, 0x00, 0x42, 0x30, + 0x07, 0x00, 0x40, 0x50, 0x20, 0x00, 0x86, 0x90, 0x00, 0x00, 0xa2, 0x90, + 0x00, 0x00, 0x62, 0x90, 0x08, 0x00, 0x42, 0x30, 0xfc, 0xff, 0x40, 0x14, + 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x86, 0x90, 0x00, 0x00, 0x62, 0x90, + 0x02, 0x00, 0x42, 0x30, 0xfd, 0xff, 0x40, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xa6, 0xa0, 0x21, 0x00, 0x82, 0x90, 0x35, 0x00, 0x40, 0x10, + 0x03, 0x00, 0x82, 0x88, 0x18, 0x00, 0x06, 0x24, 0xf8, 0xff, 0x09, 0x24, + 0x00, 0x00, 0x82, 0x98, 0x25, 0x20, 0x40, 0x00, 0x00, 0x00, 0x62, 0x90, + 0x02, 0x00, 0x42, 0x30, 0xfd, 0xff, 0x40, 0x10, 0x06, 0x10, 0xc4, 0x00, + 0xff, 0x00, 0x42, 0x30, 0xf8, 0xff, 0xc6, 0x24, 0xf9, 0xff, 0xc9, 0x14, + 0x00, 0x00, 0xa2, 0xa0, 0x05, 0x00, 0x06, 0x24, 0x23, 0x00, 0xe0, 0x10, + 0x25, 0x20, 0xe0, 0x00, 0x00, 0x00, 0x62, 0x90, 0x02, 0x00, 0x42, 0x30, + 0xfd, 0xff, 0x40, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0xa0, + 0x00, 0x00, 0x62, 0x90, 0x08, 0x00, 0x42, 0x30, 0x06, 0x00, 0x40, 0x50, + 0xff, 0xff, 0x84, 0x24, 0x00, 0x00, 0xa2, 0x90, 0x14, 0x00, 0xc0, 0x50, + 0x00, 0x00, 0x02, 0xa1, 0xff, 0xff, 0xc6, 0x24, 0xff, 0xff, 0x84, 0x24, + 0xf1, 0xff, 0x80, 0x14, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0xe0, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x90, 0x08, 0x00, 0x42, 0x30, + 0xfd, 0xff, 0x40, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa2, 0x90, + 0x03, 0x00, 0xc0, 0x50, 0xff, 0xff, 0xe7, 0x24, 0xf8, 0xff, 0x00, 0x10, + 0xff, 0xff, 0xc6, 0x24, 0x06, 0x00, 0xe0, 0x10, 0x00, 0x00, 0x02, 0xa1, + 0xf4, 0xff, 0x00, 0x10, 0x01, 0x00, 0x08, 0x25, 0xff, 0xff, 0xe7, 0x24, + 0xec, 0xff, 0x00, 0x10, 0x01, 0x00, 0x08, 0x25, 0x3f, 0x00, 0x00, 0x70, + 0x08, 0x00, 0xe0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x06, 0x24, + 0xf8, 0xff, 0x09, 0x24, 0x00, 0x00, 0x82, 0x98, 0x25, 0x20, 0x40, 0x00, + 0x00, 0x00, 0x62, 0x90, 0x02, 0x00, 0x42, 0x30, 0xfd, 0xff, 0x40, 0x10, + 0x06, 0x10, 0xc4, 0x00, 0xff, 0x00, 0x42, 0x30, 0xf8, 0xff, 0xc6, 0x24, + 0xf9, 0xff, 0xc9, 0x14, 0x00, 0x00, 0xa2, 0xa0, 0xcc, 0xff, 0x00, 0x10, + 0x04, 0x00, 0x06, 0x24 diff --git a/contrib/loaders/flash/dw-spi/mipsel-linux-gnu-transaction.inc b/contrib/loaders/flash/dw-spi/mipsel-linux-gnu-transaction.inc new file mode 100644 index 000000000..384dbadb1 --- /dev/null +++ b/contrib/loaders/flash/dw-spi/mipsel-linux-gnu-transaction.inc @@ -0,0 +1,21 @@ + 0x03, 0x00, 0x85, 0x88, 0x0b, 0x00, 0x88, 0x88, 0x0f, 0x00, 0x83, 0x88, + 0x17, 0x00, 0x86, 0x88, 0x00, 0x00, 0x85, 0x98, 0x08, 0x00, 0x88, 0x98, + 0x0c, 0x00, 0x83, 0x98, 0x14, 0x00, 0x86, 0x98, 0x00, 0x00, 0x62, 0x90, + 0x05, 0x00, 0x42, 0x30, 0x04, 0x00, 0x42, 0x38, 0xfc, 0xff, 0x40, 0x14, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x90, 0x08, 0x00, 0x42, 0x30, + 0x06, 0x00, 0x40, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc2, 0x90, + 0x00, 0x00, 0x62, 0x90, 0x08, 0x00, 0x42, 0x30, 0xfc, 0xff, 0x40, 0x14, + 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, + 0x25, 0x38, 0xa0, 0x00, 0x21, 0x40, 0x05, 0x01, 0x00, 0x00, 0xa9, 0x90, + 0x01, 0x00, 0xa5, 0x24, 0x00, 0x00, 0x62, 0x90, 0x02, 0x00, 0x42, 0x30, + 0xfd, 0xff, 0x40, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc9, 0xa0, + 0x1c, 0x00, 0x82, 0x90, 0x08, 0x00, 0x40, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x62, 0x90, 0x08, 0x00, 0x42, 0x30, 0x04, 0x00, 0x40, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc2, 0x90, 0x01, 0x00, 0xe7, 0x24, + 0xff, 0xff, 0xe2, 0xa0, 0xef, 0xff, 0xa8, 0x54, 0x00, 0x00, 0xa9, 0x90, + 0x1c, 0x00, 0x82, 0x90, 0x0c, 0x00, 0x40, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x2b, 0x10, 0xe8, 0x00, 0x09, 0x00, 0x40, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x62, 0x90, 0x08, 0x00, 0x42, 0x30, 0xfd, 0xff, 0x40, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc2, 0x90, 0x01, 0x00, 0xe7, 0x24, + 0xf9, 0xff, 0x07, 0x15, 0xff, 0xff, 0xe2, 0xa0, 0x3f, 0x00, 0x00, 0x70, + 0x08, 0x00, 0xe0, 0x03, 0x00, 0x00, 0x00, 0x00 diff --git a/doc/openocd.texi b/doc/openocd.texi index 47a6f69eb..2ec49a4c0 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -6421,6 +6421,75 @@ flash bank $_FLASHNAME fespi 0x20000000 0 0 0 $_TARGETNAME @end example @end deffn +@deffn {Flash Driver} {dw-spi} +@cindex DesignWare SPI controller driver +@cindex DW-SPI +Driver for SPI NOR flash chips connected via DesignWare SPI Core, used +in number of MCUs. +Currently, only MIPS M4K CPU architecture is supported. + +The flash size is autodetected based on the table of known JEDEC IDs hardcoded +in the OpenOCD sources. When flash size is set to @var{0}, probed Flash +size is used. + +This driver requires configuring DRAM controller first, setting up a +working area big enough to hold read/write buffers and switching Flash +chip to 32bit mode via Tcl commands. + +@quotation Note +If chip contains Boot controller, its 24/32bit setting must match +Flash chip. If Flash chip's reset line is not connected to JTAG adapter, +CPU reset may cause these configurations to be out of sync. +@end quotation + + +Mandatory driver's arguments are + +@itemize +@item @var{-freq} ... core frequency in Hz, used in communication speed +calculation. +@item @var{-simc} ... @var{SIMC} register block absolute address. +This value is the same as for Linux's driver device tree register field. +@item @var{-spi_mst} ... @var{SPI_MST} register address. When available, +it is used for switching between SPI Boot and Master controllers. This +value is the same as for Linux's driver device tree register field +second argument. Set to @var{0} if SPI Boot controller not available. +@item @var{-if_owner_offset} ... offset of @var{if_owner} field inside +@var{SPI_MST} register. Set to @var{0} if SPI Boot controller not available. +@end itemize + +Optional driver's arguments are + +@itemize +@item @var{-speed} ... SPI device communication speed in Hz. Minimal +speed depends on the @var{-freq} variable and has the value of +@var{freq/0xfffe}. The default value is @var{1000000}. +@item @var{-chip_select} ... Chip select pin. The default value +is @var{0}. +@item @var{-timeout} ... flash communication timeout in +seconds. The default value is @var{600}. +@end itemize + +For some SoCs there are shortcuts for mandatory arguments + +@itemize +@item @var{-jaguar2} ... configuration for MSCC Jaguar2 SoC family. +@item @var{-ocelot} ... configuration for MSCC Ocelot SoC family. +@end itemize + +Driver provides shortcut arguments for MSCC @var{-jaguar2} and +@var{-ocelot} network switch SOCs, which set the correct values for @var{-freq}, +@var{-simc}, @var{-spi_mst} and @var{-if_owner_offset} arguments. + +Example of equivalent configurations for Jaguar2 SoC + +@example +flash bank $_FLASHNAME dw-spi 0x40000000 0x02000000 4 4 $_TARGETNAME -freq 250000000 -simc 0x70101000 -spi_mst 0x70000024 -if_owner_offset 6 -speed 3000000 +flash bank $_FLASHNAME dw-spi 0x40000000 0x02000000 4 4 $_TARGETNAME -jaguar2 -speed 3000000 +@end example + +@end deffn + @subsection Internal Flash (Microcontrollers) @deffn {Flash Driver} {aduc702x} diff --git a/src/flash/nor/Makefile.am b/src/flash/nor/Makefile.am index afa11e7d4..829687761 100644 --- a/src/flash/nor/Makefile.am +++ b/src/flash/nor/Makefile.am @@ -26,6 +26,7 @@ NOR_DRIVERS = \ %D%/cc26xx.c \ %D%/cfi.c \ %D%/dsp5680xx_flash.c \ + %D%/dw-spi.c \ %D%/efm32.c \ %D%/em357.c \ %D%/eneispif.c \ @@ -89,6 +90,7 @@ NORHEADERS = \ %D%/cc26xx.h \ %D%/cfi.h \ %D%/driver.h \ + %D%/dw-spi-helper.h \ %D%/imp.h \ %D%/non_cfi.h \ %D%/ocl.h \ diff --git a/src/flash/nor/driver.h b/src/flash/nor/driver.h index 211661e21..852a55a11 100644 --- a/src/flash/nor/driver.h +++ b/src/flash/nor/driver.h @@ -254,6 +254,7 @@ extern const struct flash_driver cc26xx_flash; extern const struct flash_driver cc3220sf_flash; extern const struct flash_driver cfi_flash; extern const struct flash_driver dsp5680xx_flash; +extern const struct flash_driver dw_spi_flash; extern const struct flash_driver efm32_flash; extern const struct flash_driver em357_flash; extern const struct flash_driver eneispif_flash; diff --git a/src/flash/nor/drivers.c b/src/flash/nor/drivers.c index dd9995ecb..ce97b8150 100644 --- a/src/flash/nor/drivers.c +++ b/src/flash/nor/drivers.c @@ -31,6 +31,7 @@ static const struct flash_driver * const flash_drivers[] = { &cc26xx_flash, &cfi_flash, &dsp5680xx_flash, + &dw_spi_flash, &efm32_flash, &em357_flash, &eneispif_flash, diff --git a/src/flash/nor/dw-spi-helper.h b/src/flash/nor/dw-spi-helper.h new file mode 100644 index 000000000..d3537557b --- /dev/null +++ b/src/flash/nor/dw-spi-helper.h @@ -0,0 +1,102 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/** + * @file + * Driver for SPI NOR flash chips connected via DesignWare SPI Core. + * + * In order to avoid using stack, all helper function arguments are packed + * into a single struct, passed by pointer. + * + * Pointers are represented by 64 bit integers to make structs compatible + * with 64 bit targets. + * + * This file contains helper function argument structures. + */ + +#ifndef OPENOCD_FLASH_NOR_DW_SPI_HELPER_H +#define OPENOCD_FLASH_NOR_DW_SPI_HELPER_H + +#include <stdint.h> + +/** + * @brief Arguments for transaction helper function. + */ +struct dw_spi_transaction { + uint64_t buffer; + ///< Pointer to data buffer to send over SPI. + ///< Return values are stored in place of output data when + ///< dw_spi_transaction::read_flag is 1. + uint32_t size; ///< Size of dw_spi_transaction::buffer. + uint64_t status_reg; ///< Pointer to SR register. + uint64_t data_reg; ///< Pointer to DR register. + uint8_t read_flag; + ///< When 1, store RX FIFO data to dw_spi_transaction::buffer. +} __attribute__((packed)); + +/** + * @brief Arguments for check_fill helper function. + */ +struct dw_spi_check_fill { + uint32_t address; ///< Starting address. Sector aligned. + uint32_t sector_size; ///< Sector size. + uint32_t sector_count; ///< Number of sectors to check. + uint64_t status_reg; ///< Pointer to SR register. + uint64_t data_reg; ///< Pointer to DR register. + uint64_t fill_status_array; + ///< Pointer to array describing sectors fill status. + ///< 1 if filled, 0 if not filled. + uint8_t pattern; ///< Fill pattern. + uint8_t read_cmd; ///< Read data command. + uint8_t four_byte_mode; ///< Four byte addressing mode flag. +} __attribute__((packed)); + +/** + * @brief Arguments for erase helper function. + */ +struct dw_spi_erase { + uint32_t address; ///< First sector address. Sector aligned. + uint32_t sector_size; ///< Sector size. + uint32_t sector_count; ///< Number of sectors to erase. + uint64_t status_reg; ///< Pointer to SR register. + uint64_t data_reg; ///< Pointer to DR register. + uint8_t read_status_cmd; ///< Read status command. + uint8_t write_enable_cmd; ///< Write enable command. + uint8_t erase_sector_cmd; ///< Erase sector command. + uint8_t write_enable_mask; ///< Write enable mask. + uint8_t busy_mask; ///< Busy mask. + uint8_t four_byte_mode; ///< Four byte addressing mode flag. +} __attribute__((packed)); + +/** + * @brief Arguments for program helper function. + */ +struct dw_spi_program { + uint32_t address; + ///< First page address. Page aligned when write is crossing + ///< the page boundary. + uint32_t page_size; ///< Page size. + uint64_t buffer; ///< Data buffer pointer. + uint32_t buffer_size; ///< Size of dw_spi_program::buffer. + uint64_t status_reg; ///< Pointer to SR register. + uint64_t data_reg; ///< Pointer to DR register. + uint8_t read_status_cmd; ///< Read status command. + uint8_t write_enable_cmd; ///< Write enable command. + uint8_t program_cmd; ///< Program command. + uint8_t write_enable_mask; ///< Write enable mask. + uint8_t busy_mask; ///< Busy mask. + uint8_t four_byte_mode; ///< Four byte addressing mode flag. +} __attribute__((packed)); + +/** + * @brief Arguments for read helper function. + */ +struct dw_spi_read { + uint32_t address; ///< First sector address. + uint64_t buffer; ///< Data buffer pointer. + uint32_t buffer_size; ///< Size of dw_spi_read::buffer. + uint64_t status_reg; ///< Pointer to SR register. + uint64_t data_reg; ///< Pointer to DR register. + uint8_t read_cmd; ///< Read data command. + uint8_t four_byte_mode; ///< Four byte addressing mode flag. +} __attribute__((packed)); + +#endif /* OPENOCD_FLASH_NOR_DW_SPI_HELPER_H */ diff --git a/src/flash/nor/dw-spi.c b/src/flash/nor/dw-spi.c new file mode 100644 index 000000000..e1965477e --- /dev/null +++ b/src/flash/nor/dw-spi.c @@ -0,0 +1,1608 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** + * @file + * Driver for SPI NOR flash chips connected via DesignWare SPI Core. + * Controller's Linux driver is located at drivers/spi/spi-dw-mmio.c. + * DW-SPI is used in a number of processors, including Microsemi Jaguar2 and + * Ocelot switch chips. + * + * Serial interface (SI) nCS0 pin, which is usually connected to the external + * flash, cannot be controlled via GPIO controller: it is asserted only when + * TX FIFO is not empty. Since JTAG is not fast enough to fill TX FIFO and + * collect data from RX FIFO at the same time even on the slowest SPI clock + * speeds, driver can only operate using functions, loaded in target's memory. + * Therefore, it requires the user to set up DRAM controller and provide + * work-area. + * + * In Microsemi devices, serial interface pins may be governed either + * by Boot or Master controller. For these devices, additional configuration of + * spi_mst address is required to switch between the two. + * + * Currently supported devices typically have much more RAM then NOR Flash + * (Jaguar2 reference design has 256MB RAM and 32MB NOR Flash), so supporting + * work-area sizes smaller then transfer buffer seems like the unnecessary + * complication. + * + * This code was tested on Jaguar2 VSC7448 connected to Macronix MX25L25635F. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "dw-spi-helper.h" +#include "imp.h" +#include "spi.h" + +#include <helper/bits.h> +#include <helper/time_support.h> +#include <target/algorithm.h> +#include <target/breakpoints.h> +#include <target/mips32.h> +#include <target/target_type.h> + +/** + * @brief IP block placement map. + * + * Used for dynamic definition of the register map. + * + * IP block is used on different chips and placed in different locations. + * This structure defines some implementation specific variables. + */ +struct dw_spi_regmap { + uint32_t freq; ///< Clock frequency. + target_addr_t simc; ///< Absolute offset of SIMC register block. + target_addr_t spi_mst; + ///< Absolute offset of ICPU_CFG:SPI_MST register. 0 if not available. + uint8_t si_if_owner_offset; + ///< Offset of \ref si_mode bits in ICPU_CFG:SPI_MST. +}; + +/** + * @brief Register map for Jaguar2 switch devices. + */ +static const struct dw_spi_regmap jaguar2_regmap = { + .freq = 250000000UL, + .simc = 0x70101000UL, + .spi_mst = 0x70000024UL, + .si_if_owner_offset = 6, +}; + +/** + * @brief Register map for Ocelot switch devices. + */ +static const struct dw_spi_regmap ocelot_regmap = { + .freq = 250000000UL, + .simc = 0x70101000UL, + .spi_mst = 0x70000024UL, + .si_if_owner_offset = 4, +}; + +#define DW_SPI_IF_OWNER_WIDTH 2 ///< IF owner register field width. + +/** + * @brief Owner of the SI interface. + */ +enum dw_spi_si_mode { + DW_SPI_SI_MODE_BOOT = 1, + ///< Boot controller maps contents of SPI Flash to memory in read-only mode. + DW_SPI_SI_MODE_MASTER = 2, + ///< SPI controller mode for reading/writing SPI Flash. +}; + +#define DW_SPI_REG_CTRLR0 0x00 ///< General configuration register. +#define DW_SPI_REG_SIMCEN 0x08 ///< Master controller enable register. +#define DW_SPI_REG_SER 0x10 ///< Slave select register. +#define DW_SPI_REG_BAUDR 0x14 ///< Baud rate configuration register. +#define DW_SPI_REG_SR 0x28 ///< Status register. +#define DW_SPI_REG_IMR 0x2c ///< Interrupt configuration register. +#define DW_SPI_REG_DR 0x60 ///< Data register. + +#define DW_SPI_REG_CTRLR0_DFS(x) ((x) & GENMASK(3, 0)) ///< Data frame size. +#define DW_SPI_REG_CTRLR0_FRF(x) (((x) << 4) & GENMASK(5, 4)) ///< SI protocol. +#define DW_SPI_REG_CTRLR0_SCPH(x) ((!!(x)) << 6) ///< Probe position. +#define DW_SPI_REG_CTRLR0_SCPOL(x) ((!!(x)) << 7) ///< Probe polarity. +#define DW_SPI_REG_CTRLR0_TMOD(x) (((x) << 8) & GENMASK(9, 8)) ///< SI mode. +#define DW_SPI_REG_SIMCEN_SIMCEN(x) (!!(x)) ///< Controller enable. +#define DW_SPI_REG_SER_SER(x) ((x) & GENMASK(15, 0)) ///< Slave select bitmask. +#define DW_SPI_REG_BAUDR_SCKDV(x) ((x) & GENMASK(15, 0)) ///< Clock divisor. + +/** + * @brief Driver private state. + */ +struct dw_spi_driver { + bool probed; ///< Bank is probed. + uint32_t id; ///< Chip ID. + unsigned int speed; ///< Flash speed. + unsigned int timeout; ///< Flash timeout in milliseconds. + uint8_t chip_select_bitmask; ///< Chip select bitmask. + bool four_byte_mode; ///< Flash chip is in 32bit address mode. + enum dw_spi_si_mode saved_ctrl_mode; + ///< Previously selected controller mode. + struct dw_spi_regmap regmap; ///< SI controller regmap. + const struct flash_device *spi_flash; ///< SPI flash device info. +}; + +/** + * @brief Register used to pass argument struct to helper functions. + */ +#define DW_SPI_ARG_REG "r4" + +/** + * @brief Default timeout value in ms for flash transaction jobs. + */ +#define DW_SPI_TIMEOUT_DEFAULT (600 * 1000) + +/** + * @brief Timeout value in ms for short flash transactions, + * e.g. reading flash ID and status register. + */ +#define DW_SPI_TIMEOUT_TRANSACTION 1000 + +/** + * @brief Select SI interface owner. + * + * Mode selection is skipped if Boot controller not available on target + * (i.e. spi_mst command argument is not configured). + * + * @param[in] bank: Flash bank. + * @param[in] mode: New controller mode. + * @return Command execution status. + */ +static int +dw_spi_ctrl_mode(const struct flash_bank *const bank, enum dw_spi_si_mode mode) +{ + struct target *const target = bank->target; + const struct dw_spi_driver *const driver = bank->driver_priv; + const struct dw_spi_regmap *const regmap = &driver->regmap; + + if (!regmap->spi_mst) + return ERROR_OK; + + uint32_t ctrl; + int ret = target_read_u32(target, regmap->spi_mst, &ctrl); + if (ret) { + LOG_ERROR("DW SPI SPI:MST register read error"); + return ret; + } + ctrl &= ~GENMASK(DW_SPI_IF_OWNER_WIDTH + driver->regmap.si_if_owner_offset, + driver->regmap.si_if_owner_offset); + ctrl |= mode << driver->regmap.si_if_owner_offset; + + ret = target_write_u32(target, regmap->spi_mst, ctrl); + if (ret) + LOG_ERROR("DW SPI controller mode configuration error"); + + return ret; +} + +/** + * @brief Select master controller as SI interface owner. + * + * Previous interface owner is restored via dw_spi_ctrl_mode_restore() function. + * Mode selection is skipped if Boot controller not available on target + * (i.e. spi_mst command argument is not configured). + * + * @param[in] bank: Flash bank. + * @param[in] mode: New controller mode. + * @return Command execution status. + */ +static int +dw_spi_ctrl_mode_configure(const struct flash_bank *const bank, + enum dw_spi_si_mode mode) +{ + struct target *const target = bank->target; + struct dw_spi_driver *const driver = bank->driver_priv; + const struct dw_spi_regmap *const regmap = &driver->regmap; + + if (!regmap->spi_mst) + return ERROR_OK; + + uint32_t ctrl; + int ret = target_read_u32(target, regmap->spi_mst, &ctrl); + if (ret) { + LOG_ERROR("DW SPI controller mode query error"); + return ret; + } + driver->saved_ctrl_mode = + (enum dw_spi_si_mode)((ctrl >> driver->regmap.si_if_owner_offset) & + GENMASK(DW_SPI_IF_OWNER_WIDTH, 0)); + + return dw_spi_ctrl_mode(bank, mode); +} + +/** + * @brief Restore SI controller mode. + * + * Restore initially configured SI controller mode. Undo configuration done by + * dw_spi_ctrl_mode_configure() function. + * Mode selection is skipped if Boot controller not available on target + * (i.e. spi_mst command argument is not configured). + * + * @param[in] bank: Flash bank. + * @return Command execution status. + */ +static int +dw_spi_ctrl_mode_restore(const struct flash_bank *const bank) +{ + const struct dw_spi_driver *const driver = bank->driver_priv; + + return dw_spi_ctrl_mode(bank, driver->saved_ctrl_mode); +} + +/** + * @brief Enable master controller. + * + * Configuration of the master controller must be done when it is disabled. + * + * @param[in] bank: Flash bank. + * @param[in] value: New enable state. + * @return Command execution status. + */ +static int +dw_spi_master_ctrl_enable(const struct flash_bank *const bank, bool value) +{ + struct target *const target = bank->target; + const struct dw_spi_driver *const driver = bank->driver_priv; + const struct dw_spi_regmap *const regmap = &driver->regmap; + + int ret = target_write_u32(target, regmap->simc + DW_SPI_REG_SIMCEN, + DW_SPI_REG_SIMCEN_SIMCEN(value)); + if (ret) + LOG_ERROR("DW SPI master controller enable flag configuration error"); + + return ret; +} + +/** + * @brief Configure SI transfer mode. + * + * @param[in] bank: Flash bank. + * @return Command execution status. + */ +static int +dw_spi_ctrl_configure_si(const struct flash_bank *const bank) +{ + struct target *const target = bank->target; + const struct dw_spi_driver *const driver = bank->driver_priv; + const struct dw_spi_regmap *const regmap = &driver->regmap; + + // 8 bit frame; Motorola protocol; middle lo probe; TX RX mode + const uint32_t mode = DW_SPI_REG_CTRLR0_DFS(0x7) | + DW_SPI_REG_CTRLR0_FRF(0) | + DW_SPI_REG_CTRLR0_SCPH(0) | + DW_SPI_REG_CTRLR0_SCPOL(0) | + DW_SPI_REG_CTRLR0_TMOD(0); + + int ret = target_write_u32(target, regmap->simc + DW_SPI_REG_CTRLR0, mode); + if (ret) { + LOG_ERROR("DW SPI master controller configuration query error"); + return ret; + } + + ret = target_write_u32(target, regmap->simc + DW_SPI_REG_SER, + DW_SPI_REG_SER_SER(driver->chip_select_bitmask)); + if (ret) + LOG_ERROR("DW SPI slave select configuration error"); + + return ret; +} + +/** + * @brief Configure SI transfer speed. + * + * @param[in] bank: Flash bank. + * @return Command execution status. + */ +static int +dw_spi_ctrl_configure_speed(const struct flash_bank *const bank) +{ + struct target *const target = bank->target; + const struct dw_spi_driver *const driver = bank->driver_priv; + const struct dw_spi_regmap *const regmap = &driver->regmap; + + // divisor LSB must be zero + const uint16_t div = MIN((regmap->freq / driver->speed), 0xfffe) & 0xfffe; + + int ret = target_write_u32(target, regmap->simc + DW_SPI_REG_BAUDR, + DW_SPI_REG_BAUDR_SCKDV(div)); + if (ret) { + LOG_ERROR("DW SPI speed configuration error"); + return ret; + } + + unsigned int speed = regmap->freq / div; + LOG_DEBUG("DW SPI setting NOR controller speed to %u kHz", speed / 1000); + + return ret; +} + +/** + * @brief Disable SI master controller interrupts. + * + * @param[in] bank: Flash bank. + * @return Command execution status. + */ +static int +dw_spi_ctrl_disable_interrupts(const struct flash_bank *const bank) +{ + struct target *const target = bank->target; + const struct dw_spi_driver *const driver = bank->driver_priv; + const struct dw_spi_regmap *const regmap = &driver->regmap; + + int ret = target_write_u32(target, regmap->simc + DW_SPI_REG_IMR, 0); + if (ret) + LOG_ERROR("DW SPI disable interrupts error"); + + return ret; +} + +/** + * @brief Do data transaction. + * + * Buffer data are sent and replaced with received data. Total buffer size + * is a sum of TX and RX messages. RX portion of the buffer is initially + * filled with dummy values, which get replaced by the message. + * + * @param[in] bank: Flash bank. + * @param[in,out] buffer: Data buffer. If \p read flag is set, buffer is + * filled with received data. + * @param[in] size: \p buffer size. + * @param[in] read: The read flag. If set to true, read values will override + * \p buffer. + * @return Command execution status. + */ +static int +dw_spi_ctrl_transaction(const struct flash_bank *const bank, + uint8_t *const buffer, size_t size, bool read) +{ + struct target *const target = bank->target; + const struct dw_spi_driver *const driver = bank->driver_priv; + const struct dw_spi_regmap *const regmap = &driver->regmap; + + static const uint8_t target_code[] = { +#include "../../../contrib/loaders/flash/dw-spi/mipsel-linux-gnu-transaction.inc" + }; + const size_t target_code_size = sizeof(target_code); + const size_t total_working_area_size = + target_code_size + sizeof(struct dw_spi_transaction) + size; + + // allocate working area, memory args and data buffer + struct working_area *helper; + int ret = target_alloc_working_area(target, target_code_size, &helper); + if (ret) { + LOG_ERROR("DW SPI could not allocate working area. Need %zx", + total_working_area_size); + goto err_helper; + } + + struct working_area *helper_args; + ret = target_alloc_working_area(target, sizeof(struct dw_spi_transaction), + &helper_args); + if (ret) { + LOG_ERROR("DW SPI could not allocate working area. Need %zx", + total_working_area_size); + goto err_helper_args; + } + + struct working_area *target_buffer; + ret = target_alloc_working_area(target, size, &target_buffer); + if (ret) { + LOG_ERROR("DW SPI could not allocate working area. Need %zx", + total_working_area_size); + goto err_target_buffer; + } + + // write algorithm code and buffer to working areas + ret = target_write_buffer(target, helper->address, target_code_size, + target_code); + if (ret) { + LOG_ERROR("DW SPI writing to working area error"); + goto err_write_buffer; + } + + ret = target_write_buffer(target, target_buffer->address, size, buffer); + if (ret) { + LOG_ERROR("DW SPI writing to working area error"); + goto err_write_buffer; + } + + // prepare helper execution + struct mips32_algorithm mips32_algo = { .common_magic = MIPS32_COMMON_MAGIC, + .isa_mode = MIPS32_ISA_MIPS32 }; + + struct reg_param reg_param; + init_reg_param(®_param, DW_SPI_ARG_REG, 32, PARAM_OUT); + struct mem_param mem_param; + init_mem_param(&mem_param, helper_args->address, helper_args->size, + PARAM_OUT); + + // Set the arguments for the helper + buf_set_u32(reg_param.value, 0, 32, helper_args->address); + + struct dw_spi_transaction *helper_args_val = + (struct dw_spi_transaction *)mem_param.value; + target_buffer_set_u32(target, (uint8_t *)&helper_args_val->buffer, + target_buffer->address); + target_buffer_set_u32(target, (uint8_t *)&helper_args_val->size, size); + target_buffer_set_u32(target, (uint8_t *)&helper_args_val->status_reg, + regmap->simc + DW_SPI_REG_SR); + target_buffer_set_u32(target, (uint8_t *)&helper_args_val->data_reg, + regmap->simc + DW_SPI_REG_DR); + helper_args_val->read_flag = read; + + ret = target_run_algorithm(target, 1, &mem_param, 1, ®_param, + helper->address, 0, DW_SPI_TIMEOUT_TRANSACTION, + &mips32_algo); + + if (ret) { + LOG_ERROR("DW SPI flash algorithm error"); + goto cleanup; + } + + if (read) { + ret = target_read_buffer(target, target_buffer->address, size, buffer); + if (ret) + LOG_ERROR("DW SPI target buffer read error"); + } + +cleanup: + destroy_reg_param(®_param); + destroy_mem_param(&mem_param); + +err_write_buffer: + target_free_working_area(target, target_buffer); +err_target_buffer: + target_free_working_area(target, helper_args); +err_helper_args: + target_free_working_area(target, helper); +err_helper: + + return ret; +} + +/** + * @brief Check that selected region is filled with pattern. + * + * This function is used for Flash erase checking. + * + * @param[in] bank: Flash bank. + * @param[in] address: Starting address. Sector aligned. + * @param[in] sector_size: Size of sector. + * @param[in] sector_count: Number of sectors. + * @param[in] pattern: Fill pattern. + * @param[in] read_cmd: Flash read command. + * @param[out] buffer: Filled flag array. Must hold \p sector_count number + * of entries. + * @return Command execution status. + */ +static int +dw_spi_ctrl_check_sectors_fill(const struct flash_bank *const bank, + uint32_t address, size_t sector_size, + size_t sector_count, uint8_t pattern, + uint8_t read_cmd, uint8_t *buffer) +{ + struct target *const target = bank->target; + const struct dw_spi_driver *const driver = bank->driver_priv; + const struct dw_spi_regmap *const regmap = &driver->regmap; + + static const uint8_t target_code[] = { +#include "../../../contrib/loaders/flash/dw-spi/mipsel-linux-gnu-check_fill.inc" + }; + const size_t target_code_size = sizeof(target_code); + const size_t total_working_area_size = + target_code_size + sizeof(struct dw_spi_check_fill) + sector_count; + + // allocate working area, memory args and data buffer + struct working_area *helper; + int ret = target_alloc_working_area(target, target_code_size, &helper); + if (ret) { + LOG_ERROR("DW SPI could not allocate working area. Need %zx", + total_working_area_size); + goto err_helper; + } + + struct working_area *helper_args; + ret = target_alloc_working_area(target, sizeof(struct dw_spi_check_fill), + &helper_args); + if (ret) { + LOG_ERROR("DW SPI could not allocate working area. Need %zx", + total_working_area_size); + goto err_helper_args; + } + + struct working_area *target_buffer; + ret = target_alloc_working_area(target, sector_count, &target_buffer); + if (ret) { + LOG_ERROR("DW SPI could not allocate working area. Need %zx", + total_working_area_size); + goto err_target_buffer; + } + + // write algorithm code and buffer to working areas + ret = target_write_buffer(target, helper->address, target_code_size, + target_code); + if (ret) { + LOG_ERROR("DW SPI writing to working area error"); + goto err_write_buffer; + } + + // prepare helper execution + struct mips32_algorithm mips32_algo = { .common_magic = MIPS32_COMMON_MAGIC, + .isa_mode = MIPS32_ISA_MIPS32 }; + + struct reg_param reg_param; + init_reg_param(®_param, DW_SPI_ARG_REG, 32, PARAM_OUT); + struct mem_param mem_param; + init_mem_param(&mem_param, helper_args->address, helper_args->size, + PARAM_OUT); + + // Set the arguments for the helper + buf_set_u32(reg_param.value, 0, 32, helper_args->address); + + struct dw_spi_check_fill *helper_args_val = + (struct dw_spi_check_fill *)mem_param.value; + target_buffer_set_u32(target, (uint8_t *)&helper_args_val->address, + address); + target_buffer_set_u32(target, (uint8_t *)&helper_args_val->sector_size, + sector_size); + target_buffer_set_u32(target, (uint8_t *)&helper_args_val->sector_count, + sector_count); + target_buffer_set_u32(target, (uint8_t *)&helper_args_val->status_reg, + regmap->simc + DW_SPI_REG_SR); + target_buffer_set_u32(target, (uint8_t *)&helper_args_val->data_reg, + regmap->simc + DW_SPI_REG_DR); + target_buffer_set_u32(target, + (uint8_t *)&helper_args_val->fill_status_array, + target_buffer->address); + helper_args_val->pattern = pattern; + helper_args_val->read_cmd = read_cmd; + helper_args_val->four_byte_mode = driver->four_byte_mode; + + ret = target_run_algorithm(target, 1, &mem_param, 1, ®_param, + helper->address, 0, driver->timeout, + &mips32_algo); + + if (ret) { + LOG_ERROR("DW SPI flash algorithm error"); + goto cleanup; + } + + ret = target_read_buffer(target, target_buffer->address, sector_count, + buffer); + if (ret) + LOG_ERROR("DW SPI target buffer read error"); + +cleanup: + destroy_reg_param(®_param); + destroy_mem_param(&mem_param); + +err_write_buffer: + target_free_working_area(target, target_buffer); +err_target_buffer: + target_free_working_area(target, helper_args); +err_helper_args: + target_fr... [truncated message content] |