From 830ce1f69238136c0197858242f16cf44e0d6cb9 Mon Sep 17 00:00:00 2001
From: hc <hc@nodka.com>
Date: Fri, 01 Nov 2024 03:09:37 +0000
Subject: [PATCH] gpio config
---
kernel/drivers/dma/bcm2835-dma.c | 148 ++++++++++++++++++++++++++++++++++++++++--------
1 files changed, 122 insertions(+), 26 deletions(-)
diff --git a/kernel/drivers/dma/bcm2835-dma.c b/kernel/drivers/dma/bcm2835-dma.c
index 630dfbb..6161f76 100644
--- a/kernel/drivers/dma/bcm2835-dma.c
+++ b/kernel/drivers/dma/bcm2835-dma.c
@@ -29,6 +29,7 @@
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/spinlock.h>
+#include <linux/irqstage.h>
#include <linux/of.h>
#include <linux/of_dma.h>
@@ -435,10 +436,20 @@
writel(BCM2835_DMA_RESET, chan_base + BCM2835_DMA_CS);
}
+static inline void bcm2835_dma_enable_channel(struct bcm2835_chan *c)
+{
+ writel(c->desc->cb_list[0].paddr, c->chan_base + BCM2835_DMA_ADDR);
+ writel(BCM2835_DMA_ACTIVE, c->chan_base + BCM2835_DMA_CS);
+}
+
+static inline bool bcm2835_dma_oob_capable(void)
+{
+ return IS_ENABLED(CONFIG_DMA_BCM2835_OOB);
+}
+
static void bcm2835_dma_start_desc(struct bcm2835_chan *c)
{
struct virt_dma_desc *vd = vchan_next_desc(&c->vc);
- struct bcm2835_desc *d;
if (!vd) {
c->desc = NULL;
@@ -447,10 +458,41 @@
list_del(&vd->node);
- c->desc = d = to_bcm2835_dma_desc(&vd->tx);
+ c->desc = to_bcm2835_dma_desc(&vd->tx);
+ if (!bcm2835_dma_oob_capable() || !vchan_oob_pulsed(vd))
+ bcm2835_dma_enable_channel(c);
+}
- writel(d->cb_list[0].paddr, c->chan_base + BCM2835_DMA_ADDR);
- writel(BCM2835_DMA_ACTIVE, c->chan_base + BCM2835_DMA_CS);
+static bool do_channel(struct bcm2835_chan *c, struct bcm2835_desc *d)
+{
+ struct dmaengine_desc_callback cb;
+
+ if (running_oob()) {
+ if (!vchan_oob_handled(&d->vd))
+ return false;
+ dmaengine_desc_get_callback(&d->vd.tx, &cb);
+ if (dmaengine_desc_callback_valid(&cb)) {
+ vchan_unlock(&c->vc);
+ dmaengine_desc_callback_invoke(&cb, NULL);
+ vchan_lock(&c->vc);
+ }
+ return true;
+ }
+
+ if (d->cyclic) {
+ /* call the cyclic callback */
+ vchan_cyclic_callback(&d->vd);
+ } else if (!readl(c->chan_base + BCM2835_DMA_ADDR)) {
+ vchan_cookie_complete(&c->desc->vd);
+ bcm2835_dma_start_desc(c);
+ }
+
+ return true;
+}
+
+static inline bool is_base_irq_handler(void)
+{
+ return !bcm2835_dma_oob_capable() || running_oob();
}
static irqreturn_t bcm2835_dma_callback(int irq, void *data)
@@ -460,7 +502,7 @@
unsigned long flags;
/* check the shared interrupt */
- if (c->irq_flags & IRQF_SHARED) {
+ if (is_base_irq_handler() && c->irq_flags & IRQF_SHARED) {
/* check if the interrupt is enabled */
flags = readl(c->chan_base + BCM2835_DMA_CS);
/* if not set then we are not the reason for the irq */
@@ -468,7 +510,8 @@
return IRQ_NONE;
}
- spin_lock_irqsave(&c->vc.lock, flags);
+ /* CAUTION: If running in-band, hard irqs are on. */
+ vchan_lock_irqsave(&c->vc, flags);
/*
* Clear the INT flag to receive further interrupts. Keep the channel
@@ -477,22 +520,27 @@
* if this IRQ handler is threaded.) If the channel is finished, it
* will remain idle despite the ACTIVE flag being set.
*/
- writel(BCM2835_DMA_INT | BCM2835_DMA_ACTIVE,
- c->chan_base + BCM2835_DMA_CS);
+ if (is_base_irq_handler())
+ writel(BCM2835_DMA_INT | BCM2835_DMA_ACTIVE,
+ c->chan_base + BCM2835_DMA_CS);
d = c->desc;
+ if (!d)
+ goto out;
- if (d) {
- if (d->cyclic) {
- /* call the cyclic callback */
- vchan_cyclic_callback(&d->vd);
- } else if (!readl(c->chan_base + BCM2835_DMA_ADDR)) {
- vchan_cookie_complete(&c->desc->vd);
- bcm2835_dma_start_desc(c);
- }
+ if (bcm2835_dma_oob_capable() && running_oob()) {
+ /*
+ * If we cannot process this from the out-of-band
+ * stage, schedule a callback from in-band context.
+ */
+ if (!do_channel(c, d))
+ irq_post_inband(irq);
+ } else {
+ do_channel(c, d);
}
- spin_unlock_irqrestore(&c->vc.lock, flags);
+out:
+ vchan_unlock_irqrestore(&c->vc, flags);
return IRQ_HANDLED;
}
@@ -571,7 +619,7 @@
if (ret == DMA_COMPLETE || !txstate)
return ret;
- spin_lock_irqsave(&c->vc.lock, flags);
+ vchan_lock_irqsave(&c->vc, flags);
vd = vchan_find_desc(&c->vc, cookie);
if (vd) {
txstate->residue =
@@ -592,7 +640,7 @@
txstate->residue = 0;
}
- spin_unlock_irqrestore(&c->vc.lock, flags);
+ vchan_unlock_irqrestore(&c->vc, flags);
return ret;
}
@@ -602,12 +650,35 @@
struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
unsigned long flags;
- spin_lock_irqsave(&c->vc.lock, flags);
+ vchan_lock_irqsave(&c->vc, flags);
if (vchan_issue_pending(&c->vc) && !c->desc)
bcm2835_dma_start_desc(c);
- spin_unlock_irqrestore(&c->vc.lock, flags);
+ vchan_unlock_irqrestore(&c->vc, flags);
}
+
+#ifdef CONFIG_DMA_BCM2835_OOB
+static int bcm2835_dma_pulse_oob(struct dma_chan *chan)
+{
+ struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
+ unsigned long flags;
+ int ret = -EIO;
+
+ vchan_lock_irqsave(&c->vc, flags);
+ if (c->desc && vchan_oob_pulsed(&c->desc->vd)) {
+ bcm2835_dma_enable_channel(c);
+ ret = 0;
+ }
+ vchan_unlock_irqrestore(&c->vc, flags);
+
+ return ret;
+}
+#else
+static int bcm2835_dma_pulse_oob(struct dma_chan *chan)
+{
+ return -ENOTSUPP;
+}
+#endif
static struct dma_async_tx_descriptor *bcm2835_dma_prep_dma_memcpy(
struct dma_chan *chan, dma_addr_t dst, dma_addr_t src,
@@ -649,6 +720,15 @@
u32 info = BCM2835_DMA_WAIT_RESP;
u32 extra = BCM2835_DMA_INT_EN;
size_t frames;
+
+ if (!bcm2835_dma_oob_capable()) {
+ if (flags & (DMA_OOB_INTERRUPT|DMA_OOB_PULSE)) {
+ dev_err(chan->device->dev,
+ "%s: out-of-band slave transfers disabled\n",
+ __func__);
+ return NULL;
+ }
+ }
if (!is_slave_direction(direction)) {
dev_err(chan->device->dev,
@@ -715,7 +795,21 @@
return NULL;
}
- if (flags & DMA_PREP_INTERRUPT)
+ if (!bcm2835_dma_oob_capable()) {
+ if (flags & DMA_OOB_INTERRUPT) {
+ dev_err(chan->device->dev,
+ "%s: out-of-band cyclic transfers disabled\n",
+ __func__);
+ return NULL;
+ }
+ } else if (flags & DMA_OOB_PULSE) {
+ dev_err(chan->device->dev,
+ "%s: no pulse mode with out-of-band cyclic transfers\n",
+ __func__);
+ return NULL;
+ }
+
+ if (flags & (DMA_PREP_INTERRUPT|DMA_OOB_INTERRUPT))
extra |= BCM2835_DMA_INT_EN;
else
period_len = buf_len;
@@ -791,7 +885,7 @@
unsigned long flags;
LIST_HEAD(head);
- spin_lock_irqsave(&c->vc.lock, flags);
+ vchan_lock_irqsave(&c->vc, flags);
/* stop DMA activity */
if (c->desc) {
@@ -801,7 +895,7 @@
}
vchan_get_all_descriptors(&c->vc, &head);
- spin_unlock_irqrestore(&c->vc.lock, flags);
+ vchan_unlock_irqrestore(&c->vc, flags);
vchan_dma_desc_free_list(&c->vc, &head);
return 0;
@@ -912,11 +1006,13 @@
dma_cap_set(DMA_SLAVE, od->ddev.cap_mask);
dma_cap_set(DMA_PRIVATE, od->ddev.cap_mask);
dma_cap_set(DMA_CYCLIC, od->ddev.cap_mask);
+ dma_cap_set(DMA_OOB, od->ddev.cap_mask);
dma_cap_set(DMA_MEMCPY, od->ddev.cap_mask);
od->ddev.device_alloc_chan_resources = bcm2835_dma_alloc_chan_resources;
od->ddev.device_free_chan_resources = bcm2835_dma_free_chan_resources;
od->ddev.device_tx_status = bcm2835_dma_tx_status;
od->ddev.device_issue_pending = bcm2835_dma_issue_pending;
+ od->ddev.device_pulse_oob = bcm2835_dma_pulse_oob;
od->ddev.device_prep_dma_cyclic = bcm2835_dma_prep_dma_cyclic;
od->ddev.device_prep_slave_sg = bcm2835_dma_prep_slave_sg;
od->ddev.device_prep_dma_memcpy = bcm2835_dma_prep_dma_memcpy;
@@ -982,10 +1078,10 @@
continue;
/* check if there are other channels that also use this irq */
- irq_flags = 0;
+ irq_flags = IS_ENABLED(CONFIG_DMA_BCM2835_OOB) ? IRQF_OOB : 0;
for (j = 0; j <= BCM2835_DMA_MAX_DMA_CHAN_SUPPORTED; j++)
if ((i != j) && (irq[j] == irq[i])) {
- irq_flags = IRQF_SHARED;
+ irq_flags |= IRQF_SHARED;
break;
}
--
Gitblit v1.6.2