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
This commit is contained in:
Bjorn Helgaas 2020-06-04 12:59:14 -05:00
commit a1dcc1aa6f
6 changed files with 122 additions and 6 deletions

View File

@ -56,6 +56,8 @@ properties:
description: Indicates usage of spread-spectrum clocking. description: Indicates usage of spread-spectrum clocking.
type: boolean type: boolean
aspm-no-l0s: true
required: required:
- reg - reg
- dma-ranges - dma-ranges

View File

@ -178,8 +178,9 @@ config ISCSI_IBFT
Otherwise, say N. Otherwise, say N.
config RASPBERRYPI_FIRMWARE config RASPBERRYPI_FIRMWARE
tristate "Raspberry Pi Firmware Driver" bool "Raspberry Pi Firmware Driver"
depends on BCM2835_MBOX depends on BCM2835_MBOX
default USB_PCI
help help
This option enables support for communicating with the firmware on the This option enables support for communicating with the firmware on the
Raspberry Pi. Raspberry Pi.

View File

@ -12,6 +12,8 @@
#include <linux/of_platform.h> #include <linux/of_platform.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/pci.h>
#include <linux/delay.h>
#include <soc/bcm2835/raspberrypi-firmware.h> #include <soc/bcm2835/raspberrypi-firmware.h>
#define MBOX_MSG(chan, data28) (((data28) & ~0xf) | ((chan) & 0xf)) #define MBOX_MSG(chan, data28) (((data28) & ~0xf) | ((chan) & 0xf))
@ -19,6 +21,8 @@
#define MBOX_DATA28(msg) ((msg) & ~0xf) #define MBOX_DATA28(msg) ((msg) & ~0xf)
#define MBOX_CHAN_PROPERTY 8 #define MBOX_CHAN_PROPERTY 8
#define VL805_PCI_CONFIG_VERSION_OFFSET 0x50
static struct platform_device *rpi_hwmon; static struct platform_device *rpi_hwmon;
static struct platform_device *rpi_clk; 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); 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[] = { static const struct of_device_id rpi_firmware_of_match[] = {
{ .compatible = "raspberrypi,bcm2835-firmware", }, { .compatible = "raspberrypi,bcm2835-firmware", },
{}, {},

View File

@ -28,6 +28,8 @@
#include <linux/string.h> #include <linux/string.h>
#include <linux/types.h> #include <linux/types.h>
#include <soc/bcm2835/raspberrypi-firmware.h>
#include "../pci.h" #include "../pci.h"
/* BRCM_PCIE_CAP_REGS - Offset for the mandatory capability config regs */ /* 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 0x043c
#define PCIE_RC_CFG_PRIV1_ID_VAL3_CLASS_CODE_MASK 0xffffff #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_ADDR 0x1100
#define PCIE_RC_DL_MDIO_WR_DATA 0x1104 #define PCIE_RC_DL_MDIO_WR_DATA 0x1104
#define PCIE_RC_DL_MDIO_RD_DATA 0x1108 #define PCIE_RC_DL_MDIO_RD_DATA 0x1108
@ -54,11 +59,11 @@
#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LO 0x400c #define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LO 0x400c
#define PCIE_MEM_WIN0_LO(win) \ #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_MISC_CPU_2_PCIE_MEM_WIN0_HI 0x4010
#define PCIE_MEM_WIN0_HI(win) \ #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 0x402c
#define PCIE_MISC_RC_BAR1_CONFIG_LO_SIZE_MASK 0x1f #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; int num_out_wins = 0;
u16 nlw, cls, lnksta; u16 nlw, cls, lnksta;
int i, ret; int i, ret;
u32 tmp; u32 tmp, aspm_support;
/* Reset the bridge */ /* Reset the bridge */
brcm_pcie_bridge_sw_init_set(pcie, 1); brcm_pcie_bridge_sw_init_set(pcie, 1);
brcm_pcie_perst_set(pcie, 1);
usleep_range(100, 200); usleep_range(100, 200);
@ -803,6 +809,15 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie)
num_out_wins++; 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 * For config space accesses on the RC, show the right class for
* a PCIe-PCIe bridge (the default setting is to be EP mode). * 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_msi_remove(pcie);
brcm_pcie_turn_off(pcie); brcm_pcie_turn_off(pcie);
clk_disable_unprepare(pcie->clk); clk_disable_unprepare(pcie->clk);
clk_put(pcie->clk);
} }
static int brcm_pcie_remove(struct platform_device *pdev) 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 device_node *np = pdev->dev.of_node, *msi_np;
struct pci_host_bridge *bridge; struct pci_host_bridge *bridge;
struct device_node *fw_np;
struct brcm_pcie *pcie; struct brcm_pcie *pcie;
struct pci_bus *child; struct pci_bus *child;
struct resource *res; struct resource *res;
int ret; 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)); bridge = devm_pci_alloc_host_bridge(&pdev->dev, sizeof(*pcie));
if (!bridge) if (!bridge)
return -ENOMEM; return -ENOMEM;

View File

@ -16,6 +16,9 @@
#include <linux/export.h> #include <linux/export.h>
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/dmi.h> #include <linux/dmi.h>
#include <soc/bcm2835/raspberrypi-firmware.h>
#include "pci-quirks.h" #include "pci-quirks.h"
#include "xhci-ext-caps.h" #include "xhci-ext-caps.h"
@ -1243,11 +1246,24 @@ static void quirk_usb_handoff_xhci(struct pci_dev *pdev)
static void quirk_usb_early_handoff(struct pci_dev *pdev) static void quirk_usb_early_handoff(struct pci_dev *pdev)
{ {
int ret;
/* Skip Netlogic mips SoC's internal PCI USB controller. /* Skip Netlogic mips SoC's internal PCI USB controller.
* This device does not need/support EHCI/OHCI handoff * This device does not need/support EHCI/OHCI handoff
*/ */
if (pdev->vendor == 0x184e) /* vendor Netlogic */ if (pdev->vendor == 0x184e) /* vendor Netlogic */
return; 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 && if (pdev->class != PCI_CLASS_SERIAL_USB_UHCI &&
pdev->class != PCI_CLASS_SERIAL_USB_OHCI && pdev->class != PCI_CLASS_SERIAL_USB_OHCI &&
pdev->class != PCI_CLASS_SERIAL_USB_EHCI && pdev->class != PCI_CLASS_SERIAL_USB_EHCI &&

View File

@ -10,6 +10,7 @@
#include <linux/of_device.h> #include <linux/of_device.h>
struct rpi_firmware; struct rpi_firmware;
struct pci_dev;
enum rpi_firmware_property_status { enum rpi_firmware_property_status {
RPI_FIRMWARE_STATUS_REQUEST = 0, RPI_FIRMWARE_STATUS_REQUEST = 0,
@ -90,7 +91,7 @@ enum rpi_firmware_property_tag {
RPI_FIRMWARE_SET_PERIPH_REG = 0x00038045, RPI_FIRMWARE_SET_PERIPH_REG = 0x00038045,
RPI_FIRMWARE_GET_POE_HAT_VAL = 0x00030049, RPI_FIRMWARE_GET_POE_HAT_VAL = 0x00030049,
RPI_FIRMWARE_SET_POE_HAT_VAL = 0x00030050, RPI_FIRMWARE_SET_POE_HAT_VAL = 0x00030050,
RPI_FIRMWARE_NOTIFY_XHCI_RESET = 0x00030058,
/* Dispmanx TAGS */ /* Dispmanx TAGS */
RPI_FIRMWARE_FRAMEBUFFER_ALLOCATE = 0x00040001, RPI_FIRMWARE_FRAMEBUFFER_ALLOCATE = 0x00040001,
@ -141,6 +142,7 @@ int rpi_firmware_property(struct rpi_firmware *fw,
int rpi_firmware_property_list(struct rpi_firmware *fw, int rpi_firmware_property_list(struct rpi_firmware *fw,
void *data, size_t tag_size); void *data, size_t tag_size);
struct rpi_firmware *rpi_firmware_get(struct device_node *firmware_node); struct rpi_firmware *rpi_firmware_get(struct device_node *firmware_node);
int rpi_firmware_init_vl805(struct pci_dev *pdev);
#else #else
static inline int rpi_firmware_property(struct rpi_firmware *fw, u32 tag, static inline int rpi_firmware_property(struct rpi_firmware *fw, u32 tag,
void *data, size_t len) void *data, size_t len)
@ -158,6 +160,11 @@ static inline struct rpi_firmware *rpi_firmware_get(struct device_node *firmware
{ {
return NULL; return NULL;
} }
static inline int rpi_firmware_init_vl805(struct pci_dev *pdev)
{
return 0;
}
#endif #endif
#endif /* __SOC_RASPBERRY_FIRMWARE_H__ */ #endif /* __SOC_RASPBERRY_FIRMWARE_H__ */