DSMC驱动分析之RTOS

Linux  ·  2025-08-22

一 程序位置分布

RTOS中DSMC代码,drv_dsmc_host.c是主机驱动代码,drv_dsmc_slave.c是从机驱动代码,dsmc_test.c是测试程序
SDK/GA3506_Linux_Source/rtos/bsp/rockchip/common/drivers/drv_dsmc_host.c
SDK/GA3506_Linux_Source/rtos/bsp/rockchip/common/drivers/drv_dsmc_slave.c
SDK/GA3506_Linux_Source/rtos/bsp/rockchip/common/tests/dsmc_test.c
SDK/GA3506_Linux_Source/rtos/bsp/rockchip/common/hal/lib/hal/src/hal_dsmc_host.c
SDK/GA3506_Linux_Source/rtos/bsp/rockchip/common/hal/lib/hal/src/hal_dsmc_slave.c
SDK/GA3506_Linux_Source/rtos/bsp/rockchip/common/drivers/dma.c

二 主机驱动代码解析

drv_dsmc_host.c -> hal_dsmc_host.c -> drv_dsmc_host.c -> dsmc_test.c

2.1 系统如何启动驱动?

  • 首先,在drv_dsmc_host.c主机驱动中rockchip_dsmc_host_probe(void)函数下方,使用了INIT_DEVICE_EXPORT(rockchip_dsmc_host_probe)rockchip_dsmc_host_probe注册到了设备自动初始化函数

  • 这是RT-Thread自动初始化机制,听起来似乎很高大上,实际上只是将函数入口地址记录,在board_base.c中的rt_hw_board_init()启动板卡,完成基础初始化后,会在调用rt_components_board_init()
    rt_components_board_init()内部实质上是执行函数指针,也就是执行对应的rockchip_dsmc_host_probe函数来启动驱动。

2.2 主机驱动初始化代码流向

rockchip_dsmc_host_probe -> HAL_DSMC_HOST_Init -> DSMC_HOST_DataInit -> DSMC_HOST_MapInit -> 
DSMC_HOST_LbInit -> DSMC_HOST_LbCmnConfig -> DSMC_HOST_LbCmnRgnConfig -> DSMC_HOST_LbCsrConfig -> lbReadReg与lbWriteReg
  • 在底层lbReadReg与lbWriteReg会去读写寄存器配置,然后逐级返回完成初始化打印HAL_DSMC_HOST_Init OK

2.3 主机驱动代码框架

  • drv_dsmc_host.c驱动主要做外设初始化与DMA数据拷贝搬运和直接读写指定地址的用户调用实现
  • 在RTOS中drv_dsmc_host.crockchip_dsmc_host_probe初始化函数会从RTOS层调用HAL层hal_dsmc_host.c文件的HAL_DSMC_HOST_Init去写寄存器初始化DSMC外设
  • 从下图drv_dsmc_host.c函数列表中可以看出来,除了初始化外设,主要就是DMA数据拷贝搬运的实现和直接读写指定地址的实现
  • 并且驱动通过ops操作集将DMA数据拷贝搬运和直接读写指定地址函数用作上层调用函数接口
  • drv_dsmc_host.c中,除了初始化函数,还对dsmc_host_ops这个ops操作集合做读写与底层拷贝等函数的实现,本质上类似 Linux 的 file_operations platform_driverops,统一抽象了对硬件的访问接口(read/write/copy_from/copy_to等),便于上层调用和适配不同实现,(参考Linux通用的驱动篇)给ops赋值函数指针之后,在上层应用函数,就可以直接使用一个dsmc_host_ops类型的结构体,调用其读写函数,拿到DSMC通信数据
struct dsmc_host_ops
{
    rt_err_t (*read)(struct rockchip_rt_dsmc_host *dsmc_host,
                     uint32_t cs, uint32_t region,
                     unsigned long addr, uint32_t *data);
    rt_err_t (*write)(struct rockchip_rt_dsmc_host *dsmc_host,
                      uint32_t cs, uint32_t region,
                      unsigned long addr, uint32_t val);
    rt_err_t (*copy_from)(struct rockchip_rt_dsmc_host *dsmc_host,
                          uint32_t cs, uint32_t region, uint32_t from,
                          uint32_t dst_phys, size_t size);
    rt_err_t (*copy_from_state)(struct rockchip_rt_dsmc_host *dsmc_host);
    rt_err_t (*copy_to)(struct rockchip_rt_dsmc_host *dsmc_host,
                        uint32_t cs, uint32_t region, uint32_t src_phys,
                        uint32_t to, size_t size);
    rt_err_t (*copy_to_state)(struct rockchip_rt_dsmc_host *dsmc_host);
};


static struct dsmc_host_ops rockchip_dsmc_ops =
{
        .read = DSMC_HOST_ReadData,
        .write = DSMC_HOST_WriteData,
        .copy_from = DSMC_HOST_CopyFrom,
        .copy_from_state = DSMC_HOST_CopyFromState,
        .copy_to = DSMC_HOST_CopyTo,
        .copy_to_state = DSMC_HOST_CopyToState,
};

三 从机驱动代码解析

drv_dsmc_slave.c -> hal_dsmc_slave.c -> drv_dsmc_slave.c -> dsmc_test.c

3.1 系统如何启动驱动?

  • 与主机代码基本一致,drv_dsmc_slave.crockchip_dsmc_slave_probe(void)函数下方,使用了INIT_DEVICE_EXPORT(rockchip_dsmc_slave_probe)rockchip_dsmc_slave_probe注册到了设备自动初始化函数

3.2 从机驱动初始化代码流向

rockchip_dsmc_slave_probe -> HAL_DSMC_SLAVE_Init -> WRITE_REG -> rockchip_dsmc_slave_probe

  • 在底层WriteReg会去读写寄存器配置,然后逐级返回完成初始化打印rockchip dsmc local bus slave driver initialized

3.3 从机驱动代码框架

  • 从机代码比较简单,只需要初始化写寄存器,然后配置中断函数即可,主机发来的数据可以直接读取dma

HAL_DSMC_SLAVE_IrqHander是中断处理入口,判断是否要启动DMA,DSMC_SLAVE_DmaTrigger则负责具体执行通知硬件搬运数据的动作,它们一起实现了从机对主机搬运请求的响应和触发

static HAL_Status DSMC_SLAVE_DmaTrigger(struct LBC_SLAVE_CSR_REG *pReg)
{
    int timeOut = 1000;


    /* wait interrupt register empty */
    while (timeOut-- > 0) {
        if (!(READ_REG(pReg->LBC_S2H_INT_STA) &
              (0x1 << S2H_INT_FOR_DMA_NUM))) {
            break;
        }
        if (timeOut == 0) {
            HAL_DBG_ERR("Timeout waiting for s2h interrupt empty!\n");


            return HAL_TIMEOUT;
        }
        HAL_CPUDelayUs(1);
    }


    /* trigger a slave to host interrupt which will start dma hardware mode copy */
    WRITE_REG(pReg->APP_CON15, 0x1);


    return HAL_OK;
}

/**
 * @brief  DSMC_SLAVE irq hander.
 * @param  slave: pointer to a DSMC_SLAVE structure.
 * @return HAL_Status
 */
HAL_Status HAL_DSMC_SLAVE_IrqHander(struct HAL_DSMC_SLAVE *slave)
{
    struct LBC_SLAVE_CSR_REG *pReg;


    HAL_ASSERT(IS_LBC_SLAVE_CSR_INSTANCE(slave->pReg));
    pReg = slave->pReg;


    if (READ_REG(pReg->LBC_CON15)) {
        DSMC_SLAVE_DmaTrigger(pReg);
    }


    /* clear all h2s interrupt */
    WRITE_REG(pReg->APP_H2S_INT_STA, LBC_SLAVE_CSR_APP_H2S_INT_STA_APP_H2S_INT_STA_MASK);


    return HAL_OK;
}
 DSMC
评论
LJ` Blog . All Rights Reserved. Theme Jasmine by Kent Liao.
冀ICP备2025127925号 冀公网安备13082402000074号