×
单片机 > 单片机程序设计 > 详情

S3C2440驱动简析——串口驱动

发布时间:2020-08-28 发布时间:
|
对于驱动的学习停歇了几乎一周的时间,期间忙于补习Linux应用编程和搜索驱动、内核相关书籍,以便之后更进一步地学习。在之前友善提供的驱动例程里面,涉及的知识面非常有限,需要研究更多的驱动源码,了解更多的驱动知识,是当务之急。研究别人代码的同时,当然不忘自己也要动手练习。以下贴出串口驱动程序,并在程序里附上简要注释。
[c-sharp] view plain copy
 
  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 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: lx, lx, lx/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 //  
  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 (lx), 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 ");  
  180. MODULE_LICENSE("GPL v2");  
  181. MODULE_ALIAS("platform:s3c2440-uart");  

 

 

几个问题需要我们注意:

1.设备如何注册、注销

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

注册:

[c-sharp] view plain copy
 
  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 plain copy
 
  1. platform_driver_unregister(&s3c2440_serial_driver);  

 

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

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

[c-sharp] view plain copy
 
  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 plain copy
 
  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 plain copy
 
  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 plain copy
 
  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

 


『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
Keil(MDK-ARM)系列教程(七)_菜单