linux驱动移植-SPI总线设备驱动

----------------------------------------------------------------------------------------------------------------------------
内核版本:linux 5.2.8
根文件系统:busybox 1.25.0
u-boot:2016.05
----------------------------------------------------------------------------------------------------------------------------

在前面的博客中我们已经介绍了plaform总线设备驱动模型、I2C总线设备驱动模型,而本文要介绍的SPI总线设备驱动模型与I2C总线设备驱动模型相比,大体框架是一样,他们都是实际的总线。

  • SPI控制器驱动程序叫做spi_master(spi_controller),主要提供transfer函数,进行SPI协议的数据传输;spi_master驱动也是基于platform模型的,注册spi_master时也会扫描一个链表进行SPI从设备的注册,这和I2C适配器驱动基本一致;
  • SPI设备驱动,相比于I2C设备驱动,需要提供更多的硬件信息,设备名称、片选信号、最大传输速率、模式、中断号等,在driver里则使用spi_read、spi_writer 等函数,最终也会调用到 master->transfer 函数进行发送接收。

通信协议-SPI小节,我们已经对SPI协议进行了详细的介绍,并在Mini2440裸机开发之SPI(OLED SSD1306)小节中介绍了通过SPI协议去点亮OLED。在这一节将会学习SPI总线设备驱动模型。

一、SPI驱动框架

1.1 SPI框架

SPI总线设备驱动模型和我们之前介绍的I2C总线设备驱动模型类似,它由SPI核心、SPI总线驱动(或者说SPI控制器驱动、SPI主机驱动)、SPI设备驱动组成。

 

对于linux系统来说,支持各式各样的SoC,并且还想要支持各种SPI硬件芯片,就必须将一些公共的部分抽离出来,这样就抽象出了:

  • spi_device:描述具体的SPI设备,每个spi_device对应一个实际的SPI设备,比如NRF24L01、SSD1306 OLED等;
  • spi driver:描述一个SPI设备驱动,每个spi_driver描述一种SPI设备的驱动;
  • spi master(controller):描述SoC的一个SPI控制器;
  • spi transfer:SPI通信算法,用于操作实际的SPI控制器,产生 SPI硬件波形;

在一个SoC上可能有多条SPI总线,一条总线对应一个SPI总线驱动,每一条总线上又可以接多个SPI设备。

1.1.1 SPI核心

SPI核心层是linux内核用来维护和管理SPI的核心部分。SPI核心层提供接口函数,允许一个spi_master、spi_driver和spi_device初始化时在SPI核心层中注册,以及退出时进行卸载,同时还提供了SPI总线读写访问的接口。

1.1.2 SPI总线驱动

SPI总线驱动包含了SPI控制器数据结构spi_master、SPI通信算法spi_transfer和控制SPI控制器产生通信信号的函数。

经由SPI总线驱动的代码,我们可以控制SP控制器以主控方式产生片选信号、读写周期等。

1.1.3 SPI设备驱动

SPI设备驱动主要包含了数据结构spi_driver和spi_device,我们需要根据具体设备实现其中的成员函数。

1.2 目录结构

linux内核将SPI驱动相关的代码放在drivers/spi目录下,这下面的文件还是比较多的,我们大概了解一下即可。

比如有保存SoC厂家提供的SPI控制器驱动相关的文件,比如spi-stm32.c、 spi-s3c24xx.c、spi-s3c64xx.c等。

SPI核心功能文件spi.c,实现了SPI总线的初始化、注册和控制器添加和注销等相关工作。

二、SPI总线注册

linux驱动移植-platform总线设备驱动中我们已经介绍了platfrom总线的注册流程,SPI总线类型注册和platfrom总线注册类似。

2.1 SPI总线类型定义

在linux 设备模型中,总线类型由bus_type结构表示,我们所用的 I2C、SPI、USB 都是用这个结构体来定义的。该结构体定义在 include/linux/device.h文件中:

/**
 * struct bus_type - The bus type of the device
 *
 * @name:       The name of the bus.
 * @dev_name:   Used for subsystems to enumerate devices like ("foo%u", dev->id).
 * @dev_root:   Default device to use as the parent.
 * @bus_groups: Default attributes of the bus.
 * @dev_groups: Default attributes of the devices on the bus.
 * @drv_groups: Default attributes of the device drivers on the bus.
 * @match:      Called, perhaps multiple times, whenever a new device or driver
 *              is added for this bus. It should return a positive value if the
 *              given device can be handled by the given driver and zero
 *              otherwise. It may also return error code if determining that
 *              the driver supports the device is not possible. In case of
 *              -EPROBE_DEFER it will queue the device for deferred probing.
 * @uevent:     Called when a device is added, removed, or a few other things
 *              that generate uevents to add the environment variables.
 * @probe:      Called when a new device or driver add to this bus, and callback
 *              the specific driver's probe to initial the matched device.
 * @remove:     Called when a device removed from this bus.
 * @shutdown:   Called at shut-down time to quiesce the device.
 *
 * @online:     Called to put the device back online (after offlining it).
 * @offline:    Called to put the device offline for hot-removal. May fail.
 *
 * @suspend:    Called when a device on this bus wants to go to sleep mode.
 * @resume:     Called to bring a device on this bus out of sleep mode.
 * @num_vf:     Called to find out how many virtual functions a device on this
 *              bus supports.
 * @dma_configure:      Called to setup DMA configuration on a device on
 *                      this bus.
 * @pm:         Power management operations of this bus, callback the specific
 *              device driver's pm-ops.
 * @iommu_ops:  IOMMU specific operations for this bus, used to attach IOMMU
 *              driver implementations to a bus and allow the driver to do
 *              bus-specific setup
 * @p:          The private data of the driver core, only the driver core can
 *              touch this.
 * @lock_key:   Lock class key for use by the lock validator
 * @need_parent_lock:   When probing or removing a device on this bus, the
 *                      device core should lock the device's parent.
 *
 * A bus is a channel between the processor and one or more devices. For the
 * purposes of the device model, all devices are connected via a bus, even if
 * it is an internal, virtual, "platform" bus. Buses can plug into each other.
 * A USB controller is usually a PCI device, for example. The device model
 * represents the actual connections between buses and the devices they control.
 * A bus is represented by the bus_type structure. It contains the name, the
 * default attributes, the bus' methods, PM operations, and the driver core's
 * private data.
 */
struct bus_type {
        const char              *name;
        const char              *dev_name;
        struct device           *dev_root;
        const struct attribute_group **bus_groups;
        const struct attribute_group **dev_groups;
        const struct attribute_group **drv_groups;

        int (*match)(struct device *dev, struct device_driver *drv);
        int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
        int (*probe)(struct device *dev);
        int (*remove)(struct device *dev);
        void (*shutdown)(struct device *dev);

        int (*online)(struct device *dev);
        int (*offline)(struct device *dev);

        int (*suspend)(struct device *dev, pm_message_t state);
        int (*resume)(struct device *dev);

        int (*num_vf)(struct device *dev);

        int (*dma_configure)(struct device *dev);

        const struct dev_pm_ops *pm;

        const struct iommu_ops *iommu_ops;

        struct subsys_private *p;
        struct lock_class_key lock_key;

        bool need_parent_lock;
};

其中部分字段的含义如下:

  • name:总线名称;
  • bus_groups:总线属性;
  • dev_groups:该总线上所有设备的默认属性;
  • drv_groups:该总线上所有驱动的默认属性;
  • match:当有新的设备或驱动添加到总线上时match函数被调用,如果设备和驱动可以匹配,返回0;
  • uevent:当一个设备添加、移除或添加环境变量时,函数调用;
  • probe:当有新设备或驱动添加时,probe函数调用,并且回调该驱动的probe函数来初始化相关联的设备;
  • remove:设备移除时调用remove函数;
  • shutdown:设备关机时调用shutdown函数;
  • suspend:设备进入睡眠时调用suspend函数;
  • resume:设备唤醒时调用resume函数;
  • pm:总线的电源管理选项,并回调设备驱动的电源管理模块;
  • p: 驱动核心的私有数据,只有驱动核心才可以访问。使用struct subsys_private可以将struct bus_type中的部分细节屏蔽掉,利于外界使用bus_type;struct  driver_private和struct device_private都有类似的功能。

spi_bus_type是 bus_type 类型的全局变量,这个变量已经被linux内核赋值好了,其结构体成员对应的函数也已经在内核里面写好,定义在drivers/spi/spi.c:

struct bus_type spi_bus_type = {
        .name           = "spi",
        .dev_groups     = spi_dev_groups,
        .match          = spi_match_device,
        .uevent         = spi_uevent,
};

这里我们重点关注SPI匹配函数spi_device_match即可。

2.2 SPI设备和驱动匹配

spi_bus_type中的spi_match_device就是我们常说的做驱动和设备匹配的函数,不同的总线对应的match函数肯定不一样,这个我们不用管,内核都会写好。我们所用的SPI总线对应的match函数是spi_match_device函数,该函数定义在drivers/spi/spi.c:

static int spi_match_device(struct device *dev, struct device_driver *drv)
{
        const struct spi_device *spi = to_spi_device(dev);
        const struct spi_driver *sdrv = to_spi_driver(drv);

        /* Check override first, and if set, only use the named driver */
        if (spi->driver_override)
                return strcmp(spi->driver_override, drv->name) == 0;

        /* Attempt an OF style match */
        if (of_driver_match_device(dev, drv))
                return 1;

        /* Then try ACPI */
        if (acpi_driver_match_device(dev, drv))
                return 1;

        if (sdrv->id_table)
                return !!spi_match_id(sdrv->id_table, spi);

        return strcmp(spi->modalias, drv->name) == 0;
}

该函数有两个参数:设备和设备驱动,该函数主要做了一下事情:

  • 将设备转为SPI从设备类型;
  • 将驱动转为SPI驱动类型;
  • 调用of_driver_match_device进行设备树OF类型匹配;
  • 调用acpi_driver_match_device进行ACPI类型匹配;
  • 如果设置值了sdrv->id_table,进行id_table匹配;
  • 最后比较spi的驱动name和spi设备里面的modalias信息;

通过对上面匹配函数的一个简单分析,我们知道匹配函数做匹配的顺序是先匹配设备树,然后匹配id_table表,最后匹配spi的驱动name和spi设备里面的modalias:

/* modalias support makes "modprobe $MODALIAS" new-style hotplug work,
 * and the sysfs version makes coldplug work too.
 */

static const struct spi_device_id *spi_match_id(const struct spi_device_id *id,
                                                const struct spi_device *sdev)
{
        while (id->name[0]) {
                if (!strcmp(sdev->modalias, id->name))
                        return id;
                id++;
        }
        return NULL;
}

对于支持设备树的Linux版本,我们一上来做设备树匹配就完事,不支持设备树时,我们就得定义SPI设备,再用id_tabale表或name匹配,一般情况下都是选用name匹配。

2.3 SPI总线注册

SPI子系统的初始化是由spi_init函数完成的。SPI总线以模块的方式注册到内核。spi_init定义在drivers/spi/spi.c文件中:

static int __init spi_init(void)
{
        int     status;

        buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL);
        if (!buf) {
                status = -ENOMEM;
                goto err0;
        }

        status = bus_register(&spi_bus_type);      // 注册总线 在/sys/bus目录下创建spi目录
        if (status < 0)
                goto err1;

        status = class_register(&spi_master_class);   // 在/sys/class目录下创建spi_master目录
        if (status < 0)
                goto err2;

        if (IS_ENABLED(CONFIG_SPI_SLAVE)) {
                status = class_register(&spi_slave_class);
                if (status < 0)
                        goto err3;
        }

        if (IS_ENABLED(CONFIG_OF_DYNAMIC))
                WARN_ON(of_reconfig_notifier_register(&spi_of_notifier));
        if (IS_ENABLED(CONFIG_ACPI))
                WARN_ON(acpi_reconfig_notifier_register(&spi_acpi_notifier));

        return 0;

err3:
        class_unregister(&spi_master_class);
err2:
        bus_unregister(&spi_bus_type);
err1:
        kfree(buf);
        buf = NULL;
err0:
        return status;
}

我们重点关注bus_register总线注册函数,传入的参数就是我们上面介绍的spi_bus_type。

bus_register函数调用后,就会在用户空间/sys/bus目录下生成具有总线名称的目录,同时在该目录下创建devices、drivers文件夹,创建uevent、drivers_autoprobe、drivers_probe等文件。

执行如下命令:

/sys/bus/spi/devices里用来存放的是SPI设备链接,/sys/bus/spi/drivers里用来存放的是SPI驱动链接。需要注意的是在spi_bus_type总线链表里面存放的除了SPI从设备,还有SPI控制器。

从其他博文找到一张总线创建的说明图,下图是foo总线创建的过程

三、SPI核心数据结构

学习SPI驱动,首先要了解驱动框架涉及到的数据结构,知道每个数据结构以及成员的含义之后,再去看源码就容易了。

3.1 struct spi_controller

struct spi_controller抽象了控制器硬件,在SoC中的指的就是内部SPI控制器,当向SPI核心层注册一个SPI控制器时就需要提供这样的一个结构体变量。它的定义在 include/linux/spi/spi.h 文件,如下:

/**
 * struct spi_controller - interface to SPI master or slave controller
 * @dev: device interface to this driver
 * @list: link with the global spi_controller list
 * @bus_num: board-specific (and often SoC-specific) identifier for a
 *      given SPI controller.
 * @num_chipselect: chipselects are used to distinguish individual
 *      SPI slaves, and are numbered from zero to num_chipselects.
 *      each slave has a chipselect signal, but it's common that not
 *      every chipselect is connected to a slave.
 * @dma_alignment: SPI controller constraint on DMA buffers alignment.
 * @mode_bits: flags understood by this controller driver
 * @bits_per_word_mask: A mask indicating which values of bits_per_word are
 *      supported by the driver. Bit n indicates that a bits_per_word n+1 is
 *      supported. If set, the SPI core will reject any transfer with an
 *      unsupported bits_per_word. If not set, this value is simply ignored,
 *      and it's up to the individual driver to perform any validation.
 * @min_speed_hz: Lowest supported transfer speed
 * @max_speed_hz: Highest supported transfer speed
 * @flags: other constraints relevant to this driver
 * @slave: indicates that this is an SPI slave controller
 * @max_transfer_size: function that returns the max transfer size for
 *      a &spi_device; may be %NULL, so the default %SIZE_MAX will be used.
 * @max_message_size: function that returns the max message size for
 *      a &spi_device; may be %NULL, so the default %SIZE_MAX will be used.
 * @io_mutex: mutex for physical bus access
 * @bus_lock_spinlock: spinlock for SPI bus locking
 * @bus_lock_mutex: mutex for exclusion of multiple callers
 * @bus_lock_flag: indicates that the SPI bus is locked for exclusive use
 * @setup: updates the device mode and clocking records used by a
 *      device's SPI controller; protocol code may call this.  This
 *      must fail if an unrecognized or unsupported mode is requested.
 *      It's always safe to call this unless transfers are pending on
 *      the device whose settings are being modified.
 * @set_cs_timing: optional hook for SPI devices to request SPI master
 * controller for configuring specific CS setup time, hold time and inactive
 * delay interms of clock counts
 * @transfer: adds a message to the controller's transfer queue.
 * @cleanup: frees controller-specific state
 * @can_dma: determine whether this controller supports DMA
 * @queued: whether this controller is providing an internal message queue
 * @kworker: thread struct for message pump
 * @kworker_task: pointer to task for message pump kworker thread
 * @pump_messages: work struct for scheduling work to the message pump
 * @queue_lock: spinlock to syncronise access to message queue
 * @queue: message queue
 * @idling: the device is entering idle state
 * @cur_msg: the currently in-flight message
 * @cur_msg_prepared: spi_prepare_message was called for the currently
 *                    in-flight message
 * @cur_msg_mapped: message has been mapped for DMA
 * @xfer_completion: used by core transfer_one_message()
 * @busy: message pump is busy
 * @running: message pump is running
 * @rt: whether this queue is set to run as a realtime task
 * @auto_runtime_pm: the core should ensure a runtime PM reference is held
 *                   while the hardware is prepared, using the parent
 *                   device for the spidev
* @max_dma_len: Maximum length of a DMA transfer for the device.
 * @prepare_transfer_hardware: a message will soon arrive from the queue
 *      so the subsystem requests the driver to prepare the transfer hardware
 *      by issuing this call
 * @transfer_one_message: the subsystem calls the driver to transfer a single
 *      message while queuing transfers that arrive in the meantime. When the
 *      driver is finished with this message, it must call
 *      spi_finalize_current_message() so the subsystem can issue the next
 *      message
 * @unprepare_transfer_hardware: there are currently no more messages on the
 *      queue so the subsystem notifies the driver that it may relax the
 *      hardware by issuing this call
 *
 * @set_cs: set the logic level of the chip select line.  May be called
 *          from interrupt context.
 * @prepare_message: set up the controller to transfer a single message,
 *                   for example doing DMA mapping.  Called from threaded
 *                   context.
 * @transfer_one: transfer a single spi_transfer.
 *                  - return 0 if the transfer is finished,
 *                  - return 1 if the transfer is still in progress. When
 *                    the driver is finished with this transfer it must
 *                    call spi_finalize_current_transfer() so the subsystem
 *                    can issue the next transfer. Note: transfer_one and
 *                    transfer_one_message are mutually exclusive; when both
 *                    are set, the generic subsystem does not call your
 *                    transfer_one callback.
 * @handle_err: the subsystem calls the driver to handle an error that occurs
 *              in the generic implementation of transfer_one_message().
 * @mem_ops: optimized/dedicated operations for interactions with SPI memory.
 *           This field is optional and should only be implemented if the
 *           controller has native support for memory like operations.
 * @unprepare_message: undo any work done by prepare_message().
 * @slave_abort: abort the ongoing transfer request on an SPI slave controller
 * @cs_gpios: LEGACY: array of GPIO descs to use as chip select lines; one per
 *      CS number. Any individual value may be -ENOENT for CS lines that
 *      are not GPIOs (driven by the SPI controller itself). Use the cs_gpiods
 *      in new drivers.
 * @cs_gpiods: Array of GPIO descs to use as chip select lines; one per CS
 *      number. Any individual value may be NULL for CS lines that
 *      are not GPIOs (driven by the SPI controller itself).
 * @use_gpio_descriptors: Turns on the code in the SPI core to parse and grab
 *      GPIO descriptors rather than using global GPIO numbers grabbed by the
 *      driver. This will fill in @cs_gpiods and @cs_gpios should not be used,
 *      and SPI devices will have the cs_gpiod assigned rather than cs_gpio.
 * @statistics: statistics for the spi_controller
 * @dma_tx: DMA transmit channel
 * @dma_rx: DMA receive channel
 * @dummy_rx: dummy receive buffer for full-duplex devices
 * @dummy_tx: dummy transmit buffer for full-duplex devices
 * @fw_translate_cs: If the boot firmware uses different numbering scheme
 *      what Linux expects, this optional hook can be used to translate
 *      between the two.
 *
 * Each SPI controller can communicate with one or more @spi_device
 * children.  These make a small bus, sharing MOSI, MISO and SCK signals
 * but not chip select signals.  Each device may be configured to use a
 * different clock rate, since those shared signals are ignored unless
 * the chip is selected.
 *
 * The driver for an SPI controller manages access to those devices through
 * a queue of spi_message transactions, copying data between CPU memory and
 * an SPI slave device.  For each such message it queues, it calls the
 * message's completion function when the transaction completes.
 */
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;

        /* 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 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().
         */
        void (*set_cs_timing)(struct spi_device *spi, u8 setup_clk_cycles,
                              u8 hold_clk_cycles, u8 inactive_clk_cycles);

        /* 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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Graceful_scenery

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值