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