Linux驱动开发进阶(九)- SPI子系统BSP驱动

文章目录

1、前言

  1. 学习参考书籍以及本文涉及的示例程序:李山文的《Linux驱动开发进阶》
  2. 本文属于个人学习后的总结,不太具备教学功能。

2、SPI总线注册

驱动源码文件:drivers/spi/spi.c,负责注册spi总线和spi设备以及spi控制器设备:

上面的spi_init是整个总线注册的入口,但是没有spi_exit出口函数。这一点很多驱动的总线也是这样,只有注册,没有注销,这是因为总线会一直存在,只有当系统关闭时,总线才不需要了。相关的spi总线结构体如下:

除了注册bus,还注册了class:

相关class结构体为:

注册类后,会在/sys/class目录下生成spi_master文件夹,而dev_groups会在/sys/class/spi_master目录下生成相关的属性文件分组目录。

3、SPI设备注册

在注册SPI控制器驱动时,此时会扫描设备树中的spi控制器节点,找到匹配的,则会注册成设备,并且会将控制器设备树节点下的子节点全部注册为spi设备。如下代码所示,spi注册控制器时,在最后会调用of_register_spi_devices函数来解析设备树,然后将其注册为spi设备。

c 复制代码
int spi_register_controller(struct spi_controller *ctlr)
{
    
    ...
        
	/* Register devices from the device tree and ACPI */
	of_register_spi_devices(ctlr);	// 将spi子设备注册进spi总线
	acpi_register_spi_devices(ctlr);
	return status;

    ...
}

4、SPI驱动注册

SPI驱动分为控制器驱动和设备驱动,其中控制器驱动又称为BSP驱动程序,该驱动程序一般由芯片原厂的驱动工程师来编写,而设备驱动程序指对芯片的外部设备来编写驱动。

先看SPI控制器驱动的注册,该驱动注册由platform总线注册,这是因为在SPI控制器驱动注册前,SPI的设备驱动无法注册,原因就是SPI子设备驱动的注册依赖于SPI控制器驱动,只有当SPI控制器驱动注册完毕后,module_spi_driver宏才能使用(实际上是spi_register_driver和spi_unregister_driver函数)。所以SPI控制器驱动注册只能依靠platform平台总线注册,因为platform平台总线驱动编译到内核中的,一定会注册, 即module_platform_driver宏一定是可以使用的。

5、SPI BSP驱动

spi bsp驱动其实就是编写spi的控制器驱动。linux中spi控制器被抽象为spi_master或者spi_controller,在bsp中,我们常用spi_master,结构体如下:

c 复制代码
struct spi_controller {
	struct device	dev;
	struct list_head list;
	s16			bus_num;
	u16			num_chipselect;
	u16			dma_alignment;
	u32			mode_bits;
	u32			buswidth_override_bits;
	u32			bits_per_word_mask;
#define SPI_BPW_MASK(bits) BIT((bits) - 1)
#define SPI_BPW_RANGE_MASK(min, max) GENMASK((max) - 1, (min) - 1)
	u32			min_speed_hz;
	u32			max_speed_hz;
	u16			flags;
#define SPI_CONTROLLER_HALF_DUPLEX	BIT(0)	/* can't do full duplex */
#define SPI_CONTROLLER_NO_RX		BIT(1)	/* can't do buffer read */
#define SPI_CONTROLLER_NO_TX		BIT(2)	/* can't do buffer write */
#define SPI_CONTROLLER_MUST_RX		BIT(3)	/* requires rx */
#define SPI_CONTROLLER_MUST_TX		BIT(4)	/* requires tx */
#define SPI_MASTER_GPIO_SS		BIT(5)	/* GPIO CS must select slave */
	bool			slave;
	size_t (*max_transfer_size)(struct spi_device *spi);
	size_t (*max_message_size)(struct spi_device *spi);
	struct mutex		io_mutex;
	spinlock_t		bus_lock_spinlock;
	struct mutex		bus_lock_mutex;
	bool			bus_lock_flag;
	int			(*setup)(struct spi_device *spi);
	int (*set_cs_timing)(struct spi_device *spi, struct spi_delay *setup,
			     struct spi_delay *hold, struct spi_delay *inactive);
	int			(*transfer)(struct spi_device *spi,
						struct spi_message *mesg);
	void			(*cleanup)(struct spi_device *spi);
	bool			(*can_dma)(struct spi_controller *ctlr,
					   struct spi_device *spi,
					   struct spi_transfer *xfer);
	bool				queued;
	struct kthread_worker		*kworker;
	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;
	bool				last_cs_enable;
	bool				last_cs_mode_high;
	bool                            fallback;
	struct completion               xfer_completion;
	size_t				max_dma_len;
	int (*prepare_transfer_hardware)(struct spi_controller *ctlr);
	int (*transfer_one_message)(struct spi_controller *ctlr,
				    struct spi_message *mesg);
	int (*unprepare_transfer_hardware)(struct spi_controller *ctlr);
	int (*prepare_message)(struct spi_controller *ctlr,
			       struct spi_message *message);
	int (*unprepare_message)(struct spi_controller *ctlr,
				 struct spi_message *message);
	int (*slave_abort)(struct spi_controller *ctlr);
	void (*set_cs)(struct spi_device *spi, bool enable);
	int (*transfer_one)(struct spi_controller *ctlr, struct spi_device *spi,
			    struct spi_transfer *transfer);
	void (*handle_err)(struct spi_controller *ctlr,
			   struct spi_message *message);
	const struct spi_controller_mem_ops *mem_ops;
	struct spi_delay	cs_setup;
	struct spi_delay	cs_hold;
	struct spi_delay	cs_inactive;
	int			*cs_gpios;
	struct gpio_desc	**cs_gpiods;
	bool			use_gpio_descriptors;
#ifdef __GENKSYMS__
	u8			unused_native_cs;
	u8			max_native_cs;
#else
	s8			unused_native_cs;
	s8			max_native_cs;
#endif
	struct spi_statistics	statistics;
	struct dma_chan		*dma_tx;
	struct dma_chan		*dma_rx;
	void			*dummy_rx;
	void			*dummy_tx;
	int (*fw_translate_cs)(struct spi_controller *ctlr, unsigned cs);
	bool			ptp_sts_supported;
	unsigned long		irq_flags;
	ANDROID_KABI_RESERVE(1);
	ANDROID_KABI_RESERVE(2);
};

以下列出几个比较重要字段的解释,后面会重点看看transfer_one_message字段,该字段实现真实数据的传输:

linux提供了一个函数来分配一个spi控制器:

c 复制代码
static inline struct spi_controller *spi_alloc_master(struct device *host,
						      unsigned int size)
{
	return __spi_alloc_controller(host, size, false);
}

然后使用如下函数注册:

c 复制代码
int devm_spi_register_controller(struct device *dev,
				 struct spi_controller *ctlr)

对于bsp而言,在使用前必须开启spi时钟。如下所示,设备树中spi时钟属性,用来指定时钟源:

spi bsp示例程序可以参考:李山文的《Linux驱动开发进阶》spi子系统示例程序

下面贴出dummy-spimaster.c

c 复制代码
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/spi/spi.h>
#include <linux/sysfs.h>
#include <linux/slab.h>

static int dummy_spimaster_setup(struct spi_device *spi)
{
    int ret = 0;
    if(spi->chip_select >= spi->master->num_chipselect)
    {
        printk(KERN_INFO " invalid chip_select\n");
        return -1;
    }
    return ret;
}

static void dummy_spimaster_set_cs(struct spi_device *spi, bool enable)
{
	if (enable) {
        //set IO(spi->chip_select) high level
        printk(KERN_INFO "spi cs[%d]: high\n", spi->chip_select);
    }
    else {
        //set IO(spi->chip_select) low level
        printk(KERN_INFO "spi cs[%d]: low\n", spi->chip_select);
    }
}

static int dummy_spimaster_transfer_one(struct spi_master *master, struct spi_device *spi, struct spi_transfer *tfr)
{
    int bytes;
    unsigned char *rx_buff = tfr->rx_buf;
    const unsigned char *tx_buff = tfr->tx_buf;
    bytes = tfr->len;
    while(bytes) {
        if(tx_buff) {
            printk(KERN_INFO "TX: %c \n", *tx_buff);
            tx_buff ++ ;
        }
        if(rx_buff) {
            *rx_buff = 'a';//读取时每次获取'a'
            rx_buff ++ ;
        }
        bytes -- ;
    }
    return 0;
}

static int dummy_spimaster_probe(struct platform_device *pdev)
{
    int ret;
    struct spi_master *master;

    master = spi_alloc_master(&pdev->dev, 0);
    if (!master) 
    {
        printk(KERN_ERR "unable to alloc SPI master\n");
        return -EINVAL;
    }
    master->dev.of_node = pdev->dev.of_node;
    master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST;
    master->bus_num = -1;
    master->auto_runtime_pm = false;//不支持PM,如果需要支持,则需要实现platform_driver中.driver.pm
    master->num_chipselect = 4;
    master->transfer_one = dummy_spimaster_transfer_one;
    master->setup = dummy_spimaster_setup;
	master->max_speed_hz = 100 * 1000 * 1000; //100MHz
	master->min_speed_hz = 3 * 1000;    //3kHz
	master->set_cs = dummy_spimaster_set_cs;
	ret = devm_spi_register_master(&pdev->dev, master);
	if (ret) {
		dev_err(&pdev->dev, "spi register master failed!\n");
		spi_master_put(master);
		return ret;
	}
    platform_set_drvdata(pdev, master);
    return 0;
} 

static int dummy_spimaster_remove(struct platform_device *pdev)
{
    struct spi_master	*master = platform_get_drvdata(pdev);
    spi_unregister_master(master);
    printk("%s:%d\n", __FUNCTION__, __LINE__);
    return 0;
}

static const struct of_device_id dummy_spimaster_match[] = {
	{ .compatible = "dummy-spi-master", },
	{}
};
MODULE_DEVICE_TABLE(of, dummy_spimaster_match);

static struct platform_driver dummy_spimaster_driver = {
    .driver = {
        .name = "dummy_spimaster_driver",
        .owner = THIS_MODULE,
        .of_match_table = dummy_spimaster_match,
    },
    .probe = dummy_spimaster_probe,
    .remove = dummy_spimaster_remove,
    //.driver.pm = 
};

module_platform_driver(dummy_spimaster_driver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("[email protected]");
MODULE_VERSION("0.1");
MODULE_DESCRIPTION("dummy spi controller driver test");
c 复制代码
static int dummy_spimaster_transfer_one(struct spi_master *master, struct spi_device *spi, struct spi_transfer *tfr)
{
    int bytes;
    unsigned char *rx_buff = tfr->rx_buf;
    const unsigned char *tx_buff = tfr->tx_buf;
    bytes = tfr->len;
    while(bytes) {
        if(tx_buff) {
            printk(KERN_INFO "TX: %c \n", *tx_buff);
            tx_buff ++ ;
        }
        if(rx_buff) {
            *rx_buff = 'a';//读取时每次获取'a'
            rx_buff ++ ;
        }
        bytes -- ;
    }
    return 0;
}

接下来再看spi设备驱动程序:

相关推荐
浪淘沙jkp17 分钟前
AI大模型学习十:‌Ubuntu 22.04.5 调整根目录大小,解决根目录磁盘不够问题
linux·学习·ubuntu
啊吧怪不啊吧40 分钟前
Linux常见指令介绍上(入门级)
linux·运维·服务器
菜狗想要变强1 小时前
RVOS-7.实现抢占式多任务
linux·c语言·驱动开发·单片机·嵌入式硬件·risc-v
ALex_zry1 小时前
从源码到实战:深度解析`rsync`增量同步机制与高级应用
linux·网络·运维开发
余辉zmh2 小时前
【Linux系统篇】:从匿名管道到命名管道--如何理解进程通信中的管道?
linux·运维·microsoft
程序设计实验室2 小时前
Traefik,想说爱你不容易:一场动态反向代理的心累之旅
linux·docker·devops·traefik·caddy
就新年快乐吧2 小时前
【HD-RK3576-PI】定制用户升级固件
linux
爬菜2 小时前
进程(完)
linux
Pseudo…3 小时前
linux Shell编程之函数与数组(四)
linux·运维·服务器
日日行不惧千万里3 小时前
远程登录一个Linux系统,如何用命令快速知道该系统属于Linux的哪个发行版,以及该服务器的各种配置参数,运行状态?
linux·运维·服务器