linux下的SPI总线驱动在driver/spi目录中。
SPI驱动分为三层
SPI核心层 drivers/spi/spi.c 与平台无关
SPI控制器驱动层
SPI设备驱动层
每层的主要结构体和函数
SPI控制器驱动层(include/linux/spi/spi.h)
struct spi_master {
struct device dev;
s16 bus_num;//控制器对应的SPI总线号
u16 num_chipselect;//支持片选数量
int (* setup) (struct spi_device *spi);//SPI模式设备,在spi_add_device中调用
int (* transfer) (struct spi_device *spi,struct spi_message *mesg);//读写方法的函数,可能会睡眠
void (* cleanup) (struct spi_device *spi);//注销时候调用
};
SPI设备驱动层
spi_device对应某个特定的slave
struct spi_driver {
int (* probe) (struct spi_device *spi);//匹配时调用,完成spi_transfer,spi_message,spi_message_init,spi_meassage_add_tail,spi_sync,spi_write_then_read的调用
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;
};
struct spi_device {
struct device dev;
struct spi_master * master;
u32 max_speed_hz;
u8 chip_select;
u8 mode;
#define SPI_CPHA 0x01
#define SPI_CPOL 0x02
#define SPI_MODE_0 (0|0)
#define SPI_MODE_1 (0|SPI_CPHA)
#define SPI_MODE_2 (SPI_CPOL|0)
#define SPI_MODE_3 (SPI_CPOL|SPI_CPHA)
#define SPI_CS_HIGH 0x04
#define SPI_LSB_FIRST 0x08
#define SPI_3WIRE 0x10
#define SPI_LOOP 0x20
u8 bits_per_word;
int irq;
void * controller_state;
void * controller_data;
const char * modalias;
};
struct spi_transfer {
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;
};
int spi_register_driver ( struct spi_driver * sdrv);
struct spi_message {
struct list_head transfers;
struct spi_device * spi;
unsigned is_dma_mapped:1;
void (* complete) (void *context);
void * context;
unsigned actual_length;
int status;
struct list_head queue;
void * state;
}; 在每次使用spi_message可以使用函数:
void spi_message_init(structspi_message *m);
来初始化。
向spi_message添加transfers可以使用spi_message_add_tail()函数:
void spi_message_add_tail(structspi_transfer *t, struct spi_message *m);
一旦你准备好了spi_message,就可以使用spi_async()来向SPI系统提交了:
int spi_async(struct spi_device *spi,struct spi_message *message);
因为是异步的,一提交就立马返回了,这也就是说需要同步机制(complete就是了)。他确保不会睡眠,可安全的在中断handler或其他不可休眠的代码中调用。稍后会念念他的好的。
使用spi_async()需要注意的是,在complete()未返回前不要轻易访问你一提交的spi_transfer中的buffer。也不能释放SPI系统正在使用的buffer。一旦你的complete返回了,这些buffer就又是你的了。
使用完成回调机制稍显复杂,可以使用SPI系统提供的另一个同步版本:spi_sync():
int spi_sync(struct spi_device *spi,struct spi_message *message);
因为是同步的,spi_sync提交完spi_message后不会立即返回,会一直等待其被处理。一旦返回就可以重新使用buffer了。spi_sync()在drivers/spi/spi.c中实现,其调用了spi_async(),并休眠直至complete返回。
spi_device的板信息用spi_board_info来描述,在init函数调用时会初始化。
struct spi_board_info {
char modalias[KOBJ_NAME_LEN];
const void * platform_data;
void * controller_data;
int irq;
u32 max_speed_hz;
u16 bus_num;
u16 chip_select;
u8 mode;
};
int __init spi_register_board_info ( struct spi_board_info const * info,
unsigned n);
会把spi_board_info注册到链表board_list上。
之后,spi_master注册会调用scan_board_info扫描board_list找到挂接在它上面的spi设备。
然后创建并注册spi_device.
一个结构体spi_board_info对应着一个SPI设备spi_device
struct boardinfo{
struct list_head list;
struct spi_board_info board_info;
}
static LIST_HEAD(board_list);
static LIST_HEAD(spi_master_list);//初始化一个空链表
#define LIST_HEAD(name)
struct list_head name=LIST_HEAD_INIT(name)
#define LIST_HEAD_INIT(name) {&(name),&(name)}
struct list_head {struct list_head* next,*prev;};
struct device_driver结构体被定义在/include/linux/device.h,原型是:
struct device_driver {
const char *name;
struct bus_type *bus;
struct module *owner;
const char *mod_name; /* used for built-in modules */
bool suppress_bind_attrs; /* disables bind/unbind via sysfs */
int (*probe) (struct device *dev);
int (*remove) (struct device *dev);
void (*shutdown) (struct device *dev);
int (*suspend) (struct device *dev, pm_message_t state);
int (*resume) (struct device *dev);
const struct attribute_group **groups;
const struct dev_pm_ops *pm;
struct driver_private *p;
};
举例SPI驱动
spi_device设备
spi_board_info
init入口调用,spi_register_board_info(S3C_SPI_devs,ARRAY_SIZE(S3C_SPI_devs));
会把spi_board_info注册到board_list链表上。
之后会创建并注册spi_device
spi_driver驱动
1、声明并设置spi_driver结构体
2、注册spi_register_driver(&spi_driver);
3、实现probe操作,spi_transfer,spi_message的构建
spi_message_init,spi_message_add_tail,spi_sync调用
3.1、定义并设置 spi_transfer结构体
3.2、定义spi_message结构体
3.3、初始化meg,spi_message_init(&meg);
3.4、将st放入message队列spi_message_add_tail(&st,&meg);
3.5、将message与spi_device关联,发送meg,spi_sync(spi_device,&meg);
转载请注明原文地址: https://ju.6miu.com/read-1125620.html