380 lines
12 KiB
C
380 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: fddma.c
|
||
* Date: 2022-02-10 14:53:42
|
||
* LastEditTime: 2022-02-18 08:24:47
|
||
* Description: This files is for ddma interface implementation
|
||
*
|
||
* Modify History:
|
||
* Ver Who Date Changes
|
||
* ----- ------ -------- --------------------------------------
|
||
* 1.0 Zhugengyu 2022/5/13 init commit
|
||
*/
|
||
|
||
/***************************** Include Files *********************************/
|
||
#include <string.h>
|
||
|
||
#include "fkernel.h"
|
||
#include "fparameters.h"
|
||
#include "fassert.h"
|
||
#include "fdebug.h"
|
||
|
||
#include "fddma_hw.h"
|
||
#include "fddma.h"
|
||
|
||
/************************** Constant Definitions *****************************/
|
||
|
||
/**************************** Type Definitions *******************************/
|
||
|
||
/************************** Variable Definitions *****************************/
|
||
|
||
/***************** Macros (Inline Functions) Definitions *********************/
|
||
#define FDDMA_DEBUG_TAG "DDMA"
|
||
#define FDDMA_ERROR(format, ...) FT_DEBUG_PRINT_E(FDDMA_DEBUG_TAG, format, ##__VA_ARGS__)
|
||
#define FDDMA_WARN(format, ...) FT_DEBUG_PRINT_W(FDDMA_DEBUG_TAG, format, ##__VA_ARGS__)
|
||
#define FDDMA_INFO(format, ...) FT_DEBUG_PRINT_I(FDDMA_DEBUG_TAG, format, ##__VA_ARGS__)
|
||
#define FDDMA_DEBUG(format, ...) FT_DEBUG_PRINT_D(FDDMA_DEBUG_TAG, format, ##__VA_ARGS__)
|
||
|
||
/************************** Function Prototypes ******************************/
|
||
static FError FDdmaReset(FDdma *const instance);
|
||
|
||
/****************************************************************************/
|
||
/**
|
||
* @name: FDdmaCfgInitialization
|
||
* @msg: 初始化DDMA控制器
|
||
* @return {FError} FDDMA_SUCCESS表示初始化成功,其它返回值表示初始化失败
|
||
* @param {FDdma} *instance, DDMA控制器实例
|
||
* @param {FDdmaConfig} *input_config, DDMA控制器配置
|
||
*/
|
||
FError FDdmaCfgInitialization(FDdma *const instance, const FDdmaConfig *input_config)
|
||
{
|
||
FASSERT(instance && input_config);
|
||
uintptr base_addr = input_config->base_addr;
|
||
FError ret = FDDMA_SUCCESS;
|
||
|
||
if (FT_COMPONENT_IS_READY == instance->is_ready)
|
||
{
|
||
FDDMA_WARN("device is already initialized!!!");
|
||
}
|
||
|
||
FDdmaDeInitialization(instance);
|
||
instance->config = *input_config;
|
||
|
||
ret = FDdmaReset(instance);
|
||
if (FDDMA_SUCCESS == ret)
|
||
{
|
||
instance->is_ready = FT_COMPONENT_IS_READY;
|
||
FDDMA_INFO("ddma@0x%x init success !!!", base_addr);
|
||
}
|
||
|
||
return ret;
|
||
}
|
||
|
||
/**
|
||
* @name: FDdmaStart
|
||
* @msg: 启动DDMA控制器,开始传输
|
||
* @return {FError} FDDMA_SUCCESS表示启动成功,其它返回值表示启动失败
|
||
* @param {FDdma} *instance, DDMA控制器实例
|
||
*/
|
||
FError FDdmaStart(FDdma *const instance)
|
||
{
|
||
FASSERT(instance);
|
||
FError ret = FDDMA_SUCCESS;
|
||
uintptr base_addr = instance->config.base_addr;
|
||
|
||
if (FT_COMPONENT_IS_READY != instance->is_ready)
|
||
{
|
||
FDDMA_ERROR("dma instance not init !!!");
|
||
return FDDMA_ERR_NOT_INIT;
|
||
}
|
||
|
||
FDdmaEnableGlobalIrq(base_addr); /* enable ddma irq */
|
||
FDdmaEnable(base_addr);
|
||
return FDDMA_SUCCESS;
|
||
}
|
||
|
||
/**
|
||
* @name: FDdmaStop
|
||
* @msg: 停止DDMA控制器
|
||
* @return {FError} FDDMA_SUCCESS表示停止成功,其它返回值表示停止失败
|
||
* @param {FDdma} *instance, DDMA控制器实例
|
||
*/
|
||
FError FDdmaStop(FDdma *const instance)
|
||
{
|
||
FASSERT(instance);
|
||
FError ret = FDDMA_SUCCESS;
|
||
uintptr base_addr = instance->config.base_addr;
|
||
|
||
if (FT_COMPONENT_IS_READY != instance->is_ready)
|
||
{
|
||
FDDMA_ERROR("dma instance not init !!!");
|
||
return FDDMA_ERR_NOT_INIT;
|
||
}
|
||
|
||
FDdmaDisableGlobalIrq(base_addr); /* enable ddma irq */
|
||
FDdmaDisable(base_addr);
|
||
return FDDMA_SUCCESS;
|
||
}
|
||
|
||
/**
|
||
* @name: FDdmaDeInitialization
|
||
* @msg: 去初始化DDMA控制器
|
||
* @return {无}
|
||
* @param {FDdma} *instance, DDMA控制器实例
|
||
*/
|
||
void FDdmaDeInitialization(FDdma *const instance)
|
||
{
|
||
FASSERT(instance);
|
||
u32 chan;
|
||
|
||
for (chan = 0; chan < FDDMA_NUM_OF_CHAN; chan++)
|
||
{
|
||
if (instance->bind_status & BIT(chan))
|
||
{
|
||
FDDMA_WARN("channel %d has not been unbind", chan);
|
||
}
|
||
}
|
||
|
||
memset(instance, 0, sizeof(*instance));
|
||
return;
|
||
}
|
||
|
||
/**
|
||
* @name: FDdmaAllocateChan
|
||
* @msg: 按照配置分配DDMA通道
|
||
* @return {FError} FDDMA_SUCCESS表示分配成功,其它返回值表示分配失败
|
||
* @param {FDdma} *instance, DDMA控制器实例
|
||
* @param {FDdmaChan} *dma_chan, DDMA通道实例
|
||
* @param {FDdmaChanConfig} *dma_chan_config, DDMA通道配置
|
||
*/
|
||
FError FDdmaAllocateChan(FDdma *const instance, FDdmaChan *const dma_chan, const FDdmaChanConfig *dma_chan_config)
|
||
{
|
||
FASSERT(instance && dma_chan && dma_chan_config);
|
||
FError ret = FDDMA_SUCCESS;
|
||
const FDdmaChanIndex chan_idx = dma_chan_config->id;
|
||
u32 reg_val;
|
||
uintptr base_addr = instance->config.base_addr;
|
||
|
||
if (FT_COMPONENT_IS_READY != instance->is_ready)
|
||
{
|
||
FDDMA_ERROR("dma instance not init !!!");
|
||
return FDDMA_ERR_NOT_INIT;
|
||
}
|
||
|
||
if ((TRUE == dma_chan->is_used) || (BIT(chan_idx) & instance->bind_status))
|
||
{
|
||
FDDMA_ERROR("chan-%d is already is use !!!", chan_idx);
|
||
return FDDMA_ERR_CHAN_BINDED;
|
||
}
|
||
|
||
if (FDdmaIsChanRunning(base_addr, chan_idx))
|
||
{
|
||
FDDMA_ERROR("chan-%d is already running !!!", chan_idx);
|
||
return FDDMA_ERR_CHAN_BINDED;
|
||
}
|
||
|
||
if (dma_chan_config->ddr_addr % FDDMA_DDR_ADDR_ALIGMENT)
|
||
{
|
||
FDDMA_ERROR("ddr addr 0x%x must align with %d bytes",
|
||
dma_chan_config->ddr_addr, FDDMA_DDR_ADDR_ALIGMENT);
|
||
return FDDMA_ERR_INVALID_DDR_ADDR;
|
||
}
|
||
|
||
if ((FDDMA_MAX_TRANSFER_LEN < dma_chan_config->trans_len) ||
|
||
(FDDMA_MIN_TRANSFER_LEN > dma_chan_config->trans_len) ||
|
||
(0 != dma_chan_config->trans_len % FDDMA_MIN_TRANSFER_LEN))
|
||
{
|
||
FDDMA_ERROR("invalid transfer size %d Bytes !!!", dma_chan_config->trans_len);
|
||
return FDDMA_ERR_INVALID_TRANS_SIZE;
|
||
}
|
||
|
||
dma_chan->dma = instance;
|
||
instance->chan[chan_idx] = dma_chan;
|
||
|
||
if (&(dma_chan->config) != dma_chan_config)
|
||
dma_chan->config = *dma_chan_config;
|
||
|
||
FDdmaStop(instance); /* disable irq */
|
||
|
||
if (FDDMA_SUCCESS != FDdmaDisableChan(base_addr, chan_idx))
|
||
{
|
||
FDDMA_ERROR("disable DDMA@0x%x channel %d failed !!!", base_addr, chan_idx);
|
||
return FDDMA_ERR_WAIT_TIMEOUT;
|
||
}
|
||
|
||
FDdmaResetChan(base_addr, chan_idx); /* reset channel */
|
||
FDdmaSetChanSelection(base_addr, chan_idx, dma_chan->config.slave_id); /* select channel */
|
||
FDdmaSetChanBind(base_addr, chan_idx, TRUE); /* bind channel */
|
||
|
||
/* setup transfer src and dst */
|
||
/* dma_tx_req: ddr --> dev 从内存中读取数据,写入外设 */
|
||
/* dma_rx_req: dev --> ddr 从外设读取数据到内存 */
|
||
#ifdef __aarch64___
|
||
FDdmaWriteReg(base_addr, FDDMA_CHAN_DDR_LOW_ADDR_OFFSET(chan_idx), LOWER_32_BITS(dma_chan_config->ddr_addr));
|
||
FDdmaWriteReg(base_addr, FDDMA_CHAN_DDR_UP_ADDR_OFFSET(chan_idx), UPPER_32_BITS(dma_chan_config->ddr_addr));
|
||
#else
|
||
FDdmaWriteReg(base_addr, FDDMA_CHAN_DDR_LOW_ADDR_OFFSET(chan_idx), (u32)(dma_chan_config->ddr_addr));
|
||
FDdmaWriteReg(base_addr, FDDMA_CHAN_DDR_UP_ADDR_OFFSET(chan_idx), 0);
|
||
#endif
|
||
|
||
FDdmaWriteReg(base_addr, FDDMA_CHAN_DEV_ADDR_OFFSET(chan_idx), dma_chan_config->dev_addr);
|
||
FDdmaWriteReg(base_addr, FDDMA_CHAN_TS_OFFSET(chan_idx), dma_chan_config->trans_len); /* block size */
|
||
|
||
/* set channel request direction */
|
||
FDdmaSetChanDirection(base_addr, chan_idx,
|
||
(FDDMA_CHAN_REQ_RX == dma_chan->config.req_mode) ? TRUE : FALSE);
|
||
|
||
FDDMA_INFO("chan-%d ddr @0x%x", chan_idx, FDDMA_CHAN_DDR_LOW_ADDR_OFFSET(chan_idx));
|
||
FDDMA_INFO("ddr addr: 0x%x", FDdmaReadReg(base_addr, FDDMA_CHAN_DDR_LOW_ADDR_OFFSET(chan_idx)));
|
||
FDDMA_INFO("dev addr: 0x%x", FDdmaReadReg(base_addr, FDDMA_CHAN_DEV_ADDR_OFFSET(chan_idx)));
|
||
FDDMA_INFO("trans len: %d", FDdmaReadReg(base_addr, FDDMA_CHAN_TS_OFFSET(chan_idx)));
|
||
|
||
FDdmaSetChanTimeout(base_addr, chan_idx, 0xffff);
|
||
FDdmaEnableChanIrq(base_addr, chan_idx);
|
||
|
||
if (FDDMA_SUCCESS == ret)
|
||
{
|
||
instance->bind_status |= BIT(chan_idx);
|
||
dma_chan->is_used = TRUE;
|
||
FDDMA_INFO("allocate channel %d", chan_idx);
|
||
}
|
||
|
||
return ret;
|
||
}
|
||
|
||
/**
|
||
* @name: FDdmaDellocateChan
|
||
* @msg: 释放之前分配的DDMA通道
|
||
* @return {FError} FDDMA_SUCCESS表示释放成功,其它返回值表示释放失败
|
||
* @param {FDdmaChan} *dma_chan, DDMA控制器实例
|
||
*/
|
||
FError FDdmaDellocateChan(FDdmaChan *const dma_chan)
|
||
{
|
||
FASSERT(dma_chan && dma_chan->dma);
|
||
FDdma *const instance = dma_chan->dma;
|
||
const FDdmaChanIndex chan_idx = dma_chan->config.id;
|
||
uintptr base_addr = instance->config.base_addr;
|
||
FError ret = FDDMA_SUCCESS;
|
||
|
||
if (FT_COMPONENT_IS_READY != instance->is_ready)
|
||
{
|
||
FDDMA_ERROR("dma instance not init !!!");
|
||
return FDDMA_ERR_NOT_INIT;
|
||
}
|
||
|
||
if (FDDMA_SUCCESS != FDdmaDisableChan(base_addr, chan_idx))
|
||
{
|
||
FDDMA_ERROR("disable DDMA@0x%x channel %d failed !!!", base_addr, chan_idx);
|
||
return FDDMA_ERR_WAIT_TIMEOUT;
|
||
}
|
||
|
||
FDdmaResetChan(base_addr, chan_idx);
|
||
|
||
FDdmaSetChanBind(base_addr, chan_idx, FALSE); /* unbind channel */
|
||
ret = FDdmaDisableChan(base_addr, chan_idx);
|
||
if (FDDMA_SUCCESS != ret) /* disable channel */
|
||
{
|
||
FDDMA_ERROR("disable ddma@%p channel %d failed !!!", base_addr, chan_idx);
|
||
return ret;
|
||
}
|
||
|
||
FDdmaDisableChanIrq(base_addr, chan_idx); /* disable channel irq */
|
||
|
||
instance->bind_status &= ~BIT(chan_idx); /* set bind status */
|
||
instance->chan[chan_idx] = NULL;
|
||
|
||
FDDMA_INFO("deallocate channel %d", chan_idx);
|
||
memset(dma_chan, 0, sizeof(*dma_chan));
|
||
|
||
return ret;
|
||
}
|
||
|
||
/**
|
||
* @name: FDdmaActiveChan
|
||
* @msg: 使能指定的DDMA通道
|
||
* @note: 调用FDdmaAllocateChan后无需调用此函数
|
||
* @return {FError} 返回FDDMA_SUCCESS表示成功,返回其它表示失败
|
||
* @param FDdmaChan *const dma_chan, DDMA通道实例
|
||
*/
|
||
FError FDdmaActiveChan(FDdmaChan *const dma_chan)
|
||
{
|
||
FASSERT(dma_chan && dma_chan->dma);
|
||
FDdma *const instance = dma_chan->dma;
|
||
uintptr base_addr = instance->config.base_addr;
|
||
|
||
if (FT_COMPONENT_IS_READY != instance->is_ready)
|
||
{
|
||
FDDMA_ERROR("dma instance not init !!!");
|
||
return FDDMA_ERR_NOT_INIT;
|
||
}
|
||
|
||
FDdmaEnableChan(base_addr, dma_chan->config.id);
|
||
FDdmaClearChanIrq(base_addr, dma_chan->config.id); /* clear interrupt status */
|
||
return FDDMA_SUCCESS;
|
||
}
|
||
|
||
FError FDdmaDeactiveChan(FDdmaChan *const dma_chan)
|
||
{
|
||
FASSERT(dma_chan && dma_chan->dma);
|
||
FDdma *const instance = dma_chan->dma;
|
||
uintptr base_addr = instance->config.base_addr;
|
||
|
||
if (FT_COMPONENT_IS_READY != instance->is_ready)
|
||
{
|
||
FDDMA_ERROR("dma instance not init !!!");
|
||
return FDDMA_ERR_NOT_INIT;
|
||
}
|
||
|
||
return FDdmaDisableChan(base_addr, dma_chan->config.id);
|
||
}
|
||
|
||
/**
|
||
* @name: FDdmaReset
|
||
* @msg: 重置DDMA控制器
|
||
* @return {FError} FDDMA_SUCCESS表示重置成功,其它返回值表示失败
|
||
* @param {FDdma} *instance, DDMA控制器实例
|
||
*/
|
||
static FError FDdmaReset(FDdma *const instance)
|
||
{
|
||
FASSERT(instance);
|
||
uintptr base_addr = instance->config.base_addr;
|
||
FError ret = FDDMA_SUCCESS;
|
||
u32 reg_val;
|
||
u32 chan;
|
||
|
||
if (0 != instance->bind_status)
|
||
{
|
||
FDDMA_WARN("some channel not yet un-bind !!!");
|
||
}
|
||
|
||
FDdmaDisable(base_addr); /* disable ddma */
|
||
FDdmaSoftwareReset(base_addr); /* do software reset */
|
||
FDdmaDisableGlobalIrq(base_addr);
|
||
|
||
/* disable channel and its irq */
|
||
for (u32 chan = FDDMA_CHAN_0; chan < FDDMA_NUM_OF_CHAN; chan++)
|
||
{
|
||
/* disable channel */
|
||
ret = FDdmaDisableChan(base_addr, chan);
|
||
if (FDDMA_SUCCESS != ret)
|
||
{
|
||
FDDMA_ERROR("disable ddma@%p channel %d failed !!!", base_addr, chan);
|
||
break;
|
||
}
|
||
}
|
||
|
||
FDdmaDumpRegisters(base_addr);
|
||
|
||
return ret;
|
||
} |