-- -- 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; either version 2 of the License, or -- (at your option) any later version. -- -- 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; with HW.GFX.GMA.Power_And_Clocks; 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: HSW/BDW with LPT-H might need a workaround for the 2x bit clock. 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, Config.Variable)), Depends => ((Registers.Register_State, Response, Response_Length, Success) => (Registers.Register_State, Config.Variable, 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; DP_AUX_CTL_2x_Clock_Mask : constant := (if Config.Has_PCH_Aux_Channels then DP_AUX_CTL_2X_BIT_CLOCK_DIV_MASK else 0); DP_AUX_CTL_2x_Clock : constant Word32 := (if Config.Has_PCH_Aux_Channels then (if Port = DP_A then Word32 ((Config.CDClk + 1_000_000) / 2_000_000) else Word32 ((Config.Raw_Clock + 1_000_000) / 2_000_000)) elsif Config.Has_GMCH_RawClk then Word32 (Div_Round_Closest (Config.Raw_Clock, 2_000_000)) else 0); 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 or DP_AUX_CTL_2x_Clock_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) or DP_AUX_CTL_2x_Clock); 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;