UART控制器驱动流程

                                     UART控制器驱动流程

UART控制器也属于片上设备, 因此它的流程也如其他的片上设备类似,流程可参考《2410soc上的设备的驱动流程(RTC, watchdog)》。首先是静态的初始化好这个设备的相关信息,包括中断号,寄存器地址等 arch/arm/mach-s3c2410/devs.c

/* Serial port registrations */

static struct resource s3c2410_uart0_resource[] = {

       [0] = {

              .start = S3C2410_PA_UART0,  /*UART0的寄存器地址*/

              .end   = S3C2410_PA_UART0 + 0x3fff,

              .flags = IORESOURCE_MEM,

       },

       [1] = {

              .start = IRQ_S3CUART_RX0,  /*UART0的中断号*/

              .end   = IRQ_S3CUART_ERR0,

              .flags = IORESOURCE_IRQ,

       }

};

static struct resource s3c2410_uart1_resource[] = {

       [0] = {

              .start = S3C2410_PA_UART1,

              .end   = S3C2410_PA_UART1 + 0x3fff,

              .flags = IORESOURCE_MEM,

       },

       [1] = {

              .start = IRQ_S3CUART_RX1,

              .end   = IRQ_S3CUART_ERR1,

              .flags = IORESOURCE_IRQ,

       }

};

 

static struct resource s3c2410_uart2_resource[] = {

       [0] = {

              .start = S3C2410_PA_UART2,

              .end   = S3C2410_PA_UART2 + 0x3fff,

              .flags = IORESOURCE_MEM,

       },

       [1] = {

              .start = IRQ_S3CUART_RX2,

              .end   = IRQ_S3CUART_ERR2,

              .flags = IORESOURCE_IRQ,

       }

};

struct s3c24xx_uart_resources s3c2410_uart_resources[] __initdata = {  /*资源数组*/

       [0] = {

              .resources      = s3c2410_uart0_resource,

              .nr_resources  = ARRAY_SIZE(s3c2410_uart0_resource),

       },

       [1] = {

              .resources      = s3c2410_uart1_resource,

              .nr_resources  = ARRAY_SIZE(s3c2410_uart1_resource),

       },

       [2] = {

              .resources      = s3c2410_uart2_resource,

              .nr_resources  = ARRAY_SIZE(s3c2410_uart2_resource),

       },

};

 

/* yart devices */

static struct platform_device s3c24xx_uart_device0 = {

       .id          = 0,

};

 

static struct platform_device s3c24xx_uart_device1 = {

       .id          = 1,

};

 

static struct platform_device s3c24xx_uart_device2 = {

       .id          = 2,

};

 

struct platform_device *s3c24xx_uart_src[3] = {

       &s3c24xx_uart_device0,

       &s3c24xx_uart_device1,

       &s3c24xx_uart_device2,

};

 

 

首先看一下驱动的init函数:

/* module initialisation code */

 

static int __init s3c24xx_serial_modinit(void)

{

   int ret;

 

    /*注册一个UART驱动,以后会手动关联到相应的设备*/

   ret = uart_register_driver(&s3c24xx_uart_drv);

   if (ret < 0) {

      printk(KERN_ERR "failed to register UART driver/n");

      return -1;

   }

 

    /*注册一个代表UARTplatform_driver*/

   s3c2400_serial_init(); 

   s3c2410_serial_init();

   s3c2412_serial_init();

   s3c2440_serial_init();

 

   return 0;

}

static inline int s3c2400_serial_init(void)

{

   return s3c24xx_serial_init(&s3c2400_serial_drv, &s3c2400_uart_inf);

}

static int s3c24xx_serial_init(struct platform_driver *drv,

                 struct s3c24xx_uart_info *info)

{

   dbg("s3c24xx_serial_init(%p,%p)/n", drv, info);

   return platform_driver_register(drv);

}

可以看到init函数仅仅是注册了两个驱动对象, s3c24xx_uart_drv代表UART系统模型中的对象,用于将UART设备和UARTops关联起来, 而另一个驱动对象即platform_driver对象则在系统中代表UART驱动实体, 当系统在匹配到合适的设备后会调用它的probe函数。

我们重点看下platform_driver对象

static struct platform_driver s3c2400_serial_drv = {

   .probe    = s3c2400_serial_probe,    /*probe函数*/

   .remove      = s3c24xx_serial_remove,

   .suspend  = s3c24xx_serial_suspend,

   .resume      = s3c24xx_serial_resume,

   .driver      = {

      .name = "s3c2400-uart",

      .owner = THIS_MODULE,

   },

};

当系统匹配到UART后,接着调用s3c2400_serial_probe

static int s3c2400_serial_probe(struct platform_device *dev)

{

   return s3c24xx_serial_probe(dev, &s3c2400_uart_inf);

}

 

/* Device driver serial port probe */

 

static int probe_index = 0;

 

static int s3c24xx_serial_probe(struct platform_device *dev,

             struct s3c24xx_uart_info *info)

{

   struct s3c24xx_uart_port *ourport;

   int ret;

 

   dbg("s3c24xx_serial_probe(%p, %p) %d/n", dev, info, probe_index);

 

    /*找到静态定义好的相关UART口的信息*/

   ourport = &s3c24xx_serial_ports[probe_index]; 

   probe_index++;

 

   dbg("%s: initialising port %p.../n", __FUNCTION__, ourport);

   

    /*初始化UART硬件接口*/

   ret = s3c24xx_serial_init_port(ourport, info, dev);

   if (ret < 0)

      goto probe_err;

 

   dbg("%s: adding port/n", __FUNCTION__);

    

    /*关联uart_driver和这个设备,即让上层找到uart驱动层的fops*/

   uart_add_one_port(&s3c24xx_uart_drv, &ourport->port);

   platform_set_drvdata(dev, &ourport->port);  /*存数据*/

 

   return 0;

 

 probe_err:

   return ret;

}

Probe函数一般就是初始化硬件设备,并关联好fops等操作。

static struct s3c24xx_uart_port s3c24xx_serial_ports[NR_PORTS] = {

   [0] = {

      .port = {

          .lock     = SPIN_LOCK_UNLOCKED,

          .iotype      = UPIO_MEM,

          .irq      = IRQ_S3CUART_RX0,

          .uartclk  = 0,

          .fifosize = 16,

          .ops      = &s3c24xx_serial_ops,  /*这个就是uart在驱动层的操作函数*/

          .flags    = UPF_BOOT_AUTOCONF,

          .line     = 0,

      }

   },

   [1] = {

      .port = {

          .lock     = SPIN_LOCK_UNLOCKED,

          .iotype      = UPIO_MEM,

          .irq      = IRQ_S3CUART_RX1,

          .uartclk  = 0,

          .fifosize = 16,

          .ops      = &s3c24xx_serial_ops,

          .flags    = UPF_BOOT_AUTOCONF,

          .line     = 1,

      }

   },

#if NR_PORTS > 2

 

   [2] = {

      .port = {

          .lock     = SPIN_LOCK_UNLOCKED,

          .iotype      = UPIO_MEM,

          .irq      = IRQ_S3CUART_RX2,

          .uartclk  = 0,

          .fifosize = 16,

          .ops      = &s3c24xx_serial_ops,

          .flags    = UPF_BOOT_AUTOCONF,

          .line     = 2,

      }

   }

#endif

};

这个数组为每个口保存信息。

/* s3c24xx_serial_init_port

 *

 * initialise a single serial port from the platform device given

 */

 

static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport,

                 struct s3c24xx_uart_info *info,

                 struct platform_device *platdev)

{

   struct uart_port *port = &ourport->port;

   struct s3c2410_uartcfg *cfg;

   struct resource *res;

 

   dbg("s3c24xx_serial_init_port: port=%p, platdev=%p/n", port, platdev);

 

   if (platdev == NULL)

      return -ENODEV;

 

   cfg = s3c24xx_dev_to_cfg(&platdev->dev);  /*获取UART的配置信息*/

 

   if (port->mapbase != 0)

      return 0;

 

   if (cfg->hwport > 3)

      return -EINVAL;

 

   /* setup info for port */

   port->dev = &platdev->dev;

   ourport->info   = info;

 

   /* copy the info in from provided structure */

   ourport->port.fifosize = info->fifosize;

 

   dbg("s3c24xx_serial_init_port: %p (hw %d).../n", port, cfg->hwport);

 

   port->uartclk = 1;

 

   if (cfg->uart_flags & UPF_CONS_FLOW) {

      dbg("s3c24xx_serial_init_port: enabling flow control/n");

      port->flags |= UPF_CONS_FLOW;

   }

 

   /* sort our the physical and virtual addresses for each UART */

    /*获取UART的资源信息,上面讲过*/

   res = platform_get_resource(platdev, IORESOURCE_MEM, 0);

   if (res == NULL) {

      printk(KERN_ERR "failed to find memory resource for uart/n");

      return -EINVAL;

   }

 

   dbg("resource %p (%lx..%lx)/n", res, res->start, res->end);

 

   port->mapbase   = res->start;

   port->membase   = S3C24XX_VA_UART + (res->start - S3C24XX_PA_UART);

   port->irq = platform_get_irq(platdev, 0);

   if (port->irq < 0)

      port->irq = 0;

 

   ourport->clk = clk_get(&platdev->dev, "uart");

 

   dbg("port: map=%08x, mem=%08x, irq=%d, clock=%ld/n",

       port->mapbase, port->membase, port->irq, port->uartclk);

 

   /* reset the fifos (and setup the uart) */

   s3c24xx_serial_resetport(port, cfg); /*硬件初始化*/

   return 0;

}

这个函数获取并初始化好UART硬件,并为驱动操作硬件做好准备。

最后我们看一下s3c24xx_serial_ops

static struct uart_ops s3c24xx_serial_ops = {

   .pm   = s3c24xx_serial_pm,

   .tx_empty = s3c24xx_serial_tx_empty,

   .get_mctrl   = s3c24xx_serial_get_mctrl,

   .set_mctrl   = s3c24xx_serial_set_mctrl,

   .stop_tx  = s3c24xx_serial_stop_tx,

   .start_tx = s3c24xx_serial_start_tx,

   .stop_rx  = s3c24xx_serial_stop_rx,

   .enable_ms   = s3c24xx_serial_enable_ms,

   .break_ctl   = s3c24xx_serial_break_ctl,

   .startup  = s3c24xx_serial_startup,

   .shutdown = s3c24xx_serial_shutdown,

   .set_termios = s3c24xx_serial_set_termios,

   .type     = s3c24xx_serial_type,

   .release_port   = s3c24xx_serial_release_port,

   .request_port   = s3c24xx_serial_request_port,

   .config_port = s3c24xx_serial_config_port,

   .verify_port = s3c24xx_serial_verify_port,

};

这个就是驱动层的UART操作函数集。

当上面的probe函数完成后, UART口就处于就绪状态,用户可以通过设备文件来打开,读写这个设备,而相关的操作函数最终会调用这里定义的驱动层的操作函数。 而驱动要做的就是完成这些接口, 具体就不叙述了, 可以参考代码来获取深入的了解。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值