xref: /openbsd-src/sys/arch/arm64/dev/agintc.c (revision 53f700b058a22d69a8dcc857ca8f40458297214f)
1*53f700b0Skettenis /* $OpenBSD: agintc.c,v 1.62 2025/01/24 20:17:28 kettenis Exp $ */
266f727bcSkettenis /*
366f727bcSkettenis  * Copyright (c) 2007, 2009, 2011, 2017 Dale Rahn <drahn@dalerahn.com>
4f0a2ac19Skettenis  * Copyright (c) 2018 Mark Kettenis <kettenis@openbsd.org>
566f727bcSkettenis  *
666f727bcSkettenis  * Permission to use, copy, modify, and distribute this software for any
766f727bcSkettenis  * purpose with or without fee is hereby granted, provided that the above
866f727bcSkettenis  * copyright notice and this permission notice appear in all copies.
966f727bcSkettenis  *
1066f727bcSkettenis  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1166f727bcSkettenis  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1266f727bcSkettenis  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1366f727bcSkettenis  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1466f727bcSkettenis  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1566f727bcSkettenis  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1666f727bcSkettenis  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1766f727bcSkettenis  */
1866f727bcSkettenis 
1966f727bcSkettenis /*
2066f727bcSkettenis  * This is a device driver for the GICv3/GICv4 IP from ARM as specified
2166f727bcSkettenis  * in IHI0069C, an example of this hardware is the GIC 500.
2266f727bcSkettenis  */
2366f727bcSkettenis 
2466f727bcSkettenis #include <sys/param.h>
2566f727bcSkettenis #include <sys/systm.h>
2666f727bcSkettenis #include <sys/queue.h>
2766f727bcSkettenis #include <sys/malloc.h>
2866f727bcSkettenis #include <sys/device.h>
2966f727bcSkettenis #include <sys/evcount.h>
3066f727bcSkettenis 
3166f727bcSkettenis #include <machine/bus.h>
322896618dSkettenis #include <machine/cpufunc.h>
3366f727bcSkettenis #include <machine/fdt.h>
3466f727bcSkettenis 
3566f727bcSkettenis #include <dev/ofw/fdt.h>
3666f727bcSkettenis #include <dev/ofw/openfirm.h>
3766f727bcSkettenis 
3894673892Sjsg #include <machine/simplebusvar.h>
39f0a2ac19Skettenis 
4066f727bcSkettenis #define ICC_PMR		s3_0_c4_c6_0
4166f727bcSkettenis #define ICC_IAR0	s3_0_c12_c8_0
4266f727bcSkettenis #define ICC_EOIR0	s3_0_c12_c8_1
4366f727bcSkettenis #define ICC_HPPIR0	s3_0_c12_c8_2
4466f727bcSkettenis #define ICC_BPR0	s3_0_c12_c8_3
4566f727bcSkettenis 
4666f727bcSkettenis #define ICC_DIR		s3_0_c12_c11_1
479a716617Skettenis #define ICC_RPR		s3_0_c12_c11_3
4866f727bcSkettenis #define ICC_SGI1R	s3_0_c12_c11_5
4966f727bcSkettenis #define ICC_SGI0R	s3_0_c12_c11_7
5066f727bcSkettenis 
5166f727bcSkettenis #define ICC_IAR1	s3_0_c12_c12_0
5266f727bcSkettenis #define ICC_EOIR1	s3_0_c12_c12_1
5366f727bcSkettenis #define ICC_HPPIR1	s3_0_c12_c12_2
5466f727bcSkettenis #define ICC_BPR1	s3_0_c12_c12_3
5566f727bcSkettenis #define ICC_CTLR	s3_0_c12_c12_4
5666f727bcSkettenis #define ICC_SRE_EL1	s3_0_c12_c12_5
5766f727bcSkettenis #define  ICC_SRE_EL1_EN		0x7
5866f727bcSkettenis #define ICC_IGRPEN0	s3_0_c12_c12_6
5966f727bcSkettenis #define ICC_IGRPEN1	s3_0_c12_c12_7
6066f727bcSkettenis 
6166f727bcSkettenis #define _STR(x) #x
6266f727bcSkettenis #define STR(x) _STR(x)
6366f727bcSkettenis 
6466f727bcSkettenis /* distributor registers */
6566f727bcSkettenis #define GICD_CTLR		0x0000
6666f727bcSkettenis /* non-secure */
6766f727bcSkettenis #define  GICD_CTLR_RWP			(1U << 31)
68163120c5Skettenis #define  GICD_CTLR_EnableGrp1		(1 << 0)
69163120c5Skettenis #define  GICD_CTLR_EnableGrp1A		(1 << 1)
70163120c5Skettenis #define  GICD_CTLR_ARE_NS		(1 << 4)
71163120c5Skettenis #define  GICD_CTLR_DS			(1 << 6)
7266f727bcSkettenis #define GICD_TYPER		0x0004
738b7bc089Skettenis #define  GICD_TYPER_MBIS		(1 << 16)
745a4b4c9bSkettenis #define  GICD_TYPER_LPIS		(1 << 17)
75ab5b2c8cSkettenis #define  GICD_TYPER_ITLINE_M		0x1f
7666f727bcSkettenis #define GICD_IIDR		0x0008
778b7bc089Skettenis #define GICD_SETSPI_NSR		0x0040
788b7bc089Skettenis #define GICD_CLRSPI_NSR		0x0048
79d532cfaeSpatrick #define GICD_IGROUPR(i)		(0x0080 + (IRQ_TO_REG32(i) * 4))
8066f727bcSkettenis #define GICD_ISENABLER(i)	(0x0100 + (IRQ_TO_REG32(i) * 4))
8166f727bcSkettenis #define GICD_ICENABLER(i)	(0x0180 + (IRQ_TO_REG32(i) * 4))
8266f727bcSkettenis #define GICD_ISPENDR(i)		(0x0200 + (IRQ_TO_REG32(i) * 4))
8366f727bcSkettenis #define GICD_ICPENDR(i)		(0x0280 + (IRQ_TO_REG32(i) * 4))
8466f727bcSkettenis #define GICD_ISACTIVER(i)	(0x0300 + (IRQ_TO_REG32(i) * 4))
8566f727bcSkettenis #define GICD_ICACTIVER(i)	(0x0380 + (IRQ_TO_REG32(i) * 4))
8666f727bcSkettenis #define GICD_IPRIORITYR(i)	(0x0400 + (i))
8766f727bcSkettenis #define GICD_ICFGR(i)		(0x0c00 + (IRQ_TO_REG16(i) * 4))
886214007cSpatrick #define  GICD_ICFGR_TRIG_LEVEL(i)	(0x0 << (IRQ_TO_REG16BIT(i) * 2))
896214007cSpatrick #define  GICD_ICFGR_TRIG_EDGE(i)	(0x2 << (IRQ_TO_REG16BIT(i) * 2))
906214007cSpatrick #define  GICD_ICFGR_TRIG_MASK(i)	(0x2 << (IRQ_TO_REG16BIT(i) * 2))
91d532cfaeSpatrick #define GICD_IGRPMODR(i)	(0x0d00 + (IRQ_TO_REG32(i) * 4))
9216b913cdSkettenis #define GICD_NSACR(i)		(0x0e00 + (IRQ_TO_REG16(i) * 4))
9366f727bcSkettenis #define GICD_IROUTER(i)		(0x6000 + ((i) * 8))
9466f727bcSkettenis 
9566f727bcSkettenis /* redistributor registers */
9666f727bcSkettenis #define GICR_CTLR		0x00000
9766f727bcSkettenis #define  GICR_CTLR_RWP			((1U << 31) | (1 << 3))
98f0a2ac19Skettenis #define  GICR_CTLR_ENABLE_LPIS		(1 << 0)
9966f727bcSkettenis #define GICR_IIDR		0x00004
10066f727bcSkettenis #define GICR_TYPER		0x00008
10166f727bcSkettenis #define  GICR_TYPER_LAST		(1 << 4)
10266f727bcSkettenis #define  GICR_TYPER_VLPIS		(1 << 1)
10366f727bcSkettenis #define GICR_WAKER		0x00014
10466f727bcSkettenis #define  GICR_WAKER_X31			(1U << 31)
10566f727bcSkettenis #define  GICR_WAKER_CHILDRENASLEEP	(1 << 2)
10666f727bcSkettenis #define  GICR_WAKER_PROCESSORSLEEP	(1 << 1)
10766f727bcSkettenis #define  GICR_WAKER_X0			(1 << 0)
108f0a2ac19Skettenis #define GICR_PROPBASER		0x00070
109f0a2ac19Skettenis #define  GICR_PROPBASER_ISH		(1ULL << 10)
110f0a2ac19Skettenis #define  GICR_PROPBASER_IC_NORM_NC	(1ULL << 7)
111f0a2ac19Skettenis #define GICR_PENDBASER		0x00078
112f0a2ac19Skettenis #define  GICR_PENDBASER_PTZ		(1ULL << 62)
113f0a2ac19Skettenis #define  GICR_PENDBASER_ISH		(1ULL << 10)
114f0a2ac19Skettenis #define  GICR_PENDBASER_IC_NORM_NC	(1ULL << 7)
115d532cfaeSpatrick #define GICR_IGROUPR0		0x10080
11666f727bcSkettenis #define GICR_ISENABLE0		0x10100
11766f727bcSkettenis #define GICR_ICENABLE0		0x10180
11866f727bcSkettenis #define GICR_ISPENDR0		0x10200
11966f727bcSkettenis #define GICR_ICPENDR0		0x10280
12066f727bcSkettenis #define GICR_ISACTIVE0		0x10300
12166f727bcSkettenis #define GICR_ICACTIVE0		0x10380
12266f727bcSkettenis #define GICR_IPRIORITYR(i)	(0x10400 + (i))
12366f727bcSkettenis #define GICR_ICFGR0		0x10c00
12466f727bcSkettenis #define GICR_ICFGR1		0x10c04
125d532cfaeSpatrick #define GICR_IGRPMODR0		0x10d00
12666f727bcSkettenis 
127f0a2ac19Skettenis #define GICR_PROP_SIZE		(64 * 1024)
12816b913cdSkettenis #define  GICR_PROP_GROUP1	(1 << 1)
129f0a2ac19Skettenis #define  GICR_PROP_ENABLE	(1 << 0)
130f0a2ac19Skettenis #define GICR_PEND_SIZE		(64 * 1024)
131f0a2ac19Skettenis 
132f0a2ac19Skettenis #define PPI_BASE		16
133f0a2ac19Skettenis #define SPI_BASE		32
134f0a2ac19Skettenis #define LPI_BASE		8192
135f0a2ac19Skettenis 
1367d4ece3aSdrahn #define IRQ_TO_REG32(i)		(((i) >> 5) & 0x1f)
13766f727bcSkettenis #define IRQ_TO_REG32BIT(i)	((i) & 0x1f)
13866f727bcSkettenis 
1397d4ece3aSdrahn #define IRQ_TO_REG16(i)		(((i) >> 4) & 0x3f)
14066f727bcSkettenis #define IRQ_TO_REG16BIT(i)	((i) & 0xf)
14166f727bcSkettenis 
14266f727bcSkettenis #define IRQ_ENABLE	1
14366f727bcSkettenis #define IRQ_DISABLE	0
14466f727bcSkettenis 
1458b7bc089Skettenis struct agintc_mbi_range {
1468b7bc089Skettenis 	int			  mr_base;
1478b7bc089Skettenis 	int			  mr_span;
1488b7bc089Skettenis 	void			**mr_mbi;
1498b7bc089Skettenis };
1508b7bc089Skettenis 
151f83e6fb8Spatrick struct agintc_lpi_info {
152f83e6fb8Spatrick 	struct agintc_msi_softc	*li_msic;
153f83e6fb8Spatrick 	struct cpu_info		*li_ci;
154f83e6fb8Spatrick 	uint32_t		 li_deviceid;
155f83e6fb8Spatrick 	uint32_t		 li_eventid;
156f83e6fb8Spatrick 	struct intrhand		*li_ih;
157f83e6fb8Spatrick };
158f83e6fb8Spatrick 
15966f727bcSkettenis struct agintc_softc {
160f0a2ac19Skettenis 	struct simplebus_softc	 sc_sbus;
161f0a2ac19Skettenis 	struct intrq		*sc_handler;
162f83e6fb8Spatrick 	struct agintc_lpi_info	**sc_lpi;
16366f727bcSkettenis 	bus_space_tag_t		 sc_iot;
16466f727bcSkettenis 	bus_space_handle_t	 sc_d_ioh;
165934f5440Skettenis 	bus_space_handle_t	*sc_r_ioh;
166*53f700b0Skettenis 	bus_space_handle_t	*sc_rbase_ioh;
167f0a2ac19Skettenis 	bus_dma_tag_t		 sc_dmat;
1687c59a772Spatrick 	uint16_t		*sc_processor;
1695ce1efbdSkettenis 	int			 sc_cpuremap[MAXCPUS];
17066f727bcSkettenis 	int			 sc_nintr;
171f0a2ac19Skettenis 	int			 sc_nlpi;
1728b7bc089Skettenis 	bus_addr_t		 sc_mbi_addr;
1738b7bc089Skettenis 	int			 sc_mbi_nranges;
1748b7bc089Skettenis 	struct agintc_mbi_range	*sc_mbi_ranges;
175163120c5Skettenis 	int			 sc_prio_shift;
176163120c5Skettenis 	int			 sc_pmr_shift;
17716b913cdSkettenis 	int			 sc_rk3399_quirk;
17866f727bcSkettenis 	struct evcount		 sc_spur;
17966f727bcSkettenis 	int			 sc_ncells;
18066f727bcSkettenis 	int			 sc_num_redist;
181*53f700b0Skettenis 	int			 sc_num_redist_regions;
182f0a2ac19Skettenis 	struct agintc_dmamem	*sc_prop;
183f0a2ac19Skettenis 	struct agintc_dmamem	*sc_pend;
18466f727bcSkettenis 	struct interrupt_controller sc_ic;
1854002e08dSkettenis 	int			 sc_ipi_num[3]; /* id for each ipi */
1864002e08dSkettenis 	int			 sc_ipi_reason[MAXCPUS]; /* cause of ipi */
1874002e08dSkettenis 	void			*sc_ipi_irq[3]; /* irqhandle for each ipi */
18866f727bcSkettenis };
18966f727bcSkettenis struct agintc_softc *agintc_sc;
19066f727bcSkettenis 
19166f727bcSkettenis struct intrhand {
19266f727bcSkettenis 	TAILQ_ENTRY(intrhand)	 ih_list;		/* link on intrq list */
19366f727bcSkettenis 	int			(*ih_func)(void *);	/* handler */
19466f727bcSkettenis 	void			*ih_arg;		/* arg for handler */
19566f727bcSkettenis 	int			 ih_ipl;		/* IPL_* */
1967a7b3facSkettenis 	int			 ih_flags;
19766f727bcSkettenis 	int			 ih_irq;		/* IRQ number */
19866f727bcSkettenis 	struct evcount		 ih_count;
19966f727bcSkettenis 	char			*ih_name;
200452daaedSpatrick 	struct cpu_info		*ih_ci;			/* CPU the IRQ runs on */
20166f727bcSkettenis };
20266f727bcSkettenis 
20366f727bcSkettenis struct intrq {
20466f727bcSkettenis 	TAILQ_HEAD(, intrhand)	iq_list;	/* handler list */
2057c59a772Spatrick 	struct cpu_info		*iq_ci;		/* CPU the IRQ runs on */
206eb0c9b3cSpatrick 	int			iq_irq_max;	/* IRQ to mask while handling */
207eb0c9b3cSpatrick 	int			iq_irq_min;	/* lowest IRQ when shared */
20866f727bcSkettenis 	int			iq_ist;		/* share type */
209d7e3db9cSkettenis 	int			iq_route;
21066f727bcSkettenis };
21166f727bcSkettenis 
212f0a2ac19Skettenis struct agintc_dmamem {
213f0a2ac19Skettenis 	bus_dmamap_t		adm_map;
214f0a2ac19Skettenis 	bus_dma_segment_t	adm_seg;
215f0a2ac19Skettenis 	size_t			adm_size;
216f0a2ac19Skettenis 	caddr_t			adm_kva;
217f0a2ac19Skettenis };
218f0a2ac19Skettenis 
219f0a2ac19Skettenis #define AGINTC_DMA_MAP(_adm)	((_adm)->adm_map)
220f0a2ac19Skettenis #define AGINTC_DMA_LEN(_adm)	((_adm)->adm_size)
221f0a2ac19Skettenis #define AGINTC_DMA_DVA(_adm)	((_adm)->adm_map->dm_segs[0].ds_addr)
222f0a2ac19Skettenis #define AGINTC_DMA_KVA(_adm)	((void *)(_adm)->adm_kva)
223f0a2ac19Skettenis 
224f0a2ac19Skettenis struct agintc_dmamem *agintc_dmamem_alloc(bus_dma_tag_t, bus_size_t,
225f0a2ac19Skettenis 		    bus_size_t);
226f0a2ac19Skettenis void		agintc_dmamem_free(bus_dma_tag_t, struct agintc_dmamem *);
227f0a2ac19Skettenis 
22866f727bcSkettenis int		agintc_match(struct device *, void *, void *);
22966f727bcSkettenis void		agintc_attach(struct device *, struct device *, void *);
2308b7bc089Skettenis void		agintc_mbiinit(struct agintc_softc *, int, bus_addr_t);
23166f727bcSkettenis void		agintc_cpuinit(void);
23266f727bcSkettenis int		agintc_spllower(int);
23366f727bcSkettenis void		agintc_splx(int);
23466f727bcSkettenis int		agintc_splraise(int);
23566f727bcSkettenis void		agintc_setipl(int);
23625da8179Spatrick void		agintc_enable_wakeup(void);
23725da8179Spatrick void		agintc_disable_wakeup(void);
23866f727bcSkettenis void		agintc_calc_mask(void);
23966f727bcSkettenis void		agintc_calc_irq(struct agintc_softc *sc, int irq);
2406214007cSpatrick void		*agintc_intr_establish(int, int, int, struct cpu_info *,
241789e88a4Spatrick 		    int (*)(void *), void *, char *);
24266f727bcSkettenis void		*agintc_intr_establish_fdt(void *cookie, int *cell, int level,
243789e88a4Spatrick 		    struct cpu_info *, int (*func)(void *), void *arg, char *name);
2448b7bc089Skettenis void		*agintc_intr_establish_mbi(void *, uint64_t *, uint64_t *,
2458b7bc089Skettenis 		    int , struct cpu_info *, int (*)(void *), void *, char *);
24666f727bcSkettenis void		agintc_intr_disestablish(void *);
24725da8179Spatrick void		agintc_intr_set_wakeup(void *);
24866f727bcSkettenis void		agintc_irq_handler(void *);
24966f727bcSkettenis uint32_t	agintc_iack(void);
25066f727bcSkettenis void		agintc_eoi(uint32_t);
25166f727bcSkettenis void		agintc_set_priority(struct agintc_softc *sc, int, int);
25266f727bcSkettenis void		agintc_intr_enable(struct agintc_softc *, int);
25366f727bcSkettenis void		agintc_intr_disable(struct agintc_softc *, int);
2546214007cSpatrick void		agintc_intr_config(struct agintc_softc *, int, int);
25566f727bcSkettenis void		agintc_route(struct agintc_softc *, int, int,
25666f727bcSkettenis 		    struct cpu_info *);
257d7e3db9cSkettenis void		agintc_route_irq(void *, int, struct cpu_info *);
258452daaedSpatrick void		agintc_intr_barrier(void *);
25966f727bcSkettenis void		agintc_r_wait_rwp(struct agintc_softc *sc);
26066f727bcSkettenis 
261d7e3db9cSkettenis int		agintc_ipi_ddb(void *v);
2624002e08dSkettenis int		agintc_ipi_halt(void *v);
263d7e3db9cSkettenis int		agintc_ipi_nop(void *v);
264d7e3db9cSkettenis int		agintc_ipi_combined(void *);
265d7e3db9cSkettenis void		agintc_send_ipi(struct cpu_info *, int);
266d7e3db9cSkettenis 
267f83e6fb8Spatrick void		agintc_msi_discard(struct agintc_lpi_info *);
268f83e6fb8Spatrick void		agintc_msi_inv(struct agintc_lpi_info *);
269c60fe750Spatrick 
2709fdf0c62Smpi const struct cfattach	agintc_ca = {
27166f727bcSkettenis 	sizeof (struct agintc_softc), agintc_match, agintc_attach
27266f727bcSkettenis };
27366f727bcSkettenis 
27466f727bcSkettenis struct cfdriver agintc_cd = {
27566f727bcSkettenis 	NULL, "agintc", DV_DULL
27666f727bcSkettenis };
27766f727bcSkettenis 
27866f727bcSkettenis static char *agintc_compatibles[] = {
27966f727bcSkettenis 	"arm,gic-v3",
28066f727bcSkettenis 	"arm,gic-v4",
28166f727bcSkettenis 	NULL
28266f727bcSkettenis };
28366f727bcSkettenis 
28466f727bcSkettenis int
28566f727bcSkettenis agintc_match(struct device *parent, void *cfdata, void *aux)
28666f727bcSkettenis {
28766f727bcSkettenis 	struct fdt_attach_args *faa = aux;
28866f727bcSkettenis 	int i;
28966f727bcSkettenis 
29066f727bcSkettenis 	for (i = 0; agintc_compatibles[i]; i++)
29166f727bcSkettenis 		if (OF_is_compatible(faa->fa_node, agintc_compatibles[i]))
29266f727bcSkettenis 			return (1);
29366f727bcSkettenis 
29466f727bcSkettenis 	return (0);
29566f727bcSkettenis }
29666f727bcSkettenis 
29766f727bcSkettenis static void
29866f727bcSkettenis __isb(void)
29966f727bcSkettenis {
30066f727bcSkettenis 	__asm volatile("isb");
30166f727bcSkettenis }
30266f727bcSkettenis 
30366f727bcSkettenis void
30466f727bcSkettenis agintc_attach(struct device *parent, struct device *self, void *aux)
30566f727bcSkettenis {
30666f727bcSkettenis 	struct agintc_softc	*sc = (struct agintc_softc *)self;
30766f727bcSkettenis 	struct fdt_attach_args	*faa = aux;
3087c59a772Spatrick 	struct cpu_info		*ci;
3097c59a772Spatrick 	CPU_INFO_ITERATOR	 cii;
310daf2e6ccSkettenis 	u_long			 psw;
311f0a2ac19Skettenis 	uint32_t		 typer;
31216b913cdSkettenis 	uint32_t		 nsacr, oldnsacr;
313163120c5Skettenis 	uint32_t		 pmr, oldpmr;
31416b913cdSkettenis 	uint32_t		 ctrl, bits;
3157c59a772Spatrick 	uint32_t		 affinity;
31615e1ce75Spatrick 	uint64_t		 redist_stride;
317931e8929Spatrick 	int			 i, nbits, nintr;
318*53f700b0Skettenis 	int			 idx, offset, nredist;
319d7e3db9cSkettenis #ifdef MULTIPROCESSOR
3204002e08dSkettenis 	int			 nipi, ipiirq[3];
321d7e3db9cSkettenis #endif
32266f727bcSkettenis 
323daf2e6ccSkettenis 	psw = intr_disable();
32466f727bcSkettenis 	arm_init_smask();
32566f727bcSkettenis 
32666f727bcSkettenis 	sc->sc_iot = faa->fa_iot;
327f0a2ac19Skettenis 	sc->sc_dmat = faa->fa_dmat;
32866f727bcSkettenis 
329*53f700b0Skettenis 	sc->sc_num_redist_regions =
330*53f700b0Skettenis 	    OF_getpropint(faa->fa_node, "#redistributor-regions", 1);
331*53f700b0Skettenis 
332*53f700b0Skettenis 	if (faa->fa_nreg < sc->sc_num_redist_regions + 1)
333*53f700b0Skettenis 		panic("%s: missing registers", __func__);
334*53f700b0Skettenis 
33566f727bcSkettenis 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
33666f727bcSkettenis 	    faa->fa_reg[0].size, 0, &sc->sc_d_ioh))
337*53f700b0Skettenis 		panic("%s: GICD bus_space_map failed", __func__);
33866f727bcSkettenis 
339*53f700b0Skettenis 	sc->sc_rbase_ioh = mallocarray(sc->sc_num_redist_regions,
340*53f700b0Skettenis 	    sizeof(*sc->sc_rbase_ioh), M_DEVBUF, M_WAITOK);
341*53f700b0Skettenis 	for (idx = 0; idx < sc->sc_num_redist_regions; idx++) {
342*53f700b0Skettenis 		if (bus_space_map(sc->sc_iot, faa->fa_reg[1 + idx].addr,
343*53f700b0Skettenis 		    faa->fa_reg[1 + idx].size, 0, &sc->sc_rbase_ioh[idx]))
344*53f700b0Skettenis 			panic("%s: GICR bus_space_map failed", __func__);
345*53f700b0Skettenis 	}
34666f727bcSkettenis 
347f0a2ac19Skettenis 	typer = bus_space_read_4(sc->sc_iot, sc->sc_d_ioh, GICD_TYPER);
348f0a2ac19Skettenis 
349f0a2ac19Skettenis 	if (typer & GICD_TYPER_LPIS) {
350f0a2ac19Skettenis 		/* Allocate redistributor tables */
351f0a2ac19Skettenis 		sc->sc_prop = agintc_dmamem_alloc(sc->sc_dmat,
352f0a2ac19Skettenis 		    GICR_PROP_SIZE, GICR_PROP_SIZE);
353f0a2ac19Skettenis 		if (sc->sc_prop == NULL) {
354f0a2ac19Skettenis 			printf(": can't alloc LPI config table\n");
355f0a2ac19Skettenis 			goto unmap;
356f0a2ac19Skettenis 		}
357f0a2ac19Skettenis 		sc->sc_pend = agintc_dmamem_alloc(sc->sc_dmat,
358f0a2ac19Skettenis 		    GICR_PEND_SIZE, GICR_PEND_SIZE);
3597bbdd582Spatrick 		if (sc->sc_pend == NULL) {
360f0a2ac19Skettenis 			printf(": can't alloc LPI pending table\n");
361f0a2ac19Skettenis 			goto unmap;
362f0a2ac19Skettenis 		}
363f0a2ac19Skettenis 
364f0a2ac19Skettenis 		/* Minimum number of LPIs supported by any implementation. */
365f0a2ac19Skettenis 		sc->sc_nlpi = 8192;
366f0a2ac19Skettenis 	}
367f0a2ac19Skettenis 
3688b7bc089Skettenis 	if (typer & GICD_TYPER_MBIS)
3698b7bc089Skettenis 		agintc_mbiinit(sc, faa->fa_node, faa->fa_reg[0].addr);
3708b7bc089Skettenis 
37116b913cdSkettenis 	/*
372163120c5Skettenis 	 * We are guaranteed to have at least 16 priority levels, so
373163120c5Skettenis 	 * in principle we just want to use the top 4 bits of the
374163120c5Skettenis 	 * (non-secure) priority field.
375163120c5Skettenis 	 */
376163120c5Skettenis 	sc->sc_prio_shift = sc->sc_pmr_shift = 4;
377163120c5Skettenis 
378163120c5Skettenis 	/*
379163120c5Skettenis 	 * If the system supports two security states and SCR_EL3.FIQ
380163120c5Skettenis 	 * is zero, the non-secure shifted view applies.  We detect
381163120c5Skettenis 	 * this by checking whether the number of writable bits
3829ccaae38Skettenis 	 * matches the number of implemented priority bits.  If that
3839ccaae38Skettenis 	 * is the case we will need to adjust the priorities that we
3849ccaae38Skettenis 	 * write into ICC_PMR_EL1 accordingly.
3859ccaae38Skettenis 	 *
3869ccaae38Skettenis 	 * On Ampere eMAG it appears as if there are five writable
3879ccaae38Skettenis 	 * bits when we write 0xff.  But for higher priorities
3889ccaae38Skettenis 	 * (smaller values) only the top 4 bits stick.  So we use 0xbf
3899ccaae38Skettenis 	 * instead to determine the number of writable bits.
390163120c5Skettenis 	 */
391163120c5Skettenis 	ctrl = bus_space_read_4(sc->sc_iot, sc->sc_d_ioh, GICD_CTLR);
392163120c5Skettenis 	if ((ctrl & GICD_CTLR_DS) == 0) {
393163120c5Skettenis 		__asm volatile("mrs %x0, "STR(ICC_CTLR_EL1) : "=r"(ctrl));
394163120c5Skettenis 		nbits = ICC_CTLR_EL1_PRIBITS(ctrl) + 1;
395163120c5Skettenis 		__asm volatile("mrs %x0, "STR(ICC_PMR) : "=r"(oldpmr));
3969ccaae38Skettenis 		__asm volatile("msr "STR(ICC_PMR)", %x0" :: "r"(0xbf));
397163120c5Skettenis 		__asm volatile("mrs %x0, "STR(ICC_PMR) : "=r"(pmr));
398163120c5Skettenis 		__asm volatile("msr "STR(ICC_PMR)", %x0" :: "r"(oldpmr));
399163120c5Skettenis 		if (nbits == 8 - (ffs(pmr) - 1))
400163120c5Skettenis 			sc->sc_pmr_shift--;
401163120c5Skettenis 	}
402163120c5Skettenis 
403163120c5Skettenis 	/*
40416b913cdSkettenis 	 * The Rockchip RK3399 is busted.  Its GIC-500 treats all
40516b913cdSkettenis 	 * access to its memory mapped registers as "secure".  As a
40616b913cdSkettenis 	 * result, several registers don't behave as expected.  For
40716b913cdSkettenis 	 * example, the GICD_IPRIORITYRn and GICR_IPRIORITYRn
40816b913cdSkettenis 	 * registers expose the full priority range available to
40916b913cdSkettenis 	 * secure interrupts.  We need to be aware of this and write
41016b913cdSkettenis 	 * an adjusted priority value into these registers.  We also
41116b913cdSkettenis 	 * need to be careful not to touch any bits that shouldn't be
41216b913cdSkettenis 	 * writable in non-secure mode.
41316b913cdSkettenis 	 *
41416b913cdSkettenis 	 * We check whether we have secure mode access to these
41516b913cdSkettenis 	 * registers by attempting to write to the GICD_NSACR register
416163120c5Skettenis 	 * and check whether its contents actually change.  In that
417163120c5Skettenis 	 * case we need to adjust the priorities we write into
418163120c5Skettenis 	 * GICD_IPRIORITYRn and GICRIPRIORITYRn accordingly.
41916b913cdSkettenis 	 */
42016b913cdSkettenis 	oldnsacr = bus_space_read_4(sc->sc_iot, sc->sc_d_ioh, GICD_NSACR(32));
42116b913cdSkettenis 	bus_space_write_4(sc->sc_iot, sc->sc_d_ioh, GICD_NSACR(32),
42216b913cdSkettenis 	    oldnsacr ^ 0xffffffff);
42316b913cdSkettenis 	nsacr = bus_space_read_4(sc->sc_iot, sc->sc_d_ioh, GICD_NSACR(32));
42416b913cdSkettenis 	if (nsacr != oldnsacr) {
42516b913cdSkettenis 		bus_space_write_4(sc->sc_iot, sc->sc_d_ioh, GICD_NSACR(32),
42616b913cdSkettenis 		    oldnsacr);
42716b913cdSkettenis 		sc->sc_rk3399_quirk = 1;
428163120c5Skettenis 		sc->sc_prio_shift--;
429163120c5Skettenis 		printf(" sec");
43016b913cdSkettenis 	}
43116b913cdSkettenis 
432163120c5Skettenis 	printf(" shift %d:%d", sc->sc_prio_shift, sc->sc_pmr_shift);
433163120c5Skettenis 
43466f727bcSkettenis 	evcount_attach(&sc->sc_spur, "irq1023/spur", NULL);
43566f727bcSkettenis 
43666f727bcSkettenis 	__asm volatile("msr "STR(ICC_SRE_EL1)", %x0" : : "r" (ICC_SRE_EL1_EN));
43766f727bcSkettenis 	__isb();
43866f727bcSkettenis 
439f0a2ac19Skettenis 	nintr = 32 * (typer & GICD_TYPER_ITLINE_M);
44066f727bcSkettenis 	nintr += 32; /* ICD_ICTR + 1, irq 0-31 is SGI, 32+ is PPI */
44166f727bcSkettenis 	sc->sc_nintr = nintr;
44266f727bcSkettenis 
44366f727bcSkettenis 	agintc_sc = sc; /* save this for global access */
44466f727bcSkettenis 
445934f5440Skettenis 	/* find the redistributors. */
446*53f700b0Skettenis 	idx = 0;
44766f727bcSkettenis 	offset = 0;
44815e1ce75Spatrick 	redist_stride = OF_getpropint64(faa->fa_node, "redistributor-stride", 0);
449*53f700b0Skettenis 	for (nredist = 0; idx < sc->sc_num_redist_regions; nredist++) {
45066f727bcSkettenis 		uint64_t typer;
45115e1ce75Spatrick 		int32_t sz;
45266f727bcSkettenis 
453*53f700b0Skettenis 		typer = bus_space_read_8(sc->sc_iot, sc->sc_rbase_ioh[idx],
45466f727bcSkettenis 		    offset + GICR_TYPER);
45566f727bcSkettenis 
45615e1ce75Spatrick 		if (redist_stride == 0) {
45715e1ce75Spatrick 			sz = (64 * 1024 * 2);
45866f727bcSkettenis 			if (typer & GICR_TYPER_VLPIS)
45966f727bcSkettenis 				sz += (64 * 1024 * 2);
46015e1ce75Spatrick 		} else
46115e1ce75Spatrick 			sz = redist_stride;
46266f727bcSkettenis 
463934f5440Skettenis #ifdef DEBUG_AGINTC
464934f5440Skettenis 		printf("probing redistributor %d %x\n", nredist, offset);
465934f5440Skettenis #endif
466934f5440Skettenis 
467934f5440Skettenis 		offset += sz;
468*53f700b0Skettenis 		if (offset >= faa->fa_reg[1 + idx].size ||
469*53f700b0Skettenis 		    typer & GICR_TYPER_LAST) {
470*53f700b0Skettenis 			offset = 0;
471*53f700b0Skettenis 			idx++;
472934f5440Skettenis 		}
473934f5440Skettenis 	}
474934f5440Skettenis 
475*53f700b0Skettenis 	sc->sc_num_redist = nredist;
476163120c5Skettenis 	printf(" nirq %d nredist %d", nintr, sc->sc_num_redist);
477934f5440Skettenis 
478934f5440Skettenis 	sc->sc_r_ioh = mallocarray(sc->sc_num_redist,
479934f5440Skettenis 	    sizeof(*sc->sc_r_ioh), M_DEVBUF, M_WAITOK);
4807c59a772Spatrick 	sc->sc_processor = mallocarray(sc->sc_num_redist,
4817c59a772Spatrick 	    sizeof(*sc->sc_processor), M_DEVBUF, M_WAITOK);
482934f5440Skettenis 
483934f5440Skettenis 	/* submap and configure the redistributors. */
484*53f700b0Skettenis 	idx = 0;
485934f5440Skettenis 	offset = 0;
486934f5440Skettenis 	for (nredist = 0; nredist < sc->sc_num_redist; nredist++) {
487934f5440Skettenis 		uint64_t typer;
48815e1ce75Spatrick 		int32_t sz;
489934f5440Skettenis 
490*53f700b0Skettenis 		typer = bus_space_read_8(sc->sc_iot, sc->sc_rbase_ioh[idx],
491934f5440Skettenis 		    offset + GICR_TYPER);
492934f5440Skettenis 
49315e1ce75Spatrick 		if (redist_stride == 0) {
49415e1ce75Spatrick 			sz = (64 * 1024 * 2);
495934f5440Skettenis 			if (typer & GICR_TYPER_VLPIS)
496934f5440Skettenis 				sz += (64 * 1024 * 2);
49715e1ce75Spatrick 		} else
49815e1ce75Spatrick 			sz = redist_stride;
499934f5440Skettenis 
5007c59a772Spatrick 		affinity = bus_space_read_8(sc->sc_iot,
501*53f700b0Skettenis 		    sc->sc_rbase_ioh[idx], offset + GICR_TYPER) >> 32;
5027c59a772Spatrick 		CPU_INFO_FOREACH(cii, ci) {
5037c59a772Spatrick 			if (affinity == (((ci->ci_mpidr >> 8) & 0xff000000) |
5047c59a772Spatrick 			    (ci->ci_mpidr & 0x00ffffff)))
5057c59a772Spatrick 				break;
5067c59a772Spatrick 		}
50799ec89c2Spatrick 		if (ci != NULL)
50899ec89c2Spatrick 			sc->sc_cpuremap[ci->ci_cpuid] = nredist;
5097c59a772Spatrick 
5107c59a772Spatrick 		sc->sc_processor[nredist] = bus_space_read_8(sc->sc_iot,
511*53f700b0Skettenis 		    sc->sc_rbase_ioh[idx], offset + GICR_TYPER) >> 8;
51266f727bcSkettenis 
513*53f700b0Skettenis 		bus_space_subregion(sc->sc_iot, sc->sc_rbase_ioh[idx],
51466f727bcSkettenis 		    offset, sz, &sc->sc_r_ioh[nredist]);
51566f727bcSkettenis 
516f0a2ac19Skettenis 		if (sc->sc_nlpi > 0) {
517*53f700b0Skettenis 			bus_space_write_8(sc->sc_iot, sc->sc_rbase_ioh[idx],
518f0a2ac19Skettenis 			    offset + GICR_PROPBASER,
519f0a2ac19Skettenis 			    AGINTC_DMA_DVA(sc->sc_prop) |
520f0a2ac19Skettenis 			    GICR_PROPBASER_ISH | GICR_PROPBASER_IC_NORM_NC |
521f0a2ac19Skettenis 			    fls(LPI_BASE + sc->sc_nlpi - 1) - 1);
522*53f700b0Skettenis 			bus_space_write_8(sc->sc_iot, sc->sc_rbase_ioh[idx],
523f0a2ac19Skettenis 			    offset + GICR_PENDBASER,
524f0a2ac19Skettenis 			    AGINTC_DMA_DVA(sc->sc_pend) |
525f0a2ac19Skettenis 			    GICR_PENDBASER_ISH | GICR_PENDBASER_IC_NORM_NC |
526f0a2ac19Skettenis 			    GICR_PENDBASER_PTZ);
527*53f700b0Skettenis 			bus_space_write_4(sc->sc_iot, sc->sc_rbase_ioh[idx],
528f0a2ac19Skettenis 			    offset + GICR_CTLR, GICR_CTLR_ENABLE_LPIS);
529f0a2ac19Skettenis 		}
530f0a2ac19Skettenis 
53166f727bcSkettenis 		offset += sz;
532*53f700b0Skettenis 		if (offset >= faa->fa_reg[1 + idx].size ||
533*53f700b0Skettenis 		    typer & GICR_TYPER_LAST) {
534*53f700b0Skettenis 			offset = 0;
535*53f700b0Skettenis 			idx++;
536*53f700b0Skettenis 		}
53766f727bcSkettenis 	}
53866f727bcSkettenis 
53966f727bcSkettenis 	/* Disable all interrupts, clear all pending */
54066f727bcSkettenis 	for (i = 1; i < nintr / 32; i++) {
54166f727bcSkettenis 		bus_space_write_4(sc->sc_iot, sc->sc_d_ioh,
542105a7236Skettenis 		    GICD_ICACTIVER(i * 32), ~0);
543105a7236Skettenis 		bus_space_write_4(sc->sc_iot, sc->sc_d_ioh,
54466f727bcSkettenis 		    GICD_ICENABLER(i * 32), ~0);
54566f727bcSkettenis 	}
54666f727bcSkettenis 
54766f727bcSkettenis 	for (i = 4; i < nintr; i += 4) {
54866f727bcSkettenis 		/* lowest priority ?? */
54966f727bcSkettenis 		bus_space_write_4(sc->sc_iot, sc->sc_d_ioh,
55066f727bcSkettenis 		    GICD_IPRIORITYR(i), 0xffffffff);
55166f727bcSkettenis 	}
55266f727bcSkettenis 
553d532cfaeSpatrick 	/* Set all interrupts to G1NS */
554d532cfaeSpatrick 	for (i = 1; i < nintr / 32; i++) {
555d532cfaeSpatrick 		bus_space_write_4(sc->sc_iot, sc->sc_d_ioh,
556d532cfaeSpatrick 		    GICD_IGROUPR(i * 32), ~0);
557d532cfaeSpatrick 		bus_space_write_4(sc->sc_iot, sc->sc_d_ioh,
558d532cfaeSpatrick 		    GICD_IGRPMODR(i * 32), 0);
559d532cfaeSpatrick 	}
560d532cfaeSpatrick 
56166f727bcSkettenis 	for (i = 2; i < nintr / 16; i++) {
56266f727bcSkettenis 		/* irq 32 - N */
56366f727bcSkettenis 		bus_space_write_4(sc->sc_iot, sc->sc_d_ioh,
56466f727bcSkettenis 		    GICD_ICFGR(i * 16), 0);
56566f727bcSkettenis 	}
56666f727bcSkettenis 
56766f727bcSkettenis 	agintc_cpuinit();
56866f727bcSkettenis 
569f0a2ac19Skettenis 	sc->sc_handler = mallocarray(nintr,
570f0a2ac19Skettenis 	    sizeof(*sc->sc_handler), M_DEVBUF, M_ZERO | M_WAITOK);
57166f727bcSkettenis 	for (i = 0; i < nintr; i++)
572f0a2ac19Skettenis 		TAILQ_INIT(&sc->sc_handler[i].iq_list);
573f83e6fb8Spatrick 	sc->sc_lpi = mallocarray(sc->sc_nlpi,
574f83e6fb8Spatrick 	    sizeof(*sc->sc_lpi), M_DEVBUF, M_ZERO | M_WAITOK);
57566f727bcSkettenis 
57666f727bcSkettenis 	/* set priority to IPL_HIGH until configure lowers to desired IPL */
57766f727bcSkettenis 	agintc_setipl(IPL_HIGH);
57866f727bcSkettenis 
57966f727bcSkettenis 	/* initialize all interrupts as disabled */
58066f727bcSkettenis 	agintc_calc_mask();
58166f727bcSkettenis 
58266f727bcSkettenis 	/* insert self as interrupt handler */
58366f727bcSkettenis 	arm_set_intr_handler(agintc_splraise, agintc_spllower, agintc_splx,
58425da8179Spatrick 	    agintc_setipl, agintc_irq_handler, NULL,
58525da8179Spatrick 	    agintc_enable_wakeup, agintc_disable_wakeup);
58666f727bcSkettenis 
58766f727bcSkettenis 	/* enable interrupts */
58816b913cdSkettenis 	ctrl = bus_space_read_4(sc->sc_iot, sc->sc_d_ioh, GICD_CTLR);
589163120c5Skettenis 	bits = GICD_CTLR_ARE_NS | GICD_CTLR_EnableGrp1A | GICD_CTLR_EnableGrp1;
59016b913cdSkettenis 	if (sc->sc_rk3399_quirk) {
591163120c5Skettenis 		bits &= ~GICD_CTLR_EnableGrp1A;
59216b913cdSkettenis 		bits <<= 1;
59316b913cdSkettenis 	}
59416b913cdSkettenis 	bus_space_write_4(sc->sc_iot, sc->sc_d_ioh, GICD_CTLR, ctrl | bits);
59566f727bcSkettenis 
59666f727bcSkettenis 	__asm volatile("msr "STR(ICC_PMR)", %x0" :: "r"(0xff));
5979a716617Skettenis 	__asm volatile("msr "STR(ICC_BPR1)", %x0" :: "r"(0));
59816b913cdSkettenis 	__asm volatile("msr "STR(ICC_IGRPEN1)", %x0" :: "r"(1));
59966f727bcSkettenis 
600d7e3db9cSkettenis #ifdef MULTIPROCESSOR
601d7e3db9cSkettenis 	/* setup IPI interrupts */
602d7e3db9cSkettenis 
603d7e3db9cSkettenis 	/*
6044002e08dSkettenis 	 * Ideally we want three IPI interrupts, one for NOP, one for
6054002e08dSkettenis 	 * DDB and one for HALT.  However we can survive if only one
6064002e08dSkettenis 	 * is available; it is possible that most are not available to
6074002e08dSkettenis 	 * the non-secure OS.
608d7e3db9cSkettenis 	 */
609d7e3db9cSkettenis 	nipi = 0;
610d7e3db9cSkettenis 	for (i = 0; i < 16; i++) {
611d7e3db9cSkettenis 		int hwcpu = sc->sc_cpuremap[cpu_number()];
612d7e3db9cSkettenis 		int reg, oldreg;
613d7e3db9cSkettenis 
614d7e3db9cSkettenis 		oldreg = bus_space_read_1(sc->sc_iot, sc->sc_r_ioh[hwcpu],
615d7e3db9cSkettenis 		    GICR_IPRIORITYR(i));
616d7e3db9cSkettenis 		bus_space_write_1(sc->sc_iot, sc->sc_r_ioh[hwcpu],
617d7e3db9cSkettenis 		    GICR_IPRIORITYR(i), oldreg ^ 0x20);
618d7e3db9cSkettenis 
619d7e3db9cSkettenis 		/* if this interrupt is not usable, pri will be unmodified */
620d7e3db9cSkettenis 		reg = bus_space_read_1(sc->sc_iot, sc->sc_r_ioh[hwcpu],
621d7e3db9cSkettenis 		    GICR_IPRIORITYR(i));
622d7e3db9cSkettenis 		if (reg == oldreg)
623d7e3db9cSkettenis 			continue;
624d7e3db9cSkettenis 
625d7e3db9cSkettenis 		/* return to original value, will be set when used */
626d7e3db9cSkettenis 		bus_space_write_1(sc->sc_iot, sc->sc_r_ioh[hwcpu],
627d7e3db9cSkettenis 		    GICR_IPRIORITYR(i), oldreg);
628d7e3db9cSkettenis 
629d7e3db9cSkettenis 		if (nipi == 0)
630d7e3db9cSkettenis 			printf(" ipi: %d", i);
631d7e3db9cSkettenis 		else
632d7e3db9cSkettenis 			printf(", %d", i);
633d7e3db9cSkettenis 		ipiirq[nipi++] = i;
6344002e08dSkettenis 		if (nipi == 3)
635d7e3db9cSkettenis 			break;
636d7e3db9cSkettenis 	}
637d7e3db9cSkettenis 
638d7e3db9cSkettenis 	if (nipi == 0)
639d7e3db9cSkettenis 		panic("no irq available for IPI");
640d7e3db9cSkettenis 
641d7e3db9cSkettenis 	switch (nipi) {
642d7e3db9cSkettenis 	case 1:
643d7e3db9cSkettenis 		sc->sc_ipi_irq[0] = agintc_intr_establish(ipiirq[0],
6446214007cSpatrick 		    IST_EDGE_RISING, IPL_IPI|IPL_MPSAFE, NULL,
6456214007cSpatrick 		    agintc_ipi_combined, sc, "ipi");
646d7e3db9cSkettenis 		sc->sc_ipi_num[ARM_IPI_NOP] = ipiirq[0];
647d7e3db9cSkettenis 		sc->sc_ipi_num[ARM_IPI_DDB] = ipiirq[0];
6484002e08dSkettenis 		sc->sc_ipi_num[ARM_IPI_HALT] = ipiirq[0];
649d7e3db9cSkettenis 		break;
650d7e3db9cSkettenis 	case 2:
651d7e3db9cSkettenis 		sc->sc_ipi_irq[0] = agintc_intr_establish(ipiirq[0],
6526214007cSpatrick 		    IST_EDGE_RISING, IPL_IPI|IPL_MPSAFE, NULL,
6536214007cSpatrick 		    agintc_ipi_nop, sc, "ipinop");
654d7e3db9cSkettenis 		sc->sc_ipi_num[ARM_IPI_NOP] = ipiirq[0];
655d7e3db9cSkettenis 		sc->sc_ipi_irq[1] = agintc_intr_establish(ipiirq[1],
6566214007cSpatrick 		    IST_EDGE_RISING, IPL_IPI|IPL_MPSAFE, NULL,
6574002e08dSkettenis 		    agintc_ipi_combined, sc, "ipi");
6584002e08dSkettenis 		sc->sc_ipi_num[ARM_IPI_DDB] = ipiirq[1];
6594002e08dSkettenis 		sc->sc_ipi_num[ARM_IPI_HALT] = ipiirq[1];
6604002e08dSkettenis 		break;
6614002e08dSkettenis 	case 3:
6624002e08dSkettenis 		sc->sc_ipi_irq[0] = agintc_intr_establish(ipiirq[0],
6634002e08dSkettenis 		    IST_EDGE_RISING, IPL_IPI|IPL_MPSAFE, NULL,
6644002e08dSkettenis 		    agintc_ipi_nop, sc, "ipinop");
6654002e08dSkettenis 		sc->sc_ipi_num[ARM_IPI_NOP] = ipiirq[0];
6664002e08dSkettenis 		sc->sc_ipi_irq[1] = agintc_intr_establish(ipiirq[1],
6674002e08dSkettenis 		    IST_EDGE_RISING, IPL_IPI|IPL_MPSAFE, NULL,
6686214007cSpatrick 		    agintc_ipi_ddb, sc, "ipiddb");
669d7e3db9cSkettenis 		sc->sc_ipi_num[ARM_IPI_DDB] = ipiirq[1];
6704002e08dSkettenis 		sc->sc_ipi_irq[2] = agintc_intr_establish(ipiirq[2],
6714002e08dSkettenis 		    IST_EDGE_RISING, IPL_IPI|IPL_MPSAFE, NULL,
6724002e08dSkettenis 		    agintc_ipi_halt, sc, "ipihalt");
6734002e08dSkettenis 		sc->sc_ipi_num[ARM_IPI_HALT] = ipiirq[2];
674d7e3db9cSkettenis 		break;
675d7e3db9cSkettenis 	default:
676d7e3db9cSkettenis 		panic("nipi unexpected number %d", nipi);
677d7e3db9cSkettenis 	}
678d7e3db9cSkettenis 
679d7e3db9cSkettenis 	intr_send_ipi_func = agintc_send_ipi;
680d7e3db9cSkettenis #endif
681d7e3db9cSkettenis 
68266f727bcSkettenis 	sc->sc_ic.ic_node = faa->fa_node;
68366f727bcSkettenis 	sc->sc_ic.ic_cookie = self;
68466f727bcSkettenis 	sc->sc_ic.ic_establish = agintc_intr_establish_fdt;
68566f727bcSkettenis 	sc->sc_ic.ic_disestablish = agintc_intr_disestablish;
686d7e3db9cSkettenis 	sc->sc_ic.ic_route = agintc_route_irq;
687d7e3db9cSkettenis 	sc->sc_ic.ic_cpu_enable = agintc_cpuinit;
688452daaedSpatrick 	sc->sc_ic.ic_barrier = agintc_intr_barrier;
6898b7bc089Skettenis 	if (sc->sc_mbi_nranges > 0)
6908b7bc089Skettenis 		sc->sc_ic.ic_establish_msi = agintc_intr_establish_mbi;
69125da8179Spatrick 	sc->sc_ic.ic_set_wakeup = agintc_intr_set_wakeup;
69266f727bcSkettenis 	arm_intr_register_fdt(&sc->sc_ic);
69366f727bcSkettenis 
694daf2e6ccSkettenis 	intr_restore(psw);
695f0a2ac19Skettenis 
696f0a2ac19Skettenis 	/* Attach ITS. */
697f0a2ac19Skettenis 	simplebus_attach(parent, &sc->sc_sbus.sc_dev, faa);
698f0a2ac19Skettenis 
699f0a2ac19Skettenis 	return;
700f0a2ac19Skettenis 
701f0a2ac19Skettenis unmap:
702934f5440Skettenis 	if (sc->sc_r_ioh) {
703934f5440Skettenis 		free(sc->sc_r_ioh, M_DEVBUF,
704934f5440Skettenis 		    sc->sc_num_redist * sizeof(*sc->sc_r_ioh));
705934f5440Skettenis 	}
7067c59a772Spatrick 	if (sc->sc_processor) {
7077c59a772Spatrick 		free(sc->sc_processor, M_DEVBUF,
7087c59a772Spatrick 		     sc->sc_num_redist * sizeof(*sc->sc_processor));
709934f5440Skettenis 	}
710934f5440Skettenis 
711f0a2ac19Skettenis 	if (sc->sc_pend)
712f0a2ac19Skettenis 		agintc_dmamem_free(sc->sc_dmat, sc->sc_pend);
713f0a2ac19Skettenis 	if (sc->sc_prop)
714f0a2ac19Skettenis 		agintc_dmamem_free(sc->sc_dmat, sc->sc_prop);
715f0a2ac19Skettenis 
716*53f700b0Skettenis 	for (idx = 0; idx < sc->sc_num_redist_regions; idx++) {
717*53f700b0Skettenis 		bus_space_unmap(sc->sc_iot, sc->sc_rbase_ioh[idx],
718*53f700b0Skettenis 		     faa->fa_reg[1 + idx].size);
719*53f700b0Skettenis 	}
720*53f700b0Skettenis 	free(sc->sc_rbase_ioh, M_DEVBUF,
721*53f700b0Skettenis 	    sc->sc_num_redist_regions * sizeof(*sc->sc_rbase_ioh));
722*53f700b0Skettenis 
723f0a2ac19Skettenis 	bus_space_unmap(sc->sc_iot, sc->sc_d_ioh, faa->fa_reg[0].size);
72466f727bcSkettenis }
72566f727bcSkettenis 
7268b7bc089Skettenis void
7278b7bc089Skettenis agintc_mbiinit(struct agintc_softc *sc, int node, bus_addr_t addr)
7288b7bc089Skettenis {
7298b7bc089Skettenis 	uint32_t *ranges;
7308b7bc089Skettenis 	int i, len;
7318b7bc089Skettenis 
7328b7bc089Skettenis 	if (OF_getproplen(node, "msi-controller") != 0)
7338b7bc089Skettenis 		return;
7348b7bc089Skettenis 
7358b7bc089Skettenis 	len = OF_getproplen(node, "mbi-ranges");
7368b7bc089Skettenis 	if (len <= 0 || len % 2 * sizeof(uint32_t) != 0)
7378b7bc089Skettenis 		return;
7388b7bc089Skettenis 
7398b7bc089Skettenis 	ranges = malloc(len, M_TEMP, M_WAITOK);
7408b7bc089Skettenis 	OF_getpropintarray(node, "mbi-ranges", ranges, len);
7418b7bc089Skettenis 
7428b7bc089Skettenis 	sc->sc_mbi_nranges = len / (2 * sizeof(uint32_t));
7438b7bc089Skettenis 	sc->sc_mbi_ranges = mallocarray(sc->sc_mbi_nranges,
7448b7bc089Skettenis 	    sizeof(struct agintc_mbi_range), M_DEVBUF, M_WAITOK);
7458b7bc089Skettenis 
7468b7bc089Skettenis 	for (i = 0; i < sc->sc_mbi_nranges; i++) {
7478b7bc089Skettenis 		sc->sc_mbi_ranges[i].mr_base = ranges[2 * i + 0];
7488b7bc089Skettenis 		sc->sc_mbi_ranges[i].mr_span = ranges[2 * i + 1];
7498b7bc089Skettenis 		sc->sc_mbi_ranges[i].mr_mbi =
7508b7bc089Skettenis 		    mallocarray(sc->sc_mbi_ranges[i].mr_span,
7518b7bc089Skettenis 			sizeof(void *), M_DEVBUF, M_WAITOK | M_ZERO);
7528b7bc089Skettenis 	}
7538b7bc089Skettenis 
7548b7bc089Skettenis 	free(ranges, M_TEMP, len);
7558b7bc089Skettenis 
7568b7bc089Skettenis 	addr = OF_getpropint64(node, "mbi-alias", addr);
7578b7bc089Skettenis 	sc->sc_mbi_addr = addr + GICD_SETSPI_NSR;
7588b7bc089Skettenis 
7598b7bc089Skettenis 	printf(" mbi");
7608b7bc089Skettenis }
7618b7bc089Skettenis 
76266f727bcSkettenis /* Initialize redistributors on each core. */
76366f727bcSkettenis void
76466f727bcSkettenis agintc_cpuinit(void)
76566f727bcSkettenis {
76666f727bcSkettenis 	struct agintc_softc *sc = agintc_sc;
7677c59a772Spatrick 	uint32_t waker;
76866f727bcSkettenis 	int timeout = 100000;
7697c59a772Spatrick 	int hwcpu;
77066f727bcSkettenis 	int i;
77166f727bcSkettenis 
7727c59a772Spatrick 	hwcpu = sc->sc_cpuremap[cpu_number()];
77366f727bcSkettenis 	waker = bus_space_read_4(sc->sc_iot, sc->sc_r_ioh[hwcpu],
77466f727bcSkettenis 	    GICR_WAKER);
77566f727bcSkettenis 	waker &= ~(GICR_WAKER_PROCESSORSLEEP);
77666f727bcSkettenis 	bus_space_write_4(sc->sc_iot, sc->sc_r_ioh[hwcpu], GICR_WAKER,
77766f727bcSkettenis 	    waker);
77866f727bcSkettenis 
77966f727bcSkettenis 	do {
78066f727bcSkettenis 		waker = bus_space_read_4(sc->sc_iot, sc->sc_r_ioh[hwcpu],
78166f727bcSkettenis 		    GICR_WAKER);
78266f727bcSkettenis 	} while (--timeout && (waker & GICR_WAKER_CHILDRENASLEEP));
78366f727bcSkettenis 	if (timeout == 0)
78466f727bcSkettenis 		printf("%s: waker timed out\n", __func__);
78566f727bcSkettenis 
78666f727bcSkettenis 	bus_space_write_4(sc->sc_iot, sc->sc_r_ioh[hwcpu],
78766f727bcSkettenis 	    GICR_ICENABLE0, ~0);
78866f727bcSkettenis 	bus_space_write_4(sc->sc_iot, sc->sc_r_ioh[hwcpu],
78966f727bcSkettenis 	    GICR_ICPENDR0, ~0);
79066f727bcSkettenis 	bus_space_write_4(sc->sc_iot, sc->sc_r_ioh[hwcpu],
79166f727bcSkettenis 	    GICR_ICACTIVE0, ~0);
79266f727bcSkettenis 	for (i = 0; i < 32; i += 4) {
79366f727bcSkettenis 		bus_space_write_4(sc->sc_iot, sc->sc_r_ioh[hwcpu],
79466f727bcSkettenis 		    GICR_IPRIORITYR(i), ~0);
79566f727bcSkettenis 	}
796d532cfaeSpatrick 	bus_space_write_4(sc->sc_iot, sc->sc_r_ioh[hwcpu],
797d532cfaeSpatrick 	    GICR_IGROUPR0, ~0);
798d532cfaeSpatrick 	bus_space_write_4(sc->sc_iot, sc->sc_r_ioh[hwcpu],
799d532cfaeSpatrick 	    GICR_IGRPMODR0, 0);
800d7e3db9cSkettenis 
801d7e3db9cSkettenis 	if (sc->sc_ipi_irq[0] != NULL)
802d7e3db9cSkettenis 		agintc_route_irq(sc->sc_ipi_irq[0], IRQ_ENABLE, curcpu());
803d7e3db9cSkettenis 	if (sc->sc_ipi_irq[1] != NULL)
804d7e3db9cSkettenis 		agintc_route_irq(sc->sc_ipi_irq[1], IRQ_ENABLE, curcpu());
805a2ef92f0Skettenis 	if (sc->sc_ipi_irq[2] != NULL)
806a2ef92f0Skettenis 		agintc_route_irq(sc->sc_ipi_irq[2], IRQ_ENABLE, curcpu());
807d7e3db9cSkettenis 
808d7e3db9cSkettenis 	__asm volatile("msr "STR(ICC_PMR)", %x0" :: "r"(0xff));
809d7e3db9cSkettenis 	__asm volatile("msr "STR(ICC_BPR1)", %x0" :: "r"(0));
810d7e3db9cSkettenis 	__asm volatile("msr "STR(ICC_IGRPEN1)", %x0" :: "r"(1));
811daf2e6ccSkettenis 	intr_enable();
81266f727bcSkettenis }
81366f727bcSkettenis 
81466f727bcSkettenis void
815163120c5Skettenis agintc_set_priority(struct agintc_softc *sc, int irq, int ipl)
81666f727bcSkettenis {
81766f727bcSkettenis 	struct cpu_info	*ci = curcpu();
81866f727bcSkettenis 	int		 hwcpu = sc->sc_cpuremap[ci->ci_cpuid];
81966f727bcSkettenis 	uint32_t	 prival;
82066f727bcSkettenis 
821163120c5Skettenis 	prival = ((0xff - ipl) << sc->sc_prio_shift) & 0xff;
82216b913cdSkettenis 
823f0a2ac19Skettenis 	if (irq >= SPI_BASE) {
82466f727bcSkettenis 		bus_space_write_1(sc->sc_iot, sc->sc_d_ioh,
82566f727bcSkettenis 		    GICD_IPRIORITYR(irq), prival);
82666f727bcSkettenis 	} else  {
82766f727bcSkettenis 		/* only sets local redistributor */
82866f727bcSkettenis 		bus_space_write_1(sc->sc_iot, sc->sc_r_ioh[hwcpu],
82966f727bcSkettenis 		    GICR_IPRIORITYR(irq), prival);
83066f727bcSkettenis 	}
83166f727bcSkettenis }
83266f727bcSkettenis 
83366f727bcSkettenis void
834163120c5Skettenis agintc_setipl(int ipl)
83566f727bcSkettenis {
836163120c5Skettenis 	struct agintc_softc	*sc = agintc_sc;
83766f727bcSkettenis 	struct cpu_info		*ci = curcpu();
838daf2e6ccSkettenis 	u_long			 psw;
83966f727bcSkettenis 	uint32_t		 prival;
84066f727bcSkettenis 
84166f727bcSkettenis 	/* disable here is only to keep hardware in sync with ci->ci_cpl */
842daf2e6ccSkettenis 	psw = intr_disable();
843163120c5Skettenis 	ci->ci_cpl = ipl;
84466f727bcSkettenis 
845163120c5Skettenis 	prival = ((0xff - ipl) << sc->sc_pmr_shift) & 0xff;
84666f727bcSkettenis 	__asm volatile("msr "STR(ICC_PMR)", %x0" : : "r" (prival));
847d7e3db9cSkettenis 	__isb();
848d7e3db9cSkettenis 
849daf2e6ccSkettenis 	intr_restore(psw);
85066f727bcSkettenis }
85166f727bcSkettenis 
85266f727bcSkettenis void
85325da8179Spatrick agintc_enable_wakeup(void)
85425da8179Spatrick {
85525da8179Spatrick 	struct agintc_softc *sc = agintc_sc;
85625da8179Spatrick 	struct intrhand *ih;
85725da8179Spatrick 	uint8_t *prop;
85825da8179Spatrick 	int irq, wakeup;
85925da8179Spatrick 
86025da8179Spatrick 	for (irq = 0; irq < sc->sc_nintr; irq++) {
86125da8179Spatrick 		/* No handler? Disabled already. */
86225da8179Spatrick 		if (TAILQ_EMPTY(&sc->sc_handler[irq].iq_list))
86325da8179Spatrick 			continue;
86425da8179Spatrick 		/* Unless we're WAKEUP, disable. */
86525da8179Spatrick 		wakeup = 0;
86625da8179Spatrick 		TAILQ_FOREACH(ih, &sc->sc_handler[irq].iq_list, ih_list) {
86725da8179Spatrick 			if (ih->ih_flags & IPL_WAKEUP) {
86825da8179Spatrick 				wakeup = 1;
86925da8179Spatrick 				break;
87025da8179Spatrick 			}
87125da8179Spatrick 		}
87225da8179Spatrick 		if (!wakeup)
87325da8179Spatrick 			agintc_intr_disable(sc, irq);
87425da8179Spatrick 	}
87525da8179Spatrick 
87625da8179Spatrick 	for (irq = 0; irq < sc->sc_nlpi; irq++) {
877f83e6fb8Spatrick 		if (sc->sc_lpi[irq] == NULL)
878f83e6fb8Spatrick 			continue;
879f83e6fb8Spatrick 		ih = sc->sc_lpi[irq]->li_ih;
880f83e6fb8Spatrick 		KASSERT(ih != NULL);
881f83e6fb8Spatrick 		if (ih->ih_flags & IPL_WAKEUP)
88225da8179Spatrick 			continue;
88325da8179Spatrick 		prop = AGINTC_DMA_KVA(sc->sc_prop);
88425da8179Spatrick 		prop[irq] &= ~GICR_PROP_ENABLE;
88525da8179Spatrick 		/* Make globally visible. */
88625da8179Spatrick 		cpu_dcache_wb_range((vaddr_t)&prop[irq],
88725da8179Spatrick 		    sizeof(*prop));
88825da8179Spatrick 		__asm volatile("dsb sy");
889f83e6fb8Spatrick 		/* Invalidate cache */
890f83e6fb8Spatrick 		agintc_msi_inv(sc->sc_lpi[irq]);
89125da8179Spatrick 	}
89225da8179Spatrick }
89325da8179Spatrick 
89425da8179Spatrick void
89525da8179Spatrick agintc_disable_wakeup(void)
89625da8179Spatrick {
89725da8179Spatrick 	struct agintc_softc *sc = agintc_sc;
89825da8179Spatrick 	struct intrhand *ih;
89925da8179Spatrick 	uint8_t *prop;
90025da8179Spatrick 	int irq, wakeup;
90125da8179Spatrick 
90225da8179Spatrick 	for (irq = 0; irq < sc->sc_nintr; irq++) {
90325da8179Spatrick 		/* No handler? Keep disabled. */
90425da8179Spatrick 		if (TAILQ_EMPTY(&sc->sc_handler[irq].iq_list))
90525da8179Spatrick 			continue;
90625da8179Spatrick 		/* WAKEUPs are already enabled. */
90725da8179Spatrick 		wakeup = 0;
90825da8179Spatrick 		TAILQ_FOREACH(ih, &sc->sc_handler[irq].iq_list, ih_list) {
90925da8179Spatrick 			if (ih->ih_flags & IPL_WAKEUP) {
91025da8179Spatrick 				wakeup = 1;
91125da8179Spatrick 				break;
91225da8179Spatrick 			}
91325da8179Spatrick 		}
91425da8179Spatrick 		if (!wakeup)
91525da8179Spatrick 			agintc_intr_enable(sc, irq);
91625da8179Spatrick 	}
91725da8179Spatrick 
91825da8179Spatrick 	for (irq = 0; irq < sc->sc_nlpi; irq++) {
919f83e6fb8Spatrick 		if (sc->sc_lpi[irq] == NULL)
920f83e6fb8Spatrick 			continue;
921f83e6fb8Spatrick 		ih = sc->sc_lpi[irq]->li_ih;
922f83e6fb8Spatrick 		KASSERT(ih != NULL);
923f83e6fb8Spatrick 		if (ih->ih_flags & IPL_WAKEUP)
92425da8179Spatrick 			continue;
92525da8179Spatrick 		prop = AGINTC_DMA_KVA(sc->sc_prop);
92625da8179Spatrick 		prop[irq] |= GICR_PROP_ENABLE;
92725da8179Spatrick 		/* Make globally visible. */
92825da8179Spatrick 		cpu_dcache_wb_range((vaddr_t)&prop[irq],
92925da8179Spatrick 		    sizeof(*prop));
93025da8179Spatrick 		__asm volatile("dsb sy");
931f83e6fb8Spatrick 		/* Invalidate cache */
932f83e6fb8Spatrick 		agintc_msi_inv(sc->sc_lpi[irq]);
93325da8179Spatrick 	}
93425da8179Spatrick }
93525da8179Spatrick 
93625da8179Spatrick void
93766f727bcSkettenis agintc_intr_enable(struct agintc_softc *sc, int irq)
93866f727bcSkettenis {
93966f727bcSkettenis 	struct cpu_info	*ci = curcpu();
94066f727bcSkettenis 	int hwcpu = sc->sc_cpuremap[ci->ci_cpuid];
94166f727bcSkettenis 	int bit = 1 << IRQ_TO_REG32BIT(irq);
94266f727bcSkettenis 
94366f727bcSkettenis 	if (irq >= 32) {
94466f727bcSkettenis 		bus_space_write_4(sc->sc_iot, sc->sc_d_ioh,
94566f727bcSkettenis 		    GICD_ISENABLER(irq), bit);
94666f727bcSkettenis 	} else {
94766f727bcSkettenis 		bus_space_write_4(sc->sc_iot, sc->sc_r_ioh[hwcpu],
94866f727bcSkettenis 		    GICR_ISENABLE0, bit);
94966f727bcSkettenis 	}
95066f727bcSkettenis }
95166f727bcSkettenis 
95266f727bcSkettenis void
95366f727bcSkettenis agintc_intr_disable(struct agintc_softc *sc, int irq)
95466f727bcSkettenis {
95566f727bcSkettenis 	struct cpu_info	*ci = curcpu();
95666f727bcSkettenis 	int hwcpu = sc->sc_cpuremap[ci->ci_cpuid];
95766f727bcSkettenis 
95866f727bcSkettenis 	if (irq >= 32) {
95966f727bcSkettenis 		bus_space_write_4(sc->sc_iot, sc->sc_d_ioh,
96066f727bcSkettenis 		    GICD_ICENABLER(irq), 1 << IRQ_TO_REG32BIT(irq));
96166f727bcSkettenis 	} else {
96266f727bcSkettenis 		bus_space_write_4(sc->sc_iot, sc->sc_r_ioh[hwcpu],
96366f727bcSkettenis 		    GICR_ICENABLE0, 1 << IRQ_TO_REG32BIT(irq));
96466f727bcSkettenis 	}
96566f727bcSkettenis }
96666f727bcSkettenis 
96766f727bcSkettenis void
9686214007cSpatrick agintc_intr_config(struct agintc_softc *sc, int irq, int type)
9696214007cSpatrick {
9706214007cSpatrick 	uint32_t reg;
9716214007cSpatrick 
9726214007cSpatrick 	/* Don't dare to change SGIs or PPIs (yet) */
9736214007cSpatrick 	if (irq < 32)
9746214007cSpatrick 		return;
9756214007cSpatrick 
9766214007cSpatrick 	reg = bus_space_read_4(sc->sc_iot, sc->sc_d_ioh, GICD_ICFGR(irq));
9776214007cSpatrick 	reg &= ~GICD_ICFGR_TRIG_MASK(irq);
9786214007cSpatrick 	if (type == IST_EDGE_RISING)
9796214007cSpatrick 		reg |= GICD_ICFGR_TRIG_EDGE(irq);
9806214007cSpatrick 	else
9816214007cSpatrick 		reg |= GICD_ICFGR_TRIG_LEVEL(irq);
9826214007cSpatrick 	bus_space_write_4(sc->sc_iot, sc->sc_d_ioh, GICD_ICFGR(irq), reg);
9836214007cSpatrick }
9846214007cSpatrick 
9856214007cSpatrick void
98666f727bcSkettenis agintc_calc_mask(void)
98766f727bcSkettenis {
98866f727bcSkettenis 	struct agintc_softc	*sc = agintc_sc;
98966f727bcSkettenis 	int			 irq;
99066f727bcSkettenis 
99166f727bcSkettenis 	for (irq = 0; irq < sc->sc_nintr; irq++)
99266f727bcSkettenis 		agintc_calc_irq(sc, irq);
99366f727bcSkettenis }
99466f727bcSkettenis 
99566f727bcSkettenis void
99666f727bcSkettenis agintc_calc_irq(struct agintc_softc *sc, int irq)
99766f727bcSkettenis {
9987c59a772Spatrick 	struct cpu_info	*ci = sc->sc_handler[irq].iq_ci;
99966f727bcSkettenis 	struct intrhand	*ih;
100066f727bcSkettenis 	int max = IPL_NONE;
100166f727bcSkettenis 	int min = IPL_HIGH;
100266f727bcSkettenis 
1003f0a2ac19Skettenis 	TAILQ_FOREACH(ih, &sc->sc_handler[irq].iq_list, ih_list) {
100466f727bcSkettenis 		if (ih->ih_ipl > max)
100566f727bcSkettenis 			max = ih->ih_ipl;
100666f727bcSkettenis 
100766f727bcSkettenis 		if (ih->ih_ipl < min)
100866f727bcSkettenis 			min = ih->ih_ipl;
100966f727bcSkettenis 	}
101066f727bcSkettenis 
101166f727bcSkettenis 	if (max == IPL_NONE)
101266f727bcSkettenis 		min = IPL_NONE;
101366f727bcSkettenis 
1014eb0c9b3cSpatrick 	if (sc->sc_handler[irq].iq_irq_max == max &&
1015eb0c9b3cSpatrick 	    sc->sc_handler[irq].iq_irq_min == min)
1016eb0c9b3cSpatrick 		return;
1017eb0c9b3cSpatrick 
1018eb0c9b3cSpatrick 	sc->sc_handler[irq].iq_irq_max = max;
1019eb0c9b3cSpatrick 	sc->sc_handler[irq].iq_irq_min = min;
1020eb0c9b3cSpatrick 
102166f727bcSkettenis #ifdef DEBUG_AGINTC
102266f727bcSkettenis 	if (min != IPL_NONE)
102366f727bcSkettenis 		printf("irq %d to block at %d %d \n", irq, max, min );
102466f727bcSkettenis #endif
102566f727bcSkettenis 	/* Enable interrupts at lower levels, clear -> enable */
102666f727bcSkettenis 	/* Set interrupt priority/enable */
102766f727bcSkettenis 	if (min != IPL_NONE) {
102866f727bcSkettenis 		agintc_set_priority(sc, irq, min);
102966f727bcSkettenis 		agintc_route(sc, irq, IRQ_ENABLE, ci);
103066f727bcSkettenis 		agintc_intr_enable(sc, irq);
103166f727bcSkettenis 	} else {
103266f727bcSkettenis 		agintc_intr_disable(sc, irq);
103366f727bcSkettenis 		agintc_route(sc, irq, IRQ_DISABLE, ci);
103466f727bcSkettenis 	}
103566f727bcSkettenis }
103666f727bcSkettenis 
103766f727bcSkettenis void
103866f727bcSkettenis agintc_splx(int new)
103966f727bcSkettenis {
104066f727bcSkettenis 	struct cpu_info *ci = curcpu();
104166f727bcSkettenis 
104266f727bcSkettenis 	if (ci->ci_ipending & arm_smask[new])
104366f727bcSkettenis 		arm_do_pending_intr(new);
104466f727bcSkettenis 
104566f727bcSkettenis 	agintc_setipl(new);
104666f727bcSkettenis }
104766f727bcSkettenis 
104866f727bcSkettenis int
104966f727bcSkettenis agintc_spllower(int new)
105066f727bcSkettenis {
105166f727bcSkettenis 	struct cpu_info *ci = curcpu();
105266f727bcSkettenis 	int old = ci->ci_cpl;
105366f727bcSkettenis 
105466f727bcSkettenis 	agintc_splx(new);
105566f727bcSkettenis 	return (old);
105666f727bcSkettenis }
105766f727bcSkettenis 
105866f727bcSkettenis int
105966f727bcSkettenis agintc_splraise(int new)
106066f727bcSkettenis {
106166f727bcSkettenis 	struct cpu_info	*ci = curcpu();
106266f727bcSkettenis 	int old = ci->ci_cpl;
106366f727bcSkettenis 
106466f727bcSkettenis 	/*
106566f727bcSkettenis 	 * setipl must always be called because there is a race window
106666f727bcSkettenis 	 * where the variable is updated before the mask is set
106766f727bcSkettenis 	 * an interrupt occurs in that window without the mask always
106866f727bcSkettenis 	 * being set, the hardware might not get updated on the next
106966f727bcSkettenis 	 * splraise completely messing up spl protection.
107066f727bcSkettenis 	 */
107166f727bcSkettenis 	if (old > new)
107266f727bcSkettenis 		new = old;
107366f727bcSkettenis 
107466f727bcSkettenis 	agintc_setipl(new);
107566f727bcSkettenis 	return (old);
107666f727bcSkettenis }
107766f727bcSkettenis 
107866f727bcSkettenis uint32_t
107966f727bcSkettenis agintc_iack(void)
108066f727bcSkettenis {
108166f727bcSkettenis 	int irq;
108266f727bcSkettenis 
108366f727bcSkettenis 	__asm volatile("mrs %x0, "STR(ICC_IAR1) : "=r" (irq));
108466f727bcSkettenis 	__asm volatile("dsb sy");
108566f727bcSkettenis 	return irq;
108666f727bcSkettenis }
108766f727bcSkettenis 
108866f727bcSkettenis void
1089d7e3db9cSkettenis agintc_route_irq(void *v, int enable, struct cpu_info *ci)
1090d7e3db9cSkettenis {
1091d7e3db9cSkettenis 	struct agintc_softc	*sc = agintc_sc;
1092d7e3db9cSkettenis 	struct intrhand		*ih = v;
1093d7e3db9cSkettenis 
1094d7e3db9cSkettenis 	if (enable) {
1095d7e3db9cSkettenis 		agintc_set_priority(sc, ih->ih_irq,
1096eb0c9b3cSpatrick 		    sc->sc_handler[ih->ih_irq].iq_irq_min);
1097d7e3db9cSkettenis 		agintc_route(sc, ih->ih_irq, IRQ_ENABLE, ci);
1098d7e3db9cSkettenis 		agintc_intr_enable(sc, ih->ih_irq);
1099d7e3db9cSkettenis 	}
1100d7e3db9cSkettenis }
1101d7e3db9cSkettenis 
1102d7e3db9cSkettenis void
110366f727bcSkettenis agintc_route(struct agintc_softc *sc, int irq, int enable, struct cpu_info *ci)
110466f727bcSkettenis {
110566f727bcSkettenis 	/* XXX does not yet support 'participating node' */
110666f727bcSkettenis 	if (irq >= 32) {
110766f727bcSkettenis #ifdef DEBUG_AGINTC
11085ce1efbdSkettenis 		printf("router %x irq %d val %016llx\n", GICD_IROUTER(irq),
11095ce1efbdSkettenis 		    irq, ci->ci_mpidr & MPIDR_AFF);
111066f727bcSkettenis #endif
111166f727bcSkettenis 		bus_space_write_8(sc->sc_iot, sc->sc_d_ioh,
11125ce1efbdSkettenis 		    GICD_IROUTER(irq), ci->ci_mpidr & MPIDR_AFF);
111366f727bcSkettenis 	}
111466f727bcSkettenis }
111566f727bcSkettenis 
111666f727bcSkettenis void
1117452daaedSpatrick agintc_intr_barrier(void *cookie)
1118452daaedSpatrick {
1119452daaedSpatrick 	struct intrhand		*ih = cookie;
1120452daaedSpatrick 
1121452daaedSpatrick 	sched_barrier(ih->ih_ci);
1122452daaedSpatrick }
1123452daaedSpatrick 
1124452daaedSpatrick void
1125f0a2ac19Skettenis agintc_run_handler(struct intrhand *ih, void *frame, int s)
112666f727bcSkettenis {
112766f727bcSkettenis 	void *arg;
1128f0a2ac19Skettenis 	int handled;
112966f727bcSkettenis 
11307a7b3facSkettenis #ifdef MULTIPROCESSOR
11317a7b3facSkettenis 	int need_lock;
11327a7b3facSkettenis 
11337a7b3facSkettenis 	if (ih->ih_flags & IPL_MPSAFE)
11347a7b3facSkettenis 		need_lock = 0;
11357a7b3facSkettenis 	else
11367a7b3facSkettenis 		need_lock = s < IPL_SCHED;
11377a7b3facSkettenis 
11387a7b3facSkettenis 	if (need_lock)
11397a7b3facSkettenis 		KERNEL_LOCK();
11407a7b3facSkettenis #endif
11417a7b3facSkettenis 
1142f9b35d7eSkettenis 	if (ih->ih_arg)
114366f727bcSkettenis 		arg = ih->ih_arg;
114466f727bcSkettenis 	else
114566f727bcSkettenis 		arg = frame;
114666f727bcSkettenis 
114766f727bcSkettenis 	handled = ih->ih_func(arg);
114866f727bcSkettenis 	if (handled)
114966f727bcSkettenis 		ih->ih_count.ec_count++;
115066f727bcSkettenis 
11517a7b3facSkettenis #ifdef MULTIPROCESSOR
11527a7b3facSkettenis 	if (need_lock)
11537a7b3facSkettenis 		KERNEL_UNLOCK();
11547a7b3facSkettenis #endif
115566f727bcSkettenis }
1156f0a2ac19Skettenis 
1157f0a2ac19Skettenis void
1158f0a2ac19Skettenis agintc_irq_handler(void *frame)
1159f0a2ac19Skettenis {
1160f0a2ac19Skettenis 	struct agintc_softc	*sc = agintc_sc;
1161f0a2ac19Skettenis 	struct intrhand		*ih;
1162f0a2ac19Skettenis 	int			 irq, pri, s;
1163f0a2ac19Skettenis 
1164f0a2ac19Skettenis 	irq = agintc_iack();
1165f0a2ac19Skettenis 
1166f0a2ac19Skettenis #ifdef DEBUG_AGINTC
1167f0a2ac19Skettenis 	if (irq != 30)
1168f0a2ac19Skettenis 		printf("irq  %d fired\n", irq);
1169f0a2ac19Skettenis 	else {
1170f0a2ac19Skettenis 		static int cnt = 0;
1171f0a2ac19Skettenis 		if ((cnt++ % 100) == 0) {
1172f0a2ac19Skettenis 			printf("irq  %d fired * _100\n", irq);
1173f0a2ac19Skettenis #ifdef DDB
1174f0a2ac19Skettenis 			db_enter();
1175f0a2ac19Skettenis #endif
1176f0a2ac19Skettenis 		}
1177f0a2ac19Skettenis 	}
1178f0a2ac19Skettenis #endif
1179f0a2ac19Skettenis 
1180f0a2ac19Skettenis 	if (irq == 1023) {
1181f0a2ac19Skettenis 		sc->sc_spur.ec_count++;
1182f0a2ac19Skettenis 		return;
1183f0a2ac19Skettenis 	}
1184f0a2ac19Skettenis 
1185f0a2ac19Skettenis 	if ((irq >= sc->sc_nintr && irq < LPI_BASE) ||
1186f0a2ac19Skettenis 	    irq >= LPI_BASE + sc->sc_nlpi) {
1187f0a2ac19Skettenis 		return;
1188f0a2ac19Skettenis 	}
1189f0a2ac19Skettenis 
1190f0a2ac19Skettenis 	if (irq >= LPI_BASE) {
1191f83e6fb8Spatrick 		if (sc->sc_lpi[irq - LPI_BASE] == NULL)
1192f0a2ac19Skettenis 			return;
1193f83e6fb8Spatrick 		ih = sc->sc_lpi[irq - LPI_BASE]->li_ih;
1194f83e6fb8Spatrick 		KASSERT(ih != NULL);
1195f0a2ac19Skettenis 
1196f0a2ac19Skettenis 		s = agintc_splraise(ih->ih_ipl);
1197666951e3Skettenis 		intr_enable();
1198f0a2ac19Skettenis 		agintc_run_handler(ih, frame, s);
1199666951e3Skettenis 		intr_disable();
1200f0a2ac19Skettenis 		agintc_eoi(irq);
1201f0a2ac19Skettenis 
1202f0a2ac19Skettenis 		agintc_splx(s);
1203f0a2ac19Skettenis 		return;
1204f0a2ac19Skettenis 	}
1205f0a2ac19Skettenis 
1206eb0c9b3cSpatrick 	pri = sc->sc_handler[irq].iq_irq_max;
1207f0a2ac19Skettenis 	s = agintc_splraise(pri);
1208666951e3Skettenis 	intr_enable();
1209f0a2ac19Skettenis 	TAILQ_FOREACH(ih, &sc->sc_handler[irq].iq_list, ih_list) {
1210f0a2ac19Skettenis 		agintc_run_handler(ih, frame, s);
1211f0a2ac19Skettenis 	}
1212666951e3Skettenis 	intr_disable();
121366f727bcSkettenis 	agintc_eoi(irq);
121466f727bcSkettenis 
121566f727bcSkettenis 	agintc_splx(s);
121666f727bcSkettenis }
121766f727bcSkettenis 
121866f727bcSkettenis void *
121966f727bcSkettenis agintc_intr_establish_fdt(void *cookie, int *cell, int level,
1220789e88a4Spatrick     struct cpu_info *ci, int (*func)(void *), void *arg, char *name)
122166f727bcSkettenis {
122266f727bcSkettenis 	struct agintc_softc	*sc = agintc_sc;
122366f727bcSkettenis 	int			 irq;
12246214007cSpatrick 	int			 type;
122566f727bcSkettenis 
122666f727bcSkettenis 	/* 2nd cell contains the interrupt number */
122766f727bcSkettenis 	irq = cell[1];
122866f727bcSkettenis 
122966f727bcSkettenis 	/* 1st cell contains type: 0 SPI (32-X), 1 PPI (16-31) */
123066f727bcSkettenis 	if (cell[0] == 0)
1231f0a2ac19Skettenis 		irq += SPI_BASE;
123266f727bcSkettenis 	else if (cell[0] == 1)
1233f0a2ac19Skettenis 		irq += PPI_BASE;
123466f727bcSkettenis 	else
1235f0a2ac19Skettenis 		panic("%s: bogus interrupt type", sc->sc_sbus.sc_dev.dv_xname);
123666f727bcSkettenis 
12376214007cSpatrick 	/* SPIs are only active-high level or low-to-high edge */
12386214007cSpatrick 	if (cell[2] & 0x3)
12396214007cSpatrick 		type = IST_EDGE_RISING;
12406214007cSpatrick 	else
12416214007cSpatrick 		type = IST_LEVEL_HIGH;
12426214007cSpatrick 
12436214007cSpatrick 	return agintc_intr_establish(irq, type, level, ci, func, arg, name);
124466f727bcSkettenis }
124566f727bcSkettenis 
124666f727bcSkettenis void *
12476214007cSpatrick agintc_intr_establish(int irqno, int type, int level, struct cpu_info *ci,
1248789e88a4Spatrick     int (*func)(void *), void *arg, char *name)
124966f727bcSkettenis {
125066f727bcSkettenis 	struct agintc_softc	*sc = agintc_sc;
125166f727bcSkettenis 	struct intrhand		*ih;
1252daf2e6ccSkettenis 	u_long			 psw;
125366f727bcSkettenis 
1254f0a2ac19Skettenis 	if (irqno < 0 || (irqno >= sc->sc_nintr && irqno < LPI_BASE) ||
1255f0a2ac19Skettenis 	    irqno >= LPI_BASE + sc->sc_nlpi)
125666f727bcSkettenis 		panic("agintc_intr_establish: bogus irqnumber %d: %s",
125766f727bcSkettenis 		    irqno, name);
125866f727bcSkettenis 
1259789e88a4Spatrick 	if (ci == NULL)
1260789e88a4Spatrick 		ci = &cpu_info_primary;
1261789e88a4Spatrick 
126266f727bcSkettenis 	ih = malloc(sizeof *ih, M_DEVBUF, M_WAITOK);
126366f727bcSkettenis 	ih->ih_func = func;
126466f727bcSkettenis 	ih->ih_arg = arg;
1265d7e3db9cSkettenis 	ih->ih_ipl = level & IPL_IRQMASK;
1266d7e3db9cSkettenis 	ih->ih_flags = level & IPL_FLAGMASK;
126766f727bcSkettenis 	ih->ih_irq = irqno;
126866f727bcSkettenis 	ih->ih_name = name;
1269452daaedSpatrick 	ih->ih_ci = ci;
127066f727bcSkettenis 
1271daf2e6ccSkettenis 	psw = intr_disable();
127266f727bcSkettenis 
12737c59a772Spatrick 	if (irqno < LPI_BASE) {
12747c59a772Spatrick 		if (!TAILQ_EMPTY(&sc->sc_handler[irqno].iq_list) &&
12757c59a772Spatrick 		    sc->sc_handler[irqno].iq_ci != ci) {
1276daf2e6ccSkettenis 			intr_restore(psw);
12777c59a772Spatrick 			free(ih, M_DEVBUF, sizeof *ih);
12787c59a772Spatrick 			return NULL;
12797c59a772Spatrick 		}
1280f0a2ac19Skettenis 		TAILQ_INSERT_TAIL(&sc->sc_handler[irqno].iq_list, ih, ih_list);
12817c59a772Spatrick 		sc->sc_handler[irqno].iq_ci = ci;
1282f83e6fb8Spatrick 	}
128366f727bcSkettenis 
128466f727bcSkettenis 	if (name != NULL)
128566f727bcSkettenis 		evcount_attach(&ih->ih_count, name, &ih->ih_irq);
128666f727bcSkettenis 
128766f727bcSkettenis #ifdef DEBUG_AGINTC
128866f727bcSkettenis 	printf("%s: irq %d level %d [%s]\n", __func__, irqno, level, name);
128966f727bcSkettenis #endif
129066f727bcSkettenis 
1291f0a2ac19Skettenis 	if (irqno < LPI_BASE) {
12926214007cSpatrick 		agintc_intr_config(sc, irqno, type);
129366f727bcSkettenis 		agintc_calc_irq(sc, irqno);
1294f0a2ac19Skettenis 	} else {
12952896618dSkettenis 		uint8_t *prop = AGINTC_DMA_KVA(sc->sc_prop);
12962896618dSkettenis 
12973346ccb8Skettenis 		prop[irqno - LPI_BASE] = (((0xff - ih->ih_ipl) << 4) & 0xff) |
129816b913cdSkettenis 		    GICR_PROP_GROUP1 | GICR_PROP_ENABLE;
12992896618dSkettenis 
13002896618dSkettenis 		/* Make globally visible. */
130135175b85Skettenis 		cpu_dcache_wb_range((vaddr_t)&prop[irqno - LPI_BASE],
130235175b85Skettenis 		    sizeof(*prop));
13032896618dSkettenis 		__asm volatile("dsb sy");
1304f0a2ac19Skettenis 	}
130566f727bcSkettenis 
1306daf2e6ccSkettenis 	intr_restore(psw);
130766f727bcSkettenis 	return (ih);
130866f727bcSkettenis }
130966f727bcSkettenis 
131066f727bcSkettenis void
131166f727bcSkettenis agintc_intr_disestablish(void *cookie)
131266f727bcSkettenis {
131366f727bcSkettenis 	struct agintc_softc	*sc = agintc_sc;
131466f727bcSkettenis 	struct intrhand		*ih = cookie;
131566f727bcSkettenis 	int			 irqno = ih->ih_irq;
1316daf2e6ccSkettenis 	u_long			 psw;
13178b7bc089Skettenis 	struct agintc_mbi_range	*mr;
13188b7bc089Skettenis 	int			 i;
131966f727bcSkettenis 
1320daf2e6ccSkettenis 	psw = intr_disable();
132166f727bcSkettenis 
132235175b85Skettenis 	if (irqno < LPI_BASE) {
1323f0a2ac19Skettenis 		TAILQ_REMOVE(&sc->sc_handler[irqno].iq_list, ih, ih_list);
132466f727bcSkettenis 		agintc_calc_irq(sc, irqno);
132566f727bcSkettenis 
13268b7bc089Skettenis 		/* In case this is an MBI, free it */
13278b7bc089Skettenis 		for (i = 0; i < sc->sc_mbi_nranges; i++) {
13288b7bc089Skettenis 			mr = &sc->sc_mbi_ranges[i];
13298b7bc089Skettenis 			if (irqno < mr->mr_base)
13308b7bc089Skettenis 				continue;
13318b7bc089Skettenis 			if (irqno >= mr->mr_base + mr->mr_span)
13328b7bc089Skettenis 				break;
13338b7bc089Skettenis 			if (mr->mr_mbi[irqno - mr->mr_base] != NULL)
13348b7bc089Skettenis 				mr->mr_mbi[irqno - mr->mr_base] = NULL;
13358b7bc089Skettenis 		}
133635175b85Skettenis 	} else {
133735175b85Skettenis 		uint8_t *prop = AGINTC_DMA_KVA(sc->sc_prop);
133835175b85Skettenis 
133935175b85Skettenis 		prop[irqno - LPI_BASE] = 0;
134035175b85Skettenis 
134135175b85Skettenis 		/* Make globally visible. */
134235175b85Skettenis 		cpu_dcache_wb_range((vaddr_t)&prop[irqno - LPI_BASE],
134335175b85Skettenis 		    sizeof(*prop));
134435175b85Skettenis 		__asm volatile("dsb sy");
134535175b85Skettenis 	}
134635175b85Skettenis 
134735175b85Skettenis 	if (ih->ih_name != NULL)
134835175b85Skettenis 		evcount_detach(&ih->ih_count);
13498b7bc089Skettenis 
1350daf2e6ccSkettenis 	intr_restore(psw);
135166f727bcSkettenis 
135266f727bcSkettenis 	free(ih, M_DEVBUF, 0);
135366f727bcSkettenis }
135466f727bcSkettenis 
135525da8179Spatrick void
135625da8179Spatrick agintc_intr_set_wakeup(void *cookie)
135725da8179Spatrick {
135825da8179Spatrick 	struct intrhand *ih = cookie;
135925da8179Spatrick 
136025da8179Spatrick 	ih->ih_flags |= IPL_WAKEUP;
136125da8179Spatrick }
136225da8179Spatrick 
13638b7bc089Skettenis void *
13648b7bc089Skettenis agintc_intr_establish_mbi(void *self, uint64_t *addr, uint64_t *data,
13658b7bc089Skettenis     int level, struct cpu_info *ci, int (*func)(void *), void *arg, char *name)
13668b7bc089Skettenis {
13678b7bc089Skettenis 	struct agintc_softc *sc = agintc_sc;
13688b7bc089Skettenis 	struct agintc_mbi_range *mr;
13698b7bc089Skettenis 	void *cookie;
13708b7bc089Skettenis 	int i, j, hwcpu;
13718b7bc089Skettenis 
13728b7bc089Skettenis 	if (ci == NULL)
13738b7bc089Skettenis 		ci = &cpu_info_primary;
13748b7bc089Skettenis 	hwcpu = agintc_sc->sc_cpuremap[ci->ci_cpuid];
13758b7bc089Skettenis 
13768b7bc089Skettenis 	for (i = 0; i < sc->sc_mbi_nranges; i++) {
13778b7bc089Skettenis 		mr = &sc->sc_mbi_ranges[i];
13788b7bc089Skettenis 		for (j = 0; j < mr->mr_span; j++) {
13798b7bc089Skettenis 			if (mr->mr_mbi[j] != NULL)
13808b7bc089Skettenis 				continue;
13818b7bc089Skettenis 
13828b7bc089Skettenis 			cookie = agintc_intr_establish(mr->mr_base + j,
13838b7bc089Skettenis 			    IST_EDGE_RISING, level, ci, func, arg, name);
13848b7bc089Skettenis 			if (cookie == NULL)
13858b7bc089Skettenis 				return NULL;
13868b7bc089Skettenis 
13878b7bc089Skettenis 			*addr = sc->sc_mbi_addr;
13888b7bc089Skettenis 			*data = mr->mr_base + j;
13898b7bc089Skettenis 
13908b7bc089Skettenis 			mr->mr_mbi[j] = cookie;
13918b7bc089Skettenis 			return cookie;
13928b7bc089Skettenis 		}
13938b7bc089Skettenis 	}
13948b7bc089Skettenis 
13958b7bc089Skettenis 	return NULL;
13968b7bc089Skettenis }
13978b7bc089Skettenis 
139866f727bcSkettenis void
139966f727bcSkettenis agintc_eoi(uint32_t eoi)
140066f727bcSkettenis {
140166f727bcSkettenis 	__asm volatile("msr "STR(ICC_EOIR1)", %x0" :: "r" (eoi));
140266f727bcSkettenis 	__isb();
140366f727bcSkettenis }
140466f727bcSkettenis 
140566f727bcSkettenis void
140666f727bcSkettenis agintc_d_wait_rwp(struct agintc_softc *sc)
140766f727bcSkettenis {
140866f727bcSkettenis 	int count = 100000;
140966f727bcSkettenis 	uint32_t v;
141066f727bcSkettenis 
141166f727bcSkettenis 	do {
141266f727bcSkettenis 		v = bus_space_read_4(sc->sc_iot, sc->sc_d_ioh, GICD_CTLR);
141366f727bcSkettenis 	} while (--count && (v & GICD_CTLR_RWP));
141466f727bcSkettenis 
141566f727bcSkettenis 	if (count == 0)
1416d7e3db9cSkettenis 		panic("%s: RWP timed out 0x08%x", __func__, v);
141766f727bcSkettenis }
141866f727bcSkettenis 
141966f727bcSkettenis void
142066f727bcSkettenis agintc_r_wait_rwp(struct agintc_softc *sc)
142166f727bcSkettenis {
142266f727bcSkettenis 	struct cpu_info *ci = curcpu();
142366f727bcSkettenis 	int hwcpu = sc->sc_cpuremap[ci->ci_cpuid];
142466f727bcSkettenis 	int count = 100000;
142566f727bcSkettenis 	uint32_t v;
142666f727bcSkettenis 
142766f727bcSkettenis 	do {
142866f727bcSkettenis 		v = bus_space_read_4(sc->sc_iot, sc->sc_r_ioh[hwcpu],
142966f727bcSkettenis 		    GICR_CTLR);
143066f727bcSkettenis 	} while (--count && (v & GICR_CTLR_RWP));
143166f727bcSkettenis 
143266f727bcSkettenis 	if (count == 0)
1433d7e3db9cSkettenis 		panic("%s: RWP timed out 0x08%x", __func__, v);
1434d7e3db9cSkettenis }
1435d7e3db9cSkettenis 
1436d7e3db9cSkettenis #ifdef MULTIPROCESSOR
1437d7e3db9cSkettenis int
1438d7e3db9cSkettenis agintc_ipi_ddb(void *v)
1439d7e3db9cSkettenis {
1440d7e3db9cSkettenis 	/* XXX */
1441a9a6a9f2Sderaadt #ifdef DDB
1442d7e3db9cSkettenis 	db_enter();
1443a9a6a9f2Sderaadt #endif
1444d7e3db9cSkettenis 	return 1;
1445d7e3db9cSkettenis }
1446d7e3db9cSkettenis 
1447d7e3db9cSkettenis int
14484002e08dSkettenis agintc_ipi_halt(void *v)
14494002e08dSkettenis {
1450aa08aab7Skettenis 	struct agintc_softc *sc = v;
1451aa08aab7Skettenis 	int old = curcpu()->ci_cpl;
1452aa08aab7Skettenis 
1453aa08aab7Skettenis 	intr_disable();
1454aa08aab7Skettenis 	agintc_eoi(sc->sc_ipi_num[ARM_IPI_HALT]);
1455aa08aab7Skettenis 	agintc_setipl(IPL_NONE);
1456aa08aab7Skettenis 
14574002e08dSkettenis 	cpu_halt();
1458aa08aab7Skettenis 
1459aa08aab7Skettenis 	agintc_setipl(old);
1460aa08aab7Skettenis 	intr_enable();
14614002e08dSkettenis 	return 1;
14624002e08dSkettenis }
14634002e08dSkettenis 
14644002e08dSkettenis int
1465d7e3db9cSkettenis agintc_ipi_nop(void *v)
1466d7e3db9cSkettenis {
1467d7e3db9cSkettenis 	/* Nothing to do here, just enough to wake up from WFI */
1468d7e3db9cSkettenis 	return 1;
1469d7e3db9cSkettenis }
1470d7e3db9cSkettenis 
1471d7e3db9cSkettenis int
1472d7e3db9cSkettenis agintc_ipi_combined(void *v)
1473d7e3db9cSkettenis {
1474d7e3db9cSkettenis 	struct agintc_softc *sc = v;
1475d7e3db9cSkettenis 
1476d7e3db9cSkettenis 	if (sc->sc_ipi_reason[cpu_number()] == ARM_IPI_DDB) {
1477d7e3db9cSkettenis 		sc->sc_ipi_reason[cpu_number()] = ARM_IPI_NOP;
1478d7e3db9cSkettenis 		return agintc_ipi_ddb(v);
14794002e08dSkettenis 	} else if (sc->sc_ipi_reason[cpu_number()] == ARM_IPI_HALT) {
14804002e08dSkettenis 		sc->sc_ipi_reason[cpu_number()] = ARM_IPI_NOP;
14814002e08dSkettenis 		return agintc_ipi_halt(v);
1482d7e3db9cSkettenis 	} else {
1483d7e3db9cSkettenis 		return agintc_ipi_nop(v);
1484d7e3db9cSkettenis 	}
148566f727bcSkettenis }
148666f727bcSkettenis 
148766f727bcSkettenis void
1488d7e3db9cSkettenis agintc_send_ipi(struct cpu_info *ci, int id)
148966f727bcSkettenis {
1490d7e3db9cSkettenis 	struct agintc_softc	*sc = agintc_sc;
1491d7e3db9cSkettenis 	uint64_t sendmask;
149266f727bcSkettenis 
1493d7e3db9cSkettenis 	if (ci == curcpu() && id == ARM_IPI_NOP)
1494d7e3db9cSkettenis 		return;
1495d7e3db9cSkettenis 
14964002e08dSkettenis 	/* never overwrite IPI_DDB or IPI_HALT with IPI_NOP */
14974002e08dSkettenis 	if (id == ARM_IPI_DDB || id == ARM_IPI_HALT)
1498d7e3db9cSkettenis 		sc->sc_ipi_reason[ci->ci_cpuid] = id;
1499d7e3db9cSkettenis 
1500d7e3db9cSkettenis 	/* will only send 1 cpu */
15015ce1efbdSkettenis 	sendmask = (ci->ci_mpidr & MPIDR_AFF3) << 16;
15025ce1efbdSkettenis 	sendmask |= (ci->ci_mpidr & MPIDR_AFF2) << 16;
15035ce1efbdSkettenis 	sendmask |= (ci->ci_mpidr & MPIDR_AFF1) << 8;
15045ce1efbdSkettenis 	sendmask |= 1 << (ci->ci_mpidr & 0x0f);
1505d7e3db9cSkettenis 	sendmask |= (sc->sc_ipi_num[id] << 24);
1506d7e3db9cSkettenis 
1507d7e3db9cSkettenis 	__asm volatile ("msr " STR(ICC_SGI1R)", %x0" ::"r"(sendmask));
150866f727bcSkettenis }
1509d7e3db9cSkettenis #endif
1510f0a2ac19Skettenis 
1511f0a2ac19Skettenis /*
1512f0a2ac19Skettenis  * GICv3 ITS controller for MSI interrupts.
1513f0a2ac19Skettenis  */
1514f0a2ac19Skettenis #define GITS_CTLR		0x0000
1515f0a2ac19Skettenis #define  GITS_CTLR_ENABLED	(1UL << 0)
1516f0a2ac19Skettenis #define GITS_TYPER		0x0008
1517f0a2ac19Skettenis #define  GITS_TYPER_CIL		(1ULL << 36)
151848352e7bSkettenis #define  GITS_TYPER_CIDBITS(x)	(((x) >> 32) & 0xf)
1519f0a2ac19Skettenis #define  GITS_TYPER_HCC(x)	(((x) >> 24) & 0xff)
1520f0a2ac19Skettenis #define  GITS_TYPER_PTA		(1ULL << 19)
152191fec423Skettenis #define  GITS_TYPER_DEVBITS(x)	(((x) >> 13) & 0x1f)
1522f0a2ac19Skettenis #define  GITS_TYPER_ITE_SZ(x)	(((x) >> 4) & 0xf)
1523f0a2ac19Skettenis #define  GITS_TYPER_PHYS	(1ULL << 0)
1524f0a2ac19Skettenis #define GITS_CBASER		0x0080
1525f0a2ac19Skettenis #define  GITS_CBASER_VALID	(1ULL << 63)
1526f0a2ac19Skettenis #define  GITS_CBASER_IC_NORM_NC	(1ULL << 59)
1527f0a2ac19Skettenis #define  GITS_CBASER_MASK	0x1ffffffffff000ULL
1528f0a2ac19Skettenis #define GITS_CWRITER		0x0088
1529f0a2ac19Skettenis #define GITS_CREADR		0x0090
1530f0a2ac19Skettenis #define GITS_BASER(i)		(0x0100 + ((i) * 8))
1531f0a2ac19Skettenis #define  GITS_BASER_VALID	(1ULL << 63)
1532f0a2ac19Skettenis #define  GITS_BASER_INDIRECT	(1ULL << 62)
1533f0a2ac19Skettenis #define  GITS_BASER_IC_NORM_NC	(1ULL << 59)
1534f0a2ac19Skettenis #define  GITS_BASER_TYPE_MASK	(7ULL << 56)
1535f0a2ac19Skettenis #define  GITS_BASER_TYPE_DEVICE	(1ULL << 56)
153648352e7bSkettenis #define  GITS_BASER_TYPE_COLL	(4ULL << 56)
153748352e7bSkettenis #define  GITS_BASER_TTE_SZ(x)	(((x) >> 48) & 0x1f)
153891fec423Skettenis #define  GITS_BASER_PGSZ_MASK	(3ULL << 8)
153991fec423Skettenis #define  GITS_BASER_PGSZ_4K	(0ULL << 8)
154091fec423Skettenis #define  GITS_BASER_PGSZ_16K	(1ULL << 8)
154191fec423Skettenis #define  GITS_BASER_PGSZ_64K	(2ULL << 8)
15422679ff7fSpatrick #define  GITS_BASER_SZ_MASK	(0xffULL)
154391fec423Skettenis #define  GITS_BASER_PA_MASK	0x7ffffffff000ULL
1544f0a2ac19Skettenis #define GITS_TRANSLATER		0x10040
1545f0a2ac19Skettenis 
154691fec423Skettenis #define GITS_NUM_BASER		8
154791fec423Skettenis 
1548f0a2ac19Skettenis struct gits_cmd {
1549f0a2ac19Skettenis 	uint8_t cmd;
1550f0a2ac19Skettenis 	uint32_t deviceid;
1551f0a2ac19Skettenis 	uint32_t eventid;
1552f0a2ac19Skettenis 	uint32_t intid;
1553f0a2ac19Skettenis 	uint64_t dw2;
1554f0a2ac19Skettenis 	uint64_t dw3;
1555f0a2ac19Skettenis };
1556f0a2ac19Skettenis 
1557f0a2ac19Skettenis #define GITS_CMD_VALID		(1ULL << 63)
1558f0a2ac19Skettenis 
1559f0a2ac19Skettenis /* ITS commands */
1560f0a2ac19Skettenis #define SYNC	0x05
1561f0a2ac19Skettenis #define MAPD	0x08
1562f0a2ac19Skettenis #define MAPC	0x09
1563f0a2ac19Skettenis #define MAPTI	0x0a
1564c60fe750Spatrick #define INV	0x0c
1565c60fe750Spatrick #define INVALL	0x0d
1566f83e6fb8Spatrick #define DISCARD 0x0f
1567f0a2ac19Skettenis 
1568f0a2ac19Skettenis #define GITS_CMDQ_SIZE		(64 * 1024)
1569f0a2ac19Skettenis #define GITS_CMDQ_NENTRIES	(GITS_CMDQ_SIZE / sizeof(struct gits_cmd))
1570f0a2ac19Skettenis 
1571f0a2ac19Skettenis struct agintc_msi_device {
1572f0a2ac19Skettenis 	LIST_ENTRY(agintc_msi_device) md_list;
1573f0a2ac19Skettenis 
1574f0a2ac19Skettenis 	uint32_t		md_deviceid;
157556d02c00Skettenis 	uint32_t		md_events;
1576f0a2ac19Skettenis 	struct agintc_dmamem	*md_itt;
1577f0a2ac19Skettenis };
1578f0a2ac19Skettenis 
1579f0a2ac19Skettenis int	 agintc_msi_match(struct device *, void *, void *);
1580f0a2ac19Skettenis void	 agintc_msi_attach(struct device *, struct device *, void *);
1581f0a2ac19Skettenis void	*agintc_intr_establish_msi(void *, uint64_t *, uint64_t *,
1582789e88a4Spatrick 	    int , struct cpu_info *, int (*)(void *), void *, char *);
1583f0a2ac19Skettenis void	 agintc_intr_disestablish_msi(void *);
1584452daaedSpatrick void	 agintc_intr_barrier_msi(void *);
1585f0a2ac19Skettenis 
1586f0a2ac19Skettenis struct agintc_msi_softc {
1587f0a2ac19Skettenis 	struct device			sc_dev;
1588f0a2ac19Skettenis 	bus_space_tag_t			sc_iot;
1589f0a2ac19Skettenis 	bus_space_handle_t		sc_ioh;
1590f0a2ac19Skettenis 	bus_dma_tag_t			sc_dmat;
1591f0a2ac19Skettenis 
15922896618dSkettenis 	bus_addr_t			sc_msi_addr;
15932896618dSkettenis 	int				sc_msi_delta;
15942896618dSkettenis 
1595f0a2ac19Skettenis 	struct agintc_dmamem		*sc_cmdq;
1596f0a2ac19Skettenis 	uint16_t			sc_cmdidx;
159791fec423Skettenis 
159891fec423Skettenis 	int				sc_devbits;
15992679ff7fSpatrick 	uint32_t			sc_deviceid_max;
1600f0a2ac19Skettenis 	struct agintc_dmamem		*sc_dtt;
160191fec423Skettenis 	size_t				sc_dtt_pgsz;
160291fec423Skettenis 	uint8_t				sc_dte_sz;
16037afc621aSpatrick 	int				sc_dtt_indirect;
160448352e7bSkettenis 	int				sc_cidbits;
160548352e7bSkettenis 	struct agintc_dmamem		*sc_ctt;
160648352e7bSkettenis 	size_t				sc_ctt_pgsz;
160748352e7bSkettenis 	uint8_t				sc_cte_sz;
1608f0a2ac19Skettenis 	uint8_t				sc_ite_sz;
1609f0a2ac19Skettenis 
1610f0a2ac19Skettenis 	LIST_HEAD(, agintc_msi_device)	sc_msi_devices;
1611f0a2ac19Skettenis 
1612f0a2ac19Skettenis 	struct interrupt_controller	sc_ic;
1613f0a2ac19Skettenis };
1614f0a2ac19Skettenis 
16159fdf0c62Smpi const struct cfattach	agintcmsi_ca = {
1616f0a2ac19Skettenis 	sizeof (struct agintc_msi_softc), agintc_msi_match, agintc_msi_attach
1617f0a2ac19Skettenis };
1618f0a2ac19Skettenis 
1619f0a2ac19Skettenis struct cfdriver agintcmsi_cd = {
1620f0a2ac19Skettenis 	NULL, "agintcmsi", DV_DULL
1621f0a2ac19Skettenis };
1622f0a2ac19Skettenis 
1623f0a2ac19Skettenis void	agintc_msi_send_cmd(struct agintc_msi_softc *, struct gits_cmd *);
1624f0a2ac19Skettenis void	agintc_msi_wait_cmd(struct agintc_msi_softc *);
1625f0a2ac19Skettenis 
162618d3f3c5Skettenis #define CPU_IMPL(midr)  (((midr) >> 24) & 0xff)
162718d3f3c5Skettenis #define CPU_PART(midr)  (((midr) >> 4) & 0xfff)
162818d3f3c5Skettenis 
162918d3f3c5Skettenis #define CPU_IMPL_QCOM		0x51
163018d3f3c5Skettenis #define CPU_PART_ORYON		0x001
163118d3f3c5Skettenis 
1632f0a2ac19Skettenis int
1633f0a2ac19Skettenis agintc_msi_match(struct device *parent, void *cfdata, void *aux)
1634f0a2ac19Skettenis {
1635f0a2ac19Skettenis 	struct fdt_attach_args *faa = aux;
1636f0a2ac19Skettenis 
163718d3f3c5Skettenis 	/*
163818d3f3c5Skettenis 	 * XXX For some reason MSIs don't work on Qualcomm X1E SoCs in
163918d3f3c5Skettenis 	 * ACPI mode.  So skip attaching the ITS in that case.  MSIs
164018d3f3c5Skettenis 	 * work fine when booting with a DTB.
164118d3f3c5Skettenis 	 */
164218d3f3c5Skettenis 	if (OF_is_compatible(OF_peer(0), "openbsd,acpi") &&
164318d3f3c5Skettenis 	    CPU_IMPL(curcpu()->ci_midr) == CPU_IMPL_QCOM &&
164418d3f3c5Skettenis 	    CPU_PART(curcpu()->ci_midr) == CPU_PART_ORYON)
164518d3f3c5Skettenis 		return 0;
164618d3f3c5Skettenis 
1647f0a2ac19Skettenis 	return OF_is_compatible(faa->fa_node, "arm,gic-v3-its");
1648f0a2ac19Skettenis }
1649f0a2ac19Skettenis 
1650f0a2ac19Skettenis void
1651f0a2ac19Skettenis agintc_msi_attach(struct device *parent, struct device *self, void *aux)
1652f0a2ac19Skettenis {
1653f0a2ac19Skettenis 	struct agintc_msi_softc *sc = (struct agintc_msi_softc *)self;
1654f0a2ac19Skettenis 	struct fdt_attach_args *faa = aux;
1655f0a2ac19Skettenis 	struct gits_cmd cmd;
16562896618dSkettenis 	uint32_t pre_its[2];
1657f0a2ac19Skettenis 	uint64_t typer;
16587c59a772Spatrick 	int i, hwcpu;
1659f0a2ac19Skettenis 
1660f0a2ac19Skettenis 	if (faa->fa_nreg < 1) {
1661f0a2ac19Skettenis 		printf(": no registers\n");
1662f0a2ac19Skettenis 		return;
1663f0a2ac19Skettenis 	}
1664f0a2ac19Skettenis 
1665f0a2ac19Skettenis 	sc->sc_iot = faa->fa_iot;
1666f0a2ac19Skettenis 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
1667f0a2ac19Skettenis 	    faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
1668f0a2ac19Skettenis 		printf(": can't map registers\n");
1669f0a2ac19Skettenis 		return;
1670f0a2ac19Skettenis 	}
1671f0a2ac19Skettenis 	sc->sc_dmat = faa->fa_dmat;
1672f0a2ac19Skettenis 
16732896618dSkettenis 	sc->sc_msi_addr = faa->fa_reg[0].addr + GITS_TRANSLATER;
16742896618dSkettenis 	if (OF_getpropintarray(faa->fa_node, "socionext,synquacer-pre-its",
16752896618dSkettenis 	    pre_its, sizeof(pre_its)) == sizeof(pre_its)) {
16762896618dSkettenis 		sc->sc_msi_addr = pre_its[0];
16772896618dSkettenis 		sc->sc_msi_delta = 4;
16782896618dSkettenis 	}
16792896618dSkettenis 
1680f0a2ac19Skettenis 	typer = bus_space_read_8(sc->sc_iot, sc->sc_ioh, GITS_TYPER);
168148352e7bSkettenis 	if ((typer & GITS_TYPER_PHYS) == 0 || typer & GITS_TYPER_PTA) {
1682f0a2ac19Skettenis 		printf(": unsupported type 0x%016llx\n", typer);
1683f0a2ac19Skettenis 		goto unmap;
1684f0a2ac19Skettenis 	}
1685f0a2ac19Skettenis 	sc->sc_ite_sz = GITS_TYPER_ITE_SZ(typer) + 1;
168691fec423Skettenis 	sc->sc_devbits = GITS_TYPER_DEVBITS(typer) + 1;
168748352e7bSkettenis 	if (typer & GITS_TYPER_CIL)
168848352e7bSkettenis 		sc->sc_cidbits = GITS_TYPER_CIDBITS(typer) + 1;
168948352e7bSkettenis 	else
169048352e7bSkettenis 		sc->sc_cidbits = 16;
1691f0a2ac19Skettenis 
1692f0a2ac19Skettenis 	/* Set up command queue. */
1693f0a2ac19Skettenis 	sc->sc_cmdq = agintc_dmamem_alloc(sc->sc_dmat,
1694f0a2ac19Skettenis 	    GITS_CMDQ_SIZE, GITS_CMDQ_SIZE);
1695f0a2ac19Skettenis 	if (sc->sc_cmdq == NULL) {
1696f0a2ac19Skettenis 		printf(": can't alloc command queue\n");
1697f0a2ac19Skettenis 		goto unmap;
1698f0a2ac19Skettenis 	}
1699f0a2ac19Skettenis 	bus_space_write_8(sc->sc_iot, sc->sc_ioh, GITS_CBASER,
1700f0a2ac19Skettenis 	    AGINTC_DMA_DVA(sc->sc_cmdq) | GITS_CBASER_IC_NORM_NC |
1701f0a2ac19Skettenis 	    (GITS_CMDQ_SIZE / PAGE_SIZE) - 1 | GITS_CBASER_VALID);
1702f0a2ac19Skettenis 
1703f0a2ac19Skettenis 	/* Set up device translation table. */
170491fec423Skettenis 	for (i = 0; i < GITS_NUM_BASER; i++) {
1705f0a2ac19Skettenis 		uint64_t baser;
170691fec423Skettenis 		paddr_t dtt_pa;
170791fec423Skettenis 		size_t size;
1708f0a2ac19Skettenis 
1709f0a2ac19Skettenis 		baser = bus_space_read_8(sc->sc_iot, sc->sc_ioh, GITS_BASER(i));
1710f0a2ac19Skettenis 		if ((baser & GITS_BASER_TYPE_MASK) != GITS_BASER_TYPE_DEVICE)
1711f0a2ac19Skettenis 			continue;
1712f0a2ac19Skettenis 
171391fec423Skettenis 		/* Determine the maximum supported page size. */
171491fec423Skettenis 		bus_space_write_8(sc->sc_iot, sc->sc_ioh, GITS_BASER(i),
171591fec423Skettenis 		    (baser & ~GITS_BASER_PGSZ_MASK) | GITS_BASER_PGSZ_64K);
171691fec423Skettenis 		baser = bus_space_read_8(sc->sc_iot, sc->sc_ioh, GITS_BASER(i));
171791fec423Skettenis 		if ((baser & GITS_BASER_PGSZ_MASK) == GITS_BASER_PGSZ_64K)
171848352e7bSkettenis 			goto dfound;
171991fec423Skettenis 
172091fec423Skettenis 		bus_space_write_8(sc->sc_iot, sc->sc_ioh, GITS_BASER(i),
172191fec423Skettenis 		    (baser & ~GITS_BASER_PGSZ_MASK) | GITS_BASER_PGSZ_16K);
172291fec423Skettenis 		baser = bus_space_read_8(sc->sc_iot, sc->sc_ioh, GITS_BASER(i));
172391fec423Skettenis 		if ((baser & GITS_BASER_PGSZ_MASK) == GITS_BASER_PGSZ_16K)
172448352e7bSkettenis 			goto dfound;
172591fec423Skettenis 
172691fec423Skettenis 		bus_space_write_8(sc->sc_iot, sc->sc_ioh, GITS_BASER(i),
172791fec423Skettenis 		    (baser & ~GITS_BASER_PGSZ_MASK) | GITS_BASER_PGSZ_4K);
172891fec423Skettenis 		baser = bus_space_read_8(sc->sc_iot, sc->sc_ioh, GITS_BASER(i));
172991fec423Skettenis 
173048352e7bSkettenis 	dfound:
173191fec423Skettenis 		switch (baser & GITS_BASER_PGSZ_MASK) {
173291fec423Skettenis 		case GITS_BASER_PGSZ_4K:
173391fec423Skettenis 			sc->sc_dtt_pgsz = PAGE_SIZE;
173491fec423Skettenis 			break;
173591fec423Skettenis 		case GITS_BASER_PGSZ_16K:
173691fec423Skettenis 			sc->sc_dtt_pgsz = 4 * PAGE_SIZE;
173791fec423Skettenis 			break;
173891fec423Skettenis 		case GITS_BASER_PGSZ_64K:
173991fec423Skettenis 			sc->sc_dtt_pgsz = 16 * PAGE_SIZE;
174091fec423Skettenis 			break;
174191fec423Skettenis 		}
174291fec423Skettenis 
174391fec423Skettenis 		/* Calculate table size. */
174448352e7bSkettenis 		sc->sc_dte_sz = GITS_BASER_TTE_SZ(baser) + 1;
174591fec423Skettenis 		size = (1ULL << sc->sc_devbits) * sc->sc_dte_sz;
174691fec423Skettenis 		size = roundup(size, sc->sc_dtt_pgsz);
174791fec423Skettenis 
17487afc621aSpatrick 		/* Might make sense to go indirect */
17497afc621aSpatrick 		if (size > 2 * sc->sc_dtt_pgsz) {
17507afc621aSpatrick 			bus_space_write_8(sc->sc_iot, sc->sc_ioh, GITS_BASER(i),
17517afc621aSpatrick 			    baser | GITS_BASER_INDIRECT);
17527afc621aSpatrick 			if (bus_space_read_8(sc->sc_iot, sc->sc_ioh,
17537afc621aSpatrick 			    GITS_BASER(i)) & GITS_BASER_INDIRECT)
17547afc621aSpatrick 				sc->sc_dtt_indirect = 1;
17557afc621aSpatrick 		}
17567afc621aSpatrick 		if (sc->sc_dtt_indirect) {
17577afc621aSpatrick 			size = (1ULL << sc->sc_devbits);
17587afc621aSpatrick 			size /= (sc->sc_dtt_pgsz / sc->sc_dte_sz);
17597afc621aSpatrick 			size *= sizeof(uint64_t);
17607afc621aSpatrick 			size = roundup(size, sc->sc_dtt_pgsz);
1761df30041bSpatrick 		}
1762df30041bSpatrick 
17632679ff7fSpatrick 		/* Clamp down to maximum configurable num pages */
17642679ff7fSpatrick 		if (size / sc->sc_dtt_pgsz > GITS_BASER_SZ_MASK + 1)
17652679ff7fSpatrick 			size = (GITS_BASER_SZ_MASK + 1) * sc->sc_dtt_pgsz;
17662679ff7fSpatrick 
17672679ff7fSpatrick 		/* Calculate max deviceid based off configured size */
17682679ff7fSpatrick 		sc->sc_deviceid_max = (size / sc->sc_dte_sz) - 1;
17697afc621aSpatrick 		if (sc->sc_dtt_indirect)
17707afc621aSpatrick 			sc->sc_deviceid_max = ((size / sizeof(uint64_t)) *
17717afc621aSpatrick 			    (sc->sc_dtt_pgsz / sc->sc_dte_sz)) - 1;
17722679ff7fSpatrick 
177391fec423Skettenis 		/* Allocate table. */
1774f0a2ac19Skettenis 		sc->sc_dtt = agintc_dmamem_alloc(sc->sc_dmat,
177591fec423Skettenis 		    size, sc->sc_dtt_pgsz);
1776f0a2ac19Skettenis 		if (sc->sc_dtt == NULL) {
1777f0a2ac19Skettenis 			printf(": can't alloc translation table\n");
1778f0a2ac19Skettenis 			goto unmap;
1779f0a2ac19Skettenis 		}
178091fec423Skettenis 
178191fec423Skettenis 		/* Configure table. */
178291fec423Skettenis 		dtt_pa = AGINTC_DMA_DVA(sc->sc_dtt);
178391fec423Skettenis 		KASSERT((dtt_pa & GITS_BASER_PA_MASK) == dtt_pa);
17842896618dSkettenis 		bus_space_write_8(sc->sc_iot, sc->sc_ioh, GITS_BASER(i),
178591fec423Skettenis 		    GITS_BASER_IC_NORM_NC | baser & GITS_BASER_PGSZ_MASK |
17867afc621aSpatrick 		    dtt_pa | (size / sc->sc_dtt_pgsz) - 1 |
17877afc621aSpatrick 		    (sc->sc_dtt_indirect ? GITS_BASER_INDIRECT : 0) |
17887afc621aSpatrick 		    GITS_BASER_VALID);
1789f0a2ac19Skettenis 	}
1790f0a2ac19Skettenis 
179148352e7bSkettenis 	/* Set up collection translation table. */
179248352e7bSkettenis 	for (i = 0; i < GITS_NUM_BASER; i++) {
179348352e7bSkettenis 		uint64_t baser;
179448352e7bSkettenis 		paddr_t ctt_pa;
179548352e7bSkettenis 		size_t size;
179648352e7bSkettenis 
179748352e7bSkettenis 		baser = bus_space_read_8(sc->sc_iot, sc->sc_ioh, GITS_BASER(i));
179848352e7bSkettenis 		if ((baser & GITS_BASER_TYPE_MASK) != GITS_BASER_TYPE_COLL)
179948352e7bSkettenis 			continue;
180048352e7bSkettenis 
180148352e7bSkettenis 		/* Determine the maximum supported page size. */
180248352e7bSkettenis 		bus_space_write_8(sc->sc_iot, sc->sc_ioh, GITS_BASER(i),
180348352e7bSkettenis 		    (baser & ~GITS_BASER_PGSZ_MASK) | GITS_BASER_PGSZ_64K);
180448352e7bSkettenis 		baser = bus_space_read_8(sc->sc_iot, sc->sc_ioh, GITS_BASER(i));
180548352e7bSkettenis 		if ((baser & GITS_BASER_PGSZ_MASK) == GITS_BASER_PGSZ_64K)
180648352e7bSkettenis 			goto cfound;
180748352e7bSkettenis 
180848352e7bSkettenis 		bus_space_write_8(sc->sc_iot, sc->sc_ioh, GITS_BASER(i),
180948352e7bSkettenis 		    (baser & ~GITS_BASER_PGSZ_MASK) | GITS_BASER_PGSZ_16K);
181048352e7bSkettenis 		baser = bus_space_read_8(sc->sc_iot, sc->sc_ioh, GITS_BASER(i));
181148352e7bSkettenis 		if ((baser & GITS_BASER_PGSZ_MASK) == GITS_BASER_PGSZ_16K)
181248352e7bSkettenis 			goto cfound;
181348352e7bSkettenis 
181448352e7bSkettenis 		bus_space_write_8(sc->sc_iot, sc->sc_ioh, GITS_BASER(i),
181548352e7bSkettenis 		    (baser & ~GITS_BASER_PGSZ_MASK) | GITS_BASER_PGSZ_4K);
181648352e7bSkettenis 		baser = bus_space_read_8(sc->sc_iot, sc->sc_ioh, GITS_BASER(i));
181748352e7bSkettenis 
181848352e7bSkettenis 	cfound:
181948352e7bSkettenis 		switch (baser & GITS_BASER_PGSZ_MASK) {
182048352e7bSkettenis 		case GITS_BASER_PGSZ_4K:
182148352e7bSkettenis 			sc->sc_ctt_pgsz = PAGE_SIZE;
182248352e7bSkettenis 			break;
182348352e7bSkettenis 		case GITS_BASER_PGSZ_16K:
182448352e7bSkettenis 			sc->sc_ctt_pgsz = 4 * PAGE_SIZE;
182548352e7bSkettenis 			break;
182648352e7bSkettenis 		case GITS_BASER_PGSZ_64K:
182748352e7bSkettenis 			sc->sc_ctt_pgsz = 16 * PAGE_SIZE;
182848352e7bSkettenis 			break;
182948352e7bSkettenis 		}
183048352e7bSkettenis 
183148352e7bSkettenis 		/* Calculate table size. */
183248352e7bSkettenis 		sc->sc_cte_sz = GITS_BASER_TTE_SZ(baser) + 1;
183348352e7bSkettenis 		size = (1ULL << sc->sc_cidbits) * sc->sc_cte_sz;
183448352e7bSkettenis 		size = roundup(size, sc->sc_ctt_pgsz);
183548352e7bSkettenis 
183648352e7bSkettenis 		/* Allocate table. */
183748352e7bSkettenis 		sc->sc_ctt = agintc_dmamem_alloc(sc->sc_dmat,
183848352e7bSkettenis 		    size, sc->sc_ctt_pgsz);
183948352e7bSkettenis 		if (sc->sc_ctt == NULL) {
184048352e7bSkettenis 			printf(": can't alloc translation table\n");
184148352e7bSkettenis 			goto unmap;
184248352e7bSkettenis 		}
184348352e7bSkettenis 
184448352e7bSkettenis 		/* Configure table. */
184548352e7bSkettenis 		ctt_pa = AGINTC_DMA_DVA(sc->sc_ctt);
184648352e7bSkettenis 		KASSERT((ctt_pa & GITS_BASER_PA_MASK) == ctt_pa);
184748352e7bSkettenis 		bus_space_write_8(sc->sc_iot, sc->sc_ioh, GITS_BASER(i),
184848352e7bSkettenis 		    GITS_BASER_IC_NORM_NC | baser & GITS_BASER_PGSZ_MASK |
184948352e7bSkettenis 		    ctt_pa | (size / sc->sc_ctt_pgsz) - 1 | GITS_BASER_VALID);
185048352e7bSkettenis 	}
185148352e7bSkettenis 
1852f0a2ac19Skettenis 	/* Enable ITS. */
1853f0a2ac19Skettenis 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, GITS_CTLR,
1854f0a2ac19Skettenis 	    GITS_CTLR_ENABLED);
1855f0a2ac19Skettenis 
1856f0a2ac19Skettenis 	LIST_INIT(&sc->sc_msi_devices);
1857f0a2ac19Skettenis 
18587c59a772Spatrick 	/* Create one collection per core. */
18597c59a772Spatrick 	KASSERT(ncpus <= agintc_sc->sc_num_redist);
18607c59a772Spatrick 	for (i = 0; i < ncpus; i++) {
18617c59a772Spatrick 		hwcpu = agintc_sc->sc_cpuremap[i];
1862f0a2ac19Skettenis 		memset(&cmd, 0, sizeof(cmd));
1863f0a2ac19Skettenis 		cmd.cmd = MAPC;
18647c59a772Spatrick 		cmd.dw2 = GITS_CMD_VALID |
18657c59a772Spatrick 		    (agintc_sc->sc_processor[hwcpu] << 16) | i;
1866f0a2ac19Skettenis 		agintc_msi_send_cmd(sc, &cmd);
1867f0a2ac19Skettenis 		agintc_msi_wait_cmd(sc);
18687c59a772Spatrick 	}
1869f0a2ac19Skettenis 
1870f0a2ac19Skettenis 	printf("\n");
1871f0a2ac19Skettenis 
1872f0a2ac19Skettenis 	sc->sc_ic.ic_node = faa->fa_node;
1873f0a2ac19Skettenis 	sc->sc_ic.ic_cookie = sc;
1874f0a2ac19Skettenis 	sc->sc_ic.ic_establish_msi = agintc_intr_establish_msi;
1875f0a2ac19Skettenis 	sc->sc_ic.ic_disestablish = agintc_intr_disestablish_msi;
1876452daaedSpatrick 	sc->sc_ic.ic_barrier = agintc_intr_barrier_msi;
1877550856c9Sjmatthew 	sc->sc_ic.ic_gic_its_id = OF_getpropint(faa->fa_node,
1878550856c9Sjmatthew 	    "openbsd,gic-its-id", 0);
1879f0a2ac19Skettenis 	arm_intr_register_fdt(&sc->sc_ic);
1880f0a2ac19Skettenis 	return;
1881f0a2ac19Skettenis 
1882f0a2ac19Skettenis unmap:
1883f0a2ac19Skettenis 	if (sc->sc_dtt)
1884f0a2ac19Skettenis 		agintc_dmamem_free(sc->sc_dmat, sc->sc_dtt);
1885f0a2ac19Skettenis 	if (sc->sc_cmdq)
1886f0a2ac19Skettenis 		agintc_dmamem_free(sc->sc_dmat, sc->sc_cmdq);
1887f0a2ac19Skettenis 
1888f0a2ac19Skettenis 	bus_space_unmap(sc->sc_iot, sc->sc_ioh, faa->fa_reg[0].size);
1889f0a2ac19Skettenis }
1890f0a2ac19Skettenis 
1891f0a2ac19Skettenis void
1892f0a2ac19Skettenis agintc_msi_send_cmd(struct agintc_msi_softc *sc, struct gits_cmd *cmd)
1893f0a2ac19Skettenis {
1894f0a2ac19Skettenis 	struct gits_cmd *queue = AGINTC_DMA_KVA(sc->sc_cmdq);
1895f0a2ac19Skettenis 
18962896618dSkettenis 	memcpy(&queue[sc->sc_cmdidx], cmd, sizeof(*cmd));
18972896618dSkettenis 
18982896618dSkettenis 	/* Make globally visible. */
18992896618dSkettenis 	cpu_dcache_wb_range((vaddr_t)&queue[sc->sc_cmdidx], sizeof(*cmd));
1900f0a2ac19Skettenis 	__asm volatile("dsb sy");
1901f0a2ac19Skettenis 
19022896618dSkettenis 	sc->sc_cmdidx++;
1903f0a2ac19Skettenis 	sc->sc_cmdidx %= GITS_CMDQ_NENTRIES;
1904f0a2ac19Skettenis 	bus_space_write_8(sc->sc_iot, sc->sc_ioh, GITS_CWRITER,
1905f0a2ac19Skettenis 	    sc->sc_cmdidx * sizeof(*cmd));
1906f0a2ac19Skettenis }
1907f0a2ac19Skettenis 
1908f0a2ac19Skettenis void
1909f0a2ac19Skettenis agintc_msi_wait_cmd(struct agintc_msi_softc *sc)
1910f0a2ac19Skettenis {
1911f0a2ac19Skettenis 	uint64_t creadr;
1912f0a2ac19Skettenis 	int timo;
1913f0a2ac19Skettenis 
1914f0a2ac19Skettenis 	for (timo = 1000; timo > 0; timo--) {
1915f0a2ac19Skettenis 		creadr = bus_space_read_8(sc->sc_iot, sc->sc_ioh, GITS_CREADR);
1916f0a2ac19Skettenis 		if (creadr == sc->sc_cmdidx * sizeof(struct gits_cmd))
1917f0a2ac19Skettenis 			break;
1918f0a2ac19Skettenis 		delay(1);
1919f0a2ac19Skettenis 	}
1920f0a2ac19Skettenis 	if (timo == 0)
1921f0a2ac19Skettenis 		printf("%s: command queue timeout\n", sc->sc_dev.dv_xname);
1922f0a2ac19Skettenis }
1923f0a2ac19Skettenis 
19247afc621aSpatrick int
19257afc621aSpatrick agintc_msi_create_device_table(struct agintc_msi_softc *sc, uint32_t deviceid)
19267afc621aSpatrick {
19277afc621aSpatrick 	uint64_t *table = AGINTC_DMA_KVA(sc->sc_dtt);
19287afc621aSpatrick 	uint32_t idx = deviceid / (sc->sc_dtt_pgsz / sc->sc_dte_sz);
19297afc621aSpatrick 	struct agintc_dmamem *dtt;
19307afc621aSpatrick 	paddr_t dtt_pa;
19317afc621aSpatrick 
19327afc621aSpatrick 	/* Out of bounds */
19337afc621aSpatrick 	if (deviceid > sc->sc_deviceid_max)
19347afc621aSpatrick 		return ENXIO;
19357afc621aSpatrick 
19367afc621aSpatrick 	/* No need to adjust */
19377afc621aSpatrick 	if (!sc->sc_dtt_indirect)
19387afc621aSpatrick 		return 0;
19397afc621aSpatrick 
19407afc621aSpatrick 	/* Table already allocated */
19417afc621aSpatrick 	if (table[idx])
19427afc621aSpatrick 		return 0;
19437afc621aSpatrick 
19447afc621aSpatrick 	/* FIXME: leaks */
19457afc621aSpatrick 	dtt = agintc_dmamem_alloc(sc->sc_dmat,
19467afc621aSpatrick 	    sc->sc_dtt_pgsz, sc->sc_dtt_pgsz);
19477afc621aSpatrick 	if (dtt == NULL)
19487afc621aSpatrick 		return ENOMEM;
19497afc621aSpatrick 
19507afc621aSpatrick 	dtt_pa = AGINTC_DMA_DVA(dtt);
19517afc621aSpatrick 	KASSERT((dtt_pa & GITS_BASER_PA_MASK) == dtt_pa);
19527afc621aSpatrick 	table[idx] = dtt_pa | GITS_BASER_VALID;
19537afc621aSpatrick 	cpu_dcache_wb_range((vaddr_t)&table[idx], sizeof(table[idx]));
19547afc621aSpatrick 	__asm volatile("dsb sy");
19557afc621aSpatrick 	return 0;
19567afc621aSpatrick }
19577afc621aSpatrick 
1958f0a2ac19Skettenis struct agintc_msi_device *
1959f0a2ac19Skettenis agintc_msi_create_device(struct agintc_msi_softc *sc, uint32_t deviceid)
1960f0a2ac19Skettenis {
1961f0a2ac19Skettenis 	struct agintc_msi_device *md;
1962f0a2ac19Skettenis 	struct gits_cmd cmd;
1963f0a2ac19Skettenis 
19642679ff7fSpatrick 	if (deviceid > sc->sc_deviceid_max)
19652679ff7fSpatrick 		return NULL;
19662679ff7fSpatrick 
19677afc621aSpatrick 	if (agintc_msi_create_device_table(sc, deviceid) != 0)
19687afc621aSpatrick 		return NULL;
19697afc621aSpatrick 
1970f0a2ac19Skettenis 	md = malloc(sizeof(*md), M_DEVBUF, M_ZERO | M_WAITOK);
1971f0a2ac19Skettenis 	md->md_deviceid = deviceid;
1972f0a2ac19Skettenis 	md->md_itt = agintc_dmamem_alloc(sc->sc_dmat,
1973f0a2ac19Skettenis 	    32 * sc->sc_ite_sz, PAGE_SIZE);
1974f0a2ac19Skettenis 	LIST_INSERT_HEAD(&sc->sc_msi_devices, md, md_list);
1975f0a2ac19Skettenis 
1976f0a2ac19Skettenis 	memset(&cmd, 0, sizeof(cmd));
1977f0a2ac19Skettenis 	cmd.cmd = MAPD;
1978f0a2ac19Skettenis 	cmd.deviceid = deviceid;
1979f0a2ac19Skettenis 	cmd.eventid = 4;	/* size */
1980f0a2ac19Skettenis 	cmd.dw2 = AGINTC_DMA_DVA(md->md_itt) | GITS_CMD_VALID;
1981f0a2ac19Skettenis 	agintc_msi_send_cmd(sc, &cmd);
1982f0a2ac19Skettenis 	agintc_msi_wait_cmd(sc);
1983f0a2ac19Skettenis 
1984f0a2ac19Skettenis 	return md;
1985f0a2ac19Skettenis }
1986f0a2ac19Skettenis 
1987f0a2ac19Skettenis struct agintc_msi_device *
1988f0a2ac19Skettenis agintc_msi_find_device(struct agintc_msi_softc *sc, uint32_t deviceid)
1989f0a2ac19Skettenis {
1990f0a2ac19Skettenis 	struct agintc_msi_device *md;
1991f0a2ac19Skettenis 
1992f0a2ac19Skettenis 	LIST_FOREACH(md, &sc->sc_msi_devices, md_list) {
1993f0a2ac19Skettenis 		if (md->md_deviceid == deviceid)
1994f0a2ac19Skettenis 			return md;
1995f0a2ac19Skettenis 	}
1996f0a2ac19Skettenis 
1997f0a2ac19Skettenis 	return agintc_msi_create_device(sc, deviceid);
1998f0a2ac19Skettenis }
1999f0a2ac19Skettenis 
2000c60fe750Spatrick void
2001f83e6fb8Spatrick agintc_msi_discard(struct agintc_lpi_info *li)
2002c60fe750Spatrick {
2003c60fe750Spatrick 	struct agintc_msi_softc *sc;
2004f83e6fb8Spatrick 	struct cpu_info *ci;
2005c60fe750Spatrick 	struct gits_cmd cmd;
2006f83e6fb8Spatrick 	int hwcpu;
2007c60fe750Spatrick 
2008f83e6fb8Spatrick 	sc = li->li_msic;
2009f83e6fb8Spatrick 	ci = li->li_ci;
2010f83e6fb8Spatrick 	hwcpu = agintc_sc->sc_cpuremap[ci->ci_cpuid];
2011f83e6fb8Spatrick 
2012c60fe750Spatrick 	memset(&cmd, 0, sizeof(cmd));
2013f83e6fb8Spatrick 	cmd.cmd = DISCARD;
2014f83e6fb8Spatrick 	cmd.deviceid = li->li_deviceid;
2015f83e6fb8Spatrick 	cmd.eventid = li->li_eventid;
2016f83e6fb8Spatrick 	agintc_msi_send_cmd(sc, &cmd);
2017f83e6fb8Spatrick 
2018f83e6fb8Spatrick 	memset(&cmd, 0, sizeof(cmd));
2019f83e6fb8Spatrick 	cmd.cmd = SYNC;
2020f83e6fb8Spatrick 	cmd.dw2 = agintc_sc->sc_processor[hwcpu] << 16;
2021c60fe750Spatrick 	agintc_msi_send_cmd(sc, &cmd);
2022c60fe750Spatrick 	agintc_msi_wait_cmd(sc);
2023c60fe750Spatrick }
2024f83e6fb8Spatrick 
2025f83e6fb8Spatrick void
2026f83e6fb8Spatrick agintc_msi_inv(struct agintc_lpi_info *li)
2027f83e6fb8Spatrick {
2028f83e6fb8Spatrick 	struct agintc_msi_softc *sc;
2029f83e6fb8Spatrick 	struct cpu_info *ci;
2030f83e6fb8Spatrick 	struct gits_cmd cmd;
2031f83e6fb8Spatrick 	int hwcpu;
2032f83e6fb8Spatrick 
2033f83e6fb8Spatrick 	sc = li->li_msic;
2034f83e6fb8Spatrick 	ci = li->li_ci;
2035f83e6fb8Spatrick 	hwcpu = agintc_sc->sc_cpuremap[ci->ci_cpuid];
2036f83e6fb8Spatrick 
2037f83e6fb8Spatrick 	memset(&cmd, 0, sizeof(cmd));
2038f83e6fb8Spatrick 	cmd.cmd = INV;
2039f83e6fb8Spatrick 	cmd.deviceid = li->li_deviceid;
2040f83e6fb8Spatrick 	cmd.eventid = li->li_eventid;
2041f83e6fb8Spatrick 	agintc_msi_send_cmd(sc, &cmd);
2042f83e6fb8Spatrick 
2043f83e6fb8Spatrick 	memset(&cmd, 0, sizeof(cmd));
2044f83e6fb8Spatrick 	cmd.cmd = SYNC;
2045f83e6fb8Spatrick 	cmd.dw2 = agintc_sc->sc_processor[hwcpu] << 16;
2046f83e6fb8Spatrick 	agintc_msi_send_cmd(sc, &cmd);
2047f83e6fb8Spatrick 	agintc_msi_wait_cmd(sc);
2048c60fe750Spatrick }
2049c60fe750Spatrick 
2050f0a2ac19Skettenis void *
2051f0a2ac19Skettenis agintc_intr_establish_msi(void *self, uint64_t *addr, uint64_t *data,
2052789e88a4Spatrick     int level, struct cpu_info *ci, int (*func)(void *), void *arg, char *name)
2053f0a2ac19Skettenis {
2054f0a2ac19Skettenis 	struct agintc_msi_softc *sc = (struct agintc_msi_softc *)self;
2055f0a2ac19Skettenis 	struct agintc_msi_device *md;
2056f0a2ac19Skettenis 	struct gits_cmd cmd;
2057f0a2ac19Skettenis 	uint32_t deviceid = *data;
2058f0a2ac19Skettenis 	uint32_t eventid;
20597c59a772Spatrick 	int i, hwcpu;
2060f0a2ac19Skettenis 
2061789e88a4Spatrick 	if (ci == NULL)
2062789e88a4Spatrick 		ci = &cpu_info_primary;
20637c59a772Spatrick 	hwcpu = agintc_sc->sc_cpuremap[ci->ci_cpuid];
2064789e88a4Spatrick 
2065f0a2ac19Skettenis 	md = agintc_msi_find_device(sc, deviceid);
2066f0a2ac19Skettenis 	if (md == NULL)
2067f0a2ac19Skettenis 		return NULL;
2068f0a2ac19Skettenis 
206956d02c00Skettenis 	eventid = *addr;
207056d02c00Skettenis 	if (eventid > 0 && (md->md_events & (1U << eventid)))
207156d02c00Skettenis 		return NULL;
207256d02c00Skettenis 	for (; eventid < 32; eventid++) {
207356d02c00Skettenis 		if ((md->md_events & (1U << eventid)) == 0) {
207456d02c00Skettenis 			md->md_events |= (1U << eventid);
207556d02c00Skettenis 			break;
207656d02c00Skettenis 		}
207756d02c00Skettenis 	}
2078f0a2ac19Skettenis 	if (eventid >= 32)
2079f0a2ac19Skettenis 		return NULL;
2080f0a2ac19Skettenis 
2081f83e6fb8Spatrick 	for (i = 0; i < agintc_sc->sc_nlpi; i++) {
2082f83e6fb8Spatrick 		if (agintc_sc->sc_lpi[i] != NULL)
2083f0a2ac19Skettenis 			continue;
2084f0a2ac19Skettenis 
2085f83e6fb8Spatrick 		agintc_sc->sc_lpi[i] = malloc(sizeof(struct agintc_lpi_info),
2086f83e6fb8Spatrick 		    M_DEVBUF, M_WAITOK | M_ZERO);
2087f83e6fb8Spatrick 		agintc_sc->sc_lpi[i]->li_msic = sc;
2088f83e6fb8Spatrick 		agintc_sc->sc_lpi[i]->li_ci = ci;
2089f83e6fb8Spatrick 		agintc_sc->sc_lpi[i]->li_deviceid = deviceid;
2090f83e6fb8Spatrick 		agintc_sc->sc_lpi[i]->li_eventid = eventid;
2091f83e6fb8Spatrick 		agintc_sc->sc_lpi[i]->li_ih =
2092f83e6fb8Spatrick 		    agintc_intr_establish(LPI_BASE + i,
20936214007cSpatrick 		    IST_EDGE_RISING, level, ci, func, arg, name);
2094f83e6fb8Spatrick 		if (agintc_sc->sc_lpi[i]->li_ih == NULL) {
2095f83e6fb8Spatrick 			free(agintc_sc->sc_lpi[i], M_DEVBUF,
2096f83e6fb8Spatrick 			    sizeof(struct agintc_lpi_info));
2097f83e6fb8Spatrick 			agintc_sc->sc_lpi[i] = NULL;
2098f0a2ac19Skettenis 			return NULL;
2099f83e6fb8Spatrick 		}
2100f0a2ac19Skettenis 
2101f0a2ac19Skettenis 		memset(&cmd, 0, sizeof(cmd));
2102f0a2ac19Skettenis 		cmd.cmd = MAPTI;
2103f0a2ac19Skettenis 		cmd.deviceid = deviceid;
2104f0a2ac19Skettenis 		cmd.eventid = eventid;
2105f0a2ac19Skettenis 		cmd.intid = LPI_BASE + i;
21067c59a772Spatrick 		cmd.dw2 = ci->ci_cpuid;
2107f0a2ac19Skettenis 		agintc_msi_send_cmd(sc, &cmd);
2108f0a2ac19Skettenis 
2109f0a2ac19Skettenis 		memset(&cmd, 0, sizeof(cmd));
2110f0a2ac19Skettenis 		cmd.cmd = SYNC;
21117c59a772Spatrick 		cmd.dw2 = agintc_sc->sc_processor[hwcpu] << 16;
2112f0a2ac19Skettenis 		agintc_msi_send_cmd(sc, &cmd);
2113f0a2ac19Skettenis 		agintc_msi_wait_cmd(sc);
2114f0a2ac19Skettenis 
21152896618dSkettenis 		*addr = sc->sc_msi_addr + deviceid * sc->sc_msi_delta;
2116f0a2ac19Skettenis 		*data = eventid;
2117f83e6fb8Spatrick 		return &agintc_sc->sc_lpi[i];
2118f0a2ac19Skettenis 	}
2119f0a2ac19Skettenis 
2120f0a2ac19Skettenis 	return NULL;
2121f0a2ac19Skettenis }
2122f0a2ac19Skettenis 
2123f0a2ac19Skettenis void
2124f0a2ac19Skettenis agintc_intr_disestablish_msi(void *cookie)
2125f0a2ac19Skettenis {
2126f83e6fb8Spatrick 	struct agintc_lpi_info *li = *(void **)cookie;
21270bf07e22Skettenis 
2128f83e6fb8Spatrick 	agintc_intr_disestablish(li->li_ih);
2129f83e6fb8Spatrick 	agintc_msi_discard(li);
2130f83e6fb8Spatrick 	agintc_msi_inv(li);
2131f83e6fb8Spatrick 
2132f83e6fb8Spatrick 	free(li, M_DEVBUF, sizeof(*li));
2133f83e6fb8Spatrick 	*(void **)cookie = NULL;
2134f0a2ac19Skettenis }
2135f0a2ac19Skettenis 
2136452daaedSpatrick void
2137452daaedSpatrick agintc_intr_barrier_msi(void *cookie)
2138452daaedSpatrick {
2139f83e6fb8Spatrick 	struct agintc_lpi_info *li = *(void **)cookie;
2140f83e6fb8Spatrick 
2141f83e6fb8Spatrick 	agintc_intr_barrier(li->li_ih);
2142452daaedSpatrick }
2143452daaedSpatrick 
2144f0a2ac19Skettenis struct agintc_dmamem *
2145f0a2ac19Skettenis agintc_dmamem_alloc(bus_dma_tag_t dmat, bus_size_t size, bus_size_t align)
2146f0a2ac19Skettenis {
2147f0a2ac19Skettenis 	struct agintc_dmamem *adm;
2148f0a2ac19Skettenis 	int nsegs;
2149f0a2ac19Skettenis 
2150f0a2ac19Skettenis 	adm = malloc(sizeof(*adm), M_DEVBUF, M_WAITOK | M_ZERO);
2151f0a2ac19Skettenis 	adm->adm_size = size;
2152f0a2ac19Skettenis 
2153f0a2ac19Skettenis 	if (bus_dmamap_create(dmat, size, 1, size, 0,
2154f0a2ac19Skettenis 	    BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW, &adm->adm_map) != 0)
2155f0a2ac19Skettenis 		goto admfree;
2156f0a2ac19Skettenis 
2157f0a2ac19Skettenis 	if (bus_dmamem_alloc(dmat, size, align, 0, &adm->adm_seg, 1,
2158f0a2ac19Skettenis 	    &nsegs, BUS_DMA_WAITOK | BUS_DMA_ZERO) != 0)
2159f0a2ac19Skettenis 		goto destroy;
2160f0a2ac19Skettenis 
2161f0a2ac19Skettenis 	if (bus_dmamem_map(dmat, &adm->adm_seg, nsegs, size,
2162f0a2ac19Skettenis 	    &adm->adm_kva, BUS_DMA_WAITOK | BUS_DMA_NOCACHE) != 0)
2163f0a2ac19Skettenis 		goto free;
2164f0a2ac19Skettenis 
2165f0a2ac19Skettenis 	if (bus_dmamap_load_raw(dmat, adm->adm_map, &adm->adm_seg,
2166f0a2ac19Skettenis 	    nsegs, size, BUS_DMA_WAITOK) != 0)
2167f0a2ac19Skettenis 		goto unmap;
2168f0a2ac19Skettenis 
21692896618dSkettenis 	/* Make globally visible. */
21702896618dSkettenis 	cpu_dcache_wb_range((vaddr_t)adm->adm_kva, size);
21712896618dSkettenis 	__asm volatile("dsb sy");
2172f0a2ac19Skettenis 	return adm;
2173f0a2ac19Skettenis 
2174f0a2ac19Skettenis unmap:
2175f0a2ac19Skettenis 	bus_dmamem_unmap(dmat, adm->adm_kva, size);
2176f0a2ac19Skettenis free:
2177f0a2ac19Skettenis 	bus_dmamem_free(dmat, &adm->adm_seg, 1);
2178f0a2ac19Skettenis destroy:
2179f0a2ac19Skettenis 	bus_dmamap_destroy(dmat, adm->adm_map);
2180f0a2ac19Skettenis admfree:
2181f0a2ac19Skettenis 	free(adm, M_DEVBUF, sizeof(*adm));
2182f0a2ac19Skettenis 
2183f0a2ac19Skettenis 	return NULL;
2184f0a2ac19Skettenis }
2185f0a2ac19Skettenis 
2186f0a2ac19Skettenis void
2187f0a2ac19Skettenis agintc_dmamem_free(bus_dma_tag_t dmat, struct agintc_dmamem *adm)
2188f0a2ac19Skettenis {
2189f0a2ac19Skettenis 	bus_dmamem_unmap(dmat, adm->adm_kva, adm->adm_size);
2190f0a2ac19Skettenis 	bus_dmamem_free(dmat, &adm->adm_seg, 1);
2191f0a2ac19Skettenis 	bus_dmamap_destroy(dmat, adm->adm_map);
2192f0a2ac19Skettenis 	free(adm, M_DEVBUF, sizeof(*adm));
2193f0a2ac19Skettenis }
2194