Linux驅動之DMA(三)

目錄

    • 一、驅動內容
      • 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…