gma broxton: Start off with power domains and CDClk

It's close to the respective code for Skylake but still different
enough for a separate implementation. We start with a default CDClk
of 288MHz which is enough for resolutions up to 2560x1600.

Change-Id: I44364191236f421b2b89c9a019a50713f7c20525
Signed-off-by: Nico Huber <nico.huber@secunet.com>
Reviewed-on: https://review.coreboot.org/18243
Tested-by: Nico Huber <nico.h@gmx.de>
Reviewed-by: Arthur Heymans <arthur@aheymans.xyz>
diff --git a/common/broxton/Makefile.inc b/common/broxton/Makefile.inc
index 247fca1..c4360ec 100644
--- a/common/broxton/Makefile.inc
+++ b/common/broxton/Makefile.inc
@@ -1,4 +1,6 @@
+gfxinit-y += hw-gfx-gma-ddi_phy.ads
 gfxinit-y += hw-gfx-gma-plls.adb
 gfxinit-y += hw-gfx-gma-plls.ads
+gfxinit-y += hw-gfx-gma-power_and_clocks.adb
 gfxinit-y += hw-gfx-gma-power_and_clocks.ads
 gfxinit-y += hw-gfx-gma-spll.ads
diff --git a/common/broxton/hw-gfx-gma-ddi_phy.ads b/common/broxton/hw-gfx-gma-ddi_phy.ads
new file mode 100644
index 0000000..89f19f0
--- /dev/null
+++ b/common/broxton/hw-gfx-gma-ddi_phy.ads
@@ -0,0 +1,22 @@
+--
+-- Copyright (C) 2017 secunet Security Networks AG
+--
+-- 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.
+--
+
+private package HW.GFX.GMA.DDI_Phy is
+
+   type T is (BC, A);
+
+   procedure Power_On (Phy : T) is null;
+   procedure Power_Off (Phy : T) is null;
+
+end HW.GFX.GMA.DDI_Phy;
diff --git a/common/broxton/hw-gfx-gma-power_and_clocks.adb b/common/broxton/hw-gfx-gma-power_and_clocks.adb
new file mode 100644
index 0000000..12ab308
--- /dev/null
+++ b/common/broxton/hw-gfx-gma-power_and_clocks.adb
@@ -0,0 +1,321 @@
+--
+-- Copyright (C) 2014-2017 secunet Security Networks AG
+--
+-- 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.Config;
+with HW.GFX.GMA.Registers;
+with HW.GFX.GMA.Power_And_Clocks_Haswell;
+with HW.GFX.GMA.DDI_Phy;
+
+use HW.GFX.GMA.Registers;
+
+package body HW.GFX.GMA.Power_And_Clocks is
+
+   type Power_Domain is (PW1, PW2, DDI_A, DDI_BC);
+   subtype Power_Well is Power_Domain range PW1 .. PW2;
+   subtype Dynamic_Domain is Power_Domain range PW2 .. DDI_BC;
+   subtype DDI_Domain is Dynamic_Domain range DDI_A .. DDI_BC;
+
+   type DDI_Phy_Array is array (DDI_Domain) of DDI_Phy.T;
+   Phy : constant DDI_Phy_Array := (DDI_A => DDI_Phy.A, DDI_BC => DDI_Phy.BC);
+
+   NDE_RSTWRN_OPT_RST_PCH_Handshake_En : constant := 1 * 2 **  4;
+
+   FUSE_STATUS_DOWNLOAD_STATUS         : constant := 1 * 2 ** 31;
+   FUSE_STATUS_PG0_DIST_STATUS         : constant := 1 * 2 ** 27;
+
+   type Power_Well_Values is array (Power_Well) of Word32;
+   PWR_WELL_CTL_POWER_REQUEST : constant Power_Well_Values :=
+     (PW1      => 1 * 2 ** 29,
+      PW2      => 1 * 2 ** 31);
+   PWR_WELL_CTL_POWER_STATE : constant Power_Well_Values :=
+     (PW1      => 1 * 2 ** 28,
+      PW2      => 1 * 2 ** 30);
+
+   FUSE_STATUS_PGx_DIST_STATUS : constant Power_Well_Values :=
+     (PW1   => 1 * 2 ** 26,
+      PW2   => 1 * 2 ** 25);
+
+   DBUF_CTL_DBUF_POWER_REQUEST         : constant := 1 * 2 ** 31;
+   DBUF_CTL_DBUF_POWER_STATE           : constant := 1 * 2 ** 30;
+
+   ----------------------------------------------------------------------------
+
+   BXT_DE_PLL_RATIO_MASK               : constant :=      16#ff#;
+   BXT_DE_PLL_PLL_ENABLE               : constant := 1 * 2 ** 31;
+   BXT_DE_PLL_PLL_LOCK                 : constant := 1 * 2 ** 30;
+
+   CDCLK_CD2X_DIV_SEL_MASK             : constant := 3 * 2 ** 22;
+   CDCLK_CD2X_DIV_SEL_1                : constant := 0 * 2 ** 22;
+   CDCLK_CD2X_DIV_SEL_1_5              : constant := 1 * 2 ** 22;
+   CDCLK_CD2X_DIV_SEL_2                : constant := 2 * 2 ** 22;
+   CDCLK_CD2X_DIV_SEL_4                : constant := 3 * 2 ** 22;
+   CDCLK_CD2X_PIPE_NONE                : constant := 3 * 2 ** 20;
+   CDCLK_CD2X_SSA_PRECHARGE_ENABLE     : constant := 1 * 2 ** 16;
+   CDCLK_CTL_CD_FREQ_DECIMAL_MASK      : constant :=     16#7ff#;
+
+   function CDCLK_CTL_CD_FREQ_DECIMAL (Freq : Positive) return Word32 is
+   begin
+      return Word32 (2 * (Freq / 1_000_000 - 1));
+   end CDCLK_CTL_CD_FREQ_DECIMAL;
+
+   BXT_PCODE_CDCLK_CONTROL             : constant := 16#17#;
+   BXT_CDCLK_PREPARE_FOR_CHANGE        : constant := 16#8000_0000#;
+
+   ----------------------------------------------------------------------------
+
+   procedure PW_Off (PD : Power_Well)
+   is
+      Ctl1, Ctl2, Ctl3, Ctl4 : Word32;
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Read (PWR_WELL_CTL_BIOS, Ctl1);
+      Read (PWR_WELL_CTL_DRIVER, Ctl2);
+      Read (PWR_WELL_CTL_KVMR, Ctl3);
+      Read (PWR_WELL_CTL_DEBUG, Ctl4);
+      pragma Debug (Posting_Read (PWR_WELL_CTL5)); --  Result for debugging only
+      pragma Debug (Posting_Read (PWR_WELL_CTL6)); --  Result for debugging only
+
+      if ((Ctl1 or Ctl2 or Ctl3 or Ctl4) and
+          PWR_WELL_CTL_POWER_REQUEST (PD)) /= 0
+      then
+         Wait_Set_Mask (PWR_WELL_CTL_DRIVER, PWR_WELL_CTL_POWER_STATE (PD));
+      end if;
+
+      if (Ctl1 and PWR_WELL_CTL_POWER_REQUEST (PD)) /= 0 then
+         Unset_Mask (PWR_WELL_CTL_BIOS, PWR_WELL_CTL_POWER_REQUEST (PD));
+      end if;
+
+      if (Ctl2 and PWR_WELL_CTL_POWER_REQUEST (PD)) /= 0 then
+         Unset_Mask (PWR_WELL_CTL_DRIVER, PWR_WELL_CTL_POWER_REQUEST (PD));
+      end if;
+   end PW_Off;
+
+   procedure PW_On (PD : Power_Well)
+   with
+      Pre => True
+   is
+      Ctl1, Ctl2, Ctl3, Ctl4 : Word32;
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Read (PWR_WELL_CTL_BIOS, Ctl1);
+      Read (PWR_WELL_CTL_DRIVER, Ctl2);
+      Read (PWR_WELL_CTL_KVMR, Ctl3);
+      Read (PWR_WELL_CTL_DEBUG, Ctl4);
+      pragma Debug (Posting_Read (PWR_WELL_CTL5)); --  Result for debugging only
+      pragma Debug (Posting_Read (PWR_WELL_CTL6)); --  Result for debugging only
+
+      if ((Ctl1 or Ctl2 or Ctl3 or Ctl4) and
+          PWR_WELL_CTL_POWER_REQUEST (PD)) = 0
+      then
+         Wait_Unset_Mask (PWR_WELL_CTL_DRIVER, PWR_WELL_CTL_POWER_STATE (PD));
+      end if;
+
+      if (Ctl2 and PWR_WELL_CTL_POWER_REQUEST (PD)) = 0 then
+         Set_Mask (PWR_WELL_CTL_DRIVER, PWR_WELL_CTL_POWER_REQUEST (PD));
+         Wait_Set_Mask (PWR_WELL_CTL_DRIVER, PWR_WELL_CTL_POWER_STATE (PD));
+
+         Wait_Set_Mask (FUSE_STATUS, FUSE_STATUS_PGx_DIST_STATUS (PD));
+      end if;
+   end PW_On;
+
+   procedure PD_On (PD : Power_Domain) is
+   begin
+      if PD in Power_Well then
+         PW_On (PD);
+      else
+         DDI_Phy.Power_On (Phy (PD));
+      end if;
+   end PD_On;
+
+   procedure PD_Off (PD : Power_Domain) is
+   begin
+      if PD in Power_Well then
+         PW_Off (PD);
+      else
+         DDI_Phy.Power_Off (Phy (PD));
+      end if;
+   end PD_Off;
+
+   function Need_PD (PD : Dynamic_Domain; Configs : Pipe_Configs) return Boolean
+   is
+   begin
+      return (case PD is
+         when DDI_A     => Configs (Primary).Port = Internal or
+                           Configs (Secondary).Port = Internal or
+                           Configs (Tertiary).Port = Internal,
+         when DDI_BC    => Configs (Primary).Port = HDMI1 or
+                           Configs (Primary).Port = DP1 or
+                           Configs (Secondary).Port = HDMI1 or
+                           Configs (Secondary).Port = DP1 or
+                           Configs (Tertiary).Port = HDMI1 or
+                           Configs (Tertiary).Port = DP1 or
+                           Configs (Primary).Port = HDMI2 or
+                           Configs (Primary).Port = DP2 or
+                           Configs (Secondary).Port = HDMI2 or
+                           Configs (Secondary).Port = DP2 or
+                           Configs (Tertiary).Port = HDMI2 or
+                           Configs (Tertiary).Port = DP2,
+         when PW2       => (Configs (Primary).Port /= Disabled and
+                            Configs (Primary).Port /= Internal) or
+                           Configs (Secondary).Port /= Disabled or
+                           Configs (Tertiary).Port /= Disabled);
+   end Need_PD;
+
+   procedure Power_Set_To (Configs : Pipe_Configs) is
+   begin
+      for PD in reverse Dynamic_Domain loop
+         if not Need_PD (PD, Configs) then
+            PD_Off (PD);
+         end if;
+      end loop;
+      for PD in Dynamic_Domain loop
+         if Need_PD (PD, Configs) then
+            PD_On (PD);
+         end if;
+      end loop;
+   end Power_Set_To;
+
+   procedure Power_Up (Old_Configs, New_Configs : Pipe_Configs) is
+   begin
+      for PD in Dynamic_Domain loop
+         if not Need_PD (PD, Old_Configs) and Need_PD (PD, New_Configs) then
+            PD_On (PD);
+         end if;
+      end loop;
+   end Power_Up;
+
+   procedure Power_Down (Old_Configs, Tmp_Configs, New_Configs : Pipe_Configs)
+   is
+   begin
+      for PD in reverse Dynamic_Domain loop
+         if (Need_PD (PD, Old_Configs) or Need_PD (PD, Tmp_Configs)) and
+            not Need_PD (PD, New_Configs)
+         then
+            PD_Off (PD);
+         end if;
+      end loop;
+   end Power_Down;
+
+   ----------------------------------------------------------------------------
+
+   CDClk_Ref : constant := 19_200_000;
+
+   procedure Set_CDClk (Freq : Positive)
+   with
+      Pre =>
+         Freq =   CDClk_Ref or Freq = 144_000_000 or Freq = 288_000_000 or
+         Freq = 384_000_000 or Freq = 576_000_000 or Freq = 624_000_000
+   is
+      VCO : constant Natural :=
+         CDClk_Ref *
+           (if Freq = CDClk_Ref then
+               0
+            elsif Freq = 624_000_000 then
+               65
+            else
+               60);
+      CDCLK_CD2X_Div_Sel : constant Word32 :=
+        (case VCO / Freq is   -- CDClk = VCO / 2 / Div
+            when 2      => CDCLK_CD2X_DIV_SEL_1,
+            when 3      => CDCLK_CD2X_DIV_SEL_1_5,
+            when 4      => CDCLK_CD2X_DIV_SEL_2,
+            when 8      => CDCLK_CD2X_DIV_SEL_4,
+            when others => CDCLK_CD2X_DIV_SEL_1);  -- for CDClk = CDClk_Ref
+      CDCLK_CD2X_SSA_Precharge : constant Word32 :=
+        (if Freq >= 500_000_000 then CDCLK_CD2X_SSA_PRECHARGE_ENABLE else 0);
+   begin
+      Power_And_Clocks_Haswell.GT_Mailbox_Write
+        (MBox        => BXT_PCODE_CDCLK_CONTROL,
+         Value       => BXT_CDCLK_PREPARE_FOR_CHANGE);
+
+      Write
+        (Register => BXT_DE_PLL_ENABLE,
+         Value    => 16#0000_0000#);
+      Wait_Unset_Mask
+        (Register => BXT_DE_PLL_ENABLE,
+         Mask     => BXT_DE_PLL_PLL_LOCK,
+         TOut_MS  => 1);   -- 200us
+
+      Unset_And_Set_Mask
+        (Register    => BXT_DE_PLL_CTL,
+         Mask_Unset  => BXT_DE_PLL_RATIO_MASK,
+         Mask_Set    => Word32 (VCO / CDClk_Ref));
+      Write
+        (Register => BXT_DE_PLL_ENABLE,
+         Value    => BXT_DE_PLL_PLL_ENABLE);
+      Wait_Set_Mask
+        (Register => BXT_DE_PLL_ENABLE,
+         Mask     => BXT_DE_PLL_PLL_LOCK,
+         TOut_MS  => 1);   -- 200us
+
+      Write
+        (Register => CDCLK_CTL,
+         Value    => CDCLK_CD2X_Div_Sel or
+                     CDCLK_CD2X_PIPE_NONE or
+                     CDCLK_CD2X_SSA_Precharge or
+                     CDCLK_CTL_CD_FREQ_DECIMAL (Freq));
+
+      Power_And_Clocks_Haswell.GT_Mailbox_Write
+        (MBox        => BXT_PCODE_CDCLK_CONTROL,
+         Value       => Word32 ((Freq + (25_000_000 - 1)) / 25_000_000));
+   end Set_CDClk;
+
+   ----------------------------------------------------------------------------
+
+   procedure Pre_All_Off is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+      Power_And_Clocks_Haswell.PSR_Off;
+   end Pre_All_Off;
+
+   procedure Post_All_Off is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      for PD in reverse Dynamic_Domain loop
+         PD_Off (PD);
+      end loop;
+
+      Unset_Mask (DBUF_CTL, DBUF_CTL_DBUF_POWER_REQUEST);
+      Wait_Unset_Mask (DBUF_CTL, DBUF_CTL_DBUF_POWER_STATE);
+
+      -- Linux' i915 never keeps the PLL disabled but runs it
+      -- at a "ratio" of 0 with CDClk at its reference clock.
+      Set_CDClk (CDClk_Ref);
+
+      PW_Off (PW1);
+   end Post_All_Off;
+
+   procedure Initialize is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      -- no PCH for Broxton
+      Unset_Mask (NDE_RSTWRN_OPT, NDE_RSTWRN_OPT_RST_PCH_Handshake_En);
+
+      Wait_Set_Mask (FUSE_STATUS, FUSE_STATUS_PG0_DIST_STATUS);
+      PW_On (PW1);
+
+      Set_CDClk (Positive (Config.Default_CDClk_Freq));
+
+      Set_Mask (DBUF_CTL, DBUF_CTL_DBUF_POWER_REQUEST);
+      Wait_Set_Mask (DBUF_CTL, DBUF_CTL_DBUF_POWER_STATE);
+   end Initialize;
+
+end HW.GFX.GMA.Power_And_Clocks;
diff --git a/common/broxton/hw-gfx-gma-power_and_clocks.ads b/common/broxton/hw-gfx-gma-power_and_clocks.ads
index 253d537..86961f6 100644
--- a/common/broxton/hw-gfx-gma-power_and_clocks.ads
+++ b/common/broxton/hw-gfx-gma-power_and_clocks.ads
@@ -14,13 +14,13 @@
 
 private package HW.GFX.GMA.Power_And_Clocks is
 
-   procedure Pre_All_Off is null;
-   procedure Post_All_Off is null;
+   procedure Pre_All_Off;
+   procedure Post_All_Off;
 
-   procedure Initialize is null;
+   procedure Initialize;
 
-   procedure Power_Set_To (Configs : Pipe_Configs) is null;
-   procedure Power_Up (Old_Configs, New_Configs : Pipe_Configs) is null;
-   procedure Power_Down (Old_Configs, Tmp_Configs, New_Configs : Pipe_Configs) is null;
+   procedure Power_Set_To (Configs : Pipe_Configs);
+   procedure Power_Up (Old_Configs, New_Configs : Pipe_Configs);
+   procedure Power_Down (Old_Configs, Tmp_Configs, New_Configs : Pipe_Configs);
 
 end HW.GFX.GMA.Power_And_Clocks;