mirror of https://gitee.com/openkylin/linux.git
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:
commit
a1dcc1aa6f
|
@ -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
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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", },
|
||||||
{},
|
{},
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 &&
|
||||||
|
|
|
@ -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__ */
|
||||||
|
|
Loading…
Reference in New Issue