| -- |
| -- 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; |