Linux UART驱动分析
1. 介绍
8250是IBM PC及兼容机使用的一种串口芯片; 16550是一种带先进先出(FIFO)功能的8250系列串口芯片; 16550A则是16550的升级版本, 修复了FIFO相关BUG, 也是目前比较常见的串口芯片.
本文介绍的是Xilinx UART 驱动分析, 因为没有找到其datasheet, 硬件操作部分分析16550的实现.
Xilinx UART驱动主要由drivers/tty/serial/xilinx_uartps.c来实现
其相关配置和基本信息可参考<Zynq UART>
2. 结构体
uart_driver和console结构变量, 以及实现了uart_ops函数操作集定义如下图所示
static struct uart_driver cdns_uart_uart_driver = {
.owner = THIS_MODULE,
.driver_name = CDNS_UART_NAME, /* xuartps */
.dev_name = CDNS_UART_TTY_NAME, /* ttyPS */
.major = CDNS_UART_MAJOR, /* 0, 注册时动态分配 */
.minor = CDNS_UART_MINOR, /* 0, 注册时动态分配*/
.nr = CDNS_UART_NR_PORTS, /* 2 */
#ifdef CONFIG_SERIAL_XILINX_PS_UART_CONSOLE
.cons = &cdns_uart_console, /* ttyPS */
#endif
};
static struct console cdns_uart_console = {
.name = CDNS_UART_TTY_NAME, /* ttyPS */
.write = cdns_uart_console_write,
.device = uart_console_device,
.setup = cdns_uart_console_setup,
.flags = CON_PRINTBUFFER,
.index = -, /* 由cmdline指定(e.g. console=ttyPS ) */
.data = &cdns_uart_uart_driver,
};
static const struct uart_ops cdns_uart_ops = {
.set_mctrl = cdns_uart_set_mctrl,
.get_mctrl = cdns_uart_get_mctrl,
.start_tx = cdns_uart_start_tx,
.stop_tx = cdns_uart_stop_tx,
.stop_rx = cdns_uart_stop_rx,
.tx_empty = cdns_uart_tx_empty,
.break_ctl = cdns_uart_break_ctl,
.set_termios = cdns_uart_set_termios,
.startup = cdns_uart_startup,
.shutdown = cdns_uart_shutdown,
.pm = cdns_uart_pm,
.type = cdns_uart_type,
.verify_port = cdns_uart_verify_port,
.request_port = cdns_uart_request_port,
.release_port = cdns_uart_release_port,
.config_port = cdns_uart_config_port,
#ifdef CONFIG_CONSOLE_POLL
.poll_get_char = cdns_uart_poll_get_char,
.poll_put_char = cdns_uart_poll_put_char,
#endif
};
3. 初始化
模块入口为cdns_uart_init
首先注册UART驱动
uart_register_driver(&cdns_uart_uart_driver);
随后又注册platform驱动
platform_driver_register(&cdns_uart_platform_driver);
其中cdns_uart_platform_driver定义如下
static const struct of_device_id cdns_uart_of_match[] = {
{ .compatible = "xlnx,xuartps", },
{ .compatible = "cdns,uart-r1p8", },
{ .compatible = "cdns,uart-r1p12", .data = &zynqmp_uart_def },
{ .compatible = "xlnx,zynqmp-uart", .data = &zynqmp_uart_def },
{}
};
static struct platform_driver cdns_uart_platform_driver = {
.probe = cdns_uart_probe,
.remove = cdns_uart_remove,
.driver = {
.name = CDNS_UART_NAME,
.of_match_table = cdns_uart_of_match,
.pm = &cdns_uart_dev_pm_ops,
},
};
而在arch/arm/boot/dts/zynq-7000.dtsi中, 定义了uart设备树相关信息
uart0: serial@e0000000 {
compatible = "xlnx,xuartps", "cdns,uart-r1p8";
status = "disabled";
clocks = <&clkc >, <&clkc >;
clock-names = "uart_clk", "pclk";
reg = <0xE0000000 0x1000>;
interrupts = < >;
};
uart1: serial@e0001000 {
compatible = "xlnx,xuartps", "cdns,uart-r1p8";
status = "disabled";
clocks = <&clkc >, <&clkc >;
clock-names = "uart_clk", "pclk";
reg = <0xE0001000 0x1000>;
interrupts = < >;
};
关于设备树, 可参考<Linux设备树解析>
从文章中我们知道内核会将设备树解析为platform_device, 匹配后则会调用cdns_uart_probe
下面以uart0驱动probe分析一下该函数
int cdns_uart_probe(struct platform_device *pdev)
{
int id, irq;
struct uart_port *port;
struct resource *res;
struct cdns_uart *cdns_uart_data; /* 分配驱动私有数据结构体 */
cdns_uart_data = devm_kzalloc(&pdev->dev, sizeof(*cdns_uart_data), GFP_KERNEL); /* 从dts获取时钟(clocks), pclk=40, uart_clk=23 */
cdns_uart_data->pclk = devm_clk_get(&pdev->dev, "pclk");
cdns_uart_data->uartclk = devm_clk_get(&pdev->dev, "uart_clk");
/* 准备时钟源 */
clk_prepare(cdns_uart_data->pclk);
clk_prepare(cdns_uart_data->uartclk); /* 从dts获取编址(reg), start=0xE0000000,end=0xE0001000 */
platform_get_resource(pdev, IORESOURCE_MEM, ); /* 从dts获取中断(interrupts), 中断号为27 !!! */
platform_get_irq(pdev, ); /* 获取设备编号, 此处为0 */
id = of_alias_get_id(pdev->dev.of_node, "serial"); /* 初始化uart端口 */
port = cdns_uart_get_port(id); /* 设置uart端口硬件相关参数 */
port->mapbase = res->start;
port->irq = irq;
port->dev = &pdev->dev;
port->uartclk = clk_get_rate(cdns_uart_data->uartclk);
port->private_data = cdns_uart_data;
cdns_uart_data->port = port;
platform_set_drvdata(pdev, port); /* 添加uart端口 */
uart_add_one_port(&cdns_uart_uart_driver, port);
} static struct uart_port cdns_uart_port[CDNS_UART_NR_PORTS]; /* 2 */
struct uart_port *cdns_uart_get_port(int id)
{
struct uart_port *port; /* 获取本地定义的uart_port结构体变量 */
port = &cdns_uart_port[id]; spin_lock_init(&port->lock);
port->membase = NULL;
port->irq = ;
port->type = PORT_UNKNOWN; /* 会在config_port中设置为PORT_XUARTPS */
port->iotype = UPIO_MEM32; /* 串口接口寄存器的地址类型 */
port->flags = UPF_BOOT_AUTOCONF; /* 该标志会使uart_add_one_port调用config_port */
port->ops = &cdns_uart_ops; /* 即前面定义的uart_ops函数操作集 */
port->fifosize = CDNS_UART_FIFO_SIZE; /* 64 */
port->line = id; /* 0 */
port->dev = NULL;
return port;
}
4. 16550介绍
16550寄存器信息如下

RBF定义如下

THR定义如下

IER定义如下

IIR定义如下

FCR定义如下

LCR定义如下
MCR定义如下

LSR定义如下

MSR定义如下

SCR定义如下

5. 硬件操作实现
这里分析8250/16550对uart_ops的实现serial8250_pops
主要代码位于drivers/tty/serial/8250/8250_port.c
tx_empty: serial8250_tx_empty
读取并判断LSR的第THRE、TEMT位是否为1
set_mctrl: serial8250_set_mctrl
将位设置(RTS、DTR、OUT1、OUT2、LOOP)写入MCR
get_mctrl: serial8250_get_mctrl
读取MSR, 即Modem Interface的当前状态
stop_tx: serial8250_stop_tx
禁用IER的THRI/ETBEI位
start_tx: serial8250_start_tx
启用IER的THRI/ETBEI位; 当LSR的THRE位为1, 通过操作THR将circ_buf的数据搬运至UART
stop_rx: serial8250_stop_rx
禁用IER的RLSI/ELSI和RDI/ERBFI位
enable_ms: serial8250_enable_ms
启用IER的MSI/EDSSI
break_ctl: serial8250_break_ctl
启动或者禁用LCR的SBC/SetBreak位
startup: serial8250_startup
1. 设置FCR清空FIFO缓冲区, 清空中断寄存器(LSR、RX、IIR、MSR), 初始化相关寄存器
2. 调用uart_8250_ops::setup_irq(univ8250_setup_irq)
3. 设置MCR寄存器
4. 为TX/RX请求DMA通道
univ8250_setup_irq
serial_link_irq_chain
request_irq
serial8250_interrupt
dw8250_handle_irq
/* 即uart_port::handle_irq */
serial8250_handle_irq
handle_rx_dma(Running here???)
serial8250_rx_dma
/* uart_8250_port::uart_8250_dma::rx_dma */
__dma_rx_complete
tty_insert_flip_string
/* 将数据插入接收数据缓冲区 */
tty_flip_buffer_push
/* 将数据搬至线路规程层 */
tty_schedule_flip
flush_to_ldisc
serial8250_rx_chars
serial8250_read_char
uart_insert_char
tty_insert_flip_char
/* 将数据插入接收数据缓冲区 */
tty_flip_buffer_push
/* 将数据搬至线路规程层 */
tty_schedule_flip
flush_to_ldisc
shutdown: serial8250_shutdown
初始化寄存器(...), 注销中断处理程序(???)
set_termios: serial8250_set_termios
设置相关寄存器(...)
set_ldisc: serial8250_set_ldisc
如果没有设置了Modem状态, 则禁用IER的MSI位
pm: serial8250_pm
休眠(???)
type: serial8250_type
获取硬件名称
release_port: serial8250_release_port
释放端口占用物理资源, 如Memory, I/O
request_port: serial8250_request_port
请求物理资源
config_port: serial8250_config_port
按照传入参数配置端口
verify_port: serial8250_verify_port
校验端口配置是否有效
参考:
<Xinu uart-ns16550>
<AXI UART 16550 v2.0>
<XPS 16550 UART v3.00>
<dw_apb_uart Databook>
<Serial UART information>
Linux UART驱动分析的更多相关文章
- linux串口驱动分析
linux串口驱动分析 硬件资源及描写叙述 s3c2440A 通用异步接收器和发送器(UART)提供了三个独立的异步串行 I/O(SIO)port,每一个port都能够在中断模式或 DMA 模式下操作 ...
- Linux spi驱动分析(二)----SPI核心(bus、device_driver和device)
一.spi总线注册 这里所说的SPI核心,就是指/drivers/spi/目录下spi.c文件中提供给其他文件的函数,首先看下spi核心的初始化函数spi_init(void).程序如下: 点击(此处 ...
- Linux I2C驱动分析(三)----i2c_dev驱动和应用层分析 【转】
本文转载自:http://blog.chinaunix.net/uid-21558711-id-3959287.html 分类: LINUX 原文地址:Linux I2C驱动分析(三)----i2c_ ...
- 基于335X的Linux网口驱动分析
基于335X的linux网口驱动分析 一. 系统构成 1. 硬件平台 AM335X 2. LINUX内核版本 4.4.12 二. 网口驱动构架(mdio部分) mdio网口驱动部分 使用 总线.设 ...
- UART驱动分析
在linux用户层上要操作底层串口需要对/dev/ttySxxx操作,这里的ttySx指实际的终端串口. 以下以全志A64为实例,分析UART驱动以及浅谈TTY架构. linux-3.10/drive ...
- linux串口驱动分析【转】
转自:http://blog.csdn.net/hanmengaidudu/article/details/11946591 硬件资源及描述 s3c2440A 通用异步接收器和发送器(UART)提供了 ...
- linux驱动基础系列--Linux I2c驱动分析
前言 主要是想对Linux I2c驱动框架有一个整体的把控,因此会忽略协议上的某些细节,同时里面涉及到的一些驱动基础,比如平台驱动.设备模型.sysfs等也不进行详细说明原理,涉及到i2c协议部分也只 ...
- Linux gadget驱动分析1------驱动加载过程
为了解决一个问题,简单看了一遍linux gadget驱动的加载流程.做一下记录. 使用的内核为linux 2.6.35 硬件为芯唐NUC950. gadget是在UDC驱动上面的一层,如果要编写ga ...
- Linux触摸驱动分析
测试平台 宿主机平台:Ubuntu 12.04.4 LTS 目标机:Easy-ARM IMX283 目标机内核:Linux 2.6.35.3 触摸屏基础知识 一.结构 上图是电阻触摸屏的一个侧面剖视图 ...
随机推荐
- Putty——支持Telnet,ssh连接的连接软件
简介 PuTTY 的官方网站:http://www.chiark.greenend.org.uk/~sgtatham/putty/,截止到 2007年6月,发布的最高稳定版本是 0.6.PuTTY 是 ...
- 最常见的Java面试题及答案汇总(四)
反射 57. 什么是反射? 反射主要是指程序可以访问.检测和修改它本身状态或行为的一种能力 Java反射: 在Java运行时环境中,对于任意一个类,能否知道这个类有哪些属性和方法?对于任意一个对象,能 ...
- 页码0~N ,其中0,1....9都出现了几次
/* 这道题目可以暴力解答:对1~n的每个数进行从低位到高位分析 一旦这个数字num出现,a[num]++即可 第二种方法: 由0,1,...9组成的所有n位数,从n个0到n个9共10^n个数,0,1 ...
- 深入Vue响应式原理
深入Vue.js响应式原理 一.创建一个Vue应用 new Vue({ data() { return { name: 'yjh', }; }, router, store, render: h =& ...
- Linux crontab 查看所有用户的crontab任务
查看所有用户的crontab任务 - mlzhu007的专栏 - CSDN博客 https://blog.csdn.net/mlzhu007/article/details/81662091 以roo ...
- codeforces --- Round #250 (Div. 2) B. The Child and Set
<传送门> [题目大意] 给你一个sum和一个limit,现在要你在1~limit中找到一些数来使得这些数的和等于sum,如果能找到的话就输出找到的数的个数和这些数,未找到输出" ...
- spark2.2 从入门到精通全套视频教程(含网盘下载地址)
Spark2.2从入门到精通链接:https://pan.baidu.com/s/1GnPq_p4wOV916REMB_XJ5w 提取码:16zp
- Maven的仓库和settings.xml配置文件
(尊重劳动成果,转载请注明出处:https://blog.csdn.net/qq_25827845/article/details/83549846冷血之心的博客) 快速导航: Maven基础概念和安 ...
- MOOC 数据库笔记(三):关系模型之基本概念
关系模型的基本概念 关系模型简述 1.最早由E.F.Codd在1970年提出. 2.是从表(Table)及表的处理方式中抽象出来的,是在对传统表及其操作进行数学化严格定义的基础上,引入集合理论与逻辑学 ...
- java之hiberante之集合映射之list映射
这篇讲解 集合映射之List映射 1.通常对于集合,在hibernate中的处理都是使用set来完成.但是hibernate也提供了对于其他几种集合的映射. 在这里实现List的映射,List是有序的 ...