一 程序位置分布
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.c的rockchip_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_driver的ops,统一抽象了对硬件的访问接口(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.c中rockchip_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;
}
评论