Linux SPI 总线 和设备驱动架构之三:SPI控制器驱动

2023-11-08

通过第一篇文章,我们已经知道,整个SPI驱动架构可以分为协议驱动。通用接口层和控制器驱动三大部分。其中,控制器驱动负责最底层的数据收发工作,为了完成数据收发工作,控制器驱动需要完成以下这些功能:

1.申请必要的硬件资源,例如中断,DMA通道,DMA内存缓冲区等等;

2.配置SPI控制器的工作模式和参数,使之可以和相应的设备进行正确的数据交换工作;

3.向通用接口层提供接口,使得上层的协议驱动可以通过通用接口层访问控制器驱动;

4.配合通用接口层,完成数据消息队列的排队和处理,知道消息队列变为空为止;

定义控制器设备

SPI控制器遵循linux的设备模型框架,所以,一个SPI控制器在代码中对应一个device结构,对于嵌入式系统,我们通常把SPI控制器作为一个平台设备来对待,一般在设备树文件中会定义SPI控制器设备。这部分跟具体的SOC密切相关,这部分代码一般也有芯片厂商实现。

        spi0: spi@e0300000 {
                compatible = "actions,s700-spi";
                reg = <0 0xe0300000 0 0x1000>;
                interrupts = <GIC_SPI 19 IRQ_TYPE_LEVEL_HIGH>;
                #address-cells = <1>;
                #size-cells = <0>;
                clocks = <&clock CLK_SPI0>;
                clock-names = "spi0";
                status = "disabled";
        };

指定了SPI控制器的IO内存地址,中断线及中断类型,指定了clock及name

在内核解析设备树文件的时候,会把spi0注册为平台设备。根据linux设备驱动模型,有设备就会有与之对应的平台驱动,

注册SPI控制器的platform_driver

上一节中,我们知道设备树最终会把把SPI控制器注册为一个platform_device,相应的,对应的驱动就是一个平台驱动:platform_driver,他们通过platform bus进行相互匹配,以下代码来自drivers/spi/spi-owl.c中

static const struct of_device_id owl_spi_dt_ids[] = {
	{ .compatible = "actions,s900-spi" },
	{ .compatible = "actions,s700-spi" },
	{ .compatible = "actions,ats3605-spi" },
};

static struct platform_driver owl_spi_driver = {
	.probe = owl_spi_probe,
	.remove = owl_spi_remove,
	.driver = {
		.name = "spi-owl",
		.of_match_table = of_match_ptr(owl_spi_dt_ids),
	},
};

static int __init owl_spi_init(void)
{
	pr_info("[OWL] SPI controller initialization\n");

	return platform_driver_register(&owl_spi_driver);
}

subsys_initcall(owl_spi_init);

显然,在系统初始化阶段(subsys_initcall阶段)通过owl_spi_init(),注册了一个平台驱动,该驱动的名字正好也是owl_spi_driver,自然地,平台总线会把它和上一节中通过设备树注册的platform_device匹配,匹配上之后就会触发proble回调函数被调用,当然,这里的匹配是通过compatible字段进行匹配的。

注册spi_master

在linux设备模型看来,代表spi控制器的是第一节所定义的platform_device结构,但是对于SPI通用接口层来说,代表控制器的是spi_master结构。我们知道设备和驱动匹配上之后,驱动的probe回调函数就会被调用,而probe回调函数正是对驱动程序和设备进行初始化的合适时机。

static int owl_spi_probe(struct platform_device *pdev)
{
	struct device_node *np = pdev->dev.of_node;
	struct spi_master *master;
	struct owl_spi_data *aspi;
	struct resource *res;
	int ret, num_cs;
    
	master = spi_alloc_master(&pdev->dev, sizeof(*aspi));//分配spi_master结构
	if (!master)
		return -ENOMEM;

	aspi = spi_master_get_devdata(master);
	master->dev.of_node = pdev->dev.of_node;
	platform_set_drvdata(pdev, master);

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);//获取resource
	if (!res) {
		ret = -ENODEV;
		goto free_master;
	}
	aspi->phys = res->start;

	aspi->base = devm_ioremap_resource(&pdev->dev, res);
	if (IS_ERR(aspi->base)) {
		ret = PTR_ERR(aspi->base);
		goto free_master;
	}

	aspi->clk = devm_clk_get(&pdev->dev, NULL);//获取时钟
	if (IS_ERR(aspi->clk)) {
		dev_err(&pdev->dev, "spi clock not found.\n");
		ret = PTR_ERR(aspi->clk);
		goto free_master;
	}

	ret = clk_prepare_enable(aspi->clk);
	if (ret) {
		dev_err(&pdev->dev, "Unable to enable APB clock.\n");
		goto free_master;
	}

    aspi->dev = &pdev->dev;

	/* SPI controller initializations */
	owl_spi_init_hw(aspi);

	ret = of_property_read_u32(np, "num-cs", &num_cs);
	if (ret < 0)
		master->num_chipselect = 1;
	else
		master->num_chipselect = num_cs;

	master->setup = owl_spi_setup;  //设置spi_master
	master->cleanup = owl_spi_cleanup;
	master->transfer_one_message = owl_spi_transfer_one_message;
	master->bus_num = pdev->id;
	master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
	master->flags = 0;
	master->bits_per_word_mask = BIT(32 - 1) | BIT(16 - 1) | BIT(8 - 1);
	master->dev.of_node = np;

	aspi->master = master;

	init_completion(&aspi->xfer_completion);

	ret = spi_register_master(master);//向通用接口层注册spi_master结构
	if (ret) {
		dev_err(&pdev->dev, "cannot register SPI master\n");
		goto disable_clk;
	}

	return 0;

}

在上述函数中,除了完成必要的硬件资源初始化工作以外,最重要的工作就是通过spi_alloc_master函数分配了一个spi_master结构,初始化该结构,最终通过spi_register_master函数完成了对控制器的注册工作,从代码中我们可以看出,spi_master结构中的几个重要回调函数已经被赋值,这几个回调函数由通用接口层在合适的时机被调用,以便完成控制器和设备之间的数据交换。

实现spi_master结构的回调函数

事实上,SPI控制器驱动程序的主要工作,就是实现spi_master结构中的几个回调函数,其他的工作逻辑,均由通用接口层帮我完成,通用接口层会在合适时机调用这几个回调函数,这里简单介绍一下各个回调函数的作用,具体实现例子,请各位自行阅读代码树中的各个平台的例子(代码位于/drivers/spi/)

int            (*setup)(struct spi_device *spi);

当协议驱动希望修改控制器的工作模式或参数时,会调用通用接口层提供的API:spi_setup().该函数最后会调用setup回调函数来完成设置工作。

int            (*transfer)(struct spi_device *spi,  struct spi_message *mesg);

目前已经可以不用我们自己实现该回调函数,初始化时直接设为NULL即可,目前的通用接口层已经实现了消息队列化,注册spi_master时,通用接口层会提供实现好的通用函数,现在只有一些老的驱动还在使用该回调函数,新的驱动已经停止使用该回调函数,而是应该使用队列化的transfer_one_message回调,需要注意的是,我们只能选择其中一种方式设置了transfer_one_message回调,就不能设置transfer回调,反之亦然。

void            (*cleanup)(struct spi_device *spi);

当一个spi从设备(spi_device)被释放时,该回调函数会被调用,以便释放该从设备所占用的硬件资源。

int (*prepare_transfer_hardware)(struct spi_master *master);

int (*unprepare_transfer_hardware)(struct spi_master *master);

这两个回调函数用于在发起一个数据传送过程前和后,给控制器驱动一个机会,申请或释放某些必要的硬件资源,例如DMA资源和内存资源等等。

int (*transfer_one_message)(struct spi_master *master,struct spi_message *mesg);

当通用接口层发现master的队列中有消息要传送数据时,会调用该回调函数,所以该回调函数是真正完成一个消息传送的工作函数,当传送完成时,应该调用spi_finalize_current_message函数,以便通知通用接口层,发起队列中的下一个消息的传送工作。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux SPI 总线 和设备驱动架构之三:SPI控制器驱动 的相关文章

  • i.MX6U SPI浅析

    1 SPI简介 SPI 全称是 SerialPerripheral Interface xff0c 也就是串行外围设备接口 SPI 是 Motorola 公司推出的一种同步串行接口 技术 xff0c 是一种高速 全双工的同步通信总线 xff
  • Linux SPI 驱动示例

    一 Linux 下 SPI 驱动框架 SPI 驱动框架分为主机控制器驱动和设备驱动 xff0c 主机控制器也就是 SOC 的 SPI 控制器接口 1 1 SPI 主机驱动 SPI 主机驱动就是 SOC 的 SPI 控制器驱动 xff0c L
  • 关于I2C和SPI总线协议

    关于I2C和SPI总线协议 IICvs SPI 现今 xff0c 在低端数字通信应用领域 xff0c 我们随处可见IIC Inter Integrated Circuit 和 SPI Serial Peripheral Interface
  • 【STM32】基于SPI的OLED显示屏与DHT20温湿度采集显示数据

    STM32 基于SPI总线的OLED显示屏与DHT20温湿度采集显示数据 一 SPI通讯协议二 关于0 96英寸OLED模块三 硬件连接四 示例代码五 代码修改与撰写六 屏幕歌词滚动1 滚屏设置2 代码撰写 七 展示八 DHT20温湿度采集
  • spi,iic,uart,pcie区别

    一 spi SPI 是英语Serial Peripheral interface的缩写 xff0c 顾名思义就是串行外围设备接口 xff0c 是同步传输协议 xff0c 特征是 xff1a 设备有主机 xff08 master xff09
  • 通信方式梳理:GPIO,I2C,SPI,UART,USART,USB的区别

    GPIO xff0c I2C xff0c SPI xff0c UART xff0c USART xff0c USB的区别 1 简单区别 xff1a 1 xff09 GPIO xff08 General Purpose Input Outpu
  • SPI简介

    SPI全称是Serial Perripheral Interface xff0c 也就是串行外围设备接口 SPI是Motorola公司推出的一种同步串行接口技术 xff0c 是一种高速 xff0c 全双工的同步通信总线 SPI时钟频率相比I
  • 总线协议一(UART/RS232/RS485/IIC/SPI)

    目录 基础概述 xff1a 一 UART xff08 为串口通信方式 xff09 二 RS232协议 三 RS485协议 四 I2C总线协议 五 SPI总线 六 I2C和SPI的区别 基础概述 xff1a 总线的本质就是一根导线 xff0c
  • SPI总线协议基本原理及相关配置

    单片机应用中 xff0c 最常用的通信协议主要有三个 xff0c 即USART IIC和SPI 关于前两个的介绍在之前文章学习过 xff0c 这次介绍一下第三个通信协议 SPI SPI Serial Peripheral Interface
  • spi通信

    特点 常规四线通信方式 一根片选线 xff0c 一根时钟线 xff0c 两根数据线 xff1a 一根是主进从出线 xff0c 一根主出从入线 同步 xff0c 全双工 xff0c 通信方式 和谁通信就将谁的片选线拉低 xff0c 每增加一个
  • Single SPI、Dual SPI、Qaud SPI

    博主目前已经用上了 QSPI
  • 外设驱动库开发笔记6:AD719x系列ADC驱动

    前面我们讨论了AD7705这种ADC器件的驱动开发 在实际中我们使用更多的是AD719x系列的ADC芯片 包括有AD7191 AD7192和AD7193等 接下来我们就来设计并开发AD719x的驱动程序 1 功能概述 AD7192是一款适合
  • Pinctrl子系统之一了解基础概念

    1 Linux Pinctrl子系统简介 在许多soc内部都包含有pin控制器 通过pin控制器的寄存器 我们可以配置一个或者一组引脚的功能和特性 在软件方面 Linux内核提供了pinctrl子系统 目的是为了统一各soc厂商的pin脚管
  • 关于IKEv2中安全策略索引SPI的生成

    首先引入一个PF key的概念 PF KEY Key Management API 提供IKE模块和IPSec核心之间的接口 在RFC 2367中 有一个SADB GETSPI消息 这个消息就是实现允许一个进程获取SPI值 该值标识所给的s
  • micropython-SPI通讯

    micropython SPI通讯 1 什么是SPI 2 SPI通讯原理 3 Micropython中的SPI 4 ZTMR测试SPI 1 ZTMR中SPI引脚 2 ZTMRSPI自测 2 SPI 2板之间通讯测试 1 什么是SPI SPI
  • I2C与SPI通信总线协议

    仅以寄存器地址为8Bit的器件为例 例如MPU6500 LSM6DS3 I2C通信协议 I2C 的要点是了解I2C通信帧的组成部分 START起始位 STOP停止位 ACK NACK信号 从机器件地址 从机寄存器地址 I2C读的时序比较繁琐
  • 难懂?这样理解SPI与CAN很简单!

    难懂 这样理解SPI与CAN很简单 什么是串行通讯 为什么仍需使用串行通讯 SPI与CAN SPI 接口特点 CAN现场总线特点 什么是串行通讯 在正式进入主题前 我么先来介绍一下什么叫做 串行通信 串行通信是计算机的一种数据传输通信方式
  • CH347读写SPI Flash

    前面耽搁了几天 今天终于把CH347 SPI接口调试好了 CH347动态库中SPI接口函数如下 typedef struct SPI CONFIG UCHAR iMode 0 3 SPI Mode0 1 2 3 UCHAR iClock 0
  • 在 ARM 处理器上执行存储在外部 SPI 闪存中的程序

    我有一个 ARM 处理器 能够与外部闪存芯片连接 写入芯片的是为 ARM 架构编译的程序 可供执行 我需要知道如何将这些数据从外部闪存获取到 ARM 处理器上以供执行 我可以提前运行某种复制例程 将数据复制到可执行内存空间吗 我想我可以 但
  • STM32 传输结束时,循环 DMA 外设到存储器的行为如何?

    我想问一下 在以下情况下 STM32 中的 DMA SPI rx 会如何表现 我有一个指定的 例如 96 字节数组 名为 A 用于存储从 SPI 接收到的数据 我打开循环 SPI DMA 它对每个字节进行操作 配置为 96 字节 是否有可能

随机推荐

  • DBC文件的编辑方法

    1 启动candb 程序 File gt createdatabase选择模板 然后保存dbc文件 2 添加网络节点 在OverallView界面 右击Network nodes选择New弹出如下界面 设置节点名称 把前面编辑好的Messa
  • 一键提升多媒体内容质量:漫谈图像超分辨率技术

    看的一篇图像超分辨推文 在这里记录一下 方便后续查阅 作为将模糊的图像变清晰的神奇技术 图像超分辨率技术在游戏 电影 相机 医疗影像等多个领域都有广泛的应用 在这篇文章中 微软亚洲研究院的研究员们为你总结了图像超分辨率问题中的主流方法 现存
  • html原生js进度条圆形,原生JS环形进度条

    var bg document getElementById canvas1 var ctx bg getContext 2d ctx beginPath ctx lineWidth 10 ctx strokeStyle e5e5e5 va
  • jquery checkbox 设置选中和不选中

    2019独角兽企业重金招聘Python工程师标准 gt gt gt 设置选中 hasApply prop checked true 设置不选中 hasApply prop checked false 获取选中的状态 var status h
  • Active Directory的基本概念

    前言 本文是面对准备加入Active Directory编程的初学者的一份文章 主要是讲解Active Directory 活动目录 的一些概念和相关知识 这篇文章本来是不想写下来的 因为概念性内容的编写需要查阅很多的资料 也怕自己讲的不够
  • 计算机无法识别华为m3,华为M3平板怎么开启学生模式过滤不良网站?

    对于孩子使用电子设备这件事儿 家长们总会陷入到一种矛盾中 即 支持用吧 但是现如今信息复杂 孩子没有自控力 不能每天都盯着 监督不慎误入歧途怎么办 支持不用吧 但是上面又有很多资料资源 值得孩子们去学习 视频教学 益智科普类的软件层出不穷
  • layout_weight 标签用于在线性布局中指定父控件的剩余空间比例的分配

    layout weight 用于在线性布局中指定父控件的剩余空间比例的分配
  • 数学建模竞赛论文中的Word使用

    1 使用样式 除了Word原先所提供的标题 正文等样式外 还可以自定义样式 如果你发现自己是用选中文字然后用格式栏来设定格式的 一定要注意 想想其他地方是否需要相同的格式 如果是的话 最好就定义一个样式 对于相同排版表现的内容一定要坚持使用
  • 如何配置Vue脚手架(Vue CLI)

    第一步 用cmd打开命令提示符 输入命令行 npm config set registry https registry npm taobao org 第二步 打开cmd 输入命令行 npm install g vue cli 中途可能会出
  • MATLAB/simulink时域分析之性能指标(0基础)

    目录 6 时域分析 6 1 性能指标 6 1 1 典型输入信号 6 1 2 一阶系统时域响应 6 1 3 二阶系统时域响应 6 1 4 二阶系统的改善 6 时域分析 由于多数控制系统是以时间作为独立变量 所以人们往往关心输出对时间的响应 对
  • 刷脸支付创新高效促进消费者重新光临

    刷脸支付成为了移动金融产业新的焦点 这离不开代理的卖力推广 刷脸支付代理成为大多创业者的选择 从今往后 脸就是钱包走人寻常百姓家 再也不用担心发生突发情况 尴尬放回商品的局面 现在可以在便利店 部分夫妻店看见这样的画面 收银台不见了 换成了
  • python源码保护之cython

    转载请注明出处 准备 项目需要 是在windows7上操作 python3 7 针对python项目 而非单个的python程序 思路 先将py代码转成c代码 然后编译成pyd window上是pyd linux上是so 文件 安装cyth
  • base64转图片

    base64转图片 param base64Code base64码 public static void convertBase64ToImage String base64Code BufferedImage image byte im
  • Qt绘图与信号事件

    Qt应用开发的基本模式 面向对象 继承QDailog gkdialog h ifndef GK DIALOG H define GK DIALOG H include
  • 我的第一个python爬虫

    文章目录 前言 一 python爬虫是什么 二 豆瓣电影TOP250排行榜信息爬取 1 发送请求 2 获取数据 3 解析数据 4 保存数据 总结 前言 今天想跟大家分享下我完成第一个python爬虫项目的过程 同时记录自己的 第一次 我的第
  • windows下dll文件的创建详细教程

    1 前言 dll文件是啥 就不作过多赘述了 现在直接教大家如何创建与使用dll文件 本文基于windows系统 使用的编译相关工具为visual studio 2019 2 创建dll 2 1 创建dll工程 首先打开visual stud
  • LaTex使用技巧20:LaTex修改公式的编号和最后一行对齐

    写论文发现公式编号的格式不对 要求是如果是多行的公式 公式编号和公式的最后一行对齐 我原来使用的是 equation 环境 begin equation begin aligned a b c c d end aligned end equ
  • Unity中相机拍照并保存下来脚本

    以下是一个示例的Unity拍照脚本 用于拍摄相机看到的内容并保存在工程根目录下 using System using UnityEngine using System IO public class CameraCapture MonoBe
  • 八邻域断点检测

    八邻域断点检测 本文的理论思想主要来源大家可以参照 迈克老狼2012 OpenCV学习 13 细化算法 1 本文是我自己尝试着将八邻域的细化思想 运用到断点检测上 个人觉得其实仅仅是八邻域应用的一小方面大家可以尝试着往其他方面应用 其实相对
  • Linux SPI 总线 和设备驱动架构之三:SPI控制器驱动

    通过第一篇文章 我们已经知道 整个SPI驱动架构可以分为协议驱动 通用接口层和控制器驱动三大部分 其中 控制器驱动负责最底层的数据收发工作 为了完成数据收发工作 控制器驱动需要完成以下这些功能 1 申请必要的硬件资源 例如中断 DMA通道