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] viewplaincopy
/*
#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-08static 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] viewplaincopy
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] viewplaincopy
#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
相关文章推荐
- S3C2416 Linux2.6.21 驱动移植--添加UART3 及波特率设置bug消除
- S3C2416 Linux2.6.21 驱动移植--添加UART3 及波特率设置bug消除
- S3C2416 Linux2.6.21 驱动移植--添加UART3 及波特率设置bug消除
- Linux-2.6.32.2内核在mini2440上的移植(五)---添加LCD背光驱动
- S3C2416 +linux驱动移植之DM9000A
- linux-2.6.35内核移植—网卡驱动的添加
- ARM9开发板FL2440移植Linux-3.0内核————添加USB驱动
- Linux-2.6.32.2内核在mini2440上的移植(十八)---将UART2更成普通串口驱动
- linux-2.6.35内核移植—Nand flash驱动的添加(分区)
- linux gspca usb摄像头驱动添加对新型号的详细移植步骤
- linux-2.6.35内核移植—LCD驱动的添加
- linux-2.6.35内核移植—USB驱动的添加
- 基于S3C2440的Linux-3.6.6移植——UART驱动
- 【转】 linux内核移植和驱动添加(三)
- Linux-2.6.32.2内核在mini2440上的移植(十八)---将UART2更成普通串口驱动
- linux-2.6.35内核移植—SD卡驱动的添加
- 【转】LINUX-2.6.31内核移植及添加驱动
- ARM-Linux S5PV210 UART驱动(6)----platform device的添加
- Linux-2.6.32.2内核在mini2440上的移植(八)---添加ADC驱动
- S3C2416 +linux驱动移植之DM9000A