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");
/* * 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