261 lines
8.4 KiB
C
261 lines
8.4 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_hw.c
|
||
* Date: 2022-02-10 14:53:42
|
||
* LastEditTime: 2022-02-18 08:54:02
|
||
* Description: This file contains macros that can be used to access the device.
|
||
*
|
||
* 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_DATA_BARRIER();
|
||
|
||
/* 设置参数,同时触发发送命令 */
|
||
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;
|
||
} |