blob: 67e90ab0c4c310477d702413d1f53c402ae85b4c [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
30 GMBUS0_GMBUS_RATE_SELECT_MASK : constant := 7 * 2 ** 8;
31 GMBUS0_GMBUS_RATE_SELECT_100KHZ : constant := 0 * 2 ** 8;
32 GMBUS0_GMBUS_RATE_SELECT_50KHZ : constant := 1 * 2 ** 8;
33 GMBUS0_PIN_PAIR_SELECT_MASK : constant := 7 * 2 ** 0;
34 GMBUS0_PIN_PAIR_SELECT_NONE : constant := 0 * 2 ** 0;
35 GMBUS0_PIN_PAIR_SELECT_DAC : constant := 2 * 2 ** 0;
36 GMBUS0_PIN_PAIR_SELECT_LVDS : constant := 3 * 2 ** 0;
37 -- Order is C, B, D: no typo!
38 GMBUS0_PIN_PAIR_SELECT_DIGI_C : constant := 4 * 2 ** 0;
39 GMBUS0_PIN_PAIR_SELECT_DIGI_B : constant := 5 * 2 ** 0;
40 GMBUS0_PIN_PAIR_SELECT_DIGI_D : constant := 6 * 2 ** 0;
Nico Huberdde06302020-12-20 02:18:30 +010041 -- Broxton and later use different pins
Nico Huber1c3b9282017-02-09 13:57:04 +010042 GMBUS0_PIN_PAIR_SELECT_BXT_B : constant := 1 * 2 ** 0;
43 GMBUS0_PIN_PAIR_SELECT_BXT_C : constant := 2 * 2 ** 0;
Nico Huberdde06302020-12-20 02:18:30 +010044 GMBUS0_PIN_PAIR_SELECT_BXT_D : constant := 3 * 2 ** 0;
Nico Huber83693c82016-10-08 22:17:55 +020045
46 GMBUS1_SOFTWARE_CLEAR_INTERRUPT : constant := 1 * 2 ** 31;
47 GMBUS1_SOFTWARE_READY : constant := 1 * 2 ** 30;
48 GMBUS1_ENABLE_TIMEOUT : constant := 1 * 2 ** 29;
49 GMBUS1_BUS_CYCLE_SELECT_MASK : constant := 7 * 2 ** 25;
50 GMBUS1_BUS_CYCLE_STOP : constant := 1 * 2 ** 27;
51 GMBUS1_BUS_CYCLE_INDEX : constant := 1 * 2 ** 26;
52 GMBUS1_BUS_CYCLE_WAIT : constant := 1 * 2 ** 25;
53 GMBUS1_TOTAL_BYTE_COUNT_MASK : constant := 511 * 2 ** 16;
54 GMBUS1_TOTAL_BYTE_COUNT_SHIFT : constant := 16;
55 GMBUS1_8BIT_SLAVE_INDEX_MASK : constant := 255 * 2 ** 8;
56 GMBUS1_8BIT_SLAVE_INDEX_SHIFT : constant := 8;
57 GMBUS1_SLAVE_ADDRESS_MASK : constant := 127 * 2 ** 1;
58 GMBUS1_SLAVE_ADDRESS_SHIFT : constant := 1;
59 GMBUS1_DIRECTION_MASK : constant := 1 * 2 ** 0;
60 GMBUS1_DIRECTION_WRITE : constant := 0 * 2 ** 0;
61 GMBUS1_DIRECTION_READ : constant := 1 * 2 ** 0;
62
63 GMBUS2_INUSE : constant := 1 * 2 ** 15;
64 GMBUS2_HARDWARE_WAIT_PHASE : constant := 1 * 2 ** 14;
65 GMBUS2_SLAVE_STALL_TIMEOUT_ERROR : constant := 1 * 2 ** 13;
66 GMBUS2_GMBUS_INTERRUPT_STATUS : constant := 1 * 2 ** 12;
67 GMBUS2_HARDWARE_READY : constant := 1 * 2 ** 11;
68 GMBUS2_NAK_INDICATOR : constant := 1 * 2 ** 10;
69 GMBUS2_GMBUS_ACTIVE : constant := 1 * 2 ** 9;
70 GMBUS2_CURRENT_BYTE_COUNT_MASK : constant := 511 * 2 ** 0;
71
72 GMBUS4_INTERRUPT_MASK : constant := 31 * 2 ** 0;
73
74 GMBUS5_2BYTE_INDEX_ENABLE : constant := 1 * 2 ** 31;
75
Arthur Heymans229ed1c2018-03-28 16:45:43 +020076 GMBUS_Regs : constant array (0 .. 5) of Registers.Registers_Index :=
77 (if Config.Has_PCH_GMBUS then
78 (0 => Registers.PCH_GMBUS0,
79 1 => Registers.PCH_GMBUS1,
80 2 => Registers.PCH_GMBUS2,
81 3 => Registers.PCH_GMBUS3,
82 4 => Registers.PCH_GMBUS4,
83 5 => Registers.PCH_GMBUS5)
84 else
85 (0 => Registers.GMCH_GMBUS0,
86 1 => Registers.GMCH_GMBUS1,
87 2 => Registers.GMCH_GMBUS2,
88 3 => Registers.GMCH_GMBUS3,
89 4 => Registers.GMCH_GMBUS4,
90 5 => Registers.GMCH_GMBUS5));
91
Nico Huber83693c82016-10-08 22:17:55 +020092 function GMBUS1_TOTAL_BYTE_COUNT
93 (Count : HW.GFX.I2C.Transfer_Length)
94 return Word32 is
95 begin
96 return Shift_Left (Word32 (Count), GMBUS1_TOTAL_BYTE_COUNT_SHIFT);
97 end GMBUS1_TOTAL_BYTE_COUNT;
98
99 function GMBUS1_SLAVE_ADDRESS
100 (Address : HW.GFX.I2C.Transfer_Address)
101 return Word32 is
102 begin
103 return Shift_Left (Word32 (Address), GMBUS1_SLAVE_ADDRESS_SHIFT);
104 end GMBUS1_SLAVE_ADDRESS;
105
106 function GMBUS0_PIN_PAIR_SELECT (Port : PCH_Port) return Word32 is
107 begin
108 return
Nico Huber1c3b9282017-02-09 13:57:04 +0100109 (if Config.GMBUS_Alternative_Pins then
110 (case Port is
111 when PCH_HDMI_B => GMBUS0_PIN_PAIR_SELECT_BXT_B,
112 when PCH_HDMI_C => GMBUS0_PIN_PAIR_SELECT_BXT_C,
Nico Huberdde06302020-12-20 02:18:30 +0100113 when PCH_HDMI_D => GMBUS0_PIN_PAIR_SELECT_BXT_D,
Nico Huber1c3b9282017-02-09 13:57:04 +0100114 when others => GMBUS0_PIN_PAIR_SELECT_NONE)
115 else
116 (case Port is
117 when PCH_DAC => GMBUS0_PIN_PAIR_SELECT_DAC,
118 when PCH_LVDS => GMBUS0_PIN_PAIR_SELECT_LVDS,
119 when PCH_HDMI_B => GMBUS0_PIN_PAIR_SELECT_DIGI_B,
120 when PCH_HDMI_C => GMBUS0_PIN_PAIR_SELECT_DIGI_C,
121 when PCH_HDMI_D => GMBUS0_PIN_PAIR_SELECT_DIGI_D,
122 when others => GMBUS0_PIN_PAIR_SELECT_NONE));
Nico Huber83693c82016-10-08 22:17:55 +0200123 end GMBUS0_PIN_PAIR_SELECT;
124
125 ----------------------------------------------------------------------------
126
Nico Huberefa3ca82019-05-10 13:28:52 +0200127 function GMBUS_Ready (GMBUS2 : Word32) return Boolean is
128 ((GMBUS2 and (GMBUS2_HARDWARE_WAIT_PHASE or
129 GMBUS2_SLAVE_STALL_TIMEOUT_ERROR or
130 GMBUS2_GMBUS_INTERRUPT_STATUS or
131 GMBUS2_NAK_INDICATOR or
132 GMBUS2_GMBUS_ACTIVE)) = 0);
133
134 procedure Check_And_Reset (Success : out Boolean)
Nico Huber83693c82016-10-08 22:17:55 +0200135 is
136 GMBUS2 : Word32;
137 begin
Nico Huber83693c82016-10-08 22:17:55 +0200138 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
139
Nico Huberefa3ca82019-05-10 13:28:52 +0200140 Registers.Read (GMBUS_Regs (2), GMBUS2);
141 if (GMBUS2 and GMBUS2_GMBUS_ACTIVE) /= 0 then
142 Registers.Write
143 (Register => GMBUS_Regs (1),
144 Value => GMBUS1_SOFTWARE_READY or GMBUS1_BUS_CYCLE_STOP);
145 Registers.Wait_Unset_Mask
146 (Register => GMBUS_Regs (2),
147 Mask => GMBUS2_GMBUS_ACTIVE,
148 TOut_MS => 1);
149 Registers.Read (GMBUS_Regs (2), GMBUS2);
150 end if;
151 Success := GMBUS_Ready (GMBUS2);
Nico Huber83693c82016-10-08 22:17:55 +0200152
Nico Huberefa3ca82019-05-10 13:28:52 +0200153 if not Success then
154 Registers.Write (GMBUS_Regs (1), GMBUS1_SOFTWARE_CLEAR_INTERRUPT);
155 Registers.Write (GMBUS_Regs (1), 0);
156 Registers.Read (GMBUS_Regs (2), GMBUS2);
157 Success := GMBUS_Ready (GMBUS2);
158 end if;
159 end Check_And_Reset;
Nico Huber83693c82016-10-08 22:17:55 +0200160
161 procedure Init_GMBUS (Port : PCH_Port; Success : out Boolean) is
162 begin
163 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
164
165 if Config.Ungate_GMBUS_Unit_Level then
166 Registers.Set_Mask
167 (Register => Registers.PCH_DSPCLK_GATE_D,
168 Mask => PCH_DSPCLK_GATE_D_GMBUS_UNIT_LVL);
169 end if;
170
171 -- TODO: Refactor + check for timeout.
Arthur Heymans229ed1c2018-03-28 16:45:43 +0200172 Registers.Wait_Unset_Mask (GMBUS_Regs (2), GMBUS2_INUSE);
Nico Huber83693c82016-10-08 22:17:55 +0200173
Nico Huberefa3ca82019-05-10 13:28:52 +0200174 Registers.Write (GMBUS_Regs (4), 0);
175 Registers.Write (GMBUS_Regs (5), 0);
Nico Huber83693c82016-10-08 22:17:55 +0200176
Nico Huberefa3ca82019-05-10 13:28:52 +0200177 -- Resetting the state machine only works if a valid port
178 -- is selected and we don't always know which ports are
179 -- valid. So do the cleanup before we use the GMBUS with
180 -- the current port. If the port is valid, the reset should
181 -- work, if not, it shouldn't matter.
182 Registers.Write
183 (Register => GMBUS_Regs (0),
184 Value => GMBUS0_GMBUS_RATE_SELECT_100KHZ or
185 GMBUS0_PIN_PAIR_SELECT (Port));
186 Check_And_Reset (Success);
Nico Huber83693c82016-10-08 22:17:55 +0200187 end Init_GMBUS;
188
189 procedure Release_GMBUS
190 is
191 begin
192 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
193
Arthur Heymans229ed1c2018-03-28 16:45:43 +0200194 Registers.Write (GMBUS_Regs (0), GMBUS0_PIN_PAIR_SELECT_NONE);
Nico Huber83693c82016-10-08 22:17:55 +0200195
196 -- Clear INUSE. TODO: Don't do it, if timeout occured (see above).
Arthur Heymans229ed1c2018-03-28 16:45:43 +0200197 Registers.Write (GMBUS_Regs (2), GMBUS2_INUSE);
Nico Huber83693c82016-10-08 22:17:55 +0200198
199 if Config.Ungate_GMBUS_Unit_Level then
200 Registers.Unset_Mask
201 (Register => Registers.PCH_DSPCLK_GATE_D,
202 Mask => PCH_DSPCLK_GATE_D_GMBUS_UNIT_LVL);
203 end if;
204 end Release_GMBUS;
205
206 procedure I2C_Read
207 (Port : in PCH_Port;
208 Address : in HW.GFX.I2C.Transfer_Address;
209 Length : in out HW.GFX.I2C.Transfer_Length;
210 Data : out HW.GFX.I2C.Transfer_Data;
211 Success : out Boolean)
212 is
Nico Huberc1d20302019-04-28 00:00:37 +0200213 GMBUS2 : Word32 := 0;
Nico Huber83693c82016-10-08 22:17:55 +0200214 GMBUS3 : Word32;
215
216 Current : HW.GFX.I2C.Transfer_Length;
217 Transfered : HW.GFX.I2C.Transfer_Length := 0;
218 begin
219 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
220
221 Data := (others => 0);
222
223 Init_GMBUS (Port, Success);
224 if Success then
225 Registers.Write
Arthur Heymans229ed1c2018-03-28 16:45:43 +0200226 (Register => GMBUS_Regs (1),
Nico Huber83693c82016-10-08 22:17:55 +0200227 Value => GMBUS1_SOFTWARE_READY or
228 GMBUS1_BUS_CYCLE_INDEX or
229 GMBUS1_BUS_CYCLE_WAIT or
230 GMBUS1_TOTAL_BYTE_COUNT (Length) or
231 GMBUS1_SLAVE_ADDRESS (Address) or
232 GMBUS1_DIRECTION_READ);
233
234 while Success and then Transfered < Length loop
235 Registers.Wait_Set_Mask
Arthur Heymans229ed1c2018-03-28 16:45:43 +0200236 (Register => GMBUS_Regs (2),
Nico Huber83693c82016-10-08 22:17:55 +0200237 Mask => GMBUS2_HARDWARE_READY,
Nico Hubera4e7f252019-09-17 16:25:49 +0200238 TOut_MS => 500);
Nico Huber83693c82016-10-08 22:17:55 +0200239
Arthur Heymans229ed1c2018-03-28 16:45:43 +0200240 Registers.Read (GMBUS_Regs (2), GMBUS2);
Nico Huber83693c82016-10-08 22:17:55 +0200241 Success := (GMBUS2 and GMBUS2_HARDWARE_READY) /= 0 and
242 (GMBUS2 and GMBUS2_NAK_INDICATOR) = 0;
243 if Success then
244 Current := GFX.I2C.Transfer_Length'Min (Length, Transfered + 4);
245
Arthur Heymans229ed1c2018-03-28 16:45:43 +0200246 Registers.Read (GMBUS_Regs (3), GMBUS3);
Nico Huber83693c82016-10-08 22:17:55 +0200247 for I in Transfered .. Current - 1 loop
248 Data (I) := Byte (GMBUS3 and 16#ff#);
249 GMBUS3 := Shift_Right (GMBUS3, 8);
250 end loop;
251 Transfered := Current;
252 end if;
253 end loop;
254 if Success then
255 Registers.Wait_Set_Mask
Arthur Heymans229ed1c2018-03-28 16:45:43 +0200256 (Register => GMBUS_Regs (2),
Nico Huber83693c82016-10-08 22:17:55 +0200257 Mask => GMBUS2_HARDWARE_WAIT_PHASE);
258 Registers.Write
Arthur Heymans229ed1c2018-03-28 16:45:43 +0200259 (Register => GMBUS_Regs (1),
Nico Huber83693c82016-10-08 22:17:55 +0200260 Value => GMBUS1_SOFTWARE_READY or GMBUS1_BUS_CYCLE_STOP);
261 Registers.Wait_Unset_Mask
Arthur Heymans229ed1c2018-03-28 16:45:43 +0200262 (Register => GMBUS_Regs (2),
Nico Huber83693c82016-10-08 22:17:55 +0200263 Mask => GMBUS2_GMBUS_ACTIVE);
Nico Huberc1d20302019-04-28 00:00:37 +0200264 elsif (GMBUS2 and GMBUS2_NAK_INDICATOR) /= 0 then
265 Registers.Wait_Unset_Mask
266 (Register => GMBUS_Regs (2),
267 Mask => GMBUS2_GMBUS_ACTIVE);
268 Registers.Write (GMBUS_Regs (1), GMBUS1_SOFTWARE_CLEAR_INTERRUPT);
269 Registers.Write (GMBUS_Regs (1), 0);
Nico Huber83693c82016-10-08 22:17:55 +0200270 end if;
271 end if;
272 Length := Transfered;
273
274 Release_GMBUS;
275 end I2C_Read;
276
277end HW.GFX.GMA.I2C;