/*! * \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" #include "xc_drv_fmc_spi.h" /*------------------------------------------------------------------------------------ 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); xc_spi_write_bytes(reg_idx, cmd, sizeof(cmd)); 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); xc_spi_write_bytes(reg_idx, cmd, sizeof(cmd)); xc_spi_disable(reg_idx); while (xc_spi_flash_wait_busy(reg_idx)) ; } #endif 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); } 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 writeAddr, uint8_t *buff, uint16_t size) { uint32_t cur_addr = writeAddr; uint32_t end_addr = writeAddr + size; uint16_t wt_len = 0; // The current page number occupied by this data storage uint16_t CurSectorNum = 0; uint16_t CurPageNum = 0; // The position occupied by this address on the current page uint32_t CurStartPsr = 0; uint32_t mid = 0; xc_spi_flash_rdid(reg_idx ,(uint8_t*)&mid); if(mid != GD_FlASH_RDID){ uint8_t temp_buff[FLASH_PAGE_SIZE]; while (cur_addr != end_addr) { /* code */ CurPageNum = CUR_PAGE_NUM(cur_addr); CurStartPsr = CUR_START_PSR(cur_addr); xc_spi_flash_read_page(reg_idx, CurPageNum * FLASH_PAGE_SIZE, temp_buff); xc_spi_flash_erase_page(reg_idx, CurPageNum * FLASH_PAGE_SIZE); if (CurStartPsr) { memcpy(&temp_buff[CurStartPsr], buff + wt_len, (FLASH_PAGE_SIZE - CurStartPsr) > size - wt_len // 当前扇区剩余的空间是否满足写长度 ? size - wt_len : (FLASH_PAGE_SIZE - CurStartPsr)); wt_len += wt_len + ((FLASH_PAGE_SIZE - CurStartPsr) > size - wt_len ? size - wt_len : (FLASH_PAGE_SIZE - CurStartPsr)); cur_addr += wt_len; } else { memcpy(temp_buff, buff + wt_len, size - wt_len > FLASH_PAGE_SIZE ? FLASH_PAGE_SIZE : size - wt_len); cur_addr += (size - wt_len > FLASH_PAGE_SIZE ? FLASH_PAGE_SIZE : size - wt_len); wt_len += (size - wt_len > FLASH_PAGE_SIZE ? FLASH_PAGE_SIZE : size - wt_len); } xc_spi_flash_write_page(reg_idx, CurPageNum * FLASH_PAGE_SIZE, temp_buff); } }else if(mid == GD_FlASH_RDID){ uint8_t temp_buff[FLASH_SECTOR_SIZE]; while (cur_addr != end_addr) { /* code */ CurSectorNum = CUR_SECTOR_NUM(cur_addr); CurStartPsr = CUR_START_PSR(cur_addr); for(int i=0;i size - wt_len // 当前扇区剩余的空间是否满足写长度 ? size - wt_len : (FLASH_SECTOR_SIZE - CurStartPsr)); wt_len += wt_len + ((FLASH_SECTOR_SIZE - CurStartPsr) > size - wt_len ? size - wt_len : (FLASH_SECTOR_SIZE - CurStartPsr)); cur_addr += wt_len; } else { memcpy(temp_buff, buff + wt_len, size - wt_len > FLASH_SECTOR_SIZE ? FLASH_SECTOR_SIZE : size - wt_len); cur_addr += (size - wt_len > FLASH_SECTOR_SIZE ? FLASH_SECTOR_SIZE : size - wt_len); wt_len += (size - wt_len > FLASH_SECTOR_SIZE ? FLASH_SECTOR_SIZE : size - wt_len); } for(int i=0;i FLASH_PAGE_SIZE) ReqPageCnt = 2; else ReqPageCnt = 1; if (ReqPageCnt == 1) // Note that only one page is read here { xc_spi_flash_read_page(reg_idx, CurPageNum * FLASH_PAGE_SIZE, temp_buff); memcpy(buff, &temp_buff[CurStartPsr], len); } else if (ReqPageCnt == 2) { NextPageNum = CurPageNum + 1; NextStopPsr = len + CurStartPsr - FLASH_PAGE_SIZE; // First page read xc_spi_flash_read_page(reg_idx, CurPageNum * FLASH_PAGE_SIZE, temp_buff); memcpy(buff, &temp_buff[CurStartPsr], len - NextStopPsr); // second page read uint16_t HaveCopyNum = len - NextStopPsr; xc_spi_flash_read_page(reg_idx, NextPageNum * FLASH_PAGE_SIZE, temp_buff); memcpy(buff + HaveCopyNum, temp_buff, NextStopPsr); } } else { CurPageNum = CUR_PAGE_NUM(readAddr); CurStartPsr = CUR_START_PSR(readAddr); NextPageNum = CurPageNum + 1; uint16_t buff_idx = 0; uint16_t pre_len = NextPageNum * FLASH_PAGE_SIZE - readAddr; uint32_t pre_addr = CurPageNum * FLASH_PAGE_SIZE; xc_spi_flash_read_page(reg_idx, pre_addr, temp_buff); memcpy(buff, &temp_buff[CurStartPsr], pre_len); buff_idx += pre_len; pre_addr = NextPageNum * FLASH_PAGE_SIZE; while (buff_idx < len) { if ((len - buff_idx) >= FLASH_PAGE_SIZE) { xc_spi_flash_read_page(reg_idx, pre_addr, temp_buff); memcpy(&buff[buff_idx], temp_buff, FLASH_PAGE_SIZE); buff_idx += FLASH_PAGE_SIZE; pre_addr += FLASH_PAGE_SIZE; } else { xc_spi_flash_read_page(reg_idx, pre_addr, temp_buff); memcpy(&buff[buff_idx], temp_buff, len - buff_idx); buff_idx += (len - buff_idx); pre_addr += (len - buff_idx); } } } 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}; uint16_t i = 0; while (spi_sts__ssi_tfe__getf(reg_idx) == RESET) { i++; if (i >= SPI_WAIT_TIMEOUT) { //h // 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; } /** **************************************************************************************** * @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); }