diff --git a/bsps/arm/xilinx-zynq/dev/spi/zynq-qspi-flash-transfer.c b/bsps/arm/xilinx-zynq/dev/spi/zynq-qspi-flash-transfer.c new file mode 100644 index 0000000000..0d0d1ed587 --- /dev/null +++ b/bsps/arm/xilinx-zynq/dev/spi/zynq-qspi-flash-transfer.c @@ -0,0 +1,259 @@ +/* + * Copyright (C) 2024 Contemporary Software + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include + +/* + * The following variables are shared between non-interrupt processing and + * interrupt processing such that they must be global. + */ +rtems_id transfer_task; + +__attribute__((weak)) void zqspi_write_unlock(zqspiflash *driver) +{ +} + +__attribute__((weak)) void zqspi_write_lock(zqspiflash *driver) +{ +} + +static void qspi_flash_rx(void) { + while (true) { + if ( + (qspi_reg_read(ZQSPI_QSPI_REG_INTR_STATUS) & + ZQSPI_QSPI_IXR_RXNEMPTY) == 0 + ) + { + break; + } + qspi_reg_read(ZQSPI_QSPI_REG_RX_DATA); + } +} + +zqspi_error zqspi_transfer( + zqspi_transfer_buffer* transfer, + bool *initialised +) +{ + uint32_t tx_reg; + uint32_t sr; + rtems_status_code sc; + transfer_task = rtems_task_self(); + + if (*initialised == false) + { + qspi_reg_write(ZQSPI_QSPI_REG_EN, 0); + qspi_reg_write(ZQSPI_QSPI_REG_LSPI_CFG, 0x00a002eb); + qspi_flash_rx(); + qspi_reg_write( + ZQSPI_QSPI_REG_CONFIG, + ZQSPI_QSPI_CR_SSFORCE | ZQSPI_QSPI_CR_MANSTRTEN | ZQSPI_QSPI_CR_HOLDB_DR | + ZQSPI_QSPI_CR_BAUD_RATE | ZQSPI_QSPI_CR_MODE_SEL + ); + *initialised = true; + } + + zqspi_transfer_trace("transfer:TX", transfer); + + /* + * Set the chip select. + */ + qspi_reg_write(ZQSPI_QSPI_REG_CONFIG, + qspi_reg_read(ZQSPI_QSPI_REG_CONFIG) & ~ZQSPI_QSPI_CR_PCS); + + /* + * Enable SPI. + */ + qspi_reg_write(ZQSPI_QSPI_REG_EN, ZQSPI_QSPI_EN_SPI_ENABLE); + + /* + * The RX pointer can never catch up and overtake the TX pointer. + */ + transfer->tx_data = (uint32_t*) transfer->buffer; + transfer->rx_data = (uint32_t*) transfer->buffer; + transfer->tx_length = transfer->length; + transfer->rx_length = transfer->length; + + /* + * The buffer to right aligned, that is padding is add to the front of the + * buffer to get the correct aligment for the instruction size. This means + * the data in the first "transfer" is not aligned in the correct bits for + * the keyhole access to the FIFO. + */ + switch (transfer->padding) + { + case 3: + *(transfer->tx_data) >>= 24; + tx_reg = ZQSPI_QSPI_REG_TXD1; + transfer->tx_length -= 1; + transfer->sending += 1; + transfer->start = true; + break; + case 2: + *(transfer->tx_data) >>= 16; + tx_reg = ZQSPI_QSPI_REG_TXD2; + transfer->tx_length -= 2; + transfer->sending += 2; + transfer->start = true; + break; + case 1: + *(transfer->tx_data) >>= 8; + tx_reg = ZQSPI_QSPI_REG_TXD3; + transfer->tx_length -= 3; + transfer->sending += 3; + transfer->start = true; + break; + default: + tx_reg = ZQSPI_QSPI_REG_TXD0; + transfer->tx_length -= 4; + transfer->sending += 4; + if (transfer->tx_length == 0) + transfer->start = true; + break; + } + + qspi_reg_write (tx_reg, *(transfer->tx_data)); + ++(transfer->tx_data); + + if (transfer->start) + { + qspi_reg_write(ZQSPI_QSPI_REG_CONFIG, + qspi_reg_read(ZQSPI_QSPI_REG_CONFIG) | ZQSPI_QSPI_CR_MANSTRT); + + sr = qspi_reg_read(ZQSPI_QSPI_REG_INTR_STATUS); + while ((sr & ZQSPI_QSPI_IXR_TXOW) == 0) + { + sr = qspi_reg_read(ZQSPI_QSPI_REG_INTR_STATUS); + } + } + + /* Enable Interrupts */ + qspi_reg_write(ZQSPI_QSPI_REG_INTR_DISABLE, + ~(ZQSPI_QSPI_IXR_RXNEMPTY | ZQSPI_QSPI_IXR_TXUF)); + qspi_reg_write(ZQSPI_QSPI_REG_INTR_ENABLE, + (ZQSPI_QSPI_IXR_RXNEMPTY | ZQSPI_QSPI_IXR_TXUF)); + + /* Wait for transfer to complete */ + sc = rtems_event_transient_receive(RTEMS_WAIT, ZQSPI_TIMEOUT_TICKS); + if (sc != RTEMS_SUCCESSFUL) { + rtems_event_transient_clear(); + return ZQPSI_FLASH_TRANSFER_FAILED; + } + + /* + * skip the command byte. + */ + zqspi_transfer_buffer_skip(transfer, 1); + + /* + * Disable the chip select. + */ + qspi_reg_write(ZQSPI_QSPI_REG_CONFIG, + qspi_reg_read(ZQSPI_QSPI_REG_CONFIG) | ZQSPI_QSPI_CR_PCS); + + /* + * Disable SPI. + */ + qspi_reg_write(ZQSPI_QSPI_REG_EN, 0); + + zqspi_transfer_trace("transfer:RX", transfer); + + return ZQSPI_FLASH_NO_ERROR; +} + +void zqspi_transfer_intr(zqspiflash *driver) +{ + uint32_t sr; + zqspi_transfer_buffer* transfer = &(driver->buf); + + /* Disable and clear interrupts */ + qspi_reg_write(ZQSPI_QSPI_REG_INTR_DISABLE, 0xFFFFFFFF); + qspi_reg_write(ZQSPI_QSPI_REG_INTR_STATUS, 0xFFFFFFFF); + sr = qspi_reg_read(ZQSPI_QSPI_REG_INTR_STATUS); + + if (transfer->rx_length) + { + while (transfer->start && transfer->sending) + { + if ((sr & ZQSPI_QSPI_IXR_RXNEMPTY) != 0) + { + *(transfer->rx_data) = qspi_reg_read(ZQSPI_QSPI_REG_RX_DATA); + ++(transfer->rx_data); + if (transfer->rx_length > sizeof(uint32_t)) { + transfer->rx_length -= sizeof(uint32_t); + } else { + transfer->rx_length = 0; + } + if (transfer->sending > sizeof(uint32_t)) { + transfer->sending -= sizeof(uint32_t); + } else { + transfer->sending = 0; + } + } + + sr = qspi_reg_read(ZQSPI_QSPI_REG_INTR_STATUS); + } + } + + if (transfer->tx_length) + { + transfer->start = false; + while (transfer->tx_length && ((sr & ZQSPI_QSPI_IXR_TXFULL) == 0)) + { + qspi_reg_write (ZQSPI_QSPI_REG_TXD0, *(transfer->tx_data)); + ++(transfer->tx_data); + if (transfer->tx_length > sizeof(uint32_t)) { + transfer->tx_length -= sizeof(uint32_t); + } else { + transfer->tx_length = 0; + } + transfer->sending += sizeof(uint32_t); + transfer->start = true; + + sr = qspi_reg_read(ZQSPI_QSPI_REG_INTR_STATUS); + } + + if (transfer->start) + { + qspi_reg_write(ZQSPI_QSPI_REG_CONFIG, + qspi_reg_read(ZQSPI_QSPI_REG_CONFIG) | ZQSPI_QSPI_CR_MANSTRT); + } + } + + if (transfer->tx_length) { + qspi_reg_write(ZQSPI_QSPI_REG_INTR_ENABLE, + qspi_reg_read(ZQSPI_QSPI_REG_INTR_ENABLE) | ZQSPI_QSPI_IXR_TXUF); + } + if (transfer->rx_length) { + qspi_reg_write(ZQSPI_QSPI_REG_INTR_ENABLE, + qspi_reg_read(ZQSPI_QSPI_REG_INTR_ENABLE) | ZQSPI_QSPI_IXR_RXNEMPTY); + } + if (transfer->tx_length == 0 && transfer->rx_length == 0) { + (void) rtems_event_transient_send(transfer_task); + } +} diff --git a/bsps/arm/xilinx-zynq/dev/spi/zynq-qspi-flash.c b/bsps/arm/xilinx-zynq/dev/spi/zynq-qspi-flash.c new file mode 100644 index 0000000000..242d30ecde --- /dev/null +++ b/bsps/arm/xilinx-zynq/dev/spi/zynq-qspi-flash.c @@ -0,0 +1,848 @@ +/* + * Copyright (C) 2024 Contemporary Software + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +/* + * Debug tracing. + */ +#define ZQSPI_FLASH_TRACE_TRANSFER 0 + +#if ZQSPI_FLASH_TRACE_TRANSFER +static bool zqspi_trace = 1; +#endif + +#if ZQSPI_FLASH_TRACE_TRANSFER +static void zqspi_transfer_trace_header( + const char* message, + const zqspi_transfer_buffer* transfer +) +{ + if (zqspi_trace) + printf(" %s: length=%ld in=%ld out=%ld padding=%ld\n", + message, transfer->length, transfer->in, + transfer->out, transfer->padding); +} +#endif + +void zqspi_transfer_trace( + const char* message, + const zqspi_transfer_buffer* transfer +) +{ +#if ZQSPI_FLASH_TRACE_TRANSFER + if (zqspi_trace) + { + size_t c; + zqspi_transferTraceHeader(message, transfer); + for (c = 0; c < transfer->length; ++c) + { + if ((c % 16) == 0) + { + if (c) + printf("\n"); + printf(" %04lx ", c); + } + printf("%02x", transfer->buffer[c + transfer->padding]); + if ((c % 16) == 7) + printf("-"); + else + printf(" "); + } + printf("\n"); + } +#endif +} + +void qspi_reg_write(uint32_t reg, uint32_t value) +{ + volatile uint32_t* ap = (uint32_t*)(ZQSPI_QSPI_BASE + reg); + *ap = value; +} + +uint32_t qspi_reg_read(uint32_t reg) +{ + volatile uint32_t* ap = (uint32_t*)(ZQSPI_QSPI_BASE + reg); + return *ap; +} + +static inline uint8_t zqspi_get8(const uint8_t* data) +{ + return *data; +} + +static inline uint16_t zqspi_get16(const uint8_t* data) +{ + return (((uint16_t) data[1]) << 8) | data[0]; +} + +static void zqspi_transfer_buffer_clear(zqspi_transfer_buffer* transfer) +{ + transfer->length = 0; + transfer->padding = 0; + transfer->in = 0; + transfer->out = 0; +} + +static void zqspi_transfer_buffer_set_length( + zqspi_transfer_buffer* transfer, + size_t length +) +{ + transfer->length = length; + transfer->padding = (4 - (length & 3)) & 3; + transfer->in = transfer->padding; + transfer->out = transfer->padding; +} + +static zqspi_error zqspi_transfer_buffer_fill( + zqspi_transfer_buffer* transfer, + const uint8_t data, + size_t length +) +{ + if ((transfer->in + length) >= transfer->size) { + return ZQSPI_FLASH_BUFFER_OVERFLOW; + } + + memset(transfer->buffer + transfer->in, data, length); + + transfer->in += length; + + return ZQSPI_FLASH_NO_ERROR; +} + +static zqspi_error zqspi_transfer_buffer_set8( + zqspi_transfer_buffer* transfer, + const uint8_t data +) +{ + if (transfer->in >= transfer->size) { + return ZQSPI_FLASH_BUFFER_OVERFLOW; + } + + volatile uint8_t* p = &transfer->buffer[transfer->in++]; + *p = data; + + return ZQSPI_FLASH_NO_ERROR; +} + +zqspi_error zqspi_transfer_buffer_skip( + zqspi_transfer_buffer* transfer, + const size_t size +) +{ + if ((transfer->length - (transfer->out - transfer->padding)) < size) { + return ZQSPI_FLASH_BUFFER_UNDERFLOW; + } + + transfer->out += size; + return ZQSPI_FLASH_NO_ERROR; +} + +static zqspi_error zqspi_transfer_buffer_get8( + zqspi_transfer_buffer* transfer, + uint8_t* data +) +{ + if ((transfer->length - (transfer->out - transfer->padding)) < + sizeof(uint8_t)) { + return ZQSPI_FLASH_BUFFER_UNDERFLOW; + } + + *data = transfer->buffer[transfer->out++]; + + return ZQSPI_FLASH_NO_ERROR; +} + +static zqspi_error zqspi_transfer_buffer_get16( + zqspi_transfer_buffer* transfer, + uint16_t* data +) +{ + if ((transfer->length - (transfer->out - transfer->padding)) < + sizeof(uint16_t)) { + return ZQSPI_FLASH_BUFFER_UNDERFLOW; + } + + *data = ((uint16_t) transfer->buffer[transfer->out++]) << 8; + *data |= transfer->buffer[transfer->out++]; + + return ZQSPI_FLASH_NO_ERROR; +} + +static zqspi_error zqspi_transfer_buffer_copy_out( + zqspi_transfer_buffer* transfer, + uint8_t* data, + const size_t length +) +{ + if ((transfer->length - (transfer->out - transfer->padding)) < length) { + return ZQSPI_FLASH_BUFFER_UNDERFLOW; + } + memcpy(data, transfer->buffer + transfer->out, length); + transfer->out += length; + + return ZQSPI_FLASH_NO_ERROR; +} + +static zqspi_error zqspi_transfer_buffer_copy_in( + zqspi_transfer_buffer* transfer, + const uint8_t* data, + const size_t length +) +{ + if ((transfer->in + length) > transfer->size) { + return ZQSPI_FLASH_BUFFER_OVERFLOW; + } + + memcpy(transfer->buffer + transfer->in, data, length); + transfer->in += length; + + return ZQSPI_FLASH_NO_ERROR; +} + +static void zqspi_transfer_buffer_set_addr( + zqspi_transfer_buffer* transfer, + uint32_t address +) +{ +#if ZQSPI_FLASH_4BYTE_ADDRESSING + zqspi_transfer_buffer_set8(transfer, (address >> 24) & 0xff); +#endif + zqspi_transfer_buffer_set8(transfer, (address >> 16) & 0xff); + zqspi_transfer_buffer_set8(transfer, (address >> 8) & 0xff); + zqspi_transfer_buffer_set8(transfer, address & 0xff); +} + +static void zqspi_transfer_buffer_set_dir( + zqspi_transfer_buffer* transfer, + int trans_dir +) +{ + transfer->trans_dir = trans_dir; +} + +static void zqspi_transfer_buffer_set_command_len( + zqspi_transfer_buffer* transfer, + int comm_len +) +{ + transfer->command_len = comm_len; +} + +static zqspi_error zqspi_read_register( + zqspiflash *driver, + uint8_t reg, + uint16_t* value +) +{ + zqspi_error fe; + + zqspi_transfer_buffer_clear(&driver->buf); + zqspi_transfer_buffer_set_length(&driver->buf, ZQSPI_FLASH_COMMAND_SIZE + 2); + zqspi_transfer_buffer_set8(&driver->buf, reg); + zqspi_transfer_buffer_set8(&driver->buf, 0); + zqspi_transfer_buffer_set8(&driver->buf, 0); + zqspi_transfer_buffer_set_dir(&driver->buf, ZQSPI_FLASH_RX_TRANS); + zqspi_transfer_buffer_set_command_len(&driver->buf, ZQSPI_FLASH_COMMAND_SIZE); + + fe = zqspi_transfer(&driver->buf, &driver->initialised); + if (fe != ZQSPI_FLASH_NO_ERROR) + return fe; + + zqspi_transfer_buffer_get16(&driver->buf, value); + + return ZQSPI_FLASH_NO_ERROR; +} + +static zqspi_error zqspi_wait_for_write(zqspiflash *driver, uint32_t wait) +{ + uint16_t status; + zqspi_error fe; + uint32_t checks = 100; + + while (true) { + fe = zqspi_read_register(driver, ZQSPI_FLASH_READ_STATUS_FLAG_CMD, &status); + if (fe != ZQSPI_FLASH_NO_ERROR) { + return fe; + } + + status = status & 0xFF; + + if ((status & ZQSPI_FLASH_SR_E_ERR) != 0) { + return ZQSPI_FLASH_ERASE_FAILURE; + } + + /* + * A succuessful write requires the write latch and the write in + * progress have cleared. If the write in progress is not set yet the + * write latch remains set it is an error and the write command was not + * received the flash device. + */ + fe = zqspi_read_register(driver, ZQSPI_FLASH_READ_STATUS_CMD, &status); + status = status & 0xFF; + if (fe != ZQSPI_FLASH_NO_ERROR) { + return fe; + } + + if ((status & ZQSPI_FLASH_SR_WIP) == 0) + { + if ((status & ZQSPI_FLASH_SR_WEL) == 0) { + break; + } + if (checks == 0) { + return ZQSPI_FLASH_WRITE_ERASE_CMD_FAIL; + } + --checks; + } + + if (wait) { + usleep(wait); + } + } + return ZQSPI_FLASH_NO_ERROR; +} + +static zqspi_error zqspi_set_WEL(zqspiflash *driver) +{ + uint16_t status; + zqspi_error fe; + + zqspi_transfer_buffer_clear(&driver->buf); + zqspi_transfer_buffer_set_length(&driver->buf, ZQSPI_FLASH_COMMAND_SIZE); + zqspi_transfer_buffer_set8(&driver->buf, ZQSPI_FLASH_WRITE_ENABLE_CMD); + zqspi_transfer_buffer_set_dir(&driver->buf, ZQSPI_FLASH_TX_TRANS); + zqspi_transfer_buffer_set_command_len(&driver->buf, ZQSPI_FLASH_COMMAND_SIZE); + + fe = zqspi_transfer(&driver->buf, &driver->initialised); + if (fe != ZQSPI_FLASH_NO_ERROR) { + return fe; + } + + fe = zqspi_read_register(driver, ZQSPI_FLASH_READ_STATUS_CMD, &status); + if (fe != ZQSPI_FLASH_NO_ERROR) { + return fe; + } + + if ((status & ZQSPI_FLASH_SR_WEL) == 0) { + return ZQSPI_FLASH_READ_ONLY; + } + + return ZQSPI_FLASH_NO_ERROR; +} + +static zqspi_error zqspi_clear_WEL(zqspiflash *driver) { + uint16_t status; + zqspi_error fe; + + zqspi_transfer_buffer_clear(&driver->buf); + zqspi_transfer_buffer_set_length(&driver->buf, ZQSPI_FLASH_COMMAND_SIZE); + zqspi_transfer_buffer_set8(&driver->buf, ZQSPI_FLASH_WRITE_DISABLE_CMD); + zqspi_transfer_buffer_set_dir(&driver->buf, ZQSPI_FLASH_TX_TRANS); + zqspi_transfer_buffer_set_command_len(&driver->buf, ZQSPI_FLASH_COMMAND_SIZE); + + fe = zqspi_transfer(&driver->buf, &driver->initialised); + if (fe != ZQSPI_FLASH_NO_ERROR) { + return fe; + } + + fe = zqspi_read_register(driver, ZQSPI_FLASH_READ_STATUS_CMD, &status); + if (fe != ZQSPI_FLASH_NO_ERROR) { + return fe; + } + + if ((status & ZQSPI_FLASH_SR_WEL) != 0) { + return ZQSPI_FLASH_WRITE_LATCH_CLEAR_FAIL; + } + + return ZQSPI_FLASH_NO_ERROR; +} + +zqspi_error zqspi_read( + zqspiflash *driver, + uint32_t address, + void* buffer, + size_t length +) +{ + uint8_t* data = buffer; + + while (length) + { + zqspi_error fe; + size_t size; + + if (driver->flash_page_size == 0) { + return ZQSPI_FLASH_INVALID_DEVICE; + } + size = length > driver->flash_page_size ? driver->flash_page_size : length; + + zqspi_transfer_buffer_clear(&driver->buf); + zqspi_transfer_buffer_set_length( + &driver->buf, + ZQSPI_FLASH_COMMAND_SIZE + ZQSPI_FLASH_ADDRESS_SIZE + + driver->flash_read_dummies + size + ); + zqspi_transfer_buffer_set8(&driver->buf, ZQSPI_FLASH_READ_CMD); + zqspi_transfer_buffer_set_addr(&driver->buf, address); + fe = zqspi_transfer_buffer_fill(&driver->buf, 0, + driver->flash_read_dummies + size); + if (fe != ZQSPI_FLASH_NO_ERROR) { + return fe; + } + zqspi_transfer_buffer_set_dir(&driver->buf, ZQSPI_FLASH_RX_TRANS); + zqspi_transfer_buffer_set_command_len(&driver->buf, + ZQSPI_FLASH_COMMAND_SIZE + ZQSPI_FLASH_ADDRESS_SIZE + + driver->flash_read_dummies); + + fe = zqspi_transfer(&driver->buf, &driver->initialised); + if (fe != ZQSPI_FLASH_NO_ERROR) { + return fe; + } + + zqspi_transfer_buffer_skip(&driver->buf, + ZQSPI_FLASH_ADDRESS_SIZE + driver->flash_read_dummies); + + fe = zqspi_transfer_buffer_copy_out(&driver->buf, data, size); + if (fe != ZQSPI_FLASH_NO_ERROR) { + return fe; + } + + length -= size; + data += size; + address += size; + } + + return ZQSPI_FLASH_NO_ERROR; +} + +zqspi_error zqspi_blank(zqspiflash *driver, uint32_t address, size_t length) +{ + zqspi_error fe = ZQSPI_FLASH_NO_ERROR; + + if ( + (address >= driver->flash_size) || + ((address + length) > driver->flash_size) + ) + { + return ZQSPI_FLASH_BAD_ADDRESS; + } + + while (length) + { + size_t size; + + if (driver->flash_page_size == 0) { + return ZQSPI_FLASH_INVALID_DEVICE; + } + size = length > driver->flash_page_size ? driver->flash_page_size : length; + + zqspi_transfer_buffer_clear(&driver->buf); + zqspi_transfer_buffer_set_length(&driver->buf, + ZQSPI_FLASH_COMMAND_SIZE + ZQSPI_FLASH_ADDRESS_SIZE + + driver->flash_read_dummies + size); + zqspi_transfer_buffer_set8(&driver->buf, ZQSPI_FLASH_READ_CMD); + zqspi_transfer_buffer_set_addr(&driver->buf, address); + fe = zqspi_transfer_buffer_fill(&driver->buf, 0, + driver->flash_read_dummies + size); + if (fe != ZQSPI_FLASH_NO_ERROR) { + return fe; + } + zqspi_transfer_buffer_set_dir(&driver->buf, ZQSPI_FLASH_RX_TRANS); + zqspi_transfer_buffer_set_command_len(&driver->buf, + ZQSPI_FLASH_COMMAND_SIZE + ZQSPI_FLASH_ADDRESS_SIZE + + driver->flash_read_dummies); + + fe = zqspi_transfer(&driver->buf, &driver->initialised); + if (fe != ZQSPI_FLASH_NO_ERROR) { + return fe; + } + + zqspi_transfer_buffer_skip(&driver->buf, ZQSPI_FLASH_ADDRESS_SIZE); + + length -= size; + address += size; + + while (size) { + uint8_t byte = 0; + zqspi_transfer_buffer_get8(&driver->buf, &byte); + if (byte != 0xff) { + return ZQSPI_FLASH_NOT_BLANK; + } + --size; + } + } + + return fe; +} + +zqspi_error zqspi_erase(zqspiflash *driver, uint32_t address, size_t length) +{ + zqspi_error fe = ZQSPI_FLASH_NO_ERROR; + bool done = false; + + if ( + (address >= driver->flash_size) || + ((address + length) > driver->flash_size) + ) { + return ZQSPI_FLASH_BAD_ADDRESS; + } + if (driver->flash_page_size == 0) { + return ZQSPI_FLASH_INVALID_DEVICE; + } + + while (!done) { + fe = zqspi_erase_sector(driver, + (address - (address%(driver->flash_erase_sector_size)))); + if (fe != ZQSPI_FLASH_NO_ERROR) { + return fe; + } + + if (length < driver->flash_erase_sector_size) { + done = true; + } else { + address += driver->flash_erase_sector_size; + length -= driver->flash_erase_sector_size; + } + } + return fe; +} + +zqspi_error zqspi_erase_sector(zqspiflash *driver, uint32_t address) +{ + zqspi_error fe = ZQSPI_FLASH_NO_ERROR; + uint32_t base = 0; + size_t length = 0; + + if ( + (address >= driver->flash_size) || + ((address + length) > driver->flash_size) + ) + { + return ZQSPI_FLASH_BAD_ADDRESS; + } + + zqspi_write_unlock(driver); + + fe = zqspi_set_WEL(driver); + if (fe != ZQSPI_FLASH_NO_ERROR) + { + zqspi_write_lock(driver); + return fe; + } + + zqspi_transfer_buffer_clear(&driver->buf); + zqspi_transfer_buffer_set_length(&driver->buf, ZQSPI_FLASH_COMMAND_SIZE + + ZQSPI_FLASH_ADDRESS_SIZE); + zqspi_transfer_buffer_set8(&driver->buf, ZQSPI_FLASH_SEC_ERASE_CMD); + zqspi_transfer_buffer_set_addr(&driver->buf, address); + zqspi_transfer_buffer_set_dir(&driver->buf, ZQSPI_FLASH_TX_TRANS); + zqspi_transfer_buffer_set_command_len(&driver->buf, ZQSPI_FLASH_COMMAND_SIZE + + ZQSPI_FLASH_ADDRESS_SIZE); + + fe = zqspi_transfer(&driver->buf, &driver->initialised); + if (fe != ZQSPI_FLASH_NO_ERROR) + { + zqspi_clear_WEL(driver); + zqspi_write_lock(driver); + return fe; + } + + fe = zqspi_wait_for_write(driver, 1000); + if (fe != ZQSPI_FLASH_NO_ERROR) + { + zqspi_clear_WEL(driver); + zqspi_write_lock(driver); + return fe; + } + + zqspi_write_lock(driver); + + fe = zqspi_blank(driver, base, length); + if (fe != ZQSPI_FLASH_NO_ERROR) + return fe; + + return fe; +} + +zqspi_error zqspi_erase_device(zqspiflash *driver) +{ + zqspi_error fe = ZQSPI_FLASH_NO_ERROR; + + zqspi_write_unlock(driver); + + fe = zqspi_set_WEL(driver); + if (fe != ZQSPI_FLASH_NO_ERROR) + { + zqspi_write_lock(driver); + return fe; + } + + zqspi_transfer_buffer_clear(&driver->buf); + zqspi_transfer_buffer_set_length(&driver->buf, 1); + zqspi_transfer_buffer_set8(&driver->buf, ZQSPI_FLASH_BULK_ERASE_CMD); + zqspi_transfer_buffer_set_dir(&driver->buf, ZQSPI_FLASH_TX_TRANS); + zqspi_transfer_buffer_set_command_len(&driver->buf, ZQSPI_FLASH_COMMAND_SIZE + ZQSPI_FLASH_ADDRESS_SIZE); + + fe = zqspi_transfer(&driver->buf, &driver->initialised); + if (fe != ZQSPI_FLASH_NO_ERROR) + { + zqspi_clear_WEL(driver); + zqspi_write_lock(driver); + return fe; + } + + fe = zqspi_wait_for_write(driver, 1000000); + if (fe != ZQSPI_FLASH_NO_ERROR) + { + zqspi_clear_WEL(driver); + zqspi_write_lock(driver); + return fe; + } + + zqspi_write_lock(driver); + + return fe; +} + +zqspi_error zqspi_write( + zqspiflash *driver, + uint32_t address, + const void* buffer, + size_t length +) +{ + zqspi_error fe = ZQSPI_FLASH_NO_ERROR; + const uint8_t* data = buffer; + size_t size; + size_t byte; + bool write_block = false; + + if ( + (address >= driver->flash_size) || + ((address + length) > driver->flash_size) + ) + { + return ZQSPI_FLASH_BAD_ADDRESS; + } + + zqspi_write_unlock(driver); + + while (length) { + if (driver->flash_page_size == 0) { + return ZQSPI_FLASH_INVALID_DEVICE; + } + size = length > driver->flash_page_size ? driver->flash_page_size : length; + + /* + * If the address is not aligned to the start of a page transfer + * the remainder of the page so the address is aligned. + */ + if ((address % driver->flash_page_size) != 0) + { + size_t remaining = driver->flash_page_size + - (address % driver->flash_page_size); + if (size > remaining) + size = remaining; + } + + /* + * If the block is blank do not write it. + */ + for (byte = 0; byte < size; ++byte) + { + if (data[byte] != 0xff) + { + write_block = true; + break; + } + } + + if (write_block) + { + fe = zqspi_set_WEL(driver); + if (fe != ZQSPI_FLASH_NO_ERROR) + { + zqspi_write_lock(driver); + return fe; + } + + zqspi_transfer_buffer_clear(&driver->buf); + zqspi_transfer_buffer_set_length(&driver->buf, ZQSPI_FLASH_COMMAND_SIZE + + ZQSPI_FLASH_ADDRESS_SIZE + size); + zqspi_transfer_buffer_set8(&driver->buf, ZQSPI_FLASH_WRITE_CMD); + zqspi_transfer_buffer_set_addr(&driver->buf, address); + zqspi_transfer_buffer_set_dir(&driver->buf, ZQSPI_FLASH_TX_TRANS); + zqspi_transfer_buffer_set_command_len(&driver->buf, + ZQSPI_FLASH_COMMAND_SIZE + ZQSPI_FLASH_ADDRESS_SIZE); + fe = zqspi_transfer_buffer_copy_in(&driver->buf, data, size); + if (fe != ZQSPI_FLASH_NO_ERROR) + { + zqspi_write_lock(driver); + return fe; + } + + fe = zqspi_transfer(&driver->buf, &driver->initialised); + if (fe != ZQSPI_FLASH_NO_ERROR) + { + zqspi_clear_WEL(driver); + zqspi_write_lock(driver); + return fe; + } + + fe = zqspi_wait_for_write(driver, 1000); + if (fe != ZQSPI_FLASH_NO_ERROR) + { + zqspi_clear_WEL(driver); + zqspi_write_lock(driver); + return fe; + } + } + + address += size; + data += size; + length -= size; + } + + zqspi_write_lock(driver); + + return fe; +} + +zqspi_error zqspi_readid(zqspiflash *driver, uint32_t *jedec_id) +{ + zqspi_error fe; + uint8_t value = 0; + int index = 0; + + if (driver->jedec_id != 0) { + if (jedec_id != NULL) { + *jedec_id = driver->jedec_id; + } + return ZQSPI_FLASH_NO_ERROR; + } + + zqspi_transfer_buffer_clear(&driver->buf); + zqspi_transfer_buffer_set_length(&driver->buf, 1 + 3); + zqspi_transfer_buffer_set8(&driver->buf, ZQSPI_FLASH_READ_ID); + zqspi_transfer_buffer_fill(&driver->buf, 0x00, 3); + zqspi_transfer_buffer_set_dir(&driver->buf, ZQSPI_FLASH_RX_TRANS); + zqspi_transfer_buffer_set_command_len(&driver->buf, ZQSPI_FLASH_COMMAND_SIZE); + + fe = zqspi_transfer(&driver->buf, &driver->initialised); + if (fe != ZQSPI_FLASH_NO_ERROR) + return fe; + + zqspi_transfer_buffer_get8(&driver->buf, &value); + driver->jedec_id |= value << 16; + zqspi_transfer_buffer_get8(&driver->buf, &value); + driver->jedec_id |= value << 8; + zqspi_transfer_buffer_get8(&driver->buf, &value); + driver->jedec_id |= value; + + if (jedec_id != NULL) { + *jedec_id = driver->jedec_id; + } + + while (flash_dev_table[index].jedec_id != driver->jedec_id) { + if (flash_dev_table[index].jedec_id == 0) { + return ZQSPI_FLASH_INVALID_DEVICE; + } + index++; + } + driver->flash_size = flash_dev_table[index].flash_size; + driver->flash_erase_sector_size = flash_dev_table[index].sec_size; + driver->flash_page_size = flash_dev_table[index].page_size; +#if ZQSPI_FLASH_FAST_READ + driver->flash_read_dummies = 1; +#endif + + return ZQSPI_FLASH_NO_ERROR; +} + +size_t zqspi_device_size(zqspiflash *driver) +{ + return driver->flash_size; +} + +size_t zqspi_device_sector_erase_size(zqspiflash *driver) +{ + return driver->flash_erase_sector_size; +} + +zqspi_error zqspi_init(zqspiflash *driver) +{ + rtems_status_code sc; + uint8_t *zqspizqspi_buf = calloc(1, ZQSPI_FLASH_BUFFER_SIZE); + + driver->buf.size = ZQSPI_FLASH_BUFFER_SIZE; + driver->buf.length = 0; + driver->buf.padding = 0; + driver->buf.in = 0; + driver->buf.out = 0; + driver->buf.trans_dir = 0; + driver->buf.command_len = 0; + driver->buf.buffer = zqspizqspi_buf; + driver->buf.tx_data = NULL; + driver->buf.rx_data = NULL; + driver->buf.tx_length = 0; + driver->buf.rx_length = 0; + driver->buf.sending = 0; + driver->buf.start = false; + driver->initialised = false; + driver->jedec_id = 0; + driver->flash_size = 0; + driver->flash_read_dummies = 0; + driver->flash_erase_sector_size = 0; + driver->flash_page_size = 0; + + sc = rtems_interrupt_handler_install( + ZQPSI_ZYNQ_QSPI_IRQ, + NULL, + RTEMS_INTERRUPT_UNIQUE, + (rtems_interrupt_handler) zqspi_transfer_intr, + driver + ); + if (sc != RTEMS_SUCCESSFUL) { + free(driver->buf.buffer); + return ZQSPI_FLASH_RTEMS_INTR; + } + + return zqspi_readid(driver, NULL); +} + +void zqspi_close(zqspiflash *driver) +{ + free(driver->buf.buffer); +} diff --git a/bsps/arm/xilinx-zynq/dev/spi/zynq-qspi-flashdev.c b/bsps/arm/xilinx-zynq/dev/spi/zynq-qspi-flashdev.c new file mode 100644 index 0000000000..865226fd7c --- /dev/null +++ b/bsps/arm/xilinx-zynq/dev/spi/zynq-qspi-flashdev.c @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2024 Aaron Nyholm + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include + +static uint32_t zqspi_get_jedec_id(rtems_flashdev *flash) { + uint32_t jedec = 0; + zqspi_readid(flash->driver, &jedec); + return (jedec); +} + +static int zqspi_get_flash_type( + rtems_flashdev *flash, + rtems_flashdev_flash_type *type +) +{ + *type = RTEMS_FLASHDEV_NOR; + return 0; +} + +static int zqspi_read_wrapper( + rtems_flashdev *flash, + uintptr_t offset, + size_t count, + void *buffer +) +{ + zqspiflash *flash_driver = (zqspiflash*)flash->driver; + return zqspi_read(flash_driver, (uint32_t)offset, buffer, count); +} + +static int zqspi_page_info_by_off( + rtems_flashdev *flash, + off_t search_offset, + off_t *page_offset, + size_t *page_size +) +{ + zqspiflash *flash_driver = (zqspiflash*)flash->driver; + *page_size = (size_t)flash_driver->flash_page_size; + *page_offset = search_offset - (search_offset%((off_t)(*page_size))); + return 0; +} + +static int zqspi_page_info_by_index( + rtems_flashdev *flash, + off_t search_index, + off_t *page_offset, + size_t *page_size +) +{ + zqspiflash *flash_driver = (zqspiflash*)flash->driver; + *page_size = (size_t)flash_driver->flash_page_size; + *page_offset = *page_size * search_index; + return 0; +} + +static int zqspi_page_count( + rtems_flashdev *flash, + int *page_count +) +{ + zqspiflash *flash_driver = (zqspiflash*)flash->driver; + *page_count = flash_driver->flash_size / + flash_driver->flash_page_size; + return 0; +} + +static int zqspi_write_block_size( + rtems_flashdev *flash, + size_t *write_block_size +) +{ + *write_block_size = 1u; + return 0; +} + +static int zqspi_write_wrapper( + rtems_flashdev *flash, + uintptr_t offset, + size_t count, + const void *buffer +) +{ + zqspiflash *flash_driver = (zqspiflash*)flash->driver; + return zqspi_write(flash_driver, (uint32_t)offset, buffer, count); +} + +static int zqspi_erase_wrapper( + rtems_flashdev *flash, + uintptr_t offset, + size_t count +) +{ + zqspiflash *flash_driver = (zqspiflash*)flash->driver; + return zqspi_erase(flash_driver, (uint32_t)offset, count); +} + +static int zqspi_sector_info_by_off( + rtems_flashdev *flash, + off_t search_offset, + off_t *sector_offset, + size_t *sector_size +) +{ + zqspiflash *flash_driver = (zqspiflash*)flash->driver; + *sector_size = (size_t)flash_driver->flash_erase_sector_size; + *sector_offset = search_offset - (search_offset%((off_t)(*sector_size))); + return 0; +} + +static int zqspi_sector_count( + rtems_flashdev *flash, + int *sector_count +) +{ + zqspiflash *flash_driver = (zqspiflash*)flash->driver; + *sector_count = flash_driver->flash_size / + flash_driver->flash_erase_sector_size; + return 0; +} + +rtems_flashdev* zqspi_flashdev_init(zqspiflash *bmdriver) +{ + zqspi_flash_region_table *xtable = + calloc(1, sizeof(zqspi_flash_region_table)); + + if (xtable == NULL) { + return NULL; + } + + rtems_flashdev_region_table *ftable = + calloc(1, sizeof(rtems_flashdev_region_table)); + + if (ftable == NULL) { + free(xtable); + return NULL; + } + + ftable->regions = (rtems_flashdev_region*)xtable; + ftable->max_regions = ZQSPI_FLASH_MAX_REGIONS; + ftable->bit_allocator = &(xtable->zqspi_flash_bit_allocator); + + rtems_flashdev *flash = + rtems_flashdev_alloc_and_init(sizeof(rtems_flashdev)); + + if (flash == NULL) { + free(xtable); + free(ftable); + return NULL; + } + + flash->driver = bmdriver; + flash->read = &zqspi_read_wrapper; + flash->write = &zqspi_write_wrapper; + flash->erase = &zqspi_erase_wrapper; + flash->jedec_id = &zqspi_get_jedec_id; + flash->flash_type = &zqspi_get_flash_type; + flash->page_info_by_offset = &zqspi_page_info_by_off; + flash->page_info_by_index = &zqspi_page_info_by_index; + flash->page_count = &zqspi_page_count; + flash->write_block_size = &zqspi_write_block_size; + flash->sector_info_by_offset = &zqspi_sector_info_by_off; + flash->sector_count = &zqspi_sector_count; + flash->region_table = ftable; + + return flash; +} + +void zqspi_flashdev_destroy(rtems_flashdev* flash) +{ + free(flash->region_table->regions); + free(flash->region_table); + rtems_flashdev_destroy_and_free(flash); +} diff --git a/bsps/arm/xilinx-zynq/include/dev/spi/jedec_flash.h b/bsps/arm/xilinx-zynq/include/dev/spi/jedec_flash.h new file mode 100644 index 0000000000..4f32e71efd --- /dev/null +++ b/bsps/arm/xilinx-zynq/include/dev/spi/jedec_flash.h @@ -0,0 +1,23 @@ +#if !defined(_ZQSPIFLASH_JEDEC_H_) +#define _ZQSPIFLASH_JEDEC_H_ + +#include + +typedef struct { + uint32_t jedec_id; + uint32_t sec_size; + uint32_t page_size; + uint32_t flash_size; + uint32_t num_die; + char label[16]; +} flash_definition; + +typedef flash_definition* flash_def; + +flash_definition flash_dev_table[] = { + {0x012018, 0x10000, 0x100, 0x1000000, 1, "S25FL128P_64K"}, + {0x010218, 0x400000, 0x200, 0x4000000, 1, "S25FL512P_256K"}, + {0x0, 0x0, 0x0, 0x0, 0, "end of table"} +}; + +#endif diff --git a/bsps/arm/xilinx-zynq/include/dev/spi/zynq-qspi-flash-defs.h b/bsps/arm/xilinx-zynq/include/dev/spi/zynq-qspi-flash-defs.h new file mode 100644 index 0000000000..8c6fb72ea1 --- /dev/null +++ b/bsps/arm/xilinx-zynq/include/dev/spi/zynq-qspi-flash-defs.h @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2024 Contemporary Software + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#if !defined(_ZYNQ_QSPI_FLASH_DEFS_H_) +#define _ZYNQ_QSPI_FLASH_DEFS_H_ + +#include + +#define ZQSPI_QSPI_BASE 0xE000D000 + +/* + * Flash commands. + */ + +#define ZQSPI_FLASH_COMMAND_SIZE 1 +#if ZQSPI_FLASH_4BYTE_ADDRESSING + #define ZQSPI_FLASH_ADDRESS_SIZE 4 + #define ZQSPI_FLASH_WRITE_CMD 0x12 + #if ZQSPI_FLASH_FAST_READ + #define ZQSPI_FLASH_READ_CMD 0x0c + #else + #define ZQSPI_FLASH_READ_CMD 0x13 + #endif + #define ZQSPI_FLASH_SEC_ERASE_CMD 0xDC +#else + #define ZQSPI_FLASH_ADDRESS_SIZE 3 + #define ZQSPI_FLASH_WRITE_CMD 0x02 + #if ZQSPI_FLASH_FAST_READ + #define ZQSPI_FLASH_READ_CMD 0x0b + #else + #define ZQSPI_FLASH_READ_CMD 0x03 + #endif + #define ZQSPI_FLASH_SEC_ERASE_CMD 0xD8 +#endif + +#define ZQSPI_FLASH_READ_CONFIG_CMD 0x35 +#define ZQSPI_FLASH_WRITE_STATUS_CMD 0x01 +#define ZQSPI_FLASH_WRITE_DISABLE_CMD 0x04 +#define ZQSPI_FLASH_READ_STATUS_CMD 0x05 +#define ZQSPI_FLASH_WRITE_DISABLE_CMD 0x04 +#define ZQSPI_FLASH_WRITE_ENABLE_CMD 0x06 +#define ZQSPI_FLASH_BULK_ERASE_CMD 0xC7 +#define ZQSPI_FLASH_READ_ID 0x9F + +#define ZQSPI_FLASH_READ_STATUS_FLAG_CMD 0x05 + +/* + * QSPI registers. + */ +#define ZQSPI_QSPI_REG_CONFIG 0x00000000 +#define ZQSPI_QSPI_REG_INTR_STATUS 0x00000004 +#define ZQSPI_QSPI_REG_INTR_ENABLE 0x00000008 +#define ZQSPI_QSPI_REG_INTR_DISABLE 0x0000000c +#define ZQSPI_QSPI_REG_INTR_MASK 0x00000010 +#define ZQSPI_QSPI_REG_EN 0x00000014 +#define ZQSPI_QSPI_REG_DELAY 0x00000018 +#define ZQSPI_QSPI_REG_TXD0 0x0000001c +#define ZQSPI_QSPI_REG_RX_DATA 0x00000020 +#define ZQSPI_QSPI_REG_SLAVE_IDLE_COUNT 0x00000024 +#define ZQSPI_QSPI_REG_TX_THRES 0x00000028 +#define ZQSPI_QSPI_REG_RX_THRES 0x0000002c +#define ZQSPI_QSPI_REG_GPIO 0x00000030 +#define ZQSPI_QSPI_REG_LPBK_DLY_ADJ 0x00000038 +#define ZQSPI_QSPI_REG_TXD1 0x00000080 +#define ZQSPI_QSPI_REG_TXD2 0x00000084 +#define ZQSPI_QSPI_REG_TXD3 0x00000088 +#define ZQSPI_QSPI_REG_LSPI_CFG 0x000000a0 +#define ZQSPI_QSPI_REG_LSPI_STS 0x000000a4 +#define ZQSPI_QSPI_REG_MOD_ID 0x000000fc + +/* + * TX FIFO depth in words. + */ +#define ZQSPI_QSPI_FIFO_DEPTH (63) + +/* + * Control register. + */ +#define ZQSPI_QSPI_CR_HOLDB_DR (1 << 19) +#define ZQSPI_QSPI_CR_MANSTRT (1 << 16) +#define ZQSPI_QSPI_CR_MANSTRTEN (1 << 15) +#define ZQSPI_QSPI_CR_SSFORCE (1 << 14) +#define ZQSPI_QSPI_CR_PCS (1 << 10) +#define ZQSPI_QSPI_CR_BAUD_RATE_DIV_2 (0 << 3) +#define ZQSPI_QSPI_CR_BAUD_RATE_DIV_4 (1 << 3) +#define ZQSPI_QSPI_CR_BAUD_RATE_DIV_8 (2 << 3) +#define ZQSPI_QSPI_CR_MODE_SEL (1 << 0) + +/* + * Fast clock rate of 100MHz for fast reads. + */ +#define ZQSPI_QSPI_CR_BAUD_RATE_FAST ZQSPI_QSPI_CR_BAUD_RATE_DIV_2 + +/* + * Status register. + */ +#define ZQSPI_QSPI_IXR_TXUF (1 << 6) +#define ZQSPI_QSPI_IXR_RXFULL (1 << 5) +#define ZQSPI_QSPI_IXR_RXNEMPTY (1 << 4) +#define ZQSPI_QSPI_IXR_TXFULL (1 << 3) +#define ZQSPI_QSPI_IXR_TXOW (1 << 2) +#define ZQSPI_QSPI_INTR_RXOVR (1 << 0) + +/* + * Enable register. + */ +#define ZQSPI_QSPI_EN_SPI_ENABLE (1 << 0) + +/* + * Clock rate is 200MHz and 50MHz is the normal rate and 100MHz the fast rate. + */ +#if FLASH_FAST_READ + #define ZQSPI_QSPI_CR_BAUD_RATE ZQSPI_QSPI_CR_BAUD_RATE_DIV_2 +#else + #define ZQSPI_QSPI_CR_BAUD_RATE ZQSPI_QSPI_CR_BAUD_RATE_DIV_4 +#endif + +/* + * Flash Status bits. + */ +#define ZQSPI_FLASH_SR_WIP (1 << 0) +#define ZQSPI_FLASH_SR_WEL (1 << 1) +#define ZQSPI_FLASH_SR_BP0 (1 << 2) +#define ZQSPI_FLASH_SR_BP1 (1 << 3) +#define ZQSPI_FLASH_SR_BP2 (1 << 4) +#define ZQSPI_FLASH_SR_E_ERR (1 << 5) +#define ZQSPI_FLASH_SR_P_ERR (1 << 6) +#define ZQSPI_FLASH_SR_SRWD (1 << 7) + + +void zqspi_write_unlock(zqspiflash *driver); + +void zqspi_write_lock(zqspiflash *driver); + +zqspi_error zqspi_transfer(zqspi_transfer_buffer* transfer, bool *initialised); + +void zqspi_transfer_intr(zqspiflash *driver); + +#endif /* _ZYNQ_QSPI_FLASH_DEFS_H_ */ diff --git a/bsps/arm/xilinx-zynq/include/dev/spi/zynq-qspi-flash.h b/bsps/arm/xilinx-zynq/include/dev/spi/zynq-qspi-flash.h new file mode 100644 index 0000000000..38e0eea800 --- /dev/null +++ b/bsps/arm/xilinx-zynq/include/dev/spi/zynq-qspi-flash.h @@ -0,0 +1,311 @@ +/* + * Copyright (C) 2024 Contemporary Software + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#if !defined(_ZYNQ_QSPI_FLASH_H_) +#define _ZYNQ_QSPI_FLASH_H_ + +#include +#include + +/* + * Driver configuration. + */ +#define ZQSPI_FLASH_4BYTE_ADDRESSING 1 +#define ZQSPI_FLASH_FAST_READ 1 + +#define ZQSPI_FLASH_COMMAND_OFFSET (0) /* FLASH instruction */ +#define ZQSPI_FLASH_ADDRESS_1_OFFSET (1) /* Bits 31-24 of the address */ +#define ZQSPI_FLASH_ADDRESS_2_OFFSET (2) /* Bits 23-16 of the address */ +#define ZQSPI_FLASH_ADDRESS_3_OFFSET (3) /* Bits 16-8 of the address */ +#if ZQSPI_FLASH_4BYTE_ADDRESSING + #define ZQSPI_FLASH_ADDRESS_4_OFFSET (4) /* Bits 8-0 of the address */ + #define ZQSPI_FLASH_DATA_OFFSET (5) /* Start of Data for Read/Write */ +#else + #define ZQSPI_FLASH_DATA_OFFSET (4) /* Start of Data for Read/Write */ +#endif +#define ZQSPI_FLASH_SPI_MAX_PADDING (4) /* Maximum amount of padding. */ + +#define ZQSPI_FLASH_TX_TRANS 0 +#define ZQSPI_FLASH_RX_TRANS 1 + +#define ZQSPI_FLASH_BUFFER_SIZE 0x1008 + +#define ZQPSI_ZYNQ_QSPI_IRQ 51 + +#define ZQSPI_TIMEOUT_US 5000000U +#define ZQSPI_TIMEOUT_TICKS (ZQSPI_TIMEOUT_US \ + / rtems_configuration_get_microseconds_per_tick()) + + +/* + * Zynq QSPI Flash driver errors. + */ +typedef enum +{ + ZQSPI_FLASH_NO_ERROR = 0, + ZQSPI_FLASH_NOT_OPEN, + ZQSPI_FLASH_ALREADY_OPEN, + ZQSPI_FLASH_NO_MEMORY, + ZQSPI_FLASH_4BYTE_ADDR_NOT_SUPPORTED, + ZQSPI_FLASH_BUFFER_OVERFLOW, + ZQSPI_FLASH_BUFFER_UNDERFLOW, + ZQSPI_FLASH_BAD_ADDRESS, + ZQSPI_FLASH_NOT_BLANK, + ZQSPI_FLASH_ERASE_FAILURE, + ZQSPI_FLASH_READ_ONLY, + ZQSPI_FLASH_WRITE_LATCH_CLEAR_FAIL, + ZQSPI_FLASH_WRITE_LOCK_FAIL, + ZQSPI_FLASH_WRITE_ACROSS_SECTION, + ZQSPI_FLASH_WRITE_ERASE_CMD_FAIL, + ZQSPI_FLASH_LOCK_FAIL, + ZQSPI_FLASH_INVALID_DEVICE, + ZQPSI_FLASH_TRANSFER_FAILED, + ZQSPI_FLASH_RTEMS_INTR +} zqspi_error; + +/* + * A transfer buffer. + */ +typedef struct +{ + size_t size; + size_t length; + size_t padding; + size_t in; + size_t out; + uint32_t trans_dir; + uint32_t command_len; + uint8_t *buffer; + uint32_t *tx_data; + uint32_t *rx_data; + size_t tx_length; + size_t rx_length; + size_t sending; + bool start; +} zqspi_transfer_buffer; + +/* + * A flash driver instance + */ +typedef struct +{ + zqspi_transfer_buffer buf; + bool initialised; + uint32_t jedec_id; + uint64_t flash_size; + uint32_t flash_read_dummies; + uint32_t flash_erase_sector_size; + uint32_t flash_page_size; +} zqspiflash; + +/** + * @brief Initialize zqspiflash driver. + * + * @param zqspiflash Device context. + * + * @retval zqspi_error code. + */ +zqspi_error zqspi_init(zqspiflash *driver); + +/** + * @brief Close zqspiflash driver. + * + * @param zqspiflash Device context. + */ +void zqspi_close(zqspiflash *driver); + +/** + * @brief Read from zqspiflash. + * + * @param zqspiflash Device context. + * @param address Address to read from. + * @param buffer Buffer to read data into. + * @param length Length of requested read. + * + * @retval zqspi_error Error code. + */ +zqspi_error zqspi_read( + zqspiflash *driver, + uint32_t address, + void* buffer, + size_t length +); + +/** + * @brief Write to zqspiflash. + * + * @param zqspiflash Device context. + * @param address Address to write to. + * @param buffer Buffer to write data from. + * @param length Length of requested write. + * + * @retval zqspi_error Error code. + */ +zqspi_error zqspi_write( + zqspiflash *driver, + uint32_t address, + const void* buffer, + size_t length +); + +/** + * @brief Write to zqspiflash. + * + * @param zqspiflash Device context. + * @param address Address to erase. + * @param length Length of requested erase. + * + * @retval zqspi_error Error code. + */ +zqspi_error zqspi_erase( + zqspiflash *driver, + uint32_t address, + size_t length +); + +/** + * @brief Blank check zqspiflash. + * + * @param zqspiflash Device context. + * @param address Address to check. + * @param length Length of requested check. + * + * @retval zqspi_error Error code. + */ +zqspi_error zqspi_blank( + zqspiflash *driver, + uint32_t address, + size_t length +); + +/** + * @brief Erase sector of zqspiflash. + * + * @param zqspiflash Device context. + * @param address Address of sector to erase. + * + * @retval zqspi_error Error code. + */ +zqspi_error zqspi_erase_sector( + zqspiflash *driver, + uint32_t address +); + +/** + * @brief Write sector to zqspiflash. + * + * @param zqspiflash Device context. + * @param address Address to write to. + * @param length Length of requested write. + * + * @retval zqspi_error Error code. + */ +zqspi_error zqspi_write_sector( + zqspiflash *driver, + uint32_t address, + const void* buffer, + size_t length +); + +/** + * @brief Erases all of zqspiflash. + * + * @param zqspiflash Device context. + * + * @retval zqspi_error error code. + */ +zqspi_error zqspi_erase_device(zqspiflash *driver); + +/** + * @brief Get JEDEC ID of zqspiflash. + * + * @param zqspiflash Device context. + * @param jedec_id Pounter to uin32_t which the JEDEC ID is + * returned in. + * + * @retval zqspi_error error code. + */ +zqspi_error zqspi_readid(zqspiflash *driver, uint32_t *jedec_id); + +/** + * @brief Get JEDEC ID of zqspiflash. + * + * @param zqspiflash Device context. + * + * @retval size Size of flash. + */ +size_t zqspi_device_size(zqspiflash *driver); + +/** + * @brief Get size of erase sectors of zqspiflash. + * + * @param zqspiflash Device context. + * + * @retval size Size of erase sector of flash. + */ +size_t zqspi_device_sector_erase_size(zqspiflash *driver); + +/** + * @brief If debug enabled prints trace of flash transfer. + * + * @param message Trace header. + * @param transfer Transfer to print. + */ +void zqspi_transfer_trace( + const char* message, + const zqspi_transfer_buffer* transfer +); + +/** + * @brief Writes a uint32_t to a ZYNQ7000 QSPI register. + * + * @param reg Register offset. + * @param value Value to write. + */ +void qspi_reg_write(uint32_t reg, uint32_t value); + +/** + * @brief Read a uint32_t from a ZYNQ7000 QSPI register. + * + * @param reg Register offset. + * + * @retval value Value read from register. + */ +uint32_t qspi_reg_read(uint32_t reg); + +/** + * @brief Skip value in transfer buffer. + * + * @param transfer Buffer work on. + * @param size Number of bytes to skip. + * + * @retval zqspi_error error code. + */ +zqspi_error zqspi_transfer_buffer_skip( + zqspi_transfer_buffer* transfer, + const size_t size +); + +#endif /* _ZYNQ_QSPI_FLASH_H_ */ diff --git a/bsps/arm/xilinx-zynq/include/dev/spi/zynq-qspi-flashdev.h b/bsps/arm/xilinx-zynq/include/dev/spi/zynq-qspi-flashdev.h new file mode 100644 index 0000000000..4fe4b8f124 --- /dev/null +++ b/bsps/arm/xilinx-zynq/include/dev/spi/zynq-qspi-flashdev.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2024 Aaron Nyholm + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _ZYNQ_QSPI_FLASHDEV_H +#define _ZYNQ_QSPI_FLASHDEV_H + +#include +#include + +#define ZQSPI_FLASH_MAX_REGIONS ((size_t)32) + +/* + * @brief Initializes a flash device using zynq qspi flash + * driver. The flash device is not registered in this call. + * If an rtems_flashdev is created using zqspi_flash_init it must be + * destroyed using xqspi_flash_destroy. + * + * @param[in] zqspiflash A initialised zqspiflash device to wrap. + * + * @retval A pointer to the rtems_flashdev. + * @retval NULL on failure. +*/ +rtems_flashdev* zqspi_flashdev_init(zqspiflash *bmdriver); + +/* + * @brief Destroys a rtems_flashdev initialised with zqspi_flash_init. + * If an rtems_flashdev is created using zqspi_flash_init it must be + * destroyed using zqspi_flash_destroy. The zqspiflash originally passed in + * is untouched. + * + * @param[in] flash The flashdev to destroy +*/ +void zqspi_flashdev_destroy(rtems_flashdev* flash); + +/* + * @brief Struct allocating memory space for flash regions. Used by + * rtems_flashdev to store region allocations. + */ +typedef struct zqspi_flash_region_table { + rtems_flashdev_region zqspi_flash_regions[ZQSPI_FLASH_MAX_REGIONS]; + uint32_t zqspi_flash_bit_allocator; +} zqspi_flash_region_table; + +#endif /* _ZYNQ_QSPI_FLASHDEV_H */ diff --git a/spec/build/bsps/arm/xilinx-zynq/grp.yml b/spec/build/bsps/arm/xilinx-zynq/grp.yml index 8b5e53b5e3..79a75de9c8 100644 --- a/spec/build/bsps/arm/xilinx-zynq/grp.yml +++ b/spec/build/bsps/arm/xilinx-zynq/grp.yml @@ -18,6 +18,8 @@ links: uid: abi - role: build-dependency uid: obj +- role: build-dependency + uid: objflash - role: build-dependency uid: objsmp - role: build-dependency diff --git a/spec/build/bsps/arm/xilinx-zynq/objflash.yml b/spec/build/bsps/arm/xilinx-zynq/objflash.yml new file mode 100644 index 0000000000..2a99cb9ec6 --- /dev/null +++ b/spec/build/bsps/arm/xilinx-zynq/objflash.yml @@ -0,0 +1,22 @@ +SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause +build-type: objects +cflags: [] +copyrights: + - Copyright (C) 2024 Aaron Nyholm +cppflags: [] +cxxflags: [] +enabled-by: true +includes: [] +install: +- destination: ${BSP_INCLUDEDIR}/dev/spi + source: + - bsps/arm/xilinx-zynq/include/dev/spi/jedec_flash.h + - bsps/arm/xilinx-zynq/include/dev/spi/zynq-qspi-flashdev.h + - bsps/arm/xilinx-zynq/include/dev/spi/zynq-qspi-flash.h + - bsps/arm/xilinx-zynq/include/dev/spi/zynq-qspi-flash-defs.h +links: [] +source: + - bsps/arm/xilinx-zynq/dev/spi/zynq-qspi-flashdev.c + - bsps/arm/xilinx-zynq/dev/spi/zynq-qspi-flash.c + - bsps/arm/xilinx-zynq/dev/spi/zynq-qspi-flash-transfer.c +type: build