Blame view

drivers/irqchip/irq-mips-cpu.c 6.95 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
  /*
   * Copyright 2001 MontaVista Software Inc.
   * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net
   *
   * Copyright (C) 2001 Ralf Baechle
703422879   Ralf Baechle   MIPS: Whitespace ...
6
7
   * Copyright (C) 2005  MIPS Technologies, Inc.	All rights reserved.
   *	Author: Maciej W. Rozycki <macro@mips.com>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
8
9
10
   *
   * This file define the irq handler for MIPS CPU interrupts.
   *
703422879   Ralf Baechle   MIPS: Whitespace ...
11
12
   * 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
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
13
14
15
16
17
18
19
   * Free Software Foundation;  either version 2 of the  License, or (at your
   * option) any later version.
   */
  
  /*
   * Almost all MIPS CPUs define 8 interrupt sources.  They are typically
   * level triggered (i.e., cannot be cleared from CPU; must be cleared from
3838a547f   Paul Burton   irqchip: mips-cpu...
20
   * device).
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
21
   *
3838a547f   Paul Burton   irqchip: mips-cpu...
22
23
   * The first two are software interrupts (i.e. not exposed as pins) which
   * may be used for IPIs in multi-threaded single-core systems.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
24
   *
3838a547f   Paul Burton   irqchip: mips-cpu...
25
26
27
   * The last one is usually the CPU timer interrupt if the counter register
   * is present, or for old CPUs with an external FPU by convention it's the
   * FPU exception interrupt.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
28
29
30
31
   */
  #include <linux/init.h>
  #include <linux/interrupt.h>
  #include <linux/kernel.h>
ca4d3e674   David Howells   MIPS: Add missing...
32
  #include <linux/irq.h>
41a83e06e   Joel Porquet   irqchip: Prepare ...
33
  #include <linux/irqchip.h>
0916b4696   Gabor Juhos   MIPS: add irqdoma...
34
  #include <linux/irqdomain.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
35
36
37
  
  #include <asm/irq_cpu.h>
  #include <asm/mipsregs.h>
d03d0a577   Ralf Baechle   MT bulletproofing.
38
  #include <asm/mipsmtregs.h>
f64e55dcb   Andrew Bresticker   MIPS: Set vint ha...
39
  #include <asm/setup.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
40

131735afc   Paul Burton   irqchip: mips-cpu...
41
  static struct irq_domain *irq_domain;
3838a547f   Paul Burton   irqchip: mips-cpu...
42
  static struct irq_domain *ipi_domain;
131735afc   Paul Burton   irqchip: mips-cpu...
43

a93951c45   Thomas Gleixner   MIPS: irq_cpu: Co...
44
  static inline void unmask_mips_irq(struct irq_data *d)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
45
  {
131735afc   Paul Burton   irqchip: mips-cpu...
46
  	set_c0_status(IE_SW0 << d->hwirq);
569f75bd0   Ralf Baechle   Use an irq_enable...
47
  	irq_enable_hazard();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
48
  }
a93951c45   Thomas Gleixner   MIPS: irq_cpu: Co...
49
  static inline void mask_mips_irq(struct irq_data *d)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
50
  {
131735afc   Paul Burton   irqchip: mips-cpu...
51
  	clear_c0_status(IE_SW0 << d->hwirq);
569f75bd0   Ralf Baechle   Use an irq_enable...
52
  	irq_disable_hazard();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
53
  }
94dee171d   Ralf Baechle   [MIPS] Eleminate ...
54
  static struct irq_chip mips_cpu_irq_controller = {
70d21cdee   Atsushi Nemoto   [MIPS] use name i...
55
  	.name		= "MIPS",
a93951c45   Thomas Gleixner   MIPS: irq_cpu: Co...
56
57
58
59
60
  	.irq_ack	= mask_mips_irq,
  	.irq_mask	= mask_mips_irq,
  	.irq_mask_ack	= mask_mips_irq,
  	.irq_unmask	= unmask_mips_irq,
  	.irq_eoi	= unmask_mips_irq,
a3e6c1eff   Felix Fietkau   MIPS: IRQ: Fix di...
61
62
  	.irq_disable	= mask_mips_irq,
  	.irq_enable	= unmask_mips_irq,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
63
  };
d03d0a577   Ralf Baechle   MT bulletproofing.
64
65
66
  /*
   * Basically the same as above but taking care of all the MT stuff
   */
a93951c45   Thomas Gleixner   MIPS: irq_cpu: Co...
67
  static unsigned int mips_mt_cpu_irq_startup(struct irq_data *d)
d03d0a577   Ralf Baechle   MT bulletproofing.
68
69
  {
  	unsigned int vpflags = dvpe();
131735afc   Paul Burton   irqchip: mips-cpu...
70
  	clear_c0_cause(C_SW0 << d->hwirq);
d03d0a577   Ralf Baechle   MT bulletproofing.
71
  	evpe(vpflags);
a93951c45   Thomas Gleixner   MIPS: irq_cpu: Co...
72
  	unmask_mips_irq(d);
d03d0a577   Ralf Baechle   MT bulletproofing.
73
74
  	return 0;
  }
d03d0a577   Ralf Baechle   MT bulletproofing.
75
76
77
78
  /*
   * While we ack the interrupt interrupts are disabled and thus we don't need
   * to deal with concurrency issues.  Same for mips_cpu_irq_end.
   */
a93951c45   Thomas Gleixner   MIPS: irq_cpu: Co...
79
  static void mips_mt_cpu_irq_ack(struct irq_data *d)
d03d0a577   Ralf Baechle   MT bulletproofing.
80
81
  {
  	unsigned int vpflags = dvpe();
131735afc   Paul Burton   irqchip: mips-cpu...
82
  	clear_c0_cause(C_SW0 << d->hwirq);
d03d0a577   Ralf Baechle   MT bulletproofing.
83
  	evpe(vpflags);
a93951c45   Thomas Gleixner   MIPS: irq_cpu: Co...
84
  	mask_mips_irq(d);
d03d0a577   Ralf Baechle   MT bulletproofing.
85
  }
3838a547f   Paul Burton   irqchip: mips-cpu...
86
87
88
89
90
91
92
93
94
95
96
  #ifdef CONFIG_GENERIC_IRQ_IPI
  
  static void mips_mt_send_ipi(struct irq_data *d, unsigned int cpu)
  {
  	irq_hw_number_t hwirq = irqd_to_hwirq(d);
  	unsigned long flags;
  	int vpflags;
  
  	local_irq_save(flags);
  
  	/* We can only send IPIs to VPEs within the local core */
fe7a38c62   Paul Burton   MIPS: Unify check...
97
  	WARN_ON(!cpus_are_siblings(smp_processor_id(), cpu));
3838a547f   Paul Burton   irqchip: mips-cpu...
98
99
100
101
102
103
104
105
106
107
  
  	vpflags = dvpe();
  	settc(cpu_vpe_id(&cpu_data[cpu]));
  	write_vpe_c0_cause(read_vpe_c0_cause() | (C_SW0 << hwirq));
  	evpe(vpflags);
  
  	local_irq_restore(flags);
  }
  
  #endif /* CONFIG_GENERIC_IRQ_IPI */
94dee171d   Ralf Baechle   [MIPS] Eleminate ...
108
  static struct irq_chip mips_mt_cpu_irq_controller = {
70d21cdee   Atsushi Nemoto   [MIPS] use name i...
109
  	.name		= "MIPS",
a93951c45   Thomas Gleixner   MIPS: irq_cpu: Co...
110
111
112
113
114
115
  	.irq_startup	= mips_mt_cpu_irq_startup,
  	.irq_ack	= mips_mt_cpu_irq_ack,
  	.irq_mask	= mask_mips_irq,
  	.irq_mask_ack	= mips_mt_cpu_irq_ack,
  	.irq_unmask	= unmask_mips_irq,
  	.irq_eoi	= unmask_mips_irq,
a3e6c1eff   Felix Fietkau   MIPS: IRQ: Fix di...
116
117
  	.irq_disable	= mask_mips_irq,
  	.irq_enable	= unmask_mips_irq,
3838a547f   Paul Burton   irqchip: mips-cpu...
118
119
120
  #ifdef CONFIG_GENERIC_IRQ_IPI
  	.ipi_send_single = mips_mt_send_ipi,
  #endif
d03d0a577   Ralf Baechle   MT bulletproofing.
121
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
122

85f7cdacb   Andrew Bresticker   MIPS: Provide a g...
123
124
125
  asmlinkage void __weak plat_irq_dispatch(void)
  {
  	unsigned long pending = read_c0_cause() & read_c0_status() & ST0_IM;
131735afc   Paul Burton   irqchip: mips-cpu...
126
  	unsigned int virq;
85f7cdacb   Andrew Bresticker   MIPS: Provide a g...
127
128
129
130
131
132
133
134
135
136
  	int irq;
  
  	if (!pending) {
  		spurious_interrupt();
  		return;
  	}
  
  	pending >>= CAUSEB_IP;
  	while (pending) {
  		irq = fls(pending) - 1;
3838a547f   Paul Burton   irqchip: mips-cpu...
137
138
139
140
  		if (IS_ENABLED(CONFIG_GENERIC_IRQ_IPI) && irq < 2)
  			virq = irq_linear_revmap(ipi_domain, irq);
  		else
  			virq = irq_linear_revmap(irq_domain, irq);
131735afc   Paul Burton   irqchip: mips-cpu...
141
  		do_IRQ(virq);
85f7cdacb   Andrew Bresticker   MIPS: Provide a g...
142
143
144
  		pending &= ~BIT(irq);
  	}
  }
0916b4696   Gabor Juhos   MIPS: add irqdoma...
145
146
147
  static int mips_cpu_intc_map(struct irq_domain *d, unsigned int irq,
  			     irq_hw_number_t hw)
  {
82faeffa7   Julia Lawall   irqchip/mips-cpu:...
148
  	struct irq_chip *chip;
0916b4696   Gabor Juhos   MIPS: add irqdoma...
149
150
151
152
153
154
155
  
  	if (hw < 2 && cpu_has_mipsmt) {
  		/* Software interrupts are used for MT/CMT IPI */
  		chip = &mips_mt_cpu_irq_controller;
  	} else {
  		chip = &mips_cpu_irq_controller;
  	}
f64e55dcb   Andrew Bresticker   MIPS: Set vint ha...
156
157
  	if (cpu_has_vint)
  		set_vi_handler(hw, plat_irq_dispatch);
0916b4696   Gabor Juhos   MIPS: add irqdoma...
158
159
160
161
162
163
164
165
166
  	irq_set_chip_and_handler(irq, chip, handle_percpu_irq);
  
  	return 0;
  }
  
  static const struct irq_domain_ops mips_cpu_intc_irq_domain_ops = {
  	.map = mips_cpu_intc_map,
  	.xlate = irq_domain_xlate_onecell,
  };
3838a547f   Paul Burton   irqchip: mips-cpu...
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
  #ifdef CONFIG_GENERIC_IRQ_IPI
  
  struct cpu_ipi_domain_state {
  	DECLARE_BITMAP(allocated, 2);
  };
  
  static int mips_cpu_ipi_alloc(struct irq_domain *domain, unsigned int virq,
  			      unsigned int nr_irqs, void *arg)
  {
  	struct cpu_ipi_domain_state *state = domain->host_data;
  	unsigned int i, hwirq;
  	int ret;
  
  	for (i = 0; i < nr_irqs; i++) {
  		hwirq = find_first_zero_bit(state->allocated, 2);
  		if (hwirq == 2)
  			return -EBUSY;
  		bitmap_set(state->allocated, hwirq, 1);
  
  		ret = irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq,
  						    &mips_mt_cpu_irq_controller,
  						    NULL);
  		if (ret)
  			return ret;
  
  		ret = irq_set_irq_type(virq + i, IRQ_TYPE_LEVEL_HIGH);
  		if (ret)
  			return ret;
  	}
  
  	return 0;
  }
  
  static int mips_cpu_ipi_match(struct irq_domain *d, struct device_node *node,
  			      enum irq_domain_bus_token bus_token)
  {
  	bool is_ipi;
  
  	switch (bus_token) {
  	case DOMAIN_BUS_IPI:
  		is_ipi = d->bus_token == bus_token;
  		return (!node || (to_of_node(d->fwnode) == node)) && is_ipi;
  	default:
  		return 0;
  	}
  }
  
  static const struct irq_domain_ops mips_cpu_ipi_chip_ops = {
  	.alloc	= mips_cpu_ipi_alloc,
  	.match	= mips_cpu_ipi_match,
  };
  
  static void mips_cpu_register_ipi_domain(struct device_node *of_node)
  {
  	struct cpu_ipi_domain_state *ipi_domain_state;
  
  	ipi_domain_state = kzalloc(sizeof(*ipi_domain_state), GFP_KERNEL);
  	ipi_domain = irq_domain_add_hierarchy(irq_domain,
  					      IRQ_DOMAIN_FLAG_IPI_SINGLE,
  					      2, of_node,
  					      &mips_cpu_ipi_chip_ops,
  					      ipi_domain_state);
  	if (!ipi_domain)
  		panic("Failed to add MIPS CPU IPI domain");
96f0d93a4   Marc Zyngier   irqchip/MSI: Use ...
231
  	irq_domain_update_bus_token(ipi_domain, DOMAIN_BUS_IPI);
3838a547f   Paul Burton   irqchip: mips-cpu...
232
233
234
235
236
237
238
  }
  
  #else /* !CONFIG_GENERIC_IRQ_IPI */
  
  static inline void mips_cpu_register_ipi_domain(struct device_node *of_node) {}
  
  #endif /* !CONFIG_GENERIC_IRQ_IPI */
0f84c3053   Andrew Bresticker   MIPS: Always use ...
239
  static void __init __mips_cpu_irq_init(struct device_node *of_node)
0916b4696   Gabor Juhos   MIPS: add irqdoma...
240
  {
0916b4696   Gabor Juhos   MIPS: add irqdoma...
241
242
243
  	/* Mask interrupts. */
  	clear_c0_status(ST0_IM);
  	clear_c0_cause(CAUSEF_IP);
131735afc   Paul Burton   irqchip: mips-cpu...
244
245
246
247
  	irq_domain = irq_domain_add_legacy(of_node, 8, MIPS_CPU_IRQ_BASE, 0,
  					   &mips_cpu_intc_irq_domain_ops,
  					   NULL);
  	if (!irq_domain)
f7777dcc7   Ralf Baechle   MIPS: Panic messa...
248
  		panic("Failed to add irqdomain for MIPS CPU");
3838a547f   Paul Burton   irqchip: mips-cpu...
249
250
251
252
253
254
255
  
  	/*
  	 * Only proceed to register the software interrupt IPI implementation
  	 * for CPUs which implement the MIPS MT (multi-threading) ASE.
  	 */
  	if (cpu_has_mipsmt)
  		mips_cpu_register_ipi_domain(of_node);
0f84c3053   Andrew Bresticker   MIPS: Always use ...
256
257
258
259
260
261
  }
  
  void __init mips_cpu_irq_init(void)
  {
  	__mips_cpu_irq_init(NULL);
  }
0916b4696   Gabor Juhos   MIPS: add irqdoma...
262

afe8dc254   Andrew Bresticker   MIPS: Rename mips...
263
264
  int __init mips_cpu_irq_of_init(struct device_node *of_node,
  				struct device_node *parent)
0f84c3053   Andrew Bresticker   MIPS: Always use ...
265
266
  {
  	__mips_cpu_irq_init(of_node);
0916b4696   Gabor Juhos   MIPS: add irqdoma...
267
268
  	return 0;
  }
892b8cf06   Paul Burton   IRQCHIP: irq_cpu:...
269
  IRQCHIP_DECLARE(cpu_intc, "mti,cpu-interrupt-controller", mips_cpu_irq_of_init);