usb serial port 驱动_Linux驱动分析之Uart驱动

51954ea52ff5f0b5f28897a45282a4d6.gif

喜欢就关注我们吧!

前言 9e5090934af9cda70a544643766fc478.png

    之前对Uart驱动的整体架构做了介绍,现在来分析具体的驱动程序。我们以NXP 的 IMX6来进行分析。

Uart驱动分析 9e5090934af9cda70a544643766fc478.png

内核:4.20

芯片:NXP IMX6

下面的代码分析主要都在注释中,会按照驱动中函数的执行顺序分析。

//dts匹配表static const struct of_device_id imx_uart_dt_ids[] = {  { .compatible = "fsl,imx6q-uart", .data = &imx_uart_devdata[IMX6Q_UART], },  { .compatible = "fsl,imx53-uart", .data = &imx_uart_devdata[IMX53_UART], },  { .compatible = "fsl,imx1-uart", .data = &imx_uart_devdata[IMX1_UART], },  { .compatible = "fsl,imx21-uart", .data = &imx_uart_devdata[IMX21_UART], },  { /* sentinel */ }};static struct uart_driver imx_uart_uart_driver = {  .owner          = THIS_MODULE,  .driver_name    = DRIVER_NAME,  .dev_name       = DEV_NAME, //设备节点名  .major          = SERIAL_IMX_MAJOR, //主设备号  .minor          = MINOR_START, //次设备号  .nr             = ARRAY_SIZE(imx_uart_ports), //串口数  .cons           = IMX_CONSOLE,};static struct platform_driver imx_uart_platform_driver = {  .probe = imx_uart_probe, //driver和device匹配后回调  .remove = imx_uart_remove,  .id_table = imx_uart_devtype,  .driver = {    .name = "imx-uart",    .of_match_table = imx_uart_dt_ids,    .pm = &imx_uart_pm_ops,  },};//加载函数static int __init imx_uart_init(void){    //注册uart_driver  int ret = uart_register_driver(&imx_uart_uart_driver);  //注册platform_driver  ret = platform_driver_register(&imx_uart_platform_driver);  return ret;}//卸载函数static void __exit imx_uart_exit(void){    //注销uart_driver和platform_driver  platform_driver_unregister(&imx_uart_platform_driver);  uart_unregister_driver(&imx_uart_uart_driver);}module_init(imx_uart_init);module_exit(imx_uart_exit);

上面真正回调probe的是匹配platform_driver, 而不是uart_driver。所以我们会看到调用了uart_register_driver 和 platform_driver_register 。

uart_register_driver是为了向uart核心层注册。

(2) probe()函数

static int imx_uart_probe(struct platform_device *pdev){  struct imx_port *sport; //nxp对uart_port进行了封装,添加自己的成员  void __iomem *base;  int ret = 0;  u32 ucr1;  struct resource *res;  int txirq, rxirq, rtsirq;    //分配内存,并清0  sport = devm_kzalloc(&pdev->dev, sizeof(*sport), GFP_KERNEL);  if (!sport)    return -ENOMEM;    //解析设备树,保存到imx_port  ret = imx_uart_probe_dt(sport, pdev);  if (ret > 0)    imx_uart_probe_pdata(sport, pdev);  else if (ret < 0)    return ret;    //省略....    //获取IO资源,并映射  res = platform_get_resource(pdev, IORESOURCE_MEM, 0);  base = devm_ioremap_resource(&pdev->dev, res);    //省略....    //获取RX,TX,RTS中断号  rxirq = platform_get_irq(pdev, 0);  txirq = platform_get_irq(pdev, 1);  rtsirq = platform_get_irq(pdev, 2);    //填充imx_port结构体  sport->port.dev = &pdev->dev;  sport->port.mapbase = res->start; //映射地址  sport->port.membase = base; //物理地址  sport->port.type = PORT_IMX,  sport->port.iotype = UPIO_MEM;  sport->port.irq = rxirq; //接收中断  sport->port.fifosize = 32;  sport->port.ops = &imx_uart_pops; //串口操作函数  sport->port.rs485_config = imx_uart_rs485_config; //485配置  sport->port.flags = UPF_BOOT_AUTOCONF;  timer_setup(&sport->timer, imx_uart_timeout, 0); //设置定时器  sport->gpios = mctrl_gpio_init(&sport->port, 0);  if (IS_ERR(sport->gpios))    return PTR_ERR(sport->gpios);    //获取IPG时钟  sport->clk_ipg = devm_clk_get(&pdev->dev, "ipg");  //省略....   //获取PER时钟  sport->clk_per = devm_clk_get(&pdev->dev, "per");  //省略....  sport->port.uartclk = clk_get_rate(sport->clk_per);    //使能IPG时钟  ret = clk_prepare_enable(sport->clk_ipg);  //省略....   //读取寄存器值  sport->ucr1 = readl(sport->port.membase + UCR1);  sport->ucr2 = readl(sport->port.membase + UCR2);  sport->ucr3 = readl(sport->port.membase + UCR3);  sport->ucr4 = readl(sport->port.membase + UCR4);  sport->ufcr = readl(sport->port.membase + UFCR);  uart_get_rs485_mode(&pdev->dev, &sport->port.rs485);    //省略....  imx_uart_rs485_config(&sport->port, &sport->port.rs485);    //下面都是对寄存器的配置,可以查看datasheet  ucr1 = imx_uart_readl(sport, UCR1);  ucr1 &= ~(UCR1_ADEN | UCR1_TRDYEN | UCR1_IDEN | UCR1_RRDYEN |     UCR1_TXMPTYEN | UCR1_RTSDEN);  imx_uart_writel(sport, ucr1, UCR1);  if (!imx_uart_is_imx1(sport) && sport->dte_mode) {    u32 ufcr = imx_uart_readl(sport, UFCR);    if (!(ufcr & UFCR_DCEDTE))      imx_uart_writel(sport, ufcr | UFCR_DCEDTE, UFCR);    imx_uart_writel(sport,        IMX21_UCR3_RXDMUXSEL | UCR3_ADNIMP | UCR3_DSR,        UCR3);  } else {    u32 ucr3 = UCR3_DSR;    u32 ufcr = imx_uart_readl(sport, UFCR);    if (ufcr & UFCR_DCEDTE)      imx_uart_writel(sport, ufcr & ~UFCR_DCEDTE, UFCR);    if (!imx_uart_is_imx1(sport))      ucr3 |= IMX21_UCR3_RXDMUXSEL | UCR3_ADNIMP;    imx_uart_writel(sport, ucr3, UCR3);  }  clk_disable_unprepare(sport->clk_ipg);    //申请中断  if (txirq > 0) { //开启tx中断    ret = devm_request_irq(&pdev->dev, rxirq, imx_uart_rxint, 0,               dev_name(&pdev->dev), sport);    //省略.....    ret = devm_request_irq(&pdev->dev, txirq, imx_uart_txint, 0,               dev_name(&pdev->dev), sport);    //省略.....    ret = devm_request_irq(&pdev->dev, rtsirq, imx_uart_rtsint, 0,               dev_name(&pdev->dev), sport);    //省略.....  } else { //不开tx中断    ret = devm_request_irq(&pdev->dev, rxirq, imx_uart_int, 0,               dev_name(&pdev->dev), sport);    //省略.....  }   //保存imx_port  imx_uart_ports[sport->port.line] = sport;  platform_set_drvdata(pdev, sport);   //关联uart_driver和uart_port  return uart_add_one_port(&imx_uart_uart_driver, &sport->port);}

上面其实主要是寄存器配置,中断申请,最后添加port。对裸机程序熟悉的,应该能很轻松的理解,因为我们不是为了针对某款芯片,所以寄存器配置可以忽略,主要还是为了理解Uart的驱动框架。

(3) 串口操作函数(uart_ops)

static const struct uart_ops imx_uart_pops = {  .tx_empty  = imx_uart_tx_empty,  .set_mctrl  = imx_uart_set_mctrl,  .get_mctrl  = imx_uart_get_mctrl,  .stop_tx  = imx_uart_stop_tx,  .start_tx  = imx_uart_start_tx,  .stop_rx  = imx_uart_stop_rx,  .enable_ms  = imx_uart_enable_ms,  .break_ctl  = imx_uart_break_ctl,  .startup  = imx_uart_startup,  .shutdown  = imx_uart_shutdown,  .flush_buffer  = imx_uart_flush_buffer,  .set_termios  = imx_uart_set_termios, //对串口进行配置  .type    = imx_uart_type,  .config_port  = imx_uart_config_port,  .verify_port  = imx_uart_verify_port,#if defined(CONFIG_CONSOLE_POLL)  .poll_init      = imx_uart_poll_init,  .poll_get_char  = imx_uart_poll_get_char,  .poll_put_char  = imx_uart_poll_put_char,#endif};

上面的操作函数都是对具体芯片(IMX)的寄存器进行配置。需要根据具体的芯片手册来进行实现。我们简单看几个函数。

  • imx_uart_set_termios --- 配置串口

static voidimx_uart_set_termios(struct uart_port *port, struct ktermios *termios,         struct ktermios *old){  struct imx_port *sport = (struct imx_port *)port;  unsigned long flags;  u32 ucr2, old_ucr1, old_ucr2, ufcr;  unsigned int baud, quot;  unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8;  unsigned long div;  unsigned long num, denom;  uint64_t tdiv64;    //设置数据位  while ((termios->c_cflag & CSIZE) != CS7 &&         (termios->c_cflag & CSIZE) != CS8) {    termios->c_cflag &= ~CSIZE;    termios->c_cflag |= old_csize;    old_csize = CS8;  }  if ((termios->c_cflag & CSIZE) == CS8)    ucr2 = UCR2_WS | UCR2_SRST | UCR2_IRTS;  else    ucr2 = UCR2_SRST | UCR2_IRTS;    //省略.....    //设置停止位  if (termios->c_cflag & CSTOPB)    ucr2 |= UCR2_STPB;  if (termios->c_cflag & PARENB) {    ucr2 |= UCR2_PREN;    if (termios->c_cflag & PARODD)      ucr2 |= UCR2_PROE;  }  del_timer_sync(&sport->timer);  //设置波特率  baud = uart_get_baud_rate(port, termios, old, 50, port->uartclk / 16);  quot = uart_get_divisor(port, baud);  spin_lock_irqsave(&sport->port.lock, flags);    //设置奇偶校验  sport->port.read_status_mask = 0;  if (termios->c_iflag & INPCK)    sport->port.read_status_mask |= (URXD_FRMERR | URXD_PRERR);  if (termios->c_iflag & (BRKINT | PARMRK))    sport->port.read_status_mask |= URXD_BRK;    //省略.....    //关闭中断  old_ucr1 = imx_uart_readl(sport, UCR1);  imx_uart_writel(sport,      old_ucr1 & ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN),      UCR1);  old_ucr2 = imx_uart_readl(sport, UCR2);  imx_uart_writel(sport, old_ucr2 & ~UCR2_ATEN, UCR2);  while (!(imx_uart_readl(sport, USR2) & USR2_TXDC))    barrier();  /* then, disable everything */  imx_uart_writel(sport, old_ucr2 & ~(UCR2_TXEN | UCR2_RXEN | UCR2_ATEN), UCR2);  old_ucr2 &= (UCR2_TXEN | UCR2_RXEN | UCR2_ATEN);  //计算波特率值  div = sport->port.uartclk / (baud * 16);  if (baud == 38400 && quot != div)    baud = sport->port.uartclk / (quot * 16);  div = sport->port.uartclk / (baud * 16);  if (div > 7)    div = 7;  if (!div)    div = 1;  rational_best_approximation(16 * div * baud, sport->port.uartclk,    1 << 16, 1 << 16, &num, &denom);  tdiv64 = sport->port.uartclk;  tdiv64 *= num;  do_div(tdiv64, denom * 16 * div);  tty_termios_encode_baud_rate(termios,        (speed_t)tdiv64, (speed_t)tdiv64);  num -= 1;  denom -= 1;   //对上面的设置写入到寄存器中  ufcr = imx_uart_readl(sport, UFCR);  ufcr = (ufcr & (~UFCR_RFDIV)) | UFCR_RFDIV_REG(div);  imx_uart_writel(sport, ufcr, UFCR);  imx_uart_writel(sport, num, UBIR);  imx_uart_writel(sport, denom, UBMR);  if (!imx_uart_is_imx1(sport))    imx_uart_writel(sport, sport->port.uartclk / div / 1000,        IMX21_ONEMS);  imx_uart_writel(sport, old_ucr1, UCR1);  /* set the parity, stop bits and data size */  imx_uart_writel(sport, ucr2 | old_ucr2, UCR2);  if (UART_ENABLE_MS(&sport->port, termios->c_cflag))    imx_uart_enable_ms(&sport->port);  spin_unlock_irqrestore(&sport->port.lock, flags);}

应用层是通过struct termios来设置串口,传到底层就是struct ktermios。通过解析设置参数,然后配置对应的寄存器。

  • imx_uart_start_tx --- 串口发送

static void imx_uart_start_tx(struct uart_port *port){  struct imx_port *sport = (struct imx_port *)port;  u32 ucr1;    //判断是否有高优先级数据和环形buffer是否有数据  if (!sport->port.x_char && uart_circ_empty(&port->state->xmit))    return;    //省略......    //没有开启DMA,则使用Tx中断  if (!sport->dma_is_enabled) {            //触发Tx中断    ucr1 = imx_uart_readl(sport, UCR1);    imx_uart_writel(sport, ucr1 | UCR1_TXMPTYEN, UCR1);  }  if (sport->dma_is_enabled) {    if (sport->port.x_char) {      //有高优先级的数据要发送,则使用Tx中断,关闭DMA      ucr1 = imx_uart_readl(sport, UCR1);      ucr1 &= ~UCR1_TXDMAEN;      ucr1 |= UCR1_TXMPTYEN;      imx_uart_writel(sport, ucr1, UCR1);      return;    }    //环形buffer有数据,并且串口没有停止,则使用DMA进行发送    if (!uart_circ_empty(&port->state->xmit) &&        !uart_tx_stopped(port))      imx_uart_dma_tx(sport); //DMA发送    return;  }}

使用Tx中断进行发送或DMA进行发送。

  • imx_uart_rxint --- Rx中断处理函数

static irqreturn_t imx_uart_rxint(int irq, void *dev_id){  struct imx_port *sport = dev_id;  unsigned int rx, flg, ignored = 0;  struct tty_port *port = &sport->port.state->port;  spin_lock(&sport->port.lock);  while (imx_uart_readl(sport, USR2) & USR2_RDR) {    u32 usr2;    flg = TTY_NORMAL;    sport->port.icount.rx++;    rx = imx_uart_readl(sport, URXD0);    usr2 = imx_uart_readl(sport, USR2);    if (usr2 & USR2_BRCD) {      imx_uart_writel(sport, USR2_BRCD, USR2);      if (uart_handle_break(&sport->port))        continue;    }            //省略......    if (sport->port.ignore_status_mask & URXD_DUMMY_READ)      goto out;    //添加到tty核心层    if (tty_insert_flip_char(port, rx, flg) == 0)      sport->port.icount.buf_overrun++;  }out:  spin_unlock(&sport->port.lock);  tty_flip_buffer_push(port); //push给tty核心层  return IRQ_HANDLED;}

接收中断就是将收到的数据发送给tty核心层,让它去进行缓存。

总结 9e5090934af9cda70a544643766fc478.png

    上面芯片相关的可以跳着看,我们主要是去看Uart驱动的套路。学习驱动就是在学习套路,掌握了套路,它们就会变成模板了。可以和之前的《Linux驱动分析之Uart驱动架构》一起看。

2b6d4fb8442e5d39d6dd902c3dc9cba8.png 4f71df774ec523ac4dd2abe86dc075c9.png精彩推荐Linux驱动分析之Uart驱动架构如何提高C编程能力必知必会的TCP/IP知识好文!必须在看
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值