blob: 2f8b982f4b34052229520370589b0348363370dc [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
6-- the Free Software Foundation; version 2 of the License.
7--
8-- This program is distributed in the hope that it will be useful,
9-- but WITHOUT ANY WARRANTY; without even the implied warranty of
10-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11-- GNU General Public License for more details.
12--
13
14with HW.Time;
15with HW.GFX.DP_Defs;
16
17use type HW.Word8;
18use type HW.GFX.DP_Defs.Aux_Message_Command;
19
20package body HW.GFX.DP_Aux_Ch is
21
22 DP_AUX_I2C_WRITE : constant := 16#0#;
23 DP_AUX_I2C_READ : constant := 16#1#;
24 DP_AUX_I2C_WR_STATUS_REQ : constant := 16#2#;
25 DP_AUX_I2C_MOT : constant := 16#4#;
26 DP_AUX_NATIVE_WRITE : constant := 16#8#;
27 DP_AUX_NATIVE_READ : constant := 16#9#;
28
29 procedure Fill_Aux_Request
30 (Request : out DP_Defs.Aux_Request;
31 Command : in DP_Defs.Aux_Message_Command;
32 Address : in DP_Defs.Aux_Message_Address;
33 Length : in DP_Defs.Aux_Payload_Length)
34 is
35 begin
36 Request :=
37 (0 => Shift_Left (Word8 (Command), 4) or
38 Word8 (Shift_Right (Word32 (Address), 16)),
39 1 => Word8 (Shift_Right (Word32 (Address), 8) and 16#ff#),
40 2 => Word8 (Shift_Right (Word32 (Address), 0) and 16#ff#),
41 3 => Word8 (Length) - 1,
42 others => 0); -- Don't care
43 end Fill_Aux_Request;
44
45 function Is_Empty (Request : DP_Defs.Aux_Request) return Boolean is
46 begin
47 return Request (3) = 16#ff#;
48 end Is_Empty;
49
50 function Is_Aux_Ack (Response : DP_Defs.Aux_Response) return Boolean
51 with
52 Depends => (Is_Aux_Ack'Result => Response)
53 is
54 begin
55 return (Response (0) and 16#30#) = 16#00#;
56 end Is_Aux_Ack;
57
58 function Is_Aux_Defer (Response : DP_Defs.Aux_Response) return Boolean is
59 begin
60 return (Response (0) and 16#30#) = 16#20#;
61 end Is_Aux_Defer;
62
63 function Is_I2C_Ack (Response : DP_Defs.Aux_Response) return Boolean
64 with
65 Depends => (Is_I2C_Ack'Result => Response)
66 is
67 begin
68 return (Response (0) and 16#c0#) = 16#00#;
69 end Is_I2C_Ack;
70
71 function Is_I2C_Defer (Response : DP_Defs.Aux_Response) return Boolean is
72 begin
73 return (Response (0) and 16#c0#) = 16#80#;
74 end Is_I2C_Defer;
75
76 ----------------------------------------------------------------------------
77
78 procedure Do_Aux_Request
79 (Port : in T;
80 Request : in DP_Defs.Aux_Request;
81 Request_Length : in DP_Defs.Aux_Request_Length;
82 Response : out DP_Defs.Aux_Response;
83 Response_Length : out DP_Defs.Aux_Response_Length;
84 Success : out Boolean)
85 is
86 begin
87 for Try in Positive range 1 .. 32 loop
88 Aux_Request
89 (Port => Port,
90 Request => Request,
91 Request_Length => Request_Length,
92 Response => Response,
93 Response_Length => Response_Length,
94 Success => Success);
95
96 exit when not (Success and Is_Aux_Defer (Response));
97 Time.U_Delay (500);
98 end loop;
99
100 Success := Success and then Is_Aux_Ack (Response);
101 end Do_Aux_Request;
102
103 ----------------------------------------------------------------------------
104
105 procedure Aux_Read
106 (Port : in T;
107 Address : in DP_Defs.Aux_Message_Address;
108 Length : in out DP_Defs.Aux_Payload_Length;
109 Data : out DP_Defs.Aux_Payload;
110 Success : out Boolean)
111 is
112 Request : DP_Defs.Aux_Request;
113 Response : DP_Defs.Aux_Response;
114 Response_Length : DP_Defs.Aux_Response_Length;
115 begin
116 Data := (others => 0); -- Initialize
117
118 Fill_Aux_Request
119 (Request => Request,
120 Command => DP_AUX_NATIVE_READ,
121 Address => Address,
122 Length => Length);
123
124 Do_Aux_Request
125 (Port => Port,
126 Request => Request,
127 Request_Length => 4,
128 Response => Response,
129 Response_Length => Response_Length,
130 Success => Success);
131
132 Success := Success and then Response_Length > 1;
133 if Success then
134 pragma Assert (Response_Length > 1);
135 Length := Response_Length - 1;
136 Data (0 .. Length - 1) := Response (1 .. Length);
137 end if;
138 end Aux_Read;
139
140 procedure Aux_Write
141 (Port : in T;
142 Address : in DP_Defs.Aux_Message_Address;
143 Length : in DP_Defs.Aux_Payload_Length;
144 Data : in DP_Defs.Aux_Payload;
145 Success : out Boolean)
146 is
147 Request : DP_Defs.Aux_Request;
148
149 Ignored_Response : DP_Defs.Aux_Response;
150 Ignored_Response_Length : DP_Defs.Aux_Response_Length;
151 begin
152 Fill_Aux_Request
153 (Request => Request,
154 Command => DP_AUX_NATIVE_WRITE,
155 Address => Address,
156 Length => Length);
157 Request (4 .. Length + 4 - 1) := Data (0 .. Length - 1);
158
159 pragma Warnings (GNATprove, Off,
160 "unused assignment to ""Ignored_Response*""",
161 Reason => "No response expected here");
162 Do_Aux_Request
163 (Port => Port,
164 Request => Request,
165 Request_Length => 4 + Length,
166 Response => Ignored_Response,
167 Response_Length => Ignored_Response_Length,
168 Success => Success);
169 end Aux_Write;
170
171 ----------------------------------------------------------------------------
172
173 procedure I2C_Out_Packet
174 (Port : in T;
175 Command : in DP_Defs.Aux_Message_Command;
176 Address : in DP_Defs.Aux_Message_Address;
177 Length : in DP_Defs.Aux_Payload_Length;
178 Data : in DP_Defs.Aux_Payload;
179 Success : out Boolean)
180 is
181 Request : DP_Defs.Aux_Request;
182
183 Response : DP_Defs.Aux_Response;
184 Ignored_Response_Length : DP_Defs.Aux_Response_Length;
185 begin
186 Fill_Aux_Request
187 (Request => Request,
188 Command => Command,
189 Address => Address,
190 Length => Length);
191 Request (4 .. Length + 4 - 1) := Data (0 .. Length - 1);
192 for Try in Positive range 1 .. 7 loop
193 pragma Warnings (GNATprove, Off,
194 "unused assignment to ""Ignored_Response_Length""",
195 Reason => "No response expected here");
196 Do_Aux_Request
197 (Port => Port,
198 Request => Request,
199 Request_Length => (if Is_Empty (Request) then 3 else 4 + Length),
200 Response => Response,
201 Response_Length => Ignored_Response_Length,
202 Success => Success);
203 exit when not (Success and Is_I2C_Defer (Response));
204
205 -- Command was already AUX-acked. Thus, only query for
206 -- new status from now on until we get I2C-acked too.
207 Fill_Aux_Request
208 (Request => Request,
209 Command => (Command and DP_AUX_I2C_MOT) or DP_AUX_I2C_WR_STATUS_REQ,
210 Address => Address,
211 Length => 0);
212 Time.U_Delay (500);
213 end loop;
214 Success := Success and then Is_I2C_Ack (Response);
215 end I2C_Out_Packet;
216
217 procedure I2C_In_Packet
218 (Port : in T;
219 Command : in DP_Defs.Aux_Message_Command;
220 Address : in DP_Defs.Aux_Message_Address;
221 Length : in DP_Defs.Aux_Payload_Length;
222 Response : out DP_Defs.Aux_Response;
223 Response_Length : out DP_Defs.Aux_Response_Length;
224 Success : out Boolean)
225 is
226 Request : DP_Defs.Aux_Request;
227 begin
228 Fill_Aux_Request
229 (Request => Request,
230 Command => Command,
231 Address => Address,
232 Length => Length);
233 for Try in Positive range 1 .. 7 loop
234 Do_Aux_Request
235 (Port => Port,
236 Request => Request,
237 Request_Length => (if Is_Empty (Request) then 3 else 4),
238 Response => Response,
239 Response_Length => Response_Length,
240 Success => Success);
241 exit when not (Success and Is_I2C_Defer (Response));
242
243 Time.U_Delay (500);
244 end loop;
245 Success := Success and then Is_I2C_Ack (Response);
246 end I2C_In_Packet;
247
248 procedure I2C_Empty_Packet
249 (Port : in T;
250 Command : in DP_Defs.Aux_Message_Command;
251 Address : in DP_Defs.Aux_Message_Address;
252 Success : out Boolean)
253 is
254 Ignored_Response : DP_Defs.Aux_Response;
255 Ignored_Response_Length : DP_Defs.Aux_Response_Length;
256 begin
257 pragma Warnings (GNATprove, Off,
258 "unused assignment to ""Ignored_Response*""",
259 Reason => "No response expected here");
260 I2C_In_Packet
261 (Port => Port,
262 Command => Command,
263 Address => Address,
264 Length => 0,
265 Response => Ignored_Response,
266 Response_Length => Ignored_Response_Length,
267 Success => Success);
268 end I2C_Empty_Packet;
269
270 ----------------------------------------------------------------------------
271
272 procedure Do_I2C_Write
273 (Port : in T;
274 Address : in I2C.Transfer_Address;
275 Length : in DP_Defs.Aux_Payload_Length;
276 Data : in DP_Defs.Aux_Payload;
277 Success : out Boolean)
278 is
279 Ignored_Success : Boolean;
280 begin
281 -- I2C address "start" packet
282 I2C_Empty_Packet
283 (Port => Port,
284 Command => DP_AUX_I2C_WRITE or DP_AUX_I2C_MOT,
285 Address => DP_Defs.Aux_Message_Address (Address),
286 Success => Success);
287
288 if Success then
289 I2C_Out_Packet
290 (Port => Port,
291 Command => DP_AUX_I2C_WRITE or DP_AUX_I2C_MOT,
292 Address => DP_Defs.Aux_Message_Address (Address),
293 Length => Length,
294 Data => Data,
295 Success => Success);
296
297 pragma Warnings
298 (GNATprove, Off, "unused assignment to ""Ignored_Success""",
299 Reason => "Doesn't matter as long as the transfer itself succeeds");
300 -- I2C address "stop" packet
301 I2C_Empty_Packet
302 (Port => Port,
303 Command => DP_AUX_I2C_WRITE,
304 Address => DP_Defs.Aux_Message_Address (Address),
305 Success => Ignored_Success);
306 end if;
307 end Do_I2C_Write;
308
309 procedure Do_I2C_Read
310 (Port : in T;
311 Address : in I2C.Transfer_Address;
312 Length : in out I2C.Transfer_Length;
313 Data : in out I2C.Transfer_Data;
314 Success : out Boolean)
315 is
316 Xfered : Natural := 0;
317 Chunk : DP_Defs.Aux_Payload_Length := DP_Defs.Aux_Payload_Length'Last;
318
319 Tries : Natural := 0;
320 Max_Tries : constant := 4;
321
322 Response : DP_Defs.Aux_Response;
323 Response_Length : DP_Defs.Aux_Response_Length;
324
325 Ignored_Success : Boolean;
326 begin
327 -- I2C address "start" packet
328 I2C_Empty_Packet
329 (Port => Port,
330 Command => DP_AUX_I2C_READ or DP_AUX_I2C_MOT,
331 Address => DP_Defs.Aux_Message_Address (Address),
332 Success => Success);
333
334 while Success and then (Xfered < Length and Tries < Max_Tries) loop
335 I2C_In_Packet
336 (Port => Port,
337 Command => DP_AUX_I2C_READ or DP_AUX_I2C_MOT,
338 Address => DP_Defs.Aux_Message_Address (Address),
339 Length => Natural'Min (Chunk, Length - Xfered),
340 Response => Response,
341 Response_Length => Response_Length,
342 Success => Success);
343
344 if Success and then Response_Length >= 2 then
345 Chunk := Natural'Min (Response_Length - 1, Length - Xfered);
346 Data (Xfered .. Xfered + Chunk - 1) := Response (1 .. Chunk);
347 Xfered := Xfered + Chunk;
348 Tries := 0;
349 else
350 Tries := Tries + 1;
351 end if;
352 pragma Loop_Invariant (Xfered <= Length);
353 end loop;
354
355 if Success then
356 pragma Warnings
357 (GNATprove, Off, "unused assignment to ""Ignored_Success""",
358 Reason => "Doesn't matter as long as the transfer itself succeeds");
359 -- I2C address "stop" packet
360 I2C_Empty_Packet
361 (Port => Port,
362 Command => DP_AUX_I2C_READ,
363 Address => DP_Defs.Aux_Message_Address (Address),
364 Success => Ignored_Success);
365 end if;
366
367 Success := Success and then Tries < Max_Tries;
368 Length := Xfered;
369 end Do_I2C_Read;
370
371 ----------------------------------------------------------------------------
372
373 procedure I2C_Read
374 (Port : in T;
375 Address : in I2C.Transfer_Address;
376 Length : in out I2C.Transfer_Length;
377 Data : out I2C.Transfer_Data;
378 Success : out Boolean)
379 is
380 Index_Payload : DP_Defs.Aux_Payload;
381 begin
382 Data := (others => 16#00#);
383
384 Index_Payload := (others => 16#00#); -- send index 0
385 Do_I2C_Write (Port, Address, 1, Index_Payload, Success);
386
387 if Success then
388 Do_I2C_Read (Port, Address, Length, Data, Success);
389 end if;
390 end I2C_Read;
391
392end HW.GFX.DP_Aux_Ch;