• wk2124 在 rk3288 上的适配与调试


    2019-11-25

    关键字:linux驱动开发、arm驱动适配、kernel开发、SPI转串口


    WK2124 是一款 SPI 接口的 4 通道 UART 芯片。说白了就是一款通过 SPI 协议与 CPU 通信并对外表现出具备 4 个 232 串口功能的扩展芯片。它适用于 CPU 引脚资源不够或紧缺的情况,它的最高通信速率能达到 10Mbps。

    本篇文章记述的是 WK2124 芯片在软件上的适配过程,属于软件开发范畴。但其实大家都知道,干到驱动这一层,对硬件电路一窍不通的话那是真干不下去。就拿这块芯片的软件层适配来说,我们需要的官方文档有:

    1、芯片datasheet;

    2、参考驱动程序;

    3、参考原理图。

    这些文件一般芯片厂商会提供,笔者这里也准备好了一份文件,有需要的可以直接下载:

    https://pan.baidu.com/s/1tpjTmRO5xgQXF-w7YsUqdA     提取码: juz1 

    首先来看看 WK2124 的引脚封装,如下图所示:

    对于我们来说,在适配阶段需要关心的脚就 5 个,如上图标红框处所示。这些引脚的功用,datasheet 上都已有很详尽的说明:

    SPI 通信引脚在笔者的 3288 样机上所连接的 CPU 引脚是 SPI2,如下图所示:

    IRQ 脚在笔者的 3288 样机上所连接的 CPU 引脚是 GPIO7_A2,如下图所示:

    然后,还有一个最重要的,就是要让硬件人员确保板子的电路是没有问题的。至于如何让他们确认,就很玄学了。笔者不止一次因板端电路有问题导致驱动调不通,算是被坑怕了。

    以上就是适配 WK2124 芯片的前期准备。接下来就开始真正的适配过程了。

    首先是来配置 dts 文件。根据您的实际编译过程找到您所用到的 dts 文件,并操作里面的 spi2 结构,笔者的修改如下:

     1 &spi2 {
     2     status = "okay";
     3     max-freq = <48000000>;
     4     
     5     spi_wk@20 {
     6         compatible = "rockchip,spi_wk2xxx";
     7         reg = <0>;
     8         spi-max-frequency = <5000000>;
     9         //spi-cpha; //SPI_MODE0
    10         //spi-cpol;
    11         poll_mode = <0>;
    12         type = <0>;
    13         enable_dma = <0>;
    14     };
    15     
    16 };

    简单解释一下,上面第 5 行标注的是驱动名称,除了中间那个 '@' 符号,都可以随便填写。

    第 6 行填写的是驱动名称,这里的值也可以随便填写,但这个值是有依据的,要和驱动代码中配置的值一样。

    第 7 行配置的是片选引脚号。表示这个驱动所接的 SPI 片选引脚是哪一个。rk3288的SPI2有两个片选脚 SPI2_CSN1 与 SPI2_CSN0,如下图所示:

    笔者的 WK2124 的片选信号是接到 SPI2_CSN0 上的,所以第 7 行的值填的是 0。如果您是接到 SPI2_CNS1 上,则填值1。若填写的值超出该SPI最大片选脚号,在注册驱动时会报错。

    第 8 行填写的就是 SPI 的工作频率了,笔者这边为了保险起见使用 5MHZ 的时钟通信。

    第 9 行与第 10 行不配置,在rk3288中,这两个值不配置表示使用 SPI 模式0,而 WK2124 仅支持模式0。

    剩下三行直接填写值0就好了。

    dts 的配置就这么多,下面是驱动程序的适配。

    在笔者前面贴出的云盘下载链接中包含有 WK2124 官方给出的 linux 平台的驱动代码。这份驱动代码的目录结构如下图所示:

    我们需要关注的代码文件已在上图中用红框标注。

    笔者直接将这两个文件拷贝至 rk3288 源码的 SPI 驱动目录下:

    ./kernel/driver/spi/

    接着来修改 Kconfig 文件与 Makefile 文件:

    ./kernel/drivers/spi/Makefile
    ./kernel/drivers/spi/Kconfig

    Makefile 就是添加编译选项而已,告诉系统有哪些源码需要编译。通常直接在末尾加上驱动源码文件即可,如下图

     CONFIG_SPI_WK2124 是可以随便起的。而后面的 wk2xxx_spi 则是要和驱动源码文件的名称一样。

    Kconfig 中的配置是为了让内核在编译时会去编这个驱动,说白了就像一个开关。

    注意这里的字段是 SPI_WK2124,是不带 Makefile 中的 CONFIG_ 前缀的。

    在 Kconfig 中配置了信息以后,就可以在 kernel 的 make menuconfig 时看到这个驱动的配置选项了。不过笔者的 rk3288 关于 menuconfig 不是通过 make menuconfig 来配置的,而是要靠一个外部 config 文件。

    笔者的 rk3288 源码所使用的外部 config 配置文件位于:

    ./kernel/arch/arm/configs/

    在对应文件内添加如下图所示的配置即可:

    如此一来,kernel 层的驱动配置就做完了,我们在编译内核时就会去编译这个 wk2xxx_spi.c 源码的了。不信的话可以尝试着去单独编译一下内核,编译完成以后可以发现多了一个 wk2xxx_spi.o 文件,如下图所示,这就表明 WK2124 的驱动程序已经成功编出来了。

    不过如果你真的信了我的话,直接就尝试去编译,你一定编不出这个 wk2xxx_spi.o 来。因为芯片厂商提供的 linux 平台驱动程序并不是直接适配 rk3288 平台的,有些地方有差异,不修改的话编译是会直接报错的。报错也没什么,至少证明我们前面的配置是正确的,能让内核认到这个驱动程序。

    因为官方驱动程序所使用的平台不同,所以原版驱动程序有些字段是 rk3288 平台上没有的,如下图所示的 __devexit 字段:

    还有就是部分头文件是缺失或所处位置不同的。这种代码语法结构问题并不多,大家自行对照着编译错误去解决就好。文末也会附上笔者已经调好的驱动源码的。

    在驱动初始化中我们可以看到它就是简单地注册一个 SPI 驱动节点而已,如下图所示:

    上图第 1703 行的 .of_match_table 以及第 1693 行的 wk2xxx_spi_dt_match[] 都是笔者自己添加的,它们的作用就是为了适配前面 dts 配置中的驱动信息配置。简单说就是照着上图的样式来配,内核在启动时就能通过 dts 找到这个驱动程序去加载运行。还记得前面在配置 dts 节点信息时提过的 compatible 信息要与驱动中的配置信息一致的吗?这不在上图所示代码的第 1694 行应验了。

    当SPI驱动注册成功后,我们可以在板子上找到如下图所示的节点信息:

    那这个目录里面有些什么呢?没什么,对我们的芯片适配一点用也没有,纯粹是让您知道我们的SPI驱动注册成功了,后续可以通过代码来操控 CPU 上的 SPI2 资源而已。

    那接下来,由于我们前面在 dts 中有配置 compatible,因此内核还会去跑 'wk2xxx_probe()' 函数。

    那我们要在这个 wk2xxx_probe() 函数中做些什么事呢? 在回答之前先来想一想我们的引起这款芯片的初衷:为了通过一组 SPI 资源来实现扩展 4 个普通串口资源的目的。所以,我们最终的目的是要通过这款芯片对外开放 4 个通用串口设备出来,如 /dev/ttyS* 这种。而这个 SPI 驱动对用户来说是不重要的,是透明的。因此,我们得在这个 wk2xxx_probe() 函数中“弄”4个普通串口设备放到 /dev 目录下去。当然,这个动作并不是一定要在 probe() 函数中去做的,你直接在 init() 函数中做也一样。只不过我们为了遵循驱动开发规范而有意放在 probe() 中去实现而已。

    在官方驱动程序中,probe() 函数干的事情就是像普通串口驱动一样去调用 uart_register_driver() 函数注册串口驱动而已。但因为我们这个串口驱动是模拟出来的,所以它的注册方式还有点不太一样。具体有哪些不一样呢?见下图:

    NR_PORTS 表示要注册的串口数量。WK2124是一拖四的串口芯片,理论上来说我们可以注册 0 ~ 4 个串口驱动设备。但建议最好注册 4 个设备文件,因为笔者发现注册数量少于这个时会出现串口中断上不来的情况,具体原因没去查,也懒得去查。

    这里还要关注一下上图所示代码中的 mapbase 字段,它的值填的是 rk3288 SPI2 的寄存器地址。它的值在 ./kernel/arch/arm/boot/dts/rk3288.dtsi 中可以查到:

    另外就是 irq 字段了,这个值比较重要。它填入的是 WK2124 芯片的中断脚所接入的 CPU 引脚号。笔者的开发板接的是 GPIO7_A2,换算过后引脚号是 226。怎么得来的呢? 7 * 32 + 2 = 226。更具体的换算过程请参阅笔者的另一篇文章:rk平台控制GPIO方式

    在 rk3288 平台,引脚号转换成中断号直接调用 gpio_to_irq() 函数转换就行了。

    至于其它的配置,照着填写就好了。

    另外,rk3288 的串口功能似乎默认是配置了“回显”功能的。需要我们在注册串口驱动后手动关闭回显功能,很简单,加多一行代码就行,如下图所示:

    额外提一点:串口默认是会对部分数据做出额外响应的。ASCII码中有部分是属于“指令”来的,默认情况下串口驱动会对这些“指令”做出相应的响应,从而可能改变原始数据。如果想关闭这种响应,让串口变成一个纯粹的透明传输通信线,则可以在串口驱动注册时加上如下所示的设置:

        serial_rk_reg.tty_driver->init_termios.c_cc[VTIME] = 2; //等待200ms,若无数据则返回。要与VMIN配合控制。
        serial_rk_reg.tty_driver->init_termios.c_cc[VMIN] = 90; //等待接收n个字节,接收到足够的数据,立即返回,接收到不足,则等待VTIME超时后返回。
        serial_rk_reg.tty_driver->init_termios.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN); //关闭普通串口的回显功能。2020-03-25.
        
        serial_rk_reg.tty_driver->init_termios.c_lflag &= ~(ICANON|ECHO|ECHOE|ISIG);//原始模式
    
        serial_rk_reg.tty_driver->init_termios.c_iflag &= ~(INLCR|ICRNL);//输入不要回车和换行转换
        serial_rk_reg.tty_driver->init_termios.c_iflag &= ~(IXON|IXOFF|IXANY);//输入不要软件流控制
    
        serial_rk_reg.tty_driver->init_termios.c_oflag &= ~OPOST;
        serial_rk_reg.tty_driver->init_termios.c_oflag &= ~(ONLCR|OCRNL);//输出不要回车和换行转换
        serial_rk_reg.tty_driver->init_termios.c_oflag &= ~(IXON|IXOFF|IXANY);//输出不要软件流控制

    如此,我们就可以在系统运行起来以后在 /dev 目录下看到我们注册的几个虚拟串口设备文件了。

    不过,我们在 rk3288 上极有可能还无法正常读写 wk2124 芯片的寄存器。所表现出来的情况可能是在板子上电时可以正确读出一次寄存器的值,但后续的读写一直都是不成功的,写寄存器时会返回执行成功的代码,但读取时一直返回0。出现这种现象的原因很有可能是因为芯片的片选电平不正确。有条件的同学可以通过示波器来抓取片选脚的波形来验证。

    通过芯片手册我们可以知道,这款芯片在通信前需要将片选电平拉低,并在通信后将其拉高,如下图所示:

     如果您发现自己手里的芯片在通信过程中片选电平没有变化,那极有可能问题就是出在这里了。

    怎么解决呢?可以去看一下驱动代码中读写寄存器的函数:

    1、wk2xxx_read_reg()

    2、wk2xxx_write_reg()

    3、wk2xxx_read_fifo()

    4、wk2xxx_write_fifo()

    在几个函数中将 spi_transfer 结构体中的 cs_change 字段的值改为 0 去尝试一下。这个字段就是控制片选信号在通信时的翻转用的。

    如果发现即使这样更改也不能正确设置片选脚的电平,那么我们还可以通过将片选脚当成普通 GPIO 脚来看,通过代码手动设置电平值。不过这种方式很不推荐使用,因为它会占用 CPU 资源,而且我们通过代码拉了引脚电平以后一定要延时一段时间,不然可能电平还没拉下去,数据信号就出来了,这样就会导致通信失败。笔者这里仅仅贴一个将 CS 脚当成普通 GPIO 口来看待时的控制代码。完整的代码就不贴了,因为其实不推荐这样使用。

    cs0 = 263;
    printk("cs0 pin:%d
    ", cs0);
    if (!gpio_is_valid(cs0))
    {
    printk("cs0 pin invalid
    ");
    return -1;
    } 
    ret = gpio_request(cs0, "wk2xxxspi");
    if (ret != 0)
    {
    gpio_free(cs0);
    printk("request cs0 pin failed
    ");
    return -EIO;
    }
    gpio_direction_output(cs0, 0);
    gpio_set_value(cs0, 1);

    至于其它的就没有了,即使有也是小问题了。官方给的驱动程序对串口的读写都已经封装好了,对性能没什么要求的话直接用就是了。

    /*ch
    *   WKIC Ltd.
    *   By  Xu XunWei Tech 
    *   DEMO Version :1.1 Data:2016-6-25
    *
    *
    *
    *   1. compiler warnings all changes
    */
    
    #include <linux/init.h>                        
    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/init.h>
    #include <linux/slab.h>
    #include <linux/console.h>
    #include <linux/serial_core.h>
    #include <linux/delay.h>
    #include <linux/err.h>
    #include <linux/freezer.h>
    #include <linux/spi/spi.h>
    #include <linux/timer.h>
    #include <linux/workqueue.h>
    #include <linux/platform_device.h>
    #include <linux/tty.h>
    #include <linux/tty_flip.h>
    #include <linux/of.h>
    #include <linux/of_gpio.h>
    #include <linux/gpio.h>
    
    #include <asm/irq.h>
    #include <asm/io.h>
    #include "wk2xxx.h"
    
    
    
    MODULE_LICENSE("Dual BSD/GPL");
    
    #define SPI_BUFSIZ      max(32,SMP_CACHE_BYTES)
    //#define _DEBUG_WK2XXX
    //#define _DEBUG_WK2XXX1
    //#define _DEBUG_WK2XXX2
    //#define _DEBUG_WK2XXX4
    //#define _DEBUG_WK2XXX5
    #define CONFIG_DEVFS_FS
    
    
    #define WK2XXX_PAGE1        1
    #define WK2XXX_PAGE0        0
    
    
    #define WK2XXX_STATUS_PE    1
    #define WK2XXX_STATUS_FE    2
    #define WK2XXX_STATUS_BRK   4
    #define WK2XXX_STATUS_OE    8
    
    
    
    static DEFINE_MUTEX(wk2xxxs_lock);                /* race on probe */
    static DEFINE_MUTEX(wk2xxxs_reg_lock);
    static DEFINE_MUTEX(wk2xxs_work_lock);                /* work on probe */
    
    struct wk2xxx_port 
    {
        //struct timer_list mytimer;    
        
        struct uart_port port;//[NR_PORTS];
        struct spi_device *spi_wk;
        spinlock_t conf_lock;   /* shared data */
        struct workqueue_struct *workqueue;
        struct work_struct work;
        int suspending;
        void (*wk2xxx_hw_suspend) (int suspend);
        int tx_done;
    
        int force_end_work;
        int irq;
        int minor;      /* minor number */
        int tx_empty; 
        int tx_empty_flag;
    
    
        int start_tx_flag;
        int stop_tx_flag;
        int stop_rx_flag; 
        int irq_flag;
        int conf_flag;
    
        int tx_empty_fail;
        int start_tx_fail;
        int stop_tx_fail;
        int stop_rx_fail;
        int irq_fail;
        int conf_fail;
    
    
        uint8_t new_lcr;
        uint8_t new_scr; 
        /*set baud 0f register*/
        uint8_t new_baud1;
        uint8_t new_baud0;
        uint8_t new_pres;
    
    };
    
    static struct wk2xxx_port wk2xxxs[NR_PORTS]; /* the chips */
    
    static int wk2xxx_read_reg(struct spi_device *spi,uint8_t port,uint8_t reg,uint8_t *dat)
    {
        struct spi_message msg;
        uint8_t buf_wdat[2];
        uint8_t buf_rdat[2];
        int status;
        struct spi_transfer index_xfer = {
                .len            = 2,
                .cs_change      = 0,
        };
        mutex_lock(&wk2xxxs_reg_lock);
        status =0;
        spi_message_init(&msg);
        buf_wdat[0] = 0x40|(((port-1)<<4)|reg);
        buf_wdat[1] = 0x00;
        buf_rdat[0] = 0x00;
        buf_rdat[1] = 0x00;
        index_xfer.tx_buf = buf_wdat;
        index_xfer.rx_buf =(void *) buf_rdat;
        spi_message_add_tail(&index_xfer, &msg);
        status = spi_sync(spi, &msg);
        udelay(3);
        mutex_unlock(&wk2xxxs_reg_lock);
        if(status)
        {
            return status;
        }
        *dat = buf_rdat[1];
         return 0;
    
    }
    
    static int wk2xxx_write_reg(struct spi_device *spi,uint8_t port,uint8_t reg,uint8_t dat)
    {
        struct spi_message msg;
        uint8_t buf_reg[2];
        int status;
        struct spi_transfer index_xfer = {
                .len            = 2,
                .cs_change      = 0,
        };
        mutex_lock(&wk2xxxs_reg_lock);
        spi_message_init(&msg);
        /* register index */
        buf_reg[0] = ((port - 1) << 4) | reg;
        buf_reg[1] = dat;
        index_xfer.tx_buf = buf_reg;
        spi_message_add_tail(&index_xfer, &msg);
        status = spi_sync(spi, &msg);
        udelay(3);
        mutex_unlock(&wk2xxxs_reg_lock);
    
        return status;
    }
    
    #define MAX_RFCOUNT_SIZE 256
    static int wk2xxx_read_fifo(struct spi_device *spi,uint8_t port,uint8_t fifolen,uint8_t *dat)
    {
        struct spi_message msg;
        int status,i;
        uint8_t recive_fifo_data[MAX_RFCOUNT_SIZE+1]={0};
        uint8_t transmit_fifo_data[MAX_RFCOUNT_SIZE+1]={0};
        struct spi_transfer index_xfer = {
                .len            = fifolen+1,
                .cs_change      = 0,
        };  
        mutex_lock(&wk2xxxs_reg_lock);
        spi_message_init(&msg);
        /* register index */
        transmit_fifo_data[0] = ((port-1)<<4)|0xc0;
        index_xfer.tx_buf = transmit_fifo_data;
        index_xfer.rx_buf =(void *) recive_fifo_data;
        spi_message_add_tail(&index_xfer, &msg);
        
        status = spi_sync(spi, &msg);
        udelay(3);
        for(i=0;i<fifolen;i++)
            *(dat+i)=recive_fifo_data[i+1];
        mutex_unlock(&wk2xxxs_reg_lock);
    
       // printk("%s-------exit------
    ",__func__);
        return status;
    }
    
    static int wk2xxx_write_fifo(struct spi_device *spi,uint8_t port,uint8_t fifolen,uint8_t *dat)
    {
            struct spi_message msg;
            int status,i;
            uint8_t recive_fifo_data[MAX_RFCOUNT_SIZE+1]={0};
            uint8_t transmit_fifo_data[MAX_RFCOUNT_SIZE+1]={0};
            struct spi_transfer index_xfer = {
                    .len            = fifolen+1,
                    .cs_change      = 0,
            };      
             mutex_lock(&wk2xxxs_reg_lock);
            spi_message_init(&msg);
            /* register index */
            transmit_fifo_data[0] = ((port-1)<<4)|0x80;
            
             for(i=0;i<fifolen;i++)
                {
                transmit_fifo_data[i+1]=*(dat+i);
                }
            index_xfer.tx_buf = transmit_fifo_data;
            index_xfer.rx_buf =(void *) recive_fifo_data;
            spi_message_add_tail(&index_xfer, &msg);
                    
            status = spi_sync(spi, &msg);
            udelay(3);
    
            mutex_unlock(&wk2xxxs_reg_lock);
    
            //printk("%s-------exit------
    ",__func__);
            return status;
            
    }
    
    static void wk2xxxirq_app(struct uart_port *port);
    static void conf_wk2xxx_subport(struct uart_port *port);
    static void wk2xxx_work(struct work_struct *w);
    static void wk2xxx_stop_tx(struct uart_port *port);
    static u_int wk2xxx_tx_empty(struct uart_port *port);// or query the tx fifo is not empty?
    
    static int wk2xxx_dowork(struct wk2xxx_port *s)
    {    
    #ifdef _DEBUG_WK2XXX
        printk("--wk2xxx_dowork---in---
    ");
    #endif
    
        if (!s->force_end_work && !work_pending(&s->work) && !freezing(current) && !s->suspending)
        {
            queue_work(s->workqueue, &s->work);
            #ifdef _DEBUG_WK2XXX
            printk("--queue_work---ok---
    ");
            printk("--wk2xxx_dowork---exit---
    ");
            #endif
            return 1;   
        }
        else
        {
            #ifdef _DEBUG_WK2XXX
            printk("--queue_work---error---
    ");
            printk("--wk2xxx_dowork---exit---
    ");
            #endif
    
            return 0;
        }
    }
    
    static void wk2xxx_work(struct work_struct *w)
    {  
    #ifdef _DEBUG_WK2XXX
        printk("--wk2xxx_work---in---
    ");
    #endif
    
        struct wk2xxx_port *s = container_of(w, struct wk2xxx_port, work);
        uint8_t rx;
        int work_start_tx_flag; 
        int work_stop_rx_flag;
        int work_irq_flag;
        int work_conf_flag;
        do {
            mutex_lock(&wk2xxs_work_lock);
    //        printk("start_tx_flag:%d
    ", s->start_tx_flag);
            work_start_tx_flag = s->start_tx_flag;
            if(work_start_tx_flag)
                s->start_tx_flag = 0;
            work_stop_rx_flag = s->stop_rx_flag;
            if(work_stop_rx_flag)
                s->stop_rx_flag = 0;
            work_conf_flag = s->conf_flag;
    //        printk("irq_flag:%d
    ", s->irq_flag);
            work_irq_flag = s->irq_flag;
            if(work_irq_flag)
                s->irq_flag = 0;
            mutex_unlock(&wk2xxs_work_lock);
        
    //        printk("work_start_tx_flag:%d
    ", work_start_tx_flag);
            if(work_start_tx_flag)
            {
                wk2xxx_read_reg(s->spi_wk, s->port.iobase, WK2XXX_SIER, &rx);
                rx |= WK2XXX_TFTRIG_IEN; 
                wk2xxx_write_reg(s->spi_wk, s->port.iobase, WK2XXX_SIER, rx);
    #ifdef _DEBUG_WK2XXX1 
                wk2xxx_read_reg(s->spi_wk, s->port.iobase, WK2XXX_SIER, &rx);
                printk("w2xxx_work----start_tx_flag:%d--SIER:0x%x--
    ", work_start_tx_flag, rx);
    #endif
            }
    
    //        printk("work_stop_rx_flag:%d
    ", work_stop_rx_flag);
            if(work_stop_rx_flag)
            {
                wk2xxx_read_reg(s->spi_wk,s->port.iobase,WK2XXX_SIER,&rx);
    #ifdef _DEBUG_WK2XXX1 
                printk("stop_rx_flag----SIER:%d--
    ",rx);
    #endif
                rx &=~WK2XXX_RFTRIG_IEN;
                rx &=~WK2XXX_RXOUT_IEN;
                wk2xxx_write_reg(s->spi_wk,s->port.iobase,WK2XXX_SIER,rx);
    #ifdef _DEBUG_WK2XXX1 
                printk("stop_rx_flag----SIFR:%d--
    ",rx);
    #endif
                wk2xxx_read_reg(s->spi_wk,s->port.iobase,WK2XXX_SIFR,&rx);
                rx &= ~WK2XXX_RFTRIG_INT;
                rx &= ~WK2XXX_RXOVT_INT;
                wk2xxx_write_reg(s->spi_wk,s->port.iobase,WK2XXX_SIFR,rx);
    #ifdef _DEBUG_WK2XXX1 
                printk("stop_rx_flag----SIFR:%d--
    ",rx);
    #endif
            }
    
            #ifdef _DEBUG_WK2XXX1
            printk("work_irq_flag:%d
    ", work_irq_flag);
            #endif
            if(work_irq_flag)
            {
                wk2xxxirq_app(&s->port);
                s->irq_fail = 1;
            }
    
        }while (!s->force_end_work && !freezing(current) && (work_irq_flag || work_stop_rx_flag ));
    
        #ifdef _DEBUG_WK2XXX1
        printk("start_tx_fail:%d, stop_rx_fail:%d, irq_fail:%d
    ", s->start_tx_fail, s->stop_rx_fail, s->irq_fail);
        #endif
        if(s->start_tx_fail)
        {
            wk2xxx_read_reg(s->spi_wk,s->port.iobase,WK2XXX_SIER,&rx);
            rx |= WK2XXX_TFTRIG_IEN;
            wk2xxx_write_reg(s->spi_wk,s->port.iobase,WK2XXX_SIER,rx);
            s->start_tx_fail =0;
        }
    
        if(s->stop_rx_fail)
        {
            wk2xxx_read_reg(s->spi_wk,s->port.iobase,WK2XXX_SIER,&rx);
            rx &=~WK2XXX_RFTRIG_IEN;
            rx &=~WK2XXX_RXOUT_IEN;
            wk2xxx_write_reg(s->spi_wk,s->port.iobase,WK2XXX_SIER,rx);
    
            wk2xxx_read_reg(s->spi_wk,s->port.iobase,WK2XXX_SIFR,&rx);
            rx &= ~WK2XXX_RFTRIG_INT;
            rx &= ~WK2XXX_RXOVT_INT;
            wk2xxx_write_reg(s->spi_wk,s->port.iobase,WK2XXX_SIFR,rx);
            s->stop_rx_fail =0;
        }
    
        if(s->irq_fail)
        {
            s->irq_fail = 0;
            enable_irq(s->port.irq);
        }
    
    #ifdef _DEBUG_WK2XXX
        printk("--wk2xxx_work---exit---
    ");
    #endif
    }
    
    
    static void wk2xxx_rx_chars(struct uart_port *port)//vk32xx_port *port)
    {
    #ifdef _DEBUG_WK2XXX
        printk("wk2xxx_rx_chars()---------in---
    ");
    #endif
    
        struct wk2xxx_port *s = container_of(port,struct wk2xxx_port,port);
        uint8_t fsr,lsr,dat[1],rx_dat[256]={0};
        unsigned int ch,flg,sifr, ignored=0,status = 0,rx_count=0;
        int rfcnt=0,rx_num=0;
        wk2xxx_write_reg(s->spi_wk,s->port.iobase,WK2XXX_SPAGE,WK2XXX_PAGE0);//set register in page0
        wk2xxx_read_reg(s->spi_wk,s->port.iobase,WK2XXX_FSR,dat);
        fsr = dat[0];
        wk2xxx_read_reg(s->spi_wk,s->port.iobase,WK2XXX_LSR,dat);
        lsr = dat[0];
        wk2xxx_read_reg(s->spi_wk,s->port.iobase,WK2XXX_SIFR,dat);
        sifr=dat[0];
    #ifdef _DEBUG_WK2XXX
          printk("rx_chars()-port:%d--fsr:0x%x--lsr:0x%x--
    ",s->port.iobase,fsr,lsr);
    #endif
    if(!(sifr&0x80))//no error
    {  
    
    
      flg = TTY_NORMAL;
      if (fsr& WK2XXX_RDAT)
      {
        wk2xxx_read_reg(s->spi_wk,s->port.iobase,WK2XXX_RFCNT,dat);
        rfcnt=dat[0];
        if(rfcnt==0)
        {
         rfcnt=255; 
        }
    #ifdef _DEBUG_WK2XXX
    
            printk("1wk2xxx_rx_chars()----port:%d--RFCNT:0x%x----
    ",s->port.iobase,rfcnt);
    #endif
    
    #if 1  
        wk2xxx_read_fifo(s->spi_wk,s->port.iobase, rfcnt,rx_dat);
    #else
          for(rx_num=0;rx_num<rfcnt;rx_num++)
             {
               wk2xxx_read_reg(s->spi_wk,s->port.iobase,WK2XXX_FDAT,dat);
               rx_dat[rx_num]=dat[0];
             }
    #endif
    
    
    
       //printk("rx_chars_wk2xxx_read_fifo!!!!!!!!!!!
    ");
        s->port.icount.rx+=rfcnt;
            for(rx_num=0;rx_num<rfcnt;rx_num++)
        {
            if (uart_handle_sysrq_char(&s->port,rx_dat[rx_num]))//.state, ch))
                   break;//
                   
     #ifdef _DEBUG_WK2XXX5 
            printk("rx_chars:0x%x----
    ",rx_dat[rx_num]);
    #endif
                      uart_insert_char(&s->port, status, WK2XXX_OE, rx_dat[rx_num], flg);
                      rx_count++;
     
                      if ((rx_count >= 64 ) && (s->port.state->port.tty != NULL))
                      {
                       tty_flip_buffer_push(s->port.state->port.tty);
                       rx_count = 0;
                     }
    
        }//for
        #if 1
           if((rx_count > 0)&&(s->port.state->port.tty != NULL))
      {
      #ifdef _DEBUG_WK2XXX
              printk( "push buffer tty flip port = :%d count = :%d
    ",s->port.iobase,rx_count);
      #endif
             tty_flip_buffer_push(s->port.state->port.tty->port);
             rx_count = 0;
      }
    #endif
      }
    }//ifm
    else//error
    {
        while (fsr& WK2XXX_RDAT)/**/
            {
                wk2xxx_read_reg(s->spi_wk,s->port.iobase,WK2XXX_FDAT,dat);
            ch = (int)dat[0];
    
    #ifdef _DEBUG_WK2XXX
    
            printk("wk2xxx_rx_chars()----port:%d--RXDAT:0x%x----
    ",s->port.iobase,ch);
    #endif
    
            s->port.icount.rx++;
            //rx_count++;
    #ifdef _DEBUG_WK2XXX1
                printk("wk2xxx_rx_chars()----port:%d error
    ",s->port.iobase);
    #endif
                flg = TTY_NORMAL;
            if (lsr&(WK2XXX_OE |WK2XXX_FE|WK2XXX_PE|WK2XXX_BI))
                    {
    //               printk("wk2xxx_rx_chars()----port:%lx error,lsr:%x!!!!!!!!!!!!!!!!!
    ",s->port.iobase,lsr);
                            //goto handle_error;
                if (lsr & WK2XXX_PE)
                    {
                            s->port.icount.parity++;
                            status |= WK2XXX_STATUS_PE;
                            flg = TTY_PARITY;
                    }
                    if (lsr & WK2XXX_FE)
                    {
                            s->port.icount.frame++;
                            status |= WK2XXX_STATUS_FE;
                            flg = TTY_FRAME;
                    }
                    if (lsr & WK2XXX_OE)
                    {
                            s->port.icount.overrun++;
                            status |= WK2XXX_STATUS_OE;
                            flg = TTY_OVERRUN;
                    }
                    if(lsr&fsr & WK2XXX_BI)
                    {
                            s->port.icount.brk++;
                            status |= WK2XXX_STATUS_BRK;
                            flg = TTY_BREAK;
                    }
                    
                    if (++ignored > 100) 
                           goto out;
                    
                     goto ignore_char;       
        }
    
    error_return:
        if (uart_handle_sysrq_char(&s->port,ch))//.state, ch))
            goto ignore_char;
            
            uart_insert_char(&s->port, status, WK2XXX_STATUS_OE, ch, flg);
            rx_count++;
            
            if ((rx_count >= 64 ) && (s->port.state->port.tty != NULL)) 
            {
             tty_flip_buffer_push(s->port.state->port.tty);
             rx_count = 0;
            } 
    #ifdef _DEBUG_WK2XXX1
                    printk( " s->port.icount.rx = 0x%X char = 0x%X flg = 0x%X port = %d rx_count = %d
    ",s->port.icount.rx,ch,flg,s->port.iobase,rx_count);
    #endif
            ignore_char:
    
                wk2xxx_read_reg(s->spi_wk,s->port.iobase,WK2XXX_FSR,dat);
                fsr = dat[0];
                wk2xxx_read_reg(s->spi_wk,s->port.iobase,WK2XXX_LSR,dat);
                lsr = dat[0];
            }
    out:
    if((rx_count > 0)&&(s->port.state->port.tty != NULL))
    {
    #ifdef _DEBUG_WK2XXX1
            printk( "push buffer tty flip port = :%d count = :%d
    ",s->port.iobase,rx_count);
    #endif
            tty_flip_buffer_push(s->port.state->port.tty);
            rx_count = 0;
    }
    
    }//if()else
    
     #if 0
       printk( " rx_num = :%d
    ",s->port.icount.rx);
     #endif
    
    #ifdef _DEBUG_WK2XXX
                     printk("wk2xxx_rx_chars()---------out---
    ");
    #endif
    
          return;
    #ifdef SUPPORT_SYSRQ
            s->port.state->sysrq = 0;
    #endif
            goto error_return;
    
    #ifdef _DEBUG_WK2XXX
        printk("--wk2xxx_rx_chars---exit---
    ");
    #endif
    
    }
    
    static void wk2xxx_tx_chars(struct uart_port *port)//
    {
    #ifdef _DEBUG_WK2XXX
        printk("--wk2xxx_tx_chars---in---
    ");
    #endif
    
        struct wk2xxx_port *s = container_of(port,struct wk2xxx_port,port);
        uint8_t fsr,tfcnt,dat[1],txbuf[255]={0};
        int count,tx_count,i;
        wk2xxx_write_reg(s->spi_wk, s->port.iobase, WK2XXX_SPAGE,WK2XXX_PAGE0);//set register in page0
        if (s->port.x_char) 
        {
    #ifdef _DEBUG_WK2XXX
            printk("wk2xxx_tx_chars   s->port.x_char:%x,port = %d
    ",s->port.x_char,s->port.iobase);
    #endif
            wk2xxx_write_reg(s->spi_wk,s->port.iobase,WK2XXX_FDAT,s->port.x_char);
            s->port.icount.tx++;
            s->port.x_char = 0;
            goto out;
        }
    
        if(uart_circ_empty(&s->port.state->xmit) || uart_tx_stopped(&s->port))
        {
            goto out;
        }
    
        /*
         * Tried using FIFO (not checking TNF) for fifo fill:
         * still had the '1 bytes repeated' problem.
        */
        wk2xxx_read_reg(s->spi_wk, s->port.iobase, WK2XXX_FSR, dat);
        fsr = dat[0];
    
        wk2xxx_read_reg(s->spi_wk, s->port.iobase, WK2XXX_TFCNT, dat); 
        tfcnt= dat[0];
    #ifdef _DEBUG_WK2XXX
        printk("wk2xxx_tx_chars   fsr:0x%x,rfcnt:0x%x,port = %x
    ",fsr,tfcnt,s->port.iobase);
    #endif
    
    #if 1
        if(tfcnt==0)
        {
          if(fsr & WK2XXX_TFULL)
            {
            tfcnt=255;
            tx_count=0;
            }
          else 
            {
            tfcnt=0;
            tx_count=255;
            }
        }
        else
        {
            tx_count=255-tfcnt;
            #ifdef _DEBUG_WK2XXX
            printk("wk2xxx_tx_chars2   tx_count:%x,port = %x
    ",tx_count,s->port.iobase);
            #endif 
        }
    #endif
       
        count = tx_count;
        i=0;
        do
        {
            if(uart_circ_empty(&s->port.state->xmit))
                break;
            txbuf[i]=s->port.state->xmit.buf[s->port.state->xmit.tail];
            s->port.state->xmit.tail = (s->port.state->xmit.tail + 1) & (UART_XMIT_SIZE - 1);
            s->port.icount.tx++;
            i++;
    #ifdef _DEBUG_WK2XXX
            printk("tx_chars:0x%x--
    ",txbuf[i-1]);
    #endif
        }while(--count > 0);
    
    #ifdef _DEBUG_WK2XXX5 
        printk("tx_chars I:0x%x--
    ",i);
    #endif
    
    #if 1
        wk2xxx_write_fifo(s->spi_wk, s->port.iobase, i, txbuf);
    #else
        for(count=0;count<i;count++)
        {
           wk2xxx_write_reg(s->spi_wk,s->port.iobase,WK2XXX_FDAT,txbuf[count]);    
        }
    #endif
    
    out:
        wk2xxx_read_reg(s->spi_wk,s->port.iobase,WK2XXX_FSR,dat);
        fsr = dat[0];
        if(((fsr&WK2XXX_TDAT)==0)&&((fsr&WK2XXX_TBUSY)==0))
        {
            if (uart_circ_chars_pending(&s->port.state->xmit) < WAKEUP_CHARS)
               uart_write_wakeup(&s->port); 
            if (uart_circ_empty(&s->port.state->xmit))
            {
               wk2xxx_stop_tx(&s->port);
            }
        }
    #ifdef _DEBUG_WK2XXX
        printk("--wk2xxx_tx_chars---exit---
    ");
    #endif
    }
    
    static irqreturn_t wk2xxx_irq(int irq, void *dev_id)//
    {
    #ifdef _DEBUG_WK2XXX
        printk("--wk2xxx_irq---in---
    ");
    #endif
    
        struct wk2xxx_port *s = dev_id;
        disable_irq_nosync(s->port.irq);
        s->irq_flag = 1;
        if(wk2xxx_dowork(s))
        {
            //s->irq_flag = 1;
        }
        else
        {
            s->irq_flag = 0;
            s->irq_fail = 1;    
        }
        
    #ifdef _DEBUG_WK2XXX
        printk("--wk2xxx_irq---exit---
    ");
    #endif
    
        return IRQ_HANDLED;
    }
    
    static void wk2xxxirq_app(struct uart_port *port)//
    {
            struct wk2xxx_port *s = container_of(port,struct wk2xxx_port,port);
    #ifdef _DEBUG_WK2XXX
        printk("wk2xxxirq_app()------port:%d----
    ",s->port.iobase);
    #endif
            unsigned int  pass_counter = 0;
            uint8_t sifr,gifr,sier,dat[1];
    #ifdef _DEBUG_WK2XXX1
            
            uint8_t gier,sifr0,sifr1,sifr2,sifr3,sier1,sier0,sier2,sier3;
     #endif                
            wk2xxx_read_reg(s->spi_wk,WK2XXX_GPORT,WK2XXX_GIFR ,dat);
            gifr = dat[0];  
    #ifdef _DEBUG_WK2XXX1 
    //        wk2xxx_read_reg(s->spi_wk,WK2XXX_GPORT,WK2XXX_GIER ,dat);
    //        gier = dat[0]; 
    //        wk2xxx_write_reg(s->spi_wk,1,WK2XXX_SPAGE,WK2XXX_PAGE0);//set register in page0
    //        wk2xxx_write_reg(s->spi_wk,2,WK2XXX_SPAGE,WK2XXX_PAGE0);//set register in page0
    //        wk2xxx_write_reg(s->spi_wk,3,WK2XXX_SPAGE,WK2XXX_PAGE0);//set register in page0
    //        wk2xxx_write_reg(s->spi_wk,4,WK2XXX_SPAGE,WK2XXX_PAGE0);//set register in page0
    //                
    //        wk2xxx_read_reg(s->spi_wk,1,WK2XXX_SIFR,&sifr0);
    //        wk2xxx_read_reg(s->spi_wk,2,WK2XXX_SIFR,&sifr1);
    //        wk2xxx_read_reg(s->spi_wk,3,WK2XXX_SIFR,&sifr2);
    //        wk2xxx_read_reg(s->spi_wk,4,WK2XXX_SIFR,&sifr3);
    //                
    //        wk2xxx_read_reg(s->spi_wk,1,WK2XXX_SIER,&sier0);
    //        wk2xxx_read_reg(s->spi_wk,2,WK2XXX_SIER,&sier1);
    //        wk2xxx_read_reg(s->spi_wk,3,WK2XXX_SIER,&sier2);
    //        wk2xxx_read_reg(s->spi_wk,4,WK2XXX_SIER,&sier3);
    #endif      
            
                    
    #ifdef _DEBUG_WK2XXX1
        printk("irq_app....gifr:%x  gier:%x  sier1:%x  sier2:%x sier3:%x sier4:%x   sifr1:%x sifr2:%x sifr3:%x sifr4:%x 
    ",gifr,gier,sier0,sier1,sier2,sier3,sifr0,sifr1,sifr2,sifr3);
    #endif
            switch(s->port.iobase)
                {
                    case 1 :
                        if(!(gifr & WK2XXX_UT1INT))
                        {
                            return;
                        }
                        break;
                    case 2 :
                        if(!(gifr & WK2XXX_UT2INT))
                        {            
                            return;
                        }                                     
                        break;
                    case 3 :
                        if(!(gifr & WK2XXX_UT3INT))
                        {            
                            return;
                        }
                        break;
                    case 4 :
                        if(!(gifr & WK2XXX_UT4INT))
                        {               
                            return;
                        }
                        break;
                    default:
                        break;
                                    
                }
            
            wk2xxx_read_reg(s->spi_wk,s->port.iobase,WK2XXX_SIFR,dat);
            sifr = dat[0];
            wk2xxx_read_reg(s->spi_wk,s->port.iobase,WK2XXX_SIER,dat);
            sier = dat[0];
    #ifdef _DEBUG_WK2XXX1
            printk("irq_app..........sifr:%x sier:%x 
    ",sifr,sier);
    #endif
                    do {
                           if ((sifr&WK2XXX_RFTRIG_INT)||(sifr&WK2XXX_RXOVT_INT))
                            {
                                   wk2xxx_rx_chars(&s->port);
                            }
            
                            if ((sifr & WK2XXX_TFTRIG_INT)&&(sier & WK2XXX_TFTRIG_IEN ))
                            {
                                   wk2xxx_tx_chars(&s->port);
                                   return;
                            }
                            if (pass_counter++ > WK2XXX_ISR_PASS_LIMIT)
                                    break;
                            wk2xxx_read_reg(s->spi_wk,s->port.iobase,WK2XXX_SIFR,dat);
                            sifr = dat[0];                
                            wk2xxx_read_reg(s->spi_wk,s->port.iobase,WK2XXX_SIER,dat);
                            sier = dat[0];
    #ifdef _DEBUG_WK2XXX1
        printk("irq_app...........rx............tx  sifr:%x sier:%x port:%x
    ",sifr,sier,s->port.iobase);
    #endif
                   } while ((sifr&WK2XXX_RXOVT_INT)||(sifr & WK2XXX_RFTRIG_INT)||((sifr & WK2XXX_TFTRIG_INT)&&(sier & WK2XXX_TFTRIG_IEN)));
    #ifdef _DEBUG_WK2XXX
        printk("wk2xxxirq_app()---------exit---
    ");
    #endif
            
    }
    
    
    /*
     *   Return TIOCSER_TEMT when transmitter is not busy.
     */
    
    static u_int wk2xxx_tx_empty(struct uart_port *port)// or query the tx fifo is not empty?
    {
        uint8_t rx;
    //       mutex_lock(&wk2xxxs_lock);
        struct wk2xxx_port *s = container_of(port,struct wk2xxx_port,port);
    #ifdef _DEBUG_WK2XXX
            printk("wk2xxx_tx_empty()---------in---
    ");
    #endif
    
    mutex_lock(&wk2xxxs_lock);
    
    if(!(s->tx_empty_flag || s->tx_empty_fail))
    {
        wk2xxx_read_reg(s->spi_wk,s->port.iobase,WK2XXX_FSR,&rx);
        while((rx & WK2XXX_TDAT)|(rx&WK2XXX_TBUSY))
           {
              wk2xxx_read_reg(s->spi_wk,s->port.iobase,WK2XXX_FSR,&rx);
           }
        s->tx_empty = ((rx & WK2XXX_TDAT)|(rx&WK2XXX_TBUSY))<=0;
                     
        if(s->tx_empty)
        {
        s->tx_empty_flag =0;
        s->tx_empty_fail=0;
        }
        else
        {
        s->tx_empty_fail=0;
        s->tx_empty_flag =0;
        }
    }
    mutex_unlock(&wk2xxxs_lock);
    
      #ifdef _DEBUG_WK2XXX5 
                  printk("s->tx_empty_fail----FSR:%d--s->tx_empty:%d--
    ",rx,s->tx_empty);
       #endif
          
    #ifdef _DEBUG_WK2XXX
            printk("wk2xxx_tx_empty----------exit---
    ");
    #endif
        return s->tx_empty;
    
    
    }
    
    static void wk2xxx_set_mctrl(struct uart_port *port, u_int mctrl)//nothing
    {
    #ifdef _DEBUG_WK2XXX
        printk("-wk2xxx_set_mctrl---------exit---
    ");
    #endif
    }
    static u_int wk2xxx_get_mctrl(struct uart_port *port)// since no modem control line
    {       
    #ifdef _DEBUG_WK2XXX
        printk("-wk2xxx_get_mctrl---------exit---
    ");
    #endif
    
            return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
    }
    
    
    /*
     *  interrupts disabled on entry
     */
    
    static void wk2xxx_stop_tx(struct uart_port *port)//
    {
    
    #ifdef _DEBUG_WK2XXX
        printk("-wk2xxx_stop_tx------in---
    ");
    #endif
        uint8_t dat[1],sier,sifr;
        struct wk2xxx_port *s = container_of(port,struct wk2xxx_port,port);
      
        mutex_lock(&wk2xxxs_lock);
    
        if(!(s->stop_tx_flag||s->stop_tx_fail))
          {
            wk2xxx_read_reg(s->spi_wk,s->port.iobase,WK2XXX_SIER,dat);
            sier=dat[0];
            s->stop_tx_fail=(sier&WK2XXX_TFTRIG_IEN)>0;
            if(s->stop_tx_fail)
              {
      
                      wk2xxx_read_reg(s->spi_wk,s->port.iobase,WK2XXX_SIER,dat);
                      sier=dat[0];
                      sier&=~WK2XXX_TFTRIG_IEN;
                      wk2xxx_write_reg(s->spi_wk,s->port.iobase,WK2XXX_SIER,sier);
                      wk2xxx_read_reg(s->spi_wk,s->port.iobase,WK2XXX_SIFR,dat);
                      sifr=dat[0];
                      sifr&= ~WK2XXX_TFTRIG_INT;
                      wk2xxx_write_reg(s->spi_wk,s->port.iobase,WK2XXX_SIFR,sifr);
                      s->stop_tx_fail =0;
                      s->stop_tx_flag=0;
      
              }
        else
        {
              s->stop_tx_fail =0;
              s->stop_tx_flag=0;
    
    
        }
            
                     
                     
               }
    mutex_unlock(&wk2xxxs_lock); 
    #ifdef _DEBUG_WK2XXX4
        printk("-wk2xxx_stop_tx------exit---
    ");
    #endif
            
    }
    
    /*
     *  * interrupts may not be disabled on entry
    */
    static void wk2xxx_start_tx(struct uart_port *port)
    {
    #ifdef _DEBUG_WK2XXX
        printk("-wk2xxx_start_tx------in---
    ");
    #endif
    
        struct wk2xxx_port *s = container_of(port,struct wk2xxx_port,port);    
        #ifdef _DEBUG_WK2XXX
        printk("--------s->start_tx_flag:%d, s->start_tx_fail:%d
    ", s->start_tx_flag, s->start_tx_fail);
        #endif
        if(!(s->start_tx_flag||s->start_tx_fail))
        {
            s->start_tx_flag = 1;
            if(wk2xxx_dowork(s))
            {
                ;
            }
            else
            {
                s->start_tx_fail = 1;
                s->start_tx_flag = 0;
            }
        }    
    
    #ifdef _DEBUG_WK2XXX
            printk("-wk2xxx_start_tx------exit---
    ");
    #endif
    }
    
    /*
     *  * Interrupts enabled
    */
    
    static void wk2xxx_stop_rx(struct uart_port *port)
    {
    //mutex_lock(&wk2xxxs_lock);
    #ifdef _DEBUG_WK2XXX
        printk("-wk2xxx_stop_rx------in---
    ");
    #endif
     
            struct wk2xxx_port *s = container_of(port,struct wk2xxx_port,port);
    
           if(!(s->stop_rx_flag ||s->stop_rx_fail ))
            {
                s->stop_rx_flag = 1;
                if(wk2xxx_dowork(s))
                {
                    
                    ;
                }
                else
                {
                    s->stop_rx_flag = 0;
                    s->stop_rx_fail = 1;
                }
            }
    
    
    #ifdef _DEBUG_WK2XXX
        printk("-wk2xxx_stop_rx------exit---
    ");
    #endif
    
    
    }
    
    
    /*
     *  * No modem control lines
     *   */
    static void wk2xxx_enable_ms(struct uart_port *port)    //nothing
    {
    #ifdef _DEBUG_WK2XXX
        printk("-wk2xxx_enable_ms------exit---
    ");
    #endif
    
    }
    /*
     *  * Interrupts always disabled.
    */   
    static void wk2xxx_break_ctl(struct uart_port *port, int break_state)
    {
    #ifdef _DEBUG_WK2XXX
        printk("-wk2xxx_break_ctl------exit---
    ");
    #endif
    
        //break operation, but there  seems no such operation in vk32  
    }
    
    
    static int wk2xxx_startup(struct uart_port *port)//i
    {
        #ifdef _DEBUG_WK2XXX
        printk("-wk2xxx_startup------in---
    ");
        #endif
    
        uint8_t gena,grst,gier,sier,scr,dat[1];
        struct wk2xxx_port *s = container_of(port, struct wk2xxx_port, port);
        char b[12];
        if (s->suspending)
            return 0;
    
        s->force_end_work = 0;
        sprintf(b, "wk2xxx-%d", (uint8_t)s->port.iobase);
        //s->workqueue = create_singlethread_workqueue(b);
        s->workqueue = create_workqueue(b);
        
        if (!s->workqueue) 
        {
            dev_warn(&s->spi_wk->dev, "cannot create workqueue
    ");
            return -EBUSY;
        }
        
        INIT_WORK(&s->work, wk2xxx_work);
        
        if (s->wk2xxx_hw_suspend)
          s->wk2xxx_hw_suspend(0);
              
        wk2xxx_read_reg(s->spi_wk,WK2XXX_GPORT,WK2XXX_GENA,dat);
        gena=dat[0];
        #ifdef _DEBUG_WK2XXX
        printk("gena:0x%x
    ", gena);
        #endif
        switch (s->port.iobase)
        {
            case 1:
              gena|=WK2XXX_UT1EN;
              wk2xxx_write_reg(s->spi_wk,WK2XXX_GPORT,WK2XXX_GENA,gena);
              break;
            case 2:
              gena|=WK2XXX_UT2EN;
              wk2xxx_write_reg(s->spi_wk,WK2XXX_GPORT,WK2XXX_GENA,gena);
              break;
            case 3:
             gena|=WK2XXX_UT3EN;
             wk2xxx_write_reg(s->spi_wk,WK2XXX_GPORT,WK2XXX_GENA,gena);
              break;
            case 4:
              gena|=WK2XXX_UT4EN;
              wk2xxx_write_reg(s->spi_wk,WK2XXX_GPORT,WK2XXX_GENA,gena);
              break;
            default:
              printk(":con_wk2xxx_subport bad iobase %d
    ", (uint8_t)s->port.iobase);
              break;
        }
        udelay(200);
        wk2xxx_read_reg(s->spi_wk,WK2XXX_GPORT,WK2XXX_GENA,dat);
        gena=dat[0];
        #ifdef _DEBUG_WK2XXX
        printk("gena:0x%x
    ", gena);
        #endif
    
        
        wk2xxx_read_reg(s->spi_wk,WK2XXX_GPORT,WK2XXX_GRST,dat);
        grst=dat[0];
        switch (s->port.iobase)
        {
            case 1:
                    grst|=WK2XXX_UT1RST;
                    wk2xxx_write_reg(s->spi_wk,WK2XXX_GPORT,WK2XXX_GRST,grst);
                   break;
            case 2:
                    grst|=WK2XXX_UT2RST;
                    wk2xxx_write_reg(s->spi_wk,WK2XXX_GPORT,WK2XXX_GRST,grst);
                    break;
            case 3:
                   grst|=WK2XXX_UT3RST;
                   wk2xxx_write_reg(s->spi_wk,WK2XXX_GPORT,WK2XXX_GRST,grst);
                    break;
            case 4:
                    grst|=WK2XXX_UT4RST;
                    wk2xxx_write_reg(s->spi_wk,WK2XXX_GPORT,WK2XXX_GRST,grst);
                    break;
            default:
                printk(":con_wk2xxx_subport bad iobase %d
    ", (uint8_t)s->port.iobase);
                break;
        }
        wk2xxx_read_reg(s->spi_wk,WK2XXX_GPORT,WK2XXX_GRST,dat);
        grst=dat[0];
        #ifdef _DEBUG_WK2XXX
        printk("grst:0x%x
    ", grst);
        #endif
    
        
        wk2xxx_read_reg(s->spi_wk,s->port.iobase,WK2XXX_SIER,dat);
    
        sier = dat[0];
        sier &= ~WK2XXX_TFTRIG_IEN;
        sier |= WK2XXX_RFTRIG_IEN;
        sier |= WK2XXX_RXOUT_IEN;
        wk2xxx_write_reg(s->spi_wk,s->port.iobase,WK2XXX_SIER,sier);
        wk2xxx_read_reg(s->spi_wk, s->port.iobase, WK2XXX_SIER, dat);
        #ifdef _DEBUG_WK2XXX
        printk("sier:0x%x
    ", dat[0]);
        #endif
    
        wk2xxx_read_reg(s->spi_wk,s->port.iobase,WK2XXX_SCR,dat);
        scr = dat[0] | WK2XXX_TXEN|WK2XXX_RXEN;
        wk2xxx_write_reg(s->spi_wk,s->port.iobase,WK2XXX_SCR,scr);
        wk2xxx_read_reg(s->spi_wk,s->port.iobase,WK2XXX_SCR,dat);
        scr = dat[0];
    //    printk("scr:0x%x
    ", scr);
    
        //initiate the fifos
        wk2xxx_write_reg(s->spi_wk,s->port.iobase,WK2XXX_FCR,0xff);//initiate the fifos
        wk2xxx_read_reg(s->spi_wk,s->port.iobase,WK2XXX_FCR,dat);
    //    printk("fcr:0x%x
    ", dat[0]);
        wk2xxx_write_reg(s->spi_wk,s->port.iobase,WK2XXX_FCR,0xfc);
        wk2xxx_read_reg(s->spi_wk,s->port.iobase,WK2XXX_FCR,dat);
    //    printk("fcr:0x%x
    ", dat[0]);
        //set rx/tx interrupt 
        wk2xxx_write_reg(s->spi_wk,s->port.iobase,WK2XXX_SPAGE,1);  
        wk2xxx_read_reg(s->spi_wk,s->port.iobase,WK2XXX_SPAGE,dat);
    //    printk("spage:0x%x
    ", dat[0]);
    
        wk2xxx_write_reg(s->spi_wk,s->port.iobase,WK2XXX_RFTL,0X80);  
        wk2xxx_read_reg(s->spi_wk,s->port.iobase,WK2XXX_RFTL,dat);
    //    printk("rftl:0x%x
    ", dat[0]);
        wk2xxx_write_reg(s->spi_wk,s->port.iobase,WK2XXX_TFTL,0X20);  
        wk2xxx_read_reg(s->spi_wk,s->port.iobase,WK2XXX_TFTL,dat);
    //    printk("tftl:0x%x
    ", dat[0]);
        wk2xxx_write_reg(s->spi_wk,s->port.iobase,WK2XXX_SPAGE,0);  
        wk2xxx_read_reg(s->spi_wk,s->port.iobase,WK2XXX_SPAGE,dat);
    //    printk("spage:0x%x
    ", dat[0]);
    
        //enable the sub port interrupt
        wk2xxx_read_reg(s->spi_wk,WK2XXX_GPORT,WK2XXX_GIER,dat);
        gier = dat[0];
    //    printk("gier that read:0x%x
    ", gier);
        switch (s->port.iobase){
            case 1:
                    gier|=WK2XXX_UT1IE;
                    wk2xxx_write_reg(s->spi_wk,WK2XXX_GPORT,WK2XXX_GIER,gier);
                    break;
            case 2:
                    gier|=WK2XXX_UT2IE;
                    wk2xxx_write_reg(s->spi_wk,WK2XXX_GPORT,WK2XXX_GIER,gier);
                    break;
            case 3:
                    gier|=WK2XXX_UT3IE;
                    wk2xxx_write_reg(s->spi_wk,WK2XXX_GPORT,WK2XXX_GIER,gier);
                    break;
            case 4:
                    gier|=WK2XXX_UT4IE;
                    wk2xxx_write_reg(s->spi_wk,WK2XXX_GPORT,WK2XXX_GIER,gier);
                    break;
            default:
                    printk(": bad iobase %d
    ", (uint8_t)s->port.iobase);
                    break;
        }
        wk2xxx_read_reg(s->spi_wk, WK2XXX_GPORT, WK2XXX_GIER, dat);
        gier = dat[0];
    //    printk("gier that read:0x%x
    ", gier);
    
        if (s->wk2xxx_hw_suspend)
            s->wk2xxx_hw_suspend(0);
        msleep(50);
    
        uart_circ_clear(&s->port.state->xmit);
        wk2xxx_enable_ms(&s->port);
    
        // request irq 
        if(request_irq(s->port.irq, wk2xxx_irq, IRQF_SHARED|IRQF_TRIGGER_LOW, "wk2xxxspi", s) < 0)
        {
            dev_warn(&s->spi_wk->dev, "cannot allocate irq %d
    ", s->irq);
            s->port.irq = 0;
            destroy_workqueue(s->workqueue);
            s->workqueue = NULL;
            return -EBUSY;
        }
    
        udelay(100);
        udelay(100);
    #ifdef _DEBUG_WK2XXX
            printk("-wk2xxx_startup------exit---
    ");
    #endif
    
           return 0;
    }
    //* Power down all displays on reboot, poweroff or halt *
    
    static void wk2xxx_shutdown(struct uart_port *port)//
    {
    #ifdef _DEBUG_WK2XXX
        printk("-wk2xxx_shutdown------in---
    ");
    #endif
    
        uint8_t gena,dat[1];
            struct wk2xxx_port *s = container_of(port,struct wk2xxx_port,port);
        if (s->suspending)
           return;
           s->force_end_work = 1;
        if (s->workqueue) 
        {
            flush_workqueue(s->workqueue);
            destroy_workqueue(s->workqueue);
            s->workqueue = NULL;
        }
        
        if (s->port.irq)
        {
               // disable_irq_nosync(s->port.irq);      
              free_irq(s->port.irq,s);
        }
         
         wk2xxx_read_reg(s->spi_wk,WK2XXX_GPORT,WK2XXX_GENA,dat);
          gena=dat[0];
              switch (s->port.iobase)
        {
              case 1:
                      gena&=~WK2XXX_UT1EN;
                      wk2xxx_write_reg(s->spi_wk,WK2XXX_GPORT,WK2XXX_GENA,gena);
                      break;
              case 2:
                      gena&=~WK2XXX_UT2EN;
                      wk2xxx_write_reg(s->spi_wk,WK2XXX_GPORT,WK2XXX_GENA,gena);
                      break;
              case 3:
                     gena&=~WK2XXX_UT3EN;
                     wk2xxx_write_reg(s->spi_wk,WK2XXX_GPORT,WK2XXX_GENA,gena);
                      break;
              case 4:
                      gena&=~WK2XXX_UT4EN;
                      wk2xxx_write_reg(s->spi_wk,WK2XXX_GPORT,WK2XXX_GENA,gena);
                      break;
              default:
                     printk(":con_wk2xxx_subport bad iobase %d
    ", (uint8_t)s->port.iobase);
             break;
              } 
        
        
        
    #ifdef _DEBUG_WK2XXX5
            wk2xxx_read_reg(s->spi_wk,WK2XXX_GPORT,WK2XXX_GENA,dat);
          gena=dat[0];
            printk("-wk2xxx_shutdown-----port:%d--gena:%x-
    ",(uint8_t)s->port.iobase,gena);
    #endif  
    #ifdef _DEBUG_WK2XXX
            printk("-wk2xxx_shutdown-----exit---
    ");
    #endif
    
        return;
    }
    
    static void conf_wk2xxx_subport(struct uart_port *port)//i
    {
    #ifdef _DEBUG_WK2XXX
                printk("-conf_wk2xxx_subport------in---
    ");
    #endif
    
            struct wk2xxx_port *s = container_of(port,struct wk2xxx_port,port);
        uint8_t old_sier,lcr,scr,scr_ss,dat[1],baud0_ss,baud1_ss,pres_ss;
    
     
        lcr = s->new_lcr;
        scr_ss = s->new_scr;
        baud0_ss=s->new_baud0;
        baud1_ss=s->new_baud1;
        pres_ss=s->new_pres;
            wk2xxx_read_reg(s->spi_wk,s->port.iobase,WK2XXX_SIER ,dat);
            old_sier = dat[0];
            wk2xxx_write_reg(s->spi_wk,s->port.iobase,WK2XXX_SIER ,old_sier&(~(WK2XXX_TFTRIG_IEN | WK2XXX_RFTRIG_IEN | WK2XXX_RXOUT_IEN)));
        //local_irq_restore(flags);
        do{
            wk2xxx_read_reg(s->spi_wk,s->port.iobase,WK2XXX_FSR,dat);
            //ssr = dat[0];
           } while (dat[0] & WK2XXX_TBUSY);
            // then, disable everything 
            wk2xxx_read_reg(s->spi_wk,s->port.iobase,WK2XXX_SCR,dat);
            scr = dat[0];
    
            wk2xxx_write_reg(s->spi_wk,s->port.iobase,WK2XXX_SCR ,scr&(~(WK2XXX_RXEN|WK2XXX_TXEN)));
            // set the parity, stop bits and data size //
            wk2xxx_write_reg(s->spi_wk,s->port.iobase,WK2XXX_LCR ,lcr);
            // set the baud rate //
            wk2xxx_write_reg(s->spi_wk,s->port.iobase,WK2XXX_SIER ,old_sier);
            // set the baud rate //
            wk2xxx_write_reg(s->spi_wk,s->port.iobase,WK2XXX_SPAGE ,1);
            wk2xxx_write_reg(s->spi_wk,s->port.iobase,WK2XXX_BAUD0 ,baud0_ss);
            wk2xxx_write_reg(s->spi_wk,s->port.iobase,WK2XXX_BAUD1 ,baud1_ss);
            wk2xxx_write_reg(s->spi_wk,s->port.iobase,WK2XXX_PRES ,pres_ss);
    #ifdef _DEBUG_WK2XXX2
                wk2xxx_read_reg(s->spi_wk,s->port.iobase,WK2XXX_BAUD0,dat);
                printk(":WK2XXX_BAUD0=0x%X
    ", dat[0]);
                 wk2xxx_read_reg(s->spi_wk,s->port.iobase,WK2XXX_BAUD1,dat);
                printk(":WK2XXX_BAUD1=0x%X
    ", dat[0]);
                 wk2xxx_read_reg(s->spi_wk,s->port.iobase,WK2XXX_PRES,dat);
                printk(":WK2XXX_PRES=0x%X
    ", dat[0]);
    #endif
            wk2xxx_write_reg(s->spi_wk,s->port.iobase,WK2XXX_SPAGE ,0);
            wk2xxx_write_reg(s->spi_wk,s->port.iobase,WK2XXX_SCR ,scr|(WK2XXX_RXEN|WK2XXX_TXEN)  );
    
    
    #ifdef _DEBUG_WK2XXX
            printk("-conf_wk2xxx_subport------exit---
    ");
    #endif
    
    }
    
    
    // change speed
    static void wk2xxx_termios( struct uart_port *port, struct ktermios *termios,
                struct ktermios *old)
    {
    #ifdef _DEBUG_WK2XXX
           printk("-wk32xx_termios------in---
    ");
    #endif
    
        struct wk2xxx_port *s = container_of(port,struct wk2xxx_port,port);
        int baud = 0;
        uint8_t lcr,baud1,baud0,pres;
        unsigned short cflag;
        unsigned short lflag;
    
        cflag = termios->c_cflag;
        lflag = termios->c_lflag;
    #ifdef _DEBUG_WK2XXX
        printk("cflag := 0x%X  lflag : = 0x%X
    ",cflag,lflag);
    #endif
        baud1=0;
        baud0=0;
        pres=0;
        baud = tty_termios_baud_rate(termios);
        #ifdef _DEBUG_WK2XXX
        printk("baud:%d
    ", baud);
        #endif
        switch (baud) {
            case 600:
            baud1=0x4;
            baud0=0x7f;
            pres=0;
            break;
            case 1200:
            baud1=0x2;
            baud0=0x3F;
            pres=0;
            break;
            case 2400:
            baud1=0x1;
            baud0=0x1f;
            pres=0;
            break;
            case 4800:
            baud1=0x00;
            baud0=0x8f;
            pres=0;
            break;
            case 9600:
            baud1=0x00;
            baud0=0x47;
            pres=0;
            break;
            case 19200:
            baud1=0x00;
            baud0=0x23;
            pres=0;
            break;
            case 38400:
            baud1=0x00;
            baud0=0x11;
            pres=0;
            break;
            case 76800:
            baud1=0x00;
            baud0=0x08;
            pres=0;
            break; 
           
            case 1800:
            baud1=0x01;
            baud0=0x7f;
            pres=0;
            break;
            case 3600:
            baud1=0x00;
            baud0=0xbf;
            pres=0;
            break;
            case 7200:
            baud1=0x00;
            baud0=0x5f;
            pres=0;
            break;
            case 14400:
            baud1=0x00;
            baud0=0x2f;
            pres=0;
            break;
            case 28800:
            baud1=0x00;
            baud0=0x17;
            pres=0;
            break;
            case 57600:
            baud1=0x00;
            baud0=0x0b;
            pres=0;
            break;
            case 115200:
            baud1=0x00;
            baud0=0x05;
            pres=0;
            break;
            case 230400:
            baud1=0x00;
            baud0=0x02;
            pres=0;
            break;
            default:
            baud1=0x00;
            baud0=0x00;
            pres=0;
        }
        tty_termios_encode_baud_rate(termios, baud, baud);
    
        /* we are sending char from a workqueue so enable */
    
    
    #ifdef _DEBUG_WK2XXX
    
                printk("wk2xxx_termios()----port:%d--lcr:0x%x- cflag:0x%x-CSTOPB:0x%x,PARENB:0x%x,PARODD:0x%x--
    ",s->port.iobase,lcr,cflag,CSTOPB,PARENB,PARODD);
    #endif
    
            lcr =0;
            if (cflag & CSTOPB)
                    lcr|=WK2XXX_STPL;//two  stop_bits
            else
                    lcr&=~WK2XXX_STPL;//one  stop_bits
    
            if (cflag & PARENB) {
                    lcr|=WK2XXX_PAEN;//enbale spa
                    if (!(cflag & PARODD)){
    
                            lcr |= WK2XXX_PAM1;
                            lcr &= ~WK2XXX_PAM0;
                    }
                    else{
                            lcr |= WK2XXX_PAM0;//PAM0=1
                            lcr &= ~WK2XXX_PAM1;//PAM1=0
    
                    }
            }
            else{
                    lcr&=~WK2XXX_PAEN;
            }
    
    #ifdef _DEBUG_WK2XXX
    
                    printk("wk2xxx_termios()----port:%d--lcr:0x%x- cflag:0x%x-CSTOPB:0x%x,PARENB:0x%x,PARODD:0x%x--
    ",s->port.iobase,lcr,cflag,CSTOPB,PARENB,PARODD);
    #endif
    
    
        s->new_baud1=baud1;
        s->new_baud0=baud0;
        s->new_pres=pres;
    
    
        s->new_lcr = lcr;
    
    
    #if 1 // simon change
        conf_wk2xxx_subport(&s->port);
      
    #else
        if(!(s->conf_flag|| s->conf_fail))
        {
        if(wk2xxx_dowork(s))
        {
            s->conf_flag =1;
        }
        else
        {
            s->conf_fail =1;
        }
    
        }
    #endif
    #ifdef _DEBUG_WK2XXX
          printk("-vk32xx_termios------exit---
    ");
    #endif
    
    }
    
    
    static const char *wk2xxx_type(struct uart_port *port)
    {
    
    
    #ifdef _DEBUG_WK2XXX
            printk("wk2xxx_type-------------out-------- 
    ");
    #endif
            return port->type == PORT_WK2XXX ? "wk2xxx" : NULL;//this is defined in serial_core.h
    }
    
    /*
    * Release the memory region(s) being used by 'port'.
    */
    static void wk2xxx_release_port(struct uart_port *port)
    {
    //        printk("wk2xxx_release_port
    ");
    
    }
    
    /*
    * Request the memory region(s) being used by 'port'.
    */
    static int wk2xxx_request_port(struct uart_port *port)//no such memory region needed for vk32
    {
    //        printk("wk2xxx_request_port
    ");
    
            return 0;
    }
    
    /*
    * Configure/autoconfigure the port*/
    static void wk2xxx_config_port(struct uart_port *port, int flags)
    {
            struct wk2xxx_port *s = container_of(port,struct wk2xxx_port,port);
    
    #ifdef _DEBUG_WK2XXX
            printk("wk2xxx_config_port 
    ");
    #endif
    
            if (flags & UART_CONFIG_TYPE && wk2xxx_request_port(port) == 0)
                   s->port.type = PORT_WK2XXX;
    }
    
    /*
    * Verify the new serial_struct (for TIOCSSERIAL).
    * The only change we allow are to the flags and type, and
    * even then only between PORT_vk32xx and PORT_UNKNOWN
    */
    static int wk2xxx_verify_port(struct uart_port *port, struct serial_struct *ser)
    {
    
    #ifdef _DEBUG_WK2XXX
            printk("wk2xxx_verify_port 
    ");
    #endif
            int ret = 0;
            if (ser->type != PORT_UNKNOWN && ser->type != PORT_WK2XXX)
                    ret = -EINVAL;
            if (port->irq != ser->irq)
                    ret = -EINVAL;
            if (ser->io_type != SERIAL_IO_PORT)
                    ret = -EINVAL;
            //if (port->uartclk / 16 != ser->baud_base)//?a??2?è·?¨
            //      ret = -EINVAL;
            if (port->iobase != ser->port)
                    ret = -EINVAL;
            if (ser->hub6 != 0)
                    ret = -EINVAL;
            return ret;
    }
    
    
    
    
    static struct uart_ops wk2xxx_pops = {
        tx_empty:       wk2xxx_tx_empty,
        set_mctrl:      wk2xxx_set_mctrl,
        get_mctrl:      wk2xxx_get_mctrl,
        stop_tx:        wk2xxx_stop_tx,
        start_tx:       wk2xxx_start_tx,
        stop_rx:        wk2xxx_stop_rx,
        enable_ms:      wk2xxx_enable_ms,
        break_ctl:      wk2xxx_break_ctl,
        startup:        wk2xxx_startup,
        shutdown:       wk2xxx_shutdown,
        set_termios:    wk2xxx_termios,
        type:           wk2xxx_type,
        release_port:   wk2xxx_release_port,
        request_port:   wk2xxx_request_port,
        config_port:    wk2xxx_config_port,
        verify_port:    wk2xxx_verify_port,
    };
    static struct uart_driver wk2xxx_uart_driver = {
        owner:                  THIS_MODULE,
        major:               SERIAL_WK2XXX_MAJOR,
        driver_name:            "ttySWK",
        dev_name:               "ttysWK",
        minor:                  MINOR_START,
        nr:                     NR_PORTS,
        cons:                   NULL//WK2Xxx_CONSOLE,
    };
    
    static int uart_driver_registered;
    static struct spi_driver wk2xxx_driver;
    
    static int wk2xxx_probe(struct spi_device *spi)
    {
    #ifdef _DEBUG_WK2XXX
        printk("-wk2xxx_probe()------in---
    ");
    #endif
    
        uint8_t i;
        int status;
        uint8_t dat[1];    
        wk2xxx_read_reg(spi,WK2XXX_GPORT,WK2XXX_GENA,dat);
        if((dat[0]&0xf0)!=0x30)
        { 
          printk("wk2xxx_probe()  GENA = 0x%X
    ",dat[0]);
          printk(KERN_ERR "spi driver  error!!!!
    ");
          return 1;
        }
        
        mutex_lock(&wk2xxxs_lock);
        if(!uart_driver_registered)
        {
            uart_driver_registered = 1;
            status=uart_register_driver(&wk2xxx_uart_driver);
            if (status)
            {
                printk(KERN_ERR "Couldn't register wk2xxx uart driver
    ");
                mutex_unlock(&wk2xxxs_lock);
                return status;
            }
            wk2xxx_uart_driver.tty_driver->init_termios.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
            wk2xxx_uart_driver.tty_driver->init_termios.c_cc[VTIME] = 2;
            wk2xxx_uart_driver.tty_driver->init_termios.c_cc[VMIN] = 32;
        }
    
        for(i =0;i<NR_PORTS;i++)
        {
            struct wk2xxx_port *s = &wk2xxxs[i];//container_of(port,struct wk2xxx_port,port);   
            s->tx_done       = 0;
            s->spi_wk        = spi;
            s->port.line     = i;
            s->port.ops      = &wk2xxx_pops;
            s->port.uartclk  = WK_CRASTAL_CLK;
            s->port.fifosize = 64;
            s->port.iobase   = i+1;
            s->port.mapbase  = 0xff130000;
            s->port.irq      = gpio_to_irq(226);//IRQ_WK2XXX;
            s->port.iotype   = UPIO_MEM;
            s->port.flags    = ASYNC_BOOT_AUTOCONF;
            s->port.irqflags = IRQF_DISABLED;
            //s->minor       = i;
            status = uart_add_one_port(&wk2xxx_uart_driver, &s->port);
            if(status<0)
            {
               //dev_warn(&spi->dev,"uart_add_one_port failed for line i:= %d with error %d
    ",i,status);
               printk("uart_add_one_port failed for line i:= %d with error %d
    ",i,status);
            }
        }
        #ifdef _DEBUG_WK2XXX
        printk("uart_add_one_port ret:%d
    ",status);
        #endif
    
        mutex_unlock(&wk2xxxs_lock);
        return 0;
    }
    
    static int wk2xxx_remove(struct spi_device *spi)
    {
        int i;
    #ifdef _DEBUG_WK2XXX
        printk("-wk2xxx_remove()------in---
    ");
    #endif
    
        mutex_lock(&wk2xxxs_lock);
        for(i =0;i<NR_PORTS;i++)
           {
            struct wk2xxx_port *s = &wk2xxxs[i];
            uart_remove_one_port(&wk2xxx_uart_driver, &s->port);
           }
        printk("removing wk2xxx driver
    ");
        uart_unregister_driver(&wk2xxx_uart_driver);
        mutex_unlock(&wk2xxxs_lock);
    #ifdef _DEBUG_WK2XXX
        printk("-wk2xxx_remove()------exit---
    ");
    #endif
    
        return 0;
    }
    
    static int wk2xxx_resume(struct spi_device *spi)
    {
        printk("resume wk2xxx");
        return 0;
    }
    
    static const struct of_device_id wk2xxx_spi_dt_match[] = {
        { .compatible = "rockchip,spi_wk2xxx", },
        {},
    };
    
    static struct spi_driver wk2xxx_driver = {
        .driver = {
            .name           = "wk2xxxspi",
            .bus            = &spi_bus_type,
            .owner          = THIS_MODULE,
            .of_match_table = of_match_ptr(wk2xxx_spi_dt_match),
        },
    
        .probe          = wk2xxx_probe,
        .remove         = wk2xxx_remove,
        .resume         = wk2xxx_resume,
    };
    
    static int __init wk2xxx_init(void)
    {
        int retval;
        retval = spi_register_driver(&wk2xxx_driver);
        printk("register spi ret:%d
    ",retval);
    
        return retval;
    }
    
    static void __exit wk2xxx_exit(void)
    {
            spi_unregister_driver(&wk2xxx_driver);
            printk("TEST_REG:quit ");
    }
    
    
    module_init(wk2xxx_init);
    module_exit(wk2xxx_exit);
    
    MODULE_AUTHOR("WKIC Ltd");
    MODULE_DESCRIPTION("wk2xxx generic serial port driver");
    MODULE_LICENSE("GPL");
    rk3288上适配好的wk2xxx_spi.c
    /*
    *    WKIC Ltd.
    *    WK2xxx.c
    *    wk2xxx_GPIO_I2C DEMO Ver
    
    ion :1.0 Data:2014-05-20
    *    By  xuxunwei Tech 
    *
    */
    #ifndef    _SERIAL_WK2XXX_H       //_SERIAL_WK2XXX_H
    #define  _SERIAL_WK2XXX_H
        
    //#include <linux/config.h>
    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/init.h>
    #include <linux/slab.h>
    #include <linux/console.h>
    //#include <linux/serial_core.h>
    
    #include <asm/irq.h>
    //#include <asm/hardware.h>
    
    //EINT15 GPG7
    //wkxxxx  Global rigister address defines
    #define     WK2XXX_GENA     0X00
    #define     WK2XXX_GRST     0X01
    #define        WK2XXX_GMUT     0X02
    #define     WK2XXX_GIER     0X10
    #define     WK2XXX_GIFR     0X11
    #define     WK2XXX_GPDIR    0X21
    #define     WK2XXX_GPDAT    0X31
    #define     WK2XXX_GPORT    1//    /wkxxxx  Global rigister of PORT
    //wkxxxx  slave uarts  rigister address defines
    
    #define     WK2XXX_SPAGE    0X03
    //PAGE0
    #define     WK2XXX_SCR      0X04
    #define     WK2XXX_LCR      0X05
    #define     WK2XXX_FCR      0X06
    #define     WK2XXX_SIER     0X07
    #define     WK2XXX_SIFR     0X08
    #define     WK2XXX_TFCNT    0X09
    #define     WK2XXX_RFCNT    0X0A
    #define     WK2XXX_FSR      0X0B
    #define     WK2XXX_LSR      0X0C
    #define     WK2XXX_FDAT     0X0D
    #define     WK2XXX_FWCR     0X0D
    #define     WK2XXX_RS485    0X0F
    //PAGE1
    #define     WK2XXX_BAUD1    0X04
    #define     WK2XXX_BAUD0    0X05
    #define     WK2XXX_PRES     0X06
    #define     WK2XXX_RFTL     0X07
    #define     WK2XXX_TFTL     0X08
    #define     WK2XXX_FWTH     0X09
    #define     WK2XXX_FWTL     0X0A
    #define     WK2XXX_XON1     0X0B
    #define     WK2XXX_XOFF1    0X0C
    #define     WK2XXX_SADR     0X0D
    #define     WK2XXX_SAEN     0X0D
    #define     WK2XXX_RRSDLY   0X0F
    
    
    //wkxxx register bit defines
    // GENA
    #define     WK2XXX_UT4EN    0x08
    #define     WK2XXX_UT3EN    0x04
    #define     WK2XXX_UT2EN    0x02
    #define     WK2XXX_UT1EN    0x01
    //GRST
    #define     WK2XXX_UT4SLEEP    0x80
    #define     WK2XXX_UT3SLEEP    0x40
    #define     WK2XXX_UT2SLEEP    0x20
    #define     WK2XXX_UT1SLEEP    0x10
    #define     WK2XXX_UT4RST    0x08
    #define     WK2XXX_UT3RST    0x04
    #define     WK2XXX_UT2RST    0x02
    #define     WK2XXX_UT1RST    0x01
    //GIER
    #define     WK2XXX_UT4IE    0x08
    #define     WK2XXX_UT3IE    0x04
    #define     WK2XXX_UT2IE    0x02
    #define     WK2XXX_UT1IE    0x01
    //GIFR
    #define     WK2XXX_UT4INT    0x08
    #define     WK2XXX_UT3INT    0x04
    #define     WK2XXX_UT2INT    0x02
    #define     WK2XXX_UT1INT    0x01
    //SPAGE
    #define     WK2XXX_SPAGE0    0x00
    #define     WK2XXX_SPAGE1   0x01
    //SCR
    #define     WK2XXX_SLEEPEN    0x04
    #define     WK2XXX_TXEN     0x02
    #define     WK2XXX_RXEN     0x01
    //LCR
    #define     WK2XXX_BREAK    0x20
    #define     WK2XXX_IREN     0x10
    #define     WK2XXX_PAEN     0x08
    #define     WK2XXX_PAM1     0x04
    #define     WK2XXX_PAM0     0x02
    #define     WK2XXX_STPL     0x01
    //FCR
    //SIER
    #define     WK2XXX_FERR_IEN      0x80
    #define     WK2XXX_CTS_IEN       0x40
    #define     WK2XXX_RTS_IEN       0x20
    #define     WK2XXX_XOFF_IEN      0x10
    #define     WK2XXX_TFEMPTY_IEN   0x08
    #define     WK2XXX_TFTRIG_IEN    0x04
    #define     WK2XXX_RXOUT_IEN     0x02
    #define     WK2XXX_RFTRIG_IEN    0x01
    //SIFR
    #define     WK2XXX_FERR_INT      0x80
    #define     WK2XXX_CTS_INT       0x40
    #define     WK2XXX_RTS_INT       0x20
    #define     WK2XXX_XOFF_INT      0x10
    #define     WK2XXX_TFEMPTY_INT   0x08
    #define     WK2XXX_TFTRIG_INT    0x04
    #define     WK2XXX_RXOVT_INT     0x02
    #define     WK2XXX_RFTRIG_INT    0x01
    
    
    //TFCNT
    //RFCNT
    //FSR
    #define     WK2XXX_RFOE     0x80
    #define     WK2XXX_RFBI     0x40
    #define     WK2XXX_RFFE     0x20
    #define     WK2XXX_RFPE     0x10
    #define     WK2XXX_RDAT     0x08
    #define     WK2XXX_TDAT     0x04
    #define     WK2XXX_TFULL    0x02
    #define     WK2XXX_TBUSY    0x01
    //LSR
    #define     WK2XXX_OE       0x08
    #define     WK2XXX_BI       0x04
    #define     WK2XXX_FE       0x02
    #define     WK2XXX_PE       0x01
    //FWCR
    //RS485
    
    //
    #define     NR_PORTS     4
    
    //
    #define     SERIAL_WK2XXX_MAJOR            207
    #define     CALLOUT_WK2XXX_MAJOR        208    
    #define     MINOR_START                5
    
    //wk2xxx hardware configuration
    #define     IRQ_WK2XXX        88
    //#define     WK_CS_PIN         GPIO_G11//should be GPB
    #define     WK_CRASTAL_CLK  (3686400*2)
    #define     WK2XXX_CS                (GPIO_MODE_OUT | GPIO_PULLUP_DIS | VK_CS_PIN)
    #define     MAX_WK2XXX               4
    
    #define     WK2XXX_ISR_PASS_LIMIT    50
    
    //#define _DEBUG_WK2XXX
    //#define _DEBUG_WK2XXX1
    
    #define        PORT_WK2XXX             1
    #endif
    rk3288上适配好的wk2xxx.h

  • 相关阅读:
    Ubuntu 14.04 配置iptables防火墙
    转 微软Sysinternals Suite工具13年12月版下载
    转详解Zoosk千万用户实时通信背后的开源技术
    Where is Silverlight now?
    A glance at C# vNext
    silverlight 进行本地串口调用的一种可行的解决方法 之silverlight端代码
    silverlight 进行本地串口调用的一种可行的解决方法
    silverlight 中javascript 代码与托管代码的互调用 以及一些思考
    使用cglib动态创建javabean
    数据库同步核心代码
  • 原文地址:https://www.cnblogs.com/chorm590/p/11840606.html
Copyright © 2020-2023  润新知