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

Linux驱动SPI-3-注册流程spi4

首先是gpio模拟的spi注册:

1.注册SPI总线类型和注册对应类。
[ 0.308540] spi_init: entered [ 0.308701] SPI bus type 'spi' registered [ 0.308768] SPI master class 'spi_master' registered

对应代码:

static int __init spi_init(void) { int status; printk(KERN_INFO "spi_init: entered\n"); buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL); if (!buf) { status = -ENOMEM; goto err0; } //注册 SPI 总线类型 status = bus_register(&spi_bus_type); if (status < 0) goto err1; printk(KERN_INFO "SPI bus type '%s' registered\n", spi_bus_type.name); //注册 SPI master类 status = class_register(&spi_master_class); if (status < 0) goto err2; printk(KERN_INFO "SPI master class '%s' registered\n", spi_master_class.name); /* 热插拔: 根据内核配置选项, 有条件地注册动态重新配置通知器,以便 SPI 子系统能够响应设备树(Device Tree) 或 ACPI 表的动态变化。*/ if (IS_ENABLED(CONFIG_OF_DYNAMIC)) WARN_ON(of_reconfig_notifier_register(&spi_of_notifier)); if (IS_ENABLED(CONFIG_ACPI)) WARN_ON(acpi_reconfig_notifier_register(&spi_acpi_notifier)); return 0; err2: bus_unregister(&spi_bus_type); err1: kfree(buf); buf = NULL; err0: return status; }
2.内核解析设备树,找到匹配的驱动后,调用probe

从设备树往下数,首先注册的是spi4:

设备树是:

spi4 { compatible = "spi-gpio"; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_spi4>; pinctrl-assert-gpios = <&gpio5 8 GPIO_ACTIVE_LOW>; status = "okay"; gpio-sck = <&gpio5 11 0>; gpio-mosi = <&gpio5 10 0>; cs-gpios = <&gpio5 7 0>; num-chipselects = <1>; #address-cells = <1>; #size-cells = <0>; gpio_spi: gpio_spi@0 { compatible = "fairchild,74hc595"; gpio-controller; #gpio-cells = <2>; reg = <0>; registers-number = <1>; registers-default = /bits/ 8 <0x57>; spi-max-frequency = <10000>; }; };

总线上挂载的只有一个74hc595,查了一下是扩展引脚的。没有输入,设备上也没配置输入。

static struct platform_driver spi_gpio_driver = { .driver = { .name = DRIVER_NAME, .of_match_table = of_match_ptr(spi_gpio_dt_ids), }, .probe = spi_gpio_probe, .remove = spi_gpio_remove, }; static const struct of_device_id spi_gpio_dt_ids[] = { { .compatible = "spi-gpio" }, {} };
static int spi_gpio_probe(struct platform_device *pdev)

这个函数大体干了什么:

从设备树节点中获取 GPIO 引脚配置并申请资源,配置master结构体各种标志位和回调函数
[ 2.302414] spi_gpio spi4: gpio-miso property not found, switching to no-rx mode [ 2.310158] spi_gpio spi4: spi_alloc_master enter! [ 2.315209] spi_master (null): spi_bitbang_start: entered (bus_num=-1)

master = spi_alloc_master(&pdev->dev, sizeof(*spi_gpio) +
(sizeof(unsigned long) * num_devices));

/* 相当于注册spi master设备:把回调函数都设置好,然后注册设备 */ int spi_bitbang_start(struct spi_bitbang *bitbang)

bitbang_start继续配置master各种回调函数,然后注册主设备spi_register_master。

此时还没分配总线。

3.分配主线,然后根据配置好的结构体注册主设备
int spi_register_master(struct spi_master *master) { static atomic_t dyn_bus_id = ATOMIC_INIT((1<<15) - 1); struct device *dev = master->dev.parent; struct boardinfo *bi; int status = -ENODEV; int dynamic = 0; if (!dev) return -ENODEV; dev_info(&master->dev,"is master->dev\n"); dev_info(dev,"is master->dev.parent\n"); dev_info(&master->dev, "spi_register_master: entered\n"); /* 解析设备树节点中的 cs-gpios 属性, 并将这些 GPIO 信息保存到 struct spi_master 中*/ status = of_spi_register_master(master); if (status) return status; dev_info(&master->dev, "cs-gpios get %d!\n",master->num_chipselect); /* even if it's just one always-selected device, there must * be at least one chipselect 如果没有片选引脚,直接报错 */ if (master->num_chipselect == 0) return -EINVAL; /* 静态分配:有设备节点且没有总线编号:分配一个 spiX的X*/ if ((master->bus_num < 0) && master->dev.of_node) master->bus_num = of_alias_get_id(master->dev.of_node, "spi"); dev_info(&master->dev, "master->bus_num get! %d \n", master->bus_num); /* convention: dynamically assigned bus IDs count down from the max 按照惯例,动态分配的总线 ID(bus_num)从最大值开始向下递减。 */ if (master->bus_num < 0) { /* FIXME switch to an IDR based scheme, something like * I2C now uses, so we can't run out of "dynamic" IDs 这是一个待改进的注释(FIXME),建议将来改用 IDR(ID 分配器)机制, 类似于 I2C 子系统现在的做法,这样可以避免“动态 ID”耗尽的问题 */ master->bus_num = atomic_dec_return(&dyn_bus_id); dynamic = 1; } /* 将 master->queue 初始化为一个空的双向循环链表头。*/ INIT_LIST_HEAD(&master->queue); /* 初始化一个自旋锁 queue_lock,用于保护对 master->queue 的并发访问。 初始化一个自旋锁 bus_lock_spinlock,用于保护 SPI 总线的短时原子访问。*/ spin_lock_init(&master->queue_lock); spin_lock_init(&master->bus_lock_spinlock); /* 初始化互斥锁 */ mutex_init(&master->bus_lock_mutex); mutex_init(&master->io_mutex); master->bus_lock_flag = 0; init_completion(&master->xfer_completion); /* 如果设备树没设置DMA最大长度,设置成默认最大 */ if (!master->max_dma_len) master->max_dma_len = INT_MAX; /* register the device, then userspace will see it. * registration fails if the bus ID is in use. */ /* 改名:这里将设备名格式化为 spi0 */ dev_info(&master->dev, "will be renamed \n"); dev_set_name(&master->dev, "spi%u", master->bus_num); dev_info(&master->dev, "is new name \n"); /* 将device注册到设备模型中 */ status = device_add(&master->dev); if (status < 0) goto done; dev_info(&master->dev, "has been device_add \n"); /* 注册成功后,使用 dev_dbg 输出调试日志,记录已注册的主控制器名称*/ dev_dbg(dev, "registered master %s%s\n", dev_name(&master->dev), dynamic ? " (dynamic)" : ""); dev_info(dev, "registered master %s%s\n", dev_name(&master->dev), dynamic ? " (dynamic)" : ""); /* If we're using a queued driver, start the queue */ /* 判断驱动类型并初始化传输队列 */ if (master->transfer) dev_info(dev, "master is unqueued, this is deprecated\n"); else { /* 启动队列机制 */ status = spi_master_initialize_queue(master); if (status) { device_del(&master->dev); goto done; } } /* add statistics 统计信息锁 记录该控制器的传输次数、字节数、错误计数等运行时统计信息 */ spin_lock_init(&master->statistics.lock); /* 板级描述(非设备树)的 SPI 从设备注册机制:自旋锁 */ mutex_lock(&board_lock); /* 将当前 SPI 主控制器添加到全局链表 spi_master_list 中 */ list_add_tail(&master->list, &spi_master_list); /* 历 board_list,该链表保存了通过传统板级代码 注册的 SPI 从设备信息(struct boardinfo*/ list_for_each_entry(bi, &board_list, list) /* 老方式注册从设备: 对每个板级信息,调用该函数检查当前 master 是否匹配 该从设备所指定的 SPI 总线号.如果匹配,注册一个动态从设备(绑定)*/ spi_match_master_to_boardinfo(master, &bi->board_info); mutex_unlock(&board_lock); /* Register devices from the device tree and ACPI 新方式注册从设备: 从设备树 和 ACPI 表中注册 SPI 从设备 */ of_register_spi_devices(master); acpi_register_spi_devices(master); done: return status; }
[ 2.321785] spi_master (null): is master->dev [ 2.326244] spi_gpio spi4: is master->dev.parent [ 2.330906] spi_master (null): spi_register_master: entered [ 2.336566] spi_master (null): of_spi_register_master enter! [ 2.342391] spi_master (null): cs-gpios get 1! [ 2.346935] spi_master (null): master->bus_num get! -19 /* 总线设备没有设置,所以转入动态分配了 */ [ 2.352289] spi_master (null): will be renamed [ 2.357004] spi_master spi32766: is new name [ 2.362151] spi_master spi32766: has been device_add /* 这步注册完后就能看见spi4设备了 */ [ 2.367327] spi_gpio spi4: registered master spi32766 (dynamic)
4.初始化spi master的消息队列机制,开启队列
[ 2.373290] spi_master spi32766: spi_master_initialize_queue enter! [ 2.379737] spi_master spi32766: set spi_transfer_one_message! [ 2.386026] spi_master spi32766: spi_init_queue! [ 2.390786] spi_master spi32766: spi_start_queue enter! [ 2.396304] spi_master spi32766: put pump_messages to kworker!
/* 初始化spi master的消息队列机制 */ static int spi_master_initialize_queue(struct spi_master *master) { int ret; dev_info(&master->dev, "spi_master_initialize_queue enter! \n"); master->transfer = spi_queued_transfer;//传输函数 if (!master->transfer_one_message) /* 硬件层传输函数 */ master->transfer_one_message = spi_transfer_one_message; dev_info(&master->dev, "set spi_transfer_one_message! \n"); /* Initialize and start queue 初始化队列数据结构 */ ret = spi_init_queue(master); if (ret) { dev_err(&master->dev, "problem initializing queue\n"); goto err_init_queue; } dev_info(&master->dev, "spi_init_queue! \n"); master->queued = true;//成功初始化标志 ret = spi_start_queue(master);//启动队列,启动失败就删除 if (ret) { dev_err(&master->dev, "problem starting queue\n"); goto err_start_queue; } return 0; err_start_queue: spi_destroy_queue(master); err_init_queue: return ret; } /* 开启工作线程:上锁,变量置位,开锁,把pump_messages工作扔给kworker*/ static int spi_start_queue(struct spi_master *master) { unsigned long flags; dev_info(&master->dev, "spi_start_queue enter! \n"); spin_lock_irqsave(&master->queue_lock, flags); if (master->running || master->busy) { spin_unlock_irqrestore(&master->queue_lock, flags); return -EBUSY; } master->running = true; master->cur_msg = NULL; spin_unlock_irqrestore(&master->queue_lock, flags); kthread_queue_work(&master->kworker, &master->pump_messages); dev_info(&master->dev, "put pump_messages to kworker! \n"); return 0; }
至此主设备注册和初始化所有需要配置的东西都配完了,下面是从设备树解析然后注册从设备。
5.查找设备树并注册从设备spi_device
[ 2.402274] spi_master spi32766: of_register_spi_devices enter! [ 2.408389] spi_master spi32766: of_register_spi_device enter! /* 这个函数中spi_add_device注册从设备然后等待驱动匹配 */ [ 2.414675] spi_master spi32766: find modalias for /spi4/gpio_spi@0 [ 2.420991] spi_master spi32766: /spi4/gpio_spi@0 has 'reg' property (0) spi->chip_select is 0 [ 2.429714] spi_master spi32766: /spi4/gpio_spi@0 has spi-max-frequency is 10000

/spi4/gpio_spi@0 就是驱动匹配的名字

reg就是片选名,从设备树上的reg取到,片选0

还有一些模式配置:clock phase/polarity,UAL/QUAD mode这些,还有频率

把这些都配置完就进入spi_add_device,往spi4虚拟总线上添加从设备。

注册从设备函数:

int spi_add_device(struct spi_device *spi)

在新建设备前要保证这个片选没有其他设备使用。设置好片选后使用spi_setup新建设备。

[ 2.437227] spi spi32766.0: spi_setup: entered [ 2.441718] spi spi32766.0: spi_gpio_setup: entered [ 2.446710] spi spi32766.0: spi_bitbang_setup: entered [ 2.451890] spi spi32766.0: spi_bitbang_setup_transfer: entered [ 2.457906] spi spi32766.0: spi_bitbang_setup, 100000 nsec/bit [ 2.463848] spi spi32766.0: setup mode 0, 8 bits/w, 10000 Hz max --> 0 /* dev_info(&spi->dev, "setup mode %d, %s%s%s%s%u bits/w, %u Hz max --> %d\n", (int) (spi->mode & (SPI_CPOL | SPI_CPHA)), (spi->mode & SPI_CS_HIGH) ? "cs_high, " : "", (spi->mode & SPI_LSB_FIRST) ? "lsb, " : "", (spi->mode & SPI_3WIRE) ? "3wire, " : "", (spi->mode & SPI_LOOP) ? "loopback, " : "", spi->bits_per_word, spi->max_speed_hz, status); */

spi_setup:对一个 SPI 设备(从设备)进行初始化设置,确保其工作参数正确并与 SPI 主机控制器兼容

spi_gpio_setup:初始化片选引脚,调用bitbang的setup

spi_bitbang_setup:根据模式设置bitbang->txrx_word消息传输方式,还有setup_transfer在每次发送/接收消息时对从设备的设置。

然后通过device_add将新建的设备加入到总线上去。

6.加入新的从设备后自动匹配驱动,根据驱动的probe重新设置一遍
[ 2.471135] spi spi32766.0: spi_match_device: matching driver 'mc13xxx' [ 2.477891] spi spi32766.0: trying ID table match... [ 2.483078] spi spi32766.0: spi_match_device: matching driver '74x164' [ 2.489706] spi spi32766.0: OF match succeeded //从设备树匹配74x164成功了

匹配上驱动后,根据驱动的probe,重新配置一遍设备(这个驱动在/drivers/gpio中)

static int gen_74x164_probe(struct spi_device *spi) { struct gen_74x164_chip *chip; u32 nregs; int ret; /* * bits_per_word cannot be configured in platform data */ spi->bits_per_word = 8; ret = spi_setup(spi); if (ret < 0) return ret; if (of_property_read_u32(spi->dev.of_node, "registers-number", &nregs)) { dev_err(&spi->dev, "Missing registers-number property in the DT.\n"); return -EINVAL; } chip = devm_kzalloc(&spi->dev, sizeof(*chip) + nregs, GFP_KERNEL); if (!chip) return -ENOMEM; spi_set_drvdata(spi, chip); chip->gpio_chip.label = spi->modalias; chip->gpio_chip.direction_output = gen_74x164_direction_output; chip->gpio_chip.get = gen_74x164_get_value; chip->gpio_chip.set = gen_74x164_set_value; chip->gpio_chip.set_multiple = gen_74x164_set_multiple; chip->gpio_chip.base = -1; chip->registers = nregs; chip->gpio_chip.ngpio = GEN_74X164_NUMBER_GPIOS * chip->registers; of_property_read_u8_array(spi->dev.of_node, "registers-default", chip->buffer, chip->registers); chip->gpio_chip.can_sleep = true; chip->gpio_chip.parent = &spi->dev; chip->gpio_chip.owner = THIS_MODULE; mutex_init(&chip->lock); ret = __gen_74x164_write_config(chip); if (ret) { dev_err(&spi->dev, "Failed writing: %d\n", ret); goto exit_destroy; } ret = gpiochip_add_data(&chip->gpio_chip, chip); if (!ret) return 0; exit_destroy: mutex_destroy(&chip->lock); return ret; }
[ 2.494533] 74x164 spi32766.0: spi_setup: entered [ 2.499282] 74x164 spi32766.0: spi_gpio_setup: entered [ 2.504631] 74x164 spi32766.0: spi_bitbang_setup: entered [ 2.510081] 74x164 spi32766.0: spi_bitbang_setup_transfer: entered [ 2.516370] 74x164 spi32766.0: spi_bitbang_setup, 100000 nsec/bit [ 2.522576] 74x164 spi32766.0: setup mode 0, 8 bits/w, 10000 Hz max --> 0

根据驱动配置好设备后,根据设备树配置发送0x57

[ 2.529481] spi_master spi32766: __spi_queued_transfer enter! pump mesg! [ 2.536386] 74x164 spi32766.0: spi_bitbang_setup_transfer: entered

注册完毕!

[ 2.543700] spi_master spi32766: spi_device registered! /spi4/gpio_spi@0
http://www.jsqmd.com/news/471592/

相关文章:

  • 势能法求解含齿根裂纹的直齿轮时变啮合刚度,根据万志国和梁新辉文献并结合其它文献采用MATLAB...
  • 高性能后台服务分级优化--百万级IO、千万级内存、亿级CPU的递进式优化思路
  • HarmonyOS开发指南:从入门到精通——聚焦APP、游戏与PC应用
  • 告别“塑料二次元”: 2D 角色 PBR 材质化与光追重构工作流
  • 换道轨迹预测:用LSTM模型捕捉车辆的“思考“过程
  • 复试第十一天
  • 【C++】lock_guard 与 unique_lock
  • 别再用串口屏蹲车间了!WPF .NET 8上位机实战:自定义仪表盘+LiveCharts2趋势图+SignalR远程运维,附6个工业级踩坑指南
  • 支付宝立减金回收黑科技!不用消费也能把钱揣进口袋 - 可可收
  • 基于ATP仿真建模的35kV与110kV变压器PT谐振过电压问题研究
  • PHP的多个账号使用同一手机号,收货地址怎么同步?
  • LabVIEW打造超酷液压泵试验台程序:功能全解析
  • 分析2026年江苏口碑不错的液氧、高纯氮、标准气工业气体厂家 - 工业品网
  • 做了8年工业上位机,我开源了这套通用框架!多设备/多协议10分钟快速对接,开箱可商用
  • 电网同步这事儿听着玄乎,其实就像给三相交流电装个节拍器。传统锁相环遇到电压波动就哆嗦,今天咱们聊点硬核的——怎么用d轴电压归一化让这个节拍器稳如老狗
  • 拒绝“实景贴皮感”:2D 角色摄影级实景融合与动态投射工作流
  • 用COMSOL拆解变电站的电场分布:从高压柜到电缆的实战指南
  • 西门子S7 - 1500博图程序在大型生产线中的实战例程分享
  • 北京/上海/深圳/杭州/南京/无锡高端腕表维修科普:品牌故障解析+正规门店指引 - 时光修表匠
  • 光伏储能微电网系统设计与实现
  • 【LLM进阶-RAG】2.切片算法策略
  • 分析初中毕业选学校服务,合肥南亚理工学校性价比高吗 - mypinpai
  • Cursor 自己写 Skill 完整教程
  • 毛囊干细胞源头厂家怎么选择,颐美康值得考虑吗? - 工业推荐榜
  • 汇川ISP500伺服控制器方案解析:从代码到实际应用
  • 篡改人才评估模型的技术漏洞与防御体系——软件测试工程师的伦理使命
  • 虹关、察关、凤山:一条古道串起的徽州春色
  • 分析2026年哈尔滨初高中文化课培训学校,哪家口碑更好 - mypinpai
  • 光伏硅片尺寸测量:Java+YOLO+OpenCV亚像素级检测,精度达0.01mm
  • 最近在研究CST仿真超表面,特别是聚焦和聚焦涡旋的效果,感觉挺有意思的。今天就来分享一下全流程的教学,顺便穿插一些代码和代码分析,希望能帮到有需要的朋友