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