目录
-
WK系列介绍
-
WK2124芯片原理
-
寄存器介绍
-
硬件连接
-
驱动框架
-
-
驱动移植
-
驱动源码分析
-
驱动信息描述和数据结构
-
串口驱动的底层操作
-
tty驱动架构
-
wk2xxx_dowork()和wk2xxx_work函数
-
修改设备树
-
驱动调试修改
-
-
问题
WK2124
WK系列介绍
WK2124能实现SPI拓展4路UART。
每个子通道UART 的波特率、字长、校验格式可以独立设置,最高提供2Mbps 的通信速率。
每个子通道具备收/发独立的256 级FIFO,FIFO 的中断可按用户需求进行编程触发点且具备超时中断功能。
WK2124芯片原理
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ceQwMwE9-1664788113856)(image/image_OgdjENIum8.png)]
寄存器介绍
WK2124的寄存器地址按6位地址编码分为全局寄存器和子串口寄存器
全局寄存器 5个
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5tMt9CI3-1664788113857)(image/image_vq5jIEx_7E.png)]
子串口寄存器 25个 寄存器地址格式:C0C1 REG[3:0] (C0C1 两位是可分别对应子串口)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eM8jfk6F-1664788113859)(image/image_I5PVp8HvRT.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-C6N8HOKT-1664788113862)(image/image_uW0CNlJDER.png)]
硬件连接
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9iICebD5-1664788113869)(image/image_chosDex2jx.png)]
-
WK 芯片作SPI 从设备和CPU 端的主SPI 需要连接的信号有CS 信号(此信号不能一
直拉低,需要用主spi 的cs 信号控制)、CLK 信号、MOSI 信号、MISO 信号,具体
连接方式如上图。 -
IRQ 信号为WK 芯片的中断输出信号,需要连接到CPU 具有外部中断功能的GPIO
上。
驱动框架
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ibMtyfsR-1664788113871)(image/image_dzvIy5eTBX.png)]
1、 WK 驱动工作在linux 内核层,向上提供4 个串口设备节点供应用层用户调用。
也就是说WK 驱动注册成功以后,在/dev/ 目录下会生成 ttysWK0、ttysWK1、
ttysWK2、ttysWK3 共4 个串口设备节点,应用层就可以按照操作普通串口节
点的方式操作。
2、 WK 驱动需要和WK 芯片进行数据交互,数据交互是通过SPI 总线进行的,所以
WK 驱动会调用SPI 总线驱动接口进行数据收发。
驱动移植
驱动源码分析
驱动信息描述和数据结构
-
串口驱动描述
鉴于芯片的相关特性和驱动编写的需要,定义了结构体 wk2xxx_port用于对WK的SPI转串口驱动进行描述
struct wk2xxx_port {const struct wk2xxx_devtype *devtype;struct uart_driver uart;struct spi_device *spi_wk;struct workqueue_struct *workqueue;struct work_struct work;unsigned char buf[256];struct kthread_worker kworker;struct task_struct *kworker_task;struct kthread_work irq_work;int irq_gpio_num; /*中断IO的GPIO编号*/int rst_gpio_num; /*复位引脚的GPIO编号*/int irq_gpio; /*中断编号*/int minor; /* minor number */int tx_empty; struct wk2xxx_one p[NR_PORTS]; };
-
串口端口描述
定义一个结构体wk2xxx_one来描述WK2xxx芯片的串口端口进行描述,实际上是对uart_port的进一步封装,增加了两个内核队列和芯片子串口一些寄存器。
struct wk2xxx_one {struct uart_port port;//[NR_PORTS];struct kthread_work start_tx_work;struct kthread_work stop_rx_work;uint8_t line;uint8_t new_lcr_reg;uint8_t new_fwcr_reg;uint8_t new_scr_reg; /*baud register*/uint8_t new_baud1_reg;uint8_t new_baud0_reg;uint8_t new_pres_reg; };
串口驱动的底层操作
-
读全局寄存器函数
-
写全局寄存器函数
-
读子寄存器函数
-
写子寄存器函数
-
读FIFO寄存器函数
-
写FIFO寄存器函数
tty驱动架构
wk拓展uart,需要向驱动提供对串口的操作函数,
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,};
wk2xxx_dowork()和wk2xxx_work函数
这两个函数封装中断和内核线程功能,提供给串口操作函数
static int wk2xxx_dowork(struct wk2xxx_port *s)
{
#ifdef _DEBUG_WK_FUNCTIONprintk(KERN_ALERT "%s!!-port:%ld;--in--\n", __func__, s->port.iobase);
#endifif (!s->force_end_work && !work_pending(&s->work) && !freezing(current) && !s->suspending){queue_work(s->workqueue, &s->work);
#ifdef _DEBUG_WK_FUNCTIONprintk("%s!!--queue_work---ok!---\n", __func__);// 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);
#endifreturn 1;}else{
#ifdef _DEBUG_WK_FUNCTIONprintk("%s!!--queue_work---error!---\n", __func__);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// 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);// return 0;// printk("work_pending() =: %d tx_empty_flag = : %d start_tx_flag = :%d stop_tx_flag = :%d conf_flag =: %d irq_flag =: %d tx_empty=:%d\n",work_pending(&s->work),s->tx_empty_flag,s->start_tx_flag,s->stop_tx_flag,s->stop_rx_flag,s->conf_flag,s->irq_flag,s->tx_empty);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;
#ifdef _DEBUG_WK_FUNCTIONprintk(KERN_ALERT "%s!!-port:%ld;--in--\n", __func__, s->port.iobase);
#endifdo{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->spi_wk, s->port.iobase, WK2XXX_SIER, &rx);rx |= WK2XXX_TFTRIG_IEN | WK2XXX_RFTRIG_IEN | WK2XXX_RXOUT_IEN;wk2xxx_write_slave_reg(s->spi_wk, s->port.iobase, WK2XXX_SIER, rx);}if (work_stop_rx_flag){wk2xxx_read_slave_reg(s->spi_wk, s->port.iobase, WK2XXX_SIER, &rx);rx &= ~WK2XXX_RFTRIG_IEN;rx &= ~WK2XXX_RXOUT_IEN;wk2xxx_write_slave_reg(s->spi_wk, 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->spi_wk, s->port.iobase, WK2XXX_SIER, &rx);rx |= WK2XXX_TFTRIG_IEN | WK2XXX_RFTRIG_IEN | WK2XXX_RXOUT_IEN;wk2xxx_write_slave_reg(s->spi_wk, s->port.iobase, WK2XXX_SIER, rx);s->start_tx_fail = 0;}if (s->stop_rx_fail){wk2xxx_read_slave_reg(s->spi_wk, s->port.iobase, WK2XXX_SIER, &rx);rx &= ~WK2XXX_RFTRIG_IEN;rx &= ~WK2XXX_RXOUT_IEN;wk2xxx_write_slave_reg(s->spi_wk, s->port.iobase, WK2XXX_SIER, rx);s->stop_rx_fail = 0;}if (s->irq_fail){s->irq_fail = 0;enable_irq(s->port.irq);}#ifdef _DEBUG_WK_FUNCTIONprintk(KERN_ALERT "%s!!-port:%ld;--exit--\n", __func__, s->port.iobase);
#endif
fdef _DEBUG_WK_FUNCTIONprintk(KERN_ALERT "%s!!-port:%ld;--exit--\n", __func__, s->port.iobase);
#endif
}
修改设备树
选择将SPI3拓展成uart口
#define MX6UL_PAD_UART2_RX_DATA__ECSPI3_SCLK 0x0098 0x0324 0x0554 0x8 0x0
#define MX6UL_PAD_UART2_CTS_B__ECSPI3_MOSI 0x009C 0x0328 0x055C 0x8 0x0
#define MX6UL_PAD_UART2_RTS_B__ECSPI3_MISO 0x00A0 0x032C 0x0558 0x8 0x0
#define MX6UL_PAD_UART2_TX_DATA__ECSPI3_SS0 0x0094 0x0320 0x0560 0x8 0x0