blob: 20c2c3ce1cfee0248034cb5120e3012dd458d758 [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;
16with HW.GFX.DP_Defs;
17
18use type HW.Word8;
Elyes Haouas066e52e2022-09-26 12:48:47 +020019use type HW.Word32;
Nico Huber83693c82016-10-08 22:17:55 +020020use type HW.GFX.DP_Defs.Aux_Message_Command;
21
22package body HW.GFX.DP_Aux_Ch is
23
24 DP_AUX_I2C_WRITE : constant := 16#0#;
25 DP_AUX_I2C_READ : constant := 16#1#;
26 DP_AUX_I2C_WR_STATUS_REQ : constant := 16#2#;
27 DP_AUX_I2C_MOT : constant := 16#4#;
28 DP_AUX_NATIVE_WRITE : constant := 16#8#;
29 DP_AUX_NATIVE_READ : constant := 16#9#;
30
31 procedure Fill_Aux_Request
32 (Request : out DP_Defs.Aux_Request;
33 Command : in DP_Defs.Aux_Message_Command;
34 Address : in DP_Defs.Aux_Message_Address;
35 Length : in DP_Defs.Aux_Payload_Length)
36 is
37 begin
38 Request :=
39 (0 => Shift_Left (Word8 (Command), 4) or
40 Word8 (Shift_Right (Word32 (Address), 16)),
41 1 => Word8 (Shift_Right (Word32 (Address), 8) and 16#ff#),
42 2 => Word8 (Shift_Right (Word32 (Address), 0) and 16#ff#),
43 3 => Word8 (Length) - 1,
44 others => 0); -- Don't care
45 end Fill_Aux_Request;
46
47 function Is_Empty (Request : DP_Defs.Aux_Request) return Boolean is
48 begin
49 return Request (3) = 16#ff#;
50 end Is_Empty;
51
52 function Is_Aux_Ack (Response : DP_Defs.Aux_Response) return Boolean
53 with
54 Depends => (Is_Aux_Ack'Result => Response)
55 is
56 begin
57 return (Response (0) and 16#30#) = 16#00#;
58 end Is_Aux_Ack;
59
60 function Is_Aux_Defer (Response : DP_Defs.Aux_Response) return Boolean is
61 begin
62 return (Response (0) and 16#30#) = 16#20#;
63 end Is_Aux_Defer;
64
65 function Is_I2C_Ack (Response : DP_Defs.Aux_Response) return Boolean
66 with
67 Depends => (Is_I2C_Ack'Result => Response)
68 is
69 begin
70 return (Response (0) and 16#c0#) = 16#00#;
71 end Is_I2C_Ack;
72
73 function Is_I2C_Defer (Response : DP_Defs.Aux_Response) return Boolean is
74 begin
75 return (Response (0) and 16#c0#) = 16#80#;
76 end Is_I2C_Defer;
77
78 ----------------------------------------------------------------------------
79
80 procedure Do_Aux_Request
81 (Port : in T;
82 Request : in DP_Defs.Aux_Request;
83 Request_Length : in DP_Defs.Aux_Request_Length;
84 Response : out DP_Defs.Aux_Response;
85 Response_Length : out DP_Defs.Aux_Response_Length;
86 Success : out Boolean)
87 is
88 begin
89 for Try in Positive range 1 .. 32 loop
90 Aux_Request
91 (Port => Port,
92 Request => Request,
93 Request_Length => Request_Length,
94 Response => Response,
95 Response_Length => Response_Length,
96 Success => Success);
97
98 exit when not (Success and Is_Aux_Defer (Response));
99 Time.U_Delay (500);
100 end loop;
101
102 Success := Success and then Is_Aux_Ack (Response);
103 end Do_Aux_Request;
104
105 ----------------------------------------------------------------------------
106
107 procedure Aux_Read
108 (Port : in T;
109 Address : in DP_Defs.Aux_Message_Address;
110 Length : in out DP_Defs.Aux_Payload_Length;
111 Data : out DP_Defs.Aux_Payload;
112 Success : out Boolean)
113 is
114 Request : DP_Defs.Aux_Request;
115 Response : DP_Defs.Aux_Response;
116 Response_Length : DP_Defs.Aux_Response_Length;
117 begin
118 Data := (others => 0); -- Initialize
119
120 Fill_Aux_Request
121 (Request => Request,
122 Command => DP_AUX_NATIVE_READ,
123 Address => Address,
124 Length => Length);
125
126 Do_Aux_Request
127 (Port => Port,
128 Request => Request,
129 Request_Length => 4,
130 Response => Response,
131 Response_Length => Response_Length,
132 Success => Success);
133
134 Success := Success and then Response_Length > 1;
135 if Success then
136 pragma Assert (Response_Length > 1);
137 Length := Response_Length - 1;
138 Data (0 .. Length - 1) := Response (1 .. Length);
139 end if;
140 end Aux_Read;
141
142 procedure Aux_Write
143 (Port : in T;
144 Address : in DP_Defs.Aux_Message_Address;
145 Length : in DP_Defs.Aux_Payload_Length;
146 Data : in DP_Defs.Aux_Payload;
147 Success : out Boolean)
148 is
149 Request : DP_Defs.Aux_Request;
150
151 Ignored_Response : DP_Defs.Aux_Response;
152 Ignored_Response_Length : DP_Defs.Aux_Response_Length;
153 begin
154 Fill_Aux_Request
155 (Request => Request,
156 Command => DP_AUX_NATIVE_WRITE,
157 Address => Address,
158 Length => Length);
159 Request (4 .. Length + 4 - 1) := Data (0 .. Length - 1);
160
161 pragma Warnings (GNATprove, Off,
162 "unused assignment to ""Ignored_Response*""",
163 Reason => "No response expected here");
164 Do_Aux_Request
165 (Port => Port,
166 Request => Request,
167 Request_Length => 4 + Length,
168 Response => Ignored_Response,
169 Response_Length => Ignored_Response_Length,
170 Success => Success);
171 end Aux_Write;
172
173 ----------------------------------------------------------------------------
174
175 procedure I2C_Out_Packet
176 (Port : in T;
177 Command : in DP_Defs.Aux_Message_Command;
178 Address : in DP_Defs.Aux_Message_Address;
179 Length : in DP_Defs.Aux_Payload_Length;
180 Data : in DP_Defs.Aux_Payload;
181 Success : out Boolean)
182 is
183 Request : DP_Defs.Aux_Request;
184
Nico Huber5c1e43a2021-05-02 11:52:11 +0200185 Xfered : Natural := 0;
186 Tries : Natural := 0;
187 Max_Defers : constant := 7;
188
Nico Huber83693c82016-10-08 22:17:55 +0200189 Response : DP_Defs.Aux_Response;
Nico Huber5c1e43a2021-05-02 11:52:11 +0200190 Response_Length : DP_Defs.Aux_Response_Length;
Nico Huber83693c82016-10-08 22:17:55 +0200191 begin
192 Fill_Aux_Request
193 (Request => Request,
194 Command => Command,
195 Address => Address,
196 Length => Length);
197 Request (4 .. Length + 4 - 1) := Data (0 .. Length - 1);
Nico Huber5c1e43a2021-05-02 11:52:11 +0200198
199 loop
Nico Huber83693c82016-10-08 22:17:55 +0200200 Do_Aux_Request
201 (Port => Port,
202 Request => Request,
203 Request_Length => (if Is_Empty (Request) then 3 else 4 + Length),
204 Response => Response,
Nico Huber5c1e43a2021-05-02 11:52:11 +0200205 Response_Length => Response_Length,
Nico Huber83693c82016-10-08 22:17:55 +0200206 Success => Success);
Nico Huber5c1e43a2021-05-02 11:52:11 +0200207 exit when not Success;
Nico Huber83693c82016-10-08 22:17:55 +0200208
Nico Huber5c1e43a2021-05-02 11:52:11 +0200209 if Is_I2C_Ack (Response) then
210 if Response_Length <= 1 then
211 Xfered := Length;
212 else
213 Xfered := Xfered + Natural'Min (Natural (Response (1)), Length - Xfered);
214 Tries := 0;
215 end if;
216 exit when Xfered = Length;
217 elsif Is_I2C_Defer (Response) then
218 exit when Tries >= Max_Defers;
219 Tries := Tries + 1;
220 else -- Nak
221 exit;
222 end if;
223
224 -- Command was already AUX-acked. Thus, only query for new
225 -- status from now on until we got all bytes I2C-acked too.
Nico Huber83693c82016-10-08 22:17:55 +0200226 Fill_Aux_Request
227 (Request => Request,
228 Command => (Command and DP_AUX_I2C_MOT) or DP_AUX_I2C_WR_STATUS_REQ,
229 Address => Address,
230 Length => 0);
231 Time.U_Delay (500);
232 end loop;
Nico Huber5c1e43a2021-05-02 11:52:11 +0200233 Success := Success and then (Is_I2C_Ack (Response) and Xfered = Length);
Nico Huber83693c82016-10-08 22:17:55 +0200234 end I2C_Out_Packet;
235
236 procedure I2C_In_Packet
237 (Port : in T;
238 Command : in DP_Defs.Aux_Message_Command;
239 Address : in DP_Defs.Aux_Message_Address;
240 Length : in DP_Defs.Aux_Payload_Length;
241 Response : out DP_Defs.Aux_Response;
242 Response_Length : out DP_Defs.Aux_Response_Length;
243 Success : out Boolean)
244 is
245 Request : DP_Defs.Aux_Request;
246 begin
247 Fill_Aux_Request
248 (Request => Request,
249 Command => Command,
250 Address => Address,
251 Length => Length);
252 for Try in Positive range 1 .. 7 loop
253 Do_Aux_Request
254 (Port => Port,
255 Request => Request,
256 Request_Length => (if Is_Empty (Request) then 3 else 4),
257 Response => Response,
258 Response_Length => Response_Length,
259 Success => Success);
260 exit when not (Success and Is_I2C_Defer (Response));
261
262 Time.U_Delay (500);
263 end loop;
264 Success := Success and then Is_I2C_Ack (Response);
265 end I2C_In_Packet;
266
267 procedure I2C_Empty_Packet
268 (Port : in T;
269 Command : in DP_Defs.Aux_Message_Command;
270 Address : in DP_Defs.Aux_Message_Address;
271 Success : out Boolean)
272 is
273 Ignored_Response : DP_Defs.Aux_Response;
274 Ignored_Response_Length : DP_Defs.Aux_Response_Length;
275 begin
276 pragma Warnings (GNATprove, Off,
277 "unused assignment to ""Ignored_Response*""",
278 Reason => "No response expected here");
279 I2C_In_Packet
280 (Port => Port,
281 Command => Command,
282 Address => Address,
283 Length => 0,
284 Response => Ignored_Response,
285 Response_Length => Ignored_Response_Length,
286 Success => Success);
287 end I2C_Empty_Packet;
288
289 ----------------------------------------------------------------------------
290
291 procedure Do_I2C_Write
292 (Port : in T;
293 Address : in I2C.Transfer_Address;
294 Length : in DP_Defs.Aux_Payload_Length;
295 Data : in DP_Defs.Aux_Payload;
296 Success : out Boolean)
297 is
298 Ignored_Success : Boolean;
299 begin
300 -- I2C address "start" packet
301 I2C_Empty_Packet
302 (Port => Port,
303 Command => DP_AUX_I2C_WRITE or DP_AUX_I2C_MOT,
304 Address => DP_Defs.Aux_Message_Address (Address),
305 Success => Success);
306
307 if Success then
308 I2C_Out_Packet
309 (Port => Port,
310 Command => DP_AUX_I2C_WRITE or DP_AUX_I2C_MOT,
311 Address => DP_Defs.Aux_Message_Address (Address),
312 Length => Length,
313 Data => Data,
314 Success => Success);
315
316 pragma Warnings
317 (GNATprove, Off, "unused assignment to ""Ignored_Success""",
318 Reason => "Doesn't matter as long as the transfer itself succeeds");
319 -- I2C address "stop" packet
320 I2C_Empty_Packet
321 (Port => Port,
322 Command => DP_AUX_I2C_WRITE,
323 Address => DP_Defs.Aux_Message_Address (Address),
324 Success => Ignored_Success);
325 end if;
326 end Do_I2C_Write;
327
328 procedure Do_I2C_Read
329 (Port : in T;
330 Address : in I2C.Transfer_Address;
331 Length : in out I2C.Transfer_Length;
332 Data : in out I2C.Transfer_Data;
333 Success : out Boolean)
334 is
335 Xfered : Natural := 0;
336 Chunk : DP_Defs.Aux_Payload_Length := DP_Defs.Aux_Payload_Length'Last;
337
338 Tries : Natural := 0;
339 Max_Tries : constant := 4;
340
341 Response : DP_Defs.Aux_Response;
342 Response_Length : DP_Defs.Aux_Response_Length;
343
344 Ignored_Success : Boolean;
345 begin
346 -- I2C address "start" packet
347 I2C_Empty_Packet
348 (Port => Port,
349 Command => DP_AUX_I2C_READ or DP_AUX_I2C_MOT,
350 Address => DP_Defs.Aux_Message_Address (Address),
351 Success => Success);
352
353 while Success and then (Xfered < Length and Tries < Max_Tries) loop
354 I2C_In_Packet
355 (Port => Port,
356 Command => DP_AUX_I2C_READ or DP_AUX_I2C_MOT,
357 Address => DP_Defs.Aux_Message_Address (Address),
358 Length => Natural'Min (Chunk, Length - Xfered),
359 Response => Response,
360 Response_Length => Response_Length,
361 Success => Success);
362
363 if Success and then Response_Length >= 2 then
364 Chunk := Natural'Min (Response_Length - 1, Length - Xfered);
365 Data (Xfered .. Xfered + Chunk - 1) := Response (1 .. Chunk);
366 Xfered := Xfered + Chunk;
367 Tries := 0;
368 else
369 Tries := Tries + 1;
370 end if;
371 pragma Loop_Invariant (Xfered <= Length);
372 end loop;
373
374 if Success then
375 pragma Warnings
376 (GNATprove, Off, "unused assignment to ""Ignored_Success""",
377 Reason => "Doesn't matter as long as the transfer itself succeeds");
378 -- I2C address "stop" packet
379 I2C_Empty_Packet
380 (Port => Port,
381 Command => DP_AUX_I2C_READ,
382 Address => DP_Defs.Aux_Message_Address (Address),
383 Success => Ignored_Success);
384 end if;
385
386 Success := Success and then Tries < Max_Tries;
387 Length := Xfered;
388 end Do_I2C_Read;
389
390 ----------------------------------------------------------------------------
391
Angel Pons0a8174b2021-03-01 01:51:08 +0100392 procedure I2C_Write_Byte
393 (Port : in T;
394 Address : in I2C.Transfer_Address;
395 Offset : in Word8;
396 Value : in Word8;
397 Success : out Boolean)
398 is
399 Payload : constant DP_Defs.Aux_Payload := (Offset, Value, others => 16#00#);
400
401 Length : constant I2C.Transfer_Length := 1;
402 begin
403 Do_I2C_Write (Port, Address, 1 + Length, Payload, Success);
404 end I2C_Write_Byte;
405
406 procedure I2C_Read_Byte
407 (Port : in T;
408 Address : in I2C.Transfer_Address;
409 Offset : in Word8;
410 Value : out Word8;
411 Success : out Boolean)
412 is
413 Index_Payload : constant DP_Defs.Aux_Payload := (Offset, others => 16#00#);
414
415 Length : I2C.Transfer_Length := 1;
416 Data : I2C.Transfer_Data := (others => 16#00#);
417 begin
418 Do_I2C_Write (Port, Address, 1, Index_Payload, Success);
419
420 if Success then
421 Do_I2C_Read (Port, Address, Length, Data, Success);
422 Success := Success and Length = 1;
423 end if;
424 Value := Data (0);
425 end I2C_Read_Byte;
426
Nico Huber83693c82016-10-08 22:17:55 +0200427 procedure I2C_Read
428 (Port : in T;
429 Address : in I2C.Transfer_Address;
430 Length : in out I2C.Transfer_Length;
431 Data : out I2C.Transfer_Data;
432 Success : out Boolean)
433 is
434 Index_Payload : DP_Defs.Aux_Payload;
435 begin
436 Data := (others => 16#00#);
437
438 Index_Payload := (others => 16#00#); -- send index 0
439 Do_I2C_Write (Port, Address, 1, Index_Payload, Success);
440
441 if Success then
442 Do_I2C_Read (Port, Address, Length, Data, Success);
443 end if;
444 end I2C_Read;
445
446end HW.GFX.DP_Aux_Ch;