Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 65 additions & 3 deletions components/drivers/dma/dma-pl330.c
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,8 @@

/** @brief Maximum microcode program size in bytes (128 instructions) */
#define PL330_MICROCODE_SIZE 128
/* Maximum time to drain DMAEND before reusing a channel. */
#define PL330_STOP_TIMEOUT_US 1000
/** @brief Maximum number of AMBA IRQs */
#define AMBA_NR_IRQS 9

Expand Down Expand Up @@ -596,7 +598,7 @@ static int pl330_cmd_dmalp(rt_uint8_t *microcode, rt_uint8_t lc, rt_uint16_t loo
* | iter[7:0] |0 0 1 0 0 0 lc 0
*/
*microcode++ = PL330_CMD_DMALP | ((lc & 1) << 1);
*microcode |= loops - 1;
*microcode = loops - 1;

LOG_D("DMALP %u (lc: %c)", loops - 1, lc ? '1' : '0');

Expand Down Expand Up @@ -714,6 +716,35 @@ static rt_uint32_t pl330_chan_id(struct pl330 *pl330, struct pl330_chan *pc)
return pc - pl330->chans;
}

static rt_err_t pl330_wait_stopped(struct pl330 *pl330, struct pl330_chan *pc)
{
rt_uint32_t csr;
rt_uint32_t id = pl330_chan_id(pl330, pc);
rt_uint32_t timeout = PL330_STOP_TIMEOUT_US;

/* DMASEV does not imply DMAEND has retired; the microcode is still in use. */
for (;;)
{
csr = HWREG32(pl330->regs + PL330_REG_CSR(id)) & 0xf;
if (csr == PL330_CS_STOP)
{
return RT_EOK;
}
if (csr == PL330_CS_FAULT || csr == PL330_CS_FLTCMP)
{
return -RT_EIO;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 等待 DMAKILL 完成时不应因旧故障状态立即退出

pl330_dma_stop() 写入 DBGCMD 后立即调用此函数,但调试指令可能需要若干时钟周期才开始执行。如果第一次读取 CSR 时仍是原来的 FAULT/FLTCMP,这里就返回 -RT_EIO,没有等到 DMAKILL 将通道推进到 STOP,导致停止操作误报失败,也没有完成本次修改预期的同步停止。

建议区分准备/启动前的状态检查与 DMAKILL 后的停止等待:后者应允许故障状态在指令执行前短暂存在,并有界等待 STOP。主机桩测试中,将 CSR 设置为 FAULT、计划下一次轮询转为 STOP,当前函数会在第一次读取后直接退出。

依据:ARM PL330 技术参考手册 第 2.5.1 节说明调试指令可能延迟若干周期执行,第 4.3.10 节说明 DMAKILL 最终使通道进入 Stopped。

}
if (!timeout)
{
LOG_E("Channel[%u] stop timeout, csr=%x", id, csr);
return -RT_ETIMEOUT;
}

--timeout;
rt_hw_us_delay(1);
}
}

/**
* @brief Request a DMA channel from the PL330 controller
*
Expand Down Expand Up @@ -792,10 +823,16 @@ 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_err_t err;
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);

if ((err = pl330_wait_stopped(pl330, pc)))
{
return err;
}

while (HWREG32(pl330->regs + PL330_REG_DBGSTATUS) & PL330_DBGSTATUS_BUSY)
{
rt_hw_cpu_relax();
Expand Down Expand Up @@ -846,6 +883,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_err_t err;
rt_uint32_t id;
struct pl330_chan *pc = rt_container_of(chan, struct pl330_chan, parent);
struct pl330 *pl330 = raw_to_pl330(chan->ctrl);
Expand All @@ -863,7 +901,10 @@ static rt_err_t pl330_dma_stop(struct rt_dma_chan *chan)
HWREG32(pl330->regs + PL330_REG_DBGINST1) = 0;
HWREG32(pl330->regs + PL330_REG_DBGCMD) = 0;

return RT_EOK;
/* Do not release peripheral clocks or buffers until DMAKILL has retired. */
err = pl330_wait_stopped(pl330, pc);
HWREG32(pl330->regs + PL330_REG_INTCLR) = RT_BIT(id);
return err;
}

/**
Expand Down Expand Up @@ -920,6 +961,11 @@ static rt_err_t pl330_dma_prep_memcpy(struct rt_dma_chan *chan,
struct pl330 *pl330 = raw_to_pl330(chan->ctrl);
struct rt_dma_slave_config *conf = &chan->conf;

if ((err = pl330_wait_stopped(pl330, pc)))
{
return err;
}

mc = pc->microcode;

if ((err = pl330_ccr_config(conf, pl330->mode_ns, &value)))
Expand Down Expand Up @@ -1039,6 +1085,11 @@ static rt_err_t pl330_dma_prep_cyclic(struct rt_dma_chan *chan,
struct pl330 *pl330 = raw_to_pl330(chan->ctrl);
struct rt_dma_slave_config *conf = &chan->conf;

if ((err = pl330_wait_stopped(pl330, pc)))
{
return err;
}

mc = pc->microcode;

if ((err = pl330_ccr_config(conf, pl330->mode_ns, &ccr_val)))
Expand Down Expand Up @@ -1149,6 +1200,11 @@ static rt_err_t pl330_dma_prep_single(struct rt_dma_chan *chan,
struct rt_dma_slave_config *conf = &chan->conf;
rt_uint32_t cond;

if ((err = pl330_wait_stopped(pl330, pc)))
{
return err;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 准备失败后仍需允许停止旧传输

当旧传输仍处于 WFP、循环运行中或已经进入 FAULT 时,这里会返回超时或 I/O 错误。上层 rt_dma_prep_single() 会把该错误写入 chan->prep_err,而 rt_dma_chan_stop() 在发现 prep_err 非零时直接返回,不再调用控制器的 stop(),因此后续清理无法下发 DMAKILL。对于需要 DMAKILL 才能退出的状态,重试准备也无法恢复。

使用本提交中的函数进行主机桩测试,能够复现 prep 返回错误后,调用 rt_dma_chan_stop() 时控制器停止函数的调用次数为 0。建议同步调整框架的停止路径,确保准备失败不会阻止清理此前的活动传输;prep_memcpy 和 prep_cyclic 新增的检查也需要覆盖这一情况。

}

mc = pc->microcode;

if ((err = pl330_ccr_config(conf, pl330->mode_ns, &ccr_val)))
Expand Down Expand Up @@ -1196,6 +1252,12 @@ static rt_err_t pl330_dma_prep_single(struct rt_dma_chan *chan,

cond = conf->src_maxburst > 1 ? PL330_COND_BURST : PL330_COND_SINGLE;

/* Some PL330 integrations only expose peripheral requests as bursts. */
if (pl330->quirk & PL330_QUIRK_PERIPH_BURST)
{
cond = PL330_COND_BURST;
}

if (pc->is_slave)
{
mc += pl330_cmd_dmawfp(mc, cond, pc->peri_id);
Expand Down Expand Up @@ -1291,7 +1353,7 @@ static void pl330_isr(int irqno, void *params)
break;

default:
/* DMASEV may be observed before the following DMAEND executes. */
/* Cyclic events report a period; only prep/start require STOP. */
rt_dma_chan_done(&pc->parent, pc->size);
break;
}
Expand Down
12 changes: 8 additions & 4 deletions components/drivers/dma/dma_pool.c
Original file line number Diff line number Diff line change
Expand Up @@ -664,7 +664,8 @@ static rt_ubase_t dma_pool_alloc(struct rt_dma_pool *pool, rt_size_t size)
return RT_NULL;
}

max_bits = pool->bits - size;
/* The bitmap iterator's limit is exclusive; include the last fit. */
max_bits = pool->bits - size + 1;

rt_bitmap_for_each_clear_bit(pool->map, bit, max_bits)
{
Expand Down Expand Up @@ -834,10 +835,13 @@ static void dma_free(struct rt_device *dev, rt_size_t size,

rt_list_for_each_entry(pool, &dma_pool_nodes, list)
{
if (dma_handle >= pool->region.start &&
dma_handle <= pool->region.end)
/* Adjacent pools share an exclusive end/start boundary. */
if (dma_handle >= pool->start && dma_handle < pool->region.end &&
size <= pool->region.end - dma_handle)
{
if (pool->flags & RT_DMA_F_NOCACHE || !(pool->flags & RT_DMA_F_LINEAR))
if (!(flags & RT_DMA_F_NOMAP) &&
((pool->flags & RT_DMA_F_NOCACHE) ||
(flags & RT_DMA_F_NOCACHE) || !(pool->flags & RT_DMA_F_LINEAR)))
{
rt_iounmap(cpu_addr);
}
Expand Down
28 changes: 25 additions & 3 deletions components/drivers/sdio/host/sdio-dw.c
Original file line number Diff line number Diff line change
Expand Up @@ -849,6 +849,19 @@ static rt_err_t sdio_dw_submit_data_dma(struct sdio_dw *sd, struct rt_mmcsd_data
return -RT_ENOSYS;
}

if (sd->use_dma == TRANS_MODE_IDMAC && !sd->dma_64bit_address)
{
rt_uint64_t addr = (rt_ubase_t)rt_kmem_v2p(sd->last_buf);

/* Buffers can be above the controller's 32-bit DMA aperture. */
if (!sd->last_remain || addr > 0xffffffffULL ||
sd->last_remain - 1 > 0xffffffffULL - addr)
{
sd->dma_ops->stop(sd);
return -RT_ENOSYS;
}
}

sd->using_dma = RT_TRUE;

/*
Expand Down Expand Up @@ -1567,6 +1580,15 @@ static void sdio_dw_init_dma(struct sdio_dw *sd)
goto _no_dma;
}

if (!sd->dma_64bit_address &&
((rt_uint64_t)sd->dma_buf_phy > 0xffffffffULL ||
DESC_RING_BUF_SZ - 1 > 0xffffffffULL - (rt_uint64_t)sd->dma_buf_phy))
{
rt_dma_free_coherent(sd->bus_dev, DESC_RING_BUF_SZ, sd->dma_buf, sd->dma_buf_phy);
sd->dma_buf = RT_NULL;
goto _no_dma;
}

sd->dma_ops = &sdio_dw_idmac_ops;
LOG_D("Using internal DMA controller");
}
Expand Down Expand Up @@ -3217,15 +3239,15 @@ rt_err_t sdio_dw_probe(struct sdio_dw *sd)

rt_strncpy(&dev_name[len], "-cmd11", sizeof(dev_name) - len);
rt_timer_init(&sd->cmd11_timer, dev_name, sdio_dw_cmd11_timer, sd,
0, RT_TIMER_FLAG_PERIODIC);
0, RT_TIMER_FLAG_ONE_SHOT);

rt_strncpy(&dev_name[len], "-cto", sizeof(dev_name) - len);
rt_timer_init(&sd->cto_timer, dev_name, sdio_dw_cto_timer, sd,
0, RT_TIMER_FLAG_PERIODIC);
0, RT_TIMER_FLAG_ONE_SHOT);

rt_strncpy(&dev_name[len], "-dto", sizeof(dev_name) - len);
rt_timer_init(&sd->dto_timer, dev_name, sdio_dw_dto_timer, sd,
0, RT_TIMER_FLAG_PERIODIC);
0, RT_TIMER_FLAG_ONE_SHOT);

mmcsd_change(sd->slot->host);

Expand Down
Loading