Attention is currently required from: Tim Wawrzynczak.
Hello Tim Wawrzynczak,
I'd like you to do a code review. Please visit
https://review.coreboot.org/c/libgfxinit/+/82141?usp=email
to review the following change.
Change subject: gma tgl: Add support for allocating PLLs ......................................................................
gma tgl: Add support for allocating PLLs
This patch adds support for allocating both combo PHY PLLs and USB Type-C PLLs for Tiger Lake.
Verified Combo PHY (HDMI, eDP) and Type-C (DP Alt mode) on Google/delbin.
Signed-off-by: Tim Wawrzynczak twawrzynczak@chromium.org Change-Id: I9dd7e0d0180f70d73eb50d7e58718261e5e74071 --- M common/hw-gfx-gma-config.ads.template M common/tigerlake/Makefile.inc A common/tigerlake/hw-gfx-gma-plls-combo_phy.adb A common/tigerlake/hw-gfx-gma-plls-combo_phy.ads A common/tigerlake/hw-gfx-gma-plls-dkl.adb A common/tigerlake/hw-gfx-gma-plls-dkl.ads M common/tigerlake/hw-gfx-gma-plls.adb M common/tigerlake/hw-gfx-gma-plls.ads 8 files changed, 930 insertions(+), 20 deletions(-)
git pull ssh://review.coreboot.org:29418/libgfxinit refs/changes/41/82141/1
diff --git a/common/hw-gfx-gma-config.ads.template b/common/hw-gfx-gma-config.ads.template index b7af801..e168144 100644 --- a/common/hw-gfx-gma-config.ads.template +++ b/common/hw-gfx-gma-config.ads.template @@ -261,6 +261,7 @@ Has_Broadwell_CDClk : <hswbool> := CPU_Broadwell; Can_Switch_CDClk : <hswbool> := Broadwell_On; Has_Fractional_RawClk : <genbool> := Cannon_Point_On; + Has_New_Type_C_PLL_Enable : <tglbool> := Alderlake_On;
----------- DDI: ------------- End_EDP_Training_Late : <genbool> := Gen_Haswell; diff --git a/common/tigerlake/Makefile.inc b/common/tigerlake/Makefile.inc index e9ed51f..86ed920 100644 --- a/common/tigerlake/Makefile.inc +++ b/common/tigerlake/Makefile.inc @@ -6,3 +6,7 @@ gfxinit-y += hw-gfx-gma-port_detect.adb gfxinit-y += hw-gfx-gma-plls.adb gfxinit-y += hw-gfx-gma-plls.ads +gfxinit-y += hw-gfx-gma-plls-combo_phy.adb +gfxinit-y += hw-gfx-gma-plls-combo_phy.ads +gfxinit-y += hw-gfx-gma-plls-dkl.adb +gfxinit-y += hw-gfx-gma-plls-dkl.ads diff --git a/common/tigerlake/hw-gfx-gma-plls-combo_phy.adb b/common/tigerlake/hw-gfx-gma-plls-combo_phy.adb new file mode 100644 index 0000000..4e149b6 --- /dev/null +++ b/common/tigerlake/hw-gfx-gma-plls-combo_phy.adb @@ -0,0 +1,351 @@ +-- +-- Copyright (C) 2022 Google, LLC +-- +-- This program is free software; you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation; either version 2 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- + +with HW.GFX.GMA.Config; +with HW.GFX.GMA.Registers; +with HW.GFX.GMA.Power_And_Clocks; + +package body HW.GFX.GMA.PLLs.Combo_Phy is + + subtype HDMI_Clock_Range is Frequency_Type range + 25_000_000 .. Config.HDMI_Max_Clock_24bpp; + subtype DCO_Range is Pos64 range + 7_998_000_000 .. 10_000_000_000; + + type PLL_Regs_Record is record + DPLL_ENABLE : Registers.Registers_Index; + DPLL_CFGCR0 : Registers.Registers_Index; + DPLL_CFGCR1 : Registers.Registers_Index; + DPLL_SSC : Registers.Registers_Index; + end record; + type PLL_Regs_Array is array (Combo_DPLLs) of PLL_Regs_Record; + PLL_Regs : constant PLL_Regs_Array := + PLL_Regs_Array' + (DPLL0 => + (Registers.DPLL_0_ENABLE, + Registers.DPLL_0_CFGCR0, + Registers.DPLL_0_CFGCR1, + Registers.DPLL_0_SSC), + DPLL1 => + (Registers.DPLL_1_ENABLE, + Registers.DPLL_1_CFGCR0, + Registers.DPLL_1_CFGCR1, + Registers.DPLL_1_SSC)); + + DPLL_ENABLE_PLL_ENABLE : constant := 1 * 2 ** 31; + DPLL_ENABLE_PLL_LOCK : constant := 1 * 2 ** 30; + DPLL_ENABLE_POWER_ENABLE : constant := 1 * 2 ** 27; + DPLL_ENABLE_POWER_STATE : constant := 1 * 2 ** 26; + DPLL_SSC_DP : constant := 16#200#; + + subtype PDiv_Range is Positive range 1 .. 8; + subtype QDiv_Range is Natural range 0 .. 255; + subtype KDiv_Range is Positive range 1 .. 5; + + type PLL_Params is record + DCO_Integer : Word32; + DCO_Fraction : Word32; + PDiv : PDiv_Range; + KDiv : KDiv_Range; + QDiv_Mode : natural range 0 .. 1; + QDiv_Ratio : QDiv_Range; + end record; + + type DP_PLL_Params_Array is array (DP_Bandwidth) of PLL_Params; + + PLL_Params_19_2MHz : constant DP_PLL_Params_Array := DP_PLL_Params_Array' + (DP_Bandwidth_5_4 => + (DCO_Integer => 16#1a5#, + DCO_Fraction => 16#7000#, + PDiv => 2, + KDiv => 1, + QDiv_Mode => 0, + QDiv_Ratio => 0), + DP_Bandwidth_2_7 => + (DCO_Integer => 16#1a5#, + DCO_Fraction => 16#7000#, + PDiv => 2, + KDiv => 2, + QDiv_Mode => 0, + QDiv_Ratio => 0), + DP_Bandwidth_1_62 => + (DCO_Integer => 16#1a5#, + DCO_Fraction => 16#7000#, + PDiv => 4, + KDiv => 2, + QDiv_Mode => 0, + QDiv_Ratio => 0)); + + PLL_Params_24MHz : constant DP_PLL_Params_Array := DP_PLL_Params_Array' + (DP_Bandwidth_5_4 => + (DCO_Integer => 16#151#, + DCO_Fraction => 16#4000#, + PDiv => 2, + KDiv => 1, + QDiv_Mode => 0, + QDiv_Ratio => 0), + DP_Bandwidth_2_7 => + (DCO_Integer => 16#151#, + DCO_Fraction => 16#4000#, + PDiv => 2, + KDiv => 2, + QDiv_Mode => 0, + QDiv_Ratio => 0), + DP_Bandwidth_1_62 => + (DCO_Integer => 16#151#, + DCO_Fraction => 16#4000#, + PDiv => 2, + KDiv => 2, + QDiv_Mode => 0, + QDiv_Ratio => 0)); + + procedure Calc_DP_PLL_Dividers + (Bandwidth : in DP_Bandwidth; + Params : out PLL_Params) + is + Refclk : Frequency_Type; + begin + Power_And_Clocks.Get_Refclk (Refclk); + if Refclk = 24_000_000 then + Params := PLL_Params_24MHz (Bandwidth); + else + Params := PLL_Params_19_2MHz (Bandwidth); + end if; + end Calc_DP_PLL_Dividers; + + procedure Calc_HDMI_PLL_Dividers + (Dotclock : in Frequency_Type; + Params : out PLL_Params; + Success : out Boolean) + is + subtype Div_Range is Pos64 range 2 .. 102; + subtype Candidate_Index is Positive range 1 .. 46; + type Candidate_Array is array (Candidate_Index) of Div_Range; + Candidates : constant Candidate_Array := Candidate_Array' + (2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 24, 28, 30, 32, 36, 40, 42, 44, + 48, 50, 52, 54, 56, 60, 64, 66, 68, 70, 72, 76, 78, 80, 84, 88, 90, + 92, 96, 98, 100, 102, 3, 5, 7, 9, 15, 21); + AFE_Clk : constant Int64 := Dotclock * 5; + DCO_Mid : constant Int64 := (DCO_Range'First + DCO_Range'Last) / 2; + Best_DCO_Centrality : Int64 := Frequency_Type'Last; + Best_Div_Index : Candidate_Index := Candidate_Index'First; + Best_Div : Div_Range; + Best_DCO : DCO_Range := DCO_Range'First; + DCO_Found : Boolean := False; + PDiv : PDiv_Range := PDiv_Range'First; + QDiv : QDiv_Range := QDiv_Range'First; + KDiv : KDiv_Range := KDiv_Range'First; + Refclk_Freq : Power_And_Clocks.Refclk_Range; + begin + Success := False; + Params := (DCO_Integer => 0, + DCO_Fraction => 0, + PDiv => PDiv, + KDiv => KDiv, + QDiv_Mode => 0, + QDiv_Ratio => QDiv_Range'First); + for Index in Candidate_Index loop + pragma Loop_Invariant + (Best_DCO >= DCO_Range'First and Best_DCO <= DCO_Range'Last); + declare + DCO : constant Int64 := AFE_Clk * Candidates(Index); + DCO_Centrality : constant Int64 := abs (DCO - DCO_Mid); + begin + if DCO <= DCO_Range'Last and DCO >= DCO_Range'First and + DCO_Centrality < Best_DCO_Centrality + then + DCO_Found := True; + Best_DCO_Centrality := DCO_Centrality; + Best_Div_Index := Index; + Best_DCO := DCO; + end if; + end; + end loop; + + if not DCO_Found then + return; + end if; + + Best_Div := Candidates(Best_Div_Index); + if Best_Div mod 2 = 0 then + pragma Assert (Best_Div > 1); + if Best_Div = 2 then + PDiv := 2; + QDiv := 1; + KDiv := 1; + elsif Best_Div mod 4 = 0 then + PDiv := 2; + QDiv := QDiv_Range (Best_Div / 4); + KDiv := 2; + elsif Best_Div mod 6 = 0 then + PDiv := 3; + QDiv := QDiv_Range (Best_Div / 6); + KDiv := 2; + elsif Best_Div mod 5 = 0 then + PDiv := 5; + QDiv := QDiv_Range (Best_Div / 10); + KDiv := 2; + elsif Best_Div mod 14 = 0 then + PDiv := 7; + QDiv := QDiv_Range (Best_Div / 14); + KDiv := 2; + end if; + else + if Best_Div = 3 or Best_Div = 5 or Best_Div = 7 then + PDiv := PDiv_Range (Best_Div); + QDiv := 1; + KDiv := 1; + else + PDiv := PDiv_Range (Best_Div / 3); + QDiv := 1; + KDiv := 3; + end if; + end if; + Params.KDiv := (case KDiv is + when 1 => 1, + when 2 => 2, + when 3 => 4, + when others => 1); + Params.PDiv := (case PDiv is + when 2 => 1, + when 3 => 2, + when 5 => 4, + when 7 => 8, + when others => 1); + Params.QDiv_Ratio := QDiv; + Params.QDiv_Mode := (if QDiv = 1 then 0 else 1); + Power_And_Clocks.Get_Refclk (Refclk_Freq); + + -- DPLL will auto-divide by 2 if refclk is 38.4 MHz + if Refclk_Freq = 38_400_000 then + Refclk_Freq := 19_200_000; + end if; + + declare + DCO : constant Int64 := + (Best_DCO / 1_000) * (2 ** 15) / (Refclk_Freq / 1_000); + begin + Params.DCO_Integer := Word32 (DCO / (2 ** 15)); + Params.DCO_Fraction := Word32 (DCO) and 16#7fff#; + end; + + Success := True; + end Calc_HDMI_PLL_Dividers; + + procedure On + (PLL : in Combo_DPLLs; + Port_Cfg : in Port_Config; + Success : out Boolean) + is + Params : PLL_Params; + Refclk : Frequency_Type; + begin + if Port_Cfg.Display = DP then + Calc_DP_PLL_Dividers (Port_Cfg.DP.Bandwidth, Params); + Success := True; + else + if Port_Cfg.Mode.Dotclock not in HDMI_Clock_Range then + Debug.Put_Line ("Unsupported HDMI Pixel clock"); + Success := False; + return; + end if; + declare + Color_Depth : constant Int64 := Port_Cfg.Mode.BPC * 3; + Pll_Freq : constant Frequency_Type := Color_Depth * Port_Cfg.Mode.Dotclock / 24; + begin + Calc_HDMI_PLL_Dividers (Pll_Freq, Params, Success); + end; + end if; + + if not Success then + Debug.Put_Line ("Failed to calculate PLL dividers!"); + return; + end if; + + -- Display WA #22010492432: ehl, tgl, adl-p + -- Program half of the nominal DCO divider fraction value + -- for 38.4 MHz refclk + Power_And_Clocks.Get_Refclk (Refclk); + if Refclk = 38_400_000 then + Params.DCO_Fraction := Shift_Right (Params.DCO_Fraction, 1); + end if; + + Registers.Set_Mask + (Register => PLL_Regs (PLL).DPLL_ENABLE, + Mask => DPLL_ENABLE_POWER_ENABLE); + Registers.Wait_Set_Mask + (Register => PLL_Regs (PLL).DPLL_ENABLE, + Mask => DPLL_ENABLE_POWER_STATE, + Success => Success); + + if not Success then + Debug.Put_Line ("Failed to enable PLL!"); + return; + end if; + + -- Configure DPLL_SSC + Registers.Write + (Register => PLL_Regs (PLL).DPLL_SSC, + Value => (if Port_Cfg.Display = DP then DPLL_SSC_DP else 0)); + + Registers.Write + (Register => PLL_Regs (PLL).DPLL_CFGCR0, + Value => Shift_Left (Params.DCO_Fraction, 10) or + Params.DCO_Integer); + + Registers.Write + (Register => PLL_Regs (PLL).DPLL_CFGCR1, + Value => Shift_Left (Word32 (Params.QDiv_Ratio), 10) or + Shift_Left (Word32 (Params.QDiv_Mode), 9) or + Shift_left (Word32 (Params.KDiv), 6) or + Shift_Left (Word32 (Params.PDiv), 2)); + Registers.Posting_Read(PLL_Regs (PLL).DPLL_CFGCR1); + + -- Enable DPLL + Registers.Set_Mask + (Register => PLL_Regs (PLL).DPLL_ENABLE, + Mask => DPLL_ENABLE_PLL_ENABLE); + -- Wait for PLL Lock status + Registers.Wait_Set_Mask + (Register => PLL_Regs (PLL).DPLL_ENABLE, + Mask => DPLL_ENABLE_PLL_LOCK, + Success => Success); + end On; + + procedure Free (PLL : Combo_DPLLs) + is + begin + Registers.Unset_Mask + (Register => PLL_Regs (PLL).DPLL_ENABLE, + Mask => DPLL_ENABLE_PLL_ENABLE); + Registers.Wait_Unset_Mask + (Register => PLL_Regs (PLL).DPLL_ENABLE, + Mask => DPLL_ENABLE_PLL_LOCK); + + Registers.Unset_Mask + (Register => PLL_Regs (PLL).DPLL_ENABLE, + Mask => DPLL_ENABLE_POWER_ENABLE); + Registers.Wait_Unset_Mask + (Register => PLL_Regs (PLL).DPLL_ENABLE, + Mask => DPLL_ENABLE_POWER_STATE); + end Free; + + procedure All_Off is + begin + for PLL in Combo_DPLLs loop + Free (PLL); + end loop; + end All_Off; + +end HW.GFX.GMA.PLLs.Combo_Phy; diff --git a/common/tigerlake/hw-gfx-gma-plls-combo_phy.ads b/common/tigerlake/hw-gfx-gma-plls-combo_phy.ads new file mode 100644 index 0000000..8857b8c --- /dev/null +++ b/common/tigerlake/hw-gfx-gma-plls-combo_phy.ads @@ -0,0 +1,33 @@ +-- +-- Copyright (C) 2022 Google, LLC +-- +-- This program is free software; you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation; either version 2 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- + +with HW.Debug; +with GNAT.Source_Info; + +private package HW.GFX.GMA.PLLs.Combo_Phy is + + procedure On + (PLL : in Combo_DPLLs; + Port_Cfg : in Port_Config; + Success : out Boolean); + + procedure Free (PLL : Combo_DPLLs); + + procedure All_Off; + + type Value_Array is array (Combo_DPLLs) of Word32; + Register_Value : constant Value_Array := Value_Array' + (DPLL0 => 0, DPLL1 => 1); + +end HW.GFX.GMA.PLLs.Combo_Phy; diff --git a/common/tigerlake/hw-gfx-gma-plls-dkl.adb b/common/tigerlake/hw-gfx-gma-plls-dkl.adb new file mode 100644 index 0000000..cca7f24 --- /dev/null +++ b/common/tigerlake/hw-gfx-gma-plls-dkl.adb @@ -0,0 +1,409 @@ +-- +-- Copyright (C) 2022 Google, LLC +-- +-- This program is free software; you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation; either version 2 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- + +with HW.GFX.GMA.Config; +with HW.GFX.GMA.Registers; +with HW.GFX.GMA.Power_And_Clocks; + +package body HW.GFX.GMA.PLLs.DKL is + + use type HW.Word64; + + subtype Frequency_KHz is Pos64 + range (Frequency_Type'First / 1_000) .. (Frequency_Type'Last / 1_000); + subtype DCO_Range_KHz is Pos64 range 7_992_000 .. 10_000_000; + + DPLL_ENABLE_PLL_ENABLE : constant := 1 * 2 ** 31; + DPLL_ENABLE_PLL_LOCK : constant := 1 * 2 ** 30; + DPLL_ENABLE_POWER_ENABLE : constant := 1 * 2 ** 27; + DPLL_ENABLE_POWER_STATE : constant := 1 * 2 ** 26; + + function HIP_INDEX_REG (P : DKL_DPLLs) return Registers.Registers_Index + is (if P <= TCPLL4 + then Registers.HIP_INDEX_REG0 + else Registers.HIP_INDEX_REG1); + + function HIP_INDEX_VAL (P : DKL_DPLLs; Val : Word32) return Word32 is + (case P is + when TCPLL1 => Val * 2 ** 0, + when TCPLL2 => Val * 2 ** 8, + when TCPLL3 => Val * 2 ** 16, + when TCPLL4 => Val * 2 ** 24, + when TCPLL5 => Val * 2 ** 0, + when TCPLL6 => Val * 2 ** 8); + + function DKL_PLL_ENABLE (P : DKL_DPLLs) return Registers.Registers_Index + is + type PLL_Enable_Regs_Array is array (DKL_DPLLs) of Registers.Registers_Index; + DKL_ENABLE : constant PLL_Enable_Regs_Array := + PLL_Enable_Regs_Array' + (TCPLL1 => Registers.MGPLL1_ENABLE, + TCPLL2 => Registers.MGPLL2_ENABLE, + TCPLL3 => Registers.MGPLL3_ENABLE, + TCPLL4 => Registers.MGPLL4_ENABLE, + TCPLL5 => Registers.MGPLL5_ENABLE, + TCPLL6 => Registers.MGPLL6_ENABLE); + DKL_NEW_TYPE_C_ENABLE : constant PLL_Enable_Regs_Array := + PLL_Enable_Regs_Array' + (TCPLL1 => Registers.PORTTC1_PLL1_ENABLE, + TCPLL2 => Registers.PORTTC2_PLL1_ENABLE, + TCPLL3 => Registers.PORTTC3_PLL1_ENABLE, + TCPLL4 => Registers.PORTTC4_PLL1_ENABLE, + TCPLL5 => Registers.MGPLL5_ENABLE, + TCPLL6 => Registers.MGPLL6_ENABLE); + begin + if Config.Has_New_Type_C_PLL_Enable then + return DKL_NEW_TYPE_C_ENABLE (P); + else + return DKL_ENABLE (P); + end if; + end DKL_PLL_ENABLE; + + type PLL_Regs_Record is record + DKL_REFCLKIN_CTL : Registers.Registers_Index; + DKL_CLKTOP2_CORECLKCTL1 : Registers.Registers_Index; + DKL_CLKTOP2_HSCLKCTL : Registers.Registers_Index; + DKL_PLL_DIV0 : Registers.Registers_Index; + DKL_PLL_DIV1 : Registers.Registers_Index; + DKL_PLL_SSC : Registers.Registers_Index; + DKL_PLL_BIAS : Registers.Registers_Index; + DKL_PLL_COLDST_BIAS : Registers.Registers_Index; + end record; + + type PLL_Regs_Array is array (DKL_DPLLs) of PLL_Regs_Record; + PLL_Regs : constant PLL_Regs_Array := + PLL_Regs_Array' + (TCPLL1 => + (Registers.DKL_REFCLKIN_CTL_1, + Registers.DKL_CLKTOP2_CCC1_1, + Registers.DKL_CLKTOP2_HSCC_1, + Registers.DKL_PLL_DIV0_1, + Registers.DKL_PLL_DIV1_1, + Registers.DKL_PLL_SSC_1, + Registers.DKL_PLL_BIAS_1, + Registers.DKL_PLL_COLDST_BIAS_1), + TCPLL2 => + (Registers.DKL_REFCLKIN_CTL_2, + Registers.DKL_CLKTOP2_CCC1_2, + Registers.DKL_CLKTOP2_HSCC_2, + Registers.DKL_PLL_DIV0_2, + Registers.DKL_PLL_DIV1_2, + Registers.DKL_PLL_SSC_2, + Registers.DKL_PLL_BIAS_2, + Registers.DKL_PLL_COLDST_BIAS_2), + TCPLL3 => + (Registers.DKL_REFCLKIN_CTL_3, + Registers.DKL_CLKTOP2_CCC1_3, + Registers.DKL_CLKTOP2_HSCC_3, + Registers.DKL_PLL_DIV0_3, + Registers.DKL_PLL_DIV1_3, + Registers.DKL_PLL_SSC_3, + Registers.DKL_PLL_BIAS_3, + Registers.DKL_PLL_COLDST_BIAS_3), + TCPLL4 => + (Registers.DKL_REFCLKIN_CTL_4, + Registers.DKL_CLKTOP2_CCC1_4, + Registers.DKL_CLKTOP2_HSCC_4, + Registers.DKL_PLL_DIV0_4, + Registers.DKL_PLL_DIV1_4, + Registers.DKL_PLL_SSC_4, + Registers.DKL_PLL_BIAS_4, + Registers.DKL_PLL_COLDST_BIAS_4), + TCPLL5 => + (Registers.DKL_REFCLKIN_CTL_5, + Registers.DKL_CLKTOP2_CCC1_5, + Registers.DKL_CLKTOP2_HSCC_5, + Registers.DKL_PLL_DIV0_5, + Registers.DKL_PLL_DIV1_5, + Registers.DKL_PLL_SSC_5, + Registers.DKL_PLL_BIAS_5, + Registers.DKL_PLL_COLDST_BIAS_5), + TCPLL6 => + (Registers.DKL_REFCLKIN_CTL_6, + Registers.DKL_CLKTOP2_CCC1_6, + Registers.DKL_CLKTOP2_HSCC_6, + Registers.DKL_PLL_DIV0_6, + Registers.DKL_PLL_DIV1_6, + Registers.DKL_PLL_SSC_6, + Registers.DKL_PLL_BIAS_6, + Registers.DKL_PLL_COLDST_BIAS_6)); + + DKL_REFCLKIN_CTL_OD_2_MUX_MASK : constant := 7 * 2 ** 8; + DKL_CLKTOP2_CORECLKCTL1_A_DIVRATIO_MASK : constant := 16#ff# * 2 ** 8; + DKL_CLKTOP2_HSCLKCTL_MASK : constant := 16#1_FF00#; + DKL_PLL_DIV0_MASK : constant := 16#1F_FFFF#; + DKL_PLL_DIV1_MASK : constant := 16#1F_00FF#; + DKL_PLL_SSC_MASK : constant := 16#E0FF_3A00#; + DKL_PLL_BIAS_MASK : constant := 16#7FFF_FF00#; + DKL_PLL_COLD_BIAS_MASK : constant := 16#ffff#; + DKL_PLL_BIAS_FRAC_EN_H : constant := 1 * 2 ** 30; + + procedure Calc_Dividers + (Clock : in Frequency_Type; + Display : in Display_Type; + DKL_Refclkin_Ctl : out Word32; + DKL_Clktop2_Coreclkctl1 : out Word32; + DKL_Clktop2_HSClkCtl : out Word32; + DCO_Khz : out DCO_Range_KHz; + Success : out Boolean) + is + DCO_Min_Freq, DCO_Max_Freq : Int64; + type Dividers_List is array (1 .. 4) of Int64; + Dividers : constant Dividers_List := (7, 5, 3, 2); + DKL_CLKTOP2_HSCLKCTL_HSDIV_RATIO_2 : constant := 0 * 2 ** 12; + DKL_CLKTOP2_HSCLKCTL_HSDIV_RATIO_3 : constant := 1 * 2 ** 12; + DKL_CLKTOP2_HSCLKCTL_HSDIV_RATIO_5 : constant := 2 * 2 ** 12; + DKL_CLKTOP2_HSCLKCTL_HSDIV_RATIO_7 : constant := 3 * 2 ** 12; + function DKL_REFCLKIN_CTL_OD_2_MUX (N : Word32) return Word32 + is (Shift_Left (N, 8)); + function DKL_CLKTOP2_CORECLKCTL1_A_DIVRATIO (N : Word32) return Word32 + is (Shift_Left (N, 8)); + function DKL_CLKTOP2_HSCLKCTL_TLINEDRV_CLKSEL (N : Word32) return Word32 + is (Shift_Left (N, 14)); + function DKL_CLKTOP2_HSCLKCTL_CORE_INPUTSEL (N : Word32) return Word32 + is (Shift_Left (N, 16)); + function DKL_CLKTOP2_HSCLKCTL_DSDIV_RATIO (N : Word32) return Word32 + is (Shift_Left (N, 8)); + A_DivRatio, TLineDrv, Inputsel, Hsdiv : Word32; + Clock_Khz : constant Frequency_KHz := Frequency_KHz(Clock / 1_000); + begin + if Display = DP then + DCO_Min_Freq := 8_100_000; + DCO_Max_Freq := 8_100_000; + else + DCO_Min_Freq := DCO_Range_KHz'First; + DCO_Max_Freq := DCO_Range_KHz'Last; + end if; + + DKL_Refclkin_Ctl := 0; + DKL_clktop2_coreclkctl1 := 0; + DKL_CLKTOP2_hsclkctl := 0; + + DCO_KHz := DCO_Min_Freq; + Success := False; + for I in Dividers'Range loop + pragma Loop_Invariant (DCO_KHz >= DCO_Min_Freq and DCO_KHz <= DCO_Max_Freq); + for Div2 in reverse 1 .. 10 loop + declare + Tmp: constant Pos64 := Dividers (I) * Pos64 (Div2) * Pos64(Clock_Khz) * 5; + begin + if Tmp >= DCO_Min_Freq and then Tmp <= DCO_Max_Freq then + DCO_KHz := Tmp; + if Div2 >= 2 then + A_DivRatio := (if Display = DP then 10 else 5); + TLineDrv := 1; + else + A_DivRatio := 5; + TLineDrv := 0; + end if; + Inputsel := (if Display = DP then 0 else 1); + Hsdiv := (case Dividers (I) is + when 2 => DKL_CLKTOP2_HSCLKCTL_HSDIV_RATIO_2, + when 3 => DKL_CLKTOP2_HSCLKCTL_HSDIV_RATIO_3, + when 5 => DKL_CLKTOP2_HSCLKCTL_HSDIV_RATIO_5, + when 7 => DKL_CLKTOP2_HSCLKCTL_HSDIV_RATIO_7, + when others => DKL_CLKTOP2_HSCLKCTL_HSDIV_RATIO_2); + DKL_Refclkin_Ctl := DKL_REFCLKIN_CTL_OD_2_MUX (1); + DKL_clktop2_coreclkctl1 := + DKL_CLKTOP2_CORECLKCTL1_A_DIVRATIO (A_DivRatio); + DKL_CLKTOP2_hsclkctl := + DKL_CLKTOP2_HSCLKCTL_TLINEDRV_CLKSEL (TLineDrv) or + DKL_CLKTOP2_HSCLKCTL_CORE_INPUTSEL (Inputsel) or + Hsdiv or + DKL_CLKTOP2_HSCLKCTL_DSDIV_RATIO (Word32 (Div2)); + Success := True; + end if; + exit when Success; + end; + end loop; + exit when Success; + end loop; + end Calc_Dividers; + + procedure On + (PLL : in DKL_DPLLs; + Port_Cfg : in Port_Config; + Success : out Boolean) + is + DKL_Refclkin_Ctl : Word32; + DKL_Clktop2_Coreclkctl1 : Word32; + DKL_Clktop2_HSClkCtl : Word32; + DCO_Khz : DCO_Range_KHz; + Clock : Frequency_Type; + begin + if Port_Cfg.Display = HDMI then + Clock := Port_Cfg.Mode.Dotclock; + else + Clock := Frequency_Type (DP_Symbol_Rate (Port_Cfg.DP.Bandwidth)); + end if; + + Calc_Dividers + (Clock => Clock, + Display => Port_Cfg.Display, + DKL_Refclkin_Ctl => DKL_Refclkin_Ctl, + DKL_Clktop2_Coreclkctl1 => DKL_Clktop2_Coreclkctl1, + DKL_Clktop2_HSClkCtl => DKL_Clktop2_HSClkCtl, + DCO_Khz => DCO_Khz, + Success => Success); + + if not Success then + Debug.Put_Line ("Could not find dividers for port!"); + return; + end if; + + Registers.Set_Mask + (Register => DKL_PLL_ENABLE (PLL), + Mask => DPLL_ENABLE_POWER_ENABLE); + + Registers.Wait_Set_Mask + (Register => DKL_PLL_ENABLE (PLL), + Mask => DPLL_ENABLE_POWER_STATE); + + declare + Tmp : Int64; + Refclk : Power_And_Clocks.Refclk_Range; + Refclk_Khz : Power_And_Clocks.Refclk_Range_KHz; + FeedFwGain : Word32; + M1Div : constant := 2; + M2Div_Int, M2Div_Rem: Int64; + M2Div_Frac : Word32; + TDC_Target : Word32; + Prop_Coeff, Int_Coeff : Word32; + IRef_Ndiv, Iref_Itrim : Word32; + begin + Power_And_Clocks.Get_Refclk (Refclk); + Refclk_Khz := Power_And_Clocks.Refclk_Range_KHz (Refclk / 1_000); + M2Div_Int := Int64(DCO_Khz / (Refclk_Khz * M1Div)); + M2Div_Rem := Int64(DCO_Khz rem (Refclk_Khz * M1Div)); + Tmp := M2Div_Rem * 2 ** 22; + M2Div_Frac := Word32 (Tmp / Int64(Refclk_Khz * M1Div)); + + case Refclk is + when 24_000_000 => + Iref_Ndiv := 1; + Iref_Itrim := 25; + when 38_400_000 => + Iref_Ndiv := 2; + Iref_Itrim := 28; + when others => + Iref_Ndiv := 1; + Iref_Itrim := 28; + end case; + + -- Real number math converted to fixed point + -- see note in i915 about these calculations + TDC_Target := Word32 (2 * 1_000 * 100_000 * 10 / (132 * Refclk_Khz) + 5) / 10; + + if M2Div_Rem > 0 then + FeedFwGain := (M1Div * 1_000_000 * 100) / (Word32 (DCO_Khz) * 3 / 10); + else + FeedFwGain := 0; + end if; + + if DCO_Khz >= 9_000_000 then + Prop_Coeff := 5; + Int_Coeff := 10; + else + Prop_Coeff := 4; + Int_Coeff := 8; + end if; + + -- The following DKL registers are at MMIO index 2 + Registers.Write (HIP_INDEX_REG (PLL), HIP_INDEX_VAL (PLL, 2)); + + Registers.Unset_And_Set_Mask + (Register => PLL_Regs (PLL).DKL_REFCLKIN_CTL, + Mask_Unset => DKL_REFCLKIN_CTL_OD_2_MUX_MASK, + Mask_Set => DKL_Refclkin_Ctl); + + Registers.Unset_And_Set_Mask + (Register => PLL_Regs (PLL).DKL_CLKTOP2_CORECLKCTL1, + Mask_Unset => DKL_CLKTOP2_CORECLKCTL1_A_DIVRATIO_MASK, + Mask_Set => DKL_Clktop2_Coreclkctl1); + + Registers.Unset_And_Set_Mask + (Register => PLL_Regs (PLL).DKL_CLKTOP2_HSCLKCTL, + Mask_Unset => DKL_CLKTOP2_HSCLKCTL_MASK, + Mask_Set => DKL_CLKTOP2_hsclkctl); + + Registers.Unset_And_Set_mask + (Register => PLL_Regs (PLL).DKL_PLL_DIV0, + Mask_Unset => DKL_PLL_DIV0_MASK, + Mask_Set => Shift_Left (Int_Coeff, 16) or + Shift_Left (Prop_Coeff, 12) or + Shift_Left (M1Div, 8) or + Word32(M2Div_Int)); + + Registers.Unset_And_Set_Mask + (Register => PLL_Regs (PLL).DKL_PLL_DIV1, + Mask_Unset => DKL_PLL_DIV1_MASK, + Mask_Set => Shift_Left (Iref_Itrim, 16) or + TDC_Target); + + Registers.Unset_And_Set_Mask + (Register => PLL_Regs (PLL).DKL_PLL_SSC, + Mask_Unset => DKL_PLL_SSC_MASK, + Mask_Set => Shift_Left (Iref_Ndiv, 29) or + Shift_Left (4, 11)); -- SSC_STEP_NUM (always 4) + + Registers.Unset_And_Set_Mask + (Register => PLL_Regs (PLL).DKL_PLL_BIAS, + Mask_Unset => DKL_PLL_BIAS_MASK, + Mask_Set => Shift_Left (M2Div_Frac, 8) or + (if M2Div_Frac > 0 then DKL_PLL_BIAS_FRAC_EN_H else 0)); + + Registers.Unset_And_Set_Mask + (Register => PLL_Regs (PLL).DKL_PLL_COLDST_BIAS, + Mask_Unset => DKL_PLL_COLD_BIAS_MASK, + Mask_Set => FeedFwGain); + + -- Read back the last programmed PHY PLL register + Registers.Posting_Read (PLL_Regs (PLL).DKL_PLL_COLDST_BIAS); + + Registers.Set_Mask + (Register => DKL_PLL_ENABLE (PLL), + Mask => DPLL_ENABLE_PLL_ENABLE); + + Registers.Wait_Set_Mask + (Register => DKL_PLL_ENABLE (PLL), + Mask => DPLL_ENABLE_PLL_LOCK); + end; + end On; + + procedure Free (PLL : DKL_DPLLs) + is + begin + Registers.Unset_Mask + (Register => DKL_PLL_ENABLE (PLL), + Mask => DPLL_ENABLE_PLL_ENABLE); + Registers.Wait_Unset_Mask + (Register => DKL_PLL_ENABLE (PLL), + Mask => DPLL_ENABLE_PLL_LOCK); + + Registers.Unset_Mask + (Register => DKL_PLL_ENABLE (PLL), + Mask => DPLL_ENABLE_POWER_ENABLE); + Registers.Wait_Unset_Mask + (Register => DKL_PLL_ENABLE (PLL), + Mask => DPLL_ENABLE_POWER_STATE); + end Free; + + procedure All_Off is + begin + for PLL in DKL_DPLLs loop + Free (PLL); + end loop; + end All_Off; + +end HW.GFX.GMA.PLLs.DKL; diff --git a/common/tigerlake/hw-gfx-gma-plls-dkl.ads b/common/tigerlake/hw-gfx-gma-plls-dkl.ads new file mode 100644 index 0000000..51070e5 --- /dev/null +++ b/common/tigerlake/hw-gfx-gma-plls-dkl.ads @@ -0,0 +1,29 @@ +-- +-- Copyright (C) 2022 Google, LLC +-- +-- This program is free software; you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation; either version 2 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- + +with HW.Debug; +with GNAT.Source_Info; + +private package HW.GFX.GMA.PLLs.DKL is + + procedure On + (PLL : in DKL_DPLLs; + Port_Cfg : in Port_Config; + Success : out Boolean); + + procedure Free (PLL : DKL_DPLLs); + + procedure All_Off; + +end HW.GFX.GMA.PLLs.DKL; diff --git a/common/tigerlake/hw-gfx-gma-plls.adb b/common/tigerlake/hw-gfx-gma-plls.adb index 2823870..f7f0eb3 100644 --- a/common/tigerlake/hw-gfx-gma-plls.adb +++ b/common/tigerlake/hw-gfx-gma-plls.adb @@ -14,16 +14,28 @@
with HW.Debug; with GNAT.Source_Info; +with HW.GFX.GMA.PLLs.Combo_Phy; +with HW.GFX.GMA.PLLs.DKL; +with HW.GFX.GMA.Config;
package body HW.GFX.GMA.PLLs with Refined_State => (State => PLLs) is
- type Count_Range is new Natural range 0 .. 2; + type PLL_Type is (PLL_Unknown, PLL_Combo_Phy, PLL_DKL); + function Port_PLL_Type (Port : GPU_Port) return PLL_Type is + (case Port is + when DIGI_A | DIGI_B | DIGI_C => PLL_Combo_Phy, + when DDI_TC1 | DDI_TC2 | DDI_TC3 | DDI_TC4 | DDI_TC5 | DDI_TC6 => PLL_DKL, + when others => PLL_Unknown);
+ type Count_Range is new Natural range 0 .. 2; type PLL_State is record Use_Count : Count_Range; + Used_For_DP : Boolean; + Link_Rate : DP_Bandwidth; + Mode : Mode_Type; end record;
type PLL_State_Array is array (Configurable_DPLLs) of PLL_State; @@ -41,7 +53,10 @@ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity)); PLLs := (Configurable_DPLLs => - (Use_Count => 0)); + (Use_Count => 0, + Used_For_DP => False, + Link_Rate => DP_Bandwidth'First, + Mode => Invalid_Mode)); end Initialize;
procedure Alloc @@ -49,26 +64,105 @@ PLL : out T; Success : out Boolean) is + function Port_PLL_Match (Port : GPU_Port; PLL : T) return Boolean + is + begin + if Port in Combo_Port and PLL in Combo_DPLLs then + return True; + elsif Port in USBC_Port and PLL in DKL_DPLLs then + if (Port = DDI_TC1 and PLL = TCPLL1) or + (Port = DDI_TC2 and PLL = TCPLL2) or + (Port = DDI_TC3 and PLL = TCPLL3) or + (Port = DDI_TC4 and PLL = TCPLL4) or + (Port = DDI_TC5 and PLL = TCPLL5) or + (Port = DDI_TC6 and PLL = TCPLL6) + then + return True; + end if; + end if; + + return False; + end Port_PLL_Match; + + function Config_Matches (PE : HW.GFX.GMA.PLLs.PLL_State) return Boolean + is + begin + return + PE.Used_For_DP = (Port_Cfg.Display = DP) and + ((PE.Used_For_DP and PE.Link_Rate = Port_Cfg.DP.Bandwidth) or + (not PE.Used_For_DP and PE.Mode = Port_Cfg.Mode)); + end Config_Matches; begin pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity)); - PLL := Invalid_PLL; - Success := True; + + for P in Configurable_DPLLs loop + Success := PLLs (P).Use_Count /= 0 and + PLLs (P).Use_Count /= Count_Range'Last and + Port_PLL_Match (Port_Cfg.Port, P) and + Config_Matches (PLLs (P)); + if Success then + PLL := P; + PLLs (PLL).Use_Count := PLLs (PLL).Use_Count + 1; + return; + end if; + end loop; + + for P in Configurable_DPLLs loop + if PLLs (P).Use_Count = 0 and Port_PLL_Match (Port_Cfg.Port, P) then + PLL := P; + if P in Combo_DPLLs then + Combo_Phy.On (PLL, Port_Cfg, Success); + else--if P in DKL_DPLLs then + DKL.On (PLL, Port_Cfg, Success); + end if; + + if Success then + PLLs (PLL) := + (Use_Count => 1, + Used_For_DP => Port_Cfg.Display = DP, + Link_Rate => Port_Cfg.DP.Bandwidth, + Mode => Port_Cfg.Mode); + end if; + return; + end if; + end loop; + + PLL := Invalid; + Success := False; end Alloc;
procedure Free (PLL : T) is begin pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity)); + + if PLL /= Invalid_PLL then + PLLs (PLL).Use_Count := 0; + end if; + + if PLL in Combo_DPLLs then + Combo_Phy.Free (PLL); + elsif PLL in DKL_DPLLs then + DKL.Free (PLL); + end if; end Free;
procedure All_Off is begin pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity)); + Combo_Phy.All_Off; + DKL.All_Off; end All_Off;
function Register_Value (PLL : T) return Word32 is begin pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity)); - Return 0; + + -- Only required for combo ports + if PLL in Combo_DPLLs then + return Combo_Phy.Register_Value (PLL); + end if; + + return 0; end Register_Value; end HW.GFX.GMA.PLLs; diff --git a/common/tigerlake/hw-gfx-gma-plls.ads b/common/tigerlake/hw-gfx-gma-plls.ads index 3e55044..ff758a4 100644 --- a/common/tigerlake/hw-gfx-gma-plls.ads +++ b/common/tigerlake/hw-gfx-gma-plls.ads @@ -18,36 +18,25 @@ is
-- XXX: Types should be private (but that triggers a bug in SPARK GPL 2016) - type T is (Invalid_PLL, DPLL0, DPLL1, DPLL4, DPLL2); - subtype Configurable_DPLLs is T range DPLL0 .. DPLL4; + type T is (Invalid_PLL, DPLL0, DPLL1, TCPLL1, TCPLL2, TCPLL3, TCPLL4, TCPLL5, TCPLL6); + subtype Configurable_DPLLs is T range DPLL0 .. TCPLL6; + subtype Combo_DPLLs is T range DPLL0 .. DPLL1; + subtype DKL_DPLLs is T range TCPLL1 .. TCPLL6; Invalid : constant T := Invalid_PLL;
procedure Initialize with Global => (Output => State);
- pragma Warnings (Off, "unused variable ""Port_Cfg""", - Reason => "Not yet implemented."); procedure Alloc (Port_Cfg : in Port_Config; PLL : out T; Success : out Boolean); - pragma Warnings (On, "unused variable ""Port_Cfg""");
- - pragma Warnings (Off, "subprogram ""Free"" has no effect", - Reason => "Not yet implemented."); procedure Free (PLL : T); - pragma Warnings (On, "subprogram ""Free"" has no effect");
- pragma Warnings (Off, "subprogram ""All_Off"" has no effect", - Reason => "Not yet implemented."); procedure All_Off; - pragma Warnings (On, "subprogram ""All_Off"" has no effect");
- pragma Warnings (Off, "unused variable ""PLL""", - Reason => "Not yet implemented."); function Register_Value (PLL : T) return Word32; - pragma Warnings (On, "unused variable ""PLL""");
end HW.GFX.GMA.PLLs;