blob: 5c94c3f3cb6d7b0a5405bcb1512f37893c98e109 [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
116 Registers.Wait_Set_Mask
117 (Register => Registers.FUSE_STATUS,
118 Mask => FUSE_STATUS_PG0_DIST_STATUS,
119 Success => Success);
120 else
121 Success := True;
122 end if;
123 end Pre_PD_On;
124
125 procedure Post_PD_On (PD : Power_Domain)
126 is
127 DKL_CMN_UC_DW_27_UC_HEALTH : constant := 1 * 2 ** 15;
128 begin
129 if PD in PW_Domain then
130 Registers.Wait_Set_Mask
131 (Register => Registers.FUSE_STATUS,
132 Mask => FUSE_STATUS_PGx_DIST_STATUS (PD),
133 TOut_MS => 1);
134 elsif PD in AUX_USBC_Domain then
135 Registers.Write (HIP_INDEX_REG (PD), HIP_INDEX_VAL (PD, 2));
136 Registers.Wait_Set_Mask
137 (Register => DKL_CMN_UC_DW_27 (To_GPU_Port (PD)),
138 Mask => DKL_CMN_UC_DW_27_UC_HEALTH,
139 TOut_MS => 1);
140 end if;
141 end Post_PD_On;
142
143 procedure Pre_PD_Off (PD : Power_Domain) is
144 begin
145 if PD in DDI_USBC_Domain then
146 -- Could be moved to a higher level, but right now it's
147 -- convenient to do it here: When requested to turn the
148 -- power off, we know exactly that we don't want to use
149 -- the port (anymore).
150 Connectors.TC.Ownership.Disconnect (To_GPU_Port (PD));
151 end if;
152 end Pre_PD_Off;
153
154 procedure PD_On (PD : Power_Domain)
155 is
156 Ctl1, Ctl2 : Word32;
157 PD_Type : constant Domain_Types := Domain_Type (PD);
158 Success : Boolean;
159 begin
160 Registers.Read (PWR_CTL_BIOS (PD_Type), Ctl1);
161 Registers.Read (PWR_CTL_DRIVER (PD_Type), Ctl2);
162
163 if ((Ctl1 or Ctl2) and Power_Request_Mask (PD)) = 0 then
164 Registers.Wait_Unset_Mask
165 (Register => PWR_CTL_DRIVER (PD_Type),
166 Mask => Power_State_Mask (PD),
167 TOut_MS => 1);
168 end if;
169
170 if (Ctl2 and Power_Request_Mask (PD)) = 0 then
171 Pre_PD_On (PD, Success);
172 if not Success then
173 pragma Debug (Debug.Put_Line ("Connection flow failed!"));
174 return;
175 end if;
176
177 Registers.Set_Mask (PWR_CTL_DRIVER (PD_Type), Power_Request_Mask (PD));
178
179 Registers.Wait_Set_Mask
180 (Register => PWR_CTL_DRIVER (PD_Type),
181 Mask => Power_State_Mask (PD),
182 TOut_MS => 1,
183 Success => Success);
184 pragma Debug (not Success, Debug.Put_Line ("Failed to enable power domain!"));
185
186 if Success then
187 Post_PD_On (PD);
188 end if;
189 end if;
190 end PD_On;
191
192 procedure PD_Off (PD : Power_Domain)
193 is
194 Ctl1, Ctl2 : Word32;
195 PD_Type : constant Domain_Types := Domain_Type (PD);
196 begin
197 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
198
199 Registers.Read (PWR_CTL_BIOS (PD_Type), Ctl1);
200 Registers.Read (PWR_CTL_DRIVER (PD_Type), Ctl2);
201
202 if ((Ctl1 or Ctl2) and Power_Request_Mask (PD)) /= 0 then
203 Registers.Wait_Set_Mask
204 (Register => PWR_CTL_DRIVER (PD_Type),
205 Mask => Power_State_Mask (PD),
206 TOut_MS => 1);
207
208 Pre_PD_Off (PD);
209
210 Registers.Unset_Mask (PWR_CTL_DRIVER (PD_Type), Power_Request_Mask (PD));
211 Registers.Unset_Mask (PWR_CTL_BIOS (PD_Type), Power_Request_Mask (PD));
212 end if;
213 end PD_Off;
214
215 ----------------------------------------------------------------------------
216
217 function Need_PD (PD : Dynamic_Domain; Configs : Pipe_Configs) return Boolean
218 is
219 function Any_Port_Is (Port : GPU_Port) return Boolean is
220 (for some Pipe in Pipe_Index =>
221 Configs (Pipe).Port /= Disabled and then
222 Config_Helpers.To_GPU_Port (Pipe, Configs (Pipe).Port) = Port);
223 begin
224 return
225 (case PD is
226 when Dynamic_Well'Range => Need_PW (PD, Configs),
227 when Port_Domain'Range => Any_Port_Is (To_GPU_Port (PD)));
228 end Need_PD;
229
230 ----------------------------------------------------------------------------
231
232 procedure Power_Set_To (Configs : Pipe_Configs) is
233 begin
234 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
235
236 for PD in reverse Dynamic_Domain loop
237 if not Need_PD (PD, Configs) then
238 PD_Off (PD);
239 end if;
240 end loop;
241
242 for PD in Dynamic_Domain loop
243 if Need_PD (PD, Configs) then
244 PD_On (PD);
245 end if;
246 end loop;
247 end Power_Set_To;
248
249 procedure Power_Up (Old_Configs, New_Configs : Pipe_Configs) is
250 begin
251 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
252
253 -- Power wells only, Aux/DDI domains are enabled later on explicit request.
254 for PW in Dynamic_Well loop
255 if not Need_PW (PW, Old_Configs) and Need_PW (PW, New_Configs) then
256 PD_On (PW);
257 end if;
258 end loop;
259 end Power_Up;
260
261 procedure Power_Down (Old_Configs, Tmp_Configs, New_Configs : Pipe_Configs)
262 is
263 begin
264 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
265
266 for PD in reverse Dynamic_Domain loop
267 if (Need_PD (PD, Old_Configs) or Need_PD (PD, Tmp_Configs)) and
268 not Need_PD (PD, New_Configs)
269 then
270 PD_Off (PD);
271 end if;
272 end loop;
273 end Power_Down;
274
275 procedure All_Off is
276 begin
277 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
278
279 for PD in reverse Power_Domain loop
280 PD_Off (PD);
281 end loop;
282 end All_Off;
283
284end HW.GFX.GMA.Power_Domains_Common;