162306a36Sopenharmony_ci// SPDX-License-Identifier: MIT 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright © 2023 Intel Corporation 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/pci.h> 762306a36Sopenharmony_ci#include <linux/pnp.h> 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <drm/drm_managed.h> 1062306a36Sopenharmony_ci#include <drm/i915_drm.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include "i915_drv.h" 1362306a36Sopenharmony_ci#include "intel_gmch.h" 1462306a36Sopenharmony_ci#include "intel_pci_config.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_cistatic void intel_gmch_bridge_release(struct drm_device *dev, void *bridge) 1762306a36Sopenharmony_ci{ 1862306a36Sopenharmony_ci pci_dev_put(bridge); 1962306a36Sopenharmony_ci} 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ciint intel_gmch_bridge_setup(struct drm_i915_private *i915) 2262306a36Sopenharmony_ci{ 2362306a36Sopenharmony_ci int domain = pci_domain_nr(to_pci_dev(i915->drm.dev)->bus); 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci i915->gmch.pdev = pci_get_domain_bus_and_slot(domain, 0, PCI_DEVFN(0, 0)); 2662306a36Sopenharmony_ci if (!i915->gmch.pdev) { 2762306a36Sopenharmony_ci drm_err(&i915->drm, "bridge device not found\n"); 2862306a36Sopenharmony_ci return -EIO; 2962306a36Sopenharmony_ci } 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci return drmm_add_action_or_reset(&i915->drm, intel_gmch_bridge_release, 3262306a36Sopenharmony_ci i915->gmch.pdev); 3362306a36Sopenharmony_ci} 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/* Allocate space for the MCH regs if needed, return nonzero on error */ 3662306a36Sopenharmony_cistatic int 3762306a36Sopenharmony_ciintel_alloc_mchbar_resource(struct drm_i915_private *i915) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci int reg = GRAPHICS_VER(i915) >= 4 ? MCHBAR_I965 : MCHBAR_I915; 4062306a36Sopenharmony_ci u32 temp_lo, temp_hi = 0; 4162306a36Sopenharmony_ci u64 mchbar_addr; 4262306a36Sopenharmony_ci int ret; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci if (GRAPHICS_VER(i915) >= 4) 4562306a36Sopenharmony_ci pci_read_config_dword(i915->gmch.pdev, reg + 4, &temp_hi); 4662306a36Sopenharmony_ci pci_read_config_dword(i915->gmch.pdev, reg, &temp_lo); 4762306a36Sopenharmony_ci mchbar_addr = ((u64)temp_hi << 32) | temp_lo; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci /* If ACPI doesn't have it, assume we need to allocate it ourselves */ 5062306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_PNP) && mchbar_addr && 5162306a36Sopenharmony_ci pnp_range_reserved(mchbar_addr, mchbar_addr + MCHBAR_SIZE)) 5262306a36Sopenharmony_ci return 0; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci /* Get some space for it */ 5562306a36Sopenharmony_ci i915->gmch.mch_res.name = "i915 MCHBAR"; 5662306a36Sopenharmony_ci i915->gmch.mch_res.flags = IORESOURCE_MEM; 5762306a36Sopenharmony_ci ret = pci_bus_alloc_resource(i915->gmch.pdev->bus, 5862306a36Sopenharmony_ci &i915->gmch.mch_res, 5962306a36Sopenharmony_ci MCHBAR_SIZE, MCHBAR_SIZE, 6062306a36Sopenharmony_ci PCIBIOS_MIN_MEM, 6162306a36Sopenharmony_ci 0, pcibios_align_resource, 6262306a36Sopenharmony_ci i915->gmch.pdev); 6362306a36Sopenharmony_ci if (ret) { 6462306a36Sopenharmony_ci drm_dbg(&i915->drm, "failed bus alloc: %d\n", ret); 6562306a36Sopenharmony_ci i915->gmch.mch_res.start = 0; 6662306a36Sopenharmony_ci return ret; 6762306a36Sopenharmony_ci } 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci if (GRAPHICS_VER(i915) >= 4) 7062306a36Sopenharmony_ci pci_write_config_dword(i915->gmch.pdev, reg + 4, 7162306a36Sopenharmony_ci upper_32_bits(i915->gmch.mch_res.start)); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci pci_write_config_dword(i915->gmch.pdev, reg, 7462306a36Sopenharmony_ci lower_32_bits(i915->gmch.mch_res.start)); 7562306a36Sopenharmony_ci return 0; 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci/* Setup MCHBAR if possible, return true if we should disable it again */ 7962306a36Sopenharmony_civoid intel_gmch_bar_setup(struct drm_i915_private *i915) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci int mchbar_reg = GRAPHICS_VER(i915) >= 4 ? MCHBAR_I965 : MCHBAR_I915; 8262306a36Sopenharmony_ci u32 temp; 8362306a36Sopenharmony_ci bool enabled; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci if (IS_VALLEYVIEW(i915) || IS_CHERRYVIEW(i915)) 8662306a36Sopenharmony_ci return; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci i915->gmch.mchbar_need_disable = false; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci if (IS_I915G(i915) || IS_I915GM(i915)) { 9162306a36Sopenharmony_ci pci_read_config_dword(i915->gmch.pdev, DEVEN, &temp); 9262306a36Sopenharmony_ci enabled = !!(temp & DEVEN_MCHBAR_EN); 9362306a36Sopenharmony_ci } else { 9462306a36Sopenharmony_ci pci_read_config_dword(i915->gmch.pdev, mchbar_reg, &temp); 9562306a36Sopenharmony_ci enabled = temp & 1; 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci /* If it's already enabled, don't have to do anything */ 9962306a36Sopenharmony_ci if (enabled) 10062306a36Sopenharmony_ci return; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci if (intel_alloc_mchbar_resource(i915)) 10362306a36Sopenharmony_ci return; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci i915->gmch.mchbar_need_disable = true; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci /* Space is allocated or reserved, so enable it. */ 10862306a36Sopenharmony_ci if (IS_I915G(i915) || IS_I915GM(i915)) { 10962306a36Sopenharmony_ci pci_write_config_dword(i915->gmch.pdev, DEVEN, 11062306a36Sopenharmony_ci temp | DEVEN_MCHBAR_EN); 11162306a36Sopenharmony_ci } else { 11262306a36Sopenharmony_ci pci_read_config_dword(i915->gmch.pdev, mchbar_reg, &temp); 11362306a36Sopenharmony_ci pci_write_config_dword(i915->gmch.pdev, mchbar_reg, temp | 1); 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_civoid intel_gmch_bar_teardown(struct drm_i915_private *i915) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci int mchbar_reg = GRAPHICS_VER(i915) >= 4 ? MCHBAR_I965 : MCHBAR_I915; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci if (i915->gmch.mchbar_need_disable) { 12262306a36Sopenharmony_ci if (IS_I915G(i915) || IS_I915GM(i915)) { 12362306a36Sopenharmony_ci u32 deven_val; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci pci_read_config_dword(i915->gmch.pdev, DEVEN, 12662306a36Sopenharmony_ci &deven_val); 12762306a36Sopenharmony_ci deven_val &= ~DEVEN_MCHBAR_EN; 12862306a36Sopenharmony_ci pci_write_config_dword(i915->gmch.pdev, DEVEN, 12962306a36Sopenharmony_ci deven_val); 13062306a36Sopenharmony_ci } else { 13162306a36Sopenharmony_ci u32 mchbar_val; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci pci_read_config_dword(i915->gmch.pdev, mchbar_reg, 13462306a36Sopenharmony_ci &mchbar_val); 13562306a36Sopenharmony_ci mchbar_val &= ~1; 13662306a36Sopenharmony_ci pci_write_config_dword(i915->gmch.pdev, mchbar_reg, 13762306a36Sopenharmony_ci mchbar_val); 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci if (i915->gmch.mch_res.start) 14262306a36Sopenharmony_ci release_resource(&i915->gmch.mch_res); 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ciint intel_gmch_vga_set_state(struct drm_i915_private *i915, bool enable_decode) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci unsigned int reg = DISPLAY_VER(i915) >= 6 ? SNB_GMCH_CTRL : INTEL_GMCH_CTRL; 14862306a36Sopenharmony_ci u16 gmch_ctrl; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci if (pci_read_config_word(i915->gmch.pdev, reg, &gmch_ctrl)) { 15162306a36Sopenharmony_ci drm_err(&i915->drm, "failed to read control word\n"); 15262306a36Sopenharmony_ci return -EIO; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci if (!!(gmch_ctrl & INTEL_GMCH_VGA_DISABLE) == !enable_decode) 15662306a36Sopenharmony_ci return 0; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci if (enable_decode) 15962306a36Sopenharmony_ci gmch_ctrl &= ~INTEL_GMCH_VGA_DISABLE; 16062306a36Sopenharmony_ci else 16162306a36Sopenharmony_ci gmch_ctrl |= INTEL_GMCH_VGA_DISABLE; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci if (pci_write_config_word(i915->gmch.pdev, reg, gmch_ctrl)) { 16462306a36Sopenharmony_ci drm_err(&i915->drm, "failed to write control word\n"); 16562306a36Sopenharmony_ci return -EIO; 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci return 0; 16962306a36Sopenharmony_ci} 170