/* Copyright Statement:
 *
 * This software/firmware and related documentation ("AutoChips Software") are
 * protected under relevant copyright laws. The information contained herein is
 * confidential and proprietary to AutoChips Inc. and/or its licensors. Without
 * the prior written permission of AutoChips inc. and/or its licensors, any
 * reproduction, modification, use or disclosure of AutoChips Software, and
 * information contained herein, in whole or in part, shall be strictly
 * prohibited.
 *
 * AutoChips Inc. (C) 2023. All rights reserved.
 *
 * BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES
 * THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("AUTOCHIPS SOFTWARE")
 * RECEIVED FROM AUTOCHIPS AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER
 * ON AN "AS-IS" BASIS ONLY. AUTOCHIPS EXPRESSLY DISCLAIMS ANY AND ALL
 * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
 * NONINFRINGEMENT. NEITHER DOES AUTOCHIPS PROVIDE ANY WARRANTY WHATSOEVER WITH
 * RESPECT TO THE SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY,
 * INCORPORATED IN, OR SUPPLIED WITH THE AUTOCHIPS SOFTWARE, AND RECEIVER AGREES
 * TO LOOK ONLY TO SUCH THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO.
 * RECEIVER EXPRESSLY ACKNOWLEDGES THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO
 * OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES CONTAINED IN AUTOCHIPS
 * SOFTWARE. AUTOCHIPS SHALL ALSO NOT BE RESPONSIBLE FOR ANY AUTOCHIPS SOFTWARE
 * RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR
 * STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND AUTOCHIPS'S
 * ENTIRE AND CUMULATIVE LIABILITY WITH RESPECT TO THE AUTOCHIPS SOFTWARE
 * RELEASED HEREUNDER WILL BE, AT AUTOCHIPS'S OPTION, TO REVISE OR REPLACE THE
 * AUTOCHIPS SOFTWARE AT ISSUE, OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE
 * CHARGE PAID BY RECEIVER TO AUTOCHIPS FOR SUCH AUTOCHIPS SOFTWARE AT ISSUE.
 */
/******************************************************************************
 * @version: V1.0.0 demo
 ******************************************************************************/

/*!
* @file spi_w25x16.c
*
* @brief This file is the library function of spi_w25x16
*
*/

/* ===========================================  Includes  =========================================== */
#include "gpio.h"
#include "spi.h"
#include "ac780x_spi.h"
#include "spi_w25x16.h"

/* ============================================  Define  ============================================ */

/* ===========================================  Typedef  ============================================ */

/* ==========================================  Variables  =========================================== */
uint8_t g_flashWrDataBuf[W25X16_DATA_BUF_LEN];
uint8_t g_flashRdDataBuf[W25X16_DATA_BUF_LEN];
uint8_t g_flshTstDataBuf[W25X16_TEST_DAT_LEN];
uint8_t g_flashWrRdRdy;
uint16_t g_flashWrRdTime;

/* ====================================  Functions declaration  ===================================== */
static void W25X16_EnableWr(void);
static uint8_t W25X16_IsBusy(void);

/* ======================================  Functions define  ======================================== */
/**
* @brief    enable write W25X16.
*
* @param[in] void
* @return   void
*
*/
static void W25X16_EnableWr(void)
{
    /* First judge whether the w25X16 is busy, and only when it is free can it be operated */
    while(W25X16_IsBusy());
    g_flashWrDataBuf[0] = W25X16_CMD_WR_EN;
    
    SPI_TransmitPoll(SPI0, g_flashWrDataBuf, 1);
}

/**
* @brief  Determine whether w25x16 is busy
*
* @param[in] void
* @return   0->Not busy. 1->Busy.
*
*/
static uint8_t W25X16_IsBusy(void)
{
    g_flashWrDataBuf[0] = W25X16_CMD_RD_STS;
    
    SPI_TransmitReceivePoll(SPI0, g_flashRdDataBuf, g_flashWrDataBuf, 2);
    
    return (g_flashRdDataBuf[1] & W25X16_BUSY_MSK);
}

/**
* @brief    erase W25X16.
*
* @param[in] startAddr:  start Addr
* @param[in] secErNum: sector number
* @return   void
*
*/
void W25X16_ErsSector(uint32_t startAddr, uint8_t secErNum)
{
    uint8_t  ii;
    
    if ((startAddr + secErNum * W25X16_SEC_SIZE) > W25X16_ADDR_MAX){return;}
    
    for (ii = 0; ii < secErNum; ii++)
    {
        /*使能写,在擦除、写入后自动关闭写.*/
        W25X16_EnableWr();
        
        /*装载擦除指令,并发送指令.*/
        g_flashWrDataBuf[0] = W25X16_CMD_ER_SEC;
        g_flashWrDataBuf[1] = (startAddr >> 16);
        g_flashWrDataBuf[2] = (startAddr >>  8);
        g_flashWrDataBuf[3] = (startAddr >>  0);
        
        SPI_TransmitPoll(SPI0, g_flashWrDataBuf, 4);
        
        startAddr += W25X16_SEC_SIZE;
    }
}

/**
* @brief    Write specified length byte data to w25x16
*
* @param[in] startAddr: start Addr
* @param[in] pDataBuf:  data buffer
* @param[in] dataLen:   data len
* @return   void
*
*/
void W25X16_WriteBytes(uint32_t startAddr, uint8_t *pDataBuf, uint16_t dataLen)
{
    uint8_t ii,restLen, wrTimes;
    
    if ((startAddr + (dataLen - 1)) > W25X16_ADDR_MAX){return;}
    
    restLen = (dataLen % 256);  /* Less than 256 bytes in length */
    wrTimes = (dataLen / 256);  /* Write up to 256 bytes at a time */
    
    /* Satisfy 256 bytes of data write */
    for (ii = 0; ii < wrTimes; ii++)
    {
        /* enable write. */
        W25X16_EnableWr();
        
        /* write data. */
        g_flashWrDataBuf[0] = W25X16_CMD_WR_DAT;
        g_flashWrDataBuf[1] = (startAddr >> 16);
        g_flashWrDataBuf[2] = (startAddr >> 8);
        g_flashWrDataBuf[3] = (startAddr >> 0);
        memcpy(&g_flashWrDataBuf[4], &pDataBuf[ii * 256], 256); /* A single write up to 256 bytes */
        
        SPI_TransmitPoll(SPI0, g_flashWrDataBuf, (256 + 4));
        
        startAddr += 256;
        mdelay(1); /* Need to delay a few ms */
    }
    /* Write less than 256 bytes of data */
    if (restLen > 0)
    {
        /* enable write */
        W25X16_EnableWr();
        
        /* write data. */
        g_flashWrDataBuf[0] = W25X16_CMD_WR_DAT;
        g_flashWrDataBuf[1] = (startAddr >> 16);
        g_flashWrDataBuf[2] = (startAddr >> 8);
        g_flashWrDataBuf[3] = (startAddr >> 0);
        memcpy(&g_flashWrDataBuf[4], &pDataBuf[wrTimes * 256], restLen);
        
        SPI_TransmitPoll(SPI0, g_flashWrDataBuf, (restLen + 4));
        
        mdelay(1); /* Need to delay a few ms */
    }
}

/**
* @brief   Read specified length byte data from w25x16
*
* @param[in] startAddr: start addr
* @param[in] pDataBuf:  data buffer
* @param[in] dataLen:   data len
* @return   void
*
*/
void W25X16_ReadBytes(uint32_t startAddr, uint8_t *pDataBuf, uint16_t dataLen)
{
    while(W25X16_IsBusy());
    g_flashWrDataBuf[0] = W25X16_CMD_RD_DAT;
    g_flashWrDataBuf[1] = (startAddr >> 16);
    g_flashWrDataBuf[2] = (startAddr >>  8);
    g_flashWrDataBuf[3] = (startAddr >>  0);
    
    SPI_TransmitReceivePoll(SPI0, g_flashRdDataBuf, g_flashWrDataBuf, (dataLen + 4));/* Can read any length */
    
    memcpy(pDataBuf, &g_flashRdDataBuf[4], dataLen);
    mdelay(1);  /* Need to delay a few ms */
}
/* =============================================  EOF  ============================================== */

