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