linux spi架构

spi简介

串行外设接口(Serial Peripheral Interface)是一种同步外设接口,用于嵌入式系统中的微控制器和外设之间的通信。

spi四种模式

        SPI的时钟极性和相位的配置通常称为spi模式

时钟极性 CKP/Clock Polarity

        时钟极性通常写为CKP或CPOL,CKP可以配置为1或0,用于设置时钟的默认状态(IDLE)设置为高或低。CKP = 0:时钟空闲IDLE为低电平 0;CKP = 1:时钟空闲IDLE为高电平1。

时钟相位 CKE /Clock Phase (Edge)

        时钟相位通常写为CKE或CPHA。CKE = 0:在时钟信号SCK的第一个跳变沿采样;CKE = 1:在时钟信号SCK的第二个跳变沿采样。

spi时序图

spi标准

标准SPI,有4根信号线,收发数据线各一根,分别为CLK、CS、MOSI和MISO。数据线工作在全双工

扩展SPI,为了提升速率增加spi数据线,针对SPI Flash使用,半双工模式。同时,SPI协议还增加了SDR模式(单倍速率Single Data Rate)和DDR模式(双倍速率Double Data Rate)。

协议数据线数量及功能通讯方式
Single SPI(标准SPI)1根发送,1根接收全双工
Dual SPI(双线SPI)收发共用2根数据线半双工
Quad SPI(四线SPI)收发共用4根数据线半双工
Octal SPI(八线SPI)收发共用8根数据线半双工

spi控制器注册

1、控制器数据结构

struct spi_controller {

    struct device   dev;

    struct list_head list;

    /* other than negative (== assign one dynamically), bus_num is fully

     * board-specific.  usually that simplifies to being SOC-specific.

     * example:  one SOC has three SPI controllers, numbered 0..2,

     * and one board's schematics might show it using SPI-2.  software

     * would normally use bus_num=2 for that controller.

     */

    s16         bus_num;

    /* chipselects will be integral to many controllers; some others

     * might use board-specific GPIOs.

     */

    u16         num_chipselect;

    /* some SPI controllers pose alignment requirements on DMAable

     * buffers; let protocol drivers know about these requirements.

     */

    u16         dma_alignment;

    /* spi_device.mode flags understood by this controller driver */

    u32         mode_bits;

    /* spi_device.mode flags override flags for this controller */

    u32         buswidth_override_bits;

    /* bitmask of supported bits_per_word for transfers */

    u32         bits_per_word_mask;

#define SPI_BPW_MASK(bits) BIT((bits) - 1)

#define SPI_BPW_RANGE_MASK(min, max) GENMASK((max) - 1, (min) - 1)

    /* limits on transfer speed */

    u32         min_speed_hz;

    u32         max_speed_hz;

    /* other constraints relevant to this driver */

    u16         flags;

#define SPI_CONTROLLER_HALF_DUPLEX  BIT(0)  /* can't do full duplex */

#define SPI_CONTROLLER_NO_RX        BIT(1)  /* can't do buffer read */

#define SPI_CONTROLLER_NO_TX        BIT(2)  /* can't do buffer write */

#define SPI_CONTROLLER_MUST_RX      BIT(3)  /* requires rx */

#define SPI_CONTROLLER_MUST_TX      BIT(4)  /* requires tx */

#define SPI_MASTER_GPIO_SS      BIT(5)  /* GPIO CS must select slave */

    /* flag indicating this is a non-devres managed controller */

    bool            devm_allocated;

    /* flag indicating this is an SPI slave controller */

    bool            slave;

    /*

     * on some hardware transfer / message size may be constrained

     * the limit may depend on device transfer settings

     */

    size_t (*max_transfer_size)(struct spi_device *spi);

    size_t (*max_message_size)(struct spi_device *spi);

    /* I/O mutex */

    struct mutex        io_mutex;

    /* lock and mutex for SPI bus locking */

    spinlock_t      bus_lock_spinlock;

    struct mutex        bus_lock_mutex;

    /* flag indicating that the SPI bus is locked for exclusive use */

    bool            bus_lock_flag;

    /* Setup mode and clock, etc (spi driver may call many times).

     *

     * IMPORTANT:  this may be called when transfers to another

     * device are active.  DO NOT UPDATE SHARED REGISTERS in ways

     * which could break those transfers.

     */

    int         (*setup)(struct spi_device *spi);

    /*

     * set_cs_timing() method is for SPI controllers that supports

     * configuring CS timing.

     *

     * This hook allows SPI client drivers to request SPI controllers

     * to configure specific CS timing through spi_set_cs_timing() after

     * spi_setup().

     */

    int (*set_cs_timing)(struct spi_device *spi, struct spi_delay *setup,

                 struct spi_delay *hold, struct spi_delay *inactive);

    /* bidirectional bulk transfers

     *

     * + The transfer() method may not sleep; its main role is

     *   just to add the message to the queue.

     * + For now there's no remove-from-queue operation, or

     *   any other request management

     * + To a given spi_device, message queueing is pure fifo

     *

     * + The controller's main job is to process its message queue,

     *   selecting a chip (for masters), then transferring data

     * + If there are multiple spi_device children, the i/o queue

     *   arbitration algorithm is unspecified (round robin, fifo,

     *   priority, reservations, preemption, etc)

     *

     * + Chipselect stays active during the entire message

     *   (unless modified by spi_transfer.cs_change != 0).

     * + The message transfers use clock and SPI mode parameters

     *   previously established by setup() for this device

     */

    int         (*transfer)(struct spi_device *spi,

                        struct spi_message *mesg);

    /* called on release() to free memory provided by spi_controller */

    void            (*cleanup)(struct spi_device *spi);

    /*

     * Used to enable core support for DMA handling, if can_dma()

     * exists and returns true then the transfer will be mapped

     * prior to transfer_one() being called.  The driver should

     * not modify or store xfer and dma_tx and dma_rx must be set

     * while the device is prepared.

     */

    bool            (*can_dma)(struct spi_controller *ctlr,

                       struct spi_device *spi,

                       struct spi_transfer *xfer);

    /*

     * These hooks are for drivers that want to use the generic

     * controller transfer queueing mechanism. If these are used, the

     * transfer() function above must NOT be specified by the driver.

     * Over time we expect SPI drivers to be phased over to this API.

     */

    bool                queued;

    struct kthread_worker       *kworker;

    struct kthread_work     pump_messages;

    spinlock_t          queue_lock;

    struct list_head        queue;

    struct spi_message      *cur_msg;

    bool                idling;

    bool                busy;

    bool                running;

    bool                rt;

    bool                auto_runtime_pm;

    bool                            cur_msg_prepared;

    bool                cur_msg_mapped;

    bool                last_cs_enable;

    bool                last_cs_mode_high;

    bool                            fallback;

    struct completion               xfer_completion;

    size_t              max_dma_len;

    int (*prepare_transfer_hardware)(struct spi_controller *ctlr);

    int (*transfer_one_message)(struct spi_controller *ctlr,

                    struct spi_message *mesg);

    int (*unprepare_transfer_hardware)(struct spi_controller *ctlr);

    int (*prepare_message)(struct spi_controller *ctlr,

                   struct spi_message *message);

    int (*unprepare_message)(struct spi_controller *ctlr,

                 struct spi_message *message);

    int (*slave_abort)(struct spi_controller *ctlr);

    /*

     * These hooks are for drivers that use a generic implementation

     * of transfer_one_message() provied by the core.

     */

    void (*set_cs)(struct spi_device *spi, bool enable);

    int (*transfer_one)(struct spi_controller *ctlr, struct spi_device *spi,

                struct spi_transfer *transfer);

    void (*handle_err)(struct spi_controller *ctlr,

               struct spi_message *message);

    /* Optimized handlers for SPI memory-like operations. */

    const struct spi_controller_mem_ops *mem_ops;

    /* CS delays */

    struct spi_delay    cs_setup;

    struct spi_delay    cs_hold;

    struct spi_delay    cs_inactive;

    /* gpio chip select */

    int         *cs_gpios;

    struct gpio_desc    **cs_gpiods;

    bool            use_gpio_descriptors;

    u8          unused_native_cs;

    u8          max_native_cs;

    /* statistics */

    struct spi_statistics   statistics;

    /* DMA channels for use with core dmaengine helpers */

    struct dma_chan     *dma_tx;

    struct dma_chan     *dma_rx;

    /* dummy data for full duplex devices */

    void            *dummy_rx;

    void            *dummy_tx;

    int (*fw_translate_cs)(struct spi_controller *ctlr, unsigned cs);

    /*

     * Driver sets this field to indicate it is able to snapshot SPI

     * transfers (needed e.g. for reading the time of POSIX clocks)

     */

    bool            ptp_sts_supported;

    /* Interrupt enable state during PTP system timestamping */

    unsigned long       irq_flags;

};

2、控制器注册

设备树节点

    main_spi5: spi@2150000 {
        compatible = "ti,am654-mcspi","ti,omap4-mcspi";
        reg = <0x0 0x2150000 0x0 0x400>;
        interrupts = <GIC_SPI 189 IRQ_TYPE_LEVEL_HIGH>;
        clocks = <&k3_clks 271 1>;
        /* TODO:  */
        power-domains = <&k3_pds 271 TI_SCI_PD_EXCLUSIVE>;
        #address-cells = <1>;
        #size-cells = <0>;
    };

......

&main_spi5 {
    pinctrl-names = "default";
    pinctrl-0 = <&spi5_pins_default>;
    status="okay";
    //ti,pindir-d0-out-d1-in;
    spidev@0 {
        spi-max-frequency = <24000000>;
        reg = <0>;
        compatible = "linux,spidev";
    };
};

驱动probe过程

1、分配一个struct spi_controller数据结构

    if (of_property_read_bool(node, "spi-slave"))

        master = spi_alloc_slave(&pdev->dev, sizeof(*mcspi));

    else

        master = spi_alloc_master(&pdev->dev, sizeof(*mcspi));

    if (!master)

        return -ENOMEM;

2、填充spi_controller结构体,master填入pdev-dev.driver_data

    /* the spi->mode bits understood by this driver: */

    master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;

    master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32);

    master->setup = omap2_mcspi_setup;

    master->auto_runtime_pm = true;

    master->prepare_message = omap2_mcspi_prepare_message;

    master->can_dma = omap2_mcspi_can_dma;

    master->transfer_one = omap2_mcspi_transfer_one;

    master->set_cs = omap2_mcspi_set_cs;

    master->cleanup = omap2_mcspi_cleanup;

    master->slave_abort = omap2_mcspi_slave_abort;

    master->dev.of_node = node;

    master->max_speed_hz = OMAP2_MCSPI_MAX_FREQ;

    master->min_speed_hz = OMAP2_MCSPI_MAX_FREQ >> 15;

    master->use_gpio_descriptors = true;

    platform_set_drvdata(pdev, master);

3、填充私有的spi结构,包括寄存器资源,中断资源,dma资源

4、设置一些基本的寄存器

5、注册spi控制器

status = devm_spi_register_controller(&pdev->dev, master);

devm_spi_register_controller注册过程

1、检查spi_controller是否赋值了ops

2、分配bus_num,取别名做bus_num,设置控制器设备名"spi+bus_num"

3、master spi控制器如果设备树配了gpio做片选脚则给相关变量赋值

4、注册控制器设备

5、初始化 线程工作器struct kthread_worker和线程work

6、扫描board_list注册spi设备

list_for_each_entry(bi, &board_list, list)

        spi_match_controller_to_boardinfo(ctlr, &bi->board_info);

7、注册设备树中spi设备

of_register_spi_device:a、spi_alloc_device,spi_device数据结构填充基本变量,b、设置spi_device modalias,c、设备树节点获取spi的mode,片选,max_speed_hz等属性赋值spi_device相关变量,d、spi->dev.of_node,e、spi_add_device(spi); /* Register the new device */ 

int spi_add_device(struct spi_device *spi):a、spi_device设备名设置为控制名.+片选编号,b、赋值片选引脚cs_gpiod/cs_gpio,c、spi_setup,d、注册spi设备

    /* Register devices from the device tree and ACPI */

    of_register_spi_devices(ctlr);

spi控制器通用设备驱动

驱动代码路径:drivers/spi/spidev.c

soc作为一个主设备,访问spi设备的通用控制方法,提供设备节点供用户层通信。

1、设备树

&main_spi5 {
    pinctrl-names = "default";
    pinctrl-0 = <&spi5_pins_default>;
    status="okay";
    //ti,pindir-d0-out-d1-in;
    spidev@0 {
        spi-max-frequency = <24000000>;
        reg = <0>;
        compatible = "linux,spidev";
    };
};

2、spidev驱动probe函数创建字符设备

        spidev->devt = MKDEV(SPIDEV_MAJOR, minor);

        dev = device_create(spidev_class, &spi->dev, spidev->devt,

                    spidev, "spidev%d.%d",

                    spi->master->bus_num, spi->chip_select);

3、spi驱动函数接口

static const struct file_operations spidev_fops = {

    .owner =    THIS_MODULE,

    /* REVISIT switch to aio primitives, so that userspace

     * gets more complete API coverage.  It'll simplify things

     * too, except for the locking.

     */

    .write =    spidev_write,

    .read =     spidev_read,

    .unlocked_ioctl = spidev_ioctl,

    .compat_ioctl = spidev_compat_ioctl,

    .open =     spidev_open,

    .release =  spidev_release,

    .llseek =   no_llseek,

};

spidev_open:open函数根据device设备号从spi设备链表device_list中找到对应的spi设备,并保存到变量filp->private_data

    list_for_each_entry(spidev, &device_list, device_entry) {

        if (spidev->devt == inode->i_rdev) {

            status = 0;

            break;

        }

    }

......

filp->private_data = spidev;

spi控制器做从设备驱动

soc作为一个从设备,由外部的主设备访问。

驱动编写方法

1、内核配置CONFIG_SPI_SLAVE需要打开

2、根据不同soc要求,设备树节点spi节点加slave字符串属性或者compatible是单独的slave驱动

&mcu_spi1 {
    status = "okay";
    pinctrl-names = "default";
    pinctrl-0 = <&mcu_spi1_pins_default>;
    spi-slave;
    dmas = <&mcu_udmap 0xf200>, <&mcu_udmap 0x7200>;
    dma-names = "tx0", "rx0";

    slave@0 {
        spi-max-frequency = <24000000>;
        reg = <0>;
        compatible = "rohm,dh2228fv";
    };
};

spi@3230000 {

    status = "okay";

    compatible = "nvidia,tegra186-spi-slave";

    spi@0 {

        compatible = "tegra-spidev";

        reg = <0x0>;

        spi-max-frequency = <33000000>;

        controller-data {

            nvidia,enable-hw-based-cs;

            nvidia,rx-clk-tap-delay = <0x11>;

        };

    };

};

3、设备驱动与spi主设备驱动编写方法一样,通用设备驱动drivers/spi/spidev.c,主从设备通用

spi内核设备驱动与设备注册

设备注册时需要的信息

struct spi_board_info {

    /* the device name and module name are coupled, like platform_bus;

     * "modalias" is normally the driver name.

     *

     * platform_data goes to spi_device.dev.platform_data,

     * controller_data goes to spi_device.controller_data,

     * device properties are copied and attached to spi_device,

     * irq is copied too

     */

    char        modalias[SPI_NAME_SIZE];

    const void  *platform_data;

    const struct property_entry *properties;

    void        *controller_data;

    int     irq;

    /* slower signaling on noisy or low voltage boards */

    u32     max_speed_hz;


 

    /* bus_num is board specific and matches the bus_num of some

     * spi_controller that will probably be registered later.

     *

     * chip_select reflects how this chip is wired to that master;

     * it's less than num_chipselect.

     */

    u16     bus_num;

    u16     chip_select;

    /* mode becomes spi_device.mode, and is essential for chips

     * where the default of SPI_CS_HIGH = 0 is wrong.

     */

    u32     mode;

    /* ... may need additional spi_device chip config data here.

     * avoid stuff protocol drivers can set; but include stuff

     * needed to behave without being bound to a driver:

     *  - quirks like clock rate mattering when not selected

     */

};

设备注册方法

struct spi_device {

    struct device       dev;

    struct spi_controller   *controller;

    struct spi_controller   *master;    /* compatibility layer */

    u32         max_speed_hz;

    u8          chip_select;

    u8          bits_per_word;

    bool            rt;

    u32         mode;

#define SPI_CPHA    0x01            /* clock phase */

#define SPI_CPOL    0x02            /* clock polarity */

#define SPI_MODE_0  (0|0)           /* (original MicroWire) */

#define SPI_MODE_1  (0|SPI_CPHA)

#define SPI_MODE_2  (SPI_CPOL|0)

#define SPI_MODE_3  (SPI_CPOL|SPI_CPHA)

#define SPI_CS_HIGH 0x04            /* chipselect active high? */

#define SPI_LSB_FIRST   0x08            /* per-word bits-on-wire */

#define SPI_3WIRE   0x10            /* SI/SO signals shared */

#define SPI_LOOP    0x20            /* loopback mode */

#define SPI_NO_CS   0x40            /* 1 dev/bus, no chipselect */

#define SPI_READY   0x80            /* slave pulls low to pause */

#define SPI_TX_DUAL 0x100           /* transmit with 2 wires */

#define SPI_TX_QUAD 0x200           /* transmit with 4 wires */

#define SPI_RX_DUAL 0x400           /* receive with 2 wires */

#define SPI_RX_QUAD 0x800           /* receive with 4 wires */

#define SPI_CS_WORD 0x1000          /* toggle cs after each word */

#define SPI_TX_OCTAL    0x2000          /* transmit with 8 wires */

#define SPI_RX_OCTAL    0x4000          /* receive with 8 wires */

#define SPI_3WIRE_HIZ   0x8000          /* high impedance turnaround */

    int         irq;

    void            *controller_state;

    void            *controller_data;

    char            modalias[SPI_NAME_SIZE];

    const char      *driver_override;

    int         cs_gpio;    /* LEGACY: chip select gpio */

    struct gpio_desc    *cs_gpiod;  /* chip select gpio desc */

    struct spi_delay    word_delay; /* inter-word delay */

    /* the statistics */

    struct spi_statistics   statistics;

    /*

     * likely need more hooks for more protocol options affecting how

     * the controller talks to each chip, like:

     *  - memory packing (12 bit samples into low bits, others zeroed)

     *  - priority

     *  - chipselect delays

     *  - ...

     */

};

1、填充struct spi_board_info结构体,调用spi_register_board_info,spi控制器注册时会调用spi_match_controller_to_boardinfo注册spi设备

int spi_register_board_info(struct spi_board_info const *info, unsigned n)

2、设备树节点方式

&mcu_spi0 {
    pinctrl-names = "default";
    pinctrl-0 = <&mcu_spi0_pins_default &sja1124_pins_default>;
    ti,pindir-d0-out-d1-in = <1>;
    status = "okay";
    sja1124: spi-lin@0 {
        reg = <0x0>;
        #address-cells = <1>;
        #size-cells = <0>;
        compatible = "nxp,sja1124";
        /* 12 MHz */
        spi-max-frequency = <2000000>;
        /* Sample data on trailing clock edge */
        spi-cpha;
        int-gpio = <&main_gpio0 20 GPIO_ACTIVE_LOW>;
        channels = <1 1 0 0>;
        pll_mul = <5>;/* multiplication factor = 15. */
        /* baudrate = PLLCLK / (16 * IBR + FBR). */
        fbrs = <11 11 0 0>;/* */
        ibrs = <97 97 0 0>;
    };

};

3、直接调用spi_new_device

spi设备注册接口

struct spi_device *spi_new_device(struct spi_controller *ctlr,

                  struct spi_board_info *chip)

驱动注册方法

soc作为一个主设备,对特定外部设备进行控制的设备驱动程序

驱动编写方法

1、直接调用module_spi_driver,不用写module_init

static struct spi_driver sja1105_driver = {

    .driver = {

        .name  = "sja1105",

        .owner = THIS_MODULE,

        .of_match_table = of_match_ptr(sja1105_dt_ids),

    },

    .probe  = sja1105_probe,

    .remove = sja1105_remove,

};

module_spi_driver(sja1105_driver);

2、module_init里头手动调用spi_register_driver

extern int __spi_register_driver(struct module *owner, struct spi_driver *sdrv);

内核通信接口

驱动中拿到的参数

驱动注册接口driver.probe=spi_drv_probe,spi_drv_probe会调用驱动的probe传递spi_device参数。

/**

 * __spi_register_driver - register a SPI driver

 * @owner: owner module of the driver to register

 * @sdrv: the driver to register

 * Context: can sleep

 *

 * Return: zero on success, else a negative error code.

 */

int __spi_register_driver(struct module *owner, struct spi_driver *sdrv)

{

    sdrv->driver.owner = owner;

    sdrv->driver.bus = &spi_bus_type;

    sdrv->driver.probe = spi_drv_probe;

    sdrv->driver.remove = spi_drv_remove;

    if (sdrv->shutdown)

        sdrv->driver.shutdown = spi_drv_shutdown;

    return driver_register(&sdrv->driver);

}

EXPORT_SYMBOL_GPL(__spi_register_driver);

static int *_probe(struct spi_device *spi)

通信接口讲解
涉及的数据结构

struct spi_message {

    struct list_head    transfers;

    struct spi_device   *spi;

    unsigned        is_dma_mapped:1;

    /* REVISIT:  we might want a flag affecting the behavior of the

     * last transfer ... allowing things like "read 16 bit length L"

     * immediately followed by "read L bytes".  Basically imposing

     * a specific message scheduling algorithm.

     *

     * Some controller drivers (message-at-a-time queue processing)

     * could provide that as their default scheduling algorithm.  But

     * others (with multi-message pipelines) could need a flag to

     * tell them about such special cases.

     */

    /* completion is reported through a callback */

    void            (*complete)(void *context);

    void            *context;

    unsigned        frame_length;

    unsigned        actual_length;

    int         status;

    /* for optional use by whatever driver currently owns the

     * spi_message ...  between calls to spi_async and then later

     * complete(), that's the spi_controller controller driver.

     */

    struct list_head    queue;

    void            *state;

    /* list of spi_res reources when the spi message is processed */

    struct list_head        resources;

};

struct spi_transfer {

    /* it's ok if tx_buf == rx_buf (right?)

     * for MicroWire, one buffer must be null

     * buffers must work with dma_*map_single() calls, unless

     *   spi_message.is_dma_mapped reports a pre-existing mapping

     */

    const void  *tx_buf;

    void        *rx_buf;

    unsigned    len;

    dma_addr_t  tx_dma;

    dma_addr_t  rx_dma;

    struct sg_table tx_sg;

    struct sg_table rx_sg;

    unsigned    cs_change:1;

    unsigned    tx_nbits:3;

    unsigned    rx_nbits:3;

#define SPI_NBITS_SINGLE    0x01 /* 1bit transfer */

#define SPI_NBITS_DUAL      0x02 /* 2bits transfer */

#define SPI_NBITS_QUAD      0x04 /* 4bits transfer */

    u8      bits_per_word;

    u16     delay_usecs;

    struct spi_delay    delay;

    struct spi_delay    cs_change_delay;

    struct spi_delay    word_delay;

    u32     speed_hz;

    u32     effective_speed_hz;

    unsigned int    ptp_sts_word_pre;

    unsigned int    ptp_sts_word_post;

    struct ptp_system_timestamp *ptp_sts;

    bool        timestamped;

    struct list_head transfer_list;

#define SPI_TRANS_FAIL_NO_START BIT(0)

    u16     error;

};

struct spi_ioc_transfer {

    __u64       tx_buf;

    __u64       rx_buf;

    __u32       len;

    __u32       speed_hz;

    __u16       delay_usecs;

    __u8        bits_per_word;

    __u8        cs_change;

    __u8        tx_nbits;

    __u8        rx_nbits;

    __u8        word_delay_usecs;

    __u8        pad;

    /* If the contents of 'struct spi_ioc_transfer' ever change

     * incompatibly, then the ioctl number (currently 0) must change;

     * ioctls with constant size fields get a bit more in the way of

     * error checking than ones (like this) where that field varies.

     *

     * NOTE: struct layout is the same in 64bit and 32bit userspace.

     */

};

主要收发函数接口

1.同步收发接口,要等数据传输完成才返回,提供上层应用的通用spi驱动使用的是该接口,spi_sync_transfer,spi_read/spi_write也是调用spi_sync_transfer,spi_sync_transfer调用过程是创建spi_message变量,然后把spi_transfer都挂载到spi_message上,再把这个变量传给spi_sync进行收发。

2.异步收发接口,spi_async,异步传输,不等传输完成就返回。

static inline int

spi_sync_transfer(struct spi_device *spi, struct spi_transfer *xfers,

    unsigned int num_xfers)

{

    struct spi_message msg;

    spi_message_init_with_transfers(&msg, xfers, num_xfers);

    return spi_sync(spi, &msg);

}

static inline void

spi_message_init_with_transfers(struct spi_message *m,

struct spi_transfer *xfers, unsigned int num_xfers)

{

    unsigned int i;

    spi_message_init(m);

    for (i = 0; i < num_xfers; ++i)

        spi_message_add_tail(&xfers[i], m);

}

/**

 * spi_write - SPI synchronous write

 * @spi: device to which data will be written

 * @buf: data buffer

 * @len: data buffer size

 * Context: can sleep

 *

 * This function writes the buffer @buf.

 * Callable only from contexts that can sleep.

 *

 * Return: zero on success, else a negative error code.

 */

static inline int

spi_write(struct spi_device *spi, const void *buf, size_t len)

{

    struct spi_transfer t = {

            .tx_buf     = buf,

            .len        = len,

        };

    return spi_sync_transfer(spi, &t, 1);

}

/**

 * spi_read - SPI synchronous read

 * @spi: device from which data will be read

 * @buf: data buffer

 * @len: data buffer size

 * Context: can sleep

 *

 * This function reads the buffer @buf.

 * Callable only from contexts that can sleep.

 *

 * Return: zero on success, else a negative error code.

 */

static inline int

spi_read(struct spi_device *spi, void *buf, size_t len)

{

    struct spi_transfer t = {

            .rx_buf     = buf,

            .len        = len,

        };

    return spi_sync_transfer(spi, &t, 1);

}

int spi_async(struct spi_device *spi, struct spi_message *message)

{

    struct spi_controller *ctlr = spi->controller;

    int ret;

    unsigned long flags;

    ret = __spi_validate(spi, message);

    if (ret != 0)

        return ret;

    spin_lock_irqsave(&ctlr->bus_lock_spinlock, flags);

    if (ctlr->bus_lock_flag)

        ret = -EBUSY;

    else

        ret = __spi_async(spi, message);

    spin_unlock_irqrestore(&ctlr->bus_lock_spinlock, flags);

    return ret;

}

EXPORT_SYMBOL_GPL(spi_async);

内核spi通信使用步骤

1、初始化struct spi_message数据结构,初始化transfers, resources链表,

2、将spi_transfer加入spi_message链表

    for (i = 0; i < num_xfers; ++i)

        spi_message_add_tail(&xfers[i], m);

3、调用status = spi_sync(spi, &message),调用同一个spi控制器有互斥锁,所以调用spi_sync是串行执行的,并且数据传输完成或错误才会返回,所以只有等待上次spi传输完成才会进行下一次,有阻塞。

int spi_sync(struct spi_device *spi, struct spi_message *message)

{

    int ret;

    mutex_lock(&spi->controller->bus_lock_mutex);

    ret = __spi_sync(spi, message);

    mutex_unlock(&spi->controller->bus_lock_mutex);

    return ret;

}

EXPORT_SYMBOL_GPL(spi_sync);

收发过程解析

static int __spi_sync(struct spi_device *spi, struct spi_message *message)

这个函数处理过程如下:

1、判断message有效性,填充message数据成员,如completion变量context,complete函数,spi_device设备

    status = __spi_validate(spi, message);

    if (status != 0)

        return status;

    message->complete = spi_complete;

    message->context = &done;

    message->spi = spi;

2、把message加入到spi控制器消息队列

status = __spi_queued_transfer(spi, message, false);

//里头会调用list_add_tail(&msg->queue, &ctlr->queue);

3、开始收发数据

//里头会调用ret = ctlr->transfer_one_message(ctlr, ctlr->cur_msg);

__spi_pump_messages(ctlr, false);

4、等待收发数据完成,或者错误返回

wait_for_completion(&done);

static void __spi_pump_messages(struct spi_controller *ctlr, bool in_kthread)

函数处理过程如下:

1、判断是否还在收发一个message的过程,这里是直接返回,这里也是有点问题的,返回后一直在等wait_for_completion(&done),这里就退不出来了。

    /* Make sure we are not already running a message */

    if (ctlr->cur_msg) {

        spin_unlock_irqrestore(&ctlr->queue_lock, flags);

        return;

    }

2、如果就是处于已经发完了然后在处理发完后的一些处理过程那么这时候kthread_queue_work延迟一会等调度再次调到__spi_pump_messages,这里没有用kthread_queue_delayed_work这个再加一点delay

    /* If another context is idling the device then defer */

    if (ctlr->idling) {

        kthread_queue_work(&ctlr->kworker, &ctlr->pump_messages);

        spin_unlock_irqrestore(&ctlr->queue_lock, flags);

        return;

    }

3、如果控制器的消息队列已经空了,或者控制器不再需要收发了,已经不是busy了那就不需要处理了,如果是busy就需要去busy处理,这个处理需要在kthread中,所以不是通过kthread_worker调用的话,这里会调用kthread_queue_work,等待工作线程调度起来处理

    /* Check if the queue is idle */

    if (list_empty(&ctlr->queue) || !ctlr->running) {

        if (!ctlr->busy) {

            spin_unlock_irqrestore(&ctlr->queue_lock, flags);

            return;

        }

        /* Only do teardown in the thread */

        if (!in_kthread) {

            kthread_queue_work(&ctlr->kworker,

                       &ctlr->pump_messages);

            spin_unlock_irqrestore(&ctlr->queue_lock, flags);

            return;

        }

        ctlr->busy = false;

        ctlr->idling = true;

......

4、从控制器消息队列中取出message

    /* Extract head of queue */

    ctlr->cur_msg =

        list_first_entry(&ctlr->queue, struct spi_message, queue);

    list_del_init(&ctlr->cur_msg->queue);

    if (ctlr->busy)

        was_busy = true;

    else

        ctlr->busy = true;

5、散列表方式进行内存映射,用于dma传输

   ret = spi_map_msg(ctlr, ctlr->cur_msg);

    if (ret) {

        ctlr->cur_msg->status = ret;

        spi_finalize_current_message(ctlr);

        goto out;

    }

6、开始收发

ret = ctlr->transfer_one_message(ctlr, ctlr->cur_msg);

static int spi_transfer_one_message(struct spi_controller *ctlr, struct spi_message *msg)

1、片选使能脚置为有效

spi_set_cs(msg->spi, true);//gpio脚设备树配置了且SPI_NO_CS模式则会去拉脚,否则调用控制器的set_cs函数

2、从msg的transfers链表中取出transfer

list_for_each_entry(xfer, &msg->transfers, transfer_list) {

......

3、初始化xfer_completion,调用spi控制器的transfer_one函数(实际控制器的收发设置函数),然后等待收发完成

        if (xfer->tx_buf || xfer->rx_buf) {

            reinit_completion(&ctlr->xfer_completion);

            ret = ctlr->transfer_one(ctlr, msg->spi, xfer);

......

ret = spi_transfer_wait(ctlr, msg, xfer);

4、完成后无效片选脚,处理返回状态,调用spi_finalize_current_message,这里会unmap映射用于dma的内存,ctlr->cur_msg 置为NULL,kthread_queue_work(&ctlr->kworker, &ctlr->pump_messages)用于发起下一个message处理。

    if (ret != 0 || !keep_cs)

        spi_set_cs(msg->spi, false);

    if (msg->status == -EINPROGRESS)

        msg->status = ret;

    if (msg->status && ctlr->handle_err)

        ctlr->handle_err(ctlr, msg);

    spi_finalize_current_message(ctlr);

ctlr->transfer_one最终spi控制器的收发函数,对spi控制器进行配置,dma配置,中断配置,收发数据处理,最终状态处理,下面以rockchip_spi_transfer_one来说明。

static int rockchip_spi_transfer_one(struct spi_controller *ctlr,struct spi_device *spi,struct spi_transfer *xfer)

1、传输长度为0,直接结束

    /* Zero length transfers won't trigger an interrupt on completion */

    if (!xfer->len) {

        spi_finalize_current_transfer(ctlr);

        return 1;

    }

2、检查buf,len,有错返回errono, transfer_one_message检查返回值<0也会直接退出,中止收发数据

if (!xfer->tx_buf && !xfer->rx_buf) {

        dev_err(rs->dev, "No buffer for transfer\n");

        return -EINVAL;

    }

    if (xfer->len > ROCKCHIP_SPI_MAX_TRANLEN) {

        dev_err(rs->dev, "Transfer is too long (%d)\n", xfer->len);

        return -EINVAL;

    }

3、计算是否需要dma,can_dma?然后进行spi控制器配置和配置触发dma请求的收发fifo数据深度

    rs->n_bytes = xfer->bits_per_word <= 8 ? 1 : 2;

    rs->xfer = xfer;

    use_dma = ctlr->can_dma ? ctlr->can_dma(ctlr, spi, xfer) : false;

    rockchip_spi_config(rs, spi, xfer, use_dma, ctlr->slave);

4、如果需要dma,配置dma,配置收dma,配置发dma,启动收dma,启动spi,启动发dma,在收发dma完成回调里,会等待收发都完成,最后完成的调用spi_enable_chip(rs, false);

spi_finalize_current_transfer(ctlr);结束传输。

    if (use_dma)

        return rockchip_spi_prepare_dma(rs, ctlr, xfer);

5、如果不用dma,配置中断。收数据靠中断,发数据靠手动一次写一个fifolen长度往tx fifo数据寄存器写。

return rockchip_spi_prepare_irq(rs, ctlr, xfer);

static int rockchip_spi_prepare_irq(struct rockchip_spi *rs,

                    struct spi_controller *ctlr,

                    struct spi_transfer *xfer)

{

    rs->tx = xfer->tx_buf;

    rs->rx = xfer->rx_buf;

    rs->tx_left = rs->tx ? xfer->len / rs->n_bytes : 0;

    rs->rx_left = xfer->len / rs->n_bytes;

    if (rs->cs_inactive)

        writel_relaxed(INT_RF_FULL | INT_CS_INACTIVE, rs->regs + ROCKCHIP_SPI_IMR);

    else

        writel_relaxed(INT_RF_FULL, rs->regs + ROCKCHIP_SPI_IMR);

    spi_enable_chip(rs, true);

    if (rs->tx_left)

        rockchip_spi_pio_writer(rs);

    /* 1 means the transfer is in progress */

    return 1;

}

通用spi设备应用层操作spi方法

测试代码见内核目录tools/spi/spidev_test.c

应用使用步骤:

1、open设备

    fd = open(device, O_RDWR);

    if (fd < 0)

        pabort("can't open device");

2、ioctl设置spi mode,bits_per_word,速率

/*

     * spi mode

     */

    ret = ioctl(fd, SPI_IOC_WR_MODE32, &mode);

    if (ret == -1)

        pabort("can't set spi mode");

    ret = ioctl(fd, SPI_IOC_RD_MODE32, &mode);

    if (ret == -1)

        pabort("can't get spi mode");

    /*

     * bits per word

     */

    ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);

    if (ret == -1)

        pabort("can't set bits per word");

    ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits);

    if (ret == -1)

        pabort("can't get bits per word");

    /*

     * max speed hz

     */

    ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);

    if (ret == -1)

        pabort("can't set max speed hz");

    ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);

    if (ret == -1)

        pabort("can't get max speed hz");

3、收发数据方法

a、调用ioctl发送接口开始收发数据,命令字SPI_IOC_MESSAGE(num),参数结构体struct spi_ioc_transfer

    struct spi_ioc_transfer tr = {

        .tx_buf = (unsigned long)tx,

        .rx_buf = (unsigned long)rx,

        .len = len,

        .delay_usecs = delay,

        .speed_hz = speed,

        .bits_per_word = bits,

    };

    if (mode & SPI_TX_OCTAL)

        tr.tx_nbits = 8;

    else if (mode & SPI_TX_QUAD)

        tr.tx_nbits = 4;

    else if (mode & SPI_TX_DUAL)

        tr.tx_nbits = 2;

    if (mode & SPI_RX_OCTAL)

        tr.rx_nbits = 8;

    else if (mode & SPI_RX_QUAD)

        tr.rx_nbits = 4;

    else if (mode & SPI_RX_DUAL)

        tr.rx_nbits = 2;

    if (!(mode & SPI_LOOP)) {

        if (mode & (SPI_TX_OCTAL | SPI_TX_QUAD | SPI_TX_DUAL))

            tr.rx_buf = 0;

        else if (mode & (SPI_RX_OCTAL | SPI_RX_QUAD | SPI_RX_DUAL))

            tr.tx_buf = 0;

    }

    ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);

b、调用read,write收发数据

参考文章

spi时序图精讲(对照逻辑分析仪)_spi逻辑分析仪时序图-优快云博客

【图文并茂超详细】嵌入式外设 —— 搞懂spi通信原理-优快云博客

SPI时序详解-优快云博客

Linux应用开发笔记(八)SPI应用层开发及其框架_linuxspi应用编程-优快云博客

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值