Initial upstream commit

The history contained unlicensed code so everything got squashed, sorry.

Change-Id: I9f5775208f9df6fb29074bf3bc498f68cb17b3a0
Signed-off-by: Nico Huber <nico.huber@secunet.com>
diff --git a/common/hw-gfx-gma-dp_aux_request.adb b/common/hw-gfx-gma-dp_aux_request.adb
new file mode 100644
index 0000000..df4e048
--- /dev/null
+++ b/common/hw-gfx-gma-dp_aux_request.adb
@@ -0,0 +1,330 @@
+--
+-- Copyright (C) 2015-2016 secunet Security Networks AG
+--
+-- This program is free software; you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation; version 2 of the License.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU General Public License for more details.
+--
+
+with HW.Time;
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+with HW.GFX.GMA.Config;
+with HW.GFX.GMA.Registers;
+
+use type HW.Word8;
+use type HW.GFX.GMA.Registers.Registers_Invalid_Index;
+
+package body HW.GFX.GMA.DP_Aux_Request is
+
+   DP_AUX_CTL_SEND_BUSY             : constant :=    1 * 2 ** 31;
+   DP_AUX_CTL_DONE                  : constant :=    1 * 2 ** 30;
+   DP_AUX_CTL_INTERRUPT_ON_DONE     : constant :=    1 * 2 ** 29;
+   DP_AUX_CTL_TIME_OUT_ERROR        : constant :=    1 * 2 ** 28;
+   DP_AUX_CTL_TIME_OUT_TIMER_MASK   : constant :=    3 * 2 ** 26;
+   DP_AUX_CTL_TIME_OUT_TIMER_400US  : constant :=    0 * 2 ** 26;
+   DP_AUX_CTL_TIME_OUT_TIMER_600US  : constant :=    1 * 2 ** 26;
+   DP_AUX_CTL_TIME_OUT_TIMER_800US  : constant :=    2 * 2 ** 26;
+   DP_AUX_CTL_TIME_OUT_TIMER_1600US : constant :=    3 * 2 ** 26;
+   DP_AUX_CTL_RECEIVE_ERROR         : constant :=    1 * 2 ** 25;
+   DP_AUX_CTL_MESSAGE_SIZE_MASK     : constant :=   31 * 2 ** 20;
+   DP_AUX_CTL_MESSAGE_SIZE_SHIFT    : constant :=        2 ** 20;
+   DP_AUX_CTL_PRECHARGE_TIME_MASK   : constant :=   15 * 2 ** 16;
+   DP_AUX_CTL_PRECHARGE_TIME_SHIFT  : constant :=        2 ** 16;
+   DP_AUX_CTL_2X_BIT_CLOCK_DIV_MASK : constant := 2047 * 2 **  0;
+   -- TODO: 2x bit clock divider should be programmed once before any training.
+
+   subtype DP_AUX_CTL_MESSAGE_SIZE_T is Natural range 1 .. 20;
+   function DP_AUX_CTL_MESSAGE_SIZE
+     (Message_Length : DP_AUX_CTL_MESSAGE_SIZE_T)
+      return Word32;
+
+   DDI_AUX_MUTEX_MUTEX_ENABLE : constant := 1 * 2 ** 31;
+   DDI_AUX_MUTEX_MUTEX_STATUS : constant := 1 * 2 ** 30;
+
+   type AUX_CH_Data_Regs is new Positive range 1 .. 5;
+
+   type AUX_CH_Data_Regs_Array is
+      array (AUX_CH_Data_Regs) of Registers.Registers_Index;
+
+   type AUX_CH_Registers is record
+      CTL   : Registers.Registers_Index;
+      DATA  : AUX_CH_Data_Regs_Array;
+      MUTEX : Registers.Registers_Invalid_Index;
+   end record;
+
+   type AUX_CH_Registers_Array is array (DP_Port) of AUX_CH_Registers;
+
+   AUX_CH : constant AUX_CH_Registers_Array :=
+     (if Config.Has_PCH_Aux_Channels then
+         AUX_CH_Registers_Array'
+        (DP_A => AUX_CH_Registers'
+           (CTL   => Registers.DP_AUX_CTL_A,
+            DATA  => AUX_CH_Data_Regs_Array'
+              (1  => Registers.DP_AUX_DATA_A_1,
+               2  => Registers.DP_AUX_DATA_A_2,
+               3  => Registers.DP_AUX_DATA_A_3,
+               4  => Registers.DP_AUX_DATA_A_4,
+               5  => Registers.DP_AUX_DATA_A_5),
+            MUTEX => Registers.Invalid_Register),
+         DP_B => AUX_CH_Registers'
+           (CTL   => Registers.PCH_DP_AUX_CTL_B,
+            DATA  => AUX_CH_Data_Regs_Array'
+              (1  => Registers.PCH_DP_AUX_DATA_B_1,
+               2  => Registers.PCH_DP_AUX_DATA_B_2,
+               3  => Registers.PCH_DP_AUX_DATA_B_3,
+               4  => Registers.PCH_DP_AUX_DATA_B_4,
+               5  => Registers.PCH_DP_AUX_DATA_B_5),
+            MUTEX => Registers.Invalid_Register),
+         DP_C => AUX_CH_Registers'
+           (CTL   => Registers.PCH_DP_AUX_CTL_C,
+            DATA  => AUX_CH_Data_Regs_Array'
+              (1  => Registers.PCH_DP_AUX_DATA_C_1,
+               2  => Registers.PCH_DP_AUX_DATA_C_2,
+               3  => Registers.PCH_DP_AUX_DATA_C_3,
+               4  => Registers.PCH_DP_AUX_DATA_C_4,
+               5  => Registers.PCH_DP_AUX_DATA_C_5),
+            MUTEX => Registers.Invalid_Register),
+         DP_D => AUX_CH_Registers'
+           (CTL   => Registers.PCH_DP_AUX_CTL_D,
+            DATA  => AUX_CH_Data_Regs_Array'
+              (1  => Registers.PCH_DP_AUX_DATA_D_1,
+               2  => Registers.PCH_DP_AUX_DATA_D_2,
+               3  => Registers.PCH_DP_AUX_DATA_D_3,
+               4  => Registers.PCH_DP_AUX_DATA_D_4,
+               5  => Registers.PCH_DP_AUX_DATA_D_5),
+            MUTEX => Registers.Invalid_Register))
+      else
+         AUX_CH_Registers_Array'
+        (DP_A => AUX_CH_Registers'
+           (CTL   => Registers.DDI_AUX_CTL_A,
+            DATA  => AUX_CH_Data_Regs_Array'
+              (1  => Registers.DDI_AUX_DATA_A_1,
+               2  => Registers.DDI_AUX_DATA_A_2,
+               3  => Registers.DDI_AUX_DATA_A_3,
+               4  => Registers.DDI_AUX_DATA_A_4,
+               5  => Registers.DDI_AUX_DATA_A_5),
+            MUTEX => Registers.DDI_AUX_MUTEX_A),
+         DP_B => AUX_CH_Registers'
+           (CTL   => Registers.DDI_AUX_CTL_B,
+            DATA  => AUX_CH_Data_Regs_Array'
+              (1  => Registers.DDI_AUX_DATA_B_1,
+               2  => Registers.DDI_AUX_DATA_B_2,
+               3  => Registers.DDI_AUX_DATA_B_3,
+               4  => Registers.DDI_AUX_DATA_B_4,
+               5  => Registers.DDI_AUX_DATA_B_5),
+            MUTEX => Registers.DDI_AUX_MUTEX_B),
+         DP_C => AUX_CH_Registers'
+           (CTL   => Registers.DDI_AUX_CTL_C,
+            DATA  => AUX_CH_Data_Regs_Array'
+              (1  => Registers.DDI_AUX_DATA_C_1,
+               2  => Registers.DDI_AUX_DATA_C_2,
+               3  => Registers.DDI_AUX_DATA_C_3,
+               4  => Registers.DDI_AUX_DATA_C_4,
+               5  => Registers.DDI_AUX_DATA_C_5),
+            MUTEX => Registers.DDI_AUX_MUTEX_C),
+         DP_D => AUX_CH_Registers'
+           (CTL   => Registers.DDI_AUX_CTL_D,
+            DATA  => AUX_CH_Data_Regs_Array'
+              (1  => Registers.DDI_AUX_DATA_D_1,
+               2  => Registers.DDI_AUX_DATA_D_2,
+               3  => Registers.DDI_AUX_DATA_D_3,
+               4  => Registers.DDI_AUX_DATA_D_4,
+               5  => Registers.DDI_AUX_DATA_D_5),
+            MUTEX => Registers.DDI_AUX_MUTEX_D)));
+
+   ----------------------------------------------------------------------------
+
+   function DP_AUX_CTL_MESSAGE_SIZE
+     (Message_Length : DP_AUX_CTL_MESSAGE_SIZE_T)
+      return Word32
+   is
+   begin
+      return Word32 (Message_Length) * DP_AUX_CTL_MESSAGE_SIZE_SHIFT;
+   end DP_AUX_CTL_MESSAGE_SIZE;
+
+   ----------------------------------------------------------------------------
+
+   procedure Aux_Request_Low
+     (Port              : in     DP_Port;
+      Request           : in     DP_Defs.Aux_Request;
+      Request_Length    : in     DP_Defs.Aux_Request_Length;
+      Response          :    out DP_Defs.Aux_Response;
+      Response_Length   :    out DP_Defs.Aux_Response_Length;
+      Success           :    out Boolean)
+   with
+      Global => (In_Out => Registers.Register_State,
+                 Input  => Time.State),
+      Depends =>
+        ((Registers.Register_State,
+          Response,
+          Response_Length,
+          Success)
+             =>
+               (Registers.Register_State,
+                Time.State,
+                Port,
+                Request,
+                Request_Length))
+   is
+      procedure Write_Data_Reg
+        (Register : in     Registers.Registers_Index;
+         Buf      : in     DP_Defs.Aux_Request;
+         Length   : in     DP_Defs.Aux_Request_Length;
+         Offset   : in     DP_Defs.Aux_Request_Index)
+      is
+         Value : Word32;
+         Count : Natural;
+      begin
+         if Offset < Length then
+            if Length - Offset > 4 then
+               Count := 4;
+            else
+               Count := Length - Offset;
+            end if;
+
+            Value := 0;
+            for Idx in DP_Defs.Aux_Request_Index range 0 .. Count - 1 loop
+               Value := Value or
+                  Shift_Left (Word32 (Buf (Offset + Idx)), (3 - Idx) * 8);
+            end loop;
+            Registers.Write (Register => Register, Value => Value);
+         end if;
+      end Write_Data_Reg;
+
+      procedure Read_Data_Reg
+        (Register : in     Registers.Registers_Index;
+         Buf      : in out DP_Defs.Aux_Response;
+         Length   : in     DP_Defs.Aux_Response_Length;
+         Offset   : in     DP_Defs.Aux_Response_Index)
+      is
+         Value : Word32;
+         Count : DP_Defs.Aux_Response_Length;
+      begin
+         if Offset < Length then
+            if Length - Offset > 4 then
+               Count := 4;
+            else
+               Count := Length - Offset;
+            end if;
+
+            Registers.Read (Register => Register, Value => Value);
+            for Idx in 0 .. Count - 1 loop
+               Buf (Offset + Idx) :=
+                  Word8 (Shift_Right (Value, (3 - Idx) * 8) and 16#ff#);
+            end loop;
+         end if;
+      end Read_Data_Reg;
+
+      Busy : Boolean;
+      Status : Word32;
+   begin
+      Response := (others => 0); -- Don't care
+      Response_Length := DP_Defs.Aux_Response_Length'First;
+
+      if Config.Need_DP_Aux_Mutex then
+         Registers.Set_Mask
+           (Register => AUX_CH (Port).MUTEX,
+            Mask     => DDI_AUX_MUTEX_MUTEX_ENABLE);
+         Registers.Wait_Set_Mask
+           (Register => AUX_CH (Port).MUTEX,
+            Mask     => DDI_AUX_MUTEX_MUTEX_STATUS);
+      end if;
+
+      Registers.Is_Set_Mask
+        (Register => AUX_CH (Port).CTL,
+         Mask     => DP_AUX_CTL_SEND_BUSY,
+         Result   => Busy);
+      if Busy then
+         Success := False;
+      else
+         for Idx in AUX_CH_Data_Regs loop
+            Write_Data_Reg
+              (Register => AUX_CH (Port).DATA (Idx),
+               Buf      => Request,
+               Length   => Request_Length,
+               Offset   => (Natural (Idx) - 1) * 4);
+         end loop;
+
+         Registers.Unset_And_Set_Mask
+           (Register    => AUX_CH (Port).CTL,
+            Mask_Unset  => DP_AUX_CTL_INTERRUPT_ON_DONE or
+                           DP_AUX_CTL_TIME_OUT_TIMER_MASK or
+                           DP_AUX_CTL_MESSAGE_SIZE_MASK,
+            Mask_Set    => DP_AUX_CTL_SEND_BUSY or          -- starts transfer
+                           DP_AUX_CTL_DONE or               -- clears the status
+                           DP_AUX_CTL_TIME_OUT_ERROR or     -- clears the status
+                           DP_AUX_CTL_RECEIVE_ERROR or      -- clears the status
+                           DP_AUX_CTL_TIME_OUT_TIMER_600US or
+                           DP_AUX_CTL_MESSAGE_SIZE (Request_Length));
+
+         Registers.Wait_Unset_Mask
+           (Register => AUX_CH (Port).CTL,
+            Mask     => DP_AUX_CTL_SEND_BUSY);
+         Registers.Read (Register => AUX_CH (Port).CTL, Value => Status);
+         Success := (Status and
+                     (DP_AUX_CTL_TIME_OUT_ERROR or DP_AUX_CTL_RECEIVE_ERROR))
+                    = 0;
+
+         if Success then
+            Status := (Status and DP_AUX_CTL_MESSAGE_SIZE_MASK)
+                      / DP_AUX_CTL_MESSAGE_SIZE_SHIFT;
+            if Natural (Status) < DP_Defs.Aux_Response_Length'First then
+               Success := False;
+            elsif Natural (Status) > DP_Defs.Aux_Response_Length'Last then
+               Response_Length := DP_Defs.Aux_Response_Length'Last;
+            else
+               Response_Length := Natural (Status);
+            end if;
+         end if;
+
+         if Success then
+            for Idx in AUX_CH_Data_Regs loop
+               Read_Data_Reg
+                 (Register => AUX_CH (Port).DATA (Idx),
+                  Buf      => Response,
+                  Length   => Response_Length,
+                  Offset   => (Natural (Idx) - 1) * 4);
+            end loop;
+         end if;
+      end if;
+
+      if Config.Need_DP_Aux_Mutex then
+         Registers.Unset_And_Set_Mask
+           (Register    => AUX_CH (Port).MUTEX,
+            Mask_Unset  => DDI_AUX_MUTEX_MUTEX_ENABLE,
+            Mask_Set    => DDI_AUX_MUTEX_MUTEX_STATUS);  -- frees the mutex
+      end if;
+   end Aux_Request_Low;
+
+   ----------------------------------------------------------------------------
+
+   procedure Do_Aux_Request
+     (Port              : in     DP_Port;
+      Request           : in     DP_Defs.Aux_Request;
+      Request_Length    : in     DP_Defs.Aux_Request_Length;
+      Response          :    out DP_Defs.Aux_Response;
+      Response_Length   :    out DP_Defs.Aux_Response_Length;
+      Success           :    out Boolean)
+   is
+   begin
+      for Try in Positive range 1 .. 3 loop
+         Aux_Request_Low
+           (Port              => Port,
+            Request           => Request,
+            Request_Length    => Request_Length,
+            Response          => Response,
+            Response_Length   => Response_Length,
+            Success           => Success);
+         exit when Success;
+      end loop;
+   end Do_Aux_Request;
+
+end HW.GFX.GMA.DP_Aux_Request;