4287 lines · 114.6 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 <linux/suspend.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"); #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",	"NAVI10",	"NAVI14",	"NAVI12",	"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 = ddev->dev_private;	uint64_t cnt = amdgpu_asic_get_pcie_replay_count(adev); 	return snprintf(buf, PAGE_SIZE, "%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); /** * amdgpu_device_is_px - Is the device is a dGPU with HG/PX power control * * @dev: drm_device pointer * * Returns true if the device is a dGPU with HG/PX power control, * otherwise return false. */bool amdgpu_device_is_px(struct drm_device *dev){	struct amdgpu_device *adev = dev->dev_private; 	if (adev->flags & AMD_IS_PX)		return true;	return false;} /** * 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){	uint64_t last;	unsigned long flags; 	last = size - 4;	for (last += pos; pos <= last; pos += 4) {		spin_lock_irqsave(&adev->mmio_idx_lock, flags);		WREG32_NO_KIQ(mmMM_INDEX, ((uint32_t)pos) | 0x80000000);		WREG32_NO_KIQ(mmMM_INDEX_HI, pos >> 31);		if (write)			WREG32_NO_KIQ(mmMM_DATA, *buf++);		else			*buf++ = RREG32_NO_KIQ(mmMM_DATA);		spin_unlock_irqrestore(&adev->mmio_idx_lock, flags);	}} /* * MMIO register access helper functions. *//** * amdgpu_mm_rreg - read a memory mapped IO 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_mm_rreg(struct amdgpu_device *adev, uint32_t reg,			uint32_t acc_flags){	uint32_t ret; 	if (!(acc_flags & AMDGPU_REGS_NO_KIQ) && amdgpu_sriov_runtime(adev))		return amdgpu_virt_kiq_rreg(adev, reg); 	if ((reg * 4) < adev->rmmio_size && !(acc_flags & AMDGPU_REGS_IDX))		ret = readl(((void __iomem *)adev->rmmio) + (reg * 4));	else {		unsigned long flags; 		spin_lock_irqsave(&adev->mmio_idx_lock, flags);		writel((reg * 4), ((void __iomem *)adev->rmmio) + (mmMM_INDEX * 4));		ret = readl(((void __iomem *)adev->rmmio) + (mmMM_DATA * 4));		spin_unlock_irqrestore(&adev->mmio_idx_lock, flags);	}	trace_amdgpu_mm_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 (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 (offset < adev->rmmio_size)		writeb(value, adev->rmmio + offset);	else		BUG();} /** * amdgpu_mm_wreg - write to a memory mapped IO 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_mm_wreg(struct amdgpu_device *adev, uint32_t reg, uint32_t v,		    uint32_t acc_flags){	trace_amdgpu_mm_wreg(adev->pdev->device, reg, v); 	if (adev->asic_type >= CHIP_VEGA10 && reg == 0) {		adev->last_mm_index = v;	} 	if (!(acc_flags & AMDGPU_REGS_NO_KIQ) && amdgpu_sriov_runtime(adev))		return amdgpu_virt_kiq_wreg(adev, reg, v); 	if ((reg * 4) < adev->rmmio_size && !(acc_flags & AMDGPU_REGS_IDX))		writel(v, ((void __iomem *)adev->rmmio) + (reg * 4));	else {		unsigned long flags; 		spin_lock_irqsave(&adev->mmio_idx_lock, flags);		writel((reg * 4), ((void __iomem *)adev->rmmio) + (mmMM_INDEX * 4));		writel(v, ((void __iomem *)adev->rmmio) + (mmMM_DATA * 4));		spin_unlock_irqrestore(&adev->mmio_idx_lock, flags);	} 	if (adev->asic_type >= CHIP_VEGA10 && reg == 1 && adev->last_mm_index == 0x5702C) {		udelay(500);	}} /** * amdgpu_io_rreg - read an IO register * * @adev: amdgpu_device pointer * @reg: dword aligned register offset * * Returns the 32 bit value from the offset specified. */u32 amdgpu_io_rreg(struct amdgpu_device *adev, u32 reg){	if ((reg * 4) < adev->rio_mem_size)		return ioread32(adev->rio_mem + (reg * 4));	else {		iowrite32((reg * 4), adev->rio_mem + (mmMM_INDEX * 4));		return ioread32(adev->rio_mem + (mmMM_DATA * 4));	}} /** * amdgpu_io_wreg - write to an IO register * * @adev: amdgpu_device pointer * @reg: dword aligned register offset * @v: 32 bit value to write to the register * * Writes the value specified to the offset specified. */void amdgpu_io_wreg(struct amdgpu_device *adev, u32 reg, u32 v){	if (adev->asic_type >= CHIP_VEGA10 && reg == 0) {		adev->last_mm_index = v;	} 	if ((reg * 4) < adev->rio_mem_size)		iowrite32(v, adev->rio_mem + (reg * 4));	else {		iowrite32((reg * 4), adev->rio_mem + (mmMM_INDEX * 4));		iowrite32(v, adev->rio_mem + (mmMM_DATA * 4));	} 	if (adev->asic_type >= CHIP_VEGA10 && reg == 1 && adev->last_mm_index == 0x5702C) {		udelay(500);	}} /** * amdgpu_mm_rdoorbell - read a doorbell dword * * @adev: amdgpu_device pointer * @index: doorbell index * * Returns the value in the doorbell aperture at the * requested doorbell index (CIK). */u32 amdgpu_mm_rdoorbell(struct amdgpu_device *adev, u32 index){	if (index < adev->doorbell.num_doorbells) {		return readl(adev->doorbell.ptr + index);	} else {		DRM_ERROR("reading beyond doorbell aperture: 0x%08x!\n", index);		return 0;	}} /** * amdgpu_mm_wdoorbell - write a doorbell dword * * @adev: amdgpu_device pointer * @index: doorbell index * @v: value to write * * Writes @v to the doorbell aperture at the * requested doorbell index (CIK). */void amdgpu_mm_wdoorbell(struct amdgpu_device *adev, u32 index, u32 v){	if (index < adev->doorbell.num_doorbells) {		writel(v, adev->doorbell.ptr + index);	} else {		DRM_ERROR("writing beyond doorbell aperture: 0x%08x!\n", index);	}} /** * amdgpu_mm_rdoorbell64 - read a doorbell Qword * * @adev: amdgpu_device pointer * @index: doorbell index * * Returns the value in the doorbell aperture at the * requested doorbell index (VEGA10+). */u64 amdgpu_mm_rdoorbell64(struct amdgpu_device *adev, u32 index){	if (index < adev->doorbell.num_doorbells) {		return atomic64_read((atomic64_t *)(adev->doorbell.ptr + index));	} else {		DRM_ERROR("reading beyond doorbell aperture: 0x%08x!\n", index);		return 0;	}} /** * amdgpu_mm_wdoorbell64 - write a doorbell Qword * * @adev: amdgpu_device pointer * @index: doorbell index * @v: value to write * * Writes @v to the doorbell aperture at the * requested doorbell index (VEGA10+). */void amdgpu_mm_wdoorbell64(struct amdgpu_device *adev, u32 index, u64 v){	if (index < adev->doorbell.num_doorbells) {		atomic64_set((atomic64_t *)(adev->doorbell.ptr + index), v);	} else {		DRM_ERROR("writing beyond doorbell aperture: 0x%08x!\n", index);	}} /** * amdgpu_invalid_rreg - dummy reg read function * * @adev: amdgpu device pointer * @reg: offset of register * * Dummy register read function.  Used for register blocks * that certain asics don't have (all asics). * Returns the value in the register. */static uint32_t amdgpu_invalid_rreg(struct amdgpu_device *adev, uint32_t reg){	DRM_ERROR("Invalid callback to read register 0x%04X\n", reg);	BUG();	return 0;} /** * amdgpu_invalid_wreg - dummy reg write function * * @adev: amdgpu device pointer * @reg: offset of register * @v: value to write to the register * * Dummy register read function.  Used for register blocks * that certain asics don't have (all asics). */static void amdgpu_invalid_wreg(struct amdgpu_device *adev, uint32_t reg, uint32_t v){	DRM_ERROR("Invalid callback to write register 0x%04X with 0x%08X\n",		  reg, v);	BUG();} /** * amdgpu_invalid_rreg64 - dummy 64 bit reg read function * * @adev: amdgpu device pointer * @reg: offset of register * * Dummy register read function.  Used for register blocks * that certain asics don't have (all asics). * Returns the value in the register. */static uint64_t amdgpu_invalid_rreg64(struct amdgpu_device *adev, uint32_t reg){	DRM_ERROR("Invalid callback to read 64 bit register 0x%04X\n", reg);	BUG();	return 0;} /** * amdgpu_invalid_wreg64 - dummy reg write function * * @adev: amdgpu device pointer * @reg: offset of register * @v: value to write to the register * * Dummy register read function.  Used for register blocks * that certain asics don't have (all asics). */static void amdgpu_invalid_wreg64(struct amdgpu_device *adev, uint32_t reg, uint64_t v){	DRM_ERROR("Invalid callback to write 64 bit register 0x%04X with 0x%08llX\n",		  reg, v);	BUG();} /** * amdgpu_block_invalid_rreg - dummy reg read function * * @adev: amdgpu device pointer * @block: offset of instance * @reg: offset of register * * Dummy register read function.  Used for register blocks * that certain asics don't have (all asics). * Returns the value in the register. */static uint32_t amdgpu_block_invalid_rreg(struct amdgpu_device *adev,

Lines 1–500 of 4287.