gma g45: Read CDClk and calculate dot-clock limits
Numbers are taken from `intel_cdclk.c` of Linux' i915 driver.
We introduce three new procedures to the `Power_And_Clocks` interface:
o Limit_Dotclocks() limits the dot clocks of all pipe configs
according to the maximum supported CDClk. It also reports if
CDClk has to be switched for these configs.
o Update_CDClk() performs the CDClk switch if necessary. It may
further limit the dot clocks if the switch didn't succeed.
o Enable_CDClk() ensures that the CDClk is running. This may be
necessary to probe for DP displays when no pipes are active.
The latter two are no-ops for G45, as the CDClk runs at a fixed rate.
Dot clocks are limited to 90% of CDClk.
Change-Id: Ie50c0f8f51b3a0a6ed58c6461069c556cc92f51e
Signed-off-by: Nico Huber <nico.h@gmx.de>
Reviewed-on: https://review.coreboot.org/c/libgfxinit/+/35715
Reviewed-by: Matt DeVillier <matt.devillier@gmail.com>
Reviewed-by: Angel Pons <th3fanbus@gmail.com>
Reviewed-by: Arthur Heymans <arthur@aheymans.xyz>
diff --git a/common/g45/hw-gfx-gma-power_and_clocks.adb b/common/g45/hw-gfx-gma-power_and_clocks.adb
index d6ee9f9..0ecf09b 100644
--- a/common/g45/hw-gfx-gma-power_and_clocks.adb
+++ b/common/g45/hw-gfx-gma-power_and_clocks.adb
@@ -1,5 +1,6 @@
--
-- Copyright (C) 2016 secunet Security Networks AG
+-- Copyright (C) 2019 Nico Huber <nico.h@gmx.de>
--
-- 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
@@ -26,8 +27,91 @@
CLKCFG_FSB_1067 : constant Frequency_Type := 266_666_666;
CLKCFG_FSB_1333 : constant Frequency_Type := 333_333_333;
+ type Div_Array is array (0 .. 7) of Pos64;
+
+ procedure Get_VCO (VCO : out Int64; Divisors : out Div_Array)
+ is
+ G45_3200 : constant Div_Array := (12, 10, 8, 7, 5, 16, others => 1);
+ G45_4000 : constant Div_Array := (14, 12, 10, 8, 6, 20, others => 1);
+ G45_4800 : constant Div_Array := (20, 14, 12, 10, 8, 24, others => 1);
+ G45_5333 : constant Div_Array := (20, 16, 12, 12, 8, 28, others => 1);
+ G45_Divs : constant array (Natural range 0 .. 7) of Div_Array :=
+ (G45_3200, G45_4000, G45_5333, G45_4800, others => (others => 1));
+
+ GM45_2667 : constant Div_Array := (12, 8, others => 1);
+ GM45_3200 : constant Div_Array := (14, 10, others => 1);
+ GM45_4000 : constant Div_Array := (18, 12, others => 1);
+ GM45_5333 : constant Div_Array := (24, 16, others => 1);
+ GM45_Divs : constant array (Natural range 0 .. 7) of Div_Array :=
+ (0 => GM45_3200, 1 => GM45_4000, 2 => GM45_5333, 4 => GM45_2667,
+ others => (others => 1));
+
+ HPLLVCO : Word32;
+ VCO_Sel : Natural range 0 .. 7;
+ begin
+ if Config.Has_GMCH_Mobile_VCO then
+ Registers.Read (Registers.GMCH_HPLLVCO_MOBILE, HPLLVCO);
+ VCO_Sel := Natural (HPLLVCO and 7);
+ VCO :=
+ (case VCO_Sel is
+ when 0 => 3_200_000_000,
+ when 1 => 4_000_000_000,
+ when 2 => 5_333_333_333,
+ --when 3 => 6_400_000_000,
+ when 4 => 2_666_666_667,
+ --when 5 => 4_266_666_667,
+ when others => 0);
+ Divisors := GM45_Divs (VCO_Sel);
+ else
+ Registers.Read (Registers.GMCH_HPLLVCO, HPLLVCO);
+ VCO_Sel := Natural (HPLLVCO and 7);
+ VCO :=
+ (case VCO_Sel is
+ when 0 => 3_200_000_000,
+ when 1 => 4_000_000_000,
+ when 2 => 5_333_333_333,
+ when 3 => 4_800_000_000,
+ when others => 0);
+ Divisors := G45_Divs (VCO_Sel);
+ end if;
+ end Get_VCO;
+
+ procedure Get_CDClk (CDClk : out Config.CDClk_Range)
+ is
+ use type HW.Word16;
+
+ Tmp_Clk : Int64 := 0;
+
+ VCO : Int64;
+ Divisors : Div_Array;
+
+ GCFGC : Word16;
+ CDClk_Sel : Natural range 0 .. 7;
+ begin
+ if PCI_Usable then
+ Get_VCO (VCO, Divisors);
+ PCI_Read16 (GCFGC, 16#f0#);
+ if Config.Has_GMCH_Mobile_VCO then
+ CDClk_Sel := Natural (Shift_Right (GCFGC, 12) and 1);
+ else
+ CDClk_Sel := Natural (Shift_Right (GCFGC, 4) and 7);
+ end if;
+ Tmp_Clk := VCO / Divisors (CDClk_Sel);
+ end if;
+
+ if Tmp_Clk in Config.CDClk_Range then
+ CDClk := Tmp_Clk;
+ else
+ if Config.Has_GMCH_Mobile_VCO then
+ CDClk := 5_333_333_333 / 24;
+ else
+ CDClk := 5_333_333_333 / 28;
+ end if;
+ end if;
+ end Get_CDClk;
+
-- The Raw Freq is 1/4 of the FSB freq
- procedure Initialize
+ procedure Get_Raw_Clock (Raw_Clock : out Frequency_Type)
is
CLK_CFG : Word32;
type Freq_Sel is new Natural range 0 .. 7;
@@ -36,15 +120,35 @@
(Register => Registers.GMCH_CLKCFG,
Value => CLK_CFG);
case Freq_Sel (CLK_CFG and FSB_FREQ_SEL_MASK) is
- when 0 => Config.Raw_Clock := CLKCFG_FSB_1067;
- when 1 => Config.Raw_Clock := CLKCFG_FSB_533;
- when 2 => Config.Raw_Clock := CLKCFG_FSB_800;
- when 3 => Config.Raw_Clock := CLKCFG_FSB_667;
- when 4 => Config.Raw_Clock := CLKCFG_FSB_1333;
- when 5 => Config.Raw_Clock := CLKCFG_FSB_400;
- when 6 => Config.Raw_Clock := CLKCFG_FSB_1067;
- when 7 => Config.Raw_Clock := CLKCFG_FSB_1333;
+ when 0 => Raw_Clock := CLKCFG_FSB_1067;
+ when 1 => Raw_Clock := CLKCFG_FSB_533;
+ when 2 => Raw_Clock := CLKCFG_FSB_800;
+ when 3 => Raw_Clock := CLKCFG_FSB_667;
+ when 4 => Raw_Clock := CLKCFG_FSB_1333;
+ when 5 => Raw_Clock := CLKCFG_FSB_400;
+ when 6 => Raw_Clock := CLKCFG_FSB_1067;
+ when 7 => Raw_Clock := CLKCFG_FSB_1333;
end case;
+ end Get_Raw_Clock;
+
+ procedure Initialize
+ is
+ CDClk : Config.CDClk_Range;
+ begin
+ Get_CDClk (CDClk);
+ Config.CDClk := CDClk;
+ Config.Max_CDClk := CDClk;
+
+ Get_Raw_Clock (Config.Raw_Clock);
end Initialize;
+ procedure Limit_Dotclocks
+ (Configs : in out Pipe_Configs;
+ CDClk_Switch : out Boolean)
+ is
+ begin
+ Config_Helpers.Limit_Dotclocks (Configs, Config.CDClk * 90 / 100);
+ CDClk_Switch := False;
+ end Limit_Dotclocks;
+
end HW.GFX.GMA.Power_And_Clocks;
diff --git a/common/g45/hw-gfx-gma-power_and_clocks.ads b/common/g45/hw-gfx-gma-power_and_clocks.ads
index 8f6c13c..313239b 100644
--- a/common/g45/hw-gfx-gma-power_and_clocks.ads
+++ b/common/g45/hw-gfx-gma-power_and_clocks.ads
@@ -12,10 +12,22 @@
-- GNU General Public License for more details.
--
+with HW.GFX.GMA.Config_Helpers;
+
private package HW.GFX.GMA.Power_And_Clocks is
procedure Initialize;
+ procedure Limit_Dotclocks
+ (Configs : in out Pipe_Configs;
+ CDClk_Switch : out Boolean)
+ with
+ Post =>
+ not CDClk_Switch and
+ Config_Helpers.Stable_FB (Configs'Old, Configs);
+ procedure Update_CDClk (Configs : in out Pipe_Configs) is null;
+ procedure Enable_CDClk is null;
+
procedure Pre_All_Off is null;
procedure Post_All_Off is null;
diff --git a/common/hw-gfx-gma-config.ads.template b/common/hw-gfx-gma-config.ads.template
index f7312fe..481897c 100644
--- a/common/hw-gfx-gma-config.ads.template
+++ b/common/hw-gfx-gma-config.ads.template
@@ -125,6 +125,7 @@
Broxton_On : <genbool> := Gen >= Broxton;
Skylake_On : <genbool> := Gen >= Skylake;
+ GMCH_GM45 : <g45bool> := Gen_G45 and then CPU = GM45;
CPU_Ironlake : <ilkbool> := Gen_Ironlake and then CPU = Ironlake;
CPU_Sandybridge : <ilkbool> := Gen_Ironlake and then CPU = Sandybridge;
CPU_Ivybridge : <ilkbool> := Gen_Ironlake and then CPU = Ivybridge;
@@ -205,6 +206,7 @@
Has_FDI_RX_Power_Down : <genbool> := Gen_Haswell;
Has_GMCH_RawClk : <genbool> := Gen_G45;
+ Has_GMCH_Mobile_VCO : <g45bool> := GMCH_GM45;
----------- DDI: -------------
End_EDP_Training_Late : <genbool> := Gen_Haswell;
diff --git a/common/hw-gfx-gma-registers.ads b/common/hw-gfx-gma-registers.ads
index 81fb219..1d38ffd 100644
--- a/common/hw-gfx-gma-registers.ads
+++ b/common/hw-gfx-gma-registers.ads
@@ -70,6 +70,8 @@
UCGCTL1,
UCGCTL2,
GMCH_CLKCFG,
+ GMCH_HPLLVCO_MOBILE,
+ GMCH_HPLLVCO,
VCS_RING_BUFFER_TAIL,
VCS_RING_BUFFER_HEAD,
VCS_RING_BUFFER_STRT,
@@ -1617,7 +1619,9 @@
-- MCHBAR Mirror
- GMCH_CLKCFG => 16#01_0c00# / Register_Width);
+ GMCH_CLKCFG => 16#01_0c00# / Register_Width,
+ GMCH_HPLLVCO_MOBILE => 16#01_0c0f# / Register_Width,
+ GMCH_HPLLVCO => 16#01_0c38# / Register_Width);
subtype Registers_Index is Registers_Invalid_Index range
Registers_Invalid_Index'Succ (Invalid_Register) ..