MindSDK_MM32F5270/driver_examples/qspi/qspi_x25q_basic/x25q.c
Yilin Sun 3977144e90
Initial MM32F527x commit.
Signed-off-by: Yilin Sun <imi415@imi.moe>
2023-03-27 21:54:40 +08:00

555 lines
19 KiB
C

/* x25q.c */
#include "x25q.h"
#include "stdio.h"
static X25Q_DeviceInfo_Type X25Q_DeviceTable[] = X25Q_DEVICE_TABLE; /* record the device information. */
static void X25Q_EnableWrite(uint32_t dev_index, bool enable); /* enable Write. */
static uint32_t X25Q_ReadRegisterValue(uint32_t dev_index, uint32_t reg_index); /* read register value, the index of register x is (x - 1). */
static void X25Q_WriteRegisterValue(uint32_t dev_index, uint32_t reg_index, uint8_t value); /* write register value, the index of register x is (x - 1). */
static void X25Q_WaitBusyBlocking(uint32_t dev_index); /* wait for chip idle. */
static void X25Q_EnableQPI(uint32_t dev_index, bool enable); /* enable QPI mode. */
static void X25Q_EnableQE(uint32_t dev_index, bool enable); /* enable Quad Mode (IO2 & IO3 are used). */
/* init x25q. */
void X25Q_Init(void)
{
X25Q_HAL_Init(); /* init hardware. */
/* init chip operation mode. */
for (uint32_t i = 0; i < sizeof(X25Q_DeviceTable) / sizeof(X25Q_DeviceInfo_Type); i++)
{
if (0 == X25Q_DeviceTable[i].MemSize) /* invalid device info. */
{
continue;
}
X25Q_EnableQPI(i, false); /* close qpi mode, even if it is not enableed. */
switch (X25Q_DeviceTable[i].OperationMode)
{
case X25Q_OperationMode_SPI:
break; /* do nothing. */
case X25Q_OperationMode_DSPI:
break; /* do nothing. */
case X25Q_OperationMode_QSPI:
X25Q_EnableQE(i, true);
break; /* need to enable QE mode. */
case X25Q_OperationMode_QPI:
X25Q_EnableQE(i, true); /* need to enable QE mode before enable QPI mode. */
X25Q_EnableQPI(i, true); /* enable QPI mode. */
break;
default:
break;
}
}
}
/* read manufactor id. */
uint32_t X25Q_ReadManuId(uint32_t dev_index)
{
uint8_t buf[2u] = {0u};
X25Q_Xfer_Type xfer;
switch (X25Q_DeviceTable[dev_index].OperationMode) /* set the bus width. */
{
case X25Q_OperationMode_SPI: /* read manu id by spi. */
xfer.CmdBusWidth = X25Q_BusWidth_1b;
xfer.AddrBusWidth = X25Q_BusWidth_1b;
xfer.DataBusWidth = X25Q_BusWidth_1b;
break;
case X25Q_OperationMode_DSPI: /* read manu id by spi. */
xfer.CmdBusWidth = X25Q_BusWidth_1b;
xfer.AddrBusWidth = X25Q_BusWidth_1b;
xfer.DataBusWidth = X25Q_BusWidth_1b;
break;
case X25Q_OperationMode_QSPI: /* read manu id by spi. */
xfer.CmdBusWidth = X25Q_BusWidth_1b;
xfer.AddrBusWidth = X25Q_BusWidth_1b;
xfer.DataBusWidth = X25Q_BusWidth_1b;
break;
case X25Q_OperationMode_QPI: /* read manu id by qpi. */
xfer.CmdBusWidth = X25Q_BusWidth_4b;
xfer.AddrBusWidth = X25Q_BusWidth_4b;
xfer.DataBusWidth = X25Q_BusWidth_4b;
break;
default: /* invalid operation mode, using spi mode. */
xfer.CmdBusWidth = X25Q_BusWidth_1b;
xfer.AddrBusWidth = X25Q_BusWidth_1b;
xfer.DataBusWidth = X25Q_BusWidth_1b;
break;
}
xfer.DeviceIndex = dev_index;
xfer.AddrWordWidth = X25Q_WordWidth_24b; /* after 2 dummy bytes, need to send a 0x00 data, then can recv manu id. */
xfer.AddrValue = 0xFFFF00; /* use addr phase instead dummy phase. */
xfer.DummyBytes = 0u;
xfer.CmdValue = X25Q_Cmd_GetManuId;
xfer.DataLen = sizeof(buf);
xfer.DataBuf = buf;
X25Q_HAL_Read(&xfer);
return (buf[0u] << 8u) | buf[1u]; /* big endian. */
}
/* erase chip data. */
void X25Q_EraseChip(uint32_t dev_index)
{
X25Q_EnableWrite(dev_index, true); /* must enable write before erase chip. */
X25Q_Xfer_Type xfer;
switch (X25Q_DeviceTable[dev_index].OperationMode)
{
case X25Q_OperationMode_SPI: /* cmd phase use 1b bus. */
xfer.CmdBusWidth = X25Q_BusWidth_1b;
break;
case X25Q_OperationMode_DSPI: /* cmd phase use 1b bus. */
xfer.CmdBusWidth = X25Q_BusWidth_1b;
break;
case X25Q_OperationMode_QSPI: /* cmd phase use 1b bus. */
xfer.CmdBusWidth = X25Q_BusWidth_1b;
break;
case X25Q_OperationMode_QPI: /* cmd phase use 4b bus. */
xfer.CmdBusWidth = X25Q_BusWidth_4b;
break;
default: /* unknow the operation, using SPI mode. */
xfer.CmdBusWidth = X25Q_BusWidth_1b;
break;
}
xfer.DeviceIndex = dev_index;
xfer.CmdValue = X25Q_Cmd_EraseChip;
xfer.AddrBusWidth = X25Q_BusWidth_None;
xfer.DummyBytes = 0u;
xfer.DataBusWidth = X25Q_BusWidth_None;
xfer.DataLen = 0;
X25Q_HAL_Write(&xfer);
X25Q_WaitBusyBlocking(dev_index); /* wait for erase chip done. */
X25Q_EnableWrite(dev_index, false); /* disable write function. */
}
/* erase sector. */
void X25Q_EraseSector(uint32_t dev_index, uint32_t addr)
{
if (addr > X25Q_DeviceTable[dev_index].MemSize - X25Q_DeviceTable[dev_index].SectorSize)
{
return; /* addr overflow. */
}
X25Q_EnableWrite(dev_index, true); /* must enable write before erase sector. */
X25Q_Xfer_Type xfer;
switch (X25Q_DeviceTable[dev_index].OperationMode)
{
case X25Q_OperationMode_SPI: /* cmd % addr phase use 1b bus. */
xfer.CmdBusWidth = X25Q_BusWidth_1b;
xfer.AddrBusWidth = X25Q_BusWidth_1b;
break;
case X25Q_OperationMode_DSPI: /* cmd % addr phase use 1b bus. */
xfer.CmdBusWidth = X25Q_BusWidth_1b;
xfer.AddrBusWidth = X25Q_BusWidth_1b;
break;
case X25Q_OperationMode_QSPI: /* cmd % addr phase use 1b bus. */
xfer.CmdBusWidth = X25Q_BusWidth_1b;
xfer.AddrBusWidth = X25Q_BusWidth_1b;
break;
case X25Q_OperationMode_QPI: /* all phase use 4b bus. */
xfer.CmdBusWidth = X25Q_BusWidth_4b;
xfer.AddrBusWidth = X25Q_BusWidth_4b;
break;
default: /* unknow the operation, using SPI mode. */
xfer.CmdBusWidth = X25Q_BusWidth_1b;
xfer.AddrBusWidth = X25Q_BusWidth_1b;
break;
}
xfer.DeviceIndex = dev_index;
xfer.AddrWordWidth = X25Q_WordWidth_24b;
xfer.CmdValue = X25Q_Cmd_EraseSector;
xfer.AddrValue = addr;
xfer.DummyBytes = 0u;
xfer.DataBusWidth = X25Q_BusWidth_None;
xfer.DataLen = 0;
X25Q_HAL_Write(&xfer);
X25Q_WaitBusyBlocking(dev_index); /* wait for erase sector done. */
X25Q_EnableWrite(dev_index, false); /* close write. */
}
/* write page. */
void X25Q_WritePage(uint32_t dev_index, uint32_t addr, uint32_t size, uint8_t * buf)
{
if (addr + size > X25Q_DeviceTable[dev_index].MemSize) /* addr overflow. */
{
return;
}
X25Q_EnableWrite(dev_index, true); /* must enable write before write page. */
X25Q_Xfer_Type xfer;
switch (X25Q_DeviceTable[dev_index].OperationMode)
{
case X25Q_OperationMode_SPI: /* 1b bus in data phase. */
xfer.CmdBusWidth = X25Q_BusWidth_1b;
xfer.CmdValue = X25Q_Cmd_WritePage;
xfer.AddrBusWidth = X25Q_BusWidth_1b;
xfer.DataBusWidth = X25Q_BusWidth_1b;
break;
case X25Q_OperationMode_DSPI: /* DSPI use SPI mode to write data. */
xfer.CmdBusWidth = X25Q_BusWidth_1b;
xfer.CmdValue = X25Q_Cmd_WritePage;
xfer.AddrBusWidth = X25Q_BusWidth_1b;
xfer.DataBusWidth = X25Q_BusWidth_1b;
break;
case X25Q_OperationMode_QSPI: /* 4b bus in data phase. */
xfer.CmdBusWidth = X25Q_BusWidth_1b;
xfer.CmdValue = X25Q_Cmd_WritePageQuad;
xfer.AddrBusWidth = X25Q_BusWidth_1b;
xfer.DataBusWidth = X25Q_BusWidth_4b;
break;
case X25Q_OperationMode_QPI: /* 4b bus in all phase. */
xfer.CmdBusWidth = X25Q_BusWidth_4b;
xfer.CmdValue = X25Q_Cmd_WritePage;
xfer.AddrBusWidth = X25Q_BusWidth_4b;
xfer.DataBusWidth = X25Q_BusWidth_4b;
break;
default: /* unknow the operation, using SPI mode. */
xfer.CmdBusWidth = X25Q_BusWidth_1b;
xfer.CmdValue = X25Q_Cmd_WritePage;
xfer.AddrBusWidth = X25Q_BusWidth_1b;
xfer.DataBusWidth = X25Q_BusWidth_1b;
break;
}
xfer.DeviceIndex = dev_index;
xfer.AddrWordWidth = X25Q_WordWidth_24b;
xfer.AddrValue = addr;
xfer.DummyBytes = 0u;
xfer.DataLen = size;
xfer.DataBuf = buf;
X25Q_HAL_Write(&xfer);
X25Q_WaitBusyBlocking(dev_index); /* wait for write page done (chip idle). */
X25Q_EnableWrite(dev_index, false); /* close write. */
}
void X25Q_WriteData(uint32_t dev_index, uint32_t addr, uint32_t size, uint8_t * buf)
{
if (addr + size > X25Q_DeviceTable[dev_index].MemSize)/* addr overflow. */
{
return;
}
uint32_t xfer_count = 0u;
uint32_t xfer_remain = size;
uint32_t xfer_size = 0u;
/* align write. */
if (0u != addr % X25Q_DeviceTable[dev_index].PageSize)
{
uint32_t xfer_align = X25Q_DeviceTable[dev_index].PageSize - (addr % X25Q_DeviceTable[dev_index].PageSize);
xfer_size = (xfer_remain > xfer_align ? xfer_align : xfer_remain);
X25Q_WritePage(dev_index, addr + xfer_count, xfer_size, &buf[xfer_count]);
xfer_remain -= xfer_size;
xfer_count += xfer_size;
}
/* page write. */
while(xfer_remain > 0u)
{
xfer_size = (xfer_remain > X25Q_DeviceTable[dev_index].PageSize ? X25Q_DeviceTable[dev_index].PageSize : xfer_remain);
X25Q_WritePage(dev_index, addr + xfer_count, xfer_size, &buf[xfer_count]);
xfer_remain -= xfer_size;
xfer_count += xfer_size;
}
}
/* read data. */
void X25Q_ReadData(uint32_t dev_index, uint32_t addr, uint32_t size, uint8_t * buf)
{
X25Q_Xfer_Type xfer;
if (addr + size > X25Q_DeviceTable[dev_index].MemSize)
{
return; /* addr overflow. */
}
switch (X25Q_DeviceTable[dev_index].OperationMode) /* set the info of xfer. */
{
case X25Q_OperationMode_SPI: /* all phase use 1b bus. */
xfer.CmdBusWidth = X25Q_BusWidth_1b;
xfer.CmdValue = X25Q_Cmd_FaseRead;
xfer.AddrBusWidth = X25Q_BusWidth_1b;
xfer.DummyBytes = 1u;
xfer.DataBusWidth = X25Q_BusWidth_1b;
break;
case X25Q_OperationMode_DSPI: /* data phase use 2b bus. */
xfer.CmdBusWidth = X25Q_BusWidth_1b;
xfer.CmdValue = X25Q_Cmd_FaseReadDual;
xfer.AddrBusWidth = X25Q_BusWidth_1b;
xfer.DummyBytes = 2u;
xfer.DataBusWidth = X25Q_BusWidth_2b;
break;
case X25Q_OperationMode_QSPI: /* data phase use 4b bus. */
xfer.CmdBusWidth = X25Q_BusWidth_1b;
xfer.CmdValue = X25Q_Cmd_FaseReadQuad;
xfer.AddrBusWidth = X25Q_BusWidth_1b;
xfer.DummyBytes = 4u;
xfer.DataBusWidth = X25Q_BusWidth_4b;
break;
case X25Q_OperationMode_QPI: /* all phase use 4b bus. */
xfer.CmdBusWidth = X25Q_BusWidth_4b;
xfer.CmdValue = X25Q_Cmd_FaseRead;
xfer.AddrBusWidth = X25Q_BusWidth_4b;
xfer.DummyBytes = 1u;
xfer.DataBusWidth = X25Q_BusWidth_4b;
break;
default: /* unknow the operation, using SPI mode. */
xfer.CmdBusWidth = X25Q_BusWidth_1b;
xfer.CmdValue = X25Q_Cmd_FaseRead;
xfer.AddrBusWidth = X25Q_BusWidth_1b;
xfer.DummyBytes = 1u;
xfer.DataBusWidth = X25Q_BusWidth_1b;
break;
}
xfer.DeviceIndex = dev_index;
xfer.AddrWordWidth = X25Q_WordWidth_24b;
xfer.AddrValue = addr;
xfer.DataLen = size;
xfer.DataBuf = buf;
X25Q_HAL_Read(&xfer); /* read data. */
}
/* write & erase must enable write function. */
static void X25Q_EnableWrite(uint32_t dev_index, bool enable)
{
X25Q_Xfer_Type xfer;
switch (X25Q_DeviceTable[dev_index].OperationMode)
{
case X25Q_OperationMode_SPI:
xfer.CmdBusWidth = X25Q_BusWidth_1b;
break;
case X25Q_OperationMode_DSPI:
xfer.CmdBusWidth = X25Q_BusWidth_1b;
break;
case X25Q_OperationMode_QSPI:
xfer.CmdBusWidth = X25Q_BusWidth_1b;
break;
case X25Q_OperationMode_QPI:
xfer.CmdBusWidth = X25Q_BusWidth_4b;
break;
default:
xfer.CmdBusWidth = X25Q_BusWidth_1b;
break;
}
xfer.DeviceIndex = dev_index;
xfer.AddrBusWidth = X25Q_BusWidth_None; /* no addr phase. */
xfer.DummyBytes = 0u; /* no dummy phase. */
xfer.DataBusWidth = X25Q_BusWidth_None; /* no data phase. */
xfer.DataLen = 0;
if (true == enable)
{
xfer.CmdValue = X25Q_Cmd_EnableWrite; /* enable write. */
}
else
{
xfer.CmdValue = X25Q_Cmd_DisableWrite; /* disable write. */
}
X25Q_HAL_Write(&xfer);
}
/* read register value from device register n, n = reg_index + 1. */
static uint32_t X25Q_ReadRegisterValue(uint32_t dev_index, uint32_t reg_index)
{
const X25Q_Cmd_Type cmd[3u] = {X25Q_Cmd_ReadRegister1, X25Q_Cmd_ReadRegister2, X25Q_Cmd_ReadRegister3};
X25Q_Xfer_Type xfer;
uint8_t buf[1u];
switch (X25Q_DeviceTable[dev_index].OperationMode)
{
case X25Q_OperationMode_SPI: /* xfer by spi. */
xfer.CmdBusWidth = X25Q_BusWidth_1b;
xfer.DataBusWidth = X25Q_BusWidth_1b;
break;
case X25Q_OperationMode_DSPI: /* xfer by spi. */
xfer.CmdBusWidth = X25Q_BusWidth_1b;
xfer.DataBusWidth = X25Q_BusWidth_1b;
break;
case X25Q_OperationMode_QSPI: /* xfer by spi. */
xfer.CmdBusWidth = X25Q_BusWidth_1b;
xfer.DataBusWidth = X25Q_BusWidth_1b;
break;
case X25Q_OperationMode_QPI: /* xfer by qpi. */
xfer.CmdBusWidth = X25Q_BusWidth_4b;
xfer.DataBusWidth = X25Q_BusWidth_4b;
break;
default: /* unknow operation mode, using spi mode. */
xfer.CmdBusWidth = X25Q_BusWidth_1b;
xfer.DataBusWidth = X25Q_BusWidth_1b;
break;
}
xfer.DeviceIndex = dev_index;
xfer.CmdValue = cmd[reg_index];
xfer.AddrBusWidth = X25Q_BusWidth_None; /* no addr phase. */
xfer.DummyBytes = 0u; /* no dummy phase. */
xfer.DataLen = sizeof(buf);
xfer.DataBuf = buf;
X25Q_HAL_Read(&xfer);
return buf[0u];
}
/* write register value to device register n, n = reg_index + 1. */
static void X25Q_WriteRegisterValue(uint32_t dev_index, uint32_t reg_index, uint8_t value)
{
const X25Q_Cmd_Type cmd[3u] = {X25Q_Cmd_WriteRegister1, X25Q_Cmd_WriteRegister2, X25Q_Cmd_WriteRegister3};
X25Q_Xfer_Type xfer;
X25Q_EnableWrite(dev_index, true); /* enable write. */
switch (X25Q_DeviceTable[dev_index].OperationMode)
{
case X25Q_OperationMode_SPI: /* xfer by spi. */
xfer.CmdBusWidth = X25Q_BusWidth_1b;
xfer.DataBusWidth = X25Q_BusWidth_1b;
break;
case X25Q_OperationMode_DSPI: /* xfer by spi. */
xfer.CmdBusWidth = X25Q_BusWidth_1b;
xfer.DataBusWidth = X25Q_BusWidth_1b;
break;
case X25Q_OperationMode_QSPI: /* xfer by spi. */
xfer.CmdBusWidth = X25Q_BusWidth_1b;
xfer.DataBusWidth = X25Q_BusWidth_1b;
break;
case X25Q_OperationMode_QPI: /* xfer by qpi. */
xfer.CmdBusWidth = X25Q_BusWidth_4b;
xfer.DataBusWidth = X25Q_BusWidth_4b;
break;
default: /* unknow operation mode, using spi mode. */
xfer.CmdBusWidth = X25Q_BusWidth_1b;
xfer.DataBusWidth = X25Q_BusWidth_1b;
break;
}
xfer.DeviceIndex = dev_index;
xfer.CmdValue = cmd[reg_index];
xfer.AddrBusWidth = X25Q_BusWidth_None; /* no addr phase. */
xfer.DummyBytes = 0u; /* no dummy phase. */
xfer.DataLen = 1u;
xfer.DataBuf = &value;
X25Q_HAL_Write(&xfer);
X25Q_EnableWrite(dev_index, false); /* disable write. */
}
/* wait write or erase done. */
static void X25Q_WaitBusyBlocking(uint32_t dev_index)
{
while(0u != (X25Q_ReadRegisterValue(dev_index, 0u) & X25Q_REGISTER1_BUSY_MASK) )
{
}
}
/* enable qpi mode, all phase bus is 4b. */
static void X25Q_EnableQPI(uint32_t dev_index, bool enable)
{
X25Q_Xfer_Type xfer;
if (true == enable) /* xspi -> qpi */
{
xfer.CmdBusWidth = X25Q_BusWidth_1b;
xfer.CmdValue = X25Q_Cmd_EnableQpi;
}
else /* qpi -> xspi. */
{
xfer.CmdBusWidth = X25Q_BusWidth_4b;
xfer.CmdValue = X25Q_Cmd_DisableQpi;
}
xfer.DeviceIndex = dev_index;
xfer.AddrBusWidth = X25Q_BusWidth_None; /* no addr. */
xfer.DummyBytes = 0u; /* no dummy. */
xfer.DataBusWidth = X25Q_BusWidth_None; /* no data. */
xfer.DataLen = 0u;
/* write. */
X25Q_HAL_Write(&xfer);
}
/* enable QE mode, WP & HOLD be used for IO2 & IO3. */
static void X25Q_EnableQE(uint32_t dev_index, bool enable)
{
X25Q_Xfer_Type xfer;
uint8_t buf[1];
/* enable write by spi mode. */
xfer.DeviceIndex = dev_index;
xfer.CmdBusWidth = X25Q_BusWidth_1b;
xfer.CmdValue = X25Q_Cmd_EnableWrite;
xfer.AddrBusWidth = X25Q_BusWidth_None;
xfer.DummyBytes = 0u;
xfer.DataBusWidth = X25Q_BusWidth_None;
xfer.DataLen = 0u;
X25Q_HAL_Write(&xfer);
/* read register 2 value by spi mode. */
xfer.CmdValue = X25Q_Cmd_ReadRegister2;
xfer.DataBusWidth = X25Q_BusWidth_1b;
xfer.DataLen = sizeof(buf);
xfer.DataBuf = buf;
X25Q_HAL_Read(&xfer);
/* set value. */
if (true == enable)
{
buf[0] |= X25Q_REGISTER2_QE_MASK; /* set QE bit. */
}
else
{
buf[0] &= ~X25Q_REGISTER2_QE_MASK; /* clear QE bit. */
}
/* write register 2 by spi mode. */
xfer.CmdValue = X25Q_Cmd_WriteRegister2;
X25Q_HAL_Write(&xfer);
/* wait for write done. */
xfer.CmdValue = X25Q_Cmd_ReadRegister1;
buf[0] = X25Q_REGISTER1_BUSY_MASK;
while(0 != (buf[0] & X25Q_REGISTER1_BUSY_MASK) )
{
X25Q_HAL_Read(&xfer);
}
/* disable write. */
xfer.CmdValue = X25Q_Cmd_DisableWrite;
xfer.DataBusWidth = X25Q_BusWidth_None;
xfer.DataLen = 0u;
X25Q_HAL_Write(&xfer);
}
/* EOF. */