lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <1481674313-30378-5-git-send-email-dengler@linutronix.de>
Date:   Wed, 14 Dec 2016 01:11:45 +0100
From:   Holger Dengler <dengler@...utronix.de>
To:     Lee Jones <lee.jones@...aro.org>, Arnd Bergmann <arnd@...db.de>,
        Greg Kroah-Hartman <gregkh@...uxfoundation.org>,
        Vinod Koul <vinod.koul@...el.com>
Cc:     linux-kernel@...r.kernel.org, dmaengine@...r.kernel.org,
        Thomas Gleixner <tglx@...utronix.de>,
        Sebastian Siewior <bigeasy@...utronix.de>,
        Juergen Bubeck <bubeck@...ug.com>,
        Peter Mahler <mahler@...ug.com>,
        Holger Dengler <dengler@...utronix.de>,
        Benedikt Spranger <b.spranger@...utronix.de>
Subject: [PATCH 04/12] mfd: flexcard: add interrupt support

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 an interrupt domain for the non-DMA interrupts.

Signed-off-by: Benedikt Spranger <b.spranger@...utronix.de>
Signed-off-by: Holger Dengler <dengler@...utronix.de>
cc: Lee Jones <lee.jones@...aro.org>
---
 drivers/mfd/Kconfig          |   1 +
 drivers/mfd/Makefile         |   1 +
 drivers/mfd/flexcard_core.c  |  14 ++-
 drivers/mfd/flexcard_irq.c   | 238 +++++++++++++++++++++++++++++++++++++++++++
 include/linux/mfd/flexcard.h |   6 ++
 5 files changed, 258 insertions(+), 2 deletions(-)
 create mode 100644 drivers/mfd/flexcard_irq.c

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index a5a12da..85fedf6 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -302,6 +302,7 @@ config MFD_EXYNOS_LPASS
 config MFD_FLEXCARD
 	tristate "Eberspaecher Flexcard PMC II Carrier Board"
 	select MFD_CORE
+	select IRQ_DOMAIN
 	depends on PCI
 	help
 	  This is the core driver for the Eberspaecher Flexcard
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 843e57c..7d9feb4 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -212,4 +212,5 @@ obj-$(CONFIG_MFD_MT6397)	+= mt6397-core.o
 
 obj-$(CONFIG_MFD_ALTERA_A10SR)	+= altera-a10sr.o
 flexcard-objs			:= flexcard_core.o
+flexcard-objs			:= flexcard_core.o flexcard_irq.o
 obj-$(CONFIG_MFD_FLEXCARD)	+= flexcard.o
diff --git a/drivers/mfd/flexcard_core.c b/drivers/mfd/flexcard_core.c
index 73eb726..b7aead5 100644
--- a/drivers/mfd/flexcard_core.c
+++ b/drivers/mfd/flexcard_core.c
@@ -192,7 +192,8 @@ static int flexcard_tiny_probe(struct flexcard_device *priv)
 		offset += FLEXCARD_CAN_OFFSET;
 	}
 
-	return mfd_add_devices(&pdev->dev, 0, priv->cells, nr, NULL, 0, NULL);
+	return mfd_add_devices(&pdev->dev, 0, priv->cells, nr, NULL,
+			       0, priv->irq_domain);
 }
 
 static int flexcard_probe(struct pci_dev *pdev,
@@ -242,10 +243,16 @@ static int flexcard_probe(struct pci_dev *pdev,
 	}
 	priv->cardnr = ret;
 
+	ret = flexcard_setup_irq(pdev);
+	if (ret) {
+		dev_err(&pdev->dev, "unable to setup irq controller: %d", ret);
+		goto out_ida;
+	}
+
 	ret = flexcard_tiny_probe(priv);
 	if (ret) {
 		dev_err(&pdev->dev, "unable to probe tinys: %d", ret);
-		goto out_ida;
+		goto out_remove_irq;
 	}
 
 	ret = flexcard_misc_setup(priv);
@@ -268,6 +275,8 @@ static int flexcard_probe(struct pci_dev *pdev,
 
 out_mfd_dev_remove:
 	mfd_remove_devices(&pdev->dev);
+out_remove_irq:
+	flexcard_remove_irq(pdev);
 out_ida:
 	ida_simple_remove(&flexcard_ida, priv->cardnr);
 out_unmap:
@@ -285,6 +294,7 @@ static void flexcard_remove(struct pci_dev *pdev)
 	struct flexcard_device *priv = pci_get_drvdata(pdev);
 
 	mfd_remove_devices(&pdev->dev);
+	flexcard_remove_irq(pdev);
 	ida_simple_remove(&flexcard_ida, priv->cardnr);
 	iounmap(priv->bar0);
 	pci_release_regions(pdev);
diff --git a/drivers/mfd/flexcard_irq.c b/drivers/mfd/flexcard_irq.c
new file mode 100644
index 0000000..fa2063f
--- /dev/null
+++ b/drivers/mfd/flexcard_irq.c
@@ -0,0 +1,238 @@
+/*
+ * Eberspächer Flexcard PMC II Carrier Board PCI Driver - Interrupt controller
+ *
+ * Copyright (c) 2014 - 2016, Linutronix GmbH
+ * Author: Benedikt Spranger <b.spranger@...utronix.de>
+ *         Holger Dengler <dengler@...utronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#include <linux/export.h>
+#include <linux/flexcard.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irqdomain.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/pci.h>
+
+#include <linux/mfd/core.h>
+#include <linux/mfd/flexcard.h>
+
+struct fc_irq_tab {
+	u32 mskcache;
+	u32 mskoffs;
+	u32 msk;
+	u32 ackoffs;
+	u32 ack;
+};
+
+#define to_irq_tab_ack(statbit, cofs, mskofs, mskbit, ackofs, ackbit)	\
+	[statbit] = {							\
+			.mskcache	= cofs,				\
+			.mskoffs	= mskofs,			\
+			.msk		= (1U << mskbit),		\
+			.ackoffs	= ackofs,			\
+			.ack		= (1U << ackbit) }
+
+#define to_irq_tab(statbit, cofs, mskofs, mskbit)			\
+	[statbit] = {							\
+			.mskcache	= cofs,				\
+			.mskoffs	= mskofs,			\
+			.msk		= (1U << mskbit) }
+
+#define DEVMSK_OFFS	offsetof(struct fc_bar0, conf.irc)
+#define DEVACK_OFFS	offsetof(struct fc_bar0, conf.irs)
+#define DEVMSK_CACHE	offsetof(struct flexcard_device, dev_irqmsk)
+
+#define dev_to_irq_tab_ack(s, m, a)				\
+		to_irq_tab_ack(s, DEVMSK_CACHE, DEVMSK_OFFS, m,	\
+			       DEVACK_OFFS, a)
+
+#define dev_to_irq_tab(s, m)				\
+		to_irq_tab(s, DEVMSK_CACHE, DEVMSK_OFFS, m)
+
+static const struct fc_irq_tab flexcard_irq_tab[] = {
+	/* Device Interrupts */
+	dev_to_irq_tab_ack(28, 28, 0),	/* TIMER  */
+	dev_to_irq_tab_ack(29, 29, 1),	/* CC1CYS */
+	dev_to_irq_tab_ack(21, 30, 10),	/* CC2CYS */
+	dev_to_irq_tab_ack(30, 18, 2),	/* CC3CYS */
+	dev_to_irq_tab_ack(25, 19, 6),	/* CC4CYS */
+	dev_to_irq_tab_ack(26, 26, 4),	/* WAKE1A */
+	dev_to_irq_tab_ack(27, 27, 5),	/* WAKE1B */
+	dev_to_irq_tab_ack(23, 24, 8),	/* WAKE2A */
+	dev_to_irq_tab_ack(22, 25, 9),	/* WAKE2B */
+	dev_to_irq_tab_ack(19, 22, 12),	/* WAKE3A */
+	dev_to_irq_tab_ack(18, 23, 13),	/* WAKE3B */
+	dev_to_irq_tab_ack(17, 20, 14),	/* WAKE4A */
+	dev_to_irq_tab_ack(16, 21, 15),	/* WAKE4B */
+	dev_to_irq_tab(31, 15),		/* CC1T0  */
+	dev_to_irq_tab(3, 14),		/* CC2T0  */
+	dev_to_irq_tab(24, 16),		/* CC3T0  */
+	dev_to_irq_tab(20, 17),		/* CC4T0  */
+};
+
+#define NR_FLEXCARD_IRQ		ARRAY_SIZE(flexcard_irq_tab)
+
+#define VALID_DEVIRQ_MSK	((1U << 28) | \
+				 (1U << 29) | \
+				 (1U << 21) | \
+				 (1U << 30) | \
+				 (1U << 25) | \
+				 (1U << 26) | \
+				 (1U << 27) | \
+				 (1U << 23) | \
+				 (1U << 22) | \
+				 (1U << 19) | \
+				 (1U << 18) | \
+				 (1U << 17) | \
+				 (1U << 16) | \
+				 (1U << 31) | \
+				 (1U << 3)  | \
+				 (1U << 24) | \
+				 (1U << 20))
+
+static irqreturn_t flexcard_demux(int irq, void *data)
+{
+	struct flexcard_device *priv = data;
+	irqreturn_t ret = IRQ_NONE;
+	unsigned int slot, cur, stat;
+
+	stat = readl(&priv->bar0->conf.irs) & VALID_DEVIRQ_MSK;
+	while (stat) {
+		slot = __ffs(stat);
+		stat &= (1 << slot);
+		cur = irq_linear_revmap(priv->irq_domain, slot);
+		generic_handle_irq(cur);
+		ret = IRQ_HANDLED;
+	}
+	return ret;
+}
+
+static void flexcard_irq_ack(struct irq_data *d)
+{
+	struct flexcard_device *priv = irq_data_get_irq_chip_data(d);
+	const struct fc_irq_tab *tp = &flexcard_irq_tab[d->hwirq];
+	void __iomem *p = (void __iomem *)priv->bar0 + tp->ackoffs;
+
+	writel(tp->ack, p);
+}
+
+static void flexcard_irq_mask(struct irq_data *d)
+{
+	struct flexcard_device *priv = irq_data_get_irq_chip_data(d);
+	const struct fc_irq_tab *tp = &flexcard_irq_tab[d->hwirq];
+	void __iomem *p = (void __iomem *)priv->bar0 + tp->mskoffs;
+	u32 *msk = (void *)priv + tp->mskcache;
+
+	raw_spin_lock(&priv->irq_lock);
+	*msk &= ~tp->msk;
+	writel(*msk, p);
+	raw_spin_unlock(&priv->irq_lock);
+}
+
+static void flexcard_irq_unmask(struct irq_data *d)
+{
+	struct flexcard_device *priv = irq_data_get_irq_chip_data(d);
+	const struct fc_irq_tab *tp = &flexcard_irq_tab[d->hwirq];
+	void __iomem *p = (void __iomem *)priv->bar0 + tp->mskoffs;
+	u32 *msk = (void *)priv + tp->mskcache;
+
+	raw_spin_lock(&priv->irq_lock);
+	*msk |= tp->msk;
+	writel(*msk, p);
+	raw_spin_unlock(&priv->irq_lock);
+}
+
+static int flexcard_req_irq(struct pci_dev *pdev)
+{
+	struct flexcard_device *priv = pci_get_drvdata(pdev);
+	int ret;
+
+	ret = pci_enable_msi(pdev);
+	if (ret) {
+		dev_warn(&pdev->dev, "could not enable MSI\n");
+		/* shared PCI irq fallback */
+		return request_irq(pdev->irq, flexcard_demux,
+				   IRQF_NO_THREAD | IRQF_SHARED,
+				   "flexcard", priv);
+	}
+	dev_info(&pdev->dev, "MSI enabled\n");
+
+	ret = request_irq(pdev->irq, flexcard_demux, IRQF_NO_THREAD,
+			  "flexcard", priv);
+	if (ret)
+		pci_disable_msi(pdev);
+
+	return ret;
+}
+
+static struct irq_chip flexcard_irq_chip = {
+	.name		= "flexcard_irq",
+	.irq_ack	= flexcard_irq_ack,
+	.irq_mask	= flexcard_irq_mask,
+	.irq_unmask	= flexcard_irq_unmask,
+};
+
+static int flexcard_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_irq_chip,
+				      handle_level_irq, "flexcard");
+	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_irq_domain_ops = {
+	.map = flexcard_irq_domain_map,
+};
+
+int flexcard_setup_irq(struct pci_dev *pdev)
+{
+	struct flexcard_device *priv = pci_get_drvdata(pdev);
+	struct irq_domain *domain;
+	int ret;
+
+	/* Make sure none of the subirqs is enabled */
+	writel(0, &priv->bar0->conf.irc);
+	writel(0, &priv->bar0->dma.dma_irer);
+
+	raw_spin_lock_init(&priv->irq_lock);
+
+	domain = irq_domain_add_linear(NULL, NR_FLEXCARD_IRQ,
+				       &flexcard_irq_domain_ops, priv);
+	if (!domain) {
+		dev_err(&pdev->dev, "could not request irq domain\n");
+		return -ENODEV;
+	}
+
+	priv->irq_domain = domain;
+
+	ret = flexcard_req_irq(pdev);
+	if (ret)
+		irq_domain_remove(priv->irq_domain);
+
+	return ret;
+}
+
+void flexcard_remove_irq(struct pci_dev *pdev)
+{
+	struct flexcard_device *priv = pci_get_drvdata(pdev);
+
+	/* Disable all subirqs */
+	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->irq_domain);
+}
diff --git a/include/linux/mfd/flexcard.h b/include/linux/mfd/flexcard.h
index 4116305..6cb8ad0 100644
--- a/include/linux/mfd/flexcard.h
+++ b/include/linux/mfd/flexcard.h
@@ -96,9 +96,15 @@ struct fc_bar0 {
 struct flexcard_device {
 	unsigned int cardnr;
 	struct pci_dev *pdev;
+	raw_spinlock_t irq_lock;
+	struct irq_domain *irq_domain;
 	struct fc_bar0 __iomem *bar0;
 	struct mfd_cell *cells;
 	struct resource *res;
+	u32 dev_irqmsk;
 };
 
+int flexcard_setup_irq(struct pci_dev *pdev);
+void flexcard_remove_irq(struct pci_dev *pdev);
+
 #endif /* _LINUX_FLEXCARD_H */
-- 
2.1.4

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ