Initial upstream commit

The history contained unlicensed code so everything got squashed, sorry.

Change-Id: I9f5775208f9df6fb29074bf3bc498f68cb17b3a0
Signed-off-by: Nico Huber <nico.huber@secunet.com>
diff --git a/common/skylake/hw-gfx-gma-power_and_clocks_skylake.adb b/common/skylake/hw-gfx-gma-power_and_clocks_skylake.adb
new file mode 100644
index 0000000..521ef8b
--- /dev/null
+++ b/common/skylake/hw-gfx-gma-power_and_clocks_skylake.adb
@@ -0,0 +1,351 @@
+--
+-- Copyright (C) 2014-2016 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; version 2 of the License.
+--
+-- 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.Time;
+with HW.Debug;
+with HW.GFX.GMA.Config;
+with HW.GFX.GMA.Registers;
+with HW.GFX.GMA.Power_And_Clocks_Haswell;
+
+use type HW.Word64;
+
+package body HW.GFX.GMA.Power_And_Clocks_Skylake is
+
+   type Power_Domain is (MISC_IO, PW1, PW2, DDI_AE, DDI_B, DDI_C, DDI_D);
+   subtype Power_Well is Power_Domain range PW1 .. PW2;
+   subtype Dynamic_Domain is Power_Domain range PW2 .. DDI_D;
+
+   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_Domain_Values is array (Power_Domain) of Word32;
+   PWR_WELL_CTL_POWER_REQUEST : constant Power_Domain_Values :=
+     (MISC_IO  => 1 * 2 **  1,
+      DDI_AE   => 1 * 2 **  3,
+      DDI_B    => 1 * 2 **  5,
+      DDI_C    => 1 * 2 **  7,
+      DDI_D    => 1 * 2 **  9,
+      PW1      => 1 * 2 ** 29,
+      PW2      => 1 * 2 ** 31);
+   PWR_WELL_CTL_POWER_STATE : constant Power_Domain_Values :=
+     (MISC_IO  => 1 * 2 **  0,
+      DDI_AE   => 1 * 2 **  2,
+      DDI_B    => 1 * 2 **  4,
+      DDI_C    => 1 * 2 **  6,
+      DDI_D    => 1 * 2 **  8,
+      PW1      => 1 * 2 ** 28,
+      PW2      => 1 * 2 ** 30);
+
+   type Power_Well_Values is array (Power_Well) of Word32;
+   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;
+
+   ----------------------------------------------------------------------------
+
+   DPLL_CTRL1_DPLL0_LINK_RATE_MASK     : constant := 7 * 2 **  1;
+   DPLL_CTRL1_DPLL0_LINK_RATE_2700MHZ  : constant := 0 * 2 **  1;
+   DPLL_CTRL1_DPLL0_LINK_RATE_1350MHZ  : constant := 1 * 2 **  1;
+   DPLL_CTRL1_DPLL0_LINK_RATE_810MHZ   : constant := 2 * 2 **  1;
+   DPLL_CTRL1_DPLL0_LINK_RATE_1620MHZ  : constant := 3 * 2 **  1;
+   DPLL_CTRL1_DPLL0_LINK_RATE_1080MHZ  : constant := 4 * 2 **  1;
+   DPLL_CTRL1_DPLL0_LINK_RATE_2160MHZ  : constant := 5 * 2 **  1;
+   DPLL_CTRL1_DPLL0_OVERRIDE           : constant := 1 * 2 **  0;
+
+   LCPLL1_CTL_PLL_ENABLE               : constant := 1 * 2 ** 31;
+   LCPLL1_CTL_PLL_LOCK                 : constant := 1 * 2 ** 30;
+
+   ----------------------------------------------------------------------------
+
+   CDCLK_CTL_CD_FREQ_SELECT_MASK       : constant := 3 * 2 ** 26;
+   CDCLK_CTL_CD_FREQ_SELECT_450MHZ     : constant := 0 * 2 ** 26;
+   CDCLK_CTL_CD_FREQ_SELECT_540MHZ     : constant := 1 * 2 ** 26;
+   CDCLK_CTL_CD_FREQ_SELECT_337_5MHZ   : constant := 2 * 2 ** 26;
+   CDCLK_CTL_CD_FREQ_SELECT_675MHZ     : constant := 3 * 2 ** 26;
+   CDCLK_CTL_CD_FREQ_DECIMAL_MASK      : constant :=     16#7ff#;
+
+   SKL_PCODE_CDCLK_CONTROL             : constant := 7;
+   SKL_CDCLK_PREPARE_FOR_CHANGE        : constant := 3;
+   SKL_CDCLK_READY_FOR_CHANGE          : constant := 1;
+
+   GT_MAILBOX_READY                    : constant := 1 * 2 ** 31;
+
+   function CDCLK_CTL_CD_FREQ_DECIMAL
+     (Freq        : Positive;
+      Plus_Half   : Boolean)
+      return Word32 is
+   begin
+      return Word32 (2 * (Freq - 1)) or (if Plus_Half then 1 else 0);
+   end CDCLK_CTL_CD_FREQ_DECIMAL;
+
+   ----------------------------------------------------------------------------
+
+   procedure GT_Mailbox_Write (MBox : Word32; Value : Word64) is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Registers.Wait_Unset_Mask (Registers.GT_MAILBOX, GT_MAILBOX_READY);
+      Registers.Write
+        (Registers.GT_MAILBOX_DATA, Word32 (Value and 16#ffff_ffff#));
+      Registers.Write
+        (Registers.GT_MAILBOX_DATA_1, Word32 (Shift_Right (Value, 32)));
+      Registers.Write (Registers.GT_MAILBOX, GT_MAILBOX_READY or MBox);
+
+      Registers.Wait_Unset_Mask (Registers.GT_MAILBOX, GT_MAILBOX_READY);
+   end GT_Mailbox_Write;
+
+   ----------------------------------------------------------------------------
+
+   procedure PD_Off (PD : Power_Domain)
+   is
+      Ctl1, Ctl2, Ctl3, Ctl4 : Word32;
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Registers.Read (Registers.PWR_WELL_CTL_BIOS, Ctl1);
+      Registers.Read (Registers.PWR_WELL_CTL_DRIVER, Ctl2);
+      Registers.Read (Registers.PWR_WELL_CTL_KVMR, Ctl3);
+      Registers.Read (Registers.PWR_WELL_CTL_DEBUG, Ctl4);
+      pragma Debug (Registers.Posting_Read (Registers.PWR_WELL_CTL5)); --  Result for debugging only
+      pragma Debug (Registers.Posting_Read (Registers.PWR_WELL_CTL6)); --  Result for debugging only
+
+      if ((Ctl1 or Ctl2 or Ctl3 or Ctl4) and
+          PWR_WELL_CTL_POWER_REQUEST (PD)) /= 0
+      then
+         Registers.Wait_Set_Mask
+           (Register => Registers.PWR_WELL_CTL_DRIVER,
+            Mask     => PWR_WELL_CTL_POWER_STATE (PD));
+      end if;
+
+      if (Ctl1 and PWR_WELL_CTL_POWER_REQUEST (PD)) /= 0 then
+         Registers.Unset_Mask
+           (Register => Registers.PWR_WELL_CTL_BIOS,
+            Mask     => PWR_WELL_CTL_POWER_REQUEST (PD));
+      end if;
+
+      if (Ctl2 and PWR_WELL_CTL_POWER_REQUEST (PD)) /= 0 then
+         Registers.Unset_Mask
+           (Register => Registers.PWR_WELL_CTL_DRIVER,
+            Mask     => PWR_WELL_CTL_POWER_REQUEST (PD));
+      end if;
+   end PD_Off;
+
+   procedure PD_On (PD : Power_Domain)
+   with
+      Pre => True
+   is
+      Ctl1, Ctl2, Ctl3, Ctl4 : Word32;
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Registers.Read (Registers.PWR_WELL_CTL_BIOS, Ctl1);
+      Registers.Read (Registers.PWR_WELL_CTL_DRIVER, Ctl2);
+      Registers.Read (Registers.PWR_WELL_CTL_KVMR, Ctl3);
+      Registers.Read (Registers.PWR_WELL_CTL_DEBUG, Ctl4);
+      pragma Debug (Registers.Posting_Read (Registers.PWR_WELL_CTL5)); --  Result for debugging only
+      pragma Debug (Registers.Posting_Read (Registers.PWR_WELL_CTL6)); --  Result for debugging only
+
+      if ((Ctl1 or Ctl2 or Ctl3 or Ctl4) and
+          PWR_WELL_CTL_POWER_REQUEST (PD)) = 0
+      then
+         Registers.Wait_Unset_Mask
+           (Register => Registers.PWR_WELL_CTL_DRIVER,
+            Mask     => PWR_WELL_CTL_POWER_STATE (PD));
+      end if;
+
+      if (Ctl2 and PWR_WELL_CTL_POWER_REQUEST (PD)) = 0 then
+         Registers.Set_Mask
+           (Register => Registers.PWR_WELL_CTL_DRIVER,
+            Mask     => PWR_WELL_CTL_POWER_REQUEST (PD));
+         Registers.Wait_Set_Mask
+           (Register => Registers.PWR_WELL_CTL_DRIVER,
+            Mask     => PWR_WELL_CTL_POWER_STATE (PD));
+
+         if PD in Power_Well then
+            Registers.Wait_Set_Mask
+              (Register => Registers.FUSE_STATUS,
+               Mask     => FUSE_STATUS_PGx_DIST_STATUS (PD));
+         end if;
+      end if;
+   end PD_On;
+
+   function Need_PD (PD : Dynamic_Domain; Configs : Configs_Type) return Boolean
+   is
+   begin
+      return (case PD is
+         when DDI_AE    => Configs (Primary).Port = Internal or
+                           Configs (Secondary).Port = Internal or
+                           Configs (Tertiary).Port = Internal,
+         when DDI_B     => Configs (Primary).Port = Digital1 or
+                           Configs (Primary).Port = DP1 or
+                           Configs (Secondary).Port = Digital1 or
+                           Configs (Secondary).Port = DP1 or
+                           Configs (Tertiary).Port = Digital1 or
+                           Configs (Tertiary).Port = DP1,
+         when DDI_C     => Configs (Primary).Port = Digital2 or
+                           Configs (Primary).Port = DP2 or
+                           Configs (Secondary).Port = Digital2 or
+                           Configs (Secondary).Port = DP2 or
+                           Configs (Tertiary).Port = Digital2 or
+                           Configs (Tertiary).Port = DP2,
+         when DDI_D     => Configs (Primary).Port = Digital3 or
+                           Configs (Primary).Port = DP3 or
+                           Configs (Secondary).Port = Digital3 or
+                           Configs (Secondary).Port = DP3 or
+                           Configs (Tertiary).Port = Digital3 or
+                           Configs (Tertiary).Port = DP3,
+         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 Pre_All_Off is
+   begin
+      Power_And_Clocks_Haswell.PSR_Off;
+   end Pre_All_Off;
+
+   procedure Post_All_Off is
+   begin
+      for PD in reverse Dynamic_Domain loop
+         PD_Off (PD);
+      end loop;
+
+      Registers.Unset_Mask
+        (Register    => Registers.DBUF_CTL,
+         Mask        => DBUF_CTL_DBUF_POWER_REQUEST);
+      Registers.Wait_Unset_Mask
+        (Register    => Registers.DBUF_CTL,
+         Mask        => DBUF_CTL_DBUF_POWER_STATE);
+
+      Registers.Unset_Mask
+        (Register    => Registers.LCPLL1_CTL,
+         Mask        => LCPLL1_CTL_PLL_ENABLE);
+      Registers.Wait_Unset_Mask
+        (Register    => Registers.LCPLL1_CTL,
+         Mask        => LCPLL1_CTL_PLL_LOCK);
+
+      PD_Off (MISC_IO);
+      PD_Off (PW1);
+   end Post_All_Off;
+
+   procedure Initialize
+   is
+      CDClk_Change_Timeout : Time.T;
+      Timed_Out : Boolean;
+
+      MBox_Data0 : Word32;
+   begin
+      Registers.Set_Mask
+        (Register    => Registers.NDE_RSTWRN_OPT,
+         Mask        => NDE_RSTWRN_OPT_RST_PCH_Handshake_En);
+
+      Registers.Wait_Set_Mask
+        (Register    => Registers.FUSE_STATUS,
+         Mask        => FUSE_STATUS_PG0_DIST_STATUS);
+      PD_On (PW1);
+      PD_On (MISC_IO);
+
+      Registers.Write
+        (Register    => Registers.CDCLK_CTL,
+         Value       => CDCLK_CTL_CD_FREQ_SELECT_337_5MHZ or
+                        CDCLK_CTL_CD_FREQ_DECIMAL (337, True));
+      -- TODO: Set to preferred eDP rate:
+      -- Registers.Unset_And_Set_Mask
+      --   (Register    => Registers.DPLL_CTRL1,
+      --    Unset_Mask  => DPLL_CTRL1_DPLL0_LINK_RATE_MASK,
+      --    Set_Mask    => DPLL_CTRL1_DPLL0_LINK_RATE_...);
+      Registers.Set_Mask
+        (Register    => Registers.LCPLL1_CTL,
+         Mask        => LCPLL1_CTL_PLL_ENABLE);
+      Registers.Wait_Set_Mask
+        (Register    => Registers.LCPLL1_CTL,
+         Mask        => LCPLL1_CTL_PLL_LOCK);
+
+      CDClk_Change_Timeout := Time.MS_From_Now (3);
+      loop
+         GT_Mailbox_Write
+           (MBox        => SKL_PCODE_CDCLK_CONTROL,
+            Value       => SKL_CDCLK_PREPARE_FOR_CHANGE);
+         Timed_Out := Time.Timed_Out (CDClk_Change_Timeout);
+         Registers.Read (Registers.GT_MAILBOX_DATA, MBox_Data0);
+         if (MBox_Data0 and SKL_CDCLK_READY_FOR_CHANGE) =
+            SKL_CDCLK_READY_FOR_CHANGE
+         then
+            Timed_Out := False;
+            exit;
+         end if;
+         exit when Timed_Out;
+      end loop;
+
+      if not Timed_Out then
+         GT_Mailbox_Write
+           (MBox        => SKL_PCODE_CDCLK_CONTROL,
+            Value       => 16#0000_0000#);   -- 0 - 337.5MHz
+                                             -- 1 - 450.0MHz
+                                             -- 2 - 540.0MHz
+                                             -- 3 - 675.0MHz
+         Registers.Set_Mask
+           (Register    => Registers.DBUF_CTL,
+            Mask        => DBUF_CTL_DBUF_POWER_REQUEST);
+         Registers.Wait_Set_Mask
+           (Register    => Registers.DBUF_CTL,
+            Mask        => DBUF_CTL_DBUF_POWER_STATE);
+      end if;
+   end Initialize;
+
+   procedure Power_Set_To (Configs : Configs_Type) 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 : Configs_Type) 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 : Configs_Type)
+   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;
+
+end HW.GFX.GMA.Power_And_Clocks_Skylake;