/* Vendor Reset - Vendor Specific Reset Copyright (C) 2020 Geoffrey McRae Copyright (C) 2020 Adam Madsen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include "vendor-reset-dev.h" #include "soc15_common.h" #include "soc15.h" #include "common.h" #include "compat.h" int amd_common_pre_reset(struct vendor_reset_dev *dev) { struct amd_vendor_private *priv; struct pci_dev *pdev = dev->pdev; int ret; /* disable bus reset for the card, seems to be an issue with all of em */ pdev->dev_flags |= PCI_DEV_FLAGS_NO_BUS_RESET; /* do not try to reset the card under amdgpu, it will cause problems */ if (pdev->dev.driver && strcmp(pdev->dev.driver->name, "amdgpu")) return -ENOTTY; priv = kzalloc(sizeof *priv, GFP_KERNEL); if (!priv) return -ENOMEM; dev->vendor_private = priv; priv->vdev = dev; spin_lock_init(&priv->pcie_lock); spin_lock_init(&priv->reg_lock); mutex_init(&priv->smu_lock); priv->mmio_base = pci_resource_start(pdev, 5); priv->mmio_size = pci_resource_len(pdev, 5); priv->mmio = ioremap(priv->mmio_base, priv->mmio_size); if (!priv->mmio) { pci_err(pdev, "Could not mmap device\n"); ret = -ENOMEM; goto err_free; } pci_set_power_state(pdev, PCI_D0); pci_clear_master(pdev); pci_save_state(pdev); priv->saved_state = pci_store_saved_state(pdev); pci_read_config_word(pdev, PCI_COMMAND, &priv->cfg); pci_write_config_word(pdev, PCI_COMMAND, priv->cfg | PCI_COMMAND_MEMORY | PCI_COMMAND_INTX_DISABLE); return 0; err_free: kfree(priv); return ret; } int amd_common_post_reset(struct vendor_reset_dev *dev) { struct amd_vendor_private *priv = amd_private(dev); struct pci_dev *pdev = dev->pdev; if (priv->mmio) { iounmap(priv->mmio); priv->mmio = NULL; } if (priv->saved_state) { pci_load_and_free_saved_state(pdev, &priv->saved_state); pci_restore_state(pdev); } pci_write_config_word(pdev, PCI_COMMAND, priv->cfg); /* don't try to go to low power if reset failed */ if (!dev->reset_ret) pci_set_power_state(pdev, PCI_D3hot); kfree(priv); dev->vendor_private = NULL; mutex_destroy(&priv->smu_lock); return 0; } int smu_wait(struct amd_fake_dev *adev) { u32 ret; int timeout; for (timeout = 100000; timeout && (RREG32_SOC15(MP1, 0, mmMP1_SMN_C2PMSG_90) & MP1_C2PMSG_90__CONTENT_MASK) == 0; --timeout) udelay(1); if ((ret = RREG32_SOC15(MP1, 0, mmMP1_SMN_C2PMSG_90)) != 0x1) pci_info(adev_to_amd_private(adev)->vdev->pdev, "SMU error 0x%x\n", ret); return ret; } int smum_send_msg_to_smc_with_parameter(struct amd_fake_dev *adev, uint16_t msg, uint32_t parameter, uint32_t *resp) { int ret = 0; mutex_lock(&adev_to_amd_private(adev)->smu_lock); ret = smu_wait(adev); if (ret != 0x1) { ret = -ETIMEDOUT; goto out; } WREG32_SOC15(MP1, 0, mmMP1_SMN_C2PMSG_90, 0); WREG32_SOC15(MP1, 0, mmMP1_SMN_C2PMSG_82, parameter); WREG32_SOC15(MP1, 0, mmMP1_SMN_C2PMSG_66, msg); ret = smu_wait(adev); if (ret != 0x01) { pci_err(adev_to_amd_private(adev)->vdev->pdev, "Failed to send message 0x%x: return 0x%x\n", msg, ret); goto out; } if (resp) *resp = RREG32_SOC15(MP1, 0, mmMP1_SMN_C2PMSG_82); ret = ret != 0x01; out: mutex_unlock(&adev_to_amd_private(adev)->smu_lock); return ret; } int smum_send_msg_to_smc(struct amd_fake_dev *adev, uint16_t msg, uint32_t *resp) { return smum_send_msg_to_smc_with_parameter(adev, msg, 0, resp); } /* from amdgpu_atombios.c */ void amdgpu_atombios_scratch_regs_engine_hung(struct amd_fake_dev *adev, bool hung) { u32 tmp = RREG32(adev->bios_scratch_reg_offset + 3); if (hung) tmp |= ATOM_S3_ASIC_GUI_ENGINE_HUNG; else tmp &= ~ATOM_S3_ASIC_GUI_ENGINE_HUNG; WREG32(adev->bios_scratch_reg_offset + 3, tmp); } /* from amdgpu_psp.c */ int psp_wait_for(struct amd_fake_dev *adev, uint32_t reg_index, uint32_t reg_val, uint32_t mask, bool check_changed) { uint32_t val; int i; for (i = 0; i < 100000; i++) { val = RREG32(reg_index); if (check_changed) { if (val != reg_val) return 0; } else { if ((val & mask) == reg_val) return 0; } udelay(1); } return -ETIME; }