[PATCH 05/12] mfd: flexcard: add DMA interrupts
From: Holger Dengler
Date: Tue Dec 13 2016 - 19:16:17 EST
The Flexcard comprise an interrupt controller for the attached
tinys, timer, a Flexray related trigger and a second one for DMA.
Both controllers share a single IRQ line.
Add the DMA Controller interrupts.
Signed-off-by: Benedikt Spranger <b.spranger@xxxxxxxxxxxxx>
Signed-off-by: Holger Dengler <dengler@xxxxxxxxxxxxx>
cc: Lee Jones <lee.jones@xxxxxxxxxx>
---
drivers/mfd/flexcard_irq.c | 71 ++++++++++++++++++++++++++++++++++++++++++--
include/linux/mfd/flexcard.h | 2 ++
2 files changed, 71 insertions(+), 2 deletions(-)
diff --git a/drivers/mfd/flexcard_irq.c b/drivers/mfd/flexcard_irq.c
index fa2063f..15acd18 100644
--- a/drivers/mfd/flexcard_irq.c
+++ b/drivers/mfd/flexcard_irq.c
@@ -23,6 +23,12 @@
#include <linux/mfd/core.h>
#include <linux/mfd/flexcard.h>
+/*
+ * Bit 31 in dma interrupt register:
+ * DMA interrupt enable. Must be 1 to enable the DMA interrupts
+ */
+#define FLEXCARD_DMA_IRER_DIRE (1U << 31)
+
struct fc_irq_tab {
u32 mskcache;
u32 mskoffs;
@@ -49,6 +55,10 @@ struct fc_irq_tab {
#define DEVACK_OFFS offsetof(struct fc_bar0, conf.irs)
#define DEVMSK_CACHE offsetof(struct flexcard_device, dev_irqmsk)
+#define DMAMSK_OFFS offsetof(struct fc_bar0, dma.dma_irer)
+#define DMAACK_OFFS offsetof(struct fc_bar0, dma.dma_irsr)
+#define DMAMSK_CACHE offsetof(struct flexcard_device, dma_irqmsk)
+
#define dev_to_irq_tab_ack(s, m, a) \
to_irq_tab_ack(s, DEVMSK_CACHE, DEVMSK_OFFS, m, \
DEVACK_OFFS, a)
@@ -56,6 +66,10 @@ struct fc_irq_tab {
#define dev_to_irq_tab(s, m) \
to_irq_tab(s, DEVMSK_CACHE, DEVMSK_OFFS, m)
+#define dma_to_irq_tab_ack(s, m, a) \
+ to_irq_tab_ack(s, DMAMSK_CACHE, DMAMSK_OFFS, m, \
+ DMAACK_OFFS, a)
+
static const struct fc_irq_tab flexcard_irq_tab[] = {
/* Device Interrupts */
dev_to_irq_tab_ack(28, 28, 0), /* TIMER */
@@ -75,6 +89,11 @@ static const struct fc_irq_tab flexcard_irq_tab[] = {
dev_to_irq_tab(3, 14), /* CC2T0 */
dev_to_irq_tab(24, 16), /* CC3T0 */
dev_to_irq_tab(20, 17), /* CC4T0 */
+ /* DMA Interrupts */
+ dma_to_irq_tab_ack(0, 0, 0), /* DMA_C0 */
+ dma_to_irq_tab_ack(1, 1, 1), /* DMA_TE */
+ dma_to_irq_tab_ack(4, 4, 4), /* DMA_TI */
+ dma_to_irq_tab_ack(5, 5, 5), /* DMA_CBL */
};
#define NR_FLEXCARD_IRQ ARRAY_SIZE(flexcard_irq_tab)
@@ -96,6 +115,10 @@ static const struct fc_irq_tab flexcard_irq_tab[] = {
(1U << 3) | \
(1U << 24) | \
(1U << 20))
+#define VALID_DMAIRQ_MSK ((1U << 0) | \
+ (1U << 1) | \
+ (1U << 4) | \
+ (1U << 5))
static irqreturn_t flexcard_demux(int irq, void *data)
{
@@ -104,6 +127,7 @@ static irqreturn_t flexcard_demux(int irq, void *data)
unsigned int slot, cur, stat;
stat = readl(&priv->bar0->conf.irs) & VALID_DEVIRQ_MSK;
+ stat |= readl(&priv->bar0->dma.dma_irsr) & VALID_DMAIRQ_MSK;
while (stat) {
slot = __ffs(stat);
stat &= (1 << slot);
@@ -196,6 +220,30 @@ static const struct irq_domain_ops flexcard_irq_domain_ops = {
.map = flexcard_irq_domain_map,
};
+static struct irq_chip flexcard_dma_irq_chip = {
+ .name = "flexcard_dma_irq",
+ .irq_ack = flexcard_irq_ack,
+ .irq_mask = flexcard_irq_mask,
+ .irq_unmask = flexcard_irq_unmask,
+};
+
+static int flexcard_dma_irq_domain_map(struct irq_domain *d, unsigned int irq,
+ irq_hw_number_t hw)
+{
+ struct flexcard_device *priv = d->host_data;
+
+ irq_set_chip_and_handler_name(irq, &flexcard_dma_irq_chip,
+ handle_level_irq, "flexcard-dma");
+ irq_set_chip_data(irq, priv);
+ irq_modify_status(irq, IRQ_NOREQUEST | IRQ_NOAUTOEN, IRQ_NOPROBE);
+
+ return 0;
+}
+
+static const struct irq_domain_ops flexcard_dma_irq_domain_ops = {
+ .map = flexcard_dma_irq_domain_map,
+};
+
int flexcard_setup_irq(struct pci_dev *pdev)
{
struct flexcard_device *priv = pci_get_drvdata(pdev);
@@ -217,9 +265,27 @@ int flexcard_setup_irq(struct pci_dev *pdev)
priv->irq_domain = domain;
+ domain = irq_domain_add_linear(NULL, NR_FLEXCARD_IRQ,
+ &flexcard_dma_irq_domain_ops, priv);
+ if (!domain) {
+ dev_err(&pdev->dev, "could not request dma irq domain\n");
+ ret = -ENODEV;
+ goto out_irq;
+ }
+ priv->dma_domain = domain;
+
+ /* DMA IRQs must be device-globally enabled by setting bit 31 to 1 */
+ writel(FLEXCARD_DMA_IRER_DIRE, &priv->bar0->dma.dma_irer);
+
ret = flexcard_req_irq(pdev);
if (ret)
- irq_domain_remove(priv->irq_domain);
+ goto out_dma;
+
+ return 0;
+out_dma:
+ irq_domain_remove(priv->dma_domain);
+out_irq:
+ irq_domain_remove(priv->irq_domain);
return ret;
}
@@ -228,11 +294,12 @@ void flexcard_remove_irq(struct pci_dev *pdev)
{
struct flexcard_device *priv = pci_get_drvdata(pdev);
- /* Disable all subirqs */
+ /* Disable all subirqs (including global DMA-IRQ bit 31) */
writel(0, &priv->bar0->conf.irc);
writel(0, &priv->bar0->dma.dma_irer);
free_irq(pdev->irq, priv);
pci_disable_msi(pdev);
+ irq_domain_remove(priv->dma_domain);
irq_domain_remove(priv->irq_domain);
}
diff --git a/include/linux/mfd/flexcard.h b/include/linux/mfd/flexcard.h
index 6cb8ad0..819c6ef 100644
--- a/include/linux/mfd/flexcard.h
+++ b/include/linux/mfd/flexcard.h
@@ -98,10 +98,12 @@ struct flexcard_device {
struct pci_dev *pdev;
raw_spinlock_t irq_lock;
struct irq_domain *irq_domain;
+ struct irq_domain *dma_domain;
struct fc_bar0 __iomem *bar0;
struct mfd_cell *cells;
struct resource *res;
u32 dev_irqmsk;
+ u32 dma_irqmsk;
};
int flexcard_setup_irq(struct pci_dev *pdev);
--
2.1.4