rt-thread/bsp/nuvoton/libraries/m460/StdDriver/src/nu_spim.c

1384 lines
41 KiB
C

/**************************************************************************//**
* @file spim.c
* @version V1.00
* @brief M460 series SPIM driver
*
* @copyright SPDX-License-Identifier: Apache-2.0
* @copyright Copyright (C) 2021 Nuvoton Technology Corp. All rights reserved.
*****************************************************************************/
#include <stdio.h>
#include <string.h>
#include "NuMicro.h"
/** @addtogroup Standard_Driver Standard Driver
@{
*/
/** @addtogroup SPIM_Driver SPIM Driver
@{
*/
int32_t g_SPIM_i32ErrCode = 0; /*!< SPIM global error code */
/** @addtogroup SPIM_EXPORTED_FUNCTIONS SPIM Exported Functions
@{
*/
/** @cond HIDDEN_SYMBOLS */
#define ENABLE_DEBUG 0
#if ENABLE_DEBUG
#define SPIM_DBGMSG printf
#else
#define SPIM_DBGMSG(...) do { } while (0) /* disable debug */
#endif
static volatile uint8_t g_Supported_List[] =
{
MFGID_WINBOND,
MFGID_MXIC,
MFGID_EON,
MFGID_ISSI,
MFGID_SPANSION
};
static void N_delay(int n);
static void SwitchNBitOutput(uint32_t u32NBit);
static void SwitchNBitInput(uint32_t u32NBit);
static void spim_write(uint8_t pu8TxBuf[], uint32_t u32NTx);
static void spim_read(uint8_t pu8RxBuf[], uint32_t u32NRx);
static void SPIM_WriteStatusRegister(uint8_t dataBuf[], uint32_t u32NTx, uint32_t u32NBit);
static void SPIM_ReadStatusRegister(uint8_t dataBuf[], uint32_t u32NRx, uint32_t u32NBit);
static void SPIM_ReadStatusRegister2(uint8_t dataBuf[], uint32_t u32NRx, uint32_t u32NBit);
static void SPIM_WriteStatusRegister2(uint8_t dataBuf[], uint32_t u32NTx, uint32_t u32NBit);
static void SPIM_ReadStatusRegister3(uint8_t dataBuf[], uint32_t u32NRx, uint32_t u32NBit);
static void SPIM_ReadSecurityRegister(uint8_t dataBuf[], uint32_t u32NRx, uint32_t u32NBit);
static int spim_is_write_done(uint32_t u32NBit);
static int spim_wait_write_done(uint32_t u32NBit);
static void spim_set_write_enable(int isEn, uint32_t u32NBit);
static void spim_enable_spansion_quad_mode(int isEn);
static void spim_eon_set_qpi_mode(int isEn);
static void SPIM_SPANSION_4Bytes_Enable(int isEn, uint32_t u32NBit);
static void SPIM_WriteInPageDataByIo(uint32_t u32Addr, int is4ByteAddr, uint32_t u32NTx, uint8_t pu8TxBuf[], uint8_t wrCmd,
uint32_t u32NBitCmd, uint32_t u32NBitAddr, uint32_t u32NBitDat, int isSync);
static void SPIM_WriteInPageDataByPageWrite(uint32_t u32Addr, int is4ByteAddr, uint32_t u32NTx,
uint8_t pu8TxBuf[], uint32_t wrCmd, int isSync);
static void N_delay(int n)
{
while (n-- > 0)
{
__NOP();
}
}
static void SwitchNBitOutput(uint32_t u32NBit)
{
switch (u32NBit)
{
case 1UL:
SPIM_ENABLE_SING_OUTPUT_MODE(); /* 1-bit, Output. */
break;
case 2UL:
SPIM_ENABLE_DUAL_OUTPUT_MODE(); /* 2-bit, Output. */
break;
case 4UL:
SPIM_ENABLE_QUAD_OUTPUT_MODE(); /* 4-bit, Output. */
break;
default:
break;
}
}
static void SwitchNBitInput(uint32_t u32NBit)
{
switch (u32NBit)
{
case 1UL:
SPIM_ENABLE_SING_INPUT_MODE(); /* 1-bit, Input. */
break;
case 2UL:
SPIM_ENABLE_DUAL_INPUT_MODE(); /* 2-bit, Input. */
break;
case 4UL:
SPIM_ENABLE_QUAD_INPUT_MODE(); /* 4-bit, Input. */
break;
default:
break;
}
}
/**
* @brief Write data to SPI slave.
* @param pu8TxBuf Transmit buffer.
* @param u32NTx Number of bytes to transmit.
* @return None.
* @note This function sets g_SPIM_i32ErrCode to SPIM_TIMEOUT_ERR if waiting SPIM time-out.
*/
static void spim_write(uint8_t pu8TxBuf[], uint32_t u32NTx)
{
uint32_t buf_idx = 0UL;
uint32_t u32TimeOutCount = 0UL;
g_SPIM_i32ErrCode = 0;
while (u32NTx)
{
uint32_t dataNum = 0UL, dataNum2;
if (u32NTx >= 16UL)
{
dataNum = 4UL;
}
else if (u32NTx >= 12UL)
{
dataNum = 3UL;
}
else if (u32NTx >= 8UL)
{
dataNum = 2UL;
}
else if (u32NTx >= 4UL)
{
dataNum = 1UL;
}
dataNum2 = dataNum;
while (dataNum2)
{
uint32_t tmp;
memcpy(&tmp, &pu8TxBuf[buf_idx], 4U);
buf_idx += 4UL;
u32NTx -= 4UL;
dataNum2 --;
/* *((__O uint32_t *) &SPIM->TX0 + dataNum2) = tmp; */
SPIM->TX[dataNum2] = tmp;
}
if (dataNum)
{
SPIM_SET_OPMODE(SPIM_CTL0_OPMODE_IO); /* Switch to Normal mode. */
SPIM_SET_DATA_WIDTH(32UL);
SPIM_SET_DATA_NUM(dataNum);
SPIM_SET_GO();
u32TimeOutCount = SystemCoreClock; /* 1 second time-out */
SPIM_WAIT_FREE()
{
if (--u32TimeOutCount == 0)
{
g_SPIM_i32ErrCode = SPIM_TIMEOUT_ERR;
break;
}
}
}
if (u32NTx && (u32NTx < 4UL))
{
uint32_t rnm, tmp;
rnm = u32NTx;
memcpy(&tmp, &pu8TxBuf[buf_idx], u32NTx);
buf_idx += u32NTx;
u32NTx = 0UL;
SPIM->TX[0] = tmp;
SPIM_SET_OPMODE(SPIM_CTL0_OPMODE_IO); /* Switch to Normal mode. */
SPIM_SET_DATA_WIDTH(rnm * 8UL);
SPIM_SET_DATA_NUM(1UL);
SPIM_SET_GO();
u32TimeOutCount = SystemCoreClock; /* 1 second time-out */
SPIM_WAIT_FREE()
{
if (--u32TimeOutCount == 0)
{
g_SPIM_i32ErrCode = SPIM_TIMEOUT_ERR;
break;
}
}
}
}
}
/**
* @brief Read data from SPI slave.
* @param pu8TxBuf Receive buffer.
* @param u32NRx Size of receive buffer in bytes.
* @return None.
* @note This function sets g_SPIM_i32ErrCode to SPIM_TIMEOUT_ERR if waiting SPIM time-out.
*/
static void spim_read(uint8_t pu8RxBuf[], uint32_t u32NRx)
{
uint32_t buf_idx = 0UL;
uint32_t u32TimeOutCount = 0UL;
g_SPIM_i32ErrCode = 0;
while (u32NRx)
{
uint32_t dataNum = 0UL; /* number of words */
if (u32NRx >= 16UL)
{
dataNum = 4UL;
}
else if (u32NRx >= 12UL)
{
dataNum = 3UL;
}
else if (u32NRx >= 8UL)
{
dataNum = 2UL;
}
else if (u32NRx >= 4UL)
{
dataNum = 1UL;
}
if (dataNum)
{
SPIM_SET_OPMODE(SPIM_CTL0_OPMODE_IO); /* Switch to Normal mode. */
SPIM_SET_DATA_WIDTH(32UL);
SPIM_SET_DATA_NUM(dataNum);
SPIM_SET_GO();
u32TimeOutCount = SystemCoreClock; /* 1 second time-out */
SPIM_WAIT_FREE()
{
if (--u32TimeOutCount == 0)
{
g_SPIM_i32ErrCode = SPIM_TIMEOUT_ERR;
break;
}
}
}
while (dataNum)
{
uint32_t tmp;
tmp = SPIM->RX[dataNum - 1UL];
memcpy(&pu8RxBuf[buf_idx], &tmp, 4U);
buf_idx += 4UL;
dataNum --;
u32NRx -= 4UL;
}
if (u32NRx && (u32NRx < 4UL))
{
uint32_t tmp;
SPIM_SET_OPMODE(SPIM_CTL0_OPMODE_IO); /* Switch to Normal mode. */
SPIM_SET_DATA_WIDTH(u32NRx * 8UL);
SPIM_SET_DATA_NUM(1UL);
SPIM_SET_GO();
u32TimeOutCount = SystemCoreClock; /* 1 second time-out */
SPIM_WAIT_FREE()
{
if (--u32TimeOutCount == 0)
{
g_SPIM_i32ErrCode = SPIM_TIMEOUT_ERR;
break;
}
}
tmp = SPIM->RX[0];
memcpy(&pu8RxBuf[buf_idx], &tmp, u32NRx);
buf_idx += u32NRx;
u32NRx = 0UL;
}
}
}
/**
* @brief Issue Read Status Register #1 command.
* @param dataBuf Receive buffer.
* @param u32NRx Size of receive buffer.
* @param u32NBit N-bit transmit/receive.
* @return None.
*/
static void SPIM_ReadStatusRegister(uint8_t dataBuf[], uint32_t u32NRx, uint32_t u32NBit)
{
uint8_t cmdBuf[] = {OPCODE_RDSR}; /* 1-byte Read Status Register #1 command. */
SPIM_SET_SS_EN(1); /* CS activated. */
SwitchNBitOutput(u32NBit);
spim_write(cmdBuf, sizeof(cmdBuf));
SwitchNBitInput(u32NBit);
spim_read(dataBuf, u32NRx);
SPIM_SET_SS_EN(0); /* CS deactivated. */
}
/**
* @brief Issue Write Status Register #1 command.
* @param dataBuf Transmit buffer.
* @param u32NTx Size of transmit buffer.
* @param u32NBit N-bit transmit/receive.
* @return None.
*/
static void SPIM_WriteStatusRegister(uint8_t dataBuf[], uint32_t u32NTx, uint32_t u32NBit)
{
uint8_t cmdBuf[] = {OPCODE_WRSR, 0x00U}; /* 1-byte Write Status Register #1 command + 1-byte data. */
cmdBuf[1] = dataBuf[0];
SPIM_SET_SS_EN(1); /* CS activated. */
SwitchNBitOutput(u32NBit);
spim_write(cmdBuf, sizeof(cmdBuf));
SPIM_SET_SS_EN(0); /* CS deactivated. */
}
/**
* @brief Issue Read Status Register #2 command.
* @param dataBuf Receive buffer.
* @param u32NRx Size of receive buffer.
* @param u32NBit N-bit transmit/receive.
* @return None.
*/
static void SPIM_ReadStatusRegister2(uint8_t dataBuf[], uint32_t u32NRx, uint32_t u32NBit)
{
uint8_t cmdBuf[] = {OPCODE_RDSR2}; /* 1-byte Read Status Register #1 command. */
SPIM_SET_SS_EN(1); /* CS activated. */
SwitchNBitOutput(u32NBit);
spim_write(cmdBuf, sizeof(cmdBuf));
SwitchNBitInput(u32NBit);
spim_read(dataBuf, u32NRx);
SPIM_SET_SS_EN(0); /* CS deactivated. */
}
/**
* @brief Issue Winbond Write Status Register command. This command write both Status Register-1
* and Status Register-2.
* @param dataBuf Transmit buffer.
* @param u32NTx Size of transmit buffer.
* @param u32NBit N-bit transmit/receive.
* @return None.
*/
static void SPIM_WriteStatusRegister2(uint8_t dataBuf[], uint32_t u32NTx, uint32_t u32NBit)
{
uint8_t cmdBuf[3] = {OPCODE_WRSR, 0U, 0U};
cmdBuf[1] = dataBuf[0];
cmdBuf[2] = dataBuf[1];
SPIM_SET_SS_EN(1); /* CS activated. */
SwitchNBitOutput(u32NBit);
spim_write(cmdBuf, sizeof(cmdBuf));
SPIM_SET_SS_EN(0); /* CS deactivated. */
}
#if 0 /* not used */
/**
* @brief Issue Write Status Register #3 command.
* @param dataBuf Transmit buffer.
* @param u32NTx Size of transmit buffer.
* @param u32NBit N-bit transmit/receive.
* @return None.
*/
static void SPIM_WriteStatusRegister3(uint8_t dataBuf[], uint32_t u32NTx, uint32_t u32NBit)
{
uint8_t cmdBuf[] = {OPCODE_WRSR3, 0x00U}; /* 1-byte Write Status Register #2 command + 1-byte data. */
cmdBuf[1] = dataBuf[0];
SPIM_SET_SS_EN(1); /* CS activated. */
SwitchNBitOutput(u32NBit);
spim_write(cmdBuf, sizeof(cmdBuf));
SPIM_SET_SS_EN(0); /* CS deactivated. */
}
#endif
/**
* @brief Issue Read Status Register #3 command.
* @param dataBuf Receive buffer.
* @param u32NRx Size of receive buffer.
* @param u32NBit N-bit transmit/receive.
* @return None.
*/
static void SPIM_ReadStatusRegister3(uint8_t dataBuf[], uint32_t u32NRx, uint32_t u32NBit)
{
uint8_t cmdBuf[] = {OPCODE_RDSR3}; /* 1-byte Read Status Register #1 command. */
SPIM_SET_SS_EN(1); /* CS activated. */
SwitchNBitOutput(u32NBit);
spim_write(cmdBuf, sizeof(cmdBuf));
SwitchNBitInput(u32NBit);
spim_read(dataBuf, u32NRx);
SPIM_SET_SS_EN(0); /* CS deactivated. */
}
#if 0 /* not used */
/**
* @brief Issue Write Security Register command.
* @param dataBuf Transmit buffer.
* @param u32NTx Size of transmit buffer.
* @param u32NBit N-bit transmit/receive.
* @return None.
*/
static void SPIM_WriteSecurityRegister(uint8_t dataBuf[], uint32_t u32NTx, uint32_t u32NBit)
{
uint8_t cmdBuf[] = {OPCODE_WRSCUR, 0x00U}; /* 1-byte Write Status Register #2 command + 1-byte data. */
cmdBuf[1] = dataBuf[0];
SPIM_SET_SS_EN(1); /* CS activated. */
SwitchNBitOutput(u32NBit);
spim_write(cmdBuf, sizeof(cmdBuf));
SPIM_SET_SS_EN(0); /* CS deactivated. */
}
#endif
/**
* @brief Issue Read Security Register command.
* @param dataBuf Receive buffer.
* @param u32NRx Size of receive buffer.
* @param u32NBit N-bit transmit/receive.
* @return None.
*/
static void SPIM_ReadSecurityRegister(uint8_t dataBuf[], uint32_t u32NRx, uint32_t u32NBit)
{
uint8_t cmdBuf[] = {OPCODE_RDSCUR}; /* 1-byte Read Status Register #1 command. */
SPIM_SET_SS_EN(1); /* CS activated. */
SwitchNBitOutput(u32NBit);
spim_write(cmdBuf, sizeof(cmdBuf));
SwitchNBitInput(u32NBit);
spim_read(dataBuf, u32NRx);
SPIM_SET_SS_EN(0); /* CS deactivated. */
}
/**
* @brief Check if Erase/Write is done.
* @return 0: Not done. 1: Done.
*/
static int spim_is_write_done(uint32_t u32NBit)
{
uint8_t status[1];
SPIM_ReadStatusRegister(status, sizeof(status), u32NBit);
return !(status[0] & SR_WIP);
}
/**
* @brief Wait until Erase/Write done.
* @param u32NBit N-bit transmit/receive.
* @return 0 SPIM write done.
*/
static int spim_wait_write_done(uint32_t u32NBit)
{
uint32_t count;
int ret = -1;
for (count = 0UL; count < SystemCoreClock / 1000UL; count++)
{
if (spim_is_write_done(u32NBit))
{
ret = 0;
break;
}
}
if (ret != 0)
{
SPIM_DBGMSG("spim_wait_write_done time-out!!\n");
}
return ret;
}
/**
* @brief Issue Write Enable/disable command.
* @param isEn Enable/disable.
* @param u32NBit N-bit transmit/receive.
* @return None.
*/
static void spim_set_write_enable(int isEn, uint32_t u32NBit)
{
uint8_t cmdBuf[] = {0U}; /* 1-byte Write Enable command. */
cmdBuf[0] = isEn ? OPCODE_WREN : OPCODE_WRDI;
SPIM_SET_SS_EN(1); /* CS activated. */
SwitchNBitOutput(u32NBit);
spim_write(cmdBuf, sizeof(cmdBuf));
SPIM_SET_SS_EN(0); /* CS deactivated. */
}
/** @endcond HIDDEN_SYMBOLS */
/**
* @brief Get SPIM serial clock.
* @return SPI serial clock.
* @details This function calculates the serial clock of SPI in Hz.
*/
uint32_t SPIM_GetSClkFreq(void)
{
uint32_t clkDiv = SPIM_GET_CLOCK_DIVIDER();
return clkDiv ? SystemCoreClock / (clkDiv * 2U) : SystemCoreClock;
}
/**
* @brief Initialize SPIM flash.
* @param clrWP Clear Write Protect or not.
* @return 0 Success.
* @return -1 Unrecognized manufacture ID or failed on reading manufacture ID.
*/
int SPIM_InitFlash(int clrWP)
{
uint8_t idBuf[3];
uint8_t cmdBuf[1];
uint32_t i;
int32_t ret = -1;
SPIM_SET_SS_ACTLVL(0);
/*
* Because not sure in SPI or QPI mode, do QPI reset and then SPI reset.
*/
/* QPI Reset Enable */
cmdBuf[0] = OPCODE_RSTEN;
SPIM_SET_SS_EN(1); /* CS activated. */
SPIM_ENABLE_QUAD_OUTPUT_MODE(); /* 1-bit, Output. */
spim_write(cmdBuf, sizeof(cmdBuf));
SPIM_SET_SS_EN(0); /* CS deactivated. */
/* QPI Reset */
cmdBuf[0] = OPCODE_RST;
SPIM_SET_SS_EN(1); /* CS activated. */
SPIM_ENABLE_QUAD_OUTPUT_MODE(); /* 1-bit, Output. */
spim_write(cmdBuf, sizeof(cmdBuf));
SPIM_SET_SS_EN(0); /* CS deactivated. */
/* SPI ResetEnable */
cmdBuf[0] = OPCODE_RSTEN;
SPIM_SET_SS_EN(1); /* CS activated. */
SPIM_ENABLE_SING_OUTPUT_MODE(); /* 1-bit, Output. */
spim_write(cmdBuf, sizeof(cmdBuf));
SPIM_SET_SS_EN(0); /* CS deactivated. */
/* SPI Reset */
cmdBuf[0] = OPCODE_RST;
SPIM_SET_SS_EN(1); /* CS activated. */
SPIM_ENABLE_SING_OUTPUT_MODE(); /* 1-bit, Output. */
spim_write(cmdBuf, sizeof(cmdBuf));
SPIM_SET_SS_EN(0); /* CS deactivated. */
if (clrWP)
{
uint8_t dataBuf[] = {0x00U};
spim_set_write_enable(1, 1UL); /* Clear Block Protect. */
SPIM_WriteStatusRegister(dataBuf, sizeof(dataBuf), 1U);
spim_wait_write_done(1UL);
}
SPIM_ReadJedecId(idBuf, sizeof(idBuf), 1UL);
/* printf("ID: 0x%x, 0x%x, px%x\n", idBuf[0], idBuf[1], idBuf[2]); */
for (i = 0UL; i < sizeof(g_Supported_List) / sizeof(g_Supported_List[0]); i++)
{
if (idBuf[0] == g_Supported_List[i])
{
ret = 0;
}
}
if (ret != 0)
{
SPIM_DBGMSG("Flash initialize failed!! 0x%x\n", idBuf[0]);
}
return ret;
}
/**
* @brief Issue JEDEC ID command.
* @param idBuf ID buffer.
* @param u32NRx Size of ID buffer.
* @param u32NBit N-bit transmit/receive.
* @return None.
*/
void SPIM_ReadJedecId(uint8_t idBuf[], uint32_t u32NRx, uint32_t u32NBit)
{
uint8_t cmdBuf[] = { OPCODE_RDID }; /* 1-byte JEDEC ID command. */
SPIM_SET_SS_EN(1); /* CS activated. */
SwitchNBitOutput(u32NBit);
spim_write(cmdBuf, sizeof(cmdBuf));
SwitchNBitInput(u32NBit);
spim_read(idBuf, u32NRx);
SPIM_SET_SS_EN(0); /* CS deactivated. */
}
/** @cond HIDDEN_SYMBOLS */
static void spim_enable_spansion_quad_mode(int isEn)
{
uint8_t cmdBuf[3];
uint8_t dataBuf[1], status1;
cmdBuf[0] = 0x5U; /* Read Status Register-1 */
SPIM_SET_SS_EN(1);
SwitchNBitOutput(1UL);
spim_write(cmdBuf, sizeof(cmdBuf));
SwitchNBitInput(1UL);
spim_read(dataBuf, sizeof(dataBuf));
SPIM_SET_SS_EN(0);
/* SPIM_DBGMSG("SR1 = 0x%x\n", dataBuf[0]); */
status1 = dataBuf[0];
cmdBuf[0] = 0x35U; /* Read Configuration Register-1 */
SPIM_SET_SS_EN(1);
SwitchNBitOutput(1UL);
spim_write(cmdBuf, sizeof(cmdBuf));
SwitchNBitInput(1UL);
spim_read(dataBuf, sizeof(dataBuf));
SPIM_SET_SS_EN(0);
/* SPIM_DBGMSG("CR1 = 0x%x\n", dataBuf[0]); */
spim_set_write_enable(1, 1UL);
cmdBuf[0] = 0x1U; /* Write register */
cmdBuf[1] = status1;
if (isEn)
{
cmdBuf[2] = dataBuf[0] | 0x2U; /* set QUAD */
}
else
{
cmdBuf[2] = dataBuf[0] & ~0x2U; /* clear QUAD */
}
SPIM_SET_SS_EN(1);
SwitchNBitOutput(1UL);
spim_write(cmdBuf, 3UL);
SPIM_SET_SS_EN(0);
spim_set_write_enable(0, 1UL);
cmdBuf[0] = 0x35U; /* Read Configuration Register-1 */
SPIM_SET_SS_EN(1);
SwitchNBitOutput(1UL);
spim_write(cmdBuf, sizeof(cmdBuf));
SwitchNBitInput(1UL);
spim_read(dataBuf, sizeof(dataBuf));
SPIM_SET_SS_EN(0);
/* SPIM_DBGMSG("CR1 = 0x%x\n", dataBuf[0]); */
N_delay(10000);
}
/** @endcond HIDDEN_SYMBOLS */
/**
* @brief Set Quad Enable/disable.
* @param isEn Enable/disable.
* @param u32NBit N-bit transmit/receive.
* @return None.
*/
void SPIM_SetQuadEnable(int isEn, uint32_t u32NBit)
{
uint8_t idBuf[3];
uint8_t dataBuf[2];
SPIM_ReadJedecId(idBuf, sizeof(idBuf), u32NBit);
SPIM_DBGMSG("SPIM_SetQuadEnable - Flash ID is 0x%x\n", idBuf[0]);
switch (idBuf[0])
{
case MFGID_WINBOND: /* Winbond SPI flash */
SPIM_ReadStatusRegister(&dataBuf[0], 1UL, u32NBit);
SPIM_ReadStatusRegister2(&dataBuf[1], 1UL, u32NBit);
SPIM_DBGMSG("Status Register: 0x%x - 0x%x\n", dataBuf[0], dataBuf[1]);
if (isEn)
{
dataBuf[1] |= SR2_QE;
}
else
{
dataBuf[1] &= ~SR2_QE;
}
spim_set_write_enable(1, u32NBit); /* Write Enable. */
SPIM_WriteStatusRegister2(dataBuf, sizeof(dataBuf), u32NBit);
spim_wait_write_done(u32NBit);
SPIM_ReadStatusRegister(&dataBuf[0], 1UL, u32NBit);
SPIM_ReadStatusRegister2(&dataBuf[1], 1UL, u32NBit);
SPIM_DBGMSG("Status Register: 0x%x - 0x%x\n", dataBuf[0], dataBuf[1]);
break;
case MFGID_MXIC: /* MXIC SPI flash. */
case MFGID_EON:
case MFGID_ISSI: /* ISSI SPI flash. */
spim_set_write_enable(1, u32NBit); /* Write Enable. */
dataBuf[0] = isEn ? SR_QE : 0U;
SPIM_WriteStatusRegister(dataBuf, sizeof(dataBuf), u32NBit);
spim_wait_write_done(u32NBit);
break;
case MFGID_SPANSION:
spim_enable_spansion_quad_mode(isEn);
break;
default:
break;
}
}
/**
* @brief Enter/exit QPI mode.
* @param isEn Enable/disable.
* @return None.
*/
static void spim_eon_set_qpi_mode(int isEn)
{
uint8_t cmdBuf[1]; /* 1-byte command. */
uint8_t status[1];
SPIM_ReadStatusRegister(status, sizeof(status), 1UL);
SPIM_DBGMSG("Status: 0x%x\n", status[0]);
if (isEn) /* Assume in SPI mode. */
{
cmdBuf[0] = OPCODE_ENQPI;
SPIM_SET_SS_EN(1); /* CS activated. */
SwitchNBitOutput(1UL);
spim_write(cmdBuf, sizeof(cmdBuf));
SPIM_SET_SS_EN(0); /* CS deactivated. */
}
else /* Assume in QPI mode. */
{
cmdBuf[0] = OPCODE_EXQPI;
SPIM_SET_SS_EN(1); /* CS activated. */
SwitchNBitOutput(4UL);
spim_write(cmdBuf, sizeof(cmdBuf));
SPIM_SET_SS_EN(0); /* CS deactivated. */
}
SPIM_ReadStatusRegister(status, sizeof(status), 1UL);
SPIM_DBGMSG("Status: 0x%x\n", status[0]);
}
static void SPIM_SPANSION_4Bytes_Enable(int isEn, uint32_t u32NBit)
{
uint8_t cmdBuf[2];
uint8_t dataBuf[1];
cmdBuf[0] = OPCODE_BRRD;
SPIM_SET_SS_EN(1); /* CS activated. */
SwitchNBitOutput(u32NBit);
spim_write(cmdBuf, 1UL);
SwitchNBitInput(1UL);
spim_read(dataBuf, 1UL);
SPIM_SET_SS_EN(0); /* CS deactivated. */
SPIM_DBGMSG("Bank Address register= 0x%x\n", dataBuf[0]);
cmdBuf[0] = OPCODE_BRWR;
if (isEn)
{
cmdBuf[1] = dataBuf[0] | 0x80U; /* set EXTADD */
}
else
{
cmdBuf[1] = dataBuf[0] & ~0x80U; /* clear EXTADD */
}
SPIM_SET_SS_EN(1); /* CS activated. */
SwitchNBitOutput(1UL);
spim_write(cmdBuf, 2UL);
SPIM_SET_SS_EN(0); /* CS deactivated. */
}
/** @cond HIDDEN_SYMBOLS */
/**
* @brief Query 4-byte address mode enabled or not.
* @param u32NBit N-bit transmit/receive.
* @return 0: 4-byte address mode disabled. 1: 4-byte address mode enabled.
*/
int SPIM_Is4ByteModeEnable(uint32_t u32NBit)
{
int isEn = 0;
int isSupt = 0;
uint8_t idBuf[3];
uint8_t dataBuf[1];
SPIM_ReadJedecId(idBuf, sizeof(idBuf), u32NBit);
/* Based on Flash size, check if 4-byte address mode is supported. */
switch (idBuf[0])
{
case MFGID_WINBOND:
case MFGID_MXIC:
case MFGID_EON:
isSupt = (idBuf[2] < 0x19U) ? 0L : 1L;
break;
case MFGID_ISSI:
isSupt = (idBuf[2] < 0x49U) ? 0L : 1L;
break;
default:
break;
}
if (isSupt != 0)
{
if (idBuf[0] == MFGID_WINBOND)
{
/* Winbond SPI flash. */
SPIM_ReadStatusRegister3(dataBuf, sizeof(dataBuf), u32NBit);
isEn = !!(dataBuf[0] & SR3_ADR);
}
else if ((idBuf[0] == MFGID_MXIC) || (idBuf[0] == MFGID_EON))
{
/* MXIC/EON SPI flash. */
SPIM_ReadSecurityRegister(dataBuf, sizeof(dataBuf), u32NBit);
isEn = !!(dataBuf[0] & SCUR_4BYTE);
}
}
return isEn;
}
/** @endcond HIDDEN_SYMBOLS */
/**
* @brief Enter/Exit 4-byte address mode.
* @param isEn Enable/disable.
* @param u32NBit N-bit transmit/receive.
* @return 0 success
* -1 failed
*/
int SPIM_Enable_4Bytes_Mode(int isEn, uint32_t u32NBit)
{
int isSupt = 0L, ret = -1;
uint8_t idBuf[3];
uint8_t cmdBuf[1]; /* 1-byte Enter/Exit 4-Byte Mode command. */
int32_t i32TimeOutCount = 0;
SPIM_ReadJedecId(idBuf, sizeof(idBuf), u32NBit);
/* Based on Flash size, check if 4-byte address mode is supported. */
switch (idBuf[0])
{
case MFGID_WINBOND:
isSupt = (idBuf[2] < 0x16U) ? 0L : 1L;
break;
case MFGID_MXIC:
case MFGID_EON:
isSupt = (idBuf[2] < 0x19U) ? 0L : 1L;
break;
case MFGID_ISSI:
isSupt = (idBuf[2] < 0x49U) ? 0L : 1L;
break;
case MFGID_SPANSION:
SPIM_SPANSION_4Bytes_Enable(isEn, u32NBit);
isSupt = 1L;
ret = 0L;
break;
default:
break;
}
if ((isSupt) && (idBuf[0] != MFGID_SPANSION))
{
cmdBuf[0] = isEn ? OPCODE_EN4B : OPCODE_EX4B;
SPIM_SET_SS_EN(1); /* CS activated. */
SwitchNBitOutput(u32NBit);
spim_write(cmdBuf, sizeof(cmdBuf));
SPIM_SET_SS_EN(0); /* CS deactivated. */
/*
* FIXME: Per test, 4BYTE Indicator bit doesn't set after EN4B, which
* doesn't match spec(MX25L25635E), so skip the check below.
*/
ret = 0;
if (idBuf[0] != MFGID_MXIC)
{
/*
* About over 100 instrucsions executed, just want to give
* a time-out about 1 seconds to avoid infinite loop
*/
i32TimeOutCount = (SystemCoreClock) / 100;
if (isEn)
{
while ((i32TimeOutCount-- > 0) && !SPIM_Is4ByteModeEnable(u32NBit)) { }
}
else
{
while ((i32TimeOutCount-- > 0) && SPIM_Is4ByteModeEnable(u32NBit)) { }
}
if (i32TimeOutCount <= 0)
ret = -1;
}
}
return ret;
}
void SPIM_WinbondUnlock(uint32_t u32NBit)
{
uint8_t idBuf[3];
uint8_t dataBuf[4];
SPIM_ReadJedecId(idBuf, sizeof(idBuf), u32NBit);
if ((idBuf[0] != MFGID_WINBOND) || (idBuf[1] != 0x40) || (idBuf[2] != 0x16))
{
SPIM_DBGMSG("SPIM_WinbondUnlock - Not W25Q32, do nothing.\n");
return;
}
SPIM_ReadStatusRegister(&dataBuf[0], 1UL, u32NBit);
SPIM_ReadStatusRegister2(&dataBuf[1], 1UL, u32NBit);
SPIM_DBGMSG("Status Register: 0x%x - 0x%x\n", dataBuf[0], dataBuf[1]);
dataBuf[1] &= ~0x40; /* clear Status Register-1 SEC bit */
spim_set_write_enable(1, u32NBit); /* Write Enable. */
SPIM_WriteStatusRegister2(dataBuf, sizeof(dataBuf), u32NBit);
spim_wait_write_done(u32NBit);
SPIM_ReadStatusRegister(&dataBuf[0], 1UL, u32NBit);
SPIM_ReadStatusRegister2(&dataBuf[1], 1UL, u32NBit);
SPIM_DBGMSG("Status Register (after unlock): 0x%x - 0x%x\n", dataBuf[0], dataBuf[1]);
}
/**
* @brief Erase whole chip.
* @param u32NBit N-bit transmit/receive.
* @param isSync Block or not.
* @return None.
*/
void SPIM_ChipErase(uint32_t u32NBit, int isSync)
{
uint8_t cmdBuf[] = { OPCODE_CHIP_ERASE }; /* 1-byte Chip Erase command. */
spim_set_write_enable(1, u32NBit); /* Write Enable. */
SPIM_SET_SS_EN(1); /* CS activated. */
SwitchNBitOutput(u32NBit);
spim_write(cmdBuf, sizeof(cmdBuf));
SPIM_SET_SS_EN(0); /* CS deactivated. */
if (isSync)
{
spim_wait_write_done(u32NBit);
}
}
/**
* @brief Erase one block.
* @param u32Addr Block to erase which contains the u32Addr.
* @param is4ByteAddr 4-byte u32Address or not.
* @param u8ErsCmd Erase command.
* @param u32NBit N-bit transmit/receive.
* @param isSync Block or not.
* @return None.
*/
void SPIM_EraseBlock(uint32_t u32Addr, int is4ByteAddr, uint8_t u8ErsCmd, uint32_t u32NBit, int isSync)
{
uint8_t cmdBuf[16];
uint32_t buf_idx = 0UL;
spim_set_write_enable(1, u32NBit); /* Write Enable. */
cmdBuf[buf_idx++] = u8ErsCmd;
if (is4ByteAddr)
{
cmdBuf[buf_idx++] = (uint8_t)(u32Addr >> 24);
cmdBuf[buf_idx++] = (uint8_t)(u32Addr >> 16);
cmdBuf[buf_idx++] = (uint8_t)(u32Addr >> 8);
cmdBuf[buf_idx++] = (uint8_t)(u32Addr & 0xFFUL);
}
else
{
cmdBuf[buf_idx++] = (uint8_t)(u32Addr >> 16);
cmdBuf[buf_idx++] = (uint8_t)(u32Addr >> 8);
cmdBuf[buf_idx++] = (uint8_t)(u32Addr & 0xFFUL);
}
SPIM_SET_SS_EN(1); /* CS activated. */
SwitchNBitOutput(u32NBit);
spim_write(cmdBuf, buf_idx);
SPIM_SET_SS_EN(0); /* CS deactivated. */
if (isSync)
{
spim_wait_write_done(u32NBit);
}
}
/** @cond HIDDEN_SYMBOLS */
/**
* @brief Write data in the same page by I/O mode.
* @param u32Addr Start u32Address to write.
* @param is4ByteAddr 4-byte u32Address or not.
* @param u32NTx Number of bytes to write.
* @param pu8TxBuf Transmit buffer.
* @param wrCmd Write command.
* @param u32NBitCmd N-bit transmit command.
* @param u32NBitAddr N-bit transmit u32Address.
* @param u32NBitDat N-bit transmit/receive data.
* @param isSync Block or not.
* @return None.
*/
static void SPIM_WriteInPageDataByIo(uint32_t u32Addr, int is4ByteAddr, uint32_t u32NTx, uint8_t pu8TxBuf[], uint8_t wrCmd,
uint32_t u32NBitCmd, uint32_t u32NBitAddr, uint32_t u32NBitDat, int isSync)
{
uint8_t cmdBuf[16];
uint32_t buf_idx;
spim_set_write_enable(1, u32NBitCmd); /* Write Enable. */
SPIM_SET_SS_EN(1); /* CS activated. */
SwitchNBitOutput(u32NBitCmd);
cmdBuf[0] = wrCmd;
spim_write(cmdBuf, 1UL); /* Write out command. */
buf_idx = 0UL;
if (is4ByteAddr)
{
cmdBuf[buf_idx++] = (uint8_t)(u32Addr >> 24);
cmdBuf[buf_idx++] = (uint8_t)(u32Addr >> 16);
cmdBuf[buf_idx++] = (uint8_t)(u32Addr >> 8);
cmdBuf[buf_idx++] = (uint8_t) u32Addr;
}
else
{
cmdBuf[buf_idx++] = (uint8_t)(u32Addr >> 16);
cmdBuf[buf_idx++] = (uint8_t)(u32Addr >> 8);
cmdBuf[buf_idx++] = (uint8_t) u32Addr;
}
SwitchNBitOutput(u32NBitAddr);
spim_write(cmdBuf, buf_idx); /* Write out u32Address. */
SwitchNBitOutput(u32NBitDat);
spim_write(pu8TxBuf, u32NTx); /* Write out data. */
SPIM_SET_SS_EN(0); /* CS deactivated. */
if (isSync)
{
spim_wait_write_done(u32NBitCmd);
}
}
/**
* @brief Write data in the same page by Page Write mode.
* @param u32Addr Start u32Address to write.
* @param is4ByteAddr 4-byte u32Address or not.
* @param u32NTx Number of bytes to write.
* @param pu8TxBuf Transmit buffer.
* @param wrCmd Write command.
* @param isSync Block or not.
* @return None.
* @note This function sets g_SPIM_i32ErrCode to SPIM_TIMEOUT_ERR if waiting SPIM time-out.
*/
static void SPIM_WriteInPageDataByPageWrite(uint32_t u32Addr, int is4ByteAddr, uint32_t u32NTx,
uint8_t pu8TxBuf[], uint32_t wrCmd, int isSync)
{
uint32_t u32TimeOutCount = SystemCoreClock; /* 1 second time-out */
g_SPIM_i32ErrCode = 0;
if ((wrCmd == CMD_QUAD_PAGE_PROGRAM_WINBOND) ||
(wrCmd == CMD_QUAD_PAGE_PROGRAM_MXIC))
{
SPIM_SetQuadEnable(1, 1UL); /* Set Quad Enable. */
}
else if (wrCmd == CMD_QUAD_PAGE_PROGRAM_EON)
{
SPIM_SetQuadEnable(1, 1UL); /* Set Quad Enable. */
spim_eon_set_qpi_mode(1); /* Enter QPI mode. */
}
SPIM_SET_OPMODE(SPIM_CTL0_OPMODE_PAGEWRITE);/* Switch to Page Write mode. */
SPIM_SET_SPIM_MODE(wrCmd); /* SPIM mode. */
SPIM_SET_4BYTE_ADDR_EN(is4ByteAddr); /* Enable/disable 4-Byte Address. */
SPIM->SRAMADDR = (uint32_t) pu8TxBuf; /* SRAM u32Address. */
SPIM->DMACNT = u32NTx; /* Transfer length. */
SPIM->FADDR = u32Addr; /* Flash u32Address.*/
SPIM_SET_GO(); /* Go. */
if (isSync)
{
SPIM_WAIT_FREE()
{
if (--u32TimeOutCount == 0)
{
g_SPIM_i32ErrCode = SPIM_TIMEOUT_ERR;
break;
}
}
}
if (wrCmd == CMD_QUAD_PAGE_PROGRAM_EON)
{
spim_eon_set_qpi_mode(0); /* Exit QPI mode. */
}
}
/** @endcond HIDDEN_SYMBOLS */
/**
* @brief Write data to SPI Flash by sending commands manually (I/O mode).
* @param u32Addr: Start u32Address to write.
* @param is4ByteAddr: 4-byte u32Address or not.
* @param u32NTx: Number of bytes to write.
* @param pu8TxBuf: Transmit buffer.
* @param wrCmd: Write command.
* @param u32NBitCmd: N-bit transmit command.
* @param u32NBitAddr: N-bit transmit u32Address.
* @param u32NBitDat: N-bit transmit/receive data.
* @return None.
*/
void SPIM_IO_Write(uint32_t u32Addr, int is4ByteAddr, uint32_t u32NTx, uint8_t pu8TxBuf[], uint8_t wrCmd,
uint32_t u32NBitCmd, uint32_t u32NBitAddr, uint32_t u32NBitDat)
{
uint32_t pageOffset, toWr;
uint32_t buf_idx = 0UL;
pageOffset = u32Addr % 256UL;
if ((pageOffset + u32NTx) <= 256UL) /* Do all the bytes fit onto one page ? */
{
SPIM_WriteInPageDataByIo(u32Addr, is4ByteAddr, u32NTx, &pu8TxBuf[buf_idx],
wrCmd, u32NBitCmd, u32NBitAddr, u32NBitDat, 1);
}
else
{
toWr = 256UL - pageOffset; /* Size of data remaining on the first page. */
SPIM_WriteInPageDataByIo(u32Addr, is4ByteAddr, toWr, &pu8TxBuf[buf_idx],
wrCmd, u32NBitCmd, u32NBitAddr, u32NBitDat, 1);
u32Addr += toWr; /* Advance indicator. */
u32NTx -= toWr;
buf_idx += toWr;
while (u32NTx)
{
toWr = 256UL;
if (toWr > u32NTx)
{
toWr = u32NTx;
}
SPIM_WriteInPageDataByIo(u32Addr, is4ByteAddr, toWr, &pu8TxBuf[buf_idx],
wrCmd, u32NBitCmd, u32NBitAddr, u32NBitDat, 1);
u32Addr += toWr; /* Advance indicator. */
u32NTx -= toWr;
buf_idx += toWr;
}
}
}
/**
* @brief Read data from SPI Flash by sending commands manually (I/O mode).
* @param u32Addr Start u32Address to read.
* @param is4ByteAddr 4-byte u32Address or not.
* @param u32NRx Number of bytes to read.
* @param pu8RxBuf Receive buffer.
* @param rdCmd Read command.
* @param u32NBitCmd N-bit transmit command.
* @param u32NBitAddr N-bit transmit u32Address.
* @param u32NBitDat N-bit transmit/receive data.
* @param u32NDummy Number of dummy bytes following address.
* @return None.
*/
void SPIM_IO_Read(uint32_t u32Addr, int is4ByteAddr, uint32_t u32NRx, uint8_t pu8RxBuf[], uint8_t rdCmd,
uint32_t u32NBitCmd, uint32_t u32NBitAddr, uint32_t u32NBitDat, int u32NDummy)
{
uint8_t cmdBuf[16];
uint32_t buf_idx;
SPIM_SET_SS_EN(1); /* CS activated. */
cmdBuf[0] = rdCmd;
SwitchNBitOutput(u32NBitCmd);
spim_write(cmdBuf, 1UL); /* Write out command. */
buf_idx = 0UL;
if (is4ByteAddr)
{
cmdBuf[buf_idx++] = (uint8_t)(u32Addr >> 24);
cmdBuf[buf_idx++] = (uint8_t)(u32Addr >> 16);
cmdBuf[buf_idx++] = (uint8_t)(u32Addr >> 8);
cmdBuf[buf_idx++] = (uint8_t) u32Addr;
}
else
{
cmdBuf[buf_idx++] = (uint8_t)(u32Addr >> 16);
cmdBuf[buf_idx++] = (uint8_t)(u32Addr >> 8);
cmdBuf[buf_idx++] = (uint8_t) u32Addr;
}
SwitchNBitOutput(u32NBitAddr);
spim_write(cmdBuf, buf_idx); /* Write out u32Address. */
buf_idx = 0UL;
while (u32NDummy --)
{
cmdBuf[buf_idx++] = 0x00U;
}
/* Same bit mode as above. */
spim_write(cmdBuf, buf_idx); /* Write out dummy bytes. */
SwitchNBitInput(u32NBitDat);
spim_read(pu8RxBuf, u32NRx); /* Read back data. */
SPIM_SET_SS_EN(0); /* CS deactivated. */
}
/**
* @brief Write data to SPI Flash by Page Write mode.
* @param u32Addr Start address to write.
* @param is4ByteAddr 4-byte address or not.
* @param u32NTx Number of bytes to write.
* @param pu8TxBuf Transmit buffer.
* @param wrCmd Write command.
* @return None.
*/
void SPIM_DMA_Write(uint32_t u32Addr, int is4ByteAddr, uint32_t u32NTx, uint8_t pu8TxBuf[], uint32_t wrCmd)
{
uint32_t pageOffset, toWr;
uint32_t buf_idx = 0UL;
pageOffset = u32Addr % 256UL;
if ((pageOffset + u32NTx) <= 256UL)
{
/* Do all the bytes fit onto one page ? */
SPIM_WriteInPageDataByPageWrite(u32Addr, is4ByteAddr, u32NTx, pu8TxBuf, wrCmd, 1);
}
else
{
toWr = 256UL - pageOffset; /* Size of data remaining on the first page. */
SPIM_WriteInPageDataByPageWrite(u32Addr, is4ByteAddr, toWr, &pu8TxBuf[buf_idx], wrCmd, 1);
u32Addr += toWr; /* Advance indicator. */
u32NTx -= toWr;
buf_idx += toWr;
while (u32NTx)
{
toWr = 256UL;
if (toWr > u32NTx)
{
toWr = u32NTx;
}
SPIM_WriteInPageDataByPageWrite(u32Addr, is4ByteAddr, toWr, &pu8TxBuf[buf_idx], wrCmd, 1);
u32Addr += toWr; /* Advance indicator. */
u32NTx -= toWr;
buf_idx += toWr;
}
}
}
/**
* @brief Read data from SPI Flash by Page Read mode.
* @param u32Addr Start address to read.
* @param is4ByteAddr 4-byte u32Address or not.
* @param u32NRx Number of bytes to read.
* @param pu8RxBuf Receive buffer.
* @param u32RdCmd Read command.
* @param isSync Block or not.
* @return None.
* @note This function sets g_SPIM_i32ErrCode to SPIM_TIMEOUT_ERR if waiting SPIM time-out.
*/
void SPIM_DMA_Read(uint32_t u32Addr, int is4ByteAddr, uint32_t u32NRx, uint8_t pu8RxBuf[],
uint32_t u32RdCmd, int isSync)
{
uint32_t u32TimeOutCount = SystemCoreClock; /* 1 second time-out */
g_SPIM_i32ErrCode = 0;
SPIM_SET_OPMODE(SPIM_CTL0_OPMODE_PAGEREAD); /* Switch to Page Read mode. */
SPIM_SET_SPIM_MODE(u32RdCmd); /* SPIM mode. */
SPIM_SET_4BYTE_ADDR_EN(is4ByteAddr); /* Enable/disable 4-Byte Address. */
SPIM->SRAMADDR = (uint32_t) pu8RxBuf; /* SRAM u32Address. */
SPIM->DMACNT = u32NRx; /* Transfer length. */
SPIM->FADDR = u32Addr; /* Flash u32Address.*/
SPIM_SET_GO(); /* Go. */
if (isSync)
{
SPIM_WAIT_FREE() /* Wait for DMA done. */
{
if (--u32TimeOutCount == 0)
{
g_SPIM_i32ErrCode = SPIM_TIMEOUT_ERR;
break;
}
}
}
}
/**
* @brief Enter Direct Map mode.
* @param is4ByteAddr 4-byte u32Address or not.
* @param u32RdCmd Read command.
* @param u32IdleIntvl Idle interval.
* @return None.
*/
void SPIM_EnterDirectMapMode(int is4ByteAddr, uint32_t u32RdCmd, uint32_t u32IdleIntvl)
{
SPIM_SET_4BYTE_ADDR_EN(is4ByteAddr); /* Enable/disable 4-byte u32Address. */
SPIM_SET_SPIM_MODE(u32RdCmd); /* SPIM mode. */
SPIM_SET_IDL_INTVL(u32IdleIntvl); /* Idle interval. */
SPIM_SET_OPMODE(SPIM_CTL0_OPMODE_DIRECTMAP); /* Switch to Direct Map mode. */
}
/**
* @brief Exit Direct Map mode.
* @return None.
*/
void SPIM_ExitDirectMapMode(void)
{
SPIM_SET_OPMODE(SPIM_CTL0_OPMODE_IO); /* Switch back to Normal mode. */
}
/*@}*/ /* end of group SPIM_EXPORTED_FUNCTIONS */
/*@}*/ /* end of group SPIM_Driver */
/*@}*/ /* end of group Standard_Driver */