blob: 77f6b359a0fcf5a4485dcbd695f6d8ea5633c030 [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 Huber1ee57142018-06-10 12:42:46 +0200220 procedure Set_CDClk (Freq_In : Frequency_Type)
Nico Huber40820442017-01-20 14:00:53 +0100221 is
Nico Huber1ee57142018-06-10 12:42:46 +0200222 Freq : constant Frequency_Type :=
223 (if Freq_In = CDClk_Ref then CDClk_Ref
224 elsif Freq_In <= 144_000_000 then 144_000_000
225 elsif Freq_In <= 288_000_000 then 288_000_000
226 elsif Freq_In <= 384_000_000 then 384_000_000
227 elsif Freq_In <= 576_000_000 then 576_000_000
228 else 624_000_000);
Nico Huber565f33b2018-06-04 14:42:13 +0200229 VCO : constant Int64 :=
Nico Huber40820442017-01-20 14:00:53 +0100230 CDClk_Ref *
231 (if Freq = CDClk_Ref then
232 0
233 elsif Freq = 624_000_000 then
234 65
235 else
236 60);
237 CDCLK_CD2X_Div_Sel : constant Word32 :=
238 (case VCO / Freq is -- CDClk = VCO / 2 / Div
239 when 2 => CDCLK_CD2X_DIV_SEL_1,
240 when 3 => CDCLK_CD2X_DIV_SEL_1_5,
241 when 4 => CDCLK_CD2X_DIV_SEL_2,
242 when 8 => CDCLK_CD2X_DIV_SEL_4,
243 when others => CDCLK_CD2X_DIV_SEL_1); -- for CDClk = CDClk_Ref
244 CDCLK_CD2X_SSA_Precharge : constant Word32 :=
245 (if Freq >= 500_000_000 then CDCLK_CD2X_SSA_PRECHARGE_ENABLE else 0);
Nico Huber312433c2019-09-28 03:15:48 +0200246
247 Success : Boolean;
Nico Huber40820442017-01-20 14:00:53 +0100248 begin
Nico Huber312433c2019-09-28 03:15:48 +0200249 PCode.Mailbox_Write
Nico Huber40820442017-01-20 14:00:53 +0100250 (MBox => BXT_PCODE_CDCLK_CONTROL,
Nico Huber312433c2019-09-28 03:15:48 +0200251 Command => BXT_CDCLK_PREPARE_FOR_CHANGE,
252 Wait_Ready => True,
253 Success => Success);
254 if not Success then
255 pragma Debug (Debug.Put_Line
256 ("ERROR: PCODE didn't acknowledge frequency change."));
257 return;
258 end if;
Nico Huber40820442017-01-20 14:00:53 +0100259
260 Write
261 (Register => BXT_DE_PLL_ENABLE,
262 Value => 16#0000_0000#);
263 Wait_Unset_Mask
264 (Register => BXT_DE_PLL_ENABLE,
265 Mask => BXT_DE_PLL_PLL_LOCK,
266 TOut_MS => 1); -- 200us
267
268 Unset_And_Set_Mask
269 (Register => BXT_DE_PLL_CTL,
270 Mask_Unset => BXT_DE_PLL_RATIO_MASK,
271 Mask_Set => Word32 (VCO / CDClk_Ref));
272 Write
273 (Register => BXT_DE_PLL_ENABLE,
274 Value => BXT_DE_PLL_PLL_ENABLE);
275 Wait_Set_Mask
276 (Register => BXT_DE_PLL_ENABLE,
277 Mask => BXT_DE_PLL_PLL_LOCK,
278 TOut_MS => 1); -- 200us
279
280 Write
281 (Register => CDCLK_CTL,
282 Value => CDCLK_CD2X_Div_Sel or
283 CDCLK_CD2X_PIPE_NONE or
284 CDCLK_CD2X_SSA_Precharge or
285 CDCLK_CTL_CD_FREQ_DECIMAL (Freq));
286
Nico Huber312433c2019-09-28 03:15:48 +0200287 PCode.Mailbox_Write
288 (MBox => BXT_PCODE_CDCLK_CONTROL,
289 Command => Word64 ((Freq + (25_000_000 - 1)) / 25_000_000));
Nico Huber40820442017-01-20 14:00:53 +0100290 end Set_CDClk;
291
292 ----------------------------------------------------------------------------
293
294 procedure Pre_All_Off is
295 begin
296 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
297 Power_And_Clocks_Haswell.PSR_Off;
298 end Pre_All_Off;
299
300 procedure Post_All_Off is
301 begin
302 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
303
304 for PD in reverse Dynamic_Domain loop
305 PD_Off (PD);
306 end loop;
307
308 Unset_Mask (DBUF_CTL, DBUF_CTL_DBUF_POWER_REQUEST);
309 Wait_Unset_Mask (DBUF_CTL, DBUF_CTL_DBUF_POWER_STATE);
310
311 -- Linux' i915 never keeps the PLL disabled but runs it
312 -- at a "ratio" of 0 with CDClk at its reference clock.
313 Set_CDClk (CDClk_Ref);
314
315 PW_Off (PW1);
316 end Post_All_Off;
317
318 procedure Initialize is
319 begin
320 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
321
322 -- no PCH for Broxton
323 Unset_Mask (NDE_RSTWRN_OPT, NDE_RSTWRN_OPT_RST_PCH_Handshake_En);
324
325 Wait_Set_Mask (FUSE_STATUS, FUSE_STATUS_PG0_DIST_STATUS);
326 PW_On (PW1);
327
Nico Huber565f33b2018-06-04 14:42:13 +0200328 Set_CDClk (Config.Default_CDClk_Freq);
Nico Huber40820442017-01-20 14:00:53 +0100329
330 Set_Mask (DBUF_CTL, DBUF_CTL_DBUF_POWER_REQUEST);
331 Wait_Set_Mask (DBUF_CTL, DBUF_CTL_DBUF_POWER_STATE);
Arthur Heymansd1988d12018-03-28 16:27:57 +0200332
333 Config.Raw_Clock := Config.Default_RawClk_Freq;
Nico Huber40820442017-01-20 14:00:53 +0100334 end Initialize;
335
336end HW.GFX.GMA.Power_And_Clocks;