blob: e3043ded89738f08f59f053c5f074aca9f70f4b7 [file] [log] [blame]
Michal Simekeedbdab2009-03-27 14:25:49 +01001/*
Michal Simek968674b2013-08-27 10:48:29 +02002 * Copyright (C) 2007-2013 Michal Simek <monstr@monstr.eu>
3 * Copyright (C) 2012-2013 Xilinx, Inc.
Michal Simekeedbdab2009-03-27 14:25:49 +01004 * Copyright (C) 2007-2009 PetaLogix
5 * Copyright (C) 2006 Atmark Techno, Inc.
6 *
7 * This file is subject to the terms and conditions of the GNU General Public
8 * License. See the file "COPYING" in the main directory of this archive
9 * for more details.
10 */
11
Grant Likely2462bac2012-01-26 14:10:13 -070012#include <linux/irqdomain.h>
Michal Simekeedbdab2009-03-27 14:25:49 +010013#include <linux/irq.h>
Joel Porquetfd4b2672015-07-07 17:13:15 -040014#include <linux/irqchip.h>
Zubair Lutfullah Kakakhel9689c992016-11-14 12:13:49 +000015#include <linux/irqchip/chained_irq.h>
Michal Simekbcff6612013-08-27 10:49:00 +020016#include <linux/of_address.h>
Michal Simekeedbdab2009-03-27 14:25:49 +010017#include <linux/io.h>
Zubair Lutfullah Kakakhel591db742016-11-14 12:13:47 +000018#include <linux/jump_label.h>
John Williams892ee922009-07-29 22:08:40 +100019#include <linux/bug.h>
Zubair Lutfullah Kakakhel9689c992016-11-14 12:13:49 +000020#include <linux/of_irq.h>
Michal Simekeedbdab2009-03-27 14:25:49 +010021
Michal Simekeedbdab2009-03-27 14:25:49 +010022/* No one else should require these constants, so define them locally here. */
23#define ISR 0x00 /* Interrupt Status Register */
24#define IPR 0x04 /* Interrupt Pending Register */
25#define IER 0x08 /* Interrupt Enable Register */
26#define IAR 0x0c /* Interrupt Acknowledge Register */
27#define SIE 0x10 /* Set Interrupt Enable bits */
28#define CIE 0x14 /* Clear Interrupt Enable bits */
29#define IVR 0x18 /* Interrupt Vector Register */
30#define MER 0x1c /* Master Enable Register */
31
32#define MER_ME (1<<0)
33#define MER_HIE (1<<1)
34
Zubair Lutfullah Kakakhel591db742016-11-14 12:13:47 +000035static DEFINE_STATIC_KEY_FALSE(xintc_is_be);
Michal Simek1aa12432014-02-24 14:56:32 +010036
Zubair Lutfullah Kakakhel591db742016-11-14 12:13:47 +000037struct xintc_irq_chip {
38 void __iomem *base;
39 struct irq_domain *root_domain;
40 u32 intr_mask;
41};
42
43static struct xintc_irq_chip *xintc_irqc;
44
45static void xintc_write(int reg, u32 data)
Michal Simek1aa12432014-02-24 14:56:32 +010046{
Zubair Lutfullah Kakakhel591db742016-11-14 12:13:47 +000047 if (static_branch_unlikely(&xintc_is_be))
48 iowrite32be(data, xintc_irqc->base + reg);
49 else
50 iowrite32(data, xintc_irqc->base + reg);
Michal Simek1aa12432014-02-24 14:56:32 +010051}
52
Zubair Lutfullah Kakakhel591db742016-11-14 12:13:47 +000053static unsigned int xintc_read(int reg)
Michal Simek1aa12432014-02-24 14:56:32 +010054{
Zubair Lutfullah Kakakhel591db742016-11-14 12:13:47 +000055 if (static_branch_unlikely(&xintc_is_be))
56 return ioread32be(xintc_irqc->base + reg);
57 else
58 return ioread32(xintc_irqc->base + reg);
Michal Simek1aa12432014-02-24 14:56:32 +010059}
60
Thomas Gleixner6f205a42011-02-06 19:36:30 +000061static void intc_enable_or_unmask(struct irq_data *d)
Michal Simekeedbdab2009-03-27 14:25:49 +010062{
Michal Simek6c7a2672011-12-09 10:45:20 +010063 unsigned long mask = 1 << d->hwirq;
64
Zubair Lutfullah Kakakhela5734de2016-11-14 12:13:46 +000065 pr_debug("irq-xilinx: enable_or_unmask: %ld\n", d->hwirq);
steve@digidescorp.com33d9ff52009-11-17 08:43:39 -060066
67 /* ack level irqs because they can't be acked during
68 * ack function since the handle_level_irq function
69 * acks the irq before calling the interrupt handler
70 */
Thomas Gleixner4adc1922011-03-24 14:52:04 +010071 if (irqd_is_level_type(d))
Zubair Lutfullah Kakakhel591db742016-11-14 12:13:47 +000072 xintc_write(IAR, mask);
Michal Simek7958a682012-11-05 11:51:13 +010073
Zubair Lutfullah Kakakhel591db742016-11-14 12:13:47 +000074 xintc_write(SIE, mask);
Michal Simekeedbdab2009-03-27 14:25:49 +010075}
76
Thomas Gleixner6f205a42011-02-06 19:36:30 +000077static void intc_disable_or_mask(struct irq_data *d)
Michal Simekeedbdab2009-03-27 14:25:49 +010078{
Zubair Lutfullah Kakakhela5734de2016-11-14 12:13:46 +000079 pr_debug("irq-xilinx: disable: %ld\n", d->hwirq);
Zubair Lutfullah Kakakhel591db742016-11-14 12:13:47 +000080 xintc_write(CIE, 1 << d->hwirq);
Michal Simekeedbdab2009-03-27 14:25:49 +010081}
82
Thomas Gleixner6f205a42011-02-06 19:36:30 +000083static void intc_ack(struct irq_data *d)
Michal Simekeedbdab2009-03-27 14:25:49 +010084{
Zubair Lutfullah Kakakhela5734de2016-11-14 12:13:46 +000085 pr_debug("irq-xilinx: ack: %ld\n", d->hwirq);
Zubair Lutfullah Kakakhel591db742016-11-14 12:13:47 +000086 xintc_write(IAR, 1 << d->hwirq);
Michal Simekeedbdab2009-03-27 14:25:49 +010087}
88
Thomas Gleixner6f205a42011-02-06 19:36:30 +000089static void intc_mask_ack(struct irq_data *d)
Michal Simekeedbdab2009-03-27 14:25:49 +010090{
Michal Simek6c7a2672011-12-09 10:45:20 +010091 unsigned long mask = 1 << d->hwirq;
92
Zubair Lutfullah Kakakhela5734de2016-11-14 12:13:46 +000093 pr_debug("irq-xilinx: disable_and_ack: %ld\n", d->hwirq);
Zubair Lutfullah Kakakhel591db742016-11-14 12:13:47 +000094 xintc_write(CIE, mask);
95 xintc_write(IAR, mask);
Michal Simekeedbdab2009-03-27 14:25:49 +010096}
97
Michal Simekeedbdab2009-03-27 14:25:49 +010098static struct irq_chip intc_dev = {
99 .name = "Xilinx INTC",
Thomas Gleixner6f205a42011-02-06 19:36:30 +0000100 .irq_unmask = intc_enable_or_unmask,
101 .irq_mask = intc_disable_or_mask,
102 .irq_ack = intc_ack,
103 .irq_mask_ack = intc_mask_ack,
Michal Simekeedbdab2009-03-27 14:25:49 +0100104};
105
Zubair Lutfullah Kakakhel2120a432016-11-14 12:13:48 +0000106unsigned int xintc_get_irq(void)
Grant Likely2462bac2012-01-26 14:10:13 -0700107{
108 unsigned int hwirq, irq = -1;
109
Zubair Lutfullah Kakakhel591db742016-11-14 12:13:47 +0000110 hwirq = xintc_read(IVR);
Grant Likely2462bac2012-01-26 14:10:13 -0700111 if (hwirq != -1U)
Zubair Lutfullah Kakakhel591db742016-11-14 12:13:47 +0000112 irq = irq_find_mapping(xintc_irqc->root_domain, hwirq);
Grant Likely2462bac2012-01-26 14:10:13 -0700113
Zubair Lutfullah Kakakhela5734de2016-11-14 12:13:46 +0000114 pr_debug("irq-xilinx: hwirq=%d, irq=%d\n", hwirq, irq);
Michal Simekeedbdab2009-03-27 14:25:49 +0100115
116 return irq;
117}
118
Michal Simekc0d997f2012-12-13 17:30:05 +0100119static int xintc_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw)
Grant Likely2462bac2012-01-26 14:10:13 -0700120{
Zubair Lutfullah Kakakhel591db742016-11-14 12:13:47 +0000121 if (xintc_irqc->intr_mask & (1 << hw)) {
Grant Likely2462bac2012-01-26 14:10:13 -0700122 irq_set_chip_and_handler_name(irq, &intc_dev,
123 handle_edge_irq, "edge");
124 irq_clear_status_flags(irq, IRQ_LEVEL);
125 } else {
126 irq_set_chip_and_handler_name(irq, &intc_dev,
127 handle_level_irq, "level");
128 irq_set_status_flags(irq, IRQ_LEVEL);
129 }
130 return 0;
131}
132
133static const struct irq_domain_ops xintc_irq_domain_ops = {
134 .xlate = irq_domain_xlate_onetwocell,
135 .map = xintc_map,
136};
137
Zubair Lutfullah Kakakhel9689c992016-11-14 12:13:49 +0000138static void xil_intc_irq_handler(struct irq_desc *desc)
139{
140 struct irq_chip *chip = irq_desc_get_chip(desc);
141 u32 pending;
142
143 chained_irq_enter(chip, desc);
144 do {
145 pending = xintc_get_irq();
146 if (pending == -1U)
147 break;
148 generic_handle_irq(pending);
149 } while (true);
150 chained_irq_exit(chip, desc);
151}
152
Michal Simek8a9e90a2013-08-27 10:49:00 +0200153static int __init xilinx_intc_of_init(struct device_node *intc,
154 struct device_node *parent)
Michal Simekeedbdab2009-03-27 14:25:49 +0100155{
Zubair Lutfullah Kakakhel591db742016-11-14 12:13:47 +0000156 u32 nr_irq;
Zubair Lutfullah Kakakhel9689c992016-11-14 12:13:49 +0000157 int ret, irq;
Zubair Lutfullah Kakakhel591db742016-11-14 12:13:47 +0000158 struct xintc_irq_chip *irqc;
Michal Simekeedbdab2009-03-27 14:25:49 +0100159
Zubair Lutfullah Kakakhel591db742016-11-14 12:13:47 +0000160 if (xintc_irqc) {
161 pr_err("irq-xilinx: Multiple instances aren't supported\n");
162 return -EINVAL;
163 }
164
165 irqc = kzalloc(sizeof(*irqc), GFP_KERNEL);
166 if (!irqc)
167 return -ENOMEM;
168
169 xintc_irqc = irqc;
170
171 irqc->base = of_iomap(intc, 0);
172 BUG_ON(!irqc->base);
Michal Simekeedbdab2009-03-27 14:25:49 +0100173
Michal Simekbcff6612013-08-27 10:49:00 +0200174 ret = of_property_read_u32(intc, "xlnx,num-intr-inputs", &nr_irq);
175 if (ret < 0) {
Zubair Lutfullah Kakakhela5734de2016-11-14 12:13:46 +0000176 pr_err("irq-xilinx: unable to read xlnx,num-intr-inputs\n");
Zubair Lutfullah Kakakhel591db742016-11-14 12:13:47 +0000177 goto err_alloc;
Michal Simekbcff6612013-08-27 10:49:00 +0200178 }
179
Zubair Lutfullah Kakakhel591db742016-11-14 12:13:47 +0000180 ret = of_property_read_u32(intc, "xlnx,kind-of-intr", &irqc->intr_mask);
Michal Simekbcff6612013-08-27 10:49:00 +0200181 if (ret < 0) {
Zubair Lutfullah Kakakhel8a11da52016-11-14 12:13:50 +0000182 pr_warn("irq-xilinx: unable to read xlnx,kind-of-intr\n");
183 irqc->intr_mask = 0;
Michal Simekbcff6612013-08-27 10:49:00 +0200184 }
185
Zubair Lutfullah Kakakhel591db742016-11-14 12:13:47 +0000186 if (irqc->intr_mask >> nr_irq)
Zubair Lutfullah Kakakhela5734de2016-11-14 12:13:46 +0000187 pr_warn("irq-xilinx: mismatch in kind-of-intr param\n");
Michal Simekeedbdab2009-03-27 14:25:49 +0100188
Rob Herringe81f54c2017-07-18 16:43:10 -0500189 pr_info("irq-xilinx: %pOF: num_irq=%d, edge=0x%x\n",
190 intc, nr_irq, irqc->intr_mask);
Michal Simekeedbdab2009-03-27 14:25:49 +0100191
Michal Simek1aa12432014-02-24 14:56:32 +0100192
Michal Simekeedbdab2009-03-27 14:25:49 +0100193 /*
194 * Disable all external interrupts until they are
195 * explicity requested.
196 */
Zubair Lutfullah Kakakhel591db742016-11-14 12:13:47 +0000197 xintc_write(IER, 0);
Michal Simekeedbdab2009-03-27 14:25:49 +0100198
199 /* Acknowledge any pending interrupts just in case. */
Zubair Lutfullah Kakakhel591db742016-11-14 12:13:47 +0000200 xintc_write(IAR, 0xffffffff);
Michal Simekeedbdab2009-03-27 14:25:49 +0100201
202 /* Turn on the Master Enable. */
Zubair Lutfullah Kakakhel591db742016-11-14 12:13:47 +0000203 xintc_write(MER, MER_HIE | MER_ME);
204 if (!(xintc_read(MER) & (MER_HIE | MER_ME))) {
205 static_branch_enable(&xintc_is_be);
206 xintc_write(MER, MER_HIE | MER_ME);
Michal Simek1aa12432014-02-24 14:56:32 +0100207 }
Michal Simekeedbdab2009-03-27 14:25:49 +0100208
Zubair Lutfullah Kakakhel591db742016-11-14 12:13:47 +0000209 irqc->root_domain = irq_domain_add_linear(intc, nr_irq,
210 &xintc_irq_domain_ops, irqc);
211 if (!irqc->root_domain) {
212 pr_err("irq-xilinx: Unable to create IRQ domain\n");
213 goto err_alloc;
214 }
Dan Christensen7c2c8512013-03-17 04:48:56 -0500215
Zubair Lutfullah Kakakhel9689c992016-11-14 12:13:49 +0000216 if (parent) {
217 irq = irq_of_parse_and_map(intc, 0);
218 if (irq) {
219 irq_set_chained_handler_and_data(irq,
220 xil_intc_irq_handler,
221 irqc);
222 } else {
223 pr_err("irq-xilinx: interrupts property not in DT\n");
224 ret = -EINVAL;
225 goto err_alloc;
226 }
227 } else {
228 irq_set_default_host(irqc->root_domain);
229 }
Michal Simek8a9e90a2013-08-27 10:49:00 +0200230
231 return 0;
Zubair Lutfullah Kakakhel591db742016-11-14 12:13:47 +0000232
233err_alloc:
234 xintc_irqc = NULL;
235 kfree(irqc);
236 return ret;
237
Michal Simekeedbdab2009-03-27 14:25:49 +0100238}
Michal Simek8a9e90a2013-08-27 10:49:00 +0200239
Zubair Lutfullah Kakakhel83282552016-11-14 12:13:51 +0000240IRQCHIP_DECLARE(xilinx_intc_xps, "xlnx,xps-intc-1.00.a", xilinx_intc_of_init);
241IRQCHIP_DECLARE(xilinx_intc_opb, "xlnx,opb-intc-1.00.c", xilinx_intc_of_init);