/*
 * Copyright : (C) 2022 Phytium Information Technology, Inc.
 * All Rights Reserved.
 *
 * This program is OPEN SOURCE software: you can redistribute it and/or modify it
 * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd,
 * either version 1.0 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the Phytium Public License for more details.
 *
 *
 * FilePath: fsdmmc.c
 * Date: 2022-02-10 14:53:42
 * LastEditTime: 2022-02-18 08:55:23
 * Description:  This files is for
 *
 * Modify History:
 *  Ver   Who        Date         Changes
 * ----- ------     --------    --------------------------------------
 * 1.0   zhugengyu  2021/12/2    init
 */

/***************************** Include Files *********************************/
#include <string.h>

#include "fassert.h"
#include "fio.h"
#include "ferror_code.h"
#include "ftypes.h"
#include "fdebug.h"

#include "fcache.h"
#include "fsleep.h"

#include "fsdmmc_hw.h"
#include "fsdmmc.h"
#include "fsdmmc_dma.h"

/************************** Constant Definitions *****************************/

/**************************** Type Definitions *******************************/

/***************** Macros (Inline Functions) Definitions *********************/
#define FSDMMC_DEBUG_TAG "FSDMMC"
#define FSDMMC_ERROR(format, ...)   FT_DEBUG_PRINT_E(FSDMMC_DEBUG_TAG, format, ##__VA_ARGS__)
#define FSDMMC_WARN(format, ...)    FT_DEBUG_PRINT_W(FSDMMC_DEBUG_TAG, format, ##__VA_ARGS__)
#define FSDMMC_INFO(format, ...)    FT_DEBUG_PRINT_I(FSDMMC_DEBUG_TAG, format, ##__VA_ARGS__)
#define FSDMMC_DEBUG(format, ...)   FT_DEBUG_PRINT_D(FSDMMC_DEBUG_TAG, format, ##__VA_ARGS__)

/************************** Function Prototypes ******************************/

/************************** Variable Definitions *****************************/

/*****************************************************************************/

/* 此文件主要为了完成用户对外接口,用户可以使用这些接口直接开始工作 */

/* - 包括用户API的定义和实现
   - 同时包含必要的OPTION方法,方便用户进行配置
   - 如果驱动可以直接进行I/O操作,在此源文件下可以将API 进行实现 */

/**
 * @name: FSdmmcCfgInitialize
 * @msg: 初始化FSDMMC控制器, 使之可以使用
 * @return {FError} 驱动初始化的错误码信息,FSDMMC_SUCCESS 表示初始化成功,其它返回值表示初始化失败
 * @param {FSdmmc} *instance_p FSDMMC驱动控制数据
 * @param {FSdmmcConfig} *input_config_p FSDMMC用户输入配置
 * @note 输入配置通过FSdmmcLookupConfig获取,用户按照需要修改后传入此函数
 */
FError FSdmmcCfgInitialize(FSdmmc *instance_p, const FSdmmcConfig *input_config_p)
{
    FASSERT(instance_p && input_config_p);
    uintptr base_addr;
    FError ret = FSDMMC_SUCCESS;

    /*
    * If the device is started, disallow the initialize and return a Status
    * indicating it is started.  This allows the user to de-initialize the device
    * and reinitialize, but prevents a user from inadvertently
    * initializing.
    */
    if (FT_COMPONENT_IS_READY == instance_p->is_ready)
    {
        FSDMMC_WARN("device is already initialized!!!");
    }

    /*
    * Set default values and configuration data, including setting the
    * callback handlers to stubs  so the system will not crash should the
    * application not assign its own callbacks.
     */
    FSdmmcDeInitialize(instance_p);

    instance_p->config = *input_config_p;
    base_addr = instance_p->config.base_addr;

    /*
    * Check if card exists
    */
    if (!FSdmmcCheckIfCardExists(base_addr))
    {
        FSDMMC_ERROR("storage device not found !!! 0x%x", base_addr);
        return FSDMMC_ERR_CARD_NO_FOUND;
    }

    /*
    * Reset the device.
    */
    ret = FSdmmcReset(base_addr);
    if (FSDMMC_SUCCESS == ret)
        instance_p->is_ready = FT_COMPONENT_IS_READY;

    return ret;
}

/**
 * @name: FSdmmcDeInitialize
 * @msg: 去使能FSDMMC控制器, 清零实例数据
 * @return {*}
 * @param {FSdmmc} *instance_p FSDMMC驱动控制数据
 */
void FSdmmcDeInitialize(FSdmmc *instance_p)
{
    FASSERT(instance_p);

    instance_p->is_ready = 0;
    memset(instance_p, 0, sizeof(*instance_p));

    return;
}

/**
 * @name: FSdmmcMakeRawCmd
 * @msg: 组装生成待发送的命令
 * @return {*}
 * @param {FSdmmcCmd} *cmd_p 命令控制数据
 */
u32 FSdmmcMakeRawCmd(FSdmmcCmd *cmd_p)
{
    FASSERT(cmd_p);
    u32 raw_cmd = 0;

    /*
     * rawcmd :
     *   trty << 14 | opcode << 8 | cmdw << 6 | cice << 4 | crce << 3 | resp
     */
    raw_cmd |= FSDMMC_CMD_SETTING_CMDI(cmd_p->cmdidx);

    if (cmd_p->flag & FSDMMC_CMD_FLAG_ADTC)
        raw_cmd |= FSDMMC_CMD_SETTING_TRTY(0b10); /* adtc指令 */

    if (0 == (cmd_p->flag & FSDMMC_CMD_FLAG_EXP_RESP))
        raw_cmd |= FSDMMC_CMD_NO_RESP;
    else if (cmd_p->flag & FSDMMC_CMD_FLAG_EXP_LONG_RESP)
        raw_cmd |= FSDMMC_CMD_RESP_136_BIT;
    else
        raw_cmd |= FSDMMC_CMD_RESP_48_BIT;

    return raw_cmd;
}

/**
 * @name: FSdmmcWaitCmdEnd
 * @msg: 阻塞等待命令发送完成,获取命令返回的响应
 * @return {FError} FSDMMC_SUCCESS表示命令发送成功,其它表示命令发送失败
 * @param {uintptr} base_addr FSDMMC控制器基地址
 * @param {FSdmmcCmd} *cmd_p 命令控制数据
 */
static FError FSdmmcWaitCmdEnd(uintptr base_addr, FSdmmcCmd *cmd_p)
{
    FASSERT(cmd_p);
    FError ret = FSDMMC_SUCCESS;

    ret = FSdmmcWaitStatus(base_addr, FSDMMC_TIMEOUT);
    if (FSDMMC_SUCCESS != ret)
        return ret;

    if (cmd_p->flag & FSDMMC_CMD_FLAG_EXP_RESP)
    {
        if (cmd_p->flag & FSDMMC_CMD_FLAG_EXP_LONG_RESP)
        {
            cmd_p->response[0] = FSDMMC_READ_REG(base_addr, FSDMMC_CMD_RESP_1_REG_OFFSET);
            cmd_p->response[1] = FSDMMC_READ_REG(base_addr, FSDMMC_CMD_RESP_2_REG_OFFSET);
            cmd_p->response[2] = FSDMMC_READ_REG(base_addr, FSDMMC_CMD_RESP_3_REG_OFFSET);
            cmd_p->response[3] = FSDMMC_READ_REG(base_addr, FSDMMC_CMD_RESP_4_REG_OFFSET);
        }
        else
        {
            cmd_p->response[0] = FSDMMC_READ_REG(base_addr, FSDMMC_CMD_RESP_1_REG_OFFSET);
            cmd_p->response[1] = 0;
            cmd_p->response[2] = 0;
            cmd_p->response[3] = 0;
        }
    }

    FSDMMC_INFO("get cmd resp: 0x%x:0x%x:0x%x:0x%x",
                cmd_p->response[0],
                cmd_p->response[1],
                cmd_p->response[2],
                cmd_p->response[3]);

    return FSDMMC_SUCCESS;
}

/**
 * @name: FSdmmcSendCmd
 * @msg: 发送命令
 * @return {FError} FSDMMC_SUCCESS表示命令发送成功,其它表示命令发送失败
 * @param {uintptr} base_addr FSDMMC控制器基地址
 * @param {FSdmmcCmd} *cmd_p 命令控制数据
 */
void FSdmmcSendCmd(uintptr base_addr, FSdmmcCmd *cmd_p)
{
    FASSERT(cmd_p);
    u32 raw_cmd = FSdmmcMakeRawCmd(cmd_p);

    FSdmmcSendPrivateCmd(base_addr, raw_cmd, cmd_p->cmdarg);
}

/**
 * @name: FSdmmcTransferCmdPoll
 * @msg: 通过FSDMMC轮询方式发送命令,阻塞等待命令返回
 * @return {*}
 * @param {uintptr} base_addr FSDMMC控制器基地址
 * @param {FSdmmcCmd} *cmd_p 命令控制数据
 */
static FError FSdmmcTransferCmdPoll(uintptr base_addr, FSdmmcCmd *cmd_p)
{
    FASSERT(cmd_p);
    FSdmmcSendCmd(base_addr, cmd_p);
    return FSdmmcWaitCmdEnd(base_addr, cmd_p);
}

/**
 * @name: FSdmmcSendAdtcCmd
 * @msg: 发送ADTC命令
 * @return {*}
 * @param {uintptr} base_addr FSDMMC控制器基地址
 * @param {FSdmmcCmd} *cmd_p 命令控制数据
 */
static void FSdmmcSendAdtcCmd(uintptr base_addr, FSdmmcCmd *cmd_p)
{
    FASSERT(cmd_p);
    u32 raw_cmd = FSdmmcMakeRawCmd(cmd_p);

    FSdmmcClearNormalInterruptStatus(base_addr);
    raw_cmd |= FSDMMC_CMD_SETTING_TRTY(0b10); /* adtc指令 */
    FSDMMC_WRITE_REG(base_addr, FSDMMC_CMD_SETTING_REG_OFFSET, raw_cmd);
    return;
}

/**
 * @name: FSdmmcSendData
 * @msg: 发送数据
 * @return {*}
 * @param {uintptr} base_addr FSDMMC控制器基地址
 * @param {boolean} read TREU: 读数据 FALSE: 写数据
 * @param {FSdmmcCmd} *cmd_p 命令控制数据
 */
FError FSdmmcSendData(uintptr base_addr, boolean read, FSdmmcCmd *cmd_p)
{
    FASSERT(cmd_p);
    FSdmmcData *dat_p = cmd_p->data_p;
    u32 card_addr;
    u32 blk_cnt;
    FError ret = FSDMMC_SUCCESS;

    if ((dat_p->datalen >= FSDMMC_DMA_ADDR_ALIGN) && (dat_p->datalen % FSDMMC_DMA_ADDR_ALIGN != 0))
    {
        FSDMMC_ERROR("invalid size: total = %d ", dat_p->datalen);
        return FSDMMC_ERR_INVALID_BUF;
    }

    if (((uintptr)(dat_p->buf) % FSDMMC_DMA_ADDR_ALIGN) != 0)
    {
        FSDMMC_ERROR("buffer %p can not be used for DMA", dat_p->buf);
        return FSDMMC_ERR_INVALID_BUF;
    }

    card_addr = cmd_p->cmdarg;
    blk_cnt = dat_p->datalen / dat_p->blksz;
    if (dat_p->datalen % dat_p->blksz)
        blk_cnt++;

    FSDMMC_INFO("data len: %d, card addr: 0x%x, blk cnt: %d, is %s",
                dat_p->datalen, card_addr, blk_cnt, read ? "read" : "write");

    if (read)
    {
        if ((cmd_p->flag & FSDMMC_CMD_FLAG_ADTC) && (dat_p->blksz > dat_p->datalen))
        {
            FSdmmcSendAdtcCmd(base_addr, cmd_p);
        }
        /* read data */
        FSdmmcSetReadDMA(base_addr, (uintptr)card_addr, blk_cnt, dat_p->buf);
    }
    else
    {
        /* invalidate write buf */
        FCacheDCacheInvalidateRange((uintptr)dat_p->buf, dat_p->datalen);

        /* write data */
        FSdmmcSetWriteDMA(base_addr, (uintptr)card_addr, blk_cnt, dat_p->buf);
    }

    return ret;
}

/**
 * @name: FSdmmcTransferDataPoll
 * @msg: 通过FSDMMC轮询方式发送数据,阻塞等待数据返回
 * @return {*}
 * @param {uintptr} base_addr FSDMMC控制器基地址
 * @param {FSdmmcCmd} *cmd_p 待发送数据
 */
static FError FSdmmcTransferDataPoll(uintptr base_addr, FSdmmcCmd *cmd_p)
{
    FASSERT(cmd_p);
    FError ret = FSDMMC_SUCCESS;
    FSdmmcData *dat_p = cmd_p->data_p;
    const boolean read = (FSDMMC_CMD_FLAG_READ_DATA == (cmd_p->flag & FSDMMC_CMD_FLAG_READ_DATA));

    ret = FSdmmcSendData(base_addr, read, cmd_p);
    if (FSDMMC_SUCCESS != ret)
        return ret;

    ret = FSdmmcWaitCmdEnd(base_addr, cmd_p);
    if (FSDMMC_SUCCESS != ret)
        return ret;

    ret = FSdmmcWaitDMAStatus(base_addr, read, FSDMMC_TIMEOUT);
    if (FSDMMC_SUCCESS != ret)
        return ret;

    FCacheDCacheInvalidateRange((uintptr)dat_p->buf, dat_p->datalen);
    return ret;
}

/**
 * @name: FSdmmcPollTransfer
 * @msg: 通过FSDMMC轮询方式发送/接收数据和命令
 * @return {FError} 驱动初始化的错误码信息,FSDMMC_SUCCESS 表示发送/接收成功,其它返回值表示发送/接收失败
 * @param {FSdmmc} *instance_p FSDMMC驱动控制数据
 * @param {FSdmmcCmd} *cmd_data_p FSDMMC数据和命令
 * @note FSDMMC控制器初始化后才能调用此函数
 */
FError FSdmmcPollTransfer(FSdmmc *instance_p, FSdmmcCmd *cmd_data_p)
{
    FASSERT(instance_p && cmd_data_p);
    uintptr base_addr = instance_p->config.base_addr;
    FError ret = FSDMMC_SUCCESS;

    if (FALSE == FSdmmcCheckIfCardExists(base_addr))
    {
        FSDMMC_ERROR("card not found !!! fsdio ctrl base 0x%x", base_addr);
        return FSDMMC_ERR_CARD_NO_FOUND;
    }

    if (cmd_data_p->flag & FSDMMC_CMD_FLAG_EXP_DATA)
    {
        /* transfer data */
        FSDMMC_INFO("====DATA [%d] START: buf: %p=====", cmd_data_p->cmdidx, cmd_data_p->data_p->buf);
        ret = FSdmmcTransferDataPoll(base_addr, cmd_data_p);
        if (FSDMMC_SUCCESS != ret)
        {
            FSDMMC_ERROR("trans data failed 0x%x", ret);
            return ret;
        }

        FSDMMC_INFO("====DATA [%d] END 0x%x=====", cmd_data_p->cmdidx, ret);
    }
    else
    {
        /* transfer command */
        FSDMMC_INFO("=====CMD [%d] START=====", cmd_data_p->cmdidx);
        ret = FSdmmcTransferCmdPoll(base_addr, cmd_data_p);
        if (FSDMMC_SUCCESS != ret)
        {
            FSDMMC_ERROR("send cmd failed 0x%x", ret);
            return ret;
        }

        FSDMMC_INFO("=====CMD [%d] END: 0x%x=====", cmd_data_p->cmdidx, ret);
    }

    return ret;
}