1 /* 2 * (C) Copyright 2017 Rockchip Electronics Co., Ltd 3 * 4 * SPDX-License-Identifier: GPL-2.0+ 5 */ 6 7 #include <asm/io.h> 8 #include <asm/gic.h> 9 #include <config.h> 10 #include <irq-generic.h> 11 #include "irq-gic.h" 12 13 typedef enum INT_TRIG { 14 INT_LEVEL_TRIGGER, 15 INT_EDGE_TRIGGER 16 } eINT_TRIG; 17 18 typedef enum INT_SECURE { 19 INT_SECURE, 20 INT_NOSECURE 21 } eINT_SECURE; 22 23 typedef enum INT_SIGTYPE { 24 INT_SIGTYPE_IRQ, 25 INT_SIGTYPE_FIQ 26 } eINT_SIGTYPE; 27 28 #define g_gicd ((pGICD_REG)GICD_BASE) 29 #define g_gicc ((pGICC_REG)GICC_BASE) 30 31 __maybe_unused static u8 g_gic_cpumask = 0x01; 32 33 static inline void int_set_prio_filter(u32 nprio) 34 { 35 g_gicc->iccpmr = (nprio & 0xff); 36 } 37 38 static inline void int_enable_distributor(void) 39 { 40 g_gicd->icddcr = 0x01; 41 } 42 43 static inline void int_disable_distributor(void) 44 { 45 g_gicd->icddcr = 0x00; 46 } 47 48 static inline void int_enable_secure_signal(void) 49 { 50 g_gicc->iccicr |= 0x01; 51 } 52 53 static inline void int_disable_secure_signal(void) 54 { 55 g_gicc->iccicr &= (~0x01); 56 } 57 58 static inline void int_enable_nosecure_signal(void) 59 { 60 g_gicc->iccicr |= 0x02; 61 } 62 63 static inline void int_disable_nosecure_signal(void) 64 { 65 g_gicc->iccicr &= (~0x02); 66 } 67 68 static int gic_irq_set_trigger(int irq, eINT_TRIG ntrig) 69 { 70 u32 group, offset; 71 72 if (irq >= PLATFORM_GIC_IRQS_NR) 73 return -EINVAL; 74 75 group = irq / 16; 76 offset = irq % 16; 77 78 if (ntrig == INT_LEVEL_TRIGGER) 79 g_gicd->icdicfr[group] &= (~(1 << (2 * offset + 1))); 80 else 81 g_gicd->icdicfr[group] |= (1 << (2 * offset + 1)); 82 83 return 0; 84 } 85 86 __maybe_unused static int gic_irq_set_pending(int irq) 87 { 88 u32 group, offset; 89 90 if (irq >= PLATFORM_GIC_IRQS_NR) 91 return -EINVAL; 92 93 group = irq / 32; 94 offset = irq % 32; 95 g_gicd->icdispr[group] = (0x1 << offset); 96 97 return 0; 98 } 99 100 __maybe_unused static int gic_irq_clear_pending(int irq) 101 { 102 u32 group, offset; 103 104 if (irq >= PLATFORM_GIC_IRQS_NR) 105 return -EINVAL; 106 107 group = irq / 32; 108 offset = irq % 32; 109 g_gicd->icdicpr[group] = (0x1 << offset); 110 111 return 0; 112 } 113 114 __maybe_unused static int gic_irq_set_secure(int irq, eINT_SECURE nsecure) 115 { 116 u32 group, offset; 117 118 if (irq >= PLATFORM_GIC_IRQS_NR) 119 return -EINVAL; 120 121 group = irq / 32; 122 offset = irq % 32; 123 g_gicd->icdiser[group] |= nsecure << offset; 124 125 return 0; 126 } 127 128 __maybe_unused static u32 gic_get_cpumask(void) 129 { 130 u32 mask = 0, i; 131 132 for (i = mask = 0; i < 32; i += 4) { 133 mask = g_gicd->itargetsr[i]; 134 mask |= mask >> 16; 135 mask |= mask >> 8; 136 if (mask) 137 break; 138 } 139 140 if (!mask) 141 printf("GIC CPU mask not found.\n"); 142 143 debug("GIC CPU mask = 0x%08x\n", mask); 144 145 return mask; 146 } 147 148 static int gic_irq_enable(int irq) 149 { 150 #ifdef CONFIG_GICV2 151 u32 shift = (irq % 4) * 8; 152 u32 offset = (irq / 4); 153 u32 M, N; 154 155 if (irq >= PLATFORM_GIC_IRQS_NR) 156 return -EINVAL; 157 158 M = irq / 32; 159 N = irq % 32; 160 161 g_gicc->iccicr &= (~0x08); 162 g_gicd->icdiser[M] = (0x1 << N); 163 g_gicd->itargetsr[offset] &= ~(0xFF << shift); 164 g_gicd->itargetsr[offset] |= (g_gic_cpumask << shift); 165 #else 166 u32 M, N; 167 168 if (irq >= PLATFORM_GIC_IRQS_NR) 169 return -EINVAL; 170 171 M = irq / 32; 172 N = irq % 32; 173 g_gicd->icdiser[M] = (0x1 << N); 174 #endif 175 176 return 0; 177 } 178 179 static int gic_irq_disable(int irq) 180 { 181 u32 group, offset; 182 183 if (irq >= PLATFORM_GIC_IRQS_NR) 184 return -EINVAL; 185 186 group = irq / 32; 187 offset = irq % 32; 188 g_gicd->icdicer[group] = (0x1 << offset); 189 190 return 0; 191 } 192 193 /* 194 * irq_set_type - set the irq trigger type for an irq 195 * 196 * @irq: irq number 197 * @type: IRQ_TYPE_{LEVEL,EDGE}_* value - see asm/arch/irq.h 198 */ 199 static int gic_irq_set_type(int irq, unsigned int type) 200 { 201 unsigned int int_type; 202 203 switch (type) { 204 case IRQ_TYPE_EDGE_RISING: 205 case IRQ_TYPE_EDGE_FALLING: 206 int_type = INT_EDGE_TRIGGER; 207 break; 208 case IRQ_TYPE_LEVEL_HIGH: 209 case IRQ_TYPE_LEVEL_LOW: 210 int_type = INT_LEVEL_TRIGGER; 211 break; 212 default: 213 return -EINVAL; 214 } 215 216 gic_irq_set_trigger(irq, int_type); 217 218 return 0; 219 } 220 221 static void gic_irq_eoi(int irq) 222 { 223 #ifdef CONFIG_GICV2 224 g_gicc->icceoir = irq; 225 #else 226 asm volatile("msr " __stringify(ICC_EOIR1_EL1) ", %0" 227 : : "r" ((u64)irq)); 228 asm volatile("msr " __stringify(ICC_DIR_EL1) ", %0" 229 : : "r" ((u64)irq)); 230 isb(); 231 #endif 232 } 233 234 static int gic_irq_get(void) 235 { 236 #ifdef CONFIG_GICV2 237 return g_gicc->icciar & 0x3ff; /* bit9 - bit0 */ 238 #else 239 u64 irqstat; 240 241 asm volatile("mrs %0, " __stringify(ICC_IAR1_EL1) : "=r" (irqstat)); 242 return (u32)irqstat & 0x3ff; 243 #endif 244 } 245 246 static int gic_irq_init(void) 247 { 248 /* GICV3 done in: arch/arm/cpu/armv8/start.S */ 249 #ifdef CONFIG_GICV2 250 /* end of interrupt */ 251 g_gicc->icceoir = PLATFORM_GIC_IRQS_NR; 252 253 /* disable gicc and gicd */ 254 g_gicc->iccicr = 0x00; 255 g_gicd->icddcr = 0x00; 256 257 /* enable interrupt */ 258 g_gicd->icdicer[0] = 0xFFFFFFFF; 259 g_gicd->icdicer[1] = 0xFFFFFFFF; 260 g_gicd->icdicer[2] = 0xFFFFFFFF; 261 g_gicd->icdicer[3] = 0xFFFFFFFF; 262 g_gicd->icdicfr[3] &= ~(1 << 1); 263 264 /* set interrupt priority threhold min: 256 */ 265 int_set_prio_filter(0xff); 266 int_enable_secure_signal(); 267 int_enable_nosecure_signal(); 268 int_enable_distributor(); 269 270 g_gic_cpumask = gic_get_cpumask(); 271 #endif 272 273 return 0; 274 } 275 276 static struct irq_chip gic_irq_chip = { 277 .name = "gic-irq-chip", 278 .irq_init = gic_irq_init, 279 .irq_get = gic_irq_get, 280 .irq_enable = gic_irq_enable, 281 .irq_disable = gic_irq_disable, 282 .irq_eoi = gic_irq_eoi, 283 .irq_set_type = gic_irq_set_type, 284 }; 285 286 struct irq_chip *arch_gic_irq_init(void) 287 { 288 return &gic_irq_chip; 289 } 290