gma tgl: Add combo PHY programming sequence
This is required before any combo PHY ports can be used, so it will be
performed early in the modeset.
Squashed clean-up:
gma tgl: Refactor code for enhanced modularity
This patch corrects code indentation and incorporates an internal
procedure Propagate_To_Group for enhanced modularity. There are no
changes to functionality.
Signed-off-by: Dinesh Gehlot <digehlot@google.com>
Change-Id: Id0c2495872250e84057c3130cab19dea05a00b75
Signed-off-by: Tim Wawrzynczak <twawrzynczak@chromium.org>
Reviewed-on: https://review.sourcearcade.org/c/libgfxinit/+/459
Tested-by: Nico Huber <nico.h@gmx.de>
Reviewed-by: Nico Huber <nico.h@gmx.de>
diff --git a/common/tigerlake/hw-gfx-gma-combo_phy.adb b/common/tigerlake/hw-gfx-gma-combo_phy.adb
new file mode 100644
index 0000000..446fa66
--- /dev/null
+++ b/common/tigerlake/hw-gfx-gma-combo_phy.adb
@@ -0,0 +1,255 @@
+--
+-- 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 GNAT.Source_Info;
+with HW.Debug;
+with HW.GFX.GMA.Registers;
+
+package body HW.GFX.GMA.Combo_Phy is
+
+ PORT_PCS_DW1_DCC_MODE_SELECT_MASK : constant := 3 * 2 ** 20;
+ PORT_PCS_DW1_DCC_MODE_SELECT_CONTINUOUS : constant := 3 * 2 ** 20;
+ PORT_COMP_DW0_COMP_INIT : constant := 1 * 2 ** 31;
+ ICL_PORT_TX_DW8_ODCC_CLK_DIV_SEL_DIV2 : constant := 1 * 2 ** 29;
+ PORT_CL_DW5_POWER_DOWN_ENABLE : constant := 1 * 2 ** 4;
+ PHY_MISC_DE_TO_IO_COMP_PWR_DOWN : constant := 1 * 2 ** 23;
+ PORT_TX_DW8_ODCC_DIV_SEL_MASK : constant := 3 * 2 ** 29;
+ PORT_TX_DW8_ODCC_CLKSEL : constant := 1 * 2 ** 31;
+ PORT_COMP_DW8_IREFGEN : constant := 1 * 2 ** 24;
+ PORT_COMP_DW1_REF_MASK : constant := 16#ff_00ff#;
+
+ type Combo_Phy is (DDI_A, DDI_B, DDI_C);
+
+ type Phy_Regs_Record is record
+ PHY_MISC : Registers.Registers_Index;
+ PORT_CL_DW5 : Registers.Registers_Index;
+ PORT_COMP_DW0 : Registers.Registers_Index;
+ PORT_COMP_DW1 : Registers.Registers_Index;
+ PORT_COMP_DW3 : Registers.Registers_Index;
+ PORT_TX_DW8_LN0 : Registers.Registers_Index;
+ PORT_TX_DW8_GRP : Registers.Registers_Index;
+ PORT_PCS_DW1_LN0 : Registers.Registers_Index;
+ PORT_PCS_DW1_GRP : Registers.Registers_Index;
+ PORT_COMP_DW8 : Registers.Registers_Index;
+ PORT_COMP_DW9 : Registers.Registers_Index;
+ PORT_COMP_DW10 : Registers.Registers_Index;
+ end record;
+
+ type Combo_Phy_Regs is array (Combo_Phy) of Phy_Regs_Record;
+ Phy_Regs : constant Combo_Phy_Regs := Combo_Phy_Regs'
+ (DDI_A => Phy_Regs_Record'
+ (PHY_MISC => Registers.PHY_MISC_A,
+ PORT_CL_DW5 => Registers.PORT_CL_DW5_A,
+ PORT_COMP_DW0 => Registers.PORT_COMP_DW0_A,
+ PORT_COMP_DW1 => Registers.PORT_COMP_DW1_A,
+ PORT_COMP_DW3 => Registers.PORT_COMP_DW3_A,
+ PORT_TX_DW8_LN0 => Registers.PORT_TX_DW8_LN0_A,
+ PORT_TX_DW8_GRP => Registers.PORT_TX_DW8_GRP_A,
+ PORT_PCS_DW1_LN0 => Registers.PORT_PCS_DW1_LN0_A,
+ PORT_PCS_DW1_GRP => Registers.PORT_PCS_DW1_GRP_A,
+ PORT_COMP_DW8 => Registers.PORT_COMP_DW8_A,
+ PORT_COMP_DW9 => Registers.PORT_COMP_DW9_A,
+ PORT_COMP_DW10 => Registers.PORT_COMP_DW10_A),
+ DDI_B => Phy_Regs_Record'
+ (PHY_MISC => Registers.PHY_MISC_B,
+ PORT_CL_DW5 => Registers.PORT_CL_DW5_B,
+ PORT_COMP_DW0 => Registers.PORT_COMP_DW0_B,
+ PORT_COMP_DW1 => Registers.PORT_COMP_DW1_B,
+ PORT_COMP_DW3 => Registers.PORT_COMP_DW3_B,
+ PORT_TX_DW8_LN0 => Registers.PORT_TX_DW8_LN0_B,
+ PORT_TX_DW8_GRP => Registers.PORT_TX_DW8_GRP_B,
+ PORT_PCS_DW1_LN0 => Registers.PORT_PCS_DW1_LN0_B,
+ PORT_PCS_DW1_GRP => Registers.PORT_PCS_DW1_GRP_B,
+ PORT_COMP_DW8 => Registers.PORT_COMP_DW8_B,
+ PORT_COMP_DW9 => Registers.PORT_COMP_DW9_B,
+ PORT_COMP_DW10 => Registers.PORT_COMP_DW10_B),
+ DDI_C => Phy_Regs_Record'
+ (PHY_MISC => Registers.PHY_MISC_C,
+ PORT_CL_DW5 => Registers.PORT_CL_DW5_C,
+ PORT_COMP_DW0 => Registers.PORT_COMP_DW0_C,
+ PORT_COMP_DW1 => Registers.PORT_COMP_DW1_C,
+ PORT_COMP_DW3 => Registers.PORT_COMP_DW3_C,
+ PORT_TX_DW8_LN0 => Registers.PORT_TX_DW8_LN0_C,
+ PORT_TX_DW8_GRP => Registers.PORT_TX_DW8_GRP_C,
+ PORT_PCS_DW1_LN0 => Registers.PORT_PCS_DW1_LN0_C,
+ PORT_PCS_DW1_GRP => Registers.PORT_PCS_DW1_GRP_C,
+ PORT_COMP_DW8 => Registers.PORT_COMP_DW8_C,
+ PORT_COMP_DW9 => Registers.PORT_COMP_DW9_C,
+ PORT_COMP_DW10 => Registers.PORT_COMP_DW10_C));
+
+ procedure Propagate_To_Group
+ (Lane0_Register : Registers.Registers_Index;
+ Mask_Unset : Word32;
+ Mask_Set : Word32;
+ Group_Register : Registers.Registers_Index)
+ is
+ Value : Word32;
+ begin
+ -- Read from lane 0 and write to the group
+ Registers.Read (Lane0_Register, Value);
+ Value := (Value and not Mask_Unset) or Mask_Set;
+ Registers.Write (Group_Register, Value);
+ end Propagate_To_Group;
+
+ procedure Config_DCC_SusClk (Phy : Combo_Phy) is
+ begin
+ Propagate_To_Group
+ (Lane0_Register => Phy_Regs (Phy).PORT_TX_DW8_LN0,
+ Mask_Unset => PORT_TX_DW8_ODCC_DIV_SEL_MASK,
+ Mask_Set => PORT_TX_DW8_ODCC_CLKSEL or
+ ICL_PORT_TX_DW8_ODCC_CLK_DIV_SEL_DIV2,
+ Group_Register => Phy_Regs (Phy).PORT_TX_DW8_GRP);
+
+ Propagate_To_Group
+ (Lane0_Register => Phy_Regs (Phy).PORT_PCS_DW1_LN0,
+ Mask_Unset => PORT_PCS_DW1_DCC_MODE_SELECT_MASK,
+ Mask_Set => PORT_PCS_DW1_DCC_MODE_SELECT_CONTINUOUS,
+ Group_Register => Phy_Regs (Phy).PORT_PCS_DW1_GRP);
+ end Config_DCC_SusClk;
+
+ procedure Config_Procmon_Reference (Phy : Combo_Phy)
+ is
+ type Procmon_Voltage is (VOLT_0_85, VOLT_0_95, VOLT_1_05);
+ type Procmon_Process is (DOT0, DOT1);
+ type Procmon_References is record
+ DW1 : Word32;
+ DW9 : Word32;
+ DW10 : Word32;
+ end record;
+
+ DOT0_VOLT_0_85 : constant Procmon_References :=
+ (DW1 => 16#0000_0000#,
+ DW9 => 16#62ab_67bb#,
+ DW10 => 16#5191_4f96#);
+ DOT0_VOLT_0_95 : constant Procmon_References :=
+ (DW1 => 16#0000_0000#,
+ DW9 => 16#86e1_72c7#,
+ DW10 => 16#77ca_5eab#);
+ DOT0_VOLT_1_05 : constant Procmon_References :=
+ (DW1 => 16#0000_0000#,
+ DW9 => 16#98fa_82dd#,
+ DW10 => 16#89e4_6dc1#);
+ DOT1_VOLT_0_95 : constant Procmon_References :=
+ (DW1 => 16#0000_0000#,
+ DW9 => 16#93f8_7fe1#,
+ DW10 => 16#8ae8_71c5#);
+ DOT1_VOLT_1_05 : constant Procmon_References :=
+ (DW1 => 16#0044_0000#,
+ DW9 => 16#9a00_ab25#,
+ DW10 => 16#8ae3_8ff1#);
+ procedure Read_DW3 (Phy : Combo_Phy; References : out Procmon_References)
+ is
+ DW3, Tmp : Word32;
+ Process : Procmon_Process;
+ Voltage : Procmon_Voltage;
+ PROCESS_MASK : constant := 7 * 2 ** 26;
+ VOLTAGE_MASK : constant := 3 * 2 ** 24;
+ begin
+ Registers.Read (Phy_Regs (Phy).PORT_COMP_DW3, DW3);
+
+ Tmp := Shift_Right (DW3 and VOLTAGE_MASK, 24);
+ case (Tmp) is
+ when 0 => Voltage := VOLT_0_85;
+ when 1 => Voltage := VOLT_0_95;
+ when 2 => Voltage := VOLT_1_05;
+ when others => Voltage := VOLT_0_85;
+ end case;
+
+ Tmp := Shift_Right (DW3 and PROCESS_MASK, 26);
+ case (Tmp) is
+ when 0 => Process := DOT0;
+ when 1 => Process := DOT1;
+ when others => Process := DOT0;
+ end case;
+
+ if Process = DOT0 then
+ case (Voltage) is
+ when VOLT_0_85 => References := DOT0_VOLT_0_85;
+ when VOLT_0_95 => References := DOT0_VOLT_0_95;
+ when VOLT_1_05 => References := DOT0_VOLT_1_05;
+ end case;
+ else
+ case (Voltage) is
+ -- [DOT1, VOLT_0_85] is actually an invalid combination
+ when VOLT_0_95 | VOLT_0_85 => References := DOT1_VOLT_0_95;
+ when VOLT_1_05 => References := DOT1_VOLT_1_05;
+ end case;
+ end if;
+ end Read_DW3;
+
+ References : Procmon_References;
+ begin
+ Read_DW3 (Phy, References);
+ Registers.Unset_And_Set_Mask (Register => Phy_Regs (Phy).PORT_COMP_DW1,
+ Mask_Unset => PORT_COMP_DW1_REF_MASK,
+ Mask_Set => References.DW1);
+ Registers.Write (Phy_Regs (Phy).PORT_COMP_DW9 , References.DW9);
+ Registers.Write (Phy_Regs (Phy).PORT_COMP_DW10, References.DW10);
+ end Config_Procmon_Reference;
+
+ function Phy_Is_Master (Phy : Combo_Phy) return Boolean is
+ begin
+ case Phy is
+ when DDI_A => return True;
+ when others => return False;
+ end case;
+ end Phy_Is_Master;
+
+ procedure Initialize
+ is
+ Was_Enabled : Boolean;
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+ -- Initialize all combo PHYs with Combo PHY DDI Buffer Combo PHY Init Sequence
+ for Phy in Combo_Phy'range loop
+ Registers.Is_Set_Mask (
+ Phy_Regs (Phy).PORT_COMP_DW0,
+ PORT_COMP_DW0_COMP_INIT,
+ Was_Enabled);
+
+ if not Was_Enabled then
+ Config_DCC_SusClk (Phy);
+
+ Registers.Unset_Mask
+ (Phy_Regs (Phy).PHY_MISC,
+ PHY_MISC_DE_TO_IO_COMP_PWR_DOWN);
+
+ Config_Procmon_Reference (Phy);
+
+ if Phy_Is_Master (Phy) then
+ Registers.Set_Mask (Register => Phy_Regs (Phy).PORT_COMP_DW8,
+ Mask => PORT_COMP_DW8_IREFGEN);
+ end if;
+
+ Registers.Set_Mask (Register => Phy_Regs (Phy).PORT_COMP_DW0,
+ Mask => PORT_COMP_DW0_COMP_INIT);
+
+ Registers.Set_Mask (Register => Phy_Regs (Phy).PORT_CL_DW5,
+ Mask => PORT_CL_DW5_POWER_DOWN_ENABLE);
+ end if;
+ end loop;
+ end Initialize;
+
+ procedure All_Off is
+ begin
+ for Phy in Combo_Phy'range loop
+ Registers.Set_Mask (Phy_Regs (Phy).PHY_MISC,
+ PHY_MISC_DE_TO_IO_COMP_PWR_DOWN);
+
+ Registers.Unset_Mask (Phy_Regs (Phy).PORT_COMP_DW0,
+ PORT_COMP_DW0_COMP_INIT);
+ end loop;
+ end All_Off;
+
+end HW.GFX.GMA.Combo_Phy;