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
clps711x-timer.c
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 *  Cirrus Logic CLPS711X clocksource driver
 *
 *  Copyright (C) 2014 Alexander Shiyan <shc_work@mail.ru>
 */

#include <linux/clk.h>
#include <linux/clockchips.h>
#include <linux/clocksource.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/sched_clock.h>
#include <linux/slab.h>

enum {
	CLPS711X_CLKSRC_CLOCKSOURCE,
	CLPS711X_CLKSRC_CLOCKEVENT,
};

static void __iomem *tcd;

static u64 notrace clps711x_sched_clock_read(void)
{
	return ~readw(tcd);
}

static void __init clps711x_clksrc_init(struct clk *clock, void __iomem *base)
{
	unsigned long rate = clk_get_rate(clock);

	tcd = base;

	clocksource_mmio_init(tcd, "clps711x-clocksource", rate, 300, 16,
			      clocksource_mmio_readw_down);

	sched_clock_register(clps711x_sched_clock_read, 16, rate);
}

static irqreturn_t clps711x_timer_interrupt(int irq, void *dev_id)
{
	struct clock_event_device *evt = dev_id;

	evt->event_handler(evt);

	return IRQ_HANDLED;
}

static int __init _clps711x_clkevt_init(struct clk *clock, void __iomem *base,
					unsigned int irq)
{
	struct clock_event_device *clkevt;
	unsigned long rate;

	clkevt = kzalloc(sizeof(*clkevt), GFP_KERNEL);
	if (!clkevt)
		return -ENOMEM;

	rate = clk_get_rate(clock);

	/* Set Timer prescaler */
	writew(DIV_ROUND_CLOSEST(rate, HZ), base);

	clkevt->name = "clps711x-clockevent";
	clkevt->rating = 300;
	clkevt->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_C3STOP;
	clkevt->cpumask = cpumask_of(0);
	clockevents_config_and_register(clkevt, HZ, 0, 0);

	return request_irq(irq, clps711x_timer_interrupt, IRQF_TIMER,
			   "clps711x-timer", clkevt);
}

static int __init clps711x_timer_init(struct device_node *np)
{
	unsigned int irq = irq_of_parse_and_map(np, 0);
	struct clk *clock = of_clk_get(np, 0);
	void __iomem *base = of_iomap(np, 0);

	if (!base)
		return -ENOMEM;
	if (!irq)
		return -EINVAL;
	if (IS_ERR(clock))
		return PTR_ERR(clock);

	switch (of_alias_get_id(np, "timer")) {
	case CLPS711X_CLKSRC_CLOCKSOURCE:
		clps711x_clksrc_init(clock, base);
		break;
	case CLPS711X_CLKSRC_CLOCKEVENT:
		return _clps711x_clkevt_init(clock, base, irq);
	default:
		return -EINVAL;
	}

	return 0;
}
TIMER_OF_DECLARE(clps711x, "cirrus,ep7209-timer", clps711x_timer_init);
back to top