https://github.com/torvalds/linux
Revision 275e88b06a277ccf89d9c471a777e9b4f8c552b0 authored by Rob Herring on 18 December 2020, 14:39:05 UTC, committed by Bjorn Helgaas on 26 December 2020, 03:58:36 UTC
Commit b9ac0f9dc8ea ("PCI: dwc: Move dw_pcie_setup_rc() to DWC common
code") broke enumeration of downstream devices on Tegra:

In non-working case (next-20201211):

  0001:00:00.0 PCI bridge: NVIDIA Corporation Device 1ad2 (rev a1)
  0001:01:00.0 SATA controller: Marvell Technology Group Ltd. Device 9171 (rev 13)
  0005:00:00.0 PCI bridge: NVIDIA Corporation Device 1ad0 (rev a1)

In working case (v5.10-rc7):

  0001:00:00.0 PCI bridge: Molex Incorporated Device 1ad2 (rev a1)
  0001:01:00.0 SATA controller: Marvell Technology Group Ltd. Device 9171 (rev 13)
  0005:00:00.0 PCI bridge: Molex Incorporated Device 1ad0 (rev a1)
  0005:01:00.0 PCI bridge: PLX Technology, Inc. Device 3380 (rev ab)
  0005:02:02.0 PCI bridge: PLX Technology, Inc. Device 3380 (rev ab)
  0005:03:00.0 USB controller: PLX Technology, Inc. Device 3380 (rev ab)

The problem seems to be dw_pcie_setup_rc() is now called twice before and
after the link up handling. The fix is to move Tegra's link up handling to
.start_link() function like other DWC drivers. Tegra is a bit more
complicated than others as it re-inits the whole DWC controller to retry
the link. With this, the initialization ordering is restored to match the
prior sequence.

Fixes: b9ac0f9dc8ea ("PCI: dwc: Move dw_pcie_setup_rc() to DWC common code")
Link: https://lore.kernel.org/r/20201218143905.1614098-1-robh@kernel.org
Reported-by: Mian Yousaf Kaukab <ykaukab@suse.de>
Tested-by: Mian Yousaf Kaukab <ykaukab@suse.de>
Signed-off-by: Rob Herring <robh@kernel.org>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Cc: Thierry Reding <thierry.reding@gmail.com>
Cc: Jonathan Hunter <jonathanh@nvidia.com>
Cc: Vidya Sagar <vidyas@nvidia.com>
1 parent 255b2d5
Raw File
Tip revision: 275e88b06a277ccf89d9c471a777e9b4f8c552b0 authored by Rob Herring on 18 December 2020, 14:39:05 UTC
PCI: tegra: Fix host link initialization
Tip revision: 275e88b
pvpanic.c
// SPDX-License-Identifier: GPL-2.0+
/*
 *  Pvpanic Device Support
 *
 *  Copyright (C) 2013 Fujitsu.
 *  Copyright (C) 2018 ZTE.
 */

#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#include <linux/acpi.h>
#include <linux/kernel.h>
#include <linux/kexec.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/types.h>
#include <uapi/misc/pvpanic.h>

static void __iomem *base;

MODULE_AUTHOR("Hu Tao <hutao@cn.fujitsu.com>");
MODULE_DESCRIPTION("pvpanic device driver");
MODULE_LICENSE("GPL");

static void
pvpanic_send_event(unsigned int event)
{
	iowrite8(event, base);
}

static int
pvpanic_panic_notify(struct notifier_block *nb, unsigned long code,
		     void *unused)
{
	unsigned int event = PVPANIC_PANICKED;

	if (kexec_crash_loaded())
		event = PVPANIC_CRASH_LOADED;

	pvpanic_send_event(event);

	return NOTIFY_DONE;
}

static struct notifier_block pvpanic_panic_nb = {
	.notifier_call = pvpanic_panic_notify,
	.priority = 1, /* let this called before broken drm_fb_helper */
};

#ifdef CONFIG_ACPI
static int pvpanic_add(struct acpi_device *device);
static int pvpanic_remove(struct acpi_device *device);

static const struct acpi_device_id pvpanic_device_ids[] = {
	{ "QEMU0001", 0 },
	{ "", 0 }
};
MODULE_DEVICE_TABLE(acpi, pvpanic_device_ids);

static struct acpi_driver pvpanic_driver = {
	.name =		"pvpanic",
	.class =	"QEMU",
	.ids =		pvpanic_device_ids,
	.ops =		{
				.add =		pvpanic_add,
				.remove =	pvpanic_remove,
			},
	.owner =	THIS_MODULE,
};

static acpi_status
pvpanic_walk_resources(struct acpi_resource *res, void *context)
{
	struct resource r;

	if (acpi_dev_resource_io(res, &r)) {
#ifdef CONFIG_HAS_IOPORT_MAP
		base = ioport_map(r.start, resource_size(&r));
		return AE_OK;
#else
		return AE_ERROR;
#endif
	} else if (acpi_dev_resource_memory(res, &r)) {
		base = ioremap(r.start, resource_size(&r));
		return AE_OK;
	}

	return AE_ERROR;
}

static int pvpanic_add(struct acpi_device *device)
{
	int ret;

	ret = acpi_bus_get_status(device);
	if (ret < 0)
		return ret;

	if (!device->status.enabled || !device->status.functional)
		return -ENODEV;

	acpi_walk_resources(device->handle, METHOD_NAME__CRS,
			    pvpanic_walk_resources, NULL);

	if (!base)
		return -ENODEV;

	atomic_notifier_chain_register(&panic_notifier_list,
				       &pvpanic_panic_nb);

	return 0;
}

static int pvpanic_remove(struct acpi_device *device)
{

	atomic_notifier_chain_unregister(&panic_notifier_list,
					 &pvpanic_panic_nb);
	iounmap(base);

	return 0;
}

static int pvpanic_register_acpi_driver(void)
{
	return acpi_bus_register_driver(&pvpanic_driver);
}

static void pvpanic_unregister_acpi_driver(void)
{
	acpi_bus_unregister_driver(&pvpanic_driver);
}
#else
static int pvpanic_register_acpi_driver(void)
{
	return -ENODEV;
}

static void pvpanic_unregister_acpi_driver(void) {}
#endif

static int pvpanic_mmio_probe(struct platform_device *pdev)
{
	base = devm_platform_ioremap_resource(pdev, 0);
	if (IS_ERR(base))
		return PTR_ERR(base);

	atomic_notifier_chain_register(&panic_notifier_list,
				       &pvpanic_panic_nb);

	return 0;
}

static int pvpanic_mmio_remove(struct platform_device *pdev)
{

	atomic_notifier_chain_unregister(&panic_notifier_list,
					 &pvpanic_panic_nb);

	return 0;
}

static const struct of_device_id pvpanic_mmio_match[] = {
	{ .compatible = "qemu,pvpanic-mmio", },
	{}
};

static struct platform_driver pvpanic_mmio_driver = {
	.driver = {
		.name = "pvpanic-mmio",
		.of_match_table = pvpanic_mmio_match,
	},
	.probe = pvpanic_mmio_probe,
	.remove = pvpanic_mmio_remove,
};

static int __init pvpanic_mmio_init(void)
{
	if (acpi_disabled)
		return platform_driver_register(&pvpanic_mmio_driver);
	else
		return pvpanic_register_acpi_driver();
}

static void __exit pvpanic_mmio_exit(void)
{
	if (acpi_disabled)
		platform_driver_unregister(&pvpanic_mmio_driver);
	else
		pvpanic_unregister_acpi_driver();
}

module_init(pvpanic_mmio_init);
module_exit(pvpanic_mmio_exit);
back to top