本文是基于mini2440开发板Linux版本号是linux-2.6.32.2的学习笔记
static struct s3c2410_uartcfg mini2440_uartcfgs[] __initdata =
{
[0] = {
.hwport = 0,
.flags = 0,
.ucon = 0x3c5,
.ulcon = 0x03,
.ufcon = 0x51,
},
[1] = {
.hwport = 1,
.flags = 0,
.ucon = 0x3c5,
.ulcon = 0x03,
.ufcon = 0x51,
},
[2] = {
.hwport = 2,
.flags = 0,
.ucon = 0x3c5,
.ulcon = 0x03,
.ufcon = 0x51,
}
};
.hwport是硬件端口号,其他的结构体成员暂时往后看。
void __init s3c24xx_init_uarts(struct s3c2410_uartcfg *cfg, int no)
{
if (cpu == NULL)
return;
if (cpu->init_uarts == NULL)
{
printk(KERN_ERR "s3c24xx_init_uarts: cpu has no uart init\n");
}
else
(cpu->init_uarts)(cfg, no);
}
.init_uarts = s3c244x_init_uarts,
最终调用的是s3c244x_init_uarts函数。
void __init s3c244x_init_uarts(struct s3c2410_uartcfg *cfg, int no)
{
s3c24xx_init_uartdevs("s3c2440-uart", s3c2410_uart_resources, cfg, no);
}
static struct resource s3c2410_uart0_resource[] =
{
[0] = {
.start = S3C2410_PA_UART0,
.end = S3C2410_PA_UART0 + 0x3fff,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_S3CUART_RX0,
.end = IRQ_S3CUART_ERR0,
.flags = IORESOURCE_IRQ,
}
};
static struct resource s3c2410_uart1_resource[] =
{
[0] = {
.start = S3C2410_PA_UART1,
.end = S3C2410_PA_UART1 + 0x3fff,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_S3CUART_RX1,
.end = IRQ_S3CUART_ERR1,
.flags = IORESOURCE_IRQ,
}
};
static struct resource s3c2410_uart2_resource[] =
{
[0] = {
.start = S3C2410_PA_UART2,
.end = S3C2410_PA_UART2 + 0x3fff,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_S3CUART_RX2,
.end = IRQ_S3CUART_ERR2,
.flags = IORESOURCE_IRQ,
}
};
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,
},
};
struct s3c24xx_uart_resources s3c2410_uart_resources[] __initdata =
{
[0] = {
.resources = s3c2410_uart0_resource,
.nr_resources = ARRAY_SIZE(s3c2410_uart0_resource),
},
[1] = {
.resources = s3c2410_uart1_resource,
.nr_resources = ARRAY_SIZE(s3c2410_uart1_resource),
},
[2] = {
.resources = s3c2410_uart2_resource,
.nr_resources = ARRAY_SIZE(s3c2410_uart2_resource),
},
[3] = {
.resources = s3c2410_uart3_resource,
.nr_resources = ARRAY_SIZE(s3c2410_uart3_resource),
},
};
nr_resources = 2
s3c24xx_init_uartdevs函数分析
对platdev填充name,resource,num_resources,dev.platform_data等结构体成员,然后把platdev赋值给了s3c24xx_uart_devs全局变量,最终结果如下:
s3c24xx_uart_devs[0] =
{
.name = "s3c2440-uart",
.id = 0,
.resource =
{
[0] = {
.start = S3C2410_PA_UART0,
.end = S3C2410_PA_UART0 + 0x3fff,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_S3CUART_RX0,
.end = IRQ_S3CUART_ERR0,
.flags = IORESOURCE_IRQ,
}
},
.num_resources = 2,
.dev.platform_data =
{
[0] = {
.hwport = 0,
.flags = 0,
.ucon = 0x3c5,
.ulcon = 0x03,
.ufcon = 0x51,
},
[1] = {
.hwport = 1,
.flags = 0,
.ucon = 0x3c5,
.ulcon = 0x03,
.ufcon = 0x51,
},
[2] = {
.hwport = 2,
.flags = 0,
.ucon = 0x3c5,
.ulcon = 0x03,
.ufcon = 0x51,
}
},
};
从上一步看,uart device 设备为s3c24xx_uart_devs,device个数为3.
uart设备在哪里注册呢,可以看到在s3c_arch_init函数中注册了uart设备。
static int __init s3c_arch_init(void)
{
int ret;
// do the correct init for cpu
if (cpu == NULL)
panic("s3c_arch_init: NULL cpu\n");
ret = (cpu->init)();
if (ret != 0)
return ret;
ret = platform_add_devices(s3c24xx_uart_devs, nr_uarts);
return ret;
}
s3c_arch_init函数又是什么时候调用呢?通过arch_initcall(s3c_arch_init)可知,s3c_arch_init函数在内核启动时被调用。
platform_add_devices
platform_device_add(pdev);
if (pdev->id != -1)
dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id);
else
dev_set_name(&pdev->dev, "%s", pdev->name);
pdev->id = 0,1,2, 最终注册的设备为s3c2440-uart.0,s3c2440-uart.1,s3c2440-uart.2
设备路径:/sys/devices/platform/s3c2440-uart.0
/sys/devices/platform/s3c2440-uart.1
/sys/devices/platform/s3c2440-uart.2
搜索设备名s3c2440-uart,找到uart driver的注册文件/drivers/serial/s3c2440.c文件
在s3c2440.c文件中platform_driver的定义如下:
static struct platform_driver s3c2440_serial_driver =
{
.probe = s3c2440_serial_probe,
.remove = __devexit_p(s3c24xx_serial_remove),
.driver = {
.name = "s3c2440-uart",
.owner = THIS_MODULE,
},
};
最后通过调用platform_driver_register函数注册uart driver。
uart driver的probe函数为s3c2440_serial_probe,但是s3c2440_serial_probe函数又调用了s3c24xx_serial_probe函数,s3c24xx_serial_probe函数传参s3c2440_uart_inf,如下所示:
#define S3C2440_UFSTAT_TXFULL (1<<14)
#define S3C2440_UFSTAT_RXFULL (1<<6)
#define S3C2440_UFSTAT_TXSHIFT (8)
#define S3C2440_UFSTAT_RXSHIFT (0)
#define S3C2440_UFSTAT_TXMASK (63<<8)
#define S3C2440_UFSTAT_RXMASK (63)
static struct s3c24xx_uart_info s3c2440_uart_inf =
{
.name = "Samsung S3C2440 UART",
.type = PORT_S3C2440,
.fifosize = 64,
.rx_fifomask = S3C2440_UFSTAT_RXMASK,
.rx_fifoshift = S3C2440_UFSTAT_RXSHIFT,
.rx_fifofull = S3C2440_UFSTAT_RXFULL,
.tx_fifofull = S3C2440_UFSTAT_TXFULL,
.tx_fifomask = S3C2440_UFSTAT_TXMASK,
.tx_fifoshift = S3C2440_UFSTAT_TXSHIFT,
.get_clksrc = s3c2440_serial_getsource,
.set_clksrc = s3c2440_serial_setsource,
.reset_port = s3c2440_serial_resetport,
};
.fifosize = 64:表示缓冲buffer为64byte。
.rx_fifomask = 63:读缓冲buffer count正常取值为0 ~ 63,否则就是读缓冲buffer满了。
.rx_fifoshift = 0:因为 Rx FIFO Count为【5:0】,不需要移动
rx_fifofull = (1<<6):表示读缓冲buffer满了
.tx_fifofull = (1<<14):写缓冲buffer满了
.tx_fifomask = (63<<8):写缓冲buffer count正常取值为0 ~ 63,但是要右移8位
.tx_fifoshift = (8):写缓冲buffer count需要右移8位
s3c2440_serial_getsource,,s3c2440_serial_resetport几个函数放在后面看。
设置PCLK, UCON的[11:10]设置为0,2
设置UCLK, UCON的[11:10]设置为1
设置FCLK/n, UCON的[11:10]设置为3
static int s3c2440_serial_setsource(struct uart_port *port, struct s3c24xx_uart_clksrc *clk)
{
unsigned long ucon = rd_regl(port, S3C2410_UCON);
/* todo - proper fclk<>nonfclk switch. */
ucon &= ~S3C2440_UCON_CLKMASK;
if (strcmp(clk->name, "uclk") == 0)
ucon |= S3C2440_UCON_UCLK;
else if (strcmp(clk->name, "pclk") == 0)
ucon |= S3C2440_UCON_PCLK;
else if (strcmp(clk->name, "fclk") == 0)
ucon |= S3C2440_UCON_FCLK;
else {
printk(KERN_ERR "unknown clock source %s\n", clk->name);
return -EINVAL;
}
wr_regl(port, S3C2410_UCON, ucon);
return 0;
}
现在看s3c24xx_serial_probe函数。
ourport->info = info;
②ourport->port.fifosize = info->fifosize = 64,之前给的默认值是16
③获取mem资源,保存在port->mapbase和port->membase变量中
res = platform_get_resource(platdev, IORESOURCE_MEM, 0);
if (res == NULL) {
printk(KERN_ERR "failed to find memory resource for uart\n");
return -EINVAL;
}
dbg("resource %p (%lx..%lx)\n", res, res->start, res->end);
port->mapbase = res->start;
port->membase = S3C_VA_UART + res->start - (S3C_PA_UART & 0xfff00000);
④获取终端号,保存在ourport->rx_irq和ourport->tx_irq变量中
ret = platform_get_irq(platdev, 0);
if (ret < 0)
port->irq = 0;
else {
port->irq = ret;
ourport->rx_irq = ret;
ourport->tx_irq = ret + 1;
⑤获取uart时钟,保存在ourport->clk中
ourport->clk = clk_get(&platdev->dev, "uart");
⑥reset fifo,设置串口,调用函数s3c24xx_serial_resetport,实际是调用s3c2440_uart_inf的reset_port函数。
就是s3c2440_serial_resetport函数。s3c2440_serial_resetport函数设置了下面的几步的寄存器。
static struct s3c2410_uartcfg mini2440_uartcfgs[] __initdata = {
[0] = {
.hwport = 0,
.flags = 0,
.ucon = 0x3c5,
.ulcon = 0x03,
.ufcon = 0x51,
},
};
⑦给UCON0寄存器追加写入0x3c5(1111000101)
1:发送时产生电平中断(而不是脉冲中断)
1:接收时产生电平中断
1:允许接收时产生超时中断
1:允许接收时产生错误中断
0:一般模式,不用回环模式,就是一个串口的RX接TX
0:不发送break信号
01:中断或者轮询方式发送数据
01:中断或者轮询方式接收数据
⑧给 ULCON0寄存器写入0x03(00000011)
0: 普通模式(而不是红外模式)
000: 无校验位
0: 1个停止位
11: 8个数据位
⑨给 UFCON0寄存器写入0x51(1010001)
01: Tx FIFO Trigger Level = 16-byte
01: Rx FIFO Trigger Level = 8-byte
0:Reserved
0:
0:
1:使能FIFO
ktermios结构体结构体定义如下:
struct ktermios
{
tcflag_t c_iflag; /* input mode flags */
tcflag_t c_oflag; /* output mode flags */
tcflag_t c_cflag; /* control mode flags */
tcflag_t c_lflag; /* local mode flags */
cc_t c_line; /* line discipline */
cc_t c_cc[NCCS]; /* control characters */
speed_t c_ispeed; /* input speed */
speed_t c_ospeed; /* output speed */
};
下面参考大牛的博客
c_iflag常用的如下:
IGNBRK:忽略输入中的 BREAK 状态
BRKINT
IGNPAR:忽略桢错误和奇偶校验错
PARMRK:如果没有设置 IGNPAR,在有奇偶校验错或桢错误的字符前插入 /377 /0。如果既没有设置 IGNPAR 也没有设置PARMRK,将有奇偶校验错或桢错误的字符视为 /0
INPCK:启用输入奇偶检测
ISTRIP:去掉第八位
INLCR:将输入中的 NL 翻译为 CR
IGNCR:忽略输入中的回车
ICRNL:将输入中的回车翻译为新行 (除非设置了 IGNCR)
IUCLC:将输入中的大写字母映射为小写字母
IXON:启用输入的XON流控制
IXANY:允许任何字符来重新开始输出
IXOFF:启用输入的XFF流控制
c_oflag常用的如下:
OPOST:启用具体实现自行定义的输出处理。
OLCUC:将输出中的小写字母映射为大写字母。
ONLCR:将输出中的新行符映射为回车-换行。
OCRNL:将输出中的回车映射为新行符
ONOCR:不在第 0 列输出回车。
ONLRET:不输出回车。
OFILL:发送填充字符作为延时,而不是使用定时来延时。
OFDEL:填充字符是 ASCII DEL (0177)。如果不设置,填充字符则是 ASCII NUL。
NLDLY:新行延时掩码。取值为 NL0 和 NL1。
CRDLY:回车延时掩码。取值为 CR0, CR1, CR2, 或 CR3。
TABDLY:水平跳格延时掩码。取值为 TAB0, TAB1, TAB2, TAB3 (或 XTABS)。取值为 TAB3,即 XTABS,将扩展跳格为空格 (每个跳格符填充 8 个空格)。
BSDLY:回退延时掩码。取值为 BS0 或 BS1。(从来没有被实现过)
VTDLY:竖直跳格延时掩码。取值为 VT0 或 VT1。
FFDLY:进表延时掩码。取值为 FF0 或 FF1。
c_cflag 标志常量:
CBAUD:波特率掩码 (4+1 位)。
CBAUDEX:扩展的波特率掩码 (1 位),包含在 CBAUD 中。
CSIZE:字符长度掩码。取值为 CS5, CS6, CS7, 或 CS8。
CSTOPB:设置两个停止位,而不是一个。
CREAD:打开接受者。
PARENB:允许输出产生奇偶信息以及输入的奇偶校验。
PARODD:输入和输出是奇校验。
HUPCL:在最后一个进程关闭设备后,降低 modem 控制线 (挂断)。
CLOCAL:忽略 modem 控制线。
LOBLK:从非当前 shell 层阻塞输出(用于 shl )。(?)
CIBAUD:输入速度的掩码。CIBAUD 各位的值与 CBAUD 各位相同,左移了 IBSHIFT 位。
CRTSCTS:启用 RTS/CTS (硬件) 流控制。
在上面的probe函数中,有个重要的结构体uart_ops,这个结构体定义了uart驱动层的操作。
static struct uart_ops s3c24xx_serial_ops =
{
.pm = s3c24xx_serial_pm, //电源管理
.tx_empty = s3c24xx_serial_tx_empty, //检测发送缓冲区是否为空
.get_mctrl = s3c24xx_serial_get_mctrl, //获取串口是否流控
.set_mctrl = s3c24xx_serial_set_mctrl, //设置串口流控
.stop_tx = s3c24xx_serial_stop_tx, //停止发送
.start_tx = s3c24xx_serial_start_tx, //启动发送
.stop_rx = s3c24xx_serial_stop_rx, //停止接收
.enable_ms = s3c24xx_serial_enable_ms,
.break_ctl = s3c24xx_serial_break_ctl, //发送break信号
.startup = s3c24xx_serial_startup, //启动串口的发送接收
.shutdown = s3c24xx_serial_shutdown, //关闭串口
.set_termios = s3c24xx_serial_set_termios, //串口时钟,波特率等参数设置
.type = s3c24xx_serial_type,
.release_port = s3c24xx_serial_release_port, //释放串口
.request_port = s3c24xx_serial_request_port, //申请串口
.config_port = s3c24xx_serial_config_port, //串口配置
.verify_port = s3c24xx_serial_verify_port, //串口校验
};
下面一一分析里面的函数。
if (ufcon & S3C2410_UFCON_FIFOMODE)
{
if ((ufstat & info->tx_fifomask) != 0 ||(ufstat & info->tx_fifofull))
return 0;
return 1;
}
③非FIFO模式下,判断 UTRSTAT0寄存器的第2位,为1表示空了。
static int s3c24xx_serial_txempty_nofifo(struct uart_port *port)
{
return (rd_regl(port, S3C2410_UTRSTAT) & S3C2410_UTRSTAT_TXE);
}
s3c24xx_serial_get_mctrl和s3c24xx_serial_set_mctrl
硬件流控函数
s3c24xx_serial_stop_tx
TX disable,就是把port->unused[0]设置为0,如果是流控的话,还要调用s3c24xx_serial_rx_enable函数打开串口的流控制接收。
s3c24xx_serial_start_tx
TX enable,就是把port->unused[0]设置为1,如果是流控的话,还要调用s3c24xx_serial_rx_disable函数停止串口的流控制接收。
s3c24xx_serial_stop_rx
RX disable, 就是把port->unused[1]设置为0
#define S3C2410_UCON_SBREAK (1<<4)
ucon = rd_regl(port, S3C2410_UCON);
ucon |= S3C2410_UCON_SBREAK;
wr_regl(port, S3C2410_UCON, ucon);
rx_enabled(port) = 1;
ret = request_irq(ourport->rx_irq, s3c24xx_serial_rx_chars, 0, s3c24xx_serial_portname(port), ourport);
tx_enabled(port) = 1;
ret = request_irq(ourport->tx_irq, s3c24xx_serial_tx_chars, 0, s3c24xx_serial_portname(port), ourport);
free_irq(ourport->tx_irq, ourport);
tx_enabled(port) = 0;
free_irq(ourport->rx_irq, ourport);
rx_enabled(port) = 0;
termios->c_cflag &= ~(HUPCL | CMSPAR);
termios->c_cflag |= CLOCAL;
HUPCL:在最后一个进程关闭设备后,降低 modem 控制线 (挂断)。
CLOCAL:忽略 modem 控制线。
②根据termios找到对用的波特率,init_termios.c_cflag |= B9600,返回波特率baud = 9600
baud = uart_get_baud_rate(port, termios, old, 0, 115200*8);
③计算分频系数,分频系数的计算公式为: UBRDIVn = (int)( UART clock / ( buad rate x 16) ) –1
quot = s3c24xx_serial_getclk(port, &clksrc, &clk, baud);
最后将分频系数写入 UBRDIV寄存器
wr_regl(port, S3C2410_UBRDIV, quot);
④获取到时钟源clksrc,clk
⑤刚开始的时候ourport->clksrc = NULL, ourport->baudclk = NULL
需要将时钟源写入 UCON[11:10]
s3c24xx_serial_setsource(port, clksrc);
使能时钟
clk_enable(clk);
将时钟源保存起来,下次时钟源没有更新的时候不用重新设置上面的步骤
ourport->clksrc = clksrc;
ourport->baudclk = clk;
ourport->baudclk_rate = clk ? clk_get_rate(clk) : 0;
⑥ULCON寄存器的设置
设置数据位数,init_termios.c_cflag |= CS8,数据是8位
#define S3C2410_LCON_CS8 (0x3)
ulcon = S3C2410_LCON_CS8;
是否设置2个停止位,我们这里不用设置
#define S3C2410_LCON_STOPB (1<<2)
if (termios->c_cflag & CSTOPB)
ulcon |= S3C2410_LCON_STOPB;
是否设置奇偶校验位,我们这里没有奇偶校验
if (termios->c_cflag & PARENB)
{
if (termios->c_cflag & PARODD)
ulcon |= S3C2410_LCON_PODD;
else
ulcon |= S3C2410_LCON_PEVEN;
}
else
{
ulcon |= S3C2410_LCON_PNONE;
}
⑦UMCON寄存器的设置,主要是设置流控制,我们这里不用流控制
#define S3C2410_UMCOM_AFC (1<<4)
umcon = (termios->c_cflag & CRTSCTS) ? S3C2410_UMCOM_AFC : 0;
wr_regl(port, S3C2410_UMCON, umcon);
⑧给port->read_status_mask赋值
接收溢出错误中断:port->read_status_mask = S3C2410_UERSTAT_OVERRUN;
如果启用检验:
port->read_status_mask |= S3C2410_UERSTAT_FRAME | S3C2410_UERSTAT_PARITY;
我们这里是没有启用校验的。
⑨给port->ignore_status_mask赋值
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;
一定要init_termios.c_cflag |= CREAD,不然会忽略所有的输入字符
if ((termios->c_cflag & CREAD) == 0)
port->ignore_status_mask |= RXSTAT_DUMMY_READ;
Rx FIFO Trigger Level = 8-byte,即缓存中超过了8个字节数据,就会产生中断。
if (s3c24xx_serial_rx_fifocnt(ourport, ufstat) == 0)
break;
ch = rd_regb(port, S3C2410_URXH);
port->icount.rx++;
if (uerstat & S3C2410_UERSTAT_FRAME)
port->icount.frame++;
如果产生溢出,port->icount.overrun加1
if (uerstat & S3C2410_UERSTAT_OVERRUN)
port->icount.overrun++;
uart_insert_char(port, uerstat, S3C2410_UERSTAT_OVERRUN, ch, flag);
tty_flip_buffer_push(tty);
if (port->x_char)
{
wr_regb(port, S3C2410_UTXH, port->x_char);
port->icount.tx++;
port->x_char = 0;
goto out;
}
if (uart_circ_empty(xmit) || uart_tx_stopped(port))
{
s3c24xx_serial_stop_tx(port);
goto out;
}
while (!uart_circ_empty(xmit) && count-- > 0)
{
if (rd_regl(port, S3C2410_UFSTAT) & ourport->info->tx_fifofull)
break;
wr_regb(port, S3C2410_UTXH, xmit->buf[xmit->tail]);
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
port->icount.tx++;
}
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(port);
tasklet_schedule(&state->tlet);
tasklet_init(&state->tlet, uart_tasklet_action, (unsigned long)state);
tty_wakeup(state->port.tty);
if (uart_circ_empty(xmit))
s3c24xx_serial_stop_tx(port);
计算分频系数调用的函数是:
static int s3c24xx_serial_calcbaud(struct baud_calc *calc, struct uart_port *port,
struct s3c24xx_uart_clksrc *clksrc, unsigned int baud)
{
struct s3c24xx_uart_port *ourport = to_ourport(port);
unsigned long rate;
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 (ourport->info->has_divslot)
{
unsigned long div = rate / baud;
/* The UDIVSLOT register on the newer UARTs allows us to
* get a divisor adjustment of 1/16th on the baud clock.
*
* We don't keep the UDIVSLOT value (the 16ths we calculated
* by not multiplying the baud by 16) as it is easy enough
* to recalculate.
*/
calc->quot = div / 16;
calc->calc = rate / div;
}
else
{
calc->quot = (rate + (8 * baud)) / (16 * baud);
calc->calc = (rate / (calc->quot * 16));
}
calc->quot--;
return 1;
}
分频系数的计算公式为: UBRDIVn = (int)( UART clock / ( buad rate x 16) ) –1
获取时钟源,这里的时钟源应该是PCLK,假设为100M
rate = 100000000Hz。
因为是PCLK,clksrc->divisor = 1,因此rate还是100000000Hz
ourport->info->has_divslot这个变量没有定义,因此:
calc->quot = (rate + (8 * baud)) / (16 * baud);
calc->calc = (rate / (calc->quot * 16));
按照计算公式,calc->quot = (rate ) / (16 * baud),可是为什么这里
calc->quot = (rate + (8 * baud)) / (16 * baud)?
原因是:calc->quot值要进行四舍五入。
最后,calc->quot要减1才是实际的值。
static struct s3c2410_uartcfg mini2440_uartcfgs[] __initdata =
{
[0] = {
.hwport = 0,
.flags = 0,
.ucon = 0x3c5,
.ulcon = 0x03,
.ufcon = 0x51,
},
[1] = {
.hwport = 1,
.flags = 0,
.ucon = 0x3c5,
.ulcon = 0x03,
.ufcon = 0x51,
},
[2] = {
.hwport = 2,
.flags = 0,
.ucon = 0x3c5,
.ulcon = 0x03,
.ufcon = 0x51,
}
};
没有定义cfg->clocks_size,那么cfg->clocks_size = 0
当cfg->clocks_size = 0时,采用默认的时钟pclk
static struct s3c24xx_uart_clksrc tmp_clksrc =
{
.name = "pclk",
.min_baud = 0,
.max_baud = 0,
.divisor = 1,
};
最终函数输出:*clksrc = tmp_clksrc
*clk = pclk
但是如果有多个时钟呢,最终采用哪一个时钟?
每个时钟都计算出一个波特率的值,这几个值都比较接近传入的baud的值。并且让resptr指针指向所有时钟源的数组的末尾。
for (i = 0; i < cfg->clocks_size; i++, clkp++)
{
if (s3c24xx_serial_calcbaud(resptr, port, clkp, baud))
resptr++;
}
把每一个时钟源计算出来的波特率值sptr->calc与原来的baud比较,哪一个值最接近baud,就把哪一个作为uart的时钟源。
for (sptr = res; sptr < resptr; sptr++)
{
calc_deviation = baud - sptr->calc;
if (calc_deviation < 0)
calc_deviation = -calc_deviation;
if (calc_deviation < deviation)
{
best = sptr;
deviation = calc_deviation;
}
}
https://www.cnblogs.com/colife/p/5531093.html