Blame view

drivers/irqchip/irq-versatile-fpga.c 5.86 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>
41a83e06e   Joel Porquet   irqchip: Prepare ...
7
  #include <linux/irqchip.h>
2389d5014   Linus Walleij   ARM: plat-versati...
8
  #include <linux/irqchip/versatile-fpga.h>
3108e6ab2   Linus Walleij   ARM: 7389/2: plat...
9
10
  #include <linux/irqdomain.h>
  #include <linux/module.h>
9bc150318   Linus Walleij   ARM: 7516/1: plat...
11
12
  #include <linux/of.h>
  #include <linux/of_address.h>
bdd272cbb   Linus Walleij   irqchip: versatil...
13
  #include <linux/of_irq.h>
c41b16f8c   Russell King   ARM: integrator/v...
14

3108e6ab2   Linus Walleij   ARM: 7389/2: plat...
15
  #include <asm/exception.h>
c41b16f8c   Russell King   ARM: integrator/v...
16
  #include <asm/mach/irq.h>
c41b16f8c   Russell King   ARM: integrator/v...
17
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
  
  	writel(mask, f->base + IRQ_ENABLE_SET);
  }
bd0b9ac40   Thomas Gleixner   genirq: Remove ir...
65
  static void fpga_irq_handle(struct irq_desc *desc)
c41b16f8c   Russell King   ARM: integrator/v...
66
  {
6845664a6   Thomas Gleixner   arm: Cleanup the ...
67
  	struct fpga_irq_data *f = irq_desc_get_handler_data(desc);
c41b16f8c   Russell King   ARM: integrator/v...
68
69
70
  	u32 status = readl(f->base + IRQ_STATUS);
  
  	if (status == 0) {
bd0b9ac40   Thomas Gleixner   genirq: Remove ir...
71
  		do_bad_IRQ(desc);
c41b16f8c   Russell King   ARM: integrator/v...
72
73
74
75
  		return;
  	}
  
  	do {
bd0b9ac40   Thomas Gleixner   genirq: Remove ir...
76
  		unsigned int irq = ffs(status) - 1;
c41b16f8c   Russell King   ARM: integrator/v...
77
  		status &= ~(1 << irq);
3108e6ab2   Linus Walleij   ARM: 7389/2: plat...
78
  		generic_handle_irq(irq_find_mapping(f->domain, irq));
c41b16f8c   Russell King   ARM: integrator/v...
79
80
  	} while (status);
  }
3108e6ab2   Linus Walleij   ARM: 7389/2: plat...
81
82
83
84
85
86
87
88
89
90
91
92
93
  /*
   * 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...
94
  		handle_domain_irq(f->domain, irq, regs);
3108e6ab2   Linus Walleij   ARM: 7389/2: plat...
95
96
97
98
99
100
101
102
103
104
105
  		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...
106
  {
3108e6ab2   Linus Walleij   ARM: 7389/2: plat...
107
  	int i, handled;
c41b16f8c   Russell King   ARM: integrator/v...
108

3108e6ab2   Linus Walleij   ARM: 7389/2: plat...
109
110
111
112
113
114
115
116
117
118
119
120
  	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...
121
  	if (!(f->valid & BIT(hwirq)))
d94ea3f6d   Grant Likely   irqchip: Return -...
122
  		return -EPERM;
3108e6ab2   Linus Walleij   ARM: 7389/2: plat...
123
124
125
  	irq_set_chip_data(irq, f);
  	irq_set_chip_and_handler(irq, &f->chip,
  				handle_level_irq);
d17cab445   Rob Herring   irqchip: Kill off...
126
  	irq_set_probe(irq);
3108e6ab2   Linus Walleij   ARM: 7389/2: plat...
127
128
  	return 0;
  }
960097365   Krzysztof Kozlowski   irqchip: Constify...
129
  static const struct irq_domain_ops fpga_irqdomain_ops = {
3108e6ab2   Linus Walleij   ARM: 7389/2: plat...
130
131
132
  	.map = fpga_irqdomain_map,
  	.xlate = irq_domain_xlate_onetwocell,
  };
3a6ca8c5c   Linus Walleij   ARM: plat-versati...
133
134
135
  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...
136
  	struct fpga_irq_data *f;
3a6ca8c5c   Linus Walleij   ARM: plat-versati...
137
  	int i;
3108e6ab2   Linus Walleij   ARM: 7389/2: plat...
138
139
  
  	if (fpga_irq_id >= ARRAY_SIZE(fpga_irq_devices)) {
e6423f8be   Paul Bolle   irq: versatile: r...
140
141
  		pr_err("%s: too few FPGA IRQ controllers, increase CONFIG_VERSATILE_FPGA_IRQ_NR
  ", __func__);
3a6ca8c5c   Linus Walleij   ARM: plat-versati...
142
  		return;
3108e6ab2   Linus Walleij   ARM: 7389/2: plat...
143
  	}
3108e6ab2   Linus Walleij   ARM: 7389/2: plat...
144
145
  	f = &fpga_irq_devices[fpga_irq_id];
  	f->base = base;
3108e6ab2   Linus Walleij   ARM: 7389/2: plat...
146
  	f->chip.name = name;
c41b16f8c   Russell King   ARM: integrator/v...
147
148
149
  	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...
150
  	f->valid = valid;
c41b16f8c   Russell King   ARM: integrator/v...
151
152
  
  	if (parent_irq != -1) {
fcd3c5bee   Thomas Gleixner   irqchip/versatile...
153
154
  		irq_set_chained_handler_and_data(parent_irq, fpga_irq_handle,
  						 f);
c41b16f8c   Russell King   ARM: integrator/v...
155
  	}
3a6ca8c5c   Linus Walleij   ARM: plat-versati...
156
157
  	/* This will also allocate irq descriptors */
  	f->domain = irq_domain_add_simple(node, fls(valid), irq_start,
3108e6ab2   Linus Walleij   ARM: 7389/2: plat...
158
  					  &fpga_irqdomain_ops, f);
3a6ca8c5c   Linus Walleij   ARM: plat-versati...
159
160
161
162
163
164
165
166
  
  	/* 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...
167
  	pr_info("FPGA IRQ chip %d \"%s\" @ %p, %u irqs",
3108e6ab2   Linus Walleij   ARM: 7389/2: plat...
168
  		fpga_irq_id, name, base, f->used_irqs);
bdd272cbb   Linus Walleij   irqchip: versatil...
169
170
171
172
173
174
  	if (parent_irq != -1)
  		pr_cont(", parent IRQ: %d
  ", parent_irq);
  	else
  		pr_cont("
  ");
3a6ca8c5c   Linus Walleij   ARM: plat-versati...
175
176
  
  	fpga_irq_id++;
9bc150318   Linus Walleij   ARM: 7516/1: plat...
177
  }
c41b16f8c   Russell King   ARM: integrator/v...
178

9bc150318   Linus Walleij   ARM: 7516/1: plat...
179
180
181
182
  #ifdef CONFIG_OF
  int __init fpga_irq_of_init(struct device_node *node,
  			    struct device_node *parent)
  {
9bc150318   Linus Walleij   ARM: 7516/1: plat...
183
184
185
  	void __iomem *base;
  	u32 clear_mask;
  	u32 valid_mask;
bdd272cbb   Linus Walleij   irqchip: versatil...
186
  	int parent_irq;
9bc150318   Linus Walleij   ARM: 7516/1: plat...
187
188
189
190
191
192
193
194
195
196
197
198
199
  
  	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...
200
201
  	/* Some chips are cascaded from a parent IRQ */
  	parent_irq = irq_of_parse_and_map(node, 0);
2920bc9ab   Rob Herring   irqchip: versatil...
202
203
  	if (!parent_irq) {
  		set_handle_irq(fpga_handle_irq);
bdd272cbb   Linus Walleij   irqchip: versatil...
204
  		parent_irq = -1;
2920bc9ab   Rob Herring   irqchip: versatil...
205
  	}
bdd272cbb   Linus Walleij   irqchip: versatil...
206
207
  
  	fpga_irq_init(base, node->name, 0, parent_irq, valid_mask, node);
9bc150318   Linus Walleij   ARM: 7516/1: plat...
208
209
210
  
  	writel(clear_mask, base + IRQ_ENABLE_CLEAR);
  	writel(clear_mask, base + FIQ_ENABLE_CLEAR);
59318461c   Rob Herring   irqchip: versatil...
211
212
213
214
215
216
217
  	/*
  	 * 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...
218
  	return 0;
c41b16f8c   Russell King   ARM: integrator/v...
219
  }
2920bc9ab   Rob Herring   irqchip: versatil...
220
  IRQCHIP_DECLARE(arm_fpga, "arm,versatile-fpga-irq", fpga_irq_of_init);
59318461c   Rob Herring   irqchip: versatil...
221
  IRQCHIP_DECLARE(arm_fpga_sic, "arm,versatile-sic", fpga_irq_of_init);
1adea8b8b   Neil Armstrong   irqchip: versatil...
222
  IRQCHIP_DECLARE(ox810se_rps, "oxsemi,ox810se-rps-irq", fpga_irq_of_init);
9bc150318   Linus Walleij   ARM: 7516/1: plat...
223
  #endif