[PATCH V4 2/2] irqchip: qcom: Add IRQ combiner driver

2016-10-17 Thread Agustin Vega-Frias
Driver for interrupt combiners in the Top-level Control and Status
Registers (TCSR) hardware block in Qualcomm Technologies chips.

An interrupt combiner in this block combines a set of interrupts by
OR'ing the individual interrupt signals into a summary interrupt
signal routed to a parent interrupt controller, and provides read-
only, 32-bit registers to query the status of individual interrupts.
The status bit for IRQ n is bit (n % 32) within register (n / 32)
of the given combiner. Thus, each combiner can be described as a set
of register offsets and the number of IRQs managed.

Signed-off-by: Agustin Vega-Frias 
---
 drivers/irqchip/Kconfig |   8 +
 drivers/irqchip/Makefile|   1 +
 drivers/irqchip/qcom-irq-combiner.c | 332 
 3 files changed, 341 insertions(+)
 create mode 100644 drivers/irqchip/qcom-irq-combiner.c

diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 82b0b5d..05ecd91 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -279,3 +279,11 @@ config EZNPS_GIC
 config STM32_EXTI
bool
select IRQ_DOMAIN
+
+config QCOM_IRQ_COMBINER
+   bool "QCOM IRQ combiner support"
+   depends on ARCH_QCOM
+   select IRQ_DOMAIN
+   help
+ Say yes here to add support for the IRQ combiner devices embedded
+ in Qualcomm Technologies chips.
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index e4dbfc8..1818a0b 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -74,3 +74,4 @@ obj-$(CONFIG_LS_SCFG_MSI) += irq-ls-scfg-msi.o
 obj-$(CONFIG_EZNPS_GIC)+= irq-eznps.o
 obj-$(CONFIG_ARCH_ASPEED)  += irq-aspeed-vic.o
 obj-$(CONFIG_STM32_EXTI)   += irq-stm32-exti.o
+obj-$(CONFIG_QCOM_IRQ_COMBINER)+= qcom-irq-combiner.o
diff --git a/drivers/irqchip/qcom-irq-combiner.c 
b/drivers/irqchip/qcom-irq-combiner.c
new file mode 100644
index 000..b117cd7
--- /dev/null
+++ b/drivers/irqchip/qcom-irq-combiner.c
@@ -0,0 +1,332 @@
+/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+/*
+ * Driver for interrupt combiners in the Top-level Control and Status
+ * Registers (TCSR) hardware block in Qualcomm Technologies chips.
+ * An interrupt combiner in this block combines a set of interrupts by
+ * OR'ing the individual interrupt signals into a summary interrupt
+ * signal routed to a parent interrupt controller, and provides read-
+ * only, 32-bit registers to query the status of individual interrupts.
+ * The status bit for IRQ n is bit (n % 32) within register (n / 32)
+ * of the given combiner. Thus, each combiner can be described as a set
+ * of register offsets and the number of IRQs managed.
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#define REG_SIZE 32
+
+struct combiner_reg {
+   void __iomem *addr;
+   unsigned long mask;
+};
+
+struct combiner {
+   struct irq_chip irq_chip;
+   struct irq_domain   *domain;
+   int parent_irq;
+   u32 nirqs;
+   u32 nregs;
+   struct combiner_reg regs[0];
+};
+
+static inline u32 irq_register(int irq)
+{
+   return irq / REG_SIZE;
+}
+
+static inline u32 irq_bit(int irq)
+{
+   return irq % REG_SIZE;
+
+}
+
+static inline int irq_nr(u32 reg, u32 bit)
+{
+   return reg * REG_SIZE + bit;
+}
+
+/*
+ * Handler for the cascaded IRQ.
+ */
+static void combiner_handle_irq(struct irq_desc *desc)
+{
+   struct combiner *combiner = irq_desc_get_handler_data(desc);
+   struct irq_chip *chip = irq_desc_get_chip(desc);
+   u32 reg;
+
+   chained_irq_enter(chip, desc);
+
+   for (reg = 0; reg < combiner->nregs; reg++) {
+   int virq;
+   int hwirq;
+   u32 bit;
+   u32 status;
+
+   if (combiner->regs[reg].mask == 0)
+   continue;
+
+   status = readl_relaxed(combiner->regs[reg].addr);
+   status &= combiner->regs[reg].mask;
+
+   while (status) {
+   bit = __ffs(status);
+   status &= ~(1 << bit);
+   hwirq = irq_nr(reg, bit);
+   virq = irq_find_mapping(combiner->domain, hwirq);
+   if (virq >= 0)
+   generic_handle_irq(virq);
+
+   }
+   }
+
+   

[PATCH V4 2/2] irqchip: qcom: Add IRQ combiner driver

2016-10-17 Thread Agustin Vega-Frias
Driver for interrupt combiners in the Top-level Control and Status
Registers (TCSR) hardware block in Qualcomm Technologies chips.

An interrupt combiner in this block combines a set of interrupts by
OR'ing the individual interrupt signals into a summary interrupt
signal routed to a parent interrupt controller, and provides read-
only, 32-bit registers to query the status of individual interrupts.
The status bit for IRQ n is bit (n % 32) within register (n / 32)
of the given combiner. Thus, each combiner can be described as a set
of register offsets and the number of IRQs managed.

Signed-off-by: Agustin Vega-Frias 
---
 drivers/irqchip/Kconfig |   8 +
 drivers/irqchip/Makefile|   1 +
 drivers/irqchip/qcom-irq-combiner.c | 332 
 3 files changed, 341 insertions(+)
 create mode 100644 drivers/irqchip/qcom-irq-combiner.c

diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 82b0b5d..05ecd91 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -279,3 +279,11 @@ config EZNPS_GIC
 config STM32_EXTI
bool
select IRQ_DOMAIN
+
+config QCOM_IRQ_COMBINER
+   bool "QCOM IRQ combiner support"
+   depends on ARCH_QCOM
+   select IRQ_DOMAIN
+   help
+ Say yes here to add support for the IRQ combiner devices embedded
+ in Qualcomm Technologies chips.
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index e4dbfc8..1818a0b 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -74,3 +74,4 @@ obj-$(CONFIG_LS_SCFG_MSI) += irq-ls-scfg-msi.o
 obj-$(CONFIG_EZNPS_GIC)+= irq-eznps.o
 obj-$(CONFIG_ARCH_ASPEED)  += irq-aspeed-vic.o
 obj-$(CONFIG_STM32_EXTI)   += irq-stm32-exti.o
+obj-$(CONFIG_QCOM_IRQ_COMBINER)+= qcom-irq-combiner.o
diff --git a/drivers/irqchip/qcom-irq-combiner.c 
b/drivers/irqchip/qcom-irq-combiner.c
new file mode 100644
index 000..b117cd7
--- /dev/null
+++ b/drivers/irqchip/qcom-irq-combiner.c
@@ -0,0 +1,332 @@
+/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+/*
+ * Driver for interrupt combiners in the Top-level Control and Status
+ * Registers (TCSR) hardware block in Qualcomm Technologies chips.
+ * An interrupt combiner in this block combines a set of interrupts by
+ * OR'ing the individual interrupt signals into a summary interrupt
+ * signal routed to a parent interrupt controller, and provides read-
+ * only, 32-bit registers to query the status of individual interrupts.
+ * The status bit for IRQ n is bit (n % 32) within register (n / 32)
+ * of the given combiner. Thus, each combiner can be described as a set
+ * of register offsets and the number of IRQs managed.
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#define REG_SIZE 32
+
+struct combiner_reg {
+   void __iomem *addr;
+   unsigned long mask;
+};
+
+struct combiner {
+   struct irq_chip irq_chip;
+   struct irq_domain   *domain;
+   int parent_irq;
+   u32 nirqs;
+   u32 nregs;
+   struct combiner_reg regs[0];
+};
+
+static inline u32 irq_register(int irq)
+{
+   return irq / REG_SIZE;
+}
+
+static inline u32 irq_bit(int irq)
+{
+   return irq % REG_SIZE;
+
+}
+
+static inline int irq_nr(u32 reg, u32 bit)
+{
+   return reg * REG_SIZE + bit;
+}
+
+/*
+ * Handler for the cascaded IRQ.
+ */
+static void combiner_handle_irq(struct irq_desc *desc)
+{
+   struct combiner *combiner = irq_desc_get_handler_data(desc);
+   struct irq_chip *chip = irq_desc_get_chip(desc);
+   u32 reg;
+
+   chained_irq_enter(chip, desc);
+
+   for (reg = 0; reg < combiner->nregs; reg++) {
+   int virq;
+   int hwirq;
+   u32 bit;
+   u32 status;
+
+   if (combiner->regs[reg].mask == 0)
+   continue;
+
+   status = readl_relaxed(combiner->regs[reg].addr);
+   status &= combiner->regs[reg].mask;
+
+   while (status) {
+   bit = __ffs(status);
+   status &= ~(1 << bit);
+   hwirq = irq_nr(reg, bit);
+   virq = irq_find_mapping(combiner->domain, hwirq);
+   if (virq >= 0)
+   generic_handle_irq(virq);
+
+   }
+   }
+
+   chained_irq_exit(chip,