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