3.0.35 SPI主机控制器驱动和外设驱动

SPI(同步外设接口)是由motorola开发的全双工同步串行总线,其接口由MISO(串行数据输入),MOSI(串行数据输出),SCK(串行移位时钟)

SS(从使能信号)4种信号构成。SS决定了惟一的与主设备通信的从设备,主设备通过产生移位时钟来发起通信。

在3.0.35 内核中,用spi_master 结构体来描述一个spi 主机控制器驱动,如下定义(include/linux/spi/spi.h):

/**
 * struct spi_master - interface to SPI master controller
 * @dev: device interface to this driver
 * @list: link with the global spi_master 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
 * @flags: other constraints relevant to this driver
 * @bus_lock_spinlock: spinlock for SPI bus locking
 * @bus_lock_mutex: mutex for SPI bus locking
 * @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.
 * @transfer: adds a message to the controller‘s transfer queue.
 * @cleanup: frees controller-specific state
 *
 * Each SPI master 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_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;

    /* 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 */

    /* 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);

    /* 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);

    /* called on release() to free memory provided by spi_master */
    void            (*cleanup)(struct spi_device *spi);
};

分配、注册和注销SPI主控制器的API由SPI核心层提供(drivers/spi/spi.c):

struct spi_master *spi_alloc_master(struct device *dev, unsigned size);
int spi_register_master(struct spi_master *master);
void spi_unregister_master(struct spi_master *master);

在3.0.35 内核中使用 spi_driver 结构体来描述一个SPI外设驱动,可以认为是spi_master 的 client 驱动

spi_driver 结构体定义(include/linux/spi/spi.h)如下:

/**
 * struct spi_driver - Host side "protocol" driver
 * @id_table: List of SPI devices supported by this driver
 * @probe: Binds this driver to the spi device.  Drivers can verify
 *    that the device is actually present, and may need to configure
 *    characteristics (such as bits_per_word) which weren‘t needed for
 *    the initial configuration done during system setup.
 * @remove: Unbinds this driver from the spi device
 * @shutdown: Standard shutdown callback used during system state
 *    transitions such as powerdown/halt and kexec
 * @suspend: Standard suspend callback used during system state transitions
 * @resume: Standard resume callback used during system state transitions
 * @driver: SPI device drivers should initialize the name and owner
 *    field of this structure.
 *
 * This represents the kind of device driver that uses SPI messages to
 * interact with the hardware at the other end of a SPI link.  It‘s called
 * a "protocol" driver because it works through messages rather than talking
 * directly to SPI hardware (which is what the underlying SPI controller
 * driver does to pass those messages).  These protocols are defined in the
 * specification for the device(s) supported by the driver.
 *
 * As a rule, those device protocols represent the lowest level interface
 * supported by a driver, and it will support upper level interfaces too.
 * Examples of such upper levels include frameworks like MTD, networking,
 * MMC, RTC, filesystem character device nodes, and hardware monitoring.
 */
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);
    int            (*suspend)(struct spi_device *spi, pm_message_t mesg);
    int            (*resume)(struct spi_device *spi);
    struct device_driver    driver;
};

可以看出,spi_driver 结构体和 platform_driver 结构体有极大的相似性,都有 probe(), remove(), shutdown(), suspend(), resume()这样

的接口。这几乎是一切client 端驱动的习惯模板。

在SPI 外设驱动中,当透过SPI总线进行数据传输的时候,使用了一套与CPU无关的统一接口。这套接口的第一个关键数据结构就是spi_transfer,

它用于描述SPI传输,如下示:(include/linux/spi/spi.h)

/*
 * I/O INTERFACE between SPI controller and protocol drivers
 *
 * Protocol drivers use a queue of spi_messages, each transferring data
 * between the controller and memory buffers.
 *
 * The spi_messages themselves consist of a series of read+write transfer
 * segments.  Those segments always read the same number of bits as they
 * write; but one or the other is easily ignored by passing a null buffer
 * pointer.  (This is unlike most types of I/O API, because SPI hardware
 * is full duplex.)
 *
 * NOTE:  Allocation of spi_transfer and spi_message memory is entirely
 * up to the protocol driver, which guarantees the integrity of both (as
 * well as the data buffers) for as long as the message is queued.
 */
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;

    unsigned    cs_change:1;
    u8        bits_per_word;
    u16        delay_usecs;
    u32        speed_hz;

    struct list_head transfer_list;
};

而一次完整的SPI传输流程可能不只包含一次spi_transfer,它可能包含一个或多个spi_transfer, 这些 spi_transfer 最终通过spi_message 组织在一起

其定义如下(include/linux/spi/spi.h):

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        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;
};

通过spi_message_init() 可以初始化spi_message,而将 spi_transfer 添加到 spi_message 的法则是:

static inline void
spi_message_add_tail(struct spi_transfer *t, struct spi_message *m)
{
    list_add_tail(&t->transfer_list, &m->transfers);
}

发起一次spi_message 的传输有同步和异步两种方式,使用同步API时,会阻塞等待这个消息被处理完,同步操作时,使用的API如下:

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

使用异步API时,不会阻塞等待这个消息被处理完,但是可以在spi_message 的complete 字段挂载一个回调函数,当消息被处理完成后

该函数会被自动调用。异步操作时使用的API如下:

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

如下是一个典型的初始化spi_transfer, spi_message, 并进行spi数据传输的例子,同时它也是SPI核心层的一个通用API,

在SPI外设驱动中可以直接调用它进行读写操作。

/**
 * spi_write_then_read - SPI synchronous write followed by read
 * @spi: device with which data will be exchanged
 * @txbuf: data to be written (need not be dma-safe)
 * @n_tx: size of txbuf, in bytes
 * @rxbuf: buffer into which data will be read (need not be dma-safe)
 * @n_rx: size of rxbuf, in bytes
 * Context: can sleep
 *
 * This performs a half duplex MicroWire style transaction with the
 * device, sending txbuf and then reading rxbuf.  The return value
 * is zero for success, else a negative errno status code.
 * This call may only be used from a context that may sleep.
 *
 * Parameters to this routine are always copied using a small buffer;
 * portable code should never use this for more than 32 bytes.
 * Performance-sensitive or bulk transfer code should instead use
 * spi_{async,sync}() calls with dma-safe buffers.
 */
int spi_write_then_read(struct spi_device *spi,
        const void *txbuf, unsigned n_tx,
        void *rxbuf, unsigned n_rx)
{
    static DEFINE_MUTEX(lock);

    int            status;
    struct spi_message    message;
    struct spi_transfer    x[2];
    u8            *local_buf;

    /* Use preallocated DMA-safe buffer.  We can‘t avoid copying here,
     * (as a pure convenience thing), but we can keep heap costs
     * out of the hot path ...
     */
    if ((n_tx + n_rx) > SPI_BUFSIZ)
        return -EINVAL;

    spi_message_init(&message);
    memset(x, 0, sizeof x);
    if (n_tx) {
        x[0].len = n_tx;
        spi_message_add_tail(&x[0], &message);
    }
    if (n_rx) {
        x[1].len = n_rx;
        spi_message_add_tail(&x[1], &message);
    }

    /* ... unless someone else is using the pre-allocated buffer */
    if (!mutex_trylock(&lock)) {
        local_buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL);
        if (!local_buf)
            return -ENOMEM;
    } else
        local_buf = buf;

    memcpy(local_buf, txbuf, n_tx);
    x[0].tx_buf = local_buf;
    x[1].rx_buf = local_buf + n_tx;

    /* do the i/o */
    status = spi_sync(spi, &message);
    if (status == 0)
        memcpy(rxbuf, x[1].rx_buf, n_rx);

    if (x[0].tx_buf == buf)
        mutex_unlock(&lock);
    else
        kfree(local_buf);

    return status;
}
EXPORT_SYMBOL_GPL(spi_write_then_read);

SPI只是一种总线,spi_driver 有作用只是将spi外设挂接在该总线上,因此在spi_driver 的probe() 成员函数中,将注册SPI外设本身所属设备的驱动类型。

和platform_driver 对应着一个platform_device一样,spi_driver也对应一个spi_device,platform_device需要在BSP的板文件中添加板信息数据,而

spi_device也同样需要。spi_device 的板信息用 spi_board_info 结构体描述,定义如下(include/linux/spi/spi.h):

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,
     * irq is copied too
     */
    char        modalias[SPI_NAME_SIZE];
    const void    *platform_data;
    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_master 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.
     */
    u8        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
     */
};

该结构体描述了SPI外设使用的主机控制器序号,片选序号,数据比特率,SPI传输模式。

时间: 2024-11-02 09:13:16

3.0.35 SPI主机控制器驱动和外设驱动的相关文章

3.0.35 platform 总线、设备与驱动

在该内核的设备驱动模型中,关心总线.设备和驱动这三个实体. 在系统每注册一个设备的时候,由总线寻找与之匹配的驱动:在系统每注册一个驱动的时候,会由总线寻找与之匹配的设备. 一个现实的linux设备和驱动通常都需要挂载在一种总线上,对于本身依附于PCI,USB,I2C,SPI等的设备而言,这不是问题 但在嵌入式系统中,SoC系统中集成了独立的外设控制器,集成于SoC中的外设却不依赖于这类总线.基于这一背景,linux 发明了一种虚拟总线,称为platform总线,platform 所描述的资源有一

2017-2018-1 20155310 20155337《信息安全系统设计基础》实验四外设驱动程序设计

2017-2018-1 20155310 20155337<信息安全系统设计基础>实验四外设驱动程序设计 外设驱动程序设计-1 学习资源中全课中的"hqyj.嵌入式Linux应用程序开 发标准教程.pdf"中的第十一章 提交康奈尔笔记的照片(可以多张) 外设驱动程序设计-2 在Ubuntu完成资源中全课中的"hqyj.嵌入式Linux应用程序开发标准教程.pdf"中的第十一章的test试验 提交编译,加载模块,卸载模块,测试运行的截图(要多张,全屏,体现

2019-2020-1 20175310 20175317 20175320 实验四 外设驱动程序设计

2019-2020-1 20175310 20175317 20175320 实验四 外设驱动程序设计 小组成员 20175310 奚晨妍 20175317 钟睿文 20175320 龚仕杰 实验步骤 一.外设驱动程序设计-1 1.学习资源中全课中的"hqyj.嵌入式Linux应用程序开发标准教程.pdf"中的第十一章 2.提交康奈尔笔记的照片(可以多张) 笔记如下图所示: 二.外设驱动程序设计-2 在Ubuntu完成资源中全课中的"hqyj.嵌入式Linux应用程序开发标准

linux 3.0.35下globalmem 字符设备驱动实现

1.Makefile KDIR=/home/xxx/s-linux-3.0.35 PWD:=$(shell pwd) # kernel modules obj-m := globalmem.o modules: make -C $(KDIR) M=$(PWD) modules clean: rm -rf *.o *.ko *.mod.c *.markesr *.order *.symvers .PHONY:modules clean 2.globalmem.c #include <linux/m

实验报告 实验4 外设驱动程序设计

北京电子科技学院(BESTI) 实     验    报     告 课程: 密码系统设计基础                                                               班级: 1352班.1353班 姓名:王玥.刘浩晨                                                                    学号:20135318.20135232 成绩:                      

Linux-2.6.39在Tiny6410上的移植 - 外设驱动移植

Linux内核版本号:linux 2.6.39 交叉编译工具:arm-linux-gcc 4.5.1 Linux内核下载:www.kernel.org 开发板:友善之臂Tiny6410 LCD:友善之臂S70 一.移植LED驱动 打开arch/arm/mach-s3c64xx/mach-mini6410.c添加下列代码: 1 static struct gpio_led tiny6410_gpio_led[] = { 2 [0] = { 3 .name = "led1", //设备名

spi驱动框架全面分析,从master驱动到设备驱动

内核版本:linux2.6.32.2 硬件资源:s3c2440 参考:  韦东山SPI视频教程 内容概括: 1.I2C 驱动框架回顾 2.SPI 框架简单介绍 3.master 驱动框架 3.1 驱动侧 3.2 设备侧 4.SPI 设备驱动框架 4.1 设备册 4.2 驱动侧 5.设备驱动程序实例 1.I2C 驱动框架回顾 在前面学习 I2C 驱动程序的时候我们知道,I2C 驱动框架分为两层,一层是控制器驱动程序 i2c_adapter,它一般是由芯片厂商写好的,主要提供一个 algorithm

20155229 2017-2018-1 《信息安全系统设计基础》实验四 外设驱动程序设计

2017-2018-1 20155229 实验四 实验目的 学习嵌入式Linux设备驱动开发 掌握设备驱动的运作过程. 实验步骤 实验四-外设驱动程序设计-1 学习资源中全课中的"hqyj.嵌入式Linux应用程序开发标准教程.pdf"中的第十一章 提交康奈尔笔记的照片(可以多张) 实验四-外设驱动程序设计-2 在Ubuntu完成资源中全课中的"hqyj.嵌入式Linux应用程序开发标准教程.pdf"中的第十一章的test试验 提交编译,加载模块,卸载模块,测试运行

2017-2018-1 20155302 实验四 外设驱动程序设计

2017-2018-1 20155302 实验四 外设驱动程序设计 任务一 ?学习资源中全课中的"hqyj.嵌入式Linux应用程序开发标准教程.pdf"中的第十一章 ?提交康奈尔笔记的照片(可以多张) 完成情况: 第十一章主要讲述了在Linux的内核空间的嵌入式Linux设备驱动的开发. Linux设备驱动的基本概念. Linux设备驱动程序的基本功能. Linux设备运作的基本过程. 常见设备接口驱动函数. LCD设备驱动程序编写步骤. 键盘设备驱动程序编写步骤. 康奈尔笔记照片: