[PATCH 07/10] ARM: clps711x: Add CLPS711X irqchip driver
Alexander Shiyan
shc_work at mail.ru
Thu Jul 18 14:34:58 EDT 2013
This adds the irqchip driver for Cirrus Logic CLPS711X series SoCs.
Designed primarily for migration CLPS711X subarch for multiplatform & DT,
for this as the "OF" and "not-OF" calls implemented.
Signed-off-by: Alexander Shiyan <shc_work at mail.ru>
---
.../interrupt-controller/cirrus,clps711x-intc.txt | 42 ++++
arch/arm/Kconfig | 2 -
drivers/irqchip/Kconfig | 6 +
drivers/irqchip/Makefile | 1 +
drivers/irqchip/irq-clps711x.c | 277 +++++++++++++++++++++
5 files changed, 326 insertions(+), 2 deletions(-)
create mode 100644 Documentation/devicetree/bindings/interrupt-controller/cirrus,clps711x-intc.txt
create mode 100644 drivers/irqchip/irq-clps711x.c
diff --git a/Documentation/devicetree/bindings/interrupt-controller/cirrus,clps711x-intc.txt b/Documentation/devicetree/bindings/interrupt-controller/cirrus,clps711x-intc.txt
new file mode 100644
index 0000000..26f8983
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/cirrus,clps711x-intc.txt
@@ -0,0 +1,42 @@
+Cirrus Logic CLPS711X Interrupt Controller
+
+Required properties:
+
+- compatible: Should be "cirrus,clps711x-intc"
+- reg: Specifies base physical address of the registers set
+- 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.
+
+The interrupt sources are as follows:
+ID Name Description
+---------------------------
+1: BLINT Battery low (FIQ)
+3: MCINT Media changed (FIQ)
+4: CSINT CODEC sound
+5: EINT1 External 1
+6: EINT2 External 2
+7: EINT3 External 3
+8: TC1OI TC1 under flow
+9: TC2OI TC2 under flow
+10: RTCMI RTC compare match
+11: TINT 64Hz tick
+12: UTXINT1 UART1 transmit FIFO half empty
+13: URXINT1 UART1 receive FIFO half full
+14: UMSINT UART1 modem status changed
+15: SSEOTI SSI1 end of transfer
+16: KBDINT Keyboard
+17: SS2RX SSI2 receive FIFO half or greater full
+18: SS2TX SSI2 transmit FIFO less than half empty
+28: UTXINT2 UART2 transmit FIFO half empty
+29: URXINT2 UART2 receive FIFO half full
+32: DAIINT DAI interface (FIQ)
+
+Example:
+
+intc: interrupt-controller {
+ compatible = "cirrus,clps711x-intc";
+ reg = <0x80000000 0>;
+ interrupt-controller;
+ #interrupt-cells = <1>;
+};
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 5c60cb7..6f0d238 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -373,8 +373,6 @@ config ARCH_CLPS711X
select COMMON_CLK
select CPU_ARM720T
select MFD_SYSCON
- select MULTI_IRQ_HANDLER
- select SPARSE_IRQ
help
Support for Cirrus Logic 711x/721x/731x based boards.
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 1fea003..c94d00a 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -30,6 +30,12 @@ config ARM_VIC_NR
The maximum number of VICs available in the system, for
power management.
+config CLPS711X_IRQCHIP
+ def_bool y if ARCH_CLPS711X
+ select IRQ_DOMAIN
+ select MULTI_IRQ_HANDLER
+ select SPARSE_IRQ
+
config ORION_IRQCHIP
bool
select IRQ_DOMAIN
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index e65c41a..a80ff5b 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_ARCH_S3C24XX) += irq-s3c24xx.o
obj-$(CONFIG_METAG) += irq-metag-ext.o
obj-$(CONFIG_METAG_PERFCOUNTER_IRQS) += irq-metag.o
obj-$(CONFIG_ARCH_MOXART) += irq-moxart.o
+obj-$(CONFIG_CLPS711X_IRQCHIP) += irq-clps711x.o
obj-$(CONFIG_ORION_IRQCHIP) += irq-orion.o
obj-$(CONFIG_ARCH_SUNXI) += irq-sun4i.o
obj-$(CONFIG_ARCH_SPEAR3XX) += spear-shirq.o
diff --git a/drivers/irqchip/irq-clps711x.c b/drivers/irqchip/irq-clps711x.c
new file mode 100644
index 0000000..819baa9
--- /dev/null
+++ b/drivers/irqchip/irq-clps711x.c
@@ -0,0 +1,277 @@
+/*
+ * CLPS711X IRQ driver
+ *
+ * Copyright (C) 2013 Alexander Shiyan <shc_work at mail.ru>
+ *
+ * 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, or
+ * (at your option) any later version.
+ */
+
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/slab.h>
+
+#include <asm/exception.h>
+#include <asm/mach/irq.h>
+
+#include "irqchip.h"
+
+#define CLPS711X_INTSR1 (0x0240)
+#define CLPS711X_INTMR1 (0x0280)
+#define CLPS711X_BLEOI (0x0600)
+#define CLPS711X_MCEOI (0x0640)
+#define CLPS711X_TEOI (0x0680)
+#define CLPS711X_TC1EOI (0x06c0)
+#define CLPS711X_TC2EOI (0x0700)
+#define CLPS711X_RTCEOI (0x0740)
+#define CLPS711X_UMSEOI (0x0780)
+#define CLPS711X_COEOI (0x07c0)
+#define CLPS711X_INTSR2 (0x1240)
+#define CLPS711X_INTMR2 (0x1280)
+#define CLPS711X_SRXEOF (0x1600)
+#define CLPS711X_KBDEOI (0x1700)
+#define CLPS711X_INTSR3 (0x2240)
+#define CLPS711X_INTMR3 (0x2280)
+
+static const struct {
+#define CLPS711X_FLAG_EN (1 << 0)
+#define CLPS711X_FLAG_FIQ (1 << 1)
+ unsigned int flags;
+ phys_addr_t phys_eoi;
+} clps711x_irqs[] __initconst = {
+ [1] = { CLPS711X_FLAG_FIQ, CLPS711X_BLEOI, },
+ [3] = { CLPS711X_FLAG_FIQ, CLPS711X_MCEOI, },
+ [4] = { CLPS711X_FLAG_EN, CLPS711X_COEOI, },
+ [5] = { CLPS711X_FLAG_EN, },
+ [6] = { CLPS711X_FLAG_EN, },
+ [7] = { CLPS711X_FLAG_EN, },
+ [8] = { CLPS711X_FLAG_EN, CLPS711X_TC1EOI, },
+ [9] = { CLPS711X_FLAG_EN, CLPS711X_TC2EOI, },
+ [10] = { CLPS711X_FLAG_EN, CLPS711X_RTCEOI, },
+ [11] = { CLPS711X_FLAG_EN, CLPS711X_TEOI, },
+ [12] = { CLPS711X_FLAG_EN, },
+ [13] = { CLPS711X_FLAG_EN, },
+ [14] = { CLPS711X_FLAG_EN, CLPS711X_UMSEOI, },
+ [15] = { CLPS711X_FLAG_EN, CLPS711X_SRXEOF, },
+ [16] = { CLPS711X_FLAG_EN, CLPS711X_KBDEOI, },
+ [17] = { CLPS711X_FLAG_EN, },
+ [18] = { CLPS711X_FLAG_EN, },
+ [28] = { CLPS711X_FLAG_EN, },
+ [29] = { CLPS711X_FLAG_EN, },
+ [32] = { CLPS711X_FLAG_FIQ, },
+};
+
+static struct {
+ struct irq_domain *domain;
+ phys_addr_t phys_base;
+ void __iomem *intmr[3];
+ void __iomem *intsr[3];
+ void __iomem *eoi[ARRAY_SIZE(clps711x_irqs)];
+} *clps711x_intc;
+
+
+static inline u32 fls16(u32 x)
+{
+ u32 r = 15;
+
+ if (!(x & 0xff00)) {
+ x <<= 8;
+ r -= 8;
+ }
+ if (!(x & 0xf000)) {
+ x <<= 4;
+ r -= 4;
+ }
+ if (!(x & 0xc000)) {
+ x <<= 2;
+ r -= 2;
+ }
+ if (!(x & 0x8000))
+ r--;
+
+ return r;
+}
+
+static asmlinkage void __exception_irq_entry
+clps711x_handle_irq(struct pt_regs *regs)
+{
+ do {
+ u32 irqnr, irqstat;
+
+ irqstat = readw(clps711x_intc->intmr[0]) &
+ readw(clps711x_intc->intsr[0]);
+ if (irqstat) {
+ irqnr = irq_find_mapping(clps711x_intc->domain,
+ fls16(irqstat));
+ handle_IRQ(irqnr, regs);
+ }
+
+ irqstat = readw(clps711x_intc->intmr[1]) &
+ readw(clps711x_intc->intsr[1]);
+ if (irqstat) {
+ irqnr = irq_find_mapping(clps711x_intc->domain,
+ fls16(irqstat) + 16);
+ handle_IRQ(irqnr, regs);
+ continue;
+ }
+
+ break;
+ } while (1);
+}
+
+static void clps711x_intc_eoi(struct irq_data *d)
+{
+ irq_hw_number_t hwirq = irqd_to_hwirq(d);
+
+ writel(0, clps711x_intc->eoi[hwirq]);
+}
+
+static void clps711x_intc_mask(struct irq_data *d)
+{
+ irq_hw_number_t hwirq = irqd_to_hwirq(d);
+ void __iomem *intmr = clps711x_intc->intmr[hwirq / 16];
+ u32 tmp;
+
+ tmp = readl(intmr);
+ tmp &= ~(1 << (hwirq % 16));
+ writel(tmp, intmr);
+}
+
+static void clps711x_intc_unmask(struct irq_data *d)
+{
+ irq_hw_number_t hwirq = irqd_to_hwirq(d);
+ void __iomem *intmr = clps711x_intc->intmr[hwirq / 16];
+ u32 tmp;
+
+ tmp = readl(intmr);
+ tmp |= 1 << (hwirq % 16);
+ writel(tmp, intmr);
+}
+
+static struct irq_chip clps711x_intc_chip = {
+ .name = "clps711x-intc",
+ .irq_eoi = clps711x_intc_eoi,
+ .irq_mask = clps711x_intc_mask,
+ .irq_unmask = clps711x_intc_unmask,
+};
+
+static void __iomem __init *clps711x_ioremap_one(phys_addr_t reg)
+{
+ void __iomem *ret;
+
+ if (!request_mem_region(clps711x_intc->phys_base + reg, SZ_4, NULL))
+ return ERR_PTR(-EBUSY);
+
+ ret = ioremap(clps711x_intc->phys_base + reg, SZ_4);
+ if (!ret)
+ return ERR_PTR(-ENOMEM);
+
+ return ret;
+}
+
+static int __init clps711x_intc_irq_map(struct irq_domain *h, unsigned int virq,
+ irq_hw_number_t hw)
+{
+ irq_flow_handler_t handler = handle_level_irq;
+ unsigned int flags = IRQF_VALID | IRQF_PROBE;
+
+ if (!clps711x_irqs[hw].flags)
+ return 0;
+
+ if (clps711x_irqs[hw].flags & CLPS711X_FLAG_FIQ) {
+ handler = handle_bad_irq;
+ flags |= IRQF_NOAUTOEN;
+ } else if (clps711x_irqs[hw].phys_eoi) {
+ handler = handle_fasteoi_irq;
+
+ clps711x_intc->eoi[hw] =
+ clps711x_ioremap_one(clps711x_irqs[hw].phys_eoi);
+ if (IS_ERR_OR_NULL(clps711x_intc->eoi[hw]))
+ return PTR_ERR(clps711x_intc->eoi[hw]);
+
+ /* Clear down pending interrupt */
+ writel(0, clps711x_intc->eoi[hw]);
+ }
+
+ irq_set_chip_and_handler(virq, &clps711x_intc_chip, handler);
+ set_irq_flags(virq, flags);
+
+ return 0;
+}
+
+static struct irq_domain_ops clps711x_intc_ops = {
+ .map = clps711x_intc_irq_map,
+ .xlate = irq_domain_xlate_onecell,
+};
+
+static void __init _clps711x_intc_init(phys_addr_t phys_base,
+ struct device_node *np)
+{
+ clps711x_intc = kzalloc(sizeof(*clps711x_intc), GFP_KERNEL);
+ BUG_ON(!clps711x_intc);
+
+ clps711x_intc->phys_base = phys_base;
+
+ clps711x_intc->intsr[0] = clps711x_ioremap_one(CLPS711X_INTSR1);
+ BUG_ON(IS_ERR_OR_NULL(clps711x_intc->intsr[0]));
+
+ clps711x_intc->intmr[0] = clps711x_ioremap_one(CLPS711X_INTMR1);
+ BUG_ON(IS_ERR_OR_NULL(clps711x_intc->intmr[0]));
+
+ clps711x_intc->intsr[1] = clps711x_ioremap_one(CLPS711X_INTSR2);
+ BUG_ON(IS_ERR_OR_NULL(clps711x_intc->intsr[1]));
+
+ clps711x_intc->intmr[1] = clps711x_ioremap_one(CLPS711X_INTMR2);
+ BUG_ON(IS_ERR_OR_NULL(clps711x_intc->intmr[1]));
+
+ clps711x_intc->intsr[2] = clps711x_ioremap_one(CLPS711X_INTSR3);
+ BUG_ON(IS_ERR_OR_NULL(clps711x_intc->intsr[2]));
+
+ clps711x_intc->intmr[2] = clps711x_ioremap_one(CLPS711X_INTMR3);
+ BUG_ON(IS_ERR_OR_NULL(clps711x_intc->intmr[2]));
+
+ /* Disable interrupts */
+ writel(0, clps711x_intc->intmr[0]);
+ writel(0, clps711x_intc->intmr[1]);
+ writel(0, clps711x_intc->intmr[2]);
+
+ BUG_ON(IS_ERR_VALUE(irq_alloc_descs(-1, 0, ARRAY_SIZE(clps711x_irqs),
+ numa_node_id())));
+
+ clps711x_intc->domain =
+ irq_domain_add_legacy(np, ARRAY_SIZE(clps711x_irqs),
+ 0, 0, &clps711x_intc_ops, NULL);
+ BUG_ON(!clps711x_intc->domain);
+
+ irq_set_default_host(clps711x_intc->domain);
+ set_handle_irq(clps711x_handle_irq);
+
+#ifdef CONFIG_FIQ
+ init_FIQ(0);
+#endif
+}
+
+void __init clps711x_intc_init(phys_addr_t phys_base)
+{
+ _clps711x_intc_init(phys_base, NULL);
+}
+
+#ifdef CONFIG_IRQCHIP
+static int __init clps711x_intc_init_dt(struct device_node *np,
+ struct device_node *parent)
+{
+ struct resource res;
+
+ BUG_ON(of_address_to_resource(np, 0, &res));
+
+ _clps711x_intc_init(res.start, np);
+
+ return 0;
+}
+IRQCHIP_DECLARE(clps711x, "cirrus,clps711x-intc", clps711x_intc_init_dt);
+#endif
--
1.8.1.5
More information about the linux-arm-kernel
mailing list