hirtos_1.0.0_e7372ec1/hirtos_separation_kernel/src/porting_layer/cpu_architectures/armv8r_aarch32/hirtos_cpu_arch_interface-tick_timer-hypervisor.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
--
--  Copyright (c) 2022-2023, German Rivera
--
--
--  SPDX-License-Identifier: Apache-2.0
--

--
--  @summary RTOS to target platform interface - Hypervisor Tick timer driver
--

with HiRTOS.Separation_Kernel.Interrupt_Handling;
with HiRTOS_Cpu_Arch_Interface.Interrupt_Controller;
with HiRTOS_Cpu_Arch_Interface.Interrupts;
with System.Machine_Code;
with System.Storage_Elements;

package body HiRTOS_Cpu_Arch_Interface.Tick_Timer.Hypervisor with SPARK_Mode => On is

   procedure Tick_Timer_Interrupt_Handler (Arg : System.Address)
      with Pre => Cpu_In_Hypervisor_Mode;

   procedure Initialize is
      CNTP_CTL_Value : CNTP_CTL_Type;
      CNTFRQ_Value : CNTFRQ_Type;
   begin
      CNTFRQ_Value := Get_CNTFRQ;
      pragma Assert (CNTFRQ_Value = CNTFRQ_Type (HiRTOS_Platform_Parameters.System_Clock_Frequency_Hz));

      --
      --  Make sure that the timer is disabled:
      --
      --  NOTE: Even if the ENABLE bit is 0, CNTPCT continues to count.
      --
      CNTP_CTL_Value := Get_CNTHP_CTL;
      CNTP_CTL_Value.ENABLE := Timer_Disabled;
      CNTP_CTL_Value.IMASK := Timer_Interrupt_Masked;
      Set_CNTHP_CTL (CNTP_CTL_Value);

      --
      --  NOTE: the generic timer interrupt is enabled in the GIC and in the
      --  peripheral when Start_Timer is called and disabled when Stop_Timer
      --  is called.
      --
   end Initialize;

   procedure Start_Timer (Expiration_Time_Us : HiRTOS.Relative_Time_Us_Type) is
      use System.Storage_Elements;
      CNTP_CTL_Value : CNTP_CTL_Type;
      CNTP_TVAL_Value : constant CNTP_TVAL_Type :=
         CNTP_TVAL_Type (Expiration_Time_Us * Timer_Counter_Cycles_Per_Us);
      Timer_Interrupt_Id : constant
         HiRTOS_Cpu_Arch_Interface.Interrupt_Controller.Interrupt_Id_Type :=
         HiRTOS_Cpu_Arch_Interface.Interrupts.Generic_Hypervisor_Timer_Interrupt_Id;
   begin
      pragma Assert (CNTP_TVAL_Value >= CNTP_TVAL_Type (Expiration_Time_Us));

      Set_CNTHP_TVAL (CNTP_TVAL_Value);

      --
      --  Enable tick timer interrupt in the generic timer peipheral:
      --
      --  NOTE: Section G8.7.16 of ARMv8 Architecture Reference Manual says:
      --  "When the value of the ENABLE bit is 1, ISTATUS indicates whether
      --   the timer condition is met. ISTATUS takes no account of the value
      --   of the IMASK bit. If the value of ISTATUS is 1 and the value of
      --   IMASK is 0 then the timer interrupt is asserted.
      --   Setting the ENABLE bit to 0 disables the timer output signal,
      --   but the timer value accessible from CNTP_TVAL continues to
      --   count down. Disabling the output signal might be a
      --   power-saving option."
      --
      CNTP_CTL_Value := Get_CNTHP_CTL;
      CNTP_CTL_Value.ENABLE := Timer_Enabled;
      CNTP_CTL_Value.IMASK := Timer_Interrupt_Not_Masked;
      Set_CNTHP_CTL (CNTP_CTL_Value);

      --  Configure generic hypervisor timer interrupt in the GIC:
      Interrupt_Controller.Configure_Internal_Interrupt (
         Internal_Interrupt_Id => Timer_Interrupt_Id,
         Priority => HiRTOS_Cpu_Arch_Interface.Interrupts.Interrupt_Priorities (Timer_Interrupt_Id),
         Cpu_Interrupt_Line => Interrupt_Controller.Cpu_Interrupt_Fiq,
         Trigger_Mode => Interrupt_Controller.Interrupt_Level_Sensitive,
         Interrupt_Handler_Entry_Point => Tick_Timer_Interrupt_Handler'Access,
         Interrupt_Handler_Arg => To_Address (Integer_Address (CNTP_TVAL_Value)));

      --  Enable generic timer interrupt in the GIC:
      Interrupt_Controller.Enable_Internal_Interrupt (Timer_Interrupt_Id);
   end Start_Timer;

   procedure Stop_Timer is
      CNTP_CTL_Value : CNTP_CTL_Type;
   begin
      --  Disable generic timer interrupt in the GIC:
      Interrupt_Controller.Disable_Internal_Interrupt (
         HiRTOS_Cpu_Arch_Interface.Interrupts.Generic_Hypervisor_Timer_Interrupt_Id);

      --  Disable tick timer interrupt in the generic timer peipheral:
      CNTP_CTL_Value := Get_CNTHP_CTL;
      CNTP_CTL_Value.ENABLE := Timer_Disabled;
      CNTP_CTL_Value.IMASK := Timer_Interrupt_Masked;
      Set_CNTHP_CTL (CNTP_CTL_Value);
   end Stop_Timer;

   ----------------------------------------------------------------------------
   --  Private Subprograms
   ----------------------------------------------------------------------------

   procedure Tick_Timer_Interrupt_Handler (Arg : System.Address)
   is
      use System.Storage_Elements;
      CNTP_TVAL_Value : constant CNTP_TVAL_Type := CNTP_TVAL_Type (To_Integer (Arg));
   begin
      --
      --  NOTE: Setting CNTP_TVAL here serves two purposes:
      --  - Clear the timer interrupt at the timer peripheral
      --  - Set the timer to fire for the next tick
      --
      Set_CNTHP_TVAL (CNTP_TVAL_Value);
      HiRTOS.Separation_Kernel.Interrupt_Handling.Tick_Timer_Interrupt_Handler;
   end Tick_Timer_Interrupt_Handler;

   function Get_CNTHP_CTL return CNTP_CTL_Type is
      CNTP_CTL_Value : CNTP_CTL_Type;
   begin
      System.Machine_Code.Asm (
         "mrc p15, 4, %0, c14, c2, 1",
         Outputs => CNTP_CTL_Type'Asm_Output ("=r", CNTP_CTL_Value), --  %0
         Volatile => True);

      return CNTP_CTL_Value;
   end Get_CNTHP_CTL;

   procedure Set_CNTHP_CTL (CNTP_CTL_Value : CNTP_CTL_Type) is
   begin
      System.Machine_Code.Asm (
         "mcr p15, 4, %0, c14, c2, 1",
         Inputs => CNTP_CTL_Type'Asm_Input ("r", CNTP_CTL_Value), --  %0
         Volatile => True);
   end Set_CNTHP_CTL;

   function Get_CNTHP_TVAL return CNTP_TVAL_Type is
      CNTP_TVAL_Value : CNTP_TVAL_Type;
   begin
      System.Machine_Code.Asm (
         "mrc p15, 4, %0, c14, c2, 0",
         Outputs => CNTP_TVAL_Type'Asm_Output ("=r", CNTP_TVAL_Value), --  %0
         Volatile => True);

      return CNTP_TVAL_Value;
   end Get_CNTHP_TVAL;

   procedure Set_CNTHP_TVAL (CNTP_TVAL_Value : CNTP_TVAL_Type) is
   begin
      System.Machine_Code.Asm (
         "mcr p15, 4, %0, c14, c2, 0",
         Inputs => CNTP_TVAL_Type'Asm_Input ("r", CNTP_TVAL_Value), --  %0
         Volatile => True);
   end Set_CNTHP_TVAL;

end HiRTOS_Cpu_Arch_Interface.Tick_Timer.Hypervisor;