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]
Date:	Wed, 25 Mar 2015 10:51:54 +0100
From:	Holger Dengler <dengler@...utronix.de>
To:	linux-kernel@...r.kernel.org
Cc:	Peter Mahler <mahler@...ug.com>, Juergen Bubeck <bubeck@...ug.com>,
	Benedikt Spranger <b.spranger@...utronix.de>,
	Holger Dengler <dengler@...utronix.de>,
	Samuel Ortiz <sameo@...ux.intel.com>,
	Lee Jones <lee.jones@...aro.org>
Subject: [PATCH 05/11] mfd: flexcard: add interrupt support

From: Benedikt Spranger <b.spranger@...utronix.de>

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: Holger Dengler <dengler@...utronix.de>
Signed-off-by: Benedikt Spranger <b.spranger@...utronix.de>
cc: Samuel Ortiz <sameo@...ux.intel.com>
cc: Lee Jones <lee.jones@...aro.org>
---
 drivers/mfd/Kconfig             |   1 +
 drivers/mfd/flexcard/Makefile   |   2 +-
 drivers/mfd/flexcard/core.c     |  14 +++-
 drivers/mfd/flexcard/flexcard.h |   2 +
 drivers/mfd/flexcard/irq.c      | 167 ++++++++++++++++++++++++++++++++++++++++
 drivers/mfd/flexcard/irq.h      |  50 ++++++++++++
 include/linux/mfd/flexcard.h    |   2 +
 7 files changed, 235 insertions(+), 3 deletions(-)
 create mode 100644 drivers/mfd/flexcard/irq.c
 create mode 100644 drivers/mfd/flexcard/irq.h

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 3765707..f624b7f 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -220,6 +220,7 @@ config MFD_DLN2
 config MFD_FLEXCARD
 	tristate "Support for 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/flexcard/Makefile b/drivers/mfd/flexcard/Makefile
index 101000f..4f72c9c 100644
--- a/drivers/mfd/flexcard/Makefile
+++ b/drivers/mfd/flexcard/Makefile
@@ -1,2 +1,2 @@
 obj-$(CONFIG_MFD_FLEXCARD)	+= flexcard.o
-flexcard-objs			:= core.o attr.o
+flexcard-objs			:= core.o attr.o irq.o
diff --git a/drivers/mfd/flexcard/core.c b/drivers/mfd/flexcard/core.c
index c73ae5c..a6f92b4 100644
--- a/drivers/mfd/flexcard/core.c
+++ b/drivers/mfd/flexcard/core.c
@@ -131,7 +131,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_clk_setup(struct flexcard_device *priv)
@@ -265,10 +266,16 @@ static int flexcard_probe(struct pci_dev *pdev,
 		goto out_release;
 	}
 
+	ret = flexcard_setup_irq(pdev);
+	if (ret) {
+		dev_err(&pdev->dev, "unable to setup irq controller: %d", ret);
+		goto out_unmap;
+	}
+
 	ret = flexcard_tiny_probe(priv);
 	if (ret) {
 		dev_err(&pdev->dev, "unable to probe tinys: %d", ret);
-		goto out_unmap;
+		goto out_remove_irq;
 	}
 
 	ret = flexcard_clk_setup(priv);
@@ -300,6 +307,8 @@ out_deregister:
 	misc_deregister(&priv->dev);
 out_remove:
 	mfd_remove_devices(&pdev->dev);
+out_remove_irq:
+	flexcard_remove_irq(pdev);
 out_unmap:
 	iounmap(priv->conf);
 out_release:
@@ -321,6 +330,7 @@ static void flexcard_remove(struct pci_dev *pdev)
 	flexcard_misc_del_attrs(priv->dev.this_device);
 	misc_deregister(&priv->dev);
 	mfd_remove_devices(&pdev->dev);
+	flexcard_remove_irq(pdev);
 	iounmap(priv->conf);
 	pci_release_regions(pdev);
 	pci_disable_device(pdev);
diff --git a/drivers/mfd/flexcard/flexcard.h b/drivers/mfd/flexcard/flexcard.h
index 1fe451e..038c138 100644
--- a/drivers/mfd/flexcard/flexcard.h
+++ b/drivers/mfd/flexcard/flexcard.h
@@ -3,5 +3,7 @@
 
 int flexcard_misc_add_attrs(struct device *dev);
 void flexcard_misc_del_attrs(struct device *dev);
+int flexcard_setup_irq(struct pci_dev *pdev);
+void flexcard_remove_irq(struct pci_dev *pdev);
 
 #endif /* __FLEXCARD_H */
diff --git a/drivers/mfd/flexcard/irq.c b/drivers/mfd/flexcard/irq.c
new file mode 100644
index 0000000..fefcb24
--- /dev/null
+++ b/drivers/mfd/flexcard/irq.c
@@ -0,0 +1,167 @@
+/*
+ * Eberspaecher Flexcard PMC II Carrier Board PCI Driver - Interrupt controller
+ *
+ * Copyright (c) 2014,2015 Linutronix GmbH
+ * Author: Holger Dengler
+ *         Benedikt Spranger
+ *
+ * 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/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/export.h>
+#include <linux/flexcard.h>
+#include <linux/interrupt.h>
+#include <linux/irqdomain.h>
+#include <linux/miscdevice.h>
+#include <linux/pci.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/flexcard.h>
+
+#include "irq.h"
+
+static irqreturn_t flexcard_demux(int irq, void *data)
+{
+	struct flexcard_device *priv = data;
+	u32 stat;
+	int i, cur;
+
+	stat = readl(&priv->conf->irs);
+	if (!stat)
+		return IRQ_NONE;
+
+	for (i = 0; i < NR_FLEXCARD_IRQ; i++) {
+		if (stat & flexcard_irq_tab[i].status) {
+			cur = irq_find_mapping(priv->irq_domain, i);
+			if (cur)
+				generic_handle_irq(cur);
+		}
+	}
+	return IRQ_HANDLED;
+}
+
+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 void flexcard_irq_ack(struct irq_data *d)
+{
+	struct flexcard_device *priv = irq_data_get_irq_chip_data(d);
+
+	if (flexcard_irq_tab[d->hwirq].reset)
+		writel(flexcard_irq_tab[d->hwirq].reset, &priv->conf->irs);
+}
+
+static void flexcard_irq_mask(struct irq_data *d)
+{
+	struct flexcard_device *priv = irq_data_get_irq_chip_data(d);
+	unsigned long flags;
+	u32 irc;
+
+	raw_spin_lock_irqsave(&priv->irq_lock, flags);
+	irc = readl(&priv->conf->irc);
+	irc &= ~flexcard_irq_tab[d->hwirq].enable;
+	writel(irc, &priv->conf->irc);
+	raw_spin_unlock_irqrestore(&priv->irq_lock, flags);
+}
+
+static void flexcard_irq_unmask(struct irq_data *d)
+{
+	struct flexcard_device *priv = irq_data_get_irq_chip_data(d);
+	unsigned long flags;
+	u32 irc;
+
+	raw_spin_lock_irqsave(&priv->irq_lock, flags);
+	irc = readl(&priv->conf->irc);
+	irc |= flexcard_irq_tab[d->hwirq].enable;
+	writel(irc, &priv->conf->irc);
+	raw_spin_unlock_irqrestore(&priv->irq_lock, flags);
+}
+
+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 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->conf->irc);
+	writel(0, &priv->conf->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->dma_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->conf->irc);
+	writel(0, &priv->conf->dma_irer);
+
+	free_irq(pdev->irq, priv);
+	pci_disable_msi(pdev);
+	irq_domain_remove(priv->irq_domain);
+}
diff --git a/drivers/mfd/flexcard/irq.h b/drivers/mfd/flexcard/irq.h
new file mode 100644
index 0000000..b18fb5b
--- /dev/null
+++ b/drivers/mfd/flexcard/irq.h
@@ -0,0 +1,50 @@
+#ifndef FLEXCARD_IRQ_H
+#define FLEXCARD_IRQ_H
+
+struct flexcard_irq_tab {
+	u32 enable;
+	u32 reset;
+	u32 status;
+};
+
+#define to_irq_tab(e, s) {			\
+	.enable = (1 << e),			\
+	.status = (1 << s),			\
+}
+
+#define to_irq_tab_ack(e, r, s) {		\
+	.enable = (1 << e),			\
+	.reset  = (1 << r),			\
+	.status = (1 << s),			\
+}
+
+/*
+ * Interrupt Controller Register S-Box
+ * unlike other irq controllers the FlexCard bits for enable, reset and status
+ * looks more like a cryptographic S-box. Make a const table to have a more
+ * easier access to this bits in the irqchip callback functions.
+ * The table contains the registers for PMC2-cards.
+ */
+static const struct flexcard_irq_tab flexcard_irq_tab[] = {
+	to_irq_tab_ack(28, 0, 28),	/* TIMER  */
+	to_irq_tab_ack(29, 1, 29),	/* CC1CYS */
+	to_irq_tab_ack(30, 10, 21),	/* CC2CYS */
+	to_irq_tab_ack(18, 2, 30),	/* CC3CYS */
+	to_irq_tab_ack(19, 6, 25),	/* CC4CYS */
+	to_irq_tab_ack(26, 4, 26),	/* WAKE1A */
+	to_irq_tab_ack(27, 5, 27),	/* WAKE1B */
+	to_irq_tab_ack(24, 8, 23),	/* WAKE2A */
+	to_irq_tab_ack(25, 9, 22),	/* WAKE2B */
+	to_irq_tab_ack(22, 12, 19),	/* WAKE3A */
+	to_irq_tab_ack(23, 13, 18),	/* WAKE3B */
+	to_irq_tab_ack(20, 14, 17),	/* WAKE4A */
+	to_irq_tab_ack(21, 15, 16),	/* WAKE4B */
+	to_irq_tab(15, 31),		/* CC1T0  */
+	to_irq_tab(14, 3),		/* CC2T0  */
+	to_irq_tab(16, 24),		/* CC3T0  */
+	to_irq_tab(17, 20),		/* CC4T0  */
+};
+
+#define NR_FLEXCARD_IRQ		ARRAY_SIZE(flexcard_irq_tab)
+
+#endif /* FLEXCARD_IRQ_H */
diff --git a/include/linux/mfd/flexcard.h b/include/linux/mfd/flexcard.h
index 84e155c..f5b789f 100644
--- a/include/linux/mfd/flexcard.h
+++ b/include/linux/mfd/flexcard.h
@@ -22,12 +22,14 @@
 #define FLEXCARD_MAX_NAME	16
 
 struct flexcard_device {
+	raw_spinlock_t irq_lock;
 	struct pci_dev *pdev;
 	struct fc_conf_bar __iomem *conf;
 	struct mfd_cell *cells;
 	struct resource *res;
 	struct miscdevice dev;
 	struct kref ref;
+	struct irq_domain *irq_domain;
 	int cardnr;
 	char name[FLEXCARD_MAX_NAME];
 };
-- 
2.1.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ