Files
hpw421/component/xc6xx_drivers/Drivers/xc_driver/xc_drv_spi.c
T
2026-07-03 18:08:25 +08:00

674 lines
19 KiB
C

/*!
* \file xc6xxx_hal_spi.c
*
* \brief Target xc6xxx hal spi implementation
*
* \copyright Revised BSD License, see section \ref LICENSE.
*
* \code
*
* _ __ _ ________ _
* | |/ /(_)___ / ____/ /_ (_)___
* | // / __ \/ / / __ \/ / __ \
* / |/ / / / / /___/ / / / / /_/ /
* /_/|_/_/_/ /_/\____/_/ /_/_/ .___/
* /_/
* (C) 2022-2025 XinChip
*
* \endcode
*
* \author ( XinChip ) Alex-J
*
* \author ( XinChip )
*/
/*-----------------------------------------------------------------------------------
INCLUDE HEADE FILES
------------------------------------------------------------------------------------*/
#include "xc6xxx.h"
#if XC_CHECK(XC_SPI_ENABLED)
/*------------------------------------------------------------------------------------
Global Variables
-------------------------------------------------------------------------------------*/
typedef struct
{
bool SPI_DMA_IT_Enable_Flag[3];
uint8_t SPI_Mode[3];
} SPI_CtrlBlock_Typedef;
SPI_CtrlBlock_Typedef m_spi_cb;
/*------------------------------------------------------------------------------------
Func Prototype
-------------------------------------------------------------------------------------*/
eXC_RESULT xc_spi_write_bytes(uint8_t reg_idx, uint8_t *txdata, uint16_t len);
eXC_RESULT xc_spi_read_bytes(uint8_t reg_idx, uint8_t *rxdata, uint16_t len);
/*------------------------------------------------------------------------------------
Functions
-------------------------------------------------------------------------------------*/
void xc_spi_init(uint8_t reg_idx, SPI_InitCfg_t *init_cfg)
{
#if (USE_XIP == 0)
if (SPI0_IDX == reg_idx) {
cpr_rstctl_subrst_sw__ssi0_rstn__setf(RSTCTL_ENABLE);
cpr_rstctl_subrst_sw__ssi0_rstn__setf(RSTCTL_DISABLE);
cpr_ctlapbclken_grctl__ssi0_pclk_en__setf(ENABLE);
cpr_ssi0_mclk_ctl__ssi_mclk_div__setf(0);
cpr_ssi0_mclk_ctl__ssi_mclk_en__setf(1);
cpr_ssi_ctrl__ssi0_protocol__setf(SSI_CTRL0_FRF_MOTOROLA);
}
#endif
if (SPI1_IDX == reg_idx) {
cpr_rstctl_subrst_sw__ssi1_rstn__setf(RSTCTL_ENABLE);
cpr_rstctl_subrst_sw__ssi1_rstn__setf(RSTCTL_DISABLE);
cpr_ssi1_mclk_ctl__ssi_mclk_div__setf(0);
cpr_ssi1_mclk_ctl__ssi_mclk_en__setf(1);
cpr_ctlapbclken_grctl__ssi1_pclk_en__setf(ENABLE);
if (init_cfg->Mode == SPI_MODE_MASTER) {
cpr_ssi_ctrl__ssi1_master_en__setf(ENABLE);
} else if (init_cfg->Mode == SPI_MODE_SLAVE) {
cpr_ssi_ctrl__ssi1_master_en__setf(DISABLE);
}
cpr_ssi_ctrl__ssi1_protocol__setf(SSI_CTRL0_FRF_MOTOROLA);
} else if (SPI2_IDX == reg_idx) {
cpr_rstctl_subrst_sw__ssi2_rstn__setf(RSTCTL_ENABLE);
cpr_rstctl_subrst_sw__ssi2_rstn__setf(RSTCTL_DISABLE);
cpr_ssi1_mclk_ctl__ssi2_mclk_div__setf(0);
cpr_ssi1_mclk_ctl__ssi2_mclk_en__setf(1);
cpr_ctlapbclken_grctl__ssi2_pclk_en__setf(ENABLE);
cpr_ssi_ctrl__ssi2_protocol__setf(SSI_CTRL0_FRF_MOTOROLA);
}
spi_en_set(reg_idx, DISABLE);
// sste is automatically set to 1 when spi slave is initialized.
// therefor, manually clear this bit to 0 here.
spi_ctrl0__ssi_sste__setf(reg_idx, DISABLE);
spi_ctrl0__ssi_tmod__setf(reg_idx, init_cfg->Direction);
spi_ctrl0__ssi_scpol__setf(reg_idx, init_cfg->CLKPolarity);
spi_ctrl0__ssi_scpha__setf(reg_idx, init_cfg->CLKPhase);
spi_ctrl0__ssi_dfs__setf(reg_idx, init_cfg->DataSize);
spi_ie_set(reg_idx, DISABLE);
spi_se_set(reg_idx, ENABLE);
spi_baud__ssi_sckdv__setf(reg_idx, init_cfg->BaudRatePrescaler);
spi_txftl__ssi_tft__setf(reg_idx, SSI_TXFTL_FIFO_1);
spi_rxftl__ssi_rft__setf(reg_idx, SSI_RXFTL_FIFO_1);
}
static void xc_spi_enable(uint8_t reg_idx, uint8_t dfs)
{
spi_en__ssi_sen__setf(reg_idx, DISABLE);
spi_dmas_set(reg_idx, DISABLE);
spi_ctrl0__ssi_dfs__setf(reg_idx, dfs);
spi_en__ssi_sen__setf(reg_idx, ENABLE);
}
static void xc_spi_disable(uint8_t reg_idx) { spi_en__ssi_sen__setf(reg_idx, DISABLE); }
void xc_spi_rxftl_fifo_set(uint8_t reg_idx, uint8_t val)
{
spi_rxftl__ssi_rft__setf(reg_idx, val);
}
void xc_spi_txftl_fifo_set(uint8_t reg_idx, uint8_t val)
{
spi_txftl__ssi_tft__setf(reg_idx, val);
}
void xc_spi_enable_it(uint8_t reg_idx, uint8_t val) { spi_ie_set(reg_idx, val); }
void xc_spi_disable_it(uint8_t reg_idx, uint8_t val) { spi_ie_set(reg_idx, val); }
void xc_spi_flash_power_down(uint8_t reg_idx)
{
uint8_t txbuff[2] = {0};
txbuff[1] = CMD_PWRDWN;
xc_spi_enable(reg_idx, SSI_CTRL0_DFS_LEN_8BIT);
xc_spi_write_bytes(reg_idx, txbuff, sizeof(txbuff));
xc_spi_disable(reg_idx);
}
void xc_spi_flash_wake_up(uint8_t reg_idx)
{
uint8_t txbuff[2] = {0};
txbuff[1] = CMD_RELEASE_PWRDWN;
xc_spi_enable(reg_idx, SSI_CTRL0_DFS_LEN_8BIT);
xc_spi_write_bytes(reg_idx, txbuff, sizeof(txbuff));
xc_spi_disable(reg_idx);
}
static uint8_t xc_spi_flash_wait_busy(uint8_t reg_idx)
{
uint8_t cmd[2] = {0};
uint8_t sta[2] = {0xff, 0xff};
cmd[0] = CMD_READ_STATUS;
cmd[1] = 0xff;
xc_spi_enable(reg_idx, SSI_CTRL0_DFS_LEN_16BIT);
xc_spi_write_bytes(reg_idx, cmd, sizeof(cmd));
xc_spi_read_bytes(reg_idx, sta, sizeof(sta));
xc_spi_disable(reg_idx);
return (sta[1] & 0x01);
}
static void xc_spi_flash_write_enable(uint8_t reg_idx)
{
uint8_t txbuff[2] = {0};
txbuff[1] = CMD_WRITE_ENABLE;
xc_spi_enable(reg_idx, SSI_CTRL0_DFS_LEN_8BIT);
xc_spi_write_bytes(reg_idx, txbuff, sizeof(txbuff));
xc_spi_disable(reg_idx);
}
eXC_RESULT xc_spi_flash_erase_sector(uint8_t reg_idx, uint32_t Dst_Addr)
{
uint8_t cmd[4];
while (xc_spi_flash_wait_busy(reg_idx))
;
xc_spi_flash_write_enable(reg_idx);
while (xc_spi_flash_wait_busy(reg_idx))
;
cmd[0] = CMD_SECTOR_ERASE;
cmd[1] = Dst_Addr >> 16;
cmd[2] = Dst_Addr >> 8;
cmd[3] = Dst_Addr;
xc_spi_enable(reg_idx, SSI_CTRL0_DFS_LEN_16BIT);
#ifndef FAST_SPI
xc_spi_write_bytes(reg_idx, cmd, sizeof(cmd));
#else
xc_spi_write_2bytes(reg_idx, cmd, sizeof(cmd));
#endif
xc_spi_disable(reg_idx);
while (xc_spi_flash_wait_busy(reg_idx))
;
return XR_OK;
}
#if (USE_INTEGRATED_FlASH == USE_PUYA_FlASH)
void xc_spi_flash_erase_page(uint8_t reg_idx, uint32_t Dst_Addr)
{
uint8_t cmd[4];
while (xc_spi_flash_wait_busy(reg_idx))
;
xc_spi_flash_write_enable(reg_idx);
while (xc_spi_flash_wait_busy(reg_idx))
;
cmd[0] = CMD_PAGE_ERASE;
cmd[1] = Dst_Addr >> 16;
cmd[2] = Dst_Addr >> 8;
cmd[3] = Dst_Addr;
xc_spi_enable(reg_idx, SSI_CTRL0_DFS_LEN_16BIT);
#ifndef FAST_SPI
xc_spi_write_bytes(reg_idx, cmd, sizeof(cmd));
#else
xc_spi_write_2bytes(reg_idx, cmd, sizeof(cmd));
#endif
xc_spi_disable(reg_idx);
while (xc_spi_flash_wait_busy(reg_idx))
;
}
#endif
#ifndef FAST_SPI
void xc_spi_flash_write_page(uint8_t reg_idx, uint32_t WriteAddr, uint8_t *pBuffer)
{
uint32_t addr = WriteAddr;
uint8_t cmd[16 + 4] = {0};
for (uint8_t i = 0; i < 16; i++) {
while (xc_spi_flash_wait_busy(reg_idx))
;
xc_spi_flash_write_enable(reg_idx);
while (xc_spi_flash_wait_busy(reg_idx))
;
cmd[0] = CMD_PAGE_PROGRAM;
cmd[1] = addr >> 16;
cmd[2] = addr >> 8;
cmd[3] = addr;
memcpy(&cmd[4], pBuffer + i * 16, 16);
xc_spi_enable(reg_idx, SSI_CTRL0_DFS_LEN_16BIT);
xc_spi_write_bytes(reg_idx, cmd, sizeof(cmd));
addr += 16;
}
while (xc_spi_flash_wait_busy(reg_idx))
;
xc_spi_disable(reg_idx);
}
#else
void xc_spi_flash_write_page(uint8_t reg_idx, uint32_t WriteAddr, uint8_t *pBuffer)
{
uint8_t cmd[256 + 4] = {0};
while (xc_spi_flash_wait_busy(reg_idx))
;
xc_spi_flash_write_enable(reg_idx);
while (xc_spi_flash_wait_busy(reg_idx))
;
cmd[0] = CMD_PAGE_PROGRAM;
cmd[1] = WriteAddr >> 16;
cmd[2] = WriteAddr >> 8;
cmd[3] = WriteAddr;
memcpy(&cmd[4], pBuffer, FLASH_PAGE_SIZE);
xc_spi_write_2bytes(reg_idx, cmd, sizeof(cmd));
while (xc_spi_flash_wait_busy(reg_idx))
;
xc_spi_disable(reg_idx);
}
#endif
void xc_spi_flash_read_page(uint8_t reg_idx, uint32_t ReadAddr, uint8_t *pBuffer)
{
uint8_t cmd[16 + 4] = {0};
uint8_t mid[16 + 4] = {0};
uint16_t rx_idx = 0;
uint32_t addr = ReadAddr;
for (uint8_t i = 0; i < 16; i++) {
cmd[0] = CMD_READ_DATA;
cmd[1] = addr >> 16;
cmd[2] = addr >> 8;
cmd[3] = addr;
xc_spi_enable(reg_idx, SSI_CTRL0_DFS_LEN_16BIT);
xc_spi_write_bytes(reg_idx, cmd, sizeof(cmd));
xc_spi_read_bytes(reg_idx, mid, sizeof(cmd));
memcpy(&pBuffer[rx_idx], mid + 4, 16);
addr += 16;
rx_idx += 16;
}
xc_spi_disable(reg_idx);
}
/**
****************************************************************************************
* @brief
*
* @param[in]
* @param[in]
* @return[out]
*
****************************************************************************************
*/
eXC_RESULT xc_spi_flash_write(uint8_t reg_idx, uint32_t addr, uint8_t *buff,
uint16_t size)
{
eXC_RESULT ret = XR_OK;
uint32_t cur_addr = addr;
uint32_t end_addr = addr + size;
uint16_t wt_len = 0;
// The current page number occupied by this data storage
// The position occupied by this address on the current page
uint32_t cur_start_psr = 0;
uint16_t cur_size;
#ifndef GD_FlASH_INTEGRATION
uint16_t cur_page_num = 0;
uint8_t temp_buff[FLASH_PAGE_SIZE];
while (cur_addr != end_addr) {
/* code */
cur_page_num = CUR_PAGE_NUM(cur_addr);
cur_start_psr = CUR_START_PSR(cur_addr);
xc_spi_flash_read_page(reg_idx, cur_page_num * FLASH_PAGE_SIZE, temp_buff);
xc_spi_flash_erase_page(reg_idx, cur_page_num * FLASH_PAGE_SIZE);
if (cur_start_psr) {
cur_size = MIN(FLASH_PAGE_SIZE - cur_start_psr, size - wt_len);
} else {
cur_size = MIN(FLASH_PAGE_SIZE, size - wt_len);
}
memcpy(&temp_buff[cur_start_psr], buff + wt_len, cur_size);
wt_len += cur_size;
cur_addr += cur_size;
xc_spi_flash_write_page(reg_idx, cur_page_num * FLASH_PAGE_SIZE, temp_buff);
}
#else
uint16_t cur_sector_num = 0;
uint8_t temp_buff[FLASH_SECTOR_SIZE] = {0};
while (cur_addr != end_addr) {
/* code */
cur_sector_num = CUR_SECTOR_NUM(cur_addr);
cur_start_psr = CUR_START_PSR(cur_addr);
for(int i=0;i<FLASH_SECTOR_SIZE/FLASH_PAGE_SIZE;i++){
xc_spi_flash_read_page(reg_idx, CUR_SECTOR_PAGE_ADDR(cur_sector_num, i), temp_buff+i*FLASH_PAGE_SIZE);
}
xc_spi_flash_erase_sector(reg_idx, cur_sector_num * FLASH_SECTOR_SIZE);
if (cur_start_psr) {
cur_size = MIN(FLASH_SECTOR_SIZE - cur_start_psr, size - wt_len);
} else {
cur_size = MIN(FLASH_SECTOR_SIZE, size - wt_len);
}
memcpy(&temp_buff[cur_start_psr], buff + wt_len, cur_size);
wt_len += cur_size;
cur_addr += cur_size;
for(int i=0;i<FLASH_SECTOR_SIZE/FLASH_PAGE_SIZE;i++){
xc_spi_flash_write_page(reg_idx, CUR_SECTOR_PAGE_ADDR(cur_sector_num, i), temp_buff+i*FLASH_PAGE_SIZE);
}
}
#endif
return ret;
}
/**
****************************************************************************************
* @brief
*
* @param[in]
* @param[in]
* @return[out]
*
****************************************************************************************
*/
eXC_RESULT xc_spi_flash_read(uint8_t reg_idx, uint32_t addr, uint8_t *buff,
uint16_t size)
{
eXC_RESULT ret = XR_OK;
uint32_t cur_addr = addr;
uint32_t end_addr = addr + size;
uint16_t rd_len = 0;
uint16_t cur_page_num = 0;
uint32_t cur_start_psr = 0;
uint8_t temp_buff[FLASH_PAGE_SIZE] = {0};
uint16_t cur_size;
while (cur_addr != end_addr) {
cur_page_num = CUR_PAGE_NUM(cur_addr);
cur_start_psr = CUR_START_PSR(cur_addr);
xc_spi_flash_read_page(reg_idx, cur_page_num * FLASH_PAGE_SIZE, temp_buff);
if (cur_start_psr) {
cur_size = MIN(FLASH_PAGE_SIZE - cur_start_psr, size - rd_len);
} else {
cur_size = MIN(FLASH_PAGE_SIZE, size - rd_len);
}
memcpy(buff + rd_len, &temp_buff[cur_start_psr], cur_size);
cur_addr += cur_size;
rd_len += cur_size;
}
return ret;
}
void xc_spi_flash_read_128bit_uid(uint8_t reg_idx, uint8_t *buff)
{
while (xc_spi_flash_wait_busy(reg_idx)) {
DEBUG("line=%d,status=%d\n", __LINE__, xc_spi_flash_wait_busy(reg_idx));
};
uint8_t cmd[21] = {0};
uint8_t id[21] = {0};
cmd[0] = CMD_RUID;
cmd[1] = 0x00;
cmd[2] = 0x00;
cmd[3] = 0x00;
cmd[4] = 0x00;
xc_spi_enable(reg_idx, SSI_CTRL0_DFS_LEN_16BIT);
xc_spi_write_bytes(reg_idx, cmd, sizeof(cmd));
xc_spi_read_bytes(reg_idx, id, sizeof(id));
xc_spi_disable(reg_idx);
memcpy(buff, &id[5], 16);
}
eXC_RESULT xc_spi_flash_rdid(uint8_t reg_idx, uint8_t *buff)
{
while (xc_spi_flash_wait_busy(reg_idx)) {
DEBUG("line=%d,status=%d\n", __LINE__, xc_spi_flash_wait_busy(reg_idx));
};
uint8_t cmd[4] = {0};
uint8_t id[4] = {0};
cmd[0] = CMD_RDID;
cmd[1] = 0xff;
cmd[2] = 0xff;
cmd[3] = 0xff;
xc_spi_enable(reg_idx, SSI_CTRL0_DFS_LEN_16BIT);
xc_spi_write_bytes(reg_idx, cmd, sizeof(cmd));
xc_spi_read_bytes(reg_idx, id, sizeof(id));
xc_spi_disable(reg_idx);
memcpy(buff, &id[1], 3);
return XR_OK;
}
/*---------------------------------- SPI1 Master & Slave API
* ---------------------------------------*/
/**
****************************************************************************************
* @brief Only Master & Slave
*
* @param[in]
* @param[in]
****************************************************************************************
*/
static void xc_spi_datasize_set(uint8_t reg_idx, uint8_t dfs)
{
spi_en_set(reg_idx, DISABLE);
spi_ctrl0__ssi_dfs__setf(reg_idx, dfs);
spi_en_set(reg_idx, ENABLE);
}
/**
****************************************************************************************
* @brief Only applicable Master & Slave communication
*
* @param[in]
* @param[in]
****************************************************************************************
*/
eXC_RESULT xc_spi_write_bytes(uint8_t reg_idx, uint8_t *txdata, uint16_t len)
{
if (len == 0)
return XR_ERROR;
uint16_t n;
uint8_t remain = len % 2;
uint16_t cnt = len / 2;
uint16_t mid_data[128 + 1] = {0};
uint32_t i = 0;
while (spi_sts__ssi_tfe__getf(reg_idx) == RESET) {
i++;
if (i >= SPI_WAIT_TIMEOUT) {
DEBUG("func=%s,line=%d,timeout\n", __func__, __LINE__);
return XR_TIMEOUT;
}
}
if (cnt != 0) {
for (n = 0; n < cnt; n++) {
mid_data[n] = ((txdata[n * 2] << 8) | txdata[n * 2 + 1]);
}
}
if (remain != 0) {
for (n = 0; n < remain; n++) {
mid_data[cnt] |= (txdata[cnt * 2 + n] << (16 - 8 * n));
}
}
if (cnt != 0) {
if (remain != 0)
cnt = cnt + 1;
for (n = 0; n < cnt; n++)
spi_data_set(reg_idx, mid_data[n]);
} else {
spi_data_set(reg_idx, mid_data[0]);
}
while ((spi_sts__ssi_rfne__getf(reg_idx) == RESET) ||
(spi_sts__ssi_tfe__getf(reg_idx) == RESET)) {
i++;
if (i >= SPI_WAIT_TIMEOUT) {
// DEBUG("func=%s,line=%d,timeout\n", __func__, __LINE__);
return XR_TIMEOUT;
}
}
return XR_OK;
}
#if FAST_SPI
eXC_RESULT xc_spi_write_2bytes(uint8_t reg_idx, uint8_t *txdata, uint16_t size)
{
uint32_t time_out;
uint32_t temp_data;
uint16_t idx_8bit = 0;
uint16_t idx_16bit = 0;
uint8_t remain_size = size % 2;
uint16_t allign_16bit_size = size >> 1;
if (size == 0)
return XR_ERROR;
xc_spi_enable(reg_idx, SSI_CTRL0_DFS_LEN_16BIT);
while(!(spi_sts_get(reg_idx)&(1<<SSI_TFE_POS)));
while(idx_16bit < allign_16bit_size)
{
temp_data = ((txdata[idx_8bit] << 8) | txdata[idx_8bit+1]);
spi_data_set(reg_idx, temp_data);
idx_16bit++;
idx_8bit+=2;
}
if (remain_size != 0 ){
for (uint8_t i = 0; i < remain_size; i++){
temp_data |= (txdata[idx_8bit + i] << (8 - 8 * i));
}
while (!(spi_sts_get(reg_idx) & (1<<SSI_TFNF_POS)));
spi_data_set(reg_idx, temp_data);
}
while (!(spi_sts_get(reg_idx) & (1<<SSI_TFE_POS)) ||
(spi_sts_get(reg_idx) & (1<<SSI_BUSY_POS)))
{
time_out++;
if (time_out >= SPI_WAIT_TIMEOUT) {
DEBUG("func=%s,line=%d,timeout\n", __func__, __LINE__);
return XR_TIMEOUT;
}
}
return XR_OK;
}
#endif
/**
****************************************************************************************
* @brief Only applicable Master & Slave communication
*
* @param[in]
* @param[in]
****************************************************************************************
*/
eXC_RESULT xc_spi_read_bytes(uint8_t reg_idx, uint8_t *rxdata, uint16_t len)
{
if (len == 0)
return XR_ERROR;
uint16_t n;
uint8_t remain = len % 2;
uint16_t cnt = len / 2;
uint16_t mid_data = 0;
if (cnt != 0) {
for (n = 0; n < cnt; n++) {
mid_data = spi_data_get(reg_idx);
rxdata[n * 2] = mid_data >> 8;
rxdata[n * 2 + 1] = mid_data;
}
}
if (remain != 0) {
mid_data = spi_data_get(reg_idx);
for (n = 0; n < remain; n++) {
rxdata[cnt * 2 + n] = mid_data >> 8;
rxdata[cnt * 2 + 1 + n] = mid_data;
}
}
return XR_OK;
}
/**
****************************************************************************************
* @brief Only applicable Master & Slave communication
*
* @param[in]
* @param[in]
****************************************************************************************
*/
void xc_spi_write_and_read_data(uint8_t reg_idx, uint8_t *w_data, uint16_t w_size,
uint8_t *r_data, uint16_t r_size)
{
if (w_size != (spi_rxftl_get(reg_idx) + 1) * 2)
return;
xc_spi_datasize_set(reg_idx, SSI_CTRL0_DFS_LEN_16BIT);
xc_spi_write_bytes(reg_idx, w_data, w_size);
while (!(spi_is_get(reg_idx) == SSI_IS_RXFIS_SET))
;
xc_spi_read_bytes(reg_idx, r_data, r_size);
xc_spi_disable(reg_idx);
}
#endif // XC_CHECK(XC_SPI_ENABLED)