/* * 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_hw.c * Date: 2022-02-10 14:53:42 * LastEditTime: 2022-02-18 08:24:47 * Description:  This files is for ddma register rw operations * * Modify History: * Ver   Who        Date         Changes * ----- ------     --------    -------------------------------------- * 1.0 Zhugengyu 2022/5/13 init commit */ /***************************** Include Files *********************************/ #include #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-HW" #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 ******************************/ /****************************************************************************/ /** * @name: FDdmaDisable * @msg: 去使能DDMA控制器 * @return {*} * @param {uintptr} base_addr, DDMA控制器基地址 */ void FDdmaDisable(uintptr base_addr) { u32 reg_val = FDdmaReadReg(base_addr, FDDMA_CTL_OFFSET); reg_val &= ~FDDMA_CTL_ENABLE; FDdmaWriteReg(base_addr, FDDMA_CTL_OFFSET, reg_val); FDDMA_DEBUG("ddma @%p disabled : 0x%x", base_addr, FDdmaReadReg(base_addr, FDDMA_CTL_OFFSET)); return; } /** * @name: FDdmaEnable * @msg: 使能DDMA控制器 * @return {*} * @param {uintptr} base_addr, DDMA控制器基地址 */ void FDdmaEnable(uintptr base_addr) { u32 reg_val = FDdmaReadReg(base_addr, FDDMA_CTL_OFFSET); reg_val |= FDDMA_CTL_ENABLE; FDdmaWriteReg(base_addr, FDDMA_CTL_OFFSET, reg_val); FDDMA_DEBUG("ddma @%p enabled : 0x%x", base_addr, FDdmaReadReg(base_addr, FDDMA_CTL_OFFSET)); return; } /** * @name: FDdmaSoftwareReset * @msg: 复位DDMA控制器 * @return {*} * @param {uintptr} base_addr, DDMA控制器基地址 */ void FDdmaSoftwareReset(uintptr base_addr) { int delay = 10000; u32 reg_val = FDdmaReadReg(base_addr, FDDMA_CTL_OFFSET); reg_val |= FDDMA_CTL_SRST; FDdmaWriteReg(base_addr, FDDMA_CTL_OFFSET, reg_val); FDDMA_DEBUG("ddma @%p software reset start : 0x%x", base_addr, FDdmaReadReg(base_addr, FDDMA_CTL_OFFSET)); while (--delay > 0) /* wait a while to do reset */ ; reg_val &= ~FDDMA_CTL_SRST; FDdmaWriteReg(base_addr, FDDMA_CTL_OFFSET, reg_val); /* exit from software reset */ FDDMA_DEBUG("ddma @%p software reset end : 0x%x", base_addr, FDdmaReadReg(base_addr, FDDMA_CTL_OFFSET)); return; } /** * @name: FDdmaDisableGlobalIrq * @msg: 关闭DDMA全局中断 * @return {*} * @param {uintptr} base_addr, DDMA控制器基地址 */ void FDdmaDisableGlobalIrq(uintptr base_addr) { u32 reg_val = FDdmaReadReg(base_addr, FDDMA_MASK_INTR_OFFSET); reg_val |= FDDMA_MASK_EN_GLOBAL_INTR; /* write 1 and disable interrupt */ FDdmaWriteReg(base_addr, FDDMA_MASK_INTR_OFFSET, reg_val); return; } /** * @name: FDdmaEnableGlobalIrq * @msg: 打开DDMA全局中断 * @return {*} * @param {uintptr} base_addr, DDMA控制器基地址 */ void FDdmaEnableGlobalIrq(uintptr base_addr) { u32 reg_val = FDdmaReadReg(base_addr, FDDMA_MASK_INTR_OFFSET); reg_val &= ~FDDMA_MASK_EN_GLOBAL_INTR; /* write 0 and enable interrupt */ FDdmaWriteReg(base_addr, FDDMA_MASK_INTR_OFFSET, reg_val); return; } /** * @name: FDdmaDisableChanIrq * @msg: 关闭DDMA通道中断 * @return {*} * @param {uintptr} base_addr, DDMA控制器基地址 * @param {u32} chan, DDMA通道号 */ void FDdmaDisableChanIrq(uintptr base_addr, u32 chan) { FASSERT_MSG((FDDMA_NUM_OF_CHAN > chan), "chan %d not support", chan); u32 reg_val = FDdmaReadReg(base_addr, FDDMA_MASK_INTR_OFFSET); reg_val |= FDDMA_MASK_EN_CHAN_INTR(chan); FDdmaWriteReg(base_addr, FDDMA_MASK_INTR_OFFSET, reg_val); return; } /** * @name: FDdmaEnableChanIrq * @msg: 打开DDMA通道中断 * @return {*} * @param {uintptr} base_addr, DDMA控制器基地址 * @param {u32} chan, DDMA通道号 */ void FDdmaEnableChanIrq(uintptr base_addr, u32 chan) { FASSERT_MSG((FDDMA_NUM_OF_CHAN > chan), "chan %d not support", chan); u32 reg_val = FDdmaReadReg(base_addr, FDDMA_MASK_INTR_OFFSET); reg_val &= ~FDDMA_MASK_EN_CHAN_INTR(chan); /* write 0 and enable */ FDdmaWriteReg(base_addr, FDDMA_MASK_INTR_OFFSET, reg_val); return; } /** * @name: FDdmaDisableChan * @msg: 去使能DDMA通道 * @return {FError} FDDMA_SUCCESS 表示去使能成功 * @param {uintptr} base_addr, DDMA控制器基地址 * @param {u32} chan, DDMA通道号 */ FError FDdmaDisableChan(uintptr base_addr, u32 chan) { FASSERT_MSG((FDDMA_NUM_OF_CHAN > chan), "chan %d not support", chan); int delay = 1000; u32 reg_val = FDdmaReadReg(base_addr, FDDMA_CHAN_CTL_OFFSET(chan)); reg_val &= ~FDDMA_CHAN_CTL_EN; FDdmaWriteReg(base_addr, FDDMA_CHAN_CTL_OFFSET(chan), reg_val); /* 先写该位 1’b0,随后读取到该位是 1’b0 的时候,才能复位该通道 */ do { reg_val = FDdmaReadReg(base_addr, FDDMA_CHAN_CTL_OFFSET(chan)); if (delay-- <= 0) break; } while (reg_val & FDDMA_CHAN_CTL_EN); FDDMA_DEBUG("ddma @%p chan %d disabled : 0x%x", base_addr, chan, FDdmaReadReg(base_addr, FDDMA_CHAN_CTL_OFFSET(chan))); return (delay > 0) ? FDDMA_SUCCESS : FDDMA_ERR_WAIT_TIMEOUT; } /** * @name: FDdmaEnableChan * @msg: 使能DDMA通道 * @return {*} * @param {uintptr} base_addr, DDMA控制器基地址 * @param {u32} chan, DDMA通道号 */ void FDdmaEnableChan(uintptr base_addr, u32 chan) { FASSERT_MSG((FDDMA_NUM_OF_CHAN > chan), "chan %d not support", chan); u32 reg_val = FDdmaReadReg(base_addr, FDDMA_CHAN_CTL_OFFSET(chan)); reg_val |= FDDMA_CHAN_CTL_EN; FDdmaWriteReg(base_addr, FDDMA_CHAN_CTL_OFFSET(chan), reg_val); FDDMA_DEBUG("ddma @%p chan %d enabled : 0x%x", base_addr, chan, FDdmaReadReg(base_addr, FDDMA_CHAN_CTL_OFFSET(chan))); return; } /** * @name: FDdmaClearChanIrq * @msg: 清除DDMA通道中断状态 * @return {*} * @param {uintptr} base_addr, DDMA控制器基地址 * @param {u32} chan, DDMA通道号 */ void FDdmaClearChanIrq(uintptr base_addr, u32 chan) { FASSERT_MSG((FDDMA_NUM_OF_CHAN > chan), "chan %d not support", chan); /* write 1 to clear irq status of channel */ FDdmaWriteReg(base_addr, FDDMA_STA_OFFSET, FDDMA_STA_CHAN_REQ_DONE(chan)); } /** * @name: FDdmaResetChan * @msg: 重置DDMA通道 * @return {*} * @param {uintptr} base_addr, DDMA控制器基地址 * @param {u32} chan, DDMA通道号 */ void FDdmaResetChan(uintptr base_addr, u32 chan) { FASSERT_MSG((FDDMA_NUM_OF_CHAN > chan), "chan %d not support", chan); int delay = 1000; u32 reg_val; if (FDdmaIsChanRunning(base_addr, chan)) /* disable channel if running */ (void)FDdmaDisableChan(base_addr, chan); reg_val = FDdmaReadReg(base_addr, FDDMA_CHAN_CTL_OFFSET(chan)); reg_val |= FDDMA_CHAN_CTL_SRST; FDdmaWriteReg(base_addr, FDDMA_CHAN_CTL_OFFSET(chan), reg_val); while (--delay > 0) /* wait a while to do reset */ ; reg_val &= ~FDDMA_CHAN_CTL_SRST; FDdmaWriteReg(base_addr, FDDMA_CHAN_CTL_OFFSET(chan), reg_val); FDDMA_DEBUG("chan reset done, ctrl: 0x%x", FDdmaReadReg(base_addr, FDDMA_CHAN_CTL_OFFSET(chan))); return; } /** * @name: FDdmaIsChanRunning * @msg: 检查通道是否在工作中 * @return {boolean} TRUE: 在工作中 * @param {uintptr} base_addr, DDMA控制器基地址 * @param {u32} chan, DDMA通道号 */ boolean FDdmaIsChanRunning(uintptr base_addr, u32 chan) { FASSERT_MSG((FDDMA_NUM_OF_CHAN > chan), "chan %d not support", chan); u32 reg_val = FDdmaReadReg(base_addr, FDDMA_CHAN_CTL_OFFSET(chan)); return (FDDMA_CHAN_CTL_EN & reg_val) ? TRUE : FALSE; } /** * @name: FDdmaSetChanSelection * @msg: 将DDMA通道与外设绑定 * @return {*} * @param {uintptr} base_addr, DDMA控制器基地址 * @param {u32} chan, DDMA通道号 * @param {u32} slave_id, 外设对应的slave id */ void FDdmaSetChanSelection(uintptr base_addr, u32 chan, u32 slave_id) { FASSERT_MSG((FDDMA_NUM_OF_CHAN > chan), "chan %d not support", chan); FASSERT_MSG((FDDMA_MAX_SLAVE_ID >= slave_id), "invalid slave id %d", slave_id); u32 reg_val; if (FDDMA_CHAN_4 > chan) { reg_val = FDdmaReadReg(base_addr, FDDMA_CHAN_0_3_CFG_OFFSET); reg_val &= ~FDDMA_CHAN_0_3_SEL_MASK(chan); reg_val |= FDDMA_CHAN_0_3_SEL(chan, slave_id); reg_val |= FDDMA_CHAN_0_3_SEL_EN(chan); FDdmaWriteReg(base_addr, FDDMA_CHAN_0_3_CFG_OFFSET, reg_val); FDDMA_DEBUG("ddma@%p set chan-%d slave id-%d, 0x%x", base_addr, chan, slave_id, FDdmaReadReg(base_addr, FDDMA_CHAN_0_3_CFG_OFFSET)); } else { reg_val = FDdmaReadReg(base_addr, FDDMA_CHAN_4_7_CFG_OFFSET); reg_val &= ~FDDMA_CHAN_4_7_SEL_MASK(chan); reg_val |= FDDMA_CHAN_4_7_SEL(chan, slave_id); reg_val |= FDDMA_CHAN_4_7_SEL_EN(chan); FDdmaWriteReg(base_addr, FDDMA_CHAN_4_7_CFG_OFFSET, reg_val); FDDMA_DEBUG("ddma@%p set chan-%d slave id-%d, 0x%x", base_addr, chan, slave_id, FDdmaReadReg(base_addr, FDDMA_CHAN_4_7_CFG_OFFSET)); } return; } /** * @name: FDdmaSetChanBind * @msg: 修改通道的绑定状态 * @return {*} * @param {uintptr} base_addr, DDMA控制器基地址 * @param {u32} chan, DDMA通道号 * @param {boolean} bind, TRUE: 绑定,FALSE: 解除绑定 */ void FDdmaSetChanBind(uintptr base_addr, u32 chan, boolean bind) { FASSERT_MSG((FDDMA_NUM_OF_CHAN > chan), "chan %d not support", chan); u32 reg_val = FDdmaReadReg(base_addr, FDDMA_CHAN_BIND_OFFSET); if (bind) reg_val |= BIT(chan); else reg_val &= ~BIT(chan); FDDMA_DEBUG("ddma@%p %s chan-%d, 0x%x", base_addr, bind ? "bind" : "unbind", chan, FDdmaReadReg(base_addr, FDDMA_CHAN_BIND_OFFSET)); FDdmaWriteReg(base_addr, FDDMA_CHAN_BIND_OFFSET, reg_val); return; } /** * @name: FDdmaSetChanDirection * @msg: 设置通道的方向 * @return {*} * @param {uintptr} base_addr, DDMA控制器基地址 * @param {u32} chan, DDMA通道号 * @param {boolean} is_rx, TRUE: 接收, FALSE: 发送 */ void FDdmaSetChanDirection(uintptr base_addr, u32 chan, boolean is_rx) { FASSERT_MSG((FDDMA_NUM_OF_CHAN > chan), "chan %d not support", chan); u32 reg_val = FDdmaReadReg(base_addr, FDDMA_CHAN_CTL_OFFSET(chan)); if (is_rx) reg_val |= FDDMA_CHAN_CTL_RX_MODE; /* device to memory */ else reg_val &= ~FDDMA_CHAN_CTL_RX_MODE; /* memory to device */ FDdmaWriteReg(base_addr, FDDMA_CHAN_CTL_OFFSET(chan), reg_val); return; } /** * @name: FDdmaSetChanTimeout * @msg: 设置通道超时 * @return {*} * @param {uintptr} base_addr, DDMA控制器基地址 * @param {u32} chan, DDMA通道号 * @param {u32} timeout, 超时设置,0表示不启用超时 */ void FDdmaSetChanTimeout(uintptr base_addr, u32 chan, u32 timeout) { FASSERT_MSG((FDDMA_NUM_OF_CHAN > chan), "chan %d not support", chan); u32 reg_val = FDdmaReadReg(base_addr, FDDMA_CHAN_TIMEOUT_CNT_OFFSET(chan)); if (0 < timeout) { reg_val &= ~FDDMA_CHAN_TIMEOUT_CNT_MASK; reg_val |= FDDMA_CHAN_TIMEOUT_CNT_SET(timeout); reg_val |= FDDMA_CHAN_TIMEOUT_EN; } else { reg_val &= ~FDDMA_CHAN_TIMEOUT_EN; } FDdmaWriteReg(base_addr, FDDMA_CHAN_TIMEOUT_CNT_OFFSET(chan), reg_val); return; }