SPI驱动和协议(四)-SPI主端驱动2


linux内核版本:4.9

以xilinx的ultra++ scale zu3为例:


前言、 芯片手册:

xilinx UG1087,UG1085

https://www.xilinx.com/html_docs/registers/ug1087/ug1087-zynq-ultrascale-registers.html

1 SPI控制器特性

可以和各种外设进行数据交互,支持主、从、多主模式;有两个独立的SPI控制器 

全双工可同时收发、4根线(MISO;MOSI;CS;CLK;)、时钟可编成、APB可读写RX/TX FIFO


RX/TX FIFO 独立128-bytes deep(也就是128个8byte数据),通过映射的数据寄存器读写fifo,时钟这部分不太清楚;

RXFIFO:如果rxfifo满了,控制器在读取数据,会直接丢弃并overflow flag置1,软件写0清除

TXFIFO:如果txfifo满了,write会被忽略,数据暂时不会加入到txfifo;[TX_FIFO_full] 被置1,直到txfifo被读取到非满状态;

//If the TXFIFO overflows, the sticky [RX_OVERFLOW] bit is set = 1.  这句话不太理解

时钟:

The SPI controller receives two clock inputs from the PS clock subsystem  //接收两个时钟

in slave mode,the SCLK clock from the attached SPI master.  //从模式下,时钟来源于master主端

SPI_REF_CLK clock operates the controller and the baud-rate divider for the SCLK in master mode.  //CLK时钟操作:主模式下控制器的SCLK时钟分频;

LPD_LSBUS_CLK clock operates the APB slave interface for register access. //LPD_LSBUS_CLK时钟操作APB从接口以进行寄存器访问

主模式下,时钟信号SCLK  通过SPI_REF_CLK经过分频产生 [BAUD_RATE_DIV]; 最多三个外设,如果需要更多外设,需要配置[PERI_SEL] 位

从模式下,//TODO


SPI传输:

SCLK和MOSI被主端控制,数据被软件通过写寄存器传输到TXFIFO,控制器手动/自动开始排序,数据自动被传输到MOSI pin上

TXFIFO有数据,则传输持续;

软件通过读寄存器读取RXFIFO;





软件通过设置spi.Config [Man_start_en]选择手动传输方法
bit = 1.在此模式下,软件必须使用手动启动命令机制显式启动数据传输。 当[Man_start_en]位= 0时,
  当TXFIFO中有数据可用时,控制器硬件会自动启动数据传输。

软件通过向spi.Config [Man_start_com]位写入1来启动手动传输。
当软件写入1时,控制器硬件开始数据传输和
传输TXFIFO中存在的所有数据字节。 [Man_start_com]位是自清除的。
如果[Man_start_en] = 0,则忽略向该位写入1将0写入[Man_start_com]无论模式如何都无效。

从选择输入引脚必须与SCLK输入同步驱动。

控制器在SPI_Ref_Clk时钟域中运行。 输入信号在SPI_Ref_Clk域中进行同步和分析







一、设备树配置

xilinx/zynqmp-zcu102.dts
spi0: spi@ff040000 {
    compatible = "cdns,spi-r1p6";    
    status = "disabled";   
    interrupt-parent = <&gic>;    
    interrupts = <0 19 4>;    
    reg = <0x0 0xff040000 0x1000>;   
    clock-names = "ref_clk", "pclk";      
    #address-cells = <1>; 
    #size-cells = <0>;        
    power-domains = <&pd_spi0>; 
    status = "okay";
    num-cs=<0x1>;
    bifdev@0x00 {
        compatible = "horizon,spidev_bif";
        reg = <0>;
        spi-tx-bus-width = <0x1>;
        spi-rx-bus-width = <0x1>;
        spi-max-frequency = <1000000>;                                                                                                                                                                        
        };

};   


二、驱动注册函数

/xilinx-kernel/drivers/spi/spi-cadence.c
static const struct of_device_id cdns_spi_of_match[] = { 
    { .compatible = "xlnx,zynq-spi-r1p6" },
    { .compatible = "cdns,spi-r1p6" },
    { /* end of table */ }
};
MODULE_DEVICE_TABLE(of, cdns_spi_of_match);

/* cdns_spi_driver - This structure defines the SPI subsystem platform driver */
static struct platform_driver cdns_spi_driver = { 
    .probe  = cdns_spi_probe,                                                                                                                                                                                 
    .remove = cdns_spi_remove,
    .driver = { 
        .name = CDNS_SPI_NAME,
        .of_match_table = cdns_spi_of_match,
        .pm = &cdns_spi_dev_pm_ops,
    },  
};

module_platform_driver(cdns_spi_driver);

三、probe入口

3.1 初始化结构体部分

从probe函数可以获取到当前spi-contrl的struct platform_device *pdev;

struct spi_master * master = spi_alloc_master(&pdev->dev, sizeof(*xspi));  
struct cdns_spi *xspi = spi_master_get_devdata(master);
master->dev.of_node = pdev->dev.of_node;
platform_set_drvdata(pdev, master);

这部分可以看出:

spi_alloc_master //这个函数会分配 struct spi_master + sizeof(*xspi)的内存,

master->dev{ //device_initialize(&master->dev);初始化dev结构体

    parent = pdev;//父设备是platform_device *pdev->dev

    driver_data = sturct cdns_spi; //指向分配的驱动私有结构体

}

platform_device *pdev->dev->driver_data = master;//将master挂载在struct platform_device *pdev->dev->river_data上


3.2 根据设备树初始化驱动信息

//获取region内存区域
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
xspi->regs = devm_ioremap_resource(&pdev->dev, res);
//初始化时钟
xspi->pclk = devm_clk_get(&pdev->dev, "pclk");
xspi->ref_clk = devm_clk_get(&pdev->dev, "ref_clk");
ret = clk_prepare_enable(xspi->pclk);
ret = clk_prepare_enable(xspi->ref_clk);

//不知道干啥的
pm_runtime_enable(&pdev->dev);
pm_runtime_use_autosuspend(&pdev->dev);
  pm_runtime_set_autosuspend_delay(&pdev->dev, SPI_AUTOSUSPEND_TIMEOUT);
pm_runtime_set_active(&pdev->dev);

//加载设备树中信息
ret = of_property_read_u32(pdev->dev.of_node, "num-cs", &num_cs);
master->num_chipselect = num_cs;
 ret = of_property_read_u32(pdev->dev.of_node, "is-decoded-cs",
                   &xspi->is_decoded_cs);

3.3 硬件初始化

//硬件驱动初始化
/* SPI controller initializations */
cdns_spi_init_hw(xspi);
//不知道干啥的
pm_runtime_mark_last_busy(&pdev->dev);
pm_runtime_put_autosuspend(&pdev->dev);
//注册中断
irq = platform_get_irq(pdev, 0);
ret = devm_request_irq(&pdev->dev, irq, cdns_spi_irq,
                   0, pdev->name, master);
//master 结构体填充
master->prepare_transfer_hardware = cdns_prepare_transfer_hardware;
master->prepare_message = cdns_prepare_message;
master->transfer_one = cdns_transfer_one;
master->unprepare_transfer_hardware = cdns_unprepare_transfer_hardware;
master->set_cs = cdns_spi_chipselect;
master->auto_runtime_pm = true;
master->mode_bits = SPI_CPOL | SPI_CPHA;
/* Set to default valid value */
master->max_speed_hz = clk_get_rate(xspi->ref_clk) / 4;
    xspi->speed_hz = master->max_speed_hz;                                                                                                                                                                                                             master->bits_per_word_mask = SPI_BPW_MASK(8);

3.4 注册spi驱动

ret = spi_register_master(master);//注册spi控制器






猜你喜欢

转载自blog.csdn.net/sven0223/article/details/81020590