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传输模式。

你可能感兴趣的:(SPI)