blob: d8cdf44920fe023f01d7ff7ceccdb6e7a14ce8dc [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
Arthur Heymans229ed1c2018-03-28 16:45:43 +020074 GMBUS_Regs : constant array (0 .. 5) of Registers.Registers_Index :=
75 (if Config.Has_PCH_GMBUS then
76 (0 => Registers.PCH_GMBUS0,
77 1 => Registers.PCH_GMBUS1,
78 2 => Registers.PCH_GMBUS2,
79 3 => Registers.PCH_GMBUS3,
80 4 => Registers.PCH_GMBUS4,
81 5 => Registers.PCH_GMBUS5)
82 else
83 (0 => Registers.GMCH_GMBUS0,
84 1 => Registers.GMCH_GMBUS1,
85 2 => Registers.GMCH_GMBUS2,
86 3 => Registers.GMCH_GMBUS3,
87 4 => Registers.GMCH_GMBUS4,
88 5 => Registers.GMCH_GMBUS5));
89
Nico Huber83693c82016-10-08 22:17:55 +020090 function GMBUS1_TOTAL_BYTE_COUNT
91 (Count : HW.GFX.I2C.Transfer_Length)
92 return Word32 is
93 begin
94 return Shift_Left (Word32 (Count), GMBUS1_TOTAL_BYTE_COUNT_SHIFT);
95 end GMBUS1_TOTAL_BYTE_COUNT;
96
97 function GMBUS1_SLAVE_ADDRESS
98 (Address : HW.GFX.I2C.Transfer_Address)
99 return Word32 is
100 begin
101 return Shift_Left (Word32 (Address), GMBUS1_SLAVE_ADDRESS_SHIFT);
102 end GMBUS1_SLAVE_ADDRESS;
103
104 function GMBUS0_PIN_PAIR_SELECT (Port : PCH_Port) return Word32 is
105 begin
106 return
Nico Huber1c3b9282017-02-09 13:57:04 +0100107 (if Config.GMBUS_Alternative_Pins then
108 (case Port is
109 when PCH_HDMI_B => GMBUS0_PIN_PAIR_SELECT_BXT_B,
110 when PCH_HDMI_C => GMBUS0_PIN_PAIR_SELECT_BXT_C,
111 when others => GMBUS0_PIN_PAIR_SELECT_NONE)
112 else
113 (case Port is
114 when PCH_DAC => GMBUS0_PIN_PAIR_SELECT_DAC,
115 when PCH_LVDS => GMBUS0_PIN_PAIR_SELECT_LVDS,
116 when PCH_HDMI_B => GMBUS0_PIN_PAIR_SELECT_DIGI_B,
117 when PCH_HDMI_C => GMBUS0_PIN_PAIR_SELECT_DIGI_C,
118 when PCH_HDMI_D => GMBUS0_PIN_PAIR_SELECT_DIGI_D,
119 when others => GMBUS0_PIN_PAIR_SELECT_NONE));
Nico Huber83693c82016-10-08 22:17:55 +0200120 end GMBUS0_PIN_PAIR_SELECT;
121
122 ----------------------------------------------------------------------------
123
124 procedure GMBUS_Ready (Result : out Boolean)
125 is
126 GMBUS2 : Word32;
127 begin
Arthur Heymans229ed1c2018-03-28 16:45:43 +0200128 Registers.Read (GMBUS_Regs (2), GMBUS2);
Nico Huber83693c82016-10-08 22:17:55 +0200129 Result := (GMBUS2 and (GMBUS2_HARDWARE_WAIT_PHASE or
130 GMBUS2_SLAVE_STALL_TIMEOUT_ERROR or
131 GMBUS2_GMBUS_INTERRUPT_STATUS or
132 GMBUS2_NAK_INDICATOR)) = 0;
133 end GMBUS_Ready;
134
135 procedure Reset_GMBUS (Success : out Boolean) is
136 begin
137 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
138
Arthur Heymans229ed1c2018-03-28 16:45:43 +0200139 Registers.Write (GMBUS_Regs (1), GMBUS1_SOFTWARE_CLEAR_INTERRUPT);
140 Registers.Write (GMBUS_Regs (1), 0);
141 Registers.Write (GMBUS_Regs (0), GMBUS0_PIN_PAIR_SELECT_NONE);
Nico Huber83693c82016-10-08 22:17:55 +0200142
143 GMBUS_Ready (Success);
144 end Reset_GMBUS;
145
146 procedure Init_GMBUS (Port : PCH_Port; Success : out Boolean) is
147 begin
148 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
149
150 if Config.Ungate_GMBUS_Unit_Level then
151 Registers.Set_Mask
152 (Register => Registers.PCH_DSPCLK_GATE_D,
153 Mask => PCH_DSPCLK_GATE_D_GMBUS_UNIT_LVL);
154 end if;
155
156 -- TODO: Refactor + check for timeout.
Arthur Heymans229ed1c2018-03-28 16:45:43 +0200157 Registers.Wait_Unset_Mask (GMBUS_Regs (2), GMBUS2_INUSE);
Nico Huber83693c82016-10-08 22:17:55 +0200158
159 GMBUS_Ready (Success);
160 if not Success then
161 Reset_GMBUS (Success);
162 end if;
163
164 if Success then
165 Registers.Write
Arthur Heymans229ed1c2018-03-28 16:45:43 +0200166 (Register => GMBUS_Regs (0),
Nico Huber83693c82016-10-08 22:17:55 +0200167 Value => GMBUS0_GMBUS_RATE_SELECT_100KHZ or
168 GMBUS0_PIN_PAIR_SELECT (Port));
169 Registers.Write
Arthur Heymans229ed1c2018-03-28 16:45:43 +0200170 (Register => GMBUS_Regs (4),
Nico Huber83693c82016-10-08 22:17:55 +0200171 Value => 0);
172 Registers.Write
Arthur Heymans229ed1c2018-03-28 16:45:43 +0200173 (Register => GMBUS_Regs (5),
Nico Huber83693c82016-10-08 22:17:55 +0200174 Value => 0);
175 end if;
176 end Init_GMBUS;
177
178 procedure Release_GMBUS
179 is
180 begin
181 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
182
Arthur Heymans229ed1c2018-03-28 16:45:43 +0200183 Registers.Write (GMBUS_Regs (0), GMBUS0_PIN_PAIR_SELECT_NONE);
Nico Huber83693c82016-10-08 22:17:55 +0200184
185 -- Clear INUSE. TODO: Don't do it, if timeout occured (see above).
Arthur Heymans229ed1c2018-03-28 16:45:43 +0200186 Registers.Write (GMBUS_Regs (2), GMBUS2_INUSE);
Nico Huber83693c82016-10-08 22:17:55 +0200187
188 if Config.Ungate_GMBUS_Unit_Level then
189 Registers.Unset_Mask
190 (Register => Registers.PCH_DSPCLK_GATE_D,
191 Mask => PCH_DSPCLK_GATE_D_GMBUS_UNIT_LVL);
192 end if;
193 end Release_GMBUS;
194
195 procedure I2C_Read
196 (Port : in PCH_Port;
197 Address : in HW.GFX.I2C.Transfer_Address;
198 Length : in out HW.GFX.I2C.Transfer_Length;
199 Data : out HW.GFX.I2C.Transfer_Data;
200 Success : out Boolean)
201 is
202 GMBUS2,
203 GMBUS3 : Word32;
204
205 Current : HW.GFX.I2C.Transfer_Length;
206 Transfered : HW.GFX.I2C.Transfer_Length := 0;
207 begin
208 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
209
210 Data := (others => 0);
211
212 Init_GMBUS (Port, Success);
213 if Success then
214 Registers.Write
Arthur Heymans229ed1c2018-03-28 16:45:43 +0200215 (Register => GMBUS_Regs (1),
Nico Huber83693c82016-10-08 22:17:55 +0200216 Value => GMBUS1_SOFTWARE_READY or
217 GMBUS1_BUS_CYCLE_INDEX or
218 GMBUS1_BUS_CYCLE_WAIT or
219 GMBUS1_TOTAL_BYTE_COUNT (Length) or
220 GMBUS1_SLAVE_ADDRESS (Address) or
221 GMBUS1_DIRECTION_READ);
222
223 while Success and then Transfered < Length loop
224 Registers.Wait_Set_Mask
Arthur Heymans229ed1c2018-03-28 16:45:43 +0200225 (Register => GMBUS_Regs (2),
Nico Huber83693c82016-10-08 22:17:55 +0200226 Mask => GMBUS2_HARDWARE_READY,
227 TOut_MS => 55);
228
Arthur Heymans229ed1c2018-03-28 16:45:43 +0200229 Registers.Read (GMBUS_Regs (2), GMBUS2);
Nico Huber83693c82016-10-08 22:17:55 +0200230 Success := (GMBUS2 and GMBUS2_HARDWARE_READY) /= 0 and
231 (GMBUS2 and GMBUS2_NAK_INDICATOR) = 0;
232 if Success then
233 Current := GFX.I2C.Transfer_Length'Min (Length, Transfered + 4);
234
Arthur Heymans229ed1c2018-03-28 16:45:43 +0200235 Registers.Read (GMBUS_Regs (3), GMBUS3);
Nico Huber83693c82016-10-08 22:17:55 +0200236 for I in Transfered .. Current - 1 loop
237 Data (I) := Byte (GMBUS3 and 16#ff#);
238 GMBUS3 := Shift_Right (GMBUS3, 8);
239 end loop;
240 Transfered := Current;
241 end if;
242 end loop;
243 if Success then
244 Registers.Wait_Set_Mask
Arthur Heymans229ed1c2018-03-28 16:45:43 +0200245 (Register => GMBUS_Regs (2),
Nico Huber83693c82016-10-08 22:17:55 +0200246 Mask => GMBUS2_HARDWARE_WAIT_PHASE);
247 Registers.Write
Arthur Heymans229ed1c2018-03-28 16:45:43 +0200248 (Register => GMBUS_Regs (1),
Nico Huber83693c82016-10-08 22:17:55 +0200249 Value => GMBUS1_SOFTWARE_READY or GMBUS1_BUS_CYCLE_STOP);
250 Registers.Wait_Unset_Mask
Arthur Heymans229ed1c2018-03-28 16:45:43 +0200251 (Register => GMBUS_Regs (2),
Nico Huber83693c82016-10-08 22:17:55 +0200252 Mask => GMBUS2_GMBUS_ACTIVE);
253 end if;
254 end if;
255 Length := Transfered;
256
257 Release_GMBUS;
258 end I2C_Read;
259
260end HW.GFX.GMA.I2C;