WK2124

目录

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)]

  1. WK 芯片作SPI 从设备和CPU 端的主SPI 需要连接的信号有CS 信号(此信号不能一
    直拉低,需要用主spi 的cs 信号控制)、CLK 信号、MOSI 信号、MISO 信号,具体
    连接方式如上图。

  2. 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 总线驱动接口进行数据收发。

驱动移植

驱动源码分析
驱动信息描述和数据结构
  1. 串口驱动描述

    鉴于芯片的相关特性和驱动编写的需要,定义了结构体 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];
    };
    
  2. 串口端口描述

    定义一个结构体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;
    };
    
串口驱动的底层操作
  1. 读全局寄存器函数

  2. 写全局寄存器函数

  3. 读子寄存器函数

  4. 写子寄存器函数

  5. 读FIFO寄存器函数

  6. 写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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值