Unverified Commit 297a8114 authored by openeuler-ci-bot's avatar openeuler-ci-bot Committed by Gitee
Browse files

!9119 PCI: aardvark: Fix kernel panic during PIO transfer

Merge Pull Request from: @ci-robot 
 
PR sync from: Zeng Heng <zengheng4@huawei.com>
https://mailweb.openeuler.org/hyperkitty/list/kernel@openeuler.org/message/7LOO4SPEBFBPGIZ2PG4RYPHZLDIZEUNY/ 
Pali Rohár (2):
  PCI: aardvark: Increase polling delay to 1.5s while waiting for PIO
    response
  PCI: aardvark: Fix kernel panic during PIO transfer

Remi Pommarel (1):
  PCI: aardvark: Don't rely on jiffies while holding spinlock


--
2.25.1
 
https://gitee.com/src-openeuler/kernel/issues/I9R4IP 
 
Link:https://gitee.com/openeuler/kernel/pulls/9119

 

Reviewed-by: default avatarLiu YongQiang <liuyongqiang13@huawei.com>
Reviewed-by: default avatarWei Li <liwei391@huawei.com>
Signed-off-by: default avatarZhang Changzhong <zhangchangzhong@huawei.com>
parents 8e65ce01 e250ce44
Loading
Loading
Loading
Loading
+45 −14
Original line number Diff line number Diff line
@@ -166,7 +166,8 @@
	(PCIE_CONF_BUS(bus) | PCIE_CONF_DEV(PCI_SLOT(devfn))	| \
	 PCIE_CONF_FUNC(PCI_FUNC(devfn)) | PCIE_CONF_REG(where))

#define PIO_TIMEOUT_MS			1
#define PIO_RETRY_CNT			750000 /* 1.5 s */
#define PIO_RETRY_DELAY			2 /* 2 us*/

#define LINK_WAIT_MAX_RETRIES		10
#define LINK_WAIT_USLEEP_MIN		90000
@@ -373,20 +374,19 @@ static void advk_pcie_check_pio_status(struct advk_pcie *pcie)
static int advk_pcie_wait_pio(struct advk_pcie *pcie)
{
	struct device *dev = &pcie->pdev->dev;
	unsigned long timeout;
	int i;

	timeout = jiffies + msecs_to_jiffies(PIO_TIMEOUT_MS);

	while (time_before(jiffies, timeout)) {
	for (i = 0; i < PIO_RETRY_CNT; i++) {
		u32 start, isr;

		start = advk_readl(pcie, PIO_START);
		isr = advk_readl(pcie, PIO_ISR);
		if (!start && isr)
			return 0;
		udelay(PIO_RETRY_DELAY);
	}

	dev_err(dev, "config read/write timed out\n");
	dev_err(dev, "PIO read/write transfer time out\n");
	return -ETIMEDOUT;
}

@@ -399,6 +399,35 @@ static bool advk_pcie_valid_device(struct advk_pcie *pcie, struct pci_bus *bus,
	return true;
}

static bool advk_pcie_pio_is_running(struct advk_pcie *pcie)
{
	struct device *dev = &pcie->pdev->dev;

	/*
	 * Trying to start a new PIO transfer when previous has not completed
	 * cause External Abort on CPU which results in kernel panic:
	 *
	 *     SError Interrupt on CPU0, code 0xbf000002 -- SError
	 *     Kernel panic - not syncing: Asynchronous SError Interrupt
	 *
	 * Functions advk_pcie_rd_conf() and advk_pcie_wr_conf() are protected
	 * by raw_spin_lock_irqsave() at pci_lock_config() level to prevent
	 * concurrent calls at the same time. But because PIO transfer may take
	 * about 1.5s when link is down or card is disconnected, it means that
	 * advk_pcie_wait_pio() does not always have to wait for completion.
	 *
	 * Some versions of ARM Trusted Firmware handles this External Abort at
	 * EL3 level and mask it to prevent kernel panic. Relevant TF-A commit:
	 * https://git.trustedfirmware.org/TF-A/trusted-firmware-a.git/commit/?id=3c7dcdac5c50
	 */
	if (advk_readl(pcie, PIO_START)) {
		dev_err(dev, "Previous PIO read/write transfer is still running\n");
		return true;
	}

	return false;
}

static int advk_pcie_rd_conf(struct pci_bus *bus, u32 devfn,
			     int where, int size, u32 *val)
{
@@ -411,9 +440,10 @@ static int advk_pcie_rd_conf(struct pci_bus *bus, u32 devfn,
		return PCIBIOS_DEVICE_NOT_FOUND;
	}

	/* Start PIO */
	advk_writel(pcie, 0, PIO_START);
	advk_writel(pcie, 1, PIO_ISR);
	if (advk_pcie_pio_is_running(pcie)) {
		*val = 0xffffffff;
		return PCIBIOS_SET_FAILED;
	}

	/* Program the control register */
	reg = advk_readl(pcie, PIO_CTRL);
@@ -432,7 +462,8 @@ static int advk_pcie_rd_conf(struct pci_bus *bus, u32 devfn,
	/* Program the data strobe */
	advk_writel(pcie, 0xf, PIO_WR_DATA_STRB);

	/* Start the transfer */
	/* Clear PIO DONE ISR and start the transfer */
	advk_writel(pcie, 1, PIO_ISR);
	advk_writel(pcie, 1, PIO_START);

	ret = advk_pcie_wait_pio(pcie);
@@ -466,9 +497,8 @@ static int advk_pcie_wr_conf(struct pci_bus *bus, u32 devfn,
	if (where % size)
		return PCIBIOS_SET_FAILED;

	/* Start PIO */
	advk_writel(pcie, 0, PIO_START);
	advk_writel(pcie, 1, PIO_ISR);
	if (advk_pcie_pio_is_running(pcie))
		return PCIBIOS_SET_FAILED;

	/* Program the control register */
	reg = advk_readl(pcie, PIO_CTRL);
@@ -495,7 +525,8 @@ static int advk_pcie_wr_conf(struct pci_bus *bus, u32 devfn,
	/* Program the data strobe */
	advk_writel(pcie, data_strobe, PIO_WR_DATA_STRB);

	/* Start the transfer */
	/* Clear PIO DONE ISR and start the transfer */
	advk_writel(pcie, 1, PIO_ISR);
	advk_writel(pcie, 1, PIO_START);

	ret = advk_pcie_wait_pio(pcie);