blob: 5b66f07bdcd485a49e731dbc39d89c2f00e325dc [file] [log] [blame]
Nico Huber83693c82016-10-08 22:17:55 +02001--
2-- Copyright (C) 2015 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;
16
17with HW.Debug;
18with GNAT.Source_Info;
19
20with HW.GFX.GMA.Config;
21with HW.GFX.GMA.Registers;
22
23use type HW.Word8;
24
25package body HW.GFX.GMA.I2C is
26
27 PCH_DSPCLK_GATE_D_GMBUS_UNIT_LVL : constant := 1 * 2 ** 31;
28
29 GMBUS0_GMBUS_RATE_SELECT_MASK : constant := 7 * 2 ** 8;
30 GMBUS0_GMBUS_RATE_SELECT_100KHZ : constant := 0 * 2 ** 8;
31 GMBUS0_GMBUS_RATE_SELECT_50KHZ : constant := 1 * 2 ** 8;
32 GMBUS0_PIN_PAIR_SELECT_MASK : constant := 7 * 2 ** 0;
33 GMBUS0_PIN_PAIR_SELECT_NONE : constant := 0 * 2 ** 0;
34 GMBUS0_PIN_PAIR_SELECT_DAC : constant := 2 * 2 ** 0;
35 GMBUS0_PIN_PAIR_SELECT_LVDS : constant := 3 * 2 ** 0;
36 -- Order is C, B, D: no typo!
37 GMBUS0_PIN_PAIR_SELECT_DIGI_C : constant := 4 * 2 ** 0;
38 GMBUS0_PIN_PAIR_SELECT_DIGI_B : constant := 5 * 2 ** 0;
39 GMBUS0_PIN_PAIR_SELECT_DIGI_D : constant := 6 * 2 ** 0;
40
41 GMBUS1_SOFTWARE_CLEAR_INTERRUPT : constant := 1 * 2 ** 31;
42 GMBUS1_SOFTWARE_READY : constant := 1 * 2 ** 30;
43 GMBUS1_ENABLE_TIMEOUT : constant := 1 * 2 ** 29;
44 GMBUS1_BUS_CYCLE_SELECT_MASK : constant := 7 * 2 ** 25;
45 GMBUS1_BUS_CYCLE_STOP : constant := 1 * 2 ** 27;
46 GMBUS1_BUS_CYCLE_INDEX : constant := 1 * 2 ** 26;
47 GMBUS1_BUS_CYCLE_WAIT : constant := 1 * 2 ** 25;
48 GMBUS1_TOTAL_BYTE_COUNT_MASK : constant := 511 * 2 ** 16;
49 GMBUS1_TOTAL_BYTE_COUNT_SHIFT : constant := 16;
50 GMBUS1_8BIT_SLAVE_INDEX_MASK : constant := 255 * 2 ** 8;
51 GMBUS1_8BIT_SLAVE_INDEX_SHIFT : constant := 8;
52 GMBUS1_SLAVE_ADDRESS_MASK : constant := 127 * 2 ** 1;
53 GMBUS1_SLAVE_ADDRESS_SHIFT : constant := 1;
54 GMBUS1_DIRECTION_MASK : constant := 1 * 2 ** 0;
55 GMBUS1_DIRECTION_WRITE : constant := 0 * 2 ** 0;
56 GMBUS1_DIRECTION_READ : constant := 1 * 2 ** 0;
57
58 GMBUS2_INUSE : constant := 1 * 2 ** 15;
59 GMBUS2_HARDWARE_WAIT_PHASE : constant := 1 * 2 ** 14;
60 GMBUS2_SLAVE_STALL_TIMEOUT_ERROR : constant := 1 * 2 ** 13;
61 GMBUS2_GMBUS_INTERRUPT_STATUS : constant := 1 * 2 ** 12;
62 GMBUS2_HARDWARE_READY : constant := 1 * 2 ** 11;
63 GMBUS2_NAK_INDICATOR : constant := 1 * 2 ** 10;
64 GMBUS2_GMBUS_ACTIVE : constant := 1 * 2 ** 9;
65 GMBUS2_CURRENT_BYTE_COUNT_MASK : constant := 511 * 2 ** 0;
66
67 GMBUS4_INTERRUPT_MASK : constant := 31 * 2 ** 0;
68
69 GMBUS5_2BYTE_INDEX_ENABLE : constant := 1 * 2 ** 31;
70
71 function GMBUS1_TOTAL_BYTE_COUNT
72 (Count : HW.GFX.I2C.Transfer_Length)
73 return Word32 is
74 begin
75 return Shift_Left (Word32 (Count), GMBUS1_TOTAL_BYTE_COUNT_SHIFT);
76 end GMBUS1_TOTAL_BYTE_COUNT;
77
78 function GMBUS1_SLAVE_ADDRESS
79 (Address : HW.GFX.I2C.Transfer_Address)
80 return Word32 is
81 begin
82 return Shift_Left (Word32 (Address), GMBUS1_SLAVE_ADDRESS_SHIFT);
83 end GMBUS1_SLAVE_ADDRESS;
84
85 function GMBUS0_PIN_PAIR_SELECT (Port : PCH_Port) return Word32 is
86 begin
87 return
88 (case Port is
89 when PCH_DAC => GMBUS0_PIN_PAIR_SELECT_DAC,
90 when PCH_LVDS => GMBUS0_PIN_PAIR_SELECT_LVDS,
91 when PCH_HDMI_B => GMBUS0_PIN_PAIR_SELECT_DIGI_B,
92 when PCH_HDMI_C => GMBUS0_PIN_PAIR_SELECT_DIGI_C,
93 when PCH_HDMI_D => GMBUS0_PIN_PAIR_SELECT_DIGI_D,
94 when others => GMBUS0_PIN_PAIR_SELECT_NONE);
95 end GMBUS0_PIN_PAIR_SELECT;
96
97 ----------------------------------------------------------------------------
98
99 procedure GMBUS_Ready (Result : out Boolean)
100 is
101 GMBUS2 : Word32;
102 begin
103 Registers.Read (Registers.PCH_GMBUS2, GMBUS2);
104 Result := (GMBUS2 and (GMBUS2_HARDWARE_WAIT_PHASE or
105 GMBUS2_SLAVE_STALL_TIMEOUT_ERROR or
106 GMBUS2_GMBUS_INTERRUPT_STATUS or
107 GMBUS2_NAK_INDICATOR)) = 0;
108 end GMBUS_Ready;
109
110 procedure Reset_GMBUS (Success : out Boolean) is
111 begin
112 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
113
114 Registers.Write (Registers.PCH_GMBUS1, GMBUS1_SOFTWARE_CLEAR_INTERRUPT);
115 Registers.Write (Registers.PCH_GMBUS1, 0);
116 Registers.Write (Registers.PCH_GMBUS0, GMBUS0_PIN_PAIR_SELECT_NONE);
117
118 GMBUS_Ready (Success);
119 end Reset_GMBUS;
120
121 procedure Init_GMBUS (Port : PCH_Port; Success : out Boolean) is
122 begin
123 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
124
125 if Config.Ungate_GMBUS_Unit_Level then
126 Registers.Set_Mask
127 (Register => Registers.PCH_DSPCLK_GATE_D,
128 Mask => PCH_DSPCLK_GATE_D_GMBUS_UNIT_LVL);
129 end if;
130
131 -- TODO: Refactor + check for timeout.
132 Registers.Wait_Unset_Mask (Registers.PCH_GMBUS2, GMBUS2_INUSE);
133
134 GMBUS_Ready (Success);
135 if not Success then
136 Reset_GMBUS (Success);
137 end if;
138
139 if Success then
140 Registers.Write
141 (Register => Registers.PCH_GMBUS0,
142 Value => GMBUS0_GMBUS_RATE_SELECT_100KHZ or
143 GMBUS0_PIN_PAIR_SELECT (Port));
144 Registers.Write
145 (Register => Registers.PCH_GMBUS4,
146 Value => 0);
147 Registers.Write
148 (Register => Registers.PCH_GMBUS5,
149 Value => 0);
150 end if;
151 end Init_GMBUS;
152
153 procedure Release_GMBUS
154 is
155 begin
156 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
157
158 Registers.Write (Registers.PCH_GMBUS0, GMBUS0_PIN_PAIR_SELECT_NONE);
159
160 -- Clear INUSE. TODO: Don't do it, if timeout occured (see above).
161 Registers.Write (Registers.PCH_GMBUS2, GMBUS2_INUSE);
162
163 if Config.Ungate_GMBUS_Unit_Level then
164 Registers.Unset_Mask
165 (Register => Registers.PCH_DSPCLK_GATE_D,
166 Mask => PCH_DSPCLK_GATE_D_GMBUS_UNIT_LVL);
167 end if;
168 end Release_GMBUS;
169
170 procedure I2C_Read
171 (Port : in PCH_Port;
172 Address : in HW.GFX.I2C.Transfer_Address;
173 Length : in out HW.GFX.I2C.Transfer_Length;
174 Data : out HW.GFX.I2C.Transfer_Data;
175 Success : out Boolean)
176 is
177 GMBUS2,
178 GMBUS3 : Word32;
179
180 Current : HW.GFX.I2C.Transfer_Length;
181 Transfered : HW.GFX.I2C.Transfer_Length := 0;
182 begin
183 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
184
185 Data := (others => 0);
186
187 Init_GMBUS (Port, Success);
188 if Success then
189 Registers.Write
190 (Register => Registers.PCH_GMBUS1,
191 Value => GMBUS1_SOFTWARE_READY or
192 GMBUS1_BUS_CYCLE_INDEX or
193 GMBUS1_BUS_CYCLE_WAIT or
194 GMBUS1_TOTAL_BYTE_COUNT (Length) or
195 GMBUS1_SLAVE_ADDRESS (Address) or
196 GMBUS1_DIRECTION_READ);
197
198 while Success and then Transfered < Length loop
199 Registers.Wait_Set_Mask
200 (Register => Registers.PCH_GMBUS2,
201 Mask => GMBUS2_HARDWARE_READY,
202 TOut_MS => 55);
203
204 Registers.Read (Registers.PCH_GMBUS2, GMBUS2);
205 Success := (GMBUS2 and GMBUS2_HARDWARE_READY) /= 0 and
206 (GMBUS2 and GMBUS2_NAK_INDICATOR) = 0;
207 if Success then
208 Current := GFX.I2C.Transfer_Length'Min (Length, Transfered + 4);
209
210 Registers.Read (Registers.PCH_GMBUS3, GMBUS3);
211 for I in Transfered .. Current - 1 loop
212 Data (I) := Byte (GMBUS3 and 16#ff#);
213 GMBUS3 := Shift_Right (GMBUS3, 8);
214 end loop;
215 Transfered := Current;
216 end if;
217 end loop;
218 if Success then
219 Registers.Wait_Set_Mask
220 (Register => Registers.PCH_GMBUS2,
221 Mask => GMBUS2_HARDWARE_WAIT_PHASE);
222 Registers.Write
223 (Register => Registers.PCH_GMBUS1,
224 Value => GMBUS1_SOFTWARE_READY or GMBUS1_BUS_CYCLE_STOP);
225 Registers.Wait_Unset_Mask
226 (Register => Registers.PCH_GMBUS2,
227 Mask => GMBUS2_GMBUS_ACTIVE);
228 end if;
229 end if;
230 Length := Transfered;
231
232 Release_GMBUS;
233 end I2C_Read;
234
235end HW.GFX.GMA.I2C;