public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH] irqchip: Renesas IRQC driver
@ 2013-02-27  8:15 Magnus Damm
  2013-02-27  8:40 ` Simon Horman
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: Magnus Damm @ 2013-02-27  8:15 UTC (permalink / raw)
  To: linux-kernel; +Cc: linux-sh, benh, grant.likely, horms, Magnus Damm, tglx

From: Magnus Damm <damm@opensource.se>

This patch adds a driver for external IRQ pins connected
to the IRQC hardware block on recent SoCs from Renesas.

The IRQC hardware block is used together with more
recent ARM based SoCs using the GIC. As usual the GIC
requires external IRQ trigger setup somewhere else
which in this particular case happens to be IRQC.

This driver implements the glue code needed to configure
IRQ trigger and also handle mask/unmask and demux of
external IRQ pins hooked up from the IRQC to the GIC.

Tested on r8a73a4 but is designed to work with a wide
range of SoCs. The driver requires one GIC SPI per
external IRQ pin to operate.  Each driver instance
will handle up to 32 external IRQ pins.

The SoCs using this driver are currently mainly used
together with regular platform devices so this driver
allows configuration via platform data to support things
like static interrupt base address. DT support will
be added incrementally in the not so distant future.

Signed-off-by: Magnus Damm <damm@opensource.se>
---

 drivers/irqchip/Kconfig                        |    4 
 drivers/irqchip/Makefile                       |    1 
 drivers/irqchip/irq-renesas-irqc.c             |  298 ++++++++++++++++++++++++
 include/linux/platform_data/irq-renesas-irqc.h |   27 ++
 4 files changed, 330 insertions(+)

--- 0002/drivers/irqchip/Kconfig
+++ work/drivers/irqchip/Kconfig	2013-02-27 16:00:08.000000000 +0900
@@ -29,6 +29,10 @@ config RENESAS_INTC_IRQPIN
 	bool
 	select IRQ_DOMAIN
 
+config RENESAS_IRQC
+	bool
+	select IRQ_DOMAIN
+
 config VERSATILE_FPGA_IRQ
 	bool
 	select IRQ_DOMAIN
--- 0002/drivers/irqchip/Makefile
+++ work/drivers/irqchip/Makefile	2013-02-27 16:00:08.000000000 +0900
@@ -6,4 +6,5 @@ obj-$(CONFIG_ARCH_SPEAR3XX)		+= spear-sh
 obj-$(CONFIG_ARM_GIC)			+= irq-gic.o
 obj-$(CONFIG_ARM_VIC)			+= irq-vic.o
 obj-$(CONFIG_RENESAS_INTC_IRQPIN)	+= irq-renesas-intc-irqpin.o
+obj-$(CONFIG_RENESAS_IRQC)		+= irq-renesas-irqc.o
 obj-$(CONFIG_VERSATILE_FPGA_IRQ)	+= irq-versatile-fpga.o
--- /dev/null
+++ work/drivers/irqchip/irq-renesas-irqc.c	2013-02-27 17:04:02.000000000 +0900
@@ -0,0 +1,298 @@
+/*
+ * Renesas IRQC Driver
+ *
+ *  Copyright (C) 2013 Magnus Damm
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/platform_data/irq-renesas-irqc.h>
+
+#define IRQC_IRQ_MAX 32 /* maximum 32 interrupts per driver instance */
+
+#define IRQC_REQ_STS 0x00
+#define IRQC_EN_STS 0x04
+#define IRQC_EN_SET 0x08
+#define IRQC_INT_CPU_BASE(n) (0x000 + ((n) * 0x10))
+#define DETECT_STATUS 0x100
+#define IRQC_CONFIG(n) (0x180 + ((n) * 0x04))
+
+struct irqc_irq {
+	int hw_irq;
+	int requested_irq;
+	int domain_irq;
+	struct irqc_priv *p;
+};
+
+struct irqc_priv {
+	void __iomem *iomem;
+	void __iomem *cpu_int_base;
+	struct irqc_irq irq[IRQC_IRQ_MAX];
+	struct renesas_irqc_config config;
+	unsigned int number_of_irqs;
+	struct platform_device *pdev;
+	struct irq_chip irq_chip;
+	struct irq_domain *irq_domain;
+};
+
+static void irqc_dbg(struct irqc_irq *i, char *str)
+{
+	dev_dbg(&i->p->pdev->dev, "%s (%d:%d:%d)\n",
+		str, i->requested_irq, i->hw_irq, i->domain_irq);
+}
+
+static void irqc_irq_enable(struct irq_data *d)
+{
+	struct irqc_priv *p = irq_data_get_irq_chip_data(d);
+	int hw_irq = irqd_to_hwirq(d);
+
+	irqc_dbg(&p->irq[hw_irq], "enable");
+	iowrite32(BIT(hw_irq), p->cpu_int_base + IRQC_EN_SET);
+}
+
+static void irqc_irq_disable(struct irq_data *d)
+{
+	struct irqc_priv *p = irq_data_get_irq_chip_data(d);
+	int hw_irq = irqd_to_hwirq(d);
+
+	irqc_dbg(&p->irq[hw_irq], "disable");
+	iowrite32(BIT(hw_irq), p->cpu_int_base + IRQC_EN_STS);
+}
+
+#define INTC_IRQ_SENSE_VALID 0x10
+#define INTC_IRQ_SENSE(x) (x + INTC_IRQ_SENSE_VALID)
+
+static unsigned char irqc_sense[IRQ_TYPE_SENSE_MASK + 1] = {
+	[IRQ_TYPE_LEVEL_LOW] = INTC_IRQ_SENSE(0x01),
+	[IRQ_TYPE_LEVEL_HIGH] = INTC_IRQ_SENSE(0x02),
+	[IRQ_TYPE_EDGE_FALLING] = INTC_IRQ_SENSE(0x04), /* Synchronous */
+	[IRQ_TYPE_EDGE_RISING] = INTC_IRQ_SENSE(0x08), /* Synchronous */
+	[IRQ_TYPE_EDGE_BOTH] = INTC_IRQ_SENSE(0x0c),  /* Synchronous */
+};
+
+static int irqc_irq_set_type(struct irq_data *d, unsigned int type)
+{
+	struct irqc_priv *p = irq_data_get_irq_chip_data(d);
+	int hw_irq = irqd_to_hwirq(d);
+	unsigned char value = irqc_sense[type & IRQ_TYPE_SENSE_MASK];
+	unsigned long tmp;
+
+	irqc_dbg(&p->irq[hw_irq], "sense");
+
+	if (!(value & INTC_IRQ_SENSE_VALID))
+		return -EINVAL;
+
+	tmp = ioread32(p->iomem + IRQC_CONFIG(hw_irq));
+	tmp &= ~0x3f;
+	tmp |= value ^ INTC_IRQ_SENSE_VALID;
+	iowrite32(tmp, p->iomem + IRQC_CONFIG(hw_irq));
+	return 0;
+}
+
+static irqreturn_t irqc_irq_handler(int irq, void *dev_id)
+{
+	struct irqc_irq *i = dev_id;
+	struct irqc_priv *p = i->p;
+	unsigned long bit = BIT(i->hw_irq);
+
+	irqc_dbg(i, "demux1");
+
+	if (ioread32(p->iomem + DETECT_STATUS) & bit) {
+		iowrite32(bit, p->iomem + DETECT_STATUS);
+		irqc_dbg(i, "demux2");
+		generic_handle_irq(i->domain_irq);
+		return IRQ_HANDLED;
+	}
+	return IRQ_NONE;
+}
+
+static int irqc_irq_domain_map(struct irq_domain *h, unsigned int virq,
+			       irq_hw_number_t hw)
+{
+	struct irqc_priv *p = h->host_data;
+
+	p->irq[hw].domain_irq = virq;
+	p->irq[hw].hw_irq = hw;
+
+	irqc_dbg(&p->irq[hw], "map");
+	irq_set_chip_data(virq, h->host_data);
+	irq_set_chip_and_handler(virq, &p->irq_chip, handle_level_irq);
+	set_irq_flags(virq, IRQF_VALID); /* kill me now */
+	return 0;
+}
+
+static struct irq_domain_ops irqc_irq_domain_ops = {
+	.map	= irqc_irq_domain_map,
+};
+
+static int irqc_probe(struct platform_device *pdev)
+{
+	struct renesas_irqc_config *pdata = pdev->dev.platform_data;
+	struct irqc_priv *p;
+	struct resource *io;
+	struct resource *irq;
+	struct irq_chip *irq_chip;
+	const char *name = dev_name(&pdev->dev);
+	int ret;
+	int k;
+
+	p = kzalloc(sizeof(*p), GFP_KERNEL);
+	if (!p) {
+		dev_err(&pdev->dev, "failed to allocate driver data\n");
+		ret = -ENOMEM;
+		goto err0;
+	}
+
+	/* deal with driver instance configuration */
+	if (pdata)
+		memcpy(&p->config, pdata, sizeof(*pdata));
+
+	p->pdev = pdev;
+	platform_set_drvdata(pdev, p);
+
+	/* get hold of manadatory IOMEM */
+	io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!io) {
+		dev_err(&pdev->dev, "not enough IOMEM resources\n");
+		ret = -EINVAL;
+		goto err1;
+	}
+
+	/* allow any number of IRQs between 1 and IRQC_IRQ_MAX */
+	for (k = 0; k < IRQC_IRQ_MAX; k++) {
+		irq = platform_get_resource(pdev, IORESOURCE_IRQ, k);
+		if (!irq)
+			break;
+
+		p->irq[k].p = p;
+		p->irq[k].requested_irq = irq->start;
+	}
+
+	p->number_of_irqs = k;
+	if (p->number_of_irqs < 1) {
+		dev_err(&pdev->dev, "not enough IRQ resources\n");
+		ret = -EINVAL;
+		goto err1;
+	}
+
+	/* ioremap IOMEM and setup read/write callbacks */
+	p->iomem = ioremap_nocache(io->start, resource_size(io));
+	if (!p->iomem) {
+		dev_err(&pdev->dev, "failed to remap IOMEM\n");
+		ret = -ENXIO;
+		goto err2;
+	}
+
+	p->cpu_int_base = p->iomem + IRQC_INT_CPU_BASE(0); /* SYS-SPI */
+
+	irq_chip = &p->irq_chip;
+	irq_chip->name = name;
+	irq_chip->irq_mask = irqc_irq_disable;
+	irq_chip->irq_unmask = irqc_irq_enable;
+	irq_chip->irq_enable = irqc_irq_enable;
+	irq_chip->irq_disable = irqc_irq_disable;
+	irq_chip->irq_set_type = irqc_irq_set_type;
+	irq_chip->flags	= IRQCHIP_SKIP_SET_WAKE;
+
+	p->irq_domain = irq_domain_add_simple(pdev->dev.of_node,
+					      p->number_of_irqs,
+					      p->config.irq_base,
+					      &irqc_irq_domain_ops, p);
+	if (!p->irq_domain) {
+		ret = -ENXIO;
+		dev_err(&pdev->dev, "cannot initialize irq domain\n");
+		goto err2;
+	}
+
+	/* request interrupts one by one */
+	for (k = 0; k < p->number_of_irqs; k++) {
+		if (request_irq(p->irq[k].requested_irq, irqc_irq_handler,
+				0, name, &p->irq[k])) {
+			dev_err(&pdev->dev, "failed to request IRQ\n");
+			ret = -ENOENT;
+			goto err3;
+		}
+	}
+
+	dev_info(&pdev->dev, "driving %d irqs\n", p->number_of_irqs);
+
+	/* warn in case of mismatch if irq base is specified */
+	if (p->config.irq_base) {
+		if (p->config.irq_base != p->irq[0].domain_irq)
+			dev_warn(&pdev->dev, "irq base mismatch (%d/%d)\n",
+				 p->config.irq_base, p->irq[0].domain_irq);
+	}
+
+	return 0;
+err3:
+	for (; k >= 0; k--)
+		free_irq(p->irq[k - 1].requested_irq, &p->irq[k - 1]);
+
+	irq_domain_remove(p->irq_domain);
+err2:
+	iounmap(p->iomem);
+err1:
+	kfree(p);
+err0:
+	return ret;
+}
+
+static int irqc_remove(struct platform_device *pdev)
+{
+	struct irqc_priv *p = platform_get_drvdata(pdev);
+	int k;
+
+	for (k = 0; k < p->number_of_irqs; k++)
+		free_irq(p->irq[k].requested_irq, &p->irq[k]);
+
+	irq_domain_remove(p->irq_domain);
+	iounmap(p->iomem);
+	kfree(p);
+	return 0;
+}
+
+static struct platform_driver irqc_device_driver = {
+	.probe		= irqc_probe,
+	.remove		= irqc_remove,
+	.driver		= {
+		.name	= "renesas_irqc",
+	}
+};
+
+static int __init irqc_init(void)
+{
+	return platform_driver_register(&irqc_device_driver);
+}
+postcore_initcall(irqc_init);
+
+static void __exit irqc_exit(void)
+{
+	platform_driver_unregister(&irqc_device_driver);
+}
+module_exit(irqc_exit);
+
+MODULE_AUTHOR("Magnus Damm");
+MODULE_DESCRIPTION("Renesas IRQC Driver");
+MODULE_LICENSE("GPL v2");
--- /dev/null
+++ work/include/linux/platform_data/irq-renesas-irqc.h	2013-02-27 16:12:09.000000000 +0900
@@ -0,0 +1,27 @@
+/*
+ * Renesas IRQC Driver
+ *
+ *  Copyright (C) 2013 Magnus Damm
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef __IRQ_RENESAS_IRQC_H__
+#define __IRQ_RENESAS_IRQC_H__
+
+struct renesas_irqc_config {
+	unsigned int irq_base;
+};
+
+#endif /* __IRQ_RENESAS_IRQC_H__ */

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH] irqchip: Renesas IRQC driver
  2013-02-27  8:15 [PATCH] irqchip: Renesas IRQC driver Magnus Damm
@ 2013-02-27  8:40 ` Simon Horman
  2013-03-01  9:41 ` Simon Horman
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 5+ messages in thread
From: Simon Horman @ 2013-02-27  8:40 UTC (permalink / raw)
  To: Magnus Damm; +Cc: linux-kernel, linux-sh, benh, grant.likely, tglx

On Wed, Feb 27, 2013 at 05:15:01PM +0900, Magnus Damm wrote:
> From: Magnus Damm <damm@opensource.se>
> 
> This patch adds a driver for external IRQ pins connected
> to the IRQC hardware block on recent SoCs from Renesas.
> 
> The IRQC hardware block is used together with more
> recent ARM based SoCs using the GIC. As usual the GIC
> requires external IRQ trigger setup somewhere else
> which in this particular case happens to be IRQC.
> 
> This driver implements the glue code needed to configure
> IRQ trigger and also handle mask/unmask and demux of
> external IRQ pins hooked up from the IRQC to the GIC.
> 
> Tested on r8a73a4 but is designed to work with a wide
> range of SoCs. The driver requires one GIC SPI per
> external IRQ pin to operate.  Each driver instance
> will handle up to 32 external IRQ pins.

s/will/may/

> The SoCs using this driver are currently mainly used
> together with regular platform devices so this driver
> allows configuration via platform data to support things
> like static interrupt base address. DT support will
> be added incrementally in the not so distant future.
> 
> Signed-off-by: Magnus Damm <damm@opensource.se>
> ---
> 
>  drivers/irqchip/Kconfig                        |    4 
>  drivers/irqchip/Makefile                       |    1 
>  drivers/irqchip/irq-renesas-irqc.c             |  298 ++++++++++++++++++++++++
>  include/linux/platform_data/irq-renesas-irqc.h |   27 ++
>  4 files changed, 330 insertions(+)
> 
> --- 0002/drivers/irqchip/Kconfig
> +++ work/drivers/irqchip/Kconfig	2013-02-27 16:00:08.000000000 +0900
> @@ -29,6 +29,10 @@ config RENESAS_INTC_IRQPIN
>  	bool
>  	select IRQ_DOMAIN
>  
> +config RENESAS_IRQC
> +	bool
> +	select IRQ_DOMAIN
> +
>  config VERSATILE_FPGA_IRQ
>  	bool
>  	select IRQ_DOMAIN
> --- 0002/drivers/irqchip/Makefile
> +++ work/drivers/irqchip/Makefile	2013-02-27 16:00:08.000000000 +0900
> @@ -6,4 +6,5 @@ obj-$(CONFIG_ARCH_SPEAR3XX)		+= spear-sh
>  obj-$(CONFIG_ARM_GIC)			+= irq-gic.o
>  obj-$(CONFIG_ARM_VIC)			+= irq-vic.o
>  obj-$(CONFIG_RENESAS_INTC_IRQPIN)	+= irq-renesas-intc-irqpin.o
> +obj-$(CONFIG_RENESAS_IRQC)		+= irq-renesas-irqc.o
>  obj-$(CONFIG_VERSATILE_FPGA_IRQ)	+= irq-versatile-fpga.o
> --- /dev/null
> +++ work/drivers/irqchip/irq-renesas-irqc.c	2013-02-27 17:04:02.000000000 +0900
> @@ -0,0 +1,298 @@
> +/*
> + * Renesas IRQC Driver
> + *
> + *  Copyright (C) 2013 Magnus Damm
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
> + */
> +
> +#include <linux/init.h>
> +#include <linux/platform_device.h>
> +#include <linux/spinlock.h>
> +#include <linux/interrupt.h>
> +#include <linux/ioport.h>
> +#include <linux/io.h>
> +#include <linux/irq.h>
> +#include <linux/irqdomain.h>
> +#include <linux/err.h>
> +#include <linux/slab.h>
> +#include <linux/module.h>
> +#include <linux/platform_data/irq-renesas-irqc.h>
> +
> +#define IRQC_IRQ_MAX 32 /* maximum 32 interrupts per driver instance */
> +
> +#define IRQC_REQ_STS 0x00
> +#define IRQC_EN_STS 0x04
> +#define IRQC_EN_SET 0x08
> +#define IRQC_INT_CPU_BASE(n) (0x000 + ((n) * 0x10))
> +#define DETECT_STATUS 0x100
> +#define IRQC_CONFIG(n) (0x180 + ((n) * 0x04))
> +
> +struct irqc_irq {
> +	int hw_irq;
> +	int requested_irq;
> +	int domain_irq;
> +	struct irqc_priv *p;
> +};
> +
> +struct irqc_priv {
> +	void __iomem *iomem;
> +	void __iomem *cpu_int_base;
> +	struct irqc_irq irq[IRQC_IRQ_MAX];
> +	struct renesas_irqc_config config;
> +	unsigned int number_of_irqs;
> +	struct platform_device *pdev;
> +	struct irq_chip irq_chip;
> +	struct irq_domain *irq_domain;
> +};
> +
> +static void irqc_dbg(struct irqc_irq *i, char *str)
> +{
> +	dev_dbg(&i->p->pdev->dev, "%s (%d:%d:%d)\n",
> +		str, i->requested_irq, i->hw_irq, i->domain_irq);
> +}
> +
> +static void irqc_irq_enable(struct irq_data *d)
> +{
> +	struct irqc_priv *p = irq_data_get_irq_chip_data(d);
> +	int hw_irq = irqd_to_hwirq(d);
> +
> +	irqc_dbg(&p->irq[hw_irq], "enable");
> +	iowrite32(BIT(hw_irq), p->cpu_int_base + IRQC_EN_SET);
> +}
> +
> +static void irqc_irq_disable(struct irq_data *d)
> +{
> +	struct irqc_priv *p = irq_data_get_irq_chip_data(d);
> +	int hw_irq = irqd_to_hwirq(d);
> +
> +	irqc_dbg(&p->irq[hw_irq], "disable");
> +	iowrite32(BIT(hw_irq), p->cpu_int_base + IRQC_EN_STS);
> +}
> +
> +#define INTC_IRQ_SENSE_VALID 0x10
> +#define INTC_IRQ_SENSE(x) (x + INTC_IRQ_SENSE_VALID)
> +
> +static unsigned char irqc_sense[IRQ_TYPE_SENSE_MASK + 1] = {
> +	[IRQ_TYPE_LEVEL_LOW] = INTC_IRQ_SENSE(0x01),
> +	[IRQ_TYPE_LEVEL_HIGH] = INTC_IRQ_SENSE(0x02),
> +	[IRQ_TYPE_EDGE_FALLING] = INTC_IRQ_SENSE(0x04), /* Synchronous */
> +	[IRQ_TYPE_EDGE_RISING] = INTC_IRQ_SENSE(0x08), /* Synchronous */
> +	[IRQ_TYPE_EDGE_BOTH] = INTC_IRQ_SENSE(0x0c),  /* Synchronous */
> +};
> +
> +static int irqc_irq_set_type(struct irq_data *d, unsigned int type)
> +{
> +	struct irqc_priv *p = irq_data_get_irq_chip_data(d);
> +	int hw_irq = irqd_to_hwirq(d);
> +	unsigned char value = irqc_sense[type & IRQ_TYPE_SENSE_MASK];
> +	unsigned long tmp;
> +
> +	irqc_dbg(&p->irq[hw_irq], "sense");
> +
> +	if (!(value & INTC_IRQ_SENSE_VALID))
> +		return -EINVAL;
> +
> +	tmp = ioread32(p->iomem + IRQC_CONFIG(hw_irq));
> +	tmp &= ~0x3f;
> +	tmp |= value ^ INTC_IRQ_SENSE_VALID;
> +	iowrite32(tmp, p->iomem + IRQC_CONFIG(hw_irq));
> +	return 0;
> +}
> +
> +static irqreturn_t irqc_irq_handler(int irq, void *dev_id)
> +{
> +	struct irqc_irq *i = dev_id;
> +	struct irqc_priv *p = i->p;
> +	unsigned long bit = BIT(i->hw_irq);
> +
> +	irqc_dbg(i, "demux1");
> +
> +	if (ioread32(p->iomem + DETECT_STATUS) & bit) {
> +		iowrite32(bit, p->iomem + DETECT_STATUS);
> +		irqc_dbg(i, "demux2");
> +		generic_handle_irq(i->domain_irq);
> +		return IRQ_HANDLED;
> +	}
> +	return IRQ_NONE;
> +}
> +
> +static int irqc_irq_domain_map(struct irq_domain *h, unsigned int virq,
> +			       irq_hw_number_t hw)
> +{
> +	struct irqc_priv *p = h->host_data;
> +
> +	p->irq[hw].domain_irq = virq;
> +	p->irq[hw].hw_irq = hw;
> +
> +	irqc_dbg(&p->irq[hw], "map");
> +	irq_set_chip_data(virq, h->host_data);
> +	irq_set_chip_and_handler(virq, &p->irq_chip, handle_level_irq);
> +	set_irq_flags(virq, IRQF_VALID); /* kill me now */
> +	return 0;
> +}
> +
> +static struct irq_domain_ops irqc_irq_domain_ops = {
> +	.map	= irqc_irq_domain_map,
> +};
> +
> +static int irqc_probe(struct platform_device *pdev)
> +{
> +	struct renesas_irqc_config *pdata = pdev->dev.platform_data;
> +	struct irqc_priv *p;
> +	struct resource *io;
> +	struct resource *irq;
> +	struct irq_chip *irq_chip;
> +	const char *name = dev_name(&pdev->dev);
> +	int ret;
> +	int k;
> +
> +	p = kzalloc(sizeof(*p), GFP_KERNEL);
> +	if (!p) {
> +		dev_err(&pdev->dev, "failed to allocate driver data\n");
> +		ret = -ENOMEM;
> +		goto err0;
> +	}
> +
> +	/* deal with driver instance configuration */
> +	if (pdata)
> +		memcpy(&p->config, pdata, sizeof(*pdata));
> +
> +	p->pdev = pdev;
> +	platform_set_drvdata(pdev, p);
> +
> +	/* get hold of manadatory IOMEM */
> +	io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!io) {
> +		dev_err(&pdev->dev, "not enough IOMEM resources\n");
> +		ret = -EINVAL;
> +		goto err1;
> +	}
> +
> +	/* allow any number of IRQs between 1 and IRQC_IRQ_MAX */
> +	for (k = 0; k < IRQC_IRQ_MAX; k++) {
> +		irq = platform_get_resource(pdev, IORESOURCE_IRQ, k);
> +		if (!irq)
> +			break;
> +
> +		p->irq[k].p = p;
> +		p->irq[k].requested_irq = irq->start;
> +	}
> +
> +	p->number_of_irqs = k;
> +	if (p->number_of_irqs < 1) {
> +		dev_err(&pdev->dev, "not enough IRQ resources\n");
> +		ret = -EINVAL;
> +		goto err1;
> +	}
> +
> +	/* ioremap IOMEM and setup read/write callbacks */
> +	p->iomem = ioremap_nocache(io->start, resource_size(io));
> +	if (!p->iomem) {
> +		dev_err(&pdev->dev, "failed to remap IOMEM\n");
> +		ret = -ENXIO;
> +		goto err2;
> +	}
> +
> +	p->cpu_int_base = p->iomem + IRQC_INT_CPU_BASE(0); /* SYS-SPI */
> +
> +	irq_chip = &p->irq_chip;
> +	irq_chip->name = name;
> +	irq_chip->irq_mask = irqc_irq_disable;
> +	irq_chip->irq_unmask = irqc_irq_enable;
> +	irq_chip->irq_enable = irqc_irq_enable;
> +	irq_chip->irq_disable = irqc_irq_disable;
> +	irq_chip->irq_set_type = irqc_irq_set_type;
> +	irq_chip->flags	= IRQCHIP_SKIP_SET_WAKE;
> +
> +	p->irq_domain = irq_domain_add_simple(pdev->dev.of_node,
> +					      p->number_of_irqs,
> +					      p->config.irq_base,
> +					      &irqc_irq_domain_ops, p);
> +	if (!p->irq_domain) {
> +		ret = -ENXIO;
> +		dev_err(&pdev->dev, "cannot initialize irq domain\n");
> +		goto err2;
> +	}
> +
> +	/* request interrupts one by one */
> +	for (k = 0; k < p->number_of_irqs; k++) {
> +		if (request_irq(p->irq[k].requested_irq, irqc_irq_handler,
> +				0, name, &p->irq[k])) {
> +			dev_err(&pdev->dev, "failed to request IRQ\n");
> +			ret = -ENOENT;
> +			goto err3;
> +		}
> +	}
> +
> +	dev_info(&pdev->dev, "driving %d irqs\n", p->number_of_irqs);
> +
> +	/* warn in case of mismatch if irq base is specified */
> +	if (p->config.irq_base) {
> +		if (p->config.irq_base != p->irq[0].domain_irq)
> +			dev_warn(&pdev->dev, "irq base mismatch (%d/%d)\n",
> +				 p->config.irq_base, p->irq[0].domain_irq);
> +	}
> +
> +	return 0;
> +err3:
> +	for (; k >= 0; k--)
> +		free_irq(p->irq[k - 1].requested_irq, &p->irq[k - 1]);
> +
> +	irq_domain_remove(p->irq_domain);
> +err2:
> +	iounmap(p->iomem);
> +err1:
> +	kfree(p);
> +err0:
> +	return ret;
> +}
> +
> +static int irqc_remove(struct platform_device *pdev)
> +{
> +	struct irqc_priv *p = platform_get_drvdata(pdev);
> +	int k;
> +
> +	for (k = 0; k < p->number_of_irqs; k++)
> +		free_irq(p->irq[k].requested_irq, &p->irq[k]);
> +
> +	irq_domain_remove(p->irq_domain);
> +	iounmap(p->iomem);
> +	kfree(p);
> +	return 0;
> +}
> +
> +static struct platform_driver irqc_device_driver = {
> +	.probe		= irqc_probe,
> +	.remove		= irqc_remove,
> +	.driver		= {
> +		.name	= "renesas_irqc",
> +	}
> +};
> +
> +static int __init irqc_init(void)
> +{
> +	return platform_driver_register(&irqc_device_driver);
> +}
> +postcore_initcall(irqc_init);
> +
> +static void __exit irqc_exit(void)
> +{
> +	platform_driver_unregister(&irqc_device_driver);
> +}
> +module_exit(irqc_exit);
> +
> +MODULE_AUTHOR("Magnus Damm");
> +MODULE_DESCRIPTION("Renesas IRQC Driver");
> +MODULE_LICENSE("GPL v2");
> --- /dev/null
> +++ work/include/linux/platform_data/irq-renesas-irqc.h	2013-02-27 16:12:09.000000000 +0900
> @@ -0,0 +1,27 @@
> +/*
> + * Renesas IRQC Driver
> + *
> + *  Copyright (C) 2013 Magnus Damm
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
> + */
> +
> +#ifndef __IRQ_RENESAS_IRQC_H__
> +#define __IRQ_RENESAS_IRQC_H__
> +
> +struct renesas_irqc_config {
> +	unsigned int irq_base;
> +};
> +
> +#endif /* __IRQ_RENESAS_IRQC_H__ */
> 

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH] irqchip: Renesas IRQC driver
  2013-02-27  8:15 [PATCH] irqchip: Renesas IRQC driver Magnus Damm
  2013-02-27  8:40 ` Simon Horman
@ 2013-03-01  9:41 ` Simon Horman
  2013-03-01 15:02 ` Guennadi Liakhovetski
  2013-12-11 23:36 ` Sergei Shtylyov
  3 siblings, 0 replies; 5+ messages in thread
From: Simon Horman @ 2013-03-01  9:41 UTC (permalink / raw)
  To: Magnus Damm; +Cc: linux-kernel, linux-sh, benh, grant.likely, tglx

On Wed, Feb 27, 2013 at 05:15:01PM +0900, Magnus Damm wrote:
> From: Magnus Damm <damm@opensource.se>
> 
> This patch adds a driver for external IRQ pins connected
> to the IRQC hardware block on recent SoCs from Renesas.
> 
> The IRQC hardware block is used together with more
> recent ARM based SoCs using the GIC. As usual the GIC
> requires external IRQ trigger setup somewhere else
> which in this particular case happens to be IRQC.
> 
> This driver implements the glue code needed to configure
> IRQ trigger and also handle mask/unmask and demux of
> external IRQ pins hooked up from the IRQC to the GIC.
> 
> Tested on r8a73a4 but is designed to work with a wide
> range of SoCs. The driver requires one GIC SPI per
> external IRQ pin to operate.  Each driver instance
> will handle up to 32 external IRQ pins.
> 
> The SoCs using this driver are currently mainly used
> together with regular platform devices so this driver
> allows configuration via platform data to support things
> like static interrupt base address. DT support will
> be added incrementally in the not so distant future.

Hi Magnus, Hi all,

I do not expect this code to go through the renesas tree.  However, in
order to provide a basis for work on renesas SoCs I have added this patch
to the topic/intc-external-irq topic branch in the reneas tree on
kernel.org and merged it into topic/all+next.

In other words, I am not picking this series up to merge it or add it to
linux-next, rather I am storing it for reference.


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH] irqchip: Renesas IRQC driver
  2013-02-27  8:15 [PATCH] irqchip: Renesas IRQC driver Magnus Damm
  2013-02-27  8:40 ` Simon Horman
  2013-03-01  9:41 ` Simon Horman
@ 2013-03-01 15:02 ` Guennadi Liakhovetski
  2013-12-11 23:36 ` Sergei Shtylyov
  3 siblings, 0 replies; 5+ messages in thread
From: Guennadi Liakhovetski @ 2013-03-01 15:02 UTC (permalink / raw)
  To: Magnus Damm; +Cc: linux-kernel, linux-sh, benh, grant.likely, horms, tglx

On Wed, 27 Feb 2013, Magnus Damm wrote:

> From: Magnus Damm <damm@opensource.se>
> 
> This patch adds a driver for external IRQ pins connected
> to the IRQC hardware block on recent SoCs from Renesas.
> 
> The IRQC hardware block is used together with more
> recent ARM based SoCs using the GIC. As usual the GIC
> requires external IRQ trigger setup somewhere else
> which in this particular case happens to be IRQC.
> 
> This driver implements the glue code needed to configure
> IRQ trigger and also handle mask/unmask and demux of
> external IRQ pins hooked up from the IRQC to the GIC.
> 
> Tested on r8a73a4 but is designed to work with a wide
> range of SoCs. The driver requires one GIC SPI per
> external IRQ pin to operate.  Each driver instance
> will handle up to 32 external IRQ pins.
> 
> The SoCs using this driver are currently mainly used
> together with regular platform devices so this driver
> allows configuration via platform data to support things
> like static interrupt base address. DT support will
> be added incrementally in the not so distant future.
> 
> Signed-off-by: Magnus Damm <damm@opensource.se>

tested on kzm9g in topic/all+next branch of

https://git.kernel.org/cgit/linux/kernel/git/horms/renesas.git/

Tested-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>

Thanks
Guennadi

> ---
> 
>  drivers/irqchip/Kconfig                        |    4 
>  drivers/irqchip/Makefile                       |    1 
>  drivers/irqchip/irq-renesas-irqc.c             |  298 ++++++++++++++++++++++++
>  include/linux/platform_data/irq-renesas-irqc.h |   27 ++
>  4 files changed, 330 insertions(+)
> 
> --- 0002/drivers/irqchip/Kconfig
> +++ work/drivers/irqchip/Kconfig	2013-02-27 16:00:08.000000000 +0900
> @@ -29,6 +29,10 @@ config RENESAS_INTC_IRQPIN
>  	bool
>  	select IRQ_DOMAIN
>  
> +config RENESAS_IRQC
> +	bool
> +	select IRQ_DOMAIN
> +
>  config VERSATILE_FPGA_IRQ
>  	bool
>  	select IRQ_DOMAIN
> --- 0002/drivers/irqchip/Makefile
> +++ work/drivers/irqchip/Makefile	2013-02-27 16:00:08.000000000 +0900
> @@ -6,4 +6,5 @@ obj-$(CONFIG_ARCH_SPEAR3XX)		+= spear-sh
>  obj-$(CONFIG_ARM_GIC)			+= irq-gic.o
>  obj-$(CONFIG_ARM_VIC)			+= irq-vic.o
>  obj-$(CONFIG_RENESAS_INTC_IRQPIN)	+= irq-renesas-intc-irqpin.o
> +obj-$(CONFIG_RENESAS_IRQC)		+= irq-renesas-irqc.o
>  obj-$(CONFIG_VERSATILE_FPGA_IRQ)	+= irq-versatile-fpga.o
> --- /dev/null
> +++ work/drivers/irqchip/irq-renesas-irqc.c	2013-02-27 17:04:02.000000000 +0900
> @@ -0,0 +1,298 @@
> +/*
> + * Renesas IRQC Driver
> + *
> + *  Copyright (C) 2013 Magnus Damm
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
> + */
> +
> +#include <linux/init.h>
> +#include <linux/platform_device.h>
> +#include <linux/spinlock.h>
> +#include <linux/interrupt.h>
> +#include <linux/ioport.h>
> +#include <linux/io.h>
> +#include <linux/irq.h>
> +#include <linux/irqdomain.h>
> +#include <linux/err.h>
> +#include <linux/slab.h>
> +#include <linux/module.h>
> +#include <linux/platform_data/irq-renesas-irqc.h>
> +
> +#define IRQC_IRQ_MAX 32 /* maximum 32 interrupts per driver instance */
> +
> +#define IRQC_REQ_STS 0x00
> +#define IRQC_EN_STS 0x04
> +#define IRQC_EN_SET 0x08
> +#define IRQC_INT_CPU_BASE(n) (0x000 + ((n) * 0x10))
> +#define DETECT_STATUS 0x100
> +#define IRQC_CONFIG(n) (0x180 + ((n) * 0x04))
> +
> +struct irqc_irq {
> +	int hw_irq;
> +	int requested_irq;
> +	int domain_irq;
> +	struct irqc_priv *p;
> +};
> +
> +struct irqc_priv {
> +	void __iomem *iomem;
> +	void __iomem *cpu_int_base;
> +	struct irqc_irq irq[IRQC_IRQ_MAX];
> +	struct renesas_irqc_config config;
> +	unsigned int number_of_irqs;
> +	struct platform_device *pdev;
> +	struct irq_chip irq_chip;
> +	struct irq_domain *irq_domain;
> +};
> +
> +static void irqc_dbg(struct irqc_irq *i, char *str)
> +{
> +	dev_dbg(&i->p->pdev->dev, "%s (%d:%d:%d)\n",
> +		str, i->requested_irq, i->hw_irq, i->domain_irq);
> +}
> +
> +static void irqc_irq_enable(struct irq_data *d)
> +{
> +	struct irqc_priv *p = irq_data_get_irq_chip_data(d);
> +	int hw_irq = irqd_to_hwirq(d);
> +
> +	irqc_dbg(&p->irq[hw_irq], "enable");
> +	iowrite32(BIT(hw_irq), p->cpu_int_base + IRQC_EN_SET);
> +}
> +
> +static void irqc_irq_disable(struct irq_data *d)
> +{
> +	struct irqc_priv *p = irq_data_get_irq_chip_data(d);
> +	int hw_irq = irqd_to_hwirq(d);
> +
> +	irqc_dbg(&p->irq[hw_irq], "disable");
> +	iowrite32(BIT(hw_irq), p->cpu_int_base + IRQC_EN_STS);
> +}
> +
> +#define INTC_IRQ_SENSE_VALID 0x10
> +#define INTC_IRQ_SENSE(x) (x + INTC_IRQ_SENSE_VALID)
> +
> +static unsigned char irqc_sense[IRQ_TYPE_SENSE_MASK + 1] = {
> +	[IRQ_TYPE_LEVEL_LOW] = INTC_IRQ_SENSE(0x01),
> +	[IRQ_TYPE_LEVEL_HIGH] = INTC_IRQ_SENSE(0x02),
> +	[IRQ_TYPE_EDGE_FALLING] = INTC_IRQ_SENSE(0x04), /* Synchronous */
> +	[IRQ_TYPE_EDGE_RISING] = INTC_IRQ_SENSE(0x08), /* Synchronous */
> +	[IRQ_TYPE_EDGE_BOTH] = INTC_IRQ_SENSE(0x0c),  /* Synchronous */
> +};
> +
> +static int irqc_irq_set_type(struct irq_data *d, unsigned int type)
> +{
> +	struct irqc_priv *p = irq_data_get_irq_chip_data(d);
> +	int hw_irq = irqd_to_hwirq(d);
> +	unsigned char value = irqc_sense[type & IRQ_TYPE_SENSE_MASK];
> +	unsigned long tmp;
> +
> +	irqc_dbg(&p->irq[hw_irq], "sense");
> +
> +	if (!(value & INTC_IRQ_SENSE_VALID))
> +		return -EINVAL;
> +
> +	tmp = ioread32(p->iomem + IRQC_CONFIG(hw_irq));
> +	tmp &= ~0x3f;
> +	tmp |= value ^ INTC_IRQ_SENSE_VALID;
> +	iowrite32(tmp, p->iomem + IRQC_CONFIG(hw_irq));
> +	return 0;
> +}
> +
> +static irqreturn_t irqc_irq_handler(int irq, void *dev_id)
> +{
> +	struct irqc_irq *i = dev_id;
> +	struct irqc_priv *p = i->p;
> +	unsigned long bit = BIT(i->hw_irq);
> +
> +	irqc_dbg(i, "demux1");
> +
> +	if (ioread32(p->iomem + DETECT_STATUS) & bit) {
> +		iowrite32(bit, p->iomem + DETECT_STATUS);
> +		irqc_dbg(i, "demux2");
> +		generic_handle_irq(i->domain_irq);
> +		return IRQ_HANDLED;
> +	}
> +	return IRQ_NONE;
> +}
> +
> +static int irqc_irq_domain_map(struct irq_domain *h, unsigned int virq,
> +			       irq_hw_number_t hw)
> +{
> +	struct irqc_priv *p = h->host_data;
> +
> +	p->irq[hw].domain_irq = virq;
> +	p->irq[hw].hw_irq = hw;
> +
> +	irqc_dbg(&p->irq[hw], "map");
> +	irq_set_chip_data(virq, h->host_data);
> +	irq_set_chip_and_handler(virq, &p->irq_chip, handle_level_irq);
> +	set_irq_flags(virq, IRQF_VALID); /* kill me now */
> +	return 0;
> +}
> +
> +static struct irq_domain_ops irqc_irq_domain_ops = {
> +	.map	= irqc_irq_domain_map,
> +};
> +
> +static int irqc_probe(struct platform_device *pdev)
> +{
> +	struct renesas_irqc_config *pdata = pdev->dev.platform_data;
> +	struct irqc_priv *p;
> +	struct resource *io;
> +	struct resource *irq;
> +	struct irq_chip *irq_chip;
> +	const char *name = dev_name(&pdev->dev);
> +	int ret;
> +	int k;
> +
> +	p = kzalloc(sizeof(*p), GFP_KERNEL);
> +	if (!p) {
> +		dev_err(&pdev->dev, "failed to allocate driver data\n");
> +		ret = -ENOMEM;
> +		goto err0;
> +	}
> +
> +	/* deal with driver instance configuration */
> +	if (pdata)
> +		memcpy(&p->config, pdata, sizeof(*pdata));
> +
> +	p->pdev = pdev;
> +	platform_set_drvdata(pdev, p);
> +
> +	/* get hold of manadatory IOMEM */
> +	io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!io) {
> +		dev_err(&pdev->dev, "not enough IOMEM resources\n");
> +		ret = -EINVAL;
> +		goto err1;
> +	}
> +
> +	/* allow any number of IRQs between 1 and IRQC_IRQ_MAX */
> +	for (k = 0; k < IRQC_IRQ_MAX; k++) {
> +		irq = platform_get_resource(pdev, IORESOURCE_IRQ, k);
> +		if (!irq)
> +			break;
> +
> +		p->irq[k].p = p;
> +		p->irq[k].requested_irq = irq->start;
> +	}
> +
> +	p->number_of_irqs = k;
> +	if (p->number_of_irqs < 1) {
> +		dev_err(&pdev->dev, "not enough IRQ resources\n");
> +		ret = -EINVAL;
> +		goto err1;
> +	}
> +
> +	/* ioremap IOMEM and setup read/write callbacks */
> +	p->iomem = ioremap_nocache(io->start, resource_size(io));
> +	if (!p->iomem) {
> +		dev_err(&pdev->dev, "failed to remap IOMEM\n");
> +		ret = -ENXIO;
> +		goto err2;
> +	}
> +
> +	p->cpu_int_base = p->iomem + IRQC_INT_CPU_BASE(0); /* SYS-SPI */
> +
> +	irq_chip = &p->irq_chip;
> +	irq_chip->name = name;
> +	irq_chip->irq_mask = irqc_irq_disable;
> +	irq_chip->irq_unmask = irqc_irq_enable;
> +	irq_chip->irq_enable = irqc_irq_enable;
> +	irq_chip->irq_disable = irqc_irq_disable;
> +	irq_chip->irq_set_type = irqc_irq_set_type;
> +	irq_chip->flags	= IRQCHIP_SKIP_SET_WAKE;
> +
> +	p->irq_domain = irq_domain_add_simple(pdev->dev.of_node,
> +					      p->number_of_irqs,
> +					      p->config.irq_base,
> +					      &irqc_irq_domain_ops, p);
> +	if (!p->irq_domain) {
> +		ret = -ENXIO;
> +		dev_err(&pdev->dev, "cannot initialize irq domain\n");
> +		goto err2;
> +	}
> +
> +	/* request interrupts one by one */
> +	for (k = 0; k < p->number_of_irqs; k++) {
> +		if (request_irq(p->irq[k].requested_irq, irqc_irq_handler,
> +				0, name, &p->irq[k])) {
> +			dev_err(&pdev->dev, "failed to request IRQ\n");
> +			ret = -ENOENT;
> +			goto err3;
> +		}
> +	}
> +
> +	dev_info(&pdev->dev, "driving %d irqs\n", p->number_of_irqs);
> +
> +	/* warn in case of mismatch if irq base is specified */
> +	if (p->config.irq_base) {
> +		if (p->config.irq_base != p->irq[0].domain_irq)
> +			dev_warn(&pdev->dev, "irq base mismatch (%d/%d)\n",
> +				 p->config.irq_base, p->irq[0].domain_irq);
> +	}
> +
> +	return 0;
> +err3:
> +	for (; k >= 0; k--)
> +		free_irq(p->irq[k - 1].requested_irq, &p->irq[k - 1]);
> +
> +	irq_domain_remove(p->irq_domain);
> +err2:
> +	iounmap(p->iomem);
> +err1:
> +	kfree(p);
> +err0:
> +	return ret;
> +}
> +
> +static int irqc_remove(struct platform_device *pdev)
> +{
> +	struct irqc_priv *p = platform_get_drvdata(pdev);
> +	int k;
> +
> +	for (k = 0; k < p->number_of_irqs; k++)
> +		free_irq(p->irq[k].requested_irq, &p->irq[k]);
> +
> +	irq_domain_remove(p->irq_domain);
> +	iounmap(p->iomem);
> +	kfree(p);
> +	return 0;
> +}
> +
> +static struct platform_driver irqc_device_driver = {
> +	.probe		= irqc_probe,
> +	.remove		= irqc_remove,
> +	.driver		= {
> +		.name	= "renesas_irqc",
> +	}
> +};
> +
> +static int __init irqc_init(void)
> +{
> +	return platform_driver_register(&irqc_device_driver);
> +}
> +postcore_initcall(irqc_init);
> +
> +static void __exit irqc_exit(void)
> +{
> +	platform_driver_unregister(&irqc_device_driver);
> +}
> +module_exit(irqc_exit);
> +
> +MODULE_AUTHOR("Magnus Damm");
> +MODULE_DESCRIPTION("Renesas IRQC Driver");
> +MODULE_LICENSE("GPL v2");
> --- /dev/null
> +++ work/include/linux/platform_data/irq-renesas-irqc.h	2013-02-27 16:12:09.000000000 +0900
> @@ -0,0 +1,27 @@
> +/*
> + * Renesas IRQC Driver
> + *
> + *  Copyright (C) 2013 Magnus Damm
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
> + */
> +
> +#ifndef __IRQ_RENESAS_IRQC_H__
> +#define __IRQ_RENESAS_IRQC_H__
> +
> +struct renesas_irqc_config {
> +	unsigned int irq_base;
> +};
> +
> +#endif /* __IRQ_RENESAS_IRQC_H__ */
> --
> To unsubscribe from this list: send the line "unsubscribe linux-sh" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

---
Guennadi Liakhovetski, Ph.D.
Freelance Open-Source Software Developer
http://www.open-technology.de/

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH] irqchip: Renesas IRQC driver
  2013-02-27  8:15 [PATCH] irqchip: Renesas IRQC driver Magnus Damm
                   ` (2 preceding siblings ...)
  2013-03-01 15:02 ` Guennadi Liakhovetski
@ 2013-12-11 23:36 ` Sergei Shtylyov
  3 siblings, 0 replies; 5+ messages in thread
From: Sergei Shtylyov @ 2013-12-11 23:36 UTC (permalink / raw)
  To: Magnus Damm, linux-kernel; +Cc: linux-sh, benh, grant.likely, horms, tglx

Hello.

On 02/27/2013 11:15 AM, Magnus Damm wrote:

> From: Magnus Damm <damm@opensource.se>

    Magnus, explain me one thing about your driver please.

> This patch adds a driver for external IRQ pins connected
> to the IRQC hardware block on recent SoCs from Renesas.
>
> The IRQC hardware block is used together with more
> recent ARM based SoCs using the GIC. As usual the GIC
> requires external IRQ trigger setup somewhere else
> which in this particular case happens to be IRQC.
>
> This driver implements the glue code needed to configure
> IRQ trigger and also handle mask/unmask and demux of
> external IRQ pins hooked up from the IRQC to the GIC.

    Judging on the R8A779x manual pictures, IRQn enter the SoC via IRQC, right?
Why then you call request_irq() on GIC interrupt numbers and expect an 
interrupt to "enter" from the GIC side, and then only read IRQC's 
DETECT_STATUS register to make sure the matching IRQn is really active (you 
call it demux, but I don't really see much of a demultiplexing there). This 
seems to me fundamentally broken, and is probably why the interrupt from PHY 
doesn't happen despite I set the interrupt type to active low via a call to 
irq_set_irq_type()...

> Tested on r8a73a4 but is designed to work with a wide
> range of SoCs. The driver requires one GIC SPI per
> external IRQ pin to operate.  Each driver instance
> will handle up to 32 external IRQ pins.

    Ah, so you've even tested it... I got an impression from your words that 
the driver hasn't been tested.

> The SoCs using this driver are currently mainly used
> together with regular platform devices so this driver
> allows configuration via platform data to support things
> like static interrupt base address. DT support will
> be added incrementally in the not so distant future.

> Signed-off-by: Magnus Damm <damm@opensource.se>

WBR, Sergei


^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2013-12-11 22:36 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-02-27  8:15 [PATCH] irqchip: Renesas IRQC driver Magnus Damm
2013-02-27  8:40 ` Simon Horman
2013-03-01  9:41 ` Simon Horman
2013-03-01 15:02 ` Guennadi Liakhovetski
2013-12-11 23:36 ` Sergei Shtylyov

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox