zoukankan      html  css  js  c++  java
  • 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传输模式。

  • 相关阅读:
    php 计算代码执行时间
    高级php面试题
    MongoDB 或者 redis 可以替代 memcached 吗?
    mysql中myisam,innodb和memory三个存储引擎的区别
    C#发送邮件代码
    使用bootstrap table时不能显示筛选列和分页每页显示的行数
    vue2中,字符串里如何拼接绑定对象
    压缩图片方法
    jquery的ajax方法,在返回中,find方法不起作用
    vue2中,在google浏览器中正常,在ie11中不解析
  • 原文地址:https://www.cnblogs.com/aqing1987/p/4355582.html
Copyright © 2011-2022 走看看