Skip to content

Commit

Permalink
quirk: AMD Navi 10 series vendor specific reset (v2)
Browse files Browse the repository at this point in the history
Signed-off-by: Geoffrey McRae <geoff@hostfission.com>
  • Loading branch information
gnif committed Nov 26, 2019
1 parent be2eca9 commit e3bb0b3
Showing 1 changed file with 132 additions and 0 deletions.
132 changes: 132 additions & 0 deletions drivers/pci/quirks.c
Original file line number Diff line number Diff line change
Expand Up @@ -3895,6 +3895,131 @@ static int delay_250ms_after_flr(struct pci_dev *dev, int probe)
return 0;
}

/*
* AMD Navi 10 series GPUs require a vendor specific reset procedure.
* According to AMD a PSP mode 2 reset should be enough however at this
* time the details of how to perform this are not available to us.
* Instead we can signal the SMU to enter and exit BACO which has the same
* desired effect.
*/
static int reset_amd_navi10(struct pci_dev *dev, int probe)
{
const int mmMP0_SMN_C2PMSG_81 = 0x16091;
const int mmMP1_SMN_C2PMSG_66 = 0x16282;
const int mmMP1_SMN_C2PMSG_82 = 0x16292;
const int mmMP1_SMN_C2PMSG_90 = 0x1629a;

u16 cfg;
resource_size_t mmio_base, mmio_size;
uint32_t __iomem * mmio;
unsigned int sol;
unsigned int timeout;

/*
* if the device has FLR return -ENOTTY indicating that we have no
* device-specific reset method.
*/
if (pcie_has_flr(dev))
return -ENOTTY;

/* bus resets still cause navi to flake out */
dev->dev_flags |= PCI_DEV_FLAGS_NO_BUS_RESET;

if (probe)
return 0;

/* map BAR5 */
mmio_base = pci_resource_start(dev, 5);
mmio_size = pci_resource_len(dev, 5);
mmio = ioremap_nocache(mmio_base, mmio_size);
if (mmio == NULL) {
pci_disable_device(dev);
pci_err(dev, "Navi10: cannot iomap device\n");
return 0;
}

/* save the PCI state and enable memory access */
pci_read_config_word(dev, PCI_COMMAND, &cfg);
pci_write_config_word(dev, PCI_COMMAND, cfg | PCI_COMMAND_MEMORY);

#define SMU_WAIT() \
for(timeout = 1000; timeout && (readl(mmio + mmMP1_SMN_C2PMSG_90) & 0xFFFFFFFFL) == 0; --timeout) \
udelay(1000); \
if (readl(mmio + mmMP1_SMN_C2PMSG_90) != 0x1) \
pci_info(dev, "Navi10: SMU error 0x%x (line %d)\n", \
readl(mmio + mmMP1_SMN_C2PMSG_90), __LINE__);

pci_set_power_state(dev, PCI_D0);

/* it's important we wait for the SOC to be ready */
for(timeout = 1000; timeout; --timeout) {
sol = readl(mmio + mmMP0_SMN_C2PMSG_81);
if (sol != 0xFFFFFFFF)
break;
udelay(1000);
}

if (sol == 0xFFFFFFFF)
pci_warn(dev, "Navi10: timeout waiting for wakeup, continuing anyway\n");

/* check the sign of life indicator */
if (sol == 0x0) {
goto out;
}

pci_info(dev, "Navi10: performing BACO reset\n");

/* save the state around the reset */
pci_save_state(dev);

/* the SMU might be busy already, wait for it */
SMU_WAIT();

/* send PPSMC_MSG_ArmD3 with param */
writel(0x00, mmio + mmMP1_SMN_C2PMSG_90);
writel(0x00, mmio + mmMP1_SMN_C2PMSG_82); // BACO_SEQ_BACO
writel(0x46, mmio + mmMP1_SMN_C2PMSG_66);
SMU_WAIT();

/* send PPSMC_MSG_EnterBaco with param */
writel(0x00, mmio + mmMP1_SMN_C2PMSG_90);
writel(0x00, mmio + mmMP1_SMN_C2PMSG_82); // BACO_SEQ_BACO
writel(0x18, mmio + mmMP1_SMN_C2PMSG_66);
SMU_WAIT();

/* wait for the regulators to shutdown */
mdelay(1000);

/* send PPSMC_MSG_ExitBaco */
writel(0x00, mmio + mmMP1_SMN_C2PMSG_90);
writel(0x19, mmio + mmMP1_SMN_C2PMSG_66);
SMU_WAIT();

#undef SMU_WAIT

/* wait for the SOC register to become valid */
for(timeout = 1000; timeout; --timeout) {
sol = readl(mmio + mmMP0_SMN_C2PMSG_81);
if (sol != 0xFFFFFFFF)
break;
udelay(1000);
}

if (sol != 0x0) {
pci_err(dev, "Navi10: sol register = 0x%x\n", sol);
goto out;
}

out:
/* unmap BAR5 */
iounmap(mmio);

/* restore the state and command register */
pci_restore_state(dev);
pci_write_config_word(dev, PCI_COMMAND, cfg);
return 0;
}

static const struct pci_dev_reset_methods pci_dev_reset_methods[] = {
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82599_SFP_VF,
reset_intel_82599_sfp_virtfn },
Expand All @@ -3906,6 +4031,13 @@ static const struct pci_dev_reset_methods pci_dev_reset_methods[] = {
{ PCI_VENDOR_ID_INTEL, 0x0953, delay_250ms_after_flr },
{ PCI_VENDOR_ID_CHELSIO, PCI_ANY_ID,
reset_chelsio_generic_dev },
{ PCI_VENDOR_ID_ATI, 0x7310, reset_amd_navi10 },
{ PCI_VENDOR_ID_ATI, 0x7312, reset_amd_navi10 },
{ PCI_VENDOR_ID_ATI, 0x7318, reset_amd_navi10 },
{ PCI_VENDOR_ID_ATI, 0x7319, reset_amd_navi10 },
{ PCI_VENDOR_ID_ATI, 0x731a, reset_amd_navi10 },
{ PCI_VENDOR_ID_ATI, 0x731b, reset_amd_navi10 },
{ PCI_VENDOR_ID_ATI, 0x731f, reset_amd_navi10 },
{ 0 }
};

Expand Down

0 comments on commit e3bb0b3

Please sign in to comment.