xref: /netbsd-src/sys/arch/arm/acpi/gicv3_acpi.c (revision 2080b92896c34be0a43241d659e988120ab11c3b)
1*2080b928Sjmcneill /* $NetBSD: gicv3_acpi.c,v 1.8 2020/12/23 11:05:08 jmcneill Exp $ */
2425e501aSjmcneill 
3425e501aSjmcneill /*-
4425e501aSjmcneill  * Copyright (c) 2018 The NetBSD Foundation, Inc.
5425e501aSjmcneill  * All rights reserved.
6425e501aSjmcneill  *
7425e501aSjmcneill  * This code is derived from software contributed to The NetBSD Foundation
8425e501aSjmcneill  * by Jared McNeill <jmcneill@invisible.ca>.
9425e501aSjmcneill  *
10425e501aSjmcneill  * Redistribution and use in source and binary forms, with or without
11425e501aSjmcneill  * modification, are permitted provided that the following conditions
12425e501aSjmcneill  * are met:
13425e501aSjmcneill  * 1. Redistributions of source code must retain the above copyright
14425e501aSjmcneill  *    notice, this list of conditions and the following disclaimer.
15425e501aSjmcneill  * 2. Redistributions in binary form must reproduce the above copyright
16425e501aSjmcneill  *    notice, this list of conditions and the following disclaimer in the
17425e501aSjmcneill  *    documentation and/or other materials provided with the distribution.
18425e501aSjmcneill  *
19425e501aSjmcneill  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20425e501aSjmcneill  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21425e501aSjmcneill  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22425e501aSjmcneill  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23425e501aSjmcneill  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24425e501aSjmcneill  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25425e501aSjmcneill  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26425e501aSjmcneill  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27425e501aSjmcneill  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28425e501aSjmcneill  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29425e501aSjmcneill  * POSSIBILITY OF SUCH DAMAGE.
30425e501aSjmcneill  */
31425e501aSjmcneill 
32193d42ddSjmcneill #include "pci.h"
33193d42ddSjmcneill 
34425e501aSjmcneill #define	_INTR_PRIVATE
35425e501aSjmcneill 
36425e501aSjmcneill #include <sys/cdefs.h>
37*2080b928Sjmcneill __KERNEL_RCSID(0, "$NetBSD: gicv3_acpi.c,v 1.8 2020/12/23 11:05:08 jmcneill Exp $");
38425e501aSjmcneill 
39425e501aSjmcneill #include <sys/param.h>
40425e501aSjmcneill #include <sys/bus.h>
41425e501aSjmcneill #include <sys/cpu.h>
42425e501aSjmcneill #include <sys/kernel.h>
43425e501aSjmcneill #include <sys/device.h>
44425e501aSjmcneill #include <sys/kmem.h>
45425e501aSjmcneill 
46425e501aSjmcneill #include <dev/acpi/acpireg.h>
47425e501aSjmcneill #include <dev/acpi/acpivar.h>
48425e501aSjmcneill 
49425e501aSjmcneill #include <dev/fdt/fdtvar.h>
50425e501aSjmcneill 
51425e501aSjmcneill #include <arm/cortex/gicv3.h>
5235eeab52Sjmcneill #include <arm/cortex/gicv3_its.h>
53425e501aSjmcneill #include <arm/cortex/gic_reg.h>
54425e501aSjmcneill 
5597755cb2Sjmcneill #include <arm/acpi/gic_v2m_acpi.h>
5697755cb2Sjmcneill 
57425e501aSjmcneill #define	GICD_SIZE	0x10000
58425e501aSjmcneill #define	GICR_SIZE	0x20000
5935eeab52Sjmcneill #define	GITS_SIZE	0x20000
60425e501aSjmcneill 
61425e501aSjmcneill extern struct bus_space arm_generic_bs_tag;
6235eeab52Sjmcneill extern struct arm32_bus_dma_tag arm_generic_dma_tag;
63425e501aSjmcneill 
64425e501aSjmcneill struct gicv3_acpi_softc {
65425e501aSjmcneill 	struct gicv3_softc	sc_gic;
66425e501aSjmcneill 
67425e501aSjmcneill 	ACPI_MADT_GENERIC_DISTRIBUTOR *sc_madt_gicd;
68425e501aSjmcneill };
69425e501aSjmcneill 
70425e501aSjmcneill static int	gicv3_acpi_match(device_t, cfdata_t, void *);
71425e501aSjmcneill static void	gicv3_acpi_attach(device_t, device_t, void *);
72425e501aSjmcneill 
73425e501aSjmcneill static int	gicv3_acpi_map_dist(struct gicv3_acpi_softc *);
74425e501aSjmcneill static int	gicv3_acpi_map_redist(struct gicv3_acpi_softc *);
75193d42ddSjmcneill #if NPCI > 0
7697755cb2Sjmcneill static int	gicv3_acpi_map_msi(struct gicv3_acpi_softc *);
77193d42ddSjmcneill #endif
78425e501aSjmcneill 
79425e501aSjmcneill CFATTACH_DECL_NEW(gicv3_acpi, sizeof(struct gicv3_acpi_softc), gicv3_acpi_match, gicv3_acpi_attach, NULL, NULL);
80425e501aSjmcneill 
81425e501aSjmcneill static int
gicv3_acpi_match(device_t parent,cfdata_t cf,void * aux)82425e501aSjmcneill gicv3_acpi_match(device_t parent, cfdata_t cf, void *aux)
83425e501aSjmcneill {
84425e501aSjmcneill 	ACPI_SUBTABLE_HEADER *hdrp = aux;
85425e501aSjmcneill 	ACPI_MADT_GENERIC_DISTRIBUTOR *gicd;
86425e501aSjmcneill 
87425e501aSjmcneill 	if (hdrp->Type != ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR)
88425e501aSjmcneill 		return 0;
89425e501aSjmcneill 
90425e501aSjmcneill 	gicd = (ACPI_MADT_GENERIC_DISTRIBUTOR *)hdrp;
91425e501aSjmcneill 
92425e501aSjmcneill 	switch (gicd->Version) {
93425e501aSjmcneill 	case ACPI_MADT_GIC_VERSION_NONE:
94*2080b928Sjmcneill 		return __SHIFTOUT(reg_id_aa64pfr0_el1_read(), ID_AA64PFR0_EL1_GIC) != 0;
95425e501aSjmcneill 	case ACPI_MADT_GIC_VERSION_V3:
96425e501aSjmcneill 	case ACPI_MADT_GIC_VERSION_V4:
97425e501aSjmcneill 		return 1;
98425e501aSjmcneill 	default:
99425e501aSjmcneill 		return 0;
100425e501aSjmcneill 	}
101425e501aSjmcneill }
102425e501aSjmcneill 
103425e501aSjmcneill static void
gicv3_acpi_attach(device_t parent,device_t self,void * aux)104425e501aSjmcneill gicv3_acpi_attach(device_t parent, device_t self, void *aux)
105425e501aSjmcneill {
106425e501aSjmcneill 	struct gicv3_acpi_softc * const sc = device_private(self);
107425e501aSjmcneill 	ACPI_MADT_GENERIC_DISTRIBUTOR *gicd = aux;
108425e501aSjmcneill 	int error;
109425e501aSjmcneill 
110425e501aSjmcneill 	sc->sc_gic.sc_dev = self;
111425e501aSjmcneill 	sc->sc_gic.sc_bst = &arm_generic_bs_tag;
11235eeab52Sjmcneill 	sc->sc_gic.sc_dmat = &arm_generic_dma_tag;
113425e501aSjmcneill 	sc->sc_madt_gicd = gicd;
114425e501aSjmcneill 
115425e501aSjmcneill 	aprint_naive("\n");
116425e501aSjmcneill 	aprint_normal(": GICv3\n");
117425e501aSjmcneill 
118425e501aSjmcneill 	error = gicv3_acpi_map_dist(sc);
119425e501aSjmcneill 	if (error) {
120425e501aSjmcneill 		aprint_error_dev(self, "failed to map distributor: %d\n", error);
121425e501aSjmcneill 		return;
122425e501aSjmcneill 	}
123425e501aSjmcneill 
124425e501aSjmcneill 	error = gicv3_acpi_map_redist(sc);
125425e501aSjmcneill 	if (error) {
126425e501aSjmcneill 		aprint_error_dev(self, "failed to map redistributor: %d\n", error);
127425e501aSjmcneill 		return;
128425e501aSjmcneill 	}
129425e501aSjmcneill 
130425e501aSjmcneill 	error = gicv3_init(&sc->sc_gic);
131425e501aSjmcneill 	if (error) {
132425e501aSjmcneill 		aprint_error_dev(self, "failed to initialize GIC: %d\n", error);
133425e501aSjmcneill 		return;
134425e501aSjmcneill 	}
135425e501aSjmcneill 
136193d42ddSjmcneill #if NPCI > 0
13797755cb2Sjmcneill 	gicv3_acpi_map_msi(sc);
138193d42ddSjmcneill #endif
13935eeab52Sjmcneill 
140425e501aSjmcneill 	arm_fdt_irq_set_handler(gicv3_irq_handler);
141425e501aSjmcneill }
142425e501aSjmcneill 
143425e501aSjmcneill static int
gicv3_acpi_map_dist(struct gicv3_acpi_softc * sc)144425e501aSjmcneill gicv3_acpi_map_dist(struct gicv3_acpi_softc *sc)
145425e501aSjmcneill {
146425e501aSjmcneill 	const bus_addr_t addr = sc->sc_madt_gicd->BaseAddress;
147425e501aSjmcneill 	const bus_size_t size = GICD_SIZE;
148425e501aSjmcneill 	int error;
149425e501aSjmcneill 
150425e501aSjmcneill 	error = bus_space_map(sc->sc_gic.sc_bst, addr, size, 0, &sc->sc_gic.sc_bsh_d);
151425e501aSjmcneill 	if (error)
152425e501aSjmcneill 		return error;
153425e501aSjmcneill 
154425e501aSjmcneill 	return 0;
155425e501aSjmcneill }
156425e501aSjmcneill 
157425e501aSjmcneill static ACPI_STATUS
gicv3_acpi_count_gicr(ACPI_SUBTABLE_HEADER * hdrp,void * aux)158425e501aSjmcneill gicv3_acpi_count_gicr(ACPI_SUBTABLE_HEADER *hdrp, void *aux)
159425e501aSjmcneill {
160425e501aSjmcneill 	ACPI_MADT_GENERIC_REDISTRIBUTOR *gicr;
161425e501aSjmcneill 	int *count = aux;
162425e501aSjmcneill 
163425e501aSjmcneill 	if (hdrp->Type == ACPI_MADT_TYPE_GENERIC_REDISTRIBUTOR) {
164425e501aSjmcneill 		gicr = (ACPI_MADT_GENERIC_REDISTRIBUTOR *)hdrp;
165425e501aSjmcneill 		*count += howmany(gicr->Length, GICR_SIZE);
166425e501aSjmcneill 	}
167425e501aSjmcneill 
168425e501aSjmcneill 	return AE_OK;
169425e501aSjmcneill }
170425e501aSjmcneill 
171425e501aSjmcneill static ACPI_STATUS
gicv3_acpi_map_gicr(ACPI_SUBTABLE_HEADER * hdrp,void * aux)172425e501aSjmcneill gicv3_acpi_map_gicr(ACPI_SUBTABLE_HEADER *hdrp, void *aux)
173425e501aSjmcneill {
174425e501aSjmcneill 	struct gicv3_acpi_softc * const sc = aux;
175425e501aSjmcneill 	ACPI_MADT_GENERIC_REDISTRIBUTOR *gicr;
176425e501aSjmcneill 	bus_space_handle_t bsh;
177425e501aSjmcneill 	bus_size_t off;
178425e501aSjmcneill 
179425e501aSjmcneill 	if (hdrp->Type != ACPI_MADT_TYPE_GENERIC_REDISTRIBUTOR)
180425e501aSjmcneill 		return AE_OK;
181425e501aSjmcneill 
182425e501aSjmcneill 	gicr = (ACPI_MADT_GENERIC_REDISTRIBUTOR *)hdrp;
183425e501aSjmcneill 
184425e501aSjmcneill 	if (bus_space_map(sc->sc_gic.sc_bst, gicr->BaseAddress, gicr->Length, 0, &bsh) != 0) {
185425e501aSjmcneill 		aprint_error_dev(sc->sc_gic.sc_dev, "failed to map redistributor at 0x%" PRIx64 " len %#x\n",
186425e501aSjmcneill 		    gicr->BaseAddress, gicr->Length);
187425e501aSjmcneill 		return AE_OK;
188425e501aSjmcneill 	}
189425e501aSjmcneill 
190425e501aSjmcneill 	for (off = 0; off < gicr->Length; off += GICR_SIZE) {
191425e501aSjmcneill 		const int redist = sc->sc_gic.sc_bsh_r_count;
192425e501aSjmcneill 		if (bus_space_subregion(sc->sc_gic.sc_bst, bsh, off, GICR_SIZE, &sc->sc_gic.sc_bsh_r[redist]) != 0) {
193425e501aSjmcneill 			aprint_error_dev(sc->sc_gic.sc_dev, "couldn't subregion redistributor registers\n");
194425e501aSjmcneill 			return AE_OK;
195425e501aSjmcneill 		}
196425e501aSjmcneill 
197425e501aSjmcneill 		aprint_debug_dev(sc->sc_gic.sc_dev, "redist at 0x%" PRIx64 " [GICR]\n", gicr->BaseAddress + off);
198425e501aSjmcneill 
199425e501aSjmcneill 		sc->sc_gic.sc_bsh_r_count++;
200425e501aSjmcneill 
201425e501aSjmcneill 		/* If this is the last redist in this region, skip to the next one */
202425e501aSjmcneill 		const uint32_t typer = bus_space_read_4(sc->sc_gic.sc_bst, sc->sc_gic.sc_bsh_r[redist], GICR_TYPER);
203425e501aSjmcneill 		if (typer & GICR_TYPER_Last)
204425e501aSjmcneill 			break;
2052b0fe585Sjmcneill 
2062b0fe585Sjmcneill 		/* If the redistributor supports virtual LPIs, skip the VLPI register region */
2072b0fe585Sjmcneill 		if (typer & GICR_TYPER_VLPIS)
2082b0fe585Sjmcneill 			off += GICR_SIZE;
209425e501aSjmcneill 	}
210425e501aSjmcneill 
211425e501aSjmcneill 	return AE_OK;
212425e501aSjmcneill }
213425e501aSjmcneill 
214425e501aSjmcneill static ACPI_STATUS
gicv3_acpi_count_gicc(ACPI_SUBTABLE_HEADER * hdrp,void * aux)215425e501aSjmcneill gicv3_acpi_count_gicc(ACPI_SUBTABLE_HEADER *hdrp, void *aux)
216425e501aSjmcneill {
217425e501aSjmcneill 	ACPI_MADT_GENERIC_INTERRUPT *gicc;
218425e501aSjmcneill 	int *count = aux;
219425e501aSjmcneill 
220425e501aSjmcneill 	if (hdrp->Type == ACPI_MADT_TYPE_GENERIC_INTERRUPT) {
221425e501aSjmcneill 		gicc = (ACPI_MADT_GENERIC_INTERRUPT *)hdrp;
222425e501aSjmcneill 		if ((gicc->Flags & ACPI_MADT_ENABLED) != 0)
223425e501aSjmcneill 			(*count)++;
224425e501aSjmcneill 	}
225425e501aSjmcneill 
226425e501aSjmcneill 	return AE_OK;
227425e501aSjmcneill }
228425e501aSjmcneill 
229425e501aSjmcneill static ACPI_STATUS
gicv3_acpi_map_gicc(ACPI_SUBTABLE_HEADER * hdrp,void * aux)230425e501aSjmcneill gicv3_acpi_map_gicc(ACPI_SUBTABLE_HEADER *hdrp, void *aux)
231425e501aSjmcneill {
232425e501aSjmcneill 	struct gicv3_acpi_softc * const sc = aux;
233425e501aSjmcneill 	ACPI_MADT_GENERIC_INTERRUPT *gicc;
234425e501aSjmcneill 
235425e501aSjmcneill 	if (hdrp->Type != ACPI_MADT_TYPE_GENERIC_INTERRUPT)
236425e501aSjmcneill 		return AE_OK;
237425e501aSjmcneill 
238425e501aSjmcneill 	gicc = (ACPI_MADT_GENERIC_INTERRUPT *)hdrp;
239425e501aSjmcneill 	if ((gicc->Flags & ACPI_MADT_ENABLED) == 0)
240425e501aSjmcneill 		return AE_OK;
241425e501aSjmcneill 
242425e501aSjmcneill 	const int redist = sc->sc_gic.sc_bsh_r_count;
243425e501aSjmcneill 	if (bus_space_map(sc->sc_gic.sc_bst, gicc->GicrBaseAddress, GICR_SIZE, 0, &sc->sc_gic.sc_bsh_r[redist]) != 0) {
244425e501aSjmcneill 		aprint_error_dev(sc->sc_gic.sc_dev, "failed to map redistributor at 0x%" PRIx64 " len %#x\n",
245425e501aSjmcneill 		    gicc->GicrBaseAddress, GICR_SIZE);
246425e501aSjmcneill 		return AE_OK;
247425e501aSjmcneill 	}
248425e501aSjmcneill 
249425e501aSjmcneill 	aprint_debug_dev(sc->sc_gic.sc_dev, "redist at 0x%" PRIx64 " [GICC]\n", gicc->GicrBaseAddress);
250425e501aSjmcneill 
251425e501aSjmcneill 	sc->sc_gic.sc_bsh_r_count++;
252425e501aSjmcneill 
253425e501aSjmcneill 	return AE_OK;
254425e501aSjmcneill }
255425e501aSjmcneill 
256425e501aSjmcneill static int
gicv3_acpi_map_redist(struct gicv3_acpi_softc * sc)257425e501aSjmcneill gicv3_acpi_map_redist(struct gicv3_acpi_softc *sc)
258425e501aSjmcneill {
259425e501aSjmcneill 	bool use_gicr = false;
260425e501aSjmcneill 	int max_redist = 0;
261425e501aSjmcneill 
262425e501aSjmcneill 	/*
263425e501aSjmcneill 	 * Try to use GICR structures to describe redistributors. If no GICR
264425e501aSjmcneill 	 * subtables are found, use the GICR address from the GICC subtables.
265425e501aSjmcneill 	 */
266425e501aSjmcneill 	acpi_madt_walk(gicv3_acpi_count_gicr, &max_redist);
267425e501aSjmcneill 	if (max_redist != 0)
268425e501aSjmcneill 		use_gicr = true;
269425e501aSjmcneill 	else
270425e501aSjmcneill 		acpi_madt_walk(gicv3_acpi_count_gicc, &max_redist);
271425e501aSjmcneill 
272425e501aSjmcneill 	if (max_redist == 0)
273425e501aSjmcneill 		return ENODEV;
274425e501aSjmcneill 
275425e501aSjmcneill 	sc->sc_gic.sc_bsh_r = kmem_alloc(sizeof(bus_space_handle_t) * max_redist, KM_SLEEP);
276425e501aSjmcneill 	if (use_gicr)
277425e501aSjmcneill 		acpi_madt_walk(gicv3_acpi_map_gicr, sc);
278425e501aSjmcneill 	else
279425e501aSjmcneill 		acpi_madt_walk(gicv3_acpi_map_gicc, sc);
280425e501aSjmcneill 
281425e501aSjmcneill 	if (sc->sc_gic.sc_bsh_r_count == 0)
282425e501aSjmcneill 		return ENXIO;
283425e501aSjmcneill 
284425e501aSjmcneill 	return 0;
285425e501aSjmcneill }
28635eeab52Sjmcneill 
287193d42ddSjmcneill #if NPCI > 0
28835eeab52Sjmcneill static ACPI_STATUS
gicv3_acpi_map_gits(ACPI_SUBTABLE_HEADER * hdrp,void * aux)28935eeab52Sjmcneill gicv3_acpi_map_gits(ACPI_SUBTABLE_HEADER *hdrp, void *aux)
29035eeab52Sjmcneill {
29135eeab52Sjmcneill 	struct gicv3_acpi_softc * const sc = aux;
29235eeab52Sjmcneill 	ACPI_MADT_GENERIC_TRANSLATOR *gits;
29335eeab52Sjmcneill 	bus_space_handle_t bsh;
29435eeab52Sjmcneill 
29535eeab52Sjmcneill 	if (hdrp->Type != ACPI_MADT_TYPE_GENERIC_TRANSLATOR)
29635eeab52Sjmcneill 		return AE_OK;
29735eeab52Sjmcneill 
29835eeab52Sjmcneill 	gits = (ACPI_MADT_GENERIC_TRANSLATOR *)hdrp;
29935eeab52Sjmcneill 
30035eeab52Sjmcneill 	if (bus_space_map(sc->sc_gic.sc_bst, gits->BaseAddress, GITS_SIZE, 0, &bsh) != 0) {
30135eeab52Sjmcneill 		aprint_error_dev(sc->sc_gic.sc_dev, "failed to map ITS at 0x%" PRIx64 " len %#x\n",
30235eeab52Sjmcneill 		    gits->BaseAddress, GITS_SIZE);
30335eeab52Sjmcneill 		return AE_OK;
30435eeab52Sjmcneill 	}
30535eeab52Sjmcneill 
3068b7fb0a6Sjmcneill 	aprint_normal_dev(sc->sc_gic.sc_dev, "ITS #%d at 0x%" PRIx64 "\n",
3078b7fb0a6Sjmcneill 	    gits->TranslationId, gits->BaseAddress);
30835eeab52Sjmcneill 
30935eeab52Sjmcneill 	gicv3_its_init(&sc->sc_gic, bsh, gits->BaseAddress, gits->TranslationId);
31035eeab52Sjmcneill 
31135eeab52Sjmcneill 	return AE_OK;
31235eeab52Sjmcneill }
31335eeab52Sjmcneill 
31435eeab52Sjmcneill static int
gicv3_acpi_map_msi(struct gicv3_acpi_softc * sc)31597755cb2Sjmcneill gicv3_acpi_map_msi(struct gicv3_acpi_softc *sc)
31635eeab52Sjmcneill {
31735eeab52Sjmcneill 	acpi_madt_walk(gicv3_acpi_map_gits, sc);
31897755cb2Sjmcneill 	acpi_madt_walk(gic_v2m_acpi_find_msi_frame, sc->sc_gic.sc_dev);
31935eeab52Sjmcneill 
32035eeab52Sjmcneill 	return 0;
32135eeab52Sjmcneill }
32297755cb2Sjmcneill 
323193d42ddSjmcneill #endif
324