全志SPI-NG框架使用说明

一、简介

SPI-NG框架,NG为next generation的缩写,是全志新的SPI框架,在新的BSP包中已全部启用。支持SPI 主从模式的配置和使用。

二、设备树配置

sunxi,spi-bus-mode = <SUNXI_SPI_BUS_MASTER>;

有两个选项:

  • SUNXI_SPI_BUS_MASTER
  • SUNXI_SPI_BUS_SLAVE

驱动默认配置为:SUNXI_SPI_BUS_MASTER,当选为SUNXI_SPI_BUS_SLAVE模式时,还可进行SLAVE详细的配置。

dma0:dma-controller@03002000 {
    compatible = "allwinner,sun8i-dma";
    reg = <0x0 0x03002000 0x0 0x1000>;
    interrupts = <GIC_SPI 10 IRQ_TYPE_LEVEL_HIGH>;
    clocks = <&clk_dma>;
    #dma-cells = <1>;
    dma-channels = <24>;   //打开SPI相应通道的DMA
    dma-requests = <64>;
    status = "okay";
};
spi1: spi@05011000 {
    #address-cells = <1>;
    #size-cells = <0>;
    compatible = "allwinner,sunxi-spi-v0.91";
    device_type = "spi1";
    reg = <0x0 0x05011000 0x0 0x1000>;
    interrupts = <GIC_SPI 66 IRQ_TYPE_LEVEL_HIGH>;
    clocks = <&clk_pll_periph0>, <&clk_spi1>;
    clock-names = "pll", "mod", "ahb";
    clock-frequency = <100000000>;
    pinctrl-names = "default", "sleep";
    pinctrl-0 = <&spi1_pins_a &spi1_pins_b>;
    pinctrl-1 = <&spi1_pins_c>;
    use_dma = <1>;
    dmas = <&dma0 23>, <&dma0 23>;
    dma-names = "tx", "rx";
    sunxi,spi-num-cs = <2>;
    sunxi,spi-bus-mode = <SUNXI_SPI_BUS_SLAVE>;
    sunxi,spi-slave-cs = <0>;
    sunxi,spi-slave-mode = <SUNXI_SPI_SLAVE_CYCLIC_MODE>;
    sunxi,spi-cs-mode = <0>;  //0 auto, 1 soft
    status = "disabled";
};
sunxi,spi-slave-mode = <SUNXI_SPI_SLAVE_CYCLIC_MODE>;

SLAVE的方式有两个选项:

  • SUNXI_SPI_SLAVE_GENERAL_MODE
  • SUNXI_SPI_SLAVE_CYCLIC_MODE

驱动默认配置为SUNXI_SPI_SLAVE_GENERAL_MODE

SUNXI_SPI_SLAVE_GENERAL_MODE 为普通的模式,可以用来作为从设备的数据收发。SUNXI_SPI_SLAVE_CYCLIC_MODE 为DMA回环模式,只能用来作为数据的接收,不间断的接收。

三、驱动初始化调用流程

sunxi_spi_init()
    platform_driver_register()
        sunxi_spi_probe()                    //生成struct sunxi_spi *sspi结构体
            dma_set_mask_and_coherent()        //设置 DMA 掩码以告知内核 设备 DMA 寻址功能
            dma_set_max_seg_size()            //设备定义单次 DMA 传输的最大字节数,这里配置的是页大小,4KB
            sunxi_spi_resource_get()        //获取设备树资源,字段一一获取
            spi_alloc_master()
            sunxi_spi_request_dma()            //获取DMA的收发通道
            sunxi_spi_hw_init()                //硬件配置,包括配置寄存器,时钟,配置为从模式等
            spi_register_master()            //注册SPI控制器,后面的SPI操作就用这个控制器中的函数了
            sunxi_spi_create_cyclic_dev()    //如果是SLAVE_CYCLIC_MODE设备,则创建相关设备

sunxi_spi_probe函数执行过程:

sunxi_spi_probe()
    sunxi_spi_resource_get                (获取设备树资源配置)
        这里有一个获取ready-gpio的配置

    //这里进行相关函数的配置
    sspi->ctlr->prepare_message        = sunxi_spi_prepare_message;
    sspi->ctlr->unprepare_message    = sunxi_spi_unprepare_message;
    sspi->ctlr->setup            = sunxi_spi_setup;
    sspi->ctlr->set_cs            = sunxi_spi_set_cs;
    if (sspi->use_dma)
        sspi->ctlr->can_dma        = sunxi_spi_can_dma;
    sspi->ctlr->transfer_one    = sunxi_spi_transfer_one;
    sspi->ctlr->handle_err        = sunxi_spi_handle_err;

    ret = devm_request_irq(sspi->dev, sspi->irq, sunxi_spi_handler, 0, dev_name(sspi->dev), sspi);    //spi本身自己的中断
    gpio_direction_output(sspi->ready_gpio, 0);    //将这个引脚电平输出为低。

ready-gpio这个脚就是用来做同步的,主要解决主从双方何时收发数据。 如果没有这个GPIO脚,就是双方自己约定一个休眠时间,发完数据后等待一段时间再处理。

四、主模式(master)数据收发流程

1、发送数据

sunxi_spi_transfer_one()
        sunxi_spi_xfer_master()                        (mode_type : SINGLE_HALF_DUPLEX_TX - 2)
            sunxi_spi_dma_tx()                        //如果符合使用DMA的条件则使用DMA传输
            sunxi_spi_start_xfer()                    //开启传输
            sunxi_spi_cpu_tx()                        //如果不符合使用DMA的条件则使用CPU传输
            wait_for_completion_timeout()            //等待数据传输完成
                sunxi_spi_dma_cb_tx()                //如果DMA传输则调这个函数,什么也没有做
                sunxi_spi_handler()
                    sunxi_spi_bus_handler()            //将done complete,唤醒wait操作

2、接收数据

sunxi_spi_transfer_one()
        sunxi_spi_xfer_master()                        (mode_type : SINGLE_HALF_DUPLEX_RX - 1)
            sunxi_spi_dma_rx()                        //如果符合使用DMA的条件则使用DMA传输
            sunxi_spi_start_xfer()
            sunxi_spi_cpu_rx()                        //如果不符合使用DMA的条件则使用CPU传输
            wait_for_completion_timeout()            //等待数据传输完成
                sunxi_spi_dma_cb_rx()                //如果DMA传输则调这个函数,将done complete,唤醒wait操作
                sunxi_spi_handler()
                    sunxi_spi_bus_handler()            //如果CPU传输将done complete,唤醒wait操作

五、从模式(slave)数据收发流程

1、接收数据

sunxi_spi_transfer_one()
        sunxi_spi_xfer_slave()                        (mode_type : SINGLE_HALF_DUPLEX_RX - 1)
            sunxi_spi_dma_rx()                        //如果符合使用DMA的条件则使用DMA传输
            sunxi_spi_enable_irq()                    //如果不符合则使能数据ready中断
            wait_for_completion_interruptible()        //等待数据传输完成
                sunxi_spi_dma_cb_rx()                //如果DMA传输完则调这个函数,将done complete,唤醒wait操作
                sunxi_spi_handler()
                    sunxi_spi_bus_handler()            //将done complete,唤醒wait操作
            sunxi_spi_cpu_rx()                        //如果不符合使用DMA的条件则使用CPU传输

注意:如果是SUNXI_SPI_SLAVE_CYCLIC_MODE模式则不等待数据接收完毕,直接返回了,也就是不会调用wait_for_completion_interruptible函数

2、发送数据

sunxi_spi_transfer_one()
        sunxi_spi_xfer_slave()                        (mode_type : SINGLE_HALF_DUPLEX_TX - 2)
            sunxi_spi_dma_tx()                        //如果符合使用DMA的条件则使用DMA传输
            sunxi_spi_enable_irq()                    //使能数据发送完成中断
            wait_for_completion_interruptible()        //等待数据传输完成
                sunxi_spi_dma_cb_tx()                //如果DMA传输则调这个函数,什么也没有做
                sunxi_spi_handler()
                    sunxi_spi_bus_handler()            //将done complete,唤醒wait操作

3、GENERAL_MODE模式

全志提供了spi-sunxi-slave-test.c 设计了一个简单的方案,针对主机先发送操作码,然后发送数据给从机,从机将这个数据存到一个cache中,并通过sys接口给上层去访问。

有个packet head结构,如下:

struct sunxi_spi_slave_head {
    u8 op_code;                            //代表操作码
    u8 bits;                            //SPI_NBITS_SINGLE 单bit位传输
    u16 addr;                            //要操作的地址
    u16 len;                            //数据的长度
};
{ 0x03 0x01 0x00 0x00 0x00 0x20 }

op_code类型:

#define SUNXI_OP_WR            0x01        /* 这里其实是全双工的方式,MOSI和MISO均有数据 */
#define SUNXI_OP_RD            0x02
#define SUNXI_OP_WR_RD        0x03        /* 这里其实是全双工的方式,MOSI和MISO均有数据 */
#define SUNXI_OP_HEAD        0xff        /* Only used in kernel space */

数据收发流程:

sunxi_spi_slave_test_submit()                                //spi-sunxi-slave-test.c
    sunxi_spi_transfer_one()                                //spi-ng/spi-sunxi.c
        sunxi_spi_xfer_slave()
            wait_for_completion_interruptible()                //一直等待数据传输完
            sunxi_spi_cpu_rx()                                //有数据后从寄存器中进行拷贝
    sunxi_spi_slave_test_complete()                            //spi-sunxi-slave-test.c (数据传输完,到达complete,此时可以将数据拷贝的驱动的其位置)
        sunxi_spi_slave_test_submit()                            //开始下一轮的数据获取

测试命令:

(在主设备中运行)半双工收发测试

./spidev_test -D /dev/spidev1.0 -s 10000000 -S 32 -I 1 -v -H -O

从设备中执行如下命令,查看收到的数据

hexdump -C /sys/devices/platform/soc/spi1/spi_master/spi1/spi1.0/sunxi-slave-test

4、CYCLIC_MODE模式

DMA环形设备 sunxi_spi_create_cyclic_dev,在选择这个模式时会自动创建该设备,对应的操作函数如下:

static const struct file_operations spi_cyclic_dev_fops =
{
    .open = spi_cyclic_dev_open,
    .release = spi_cyclic_dev_release,
    .unlocked_ioctl = spi_cyclic_dev_ioctl,
    .mmap = spi_cyclic_dev_mmap,
};

上层操作如下:

spi_cyclic_dev_ioctl()
    SPI_CYCLIC_DEV_START
    设置定时器的timeout
    创建spi device
    开始传输数据

sunxi_spi_slave_cyclic_dev_hrtimer_handler()       //定时执行函数
DMA设定传输的字节数为t->len,PAGE_SIZE,4096字节
dmaengine_tx_status       //查看当前传输的状态,state.residue返回剩余传输字节数
current_pos               //为当前传输了多少数据
rx_last_pos               //上次传输了多少数据

如果current_pos不等于rx_last_pos
【1】 如果current_pos大于rx_last_pos,说明有新的数据过来了
【2】 如果current_pos小于rx_last_pos,说明DMA传输完了,存在数据覆盖了

sspi->cyclic_dev->t->len - sspi->cyclic_dev->rx_last_pos  //上次剩余传输的字节数
sspi->cyclic_dev->buf_size - sspi->cyclic_dev->buf_pos    //缓冲区空间剩余的空间

sspi->cyclic_dev->buf_size       //这是用户设定的大小,测试程序设置的是64KB
sspi->cyclic_dev->user_buf       //这是分配的缓冲区
sspi->cyclic_dev->buf_pos        //这是缓冲区的当前位置

//更新位置
sspi->cyclic_dev->buf_pos += op_len;
sspi->cyclic_dev->rx_last_pos += op_len;

总的来说就是不停的接收数据,并把数据放到用户缓冲区中去。
hrtimer_forward_now()            //启动下一轮定时器

针对这个设备,全志提供了一个测试程序spislave-cyclic-test,对应源代码为:spislave-cyclic-test.c使用命令为(在从设备中运行10us的定时器):

./spislave-cyclic-test -D /dev/spi1_cyclic_dev -t 10000

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值