/*
 * Copyright 2022 MindMotion Microelectronics Co., Ltd.
 * All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include "board_init.h"
#include "sdspi.h"
#include "hal_spi.h"

/* pins:
 * tx : PC12/SPI_MOSI
 * rx : PC8/SPI0_MISO
 * clk: PC12/SPI0_SCK
 * cs : PC11/SPI0_PCS0
 */

#define BOARD_SDSPI_TX_GPIO_PORT    GPIOC
#define BOARD_SDSPI_TX_GPIO_PIN     GPIO_PIN_12

#define BOARD_SDSPI_RX_GPIO_PORT    GPIOC
#define BOARD_SDSPI_RX_GPIO_PIN     GPIO_PIN_11

#define BOARD_SDSPI_CLK_GPIO_PORT   GPIOC
#define BOARD_SDSPI_CLK_GPIO_PIN    GPIO_PIN_10

#define BOARD_SDSPI_CS_GPIO_PORT    GPIOA
#define BOARD_SDSPI_CS_GPIO_PIN     GPIO_PIN_15

#define BOARD_SDSPI_SPI_PORT        SPI3

SDSPI_ApiRetStatus_Type sdspi_spi_init(void);
SDSPI_ApiRetStatus_Type sdspi_spi_freq(uint32_t hz);
SDSPI_ApiRetStatus_Type sdspi_spi_xfer(uint8_t *in, uint8_t *out, uint32_t len);

const SDSPI_Interface_Type board_sdspi_if =
{
    .baudrate = 1000000u, /* 1mhz. */
    .spi_init = sdspi_spi_init,
    .spi_freq = sdspi_spi_freq,
    .spi_xfer = sdspi_spi_xfer
};

uint32_t board_sdspi_delay_count;

SDSPI_ApiRetStatus_Type sdspi_spi_init(void)
{
    GPIO_WriteBit(BOARD_SDSPI_CS_GPIO_PORT, BOARD_SDSPI_CS_GPIO_PIN, 1u);

    /* Setup SPI module. */
    SPI_Master_Init_Type spi_init;
    spi_init.ClockFreqHz = CLOCK_APB1_FREQ;
    spi_init.BaudRate = SDMMC_CLOCK_400KHZ;
    spi_init.XferMode = SPI_XferMode_TxRx;
    spi_init.PolPha = SPI_PolPha_Alt2;
    spi_init.DataWidth = SPI_DataWidth_8b;
    spi_init.LSB = false;
    spi_init.AutoCS = true;
    SPI_InitMaster(BOARD_SDSPI_SPI_PORT, &spi_init);

    /* Enable SPI. */
    SPI_Enable(BOARD_SDSPI_SPI_PORT, true);

    return SDSPI_ApiRetStatus_Success;
}

void SPI_SetBaudRate(SPI_Type * SPIx, uint32_t src_clk, uint32_t baudrate)
{
    uint32_t div = src_clk / baudrate;
    if (div < 2u)
    {
        /* div = 0, 1 is not allowed. */
        div = 2u;
    }
    SPIx->SPBRG = div;
    if (div <= 4u)
    {
        /* to support high speed mode. */
        SPIx->CCTL |= (SPI_I2S_CCTL_TXEDGE_MASK | SPI_I2S_CCTL_RXEDGE_MASK);
    }
    else
    {
        SPIx->CCTL &= ~(SPI_I2S_CCTL_TXEDGE_MASK | SPI_I2S_CCTL_RXEDGE_MASK);
    }
}

SDSPI_ApiRetStatus_Type sdspi_spi_freq(uint32_t hz)
{
    switch (hz)
    {
    case SDMMC_CLOCK_400KHZ:
        SPI_Enable(BOARD_SDSPI_SPI_PORT, false);
        SPI_SetBaudRate(BOARD_SDSPI_SPI_PORT,CLOCK_APB1_FREQ,SDMMC_CLOCK_400KHZ);
        SPI_Enable(BOARD_SDSPI_SPI_PORT, true);
        break;
    default:
        SPI_Enable(BOARD_SDSPI_SPI_PORT, false);
        SPI_SetBaudRate(BOARD_SDSPI_SPI_PORT,CLOCK_APB1_FREQ,SD_CLOCK_25MHZ);
        SPI_Enable(BOARD_SDSPI_SPI_PORT, true);
        break;
    }
    return SDSPI_ApiRetStatus_Success;
}

uint8_t spi_xfer(uint8_t tx_dat)
{
    uint8_t rx_dat = 0u;

    while ( SPI_STATUS_TX_FULL & SPI_GetStatus(BOARD_SDSPI_SPI_PORT) )
    {}
    SPI_PutData(BOARD_SDSPI_SPI_PORT, tx_dat);

    while (0u == (SPI_STATUS_RX_DONE & SPI_GetStatus(BOARD_SDSPI_SPI_PORT)) )
    {}
    rx_dat = SPI_GetData(BOARD_SDSPI_SPI_PORT);

    return rx_dat;
}

SDSPI_ApiRetStatus_Type sdspi_spi_xfer(uint8_t *in, uint8_t *out, uint32_t len)
{
    uint8_t inbuf, outbuf;

    for (uint32_t i = 0u; i < len; i++)
    {
        inbuf = (in == NULL) ? SDSPI_DUMMY_DATA: *in++;
        outbuf = spi_xfer(inbuf);
        if (out)
        {
            *out = outbuf;
            out++;
        }
    }
    return SDSPI_ApiRetStatus_Success;
}

/* EOF. */
