SPI子系统驱动架构 - 具体实现

文章系列

SPI子系统驱动架构 - 简介

SPI子系统驱动架构 - 驱动框架

SPI子系统驱动架构 - 具体实现

SPI设备注册流程

同I2C驱动的设备注册流程类似,遵从platform_bus_register->spi_bus_register->spi设备的流程

spi主控制器的注册流程

spi主控制器设备是挂载在platform总线上的,所以要通过platform bus子系统的注册方法来注册设备,遵从device-bus-driver的规律,首先要进行platform_device的注册,这是通过在系统初始化的时候,根据设备树的解析来进行的,具体参考linux设备树的解释
接着进行platform_driver的注册,这是通过函数platform_driver_register()来进行注册,之后匹配成功后就进入driver->probe函数,‘probe’函数会做一些初始化工作,主要是初始化结构体struct spi_master和struct spi_bitbang,之后就进入spi主控制器设备的注册函数,在文章系列2中有介绍相关函数,本文主要介绍函数spi_bitbang_start

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

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

    spin_lock_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_message = spi_bitbang_transfer_one;---------------初始化相关函数字段

    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;----------初始化相关函数字段
            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));-----主控制器注册
    if (ret)
        spi_master_put(master);

    return 0;
}

上面的一些‘初始化相关函数字段‘工作,是函数默认的用法,当然自己有特殊需求可以自定义另一套函数,下面看看函数spi_register_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);
    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;
    }

    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);-------spi主控制器设备注册
    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);------初始化spi工作队列,下面会介绍此函数
        if (status) {
            device_del(&master->dev);
            goto done;
        }
    }

    mutex_lock(&board_lock);
    list_add_tail(&master->list, &spi_master_list);
    list_for_each_entry(bi, &board_list, list)
        spi_match_master_to_boardinfo(master, &bi->board_info);
    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;
}

spi主控制器注册完成后,就会把挂载在总线上的从设备进行device注册,在函数of_register_spi_devices中进行,下面主要介绍上面提及的函数spi_master_initialize_queue

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

    master->transfer = spi_queued_transfer;---------初始化字段master->transfer,重要!
    if (!master->transfer_one_message)--------------初始化字段transfer_one_message
        master->transfer_one_message = spi_transfer_one_message;

    /* Initialize and start queue */
    ret = spi_init_queue(master);-------------------初始化工作队列
    if (ret) {
        dev_err(&master->dev, "problem initializing queue\n");
        goto err_init_queue;
    }
    master->queued = true;
    ret = spi_start_queue(master);------------------队列开始工作
    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;
}

上面的一些初始化字段工作都是为后面的数据传输过程做准备,在‘SPI数据传输流程‘这一节会有详细介绍,在上面的spi_init_queue这一初始化工作中会初始化工作队列函数,如下

init_kthread_work(&master->pump_messages, spi_pump_messages);

其中函数spi_pump_messages就是一直进行数据传输的主要工作详情见‘SPI数据传输流程‘这一节

spi从设备的注册流程

在文章系列2中关于spi从设备device注册函数主要有3个,其中spi_new_device会分别调用spi_alloc_device和spi_add_device这两个函数,这两函数分别实现不同的功能,如下介绍

struct spi_device *spi_alloc_device(struct spi_master *master)
{
    struct spi_device   *spi;

    if (!spi_master_get(master))
        return NULL;

    spi = kzalloc(sizeof(*spi), GFP_KERNEL);
    if (!spi) {
        spi_master_put(master);
        return NULL;
    }

    spi->master = master;
    spi->dev.parent = &master->dev;
    spi->dev.bus = &spi_bus_type;--------设置总线类型
    spi->dev.release = spidev_release;
    spi->cs_gpio = -ENOENT;
    device_initialize(&spi->dev);
    return spi;
}
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];

    /* 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);
    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);-------设备注册
    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;
}

完成spi主控制器的device注册后,就driver的注册,注册函数如下

int spi_register_driver(struct spi_driver *sdrv)
{
    sdrv->driver.bus = &spi_bus_type;
    if (sdrv->probe)
        sdrv->driver.probe = spi_drv_probe;
    if (sdrv->remove)
        sdrv->driver.remove = spi_drv_remove;
    if (sdrv->shutdown)
        sdrv->driver.shutdown = spi_drv_shutdown;
    return driver_register(&sdrv->driver);--------驱动注册
}

上面的工作匹配完成后,就进入spi_driver->probe函数,一般此函数的功能是向用户空间提供操作接口来对spi设备进行读写操作,具体数据传输流程下面介绍

SPI数据传输流程

在include/linux/spi/spi.h中定义了许多数据传输函数,列举如下:
一个数据传输总图
spi-transfer图

spi_write(struct spi_device *spi, const void *buf, size_t len)
spi_read(struct spi_device *spi, void *buf, size_t len)
spi_w8r8(struct spi_device *spi, u8 cmd)
spi_w8r16(struct spi_device *spi, u8 cmd)
spi_w8r16be(struct spi_device *spi, u8 cmd)
.
.
.
extern int spi_setup(struct spi_device *spi);
extern int spi_async(struct spi_device *spi, struct spi_message *message);
extern int spi_async_locked(struct spi_device *spi,struct spi_message *message);

extern int spi_sync(struct spi_device *spi, struct spi_message *message);
extern int spi_sync_locked(struct spi_device *spi, struct spi_message *message);
extern int spi_bus_lock(struct spi_master *master);
extern int spi_bus_unlock(struct spi_master *master);

没有bitbang的传输流程

总结上面所有的数据传输函数,总体上分两种:同步传输和异步传输,对应的函数为:spi_sync和spi_async,而这两个函数最终都会调用函数__spi_async

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

    message->spi = spi;

    trace_spi_message_submit(message);

    return master->transfer(spi, message);-----调用字段transfer函数
}

可见最终是调用具体spi控制器的transfer函数,在注册的时候要初始化对应的字段

带有bitbang的传输流程

不管是带bitbang还是不带,数据传输流程使用的函数都是上面列举的一些函数,所以在bitbang的传输流程中最后也会调用master->transfer

在上面我们介绍spi主控制器注册流程的时候提到了函数spi_master_initialize_queue,里面有一步是初始化master->transfer字段,初始化相应的函数为spi_queued_transfer

static int spi_queued_transfer(struct spi_device *spi, struct spi_message *msg)
{
    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);-------------------------将msg加入到master的队列
    if (!master->busy)
        queue_kthread_work(&master->kworker, &master->pump_messages);---启动队列工作

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

上面做完后数据开始传输,数据传输的函数为master->pump_messages,而上面已经介绍过队列开启的函数字段为spi_pump_messages

static void spi_pump_messages(struct kthread_work *work)
{
    struct spi_master *master =
        container_of(work, struct spi_master, pump_messages);
    unsigned long flags;
    bool was_busy = false;
    int ret;

    /* Lock queue and check for queue work */
    spin_lock_irqsave(&master->queue_lock, flags);
    if (list_empty(&master->queue) || !master->running) {
        if (!master->busy) {
            spin_unlock_irqrestore(&master->queue_lock, flags);
            return;
        }
        master->busy = false;
        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))
            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);
        return;
    }

    /* Make sure we are not already running a message */
    if (master->cur_msg) {
        spin_unlock_irqrestore(&master->queue_lock, flags);
        return;
    }
    /* Extract head of queue */
    master->cur_msg =
        list_first_entry(&master->queue, struct spi_message, queue);

    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);
    if (ret) {
        dev_err(&master->dev,
            "failed to transfer one message from queue\n");
        return;
    }
}

spi_pump_messages函数就是进行数据传输的工作,结合总图可以了解的清晰明白

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值