gnat_arm_elf_13.2.1_db1e9283/arm-eabi/lib/gnat/embedded-zynq7000/gnarl/s-armgic.adb

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
------------------------------------------------------------------------------
--                                                                          --
--                  GNAT RUN-TIME LIBRARY (GNARL) COMPONENTS                --
--                                                                          --
--                         S Y S T E M . A R M _ G I C                      --
--                                                                          --
--                                  B o d y                                 --
--                                                                          --
--        Copyright (C) 1999-2002 Universidad Politecnica de Madrid         --
--             Copyright (C) 2003-2006 The European Space Agency            --
--                     Copyright (C) 2003-2022, AdaCore                     --
--                                                                          --
-- GNARL is free software; you can  redistribute it  and/or modify it under --
-- terms of the  GNU General Public License as published  by the Free Soft- --
-- ware  Foundation;  either version 3,  or (at your option) any later ver- --
-- sion. GNARL is distributed in the hope that it will be useful, but WITH- --
-- OUT ANY WARRANTY;  without even the  implied warranty of MERCHANTABILITY --
-- or FITNESS FOR A PARTICULAR PURPOSE.                                     --
--                                                                          --
-- As a special exception under Section 7 of GPL version 3, you are granted --
-- additional permissions described in the GCC Runtime Library Exception,   --
-- version 3.1, as published by the Free Software Foundation.               --
--                                                                          --
-- You should have received a copy of the GNU General Public License and    --
-- a copy of the GCC Runtime Library Exception along with this program;     --
-- see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see    --
-- <http://www.gnu.org/licenses/>.                                          --
--                                                                          --
-- GNARL was developed by the GNARL team at Florida State University.       --
-- Extensive contributions were provided by Ada Core Technologies, Inc.     --
--                                                                          --
-- The port of GNARL to bare board targets was initially developed by the   --
-- Real-Time Systems Group at the Technical University of Madrid.           --
--                                                                          --
------------------------------------------------------------------------------

with Interfaces;               use Interfaces;
with System.BB.Parameters;
with System.Machine_Code;

package body System.ARM_GIC is

   use System.BB.Interrupts;

   subtype PRI is Unsigned_8;
   --  Type for GIC interrupt priorities. Note that 0 is the highest
   --  priority, which is reserved for the kernel and has no corresponding
   --  Interrupt_Priority value, and 255 is the lowest. We assume the
   --  PRIGROUP setting is such that the 4 most significant bits determine
   --  the priority group used for preemption. However, if less bits are
   --  implemented, this should still work.

   subtype Banked_Interrupt is Interrupt_ID range 0 .. 31;
   Interrupts_Enabled  : array (Banked_Interrupt) of Boolean :=
                           (others => False);
   Interrupts_Priority : array (Banked_Interrupt) of PRI;

   subtype GIC_Interrupts is Interrupt_ID;

   --  Supports up to 192 interrupts, MaxIRQs need to be a aligned on 32
   --  Number_of_IRQs = Interrupt_ID'Last + 1
   --  Max_IRQs = (Number_Of_IRQs + 31) / 32 * 32
   Max_IRQs : constant Natural :=
     Natural'Min (192,
                  ((Interrupt_ID'Last / 32 + 1) * 32));

   function Reg_Num_32 (Intnum : GIC_Interrupts) return GIC_Interrupts
   is (Intnum / 32);

   type Reg32 is new Unsigned_32 with Volatile;
   type Reg8  is new Unsigned_8  with Volatile;

   --  32-bit registers set
   --  Accessed by 32 bits chunks
   type Bits32_Register_Array is array
     (Natural range 0 .. Max_IRQs / 32 - 1) of Reg32
     with Pack;

   --  Byte registers set
   --  Accessed by 8 bits chunks
   type Byte_Register_Array is array
     (Natural range 0 .. Max_IRQs - 1) of Reg8
     with Pack;

   --  Byte registers set, accessed by 32-bit chunks
   type Byte_Register_Array32 is array
     (Natural range 0 .. Max_IRQs / 4 - 1) of Reg32
     with Pack;

   type GICD_Peripheral is record
      CTLR       : Reg32;
      ISENABLER  : Bits32_Register_Array;
      ICENABLER  : Bits32_Register_Array;
      IPRIORITYR : Byte_Register_Array;
      ITARGETSR  : Byte_Register_Array32;
      ICFGR      : Byte_Register_Array32;
      SGIR       : Reg32;
   end record;

   for GICD_Peripheral use record
      CTLR       at 16#000# range 0 .. 31;
      ISENABLER  at 16#100# range 0 .. Max_IRQs - 1;
      ICENABLER  at 16#180# range 0 .. Max_IRQs - 1;
      IPRIORITYR at 16#400# range 0 .. Max_IRQs * 8 - 1;
      ITARGETSR  at 16#800# range 0 .. Max_IRQs * 8 - 1;
      ICFGR      at 16#C00# range 0 .. Max_IRQs * 8 - 1;
      SGIR       at 16#F00# range 0 .. 31;
   end record;

   type GICC_Peripheral is record
      CTLR : Reg32;
      PMR  : Reg32;
      BPR  : Reg32;
      IAR  : Reg32;
      EOIR : Reg32;
   end record;

   for GICC_Peripheral use record
      CTLR at 16#00# range 0 .. 31;
      PMR  at 16#04# range 0 .. 31;
      BPR  at 16#08# range 0 .. 31;
      IAR  at 16#0C# range 0 .. 31;
      EOIR at 16#10# range 0 .. 31;
   end record;

   GICD : GICD_Peripheral
     with Import, Address => BB.Parameters.GICD_Base_Address;
   GICC : GICC_Peripheral
     with Import, Address => BB.Parameters.GICC_Base_Address;

   function To_PRI (P : Integer) return PRI with Inline;
   --  Return the PRI mask for the given Ada priority. Note that the zero
   --  value here means no mask, so no interrupts are masked.

   function To_Priority (P : PRI) return Interrupt_Priority with Inline;
   --  Given an ARM interrupt priority (PRI value), determine the Ada
   --  priority.
   --  While the value 0 is reserved for the kernel and has no Ada
   --  priority that represents it, Interrupt_Priority'Last is closest.

   ------------
   -- To_PRI --
   ------------

   function To_PRI (P : Integer) return PRI is
   begin
      if P < Interrupt_Priority'First then
         --  Do not mask any interrupt
         return 255;
      else
         --  change range 240 .. 254
         return PRI (Interrupt_Priority'Last - P) * 16;
      end if;
   end To_PRI;

   -----------------
   -- To_Priority --
   -----------------

   function To_Priority (P : PRI) return Interrupt_Priority is
   begin
      if P = 0 then
         return Interrupt_Priority'Last;
      else
         return Interrupt_Priority'Last - Any_Priority'Base (P / 16);
      end if;
   end To_Priority;

   ---------------------
   -- Initialize_GICC --
   ---------------------

   procedure Initialize_GICC
   is
      Int_Mask : Unsigned_32 := 0;
   begin
      --  Core-specific part of the GIC configuration:
      --  The GICC (CPU Interface) is banked for each CPU, so has to be
      --  configured each time.
      --  The PPI and SGI exceptions are also CPU-specific so are banked.
      --  see 4.1.4 in the ARM GIC Architecture Specification v2 document

      --  Mask all interrupts

      GICC.PMR := 0;

      --  Binary point register:
      --  The register defines the point at which the priority value fields
      --  split into two parts.

      GICC.BPR := 3;

      --  Disable banked interrupts by default

      GICD.ICENABLER (0) := 16#FFFF_FFFF#;

      --  Enable the CPU-specific interrupts that have a handler registered.
      --
      --  On CPU0, no interrupt is registered for now so this has no effect.
      --  On the other CPUs, as interrupts are registered via a call to
      --  Interrupts.Install_Interrupt_Handler before the CPUs are started,
      --  the following properly takes care of initializing the interrupt mask
      --  and priorities for those.

      for J in Interrupts_Enabled'Range loop
         if Interrupts_Enabled (J) then
            Int_Mask := Int_Mask or 2 ** J;
            GICD.IPRIORITYR (J) := Reg8 (Interrupts_Priority (J));
         end if;
      end loop;

      if Int_Mask /= 0 then
         GICD.ISENABLER (0) := Reg32 (Int_Mask);
      end if;

      --  Set the Enable Group1 bit to the GICC CTLR register.
      --  The view we have here is a GICv1 or GICv2 version with Security
      --  extension, from a non-secure mode.

      GICC.CTLR := 1;
   end Initialize_GICC;

   ---------------------
   -- Initialize_GICD --
   ---------------------

   procedure Initialize_GICD
   is
   begin
      GICD.CTLR := 0;

      --  Disable all shared Interrupts
      for J in 1 .. GICD.ICENABLER'Last loop
         GICD.ICENABLER (J) := 16#FFFF_FFFF#;
      end loop;

      --  default priority
      for J in GICD.IPRIORITYR'Range loop
         GICD.IPRIORITYR (J) := 0;
      end loop;

      --  Set the target cpu

      --  ITARGETSR(0) is read-only and redirects all IRQs to the currently
      --  running CPU. We initialize the shared interrupts targets to the
      --  same value so that we're sure to receive them.
      for Reg_Num in 8 .. GICD.ITARGETSR'Last loop
         GICD.ITARGETSR (Reg_Num) := GICD.ITARGETSR (0);
      end loop;

      GICD.CTLR := 3;
   end Initialize_GICD;

   -------------------------
   -- Define_IRQ_Triggers --
   -------------------------

   procedure Define_IRQ_Triggers (Config : ICFGR)
   is
   begin
      for J in Config'Range loop
         GICD.ICFGR (J) := Reg32 (Config (J));
      end loop;
   end Define_IRQ_Triggers;

   -----------------
   -- IRQ_Handler --
   -----------------

   procedure IRQ_Handler
   is
      IAR    : constant Unsigned_32 := Unsigned_32 (GICC.IAR);
      Int_Id : constant Unsigned_32 := IAR and 16#3FF#;
   begin
      if Int_Id = 16#3FF# then
         --  Spurious interrupt
         return;
      end if;

      Interrupt_Wrapper (Interrupt_ID (Int_Id));

      --  Clear interrupt request
      GICC.EOIR := Reg32 (IAR);
   end IRQ_Handler;

   -------------------------------
   -- Install_Interrupt_Handler --
   -------------------------------

   procedure Install_Interrupt_Handler
     (Interrupt : Interrupt_ID;
      Prio      : Interrupt_Priority)
   is
   begin
      GICD.IPRIORITYR (Interrupt) := Reg8 (To_PRI (Prio));
      GICD.ISENABLER (Reg_Num_32 (Interrupt)) := 2 ** (Interrupt mod 32);

      --  Handlers are registered before the CPUs are awaken (only the CPU 0
      --  executes Install_Interrupt_Handler.
      --  So we save the registered interrupts to properly initialize the
      --  other CPUs for banked interrupts.
      if Interrupt in Banked_Interrupt then
         Interrupts_Priority (Interrupt) := To_PRI (Prio);
         Interrupts_Enabled (Interrupt)  := True;
      end if;
   end Install_Interrupt_Handler;

   ---------------------------
   -- Priority_Of_Interrupt --
   ---------------------------

   function Priority_Of_Interrupt
     (Interrupt : System.BB.Interrupts.Interrupt_ID)
     return System.Any_Priority
   is
   begin
      return To_Priority (Unsigned_8 (GICD.IPRIORITYR (Interrupt)));
   end Priority_Of_Interrupt;

   --------------------------
   -- Set_Current_Priority --
   --------------------------

   procedure Set_Current_Priority (Priority : Integer) is
   begin
      GICC.PMR := Reg32 (To_PRI (Priority));
   end Set_Current_Priority;

   ----------------
   -- Power_Down --
   ----------------

   procedure Power_Down is
   begin
      System.Machine_Code.Asm ("wfi", Volatile => True);
   end Power_Down;

   --------------
   -- Poke_CPU --
   --------------

   procedure Poke_CPU (CPU_Id         : System.Multiprocessors.CPU;
                       Poke_Interrupt : Interrupt_ID)
   is
   begin
      GICD.SGIR :=
        2 ** (15 + Natural (CPU_Id)) + Reg32 (Poke_Interrupt);
   end Poke_CPU;

end System.ARM_GIC;