S3C2440驱动简析——串口驱动

来源:互联网 发布:中美俄三国关系知乎 编辑:程序博客网 时间:2024/05/12 14:19

S3C2440驱动简析——串口驱动

     对于驱动的学习停歇了几乎一周的时间,期间忙于补习Linux应用编程和搜索驱动、内核相关书籍,以便之后更进一步地学习。在之前友善提供的驱动例程里面,涉及的知识面非常有限,需要研究更多的驱动源码,了解更多的驱动知识,是当务之急。研究别人代码的同时,当然不忘自己也要动手练习。以下贴出串口驱动程序,并在程序里附上简要注释。

 

[c-sharp] view plaincopy
  1. /* linux/drivers/serial/s3c2440.c 
  2.  * 
  3.  * Driver for Samsung S3C2440 and S3C2442 SoC onboard UARTs. 
  4.  * 
  5.  * Ben Dooks, Copyright (c) 2003-2005,2008 Simtec Electronics 
  6.  *  http://armlinux.simtec.co.uk/ 
  7.  * 
  8.  * This program is free software; you can redistribute it and/or modify 
  9.  * it under the terms of the GNU General Public License version 2 as 
  10.  * published by the Free Software Foundation. 
  11. */  
  12.  
  13. #include <linux/module.h>  
  14. #include <linux/ioport.h>  
  15. #include <linux/io.h>  
  16. #include <linux/platform_device.h>  
  17. #include <linux/init.h>  
  18. #include <linux/serial_core.h>  
  19. #include <linux/serial.h>  
  20.  
  21. #include <asm/irq.h>  
  22. #include <mach/hardware.h>  
  23.  
  24. #include <plat/regs-serial.h>  
  25. #include <mach/regs-gpio.h>  
  26.  
  27. #include "samsung.h"  
  28.   
  29.   
  30. static int s3c2440_serial_setsource(struct uart_port *port,  
  31.                      struct s3c24xx_uart_clksrc *clk)  
  32. {             //本函数选定串口端口和时钟源  
  33.     unsigned long ucon = rd_regl(port, S3C2410_UCON);   //读取寄存器UCON  
  34.   
  35.     /* todo - proper fclk<>nonfclk switch. */  
  36.   
  37.     ucon &= ~S3C2440_UCON_CLKMASK;    //#define S3C2440_UCON_CLKMASK (3<<10)  
  38.   
  39.     if (strcmp(clk->name, "uclk") == 0)           //选择时钟源  
  40.         ucon |= S3C2440_UCON_UCLK;  
  41.     else if (strcmp(clk->name, "pclk") == 0)  
  42.         ucon |= S3C2440_UCON_PCLK;  
  43.     else if (strcmp(clk->name, "fclk") == 0)  
  44.         ucon |= S3C2440_UCON_FCLK;  
  45.     else {  
  46.         printk(KERN_ERR "unknown clock source %s/n", clk->name);  
  47.         return -EINVAL;  
  48.     }  
  49.   
  50.     wr_regl(port, S3C2410_UCON, ucon);          //把设置过的ucon写回串口控制寄存器  
  51.     return 0;  
  52. }  
  53.   
  54.   
  55. static int s3c2440_serial_getsource(struct uart_port *port,  
  56.                     struct s3c24xx_uart_clksrc *clk)  
  57. {                 //设置时钟源和对应预分频值  
  58.     unsigned long ucon = rd_regl(port, S3C2410_UCON);  
  59.     unsigned long ucon0, ucon1, ucon2;  
  60.   
  61.     switch (ucon & S3C2440_UCON_CLKMASK) {  
  62.     case S3C2440_UCON_UCLK:  
  63.         clk->divisor = 1;  
  64.         clk->name = "uclk";  
  65.         break;  
  66.   
  67.     case S3C2440_UCON_PCLK:  
  68.     case S3C2440_UCON_PCLK2:  
  69.         clk->divisor = 1;  
  70.         clk->name = "pclk";  
  71.         break;  
  72.   
  73.     case S3C2440_UCON_FCLK:  
  74.         /* the fun of calculating the uart divisors on 
  75.          * the s3c2440 */  
  76.   
  77.         ucon0 = __raw_readl(S3C24XX_VA_UART0 + S3C2410_UCON);  
  78.         ucon1 = __raw_readl(S3C24XX_VA_UART1 + S3C2410_UCON);  
  79.         ucon2 = __raw_readl(S3C24XX_VA_UART2 + S3C2410_UCON);  
  80.   
  81.         printk("ucons: %08lx, %08lx, %08lx/n", ucon0, ucon1, ucon2);  
  82.   
  83.         ucon0 &= S3C2440_UCON0_DIVMASK;  
  84.         ucon1 &= S3C2440_UCON1_DIVMASK;  
  85.         ucon2 &= S3C2440_UCON2_DIVMASK;  
  86.   
  87.         if (ucon0 != 0) {  
  88.             clk->divisor = ucon0 >> S3C2440_UCON_DIVSHIFT;  
  89.             clk->divisor += 6;  
  90.         } else if (ucon1 != 0) {  
  91.             clk->divisor = ucon1 >> S3C2440_UCON_DIVSHIFT;  
  92.             clk->divisor += 21;  
  93.         } else if (ucon2 != 0) {  
  94.             clk->divisor = ucon2 >> S3C2440_UCON_DIVSHIFT;  
  95.             clk->divisor += 36;  
  96.         } else {  
  97.             /* manual calims 44, seems to be 9 */  
  98.             clk->divisor = 9;  
  99.         }  
  100.   
  101.         clk->name = "fclk";  
  102.         break;  
  103.     }  
  104.   
  105.     return 0;  
  106. }  
  107.   
  108. static int s3c2440_serial_resetport(struct uart_port *port,  
  109.                     struct s3c2410_uartcfg *cfg)  
  110. {             //重设串口  
  111.     unsigned long ucon = rd_regl(port, S3C2410_UCON);  
  112.   
  113.     dbg("s3c2440_serial_resetport: port=%p (%08lx), cfg=%p/n",  
  114.         port, port->mapbase, cfg);  
  115.   
  116.     /* ensure we don't change the clock settings... */  
  117.   
  118.     ucon &= (S3C2440_UCON0_DIVMASK | (3<<10));                
  119.   
  120.     wr_regl(port, S3C2410_UCON,  ucon | cfg->ucon);   //重新设置寄存器UCON  
  121.     wr_regl(port, S3C2410_ULCON, cfg->ulcon);         //重新设置寄存器ULCON  
  122.   
  123.     /* reset both fifos */  
  124.   
  125.     wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH);  //重启fifo  
  126.     wr_regl(port, S3C2410_UFCON, cfg->ufcon);         //重新设定寄存器UFCON  
  127.   
  128.     return 0;  
  129. }  
  130.   
  131. static struct s3c24xx_uart_info s3c2440_uart_inf = {      //串口设备环境信息和提供的操作函数  
  132.     .name       = "Samsung S3C2440 UART",  
  133.     .type       = PORT_S3C2440,  
  134.     .fifosize   = 64,                           
  135.     .rx_fifomask    = S3C2440_UFSTAT_RXMASK,  
  136.     .rx_fifoshift   = S3C2440_UFSTAT_RXSHIFT,  
  137.     .rx_fifofull    = S3C2440_UFSTAT_RXFULL,  
  138.     .tx_fifofull    = S3C2440_UFSTAT_TXFULL,  
  139.     .tx_fifomask    = S3C2440_UFSTAT_TXMASK,  
  140.     .tx_fifoshift   = S3C2440_UFSTAT_TXSHIFT,  
  141.     .get_clksrc = s3c2440_serial_getsource,  
  142.     .set_clksrc = s3c2440_serial_setsource,  
  143.     .reset_port = s3c2440_serial_resetport,  
  144. };  
  145.   
  146. /* device management */  
  147.   
  148. static int s3c2440_serial_probe(struct platform_device *dev)  
  149. {                 //完成串口的添加  
  150.     dbg("s3c2440_serial_probe: dev=%p/n", dev);  
  151.     return s3c24xx_serial_probe(dev, &s3c2440_uart_inf);    
  152. }  
  153.   
  154. static struct platform_driver s3c2440_serial_driver = {       //注册串口设备  
  155.     .probe      = s3c2440_serial_probe,  
  156.     .remove     = __devexit_p(s3c24xx_serial_remove),  
  157.     .driver     = {  
  158.         .name   = "s3c2440-uart",  
  159.         .owner  = THIS_MODULE,  
  160.     },  
  161. };  
  162.   
  163. s3c24xx_console_init(&s3c2440_serial_driver, &s3c2440_uart_inf);  
  164.   
  165. static int __init s3c2440_serial_init(void)  
  166. {               //初始化模块  
  167.     return s3c24xx_serial_init(&s3c2440_serial_driver, &s3c2440_uart_inf);  
  168. }  
  169.   
  170. static void __exit s3c2440_serial_exit(void)  
  171. {               //退出模块  
  172.     platform_driver_unregister(&s3c2440_serial_driver);     //注销串口设备  
  173. }  
  174.   
  175. module_init(s3c2440_serial_init);  
  176. module_exit(s3c2440_serial_exit);  
  177.   
  178. MODULE_DESCRIPTION("Samsung S3C2440,S3C2442 SoC Serial port driver");  
  179. MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");  
  180. MODULE_LICENSE("GPL v2");  
  181. MODULE_ALIAS("platform:s3c2440-uart");  

 

 

几个问题需要我们注意:

1.设备如何注册、注销

串口驱动被作为一个单独的模块被加载进内核,在模块的加载和卸载函数中,只需注册和注销一个platform_driver结构体。

注册:

[c-sharp] view plaincopy
  1. static struct platform_driver s3c2440_serial_driver = {  
  2.     .probe      = s3c2440_serial_probe,  
  3.     .remove     = __devexit_p(s3c24xx_serial_remove),  
  4.     .driver     = {  
  5.         .name   = "s3c2440-uart",  
  6.         .owner  = THIS_MODULE,  
  7.     },  
  8. };  

 

注销:

[c-sharp] view plaincopy
  1. platform_driver_unregister(&s3c2440_serial_driver);  

 

2.几个非常重要的结构体

s3c2410_uartcfg :保存ucon ulcon ufcon三个串口寄存器的值

[c-sharp] view plaincopy
  1. struct s3c2410_uartcfg {  
  2.     unsigned char      hwport;   /* hardware port number */  
  3.     unsigned char      unused;  
  4.     unsigned short     flags;  
  5.     upf_t          uart_flags;   /* default uart flags */  
  6.   
  7.     unsigned int       has_fracval;  
  8.   
  9.     unsigned long      ucon;     /* value of ucon for port */  
  10.     unsigned long      ulcon;    /* value of ulcon for port */  
  11.     unsigned long      ufcon;    /* value of ufcon for port */  
  12.   
  13.     struct s3c24xx_uart_clksrc *clocks;  
  14.     unsigned int            clocks_size;  
  15. };  

 

 

s3c24xx_uart_info :提供串口设备环境信息,并提供三个函数的接口

[c-sharp] view plaincopy
  1. struct s3c24xx_uart_info {  
  2.     char            *name;  
  3.     unsigned int        type;  
  4.     unsigned int        fifosize;  
  5.     unsigned long       rx_fifomask;  
  6.     unsigned long       rx_fifoshift;  
  7.     unsigned long       rx_fifofull;  
  8.     unsigned long       tx_fifomask;  
  9.     unsigned long       tx_fifoshift;  
  10.     unsigned long       tx_fifofull;  
  11.   
  12.     /* uart port features */  
  13.   
  14.     unsigned int        has_divslot:1;  
  15.   
  16.     /* clock source control */  
  17.   
  18.     int (*get_clksrc)(struct uart_port *, struct s3c24xx_uart_clksrc *clk);  
  19.     int (*set_clksrc)(struct uart_port *, struct s3c24xx_uart_clksrc *clk);  
  20.   
  21.     /* uart controls */  
  22.     int (*reset_port)(struct uart_port *, struct s3c2410_uartcfg *);  
  23. };  

 

 

platform_device :设备的信息

 

[c-sharp] view plaincopy
  1. struct platform_device {  
  2.     const char  * name;  
  3.     int     id;  
  4.     struct device   dev;  
  5.     u32     num_resources;  
  6.     struct resource * resource;  
  7.   
  8.     const struct platform_device_id *id_entry;  
  9.   
  10.     /* arch specific additions */  
  11.     struct pdev_archdata    archdata;  
  12. };  

 

 

platform_driver :设备注册用

[c-sharp] view plaincopy
  1. struct platform_driver {  
  2.     int (*probe)(struct platform_device *);  
  3.     int (*remove)(struct platform_device *);  
  4.     void (*shutdown)(struct platform_device *);  
  5.     int (*suspend)(struct platform_device *, pm_message_t state);  
  6.     int (*resume)(struct platform_device *);  
  7.     struct device_driver driver;  
  8.     const struct platform_device_id *id_table;  
  9. };  

 

 

3.读写寄存器的宏定义

(1)读寄存器

unsigned long ucon = rd_regl(port, S3C2410_UCON);

 

#define rd_regl(port, reg)      (__raw_readl(portaddr(port, reg)))

static unsigned char __raw_readb(unsigned int ptr)

{

       return *((volatile unsigned char *)ptr);

}

#define portaddr(port, reg)    ((port)->membase + (reg))

(2)写寄存器

wr_regl(port, S3C2410_UCON, ucon);

#define wr_regl(port, reg, val)   __raw_writel(val, portaddr(port, reg))

#define portaddr(port, reg)          ((port)->membase + (reg))

#define __raw_writel(v,p)           (*(unsigned long *)(p) = (v))

 

4.函数的注册方式

     细心的朋友可能会发现,我们之前一直使用的是传统的 device driver 机制(通过 driver_register 函数进行注册)本串口所使用的是一个设备用 Platform_device 表示,驱动用 Platform_driver 进行注册的机制。而后者是在内核2.6版本所提出来的新事物,其优势在于platform机制将设备本身的资源注册进内核,由内核统一管理,在驱动程序中使用这些资源时通过 platform device 提供的标准接口进行申请并使用。这样提高了驱动和资源管理的独立性,并且拥有较好的可移植性和安全性(这些标准接口是安全的)。关于这两种机制更深入的分析,请看以下链接:http://blog.csdn.net/jarvis_xian/archive/2011/05/23/6440649.aspx


原创粉丝点击