目录

1. 前言

2. SPI控制器驱动

2.1 dts

2.2 匹配注册

2.3 probe函数

2.4 数据收发函数:spi_st_transfer_one

2.5 中断处理程序:spi_st_irq


1. 前言

SPI控制器驱动是对SPI硬件体系结构中控制器端的实现,本文以路径为drivers/spi/spi-st-ssc4.c的SPI控制器驱动为例,简单分析下SPI控制器驱动。

2. SPI控制器驱动

2.1 dts

spi@9840000 {compatible = "st,comms-ssc4-spi";reg = <0x9840000 0x110>;interrupts = <GIC_SPI 112 IRQ_TYPE_LEVEL_HIGH>;clocks = <&clk_s_c0_flexgen CLK_EXT2F_A9>;clock-names = "ssc";pinctrl-0 = <&pinctrl_spi0_default>;pinctrl-names = "default";#address-cells = <1>;#size-cells = <0>;status = "disabled";
};

SPI控制器驱动一般采用platform设备模型编写并注册进内核,在采用dts描述设备的情况下,struct platform_device结构体在系统解析dts时自动生成,无需自己定义。

上面的dts节点就是与控制器驱动匹配的节点,分别描述了用来与驱动匹配compatible字符串、控制器I/O地址、控制器中断、控制器时钟、 控制器时钟名字、管脚复用配置、管脚复用配置的名字、状态。

2.2 匹配注册

static const struct dev_pm_ops spi_st_pm = {                     //1SET_SYSTEM_SLEEP_PM_OPS(spi_st_suspend, spi_st_resume)SET_RUNTIME_PM_OPS(spi_st_runtime_suspend, spi_st_runtime_resume, NULL)
};static const struct of_device_id stm_spi_match[] = {             //2{ .compatible = "st,comms-ssc4-spi", },{},
};
MODULE_DEVICE_TABLE(of, stm_spi_match);static struct platform_driver spi_st_driver = {                  //3.driver = {.name = "spi-st",.pm = &spi_st_pm,.of_match_table = of_match_ptr(stm_spi_match),},.probe = spi_st_probe,.remove = spi_st_remove,
};
module_platform_driver(spi_st_driver);                           //4

1. 电源管理相关操作。

2. 与dts的compatible属性进行比较,相同则调用probe函数。

3. struct platform_driver结构体,对必要的成员进行初始化,如of_match_table、probe函数、remove函数。

4. platform设备注册、注销方法。

2.3 probe函数

static int spi_st_probe(struct platform_device *pdev)
{struct device_node *np = pdev->dev.of_node;struct spi_master *master;struct resource *res;struct spi_st *spi_st;int irq, ret = 0;u32 var;master = spi_alloc_master(&pdev->dev, sizeof(*spi_st));                  //1if (!master)return -ENOMEM;master->dev.of_node        = np;master->mode_bits      = MODEBITS;master->setup            = spi_st_setup;master->cleanup          = spi_st_cleanup;master->transfer_one       = spi_st_transfer_one;                       //2master->bits_per_word_mask  = SPI_BPW_MASK(8) | SPI_BPW_MASK(16);master->auto_runtime_pm        = true;master->bus_num          = pdev->id;spi_st               = spi_master_get_devdata(master);                    //3spi_st->clk = devm_clk_get(&pdev->dev, "ssc");                           //4if (IS_ERR(spi_st->clk)) {dev_err(&pdev->dev, "Unable to request clock\n");ret = PTR_ERR(spi_st->clk);goto put_master;}ret = clk_prepare_enable(spi_st->clk);                                   //5if (ret)goto put_master;init_completion(&spi_st->done);                                          //6/* Get resources */res = platform_get_resource(pdev, IORESOURCE_MEM, 0);                    //7spi_st->base = devm_ioremap_resource(&pdev->dev, res);if (IS_ERR(spi_st->base)) {ret = PTR_ERR(spi_st->base);goto clk_disable;}/* Disable I2C and Reset SSC */writel_relaxed(0x0, spi_st->base + SSC_I2C);                            //8var = readw_relaxed(spi_st->base + SSC_CTL);var |= SSC_CTL_SR;writel_relaxed(var, spi_st->base + SSC_CTL);udelay(1);var = readl_relaxed(spi_st->base + SSC_CTL);var &= ~SSC_CTL_SR;writel_relaxed(var, spi_st->base + SSC_CTL);/* Set SSC into slave mode before reconfiguring PIO pins */             //9var = readl_relaxed(spi_st->base + SSC_CTL);var &= ~SSC_CTL_MS;writel_relaxed(var, spi_st->base + SSC_CTL);irq = irq_of_parse_and_map(np, 0);                                      //10if (!irq) {dev_err(&pdev->dev, "IRQ missing or invalid\n");ret = -EINVAL;goto clk_disable;}ret = devm_request_irq(&pdev->dev, irq, spi_st_irq, 0,                  //11pdev->name, spi_st);if (ret) {dev_err(&pdev->dev, "Failed to request irq %d\n", irq);goto clk_disable;}/* by default the device is on */pm_runtime_set_active(&pdev->dev);pm_runtime_enable(&pdev->dev);platform_set_drvdata(pdev, master);                                     //12ret = devm_spi_register_master(&pdev->dev, master);                     //13if (ret) {dev_err(&pdev->dev, "Failed to register master\n");goto rpm_disable;}return 0;rpm_disable:pm_runtime_disable(&pdev->dev);
clk_disable:clk_disable_unprepare(spi_st->clk);
put_master:spi_master_put(master);return ret;
}

1. 申请struct spi_master内存。

2. 初始化spi_master中的成员函数transfer_one,如果控制器驱动不实现transfer和transfer_one_message,内核会自动填充默认的spi_queued_transfer函数,最终控制器驱动只需要实现transfer_one函数。

3. 获取私有数据,大小为sizeof(*spi_st)。

4. 获取时钟名为“ssc”的控制器时钟。

5. 时钟使能。

6. 初始化completion。

7. 获取到控制器的I/O地址并映射为虚拟地址。

8. 禁用I2C并重置SSC。

9. 在重新配置PIO引脚之前将SSC设置为从模式。

10. 获取中断号。

11. 申请中断并设置中断服务程序。

12. 将struct spi_master设置为platform_device的私有数据。

13. 将初始化后的spi_master注册进内核。

2.4 数据收发函数:spi_st_transfer_one

static int spi_st_transfer_one(struct spi_master *master,struct spi_device *spi, struct spi_transfer *t)
{struct spi_st *spi_st = spi_master_get_devdata(master);uint32_t ctl = 0;spi_st->tx_ptr = t->tx_buf;                                         //1spi_st->rx_ptr = t->rx_buf;if (spi->bits_per_word > 8) {                                       //2spi_st->bytes_per_word = 2;spi_st->words_remaining = t->len / 2;} else if (spi->bits_per_word == 8 && !(t->len & 0x1)) {spi_st->bytes_per_word = 2;spi_st->words_remaining = t->len / 2;ctl = readl_relaxed(spi_st->base + SSC_CTL);writel_relaxed((ctl | 0xf), spi_st->base + SSC_CTL);readl_relaxed(spi_st->base + SSC_RBUF);} else {spi_st->bytes_per_word = 1;spi_st->words_remaining = t->len;}reinit_completion(&spi_st->done);                                   //3ssc_write_tx_fifo(spi_st);                                          //4writel_relaxed(SSC_IEN_TEEN, spi_st->base + SSC_IEN);wait_for_completion(&spi_st->done);                                 //5if (ctl)                                                            //6writel_relaxed(ctl, spi_st->base + SSC_CTL);spi_finalize_current_transfer(spi->master);                         //7return t->len;
}

1. 设置传输(tx buf以及rx buf)。

2. 如果字大于8bit,则任何大于8位/字的内容都需要RX/TX缓冲区中的2个字节/字,如果传输是偶数长度和8位/字,则实现为半长16位/字传输,并将SSC_CTL设置为16位/字,其余情况设置spi_st->bytes_per_word = 1,spi_st->words_remaining = t->len。

3. 重新初始化completion。

4. 通过写入tx fifo开始传输。

5. 等待传输完成。

6. 必要时恢复SSC_CTL。

7. 通知一次传输的完成。

2.5 中断处理程序:spi_st_irq

static irqreturn_t spi_st_irq(int irq, void *dev_id)
{struct spi_st *spi_st = (struct spi_st *)dev_id;ssc_read_rx_fifo(spi_st);                                  //1if (spi_st->words_remaining) {                             //2ssc_write_tx_fifo(spi_st);} else {writel_relaxed(0x0, spi_st->base + SSC_IEN);readl(spi_st->base + SSC_IEN);complete(&spi_st->done);}return IRQ_HANDLED;
}

当tx移位寄存器为空时触发中断。

1. 读取rx fifo。

2. 如果spi_st->words_remaining不为0,通过写入tx fifo开始传输,否则读取SSC_IEN以确保在重新启用中断之前设置该位,通知一次传输的完成。

Linux SPI子系统(3):SPI控制器驱动层相关推荐

  1. Linux SPI驱动框架(2)——控制器驱动层

    SPI控制器驱动层   上节中,讲了SPI核心层的东西,这一部分,以全志平台SPI控制器驱动为例,对SPI控制器驱动进行说明. SPI控制器驱动,即SPI硬件控制器对应的驱动,核心部分需要实现硬件SP ...

  2. Linux内核USB总线--设备控制器驱动框架分析

    正文 1.概述 如下图所示,USB控制器可以呈现出两种不同的状态.USB控制器作为Host时,称为USB主机控制器,使用USB主机控制器驱动.USB控制器作为Device时,称为USB设备控制器,使用 ...

  3. Linux I2C子系统分析-I2C设备驱动

    接下来以一个实际的例子来看I2C设备驱动,就以drivers/i2c/i2c-dev.c为例. 先看它的初始化和注销函数 [cpp] view plaincopy static int __init  ...

  4. Linux I2C子系统分析-I2C总线驱动

    在drivers/i2c/busses下包含各种I2C总线驱动,如S3C2440的I2C总线驱动i2c-s3c2410.c,使用GPIO模拟I2C总线的驱动i2c-gpio.c,这里只分析i2c-gp ...

  5. Linux SPI驱动框架(3)——设备驱动层

    SPI设备驱动层   Linux SPI驱动框架(1)和(2)中分别介绍了SPI框架中核心层,和控制器驱动层.其实实际开发过程中,不是IC原厂工程师比较少会接触控制器驱动层,设备驱动层才是接触比较多的 ...

  6. Linux SPI 子系统(x86平台)

    Linux SPI 子系统(x86平台) 文章目录 Linux SPI 子系统(x86平台) 前言 总述 SPI 硬件系统与软件抽象之间的关系 SPI 驱动的 Probe 和 Match 过程 SPI ...

  7. linux鼠标驱动程序,Linux usb子系统(一) _写一个usb鼠标驱动

    USB总线是一种典型的热插拔的总线标准,由于其优异的性能几乎成为了当下大小设备中的标配. USB的驱动可以分为3类:SoC的USB控制器的驱动,主机端USB设备的驱动,设备上的USB Gadget驱动 ...

  8. 137.Linux输入子系统基本概念

    文章目录 0.前言 1.简介 2.框架 3.输入子系统核心层 4.输入子系统驱动编写 4.1 基本变量 4.2 input_dev的注册 4.2.1 设置事件值的方法代码 4.3 上报输入事件 4.4 ...

  9. Linux input 子系统详解

    1. 模块概述 1.1.相关资料和代码研究 drivers/input/ include/uapi/linux/input-event-codes.h 2. 模块功能 linux核心的输入框架 3. ...

最新文章

  1. java 对象初始化属性,JAVA中类属性的初始化
  2. DBA(三):MySQL主从同步、复制模式
  3. IDEA之过滤那些不重要的文件
  4. --SQL code# --创建表及字段描述信息
  5. Fortinet 荣膺谷歌云年度安全技术合作伙伴奖
  6. iphone固件降级_iPhone无法开机怎么办?三种快速维修方法
  7. kibana创建es索引_java操作es动态创建索引(按月生成),索引类型,索引别名
  8. 逼自己玩命学了3个多月,吃透这19个架构视频!分享给你,让你今年进个大厂!(限时领)...
  9. Redis的RDB AOF DATABASE
  10. java awt区域,构建Java Swing中的区域图
  11. edius隐藏快捷键_Edius常用快捷键
  12. js中定义变量时单引号和双引号的区别
  13. 计算机win7设置用户密码,怎么给win7电脑设置开机密码_w7电脑开机密码怎么设置...
  14. 小程序、容器、SCF、直播加速…最全面的云端架构技术揭秘
  15. antdownload百度网盘下载器、下载不限速
  16. Madgwick AHRS算法笔记
  17. LED及LCD冲突问题解决及LCD的驱动改进(蓝桥杯嵌入式stm32G431RBT6)
  18. 2022杭电多校4 G - Climb Stairs
  19. 深入理解工具链-自己搭建STM32编程IDE
  20. Openwrt Lede koolshare固件下屏蔽固定MAC地址以及屏蔽某些网站

热门文章

  1. 人工智能岗位可以考什么证书?考试难不难?
  2. 虚拟化服务器查看密码策略,Windows Server 2012 RemoteApp体验-通过RD Web修改用户密码...
  3. java五子棋核心算法_五子棋的核心算法 | 学步园
  4. 在复杂交通环境下智能汽车行驶风险评估与智能决策——王建强
  5. SIwave仿真手册——电源完整性仿真之PDN阻抗的提取(四)
  6. 191_Ubuntu 18.04安装Samba服务器及配置
  7. 基于SpringBoot的便捷网住宿预约系统的设计与实现
  8. Linux惊群效应之Nginx解决方案
  9. 包工协议书样本_工程清包工合同协议书样本
  10. Excel如何直接应用主题效果美化工作表