1*94673892Sjsg /* $OpenBSD: ampintc.c,v 1.31 2023/09/22 01:10:43 jsg Exp $ */
2f24071e5Spatrick /*
3f24071e5Spatrick * Copyright (c) 2007,2009,2011 Dale Rahn <drahn@openbsd.org>
4f24071e5Spatrick *
5f24071e5Spatrick * Permission to use, copy, modify, and distribute this software for any
6f24071e5Spatrick * purpose with or without fee is hereby granted, provided that the above
7f24071e5Spatrick * copyright notice and this permission notice appear in all copies.
8f24071e5Spatrick *
9f24071e5Spatrick * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10f24071e5Spatrick * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11f24071e5Spatrick * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12f24071e5Spatrick * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13f24071e5Spatrick * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14f24071e5Spatrick * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15f24071e5Spatrick * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16f24071e5Spatrick */
17f24071e5Spatrick
18f24071e5Spatrick /*
19f24071e5Spatrick * This driver implements the interrupt controller as specified in
20f24071e5Spatrick * DDI0407E_cortex_a9_mpcore_r2p0_trm with the
21f24071e5Spatrick * IHI0048A_gic_architecture_spec_v1_0 underlying specification
22f24071e5Spatrick */
23f24071e5Spatrick #include <sys/param.h>
24f24071e5Spatrick #include <sys/systm.h>
25f24071e5Spatrick #include <sys/queue.h>
26f24071e5Spatrick #include <sys/malloc.h>
27f24071e5Spatrick #include <sys/device.h>
28f24071e5Spatrick #include <sys/evcount.h>
29f24071e5Spatrick
30c17c7ba4Spatrick #include <uvm/uvm_extern.h>
31c17c7ba4Spatrick
32f24071e5Spatrick #include <machine/bus.h>
33f24071e5Spatrick #include <machine/fdt.h>
34f24071e5Spatrick
35f24071e5Spatrick #include <dev/ofw/fdt.h>
36f24071e5Spatrick #include <dev/ofw/openfirm.h>
37f24071e5Spatrick
38*94673892Sjsg #include <machine/simplebusvar.h>
39c17c7ba4Spatrick
40f24071e5Spatrick /* registers */
41f24071e5Spatrick #define ICD_DCR 0x000
42f24071e5Spatrick #define ICD_DCR_ES 0x00000001
43f24071e5Spatrick #define ICD_DCR_ENS 0x00000002
44f24071e5Spatrick
45f24071e5Spatrick #define ICD_ICTR 0x004
46f24071e5Spatrick #define ICD_ICTR_LSPI_SH 11
47f24071e5Spatrick #define ICD_ICTR_LSPI_M 0x1f
48f24071e5Spatrick #define ICD_ICTR_CPU_SH 5
49f24071e5Spatrick #define ICD_ICTR_CPU_M 0x07
50f24071e5Spatrick #define ICD_ICTR_ITL_SH 0
51f24071e5Spatrick #define ICD_ICTR_ITL_M 0x1f
52f24071e5Spatrick #define ICD_IDIR 0x008
53f24071e5Spatrick #define ICD_DIR_PROD_SH 24
54f24071e5Spatrick #define ICD_DIR_PROD_M 0xff
55f24071e5Spatrick #define ICD_DIR_REV_SH 12
56f24071e5Spatrick #define ICD_DIR_REV_M 0xfff
57f24071e5Spatrick #define ICD_DIR_IMP_SH 0
58f24071e5Spatrick #define ICD_DIR_IMP_M 0xfff
59f24071e5Spatrick
6018a1c566Spatrick #define IRQ_TO_REG32(i) (((i) >> 5) & 0x1f)
61f24071e5Spatrick #define IRQ_TO_REG32BIT(i) ((i) & 0x1f)
6218a1c566Spatrick #define IRQ_TO_REG4(i) (((i) >> 2) & 0xff)
63f24071e5Spatrick #define IRQ_TO_REG4BIT(i) ((i) & 0x3)
6418a1c566Spatrick #define IRQ_TO_REG16(i) (((i) >> 4) & 0x3f)
65edffca28Spatrick #define IRQ_TO_REG16BIT(i) ((i) & 0xf)
66f24071e5Spatrick #define IRQ_TO_REGBIT_S(i) 8
67f24071e5Spatrick #define IRQ_TO_REG4BIT_M(i) 8
68f24071e5Spatrick
69f24071e5Spatrick #define ICD_ISRn(i) (0x080 + (IRQ_TO_REG32(i) * 4))
70f24071e5Spatrick #define ICD_ISERn(i) (0x100 + (IRQ_TO_REG32(i) * 4))
71f24071e5Spatrick #define ICD_ICERn(i) (0x180 + (IRQ_TO_REG32(i) * 4))
72f24071e5Spatrick #define ICD_ISPRn(i) (0x200 + (IRQ_TO_REG32(i) * 4))
73f24071e5Spatrick #define ICD_ICPRn(i) (0x280 + (IRQ_TO_REG32(i) * 4))
74f24071e5Spatrick #define ICD_ABRn(i) (0x300 + (IRQ_TO_REG32(i) * 4))
75f24071e5Spatrick #define ICD_IPRn(i) (0x400 + (i))
76f24071e5Spatrick #define ICD_IPTRn(i) (0x800 + (i))
77f24071e5Spatrick #define ICD_ICRn(i) (0xC00 + (IRQ_TO_REG16(i) * 4))
78edffca28Spatrick #define ICD_ICR_TRIG_LEVEL(i) (0x0 << (IRQ_TO_REG16BIT(i) * 2))
79edffca28Spatrick #define ICD_ICR_TRIG_EDGE(i) (0x2 << (IRQ_TO_REG16BIT(i) * 2))
80edffca28Spatrick #define ICD_ICR_TRIG_MASK(i) (0x2 << (IRQ_TO_REG16BIT(i) * 2))
81edffca28Spatrick
82f24071e5Spatrick /*
83f24071e5Spatrick * what about (ppi|spi)_status
84f24071e5Spatrick */
85f24071e5Spatrick #define ICD_PPI 0xD00
86f24071e5Spatrick #define ICD_PPI_GTIMER (1 << 11)
87f24071e5Spatrick #define ICD_PPI_FIQ (1 << 12)
88f24071e5Spatrick #define ICD_PPI_PTIMER (1 << 13)
89f24071e5Spatrick #define ICD_PPI_PWDOG (1 << 14)
90f24071e5Spatrick #define ICD_PPI_IRQ (1 << 15)
91f24071e5Spatrick #define ICD_SPI_BASE 0xD04
92f24071e5Spatrick #define ICD_SPIn(i) (ICD_SPI_BASE + ((i) * 4))
93f24071e5Spatrick
94f24071e5Spatrick
95f24071e5Spatrick #define ICD_SGIR 0xF00
96f24071e5Spatrick
97f24071e5Spatrick #define ICD_PERIPH_ID_0 0xFD0
98f24071e5Spatrick #define ICD_PERIPH_ID_1 0xFD4
99f24071e5Spatrick #define ICD_PERIPH_ID_2 0xFD8
100f24071e5Spatrick #define ICD_PERIPH_ID_3 0xFDC
101f24071e5Spatrick #define ICD_PERIPH_ID_4 0xFE0
102f24071e5Spatrick #define ICD_PERIPH_ID_5 0xFE4
103f24071e5Spatrick #define ICD_PERIPH_ID_6 0xFE8
104f24071e5Spatrick #define ICD_PERIPH_ID_7 0xFEC
105f24071e5Spatrick
106f24071e5Spatrick #define ICD_COMP_ID_0 0xFEC
107f24071e5Spatrick #define ICD_COMP_ID_1 0xFEC
108f24071e5Spatrick #define ICD_COMP_ID_2 0xFEC
109f24071e5Spatrick #define ICD_COMP_ID_3 0xFEC
110f24071e5Spatrick
111f24071e5Spatrick
112f24071e5Spatrick #define ICPICR 0x00
113f24071e5Spatrick #define ICPIPMR 0x04
114f24071e5Spatrick /* XXX - must left justify bits to 0 - 7 */
115f24071e5Spatrick #define ICMIPMR_SH 4
116f24071e5Spatrick #define ICPBPR 0x08
117f24071e5Spatrick #define ICPIAR 0x0C
118f24071e5Spatrick #define ICPIAR_IRQ_SH 0
119f24071e5Spatrick #define ICPIAR_IRQ_M 0x3ff
120f24071e5Spatrick #define ICPIAR_CPUID_SH 10
121f24071e5Spatrick #define ICPIAR_CPUID_M 0x7
122f24071e5Spatrick #define ICPIAR_NO_PENDING_IRQ ICPIAR_IRQ_M
123f24071e5Spatrick #define ICPEOIR 0x10
124f24071e5Spatrick #define ICPPRP 0x14
125f24071e5Spatrick #define ICPHPIR 0x18
126f24071e5Spatrick #define ICPIIR 0xFC
127f24071e5Spatrick
128f24071e5Spatrick /*
129f24071e5Spatrick * what about periph_id and component_id
130f24071e5Spatrick */
131f24071e5Spatrick
132f24071e5Spatrick #define IRQ_ENABLE 1
133f24071e5Spatrick #define IRQ_DISABLE 0
134f24071e5Spatrick
135f24071e5Spatrick struct ampintc_softc {
136c17c7ba4Spatrick struct simplebus_softc sc_sbus;
137d7e3db9cSkettenis struct intrq *sc_handler;
138f24071e5Spatrick int sc_nintr;
139f24071e5Spatrick bus_space_tag_t sc_iot;
140f24071e5Spatrick bus_space_handle_t sc_d_ioh, sc_p_ioh;
1410d4f5be1Skettenis uint8_t sc_cpu_mask[ICD_ICTR_CPU_M + 1];
142f24071e5Spatrick struct evcount sc_spur;
143f24071e5Spatrick struct interrupt_controller sc_ic;
144d7e3db9cSkettenis int sc_ipi_reason[ICD_ICTR_CPU_M + 1];
1454002e08dSkettenis int sc_ipi_num[3];
146f24071e5Spatrick };
147f24071e5Spatrick struct ampintc_softc *ampintc;
148f24071e5Spatrick
149f24071e5Spatrick
150f24071e5Spatrick struct intrhand {
151f24071e5Spatrick TAILQ_ENTRY(intrhand) ih_list; /* link on intrq list */
152f24071e5Spatrick int (*ih_func)(void *); /* handler */
153f24071e5Spatrick void *ih_arg; /* arg for handler */
154f24071e5Spatrick int ih_ipl; /* IPL_* */
1557a7b3facSkettenis int ih_flags;
156f24071e5Spatrick int ih_irq; /* IRQ number */
157f24071e5Spatrick struct evcount ih_count;
158f24071e5Spatrick char *ih_name;
159452daaedSpatrick struct cpu_info *ih_ci; /* CPU the IRQ runs on */
160f24071e5Spatrick };
161f24071e5Spatrick
162f24071e5Spatrick struct intrq {
163f24071e5Spatrick TAILQ_HEAD(, intrhand) iq_list; /* handler list */
164de70244eSpatrick struct cpu_info *iq_ci; /* CPU the IRQ runs on */
165eb0c9b3cSpatrick int iq_irq_max; /* IRQ to mask while handling */
166eb0c9b3cSpatrick int iq_irq_min; /* lowest IRQ when shared */
167f24071e5Spatrick int iq_ist; /* share type */
168f24071e5Spatrick };
169f24071e5Spatrick
170f24071e5Spatrick
171f24071e5Spatrick int ampintc_match(struct device *, void *, void *);
172f24071e5Spatrick void ampintc_attach(struct device *, struct device *, void *);
1734002e08dSkettenis int ampintc_activate(struct device *, int);
1744002e08dSkettenis void ampintc_init(struct ampintc_softc *);
175d7e3db9cSkettenis void ampintc_cpuinit(void);
176f24071e5Spatrick int ampintc_spllower(int);
177f24071e5Spatrick void ampintc_splx(int);
178f24071e5Spatrick int ampintc_splraise(int);
179f24071e5Spatrick void ampintc_setipl(int);
180f24071e5Spatrick void ampintc_calc_mask(void);
181de70244eSpatrick void ampintc_calc_irq(struct ampintc_softc *, int);
182789e88a4Spatrick void *ampintc_intr_establish(int, int, int, struct cpu_info *,
183f24071e5Spatrick int (*)(void *), void *, char *);
184789e88a4Spatrick void *ampintc_intr_establish_fdt(void *, int *, int,
185789e88a4Spatrick struct cpu_info *, int (*)(void *), void *, char *);
186f24071e5Spatrick void ampintc_intr_disestablish(void *);
187f24071e5Spatrick void ampintc_irq_handler(void *);
188f24071e5Spatrick const char *ampintc_intr_string(void *);
189f24071e5Spatrick uint32_t ampintc_iack(void);
190f24071e5Spatrick void ampintc_eoi(uint32_t);
191f24071e5Spatrick void ampintc_set_priority(int, int);
192f24071e5Spatrick void ampintc_intr_enable(int);
193f24071e5Spatrick void ampintc_intr_disable(int);
194edffca28Spatrick void ampintc_intr_config(int, int);
1950d4f5be1Skettenis void ampintc_route(int, int, struct cpu_info *);
196d7e3db9cSkettenis void ampintc_route_irq(void *, int, struct cpu_info *);
197452daaedSpatrick void ampintc_intr_barrier(void *);
198d7e3db9cSkettenis
199d7e3db9cSkettenis int ampintc_ipi_combined(void *);
200d7e3db9cSkettenis int ampintc_ipi_nop(void *);
201d7e3db9cSkettenis int ampintc_ipi_ddb(void *);
2024002e08dSkettenis int ampintc_ipi_halt(void *);
203d7e3db9cSkettenis void ampintc_send_ipi(struct cpu_info *, int);
204f24071e5Spatrick
2059fdf0c62Smpi const struct cfattach ampintc_ca = {
2064002e08dSkettenis sizeof (struct ampintc_softc), ampintc_match, ampintc_attach,
2074002e08dSkettenis NULL, ampintc_activate
208f24071e5Spatrick };
209f24071e5Spatrick
210f24071e5Spatrick struct cfdriver ampintc_cd = {
211f24071e5Spatrick NULL, "ampintc", DV_DULL
212f24071e5Spatrick };
213f24071e5Spatrick
214f24071e5Spatrick static char *ampintc_compatibles[] = {
215f24071e5Spatrick "arm,cortex-a7-gic",
216f24071e5Spatrick "arm,cortex-a9-gic",
217f24071e5Spatrick "arm,cortex-a15-gic",
218b78d0bc7Sjsg "arm,gic-400",
219f24071e5Spatrick NULL
220f24071e5Spatrick };
221f24071e5Spatrick
222f24071e5Spatrick int
ampintc_match(struct device * parent,void * cfdata,void * aux)223f24071e5Spatrick ampintc_match(struct device *parent, void *cfdata, void *aux)
224f24071e5Spatrick {
225f24071e5Spatrick struct fdt_attach_args *faa = aux;
226f24071e5Spatrick int i;
227f24071e5Spatrick
228f24071e5Spatrick for (i = 0; ampintc_compatibles[i]; i++)
229f24071e5Spatrick if (OF_is_compatible(faa->fa_node, ampintc_compatibles[i]))
230f24071e5Spatrick return (1);
231f24071e5Spatrick
232f24071e5Spatrick return (0);
233f24071e5Spatrick }
234f24071e5Spatrick
235f24071e5Spatrick void
ampintc_attach(struct device * parent,struct device * self,void * aux)236f24071e5Spatrick ampintc_attach(struct device *parent, struct device *self, void *aux)
237f24071e5Spatrick {
238f24071e5Spatrick struct ampintc_softc *sc = (struct ampintc_softc *)self;
239f24071e5Spatrick struct fdt_attach_args *faa = aux;
2400d4f5be1Skettenis int i, nintr, ncpu;
2410d4f5be1Skettenis uint32_t ictr;
242d7e3db9cSkettenis #ifdef MULTIPROCESSOR
2434002e08dSkettenis int nipi, ipiirq[3];
244d7e3db9cSkettenis #endif
245f24071e5Spatrick
246f24071e5Spatrick ampintc = sc;
247f24071e5Spatrick
248f24071e5Spatrick arm_init_smask();
249f24071e5Spatrick
250f24071e5Spatrick sc->sc_iot = faa->fa_iot;
251f24071e5Spatrick
252f24071e5Spatrick /* First row: ICD */
253f24071e5Spatrick if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
254f24071e5Spatrick faa->fa_reg[0].size, 0, &sc->sc_d_ioh))
255f24071e5Spatrick panic("%s: ICD bus_space_map failed!", __func__);
256f24071e5Spatrick
257f24071e5Spatrick /* Second row: ICP */
258f24071e5Spatrick if (bus_space_map(sc->sc_iot, faa->fa_reg[1].addr,
259f24071e5Spatrick faa->fa_reg[1].size, 0, &sc->sc_p_ioh))
260f24071e5Spatrick panic("%s: ICP bus_space_map failed!", __func__);
261f24071e5Spatrick
262f24071e5Spatrick evcount_attach(&sc->sc_spur, "irq1023/spur", NULL);
263f24071e5Spatrick
2640d4f5be1Skettenis ictr = bus_space_read_4(sc->sc_iot, sc->sc_d_ioh, ICD_ICTR);
2650d4f5be1Skettenis nintr = 32 * ((ictr >> ICD_ICTR_ITL_SH) & ICD_ICTR_ITL_M);
266f24071e5Spatrick nintr += 32; /* ICD_ICTR + 1, irq 0-31 is SGI, 32+ is PPI */
267f24071e5Spatrick sc->sc_nintr = nintr;
2680d4f5be1Skettenis ncpu = ((ictr >> ICD_ICTR_CPU_SH) & ICD_ICTR_CPU_M) + 1;
2690d4f5be1Skettenis printf(" nirq %d, ncpu %d", nintr, ncpu);
2700d4f5be1Skettenis
2710d4f5be1Skettenis KASSERT(curcpu()->ci_cpuid <= ICD_ICTR_CPU_M);
2720d4f5be1Skettenis sc->sc_cpu_mask[curcpu()->ci_cpuid] =
2730d4f5be1Skettenis bus_space_read_1(sc->sc_iot, sc->sc_d_ioh, ICD_IPTRn(0));
274f24071e5Spatrick
2754002e08dSkettenis ampintc_init(sc);
276f24071e5Spatrick
277f24071e5Spatrick /* software reset of the part? */
278f24071e5Spatrick /* set protection bit (kernel only)? */
279f24071e5Spatrick
280f24071e5Spatrick /* XXX - check power saving bit */
281f24071e5Spatrick
282d7e3db9cSkettenis sc->sc_handler = mallocarray(nintr, sizeof(*sc->sc_handler), M_DEVBUF,
283d7e3db9cSkettenis M_ZERO | M_NOWAIT);
284f24071e5Spatrick for (i = 0; i < nintr; i++) {
285d7e3db9cSkettenis TAILQ_INIT(&sc->sc_handler[i].iq_list);
286f24071e5Spatrick }
287f24071e5Spatrick
288f24071e5Spatrick ampintc_setipl(IPL_HIGH); /* XXX ??? */
289f24071e5Spatrick ampintc_calc_mask();
290f24071e5Spatrick
291f24071e5Spatrick /* insert self as interrupt handler */
292f24071e5Spatrick arm_set_intr_handler(ampintc_splraise, ampintc_spllower, ampintc_splx,
2935dee5702Skettenis ampintc_setipl, ampintc_irq_handler, NULL, NULL, NULL);
294f24071e5Spatrick
295d7e3db9cSkettenis #ifdef MULTIPROCESSOR
296d7e3db9cSkettenis /* setup IPI interrupts */
297d7e3db9cSkettenis
298d7e3db9cSkettenis /*
2994002e08dSkettenis * Ideally we want three IPI interrupts, one for NOP, one for
3004002e08dSkettenis * DDB and one for HALT. However we can survive if only one
3014002e08dSkettenis * is available; it is possible that most are not available to
3024002e08dSkettenis * the non-secure OS.
303d7e3db9cSkettenis */
304d7e3db9cSkettenis nipi = 0;
305d7e3db9cSkettenis for (i = 0; i < 16; i++) {
306d7e3db9cSkettenis int reg, oldreg;
307d7e3db9cSkettenis
308d7e3db9cSkettenis oldreg = bus_space_read_1(sc->sc_iot, sc->sc_d_ioh,
309d7e3db9cSkettenis ICD_IPRn(i));
310d7e3db9cSkettenis bus_space_write_1(sc->sc_iot, sc->sc_d_ioh, ICD_IPRn(i),
311d7e3db9cSkettenis oldreg ^ 0x20);
312d7e3db9cSkettenis
313d7e3db9cSkettenis /* if this interrupt is not usable, route will be zero */
314d7e3db9cSkettenis reg = bus_space_read_1(sc->sc_iot, sc->sc_d_ioh, ICD_IPRn(i));
315d7e3db9cSkettenis if (reg == oldreg)
316d7e3db9cSkettenis continue;
317d7e3db9cSkettenis
318d7e3db9cSkettenis /* return to original value, will be set when used */
319d7e3db9cSkettenis bus_space_write_1(sc->sc_iot, sc->sc_d_ioh, ICD_IPRn(i),
320d7e3db9cSkettenis oldreg);
321d7e3db9cSkettenis
322d7e3db9cSkettenis if (nipi == 0)
323d7e3db9cSkettenis printf(" ipi: %d", i);
324d7e3db9cSkettenis else
325d7e3db9cSkettenis printf(", %d", i);
326d7e3db9cSkettenis ipiirq[nipi++] = i;
3274002e08dSkettenis if (nipi == 3)
328d7e3db9cSkettenis break;
329d7e3db9cSkettenis }
330d7e3db9cSkettenis
331d7e3db9cSkettenis if (nipi == 0)
332d7e3db9cSkettenis panic ("no irq available for IPI");
333d7e3db9cSkettenis
334d7e3db9cSkettenis switch (nipi) {
335d7e3db9cSkettenis case 1:
336d7e3db9cSkettenis ampintc_intr_establish(ipiirq[0], IST_EDGE_RISING,
337789e88a4Spatrick IPL_IPI|IPL_MPSAFE, NULL, ampintc_ipi_combined, sc, "ipi");
338d7e3db9cSkettenis sc->sc_ipi_num[ARM_IPI_NOP] = ipiirq[0];
339d7e3db9cSkettenis sc->sc_ipi_num[ARM_IPI_DDB] = ipiirq[0];
3404002e08dSkettenis sc->sc_ipi_num[ARM_IPI_HALT] = ipiirq[0];
341d7e3db9cSkettenis break;
342d7e3db9cSkettenis case 2:
343d7e3db9cSkettenis ampintc_intr_establish(ipiirq[0], IST_EDGE_RISING,
344789e88a4Spatrick IPL_IPI|IPL_MPSAFE, NULL, ampintc_ipi_nop, sc, "ipinop");
345d7e3db9cSkettenis sc->sc_ipi_num[ARM_IPI_NOP] = ipiirq[0];
346d7e3db9cSkettenis ampintc_intr_establish(ipiirq[1], IST_EDGE_RISING,
3474002e08dSkettenis IPL_IPI|IPL_MPSAFE, NULL, ampintc_ipi_combined, sc, "ipi");
3484002e08dSkettenis sc->sc_ipi_num[ARM_IPI_DDB] = ipiirq[1];
3494002e08dSkettenis sc->sc_ipi_num[ARM_IPI_HALT] = ipiirq[1];
3504002e08dSkettenis break;
3514002e08dSkettenis case 3:
3524002e08dSkettenis ampintc_intr_establish(ipiirq[0], IST_EDGE_RISING,
3534002e08dSkettenis IPL_IPI|IPL_MPSAFE, NULL, ampintc_ipi_nop, sc, "ipinop");
3544002e08dSkettenis sc->sc_ipi_num[ARM_IPI_NOP] = ipiirq[0];
3554002e08dSkettenis ampintc_intr_establish(ipiirq[1], IST_EDGE_RISING,
356789e88a4Spatrick IPL_IPI|IPL_MPSAFE, NULL, ampintc_ipi_ddb, sc, "ipiddb");
357d7e3db9cSkettenis sc->sc_ipi_num[ARM_IPI_DDB] = ipiirq[1];
3584002e08dSkettenis ampintc_intr_establish(ipiirq[2], IST_EDGE_RISING,
3594002e08dSkettenis IPL_IPI|IPL_MPSAFE, NULL, ampintc_ipi_halt, sc, "ipihalt");
3604002e08dSkettenis sc->sc_ipi_num[ARM_IPI_HALT] = ipiirq[2];
361d7e3db9cSkettenis break;
362d7e3db9cSkettenis default:
363d7e3db9cSkettenis panic("nipi unexpected number %d", nipi);
364d7e3db9cSkettenis }
365d7e3db9cSkettenis
366d7e3db9cSkettenis intr_send_ipi_func = ampintc_send_ipi;
367d7e3db9cSkettenis #endif
368d7e3db9cSkettenis
369f24071e5Spatrick /* enable interrupts */
370f24071e5Spatrick bus_space_write_4(sc->sc_iot, sc->sc_d_ioh, ICD_DCR, 3);
371f24071e5Spatrick bus_space_write_4(sc->sc_iot, sc->sc_p_ioh, ICPICR, 1);
372daf2e6ccSkettenis intr_enable();
373f24071e5Spatrick
374f24071e5Spatrick sc->sc_ic.ic_node = faa->fa_node;
375f24071e5Spatrick sc->sc_ic.ic_cookie = self;
376f24071e5Spatrick sc->sc_ic.ic_establish = ampintc_intr_establish_fdt;
377f24071e5Spatrick sc->sc_ic.ic_disestablish = ampintc_intr_disestablish;
378d7e3db9cSkettenis sc->sc_ic.ic_route = ampintc_route_irq;
379d7e3db9cSkettenis sc->sc_ic.ic_cpu_enable = ampintc_cpuinit;
380452daaedSpatrick sc->sc_ic.ic_barrier = ampintc_intr_barrier;
381f24071e5Spatrick arm_intr_register_fdt(&sc->sc_ic);
382c17c7ba4Spatrick
383c17c7ba4Spatrick /* attach GICv2M frame controller */
384c17c7ba4Spatrick simplebus_attach(parent, &sc->sc_sbus.sc_dev, faa);
385f24071e5Spatrick }
386f24071e5Spatrick
3874002e08dSkettenis int
ampintc_activate(struct device * self,int act)3884002e08dSkettenis ampintc_activate(struct device *self, int act)
3894002e08dSkettenis {
3904002e08dSkettenis struct ampintc_softc *sc = (struct ampintc_softc *)self;
3914002e08dSkettenis struct cpu_info *ci;
3924002e08dSkettenis int irq, min;
3934002e08dSkettenis
3944002e08dSkettenis switch (act) {
3954002e08dSkettenis case DVACT_RESUME:
3964002e08dSkettenis for (irq = 0; irq < sc->sc_nintr; irq++) {
3974002e08dSkettenis ci = sc->sc_handler[irq].iq_ci;
3984002e08dSkettenis min = sc->sc_handler[irq].iq_irq_min;
3994002e08dSkettenis if (min != IPL_NONE) {
4004002e08dSkettenis ampintc_set_priority(irq, min);
4014002e08dSkettenis ampintc_intr_enable(irq);
4024002e08dSkettenis ampintc_route(irq, IRQ_ENABLE, ci);
4034002e08dSkettenis } else {
4044002e08dSkettenis ampintc_intr_disable(irq);
4054002e08dSkettenis }
4064002e08dSkettenis }
4074002e08dSkettenis
4084002e08dSkettenis /* enable interrupts */
4094002e08dSkettenis bus_space_write_4(sc->sc_iot, sc->sc_d_ioh, ICD_DCR, 3);
4104002e08dSkettenis bus_space_write_4(sc->sc_iot, sc->sc_p_ioh, ICPICR, 1);
4114002e08dSkettenis break;
4124002e08dSkettenis }
4134002e08dSkettenis
4144002e08dSkettenis return 0;
4154002e08dSkettenis }
4164002e08dSkettenis
4174002e08dSkettenis void
ampintc_init(struct ampintc_softc * sc)4184002e08dSkettenis ampintc_init(struct ampintc_softc *sc)
4194002e08dSkettenis {
4204002e08dSkettenis int i;
4214002e08dSkettenis
4224002e08dSkettenis /* Disable all interrupts, clear all pending */
4234002e08dSkettenis for (i = 0; i < sc->sc_nintr / 32; i++) {
4244002e08dSkettenis bus_space_write_4(sc->sc_iot, sc->sc_d_ioh,
4254002e08dSkettenis ICD_ICERn(i * 32), ~0);
4264002e08dSkettenis bus_space_write_4(sc->sc_iot, sc->sc_d_ioh,
4274002e08dSkettenis ICD_ICPRn(i * 32), ~0);
4284002e08dSkettenis }
4294002e08dSkettenis for (i = 0; i < sc->sc_nintr; i++) {
4304002e08dSkettenis /* lowest priority ?? */
4314002e08dSkettenis bus_space_write_1(sc->sc_iot, sc->sc_d_ioh, ICD_IPRn(i), 0xff);
4324002e08dSkettenis /* target no cpus */
4334002e08dSkettenis bus_space_write_1(sc->sc_iot, sc->sc_d_ioh, ICD_IPTRn(i), 0);
4344002e08dSkettenis }
4354002e08dSkettenis for (i = 2; i < sc->sc_nintr / 16; i++) {
4364002e08dSkettenis /* irq 32 - N */
4374002e08dSkettenis bus_space_write_4(sc->sc_iot, sc->sc_d_ioh,
4384002e08dSkettenis ICD_ICRn(i * 16), 0);
4394002e08dSkettenis }
4404002e08dSkettenis }
4414002e08dSkettenis
442f24071e5Spatrick void
ampintc_set_priority(int irq,int pri)443f24071e5Spatrick ampintc_set_priority(int irq, int pri)
444f24071e5Spatrick {
445f24071e5Spatrick struct ampintc_softc *sc = ampintc;
446f24071e5Spatrick uint32_t prival;
447f24071e5Spatrick
448f24071e5Spatrick /*
449f24071e5Spatrick * We only use 16 (13 really) interrupt priorities,
450f24071e5Spatrick * and a CPU is only required to implement bit 4-7 of each field
451f24071e5Spatrick * so shift into the top bits.
452f24071e5Spatrick * also low values are higher priority thus IPL_HIGH - pri
453f24071e5Spatrick */
454f24071e5Spatrick prival = (IPL_HIGH - pri) << ICMIPMR_SH;
455f24071e5Spatrick bus_space_write_1(sc->sc_iot, sc->sc_d_ioh, ICD_IPRn(irq), prival);
456f24071e5Spatrick }
457f24071e5Spatrick
458f24071e5Spatrick void
ampintc_setipl(int new)459f24071e5Spatrick ampintc_setipl(int new)
460f24071e5Spatrick {
461f24071e5Spatrick struct cpu_info *ci = curcpu();
462f24071e5Spatrick struct ampintc_softc *sc = ampintc;
463daf2e6ccSkettenis u_long psw;
464f24071e5Spatrick
465f24071e5Spatrick /* disable here is only to keep hardware in sync with ci->ci_cpl */
466daf2e6ccSkettenis psw = intr_disable();
467f24071e5Spatrick ci->ci_cpl = new;
468f24071e5Spatrick
469f24071e5Spatrick /* low values are higher priority thus IPL_HIGH - pri */
470f24071e5Spatrick bus_space_write_4(sc->sc_iot, sc->sc_p_ioh, ICPIPMR,
471f24071e5Spatrick (IPL_HIGH - new) << ICMIPMR_SH);
472daf2e6ccSkettenis intr_restore(psw);
473f24071e5Spatrick }
474f24071e5Spatrick
475f24071e5Spatrick void
ampintc_intr_enable(int irq)476f24071e5Spatrick ampintc_intr_enable(int irq)
477f24071e5Spatrick {
478f24071e5Spatrick struct ampintc_softc *sc = ampintc;
479f24071e5Spatrick
480f24071e5Spatrick #ifdef DEBUG
481f24071e5Spatrick printf("enable irq %d register %x bitmask %08x\n",
482f24071e5Spatrick irq, ICD_ISERn(irq), 1 << IRQ_TO_REG32BIT(irq));
483f24071e5Spatrick #endif
484f24071e5Spatrick
485f24071e5Spatrick bus_space_write_4(sc->sc_iot, sc->sc_d_ioh, ICD_ISERn(irq),
486f24071e5Spatrick 1 << IRQ_TO_REG32BIT(irq));
487f24071e5Spatrick }
488f24071e5Spatrick
489f24071e5Spatrick void
ampintc_intr_disable(int irq)490f24071e5Spatrick ampintc_intr_disable(int irq)
491f24071e5Spatrick {
492f24071e5Spatrick struct ampintc_softc *sc = ampintc;
493f24071e5Spatrick
494f24071e5Spatrick bus_space_write_4(sc->sc_iot, sc->sc_d_ioh, ICD_ICERn(irq),
495f24071e5Spatrick 1 << IRQ_TO_REG32BIT(irq));
496f24071e5Spatrick }
497f24071e5Spatrick
498edffca28Spatrick void
ampintc_intr_config(int irqno,int type)499edffca28Spatrick ampintc_intr_config(int irqno, int type)
500edffca28Spatrick {
501edffca28Spatrick struct ampintc_softc *sc = ampintc;
502edffca28Spatrick uint32_t ctrl;
503edffca28Spatrick
504edffca28Spatrick ctrl = bus_space_read_4(sc->sc_iot, sc->sc_d_ioh, ICD_ICRn(irqno));
505edffca28Spatrick
506edffca28Spatrick ctrl &= ~ICD_ICR_TRIG_MASK(irqno);
507edffca28Spatrick if (type == IST_EDGE_RISING)
508edffca28Spatrick ctrl |= ICD_ICR_TRIG_EDGE(irqno);
509edffca28Spatrick else
510edffca28Spatrick ctrl |= ICD_ICR_TRIG_LEVEL(irqno);
511edffca28Spatrick
512edffca28Spatrick bus_space_write_4(sc->sc_iot, sc->sc_d_ioh, ICD_ICRn(irqno), ctrl);
513edffca28Spatrick }
514f24071e5Spatrick
515f24071e5Spatrick void
ampintc_calc_mask(void)516f24071e5Spatrick ampintc_calc_mask(void)
517f24071e5Spatrick {
518f24071e5Spatrick struct ampintc_softc *sc = ampintc;
519f24071e5Spatrick int irq;
520f24071e5Spatrick
521de70244eSpatrick for (irq = 0; irq < sc->sc_nintr; irq++)
522de70244eSpatrick ampintc_calc_irq(sc, irq);
523de70244eSpatrick }
524de70244eSpatrick
525de70244eSpatrick void
ampintc_calc_irq(struct ampintc_softc * sc,int irq)526de70244eSpatrick ampintc_calc_irq(struct ampintc_softc *sc, int irq)
527de70244eSpatrick {
528de70244eSpatrick struct cpu_info *ci = sc->sc_handler[irq].iq_ci;
529de70244eSpatrick struct intrhand *ih;
530f24071e5Spatrick int max = IPL_NONE;
531f24071e5Spatrick int min = IPL_HIGH;
532de70244eSpatrick
533d7e3db9cSkettenis TAILQ_FOREACH(ih, &sc->sc_handler[irq].iq_list, ih_list) {
534f24071e5Spatrick if (ih->ih_ipl > max)
535f24071e5Spatrick max = ih->ih_ipl;
536f24071e5Spatrick
537f24071e5Spatrick if (ih->ih_ipl < min)
538f24071e5Spatrick min = ih->ih_ipl;
539f24071e5Spatrick }
540f24071e5Spatrick
541f24071e5Spatrick if (max == IPL_NONE)
542f24071e5Spatrick min = IPL_NONE;
543f24071e5Spatrick
544eb0c9b3cSpatrick if (sc->sc_handler[irq].iq_irq_max == max &&
545eb0c9b3cSpatrick sc->sc_handler[irq].iq_irq_min == min)
546de70244eSpatrick return;
547eb0c9b3cSpatrick
548eb0c9b3cSpatrick sc->sc_handler[irq].iq_irq_max = max;
549eb0c9b3cSpatrick sc->sc_handler[irq].iq_irq_min = min;
550eb0c9b3cSpatrick
551f24071e5Spatrick /* Enable interrupts at lower levels, clear -> enable */
552f24071e5Spatrick /* Set interrupt priority/enable */
553f24071e5Spatrick if (min != IPL_NONE) {
554f24071e5Spatrick ampintc_set_priority(irq, min);
555f24071e5Spatrick ampintc_intr_enable(irq);
5560d4f5be1Skettenis ampintc_route(irq, IRQ_ENABLE, ci);
557f24071e5Spatrick } else {
558f24071e5Spatrick ampintc_intr_disable(irq);
5590d4f5be1Skettenis ampintc_route(irq, IRQ_DISABLE, ci);
560f24071e5Spatrick }
561f24071e5Spatrick }
562f24071e5Spatrick
563f24071e5Spatrick void
ampintc_splx(int new)564f24071e5Spatrick ampintc_splx(int new)
565f24071e5Spatrick {
566f24071e5Spatrick struct cpu_info *ci = curcpu();
567f24071e5Spatrick
568f24071e5Spatrick if (ci->ci_ipending & arm_smask[new])
569f24071e5Spatrick arm_do_pending_intr(new);
570f24071e5Spatrick
571f24071e5Spatrick ampintc_setipl(new);
572f24071e5Spatrick }
573f24071e5Spatrick
574f24071e5Spatrick int
ampintc_spllower(int new)575f24071e5Spatrick ampintc_spllower(int new)
576f24071e5Spatrick {
577f24071e5Spatrick struct cpu_info *ci = curcpu();
578f24071e5Spatrick int old = ci->ci_cpl;
579f24071e5Spatrick ampintc_splx(new);
580f24071e5Spatrick return (old);
581f24071e5Spatrick }
582f24071e5Spatrick
583f24071e5Spatrick int
ampintc_splraise(int new)584f24071e5Spatrick ampintc_splraise(int new)
585f24071e5Spatrick {
586f24071e5Spatrick struct cpu_info *ci = curcpu();
587f24071e5Spatrick int old;
588f24071e5Spatrick old = ci->ci_cpl;
589f24071e5Spatrick
590f24071e5Spatrick /*
591f24071e5Spatrick * setipl must always be called because there is a race window
592f24071e5Spatrick * where the variable is updated before the mask is set
593f24071e5Spatrick * an interrupt occurs in that window without the mask always
594f24071e5Spatrick * being set, the hardware might not get updated on the next
595f24071e5Spatrick * splraise completely messing up spl protection.
596f24071e5Spatrick */
597f24071e5Spatrick if (old > new)
598f24071e5Spatrick new = old;
599f24071e5Spatrick
600f24071e5Spatrick ampintc_setipl(new);
601f24071e5Spatrick
602f24071e5Spatrick return (old);
603f24071e5Spatrick }
604f24071e5Spatrick
605f24071e5Spatrick
606f24071e5Spatrick uint32_t
ampintc_iack(void)607f24071e5Spatrick ampintc_iack(void)
608f24071e5Spatrick {
609f24071e5Spatrick uint32_t intid;
610f24071e5Spatrick struct ampintc_softc *sc = ampintc;
611f24071e5Spatrick
612f24071e5Spatrick intid = bus_space_read_4(sc->sc_iot, sc->sc_p_ioh, ICPIAR);
613f24071e5Spatrick
614f24071e5Spatrick return (intid);
615f24071e5Spatrick }
616f24071e5Spatrick
617f24071e5Spatrick void
ampintc_eoi(uint32_t eoi)618f24071e5Spatrick ampintc_eoi(uint32_t eoi)
619f24071e5Spatrick {
620f24071e5Spatrick struct ampintc_softc *sc = ampintc;
621f24071e5Spatrick
622f24071e5Spatrick bus_space_write_4(sc->sc_iot, sc->sc_p_ioh, ICPEOIR, eoi);
623f24071e5Spatrick }
624f24071e5Spatrick
625f24071e5Spatrick void
ampintc_route(int irq,int enable,struct cpu_info * ci)6260d4f5be1Skettenis ampintc_route(int irq, int enable, struct cpu_info *ci)
627f24071e5Spatrick {
628f24071e5Spatrick struct ampintc_softc *sc = ampintc;
6290d4f5be1Skettenis uint8_t mask, val;
6300d4f5be1Skettenis
6310d4f5be1Skettenis KASSERT(ci->ci_cpuid <= ICD_ICTR_CPU_M);
6320d4f5be1Skettenis mask = sc->sc_cpu_mask[ci->ci_cpuid];
633f24071e5Spatrick
634f24071e5Spatrick val = bus_space_read_1(sc->sc_iot, sc->sc_d_ioh, ICD_IPTRn(irq));
635f24071e5Spatrick if (enable == IRQ_ENABLE)
6360d4f5be1Skettenis val |= mask;
637f24071e5Spatrick else
6380d4f5be1Skettenis val &= ~mask;
639f24071e5Spatrick bus_space_write_1(sc->sc_iot, sc->sc_d_ioh, ICD_IPTRn(irq), val);
640f24071e5Spatrick }
641f24071e5Spatrick
642f24071e5Spatrick void
ampintc_cpuinit(void)6432f346d2bSkettenis ampintc_cpuinit(void)
644d7e3db9cSkettenis {
645d7e3db9cSkettenis struct ampintc_softc *sc = ampintc;
646de70244eSpatrick int i, irq;
647d7e3db9cSkettenis
648d7e3db9cSkettenis /* XXX - this is the only cpu specific call to set this */
649d7e3db9cSkettenis if (sc->sc_cpu_mask[cpu_number()] == 0) {
650d7e3db9cSkettenis for (i = 0; i < 32; i++) {
651d7e3db9cSkettenis int cpumask =
652d7e3db9cSkettenis bus_space_read_1(sc->sc_iot, sc->sc_d_ioh,
653d7e3db9cSkettenis ICD_IPTRn(i));
654d7e3db9cSkettenis
655d7e3db9cSkettenis if (cpumask != 0) {
656d7e3db9cSkettenis sc->sc_cpu_mask[cpu_number()] = cpumask;
657d7e3db9cSkettenis break;
658d7e3db9cSkettenis }
659d7e3db9cSkettenis }
660d7e3db9cSkettenis }
661d7e3db9cSkettenis
662d7e3db9cSkettenis if (sc->sc_cpu_mask[cpu_number()] == 0)
663d7e3db9cSkettenis panic("could not determine cpu target mask");
664de70244eSpatrick
665de70244eSpatrick for (irq = 0; irq < sc->sc_nintr; irq++) {
666de70244eSpatrick if (sc->sc_handler[irq].iq_ci != curcpu())
667de70244eSpatrick continue;
668de70244eSpatrick if (sc->sc_handler[irq].iq_irq_min != IPL_NONE)
669de70244eSpatrick ampintc_route(irq, IRQ_ENABLE, curcpu());
670de70244eSpatrick else
671de70244eSpatrick ampintc_route(irq, IRQ_DISABLE, curcpu());
672de70244eSpatrick }
6734002e08dSkettenis
6744002e08dSkettenis /*
6754002e08dSkettenis * If a secondary CPU is turned off from an IPI handler and
6764002e08dSkettenis * the GIC did not go through a full reset (for example when
6774002e08dSkettenis * we fail to suspend) the IPI might still be active. So
6784002e08dSkettenis * signal EOI here to make sure new interrupts will be
6794002e08dSkettenis * serviced.
6804002e08dSkettenis */
6814002e08dSkettenis ampintc_eoi(sc->sc_ipi_num[ARM_IPI_HALT]);
682d7e3db9cSkettenis }
683d7e3db9cSkettenis
684d7e3db9cSkettenis void
ampintc_route_irq(void * v,int enable,struct cpu_info * ci)685d7e3db9cSkettenis ampintc_route_irq(void *v, int enable, struct cpu_info *ci)
686d7e3db9cSkettenis {
687d7e3db9cSkettenis struct ampintc_softc *sc = ampintc;
688d7e3db9cSkettenis struct intrhand *ih = v;
689d7e3db9cSkettenis
690d7e3db9cSkettenis bus_space_write_4(sc->sc_iot, sc->sc_p_ioh, ICPICR, 1);
691d7e3db9cSkettenis bus_space_write_4(sc->sc_iot, sc->sc_d_ioh, ICD_ICRn(ih->ih_irq), 0);
692d7e3db9cSkettenis if (enable) {
693d7e3db9cSkettenis ampintc_set_priority(ih->ih_irq,
694eb0c9b3cSpatrick sc->sc_handler[ih->ih_irq].iq_irq_min);
695d7e3db9cSkettenis ampintc_intr_enable(ih->ih_irq);
696d7e3db9cSkettenis }
697d7e3db9cSkettenis
698d7e3db9cSkettenis ampintc_route(ih->ih_irq, enable, ci);
699d7e3db9cSkettenis }
700d7e3db9cSkettenis
701d7e3db9cSkettenis void
ampintc_intr_barrier(void * cookie)702452daaedSpatrick ampintc_intr_barrier(void *cookie)
703452daaedSpatrick {
704452daaedSpatrick struct intrhand *ih = cookie;
705452daaedSpatrick
706452daaedSpatrick sched_barrier(ih->ih_ci);
707452daaedSpatrick }
708452daaedSpatrick
709452daaedSpatrick void
ampintc_run_handler(struct intrhand * ih,void * frame,int s)7107b137141Skettenis ampintc_run_handler(struct intrhand *ih, void *frame, int s)
7117b137141Skettenis {
7127b137141Skettenis void *arg;
7137b137141Skettenis int handled;
7147b137141Skettenis
7157b137141Skettenis #ifdef MULTIPROCESSOR
7167b137141Skettenis int need_lock;
7177b137141Skettenis
7187b137141Skettenis if (ih->ih_flags & IPL_MPSAFE)
7197b137141Skettenis need_lock = 0;
7207b137141Skettenis else
7217b137141Skettenis need_lock = s < IPL_SCHED;
7227b137141Skettenis
7237b137141Skettenis if (need_lock)
7247b137141Skettenis KERNEL_LOCK();
7257b137141Skettenis #endif
7267b137141Skettenis
727f9b35d7eSkettenis if (ih->ih_arg)
7287b137141Skettenis arg = ih->ih_arg;
7297b137141Skettenis else
7307b137141Skettenis arg = frame;
7317b137141Skettenis
7327b137141Skettenis handled = ih->ih_func(arg);
7337b137141Skettenis if (handled)
7347b137141Skettenis ih->ih_count.ec_count++;
7357b137141Skettenis
7367b137141Skettenis #ifdef MULTIPROCESSOR
7377b137141Skettenis if (need_lock)
7387b137141Skettenis KERNEL_UNLOCK();
7397b137141Skettenis #endif
7407b137141Skettenis }
7417b137141Skettenis
7427b137141Skettenis void
ampintc_irq_handler(void * frame)743f24071e5Spatrick ampintc_irq_handler(void *frame)
744f24071e5Spatrick {
745f24071e5Spatrick struct ampintc_softc *sc = ampintc;
746f24071e5Spatrick struct intrhand *ih;
747f24071e5Spatrick uint32_t iack_val;
7487b137141Skettenis int irq, pri, s;
749f24071e5Spatrick
750f24071e5Spatrick iack_val = ampintc_iack();
751f24071e5Spatrick #ifdef DEBUG_INTC
752f24071e5Spatrick if (iack_val != 27)
753f24071e5Spatrick printf("irq %d fired\n", iack_val);
754f24071e5Spatrick else {
755f24071e5Spatrick static int cnt = 0;
756f24071e5Spatrick if ((cnt++ % 100) == 0) {
757f24071e5Spatrick printf("irq %d fired * _100\n", iack_val);
758f24071e5Spatrick #ifdef DDB
759e97088d6Smpi db_enter();
760f24071e5Spatrick #endif
761f24071e5Spatrick }
762f24071e5Spatrick
763f24071e5Spatrick }
764f24071e5Spatrick #endif
765f24071e5Spatrick
7668daa750dSpatrick irq = iack_val & ICPIAR_IRQ_M;
7678daa750dSpatrick
7688daa750dSpatrick if (irq == 1023) {
769f24071e5Spatrick sc->sc_spur.ec_count++;
770f24071e5Spatrick return;
771f24071e5Spatrick }
7728daa750dSpatrick
7738daa750dSpatrick if (irq >= sc->sc_nintr)
7748daa750dSpatrick return;
775f24071e5Spatrick
776eb0c9b3cSpatrick pri = sc->sc_handler[irq].iq_irq_max;
777f24071e5Spatrick s = ampintc_splraise(pri);
778daf2e6ccSkettenis intr_enable();
7797b137141Skettenis TAILQ_FOREACH(ih, &sc->sc_handler[irq].iq_list, ih_list) {
7807b137141Skettenis ampintc_run_handler(ih, frame, s);
781f24071e5Spatrick }
7827b137141Skettenis intr_disable();
783f24071e5Spatrick ampintc_eoi(iack_val);
784f24071e5Spatrick
785f24071e5Spatrick ampintc_splx(s);
786f24071e5Spatrick }
787f24071e5Spatrick
788f24071e5Spatrick void *
ampintc_intr_establish_fdt(void * cookie,int * cell,int level,struct cpu_info * ci,int (* func)(void *),void * arg,char * name)789f24071e5Spatrick ampintc_intr_establish_fdt(void *cookie, int *cell, int level,
790789e88a4Spatrick struct cpu_info *ci, int (*func)(void *), void *arg, char *name)
791f24071e5Spatrick {
792f24071e5Spatrick struct ampintc_softc *sc = (struct ampintc_softc *)cookie;
793f24071e5Spatrick int irq;
794edffca28Spatrick int type;
795f24071e5Spatrick
796f24071e5Spatrick /* 2nd cell contains the interrupt number */
797f24071e5Spatrick irq = cell[1];
798f24071e5Spatrick
799f24071e5Spatrick /* 1st cell contains type: 0 SPI (32-X), 1 PPI (16-31) */
800f24071e5Spatrick if (cell[0] == 0)
801f24071e5Spatrick irq += 32;
802f24071e5Spatrick else if (cell[0] == 1)
803f24071e5Spatrick irq += 16;
804f24071e5Spatrick else
805c17c7ba4Spatrick panic("%s: bogus interrupt type", sc->sc_sbus.sc_dev.dv_xname);
806f24071e5Spatrick
807edffca28Spatrick /* SPIs are only active-high level or low-to-high edge */
808edffca28Spatrick if (cell[2] & 0x3)
809edffca28Spatrick type = IST_EDGE_RISING;
810edffca28Spatrick else
811edffca28Spatrick type = IST_LEVEL_HIGH;
812edffca28Spatrick
813789e88a4Spatrick return ampintc_intr_establish(irq, type, level, ci, func, arg, name);
814f24071e5Spatrick }
815f24071e5Spatrick
816f24071e5Spatrick void *
ampintc_intr_establish(int irqno,int type,int level,struct cpu_info * ci,int (* func)(void *),void * arg,char * name)817789e88a4Spatrick ampintc_intr_establish(int irqno, int type, int level, struct cpu_info *ci,
818789e88a4Spatrick int (*func)(void *), void *arg, char *name)
819f24071e5Spatrick {
820f24071e5Spatrick struct ampintc_softc *sc = ampintc;
821f24071e5Spatrick struct intrhand *ih;
822daf2e6ccSkettenis u_long psw;
823f24071e5Spatrick
824f24071e5Spatrick if (irqno < 0 || irqno >= sc->sc_nintr)
825f24071e5Spatrick panic("ampintc_intr_establish: bogus irqnumber %d: %s",
826f24071e5Spatrick irqno, name);
827f24071e5Spatrick
828789e88a4Spatrick if (ci == NULL)
829789e88a4Spatrick ci = &cpu_info_primary;
830789e88a4Spatrick
831d7e3db9cSkettenis if (irqno < 16) {
832d7e3db9cSkettenis /* SGI are only EDGE */
833d7e3db9cSkettenis type = IST_EDGE_RISING;
834d7e3db9cSkettenis } else if (irqno < 32) {
835d7e3db9cSkettenis /* PPI are only LEVEL */
836d7e3db9cSkettenis type = IST_LEVEL_HIGH;
837d7e3db9cSkettenis }
838d7e3db9cSkettenis
839f24071e5Spatrick ih = malloc(sizeof(*ih), M_DEVBUF, M_WAITOK);
840f24071e5Spatrick ih->ih_func = func;
841f24071e5Spatrick ih->ih_arg = arg;
842d7e3db9cSkettenis ih->ih_ipl = level & IPL_IRQMASK;
843d7e3db9cSkettenis ih->ih_flags = level & IPL_FLAGMASK;
844f24071e5Spatrick ih->ih_irq = irqno;
845f24071e5Spatrick ih->ih_name = name;
846452daaedSpatrick ih->ih_ci = ci;
847f24071e5Spatrick
848daf2e6ccSkettenis psw = intr_disable();
849f24071e5Spatrick
850de70244eSpatrick if (!TAILQ_EMPTY(&sc->sc_handler[irqno].iq_list) &&
851de70244eSpatrick sc->sc_handler[irqno].iq_ci != ci) {
852de70244eSpatrick free(ih, M_DEVBUF, sizeof(*ih));
853daf2e6ccSkettenis intr_restore(psw);
854de70244eSpatrick return NULL;
855de70244eSpatrick }
856de70244eSpatrick
857d7e3db9cSkettenis TAILQ_INSERT_TAIL(&sc->sc_handler[irqno].iq_list, ih, ih_list);
858de70244eSpatrick sc->sc_handler[irqno].iq_ci = ci;
859f24071e5Spatrick
860f24071e5Spatrick if (name != NULL)
861f24071e5Spatrick evcount_attach(&ih->ih_count, name, &ih->ih_irq);
862f24071e5Spatrick
863f24071e5Spatrick #ifdef DEBUG_INTC
864f24071e5Spatrick printf("ampintc_intr_establish irq %d level %d [%s]\n", irqno, level,
865f24071e5Spatrick name);
866f24071e5Spatrick #endif
867edffca28Spatrick
868edffca28Spatrick ampintc_intr_config(irqno, type);
869f24071e5Spatrick ampintc_calc_mask();
870f24071e5Spatrick
871daf2e6ccSkettenis intr_restore(psw);
872f24071e5Spatrick return (ih);
873f24071e5Spatrick }
874f24071e5Spatrick
875f24071e5Spatrick void
ampintc_intr_disestablish(void * cookie)876f24071e5Spatrick ampintc_intr_disestablish(void *cookie)
877f24071e5Spatrick {
878f24071e5Spatrick struct ampintc_softc *sc = ampintc;
879f24071e5Spatrick struct intrhand *ih = cookie;
880daf2e6ccSkettenis u_long psw;
881f24071e5Spatrick
882f24071e5Spatrick #ifdef DEBUG_INTC
883f24071e5Spatrick printf("ampintc_intr_disestablish irq %d level %d [%s]\n",
884f24071e5Spatrick ih->ih_irq, ih->ih_ipl, ih->ih_name);
885f24071e5Spatrick #endif
886f24071e5Spatrick
887daf2e6ccSkettenis psw = intr_disable();
888f24071e5Spatrick
889d7e3db9cSkettenis TAILQ_REMOVE(&sc->sc_handler[ih->ih_irq].iq_list, ih, ih_list);
890f24071e5Spatrick if (ih->ih_name != NULL)
891f24071e5Spatrick evcount_detach(&ih->ih_count);
892f24071e5Spatrick
893f24071e5Spatrick ampintc_calc_mask();
894f24071e5Spatrick
895daf2e6ccSkettenis intr_restore(psw);
896daf2e6ccSkettenis
897daf2e6ccSkettenis free(ih, M_DEVBUF, sizeof(*ih));
898f24071e5Spatrick }
899f24071e5Spatrick
900f24071e5Spatrick const char *
ampintc_intr_string(void * cookie)901f24071e5Spatrick ampintc_intr_string(void *cookie)
902f24071e5Spatrick {
903f24071e5Spatrick struct intrhand *ih = (struct intrhand *)cookie;
904f24071e5Spatrick static char irqstr[1 + sizeof("ampintc irq ") + 4];
905f24071e5Spatrick
906f24071e5Spatrick snprintf(irqstr, sizeof irqstr, "ampintc irq %d", ih->ih_irq);
907f24071e5Spatrick return irqstr;
908f24071e5Spatrick }
909c17c7ba4Spatrick
910c17c7ba4Spatrick /*
911c17c7ba4Spatrick * GICv2m frame controller for MSI interrupts.
912c17c7ba4Spatrick */
913c17c7ba4Spatrick #define GICV2M_TYPER 0x008
914c17c7ba4Spatrick #define GICV2M_TYPER_SPI_BASE(x) (((x) >> 16) & 0x3ff)
915c17c7ba4Spatrick #define GICV2M_TYPER_SPI_COUNT(x) (((x) >> 0) & 0x3ff)
916c17c7ba4Spatrick #define GICV2M_SETSPI_NS 0x040
917c17c7ba4Spatrick
918c17c7ba4Spatrick int ampintc_msi_match(struct device *, void *, void *);
919c17c7ba4Spatrick void ampintc_msi_attach(struct device *, struct device *, void *);
920c17c7ba4Spatrick void *ampintc_intr_establish_msi(void *, uint64_t *, uint64_t *,
921789e88a4Spatrick int , struct cpu_info *, int (*)(void *), void *, char *);
922c17c7ba4Spatrick void ampintc_intr_disestablish_msi(void *);
923452daaedSpatrick void ampintc_intr_barrier_msi(void *);
924c17c7ba4Spatrick
925c17c7ba4Spatrick struct ampintc_msi_softc {
926c17c7ba4Spatrick struct device sc_dev;
927c17c7ba4Spatrick bus_space_tag_t sc_iot;
928c17c7ba4Spatrick bus_space_handle_t sc_ioh;
929c50a818aSpatrick int sc_node;
930c17c7ba4Spatrick paddr_t sc_addr;
931c17c7ba4Spatrick int sc_bspi;
932c17c7ba4Spatrick int sc_nspi;
933c17c7ba4Spatrick void **sc_spi;
934c17c7ba4Spatrick struct interrupt_controller sc_ic;
935c17c7ba4Spatrick };
936c17c7ba4Spatrick
9379fdf0c62Smpi const struct cfattach ampintcmsi_ca = {
938c17c7ba4Spatrick sizeof (struct ampintc_msi_softc), ampintc_msi_match, ampintc_msi_attach
939c17c7ba4Spatrick };
940c17c7ba4Spatrick
941c17c7ba4Spatrick struct cfdriver ampintcmsi_cd = {
942c17c7ba4Spatrick NULL, "ampintcmsi", DV_DULL
943c17c7ba4Spatrick };
944c17c7ba4Spatrick
945c17c7ba4Spatrick int
ampintc_msi_match(struct device * parent,void * cfdata,void * aux)946c17c7ba4Spatrick ampintc_msi_match(struct device *parent, void *cfdata, void *aux)
947c17c7ba4Spatrick {
948c17c7ba4Spatrick struct fdt_attach_args *faa = aux;
949c17c7ba4Spatrick
950c17c7ba4Spatrick return OF_is_compatible(faa->fa_node, "arm,gic-v2m-frame");
951c17c7ba4Spatrick }
952c17c7ba4Spatrick
953c17c7ba4Spatrick void
ampintc_msi_attach(struct device * parent,struct device * self,void * aux)954c17c7ba4Spatrick ampintc_msi_attach(struct device *parent, struct device *self, void *aux)
955c17c7ba4Spatrick {
956c17c7ba4Spatrick struct ampintc_msi_softc *sc = (struct ampintc_msi_softc *)self;
957c17c7ba4Spatrick struct fdt_attach_args *faa = aux;
958c17c7ba4Spatrick uint32_t typer;
959c17c7ba4Spatrick
960c50a818aSpatrick sc->sc_node = faa->fa_node;
961c17c7ba4Spatrick sc->sc_iot = faa->fa_iot;
962c17c7ba4Spatrick if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
963c17c7ba4Spatrick faa->fa_reg[0].size, 0, &sc->sc_ioh))
964c17c7ba4Spatrick panic("%s: bus_space_map failed!", __func__);
965c17c7ba4Spatrick
966c17c7ba4Spatrick /* XXX: Hack to retrieve the physical address (from a CPU PoV). */
967c17c7ba4Spatrick if (!pmap_extract(pmap_kernel(), sc->sc_ioh, &sc->sc_addr)) {
968c17c7ba4Spatrick printf(": cannot retrieve msi addr\n");
969c17c7ba4Spatrick return;
970c17c7ba4Spatrick }
971c17c7ba4Spatrick
972c17c7ba4Spatrick typer = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GICV2M_TYPER);
973c17c7ba4Spatrick sc->sc_bspi = GICV2M_TYPER_SPI_BASE(typer);
974c17c7ba4Spatrick sc->sc_nspi = GICV2M_TYPER_SPI_COUNT(typer);
975c17c7ba4Spatrick
976c17c7ba4Spatrick sc->sc_bspi = OF_getpropint(faa->fa_node,
977c17c7ba4Spatrick "arm,msi-base-spi", sc->sc_bspi);
978c17c7ba4Spatrick sc->sc_nspi = OF_getpropint(faa->fa_node,
979c17c7ba4Spatrick "arm,msi-num-spis", sc->sc_nspi);
980c17c7ba4Spatrick
981c17c7ba4Spatrick printf(": nspi %d\n", sc->sc_nspi);
982c17c7ba4Spatrick
98375baf936Skettenis sc->sc_spi = mallocarray(sc->sc_nspi, sizeof(void *), M_DEVBUF,
984c17c7ba4Spatrick M_WAITOK|M_ZERO);
985c17c7ba4Spatrick
986c17c7ba4Spatrick sc->sc_ic.ic_node = faa->fa_node;
987c17c7ba4Spatrick sc->sc_ic.ic_cookie = sc;
988c17c7ba4Spatrick sc->sc_ic.ic_establish_msi = ampintc_intr_establish_msi;
989c17c7ba4Spatrick sc->sc_ic.ic_disestablish = ampintc_intr_disestablish_msi;
990452daaedSpatrick sc->sc_ic.ic_barrier = ampintc_intr_barrier_msi;
991c17c7ba4Spatrick arm_intr_register_fdt(&sc->sc_ic);
992c17c7ba4Spatrick }
993c17c7ba4Spatrick
994c17c7ba4Spatrick void *
ampintc_intr_establish_msi(void * self,uint64_t * addr,uint64_t * data,int level,struct cpu_info * ci,int (* func)(void *),void * arg,char * name)995c17c7ba4Spatrick ampintc_intr_establish_msi(void *self, uint64_t *addr, uint64_t *data,
996789e88a4Spatrick int level, struct cpu_info *ci, int (*func)(void *), void *arg, char *name)
997c17c7ba4Spatrick {
998c17c7ba4Spatrick struct ampintc_msi_softc *sc = (struct ampintc_msi_softc *)self;
999c50a818aSpatrick extern LIST_HEAD(, interrupt_controller) interrupt_controllers;
1000c50a818aSpatrick struct interrupt_controller *ic;
1001c50a818aSpatrick struct machine_intr_handle *ih;
1002c17c7ba4Spatrick void *cookie;
1003c50a818aSpatrick int cells[3];
1004c17c7ba4Spatrick int i;
1005c17c7ba4Spatrick
1006c50a818aSpatrick LIST_FOREACH(ic, &interrupt_controllers, ic_list) {
1007c50a818aSpatrick if (ic->ic_node == OF_parent(sc->sc_node))
1008c50a818aSpatrick break;
1009c50a818aSpatrick }
1010c50a818aSpatrick if (ic == NULL)
1011c50a818aSpatrick return NULL;
1012c50a818aSpatrick
1013c50a818aSpatrick cells[0] = 0; /* SPI */
1014c50a818aSpatrick cells[2] = 1; /* Edge-Rising */
1015c50a818aSpatrick
1016c17c7ba4Spatrick for (i = 0; i < sc->sc_nspi; i++) {
1017c17c7ba4Spatrick if (sc->sc_spi[i] != NULL)
1018c17c7ba4Spatrick continue;
1019c17c7ba4Spatrick
1020c50a818aSpatrick cells[1] = sc->sc_bspi + i - 32;
1021c50a818aSpatrick cookie = ic->ic_establish(ic->ic_cookie, cells,
1022c50a818aSpatrick level, ci, func, arg, name);
1023c17c7ba4Spatrick if (cookie == NULL)
1024c17c7ba4Spatrick return NULL;
1025c17c7ba4Spatrick
1026c50a818aSpatrick ih = malloc(sizeof(*ih), M_DEVBUF, M_WAITOK);
1027c50a818aSpatrick ih->ih_ic = ic;
1028c50a818aSpatrick ih->ih_ih = cookie;
1029c50a818aSpatrick
1030c17c7ba4Spatrick *addr = sc->sc_addr + GICV2M_SETSPI_NS;
1031895dad33Skettenis *data = sc->sc_bspi + i;
1032c50a818aSpatrick sc->sc_spi[i] = ih;
1033c17c7ba4Spatrick return &sc->sc_spi[i];
1034c17c7ba4Spatrick }
1035c17c7ba4Spatrick
1036c17c7ba4Spatrick return NULL;
1037c17c7ba4Spatrick }
1038c17c7ba4Spatrick
1039c17c7ba4Spatrick void
ampintc_intr_disestablish_msi(void * cookie)1040c17c7ba4Spatrick ampintc_intr_disestablish_msi(void *cookie)
1041c17c7ba4Spatrick {
1042c50a818aSpatrick fdt_intr_disestablish(*(void **)cookie);
1043c17c7ba4Spatrick *(void **)cookie = NULL;
1044c17c7ba4Spatrick }
1045d7e3db9cSkettenis
1046452daaedSpatrick void
ampintc_intr_barrier_msi(void * cookie)1047452daaedSpatrick ampintc_intr_barrier_msi(void *cookie)
1048452daaedSpatrick {
1049beae6a4fSpatrick intr_barrier(*(void **)cookie);
1050452daaedSpatrick }
1051452daaedSpatrick
1052d7e3db9cSkettenis #ifdef MULTIPROCESSOR
1053d7e3db9cSkettenis int
ampintc_ipi_ddb(void * v)1054d7e3db9cSkettenis ampintc_ipi_ddb(void *v)
1055d7e3db9cSkettenis {
1056d7e3db9cSkettenis /* XXX */
1057a9a6a9f2Sderaadt #ifdef DDB
1058d7e3db9cSkettenis db_enter();
1059a9a6a9f2Sderaadt #endif
1060d7e3db9cSkettenis return 1;
1061d7e3db9cSkettenis }
1062d7e3db9cSkettenis
1063d7e3db9cSkettenis int
ampintc_ipi_halt(void * v)10644002e08dSkettenis ampintc_ipi_halt(void *v)
10654002e08dSkettenis {
10664002e08dSkettenis cpu_halt();
10674002e08dSkettenis return 1;
10684002e08dSkettenis }
10694002e08dSkettenis
10704002e08dSkettenis int
ampintc_ipi_nop(void * v)1071d7e3db9cSkettenis ampintc_ipi_nop(void *v)
1072d7e3db9cSkettenis {
1073d7e3db9cSkettenis /* Nothing to do here, just enough to wake up from WFI */
1074d7e3db9cSkettenis return 1;
1075d7e3db9cSkettenis }
1076d7e3db9cSkettenis
1077d7e3db9cSkettenis int
ampintc_ipi_combined(void * v)1078d7e3db9cSkettenis ampintc_ipi_combined(void *v)
1079d7e3db9cSkettenis {
1080d7e3db9cSkettenis struct ampintc_softc *sc = (struct ampintc_softc *)v;
1081d7e3db9cSkettenis
1082d7e3db9cSkettenis if (sc->sc_ipi_reason[cpu_number()] == ARM_IPI_DDB) {
1083d7e3db9cSkettenis sc->sc_ipi_reason[cpu_number()] = ARM_IPI_NOP;
1084d7e3db9cSkettenis return ampintc_ipi_ddb(v);
10854002e08dSkettenis } else if (sc->sc_ipi_reason[cpu_number()] == ARM_IPI_HALT) {
10864002e08dSkettenis sc->sc_ipi_reason[cpu_number()] = ARM_IPI_NOP;
10874002e08dSkettenis return ampintc_ipi_halt(v);
1088d7e3db9cSkettenis } else {
1089d7e3db9cSkettenis return ampintc_ipi_nop(v);
1090d7e3db9cSkettenis }
1091d7e3db9cSkettenis }
1092d7e3db9cSkettenis
1093d7e3db9cSkettenis void
ampintc_send_ipi(struct cpu_info * ci,int id)1094d7e3db9cSkettenis ampintc_send_ipi(struct cpu_info *ci, int id)
1095d7e3db9cSkettenis {
1096d7e3db9cSkettenis struct ampintc_softc *sc = ampintc;
1097d7e3db9cSkettenis int sendmask;
1098d7e3db9cSkettenis
1099d7e3db9cSkettenis if (ci == curcpu() && id == ARM_IPI_NOP)
1100d7e3db9cSkettenis return;
1101d7e3db9cSkettenis
11024002e08dSkettenis /* never overwrite IPI_DDB or IPI_HALT with IPI_NOP */
11034002e08dSkettenis if (id == ARM_IPI_DDB || id == ARM_IPI_HALT)
1104d7e3db9cSkettenis sc->sc_ipi_reason[ci->ci_cpuid] = id;
1105d7e3db9cSkettenis
1106d7e3db9cSkettenis /* currently will only send to one cpu */
1107cb3eb802Skettenis sendmask = sc->sc_cpu_mask[ci->ci_cpuid] << 16;
1108d7e3db9cSkettenis sendmask |= sc->sc_ipi_num[id];
1109d7e3db9cSkettenis
1110d7e3db9cSkettenis bus_space_write_4(sc->sc_iot, sc->sc_d_ioh, ICD_SGIR, sendmask);
1111d7e3db9cSkettenis }
1112d7e3db9cSkettenis #endif
1113