4
0
mirror of https://github.com/RT-Thread/rt-thread.git synced 2025-02-25 16:37:06 +08:00

374 lines
10 KiB
C
Raw Normal View History

2023-08-23 09:11:03 +08:00
#include "xil_exception.h"
#include "xsdps.h"
#include <stdio.h>
#include "xparameters.h"
#include "rtthread.h"
#include <rtdevice.h>
#define DBG_TAG "drv.mmc"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
#define EMMC_BLOCK_SIZE (512)
extern u16 TransferMode;
extern s32 XSdPs_CmdTransfer(XSdPs *InstancePtr, u32 Cmd, u32 Arg, u32 BlkCnt);
extern void XSdPs_SetupADMA2DescTbl(XSdPs *InstancePtr, u32 BlkCnt, const u8 *Buff);
// replace XSdPs_ReadPolled, using `rt_thread_mdelay`
s32 XSdPs_ReadPolled_rtt(XSdPs *InstancePtr, u32 Arg, u32 BlkCnt, u8 *Buff)
{
s32 Status;
u32 PresentStateReg;
u32 StatusReg;
if ((InstancePtr->HC_Version != XSDPS_HC_SPEC_V3) ||
((InstancePtr->Host_Caps & XSDPS_CAPS_SLOT_TYPE_MASK)
!= XSDPS_CAPS_EMB_SLOT)) {
if(InstancePtr->Config.CardDetect != 0U) {
/* Check status to ensure card is initialized */
PresentStateReg = XSdPs_ReadReg(InstancePtr->Config.BaseAddress,
XSDPS_PRES_STATE_OFFSET);
if ((PresentStateReg & XSDPS_PSR_CARD_INSRT_MASK) == 0x0U) {
Status = XST_FAILURE;
goto RETURN_PATH;
}
}
}
/* Set block size to 512 if not already set */
if( XSdPs_ReadReg(InstancePtr->Config.BaseAddress,
XSDPS_BLK_SIZE_OFFSET) != XSDPS_BLK_SIZE_512_MASK ) {
Status = XSdPs_SetBlkSize(InstancePtr,
XSDPS_BLK_SIZE_512_MASK);
if (Status != XST_SUCCESS) {
Status = XST_FAILURE;
goto RETURN_PATH;
}
}
XSdPs_SetupADMA2DescTbl(InstancePtr, BlkCnt, Buff);
TransferMode = XSDPS_TM_AUTO_CMD12_EN_MASK |
XSDPS_TM_BLK_CNT_EN_MASK | XSDPS_TM_DAT_DIR_SEL_MASK |
XSDPS_TM_DMA_EN_MASK | XSDPS_TM_MUL_SIN_BLK_SEL_MASK;
if (InstancePtr->Config.IsCacheCoherent == 0) {
Xil_DCacheInvalidateRange((INTPTR)Buff,
BlkCnt * XSDPS_BLK_SIZE_512_MASK);
}
/* Send block read command */
Status = XSdPs_CmdTransfer(InstancePtr, CMD18, Arg, BlkCnt);
if (Status != XST_SUCCESS) {
Status = XST_FAILURE;
goto RETURN_PATH;
}
/* Check for transfer complete */
do {
StatusReg = XSdPs_ReadReg16(InstancePtr->Config.BaseAddress,
XSDPS_NORM_INTR_STS_OFFSET);
if ((StatusReg & XSDPS_INTR_ERR_MASK) != 0U) {
/* Write to clear error bits */
XSdPs_WriteReg16(InstancePtr->Config.BaseAddress,
XSDPS_ERR_INTR_STS_OFFSET,
XSDPS_ERROR_INTR_ALL_MASK);
Status = XST_FAILURE;
goto RETURN_PATH;
}
// delay 1ms
if ((StatusReg & XSDPS_INTR_TC_MASK) == 0U)
rt_thread_mdelay(1);
} while((StatusReg & XSDPS_INTR_TC_MASK) == 0U);
/* Write to clear bit */
XSdPs_WriteReg16(InstancePtr->Config.BaseAddress,
XSDPS_NORM_INTR_STS_OFFSET, XSDPS_INTR_TC_MASK);
Status = (s32)XSdPs_ReadReg(InstancePtr->Config.BaseAddress,
XSDPS_RESP0_OFFSET);
Status = XST_SUCCESS;
RETURN_PATH:
return Status;
}
// replace XSdPs_WritePolled, using rt_thread_mdelay
s32 XSdPs_WritePolled_rtt(XSdPs *InstancePtr, u32 Arg, u32 BlkCnt, const u8 *Buff)
{
s32 Status;
u32 PresentStateReg;
u32 StatusReg;
if ((InstancePtr->HC_Version != XSDPS_HC_SPEC_V3) ||
((InstancePtr->Host_Caps & XSDPS_CAPS_SLOT_TYPE_MASK)
!= XSDPS_CAPS_EMB_SLOT)) {
if(InstancePtr->Config.CardDetect != 0U) {
/* Check status to ensure card is initialized */
PresentStateReg = XSdPs_ReadReg(InstancePtr->Config.BaseAddress,
XSDPS_PRES_STATE_OFFSET);
if ((PresentStateReg & XSDPS_PSR_CARD_INSRT_MASK) == 0x0U) {
Status = XST_FAILURE;
goto RETURN_PATH;
}
}
}
/* Set block size to 512 if not already set */
if( XSdPs_ReadReg(InstancePtr->Config.BaseAddress,
XSDPS_BLK_SIZE_OFFSET) != XSDPS_BLK_SIZE_512_MASK ) {
Status = XSdPs_SetBlkSize(InstancePtr,
XSDPS_BLK_SIZE_512_MASK);
if (Status != XST_SUCCESS) {
Status = XST_FAILURE;
goto RETURN_PATH;
}
}
XSdPs_SetupADMA2DescTbl(InstancePtr, BlkCnt, Buff);
if (InstancePtr->Config.IsCacheCoherent == 0) {
Xil_DCacheFlushRange((INTPTR)Buff,
BlkCnt * XSDPS_BLK_SIZE_512_MASK);
}
TransferMode = XSDPS_TM_AUTO_CMD12_EN_MASK |
XSDPS_TM_BLK_CNT_EN_MASK |
XSDPS_TM_MUL_SIN_BLK_SEL_MASK | XSDPS_TM_DMA_EN_MASK;
/* Send block write command */
Status = XSdPs_CmdTransfer(InstancePtr, CMD25, Arg, BlkCnt);
if (Status != XST_SUCCESS) {
Status = XST_FAILURE;
goto RETURN_PATH;
}
/*
* Check for transfer complete
* Polling for response for now
*/
do {
StatusReg = XSdPs_ReadReg16(InstancePtr->Config.BaseAddress,
XSDPS_NORM_INTR_STS_OFFSET);
if ((StatusReg & XSDPS_INTR_ERR_MASK) != 0U) {
/* Write to clear error bits */
XSdPs_WriteReg16(InstancePtr->Config.BaseAddress,
XSDPS_ERR_INTR_STS_OFFSET,
XSDPS_ERROR_INTR_ALL_MASK);
Status = XST_FAILURE;
goto RETURN_PATH;
}
if ((StatusReg & XSDPS_INTR_TC_MASK) == 0U)
rt_thread_mdelay(1);
} while((StatusReg & XSDPS_INTR_TC_MASK) == 0U);
/* Write to clear bit */
XSdPs_WriteReg16(InstancePtr->Config.BaseAddress,
XSDPS_NORM_INTR_STS_OFFSET, XSDPS_INTR_TC_MASK);
Status = XST_SUCCESS;
RETURN_PATH:
return Status;
}
struct rt_hw_emmc_device {
struct rt_device dev;
XSdPs emmc;
XSdPs_Config *cfg;
struct rt_mutex lock;
u16 dev_id;
};
#ifdef BSP_USING_SDIO0
static struct rt_hw_emmc_device emmc0 = {
.dev_id = XPAR_PS7_SD_0_DEVICE_ID,
};
#endif
#ifdef BSP_USING_SDIO1
static struct rt_hw_emmc_device emmc1 = {
.dev_id = XPAR_PS7_SD_1_DEVICE_ID,
};
#endif
#define __INIT_CHECK_CFG(cfg) \
if (Status != XST_SUCCESS) \
{ \
rt_kprintf("EMMC Config '%s' failed: err: %d\n", cfg, Status); \
return -RT_EIO; \
}
static rt_err_t rt_sdcard_init(rt_device_t dev)
{
RT_ASSERT(dev);
struct rt_hw_emmc_device *pdev = rt_container_of(dev, struct rt_hw_emmc_device, dev);
RT_ASSERT(pdev != RT_NULL);
rt_mutex_take(&pdev->lock, RT_WAITING_FOREVER);
if (pdev->cfg != RT_NULL)
{
LOG_I("init '%s' success", pdev->dev.parent.name);
rt_mutex_release(&pdev->lock);
return RT_EOK;
}
pdev->cfg = XSdPs_LookupConfig(pdev->dev_id);
RT_ASSERT(pdev->cfg != RT_NULL);
s32 Status = XSdPs_CfgInitialize(&pdev->emmc, pdev->cfg, pdev->cfg->BaseAddress);
__INIT_CHECK_CFG("cfg");
Status = XSdPs_CardInitialize(&pdev->emmc);
__INIT_CHECK_CFG("init");
LOG_I("init '%s' success", pdev->dev.parent.name);
rt_mutex_release(&pdev->lock);
return RT_EOK;
}
rt_inline rt_uint32_t _emmc_start_addr(struct rt_hw_emmc_device *pdev)
{
return 0;
}
rt_inline rt_uint32_t _emmc_size(struct rt_hw_emmc_device *pdev)
{
return pdev->emmc.SectorCount;
}
rt_inline rt_uint32_t _emmc_end_addr(struct rt_hw_emmc_device *pdev)
{
return pdev->emmc.SectorCount;
}
static rt_err_t rt_sdcard_open(rt_device_t dev, rt_uint16_t oflag)
{
return RT_EOK;
}
static rt_err_t rt_sdcard_close(rt_device_t dev)
{
return RT_EOK;
}
static rt_ssize_t rt_sdcard_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
{
s32 status;
RT_ASSERT(dev);
struct rt_hw_emmc_device *pdev = rt_container_of(dev, struct rt_hw_emmc_device, dev);
RT_ASSERT(pdev != RT_NULL);
pos += _emmc_start_addr(pdev);
if (size + pos >= _emmc_end_addr(pdev))
{
LOG_E("addr out of ranger: %d %d", pos, size);
return 0;
}
rt_mutex_take(&pdev->lock, RT_WAITING_FOREVER);
status = XSdPs_ReadPolled_rtt(&pdev->emmc, pos, size, buffer);
rt_mutex_release(&pdev->lock);
if (status == XST_SUCCESS)
return size;
rt_kprintf("sdcard read failed: %d\n", status);
return 0;
}
static rt_uint8_t sdio_write_buf[2][EMMC_BLOCK_SIZE] __attribute__((aligned(32)));
static rt_ssize_t rt_sdcard_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
{
s32 status;
rt_ssize_t rt = 0;
RT_ASSERT(dev);
struct rt_hw_emmc_device *pdev = rt_container_of(dev, struct rt_hw_emmc_device, dev);
RT_ASSERT(pdev != RT_NULL);
pos += _emmc_start_addr(pdev);
if (size + pos >= _emmc_end_addr(pdev))
{
LOG_E("addr out of ranger: %d %d", pos, size);
return 0;
}
rt_mutex_take(&pdev->lock, RT_WAITING_FOREVER);
for (int i = 0; i < size; i++)
{
rt_memcpy(sdio_write_buf[pdev->dev_id], ((rt_uint8_t *)buffer) + EMMC_BLOCK_SIZE * i, EMMC_BLOCK_SIZE);
status = XSdPs_WritePolled_rtt(&pdev->emmc, pos + i, 1, sdio_write_buf[pdev->dev_id]);
if (status != XST_SUCCESS)
{
rt_kprintf("sdcard write failed: %d\n", status);
break;
}
rt++;
}
rt_mutex_release(&pdev->lock);
return rt;
}
static rt_err_t rt_sdcard_control(rt_device_t dev, int cmd, void *args)
{
RT_ASSERT(dev);
struct rt_hw_emmc_device *pdev = rt_container_of(dev, struct rt_hw_emmc_device, dev);
RT_ASSERT(pdev != RT_NULL);
if (cmd == RT_DEVICE_CTRL_BLK_GETGEOME)
{
struct rt_device_blk_geometry *geometry;
geometry = (struct rt_device_blk_geometry *)args;
if (geometry == RT_NULL)
return -RT_ERROR;
geometry->bytes_per_sector = EMMC_BLOCK_SIZE;
geometry->block_size = EMMC_BLOCK_SIZE;
geometry->sector_count = _emmc_size(pdev);
}
return RT_EOK;
}
static void _init_emmc_dev(struct rt_hw_emmc_device *pdev, const char *name)
{
rt_mutex_init(&pdev->lock, name, RT_IPC_FLAG_PRIO);
pdev->dev.type = RT_Device_Class_Block;
pdev->dev.init = rt_sdcard_init;
pdev->dev.open = rt_sdcard_open;
pdev->dev.close = rt_sdcard_close;
pdev->dev.read = rt_sdcard_read;
pdev->dev.write = rt_sdcard_write;
pdev->dev.control = rt_sdcard_control;
pdev->dev.user_data = RT_NULL;
rt_device_register(&pdev->dev, name, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_STANDALONE);
}
int rt_hw_emmc_init(void)
{
#ifdef BSP_USING_SDIO0
_init_emmc_dev(&emmc0, "sd0");
#endif
#ifdef BSP_USING_SDIO1
_init_emmc_dev(&emmc1, "sd1");
#endif
return RT_EOK;
}
INIT_BOARD_EXPORT(rt_hw_emmc_init);