| -- |
| -- 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.Default_CDClk_Freq + 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; |