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\n", cs0); if (!gpio_is_valid(cs0)) { printk("cs0 pin invalid\n"); return -1; } ret = gpio_request(cs0, "wk2xxxspi"); if (ret != 0) { gpio_free(cs0); printk("request cs0 pin failed\n"); 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------\n",__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------\n",__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---\n"); #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---\n"); printk("--wk2xxx_dowork---exit---\n"); #endif return 1; } else { #ifdef _DEBUG_WK2XXX printk("--queue_work---error---\n"); printk("--wk2xxx_dowork---exit---\n"); #endif return 0; } } static void wk2xxx_work(struct work_struct *w) { #ifdef _DEBUG_WK2XXX printk("--wk2xxx_work---in---\n"); #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\n", 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\n", 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\n", 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--\n", work_start_tx_flag, rx); #endif } // printk("work_stop_rx_flag:%d\n", 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--\n",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--\n",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--\n",rx); #endif } #ifdef _DEBUG_WK2XXX1 printk("work_irq_flag:%d\n", 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\n", 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---\n"); #endif } static void wk2xxx_rx_chars(struct uart_port *port)//vk32xx_port *port) { #ifdef _DEBUG_WK2XXX printk("wk2xxx_rx_chars()---------in---\n"); #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--\n",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----\n",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!!!!!!!!!!!\n"); 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----\n",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\n",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----\n",s->port.iobase,ch); #endif s->port.icount.rx++; //rx_count++; #ifdef _DEBUG_WK2XXX1 printk("wk2xxx_rx_chars()----port:%d error\n",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!!!!!!!!!!!!!!!!!\n",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\n",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\n",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\n",s->port.icount.rx); #endif #ifdef _DEBUG_WK2XXX printk("wk2xxx_rx_chars()---------out---\n"); #endif return; #ifdef SUPPORT_SYSRQ s->port.state->sysrq = 0; #endif goto error_return; #ifdef _DEBUG_WK2XXX printk("--wk2xxx_rx_chars---exit---\n"); #endif } static void wk2xxx_tx_chars(struct uart_port *port)// { #ifdef _DEBUG_WK2XXX printk("--wk2xxx_tx_chars---in---\n"); #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\n",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\n",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\n",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--\n",txbuf[i-1]); #endif }while(--count > 0); #ifdef _DEBUG_WK2XXX5 printk("tx_chars I:0x%x--\n",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---\n"); #endif } static irqreturn_t wk2xxx_irq(int irq, void *dev_id)// { #ifdef _DEBUG_WK2XXX printk("--wk2xxx_irq---in---\n"); #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---\n"); #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----\n",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 \n",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 \n",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\n",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---\n"); #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---\n"); #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--\n",rx,s->tx_empty); #endif #ifdef _DEBUG_WK2XXX printk("wk2xxx_tx_empty----------exit---\n"); #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---\n"); #endif } static u_int wk2xxx_get_mctrl(struct uart_port *port)// since no modem control line { #ifdef _DEBUG_WK2XXX printk("-wk2xxx_get_mctrl---------exit---\n"); #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---\n"); #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---\n"); #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---\n"); #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\n", 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---\n"); #endif } /* * * Interrupts enabled */ static void wk2xxx_stop_rx(struct uart_port *port) { //mutex_lock(&wk2xxxs_lock); #ifdef _DEBUG_WK2XXX printk("-wk2xxx_stop_rx------in---\n"); #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---\n"); #endif } /* * * No modem control lines * */ static void wk2xxx_enable_ms(struct uart_port *port) //nothing { #ifdef _DEBUG_WK2XXX printk("-wk2xxx_enable_ms------exit---\n"); #endif } /* * * Interrupts always disabled. */ static void wk2xxx_break_ctl(struct uart_port *port, int break_state) { #ifdef _DEBUG_WK2XXX printk("-wk2xxx_break_ctl------exit---\n"); #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---\n"); #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\n"); 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\n", 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\n", (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\n", 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\n", (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\n", 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\n", 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\n", 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\n", 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\n", 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\n", 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\n", 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\n", 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\n", 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\n", 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\n", (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\n", 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\n", 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---\n"); #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---\n"); #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\n", (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-\n",(uint8_t)s->port.iobase,gena); #endif #ifdef _DEBUG_WK2XXX printk("-wk2xxx_shutdown-----exit---\n"); #endif return; } static void conf_wk2xxx_subport(struct uart_port *port)//i { #ifdef _DEBUG_WK2XXX printk("-conf_wk2xxx_subport------in---\n"); #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\n", dat[0]); wk2xxx_read_reg(s->spi_wk,s->port.iobase,WK2XXX_BAUD1,dat); printk(":WK2XXX_BAUD1=0x%X\n", dat[0]); wk2xxx_read_reg(s->spi_wk,s->port.iobase,WK2XXX_PRES,dat); printk(":WK2XXX_PRES=0x%X\n", 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---\n"); #endif } // change speed static void wk2xxx_termios( struct uart_port *port, struct ktermios *termios, struct ktermios *old) { #ifdef _DEBUG_WK2XXX printk("-wk32xx_termios------in---\n"); #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\n",cflag,lflag); #endif baud1=0; baud0=0; pres=0; baud = tty_termios_baud_rate(termios); #ifdef _DEBUG_WK2XXX printk("baud:%d\n", 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--\n",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--\n",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---\n"); #endif } static const char *wk2xxx_type(struct uart_port *port) { #ifdef _DEBUG_WK2XXX printk("wk2xxx_type-------------out-------- \n"); #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\n"); } /* * 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\n"); 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 \n"); #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 \n"); #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---\n"); #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\n",dat[0]); printk(KERN_ERR "spi driver error!!!!\n"); 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\n"); 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\n",i,status); printk("uart_add_one_port failed for line i:= %d with error %d\n",i,status); } } #ifdef _DEBUG_WK2XXX printk("uart_add_one_port ret:%d\n",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---\n"); #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\n"); uart_unregister_driver(&wk2xxx_uart_driver); mutex_unlock(&wk2xxxs_lock); #ifdef _DEBUG_WK2XXX printk("-wk2xxx_remove()------exit---\n"); #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\n",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