/**
  ******************************************************************************
  * @file    spi_norflash.c
  * @author  Kiwi Software Team
  * @brief
  *          This file provides firmware functions to manage spi norflash.
  * @note
  *
  * Copyright (c) 2024, Kiwi Instruments Co,. Ltd.
  *
  * Redistribution and use in source and binary forms, with or without modification,
  * are permitted provided that the following conditions are met:
  *
  *   1. Redistributions of source code must retain the above copyright notice,
  *      this list of conditions and the following disclaimer.
  *
  *   2. Redistributions in binary form must reproduce the above copyright notice,
  *      this list of conditions and the following disclaimer in the documentation
  *      and/or other materials provided with the distribution.
  *
  *   3. Neither the name of the copyright holder nor the names of its contributors
  *      may be used to endorse or promote products derived from this software without
  *      specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
  * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  * THE POSSIBILITY OF SUCH DAMAGE.
  ******************************************************************************
  */

#include "stdlib.h"
#include "string.h"
#include "spi_norflash.h"


uint8_t *mallocWrBuf = NULL;


#define __MANUFAID_FIRST__      0x00
#define __DEVICEID_FIRST__      0x01
#define __SPI_DUMMY_DATA__      0x5A

uint8_t spiFlashRdId    = 0x9F;
uint8_t spiFlashRes[4]  = {0xAB, __SPI_DUMMY_DATA__, __SPI_DUMMY_DATA__, __SPI_DUMMY_DATA__};
uint8_t spiFlashRmes[4] = {0x90, __SPI_DUMMY_DATA__, __SPI_DUMMY_DATA__, __MANUFAID_FIRST__};
uint8_t spiFlashRdSr    = 0x05;
uint8_t spiFlashWrEn    = 0x06;
uint8_t spiFlashWrDi    = 0x04;
uint8_t spiFlashSe[4]   = {0x20, __SPI_DUMMY_DATA__, __SPI_DUMMY_DATA__, __SPI_DUMMY_DATA__};
uint8_t spiFlashRdDt[4] = {0x03, __SPI_DUMMY_DATA__, __SPI_DUMMY_DATA__, __SPI_DUMMY_DATA__};
uint8_t spiFlashPP[4]   = {0x02, __SPI_DUMMY_DATA__, __SPI_DUMMY_DATA__, __SPI_DUMMY_DATA__};



DDL_Status_T DDL_SPI_NorFlash_Init(void)
{
	SPI_Init_T pSPIStruct;

	DDL_SPI_StructInit(&pSPIStruct);
	pSPIStruct.baudRate = 1000000;
	pSPIStruct.ssTimCfg.hldTim = 0;
	pSPIStruct.ssTimCfg.setTim = 0;
	pSPIStruct.nss[0].nssEn = 1;
	pSPIStruct.nss[0].hwCtrl = SPI_NSS_SOFT;

	mallocWrBuf = malloc(4 + __MAX_PP_DATE_LENGTH__);

    if (DDL_SPI_Init(SPI, &pSPIStruct) != DDL_OK)
	{
		__asm volatile("bkpt 0");
	}

	__DDL_SPI_PULL_CS_PIN2HIGH(SPI, 0);

	return DDL_OK;
}


DDL_Status_T DDL_SPI_NorFlash_RDID(uint8_t *idbuf, uint8_t idlen, uint32_t timeout)
{
	DDL_Status_T ret = DDL_OK;

	if (idlen < 3)
	{
		return DDL_ERROR;
	}

	__DDL_SPI_PULL_CS_PIN2LOW(SPI, 0);
	DDL_SPI_Transmit(SPI, &spiFlashRdId, 1, timeout);
	ret = DDL_SPI_MasterReceive(SPI, idbuf, 3, timeout);
	__DDL_SPI_PULL_CS_PIN2HIGH(SPI, 0);

	return ret;
}


DDL_Status_T DDL_SPI_NorFlash_RMES(uint8_t *mesbuf, uint8_t meslen, uint32_t timeout)
{
	DDL_Status_T ret = DDL_OK;

	if (meslen < 2)
	{
		return DDL_ERROR;
	}

	__DDL_SPI_PULL_CS_PIN2LOW(SPI, 0);
	DDL_SPI_Transmit(SPI, &spiFlashRmes[0], 4, timeout);
	ret = DDL_SPI_MasterReceive(SPI, mesbuf, 2, timeout);
	__DDL_SPI_PULL_CS_PIN2HIGH(SPI, 0);

	return ret;
}


DDL_Status_T DDL_SPI_NorFlash_RDSR(uint8_t *srbuf, uint8_t srlen, uint32_t timeout)
{
	DDL_Status_T ret = DDL_OK;

	if (srlen < 1)
	{
		return DDL_ERROR;
	}

	__DDL_SPI_MODULE_DISABLE(SPI);
	__DDL_SPI_MODULE_ENABLE(SPI);
	__DDL_SPI_PULL_CS_PIN2LOW(SPI, 0);
	DDL_SPI_Transmit(SPI, &spiFlashRdSr, 1, timeout);
	ret = DDL_SPI_MasterReceive(SPI, srbuf, 1, timeout);
	__DDL_SPI_PULL_CS_PIN2HIGH(SPI, 0);

	return ret;
}


DDL_Status_T DDL_SPI_NorFlash_WREN(void)
{
	DDL_Status_T ret = DDL_OK;

	__DDL_SPI_PULL_CS_PIN2LOW(SPI, 0);
	DDL_SPI_Transmit(SPI, &spiFlashWrEn, 1, __NF_ACCESS_WAITFOREVER__);
	__DDL_SPI_PULL_CS_PIN2HIGH(SPI, 0);

	return ret;
}


DDL_Status_T DDL_SPI_NorFlash_WRDI(void)
{
	DDL_Status_T ret = DDL_OK;

	__DDL_SPI_PULL_CS_PIN2LOW(SPI, 0);
	DDL_SPI_Transmit(SPI, &spiFlashWrDi, 1, __NF_ACCESS_WAITFOREVER__);
	__DDL_SPI_PULL_CS_PIN2HIGH(SPI, 0);

	return ret;
}


/**
  * Nor Flash sector Erase.
  */
DDL_Status_T DDL_SPI_NorFlash_SE(uint32_t nfaddr, uint32_t timeout)
{
	DDL_Status_T ret = DDL_OK;
	uint8_t nf_sts = 0;

	if(DDL_SPI_NorFlash_WREN() != DDL_OK)
	{
		return DDL_ERROR;
	}

	if (DDL_SPI_NorFlash_RDSR(&nf_sts, 1, timeout) != DDL_OK)
	{
		return DDL_ERROR;
	}

	if (!(nf_sts & NORFLASH_STR_WEL))
	{
		return DDL_BUSY;
	}

	/* Construct SE commands */
	spiFlashSe[1] = (uint8_t)(nfaddr >> 16 & 0xFF);
	spiFlashSe[2] = (uint8_t)(nfaddr >>  8 & 0xFF);
	spiFlashSe[3] = (uint8_t)(nfaddr >>  0 & 0xFF);
	__DDL_SPI_PULL_CS_PIN2LOW(SPI, 0);
	DDL_SPI_Transmit(SPI, spiFlashSe, 4, timeout);
	__DDL_SPI_PULL_CS_PIN2HIGH(SPI, 0);

	/* Wait till sector Erase completes */
	while(1)
	{
		if(DDL_SPI_NorFlash_RDSR(&nf_sts, 1, timeout) != DDL_OK)
		{
			return DDL_ERROR;
		}

		/* Write in Process completes*/
		if(!(nf_sts & NORFLASH_STR_WIP))
		{
			break;
		}
	}

	return ret;
}


/**
  * Nor Flash Page Program.
  */
DDL_Status_T DDL_SPI_NorFlash_PP(uint32_t nfaddr, uint8_t *wrbuf, uint8_t wrlen, uint32_t timeout)
{
	DDL_Status_T ret = DDL_OK;
	uint8_t nf_sts = 0;

	if (wrlen > __MAX_PP_DATE_LENGTH__)
	{
		return DDL_ERROR;
	}

	if(DDL_SPI_NorFlash_WREN() != DDL_OK)
	{
		return DDL_ERROR;
	}

	if (DDL_SPI_NorFlash_RDSR(&nf_sts, 1, timeout) != DDL_OK)
	{
		return DDL_ERROR;
	}

	if (!(nf_sts & NORFLASH_STR_WEL))
	{
		return DDL_BUSY;
	}

	/* Construct SE commands */
	spiFlashPP[1] = (uint8_t)(nfaddr >> 16 & 0xFF);
	spiFlashPP[2] = (uint8_t)(nfaddr >>  8 & 0xFF);
	spiFlashPP[3] = (uint8_t)(nfaddr >>  0 & 0xFF);

	memcpy(mallocWrBuf, spiFlashPP, 4);
	memcpy(mallocWrBuf + 4, wrbuf, wrlen);

	__DDL_SPI_PULL_CS_PIN2LOW(SPI, 0);
	DDL_SPI_Transmit(SPI, mallocWrBuf, wrlen + 4, timeout);
	__DDL_SPI_PULL_CS_PIN2HIGH(SPI, 0);

	/* Wait till Page Program completes */
	while(1)
	{
		if(DDL_SPI_NorFlash_RDSR(&nf_sts, 1, timeout) != DDL_OK)
		{
			return DDL_ERROR;
		}

		/* Write in Process completes*/
		if(!(nf_sts & NORFLASH_STR_WIP))
		{
			break;
		}
	}

	return ret;
}


/**
  * Nor Flash Read Data.
  */
DDL_Status_T DDL_SPI_NorFlash_RDDT(uint32_t nfaddr, uint8_t *rdbuf, uint8_t rdlen, uint32_t timeout)
{
	DDL_Status_T ret = DDL_OK;
	uint8_t nf_sts = 0;

	/* Wait till Write In Process completes */
	while(1)
	{
		if(DDL_SPI_NorFlash_RDSR(&nf_sts, 1, timeout) != DDL_OK)
		{
			return DDL_ERROR;
		}

		/* Write in Process completes*/
		if(!(nf_sts & NORFLASH_STR_WIP))
		{
			break;
		}
	}

	DDL_Delay(1);
	/* Construct SE commands */
	spiFlashRdDt[1] = (uint8_t)(nfaddr >> 16 & 0xFF);
	spiFlashRdDt[2] = (uint8_t)(nfaddr >>  8 & 0xFF);
	spiFlashRdDt[3] = (uint8_t)(nfaddr >>  0 & 0xFF);
	__DDL_SPI_PULL_CS_PIN2LOW(SPI, 0);
	DDL_SPI_Transmit(SPI, &spiFlashRdDt[0], 4, timeout);
	ret = DDL_SPI_MasterReceive(SPI, rdbuf, rdlen, timeout);
	__DDL_SPI_PULL_CS_PIN2HIGH(SPI, 0);

	return ret;
}

