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