Skip to content
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ properties:
- enum:
- microchip,mpfs-pdma
- sifive,fu540-c000-pdma
- sifive,fu740-c000-pdma
- const: sifive,pdma0
description:
Should be "sifive,<chip>-pdma" and "sifive,pdma<version>".
Expand Down
9 changes: 9 additions & 0 deletions arch/riscv/boot/dts/sifive/fu740-c000.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,15 @@
clocks = <&prci FU740_PRCI_CLK_PCLK>;
status = "disabled";
};
dma: dma-controller@3000000 {
compatible = "sifive,fu740-c000-pdma", "sifive,pdma0";
reg = <0x0 0x3000000 0x0 0x100000>;
interrupt-parent = <&plic0>;
interrupts = <11>, <12>, <13>, <14>, <15>, <16>, <17>, <18>;
dma-channels = <4>;
clocks = <&prci FU740_PRCI_CLK_PCLK>;
#dma-cells = <1>;
};
pcie@e00000000 {
compatible = "sifive,fu740-pcie";
#address-cells = <3>;
Expand Down
63 changes: 51 additions & 12 deletions drivers/dma/sf-pdma/sf-pdma.c
Original file line number Diff line number Diff line change
Expand Up @@ -298,33 +298,56 @@ static void sf_pdma_free_desc(struct virt_dma_desc *vdesc)
static void sf_pdma_donebh_tasklet(struct tasklet_struct *t)
{
struct sf_pdma_chan *chan = from_tasklet(chan, t, done_tasklet);
struct sf_pdma_desc *desc;
unsigned long flags;

spin_lock_irqsave(&chan->lock, flags);
if (chan->xfer_err) {
chan->retries = MAX_RETRY;
chan->status = DMA_COMPLETE;
chan->xfer_err = false;
spin_lock_irqsave(&chan->vchan.lock, flags);
desc = chan->desc;
if (!desc) {
/*
* The descriptor was already freed (e.g., by terminate_all
* or completion on another CPU). Nothing to do.
*/
spin_unlock_irqrestore(&chan->vchan.lock, flags);
return;
}
spin_unlock_irqrestore(&chan->lock, flags);

spin_lock_irqsave(&chan->vchan.lock, flags);
list_del(&chan->desc->vdesc.node);
vchan_cookie_complete(&chan->desc->vdesc);
list_del(&desc->vdesc.node);
vchan_cookie_complete(&desc->vdesc);

chan->desc = sf_pdma_get_first_pending_desc(chan);
if (chan->desc)
sf_pdma_xfer_desc(chan);

spin_unlock_irqrestore(&chan->vchan.lock, flags);

spin_lock_irqsave(&chan->lock, flags);
if (chan->xfer_err) {
chan->retries = MAX_RETRY;
chan->status = DMA_COMPLETE;
chan->xfer_err = false;
}
spin_unlock_irqrestore(&chan->lock, flags);
}

static void sf_pdma_errbh_tasklet(struct tasklet_struct *t)
{
struct sf_pdma_chan *chan = from_tasklet(chan, t, err_tasklet);
struct sf_pdma_desc *desc = chan->desc;
struct sf_pdma_desc *desc;
unsigned long flags;

spin_lock_irqsave(&chan->vchan.lock, flags);
desc = chan->desc;
if (!desc) {
/*
* The descriptor was already freed (e.g., by terminate_all
* or completion on another CPU). Nothing to do.
*/
spin_unlock_irqrestore(&chan->vchan.lock, flags);
return;
}
spin_unlock_irqrestore(&chan->vchan.lock, flags);

spin_lock_irqsave(&chan->lock, flags);
if (chan->retries <= 0) {
/* fail to recover */
Expand All @@ -346,9 +369,25 @@ static irqreturn_t sf_pdma_done_isr(int irq, void *dev_id)
struct sf_pdma_chan *chan = dev_id;
struct pdma_regs *regs = &chan->regs;
u64 residue;
u32 control_reg;

spin_lock(&chan->vchan.lock);
writel((readl(regs->ctrl)) & ~PDMA_DONE_STATUS_MASK, regs->ctrl);
control_reg = readl(regs->ctrl);
if (control_reg & PDMA_ERR_STATUS_MASK) {
spin_unlock(&chan->vchan.lock);
return IRQ_HANDLED;
}

/*
* Check if DONE bit is still set. If not, the error ISR on another
* CPU has already cleared it, so abort to avoid double-processing.
*/
if (!(control_reg & PDMA_DONE_STATUS_MASK)) {
spin_unlock(&chan->vchan.lock);
return IRQ_HANDLED;
}

writel((control_reg & ~PDMA_DONE_STATUS_MASK), regs->ctrl);
residue = readq(regs->residue);

if (!residue) {
Expand All @@ -375,7 +414,7 @@ static irqreturn_t sf_pdma_err_isr(int irq, void *dev_id)
struct pdma_regs *regs = &chan->regs;

spin_lock(&chan->lock);
writel((readl(regs->ctrl)) & ~PDMA_ERR_STATUS_MASK, regs->ctrl);
writel((readl(regs->ctrl)) & ~(PDMA_DONE_STATUS_MASK | PDMA_ERR_STATUS_MASK), regs->ctrl);
spin_unlock(&chan->lock);

tasklet_schedule(&chan->err_tasklet);
Expand Down
4 changes: 2 additions & 2 deletions drivers/dma/sf-pdma/sf-pdma.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

#define PDMA_MAX_NR_CH 4

#define PDMA_BASE_ADDR 0x3000000
#define PDMA_BASE_OFFSET 0x80000
#define PDMA_CHAN_OFFSET 0x1000

/* Register Offset */
Expand Down Expand Up @@ -54,7 +54,7 @@
/* Error Recovery */
#define MAX_RETRY 1

#define SF_PDMA_REG_BASE(ch) (pdma->membase + (PDMA_CHAN_OFFSET * (ch)))
#define SF_PDMA_REG_BASE(ch) (pdma->membase + PDMA_BASE_OFFSET + (PDMA_CHAN_OFFSET * (ch)))

struct pdma_regs {
/* read-write regs */
Expand Down
Loading