blob: fe10208e167627693b5039e569b2828539f14948 [file] [log] [blame]
--
-- 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.PCode;
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 : Frequency_Type) 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;
function Normalize_CDClk (CDClk : in Int64) return Config.CDClk_Range is
(if CDClk <= CDClk_Ref then CDClk_Ref
elsif CDClk <= 144_000_000 then 144_000_000
elsif CDClk <= 288_000_000 then 288_000_000
elsif CDClk <= 384_000_000 then 384_000_000
elsif CDClk <= 576_000_000 then 576_000_000
else 624_000_000);
procedure Get_Cur_CDClk (CDClk : out Config.CDClk_Range)
is
CDCLK_CTL : Word32;
begin
Registers.Read (Registers.CDCLK_CTL, CDCLK_CTL);
CDCLK_CTL := CDCLK_CTL and CDCLK_CTL_CD_FREQ_DECIMAL_MASK;
CDClk := Normalize_CDClk (Int64 (CDCLK_CTL) * 500_000 + 1_000_000);
end Get_Cur_CDClk;
procedure Set_CDClk (Freq_In : Frequency_Type)
is
Freq : constant Config.CDClk_Range := Normalize_CDClk (Freq_In);
VCO : constant Int64 :=
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);
Success : Boolean;
begin
PCode.Mailbox_Write
(MBox => BXT_PCODE_CDCLK_CONTROL,
Command => BXT_CDCLK_PREPARE_FOR_CHANGE,
Wait_Ready => True,
Success => Success);
if not Success then
pragma Debug (Debug.Put_Line
("ERROR: PCODE didn't acknowledge frequency change."));
return;
end if;
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));
PCode.Mailbox_Write
(MBox => BXT_PCODE_CDCLK_CONTROL,
Command => Word64 ((Freq + (25_000_000 - 1)) / 25_000_000));
Config.CDClk := Freq;
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);
Config.Max_CDClk := 624_000_000;
Get_Cur_CDClk (Config.CDClk);
Set_CDClk (Config.Default_CDClk_Freq);
Set_Mask (DBUF_CTL, DBUF_CTL_DBUF_POWER_REQUEST);
Wait_Set_Mask (DBUF_CTL, DBUF_CTL_DBUF_POWER_STATE);
Config.Raw_Clock := Config.Default_RawClk_Freq;
end Initialize;
procedure Limit_Dotclocks
(Configs : in out Pipe_Configs;
CDClk_Switch : out Boolean)
is
begin
Config_Helpers.Limit_Dotclocks (Configs, Config.Max_CDClk);
CDClk_Switch :=
Config.CDClk /= Normalize_CDClk
(Config_Helpers.Highest_Dotclock (Configs));
end Limit_Dotclocks;
procedure Update_CDClk (Configs : in out Pipe_Configs)
is
New_CDClk : constant Frequency_Type :=
Config_Helpers.Highest_Dotclock (Configs);
begin
Set_CDClk (New_CDClk);
Config_Helpers.Limit_Dotclocks (Configs, Config.CDClk);
end Update_CDClk;
procedure Enable_CDClk is
begin
-- CDClk_Ref means we have CDClk effectively disabled
if Config.CDClk = CDClk_Ref then
Set_CDClk (Config.Default_CDClk_Freq);
end if;
end Enable_CDClk;
end HW.GFX.GMA.Power_And_Clocks;