rt-thread/bsp/phytium/libraries/standalone/drivers/mmc/fsdmmc/fsdmmc_hw.c

258 lines
8.3 KiB
C
Raw Normal View History

/*
* 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_hw.c
* Date: 2022-02-10 14:53:42
* LastEditTime: 2022-02-18 08:54:02
* Description:  This files is for
*
* Modify History:
* Ver   Who        Date         Changes
* ----- ------     --------    --------------------------------------
* 1.0 zhugengyu 2021/12/2 init
*/
/***************************** Include Files *********************************/
#include "fassert.h"
#include "fdebug.h"
#include "fsdmmc_hw.h"
#include "fsdmmc.h"
/************************** Constant Definitions *****************************/
/**************************** Type Definitions *******************************/
/************************** Variable Definitions *****************************/
/***************** Macros (Inline Functions) Definitions *********************/
#define FSDMMC_DEBUG_TAG "FSDMMC-HW"
#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 ******************************/
/**
* @name: FSdmmcSoftwareReset
* @msg:
* @return {*}
* @param {uintptr} base_addr FSDMMC控制器基地址
* @param {int} retries
*/
FError FSdmmcSoftwareReset(uintptr base_addr, int retries)
{
FASSERT(retries > 1);
u32 reg_val;
FSDMMC_SET_BIT(base_addr, FSDMMC_SOFTWARE_RESET_REG_OFFSET, FSDMMC_SOFTWARE_RESET_SRST);
FSDMMC_CLR_BIT(base_addr, FSDMMC_SOFTWARE_RESET_REG_OFFSET, FSDMMC_SOFTWARE_RESET_SRST);
do
{
reg_val = FSDMMC_READ_REG(base_addr, FSDMMC_STATUS_REG_OFFSET);
}
while (!(reg_val & FSDMMC_STATUS_IDIE) &&
(retries-- > 0));
if (!(reg_val & FSDMMC_STATUS_IDIE) && (retries <= 0))
{
FSDMMC_ERROR("software reset timeout!!! status: 0x%x", reg_val);
return FSDMMC_ERR_TIMEOUT;
}
return FSDMMC_SUCCESS;
}
/**
* @name: FSdmmcSetCardClk
* @msg: FSDMMC的时钟
* @return {*}
* @param {uintptr} base_addr FSDMMC控制器基地址
* @param {u32} clk_freq_hz HZ
*/
FError FSdmmcSetCardClk(uintptr base_addr, u32 clk_freq_hz)
{
FSDMMC_WRITE_REG(base_addr, FSDMMC_CLOCK_DIV_REG_OFFSET, FSDMMC_CLK_DIVIDER(clk_freq_hz));
FSDMMC_WRITE_REG(base_addr, FSDMMC_SD_DRV_REG_OFFSET, FSDMMC_DEFAULT_DRV);
FSDMMC_WRITE_REG(base_addr, FSDMMC_SD_SAMP_REG_OFFSET, FSDMMC_DEFAULT_SAMP);
return FSdmmcSoftwareReset(base_addr, FSDMMC_TIMEOUT);
}
static const char *FSdmmcGetRespTypeStr(u32 hw_cmd)
{
const char *str;
switch (FSDMMC_CMD_RESP_MASK & hw_cmd)
{
case FSDMMC_CMD_NO_RESP:
str = "NONE";
break;
case FSDMMC_CMD_RESP_136_BIT:
str = "LONG";
break;
case FSDMMC_CMD_RESP_48_BIT:
str = "SHORT";
break;
case FSDMMC_CMD_RESP_48_BIT_BUSY_CHECK:
str = "SHORT CHECK BUSY";
break;
default:
FASSERT(0);
}
return str;
}
/**
* @name: FSdmmcSendPrivateCmd
* @msg:
* @return {*}
* @param {uintptr} base_addr FSDMMC控制器基地址
* @param {u32} cmd
* @param {u32} arg
*/
void FSdmmcSendPrivateCmd(uintptr base_addr, u32 cmd, u32 arg)
{
/* 清空状态寄存器 */
FSdmmcClearNormalInterruptStatus(base_addr);
FSdmmcClearErrorInterruptStatus(base_addr);
FSdmmcClearBDInterruptStatus(base_addr);
/* 设置命令 */
FSDMMC_WRITE_REG(base_addr, FSDMMC_CMD_SETTING_REG_OFFSET, cmd);
/* 设置参数,同时触发发送命令 */
FSDMMC_WRITE_REG(base_addr, FSDMMC_ARGUMENT_REG_OFFSET, FSDMMC_ARGUMENT_MASK & arg);
FSDMMC_INFO("CMD: 0x%08x ", FSDMMC_READ_REG(base_addr, FSDMMC_CMD_SETTING_REG_OFFSET));
FSDMMC_INFO("ARG: 0x%08x", FSDMMC_READ_REG(base_addr, FSDMMC_ARGUMENT_REG_OFFSET));
FSDMMC_INFO("RESP: %s", FSdmmcGetRespTypeStr(cmd));
}
/**
* @name: FSdmmcReset
* @msg: FSDMMC控制器
* @return {*}
* @param {uintptr} base_addr FSDMMC控制器基地址
*/
FError FSdmmcReset(uintptr base_addr)
{
u32 reg_val;
FError ret = FSDMMC_SUCCESS;
ret = FSdmmcSoftwareReset(base_addr, FSDMMC_TIMEOUT);
if (FSDMMC_SUCCESS != ret)
return ret;
/* set card detection */
FSDMMC_WRITE_REG(base_addr, FSDMMC_SD_SEN_REG_OFFSET, 0x0);
reg_val = FSDMMC_SEN_CREFR_VAL | FSDMMC_SEN_DEBNCE_VAL;
FSDMMC_WRITE_REG(base_addr, FSDMMC_SD_SEN_REG_OFFSET, reg_val);
/* configure cmd data timeout */
FSDMMC_WRITE_REG(base_addr, FSDMMC_TIMEOUT_CMD_REG_OFFSET, FSDMMC_CMD_TIMEOUT);
FSDMMC_WRITE_REG(base_addr, FSDMMC_TIMEOUT_DATA_REG_OFFSET, FSDMMC_DATA_TIMEOUT);
/* handle DMA cache */
FSDMMC_WRITE_REG(base_addr, FSDMMC_HDS_AXI_REG_CONF1_REG_OFFSET, FSDMMC_AXI_CONF1);
FSDMMC_WRITE_REG(base_addr, FSDMMC_HDS_AXI_REG_CONF2_REG_OFFSET, FSDMMC_AXI_CONF2);
/* set ending */
reg_val = FSDMMC_PERMDW_STD_END | FSDMMC_PERMDR_STD_END;
FSDMMC_WRITE_REG(base_addr, FSDMMC_CONTROLL_SETTING_REG_OFFSET, reg_val);
/* disable interrupt */
FSDMMC_WRITE_REG(base_addr, FSDMMC_NORMAL_INT_EN_REG_OFFSET, 0x0);
FSDMMC_WRITE_REG(base_addr, FSDMMC_ERROR_INT_EN_REG_OFFSET, 0x0);
FSDMMC_WRITE_REG(base_addr, FSDMMC_BD_ISR_EN_REG_OFFSET, 0x0);
/* clear interrupr status */
FSdmmcClearNormalInterruptStatus(base_addr);
FSdmmcClearErrorInterruptStatus(base_addr);
FSdmmcClearBDInterruptStatus(base_addr);
return ret;
}
/**
* @name: FSdmmcWaitStatus
* @msg:
* @return {*}
* @param {uintptr} base_addr FSDMMC控制器基地址
* @param {int} retries
*/
FError FSdmmcWaitStatus(uintptr base_addr, int retries)
{
FASSERT(retries > 1);
const u32 status_mask = FSDMMC_NORMAL_INT_STATUS_CC | FSDMMC_NORMAL_INT_STATUS_EI; /* 等待命令完成或者发生错误 */
u32 status;
do
{
status = status_mask & FSDMMC_READ_REG(base_addr, FSDMMC_NORMAL_INT_STATUS_REG_OFFSET);
}
while ((!status) && (retries-- > 0));
if (FSDMMC_NORMAL_INT_STATUS_EI & status)
{
FSDMMC_ERROR("error status: 0x%x, remain retries: %d", status, retries);
FSdmmcReset(base_addr);
return FSDMMC_ERR_CMD_FAILED;
}
else if (0 >= retries)
{
FSDMMC_ERROR("wait timeout!!! status 0x%x", status);
return FSDMMC_ERR_TIMEOUT;
}
return FSDMMC_SUCCESS;
}
/**
* @name: FSdmmcWaitDMAStatus
* @msg:
* @return {*}
* @param {uintptr} base_addr FSDMMC控制器基地址
* @param {boolean} read TRUE: FALSE:
* @param {int} retries
*/
FError FSdmmcWaitDMAStatus(uintptr base_addr, boolean read, int retries)
{
const u32 status_mask = read ?
(FSDMMC_BD_ISR_REG_RESPE | FSDMMC_BD_ISR_REG_DAIS) : /* 等待DMA传输完成或者发生错误 */
(FSDMMC_BD_ISR_REG_TRS | FSDMMC_BD_ISR_REG_DAIS); /* 等待DMA传输完成或者发生错误 */
u32 status;
/* 等待DMA传输完成或者发生错误 */
do
{
status = status_mask & FSDMMC_READ_REG(base_addr, FSDMMC_BD_ISR_REG_OFFSET);
}
while ((!status) && (retries-- > 0));
if (status & FSDMMC_BD_ISR_REG_DAIS)
{
FSDMMC_ERROR("BD Data error when %s blk!", read ? "read" : "write");
FSdmmcReset(base_addr);
return FSDMMC_ERR_DATA_FAILED;
}
else if (0 >= retries)
{
FSDMMC_ERROR("BD Data timeout !!!");
return FSDMMC_ERR_TIMEOUT;
}
return FSDMMC_SUCCESS;
}