文章目录
1、简介
Linux中的SPI驱动框架和I2C驱动框架类似,都是分为控制器驱动和设备驱动,同样地,前者也是SoC厂商写好的,后者才是针对某个具体的外设。
2、SPI控制器、设备驱动的结构体定义
2.1 结构体定义–SPI控制器
SPI控制器驱动的结构体在include/linux/spi/spi.h
中定义:
struct spi_master {
struct device dev;
struct list_head list;
// ...
int (*setup)(struct spi_device *spi);
int (*transfer)(struct spi_device *spi,
struct spi_message *mesg);
void (*cleanup)(struct spi_device *spi);
// ...
int (*transfer_one_message)(struct spi_master *master,
struct spi_message *mesg);
// ...
int (*transfer_one)(struct spi_master *master, struct spi_device *spi,
struct spi_transfer *transfer);
// ...
};
同样地,也需要将结构体告诉内核,相关函数如下:
struct spi_master *spi_alloc_master(struct device *dev, unsigned size); // 用于分配一个spi_master,dev为platform设备
void spi_master_put(struct spi_master *master); // 与前者相反,用于释放
int spi_register_master(struct spi_master *master); // 将spi_master注册进内核
void spi_unregister_master(struct spi_master *master); // 与前者相反,注销
2.2 结构体定义–SPI设备
SPI设备的结构体在include/linux/spi/spi.h
中定义:
struct spi_device {
struct device dev;
struct spi_master *master; // 包含“SPI控制器”成员
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;
};
如果使用设备树的写法,则SPI设备在系统解析设备树的时候会配置该结构体,不需要我们手动配置。而传统不使用设备树则可以通过以下函数来注册进内核:
// 注册方法1
struct spi_device *spi_new_device(struct spi_master *master, struct spi_board_info *board_info);
// 注册方法2(两阶段操作,与前者不同,注册之前可以先配置spi_device)
struct spi_device *spi_alloc_device(struct spi_master *master);
int spi_add_device(struct spi_device *spi);
// 注销
void spi_unregister_device(struct spi_device *spi);
2.3 结构体定义–SPI驱动
SPI驱动的结构体在include/linux/spi/spi.h
中定义:
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;
};
驱动的结构体也要注册进内核,相关函数如下:
int spi_register_driver(struct spi_driver *sdrv);
void spi_unregister_driver(struct spi_driver *sdrv);
3、SPI总线、设备、驱动、硬件操作的联系
3.1 SPI控制器驱动加载到内核的过程
以Freescale i.MX6ULL,Linux 4.1.15版本内核为例,在设备树文件imx6ull.dtsi
中可以看到SPI节点如下:
ecspi1: ecspi@02008000 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "fsl,imx6ul-ecspi", "fsl,imx51-ecspi";
reg = <0x02008000 0x4000>;
interrupts = <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6UL_CLK_ECSPI1>,
<&clks IMX6UL_CLK_ECSPI1>;
clock-names = "ipg", "per";
dmas = <&sdma 3 7 1>, <&sdma 4 7 2>;
dma-names = "rx", "tx";
status = "disabled";
};
直接grep搜索compatible
属性"fsl,imx6ul-ecspi
",发现在drivers/spi/spi-imx.c
中有使用,如果查看整体架构就会发现SPI控制器驱动也还是使用的platform平台设备驱动方式:
static const struct of_device_id spi_imx_dt_ids[] = {
// ...
{
.compatible = "fsl,imx6ul-ecspi", .data = &imx6ul_ecspi_devtype_data, },
{
/* sentinel */ }
};
static struct platform_driver spi_imx_driver = {
.driver = {
.name = DRIVER_NAME,
.of_match_table = spi_imx_dt_ids,
.pm = IMX_SPI_PM,
},
.id_table = spi_imx_devtype,
.probe = spi_imx_probe,
.remove = spi_imx_remove,
};
module_platform_driver(spi_imx_driver);
3.2 SPI控制器驱动如何操作硬件
以Freescale i.MX6ULL,Linux 4.1.15版本内核为例:
spi_imx->bitbang.txrx_bufs = spi_imx_transfer;
spi_imx_pio_transfer(spi, transfer);
struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master);
spi_imx->tx_buf = transfer->tx_buf;
spi_imx->rx_buf = transfer->rx_buf;
spi_imx_push(spi_imx); // 启动传输
3.3 SPI设备 <-----> SPI驱动
不管是SPI控制器、SPI设备、还是SPI驱动,注册时都会指定总线类型,而总线类型结构体里包含match
匹配函数。以SPI驱动方面为例:
// 驱动注册函数里面指定总线类型spi_bus_type:drivers/spi/spi.c
int spi_register_driver(struct spi_driver *sdrv)
{
sdrv->driver.bus = &spi_bus_type; // ------->
// ...
return driver_register(&sdrv->driver);
}
struct bus_type spi_bus_type = {
.name = "spi",
.dev_groups = spi_dev_groups,
.match = spi_match_device, // ------->
.uevent = spi_uevent,
};
static int spi_match_device(struct device *dev, struct device_driver *drv)
{
const struct spi_device *spi = to_spi_device(dev);
const struct spi_driver *sdrv = to_spi_driver(drv);
/* Attempt an OF style match */
if (of_driver_match_device(dev, drv))
return 1;
/* Then try ACPI */
if (acpi_driver_match_device(dev, drv))
return 1;
if (sdrv->id_table)
return !!spi_match_id(sdrv->id_table, spi);
return strcmp(spi->modalias, drv->name) == 0;
}
3.4 SPI设备 <-----> SPI控制器
设备树方式:设备放在哪个节点下就是使用哪个控制器。
// imx6qdl-sabresd.dtsi
&ecspi1 {
fsl,spi-num-chipselects = <1>;
cs-gpios = <&gpio4 9 0>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_ecspi1>;
status = "okay";
flash: m25p80@0 {
#address-cells = <1>;
#size-cells = <1>;
compatible = "st,m25p32";
spi-max-frequency = <20000000>;
reg = <0>;
};
};
传统方式:创建设备时已经指定了使用哪个spi_master
控制器。
struct spi_device *spi_new_device(struct spi_master *, struct spi_board_info *);
3.5 SPI驱动 <-----> SPI控制器
事实上,SPI驱动无法“指定”哪个SPI控制器,它是通过probe
函数参数里“被动”地从SPI设备的结构体里获得SPI控制器的信息,这也就是将设备、驱动分离的目的:
int spi_register_driver(struct spi_driver *sdrv);
spi_driver.probe // int(*probe)(struct spi_device *spi);
spi_device.master
4、SPI驱动传输数据API函数
struct spi_message {
struct list_head transfers;
struct spi_device *spi;
unsigned is_dma_mapped:1;
void (*complete)(void *context);
void *context;
unsigned frame_length;
unsigned actual_length;
int status;
struct list_head queue;
void *state;
};
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;
#define SPI_NBITS_SINGLE 0x01 /* 1bit transfer */
#define SPI_NBITS_DUAL 0x02 /* 2bits transfer */
#define SPI_NBITS_QUAD 0x04 /* 4bits transfer */
u8 bits_per_word;
u16 delay_usecs;
u32 speed_hz;
struct list_head transfer_list;
};
void spi_message_init(struct spi_message *m); // 初始化消息
void spi_message_add_tail(struct spi_transfer *t, struct spi_message *m); // 将消息添加到队列尾部
int spi_sync(struct spi_device *spi, struct spi_message *message); // 启动同步传输:阻塞到传输完毕
int spi_async(struct spi_device *spi, struct spi_message *message) // 启动异步传输:调用后立马返回,但需要设置传输完成的回调函数complete
5、SPI设备驱动框架
5.1、SPI设备(设备树形式)
// imx6qdl-sabresd.dtsi
&ecspi1 {
fsl,spi-num-chipselects = <1>;
cs-gpios = <&gpio4 9 0>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_ecspi1>;
status = "okay";
flash: m25p80@0 {
#address-cells = <1>;
#size-cells = <1>;
compatible = "st,m25p32";
spi-max-frequency = <20000000>;
reg = <0>;
};
};
5.2、SPI驱动
static int xxx_open(struct inode *inode, struct file *filp)
{
// ...
return 0;
}
static int xxx_close(struct inode *inode, struct file *filp)
{
// ...
return 0;
}
static struct file_operations xxx_fops = {
.owner = THIS_MODULE,
.open = xxx_open,
.release = xxx_close,
};
static int xxx_probe(struct spi_device *spi)
{
// ...
cdev_init(&cdev, &xxx_fops);
return 0;
}
static int xxx_remove(struct spi_device *spi)
{
// ...
cdev_del(&cdev);
return 0;
}
/* 传统不使用设备树的匹配列表 */
static const struct spi_device_id xxx_id[] = {
{
"xxx", 0}, /* <用于匹配dev的名字> <私有数据> */
{
}
};
/* 使用设备树的匹配列表 */
static const struct of_device_id xxx_of_match[] = {
{
.compatible = "xxx"}, /* 与设备树节点匹配的名字 */
{
}
};
static struct spi_driver xxx_driver = {
.probe = xxx_probe,
.remove = xxx_remove,
.driver = {
.owner = THIS_MODULE,
.name = "xxx",
.of_match_table = xxx_of_match,
},
.id_table = xxx_id,
};
static int __init xxx_init(void)
{
spi_register_driver(&xxx_driver);
return 0;
}
static void __exit xxx_exit(void)
{
spi_unregister_driver(&xxx_driver);
}
module_init(xxx_init);
module_exit(xxx_exit);
MODULE_LICENSE("GPL");
6、W25Q32驱动示例
后续更新…
7、W25Q32测试程序
后续更新…
8、W25Q32测试结果
后续更新…