summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGUI <[email protected]>2026-08-14 12:42:01 +0800
committerGitHub <[email protected]>2026-08-14 12:42:01 +0800
commitf2c05253d11f0b5746ade4bdb7d8d838bb7a5df1 (patch)
tree63e8bc39e0391bd0e0587a96d59dc3a7b42a732f
parentc1418dbd2e3b9596b4082b111b80bd5f86e76028 (diff)
[dm][dma] Update DMA (#11678)
* [dm][dma] update DMA interface Extend DMA mapping to honor OFW dma-ranges even when no reserved memory pool is present, and add PCI host-bridge DMA-region translation for PCI devices. Prefer 32-bit-addressable allocations for translated devices, improve pool allocation fallbacks and synchronization, and establish a linear mapping for installed DMA pools when possible. Signed-off-by: GuEe-GUI <[email protected]> * [dm][dma] fixup PL330 work flow Fix PL330 channel allocation so device-tree cells select peripheral request lines rather than execution channels. Generate proper peripheral DMA microcode using DMAWFP, DMALDP, DMASTP, and DMAFLUSHP, encode burst lengths correctly, honor transfer direction and non-secure attributes, and allocate microcode from DMA-coherent memory. Signed-off-by: GuEe-GUI <[email protected]> * style: format code with clang-format * style: format complete changed files with clang-format * drivers: dma: fix diagnostic format types * style: format DMA changes for current master --------- Signed-off-by: GuEe-GUI <[email protected]>
-rw-r--r--bsp/qemu-virt64-aarch64/README.md10
-rw-r--r--bsp/qemu-virt64-aarch64/README_zh.md10
-rwxr-xr-xbsp/qemu-virt64-aarch64/amp.dtsi2
-rw-r--r--components/drivers/dma/dma-pl330.c275
-rw-r--r--components/drivers/dma/dma_pool.c305
-rw-r--r--libcpu/aarch64/common/setup.c111
6 files changed, 592 insertions, 121 deletions
diff --git a/bsp/qemu-virt64-aarch64/README.md b/bsp/qemu-virt64-aarch64/README.md
index 8ac4012c1c..a4353865bc 100644
--- a/bsp/qemu-virt64-aarch64/README.md
+++ b/bsp/qemu-virt64-aarch64/README.md
@@ -191,10 +191,10 @@ VirtIO Sound:
AMP heterogeneous multi-core (RPMSG slave, **requires Smart version**):
```
-./qemu.py -dtbo amp.dtsi -mem 258
+./qemu.py -dtbo amp.dtsi -mem 300
```
-> **Note**: The AMP demo requires the **Smart version** (`RT_USING_SMART`) and **`-mem 258`** (memory layout in `amp.dtsi` needs ~258 MB; the default 128 MB is insufficient). `qemu.py` also increments `-smp` by 1 when `amp_soc` is detected (e.g. `RT_CPUS_NR=4` runs as `-smp 5`). QEMU `loader` pre-loads `amp.dtb` and `rtthread.bin` to fixed physical addresses in `amp.dtsi` (e.g. `kernel-entry = 0x48480000`); the primary core then starts the slave via PSCI. The standard kernel must run in a region where virtual addresses equal physical addresses and cannot be used for the slave address space. Enable Smart in menuconfig and rebuild.
+> **Note**: The AMP demo requires the **Smart version** (`RT_USING_SMART`) and **`-mem 300`** (memory layout in `amp.dtsi` needs ~300 MB; the default 128 MB is insufficient). `qemu.py` also increments `-smp` by 1 when `amp_soc` is detected (e.g. `RT_CPUS_NR=4` runs as `-smp 5`). QEMU `loader` pre-loads `amp.dtb` and `rtthread.bin` to fixed physical addresses in `amp.dtsi` (e.g. `kernel-entry = 0x48480000`); the primary core then starts the slave via PSCI. The standard kernel must run in a region where virtual addresses equal physical addresses and cannot be used for the slave address space. Enable Smart in menuconfig and rebuild.
Camera demo (requires `-graphic` for framebuffer overlay in `camera.c`):
@@ -328,12 +328,12 @@ Default application. Prints `hello rt-thread` after boot, then enters msh.
QEMU `loader` pre-loads `amp.dtb` and `rtthread.bin` to addresses in `amp.dtsi`; the primary core then boots the slave CPU via the `amp_soc` devicetree node and PSCI. Primary and slave communicate through VirtIO RPMSG and Mailbox. The primary writes VirtIO Block `vdb` resource info into the slave DTB before starting the slave. `qemu.py` increments `-smp` by 1 for the extra CPU.
-**Version requirement**: **Smart version** required (`RT_USING_SMART` in menuconfig), plus **`-mem 258`**. The standard kernel must run with virtual addresses equal to physical addresses. The AMP slave is loaded into a separate physical memory region (see `amp_memory`, `kernel-entry` in `amp.dtsi`), which violates that constraint — the standard kernel cannot be used for AMP demos.
+**Version requirement**: **Smart version** required (`RT_USING_SMART` in menuconfig), plus **`-mem 300`**. The standard kernel must run with virtual addresses equal to physical addresses. The AMP slave is loaded into a separate physical memory region (see `amp_memory`, `kernel-entry` in `amp.dtsi`), which violates that constraint — the standard kernel cannot be used for AMP demos.
Startup:
```
-./qemu.py -dtbo amp.dtsi -mem 258
+./qemu.py -dtbo amp.dtsi -mem 300
```
Slave console: `telnet localhost 4323`.
@@ -396,6 +396,6 @@ Optional VirtIO device arguments (add in `qemu.py` as needed):
- GIC auto-switches to v3 when exceeding 8 cores; or specify `-gic 3` manually. `gic-version=max` is set only internally when using `-el 2` with GICv3.
- 9P directory sharing requires QEMU built with virtfs (`--enable-virtfs`).
- `-gl` requires QEMU built with OpenGL / VirGL (`--enable-opengl`, `--enable-virglrenderer`); this is independent of RT-Thread OpenGL support — the default software stack is 2D graphics only.
-- AMP (`-dtbo`) requires Smart kernel (`RT_USING_SMART`), `-mem 258`, and auto-increments CPU count; the standard kernel requires VA=PA and cannot be used with slave images loaded at fixed physical addresses.
+- AMP (`-dtbo`) requires Smart kernel (`RT_USING_SMART`), `-mem 300`, and auto-increments CPU count; the standard kernel requires VA=PA and cannot be used with slave images loaded at fixed physical addresses.
- `-camera` requires a host V4L2 device path, Linux, and libvfio-user; your user must have read access to the device (typically via the `video` group); `-graphic` is needed only for the `camera.c` framebuffer preview demo.
- Override root device or boot parameters via `-bootargs`.
diff --git a/bsp/qemu-virt64-aarch64/README_zh.md b/bsp/qemu-virt64-aarch64/README_zh.md
index 0b623627d8..3b93d54028 100644
--- a/bsp/qemu-virt64-aarch64/README_zh.md
+++ b/bsp/qemu-virt64-aarch64/README_zh.md
@@ -187,10 +187,10 @@ VirtIO Sound 音频:
AMP 异构多核(RPMSG 从核,**须 Smart 版本**):
```
-./qemu.py -dtbo amp.dtsi -mem 258
+./qemu.py -dtbo amp.dtsi -mem 300
```
-> **说明**:AMP 示例要求启用 **Smart 版本**(`RT_USING_SMART`),且必须使用 **`-mem 258`**(`amp.dtsi` 内存布局约需 258 MB,默认 128 MB 不足)。`qemu.py` 检测到 `amp_soc` 时会将 `-smp` 自动加 1(例如 `RT_CPUS_NR=4` 时实际以 `-smp 5` 启动)。QEMU `loader` 会预先将 `amp.dtb` 与 `rtthread.bin` 加载到 `amp.dtsi` 指定的固定物理地址(如 `kernel-entry = 0x48480000`),主核再通过 PSCI 拉起从核。标准版内核要求在虚拟地址与物理地址相等的区域运行,无法用于从核地址空间。请在 menuconfig 中开启 Smart 后重新编译。
+> **说明**:AMP 示例要求启用 **Smart 版本**(`RT_USING_SMART`),且必须使用 **`-mem 300`**(`amp.dtsi` 内存布局约需 300 MB,默认 128 MB 不足)。`qemu.py` 检测到 `amp_soc` 时会将 `-smp` 自动加 1(例如 `RT_CPUS_NR=4` 时实际以 `-smp 5` 启动)。QEMU `loader` 会预先将 `amp.dtb` 与 `rtthread.bin` 加载到 `amp.dtsi` 指定的固定物理地址(如 `kernel-entry = 0x48480000`),主核再通过 PSCI 拉起从核。标准版内核要求在虚拟地址与物理地址相等的区域运行,无法用于从核地址空间。请在 menuconfig 中开启 Smart 后重新编译。
摄像头演示(`camera.c` 叠加画面到 framebuffer,需配合 `-graphic`):
@@ -324,12 +324,12 @@ Linux 下需预先配置 TAP 网桥,Windows / WSL 下 TAP 支持有限。TAP �
QEMU `loader` 预先将 `amp.dtb` 与 `rtthread.bin` 加载到 `amp.dtsi` 指定地址,主核再通过 `amp_soc` 设备树节点与 PSCI 拉起从核 CPU。主从之间通过 VirtIO RPMSG + Mailbox 通信。主核将 VirtIO Block `vdb` 的资源信息写入从核 DTB 后启动从核。`qemu.py` 会为这颗额外 CPU 将 `-smp` 加 1。
-**版本要求**:须 **Smart 版本**(menuconfig 中 `RT_USING_SMART`),且须使用 **`-mem 258`**。标准版内核必须在虚拟地址与物理地址一致的映射下运行;AMP 从核加载到独立物理内存区域(见 `amp.dtsi` 中 `amp_memory`、`kernel-entry` 等),不满足该约束,因此标准版无法用于 AMP 演示。
+**版本要求**:须 **Smart 版本**(menuconfig 中 `RT_USING_SMART`),且须使用 **`-mem 300`**。标准版内核必须在虚拟地址与物理地址一致的映射下运行;AMP 从核加载到独立物理内存区域(见 `amp.dtsi` 中 `amp_memory`、`kernel-entry` 等),不满足该约束,因此标准版无法用于 AMP 演示。
启动方式:
```
-./qemu.py -dtbo amp.dtsi -mem 258
+./qemu.py -dtbo amp.dtsi -mem 300
```
从核控制台:`telnet localhost 4323`。
@@ -392,6 +392,6 @@ VirtIO 设备可选参数(在 `qemu.py` 中按需添加):
- 超过 8 核时 GIC 自动切换为 v3,也可手动指定 `-gic 3`。`gic-version=max` 仅在 `-el 2` 且 GICv3 时由脚本内部设置。
- 启用 9P 目录共享需 QEMU 编译时包含 virtfs(`--enable-virtfs`)。
- 使用 `-gl` 需 QEMU 编译时包含 OpenGL / VirGL(`--enable-opengl`、`--enable-virglrenderer`);这与 RT-Thread 是否支持 OpenGL 无关,当前软件栈默认仅 2D 图形。
-- AMP(`-dtbo`)须 Smart 版本内核(`RT_USING_SMART`)、`-mem 258`,且自动增加 CPU 数;标准版要求 VA=PA 运行,无法用于从核固定物理地址加载场景。
+- AMP(`-dtbo`)须 Smart 版本内核(`RT_USING_SMART`)、`-mem 300`,且自动增加 CPU 数;标准版要求 VA=PA 运行,无法用于从核固定物理地址加载场景。
- `-camera` 须传入宿主机 V4L2 设备路径,仅支持 Linux,且需 libvfio-user;当前用户须能访问该设备(通常加入 `video` 组);`-graphic` 仅 `camera.c` framebuffer 预览演示需要。
- 修改根设备或启动参数时,通过 `-bootargs` 覆盖默认参数。
diff --git a/bsp/qemu-virt64-aarch64/amp.dtsi b/bsp/qemu-virt64-aarch64/amp.dtsi
index 2491d1b8a3..01ff9491d9 100755
--- a/bsp/qemu-virt64-aarch64/amp.dtsi
+++ b/bsp/qemu-virt64-aarch64/amp.dtsi
@@ -19,7 +19,7 @@
#define AMP_MEMORY_SIZE (128 * SIZE_MB)
/*
- * RUN COMMAND: ./qemu.py -dtbo amp.dtsi -mem 258
+ * RUN COMMAND: ./qemu.py -dtbo amp.dtsi -mem 300
* REMOTE: telnet localhost 4323
*/
diff --git a/components/drivers/dma/dma-pl330.c b/components/drivers/dma/dma-pl330.c
index 8fa207a94d..7a21d0e6e3 100644
--- a/components/drivers/dma/dma-pl330.c
+++ b/components/drivers/dma/dma-pl330.c
@@ -213,25 +213,25 @@
#define PL330_CMD_DMAWMB 0x13
/** @brief Microcode instruction sizes */
-#define PL330_SIZE_DMAADDH 3
-#define PL330_SIZE_DMAEND 1
+#define PL330_SIZE_DMAADDH 3
+#define PL330_SIZE_DMAEND 1
#define PL330_SIZE_DMAFLUSHP 2
-#define PL330_SIZE_DMALD 1
-#define PL330_SIZE_DMALDP 2
-#define PL330_SIZE_DMALP 2
-#define PL330_SIZE_DMALPEND 2
-#define PL330_SIZE_DMAKILL 1
-#define PL330_SIZE_DMAMOV 6
-#define PL330_SIZE_DMANOP 1
-#define PL330_SIZE_DMARMB 1
-#define PL330_SIZE_DMASEV 2
-#define PL330_SIZE_DMAST 1
-#define PL330_SIZE_DMASTP 2
-#define PL330_SIZE_DMASTZ 1
-#define PL330_SIZE_DMAWFE 2
-#define PL330_SIZE_DMAWFP 2
-#define PL330_SIZE_DMAWMB 1
-#define PL330_SIZE_DMAGO 6
+#define PL330_SIZE_DMALD 1
+#define PL330_SIZE_DMALDP 2
+#define PL330_SIZE_DMALP 2
+#define PL330_SIZE_DMALPEND 2
+#define PL330_SIZE_DMAKILL 1
+#define PL330_SIZE_DMAMOV 6
+#define PL330_SIZE_DMANOP 1
+#define PL330_SIZE_DMARMB 1
+#define PL330_SIZE_DMASEV 2
+#define PL330_SIZE_DMAST 1
+#define PL330_SIZE_DMASTP 2
+#define PL330_SIZE_DMASTZ 1
+#define PL330_SIZE_DMAWFE 2
+#define PL330_SIZE_DMAWFP 2
+#define PL330_SIZE_DMAWMB 1
+#define PL330_SIZE_DMAGO 6
/** @brief DMAMOV to Source Address Register */
#define PL330_DIR_SAR 0
@@ -242,12 +242,14 @@
/** @brief Source address increment flag in CCR */
#define PL330_SRC_INC RT_BIT(0)
+#define PL330_SRC_NS RT_BIT(9)
/** @brief Source burst size shift in CCR */
#define PL330_SRC_BURST_SIZE_SHIFT 1
/** @brief Source burst length shift in CCR */
#define PL330_SRC_BURST_LEN_SHIFT 4
/** @brief Destination address increment flag in CCR */
#define PL330_DST_INC RT_BIT(14)
+#define PL330_DST_NS RT_BIT(23)
/** @brief Destination burst size shift in CCR */
#define PL330_DST_BURST_SIZE_SHIFT 15
/** @brief Destination burst length shift in CCR */
@@ -269,20 +271,22 @@
* @brief PL330 DMA channel descriptor
*
* Extends the generic DMA channel with microcode buffer management.
- * The microcode is generated at prep time and stored in microcode_raw
- * with alignment padding.
+ * The microcode is generated at prep time in DMA-coherent memory.
*/
struct pl330_chan
{
struct rt_dma_chan parent; /**< Generic DMA channel */
rt_bool_t enabled; /**< Channel is currently in use */
+ rt_bool_t is_slave; /**< Channel serves a peripheral request */
+ rt_bool_t start_logged; /**< First-start diagnostic emitted */
+ rt_bool_t irq_logged; /**< First-IRQ diagnostic emitted */
+ rt_uint8_t peri_id; /**< PL330 peripheral request ID */
rt_size_t size; /**< Transfer size for callback reporting */
void *microcode; /**< Aligned microcode buffer pointer */
rt_size_t microcode_len; /**< Microcode length in bytes */
rt_ubase_t microcode_dma; /**< Physical address of microcode buffer */
- rt_uint8_t microcode_raw[PL330_MICROCODE_SIZE + 4]; /**< Raw microcode storage (+4 for alignment) */
};
/**
@@ -331,7 +335,7 @@ static void pl330_read_config(struct pl330 *pl330)
void *regs = pl330->regs;
value = HWREG32(regs + PL330_REG_CR(0));
- pl330->mode_ns = !!PL330_CR0_NUM_EVENTS(value);
+ pl330->mode_ns = PL330_CR0_MGR_NS_AT_RST(value);
pl330->num_chan = PL330_CR0_NUM_CHNLS(value) + 1;
pl330->num_events = PL330_CR0_NUM_EVENTS(value) + 1;
@@ -358,11 +362,32 @@ static void pl330_read_config(struct pl330 *pl330)
*
* @return RT_EOK on success, -RT_EINVAL if address width is unsupported
*/
-static rt_err_t pl330_ccr_config(struct rt_dma_slave_config *conf, rt_uint32_t *ccr)
+static rt_err_t pl330_ccr_config(struct rt_dma_slave_config *conf,
+ rt_bool_t nonsecure, rt_uint32_t *ccr)
{
- *ccr = PL330_SRC_INC | PL330_DST_INC;
- *ccr |= conf->src_maxburst << PL330_SRC_BURST_LEN_SHIFT;
- *ccr |= conf->dst_maxburst << PL330_DST_BURST_LEN_SHIFT;
+ if (!conf->src_maxburst || conf->src_maxburst > 16 ||
+ !conf->dst_maxburst || conf->dst_maxburst > 16)
+ {
+ return -RT_EINVAL;
+ }
+
+ *ccr = (conf->src_maxburst - 1) << PL330_SRC_BURST_LEN_SHIFT;
+ *ccr |= (conf->dst_maxburst - 1) << PL330_DST_BURST_LEN_SHIFT;
+
+ if (conf->direction != RT_DMA_DEV_TO_MEM)
+ {
+ *ccr |= PL330_SRC_INC;
+ }
+
+ if (conf->direction != RT_DMA_MEM_TO_DEV)
+ {
+ *ccr |= PL330_DST_INC;
+ }
+
+ if (nonsecure)
+ {
+ *ccr |= PL330_SRC_NS | PL330_DST_NS;
+ }
switch (conf->src_addr_width)
{
@@ -512,6 +537,45 @@ static int pl330_cmd_dmast(rt_uint8_t *microcode, rt_uint32_t cond)
return PL330_SIZE_DMAST;
}
+static int pl330_cmd_dmaflushp(rt_uint8_t *microcode, rt_uint8_t peri)
+{
+ *microcode++ = PL330_CMD_DMAFLUSHP;
+ *microcode = (peri & 0x1f) << 3;
+
+ return PL330_SIZE_DMAFLUSHP;
+}
+
+static int pl330_cmd_dmaldp(rt_uint8_t *microcode, rt_uint32_t cond,
+ rt_uint8_t peri)
+{
+ *microcode++ = PL330_CMD_DMALDP |
+ (cond == PL330_COND_BURST ? RT_BIT(1) : 0);
+ *microcode = (peri & 0x1f) << 3;
+
+ return PL330_SIZE_DMALDP;
+}
+
+static int pl330_cmd_dmastp(rt_uint8_t *microcode, rt_uint32_t cond,
+ rt_uint8_t peri)
+{
+ *microcode++ = PL330_CMD_DMASTP |
+ (cond == PL330_COND_BURST ? RT_BIT(1) : 0);
+ *microcode = (peri & 0x1f) << 3;
+
+ return PL330_SIZE_DMASTP;
+}
+
+static int pl330_cmd_dmawfp(rt_uint8_t *microcode, rt_uint32_t cond,
+ rt_uint8_t peri)
+{
+ *microcode++ = PL330_CMD_DMAWFP |
+ (cond == PL330_COND_BURST ? RT_BIT(1) : 0) |
+ (cond == PL330_COND_ALWAYS ? RT_BIT(0) : 0);
+ *microcode = (peri & 0x1f) << 3;
+
+ return PL330_SIZE_DMAWFP;
+}
+
/**
* @brief Emit a DMALP (loop begin) microcode instruction
*
@@ -653,9 +717,8 @@ static rt_uint32_t pl330_chan_id(struct pl330 *pl330, struct pl330_chan *pc)
/**
* @brief Request a DMA channel from the PL330 controller
*
- * If fw_data (device tree args) specifies a channel number, that
- * specific channel is requested. Otherwise any unallocated channel
- * is used. Marks the channel as enabled and unmasks its IRQ.
+ * Device-tree arguments identify a peripheral request line, while the
+ * controller allocates any free execution channel.
*
* @param[in] ctrl DMA controller
* @param[in] slave Requesting device
@@ -666,46 +729,34 @@ static rt_uint32_t pl330_chan_id(struct pl330 *pl330, struct pl330_chan *pc)
static struct rt_dma_chan *pl330_dma_request_chan(struct rt_dma_controller *ctrl,
struct rt_device *slave, void *fw_data)
{
- int idx = -1;
+ int peri_id = -1;
struct pl330_chan *pc;
struct pl330 *pl330 = raw_to_pl330(ctrl);
struct rt_ofw_cell_args *args = fw_data;
- if (args && (idx = args->args[0]) >= pl330->num_chan)
+ if (args && (peri_id = args->args[0]) >= pl330->num_peri)
{
return rt_err_ptr(-RT_EINVAL);
}
- if (idx >= 0)
+ for (int i = 0; i < pl330->num_chan; ++i)
{
- pc = &pl330->chans[idx];
+ pc = &pl330->chans[i];
- if (pc->enabled)
+ if (!pc->enabled)
{
- return rt_err_ptr(-RT_EBUSY);
+ goto _found;
}
}
- else
- {
- /*
- * Memory to Memory is often called after after DM has been initialized,
- * So just return unused chan
- */
- for (int i = 0; i < pl330->num_chan; ++i)
- {
- pc = &pl330->chans[i];
- if (!pc->enabled)
- {
- goto _found;
- }
- }
-
- return RT_NULL;
- }
+ return RT_NULL;
_found:
pc->enabled = RT_TRUE;
+ pc->is_slave = peri_id >= 0;
+ pc->start_logged = RT_FALSE;
+ pc->irq_logged = RT_FALSE;
+ pc->peri_id = peri_id >= 0 ? peri_id : 0;
HWREG32(pl330->regs + PL330_REG_INTEN) |= RT_BIT(pl330_chan_id(pl330, pc));
return &pc->parent;
@@ -723,6 +774,7 @@ static rt_err_t pl330_dma_release_chan(struct rt_dma_chan *chan)
struct pl330_chan *pc = rt_container_of(chan, struct pl330_chan, parent);
pc->enabled = RT_FALSE;
+ pc->is_slave = RT_FALSE;
return RT_EOK;
}
@@ -740,6 +792,7 @@ static rt_err_t pl330_dma_release_chan(struct rt_dma_chan *chan)
*/
static rt_err_t pl330_dma_start(struct rt_dma_chan *chan)
{
+ rt_uint32_t id, inst0;
struct pl330_chan *pc = rt_container_of(chan, struct pl330_chan, parent);
struct pl330 *pl330 = raw_to_pl330(chan->ctrl);
@@ -750,10 +803,34 @@ static rt_err_t pl330_dma_start(struct rt_dma_chan *chan)
rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, pc->microcode, pc->microcode_len);
- HWREG32(pl330->regs + PL330_REG_DBGINST0) = ((pl330_chan_id(pl330, pc) + 1) << 8) | (PL330_CMD_DMAGO << 16);
+ id = pl330_chan_id(pl330, pc);
+ inst0 = ((PL330_CMD_DMAGO | (pl330->mode_ns ? RT_BIT(1) : 0)) << 16) |
+ (id << 24);
+
+ /* DMAGO is issued by the manager thread; its second byte selects a channel. */
+ HWREG32(pl330->regs + PL330_REG_DBGINST0) = inst0;
HWREG32(pl330->regs + PL330_REG_DBGINST1) = pc->microcode_dma;
HWREG32(pl330->regs + PL330_REG_DBGCMD) = 0;
+ if (!pc->start_logged)
+ {
+ pc->start_logged = RT_TRUE;
+ LOG_D("ch%u start peri=%u csr=%x inten=%08x mc=%08x", id, pc->peri_id,
+ HWREG32(pl330->regs + PL330_REG_CSR(id)) & 0xf,
+ HWREG32(pl330->regs + PL330_REG_INTEN),
+ (rt_uint32_t)pc->microcode_dma);
+
+ if ((HWREG32(pl330->regs + PL330_REG_CSR(id)) & 0xf) == PL330_CS_FAULT)
+ {
+ LOG_E("ch%u fault ftr=%08x fsrc=%08x fsrd=%08x dsr=%08x cpc=%08x",
+ id, HWREG32(pl330->regs + PL330_REG_FTR(id)),
+ HWREG32(pl330->regs + PL330_REG_FSRC),
+ HWREG32(pl330->regs + PL330_REG_FSRD),
+ HWREG32(pl330->regs + PL330_REG_DSR),
+ HWREG32(pl330->regs + PL330_REG_CPC(id)));
+ }
+ }
+
return RT_EOK;
}
@@ -769,6 +846,7 @@ static rt_err_t pl330_dma_start(struct rt_dma_chan *chan)
*/
static rt_err_t pl330_dma_stop(struct rt_dma_chan *chan)
{
+ rt_uint32_t id;
struct pl330_chan *pc = rt_container_of(chan, struct pl330_chan, parent);
struct pl330 *pl330 = raw_to_pl330(chan->ctrl);
@@ -777,7 +855,11 @@ static rt_err_t pl330_dma_stop(struct rt_dma_chan *chan)
rt_hw_cpu_relax();
}
- HWREG32(pl330->regs + PL330_REG_DBGINST0) = ((pl330_chan_id(pl330, pc) + 1) << 8) | PL330_CMD_DMAKILL;
+ id = pl330_chan_id(pl330, pc);
+
+ /* DMAKILL is executed in the selected channel debug thread. */
+ HWREG32(pl330->regs + PL330_REG_DBGINST0) =
+ (PL330_CMD_DMAKILL << 16) | RT_BIT(0) | (id << 8);
HWREG32(pl330->regs + PL330_REG_DBGINST1) = 0;
HWREG32(pl330->regs + PL330_REG_DBGCMD) = 0;
@@ -840,7 +922,7 @@ static rt_err_t pl330_dma_prep_memcpy(struct rt_dma_chan *chan,
mc = pc->microcode;
- if ((err = pl330_ccr_config(conf, &value)))
+ if ((err = pl330_ccr_config(conf, pl330->mode_ns, &value)))
{
return err;
}
@@ -959,7 +1041,7 @@ static rt_err_t pl330_dma_prep_cyclic(struct rt_dma_chan *chan,
mc = pc->microcode;
- if ((err = pl330_ccr_config(conf, &ccr_val)))
+ if ((err = pl330_ccr_config(conf, pl330->mode_ns, &ccr_val)))
{
return err;
}
@@ -1065,10 +1147,11 @@ static rt_err_t pl330_dma_prep_single(struct rt_dma_chan *chan,
struct pl330_chan *pc = rt_container_of(chan, struct pl330_chan, parent);
struct pl330 *pl330 = raw_to_pl330(chan->ctrl);
struct rt_dma_slave_config *conf = &chan->conf;
+ rt_uint32_t cond;
mc = pc->microcode;
- if ((err = pl330_ccr_config(conf, &ccr_val)))
+ if ((err = pl330_ccr_config(conf, pl330->mode_ns, &ccr_val)))
{
return err;
}
@@ -1086,6 +1169,11 @@ static rt_err_t pl330_dma_prep_single(struct rt_dma_chan *chan,
mc += pl330_cmd_dmamov(mc, PL330_DIR_DAR, dma_buf_addr);
}
+ if (pc->is_slave && !(pl330->quirk & PL330_QUIRK_BROKEN_NO_FLUSHP))
+ {
+ mc += pl330_cmd_dmaflushp(mc, pc->peri_id);
+ }
+
burst_bytes = conf->src_addr_width * conf->src_maxburst;
if (!burst_bytes || !buf_len)
{
@@ -1106,8 +1194,28 @@ static rt_err_t pl330_dma_prep_single(struct rt_dma_chan *chan,
mc += pl330_cmd_dmalp(mc, 0 /* LC0 */, loop);
rt_uint8_t ljmp = mc - pc->microcode;
- mc += pl330_cmd_dmald(mc, PL330_COND_ALWAYS);
- mc += pl330_cmd_dmast(mc, PL330_COND_ALWAYS);
+ cond = conf->src_maxburst > 1 ? PL330_COND_BURST : PL330_COND_SINGLE;
+
+ if (pc->is_slave)
+ {
+ mc += pl330_cmd_dmawfp(mc, cond, pc->peri_id);
+
+ if (dir == RT_DMA_MEM_TO_DEV)
+ {
+ mc += pl330_cmd_dmald(mc, cond);
+ mc += pl330_cmd_dmastp(mc, cond, pc->peri_id);
+ }
+ else
+ {
+ mc += pl330_cmd_dmaldp(mc, cond, pc->peri_id);
+ mc += pl330_cmd_dmast(mc, cond);
+ }
+ }
+ else
+ {
+ mc += pl330_cmd_dmald(mc, PL330_COND_ALWAYS);
+ mc += pl330_cmd_dmast(mc, PL330_COND_ALWAYS);
+ }
mc += pl330_cmd_dmalpend(mc,
PL330_COND_ALWAYS,
@@ -1140,11 +1248,8 @@ static const struct rt_dma_controller_ops pl330_dma_ops = {
/**
* @brief PL330 interrupt service routine
*
- * Handles all channel interrupts. Reads the Interrupt Status Register
- * to identify which channel triggered, then checks the Channel Status
- * Register:
- * - PL330_CS_CMPLT / PL330_CS_STOP: transfer completed successfully
- * - PL330_CS_FAULT / PL330_CS_FLTCMP: transfer faulted
+ * Handles completion events and reports channel faults. DMASEV can be
+ * observed before the following DMAEND instruction is executed.
*
* @param[in] irqno Hardware IRQ number
* @param[in] params Controller instance pointer
@@ -1170,13 +1275,15 @@ static void pl330_isr(int irqno, void *params)
csr = HWREG32(pl330->regs + PL330_REG_CSR(i)) & 0xf;
- switch (csr)
+ if (!pc->irq_logged)
{
- case PL330_CS_CMPLT:
- case PL330_CS_STOP:
- rt_dma_chan_done(&pc->parent, pc->size);
- break;
+ pc->irq_logged = RT_TRUE;
+ LOG_D("ch%d irq mis=%08x csr=%x cpc=%08x", i, isr, csr,
+ HWREG32(pl330->regs + PL330_REG_CPC(i)));
+ }
+ switch (csr)
+ {
case PL330_CS_FAULT:
case PL330_CS_FLTCMP:
LOG_E("Channel[%d] fault", i);
@@ -1184,7 +1291,8 @@ static void pl330_isr(int irqno, void *params)
break;
default:
- LOG_E("Unhandle CSR = %x", csr);
+ /* DMASEV may be observed before the following DMAEND executes. */
+ rt_dma_chan_done(&pc->parent, pc->size);
break;
}
}
@@ -1225,6 +1333,17 @@ static void pl330_free(struct pl330 *pl330)
if (pl330->chans)
{
+ for (int i = 0; i < pl330->num_chan; ++i)
+ {
+ struct pl330_chan *chan = &pl330->chans[i];
+
+ if (chan->microcode)
+ {
+ rt_dma_free_coherent(pl330->parent.dev, PL330_MICROCODE_SIZE,
+ chan->microcode, chan->microcode_dma);
+ }
+ }
+
rt_free(pl330->chans);
}
@@ -1262,6 +1381,8 @@ static rt_err_t pl330_probe(struct rt_platform_device *pdev)
return -RT_ENOMEM;
}
+ pl330->parent.dev = dev;
+
pl330->regs = rt_dm_dev_iomap(dev, 0);
if (!pl330->regs)
@@ -1357,17 +1478,17 @@ static rt_err_t pl330_probe(struct rt_platform_device *pdev)
for (int i = 0; i < pl330->num_chan; ++i)
{
- int offset = 0;
struct pl330_chan *chan = &pl330->chans[i];
- chan->microcode_dma = (rt_ubase_t)rt_kmem_v2p(chan->microcode_raw);
- offset = RT_ALIGN(chan->microcode_dma, 4) - chan->microcode_dma;
-
- chan->microcode = chan->microcode_raw + offset;
- chan->microcode_dma += offset;
+ chan->microcode = rt_dma_alloc_coherent(dev, PL330_MICROCODE_SIZE,
+ &chan->microcode_dma);
+ if (!chan->microcode)
+ {
+ err = -RT_ENOMEM;
+ goto _fail;
+ }
}
- pl330->parent.dev = dev;
pl330->parent.ops = &pl330_dma_ops;
rt_dma_controller_add_direction(&pl330->parent, RT_DMA_MEM_TO_MEM);
rt_dma_controller_add_direction(&pl330->parent, RT_DMA_MEM_TO_DEV);
diff --git a/components/drivers/dma/dma_pool.c b/components/drivers/dma/dma_pool.c
index 79b89e39a2..75e28cced6 100644
--- a/components/drivers/dma/dma_pool.c
+++ b/components/drivers/dma/dma_pool.c
@@ -31,6 +31,7 @@
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
+#include <mmu.h>
#include <mm_aspace.h>
#include <mm_memblock.h>
#include <dt-bindings/size.h>
@@ -39,6 +40,7 @@ static RT_DEFINE_SPINLOCK(dma_pools_lock);
static rt_list_t dma_pool_nodes = RT_LIST_OBJECT_INIT(dma_pool_nodes);
static struct rt_dma_pool *dma_pool_install(rt_region_t *region);
+static rt_err_t dma_pool_map_linear(struct rt_dma_pool *pool);
static void *dma_alloc(struct rt_device *dev, rt_size_t size,
rt_ubase_t *dma_handle, rt_ubase_t flags);
@@ -202,7 +204,10 @@ rt_inline rt_ubase_t ofw_addr_dma2cpu(struct rt_device *dev, rt_ubase_t addr)
static void *ofw_dma_map_alloc(struct rt_device *dev, rt_size_t size,
rt_ubase_t *dma_handle, rt_ubase_t flags)
{
- void *cpu_addr = dma_alloc(dev, size, dma_handle, flags);
+ void *cpu_addr;
+
+ flags |= RT_DMA_F_32BITS;
+ cpu_addr = dma_alloc(dev, size, dma_handle, flags);
if (cpu_addr && dma_handle)
{
@@ -302,17 +307,37 @@ static const struct rt_dma_map_ops ofw_dma_map_ops = {
.sync_in_data = ofw_dma_map_sync_in_data,
};
+static rt_bool_t ofw_node_has_dma_ranges(struct rt_ofw_node *np)
+{
+ struct rt_ofw_node *bus;
+
+ for (bus = np ? np->parent : RT_NULL; bus; bus = bus->parent)
+ {
+ rt_ssize_t len;
+
+ if (rt_ofw_get_prop(bus, "dma-ranges", &len) && len)
+ {
+ return RT_TRUE;
+ }
+ }
+
+ return RT_FALSE;
+}
+
/**
- * @brief Determine DMA operations for a device tree device with memory-region
+ * @brief Determine DMA operations for a device tree device
*
* Parses the "memory-region" property to install DMA pools from
* reserved-memory nodes. Sets RT_DMA_F_NOMAP if "no-map" is set,
* and RT_DMA_F_NOCACHE if the device is not DMA-coherent.
*
+ * Also installs ofw_dma_map_ops when the parent bus chain provides
+ * "dma-ranges", so CPU physical addresses are translated for DMA.
+ *
* @param[in] dev Device to configure DMA operations for
*
- * @return Pointer to DMA map ops (ofw_dma_map_ops) if regions were found,
- * RT_NULL otherwise
+ * @return Pointer to DMA map ops (ofw_dma_map_ops) when translation
+ * or memory-region pools apply, RT_NULL otherwise
*/
static const struct rt_dma_map_ops *ofw_device_dma_ops(struct rt_device *dev)
{
@@ -332,12 +357,7 @@ static const struct rt_dma_map_ops *ofw_device_dma_ops(struct rt_device *dev)
if (!(mem_np = rt_ofw_find_node_by_phandle(phandle)))
{
- if (region_nr == 0)
- {
- return RT_NULL;
- }
-
- break;
+ continue;
}
if ((err = rt_ofw_get_address(mem_np, 0, &addr, &size)))
@@ -374,7 +394,7 @@ static const struct rt_dma_map_ops *ofw_device_dma_ops(struct rt_device *dev)
++region_nr;
}
- if (region_nr)
+ if (region_nr || ofw_node_has_dma_ranges(np))
{
ops = &ofw_dma_map_ops;
}
@@ -383,14 +403,197 @@ static const struct rt_dma_map_ops *ofw_device_dma_ops(struct rt_device *dev)
}
#endif /* RT_USING_OFW */
+#ifdef RT_USING_PCI
+static struct rt_pci_host_bridge *pci_device_host_bridge(struct rt_device *dev)
+{
+ struct rt_pci_device *pdev = rt_container_of(dev, struct rt_pci_device, parent);
+
+ return rt_pci_find_host_bridge(pdev->bus);
+}
+
+static rt_uint64_t pci_host_cpu_to_bus(struct rt_pci_host_bridge *host_bridge, rt_uint64_t cpu_addr)
+{
+ if (!host_bridge || !host_bridge->dma_regions)
+ {
+ return ~0ULL;
+ }
+
+ for (int i = 0; i < host_bridge->dma_regions_nr; ++i)
+ {
+ struct rt_pci_bus_region *region = &host_bridge->dma_regions[i];
+
+ if (cpu_addr >= region->cpu_addr &&
+ cpu_addr < region->cpu_addr + region->size)
+ {
+ return region->phy_addr + (cpu_addr - region->cpu_addr);
+ }
+ }
+
+ return ~0ULL;
+}
+
+static rt_uint64_t pci_host_bus_to_cpu(struct rt_pci_host_bridge *host_bridge, rt_uint64_t pci_addr)
+{
+ if (!host_bridge || !host_bridge->dma_regions)
+ {
+ return ~0ULL;
+ }
+
+ for (int i = 0; i < host_bridge->dma_regions_nr; ++i)
+ {
+ struct rt_pci_bus_region *region = &host_bridge->dma_regions[i];
+
+ if (pci_addr >= region->phy_addr && pci_addr < region->phy_addr + region->size)
+ {
+ return region->cpu_addr + (pci_addr - region->phy_addr);
+ }
+ }
+
+ return ~0ULL;
+}
+
+static rt_ubase_t pci_addr_cpu2dma(struct rt_device *dev, rt_ubase_t addr)
+{
+ rt_uint64_t pci_dma;
+ struct rt_pci_host_bridge *host_bridge = pci_device_host_bridge(dev);
+
+ if (!host_bridge)
+ {
+ return addr;
+ }
+
+ pci_dma = pci_host_cpu_to_bus(host_bridge, addr);
+
+ return pci_dma == ~0ULL ? addr : (rt_ubase_t)pci_dma;
+}
+
+static rt_ubase_t pci_addr_dma2cpu(struct rt_device *dev, rt_ubase_t addr)
+{
+ rt_uint64_t cpu_addr;
+ struct rt_pci_host_bridge *host_bridge = pci_device_host_bridge(dev);
+
+ if (!host_bridge)
+ {
+ return addr;
+ }
+
+ cpu_addr = pci_host_bus_to_cpu(host_bridge, addr);
+
+ return cpu_addr == ~0ULL ? addr : (rt_ubase_t)cpu_addr;
+}
+
+static void *pci_dma_map_alloc(struct rt_device *dev, rt_size_t size,
+ rt_ubase_t *dma_handle, rt_ubase_t flags)
+{
+ void *cpu_addr;
+
+ flags |= RT_DMA_F_32BITS;
+ cpu_addr = dma_alloc(dev, size, dma_handle, flags);
+
+ if (cpu_addr && dma_handle)
+ {
+ *dma_handle = pci_addr_cpu2dma(dev, *dma_handle);
+ }
+
+ return cpu_addr;
+}
+
+static void pci_dma_map_free(struct rt_device *dev, rt_size_t size,
+ void *cpu_addr, rt_ubase_t dma_handle, rt_ubase_t flags)
+{
+ dma_handle = pci_addr_dma2cpu(dev, dma_handle);
+
+ dma_free(dev, size, cpu_addr, dma_handle, flags);
+}
+
+static rt_err_t pci_dma_map_sync_out_data(struct rt_device *dev,
+ void *data, rt_size_t size,
+ rt_ubase_t *dma_handle, rt_ubase_t flags)
+{
+ rt_err_t err;
+
+ if (flags & RT_DMA_F_NOCACHE)
+ {
+ err = dma_map_nocoherent_sync_out_data(dev, data, size, dma_handle, flags);
+ }
+ else
+ {
+ err = dma_map_coherent_sync_out_data(dev, data, size, dma_handle, flags);
+ }
+
+ if (!err && dma_handle)
+ {
+ *dma_handle = pci_addr_cpu2dma(dev, *dma_handle);
+ }
+
+ return err;
+}
+
+static rt_err_t pci_dma_map_sync_in_data(struct rt_device *dev,
+ void *out_data, rt_size_t size,
+ rt_ubase_t dma_handle, rt_ubase_t flags)
+{
+ dma_handle = pci_addr_dma2cpu(dev, dma_handle);
+
+ if (flags & RT_DMA_F_NOCACHE)
+ {
+ return dma_map_nocoherent_sync_in_data(dev, out_data, size, dma_handle, flags);
+ }
+
+ return dma_map_coherent_sync_in_data(dev, out_data, size, dma_handle, flags);
+}
+
+static const struct rt_dma_map_ops pci_dma_map_ops = {
+ .alloc = pci_dma_map_alloc,
+ .free = pci_dma_map_free,
+ .sync_out_data = pci_dma_map_sync_out_data,
+ .sync_in_data = pci_dma_map_sync_in_data,
+};
+
+static const struct rt_dma_map_ops *pci_device_dma_ops(struct rt_device *dev)
+{
+ struct rt_bus *bus = dev->bus;
+ struct rt_pci_host_bridge *host_bridge;
+
+ if (!bus || rt_strcmp(bus->name, "pci"))
+ {
+ return RT_NULL;
+ }
+
+ host_bridge = pci_device_host_bridge(dev);
+
+ if (!host_bridge || !host_bridge->dma_regions_nr)
+ {
+ return RT_NULL;
+ }
+
+ return &pci_dma_map_ops;
+}
+#endif /* RT_USING_PCI */
+
+#ifdef RT_USING_OFW
+const struct rt_dma_map_ops *rt_dma_ofw_device_map_ops(struct rt_device *dev)
+{
+ if (!dev || !dev->ofw_node)
+ {
+ return RT_NULL;
+ }
+
+ return ofw_device_dma_ops(dev);
+}
+#endif /* RT_USING_OFW */
+
/**
* @brief Select DMA operations for a device
*
* Priority:
* 1. Device-specific dma_ops (if already set)
- * 2. Device tree memory-region ops (if device has ofw_node)
+ * 2. Device tree ops (memory-region pools and/or dma-ranges translation)
* 3. Coherent or non-coherent fallback based on device property
*
+ * PCI devices with OFW bind dma_ops early in rt_pci_ofw_device_init();
+ * other PCI devices fall back to pci_device_dma_ops() here.
+ *
* The result is cached in dev->dma_ops for subsequent calls.
*
* @param[in] dev Device to query
@@ -409,6 +612,15 @@ static const struct rt_dma_map_ops *device_dma_ops(struct rt_device *dev)
#ifdef RT_USING_OFW
if (dev->ofw_node && (ops = ofw_device_dma_ops(dev)))
{
+ dev->dma_ops = ops;
+ return ops;
+ }
+#endif
+
+#ifdef RT_USING_PCI
+ if ((ops = pci_device_dma_ops(dev)))
+ {
+ dev->dma_ops = ops;
return ops;
}
#endif
@@ -443,6 +655,15 @@ static rt_ubase_t dma_pool_alloc(struct rt_dma_pool *pool, rt_size_t size)
rt_size_t bit, next_bit, end_bit, max_bits;
size = RT_DIV_ROUND_UP(size, ARCH_PAGE_SIZE);
+
+ if (!size || size > pool->bits)
+ {
+ LOG_W("%s: request %u page(s) exceeds pool capacity %u page(s)",
+ pool->region.name ? pool->region.name : "<anon>",
+ (unsigned)size, (unsigned)pool->bits);
+ return RT_NULL;
+ }
+
max_bits = pool->bits - size;
rt_bitmap_for_each_clear_bit(pool->map, bit, max_bits)
@@ -549,10 +770,19 @@ static void *dma_alloc(struct rt_device *dev, rt_size_t size,
if (*dma_handle && !(flags & RT_DMA_F_NOMAP))
{
- if (flags & RT_DMA_F_NOCACHE)
+ if (pool->flags & RT_DMA_F_NOCACHE)
{
dma_buffer = rt_ioremap_nocache((void *)*dma_handle, size);
}
+ else if (flags & RT_DMA_F_NOCACHE)
+ {
+ /* Honor caller NOCACHE before LINEAR pool shortcut. */
+ dma_buffer = rt_ioremap_nocache((void *)*dma_handle, size);
+ }
+ else if (pool->flags & RT_DMA_F_LINEAR)
+ {
+ dma_buffer = (void *)(*dma_handle - PV_OFFSET);
+ }
else if (flags & RT_DMA_F_WT)
{
dma_buffer = rt_ioremap_wt((void *)*dma_handle, size);
@@ -607,7 +837,10 @@ static void dma_free(struct rt_device *dev, rt_size_t size,
if (dma_handle >= pool->region.start &&
dma_handle <= pool->region.end)
{
- rt_iounmap(cpu_addr);
+ if (pool->flags & RT_DMA_F_NOCACHE || !(pool->flags & RT_DMA_F_LINEAR))
+ {
+ rt_iounmap(cpu_addr);
+ }
dma_pool_free(pool, dma_handle, size);
@@ -767,11 +1000,48 @@ rt_err_t rt_dma_sync_in_data(struct rt_device *dev, void *out_data, rt_size_t si
}
/**
+ * @brief Map a linear DMA pool into the kernel address space
+ *
+ * Reserved pool memory is excluded from rt_memblock_setup_memory_environment().
+ * Install a cached linear mapping so RT_DMA_F_LINEAR pools are CPU-accessible.
+ */
+static rt_err_t dma_pool_map_linear(struct rt_dma_pool *pool)
+{
+ rt_region_t *region = &pool->region;
+ rt_size_t start = RT_ALIGN_DOWN(region->start, ARCH_PAGE_SIZE);
+ rt_size_t end = RT_ALIGN(region->end, ARCH_PAGE_SIZE);
+ void *va;
+
+ if (!(pool->flags & RT_DMA_F_LINEAR) || start >= end)
+ {
+ return RT_EOK;
+ }
+
+ struct rt_mm_va_hint hint = {
+ .flags = MMF_MAP_FIXED,
+ .limit_start = rt_kernel_space.start,
+ .limit_range_size = rt_kernel_space.size,
+ .map_size = end - start,
+ .prefer = (void *)(start - PV_OFFSET),
+ };
+
+ if (rt_aspace_map_phy(&rt_kernel_space, &hint, MMU_MAP_K_RWCB,
+ start >> MM_PAGE_SHIFT, &va))
+ {
+ LOG_E("map %s [0x%lx, 0x%lx] failed", region->name,
+ (unsigned long)start, (unsigned long)end);
+ return -RT_ERROR;
+ }
+
+ return RT_EOK;
+}
+
+/**
* @brief Install a DMA memory pool from a region descriptor
*
* Creates a new DMA pool covering the given memory region. Allocates
* a bitmap for page tracking. Pools under 4GB are marked RT_DMA_F_32BITS.
- * Pools are automatically marked RT_DMA_F_LINEAR.
+ * Pools are automatically marked RT_DMA_F_LINEAR and mapped into kernel VA.
*
* Must be called with the DMA pools lock NOT held.
*
@@ -824,6 +1094,11 @@ static struct rt_dma_pool *dma_pool_install(rt_region_t *region)
rt_list_insert_before(&dma_pool_nodes, &pool->list);
region_pool_unlock();
+ if (dma_pool_map_linear(pool))
+ {
+ LOG_W("linear map failed for pool %s", region->name);
+ }
+
return pool;
_fail:
diff --git a/libcpu/aarch64/common/setup.c b/libcpu/aarch64/common/setup.c
index 89acc15009..f91f4e250e 100644
--- a/libcpu/aarch64/common/setup.c
+++ b/libcpu/aarch64/common/setup.c
@@ -44,13 +44,11 @@ extern struct cpu_ops_t cpu_spin_table_ops;
extern int rt_hw_cpu_id(void);
#endif
-rt_uint64_t rt_cpu_mpidr_table[] =
-{
+rt_uint64_t rt_cpu_mpidr_table[] = {
[RT_CPUS_NR] = 0,
};
-static struct cpu_ops_t *cpu_ops[] =
-{
+static struct cpu_ops_t *cpu_ops[] = {
#ifdef RT_USING_SMP
&cpu_psci_ops,
&cpu_spin_table_ops,
@@ -60,7 +58,7 @@ static struct cpu_ops_t *cpu_ops[] =
#ifdef ARCH_USING_CPUIDLE
struct rt_dvfs_idle *cpu_idle[RT_CPUS_NR] = {};
#endif
-static struct rt_ofw_node *cpu_np[RT_CPUS_NR] = { };
+static struct rt_ofw_node *cpu_np[RT_CPUS_NR] = {};
void rt_hw_fdt_install_early(void *fdt)
{
@@ -100,15 +98,15 @@ static void cpu_loops_per_tick_init(void)
while (cpu_get_cycles() < cycles_end1)
{
- __asm__ volatile ("nop");
- __asm__ volatile ("add %0, %0, #1":"=r"(cycles_count1));
+ __asm__ volatile("nop");
+ __asm__ volatile("add %0, %0, #1" : "=r"(cycles_count1));
}
cycles_end2 = cpu_get_cycles() + step;
while (cpu_get_cycles() < cycles_end2)
{
- __asm__ volatile ("add %0, %0, #1":"=r"(cycles_count2));
+ __asm__ volatile("add %0, %0, #1" : "=r"(cycles_count2));
}
if ((rt_int32_t)(cycles_count2 - cycles_count1) > 0)
@@ -146,7 +144,7 @@ rt_weak void rt_hw_idle_wfi(void)
{
rt_dvfs_idle_entry(cpuidle);
- __asm__ volatile ("wfi");
+ __asm__ volatile("wfi");
rt_dvfs_idle_exit(cpuidle);
@@ -154,7 +152,7 @@ rt_weak void rt_hw_idle_wfi(void)
}
#endif /* ARCH_USING_CPUIDLE */
- __asm__ volatile ("wfi");
+ __asm__ volatile("wfi");
}
static void system_vectors_init(void)
@@ -266,9 +264,9 @@ void rt_hw_common_setup(void)
system_vectors_init();
#ifdef RT_USING_SMART
- rt_hw_mmu_map_init(&rt_kernel_space, (void*)0xffffffff00000000, 0x20000000, MMUTable, pv_off);
+ rt_hw_mmu_map_init(&rt_kernel_space, (void *)0xffffffff00000000, 0x20000000, MMUTable, pv_off);
#else
- rt_hw_mmu_map_init(&rt_kernel_space, (void*)0xffffd0000000, 0x20000000, MMUTable, 0);
+ rt_hw_mmu_map_init(&rt_kernel_space, (void *)0xffffd0000000, 0x20000000, MMUTable, 0);
#endif
kernel_start = RT_ALIGN_DOWN((rt_size_t)rt_kmem_v2p((void *)&_start) - 64, ARCH_PAGE_SIZE);
@@ -282,9 +280,9 @@ void rt_hw_common_setup(void)
platform_mem_region.start = kernel_start;
#ifndef RT_USING_BUILTIN_FDT
- platform_mem_region.end = fdt_end;
+ platform_mem_region.end = fdt_end;
#else
- platform_mem_region.end = init_page_end;
+ platform_mem_region.end = init_page_end;
(void)fdt_start;
(void)fdt_end;
#endif
@@ -342,10 +340,14 @@ void rt_hw_common_setup(void)
rt_fdt_scan_memory();
#ifdef RT_USING_DMA
- do {
+ do
+ {
const char *bootargs;
rt_ubase_t dma_pool_base;
rt_size_t cma_size = 0, coherent_pool_size = 0;
+ rt_size_t pool_total;
+ struct rt_memblock *memory;
+ struct rt_mmblk_reg *mem_reg;
if (!rt_fdt_bootargs_select("cma=", 0, &bootargs))
{
@@ -362,21 +364,94 @@ void rt_hw_common_setup(void)
if (cma_size || coherent_pool_size)
{
LOG_W("DMA pool %s=%u > %s=%u",
- "CMA", cma_size, "coherent-pool", coherent_pool_size);
+ "CMA", cma_size, "coherent-pool", coherent_pool_size);
}
cma_size = 8 * SIZE_MB;
coherent_pool_size = 2 * SIZE_MB;
}
+ pool_total = cma_size + coherent_pool_size;
+
+ /*
+ * Default: place the pool after early boot reservations (kernel/heap/fdt),
+ * below 4G for RT_DMA_F_32BITS (PCIe).
+ *
+ * On a single contiguous RAM bank, keep the pool at the bank tail so
+ * memblock installs one contiguous free range (avoids an MMU hole).
+ * The platform must expose RAM through dma_pool_base + pool_total.
+ */
dma_pool_base = platform_mem_region.end;
+
+ memory = rt_memblock_get_memory();
+ {
+ rt_uint32_t mem_count = 0;
+ rt_size_t mem_span_start = 0, mem_span_end = 0;
+
+ rt_slist_for_each_entry(mem_reg, &memory->reg_list, node)
+ {
+ mem_count++;
+ if (mem_count == 1)
+ {
+ mem_span_start = mem_reg->memreg.start;
+ }
+ if (mem_reg->memreg.end > mem_span_end)
+ {
+ mem_span_end = mem_reg->memreg.end;
+ }
+ }
+
+ if (mem_count == 1 &&
+ dma_pool_base + pool_total <= mem_span_end &&
+ dma_pool_base >= mem_span_start &&
+ dma_pool_base + pool_total < mem_span_end)
+ {
+ dma_pool_base = RT_ALIGN_DOWN(mem_span_end - pool_total, ARCH_PAGE_SIZE);
+ }
+ }
+
+ if (dma_pool_base + pool_total > (4UL * SIZE_GB))
+ {
+ rt_size_t zone_end = 0;
+
+ rt_slist_for_each_entry(mem_reg, &memory->reg_list, node)
+ {
+ rt_size_t start = mem_reg->memreg.start;
+ rt_size_t end = mem_reg->memreg.end;
+
+ if (start >= (4UL * SIZE_GB))
+ {
+ continue;
+ }
+
+ if (end > (4UL * SIZE_GB))
+ {
+ end = (4UL * SIZE_GB);
+ }
+
+ if (end > zone_end)
+ {
+ zone_end = end;
+ }
+ }
+
+ if (zone_end < platform_mem_region.end + pool_total)
+ {
+ LOG_E("No room for sub-4G DMA pool (%lu bytes)",
+ (unsigned long)pool_total);
+ break;
+ }
+
+ dma_pool_base = RT_ALIGN_DOWN(zone_end - pool_total, ARCH_PAGE_SIZE);
+ }
+
rt_memblock_reserve_memory("dma-pool",
- dma_pool_base, dma_pool_base + cma_size + coherent_pool_size, MEMBLOCK_NONE);
+ dma_pool_base, dma_pool_base + pool_total, MEMBLOCK_NONE);
if (rt_dma_pool_extract(cma_size, coherent_pool_size))
{
LOG_E("Alloc DMA pool %s=%u, %s=%u fail",
- "CMA", cma_size, "coherent-pool", coherent_pool_size);
+ "CMA", cma_size, "coherent-pool", coherent_pool_size);
}
} while (0);
#endif /* RT_USING_DMA */