You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
vendor-reset/src/amd/common.c

196 lines
4.8 KiB
C

/*
Vendor Reset - Vendor Specific Reset
Copyright (C) 2020 Geoffrey McRae <geoff@hostfission.com>
Copyright (C) 2020 Adam Madsen <adam@ajmadsen.com>
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 <linux/mm.h>
#include <linux/pci.h>
#include <linux/delay.h>
#include <linux/printk.h>
#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;
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;
}