blob: f6576d95e056c551d2bee87cc1143ed987a7b79a [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;
20with HW.GFX.GMA.Power_And_Clocks_Haswell;
21with HW.GFX.GMA.DDI_Phy;
22
23use HW.GFX.GMA.Registers;
24
25package body HW.GFX.GMA.Power_And_Clocks is
26
27 type Power_Domain is (PW1, PW2, DDI_A, DDI_BC);
28 subtype Power_Well is Power_Domain range PW1 .. PW2;
29 subtype Dynamic_Domain is Power_Domain range PW2 .. DDI_BC;
30 subtype DDI_Domain is Dynamic_Domain range DDI_A .. DDI_BC;
31
32 type DDI_Phy_Array is array (DDI_Domain) of DDI_Phy.T;
33 Phy : constant DDI_Phy_Array := (DDI_A => DDI_Phy.A, DDI_BC => DDI_Phy.BC);
34
35 NDE_RSTWRN_OPT_RST_PCH_Handshake_En : constant := 1 * 2 ** 4;
36
37 FUSE_STATUS_DOWNLOAD_STATUS : constant := 1 * 2 ** 31;
38 FUSE_STATUS_PG0_DIST_STATUS : constant := 1 * 2 ** 27;
39
40 type Power_Well_Values is array (Power_Well) of Word32;
41 PWR_WELL_CTL_POWER_REQUEST : constant Power_Well_Values :=
42 (PW1 => 1 * 2 ** 29,
43 PW2 => 1 * 2 ** 31);
44 PWR_WELL_CTL_POWER_STATE : constant Power_Well_Values :=
45 (PW1 => 1 * 2 ** 28,
46 PW2 => 1 * 2 ** 30);
47
48 FUSE_STATUS_PGx_DIST_STATUS : constant Power_Well_Values :=
49 (PW1 => 1 * 2 ** 26,
50 PW2 => 1 * 2 ** 25);
51
52 DBUF_CTL_DBUF_POWER_REQUEST : constant := 1 * 2 ** 31;
53 DBUF_CTL_DBUF_POWER_STATE : constant := 1 * 2 ** 30;
54
55 ----------------------------------------------------------------------------
56
57 BXT_DE_PLL_RATIO_MASK : constant := 16#ff#;
58 BXT_DE_PLL_PLL_ENABLE : constant := 1 * 2 ** 31;
59 BXT_DE_PLL_PLL_LOCK : constant := 1 * 2 ** 30;
60
61 CDCLK_CD2X_DIV_SEL_MASK : constant := 3 * 2 ** 22;
62 CDCLK_CD2X_DIV_SEL_1 : constant := 0 * 2 ** 22;
63 CDCLK_CD2X_DIV_SEL_1_5 : constant := 1 * 2 ** 22;
64 CDCLK_CD2X_DIV_SEL_2 : constant := 2 * 2 ** 22;
65 CDCLK_CD2X_DIV_SEL_4 : constant := 3 * 2 ** 22;
66 CDCLK_CD2X_PIPE_NONE : constant := 3 * 2 ** 20;
67 CDCLK_CD2X_SSA_PRECHARGE_ENABLE : constant := 1 * 2 ** 16;
68 CDCLK_CTL_CD_FREQ_DECIMAL_MASK : constant := 16#7ff#;
69
Nico Huber565f33b2018-06-04 14:42:13 +020070 function CDCLK_CTL_CD_FREQ_DECIMAL (Freq : Frequency_Type) return Word32 is
Nico Huber40820442017-01-20 14:00:53 +010071 begin
72 return Word32 (2 * (Freq / 1_000_000 - 1));
73 end CDCLK_CTL_CD_FREQ_DECIMAL;
74
75 BXT_PCODE_CDCLK_CONTROL : constant := 16#17#;
76 BXT_CDCLK_PREPARE_FOR_CHANGE : constant := 16#8000_0000#;
77
78 ----------------------------------------------------------------------------
79
80 procedure PW_Off (PD : Power_Well)
81 is
82 Ctl1, Ctl2, Ctl3, Ctl4 : Word32;
83 begin
84 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
85
86 Read (PWR_WELL_CTL_BIOS, Ctl1);
87 Read (PWR_WELL_CTL_DRIVER, Ctl2);
88 Read (PWR_WELL_CTL_KVMR, Ctl3);
89 Read (PWR_WELL_CTL_DEBUG, Ctl4);
90 pragma Debug (Posting_Read (PWR_WELL_CTL5)); -- Result for debugging only
91 pragma Debug (Posting_Read (PWR_WELL_CTL6)); -- Result for debugging only
92
93 if ((Ctl1 or Ctl2 or Ctl3 or Ctl4) and
94 PWR_WELL_CTL_POWER_REQUEST (PD)) /= 0
95 then
96 Wait_Set_Mask (PWR_WELL_CTL_DRIVER, PWR_WELL_CTL_POWER_STATE (PD));
97 end if;
98
99 if (Ctl1 and PWR_WELL_CTL_POWER_REQUEST (PD)) /= 0 then
100 Unset_Mask (PWR_WELL_CTL_BIOS, PWR_WELL_CTL_POWER_REQUEST (PD));
101 end if;
102
103 if (Ctl2 and PWR_WELL_CTL_POWER_REQUEST (PD)) /= 0 then
104 Unset_Mask (PWR_WELL_CTL_DRIVER, PWR_WELL_CTL_POWER_REQUEST (PD));
105 end if;
106 end PW_Off;
107
108 procedure PW_On (PD : Power_Well)
109 with
110 Pre => True
111 is
112 Ctl1, Ctl2, Ctl3, Ctl4 : Word32;
113 begin
114 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
115
116 Read (PWR_WELL_CTL_BIOS, Ctl1);
117 Read (PWR_WELL_CTL_DRIVER, Ctl2);
118 Read (PWR_WELL_CTL_KVMR, Ctl3);
119 Read (PWR_WELL_CTL_DEBUG, Ctl4);
120 pragma Debug (Posting_Read (PWR_WELL_CTL5)); -- Result for debugging only
121 pragma Debug (Posting_Read (PWR_WELL_CTL6)); -- Result for debugging only
122
123 if ((Ctl1 or Ctl2 or Ctl3 or Ctl4) and
124 PWR_WELL_CTL_POWER_REQUEST (PD)) = 0
125 then
126 Wait_Unset_Mask (PWR_WELL_CTL_DRIVER, PWR_WELL_CTL_POWER_STATE (PD));
127 end if;
128
129 if (Ctl2 and PWR_WELL_CTL_POWER_REQUEST (PD)) = 0 then
130 Set_Mask (PWR_WELL_CTL_DRIVER, PWR_WELL_CTL_POWER_REQUEST (PD));
131 Wait_Set_Mask (PWR_WELL_CTL_DRIVER, PWR_WELL_CTL_POWER_STATE (PD));
132
133 Wait_Set_Mask (FUSE_STATUS, FUSE_STATUS_PGx_DIST_STATUS (PD));
134 end if;
135 end PW_On;
136
137 procedure PD_On (PD : Power_Domain) is
138 begin
139 if PD in Power_Well then
140 PW_On (PD);
141 else
142 DDI_Phy.Power_On (Phy (PD));
143 end if;
144 end PD_On;
145
146 procedure PD_Off (PD : Power_Domain) is
147 begin
148 if PD in Power_Well then
149 PW_Off (PD);
150 else
151 DDI_Phy.Power_Off (Phy (PD));
152 end if;
153 end PD_Off;
154
155 function Need_PD (PD : Dynamic_Domain; Configs : Pipe_Configs) return Boolean
156 is
157 begin
158 return (case PD is
159 when DDI_A => Configs (Primary).Port = Internal or
160 Configs (Secondary).Port = Internal or
161 Configs (Tertiary).Port = Internal,
162 when DDI_BC => Configs (Primary).Port = HDMI1 or
163 Configs (Primary).Port = DP1 or
164 Configs (Secondary).Port = HDMI1 or
165 Configs (Secondary).Port = DP1 or
166 Configs (Tertiary).Port = HDMI1 or
167 Configs (Tertiary).Port = DP1 or
168 Configs (Primary).Port = HDMI2 or
169 Configs (Primary).Port = DP2 or
170 Configs (Secondary).Port = HDMI2 or
171 Configs (Secondary).Port = DP2 or
172 Configs (Tertiary).Port = HDMI2 or
173 Configs (Tertiary).Port = DP2,
174 when PW2 => (Configs (Primary).Port /= Disabled and
175 Configs (Primary).Port /= Internal) or
176 Configs (Secondary).Port /= Disabled or
177 Configs (Tertiary).Port /= Disabled);
178 end Need_PD;
179
180 procedure Power_Set_To (Configs : Pipe_Configs) is
181 begin
182 for PD in reverse Dynamic_Domain loop
183 if not Need_PD (PD, Configs) then
184 PD_Off (PD);
185 end if;
186 end loop;
187 for PD in Dynamic_Domain loop
188 if Need_PD (PD, Configs) then
189 PD_On (PD);
190 end if;
191 end loop;
192 end Power_Set_To;
193
194 procedure Power_Up (Old_Configs, New_Configs : Pipe_Configs) is
195 begin
196 for PD in Dynamic_Domain loop
197 if not Need_PD (PD, Old_Configs) and Need_PD (PD, New_Configs) then
198 PD_On (PD);
199 end if;
200 end loop;
201 end Power_Up;
202
203 procedure Power_Down (Old_Configs, Tmp_Configs, New_Configs : Pipe_Configs)
204 is
205 begin
206 for PD in reverse Dynamic_Domain loop
207 if (Need_PD (PD, Old_Configs) or Need_PD (PD, Tmp_Configs)) and
208 not Need_PD (PD, New_Configs)
209 then
210 PD_Off (PD);
211 end if;
212 end loop;
213 end Power_Down;
214
215 ----------------------------------------------------------------------------
216
217 CDClk_Ref : constant := 19_200_000;
218
Nico Huber1ee57142018-06-10 12:42:46 +0200219 procedure Set_CDClk (Freq_In : Frequency_Type)
Nico Huber40820442017-01-20 14:00:53 +0100220 is
Nico Huber1ee57142018-06-10 12:42:46 +0200221 Freq : constant Frequency_Type :=
222 (if Freq_In = CDClk_Ref then CDClk_Ref
223 elsif Freq_In <= 144_000_000 then 144_000_000
224 elsif Freq_In <= 288_000_000 then 288_000_000
225 elsif Freq_In <= 384_000_000 then 384_000_000
226 elsif Freq_In <= 576_000_000 then 576_000_000
227 else 624_000_000);
Nico Huber565f33b2018-06-04 14:42:13 +0200228 VCO : constant Int64 :=
Nico Huber40820442017-01-20 14:00:53 +0100229 CDClk_Ref *
230 (if Freq = CDClk_Ref then
231 0
232 elsif Freq = 624_000_000 then
233 65
234 else
235 60);
236 CDCLK_CD2X_Div_Sel : constant Word32 :=
237 (case VCO / Freq is -- CDClk = VCO / 2 / Div
238 when 2 => CDCLK_CD2X_DIV_SEL_1,
239 when 3 => CDCLK_CD2X_DIV_SEL_1_5,
240 when 4 => CDCLK_CD2X_DIV_SEL_2,
241 when 8 => CDCLK_CD2X_DIV_SEL_4,
242 when others => CDCLK_CD2X_DIV_SEL_1); -- for CDClk = CDClk_Ref
243 CDCLK_CD2X_SSA_Precharge : constant Word32 :=
244 (if Freq >= 500_000_000 then CDCLK_CD2X_SSA_PRECHARGE_ENABLE else 0);
245 begin
246 Power_And_Clocks_Haswell.GT_Mailbox_Write
247 (MBox => BXT_PCODE_CDCLK_CONTROL,
248 Value => BXT_CDCLK_PREPARE_FOR_CHANGE);
249
250 Write
251 (Register => BXT_DE_PLL_ENABLE,
252 Value => 16#0000_0000#);
253 Wait_Unset_Mask
254 (Register => BXT_DE_PLL_ENABLE,
255 Mask => BXT_DE_PLL_PLL_LOCK,
256 TOut_MS => 1); -- 200us
257
258 Unset_And_Set_Mask
259 (Register => BXT_DE_PLL_CTL,
260 Mask_Unset => BXT_DE_PLL_RATIO_MASK,
261 Mask_Set => Word32 (VCO / CDClk_Ref));
262 Write
263 (Register => BXT_DE_PLL_ENABLE,
264 Value => BXT_DE_PLL_PLL_ENABLE);
265 Wait_Set_Mask
266 (Register => BXT_DE_PLL_ENABLE,
267 Mask => BXT_DE_PLL_PLL_LOCK,
268 TOut_MS => 1); -- 200us
269
270 Write
271 (Register => CDCLK_CTL,
272 Value => CDCLK_CD2X_Div_Sel or
273 CDCLK_CD2X_PIPE_NONE or
274 CDCLK_CD2X_SSA_Precharge or
275 CDCLK_CTL_CD_FREQ_DECIMAL (Freq));
276
277 Power_And_Clocks_Haswell.GT_Mailbox_Write
278 (MBox => BXT_PCODE_CDCLK_CONTROL,
279 Value => Word32 ((Freq + (25_000_000 - 1)) / 25_000_000));
280 end Set_CDClk;
281
282 ----------------------------------------------------------------------------
283
284 procedure Pre_All_Off is
285 begin
286 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
287 Power_And_Clocks_Haswell.PSR_Off;
288 end Pre_All_Off;
289
290 procedure Post_All_Off is
291 begin
292 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
293
294 for PD in reverse Dynamic_Domain loop
295 PD_Off (PD);
296 end loop;
297
298 Unset_Mask (DBUF_CTL, DBUF_CTL_DBUF_POWER_REQUEST);
299 Wait_Unset_Mask (DBUF_CTL, DBUF_CTL_DBUF_POWER_STATE);
300
301 -- Linux' i915 never keeps the PLL disabled but runs it
302 -- at a "ratio" of 0 with CDClk at its reference clock.
303 Set_CDClk (CDClk_Ref);
304
305 PW_Off (PW1);
306 end Post_All_Off;
307
308 procedure Initialize is
309 begin
310 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
311
312 -- no PCH for Broxton
313 Unset_Mask (NDE_RSTWRN_OPT, NDE_RSTWRN_OPT_RST_PCH_Handshake_En);
314
315 Wait_Set_Mask (FUSE_STATUS, FUSE_STATUS_PG0_DIST_STATUS);
316 PW_On (PW1);
317
Nico Huber565f33b2018-06-04 14:42:13 +0200318 Set_CDClk (Config.Default_CDClk_Freq);
Nico Huber40820442017-01-20 14:00:53 +0100319
320 Set_Mask (DBUF_CTL, DBUF_CTL_DBUF_POWER_REQUEST);
321 Wait_Set_Mask (DBUF_CTL, DBUF_CTL_DBUF_POWER_STATE);
Arthur Heymansd1988d12018-03-28 16:27:57 +0200322
323 Config.Raw_Clock := Config.Default_RawClk_Freq;
Nico Huber40820442017-01-20 14:00:53 +0100324 end Initialize;
325
326end HW.GFX.GMA.Power_And_Clocks;