/* * 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/kthread.h>#include <linux/console.h>#include <linux/slab.h>#include <linux/debugfs.h>#include <drm/drmP.h>#include <drm/drm_crtc_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 "amd_pcie.h"#ifdef CONFIG_DRM_AMDGPU_CIK#include "cik.h"#endif#include "vi.h"#include "bif/bif_4_1_d.h" static int amdgpu_debugfs_regs_init(struct amdgpu_device *adev);static void amdgpu_debugfs_regs_cleanup(struct amdgpu_device *adev); static const char *amdgpu_asic_name[] = { "BONAIRE", "KAVERI", "KABINI", "HAWAII", "MULLINS", "TOPAZ", "TONGA", "FIJI", "CARRIZO", "STONEY", "POLARIS10", "POLARIS11", "LAST",}; 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;} /* * MMIO register access helper functions. */uint32_t amdgpu_mm_rreg(struct amdgpu_device *adev, uint32_t reg, bool always_indirect){ uint32_t ret; if ((reg * 4) < adev->rmmio_size && !always_indirect) 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;} void amdgpu_mm_wreg(struct amdgpu_device *adev, uint32_t reg, uint32_t v, bool always_indirect){ trace_amdgpu_mm_wreg(adev->pdev->device, reg, v); if ((reg * 4) < adev->rmmio_size && !always_indirect) 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); }} 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)); }} void amdgpu_io_wreg(struct amdgpu_device *adev, u32 reg, u32 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)); }} /** * 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_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_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, uint32_t block, uint32_t reg){ DRM_ERROR("Invalid callback to read register 0x%04X in block 0x%04X\n", reg, block); BUG(); return 0;} /** * amdgpu_block_invalid_wreg - dummy reg write function * * @adev: amdgpu device pointer * @block: offset of instance * @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_block_invalid_wreg(struct amdgpu_device *adev, uint32_t block, uint32_t reg, uint32_t v){ DRM_ERROR("Invalid block callback to write register 0x%04X in block 0x%04X with 0x%08X\n", reg, block, v); BUG();} static int amdgpu_vram_scratch_init(struct amdgpu_device *adev){ int r; if (adev->vram_scratch.robj == NULL) { r = amdgpu_bo_create(adev, AMDGPU_GPU_PAGE_SIZE, PAGE_SIZE, true, AMDGPU_GEM_DOMAIN_VRAM, AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED, NULL, NULL, &adev->vram_scratch.robj); if (r) { return r; } } r = amdgpu_bo_reserve(adev->vram_scratch.robj, false); if (unlikely(r != 0)) return r; r = amdgpu_bo_pin(adev->vram_scratch.robj, AMDGPU_GEM_DOMAIN_VRAM, &adev->vram_scratch.gpu_addr); if (r) { amdgpu_bo_unreserve(adev->vram_scratch.robj); return r; } r = amdgpu_bo_kmap(adev->vram_scratch.robj, (void **)&adev->vram_scratch.ptr); if (r) amdgpu_bo_unpin(adev->vram_scratch.robj); amdgpu_bo_unreserve(adev->vram_scratch.robj); return r;} static void amdgpu_vram_scratch_fini(struct amdgpu_device *adev){ int r; if (adev->vram_scratch.robj == NULL) { return; } r = amdgpu_bo_reserve(adev->vram_scratch.robj, false); if (likely(r == 0)) { amdgpu_bo_kunmap(adev->vram_scratch.robj); amdgpu_bo_unpin(adev->vram_scratch.robj); amdgpu_bo_unreserve(adev->vram_scratch.robj); } amdgpu_bo_unref(&adev->vram_scratch.robj);} /** * amdgpu_program_register_sequence - program an array of registers. * * @adev: amdgpu_device pointer * @registers: pointer to the register array * @array_size: size of the register array * * Programs an array or registers with and and or masks. * This is a helper for setting golden registers. */void amdgpu_program_register_sequence(struct amdgpu_device *adev, const u32 *registers, const u32 array_size){ u32 tmp, reg, and_mask, or_mask; int i; if (array_size % 3) return; for (i = 0; i < array_size; i +=3) { reg = registers[i + 0]; and_mask = registers[i + 1]; or_mask = registers[i + 2]; if (and_mask == 0xffffffff) { tmp = or_mask; } else { tmp = RREG32(reg); tmp &= ~and_mask; tmp |= or_mask; } WREG32(reg, tmp); }} void amdgpu_pci_config_reset(struct amdgpu_device *adev){ pci_write_config_dword(adev->pdev, 0x7c, AMDGPU_ASIC_RESET_DATA);} /* * GPU doorbell aperture helpers function. *//** * amdgpu_doorbell_init - Init doorbell driver information. * * @adev: amdgpu_device pointer * * Init doorbell driver information (CIK) * Returns 0 on success, error on failure. */static int amdgpu_doorbell_init(struct amdgpu_device *adev){ /* doorbell bar mapping */ adev->doorbell.base = pci_resource_start(adev->pdev, 2); adev->doorbell.size = pci_resource_len(adev->pdev, 2); adev->doorbell.num_doorbells = min_t(u32, adev->doorbell.size / sizeof(u32), AMDGPU_DOORBELL_MAX_ASSIGNMENT+1); if (adev->doorbell.num_doorbells == 0) return -EINVAL; adev->doorbell.ptr = ioremap(adev->doorbell.base, adev->doorbell.num_doorbells * sizeof(u32)); if (adev->doorbell.ptr == NULL) { return -ENOMEM; } DRM_INFO("doorbell mmio base: 0x%08X\n", (uint32_t)adev->doorbell.base); DRM_INFO("doorbell mmio size: %u\n", (unsigned)adev->doorbell.size); return 0;} /** * amdgpu_doorbell_fini - Tear down doorbell driver information. * * @adev: amdgpu_device pointer * * Tear down doorbell driver information (CIK) */static void amdgpu_doorbell_fini(struct amdgpu_device *adev){ iounmap(adev->doorbell.ptr); adev->doorbell.ptr = NULL;} /** * amdgpu_doorbell_get_kfd_info - Report doorbell configuration required to * setup amdkfd * * @adev: amdgpu_device pointer * @aperture_base: output returning doorbell aperture base physical address * @aperture_size: output returning doorbell aperture size in bytes * @start_offset: output returning # of doorbell bytes reserved for amdgpu. * * amdgpu and amdkfd share the doorbell aperture. amdgpu sets it up, * takes doorbells required for its own rings and reports the setup to amdkfd. * amdgpu reserved doorbells are at the start of the doorbell aperture. */void amdgpu_doorbell_get_kfd_info(struct amdgpu_device *adev, phys_addr_t *aperture_base, size_t *aperture_size, size_t *start_offset){ /* * The first num_doorbells are used by amdgpu. * amdkfd takes whatever's left in the aperture. */ if (adev->doorbell.size > adev->doorbell.num_doorbells * sizeof(u32)) { *aperture_base = adev->doorbell.base; *aperture_size = adev->doorbell.size; *start_offset = adev->doorbell.num_doorbells * sizeof(u32); } else { *aperture_base = 0; *aperture_size = 0; *start_offset = 0; }} /* * amdgpu_wb_*() * Writeback is the the method by which the the GPU updates special pages * in memory with the status of certain GPU events (fences, ring pointers, * etc.). */ /** * amdgpu_wb_fini - Disable Writeback and free memory * * @adev: amdgpu_device pointer * * Disables Writeback and frees the Writeback memory (all asics). * Used at driver shutdown. */static void amdgpu_wb_fini(struct amdgpu_device *adev){ if (adev->wb.wb_obj) { if (!amdgpu_bo_reserve(adev->wb.wb_obj, false)) { amdgpu_bo_kunmap(adev->wb.wb_obj); amdgpu_bo_unpin(adev->wb.wb_obj); amdgpu_bo_unreserve(adev->wb.wb_obj); } amdgpu_bo_unref(&adev->wb.wb_obj); adev->wb.wb = NULL; adev->wb.wb_obj = NULL; }} /** * amdgpu_wb_init- Init Writeback driver info and allocate memory * * @adev: amdgpu_device pointer * * Disables Writeback and frees the Writeback memory (all asics). * Used at driver startup. * Returns 0 on success or an -error on failure. */static int amdgpu_wb_init(struct amdgpu_device *adev){ int r; if (adev->wb.wb_obj == NULL) { r = amdgpu_bo_create(adev, AMDGPU_MAX_WB * 4, PAGE_SIZE, true, AMDGPU_GEM_DOMAIN_GTT, 0, NULL, NULL, &adev->wb.wb_obj); if (r) { dev_warn(adev->dev, "(%d) create WB bo failed\n", r); return r; } r = amdgpu_bo_reserve(adev->wb.wb_obj, false); if (unlikely(r != 0)) { amdgpu_wb_fini(adev); return r; } r = amdgpu_bo_pin(adev->wb.wb_obj, AMDGPU_GEM_DOMAIN_GTT, &adev->wb.gpu_addr); if (r) { amdgpu_bo_unreserve(adev->wb.wb_obj); dev_warn(adev->dev, "(%d) pin WB bo failed\n", r); amdgpu_wb_fini(adev); return r; } r = amdgpu_bo_kmap(adev->wb.wb_obj, (void **)&adev->wb.wb); amdgpu_bo_unreserve(adev->wb.wb_obj); if (r) { dev_warn(adev->dev, "(%d) map WB bo failed\n", r); amdgpu_wb_fini(adev); return r; } adev->wb.num_wb = AMDGPU_MAX_WB; memset(&adev->wb.used, 0, sizeof(adev->wb.used)); /* clear wb memory */ memset((char *)adev->wb.wb, 0, AMDGPU_GPU_PAGE_SIZE); } return 0;} /** * amdgpu_wb_get - Allocate a wb entry *Lines 1–500 of 2591. Load more
/* * 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/kthread.h> #include <linux/console.h> #include <linux/slab.h> #include <linux/debugfs.h> #include <drm/drmP.h> #include <drm/drm_crtc_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 "amd_pcie.h" #ifdef CONFIG_DRM_AMDGPU_CIK #include "cik.h" #endif #include "vi.h" #include "bif/bif_4_1_d.h" static int amdgpu_debugfs_regs_init(struct amdgpu_device *adev); static void amdgpu_debugfs_regs_cleanup(struct amdgpu_device *adev); static const char *amdgpu_asic_name[] = { "BONAIRE", "KAVERI", "KABINI", "HAWAII", "MULLINS", "TOPAZ", "TONGA", "FIJI", "CARRIZO", "STONEY", "POLARIS10", "POLARIS11", "LAST", }; 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; } /* * MMIO register access helper functions. */ uint32_t amdgpu_mm_rreg(struct amdgpu_device *adev, uint32_t reg, bool always_indirect) { uint32_t ret; if ((reg * 4) < adev->rmmio_size && !always_indirect) 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; } void amdgpu_mm_wreg(struct amdgpu_device *adev, uint32_t reg, uint32_t v, bool always_indirect) { trace_amdgpu_mm_wreg(adev->pdev->device, reg, v); if ((reg * 4) < adev->rmmio_size && !always_indirect) 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); } } 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)); } } void amdgpu_io_wreg(struct amdgpu_device *adev, u32 reg, u32 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)); } } /** * 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_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_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, uint32_t block, uint32_t reg) { DRM_ERROR("Invalid callback to read register 0x%04X in block 0x%04X\n", reg, block); BUG(); return 0; } /** * amdgpu_block_invalid_wreg - dummy reg write function * * @adev: amdgpu device pointer * @block: offset of instance * @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_block_invalid_wreg(struct amdgpu_device *adev, uint32_t block, uint32_t reg, uint32_t v) { DRM_ERROR("Invalid block callback to write register 0x%04X in block 0x%04X with 0x%08X\n", reg, block, v); BUG(); } static int amdgpu_vram_scratch_init(struct amdgpu_device *adev) { int r; if (adev->vram_scratch.robj == NULL) { r = amdgpu_bo_create(adev, AMDGPU_GPU_PAGE_SIZE, PAGE_SIZE, true, AMDGPU_GEM_DOMAIN_VRAM, AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED, NULL, NULL, &adev->vram_scratch.robj); if (r) { return r; } } r = amdgpu_bo_reserve(adev->vram_scratch.robj, false); if (unlikely(r != 0)) return r; r = amdgpu_bo_pin(adev->vram_scratch.robj, AMDGPU_GEM_DOMAIN_VRAM, &adev->vram_scratch.gpu_addr); if (r) { amdgpu_bo_unreserve(adev->vram_scratch.robj); return r; } r = amdgpu_bo_kmap(adev->vram_scratch.robj, (void **)&adev->vram_scratch.ptr); if (r) amdgpu_bo_unpin(adev->vram_scratch.robj); amdgpu_bo_unreserve(adev->vram_scratch.robj); return r; } static void amdgpu_vram_scratch_fini(struct amdgpu_device *adev) { int r; if (adev->vram_scratch.robj == NULL) { return; } r = amdgpu_bo_reserve(adev->vram_scratch.robj, false); if (likely(r == 0)) { amdgpu_bo_kunmap(adev->vram_scratch.robj); amdgpu_bo_unpin(adev->vram_scratch.robj); amdgpu_bo_unreserve(adev->vram_scratch.robj); } amdgpu_bo_unref(&adev->vram_scratch.robj); } /** * amdgpu_program_register_sequence - program an array of registers. * * @adev: amdgpu_device pointer * @registers: pointer to the register array * @array_size: size of the register array * * Programs an array or registers with and and or masks. * This is a helper for setting golden registers. */ void amdgpu_program_register_sequence(struct amdgpu_device *adev, const u32 *registers, const u32 array_size) { u32 tmp, reg, and_mask, or_mask; int i; if (array_size % 3) return; for (i = 0; i < array_size; i +=3) { reg = registers[i + 0]; and_mask = registers[i + 1]; or_mask = registers[i + 2]; if (and_mask == 0xffffffff) { tmp = or_mask; } else { tmp = RREG32(reg); tmp &= ~and_mask; tmp |= or_mask; } WREG32(reg, tmp); } } void amdgpu_pci_config_reset(struct amdgpu_device *adev) { pci_write_config_dword(adev->pdev, 0x7c, AMDGPU_ASIC_RESET_DATA); } /* * GPU doorbell aperture helpers function. */ /** * amdgpu_doorbell_init - Init doorbell driver information. * * @adev: amdgpu_device pointer * * Init doorbell driver information (CIK) * Returns 0 on success, error on failure. */ static int amdgpu_doorbell_init(struct amdgpu_device *adev) { /* doorbell bar mapping */ adev->doorbell.base = pci_resource_start(adev->pdev, 2); adev->doorbell.size = pci_resource_len(adev->pdev, 2); adev->doorbell.num_doorbells = min_t(u32, adev->doorbell.size / sizeof(u32), AMDGPU_DOORBELL_MAX_ASSIGNMENT+1); if (adev->doorbell.num_doorbells == 0) return -EINVAL; adev->doorbell.ptr = ioremap(adev->doorbell.base, adev->doorbell.num_doorbells * sizeof(u32)); if (adev->doorbell.ptr == NULL) { return -ENOMEM; } DRM_INFO("doorbell mmio base: 0x%08X\n", (uint32_t)adev->doorbell.base); DRM_INFO("doorbell mmio size: %u\n", (unsigned)adev->doorbell.size); return 0; } /** * amdgpu_doorbell_fini - Tear down doorbell driver information. * * @adev: amdgpu_device pointer * * Tear down doorbell driver information (CIK) */ static void amdgpu_doorbell_fini(struct amdgpu_device *adev) { iounmap(adev->doorbell.ptr); adev->doorbell.ptr = NULL; } /** * amdgpu_doorbell_get_kfd_info - Report doorbell configuration required to * setup amdkfd * * @adev: amdgpu_device pointer * @aperture_base: output returning doorbell aperture base physical address * @aperture_size: output returning doorbell aperture size in bytes * @start_offset: output returning # of doorbell bytes reserved for amdgpu. * * amdgpu and amdkfd share the doorbell aperture. amdgpu sets it up, * takes doorbells required for its own rings and reports the setup to amdkfd. * amdgpu reserved doorbells are at the start of the doorbell aperture. */ void amdgpu_doorbell_get_kfd_info(struct amdgpu_device *adev, phys_addr_t *aperture_base, size_t *aperture_size, size_t *start_offset) { /* * The first num_doorbells are used by amdgpu. * amdkfd takes whatever's left in the aperture. */ if (adev->doorbell.size > adev->doorbell.num_doorbells * sizeof(u32)) { *aperture_base = adev->doorbell.base; *aperture_size = adev->doorbell.size; *start_offset = adev->doorbell.num_doorbells * sizeof(u32); } else { *aperture_base = 0; *aperture_size = 0; *start_offset = 0; } } /* * amdgpu_wb_*() * Writeback is the the method by which the the GPU updates special pages * in memory with the status of certain GPU events (fences, ring pointers, * etc.). */ /** * amdgpu_wb_fini - Disable Writeback and free memory * * @adev: amdgpu_device pointer * * Disables Writeback and frees the Writeback memory (all asics). * Used at driver shutdown. */ static void amdgpu_wb_fini(struct amdgpu_device *adev) { if (adev->wb.wb_obj) { if (!amdgpu_bo_reserve(adev->wb.wb_obj, false)) { amdgpu_bo_kunmap(adev->wb.wb_obj); amdgpu_bo_unpin(adev->wb.wb_obj); amdgpu_bo_unreserve(adev->wb.wb_obj); } amdgpu_bo_unref(&adev->wb.wb_obj); adev->wb.wb = NULL; adev->wb.wb_obj = NULL; } } /** * amdgpu_wb_init- Init Writeback driver info and allocate memory * * @adev: amdgpu_device pointer * * Disables Writeback and frees the Writeback memory (all asics). * Used at driver startup. * Returns 0 on success or an -error on failure. */ static int amdgpu_wb_init(struct amdgpu_device *adev) { int r; if (adev->wb.wb_obj == NULL) { r = amdgpu_bo_create(adev, AMDGPU_MAX_WB * 4, PAGE_SIZE, true, AMDGPU_GEM_DOMAIN_GTT, 0, NULL, NULL, &adev->wb.wb_obj); if (r) { dev_warn(adev->dev, "(%d) create WB bo failed\n", r); return r; } r = amdgpu_bo_reserve(adev->wb.wb_obj, false); if (unlikely(r != 0)) { amdgpu_wb_fini(adev); return r; } r = amdgpu_bo_pin(adev->wb.wb_obj, AMDGPU_GEM_DOMAIN_GTT, &adev->wb.gpu_addr); if (r) { amdgpu_bo_unreserve(adev->wb.wb_obj); dev_warn(adev->dev, "(%d) pin WB bo failed\n", r); amdgpu_wb_fini(adev); return r; } r = amdgpu_bo_kmap(adev->wb.wb_obj, (void **)&adev->wb.wb); amdgpu_bo_unreserve(adev->wb.wb_obj); if (r) { dev_warn(adev->dev, "(%d) map WB bo failed\n", r); amdgpu_wb_fini(adev); return r; } adev->wb.num_wb = AMDGPU_MAX_WB; memset(&adev->wb.used, 0, sizeof(adev->wb.used)); /* clear wb memory */ memset((char *)adev->wb.wb, 0, AMDGPU_GPU_PAGE_SIZE); } return 0; } /** * amdgpu_wb_get - Allocate a wb entry *