blob: 94ca2193d3877f5d15409f2eea6dba771ae8a691 [file] [log] [blame]
Nico Huber27d26272026-05-27 19:01:09 +02001--
2-- Copyright (C) 2022 Google, LLC
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;
16with HW.Debug;
17
18with HW.GFX.GMA.Config;
19with HW.GFX.GMA.Config_Helpers;
20with HW.GFX.GMA.Connectors.TC.Ownership;
21
22package body HW.GFX.GMA.Power_Domains_Common is
23
24 function DDI_Index (DDI : DDI_Domain) return Natural
25 is
26 (Power_Domain'Pos (DDI) - Power_Domain'Pos (DDI_Domain'First));
27
28 function AUX_Index (AUX : AUX_Domain) return Natural
29 is
30 (Power_Domain'Pos (AUX) - Power_Domain'Pos (AUX_Domain'First));
31
32 function AUX_USBC_Index (AUX: AUX_USBC_Domain) return Natural
33 is
34 (Power_Domain'Pos (AUX) - Power_Domain'Pos (AUX_USBC_Domain'First));
35
36 ----------------------------------------------------------------------------
37
38 FUSE_STATUS_PG0_DIST_STATUS : constant := 1 * 2 ** 27;
39
40 ----------------------------------------------------------------------------
41
42 type Domain_Types is (Power_Well, Power_DDI, Power_AUX);
43
44 function Domain_Type (PD : Power_Domain) return Domain_Types
45 is
46 (case PD is
47 when PW_Domain'Range => Power_Well,
48 when DDI_Domain'Range => Power_DDI,
49 when AUX_Domain'Range => Power_AUX);
50
51 type Power_Well_Regs is array (Domain_Types) of Registers.Registers_Index;
52 PWR_CTL_BIOS : constant Power_Well_Regs :=
53 (Power_Well => Registers.PWR_WELL_CTL_BIOS,
54 Power_DDI => Registers.PWR_DDI_CTL_BIOS,
55 Power_AUX => Registers.PWR_AUX_CTL_BIOS);
56 PWR_CTL_DRIVER : constant Power_Well_Regs :=
57 (Power_Well => Registers.PWR_WELL_CTL_DRIVER,
58 Power_DDI => Registers.PWR_DDI_CTL_DRIVER,
59 Power_AUX => Registers.PWR_AUX_CTL_DRIVER);
60
61 function Power_State_Mask (PD : Power_Domain) return Word32
62 is
63 (case PD is
64 when PW_Domain'Range => PW_State_Mask (PD),
65 when DDI_Domain'Range => 1 * 2 ** (2 * DDI_Index (PD)),
66 when AUX_Domain'Range => 1 * 2 ** (2 * AUX_Index (PD)));
67
68 function Power_Request_Mask (PD : Power_Domain) return Word32 is
69 begin
70 return Shift_Left (Power_State_Mask (PD), 1);
71 end Power_Request_Mask;
72
73 ----------------------------------------------------------------------------
74
75 function HIP_INDEX_REG (Aux : AUX_Domain) return Registers.Registers_Index
76 is
77 (if Aux <= AUX_USBC4
78 then Registers.HIP_INDEX_REG0
79 else Registers.HIP_INDEX_REG1);
80
81 function HIP_INDEX_VAL (Aux : AUX_USBC_Domain; Val : Word32) return Word32
82 is
83 (Val * 2 ** (8 * (AUX_USBC_Index (Aux) mod 4)));
84
85 DKL_CMN_UC_DW_27 : constant array (USBC_Port) of Registers.Registers_Index :=
86 (DDI_TC1 => Registers.DKL_CMN_UC_DW_27_1,
87 DDI_TC2 => Registers.DKL_CMN_UC_DW_27_2,
88 DDI_TC3 => Registers.DKL_CMN_UC_DW_27_3,
89 DDI_TC4 => Registers.DKL_CMN_UC_DW_27_4,
90 DDI_TC5 => Registers.DKL_CMN_UC_DW_27_5,
91 DDI_TC6 => Registers.DKL_CMN_UC_DW_27_6);
92
93 ----------------------------------------------------------------------------
94
95 AUX_CTL_Regs : constant array (USBC_Port) of Registers.Registers_Index :=
96 (DDI_TC1 => Registers.DDI_AUX_CTL_USBC1,
97 DDI_TC2 => Registers.DDI_AUX_CTL_USBC2,
98 DDI_TC3 => Registers.DDI_AUX_CTL_USBC3,
99 DDI_TC4 => Registers.DDI_AUX_CTL_USBC4,
100 DDI_TC5 => Registers.DDI_AUX_CTL_USBC5,
101 DDI_TC6 => Registers.DDI_AUX_CTL_USBC6);
102
103 ----------------------------------------------------------------------------
104
105 procedure Pre_PD_On (PD : in Power_Domain; Success : out Boolean)
106 is
107 DP_AUX_CH_CTL_TBT_IO : constant := 1 * 2 ** 11;
108 begin
109 if PD in AUX_USBC_Domain then
110 -- Disable TBT IO mode for AUX
111 Registers.Unset_Mask
112 (Register => AUX_CTL_Regs (To_GPU_Port (PD)),
113 Mask => DP_AUX_CH_CTL_TBT_IO);
114 Connectors.TC.Ownership.Claimed (To_GPU_Port (PD), Success);
115 elsif PD = PW1 then
Nico Huber7edb1a72026-05-27 21:10:10 +0200116 if Config.Gen_AlderlakeP then
117 Registers.Set_Mask
118 (Register => Registers.GEN8_CHICKEN_DCPR_1,
119 Mask => 1 * 2 ** 15); -- DISABLE_FLR_SRC
120 end if;
Nico Huber27d26272026-05-27 19:01:09 +0200121 Registers.Wait_Set_Mask
122 (Register => Registers.FUSE_STATUS,
123 Mask => FUSE_STATUS_PG0_DIST_STATUS,
124 Success => Success);
125 else
126 Success := True;
127 end if;
128 end Pre_PD_On;
129
130 procedure Post_PD_On (PD : Power_Domain)
131 is
132 DKL_CMN_UC_DW_27_UC_HEALTH : constant := 1 * 2 ** 15;
133 begin
134 if PD in PW_Domain then
135 Registers.Wait_Set_Mask
136 (Register => Registers.FUSE_STATUS,
137 Mask => FUSE_STATUS_PGx_DIST_STATUS (PD),
138 TOut_MS => 1);
139 elsif PD in AUX_USBC_Domain then
140 Registers.Write (HIP_INDEX_REG (PD), HIP_INDEX_VAL (PD, 2));
141 Registers.Wait_Set_Mask
142 (Register => DKL_CMN_UC_DW_27 (To_GPU_Port (PD)),
143 Mask => DKL_CMN_UC_DW_27_UC_HEALTH,
144 TOut_MS => 1);
145 end if;
146 end Post_PD_On;
147
148 procedure Pre_PD_Off (PD : Power_Domain) is
149 begin
150 if PD in DDI_USBC_Domain then
151 -- Could be moved to a higher level, but right now it's
152 -- convenient to do it here: When requested to turn the
153 -- power off, we know exactly that we don't want to use
154 -- the port (anymore).
155 Connectors.TC.Ownership.Disconnect (To_GPU_Port (PD));
156 end if;
157 end Pre_PD_Off;
158
159 procedure PD_On (PD : Power_Domain)
160 is
161 Ctl1, Ctl2 : Word32;
162 PD_Type : constant Domain_Types := Domain_Type (PD);
163 Success : Boolean;
164 begin
165 Registers.Read (PWR_CTL_BIOS (PD_Type), Ctl1);
166 Registers.Read (PWR_CTL_DRIVER (PD_Type), Ctl2);
167
168 if ((Ctl1 or Ctl2) and Power_Request_Mask (PD)) = 0 then
169 Registers.Wait_Unset_Mask
170 (Register => PWR_CTL_DRIVER (PD_Type),
171 Mask => Power_State_Mask (PD),
172 TOut_MS => 1);
173 end if;
174
175 if (Ctl2 and Power_Request_Mask (PD)) = 0 then
176 Pre_PD_On (PD, Success);
177 if not Success then
178 pragma Debug (Debug.Put_Line ("Connection flow failed!"));
179 return;
180 end if;
181
182 Registers.Set_Mask (PWR_CTL_DRIVER (PD_Type), Power_Request_Mask (PD));
183
184 Registers.Wait_Set_Mask
185 (Register => PWR_CTL_DRIVER (PD_Type),
186 Mask => Power_State_Mask (PD),
187 TOut_MS => 1,
188 Success => Success);
189 pragma Debug (not Success, Debug.Put_Line ("Failed to enable power domain!"));
190
Nico Huber7edb1a72026-05-27 21:10:10 +0200191 if Success or else
192 (Config.Gen_AlderlakeP and then PD in AUX_USBC_Domain)
193 then
194 -- FIXME: Is it really expected?
195 pragma Debug (not Success, Debug.Put_Line ("AUX timeout expected."));
Nico Huber27d26272026-05-27 19:01:09 +0200196 Post_PD_On (PD);
197 end if;
198 end if;
199 end PD_On;
200
201 procedure PD_Off (PD : Power_Domain)
202 is
203 Ctl1, Ctl2 : Word32;
204 PD_Type : constant Domain_Types := Domain_Type (PD);
205 begin
206 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
207
208 Registers.Read (PWR_CTL_BIOS (PD_Type), Ctl1);
209 Registers.Read (PWR_CTL_DRIVER (PD_Type), Ctl2);
210
211 if ((Ctl1 or Ctl2) and Power_Request_Mask (PD)) /= 0 then
212 Registers.Wait_Set_Mask
213 (Register => PWR_CTL_DRIVER (PD_Type),
214 Mask => Power_State_Mask (PD),
215 TOut_MS => 1);
216
217 Pre_PD_Off (PD);
218
219 Registers.Unset_Mask (PWR_CTL_DRIVER (PD_Type), Power_Request_Mask (PD));
220 Registers.Unset_Mask (PWR_CTL_BIOS (PD_Type), Power_Request_Mask (PD));
221 end if;
222 end PD_Off;
223
224 ----------------------------------------------------------------------------
225
226 function Need_PD (PD : Dynamic_Domain; Configs : Pipe_Configs) return Boolean
227 is
228 function Any_Port_Is (Port : GPU_Port) return Boolean is
229 (for some Pipe in Pipe_Index =>
230 Configs (Pipe).Port /= Disabled and then
231 Config_Helpers.To_GPU_Port (Pipe, Configs (Pipe).Port) = Port);
232 begin
233 return
234 (case PD is
235 when Dynamic_Well'Range => Need_PW (PD, Configs),
236 when Port_Domain'Range => Any_Port_Is (To_GPU_Port (PD)));
237 end Need_PD;
238
239 ----------------------------------------------------------------------------
240
241 procedure Power_Set_To (Configs : Pipe_Configs) is
242 begin
243 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
244
245 for PD in reverse Dynamic_Domain loop
246 if not Need_PD (PD, Configs) then
247 PD_Off (PD);
248 end if;
249 end loop;
250
251 for PD in Dynamic_Domain loop
252 if Need_PD (PD, Configs) then
253 PD_On (PD);
254 end if;
255 end loop;
256 end Power_Set_To;
257
258 procedure Power_Up (Old_Configs, New_Configs : Pipe_Configs) is
259 begin
260 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
261
262 -- Power wells only, Aux/DDI domains are enabled later on explicit request.
263 for PW in Dynamic_Well loop
264 if not Need_PW (PW, Old_Configs) and Need_PW (PW, New_Configs) then
265 PD_On (PW);
266 end if;
267 end loop;
268 end Power_Up;
269
270 procedure Power_Down (Old_Configs, Tmp_Configs, New_Configs : Pipe_Configs)
271 is
272 begin
273 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
274
275 for PD in reverse Dynamic_Domain loop
276 if (Need_PD (PD, Old_Configs) or Need_PD (PD, Tmp_Configs)) and
277 not Need_PD (PD, New_Configs)
278 then
279 PD_Off (PD);
280 end if;
281 end loop;
282 end Power_Down;
283
284 procedure All_Off is
285 begin
286 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
287
288 for PD in reverse Power_Domain loop
289 PD_Off (PD);
290 end loop;
291 end All_Off;
292
293end HW.GFX.GMA.Power_Domains_Common;