Board logo

标题: linux UART串口驱动开发文档 01 [打印本页]

作者: samwalton    时间: 2013-8-16 10:04     标题: linux UART串口驱动开发文档 01

w83697/w83977 super I/O串口驱动开发
    内容简介: 介绍了Linux下的串口驱动的设计层次及接口, 并指出串口与TTY终端之间的关联层次(串口可作TTY终端使用), 以及Linux下的中断处理机制/中断共享机制, 还有串口缓冲机制当中涉及的软中断机制; 其中有关w83697/w83977 IC方面的知识, 具体参考相关手册, 对串口的配置寄存器有详细介绍, 本文不再进行说明。
    目录索引:
    一。 Linux的串口接口及层次。
    二。 Linux的中断机制及中断共享机制。
    三。 Linux的软中断机制。
    四。 TTY与串口的具体关联。
    一。 Linux的串口接口及层次。
    串口是使用已经非常广的设备了, 因此在linux下面的支持已经很完善了, 具有统一的编程接口, 驱动开发者所要完整的工作就是针对不同的串口IC来做完成相应的配置宏, 这此配置宏包括读与写, 中断打开与关闭(如传送与接收中断), 接收状态处理, 有FIFO时还要处理FIFO的状态。 如下我们就首先切入这一部分, 具体了解一下与硬件串口IC相关的部分在驱动中的处理, 这一部分可以说是串口驱动中的最基础部分, 直接与硬件打交道, 完成最底层具体的串口数据传输。
    1. 串口硬件资源的处理。
    W83697及W83977在ep93xx板子上的映射的硬件物理空间如下:
    W83697: 0x20000000起1K空间。
    W83977: 0x30000000起1K空间。
    因为串口设备的特殊性, 可以当作终端使用, 但是终端的使用在内核还未完全初始化之前(关于串口与终端的关联及层次在第四节中详细), 此时还没有通过mem_init()建立内核的虚存管理机制, 所以不能通过ioreamp来进行物理内存到虚存的映射(物理内存必须由内核映射成系统管理的虚拟内存后才能进行读写访问), 这与先前所讲的framebuffer的物理内存映射是不同的, 具体原因如下:
    √终端在注册并使用的调用路径如下:
    start_kernel→console_init→uart_console_init→ep93xxuart_console_init→register_console→csambuart_console_write.
    √FrameBuffer显卡驱动中的物理内存映射调用路径如下:
    start_kernel→ rest_init→init(内核初始线程)→ do_basic_setup→ do_initcalls→fbmem_init→lanrryfb_init
    (Linux下用__setup启动初期初始机制与__initcall系统初始化完成后的调用机制, 这两个机制本质没有什么差别,主要是执行时所处的系统时段)
    √串口物理内存映射到虚存的时机:
    依据上面所介绍的两条执行路径,再看内核的内存初始化的调用时期,只有完成这个初始化后才能进行物理内存到虚存的映射,内存的初始化主要是在start_kernel中调用的mem_init,这个调用明显在uart_console_init之后,在fbmem_init之后,到此就全部说明了为何不能在对串口使用ioremap进行物理内存的映射了。那么究竟要在什么时机用什么方法进行串口物理内存的映射呢?
    √串口物理内存的映射方式:
    参考ep93xx的板载I/O的映射处理,它的处理方式是一次性将所有的物理I/O所在的内存空间映射到虚存空间,映射的基址是IO_BASE_VIRT,大小是IO_SIZE.
    /* Where in virtual memory the IO devices (timers, system controllers
    * and so on)。 This gets used in arch/arm/mach-ep93xx/mm.c.*/
    #define IO_BASE_VIRT 0xFF000000 // Virtual address of IO
    #define IO_BASE_PHYS 0x80000000 // Physical address of IO
    #define IO_SIZE 0x00A00000 // How much?
    完成映射的函数是ep93xx_map_io, 所有要进行映射内存都在ep93xx_io_desc结构当中描述,我们的串口映射也加在这个地方,基址分别如下:
    文件: linux-2.4.21/include/asm-arm/arch-ep93xx/regmap.h
    #define IO_W83697_UART_BASE 0x20000000
    #define IO_W83697_UART_SIZE 0x1000
    #define IO_W83977_UART_BASE 0x30000000
    #define IO_W83977_UART_SIZE 0x1000
    #define IO_SIZE_2 (IO_SIZE+0x100000)
    #define IO_BASE83697_VIRT IO_BASE_VIRT+IO_SIZE
    #define IO_BASE83977_VIRT IO_BASE_VIRT+IO_SIZE_2
    ep93xx_map_io完成是在arch初始化中赋值给struct machine_desc mdesc这个机器描述结构体,主要由位于mach-ep93xxarch.c文件中如下宏完成此结构的初始化:
    MACHINE_START(EDB9302, “edb9302”)
    ……
    MAPIO(ep93xx_map_io) //初始化。 map_io= ep93xx_map_io…
    MACHINE_END
    最终这个函数在调用路径如下:
    start_kernel→setup_arch→paging_init→(mdesc->map_io())
    至此完成串口物理内存的映射,这个过程在console_init调用之前,因此不会有问题, 此种方法建立映射是直接创建物理内存页与虚存页的对应,大小为4k一页,最终调用的是create_mapping(), 建立页表映射是与具体的平台相关的,位于mach_ep93xx/mm/ proc-arm920.S文件中提供了与具体平台相关的页表建立函数,其中包括TLB表操作/Cache操作/页表操作等:
    在上层的start_kernel→setup_arch→ setup_processor调用下,会在proc-arm920.S文件中查找“.proc.info”节的__arm920_proc_info,并从中找到配置的process相关的操作函数,具体的arm页表建立的详情须要参看arm内存管理的相关手册。
    .section “.proc.info”, #alloc, #execinstr
    .type __arm920_proc_info,#object
    __arm920_proc_info:
    .long 0x41009200
    ……
    .long arm920_processor_functions
    .size __arm920_proc_info, . - __arm920_proc_info
    在arm920_processor_functions中包含的页表操作如下:
    /* pgtable */
    .word cpu_arm920_set_pgd
    .word cpu_arm920_set_pmd
    .word cpu_arm920_set_pte
    2. 与串口硬件相关的宏主。
    如下, 下面将详术如下, 并指出其具体被使用的环境上下文:
    <1>. 读写数据。
    #define UART_GET_CHAR(p) ((readb((p)->membase + W83697_UARTDR)) & 0xff)
    #define UART_PUT_CHAR(p, c) writeb((c), (p)->membase + W83697_UARTDR)
    <2>. 接收发送状态。
    #define UART_GET_RSR(p) ((readb((p)->membase + W83697_UARTRSR)) & 0xff)
    #define UART_PUT_RSR(p, c) writeb((c), (p)->membase + W83697_UARTRSR)
    <3>. 发送及接收中断状态。
    #define UART_GET_CR(p) ((readb((p)->membase + W83697_UARTCR)) & 0xff)
    #define UART_PUT_CR(p,c) writeb((c), (p)->membase + W83697_UARTCR)
    #define UART_GET_INT_STATUS(p) ((readb((p)->membase + W83697_UARTIIR)) & 0xff)
    <4>. 以及其它的中断使能设置等, 在传送时打开传送中断即会产生传送中断。
    #define UART_PUT_ICR(p, c) writeb((c), (p)->membase + W83697_UARTICR)
    <5>. FIFO的状态, 是否读空/是否写满; 每次读时必须读至FIFO空, 写时必须等到FIFO不满时才能写(要等硬件传送完) .
    接收中断读空FIFO的判断:
    status = UART_GET_FR(port);
    while (UART_RX_DATA(status) && max_count--) {
    ……
    }
    发送中断写FIFO: 当发送缓冲区中有数据要传送时, 置发送中断使能, 随后即产生传送中断, 此时FIFO为空, 传送半个FIFO大小的字节, 如果发送缓冲区数据传完,则关闭发送中断。
    <6>. 传送时可直接写串口数据口, 而不使用中断, 但必须等待检测FIFO的状态
    do {
    status = UART_GET_FR(port);
    } while (!UART_TX_READY(status)); //wait for tx buffer not full…




欢迎光临 电子技术论坛_中国专业的电子工程师学习交流社区-中电网技术论坛 (http://bbs.eccn.com/) Powered by Discuz! 7.0.0