S3C2440驱动简析——串口驱动

来源:互联网 发布:php mysql 三表联查 编辑:程序博客网 时间:2024/05/01 05:03

对于驱动的学习停歇了几乎一周的时间,期间忙于补习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/aniu127/article/details/38402911

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 海尔电视机的设置调乱了怎么办 老公弟兄两个有个偏心的婆婆怎么办 农商银行u盾密码忘了怎么办 不熟的表弟表妹向你借钱怎么办 开货车撞到人家房子了怎么办 坐骨被摔跤后好多年没好怎么办 自动挡汽车电子手刹刹车失灵怎么办 买家拍了不包邮的宝贝付款了怎么办 包邮快递买家不要了运费怎么办 舞蹈劈叉练出肌肉劈不下去怎么办 腰间盘突出压迫神经腿疼怎么办盘 绑定了我身份证的微信被盗了怎么办 用身份证办的手机卡不用了怎么办 平安陆金所交易密码忘了怎么办 陆金所密码交易密码忘记怎么办 带介指手指月肿了拿不下来怎么办 老公搞建筑的要长期在外地怎么办 媳妇跟婆婆吵架老公帮婆婆该怎么办 在家里礼佛香炉剩下的香头怎么办 精索曲张最近一打篮球就蛋疼怎么办 都两天了快递还是显示已发货怎么办 中通快递到达一天就是不派送怎么办 顺丰派送员把快递寄错了怎么办 顺丰快递把户籍卡弄丢了怎么办 金立手机不小心设置成英文了怎么办 三星手机不小心设置成英文了怎么办 手游方舟国际版渡渡鸟跟丢了怎么办 一打电话4g变2g怎么办 手机4g突然变2g怎么办 江湖风云录八卦门任务拒绝了怎么办 百度网盘下载时显示违规信息怎么办 在海马助手下载的游戏闪退怎么办 手机版百度云盘不能普通下载怎么办 手机版百度云盘一直加载中怎么办 登别人的网盘单同步通讯录了怎么办 被培训公司骗了贷款之后该怎么办 乐教乐学孩子登陆你那忘记了怎么办 脸擦破了痂掉了留斑怎么办 挤黑头后鼻子又红又疼怎么办 香奈儿邂逅清新淡香水不喷怎么办 脚面被压了肿起来了怎么办