Commit a1dcc1aa authored by Bjorn Helgaas's avatar Bjorn Helgaas
Browse files

Merge branch 'remotes/lorenzo/pci/brcmstb'

  - Assert fundamental reset on initialization (Nicolas Saenz Julienne)

  - Remove unnecessary clk_put(); devm_clk_get() handles this automatically
    (Jim Quinlan)

  - Fix outbound memory window register stride offset (Jim Quinlan)

  - Add "aspm-no-l0s" property for brcmstb and disable ASPM L0s when
    present (Jim Quinlan)

  - Add property to notify Raspberry Pi firmware of xHCI reset (Nicolas
    Saenz Julienne)

  - Add Raspberry Pi VL805 xHCI init function to trigger VL805 firmware
    load (Nicolas Saenz Julienne)

  - Wait in brcmstb probe for Raspberry Pi VL805 firmware initialization
    (Nicolas Saenz Julienne)

  - Load Raspberry Pi VL805 firmware in USB early handoff quirk (Nicolas
    Saenz Julienne)

* remotes/lorenzo/pci/brcmstb:
  USB: pci-quirks: Add Raspberry Pi 4 quirk
  PCI: brcmstb: Wait for Raspberry Pi's firmware when present
  firmware: raspberrypi: Introduce vl805 init routine
  soc: bcm2835: Add notify xHCI reset property
  PCI: brcmstb: Disable L0s component of ASPM if requested
  dt-bindings: PCI: brcmstb: New prop 'aspm-no-l0s'
  PCI: brcmstb: Fix window register offset from 4 to 8
  PCI: brcmstb: Don't clk_put() a managed clock
  PCI: brcmstb: Assert fundamental reset on initialization
parents 754262d1 c65822fe
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -56,6 +56,8 @@ properties:
    description: Indicates usage of spread-spectrum clocking.
    type: boolean

  aspm-no-l0s: true

required:
  - reg
  - dma-ranges
+2 −1
Original line number Diff line number Diff line
@@ -178,8 +178,9 @@ config ISCSI_IBFT
	  Otherwise, say N.

config RASPBERRYPI_FIRMWARE
	tristate "Raspberry Pi Firmware Driver"
	bool "Raspberry Pi Firmware Driver"
	depends on BCM2835_MBOX
	default USB_PCI
	help
	  This option enables support for communicating with the firmware on the
	  Raspberry Pi.
+61 −0
Original line number Diff line number Diff line
@@ -12,6 +12,8 @@
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/pci.h>
#include <linux/delay.h>
#include <soc/bcm2835/raspberrypi-firmware.h>

#define MBOX_MSG(chan, data28)		(((data28) & ~0xf) | ((chan) & 0xf))
@@ -19,6 +21,8 @@
#define MBOX_DATA28(msg)		((msg) & ~0xf)
#define MBOX_CHAN_PROPERTY		8

#define VL805_PCI_CONFIG_VERSION_OFFSET		0x50

static struct platform_device *rpi_hwmon;
static struct platform_device *rpi_clk;

@@ -286,6 +290,63 @@ struct rpi_firmware *rpi_firmware_get(struct device_node *firmware_node)
}
EXPORT_SYMBOL_GPL(rpi_firmware_get);

/*
 * The Raspberry Pi 4 gets its USB functionality from VL805, a PCIe chip that
 * implements xHCI. After a PCI reset, VL805's firmware may either be loaded
 * directly from an EEPROM or, if not present, by the SoC's co-processor,
 * VideoCore. RPi4's VideoCore OS contains both the non public firmware load
 * logic and the VL805 firmware blob. This function triggers the aforementioned
 * process.
 */
int rpi_firmware_init_vl805(struct pci_dev *pdev)
{
	struct device_node *fw_np;
	struct rpi_firmware *fw;
	u32 dev_addr, version;
	int ret;

	fw_np = of_find_compatible_node(NULL, NULL,
					"raspberrypi,bcm2835-firmware");
	if (!fw_np)
		return 0;

	fw = rpi_firmware_get(fw_np);
	of_node_put(fw_np);
	if (!fw)
		return -ENODEV;

	/*
	 * Make sure we don't trigger a firmware load unnecessarily.
	 *
	 * If something went wrong with PCI, this whole exercise would be
	 * futile as VideoCore expects from us a configured PCI bus. Just take
	 * the faulty version (likely ~0) and let xHCI's registration fail
	 * further down the line.
	 */
	pci_read_config_dword(pdev, VL805_PCI_CONFIG_VERSION_OFFSET, &version);
	if (version)
		goto exit;

	dev_addr = pdev->bus->number << 20 | PCI_SLOT(pdev->devfn) << 15 |
		   PCI_FUNC(pdev->devfn) << 12;

	ret = rpi_firmware_property(fw, RPI_FIRMWARE_NOTIFY_XHCI_RESET,
				    &dev_addr, sizeof(dev_addr));
	if (ret)
		return ret;

	/* Wait for vl805 to startup */
	usleep_range(200, 1000);

	pci_read_config_dword(pdev, VL805_PCI_CONFIG_VERSION_OFFSET,
			      &version);
exit:
	pci_info(pdev, "VL805 firmware version %08x\n", version);

	return 0;
}
EXPORT_SYMBOL_GPL(rpi_firmware_init_vl805);

static const struct of_device_id rpi_firmware_of_match[] = {
	{ .compatible = "raspberrypi,bcm2835-firmware", },
	{},
+33 −4
Original line number Diff line number Diff line
@@ -28,6 +28,8 @@
#include <linux/string.h>
#include <linux/types.h>

#include <soc/bcm2835/raspberrypi-firmware.h>

#include "../pci.h"

/* BRCM_PCIE_CAP_REGS - Offset for the mandatory capability config regs */
@@ -41,6 +43,9 @@
#define PCIE_RC_CFG_PRIV1_ID_VAL3			0x043c
#define  PCIE_RC_CFG_PRIV1_ID_VAL3_CLASS_CODE_MASK	0xffffff

#define PCIE_RC_CFG_PRIV1_LINK_CAPABILITY			0x04dc
#define  PCIE_RC_CFG_PRIV1_LINK_CAPABILITY_ASPM_SUPPORT_MASK	0xc00

#define PCIE_RC_DL_MDIO_ADDR				0x1100
#define PCIE_RC_DL_MDIO_WR_DATA				0x1104
#define PCIE_RC_DL_MDIO_RD_DATA				0x1108
@@ -54,11 +59,11 @@

#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LO		0x400c
#define PCIE_MEM_WIN0_LO(win)	\
		PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LO + ((win) * 4)
		PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LO + ((win) * 8)

#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_HI		0x4010
#define PCIE_MEM_WIN0_HI(win)	\
		PCIE_MISC_CPU_2_PCIE_MEM_WIN0_HI + ((win) * 4)
		PCIE_MISC_CPU_2_PCIE_MEM_WIN0_HI + ((win) * 8)

#define PCIE_MISC_RC_BAR1_CONFIG_LO			0x402c
#define  PCIE_MISC_RC_BAR1_CONFIG_LO_SIZE_MASK		0x1f
@@ -693,10 +698,11 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie)
	int num_out_wins = 0;
	u16 nlw, cls, lnksta;
	int i, ret;
	u32 tmp;
	u32 tmp, aspm_support;

	/* Reset the bridge */
	brcm_pcie_bridge_sw_init_set(pcie, 1);
	brcm_pcie_perst_set(pcie, 1);

	usleep_range(100, 200);

@@ -803,6 +809,15 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie)
		num_out_wins++;
	}

	/* Don't advertise L0s capability if 'aspm-no-l0s' */
	aspm_support = PCIE_LINK_STATE_L1;
	if (!of_property_read_bool(pcie->np, "aspm-no-l0s"))
		aspm_support |= PCIE_LINK_STATE_L0S;
	tmp = readl(base + PCIE_RC_CFG_PRIV1_LINK_CAPABILITY);
	u32p_replace_bits(&tmp, aspm_support,
		PCIE_RC_CFG_PRIV1_LINK_CAPABILITY_ASPM_SUPPORT_MASK);
	writel(tmp, base + PCIE_RC_CFG_PRIV1_LINK_CAPABILITY);

	/*
	 * For config space accesses on the RC, show the right class for
	 * a PCIe-PCIe bridge (the default setting is to be EP mode).
@@ -899,7 +914,6 @@ static void __brcm_pcie_remove(struct brcm_pcie *pcie)
	brcm_msi_remove(pcie);
	brcm_pcie_turn_off(pcie);
	clk_disable_unprepare(pcie->clk);
	clk_put(pcie->clk);
}

static int brcm_pcie_remove(struct platform_device *pdev)
@@ -917,11 +931,26 @@ static int brcm_pcie_probe(struct platform_device *pdev)
{
	struct device_node *np = pdev->dev.of_node, *msi_np;
	struct pci_host_bridge *bridge;
	struct device_node *fw_np;
	struct brcm_pcie *pcie;
	struct pci_bus *child;
	struct resource *res;
	int ret;

	/*
	 * We have to wait for Raspberry Pi's firmware interface to be up as a
	 * PCI fixup, rpi_firmware_init_vl805(), depends on it. This driver's
	 * probe can race with the firmware interface's (see
	 * drivers/firmware/raspberrypi.c) and potentially break the PCI fixup.
	 */
	fw_np = of_find_compatible_node(NULL, NULL,
					"raspberrypi,bcm2835-firmware");
	if (fw_np && !rpi_firmware_get(fw_np)) {
		of_node_put(fw_np);
		return -EPROBE_DEFER;
	}
	of_node_put(fw_np);

	bridge = devm_pci_alloc_host_bridge(&pdev->dev, sizeof(*pcie));
	if (!bridge)
		return -ENOMEM;
+16 −0
Original line number Diff line number Diff line
@@ -16,6 +16,9 @@
#include <linux/export.h>
#include <linux/acpi.h>
#include <linux/dmi.h>

#include <soc/bcm2835/raspberrypi-firmware.h>

#include "pci-quirks.h"
#include "xhci-ext-caps.h"

@@ -1243,11 +1246,24 @@ iounmap:

static void quirk_usb_early_handoff(struct pci_dev *pdev)
{
	int ret;

	/* Skip Netlogic mips SoC's internal PCI USB controller.
	 * This device does not need/support EHCI/OHCI handoff
	 */
	if (pdev->vendor == 0x184e)	/* vendor Netlogic */
		return;

	if (pdev->vendor == PCI_VENDOR_ID_VIA && pdev->device == 0x3483) {
		ret = rpi_firmware_init_vl805(pdev);
		if (ret) {
			/* Firmware might be outdated, or something failed */
			dev_warn(&pdev->dev,
				 "Failed to load VL805's firmware: %d. Will continue to attempt to work, but bad things might happen. You should fix this...\n",
				 ret);
		}
	}

	if (pdev->class != PCI_CLASS_SERIAL_USB_UHCI &&
			pdev->class != PCI_CLASS_SERIAL_USB_OHCI &&
			pdev->class != PCI_CLASS_SERIAL_USB_EHCI &&
Loading