您的位置:首页 > 运维架构 > Linux

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

2012-07-04 11:16 441 查看


一,移植环境

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

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

/*

#if defined (CONFIG_CPU_S3C6400) || defined (CONFIG_CPU_S3C6410)

static struct resource s3c_uart3_resource[] = {

[0] = {

.start = S3C2443_PA_UART3,

.end = S3C2443_PA_UART3 + 0x3ff,

.flags = IORESOURCE_MEM,

},

[1] = {

.start = IRQ_UART3,

.end = IRQ_UART3,

.flags = IORESOURCE_IRQ,

}

};

#endif

** change by bobee, 2012-03-8 */

static struct resource s3c2410_uart3_resource[] = {

[0] = {

.start = S3C2443_PA_UART3,

.end = S3C2443_PA_UART3 + 0x3fff,

.flags = IORESOURCE_MEM,

},

[1] = {

.start = IRQ_S3CUART_RX3,

.end = IRQ_S3CUART_ERR3,

.flags = IORESOURCE_IRQ,

}

};


<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

// add by bobee, 2012-03-08

[3] = {

.hwport = 3,

.flags = 0,

/* Use PCLK */

.ucon = 0x3c5,

//.ucon = 0xfc5,

.ulcon = 0x03,

.ufcon = 0x51,

.clocks = smdk2416_serial_clocks,

.clocks_size = ARRAY_SIZE(smdk2416_serial_clocks),

}


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

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

[cpp] view
plaincopy

static void s3c2443_irq_demux_uart3(unsigned int irq, struct irq_desc *desc)

{

//s3c2443_irq_demux(IRQ_S3C2443_UART3, 3);

s3c2443_irq_demux(IRQ_S3C2443_RX3, 3); // change by bobee, 2012-03-09

}

更改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

struct baud_calc {

struct s3c24xx_uart_clksrc *clksrc;

unsigned int calc;

unsigned int quot;

unsigned int udivslot; // add by bobee, 2012-03-07

struct clk *src;

};


<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

static int s3c24xx_serial_calcbaud(struct baud_calc *calc,

struct uart_port *port,

struct s3c24xx_uart_clksrc *clksrc,

unsigned int baud)

{

unsigned long rate;

// change by bobee, 2012-06-07

#if 1

unsigned long tempdiv;

unsigned int nslot, serial_result_1, serial_result_2;

calc->src = clk_get(port->dev, clksrc->name);

if (calc->src == NULL || IS_ERR(calc->src))

return 0;

baud/=100;

rate = clk_get_rate(calc->src);

rate /= clksrc->divisor;

calc->clksrc = clksrc;

tempdiv = (rate*10/(baud*16))-1000;

serial_result_1 = ((tempdiv%1000)*16)/1000;

serial_result_2 = (((tempdiv%1000)*16) - (serial_result_1*1000));

if (serial_result_2 >=500)

nslot = serial_result_1+1;

else

nslot = serial_result_1;

calc->quot = tempdiv/1000;

calc->calc = (rate / (calc->quot * 16));

calc->udivslot = udivslot_table[nslot];

printk(KERN_DEBUG "FOUND quot %d, calc %d, slot 0x%x, nslot %d\n",

calc->quot, calc->calc ,

calc->udivslot, nslot);

#else

calc->src = clk_get(port->dev, clksrc->name);

if (calc->src == NULL || IS_ERR(calc->src))

return 0;

rate = clk_get_rate(calc->src);

rate /= clksrc->divisor;

calc->clksrc = clksrc;

#if 0

/* Error to calculation of UART BAUD RATE DIVISOR */

calc->quot = (rate + (8 * baud)) / (16 * baud);

#else

/* Error correction */

calc->quot = (rate) / (16 * baud);

#endif

calc->calc = (rate / (calc->quot * 16));

calc->quot--;

#endif

return 1;

}



<5>. 修改函数s3c24xx_serial_set_termios()

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

[cpp] view
plaincopy

static void s3c24xx_serial_set_termios(struct uart_port *port,

struct ktermios *termios,

struct ktermios *old)

{

struct s3c2410_uartcfg *cfg = s3c24xx_port_to_cfg(port);

struct s3c24xx_uart_port *ourport = to_ourport(port);

struct s3c24xx_uart_clksrc *clksrc = NULL;

struct clk *clk = NULL;

unsigned long flags;

unsigned int baud, quot;

unsigned int ulcon;

unsigned int umcon;

unsigned int udivslot = 0, div; // add by bobee, 2012-03-07

/*

* We don't support modem control lines.

*/

termios->c_cflag &= ~(HUPCL | CMSPAR);

termios->c_cflag |= CLOCAL;

/*

* Ask the core to calculate the divisor for us.

*/

baud = uart_get_baud_rate(port, termios, old, 0, 115200*8);

if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST)

quot = port->custom_divisor;

else

quot = s3c24xx_serial_getclk(port, &clksrc, &clk, baud);

/* check to see if we need to change clock source */

if (ourport->clksrc != clksrc || ourport->baudclk != clk) {

s3c24xx_serial_setsource(port, clksrc);

if (ourport->baudclk != NULL && !IS_ERR(ourport->baudclk)) {

clk_disable(ourport->baudclk);

ourport->baudclk = NULL;

}

clk_enable(clk);

ourport->clksrc = clksrc;

ourport->baudclk = clk;

}

// add by bobee, 2012-03-07

#if defined(CONFIG_CPU_S3C2416)

div = clk_get_rate(clk) / baud;

udivslot = udivslot_table[div & 15];

#endif

switch (termios->c_cflag & CSIZE) {

case CS5:

dbg("config: 5bits/char\n");

ulcon = S3C2410_LCON_CS5;

break;

case CS6:

dbg("config: 6bits/char\n");

ulcon = S3C2410_LCON_CS6;

break;

case CS7:

dbg("config: 7bits/char\n");

ulcon = S3C2410_LCON_CS7;

break;

case CS8:

default:

dbg("config: 8bits/char\n");

ulcon = S3C2410_LCON_CS8;

break;

}

/* preserve original lcon IR settings */

ulcon |= (cfg->ulcon & S3C2410_LCON_IRM);

if (termios->c_cflag & CSTOPB)

ulcon |= S3C2410_LCON_STOPB;

umcon = (termios->c_cflag & CRTSCTS) ? S3C2410_UMCOM_AFC : 0;

if (termios->c_cflag & PARENB) {

if (termios->c_cflag & PARODD)

ulcon |= S3C2410_LCON_PODD;

else

ulcon |= S3C2410_LCON_PEVEN;

} else {

ulcon |= S3C2410_LCON_PNONE;

}

spin_lock_irqsave(&port->lock, flags);

dbg("setting ulcon to %08x, brddiv to %d\n", ulcon, quot);

wr_regl(port, S3C2410_ULCON, ulcon);

wr_regl(port, S3C2410_UBRDIV, quot);

#if defined(CONFIG_CPU_S3C2416)

wr_regl(port, S3C_UDIVSLOT, udivslot); // add by bobee, 2012-03-07

#endif

wr_regl(port, S3C2410_UMCON, umcon);

dbg("uart: ulcon = 0x%08x, ucon = 0x%08x, ufcon = 0x%08x\n",

rd_regl(port, S3C2410_ULCON),

rd_regl(port, S3C2410_UCON),

rd_regl(port, S3C2410_UFCON));

#if defined(CONFIG_CPU_S3C2416)

dbg("UDIVSLOT=0x%08x, UBRDIV=0x%08x\n",

rd_regl(port, S3C2410_UBRDIV),

rd_regl(port, S3C_UDIVSLOT));

#endif

dbg("baud=%d,clk=%ld \n", baud, clk_get_rate(clk));

/*

* Update the per-port timeout.

*/

uart_update_timeout(port, termios->c_cflag, baud);

/*

* Which character status flags are we interested in?

*/

port->read_status_mask = S3C2410_UERSTAT_OVERRUN;

if (termios->c_iflag & INPCK)

port->read_status_mask |= S3C2410_UERSTAT_FRAME | S3C2410_UERSTAT_PARITY;

/*

* Which character status flags should we ignore?

*/

port->ignore_status_mask = 0;

if (termios->c_iflag & IGNPAR)

port->ignore_status_mask |= S3C2410_UERSTAT_OVERRUN;

if (termios->c_iflag & IGNBRK && termios->c_iflag & IGNPAR)

port->ignore_status_mask |= S3C2410_UERSTAT_FRAME;

/*

* Ignore all characters if CREAD is not set.

*/

if ((termios->c_cflag & CREAD) == 0)

port->ignore_status_mask |= RXSTAT_DUMMY_READ;

spin_unlock_irqrestore(&port->lock, flags);

}


<6>. 修改结构体 s3c24xx_uart_drv

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

[cpp] view
plaincopy

static struct uart_driver s3c24xx_uart_drv = {

.owner = THIS_MODULE,

.dev_name = "s3c2410_serial",

.nr = NR_PORTS, //3, // change by bobee, 2012-03-07

.cons = S3C24XX_SERIAL_CONSOLE,

.driver_name = S3C24XX_SERIAL_NAME,

.major = S3C24XX_SERIAL_MAJOR,

.minor = S3C24XX_SERIAL_MINOR,

};


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

[cpp] view
plaincopy

#if defined (CONFIG_CPU_S3C2416) // add by bobee, 2011-03-07

[3] = {

.port = {

.lock = SPIN_LOCK_UNLOCKED,

.iotype = UPIO_MEM,

.irq = IRQ_S3CUART_RX3,

.uartclk = 0,

.fifosize = 16,

.ops = &s3c24xx_serial_ops,

.flags = UPF_BOOT_AUTOCONF,

.line = 3,

}

}

#endif

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

转自:http://blog.csdn.net/anthone_ligang/article/details/7466070
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: