From 6aba739f0542993635aabf7716a60972bf099076 Mon Sep 17 00:00:00 2001 From: Lee Lup Yuen Date: Wed, 16 Nov 2022 15:29:43 +0800 Subject: [PATCH] arch/arm64: Add support for Generic Interrupt Controller Version 2 Currently NuttX on Arm64 supports Generic Interrupt Controller (GIC) Versions 3 and 4: [`arm64_gicv3.c`](https://github.com/apache/incubator-nuttx/blob/master/arch/arm64/src/common/arm64_gicv3.c), [`arm64_gic.h`](https://github.com/apache/incubator-nuttx/blob/master/arch/arm64/src/common/arm64_gic.h). This PR adds support for GIC Version 2, which is needed by [Pine64 PinePhone](https://lupyuen.github.io/articles/interrupt) based on Allwinner A64 SoC. This 64-bit implementation of GIC v2 is mostly identical to the existing GIC v2 for 32-bit Armv7-A ([`armv7-a/arm_gicv2.c`](https://github.com/apache/incubator-nuttx/blob/master/arch/arm/src/armv7-a/arm_gicv2.c), [`armv7-a/gic.h`](https://github.com/apache/incubator-nuttx/blob/master/arch/arm/src/armv7-a/gic.h)), with minor modifications to support 64-bit Registers (Interrupt Context). - `arch/arm64/Kconfig`: Under "ARM64 Options", we added an integer option `ARM_GIC_VERSION` ("GIC version") that selects the GIC Version. Valid values are 2, 3 and 4, default is 3. - `arch/arm64/src/common/arm64_gicv2.c`: Implements 64-bit GIC v2 based on 32-bit [`armv7-a/arm_gicv2.c`](https://github.com/apache/incubator-nuttx/blob/master/arch/arm/src/armv7-a/arm_gicv2.c) and [`armv7-a/gic.h`](https://github.com/apache/incubator-nuttx/blob/master/arch/arm/src/armv7-a/gic.h), modified to support 64-bit Registers (Interrupt Context). Function and Macro Names have not been changed, for easier cross-referencing between the 32-bit and 64-bit implementations of GIC v2. - `arch/arm64/src/common/arm64_gicv3.c`: Added Conditional Compilation for GIC v3. This file will not be compiled if `ARM_GIC_VERSION` is 2. - `arch/arm64/src/common/arm64_gic.h`: Added the Version Identifier for GIC v2. At startup we read the GIC Version from hardware and verify that it matches `ARM_GIC_VERSION`. - `arch/arm64/include/qemu/chip.h`: Added the QEMU Base Addresses for GIC v2. - `arch/arm64/src/common/Make.defs`: Added the source file that implements GIC v2. - `boards/arm64/qemu/qemu-armv8a/README.txt`: Added the documentation for testing GIC v2 with QEMU. - `boards/arm64/qemu/qemu-armv8a/configs/nsh_gicv2/defconfig`: Added the Board Configuration `qemu-armv8a:nsh_gicv2` for testing GIC v2 with QEMU. Identical to `qemu-armv8a:nsh`, except that `ARM_GIC_VERSION` is 2. --- arch/arm64/Kconfig | 8 + arch/arm64/include/qemu/chip.h | 13 + arch/arm64/src/common/Make.defs | 2 +- arch/arm64/src/common/arm64_gic.h | 1 + arch/arm64/src/common/arm64_gicv2.c | 1389 +++++++++++++++++ arch/arm64/src/common/arm64_gicv3.c | 4 + boards/arm64/qemu/qemu-armv8a/README.txt | 21 +- .../qemu-armv8a/configs/nsh_gicv2/defconfig | 66 + 8 files changed, 1500 insertions(+), 4 deletions(-) create mode 100644 arch/arm64/src/common/arm64_gicv2.c create mode 100644 boards/arm64/qemu/qemu-armv8a/configs/nsh_gicv2/defconfig diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 483f3f6073..cb74385bf4 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -95,6 +95,14 @@ config ARM_HAVE_NEON ---help--- Decide whether support NEON instruction +config ARM_GIC_VERSION + int "GIC version" + default 3 + range 2 4 + ---help--- + Version of Generic Interrupt Controller (GIC) supported by the + architecture + if ARCH_CHIP_QEMU source "arch/arm64/src/qemu/Kconfig" endif diff --git a/arch/arm64/include/qemu/chip.h b/arch/arm64/include/qemu/chip.h index 745876852b..6b6cae7521 100644 --- a/arch/arm64/include/qemu/chip.h +++ b/arch/arm64/include/qemu/chip.h @@ -35,9 +35,22 @@ #if defined(CONFIG_ARCH_CHIP_QEMU) +#if CONFIG_ARM_GIC_VERSION == 2 + +#define CONFIG_GICD_BASE 0x8000000 +#define CONFIG_GICR_BASE 0x8010000 + +#elif CONFIG_ARM_GIC_VERSION == 3 || CONFIG_ARM_GIC_VERSION == 4 + #define CONFIG_GICD_BASE 0x8000000 #define CONFIG_GICR_BASE 0x80a0000 +#else + +#error CONFIG_ARM_GIC_VERSION should be 2, 3 or 4 + +#endif /* CONFIG_ARM_GIC_VERSION */ + #define CONFIG_RAMBANK1_ADDR 0x40000000 #define CONFIG_RAMBANK1_SIZE MB(128) diff --git a/arch/arm64/src/common/Make.defs b/arch/arm64/src/common/Make.defs index 4e4b1a3a76..515b85f793 100644 --- a/arch/arm64/src/common/Make.defs +++ b/arch/arm64/src/common/Make.defs @@ -52,7 +52,7 @@ CMN_CSRCS += arm64_sigdeliver.c # Common C source files ( hardware BSP ) CMN_CSRCS += arm64_mmu.c arm64_arch_timer.c arm64_cache.c -CMN_CSRCS += arm64_doirq.c arm64_gicv3.c arm64_fatal.c +CMN_CSRCS += arm64_doirq.c arm64_gicv2.c arm64_gicv3.c arm64_fatal.c CMN_CSRCS += arm64_syscall.c arm64_cpu_psci.c # Use common heap allocation for now (may need to be customized later) diff --git a/arch/arm64/src/common/arm64_gic.h b/arch/arm64/src/common/arm64_gic.h index 496fa944a6..73fa4b4b68 100644 --- a/arch/arm64/src/common/arm64_gic.h +++ b/arch/arm64/src/common/arm64_gic.h @@ -99,6 +99,7 @@ * [3:0] - IMPLEMENTATION DEFINED. */ #define GICD_PIDR2_ARCH_MASK 0xf0 +#define GICD_PIDR2_ARCH_GICV2 0x20 #define GICD_PIDR2_ARCH_GICV3 0x30 #define GICD_PIDR2_ARCH_GICV4 0x40 diff --git a/arch/arm64/src/common/arm64_gicv2.c b/arch/arm64/src/common/arm64_gicv2.c new file mode 100644 index 0000000000..7e1ce03e14 --- /dev/null +++ b/arch/arm64/src/common/arm64_gicv2.c @@ -0,0 +1,1389 @@ +/**************************************************************************** + * arch/arm64/src/common/arm64_gicv2.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/* Reference: + * Cortex™-A9 MPCore, Revision: r4p1, Technical Reference Manual, ARM DDI + * 0407I (ID091612). + * + * Includes some removed registers from the r2p2 version as well. ARM DDI + * 0407F (ID050110) + */ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include "arm64_internal.h" +#include "arm64_gic.h" + +#if CONFIG_ARM_GIC_VERSION == 2 + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* GIC Distributor / Redistributor Register Interface Base Addresses */ + +#define MPCORE_ICD_VBASE CONFIG_GICD_BASE +#define MPCORE_ICC_VBASE CONFIG_GICR_BASE + +/* Generic indexing helpers *************************************************/ + +/* 1x32 bit field per register */ + +#define GIC_INDEX1(n) (n) /* 1 field per word */ +#define GIC_OFFSET1(n) (GIC_INDEX1(n) << 2) /* 32-bit word offset */ +#define GIC_SHIFT1(n) (0) /* No shift */ +#define GIC_MASK1(n) (0xffffffff) /* Whole word */ + +/* 2x16 bit field per register */ + +#define GIC_INDEX2(n) (n >> 1) /* 2 fields per word */ +#define GIC_OFFSET2(n) (GIC_INDEX2(n) << 2) /* 32-bit word offset */ +#define GIC_SHIFT2(n) (((n) & 1) << 4) /* Shift 16-bits per field */ +#define GIC_MASK2(n) (0xffff << GIC_SHIFT2(n)) /* 16-bit mask */ + +/* 4x8 bit field per register */ + +#define GIC_INDEX4(n) (n >> 2) /* 4 fields per word */ +#define GIC_OFFSET4(n) (GIC_INDEX4(n) << 2) /* 32-bit word offset */ +#define GIC_SHIFT4(n) (((n) & 3) << 3) /* Shift 8-bits per field */ +#define GIC_MASK4(n) (0xff << GIC_SHIFT4(n)) /* 8-bit mask */ + +/* 8x4 bit field per register */ + +#define GIC_INDEX8(n) (n >> 3) /* 8 fields per word */ +#define GIC_OFFSET8(n) (GIC_INDEX8(n) << 2) /* 32-bit word offset */ +#define GIC_SHIFT8(n) (((n) & 7) << 2) /* Shift 4-bits per field */ +#define GIC_MASK8(n) (15 << GIC_SHIFT8(n)) /* 4-bit mask */ + +/* 16x2 bit field per register */ + +#define GIC_INDEX16(n) (n >> 4) /* 16 fields per word */ +#define GIC_OFFSET16(n) (GIC_INDEX16(n) << 2) /* 32-bit word offset */ +#define GIC_SHIFT16(n) (((n) & 15) << 1) /* Shift 2-bits per field */ +#define GIC_MASK16(n) (3 << GIC_SHIFT16(n)) /* 2-bit mask */ + +/* 32x1 bit field per register */ + +#define GIC_INDEX32(n) (n >> 5) /* 32 fields per word */ +#define GIC_OFFSET32(n) (GIC_INDEX32(n) << 2) /* 32-bit word offset */ +#define GIC_SHIFT32(n) ((n) & 31) /* Shift 1-bit per field */ +#define GIC_MASK32(n) (1 << GIC_SHIFT32(n)) /* 1-bit mask */ + +/* GIC Register Offsets *****************************************************/ + +/* CPU Interface registers */ + +#define GIC_ICCICR_OFFSET 0x0000 /* CPU Interface Control Register */ +#define GIC_ICCPMR_OFFSET 0x0004 /* Interrupt Priority Mask Register */ +#define GIC_ICCBPR_OFFSET 0x0008 /* Binary point Register */ +#define GIC_ICCIAR_OFFSET 0x000c /* Interrupt Acknowledge */ +#define GIC_ICCEOIR_OFFSET 0x0010 /* End of interrupt */ +#define GIC_ICCRPR_OFFSET 0x0014 /* Running interrupt */ +#define GIC_ICCHPIR_OFFSET 0x0018 /* Highest pending interrupt */ +#define GIC_ICCABPR_OFFSET 0x001c /* Aliased Non-secure Binary Point Register */ +#define GIC_ICCAIAR_OFFSET 0x0020 /* Aliased Interrupt Acknowledge Register */ +#define GIC_ICCAEOIR_OFFSET 0x0024 /* Aliased End of Interrupt Register */ +#define GIC_ICCAHPIR_OFFSET 0x0028 /* Aliased Highest Priority Pending Interrupt Register */ + /* 0x002c-0x003c: Reserved */ + /* 0x0040-0x00cf: Implementation defined */ +#define GIC_ICCAPR1_OFFSET 0x00d0 /* Active Priorities Register 1 */ +#define GIC_ICCAPR2_OFFSET 0x00d4 /* Active Priorities Register 2 */ +#define GIC_ICCAPR3_OFFSET 0x00d8 /* Active Priorities Register 3 */ +#define GIC_ICCAPR4_OFFSET 0x00dc /* Active Priorities Register 4 */ +#define GIC_ICCNSAPR1_OFFSET 0x00e0 /* Non-secure Active Priorities Register 1 */ +#define GIC_ICCNSAPR2_OFFSET 0x00e4 /* Non-secure Active Priorities Register 2 */ +#define GIC_ICCNSAPR3_OFFSET 0x00e8 /* Non-secure Active Priorities Register 3 */ +#define GIC_ICCNSAPR4_OFFSET 0x00ec /* Non-secure Active Priorities Register 4 */ + /* 0x00ed-0x00f8: Reserved */ +#define GIC_ICCIDR_OFFSET 0x00fc /* CPU Interface Implementer ID Register */ +#define GIC_ICCDIR_OFFSET 0x1000 /* Deactivate Interrupt Register */ + +/* Distributor Registers */ + +#define GIC_ICDDCR_OFFSET 0x0000 /* Distributor Control Register */ +#define GIC_ICDICTR_OFFSET 0x0004 /* Interrupt Controller Type Register */ +#define GIC_ICDIIDR_OFFSET 0x0008 /* Distributor Implementer ID Register */ + /* 0x000c-0x001c: Reserved */ + /* 0x0020-0x003c: Implementation defined */ + /* 0x0040-0x007c: Reserved */ + +/* Interrupt Security Registers: 0x0080-0x009c */ + +#define GIC_ICDISR_OFFSET(n) (0x0080 + GIC_OFFSET32(n)) + +/* Interrupt Set-Enable Registers: 0x0100-0x011c */ + +#define GIC_ICDISER_OFFSET(n) (0x0100 + GIC_OFFSET32(n)) + +/* Interrupt Clear-Enable Registers: 0x0180-0x019c */ + +#define GIC_ICDICER_OFFSET(n) (0x0180 + GIC_OFFSET32(n)) + +/* Interrupt Set-Pending Registers: 0x0200-0x027c */ + +#define GIC_ICDISPR_OFFSET(n) (0x0200 + GIC_OFFSET32(n)) + +/* Interrupt Clear-Pending Registers: 0x0280-0x02fc */ + +#define GIC_ICDICPR_OFFSET(n) (0x0280 + GIC_OFFSET32(n)) + +/* GICv2 Interrupt Set-Active Registers: 0x0300-0x31c */ + +#define GIC_ICDSAR_OFFSET(n) (0x0300 + GIC_OFFSET32(n)) + +/* Interrupt Clear-Active Registers: 0x380-0x3fc */ + +#define GIC_ICDCAR_OFFSET(n) (0x0380 + GIC_OFFSET32(n)) + +/* Interrupt Priority Registers: 0x0400-0x04fc */ + +#define GIC_ICDIPR_OFFSET(n) (0x0400 + GIC_OFFSET4(n)) + +/* 0x0500-0x07fc: Reserved */ + +/* Interrupt Processor Target Registers: 0x0800-0x08fc */ + +#define GIC_ICDIPTR_OFFSET(n) (0x0800 + GIC_OFFSET4(n)) + +/* 0x0900-0x0bfc: Reserved */ + +/* Interrupt Configuration Registers: 0x0c00-0x0c3c */ + +#define GIC_ICDICFR_OFFSET(n) (0x0c00 + GIC_OFFSET16(n)) + +/* 0x0d00-0x0dfc: Implementation defined */ + +/* PPI Status Register: 0x0d00 */ + +/* SPI Status Registers: 0x0d04-0x0d1c */ + +#define GIC_ICDPPISR_OFFSET 0x0d00 /* PPI Status Register */ +#define GIC_ICDSPISR_OFFSET(n) (0x0d00 + GIC_OFFSET32(n)) + +/* 0x0d80-0x0dfc: Reserved */ + +/* Non-secure Access Control Registers, optional: 00xe00-0x0efc */ + +#define GIC_ICDNSACR_OFFSET(n) (0x0e00 + GIC_OFFSET32(n)) + +/* Software Generated Interrupt Register: 0x0f00 */ + +#define GIC_ICDSGIR_OFFSET 0x0f00 /* Software Generated Interrupt Register */ + +/* 0x0f0c-0x0f0c: Reserved */ + +/* Peripheral Identification Registers: 0x0fd0-0xfe8 */ + +#define GIC_ICDPIDR_OFFSET(n) (0x0fd0 + ((n) << 2)) + +/* SGI Clear-Pending Registers: 0x0f10-0x0f1c */ + +#define GIC_ICDSCPR_OFFSET(n) (0x0f10 + GIC_OFFSET8(n)) + +/* SGI Set-Pending Registers: 0x0f20-0x0f2c */ + +#define GIC_ICDSSPR_OFFSET(n) (0x0f20 + GIC_OFFSET8(n)) + +/* 0x0f30-0x0fcc: Reserved */ + +/* 0x0fd0-0x0ffc: Implementation defined */ + +/* Component Identification Registers: 0x0ff0-0x0ffc */ + +#define GIC_ICDCIDR_OFFSET(n) (0x0ff0 + ((n) << 2)) + +/* 0x0f04-0x0ffc: Reserved */ + +/* GIC Register Addresses ***************************************************/ + +/* The Interrupt Controller is a single functional unit that is located in a + * Cortex-A9 MPCore design. There is one interrupt interface per Cortex-A9 + * processor. Registers are memory mapped and accessed through a chip- + * specific private memory spaced (see mpcore.h). + */ + +/* CPU Interface registers */ + +#define GIC_ICCICR (MPCORE_ICC_VBASE+GIC_ICCICR_OFFSET) +#define GIC_ICCPMR (MPCORE_ICC_VBASE+GIC_ICCPMR_OFFSET) +#define GIC_ICCBPR (MPCORE_ICC_VBASE+GIC_ICCBPR_OFFSET) +#define GIC_ICCIAR (MPCORE_ICC_VBASE+GIC_ICCIAR_OFFSET) +#define GIC_ICCEOIR (MPCORE_ICC_VBASE+GIC_ICCEOIR_OFFSET) +#define GIC_ICCRPR (MPCORE_ICC_VBASE+GIC_ICCRPR_OFFSET) +#define GIC_ICCHPIR (MPCORE_ICC_VBASE+GIC_ICCHPIR_OFFSET) +#define GIC_ICCABPR (MPCORE_ICC_VBASE+GIC_ICCABPR_OFFSET) +#define GIC_ICCAIAR (MPCORE_ICC_VBASE+GIC_ICCAIAR_OFFSET) +#define GIC_ICCAEOIR (MPCORE_ICC_VBASE+GIC_ICCAEOIR_OFFSET) +#define GIC_ICCAHPIR (MPCORE_ICC_VBASE+GIC_ICCAHPIR_OFFSET) +#define GIC_ICCAPR1 (MPCORE_ICC_VBASE+GIC_ICCAPR1_OFFSET) +#define GIC_ICCAPR2 (MPCORE_ICC_VBASE+GIC_ICCAPR2_OFFSET) +#define GIC_ICCAPR3 (MPCORE_ICC_VBASE+GIC_ICCAPR3_OFFSET) +#define GIC_ICCAPR4 (MPCORE_ICC_VBASE+GIC_ICCAPR4_OFFSET) +#define GIC_ICCNSAPR1 (MPCORE_ICC_VBASE+GIC_ICCNSAPR1_OFFSET) +#define GIC_ICCNSAPR2 (MPCORE_ICC_VBASE+GIC_ICCNSAPR2_OFFSET) +#define GIC_ICCNSAPR3 (MPCORE_ICC_VBASE+GIC_ICCNSAPR3_OFFSET) +#define GIC_ICCNSAPR4 (MPCORE_ICC_VBASE+GIC_ICCNSAPR4_OFFSET) +#define GIC_ICCIDR (MPCORE_ICC_VBASE+GIC_ICCIDR_OFFSET) +#define GIC_ICCDIR (MPCORE_ICC_VBASE+GIC_ICCDIR_OFFSET) + +/* Distributor Registers */ + +#define GIC_ICDDCR (MPCORE_ICD_VBASE+GIC_ICDDCR_OFFSET) +#define GIC_ICDICTR (MPCORE_ICD_VBASE+GIC_ICDICTR_OFFSET) +#define GIC_ICDIIDR (MPCORE_ICD_VBASE+GIC_ICDIIDR_OFFSET) +#define GIC_ICDISR(n) (MPCORE_ICD_VBASE+GIC_ICDISR_OFFSET(n)) +#define GIC_ICDISER(n) (MPCORE_ICD_VBASE+GIC_ICDISER_OFFSET(n)) +#define GIC_ICDICER(n) (MPCORE_ICD_VBASE+GIC_ICDICER_OFFSET(n)) +#define GIC_ICDISPR(n) (MPCORE_ICD_VBASE+GIC_ICDISPR_OFFSET(n)) +#define GIC_ICDICPR(n) (MPCORE_ICD_VBASE+GIC_ICDICPR_OFFSET(n)) +#define GIC_ICDSAR(n) (MPCORE_ICD_VBASE+GIC_ICDSAR_OFFSET(n)) +#define GIC_ICDCAR(n) (MPCORE_ICD_VBASE+GIC_ICDCAR_OFFSET(n)) +#define GIC_ICDIPR(n) (MPCORE_ICD_VBASE+GIC_ICDIPR_OFFSET(n)) +#define GIC_ICDIPTR(n) (MPCORE_ICD_VBASE+GIC_ICDIPTR_OFFSET(n)) +#define GIC_ICDICFR(n) (MPCORE_ICD_VBASE+GIC_ICDICFR_OFFSET(n)) +#define GIC_ICDPPISR (MPCORE_ICD_VBASE+GIC_ICDPPISR_OFFSET) +#define GIC_ICDSPISR(n) (MPCORE_ICD_VBASE+GIC_ICDSPISR_OFFSET(n)) +#define GIC_ICDNSACR(n) (MPCORE_ICD_VBASE+GIC_ICDNSACR_OFFSET(n)) +#define GIC_ICDSGIR (MPCORE_ICD_VBASE+GIC_ICDSGIR_OFFSET) +#define GIC_ICDPIDR(n) (MPCORE_ICD_VBASE+GIC_ICDPIDR_OFFSET(n)) +#define GIC_ICDSCPR(n) (MPCORE_ICD_VBASE+GIC_ICDSCPR_OFFSET(n)) +#define GIC_ICDSSPR(n) (MPCORE_ICD_VBASE+GIC_ICDSSPR_OFFSET(n)) +#define GIC_ICDCIDR(n) (MPCORE_ICD_VBASE+GIC_ICDCIDR_OFFSET(n)) + +/* GIC Register Bit Definitions *********************************************/ + +/* CPU Interface registers */ + +/* CPU Interface Control Register -- without security extensions */ + +#define GIC_ICCICR_ENABLE (1 << 0) /* Bit 0: Enable the CPU interface for this GIC */ + /* Bits 1-31: Reserved */ + +/* CPU Interface Control Register -- with security extensions, + * non-secure copy + */ + +#define GIC_ICCICRU_ENABLEGRP1 (1 << 0) /* Bit 0: Enable Group 1 interrupts for the CPU */ + /* Bits 1-4: Reserved */ +#define GIC_ICCICRU_FIQBYPDISGRP1 (1 << 5) /* Bit 5: FIQ disabled for CPU Group 1*/ +#define GIC_ICCICRU_IRQBYPDISGRP1 (1 << 6) /* Bit 6: IRQ disabled for CPU Group 1*/ + /* Bits 7-8: Reserved */ +#define GIC_ICCICRU_EOIMODENS (1 << 9) /* Bit 9: Control EIOIR access (non-secure) */ + /* Bits 10-31: Reserved */ + +/* CPU Interface Control Register -- with security extensions, + * secure copy + */ + +#define GIC_ICCICRS_ENABLEGRP0 (1 << 0) /* Bit 0: Enable Group 0 interrupts for the CPU */ +#define GIC_ICCICRS_ENABLEGRP1 (1 << 1) /* Bit 1: Enable Group 1 interrupts for the CPU */ +#define GIC_ICCICRS_ACKTCTL (1 << 2) /* Bit 2: Group 1 interrupt activation control */ +#define GIC_ICCICRS_FIQEN (1 << 3) /* Bit 3: Signal Group 0 via FIQ */ +#define GIC_ICCICRS_CBPR (1 << 4) /* Bit 4: Control Group 0/1 Pre-emption */ +#define GIC_ICCICRS_FIQBYPDISGRP0 (1 << 5) /* Bit 5: FIQ disabled for CPU Group 0 */ +#define GIC_ICCICRS_IRQBYPDISGRP0 (1 << 6) /* Bit 6: IRQ disabled for CPU Group 0 */ +#define GIC_ICCICRS_FIQBYPDISGRP1 (1 << 7) /* Bit 5: FIQ disabled for CPU Group 1 */ +#define GIC_ICCICRS_IRQBYPDISGRP1 (1 << 8) /* Bit 6: IRQ disabled for CPU Group 1 */ +#define GIC_ICCICRS_EOIMODES (1 << 9) /* Bit 6: Control EIOIR access (secure) */ +#define GIC_ICCICRS_EOIMODENS (1 << 10) /* Bit 10: Control EIOIR access (non-secure) */ + /* Bits 11-31: Reserved */ + +/* Interrupt Priority Mask Register. Priority values are 8-bit unsigned + * binary. A GIC supports a minimum of 16 and a maximum of 256 priority + * levels. As a result, PMR settings make sense. + */ + +#define GIC_ICCPMR_SHIFT (0) /* Bits 0-7: Priority mask */ +#define GIC_ICCPMR_MASK (0xff << GIC_ICCPMR_SHIFT) +# define GIC_ICCPMR_VALUE(n) ((uint32_t)(n) << GIC_ICCPMR_SHIFT) + /* Bits 8-31: Reserved */ + +/* Binary point Register and Aliased Non-secure Binary Point Register. + * Priority values are 8-bit unsigned binary. A GIC supports a minimum of + * 16 and a maximum of 256 priority levels. As a result, not all binary + * point settings make sense. + */ + +#define GIC_ICCBPR_SHIFT (0) /* Bits 0-2: Binary point */ +#define GIC_ICCBPR_MASK (7 << GIC_ICCBPR_SHIFT) +# define GIC_ICCBPR_1_7 (0 << GIC_ICCBPR_SHIFT) /* Priority bits [7:1] compared for pre-emption */ +# define GIC_ICCBPR_2_7 (1 << GIC_ICCBPR_SHIFT) /* Priority bits [7:2] compared for pre-emption */ +# define GIC_ICCBPR_3_7 (2 << GIC_ICCBPR_SHIFT) /* Priority bits [7:2] compared for pre-emption */ +# define GIC_ICCBPR_4_7 (3 << GIC_ICCBPR_SHIFT) /* Priority bits [7:2] compared for pre-emption */ +# define GIC_ICCBPR_5_7 (4 << GIC_ICCBPR_SHIFT) /* Priority bits [7:5] compared for pre-emption */ +# define GIC_ICCBPR_6_7 (5 << GIC_ICCBPR_SHIFT) /* Priority bits [7:6] compared for pre-emption */ +# define GIC_ICCBPR_7_7 (6 << GIC_ICCBPR_SHIFT) /* Priority bit [7] compared for pre-emption */ +# define GIC_ICCBPR_NOPREMPT (7 << GIC_ICCBPR_SHIFT) /* No pre-emption is performed */ + + /* Bits 3-31: Reserved */ + +/* Interrupt Acknowledge Register */ + +#define GIC_ICCIAR_INTID_SHIFT (0) /* Bits 0-9: Interrupt ID */ +#define GIC_ICCIAR_INTID_MASK (0x3ff << GIC_ICCIAR_INTID_SHIFT) +# define GIC_ICCIAR_INTID(n) ((uint32_t)(n) << GIC_ICCIAR_INTID_SHIFT) +#define GIC_ICCIAR_CPUSRC_SHIFT (10) /* Bits 10-12: CPU source ID */ +#define GIC_ICCIAR_CPUSRC_MASK (7 << GIC_ICCIAR_CPUSRC_SHIFT) +# define GIC_ICCIAR_CPUSRC(n) ((uint32_t)(n) << GIC_ICCIAR_CPUSRC_SHIFT) + + /* Bits 13-31: Reserved */ + +/* End of Interrupt Register */ + +#define GIC_ICCEOIR_SPURIOUS (0x3ff) + +#define GIC_ICCEOIR_INTID_SHIFT (0) /* Bits 0-9: Interrupt ID */ +#define GIC_ICCEOIR_INTID_MASK (0x3ff << GIC_ICCEOIR_INTID_SHIFT) +# define GIC_ICCEOIR_INTID(n) ((uint32_t)(n) << GIC_ICCEOIR_INTID_SHIFT) +#define GIC_ICCEOIR_CPUSRC_SHIFT (10) /* Bits 10-12: CPU source ID */ +#define GIC_ICCEOIR_CPUSRC_MASK (7 << GIC_ICCEOIR_CPUSRC_SHIFT) +# define GIC_ICCEOIR_CPUSRC(n) ((uint32_t)(n) << GIC_ICCEOIR_CPUSRC_SHIFT) + + /* Bits 13-31: Reserved */ + +/* Running Interrupt Register */ + + /* Bits 0-3: Reserved */ +#define GIC_ICCRPR_PRIO_SHIFT (4) /* Bits 4-7: Priority mask */ +#define GIC_ICCRPR_PRIO_MASK (15 << GIC_ICCRPR_PRIO_SHIFT) +# define GIC_ICCRPR_PRIO_VALUE(n) ((uint32_t)(n) << GIC_ICCRPR_PRIO_SHIFT) + + /* Bits 8-31: Reserved */ + +/* Highest Pending Interrupt Register */ + +#define GIC_ICCHPIR_INTID_SHIFT (0) /* Bits 0-9: Interrupt ID */ +#define GIC_ICCHPIR_INTID_MASK (0x3ff << GIC_ICCHPIR_INTID_SHIFT) +# define GIC_ICCHPIR_INTID(n) ((uint32_t)(n) << GIC_ICCHPIR_INTID_SHIFT) +#define GIC_ICCHPIR_CPUSRC_SHIFT (10) /* Bits 10-12: CPU source ID */ +#define GIC_ICCHPIR_CPUSRC_MASK (7 << GIC_ICCHPIR_CPUSRC_SHIFT) +# define GIC_ICCHPIR_CPUSRC(n) ((uint32_t)(n) << GIC_ICCHPIR_CPUSRC_SHIFT) + + /* Bits 13-31: Reserved */ + +/* Aliased Interrupt Acknowledge Register */ +#define GIC_ICCAIAR_ + +/* Aliased End of Interrupt Register */ +#define GIC_ICCAEOIR_ + +/* Aliased Highest Priority Pending Interrupt Register */ +#define GIC_ICCAHPIR_ + +/* Active Priorities Register 1 */ +#define GIC_ICCAPR1_ + +/* Active Priorities Register 2 */ +#define GIC_ICCAPR2_ + +/* Active Priorities Register 3 */ +#define GIC_ICCAPR3_ + +/* Active Priorities Register 4 */ +#define GIC_ICCAPR4_ + +/* Non-secure Active Priorities Register 1 */ +#define GIC_ICCNSAPR1_ + +/* Non-secure Active Priorities Register 2 */ +#define GIC_ICCNSAPR2_ + +/* Non-secure Active Priorities Register 3 */ +#define GIC_ICCNSAPR3_ + +/* Non-secure Active Priorities Register 4 */ +#define GIC_ICCNSAPR4_ + +/* CPU Interface Implementer ID Register */ + +#define GIC_ICCIDR_IMPL_SHIFT (0) /* Bits 0-11: Implementer */ +#define GIC_ICCIDR_IMPL_MASK (0xfff << GIC_ICCIDR_IMPL_SHIFT) +#define GIC_ICCIDR_REVISION_SHIFT (12) /* Bits 12-15: Revision number */ +#define GIC_ICCIDR_REVISION_MASK (15 << GIC_ICCIDR_REVISION_SHIFT) +#define GIC_ICCIDR_ARCHNO_SHIFT (16) /* Bits 16-19: Architecture number */ +#define GIC_ICCIDR_ARCHNO_MASK (15 << GIC_ICCIDR_ARCHNO_SHIFT) +#define GIC_ICCIDR_PARTNO_SHIFT (20) /* Bits 20-31: Part number */ +#define GIC_ICCIDR_PARTNO_MASK (0xfff << GIC_ICCIDR_PARTNO_SHIFT) + +/* Deactivate Interrupt Register */ +#define GIC_ICCDIR_ + +/* Distributor Registers */ + +/* Distributor Control Register -- without security extensions */ + +#define GIC_ICDDCR_ENABLE (1 << 0) /* Bit 0: Enable forwarding of interrupts */ + /* Bits 1-31: Reserved */ + +/* Distributor Control Register -- with security extensions */ + +#define GIC_ICDDCR_ENABLEGRP0 (1 << 0) /* Bit 0: Enable forwarding of Group 0 interrupts */ +#define GIC_ICDDCR_ENABLEGRP1 (1 << 1) /* Bit 1: Enable forwarding of Group 1 interrupts */ + /* Bits 2-31: Reserved */ + +/* Interrupt Controller Type Register */ + +#define GIC_ICDICTR_ITLINES_SHIFT (0) /* Bits 0-4: It lines number */ +#define GIC_ICDICTR_ITLINES_MASK (0x1f << GIC_ICDICTR_ITLINES_SHIFT) +#define GIC_ICDICTR_CPUNO_SHIFT (5) /* Bits 5-7: CPU number */ +#define GIC_ICDICTR_CPUNO_MASK (7 << GIC_ICDICTR_CPUNO_SHIFT) + /* Bits 8-9: Reserved */ +#define GIC_ICDICTR_SECEXTNS (1 << 10) /* Bit 10: Number of security domains */ +#define GIC_ICDICTR_LSPI_SHIFT (11) /* Bits 11-15: Number of Lockable Shared Peripheral Interrupts */ +#define GIC_ICDICTR_LSPI_MASK (0x1f << GIC_ICDICTR_LSPI_SHIFT) + /* Bits 16-31: Reserved */ + +/* Distributor Implementer ID Register */ + +#define GIC_ICDIIDR_IMPL_SHIFT (0) /* Bits 0-11: Implementer */ +#define GIC_ICDIIDR_IMPL_MASK (0xfff << GIC_ICDIIDR_IMPL_SHIFT) +#define GIC_ICDIIDR_REVISION_SHIFT (12) /* Bits 12-23: Revision number */ +#define GIC_ICDIIDR_REVISION_MASK (0xfff << GIC_ICDIIDR_REVISION_SHIFT) +#define GIC_ICDIIDR_VERSION_SHIFT (24) /* Bits 24-31: Iimplementer version */ +#define GIC_ICDIIDR_VERSION_MASK (0xff << GIC_ICDIIDR_VERSION_SHIFT) + +/* Interrupt Security Registers: 0x0080-0x009c */ + +#define GIC_ICDISR_INT(n) GIC_MASK32(n) + +/* Interrupt Set-Enable. + * + * NOTE: + * In the Cortex-A9 MPCore, SGIs are always enabled. The corresponding bits + * in the ICDISERn are read as one, write ignored + */ + +#define GIC_ICDISER_INT(n) GIC_MASK32(n) + +/* Interrupt Clear-Enable. + * + * NOTE: + * In the Cortex-A9 MPCore, SGIs are always enabled. The corresponding bits + * in the ICDICERn are read as one, write ignored + */ + +#define GIC_ICDICER_INT(n) GIC_MASK32(n) + +/* Interrupt Set-Pending */ + +#define GIC_ICDISPR_INT(n) GIC_MASK32(n) + +/* Interrupt Clear-Pending */ + +#define GIC_ICDICPR_INT(n) GIC_MASK32(n) + +/* GICv2 Interrupt Set-Active Registers */ + +#define GIC_ICDSAR_INT(n) GIC_MASK32(n) + +/* Interrupt Clear-Active Registers */ + +#define GIC_ICDCAR_INT(n) GIC_MASK32(n) + +/* Interrupt Priority Registers */ + +#define GIC_ICDIPR_ID_SHIFT(n) GIC_SHIFT4(n) +#define GIC_ICDIPR_ID_MASK(n) GIC_MASK4(n) +# define GIC_ICDIPR_ID(n,p) ((uint32_t)(p) << GIC_SHIFT4(n)) + +/* Interrupt Processor Target Registers */ + +#define CPU0_TARGET (1 << 0) +#define CPU1_TARGET (1 << 1) +#define CPU2_TARGET (1 << 2) +#define CPU3_TARGET (1 << 3) + +#define GIC_ICDIPTR_ID_SHIFT(n) GIC_SHIFT4(n) +#define GIC_ICDIPTR_ID_MASK(n) GIC_MASK4(n) +# define GIC_ICDIPTR_ID(n,t) ((uint32_t)(t) <> GIC_ICDICTR_ITLINES_SHIFT; + return (field + 1) << 5; +} + +/**************************************************************************** + * Name: arm_cpu_sgi + * + * Description: + * Perform a Software Generated Interrupt (SGI). If CONFIG_SMP is + * selected, then the SGI is sent to all CPUs specified in the CPU set. + * That set may include the current CPU. + * + * If CONFIG_SMP is not selected, the cpuset is ignored and SGI is sent + * only to the current CPU. + * + * Input Parameters: + * sgi - The SGI interrupt ID (0-15) + * cpuset - The set of CPUs to receive the SGI + * + * Returned Value: + * OK is always returned at present. + * + ****************************************************************************/ + +static inline void arm_cpu_sgi(int sgi, unsigned int cpuset) +{ + uint32_t regval; + +#ifdef CONFIG_SMP + regval = GIC_ICDSGIR_INTID(sgi) | GIC_ICDSGIR_CPUTARGET(cpuset) | + GIC_ICDSGIR_TGTFILTER_LIST; +#else + regval = GIC_ICDSGIR_INTID(sgi) | GIC_ICDSGIR_CPUTARGET(0) | + GIC_ICDSGIR_TGTFILTER_THIS; +#endif + + putreg32(regval, GIC_ICDSGIR); +} + +/**************************************************************************** + * Name: gic_validate_dist_version + * + * Description: + * Verify that GIC Version is 2. + * + * Input Parameters: + * None + * + * Returned Value: + * Zero (OK) if GIC Version is 2; -ENODEV if GIC Version isn't 2. + * + ****************************************************************************/ + +static int gic_validate_dist_version(void) +{ + uint32_t reg; + + /* Read the Peripheral ID2 Register (ICPIDR2) */ + + reg = getreg32(GIC_ICDPIDR(GIC_ICPIDR2)) & GICD_PIDR2_ARCH_MASK; + + /* GIC Version should be 2 */ + + if (reg == GICD_PIDR2_ARCH_GICV2) + { + sinfo("GICv2 detected\n"); + } + else + { + sinfo("GICv2 not detected\n"); + return -ENODEV; + } + + return 0; +} + +/**************************************************************************** + * Name: arm_gic_irq_trigger + * + * Description: + * Set the trigger type for the specified IRQ source and the current CPU. + * + * Since this API is not supported on all architectures, it should be + * avoided in common implementations where possible. + * + * Input Parameters: + * irq - The interrupt request to modify. + * edge - False: Active HIGH level sensitive, True: Rising edge sensitive + * + * Returned Value: + * Zero (OK) on success; a negated errno value is returned on any failure. + * + ****************************************************************************/ + +static int arm_gic_irq_trigger(int irq, bool edge) +{ + uintptr_t regaddr; + uint32_t regval; + uint32_t intcfg; + + if (irq > GIC_IRQ_SGI15 && irq < NR_IRQS) + { + /* Get the address of the Interrupt Configuration Register for this + * irq. + */ + + regaddr = GIC_ICDICFR(irq); + + /* Get the new Interrupt configuration bit setting */ + + intcfg = (edge ? (INT_ICDICFR_EDGE | INT_ICDICFR_1N) : INT_ICDICFR_1N); + + /* Write the correct interrupt trigger to the Interrupt Configuration + * Register. + */ + + regval = getreg32(regaddr); + regval &= ~GIC_ICDICFR_ID_MASK(irq); + regval |= GIC_ICDICFR_ID(irq, intcfg); + putreg32(regval, regaddr); + + return OK; + } + + return -EINVAL; +} + +/**************************************************************************** + * Name: arm_gic0_initialize + * + * Description: + * Perform common, one-time GIC initialization on CPU0 only. Both + * arm_gic0_initialize() must be called on CPU0; arm_gic_initialize() must + * be called for all CPUs. + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void arm_gic0_initialize(void) +{ + unsigned int nlines = arm_gic_nlines(); + unsigned int irq; + + /* Initialize SPIs. The following should be done only by CPU0. */ + + /* A processor in Secure State sets: + * + * 1. Which interrupts are non-secure (ICDISR). All set to zero (group + * 0). + * 2. Trigger mode of the SPI (ICDICFR). All fields set to 0b01->Level + * sensitive, 1-N model. + * 3. Interrupt Clear-Enable (ICDICER) + * 3. Priority of the SPI using the priority set register (ICDIPR). + * Priority values are 8-bit unsigned binary. A GIC supports a + * minimum of 16 and a maximum of 256 priority levels. Here all + * are set to the middle priority 128 (0x80). + * 4. Target that receives the SPI interrupt (ICDIPTR). Set all to + * CPU0. + */ + + /* Registers with 1-bit per interrupt */ + + for (irq = GIC_IRQ_SPI; irq < nlines; irq += 32) + { + putreg32(0x00000000, GIC_ICDISR(irq)); /* SPIs group 0 */ + putreg32(0xffffffff, GIC_ICDICER(irq)); /* SPIs disabled */ + } + + /* Registers with 2-bits per interrupt */ + + for (irq = GIC_IRQ_SPI; irq < nlines; irq += 16) + { + putreg32(0x55555555, GIC_ICDICFR(irq)); /* SPIs level sensitive */ + } + + /* Registers with 8-bits per interrupt */ + + for (irq = GIC_IRQ_SPI; irq < nlines; irq += 4) + { + putreg32(0x80808080, GIC_ICDIPR(irq)); /* SPI priority */ + putreg32(0x01010101, GIC_ICDIPTR(irq)); /* SPI on CPU0 */ + } + +#ifdef CONFIG_SMP + /* Attach SGI interrupt handlers. This attaches the handler to all CPUs. */ + + DEBUGVERIFY(irq_attach(GIC_IRQ_SGI1, arm_start_handler, NULL)); + DEBUGVERIFY(irq_attach(GIC_IRQ_SGI2, arm_pause_handler, NULL)); +#endif +} + +/**************************************************************************** + * Name: arm_gic_initialize + * + * Description: + * Perform common GIC initialization for the current CPU (all CPUs) + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void arm_gic_initialize(void) +{ + uint32_t iccicr; + uint32_t icddcr; + + /* Initialize PPIs. The following steps need to be done by all CPUs */ + + /* Initialize SGIs and PPIs. NOTE: A processor in non-secure state cannot + * program its interrupt security registers and must get a secure processor + * to program the registers. + */ + + /* Registers with 1-bit per interrupt */ + + putreg32(0x00000000, GIC_ICDISR(0)); /* SGIs and PPIs secure */ + putreg32(0xf8000000, GIC_ICDICER(0)); /* PPIs disabled */ + + /* Registers with 8-bits per interrupt */ + + putreg32(0x80808080, GIC_ICDIPR(0)); /* SGI[3:0] priority */ + putreg32(0x80808080, GIC_ICDIPR(4)); /* SGI[4:7] priority */ + putreg32(0x80808080, GIC_ICDIPR(8)); /* SGI[8:11] priority */ + putreg32(0x80808080, GIC_ICDIPR(12)); /* SGI[12:15] priority */ + putreg32(0x80000000, GIC_ICDIPR(24)); /* PPI[0] priority */ + putreg32(0x80808080, GIC_ICDIPR(28)); /* PPI[1:4] priority */ + + /* Set the binary point register. + * + * Priority values are 8-bit unsigned binary. The binary point is a 3-bit + * field; the value n (n=0-6) specifies that bits (n+1) through bit 7 are + * used in the comparison for interrupt pre-emption. A GIC supports a + * minimum of 16 and a maximum of 256 priority levels so not all binary + * point settings may be meaningul. The special value n=7 + * (GIC_ICCBPR_NOPREMPT) disables pre-emption. We disable all pre-emption + * here to prevent nesting of interrupt handling. + */ + + putreg32(GIC_ICCBPR_NOPREMPT, GIC_ICCBPR); + + /* Program the idle priority in the PMR */ + + putreg32(GIC_ICCPMR_MASK, GIC_ICCPMR); + + /* Configure the CPU Interface Control Register */ + + iccicr = getreg32(GIC_ICCICR); + +#if defined(CONFIG_ARCH_TRUSTZONE_SECURE) || defined(CONFIG_ARCH_TRUSTZONE_BOTH) + /* Clear secure state ICCICR bits to be configured below */ + + iccicr &= ~(GIC_ICCICRS_FIQEN | GIC_ICCICRS_ACKTCTL | GIC_ICCICRS_CBPR | + GIC_ICCICRS_EOIMODES | GIC_ICCICRS_EOIMODENS | + GIC_ICCICRS_ENABLEGRP0 | GIC_ICCICRS_ENABLEGRP1 | + GIC_ICCICRS_FIQBYPDISGRP0 | GIC_ICCICRS_IRQBYPDISGRP0 | + GIC_ICCICRS_FIQBYPDISGRP1 | GIC_ICCICRS_IRQBYPDISGRP1); + +#elif defined(CONFIG_ARCH_TRUSTZONE_NONSECURE) + /* Clear non-secure state ICCICR bits to be configured below */ + + iccicr &= ~(GIC_ICCICRS_EOIMODENS | GIC_ICCICRU_ENABLEGRP1 | + GIC_ICCICRU_FIQBYPDISGRP1 | GIC_ICCICRU_IRQBYPDISGRP1); + +#endif + +#if defined(CONFIG_ARCH_TRUSTZONE_SECURE) + /* Set FIQn=1 if secure interrupts are to signal using nfiq_c. + * + * NOTE: Only for processors that operate in secure state. + * REVISIT: Do I need to do this? + */ + + /* iccicr |= GIC_ICCICRS_FIQEN; */ + +#elif defined(CONFIG_ARCH_TRUSTZONE_BOTH) + /* Set FIQn=1 if secure interrupts are to signal using nfiq_c. + * + * NOTE: Only for processors that operate in secure state. + * REVISIT: Do I need to do this? + */ + + iccicr |= GIC_ICCICRS_FIQEN; +#endif + +#if defined(CONFIG_ARCH_TRUSTZONE_SECURE) + /* Program the AckCtl bit to select the required interrupt acknowledge + * behavior. + * + * NOTE: Only for processors that operate in both secure and non-secure + * state. + * REVISIT: This is here only for superstituous reasons. I don't think + * I need this setting in this configuration. + */ + + iccicr |= GIC_ICCICRS_ACKTCTL; + +#elif defined(CONFIG_ARCH_TRUSTZONE_BOTH) + /* Program the AckCtl bit to select the required interrupt acknowledge + * behavior. + * + * NOTE: Only for processors that operate in both secure and non-secure + * state. + */ + + iccicr |= GIC_ICCICRS_ACKTCTL; + + /* Program the SBPR bit to select the required binary pointer behavior. + * + * NOTE: Only for processors that operate in both secure and non-secure + * state. + */ + + iccicr |= GIC_ICCICRS_CBPR; +#endif + +#if defined(CONFIG_ARCH_TRUSTZONE_SECURE) || defined(CONFIG_ARCH_TRUSTZONE_BOTH) + /* Set EnableS=1 to enable CPU interface to signal secure interrupts. + * + * NOTE: Only for processors that operate in secure state. + */ + + iccicr |= GIC_ICCICRS_EOIMODES; +#endif + +#if defined(CONFIG_ARCH_TRUSTZONE_NONSECURE) + /* Set EnableNS=1 to enable the CPU to signal non-secure interrupts. + * + * NOTE: Only for processors that operate in non-secure state. + */ + + iccicr |= GIC_ICCICRS_EOIMODENS; + +#elif defined(CONFIG_ARCH_TRUSTZONE_BOTH) + /* Set EnableNS=1 to enable the CPU to signal non-secure interrupts. + * + * NOTE: Only for processors that operate in non-secure state. + */ + + iccicr |= GIC_ICCICRU_EOIMODENS; +#endif + + #ifdef CONFIG_ARCH_TRUSTZONE_BOTH + /* If the processor operates in both security states and SBPR=0, then it + * must switch to the other security state and repeat the programming of + * the binary point register so that the binary point will be programmed + * for interrupts in both security states. + */ + +# warning Missing logic +#endif + +#if !defined(CONFIG_ARCH_HAVE_TRUSTZONE) + /* Enable the distributor by setting the Enable bit in the enable + * register (no security extensions). + */ + + iccicr |= GIC_ICCICR_ENABLE; + icddcr = GIC_ICDDCR_ENABLE; + +#elif defined(CONFIG_ARCH_TRUSTZONE_SECURE) + /* Enable the Group 0 interrupts, FIQEn and disable Group 0/1 + * bypass. + */ + +#if 0 /* REVISIT -- I don't know why this needs to be like this */ + iccicr |= (GIC_ICCICRS_ENABLEGRP0 | GIC_ICCICRS_FIQBYPDISGRP0 | + GIC_ICCICRS_IRQBYPDISGRP0 | GIC_ICCICRS_FIQBYPDISGRP1 | + GIC_ICCICRS_IRQBYPDISGRP1); +#else + iccicr |= (GIC_ICCICRS_ENABLEGRP0 | GIC_ICCICRS_ENABLEGRP1 | + GIC_ICCICRS_FIQBYPDISGRP0 | GIC_ICCICRS_IRQBYPDISGRP0 | + GIC_ICCICRS_FIQBYPDISGRP1 | GIC_ICCICRS_IRQBYPDISGRP1); +#endif + icddcr = GIC_ICDDCR_ENABLEGRP0; + +#elif defined(CONFIG_ARCH_TRUSTZONE_BOTH) + /* Enable the Group 0/1 interrupts, FIQEn and disable Group 0/1 + * bypass. + */ + + iccicr |= (GIC_ICCICRS_ENABLEGRP0 | GIC_ICCICRS_ENABLEGRP1 | + GIC_ICCICRS_FIQBYPDISGRP0 | GIC_ICCICRS_IRQBYPDISGRP0 | + GIC_ICCICRS_FIQBYPDISGRP1 | GIC_ICCICRS_IRQBYPDISGRP1); + icddcr = (GIC_ICDDCR_ENABLEGRP0 | GIC_ICDDCR_ENABLEGRP1); + +#else /* defined(CONFIG_ARCH_TRUSTZONE_NONSECURE) */ + /* Enable the Group 1 interrupts and disable Group 1 bypass. */ + + iccicr |= (GIC_ICCICRU_ENABLEGRP1 | GIC_ICCICRU_FIQBYPDISGRP1 | + GIC_ICCICRU_IRQBYPDISGRP1); + icddcr = GIC_ICDDCR_ENABLE; + +#endif + + /* Write the final ICCICR value to enable the GIC. */ + + putreg32(iccicr, GIC_ICCICR); + +#ifdef CONFIG_ARCH_TRUSTZONE_BOTH + /* A processor in the secure state must then switch to the non-secure + * a repeat setting of the enable bit in the enable register. This + * enables distributor to respond to interrupt in both security states. + * REVISIT: Initial implementation operates only in secure state. + */ + +# warning Missing logic +#endif + + /* Write the ICDDCR value to enable the forwarding of interrupt by the + * distributor. + */ + + putreg32(icddcr, GIC_ICDDCR); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: arm64_decodeirq + * + * Description: + * This function is called from the IRQ vector handler in arm_vectors.S. + * At this point, the interrupt has been taken and the registers have + * been saved on the stack. This function simply needs to determine the + * the irq number of the interrupt and then to call arm_doirq to dispatch + * the interrupt. + * + * Input Parameters: + * regs - A pointer to the register save area on the stack. + * + ****************************************************************************/ + +uint64_t * arm64_decodeirq(uint64_t * regs) +{ + uint32_t regval; + int irq; + + /* Read the interrupt acknowledge register and get the interrupt ID */ + + regval = getreg32(GIC_ICCIAR); + irq = (regval & GIC_ICCIAR_INTID_MASK) >> GIC_ICCIAR_INTID_SHIFT; + + /* Ignore spurions IRQs. ICCIAR will report 1023 if there is no pending + * interrupt. + */ + + DEBUGASSERT(irq < NR_IRQS || irq == 1023); + if (irq < NR_IRQS) + { + /* Dispatch the interrupt */ + + regs = arm64_doirq(irq, regs); + } + + /* Write to the end-of-interrupt register */ + + putreg32(regval, GIC_ICCEOIR); + return regs; +} + +/**************************************************************************** + * Name: up_enable_irq + * + * Description: + * On many architectures, there are three levels of interrupt enabling: (1) + * at the global level, (2) at the level of the interrupt controller, + * and (3) at the device level. In order to receive interrupts, they + * must be enabled at all three levels. + * + * This function implements enabling of the device specified by 'irq' + * at the interrupt controller level if supported by the architecture + * (up_irq_restore() supports the global level, the device level is + * hardware specific). + * + * Since this API is not supported on all architectures, it should be + * avoided in common implementations where possible. + * + ****************************************************************************/ + +void up_enable_irq(int irq) +{ + /* Ignore invalid interrupt IDs. Also, in the Cortex-A9 MPCore, SGIs are + * always enabled. The corresponding bits in the ICDISERn are read as + * one, write ignored. + */ + + if (irq > GIC_IRQ_SGI15 && irq < NR_IRQS) + { + uintptr_t regaddr; + + /* Write '1' to the corresponding bit in the distributor Interrupt + * Set-Enable Register (ICDISER) + */ + + regaddr = GIC_ICDISER(irq); + putreg32(GIC_ICDISER_INT(irq), regaddr); + } +} + +/**************************************************************************** + * Name: up_disable_irq + * + * Description: + * This function implements disabling of the device specified by 'irq' + * at the interrupt controller level if supported by the architecture + * (up_irq_save() supports the global level, the device level is hardware + * specific). + * + * Since this API is not supported on all architectures, it should be + * avoided in common implementations where possible. + * + ****************************************************************************/ + +void up_disable_irq(int irq) +{ + /* Ignore invalid interrupt IDs. Also, in the Cortex-A9 MPCore, SGIs are + * always enabled. The corresponding bits in the ICDISERn are read as + * one, write ignored. + */ + + if (irq > GIC_IRQ_SGI15 && irq < NR_IRQS) + { + uintptr_t regaddr; + + /* Write '1' to the corresponding bit in the distributor Interrupt + * Clear-Enable Register (ICDISER) + */ + + regaddr = GIC_ICDICER(irq); + putreg32(GIC_ICDICER_INT(irq), regaddr); + } +} + +/**************************************************************************** + * Name: up_prioritize_irq + * + * Description: + * Set the priority of an IRQ. + * + * Since this API is not supported on all architectures, it should be + * avoided in common implementations where possible. + * + ****************************************************************************/ + +int up_prioritize_irq(int irq, int priority) +{ + DEBUGASSERT(irq >= 0 && irq < NR_IRQS && priority >= 0 && priority <= 255); + + /* Ignore invalid interrupt IDs */ + + if (irq >= 0 && irq < NR_IRQS) + { + uintptr_t regaddr; + uint32_t regval; + + /* Write the new priority to the corresponding field in the in the + * distributor Interrupt Priority Register (GIC_ICDIPR). + */ + + regaddr = GIC_ICDIPR(irq); + regval = getreg32(regaddr); + regval &= ~GIC_ICDIPR_ID_MASK(irq); + regval |= GIC_ICDIPR_ID(irq, priority); + putreg32(regval, regaddr); + + return OK; + } + + return -EINVAL; +} + +/**************************************************************************** + * Name: up_affinity_irq + * + * Description: + * Set an IRQ affinity by software. + * + ****************************************************************************/ + +void up_affinity_irq(int irq, cpu_set_t cpuset) +{ + if (irq >= GIC_IRQ_SPI && irq < NR_IRQS) + { + uintptr_t regaddr; + uint32_t regval; + + /* Write the new cpuset to the corresponding field in the in the + * distributor Interrupt Processor Target Register (GIC_ICDIPTR). + */ + + regaddr = GIC_ICDIPTR(irq); + regval = getreg32(regaddr); + regval &= ~GIC_ICDIPTR_ID_MASK(irq); + regval |= GIC_ICDIPTR_ID(irq, cpuset); + putreg32(regval, regaddr); + } +} + +/**************************************************************************** + * Name: up_trigger_irq + * + * Description: + * Perform a Software Generated Interrupt (SGI). If CONFIG_SMP is + * selected, then the SGI is sent to all CPUs specified in the CPU set. + * That set may include the current CPU. + * + * If CONFIG_SMP is not selected, the cpuset is ignored and SGI is sent + * only to the current CPU. + * + * Input Parameters + * irq - The SGI interrupt ID (0-15) + * cpuset - The set of CPUs to receive the SGI + * + ****************************************************************************/ + +void up_trigger_irq(int irq, cpu_set_t cpuset) +{ + if (irq >= 0 && irq <= GIC_IRQ_SGI15) + { + arm_cpu_sgi(irq, cpuset); + } + else if (irq >= 0 && irq < NR_IRQS) + { + uintptr_t regaddr; + + /* Write '1' to the corresponding bit in the distributor Interrupt + * Set-Pending (ICDISPR) + */ + + regaddr = GIC_ICDISPR(irq); + putreg32(GIC_ICDISPR_INT(irq), regaddr); + } +} + +/**************************************************************************** + * Name: arm64_gic_irq_set_priority + * + * Description: + * Set the interrupt priority and type. + * + * If CONFIG_SMP is not selected, the cpuset is ignored and SGI is sent + * only to the current CPU. + * + * Input Parameters + * intid - The SGI interrupt ID (0-15) + * prio - The interrupt priority + * flags - Bit IRQ_TYPE_EDGE is 1 if interrupt should be edge-triggered + * + * Returned Value: + * None + * + ****************************************************************************/ + +void arm64_gic_irq_set_priority(unsigned int intid, unsigned int prio, + uint32_t flags) +{ + int ret; + + /* Disable the interrupt */ + + up_disable_irq(intid); + + /* Set the interrupt priority */ + + ret = up_prioritize_irq(intid, prio); + DEBUGASSERT(ret == OK); + + /* Configure interrupt type */ + + if (!GIC_IS_SGI(intid)) + { + if (flags & IRQ_TYPE_EDGE) + { + ret = arm_gic_irq_trigger(intid, true); + DEBUGASSERT(ret == OK); + } + else + { + ret = arm_gic_irq_trigger(intid, false); + DEBUGASSERT(ret == OK); + } + } +} + +/**************************************************************************** + * Name: arm64_gic_initialize + * + * Description: + * Initialize GIC. Called by CPU0 only. + * + * Input Parameters + * None + * + * Returned Value: + * Zero (OK) on success; a negated errno value is returned on any failure. + * + ****************************************************************************/ + +int arm64_gic_initialize(void) +{ + int err; + + /* Verify that GIC Version is 2 */ + + err = gic_validate_dist_version(); + if (err) + { + sinfo("no distributor detected, giving up ret=%d\n", err); + return err; + } + + /* CPU0-specific initialization for GIC */ + + arm_gic0_initialize(); + + /* CPU-generic initialization for GIC */ + + arm_gic_initialize(); + + return 0; +} + +#ifdef CONFIG_SMP + +/**************************************************************************** + * Name: arm64_gic_secondary_init + * + * Description: + * Initialize GIC. Called by all CPUs except CPU0. + * + * Input Parameters + * None + * + * Returned Value: + * None + * + ****************************************************************************/ + +void arm64_gic_secondary_init(void) +{ + /* CPU-generic initialization for GIC */ + + arm_gic_initialize(); +} + +#endif /* CONFIG_SMP */ + +#endif /* CONFIG_ARM_GIC_VERSION == 2 */ diff --git a/arch/arm64/src/common/arm64_gicv3.c b/arch/arm64/src/common/arm64_gicv3.c index f4ab3fb205..5df8b9c9cc 100644 --- a/arch/arm64/src/common/arm64_gicv3.c +++ b/arch/arm64/src/common/arm64_gicv3.c @@ -36,6 +36,8 @@ #include "arm64_gic.h" #include "arm64_fatal.h" +#if CONFIG_ARM_GIC_VERSION == 3 || CONFIG_ARM_GIC_VERSION == 4 + /*************************************************************************** * Pre-processor Definitions ***************************************************************************/ @@ -615,3 +617,5 @@ void arm64_gic_secondary_init(void) } #endif + +#endif /* CONFIG_ARM_GIC_VERSION == 3 || CONFIG_ARM_GIC_VERSION == 4 */ diff --git a/boards/arm64/qemu/qemu-armv8a/README.txt b/boards/arm64/qemu/qemu-armv8a/README.txt index 51f436bd42..898a8f484d 100644 --- a/boards/arm64/qemu/qemu-armv8a/README.txt +++ b/boards/arm64/qemu/qemu-armv8a/README.txt @@ -4,7 +4,7 @@ README.txt This board configuration will use QEMU to emulate generic ARM64 v8-A series hardware platform and provides support for these devices: - - GICv3 interrupt controller + - GICv2 and GICv3 interrupt controllers - ARM Generic Timer - PL011 UART controller @@ -41,7 +41,7 @@ Getting Started $ qemu-system-aarch64 --help 3. Configuring and running - 3.1 Single Core + 3.1 Single Core (GICv3) Configuring NuttX and compile: $ ./tools/configure.sh -l qemu-armv8a:nsh $ make @@ -51,7 +51,7 @@ Getting Started -net none -chardev stdio,id=con,mux=on -serial chardev:con \ -mon chardev=con,mode=readline -kernel ./nuttx - 3.2 SMP + 3.2 SMP (GICv3) Configuring NuttX and compile: $ ./tools/configure.sh -l qemu-armv8a:nsh_smp $ make @@ -61,6 +61,16 @@ Getting Started -net none -chardev stdio,id=con,mux=on -serial chardev:con \ -mon chardev=con,mode=readline -kernel ./nuttx + 3.3 Single Core (GICv2) + Configuring NuttX and compile: + $ ./tools/configure.sh -l qemu-armv8a:nsh_gicv2 + $ make + Running with qemu + $ qemu-system-aarch64 -cpu cortex-a53 -nographic \ + -machine virt,virtualization=on,gic-version=2 \ + -net none -chardev stdio,id=con,mux=on -serial chardev:con \ + -mon chardev=con,mode=readline -kernel ./nuttx + Note: 1. Make sure the aarch64-none-elf toolchain install PATH has been added to environment variable 2. To quit QEMU, type Ctrl + X @@ -70,6 +80,11 @@ Getting Started Status ====== +2022-11-18: +1. Added support for GICv2. + +2. Added board configuration for nsh_gicv2. + 2022-10-13: 1. Renamed the board configuration name from qemu-a53 to qemu-v8a. diff --git a/boards/arm64/qemu/qemu-armv8a/configs/nsh_gicv2/defconfig b/boards/arm64/qemu/qemu-armv8a/configs/nsh_gicv2/defconfig new file mode 100644 index 0000000000..f6056bb21d --- /dev/null +++ b/boards/arm64/qemu/qemu-armv8a/configs/nsh_gicv2/defconfig @@ -0,0 +1,66 @@ +# +# This file is autogenerated: PLEASE DO NOT EDIT IT. +# +# You can use "make menuconfig" to make any modifications to the installed .config file. +# You can then do "make savedefconfig" to generate a new defconfig file that includes your +# modifications. +# +CONFIG_ARCH="arm64" +CONFIG_ARCH_ARM64=y +CONFIG_ARCH_BOARD="qemu-armv8a" +CONFIG_ARCH_BOARD_QEMU_ARMV8A=y +CONFIG_ARCH_CHIP="qemu" +CONFIG_ARCH_CHIP_QEMU=y +CONFIG_ARCH_CHIP_QEMU_A53=y +CONFIG_ARCH_INTERRUPTSTACK=4096 +CONFIG_ARM_GIC_VERSION=2 +CONFIG_BUILTIN=y +CONFIG_DEBUG_ASSERTIONS=y +CONFIG_DEBUG_ERROR=y +CONFIG_DEBUG_FEATURES=y +CONFIG_DEBUG_FULLOPT=y +CONFIG_DEBUG_INFO=y +CONFIG_DEBUG_SCHED=y +CONFIG_DEBUG_SCHED_ERROR=y +CONFIG_DEBUG_SCHED_INFO=y +CONFIG_DEBUG_SCHED_WARN=y +CONFIG_DEBUG_SYMBOLS=y +CONFIG_DEBUG_WARN=y +CONFIG_DEFAULT_TASK_STACKSIZE=8192 +CONFIG_DEV_ZERO=y +CONFIG_EXAMPLES_HELLO=y +CONFIG_EXPERIMENTAL=y +CONFIG_FS_PROCFS=y +CONFIG_FS_ROMFS=y +CONFIG_HAVE_CXX=y +CONFIG_HAVE_CXXINITIALIZE=y +CONFIG_IDLETHREAD_STACKSIZE=8192 +CONFIG_INIT_ENTRYPOINT="nsh_main" +CONFIG_INTELHEX_BINARY=y +CONFIG_NSH_ARCHINIT=y +CONFIG_NSH_BUILTIN_APPS=y +CONFIG_NSH_FILEIOSIZE=512 +CONFIG_NSH_READLINE=y +CONFIG_NSH_ROMFSETC=y +CONFIG_PREALLOC_TIMERS=4 +CONFIG_PTHREAD_STACK_MIN=8192 +CONFIG_QEMU_UART_PL011=y +CONFIG_RAMLOG=y +CONFIG_RAM_SIZE=134217728 +CONFIG_RAM_START=0x40000000 +CONFIG_RAW_BINARY=y +CONFIG_READLINE_CMD_HISTORY=y +CONFIG_RR_INTERVAL=200 +CONFIG_SCHED_HPWORK=y +CONFIG_SCHED_HPWORKPRIORITY=192 +CONFIG_SPINLOCK=y +CONFIG_STACK_COLORATION=y +CONFIG_START_MONTH=3 +CONFIG_START_YEAR=2022 +CONFIG_SYMTAB_ORDEREDBYNAME=y +CONFIG_SYSTEM_NSH=y +CONFIG_SYSTEM_SYSTEM=y +CONFIG_TESTING_GETPRIME=y +CONFIG_TESTING_OSTEST=y +CONFIG_UART1_SERIAL_CONSOLE=y +CONFIG_USEC_PER_TICK=1000