blob: e8087edb03b8f7622ff55288eb0ffd83f990c23f [file] [log] [blame]
Nico Huber83693c82016-10-08 22:17:55 +02001--
Nico Huber6b4678d2019-09-22 21:31:52 +02002-- Copyright (C) 2014-2016, 2019 secunet Security Networks AG
Nico Huber83693c82016-10-08 22:17:55 +02003--
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 GNAT.Source_Info;
16
17with HW.Time;
18with HW.Debug;
19with HW.GFX.GMA.Config;
20with HW.GFX.GMA.Registers;
Nico Huber312433c2019-09-28 03:15:48 +020021with HW.GFX.GMA.PCode;
Nico Huber83693c82016-10-08 22:17:55 +020022with HW.GFX.GMA.Power_And_Clocks_Haswell;
23
24use type HW.Word64;
25
26package body HW.GFX.GMA.Power_And_Clocks_Skylake is
27
28 type Power_Domain is (MISC_IO, PW1, PW2, DDI_AE, DDI_B, DDI_C, DDI_D);
29 subtype Power_Well is Power_Domain range PW1 .. PW2;
30 subtype Dynamic_Domain is Power_Domain range PW2 .. DDI_D;
31
32 NDE_RSTWRN_OPT_RST_PCH_Handshake_En : constant := 1 * 2 ** 4;
33
34 FUSE_STATUS_DOWNLOAD_STATUS : constant := 1 * 2 ** 31;
35 FUSE_STATUS_PG0_DIST_STATUS : constant := 1 * 2 ** 27;
36
Nico Huber6b4678d2019-09-22 21:31:52 +020037 DFSM_DISPLAY_CDCLK_LIMIT_675MHZ : constant := 0 * 2 ** 23;
38 DFSM_DISPLAY_CDCLK_LIMIT_540MHZ : constant := 1 * 2 ** 23;
39 DFSM_DISPLAY_CDCLK_LIMIT_450MHZ : constant := 2 * 2 ** 23;
40 DFSM_DISPLAY_CDCLK_LIMIT_337_5MHZ : constant := 3 * 2 ** 23;
41 DFSM_DISPLAY_CDCLK_LIMIT_MASK : constant := 3 * 2 ** 23;
42
Nico Huber83693c82016-10-08 22:17:55 +020043 type Power_Domain_Values is array (Power_Domain) of Word32;
44 PWR_WELL_CTL_POWER_REQUEST : constant Power_Domain_Values :=
45 (MISC_IO => 1 * 2 ** 1,
46 DDI_AE => 1 * 2 ** 3,
47 DDI_B => 1 * 2 ** 5,
48 DDI_C => 1 * 2 ** 7,
49 DDI_D => 1 * 2 ** 9,
50 PW1 => 1 * 2 ** 29,
51 PW2 => 1 * 2 ** 31);
52 PWR_WELL_CTL_POWER_STATE : constant Power_Domain_Values :=
53 (MISC_IO => 1 * 2 ** 0,
54 DDI_AE => 1 * 2 ** 2,
55 DDI_B => 1 * 2 ** 4,
56 DDI_C => 1 * 2 ** 6,
57 DDI_D => 1 * 2 ** 8,
58 PW1 => 1 * 2 ** 28,
59 PW2 => 1 * 2 ** 30);
60
61 type Power_Well_Values is array (Power_Well) of Word32;
62 FUSE_STATUS_PGx_DIST_STATUS : constant Power_Well_Values :=
63 (PW1 => 1 * 2 ** 26,
64 PW2 => 1 * 2 ** 25);
65
66 DBUF_CTL_DBUF_POWER_REQUEST : constant := 1 * 2 ** 31;
67 DBUF_CTL_DBUF_POWER_STATE : constant := 1 * 2 ** 30;
68
69 ----------------------------------------------------------------------------
70
71 DPLL_CTRL1_DPLL0_LINK_RATE_MASK : constant := 7 * 2 ** 1;
72 DPLL_CTRL1_DPLL0_LINK_RATE_2700MHZ : constant := 0 * 2 ** 1;
73 DPLL_CTRL1_DPLL0_LINK_RATE_1350MHZ : constant := 1 * 2 ** 1;
74 DPLL_CTRL1_DPLL0_LINK_RATE_810MHZ : constant := 2 * 2 ** 1;
75 DPLL_CTRL1_DPLL0_LINK_RATE_1620MHZ : constant := 3 * 2 ** 1;
76 DPLL_CTRL1_DPLL0_LINK_RATE_1080MHZ : constant := 4 * 2 ** 1;
77 DPLL_CTRL1_DPLL0_LINK_RATE_2160MHZ : constant := 5 * 2 ** 1;
78 DPLL_CTRL1_DPLL0_OVERRIDE : constant := 1 * 2 ** 0;
79
80 LCPLL1_CTL_PLL_ENABLE : constant := 1 * 2 ** 31;
81 LCPLL1_CTL_PLL_LOCK : constant := 1 * 2 ** 30;
82
83 ----------------------------------------------------------------------------
84
85 CDCLK_CTL_CD_FREQ_SELECT_MASK : constant := 3 * 2 ** 26;
86 CDCLK_CTL_CD_FREQ_SELECT_450MHZ : constant := 0 * 2 ** 26;
87 CDCLK_CTL_CD_FREQ_SELECT_540MHZ : constant := 1 * 2 ** 26;
88 CDCLK_CTL_CD_FREQ_SELECT_337_5MHZ : constant := 2 * 2 ** 26;
89 CDCLK_CTL_CD_FREQ_SELECT_675MHZ : constant := 3 * 2 ** 26;
90 CDCLK_CTL_CD_FREQ_DECIMAL_MASK : constant := 16#7ff#;
91
92 SKL_PCODE_CDCLK_CONTROL : constant := 7;
93 SKL_CDCLK_PREPARE_FOR_CHANGE : constant := 3;
94 SKL_CDCLK_READY_FOR_CHANGE : constant := 1;
95
Nico Huber6b4678d2019-09-22 21:31:52 +020096 function CDCLK_CTL_CD_FREQ_DECIMAL (CDClk : Frequency_Type) return Word32 is
Nico Huber83693c82016-10-08 22:17:55 +020097 begin
Nico Huber6b4678d2019-09-22 21:31:52 +020098 -- Weirdest representation: CDClk - 1MHz in 10.1 (10 + 1 fractional bit)
99 return Word32 ((CDClk - 1_000_000) / 500_000);
Nico Huber83693c82016-10-08 22:17:55 +0200100 end CDCLK_CTL_CD_FREQ_DECIMAL;
101
102 ----------------------------------------------------------------------------
103
Nico Huber83693c82016-10-08 22:17:55 +0200104 procedure PD_Off (PD : Power_Domain)
105 is
106 Ctl1, Ctl2, Ctl3, Ctl4 : Word32;
107 begin
108 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
109
110 Registers.Read (Registers.PWR_WELL_CTL_BIOS, Ctl1);
111 Registers.Read (Registers.PWR_WELL_CTL_DRIVER, Ctl2);
112 Registers.Read (Registers.PWR_WELL_CTL_KVMR, Ctl3);
113 Registers.Read (Registers.PWR_WELL_CTL_DEBUG, Ctl4);
114 pragma Debug (Registers.Posting_Read (Registers.PWR_WELL_CTL5)); -- Result for debugging only
115 pragma Debug (Registers.Posting_Read (Registers.PWR_WELL_CTL6)); -- Result for debugging only
116
117 if ((Ctl1 or Ctl2 or Ctl3 or Ctl4) and
118 PWR_WELL_CTL_POWER_REQUEST (PD)) /= 0
119 then
120 Registers.Wait_Set_Mask
121 (Register => Registers.PWR_WELL_CTL_DRIVER,
122 Mask => PWR_WELL_CTL_POWER_STATE (PD));
123 end if;
124
125 if (Ctl1 and PWR_WELL_CTL_POWER_REQUEST (PD)) /= 0 then
126 Registers.Unset_Mask
127 (Register => Registers.PWR_WELL_CTL_BIOS,
128 Mask => PWR_WELL_CTL_POWER_REQUEST (PD));
129 end if;
130
131 if (Ctl2 and PWR_WELL_CTL_POWER_REQUEST (PD)) /= 0 then
132 Registers.Unset_Mask
133 (Register => Registers.PWR_WELL_CTL_DRIVER,
134 Mask => PWR_WELL_CTL_POWER_REQUEST (PD));
135 end if;
136 end PD_Off;
137
138 procedure PD_On (PD : Power_Domain)
139 with
140 Pre => True
141 is
142 Ctl1, Ctl2, Ctl3, Ctl4 : Word32;
143 begin
144 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
145
146 Registers.Read (Registers.PWR_WELL_CTL_BIOS, Ctl1);
147 Registers.Read (Registers.PWR_WELL_CTL_DRIVER, Ctl2);
148 Registers.Read (Registers.PWR_WELL_CTL_KVMR, Ctl3);
149 Registers.Read (Registers.PWR_WELL_CTL_DEBUG, Ctl4);
150 pragma Debug (Registers.Posting_Read (Registers.PWR_WELL_CTL5)); -- Result for debugging only
151 pragma Debug (Registers.Posting_Read (Registers.PWR_WELL_CTL6)); -- Result for debugging only
152
153 if ((Ctl1 or Ctl2 or Ctl3 or Ctl4) and
154 PWR_WELL_CTL_POWER_REQUEST (PD)) = 0
155 then
156 Registers.Wait_Unset_Mask
157 (Register => Registers.PWR_WELL_CTL_DRIVER,
158 Mask => PWR_WELL_CTL_POWER_STATE (PD));
159 end if;
160
161 if (Ctl2 and PWR_WELL_CTL_POWER_REQUEST (PD)) = 0 then
162 Registers.Set_Mask
163 (Register => Registers.PWR_WELL_CTL_DRIVER,
164 Mask => PWR_WELL_CTL_POWER_REQUEST (PD));
165 Registers.Wait_Set_Mask
166 (Register => Registers.PWR_WELL_CTL_DRIVER,
167 Mask => PWR_WELL_CTL_POWER_STATE (PD));
168
169 if PD in Power_Well then
170 Registers.Wait_Set_Mask
171 (Register => Registers.FUSE_STATUS,
172 Mask => FUSE_STATUS_PGx_DIST_STATUS (PD));
173 end if;
174 end if;
175 end PD_On;
176
Nico Huber99f10f32016-11-20 00:34:05 +0100177 function Need_PD (PD : Dynamic_Domain; Configs : Pipe_Configs) return Boolean
Nico Huber83693c82016-10-08 22:17:55 +0200178 is
179 begin
180 return (case PD is
181 when DDI_AE => Configs (Primary).Port = Internal or
182 Configs (Secondary).Port = Internal or
183 Configs (Tertiary).Port = Internal,
Nico Huber0d454cd2016-11-21 13:33:43 +0100184 when DDI_B => Configs (Primary).Port = HDMI1 or
Nico Huber83693c82016-10-08 22:17:55 +0200185 Configs (Primary).Port = DP1 or
Nico Huber0d454cd2016-11-21 13:33:43 +0100186 Configs (Secondary).Port = HDMI1 or
Nico Huber83693c82016-10-08 22:17:55 +0200187 Configs (Secondary).Port = DP1 or
Nico Huber0d454cd2016-11-21 13:33:43 +0100188 Configs (Tertiary).Port = HDMI1 or
Nico Huber83693c82016-10-08 22:17:55 +0200189 Configs (Tertiary).Port = DP1,
Nico Huber0d454cd2016-11-21 13:33:43 +0100190 when DDI_C => Configs (Primary).Port = HDMI2 or
Nico Huber83693c82016-10-08 22:17:55 +0200191 Configs (Primary).Port = DP2 or
Nico Huber0d454cd2016-11-21 13:33:43 +0100192 Configs (Secondary).Port = HDMI2 or
Nico Huber83693c82016-10-08 22:17:55 +0200193 Configs (Secondary).Port = DP2 or
Nico Huber0d454cd2016-11-21 13:33:43 +0100194 Configs (Tertiary).Port = HDMI2 or
Nico Huber83693c82016-10-08 22:17:55 +0200195 Configs (Tertiary).Port = DP2,
Nico Huber0d454cd2016-11-21 13:33:43 +0100196 when DDI_D => Configs (Primary).Port = HDMI3 or
Nico Huber83693c82016-10-08 22:17:55 +0200197 Configs (Primary).Port = DP3 or
Nico Huber0d454cd2016-11-21 13:33:43 +0100198 Configs (Secondary).Port = HDMI3 or
Nico Huber83693c82016-10-08 22:17:55 +0200199 Configs (Secondary).Port = DP3 or
Nico Huber0d454cd2016-11-21 13:33:43 +0100200 Configs (Tertiary).Port = HDMI3 or
Nico Huber83693c82016-10-08 22:17:55 +0200201 Configs (Tertiary).Port = DP3,
202 when PW2 => (Configs (Primary).Port /= Disabled and
203 Configs (Primary).Port /= Internal) or
204 Configs (Secondary).Port /= Disabled or
205 Configs (Tertiary).Port /= Disabled);
206 end Need_PD;
207
208 ----------------------------------------------------------------------------
209
210 procedure Pre_All_Off is
211 begin
212 Power_And_Clocks_Haswell.PSR_Off;
213 end Pre_All_Off;
214
215 procedure Post_All_Off is
216 begin
217 for PD in reverse Dynamic_Domain loop
218 PD_Off (PD);
219 end loop;
220
221 Registers.Unset_Mask
222 (Register => Registers.DBUF_CTL,
223 Mask => DBUF_CTL_DBUF_POWER_REQUEST);
224 Registers.Wait_Unset_Mask
225 (Register => Registers.DBUF_CTL,
226 Mask => DBUF_CTL_DBUF_POWER_STATE);
227
228 Registers.Unset_Mask
229 (Register => Registers.LCPLL1_CTL,
230 Mask => LCPLL1_CTL_PLL_ENABLE);
231 Registers.Wait_Unset_Mask
232 (Register => Registers.LCPLL1_CTL,
233 Mask => LCPLL1_CTL_PLL_LOCK);
234
235 PD_Off (MISC_IO);
236 PD_Off (PW1);
237 end Post_All_Off;
238
Nico Huber6b4678d2019-09-22 21:31:52 +0200239 function Normalize_CDClk (CDClk : in Int64) return Config.CDClk_Range is
240 ( if CDClk <= 337_500_000 then 337_500_000
241 elsif CDClk <= 450_000_000 then 450_000_000
242 elsif CDClk <= 540_000_000 then 540_000_000
243 else 675_000_000);
244
245 procedure Get_Cur_CDClk (CDClk : out Config.CDClk_Range)
Nico Huber83693c82016-10-08 22:17:55 +0200246 is
Nico Huber6b4678d2019-09-22 21:31:52 +0200247 CDCLK_CTL : Word32;
248 begin
249 Registers.Read (Registers.CDCLK_CTL, CDCLK_CTL);
250 CDCLK_CTL := CDCLK_CTL and CDCLK_CTL_CD_FREQ_DECIMAL_MASK;
251 CDClk := Normalize_CDClk (Int64 (CDCLK_CTL) * 500_000 + 1_000_000);
252 end Get_Cur_CDClk;
253
254 procedure Get_Max_CDClk (CDClk : out Config.CDClk_Range)
255 is
256 DFSM : Word32;
257 begin
258 Registers.Read (Registers.DFSM, DFSM);
259 CDClk :=
260 (case DFSM and DFSM_DISPLAY_CDCLK_LIMIT_MASK is
261 when DFSM_DISPLAY_CDCLK_LIMIT_675MHZ => 675_000_000,
262 when DFSM_DISPLAY_CDCLK_LIMIT_540MHZ => 540_000_000,
263 when DFSM_DISPLAY_CDCLK_LIMIT_450MHZ => 450_000_000,
264 when others => 337_500_000);
265 end Get_Max_CDClk;
266
267 procedure Set_CDClk (CDClk_In : Frequency_Type)
268 is
269 CDClk : constant Config.CDClk_Range :=
270 Normalize_CDClk (Frequency_Type'Min (CDClk_In, Config.Max_CDClk));
Nico Huber312433c2019-09-28 03:15:48 +0200271 Success : Boolean;
Nico Huber83693c82016-10-08 22:17:55 +0200272 begin
Nico Huber6b4678d2019-09-22 21:31:52 +0200273 PCode.Mailbox_Request
274 (MBox => SKL_PCODE_CDCLK_CONTROL,
275 Command => SKL_CDCLK_PREPARE_FOR_CHANGE,
276 Reply_Mask => SKL_CDCLK_READY_FOR_CHANGE,
277 Wait_Ready => True,
278 Success => Success);
279
280 if not Success then
281 pragma Debug (Debug.Put_Line
282 ("ERROR: PCODE not ready for frequency change."));
283 return;
284 end if;
285
286 Registers.Write
287 (Register => Registers.CDCLK_CTL,
288 Value => (case CDClk is
289 when 675_000_000 => CDCLK_CTL_CD_FREQ_SELECT_675MHZ,
290 when 540_000_000 => CDCLK_CTL_CD_FREQ_SELECT_540MHZ,
291 when 450_000_000 => CDCLK_CTL_CD_FREQ_SELECT_450MHZ,
292 when others => CDCLK_CTL_CD_FREQ_SELECT_337_5MHZ)
293 or CDCLK_CTL_CD_FREQ_DECIMAL (CDClk));
294
295 PCode.Mailbox_Write
296 (MBox => SKL_PCODE_CDCLK_CONTROL,
297 Command => (case CDClk is
298 when 675_000_000 => 3,
299 when 540_000_000 => 2,
300 when 450_000_000 => 1,
301 when others => 0));
302 Registers.Set_Mask
303 (Register => Registers.DBUF_CTL,
304 Mask => DBUF_CTL_DBUF_POWER_REQUEST);
305 Registers.Wait_Set_Mask
306 (Register => Registers.DBUF_CTL,
307 Mask => DBUF_CTL_DBUF_POWER_STATE);
308
309 Config.CDClk := CDClk;
310 end Set_CDClk;
311
312 procedure Initialize is
313 begin
Nico Huber83693c82016-10-08 22:17:55 +0200314 Registers.Set_Mask
315 (Register => Registers.NDE_RSTWRN_OPT,
316 Mask => NDE_RSTWRN_OPT_RST_PCH_Handshake_En);
317
318 Registers.Wait_Set_Mask
319 (Register => Registers.FUSE_STATUS,
320 Mask => FUSE_STATUS_PG0_DIST_STATUS);
321 PD_On (PW1);
322 PD_On (MISC_IO);
323
Nico Huber83693c82016-10-08 22:17:55 +0200324 -- TODO: Set to preferred eDP rate:
325 -- Registers.Unset_And_Set_Mask
326 -- (Register => Registers.DPLL_CTRL1,
327 -- Unset_Mask => DPLL_CTRL1_DPLL0_LINK_RATE_MASK,
328 -- Set_Mask => DPLL_CTRL1_DPLL0_LINK_RATE_...);
329 Registers.Set_Mask
330 (Register => Registers.LCPLL1_CTL,
331 Mask => LCPLL1_CTL_PLL_ENABLE);
332 Registers.Wait_Set_Mask
333 (Register => Registers.LCPLL1_CTL,
334 Mask => LCPLL1_CTL_PLL_LOCK);
335
Nico Huber6b4678d2019-09-22 21:31:52 +0200336 Get_Cur_CDClk (Config.CDClk);
337 Get_Max_CDClk (Config.Max_CDClk);
338 Set_CDClk (Config.Default_CDClk_Freq);
Arthur Heymansd1988d12018-03-28 16:27:57 +0200339
340 Config.Raw_Clock := Config.Default_RawClk_Freq;
Nico Huber83693c82016-10-08 22:17:55 +0200341 end Initialize;
342
Nico Huber6b4678d2019-09-22 21:31:52 +0200343 procedure Limit_Dotclocks
344 (Configs : in out Pipe_Configs;
345 CDClk_Switch : out Boolean)
346 is
347 begin
348 Config_Helpers.Limit_Dotclocks (Configs, Config.Max_CDClk);
349 CDClk_Switch :=
350 Config.CDClk /= Normalize_CDClk
351 (Config_Helpers.Highest_Dotclock (Configs));
352 end Limit_Dotclocks;
353
354 procedure Update_CDClk (Configs : in out Pipe_Configs)
355 is
356 New_CDClk : constant Frequency_Type :=
357 Config_Helpers.Highest_Dotclock (Configs);
358 begin
359 Set_CDClk (New_CDClk);
360 Config_Helpers.Limit_Dotclocks (Configs, Config.CDClk);
361 end Update_CDClk;
362
Nico Huber99f10f32016-11-20 00:34:05 +0100363 procedure Power_Set_To (Configs : Pipe_Configs) is
Nico Huber83693c82016-10-08 22:17:55 +0200364 begin
365 for PD in reverse Dynamic_Domain loop
366 if not Need_PD (PD, Configs) then
367 PD_Off (PD);
368 end if;
369 end loop;
370 for PD in Dynamic_Domain loop
371 if Need_PD (PD, Configs) then
372 PD_On (PD);
373 end if;
374 end loop;
375 end Power_Set_To;
376
Nico Huber99f10f32016-11-20 00:34:05 +0100377 procedure Power_Up (Old_Configs, New_Configs : Pipe_Configs) is
Nico Huber83693c82016-10-08 22:17:55 +0200378 begin
379 for PD in Dynamic_Domain loop
380 if not Need_PD (PD, Old_Configs) and Need_PD (PD, New_Configs) then
381 PD_On (PD);
382 end if;
383 end loop;
384 end Power_Up;
385
Nico Huber99f10f32016-11-20 00:34:05 +0100386 procedure Power_Down (Old_Configs, Tmp_Configs, New_Configs : Pipe_Configs)
Nico Huber83693c82016-10-08 22:17:55 +0200387 is
388 begin
389 for PD in reverse Dynamic_Domain loop
390 if (Need_PD (PD, Old_Configs) or Need_PD (PD, Tmp_Configs)) and
391 not Need_PD (PD, New_Configs)
392 then
393 PD_Off (PD);
394 end if;
395 end loop;
396 end Power_Down;
397
398end HW.GFX.GMA.Power_And_Clocks_Skylake;