| -- |
| -- Copyright (C) 2015-2016 secunet Security Networks AG |
| -- |
| -- This program is free software; you can redistribute it and/or modify |
| -- it under the terms of the GNU General Public License as published by |
| -- the Free Software Foundation; either version 2 of the License, or |
| -- (at your option) any later version. |
| -- |
| -- This program is distributed in the hope that it will be useful, |
| -- but WITHOUT ANY WARRANTY; without even the implied warranty of |
| -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| -- GNU General Public License for more details. |
| -- |
| |
| with HW.Time; |
| with HW.GFX.DP_Defs; |
| |
| use type HW.Word8; |
| use type HW.Word32; |
| use type HW.GFX.DP_Defs.Aux_Message_Command; |
| |
| package body HW.GFX.DP_Aux_Ch is |
| |
| DP_AUX_I2C_WRITE : constant := 16#0#; |
| DP_AUX_I2C_READ : constant := 16#1#; |
| DP_AUX_I2C_WR_STATUS_REQ : constant := 16#2#; |
| DP_AUX_I2C_MOT : constant := 16#4#; |
| DP_AUX_NATIVE_WRITE : constant := 16#8#; |
| DP_AUX_NATIVE_READ : constant := 16#9#; |
| |
| procedure Fill_Aux_Request |
| (Request : out DP_Defs.Aux_Request; |
| Command : in DP_Defs.Aux_Message_Command; |
| Address : in DP_Defs.Aux_Message_Address; |
| Length : in DP_Defs.Aux_Payload_Length) |
| is |
| begin |
| Request := |
| (0 => Shift_Left (Word8 (Command), 4) or |
| Word8 (Shift_Right (Word32 (Address), 16)), |
| 1 => Word8 (Shift_Right (Word32 (Address), 8) and 16#ff#), |
| 2 => Word8 (Shift_Right (Word32 (Address), 0) and 16#ff#), |
| 3 => Word8 (Length) - 1, |
| others => 0); -- Don't care |
| end Fill_Aux_Request; |
| |
| function Is_Empty (Request : DP_Defs.Aux_Request) return Boolean is |
| begin |
| return Request (3) = 16#ff#; |
| end Is_Empty; |
| |
| function Is_Aux_Ack (Response : DP_Defs.Aux_Response) return Boolean |
| with |
| Depends => (Is_Aux_Ack'Result => Response) |
| is |
| begin |
| return (Response (0) and 16#30#) = 16#00#; |
| end Is_Aux_Ack; |
| |
| function Is_Aux_Defer (Response : DP_Defs.Aux_Response) return Boolean is |
| begin |
| return (Response (0) and 16#30#) = 16#20#; |
| end Is_Aux_Defer; |
| |
| function Is_I2C_Ack (Response : DP_Defs.Aux_Response) return Boolean |
| with |
| Depends => (Is_I2C_Ack'Result => Response) |
| is |
| begin |
| return (Response (0) and 16#c0#) = 16#00#; |
| end Is_I2C_Ack; |
| |
| function Is_I2C_Defer (Response : DP_Defs.Aux_Response) return Boolean is |
| begin |
| return (Response (0) and 16#c0#) = 16#80#; |
| end Is_I2C_Defer; |
| |
| ---------------------------------------------------------------------------- |
| |
| procedure Do_Aux_Request |
| (Port : in T; |
| Request : in DP_Defs.Aux_Request; |
| Request_Length : in DP_Defs.Aux_Request_Length; |
| Response : out DP_Defs.Aux_Response; |
| Response_Length : out DP_Defs.Aux_Response_Length; |
| Success : out Boolean) |
| is |
| begin |
| for Try in Positive range 1 .. 32 loop |
| Aux_Request |
| (Port => Port, |
| Request => Request, |
| Request_Length => Request_Length, |
| Response => Response, |
| Response_Length => Response_Length, |
| Success => Success); |
| |
| exit when not (Success and Is_Aux_Defer (Response)); |
| Time.U_Delay (500); |
| end loop; |
| |
| Success := Success and then Is_Aux_Ack (Response); |
| end Do_Aux_Request; |
| |
| ---------------------------------------------------------------------------- |
| |
| procedure Aux_Read |
| (Port : in T; |
| Address : in DP_Defs.Aux_Message_Address; |
| Length : in out DP_Defs.Aux_Payload_Length; |
| Data : out DP_Defs.Aux_Payload; |
| Success : out Boolean) |
| is |
| Request : DP_Defs.Aux_Request; |
| Response : DP_Defs.Aux_Response; |
| Response_Length : DP_Defs.Aux_Response_Length; |
| begin |
| Data := (others => 0); -- Initialize |
| |
| Fill_Aux_Request |
| (Request => Request, |
| Command => DP_AUX_NATIVE_READ, |
| Address => Address, |
| Length => Length); |
| |
| Do_Aux_Request |
| (Port => Port, |
| Request => Request, |
| Request_Length => 4, |
| Response => Response, |
| Response_Length => Response_Length, |
| Success => Success); |
| |
| Success := Success and then Response_Length > 1; |
| if Success then |
| pragma Assert (Response_Length > 1); |
| Length := Response_Length - 1; |
| Data (0 .. Length - 1) := Response (1 .. Length); |
| end if; |
| end Aux_Read; |
| |
| procedure Aux_Write |
| (Port : in T; |
| Address : in DP_Defs.Aux_Message_Address; |
| Length : in DP_Defs.Aux_Payload_Length; |
| Data : in DP_Defs.Aux_Payload; |
| Success : out Boolean) |
| is |
| Request : DP_Defs.Aux_Request; |
| |
| Ignored_Response : DP_Defs.Aux_Response; |
| Ignored_Response_Length : DP_Defs.Aux_Response_Length; |
| begin |
| Fill_Aux_Request |
| (Request => Request, |
| Command => DP_AUX_NATIVE_WRITE, |
| Address => Address, |
| Length => Length); |
| Request (4 .. Length + 4 - 1) := Data (0 .. Length - 1); |
| |
| pragma Warnings (GNATprove, Off, |
| "unused assignment to ""Ignored_Response*""", |
| Reason => "No response expected here"); |
| Do_Aux_Request |
| (Port => Port, |
| Request => Request, |
| Request_Length => 4 + Length, |
| Response => Ignored_Response, |
| Response_Length => Ignored_Response_Length, |
| Success => Success); |
| end Aux_Write; |
| |
| ---------------------------------------------------------------------------- |
| |
| procedure I2C_Out_Packet |
| (Port : in T; |
| Command : in DP_Defs.Aux_Message_Command; |
| Address : in DP_Defs.Aux_Message_Address; |
| Length : in DP_Defs.Aux_Payload_Length; |
| Data : in DP_Defs.Aux_Payload; |
| Success : out Boolean) |
| is |
| Request : DP_Defs.Aux_Request; |
| |
| Xfered : Natural := 0; |
| Tries : Natural := 0; |
| Max_Defers : constant := 7; |
| |
| Response : DP_Defs.Aux_Response; |
| Response_Length : DP_Defs.Aux_Response_Length; |
| begin |
| Fill_Aux_Request |
| (Request => Request, |
| Command => Command, |
| Address => Address, |
| Length => Length); |
| Request (4 .. Length + 4 - 1) := Data (0 .. Length - 1); |
| |
| loop |
| Do_Aux_Request |
| (Port => Port, |
| Request => Request, |
| Request_Length => (if Is_Empty (Request) then 3 else 4 + Length), |
| Response => Response, |
| Response_Length => Response_Length, |
| Success => Success); |
| exit when not Success; |
| |
| if Is_I2C_Ack (Response) then |
| if Response_Length <= 1 then |
| Xfered := Length; |
| else |
| Xfered := Xfered + Natural'Min (Natural (Response (1)), Length - Xfered); |
| Tries := 0; |
| end if; |
| exit when Xfered = Length; |
| elsif Is_I2C_Defer (Response) then |
| exit when Tries >= Max_Defers; |
| Tries := Tries + 1; |
| else -- Nak |
| exit; |
| end if; |
| |
| -- Command was already AUX-acked. Thus, only query for new |
| -- status from now on until we got all bytes I2C-acked too. |
| Fill_Aux_Request |
| (Request => Request, |
| Command => (Command and DP_AUX_I2C_MOT) or DP_AUX_I2C_WR_STATUS_REQ, |
| Address => Address, |
| Length => 0); |
| Time.U_Delay (500); |
| end loop; |
| Success := Success and then (Is_I2C_Ack (Response) and Xfered = Length); |
| end I2C_Out_Packet; |
| |
| procedure I2C_In_Packet |
| (Port : in T; |
| Command : in DP_Defs.Aux_Message_Command; |
| Address : in DP_Defs.Aux_Message_Address; |
| Length : in DP_Defs.Aux_Payload_Length; |
| Response : out DP_Defs.Aux_Response; |
| Response_Length : out DP_Defs.Aux_Response_Length; |
| Success : out Boolean) |
| is |
| Request : DP_Defs.Aux_Request; |
| begin |
| Fill_Aux_Request |
| (Request => Request, |
| Command => Command, |
| Address => Address, |
| Length => Length); |
| for Try in Positive range 1 .. 7 loop |
| Do_Aux_Request |
| (Port => Port, |
| Request => Request, |
| Request_Length => (if Is_Empty (Request) then 3 else 4), |
| Response => Response, |
| Response_Length => Response_Length, |
| Success => Success); |
| exit when not (Success and Is_I2C_Defer (Response)); |
| |
| Time.U_Delay (500); |
| end loop; |
| Success := Success and then Is_I2C_Ack (Response); |
| end I2C_In_Packet; |
| |
| procedure I2C_Empty_Packet |
| (Port : in T; |
| Command : in DP_Defs.Aux_Message_Command; |
| Address : in DP_Defs.Aux_Message_Address; |
| Success : out Boolean) |
| is |
| Ignored_Response : DP_Defs.Aux_Response; |
| Ignored_Response_Length : DP_Defs.Aux_Response_Length; |
| begin |
| pragma Warnings (GNATprove, Off, |
| "unused assignment to ""Ignored_Response*""", |
| Reason => "No response expected here"); |
| I2C_In_Packet |
| (Port => Port, |
| Command => Command, |
| Address => Address, |
| Length => 0, |
| Response => Ignored_Response, |
| Response_Length => Ignored_Response_Length, |
| Success => Success); |
| end I2C_Empty_Packet; |
| |
| ---------------------------------------------------------------------------- |
| |
| procedure Do_I2C_Write |
| (Port : in T; |
| Address : in I2C.Transfer_Address; |
| Length : in DP_Defs.Aux_Payload_Length; |
| Data : in DP_Defs.Aux_Payload; |
| Success : out Boolean) |
| is |
| Ignored_Success : Boolean; |
| begin |
| -- I2C address "start" packet |
| I2C_Empty_Packet |
| (Port => Port, |
| Command => DP_AUX_I2C_WRITE or DP_AUX_I2C_MOT, |
| Address => DP_Defs.Aux_Message_Address (Address), |
| Success => Success); |
| |
| if Success then |
| I2C_Out_Packet |
| (Port => Port, |
| Command => DP_AUX_I2C_WRITE or DP_AUX_I2C_MOT, |
| Address => DP_Defs.Aux_Message_Address (Address), |
| Length => Length, |
| Data => Data, |
| Success => Success); |
| |
| pragma Warnings |
| (GNATprove, Off, "unused assignment to ""Ignored_Success""", |
| Reason => "Doesn't matter as long as the transfer itself succeeds"); |
| -- I2C address "stop" packet |
| I2C_Empty_Packet |
| (Port => Port, |
| Command => DP_AUX_I2C_WRITE, |
| Address => DP_Defs.Aux_Message_Address (Address), |
| Success => Ignored_Success); |
| end if; |
| end Do_I2C_Write; |
| |
| procedure Do_I2C_Read |
| (Port : in T; |
| Address : in I2C.Transfer_Address; |
| Length : in out I2C.Transfer_Length; |
| Data : in out I2C.Transfer_Data; |
| Success : out Boolean) |
| is |
| Xfered : Natural := 0; |
| Chunk : DP_Defs.Aux_Payload_Length := DP_Defs.Aux_Payload_Length'Last; |
| |
| Tries : Natural := 0; |
| Max_Tries : constant := 4; |
| |
| Response : DP_Defs.Aux_Response; |
| Response_Length : DP_Defs.Aux_Response_Length; |
| |
| Ignored_Success : Boolean; |
| begin |
| -- I2C address "start" packet |
| I2C_Empty_Packet |
| (Port => Port, |
| Command => DP_AUX_I2C_READ or DP_AUX_I2C_MOT, |
| Address => DP_Defs.Aux_Message_Address (Address), |
| Success => Success); |
| |
| while Success and then (Xfered < Length and Tries < Max_Tries) loop |
| I2C_In_Packet |
| (Port => Port, |
| Command => DP_AUX_I2C_READ or DP_AUX_I2C_MOT, |
| Address => DP_Defs.Aux_Message_Address (Address), |
| Length => Natural'Min (Chunk, Length - Xfered), |
| Response => Response, |
| Response_Length => Response_Length, |
| Success => Success); |
| |
| if Success and then Response_Length >= 2 then |
| Chunk := Natural'Min (Response_Length - 1, Length - Xfered); |
| Data (Xfered .. Xfered + Chunk - 1) := Response (1 .. Chunk); |
| Xfered := Xfered + Chunk; |
| Tries := 0; |
| else |
| Tries := Tries + 1; |
| end if; |
| pragma Loop_Invariant (Xfered <= Length); |
| end loop; |
| |
| if Success then |
| pragma Warnings |
| (GNATprove, Off, "unused assignment to ""Ignored_Success""", |
| Reason => "Doesn't matter as long as the transfer itself succeeds"); |
| -- I2C address "stop" packet |
| I2C_Empty_Packet |
| (Port => Port, |
| Command => DP_AUX_I2C_READ, |
| Address => DP_Defs.Aux_Message_Address (Address), |
| Success => Ignored_Success); |
| end if; |
| |
| Success := Success and then Tries < Max_Tries; |
| Length := Xfered; |
| end Do_I2C_Read; |
| |
| ---------------------------------------------------------------------------- |
| |
| procedure I2C_Write_Byte |
| (Port : in T; |
| Address : in I2C.Transfer_Address; |
| Offset : in Word8; |
| Value : in Word8; |
| Success : out Boolean) |
| is |
| Payload : constant DP_Defs.Aux_Payload := (Offset, Value, others => 16#00#); |
| |
| Length : constant I2C.Transfer_Length := 1; |
| begin |
| Do_I2C_Write (Port, Address, 1 + Length, Payload, Success); |
| end I2C_Write_Byte; |
| |
| procedure I2C_Read_Byte |
| (Port : in T; |
| Address : in I2C.Transfer_Address; |
| Offset : in Word8; |
| Value : out Word8; |
| Success : out Boolean) |
| is |
| Index_Payload : constant DP_Defs.Aux_Payload := (Offset, others => 16#00#); |
| |
| Length : I2C.Transfer_Length := 1; |
| Data : I2C.Transfer_Data := (others => 16#00#); |
| begin |
| Do_I2C_Write (Port, Address, 1, Index_Payload, Success); |
| |
| if Success then |
| Do_I2C_Read (Port, Address, Length, Data, Success); |
| Success := Success and Length = 1; |
| end if; |
| Value := Data (0); |
| end I2C_Read_Byte; |
| |
| procedure I2C_Read |
| (Port : in T; |
| Address : in I2C.Transfer_Address; |
| Length : in out I2C.Transfer_Length; |
| Data : out I2C.Transfer_Data; |
| Success : out Boolean) |
| is |
| Index_Payload : DP_Defs.Aux_Payload; |
| begin |
| Data := (others => 16#00#); |
| |
| Index_Payload := (others => 16#00#); -- send index 0 |
| Do_I2C_Write (Port, Address, 1, Index_Payload, Success); |
| |
| if Success then |
| Do_I2C_Read (Port, Address, Length, Data, Success); |
| end if; |
| end I2C_Read; |
| |
| end HW.GFX.DP_Aux_Ch; |