blob: a958e260fd5bd729d5802e60a7ca7d2349d2e7eb [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 Huber1c3b9282017-02-09 13:57:04 +010041 -- Broxton uses different pins
42 GMBUS0_PIN_PAIR_SELECT_BXT_B : constant := 1 * 2 ** 0;
43 GMBUS0_PIN_PAIR_SELECT_BXT_C : constant := 2 * 2 ** 0;
Nico Huber83693c82016-10-08 22:17:55 +020044
45 GMBUS1_SOFTWARE_CLEAR_INTERRUPT : constant := 1 * 2 ** 31;
46 GMBUS1_SOFTWARE_READY : constant := 1 * 2 ** 30;
47 GMBUS1_ENABLE_TIMEOUT : constant := 1 * 2 ** 29;
48 GMBUS1_BUS_CYCLE_SELECT_MASK : constant := 7 * 2 ** 25;
49 GMBUS1_BUS_CYCLE_STOP : constant := 1 * 2 ** 27;
50 GMBUS1_BUS_CYCLE_INDEX : constant := 1 * 2 ** 26;
51 GMBUS1_BUS_CYCLE_WAIT : constant := 1 * 2 ** 25;
52 GMBUS1_TOTAL_BYTE_COUNT_MASK : constant := 511 * 2 ** 16;
53 GMBUS1_TOTAL_BYTE_COUNT_SHIFT : constant := 16;
54 GMBUS1_8BIT_SLAVE_INDEX_MASK : constant := 255 * 2 ** 8;
55 GMBUS1_8BIT_SLAVE_INDEX_SHIFT : constant := 8;
56 GMBUS1_SLAVE_ADDRESS_MASK : constant := 127 * 2 ** 1;
57 GMBUS1_SLAVE_ADDRESS_SHIFT : constant := 1;
58 GMBUS1_DIRECTION_MASK : constant := 1 * 2 ** 0;
59 GMBUS1_DIRECTION_WRITE : constant := 0 * 2 ** 0;
60 GMBUS1_DIRECTION_READ : constant := 1 * 2 ** 0;
61
62 GMBUS2_INUSE : constant := 1 * 2 ** 15;
63 GMBUS2_HARDWARE_WAIT_PHASE : constant := 1 * 2 ** 14;
64 GMBUS2_SLAVE_STALL_TIMEOUT_ERROR : constant := 1 * 2 ** 13;
65 GMBUS2_GMBUS_INTERRUPT_STATUS : constant := 1 * 2 ** 12;
66 GMBUS2_HARDWARE_READY : constant := 1 * 2 ** 11;
67 GMBUS2_NAK_INDICATOR : constant := 1 * 2 ** 10;
68 GMBUS2_GMBUS_ACTIVE : constant := 1 * 2 ** 9;
69 GMBUS2_CURRENT_BYTE_COUNT_MASK : constant := 511 * 2 ** 0;
70
71 GMBUS4_INTERRUPT_MASK : constant := 31 * 2 ** 0;
72
73 GMBUS5_2BYTE_INDEX_ENABLE : constant := 1 * 2 ** 31;
74
Arthur Heymans229ed1c2018-03-28 16:45:43 +020075 GMBUS_Regs : constant array (0 .. 5) of Registers.Registers_Index :=
76 (if Config.Has_PCH_GMBUS then
77 (0 => Registers.PCH_GMBUS0,
78 1 => Registers.PCH_GMBUS1,
79 2 => Registers.PCH_GMBUS2,
80 3 => Registers.PCH_GMBUS3,
81 4 => Registers.PCH_GMBUS4,
82 5 => Registers.PCH_GMBUS5)
83 else
84 (0 => Registers.GMCH_GMBUS0,
85 1 => Registers.GMCH_GMBUS1,
86 2 => Registers.GMCH_GMBUS2,
87 3 => Registers.GMCH_GMBUS3,
88 4 => Registers.GMCH_GMBUS4,
89 5 => Registers.GMCH_GMBUS5));
90
Nico Huber83693c82016-10-08 22:17:55 +020091 function GMBUS1_TOTAL_BYTE_COUNT
92 (Count : HW.GFX.I2C.Transfer_Length)
93 return Word32 is
94 begin
95 return Shift_Left (Word32 (Count), GMBUS1_TOTAL_BYTE_COUNT_SHIFT);
96 end GMBUS1_TOTAL_BYTE_COUNT;
97
98 function GMBUS1_SLAVE_ADDRESS
99 (Address : HW.GFX.I2C.Transfer_Address)
100 return Word32 is
101 begin
102 return Shift_Left (Word32 (Address), GMBUS1_SLAVE_ADDRESS_SHIFT);
103 end GMBUS1_SLAVE_ADDRESS;
104
105 function GMBUS0_PIN_PAIR_SELECT (Port : PCH_Port) return Word32 is
106 begin
107 return
Nico Huber1c3b9282017-02-09 13:57:04 +0100108 (if Config.GMBUS_Alternative_Pins then
109 (case Port is
110 when PCH_HDMI_B => GMBUS0_PIN_PAIR_SELECT_BXT_B,
111 when PCH_HDMI_C => GMBUS0_PIN_PAIR_SELECT_BXT_C,
112 when others => GMBUS0_PIN_PAIR_SELECT_NONE)
113 else
114 (case Port is
115 when PCH_DAC => GMBUS0_PIN_PAIR_SELECT_DAC,
116 when PCH_LVDS => GMBUS0_PIN_PAIR_SELECT_LVDS,
117 when PCH_HDMI_B => GMBUS0_PIN_PAIR_SELECT_DIGI_B,
118 when PCH_HDMI_C => GMBUS0_PIN_PAIR_SELECT_DIGI_C,
119 when PCH_HDMI_D => GMBUS0_PIN_PAIR_SELECT_DIGI_D,
120 when others => GMBUS0_PIN_PAIR_SELECT_NONE));
Nico Huber83693c82016-10-08 22:17:55 +0200121 end GMBUS0_PIN_PAIR_SELECT;
122
123 ----------------------------------------------------------------------------
124
Nico Huberefa3ca82019-05-10 13:28:52 +0200125 function GMBUS_Ready (GMBUS2 : Word32) return Boolean is
126 ((GMBUS2 and (GMBUS2_HARDWARE_WAIT_PHASE or
127 GMBUS2_SLAVE_STALL_TIMEOUT_ERROR or
128 GMBUS2_GMBUS_INTERRUPT_STATUS or
129 GMBUS2_NAK_INDICATOR or
130 GMBUS2_GMBUS_ACTIVE)) = 0);
131
132 procedure Check_And_Reset (Success : out Boolean)
Nico Huber83693c82016-10-08 22:17:55 +0200133 is
134 GMBUS2 : Word32;
135 begin
Nico Huber83693c82016-10-08 22:17:55 +0200136 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
137
Nico Huberefa3ca82019-05-10 13:28:52 +0200138 Registers.Read (GMBUS_Regs (2), GMBUS2);
139 if (GMBUS2 and GMBUS2_GMBUS_ACTIVE) /= 0 then
140 Registers.Write
141 (Register => GMBUS_Regs (1),
142 Value => GMBUS1_SOFTWARE_READY or GMBUS1_BUS_CYCLE_STOP);
143 Registers.Wait_Unset_Mask
144 (Register => GMBUS_Regs (2),
145 Mask => GMBUS2_GMBUS_ACTIVE,
146 TOut_MS => 1);
147 Registers.Read (GMBUS_Regs (2), GMBUS2);
148 end if;
149 Success := GMBUS_Ready (GMBUS2);
Nico Huber83693c82016-10-08 22:17:55 +0200150
Nico Huberefa3ca82019-05-10 13:28:52 +0200151 if not Success then
152 Registers.Write (GMBUS_Regs (1), GMBUS1_SOFTWARE_CLEAR_INTERRUPT);
153 Registers.Write (GMBUS_Regs (1), 0);
154 Registers.Read (GMBUS_Regs (2), GMBUS2);
155 Success := GMBUS_Ready (GMBUS2);
156 end if;
157 end Check_And_Reset;
Nico Huber83693c82016-10-08 22:17:55 +0200158
159 procedure Init_GMBUS (Port : PCH_Port; Success : out Boolean) is
160 begin
161 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
162
163 if Config.Ungate_GMBUS_Unit_Level then
164 Registers.Set_Mask
165 (Register => Registers.PCH_DSPCLK_GATE_D,
166 Mask => PCH_DSPCLK_GATE_D_GMBUS_UNIT_LVL);
167 end if;
168
169 -- TODO: Refactor + check for timeout.
Arthur Heymans229ed1c2018-03-28 16:45:43 +0200170 Registers.Wait_Unset_Mask (GMBUS_Regs (2), GMBUS2_INUSE);
Nico Huber83693c82016-10-08 22:17:55 +0200171
Nico Huberefa3ca82019-05-10 13:28:52 +0200172 Registers.Write (GMBUS_Regs (4), 0);
173 Registers.Write (GMBUS_Regs (5), 0);
Nico Huber83693c82016-10-08 22:17:55 +0200174
Nico Huberefa3ca82019-05-10 13:28:52 +0200175 -- Resetting the state machine only works if a valid port
176 -- is selected and we don't always know which ports are
177 -- valid. So do the cleanup before we use the GMBUS with
178 -- the current port. If the port is valid, the reset should
179 -- work, if not, it shouldn't matter.
180 Registers.Write
181 (Register => GMBUS_Regs (0),
182 Value => GMBUS0_GMBUS_RATE_SELECT_100KHZ or
183 GMBUS0_PIN_PAIR_SELECT (Port));
184 Check_And_Reset (Success);
Nico Huber83693c82016-10-08 22:17:55 +0200185 end Init_GMBUS;
186
187 procedure Release_GMBUS
188 is
189 begin
190 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
191
Arthur Heymans229ed1c2018-03-28 16:45:43 +0200192 Registers.Write (GMBUS_Regs (0), GMBUS0_PIN_PAIR_SELECT_NONE);
Nico Huber83693c82016-10-08 22:17:55 +0200193
194 -- Clear INUSE. TODO: Don't do it, if timeout occured (see above).
Arthur Heymans229ed1c2018-03-28 16:45:43 +0200195 Registers.Write (GMBUS_Regs (2), GMBUS2_INUSE);
Nico Huber83693c82016-10-08 22:17:55 +0200196
197 if Config.Ungate_GMBUS_Unit_Level then
198 Registers.Unset_Mask
199 (Register => Registers.PCH_DSPCLK_GATE_D,
200 Mask => PCH_DSPCLK_GATE_D_GMBUS_UNIT_LVL);
201 end if;
202 end Release_GMBUS;
203
204 procedure I2C_Read
205 (Port : in PCH_Port;
206 Address : in HW.GFX.I2C.Transfer_Address;
207 Length : in out HW.GFX.I2C.Transfer_Length;
208 Data : out HW.GFX.I2C.Transfer_Data;
209 Success : out Boolean)
210 is
Nico Huberc1d20302019-04-28 00:00:37 +0200211 GMBUS2 : Word32 := 0;
Nico Huber83693c82016-10-08 22:17:55 +0200212 GMBUS3 : Word32;
213
214 Current : HW.GFX.I2C.Transfer_Length;
215 Transfered : HW.GFX.I2C.Transfer_Length := 0;
216 begin
217 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
218
219 Data := (others => 0);
220
221 Init_GMBUS (Port, Success);
222 if Success then
223 Registers.Write
Arthur Heymans229ed1c2018-03-28 16:45:43 +0200224 (Register => GMBUS_Regs (1),
Nico Huber83693c82016-10-08 22:17:55 +0200225 Value => GMBUS1_SOFTWARE_READY or
226 GMBUS1_BUS_CYCLE_INDEX or
227 GMBUS1_BUS_CYCLE_WAIT or
228 GMBUS1_TOTAL_BYTE_COUNT (Length) or
229 GMBUS1_SLAVE_ADDRESS (Address) or
230 GMBUS1_DIRECTION_READ);
231
232 while Success and then Transfered < Length loop
233 Registers.Wait_Set_Mask
Arthur Heymans229ed1c2018-03-28 16:45:43 +0200234 (Register => GMBUS_Regs (2),
Nico Huber83693c82016-10-08 22:17:55 +0200235 Mask => GMBUS2_HARDWARE_READY,
Nico Hubera4e7f252019-09-17 16:25:49 +0200236 TOut_MS => 500);
Nico Huber83693c82016-10-08 22:17:55 +0200237
Arthur Heymans229ed1c2018-03-28 16:45:43 +0200238 Registers.Read (GMBUS_Regs (2), GMBUS2);
Nico Huber83693c82016-10-08 22:17:55 +0200239 Success := (GMBUS2 and GMBUS2_HARDWARE_READY) /= 0 and
240 (GMBUS2 and GMBUS2_NAK_INDICATOR) = 0;
241 if Success then
242 Current := GFX.I2C.Transfer_Length'Min (Length, Transfered + 4);
243
Arthur Heymans229ed1c2018-03-28 16:45:43 +0200244 Registers.Read (GMBUS_Regs (3), GMBUS3);
Nico Huber83693c82016-10-08 22:17:55 +0200245 for I in Transfered .. Current - 1 loop
246 Data (I) := Byte (GMBUS3 and 16#ff#);
247 GMBUS3 := Shift_Right (GMBUS3, 8);
248 end loop;
249 Transfered := Current;
250 end if;
251 end loop;
252 if Success then
253 Registers.Wait_Set_Mask
Arthur Heymans229ed1c2018-03-28 16:45:43 +0200254 (Register => GMBUS_Regs (2),
Nico Huber83693c82016-10-08 22:17:55 +0200255 Mask => GMBUS2_HARDWARE_WAIT_PHASE);
256 Registers.Write
Arthur Heymans229ed1c2018-03-28 16:45:43 +0200257 (Register => GMBUS_Regs (1),
Nico Huber83693c82016-10-08 22:17:55 +0200258 Value => GMBUS1_SOFTWARE_READY or GMBUS1_BUS_CYCLE_STOP);
259 Registers.Wait_Unset_Mask
Arthur Heymans229ed1c2018-03-28 16:45:43 +0200260 (Register => GMBUS_Regs (2),
Nico Huber83693c82016-10-08 22:17:55 +0200261 Mask => GMBUS2_GMBUS_ACTIVE);
Nico Huberc1d20302019-04-28 00:00:37 +0200262 elsif (GMBUS2 and GMBUS2_NAK_INDICATOR) /= 0 then
263 Registers.Wait_Unset_Mask
264 (Register => GMBUS_Regs (2),
265 Mask => GMBUS2_GMBUS_ACTIVE);
266 Registers.Write (GMBUS_Regs (1), GMBUS1_SOFTWARE_CLEAR_INTERRUPT);
267 Registers.Write (GMBUS_Regs (1), 0);
Nico Huber83693c82016-10-08 22:17:55 +0200268 end if;
269 end if;
270 Length := Transfered;
271
272 Release_GMBUS;
273 end I2C_Read;
274
275end HW.GFX.GMA.I2C;