blob: 9172dad60b269487b69e5dc5412e68885a542099 [file] [log] [blame]
Tim Wawrzynczak68deeb42022-09-09 10:59:08 -06001--
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 HW.GFX.GMA.Config;
16with HW.GFX.GMA.Registers;
17with HW.GFX.GMA.Power_And_Clocks;
18
19package body HW.GFX.GMA.PLLs.Dekel_Phy is
20
21 use type HW.Word64;
22
23 subtype Frequency_KHz is Pos64
24 range (Frequency_Type'First / 1_000) .. (Frequency_Type'Last / 1_000);
25 subtype DCO_Range_KHz is Pos64 range 7_992_000 .. 10_000_000;
26
27 DPLL_ENABLE_PLL_ENABLE : constant := 1 * 2 ** 31;
28 DPLL_ENABLE_PLL_LOCK : constant := 1 * 2 ** 30;
29 DPLL_ENABLE_POWER_ENABLE : constant := 1 * 2 ** 27;
30 DPLL_ENABLE_POWER_STATE : constant := 1 * 2 ** 26;
31
32 function HIP_INDEX_REG (P : DKL_DPLLs) return Registers.Registers_Index
33 is (if P <= TCPLL4
34 then Registers.HIP_INDEX_REG0
35 else Registers.HIP_INDEX_REG1);
36
37 function HIP_INDEX_VAL (P : DKL_DPLLs; Val : Word32) return Word32 is
38 (case P is
39 when TCPLL1 => Val * 2 ** 0,
40 when TCPLL2 => Val * 2 ** 8,
41 when TCPLL3 => Val * 2 ** 16,
42 when TCPLL4 => Val * 2 ** 24,
43 when TCPLL5 => Val * 2 ** 0,
44 when TCPLL6 => Val * 2 ** 8);
45
46 function DKL_PLL_ENABLE (P : DKL_DPLLs) return Registers.Registers_Index is
47 (if Config.Has_New_Type_C_PLL_Enable then
48 (case P is
49 when TCPLL1 => Registers.PORTTC1_PLL1_ENABLE,
50 when TCPLL2 => Registers.PORTTC2_PLL1_ENABLE,
51 when TCPLL3 => Registers.PORTTC3_PLL1_ENABLE,
52 when TCPLL4 => Registers.PORTTC4_PLL1_ENABLE,
53 when TCPLL5 => Registers.MGPLL5_ENABLE,
54 when TCPLL6 => Registers.MGPLL6_ENABLE)
55 else
56 (case P is
57 when TCPLL1 => Registers.MGPLL1_ENABLE,
58 when TCPLL2 => Registers.MGPLL2_ENABLE,
59 when TCPLL3 => Registers.MGPLL3_ENABLE,
60 when TCPLL4 => Registers.MGPLL4_ENABLE,
61 when TCPLL5 => Registers.MGPLL5_ENABLE,
62 when TCPLL6 => Registers.MGPLL6_ENABLE));
63
64 type PLL_Regs_Record is record
65 DKL_REFCLKIN_CTL : Registers.Registers_Index;
66 DKL_CLKTOP2_CORECLKCTL1 : Registers.Registers_Index;
67 DKL_CLKTOP2_HSCLKCTL : Registers.Registers_Index;
68 DKL_PLL_DIV0 : Registers.Registers_Index;
69 DKL_PLL_DIV1 : Registers.Registers_Index;
70 DKL_PLL_SSC : Registers.Registers_Index;
71 DKL_PLL_BIAS : Registers.Registers_Index;
72 DKL_PLL_COLDST_BIAS : Registers.Registers_Index;
73 end record;
74
75 type PLL_Regs_Array is array (DKL_DPLLs) of PLL_Regs_Record;
76 PLL_Regs : constant PLL_Regs_Array :=
77 PLL_Regs_Array'
78 (TCPLL1 =>
79 (Registers.DKL_REFCLKIN_CTL_1,
80 Registers.DKL_CLKTOP2_CCC1_1,
81 Registers.DKL_CLKTOP2_HSCC_1,
82 Registers.DKL_PLL_DIV0_1,
83 Registers.DKL_PLL_DIV1_1,
84 Registers.DKL_PLL_SSC_1,
85 Registers.DKL_PLL_BIAS_1,
86 Registers.DKL_PLL_COLDST_BIAS_1),
87 TCPLL2 =>
88 (Registers.DKL_REFCLKIN_CTL_2,
89 Registers.DKL_CLKTOP2_CCC1_2,
90 Registers.DKL_CLKTOP2_HSCC_2,
91 Registers.DKL_PLL_DIV0_2,
92 Registers.DKL_PLL_DIV1_2,
93 Registers.DKL_PLL_SSC_2,
94 Registers.DKL_PLL_BIAS_2,
95 Registers.DKL_PLL_COLDST_BIAS_2),
96 TCPLL3 =>
97 (Registers.DKL_REFCLKIN_CTL_3,
98 Registers.DKL_CLKTOP2_CCC1_3,
99 Registers.DKL_CLKTOP2_HSCC_3,
100 Registers.DKL_PLL_DIV0_3,
101 Registers.DKL_PLL_DIV1_3,
102 Registers.DKL_PLL_SSC_3,
103 Registers.DKL_PLL_BIAS_3,
104 Registers.DKL_PLL_COLDST_BIAS_3),
105 TCPLL4 =>
106 (Registers.DKL_REFCLKIN_CTL_4,
107 Registers.DKL_CLKTOP2_CCC1_4,
108 Registers.DKL_CLKTOP2_HSCC_4,
109 Registers.DKL_PLL_DIV0_4,
110 Registers.DKL_PLL_DIV1_4,
111 Registers.DKL_PLL_SSC_4,
112 Registers.DKL_PLL_BIAS_4,
113 Registers.DKL_PLL_COLDST_BIAS_4),
114 TCPLL5 =>
115 (Registers.DKL_REFCLKIN_CTL_5,
116 Registers.DKL_CLKTOP2_CCC1_5,
117 Registers.DKL_CLKTOP2_HSCC_5,
118 Registers.DKL_PLL_DIV0_5,
119 Registers.DKL_PLL_DIV1_5,
120 Registers.DKL_PLL_SSC_5,
121 Registers.DKL_PLL_BIAS_5,
122 Registers.DKL_PLL_COLDST_BIAS_5),
123 TCPLL6 =>
124 (Registers.DKL_REFCLKIN_CTL_6,
125 Registers.DKL_CLKTOP2_CCC1_6,
126 Registers.DKL_CLKTOP2_HSCC_6,
127 Registers.DKL_PLL_DIV0_6,
128 Registers.DKL_PLL_DIV1_6,
129 Registers.DKL_PLL_SSC_6,
130 Registers.DKL_PLL_BIAS_6,
131 Registers.DKL_PLL_COLDST_BIAS_6));
132
133 DKL_REFCLKIN_CTL_OD_2_MUX_MASK : constant := 7 * 2 ** 8;
134 DKL_CLKTOP2_CORECLKCTL1_A_DIVRATIO_MASK : constant := 16#ff# * 2 ** 8;
135 DKL_CLKTOP2_HSCLKCTL_MASK : constant := 16#1_FF00#;
136 DKL_PLL_DIV0_MASK : constant := 16#1F_FFFF#;
137 DKL_PLL_DIV1_MASK : constant := 16#1F_00FF#;
138 DKL_PLL_SSC_MASK : constant := 16#E0FF_3A00#;
139 DKL_PLL_BIAS_MASK : constant := 16#7FFF_FF00#;
140 DKL_PLL_COLD_BIAS_MASK : constant := 16#ffff#;
141 DKL_PLL_BIAS_FRAC_EN_H : constant := 1 * 2 ** 30;
142
143 procedure Calc_Dividers
144 (Clock : in Frequency_Type;
145 Display : in Display_Type;
146 DKL_Refclkin_Ctl : out Word32;
147 DKL_Clktop2_Coreclkctl1 : out Word32;
148 DKL_Clktop2_HSClkCtl : out Word32;
149 DCO_Khz : out DCO_Range_KHz;
150 Success : out Boolean)
151 is
152 DCO_Min_Freq, DCO_Max_Freq : Int64;
153 type Dividers_List is array (1 .. 4) of Int64;
154 Dividers : constant Dividers_List := (7, 5, 3, 2);
155 DKL_CLKTOP2_HSCLKCTL_HSDIV_RATIO_2 : constant := 0 * 2 ** 12;
156 DKL_CLKTOP2_HSCLKCTL_HSDIV_RATIO_3 : constant := 1 * 2 ** 12;
157 DKL_CLKTOP2_HSCLKCTL_HSDIV_RATIO_5 : constant := 2 * 2 ** 12;
158 DKL_CLKTOP2_HSCLKCTL_HSDIV_RATIO_7 : constant := 3 * 2 ** 12;
159 function DKL_REFCLKIN_CTL_OD_2_MUX (N : Word32) return Word32
160 is (Shift_Left (N, 8));
161 function DKL_CLKTOP2_CORECLKCTL1_A_DIVRATIO (N : Word32) return Word32
162 is (Shift_Left (N, 8));
163 function DKL_CLKTOP2_HSCLKCTL_TLINEDRV_CLKSEL (N : Word32) return Word32
164 is (Shift_Left (N, 14));
165 function DKL_CLKTOP2_HSCLKCTL_CORE_INPUTSEL (N : Word32) return Word32
166 is (Shift_Left (N, 16));
167 function DKL_CLKTOP2_HSCLKCTL_DSDIV_RATIO (N : Word32) return Word32
168 is (Shift_Left (N, 8));
169 Clock_Khz : constant Frequency_KHz := Frequency_KHz(Clock / 1_000);
170 begin
171 if Display = DP then
172 DCO_Min_Freq := 8_100_000;
173 DCO_Max_Freq := 8_100_000;
174 else
175 DCO_Min_Freq := DCO_Range_KHz'First;
176 DCO_Max_Freq := DCO_Range_KHz'Last;
177 end if;
178
179 DKL_Refclkin_Ctl := 0;
180 DKL_clktop2_coreclkctl1 := 0;
181 DKL_CLKTOP2_hsclkctl := 0;
182
183 DCO_KHz := DCO_Min_Freq;
184 Success := False;
185 for I in Dividers'Range loop
186 pragma Loop_Invariant (DCO_KHz >= DCO_Min_Freq and DCO_KHz <= DCO_Max_Freq);
187 for Div2 in reverse 1 .. 10 loop
188 declare
189 Tmp : constant Pos64 := Dividers (I) * Pos64 (Div2) * Pos64(Clock_Khz) * 5;
190 A_DivRatio, TLineDrv, Inputsel, Hsdiv : Word32;
191 begin
192 if Tmp >= DCO_Min_Freq and then Tmp <= DCO_Max_Freq then
193 DCO_KHz := Tmp;
194 if Div2 >= 2 then
195 A_DivRatio := (if Display = DP then 10 else 5);
196 TLineDrv := 1;
197 else
198 A_DivRatio := 5;
199 TLineDrv := 0;
200 end if;
201 Inputsel := (if Display = DP then 0 else 1);
202 Hsdiv := (case Dividers (I) is
203 when 2 => DKL_CLKTOP2_HSCLKCTL_HSDIV_RATIO_2,
204 when 3 => DKL_CLKTOP2_HSCLKCTL_HSDIV_RATIO_3,
205 when 5 => DKL_CLKTOP2_HSCLKCTL_HSDIV_RATIO_5,
206 when 7 => DKL_CLKTOP2_HSCLKCTL_HSDIV_RATIO_7,
207 when others => DKL_CLKTOP2_HSCLKCTL_HSDIV_RATIO_2);
208 DKL_Refclkin_Ctl := DKL_REFCLKIN_CTL_OD_2_MUX (1);
209 DKL_clktop2_coreclkctl1 :=
210 DKL_CLKTOP2_CORECLKCTL1_A_DIVRATIO (A_DivRatio);
211 DKL_CLKTOP2_hsclkctl :=
212 DKL_CLKTOP2_HSCLKCTL_TLINEDRV_CLKSEL (TLineDrv) or
213 DKL_CLKTOP2_HSCLKCTL_CORE_INPUTSEL (Inputsel) or
214 Hsdiv or
215 DKL_CLKTOP2_HSCLKCTL_DSDIV_RATIO (Word32 (Div2));
216 Success := True;
217 end if;
218 exit when Success;
219 end;
220 end loop;
221 exit when Success;
222 end loop;
223 end Calc_Dividers;
224
225 procedure On
226 (PLL : in DKL_DPLLs;
227 Port_Cfg : in Port_Config;
228 Success : out Boolean)
229 is
230 DKL_Refclkin_Ctl : Word32;
231 DKL_Clktop2_Coreclkctl1 : Word32;
232 DKL_Clktop2_HSClkCtl : Word32;
233 DCO_Khz : DCO_Range_KHz;
234 Clock : Frequency_Type;
235 begin
236 if Port_Cfg.Display = HDMI then
237 Clock := Port_Cfg.Mode.Dotclock;
238 else
239 Clock := Frequency_Type (DP_Symbol_Rate (Port_Cfg.DP.Bandwidth));
240 end if;
241
242 Calc_Dividers
243 (Clock => Clock,
244 Display => Port_Cfg.Display,
245 DKL_Refclkin_Ctl => DKL_Refclkin_Ctl,
246 DKL_Clktop2_Coreclkctl1 => DKL_Clktop2_Coreclkctl1,
247 DKL_Clktop2_HSClkCtl => DKL_Clktop2_HSClkCtl,
248 DCO_Khz => DCO_Khz,
249 Success => Success);
250
251 if not Success then
252 Debug.Put_Line ("Could not find dividers for port!");
253 return;
254 end if;
255
256 Registers.Set_Mask
257 (Register => DKL_PLL_ENABLE (PLL),
258 Mask => DPLL_ENABLE_POWER_ENABLE);
259
260 Registers.Wait_Set_Mask
261 (Register => DKL_PLL_ENABLE (PLL),
262 Mask => DPLL_ENABLE_POWER_STATE);
263
264 declare
265 Tmp : Int64;
266 Refclk : Power_And_Clocks.Refclk_Range;
267 Refclk_Khz : Power_And_Clocks.Refclk_Range_KHz;
268 FeedFwGain : Word32;
269 M1Div : constant := 2;
270 M2Div_Int, M2Div_Rem: Int64;
271 M2Div_Frac : Word32;
272 TDC_Target : Word32;
273 Prop_Coeff, Int_Coeff : Word32;
274 IRef_Ndiv, Iref_Itrim : Word32;
275 begin
276 Power_And_Clocks.Get_Refclk (Refclk);
277 Refclk_Khz := Power_And_Clocks.Refclk_Range_KHz (Refclk / 1_000);
278 M2Div_Int := Int64(DCO_Khz / (Refclk_Khz * M1Div));
279 M2Div_Rem := Int64(DCO_Khz rem (Refclk_Khz * M1Div));
280 Tmp := M2Div_Rem * 2 ** 22;
281 M2Div_Frac := Word32 (Tmp / Int64(Refclk_Khz * M1Div));
282
283 case Refclk is
284 when 24_000_000 =>
285 Iref_Ndiv := 1;
286 Iref_Itrim := 25;
287 when 38_400_000 =>
288 Iref_Ndiv := 2;
289 Iref_Itrim := 28;
290 when others =>
291 Iref_Ndiv := 1;
292 Iref_Itrim := 28;
293 end case;
294
295 -- Real number math converted to fixed point
296 -- see note in i915 about these calculations
297 TDC_Target := Word32 (2 * 1_000 * 100_000 * 10 / (132 * Refclk_Khz) + 5) / 10;
298
299 if M2Div_Rem > 0 then
300 FeedFwGain := (M1Div * 1_000_000 * 100) / (Word32 (DCO_Khz) * 3 / 10);
301 else
302 FeedFwGain := 0;
303 end if;
304
305 if DCO_Khz >= 9_000_000 then
306 Prop_Coeff := 5;
307 Int_Coeff := 10;
308 else
309 Prop_Coeff := 4;
310 Int_Coeff := 8;
311 end if;
312
313 -- The following DKL registers are at MMIO index 2
314 Registers.Write (HIP_INDEX_REG (PLL), HIP_INDEX_VAL (PLL, 2));
315
316 Registers.Unset_And_Set_Mask
317 (Register => PLL_Regs (PLL).DKL_REFCLKIN_CTL,
318 Mask_Unset => DKL_REFCLKIN_CTL_OD_2_MUX_MASK,
319 Mask_Set => DKL_Refclkin_Ctl);
320
321 Registers.Unset_And_Set_Mask
322 (Register => PLL_Regs (PLL).DKL_CLKTOP2_CORECLKCTL1,
323 Mask_Unset => DKL_CLKTOP2_CORECLKCTL1_A_DIVRATIO_MASK,
324 Mask_Set => DKL_Clktop2_Coreclkctl1);
325
326 Registers.Unset_And_Set_Mask
327 (Register => PLL_Regs (PLL).DKL_CLKTOP2_HSCLKCTL,
328 Mask_Unset => DKL_CLKTOP2_HSCLKCTL_MASK,
329 Mask_Set => DKL_CLKTOP2_hsclkctl);
330
331 Registers.Unset_And_Set_mask
332 (Register => PLL_Regs (PLL).DKL_PLL_DIV0,
333 Mask_Unset => DKL_PLL_DIV0_MASK,
334 Mask_Set => Shift_Left (Int_Coeff, 16) or
335 Shift_Left (Prop_Coeff, 12) or
336 Shift_Left (M1Div, 8) or
337 Word32(M2Div_Int));
338
339 Registers.Unset_And_Set_Mask
340 (Register => PLL_Regs (PLL).DKL_PLL_DIV1,
341 Mask_Unset => DKL_PLL_DIV1_MASK,
342 Mask_Set => Shift_Left (Iref_Itrim, 16) or
343 TDC_Target);
344
345 Registers.Unset_And_Set_Mask
346 (Register => PLL_Regs (PLL).DKL_PLL_SSC,
347 Mask_Unset => DKL_PLL_SSC_MASK,
348 Mask_Set => Shift_Left (Iref_Ndiv, 29) or
349 Shift_Left (4, 11)); -- SSC_STEP_NUM (always 4)
350
351 Registers.Unset_And_Set_Mask
352 (Register => PLL_Regs (PLL).DKL_PLL_BIAS,
353 Mask_Unset => DKL_PLL_BIAS_MASK,
354 Mask_Set => Shift_Left (M2Div_Frac, 8) or
355 (if M2Div_Frac > 0 then DKL_PLL_BIAS_FRAC_EN_H else 0));
356
357 Registers.Unset_And_Set_Mask
358 (Register => PLL_Regs (PLL).DKL_PLL_COLDST_BIAS,
359 Mask_Unset => DKL_PLL_COLD_BIAS_MASK,
360 Mask_Set => FeedFwGain);
361
362 -- Read back the last programmed PHY PLL register
363 Registers.Posting_Read (PLL_Regs (PLL).DKL_PLL_COLDST_BIAS);
364
365 Registers.Set_Mask
366 (Register => DKL_PLL_ENABLE (PLL),
367 Mask => DPLL_ENABLE_PLL_ENABLE);
368
369 Registers.Wait_Set_Mask
370 (Register => DKL_PLL_ENABLE (PLL),
371 Mask => DPLL_ENABLE_PLL_LOCK);
372 end;
373 end On;
374
375 procedure Free (PLL : DKL_DPLLs)
376 is
377 begin
378 Registers.Unset_Mask
379 (Register => DKL_PLL_ENABLE (PLL),
380 Mask => DPLL_ENABLE_PLL_ENABLE);
381 Registers.Wait_Unset_Mask
382 (Register => DKL_PLL_ENABLE (PLL),
383 Mask => DPLL_ENABLE_PLL_LOCK);
384
385 Registers.Unset_Mask
386 (Register => DKL_PLL_ENABLE (PLL),
387 Mask => DPLL_ENABLE_POWER_ENABLE);
388 Registers.Wait_Unset_Mask
389 (Register => DKL_PLL_ENABLE (PLL),
390 Mask => DPLL_ENABLE_POWER_STATE);
391 end Free;
392
393 procedure All_Off is
394 begin
395 for PLL in DKL_DPLLs loop
396 Free (PLL);
397 end loop;
398 end All_Off;
399
400end HW.GFX.GMA.PLLs.Dekel_Phy;