在本文中,我们将介绍关于spi-mem Linux内核框架的工作,该框架将允许在SPI NOR设备和常规SPI设备以及SPI NAND设备上复用SPI控制器驱动程序。
在过去,SPI是一个简单的协议,总线上的所有设备只共享3根信号线:
另外每个设备有一个独立信号线,用于选择我们想要通信的设备:
但随后SPI存储出现了。它从较小且相对较慢的SPI NORs开始,如dataflash、EEPROMs和SRAMs,然后逐渐发展到较大的SPI NORs和SPI NANDs。像往常一样,当涉及到处理存储时,我们希望得到最佳性能表现。SPI总线的限制很快成为瓶颈,因此供应商决定添加更多的I/O线路,并使 MISO/MOSI 线可以双向通信。现在我们看到SPI控制器支持最多8路I/O。这就是业内所说的DualSPI QuadSPI和OctoSPI。
为了在主从设备的数据传输中用上所有的I/O线,必须有某种主从设备之间的协议,这样双方才能知道,何时可以在I/O线上收发数据,应该使用多少根I/O线等。这些由一组从设备预定义的操作规定了如何进行,主设备必须遵循这组操作的规定,进入特定的发送或接收状态。SPI存储器操作通常包括:
请注意,虽然这个协议倾向于被用于存储设备,但并没有什么能限制它只能用于存储设备,如果一些FPGA使用相同的协议来操作非存储设备,我也不会感到惊讶。
Linux支持双线SPI和四线SPI模式已经有一段时间了(v3.12), SPI设备驱动程序可以为每个SPI传输指定I/O通道的数量。使用这种方式,对SPI存储的操作可以被分为多次SPI传输,每次SPI传输使用预定义数量的I/O通道进行传输。
这种方式可以正常工作,直到一些IP供应商决定让它们的SPI控制器更加智能,嵌入某种高级接口,可以在单个的步骤中执行SPI存储器的操作,而不是使用分开的多次传输操作。(事实上,大多数SPI控制器甚至比这更加智能,可以允许你直接将SPI存储映射到CPU的地址空间,但让我们先把这种情况留待以后处理吧)。在这种情况下,我们需要赋予SPI控制器更多的控制权,这样它就可以决定具体该做什么,而不必从一组分散的SPI传输命令中,重建SPI存储器操作。
当时的决定是,将这些控制器专门用于一个任务,控制SPI NORs(当时这是唯一会用到双线和四线模式的情况),SPI NOR框架就是为此而创建的。
由于这个决定,我们现在在Linux中有一个SPI NOR框架用于连接SPI NOR控制器驱动和SPI NOR的逻辑代码(spi-nor 子系统),同时我们有常规的SPI控制器驱动,可以进行基础的SPI传输(spi 子系统)。然而,从硬件的角度看,能为SPI NOR提供特殊特性的SPI控制器,一般也拥有进行基本传输的能力,即可用于控制常规的SPI设备。不幸的是,基于当前的spi-nor 子系统和spi 子系统是分裂开来的情况,如果一个SPI控制器被spi-nor子系统的驱动支持了,它将无法被用于与spi子系统中的常规设备进行通信。
作为一个针对这个问题的部分的解决方案,->spi_flash_read()操作被添加到结构体 spi_controller中,这允许spi子系统中的常规spi控制器驱动提供一个较优的方式,来从SPI NOR存储中读取数据,这种方式被通用SPI NOR驱动m25p80所使用。然而,这个解决方案是部分的,因为它只优化了读取,并且仅限于SPI NORs。
在当前的架构中,我们有
我们之前已经看到,基于SPI NOR框架,SPI NOR存储器已经得到了适当的支持。但NORs 并非SPI总线上唯一的存储设备,SPI NANDs 正在变得越来越流行。
Peter Pan提出了一个遵循SPI NOR模型的,用于支持SPI NAND设备的框架: SPI控制器必须实现SPI NAND控制器接口才能控制SPI NAND。但是当我们更深入地参与到这个开发中时,我们很快意识到沿着这条路走会有多么麻烦,因为这意味着,如果SPI控制器想要同时控制两种设备,就必须同时实现SPI NOR和SPI NAND接口。当SPI NVRAM或任何其他类型的存储制造商决定采用SPI总线时,将会发生什么?再添加一个SPI控制器必须实现的接口?这听起来不是个好主意。
因此我们决定用另外的方式解决这个问题,尝试找出SPI NANDs和SPI NORs的共同点。SPI NORs和SPI NANDs 指令集不同,行为和约束也不同(主要是由于NOR和NAND本身的不同),但当与设备交互时,都遵循同样的SPI存储器操作语义,这也是高级控制器都在尝试优化的部分。
SPI 存储器层只是提供一种方式给SPI控制器驱动,用于传递高级SPI存储器操作,而不是让它们处理SPI传输细节并自行尝试优化它们。这同样简化了SPI存储器驱动,因为它们只需要按照SPI存储器规范发送SPI存储器操作指令,不需要关心复杂的、不断发展的、依赖具体存储器的接口。
有了这个新的架构,SPI NOR和SPI NAND都可以基于相同的SPI控制器驱动进行支持了。m25p80驱动将被修改成,使用spi-mem接口,取代具有局限性的->spi_flash_read()接口。目前,我们仍然有专用的SPI NOR控制器驱动,但最终目标是移除它们,并将它们移植为 drivers/spi 下的普通SPI控制器驱动。非常欢迎这方面的帮助和贡献。
SPI存储器的API 由 include/linux/spi/spi-mem.h 描述。
希望使用SPI存储器API的SPI设备驱动程序,应该将自己声明为spi_mem_drivers,并实现->probe()和->remove()函数。
它们将被传入一个spi_mem对象,它只是一个围绕spi_device对象的简单包装,我们引入一个不同的对象的原因是,我们希望能够拓展spi_mem对象,并在需要时附加更多的信息(例如存储器类型,存储器组织方式和其他的高级SPI控制器可能需要的信息)。
当驱动想要执行SPI存储器操作时,它将填充spi_mem_op结构并调用spi_mem_exec_op()。另外,可以使用spi_mem_supports_op(),测试一个SPI控制器是否支持特定的存储器操作,使用spi_mem_adjust_op_size(),获取控制器支持的最大传输大小,并尝试拆分数据传输以避免超出限制。
现在让我们看下控制器端。一个希望优化SPI存储器操作的SPI控制器,可以实现spi_mem_ops接口,该接口包含三个直接对应用户API的方法:
注意,当spi_mem_ops 没有实现时,core层将通过创建由多个SPI传输组成的SPI消息,来添加对该特性的通用支持,就像以前通用SPI NOR控制器驱动程序(名为m25p80)所做的那样。
如你所见,这些API非常直截了当,所以希望有更多的SPI存储器驱动能够转换为使用它,而不是手动创建包含多个SPI传输的SPI消息。
一部分已经被贡献出去并合并,计划成为Linux 4.18的一部分:
先进的SPI控制器不仅能够优化SPI存储器操作的执行,它们可以进一步将所有存储器访问的复杂性隐藏起来,提供一个直接映射的IOMEM区域,对此区域的访问会自动在总线上触发SPI存储器操作,为你完成数据的收发,这样的行为就像一个连接在并行的内存总线上的内存。可以想象,这将允许更高的吞吐量和更少的用于SPI存储器操作管理的CPU时间,但这同时也是一个难以通过常规的方式进行支持的功能。我们已经在linux-mtd邮件列表上发布了一个支持这种直接映射功能的建议。
如前所述,另一个具有挑战性的主题是,将所有的SPI NOR控制器驱动转换为基于SPI mem模型,以便所有的QSPI控制器都真正表现为SPI控制器而非SPI NOR控制器。这可能需要一些时间,因为目前在driver/mtd/spi-nor 下有10个驱动,我们只知道其中2个被转换为了SPI mem方法(fsl-quadspi和atmel-quadspi)。
本文地址 https://cloud.tencent.com/developer/article/1560552
译自 https://bootlin.com/blog/spi-mem-bringing-some-consistency-to-the-spi-memory-ecosystem/
原作者 Boris Brezillon