文章系列
SPI驱动框架
spi驱动框架如图所示,驱动框架分用户层、核心层和硬件层,用户层主要向上层开发人员提供接口,硬件层是spi模块和spi设备的硬件描述,我们主要认识核心层spi-core。
核心层中分设备层和控制器层,设备层提供spi从设备的注册,控制器层提供芯片spi控制模块的注册,一般控制器层的驱动在内核中都已经提供了,我们主要是编写自己的设备驱动,具体编写就可以参考别的器件的驱动。从图中我们可以看到控制器层有spi_master和spi_bitbang两个结构体代表控制器模块,区别在于spi_bitbang会提供一种机制,而这种机制是spi_master注册时没有的,具体下文介绍。在整个设备的注册流程中,系统驱动后会先注册控制器的驱动,然后会注册挂在spi总线上的设备层的驱动。
spi驱动的代码都在drivers/spi/目录下,进去后感觉乱糟糟的,不像i2c那样分的清楚,不过代码看多了之后就清楚了,呵呵。主要的文件是spi.c和spi-bitbang.c,这些都是核心文件,spidev.c是一个设备层的文件,其他大多是控制器代码文件。
SPI主控制器(总线)驱动介绍
spi主控制器的注册是通过platform bus来进行的,在系统初始化时会进行spi的platform_device的注册,然后具体SoC上的spi模块的platform_driver的注册在各自的文件中,注册匹配后就会进入probe函数来进行spi主控制器spi_master的注册,相关结构体下面介绍
结构体介绍
spi_master定义了SPI主控制器,spi_transfer 和spi_message 是数据传输时使用的结构体,spi_transfer的链表挂在spi_message 的链表中,所以每次数据传输时,一个spi_message 会有多个spi_transfer来进行传输,每次进行数据传输时,都会检查spi_message 的状态,看是否合适进行传输
struct spi_master {
struct device dev;
struct list_head list;
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;
/* bitmask of supported bits_per_word for transfers */
u32 bits_per_word_mask;
/* limits on transfer speed */
u32 min_speed_hz;
u32 max_speed_hz;
/* other constraints relevant to this driver */
u16 flags;
/*
* on some hardware transfer size may be constrained
* the limit may depend on device transfer settings
*/
size_t (*max_transfer_size)(struct spi_device *spi);
/* 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;
int (*setup)(struct spi_device *spi);
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);
bool (*can_dma)(struct spi_master *master,
struct spi_device *spi,
struct spi_transfer *xfer);
bool queued;
struct kthread_worker kworker;
struct task_struct *kworker_task;
struct kthread_work pump_messages;
spinlock_t queue_lock;
struct list_head queue;
struct spi_message *cur_msg;
bool idling;
bool busy;
bool running;
bool rt;
bool auto_runtime_pm;
bool cur_msg_prepared;
bool cur_msg_mapped;
struct completion xfer_completion;
size_t max_dma_len;
int (*prepare_transfer_hardware)(struct spi_master *master);
int (*transfer_one_message)(struct spi_master *master,
struct spi_message *mesg);
int (*unprepare_transfer_hardware)(struct spi_master *master);
int (*prepare_message)(struct spi_master *master,
struct spi_message *message);
int (*unprepare_message)(struct spi_master *master,
struct spi_message *message);
int (*spi_flash_read)(struct spi_device *spi,
struct spi_flash_read_message *msg);
/*
* These hooks are for drivers that use a generic implementation
* of transfer_one_message() provied by the core.
*/
void (*set_cs)(struct spi_device *spi, bool enable);
int (*transfer_one)(struct spi_master *master, struct spi_device *spi,
struct spi_transfer *transfer);
void (*handle_err)(struct spi_master *master,
struct spi_message *message);
/* gpio chip select */
int *cs_gpios;
/* statistics */
struct spi_statistics statistics;
/* DMA channels for use with core dmaengine helpers */
struct dma_chan *dma_tx;
struct dma_chan *dma_rx;
/* dummy data for full duplex devices */
void *dummy_rx;
void *dummy_tx;
int (*fw_translate_cs)(struct spi_master *master, unsigned cs);
};
struct spi_transfer {
const void *tx_buf;
void *rx_buf;
unsigned len;
dma_addr_t tx_dma;
dma_addr_t rx_dma;
struct sg_table tx_sg;
struct sg_table rx_sg;
unsigned cs_change:1;
unsigned tx_nbits:3;
unsigned rx_nbits:3;
u8 bits_per_word;
u16 delay_usecs;
u32 speed_hz;
struct list_head transfer_list;
};
struct spi_message {
struct list_head transfers;
struct spi_device *spi;
unsigned is_dma_mapped:1;
/* completion is reported through a callback */
void (*complete)(void *context);
void *context;
unsigned frame_length;
unsigned actual_length;
int status;
struct list_head queue;
void *state;
/* list of spi_res reources when the spi message is processed */
struct list_head resources;
};
spi_bitbang是另一套主控制器结构体,它提供一种数据传输机制,struct spi_bitbang其实就是包装了spi_maser
struct spi_bitbang {
spinlock_t lock;
u8 busy;
u8 use_dma;
u8 flags; /* extra spi->mode support */
struct spi_master *master;
/* setup_transfer() changes clock and/or wordsize to match settings
* for this transfer; zeroes restore defaults from spi_device.
*/
int (*setup_transfer)(struct spi_device *spi,
struct spi_transfer *t);
void (*chipselect)(struct spi_device *spi, int is_on);
#define BITBANG_CS_ACTIVE 1 /* normally nCS, active low */
#define BITBANG_CS_INACTIVE 0
/* txrx_bufs() may handle dma mapping for transfers that don't
* already have one (transfer.{tx,rx}_dma is zero), or use PIO
*/
int (*txrx_bufs)(struct spi_device *spi, struct spi_transfer *t);
/* txrx_word[SPI_MODE_*]() just looks like a shift register */
u32 (*txrx_word[4])(struct spi_device *spi,
unsigned nsecs,
u32 word, u8 bits);
};
函数介绍
spi主控制器设备驱动注册与卸载函数
extern int spi_register_master(struct spi_master *master);
extern void spi_unregister_master(struct spi_master *master);
现在进行spi主控制器设备的注册一般使用下面的函数,对应于struct spi_bitbang,使用这个函数进行注册会开启开启线程来进行spi数据传输
extern int spi_bitbang_start(struct spi_bitbang *spi);
extern void spi_bitbang_stop(struct spi_bitbang *spi);
SPI设备驱动介绍
结构体介绍
spi_device 等同于驱动模型中的device
struct spi_device {
struct device dev;
struct spi_master *master;
u32 max_speed_hz;
u8 chip_select;
u8 bits_per_word;
u16 mode;
int irq;
void *controller_state;
void *controller_data;
char modalias[SPI_NAME_SIZE];
int cs_gpio; /* chip select gpio */
/* the statistics */
struct spi_statistics statistics;
};
spi_driver 等同于驱动模型中的driver
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);
struct device_driver driver;
};
函数介绍
spi从设备驱动driver注册与卸载函数
spi_register_driver(driver)
spi_unregister_driver()
spi从设备device注册与卸载函数
extern struct spi_device *
spi_alloc_device(struct spi_master *master);
extern int
spi_add_device(struct spi_device *spi);
extern struct spi_device *
spi_new_device(struct spi_master *, struct spi_board_info *);
extern void spi_unregister_device(struct spi_device *spi);