mirror of
https://github.com/torvalds/linux.git
synced 2026-06-02 11:33:28 +02:00
Merge branch 'pci/controller/qcom'
- Select PCI Power Control Slot driver so slot voltage rails can be turned on/off if described in Root Port device tree node (Qiang Yu) - Parse only PCI bridge child nodes in device tree, skipping unrelated nodes such as OPP (Operating Performance Points), which caused probe failures (Krishna Chaitanya Chundru) - Add 8.0 GT/s and 32.0 GT/s equalization settings (Ziyue Zhang) - Fix typo in CURSOR macro names (Ziyue Zhang) - Consolidate Root Port 'phy' and 'reset' properties in struct qcom_pcie_port, regardless of whether we got them from the Root Port node or the host bridge node (Manivannan Sadhasivam) - Fetch and map the ELBI register space in the DWC core rather than in each driver individually (Krishna Chaitanya Chundru) - Enable ECAM mechanism in DWC core by setting up iATU with 'CFG Shift Feature' and use this in the qcom driver (Krishna Chaitanya Chundru) * pci/controller/qcom: PCI: dwc: Support ECAM mechanism by enabling iATU 'CFG Shift Feature' PCI: qcom: Prepare for the DWC ECAM enablement PCI: dwc: Prepare the driver for enabling ECAM mechanism using iATU 'CFG Shift Feature' PCI: dwc: Add support for ELBI resource mapping PCI: qcom: Move host bridge 'phy' and 'reset' pointers to struct qcom_pcie_port PCI: qcom: Fix macro typo for CURSOR PCI: qcom: Add equalization settings for 8.0 GT/s and 32.0 GT/s PCI: qcom: Restrict port parsing only to PCIe bridge child nodes PCI: qcom: Select PCI Power Control Slot driver
This commit is contained in:
commit
531abff0fa
|
|
@ -20,6 +20,7 @@ config PCIE_DW_HOST
|
|||
bool
|
||||
select PCIE_DW
|
||||
select IRQ_MSI_LIB
|
||||
select PCI_HOST_COMMON
|
||||
|
||||
config PCIE_DW_EP
|
||||
bool
|
||||
|
|
@ -298,6 +299,7 @@ config PCIE_QCOM
|
|||
select CRC8
|
||||
select PCIE_QCOM_COMMON
|
||||
select PCI_HOST_COMMON
|
||||
select PCI_PWRCTRL_SLOT
|
||||
help
|
||||
Say Y here to enable PCIe controller support on Qualcomm SoCs. The
|
||||
PCIe controller uses the DesignWare core plus Qualcomm-specific
|
||||
|
|
|
|||
|
|
@ -53,7 +53,6 @@
|
|||
|
||||
struct exynos_pcie {
|
||||
struct dw_pcie pci;
|
||||
void __iomem *elbi_base;
|
||||
struct clk_bulk_data *clks;
|
||||
struct phy *phy;
|
||||
struct regulator_bulk_data supplies[2];
|
||||
|
|
@ -71,73 +70,78 @@ static u32 exynos_pcie_readl(void __iomem *base, u32 reg)
|
|||
|
||||
static void exynos_pcie_sideband_dbi_w_mode(struct exynos_pcie *ep, bool on)
|
||||
{
|
||||
struct dw_pcie *pci = &ep->pci;
|
||||
u32 val;
|
||||
|
||||
val = exynos_pcie_readl(ep->elbi_base, PCIE_ELBI_SLV_AWMISC);
|
||||
val = exynos_pcie_readl(pci->elbi_base, PCIE_ELBI_SLV_AWMISC);
|
||||
if (on)
|
||||
val |= PCIE_ELBI_SLV_DBI_ENABLE;
|
||||
else
|
||||
val &= ~PCIE_ELBI_SLV_DBI_ENABLE;
|
||||
exynos_pcie_writel(ep->elbi_base, val, PCIE_ELBI_SLV_AWMISC);
|
||||
exynos_pcie_writel(pci->elbi_base, val, PCIE_ELBI_SLV_AWMISC);
|
||||
}
|
||||
|
||||
static void exynos_pcie_sideband_dbi_r_mode(struct exynos_pcie *ep, bool on)
|
||||
{
|
||||
struct dw_pcie *pci = &ep->pci;
|
||||
u32 val;
|
||||
|
||||
val = exynos_pcie_readl(ep->elbi_base, PCIE_ELBI_SLV_ARMISC);
|
||||
val = exynos_pcie_readl(pci->elbi_base, PCIE_ELBI_SLV_ARMISC);
|
||||
if (on)
|
||||
val |= PCIE_ELBI_SLV_DBI_ENABLE;
|
||||
else
|
||||
val &= ~PCIE_ELBI_SLV_DBI_ENABLE;
|
||||
exynos_pcie_writel(ep->elbi_base, val, PCIE_ELBI_SLV_ARMISC);
|
||||
exynos_pcie_writel(pci->elbi_base, val, PCIE_ELBI_SLV_ARMISC);
|
||||
}
|
||||
|
||||
static void exynos_pcie_assert_core_reset(struct exynos_pcie *ep)
|
||||
{
|
||||
struct dw_pcie *pci = &ep->pci;
|
||||
u32 val;
|
||||
|
||||
val = exynos_pcie_readl(ep->elbi_base, PCIE_CORE_RESET);
|
||||
val = exynos_pcie_readl(pci->elbi_base, PCIE_CORE_RESET);
|
||||
val &= ~PCIE_CORE_RESET_ENABLE;
|
||||
exynos_pcie_writel(ep->elbi_base, val, PCIE_CORE_RESET);
|
||||
exynos_pcie_writel(ep->elbi_base, 0, PCIE_STICKY_RESET);
|
||||
exynos_pcie_writel(ep->elbi_base, 0, PCIE_NONSTICKY_RESET);
|
||||
exynos_pcie_writel(pci->elbi_base, val, PCIE_CORE_RESET);
|
||||
exynos_pcie_writel(pci->elbi_base, 0, PCIE_STICKY_RESET);
|
||||
exynos_pcie_writel(pci->elbi_base, 0, PCIE_NONSTICKY_RESET);
|
||||
}
|
||||
|
||||
static void exynos_pcie_deassert_core_reset(struct exynos_pcie *ep)
|
||||
{
|
||||
struct dw_pcie *pci = &ep->pci;
|
||||
u32 val;
|
||||
|
||||
val = exynos_pcie_readl(ep->elbi_base, PCIE_CORE_RESET);
|
||||
val = exynos_pcie_readl(pci->elbi_base, PCIE_CORE_RESET);
|
||||
val |= PCIE_CORE_RESET_ENABLE;
|
||||
|
||||
exynos_pcie_writel(ep->elbi_base, val, PCIE_CORE_RESET);
|
||||
exynos_pcie_writel(ep->elbi_base, 1, PCIE_STICKY_RESET);
|
||||
exynos_pcie_writel(ep->elbi_base, 1, PCIE_NONSTICKY_RESET);
|
||||
exynos_pcie_writel(ep->elbi_base, 1, PCIE_APP_INIT_RESET);
|
||||
exynos_pcie_writel(ep->elbi_base, 0, PCIE_APP_INIT_RESET);
|
||||
exynos_pcie_writel(pci->elbi_base, val, PCIE_CORE_RESET);
|
||||
exynos_pcie_writel(pci->elbi_base, 1, PCIE_STICKY_RESET);
|
||||
exynos_pcie_writel(pci->elbi_base, 1, PCIE_NONSTICKY_RESET);
|
||||
exynos_pcie_writel(pci->elbi_base, 1, PCIE_APP_INIT_RESET);
|
||||
exynos_pcie_writel(pci->elbi_base, 0, PCIE_APP_INIT_RESET);
|
||||
}
|
||||
|
||||
static int exynos_pcie_start_link(struct dw_pcie *pci)
|
||||
{
|
||||
struct exynos_pcie *ep = to_exynos_pcie(pci);
|
||||
u32 val;
|
||||
|
||||
val = exynos_pcie_readl(ep->elbi_base, PCIE_SW_WAKE);
|
||||
val = exynos_pcie_readl(pci->elbi_base, PCIE_SW_WAKE);
|
||||
val &= ~PCIE_BUS_EN;
|
||||
exynos_pcie_writel(ep->elbi_base, val, PCIE_SW_WAKE);
|
||||
exynos_pcie_writel(pci->elbi_base, val, PCIE_SW_WAKE);
|
||||
|
||||
/* assert LTSSM enable */
|
||||
exynos_pcie_writel(ep->elbi_base, PCIE_ELBI_LTSSM_ENABLE,
|
||||
exynos_pcie_writel(pci->elbi_base, PCIE_ELBI_LTSSM_ENABLE,
|
||||
PCIE_APP_LTSSM_ENABLE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void exynos_pcie_clear_irq_pulse(struct exynos_pcie *ep)
|
||||
{
|
||||
u32 val = exynos_pcie_readl(ep->elbi_base, PCIE_IRQ_PULSE);
|
||||
struct dw_pcie *pci = &ep->pci;
|
||||
|
||||
exynos_pcie_writel(ep->elbi_base, val, PCIE_IRQ_PULSE);
|
||||
u32 val = exynos_pcie_readl(pci->elbi_base, PCIE_IRQ_PULSE);
|
||||
|
||||
exynos_pcie_writel(pci->elbi_base, val, PCIE_IRQ_PULSE);
|
||||
}
|
||||
|
||||
static irqreturn_t exynos_pcie_irq_handler(int irq, void *arg)
|
||||
|
|
@ -150,12 +154,14 @@ static irqreturn_t exynos_pcie_irq_handler(int irq, void *arg)
|
|||
|
||||
static void exynos_pcie_enable_irq_pulse(struct exynos_pcie *ep)
|
||||
{
|
||||
struct dw_pcie *pci = &ep->pci;
|
||||
|
||||
u32 val = IRQ_INTA_ASSERT | IRQ_INTB_ASSERT |
|
||||
IRQ_INTC_ASSERT | IRQ_INTD_ASSERT;
|
||||
|
||||
exynos_pcie_writel(ep->elbi_base, val, PCIE_IRQ_EN_PULSE);
|
||||
exynos_pcie_writel(ep->elbi_base, 0, PCIE_IRQ_EN_LEVEL);
|
||||
exynos_pcie_writel(ep->elbi_base, 0, PCIE_IRQ_EN_SPECIAL);
|
||||
exynos_pcie_writel(pci->elbi_base, val, PCIE_IRQ_EN_PULSE);
|
||||
exynos_pcie_writel(pci->elbi_base, 0, PCIE_IRQ_EN_LEVEL);
|
||||
exynos_pcie_writel(pci->elbi_base, 0, PCIE_IRQ_EN_SPECIAL);
|
||||
}
|
||||
|
||||
static u32 exynos_pcie_read_dbi(struct dw_pcie *pci, void __iomem *base,
|
||||
|
|
@ -211,8 +217,7 @@ static struct pci_ops exynos_pci_ops = {
|
|||
|
||||
static bool exynos_pcie_link_up(struct dw_pcie *pci)
|
||||
{
|
||||
struct exynos_pcie *ep = to_exynos_pcie(pci);
|
||||
u32 val = exynos_pcie_readl(ep->elbi_base, PCIE_ELBI_RDLH_LINKUP);
|
||||
u32 val = exynos_pcie_readl(pci->elbi_base, PCIE_ELBI_RDLH_LINKUP);
|
||||
|
||||
return val & PCIE_ELBI_XMLH_LINKUP;
|
||||
}
|
||||
|
|
@ -295,11 +300,6 @@ static int exynos_pcie_probe(struct platform_device *pdev)
|
|||
if (IS_ERR(ep->phy))
|
||||
return PTR_ERR(ep->phy);
|
||||
|
||||
/* External Local Bus interface (ELBI) registers */
|
||||
ep->elbi_base = devm_platform_ioremap_resource_byname(pdev, "elbi");
|
||||
if (IS_ERR(ep->elbi_base))
|
||||
return PTR_ERR(ep->elbi_base);
|
||||
|
||||
ret = devm_clk_bulk_get_all_enabled(dev, &ep->clks);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
|
|
|||
|
|
@ -352,6 +352,7 @@ static int al_pcie_probe(struct platform_device *pdev)
|
|||
return -ENOENT;
|
||||
}
|
||||
al_pcie->ecam_size = resource_size(ecam_res);
|
||||
pci->pp.native_ecam = true;
|
||||
|
||||
controller_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
||||
"controller");
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
* Author: Jingoo Han <jg1.han@samsung.com>
|
||||
*/
|
||||
|
||||
#include <linux/align.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/irqchip/chained_irq.h>
|
||||
#include <linux/irqchip/irq-msi-lib.h>
|
||||
|
|
@ -32,6 +33,8 @@ static struct pci_ops dw_child_pcie_ops;
|
|||
MSI_FLAG_PCI_MSIX | \
|
||||
MSI_GENERIC_FLAGS_MASK)
|
||||
|
||||
#define IS_256MB_ALIGNED(x) IS_ALIGNED(x, SZ_256M)
|
||||
|
||||
static const struct msi_parent_ops dw_pcie_msi_parent_ops = {
|
||||
.required_flags = DW_PCIE_MSI_FLAGS_REQUIRED,
|
||||
.supported_flags = DW_PCIE_MSI_FLAGS_SUPPORTED,
|
||||
|
|
@ -413,6 +416,95 @@ static void dw_pcie_host_request_msg_tlp_res(struct dw_pcie_rp *pp)
|
|||
}
|
||||
}
|
||||
|
||||
static int dw_pcie_config_ecam_iatu(struct dw_pcie_rp *pp)
|
||||
{
|
||||
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
|
||||
struct dw_pcie_ob_atu_cfg atu = {0};
|
||||
resource_size_t bus_range_max;
|
||||
struct resource_entry *bus;
|
||||
int ret;
|
||||
|
||||
bus = resource_list_first_type(&pp->bridge->windows, IORESOURCE_BUS);
|
||||
|
||||
/*
|
||||
* Root bus under the host bridge doesn't require any iATU configuration
|
||||
* as DBI region will be used to access root bus config space.
|
||||
* Immediate bus under Root Bus, needs type 0 iATU configuration and
|
||||
* remaining buses need type 1 iATU configuration.
|
||||
*/
|
||||
atu.index = 0;
|
||||
atu.type = PCIE_ATU_TYPE_CFG0;
|
||||
atu.parent_bus_addr = pp->cfg0_base + SZ_1M;
|
||||
/* 1MiB is to cover 1 (bus) * 32 (devices) * 8 (functions) */
|
||||
atu.size = SZ_1M;
|
||||
atu.ctrl2 = PCIE_ATU_CFG_SHIFT_MODE_ENABLE;
|
||||
ret = dw_pcie_prog_outbound_atu(pci, &atu);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
bus_range_max = resource_size(bus->res);
|
||||
|
||||
if (bus_range_max < 2)
|
||||
return 0;
|
||||
|
||||
/* Configure remaining buses in type 1 iATU configuration */
|
||||
atu.index = 1;
|
||||
atu.type = PCIE_ATU_TYPE_CFG1;
|
||||
atu.parent_bus_addr = pp->cfg0_base + SZ_2M;
|
||||
atu.size = (SZ_1M * bus_range_max) - SZ_2M;
|
||||
atu.ctrl2 = PCIE_ATU_CFG_SHIFT_MODE_ENABLE;
|
||||
|
||||
return dw_pcie_prog_outbound_atu(pci, &atu);
|
||||
}
|
||||
|
||||
static int dw_pcie_create_ecam_window(struct dw_pcie_rp *pp, struct resource *res)
|
||||
{
|
||||
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
|
||||
struct device *dev = pci->dev;
|
||||
struct resource_entry *bus;
|
||||
|
||||
bus = resource_list_first_type(&pp->bridge->windows, IORESOURCE_BUS);
|
||||
if (!bus)
|
||||
return -ENODEV;
|
||||
|
||||
pp->cfg = pci_ecam_create(dev, res, bus->res, &pci_generic_ecam_ops);
|
||||
if (IS_ERR(pp->cfg))
|
||||
return PTR_ERR(pp->cfg);
|
||||
|
||||
pci->dbi_base = pp->cfg->win;
|
||||
pci->dbi_phys_addr = res->start;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool dw_pcie_ecam_enabled(struct dw_pcie_rp *pp, struct resource *config_res)
|
||||
{
|
||||
struct resource *bus_range;
|
||||
u64 nr_buses;
|
||||
|
||||
/* Vendor glue drivers may implement their own ECAM mechanism */
|
||||
if (pp->native_ecam)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* PCIe spec r6.0, sec 7.2.2 mandates the base address used for ECAM to
|
||||
* be aligned on a 2^(n+20) byte boundary, where n is the number of bits
|
||||
* used for representing 'bus' in BDF. Since the DWC cores always use 8
|
||||
* bits for representing 'bus', the base address has to be aligned to
|
||||
* 2^28 byte boundary, which is 256 MiB.
|
||||
*/
|
||||
if (!IS_256MB_ALIGNED(config_res->start))
|
||||
return false;
|
||||
|
||||
bus_range = resource_list_first_type(&pp->bridge->windows, IORESOURCE_BUS)->res;
|
||||
if (!bus_range)
|
||||
return false;
|
||||
|
||||
nr_buses = resource_size(config_res) >> PCIE_ECAM_BUS_SHIFT;
|
||||
|
||||
return nr_buses >= resource_size(bus_range);
|
||||
}
|
||||
|
||||
static int dw_pcie_host_get_resources(struct dw_pcie_rp *pp)
|
||||
{
|
||||
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
|
||||
|
|
@ -422,10 +514,6 @@ static int dw_pcie_host_get_resources(struct dw_pcie_rp *pp)
|
|||
struct resource *res;
|
||||
int ret;
|
||||
|
||||
ret = dw_pcie_get_resources(pci);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "config");
|
||||
if (!res) {
|
||||
dev_err(dev, "Missing \"config\" reg space\n");
|
||||
|
|
@ -435,9 +523,32 @@ static int dw_pcie_host_get_resources(struct dw_pcie_rp *pp)
|
|||
pp->cfg0_size = resource_size(res);
|
||||
pp->cfg0_base = res->start;
|
||||
|
||||
pp->va_cfg0_base = devm_pci_remap_cfg_resource(dev, res);
|
||||
if (IS_ERR(pp->va_cfg0_base))
|
||||
return PTR_ERR(pp->va_cfg0_base);
|
||||
pp->ecam_enabled = dw_pcie_ecam_enabled(pp, res);
|
||||
if (pp->ecam_enabled) {
|
||||
ret = dw_pcie_create_ecam_window(pp, res);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pp->bridge->ops = (struct pci_ops *)&pci_generic_ecam_ops.pci_ops;
|
||||
pp->bridge->sysdata = pp->cfg;
|
||||
pp->cfg->priv = pp;
|
||||
} else {
|
||||
pp->va_cfg0_base = devm_pci_remap_cfg_resource(dev, res);
|
||||
if (IS_ERR(pp->va_cfg0_base))
|
||||
return PTR_ERR(pp->va_cfg0_base);
|
||||
|
||||
/* Set default bus ops */
|
||||
pp->bridge->ops = &dw_pcie_ops;
|
||||
pp->bridge->child_ops = &dw_child_pcie_ops;
|
||||
pp->bridge->sysdata = pp;
|
||||
}
|
||||
|
||||
ret = dw_pcie_get_resources(pci);
|
||||
if (ret) {
|
||||
if (pp->cfg)
|
||||
pci_ecam_free(pp->cfg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Get the I/O range from DT */
|
||||
win = resource_list_first_type(&pp->bridge->windows, IORESOURCE_IO);
|
||||
|
|
@ -476,14 +587,10 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Set default bus ops */
|
||||
bridge->ops = &dw_pcie_ops;
|
||||
bridge->child_ops = &dw_child_pcie_ops;
|
||||
|
||||
if (pp->ops->init) {
|
||||
ret = pp->ops->init(pp);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto err_free_ecam;
|
||||
}
|
||||
|
||||
if (pci_msi_enabled()) {
|
||||
|
|
@ -525,6 +632,14 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp)
|
|||
if (ret)
|
||||
goto err_free_msi;
|
||||
|
||||
if (pp->ecam_enabled) {
|
||||
ret = dw_pcie_config_ecam_iatu(pp);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to configure iATU in ECAM mode\n");
|
||||
goto err_free_msi;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate the resource for MSG TLP before programming the iATU
|
||||
* outbound window in dw_pcie_setup_rc(). Since the allocation depends
|
||||
|
|
@ -560,8 +675,6 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp)
|
|||
/* Ignore errors, the link may come up later */
|
||||
dw_pcie_wait_for_link(pci);
|
||||
|
||||
bridge->sysdata = pp;
|
||||
|
||||
ret = pci_host_probe(bridge);
|
||||
if (ret)
|
||||
goto err_stop_link;
|
||||
|
|
@ -587,6 +700,10 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp)
|
|||
if (pp->ops->deinit)
|
||||
pp->ops->deinit(pp);
|
||||
|
||||
err_free_ecam:
|
||||
if (pp->cfg)
|
||||
pci_ecam_free(pp->cfg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dw_pcie_host_init);
|
||||
|
|
@ -609,6 +726,9 @@ void dw_pcie_host_deinit(struct dw_pcie_rp *pp)
|
|||
|
||||
if (pp->ops->deinit)
|
||||
pp->ops->deinit(pp);
|
||||
|
||||
if (pp->cfg)
|
||||
pci_ecam_free(pp->cfg);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dw_pcie_host_deinit);
|
||||
|
||||
|
|
|
|||
|
|
@ -167,6 +167,14 @@ int dw_pcie_get_resources(struct dw_pcie *pci)
|
|||
}
|
||||
}
|
||||
|
||||
/* ELBI is an optional resource */
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "elbi");
|
||||
if (res) {
|
||||
pci->elbi_base = devm_ioremap_resource(pci->dev, res);
|
||||
if (IS_ERR(pci->elbi_base))
|
||||
return PTR_ERR(pci->elbi_base);
|
||||
}
|
||||
|
||||
/* LLDD is supposed to manually switch the clocks and resets state */
|
||||
if (dw_pcie_cap_is(pci, REQ_RES)) {
|
||||
ret = dw_pcie_get_clocks(pci);
|
||||
|
|
@ -500,7 +508,7 @@ int dw_pcie_prog_outbound_atu(struct dw_pcie *pci,
|
|||
val = dw_pcie_enable_ecrc(val);
|
||||
dw_pcie_writel_atu_ob(pci, atu->index, PCIE_ATU_REGION_CTRL1, val);
|
||||
|
||||
val = PCIE_ATU_ENABLE;
|
||||
val = PCIE_ATU_ENABLE | atu->ctrl2;
|
||||
if (atu->type == PCIE_ATU_TYPE_MSG) {
|
||||
/* The data-less messages only for now */
|
||||
val |= PCIE_ATU_INHIBIT_PAYLOAD | atu->code;
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
#include <linux/irq.h>
|
||||
#include <linux/msi.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pci-ecam.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
#include <linux/pci-epc.h>
|
||||
|
|
@ -124,7 +125,6 @@
|
|||
#define GEN3_RELATED_OFF_GEN3_EQ_DISABLE BIT(16)
|
||||
#define GEN3_RELATED_OFF_RATE_SHADOW_SEL_SHIFT 24
|
||||
#define GEN3_RELATED_OFF_RATE_SHADOW_SEL_MASK GENMASK(25, 24)
|
||||
#define GEN3_RELATED_OFF_RATE_SHADOW_SEL_16_0GT 0x1
|
||||
|
||||
#define GEN3_EQ_CONTROL_OFF 0x8A8
|
||||
#define GEN3_EQ_CONTROL_OFF_FB_MODE GENMASK(3, 0)
|
||||
|
|
@ -135,8 +135,8 @@
|
|||
#define GEN3_EQ_FB_MODE_DIR_CHANGE_OFF 0x8AC
|
||||
#define GEN3_EQ_FMDC_T_MIN_PHASE23 GENMASK(4, 0)
|
||||
#define GEN3_EQ_FMDC_N_EVALS GENMASK(9, 5)
|
||||
#define GEN3_EQ_FMDC_MAX_PRE_CUSROR_DELTA GENMASK(13, 10)
|
||||
#define GEN3_EQ_FMDC_MAX_POST_CUSROR_DELTA GENMASK(17, 14)
|
||||
#define GEN3_EQ_FMDC_MAX_PRE_CURSOR_DELTA GENMASK(13, 10)
|
||||
#define GEN3_EQ_FMDC_MAX_POST_CURSOR_DELTA GENMASK(17, 14)
|
||||
|
||||
#define PCIE_PORT_MULTI_LANE_CTRL 0x8C0
|
||||
#define PORT_MLTI_UPCFG_SUPPORT BIT(7)
|
||||
|
|
@ -170,6 +170,7 @@
|
|||
#define PCIE_ATU_REGION_CTRL2 0x004
|
||||
#define PCIE_ATU_ENABLE BIT(31)
|
||||
#define PCIE_ATU_BAR_MODE_ENABLE BIT(30)
|
||||
#define PCIE_ATU_CFG_SHIFT_MODE_ENABLE BIT(28)
|
||||
#define PCIE_ATU_INHIBIT_PAYLOAD BIT(22)
|
||||
#define PCIE_ATU_FUNC_NUM_MATCH_EN BIT(19)
|
||||
#define PCIE_ATU_LOWER_BASE 0x008
|
||||
|
|
@ -388,6 +389,7 @@ struct dw_pcie_ob_atu_cfg {
|
|||
u8 func_no;
|
||||
u8 code;
|
||||
u8 routing;
|
||||
u32 ctrl2;
|
||||
u64 parent_bus_addr;
|
||||
u64 pci_addr;
|
||||
u64 size;
|
||||
|
|
@ -426,6 +428,9 @@ struct dw_pcie_rp {
|
|||
struct resource *msg_res;
|
||||
bool use_linkup_irq;
|
||||
struct pci_eq_presets presets;
|
||||
struct pci_config_window *cfg;
|
||||
bool ecam_enabled;
|
||||
bool native_ecam;
|
||||
};
|
||||
|
||||
struct dw_pcie_ep_ops {
|
||||
|
|
@ -493,6 +498,7 @@ struct dw_pcie {
|
|||
resource_size_t dbi_phys_addr;
|
||||
void __iomem *dbi_base2;
|
||||
void __iomem *atu_base;
|
||||
void __iomem *elbi_base;
|
||||
resource_size_t atu_phys_addr;
|
||||
size_t atu_size;
|
||||
resource_size_t parent_bus_offset;
|
||||
|
|
|
|||
|
|
@ -8,9 +8,11 @@
|
|||
#include "pcie-designware.h"
|
||||
#include "pcie-qcom-common.h"
|
||||
|
||||
void qcom_pcie_common_set_16gt_equalization(struct dw_pcie *pci)
|
||||
void qcom_pcie_common_set_equalization(struct dw_pcie *pci)
|
||||
{
|
||||
struct device *dev = pci->dev;
|
||||
u32 reg;
|
||||
u16 speed;
|
||||
|
||||
/*
|
||||
* GEN3_RELATED_OFF register is repurposed to apply equalization
|
||||
|
|
@ -19,32 +21,40 @@ void qcom_pcie_common_set_16gt_equalization(struct dw_pcie *pci)
|
|||
* determines the data rate for which these equalization settings are
|
||||
* applied.
|
||||
*/
|
||||
reg = dw_pcie_readl_dbi(pci, GEN3_RELATED_OFF);
|
||||
reg &= ~GEN3_RELATED_OFF_GEN3_ZRXDC_NONCOMPL;
|
||||
reg &= ~GEN3_RELATED_OFF_RATE_SHADOW_SEL_MASK;
|
||||
reg |= FIELD_PREP(GEN3_RELATED_OFF_RATE_SHADOW_SEL_MASK,
|
||||
GEN3_RELATED_OFF_RATE_SHADOW_SEL_16_0GT);
|
||||
dw_pcie_writel_dbi(pci, GEN3_RELATED_OFF, reg);
|
||||
|
||||
reg = dw_pcie_readl_dbi(pci, GEN3_EQ_FB_MODE_DIR_CHANGE_OFF);
|
||||
reg &= ~(GEN3_EQ_FMDC_T_MIN_PHASE23 |
|
||||
GEN3_EQ_FMDC_N_EVALS |
|
||||
GEN3_EQ_FMDC_MAX_PRE_CUSROR_DELTA |
|
||||
GEN3_EQ_FMDC_MAX_POST_CUSROR_DELTA);
|
||||
reg |= FIELD_PREP(GEN3_EQ_FMDC_T_MIN_PHASE23, 0x1) |
|
||||
FIELD_PREP(GEN3_EQ_FMDC_N_EVALS, 0xd) |
|
||||
FIELD_PREP(GEN3_EQ_FMDC_MAX_PRE_CUSROR_DELTA, 0x5) |
|
||||
FIELD_PREP(GEN3_EQ_FMDC_MAX_POST_CUSROR_DELTA, 0x5);
|
||||
dw_pcie_writel_dbi(pci, GEN3_EQ_FB_MODE_DIR_CHANGE_OFF, reg);
|
||||
for (speed = PCIE_SPEED_8_0GT; speed <= pcie_link_speed[pci->max_link_speed]; speed++) {
|
||||
if (speed > PCIE_SPEED_32_0GT) {
|
||||
dev_warn(dev, "Skipped equalization settings for unsupported data rate\n");
|
||||
break;
|
||||
}
|
||||
|
||||
reg = dw_pcie_readl_dbi(pci, GEN3_EQ_CONTROL_OFF);
|
||||
reg &= ~(GEN3_EQ_CONTROL_OFF_FB_MODE |
|
||||
GEN3_EQ_CONTROL_OFF_PHASE23_EXIT_MODE |
|
||||
GEN3_EQ_CONTROL_OFF_FOM_INC_INITIAL_EVAL |
|
||||
GEN3_EQ_CONTROL_OFF_PSET_REQ_VEC);
|
||||
dw_pcie_writel_dbi(pci, GEN3_EQ_CONTROL_OFF, reg);
|
||||
reg = dw_pcie_readl_dbi(pci, GEN3_RELATED_OFF);
|
||||
reg &= ~GEN3_RELATED_OFF_GEN3_ZRXDC_NONCOMPL;
|
||||
reg &= ~GEN3_RELATED_OFF_RATE_SHADOW_SEL_MASK;
|
||||
reg |= FIELD_PREP(GEN3_RELATED_OFF_RATE_SHADOW_SEL_MASK,
|
||||
speed - PCIE_SPEED_8_0GT);
|
||||
dw_pcie_writel_dbi(pci, GEN3_RELATED_OFF, reg);
|
||||
|
||||
reg = dw_pcie_readl_dbi(pci, GEN3_EQ_FB_MODE_DIR_CHANGE_OFF);
|
||||
reg &= ~(GEN3_EQ_FMDC_T_MIN_PHASE23 |
|
||||
GEN3_EQ_FMDC_N_EVALS |
|
||||
GEN3_EQ_FMDC_MAX_PRE_CURSOR_DELTA |
|
||||
GEN3_EQ_FMDC_MAX_POST_CURSOR_DELTA);
|
||||
reg |= FIELD_PREP(GEN3_EQ_FMDC_T_MIN_PHASE23, 0x1) |
|
||||
FIELD_PREP(GEN3_EQ_FMDC_N_EVALS, 0xd) |
|
||||
FIELD_PREP(GEN3_EQ_FMDC_MAX_PRE_CURSOR_DELTA, 0x5) |
|
||||
FIELD_PREP(GEN3_EQ_FMDC_MAX_POST_CURSOR_DELTA, 0x5);
|
||||
dw_pcie_writel_dbi(pci, GEN3_EQ_FB_MODE_DIR_CHANGE_OFF, reg);
|
||||
|
||||
reg = dw_pcie_readl_dbi(pci, GEN3_EQ_CONTROL_OFF);
|
||||
reg &= ~(GEN3_EQ_CONTROL_OFF_FB_MODE |
|
||||
GEN3_EQ_CONTROL_OFF_PHASE23_EXIT_MODE |
|
||||
GEN3_EQ_CONTROL_OFF_FOM_INC_INITIAL_EVAL |
|
||||
GEN3_EQ_CONTROL_OFF_PSET_REQ_VEC);
|
||||
dw_pcie_writel_dbi(pci, GEN3_EQ_CONTROL_OFF, reg);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(qcom_pcie_common_set_16gt_equalization);
|
||||
EXPORT_SYMBOL_GPL(qcom_pcie_common_set_equalization);
|
||||
|
||||
void qcom_pcie_common_set_16gt_lane_margining(struct dw_pcie *pci)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
struct dw_pcie;
|
||||
|
||||
void qcom_pcie_common_set_16gt_equalization(struct dw_pcie *pci);
|
||||
void qcom_pcie_common_set_equalization(struct dw_pcie *pci);
|
||||
void qcom_pcie_common_set_16gt_lane_margining(struct dw_pcie *pci);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -179,7 +179,6 @@ struct qcom_pcie_ep_cfg {
|
|||
* struct qcom_pcie_ep - Qualcomm PCIe Endpoint Controller
|
||||
* @pci: Designware PCIe controller struct
|
||||
* @parf: Qualcomm PCIe specific PARF register base
|
||||
* @elbi: Designware PCIe specific ELBI register base
|
||||
* @mmio: MMIO register base
|
||||
* @perst_map: PERST regmap
|
||||
* @mmio_res: MMIO region resource
|
||||
|
|
@ -202,7 +201,6 @@ struct qcom_pcie_ep {
|
|||
struct dw_pcie pci;
|
||||
|
||||
void __iomem *parf;
|
||||
void __iomem *elbi;
|
||||
void __iomem *mmio;
|
||||
struct regmap *perst_map;
|
||||
struct resource *mmio_res;
|
||||
|
|
@ -267,10 +265,9 @@ static void qcom_pcie_ep_configure_tcsr(struct qcom_pcie_ep *pcie_ep)
|
|||
|
||||
static bool qcom_pcie_dw_link_up(struct dw_pcie *pci)
|
||||
{
|
||||
struct qcom_pcie_ep *pcie_ep = to_pcie_ep(pci);
|
||||
u32 reg;
|
||||
|
||||
reg = readl_relaxed(pcie_ep->elbi + ELBI_SYS_STTS);
|
||||
reg = readl_relaxed(pci->elbi_base + ELBI_SYS_STTS);
|
||||
|
||||
return reg & XMLH_LINK_UP;
|
||||
}
|
||||
|
|
@ -294,16 +291,15 @@ static void qcom_pcie_dw_stop_link(struct dw_pcie *pci)
|
|||
static void qcom_pcie_dw_write_dbi2(struct dw_pcie *pci, void __iomem *base,
|
||||
u32 reg, size_t size, u32 val)
|
||||
{
|
||||
struct qcom_pcie_ep *pcie_ep = to_pcie_ep(pci);
|
||||
int ret;
|
||||
|
||||
writel(1, pcie_ep->elbi + ELBI_CS2_ENABLE);
|
||||
writel(1, pci->elbi_base + ELBI_CS2_ENABLE);
|
||||
|
||||
ret = dw_pcie_write(pci->dbi_base2 + reg, size, val);
|
||||
if (ret)
|
||||
dev_err(pci->dev, "Failed to write DBI2 register (0x%x): %d\n", reg, ret);
|
||||
|
||||
writel(0, pcie_ep->elbi + ELBI_CS2_ENABLE);
|
||||
writel(0, pci->elbi_base + ELBI_CS2_ENABLE);
|
||||
}
|
||||
|
||||
static void qcom_pcie_ep_icc_update(struct qcom_pcie_ep *pcie_ep)
|
||||
|
|
@ -511,10 +507,10 @@ static int qcom_pcie_perst_deassert(struct dw_pcie *pci)
|
|||
goto err_disable_resources;
|
||||
}
|
||||
|
||||
if (pcie_link_speed[pci->max_link_speed] == PCIE_SPEED_16_0GT) {
|
||||
qcom_pcie_common_set_16gt_equalization(pci);
|
||||
qcom_pcie_common_set_equalization(pci);
|
||||
|
||||
if (pcie_link_speed[pci->max_link_speed] == PCIE_SPEED_16_0GT)
|
||||
qcom_pcie_common_set_16gt_lane_margining(pci);
|
||||
}
|
||||
|
||||
/*
|
||||
* The physical address of the MMIO region which is exposed as the BAR
|
||||
|
|
@ -583,11 +579,6 @@ static int qcom_pcie_ep_get_io_resources(struct platform_device *pdev,
|
|||
return PTR_ERR(pci->dbi_base);
|
||||
pci->dbi_base2 = pci->dbi_base;
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "elbi");
|
||||
pcie_ep->elbi = devm_pci_remap_cfg_resource(dev, res);
|
||||
if (IS_ERR(pcie_ep->elbi))
|
||||
return PTR_ERR(pcie_ep->elbi);
|
||||
|
||||
pcie_ep->mmio_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
||||
"mmio");
|
||||
if (!pcie_ep->mmio_res) {
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@
|
|||
#define PARF_AXI_MSTR_WR_ADDR_HALT_V2 0x1a8
|
||||
#define PARF_Q2A_FLUSH 0x1ac
|
||||
#define PARF_LTSSM 0x1b0
|
||||
#define PARF_SLV_DBI_ELBI 0x1b4
|
||||
#define PARF_INT_ALL_STATUS 0x224
|
||||
#define PARF_INT_ALL_CLEAR 0x228
|
||||
#define PARF_INT_ALL_MASK 0x22c
|
||||
|
|
@ -64,6 +65,16 @@
|
|||
#define PARF_DBI_BASE_ADDR_V2_HI 0x354
|
||||
#define PARF_SLV_ADDR_SPACE_SIZE_V2 0x358
|
||||
#define PARF_SLV_ADDR_SPACE_SIZE_V2_HI 0x35c
|
||||
#define PARF_BLOCK_SLV_AXI_WR_BASE 0x360
|
||||
#define PARF_BLOCK_SLV_AXI_WR_BASE_HI 0x364
|
||||
#define PARF_BLOCK_SLV_AXI_WR_LIMIT 0x368
|
||||
#define PARF_BLOCK_SLV_AXI_WR_LIMIT_HI 0x36c
|
||||
#define PARF_BLOCK_SLV_AXI_RD_BASE 0x370
|
||||
#define PARF_BLOCK_SLV_AXI_RD_BASE_HI 0x374
|
||||
#define PARF_BLOCK_SLV_AXI_RD_LIMIT 0x378
|
||||
#define PARF_BLOCK_SLV_AXI_RD_LIMIT_HI 0x37c
|
||||
#define PARF_ECAM_BASE 0x380
|
||||
#define PARF_ECAM_BASE_HI 0x384
|
||||
#define PARF_NO_SNOOP_OVERRIDE 0x3d4
|
||||
#define PARF_ATU_BASE_ADDR 0x634
|
||||
#define PARF_ATU_BASE_ADDR_HI 0x638
|
||||
|
|
@ -87,6 +98,7 @@
|
|||
|
||||
/* PARF_SYS_CTRL register fields */
|
||||
#define MAC_PHY_POWERDOWN_IN_P2_D_MUX_EN BIT(29)
|
||||
#define PCIE_ECAM_BLOCKER_EN BIT(26)
|
||||
#define MST_WAKEUP_EN BIT(13)
|
||||
#define SLV_WAKEUP_EN BIT(12)
|
||||
#define MSTR_ACLK_CGC_DIS BIT(10)
|
||||
|
|
@ -134,6 +146,9 @@
|
|||
/* PARF_LTSSM register fields */
|
||||
#define LTSSM_EN BIT(8)
|
||||
|
||||
/* PARF_SLV_DBI_ELBI */
|
||||
#define SLV_DBI_ELBI_ADDR_BASE GENMASK(11, 0)
|
||||
|
||||
/* PARF_INT_ALL_{STATUS/CLEAR/MASK} register fields */
|
||||
#define PARF_INT_ALL_LINK_UP BIT(13)
|
||||
#define PARF_INT_MSI_DEV_0_7 GENMASK(30, 23)
|
||||
|
|
@ -275,11 +290,8 @@ struct qcom_pcie_port {
|
|||
struct qcom_pcie {
|
||||
struct dw_pcie *pci;
|
||||
void __iomem *parf; /* DT parf */
|
||||
void __iomem *elbi; /* DT elbi */
|
||||
void __iomem *mhi;
|
||||
union qcom_pcie_resources res;
|
||||
struct phy *phy;
|
||||
struct gpio_desc *reset;
|
||||
struct icc_path *icc_mem;
|
||||
struct icc_path *icc_cpu;
|
||||
const struct qcom_pcie_cfg *cfg;
|
||||
|
|
@ -296,11 +308,8 @@ static void qcom_perst_assert(struct qcom_pcie *pcie, bool assert)
|
|||
struct qcom_pcie_port *port;
|
||||
int val = assert ? 1 : 0;
|
||||
|
||||
if (list_empty(&pcie->ports))
|
||||
gpiod_set_value_cansleep(pcie->reset, val);
|
||||
else
|
||||
list_for_each_entry(port, &pcie->ports, list)
|
||||
gpiod_set_value_cansleep(port->reset, val);
|
||||
list_for_each_entry(port, &pcie->ports, list)
|
||||
gpiod_set_value_cansleep(port->reset, val);
|
||||
|
||||
usleep_range(PERST_DELAY_US, PERST_DELAY_US + 500);
|
||||
}
|
||||
|
|
@ -317,14 +326,55 @@ static void qcom_ep_reset_deassert(struct qcom_pcie *pcie)
|
|||
qcom_perst_assert(pcie, false);
|
||||
}
|
||||
|
||||
static void qcom_pci_config_ecam(struct dw_pcie_rp *pp)
|
||||
{
|
||||
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
|
||||
struct qcom_pcie *pcie = to_qcom_pcie(pci);
|
||||
u64 addr, addr_end;
|
||||
u32 val;
|
||||
|
||||
writel_relaxed(lower_32_bits(pci->dbi_phys_addr), pcie->parf + PARF_ECAM_BASE);
|
||||
writel_relaxed(upper_32_bits(pci->dbi_phys_addr), pcie->parf + PARF_ECAM_BASE_HI);
|
||||
|
||||
/*
|
||||
* The only device on the root bus is a single Root Port. If we try to
|
||||
* access any devices other than Device/Function 00.0 on Bus 0, the TLP
|
||||
* will go outside of the controller to the PCI bus. But with CFG Shift
|
||||
* Feature (ECAM) enabled in iATU, there is no guarantee that the
|
||||
* response is going to be all F's. Hence, to make sure that the
|
||||
* requester gets all F's response for accesses other than the Root
|
||||
* Port, configure iATU to block the transactions starting from
|
||||
* function 1 of the root bus to the end of the root bus (i.e., from
|
||||
* dbi_base + 4KB to dbi_base + 1MB).
|
||||
*/
|
||||
addr = pci->dbi_phys_addr + SZ_4K;
|
||||
writel_relaxed(lower_32_bits(addr), pcie->parf + PARF_BLOCK_SLV_AXI_WR_BASE);
|
||||
writel_relaxed(upper_32_bits(addr), pcie->parf + PARF_BLOCK_SLV_AXI_WR_BASE_HI);
|
||||
|
||||
writel_relaxed(lower_32_bits(addr), pcie->parf + PARF_BLOCK_SLV_AXI_RD_BASE);
|
||||
writel_relaxed(upper_32_bits(addr), pcie->parf + PARF_BLOCK_SLV_AXI_RD_BASE_HI);
|
||||
|
||||
addr_end = pci->dbi_phys_addr + SZ_1M - 1;
|
||||
|
||||
writel_relaxed(lower_32_bits(addr_end), pcie->parf + PARF_BLOCK_SLV_AXI_WR_LIMIT);
|
||||
writel_relaxed(upper_32_bits(addr_end), pcie->parf + PARF_BLOCK_SLV_AXI_WR_LIMIT_HI);
|
||||
|
||||
writel_relaxed(lower_32_bits(addr_end), pcie->parf + PARF_BLOCK_SLV_AXI_RD_LIMIT);
|
||||
writel_relaxed(upper_32_bits(addr_end), pcie->parf + PARF_BLOCK_SLV_AXI_RD_LIMIT_HI);
|
||||
|
||||
val = readl_relaxed(pcie->parf + PARF_SYS_CTRL);
|
||||
val |= PCIE_ECAM_BLOCKER_EN;
|
||||
writel_relaxed(val, pcie->parf + PARF_SYS_CTRL);
|
||||
}
|
||||
|
||||
static int qcom_pcie_start_link(struct dw_pcie *pci)
|
||||
{
|
||||
struct qcom_pcie *pcie = to_qcom_pcie(pci);
|
||||
|
||||
if (pcie_link_speed[pci->max_link_speed] == PCIE_SPEED_16_0GT) {
|
||||
qcom_pcie_common_set_16gt_equalization(pci);
|
||||
qcom_pcie_common_set_equalization(pci);
|
||||
|
||||
if (pcie_link_speed[pci->max_link_speed] == PCIE_SPEED_16_0GT)
|
||||
qcom_pcie_common_set_16gt_lane_margining(pci);
|
||||
}
|
||||
|
||||
/* Enable Link Training state machine */
|
||||
if (pcie->cfg->ops->ltssm_enable)
|
||||
|
|
@ -413,12 +463,17 @@ static void qcom_pcie_configure_dbi_atu_base(struct qcom_pcie *pcie)
|
|||
|
||||
static void qcom_pcie_2_1_0_ltssm_enable(struct qcom_pcie *pcie)
|
||||
{
|
||||
struct dw_pcie *pci = pcie->pci;
|
||||
u32 val;
|
||||
|
||||
if (!pci->elbi_base) {
|
||||
dev_err(pci->dev, "ELBI is not present\n");
|
||||
return;
|
||||
}
|
||||
/* enable link training */
|
||||
val = readl(pcie->elbi + ELBI_SYS_CTRL);
|
||||
val = readl(pci->elbi_base + ELBI_SYS_CTRL);
|
||||
val |= ELBI_SYS_CTRL_LT_ENABLE;
|
||||
writel(val, pcie->elbi + ELBI_SYS_CTRL);
|
||||
writel(val, pci->elbi_base + ELBI_SYS_CTRL);
|
||||
}
|
||||
|
||||
static int qcom_pcie_get_resources_2_1_0(struct qcom_pcie *pcie)
|
||||
|
|
@ -1233,63 +1288,39 @@ static bool qcom_pcie_link_up(struct dw_pcie *pci)
|
|||
return val & PCI_EXP_LNKSTA_DLLLA;
|
||||
}
|
||||
|
||||
static void qcom_pcie_phy_exit(struct qcom_pcie *pcie)
|
||||
{
|
||||
struct qcom_pcie_port *port;
|
||||
|
||||
if (list_empty(&pcie->ports))
|
||||
phy_exit(pcie->phy);
|
||||
else
|
||||
list_for_each_entry(port, &pcie->ports, list)
|
||||
phy_exit(port->phy);
|
||||
}
|
||||
|
||||
static void qcom_pcie_phy_power_off(struct qcom_pcie *pcie)
|
||||
{
|
||||
struct qcom_pcie_port *port;
|
||||
|
||||
if (list_empty(&pcie->ports)) {
|
||||
phy_power_off(pcie->phy);
|
||||
} else {
|
||||
list_for_each_entry(port, &pcie->ports, list)
|
||||
phy_power_off(port->phy);
|
||||
}
|
||||
list_for_each_entry(port, &pcie->ports, list)
|
||||
phy_power_off(port->phy);
|
||||
}
|
||||
|
||||
static int qcom_pcie_phy_power_on(struct qcom_pcie *pcie)
|
||||
{
|
||||
struct qcom_pcie_port *port;
|
||||
int ret = 0;
|
||||
int ret;
|
||||
|
||||
if (list_empty(&pcie->ports)) {
|
||||
ret = phy_set_mode_ext(pcie->phy, PHY_MODE_PCIE, PHY_MODE_PCIE_RC);
|
||||
list_for_each_entry(port, &pcie->ports, list) {
|
||||
ret = phy_set_mode_ext(port->phy, PHY_MODE_PCIE, PHY_MODE_PCIE_RC);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = phy_power_on(pcie->phy);
|
||||
if (ret)
|
||||
ret = phy_power_on(port->phy);
|
||||
if (ret) {
|
||||
qcom_pcie_phy_power_off(pcie);
|
||||
return ret;
|
||||
} else {
|
||||
list_for_each_entry(port, &pcie->ports, list) {
|
||||
ret = phy_set_mode_ext(port->phy, PHY_MODE_PCIE, PHY_MODE_PCIE_RC);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = phy_power_on(port->phy);
|
||||
if (ret) {
|
||||
qcom_pcie_phy_power_off(pcie);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcom_pcie_host_init(struct dw_pcie_rp *pp)
|
||||
{
|
||||
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
|
||||
struct qcom_pcie *pcie = to_qcom_pcie(pci);
|
||||
u16 offset;
|
||||
int ret;
|
||||
|
||||
qcom_ep_reset_assert(pcie);
|
||||
|
|
@ -1298,6 +1329,17 @@ static int qcom_pcie_host_init(struct dw_pcie_rp *pp)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (pp->ecam_enabled) {
|
||||
/*
|
||||
* Override ELBI when ECAM is enabled, as when ECAM is enabled,
|
||||
* ELBI moves under the 'config' space.
|
||||
*/
|
||||
offset = FIELD_GET(SLV_DBI_ELBI_ADDR_BASE, readl(pcie->parf + PARF_SLV_DBI_ELBI));
|
||||
pci->elbi_base = pci->dbi_base + offset;
|
||||
|
||||
qcom_pci_config_ecam(pp);
|
||||
}
|
||||
|
||||
ret = qcom_pcie_phy_power_on(pcie);
|
||||
if (ret)
|
||||
goto err_deinit;
|
||||
|
|
@ -1708,6 +1750,8 @@ static int qcom_pcie_parse_ports(struct qcom_pcie *pcie)
|
|||
int ret = -ENOENT;
|
||||
|
||||
for_each_available_child_of_node_scoped(dev->of_node, of_port) {
|
||||
if (!of_node_is_type(of_port, "pci"))
|
||||
continue;
|
||||
ret = qcom_pcie_parse_port(pcie, of_port);
|
||||
if (ret)
|
||||
goto err_port_del;
|
||||
|
|
@ -1716,8 +1760,10 @@ static int qcom_pcie_parse_ports(struct qcom_pcie *pcie)
|
|||
return ret;
|
||||
|
||||
err_port_del:
|
||||
list_for_each_entry_safe(port, tmp, &pcie->ports, list)
|
||||
list_for_each_entry_safe(port, tmp, &pcie->ports, list) {
|
||||
phy_exit(port->phy);
|
||||
list_del(&port->list);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -1725,20 +1771,32 @@ static int qcom_pcie_parse_ports(struct qcom_pcie *pcie)
|
|||
static int qcom_pcie_parse_legacy_binding(struct qcom_pcie *pcie)
|
||||
{
|
||||
struct device *dev = pcie->pci->dev;
|
||||
struct qcom_pcie_port *port;
|
||||
struct gpio_desc *reset;
|
||||
struct phy *phy;
|
||||
int ret;
|
||||
|
||||
pcie->phy = devm_phy_optional_get(dev, "pciephy");
|
||||
if (IS_ERR(pcie->phy))
|
||||
return PTR_ERR(pcie->phy);
|
||||
phy = devm_phy_optional_get(dev, "pciephy");
|
||||
if (IS_ERR(phy))
|
||||
return PTR_ERR(phy);
|
||||
|
||||
pcie->reset = devm_gpiod_get_optional(dev, "perst", GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(pcie->reset))
|
||||
return PTR_ERR(pcie->reset);
|
||||
reset = devm_gpiod_get_optional(dev, "perst", GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(reset))
|
||||
return PTR_ERR(reset);
|
||||
|
||||
ret = phy_init(pcie->phy);
|
||||
ret = phy_init(phy);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
|
||||
if (!port)
|
||||
return -ENOMEM;
|
||||
|
||||
port->reset = reset;
|
||||
port->phy = phy;
|
||||
INIT_LIST_HEAD(&port->list);
|
||||
list_add_tail(&port->list, &pcie->ports);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1829,12 +1887,6 @@ static int qcom_pcie_probe(struct platform_device *pdev)
|
|||
goto err_pm_runtime_put;
|
||||
}
|
||||
|
||||
pcie->elbi = devm_platform_ioremap_resource_byname(pdev, "elbi");
|
||||
if (IS_ERR(pcie->elbi)) {
|
||||
ret = PTR_ERR(pcie->elbi);
|
||||
goto err_pm_runtime_put;
|
||||
}
|
||||
|
||||
/* MHI region is optional */
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mhi");
|
||||
if (res) {
|
||||
|
|
@ -1952,9 +2004,10 @@ static int qcom_pcie_probe(struct platform_device *pdev)
|
|||
err_host_deinit:
|
||||
dw_pcie_host_deinit(pp);
|
||||
err_phy_exit:
|
||||
qcom_pcie_phy_exit(pcie);
|
||||
list_for_each_entry_safe(port, tmp, &pcie->ports, list)
|
||||
list_for_each_entry_safe(port, tmp, &pcie->ports, list) {
|
||||
phy_exit(port->phy);
|
||||
list_del(&port->list);
|
||||
}
|
||||
err_pm_runtime_put:
|
||||
pm_runtime_put(dev);
|
||||
pm_runtime_disable(dev);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user