blob: 83c7c28024d305f33ffa2baab3595d909a065d5c [file] [log] [blame]
Nico Huberf6266002017-02-03 12:17:28 +01001--
2-- Copyright (C) 2017 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
6-- the Free Software Foundation; either version 2 of the License, or
7-- (at your option) any later version.
8--
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 GNAT.Source_Info;
16with HW.Debug;
17
18with HW.GFX.GMA.Registers;
19
20use HW.GFX.GMA.Registers;
21
22package body HW.GFX.GMA.DDI_Phy is
23
24 subtype Dual_Channel is T range BC .. BC;
25
26 type DDI_Range is record
27 First : DDI_Phy_Port;
28 Last : DDI_Phy_Port;
29 end record;
30 type DDI_Range_Array is array (T) of DDI_Range;
31 DDIs : constant DDI_Range_Array :=
32 (A => (DIGI_A, DIGI_A),
33 BC => (DIGI_B, DIGI_C));
34
35 ----------------------------------------------------------------------------
36
37 type CL1CM is record
38 DW0 : Registers_Index;
39 DW9 : Registers_Index;
40 DW10 : Registers_Index;
41 DW28 : Registers_Index;
42 DW30 : Registers_Index;
43 end record;
44 type CL1CM_Array is array (T) of CL1CM;
45 PORT_CL1CM : constant CL1CM_Array :=
46 (A =>
47 (DW0 => BXT_PORT_CL1CM_DW0_A,
48 DW9 => BXT_PORT_CL1CM_DW9_A,
49 DW10 => BXT_PORT_CL1CM_DW10_A,
50 DW28 => BXT_PORT_CL1CM_DW28_A,
51 DW30 => BXT_PORT_CL1CM_DW30_A),
52 BC =>
53 (DW0 => BXT_PORT_CL1CM_DW0_BC,
54 DW9 => BXT_PORT_CL1CM_DW9_BC,
55 DW10 => BXT_PORT_CL1CM_DW10_BC,
56 DW28 => BXT_PORT_CL1CM_DW28_BC,
57 DW30 => BXT_PORT_CL1CM_DW30_BC));
58
59 type CL2CM is record
60 DW6 : Registers_Index;
61 end record;
62 type CL2CM_Array is array (Dual_Channel) of CL2CM;
63 PORT_CL2CM : constant CL2CM_Array :=
64 (BC => (DW6 => BXT_PORT_CL2CM_DW6_BC));
65
66 type Port_Ref_Regs is record
67 DW3 : Registers_Index;
68 DW6 : Registers_Index;
69 DW8 : Registers_Index;
70 end record;
71 type Port_Ref_Array is array (T) of Port_Ref_Regs;
72 PORT_REF : constant Port_Ref_Array :=
73 (A =>
74 (DW3 => BXT_PORT_REF_DW3_A,
75 DW6 => BXT_PORT_REF_DW6_A,
76 DW8 => BXT_PORT_REF_DW8_A),
77 BC =>
78 (DW3 => BXT_PORT_REF_DW3_BC,
79 DW6 => BXT_PORT_REF_DW6_BC,
80 DW8 => BXT_PORT_REF_DW8_BC));
81
82 type Regs is array (T) of Registers_Index;
83 PHY_CTL_FAMILY : constant Regs :=
84 (A => BXT_PHY_CTL_FAM_EDP, BC => BXT_PHY_CTL_FAM_DDI);
85
86 type DDI_Regs is array (DDI_Phy_Port) of Registers_Index;
87 PHY_CTL : constant DDI_Regs :=
88 (DIGI_A => BXT_PHY_CTL_A,
89 DIGI_B => BXT_PHY_CTL_B,
90 DIGI_C => BXT_PHY_CTL_C);
91
92 ----------------------------------------------------------------------------
93
94 type Values is array (T) of Word32;
95 GT_DISPLAY_POWER_ON : constant Values :=
96 (A => 1 * 2 ** 1,
97 BC => 1 * 2 ** 0);
98
99 PORT_CL1CM_PHY_POWER_GOOD : constant := 1 * 2 ** 16;
100 PORT_CL1CM_PHY_RESERVED : constant := 1 * 2 ** 7;
101
102 PORT_CL1CM_IREFxRC_OFFSET_SHIFT : constant := 8;
103 PORT_CL1CM_IREFxRC_OFFSET_MASK : constant := 16#ff# * 2 ** 8;
104
105 PORT_CL1CM_OCL1_POWER_DOWN_EN : constant := 1 * 2 ** 23;
106 PORT_CL1CM_OLDO_DYN_POWER_DOWN_EN : constant := 1 * 2 ** 22;
107 PORT_CL1CM_SUS_CLK_CONFIG : constant := 3 * 2 ** 0;
108
109 PORT_CL2CM_OLDO_DYN_POWER_DOWN_EN : constant := 1 * 2 ** 28;
110
111 PORT_REF_GRC_DONE : constant := 1 * 2 ** 22;
112
113 PORT_REF_GRC_CODE_SHIFT : constant := 24;
114 PORT_REF_GRC_FAST_SHIFT : constant := 16;
115 PORT_REF_GRC_SLOW_SHIFT : constant := 8;
116
117 PORT_REF_GRC_DISABLE : constant := 1 * 2 ** 15;
118 PORT_REF_GRC_READY_OVERRIDE : constant := 1 * 2 ** 1;
119
120 PHY_CTL_FAM_CMN_RESET_DIS : constant := 1 * 2 ** 31;
121
122 PHY_CTL_CMNLANE_POWERDOWN_ACK : constant := 1 * 2 ** 10;
123
124 ----------------------------------------------------------------------------
125
126 procedure Is_Enabled (Phy : in T; Enabled : out Boolean)
127 is
128 Phy_Pwr : Word32;
129 begin
130 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
131
132 Is_Set_Mask (BXT_P_CR_GT_DISP_PWRON, GT_DISPLAY_POWER_ON (Phy), Enabled);
133
134 if Enabled then
135 Read (PORT_CL1CM (Phy).DW0, Phy_Pwr);
136 Enabled :=
137 (Phy_Pwr and (PORT_CL1CM_PHY_POWER_GOOD or PORT_CL1CM_PHY_RESERVED))
138 = PORT_CL1CM_PHY_POWER_GOOD;
139 pragma Debug (not Enabled, Debug.Put_Line ("DDI PHY power unsettled"));
140 end if;
141
142 if Enabled then
143 Is_Set_Mask (PHY_CTL_FAMILY (Phy), PHY_CTL_FAM_CMN_RESET_DIS, Enabled);
144 pragma Debug (not Enabled, Debug.Put_Line ("DDI PHY still in reset"));
145 end if;
146
147 for DDI in DDIs (Phy).First .. DDIs (Phy).Last loop
148 if Enabled then
149 declare
150 Common_Lane_Powerdown : Boolean;
151 begin
152 Is_Set_Mask
153 (Register => PHY_CTL (DDI),
154 Mask => PHY_CTL_CMNLANE_POWERDOWN_ACK,
155 Result => Common_Lane_Powerdown);
156 Enabled := not Common_Lane_Powerdown;
157 pragma Debug
158 (not Enabled, Debug.Put_Line ("Common lane powered down"));
159 end;
160 end if;
161 end loop;
162 end Is_Enabled;
163
164 procedure Power_On_Phy (Phy : T)
165 with
166 Pre => True
167 is
168 begin
169 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
170
171 Set_Mask (BXT_P_CR_GT_DISP_PWRON, GT_DISPLAY_POWER_ON (Phy));
172
173 Wait
174 (Register => PORT_CL1CM (Phy).DW0,
175 Mask => PORT_CL1CM_PHY_POWER_GOOD or
176 PORT_CL1CM_PHY_RESERVED,
177 Value => PORT_CL1CM_PHY_POWER_GOOD,
178 TOut_MS => 1); -- 50~100us
179
180 Unset_And_Set_Mask
181 (Register => PORT_CL1CM (Phy).DW9,
182 Mask_Unset => PORT_CL1CM_IREFxRC_OFFSET_MASK,
183 Mask_Set => Shift_Left
184 (16#e4#, PORT_CL1CM_IREFxRC_OFFSET_SHIFT));
185 Unset_And_Set_Mask
186 (Register => PORT_CL1CM (Phy).DW10,
187 Mask_Unset => PORT_CL1CM_IREFxRC_OFFSET_MASK,
188 Mask_Set => Shift_Left
189 (16#e4#, PORT_CL1CM_IREFxRC_OFFSET_SHIFT));
190
191 Set_Mask
192 (Register => PORT_CL1CM (Phy).DW28,
193 Mask => PORT_CL1CM_OCL1_POWER_DOWN_EN or
194 PORT_CL1CM_OLDO_DYN_POWER_DOWN_EN or
195 PORT_CL1CM_SUS_CLK_CONFIG);
196
197 if Phy in Dual_Channel then
198 Set_Mask (PORT_CL2CM (Phy).DW6, PORT_CL2CM_OLDO_DYN_POWER_DOWN_EN);
199 end if;
200
201 if Phy = BC then
202 declare
203 GRC_Val : Word32;
204 begin
205 -- take RCOMP calibration result from A
206
207 Wait_Set_Mask (PORT_REF (A).DW3, PORT_REF_GRC_DONE, TOut_MS => 10);
208 Read (PORT_REF (A).DW6, GRC_Val);
209 GRC_Val := Shift_Right (GRC_Val, PORT_REF_GRC_CODE_SHIFT);
210
211 -- use it for BC too
212 GRC_Val := Shift_Left (GRC_Val, PORT_REF_GRC_FAST_SHIFT) or
213 Shift_Left (GRC_Val, PORT_REF_GRC_SLOW_SHIFT) or
214 GRC_Val;
215 Write (PORT_REF (Phy).DW6, GRC_Val);
216
217 Set_Mask
218 (Register => PORT_REF (Phy).DW8,
219 Mask => PORT_REF_GRC_DISABLE or
220 PORT_REF_GRC_READY_OVERRIDE);
221 end;
222 end if;
223
224 Set_Mask (PHY_CTL_FAMILY (Phy), PHY_CTL_FAM_CMN_RESET_DIS);
225 end Power_On_Phy;
226
227 procedure Power_On (Phy : T)
228 is
229 Phy_A_Enabled : Boolean := False;
230 Enabled : Boolean;
231 begin
232 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
233
234 Is_Enabled (Phy, Enabled);
235 pragma Debug (Enabled, Debug.Put_Line ("DDI PHY already enabled"));
236
237 if not Enabled then
238 if Phy = BC then
239 -- PHY BC needs RCOMP calibration results from PHY A
240 Is_Enabled (A, Phy_A_Enabled);
241 if not Phy_A_Enabled then
242 Power_On_Phy (A);
243 end if;
244 end if;
245
246 Power_On_Phy (Phy);
247
248 if Phy = BC and then not Phy_A_Enabled then
249 Power_Off (A);
250 end if;
251 end if;
252 end Power_On;
253
254 procedure Power_Off (Phy : T) is
255 begin
256 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
257
258 Unset_Mask (PHY_CTL_FAMILY (Phy), PHY_CTL_FAM_CMN_RESET_DIS);
259 Unset_Mask (BXT_P_CR_GT_DISP_PWRON, GT_DISPLAY_POWER_ON (Phy));
260 end Power_Off;
261
262end HW.GFX.GMA.DDI_Phy;