blob: 22064abd034e793faa437c74cd8744c06201043c [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;
22
23use type HW.Word8;
24use type HW.GFX.GMA.Registers.Registers_Invalid_Index;
25
26package body HW.GFX.GMA.DP_Aux_Request is
27
28 DP_AUX_CTL_SEND_BUSY : constant := 1 * 2 ** 31;
29 DP_AUX_CTL_DONE : constant := 1 * 2 ** 30;
30 DP_AUX_CTL_INTERRUPT_ON_DONE : constant := 1 * 2 ** 29;
31 DP_AUX_CTL_TIME_OUT_ERROR : constant := 1 * 2 ** 28;
32 DP_AUX_CTL_TIME_OUT_TIMER_MASK : constant := 3 * 2 ** 26;
33 DP_AUX_CTL_TIME_OUT_TIMER_400US : constant := 0 * 2 ** 26;
34 DP_AUX_CTL_TIME_OUT_TIMER_600US : constant := 1 * 2 ** 26;
35 DP_AUX_CTL_TIME_OUT_TIMER_800US : constant := 2 * 2 ** 26;
36 DP_AUX_CTL_TIME_OUT_TIMER_1600US : constant := 3 * 2 ** 26;
37 DP_AUX_CTL_RECEIVE_ERROR : constant := 1 * 2 ** 25;
38 DP_AUX_CTL_MESSAGE_SIZE_MASK : constant := 31 * 2 ** 20;
39 DP_AUX_CTL_MESSAGE_SIZE_SHIFT : constant := 2 ** 20;
40 DP_AUX_CTL_PRECHARGE_TIME_MASK : constant := 15 * 2 ** 16;
41 DP_AUX_CTL_PRECHARGE_TIME_SHIFT : constant := 2 ** 16;
42 DP_AUX_CTL_2X_BIT_CLOCK_DIV_MASK : constant := 2047 * 2 ** 0;
Nico Huberabe3de22016-10-20 15:03:46 +020043 -- TODO: HSW/BDW with LPT-H might need a workaround for the 2x bit clock.
Nico Huber83693c82016-10-08 22:17:55 +020044
45 subtype DP_AUX_CTL_MESSAGE_SIZE_T is Natural range 1 .. 20;
46 function DP_AUX_CTL_MESSAGE_SIZE
47 (Message_Length : DP_AUX_CTL_MESSAGE_SIZE_T)
48 return Word32;
49
50 DDI_AUX_MUTEX_MUTEX_ENABLE : constant := 1 * 2 ** 31;
51 DDI_AUX_MUTEX_MUTEX_STATUS : constant := 1 * 2 ** 30;
52
53 type AUX_CH_Data_Regs is new Positive range 1 .. 5;
54
55 type AUX_CH_Data_Regs_Array is
56 array (AUX_CH_Data_Regs) of Registers.Registers_Index;
57
58 type AUX_CH_Registers is record
59 CTL : Registers.Registers_Index;
60 DATA : AUX_CH_Data_Regs_Array;
61 MUTEX : Registers.Registers_Invalid_Index;
62 end record;
63
64 type AUX_CH_Registers_Array is array (DP_Port) of AUX_CH_Registers;
65
66 AUX_CH : constant AUX_CH_Registers_Array :=
67 (if Config.Has_PCH_Aux_Channels then
68 AUX_CH_Registers_Array'
69 (DP_A => AUX_CH_Registers'
70 (CTL => Registers.DP_AUX_CTL_A,
71 DATA => AUX_CH_Data_Regs_Array'
72 (1 => Registers.DP_AUX_DATA_A_1,
73 2 => Registers.DP_AUX_DATA_A_2,
74 3 => Registers.DP_AUX_DATA_A_3,
75 4 => Registers.DP_AUX_DATA_A_4,
76 5 => Registers.DP_AUX_DATA_A_5),
77 MUTEX => Registers.Invalid_Register),
78 DP_B => AUX_CH_Registers'
79 (CTL => Registers.PCH_DP_AUX_CTL_B,
80 DATA => AUX_CH_Data_Regs_Array'
81 (1 => Registers.PCH_DP_AUX_DATA_B_1,
82 2 => Registers.PCH_DP_AUX_DATA_B_2,
83 3 => Registers.PCH_DP_AUX_DATA_B_3,
84 4 => Registers.PCH_DP_AUX_DATA_B_4,
85 5 => Registers.PCH_DP_AUX_DATA_B_5),
86 MUTEX => Registers.Invalid_Register),
87 DP_C => AUX_CH_Registers'
88 (CTL => Registers.PCH_DP_AUX_CTL_C,
89 DATA => AUX_CH_Data_Regs_Array'
90 (1 => Registers.PCH_DP_AUX_DATA_C_1,
91 2 => Registers.PCH_DP_AUX_DATA_C_2,
92 3 => Registers.PCH_DP_AUX_DATA_C_3,
93 4 => Registers.PCH_DP_AUX_DATA_C_4,
94 5 => Registers.PCH_DP_AUX_DATA_C_5),
95 MUTEX => Registers.Invalid_Register),
96 DP_D => AUX_CH_Registers'
97 (CTL => Registers.PCH_DP_AUX_CTL_D,
98 DATA => AUX_CH_Data_Regs_Array'
99 (1 => Registers.PCH_DP_AUX_DATA_D_1,
100 2 => Registers.PCH_DP_AUX_DATA_D_2,
101 3 => Registers.PCH_DP_AUX_DATA_D_3,
102 4 => Registers.PCH_DP_AUX_DATA_D_4,
103 5 => Registers.PCH_DP_AUX_DATA_D_5),
104 MUTEX => Registers.Invalid_Register))
105 else
106 AUX_CH_Registers_Array'
107 (DP_A => AUX_CH_Registers'
108 (CTL => Registers.DDI_AUX_CTL_A,
109 DATA => AUX_CH_Data_Regs_Array'
110 (1 => Registers.DDI_AUX_DATA_A_1,
111 2 => Registers.DDI_AUX_DATA_A_2,
112 3 => Registers.DDI_AUX_DATA_A_3,
113 4 => Registers.DDI_AUX_DATA_A_4,
114 5 => Registers.DDI_AUX_DATA_A_5),
115 MUTEX => Registers.DDI_AUX_MUTEX_A),
116 DP_B => AUX_CH_Registers'
117 (CTL => Registers.DDI_AUX_CTL_B,
118 DATA => AUX_CH_Data_Regs_Array'
119 (1 => Registers.DDI_AUX_DATA_B_1,
120 2 => Registers.DDI_AUX_DATA_B_2,
121 3 => Registers.DDI_AUX_DATA_B_3,
122 4 => Registers.DDI_AUX_DATA_B_4,
123 5 => Registers.DDI_AUX_DATA_B_5),
124 MUTEX => Registers.DDI_AUX_MUTEX_B),
125 DP_C => AUX_CH_Registers'
126 (CTL => Registers.DDI_AUX_CTL_C,
127 DATA => AUX_CH_Data_Regs_Array'
128 (1 => Registers.DDI_AUX_DATA_C_1,
129 2 => Registers.DDI_AUX_DATA_C_2,
130 3 => Registers.DDI_AUX_DATA_C_3,
131 4 => Registers.DDI_AUX_DATA_C_4,
132 5 => Registers.DDI_AUX_DATA_C_5),
133 MUTEX => Registers.DDI_AUX_MUTEX_C),
134 DP_D => AUX_CH_Registers'
135 (CTL => Registers.DDI_AUX_CTL_D,
136 DATA => AUX_CH_Data_Regs_Array'
137 (1 => Registers.DDI_AUX_DATA_D_1,
138 2 => Registers.DDI_AUX_DATA_D_2,
139 3 => Registers.DDI_AUX_DATA_D_3,
140 4 => Registers.DDI_AUX_DATA_D_4,
141 5 => Registers.DDI_AUX_DATA_D_5),
142 MUTEX => Registers.DDI_AUX_MUTEX_D)));
143
144 ----------------------------------------------------------------------------
145
146 function DP_AUX_CTL_MESSAGE_SIZE
147 (Message_Length : DP_AUX_CTL_MESSAGE_SIZE_T)
148 return Word32
149 is
150 begin
151 return Word32 (Message_Length) * DP_AUX_CTL_MESSAGE_SIZE_SHIFT;
152 end DP_AUX_CTL_MESSAGE_SIZE;
153
154 ----------------------------------------------------------------------------
155
156 procedure Aux_Request_Low
157 (Port : in DP_Port;
158 Request : in DP_Defs.Aux_Request;
159 Request_Length : in DP_Defs.Aux_Request_Length;
160 Response : out DP_Defs.Aux_Response;
161 Response_Length : out DP_Defs.Aux_Response_Length;
162 Success : out Boolean)
163 with
164 Global => (In_Out => Registers.Register_State,
165 Input => Time.State),
166 Depends =>
167 ((Registers.Register_State,
168 Response,
169 Response_Length,
170 Success)
171 =>
172 (Registers.Register_State,
173 Time.State,
174 Port,
175 Request,
176 Request_Length))
177 is
178 procedure Write_Data_Reg
179 (Register : in Registers.Registers_Index;
180 Buf : in DP_Defs.Aux_Request;
181 Length : in DP_Defs.Aux_Request_Length;
182 Offset : in DP_Defs.Aux_Request_Index)
183 is
184 Value : Word32;
185 Count : Natural;
186 begin
187 if Offset < Length then
188 if Length - Offset > 4 then
189 Count := 4;
190 else
191 Count := Length - Offset;
192 end if;
193
194 Value := 0;
195 for Idx in DP_Defs.Aux_Request_Index range 0 .. Count - 1 loop
196 Value := Value or
197 Shift_Left (Word32 (Buf (Offset + Idx)), (3 - Idx) * 8);
198 end loop;
199 Registers.Write (Register => Register, Value => Value);
200 end if;
201 end Write_Data_Reg;
202
203 procedure Read_Data_Reg
204 (Register : in Registers.Registers_Index;
205 Buf : in out DP_Defs.Aux_Response;
206 Length : in DP_Defs.Aux_Response_Length;
207 Offset : in DP_Defs.Aux_Response_Index)
208 is
209 Value : Word32;
210 Count : DP_Defs.Aux_Response_Length;
211 begin
212 if Offset < Length then
213 if Length - Offset > 4 then
214 Count := 4;
215 else
216 Count := Length - Offset;
217 end if;
218
219 Registers.Read (Register => Register, Value => Value);
220 for Idx in 0 .. Count - 1 loop
221 Buf (Offset + Idx) :=
222 Word8 (Shift_Right (Value, (3 - Idx) * 8) and 16#ff#);
223 end loop;
224 end if;
225 end Read_Data_Reg;
226
Nico Huberabe3de22016-10-20 15:03:46 +0200227 DP_AUX_CTL_2x_Clock_Mask : constant :=
228 (if Config.Has_PCH_Aux_Channels then
229 DP_AUX_CTL_2X_BIT_CLOCK_DIV_MASK else 0);
230 DP_AUX_CTL_2x_Clock : constant Word32 :=
231 (if Config.Has_PCH_Aux_Channels then
232 (if Port = DP_A then
233 Word32 ((Config.Default_CDClk_Freq + 1_000_000) / 2_000_000)
234 else
235 Word32 ((Config.Default_RawClk_Freq + 1_000_000) / 2_000_000))
236 else 0);
237
Nico Huber83693c82016-10-08 22:17:55 +0200238 Busy : Boolean;
239 Status : Word32;
240 begin
241 Response := (others => 0); -- Don't care
242 Response_Length := DP_Defs.Aux_Response_Length'First;
243
244 if Config.Need_DP_Aux_Mutex then
245 Registers.Set_Mask
246 (Register => AUX_CH (Port).MUTEX,
247 Mask => DDI_AUX_MUTEX_MUTEX_ENABLE);
248 Registers.Wait_Set_Mask
249 (Register => AUX_CH (Port).MUTEX,
250 Mask => DDI_AUX_MUTEX_MUTEX_STATUS);
251 end if;
252
253 Registers.Is_Set_Mask
254 (Register => AUX_CH (Port).CTL,
255 Mask => DP_AUX_CTL_SEND_BUSY,
256 Result => Busy);
257 if Busy then
258 Success := False;
259 else
260 for Idx in AUX_CH_Data_Regs loop
261 Write_Data_Reg
262 (Register => AUX_CH (Port).DATA (Idx),
263 Buf => Request,
264 Length => Request_Length,
265 Offset => (Natural (Idx) - 1) * 4);
266 end loop;
267
268 Registers.Unset_And_Set_Mask
269 (Register => AUX_CH (Port).CTL,
270 Mask_Unset => DP_AUX_CTL_INTERRUPT_ON_DONE or
271 DP_AUX_CTL_TIME_OUT_TIMER_MASK or
Nico Huberabe3de22016-10-20 15:03:46 +0200272 DP_AUX_CTL_MESSAGE_SIZE_MASK or
273 DP_AUX_CTL_2x_Clock_Mask,
Nico Huber83693c82016-10-08 22:17:55 +0200274 Mask_Set => DP_AUX_CTL_SEND_BUSY or -- starts transfer
275 DP_AUX_CTL_DONE or -- clears the status
276 DP_AUX_CTL_TIME_OUT_ERROR or -- clears the status
277 DP_AUX_CTL_RECEIVE_ERROR or -- clears the status
278 DP_AUX_CTL_TIME_OUT_TIMER_600US or
Nico Huberabe3de22016-10-20 15:03:46 +0200279 DP_AUX_CTL_MESSAGE_SIZE (Request_Length) or
280 DP_AUX_CTL_2x_Clock);
Nico Huber83693c82016-10-08 22:17:55 +0200281
282 Registers.Wait_Unset_Mask
283 (Register => AUX_CH (Port).CTL,
284 Mask => DP_AUX_CTL_SEND_BUSY);
285 Registers.Read (Register => AUX_CH (Port).CTL, Value => Status);
286 Success := (Status and
287 (DP_AUX_CTL_TIME_OUT_ERROR or DP_AUX_CTL_RECEIVE_ERROR))
288 = 0;
289
290 if Success then
291 Status := (Status and DP_AUX_CTL_MESSAGE_SIZE_MASK)
292 / DP_AUX_CTL_MESSAGE_SIZE_SHIFT;
293 if Natural (Status) < DP_Defs.Aux_Response_Length'First then
294 Success := False;
295 elsif Natural (Status) > DP_Defs.Aux_Response_Length'Last then
296 Response_Length := DP_Defs.Aux_Response_Length'Last;
297 else
298 Response_Length := Natural (Status);
299 end if;
300 end if;
301
302 if Success then
303 for Idx in AUX_CH_Data_Regs loop
304 Read_Data_Reg
305 (Register => AUX_CH (Port).DATA (Idx),
306 Buf => Response,
307 Length => Response_Length,
308 Offset => (Natural (Idx) - 1) * 4);
309 end loop;
310 end if;
311 end if;
312
313 if Config.Need_DP_Aux_Mutex then
314 Registers.Unset_And_Set_Mask
315 (Register => AUX_CH (Port).MUTEX,
316 Mask_Unset => DDI_AUX_MUTEX_MUTEX_ENABLE,
317 Mask_Set => DDI_AUX_MUTEX_MUTEX_STATUS); -- frees the mutex
318 end if;
319 end Aux_Request_Low;
320
321 ----------------------------------------------------------------------------
322
323 procedure Do_Aux_Request
324 (Port : in DP_Port;
325 Request : in DP_Defs.Aux_Request;
326 Request_Length : in DP_Defs.Aux_Request_Length;
327 Response : out DP_Defs.Aux_Response;
328 Response_Length : out DP_Defs.Aux_Response_Length;
329 Success : out Boolean)
330 is
331 begin
332 for Try in Positive range 1 .. 3 loop
333 Aux_Request_Low
334 (Port => Port,
335 Request => Request,
336 Request_Length => Request_Length,
337 Response => Response,
338 Response_Length => Response_Length,
339 Success => Success);
340 exit when Success;
341 end loop;
342 end Do_Aux_Request;
343
344end HW.GFX.GMA.DP_Aux_Request;