Merge branch 'lorenzo/pci/tegra'
- Fix Tegra OF node reference leak (Nishka Dasgupta) - Add #defines for PCIe Data Link Feature and Physical Layer 16.0 GT/s features (Vidya Sagar) - Disable MSI for Tegra Root Ports since they don't support using MSI for all Root Port events (Vidya Sagar) - Group DesignWare write-protected register writes together (Vidya Sagar) - Move DesignWare capability search interfaces so they can be used by both host and endpoint drivers (Vidya Sagar) - Add DesignWare extended capability search interfaces (Vidya Sagar) - Export dw_pcie_wait_for_link() so drivers can be modules (Vidya Sagar) - Add "snps,enable-cdm-check" DT binding for Configuration Dependent Module (CDM) register checking (Vidya Sagar) - Add DesignWare support for "snps,enable-cdm-check" CDM checking (Vidya Sagar) - Add "supports-clkreq" DT binding for host drivers to decide whether to advertise low power features (Vidya Sagar) - Add DT binding for Tegra194 (Vidya Sagar) - Add DT binding for Tegra194 P2U (PIPE to UPHY) block (Vidya Sagar) - Add support for Tegra194 P2U (PIPE to UPHY) (Vidya Sagar) - Add support for Tegra194 host controller (Vidya Sagar) - Add Tegra support for sideband PERST# and CLKREQ# for C5 (Vidya Sagar) - Add Tegra support for slot regulators for p2972-0000 platform (Vidya Sagar) * lorenzo/pci/tegra: arm64: tegra: Add PCIe slot supply information in p2972-0000 platform arm64: tegra: Add configuration for PCIe C5 sideband signals PCI: tegra: Add support to enable slot regulators PCI: tegra: Add support to configure sideband pins dt-bindings: PCI: tegra: Add PCIe slot supplies regulator entries dt-bindings: PCI: tegra: Add sideband pins configuration entries PCI: tegra: Add Tegra194 PCIe support phy: tegra: Add PCIe PIPE2UPHY support dt-bindings: PHY: P2U: Add Tegra194 P2U block dt-bindings: PCI: tegra: Add device tree support for Tegra194 dt-bindings: Add PCIe supports-clkreq property PCI: dwc: Add support to enable CDM register check dt-bindings: PCI: designware: Add binding for CDM register check PCI: dwc: Export dw_pcie_wait_for_link() API PCI: dwc: Add extended configuration space capability search API PCI: dwc: Move config space capability search API PCI: dwc: Group DBI registers writes requiring unlocking PCI: Disable MSI for Tegra root ports PCI: Add #defines for some of PCIe spec r4.0 features PCI: tegra: Fix OF node reference leak
This commit is contained in:
@@ -236,6 +236,16 @@ config PCI_MESON
|
||||
and therefore the driver re-uses the DesignWare core functions to
|
||||
implement the driver.
|
||||
|
||||
config PCIE_TEGRA194
|
||||
tristate "NVIDIA Tegra194 (and later) PCIe controller"
|
||||
depends on ARCH_TEGRA_194_SOC || COMPILE_TEST
|
||||
depends on PCI_MSI_IRQ_DOMAIN
|
||||
select PCIE_DW_HOST
|
||||
select PHY_TEGRA194_P2U
|
||||
help
|
||||
Say Y here if you want support for DesignWare core based PCIe host
|
||||
controller found in NVIDIA Tegra194 SoC.
|
||||
|
||||
config PCIE_UNIPHIER
|
||||
bool "Socionext UniPhier PCIe controllers"
|
||||
depends on ARCH_UNIPHIER || COMPILE_TEST
|
||||
|
@@ -16,6 +16,7 @@ obj-$(CONFIG_PCIE_ARTPEC6) += pcie-artpec6.o
|
||||
obj-$(CONFIG_PCIE_KIRIN) += pcie-kirin.o
|
||||
obj-$(CONFIG_PCIE_HISI_STB) += pcie-histb.o
|
||||
obj-$(CONFIG_PCI_MESON) += pci-meson.o
|
||||
obj-$(CONFIG_PCIE_TEGRA194) += pcie-tegra194.o
|
||||
obj-$(CONFIG_PCIE_UNIPHIER) += pcie-uniphier.o
|
||||
|
||||
# The following drivers are for devices that use the generic ACPI
|
||||
|
@@ -40,39 +40,6 @@ void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar)
|
||||
__dw_pcie_ep_reset_bar(pci, bar, 0);
|
||||
}
|
||||
|
||||
static u8 __dw_pcie_ep_find_next_cap(struct dw_pcie *pci, u8 cap_ptr,
|
||||
u8 cap)
|
||||
{
|
||||
u8 cap_id, next_cap_ptr;
|
||||
u16 reg;
|
||||
|
||||
if (!cap_ptr)
|
||||
return 0;
|
||||
|
||||
reg = dw_pcie_readw_dbi(pci, cap_ptr);
|
||||
cap_id = (reg & 0x00ff);
|
||||
|
||||
if (cap_id > PCI_CAP_ID_MAX)
|
||||
return 0;
|
||||
|
||||
if (cap_id == cap)
|
||||
return cap_ptr;
|
||||
|
||||
next_cap_ptr = (reg & 0xff00) >> 8;
|
||||
return __dw_pcie_ep_find_next_cap(pci, next_cap_ptr, cap);
|
||||
}
|
||||
|
||||
static u8 dw_pcie_ep_find_capability(struct dw_pcie *pci, u8 cap)
|
||||
{
|
||||
u8 next_cap_ptr;
|
||||
u16 reg;
|
||||
|
||||
reg = dw_pcie_readw_dbi(pci, PCI_CAPABILITY_LIST);
|
||||
next_cap_ptr = (reg & 0x00ff);
|
||||
|
||||
return __dw_pcie_ep_find_next_cap(pci, next_cap_ptr, cap);
|
||||
}
|
||||
|
||||
static int dw_pcie_ep_write_header(struct pci_epc *epc, u8 func_no,
|
||||
struct pci_epf_header *hdr)
|
||||
{
|
||||
@@ -620,9 +587,9 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep)
|
||||
dev_err(dev, "Failed to reserve memory for MSI/MSI-X\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
ep->msi_cap = dw_pcie_ep_find_capability(pci, PCI_CAP_ID_MSI);
|
||||
ep->msi_cap = dw_pcie_find_capability(pci, PCI_CAP_ID_MSI);
|
||||
|
||||
ep->msix_cap = dw_pcie_ep_find_capability(pci, PCI_CAP_ID_MSIX);
|
||||
ep->msix_cap = dw_pcie_find_capability(pci, PCI_CAP_ID_MSIX);
|
||||
|
||||
offset = dw_pcie_ep_find_ext_capability(pci, PCI_EXT_CAP_ID_REBAR);
|
||||
if (offset) {
|
||||
|
@@ -644,6 +644,12 @@ void dw_pcie_setup_rc(struct pcie_port *pp)
|
||||
u32 val, ctrl, num_ctrls;
|
||||
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
|
||||
|
||||
/*
|
||||
* Enable DBI read-only registers for writing/updating configuration.
|
||||
* Write permission gets disabled towards the end of this function.
|
||||
*/
|
||||
dw_pcie_dbi_ro_wr_en(pci);
|
||||
|
||||
dw_pcie_setup(pci);
|
||||
|
||||
if (!pp->ops->msi_host_init) {
|
||||
@@ -666,12 +672,10 @@ void dw_pcie_setup_rc(struct pcie_port *pp)
|
||||
dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_1, 0x00000000);
|
||||
|
||||
/* Setup interrupt pins */
|
||||
dw_pcie_dbi_ro_wr_en(pci);
|
||||
val = dw_pcie_readl_dbi(pci, PCI_INTERRUPT_LINE);
|
||||
val &= 0xffff00ff;
|
||||
val |= 0x00000100;
|
||||
dw_pcie_writel_dbi(pci, PCI_INTERRUPT_LINE, val);
|
||||
dw_pcie_dbi_ro_wr_dis(pci);
|
||||
|
||||
/* Setup bus numbers */
|
||||
val = dw_pcie_readl_dbi(pci, PCI_PRIMARY_BUS);
|
||||
@@ -703,15 +707,13 @@ void dw_pcie_setup_rc(struct pcie_port *pp)
|
||||
|
||||
dw_pcie_wr_own_conf(pp, PCI_BASE_ADDRESS_0, 4, 0);
|
||||
|
||||
/* Enable write permission for the DBI read-only register */
|
||||
dw_pcie_dbi_ro_wr_en(pci);
|
||||
/* Program correct class for RC */
|
||||
dw_pcie_wr_own_conf(pp, PCI_CLASS_DEVICE, 2, PCI_CLASS_BRIDGE_PCI);
|
||||
/* Better disable write permission right after the update */
|
||||
dw_pcie_dbi_ro_wr_dis(pci);
|
||||
|
||||
dw_pcie_rd_own_conf(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, 4, &val);
|
||||
val |= PORT_LOGIC_SPEED_CHANGE;
|
||||
dw_pcie_wr_own_conf(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, 4, val);
|
||||
|
||||
dw_pcie_dbi_ro_wr_dis(pci);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dw_pcie_setup_rc);
|
||||
|
@@ -14,6 +14,86 @@
|
||||
|
||||
#include "pcie-designware.h"
|
||||
|
||||
/*
|
||||
* These interfaces resemble the pci_find_*capability() interfaces, but these
|
||||
* are for configuring host controllers, which are bridges *to* PCI devices but
|
||||
* are not PCI devices themselves.
|
||||
*/
|
||||
static u8 __dw_pcie_find_next_cap(struct dw_pcie *pci, u8 cap_ptr,
|
||||
u8 cap)
|
||||
{
|
||||
u8 cap_id, next_cap_ptr;
|
||||
u16 reg;
|
||||
|
||||
if (!cap_ptr)
|
||||
return 0;
|
||||
|
||||
reg = dw_pcie_readw_dbi(pci, cap_ptr);
|
||||
cap_id = (reg & 0x00ff);
|
||||
|
||||
if (cap_id > PCI_CAP_ID_MAX)
|
||||
return 0;
|
||||
|
||||
if (cap_id == cap)
|
||||
return cap_ptr;
|
||||
|
||||
next_cap_ptr = (reg & 0xff00) >> 8;
|
||||
return __dw_pcie_find_next_cap(pci, next_cap_ptr, cap);
|
||||
}
|
||||
|
||||
u8 dw_pcie_find_capability(struct dw_pcie *pci, u8 cap)
|
||||
{
|
||||
u8 next_cap_ptr;
|
||||
u16 reg;
|
||||
|
||||
reg = dw_pcie_readw_dbi(pci, PCI_CAPABILITY_LIST);
|
||||
next_cap_ptr = (reg & 0x00ff);
|
||||
|
||||
return __dw_pcie_find_next_cap(pci, next_cap_ptr, cap);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dw_pcie_find_capability);
|
||||
|
||||
static u16 dw_pcie_find_next_ext_capability(struct dw_pcie *pci, u16 start,
|
||||
u8 cap)
|
||||
{
|
||||
u32 header;
|
||||
int ttl;
|
||||
int pos = PCI_CFG_SPACE_SIZE;
|
||||
|
||||
/* minimum 8 bytes per capability */
|
||||
ttl = (PCI_CFG_SPACE_EXP_SIZE - PCI_CFG_SPACE_SIZE) / 8;
|
||||
|
||||
if (start)
|
||||
pos = start;
|
||||
|
||||
header = dw_pcie_readl_dbi(pci, pos);
|
||||
/*
|
||||
* If we have no capabilities, this is indicated by cap ID,
|
||||
* cap version and next pointer all being 0.
|
||||
*/
|
||||
if (header == 0)
|
||||
return 0;
|
||||
|
||||
while (ttl-- > 0) {
|
||||
if (PCI_EXT_CAP_ID(header) == cap && pos != start)
|
||||
return pos;
|
||||
|
||||
pos = PCI_EXT_CAP_NEXT(header);
|
||||
if (pos < PCI_CFG_SPACE_SIZE)
|
||||
break;
|
||||
|
||||
header = dw_pcie_readl_dbi(pci, pos);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u16 dw_pcie_find_ext_capability(struct dw_pcie *pci, u8 cap)
|
||||
{
|
||||
return dw_pcie_find_next_ext_capability(pci, 0, cap);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dw_pcie_find_ext_capability);
|
||||
|
||||
int dw_pcie_read(void __iomem *addr, int size, u32 *val)
|
||||
{
|
||||
if (!IS_ALIGNED((uintptr_t)addr, size)) {
|
||||
@@ -376,10 +456,11 @@ int dw_pcie_wait_for_link(struct dw_pcie *pci)
|
||||
usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX);
|
||||
}
|
||||
|
||||
dev_err(pci->dev, "Phy link never came up\n");
|
||||
dev_info(pci->dev, "Phy link never came up\n");
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dw_pcie_wait_for_link);
|
||||
|
||||
int dw_pcie_link_up(struct dw_pcie *pci)
|
||||
{
|
||||
@@ -468,4 +549,11 @@ void dw_pcie_setup(struct dw_pcie *pci)
|
||||
break;
|
||||
}
|
||||
dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val);
|
||||
|
||||
if (of_property_read_bool(np, "snps,enable-cdm-check")) {
|
||||
val = dw_pcie_readl_dbi(pci, PCIE_PL_CHK_REG_CONTROL_STATUS);
|
||||
val |= PCIE_PL_CHK_REG_CHK_REG_CONTINUOUS |
|
||||
PCIE_PL_CHK_REG_CHK_REG_START;
|
||||
dw_pcie_writel_dbi(pci, PCIE_PL_CHK_REG_CONTROL_STATUS, val);
|
||||
}
|
||||
}
|
||||
|
@@ -86,6 +86,15 @@
|
||||
#define PCIE_MISC_CONTROL_1_OFF 0x8BC
|
||||
#define PCIE_DBI_RO_WR_EN BIT(0)
|
||||
|
||||
#define PCIE_PL_CHK_REG_CONTROL_STATUS 0xB20
|
||||
#define PCIE_PL_CHK_REG_CHK_REG_START BIT(0)
|
||||
#define PCIE_PL_CHK_REG_CHK_REG_CONTINUOUS BIT(1)
|
||||
#define PCIE_PL_CHK_REG_CHK_REG_COMPARISON_ERROR BIT(16)
|
||||
#define PCIE_PL_CHK_REG_CHK_REG_LOGIC_ERROR BIT(17)
|
||||
#define PCIE_PL_CHK_REG_CHK_REG_COMPLETE BIT(18)
|
||||
|
||||
#define PCIE_PL_CHK_REG_ERR_ADDR 0xB28
|
||||
|
||||
/*
|
||||
* iATU Unroll-specific register definitions
|
||||
* From 4.80 core version the address translation will be made by unroll
|
||||
@@ -251,6 +260,9 @@ struct dw_pcie {
|
||||
#define to_dw_pcie_from_ep(endpoint) \
|
||||
container_of((endpoint), struct dw_pcie, ep)
|
||||
|
||||
u8 dw_pcie_find_capability(struct dw_pcie *pci, u8 cap);
|
||||
u16 dw_pcie_find_ext_capability(struct dw_pcie *pci, u8 cap);
|
||||
|
||||
int dw_pcie_read(void __iomem *addr, int size, u32 *val);
|
||||
int dw_pcie_write(void __iomem *addr, int size, u32 val);
|
||||
|
||||
|
1732
drivers/pci/controller/dwc/pcie-tegra194.c
Normal file
1732
drivers/pci/controller/dwc/pcie-tegra194.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -2237,14 +2237,15 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
|
||||
err = of_pci_get_devfn(port);
|
||||
if (err < 0) {
|
||||
dev_err(dev, "failed to parse address: %d\n", err);
|
||||
return err;
|
||||
goto err_node_put;
|
||||
}
|
||||
|
||||
index = PCI_SLOT(err);
|
||||
|
||||
if (index < 1 || index > soc->num_ports) {
|
||||
dev_err(dev, "invalid port number: %d\n", index);
|
||||
return -EINVAL;
|
||||
err = -EINVAL;
|
||||
goto err_node_put;
|
||||
}
|
||||
|
||||
index--;
|
||||
@@ -2253,12 +2254,13 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
|
||||
if (err < 0) {
|
||||
dev_err(dev, "failed to parse # of lanes: %d\n",
|
||||
err);
|
||||
return err;
|
||||
goto err_node_put;
|
||||
}
|
||||
|
||||
if (value > 16) {
|
||||
dev_err(dev, "invalid # of lanes: %u\n", value);
|
||||
return -EINVAL;
|
||||
err = -EINVAL;
|
||||
goto err_node_put;
|
||||
}
|
||||
|
||||
lanes |= value << (index << 3);
|
||||
@@ -2272,13 +2274,15 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
|
||||
lane += value;
|
||||
|
||||
rp = devm_kzalloc(dev, sizeof(*rp), GFP_KERNEL);
|
||||
if (!rp)
|
||||
return -ENOMEM;
|
||||
if (!rp) {
|
||||
err = -ENOMEM;
|
||||
goto err_node_put;
|
||||
}
|
||||
|
||||
err = of_address_to_resource(port, 0, &rp->regs);
|
||||
if (err < 0) {
|
||||
dev_err(dev, "failed to parse address: %d\n", err);
|
||||
return err;
|
||||
goto err_node_put;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&rp->list);
|
||||
@@ -2330,6 +2334,10 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
|
||||
err_node_put:
|
||||
of_node_put(port);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@@ -2591,6 +2591,59 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NVIDIA,
|
||||
PCI_DEVICE_ID_NVIDIA_NVENET_15,
|
||||
nvenet_msi_disable);
|
||||
|
||||
/*
|
||||
* PCIe spec r4.0 sec 7.7.1.2 and sec 7.7.2.2 say that if MSI/MSI-X is enabled,
|
||||
* then the device can't use INTx interrupts. Tegra's PCIe root ports don't
|
||||
* generate MSI interrupts for PME and AER events instead only INTx interrupts
|
||||
* are generated. Though Tegra's PCIe root ports can generate MSI interrupts
|
||||
* for other events, since PCIe specificiation doesn't support using a mix of
|
||||
* INTx and MSI/MSI-X, it is required to disable MSI interrupts to avoid port
|
||||
* service drivers registering their respective ISRs for MSIs.
|
||||
*/
|
||||
static void pci_quirk_nvidia_tegra_disable_rp_msi(struct pci_dev *dev)
|
||||
{
|
||||
dev->no_msi = 1;
|
||||
}
|
||||
DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_NVIDIA, 0x1ad0,
|
||||
PCI_CLASS_BRIDGE_PCI, 8,
|
||||
pci_quirk_nvidia_tegra_disable_rp_msi);
|
||||
DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_NVIDIA, 0x1ad1,
|
||||
PCI_CLASS_BRIDGE_PCI, 8,
|
||||
pci_quirk_nvidia_tegra_disable_rp_msi);
|
||||
DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_NVIDIA, 0x1ad2,
|
||||
PCI_CLASS_BRIDGE_PCI, 8,
|
||||
pci_quirk_nvidia_tegra_disable_rp_msi);
|
||||
DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_NVIDIA, 0x0bf0,
|
||||
PCI_CLASS_BRIDGE_PCI, 8,
|
||||
pci_quirk_nvidia_tegra_disable_rp_msi);
|
||||
DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_NVIDIA, 0x0bf1,
|
||||
PCI_CLASS_BRIDGE_PCI, 8,
|
||||
pci_quirk_nvidia_tegra_disable_rp_msi);
|
||||
DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_NVIDIA, 0x0e1c,
|
||||
PCI_CLASS_BRIDGE_PCI, 8,
|
||||
pci_quirk_nvidia_tegra_disable_rp_msi);
|
||||
DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_NVIDIA, 0x0e1d,
|
||||
PCI_CLASS_BRIDGE_PCI, 8,
|
||||
pci_quirk_nvidia_tegra_disable_rp_msi);
|
||||
DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_NVIDIA, 0x0e12,
|
||||
PCI_CLASS_BRIDGE_PCI, 8,
|
||||
pci_quirk_nvidia_tegra_disable_rp_msi);
|
||||
DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_NVIDIA, 0x0e13,
|
||||
PCI_CLASS_BRIDGE_PCI, 8,
|
||||
pci_quirk_nvidia_tegra_disable_rp_msi);
|
||||
DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_NVIDIA, 0x0fae,
|
||||
PCI_CLASS_BRIDGE_PCI, 8,
|
||||
pci_quirk_nvidia_tegra_disable_rp_msi);
|
||||
DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_NVIDIA, 0x0faf,
|
||||
PCI_CLASS_BRIDGE_PCI, 8,
|
||||
pci_quirk_nvidia_tegra_disable_rp_msi);
|
||||
DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_NVIDIA, 0x10e5,
|
||||
PCI_CLASS_BRIDGE_PCI, 8,
|
||||
pci_quirk_nvidia_tegra_disable_rp_msi);
|
||||
DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_NVIDIA, 0x10e6,
|
||||
PCI_CLASS_BRIDGE_PCI, 8,
|
||||
pci_quirk_nvidia_tegra_disable_rp_msi);
|
||||
|
||||
/*
|
||||
* Some versions of the MCP55 bridge from Nvidia have a legacy IRQ routing
|
||||
* config register. This register controls the routing of legacy
|
||||
|
Reference in New Issue
Block a user