blob: fe10208e167627693b5039e569b2828539f14948 [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;
Nico Huber40820442017-01-20 14:00:53 +010021with HW.GFX.GMA.Power_And_Clocks_Haswell;
22with 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
160 when DDI_A => Configs (Primary).Port = Internal or
161 Configs (Secondary).Port = Internal or
162 Configs (Tertiary).Port = Internal,
163 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
176 Configs (Primary).Port /= Internal) or
177 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
195 procedure Power_Up (Old_Configs, New_Configs : Pipe_Configs) is
196 begin
197 for PD in Dynamic_Domain loop
198 if not Need_PD (PD, Old_Configs) and Need_PD (PD, New_Configs) then
199 PD_On (PD);
200 end if;
201 end loop;
202 end Power_Up;
203
204 procedure Power_Down (Old_Configs, Tmp_Configs, New_Configs : Pipe_Configs)
205 is
206 begin
207 for PD in reverse Dynamic_Domain loop
208 if (Need_PD (PD, Old_Configs) or Need_PD (PD, Tmp_Configs)) and
209 not Need_PD (PD, New_Configs)
210 then
211 PD_Off (PD);
212 end if;
213 end loop;
214 end Power_Down;
215
216 ----------------------------------------------------------------------------
217
218 CDClk_Ref : constant := 19_200_000;
219
Nico Huber8469b002019-09-22 21:31:52 +0200220 function Normalize_CDClk (CDClk : in Int64) return Config.CDClk_Range is
221 (if CDClk <= CDClk_Ref then CDClk_Ref
222 elsif CDClk <= 144_000_000 then 144_000_000
223 elsif CDClk <= 288_000_000 then 288_000_000
224 elsif CDClk <= 384_000_000 then 384_000_000
225 elsif CDClk <= 576_000_000 then 576_000_000
226 else 624_000_000);
227
228 procedure Get_Cur_CDClk (CDClk : out Config.CDClk_Range)
229 is
230 CDCLK_CTL : Word32;
231 begin
232 Registers.Read (Registers.CDCLK_CTL, CDCLK_CTL);
233 CDCLK_CTL := CDCLK_CTL and CDCLK_CTL_CD_FREQ_DECIMAL_MASK;
234 CDClk := Normalize_CDClk (Int64 (CDCLK_CTL) * 500_000 + 1_000_000);
235 end Get_Cur_CDClk;
236
Nico Huber1ee57142018-06-10 12:42:46 +0200237 procedure Set_CDClk (Freq_In : Frequency_Type)
Nico Huber40820442017-01-20 14:00:53 +0100238 is
Nico Huber8469b002019-09-22 21:31:52 +0200239 Freq : constant Config.CDClk_Range := Normalize_CDClk (Freq_In);
Nico Huber565f33b2018-06-04 14:42:13 +0200240 VCO : constant Int64 :=
Nico Huber40820442017-01-20 14:00:53 +0100241 CDClk_Ref *
242 (if Freq = CDClk_Ref then
243 0
244 elsif Freq = 624_000_000 then
245 65
246 else
247 60);
248 CDCLK_CD2X_Div_Sel : constant Word32 :=
249 (case VCO / Freq is -- CDClk = VCO / 2 / Div
250 when 2 => CDCLK_CD2X_DIV_SEL_1,
251 when 3 => CDCLK_CD2X_DIV_SEL_1_5,
252 when 4 => CDCLK_CD2X_DIV_SEL_2,
253 when 8 => CDCLK_CD2X_DIV_SEL_4,
254 when others => CDCLK_CD2X_DIV_SEL_1); -- for CDClk = CDClk_Ref
255 CDCLK_CD2X_SSA_Precharge : constant Word32 :=
256 (if Freq >= 500_000_000 then CDCLK_CD2X_SSA_PRECHARGE_ENABLE else 0);
Nico Huber312433c2019-09-28 03:15:48 +0200257
258 Success : Boolean;
Nico Huber40820442017-01-20 14:00:53 +0100259 begin
Nico Huber312433c2019-09-28 03:15:48 +0200260 PCode.Mailbox_Write
Nico Huber40820442017-01-20 14:00:53 +0100261 (MBox => BXT_PCODE_CDCLK_CONTROL,
Nico Huber312433c2019-09-28 03:15:48 +0200262 Command => BXT_CDCLK_PREPARE_FOR_CHANGE,
263 Wait_Ready => True,
264 Success => Success);
265 if not Success then
266 pragma Debug (Debug.Put_Line
267 ("ERROR: PCODE didn't acknowledge frequency change."));
268 return;
269 end if;
Nico Huber40820442017-01-20 14:00:53 +0100270
271 Write
272 (Register => BXT_DE_PLL_ENABLE,
273 Value => 16#0000_0000#);
274 Wait_Unset_Mask
275 (Register => BXT_DE_PLL_ENABLE,
276 Mask => BXT_DE_PLL_PLL_LOCK,
277 TOut_MS => 1); -- 200us
278
279 Unset_And_Set_Mask
280 (Register => BXT_DE_PLL_CTL,
281 Mask_Unset => BXT_DE_PLL_RATIO_MASK,
282 Mask_Set => Word32 (VCO / CDClk_Ref));
283 Write
284 (Register => BXT_DE_PLL_ENABLE,
285 Value => BXT_DE_PLL_PLL_ENABLE);
286 Wait_Set_Mask
287 (Register => BXT_DE_PLL_ENABLE,
288 Mask => BXT_DE_PLL_PLL_LOCK,
289 TOut_MS => 1); -- 200us
290
291 Write
292 (Register => CDCLK_CTL,
293 Value => CDCLK_CD2X_Div_Sel or
294 CDCLK_CD2X_PIPE_NONE or
295 CDCLK_CD2X_SSA_Precharge or
296 CDCLK_CTL_CD_FREQ_DECIMAL (Freq));
297
Nico Huber312433c2019-09-28 03:15:48 +0200298 PCode.Mailbox_Write
299 (MBox => BXT_PCODE_CDCLK_CONTROL,
300 Command => Word64 ((Freq + (25_000_000 - 1)) / 25_000_000));
Nico Huber8469b002019-09-22 21:31:52 +0200301
302 Config.CDClk := Freq;
Nico Huber40820442017-01-20 14:00:53 +0100303 end Set_CDClk;
304
305 ----------------------------------------------------------------------------
306
307 procedure Pre_All_Off is
308 begin
309 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
310 Power_And_Clocks_Haswell.PSR_Off;
311 end Pre_All_Off;
312
313 procedure Post_All_Off is
314 begin
315 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
316
317 for PD in reverse Dynamic_Domain loop
318 PD_Off (PD);
319 end loop;
320
321 Unset_Mask (DBUF_CTL, DBUF_CTL_DBUF_POWER_REQUEST);
322 Wait_Unset_Mask (DBUF_CTL, DBUF_CTL_DBUF_POWER_STATE);
323
324 -- Linux' i915 never keeps the PLL disabled but runs it
325 -- at a "ratio" of 0 with CDClk at its reference clock.
326 Set_CDClk (CDClk_Ref);
327
328 PW_Off (PW1);
329 end Post_All_Off;
330
331 procedure Initialize is
332 begin
333 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
334
335 -- no PCH for Broxton
336 Unset_Mask (NDE_RSTWRN_OPT, NDE_RSTWRN_OPT_RST_PCH_Handshake_En);
337
338 Wait_Set_Mask (FUSE_STATUS, FUSE_STATUS_PG0_DIST_STATUS);
339 PW_On (PW1);
340
Nico Huber8469b002019-09-22 21:31:52 +0200341 Config.Max_CDClk := 624_000_000;
342 Get_Cur_CDClk (Config.CDClk);
Nico Huber565f33b2018-06-04 14:42:13 +0200343 Set_CDClk (Config.Default_CDClk_Freq);
Nico Huber40820442017-01-20 14:00:53 +0100344
345 Set_Mask (DBUF_CTL, DBUF_CTL_DBUF_POWER_REQUEST);
346 Wait_Set_Mask (DBUF_CTL, DBUF_CTL_DBUF_POWER_STATE);
Arthur Heymansd1988d12018-03-28 16:27:57 +0200347
348 Config.Raw_Clock := Config.Default_RawClk_Freq;
Nico Huber40820442017-01-20 14:00:53 +0100349 end Initialize;
350
Nico Huber8469b002019-09-22 21:31:52 +0200351 procedure Limit_Dotclocks
352 (Configs : in out Pipe_Configs;
353 CDClk_Switch : out Boolean)
354 is
355 begin
356 Config_Helpers.Limit_Dotclocks (Configs, Config.Max_CDClk);
357 CDClk_Switch :=
358 Config.CDClk /= Normalize_CDClk
359 (Config_Helpers.Highest_Dotclock (Configs));
360 end Limit_Dotclocks;
361
362 procedure Update_CDClk (Configs : in out Pipe_Configs)
363 is
364 New_CDClk : constant Frequency_Type :=
365 Config_Helpers.Highest_Dotclock (Configs);
366 begin
367 Set_CDClk (New_CDClk);
368 Config_Helpers.Limit_Dotclocks (Configs, Config.CDClk);
369 end Update_CDClk;
370
371 procedure Enable_CDClk is
372 begin
373 -- CDClk_Ref means we have CDClk effectively disabled
374 if Config.CDClk = CDClk_Ref then
375 Set_CDClk (Config.Default_CDClk_Freq);
376 end if;
377 end Enable_CDClk;
378
Nico Huber40820442017-01-20 14:00:53 +0100379end HW.GFX.GMA.Power_And_Clocks;