Bradley Bolen | 571d3b5 | 2018-04-18 12:13:06 +0300 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (c) 2018 Marvell |
| 3 | * Copyright (c) 2018 Lexmark International, Inc. |
Stephanos Ioannidis | 2b44173 | 2019-12-19 14:54:46 +0900 | [diff] [blame] | 4 | * Copyright (c) 2019 Stephanos Ioannidis <root@stephanos.io> |
Bradley Bolen | 571d3b5 | 2018-04-18 12:13:06 +0300 | [diff] [blame] | 5 | * |
| 6 | * SPDX-License-Identifier: Apache-2.0 |
| 7 | */ |
| 8 | |
Stephanos Ioannidis | 2b44173 | 2019-12-19 14:54:46 +0900 | [diff] [blame] | 9 | /* |
Sandeep Tripathy | bd985dc | 2020-04-22 01:55:37 +0530 | [diff] [blame] | 10 | * NOTE: This driver implements the GICv1 and GICv2 interfaces. |
Stephanos Ioannidis | 2b44173 | 2019-12-19 14:54:46 +0900 | [diff] [blame] | 11 | */ |
| 12 | |
Bradley Bolen | 571d3b5 | 2018-04-18 12:13:06 +0300 | [diff] [blame] | 13 | #include <sw_isr_table.h> |
Bradley Bolen | 571d3b5 | 2018-04-18 12:13:06 +0300 | [diff] [blame] | 14 | #include <dt-bindings/interrupt-controller/arm-gic.h> |
Stephanos Ioannidis | 2b44173 | 2019-12-19 14:54:46 +0900 | [diff] [blame] | 15 | #include <drivers/interrupt_controller/gic.h> |
Bradley Bolen | 571d3b5 | 2018-04-18 12:13:06 +0300 | [diff] [blame] | 16 | |
Stephanos Ioannidis | 50519ce | 2020-02-11 15:47:59 +0900 | [diff] [blame] | 17 | void arm_gic_irq_enable(unsigned int irq) |
| 18 | { |
| 19 | int int_grp, int_off; |
| 20 | |
| 21 | int_grp = irq / 32; |
| 22 | int_off = irq % 32; |
| 23 | |
| 24 | sys_write32((1 << int_off), (GICD_ISENABLERn + int_grp * 4)); |
| 25 | } |
| 26 | |
| 27 | void arm_gic_irq_disable(unsigned int irq) |
| 28 | { |
| 29 | int int_grp, int_off; |
| 30 | |
| 31 | int_grp = irq / 32; |
| 32 | int_off = irq % 32; |
| 33 | |
| 34 | sys_write32((1 << int_off), (GICD_ICENABLERn + int_grp * 4)); |
| 35 | } |
| 36 | |
| 37 | bool arm_gic_irq_is_enabled(unsigned int irq) |
| 38 | { |
| 39 | int int_grp, int_off; |
| 40 | unsigned int enabler; |
| 41 | |
| 42 | int_grp = irq / 32; |
| 43 | int_off = irq % 32; |
| 44 | |
| 45 | enabler = sys_read32(GICD_ISENABLERn + int_grp * 4); |
| 46 | |
| 47 | return (enabler & (1 << int_off)) != 0; |
| 48 | } |
| 49 | |
| 50 | void arm_gic_irq_set_priority( |
Kumar Gala | a1b77fd | 2020-05-27 11:26:57 -0500 | [diff] [blame] | 51 | unsigned int irq, unsigned int prio, uint32_t flags) |
Stephanos Ioannidis | 50519ce | 2020-02-11 15:47:59 +0900 | [diff] [blame] | 52 | { |
| 53 | int int_grp, int_off; |
Kumar Gala | a1b77fd | 2020-05-27 11:26:57 -0500 | [diff] [blame] | 54 | uint32_t val; |
Stephanos Ioannidis | 50519ce | 2020-02-11 15:47:59 +0900 | [diff] [blame] | 55 | |
| 56 | /* Set priority */ |
| 57 | sys_write8(prio & 0xff, GICD_IPRIORITYRn + irq); |
| 58 | |
| 59 | /* Set interrupt type */ |
Bartlomiej Flak | c0be43e | 2020-04-17 15:09:20 +0200 | [diff] [blame] | 60 | int_grp = (irq / 16) * 4; |
Stephanos Ioannidis | 50519ce | 2020-02-11 15:47:59 +0900 | [diff] [blame] | 61 | int_off = (irq % 16) * 2; |
| 62 | |
Bartlomiej Flak | c0be43e | 2020-04-17 15:09:20 +0200 | [diff] [blame] | 63 | val = sys_read32(GICD_ICFGRn + int_grp); |
Stephanos Ioannidis | 495407a | 2020-03-19 14:33:38 +0900 | [diff] [blame] | 64 | val &= ~(GICD_ICFGR_MASK << int_off); |
Stephanos Ioannidis | 50519ce | 2020-02-11 15:47:59 +0900 | [diff] [blame] | 65 | if (flags & IRQ_TYPE_EDGE) { |
Stephanos Ioannidis | 495407a | 2020-03-19 14:33:38 +0900 | [diff] [blame] | 66 | val |= (GICD_ICFGR_TYPE << int_off); |
Stephanos Ioannidis | 50519ce | 2020-02-11 15:47:59 +0900 | [diff] [blame] | 67 | } |
| 68 | |
Bartlomiej Flak | c0be43e | 2020-04-17 15:09:20 +0200 | [diff] [blame] | 69 | sys_write32(val, GICD_ICFGRn + int_grp); |
Stephanos Ioannidis | 50519ce | 2020-02-11 15:47:59 +0900 | [diff] [blame] | 70 | } |
| 71 | |
| 72 | unsigned int arm_gic_get_active(void) |
| 73 | { |
| 74 | int irq; |
| 75 | |
| 76 | irq = sys_read32(GICC_IAR) & 0x3ff; |
| 77 | return irq; |
| 78 | } |
| 79 | |
| 80 | void arm_gic_eoi(unsigned int irq) |
| 81 | { |
Sandeep Tripathy | ccb4b1e | 2020-06-26 17:50:14 +0530 | [diff] [blame] | 82 | /* |
| 83 | * Ensure the write to peripheral registers are *complete* before the write |
| 84 | * to GIC_EOIR. |
| 85 | * |
| 86 | * Note: The completion gurantee depends on various factors of system design |
| 87 | * and the barrier is the best core can do by which execution of further |
| 88 | * instructions waits till the barrier is alive. |
| 89 | */ |
| 90 | __DSB(); |
| 91 | |
Stephanos Ioannidis | 50519ce | 2020-02-11 15:47:59 +0900 | [diff] [blame] | 92 | /* set to inactive */ |
| 93 | sys_write32(irq, GICC_EOIR); |
| 94 | } |
Bradley Bolen | 571d3b5 | 2018-04-18 12:13:06 +0300 | [diff] [blame] | 95 | |
| 96 | static void gic_dist_init(void) |
| 97 | { |
| 98 | unsigned int gic_irqs, i; |
| 99 | |
| 100 | gic_irqs = sys_read32(GICD_TYPER) & 0x1f; |
| 101 | gic_irqs = (gic_irqs + 1) * 32; |
Stephanos Ioannidis | 50519ce | 2020-02-11 15:47:59 +0900 | [diff] [blame] | 102 | if (gic_irqs > 1020) { |
Bradley Bolen | 571d3b5 | 2018-04-18 12:13:06 +0300 | [diff] [blame] | 103 | gic_irqs = 1020; |
Stephanos Ioannidis | 50519ce | 2020-02-11 15:47:59 +0900 | [diff] [blame] | 104 | } |
Bradley Bolen | 571d3b5 | 2018-04-18 12:13:06 +0300 | [diff] [blame] | 105 | |
| 106 | /* |
| 107 | * Disable the forwarding of pending interrupts |
| 108 | * from the Distributor to the CPU interfaces |
| 109 | */ |
Stephanos Ioannidis | 2b44173 | 2019-12-19 14:54:46 +0900 | [diff] [blame] | 110 | sys_write32(0, GICD_CTLR); |
Bradley Bolen | 571d3b5 | 2018-04-18 12:13:06 +0300 | [diff] [blame] | 111 | |
| 112 | /* |
| 113 | * Set all global interrupts to this CPU only. |
| 114 | */ |
Stephanos Ioannidis | 50519ce | 2020-02-11 15:47:59 +0900 | [diff] [blame] | 115 | for (i = GIC_SPI_INT_BASE; i < gic_irqs; i += 4) { |
Bradley Bolen | 571d3b5 | 2018-04-18 12:13:06 +0300 | [diff] [blame] | 116 | sys_write32(0x01010101, GICD_ITARGETSRn + i); |
Stephanos Ioannidis | 50519ce | 2020-02-11 15:47:59 +0900 | [diff] [blame] | 117 | } |
Bradley Bolen | 571d3b5 | 2018-04-18 12:13:06 +0300 | [diff] [blame] | 118 | |
| 119 | /* |
| 120 | * Set all global interrupts to be level triggered, active low. |
| 121 | */ |
Stephanos Ioannidis | 50519ce | 2020-02-11 15:47:59 +0900 | [diff] [blame] | 122 | for (i = GIC_SPI_INT_BASE; i < gic_irqs; i += 16) { |
Bradley Bolen | 571d3b5 | 2018-04-18 12:13:06 +0300 | [diff] [blame] | 123 | sys_write32(0, GICD_ICFGRn + i / 4); |
Stephanos Ioannidis | 50519ce | 2020-02-11 15:47:59 +0900 | [diff] [blame] | 124 | } |
Bradley Bolen | 571d3b5 | 2018-04-18 12:13:06 +0300 | [diff] [blame] | 125 | |
| 126 | /* Set priority on all global interrupts. */ |
Stephanos Ioannidis | 50519ce | 2020-02-11 15:47:59 +0900 | [diff] [blame] | 127 | for (i = GIC_SPI_INT_BASE; i < gic_irqs; i += 4) { |
Bradley Bolen | 571d3b5 | 2018-04-18 12:13:06 +0300 | [diff] [blame] | 128 | sys_write32(0, GICD_IPRIORITYRn + i); |
Stephanos Ioannidis | 50519ce | 2020-02-11 15:47:59 +0900 | [diff] [blame] | 129 | } |
Bradley Bolen | 571d3b5 | 2018-04-18 12:13:06 +0300 | [diff] [blame] | 130 | |
| 131 | /* Set all interrupts to group 0 */ |
Stephanos Ioannidis | 50519ce | 2020-02-11 15:47:59 +0900 | [diff] [blame] | 132 | for (i = GIC_SPI_INT_BASE; i < gic_irqs; i += 32) { |
Bradley Bolen | 571d3b5 | 2018-04-18 12:13:06 +0300 | [diff] [blame] | 133 | sys_write32(0, GICD_IGROUPRn + i / 8); |
Stephanos Ioannidis | 50519ce | 2020-02-11 15:47:59 +0900 | [diff] [blame] | 134 | } |
Bradley Bolen | 571d3b5 | 2018-04-18 12:13:06 +0300 | [diff] [blame] | 135 | |
| 136 | /* |
| 137 | * Disable all interrupts. Leave the PPI and SGIs alone |
| 138 | * as these enables are banked registers. |
| 139 | */ |
| 140 | for (i = GIC_SPI_INT_BASE; i < gic_irqs; i += 32) { |
Stephanos Ioannidis | 2b44173 | 2019-12-19 14:54:46 +0900 | [diff] [blame] | 141 | #ifndef CONFIG_GIC_V1 |
Bradley Bolen | 571d3b5 | 2018-04-18 12:13:06 +0300 | [diff] [blame] | 142 | sys_write32(0xffffffff, GICD_ICACTIVERn + i / 8); |
Stephanos Ioannidis | 2b44173 | 2019-12-19 14:54:46 +0900 | [diff] [blame] | 143 | #endif |
Bradley Bolen | 571d3b5 | 2018-04-18 12:13:06 +0300 | [diff] [blame] | 144 | sys_write32(0xffffffff, GICD_ICENABLERn + i / 8); |
| 145 | } |
| 146 | |
| 147 | /* |
| 148 | * Enable the forwarding of pending interrupts |
| 149 | * from the Distributor to the CPU interfaces |
| 150 | */ |
Stephanos Ioannidis | 2b44173 | 2019-12-19 14:54:46 +0900 | [diff] [blame] | 151 | sys_write32(1, GICD_CTLR); |
Bradley Bolen | 571d3b5 | 2018-04-18 12:13:06 +0300 | [diff] [blame] | 152 | } |
| 153 | |
| 154 | static void gic_cpu_init(void) |
| 155 | { |
| 156 | int i; |
Kumar Gala | a1b77fd | 2020-05-27 11:26:57 -0500 | [diff] [blame] | 157 | uint32_t val; |
Bradley Bolen | 571d3b5 | 2018-04-18 12:13:06 +0300 | [diff] [blame] | 158 | |
| 159 | /* |
| 160 | * Deal with the banked PPI and SGI interrupts - disable all |
| 161 | * PPI interrupts, ensure all SGI interrupts are enabled. |
| 162 | */ |
Stephanos Ioannidis | 2b44173 | 2019-12-19 14:54:46 +0900 | [diff] [blame] | 163 | #ifndef CONFIG_GIC_V1 |
Bradley Bolen | 571d3b5 | 2018-04-18 12:13:06 +0300 | [diff] [blame] | 164 | sys_write32(0xffffffff, GICD_ICACTIVERn); |
Stephanos Ioannidis | 2b44173 | 2019-12-19 14:54:46 +0900 | [diff] [blame] | 165 | #endif |
Bradley Bolen | 571d3b5 | 2018-04-18 12:13:06 +0300 | [diff] [blame] | 166 | sys_write32(0xffff0000, GICD_ICENABLERn); |
| 167 | sys_write32(0x0000ffff, GICD_ISENABLERn); |
| 168 | |
| 169 | /* |
| 170 | * Set priority on PPI and SGI interrupts |
| 171 | */ |
Stephanos Ioannidis | 50519ce | 2020-02-11 15:47:59 +0900 | [diff] [blame] | 172 | for (i = 0; i < 32; i += 4) { |
Bradley Bolen | 571d3b5 | 2018-04-18 12:13:06 +0300 | [diff] [blame] | 173 | sys_write32(0xa0a0a0a0, GICD_IPRIORITYRn + i); |
Stephanos Ioannidis | 50519ce | 2020-02-11 15:47:59 +0900 | [diff] [blame] | 174 | } |
Bradley Bolen | 571d3b5 | 2018-04-18 12:13:06 +0300 | [diff] [blame] | 175 | |
| 176 | sys_write32(0xf0, GICC_PMR); |
| 177 | |
| 178 | /* |
| 179 | * Enable interrupts and signal them using the IRQ signal. |
| 180 | */ |
Stephanos Ioannidis | 2b44173 | 2019-12-19 14:54:46 +0900 | [diff] [blame] | 181 | val = sys_read32(GICC_CTLR); |
| 182 | #ifndef CONFIG_GIC_V1 |
| 183 | val &= ~GICC_CTLR_BYPASS_MASK; |
| 184 | #endif |
| 185 | val |= GICC_CTLR_ENABLE_MASK; |
| 186 | sys_write32(val, GICC_CTLR); |
Bradley Bolen | 571d3b5 | 2018-04-18 12:13:06 +0300 | [diff] [blame] | 187 | } |
| 188 | |
Bradley Bolen | 571d3b5 | 2018-04-18 12:13:06 +0300 | [diff] [blame] | 189 | /** |
| 190 | * |
| 191 | * @brief Initialize the GIC device driver |
| 192 | * |
| 193 | * |
| 194 | * @return N/A |
| 195 | */ |
| 196 | #define GIC_PARENT_IRQ 0 |
| 197 | #define GIC_PARENT_IRQ_PRI 0 |
| 198 | #define GIC_PARENT_IRQ_FLAGS 0 |
Carlo Caione | 0d1a6dc | 2020-12-11 11:03:48 +0100 | [diff] [blame] | 199 | int arm_gic_init(const struct device *unused) |
Bradley Bolen | 571d3b5 | 2018-04-18 12:13:06 +0300 | [diff] [blame] | 200 | { |
Carlo Caione | 0d1a6dc | 2020-12-11 11:03:48 +0100 | [diff] [blame] | 201 | ARG_UNUSED(unused); |
| 202 | |
Bradley Bolen | 571d3b5 | 2018-04-18 12:13:06 +0300 | [diff] [blame] | 203 | /* Init of Distributor interface registers */ |
| 204 | gic_dist_init(); |
| 205 | |
| 206 | /* Init CPU interface registers */ |
| 207 | gic_cpu_init(); |
| 208 | |
| 209 | return 0; |
| 210 | } |
Carlo Caione | 0d1a6dc | 2020-12-11 11:03:48 +0100 | [diff] [blame] | 211 | |
| 212 | SYS_INIT(arm_gic_init, PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT); |