目录

    • 一、驱动内容
      • 1. 核心结构体解析
      • 2. 关键模块解析
      • 3. 驱动初始化流程
      • 4. 关键寄存器操作
      • 5. 典型工作流程
      • 6. 代码特点
      • 7. 重要函数列表
      • 8. 使用示例
    • 二、驱动中DMA的使用
      • 1. DMA通道初始化(`imx_uart_dma_init`)
      • 2. DMA发送流程(`imx_uart_dma_tx`)
      • 3. DMA发送完成回调(`imx_uart_dma_tx_callback`)
      • 4. DMA接收实现(`imx_uart_start_rx_dma`)
      • 5. DMA接收回调(`imx_uart_dma_rx_callback`)
      • 6. DMA引擎核心操作
      • 7. DMA寄存器配置
      • 8. DMA中断处理
      • 9. DMA性能优化
      • 10. DMA错误处理
      • 11. DMA与传统中断模式对比
      • 12. DMA传输生命周期
      • 13. DMA寄存器配置示例
      • 14. DMA缓冲区管理
      • 15. DMA性能调优建议
      • 16. DMA调试方法
      • 17. DMA异常处理
      • 18. DMA与电源管理
      • 19. DMA性能指标
      • 20. DMA调试寄存器
      • 21. DMA典型问题排查

  本文分析的驱动源码路径为:https://github.com/rockchip-linux/kernel/blob/develop-5.10/drivers/tty/serial/imx.c

一、驱动内容

1. 核心结构体解析

  1. imx_uart_data(硬件特性描述)
struct imx_uart_data {unsigned uts_reg;        // UART测试寄存器偏移enum imx_uart_type devtype; // 设备类型(i.MX1/21/53/6Q)
};
  1. imx_port(核心驱动结构)
struct imx_port {struct uart_port port;  // 标准UART端口结构struct timer_list timer; // 调制解调器状态轮询定时器struct dma_chan *dma_chan_rx, *dma_chan_tx; // DMA通道void *rx_buf;           // DMA接收缓冲区struct circ_buf rx_ring; // 接收环形缓冲区// 其他寄存器缓存、GPIO、时钟等成员...
};

2. 关键模块解析

DMA传输管理

  • DMA通道初始化:imx_uart_dma_init
  • DMA发送流程:imx_uart_dma_tx
  • DMA接收回调处理:imx_uart_dma_rx_callback

中断处理:

  • 主中断处理函数
static irqreturn_t imx_uart_int(int irq, void *dev_id)
{// 处理各种中断源(接收、发送、状态变化等)if (usr1 & USR1_RRDY) { // 接收就绪__imx_uart_rxint();}if (usr1 & USR1_TRDY || usr2 & USR2_TXDC) { // 发送完成imx_uart_transmit_buffer();}
}
  • DMA接收中断处理
static void imx_uart_dma_rx_callback(void *data)
{// 处理DMA接收完成事件dma_sync_sg_for_cpu(); // 同步数据tty_insert_flip_string(); // 将数据送入TTY层dma_sync_sg_for_device(); // 重新同步DMA
}

UART核心操作:

  • 发送缓冲区处理
static void imx_uart_transmit_buffer(struct imx_port *sport)
{// 处理XON/XOFF字符if (sport->port.x_char) {imx_uart_writel(sport, sport->port.x_char, URTX0);return;}// 使用DMA或PIO发送数据if (sport->dma_is_enabled) {imx_uart_dma_tx(sport);} else {// PIO模式发送while (!uart_circ_empty(xmit) && !TXFULL) {imx_uart_writel(sport, xmit->buf[xmit->tail], URTX0);}}
}
  • 接收错误处理
static void imx_uart_clear_rx_errors(struct imx_port *sport)
{// 处理帧错误、校验错误、溢出等错误if (usr2 & USR2_ORE) {sport->port.icount.overrun++;imx_uart_writel(sport, USR2_ORE, USR2);}
}

电源管理:

  • 挂起/恢复操作
static int imx_uart_suspend_noirq(struct device *dev)
{// 保存寄存器上下文imx_uart_save_context(sport);clk_disable(sport->clk_ipg); // 关闭时钟
}static int imx_uart_resume_noirq(struct device *dev)
{// 恢复寄存器状态imx_uart_restore_context(sport);
}

控制台支持(可选):

#if IS_ENABLED(CONFIG_SERIAL_IMX_CONSOLE)
static void imx_uart_console_write(struct console *co, const char *s, unsigned int count)
{// 控制台输出实现while (imx_uart_readl(sport, UTS) & UTS_TXFULL) {}imx_uart_writel(sport, ch, URTX0);
}
#endif

3. 驱动初始化流程

static int imx_uart_probe(struct platform_device *pdev)
{// 1. 获取平台资源base = devm_ioremap_resource(...); // 寄存器映射sport->clk_ipg = devm_clk_get(...); // 获取时钟// 2. 初始化UART端口sport->port.ops = &imx_uart_pops; // 操作函数集sport->port.uartclk = clk_get_rate(sport->clk_per); // 设置时钟频率// 3. 注册DMA通道if (imx_uart_dma_init(sport) == 0) {imx_uart_enable_dma(sport); // 启用DMA}// 4. 注册UART端口return uart_add_one_port(&imx_uart_uart_driver, &sport->port);
}

4. 关键寄存器操作

  1. 发送寄存器(URTX0)
imx_uart_writel(sport, data, URTX0); // 发送单个字符
  1. 控制寄存器(UCR1-UCR4)
// 启用DMA发送
ucr1 |= UCR1_TXDMAEN;
imx_uart_writel(sport, ucr1, UCR1);
  1. FIFO控制寄存器(UFCR)
imx_uart_writel(sport, TXTL_DMA << UFCR_TXTL_SHF, UFCR); // 设置FIFO触发级别

5. 典型工作流程

数据发送流程:

用户写入数据 → imx_uart_start_tx() → └─ DMA模式: imx_uart_dma_tx() → dmaengine_prep_slave_sg() → 启动DMA传输 → └─ PIO模式: imx_uart_writel() → 硬件发送 → 中断处理 → imx_uart_txint()

数据接收流程:

硬件接收 → DMA中断 → imx_uart_dma_rx_callback() → └─ tty_insert_flip_string() → 提交到TTY层 → 用户读取

电源管理:

suspend → imx_uart_suspend_noirq() → └─ 保存寄存器状态 → 禁用时钟 → 进入睡眠
resume → imx_uart_resume_noirq() → └─ 恢复寄存器 → 启用时钟 → 恢复运行

6. 代码特点

  1. DMA优化

    • 使用循环DMA(cyclic DMA)实现高效接收
    • 支持DMA和PIO两种传输模式
    • 自动切换DMA/POLL模式
  2. 硬件特性适配

    • 支持i.MX1/21/53/6Q等不同版本
    • 处理DTE/RTS/CTS等硬件流控
    • 支持RS485模式
  3. 错误处理

    • 处理溢出(OVERRUN)、帧错误(FRAMERR)、校验错误(PARITYERR)等
    • 自动恢复机制(如imx_uart_flush_buffer())

7. 重要函数列表

函数名功能说明调用时机
imx_uart_dma_tx()启动DMA发送有数据需要发送时
imx_uart_dma_rx_callback()DMA接收完成回调接收DMA完成时
imx_uart_transmit_buffer()发送数据处理发送缓冲区有数据时
imx_uart_set_termios()设置串口参数(波特率等)termios配置变更时
imx_uart_rs485_config()RS485模式配置RS485模式切换时
imx_uart_probe()驱动探测函数平台设备匹配时

8. 使用示例

  1. 发送数据
// 用户调用write()时,最终调用
imx_uart_start_tx()imx_uart_dma_tx() → 启动DMA传输
  1. 接收数据
// 硬件接收到数据时触发
DMA中断 → imx_uart_dma_rx_callback() → 数据提交到TTY层
  1. 设置波特率
stty /dev/ttymxc0 115200
→ 内核调用imx_uart_set_termios() → 重新配置UBIR/UBMR寄存器

二、驱动中DMA的使用

1. DMA通道初始化(imx_uart_dma_init

static int imx_uart_dma_init(struct imx_port *sport)
{struct dma_slave_config slave_config = {};struct device *dev = sport->port.dev;int ret;// 1. 请求DMA通道(接收方向)sport->dma_chan_rx = dma_request_slave_channel(dev, "rx");if (!sport->dma_chan_rx) {dev_dbg(dev, "cannot get the DMA channel.\n");ret = -EINVAL;goto err;}// 2. 配置DMA参数(接收方向)slave_config.direction = DMA_DEV_TO_MEM; // 设备到内存slave_config.src_addr = sport->port.mapbase + URXD0; // UART接收寄存器物理地址slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; // 每次传输1字节slave_config.src_maxburst = RXTL_DMA - 1; // 突发传输大小(比FIFO触发级少1)ret = dmaengine_slave_config(sport->dma_chan_rx, &slave_config); // 应用配置// 3. 分配接收缓冲区sport->rx_buf = kzalloc(RX_BUF_SIZE, GFP_KERNEL); // 16页大小if (!sport->rx_buf) {ret = -ENOMEM;goto err;}sport->rx_ring.buf = sport->rx_buf;// 4. 请求并配置发送DMA通道sport->dma_chan_tx = dma_request_slave_channel(dev, "tx");slave_config.direction = DMA_MEM_TO_DEV; // 内存到设备slave_config.dst_addr = sport->port.mapbase + URTX0; // UART发送寄存器物理地址slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;slave_config.dst_maxburst = TXTL_DMA; // 发送FIFO触发级别dmaengine_slave_config(sport->dma_chan_tx, &slave_config);
}
  1. 通道请求

    • dma_request_slave_channel(dev, "rx"):通过设备树或平台数据匹配DMA通道
    • dma_request_slave_channel(dev, "tx"):同上,请求发送通道
  2. DMA配置

    • 方向DMA_DEV_TO_MEM(接收)或 DMA_MEM_TO_DEV(发送)
    • 地址宽度DMA_SLAVE_BUSWIDTH_1_BYTE保证字节对齐传输
    • 突发传输src_maxburst设置DMA突发传输大小,与FIFO触发级别配合
  3. 缓冲区管理

    • rx_buf = kzalloc(RX_BUF_SIZE, GFP_KERNEL):分配连续内存作为DMA缓冲区
    • RX_BUF_SIZE = RX_DMA_PERIODS * PAGE_SIZE / 4:16个周期,每个周期1页/4=4KB

2. DMA发送流程(imx_uart_dma_tx

static void imx_uart_dma_tx(struct imx_port *sport)
{struct circ_buf *xmit = &sport->port.state->xmit;struct scatterlist *sgl = sport->tx_sgl;struct dma_async_tx_descriptor *desc;struct dma_chan	*chan = sport->dma_chan_tx;// 1. 检查是否已有DMA传输if (sport->dma_is_txing)return;// 2. 准备DMA映射sport->tx_bytes = uart_circ_chars_pending(xmit);if (xmit->tail < xmit->head || xmit->head == 0) {// 单个scatterlistsg_init_one(sgl, xmit->buf + xmit->tail, sport->tx_bytes);} else {// 环形缓冲区分两段映射sg_init_table(sgl, 2);sg_set_buf(sgl, xmit->buf + xmit->tail, UART_XMIT_SIZE - xmit->tail);sg_set_buf(sgl + 1, xmit->buf, xmit->head);}// 3. 执行DMA映射ret = dma_map_sg(dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE);// 4. 准备DMA描述符desc = dmaengine_prep_slave_sg(chan, sgl, ret, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT);// 5. 设置回调desc->callback = imx_uart_dma_tx_callback;desc->callback_param = sport;// 6. 提交并启动DMAdmaengine_submit(desc);dma_async_issue_pending(chan);
}
  1. 环形缓冲区分段

    • 当发送缓冲区头尾指针未回绕时,单段映射
    • 当头尾指针回绕时,使用两个scatterlist实现环形缓冲区映射
  2. DMA映射

    • dma_map_sg():将虚拟地址转换为DMA物理地址
    • DMA_TO_DEVICE:传输方向标志
  3. 描述符准备

    • dmaengine_prep_slave_sg():准备scatter-gather DMA传输
    • DMA_PREP_INTERRUPT:要求传输完成时触发中断
  4. 传输启动

    • dmaengine_submit():将描述符加入传输队列
    • dma_async_issue_pending():激活DMA传输

3. DMA发送完成回调(imx_uart_dma_tx_callback

static void imx_uart_dma_tx_callback(void *data)
{struct imx_port *sport = data;struct scatterlist *sgl = &sport->tx_sgl[0];struct circ_buf *xmit = &sport->port.state->xmit;// 1. 解除DMA映射dma_unmap_sg(sport->port.dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE);// 2. 更新发送状态xmit->tail = (xmit->tail + sport->tx_bytes) & (UART_XMIT_SIZE - 1);sport->port.icount.tx += sport->tx_bytes;// 3. 重置DMA状态sport->dma_is_txing = 0;// 4. 唤醒等待的写入进程if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)uart_write_wakeup(&sport->port);// 5. 如果还有数据,重新启动DMAif (!uart_circ_empty(xmit) && !uart_tx_stopped(&sport->port))imx_uart_dma_tx(sport);
}
  1. 传输完成处理

    • 更新发送缓冲区尾指针xmit->tail
    • 增加发送计数icount.tx
  2. 自动重启机制

    • 如果发送缓冲区仍有数据,自动重新启动DMA传输
    • 实现"伪循环"效果(非硬件循环,而是软件层重提交)
  3. 同步与唤醒

    • uart_write_wakeup():唤醒等待的写入进程
    • 保证数据流的连续性

4. DMA接收实现(imx_uart_start_rx_dma

static int imx_uart_start_rx_dma(struct imx_port *sport)
{struct scatterlist *sgl = &sport->rx_sgl;struct dma_chan	*chan = sport->dma_chan_rx;struct device *dev = sport->port.dev;struct dma_async_tx_descriptor *desc;int ret;// 1. 初始化scatterlistsg_init_one(sgl, sport->rx_buf, RX_BUF_SIZE);// 2. 执行DMA映射ret = dma_map_sg(dev, sgl, 1, DMA_FROM_DEVICE);if (ret == 0) {dev_err(dev, "DMA mapping error for RX.\n");return -EINVAL;}// 3. 准备循环DMA描述符desc = dmaengine_prep_dma_cyclic(chan, sg_dma_address(sgl), sg_dma_len(sgl),sg_dma_len(sgl) / sport->rx_periods, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT);// 4. 设置回调desc->callback = imx_uart_dma_rx_callback;desc->callback_param = sport;// 5. 提交并启动DMAsport->dma_is_rxing = 1;sport->rx_cookie = dmaengine_submit(desc);dma_async_issue_pending(chan);return 0;
}
  1. 循环DMA(Cyclic DMA)

    • dmaengine_prep_dma_cyclic():创建循环传输
    • 每当DMA传输到缓冲区末尾时自动回到开头,适合流式接收
  2. 缓冲区管理

    • rx_ring:环形缓冲区跟踪读写指针
    • rx_periods:将缓冲区分成16个周期(RX_DMA_PERIODS
  3. 硬件级优化

    • src_maxburst = RXTL_DMA - 1:突发传输大小比FIFO触发级少1
    • 利用aging timer机制:当字符在FIFO中停留时间过长时触发DMA传输

5. DMA接收回调(imx_uart_dma_rx_callback

static void imx_uart_dma_rx_callback(void *data)
{struct imx_port *sport = data;struct scatterlist *sgl = &sport->rx_sgl;struct tty_port *port = &sport->port.state->port;struct dma_tx_state state;enum dma_status status;unsigned int w_bytes = 0, r_bytes, bd_size;// 1. 获取DMA状态status = dmaengine_tx_status(chan, sport->rx_cookie, &state);// 2. 计算数据位置rx_ring->head = sg_dma_len(sgl) - state.residue; // 当前写入位置bd_size = sg_dma_len(sgl) / sport->rx_periods; // 每个周期大小rx_ring->tail = ((rx_ring->head-1) / bd_size) * bd_size; // 当前读取位置// 3. 数据搬运if (rx_ring->head > rx_ring->tail) {r_bytes = rx_ring->head - rx_ring->tail;dma_sync_sg_for_cpu(); // 同步CPU访问w_bytes = tty_insert_flip_string(); // 搬运到TTY层dma_sync_sg_for_device(); // 重新同步DMA访问}// 4. 提交TTY缓冲区if (w_bytes) {tty_flip_buffer_push(port);dev_dbg(sport->port.dev, "Received %d bytes via DMA\n", w_bytes);}
}
  1. 缓冲区同步

    • dma_sync_sg_for_cpu():CPU访问前同步
    • dma_sync_sg_for_device():DMA访问前重新同步
  2. 数据量计算

    • state.residue:剩余未传输字节数
    • head = total - residue:计算当前写入位置
    • tail = head所在周期的起始位置:确定读取范围
  3. 数据搬运

    • tty_insert_flip_string():将DMA缓冲区数据插入TTY翻转缓冲区
    • tty_flip_buffer_push():提交缓冲区供上层读取

6. DMA引擎核心操作

操作类型函数调用功能说明
通道请求dma_request_slave_channel()获取DMA通道
通道配置dmaengine_slave_config()设置传输方向、地址、突发大小等
单次传输准备dmaengine_prep_slave_sg() 准备scatter-gather DMA传输
循环传输准备dmaengine_prep_dma_cyclic()准备循环DMA传输(用于接收)
映射/解映射dma_map_sg() / dma_unmap_sg()虚拟地址到物理地址映射
同步CPU访问dma_sync_sg_for_cpu()保证CPU访问前数据一致性
同步设备访问dma_sync_sg_for_device()保证DMA访问前数据一致性
提交传输dmaengine_submit()将描述符加入传输队列
启动传输dma_async_issue_pending()激活DMA传输
终止传输dmaengine_terminate_all()终止所有待处理的DMA传输

7. DMA寄存器配置

static void imx_uart_enable_dma(struct imx_port *sport)
{u32 ucr1 = imx_uart_readl(sport, UCR1);ucr1 |= UCR1_RXDMAEN | UCR1_TXDMAEN | UCR1_ATDMAEN; // 启用DMA功能imx_uart_writel(sport, ucr1, UCR1);
}
寄存器位定义功能
UCR1UCR1_RXDMAEN接收DMA使能
UCR1_TXDMAEN发送DMA使能
UCR1_ATDMAEN老化定时器DMA使能(接收)
UFCRUFCR_TXTL发送FIFO触发级别
UFCR_RXTL接收FIFO触发级别

8. DMA中断处理

static irqreturn_t imx_uart_int(int irq, void *dev_id)
{// 1. 读取中断状态usr1 = imx_uart_readl(sport, USR1);usr2 = imx_uart_readl(sport, USR2);// 2. 处理发送完成中断if (usr1 & USR1_TRDY || usr2 & USR2_TXDC) {imx_uart_transmit_buffer(sport);}// 3. 处理接收中断if (usr1 & (USR1_RRDY | USR1_AGTIM)) {__imx_uart_rxint(irq, dev_id);}
}
  1. 发送中断

    • USR1_TRDY:发送缓冲区可写
    • USR2_TXDC:传输完成
  2. 接收中断

    • USR1_RRDY:接收缓冲区准备就绪
    • USR1_AGTIM:老化定时器超时(接收中断)

9. DMA性能优化

  1. 突发传输(Burst Size)

    slave_config.src_maxburst = RXTL_DMA - 1; // 接收突发大小=FIFO触发级-1
    slave_config.dst_maxburst = TXTL_DMA; // 发送突发大小=FIFO触发级
    
    • 保证DMA传输与FIFO触发级别匹配,避免过度中断
  2. 循环接收优化

    #define RX_DMA_PERIODS 16
    #define RX_BUF_SIZE (RX_DMA_PERIODS * PAGE_SIZE / 4)
    
    • 将4KB缓冲区划分为16个周期,每个周期256字节
    • 通过dmaengine_prep_dma_cyclic()实现自动循环
  3. 零拷贝优化

    • 数据直接从DMA缓冲区搬运到TTY层
    • 减少内存拷贝次数

10. DMA错误处理

  1. DMA错误检测

    status = dmaengine_tx_status(chan, sport->rx_cookie, &state);
    if (status == DMA_ERROR) {imx_uart_clear_rx_errors(sport);return;
    }
    
  2. 传输终止

    dmaengine_terminate_sync(sport->dma_chan_tx); // 终止发送
    dmaengine_terminate_sync(sport->dma_chan_rx); // 终止接收
    
  3. 异常处理

    • 溢出处理USR2_ORE标志
    • 帧错误处理USR1_FRAMERR标志
    • 校验错误处理USR1_PARITYERR标志

11. DMA与传统中断模式对比

特性DMA模式中断模式
CPU占用率极低较高
传输效率高(突发传输)低(逐字节中断)
适用场景大数据量传输小数据量或无DMA支持平台
中断频率低(每DMA周期触发)高(每字节触发)
缓冲区管理环形DMA缓冲区内核环形缓冲区
数据搬运次数1次/周期1次/字节

12. DMA传输生命周期

1. 初始化:→ 请求DMA通道→ 配置DMA参数→ 分配缓冲区2. 启动传输:→ 准备描述符→ 提交并启动DMA3. 传输中:→ 硬件DMA搬运数据→ 传输完成触发中断→ 调用回调函数4. 完成处理:→ 同步CPU访问→ 数据搬运到TTY层→ 重新启动DMA(如需要)5. 终止:→ 终止DMA传输→ 释放DMA缓冲区→ 释放DMA通道

13. DMA寄存器配置示例

/* FIFO控制寄存器配置 */
imx_uart_writel(sport, TXTL_DMA << UFCR_TXTL_SHF | RXTL_DMA, UFCR);/* 启用DMA功能 */
ucr1 |= UCR1_RXDMAEN | UCR1_TXDMAEN | UCR1_ATDMAEN;
imx_uart_writel(sport, ucr1, UCR1);
  1. FIFO触发级

    • 发送:TXTL_DMA = 8(8字节触发)
    • 接收:RXTL_DMA = 9(9字节触发)
  2. 老化定时器

    • UCR1_ATDMAEN:启用老化定时器DMA模式
    • 当字符在FIFO中停留时间过长时触发DMA传输

14. DMA缓冲区管理

struct imx_port {// ...void *rx_buf;            // DMA接收缓冲区struct circ_buf rx_ring;  // 环形缓冲区管理// ...
};

环形缓冲区管理:

// 计算当前接收位置
rx_ring.head = sg_dma_len(sgl) - state.residue;
bd_size = sg_dma_len(sgl) / sport->rx_periods;
rx_ring.tail = ((rx_ring.head-1) / bd_size) * bd_size;// 数据量计算
r_bytes = rx_ring.head - rx_ring.tail;

特点:

  1. 硬件级环形:DMA硬件自动循环填充缓冲区
  2. 软件级环形rx_ring跟踪读取位置
  3. 零拷贝:数据直接从DMA缓冲区搬运到TTY层

15. DMA性能调优建议

  1. 调整FIFO触发级

    imx_uart_setup_ufcr(sport, TXTL_DMA, RXTL_DMA);
    
    • 发送:8字节触发
    • 接收:9字节触发(老化定时器触发)
  2. 调整DMA周期数

    #define RX_DMA_PERIODS 16
    #define RX_BUF_SIZE (RX_DMA_PERIODS * PAGE_SIZE / 4)
    
    • 16个周期,每个周期256字节(总4KB)
    • 根据数据流量调整周期数
  3. 优化突发大小

    slave_config.src_maxburst = RXTL_DMA - 1; // 接收突发大小=FIFO触发级-1
    slave_config.dst_maxburst = TXTL_DMA;       // 发送突发大小=FIFO触发级
    
    • 匹配FIFO触发级别,避免DMA与FIFO冲突

16. DMA调试方法

  1. 查看DMA通道状态

    cat /proc/interrupts | grep imx
    
  2. 日志输出

    dev_dbg(sport->port.dev, "TX: %d bytes by DMA\n", w_bytes); // 发送日志
    dev_dbg(sport->port.dev, "RX: %d bytes received\n", w_bytes); // 接收日志
    
  3. 手动触发DMA测试

    echo "DMA test" > /dev/ttymxc0  // 测试发送DMA
    cat /dev/ttymxc0              // 测试接收DMA
    
  4. 错误统计

    sport->port.icount.rx += w_bytes; // 记录接收字节数
    sport->port.icount.overrun++     // 记录溢出错误
    

17. DMA异常处理

static void imx_uart_clear_rx_errors(struct imx_port *sport)
{// 处理接收错误(帧错误、校验错误、溢出等)if (usr2 & USR2_ORE) {sport->port.icount.overrun++;imx_uart_writel(sport, USR2_ORE, USR2);}
}
  1. 溢出处理

    • 清除USR2_ORE标志
    • 增加溢出计数器
  2. 帧错误

    • 清除USR1_FRAMERR标志
    • 增加帧错误计数器
  3. 校验错误

    • 清除USR1_PARITYERR标志
    • 增加校验错误计数器

18. DMA与电源管理

static int imx_uart_suspend(struct device *dev)
{if (sport->dma_is_enabled) {dmaengine_terminate_sync(sport->dma_chan_tx);dmaengine_terminate_sync(sport->dma_chan_rx);}
}static int imx_uart_resume(struct device *dev)
{if (sport->dma_is_enabled) {imx_uart_enable_dma(sport);imx_uart_start_rx_dma(sport);}
}

电源管理要点:

  1. 挂起时

    • 终止所有DMA传输
    • 保存寄存器状态
  2. 恢复时

    • 恢复寄存器状态
    • 重新启动DMA接收

19. DMA性能指标

指标DMA模式中断模式优化空间
CPU占用率<1%5-10%可进一步优化突发大小
传输延迟可调整FIFO触发级
最大传输速率115200+115200可调整DMA周期数
数据完整性一般可增强错误处理

20. DMA调试寄存器

// 读取DMA相关状态寄存器
usr1 = imx_uart_readl(sport, USR1);
usr2 = imx_uart_readl(sport, USR2);// 打印DMA状态
dev_dbg(sport->port.dev, "DMA state: head=%u, tail=%u, residue=%u\n",rx_ring.head, rx_ring.tail, state.residue);
  1. USR1

    • USR1_RRDY:接收就绪
    • USR1_TRDY:发送就绪
  2. USR2

    • USR2_ORE:溢出错误
    • USR2_RDR:接收数据就绪
  3. DMA状态

    • dma_tx_nents:当前传输段数
    • dma_cookie:DMA事务标识

21. DMA典型问题排查

  1. DMA通道获取失败

    • 原因:设备树未配置DMA
    • 解决:检查dtsdma-channels属性
  2. DMA映射失败

    • 原因:内存分配失败或DMA地址越界
    • 解决:检查rx_buf分配和dma_map_sg()返回值
  3. DMA传输失败

    • 原因:DMA配置错误或硬件故障
    • 解决:检查dmaengine_slave_config()返回值
  4. DMA缓冲区溢出

    • 原因:处理速度不足
    • 解决:增大RX_BUF_SIZE或调整FIFO触发级

  通过以上深度解析,可以全面理解该UART驱动中DMA的实现机制。DMA模式显著降低了CPU占用率,特别适合高速串口通信场景。实际开发中,需要根据具体应用场景调整DMA缓冲区大小、FIFO触发级和突发传输大小等参数以获得最佳性能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/94202.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/94202.shtml
英文地址,请注明出处:http://en.pswp.cn/pingmian/94202.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MongoDB 分片集群把非分片集合转成分片集合

记得关注一下博主&#xff0c;博主每天都会更新IT技术&#xff0c;让你有意想不到的小收获哦^_^ 文章目录*记得关注一下博主&#xff0c;博主每天都会更新IT技术&#xff0c;让你有意想不到的小收获哦^_^*一、MongDB集群启停止1、MonogoDB分片集群服务启动顺序(三台主机都要操作…

mybatis过渡到mybatis-plus过程中需要注意的地方

将 MyBatis 升级为 MyBatis-Plus&#xff08;简称 MP&#xff09;是一个平滑过渡的过程&#xff0c;因为 MP 是 MyBatis 的增强工具&#xff08;而非替代&#xff09;&#xff0c;但仍有一些关键注意事项需要关注&#xff0c;以确保升级后功能兼容且能充分利用 MP 的特性&#…

openEuler系统中如何将docker安装在指定目录

在 openEuler 中&#xff0c;Docker 的默认数据存储目录为 /var/lib/docker&#xff08;程序文件通常安装在系统默认路径&#xff0c;一般无需修改&#xff09;。若需将 Docker 数据&#xff08;镜像、容器、卷等&#xff09;存储到指定目录&#xff0c;可通过修改 Docker 配置…

2.4 我国金融市场的监管体制

1、国务院金融发展委员会职责 2、中国人民银行职责

PHP - 实例属性访问与静态方法调用的性能差异解析

观察到了一个看似矛盾的现象&#xff1a;实例属性访问更快&#xff0c;但静态方法调用更快。这实际上是两种不同的操作&#xff0c;下面我将详细解释其中的原理和差异。1. 实例属性访问为什么快访问机制class MyClass {public $instanceProp 1; }$obj new MyClass(); $value …

音视频面试题集锦第 31 期

音视频面试题集锦第 31 期&#xff1a; 1、I 帧、P 帧和 B 帧的概念及区别&#xff1f;2、视频编码中的码率控制技术有哪些&#xff1f;3、音频采样参数有哪些&#xff1f;4、RTMP 和 HLS 协议各有什么特点&#xff1f;如何选择&#xff1f;5、WebRTC 中的 ICE、STUN、TURN 各…

企业视频库管理高效策略

内容概要本文全面探讨企业视频库管理的高效策略&#xff0c;旨在帮助组织优化视频资源处理。首先&#xff0c;我们将介绍企业视频库管理的基本概念和核心价值。接着&#xff0c;深入分析智能分类核心技术如何通过AI算法实现视频自动归类。之后&#xff0c;阐述云集成实现路径&a…

WebSocket和跨域问题

WebSocket 特点 WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议。简单来说&#xff0c;它就像是在客户端和服务器之间建立了一条"专用通道"&#xff0c;双方可以随时主动发送消息给对方&#xff0c;而不需要像HTTP那样总是由客户端发起请求。 同一时间、双向…

微服务-19.什么是网关

一.网关曾经我们的项目是单体项目&#xff0c;前端只需要请求8080端口&#xff0c;就可以获取所有需要的数据和服务并进行渲染。但是拆分成微服务后&#xff0c;会面临几大问题&#xff1a;1.但是现在我们将该单体项目拆分成了微服务项目&#xff0c;每个项目都有自己独立的端口…

从字节码层面剖析以太坊智能合约创建原理

1. 引言 阅读完本文之后&#xff0c;将能理解一下字节码含义&#xff1a; 608060405260405160893803806089833981016040819052601e916025565b600055603d565b600060208284031215603657600080fd5b5051919050565b603f80604a6000396000f3fe6080604052600080fdfea2646970667358221…

typora无需激活版及最新激活版方法!双击安装就能用

介绍 Typora 是一款Markdown编辑器&#xff0c;支持实时预览&#xff0c;所见即所得。跨平台&#xff0c;支持Windows、macOS、Linux。适合写作、笔记、技术文档等。本教程将提供合法安全的安装方案&#xff0c;并解决常见问题&#xff0c;助你高效完成部署&#xff01; 直接…

基于Java、GeoTools与PostGIS的对跖点求解研究

目录 前言 一、对跖点简介 1、地理学定义 2、人生哲学含义 二、对跖点求解 1、Java求解 2、Geotools求解 3、PostGIS求解 4、三种计算方法的对比 5、Leaflet展示对跖点 三、总结 前言 在地理信息系统&#xff08;GIS&#xff09;领域&#xff0c;对跖点&#xff08;A…

Linux-函数的使用-编写监控脚本

Linux-函数的使用-编写监控脚本前言一、监控cpu二、采集内存的使用信息三、采集磁盘和分区的使用信息四、显示进程的信息前言 编写监控脚本实现以下功能 监控cpu&#xff0c;内存&#xff0c;磁盘&#xff0c;进程等信息&#xff0c;每隔5分钟记录这些信息到日志文件里perform…

Authelia:开源双因素认证与单点登录解决方案

项目标题与描述 Authelia是一个开源的认证和授权服务器&#xff0c;专注于为应用程序提供双因素认证&#xff08;2FA&#xff09;和单点登录&#xff08;SSO&#xff09;功能。通过Web门户&#xff0c;Authelia能够作为身份和访问管理&#xff08;IAM&#xff09;系统&#xff…

Apache Ozone 介绍与部署使用(最新版2.0.0)

目录 一、软件介绍 二、软件架构 Ozone Manager&#xff08;OM&#xff09; Storage Container Manager&#xff08;SCM&#xff09; Containers Datanodes Storage Containers Recon Recon 和 Ozone Manager Recon 和 Storage Container Manager 三、安装部署 准备…

Review --- Linux

Review — Linux Linux 是一种开源的类 Unix 操作系统内核&#xff0c;广泛应用于服务器、嵌入式设备和个人计算机中。其核心特点是开源、稳定、安全和高度的可定制性。对于大学毕业生而言&#xff0c;掌握 Linux 的基本操作和原理是进入 IT 行业的重要技能之一。 Linux 的基本…

【msyql 】占用硬盘太大 ,那些文件可以清理

从目录内容来看&#xff0c;这台 MySQL 服务器上主要是 xxl-job 调度平台的数据库。占用空间最大的是&#xff1a;24G xxl_job_log.ibd这个文件是 xxl-job 的任务执行日志表&#xff0c;随着时间推移&#xff0c;日志量会非常大。可以清理的文件和方法1. 清理 xxl_job_log 表数…

58 C++ 现代C++编程艺术7-模板友元

C 现代C编程艺术7-模板友元 文章目录C 现代C编程艺术7-模板友元一、基础应用场景 &#x1f9e9;1. 模板类声明友元函数2. 普通类声明模板函数为友元二、模板类互访场景 ⚙️1. 同类模板互访&#xff08;一对一&#xff09;2. 异类模板互访&#xff08;多对多&#xff09;三、高…

Undertow —— JBOSS 的社区版,redhat 下场维护的开源项目,顶顶好用的 Java web server

Undertow JBoss Community Undertow Undertow is a flexible performant web server written in java, providing both blocking and non-blocking API’s based on NIO. Undertow 是一个用 Java 编写的灵活高性能 Web 服务器&#xff0c;提供基于 NIO 的阻塞和非阻塞 API。…

【AI智能体】Dify 搭建业务单据差异核对助手实战详解

目录 一、前言 二、Dify介绍 2.1 Dify 是什么 2.2 Dify 核心特性 2.2.1 Dify特点 2.2.2 Dify 多模型支持 2.2.3 Dify 适应场景 2.2.4 基于Dify 搭建发票识别应用优势 三、Dify 搭建业务单据核对助手实战过程 3.1 前置准备 3.1.1 安装必要的插件 3.2 完整操作步骤 3…