硬件相关
传输协议
P1,P0:为器件地址,这个由芯片引脚IA1、IA0控制。C1,CO:子串口通道号,00~11分别对应子串口1到子串口4。A3,A2,A1,A0:子串口寄存器地址。
驱动实现
设备树节点
设备树是现行的linux驱动与设备匹配体系,设备树文件在arch\arm64\boot\dts文件夹下,我这里需要匹配的就是i2c设备节点。
i2c_3: i2c@0x9801B900 {
compatible = "socname,soc-i2c";
reg = <0x9801B900 0x400>;
interrupt-parent = <&mux_intc>;
interrupts = <0 23>;
i2c-num = <3>;
status = "okay";
#address-cells = <1>;
#size-cells = <0>;
wkxxxx {
compatible = "company,wkxxxx";
reg = <0x70>;
irq_gpio = <&rtk_misc_gpio 15 0 0>;/*igpio15, intput, default low*/
};
};
compatible属性是设备与驱动匹配的核心,reg是用到的寄存器资源,wkxxxx的reg与i2c总线上的设备地址有关,有些硬件手册给出的设备地址是加上读写位的8位地址,设备树这里的话就需要转换为7位地址,irq_gpio是申请的中断引脚资源。
驱动加载与卸载
static int __init wk2xxx_init(void)
{
int retval;
retval = i2c_add_driver(&wk2xxx_i2c_driver);
printk(KERN_ALERT "rgister i2c_driver return v = :%d\n",retval);
return retval;
}
static void __exit wk2xxx_exit(void)
{
printk(KERN_ERR "%s, wk2xxx_i2c_driver:quit\n", __func__);
return i2c_del_driver(&wk2xxx_i2c_driver);
}
module_init(wk2xxx_init);
module_exit(wk2xxx_exit);
MODULE_AUTHOR("WKMIC Ltd");
MODULE_DESCRIPTION("wk2xxx generic serial port driver");
MODULE_LICENSE("GPL");
驱动匹配
linux为了规范化代码,设计了将驱动和设备分开存放的模式,并提供了匹配机制,系统会对比驱动和设备的compatible属性进行匹配,匹配成功则会调用probe函数根据设备树存储的硬件信息进行驱动初始化。
static const struct i2c_device_id wk2xxx_i2c_id_table[]={
{"wkxxxx_i2c",0},
{}
};
static struct of_device_id wk2xxx_i2c_dt_ids[] = {
{.compatible = "company,wkxxxx", },
{}
};
MODULE_DEVICE_TABLE(of, wk2xxx_i2c_dt_ids);
static struct i2c_driver wk2xxx_i2c_driver = {
.driver = {
.name = "wkxxxx_i2c",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(wk2xxx_i2c_dt_ids),
},
.probe = wk2xxx_probe,
.remove = wk2xxx_remove,
.id_table = wk2xxx_i2c_id_table,
};
驱动功能初始化与注销
static int wk2xxx_probe(struct i2c_client *client,const struct i2c_device_id *dev_id)
{
uint8_t i;
int status, irq;
uint8_t dat[1];
irq = rockchip_i2c_parse_dt(&client->dev); //申请中断
if(irq<0)
{
return 1;
}
wk2xxx_read_global_reg(client,WK2XXX_GENA,dat);
if((dat[0]&0xf0)!=0xb0)
{
printk(KERN_ALERT "wk2xxx_probe() GENA = 0x%X\n",dat[0]);
printk(KERN_ERR "i2c 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;
}
}
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->wk2xxx_i2c_client = client;
s->port.line = i;
s->port.ops = &wk2xxx_pops;
s->port.uartclk = WK_CRASTAL_CLK;
s->port.fifosize = 256;
s->port.iobase = i+1;
s->port.irq = irq;
s->port.iotype = SERIAL_IO_PORT;
s->port.flags = ASYNC_BOOT_AUTOCONF;
status = uart_add_one_port(&wk2xxx_uart_driver, &s->port);
if(status<0)
{
printk(KERN_ALERT "uart_add_one_port failed for line i:= %d with error %d\n",i,status);
mutex_unlock(&wk2xxxs_lock);
return status;
}
}
mutex_unlock(&wk2xxxs_lock);
return status;
}
static int wk2xxx_remove(struct i2c_client *i2c_client)
{
int i;
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( KERN_ERR"removing wk2xxx driver\n");
uart_unregister_driver(&wk2xxx_uart_driver);
mutex_unlock(&wk2xxxs_lock);
return 0;
}
内核提供的打包串口功能的结构体
在probe函数执行的过程中,打包串口功能实现指针的结构体被加载进内核。
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
};
申请中断
static int rockchip_i2c_parse_dt(struct device *dev)
{
int irq_gpio, irq_flags, irq;
//从设备树获取IRQ——GPIO
irq_gpio = of_get_named_gpio_flags(dev->of_node, "irq_gpio", 0,(enum of_gpio_flags *)&irq_flags);
if (!gpio_is_valid(irq_gpio))
{
printk(KERN_ERR"invalid wk2xxx_irq_gpio: %d\n", irq_gpio);
return -1;
}
if(gpio_request(irq_gpio, "gpio15"))
{
printk(KERN_ERR"gpio_request gpio15 failed!\n");
return -1;
}
irq = gpio_to_irq(irq_gpio);
if(!irq)
{
printk(KERN_ERR"wk2xxx_irqGPIO: %d get irq failed!\n", irq);
return -1;
}
printk(KERN_ERR"wk2xxx_irq_gpio: %d, irq: %d", irq_gpio, irq);
return irq;
}
i2c操作函数
/*
* This function write wk2xxx of Global register:
*/
static int wk2xxx_write_global_reg(struct i2c_client *client,uint8_t greg,uint8_t dat)
{
struct i2c_msg msg;
uint8_t cmd_addr,wk_addr = 0;
uint8_t status = 0;
uint8_t wk_buf[2];
cmd_addr=(((client->addr)<<1)|(greg&0x30)>>2)>>1; //根据硬件手册的指令协议
mutex_lock(&wk2xxxs_reg_lock);
wk_buf[0] = greg&0x0f;
wk_buf[1] = dat;
msg.addr = cmd_addr;
msg.flags = 0;
msg.len = 2;
msg.buf = wk_buf;
if (i2c_transfer(client->adapter, &msg, 1) < 0) {
printk(KERN_ALERT "wk2xxx_write_global_reg1(i2c) w_error!\n");
status = 1;
mutex_unlock(&wk2xxxs_reg_lock);
return status;
}
mutex_unlock(&wk2xxxs_reg_lock);
return status;
}
/*
* This function read wk2xxx of slave register:
*/
static int wk2xxx_read_slave_reg(struct i2c_client *client,uint8_t port,uint8_t sreg,uint8_t *dat)
{ struct i2c_msg msg[2];
uint8_t cmd_addr,wk_addr = 0;
uint8_t ret, status = 0;
uint8_t wk_reg[1],wk_dat[1];
cmd_addr=(((client->addr)<<1)|(port-1)<<2)>>1;
mutex_lock(&wk2xxxs_reg_lock);
/***********************************************/
wk_reg[0] = sreg&0x0f;
msg[0].addr = cmd_addr;
msg[0].flags = 0;
msg[0].len = 1;
msg[0].buf = wk_reg;
if (i2c_transfer(client->adapter, &msg[0], 1) < 0) {
printk(KERN_ALERT "wk2xxx_read_slave_reg1(i2c) w_error!\n");
status = 1;
mutex_unlock(&wk2xxxs_reg_lock);
return status;
}
/**********************************************/
msg[1].addr = cmd_addr;
msg[1].flags = I2C_M_RD;
msg[1].len = 1;
msg[1].buf = wk_dat;
ret=i2c_transfer(client->adapter, &msg[1], 1);
if(ret!=1){
printk(KERN_ALERT "wk2xxx_read_slave_reg2(i2c) w_error!\n");
*dat=0x0;
status = 1;
mutex_unlock(&wk2xxxs_reg_lock);
return status;
}
*dat=wk_dat[0];
mutex_unlock(&wk2xxxs_reg_lock);
return status;
}
/*
* This function write wk2xxx of Slave register:
*/
static int wk2xxx_write_slave_reg(struct i2c_client *client,uint8_t port,uint8_t sreg,uint8_t dat)
{
struct i2c_msg msg;
uint8_t cmd_addr,wk_addr = 0;
uint8_t status = 0;
uint8_t wk_buf[2];
cmd_addr=(((client->addr)<<1)|(port-1)<<2)>>1;
mutex_lock(&wk2xxxs_reg_lock);
wk_buf[0] = sreg&0x0f;
wk_buf[1] = dat;
msg.addr = cmd_addr;
msg.flags = 0;
msg.len = 2;
msg.buf = wk_buf;
if (i2c_transfer(client->adapter, &msg, 1) < 0) {
printk(KERN_ALERT "wk2xxx_write_slave_reg1(i2c) w_error!\n");
status = 1;
mutex_unlock(&wk2xxxs_reg_lock);
return status;
}
mutex_unlock(&wk2xxxs_reg_lock);
return status;
}
#define MAX_RFCOUNT_SIZE 256
/*
* This function read wk2xxx of fifo:
*/
static int wk2xxx_read_fifo(struct i2c_client *client,uint8_t port,uint8_t fifolen,uint8_t *dat)
{
struct i2c_msg msg;
uint8_t cmd_addr,wk_addr = 0;
wk_addr = 0x03;
int i, status = 0;
uint8_t fifo_data[256] = {0};
cmd_addr = (((client->addr)<<1)|(port-1)<<2|0x03) >> 1;
if(!(fifolen>0)){
printk(KERN_ERR "%s,read fifolen error!!\n", __func__);
return 1;
}
mutex_lock(&wk2xxxs_reg_lock);
msg.addr = cmd_addr;
msg.flags = I2C_M_RD;
msg.len = fifolen;
msg.buf = fifo_data;
if (i2c_transfer(client->adapter, &msg, 1) < 0) {
printk(KERN_ALERT " wk2xxx_read_fifo1(i2c) w_error!\n");
status = 1;
mutex_unlock(&wk2xxxs_reg_lock);
return status;
}
for (i = 0; i < fifolen; i++)
*(dat + i) = fifo_data[i];
mutex_unlock(&wk2xxxs_reg_lock);
return status;
}
/*
* This function write wk2xxx of fifo:
*/
static int wk2xxx_write_fifo(struct i2c_client *client,uint8_t port,uint8_t fifolen,uint8_t *dat)
{
struct i2c_msg msg;
uint8_t cmd_addr,wk_addr = 0;
int i, status = 0;
uint8_t fifo_data[256] = {0};
cmd_addr = (((client->addr)<<1)|(port-1)<<2|0x02) >> 1;
if(!(fifolen>0)){
printk(KERN_ERR "%s,wrire fifolen error!!\n", __func__);
return 1;
}
for (i = 0; i < fifolen; i++)
fifo_data[i] = *(dat + i);
mutex_lock(&wk2xxxs_reg_lock);
msg.addr = cmd_addr;
msg.flags = 0;
msg.len = fifolen;
msg.buf = fifo_data;
if (i2c_transfer(client->adapter, &msg, 1) < 0) {
printk(KERN_ALERT " wk2xxx_write_fifo1(i2c) w_error!\n");
status = 1;
mutex_unlock(&wk2xxxs_reg_lock);
return status;
}
mutex_unlock(&wk2xxxs_reg_lock);
return status;
}
串口工作函数
struct wk2xxx_port
{
//struct timer_list mytimer;
struct uart_port port;//[NR_PORTS];
struct i2c_client *wk2xxx_i2c_client;
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_fwcr;
uint8_t new_scr;
/*set baud 0f register*/
uint8_t new_baud1;
uint8_t new_baud0;
uint8_t new_pres;
};
static int wk2xxx_dowork(struct wk2xxx_port *s)
{
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("work_pending =: %d s->force_end_work = : %d freezing(current) = :%d s->suspending= :%d\n" ,work_pending(&s->work),s->force_end_work ,freezing(current),s->suspending);
#endif
return 1;
}
else
{
#ifdef _DEBUG_WK2XXX
printk( "--queue_work---error---\n");
#endif
return 0;
}
}
static void wk2xxx_work(struct work_struct *w)
{
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);
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;
//if( work_conf_flag)
// s->conf_flag = 0;
work_irq_flag = s->irq_flag;
if(work_irq_flag)
s->irq_flag = 0;
mutex_unlock(&wk2xxs_work_lock);
if(work_start_tx_flag)
{
wk2xxx_read_slave_reg(s->wk2xxx_i2c_client,s->port.iobase,WK2XXX_SIER,&rx);
rx |= WK2XXX_TFTRIG_IEN|WK2XXX_RFTRIG_IEN|WK2XXX_RXOUT_IEN;
wk2xxx_write_slave_reg(s->wk2xxx_i2c_client,s->port.iobase,WK2XXX_SIER,rx);
}
if(work_stop_rx_flag)
{
wk2xxx_read_slave_reg(s->wk2xxx_i2c_client,s->port.iobase,WK2XXX_SIER,&rx);
rx &=~WK2XXX_RFTRIG_IEN;
rx &=~WK2XXX_RXOUT_IEN;
wk2xxx_write_slave_reg(s->wk2xxx_i2c_client,s->port.iobase,WK2XXX_SIER,rx);
}
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 ));
if(s->start_tx_fail)
{
wk2xxx_read_slave_reg(s->wk2xxx_i2c_client,s->port.iobase,WK2XXX_SIER,&rx);
rx |= WK2XXX_TFTRIG_IEN|WK2XXX_RFTRIG_IEN|WK2XXX_RXOUT_IEN;
wk2xxx_write_slave_reg(s->wk2xxx_i2c_client,s->port.iobase,WK2XXX_SIER,rx);
s->start_tx_fail =0;
}
if(s->stop_rx_fail)
{
wk2xxx_read_slave_reg(s->wk2xxx_i2c_client,s->port.iobase,WK2XXX_SIER,&rx);
rx &=~WK2XXX_RFTRIG_IEN;
rx &=~WK2XXX_RXOUT_IEN;
wk2xxx_write_slave_reg(s->wk2xxx_i2c_client,s->port.iobase,WK2XXX_SIER,rx);
s->stop_rx_fail =0;
}
if(s->irq_fail)
{
s->irq_fail = 0;
enable_irq(s->port.irq);
}
}
static void wk2xxx_rx_chars(struct uart_port *port)
{
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,rfcnt2=0,rx_num=0;
wk2xxx_write_slave_reg(s->wk2xxx_i2c_client,s->port.iobase,WK2XXX_SPAGE,WK2XXX_PAGE0);//set register in page0
wk2xxx_read_slave_reg(s->wk2xxx_i2c_client,s->port.iobase,WK2XXX_FSR,dat);
fsr = dat[0];
wk2xxx_read_slave_reg(s->wk2xxx_i2c_client,s->port.iobase,WK2XXX_LSR,dat);
lsr = dat[0];
wk2xxx_read_slave_reg(s->wk2xxx_i2c_client,s->port.iobase,WK2XXX_SIFR,dat);
sifr=dat[0];
if(!(sifr&0x80))//no error
{
flg = TTY_NORMAL;
if (fsr& WK2XXX_RDAT)
{
wk2xxx_read_slave_reg(s->wk2xxx_i2c_client,s->port.iobase,WK2XXX_RFCNT,dat);
rfcnt=dat[0];
wk2xxx_read_slave_reg(s->wk2xxx_i2c_client,s->port.iobase,WK2XXX_RFCNT,dat);
rfcnt2=dat[0];
if(!(rfcnt2>=rfcnt))
{rfcnt=rfcnt2;}
rfcnt=(rfcnt==0)?255:rfcnt;
#if 1
wk2xxx_read_fifo(s->wk2xxx_i2c_client,s->port.iobase, rfcnt,rx_dat);
#else
for(rx_num=0;rx_num<rfcnt;rx_num++)
{
wk2xxx_read_slave_reg(s->wk2xxx_i2c_client,s->port.iobase,WK2XXX_FDAT,dat);
rx_dat[rx_num]=dat[0];
}
#endif
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(KERN_ALERT "rx_chars:0x%x----\n",rx_dat[rx_num]);
#endif
uart_insert_char(&s->port, status, WK2XXX_STATUS_OE, rx_dat[rx_num], flg);
rx_count++;
if ((rx_count >= 64 ) && (s->port.state->port.tty->port != NULL))
{
tty_flip_buffer_push(s->port.state->port.tty->port);
rx_count = 0;
}
}//for
if((rx_count > 0)&&(s->port.state->port.tty->port!= NULL))
{
#ifdef _DEBUG_WK2XXX
printk(KERN_ALERT "push buffer tty flip port = :%lx count =:%d\n",s->port.iobase,rx_count);
#endif
tty_flip_buffer_push(s->port.state->port.tty->port);
rx_count = 0;
}
}
}//ifm
else//error
{
while (fsr& WK2XXX_RDAT)/**/
{
wk2xxx_read_slave_reg(s->wk2xxx_i2c_client,s->port.iobase,WK2XXX_FDAT,dat);
ch = (int)dat[0];
#ifdef _DEBUG_WK2XXX
printk(KERN_ALERT "wk2xxx_rx_chars()----port:%lx--RXDAT:0x%x----\n",s->port.iobase,ch);
#endif
s->port.icount.rx++;
flg = TTY_NORMAL;
if (lsr&(WK2XXX_OE |WK2XXX_FE|WK2XXX_PE|WK2XXX_BI))
{
printk(KERN_ALERT "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->port != NULL))
{
tty_flip_buffer_push(s->port.state->port.tty->port);
rx_count = 0;
}
ignore_char:
wk2xxx_read_slave_reg(s->wk2xxx_i2c_client,s->port.iobase,WK2XXX_FSR,dat);
fsr = dat[0];
wk2xxx_read_slave_reg(s->wk2xxx_i2c_client,s->port.iobase,WK2XXX_LSR,dat);
lsr = dat[0];
}
out:
if((rx_count > 0)&&(s->port.state->port.tty->port != NULL))
{
#ifdef _DEBUG_WK2XXX1
printk(KERN_ALERT "push buffer tty flip port = :%lx count = :%d\n",s->port.iobase,rx_count);
#endif
tty_flip_buffer_push(s->port.state->port.tty->port);
rx_count = 0;
}
}//if()else
return;
#ifdef SUPPORT_SYSRQ
s->port.state->sysrq = 0;
#endif
goto error_return;
}
static void wk2xxx_tx_chars(struct uart_port *port)
{
struct wk2xxx_port *s = container_of(port,struct wk2xxx_port,port);
uint8_t fsr,tfcnt,dat[1],txbuf[256]={0};
int count,tx_count,i;
if (s->port.x_char)
{
wk2xxx_write_slave_reg(s->wk2xxx_i2c_client,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;
}
wk2xxx_read_slave_reg(s->wk2xxx_i2c_client,s->port.iobase,WK2XXX_FSR,dat);
fsr = dat[0];
wk2xxx_read_slave_reg(s->wk2xxx_i2c_client,s->port.iobase,WK2XXX_TFCNT,dat);
tfcnt= dat[0];
#ifdef _DEBUG_WK2XXX
printk(KERN_ALERT "wk2xxx_tx_chars fsr:0x%x,rfcnt:0x%x,port = %lx\n",fsr,tfcnt,s->port.iobase);
#endif
if(tfcnt==0)
{
tx_count=(fsr & WK2XXX_TFULL)?0:255;
#ifdef _DEBUG_WK2XXX
printk(KERN_ALERT "wk2xxx_tx_chars2 tx_count:%x,port = %lx\n",tx_count,s->port.iobase);
#endif
}
else
{
tx_count=255-tfcnt;
#ifdef _DEBUG_WK2XXX
printk(KERN_ALERT "wk2xxx_tx_chars2 tx_count:%x,port = %lx\n",tx_count,s->port.iobase);
#endif
}
#ifdef _DEBUG_WK2XXX
printk(KERN_ALERT "fsr:%x\n",fsr);
#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(KERN_ALERT "tx_chars:0x%x--\n",txbuf[i-1]);
#endif
}while(--count>0);
#if 0
printk(KERN_ALERT "tx_chars:port:%ld;tx_count:%x-\n",s->port.iobase,i);
#endif
#if 1
wk2xxx_write_fifo(s->wk2xxx_i2c_client,s->port.iobase,i,txbuf);
#else
for(count=0;count<i;count++)
{
wk2xxx_write_slave_reg(s->wk2xxx_i2c_client,s->port.iobase,WK2XXX_FDAT,txbuf[count]);
}
#endif
out:wk2xxx_read_slave_reg(s->wk2xxx_i2c_client,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);
}
}
}
static irqreturn_t wk2xxx_irq(int irq, void *dev_id)//
{
struct wk2xxx_port *s = dev_id;
disable_irq_nosync(s->port.irq);
#if 0
printk( "--wk2xxx_irq---in---\n");
#endif
s->irq_flag = 1;
if(wk2xxx_dowork(s))
{
;
}
else
{
s->irq_flag = 0;
s->irq_fail = 1;
}
return IRQ_HANDLED;
}
static void wk2xxxirq_app(struct uart_port *port)//
{
struct wk2xxx_port *s = container_of(port,struct wk2xxx_port,port);
unsigned int pass_counter = 0;
uint8_t sifr,gifr,sier,dat[1];
#ifdef _DEBUG_WK2XXX
printk(KERN_ALERT "wk2xxxirq_app()------port:%lx--------------\n",s->port.iobase);
#endif
wk2xxx_read_global_reg(s->wk2xxx_i2c_client,WK2XXX_GIFR ,dat);
gifr = dat[0];
#if 0
wk2xxx_read_global_reg(s->wk2xxx_i2c_client,WK2XXX_GIER ,dat);
gier = dat[0];
wk2xxx_read_slave_reg(s->wk2xxx_i2c_client,1,WK2XXX_SIFR,&sifr0);
wk2xxx_read_slave_reg(s->wk2xxx_i2c_client,2,WK2XXX_SIFR,&sifr1);
wk2xxx_read_slave_reg(s->wk2xxx_i2c_client,3,WK2XXX_SIFR,&sifr2);
wk2xxx_read_slave_reg(s->wk2xxx_i2c_client,4,WK2XXX_SIFR,&sifr3);
wk2xxx_read_slave_reg(s->wk2xxx_i2c_client,1,WK2XXX_SIER,&sier0);
wk2xxx_read_slave_reg(s->wk2xxx_i2c_client,2,WK2XXX_SIER,&sier1);
wk2xxx_read_slave_reg(s->wk2xxx_i2c_client,3,WK2XXX_SIER,&sier2);
wk2xxx_read_slave_reg(s->wk2xxx_i2c_client,4,WK2XXX_SIER,&sier3);
printk(KERN_ALERT "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_slave_reg(s->wk2xxx_i2c_client,s->port.iobase,WK2XXX_SIFR,dat);
sifr = dat[0];
wk2xxx_read_slave_reg(s->wk2xxx_i2c_client,s->port.iobase,WK2XXX_SIER,dat);
sier = dat[0];
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_slave_reg(s->wk2xxx_i2c_client,s->port.iobase,WK2XXX_SIFR,dat);
sifr = dat[0];
wk2xxx_read_slave_reg(s->wk2xxx_i2c_client,s->port.iobase,WK2XXX_SIER,dat);
sier = dat[0];
} while ((sifr&WK2XXX_RXOVT_INT)||(sifr & WK2XXX_RFTRIG_INT)||((sifr & WK2XXX_TFTRIG_INT)&&(sier & WK2XXX_TFTRIG_IEN)));
}
/*
* 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 tx;
struct wk2xxx_port *s = container_of(port,struct wk2xxx_port,port);
#ifdef _DEBUG_WK2XXX
printk(KERN_ALERT "wk2xxx_tx_empty()---------in---\n");
#endif
mutex_lock(&wk2xxxs_lock);
if(!(s->tx_empty_flag || s->tx_empty_fail))
{
wk2xxx_read_slave_reg(s->wk2xxx_i2c_client,s->port.iobase,WK2XXX_FSR,&tx);
while((tx & WK2XXX_TDAT)|(tx&WK2XXX_TBUSY))
{
wk2xxx_read_slave_reg(s->wk2xxx_i2c_client,s->port.iobase,WK2XXX_FSR,&tx);
}
s->tx_empty = ((tx & WK2XXX_TDAT)|(tx&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);
return s->tx_empty;
}
static void wk2xxx_set_mctrl(struct uart_port *port, u_int mctrl)//nothing
{
#ifdef _DEBUG_WK2XXX
printk(KERN_ALERT "%s!!\n", __func__);
#endif
}
static u_int wk2xxx_get_mctrl(struct uart_port *port)// since no modem control line
{
return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
}
/*
* interrupts disabled on entry
*/
static void wk2xxx_stop_tx(struct uart_port *port)//
{
uint8_t sier;
struct wk2xxx_port *s = container_of(port,struct wk2xxx_port,port);
#ifdef _DEBUG_WK2XXX
printk(KERN_ALERT "-wk2xxx_stop_tx------in---\n");
#endif
mutex_lock(&wk2xxxs_lock);
wk2xxx_read_slave_reg(s->wk2xxx_i2c_client,s->port.iobase,WK2XXX_SIER,&sier);
sier&=~WK2XXX_TFTRIG_IEN;
wk2xxx_write_slave_reg(s->wk2xxx_i2c_client,s->port.iobase,WK2XXX_SIER,sier);
mutex_unlock(&wk2xxxs_lock);
#ifdef _DEBUG_WK2XXX4
printk(KERN_ALERT "-wk2xxx_stop_tx------exit---\n");
#endif
}
/*
* * interrupts may not be disabled on entry
*/
static void wk2xxx_start_tx(struct uart_port *port)
{
struct wk2xxx_port *s = container_of(port,struct wk2xxx_port,port);
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;
}
}
}
/*
* * Interrupts enabled
*/
static void wk2xxx_stop_rx(struct uart_port *port)
{
struct wk2xxx_port *s = container_of(port,struct wk2xxx_port,port);
#ifdef _DEBUG_WK2XXX
printk(KERN_ALERT "%s!!\n", __func__);
#endif
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;
}
}
}
/*
* * No modem control lines
* */
static void wk2xxx_enable_ms(struct uart_port *port) //nothing
{
#ifdef _DEBUG_WK2XXX
printk(KERN_ALERT "%s!!\n", __func__);
#endif
}
/*
* * Interrupts always disabled.
*/
static void wk2xxx_break_ctl(struct uart_port *port, int break_state)
{
#ifdef _DEBUG_WK2XXX
printk(KERN_ALERT "%s!!\n", __func__);
#endif
}
static int wk2xxx_startup(struct uart_port *port)//i
{
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_workqueue(b);
if (!s->workqueue)
{
dev_warn(&s->wk2xxx_i2c_client->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_global_reg(s->wk2xxx_i2c_client,WK2XXX_GENA,dat);
gena=dat[0];
switch (s->port.iobase)
{
case 1:
gena|=WK2XXX_UT1EN;
wk2xxx_write_global_reg(s->wk2xxx_i2c_client,WK2XXX_GENA,gena);
break;
case 2:
gena|=WK2XXX_UT2EN;
wk2xxx_write_global_reg(s->wk2xxx_i2c_client,WK2XXX_GENA,gena);
break;
case 3:
gena|=WK2XXX_UT3EN;
wk2xxx_write_global_reg(s->wk2xxx_i2c_client,WK2XXX_GENA,gena);
break;
case 4:
gena|=WK2XXX_UT4EN;
wk2xxx_write_global_reg(s->wk2xxx_i2c_client,WK2XXX_GENA,gena);
break;
default:
printk(KERN_ALERT ":con_wk2xxx_subport bad iobase %d\n", (uint8_t)s->port.iobase);
break;
}
wk2xxx_read_global_reg(s->wk2xxx_i2c_client,WK2XXX_GRST,dat);
grst=dat[0];
switch (s->port.iobase)
{
case 1:
grst|=WK2XXX_UT1RST;
wk2xxx_write_global_reg(s->wk2xxx_i2c_client,WK2XXX_GRST,grst);
break;
case 2:
grst|=WK2XXX_UT2RST;
wk2xxx_write_global_reg(s->wk2xxx_i2c_client,WK2XXX_GRST,grst);
break;
case 3:
grst|=WK2XXX_UT3RST;
wk2xxx_write_global_reg(s->wk2xxx_i2c_client,WK2XXX_GRST,grst);
break;
case 4:
grst|=WK2XXX_UT4RST;
wk2xxx_write_global_reg(s->wk2xxx_i2c_client,WK2XXX_GRST,grst);
break;
default:
printk(KERN_ALERT "bad iobase %d\n", (uint8_t)s->port.iobase);
break;
}
wk2xxx_read_slave_reg(s->wk2xxx_i2c_client,s->port.iobase,WK2XXX_SIER,dat);
sier = dat[0];
sier &= ~WK2XXX_TFTRIG_IEN;
sier |= WK2XXX_RFTRIG_IEN;
sier |= WK2XXX_RXOUT_IEN;
wk2xxx_write_slave_reg(s->wk2xxx_i2c_client,s->port.iobase,WK2XXX_SIER,sier);
wk2xxx_read_slave_reg(s->wk2xxx_i2c_client,s->port.iobase,WK2XXX_SCR,dat);
scr = dat[0] | WK2XXX_TXEN|WK2XXX_RXEN;
wk2xxx_write_slave_reg(s->wk2xxx_i2c_client,s->port.iobase,WK2XXX_SCR,scr);
//initiate the fifos
wk2xxx_write_slave_reg(s->wk2xxx_i2c_client,s->port.iobase,WK2XXX_FCR,0xff);//initiate the fifos
wk2xxx_write_slave_reg(s->wk2xxx_i2c_client,s->port.iobase,WK2XXX_FCR,0xfc);
//set rx/tx interrupt
wk2xxx_write_slave_reg(s->wk2xxx_i2c_client,s->port.iobase,WK2XXX_SPAGE,1);
wk2xxx_write_slave_reg(s->wk2xxx_i2c_client,s->port.iobase,WK2XXX_RFTL,0x40);
wk2xxx_write_slave_reg(s->wk2xxx_i2c_client,s->port.iobase,WK2XXX_TFTL,0X20);
wk2xxx_write_slave_reg(s->wk2xxx_i2c_client,s->port.iobase,WK2XXX_SPAGE,0);
//enable the sub port interrupt
wk2xxx_read_global_reg(s->wk2xxx_i2c_client,WK2XXX_GIER,dat);
gier = dat[0];
switch (s->port.iobase)
{
case 1:
gier|=WK2XXX_UT1IE;
wk2xxx_write_global_reg(s->wk2xxx_i2c_client,WK2XXX_GIER,gier);
break;
case 2:
gier|=WK2XXX_UT2IE;
wk2xxx_write_global_reg(s->wk2xxx_i2c_client,WK2XXX_GIER,gier);
break;
case 3:
gier|=WK2XXX_UT3IE;
wk2xxx_write_global_reg(s->wk2xxx_i2c_client,WK2XXX_GIER,gier);
break;
case 4:
gier|=WK2XXX_UT4IE;
wk2xxx_write_global_reg(s->wk2xxx_i2c_client,WK2XXX_GIER,gier);
break;
default:
printk(KERN_ALERT ": bad iobase %d\n", (uint8_t)s->port.iobase);
break;
}
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, "wk2xxx_irq_gpio", s) < 0)
{
dev_warn(&s->wk2xxx_i2c_client->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(KERN_ALERT "%s!!**finish**\n", __func__);
#endif
return 0;
}
//* Power down all displays on reboot, poweroff or halt *
static void wk2xxx_shutdown(struct uart_port *port)
{
uint8_t gena,dat[1];
struct wk2xxx_port *s = container_of(port,struct wk2xxx_port,port);
#ifdef _DEBUG_WK2XXX
printk(KERN_ALERT "%s!!**start**\n", __func__);
#endif
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)
{
free_irq(s->port.irq, s);//释放中断
}
wk2xxx_read_global_reg(s->wk2xxx_i2c_client,WK2XXX_GENA,dat);
gena=dat[0];
switch (s->port.iobase)
{
case 1:
gena&=~WK2XXX_UT1EN;
wk2xxx_write_global_reg(s->wk2xxx_i2c_client,WK2XXX_GENA,gena);
break;
case 2:
gena&=~WK2XXX_UT2EN;
wk2xxx_write_global_reg(s->wk2xxx_i2c_client,WK2XXX_GENA,gena);
break;
case 3:
gena&=~WK2XXX_UT3EN;
wk2xxx_write_global_reg(s->wk2xxx_i2c_client,WK2XXX_GENA,gena);
break;
case 4:
gena&=~WK2XXX_UT4EN;
wk2xxx_write_global_reg(s->wk2xxx_i2c_client,WK2XXX_GENA,gena);
break;
default:
printk(KERN_ALERT "bad iobase %d\n", (uint8_t)s->port.iobase);
break;
}
}
static void conf_wk2xxx_subport(struct uart_port *port)//i
{
struct wk2xxx_port *s = container_of(port,struct wk2xxx_port,port);
uint8_t old_sier,fwcr,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;
fwcr=s->new_fwcr;
wk2xxx_read_slave_reg(s->wk2xxx_i2c_client,s->port.iobase,WK2XXX_SIER ,dat);
old_sier = dat[0];
wk2xxx_write_slave_reg(s->wk2xxx_i2c_client,s->port.iobase,WK2XXX_SIER ,old_sier&(~(WK2XXX_TFTRIG_IEN | WK2XXX_RFTRIG_IEN | WK2XXX_RXOUT_IEN)));
//local_irq_restore(flags);
do{
wk2xxx_read_slave_reg(s->wk2xxx_i2c_client,s->port.iobase,WK2XXX_FSR,dat);
} while (dat[0] & WK2XXX_TBUSY);
// then, disable tx and rx
wk2xxx_read_slave_reg(s->wk2xxx_i2c_client,s->port.iobase,WK2XXX_SCR,dat);
scr = dat[0];
wk2xxx_write_slave_reg(s->wk2xxx_i2c_client,s->port.iobase,WK2XXX_SCR ,scr&(~(WK2XXX_RXEN|WK2XXX_TXEN)));
// set the parity, stop bits and data size //
wk2xxx_write_slave_reg(s->wk2xxx_i2c_client,s->port.iobase,WK2XXX_LCR ,lcr);
/*set cts and rst*/
if(fwcr>0){
#ifdef _DEBUG_WK2XXX
printk(KERN_ALERT "-conf_wk2xxx_subport-set ctsrts--fwcr=0x%X\n",fwcr);
#endif
wk2xxx_write_slave_reg(s->wk2xxx_i2c_client,s->port.iobase,WK2XXX_FWCR,fwcr);
wk2xxx_write_slave_reg(s->wk2xxx_i2c_client,s->port.iobase,WK2XXX_SPAGE ,1);
wk2xxx_write_slave_reg(s->wk2xxx_i2c_client,s->port.iobase,WK2XXX_FWTH,0XF0);
wk2xxx_write_slave_reg(s->wk2xxx_i2c_client,s->port.iobase,WK2XXX_FWTL,0X80);
wk2xxx_write_slave_reg(s->wk2xxx_i2c_client,s->port.iobase,WK2XXX_SPAGE ,0);
}
wk2xxx_write_slave_reg(s->wk2xxx_i2c_client,s->port.iobase,WK2XXX_SIER ,old_sier);
// set the baud rate //
wk2xxx_write_slave_reg(s->wk2xxx_i2c_client,s->port.iobase,WK2XXX_SPAGE ,1);
wk2xxx_write_slave_reg(s->wk2xxx_i2c_client,s->port.iobase,WK2XXX_BAUD0 ,baud0_ss);
wk2xxx_write_slave_reg(s->wk2xxx_i2c_client,s->port.iobase,WK2XXX_BAUD1 ,baud1_ss);
wk2xxx_write_slave_reg(s->wk2xxx_i2c_client,s->port.iobase,WK2XXX_PRES ,pres_ss);
wk2xxx_write_slave_reg(s->wk2xxx_i2c_client,s->port.iobase,WK2XXX_SPAGE ,0);
wk2xxx_write_slave_reg(s->wk2xxx_i2c_client,s->port.iobase,WK2XXX_SCR ,scr|(WK2XXX_RXEN|WK2XXX_TXEN));
}
// change speed
static void wk2xxx_termios( struct uart_port *port, struct ktermios *termios,
struct ktermios *old)
{
struct wk2xxx_port *s = container_of(port,struct wk2xxx_port,port);
int baud = 0;
uint8_t lcr=0,fwcr,baud1,baud0,pres;
unsigned short cflag;
unsigned short lflag;
cflag = termios->c_cflag;
lflag = termios->c_lflag;
baud1=0;
baud0=0;
pres=0;
baud = tty_termios_baud_rate(termios);
printk(KERN_ALERT "wk2xxx_termios()----baud = tty_termios_baud_rate(termios);- baud:%d--\n",baud);
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;
break;
}
tty_termios_encode_baud_rate(termios, baud, baud);
/* we are sending char from a workqueue so enable */
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;
}
/*set rts and cts*/
fwcr=(termios->c_cflag&CRTSCTS)?0X30:0;
s->new_baud1=baud1;
s->new_baud0=baud0;
s->new_pres=pres;
s->new_lcr = lcr;
s->new_fwcr = fwcr;
conf_wk2xxx_subport(&s->port);
}
static const char *wk2xxx_type(struct uart_port *port)
{
return port->type == PORT_WK2XXX ? "wk2xxx" : NULL;//this is defined in serial_core.h
}
static void wk2xxx_release_port(struct uart_port *port)
{
//Not implement
}
static int wk2xxx_request_port(struct uart_port *port)//no such memory region needed for wk2xxx
{
//Not implement
return 0;
}
static void wk2xxx_config_port(struct uart_port *port, int flags)
{
struct wk2xxx_port *s = container_of(port,struct wk2xxx_port,port);
if (flags & UART_CONFIG_TYPE && wk2xxx_request_port(port) == 0)
s->port.type = PORT_WK2XXX;
}
static int wk2xxx_verify_port(struct uart_port *port, struct serial_struct *ser)
{
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)
// ret = -EINVAL;
if (port->iobase != ser->port)
ret = -EINVAL;
if (ser->hub6 != 0)
ret = -EINVAL;
return ret;
}