blob: 2ab7f21ab1729f6fd61b0ff1f484db556b80140a [file] [log] [blame]
Nico Huber83693c82016-10-08 22:17:55 +02001--
2-- Copyright (C) 2014-2016 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
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
37 type Power_Domain_Values is array (Power_Domain) of Word32;
38 PWR_WELL_CTL_POWER_REQUEST : constant Power_Domain_Values :=
39 (MISC_IO => 1 * 2 ** 1,
40 DDI_AE => 1 * 2 ** 3,
41 DDI_B => 1 * 2 ** 5,
42 DDI_C => 1 * 2 ** 7,
43 DDI_D => 1 * 2 ** 9,
44 PW1 => 1 * 2 ** 29,
45 PW2 => 1 * 2 ** 31);
46 PWR_WELL_CTL_POWER_STATE : constant Power_Domain_Values :=
47 (MISC_IO => 1 * 2 ** 0,
48 DDI_AE => 1 * 2 ** 2,
49 DDI_B => 1 * 2 ** 4,
50 DDI_C => 1 * 2 ** 6,
51 DDI_D => 1 * 2 ** 8,
52 PW1 => 1 * 2 ** 28,
53 PW2 => 1 * 2 ** 30);
54
55 type Power_Well_Values is array (Power_Well) of Word32;
56 FUSE_STATUS_PGx_DIST_STATUS : constant Power_Well_Values :=
57 (PW1 => 1 * 2 ** 26,
58 PW2 => 1 * 2 ** 25);
59
60 DBUF_CTL_DBUF_POWER_REQUEST : constant := 1 * 2 ** 31;
61 DBUF_CTL_DBUF_POWER_STATE : constant := 1 * 2 ** 30;
62
63 ----------------------------------------------------------------------------
64
65 DPLL_CTRL1_DPLL0_LINK_RATE_MASK : constant := 7 * 2 ** 1;
66 DPLL_CTRL1_DPLL0_LINK_RATE_2700MHZ : constant := 0 * 2 ** 1;
67 DPLL_CTRL1_DPLL0_LINK_RATE_1350MHZ : constant := 1 * 2 ** 1;
68 DPLL_CTRL1_DPLL0_LINK_RATE_810MHZ : constant := 2 * 2 ** 1;
69 DPLL_CTRL1_DPLL0_LINK_RATE_1620MHZ : constant := 3 * 2 ** 1;
70 DPLL_CTRL1_DPLL0_LINK_RATE_1080MHZ : constant := 4 * 2 ** 1;
71 DPLL_CTRL1_DPLL0_LINK_RATE_2160MHZ : constant := 5 * 2 ** 1;
72 DPLL_CTRL1_DPLL0_OVERRIDE : constant := 1 * 2 ** 0;
73
74 LCPLL1_CTL_PLL_ENABLE : constant := 1 * 2 ** 31;
75 LCPLL1_CTL_PLL_LOCK : constant := 1 * 2 ** 30;
76
77 ----------------------------------------------------------------------------
78
79 CDCLK_CTL_CD_FREQ_SELECT_MASK : constant := 3 * 2 ** 26;
80 CDCLK_CTL_CD_FREQ_SELECT_450MHZ : constant := 0 * 2 ** 26;
81 CDCLK_CTL_CD_FREQ_SELECT_540MHZ : constant := 1 * 2 ** 26;
82 CDCLK_CTL_CD_FREQ_SELECT_337_5MHZ : constant := 2 * 2 ** 26;
83 CDCLK_CTL_CD_FREQ_SELECT_675MHZ : constant := 3 * 2 ** 26;
84 CDCLK_CTL_CD_FREQ_DECIMAL_MASK : constant := 16#7ff#;
85
86 SKL_PCODE_CDCLK_CONTROL : constant := 7;
87 SKL_CDCLK_PREPARE_FOR_CHANGE : constant := 3;
88 SKL_CDCLK_READY_FOR_CHANGE : constant := 1;
89
Nico Huber83693c82016-10-08 22:17:55 +020090 function CDCLK_CTL_CD_FREQ_DECIMAL
Nico Huberdb684412018-06-04 22:44:58 +020091 (Freq : Pos16;
Nico Huber83693c82016-10-08 22:17:55 +020092 Plus_Half : Boolean)
93 return Word32 is
94 begin
Nico Huberdb684412018-06-04 22:44:58 +020095 return Word32 (2 * (Pos32 (Freq) - 1)) or (if Plus_Half then 1 else 0);
Nico Huber83693c82016-10-08 22:17:55 +020096 end CDCLK_CTL_CD_FREQ_DECIMAL;
97
98 ----------------------------------------------------------------------------
99
Nico Huber83693c82016-10-08 22:17:55 +0200100 procedure PD_Off (PD : Power_Domain)
101 is
102 Ctl1, Ctl2, Ctl3, Ctl4 : Word32;
103 begin
104 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
105
106 Registers.Read (Registers.PWR_WELL_CTL_BIOS, Ctl1);
107 Registers.Read (Registers.PWR_WELL_CTL_DRIVER, Ctl2);
108 Registers.Read (Registers.PWR_WELL_CTL_KVMR, Ctl3);
109 Registers.Read (Registers.PWR_WELL_CTL_DEBUG, Ctl4);
110 pragma Debug (Registers.Posting_Read (Registers.PWR_WELL_CTL5)); -- Result for debugging only
111 pragma Debug (Registers.Posting_Read (Registers.PWR_WELL_CTL6)); -- Result for debugging only
112
113 if ((Ctl1 or Ctl2 or Ctl3 or Ctl4) and
114 PWR_WELL_CTL_POWER_REQUEST (PD)) /= 0
115 then
116 Registers.Wait_Set_Mask
117 (Register => Registers.PWR_WELL_CTL_DRIVER,
118 Mask => PWR_WELL_CTL_POWER_STATE (PD));
119 end if;
120
121 if (Ctl1 and PWR_WELL_CTL_POWER_REQUEST (PD)) /= 0 then
122 Registers.Unset_Mask
123 (Register => Registers.PWR_WELL_CTL_BIOS,
124 Mask => PWR_WELL_CTL_POWER_REQUEST (PD));
125 end if;
126
127 if (Ctl2 and PWR_WELL_CTL_POWER_REQUEST (PD)) /= 0 then
128 Registers.Unset_Mask
129 (Register => Registers.PWR_WELL_CTL_DRIVER,
130 Mask => PWR_WELL_CTL_POWER_REQUEST (PD));
131 end if;
132 end PD_Off;
133
134 procedure PD_On (PD : Power_Domain)
135 with
136 Pre => True
137 is
138 Ctl1, Ctl2, Ctl3, Ctl4 : Word32;
139 begin
140 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
141
142 Registers.Read (Registers.PWR_WELL_CTL_BIOS, Ctl1);
143 Registers.Read (Registers.PWR_WELL_CTL_DRIVER, Ctl2);
144 Registers.Read (Registers.PWR_WELL_CTL_KVMR, Ctl3);
145 Registers.Read (Registers.PWR_WELL_CTL_DEBUG, Ctl4);
146 pragma Debug (Registers.Posting_Read (Registers.PWR_WELL_CTL5)); -- Result for debugging only
147 pragma Debug (Registers.Posting_Read (Registers.PWR_WELL_CTL6)); -- Result for debugging only
148
149 if ((Ctl1 or Ctl2 or Ctl3 or Ctl4) and
150 PWR_WELL_CTL_POWER_REQUEST (PD)) = 0
151 then
152 Registers.Wait_Unset_Mask
153 (Register => Registers.PWR_WELL_CTL_DRIVER,
154 Mask => PWR_WELL_CTL_POWER_STATE (PD));
155 end if;
156
157 if (Ctl2 and PWR_WELL_CTL_POWER_REQUEST (PD)) = 0 then
158 Registers.Set_Mask
159 (Register => Registers.PWR_WELL_CTL_DRIVER,
160 Mask => PWR_WELL_CTL_POWER_REQUEST (PD));
161 Registers.Wait_Set_Mask
162 (Register => Registers.PWR_WELL_CTL_DRIVER,
163 Mask => PWR_WELL_CTL_POWER_STATE (PD));
164
165 if PD in Power_Well then
166 Registers.Wait_Set_Mask
167 (Register => Registers.FUSE_STATUS,
168 Mask => FUSE_STATUS_PGx_DIST_STATUS (PD));
169 end if;
170 end if;
171 end PD_On;
172
Nico Huber99f10f32016-11-20 00:34:05 +0100173 function Need_PD (PD : Dynamic_Domain; Configs : Pipe_Configs) return Boolean
Nico Huber83693c82016-10-08 22:17:55 +0200174 is
175 begin
176 return (case PD is
177 when DDI_AE => Configs (Primary).Port = Internal or
178 Configs (Secondary).Port = Internal or
179 Configs (Tertiary).Port = Internal,
Nico Huber0d454cd2016-11-21 13:33:43 +0100180 when DDI_B => Configs (Primary).Port = HDMI1 or
Nico Huber83693c82016-10-08 22:17:55 +0200181 Configs (Primary).Port = DP1 or
Nico Huber0d454cd2016-11-21 13:33:43 +0100182 Configs (Secondary).Port = HDMI1 or
Nico Huber83693c82016-10-08 22:17:55 +0200183 Configs (Secondary).Port = DP1 or
Nico Huber0d454cd2016-11-21 13:33:43 +0100184 Configs (Tertiary).Port = HDMI1 or
Nico Huber83693c82016-10-08 22:17:55 +0200185 Configs (Tertiary).Port = DP1,
Nico Huber0d454cd2016-11-21 13:33:43 +0100186 when DDI_C => Configs (Primary).Port = HDMI2 or
Nico Huber83693c82016-10-08 22:17:55 +0200187 Configs (Primary).Port = DP2 or
Nico Huber0d454cd2016-11-21 13:33:43 +0100188 Configs (Secondary).Port = HDMI2 or
Nico Huber83693c82016-10-08 22:17:55 +0200189 Configs (Secondary).Port = DP2 or
Nico Huber0d454cd2016-11-21 13:33:43 +0100190 Configs (Tertiary).Port = HDMI2 or
Nico Huber83693c82016-10-08 22:17:55 +0200191 Configs (Tertiary).Port = DP2,
Nico Huber0d454cd2016-11-21 13:33:43 +0100192 when DDI_D => Configs (Primary).Port = HDMI3 or
Nico Huber83693c82016-10-08 22:17:55 +0200193 Configs (Primary).Port = DP3 or
Nico Huber0d454cd2016-11-21 13:33:43 +0100194 Configs (Secondary).Port = HDMI3 or
Nico Huber83693c82016-10-08 22:17:55 +0200195 Configs (Secondary).Port = DP3 or
Nico Huber0d454cd2016-11-21 13:33:43 +0100196 Configs (Tertiary).Port = HDMI3 or
Nico Huber83693c82016-10-08 22:17:55 +0200197 Configs (Tertiary).Port = DP3,
198 when PW2 => (Configs (Primary).Port /= Disabled and
199 Configs (Primary).Port /= Internal) or
200 Configs (Secondary).Port /= Disabled or
201 Configs (Tertiary).Port /= Disabled);
202 end Need_PD;
203
204 ----------------------------------------------------------------------------
205
206 procedure Pre_All_Off is
207 begin
208 Power_And_Clocks_Haswell.PSR_Off;
209 end Pre_All_Off;
210
211 procedure Post_All_Off is
212 begin
213 for PD in reverse Dynamic_Domain loop
214 PD_Off (PD);
215 end loop;
216
217 Registers.Unset_Mask
218 (Register => Registers.DBUF_CTL,
219 Mask => DBUF_CTL_DBUF_POWER_REQUEST);
220 Registers.Wait_Unset_Mask
221 (Register => Registers.DBUF_CTL,
222 Mask => DBUF_CTL_DBUF_POWER_STATE);
223
224 Registers.Unset_Mask
225 (Register => Registers.LCPLL1_CTL,
226 Mask => LCPLL1_CTL_PLL_ENABLE);
227 Registers.Wait_Unset_Mask
228 (Register => Registers.LCPLL1_CTL,
229 Mask => LCPLL1_CTL_PLL_LOCK);
230
231 PD_Off (MISC_IO);
232 PD_Off (PW1);
233 end Post_All_Off;
234
235 procedure Initialize
236 is
Nico Huber312433c2019-09-28 03:15:48 +0200237 Success : Boolean;
Nico Huber83693c82016-10-08 22:17:55 +0200238 begin
239 Registers.Set_Mask
240 (Register => Registers.NDE_RSTWRN_OPT,
241 Mask => NDE_RSTWRN_OPT_RST_PCH_Handshake_En);
242
243 Registers.Wait_Set_Mask
244 (Register => Registers.FUSE_STATUS,
245 Mask => FUSE_STATUS_PG0_DIST_STATUS);
246 PD_On (PW1);
247 PD_On (MISC_IO);
248
249 Registers.Write
250 (Register => Registers.CDCLK_CTL,
251 Value => CDCLK_CTL_CD_FREQ_SELECT_337_5MHZ or
252 CDCLK_CTL_CD_FREQ_DECIMAL (337, True));
253 -- TODO: Set to preferred eDP rate:
254 -- Registers.Unset_And_Set_Mask
255 -- (Register => Registers.DPLL_CTRL1,
256 -- Unset_Mask => DPLL_CTRL1_DPLL0_LINK_RATE_MASK,
257 -- Set_Mask => DPLL_CTRL1_DPLL0_LINK_RATE_...);
258 Registers.Set_Mask
259 (Register => Registers.LCPLL1_CTL,
260 Mask => LCPLL1_CTL_PLL_ENABLE);
261 Registers.Wait_Set_Mask
262 (Register => Registers.LCPLL1_CTL,
263 Mask => LCPLL1_CTL_PLL_LOCK);
264
Nico Huber312433c2019-09-28 03:15:48 +0200265 PCode.Mailbox_Request
266 (MBox => SKL_PCODE_CDCLK_CONTROL,
267 Command => SKL_CDCLK_PREPARE_FOR_CHANGE,
268 Reply_Mask => SKL_CDCLK_READY_FOR_CHANGE,
269 Wait_Ready => True,
270 Success => Success);
Nico Huber85408052017-07-19 14:14:34 +0200271
Nico Huber312433c2019-09-28 03:15:48 +0200272 pragma Debug (not Success, Debug.Put_Line
273 ("ERROR: PCODE not ready for frequency change."));
Nico Huber83693c82016-10-08 22:17:55 +0200274
Nico Huber312433c2019-09-28 03:15:48 +0200275 if Success then
276 PCode.Mailbox_Write
Nico Huber83693c82016-10-08 22:17:55 +0200277 (MBox => SKL_PCODE_CDCLK_CONTROL,
Nico Huber312433c2019-09-28 03:15:48 +0200278 Command => 16#0000_0000#); -- 0 - 337.5MHz
Nico Huber83693c82016-10-08 22:17:55 +0200279 -- 1 - 450.0MHz
280 -- 2 - 540.0MHz
281 -- 3 - 675.0MHz
282 Registers.Set_Mask
283 (Register => Registers.DBUF_CTL,
284 Mask => DBUF_CTL_DBUF_POWER_REQUEST);
285 Registers.Wait_Set_Mask
286 (Register => Registers.DBUF_CTL,
287 Mask => DBUF_CTL_DBUF_POWER_STATE);
288 end if;
Arthur Heymansd1988d12018-03-28 16:27:57 +0200289
290 Config.Raw_Clock := Config.Default_RawClk_Freq;
Nico Huber83693c82016-10-08 22:17:55 +0200291 end Initialize;
292
Nico Huber99f10f32016-11-20 00:34:05 +0100293 procedure Power_Set_To (Configs : Pipe_Configs) is
Nico Huber83693c82016-10-08 22:17:55 +0200294 begin
295 for PD in reverse Dynamic_Domain loop
296 if not Need_PD (PD, Configs) then
297 PD_Off (PD);
298 end if;
299 end loop;
300 for PD in Dynamic_Domain loop
301 if Need_PD (PD, Configs) then
302 PD_On (PD);
303 end if;
304 end loop;
305 end Power_Set_To;
306
Nico Huber99f10f32016-11-20 00:34:05 +0100307 procedure Power_Up (Old_Configs, New_Configs : Pipe_Configs) is
Nico Huber83693c82016-10-08 22:17:55 +0200308 begin
309 for PD in Dynamic_Domain loop
310 if not Need_PD (PD, Old_Configs) and Need_PD (PD, New_Configs) then
311 PD_On (PD);
312 end if;
313 end loop;
314 end Power_Up;
315
Nico Huber99f10f32016-11-20 00:34:05 +0100316 procedure Power_Down (Old_Configs, Tmp_Configs, New_Configs : Pipe_Configs)
Nico Huber83693c82016-10-08 22:17:55 +0200317 is
318 begin
319 for PD in reverse Dynamic_Domain loop
320 if (Need_PD (PD, Old_Configs) or Need_PD (PD, Tmp_Configs)) and
321 not Need_PD (PD, New_Configs)
322 then
323 PD_Off (PD);
324 end if;
325 end loop;
326 end Power_Down;
327
328end HW.GFX.GMA.Power_And_Clocks_Skylake;