1/* $NetBSD: gicv3_acpi.c,v 1.8 2020/12/23 11:05:08 jmcneill Exp $ */
2
3/*-
4 * Copyright (c) 2018 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Jared McNeill <jmcneill@invisible.ca>.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include "pci.h"
33
34#define	_INTR_PRIVATE
35
36#include <sys/cdefs.h>
37__KERNEL_RCSID(0, "$NetBSD: gicv3_acpi.c,v 1.8 2020/12/23 11:05:08 jmcneill Exp $");
38
39#include <sys/param.h>
40#include <sys/bus.h>
41#include <sys/cpu.h>
42#include <sys/kernel.h>
43#include <sys/device.h>
44#include <sys/kmem.h>
45
46#include <dev/acpi/acpireg.h>
47#include <dev/acpi/acpivar.h>
48
49#include <dev/fdt/fdtvar.h>
50
51#include <arm/cortex/gicv3.h>
52#include <arm/cortex/gicv3_its.h>
53#include <arm/cortex/gic_reg.h>
54
55#include <arm/acpi/gic_v2m_acpi.h>
56
57#define	GICD_SIZE	0x10000
58#define	GICR_SIZE	0x20000
59#define	GITS_SIZE	0x20000
60
61extern struct bus_space arm_generic_bs_tag;
62extern struct arm32_bus_dma_tag arm_generic_dma_tag;
63
64struct gicv3_acpi_softc {
65	struct gicv3_softc	sc_gic;
66
67	ACPI_MADT_GENERIC_DISTRIBUTOR *sc_madt_gicd;
68};
69
70static int	gicv3_acpi_match(device_t, cfdata_t, void *);
71static void	gicv3_acpi_attach(device_t, device_t, void *);
72
73static int	gicv3_acpi_map_dist(struct gicv3_acpi_softc *);
74static int	gicv3_acpi_map_redist(struct gicv3_acpi_softc *);
75#if NPCI > 0
76static int	gicv3_acpi_map_msi(struct gicv3_acpi_softc *);
77#endif
78
79CFATTACH_DECL_NEW(gicv3_acpi, sizeof(struct gicv3_acpi_softc), gicv3_acpi_match, gicv3_acpi_attach, NULL, NULL);
80
81static int
82gicv3_acpi_match(device_t parent, cfdata_t cf, void *aux)
83{
84	ACPI_SUBTABLE_HEADER *hdrp = aux;
85	ACPI_MADT_GENERIC_DISTRIBUTOR *gicd;
86
87	if (hdrp->Type != ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR)
88		return 0;
89
90	gicd = (ACPI_MADT_GENERIC_DISTRIBUTOR *)hdrp;
91
92	switch (gicd->Version) {
93	case ACPI_MADT_GIC_VERSION_NONE:
94		return __SHIFTOUT(reg_id_aa64pfr0_el1_read(), ID_AA64PFR0_EL1_GIC) != 0;
95	case ACPI_MADT_GIC_VERSION_V3:
96	case ACPI_MADT_GIC_VERSION_V4:
97		return 1;
98	default:
99		return 0;
100	}
101}
102
103static void
104gicv3_acpi_attach(device_t parent, device_t self, void *aux)
105{
106	struct gicv3_acpi_softc * const sc = device_private(self);
107	ACPI_MADT_GENERIC_DISTRIBUTOR *gicd = aux;
108	int error;
109
110	sc->sc_gic.sc_dev = self;
111	sc->sc_gic.sc_bst = &arm_generic_bs_tag;
112	sc->sc_gic.sc_dmat = &arm_generic_dma_tag;
113	sc->sc_madt_gicd = gicd;
114
115	aprint_naive("\n");
116	aprint_normal(": GICv3\n");
117
118	error = gicv3_acpi_map_dist(sc);
119	if (error) {
120		aprint_error_dev(self, "failed to map distributor: %d\n", error);
121		return;
122	}
123
124	error = gicv3_acpi_map_redist(sc);
125	if (error) {
126		aprint_error_dev(self, "failed to map redistributor: %d\n", error);
127		return;
128	}
129
130	error = gicv3_init(&sc->sc_gic);
131	if (error) {
132		aprint_error_dev(self, "failed to initialize GIC: %d\n", error);
133		return;
134	}
135
136#if NPCI > 0
137	gicv3_acpi_map_msi(sc);
138#endif
139
140	arm_fdt_irq_set_handler(gicv3_irq_handler);
141}
142
143static int
144gicv3_acpi_map_dist(struct gicv3_acpi_softc *sc)
145{
146	const bus_addr_t addr = sc->sc_madt_gicd->BaseAddress;
147	const bus_size_t size = GICD_SIZE;
148	int error;
149
150	error = bus_space_map(sc->sc_gic.sc_bst, addr, size, 0, &sc->sc_gic.sc_bsh_d);
151	if (error)
152		return error;
153
154	return 0;
155}
156
157static ACPI_STATUS
158gicv3_acpi_count_gicr(ACPI_SUBTABLE_HEADER *hdrp, void *aux)
159{
160	ACPI_MADT_GENERIC_REDISTRIBUTOR *gicr;
161	int *count = aux;
162
163	if (hdrp->Type == ACPI_MADT_TYPE_GENERIC_REDISTRIBUTOR) {
164		gicr = (ACPI_MADT_GENERIC_REDISTRIBUTOR *)hdrp;
165		*count += howmany(gicr->Length, GICR_SIZE);
166	}
167
168	return AE_OK;
169}
170
171static ACPI_STATUS
172gicv3_acpi_map_gicr(ACPI_SUBTABLE_HEADER *hdrp, void *aux)
173{
174	struct gicv3_acpi_softc * const sc = aux;
175	ACPI_MADT_GENERIC_REDISTRIBUTOR *gicr;
176	bus_space_handle_t bsh;
177	bus_size_t off;
178
179	if (hdrp->Type != ACPI_MADT_TYPE_GENERIC_REDISTRIBUTOR)
180		return AE_OK;
181
182	gicr = (ACPI_MADT_GENERIC_REDISTRIBUTOR *)hdrp;
183
184	if (bus_space_map(sc->sc_gic.sc_bst, gicr->BaseAddress, gicr->Length, 0, &bsh) != 0) {
185		aprint_error_dev(sc->sc_gic.sc_dev, "failed to map redistributor at 0x%" PRIx64 " len %#x\n",
186		    gicr->BaseAddress, gicr->Length);
187		return AE_OK;
188	}
189
190	for (off = 0; off < gicr->Length; off += GICR_SIZE) {
191		const int redist = sc->sc_gic.sc_bsh_r_count;
192		if (bus_space_subregion(sc->sc_gic.sc_bst, bsh, off, GICR_SIZE, &sc->sc_gic.sc_bsh_r[redist]) != 0) {
193			aprint_error_dev(sc->sc_gic.sc_dev, "couldn't subregion redistributor registers\n");
194			return AE_OK;
195		}
196
197		aprint_debug_dev(sc->sc_gic.sc_dev, "redist at 0x%" PRIx64 " [GICR]\n", gicr->BaseAddress + off);
198
199		sc->sc_gic.sc_bsh_r_count++;
200
201		/* If this is the last redist in this region, skip to the next one */
202		const uint32_t typer = bus_space_read_4(sc->sc_gic.sc_bst, sc->sc_gic.sc_bsh_r[redist], GICR_TYPER);
203		if (typer & GICR_TYPER_Last)
204			break;
205
206		/* If the redistributor supports virtual LPIs, skip the VLPI register region */
207		if (typer & GICR_TYPER_VLPIS)
208			off += GICR_SIZE;
209	}
210
211	return AE_OK;
212}
213
214static ACPI_STATUS
215gicv3_acpi_count_gicc(ACPI_SUBTABLE_HEADER *hdrp, void *aux)
216{
217	ACPI_MADT_GENERIC_INTERRUPT *gicc;
218	int *count = aux;
219
220	if (hdrp->Type == ACPI_MADT_TYPE_GENERIC_INTERRUPT) {
221		gicc = (ACPI_MADT_GENERIC_INTERRUPT *)hdrp;
222		if ((gicc->Flags & ACPI_MADT_ENABLED) != 0)
223			(*count)++;
224	}
225
226	return AE_OK;
227}
228
229static ACPI_STATUS
230gicv3_acpi_map_gicc(ACPI_SUBTABLE_HEADER *hdrp, void *aux)
231{
232	struct gicv3_acpi_softc * const sc = aux;
233	ACPI_MADT_GENERIC_INTERRUPT *gicc;
234
235	if (hdrp->Type != ACPI_MADT_TYPE_GENERIC_INTERRUPT)
236		return AE_OK;
237
238	gicc = (ACPI_MADT_GENERIC_INTERRUPT *)hdrp;
239	if ((gicc->Flags & ACPI_MADT_ENABLED) == 0)
240		return AE_OK;
241
242	const int redist = sc->sc_gic.sc_bsh_r_count;
243	if (bus_space_map(sc->sc_gic.sc_bst, gicc->GicrBaseAddress, GICR_SIZE, 0, &sc->sc_gic.sc_bsh_r[redist]) != 0) {
244		aprint_error_dev(sc->sc_gic.sc_dev, "failed to map redistributor at 0x%" PRIx64 " len %#x\n",
245		    gicc->GicrBaseAddress, GICR_SIZE);
246		return AE_OK;
247	}
248
249	aprint_debug_dev(sc->sc_gic.sc_dev, "redist at 0x%" PRIx64 " [GICC]\n", gicc->GicrBaseAddress);
250
251	sc->sc_gic.sc_bsh_r_count++;
252
253	return AE_OK;
254}
255
256static int
257gicv3_acpi_map_redist(struct gicv3_acpi_softc *sc)
258{
259	bool use_gicr = false;
260	int max_redist = 0;
261
262	/*
263	 * Try to use GICR structures to describe redistributors. If no GICR
264	 * subtables are found, use the GICR address from the GICC subtables.
265	 */
266	acpi_madt_walk(gicv3_acpi_count_gicr, &max_redist);
267	if (max_redist != 0)
268		use_gicr = true;
269	else
270		acpi_madt_walk(gicv3_acpi_count_gicc, &max_redist);
271
272	if (max_redist == 0)
273		return ENODEV;
274
275	sc->sc_gic.sc_bsh_r = kmem_alloc(sizeof(bus_space_handle_t) * max_redist, KM_SLEEP);
276	if (use_gicr)
277		acpi_madt_walk(gicv3_acpi_map_gicr, sc);
278	else
279		acpi_madt_walk(gicv3_acpi_map_gicc, sc);
280
281	if (sc->sc_gic.sc_bsh_r_count == 0)
282		return ENXIO;
283
284	return 0;
285}
286
287#if NPCI > 0
288static ACPI_STATUS
289gicv3_acpi_map_gits(ACPI_SUBTABLE_HEADER *hdrp, void *aux)
290{
291	struct gicv3_acpi_softc * const sc = aux;
292	ACPI_MADT_GENERIC_TRANSLATOR *gits;
293	bus_space_handle_t bsh;
294
295	if (hdrp->Type != ACPI_MADT_TYPE_GENERIC_TRANSLATOR)
296		return AE_OK;
297
298	gits = (ACPI_MADT_GENERIC_TRANSLATOR *)hdrp;
299
300	if (bus_space_map(sc->sc_gic.sc_bst, gits->BaseAddress, GITS_SIZE, 0, &bsh) != 0) {
301		aprint_error_dev(sc->sc_gic.sc_dev, "failed to map ITS at 0x%" PRIx64 " len %#x\n",
302		    gits->BaseAddress, GITS_SIZE);
303		return AE_OK;
304	}
305
306	aprint_normal_dev(sc->sc_gic.sc_dev, "ITS #%d at 0x%" PRIx64 "\n",
307	    gits->TranslationId, gits->BaseAddress);
308
309	gicv3_its_init(&sc->sc_gic, bsh, gits->BaseAddress, gits->TranslationId);
310
311	return AE_OK;
312}
313
314static int
315gicv3_acpi_map_msi(struct gicv3_acpi_softc *sc)
316{
317	acpi_madt_walk(gicv3_acpi_map_gits, sc);
318	acpi_madt_walk(gic_v2m_acpi_find_msi_frame, sc->sc_gic.sc_dev);
319
320	return 0;
321}
322
323#endif
324