blob: 8bc0c5838748cc9bcdee29c8e16a55759d0dea41 [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
184 Response : DP_Defs.Aux_Response;
185 Ignored_Response_Length : DP_Defs.Aux_Response_Length;
186 begin
187 Fill_Aux_Request
188 (Request => Request,
189 Command => Command,
190 Address => Address,
191 Length => Length);
192 Request (4 .. Length + 4 - 1) := Data (0 .. Length - 1);
193 for Try in Positive range 1 .. 7 loop
194 pragma Warnings (GNATprove, Off,
195 "unused assignment to ""Ignored_Response_Length""",
196 Reason => "No response expected here");
197 Do_Aux_Request
198 (Port => Port,
199 Request => Request,
200 Request_Length => (if Is_Empty (Request) then 3 else 4 + Length),
201 Response => Response,
202 Response_Length => Ignored_Response_Length,
203 Success => Success);
204 exit when not (Success and Is_I2C_Defer (Response));
205
206 -- Command was already AUX-acked. Thus, only query for
207 -- new status from now on until we get I2C-acked too.
208 Fill_Aux_Request
209 (Request => Request,
210 Command => (Command and DP_AUX_I2C_MOT) or DP_AUX_I2C_WR_STATUS_REQ,
211 Address => Address,
212 Length => 0);
213 Time.U_Delay (500);
214 end loop;
215 Success := Success and then Is_I2C_Ack (Response);
216 end I2C_Out_Packet;
217
218 procedure I2C_In_Packet
219 (Port : in T;
220 Command : in DP_Defs.Aux_Message_Command;
221 Address : in DP_Defs.Aux_Message_Address;
222 Length : in DP_Defs.Aux_Payload_Length;
223 Response : out DP_Defs.Aux_Response;
224 Response_Length : out DP_Defs.Aux_Response_Length;
225 Success : out Boolean)
226 is
227 Request : DP_Defs.Aux_Request;
228 begin
229 Fill_Aux_Request
230 (Request => Request,
231 Command => Command,
232 Address => Address,
233 Length => Length);
234 for Try in Positive range 1 .. 7 loop
235 Do_Aux_Request
236 (Port => Port,
237 Request => Request,
238 Request_Length => (if Is_Empty (Request) then 3 else 4),
239 Response => Response,
240 Response_Length => Response_Length,
241 Success => Success);
242 exit when not (Success and Is_I2C_Defer (Response));
243
244 Time.U_Delay (500);
245 end loop;
246 Success := Success and then Is_I2C_Ack (Response);
247 end I2C_In_Packet;
248
249 procedure I2C_Empty_Packet
250 (Port : in T;
251 Command : in DP_Defs.Aux_Message_Command;
252 Address : in DP_Defs.Aux_Message_Address;
253 Success : out Boolean)
254 is
255 Ignored_Response : DP_Defs.Aux_Response;
256 Ignored_Response_Length : DP_Defs.Aux_Response_Length;
257 begin
258 pragma Warnings (GNATprove, Off,
259 "unused assignment to ""Ignored_Response*""",
260 Reason => "No response expected here");
261 I2C_In_Packet
262 (Port => Port,
263 Command => Command,
264 Address => Address,
265 Length => 0,
266 Response => Ignored_Response,
267 Response_Length => Ignored_Response_Length,
268 Success => Success);
269 end I2C_Empty_Packet;
270
271 ----------------------------------------------------------------------------
272
273 procedure Do_I2C_Write
274 (Port : in T;
275 Address : in I2C.Transfer_Address;
276 Length : in DP_Defs.Aux_Payload_Length;
277 Data : in DP_Defs.Aux_Payload;
278 Success : out Boolean)
279 is
280 Ignored_Success : Boolean;
281 begin
282 -- I2C address "start" packet
283 I2C_Empty_Packet
284 (Port => Port,
285 Command => DP_AUX_I2C_WRITE or DP_AUX_I2C_MOT,
286 Address => DP_Defs.Aux_Message_Address (Address),
287 Success => Success);
288
289 if Success then
290 I2C_Out_Packet
291 (Port => Port,
292 Command => DP_AUX_I2C_WRITE or DP_AUX_I2C_MOT,
293 Address => DP_Defs.Aux_Message_Address (Address),
294 Length => Length,
295 Data => Data,
296 Success => Success);
297
298 pragma Warnings
299 (GNATprove, Off, "unused assignment to ""Ignored_Success""",
300 Reason => "Doesn't matter as long as the transfer itself succeeds");
301 -- I2C address "stop" packet
302 I2C_Empty_Packet
303 (Port => Port,
304 Command => DP_AUX_I2C_WRITE,
305 Address => DP_Defs.Aux_Message_Address (Address),
306 Success => Ignored_Success);
307 end if;
308 end Do_I2C_Write;
309
310 procedure Do_I2C_Read
311 (Port : in T;
312 Address : in I2C.Transfer_Address;
313 Length : in out I2C.Transfer_Length;
314 Data : in out I2C.Transfer_Data;
315 Success : out Boolean)
316 is
317 Xfered : Natural := 0;
318 Chunk : DP_Defs.Aux_Payload_Length := DP_Defs.Aux_Payload_Length'Last;
319
320 Tries : Natural := 0;
321 Max_Tries : constant := 4;
322
323 Response : DP_Defs.Aux_Response;
324 Response_Length : DP_Defs.Aux_Response_Length;
325
326 Ignored_Success : Boolean;
327 begin
328 -- I2C address "start" packet
329 I2C_Empty_Packet
330 (Port => Port,
331 Command => DP_AUX_I2C_READ or DP_AUX_I2C_MOT,
332 Address => DP_Defs.Aux_Message_Address (Address),
333 Success => Success);
334
335 while Success and then (Xfered < Length and Tries < Max_Tries) loop
336 I2C_In_Packet
337 (Port => Port,
338 Command => DP_AUX_I2C_READ or DP_AUX_I2C_MOT,
339 Address => DP_Defs.Aux_Message_Address (Address),
340 Length => Natural'Min (Chunk, Length - Xfered),
341 Response => Response,
342 Response_Length => Response_Length,
343 Success => Success);
344
345 if Success and then Response_Length >= 2 then
346 Chunk := Natural'Min (Response_Length - 1, Length - Xfered);
347 Data (Xfered .. Xfered + Chunk - 1) := Response (1 .. Chunk);
348 Xfered := Xfered + Chunk;
349 Tries := 0;
350 else
351 Tries := Tries + 1;
352 end if;
353 pragma Loop_Invariant (Xfered <= Length);
354 end loop;
355
356 if Success then
357 pragma Warnings
358 (GNATprove, Off, "unused assignment to ""Ignored_Success""",
359 Reason => "Doesn't matter as long as the transfer itself succeeds");
360 -- I2C address "stop" packet
361 I2C_Empty_Packet
362 (Port => Port,
363 Command => DP_AUX_I2C_READ,
364 Address => DP_Defs.Aux_Message_Address (Address),
365 Success => Ignored_Success);
366 end if;
367
368 Success := Success and then Tries < Max_Tries;
369 Length := Xfered;
370 end Do_I2C_Read;
371
372 ----------------------------------------------------------------------------
373
Angel Pons0a8174b2021-03-01 01:51:08 +0100374 procedure I2C_Write_Byte
375 (Port : in T;
376 Address : in I2C.Transfer_Address;
377 Offset : in Word8;
378 Value : in Word8;
379 Success : out Boolean)
380 is
381 Payload : constant DP_Defs.Aux_Payload := (Offset, Value, others => 16#00#);
382
383 Length : constant I2C.Transfer_Length := 1;
384 begin
385 Do_I2C_Write (Port, Address, 1 + Length, Payload, Success);
386 end I2C_Write_Byte;
387
388 procedure I2C_Read_Byte
389 (Port : in T;
390 Address : in I2C.Transfer_Address;
391 Offset : in Word8;
392 Value : out Word8;
393 Success : out Boolean)
394 is
395 Index_Payload : constant DP_Defs.Aux_Payload := (Offset, others => 16#00#);
396
397 Length : I2C.Transfer_Length := 1;
398 Data : I2C.Transfer_Data := (others => 16#00#);
399 begin
400 Do_I2C_Write (Port, Address, 1, Index_Payload, Success);
401
402 if Success then
403 Do_I2C_Read (Port, Address, Length, Data, Success);
404 Success := Success and Length = 1;
405 end if;
406 Value := Data (0);
407 end I2C_Read_Byte;
408
Nico Huber83693c82016-10-08 22:17:55 +0200409 procedure I2C_Read
410 (Port : in T;
411 Address : in I2C.Transfer_Address;
412 Length : in out I2C.Transfer_Length;
413 Data : out I2C.Transfer_Data;
414 Success : out Boolean)
415 is
416 Index_Payload : DP_Defs.Aux_Payload;
417 begin
418 Data := (others => 16#00#);
419
420 Index_Payload := (others => 16#00#); -- send index 0
421 Do_I2C_Write (Port, Address, 1, Index_Payload, Success);
422
423 if Success then
424 Do_I2C_Read (Port, Address, Length, Data, Success);
425 end if;
426 end I2C_Read;
427
428end HW.GFX.DP_Aux_Ch;