[PATCH 19/19] irqchip: add irq-nationalchip.c and irq-csky.c

From: Guo Ren
Date: Sun Mar 18 2018 - 15:53:05 EST


Signed-off-by: Guo Ren <ren_guo@xxxxxxxxx>
---
drivers/irqchip/Makefile | 1 +
drivers/irqchip/irq-csky.c | 151 ++++++++++++++++++++++++++++
drivers/irqchip/irq-nationalchip.c | 196 +++++++++++++++++++++++++++++++++++++
3 files changed, 348 insertions(+)
create mode 100644 drivers/irqchip/irq-csky.c
create mode 100644 drivers/irqchip/irq-nationalchip.c

diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index d27e3e3..7b98917 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -85,3 +85,4 @@ obj-$(CONFIG_IRQ_UNIPHIER_AIDET) += irq-uniphier-aidet.o
obj-$(CONFIG_ARCH_SYNQUACER) += irq-sni-exiu.o
obj-$(CONFIG_MESON_IRQ_GPIO) += irq-meson-gpio.o
obj-$(CONFIG_GOLDFISH_PIC) += irq-goldfish-pic.o
+obj-$(CONFIG_CSKY) += irq-csky.o irq-nationalchip.o
diff --git a/drivers/irqchip/irq-csky.c b/drivers/irqchip/irq-csky.c
new file mode 100644
index 0000000..77041e3
--- /dev/null
+++ b/drivers/irqchip/irq-csky.c
@@ -0,0 +1,151 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2018 Hangzhou C-SKY Microsystems co.,ltd.
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/module.h>
+#include <linux/irqdomain.h>
+#include <linux/irqchip.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+
+static unsigned int intc_reg;
+
+#define CK_VA_INTC_ICR (void *)(intc_reg + 0x00) /* Interrupt control register(High 16bits) */
+#define CK_VA_INTC_ISR (void *)(intc_reg + 0x00) /* Interrupt status register(Low 16bits) */
+#define CK_VA_INTC_NEN31_00 (void *)(intc_reg + 0x10) /* Normal interrupt enable register Low */
+#define CK_VA_INTC_NEN63_32 (void *)(intc_reg + 0x28) /* Normal interrupt enable register High */
+#define CK_VA_INTC_IFR31_00 (void *)(intc_reg + 0x08) /* Normal interrupt force register Low */
+#define CK_VA_INTC_IFR63_32 (void *)(intc_reg + 0x20) /* Normal interrupt force register High */
+#define CK_VA_INTC_SOURCE (void *)(intc_reg + 0x40) /* Proiority Level Select Registers 0 */
+
+static void ck_irq_mask(struct irq_data *d)
+{
+ unsigned int temp, irq;
+
+ irq = d->irq;
+
+ if (irq < 32) {
+ temp = __raw_readl(CK_VA_INTC_NEN31_00);
+ temp &= ~(1 << irq);
+ __raw_writel(temp, CK_VA_INTC_NEN31_00);
+ } else {
+ temp = __raw_readl(CK_VA_INTC_NEN63_32);
+ temp &= ~(1 << (irq -32));
+ __raw_writel(temp, CK_VA_INTC_NEN63_32);
+ }
+}
+
+static void ck_irq_unmask(struct irq_data *d)
+{
+ unsigned int temp, irq;
+
+ irq = d->irq;
+
+ /* set IFR to support rising edge triggering */
+ if (irq < 32) {
+ temp = __raw_readl(CK_VA_INTC_IFR31_00);
+ temp &= ~(1 << irq);
+ __raw_writel(temp, CK_VA_INTC_IFR31_00);
+ } else {
+ temp = __raw_readl(CK_VA_INTC_IFR63_32);
+ temp &= ~(1 << (irq -32));
+ __raw_writel(temp, CK_VA_INTC_IFR63_32);
+ }
+
+ if (irq < 32) {
+ temp = __raw_readl(CK_VA_INTC_NEN31_00);
+ temp |= 1 << irq;
+ __raw_writel(temp, CK_VA_INTC_NEN31_00);
+ } else {
+ temp = __raw_readl(CK_VA_INTC_NEN63_32);
+ temp |= 1 << (irq -32);
+ __raw_writel(temp, CK_VA_INTC_NEN63_32);
+ }
+}
+
+static struct irq_chip ck_irq_chip = {
+ .name = "csky_intc_v1",
+ .irq_mask = ck_irq_mask,
+ .irq_unmask = ck_irq_unmask,
+};
+
+static int ck_irq_map(struct irq_domain *h, unsigned int virq,
+ irq_hw_number_t hw_irq_num)
+{
+ irq_set_chip_and_handler(virq, &ck_irq_chip, handle_level_irq);
+ return 0;
+}
+
+static const struct irq_domain_ops ck_irq_ops = {
+ .map = ck_irq_map,
+ .xlate = irq_domain_xlate_onecell,
+};
+
+static unsigned int ck_get_irqno(void)
+{
+ unsigned int temp;
+ temp = __raw_readl(CK_VA_INTC_ISR);
+ return temp & 0x3f;
+};
+
+static int __init
+__intc_init(struct device_node *np, struct device_node *parent, bool ave)
+{
+ struct irq_domain *root_domain;
+ int i;
+
+ csky_get_auto_irqno = ck_get_irqno;
+
+ if (parent)
+ panic("pic not a root intc\n");
+
+ intc_reg = (unsigned int)of_iomap(np, 0);
+ if (!intc_reg)
+ panic("%s, of_iomap err.\n", __func__);
+
+ __raw_writel(0, CK_VA_INTC_NEN31_00);
+ __raw_writel(0, CK_VA_INTC_NEN63_32);
+
+ if (ave == true)
+ __raw_writel( 0xc0000000, CK_VA_INTC_ICR);
+ else
+ __raw_writel( 0x0, CK_VA_INTC_ICR);
+ /*
+ * csky irq ctrl has 64 sources.
+ */
+ #define INTC_IRQS 64
+ for (i=0; i<INTC_IRQS; i=i+4)
+ __raw_writel((i+3)|((i+2)<<8)|((i+1)<<16)|(i<<24),
+ CK_VA_INTC_SOURCE + i);
+
+ root_domain = irq_domain_add_legacy(np, INTC_IRQS, 0, 0, &ck_irq_ops, NULL);
+ if (!root_domain)
+ panic("root irq domain not available\n");
+
+ irq_set_default_host(root_domain);
+
+ return 0;
+}
+
+static int __init
+intc_init(struct device_node *np, struct device_node *parent)
+{
+
+ return __intc_init(np, parent, false);
+}
+IRQCHIP_DECLARE(csky_intc_v1, "csky,intc-v1", intc_init);
+
+/*
+ * use auto vector exceptions 10 for interrupt.
+ */
+static int __init
+intc_init_ave(struct device_node *np, struct device_node *parent)
+{
+ return __intc_init(np, parent, true);
+}
+IRQCHIP_DECLARE(csky_intc_v1_ave, "csky,intc-v1,ave", intc_init_ave);
+
diff --git a/drivers/irqchip/irq-nationalchip.c b/drivers/irqchip/irq-nationalchip.c
new file mode 100644
index 0000000..8c09ebd
--- /dev/null
+++ b/drivers/irqchip/irq-nationalchip.c
@@ -0,0 +1,196 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2018 Hangzhou NationalChip Science & Technology Co.,Ltd.
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/module.h>
+#include <linux/irqdomain.h>
+#include <linux/irqchip.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+
+#define NC_VA_INTC_NINT31_00 (void *)(intc_reg + 0x00)
+#define NC_VA_INTC_NINT63_32 (void *)(intc_reg + 0x04)
+#define NC_VA_INTC_NPEND31_00 (void *)(intc_reg + 0x10)
+#define NC_VA_INTC_NPEND63_32 (void *)(intc_reg + 0x14)
+#define NC_VA_INTC_NENSET31_00 (void *)(intc_reg + 0x20)
+#define NC_VA_INTC_NENSET63_32 (void *)(intc_reg + 0x24)
+#define NC_VA_INTC_NENCLR31_00 (void *)(intc_reg + 0x30)
+#define NC_VA_INTC_NENCLR63_32 (void *)(intc_reg + 0x34)
+#define NC_VA_INTC_NEN31_00 (void *)(intc_reg + 0x40)
+#define NC_VA_INTC_NEN63_32 (void *)(intc_reg + 0x44)
+#define NC_VA_INTC_NMASK31_00 (void *)(intc_reg + 0x50)
+#define NC_VA_INTC_NMASK63_32 (void *)(intc_reg + 0x54)
+#define NC_VA_INTC_SOURCE (void *)(intc_reg + 0x60)
+
+static unsigned int intc_reg;
+
+static void nc_irq_mask(struct irq_data *d)
+{
+ unsigned int mask, irq;
+
+ irq = d->irq;
+
+ if (irq < 32) {
+ mask = __raw_readl(NC_VA_INTC_NMASK31_00);
+ mask |= 1 << irq;
+ __raw_writel(mask, NC_VA_INTC_NMASK31_00);
+ } else {
+ mask = __raw_readl(NC_VA_INTC_NMASK63_32);
+ mask |= 1 << (irq - 32);
+ __raw_writel(mask, NC_VA_INTC_NMASK63_32);
+ }
+}
+
+static void nc_irq_unmask(struct irq_data *d)
+{
+ unsigned int mask, irq;
+
+ irq = d->irq;
+
+ if (irq < 32) {
+ mask = __raw_readl(NC_VA_INTC_NMASK31_00);
+ mask &= ~( 1 << irq);
+ __raw_writel(mask, NC_VA_INTC_NMASK31_00);
+ } else {
+ mask = __raw_readl( NC_VA_INTC_NMASK63_32);
+ mask &= ~(1 << (irq - 32));
+ __raw_writel(mask, NC_VA_INTC_NMASK63_32);
+ }
+}
+
+static void nc_irq_en(struct irq_data *d)
+{
+ unsigned int mask, irq;
+
+ irq = d->irq;
+
+ if (irq < 32) {
+ mask = 1 << irq;
+ __raw_writel(mask, NC_VA_INTC_NENSET31_00);
+ } else {
+ mask = 1 << (irq - 32);
+ __raw_writel(mask, NC_VA_INTC_NENSET63_32);
+ }
+
+ nc_irq_unmask(d);
+}
+
+static void nc_irq_dis(struct irq_data *d)
+{
+ unsigned int mask, irq;
+
+ irq = d->irq;
+
+ if (irq < 32) {
+ mask = 1 << irq;
+ __raw_writel(mask, NC_VA_INTC_NENCLR31_00);
+ } else {
+ mask = 1 << (irq - 32);
+ __raw_writel(mask, NC_VA_INTC_NENCLR63_32);
+ }
+
+ nc_irq_mask(d);
+}
+
+struct irq_chip nc_irq_chip = {
+ .name = "nationalchip_intc_v1",
+ .irq_mask = nc_irq_mask,
+ .irq_unmask = nc_irq_unmask,
+ .irq_enable = nc_irq_en,
+ .irq_disable = nc_irq_dis,
+};
+
+inline int ff1_64(unsigned int hi, unsigned int lo)
+{
+ int result;
+ asm volatile(
+ "ff1 %0\n"
+ :"=r"(hi)
+ :"r"(hi)
+ :
+ );
+
+ asm volatile(
+ "ff1 %0\n"
+ :"=r"(lo)
+ :"r"(lo)
+ :
+ );
+ if( lo != 32 )
+ result = 31-lo;
+ else if( hi != 32 ) result = 31-hi + 32;
+ else {
+ printk("nc_get_irqno error hi:%x, lo:%x.\n", hi, lo);
+ result = NR_IRQS;
+ }
+ return result;
+}
+
+unsigned int nc_get_irqno(void)
+{
+ unsigned int nint64hi, nint64lo, irq_no;
+
+ nint64lo = __raw_readl(NC_VA_INTC_NINT31_00);
+ nint64hi = __raw_readl(NC_VA_INTC_NINT63_32);
+ irq_no = ff1_64(nint64hi, nint64lo);
+
+ return irq_no;
+}
+
+static int irq_map(struct irq_domain *h, unsigned int virq,
+ irq_hw_number_t hw_irq_num)
+{
+ irq_set_chip_and_handler(virq, &nc_irq_chip, handle_level_irq);
+
+ return 0;
+}
+
+static const struct irq_domain_ops nc_irq_ops = {
+ .map = irq_map,
+ .xlate = irq_domain_xlate_onecell,
+};
+
+static int __init
+intc_init(struct device_node *intc, struct device_node *parent)
+{
+ struct irq_domain *root_domain;
+ unsigned int i;
+
+ if (parent)
+ panic("DeviceTree incore intc not a root irq controller\n");
+
+ csky_get_auto_irqno = nc_get_irqno;
+
+ intc_reg = (unsigned int) of_iomap(intc, 0);
+ if (!intc_reg)
+ panic("Nationalchip Intc Reg: %x.\n", intc_reg);
+
+ __raw_writel(0xffffffff, NC_VA_INTC_NENCLR31_00);
+ __raw_writel(0xffffffff, NC_VA_INTC_NENCLR63_32);
+ __raw_writel(0xffffffff, NC_VA_INTC_NMASK31_00);
+ __raw_writel(0xffffffff, NC_VA_INTC_NMASK63_32);
+
+ /*
+ * nationalchip irq ctrl has 64 sources.
+ */
+ #define INTC_IRQS 64
+ for (i=0; i<INTC_IRQS; i=i+4)
+ __raw_writel(i|((i+1)<<8)|((i+2)<<16)|((i+3)<<24),
+ NC_VA_INTC_SOURCE + i);
+
+ root_domain = irq_domain_add_legacy(intc, INTC_IRQS, 0, 0,
+ &nc_irq_ops, NULL);
+ if (!root_domain)
+ panic("root irq domain not avail\n");
+
+ irq_set_default_host(root_domain);
+
+ return 0;
+}
+
+IRQCHIP_DECLARE(nationalchip_intc_v1_ave, "nationalchip,intc-v1,ave", intc_init);
+
--
2.7.4