summaryrefslogtreecommitdiffstats
path: root/drivers/irqchip
diff options
context:
space:
mode:
authorThomas Gleixner <tglx@linutronix.de>2016-02-21 14:54:18 -0500
committerThomas Gleixner <tglx@linutronix.de>2016-02-21 14:54:18 -0500
commit16aba533fb1394634bf27dbaa930d8f2098d982f (patch)
tree23b821964a9fd878013c78ee84656bf49f8ee88b /drivers/irqchip
parentfa00cb265e45f62353645c98d67460de64e56aff (diff)
parent1ad9a57633e407f40a288ed1d8660fe06446a002 (diff)
Merge tag 'irqchip-core-4.6-2' of git://git.infradead.org/users/jcooper/linux into irq/core
Pull the second round of irqchip core changes for v4.6 from Jason Cooper: - mvebu: - Add odmi driver for Marvell 7K/8K SoCs - Replace driver-specific set_affinity with generic version - mips: - Move ath79 MISC and CPU drivers from arch/ code to irqchip/ - tango: - Add support for Sigma Designs SMP8[67]xx ctrl
Diffstat (limited to 'drivers/irqchip')
-rw-r--r--drivers/irqchip/Kconfig9
-rw-r--r--drivers/irqchip/Makefile4
-rw-r--r--drivers/irqchip/irq-ath79-cpu.c97
-rw-r--r--drivers/irqchip/irq-ath79-misc.c189
-rw-r--r--drivers/irqchip/irq-gic-v2m.c14
-rw-r--r--drivers/irqchip/irq-gic.c2
-rw-r--r--drivers/irqchip/irq-mvebu-odmi.c236
-rw-r--r--drivers/irqchip/irq-tango.c232
8 files changed, 769 insertions, 14 deletions
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 0bd819c6c911..0ffbdfdc4170 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -161,6 +161,11 @@ config ST_IRQCHIP
161 help 161 help
162 Enables SysCfg Controlled IRQs on STi based platforms. 162 Enables SysCfg Controlled IRQs on STi based platforms.
163 163
164config TANGO_IRQ
165 bool
166 select IRQ_DOMAIN
167 select GENERIC_IRQ_CHIP
168
164config TB10X_IRQC 169config TB10X_IRQC
165 bool 170 bool
166 select IRQ_DOMAIN 171 select IRQ_DOMAIN
@@ -229,3 +234,7 @@ config IRQ_MXS
229 def_bool y if MACH_ASM9260 || ARCH_MXS 234 def_bool y if MACH_ASM9260 || ARCH_MXS
230 select IRQ_DOMAIN 235 select IRQ_DOMAIN
231 select STMP_DEVICE 236 select STMP_DEVICE
237
238config MVEBU_ODMI
239 bool
240 select GENERIC_MSI_IRQ_DOMAIN
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index ba0295d16a46..8698710fda37 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -1,5 +1,7 @@
1obj-$(CONFIG_IRQCHIP) += irqchip.o 1obj-$(CONFIG_IRQCHIP) += irqchip.o
2 2
3obj-$(CONFIG_ATH79) += irq-ath79-cpu.o
4obj-$(CONFIG_ATH79) += irq-ath79-misc.o
3obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2835.o 5obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2835.o
4obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2836.o 6obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2836.o
5obj-$(CONFIG_ARCH_EXYNOS) += exynos-combiner.o 7obj-$(CONFIG_ARCH_EXYNOS) += exynos-combiner.o
@@ -40,6 +42,7 @@ obj-$(CONFIG_VERSATILE_FPGA_IRQ) += irq-versatile-fpga.o
40obj-$(CONFIG_ARCH_NSPIRE) += irq-zevio.o 42obj-$(CONFIG_ARCH_NSPIRE) += irq-zevio.o
41obj-$(CONFIG_ARCH_VT8500) += irq-vt8500.o 43obj-$(CONFIG_ARCH_VT8500) += irq-vt8500.o
42obj-$(CONFIG_ST_IRQCHIP) += irq-st.o 44obj-$(CONFIG_ST_IRQCHIP) += irq-st.o
45obj-$(CONFIG_TANGO_IRQ) += irq-tango.o
43obj-$(CONFIG_TB10X_IRQC) += irq-tb10x.o 46obj-$(CONFIG_TB10X_IRQC) += irq-tb10x.o
44obj-$(CONFIG_TS4800_IRQ) += irq-ts4800.o 47obj-$(CONFIG_TS4800_IRQ) += irq-ts4800.o
45obj-$(CONFIG_XTENSA) += irq-xtensa-pic.o 48obj-$(CONFIG_XTENSA) += irq-xtensa-pic.o
@@ -60,3 +63,4 @@ obj-$(CONFIG_ARCH_SA1100) += irq-sa11x0.o
60obj-$(CONFIG_INGENIC_IRQ) += irq-ingenic.o 63obj-$(CONFIG_INGENIC_IRQ) += irq-ingenic.o
61obj-$(CONFIG_IMX_GPCV2) += irq-imx-gpcv2.o 64obj-$(CONFIG_IMX_GPCV2) += irq-imx-gpcv2.o
62obj-$(CONFIG_PIC32_EVIC) += irq-pic32-evic.o 65obj-$(CONFIG_PIC32_EVIC) += irq-pic32-evic.o
66obj-$(CONFIG_MVEBU_ODMI) += irq-mvebu-odmi.o
diff --git a/drivers/irqchip/irq-ath79-cpu.c b/drivers/irqchip/irq-ath79-cpu.c
new file mode 100644
index 000000000000..befe93c5a51a
--- /dev/null
+++ b/drivers/irqchip/irq-ath79-cpu.c
@@ -0,0 +1,97 @@
1/*
2 * Atheros AR71xx/AR724x/AR913x specific interrupt handling
3 *
4 * Copyright (C) 2015 Alban Bedel <albeu@free.fr>
5 * Copyright (C) 2010-2011 Jaiganesh Narayanan <jnarayanan@atheros.com>
6 * Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org>
7 * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
8 *
9 * Parts of this file are based on Atheros' 2.6.15/2.6.31 BSP
10 *
11 * This program is free software; you can redistribute it and/or modify it
12 * under the terms of the GNU General Public License version 2 as published
13 * by the Free Software Foundation.
14 */
15
16#include <linux/interrupt.h>
17#include <linux/irqchip.h>
18#include <linux/of.h>
19
20#include <asm/irq_cpu.h>
21#include <asm/mach-ath79/ath79.h>
22
23/*
24 * The IP2/IP3 lines are tied to a PCI/WMAC/USB device. Drivers for
25 * these devices typically allocate coherent DMA memory, however the
26 * DMA controller may still have some unsynchronized data in the FIFO.
27 * Issue a flush in the handlers to ensure that the driver sees
28 * the update.
29 *
30 * This array map the interrupt lines to the DDR write buffer channels.
31 */
32
33static unsigned irq_wb_chan[8] = {
34 -1, -1, -1, -1, -1, -1, -1, -1,
35};
36
37asmlinkage void plat_irq_dispatch(void)
38{
39 unsigned long pending;
40 int irq;
41
42 pending = read_c0_status() & read_c0_cause() & ST0_IM;
43
44 if (!pending) {
45 spurious_interrupt();
46 return;
47 }
48
49 pending >>= CAUSEB_IP;
50 while (pending) {
51 irq = fls(pending) - 1;
52 if (irq < ARRAY_SIZE(irq_wb_chan) && irq_wb_chan[irq] != -1)
53 ath79_ddr_wb_flush(irq_wb_chan[irq]);
54 do_IRQ(MIPS_CPU_IRQ_BASE + irq);
55 pending &= ~BIT(irq);
56 }
57}
58
59static int __init ar79_cpu_intc_of_init(
60 struct device_node *node, struct device_node *parent)
61{
62 int err, i, count;
63
64 /* Fill the irq_wb_chan table */
65 count = of_count_phandle_with_args(
66 node, "qca,ddr-wb-channels", "#qca,ddr-wb-channel-cells");
67
68 for (i = 0; i < count; i++) {
69 struct of_phandle_args args;
70 u32 irq = i;
71
72 of_property_read_u32_index(
73 node, "qca,ddr-wb-channel-interrupts", i, &irq);
74 if (irq >= ARRAY_SIZE(irq_wb_chan))
75 continue;
76
77 err = of_parse_phandle_with_args(
78 node, "qca,ddr-wb-channels",
79 "#qca,ddr-wb-channel-cells",
80 i, &args);
81 if (err)
82 return err;
83
84 irq_wb_chan[irq] = args.args[0];
85 }
86
87 return mips_cpu_irq_of_init(node, parent);
88}
89IRQCHIP_DECLARE(ar79_cpu_intc, "qca,ar7100-cpu-intc",
90 ar79_cpu_intc_of_init);
91
92void __init ath79_cpu_irq_init(unsigned irq_wb_chan2, unsigned irq_wb_chan3)
93{
94 irq_wb_chan[2] = irq_wb_chan2;
95 irq_wb_chan[3] = irq_wb_chan3;
96 mips_cpu_irq_init();
97}
diff --git a/drivers/irqchip/irq-ath79-misc.c b/drivers/irqchip/irq-ath79-misc.c
new file mode 100644
index 000000000000..aa7290784636
--- /dev/null
+++ b/drivers/irqchip/irq-ath79-misc.c
@@ -0,0 +1,189 @@
1/*
2 * Atheros AR71xx/AR724x/AR913x MISC interrupt controller
3 *
4 * Copyright (C) 2015 Alban Bedel <albeu@free.fr>
5 * Copyright (C) 2010-2011 Jaiganesh Narayanan <jnarayanan@atheros.com>
6 * Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org>
7 * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
8 *
9 * Parts of this file are based on Atheros' 2.6.15/2.6.31 BSP
10 *
11 * This program is free software; you can redistribute it and/or modify it
12 * under the terms of the GNU General Public License version 2 as published
13 * by the Free Software Foundation.
14 */
15
16#include <linux/irqchip.h>
17#include <linux/irqchip/chained_irq.h>
18#include <linux/of_address.h>
19#include <linux/of_irq.h>
20
21#define AR71XX_RESET_REG_MISC_INT_STATUS 0
22#define AR71XX_RESET_REG_MISC_INT_ENABLE 4
23
24#define ATH79_MISC_IRQ_COUNT 32
25
26static void ath79_misc_irq_handler(struct irq_desc *desc)
27{
28 struct irq_domain *domain = irq_desc_get_handler_data(desc);
29 struct irq_chip *chip = irq_desc_get_chip(desc);
30 void __iomem *base = domain->host_data;
31 u32 pending;
32
33 chained_irq_enter(chip, desc);
34
35 pending = __raw_readl(base + AR71XX_RESET_REG_MISC_INT_STATUS) &
36 __raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE);
37
38 if (!pending) {
39 spurious_interrupt();
40 chained_irq_exit(chip, desc);
41 return;
42 }
43
44 while (pending) {
45 int bit = __ffs(pending);
46
47 generic_handle_irq(irq_linear_revmap(domain, bit));
48 pending &= ~BIT(bit);
49 }
50
51 chained_irq_exit(chip, desc);
52}
53
54static void ar71xx_misc_irq_unmask(struct irq_data *d)
55{
56 void __iomem *base = irq_data_get_irq_chip_data(d);
57 unsigned int irq = d->hwirq;
58 u32 t;
59
60 t = __raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE);
61 __raw_writel(t | BIT(irq), base + AR71XX_RESET_REG_MISC_INT_ENABLE);
62
63 /* flush write */
64 __raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE);
65}
66
67static void ar71xx_misc_irq_mask(struct irq_data *d)
68{
69 void __iomem *base = irq_data_get_irq_chip_data(d);
70 unsigned int irq = d->hwirq;
71 u32 t;
72
73 t = __raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE);
74 __raw_writel(t & ~BIT(irq), base + AR71XX_RESET_REG_MISC_INT_ENABLE);
75
76 /* flush write */
77 __raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE);
78}
79
80static void ar724x_misc_irq_ack(struct irq_data *d)
81{
82 void __iomem *base = irq_data_get_irq_chip_data(d);
83 unsigned int irq = d->hwirq;
84 u32 t;
85
86 t = __raw_readl(base + AR71XX_RESET_REG_MISC_INT_STATUS);
87 __raw_writel(t & ~BIT(irq), base + AR71XX_RESET_REG_MISC_INT_STATUS);
88
89 /* flush write */
90 __raw_readl(base + AR71XX_RESET_REG_MISC_INT_STATUS);
91}
92
93static struct irq_chip ath79_misc_irq_chip = {
94 .name = "MISC",
95 .irq_unmask = ar71xx_misc_irq_unmask,
96 .irq_mask = ar71xx_misc_irq_mask,
97};
98
99static int misc_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw)
100{
101 irq_set_chip_and_handler(irq, &ath79_misc_irq_chip, handle_level_irq);
102 irq_set_chip_data(irq, d->host_data);
103 return 0;
104}
105
106static const struct irq_domain_ops misc_irq_domain_ops = {
107 .xlate = irq_domain_xlate_onecell,
108 .map = misc_map,
109};
110
111static void __init ath79_misc_intc_domain_init(
112 struct irq_domain *domain, int irq)
113{
114 void __iomem *base = domain->host_data;
115
116 /* Disable and clear all interrupts */
117 __raw_writel(0, base + AR71XX_RESET_REG_MISC_INT_ENABLE);
118 __raw_writel(0, base + AR71XX_RESET_REG_MISC_INT_STATUS);
119
120 irq_set_chained_handler_and_data(irq, ath79_misc_irq_handler, domain);
121}
122
123static int __init ath79_misc_intc_of_init(
124 struct device_node *node, struct device_node *parent)
125{
126 struct irq_domain *domain;
127 void __iomem *base;
128 int irq;
129
130 irq = irq_of_parse_and_map(node, 0);
131 if (!irq) {
132 pr_err("Failed to get MISC IRQ\n");
133 return -EINVAL;
134 }
135
136 base = of_iomap(node, 0);
137 if (!base) {
138 pr_err("Failed to get MISC IRQ registers\n");
139 return -ENOMEM;
140 }
141
142 domain = irq_domain_add_linear(node, ATH79_MISC_IRQ_COUNT,
143 &misc_irq_domain_ops, base);
144 if (!domain) {
145 pr_err("Failed to add MISC irqdomain\n");
146 return -EINVAL;
147 }
148
149 ath79_misc_intc_domain_init(domain, irq);
150 return 0;
151}
152
153static int __init ar7100_misc_intc_of_init(
154 struct device_node *node, struct device_node *parent)
155{
156 ath79_misc_irq_chip.irq_mask_ack = ar71xx_misc_irq_mask;
157 return ath79_misc_intc_of_init(node, parent);
158}
159
160IRQCHIP_DECLARE(ar7100_misc_intc, "qca,ar7100-misc-intc",
161 ar7100_misc_intc_of_init);
162
163static int __init ar7240_misc_intc_of_init(
164 struct device_node *node, struct device_node *parent)
165{
166 ath79_misc_irq_chip.irq_ack = ar724x_misc_irq_ack;
167 return ath79_misc_intc_of_init(node, parent);
168}
169
170IRQCHIP_DECLARE(ar7240_misc_intc, "qca,ar7240-misc-intc",
171 ar7240_misc_intc_of_init);
172
173void __init ath79_misc_irq_init(void __iomem *regs, int irq,
174 int irq_base, bool is_ar71xx)
175{
176 struct irq_domain *domain;
177
178 if (is_ar71xx)
179 ath79_misc_irq_chip.irq_mask_ack = ar71xx_misc_irq_mask;
180 else
181 ath79_misc_irq_chip.irq_ack = ar724x_misc_irq_ack;
182
183 domain = irq_domain_add_legacy(NULL, ATH79_MISC_IRQ_COUNT,
184 irq_base, 0, &misc_irq_domain_ops, regs);
185 if (!domain)
186 panic("Failed to create MISC irqdomain");
187
188 ath79_misc_intc_domain_init(domain, irq);
189}
diff --git a/drivers/irqchip/irq-gic-v2m.c b/drivers/irqchip/irq-gic-v2m.c
index c779f83e511d..28f047c61baa 100644
--- a/drivers/irqchip/irq-gic-v2m.c
+++ b/drivers/irqchip/irq-gic-v2m.c
@@ -92,18 +92,6 @@ static struct msi_domain_info gicv2m_msi_domain_info = {
92 .chip = &gicv2m_msi_irq_chip, 92 .chip = &gicv2m_msi_irq_chip,
93}; 93};
94 94
95static int gicv2m_set_affinity(struct irq_data *irq_data,
96 const struct cpumask *mask, bool force)
97{
98 int ret;
99
100 ret = irq_chip_set_affinity_parent(irq_data, mask, force);
101 if (ret == IRQ_SET_MASK_OK)
102 ret = IRQ_SET_MASK_OK_DONE;
103
104 return ret;
105}
106
107static void gicv2m_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) 95static void gicv2m_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
108{ 96{
109 struct v2m_data *v2m = irq_data_get_irq_chip_data(data); 97 struct v2m_data *v2m = irq_data_get_irq_chip_data(data);
@@ -122,7 +110,7 @@ static struct irq_chip gicv2m_irq_chip = {
122 .irq_mask = irq_chip_mask_parent, 110 .irq_mask = irq_chip_mask_parent,
123 .irq_unmask = irq_chip_unmask_parent, 111 .irq_unmask = irq_chip_unmask_parent,
124 .irq_eoi = irq_chip_eoi_parent, 112 .irq_eoi = irq_chip_eoi_parent,
125 .irq_set_affinity = gicv2m_set_affinity, 113 .irq_set_affinity = irq_chip_set_affinity_parent,
126 .irq_compose_msi_msg = gicv2m_compose_msi_msg, 114 .irq_compose_msi_msg = gicv2m_compose_msi_msg,
127}; 115};
128 116
diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c
index 911758c056c1..e14f2f2a7263 100644
--- a/drivers/irqchip/irq-gic.c
+++ b/drivers/irqchip/irq-gic.c
@@ -319,7 +319,7 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val,
319 writel_relaxed(val | bit, reg); 319 writel_relaxed(val | bit, reg);
320 raw_spin_unlock_irqrestore(&irq_controller_lock, flags); 320 raw_spin_unlock_irqrestore(&irq_controller_lock, flags);
321 321
322 return IRQ_SET_MASK_OK; 322 return IRQ_SET_MASK_OK_DONE;
323} 323}
324#endif 324#endif
325 325
diff --git a/drivers/irqchip/irq-mvebu-odmi.c b/drivers/irqchip/irq-mvebu-odmi.c
new file mode 100644
index 000000000000..b4d367868dbb
--- /dev/null
+++ b/drivers/irqchip/irq-mvebu-odmi.c
@@ -0,0 +1,236 @@
1/*
2 * Copyright (C) 2016 Marvell
3 *
4 * Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
5 *
6 * This file is licensed under the terms of the GNU General Public
7 * License version 2. This program is licensed "as is" without any
8 * warranty of any kind, whether express or implied.
9 */
10
11#define pr_fmt(fmt) "GIC-ODMI: " fmt
12
13#include <linux/irq.h>
14#include <linux/irqchip.h>
15#include <linux/irqdomain.h>
16#include <linux/kernel.h>
17#include <linux/msi.h>
18#include <linux/of_address.h>
19#include <linux/slab.h>
20#include <dt-bindings/interrupt-controller/arm-gic.h>
21
22#define GICP_ODMIN_SET 0x40
23#define GICP_ODMI_INT_NUM_SHIFT 12
24#define GICP_ODMIN_GM_EP_R0 0x110
25#define GICP_ODMIN_GM_EP_R1 0x114
26#define GICP_ODMIN_GM_EA_R0 0x108
27#define GICP_ODMIN_GM_EA_R1 0x118
28
29/*
30 * We don't support the group events, so we simply have 8 interrupts
31 * per frame.
32 */
33#define NODMIS_SHIFT 3
34#define NODMIS_PER_FRAME (1 << NODMIS_SHIFT)
35#define NODMIS_MASK (NODMIS_PER_FRAME - 1)
36
37struct odmi_data {
38 struct resource res;
39 void __iomem *base;
40 unsigned int spi_base;
41};
42
43static struct odmi_data *odmis;
44static unsigned long *odmis_bm;
45static unsigned int odmis_count;
46
47/* Protects odmis_bm */
48static DEFINE_SPINLOCK(odmis_bm_lock);
49
50static void odmi_compose_msi_msg(struct irq_data *d, struct msi_msg *msg)
51{
52 struct odmi_data *odmi;
53 phys_addr_t addr;
54 unsigned int odmin;
55
56 if (WARN_ON(d->hwirq >= odmis_count * NODMIS_PER_FRAME))
57 return;
58
59 odmi = &odmis[d->hwirq >> NODMIS_SHIFT];
60 odmin = d->hwirq & NODMIS_MASK;
61
62 addr = odmi->res.start + GICP_ODMIN_SET;
63
64 msg->address_hi = upper_32_bits(addr);
65 msg->address_lo = lower_32_bits(addr);
66 msg->data = odmin << GICP_ODMI_INT_NUM_SHIFT;
67}
68
69static struct irq_chip odmi_irq_chip = {
70 .name = "ODMI",
71 .irq_mask = irq_chip_mask_parent,
72 .irq_unmask = irq_chip_unmask_parent,
73 .irq_eoi = irq_chip_eoi_parent,
74 .irq_set_affinity = irq_chip_set_affinity_parent,
75 .irq_compose_msi_msg = odmi_compose_msi_msg,
76};
77
78static int odmi_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
79 unsigned int nr_irqs, void *args)
80{
81 struct odmi_data *odmi = NULL;
82 struct irq_fwspec fwspec;
83 struct irq_data *d;
84 unsigned int hwirq, odmin;
85 int ret;
86
87 spin_lock(&odmis_bm_lock);
88 hwirq = find_first_zero_bit(odmis_bm, NODMIS_PER_FRAME * odmis_count);
89 if (hwirq >= NODMIS_PER_FRAME * odmis_count) {
90 spin_unlock(&odmis_bm_lock);
91 return -ENOSPC;
92 }
93
94 __set_bit(hwirq, odmis_bm);
95 spin_unlock(&odmis_bm_lock);
96
97 odmi = &odmis[hwirq >> NODMIS_SHIFT];
98 odmin = hwirq & NODMIS_MASK;
99
100 fwspec.fwnode = domain->parent->fwnode;
101 fwspec.param_count = 3;
102 fwspec.param[0] = GIC_SPI;
103 fwspec.param[1] = odmi->spi_base - 32 + odmin;
104 fwspec.param[2] = IRQ_TYPE_EDGE_RISING;
105
106 ret = irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec);
107 if (ret) {
108 pr_err("Cannot allocate parent IRQ\n");
109 spin_lock(&odmis_bm_lock);
110 __clear_bit(odmin, odmis_bm);
111 spin_unlock(&odmis_bm_lock);
112 return ret;
113 }
114
115 /* Configure the interrupt line to be edge */
116 d = irq_domain_get_irq_data(domain->parent, virq);
117 d->chip->irq_set_type(d, IRQ_TYPE_EDGE_RISING);
118
119 irq_domain_set_hwirq_and_chip(domain, virq, hwirq,
120 &odmi_irq_chip, NULL);
121
122 return 0;
123}
124
125static void odmi_irq_domain_free(struct irq_domain *domain,
126 unsigned int virq, unsigned int nr_irqs)
127{
128 struct irq_data *d = irq_domain_get_irq_data(domain, virq);
129
130 if (d->hwirq >= odmis_count * NODMIS_PER_FRAME) {
131 pr_err("Failed to teardown msi. Invalid hwirq %lu\n", d->hwirq);
132 return;
133 }
134
135 irq_domain_free_irqs_parent(domain, virq, nr_irqs);
136
137 /* Actually free the MSI */
138 spin_lock(&odmis_bm_lock);
139 __clear_bit(d->hwirq, odmis_bm);
140 spin_unlock(&odmis_bm_lock);
141}
142
143static const struct irq_domain_ops odmi_domain_ops = {
144 .alloc = odmi_irq_domain_alloc,
145 .free = odmi_irq_domain_free,
146};
147
148static struct irq_chip odmi_msi_irq_chip = {
149 .name = "ODMI",
150};
151
152static struct msi_domain_ops odmi_msi_ops = {
153};
154
155static struct msi_domain_info odmi_msi_domain_info = {
156 .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS),
157 .ops = &odmi_msi_ops,
158 .chip = &odmi_msi_irq_chip,
159};
160
161static int __init mvebu_odmi_init(struct device_node *node,
162 struct device_node *parent)
163{
164 struct irq_domain *inner_domain, *plat_domain;
165 int ret, i;
166
167 if (of_property_read_u32(node, "marvell,odmi-frames", &odmis_count))
168 return -EINVAL;
169
170 odmis = kcalloc(odmis_count, sizeof(struct odmi_data), GFP_KERNEL);
171 if (!odmis)
172 return -ENOMEM;
173
174 odmis_bm = kcalloc(BITS_TO_LONGS(odmis_count * NODMIS_PER_FRAME),
175 sizeof(long), GFP_KERNEL);
176 if (!odmis_bm) {
177 ret = -ENOMEM;
178 goto err_alloc;
179 }
180
181 for (i = 0; i < odmis_count; i++) {
182 struct odmi_data *odmi = &odmis[i];
183
184 ret = of_address_to_resource(node, i, &odmi->res);
185 if (ret)
186 goto err_unmap;
187
188 odmi->base = of_io_request_and_map(node, i, "odmi");
189 if (IS_ERR(odmi->base)) {
190 ret = PTR_ERR(odmi->base);
191 goto err_unmap;
192 }
193
194 if (of_property_read_u32_index(node, "marvell,spi-base",
195 i, &odmi->spi_base)) {
196 ret = -EINVAL;
197 goto err_unmap;
198 }
199 }
200
201 inner_domain = irq_domain_create_linear(of_node_to_fwnode(node),
202 odmis_count * NODMIS_PER_FRAME,
203 &odmi_domain_ops, NULL);
204 if (!inner_domain) {
205 ret = -ENOMEM;
206 goto err_unmap;
207 }
208
209 inner_domain->parent = irq_find_host(parent);
210
211 plat_domain = platform_msi_create_irq_domain(of_node_to_fwnode(node),
212 &odmi_msi_domain_info,
213 inner_domain);
214 if (!plat_domain) {
215 ret = -ENOMEM;
216 goto err_remove_inner;
217 }
218
219 return 0;
220
221err_remove_inner:
222 irq_domain_remove(inner_domain);
223err_unmap:
224 for (i = 0; i < odmis_count; i++) {
225 struct odmi_data *odmi = &odmis[i];
226
227 if (odmi->base && !IS_ERR(odmi->base))
228 iounmap(odmis[i].base);
229 }
230 kfree(odmis_bm);
231err_alloc:
232 kfree(odmis);
233 return ret;
234}
235
236IRQCHIP_DECLARE(mvebu_odmi, "marvell,odmi-controller", mvebu_odmi_init);
diff --git a/drivers/irqchip/irq-tango.c b/drivers/irqchip/irq-tango.c
new file mode 100644
index 000000000000..bdbb5c0ff7fe
--- /dev/null
+++ b/drivers/irqchip/irq-tango.c
@@ -0,0 +1,232 @@
1/*
2 * Copyright (C) 2014 Mans Rullgard <mans@mansr.com>
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 */
9
10#include <linux/init.h>
11#include <linux/irq.h>
12#include <linux/irqchip.h>
13#include <linux/irqchip/chained_irq.h>
14#include <linux/ioport.h>
15#include <linux/io.h>
16#include <linux/of_address.h>
17#include <linux/of_irq.h>
18#include <linux/slab.h>
19
20#define IRQ0_CTL_BASE 0x0000
21#define IRQ1_CTL_BASE 0x0100
22#define EDGE_CTL_BASE 0x0200
23#define IRQ2_CTL_BASE 0x0300
24
25#define IRQ_CTL_HI 0x18
26#define EDGE_CTL_HI 0x20
27
28#define IRQ_STATUS 0x00
29#define IRQ_RAWSTAT 0x04
30#define IRQ_EN_SET 0x08
31#define IRQ_EN_CLR 0x0c
32#define IRQ_SOFT_SET 0x10
33#define IRQ_SOFT_CLR 0x14
34
35#define EDGE_STATUS 0x00
36#define EDGE_RAWSTAT 0x04
37#define EDGE_CFG_RISE 0x08
38#define EDGE_CFG_FALL 0x0c
39#define EDGE_CFG_RISE_SET 0x10
40#define EDGE_CFG_RISE_CLR 0x14
41#define EDGE_CFG_FALL_SET 0x18
42#define EDGE_CFG_FALL_CLR 0x1c
43
44struct tangox_irq_chip {
45 void __iomem *base;
46 unsigned long ctl;
47};
48
49static inline u32 intc_readl(struct tangox_irq_chip *chip, int reg)
50{
51 return readl_relaxed(chip->base + reg);
52}
53
54static inline void intc_writel(struct tangox_irq_chip *chip, int reg, u32 val)
55{
56 writel_relaxed(val, chip->base + reg);
57}
58
59static void tangox_dispatch_irqs(struct irq_domain *dom, unsigned int status,
60 int base)
61{
62 unsigned int hwirq;
63 unsigned int virq;
64
65 while (status) {
66 hwirq = __ffs(status);
67 virq = irq_find_mapping(dom, base + hwirq);
68 if (virq)
69 generic_handle_irq(virq);
70 status &= ~BIT(hwirq);
71 }
72}
73
74static void tangox_irq_handler(struct irq_desc *desc)
75{
76 struct irq_domain *dom = irq_desc_get_handler_data(desc);
77 struct irq_chip *host_chip = irq_desc_get_chip(desc);
78 struct tangox_irq_chip *chip = dom->host_data;
79 unsigned int status_lo, status_hi;
80
81 chained_irq_enter(host_chip, desc);
82
83 status_lo = intc_readl(chip, chip->ctl + IRQ_STATUS);
84 status_hi = intc_readl(chip, chip->ctl + IRQ_CTL_HI + IRQ_STATUS);
85
86 tangox_dispatch_irqs(dom, status_lo, 0);
87 tangox_dispatch_irqs(dom, status_hi, 32);
88
89 chained_irq_exit(host_chip, desc);
90}
91
92static int tangox_irq_set_type(struct irq_data *d, unsigned int flow_type)
93{
94 struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
95 struct tangox_irq_chip *chip = gc->domain->host_data;
96 struct irq_chip_regs *regs = &gc->chip_types[0].regs;
97
98 switch (flow_type & IRQ_TYPE_SENSE_MASK) {
99 case IRQ_TYPE_EDGE_RISING:
100 intc_writel(chip, regs->type + EDGE_CFG_RISE_SET, d->mask);
101 intc_writel(chip, regs->type + EDGE_CFG_FALL_CLR, d->mask);
102 break;
103
104 case IRQ_TYPE_EDGE_FALLING:
105 intc_writel(chip, regs->type + EDGE_CFG_RISE_CLR, d->mask);
106 intc_writel(chip, regs->type + EDGE_CFG_FALL_SET, d->mask);
107 break;
108
109 case IRQ_TYPE_LEVEL_HIGH:
110 intc_writel(chip, regs->type + EDGE_CFG_RISE_CLR, d->mask);
111 intc_writel(chip, regs->type + EDGE_CFG_FALL_CLR, d->mask);
112 break;
113
114 case IRQ_TYPE_LEVEL_LOW:
115 intc_writel(chip, regs->type + EDGE_CFG_RISE_SET, d->mask);
116 intc_writel(chip, regs->type + EDGE_CFG_FALL_SET, d->mask);
117 break;
118
119 default:
120 pr_err("Invalid trigger mode %x for IRQ %d\n",
121 flow_type, d->irq);
122 return -EINVAL;
123 }
124
125 return irq_setup_alt_chip(d, flow_type);
126}
127
128static void __init tangox_irq_init_chip(struct irq_chip_generic *gc,
129 unsigned long ctl_offs,
130 unsigned long edge_offs)
131{
132 struct tangox_irq_chip *chip = gc->domain->host_data;
133 struct irq_chip_type *ct = gc->chip_types;
134 unsigned long ctl_base = chip->ctl + ctl_offs;
135 unsigned long edge_base = EDGE_CTL_BASE + edge_offs;
136 int i;
137
138 gc->reg_base = chip->base;
139 gc->unused = 0;
140
141 for (i = 0; i < 2; i++) {
142 ct[i].chip.irq_ack = irq_gc_ack_set_bit;
143 ct[i].chip.irq_mask = irq_gc_mask_disable_reg;
144 ct[i].chip.irq_mask_ack = irq_gc_mask_disable_reg_and_ack;
145 ct[i].chip.irq_unmask = irq_gc_unmask_enable_reg;
146 ct[i].chip.irq_set_type = tangox_irq_set_type;
147 ct[i].chip.name = gc->domain->name;
148
149 ct[i].regs.enable = ctl_base + IRQ_EN_SET;
150 ct[i].regs.disable = ctl_base + IRQ_EN_CLR;
151 ct[i].regs.ack = edge_base + EDGE_RAWSTAT;
152 ct[i].regs.type = edge_base;
153 }
154
155 ct[0].type = IRQ_TYPE_LEVEL_MASK;
156 ct[0].handler = handle_level_irq;
157
158 ct[1].type = IRQ_TYPE_EDGE_BOTH;
159 ct[1].handler = handle_edge_irq;
160
161 intc_writel(chip, ct->regs.disable, 0xffffffff);
162 intc_writel(chip, ct->regs.ack, 0xffffffff);
163}
164
165static void __init tangox_irq_domain_init(struct irq_domain *dom)
166{
167 struct irq_chip_generic *gc;
168 int i;
169
170 for (i = 0; i < 2; i++) {
171 gc = irq_get_domain_generic_chip(dom, i * 32);
172 tangox_irq_init_chip(gc, i * IRQ_CTL_HI, i * EDGE_CTL_HI);
173 }
174}
175
176static int __init tangox_irq_init(void __iomem *base, struct resource *baseres,
177 struct device_node *node)
178{
179 struct tangox_irq_chip *chip;
180 struct irq_domain *dom;
181 struct resource res;
182 int irq;
183 int err;
184
185 irq = irq_of_parse_and_map(node, 0);
186 if (!irq)
187 panic("%s: failed to get IRQ", node->name);
188
189 err = of_address_to_resource(node, 0, &res);
190 if (err)
191 panic("%s: failed to get address", node->name);
192
193 chip = kzalloc(sizeof(*chip), GFP_KERNEL);
194 chip->ctl = res.start - baseres->start;
195 chip->base = base;
196
197 dom = irq_domain_add_linear(node, 64, &irq_generic_chip_ops, chip);
198 if (!dom)
199 panic("%s: failed to create irqdomain", node->name);
200
201 err = irq_alloc_domain_generic_chips(dom, 32, 2, node->name,
202 handle_level_irq, 0, 0, 0);
203 if (err)
204 panic("%s: failed to allocate irqchip", node->name);
205
206 tangox_irq_domain_init(dom);
207
208 irq_set_chained_handler(irq, tangox_irq_handler);
209 irq_set_handler_data(irq, dom);
210
211 return 0;
212}
213
214static int __init tangox_of_irq_init(struct device_node *node,
215 struct device_node *parent)
216{
217 struct device_node *c;
218 struct resource res;
219 void __iomem *base;
220
221 base = of_iomap(node, 0);
222 if (!base)
223 panic("%s: of_iomap failed", node->name);
224
225 of_address_to_resource(node, 0, &res);
226
227 for_each_child_of_node(node, c)
228 tangox_irq_init(base, &res, c);
229
230 return 0;
231}
232IRQCHIP_DECLARE(tangox_intc, "sigma,smp8642-intc", tangox_of_irq_init);