Blame view

drivers/irqchip/irq-versatile-fpga.c 5.83 KB
c41b16f8c   Russell King   ARM: integrator/v...
1
2
3
  /*
   *  Support for Versatile FPGA-based IRQ controllers
   */
3a6ca8c5c   Linus Walleij   ARM: plat-versati...
4
  #include <linux/bitops.h>
c41b16f8c   Russell King   ARM: integrator/v...
5
6
  #include <linux/irq.h>
  #include <linux/io.h>
2389d5014   Linus Walleij   ARM: plat-versati...
7
  #include <linux/irqchip/versatile-fpga.h>
3108e6ab2   Linus Walleij   ARM: 7389/2: plat...
8
9
  #include <linux/irqdomain.h>
  #include <linux/module.h>
9bc150318   Linus Walleij   ARM: 7516/1: plat...
10
11
  #include <linux/of.h>
  #include <linux/of_address.h>
bdd272cbb   Linus Walleij   irqchip: versatil...
12
  #include <linux/of_irq.h>
c41b16f8c   Russell King   ARM: integrator/v...
13

3108e6ab2   Linus Walleij   ARM: 7389/2: plat...
14
  #include <asm/exception.h>
c41b16f8c   Russell King   ARM: integrator/v...
15
  #include <asm/mach/irq.h>
c41b16f8c   Russell King   ARM: integrator/v...
16

2920bc9ab   Rob Herring   irqchip: versatil...
17
  #include "irqchip.h"
c41b16f8c   Russell King   ARM: integrator/v...
18
19
20
21
  #define IRQ_STATUS		0x00
  #define IRQ_RAW_STATUS		0x04
  #define IRQ_ENABLE_SET		0x08
  #define IRQ_ENABLE_CLEAR	0x0c
9bc150318   Linus Walleij   ARM: 7516/1: plat...
22
23
24
25
26
27
28
  #define INT_SOFT_SET		0x10
  #define INT_SOFT_CLEAR		0x14
  #define FIQ_STATUS		0x20
  #define FIQ_RAW_STATUS		0x24
  #define FIQ_ENABLE		0x28
  #define FIQ_ENABLE_SET		0x28
  #define FIQ_ENABLE_CLEAR	0x2C
c41b16f8c   Russell King   ARM: integrator/v...
29

59318461c   Rob Herring   irqchip: versatil...
30
  #define PIC_ENABLES             0x20	/* set interrupt pass through bits */
3108e6ab2   Linus Walleij   ARM: 7389/2: plat...
31
32
33
  /**
   * struct fpga_irq_data - irq data container for the FPGA IRQ controller
   * @base: memory offset in virtual memory
3108e6ab2   Linus Walleij   ARM: 7389/2: plat...
34
35
36
37
38
39
40
   * @chip: chip container for this instance
   * @domain: IRQ domain for this instance
   * @valid: mask for valid IRQs on this controller
   * @used_irqs: number of active IRQs on this controller
   */
  struct fpga_irq_data {
  	void __iomem *base;
3108e6ab2   Linus Walleij   ARM: 7389/2: plat...
41
42
43
44
45
46
47
  	struct irq_chip chip;
  	u32 valid;
  	struct irq_domain *domain;
  	u8 used_irqs;
  };
  
  /* we cannot allocate memory when the controllers are initially registered */
2389d5014   Linus Walleij   ARM: plat-versati...
48
  static struct fpga_irq_data fpga_irq_devices[CONFIG_VERSATILE_FPGA_IRQ_NR];
3108e6ab2   Linus Walleij   ARM: 7389/2: plat...
49
  static int fpga_irq_id;
c41b16f8c   Russell King   ARM: integrator/v...
50
51
52
  static void fpga_irq_mask(struct irq_data *d)
  {
  	struct fpga_irq_data *f = irq_data_get_irq_chip_data(d);
3108e6ab2   Linus Walleij   ARM: 7389/2: plat...
53
  	u32 mask = 1 << d->hwirq;
c41b16f8c   Russell King   ARM: integrator/v...
54
55
56
57
58
59
60
  
  	writel(mask, f->base + IRQ_ENABLE_CLEAR);
  }
  
  static void fpga_irq_unmask(struct irq_data *d)
  {
  	struct fpga_irq_data *f = irq_data_get_irq_chip_data(d);
3108e6ab2   Linus Walleij   ARM: 7389/2: plat...
61
  	u32 mask = 1 << d->hwirq;
c41b16f8c   Russell King   ARM: integrator/v...
62
63
64
65
66
67
  
  	writel(mask, f->base + IRQ_ENABLE_SET);
  }
  
  static void fpga_irq_handle(unsigned int irq, struct irq_desc *desc)
  {
6845664a6   Thomas Gleixner   arm: Cleanup the ...
68
  	struct fpga_irq_data *f = irq_desc_get_handler_data(desc);
c41b16f8c   Russell King   ARM: integrator/v...
69
70
71
72
73
74
75
76
77
78
  	u32 status = readl(f->base + IRQ_STATUS);
  
  	if (status == 0) {
  		do_bad_IRQ(irq, desc);
  		return;
  	}
  
  	do {
  		irq = ffs(status) - 1;
  		status &= ~(1 << irq);
3108e6ab2   Linus Walleij   ARM: 7389/2: plat...
79
  		generic_handle_irq(irq_find_mapping(f->domain, irq));
c41b16f8c   Russell King   ARM: integrator/v...
80
81
  	} while (status);
  }
3108e6ab2   Linus Walleij   ARM: 7389/2: plat...
82
83
84
85
86
87
88
89
90
91
92
93
94
  /*
   * Handle each interrupt in a single FPGA IRQ controller.  Returns non-zero
   * if we've handled at least one interrupt.  This does a single read of the
   * status register and handles all interrupts in order from LSB first.
   */
  static int handle_one_fpga(struct fpga_irq_data *f, struct pt_regs *regs)
  {
  	int handled = 0;
  	int irq;
  	u32 status;
  
  	while ((status  = readl(f->base + IRQ_STATUS))) {
  		irq = ffs(status) - 1;
84bc73990   Marc Zyngier   irqchip: versatil...
95
  		handle_domain_irq(f->domain, irq, regs);
3108e6ab2   Linus Walleij   ARM: 7389/2: plat...
96
97
98
99
100
101
102
103
104
105
106
  		handled = 1;
  	}
  
  	return handled;
  }
  
  /*
   * Keep iterating over all registered FPGA IRQ controllers until there are
   * no pending interrupts.
   */
  asmlinkage void __exception_irq_entry fpga_handle_irq(struct pt_regs *regs)
c41b16f8c   Russell King   ARM: integrator/v...
107
  {
3108e6ab2   Linus Walleij   ARM: 7389/2: plat...
108
  	int i, handled;
c41b16f8c   Russell King   ARM: integrator/v...
109

3108e6ab2   Linus Walleij   ARM: 7389/2: plat...
110
111
112
113
114
115
116
117
118
119
120
121
  	do {
  		for (i = 0, handled = 0; i < fpga_irq_id; ++i)
  			handled |= handle_one_fpga(&fpga_irq_devices[i], regs);
  	} while (handled);
  }
  
  static int fpga_irqdomain_map(struct irq_domain *d, unsigned int irq,
  		irq_hw_number_t hwirq)
  {
  	struct fpga_irq_data *f = d->host_data;
  
  	/* Skip invalid IRQs, only register handlers for the real ones */
3a6ca8c5c   Linus Walleij   ARM: plat-versati...
122
  	if (!(f->valid & BIT(hwirq)))
d94ea3f6d   Grant Likely   irqchip: Return -...
123
  		return -EPERM;
3108e6ab2   Linus Walleij   ARM: 7389/2: plat...
124
125
126
127
  	irq_set_chip_data(irq, f);
  	irq_set_chip_and_handler(irq, &f->chip,
  				handle_level_irq);
  	set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
3108e6ab2   Linus Walleij   ARM: 7389/2: plat...
128
129
130
131
132
133
134
  	return 0;
  }
  
  static struct irq_domain_ops fpga_irqdomain_ops = {
  	.map = fpga_irqdomain_map,
  	.xlate = irq_domain_xlate_onetwocell,
  };
3a6ca8c5c   Linus Walleij   ARM: plat-versati...
135
136
137
  void __init fpga_irq_init(void __iomem *base, const char *name, int irq_start,
  			  int parent_irq, u32 valid, struct device_node *node)
  {
3108e6ab2   Linus Walleij   ARM: 7389/2: plat...
138
  	struct fpga_irq_data *f;
3a6ca8c5c   Linus Walleij   ARM: plat-versati...
139
  	int i;
3108e6ab2   Linus Walleij   ARM: 7389/2: plat...
140
141
  
  	if (fpga_irq_id >= ARRAY_SIZE(fpga_irq_devices)) {
e6423f8be   Paul Bolle   irq: versatile: r...
142
143
  		pr_err("%s: too few FPGA IRQ controllers, increase CONFIG_VERSATILE_FPGA_IRQ_NR
  ", __func__);
3a6ca8c5c   Linus Walleij   ARM: plat-versati...
144
  		return;
3108e6ab2   Linus Walleij   ARM: 7389/2: plat...
145
  	}
3108e6ab2   Linus Walleij   ARM: 7389/2: plat...
146
147
  	f = &fpga_irq_devices[fpga_irq_id];
  	f->base = base;
3108e6ab2   Linus Walleij   ARM: 7389/2: plat...
148
  	f->chip.name = name;
c41b16f8c   Russell King   ARM: integrator/v...
149
150
151
  	f->chip.irq_ack = fpga_irq_mask;
  	f->chip.irq_mask = fpga_irq_mask;
  	f->chip.irq_unmask = fpga_irq_unmask;
3108e6ab2   Linus Walleij   ARM: 7389/2: plat...
152
  	f->valid = valid;
c41b16f8c   Russell King   ARM: integrator/v...
153
154
  
  	if (parent_irq != -1) {
6845664a6   Thomas Gleixner   arm: Cleanup the ...
155
156
  		irq_set_handler_data(parent_irq, f);
  		irq_set_chained_handler(parent_irq, fpga_irq_handle);
c41b16f8c   Russell King   ARM: integrator/v...
157
  	}
3a6ca8c5c   Linus Walleij   ARM: plat-versati...
158
159
  	/* This will also allocate irq descriptors */
  	f->domain = irq_domain_add_simple(node, fls(valid), irq_start,
3108e6ab2   Linus Walleij   ARM: 7389/2: plat...
160
  					  &fpga_irqdomain_ops, f);
3a6ca8c5c   Linus Walleij   ARM: plat-versati...
161
162
163
164
165
166
167
168
  
  	/* This will allocate all valid descriptors in the linear case */
  	for (i = 0; i < fls(valid); i++)
  		if (valid & BIT(i)) {
  			if (!irq_start)
  				irq_create_mapping(f->domain, i);
  			f->used_irqs++;
  		}
bdd272cbb   Linus Walleij   irqchip: versatil...
169
  	pr_info("FPGA IRQ chip %d \"%s\" @ %p, %u irqs",
3108e6ab2   Linus Walleij   ARM: 7389/2: plat...
170
  		fpga_irq_id, name, base, f->used_irqs);
bdd272cbb   Linus Walleij   irqchip: versatil...
171
172
173
174
175
176
  	if (parent_irq != -1)
  		pr_cont(", parent IRQ: %d
  ", parent_irq);
  	else
  		pr_cont("
  ");
3a6ca8c5c   Linus Walleij   ARM: plat-versati...
177
178
  
  	fpga_irq_id++;
9bc150318   Linus Walleij   ARM: 7516/1: plat...
179
  }
c41b16f8c   Russell King   ARM: integrator/v...
180

9bc150318   Linus Walleij   ARM: 7516/1: plat...
181
182
183
184
  #ifdef CONFIG_OF
  int __init fpga_irq_of_init(struct device_node *node,
  			    struct device_node *parent)
  {
9bc150318   Linus Walleij   ARM: 7516/1: plat...
185
186
187
  	void __iomem *base;
  	u32 clear_mask;
  	u32 valid_mask;
bdd272cbb   Linus Walleij   irqchip: versatil...
188
  	int parent_irq;
9bc150318   Linus Walleij   ARM: 7516/1: plat...
189
190
191
192
193
194
195
196
197
198
199
200
201
  
  	if (WARN_ON(!node))
  		return -ENODEV;
  
  	base = of_iomap(node, 0);
  	WARN(!base, "unable to map fpga irq registers
  ");
  
  	if (of_property_read_u32(node, "clear-mask", &clear_mask))
  		clear_mask = 0;
  
  	if (of_property_read_u32(node, "valid-mask", &valid_mask))
  		valid_mask = 0;
bdd272cbb   Linus Walleij   irqchip: versatil...
202
203
  	/* Some chips are cascaded from a parent IRQ */
  	parent_irq = irq_of_parse_and_map(node, 0);
2920bc9ab   Rob Herring   irqchip: versatil...
204
205
  	if (!parent_irq) {
  		set_handle_irq(fpga_handle_irq);
bdd272cbb   Linus Walleij   irqchip: versatil...
206
  		parent_irq = -1;
2920bc9ab   Rob Herring   irqchip: versatil...
207
  	}
bdd272cbb   Linus Walleij   irqchip: versatil...
208
209
  
  	fpga_irq_init(base, node->name, 0, parent_irq, valid_mask, node);
9bc150318   Linus Walleij   ARM: 7516/1: plat...
210
211
212
  
  	writel(clear_mask, base + IRQ_ENABLE_CLEAR);
  	writel(clear_mask, base + FIQ_ENABLE_CLEAR);
59318461c   Rob Herring   irqchip: versatil...
213
214
215
216
217
218
219
  	/*
  	 * On Versatile AB/PB, some secondary interrupts have a direct
  	 * pass-thru to the primary controller for IRQs 20 and 22-31 which need
  	 * to be enabled. See section 3.10 of the Versatile AB user guide.
  	 */
  	if (of_device_is_compatible(node, "arm,versatile-sic"))
  		writel(0xffd00000, base + PIC_ENABLES);
9bc150318   Linus Walleij   ARM: 7516/1: plat...
220
  	return 0;
c41b16f8c   Russell King   ARM: integrator/v...
221
  }
2920bc9ab   Rob Herring   irqchip: versatil...
222
  IRQCHIP_DECLARE(arm_fpga, "arm,versatile-fpga-irq", fpga_irq_of_init);
59318461c   Rob Herring   irqchip: versatil...
223
  IRQCHIP_DECLARE(arm_fpga_sic, "arm,versatile-sic", fpga_irq_of_init);
9bc150318   Linus Walleij   ARM: 7516/1: plat...
224
  #endif