blob: fe26f1c4ddfe716d00a62aff3041cce1f48c4517 [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
125 procedure GMBUS_Ready (Result : out Boolean)
126 is
127 GMBUS2 : Word32;
128 begin
Arthur Heymans229ed1c2018-03-28 16:45:43 +0200129 Registers.Read (GMBUS_Regs (2), GMBUS2);
Nico Huber83693c82016-10-08 22:17:55 +0200130 Result := (GMBUS2 and (GMBUS2_HARDWARE_WAIT_PHASE or
131 GMBUS2_SLAVE_STALL_TIMEOUT_ERROR or
132 GMBUS2_GMBUS_INTERRUPT_STATUS or
133 GMBUS2_NAK_INDICATOR)) = 0;
134 end GMBUS_Ready;
135
136 procedure Reset_GMBUS (Success : out Boolean) is
137 begin
138 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
139
Arthur Heymans229ed1c2018-03-28 16:45:43 +0200140 Registers.Write (GMBUS_Regs (1), GMBUS1_SOFTWARE_CLEAR_INTERRUPT);
141 Registers.Write (GMBUS_Regs (1), 0);
142 Registers.Write (GMBUS_Regs (0), GMBUS0_PIN_PAIR_SELECT_NONE);
Nico Huber83693c82016-10-08 22:17:55 +0200143
144 GMBUS_Ready (Success);
145 end Reset_GMBUS;
146
147 procedure Init_GMBUS (Port : PCH_Port; Success : out Boolean) is
148 begin
149 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
150
151 if Config.Ungate_GMBUS_Unit_Level then
152 Registers.Set_Mask
153 (Register => Registers.PCH_DSPCLK_GATE_D,
154 Mask => PCH_DSPCLK_GATE_D_GMBUS_UNIT_LVL);
155 end if;
156
157 -- TODO: Refactor + check for timeout.
Arthur Heymans229ed1c2018-03-28 16:45:43 +0200158 Registers.Wait_Unset_Mask (GMBUS_Regs (2), GMBUS2_INUSE);
Nico Huber83693c82016-10-08 22:17:55 +0200159
160 GMBUS_Ready (Success);
161 if not Success then
162 Reset_GMBUS (Success);
163 end if;
164
165 if Success then
166 Registers.Write
Arthur Heymans229ed1c2018-03-28 16:45:43 +0200167 (Register => GMBUS_Regs (0),
Nico Huber83693c82016-10-08 22:17:55 +0200168 Value => GMBUS0_GMBUS_RATE_SELECT_100KHZ or
169 GMBUS0_PIN_PAIR_SELECT (Port));
170 Registers.Write
Arthur Heymans229ed1c2018-03-28 16:45:43 +0200171 (Register => GMBUS_Regs (4),
Nico Huber83693c82016-10-08 22:17:55 +0200172 Value => 0);
173 Registers.Write
Arthur Heymans229ed1c2018-03-28 16:45:43 +0200174 (Register => GMBUS_Regs (5),
Nico Huber83693c82016-10-08 22:17:55 +0200175 Value => 0);
176 end if;
177 end Init_GMBUS;
178
179 procedure Release_GMBUS
180 is
181 begin
182 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
183
Arthur Heymans229ed1c2018-03-28 16:45:43 +0200184 Registers.Write (GMBUS_Regs (0), GMBUS0_PIN_PAIR_SELECT_NONE);
Nico Huber83693c82016-10-08 22:17:55 +0200185
186 -- Clear INUSE. TODO: Don't do it, if timeout occured (see above).
Arthur Heymans229ed1c2018-03-28 16:45:43 +0200187 Registers.Write (GMBUS_Regs (2), GMBUS2_INUSE);
Nico Huber83693c82016-10-08 22:17:55 +0200188
189 if Config.Ungate_GMBUS_Unit_Level then
190 Registers.Unset_Mask
191 (Register => Registers.PCH_DSPCLK_GATE_D,
192 Mask => PCH_DSPCLK_GATE_D_GMBUS_UNIT_LVL);
193 end if;
194 end Release_GMBUS;
195
196 procedure I2C_Read
197 (Port : in PCH_Port;
198 Address : in HW.GFX.I2C.Transfer_Address;
199 Length : in out HW.GFX.I2C.Transfer_Length;
200 Data : out HW.GFX.I2C.Transfer_Data;
201 Success : out Boolean)
202 is
Nico Huberc1d20302019-04-28 00:00:37 +0200203 GMBUS2 : Word32 := 0;
Nico Huber83693c82016-10-08 22:17:55 +0200204 GMBUS3 : Word32;
205
206 Current : HW.GFX.I2C.Transfer_Length;
207 Transfered : HW.GFX.I2C.Transfer_Length := 0;
208 begin
209 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
210
211 Data := (others => 0);
212
213 Init_GMBUS (Port, Success);
214 if Success then
215 Registers.Write
Arthur Heymans229ed1c2018-03-28 16:45:43 +0200216 (Register => GMBUS_Regs (1),
Nico Huber83693c82016-10-08 22:17:55 +0200217 Value => GMBUS1_SOFTWARE_READY or
218 GMBUS1_BUS_CYCLE_INDEX or
219 GMBUS1_BUS_CYCLE_WAIT or
220 GMBUS1_TOTAL_BYTE_COUNT (Length) or
221 GMBUS1_SLAVE_ADDRESS (Address) or
222 GMBUS1_DIRECTION_READ);
223
224 while Success and then Transfered < Length loop
225 Registers.Wait_Set_Mask
Arthur Heymans229ed1c2018-03-28 16:45:43 +0200226 (Register => GMBUS_Regs (2),
Nico Huber83693c82016-10-08 22:17:55 +0200227 Mask => GMBUS2_HARDWARE_READY,
228 TOut_MS => 55);
229
Arthur Heymans229ed1c2018-03-28 16:45:43 +0200230 Registers.Read (GMBUS_Regs (2), GMBUS2);
Nico Huber83693c82016-10-08 22:17:55 +0200231 Success := (GMBUS2 and GMBUS2_HARDWARE_READY) /= 0 and
232 (GMBUS2 and GMBUS2_NAK_INDICATOR) = 0;
233 if Success then
234 Current := GFX.I2C.Transfer_Length'Min (Length, Transfered + 4);
235
Arthur Heymans229ed1c2018-03-28 16:45:43 +0200236 Registers.Read (GMBUS_Regs (3), GMBUS3);
Nico Huber83693c82016-10-08 22:17:55 +0200237 for I in Transfered .. Current - 1 loop
238 Data (I) := Byte (GMBUS3 and 16#ff#);
239 GMBUS3 := Shift_Right (GMBUS3, 8);
240 end loop;
241 Transfered := Current;
242 end if;
243 end loop;
244 if Success then
245 Registers.Wait_Set_Mask
Arthur Heymans229ed1c2018-03-28 16:45:43 +0200246 (Register => GMBUS_Regs (2),
Nico Huber83693c82016-10-08 22:17:55 +0200247 Mask => GMBUS2_HARDWARE_WAIT_PHASE);
248 Registers.Write
Arthur Heymans229ed1c2018-03-28 16:45:43 +0200249 (Register => GMBUS_Regs (1),
Nico Huber83693c82016-10-08 22:17:55 +0200250 Value => GMBUS1_SOFTWARE_READY or GMBUS1_BUS_CYCLE_STOP);
251 Registers.Wait_Unset_Mask
Arthur Heymans229ed1c2018-03-28 16:45:43 +0200252 (Register => GMBUS_Regs (2),
Nico Huber83693c82016-10-08 22:17:55 +0200253 Mask => GMBUS2_GMBUS_ACTIVE);
Nico Huberc1d20302019-04-28 00:00:37 +0200254 elsif (GMBUS2 and GMBUS2_NAK_INDICATOR) /= 0 then
255 Registers.Wait_Unset_Mask
256 (Register => GMBUS_Regs (2),
257 Mask => GMBUS2_GMBUS_ACTIVE);
258 Registers.Write (GMBUS_Regs (1), GMBUS1_SOFTWARE_CLEAR_INTERRUPT);
259 Registers.Write (GMBUS_Regs (1), 0);
Nico Huber83693c82016-10-08 22:17:55 +0200260 end if;
261 end if;
262 Length := Transfered;
263
264 Release_GMBUS;
265 end I2C_Read;
266
267end HW.GFX.GMA.I2C;