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

一,移植环境

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

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_RX3S3C2410_IRQSUB(24)#define IRQ_S3C2443_TX3S3C2410_IRQSUB(25)#define IRQ_S3C2443_ERR3S3C2410_IRQSUB(26)

// add by bobee, 2012-03-08

#define IRQ_UART3 IRQ_S3C2443_UART3#define IRQ_S3CUART_RX3 IRQ_S3C2443_RX3#define IRQ_S3CUART_TX3IRQ_S3C2443_TX3#define IRQ_S3CUART_ERR3IRQ_S3C2443_ERR3

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

#if defined (CONFIG_CPU_S3C6400) || defined (CONFIG_CPU_S3C6410)#define NR_PORTS (4)#define UART_FIFO_SIZE64#define UART_HAS_INTMSK#define UART_C_CFLAG#define UART_UMCON#define UART_CLK115200#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-07struct clk*src;};

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

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

// add by bobee, 2012-03-07const 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 1unsigned 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;elsenslot = 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);#elsecalc->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);#endifcalc->calc = (rate / (calc->quot * 16));calc->quot--;#endifreturn 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;elsequot = 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];#endifswitch (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;elseulcon |= 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#endifwr_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));#endifdbg("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.

不是每个人都一定快乐,不是每种痛都一定要述说。

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

相关文章:

你感兴趣的文章:

标签云: