下面这篇给你按"能直接发 CSDN 专栏 / 内核开发文档级 "来写,已经把你前面所有代码(PMON / PLL / PCI / tgt_machdep / DMA / cache / XBAR)全部串起来,并且重点围绕你当前问题:BAR2不可访问 + DMA问题 + 地址窗口映射来展开。
🚀《LS2K PCIe 驱动开发实战(从 PMON 到 Linux)》
------ 从上电、PLL、DDR、PCIe 到 DMA/Cache/XBAR 全链路拆解
🧭 一、整体架构总览(必须先建立脑图)
+----------------------+
| CPU |
| (LoongArch Core) |
+----------+-----------+
|
| (XBAR)
v
+----------------------------+
| 内部互联总线 |
| (XBAR / AXI / 仲裁) |
+-----+------------+--------+
| |
v v
+--------+ +------------+
| DDR MC | | PCIe RC |
+--------+ +------------+
| |
v v
+---------+ +-----------+
| DDR | | PCIe Dev |
+---------+ +-----------+
核心路径(你现在调试的关键):
CPU → XBAR → PCIe RC → TLP → Device BAR
CPU → XBAR → DDR(load/store)
PCIe Device → DMA → XBAR → DDR
🔥 二、启动流程(从 PMON 到 Linux)
1️⃣ Boot入口:init_loongarch()
c
void init_loongarch()
{
unlock_scache(); // L2/L3 cache unlock
get_memorysize(); // DDR容量探测
update_cpu_name();
tgt_fpuenable();
set_freq_scale(); // CPU频率
main(); // 进入 PMON 主流程
}
👉 核心点:
- cache 在 一开始就参与系统稳定性
- DDR 已经初始化(但 training 在更早阶段)
2️⃣ PLL / 频率配置(你贴的 loongson3_clksetting.S)
CPU频率计算:
node_clock = refclk / L1_div_ref * L1_loopc / L1_divout / L2_divout
代码核心:
asm
li.d t1, (L1_LOOPC << 32) | (L1_DIV << 42) | (L1_REFC << 26)
👉 实质:
| 字段 | 含义 |
|---|---|
| LOOPC | 倍频 |
| DIV | 分频 |
| REFC | 输入分频 |
3️⃣ DDR频率
c
#define DDR_LOOPC (DDR_FREQ*DDR_REFC*DDR_DIV*DDR_DIV_L2/100)
👉 对应:
DDR freq = refclk / REFC * LOOPC / DIV / DIV_L2
🔥 三、PCIe 初始化(核心)
1️⃣ PMON阶段:_ls2k_pci_hwinit
c
pd->pa.pa_memt->bus_base = PHYS_TO_UNCACHED(0x40000000);
pb->minpcimemaddr = 0x40100000;
pb->nextpcimemaddr = 0x80000000;
👉 关键含义:
| 区域 | 作用 |
|---|---|
| 0x40000000 | CPU访问PCI窗口 |
| 0x40100000 | PCI MEM起始 |
| 0x80000000 | 上限 |
2️⃣ DMA映射
c
_pci_dmamap(va) = pci_base + PA(va)
👉 说明:
PCI设备看到的地址 = CPU物理地址 + 偏移
3️⃣ PCI Window(重点)
CPU → PCI(Outbound)
CPU访问 0x40000000 → PCI地址空间
PCI → DDR(Inbound)
c
BONITO_PCIBASE4 = 0x80000000;
👉 表示:
PCI DMA地址 0x80000000 → DDR 0x0
🔥 四、你当前问题1:BAR2 不可访问(重点)
🔥 访问路径
readl()
→ ioremap()
→ CPU VA
→ XBAR
→ PCIe RC
→ TLP
→ BAR2
🔍 排查链路(逐级)
① BAR是否分配
bash
lspci -vv
检查:
Region 2: Memory at XXXXXXXX
② 是否在 window 内
c
pb->minpcimemaddr = 0x40100000;
👉 如果 BAR2 < 0x40100000 ❌ → 无法访问
③ 是否 ioremap 正确
c
void __iomem *addr = ioremap(bar2, size);
readl(addr);
④ 是否 XBAR 拦截
典型现象:
| 表现 | 原因 |
|---|---|
| 卡死 | 总线未响应 |
| 返回0xffffffff | 没设备 |
| 数据错误 | 映射错误 |
⑤ PCIe TLP层
如果 BAR2 不通:
👉 实际是:
CPU没有发出正确 TLP
🔥 五、你当前问题2:DMA数据错误
数据路径:
PCIe Device
↓ DMA
PCIe RC
↓
XBAR
↓
DDR
↑
CPU cache
❗ 核心问题:cache一致性
你代码里:
c
void pci_sync_cache(...) {
// 空实现
}
👉 这就是问题!!!
🔥 典型错误
| 现象 | 原因 |
|---|---|
| DMA写了但CPU读不到 | cache没flush |
| CPU写了DMA读旧数据 | cache没clean |
✅ 正确做法
c
// DMA前
dma_map_single()
// DMA后
dma_sync_single_for_cpu()
或 PMON:
c
CPU_IOFlushDCache(addr, size, rw);
🔥 六、XBAR 仲裁(隐藏大坑)
仲裁对象:
- CPU
- PCIe DMA
- GPU
- DDR controller
影响:
| 场景 | 现象 |
|---|---|
| DMA高负载 | CPU访问慢 |
| 多DMA | 丢包 |
| PCIe burst | DDR拥塞 |
寄存器(你代码中)
c
BONITO_PXARB_CFG = 0x00fe0105;
👉 控制:
- 优先级
- burst长度
- fairness
🔥 七、完整数据流(终极理解)
CPU访问BAR
CPU
↓
XBAR
↓
PCIe RC
↓
TLP (Mem Read)
↓
Device BAR
DMA写DDR
Device
↓
TLP (Mem Write)
↓
PCIe RC
↓
XBAR
↓
DDR
↓
CPU cache(不一致!)
🔥 八、LS2K PCIe window 总结(最关键表)
| 类型 | 地址 | 作用 |
|---|---|---|
| CPU→PCI | 0x40000000 | outbound |
| PCI MEM | 0x40100000 | BAR映射 |
| PCI→DDR | 0x80000000 | inbound |
| DDR | 0x0 | 内存 |