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