--- /dev/null
+From 71f677a91046599ece96ebab21df956ce909c456 Mon Sep 17 00:00:00 2001
+From: Russell Currey <ruscur@russell.cc>
+Date: Fri, 17 Feb 2017 14:33:01 +1100
+Subject: drm/ast: Handle configuration without P2A bridge
+
+From: Russell Currey <ruscur@russell.cc>
+
+commit 71f677a91046599ece96ebab21df956ce909c456 upstream.
+
+The ast driver configures a window to enable access into BMC
+memory space in order to read some configuration registers.
+
+If this window is disabled, which it can be from the BMC side,
+the ast driver can't function.
+
+Closing this window is a necessity for security if a machine's
+host side and BMC side are controlled by different parties;
+i.e. a cloud provider offering machines "bare metal".
+
+A recent patch went in to try to check if that window is open
+but it does so by trying to access the registers in question
+and testing if the result is 0xffffffff.
+
+This method will trigger a PCIe error when the window is closed
+which on some systems will be fatal (it will trigger an EEH
+for example on POWER which will take out the device).
+
+This patch improves this in two ways:
+
+ - First, if the firmware has put properties in the device-tree
+containing the relevant configuration information, we use these.
+
+ - Otherwise, a bit in one of the SCU scratch registers (which
+are readable via the VGA register space and writeable by the BMC)
+will indicate if the BMC has closed the window. This bit has been
+defined by Y.C Chen from Aspeed.
+
+If the window is closed and the configuration isn't available from
+the device-tree, some sane defaults are used. Those defaults are
+hopefully sufficient for standard video modes used on a server.
+
+Signed-off-by: Russell Currey <ruscur@russell.cc>
+Acked-by: Joel Stanley <joel@jms.id.au>
+Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
+Signed-off-by: Dave Airlie <airlied@redhat.com>
+Cc: Ben Hutchings <ben.hutchings@codethink.co.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/gpu/drm/ast/ast_drv.h | 6
+ drivers/gpu/drm/ast/ast_main.c | 264 +++++++++++++++++++++++++----------------
+ drivers/gpu/drm/ast/ast_post.c | 7 -
+ 3 files changed, 168 insertions(+), 109 deletions(-)
+
+--- a/drivers/gpu/drm/ast/ast_drv.h
++++ b/drivers/gpu/drm/ast/ast_drv.h
+@@ -113,7 +113,11 @@ struct ast_private {
+ struct ttm_bo_kmap_obj cache_kmap;
+ int next_cursor;
+ bool support_wide_screen;
+- bool DisableP2A;
++ enum {
++ ast_use_p2a,
++ ast_use_dt,
++ ast_use_defaults
++ } config_mode;
+
+ enum ast_tx_chip tx_chip_type;
+ u8 dp501_maxclk;
+--- a/drivers/gpu/drm/ast/ast_main.c
++++ b/drivers/gpu/drm/ast/ast_main.c
+@@ -62,13 +62,84 @@ uint8_t ast_get_index_reg_mask(struct as
+ return ret;
+ }
+
++static void ast_detect_config_mode(struct drm_device *dev, u32 *scu_rev)
++{
++ struct device_node *np = dev->pdev->dev.of_node;
++ struct ast_private *ast = dev->dev_private;
++ uint32_t data, jregd0, jregd1;
++
++ /* Defaults */
++ ast->config_mode = ast_use_defaults;
++ *scu_rev = 0xffffffff;
++
++ /* Check if we have device-tree properties */
++ if (np && !of_property_read_u32(np, "aspeed,scu-revision-id",
++ scu_rev)) {
++ /* We do, disable P2A access */
++ ast->config_mode = ast_use_dt;
++ DRM_INFO("Using device-tree for configuration\n");
++ return;
++ }
++
++ /* Not all families have a P2A bridge */
++ if (dev->pdev->device != PCI_CHIP_AST2000)
++ return;
++
++ /*
++ * The BMC will set SCU 0x40 D[12] to 1 if the P2 bridge
++ * is disabled. We force using P2A if VGA only mode bit
++ * is set D[7]
++ */
++ jregd0 = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd0, 0xff);
++ jregd1 = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd1, 0xff);
++ if (!(jregd0 & 0x80) || !(jregd1 & 0x10)) {
++ /* Double check it's actually working */
++ data = ast_read32(ast, 0xf004);
++ if (data != 0xFFFFFFFF) {
++ /* P2A works, grab silicon revision */
++ ast->config_mode = ast_use_p2a;
++
++ DRM_INFO("Using P2A bridge for configuration\n");
++
++ /* Read SCU7c (silicon revision register) */
++ ast_write32(ast, 0xf004, 0x1e6e0000);
++ ast_write32(ast, 0xf000, 0x1);
++ *scu_rev = ast_read32(ast, 0x1207c);
++ return;
++ }
++ }
++
++ /* We have a P2A bridge but it's disabled */
++ DRM_INFO("P2A bridge disabled, using default configuration\n");
++}
+
+ static int ast_detect_chip(struct drm_device *dev, bool *need_post)
+ {
+ struct ast_private *ast = dev->dev_private;
+- uint32_t data, jreg;
++ uint32_t jreg, scu_rev;
++
++ /*
++ * If VGA isn't enabled, we need to enable now or subsequent
++ * access to the scratch registers will fail. We also inform
++ * our caller that it needs to POST the chip
++ * (Assumption: VGA not enabled -> need to POST)
++ */
++ if (!ast_is_vga_enabled(dev)) {
++ ast_enable_vga(dev);
++ DRM_INFO("VGA not enabled on entry, requesting chip POST\n");
++ *need_post = true;
++ } else
++ *need_post = false;
++
++
++ /* Enable extended register access */
++ ast_enable_mmio(dev);
+ ast_open_key(ast);
+
++ /* Find out whether P2A works or whether to use device-tree */
++ ast_detect_config_mode(dev, &scu_rev);
++
++ /* Identify chipset */
+ if (dev->pdev->device == PCI_CHIP_AST1180) {
+ ast->chip = AST1100;
+ DRM_INFO("AST 1180 detected\n");
+@@ -80,12 +151,7 @@ static int ast_detect_chip(struct drm_de
+ ast->chip = AST2300;
+ DRM_INFO("AST 2300 detected\n");
+ } else if (dev->pdev->revision >= 0x10) {
+- uint32_t data;
+- ast_write32(ast, 0xf004, 0x1e6e0000);
+- ast_write32(ast, 0xf000, 0x1);
+-
+- data = ast_read32(ast, 0x1207c);
+- switch (data & 0x0300) {
++ switch (scu_rev & 0x0300) {
+ case 0x0200:
+ ast->chip = AST1100;
+ DRM_INFO("AST 1100 detected\n");
+@@ -110,26 +176,6 @@ static int ast_detect_chip(struct drm_de
+ }
+ }
+
+- /*
+- * If VGA isn't enabled, we need to enable now or subsequent
+- * access to the scratch registers will fail. We also inform
+- * our caller that it needs to POST the chip
+- * (Assumption: VGA not enabled -> need to POST)
+- */
+- if (!ast_is_vga_enabled(dev)) {
+- ast_enable_vga(dev);
+- ast_enable_mmio(dev);
+- DRM_INFO("VGA not enabled on entry, requesting chip POST\n");
+- *need_post = true;
+- } else
+- *need_post = false;
+-
+- /* Check P2A Access */
+- ast->DisableP2A = true;
+- data = ast_read32(ast, 0xf004);
+- if (data != 0xFFFFFFFF)
+- ast->DisableP2A = false;
+-
+ /* Check if we support wide screen */
+ switch (ast->chip) {
+ case AST1180:
+@@ -146,17 +192,12 @@ static int ast_detect_chip(struct drm_de
+ ast->support_wide_screen = true;
+ else {
+ ast->support_wide_screen = false;
+- if (ast->DisableP2A == false) {
+- /* Read SCU7c (silicon revision register) */
+- ast_write32(ast, 0xf004, 0x1e6e0000);
+- ast_write32(ast, 0xf000, 0x1);
+- data = ast_read32(ast, 0x1207c);
+- data &= 0x300;
+- if (ast->chip == AST2300 && data == 0x0) /* ast1300 */
+- ast->support_wide_screen = true;
+- if (ast->chip == AST2400 && data == 0x100) /* ast1400 */
+- ast->support_wide_screen = true;
+- }
++ if (ast->chip == AST2300 &&
++ (scu_rev & 0x300) == 0x0) /* ast1300 */
++ ast->support_wide_screen = true;
++ if (ast->chip == AST2400 &&
++ (scu_rev & 0x300) == 0x100) /* ast1400 */
++ ast->support_wide_screen = true;
+ }
+ break;
+ }
+@@ -220,85 +261,102 @@ static int ast_detect_chip(struct drm_de
+
+ static int ast_get_dram_info(struct drm_device *dev)
+ {
++ struct device_node *np = dev->pdev->dev.of_node;
+ struct ast_private *ast = dev->dev_private;
+- uint32_t data, data2;
+- uint32_t denum, num, div, ref_pll;
++ uint32_t mcr_cfg, mcr_scu_mpll, mcr_scu_strap;
++ uint32_t denum, num, div, ref_pll, dsel;
+
+- if (ast->DisableP2A)
+- {
++ switch (ast->config_mode) {
++ case ast_use_dt:
++ /*
++ * If some properties are missing, use reasonable
++ * defaults for AST2400
++ */
++ if (of_property_read_u32(np, "aspeed,mcr-configuration",
++ &mcr_cfg))
++ mcr_cfg = 0x00000577;
++ if (of_property_read_u32(np, "aspeed,mcr-scu-mpll",
++ &mcr_scu_mpll))
++ mcr_scu_mpll = 0x000050C0;
++ if (of_property_read_u32(np, "aspeed,mcr-scu-strap",
++ &mcr_scu_strap))
++ mcr_scu_strap = 0;
++ break;
++ case ast_use_p2a:
++ ast_write32(ast, 0xf004, 0x1e6e0000);
++ ast_write32(ast, 0xf000, 0x1);
++ mcr_cfg = ast_read32(ast, 0x10004);
++ mcr_scu_mpll = ast_read32(ast, 0x10120);
++ mcr_scu_strap = ast_read32(ast, 0x10170);
++ break;
++ case ast_use_defaults:
++ default:
+ ast->dram_bus_width = 16;
+ ast->dram_type = AST_DRAM_1Gx16;
+ ast->mclk = 396;
++ return 0;
+ }
+- else
+- {
+- ast_write32(ast, 0xf004, 0x1e6e0000);
+- ast_write32(ast, 0xf000, 0x1);
+- data = ast_read32(ast, 0x10004);
+
+- if (data & 0x40)
+- ast->dram_bus_width = 16;
+- else
+- ast->dram_bus_width = 32;
+-
+- if (ast->chip == AST2300 || ast->chip == AST2400) {
+- switch (data & 0x03) {
+- case 0:
+- ast->dram_type = AST_DRAM_512Mx16;
+- break;
+- default:
+- case 1:
+- ast->dram_type = AST_DRAM_1Gx16;
+- break;
+- case 2:
+- ast->dram_type = AST_DRAM_2Gx16;
+- break;
+- case 3:
+- ast->dram_type = AST_DRAM_4Gx16;
+- break;
+- }
+- } else {
+- switch (data & 0x0c) {
+- case 0:
+- case 4:
+- ast->dram_type = AST_DRAM_512Mx16;
+- break;
+- case 8:
+- if (data & 0x40)
+- ast->dram_type = AST_DRAM_1Gx16;
+- else
+- ast->dram_type = AST_DRAM_512Mx32;
+- break;
+- case 0xc:
+- ast->dram_type = AST_DRAM_1Gx32;
+- break;
+- }
+- }
++ if (mcr_cfg & 0x40)
++ ast->dram_bus_width = 16;
++ else
++ ast->dram_bus_width = 32;
+
+- data = ast_read32(ast, 0x10120);
+- data2 = ast_read32(ast, 0x10170);
+- if (data2 & 0x2000)
+- ref_pll = 14318;
+- else
+- ref_pll = 12000;
+-
+- denum = data & 0x1f;
+- num = (data & 0x3fe0) >> 5;
+- data = (data & 0xc000) >> 14;
+- switch (data) {
+- case 3:
+- div = 0x4;
++ if (ast->chip == AST2300 || ast->chip == AST2400) {
++ switch (mcr_cfg & 0x03) {
++ case 0:
++ ast->dram_type = AST_DRAM_512Mx16;
+ break;
+- case 2:
++ default:
+ case 1:
+- div = 0x2;
++ ast->dram_type = AST_DRAM_1Gx16;
+ break;
+- default:
+- div = 0x1;
++ case 2:
++ ast->dram_type = AST_DRAM_2Gx16;
++ break;
++ case 3:
++ ast->dram_type = AST_DRAM_4Gx16;
++ break;
++ }
++ } else {
++ switch (mcr_cfg & 0x0c) {
++ case 0:
++ case 4:
++ ast->dram_type = AST_DRAM_512Mx16;
++ break;
++ case 8:
++ if (mcr_cfg & 0x40)
++ ast->dram_type = AST_DRAM_1Gx16;
++ else
++ ast->dram_type = AST_DRAM_512Mx32;
++ break;
++ case 0xc:
++ ast->dram_type = AST_DRAM_1Gx32;
+ break;
+ }
+- ast->mclk = ref_pll * (num + 2) / (denum + 2) * (div * 1000);
+ }
++
++ if (mcr_scu_strap & 0x2000)
++ ref_pll = 14318;
++ else
++ ref_pll = 12000;
++
++ denum = mcr_scu_mpll & 0x1f;
++ num = (mcr_scu_mpll & 0x3fe0) >> 5;
++ dsel = (mcr_scu_mpll & 0xc000) >> 14;
++ switch (dsel) {
++ case 3:
++ div = 0x4;
++ break;
++ case 2:
++ case 1:
++ div = 0x2;
++ break;
++ default:
++ div = 0x1;
++ break;
++ }
++ ast->mclk = ref_pll * (num + 2) / (denum + 2) * (div * 1000);
+ return 0;
+ }
+
+--- a/drivers/gpu/drm/ast/ast_post.c
++++ b/drivers/gpu/drm/ast/ast_post.c
+@@ -375,17 +375,14 @@ void ast_post_gpu(struct drm_device *dev
+ ast_enable_mmio(dev);
+ ast_set_def_ext_reg(dev);
+
+- if (ast->DisableP2A == false)
+- {
++ if (ast->config_mode == ast_use_p2a) {
+ if (ast->chip == AST2300 || ast->chip == AST2400)
+ ast_init_dram_2300(dev);
+ else
+ ast_init_dram_reg(dev);
+
+ ast_init_3rdtx(dev);
+- }
+- else
+- {
++ } else {
+ if (ast->tx_chip_type != AST_TX_NONE)
+ ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xa3, 0xcf, 0x80); /* Enable DVO */
+ }