5390 lines · 144.8 KB · C
RawDownload
/* * Copyright 2008 Advanced Micro Devices, Inc. * Copyright 2008 Red Hat Inc. * Copyright 2009 Jerome Glisse. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * Authors: Dave Airlie *          Alex Deucher *          Jerome Glisse */#include <linux/power_supply.h>#include <linux/kthread.h>#include <linux/module.h>#include <linux/console.h>#include <linux/slab.h> #include <drm/drm_atomic_helper.h>#include <drm/drm_probe_helper.h>#include <drm/amdgpu_drm.h>#include <linux/vgaarb.h>#include <linux/vga_switcheroo.h>#include <linux/efi.h>#include "amdgpu.h"#include "amdgpu_trace.h"#include "amdgpu_i2c.h"#include "atom.h"#include "amdgpu_atombios.h"#include "amdgpu_atomfirmware.h"#include "amd_pcie.h"#ifdef CONFIG_DRM_AMDGPU_SI#include "si.h"#endif#ifdef CONFIG_DRM_AMDGPU_CIK#include "cik.h"#endif#include "vi.h"#include "soc15.h"#include "nv.h"#include "bif/bif_4_1_d.h"#include <linux/pci.h>#include <linux/firmware.h>#include "amdgpu_vf_error.h" #include "amdgpu_amdkfd.h"#include "amdgpu_pm.h" #include "amdgpu_xgmi.h"#include "amdgpu_ras.h"#include "amdgpu_pmu.h"#include "amdgpu_fru_eeprom.h"#include "amdgpu_reset.h" #include <linux/suspend.h>#include <drm/task_barrier.h>#include <linux/pm_runtime.h> MODULE_FIRMWARE("amdgpu/vega10_gpu_info.bin");MODULE_FIRMWARE("amdgpu/vega12_gpu_info.bin");MODULE_FIRMWARE("amdgpu/raven_gpu_info.bin");MODULE_FIRMWARE("amdgpu/picasso_gpu_info.bin");MODULE_FIRMWARE("amdgpu/raven2_gpu_info.bin");MODULE_FIRMWARE("amdgpu/arcturus_gpu_info.bin");MODULE_FIRMWARE("amdgpu/renoir_gpu_info.bin");MODULE_FIRMWARE("amdgpu/navi10_gpu_info.bin");MODULE_FIRMWARE("amdgpu/navi14_gpu_info.bin");MODULE_FIRMWARE("amdgpu/navi12_gpu_info.bin");MODULE_FIRMWARE("amdgpu/vangogh_gpu_info.bin"); #define AMDGPU_RESUME_MS		2000 const char *amdgpu_asic_name[] = {	"TAHITI",	"PITCAIRN",	"VERDE",	"OLAND",	"HAINAN",	"BONAIRE",	"KAVERI",	"KABINI",	"HAWAII",	"MULLINS",	"TOPAZ",	"TONGA",	"FIJI",	"CARRIZO",	"STONEY",	"POLARIS10",	"POLARIS11",	"POLARIS12",	"VEGAM",	"VEGA10",	"VEGA12",	"VEGA20",	"RAVEN",	"ARCTURUS",	"RENOIR",	"ALDEBARAN",	"NAVI10",	"NAVI14",	"NAVI12",	"SIENNA_CICHLID",	"NAVY_FLOUNDER",	"VANGOGH",	"DIMGREY_CAVEFISH",	"LAST",}; /** * DOC: pcie_replay_count * * The amdgpu driver provides a sysfs API for reporting the total number * of PCIe replays (NAKs) * The file pcie_replay_count is used for this and returns the total * number of replays as a sum of the NAKs generated and NAKs received */ static ssize_t amdgpu_device_get_pcie_replay_count(struct device *dev,		struct device_attribute *attr, char *buf){	struct drm_device *ddev = dev_get_drvdata(dev);	struct amdgpu_device *adev = drm_to_adev(ddev);	uint64_t cnt = amdgpu_asic_get_pcie_replay_count(adev); 	return sysfs_emit(buf, "%llu\n", cnt);} static DEVICE_ATTR(pcie_replay_count, S_IRUGO,		amdgpu_device_get_pcie_replay_count, NULL); static void amdgpu_device_get_pcie_info(struct amdgpu_device *adev); /** * DOC: product_name * * The amdgpu driver provides a sysfs API for reporting the product name * for the device * The file serial_number is used for this and returns the product name * as returned from the FRU. * NOTE: This is only available for certain server cards */ static ssize_t amdgpu_device_get_product_name(struct device *dev,		struct device_attribute *attr, char *buf){	struct drm_device *ddev = dev_get_drvdata(dev);	struct amdgpu_device *adev = drm_to_adev(ddev); 	return sysfs_emit(buf, "%s\n", adev->product_name);} static DEVICE_ATTR(product_name, S_IRUGO,		amdgpu_device_get_product_name, NULL); /** * DOC: product_number * * The amdgpu driver provides a sysfs API for reporting the part number * for the device * The file serial_number is used for this and returns the part number * as returned from the FRU. * NOTE: This is only available for certain server cards */ static ssize_t amdgpu_device_get_product_number(struct device *dev,		struct device_attribute *attr, char *buf){	struct drm_device *ddev = dev_get_drvdata(dev);	struct amdgpu_device *adev = drm_to_adev(ddev); 	return sysfs_emit(buf, "%s\n", adev->product_number);} static DEVICE_ATTR(product_number, S_IRUGO,		amdgpu_device_get_product_number, NULL); /** * DOC: serial_number * * The amdgpu driver provides a sysfs API for reporting the serial number * for the device * The file serial_number is used for this and returns the serial number * as returned from the FRU. * NOTE: This is only available for certain server cards */ static ssize_t amdgpu_device_get_serial_number(struct device *dev,		struct device_attribute *attr, char *buf){	struct drm_device *ddev = dev_get_drvdata(dev);	struct amdgpu_device *adev = drm_to_adev(ddev); 	return sysfs_emit(buf, "%s\n", adev->serial);} static DEVICE_ATTR(serial_number, S_IRUGO,		amdgpu_device_get_serial_number, NULL); /** * amdgpu_device_supports_px - Is the device a dGPU with ATPX power control * * @dev: drm_device pointer * * Returns true if the device is a dGPU with ATPX power control, * otherwise return false. */bool amdgpu_device_supports_px(struct drm_device *dev){	struct amdgpu_device *adev = drm_to_adev(dev); 	if ((adev->flags & AMD_IS_PX) && !amdgpu_is_atpx_hybrid())		return true;	return false;} /** * amdgpu_device_supports_boco - Is the device a dGPU with ACPI power resources * * @dev: drm_device pointer * * Returns true if the device is a dGPU with ACPI power control, * otherwise return false. */bool amdgpu_device_supports_boco(struct drm_device *dev){	struct amdgpu_device *adev = drm_to_adev(dev); 	if (adev->has_pr3 ||	    ((adev->flags & AMD_IS_PX) && amdgpu_is_atpx_hybrid()))		return true;	return false;} /** * amdgpu_device_supports_baco - Does the device support BACO * * @dev: drm_device pointer * * Returns true if the device supporte BACO, * otherwise return false. */bool amdgpu_device_supports_baco(struct drm_device *dev){	struct amdgpu_device *adev = drm_to_adev(dev); 	return amdgpu_asic_supports_baco(adev);} /* * VRAM access helper functions */ /** * amdgpu_device_vram_access - read/write a buffer in vram * * @adev: amdgpu_device pointer * @pos: offset of the buffer in vram * @buf: virtual address of the buffer in system memory * @size: read/write size, sizeof(@buf) must > @size * @write: true - write to vram, otherwise - read from vram */void amdgpu_device_vram_access(struct amdgpu_device *adev, loff_t pos,			       uint32_t *buf, size_t size, bool write){	unsigned long flags;	uint32_t hi = ~0;	uint64_t last;  #ifdef CONFIG_64BIT	last = min(pos + size, adev->gmc.visible_vram_size);	if (last > pos) {		void __iomem *addr = adev->mman.aper_base_kaddr + pos;		size_t count = last - pos; 		if (write) {			memcpy_toio(addr, buf, count);			mb();			amdgpu_asic_flush_hdp(adev, NULL);		} else {			amdgpu_asic_invalidate_hdp(adev, NULL);			mb();			memcpy_fromio(buf, addr, count);		} 		if (count == size)			return; 		pos += count;		buf += count / 4;		size -= count;	}#endif 	spin_lock_irqsave(&adev->mmio_idx_lock, flags);	for (last = pos + size; pos < last; pos += 4) {		uint32_t tmp = pos >> 31; 		WREG32_NO_KIQ(mmMM_INDEX, ((uint32_t)pos) | 0x80000000);		if (tmp != hi) {			WREG32_NO_KIQ(mmMM_INDEX_HI, tmp);			hi = tmp;		}		if (write)			WREG32_NO_KIQ(mmMM_DATA, *buf++);		else			*buf++ = RREG32_NO_KIQ(mmMM_DATA);	}	spin_unlock_irqrestore(&adev->mmio_idx_lock, flags);} /* * register access helper functions. */ /* Check if hw access should be skipped because of hotplug or device error */bool amdgpu_device_skip_hw_access(struct amdgpu_device *adev){	if (adev->in_pci_err_recovery)		return true; #ifdef CONFIG_LOCKDEP	/*	 * This is a bit complicated to understand, so worth a comment. What we assert	 * here is that the GPU reset is not running on another thread in parallel.	 *	 * For this we trylock the read side of the reset semaphore, if that succeeds	 * we know that the reset is not running in paralell.	 *	 * If the trylock fails we assert that we are either already holding the read	 * side of the lock or are the reset thread itself and hold the write side of	 * the lock.	 */	if (in_task()) {		if (down_read_trylock(&adev->reset_sem))			up_read(&adev->reset_sem);		else			lockdep_assert_held(&adev->reset_sem);	}#endif	return false;} /** * amdgpu_device_rreg - read a memory mapped IO or indirect register * * @adev: amdgpu_device pointer * @reg: dword aligned register offset * @acc_flags: access flags which require special behavior * * Returns the 32 bit value from the offset specified. */uint32_t amdgpu_device_rreg(struct amdgpu_device *adev,			    uint32_t reg, uint32_t acc_flags){	uint32_t ret; 	if (amdgpu_device_skip_hw_access(adev))		return 0; 	if ((reg * 4) < adev->rmmio_size) {		if (!(acc_flags & AMDGPU_REGS_NO_KIQ) &&		    amdgpu_sriov_runtime(adev) &&		    down_read_trylock(&adev->reset_sem)) {			ret = amdgpu_kiq_rreg(adev, reg);			up_read(&adev->reset_sem);		} else {			ret = readl(((void __iomem *)adev->rmmio) + (reg * 4));		}	} else {		ret = adev->pcie_rreg(adev, reg * 4);	} 	trace_amdgpu_device_rreg(adev->pdev->device, reg, ret); 	return ret;} /* * MMIO register read with bytes helper functions * @offset:bytes offset from MMIO start **/ /** * amdgpu_mm_rreg8 - read a memory mapped IO register * * @adev: amdgpu_device pointer * @offset: byte aligned register offset * * Returns the 8 bit value from the offset specified. */uint8_t amdgpu_mm_rreg8(struct amdgpu_device *adev, uint32_t offset){	if (amdgpu_device_skip_hw_access(adev))		return 0; 	if (offset < adev->rmmio_size)		return (readb(adev->rmmio + offset));	BUG();} /* * MMIO register write with bytes helper functions * @offset:bytes offset from MMIO start * @value: the value want to be written to the register **//** * amdgpu_mm_wreg8 - read a memory mapped IO register * * @adev: amdgpu_device pointer * @offset: byte aligned register offset * @value: 8 bit value to write * * Writes the value specified to the offset specified. */void amdgpu_mm_wreg8(struct amdgpu_device *adev, uint32_t offset, uint8_t value){	if (amdgpu_device_skip_hw_access(adev))		return; 	if (offset < adev->rmmio_size)		writeb(value, adev->rmmio + offset);	else		BUG();} /** * amdgpu_device_wreg - write to a memory mapped IO or indirect register * * @adev: amdgpu_device pointer * @reg: dword aligned register offset * @v: 32 bit value to write to the register * @acc_flags: access flags which require special behavior * * Writes the value specified to the offset specified. */void amdgpu_device_wreg(struct amdgpu_device *adev,			uint32_t reg, uint32_t v,			uint32_t acc_flags){	if (amdgpu_device_skip_hw_access(adev))		return; 	if ((reg * 4) < adev->rmmio_size) {		if (!(acc_flags & AMDGPU_REGS_NO_KIQ) &&		    amdgpu_sriov_runtime(adev) &&		    down_read_trylock(&adev->reset_sem)) {			amdgpu_kiq_wreg(adev, reg, v);			up_read(&adev->reset_sem);		} else {			writel(v, ((void __iomem *)adev->rmmio) + (reg * 4));		}	} else {		adev->pcie_wreg(adev, reg * 4, v);	} 	trace_amdgpu_device_wreg(adev->pdev->device, reg, v);} /* * amdgpu_mm_wreg_mmio_rlc -  write register either with mmio or with RLC path if in range * * this function is invoked only the debugfs register access * */void amdgpu_mm_wreg_mmio_rlc(struct amdgpu_device *adev,			     uint32_t reg, uint32_t v){	if (amdgpu_device_skip_hw_access(adev))		return; 	if (amdgpu_sriov_fullaccess(adev) &&	    adev->gfx.rlc.funcs &&	    adev->gfx.rlc.funcs->is_rlcg_access_range) {		if (adev->gfx.rlc.funcs->is_rlcg_access_range(adev, reg))			return adev->gfx.rlc.funcs->rlcg_wreg(adev, reg, v, 0);	} else {		writel(v, ((void __iomem *)adev->rmmio) + (reg * 4));	}} /** * amdgpu_mm_rdoorbell - read a doorbell dword

Lines 1–500 of 5390.