From 7235711a43b6839f5759327d003fa334c4a703f2 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Thu, 4 Apr 2013 13:58:09 -0400 Subject: [PATCH] drm/radeon: add support for ASPM on CIK asics Enables PCIE ASPM (Active State Power Management) on CIK asics. Signed-off-by: Alex Deucher --- drivers/gpu/drm/radeon/cik.c | 151 ++++++++++++++++++++++++++++++++++ drivers/gpu/drm/radeon/cikd.h | 72 +++++++++++++++- 2 files changed, 222 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c index a30fb32c2301..0da9d6715ef5 100644 --- a/drivers/gpu/drm/radeon/cik.c +++ b/drivers/gpu/drm/radeon/cik.c @@ -62,6 +62,7 @@ extern void si_rlc_fini(struct radeon_device *rdev); extern int si_rlc_init(struct radeon_device *rdev); static void cik_rlc_stop(struct radeon_device *rdev); static void cik_pcie_gen3_enable(struct radeon_device *rdev); +static void cik_program_aspm(struct radeon_device *rdev); /* * Indirect registers accessor @@ -5952,6 +5953,8 @@ static int cik_startup(struct radeon_device *rdev) /* enable pcie gen2/3 link */ cik_pcie_gen3_enable(rdev); + /* enable aspm */ + cik_program_aspm(rdev); cik_mc_program(rdev); @@ -7212,3 +7215,151 @@ static void cik_pcie_gen3_enable(struct radeon_device *rdev) udelay(1); } } + +static void cik_program_aspm(struct radeon_device *rdev) +{ + u32 data, orig; + bool disable_l0s = false, disable_l1 = false, disable_plloff_in_l1 = false; + bool disable_clkreq = false; + + if (radeon_aspm == 0) + return; + + /* XXX double check IGPs */ + if (rdev->flags & RADEON_IS_IGP) + return; + + if (!(rdev->flags & RADEON_IS_PCIE)) + return; + + orig = data = RREG32_PCIE_PORT(PCIE_LC_N_FTS_CNTL); + data &= ~LC_XMIT_N_FTS_MASK; + data |= LC_XMIT_N_FTS(0x24) | LC_XMIT_N_FTS_OVERRIDE_EN; + if (orig != data) + WREG32_PCIE_PORT(PCIE_LC_N_FTS_CNTL, data); + + orig = data = RREG32_PCIE_PORT(PCIE_LC_CNTL3); + data |= LC_GO_TO_RECOVERY; + if (orig != data) + WREG32_PCIE_PORT(PCIE_LC_CNTL3, data); + + orig = data = RREG32_PCIE_PORT(PCIE_P_CNTL); + data |= P_IGNORE_EDB_ERR; + if (orig != data) + WREG32_PCIE_PORT(PCIE_P_CNTL, data); + + orig = data = RREG32_PCIE_PORT(PCIE_LC_CNTL); + data &= ~(LC_L0S_INACTIVITY_MASK | LC_L1_INACTIVITY_MASK); + data |= LC_PMI_TO_L1_DIS; + if (!disable_l0s) + data |= LC_L0S_INACTIVITY(7); + + if (!disable_l1) { + data |= LC_L1_INACTIVITY(7); + data &= ~LC_PMI_TO_L1_DIS; + if (orig != data) + WREG32_PCIE_PORT(PCIE_LC_CNTL, data); + + if (!disable_plloff_in_l1) { + bool clk_req_support; + + orig = data = RREG32_PCIE_PORT(PB0_PIF_PWRDOWN_0); + data &= ~(PLL_POWER_STATE_IN_OFF_0_MASK | PLL_POWER_STATE_IN_TXS2_0_MASK); + data |= PLL_POWER_STATE_IN_OFF_0(7) | PLL_POWER_STATE_IN_TXS2_0(7); + if (orig != data) + WREG32_PCIE_PORT(PB0_PIF_PWRDOWN_0, data); + + orig = data = RREG32_PCIE_PORT(PB0_PIF_PWRDOWN_1); + data &= ~(PLL_POWER_STATE_IN_OFF_1_MASK | PLL_POWER_STATE_IN_TXS2_1_MASK); + data |= PLL_POWER_STATE_IN_OFF_1(7) | PLL_POWER_STATE_IN_TXS2_1(7); + if (orig != data) + WREG32_PCIE_PORT(PB0_PIF_PWRDOWN_1, data); + + orig = data = RREG32_PCIE_PORT(PB1_PIF_PWRDOWN_0); + data &= ~(PLL_POWER_STATE_IN_OFF_0_MASK | PLL_POWER_STATE_IN_TXS2_0_MASK); + data |= PLL_POWER_STATE_IN_OFF_0(7) | PLL_POWER_STATE_IN_TXS2_0(7); + if (orig != data) + WREG32_PCIE_PORT(PB1_PIF_PWRDOWN_0, data); + + orig = data = RREG32_PCIE_PORT(PB1_PIF_PWRDOWN_1); + data &= ~(PLL_POWER_STATE_IN_OFF_1_MASK | PLL_POWER_STATE_IN_TXS2_1_MASK); + data |= PLL_POWER_STATE_IN_OFF_1(7) | PLL_POWER_STATE_IN_TXS2_1(7); + if (orig != data) + WREG32_PCIE_PORT(PB1_PIF_PWRDOWN_1, data); + + orig = data = RREG32_PCIE_PORT(PCIE_LC_LINK_WIDTH_CNTL); + data &= ~LC_DYN_LANES_PWR_STATE_MASK; + data |= LC_DYN_LANES_PWR_STATE(3); + if (orig != data) + WREG32_PCIE_PORT(PCIE_LC_LINK_WIDTH_CNTL, data); + + if (!disable_clkreq) { + struct pci_dev *root = rdev->pdev->bus->self; + u32 lnkcap; + + clk_req_support = false; + pcie_capability_read_dword(root, PCI_EXP_LNKCAP, &lnkcap); + if (lnkcap & PCI_EXP_LNKCAP_CLKPM) + clk_req_support = true; + } else { + clk_req_support = false; + } + + if (clk_req_support) { + orig = data = RREG32_PCIE_PORT(PCIE_LC_CNTL2); + data |= LC_ALLOW_PDWN_IN_L1 | LC_ALLOW_PDWN_IN_L23; + if (orig != data) + WREG32_PCIE_PORT(PCIE_LC_CNTL2, data); + + orig = data = RREG32_SMC(THM_CLK_CNTL); + data &= ~(CMON_CLK_SEL_MASK | TMON_CLK_SEL_MASK); + data |= CMON_CLK_SEL(1) | TMON_CLK_SEL(1); + if (orig != data) + WREG32_SMC(THM_CLK_CNTL, data); + + orig = data = RREG32_SMC(MISC_CLK_CTRL); + data &= ~(DEEP_SLEEP_CLK_SEL_MASK | ZCLK_SEL_MASK); + data |= DEEP_SLEEP_CLK_SEL(1) | ZCLK_SEL(1); + if (orig != data) + WREG32_SMC(MISC_CLK_CTRL, data); + + orig = data = RREG32_SMC(CG_CLKPIN_CNTL); + data &= ~BCLK_AS_XCLK; + if (orig != data) + WREG32_SMC(CG_CLKPIN_CNTL, data); + + orig = data = RREG32_SMC(CG_CLKPIN_CNTL_2); + data &= ~FORCE_BIF_REFCLK_EN; + if (orig != data) + WREG32_SMC(CG_CLKPIN_CNTL_2, data); + + orig = data = RREG32_SMC(MPLL_BYPASSCLK_SEL); + data &= ~MPLL_CLKOUT_SEL_MASK; + data |= MPLL_CLKOUT_SEL(4); + if (orig != data) + WREG32_SMC(MPLL_BYPASSCLK_SEL, data); + } + } + } else { + if (orig != data) + WREG32_PCIE_PORT(PCIE_LC_CNTL, data); + } + + orig = data = RREG32_PCIE_PORT(PCIE_CNTL2); + data |= SLV_MEM_LS_EN | MST_MEM_LS_EN | REPLAY_MEM_LS_EN; + if (orig != data) + WREG32_PCIE_PORT(PCIE_CNTL2, data); + + if (!disable_l0s) { + data = RREG32_PCIE_PORT(PCIE_LC_N_FTS_CNTL); + if((data & LC_N_FTS_MASK) == LC_N_FTS_MASK) { + data = RREG32_PCIE_PORT(PCIE_LC_STATUS1); + if ((data & LC_REVERSE_XMIT) && (data & LC_REVERSE_RCVR)) { + orig = data = RREG32_PCIE_PORT(PCIE_LC_CNTL); + data &= ~LC_L0S_INACTIVITY_MASK; + if (orig != data) + WREG32_PCIE_PORT(PCIE_LC_CNTL, data); + } + } + } +} diff --git a/drivers/gpu/drm/radeon/cikd.h b/drivers/gpu/drm/radeon/cikd.h index a1f376e6dd31..d50593fd9cd5 100644 --- a/drivers/gpu/drm/radeon/cikd.h +++ b/drivers/gpu/drm/radeon/cikd.h @@ -32,10 +32,53 @@ #define GENERAL_PWRMGT 0xC0200000 # define GPU_COUNTER_CLK (1 << 15) +#define MPLL_BYPASSCLK_SEL 0xC050019C +# define MPLL_CLKOUT_SEL(x) ((x) << 8) +# define MPLL_CLKOUT_SEL_MASK 0xFF00 #define CG_CLKPIN_CNTL 0xC05001A0 # define XTALIN_DIVIDE (1 << 1) +# define BCLK_AS_XCLK (1 << 2) +#define CG_CLKPIN_CNTL_2 0xC05001A4 +# define FORCE_BIF_REFCLK_EN (1 << 3) +# define MUX_TCLK_TO_XCLK (1 << 8) +#define THM_CLK_CNTL 0xC05001A8 +# define CMON_CLK_SEL(x) ((x) << 0) +# define CMON_CLK_SEL_MASK 0xFF +# define TMON_CLK_SEL(x) ((x) << 8) +# define TMON_CLK_SEL_MASK 0xFF00 +#define MISC_CLK_CTRL 0xC05001AC +# define DEEP_SLEEP_CLK_SEL(x) ((x) << 0) +# define DEEP_SLEEP_CLK_SEL_MASK 0xFF +# define ZCLK_SEL(x) ((x) << 8) +# define ZCLK_SEL_MASK 0xFF00 /* PCIE registers idx/data 0x38/0x3c */ +#define PB0_PIF_PWRDOWN_0 0x1100012 /* PCIE */ +# define PLL_POWER_STATE_IN_TXS2_0(x) ((x) << 7) +# define PLL_POWER_STATE_IN_TXS2_0_MASK (0x7 << 7) +# define PLL_POWER_STATE_IN_TXS2_0_SHIFT 7 +# define PLL_POWER_STATE_IN_OFF_0(x) ((x) << 10) +# define PLL_POWER_STATE_IN_OFF_0_MASK (0x7 << 10) +# define PLL_POWER_STATE_IN_OFF_0_SHIFT 10 +# define PLL_RAMP_UP_TIME_0(x) ((x) << 24) +# define PLL_RAMP_UP_TIME_0_MASK (0x7 << 24) +# define PLL_RAMP_UP_TIME_0_SHIFT 24 +#define PB0_PIF_PWRDOWN_1 0x1100013 /* PCIE */ +# define PLL_POWER_STATE_IN_TXS2_1(x) ((x) << 7) +# define PLL_POWER_STATE_IN_TXS2_1_MASK (0x7 << 7) +# define PLL_POWER_STATE_IN_TXS2_1_SHIFT 7 +# define PLL_POWER_STATE_IN_OFF_1(x) ((x) << 10) +# define PLL_POWER_STATE_IN_OFF_1_MASK (0x7 << 10) +# define PLL_POWER_STATE_IN_OFF_1_SHIFT 10 +# define PLL_RAMP_UP_TIME_1(x) ((x) << 24) +# define PLL_RAMP_UP_TIME_1_MASK (0x7 << 24) +# define PLL_RAMP_UP_TIME_1_SHIFT 24 + +#define PCIE_CNTL2 0x1001001c /* PCIE */ +# define SLV_MEM_LS_EN (1 << 16) +# define MST_MEM_LS_EN (1 << 18) +# define REPLAY_MEM_LS_EN (1 << 19) + #define PCIE_LC_STATUS1 0x1400028 /* PCIE */ # define LC_REVERSE_RCVR (1 << 0) # define LC_REVERSE_XMIT (1 << 1) @@ -44,6 +87,22 @@ # define LC_DETECTED_LINK_WIDTH_MASK (0x7 << 5) # define LC_DETECTED_LINK_WIDTH_SHIFT 5 +#define PCIE_P_CNTL 0x1400040 /* PCIE */ +# define P_IGNORE_EDB_ERR (1 << 6) + +#define PB1_PIF_PWRDOWN_0 0x2100012 /* PCIE */ +#define PB1_PIF_PWRDOWN_1 0x2100013 /* PCIE */ + +#define PCIE_LC_CNTL 0x100100A0 /* PCIE */ +# define LC_L0S_INACTIVITY(x) ((x) << 8) +# define LC_L0S_INACTIVITY_MASK (0xf << 8) +# define LC_L0S_INACTIVITY_SHIFT 8 +# define LC_L1_INACTIVITY(x) ((x) << 12) +# define LC_L1_INACTIVITY_MASK (0xf << 12) +# define LC_L1_INACTIVITY_SHIFT 12 +# define LC_PMI_TO_L1_DIS (1 << 16) +# define LC_ASPM_TO_L1_DIS (1 << 24) + #define PCIE_LC_LINK_WIDTH_CNTL 0x100100A2 /* PCIE */ # define LC_LINK_WIDTH_SHIFT 0 # define LC_LINK_WIDTH_MASK 0x7 @@ -65,7 +124,12 @@ # define LC_DYN_LANES_PWR_STATE(x) ((x) << 21) # define LC_DYN_LANES_PWR_STATE_MASK (0x3 << 21) # define LC_DYN_LANES_PWR_STATE_SHIFT 21 - +#define PCIE_LC_N_FTS_CNTL 0x100100a3 /* PCIE */ +# define LC_XMIT_N_FTS(x) ((x) << 0) +# define LC_XMIT_N_FTS_MASK (0xff << 0) +# define LC_XMIT_N_FTS_SHIFT 0 +# define LC_XMIT_N_FTS_OVERRIDE_EN (1 << 8) +# define LC_N_FTS_MASK (0xff << 24) #define PCIE_LC_SPEED_CNTL 0x100100A4 /* PCIE */ # define LC_GEN2_EN_STRAP (1 << 0) # define LC_GEN3_EN_STRAP (1 << 1) @@ -87,6 +151,12 @@ # define LC_OTHER_SIDE_EVER_SENT_GEN3 (1 << 20) # define LC_OTHER_SIDE_SUPPORTS_GEN3 (1 << 21) +#define PCIE_LC_CNTL2 0x100100B1 /* PCIE */ +# define LC_ALLOW_PDWN_IN_L1 (1 << 17) +# define LC_ALLOW_PDWN_IN_L23 (1 << 18) + +#define PCIE_LC_CNTL3 0x100100B5 /* PCIE */ +# define LC_GO_TO_RECOVERY (1 << 30) #define PCIE_LC_CNTL4 0x100100B6 /* PCIE */ # define LC_REDO_EQ (1 << 5) # define LC_SET_QUIESCE (1 << 13)