| Tim Wawrzynczak | 68deeb4 | 2022-09-09 10:59:08 -0600 | [diff] [blame^] | 1 | -- |
| 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 | |
| 15 | with HW.GFX.GMA.Config; |
| 16 | with HW.GFX.GMA.Registers; |
| 17 | with HW.GFX.GMA.Power_And_Clocks; |
| 18 | |
| 19 | package 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 | |
| 400 | end HW.GFX.GMA.PLLs.Dekel_Phy; |