blob: 521ef8b3a62ea2297e7b20ccd8369fb5c58b32b7 [file] [log] [blame]
Nico Huber83693c82016-10-08 22:17:55 +02001--
2-- Copyright (C) 2014-2016 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; version 2 of the License.
7--
8-- This program is distributed in the hope that it will be useful,
9-- but WITHOUT ANY WARRANTY; without even the implied warranty of
10-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11-- GNU General Public License for more details.
12--
13
14with GNAT.Source_Info;
15
16with HW.Time;
17with HW.Debug;
18with HW.GFX.GMA.Config;
19with HW.GFX.GMA.Registers;
20with HW.GFX.GMA.Power_And_Clocks_Haswell;
21
22use type HW.Word64;
23
24package body HW.GFX.GMA.Power_And_Clocks_Skylake is
25
26 type Power_Domain is (MISC_IO, PW1, PW2, DDI_AE, DDI_B, DDI_C, DDI_D);
27 subtype Power_Well is Power_Domain range PW1 .. PW2;
28 subtype Dynamic_Domain is Power_Domain range PW2 .. DDI_D;
29
30 NDE_RSTWRN_OPT_RST_PCH_Handshake_En : constant := 1 * 2 ** 4;
31
32 FUSE_STATUS_DOWNLOAD_STATUS : constant := 1 * 2 ** 31;
33 FUSE_STATUS_PG0_DIST_STATUS : constant := 1 * 2 ** 27;
34
35 type Power_Domain_Values is array (Power_Domain) of Word32;
36 PWR_WELL_CTL_POWER_REQUEST : constant Power_Domain_Values :=
37 (MISC_IO => 1 * 2 ** 1,
38 DDI_AE => 1 * 2 ** 3,
39 DDI_B => 1 * 2 ** 5,
40 DDI_C => 1 * 2 ** 7,
41 DDI_D => 1 * 2 ** 9,
42 PW1 => 1 * 2 ** 29,
43 PW2 => 1 * 2 ** 31);
44 PWR_WELL_CTL_POWER_STATE : constant Power_Domain_Values :=
45 (MISC_IO => 1 * 2 ** 0,
46 DDI_AE => 1 * 2 ** 2,
47 DDI_B => 1 * 2 ** 4,
48 DDI_C => 1 * 2 ** 6,
49 DDI_D => 1 * 2 ** 8,
50 PW1 => 1 * 2 ** 28,
51 PW2 => 1 * 2 ** 30);
52
53 type Power_Well_Values is array (Power_Well) of Word32;
54 FUSE_STATUS_PGx_DIST_STATUS : constant Power_Well_Values :=
55 (PW1 => 1 * 2 ** 26,
56 PW2 => 1 * 2 ** 25);
57
58 DBUF_CTL_DBUF_POWER_REQUEST : constant := 1 * 2 ** 31;
59 DBUF_CTL_DBUF_POWER_STATE : constant := 1 * 2 ** 30;
60
61 ----------------------------------------------------------------------------
62
63 DPLL_CTRL1_DPLL0_LINK_RATE_MASK : constant := 7 * 2 ** 1;
64 DPLL_CTRL1_DPLL0_LINK_RATE_2700MHZ : constant := 0 * 2 ** 1;
65 DPLL_CTRL1_DPLL0_LINK_RATE_1350MHZ : constant := 1 * 2 ** 1;
66 DPLL_CTRL1_DPLL0_LINK_RATE_810MHZ : constant := 2 * 2 ** 1;
67 DPLL_CTRL1_DPLL0_LINK_RATE_1620MHZ : constant := 3 * 2 ** 1;
68 DPLL_CTRL1_DPLL0_LINK_RATE_1080MHZ : constant := 4 * 2 ** 1;
69 DPLL_CTRL1_DPLL0_LINK_RATE_2160MHZ : constant := 5 * 2 ** 1;
70 DPLL_CTRL1_DPLL0_OVERRIDE : constant := 1 * 2 ** 0;
71
72 LCPLL1_CTL_PLL_ENABLE : constant := 1 * 2 ** 31;
73 LCPLL1_CTL_PLL_LOCK : constant := 1 * 2 ** 30;
74
75 ----------------------------------------------------------------------------
76
77 CDCLK_CTL_CD_FREQ_SELECT_MASK : constant := 3 * 2 ** 26;
78 CDCLK_CTL_CD_FREQ_SELECT_450MHZ : constant := 0 * 2 ** 26;
79 CDCLK_CTL_CD_FREQ_SELECT_540MHZ : constant := 1 * 2 ** 26;
80 CDCLK_CTL_CD_FREQ_SELECT_337_5MHZ : constant := 2 * 2 ** 26;
81 CDCLK_CTL_CD_FREQ_SELECT_675MHZ : constant := 3 * 2 ** 26;
82 CDCLK_CTL_CD_FREQ_DECIMAL_MASK : constant := 16#7ff#;
83
84 SKL_PCODE_CDCLK_CONTROL : constant := 7;
85 SKL_CDCLK_PREPARE_FOR_CHANGE : constant := 3;
86 SKL_CDCLK_READY_FOR_CHANGE : constant := 1;
87
88 GT_MAILBOX_READY : constant := 1 * 2 ** 31;
89
90 function CDCLK_CTL_CD_FREQ_DECIMAL
91 (Freq : Positive;
92 Plus_Half : Boolean)
93 return Word32 is
94 begin
95 return Word32 (2 * (Freq - 1)) or (if Plus_Half then 1 else 0);
96 end CDCLK_CTL_CD_FREQ_DECIMAL;
97
98 ----------------------------------------------------------------------------
99
100 procedure GT_Mailbox_Write (MBox : Word32; Value : Word64) is
101 begin
102 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
103
104 Registers.Wait_Unset_Mask (Registers.GT_MAILBOX, GT_MAILBOX_READY);
105 Registers.Write
106 (Registers.GT_MAILBOX_DATA, Word32 (Value and 16#ffff_ffff#));
107 Registers.Write
108 (Registers.GT_MAILBOX_DATA_1, Word32 (Shift_Right (Value, 32)));
109 Registers.Write (Registers.GT_MAILBOX, GT_MAILBOX_READY or MBox);
110
111 Registers.Wait_Unset_Mask (Registers.GT_MAILBOX, GT_MAILBOX_READY);
112 end GT_Mailbox_Write;
113
114 ----------------------------------------------------------------------------
115
116 procedure PD_Off (PD : Power_Domain)
117 is
118 Ctl1, Ctl2, Ctl3, Ctl4 : Word32;
119 begin
120 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
121
122 Registers.Read (Registers.PWR_WELL_CTL_BIOS, Ctl1);
123 Registers.Read (Registers.PWR_WELL_CTL_DRIVER, Ctl2);
124 Registers.Read (Registers.PWR_WELL_CTL_KVMR, Ctl3);
125 Registers.Read (Registers.PWR_WELL_CTL_DEBUG, Ctl4);
126 pragma Debug (Registers.Posting_Read (Registers.PWR_WELL_CTL5)); -- Result for debugging only
127 pragma Debug (Registers.Posting_Read (Registers.PWR_WELL_CTL6)); -- Result for debugging only
128
129 if ((Ctl1 or Ctl2 or Ctl3 or Ctl4) and
130 PWR_WELL_CTL_POWER_REQUEST (PD)) /= 0
131 then
132 Registers.Wait_Set_Mask
133 (Register => Registers.PWR_WELL_CTL_DRIVER,
134 Mask => PWR_WELL_CTL_POWER_STATE (PD));
135 end if;
136
137 if (Ctl1 and PWR_WELL_CTL_POWER_REQUEST (PD)) /= 0 then
138 Registers.Unset_Mask
139 (Register => Registers.PWR_WELL_CTL_BIOS,
140 Mask => PWR_WELL_CTL_POWER_REQUEST (PD));
141 end if;
142
143 if (Ctl2 and PWR_WELL_CTL_POWER_REQUEST (PD)) /= 0 then
144 Registers.Unset_Mask
145 (Register => Registers.PWR_WELL_CTL_DRIVER,
146 Mask => PWR_WELL_CTL_POWER_REQUEST (PD));
147 end if;
148 end PD_Off;
149
150 procedure PD_On (PD : Power_Domain)
151 with
152 Pre => True
153 is
154 Ctl1, Ctl2, Ctl3, Ctl4 : Word32;
155 begin
156 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
157
158 Registers.Read (Registers.PWR_WELL_CTL_BIOS, Ctl1);
159 Registers.Read (Registers.PWR_WELL_CTL_DRIVER, Ctl2);
160 Registers.Read (Registers.PWR_WELL_CTL_KVMR, Ctl3);
161 Registers.Read (Registers.PWR_WELL_CTL_DEBUG, Ctl4);
162 pragma Debug (Registers.Posting_Read (Registers.PWR_WELL_CTL5)); -- Result for debugging only
163 pragma Debug (Registers.Posting_Read (Registers.PWR_WELL_CTL6)); -- Result for debugging only
164
165 if ((Ctl1 or Ctl2 or Ctl3 or Ctl4) and
166 PWR_WELL_CTL_POWER_REQUEST (PD)) = 0
167 then
168 Registers.Wait_Unset_Mask
169 (Register => Registers.PWR_WELL_CTL_DRIVER,
170 Mask => PWR_WELL_CTL_POWER_STATE (PD));
171 end if;
172
173 if (Ctl2 and PWR_WELL_CTL_POWER_REQUEST (PD)) = 0 then
174 Registers.Set_Mask
175 (Register => Registers.PWR_WELL_CTL_DRIVER,
176 Mask => PWR_WELL_CTL_POWER_REQUEST (PD));
177 Registers.Wait_Set_Mask
178 (Register => Registers.PWR_WELL_CTL_DRIVER,
179 Mask => PWR_WELL_CTL_POWER_STATE (PD));
180
181 if PD in Power_Well then
182 Registers.Wait_Set_Mask
183 (Register => Registers.FUSE_STATUS,
184 Mask => FUSE_STATUS_PGx_DIST_STATUS (PD));
185 end if;
186 end if;
187 end PD_On;
188
189 function Need_PD (PD : Dynamic_Domain; Configs : Configs_Type) return Boolean
190 is
191 begin
192 return (case PD is
193 when DDI_AE => Configs (Primary).Port = Internal or
194 Configs (Secondary).Port = Internal or
195 Configs (Tertiary).Port = Internal,
196 when DDI_B => Configs (Primary).Port = Digital1 or
197 Configs (Primary).Port = DP1 or
198 Configs (Secondary).Port = Digital1 or
199 Configs (Secondary).Port = DP1 or
200 Configs (Tertiary).Port = Digital1 or
201 Configs (Tertiary).Port = DP1,
202 when DDI_C => Configs (Primary).Port = Digital2 or
203 Configs (Primary).Port = DP2 or
204 Configs (Secondary).Port = Digital2 or
205 Configs (Secondary).Port = DP2 or
206 Configs (Tertiary).Port = Digital2 or
207 Configs (Tertiary).Port = DP2,
208 when DDI_D => Configs (Primary).Port = Digital3 or
209 Configs (Primary).Port = DP3 or
210 Configs (Secondary).Port = Digital3 or
211 Configs (Secondary).Port = DP3 or
212 Configs (Tertiary).Port = Digital3 or
213 Configs (Tertiary).Port = DP3,
214 when PW2 => (Configs (Primary).Port /= Disabled and
215 Configs (Primary).Port /= Internal) or
216 Configs (Secondary).Port /= Disabled or
217 Configs (Tertiary).Port /= Disabled);
218 end Need_PD;
219
220 ----------------------------------------------------------------------------
221
222 procedure Pre_All_Off is
223 begin
224 Power_And_Clocks_Haswell.PSR_Off;
225 end Pre_All_Off;
226
227 procedure Post_All_Off is
228 begin
229 for PD in reverse Dynamic_Domain loop
230 PD_Off (PD);
231 end loop;
232
233 Registers.Unset_Mask
234 (Register => Registers.DBUF_CTL,
235 Mask => DBUF_CTL_DBUF_POWER_REQUEST);
236 Registers.Wait_Unset_Mask
237 (Register => Registers.DBUF_CTL,
238 Mask => DBUF_CTL_DBUF_POWER_STATE);
239
240 Registers.Unset_Mask
241 (Register => Registers.LCPLL1_CTL,
242 Mask => LCPLL1_CTL_PLL_ENABLE);
243 Registers.Wait_Unset_Mask
244 (Register => Registers.LCPLL1_CTL,
245 Mask => LCPLL1_CTL_PLL_LOCK);
246
247 PD_Off (MISC_IO);
248 PD_Off (PW1);
249 end Post_All_Off;
250
251 procedure Initialize
252 is
253 CDClk_Change_Timeout : Time.T;
254 Timed_Out : Boolean;
255
256 MBox_Data0 : Word32;
257 begin
258 Registers.Set_Mask
259 (Register => Registers.NDE_RSTWRN_OPT,
260 Mask => NDE_RSTWRN_OPT_RST_PCH_Handshake_En);
261
262 Registers.Wait_Set_Mask
263 (Register => Registers.FUSE_STATUS,
264 Mask => FUSE_STATUS_PG0_DIST_STATUS);
265 PD_On (PW1);
266 PD_On (MISC_IO);
267
268 Registers.Write
269 (Register => Registers.CDCLK_CTL,
270 Value => CDCLK_CTL_CD_FREQ_SELECT_337_5MHZ or
271 CDCLK_CTL_CD_FREQ_DECIMAL (337, True));
272 -- TODO: Set to preferred eDP rate:
273 -- Registers.Unset_And_Set_Mask
274 -- (Register => Registers.DPLL_CTRL1,
275 -- Unset_Mask => DPLL_CTRL1_DPLL0_LINK_RATE_MASK,
276 -- Set_Mask => DPLL_CTRL1_DPLL0_LINK_RATE_...);
277 Registers.Set_Mask
278 (Register => Registers.LCPLL1_CTL,
279 Mask => LCPLL1_CTL_PLL_ENABLE);
280 Registers.Wait_Set_Mask
281 (Register => Registers.LCPLL1_CTL,
282 Mask => LCPLL1_CTL_PLL_LOCK);
283
284 CDClk_Change_Timeout := Time.MS_From_Now (3);
285 loop
286 GT_Mailbox_Write
287 (MBox => SKL_PCODE_CDCLK_CONTROL,
288 Value => SKL_CDCLK_PREPARE_FOR_CHANGE);
289 Timed_Out := Time.Timed_Out (CDClk_Change_Timeout);
290 Registers.Read (Registers.GT_MAILBOX_DATA, MBox_Data0);
291 if (MBox_Data0 and SKL_CDCLK_READY_FOR_CHANGE) =
292 SKL_CDCLK_READY_FOR_CHANGE
293 then
294 Timed_Out := False;
295 exit;
296 end if;
297 exit when Timed_Out;
298 end loop;
299
300 if not Timed_Out then
301 GT_Mailbox_Write
302 (MBox => SKL_PCODE_CDCLK_CONTROL,
303 Value => 16#0000_0000#); -- 0 - 337.5MHz
304 -- 1 - 450.0MHz
305 -- 2 - 540.0MHz
306 -- 3 - 675.0MHz
307 Registers.Set_Mask
308 (Register => Registers.DBUF_CTL,
309 Mask => DBUF_CTL_DBUF_POWER_REQUEST);
310 Registers.Wait_Set_Mask
311 (Register => Registers.DBUF_CTL,
312 Mask => DBUF_CTL_DBUF_POWER_STATE);
313 end if;
314 end Initialize;
315
316 procedure Power_Set_To (Configs : Configs_Type) is
317 begin
318 for PD in reverse Dynamic_Domain loop
319 if not Need_PD (PD, Configs) then
320 PD_Off (PD);
321 end if;
322 end loop;
323 for PD in Dynamic_Domain loop
324 if Need_PD (PD, Configs) then
325 PD_On (PD);
326 end if;
327 end loop;
328 end Power_Set_To;
329
330 procedure Power_Up (Old_Configs, New_Configs : Configs_Type) is
331 begin
332 for PD in Dynamic_Domain loop
333 if not Need_PD (PD, Old_Configs) and Need_PD (PD, New_Configs) then
334 PD_On (PD);
335 end if;
336 end loop;
337 end Power_Up;
338
339 procedure Power_Down (Old_Configs, Tmp_Configs, New_Configs : Configs_Type)
340 is
341 begin
342 for PD in reverse Dynamic_Domain loop
343 if (Need_PD (PD, Old_Configs) or Need_PD (PD, Tmp_Configs)) and
344 not Need_PD (PD, New_Configs)
345 then
346 PD_Off (PD);
347 end if;
348 end loop;
349 end Power_Down;
350
351end HW.GFX.GMA.Power_And_Clocks_Skylake;