forked from Imagelibrary/rtems
bsps/xilinx-zynq: Add QSPI flash driver
This commit is contained in:
committed by
Kinsey Moore
parent
d6f3cd72ee
commit
177a8c57ea
259
bsps/arm/xilinx-zynq/dev/spi/zynq-qspi-flash-transfer.c
Normal file
259
bsps/arm/xilinx-zynq/dev/spi/zynq-qspi-flash-transfer.c
Normal file
@@ -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 <rtems.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <dev/spi/zynq-qspi-flash-defs.h>
|
||||
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
848
bsps/arm/xilinx-zynq/dev/spi/zynq-qspi-flash.c
Normal file
848
bsps/arm/xilinx-zynq/dev/spi/zynq-qspi-flash.c
Normal file
@@ -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 <rtems.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <dev/spi/zynq-qspi-flash.h>
|
||||
#include <dev/spi/zynq-qspi-flash-defs.h>
|
||||
#include <dev/spi/jedec_flash.h>
|
||||
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
202
bsps/arm/xilinx-zynq/dev/spi/zynq-qspi-flashdev.c
Normal file
202
bsps/arm/xilinx-zynq/dev/spi/zynq-qspi-flashdev.c
Normal file
@@ -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 <bsp/irq.h>
|
||||
#include <dev/spi/zynq-qspi-flashdev.h>
|
||||
#include <dev/spi/zynq-qspi-flash.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
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);
|
||||
}
|
||||
23
bsps/arm/xilinx-zynq/include/dev/spi/jedec_flash.h
Normal file
23
bsps/arm/xilinx-zynq/include/dev/spi/jedec_flash.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#if !defined(_ZQSPIFLASH_JEDEC_H_)
|
||||
#define _ZQSPIFLASH_JEDEC_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
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
|
||||
161
bsps/arm/xilinx-zynq/include/dev/spi/zynq-qspi-flash-defs.h
Normal file
161
bsps/arm/xilinx-zynq/include/dev/spi/zynq-qspi-flash-defs.h
Normal file
@@ -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 <dev/spi/zynq-qspi-flash.h>
|
||||
|
||||
#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_ */
|
||||
311
bsps/arm/xilinx-zynq/include/dev/spi/zynq-qspi-flash.h
Normal file
311
bsps/arm/xilinx-zynq/include/dev/spi/zynq-qspi-flash.h
Normal file
@@ -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 <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/*
|
||||
* 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_ */
|
||||
66
bsps/arm/xilinx-zynq/include/dev/spi/zynq-qspi-flashdev.h
Normal file
66
bsps/arm/xilinx-zynq/include/dev/spi/zynq-qspi-flashdev.h
Normal file
@@ -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 <dev/flash/flashdev.h>
|
||||
#include <dev/spi/zynq-qspi-flash.h>
|
||||
|
||||
#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 */
|
||||
Reference in New Issue
Block a user