一,移植环境
(红色粗字体字为修改后内容,蓝色粗体字为特别注意内容)
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
二,添加UART3
S3C2416支持4个UART,但友坚给出的内核只支持3个UART(ttySAC0,ttySAC1,ttySAC2),而不支持UART3 及 ttySAC3。下面介绍在友坚给出的内核基础上实现UART3。
1.添加UART3的资源设置
<1>. 打开 kernel2416/arch/arm/plat-s3c24xx/devs.c ,定位到113行附近,更改如下:
- /*
- #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[] 中添加以下代码:
- // 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行后,更改如下:
- 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 中添加如下变量
- 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设置值)
- 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 的值,如果不设置此值,会导致波特率不准的问题)
- 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行附近,修改如下:
- 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的设置
- #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.