blob: c0570592037c4c5f71a22c7210488b75a2a4c360 [file] [log] [blame]
Nico Huber83693c82016-10-08 22:17:55 +02001--
2-- Copyright (C) 2015-2016 secunet Security Networks AG
3--
4-- This program is free software; you can redistribute it and/or modify
5-- it under the terms of the GNU General Public License as published by
Nico Huber125a29e2016-10-18 00:23:54 +02006-- the Free Software Foundation; either version 2 of the License, or
7-- (at your option) any later version.
Nico Huber83693c82016-10-08 22:17:55 +02008--
9-- This program is distributed in the hope that it will be useful,
10-- but WITHOUT ANY WARRANTY; without even the implied warranty of
11-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12-- GNU General Public License for more details.
13--
14
15with HW.Time;
16
17with HW.Debug;
18with GNAT.Source_Info;
19
20with HW.GFX.GMA.Config;
21with HW.GFX.GMA.Registers;
Arthur Heymansd1988d12018-03-28 16:27:57 +020022with HW.GFX.GMA.Power_And_Clocks;
Nico Huber83693c82016-10-08 22:17:55 +020023
24use type HW.Word8;
25use type HW.GFX.GMA.Registers.Registers_Invalid_Index;
26
27package body HW.GFX.GMA.DP_Aux_Request is
28
29 DP_AUX_CTL_SEND_BUSY : constant := 1 * 2 ** 31;
30 DP_AUX_CTL_DONE : constant := 1 * 2 ** 30;
31 DP_AUX_CTL_INTERRUPT_ON_DONE : constant := 1 * 2 ** 29;
32 DP_AUX_CTL_TIME_OUT_ERROR : constant := 1 * 2 ** 28;
33 DP_AUX_CTL_TIME_OUT_TIMER_MASK : constant := 3 * 2 ** 26;
34 DP_AUX_CTL_TIME_OUT_TIMER_400US : constant := 0 * 2 ** 26;
35 DP_AUX_CTL_TIME_OUT_TIMER_600US : constant := 1 * 2 ** 26;
36 DP_AUX_CTL_TIME_OUT_TIMER_800US : constant := 2 * 2 ** 26;
37 DP_AUX_CTL_TIME_OUT_TIMER_1600US : constant := 3 * 2 ** 26;
38 DP_AUX_CTL_RECEIVE_ERROR : constant := 1 * 2 ** 25;
39 DP_AUX_CTL_MESSAGE_SIZE_MASK : constant := 31 * 2 ** 20;
40 DP_AUX_CTL_MESSAGE_SIZE_SHIFT : constant := 2 ** 20;
41 DP_AUX_CTL_PRECHARGE_TIME_MASK : constant := 15 * 2 ** 16;
42 DP_AUX_CTL_PRECHARGE_TIME_SHIFT : constant := 2 ** 16;
43 DP_AUX_CTL_2X_BIT_CLOCK_DIV_MASK : constant := 2047 * 2 ** 0;
Nico Huberabe3de22016-10-20 15:03:46 +020044 -- TODO: HSW/BDW with LPT-H might need a workaround for the 2x bit clock.
Nico Huber83693c82016-10-08 22:17:55 +020045
46 subtype DP_AUX_CTL_MESSAGE_SIZE_T is Natural range 1 .. 20;
47 function DP_AUX_CTL_MESSAGE_SIZE
48 (Message_Length : DP_AUX_CTL_MESSAGE_SIZE_T)
49 return Word32;
50
51 DDI_AUX_MUTEX_MUTEX_ENABLE : constant := 1 * 2 ** 31;
52 DDI_AUX_MUTEX_MUTEX_STATUS : constant := 1 * 2 ** 30;
53
54 type AUX_CH_Data_Regs is new Positive range 1 .. 5;
55
56 type AUX_CH_Data_Regs_Array is
57 array (AUX_CH_Data_Regs) of Registers.Registers_Index;
58
59 type AUX_CH_Registers is record
60 CTL : Registers.Registers_Index;
61 DATA : AUX_CH_Data_Regs_Array;
62 MUTEX : Registers.Registers_Invalid_Index;
63 end record;
64
65 type AUX_CH_Registers_Array is array (DP_Port) of AUX_CH_Registers;
66
67 AUX_CH : constant AUX_CH_Registers_Array :=
68 (if Config.Has_PCH_Aux_Channels then
69 AUX_CH_Registers_Array'
70 (DP_A => AUX_CH_Registers'
71 (CTL => Registers.DP_AUX_CTL_A,
72 DATA => AUX_CH_Data_Regs_Array'
73 (1 => Registers.DP_AUX_DATA_A_1,
74 2 => Registers.DP_AUX_DATA_A_2,
75 3 => Registers.DP_AUX_DATA_A_3,
76 4 => Registers.DP_AUX_DATA_A_4,
77 5 => Registers.DP_AUX_DATA_A_5),
78 MUTEX => Registers.Invalid_Register),
79 DP_B => AUX_CH_Registers'
80 (CTL => Registers.PCH_DP_AUX_CTL_B,
81 DATA => AUX_CH_Data_Regs_Array'
82 (1 => Registers.PCH_DP_AUX_DATA_B_1,
83 2 => Registers.PCH_DP_AUX_DATA_B_2,
84 3 => Registers.PCH_DP_AUX_DATA_B_3,
85 4 => Registers.PCH_DP_AUX_DATA_B_4,
86 5 => Registers.PCH_DP_AUX_DATA_B_5),
87 MUTEX => Registers.Invalid_Register),
88 DP_C => AUX_CH_Registers'
89 (CTL => Registers.PCH_DP_AUX_CTL_C,
90 DATA => AUX_CH_Data_Regs_Array'
91 (1 => Registers.PCH_DP_AUX_DATA_C_1,
92 2 => Registers.PCH_DP_AUX_DATA_C_2,
93 3 => Registers.PCH_DP_AUX_DATA_C_3,
94 4 => Registers.PCH_DP_AUX_DATA_C_4,
95 5 => Registers.PCH_DP_AUX_DATA_C_5),
96 MUTEX => Registers.Invalid_Register),
97 DP_D => AUX_CH_Registers'
98 (CTL => Registers.PCH_DP_AUX_CTL_D,
99 DATA => AUX_CH_Data_Regs_Array'
100 (1 => Registers.PCH_DP_AUX_DATA_D_1,
101 2 => Registers.PCH_DP_AUX_DATA_D_2,
102 3 => Registers.PCH_DP_AUX_DATA_D_3,
103 4 => Registers.PCH_DP_AUX_DATA_D_4,
104 5 => Registers.PCH_DP_AUX_DATA_D_5),
105 MUTEX => Registers.Invalid_Register))
106 else
107 AUX_CH_Registers_Array'
108 (DP_A => AUX_CH_Registers'
109 (CTL => Registers.DDI_AUX_CTL_A,
110 DATA => AUX_CH_Data_Regs_Array'
111 (1 => Registers.DDI_AUX_DATA_A_1,
112 2 => Registers.DDI_AUX_DATA_A_2,
113 3 => Registers.DDI_AUX_DATA_A_3,
114 4 => Registers.DDI_AUX_DATA_A_4,
115 5 => Registers.DDI_AUX_DATA_A_5),
116 MUTEX => Registers.DDI_AUX_MUTEX_A),
117 DP_B => AUX_CH_Registers'
118 (CTL => Registers.DDI_AUX_CTL_B,
119 DATA => AUX_CH_Data_Regs_Array'
120 (1 => Registers.DDI_AUX_DATA_B_1,
121 2 => Registers.DDI_AUX_DATA_B_2,
122 3 => Registers.DDI_AUX_DATA_B_3,
123 4 => Registers.DDI_AUX_DATA_B_4,
124 5 => Registers.DDI_AUX_DATA_B_5),
125 MUTEX => Registers.DDI_AUX_MUTEX_B),
126 DP_C => AUX_CH_Registers'
127 (CTL => Registers.DDI_AUX_CTL_C,
128 DATA => AUX_CH_Data_Regs_Array'
129 (1 => Registers.DDI_AUX_DATA_C_1,
130 2 => Registers.DDI_AUX_DATA_C_2,
131 3 => Registers.DDI_AUX_DATA_C_3,
132 4 => Registers.DDI_AUX_DATA_C_4,
133 5 => Registers.DDI_AUX_DATA_C_5),
134 MUTEX => Registers.DDI_AUX_MUTEX_C),
135 DP_D => AUX_CH_Registers'
136 (CTL => Registers.DDI_AUX_CTL_D,
137 DATA => AUX_CH_Data_Regs_Array'
138 (1 => Registers.DDI_AUX_DATA_D_1,
139 2 => Registers.DDI_AUX_DATA_D_2,
140 3 => Registers.DDI_AUX_DATA_D_3,
141 4 => Registers.DDI_AUX_DATA_D_4,
142 5 => Registers.DDI_AUX_DATA_D_5),
143 MUTEX => Registers.DDI_AUX_MUTEX_D)));
144
145 ----------------------------------------------------------------------------
146
147 function DP_AUX_CTL_MESSAGE_SIZE
148 (Message_Length : DP_AUX_CTL_MESSAGE_SIZE_T)
149 return Word32
150 is
151 begin
152 return Word32 (Message_Length) * DP_AUX_CTL_MESSAGE_SIZE_SHIFT;
153 end DP_AUX_CTL_MESSAGE_SIZE;
154
155 ----------------------------------------------------------------------------
156
157 procedure Aux_Request_Low
158 (Port : in DP_Port;
159 Request : in DP_Defs.Aux_Request;
160 Request_Length : in DP_Defs.Aux_Request_Length;
161 Response : out DP_Defs.Aux_Response;
162 Response_Length : out DP_Defs.Aux_Response_Length;
163 Success : out Boolean)
164 with
165 Global => (In_Out => Registers.Register_State,
Nico Huber30e84082018-06-10 13:28:05 +0200166 Input => (Time.State, Config.Variable)),
Nico Huber83693c82016-10-08 22:17:55 +0200167 Depends =>
168 ((Registers.Register_State,
169 Response,
170 Response_Length,
171 Success)
172 =>
173 (Registers.Register_State,
Nico Huber30e84082018-06-10 13:28:05 +0200174 Config.Variable,
Nico Huber83693c82016-10-08 22:17:55 +0200175 Time.State,
176 Port,
177 Request,
178 Request_Length))
179 is
180 procedure Write_Data_Reg
181 (Register : in Registers.Registers_Index;
182 Buf : in DP_Defs.Aux_Request;
183 Length : in DP_Defs.Aux_Request_Length;
184 Offset : in DP_Defs.Aux_Request_Index)
185 is
186 Value : Word32;
187 Count : Natural;
188 begin
189 if Offset < Length then
190 if Length - Offset > 4 then
191 Count := 4;
192 else
193 Count := Length - Offset;
194 end if;
195
196 Value := 0;
197 for Idx in DP_Defs.Aux_Request_Index range 0 .. Count - 1 loop
198 Value := Value or
199 Shift_Left (Word32 (Buf (Offset + Idx)), (3 - Idx) * 8);
200 end loop;
201 Registers.Write (Register => Register, Value => Value);
202 end if;
203 end Write_Data_Reg;
204
205 procedure Read_Data_Reg
206 (Register : in Registers.Registers_Index;
207 Buf : in out DP_Defs.Aux_Response;
208 Length : in DP_Defs.Aux_Response_Length;
209 Offset : in DP_Defs.Aux_Response_Index)
210 is
211 Value : Word32;
212 Count : DP_Defs.Aux_Response_Length;
213 begin
214 if Offset < Length then
215 if Length - Offset > 4 then
216 Count := 4;
217 else
218 Count := Length - Offset;
219 end if;
220
221 Registers.Read (Register => Register, Value => Value);
222 for Idx in 0 .. Count - 1 loop
223 Buf (Offset + Idx) :=
224 Word8 (Shift_Right (Value, (3 - Idx) * 8) and 16#ff#);
225 end loop;
226 end if;
227 end Read_Data_Reg;
228
Nico Huberabe3de22016-10-20 15:03:46 +0200229 DP_AUX_CTL_2x_Clock_Mask : constant :=
230 (if Config.Has_PCH_Aux_Channels then
231 DP_AUX_CTL_2X_BIT_CLOCK_DIV_MASK else 0);
232 DP_AUX_CTL_2x_Clock : constant Word32 :=
233 (if Config.Has_PCH_Aux_Channels then
234 (if Port = DP_A then
235 Word32 ((Config.Default_CDClk_Freq + 1_000_000) / 2_000_000)
236 else
Arthur Heymansd1988d12018-03-28 16:27:57 +0200237 Word32 ((Config.Raw_Clock + 1_000_000) / 2_000_000))
Arthur Heymans73ea0322018-03-28 17:17:07 +0200238 elsif Config.Has_GMCH_RawClk then
239 Word32 (Div_Round_Closest (Config.Raw_Clock, 2_000_000))
Nico Huberabe3de22016-10-20 15:03:46 +0200240 else 0);
241
Nico Huber83693c82016-10-08 22:17:55 +0200242 Busy : Boolean;
243 Status : Word32;
244 begin
245 Response := (others => 0); -- Don't care
246 Response_Length := DP_Defs.Aux_Response_Length'First;
247
248 if Config.Need_DP_Aux_Mutex then
249 Registers.Set_Mask
250 (Register => AUX_CH (Port).MUTEX,
251 Mask => DDI_AUX_MUTEX_MUTEX_ENABLE);
252 Registers.Wait_Set_Mask
253 (Register => AUX_CH (Port).MUTEX,
254 Mask => DDI_AUX_MUTEX_MUTEX_STATUS);
255 end if;
256
257 Registers.Is_Set_Mask
258 (Register => AUX_CH (Port).CTL,
259 Mask => DP_AUX_CTL_SEND_BUSY,
260 Result => Busy);
261 if Busy then
262 Success := False;
263 else
264 for Idx in AUX_CH_Data_Regs loop
265 Write_Data_Reg
266 (Register => AUX_CH (Port).DATA (Idx),
267 Buf => Request,
268 Length => Request_Length,
269 Offset => (Natural (Idx) - 1) * 4);
270 end loop;
271
272 Registers.Unset_And_Set_Mask
273 (Register => AUX_CH (Port).CTL,
274 Mask_Unset => DP_AUX_CTL_INTERRUPT_ON_DONE or
275 DP_AUX_CTL_TIME_OUT_TIMER_MASK or
Nico Huberabe3de22016-10-20 15:03:46 +0200276 DP_AUX_CTL_MESSAGE_SIZE_MASK or
277 DP_AUX_CTL_2x_Clock_Mask,
Nico Huber83693c82016-10-08 22:17:55 +0200278 Mask_Set => DP_AUX_CTL_SEND_BUSY or -- starts transfer
279 DP_AUX_CTL_DONE or -- clears the status
280 DP_AUX_CTL_TIME_OUT_ERROR or -- clears the status
281 DP_AUX_CTL_RECEIVE_ERROR or -- clears the status
282 DP_AUX_CTL_TIME_OUT_TIMER_600US or
Nico Huberabe3de22016-10-20 15:03:46 +0200283 DP_AUX_CTL_MESSAGE_SIZE (Request_Length) or
284 DP_AUX_CTL_2x_Clock);
Nico Huber83693c82016-10-08 22:17:55 +0200285
286 Registers.Wait_Unset_Mask
287 (Register => AUX_CH (Port).CTL,
288 Mask => DP_AUX_CTL_SEND_BUSY);
289 Registers.Read (Register => AUX_CH (Port).CTL, Value => Status);
290 Success := (Status and
291 (DP_AUX_CTL_TIME_OUT_ERROR or DP_AUX_CTL_RECEIVE_ERROR))
292 = 0;
293
294 if Success then
295 Status := (Status and DP_AUX_CTL_MESSAGE_SIZE_MASK)
296 / DP_AUX_CTL_MESSAGE_SIZE_SHIFT;
297 if Natural (Status) < DP_Defs.Aux_Response_Length'First then
298 Success := False;
299 elsif Natural (Status) > DP_Defs.Aux_Response_Length'Last then
300 Response_Length := DP_Defs.Aux_Response_Length'Last;
301 else
302 Response_Length := Natural (Status);
303 end if;
304 end if;
305
306 if Success then
307 for Idx in AUX_CH_Data_Regs loop
308 Read_Data_Reg
309 (Register => AUX_CH (Port).DATA (Idx),
310 Buf => Response,
311 Length => Response_Length,
312 Offset => (Natural (Idx) - 1) * 4);
313 end loop;
314 end if;
315 end if;
316
317 if Config.Need_DP_Aux_Mutex then
318 Registers.Unset_And_Set_Mask
319 (Register => AUX_CH (Port).MUTEX,
320 Mask_Unset => DDI_AUX_MUTEX_MUTEX_ENABLE,
321 Mask_Set => DDI_AUX_MUTEX_MUTEX_STATUS); -- frees the mutex
322 end if;
323 end Aux_Request_Low;
324
325 ----------------------------------------------------------------------------
326
327 procedure Do_Aux_Request
328 (Port : in DP_Port;
329 Request : in DP_Defs.Aux_Request;
330 Request_Length : in DP_Defs.Aux_Request_Length;
331 Response : out DP_Defs.Aux_Response;
332 Response_Length : out DP_Defs.Aux_Response_Length;
333 Success : out Boolean)
334 is
335 begin
336 for Try in Positive range 1 .. 3 loop
337 Aux_Request_Low
338 (Port => Port,
339 Request => Request,
340 Request_Length => Request_Length,
341 Response => Response,
342 Response_Length => Response_Length,
343 Success => Success);
344 exit when Success;
345 end loop;
346 end Do_Aux_Request;
347
348end HW.GFX.GMA.DP_Aux_Request;