Linux pcie 学习总结 1

前言

前后折腾了好几个月,终于把PCIe最最基本的功能做出来后,成就感相当的大!我把能踩的坑都踩了一遍, 总结下学习经验吧。半路出家学的嵌入式编程,一直抱着团队技术大牛大腿,然后摸索过河。希望自己将来能够独当一面。

环境

Linux 内核 6.10, 嗯大概。

yocto 编译

Cortex-A65, 自带gicv3, ITS, SMMU ( 之前只搞cortex M 或者 R, 看到做pcie需要用到这些辅助功能...san值都掉光了。 )尤其是ITS, 直接把我CPU干冒烟了。 看reference manual真的能减肥。

概述

这里就不介绍啥是PCIe了,站里好多博客讲的都相当详细。耐着心思看完,就能理解个大概了。我个菜鸟就不班门弄斧了。 

这里总结的,都是我自己的开发经验,以及避坑指南。

正文

1 我写的PCIe驱动是什么鬼

为了让PCIe跑起来,我们需要好几种PCIe驱动. 新人容易混淆,我简单讲讲吧。

1. 1 PCIe controller (或PCIe Subsystem)的驱动。 很幸运, 你不用写,有现成。 都在linux drivers/pcie/controller 文件夹下。 这些底层驱动(直接修改PCIe寄存器)通常由PCIe 这个IP的供应商提供,例如synopsis 或者cadence。 如果大神你就是做IP的, 请留下你的联系方式,然后滑走。嗯,我还有很多不懂的地方, 将来找机会请教你。 对于这些驱动, 你可以直接拿来用。最基本的功能是有保障的,但是有可能不全面。 例如某些底层驱动竟然不支持自带的uDMA。(被我同事喷死了)

1. 2. PCIe 内核驱动。 嗯, Linux 代码开源, 自带PCIe驱动,你也不用写。 PCIe Device Enumeration,capabilities 的解析, inbound/outbound bar 地址的设置, Linux都替你搞定了。 如果以上这几步出错,90%的可能是你device tree的设置没搞清楚,5% 的可能是设备clock没有使能。4·99% 可能是你没搞懂你使用的EP。最后0.01% 可能是你家芯片有个隐藏的开关, 你看技术文档的时候漏掉了,或者你们的硬件工程师忘记跟你提了。 从自己身上找原因吧。

1. 3. framework,这就是你我码农需要写的东东啦。 先说下设备探测的顺序, 编译的时候开启PCIe支持(menuconfig, 或者defconfig 内开启),device tree里使能你家的PCIe Controller, device ID 和 vendor ID 在kernel device probe阶段对上了, linux pcie 内核驱动就会调用你写的pcie framework。 然后你的pcie framework在调用由cadence或synopsis提供的底层驱动来设置硬件。

那么我们的framwork具体需要干啥呢? 简单来说,大家可能使用同一个PCIe IP, 但是每个soc内部链接不同,PCIe subsystem 的base address不同,PCIe接上的PHY也不同。Framework 驱动就是要把这些零散的信息整合进某个structure里面(通常由底层驱动的heander 提供),然后交给底层驱动去处理。 RC 还是 EP模式啊,速度多少, gen3 还是gen4?几根lane?这些有可能需要你自己设置。 报错了,得找哪根SPI interrupt啊?framework得自己设置中断以及callback Function。底层驱动基本设置完了,要linkup了? framwork得把自己的PHY LINKUP function的handler 告诉底层驱动,然后让其调用。具体怎么写,可以参考linux下同行们的代码。 能upstraming的代码都是久经考验的。

1. 4. EP 驱动。 我们的PCIe是RC 模式,以不变应万变,对面接上啥设备,linux就会加载相对应的设备驱动。  EP设备驱动的探测是在PCIe controller设置完成之后开始的。如果你的pcie 开始设备探测了,嗯说明你的pcie subsystem工作正常,linux kernel认可了你的framework,phy起码LINKUP了。 (撒花🎉)。 如果你用的是普通的,成熟的pcie 设备,只要找到对应的设备驱动就好。如果你是设备驱动的开发者,这里有个小坑。 设备驱动除了vendorID和deviceID, 还有一个CLASS ID(分类?),这个ID在device的config space里。 如果是半成品设备,或者简单的用于测试的设备。CLASS  ID 有可能没设置,值为零。 LINUX kernel 看到class id 为零的设备, 是略过,不给分配bar address的。结果就是lspci 的时候,看不到EP 设备。

2. SMMU

干了很久单片机, 只认的MPU, MMU只听过。现在来个SMMU。在schema block上看到那玩意儿,顿时觉得天旋地转,两眼一黑。 其实SMMU用起来不难,虽然我还有很多很多不明白的地方。但是地址转换这个基本功能已经能用了。 总觉得SMMU是给没有ATS功能的设备用的。 嗯, 我还不明白ATS原理,弄明白了再回来水一篇文章。

SMMU的原理我就不讲了,讲下这玩意儿怎么合着PCIe用。 万一smmu不干活,怎么debug。

首先,我们不一定要用SMMU。(看soc的架构呗,我是没得选)地址转换有两种方法

* 软件转换, virt_to_phy。 EP要写入RC那端的CPU地址的话,直接使用物理地址就好。

* SMMU, 如果打算使用SMMU, 那就不需要使用virt_to_phy。 内核环境下,直接alloc 一个buffer,然后告诉EP 向这个地址写入或读取。EP 写入或读取的package中就会包含这个buffer的虚拟地址 (通常FFFF开头)。 PCIe收到来自EP的读取或写入package之后,会把它转换成AXI的transaction。然后发往它连接着的interconnect。这个transaction到达interconnect之前会路过SMMU,SMMU根据地址长短,长相(开头一堆FFFF)还有最最最重要的STREAM ID 来判断,要不要进行地址转换。axi transaction在转换地址后,带着读取或者写入的任务就冲着interconnect以及指定的buffer(内存)去了。

记住哦,如果是CPU向EP设备写入或读取,那就没有SMMU什么事了。

这里有个小小的问题,路过的大神帮一下吧。能让pcie设备同时使用物理地址和虚拟地址吗? 

好了,刚才一带而过的stream  ID 是什么鬼。 嗯, 大部分情况下,pcie 喂给SMMU 的steam ID 就是设备被分配的BDF。PCIe 将EP 读写package转成axi transaction的时候,也会把EP 的BDF转成stream id。 这个纯硬件设置,找你家的architect或者硬件design问问吧。 smmu也有要求sub streamid。 但是对于pcie设备来说,这个sub streamid 通常为零。 不解释。

SMMU debug 没啥好办法,这口锅通常可以按在device tree 上。 

首先看看iommus 或者iommu-maps 这个property有没有在pice controller node下。如果用 iommu-maps,得看看启始RID(request id, 其实就是BDF)加上length有木有覆盖到所有的pcie 设备BDF。 

pcie device 的RID通常是0x100开始。

3. MSI

累死了,回头写。

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值