[v2,3/3] irqchip: add nps Internal and external irqchips
diff mbox

Message ID 1454418899-25500-4-git-send-email-noamc@ezchip.com
State Superseded
Headers show

Commit Message

Noam Camus Feb. 2, 2016, 1:14 p.m. UTC
From: Noam Camus <noamc@ezchip.com>

Adding EZchip NPS400 support.
NPS internal interrupts are internally handled at
Multi Thread Manager (MTM) that is signaled for deactivating
an interrupt.
External interrupts is handled also at Global Interrupt
Controller (GIC) e.g. serial and network devices.

Signed-off-by: Noam Camus <noamc@ezchip.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Jason Cooper <jason@lakedaemon.net>
Cc: Marc Zyngier <marc.zyngier@arm.com>
---
 .../interrupt-controller/ezchip,nps400-ic.txt      |   17 +++
 drivers/irqchip/Kconfig                            |    6 +
 drivers/irqchip/Makefile                           |    1 +
 drivers/irqchip/irq-eznps.c                        |  151 ++++++++++++++++++++
 4 files changed, 175 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/interrupt-controller/ezchip,nps400-ic.txt
 create mode 100644 drivers/irqchip/irq-eznps.c

Comments

Thomas Gleixner Feb. 2, 2016, 1:35 p.m. UTC | #1
On Tue, 2 Feb 2016, Noam Camus wrote:
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/irqdomain.h>
> +#include <linux/irqchip.h>
> +#include <soc/nps/common.h>
> +
> +#undef NR_CPU_IRQS

What's that #undef for?

> +#define NR_CPU_IRQS	8  /* number of interrupt lines of NPS400 CPU */
> +#define TIMER0_IRQ	3

> +static void nps400_irq_eoi_global(struct irq_data *irqd)
> +{
> +	unsigned int __maybe_unused irq = irqd_to_hwirq(irqd);
> +
> +	write_aux_reg(CTOP_AUX_IACK, 1 << irq);
> +
> +	/* Don't ack before all device access attempts are done */
> +	mb();

And what is that memory barrier for if this is not on ARC?

> +
> +#ifdef __arc__
> +	__asm__ __volatile__ (
> +	"       .word %0\n"

First of all this wants to be .inst not .word.

> +	:
> +	: "i"(CTOP_INST_RSPI_GIC_0_R12)
> +	: "memory");

And this needs be defined as an inline somewhere in arch/arc and not in the
driver. In the driver you do:

#ifdef CONFIG_ARCH_ARC
# include <arch/....>
#else
static inline void arc_ack_gic(void) { }
#endif

static void nps400_irq_eoi_global
{
	....
	arc_ack_gic();
}

Hmm?

> +static int nps400_irq_map(struct irq_domain *d, unsigned int virq,
> +			  irq_hw_number_t hw)
> +{
> +	switch (hw) {
> +	case TIMER0_IRQ:
> +#ifdef CONFIG_SMP
> +	case IPI_IRQ:
> +#endif
> +		irq_set_percpu_devid(virq);
> +		irq_set_chip_and_handler(virq, &nps400_irq_chip_percpu,
> +					 handle_percpu_devid_irq);
> +	break;

		break;
Please

> +	default:
> +		irq_set_chip_and_handler(virq, &nps400_irq_chip_fasteoi,
> +					 handle_fasteoi_irq);
> +	break;

Ditto.

Thanks,

	tglx
kbuild test robot Feb. 2, 2016, 8:40 p.m. UTC | #2
Hi Noam,

[auto build test ERROR on tip/irq/core]
[cannot apply to v4.5-rc2 next-20160202]
[if your patch is applied to the wrong git tree, please drop us a note to help improving the system]

url:    https://github.com/0day-ci/linux/commits/Noam-Camus/Adding-NPS400-drivers/20160202-213530
config: arm-allmodconfig (attached as .config)
reproduce:
        wget https://git.kernel.org/cgit/linux/kernel/git/wfg/lkp-tests.git/plain/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        make.cross ARCH=arm 

All error/warnings (new ones prefixed by >>):

   drivers/irqchip/irq-eznps.c: In function 'nps400_irq_mask':
>> drivers/irqchip/irq-eznps.c:43:2: error: implicit declaration of function 'irqd_to_hwirq' [-Werror=implicit-function-declaration]
     unsigned int irq = irqd_to_hwirq(irqd);
     ^
   drivers/irqchip/irq-eznps.c: At top level:
>> drivers/irqchip/irq-eznps.c:85:15: error: variable 'nps400_irq_chip_fasteoi' has initializer but incomplete type
    static struct irq_chip nps400_irq_chip_fasteoi = {
                  ^
>> drivers/irqchip/irq-eznps.c:86:2: error: unknown field 'name' specified in initializer
     .name  = "NPS400 IC Global",
     ^
>> drivers/irqchip/irq-eznps.c:86:2: warning: excess elements in struct initializer
   drivers/irqchip/irq-eznps.c:86:2: warning: (near initialization for 'nps400_irq_chip_fasteoi')
>> drivers/irqchip/irq-eznps.c:87:2: error: unknown field 'irq_mask' specified in initializer
     .irq_mask = nps400_irq_mask,
     ^
   drivers/irqchip/irq-eznps.c:87:2: warning: excess elements in struct initializer
   drivers/irqchip/irq-eznps.c:87:2: warning: (near initialization for 'nps400_irq_chip_fasteoi')
>> drivers/irqchip/irq-eznps.c:88:2: error: unknown field 'irq_unmask' specified in initializer
     .irq_unmask = nps400_irq_unmask,
     ^
   drivers/irqchip/irq-eznps.c:88:2: warning: excess elements in struct initializer
   drivers/irqchip/irq-eznps.c:88:2: warning: (near initialization for 'nps400_irq_chip_fasteoi')
>> drivers/irqchip/irq-eznps.c:89:2: error: unknown field 'irq_eoi' specified in initializer
     .irq_eoi = nps400_irq_eoi_global,
     ^
   drivers/irqchip/irq-eznps.c:89:2: warning: excess elements in struct initializer
   drivers/irqchip/irq-eznps.c:89:2: warning: (near initialization for 'nps400_irq_chip_fasteoi')
>> drivers/irqchip/irq-eznps.c:92:15: error: variable 'nps400_irq_chip_percpu' has initializer but incomplete type
    static struct irq_chip nps400_irq_chip_percpu = {
                  ^
   drivers/irqchip/irq-eznps.c:93:2: error: unknown field 'name' specified in initializer
     .name  = "NPS400 IC",
     ^
   drivers/irqchip/irq-eznps.c:93:2: warning: excess elements in struct initializer
   drivers/irqchip/irq-eznps.c:93:2: warning: (near initialization for 'nps400_irq_chip_percpu')
   drivers/irqchip/irq-eznps.c:94:2: error: unknown field 'irq_mask' specified in initializer
     .irq_mask = nps400_irq_mask,
     ^
   drivers/irqchip/irq-eznps.c:94:2: warning: excess elements in struct initializer
   drivers/irqchip/irq-eznps.c:94:2: warning: (near initialization for 'nps400_irq_chip_percpu')
   drivers/irqchip/irq-eznps.c:95:2: error: unknown field 'irq_unmask' specified in initializer
     .irq_unmask = nps400_irq_unmask,
     ^
   drivers/irqchip/irq-eznps.c:95:2: warning: excess elements in struct initializer
   drivers/irqchip/irq-eznps.c:95:2: warning: (near initialization for 'nps400_irq_chip_percpu')
   drivers/irqchip/irq-eznps.c:96:2: error: unknown field 'irq_eoi' specified in initializer
     .irq_eoi = nps400_irq_eoi,
     ^
   drivers/irqchip/irq-eznps.c:96:2: warning: excess elements in struct initializer
   drivers/irqchip/irq-eznps.c:96:2: warning: (near initialization for 'nps400_irq_chip_percpu')
   drivers/irqchip/irq-eznps.c: In function 'nps400_irq_map':
>> drivers/irqchip/irq-eznps.c:107:3: error: implicit declaration of function 'irq_set_percpu_devid' [-Werror=implicit-function-declaration]
      irq_set_percpu_devid(virq);
      ^
>> drivers/irqchip/irq-eznps.c:108:3: error: implicit declaration of function 'irq_set_chip_and_handler' [-Werror=implicit-function-declaration]
      irq_set_chip_and_handler(virq, &nps400_irq_chip_percpu,
      ^
>> drivers/irqchip/irq-eznps.c:109:7: error: 'handle_percpu_devid_irq' undeclared (first use in this function)
          handle_percpu_devid_irq);
          ^
   drivers/irqchip/irq-eznps.c:109:7: note: each undeclared identifier is reported only once for each function it appears in
>> drivers/irqchip/irq-eznps.c:113:7: error: 'handle_fasteoi_irq' undeclared (first use in this function)
          handle_fasteoi_irq);
          ^
   cc1: some warnings being treated as errors

vim +/irqd_to_hwirq +43 drivers/irqchip/irq-eznps.c

    37	 * with private HW copy per CPU.
    38	 */
    39	
    40	static void nps400_irq_mask(struct irq_data *irqd)
    41	{
    42		unsigned int ienb;
  > 43		unsigned int irq = irqd_to_hwirq(irqd);
    44	
    45		ienb = read_aux_reg(AUX_IENABLE);
    46		ienb &= ~(1 << irq);
    47		write_aux_reg(AUX_IENABLE, ienb);
    48	}
    49	
    50	static void nps400_irq_unmask(struct irq_data *irqd)
    51	{
    52		unsigned int ienb;
    53		unsigned int irq = irqd_to_hwirq(irqd);
    54	
    55		ienb = read_aux_reg(AUX_IENABLE);
    56		ienb |= (1 << irq);
    57		write_aux_reg(AUX_IENABLE, ienb);
    58	}
    59	
    60	static void nps400_irq_eoi_global(struct irq_data *irqd)
    61	{
    62		unsigned int __maybe_unused irq = irqd_to_hwirq(irqd);
    63	
    64		write_aux_reg(CTOP_AUX_IACK, 1 << irq);
    65	
    66		/* Don't ack before all device access attempts are done */
    67		mb();
    68	
    69	#ifdef __arc__
    70		__asm__ __volatile__ (
    71		"       .word %0\n"
    72		:
    73		: "i"(CTOP_INST_RSPI_GIC_0_R12)
    74		: "memory");
    75	#endif
    76	}
    77	
    78	static void nps400_irq_eoi(struct irq_data *irqd)
    79	{
    80		unsigned int __maybe_unused irq = irqd_to_hwirq(irqd);
    81	
    82		write_aux_reg(CTOP_AUX_IACK, 1 << irq);
    83	}
    84	
  > 85	static struct irq_chip nps400_irq_chip_fasteoi = {
  > 86		.name		= "NPS400 IC Global",
  > 87		.irq_mask	= nps400_irq_mask,
  > 88		.irq_unmask	= nps400_irq_unmask,
  > 89		.irq_eoi	= nps400_irq_eoi_global,
    90	};
    91	
  > 92	static struct irq_chip nps400_irq_chip_percpu = {
    93		.name		= "NPS400 IC",
    94		.irq_mask	= nps400_irq_mask,
  > 95		.irq_unmask	= nps400_irq_unmask,
  > 96		.irq_eoi	= nps400_irq_eoi,
    97	};
    98	
    99	static int nps400_irq_map(struct irq_domain *d, unsigned int virq,
   100				  irq_hw_number_t hw)
   101	{
   102		switch (hw) {
   103		case TIMER0_IRQ:
   104	#ifdef CONFIG_SMP
   105		case IPI_IRQ:
   106	#endif
 > 107			irq_set_percpu_devid(virq);
 > 108			irq_set_chip_and_handler(virq, &nps400_irq_chip_percpu,
 > 109						 handle_percpu_devid_irq);
   110		break;
   111		default:
   112			irq_set_chip_and_handler(virq, &nps400_irq_chip_fasteoi,
 > 113						 handle_fasteoi_irq);
   114		break;
   115		}
   116	

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

Patch
diff mbox

diff --git a/Documentation/devicetree/bindings/interrupt-controller/ezchip,nps400-ic.txt b/Documentation/devicetree/bindings/interrupt-controller/ezchip,nps400-ic.txt
new file mode 100644
index 0000000..888b2b9
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/ezchip,nps400-ic.txt
@@ -0,0 +1,17 @@ 
+EZchip NPS Interrupt Controller
+
+Required properties:
+
+- compatible : should be "ezchip,nps400-ic"
+- interrupt-controller : Identifies the node as an interrupt controller
+- #interrupt-cells : Specifies the number of cells needed to encode an
+  interrupt source. The value shall be 1.
+
+
+Example:
+
+intc: interrupt-controller {
+	compatible = "ezchip,nps400-ic";
+	interrupt-controller;
+	#interrupt-cells = <1>;
+};
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 4d7294e..bc5e775 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -193,3 +193,9 @@  config IRQ_MXS
 	def_bool y if MACH_ASM9260 || ARCH_MXS
 	select IRQ_DOMAIN
 	select STMP_DEVICE
+
+config EZNPS_GIC
+	bool "NPS400 Global Interrupt Manager (GIM)"
+	select IRQ_DOMAIN
+	help
+	  Support the EZchip NPS400 global interrupt controller
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 177f78f..1390142 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -55,3 +55,4 @@  obj-$(CONFIG_RENESAS_H8S_INTC)		+= irq-renesas-h8s.o
 obj-$(CONFIG_ARCH_SA1100)		+= irq-sa11x0.o
 obj-$(CONFIG_INGENIC_IRQ)		+= irq-ingenic.o
 obj-$(CONFIG_IMX_GPCV2)			+= irq-imx-gpcv2.o
+obj-$(CONFIG_EZNPS_GIC)			+= irq-eznps.o
diff --git a/drivers/irqchip/irq-eznps.c b/drivers/irqchip/irq-eznps.c
new file mode 100644
index 0000000..ac29f32
--- /dev/null
+++ b/drivers/irqchip/irq-eznps.c
@@ -0,0 +1,151 @@ 
+/*
+ * Copyright(c) 2015 EZchip Technologies.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ */
+
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/irqdomain.h>
+#include <linux/irqchip.h>
+#include <soc/nps/common.h>
+
+#undef NR_CPU_IRQS
+#define NR_CPU_IRQS	8  /* number of interrupt lines of NPS400 CPU */
+#define TIMER0_IRQ	3
+
+/*
+ * NPS400 core includes an Interrupt Controller (IC) support.
+ * All cores can deactivate level irqs at first level control
+ * at cores mesh layer called MTM.
+ * For devices out side chip e.g. uart, network there is another
+ * level called Global Interrupt Manager (GIM).
+ * This second level can control level and edge interrupt.
+ *
+ * NOTE: AUX_IENABLE and CTOP_AUX_IACK are auxiliary registers
+ * with private HW copy per CPU.
+ */
+
+static void nps400_irq_mask(struct irq_data *irqd)
+{
+	unsigned int ienb;
+	unsigned int irq = irqd_to_hwirq(irqd);
+
+	ienb = read_aux_reg(AUX_IENABLE);
+	ienb &= ~(1 << irq);
+	write_aux_reg(AUX_IENABLE, ienb);
+}
+
+static void nps400_irq_unmask(struct irq_data *irqd)
+{
+	unsigned int ienb;
+	unsigned int irq = irqd_to_hwirq(irqd);
+
+	ienb = read_aux_reg(AUX_IENABLE);
+	ienb |= (1 << irq);
+	write_aux_reg(AUX_IENABLE, ienb);
+}
+
+static void nps400_irq_eoi_global(struct irq_data *irqd)
+{
+	unsigned int __maybe_unused irq = irqd_to_hwirq(irqd);
+
+	write_aux_reg(CTOP_AUX_IACK, 1 << irq);
+
+	/* Don't ack before all device access attempts are done */
+	mb();
+
+#ifdef __arc__
+	__asm__ __volatile__ (
+	"       .word %0\n"
+	:
+	: "i"(CTOP_INST_RSPI_GIC_0_R12)
+	: "memory");
+#endif
+}
+
+static void nps400_irq_eoi(struct irq_data *irqd)
+{
+	unsigned int __maybe_unused irq = irqd_to_hwirq(irqd);
+
+	write_aux_reg(CTOP_AUX_IACK, 1 << irq);
+}
+
+static struct irq_chip nps400_irq_chip_fasteoi = {
+	.name		= "NPS400 IC Global",
+	.irq_mask	= nps400_irq_mask,
+	.irq_unmask	= nps400_irq_unmask,
+	.irq_eoi	= nps400_irq_eoi_global,
+};
+
+static struct irq_chip nps400_irq_chip_percpu = {
+	.name		= "NPS400 IC",
+	.irq_mask	= nps400_irq_mask,
+	.irq_unmask	= nps400_irq_unmask,
+	.irq_eoi	= nps400_irq_eoi,
+};
+
+static int nps400_irq_map(struct irq_domain *d, unsigned int virq,
+			  irq_hw_number_t hw)
+{
+	switch (hw) {
+	case TIMER0_IRQ:
+#ifdef CONFIG_SMP
+	case IPI_IRQ:
+#endif
+		irq_set_percpu_devid(virq);
+		irq_set_chip_and_handler(virq, &nps400_irq_chip_percpu,
+					 handle_percpu_devid_irq);
+	break;
+	default:
+		irq_set_chip_and_handler(virq, &nps400_irq_chip_fasteoi,
+					 handle_fasteoi_irq);
+	break;
+	}
+
+	return 0;
+}
+
+static const struct irq_domain_ops nps400_irq_ops = {
+	.xlate = irq_domain_xlate_onecell,
+	.map = nps400_irq_map,
+};
+
+static int __init nps400_of_init(struct device_node *node,
+				 struct device_node *parent)
+{
+	static struct irq_domain *nps400_root_domain;
+
+	if (parent)
+		panic("DeviceTree incore ic not a root irq controller\n");
+
+	nps400_root_domain = irq_domain_add_linear(node, NR_CPU_IRQS,
+						   &nps400_irq_ops, NULL);
+
+	if (!nps400_root_domain)
+		panic("nps400 root irq domain not avail\n");
+
+	/*
+	 * Needed for primary domain lookup to succeed
+	 * This is a primary irqchip, and can never have a parent
+	 */
+	irq_set_default_host(nps400_root_domain);
+
+#ifdef CONFIG_SMP
+	irq_create_mapping(nps400_root_domain, IPI_IRQ);
+#endif
+
+	return 0;
+}
+IRQCHIP_DECLARE(ezchip_nps400_ic, "ezchip,nps400-ic", nps400_of_init);