1*94673892Sjsg /* $OpenBSD: ampintc.c,v 1.32 2023/09/22 01:10:43 jsg Exp $ */
2027018efSpatrick /*
3027018efSpatrick * Copyright (c) 2007,2009,2011 Dale Rahn <drahn@openbsd.org>
4027018efSpatrick *
5027018efSpatrick * Permission to use, copy, modify, and distribute this software for any
6027018efSpatrick * purpose with or without fee is hereby granted, provided that the above
7027018efSpatrick * copyright notice and this permission notice appear in all copies.
8027018efSpatrick *
9027018efSpatrick * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10027018efSpatrick * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11027018efSpatrick * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12027018efSpatrick * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13027018efSpatrick * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14027018efSpatrick * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15027018efSpatrick * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16027018efSpatrick */
17027018efSpatrick
18027018efSpatrick /*
19027018efSpatrick * This driver implements the interrupt controller as specified in
20027018efSpatrick * DDI0407E_cortex_a9_mpcore_r2p0_trm with the
21027018efSpatrick * IHI0048A_gic_architecture_spec_v1_0 underlying specification
22027018efSpatrick */
23027018efSpatrick #include <sys/param.h>
24027018efSpatrick #include <sys/systm.h>
25027018efSpatrick #include <sys/queue.h>
26027018efSpatrick #include <sys/malloc.h>
27027018efSpatrick #include <sys/device.h>
28027018efSpatrick #include <sys/evcount.h>
296fc522b1Skettenis
3023a4b636Spatrick #include <uvm/uvm_extern.h>
3123a4b636Spatrick
32027018efSpatrick #include <machine/bus.h>
336fc522b1Skettenis #include <machine/fdt.h>
346fc522b1Skettenis
356fc522b1Skettenis #include <arm/cpufunc.h>
36027018efSpatrick
376fc522b1Skettenis #include <dev/ofw/fdt.h>
386fc522b1Skettenis #include <dev/ofw/openfirm.h>
39d730c68aSrapha
40*94673892Sjsg #include <machine/simplebusvar.h>
4123a4b636Spatrick
42027018efSpatrick /* registers */
43027018efSpatrick #define ICD_DCR 0x000
44027018efSpatrick #define ICD_DCR_ES 0x00000001
45027018efSpatrick #define ICD_DCR_ENS 0x00000002
46027018efSpatrick
47027018efSpatrick #define ICD_ICTR 0x004
48027018efSpatrick #define ICD_ICTR_LSPI_SH 11
49027018efSpatrick #define ICD_ICTR_LSPI_M 0x1f
50027018efSpatrick #define ICD_ICTR_CPU_SH 5
51027018efSpatrick #define ICD_ICTR_CPU_M 0x07
52027018efSpatrick #define ICD_ICTR_ITL_SH 0
53027018efSpatrick #define ICD_ICTR_ITL_M 0x1f
54027018efSpatrick #define ICD_IDIR 0x008
55027018efSpatrick #define ICD_DIR_PROD_SH 24
56027018efSpatrick #define ICD_DIR_PROD_M 0xff
57027018efSpatrick #define ICD_DIR_REV_SH 12
58027018efSpatrick #define ICD_DIR_REV_M 0xfff
59027018efSpatrick #define ICD_DIR_IMP_SH 0
60027018efSpatrick #define ICD_DIR_IMP_M 0xfff
61027018efSpatrick
6218a1c566Spatrick #define IRQ_TO_REG32(i) (((i) >> 5) & 0x1f)
63027018efSpatrick #define IRQ_TO_REG32BIT(i) ((i) & 0x1f)
6418a1c566Spatrick #define IRQ_TO_REG4(i) (((i) >> 2) & 0xff)
65027018efSpatrick #define IRQ_TO_REG4BIT(i) ((i) & 0x3)
6618a1c566Spatrick #define IRQ_TO_REG16(i) (((i) >> 4) & 0x3f)
6723a4b636Spatrick #define IRQ_TO_REG16BIT(i) ((i) & 0xf)
68027018efSpatrick #define IRQ_TO_REGBIT_S(i) 8
69027018efSpatrick #define IRQ_TO_REG4BIT_M(i) 8
70027018efSpatrick
71027018efSpatrick #define ICD_ISRn(i) (0x080 + (IRQ_TO_REG32(i) * 4))
72027018efSpatrick #define ICD_ISERn(i) (0x100 + (IRQ_TO_REG32(i) * 4))
73027018efSpatrick #define ICD_ICERn(i) (0x180 + (IRQ_TO_REG32(i) * 4))
74027018efSpatrick #define ICD_ISPRn(i) (0x200 + (IRQ_TO_REG32(i) * 4))
75027018efSpatrick #define ICD_ICPRn(i) (0x280 + (IRQ_TO_REG32(i) * 4))
76027018efSpatrick #define ICD_ABRn(i) (0x300 + (IRQ_TO_REG32(i) * 4))
77027018efSpatrick #define ICD_IPRn(i) (0x400 + (i))
78027018efSpatrick #define ICD_IPTRn(i) (0x800 + (i))
79027018efSpatrick #define ICD_ICRn(i) (0xC00 + (IRQ_TO_REG16(i) * 4))
8023a4b636Spatrick #define ICD_ICR_TRIG_LEVEL(i) (0x0 << (IRQ_TO_REG16BIT(i) * 2))
8123a4b636Spatrick #define ICD_ICR_TRIG_EDGE(i) (0x2 << (IRQ_TO_REG16BIT(i) * 2))
8223a4b636Spatrick #define ICD_ICR_TRIG_MASK(i) (0x2 << (IRQ_TO_REG16BIT(i) * 2))
8323a4b636Spatrick
84027018efSpatrick /*
85027018efSpatrick * what about (ppi|spi)_status
86027018efSpatrick */
87027018efSpatrick #define ICD_PPI 0xD00
88027018efSpatrick #define ICD_PPI_GTIMER (1 << 11)
89027018efSpatrick #define ICD_PPI_FIQ (1 << 12)
90027018efSpatrick #define ICD_PPI_PTIMER (1 << 13)
91027018efSpatrick #define ICD_PPI_PWDOG (1 << 14)
92027018efSpatrick #define ICD_PPI_IRQ (1 << 15)
93027018efSpatrick #define ICD_SPI_BASE 0xD04
94027018efSpatrick #define ICD_SPIn(i) (ICD_SPI_BASE + ((i) * 4))
95027018efSpatrick
96027018efSpatrick
97027018efSpatrick #define ICD_SGIR 0xF00
98027018efSpatrick
99027018efSpatrick #define ICD_PERIPH_ID_0 0xFD0
100027018efSpatrick #define ICD_PERIPH_ID_1 0xFD4
101027018efSpatrick #define ICD_PERIPH_ID_2 0xFD8
102027018efSpatrick #define ICD_PERIPH_ID_3 0xFDC
103027018efSpatrick #define ICD_PERIPH_ID_4 0xFE0
104027018efSpatrick #define ICD_PERIPH_ID_5 0xFE4
105027018efSpatrick #define ICD_PERIPH_ID_6 0xFE8
106027018efSpatrick #define ICD_PERIPH_ID_7 0xFEC
107027018efSpatrick
108027018efSpatrick #define ICD_COMP_ID_0 0xFEC
109027018efSpatrick #define ICD_COMP_ID_1 0xFEC
110027018efSpatrick #define ICD_COMP_ID_2 0xFEC
111027018efSpatrick #define ICD_COMP_ID_3 0xFEC
112027018efSpatrick
113027018efSpatrick
114027018efSpatrick #define ICPICR 0x00
115027018efSpatrick #define ICPIPMR 0x04
116027018efSpatrick /* XXX - must left justify bits to 0 - 7 */
117027018efSpatrick #define ICMIPMR_SH 4
118027018efSpatrick #define ICPBPR 0x08
119027018efSpatrick #define ICPIAR 0x0C
120027018efSpatrick #define ICPIAR_IRQ_SH 0
121027018efSpatrick #define ICPIAR_IRQ_M 0x3ff
122027018efSpatrick #define ICPIAR_CPUID_SH 10
123027018efSpatrick #define ICPIAR_CPUID_M 0x7
124027018efSpatrick #define ICPIAR_NO_PENDING_IRQ ICPIAR_IRQ_M
125027018efSpatrick #define ICPEOIR 0x10
126027018efSpatrick #define ICPPRP 0x14
127027018efSpatrick #define ICPHPIR 0x18
128027018efSpatrick #define ICPIIR 0xFC
1296fc522b1Skettenis
130027018efSpatrick /*
131027018efSpatrick * what about periph_id and component_id
132027018efSpatrick */
133027018efSpatrick
134027018efSpatrick #define IRQ_ENABLE 1
135027018efSpatrick #define IRQ_DISABLE 0
136027018efSpatrick
137027018efSpatrick struct ampintc_softc {
13823a4b636Spatrick struct simplebus_softc sc_sbus;
1390b5401c0Skettenis struct intrq *sc_handler;
140027018efSpatrick int sc_nintr;
141027018efSpatrick bus_space_tag_t sc_iot;
142027018efSpatrick bus_space_handle_t sc_d_ioh, sc_p_ioh;
1436b5838f8Skettenis uint8_t sc_cpu_mask[ICD_ICTR_CPU_M + 1];
144c7189a0dSpatrick struct evcount sc_spur;
1456fc522b1Skettenis struct interrupt_controller sc_ic;
146ad0d549bSkettenis int sc_ipi_reason[ICD_ICTR_CPU_M + 1];
147ad0d549bSkettenis int sc_ipi_num[2];
148027018efSpatrick };
149027018efSpatrick struct ampintc_softc *ampintc;
150027018efSpatrick
151027018efSpatrick
152027018efSpatrick struct intrhand {
153027018efSpatrick TAILQ_ENTRY(intrhand) ih_list; /* link on intrq list */
154027018efSpatrick int (*ih_func)(void *); /* handler */
155027018efSpatrick void *ih_arg; /* arg for handler */
156027018efSpatrick int ih_ipl; /* IPL_* */
157abff443dSkettenis int ih_flags;
158027018efSpatrick int ih_irq; /* IRQ number */
159027018efSpatrick struct evcount ih_count;
160027018efSpatrick char *ih_name;
161027018efSpatrick };
162027018efSpatrick
163027018efSpatrick struct intrq {
164027018efSpatrick TAILQ_HEAD(, intrhand) iq_list; /* handler list */
165eb0c9b3cSpatrick int iq_irq_max; /* IRQ to mask while handling */
166eb0c9b3cSpatrick int iq_irq_min; /* lowest IRQ when shared */
167027018efSpatrick int iq_ist; /* share type */
168027018efSpatrick };
169027018efSpatrick
170027018efSpatrick
171027018efSpatrick int ampintc_match(struct device *, void *, void *);
172027018efSpatrick void ampintc_attach(struct device *, struct device *, void *);
173ad0d549bSkettenis void ampintc_cpuinit(void);
174027018efSpatrick int ampintc_spllower(int);
175027018efSpatrick void ampintc_splx(int);
176027018efSpatrick int ampintc_splraise(int);
177027018efSpatrick void ampintc_setipl(int);
178027018efSpatrick void ampintc_calc_mask(void);
179789e88a4Spatrick void *ampintc_intr_establish(int, int, int, struct cpu_info *,
1806fc522b1Skettenis int (*)(void *), void *, char *);
181789e88a4Spatrick void *ampintc_intr_establish_ext(int, int, struct cpu_info *,
182789e88a4Spatrick int (*)(void *), void *, char *);
183789e88a4Spatrick void *ampintc_intr_establish_fdt(void *, int *, int,
184789e88a4Spatrick struct cpu_info *, int (*)(void *), void *, char *);
185027018efSpatrick void ampintc_intr_disestablish(void *);
186027018efSpatrick void ampintc_irq_handler(void *);
187027018efSpatrick const char *ampintc_intr_string(void *);
188027018efSpatrick uint32_t ampintc_iack(void);
189027018efSpatrick void ampintc_eoi(uint32_t);
190027018efSpatrick void ampintc_set_priority(int, int);
191027018efSpatrick void ampintc_intr_enable(int);
192027018efSpatrick void ampintc_intr_disable(int);
19323a4b636Spatrick void ampintc_intr_config(int, int);
1946b5838f8Skettenis void ampintc_route(int, int, struct cpu_info *);
195ad0d549bSkettenis void ampintc_route_irq(void *, int, struct cpu_info *);
196ad0d549bSkettenis
197ad0d549bSkettenis int ampintc_ipi_combined(void *);
198ad0d549bSkettenis int ampintc_ipi_nop(void *);
199ad0d549bSkettenis int ampintc_ipi_ddb(void *);
200ad0d549bSkettenis void ampintc_send_ipi(struct cpu_info *, int);
201027018efSpatrick
202e3ee5e84Smpi const struct cfattach ampintc_ca = {
203027018efSpatrick sizeof (struct ampintc_softc), ampintc_match, ampintc_attach
204027018efSpatrick };
205027018efSpatrick
206027018efSpatrick struct cfdriver ampintc_cd = {
207027018efSpatrick NULL, "ampintc", DV_DULL
208027018efSpatrick };
209027018efSpatrick
2106fc522b1Skettenis static char *ampintc_compatibles[] = {
2116fc522b1Skettenis "arm,cortex-a7-gic",
2126fc522b1Skettenis "arm,cortex-a9-gic",
2136fc522b1Skettenis "arm,cortex-a15-gic",
214b78d0bc7Sjsg "arm,gic-400",
2156fc522b1Skettenis NULL
2166fc522b1Skettenis };
2176fc522b1Skettenis
218027018efSpatrick int
ampintc_match(struct device * parent,void * cfdata,void * aux)219027018efSpatrick ampintc_match(struct device *parent, void *cfdata, void *aux)
220027018efSpatrick {
2216fc522b1Skettenis struct fdt_attach_args *faa = aux;
2226fc522b1Skettenis int i;
2236fc522b1Skettenis
2246fc522b1Skettenis for (i = 0; ampintc_compatibles[i]; i++)
2256fc522b1Skettenis if (OF_is_compatible(faa->fa_node, ampintc_compatibles[i]))
226027018efSpatrick return (1);
2276fc522b1Skettenis
2286fc522b1Skettenis return (0);
229027018efSpatrick }
230027018efSpatrick
231027018efSpatrick void
ampintc_attach(struct device * parent,struct device * self,void * aux)2326fc522b1Skettenis ampintc_attach(struct device *parent, struct device *self, void *aux)
233027018efSpatrick {
234027018efSpatrick struct ampintc_softc *sc = (struct ampintc_softc *)self;
2356fc522b1Skettenis struct fdt_attach_args *faa = aux;
2366b5838f8Skettenis int i, nintr, ncpu;
2376b5838f8Skettenis uint32_t ictr;
238ad0d549bSkettenis #ifdef MULTIPROCESSOR
239ad0d549bSkettenis int nipi, ipiirq[2];
240ad0d549bSkettenis #endif
241027018efSpatrick
242027018efSpatrick ampintc = sc;
243027018efSpatrick
244027018efSpatrick arm_init_smask();
245027018efSpatrick
2466fc522b1Skettenis sc->sc_iot = faa->fa_iot;
247027018efSpatrick
2486fc522b1Skettenis /* First row: ICD */
2496fc522b1Skettenis if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
2506fc522b1Skettenis faa->fa_reg[0].size, 0, &sc->sc_d_ioh))
2516fc522b1Skettenis panic("%s: ICD bus_space_map failed!", __func__);
252d730c68aSrapha
2536fc522b1Skettenis /* Second row: ICP */
2546fc522b1Skettenis if (bus_space_map(sc->sc_iot, faa->fa_reg[1].addr,
2556fc522b1Skettenis faa->fa_reg[1].size, 0, &sc->sc_p_ioh))
2566fc522b1Skettenis panic("%s: ICP bus_space_map failed!", __func__);
257027018efSpatrick
258c7189a0dSpatrick evcount_attach(&sc->sc_spur, "irq1023/spur", NULL);
259c7189a0dSpatrick
2606b5838f8Skettenis ictr = bus_space_read_4(sc->sc_iot, sc->sc_d_ioh, ICD_ICTR);
2616b5838f8Skettenis nintr = 32 * ((ictr >> ICD_ICTR_ITL_SH) & ICD_ICTR_ITL_M);
262027018efSpatrick nintr += 32; /* ICD_ICTR + 1, irq 0-31 is SGI, 32+ is PPI */
263027018efSpatrick sc->sc_nintr = nintr;
2646b5838f8Skettenis ncpu = ((ictr >> ICD_ICTR_CPU_SH) & ICD_ICTR_CPU_M) + 1;
26523a4b636Spatrick printf(" nirq %d, ncpu %d", nintr, ncpu);
266027018efSpatrick
2676b5838f8Skettenis KASSERT(curcpu()->ci_cpuid <= ICD_ICTR_CPU_M);
2686b5838f8Skettenis sc->sc_cpu_mask[curcpu()->ci_cpuid] =
2696b5838f8Skettenis bus_space_read_1(sc->sc_iot, sc->sc_d_ioh, ICD_IPTRn(0));
270e851b7eaSkettenis
271027018efSpatrick /* Disable all interrupts, clear all pending */
272027018efSpatrick for (i = 0; i < nintr/32; i++) {
2736fc522b1Skettenis bus_space_write_4(sc->sc_iot, sc->sc_d_ioh,
2746fc522b1Skettenis ICD_ICERn(i*32), ~0);
2756fc522b1Skettenis bus_space_write_4(sc->sc_iot, sc->sc_d_ioh,
2766fc522b1Skettenis ICD_ICPRn(i*32), ~0);
277027018efSpatrick }
278027018efSpatrick for (i = 0; i < nintr; i++) {
279027018efSpatrick /* lowest priority ?? */
2806fc522b1Skettenis bus_space_write_1(sc->sc_iot, sc->sc_d_ioh, ICD_IPRn(i), 0xff);
281027018efSpatrick /* target no cpus */
2826fc522b1Skettenis bus_space_write_1(sc->sc_iot, sc->sc_d_ioh, ICD_IPTRn(i), 0);
283027018efSpatrick }
284027018efSpatrick for (i = 2; i < nintr/16; i++) {
285027018efSpatrick /* irq 32 - N */
2866fc522b1Skettenis bus_space_write_4(sc->sc_iot, sc->sc_d_ioh, ICD_ICRn(i*16), 0);
287027018efSpatrick }
288027018efSpatrick
289027018efSpatrick /* software reset of the part? */
290027018efSpatrick /* set protection bit (kernel only)? */
291027018efSpatrick
292027018efSpatrick /* XXX - check power saving bit */
293027018efSpatrick
2940b5401c0Skettenis sc->sc_handler = mallocarray(nintr, sizeof(*sc->sc_handler), M_DEVBUF,
2950b5401c0Skettenis M_ZERO | M_NOWAIT);
296027018efSpatrick for (i = 0; i < nintr; i++) {
2970b5401c0Skettenis TAILQ_INIT(&sc->sc_handler[i].iq_list);
298027018efSpatrick }
299027018efSpatrick
300027018efSpatrick ampintc_setipl(IPL_HIGH); /* XXX ??? */
301027018efSpatrick ampintc_calc_mask();
302027018efSpatrick
303027018efSpatrick /* insert self as interrupt handler */
304027018efSpatrick arm_set_intr_handler(ampintc_splraise, ampintc_spllower, ampintc_splx,
305027018efSpatrick ampintc_setipl, ampintc_intr_establish_ext,
306027018efSpatrick ampintc_intr_disestablish, ampintc_intr_string, ampintc_irq_handler);
307027018efSpatrick
308ad0d549bSkettenis #ifdef MULTIPROCESSOR
309ad0d549bSkettenis /* setup IPI interrupts */
310ad0d549bSkettenis
311ad0d549bSkettenis /*
312ad0d549bSkettenis * Ideally we want two IPI interrupts, one for NOP and one for
313ad0d549bSkettenis * DDB, however we can survive if only one is available it is
314ad0d549bSkettenis * possible that most are not available to the non-secure OS.
315ad0d549bSkettenis */
316ad0d549bSkettenis nipi = 0;
317ad0d549bSkettenis for (i = 0; i < 16; i++) {
318ad0d549bSkettenis int reg, oldreg;
319ad0d549bSkettenis
320ad0d549bSkettenis oldreg = bus_space_read_1(sc->sc_iot, sc->sc_d_ioh,
321ad0d549bSkettenis ICD_IPRn(i));
322ad0d549bSkettenis bus_space_write_1(sc->sc_iot, sc->sc_d_ioh, ICD_IPRn(i),
323ad0d549bSkettenis oldreg ^ 0x20);
324ad0d549bSkettenis
325ad0d549bSkettenis /* if this interrupt is not usable, route will be zero */
326ad0d549bSkettenis reg = bus_space_read_1(sc->sc_iot, sc->sc_d_ioh, ICD_IPRn(i));
327ad0d549bSkettenis if (reg == oldreg)
328ad0d549bSkettenis continue;
329ad0d549bSkettenis
330ad0d549bSkettenis /* return to original value, will be set when used */
331ad0d549bSkettenis bus_space_write_1(sc->sc_iot, sc->sc_d_ioh, ICD_IPRn(i),
332ad0d549bSkettenis oldreg);
333ad0d549bSkettenis
334ad0d549bSkettenis if (nipi == 0)
335ad0d549bSkettenis printf(" ipi: %d", i);
336ad0d549bSkettenis else
337ad0d549bSkettenis printf(", %d", i);
338ad0d549bSkettenis ipiirq[nipi++] = i;
339ad0d549bSkettenis if (nipi == 2)
340ad0d549bSkettenis break;
341ad0d549bSkettenis }
342ad0d549bSkettenis
343ad0d549bSkettenis if (nipi == 0)
344ad0d549bSkettenis panic ("no irq available for IPI");
345ad0d549bSkettenis
346ad0d549bSkettenis switch (nipi) {
347ad0d549bSkettenis case 1:
348ad0d549bSkettenis ampintc_intr_establish(ipiirq[0], IST_EDGE_RISING,
349ad0d549bSkettenis IPL_IPI|IPL_MPSAFE, ampintc_ipi_combined, sc, "ipi");
350ad0d549bSkettenis sc->sc_ipi_num[ARM_IPI_NOP] = ipiirq[0];
351ad0d549bSkettenis sc->sc_ipi_num[ARM_IPI_DDB] = ipiirq[0];
352ad0d549bSkettenis break;
353ad0d549bSkettenis case 2:
354ad0d549bSkettenis ampintc_intr_establish(ipiirq[0], IST_EDGE_RISING,
355ad0d549bSkettenis IPL_IPI|IPL_MPSAFE, ampintc_ipi_nop, sc, "ipinop");
356ad0d549bSkettenis sc->sc_ipi_num[ARM_IPI_NOP] = ipiirq[0];
357ad0d549bSkettenis ampintc_intr_establish(ipiirq[1], IST_EDGE_RISING,
358ad0d549bSkettenis IPL_IPI|IPL_MPSAFE, ampintc_ipi_ddb, sc, "ipiddb");
359ad0d549bSkettenis sc->sc_ipi_num[ARM_IPI_DDB] = ipiirq[1];
360ad0d549bSkettenis break;
361ad0d549bSkettenis default:
362ad0d549bSkettenis panic("nipi unexpected number %d", nipi);
363ad0d549bSkettenis }
364ad0d549bSkettenis
365ad0d549bSkettenis intr_send_ipi_func = ampintc_send_ipi;
366ad0d549bSkettenis #endif
367ad0d549bSkettenis
368027018efSpatrick /* enable interrupts */
3696fc522b1Skettenis bus_space_write_4(sc->sc_iot, sc->sc_d_ioh, ICD_DCR, 3);
3706fc522b1Skettenis bus_space_write_4(sc->sc_iot, sc->sc_p_ioh, ICPICR, 1);
371ebd24745Sjsg enable_interrupts(PSR_I);
3726fc522b1Skettenis
3736fc522b1Skettenis sc->sc_ic.ic_node = faa->fa_node;
3746fc522b1Skettenis sc->sc_ic.ic_cookie = self;
3756fc522b1Skettenis sc->sc_ic.ic_establish = ampintc_intr_establish_fdt;
3762b8e4081Spatrick sc->sc_ic.ic_disestablish = ampintc_intr_disestablish;
377ad0d549bSkettenis sc->sc_ic.ic_route = ampintc_route_irq;
378ad0d549bSkettenis sc->sc_ic.ic_cpu_enable = ampintc_cpuinit;
3796fc522b1Skettenis arm_intr_register_fdt(&sc->sc_ic);
38023a4b636Spatrick
38123a4b636Spatrick /* attach GICv2M frame controller */
38223a4b636Spatrick simplebus_attach(parent, &sc->sc_sbus.sc_dev, faa);
383027018efSpatrick }
384027018efSpatrick
385027018efSpatrick void
ampintc_set_priority(int irq,int pri)386027018efSpatrick ampintc_set_priority(int irq, int pri)
387027018efSpatrick {
388027018efSpatrick struct ampintc_softc *sc = ampintc;
389027018efSpatrick uint32_t prival;
390027018efSpatrick
391027018efSpatrick /*
392027018efSpatrick * We only use 16 (13 really) interrupt priorities,
393027018efSpatrick * and a CPU is only required to implement bit 4-7 of each field
394027018efSpatrick * so shift into the top bits.
395027018efSpatrick * also low values are higher priority thus IPL_HIGH - pri
396027018efSpatrick */
397027018efSpatrick prival = (IPL_HIGH - pri) << ICMIPMR_SH;
398027018efSpatrick bus_space_write_1(sc->sc_iot, sc->sc_d_ioh, ICD_IPRn(irq), prival);
399027018efSpatrick }
400027018efSpatrick
401027018efSpatrick void
ampintc_setipl(int new)402027018efSpatrick ampintc_setipl(int new)
403027018efSpatrick {
404027018efSpatrick struct cpu_info *ci = curcpu();
405027018efSpatrick struct ampintc_softc *sc = ampintc;
406027018efSpatrick int psw;
407027018efSpatrick
408027018efSpatrick /* disable here is only to keep hardware in sync with ci->ci_cpl */
409ebd24745Sjsg psw = disable_interrupts(PSR_I);
410027018efSpatrick ci->ci_cpl = new;
411027018efSpatrick
412027018efSpatrick /* low values are higher priority thus IPL_HIGH - pri */
413027018efSpatrick bus_space_write_4(sc->sc_iot, sc->sc_p_ioh, ICPIPMR,
414027018efSpatrick (IPL_HIGH - new) << ICMIPMR_SH);
415027018efSpatrick restore_interrupts(psw);
416027018efSpatrick }
417027018efSpatrick
418027018efSpatrick void
ampintc_intr_enable(int irq)419027018efSpatrick ampintc_intr_enable(int irq)
420027018efSpatrick {
421027018efSpatrick struct ampintc_softc *sc = ampintc;
422027018efSpatrick
423027018efSpatrick #ifdef DEBUG
424027018efSpatrick printf("enable irq %d register %x bitmask %08x\n",
425027018efSpatrick irq, ICD_ISERn(irq), 1 << IRQ_TO_REG32BIT(irq));
426027018efSpatrick #endif
427027018efSpatrick
428027018efSpatrick bus_space_write_4(sc->sc_iot, sc->sc_d_ioh, ICD_ISERn(irq),
429027018efSpatrick 1 << IRQ_TO_REG32BIT(irq));
430027018efSpatrick }
431027018efSpatrick
432027018efSpatrick void
ampintc_intr_disable(int irq)433027018efSpatrick ampintc_intr_disable(int irq)
434027018efSpatrick {
435027018efSpatrick struct ampintc_softc *sc = ampintc;
436027018efSpatrick
437027018efSpatrick bus_space_write_4(sc->sc_iot, sc->sc_d_ioh, ICD_ICERn(irq),
438027018efSpatrick 1 << IRQ_TO_REG32BIT(irq));
439027018efSpatrick }
440027018efSpatrick
44123a4b636Spatrick void
ampintc_intr_config(int irqno,int type)44223a4b636Spatrick ampintc_intr_config(int irqno, int type)
44323a4b636Spatrick {
44423a4b636Spatrick struct ampintc_softc *sc = ampintc;
44523a4b636Spatrick uint32_t ctrl;
44623a4b636Spatrick
44723a4b636Spatrick ctrl = bus_space_read_4(sc->sc_iot, sc->sc_d_ioh, ICD_ICRn(irqno));
44823a4b636Spatrick
44923a4b636Spatrick ctrl &= ~ICD_ICR_TRIG_MASK(irqno);
45023a4b636Spatrick if (type == IST_EDGE_RISING)
45123a4b636Spatrick ctrl |= ICD_ICR_TRIG_EDGE(irqno);
45223a4b636Spatrick else
45323a4b636Spatrick ctrl |= ICD_ICR_TRIG_LEVEL(irqno);
45423a4b636Spatrick
45523a4b636Spatrick bus_space_write_4(sc->sc_iot, sc->sc_d_ioh, ICD_ICRn(irqno), ctrl);
45623a4b636Spatrick }
457027018efSpatrick
458027018efSpatrick void
ampintc_calc_mask(void)459027018efSpatrick ampintc_calc_mask(void)
460027018efSpatrick {
461027018efSpatrick struct cpu_info *ci = curcpu();
462027018efSpatrick struct ampintc_softc *sc = ampintc;
463027018efSpatrick struct intrhand *ih;
4646b5838f8Skettenis int irq;
465027018efSpatrick
466027018efSpatrick for (irq = 0; irq < sc->sc_nintr; irq++) {
467027018efSpatrick int max = IPL_NONE;
468027018efSpatrick int min = IPL_HIGH;
4690b5401c0Skettenis TAILQ_FOREACH(ih, &sc->sc_handler[irq].iq_list, ih_list) {
470027018efSpatrick if (ih->ih_ipl > max)
471027018efSpatrick max = ih->ih_ipl;
472027018efSpatrick
473027018efSpatrick if (ih->ih_ipl < min)
474027018efSpatrick min = ih->ih_ipl;
475027018efSpatrick }
476027018efSpatrick
477738a7e0eSkettenis if (max == IPL_NONE)
478738a7e0eSkettenis min = IPL_NONE;
479738a7e0eSkettenis
4800b5401c0Skettenis if (sc->sc_handler[irq].iq_irq_max == max &&
4810b5401c0Skettenis sc->sc_handler[irq].iq_irq_min == min)
482027018efSpatrick continue;
483eb0c9b3cSpatrick
4840b5401c0Skettenis sc->sc_handler[irq].iq_irq_max = max;
4850b5401c0Skettenis sc->sc_handler[irq].iq_irq_min = min;
486027018efSpatrick
487027018efSpatrick /* Enable interrupts at lower levels, clear -> enable */
488027018efSpatrick /* Set interrupt priority/enable */
489027018efSpatrick if (min != IPL_NONE) {
490027018efSpatrick ampintc_set_priority(irq, min);
491027018efSpatrick ampintc_intr_enable(irq);
4926b5838f8Skettenis ampintc_route(irq, IRQ_ENABLE, ci);
493027018efSpatrick } else {
494027018efSpatrick ampintc_intr_disable(irq);
4956b5838f8Skettenis ampintc_route(irq, IRQ_DISABLE, ci);
496027018efSpatrick }
497027018efSpatrick }
498027018efSpatrick ampintc_setipl(ci->ci_cpl);
499027018efSpatrick }
500027018efSpatrick
501027018efSpatrick void
ampintc_splx(int new)502027018efSpatrick ampintc_splx(int new)
503027018efSpatrick {
504027018efSpatrick struct cpu_info *ci = curcpu();
505027018efSpatrick
506027018efSpatrick if (ci->ci_ipending & arm_smask[new])
507027018efSpatrick arm_do_pending_intr(new);
508027018efSpatrick
509027018efSpatrick ampintc_setipl(new);
510027018efSpatrick }
511027018efSpatrick
512027018efSpatrick int
ampintc_spllower(int new)513027018efSpatrick ampintc_spllower(int new)
514027018efSpatrick {
515027018efSpatrick struct cpu_info *ci = curcpu();
516027018efSpatrick int old = ci->ci_cpl;
517027018efSpatrick ampintc_splx(new);
518027018efSpatrick return (old);
519027018efSpatrick }
520027018efSpatrick
521027018efSpatrick int
ampintc_splraise(int new)522027018efSpatrick ampintc_splraise(int new)
523027018efSpatrick {
524027018efSpatrick struct cpu_info *ci = curcpu();
525027018efSpatrick int old;
526027018efSpatrick old = ci->ci_cpl;
527027018efSpatrick
528027018efSpatrick /*
529027018efSpatrick * setipl must always be called because there is a race window
530027018efSpatrick * where the variable is updated before the mask is set
531027018efSpatrick * an interrupt occurs in that window without the mask always
532027018efSpatrick * being set, the hardware might not get updated on the next
533027018efSpatrick * splraise completely messing up spl protection.
534027018efSpatrick */
535027018efSpatrick if (old > new)
536027018efSpatrick new = old;
537027018efSpatrick
538027018efSpatrick ampintc_setipl(new);
539027018efSpatrick
540027018efSpatrick return (old);
541027018efSpatrick }
542027018efSpatrick
543027018efSpatrick
544027018efSpatrick uint32_t
ampintc_iack(void)545027018efSpatrick ampintc_iack(void)
546027018efSpatrick {
547027018efSpatrick uint32_t intid;
548027018efSpatrick struct ampintc_softc *sc = ampintc;
549027018efSpatrick
550027018efSpatrick intid = bus_space_read_4(sc->sc_iot, sc->sc_p_ioh, ICPIAR);
551027018efSpatrick
552027018efSpatrick return (intid);
553027018efSpatrick }
554027018efSpatrick
555027018efSpatrick void
ampintc_eoi(uint32_t eoi)556027018efSpatrick ampintc_eoi(uint32_t eoi)
557027018efSpatrick {
558027018efSpatrick struct ampintc_softc *sc = ampintc;
559027018efSpatrick
560027018efSpatrick bus_space_write_4(sc->sc_iot, sc->sc_p_ioh, ICPEOIR, eoi);
561027018efSpatrick }
562027018efSpatrick
563027018efSpatrick void
ampintc_route(int irq,int enable,struct cpu_info * ci)5646b5838f8Skettenis ampintc_route(int irq, int enable, struct cpu_info *ci)
565027018efSpatrick {
566027018efSpatrick struct ampintc_softc *sc = ampintc;
5676b5838f8Skettenis uint8_t mask, val;
5686b5838f8Skettenis
5696b5838f8Skettenis KASSERT(ci->ci_cpuid <= ICD_ICTR_CPU_M);
5706b5838f8Skettenis mask = sc->sc_cpu_mask[ci->ci_cpuid];
571027018efSpatrick
572027018efSpatrick val = bus_space_read_1(sc->sc_iot, sc->sc_d_ioh, ICD_IPTRn(irq));
573027018efSpatrick if (enable == IRQ_ENABLE)
5746b5838f8Skettenis val |= mask;
575027018efSpatrick else
5766b5838f8Skettenis val &= ~mask;
577027018efSpatrick bus_space_write_1(sc->sc_iot, sc->sc_d_ioh, ICD_IPTRn(irq), val);
578027018efSpatrick }
579027018efSpatrick
580027018efSpatrick void
ampintc_cpuinit(void)581ad0d549bSkettenis ampintc_cpuinit(void)
582ad0d549bSkettenis {
583ad0d549bSkettenis struct ampintc_softc *sc = ampintc;
584ad0d549bSkettenis int i;
585ad0d549bSkettenis
586ad0d549bSkettenis /* XXX - this is the only cpu specific call to set this */
587ad0d549bSkettenis if (sc->sc_cpu_mask[cpu_number()] == 0) {
588ad0d549bSkettenis for (i = 0; i < 32; i++) {
589ad0d549bSkettenis int cpumask =
590ad0d549bSkettenis bus_space_read_1(sc->sc_iot, sc->sc_d_ioh,
591ad0d549bSkettenis ICD_IPTRn(i));
592ad0d549bSkettenis
593ad0d549bSkettenis if (cpumask != 0) {
594ad0d549bSkettenis sc->sc_cpu_mask[cpu_number()] = cpumask;
595ad0d549bSkettenis break;
596ad0d549bSkettenis }
597ad0d549bSkettenis }
598ad0d549bSkettenis }
599ad0d549bSkettenis
600ad0d549bSkettenis if (sc->sc_cpu_mask[cpu_number()] == 0)
601ad0d549bSkettenis panic("could not determine cpu target mask");
602ad0d549bSkettenis }
603ad0d549bSkettenis
604ad0d549bSkettenis void
ampintc_route_irq(void * v,int enable,struct cpu_info * ci)605ad0d549bSkettenis ampintc_route_irq(void *v, int enable, struct cpu_info *ci)
606ad0d549bSkettenis {
607ad0d549bSkettenis struct ampintc_softc *sc = ampintc;
608ad0d549bSkettenis struct intrhand *ih = v;
609ad0d549bSkettenis
610ad0d549bSkettenis bus_space_write_4(sc->sc_iot, sc->sc_p_ioh, ICPICR, 1);
611ad0d549bSkettenis bus_space_write_4(sc->sc_iot, sc->sc_d_ioh, ICD_ICRn(ih->ih_irq), 0);
612ad0d549bSkettenis if (enable) {
613ad0d549bSkettenis ampintc_set_priority(ih->ih_irq,
614ad0d549bSkettenis sc->sc_handler[ih->ih_irq].iq_irq_min);
615ad0d549bSkettenis ampintc_intr_enable(ih->ih_irq);
616ad0d549bSkettenis }
617ad0d549bSkettenis
618ad0d549bSkettenis ampintc_route(ih->ih_irq, enable, ci);
619ad0d549bSkettenis }
620ad0d549bSkettenis
621ad0d549bSkettenis void
ampintc_irq_handler(void * frame)622027018efSpatrick ampintc_irq_handler(void *frame)
623027018efSpatrick {
624027018efSpatrick struct ampintc_softc *sc = ampintc;
625027018efSpatrick struct intrhand *ih;
626027018efSpatrick void *arg;
627027018efSpatrick uint32_t iack_val;
6287e841682Skettenis int irq, pri, s, handled;
629027018efSpatrick
630027018efSpatrick iack_val = ampintc_iack();
631027018efSpatrick #ifdef DEBUG_INTC
632027018efSpatrick if (iack_val != 27)
633027018efSpatrick printf("irq %d fired\n", iack_val);
634027018efSpatrick else {
635027018efSpatrick static int cnt = 0;
636027018efSpatrick if ((cnt++ % 100) == 0) {
637027018efSpatrick printf("irq %d fired * _100\n", iack_val);
638e08d6af1Smiod #ifdef DDB
639e97088d6Smpi db_enter();
640e08d6af1Smiod #endif
641027018efSpatrick }
642027018efSpatrick
643027018efSpatrick }
644027018efSpatrick #endif
645027018efSpatrick
6468daa750dSpatrick irq = iack_val & ICPIAR_IRQ_M;
6478daa750dSpatrick
6488daa750dSpatrick if (irq == 1023) {
649c7189a0dSpatrick sc->sc_spur.ec_count++;
650027018efSpatrick return;
651027018efSpatrick }
6528daa750dSpatrick
6538daa750dSpatrick if (irq >= sc->sc_nintr)
6548daa750dSpatrick return;
655027018efSpatrick
6560b5401c0Skettenis pri = sc->sc_handler[irq].iq_irq_max;
657027018efSpatrick s = ampintc_splraise(pri);
6580b5401c0Skettenis TAILQ_FOREACH(ih, &sc->sc_handler[irq].iq_list, ih_list) {
659abff443dSkettenis #ifdef MULTIPROCESSOR
660abff443dSkettenis int need_lock;
661abff443dSkettenis
662abff443dSkettenis if (ih->ih_flags & IPL_MPSAFE)
663abff443dSkettenis need_lock = 0;
664abff443dSkettenis else
665abff443dSkettenis need_lock = s < IPL_SCHED;
666abff443dSkettenis
667abff443dSkettenis if (need_lock)
668abff443dSkettenis KERNEL_LOCK();
669abff443dSkettenis #endif
670abff443dSkettenis
671fe0d117cSjsg if (ih->ih_arg)
672027018efSpatrick arg = ih->ih_arg;
673027018efSpatrick else
674027018efSpatrick arg = frame;
675027018efSpatrick
6767e841682Skettenis enable_interrupts(PSR_I);
6777e841682Skettenis handled = ih->ih_func(arg);
6787e841682Skettenis disable_interrupts(PSR_I);
6797e841682Skettenis if (handled)
680027018efSpatrick ih->ih_count.ec_count++;
681027018efSpatrick
682abff443dSkettenis #ifdef MULTIPROCESSOR
683abff443dSkettenis if (need_lock)
684abff443dSkettenis KERNEL_UNLOCK();
685abff443dSkettenis #endif
686027018efSpatrick }
687027018efSpatrick ampintc_eoi(iack_val);
688027018efSpatrick
689027018efSpatrick ampintc_splx(s);
690027018efSpatrick }
691027018efSpatrick
692027018efSpatrick void *
ampintc_intr_establish_ext(int irqno,int level,struct cpu_info * ci,int (* func)(void *),void * arg,char * name)693789e88a4Spatrick ampintc_intr_establish_ext(int irqno, int level, struct cpu_info *ci,
694789e88a4Spatrick int (*func)(void *), void *arg, char *name)
695027018efSpatrick {
69623a4b636Spatrick return ampintc_intr_establish(irqno+32, IST_LEVEL_HIGH, level,
697789e88a4Spatrick ci, func, arg, name);
698027018efSpatrick }
699027018efSpatrick
700027018efSpatrick void *
ampintc_intr_establish_fdt(void * cookie,int * cell,int level,struct cpu_info * ci,int (* func)(void *),void * arg,char * name)7016fc522b1Skettenis ampintc_intr_establish_fdt(void *cookie, int *cell, int level,
702789e88a4Spatrick struct cpu_info *ci, int (*func)(void *), void *arg, char *name)
7036fc522b1Skettenis {
7046fc522b1Skettenis struct ampintc_softc *sc = (struct ampintc_softc *)cookie;
7056fc522b1Skettenis int irq;
70623a4b636Spatrick int type;
7076fc522b1Skettenis
7086fc522b1Skettenis /* 2nd cell contains the interrupt number */
7096fc522b1Skettenis irq = cell[1];
7106fc522b1Skettenis
7116fc522b1Skettenis /* 1st cell contains type: 0 SPI (32-X), 1 PPI (16-31) */
7126fc522b1Skettenis if (cell[0] == 0)
7136fc522b1Skettenis irq += 32;
7146fc522b1Skettenis else if (cell[0] == 1)
7156fc522b1Skettenis irq += 16;
7166fc522b1Skettenis else
71723a4b636Spatrick panic("%s: bogus interrupt type", sc->sc_sbus.sc_dev.dv_xname);
7186fc522b1Skettenis
71923a4b636Spatrick /* SPIs are only active-high level or low-to-high edge */
72023a4b636Spatrick if (cell[2] & 0x3)
72123a4b636Spatrick type = IST_EDGE_RISING;
72223a4b636Spatrick else
72323a4b636Spatrick type = IST_LEVEL_HIGH;
72423a4b636Spatrick
725789e88a4Spatrick return ampintc_intr_establish(irq, type, level, ci, func, arg, name);
7266fc522b1Skettenis }
7276fc522b1Skettenis
7286fc522b1Skettenis void *
ampintc_intr_establish(int irqno,int type,int level,struct cpu_info * ci,int (* func)(void *),void * arg,char * name)729789e88a4Spatrick ampintc_intr_establish(int irqno, int type, int level, struct cpu_info *ci,
730789e88a4Spatrick int (*func)(void *), void *arg, char *name)
731027018efSpatrick {
732027018efSpatrick struct ampintc_softc *sc = ampintc;
733027018efSpatrick struct intrhand *ih;
734027018efSpatrick int psw;
735027018efSpatrick
736027018efSpatrick if (irqno < 0 || irqno >= sc->sc_nintr)
737027018efSpatrick panic("ampintc_intr_establish: bogus irqnumber %d: %s",
738027018efSpatrick irqno, name);
739027018efSpatrick
740789e88a4Spatrick if (ci == NULL)
741789e88a4Spatrick ci = &cpu_info_primary;
742789e88a4Spatrick else if (!CPU_IS_PRIMARY(ci))
743789e88a4Spatrick return NULL;
744789e88a4Spatrick
74523a4b636Spatrick if (irqno < 16) {
74623a4b636Spatrick /* SGI are only EDGE */
74723a4b636Spatrick type = IST_EDGE_RISING;
74823a4b636Spatrick } else if (irqno < 32) {
74923a4b636Spatrick /* PPI are only LEVEL */
75023a4b636Spatrick type = IST_LEVEL_HIGH;
75123a4b636Spatrick }
75223a4b636Spatrick
753c88bb1b8Spatrick ih = malloc(sizeof(*ih), M_DEVBUF, M_WAITOK);
754027018efSpatrick ih->ih_func = func;
755027018efSpatrick ih->ih_arg = arg;
756ad0d549bSkettenis ih->ih_ipl = level & IPL_IRQMASK;
757ad0d549bSkettenis ih->ih_flags = level & IPL_FLAGMASK;
758027018efSpatrick ih->ih_irq = irqno;
759027018efSpatrick ih->ih_name = name;
760027018efSpatrick
761ebd24745Sjsg psw = disable_interrupts(PSR_I);
762027018efSpatrick
7630b5401c0Skettenis TAILQ_INSERT_TAIL(&sc->sc_handler[irqno].iq_list, ih, ih_list);
764027018efSpatrick
765027018efSpatrick if (name != NULL)
766027018efSpatrick evcount_attach(&ih->ih_count, name, &ih->ih_irq);
767027018efSpatrick
768027018efSpatrick #ifdef DEBUG_INTC
769027018efSpatrick printf("ampintc_intr_establish irq %d level %d [%s]\n", irqno, level,
770027018efSpatrick name);
771027018efSpatrick #endif
77223a4b636Spatrick
77323a4b636Spatrick ampintc_intr_config(irqno, type);
774027018efSpatrick ampintc_calc_mask();
775027018efSpatrick
776027018efSpatrick restore_interrupts(psw);
777027018efSpatrick return (ih);
778027018efSpatrick }
779027018efSpatrick
780027018efSpatrick void
ampintc_intr_disestablish(void * cookie)781027018efSpatrick ampintc_intr_disestablish(void *cookie)
782027018efSpatrick {
7837c01c46aSpatrick struct ampintc_softc *sc = ampintc;
784027018efSpatrick struct intrhand *ih = cookie;
7857c01c46aSpatrick int psw;
7867c01c46aSpatrick
7877c01c46aSpatrick #ifdef DEBUG_INTC
7887c01c46aSpatrick printf("ampintc_intr_disestablish irq %d level %d [%s]\n",
7897c01c46aSpatrick ih->ih_irq, ih->ih_ipl, ih->ih_name);
7907c01c46aSpatrick #endif
7917c01c46aSpatrick
792ebd24745Sjsg psw = disable_interrupts(PSR_I);
7937c01c46aSpatrick
7940b5401c0Skettenis TAILQ_REMOVE(&sc->sc_handler[ih->ih_irq].iq_list, ih, ih_list);
795027018efSpatrick if (ih->ih_name != NULL)
796027018efSpatrick evcount_detach(&ih->ih_count);
7977c01c46aSpatrick free(ih, M_DEVBUF, sizeof(*ih));
7987c01c46aSpatrick
7997c01c46aSpatrick ampintc_calc_mask();
8007c01c46aSpatrick
801027018efSpatrick restore_interrupts(psw);
802027018efSpatrick }
803027018efSpatrick
804027018efSpatrick const char *
ampintc_intr_string(void * cookie)805027018efSpatrick ampintc_intr_string(void *cookie)
806027018efSpatrick {
807027018efSpatrick struct intrhand *ih = (struct intrhand *)cookie;
808027018efSpatrick static char irqstr[1 + sizeof("ampintc irq ") + 4];
809027018efSpatrick
810027018efSpatrick snprintf(irqstr, sizeof irqstr, "ampintc irq %d", ih->ih_irq);
811027018efSpatrick return irqstr;
812027018efSpatrick }
81323a4b636Spatrick
81423a4b636Spatrick /*
81523a4b636Spatrick * GICv2m frame controller for MSI interrupts.
81623a4b636Spatrick */
81723a4b636Spatrick #define GICV2M_TYPER 0x008
81823a4b636Spatrick #define GICV2M_TYPER_SPI_BASE(x) (((x) >> 16) & 0x3ff)
81923a4b636Spatrick #define GICV2M_TYPER_SPI_COUNT(x) (((x) >> 0) & 0x3ff)
82023a4b636Spatrick #define GICV2M_SETSPI_NS 0x040
82123a4b636Spatrick
82223a4b636Spatrick int ampintc_msi_match(struct device *, void *, void *);
82323a4b636Spatrick void ampintc_msi_attach(struct device *, struct device *, void *);
82423a4b636Spatrick void *ampintc_intr_establish_msi(void *, uint64_t *, uint64_t *,
825789e88a4Spatrick int , struct cpu_info *, int (*)(void *), void *, char *);
82623a4b636Spatrick void ampintc_intr_disestablish_msi(void *);
82723a4b636Spatrick
82823a4b636Spatrick struct ampintc_msi_softc {
82923a4b636Spatrick struct device sc_dev;
83023a4b636Spatrick bus_space_tag_t sc_iot;
83123a4b636Spatrick bus_space_handle_t sc_ioh;
83223a4b636Spatrick paddr_t sc_addr;
83323a4b636Spatrick int sc_bspi;
83423a4b636Spatrick int sc_nspi;
83523a4b636Spatrick void **sc_spi;
83623a4b636Spatrick struct interrupt_controller sc_ic;
83723a4b636Spatrick };
83823a4b636Spatrick
839e3ee5e84Smpi const struct cfattach ampintcmsi_ca = {
84023a4b636Spatrick sizeof (struct ampintc_msi_softc), ampintc_msi_match, ampintc_msi_attach
84123a4b636Spatrick };
84223a4b636Spatrick
84323a4b636Spatrick struct cfdriver ampintcmsi_cd = {
84423a4b636Spatrick NULL, "ampintcmsi", DV_DULL
84523a4b636Spatrick };
84623a4b636Spatrick
84723a4b636Spatrick int
ampintc_msi_match(struct device * parent,void * cfdata,void * aux)84823a4b636Spatrick ampintc_msi_match(struct device *parent, void *cfdata, void *aux)
84923a4b636Spatrick {
85023a4b636Spatrick struct fdt_attach_args *faa = aux;
85123a4b636Spatrick
85223a4b636Spatrick return OF_is_compatible(faa->fa_node, "arm,gic-v2m-frame");
85323a4b636Spatrick }
85423a4b636Spatrick
85523a4b636Spatrick void
ampintc_msi_attach(struct device * parent,struct device * self,void * aux)85623a4b636Spatrick ampintc_msi_attach(struct device *parent, struct device *self, void *aux)
85723a4b636Spatrick {
85823a4b636Spatrick struct ampintc_msi_softc *sc = (struct ampintc_msi_softc *)self;
85923a4b636Spatrick struct fdt_attach_args *faa = aux;
86023a4b636Spatrick uint32_t typer;
86123a4b636Spatrick
86223a4b636Spatrick sc->sc_iot = faa->fa_iot;
86323a4b636Spatrick if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
86423a4b636Spatrick faa->fa_reg[0].size, 0, &sc->sc_ioh))
86523a4b636Spatrick panic("%s: bus_space_map failed!", __func__);
86623a4b636Spatrick
86723a4b636Spatrick /* XXX: Hack to retrieve the physical address (from a CPU PoV). */
86823a4b636Spatrick if (!pmap_extract(pmap_kernel(), sc->sc_ioh, &sc->sc_addr)) {
86923a4b636Spatrick printf(": cannot retrieve msi addr\n");
87023a4b636Spatrick return;
87123a4b636Spatrick }
87223a4b636Spatrick
87323a4b636Spatrick typer = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GICV2M_TYPER);
87423a4b636Spatrick sc->sc_bspi = GICV2M_TYPER_SPI_BASE(typer);
87523a4b636Spatrick sc->sc_nspi = GICV2M_TYPER_SPI_COUNT(typer);
87623a4b636Spatrick
87723a4b636Spatrick sc->sc_bspi = OF_getpropint(faa->fa_node,
87823a4b636Spatrick "arm,msi-base-spi", sc->sc_bspi);
87923a4b636Spatrick sc->sc_nspi = OF_getpropint(faa->fa_node,
88023a4b636Spatrick "arm,msi-num-spis", sc->sc_nspi);
88123a4b636Spatrick
88223a4b636Spatrick printf(": nspi %d\n", sc->sc_nspi);
88323a4b636Spatrick
88423a4b636Spatrick sc->sc_spi = mallocarray(sc->sc_nspi, sizeof(void *), M_DEVBUF,
88523a4b636Spatrick M_WAITOK|M_ZERO);
88623a4b636Spatrick
88723a4b636Spatrick sc->sc_ic.ic_node = faa->fa_node;
88823a4b636Spatrick sc->sc_ic.ic_cookie = sc;
88923a4b636Spatrick sc->sc_ic.ic_establish_msi = ampintc_intr_establish_msi;
89023a4b636Spatrick sc->sc_ic.ic_disestablish = ampintc_intr_disestablish_msi;
89123a4b636Spatrick arm_intr_register_fdt(&sc->sc_ic);
89223a4b636Spatrick }
89323a4b636Spatrick
89423a4b636Spatrick 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)89523a4b636Spatrick ampintc_intr_establish_msi(void *self, uint64_t *addr, uint64_t *data,
896789e88a4Spatrick int level, struct cpu_info *ci, int (*func)(void *), void *arg, char *name)
89723a4b636Spatrick {
89823a4b636Spatrick struct ampintc_msi_softc *sc = (struct ampintc_msi_softc *)self;
89923a4b636Spatrick void *cookie;
90023a4b636Spatrick int i;
90123a4b636Spatrick
90223a4b636Spatrick for (i = 0; i < sc->sc_nspi; i++) {
90323a4b636Spatrick if (sc->sc_spi[i] != NULL)
90423a4b636Spatrick continue;
90523a4b636Spatrick
90623a4b636Spatrick cookie = ampintc_intr_establish(sc->sc_bspi + i,
907789e88a4Spatrick IST_EDGE_RISING, level, ci, func, arg, name);
90823a4b636Spatrick if (cookie == NULL)
90923a4b636Spatrick return NULL;
91023a4b636Spatrick
91123a4b636Spatrick *addr = sc->sc_addr + GICV2M_SETSPI_NS;
91223a4b636Spatrick *data = sc->sc_bspi + i;
91323a4b636Spatrick sc->sc_spi[i] = cookie;
91423a4b636Spatrick return &sc->sc_spi[i];
91523a4b636Spatrick }
91623a4b636Spatrick
91723a4b636Spatrick return NULL;
91823a4b636Spatrick }
91923a4b636Spatrick
92023a4b636Spatrick void
ampintc_intr_disestablish_msi(void * cookie)92123a4b636Spatrick ampintc_intr_disestablish_msi(void *cookie)
92223a4b636Spatrick {
92323a4b636Spatrick ampintc_intr_disestablish(*(void **)cookie);
92423a4b636Spatrick *(void **)cookie = NULL;
92523a4b636Spatrick }
926ad0d549bSkettenis
927ad0d549bSkettenis #ifdef MULTIPROCESSOR
928ad0d549bSkettenis int
ampintc_ipi_ddb(void * v)929ad0d549bSkettenis ampintc_ipi_ddb(void *v)
930ad0d549bSkettenis {
931ad0d549bSkettenis /* XXX */
932ad0d549bSkettenis db_enter();
933ad0d549bSkettenis return 1;
934ad0d549bSkettenis }
935ad0d549bSkettenis
936ad0d549bSkettenis int
ampintc_ipi_nop(void * v)937ad0d549bSkettenis ampintc_ipi_nop(void *v)
938ad0d549bSkettenis {
939ad0d549bSkettenis /* Nothing to do here, just enough to wake up from WFI */
940ad0d549bSkettenis return 1;
941ad0d549bSkettenis }
942ad0d549bSkettenis
943ad0d549bSkettenis int
ampintc_ipi_combined(void * v)944ad0d549bSkettenis ampintc_ipi_combined(void *v)
945ad0d549bSkettenis {
946ad0d549bSkettenis struct ampintc_softc *sc = (struct ampintc_softc *)v;
947ad0d549bSkettenis
948ad0d549bSkettenis if (sc->sc_ipi_reason[cpu_number()] == ARM_IPI_DDB) {
949ad0d549bSkettenis sc->sc_ipi_reason[cpu_number()] = ARM_IPI_NOP;
950ad0d549bSkettenis return ampintc_ipi_ddb(v);
951ad0d549bSkettenis } else {
952ad0d549bSkettenis return ampintc_ipi_nop(v);
953ad0d549bSkettenis }
954ad0d549bSkettenis }
955ad0d549bSkettenis
956ad0d549bSkettenis void
ampintc_send_ipi(struct cpu_info * ci,int id)957ad0d549bSkettenis ampintc_send_ipi(struct cpu_info *ci, int id)
958ad0d549bSkettenis {
959ad0d549bSkettenis struct ampintc_softc *sc = ampintc;
960ad0d549bSkettenis int sendmask;
961ad0d549bSkettenis
962ad0d549bSkettenis if (ci == curcpu() && id == ARM_IPI_NOP)
963ad0d549bSkettenis return;
964ad0d549bSkettenis
965ad0d549bSkettenis /* never overwrite IPI_DDB with IPI_NOP */
966ad0d549bSkettenis if (id == ARM_IPI_DDB)
967ad0d549bSkettenis sc->sc_ipi_reason[ci->ci_cpuid] = id;
968ad0d549bSkettenis
969ad0d549bSkettenis /* currently will only send to one cpu */
970ad0d549bSkettenis sendmask = 1 << (16 + ci->ci_cpuid);
971ad0d549bSkettenis sendmask |= sc->sc_ipi_num[id];
972ad0d549bSkettenis
973ad0d549bSkettenis bus_space_write_4(sc->sc_iot, sc->sc_d_ioh, ICD_SGIR, sendmask);
974ad0d549bSkettenis }
975ad0d549bSkettenis #endif
976