一、Linux SPI驱动框架
Linux下的spi驱动和i2c驱动十分类似,也可以分为三个部分:SPI核心,spi主机控制器(i2c中叫做i2c适配器),spi设备。
1.spi核心
spi核心提供了主机控制器的注册与注销方法、spi设备注册与注销方法、以及spi通信方法。源码位置位于kernel/drivers/spi/spi.c
2.spi主机控制器
spi的主机控制器用spi_master结构体描述:
//有部分删减
struct spi_master {
struct device dev;
struct list_head list;
s16 bus_num;
u16 num_chipselect;
u16 dma_alignment;
u16 mode_bits;
/* bitmask of supported bits_per_word for transfers */
u32 bits_per_word_mask;
#define SPI_BPW_MASK(bits) BIT((bits) - 1)
#define SPI_BIT_MASK(bits) (((bits) == 32) ? ~0U : (BIT(bits) - 1))
#define SPI_BPW_RANGE_MASK(min, max) (SPI_BIT_MASK(max) - SPI_BIT_MASK(min - 1))
/* limits on transfer speed */
u32 min_speed_hz;
u32 max_speed_hz;
/* 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 */
#define SPI_MASTER_MUST_RX BIT(3) /* requires rx */
#define SPI_MASTER_MUST_TX BIT(4) /* requires tx */
/* 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);
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;
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;
};
transfer函数就是spi的通信函数,和 i2c_algorithm 中的 master_xfer 函数一样,控制器数据传输函数。
spi的主机端最终会和硬件上的spi控制器打交道,因此spi主机控制器的驱动一般都是由芯片厂商完成的,他们的主要工作就是实现transfer函数。
spi核心提供的常用spi主机控制器的API函数如下:
/*申请一个spi主机控制器*/
struct spi_master *spi_alloc_master(struct device *dev, unsigned size)
/*释放一个spi主机控制器*/
static inline void spi_master_put(struct spi_master *master)
/*向内核注册一个spi主机控制器*/
int spi_register_master(struct spi_master *master)
/*从内核注销一个spi主机控制器*/
void spi_unregister_master(struct spi_master *master)
RK3288开发板的spi0主机控制器的设备树节点如下:
spi0: spi@ff110000 {
compatible = "rockchip,rk3288-spi", "rockchip,rk3066-spi";
clocks = <&cru SCLK_SPI0>, <&cru PCLK_SPI0>;
clock-names = "spiclk", "apb_pclk";
dmas = <&dmac_peri 11>, <&dmac_peri 12>;
dma-names = "tx", "rx";
interrupts = <GIC_SPI 44 IRQ_TYPE_LEVEL_HIGH>;
pinctrl-names = "default";
pinctrl-0 = <&spi0_clk &spi0_tx &spi0_rx &spi0_cs0>;
reg = <0x0 0xff110000 0x0 0x1000>;
#address-cells = <1>;
#size-cells = <0>;
status = "disabled";
};
/*spi0引脚配置节点*/
spi0 {
spi0_clk: spi0-clk {
rockchip,pins = <5 12 RK_FUNC_1 &pcfg_pull_up>;
};
spi0_cs0: spi0-cs0 {
rockchip,pins = <5 13 RK_FUNC_1 &pcfg_pull_up>;
};
spi0_tx: spi0-tx {
rockchip,pins = <5 14 RK_FUNC_1 &pcfg_pull_up>;
};
spi0_rx: spi0-rx {
rockchip,pins = <5 15 RK_FUNC_1 &pcfg_pull_up>;
};
spi0_cs1: spi0-cs1 {
rockchip,pins = <5 16 RK_FUNC_1 &pcfg_pull_up>;
};
};
与之对应的spi0主机控制器的驱动源码位于kernel/drivers/spi/spi-rockchip.c
static const struct of_device_id rockchip_spi_dt_match[] = {
{
.compatible = "rockchip,px30-spi", },
{
.compatible = "rockchip,rv1108-spi", },
{
.compatible = "rockchip,rk3036-spi", },
{
.compatible = "rockchip,rk3066-spi", },
{
.compatible = "rockchip,rk3188-spi", },
{
.compatible = "rockchip,rk3228-spi", },
{
.compatible = "rockchip,rk3288-spi", },
{
.compatible = "rockchip,rk3368-spi", },
{
.compatible = "rockchip,rk3399-spi", },
{
},
};
MODULE_DEVICE_TABLE(of, rockchip_spi_dt_match);
static struct platform_driver rockchip_spi_driver = {
.driver = {
.name = DRIVER_NAME,
.pm = &rockchip_spi_pm,
.of_match_table = of_match_ptr(rockchip_spi_dt_match),
},
.probe = rockchip_spi_probe,
.remove = rockchip_spi_remove,
};
可以看到rockchip_spi_dt_match匹配表中有很多芯片的compatible属性,只要设备树中有一个与之匹配,那么rockchip_spi_driver结构体的rockchip_spi_probe函数就会执行。
RK3288开发板的spi的通信函数为rockchip_spi_transfer_one。
static int rockchip_spi_transfer_one(
struct spi_master *master,
struct spi_device *spi,
struct spi_transfer *xfer)
rockchip_spi_transfer_one函数最终会调用下面两个函数来完成与硬件的数据交互。
static int rockchip_spi_pio_transfer(struct rockchip_spi *rs)
static void rockchip_spi_pio_reader(struct rockchip_spi *rs)
3.spi设备和驱动
spi设备通过spi_device结构体描述。
struct spi_device {
struct device dev;
struct spi_master *master;
u32 max_speed_hz;
u8 chip_select;
u8 bits_per_word;
u16 mode;
#define SPI_CPHA 0x01 /* clock phase */
#define SPI_CPOL 0x02 /* clock polarity */
#define SPI_MODE_0 (0|0) /* (original MicroWire) */
#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 /* chipselect active high? */
#define SPI_LSB_FIRST 0x08 /* per-word bits-on-wire */
#define SPI_3WIRE 0x10 /* SI/SO signals shared */
#define SPI_LOOP 0x20 /* loopback mode */
#define SPI_NO_CS 0x40 /* 1 dev/bus, no chipselect */
#define SPI_READY 0x80 /* slave pulls low to pause */
#define SPI_TX_DUAL 0x100 /* transmit with 2 wires */
#define SPI_TX_QUAD 0x200 /* transmit with 4 wires */
#define SPI_RX_DUAL 0x400 /* receive with 2 wires */
#define SPI_RX_QUAD 0x800 /* receive with 4 wires */
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设备驱动通过spi_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设备和驱动匹配后,spi_driver结构体的probe函数就会执行。
spi_driver的常用的API函数如下:
/*spi_driver注册函数*/
#define spi_register_driver(driver)
/*spi_driver注销函数*/
static inline void spi_unregister_driver(struct spi_driver *sdrv)
4.spi设备和驱动匹配过程
spi设备和驱动的匹配是由spi总线完成的,spi总线定义如下:
struct bus_type spi_bus_type = {
.name = "spi",
.dev_groups = spi_dev_groups,
.match = spi_match_device,
.uevent = spi_uevent,
};
EXPORT_SYMBOL_GPL(spi_bus_type);
spi_match_device就是匹配函数,分析如下:
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);
/* 设备树匹配方式 */
if (of_driver_match_device(dev,

本文深入探讨了Linux SPI驱动框架,包括SPI核心、主机控制器、设备及驱动的详细解析。并通过w25q32 NOR Flash的具体案例,展示了设备树配置、驱动编写及测试APP的全过程。
最低0.47元/天 解锁文章
5628

被折叠的 条评论
为什么被折叠?



