blob: 29ce9c15c65887110e3801cc9fd028b5bd37c748 [file] [log] [blame]
Nico Huber83693c82016-10-08 22:17:55 +02001--
2-- Copyright (C) 2015 secunet Security Networks AG
Nico Huberc1d20302019-04-28 00:00:37 +02003-- Copyright (C) 2019 Nico Huber <nico.h@gmx.de>
Nico Huber83693c82016-10-08 22:17:55 +02004--
5-- This program is free software; you can redistribute it and/or modify
6-- it under the terms of the GNU General Public License as published by
Nico Huber125a29e2016-10-18 00:23:54 +02007-- the Free Software Foundation; either version 2 of the License, or
8-- (at your option) any later version.
Nico Huber83693c82016-10-08 22:17:55 +02009--
10-- This program is distributed in the hope that it will be useful,
11-- but WITHOUT ANY WARRANTY; without even the implied warranty of
12-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13-- GNU General Public License for more details.
14--
15
16with HW.Time;
17
18with HW.Debug;
19with GNAT.Source_Info;
20
21with HW.GFX.GMA.Config;
22with HW.GFX.GMA.Registers;
23
24use type HW.Word8;
25
26package body HW.GFX.GMA.I2C is
27
28 PCH_DSPCLK_GATE_D_GMBUS_UNIT_LVL : constant := 1 * 2 ** 31;
29
Tim Wawrzynczak732feb42022-09-09 10:32:37 -060030 function GMBUS0_PIN_PAIR_SELECT_MASK return Word32 is
31 (if Config.Has_Type_C_Ports then 15 * 2 ** 0 else 7 * 2 ** 0);
32
Nico Huber83693c82016-10-08 22:17:55 +020033 GMBUS0_GMBUS_RATE_SELECT_MASK : constant := 7 * 2 ** 8;
34 GMBUS0_GMBUS_RATE_SELECT_100KHZ : constant := 0 * 2 ** 8;
35 GMBUS0_GMBUS_RATE_SELECT_50KHZ : constant := 1 * 2 ** 8;
Nico Huber83693c82016-10-08 22:17:55 +020036 GMBUS0_PIN_PAIR_SELECT_NONE : constant := 0 * 2 ** 0;
37 GMBUS0_PIN_PAIR_SELECT_DAC : constant := 2 * 2 ** 0;
38 GMBUS0_PIN_PAIR_SELECT_LVDS : constant := 3 * 2 ** 0;
39 -- Order is C, B, D: no typo!
40 GMBUS0_PIN_PAIR_SELECT_DIGI_C : constant := 4 * 2 ** 0;
41 GMBUS0_PIN_PAIR_SELECT_DIGI_B : constant := 5 * 2 ** 0;
42 GMBUS0_PIN_PAIR_SELECT_DIGI_D : constant := 6 * 2 ** 0;
Nico Huberdde06302020-12-20 02:18:30 +010043 -- Broxton and later use different pins
Nico Huber1c3b9282017-02-09 13:57:04 +010044 GMBUS0_PIN_PAIR_SELECT_BXT_B : constant := 1 * 2 ** 0;
45 GMBUS0_PIN_PAIR_SELECT_BXT_C : constant := 2 * 2 ** 0;
Nico Huberdde06302020-12-20 02:18:30 +010046 GMBUS0_PIN_PAIR_SELECT_BXT_D : constant := 3 * 2 ** 0;
Tim Wawrzynczak732feb42022-09-09 10:32:37 -060047 -- Tiger Lake and later use even different pins
48 GMBUS0_PIN_PAIR_SELECT_TGL_1 : constant := 1 * 2 ** 0;
49 GMBUS0_PIN_PAIR_SELECT_TGL_2 : constant := 2 * 2 ** 0;
50 GMBUS0_PIN_PAIR_SELECT_TGL_3 : constant := 3 * 2 ** 0;
51 GMBUS0_PIN_PAIR_SELECT_TGL_TC1 : constant := 9 * 2 ** 0;
52 GMBUS0_PIN_PAIR_SELECT_TGL_TC2 : constant := 10 * 2 ** 0;
53 GMBUS0_PIN_PAIR_SELECT_TGL_TC3 : constant := 11 * 2 ** 0;
54 GMBUS0_PIN_PAIR_SELECT_TGL_TC4 : constant := 12 * 2 ** 0;
55 GMBUS0_PIN_PAIR_SELECT_TGL_TC5 : constant := 13 * 2 ** 0;
56 GMBUS0_PIN_PAIR_SELECT_TGL_TC6 : constant := 14 * 2 ** 0;
Nico Huber83693c82016-10-08 22:17:55 +020057
58 GMBUS1_SOFTWARE_CLEAR_INTERRUPT : constant := 1 * 2 ** 31;
59 GMBUS1_SOFTWARE_READY : constant := 1 * 2 ** 30;
60 GMBUS1_ENABLE_TIMEOUT : constant := 1 * 2 ** 29;
61 GMBUS1_BUS_CYCLE_SELECT_MASK : constant := 7 * 2 ** 25;
62 GMBUS1_BUS_CYCLE_STOP : constant := 1 * 2 ** 27;
63 GMBUS1_BUS_CYCLE_INDEX : constant := 1 * 2 ** 26;
64 GMBUS1_BUS_CYCLE_WAIT : constant := 1 * 2 ** 25;
65 GMBUS1_TOTAL_BYTE_COUNT_MASK : constant := 511 * 2 ** 16;
66 GMBUS1_TOTAL_BYTE_COUNT_SHIFT : constant := 16;
67 GMBUS1_8BIT_SLAVE_INDEX_MASK : constant := 255 * 2 ** 8;
68 GMBUS1_8BIT_SLAVE_INDEX_SHIFT : constant := 8;
69 GMBUS1_SLAVE_ADDRESS_MASK : constant := 127 * 2 ** 1;
70 GMBUS1_SLAVE_ADDRESS_SHIFT : constant := 1;
71 GMBUS1_DIRECTION_MASK : constant := 1 * 2 ** 0;
72 GMBUS1_DIRECTION_WRITE : constant := 0 * 2 ** 0;
73 GMBUS1_DIRECTION_READ : constant := 1 * 2 ** 0;
74
75 GMBUS2_INUSE : constant := 1 * 2 ** 15;
76 GMBUS2_HARDWARE_WAIT_PHASE : constant := 1 * 2 ** 14;
77 GMBUS2_SLAVE_STALL_TIMEOUT_ERROR : constant := 1 * 2 ** 13;
78 GMBUS2_GMBUS_INTERRUPT_STATUS : constant := 1 * 2 ** 12;
79 GMBUS2_HARDWARE_READY : constant := 1 * 2 ** 11;
80 GMBUS2_NAK_INDICATOR : constant := 1 * 2 ** 10;
81 GMBUS2_GMBUS_ACTIVE : constant := 1 * 2 ** 9;
82 GMBUS2_CURRENT_BYTE_COUNT_MASK : constant := 511 * 2 ** 0;
83
84 GMBUS4_INTERRUPT_MASK : constant := 31 * 2 ** 0;
85
86 GMBUS5_2BYTE_INDEX_ENABLE : constant := 1 * 2 ** 31;
87
Arthur Heymans229ed1c2018-03-28 16:45:43 +020088 GMBUS_Regs : constant array (0 .. 5) of Registers.Registers_Index :=
89 (if Config.Has_PCH_GMBUS then
90 (0 => Registers.PCH_GMBUS0,
91 1 => Registers.PCH_GMBUS1,
92 2 => Registers.PCH_GMBUS2,
93 3 => Registers.PCH_GMBUS3,
94 4 => Registers.PCH_GMBUS4,
95 5 => Registers.PCH_GMBUS5)
96 else
97 (0 => Registers.GMCH_GMBUS0,
98 1 => Registers.GMCH_GMBUS1,
99 2 => Registers.GMCH_GMBUS2,
100 3 => Registers.GMCH_GMBUS3,
101 4 => Registers.GMCH_GMBUS4,
102 5 => Registers.GMCH_GMBUS5));
103
Nico Huber83693c82016-10-08 22:17:55 +0200104 function GMBUS1_TOTAL_BYTE_COUNT
105 (Count : HW.GFX.I2C.Transfer_Length)
106 return Word32 is
107 begin
108 return Shift_Left (Word32 (Count), GMBUS1_TOTAL_BYTE_COUNT_SHIFT);
109 end GMBUS1_TOTAL_BYTE_COUNT;
110
111 function GMBUS1_SLAVE_ADDRESS
112 (Address : HW.GFX.I2C.Transfer_Address)
113 return Word32 is
114 begin
115 return Shift_Left (Word32 (Address), GMBUS1_SLAVE_ADDRESS_SHIFT);
116 end GMBUS1_SLAVE_ADDRESS;
117
118 function GMBUS0_PIN_PAIR_SELECT (Port : PCH_Port) return Word32 is
119 begin
120 return
Tim Wawrzynczak732feb42022-09-09 10:32:37 -0600121 (if Config.Has_Type_C_Ports then
122 (case Port is
123 when PCH_HDMI_A => GMBUS0_PIN_PAIR_SELECT_TGL_1,
124 when PCH_HDMI_B => GMBUS0_PIN_PAIR_SELECT_TGL_2,
125 when PCH_HDMI_C => GMBUS0_PIN_PAIR_SELECT_TGL_3,
126 when PCH_TC1 => GMBUS0_PIN_PAIR_SELECT_TGL_TC1,
127 when PCH_TC2 => GMBUS0_PIN_PAIR_SELECT_TGL_TC2,
128 when PCH_TC3 => GMBUS0_PIN_PAIR_SELECT_TGL_TC3,
129 when PCH_TC4 => GMBUS0_PIN_PAIR_SELECT_TGL_TC4,
130 when PCH_TC5 => GMBUS0_PIN_PAIR_SELECT_TGL_TC5,
131 when PCH_TC6 => GMBUS0_PIN_PAIR_SELECT_TGL_TC6,
132 when others => GMBUS0_PIN_PAIR_SELECT_NONE)
133 elsif Config.GMBUS_Alternative_Pins then
Nico Huber1c3b9282017-02-09 13:57:04 +0100134 (case Port is
135 when PCH_HDMI_B => GMBUS0_PIN_PAIR_SELECT_BXT_B,
136 when PCH_HDMI_C => GMBUS0_PIN_PAIR_SELECT_BXT_C,
Nico Huberdde06302020-12-20 02:18:30 +0100137 when PCH_HDMI_D => GMBUS0_PIN_PAIR_SELECT_BXT_D,
Nico Huber1c3b9282017-02-09 13:57:04 +0100138 when others => GMBUS0_PIN_PAIR_SELECT_NONE)
139 else
140 (case Port is
141 when PCH_DAC => GMBUS0_PIN_PAIR_SELECT_DAC,
142 when PCH_LVDS => GMBUS0_PIN_PAIR_SELECT_LVDS,
143 when PCH_HDMI_B => GMBUS0_PIN_PAIR_SELECT_DIGI_B,
144 when PCH_HDMI_C => GMBUS0_PIN_PAIR_SELECT_DIGI_C,
145 when PCH_HDMI_D => GMBUS0_PIN_PAIR_SELECT_DIGI_D,
146 when others => GMBUS0_PIN_PAIR_SELECT_NONE));
Nico Huber83693c82016-10-08 22:17:55 +0200147 end GMBUS0_PIN_PAIR_SELECT;
148
149 ----------------------------------------------------------------------------
150
Nico Huberefa3ca82019-05-10 13:28:52 +0200151 function GMBUS_Ready (GMBUS2 : Word32) return Boolean is
152 ((GMBUS2 and (GMBUS2_HARDWARE_WAIT_PHASE or
153 GMBUS2_SLAVE_STALL_TIMEOUT_ERROR or
154 GMBUS2_GMBUS_INTERRUPT_STATUS or
155 GMBUS2_NAK_INDICATOR or
156 GMBUS2_GMBUS_ACTIVE)) = 0);
157
158 procedure Check_And_Reset (Success : out Boolean)
Nico Huber83693c82016-10-08 22:17:55 +0200159 is
160 GMBUS2 : Word32;
161 begin
Nico Huber83693c82016-10-08 22:17:55 +0200162 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
163
Nico Huberefa3ca82019-05-10 13:28:52 +0200164 Registers.Read (GMBUS_Regs (2), GMBUS2);
165 if (GMBUS2 and GMBUS2_GMBUS_ACTIVE) /= 0 then
166 Registers.Write
167 (Register => GMBUS_Regs (1),
168 Value => GMBUS1_SOFTWARE_READY or GMBUS1_BUS_CYCLE_STOP);
169 Registers.Wait_Unset_Mask
170 (Register => GMBUS_Regs (2),
171 Mask => GMBUS2_GMBUS_ACTIVE,
172 TOut_MS => 1);
173 Registers.Read (GMBUS_Regs (2), GMBUS2);
174 end if;
175 Success := GMBUS_Ready (GMBUS2);
Nico Huber83693c82016-10-08 22:17:55 +0200176
Nico Huberefa3ca82019-05-10 13:28:52 +0200177 if not Success then
178 Registers.Write (GMBUS_Regs (1), GMBUS1_SOFTWARE_CLEAR_INTERRUPT);
179 Registers.Write (GMBUS_Regs (1), 0);
180 Registers.Read (GMBUS_Regs (2), GMBUS2);
181 Success := GMBUS_Ready (GMBUS2);
182 end if;
183 end Check_And_Reset;
Nico Huber83693c82016-10-08 22:17:55 +0200184
185 procedure Init_GMBUS (Port : PCH_Port; Success : out Boolean) is
186 begin
187 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
188
189 if Config.Ungate_GMBUS_Unit_Level then
190 Registers.Set_Mask
191 (Register => Registers.PCH_DSPCLK_GATE_D,
192 Mask => PCH_DSPCLK_GATE_D_GMBUS_UNIT_LVL);
193 end if;
194
195 -- TODO: Refactor + check for timeout.
Arthur Heymans229ed1c2018-03-28 16:45:43 +0200196 Registers.Wait_Unset_Mask (GMBUS_Regs (2), GMBUS2_INUSE);
Nico Huber83693c82016-10-08 22:17:55 +0200197
Nico Huberefa3ca82019-05-10 13:28:52 +0200198 Registers.Write (GMBUS_Regs (4), 0);
199 Registers.Write (GMBUS_Regs (5), 0);
Nico Huber83693c82016-10-08 22:17:55 +0200200
Nico Huberefa3ca82019-05-10 13:28:52 +0200201 -- Resetting the state machine only works if a valid port
202 -- is selected and we don't always know which ports are
203 -- valid. So do the cleanup before we use the GMBUS with
204 -- the current port. If the port is valid, the reset should
205 -- work, if not, it shouldn't matter.
206 Registers.Write
207 (Register => GMBUS_Regs (0),
208 Value => GMBUS0_GMBUS_RATE_SELECT_100KHZ or
209 GMBUS0_PIN_PAIR_SELECT (Port));
210 Check_And_Reset (Success);
Nico Huber83693c82016-10-08 22:17:55 +0200211 end Init_GMBUS;
212
213 procedure Release_GMBUS
214 is
215 begin
216 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
217
Arthur Heymans229ed1c2018-03-28 16:45:43 +0200218 Registers.Write (GMBUS_Regs (0), GMBUS0_PIN_PAIR_SELECT_NONE);
Nico Huber83693c82016-10-08 22:17:55 +0200219
220 -- Clear INUSE. TODO: Don't do it, if timeout occured (see above).
Arthur Heymans229ed1c2018-03-28 16:45:43 +0200221 Registers.Write (GMBUS_Regs (2), GMBUS2_INUSE);
Nico Huber83693c82016-10-08 22:17:55 +0200222
223 if Config.Ungate_GMBUS_Unit_Level then
224 Registers.Unset_Mask
225 (Register => Registers.PCH_DSPCLK_GATE_D,
226 Mask => PCH_DSPCLK_GATE_D_GMBUS_UNIT_LVL);
227 end if;
228 end Release_GMBUS;
229
230 procedure I2C_Read
231 (Port : in PCH_Port;
232 Address : in HW.GFX.I2C.Transfer_Address;
233 Length : in out HW.GFX.I2C.Transfer_Length;
234 Data : out HW.GFX.I2C.Transfer_Data;
235 Success : out Boolean)
236 is
Nico Huberc1d20302019-04-28 00:00:37 +0200237 GMBUS2 : Word32 := 0;
Nico Huber83693c82016-10-08 22:17:55 +0200238 GMBUS3 : Word32;
239
240 Current : HW.GFX.I2C.Transfer_Length;
241 Transfered : HW.GFX.I2C.Transfer_Length := 0;
242 begin
243 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
244
245 Data := (others => 0);
246
247 Init_GMBUS (Port, Success);
248 if Success then
249 Registers.Write
Arthur Heymans229ed1c2018-03-28 16:45:43 +0200250 (Register => GMBUS_Regs (1),
Nico Huber83693c82016-10-08 22:17:55 +0200251 Value => GMBUS1_SOFTWARE_READY or
252 GMBUS1_BUS_CYCLE_INDEX or
253 GMBUS1_BUS_CYCLE_WAIT or
254 GMBUS1_TOTAL_BYTE_COUNT (Length) or
255 GMBUS1_SLAVE_ADDRESS (Address) or
256 GMBUS1_DIRECTION_READ);
257
258 while Success and then Transfered < Length loop
259 Registers.Wait_Set_Mask
Arthur Heymans229ed1c2018-03-28 16:45:43 +0200260 (Register => GMBUS_Regs (2),
Nico Huber83693c82016-10-08 22:17:55 +0200261 Mask => GMBUS2_HARDWARE_READY,
Nico Hubera4e7f252019-09-17 16:25:49 +0200262 TOut_MS => 500);
Nico Huber83693c82016-10-08 22:17:55 +0200263
Arthur Heymans229ed1c2018-03-28 16:45:43 +0200264 Registers.Read (GMBUS_Regs (2), GMBUS2);
Nico Huber83693c82016-10-08 22:17:55 +0200265 Success := (GMBUS2 and GMBUS2_HARDWARE_READY) /= 0 and
266 (GMBUS2 and GMBUS2_NAK_INDICATOR) = 0;
267 if Success then
268 Current := GFX.I2C.Transfer_Length'Min (Length, Transfered + 4);
269
Arthur Heymans229ed1c2018-03-28 16:45:43 +0200270 Registers.Read (GMBUS_Regs (3), GMBUS3);
Nico Huber83693c82016-10-08 22:17:55 +0200271 for I in Transfered .. Current - 1 loop
272 Data (I) := Byte (GMBUS3 and 16#ff#);
273 GMBUS3 := Shift_Right (GMBUS3, 8);
274 end loop;
275 Transfered := Current;
276 end if;
277 end loop;
278 if Success then
279 Registers.Wait_Set_Mask
Arthur Heymans229ed1c2018-03-28 16:45:43 +0200280 (Register => GMBUS_Regs (2),
Nico Huber83693c82016-10-08 22:17:55 +0200281 Mask => GMBUS2_HARDWARE_WAIT_PHASE);
282 Registers.Write
Arthur Heymans229ed1c2018-03-28 16:45:43 +0200283 (Register => GMBUS_Regs (1),
Nico Huber83693c82016-10-08 22:17:55 +0200284 Value => GMBUS1_SOFTWARE_READY or GMBUS1_BUS_CYCLE_STOP);
285 Registers.Wait_Unset_Mask
Arthur Heymans229ed1c2018-03-28 16:45:43 +0200286 (Register => GMBUS_Regs (2),
Nico Huber83693c82016-10-08 22:17:55 +0200287 Mask => GMBUS2_GMBUS_ACTIVE);
Nico Huberc1d20302019-04-28 00:00:37 +0200288 elsif (GMBUS2 and GMBUS2_NAK_INDICATOR) /= 0 then
289 Registers.Wait_Unset_Mask
290 (Register => GMBUS_Regs (2),
291 Mask => GMBUS2_GMBUS_ACTIVE);
292 Registers.Write (GMBUS_Regs (1), GMBUS1_SOFTWARE_CLEAR_INTERRUPT);
293 Registers.Write (GMBUS_Regs (1), 0);
Nico Huber83693c82016-10-08 22:17:55 +0200294 end if;
295 end if;
296 Length := Transfered;
297
298 Release_GMBUS;
299 end I2C_Read;
300
301end HW.GFX.GMA.I2C;