Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4b83bd45 authored by Gavin Shan's avatar Gavin Shan Committed by Benjamin Herrenschmidt
Browse files

powerpc/eeh: Don't use pci_dev during BAR restore



While restoring BARs for one specific PCI device, the pci_dev
instance should have been released. So it's not reliable to use
the pci_dev instance on restoring BARs. However, we still need
some information (e.g. PCIe capability position, header type) from
the pci_dev instance. So we have to store those information to
EEH device in advance.

Signed-off-by: default avatarGavin Shan <shangw@linux.vnet.ibm.com>
Signed-off-by: default avatarBenjamin Herrenschmidt <benh@kernel.crashing.org>
parent f5c57710
Loading
Loading
Loading
Loading
+6 −2
Original line number Diff line number Diff line
@@ -84,8 +84,11 @@ struct eeh_pe {
 * another tree except the currently existing tree of PCI
 * buses and PCI devices
 */
#define EEH_DEV_IRQ_DISABLED	(1 << 0)	/* Interrupt disabled	*/
#define EEH_DEV_DISCONNECTED	(1 << 1)	/* Removing from PE	*/
#define EEH_DEV_BRIDGE		(1 << 0)	/* PCI bridge		*/
#define EEH_DEV_ROOT_PORT	(1 << 1)	/* PCIe root port	*/
#define EEH_DEV_DS_PORT		(1 << 2)	/* Downstream port	*/
#define EEH_DEV_IRQ_DISABLED	(1 << 3)	/* Interrupt disabled	*/
#define EEH_DEV_DISCONNECTED	(1 << 4)	/* Removing from PE	*/

struct eeh_dev {
	int mode;			/* EEH mode			*/
@@ -93,6 +96,7 @@ struct eeh_dev {
	int config_addr;		/* Config address		*/
	int pe_config_addr;		/* PE config address		*/
	u32 config_space[16];		/* Saved PCI config space	*/
	u8 pcie_cap;			/* Saved PCIe capability	*/
	struct eeh_pe *pe;		/* Associated PE		*/
	struct list_head list;		/* Form link list in the PE	*/
	struct pci_controller *phb;	/* Associated PHB		*/
+12 −13
Original line number Diff line number Diff line
@@ -578,7 +578,7 @@ void eeh_pe_state_clear(struct eeh_pe *pe, int state)
 * blocked on normal path during the stage. So we need utilize
 * eeh operations, which is always permitted.
 */
static void eeh_bridge_check_link(struct pci_dev *pdev,
static void eeh_bridge_check_link(struct eeh_dev *edev,
				  struct device_node *dn)
{
	int cap;
@@ -589,16 +589,17 @@ static void eeh_bridge_check_link(struct pci_dev *pdev,
	 * We only check root port and downstream ports of
	 * PCIe switches
	 */
	if (!pci_is_pcie(pdev) ||
	    (pci_pcie_type(pdev) != PCI_EXP_TYPE_ROOT_PORT &&
	     pci_pcie_type(pdev) != PCI_EXP_TYPE_DOWNSTREAM))
	if (!(edev->mode & (EEH_DEV_ROOT_PORT | EEH_DEV_DS_PORT)))
		return;

	pr_debug("%s: Check PCIe link for %s ...\n",
		 __func__, pci_name(pdev));
	pr_debug("%s: Check PCIe link for %04x:%02x:%02x.%01x ...\n",
		 __func__, edev->phb->global_number,
		 edev->config_addr >> 8,
		 PCI_SLOT(edev->config_addr & 0xFF),
		 PCI_FUNC(edev->config_addr & 0xFF));

	/* Check slot status */
	cap = pdev->pcie_cap;
	cap = edev->pcie_cap;
	eeh_ops->read_config(dn, cap + PCI_EXP_SLTSTA, 2, &val);
	if (!(val & PCI_EXP_SLTSTA_PDS)) {
		pr_debug("  No card in the slot (0x%04x) !\n", val);
@@ -652,8 +653,7 @@ static void eeh_bridge_check_link(struct pci_dev *pdev,
#define BYTE_SWAP(OFF)	(8*((OFF)/4)+3-(OFF))
#define SAVED_BYTE(OFF)	(((u8 *)(edev->config_space))[BYTE_SWAP(OFF)])

static void eeh_restore_bridge_bars(struct pci_dev *pdev,
				    struct eeh_dev *edev,
static void eeh_restore_bridge_bars(struct eeh_dev *edev,
				    struct device_node *dn)
{
	int i;
@@ -679,7 +679,7 @@ static void eeh_restore_bridge_bars(struct pci_dev *pdev,
	eeh_ops->write_config(dn, PCI_COMMAND, 4, edev->config_space[1]);

	/* Check the PCIe link is ready */
	eeh_bridge_check_link(pdev, dn);
	eeh_bridge_check_link(edev, dn);
}

static void eeh_restore_device_bars(struct eeh_dev *edev,
@@ -729,12 +729,11 @@ static void eeh_restore_device_bars(struct eeh_dev *edev,
static void *eeh_restore_one_device_bars(void *data, void *flag)
{
	struct eeh_dev *edev = (struct eeh_dev *)data;
	struct pci_dev *pdev = eeh_dev_to_pci_dev(edev);
	struct device_node *dn = eeh_dev_to_of_node(edev);

	/* Do special restore for bridges */
	if (pdev->hdr_type == PCI_HEADER_TYPE_BRIDGE)
		eeh_restore_bridge_bars(pdev, edev, dn);
	if (edev->mode & EEH_DEV_BRIDGE)
		eeh_restore_bridge_bars(edev, dn);
	else
		eeh_restore_device_bars(edev, dn);

+11 −0
Original line number Diff line number Diff line
@@ -124,6 +124,17 @@ static int powernv_eeh_dev_probe(struct pci_dev *dev, void *flag)
	/* Initialize eeh device */
	edev->class_code	= dev->class;
	edev->mode		= 0;
	if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE)
		edev->mode |= EEH_DEV_BRIDGE;
	if (pci_is_pcie(dev)) {
		edev->pcie_cap = pci_pcie_cap(dev);

		if (pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT)
			edev->mode |= EEH_DEV_ROOT_PORT;
		else if (pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM)
			edev->mode |= EEH_DEV_DS_PORT;
	}

	edev->config_addr	= ((dev->bus->number << 8) | dev->devfn);
	edev->pe_config_addr	= phb->bdfn_to_pe(phb, dev->bus, dev->devfn & 0xff);

+62 −1
Original line number Diff line number Diff line
@@ -133,6 +133,48 @@ static int pseries_eeh_init(void)
	return 0;
}

static int pseries_eeh_cap_start(struct device_node *dn)
{
	struct pci_dn *pdn = PCI_DN(dn);
	u32 status;

	if (!pdn)
		return 0;

	rtas_read_config(pdn, PCI_STATUS, 2, &status);
	if (!(status & PCI_STATUS_CAP_LIST))
		return 0;

	return PCI_CAPABILITY_LIST;
}


static int pseries_eeh_find_cap(struct device_node *dn, int cap)
{
	struct pci_dn *pdn = PCI_DN(dn);
	int pos = pseries_eeh_cap_start(dn);
	int cnt = 48;	/* Maximal number of capabilities */
	u32 id;

	if (!pos)
		return 0;

        while (cnt--) {
		rtas_read_config(pdn, pos, 1, &pos);
		if (pos < 0x40)
			break;
		pos &= ~3;
		rtas_read_config(pdn, pos + PCI_CAP_LIST_ID, 1, &id);
		if (id == 0xff)
			break;
		if (id == cap)
			return pos;
		pos += PCI_CAP_LIST_NEXT;
	}

	return 0;
}

/**
 * pseries_eeh_of_probe - EEH probe on the given device
 * @dn: OF node
@@ -146,8 +188,10 @@ static void *pseries_eeh_of_probe(struct device_node *dn, void *flag)
{
	struct eeh_dev *edev;
	struct eeh_pe pe;
	struct pci_dn *pdn = PCI_DN(dn);
	const u32 *class_code, *vendor_id, *device_id;
	const u32 *regs;
	u32 pcie_flags;
	int enable = 0;
	int ret;

@@ -167,9 +211,26 @@ static void *pseries_eeh_of_probe(struct device_node *dn, void *flag)
	if (dn->type && !strcmp(dn->type, "isa"))
		return NULL;

	/* Update class code and mode of eeh device */
	/*
	 * Update class code and mode of eeh device. We need
	 * correctly reflects that current device is root port
	 * or PCIe switch downstream port.
	 */
	edev->class_code = *class_code;
	edev->pcie_cap = pseries_eeh_find_cap(dn, PCI_CAP_ID_EXP);
	edev->mode = 0;
	if ((edev->class_code >> 8) == PCI_CLASS_BRIDGE_PCI) {
		edev->mode |= EEH_DEV_BRIDGE;
		if (edev->pcie_cap) {
			rtas_read_config(pdn, edev->pcie_cap + PCI_EXP_FLAGS,
					 2, &pcie_flags);
			pcie_flags = (pcie_flags & PCI_EXP_FLAGS_TYPE) >> 4;
			if (pcie_flags == PCI_EXP_TYPE_ROOT_PORT)
				edev->mode |= EEH_DEV_ROOT_PORT;
			else if (pcie_flags == PCI_EXP_TYPE_DOWNSTREAM)
				edev->mode |= EEH_DEV_DS_PORT;
		}
	}

	/* Retrieve the device address */
	regs = of_get_property(dn, "reg", NULL);