目录
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_FUNCTION
printk(KERN_ALERT "%s!!-port:%ld;--in--\n", __func__, s->port.iobase);
#endif
if (!s->force_end_work && !work_pending(&s->work) && !freezing(current) && !s->suspending)
{
queue_work(s->workqueue, &s->work);
#ifdef _DEBUG_WK_FUNCTION
printk("%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);
#endif
return 1;
}
else
{
#ifdef _DEBUG_WK_FUNCTION
printk("%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_FUNCTION
printk(KERN_ALERT "%s!!-port:%ld;--in--\n", __func__, s->port.iobase);
#endif
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->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_FUNCTION
printk(KERN_ALERT "%s!!-port:%ld;--exit--\n", __func__, s->port.iobase);
#endif
fdef _DEBUG_WK_FUNCTION
printk(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