Skip to content

bcm2835-dma: Fixes for dma_abort #5482

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 30, 2023
Merged
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
78 changes: 42 additions & 36 deletions drivers/dma/bcm2835-dma.c
Original file line number Diff line number Diff line change
Expand Up @@ -242,15 +242,16 @@ struct bcm2835_desc {
#define BCM2711_DMA40_WR_PAUSED BIT(5) /* Writing is paused */
#define BCM2711_DMA40_DREQ_PAUSED BIT(6) /* Is paused by DREQ flow control */
#define BCM2711_DMA40_WAITING_FOR_WRITES BIT(7) /* Waiting for last write */
// we always want to run in supervisor mode
#define BCM2711_DMA40_PROT (BIT(8)|BIT(9))
#define BCM2711_DMA40_ERR BIT(10)
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we have the BCM2711_DMA40_PROT bits here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Added commit to move PROT bits definition.

#define BCM2711_DMA40_QOS(x) (((x) & 0x1f) << 16)
#define BCM2711_DMA40_PANIC_QOS(x) (((x) & 0x1f) << 20)
#define BCM2711_DMA40_TRANSACTIONS BIT(25)
#define BCM2711_DMA40_WAIT_FOR_WRITES BIT(28)
#define BCM2711_DMA40_DISDEBUG BIT(29)
#define BCM2711_DMA40_ABORT BIT(30)
#define BCM2711_DMA40_HALT BIT(31)
// we always want to run in supervisor mode
#define BCM2711_DMA40_PROT (BIT(8)|BIT(9))

#define BCM2711_DMA40_CS_FLAGS(x) (x & (BCM2711_DMA40_QOS(15) | \
BCM2711_DMA40_PANIC_QOS(15) | \
Expand Down Expand Up @@ -663,30 +664,37 @@ static void bcm2835_dma_fill_cb_chain_with_sg(
static void bcm2835_dma_abort(struct bcm2835_chan *c)
{
void __iomem *chan_base = c->chan_base;
long int timeout = 10000;

/*
* A zero control block address means the channel is idle.
* (The ACTIVE flag in the CS register is not a reliable indicator.)
*/
if (!readl(chan_base + BCM2835_DMA_ADDR))
return;
long timeout = 100;

if (c->is_40bit_channel) {
/* Halt the current DMA */
writel(readl(chan_base + BCM2711_DMA40_CS) | BCM2711_DMA40_HALT,
/*
* A zero control block address means the channel is idle.
* (The ACTIVE flag in the CS register is not a reliable indicator.)
*/
if (!readl(chan_base + BCM2711_DMA40_CB))
return;

/* Pause the current DMA */
writel(readl(chan_base + BCM2711_DMA40_CS) & ~BCM2711_DMA40_ACTIVE,
chan_base + BCM2711_DMA40_CS);

while ((readl(chan_base + BCM2711_DMA40_CS) & BCM2711_DMA40_HALT) && --timeout)
/* wait for outstanding transactions to complete */
while ((readl(chan_base + BCM2711_DMA40_CS) & BCM2711_DMA40_TRANSACTIONS) &&
--timeout)
cpu_relax();

/* Peripheral might be stuck and fail to halt */
/* Peripheral might be stuck and fail to complete */
if (!timeout)
dev_err(c->vc.chan.device->dev,
"failed to halt dma\n");
"failed to complete pause on dma %d (CS:%08x)\n", c->ch,
readl(chan_base + BCM2711_DMA40_CS));

/* Set CS back to default state */
writel(BCM2711_DMA40_PROT, chan_base + BCM2711_DMA40_CS);
writel(0, chan_base + BCM2711_DMA40_CB);

/* Reset the DMA */
writel(readl(chan_base + BCM2711_DMA40_DEBUG) | BCM2711_DMA40_DEBUG_RESET,
chan_base + BCM2711_DMA40_DEBUG);
} else {
/*
* A zero control block address means the channel is idle.
Expand All @@ -695,36 +703,34 @@ static void bcm2835_dma_abort(struct bcm2835_chan *c)
if (!readl(chan_base + BCM2835_DMA_ADDR))
return;

/* Write 0 to the active bit - Pause the DMA */
writel(readl(chan_base + BCM2835_DMA_CS) & ~BCM2835_DMA_ACTIVE,
chan_base + BCM2835_DMA_CS);

/* wait for DMA to be paused */
while ((readl(chan_base + BCM2835_DMA_CS) & BCM2835_DMA_WAITING_FOR_WRITES)
&& --timeout)
cpu_relax();

/* Peripheral might be stuck and fail to signal AXI write responses */
if (!timeout)
dev_err(c->vc.chan.device->dev,
"failed to pause dma\n");

/* We need to clear the next DMA block pending */
writel(0, chan_base + BCM2835_DMA_NEXTCB);

/* Abort the DMA, which needs to be enabled to complete */
writel(readl(chan_base + BCM2835_DMA_CS) | BCM2835_DMA_ABORT | BCM2835_DMA_ACTIVE,
chan_base + BCM2835_DMA_CS);
chan_base + BCM2835_DMA_CS);

/* wait for DMA to have been aborted */
timeout = 10000;
/* wait for DMA to be aborted */
while ((readl(chan_base + BCM2835_DMA_CS) & BCM2835_DMA_ABORT) && --timeout)
cpu_relax();

/* Peripheral might be stuck and fail to signal AXI write responses */
if (!timeout)
/* Write 0 to the active bit - Pause the DMA */
writel(readl(chan_base + BCM2835_DMA_CS) & ~BCM2835_DMA_ACTIVE,
chan_base + BCM2835_DMA_CS);

/*
* Peripheral might be stuck and fail to complete
* This is expected when dreqs are enabled but not asserted
* so only report error in non dreq case
*/
if (!timeout && !(readl(chan_base + BCM2835_DMA_TI) &
(BCM2835_DMA_S_DREQ | BCM2835_DMA_D_DREQ)))
dev_err(c->vc.chan.device->dev,
"failed to abort dma\n");
"failed to complete pause on dma %d (CS:%08x)\n", c->ch,
readl(chan_base + BCM2835_DMA_CS));

/* Set CS back to default state and reset the DMA */
writel(BCM2835_DMA_RESET, chan_base + BCM2835_DMA_CS);
}
}

Expand Down