gma broxton: Implement DDI PHY power handling

DDI PHYs is a concept common to current Atom processor series. It
seems the PHYs are implemented on the same die as the graphics core
but still need to be configured separately. Based on the assumption
that we start with disabled PHYs and it was always us if they are
enabled, we only have to do a small share of what Linux' i915 does.

v2: Wait for GRC done only if we want to copy its results.

Change-Id: I1e59f80daa08dc64b8c3dff34202ace5dd4c5f73
Signed-off-by: Nico Huber <nico.huber@secunet.com>
Reviewed-on: https://review.coreboot.org/18422
Tested-by: Nico Huber <nico.h@gmx.de>
Reviewed-by: Arthur Heymans <arthur@aheymans.xyz>
This commit is contained in:
Nico Huber
2017-02-03 12:17:28 +01:00
committed by Nico Huber
parent 408204409b
commit f6266004f9
4 changed files with 320 additions and 3 deletions

View File

@@ -1,3 +1,4 @@
gfxinit-y += hw-gfx-gma-ddi_phy.adb
gfxinit-y += hw-gfx-gma-ddi_phy.ads
gfxinit-y += hw-gfx-gma-plls.adb
gfxinit-y += hw-gfx-gma-plls.ads

View File

@@ -0,0 +1,262 @@
--
-- 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.
--
with GNAT.Source_Info;
with HW.Debug;
with HW.GFX.GMA.Registers;
use HW.GFX.GMA.Registers;
package body HW.GFX.GMA.DDI_Phy is
subtype Dual_Channel is T range BC .. BC;
type DDI_Range is record
First : DDI_Phy_Port;
Last : DDI_Phy_Port;
end record;
type DDI_Range_Array is array (T) of DDI_Range;
DDIs : constant DDI_Range_Array :=
(A => (DIGI_A, DIGI_A),
BC => (DIGI_B, DIGI_C));
----------------------------------------------------------------------------
type CL1CM is record
DW0 : Registers_Index;
DW9 : Registers_Index;
DW10 : Registers_Index;
DW28 : Registers_Index;
DW30 : Registers_Index;
end record;
type CL1CM_Array is array (T) of CL1CM;
PORT_CL1CM : constant CL1CM_Array :=
(A =>
(DW0 => BXT_PORT_CL1CM_DW0_A,
DW9 => BXT_PORT_CL1CM_DW9_A,
DW10 => BXT_PORT_CL1CM_DW10_A,
DW28 => BXT_PORT_CL1CM_DW28_A,
DW30 => BXT_PORT_CL1CM_DW30_A),
BC =>
(DW0 => BXT_PORT_CL1CM_DW0_BC,
DW9 => BXT_PORT_CL1CM_DW9_BC,
DW10 => BXT_PORT_CL1CM_DW10_BC,
DW28 => BXT_PORT_CL1CM_DW28_BC,
DW30 => BXT_PORT_CL1CM_DW30_BC));
type CL2CM is record
DW6 : Registers_Index;
end record;
type CL2CM_Array is array (Dual_Channel) of CL2CM;
PORT_CL2CM : constant CL2CM_Array :=
(BC => (DW6 => BXT_PORT_CL2CM_DW6_BC));
type Port_Ref_Regs is record
DW3 : Registers_Index;
DW6 : Registers_Index;
DW8 : Registers_Index;
end record;
type Port_Ref_Array is array (T) of Port_Ref_Regs;
PORT_REF : constant Port_Ref_Array :=
(A =>
(DW3 => BXT_PORT_REF_DW3_A,
DW6 => BXT_PORT_REF_DW6_A,
DW8 => BXT_PORT_REF_DW8_A),
BC =>
(DW3 => BXT_PORT_REF_DW3_BC,
DW6 => BXT_PORT_REF_DW6_BC,
DW8 => BXT_PORT_REF_DW8_BC));
type Regs is array (T) of Registers_Index;
PHY_CTL_FAMILY : constant Regs :=
(A => BXT_PHY_CTL_FAM_EDP, BC => BXT_PHY_CTL_FAM_DDI);
type DDI_Regs is array (DDI_Phy_Port) of Registers_Index;
PHY_CTL : constant DDI_Regs :=
(DIGI_A => BXT_PHY_CTL_A,
DIGI_B => BXT_PHY_CTL_B,
DIGI_C => BXT_PHY_CTL_C);
----------------------------------------------------------------------------
type Values is array (T) of Word32;
GT_DISPLAY_POWER_ON : constant Values :=
(A => 1 * 2 ** 1,
BC => 1 * 2 ** 0);
PORT_CL1CM_PHY_POWER_GOOD : constant := 1 * 2 ** 16;
PORT_CL1CM_PHY_RESERVED : constant := 1 * 2 ** 7;
PORT_CL1CM_IREFxRC_OFFSET_SHIFT : constant := 8;
PORT_CL1CM_IREFxRC_OFFSET_MASK : constant := 16#ff# * 2 ** 8;
PORT_CL1CM_OCL1_POWER_DOWN_EN : constant := 1 * 2 ** 23;
PORT_CL1CM_OLDO_DYN_POWER_DOWN_EN : constant := 1 * 2 ** 22;
PORT_CL1CM_SUS_CLK_CONFIG : constant := 3 * 2 ** 0;
PORT_CL2CM_OLDO_DYN_POWER_DOWN_EN : constant := 1 * 2 ** 28;
PORT_REF_GRC_DONE : constant := 1 * 2 ** 22;
PORT_REF_GRC_CODE_SHIFT : constant := 24;
PORT_REF_GRC_FAST_SHIFT : constant := 16;
PORT_REF_GRC_SLOW_SHIFT : constant := 8;
PORT_REF_GRC_DISABLE : constant := 1 * 2 ** 15;
PORT_REF_GRC_READY_OVERRIDE : constant := 1 * 2 ** 1;
PHY_CTL_FAM_CMN_RESET_DIS : constant := 1 * 2 ** 31;
PHY_CTL_CMNLANE_POWERDOWN_ACK : constant := 1 * 2 ** 10;
----------------------------------------------------------------------------
procedure Is_Enabled (Phy : in T; Enabled : out Boolean)
is
Phy_Pwr : Word32;
begin
pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
Is_Set_Mask (BXT_P_CR_GT_DISP_PWRON, GT_DISPLAY_POWER_ON (Phy), Enabled);
if Enabled then
Read (PORT_CL1CM (Phy).DW0, Phy_Pwr);
Enabled :=
(Phy_Pwr and (PORT_CL1CM_PHY_POWER_GOOD or PORT_CL1CM_PHY_RESERVED))
= PORT_CL1CM_PHY_POWER_GOOD;
pragma Debug (not Enabled, Debug.Put_Line ("DDI PHY power unsettled"));
end if;
if Enabled then
Is_Set_Mask (PHY_CTL_FAMILY (Phy), PHY_CTL_FAM_CMN_RESET_DIS, Enabled);
pragma Debug (not Enabled, Debug.Put_Line ("DDI PHY still in reset"));
end if;
for DDI in DDIs (Phy).First .. DDIs (Phy).Last loop
if Enabled then
declare
Common_Lane_Powerdown : Boolean;
begin
Is_Set_Mask
(Register => PHY_CTL (DDI),
Mask => PHY_CTL_CMNLANE_POWERDOWN_ACK,
Result => Common_Lane_Powerdown);
Enabled := not Common_Lane_Powerdown;
pragma Debug
(not Enabled, Debug.Put_Line ("Common lane powered down"));
end;
end if;
end loop;
end Is_Enabled;
procedure Power_On_Phy (Phy : T)
with
Pre => True
is
begin
pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
Set_Mask (BXT_P_CR_GT_DISP_PWRON, GT_DISPLAY_POWER_ON (Phy));
Wait
(Register => PORT_CL1CM (Phy).DW0,
Mask => PORT_CL1CM_PHY_POWER_GOOD or
PORT_CL1CM_PHY_RESERVED,
Value => PORT_CL1CM_PHY_POWER_GOOD,
TOut_MS => 1); -- 50~100us
Unset_And_Set_Mask
(Register => PORT_CL1CM (Phy).DW9,
Mask_Unset => PORT_CL1CM_IREFxRC_OFFSET_MASK,
Mask_Set => Shift_Left
(16#e4#, PORT_CL1CM_IREFxRC_OFFSET_SHIFT));
Unset_And_Set_Mask
(Register => PORT_CL1CM (Phy).DW10,
Mask_Unset => PORT_CL1CM_IREFxRC_OFFSET_MASK,
Mask_Set => Shift_Left
(16#e4#, PORT_CL1CM_IREFxRC_OFFSET_SHIFT));
Set_Mask
(Register => PORT_CL1CM (Phy).DW28,
Mask => PORT_CL1CM_OCL1_POWER_DOWN_EN or
PORT_CL1CM_OLDO_DYN_POWER_DOWN_EN or
PORT_CL1CM_SUS_CLK_CONFIG);
if Phy in Dual_Channel then
Set_Mask (PORT_CL2CM (Phy).DW6, PORT_CL2CM_OLDO_DYN_POWER_DOWN_EN);
end if;
if Phy = BC then
declare
GRC_Val : Word32;
begin
-- take RCOMP calibration result from A
Wait_Set_Mask (PORT_REF (A).DW3, PORT_REF_GRC_DONE, TOut_MS => 10);
Read (PORT_REF (A).DW6, GRC_Val);
GRC_Val := Shift_Right (GRC_Val, PORT_REF_GRC_CODE_SHIFT);
-- use it for BC too
GRC_Val := Shift_Left (GRC_Val, PORT_REF_GRC_FAST_SHIFT) or
Shift_Left (GRC_Val, PORT_REF_GRC_SLOW_SHIFT) or
GRC_Val;
Write (PORT_REF (Phy).DW6, GRC_Val);
Set_Mask
(Register => PORT_REF (Phy).DW8,
Mask => PORT_REF_GRC_DISABLE or
PORT_REF_GRC_READY_OVERRIDE);
end;
end if;
Set_Mask (PHY_CTL_FAMILY (Phy), PHY_CTL_FAM_CMN_RESET_DIS);
end Power_On_Phy;
procedure Power_On (Phy : T)
is
Phy_A_Enabled : Boolean := False;
Enabled : Boolean;
begin
pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
Is_Enabled (Phy, Enabled);
pragma Debug (Enabled, Debug.Put_Line ("DDI PHY already enabled"));
if not Enabled then
if Phy = BC then
-- PHY BC needs RCOMP calibration results from PHY A
Is_Enabled (A, Phy_A_Enabled);
if not Phy_A_Enabled then
Power_On_Phy (A);
end if;
end if;
Power_On_Phy (Phy);
if Phy = BC and then not Phy_A_Enabled then
Power_Off (A);
end if;
end if;
end Power_On;
procedure Power_Off (Phy : T) is
begin
pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
Unset_Mask (PHY_CTL_FAMILY (Phy), PHY_CTL_FAM_CMN_RESET_DIS);
Unset_Mask (BXT_P_CR_GT_DISP_PWRON, GT_DISPLAY_POWER_ON (Phy));
end Power_Off;
end HW.GFX.GMA.DDI_Phy;

View File

@@ -16,7 +16,9 @@ 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;
procedure Power_On (Phy : T);
procedure Power_Off (Phy : T);
subtype DDI_Phy_Port is GPU_Port range DIGI_A .. DIGI_C;
end HW.GFX.GMA.DDI_Phy;

View File

@@ -212,6 +212,11 @@ is
DP_TP_STATUS_E,
SRD_CTL,
SRD_STATUS,
BXT_PHY_CTL_A,
BXT_PHY_CTL_B,
BXT_PHY_CTL_C,
BXT_PHY_CTL_FAM_EDP,
BXT_PHY_CTL_FAM_DDI,
AUD_VID_DID,
PFA_WIN_POS,
PFA_WIN_SZ,
@@ -237,6 +242,9 @@ is
PS_WIN_POS_1_C,
PS_WIN_SZ_1_C,
PS_CTRL_1_C,
BXT_PORT_CL1CM_DW0_BC,
BXT_PORT_CL1CM_DW9_BC,
BXT_PORT_CL1CM_DW10_BC,
DPLL1_CFGR1,
DPLL1_CFGR2,
DPLL2_CFGR1,
@@ -246,6 +254,12 @@ is
DPLL_CTRL1,
DPLL_CTRL2,
DPLL_STATUS,
BXT_PORT_CL1CM_DW28_BC,
BXT_PORT_CL1CM_DW30_BC,
BXT_PORT_REF_DW3_BC,
BXT_PORT_REF_DW6_BC,
BXT_PORT_REF_DW8_BC,
BXT_PORT_CL2CM_DW6_BC,
BXT_DE_PLL_CTL,
HTOTAL_EDP,
HBLANK_EDP,
@@ -457,9 +471,18 @@ is
FDI_RXC_TUSIZE1,
QUIRK_F2060,
TRANSC_CHICKEN2,
BXT_P_CR_GT_DISP_PWRON,
GT_MAILBOX,
GT_MAILBOX_DATA,
GT_MAILBOX_DATA_1);
GT_MAILBOX_DATA_1,
BXT_PORT_CL1CM_DW0_A,
BXT_PORT_CL1CM_DW9_A,
BXT_PORT_CL1CM_DW10_A,
BXT_PORT_CL1CM_DW28_A,
BXT_PORT_CL1CM_DW30_A,
BXT_PORT_REF_DW3_A,
BXT_PORT_REF_DW6_A,
BXT_PORT_REF_DW8_A);
pragma Warnings
(GNATprove, Off, "pragma ""KEEP_NAMES"" ignored *(not yet supported)",
@@ -836,6 +859,35 @@ is
BXT_DE_PLL_CTL => 16#06_d000# / Register_Width,
BXT_DE_PLL_ENABLE => 16#04_6070# / Register_Width,
-- Broxton DDI PHY registers
BXT_P_CR_GT_DISP_PWRON => 16#13_8090# / Register_Width,
BXT_PHY_CTL_A => 16#06_4c00# / Register_Width,
BXT_PHY_CTL_B => 16#06_4c10# / Register_Width,
BXT_PHY_CTL_C => 16#06_4c20# / Register_Width,
BXT_PHY_CTL_FAM_EDP => 16#06_4c80# / Register_Width,
BXT_PHY_CTL_FAM_DDI => 16#06_4c90# / Register_Width,
-- Broxton DDI PHY common lane registers
BXT_PORT_CL1CM_DW0_A => 16#16_2000# / Register_Width,
BXT_PORT_CL1CM_DW0_BC => 16#06_c000# / Register_Width,
BXT_PORT_CL1CM_DW9_A => 16#16_2024# / Register_Width,
BXT_PORT_CL1CM_DW9_BC => 16#06_c024# / Register_Width,
BXT_PORT_CL1CM_DW10_A => 16#16_2028# / Register_Width,
BXT_PORT_CL1CM_DW10_BC => 16#06_c028# / Register_Width,
BXT_PORT_CL1CM_DW28_A => 16#16_2070# / Register_Width,
BXT_PORT_CL1CM_DW28_BC => 16#06_c070# / Register_Width,
BXT_PORT_CL1CM_DW30_A => 16#16_2078# / Register_Width,
BXT_PORT_CL1CM_DW30_BC => 16#06_c078# / Register_Width,
BXT_PORT_CL2CM_DW6_BC => 16#06_c358# / Register_Width,
-- Broxton DDI PHY ref registers
BXT_PORT_REF_DW3_A => 16#16_218c# / Register_Width,
BXT_PORT_REF_DW3_BC => 16#06_c18c# / Register_Width,
BXT_PORT_REF_DW6_A => 16#16_2198# / Register_Width,
BXT_PORT_REF_DW6_BC => 16#06_c198# / Register_Width,
BXT_PORT_REF_DW8_A => 16#16_21a0# / Register_Width,
BXT_PORT_REF_DW8_BC => 16#06_c1a0# / Register_Width,
-- Power Down Well registers
PWR_WELL_CTL_BIOS => 16#04_5400# / Register_Width,
PWR_WELL_CTL_DRIVER => 16#04_5404# / Register_Width,