SPI 总线协议和驱动

    协议介绍

        SPI(Serial Peripheral Interface),即串行外设接口,是一种串口通信接口协议。SPI遵循主从模式框架设计架构,但只能有一个主设备。SPI总线有4条信号线,CS(片选信号线),SCLK(时钟信号线),MOSI(主出从入信号线)和MISO(主入从出信号线),因此SPI是全双工总线。SPI的工作原理比较简单,主设备控制片选和时钟输出,输出数据到从设备及读取从设备输入的数据。

        SPI协议有四种时钟工作模式(Mode0,Mode1,Mode2和Mode3),要求主从设备工作模式一致。从设备在出厂之后,时钟工作模式已经固定,一般通过修改主设备的时钟工作模式来匹配从设备时钟模式。SPI的四种工作模式取决于时钟极性(CPOL)和时钟相位(CPHA)的组合,时钟极性CPOL用来配置SCLK的电平处于哪种状态时是空闲态或者工作态,而时钟相位CPHA用来配置数据采样在哪个时钟沿:

    (1)CPOL=0,表示SCLK低电平时是空闲态,也就是SCLK高电平时是工作态

    (2)CPOL=1,表示SCLK高电平时是工作态,也就是SCLK低电平时是工作态

    (3)CPHA=0,表示数据采样在第一个时钟沿,数据发送在第二个时钟沿

    (4)CPHA=1,表示数据采样在第二个时钟沿,数据发送在第一个时钟沿

        协议工作时序图如下(图片来源于网络):

        944893-20161027161424453-974917858.gif

        SPI总线具有传输速率快,全双工和硬件电路简单等有点,但是有个缺点:没有类似于I2C总线的应答机制。

 

    内核框架

        内核中SPI总线协议框架类似于I2C的总线框架。核心层提供spi_master,spi_device,spi_driver,spi_message和spi_transfer核心数据结构和API接口,供上层系统注册主设备驱动,从设备以及从设备驱动。

        1,数据结构:

        (1)struct spi_master:主设备和驱动。spi_master结构包含主设备速率,工作模式,统计相关信息。采用了kernel workqueue的工作模式,提供了数据传输相关的回调接口:transfer()接口用于将spi_message加入消息队列,workqueue从消息队列中取出spi_message,通过transfer_one_message()接口发送一个spi_message,每个spi_message包含多个spi_transfer,最终通过transfer_one()接口发送一个spi_transfer。

struct spi_master {
    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 */
    u16            mode_bits;

    /* bitmask of supported bits_per_word for transfers */
    u32            bits_per_word_mask;    // 一个transfer的bit数掩码
#define SPI_BPW_MASK(bits) BIT((bits) - 1)
#define SPI_BIT_MASK(bits) (((bits) == 32) ? ~0U : (BIT(bits) - 1))
#define SPI_BPW_RANGE_MASK(min, max) (SPI_BIT_MASK(max) - SPI_BIT_MASK(min - 1))

    /* limits on transfer speed */
    u32            min_speed_hz;    // 支持的最小传输速率
    u32            max_speed_hz;    // 支持的最大传输速率

    /* other constraints relevant to this driver */
    u16            flags;
#define SPI_MASTER_HALF_DUPLEX    BIT(0)        /* can't do full duplex */
#define SPI_MASTER_NO_RX    BIT(1)        /* can't do buffer read */
#define SPI_MASTER_NO_TX    BIT(2)        /* can't do buffer write */
#define SPI_MASTER_MUST_RX      BIT(3)        /* requires rx */
#define SPI_MASTER_MUST_TX      BIT(4)        /* requires tx */

    /* 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);    // 每个transfer传输之前由 spi_driver调用,用于配置传输参数(不同从设备在速率和时钟模式上都可能不同,因此每个transfer都需要重新配置)

    /* 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 master's main job is to process its message queue,
     *   selecting a chip 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);    // spi_async() / spi_sync() 接口调用该方法将spi_message消息加入队列

    /* called on release() to free memory provided by spi_master */
    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_master *master,
                       struct spi_device *spi,
                       struct spi_transfer *xfer);

    /*
     * These hooks are for drivers that want to use the generic
     * master 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 task_struct        *kworker_task;
    struct kthread_work        pump_messages;    // work queue,用于处理队列中的spi_message

    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;
    struct completion               xfer_completion;
    size_t                max_dma_len;

    int (*prepare_transfer_hardware)(struct spi_master *master);      // 准备好硬件,用于实际数据传输
    int (*transfer_one_message)(struct spi_master *master,
                    struct spi_message *mesg);                                            // 发送一个spi_message

    int (*unprepare_transfer_hardware)(struct spi_master *master);
    int (*prepare_message)(struct spi_master *master,
                   struct spi_message *message);

    int (*unprepare_message)(struct spi_master *master,
                 struct spi_message *message);

    /*
     * 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_master *master, struct spi_device *spi,
                struct spi_transfer *transfer);                          // 发送一个 spi_transfer

    void (*handle_err)(struct spi_master *master,
               struct spi_message *message);

    /* gpio chip select */
    int            *cs_gpios;

    /* 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;
};
        (2)struct spi_device:spi_device用于描述一个spi从设备,一般在主设备注册时,通过平台相关代码配置或者设备树配置自动创建并注册。

struct spi_device {
    struct device        dev;
    struct spi_master    *master;
    u32            max_speed_hz;    // 支持的最大传输速率
    u8            chip_select;    // 片选
    u8            bits_per_word;    // 单位传输bit数
    u16            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 */
    int            irq;
    void            *controller_state;
    void            *controller_data;
    char            modalias[SPI_NAME_SIZE];
    int            cs_gpio;    /* chip select gpio */

    /* 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
     *  - drop chipselect after each word
     *  - chipselect delays
     *  - ...
     */
};

        (3)struct spi_driver:从设备驱动

struct spi_driver {
    const struct spi_device_id *id_table;
    int            (*probe)(struct spi_device *spi);
    int            (*remove)(struct spi_device *spi);
    void            (*shutdown)(struct spi_device *spi);
    struct device_driver    driver;
};
        (4)struct spi_message:spi消息结构

struct spi_message {
    struct list_head    transfers;    // 包含的spi_transfer列表

    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_master controller driver.
     */
    struct list_head    queue;
    void            *state;
};
        (5)spi_transfer:包含在spi_message中的spi_transfer

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;    // 发送buffer
    void        *rx_buf;    // 接收buffer
    unsigned    len;    // buffer长度

    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;
    u32        speed_hz;

    struct list_head transfer_list;
};
        2,框架接口:

        (1)spi_master注册接口:int spi_register_master(struct spi_master *master);

int spi_register_master(struct spi_master *master)
{
    static atomic_t        dyn_bus_id = ATOMIC_INIT((1<<15) - 1);
    struct device        *dev = master->dev.parent;
    struct boardinfo    *bi;
    int            status = -ENODEV;
    int            dynamic = 0;

    if (!dev)
        return -ENODEV;

    status = of_spi_register_master(master);    // 从设备树配置中获取片选个数,和片选gpio编号
    if (status)
        return status;

    /* even if it's just one always-selected device, there must
     * be at least one chipselect
     */
    if (master->num_chipselect == 0)
        return -EINVAL;

    if ((master->bus_num < 0) && master->dev.of_node)
        master->bus_num = of_alias_get_id(master->dev.of_node, "spi");

    /* convention:  dynamically assigned bus IDs count down from the max */
    if (master->bus_num < 0) {
        /* FIXME switch to an IDR based scheme, something like
         * I2C now uses, so we can't run out of "dynamic" IDs
         */
        master->bus_num = atomic_dec_return(&dyn_bus_id);
        dynamic = 1;
    }

    INIT_LIST_HEAD(&master->queue);
    spin_lock_init(&master->queue_lock);
    spin_lock_init(&master->bus_lock_spinlock);
    mutex_init(&master->bus_lock_mutex);
    master->bus_lock_flag = 0;
    init_completion(&master->xfer_completion);
    if (!master->max_dma_len)
        master->max_dma_len = INT_MAX;

    /* register the device, then userspace will see it.
     * registration fails if the bus ID is in use.
     */
    dev_set_name(&master->dev, "spi%u", master->bus_num);
    status = device_add(&master->dev);    // 注册master到kernel设备驱动框架
    if (status < 0)
        goto done;
    dev_dbg(dev, "registered master %s%s\n", dev_name(&master->dev),
            dynamic ? " (dynamic)" : "");

    /* If we're using a queued driver, start the queue */
    if (master->transfer)
        dev_info(dev, "master is unqueued, this is deprecated\n");
    else {
        status = spi_master_initialize_queue(master);    // 初始化主设备消息队列处理机制,workqueue
        if (status) {
            device_del(&master->dev);
            goto done;
        }
    }
    /* add statistics */
    spin_lock_init(&master->statistics.lock);

    mutex_lock(&board_lock);
    list_add_tail(&master->list, &spi_master_list);    // 将master加入 spi_master_list 列表中
    list_for_each_entry(bi, &board_list, list)
        spi_match_master_to_boardinfo(master, &bi->board_info);    // 从预配置的board_list中创建匹配的spi_device

    mutex_unlock(&board_lock);

    /* Register devices from the device tree and ACPI */
    of_register_spi_devices(master);    // 从设备树配置中创建并注册所有的spi_device
    acpi_register_spi_devices(master);
done:
    return status;
}

static int spi_master_initialize_queue(struct spi_master *master)
{
    int ret;

    master->transfer = spi_queued_transfer;    // 设置transfer方法为 spi_queued_transfer(),该方法将spi_message加入master的message                                                                                           queue
    if (!master->transfer_one_message)
        master->transfer_one_message = spi_transfer_one_message;    // spi_transfer_one_message用来轮询message中的所有spi_transfer,调                                                                                                                          用 master中的transfer_one来一个个传输spi_transfer

    /* Initialize and start queue */
    ret = spi_init_queue(master);    // 初始化workqueue线程
    if (ret) {
        dev_err(&master->dev, "problem initializing queue\n");
        goto err_init_queue;
    }
    master->queued = true;
    ret = spi_start_queue(master);    // 启动workqueue线程,执行spi_pump_messages()方法从消息队列获取spi_message进行传输
    if (ret) {
        dev_err(&master->dev, "problem starting queue\n");
        goto err_start_queue;
    }

    return 0;

err_start_queue:
    spi_destroy_queue(master);
err_init_queue:
    return ret;
}

static void of_register_spi_devices(struct spi_master *master)
{
    struct spi_device *spi;
    struct device_node *nc;

    if (!master->dev.of_node)
        return;

    for_each_available_child_of_node(master->dev.of_node, nc) {
        spi = of_register_spi_device(master, nc);    // 轮询注册每个spi_device
        if (IS_ERR(spi))
            dev_warn(&master->dev, "Failed to create SPI device for %s\n",
                nc->full_name);
    }
}

of_register_spi_device(struct spi_master *master, struct device_node *nc)
{
    struct spi_device *spi;
    int rc;
    u32 value;

    /* Alloc an spi_device */
    spi = spi_alloc_device(master);    // 分配spi_device结构
    if (!spi) {
        dev_err(&master->dev, "spi_device alloc error for %s\n",
            nc->full_name);
        rc = -ENOMEM;
        goto err_out;
    }

    /* Select device driver */
    rc = of_modalias_node(nc, spi->modalias,
                sizeof(spi->modalias));
    if (rc < 0) {
        dev_err(&master->dev, "cannot find modalias for %s\n",
            nc->full_name);
        goto err_out;
    }

    /* Device address */
    rc = of_property_read_u32(nc, "reg", &value);
    if (rc) {
        dev_err(&master->dev, "%s has no valid 'reg' property (%d)\n",
            nc->full_name, rc);
        goto err_out;
    }
    spi->chip_select = value;    // 配置片选编号

    /* Mode (clock phase/polarity/etc.) */
    if (of_find_property(nc, "spi-cpha", NULL))
        spi->mode |= SPI_CPHA;
    if (of_find_property(nc, "spi-cpol", NULL))
        spi->mode |= SPI_CPOL;
    if (of_find_property(nc, "spi-cs-high", NULL))
        spi->mode |= SPI_CS_HIGH;
    if (of_find_property(nc, "spi-3wire", NULL))
        spi->mode |= SPI_3WIRE;
    if (of_find_property(nc, "spi-lsb-first", NULL))
        spi->mode |= SPI_LSB_FIRST;

    /* Device DUAL/QUAD mode */
    if (!of_property_read_u32(nc, "spi-tx-bus-width", &value)) {
        switch (value) {
        case 1:
            break;
        case 2:
            spi->mode |= SPI_TX_DUAL;
            break;
        case 4:
            spi->mode |= SPI_TX_QUAD;
            break;
        default:
            dev_warn(&master->dev,
                "spi-tx-bus-width %d not supported\n",
                value);
            break;
        }
    }

    if (!of_property_read_u32(nc, "spi-rx-bus-width", &value)) {
        switch (value) {
        case 1:
            break;
        case 2:
            spi->mode |= SPI_RX_DUAL;
            break;
        case 4:
            spi->mode |= SPI_RX_QUAD;
            break;
        default:
            dev_warn(&master->dev,
                "spi-rx-bus-width %d not supported\n",
                value);
            break;
        }
    }

    /* Device speed */
    rc = of_property_read_u32(nc, "spi-max-frequency", &value);
    if (rc) {
        dev_err(&master->dev, "%s has no valid 'spi-max-frequency' property (%d)\n",
            nc->full_name, rc);
        goto err_out;
    }
    spi->max_speed_hz = value;

    /* Store a pointer to the node in the device structure */
    of_node_get(nc);
    spi->dev.of_node = nc;

    /* Register the new device */
    rc = spi_add_device(spi);    // 注册spi_device到spi_bus_type中
    if (rc) {
        dev_err(&master->dev, "spi_device register error %s\n",
            nc->full_name);
        goto err_out;
    }

    return spi;

err_out:
    spi_dev_put(spi);
    return ERR_PTR(rc);
}

int spi_add_device(struct spi_device *spi)
{
    static DEFINE_MUTEX(spi_add_lock);
    struct spi_master *master = spi->master;
    struct device *dev = master->dev.parent;
    int status;

    /* Chipselects are numbered 0..max; validate. */
    if (spi->chip_select >= master->num_chipselect) {
        dev_err(dev, "cs%d >= max %d\n",
            spi->chip_select,
            master->num_chipselect);
        return -EINVAL;
    }

    /* Set the bus ID string */
    spi_dev_set_name(spi);

    /* We need to make sure there's no other device with this
     * chipselect **BEFORE** we call setup(), else we'll trash
     * its configuration.  Lock against concurrent add() calls.
     */
    mutex_lock(&spi_add_lock);

    status = bus_for_each_dev(&spi_bus_type, NULL, spi, spi_dev_check);
    if (status) {
        dev_err(dev, "chipselect %d already in use\n",
                spi->chip_select);
        goto done;
    }

    if (master->cs_gpios)
        spi->cs_gpio = master->cs_gpios[spi->chip_select];    // 获取片选gpio

    /* Drivers may modify this initial i/o setup, but will
     * normally rely on the device being setup.  Devices
     * using SPI_CS_HIGH can't coexist well otherwise...
     */
    status = spi_setup(spi);    // 内部调用master->setup设置传输参数
    if (status < 0) {
        dev_err(dev, "can't setup %s, status %d\n",
                dev_name(&spi->dev), status);
        goto done;
    }

    /* Device may be bound to an active driver when this returns */
    status = device_add(&spi->dev);    // 将spi_device注册进设备驱动框架
    if (status < 0)
        dev_err(dev, "can't add %s, status %d\n",
                dev_name(&spi->dev), status);
    else
        dev_dbg(dev, "registered child %s\n", dev_name(&spi->dev));

done:
    mutex_unlock(&spi_add_lock);
    return status;
}

int spi_setup(struct spi_device *spi)
{
    unsigned    bad_bits, ugly_bits;
    int        status;

    /* check mode to prevent that DUAL and QUAD set at the same time
     */
    if (((spi->mode & SPI_TX_DUAL) && (spi->mode & SPI_TX_QUAD)) ||
        ((spi->mode & SPI_RX_DUAL) && (spi->mode & SPI_RX_QUAD))) {
        dev_err(&spi->dev,
        "setup: can not select dual and quad at the same time\n");
        return -EINVAL;
    }
    /* if it is SPI_3WIRE mode, DUAL and QUAD should be forbidden
     */
    if ((spi->mode & SPI_3WIRE) && (spi->mode &
        (SPI_TX_DUAL | SPI_TX_QUAD | SPI_RX_DUAL | SPI_RX_QUAD)))
        return -EINVAL;
    /* help drivers fail *cleanly* when they need options
     * that aren't supported with their current master
     */
    bad_bits = spi->mode & ~spi->master->mode_bits;
    ugly_bits = bad_bits &
            (SPI_TX_DUAL | SPI_TX_QUAD | SPI_RX_DUAL | SPI_RX_QUAD);
    if (ugly_bits) {
        dev_warn(&spi->dev,
             "setup: ignoring unsupported mode bits %x\n",
             ugly_bits);
        spi->mode &= ~ugly_bits;
        bad_bits &= ~ugly_bits;
    }
    if (bad_bits) {
        dev_err(&spi->dev, "setup: unsupported mode bits %x\n",
            bad_bits);
        return -EINVAL;
    }

    if (!spi->bits_per_word)
        spi->bits_per_word = 8;

    status = __spi_validate_bits_per_word(spi->master, spi->bits_per_word);
    if (status)
        return status;

    if (!spi->max_speed_hz)
        spi->max_speed_hz = spi->master->max_speed_hz;

    if (spi->master->setup)
        status = spi->master->setup(spi);    // 调用master->setup()

    spi_set_cs(spi, false);    // 先禁止片选

    dev_dbg(&spi->dev, "setup mode %d, %s%s%s%s%u bits/w, %u Hz max --> %d\n",
            (int) (spi->mode & (SPI_CPOL | SPI_CPHA)),
            (spi->mode & SPI_CS_HIGH) ? "cs_high, " : "",
            (spi->mode & SPI_LSB_FIRST) ? "lsb, " : "",
            (spi->mode & SPI_3WIRE) ? "3wire, " : "",
            (spi->mode & SPI_LOOP) ? "loopback, " : "",
            spi->bits_per_word, spi->max_speed_hz,
            status);

    return status;
}

static void spi_set_cs(struct spi_device *spi, bool enable)
{
    if (spi->mode & SPI_CS_HIGH)
        enable = !enable;

    if (gpio_is_valid(spi->cs_gpio))
        gpio_set_value(spi->cs_gpio, !enable);    // 优先设置gpio片选信号
    else if (spi->master->set_cs)
        spi->master->set_cs(spi, !enable);    // 否则调用master->set_cs()
}

        (2)数据传输(异步传输):int spi_async(struct spi_device *spi, struct spi_message *message)

int spi_async(struct spi_device *spi, struct spi_message *message)
{
    struct spi_master *master = spi->master;
    int ret;
    unsigned long flags;

    ret = __spi_validate(spi, message);
    if (ret != 0)
        return ret;

    spin_lock_irqsave(&master->bus_lock_spinlock, flags);

    if (master->bus_lock_flag)
        ret = -EBUSY;
    else
        ret = __spi_async(spi, message);     // 调用无锁版本

    spin_unlock_irqrestore(&master->bus_lock_spinlock, flags);

    return ret;
}

static int __spi_async(struct spi_device *spi, struct spi_message *message)
{
    struct spi_master *master = spi->master;

    message->spi = spi;

    SPI_STATISTICS_INCREMENT_FIELD(&master->statistics, spi_async);
    SPI_STATISTICS_INCREMENT_FIELD(&spi->statistics, spi_async);    // 更新统计信息

    trace_spi_message_submit(message);

    return master->transfer(spi, message);    // 调用master->transfer,即spi_queued_transfer(),将spi_message加入消息队列
}

static int spi_queued_transfer(struct spi_device *spi, struct spi_message *msg)
{
    return __spi_queued_transfer(spi, msg, true);    // true表示消息加入队列后,需要启动workqueue线程去处理队列中的消息,因为这个方法只会被异步传输方法调用。
}

static int __spi_queued_transfer(struct spi_device *spi,
                 struct spi_message *msg,
                 bool need_pump)
{
    struct spi_master *master = spi->master;
    unsigned long flags;

    spin_lock_irqsave(&master->queue_lock, flags);

    if (!master->running) {
        spin_unlock_irqrestore(&master->queue_lock, flags);
        return -ESHUTDOWN;
    }
    msg->actual_length = 0;
    msg->status = -EINPROGRESS;

    list_add_tail(&msg->queue, &master->queue);    // spi_message加入master消息队列
    if (!master->busy && need_pump)
        queue_kthread_work(&master->kworker, &master->pump_messages);    // need_pump为true,启动workqueue线程处理消息。其中master->pump_message方法为spi_pump_messages()方法

    spin_unlock_irqrestore(&master->queue_lock, flags);
    return 0;
}

// spi_pump_messages()运行于workqueue线程

static void spi_pump_messages(struct kthread_work *work)
{
    struct spi_master *master =
        container_of(work, struct spi_master, pump_messages);

    __spi_pump_messages(master, true);    // 执行实际的数据处理
}

static void __spi_pump_messages(struct spi_master *master, bool in_kthread)
{
    unsigned long flags;
    bool was_busy = false;
    int ret;

    /* Lock queue */
    spin_lock_irqsave(&master->queue_lock, flags);

    /* Make sure we are not already running a message */
    if (master->cur_msg) {
        spin_unlock_irqrestore(&master->queue_lock, flags);
        return;
    }

    /* If another context is idling the device then defer */
    if (master->idling) {
        queue_kthread_work(&master->kworker, &master->pump_messages);
        spin_unlock_irqrestore(&master->queue_lock, flags);
        return;
    }

    /* Check if the queue is idle */
    if (list_empty(&master->queue) || !master->running) {
        if (!master->busy) {
            spin_unlock_irqrestore(&master->queue_lock, flags);
            return;
        }

        /* Only do teardown in the thread */
        if (!in_kthread) {
            queue_kthread_work(&master->kworker,
                       &master->pump_messages);
            spin_unlock_irqrestore(&master->queue_lock, flags);
            return;
        }

        master->busy = false;
        master->idling = true;
        spin_unlock_irqrestore(&master->queue_lock, flags);

        kfree(master->dummy_rx);
        master->dummy_rx = NULL;
        kfree(master->dummy_tx);
        master->dummy_tx = NULL;
        if (master->unprepare_transfer_hardware &&
            master->unprepare_transfer_hardware(master))    // 调用master->unprepare_transfer_hardware结束上一次硬件传输

            dev_err(&master->dev,
                "failed to unprepare transfer hardware\n");
        if (master->auto_runtime_pm) {
            pm_runtime_mark_last_busy(master->dev.parent);
            pm_runtime_put_autosuspend(master->dev.parent);
        }
        trace_spi_master_idle(master);

        spin_lock_irqsave(&master->queue_lock, flags);
        master->idling = false;
        spin_unlock_irqrestore(&master->queue_lock, flags);
        return;
    }

    /* Extract head of queue */
    master->cur_msg =
        list_first_entry(&master->queue, struct spi_message, queue);    // 从消息队列头获取一个message

    list_del_init(&master->cur_msg->queue);
    if (master->busy)
        was_busy = true;
    else
        master->busy = true;
    spin_unlock_irqrestore(&master->queue_lock, flags);

    if (!was_busy && master->auto_runtime_pm) {
        ret = pm_runtime_get_sync(master->dev.parent);
        if (ret < 0) {
            dev_err(&master->dev, "Failed to power device: %d\n",
                ret);
            return;
        }
    }

    if (!was_busy)
        trace_spi_master_busy(master);

    if (!was_busy && master->prepare_transfer_hardware) {
        ret = master->prepare_transfer_hardware(master);    // 准备好新的传输硬件条件

        if (ret) {
            dev_err(&master->dev,
                "failed to prepare transfer hardware\n");

            if (master->auto_runtime_pm)
                pm_runtime_put(master->dev.parent);
            return;
        }
    }

    trace_spi_message_start(master->cur_msg);

    if (master->prepare_message) {
        ret = master->prepare_message(master, master->cur_msg);    // 如果有传输消息准备方法,就调用

        if (ret) {
            dev_err(&master->dev,
                "failed to prepare message: %d\n", ret);
            master->cur_msg->status = ret;
            spi_finalize_current_message(master);
            return;
        }
        master->cur_msg_prepared = true;
    }

    ret = spi_map_msg(master, master->cur_msg);
    if (ret) {
        master->cur_msg->status = ret;
        spi_finalize_current_message(master);
        return;
    }

    ret = master->transfer_one_message(master, master->cur_msg);    // 传输一个spi_message,即spi_transfer_one_message()
    if (ret) {
        dev_err(&master->dev,
            "failed to transfer one message from queue\n");
        return;
    }
}

static int spi_transfer_one_message(struct spi_master *master,
                    struct spi_message *msg)
{
    struct spi_transfer *xfer;
    bool keep_cs = false;
    int ret = 0;
    unsigned long ms = 1;
    struct spi_statistics *statm = &master->statistics;
    struct spi_statistics *stats = &msg->spi->statistics;

    spi_set_cs(msg->spi, true);

    SPI_STATISTICS_INCREMENT_FIELD(statm, messages);
    SPI_STATISTICS_INCREMENT_FIELD(stats, messages);

    list_for_each_entry(xfer, &msg->transfers, transfer_list) {    // 轮询message下的所有transfer
        trace_spi_transfer_start(msg, xfer);

        spi_statistics_add_transfer_stats(statm, xfer, master);
        spi_statistics_add_transfer_stats(stats, xfer, master);

        if (xfer->tx_buf || xfer->rx_buf) {
            reinit_completion(&master->xfer_completion);

            ret = master->transfer_one(master, msg->spi, xfer);    // 调用master->transfer_one()传输spi_transfer
            if (ret < 0) {
                SPI_STATISTICS_INCREMENT_FIELD(statm,
                                   errors);
                SPI_STATISTICS_INCREMENT_FIELD(stats,
                                   errors);
                dev_err(&msg->spi->dev,
                    "SPI transfer failed: %d\n", ret);
                goto out;
            }

            if (ret > 0) {
                ret = 0;
                ms = xfer->len * 8 * 1000 / xfer->speed_hz;
                ms += ms + 100; /* some tolerance */

                ms = wait_for_completion_timeout(&master->xfer_completion,
                                 msecs_to_jiffies(ms));    // 当前线程加入传输完成队列,传输完成时中断处理函数会唤醒当前线程,或者超时唤醒

            }

            if (ms == 0) {
                SPI_STATISTICS_INCREMENT_FIELD(statm,
                                   timedout);
                SPI_STATISTICS_INCREMENT_FIELD(stats,
                                   timedout);
                dev_err(&msg->spi->dev,
                    "SPI transfer timed out\n");
                msg->status = -ETIMEDOUT;
            }
        } else {
            if (xfer->len)
                dev_err(&msg->spi->dev,
                    "Bufferless transfer has length %u\n",
                    xfer->len);
        }

        trace_spi_transfer_stop(msg, xfer);

        if (msg->status != -EINPROGRESS)
            goto out;

        if (xfer->delay_usecs)
            udelay(xfer->delay_usecs);

        if (xfer->cs_change) {
            if (list_is_last(&xfer->transfer_list,
                     &msg->transfers)) {
                keep_cs = true;
            } else {
                spi_set_cs(msg->spi, false);
                udelay(10);
                spi_set_cs(msg->spi, true);
            }
        }

        msg->actual_length += xfer->len;    // 更新实际传输长度
    }

out:
    if (ret != 0 || !keep_cs)
        spi_set_cs(msg->spi, false);

    if (msg->status == -EINPROGRESS)
        msg->status = ret;

    if (msg->status && master->handle_err)
        master->handle_err(master, msg);

    spi_finalize_current_message(master); //结束当前消息

    return ret;
}

    注意,master->transter_one由实际spi主设备驱动实现,用来完成实际的传输协议(在后面SPI bitbang中解释)。

        (3)数据传输(同步传输):int spi_sync(struct spi_device *spi, struct spi_message *message)

int spi_sync(struct spi_device *spi, struct spi_message *message)
{
    return __spi_sync(spi, message, 0);    // 同步数据传输
}

static int __spi_sync(struct spi_device *spi, struct spi_message *message,
              int bus_locked)
{
    DECLARE_COMPLETION_ONSTACK(done);
    int status;
    struct spi_master *master = spi->master;
    unsigned long flags;

    status = __spi_validate(spi, message);
    if (status != 0)
        return status;

    message->complete = spi_complete;
    message->context = &done;
    message->spi = spi;

    SPI_STATISTICS_INCREMENT_FIELD(&master->statistics, spi_sync);
    SPI_STATISTICS_INCREMENT_FIELD(&spi->statistics, spi_sync);

    if (!bus_locked)
        mutex_lock(&master->bus_lock_mutex);

    /* If we're not using the legacy transfer method then we will
     * try to transfer in the calling context so special case.
     * This code would be less tricky if we could remove the
     * support for driver implemented message queues.
     */
    if (master->transfer == spi_queued_transfer) {
        spin_lock_irqsave(&master->bus_lock_spinlock, flags);

        trace_spi_message_submit(message);

        status = __spi_queued_transfer(spi, message, false);    // false表示将spi_message放进消息队列,但是不启动workqueue线程处理

        spin_unlock_irqrestore(&master->bus_lock_spinlock, flags);
    } else {
        status = spi_async_locked(spi, message);
    }

    if (!bus_locked)
        mutex_unlock(&master->bus_lock_mutex);

    if (status == 0) {
        /* Push out the messages in the calling context if we
         * can.
         */
        if (master->transfer == spi_queued_transfer) {
            SPI_STATISTICS_INCREMENT_FIELD(&master->statistics,
                               spi_sync_immediate);
            SPI_STATISTICS_INCREMENT_FIELD(&spi->statistics,
                               spi_sync_immediate);
            __spi_pump_messages(master, false);    // 在当前线程中取出消息队列中的消息,同步处理。
        }

        wait_for_completion(&done);
        status = message->status;
    }
    message->context = NULL;
    return status;
}

 

    SPI master驱动分析:SPI bitbang

        spi bitbang是软件模拟的spi主设备驱动。当硬件上没有spi总线主控设备时,可以在软件上模拟spi主控设备,通过gpio信号线同从设备通信。spi bitbang依赖于gpio模拟spi的四根信号线,所以bitbang相关的代码依赖spi-gpio驱动。直接看源码:

        static struct platform_driver spi_gpio_driver = {
    .driver = {
        .name    = DRIVER_NAME,
        .of_match_table = of_match_ptr(spi_gpio_dt_ids),
    },
    .probe        = spi_gpio_probe,    // spi-gpio探测函数
    .remove        = spi_gpio_remove,
};

static int spi_gpio_probe(struct platform_device *pdev)
{
    int                status;
    struct spi_master        *master;
    struct spi_gpio            *spi_gpio;
    struct spi_gpio_platform_data    *pdata;
    u16 master_flags = 0;
    bool use_of = 0;
    int num_devices;

    status = spi_gpio_probe_dt(pdev);    // 从设备树配置中获取spi相关配置信息: MOSI,MISO,SCLK等gpio信息
    if (status < 0)
        return status;
    if (status > 0)
        use_of = 1;

    pdata = dev_get_platdata(&pdev->dev);
#ifdef GENERIC_BITBANG
    if (!pdata || (!use_of && !pdata->num_chipselect))
        return -ENODEV;
#endif

    if (use_of && !SPI_N_CHIPSEL)
        num_devices = 1;
    else
        num_devices = SPI_N_CHIPSEL;

    status = spi_gpio_request(pdata, dev_name(&pdev->dev), &master_flags);    // 请求MOSI,MISO,SCLK等gpio信息
    if (status < 0)
        return status;

    master = spi_alloc_master(&pdev->dev, sizeof(*spi_gpio) +
                    (sizeof(unsigned long) * num_devices));    // 分配spi_master结构对象

    if (!master) {
        status = -ENOMEM;
        goto gpio_free;
    }
    spi_gpio = spi_master_get_devdata(master);
    platform_set_drvdata(pdev, spi_gpio);

    spi_gpio->pdev = pdev;
    if (pdata)
        spi_gpio->pdata = *pdata;

    master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32);
    master->flags = master_flags;
    master->bus_num = pdev->id;
    master->num_chipselect = num_devices;
    master->setup = spi_gpio_setup;
    master->cleanup = spi_gpio_cleanup;    // 设置setup和cleanup方法

#ifdef CONFIG_OF
    master->dev.of_node = pdev->dev.of_node;

    if (use_of) {
        int i;
        struct device_node *np = pdev->dev.of_node;

        /*
         * In DT environments, take the CS GPIO from the "cs-gpios"
         * property of the node.
         */

        if (!SPI_N_CHIPSEL)
            spi_gpio->cs_gpios[0] = SPI_GPIO_NO_CHIPSELECT;
        else
            for (i = 0; i < SPI_N_CHIPSEL; i++) {
                status = of_get_named_gpio(np, "cs-gpios", i);
                if (status < 0) {
                    dev_err(&pdev->dev,
                        "invalid cs-gpios property\n");
                    goto gpio_free;
                }
                spi_gpio->cs_gpios[i] = status;
            }
    }
#endif

    spi_gpio->bitbang.master = master;
    spi_gpio->bitbang.chipselect = spi_gpio_chipselect;    // 设置spi bitbang的片选方法

    // 初始化4种时钟模式下的传输方法

    if ((master_flags & (SPI_MASTER_NO_TX | SPI_MASTER_NO_RX)) == 0) {
        spi_gpio->bitbang.txrx_word[SPI_MODE_0] = spi_gpio_txrx_word_mode0;
        spi_gpio->bitbang.txrx_word[SPI_MODE_1] = spi_gpio_txrx_word_mode1;
        spi_gpio->bitbang.txrx_word[SPI_MODE_2] = spi_gpio_txrx_word_mode2;
        spi_gpio->bitbang.txrx_word[SPI_MODE_3] = spi_gpio_txrx_word_mode3;

    } else {
        spi_gpio->bitbang.txrx_word[SPI_MODE_0] = spi_gpio_spec_txrx_word_mode0;
        spi_gpio->bitbang.txrx_word[SPI_MODE_1] = spi_gpio_spec_txrx_word_mode1;
        spi_gpio->bitbang.txrx_word[SPI_MODE_2] = spi_gpio_spec_txrx_word_mode2;
        spi_gpio->bitbang.txrx_word[SPI_MODE_3] = spi_gpio_spec_txrx_word_mode3;
    }
    spi_gpio->bitbang.setup_transfer = spi_bitbang_setup_transfer;    //设置bitbang的setup_transfer函数
    spi_gpio->bitbang.flags = SPI_CS_HIGH;    // 片选信号,高电平有效

    status = spi_bitbang_start(&spi_gpio->bitbang);    // 启动bitbang,并注册master
    if (status < 0) {
gpio_free:
        if (SPI_MISO_GPIO != SPI_GPIO_NO_MISO)
            gpio_free(SPI_MISO_GPIO);
        if (SPI_MOSI_GPIO != SPI_GPIO_NO_MOSI)
            gpio_free(SPI_MOSI_GPIO);
        gpio_free(SPI_SCK_GPIO);
        spi_master_put(master);
    }

    return status;
}

int spi_bitbang_start(struct spi_bitbang *bitbang)
{
    struct spi_master *master = bitbang->master;
    int ret;

    if (!master || !bitbang->chipselect)
        return -EINVAL;

    mutex_init(&bitbang->lock);

    if (!master->mode_bits)
        master->mode_bits = SPI_CPOL | SPI_CPHA | bitbang->flags;

    if (master->transfer || master->transfer_one_message)
        return -EINVAL;

    master->prepare_transfer_hardware = spi_bitbang_prepare_hardware;
    master->unprepare_transfer_hardware = spi_bitbang_unprepare_hardware;
    master->transfer_one = spi_bitbang_transfer_one;
    master->set_cs = spi_bitbang_set_cs;    // 设置bitbang硬件准备相关的函数,设置片选函数和spi_transfer传输函数

    if (!bitbang->txrx_bufs) {
        bitbang->use_dma = 0;
        bitbang->txrx_bufs = spi_bitbang_bufs;
        if (!master->setup) {
            if (!bitbang->setup_transfer)
                bitbang->setup_transfer =
                     spi_bitbang_setup_transfer;
            master->setup = spi_bitbang_setup;    // 注册时调用bitbang_setup函数
            master->cleanup = spi_bitbang_cleanup;
        }
    }

    /* driver may get busy before register() returns, especially
     * if someone registered boardinfo for devices
     */
    ret = spi_register_master(spi_master_get(master));    // 注册bitbang master
    if (ret)
        spi_master_put(master);

    return 0;
}
    (1)注册master加载每个spi_devices时,调用master->setup,即spi_bitbang_setup:

int spi_bitbang_setup(struct spi_device *spi)
{
    struct spi_bitbang_cs    *cs = spi->controller_state;
    struct spi_bitbang    *bitbang;

    bitbang = spi_master_get_devdata(spi->master);

    if (!cs) {
        cs = kzalloc(sizeof(*cs), GFP_KERNEL);
        if (!cs)
            return -ENOMEM;
        spi->controller_state = cs;
    }

    /* per-word shift register access, in hardware or bitbanging */
    cs->txrx_word = bitbang->txrx_word[spi->mode & (SPI_CPOL|SPI_CPHA)];    // 根据每个spi_device设备支持的模式选择不同的传输函数
    if (!cs->txrx_word)
        return -EINVAL;

    if (bitbang->setup_transfer) {
        int retval = bitbang->setup_transfer(spi, NULL);    // NULL表示,调用spi_bitbang_setup_transfer()设置spi_device的默认传输函数和速率

        if (retval < 0)
            return retval;
    }

    dev_dbg(&spi->dev, "%s, %u nsec/bit\n", __func__, 2 * cs->nsecs);

    /* NOTE we _need_ to call chipselect() early, ideally with adapter
     * setup, unless the hardware defaults cooperate to avoid confusion
     * between normal (active low) and inverted chipselects.
     */

    /* deselect chip (low or high) */
    mutex_lock(&bitbang->lock);
    if (!bitbang->busy) {
        bitbang->chipselect(spi, BITBANG_CS_INACTIVE);    // 调用spi_gpio_chipselect() 
        ndelay(cs->nsecs);
    }
    mutex_unlock(&bitbang->lock);

    return 0;
}

int spi_bitbang_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
{
    struct spi_bitbang_cs    *cs = spi->controller_state;
    u8            bits_per_word;
    u32            hz;

    if (t) {
        bits_per_word = t->bits_per_word;
        hz = t->speed_hz;
    } else {
        bits_per_word = 0;
        hz = 0;
    }

    /* spi_transfer level calls that work per-word */
    if (!bits_per_word)
        bits_per_word = spi->bits_per_word;

    // 设置spi_device各位宽传输函数
    if (bits_per_word <= 8)
        cs->txrx_bufs = bitbang_txrx_8;
    else if (bits_per_word <= 16)
        cs->txrx_bufs = bitbang_txrx_16;
    else if (bits_per_word <= 32)
        cs->txrx_bufs = bitbang_txrx_32;

    else
        return -EINVAL;

    /* nsecs = (clock period)/2 */

    // 设置spi_device的传输速率
    if (!hz)
        hz = spi->max_speed_hz;
    if (hz) {
        cs->nsecs = (1000000000/2) / hz;
        if (cs->nsecs > (MAX_UDELAY_MS * 1000 * 1000))
            return -EINVAL;
    }

    return 0;
}

static void spi_gpio_chipselect(struct spi_device *spi, int is_active)
{
    struct spi_gpio *spi_gpio = spi_to_spi_gpio(spi);
    unsigned long cs = spi_gpio->cs_gpios[spi->chip_select];

    /* set initial clock polarity */
    if (is_active)
        setsck(spi, spi->mode & SPI_CPOL);

    if (cs != SPI_GPIO_NO_CHIPSELECT) {
        /* SPI is normally active-low */
        gpio_set_value_cansleep(cs, (spi->mode & SPI_CS_HIGH) ? is_active : !is_active);    // 设置片选信号
    }
}

    (2)数据传输过程到master->transfer_one,即调用spi_bitbang_transfer_one:

static int spi_bitbang_transfer_one(struct spi_master *master,
                    struct spi_device *spi,
                    struct spi_transfer *transfer)
{
    struct spi_bitbang *bitbang = spi_master_get_devdata(master);
    int status = 0;

    if (bitbang->setup_transfer) {
        status = bitbang->setup_transfer(spi, transfer);    // 先设置spi的传输函数和传输速率
        if (status < 0)
            goto out;
    }

    if (transfer->len)
        status = bitbang->txrx_bufs(spi, transfer);    // 调用实际的传输函数,即spi_bitbang_bufs

    if (status == transfer->len)
        status = 0;
    else if (status >= 0)
        status = -EREMOTEIO;

out:
    spi_finalize_current_transfer(master);

    return status;
}

static int spi_bitbang_bufs(struct spi_device *spi, struct spi_transfer *t)
{
    struct spi_bitbang_cs    *cs = spi->controller_state;
    unsigned        nsecs = cs->nsecs;

    return cs->txrx_bufs(spi, cs->txrx_word, nsecs, t);    // 根据spi_device的片选传输字长选取不同的传输函数
}

    以8bit传输函数为例:

static unsigned bitbang_txrx_8(
    struct spi_device    *spi,
    u32            (*txrx_word)(struct spi_device *spi,
                    unsigned nsecs,
                    u32 word, u8 bits),
    unsigned        ns,
    struct spi_transfer    *t
) {
    unsigned        bits = t->bits_per_word;
    unsigned        count = t->len;
    const u8        *tx = t->tx_buf;
    u8            *rx = t->rx_buf;

    while (likely(count > 0)) {
        u8        word = 0;

        if (tx)
            word = *tx++;
        word = txrx_word(spi, ns, word, bits);    // 调用实际传输函数spi_gpio_txrx_word_modex
        if (rx)
            *rx++ = word;
        count -= 1;
    }
    return t->len - count;
}

    以spi_gpio_txrx_word_mode0为例,

static u32 spi_gpio_txrx_word_mode0(struct spi_device *spi,
        unsigned nsecs, u32 word, u8 bits)
{
    return bitbang_txrx_be_cpha0(spi, nsecs, 0, 0, word, bits);    // 调用bitbang模拟收发函数
}

// 软件模拟SCLK,MISO和MOSI信号实现数据收发

static inline u32
bitbang_txrx_be_cpha0(struct spi_device *spi,
        unsigned nsecs, unsigned cpol, unsigned flags,
        u32 word, u8 bits)
{
    /* if (cpol == 0) this is SPI_MODE_0; else this is SPI_MODE_2 */

    u32 oldbit = (!(word & (1<<(bits-1)))) << 31;
    /* clock starts at inactive polarity */
    for (word <<= (32 - bits); likely(bits); bits--) {

        /* setup MSB (to slave) on trailing edge */
        if ((flags & SPI_MASTER_NO_TX) == 0) {
            if ((word & (1 << 31)) != oldbit) {
                setmosi(spi, word & (1 << 31));
                oldbit = word & (1 << 31);
            }
        }
        spidelay(nsecs);    /* T(setup) */

        setsck(spi, !cpol);
        spidelay(nsecs);

        /* sample MSB (from slave) on leading edge */
        word <<= 1;
        if ((flags & SPI_MASTER_NO_RX) == 0)
            word |= getmiso(spi);
        setsck(spi, cpol);
    }
    return word;
}
 

转载于:https://my.oschina.net/yepanl/blog/2992210

  • 0
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值