当前位置: 首页 > news >正文

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

文章目录

  • 1、前言
  • 2、SPI总线注册
  • 3、SPI设备注册
  • 4、SPI驱动注册
  • 5、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设备。

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,结构体如下:

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;
#elses8			unused_native_cs;s8			max_native_cs;
#endifstruct 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控制器:

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

然后使用如下函数注册:

int devm_spi_register_controller(struct device *dev,struct spi_controller *ctlr)

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

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

下面贴出dummy-spimaster.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 levelprintk(KERN_INFO "spi cs[%d]: high\n", spi->chip_select);}else {//set IO(spi->chip_select) low levelprintk(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.pmmaster->num_chipselect = 4;master->transfer_one = dummy_spimaster_transfer_one;master->setup = dummy_spimaster_setup;master->max_speed_hz = 100 * 1000 * 1000; //100MHzmaster->min_speed_hz = 3 * 1000;    //3kHzmaster->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("1477153217@qq.com");
MODULE_VERSION("0.1");
MODULE_DESCRIPTION("dummy spi controller driver test");

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设备驱动程序:

相关文章:

  • C# 程序结构||C# 基本语法
  • 类的生命周期
  • YOLOv2训练详细实践指南
  • C++开发中的DUMP文件:解决崩溃与性能问题的利器(全文字数2w+)
  • 时间序列:A TIME SERIES IS WORTH 64 WORDS: LONG-TERM FORECASTING WITH TRANSFORMERS
  • 【实战中提升自己】 防火墙篇之VPX部署–L2TP over IPSEC
  • CTF--eval
  • 控制反转(IoC)和依赖注入(DI)实现及常用注解
  • 怎样利用 macOS 自带功能快速进行批量重命名文件教程
  • 服务器内存规格详解
  • 饭店管理系统(下篇):程序打包为exe给用户使用
  • 2. kubernetes操作概览
  • Gradle相关配置文件的关系、作用及使用方式
  • 【时时三省】(C语言基础)选择结构程序设计习题1
  • Python异步编程入门:Async/Await实战详解
  • vector常用的接口和底层
  • AI对话高阶玩法:解锁模型潜能的实用案例教程
  • 消息中间件面试题
  • 开源TTS项目GPT-SoVITS,支持跨语言合成、支持多语言~
  • java面向对象06:封装
  • 山西一国道发生塌陷,造成4车追尾2人死亡
  • 孙颖莎4比1击败陈幸同,与蒯曼会师澳门世界杯女单决赛
  • 从黄仁勋到美国消费者,都在“突围”
  • 央视网评论员:婚约不是性许可——山西订婚强奸案背后的性教育盲区
  • 南华期货递表港交所,冲刺第二家“A+H”股上市期货公司
  • 推动中阿合作“向新而行”,这场论坛在上海松江举行