S3C2416 Linux2.6.21 驱动移植--添加UART3 及波特率设置bug消除

来源:互联网 发布:如何用qq空间做淘宝客 编辑:程序博客网 时间:2024/05/24 07:33

 一,移植环境

红色粗字体字为修改后内容,蓝色粗体字为特别注意内容)

1.主机环境:Virtualbox 下ubuntu-10.10

2.编译编译环境:arm-linux-gcc v4.4.3

3.uboot : U-Boot 1.3.4(友坚提供)

4.linux内核版本:2.6.21.5

5.硬件平台:采用友坚UT2416CV02核心板开发的平台

6.参考:

        linux内核支持S3C2416的UART3 (http://www.itkee.com/os/detail-1677.html )

        S3C2416 User's Manual, Revision 1.10

http://www.urbetter.com/product_UT2416BV02.asp



 

二,添加UART3

      S3C2416支持4个UART,但友坚给出的内核只支持3个UART(ttySAC0,ttySAC1,ttySAC2),而不支持UART3 及 ttySAC3。下面介绍在友坚给出的内核基础上实现UART3。

 

1.添加UART3的资源设置

       <1>. 打开 kernel2416/arch/arm/plat-s3c24xx/devs.c ,定位到113行附近,更改如下:

[cpp] view plaincopy
  1. /* 
  2. #if defined (CONFIG_CPU_S3C6400) || defined (CONFIG_CPU_S3C6410)  
  3. static struct resource s3c_uart3_resource[] = { 
  4.     [0] = { 
  5.         .start = S3C2443_PA_UART3, 
  6.         .end   = S3C2443_PA_UART3 + 0x3ff, 
  7.         .flags = IORESOURCE_MEM, 
  8.     }, 
  9.  
  10.     [1] = { 
  11.         .start = IRQ_UART3, 
  12.         .end   = IRQ_UART3, 
  13.         .flags = IORESOURCE_IRQ, 
  14.     } 
  15. }; 
  16. #endif 
  17. **               change by bobee, 2012-03-8                 */  
  18. static struct resource s3c2410_uart3_resource[] = {  
  19.     [0] = {  
  20.         .start = S3C2443_PA_UART3,  
  21.         .end   = S3C2443_PA_UART3 + 0x3fff,  
  22.         .flags = IORESOURCE_MEM,  
  23.     },  
  24.     [1] = {  
  25.         .start = IRQ_S3CUART_RX3,  
  26.         .end   = IRQ_S3CUART_ERR3,  
  27.         .flags = IORESOURCE_IRQ,  
  28.     }  
  29. };  

      <2>. 定位到157行附近,

#if defined (CONFIG_CPU_S3C6400) || defined (CONFIG_CPU_S3C6410) || defined (CONFIG_CPU_S3C2416)
      [3] = {
                   .resources = s3c2410_uart3_resource,
                   .nr_resources = ARRAY_SIZE(s3c2410_uart3_resource),
      },
#endif


      <3>. 定位到179行附近,

 #if defined (CONFIG_CPU_S3C6400) || defined (CONFIG_CPU_S3C6410) || defined (CONFIG_CPU_S3C2416)// add by bobee, 2011-03-08

static struct platform_device s3c24xx_uart_device3 = {
           .id  = 3,
};
#endif


    <4>. 定位到189行附近, 

#if defined (CONFIG_CPU_S3C6400) || defined (CONFIG_CPU_S3C6410) ||  defined (CONFIG_CPU_S3C2416) // add by bobee, 2011-03-08
        &s3c24xx_uart_device3,
#endif

2.添加UART3的默认配置

         打开kernel2416/arch/arm/mach-s3c2416/mach-smdk2416.c,定位到113行后,在结构体smdk2416_uartcfgs[] 中添加以下代码:

[cpp] view plaincopy
  1. // add by bobee, 2012-03-08  
  2. [3] = {  
  3.     .hwport      = 3,  
  4.     .flags       = 0,  
  5.     /* Use PCLK */  
  6.     .ucon        = 0x3c5,  
  7.     //.ucon      = 0xfc5,  
  8.     .ulcon       = 0x03,  
  9.     .ufcon       = 0x51,  
  10.     .clocks      = smdk2416_serial_clocks,  
  11.     .clocks_size = ARRAY_SIZE(smdk2416_serial_clocks),  
  12. }  

3.更改s3c2416 的UART3 的中断相关函数

         打开kernel2416/arch/arm/mach-s3c2416/irq.c,定位到181行后,更改如下:

[cpp] view plaincopy
  1. static void s3c2443_irq_demux_uart3(unsigned int irq, struct irq_desc *desc)  
  2. {  
  3.     //s3c2443_irq_demux(IRQ_S3C2443_UART3, 3);  
  4.     s3c2443_irq_demux(IRQ_S3C2443_RX3, 3);      // change by bobee, 2012-03-09  
  5. }  

      更改UART3的子串口中断掩码

//#define SUBMSK_UART3 (0xf << (IRQ_S3C2443_RX3 - S3C2410_IRQSUB(0)))
// change by bobee, 2012-03-09
#define SUBMSK_UART3 (0x7 << (IRQ_S3C2443_RX3 - S3C2410_IRQSUB(0)))



4.更改头文件regs-serial.h 中的相关定义

          打开kernel2416/include/asm-arm/arch-s3c2410/irqs.h , 定位到153行后,添加如下定义

/* UART3 */
#define IRQ_S3C2443_RX3  S3C2410_IRQSUB(24)
#define IRQ_S3C2443_TX3  S3C2410_IRQSUB(25)
#define IRQ_S3C2443_ERR3 S3C2410_IRQSUB(26)

// add by bobee, 2012-03-08

#define IRQ_UART3                     IRQ_S3C2443_UART3
#define IRQ_S3CUART_RX3         IRQ_S3C2443_RX3
#define IRQ_S3CUART_TX3         IRQ_S3C2443_TX3
#define IRQ_S3CUART_ERR3      IRQ_S3C2443_ERR3
    

      更改串口数,定位到366行后,

#if defined (CONFIG_CPU_S3C6400) || defined (CONFIG_CPU_S3C6410)
#define NR_PORTS  (4)
#define UART_FIFO_SIZE 64
#define UART_HAS_INTMSK
#define UART_C_CFLAG
#define UART_UMCON
#define UART_CLK 115200
#elif defined (CONFIG_CPU_S3C2416)
#define NR_PORTS  (4)  // change by bobee, 2012-03-08
#else
#define NR_PORTS  (3)
#endif

5.修改串口驱动文件

    打开/kernel2416/drivers/serial/s3c2410.c

   <1>. 定位到165行,更改串口个数

#ifdef CONFIG_CPU_S3C2400
#define NR_PORTS (2)
#else
#define NR_PORTS    (4)        //  (3)     // change by bobee, 2012-03-08
#endif

     <2>. 定位到627行,在结构体 struct baud_calc 中添加如下变量

[cpp] view plaincopy
  1. struct baud_calc {  
  2.     struct s3c24xx_uart_clksrc  *clksrc;  
  3.     unsigned int             calc;  
  4.     unsigned int             quot;  
  5.     unsigned int             udivslot;      // add by bobee, 2012-03-07  
  6.     struct clk          *src;  
  7. };  

 

     <3>.  添加 S3C2416 波特率 dividing slot register UDIVSLOTn 查询表:

        (添加在上面struct baud_calc  结构体定义后)

// add by bobee, 2012-03-07
const unsigned int udivslot_table[16] = {0x0000, 0x0080, 0x0808, 0x0888, 0x2222, 0x4924, 0x4a52, 0x54aa,
           0x5555, 0xd555, 0xd5d5, 0xddd5, 0xdddd, 0xdfdd, 0xdfdf, 0xffdf};

     <4>. 定位到636行附近,更改波特率计算函数s3c24xx_serial_calcbaud,

              更改如下:(更改后可以改善波特率 UBRDIVn设置值

 

[cpp] view plaincopy
  1. static int s3c24xx_serial_calcbaud(struct baud_calc *calc,  
  2.                    struct uart_port *port,  
  3.                    struct s3c24xx_uart_clksrc *clksrc,  
  4.                    unsigned int baud)  
  5. {  
  6.     unsigned long rate;  
  7. // change by bobee, 2012-06-07  
  8. #if 1  
  9.     unsigned long tempdiv;  
  10.     unsigned int nslot, serial_result_1, serial_result_2;  
  11.   
  12.   
  13.     calc->src = clk_get(port->dev, clksrc->name);  
  14.     if (calc->src == NULL || IS_ERR(calc->src))  
  15.         return 0;  
  16.   
  17.     baud/=100;  
  18.   
  19.     rate = clk_get_rate(calc->src);  
  20.     rate /= clksrc->divisor;  
  21.   
  22.     calc->clksrc = clksrc;  
  23.   
  24.     tempdiv = (rate*10/(baud*16))-1000;  
  25.     serial_result_1 = ((tempdiv%1000)*16)/1000;  
  26.     serial_result_2 = (((tempdiv%1000)*16) - (serial_result_1*1000));  
  27.   
  28.     if (serial_result_2 >=500)  
  29.         nslot = serial_result_1+1;  
  30.     else  
  31.         nslot = serial_result_1;  
  32.   
  33.     calc->quot = tempdiv/1000;  
  34.     calc->calc = (rate / (calc->quot * 16));  
  35.     calc->udivslot = udivslot_table[nslot];  
  36.     printk(KERN_DEBUG  "FOUND quot %d, calc %d, slot 0x%x, nslot %d\n",  
  37.                    calc->quot, calc->calc ,  
  38.                    calc->udivslot, nslot);  
  39.   
  40. #else  
  41.     calc->src = clk_get(port->dev, clksrc->name);  
  42.     if (calc->src == NULL || IS_ERR(calc->src))  
  43.         return 0;  
  44.   
  45.     rate = clk_get_rate(calc->src);  
  46.     rate /= clksrc->divisor;  
  47.   
  48.     calc->clksrc = clksrc;  
  49. #if 0  
  50.     /* Error to calculation of UART BAUD RATE DIVISOR */  
  51.     calc->quot = (rate + (8 * baud)) / (16 * baud);  
  52. #else  
  53.     /* Error correction */  
  54.     calc->quot = (rate) / (16 * baud);  
  55. #endif  
  56.     calc->calc = (rate / (calc->quot * 16));  
  57.   
  58.     calc->quot--;  
  59.   
  60. #endif  
  61.     return 1;  
  62. }  


      <5>. 修改函数s3c24xx_serial_set_termios()

  更改如下:(这里主要是 设置 寄存器UDIVSLOTn 的值,如果不设置此值,会导致波特率不准的问题

[cpp] view plaincopy
  1. static void s3c24xx_serial_set_termios(struct uart_port *port,  
  2.                        struct ktermios *termios,  
  3.                        struct ktermios *old)  
  4. {  
  5.     struct s3c2410_uartcfg *cfg = s3c24xx_port_to_cfg(port);  
  6.     struct s3c24xx_uart_port *ourport = to_ourport(port);  
  7.     struct s3c24xx_uart_clksrc *clksrc = NULL;  
  8.     struct clk *clk = NULL;  
  9.     unsigned long flags;  
  10.     unsigned int baud, quot;  
  11.     unsigned int ulcon;  
  12.     unsigned int umcon;  
  13.     unsigned int udivslot = 0, div; // add by bobee, 2012-03-07  
  14.   
  15.     /* 
  16.      * We don't support modem control lines. 
  17.      */  
  18.     termios->c_cflag &= ~(HUPCL | CMSPAR);  
  19.     termios->c_cflag |= CLOCAL;  
  20.   
  21.     /* 
  22.      * Ask the core to calculate the divisor for us. 
  23.      */  
  24.   
  25.     baud = uart_get_baud_rate(port, termios, old, 0, 115200*8);  
  26.   
  27.     if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST)  
  28.         quot = port->custom_divisor;  
  29.     else  
  30.         quot = s3c24xx_serial_getclk(port, &clksrc, &clk, baud);  
  31.   
  32.     /* check to see if we need  to change clock source */  
  33.   
  34.     if (ourport->clksrc != clksrc || ourport->baudclk != clk) {  
  35.         s3c24xx_serial_setsource(port, clksrc);  
  36.   
  37.         if (ourport->baudclk != NULL && !IS_ERR(ourport->baudclk)) {  
  38.             clk_disable(ourport->baudclk);  
  39.             ourport->baudclk  = NULL;  
  40.         }  
  41.   
  42.         clk_enable(clk);  
  43.   
  44.         ourport->clksrc = clksrc;  
  45.         ourport->baudclk = clk;  
  46.     }  
  47. // add by bobee, 2012-03-07  
  48. #if defined(CONFIG_CPU_S3C2416)  
  49.     div = clk_get_rate(clk) / baud;  
  50.     udivslot = udivslot_table[div & 15];  
  51.   
  52. #endif  
  53.     switch (termios->c_cflag & CSIZE) {  
  54.     case CS5:  
  55.         dbg("config: 5bits/char\n");  
  56.         ulcon = S3C2410_LCON_CS5;  
  57.         break;  
  58.     case CS6:  
  59.         dbg("config: 6bits/char\n");  
  60.         ulcon = S3C2410_LCON_CS6;  
  61.         break;  
  62.     case CS7:  
  63.         dbg("config: 7bits/char\n");  
  64.         ulcon = S3C2410_LCON_CS7;  
  65.         break;  
  66.     case CS8:  
  67.     default:  
  68.         dbg("config: 8bits/char\n");  
  69.         ulcon = S3C2410_LCON_CS8;  
  70.         break;  
  71.     }  
  72.   
  73.     /* preserve original lcon IR settings */  
  74.     ulcon |= (cfg->ulcon & S3C2410_LCON_IRM);  
  75.   
  76.     if (termios->c_cflag & CSTOPB)  
  77.         ulcon |= S3C2410_LCON_STOPB;  
  78.   
  79.     umcon = (termios->c_cflag & CRTSCTS) ? S3C2410_UMCOM_AFC : 0;  
  80.   
  81.     if (termios->c_cflag & PARENB) {  
  82.         if (termios->c_cflag & PARODD)  
  83.             ulcon |= S3C2410_LCON_PODD;  
  84.         else  
  85.             ulcon |= S3C2410_LCON_PEVEN;  
  86.     } else {  
  87.         ulcon |= S3C2410_LCON_PNONE;  
  88.     }  
  89.   
  90.     spin_lock_irqsave(&port->lock, flags);  
  91.   
  92.     dbg("setting ulcon to %08x, brddiv to %d\n", ulcon, quot);  
  93.   
  94.     wr_regl(port, S3C2410_ULCON, ulcon);  
  95.     wr_regl(port, S3C2410_UBRDIV, quot);  
  96. #if defined(CONFIG_CPU_S3C2416)  
  97.     wr_regl(port, S3C_UDIVSLOT, udivslot);          // add by bobee, 2012-03-07  
  98. #endif  
  99.     wr_regl(port, S3C2410_UMCON, umcon);  
  100.   
  101.     dbg("uart: ulcon = 0x%08x, ucon = 0x%08x, ufcon = 0x%08x\n",  
  102.         rd_regl(port, S3C2410_ULCON),  
  103.         rd_regl(port, S3C2410_UCON),  
  104.         rd_regl(port, S3C2410_UFCON));  
  105. #if defined(CONFIG_CPU_S3C2416)  
  106.     dbg("UDIVSLOT=0x%08x, UBRDIV=0x%08x\n",   
  107.             rd_regl(port, S3C2410_UBRDIV),  
  108.         rd_regl(port, S3C_UDIVSLOT));  
  109. #endif  
  110.     dbg("baud=%d,clk=%ld \n", baud, clk_get_rate(clk));   
  111.   
  112.   
  113.     /* 
  114.      * Update the per-port timeout. 
  115.      */  
  116.     uart_update_timeout(port, termios->c_cflag, baud);  
  117.   
  118.     /* 
  119.      * Which character status flags are we interested in? 
  120.      */  
  121.     port->read_status_mask = S3C2410_UERSTAT_OVERRUN;  
  122.     if (termios->c_iflag & INPCK)  
  123.         port->read_status_mask |= S3C2410_UERSTAT_FRAME | S3C2410_UERSTAT_PARITY;  
  124.   
  125.     /* 
  126.      * Which character status flags should we ignore? 
  127.      */  
  128.     port->ignore_status_mask = 0;  
  129.     if (termios->c_iflag & IGNPAR)  
  130.         port->ignore_status_mask |= S3C2410_UERSTAT_OVERRUN;  
  131.     if (termios->c_iflag & IGNBRK && termios->c_iflag & IGNPAR)  
  132.         port->ignore_status_mask |= S3C2410_UERSTAT_FRAME;  
  133.   
  134.     /* 
  135.      * Ignore all characters if CREAD is not set. 
  136.      */  
  137.     if ((termios->c_cflag & CREAD) == 0)  
  138.         port->ignore_status_mask |= RXSTAT_DUMMY_READ;  
  139.   
  140.     spin_unlock_irqrestore(&port->lock, flags);  
  141. }  

   <6>. 修改结构体 s3c24xx_uart_drv

        定位到1011行附近,修改如下:

[cpp] view plaincopy
  1. static struct uart_driver s3c24xx_uart_drv = {  
  2.     .owner      = THIS_MODULE,  
  3.     .dev_name   = "s3c2410_serial",  
  4.     .nr     = NR_PORTS, //3,            // change by bobee, 2012-03-07  
  5.     .cons       = S3C24XX_SERIAL_CONSOLE,  
  6.     .driver_name    = S3C24XX_SERIAL_NAME,  
  7.     .major      = S3C24XX_SERIAL_MAJOR,  
  8.     .minor      = S3C24XX_SERIAL_MINOR,  
  9. };  

     <7>. 在结构体 s3c24xx_serial_port[NR_PORT]中添加,uart3的设置

[cpp] view plaincopy
  1. #if defined (CONFIG_CPU_S3C2416) // add by bobee, 2011-03-07  
  2.     [3] = {  
  3.         .port = {  
  4.             .lock       = SPIN_LOCK_UNLOCKED,  
  5.             .iotype     = UPIO_MEM,  
  6.             .irq        = IRQ_S3CUART_RX3,  
  7.             .uartclk    = 0,  
  8.             .fifosize   = 16,  
  9.             .ops        = &s3c24xx_serial_ops,  
  10.             .flags      = UPF_BOOT_AUTOCONF,  
  11.             .line       = 3,  
  12.         }  
  13.     }  
  14. #endif  

 


到此位置,已经成功添加串口 UART3,并修补了原来波特率不能设置9600 bug.



转自:http://blog.csdn.net/anthone_ligang/article/details/7466070

原创粉丝点击