blob: 279071a1134102109e98de47683c53d311195fca [file] [log] [blame]
Nico Huber40820442017-01-20 14:00:53 +01001--
2-- Copyright (C) 2014-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;
16
17with HW.Debug;
18with HW.GFX.GMA.Config;
19with HW.GFX.GMA.Registers;
Nico Huber312433c2019-09-28 03:15:48 +020020with HW.GFX.GMA.PCode;
Angel Pons3f86b0b2020-07-18 00:22:32 +020021with HW.GFX.GMA.Transcoder;
Nico Huber40820442017-01-20 14:00:53 +010022with HW.GFX.GMA.DDI_Phy;
23
24use HW.GFX.GMA.Registers;
25
26package body HW.GFX.GMA.Power_And_Clocks is
27
28 type Power_Domain is (PW1, PW2, DDI_A, DDI_BC);
29 subtype Power_Well is Power_Domain range PW1 .. PW2;
30 subtype Dynamic_Domain is Power_Domain range PW2 .. DDI_BC;
31 subtype DDI_Domain is Dynamic_Domain range DDI_A .. DDI_BC;
32
33 type DDI_Phy_Array is array (DDI_Domain) of DDI_Phy.T;
34 Phy : constant DDI_Phy_Array := (DDI_A => DDI_Phy.A, DDI_BC => DDI_Phy.BC);
35
36 NDE_RSTWRN_OPT_RST_PCH_Handshake_En : constant := 1 * 2 ** 4;
37
38 FUSE_STATUS_DOWNLOAD_STATUS : constant := 1 * 2 ** 31;
39 FUSE_STATUS_PG0_DIST_STATUS : constant := 1 * 2 ** 27;
40
41 type Power_Well_Values is array (Power_Well) of Word32;
42 PWR_WELL_CTL_POWER_REQUEST : constant Power_Well_Values :=
43 (PW1 => 1 * 2 ** 29,
44 PW2 => 1 * 2 ** 31);
45 PWR_WELL_CTL_POWER_STATE : constant Power_Well_Values :=
46 (PW1 => 1 * 2 ** 28,
47 PW2 => 1 * 2 ** 30);
48
49 FUSE_STATUS_PGx_DIST_STATUS : constant Power_Well_Values :=
50 (PW1 => 1 * 2 ** 26,
51 PW2 => 1 * 2 ** 25);
52
53 DBUF_CTL_DBUF_POWER_REQUEST : constant := 1 * 2 ** 31;
54 DBUF_CTL_DBUF_POWER_STATE : constant := 1 * 2 ** 30;
55
56 ----------------------------------------------------------------------------
57
58 BXT_DE_PLL_RATIO_MASK : constant := 16#ff#;
59 BXT_DE_PLL_PLL_ENABLE : constant := 1 * 2 ** 31;
60 BXT_DE_PLL_PLL_LOCK : constant := 1 * 2 ** 30;
61
62 CDCLK_CD2X_DIV_SEL_MASK : constant := 3 * 2 ** 22;
63 CDCLK_CD2X_DIV_SEL_1 : constant := 0 * 2 ** 22;
64 CDCLK_CD2X_DIV_SEL_1_5 : constant := 1 * 2 ** 22;
65 CDCLK_CD2X_DIV_SEL_2 : constant := 2 * 2 ** 22;
66 CDCLK_CD2X_DIV_SEL_4 : constant := 3 * 2 ** 22;
67 CDCLK_CD2X_PIPE_NONE : constant := 3 * 2 ** 20;
68 CDCLK_CD2X_SSA_PRECHARGE_ENABLE : constant := 1 * 2 ** 16;
69 CDCLK_CTL_CD_FREQ_DECIMAL_MASK : constant := 16#7ff#;
70
Nico Huber565f33b2018-06-04 14:42:13 +020071 function CDCLK_CTL_CD_FREQ_DECIMAL (Freq : Frequency_Type) return Word32 is
Nico Huber40820442017-01-20 14:00:53 +010072 begin
73 return Word32 (2 * (Freq / 1_000_000 - 1));
74 end CDCLK_CTL_CD_FREQ_DECIMAL;
75
76 BXT_PCODE_CDCLK_CONTROL : constant := 16#17#;
77 BXT_CDCLK_PREPARE_FOR_CHANGE : constant := 16#8000_0000#;
78
79 ----------------------------------------------------------------------------
80
81 procedure PW_Off (PD : Power_Well)
82 is
83 Ctl1, Ctl2, Ctl3, Ctl4 : Word32;
84 begin
85 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
86
87 Read (PWR_WELL_CTL_BIOS, Ctl1);
88 Read (PWR_WELL_CTL_DRIVER, Ctl2);
89 Read (PWR_WELL_CTL_KVMR, Ctl3);
90 Read (PWR_WELL_CTL_DEBUG, Ctl4);
91 pragma Debug (Posting_Read (PWR_WELL_CTL5)); -- Result for debugging only
92 pragma Debug (Posting_Read (PWR_WELL_CTL6)); -- Result for debugging only
93
94 if ((Ctl1 or Ctl2 or Ctl3 or Ctl4) and
95 PWR_WELL_CTL_POWER_REQUEST (PD)) /= 0
96 then
97 Wait_Set_Mask (PWR_WELL_CTL_DRIVER, PWR_WELL_CTL_POWER_STATE (PD));
98 end if;
99
100 if (Ctl1 and PWR_WELL_CTL_POWER_REQUEST (PD)) /= 0 then
101 Unset_Mask (PWR_WELL_CTL_BIOS, PWR_WELL_CTL_POWER_REQUEST (PD));
102 end if;
103
104 if (Ctl2 and PWR_WELL_CTL_POWER_REQUEST (PD)) /= 0 then
105 Unset_Mask (PWR_WELL_CTL_DRIVER, PWR_WELL_CTL_POWER_REQUEST (PD));
106 end if;
107 end PW_Off;
108
109 procedure PW_On (PD : Power_Well)
110 with
111 Pre => True
112 is
113 Ctl1, Ctl2, Ctl3, Ctl4 : Word32;
114 begin
115 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
116
117 Read (PWR_WELL_CTL_BIOS, Ctl1);
118 Read (PWR_WELL_CTL_DRIVER, Ctl2);
119 Read (PWR_WELL_CTL_KVMR, Ctl3);
120 Read (PWR_WELL_CTL_DEBUG, Ctl4);
121 pragma Debug (Posting_Read (PWR_WELL_CTL5)); -- Result for debugging only
122 pragma Debug (Posting_Read (PWR_WELL_CTL6)); -- Result for debugging only
123
124 if ((Ctl1 or Ctl2 or Ctl3 or Ctl4) and
125 PWR_WELL_CTL_POWER_REQUEST (PD)) = 0
126 then
127 Wait_Unset_Mask (PWR_WELL_CTL_DRIVER, PWR_WELL_CTL_POWER_STATE (PD));
128 end if;
129
130 if (Ctl2 and PWR_WELL_CTL_POWER_REQUEST (PD)) = 0 then
131 Set_Mask (PWR_WELL_CTL_DRIVER, PWR_WELL_CTL_POWER_REQUEST (PD));
132 Wait_Set_Mask (PWR_WELL_CTL_DRIVER, PWR_WELL_CTL_POWER_STATE (PD));
133
134 Wait_Set_Mask (FUSE_STATUS, FUSE_STATUS_PGx_DIST_STATUS (PD));
135 end if;
136 end PW_On;
137
138 procedure PD_On (PD : Power_Domain) is
139 begin
140 if PD in Power_Well then
141 PW_On (PD);
142 else
143 DDI_Phy.Power_On (Phy (PD));
144 end if;
145 end PD_On;
146
147 procedure PD_Off (PD : Power_Domain) is
148 begin
149 if PD in Power_Well then
150 PW_Off (PD);
151 else
152 DDI_Phy.Power_Off (Phy (PD));
153 end if;
154 end PD_Off;
155
156 function Need_PD (PD : Dynamic_Domain; Configs : Pipe_Configs) return Boolean
157 is
158 begin
159 return (case PD is
Nico Huber8beafd72020-01-07 14:59:44 +0100160 when DDI_A => Configs (Primary).Port = eDP or
161 Configs (Secondary).Port = eDP or
162 Configs (Tertiary).Port = eDP,
Nico Huber40820442017-01-20 14:00:53 +0100163 when DDI_BC => Configs (Primary).Port = HDMI1 or
164 Configs (Primary).Port = DP1 or
165 Configs (Secondary).Port = HDMI1 or
166 Configs (Secondary).Port = DP1 or
167 Configs (Tertiary).Port = HDMI1 or
168 Configs (Tertiary).Port = DP1 or
169 Configs (Primary).Port = HDMI2 or
170 Configs (Primary).Port = DP2 or
171 Configs (Secondary).Port = HDMI2 or
172 Configs (Secondary).Port = DP2 or
173 Configs (Tertiary).Port = HDMI2 or
174 Configs (Tertiary).Port = DP2,
175 when PW2 => (Configs (Primary).Port /= Disabled and
Nico Huber8beafd72020-01-07 14:59:44 +0100176 Configs (Primary).Port /= eDP) or
Nico Huber40820442017-01-20 14:00:53 +0100177 Configs (Secondary).Port /= Disabled or
178 Configs (Tertiary).Port /= Disabled);
179 end Need_PD;
180
181 procedure Power_Set_To (Configs : Pipe_Configs) is
182 begin
183 for PD in reverse Dynamic_Domain loop
184 if not Need_PD (PD, Configs) then
185 PD_Off (PD);
186 end if;
187 end loop;
188 for PD in Dynamic_Domain loop
189 if Need_PD (PD, Configs) then
190 PD_On (PD);
191 end if;
192 end loop;
193 end Power_Set_To;
194
Nico Huber41e86742024-07-17 17:10:28 +0200195 procedure Power_Up (Port : Active_Port_Type; Success : out Boolean) is
196 begin
197 case Port is
198 when eDP => PD_On (DDI_A);
199 when DP1 | HDMI1 | DP2 | HDMI2 => PW_On (PW2); PD_On (DDI_BC);
200 when others => null;
201 end case;
202 Success := True;
203 end Power_Up;
204
Nico Huber40820442017-01-20 14:00:53 +0100205 procedure Power_Up (Old_Configs, New_Configs : Pipe_Configs) is
206 begin
207 for PD in Dynamic_Domain loop
208 if not Need_PD (PD, Old_Configs) and Need_PD (PD, New_Configs) then
209 PD_On (PD);
210 end if;
211 end loop;
212 end Power_Up;
213
214 procedure Power_Down (Old_Configs, Tmp_Configs, New_Configs : Pipe_Configs)
215 is
216 begin
217 for PD in reverse Dynamic_Domain loop
218 if (Need_PD (PD, Old_Configs) or Need_PD (PD, Tmp_Configs)) and
219 not Need_PD (PD, New_Configs)
220 then
221 PD_Off (PD);
222 end if;
223 end loop;
224 end Power_Down;
225
226 ----------------------------------------------------------------------------
227
228 CDClk_Ref : constant := 19_200_000;
229
Nico Huber8469b002019-09-22 21:31:52 +0200230 function Normalize_CDClk (CDClk : in Int64) return Config.CDClk_Range is
231 (if CDClk <= CDClk_Ref then CDClk_Ref
232 elsif CDClk <= 144_000_000 then 144_000_000
233 elsif CDClk <= 288_000_000 then 288_000_000
234 elsif CDClk <= 384_000_000 then 384_000_000
235 elsif CDClk <= 576_000_000 then 576_000_000
236 else 624_000_000);
237
238 procedure Get_Cur_CDClk (CDClk : out Config.CDClk_Range)
239 is
240 CDCLK_CTL : Word32;
241 begin
242 Registers.Read (Registers.CDCLK_CTL, CDCLK_CTL);
243 CDCLK_CTL := CDCLK_CTL and CDCLK_CTL_CD_FREQ_DECIMAL_MASK;
244 CDClk := Normalize_CDClk (Int64 (CDCLK_CTL) * 500_000 + 1_000_000);
245 end Get_Cur_CDClk;
246
Nico Huber1ee57142018-06-10 12:42:46 +0200247 procedure Set_CDClk (Freq_In : Frequency_Type)
Nico Huber40820442017-01-20 14:00:53 +0100248 is
Nico Huber8469b002019-09-22 21:31:52 +0200249 Freq : constant Config.CDClk_Range := Normalize_CDClk (Freq_In);
Nico Huber565f33b2018-06-04 14:42:13 +0200250 VCO : constant Int64 :=
Nico Huber40820442017-01-20 14:00:53 +0100251 CDClk_Ref *
252 (if Freq = CDClk_Ref then
253 0
254 elsif Freq = 624_000_000 then
255 65
256 else
257 60);
258 CDCLK_CD2X_Div_Sel : constant Word32 :=
259 (case VCO / Freq is -- CDClk = VCO / 2 / Div
260 when 2 => CDCLK_CD2X_DIV_SEL_1,
261 when 3 => CDCLK_CD2X_DIV_SEL_1_5,
262 when 4 => CDCLK_CD2X_DIV_SEL_2,
263 when 8 => CDCLK_CD2X_DIV_SEL_4,
264 when others => CDCLK_CD2X_DIV_SEL_1); -- for CDClk = CDClk_Ref
265 CDCLK_CD2X_SSA_Precharge : constant Word32 :=
266 (if Freq >= 500_000_000 then CDCLK_CD2X_SSA_PRECHARGE_ENABLE else 0);
Nico Huber312433c2019-09-28 03:15:48 +0200267
268 Success : Boolean;
Nico Huber40820442017-01-20 14:00:53 +0100269 begin
Nico Huber312433c2019-09-28 03:15:48 +0200270 PCode.Mailbox_Write
Nico Huber40820442017-01-20 14:00:53 +0100271 (MBox => BXT_PCODE_CDCLK_CONTROL,
Nico Huber312433c2019-09-28 03:15:48 +0200272 Command => BXT_CDCLK_PREPARE_FOR_CHANGE,
273 Wait_Ready => True,
274 Success => Success);
275 if not Success then
276 pragma Debug (Debug.Put_Line
277 ("ERROR: PCODE didn't acknowledge frequency change."));
278 return;
279 end if;
Nico Huber40820442017-01-20 14:00:53 +0100280
281 Write
282 (Register => BXT_DE_PLL_ENABLE,
283 Value => 16#0000_0000#);
284 Wait_Unset_Mask
285 (Register => BXT_DE_PLL_ENABLE,
286 Mask => BXT_DE_PLL_PLL_LOCK,
287 TOut_MS => 1); -- 200us
288
289 Unset_And_Set_Mask
290 (Register => BXT_DE_PLL_CTL,
291 Mask_Unset => BXT_DE_PLL_RATIO_MASK,
292 Mask_Set => Word32 (VCO / CDClk_Ref));
293 Write
294 (Register => BXT_DE_PLL_ENABLE,
295 Value => BXT_DE_PLL_PLL_ENABLE);
296 Wait_Set_Mask
297 (Register => BXT_DE_PLL_ENABLE,
298 Mask => BXT_DE_PLL_PLL_LOCK,
299 TOut_MS => 1); -- 200us
300
301 Write
302 (Register => CDCLK_CTL,
303 Value => CDCLK_CD2X_Div_Sel or
304 CDCLK_CD2X_PIPE_NONE or
305 CDCLK_CD2X_SSA_Precharge or
306 CDCLK_CTL_CD_FREQ_DECIMAL (Freq));
307
Nico Huber312433c2019-09-28 03:15:48 +0200308 PCode.Mailbox_Write
309 (MBox => BXT_PCODE_CDCLK_CONTROL,
310 Command => Word64 ((Freq + (25_000_000 - 1)) / 25_000_000));
Nico Huber8469b002019-09-22 21:31:52 +0200311
312 Config.CDClk := Freq;
Nico Huber40820442017-01-20 14:00:53 +0100313 end Set_CDClk;
314
315 ----------------------------------------------------------------------------
316
317 procedure Pre_All_Off is
318 begin
319 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
Angel Pons3f86b0b2020-07-18 00:22:32 +0200320 Transcoder.PSR_Off;
Nico Huber40820442017-01-20 14:00:53 +0100321 end Pre_All_Off;
322
323 procedure Post_All_Off is
324 begin
325 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
326
327 for PD in reverse Dynamic_Domain loop
328 PD_Off (PD);
329 end loop;
330
Tim Wawrzynczakb6df6832022-09-09 11:47:27 -0600331 Unset_Mask (DBUF_CTL_S0, DBUF_CTL_DBUF_POWER_REQUEST);
332 Wait_Unset_Mask (DBUF_CTL_S0, DBUF_CTL_DBUF_POWER_STATE);
Nico Huber40820442017-01-20 14:00:53 +0100333
334 -- Linux' i915 never keeps the PLL disabled but runs it
335 -- at a "ratio" of 0 with CDClk at its reference clock.
336 Set_CDClk (CDClk_Ref);
337
338 PW_Off (PW1);
339 end Post_All_Off;
340
341 procedure Initialize is
342 begin
343 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
344
345 -- no PCH for Broxton
346 Unset_Mask (NDE_RSTWRN_OPT, NDE_RSTWRN_OPT_RST_PCH_Handshake_En);
347
348 Wait_Set_Mask (FUSE_STATUS, FUSE_STATUS_PG0_DIST_STATUS);
349 PW_On (PW1);
350
Nico Huber8469b002019-09-22 21:31:52 +0200351 Config.Max_CDClk := 624_000_000;
352 Get_Cur_CDClk (Config.CDClk);
Nico Huber565f33b2018-06-04 14:42:13 +0200353 Set_CDClk (Config.Default_CDClk_Freq);
Nico Huber40820442017-01-20 14:00:53 +0100354
Tim Wawrzynczakb6df6832022-09-09 11:47:27 -0600355 Set_Mask (DBUF_CTL_S0, DBUF_CTL_DBUF_POWER_REQUEST);
356 Wait_Set_Mask (DBUF_CTL_S0, DBUF_CTL_DBUF_POWER_STATE);
Arthur Heymansd1988d12018-03-28 16:27:57 +0200357
358 Config.Raw_Clock := Config.Default_RawClk_Freq;
Nico Huber40820442017-01-20 14:00:53 +0100359 end Initialize;
360
Nico Huber8469b002019-09-22 21:31:52 +0200361 procedure Limit_Dotclocks
362 (Configs : in out Pipe_Configs;
363 CDClk_Switch : out Boolean)
364 is
365 begin
366 Config_Helpers.Limit_Dotclocks (Configs, Config.Max_CDClk);
367 CDClk_Switch :=
368 Config.CDClk /= Normalize_CDClk
369 (Config_Helpers.Highest_Dotclock (Configs));
370 end Limit_Dotclocks;
371
372 procedure Update_CDClk (Configs : in out Pipe_Configs)
373 is
374 New_CDClk : constant Frequency_Type :=
375 Config_Helpers.Highest_Dotclock (Configs);
376 begin
377 Set_CDClk (New_CDClk);
378 Config_Helpers.Limit_Dotclocks (Configs, Config.CDClk);
379 end Update_CDClk;
380
381 procedure Enable_CDClk is
382 begin
383 -- CDClk_Ref means we have CDClk effectively disabled
384 if Config.CDClk = CDClk_Ref then
385 Set_CDClk (Config.Default_CDClk_Freq);
386 end if;
387 end Enable_CDClk;
388
Nico Huber40820442017-01-20 14:00:53 +0100389end HW.GFX.GMA.Power_And_Clocks;