389 lines
12 KiB
C
389 lines
12 KiB
C
|
/*
|
|||
|
* 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;
|
|||
|
}
|