diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index ce5d0e2..562304c 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -9,5 +9,5 @@ obj-$(CONFIG_PCI_MVEBU) += pci-mvebu.o pci-mvebu-phy.o obj-$(CONFIG_PCI_TEGRA) += pci-tegra.o -obj-$(CONFIG_PCIE_DW) += pcie-designware.o +obj-$(CONFIG_PCIE_DW) += pcie-designware.o pcie-designware-host.o obj-$(CONFIG_PCI_IMX6) += pci-imx6.o diff --git a/drivers/pci/pci-imx6.c b/drivers/pci/pci-imx6.c index fc56a0d..38e002a 100644 --- a/drivers/pci/pci-imx6.c +++ b/drivers/pci/pci-imx6.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * PCIe host controller driver for Freescale i.MX6 SoCs * @@ -5,10 +6,6 @@ * http://www.kosagi.com * * Author: Sean Cross - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #include @@ -35,7 +32,7 @@ #include "pcie-designware.h" -#define to_imx6_pcie(x) container_of(x, struct imx6_pcie, pp) +#define to_imx6_pcie(x) ((x)->dev->priv) enum imx6_pcie_variants { IMX6Q, @@ -43,19 +40,19 @@ }; struct imx6_pcie { + struct dw_pcie *pci; int reset_gpio; struct clk *pcie_bus; struct clk *pcie_phy; struct clk *pcie; - struct pcie_port pp; void __iomem *iomuxc_gpr; enum imx6_pcie_variants variant; - void __iomem *mem_base; u32 tx_deemph_gen1; u32 tx_deemph_gen2_3p5db; u32 tx_deemph_gen2_6db; u32 tx_swing_full; u32 tx_swing_low; + int link_gen; }; /* PCIe Root Complex registers (memory-mapped) */ @@ -72,7 +69,6 @@ #define PCIE_PL_PFLR_LINK_STATE_MASK (0x3f << 16) #define PCIE_PL_PFLR_FORCE_LINK (1 << 15) #define PCIE_PHY_DEBUG_R0 (PL_OFFSET + 0x28) -#define PCIE_PHY_DEBUG_R1 (PL_OFFSET + 0x2c) #define PCIE_PHY_DEBUG_R1_XMLH_LINK_IN_TRAINING (1 << 29) #define PCIE_PHY_DEBUG_R1_XMLH_LINK_UP (1 << 4) @@ -96,14 +92,15 @@ #define PHY_RX_OVRD_IN_LO_RX_DATA_EN (1 << 5) #define PHY_RX_OVRD_IN_LO_RX_PLL_EN (1 << 3) -static int pcie_phy_poll_ack(void __iomem *dbi_base, int exp_val) +static int pcie_phy_poll_ack(struct imx6_pcie *imx6_pcie, int exp_val) { + struct dw_pcie *pci = imx6_pcie->pci; u32 val; u32 max_iterations = 10; u32 wait_counter = 0; do { - val = readl(dbi_base + PCIE_PHY_STAT); + val = dw_pcie_readl_dbi(pci, PCIE_PHY_STAT); val = (val >> PCIE_PHY_STAT_ACK_LOC) & 0x1; wait_counter++; @@ -116,129 +113,130 @@ return -ETIMEDOUT; } -static int pcie_phy_wait_ack(void __iomem *dbi_base, int addr) +static int pcie_phy_wait_ack(struct imx6_pcie *imx6_pcie, int addr) { + struct dw_pcie *pci = imx6_pcie->pci; u32 val; int ret; val = addr << PCIE_PHY_CTRL_DATA_LOC; - writel(val, dbi_base + PCIE_PHY_CTRL); val |= (0x1 << PCIE_PHY_CTRL_CAP_ADR_LOC); - writel(val, dbi_base + PCIE_PHY_CTRL); + dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, val); - ret = pcie_phy_poll_ack(dbi_base, 1); + ret = pcie_phy_poll_ack(imx6_pcie, 1); if (ret) return ret; val = addr << PCIE_PHY_CTRL_DATA_LOC; - writel(val, dbi_base + PCIE_PHY_CTRL); + dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, val); - return pcie_phy_poll_ack(dbi_base, 0); + return pcie_phy_poll_ack(imx6_pcie, 0); } /* Read from the 16-bit PCIe PHY control registers (not memory-mapped) */ -static int pcie_phy_read(void __iomem *dbi_base, int addr , int *data) +static int pcie_phy_read(struct imx6_pcie *imx6_pcie, int addr , int *data) { + struct dw_pcie *pci = imx6_pcie->pci; u32 val, phy_ctl; int ret; - ret = pcie_phy_wait_ack(dbi_base, addr); + ret = pcie_phy_wait_ack(imx6_pcie, addr); if (ret) return ret; /* assert Read signal */ phy_ctl = 0x1 << PCIE_PHY_CTRL_RD_LOC; - writel(phy_ctl, dbi_base + PCIE_PHY_CTRL); + dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, phy_ctl); - ret = pcie_phy_poll_ack(dbi_base, 1); + ret = pcie_phy_poll_ack(imx6_pcie, 1); if (ret) return ret; - val = readl(dbi_base + PCIE_PHY_STAT); + val = dw_pcie_readl_dbi(pci, PCIE_PHY_STAT); *data = val & 0xffff; /* deassert Read signal */ - writel(0x00, dbi_base + PCIE_PHY_CTRL); + dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, 0x00); - return pcie_phy_poll_ack(dbi_base, 0); + return pcie_phy_poll_ack(imx6_pcie, 0); } -static int pcie_phy_write(void __iomem *dbi_base, int addr, int data) +static int pcie_phy_write(struct imx6_pcie *imx6_pcie, int addr, int data) { + struct dw_pcie *pci = imx6_pcie->pci; u32 var; int ret; /* write addr */ /* cap addr */ - ret = pcie_phy_wait_ack(dbi_base, addr); + ret = pcie_phy_wait_ack(imx6_pcie, addr); if (ret) return ret; var = data << PCIE_PHY_CTRL_DATA_LOC; - writel(var, dbi_base + PCIE_PHY_CTRL); + dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, var); /* capture data */ var |= (0x1 << PCIE_PHY_CTRL_CAP_DAT_LOC); - writel(var, dbi_base + PCIE_PHY_CTRL); + dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, var); - ret = pcie_phy_poll_ack(dbi_base, 1); + ret = pcie_phy_poll_ack(imx6_pcie, 1); if (ret) return ret; /* deassert cap data */ var = data << PCIE_PHY_CTRL_DATA_LOC; - writel(var, dbi_base + PCIE_PHY_CTRL); + dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, var); /* wait for ack de-assertion */ - ret = pcie_phy_poll_ack(dbi_base, 0); + ret = pcie_phy_poll_ack(imx6_pcie, 0); if (ret) return ret; /* assert wr signal */ var = 0x1 << PCIE_PHY_CTRL_WR_LOC; - writel(var, dbi_base + PCIE_PHY_CTRL); + dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, var); /* wait for ack */ - ret = pcie_phy_poll_ack(dbi_base, 1); + ret = pcie_phy_poll_ack(imx6_pcie, 1); if (ret) return ret; /* deassert wr signal */ var = data << PCIE_PHY_CTRL_DATA_LOC; - writel(var, dbi_base + PCIE_PHY_CTRL); + dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, var); /* wait for ack de-assertion */ - ret = pcie_phy_poll_ack(dbi_base, 0); + ret = pcie_phy_poll_ack(imx6_pcie, 0); if (ret) return ret; - writel(0x0, dbi_base + PCIE_PHY_CTRL); + dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, 0x0); return 0; } -static void imx6_pcie_reset_phy(struct pcie_port *pp) +static void imx6_pcie_reset_phy(struct imx6_pcie *imx6_pcie) { uint32_t temp; - pcie_phy_read(pp->dbi_base, PHY_RX_OVRD_IN_LO, &temp); + pcie_phy_read(imx6_pcie, PHY_RX_OVRD_IN_LO, &temp); temp |= (PHY_RX_OVRD_IN_LO_RX_DATA_EN | PHY_RX_OVRD_IN_LO_RX_PLL_EN); - pcie_phy_write(pp->dbi_base, PHY_RX_OVRD_IN_LO, temp); + pcie_phy_write(imx6_pcie, PHY_RX_OVRD_IN_LO, temp); udelay(2000); - pcie_phy_read(pp->dbi_base, PHY_RX_OVRD_IN_LO, &temp); + pcie_phy_read(imx6_pcie, PHY_RX_OVRD_IN_LO, &temp); temp &= ~(PHY_RX_OVRD_IN_LO_RX_DATA_EN | PHY_RX_OVRD_IN_LO_RX_PLL_EN); - pcie_phy_write(pp->dbi_base, PHY_RX_OVRD_IN_LO, temp); + pcie_phy_write(imx6_pcie, PHY_RX_OVRD_IN_LO, temp); } -static int imx6_pcie_assert_core_reset(struct pcie_port *pp) +static void imx6_pcie_assert_core_reset(struct imx6_pcie *imx6_pcie) { - struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp); - u32 val, gpr1, gpr12; + u32 gpr1; switch (imx6_pcie->variant) { case IMX6QP: @@ -247,34 +245,7 @@ writel(gpr1, imx6_pcie->iomuxc_gpr + IOMUXC_GPR1); break; case IMX6Q: - /* - * If the bootloader already enabled the link we need some special - * handling to get the core back into a state where it is safe to - * touch it for configuration. As there is no dedicated reset signal - * wired up for MX6QDL, we need to manually force LTSSM into "detect" - * state before completely disabling LTSSM, which is a prerequisite - * for core configuration. - * - * If both LTSSM_ENABLE and REF_SSP_ENABLE are active we have a strong - * indication that the bootloader activated the link. - */ gpr1 = readl(imx6_pcie->iomuxc_gpr + IOMUXC_GPR1); - gpr12 = readl(imx6_pcie->iomuxc_gpr + IOMUXC_GPR12); - - if ((gpr1 & IMX6Q_GPR1_PCIE_REF_CLK_EN) && - (gpr12 & IMX6Q_GPR12_PCIE_CTL_2)) { - val = readl(pp->dbi_base + PCIE_PL_PFLR); - val &= ~PCIE_PL_PFLR_LINK_STATE_MASK; - val |= PCIE_PL_PFLR_FORCE_LINK; - - data_abort_mask(); - writel(val, pp->dbi_base + PCIE_PL_PFLR); - data_abort_unmask(); - - gpr12 &= ~IMX6Q_GPR12_PCIE_CTL_2; - writel(gpr12, imx6_pcie->iomuxc_gpr + IOMUXC_GPR12); - } - gpr1 |= IMX6Q_GPR1_PCIE_TEST_PD; writel(gpr1, imx6_pcie->iomuxc_gpr + IOMUXC_GPR1); @@ -282,28 +253,12 @@ writel(gpr1, imx6_pcie->iomuxc_gpr + IOMUXC_GPR1); break; } - - return 0; } -static int imx6_pcie_deassert_core_reset(struct pcie_port *pp) +static int imx6_pcie_enable_ref_clk(struct imx6_pcie *imx6_pcie) { - struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp); - int ret; u32 gpr1; - ret = clk_enable(imx6_pcie->pcie_phy); - if (ret) - goto err_pcie_phy; - - ret = clk_enable(imx6_pcie->pcie_bus); - if (ret) - goto err_pcie_bus; - - ret = clk_enable(imx6_pcie->pcie); - if (ret) - goto err_pcie; - /* power up core phy and enable ref clock */ gpr1 = readl(imx6_pcie->iomuxc_gpr + IOMUXC_GPR1); gpr1 &= ~IMX6Q_GPR1_PCIE_TEST_PD; @@ -319,6 +274,39 @@ gpr1 |= IMX6Q_GPR1_PCIE_REF_CLK_EN; writel(gpr1, imx6_pcie->iomuxc_gpr + IOMUXC_GPR1); + return 0; +} + +static void imx6_pcie_deassert_core_reset(struct imx6_pcie *imx6_pcie) +{ + struct device_d *dev = imx6_pcie->pci->dev; + int ret; + u32 gpr1; + + ret = clk_enable(imx6_pcie->pcie_phy); + if (ret) { + dev_err(dev, "unable to enable pcie_phy clock\n"); + return; + } + + ret = clk_enable(imx6_pcie->pcie_bus); + if (ret) { + dev_err(dev, "unable to enable pcie_bus clock\n"); + goto err_pcie_bus; + } + + ret = clk_enable(imx6_pcie->pcie); + if (ret) { + dev_err(dev, "unable to enable pcie clock\n"); + goto err_pcie; + } + + ret = imx6_pcie_enable_ref_clk(imx6_pcie); + if (ret) { + dev_err(dev, "unable to enable pcie ref clock\n"); + goto err_ref_clk; + } + /* allow the clocks to stabilize */ udelay(200); @@ -344,20 +332,18 @@ break; } - return 0; + return; +err_ref_clk: + clk_disable(imx6_pcie->pcie); err_pcie: clk_disable(imx6_pcie->pcie_bus); err_pcie_bus: clk_disable(imx6_pcie->pcie_phy); -err_pcie_phy: - return ret; - } -static void imx6_pcie_init_phy(struct pcie_port *pp) +static void imx6_pcie_init_phy(struct imx6_pcie *imx6_pcie) { - struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp); u32 gpr12, gpr8; gpr12 = readl(imx6_pcie->iomuxc_gpr + IOMUXC_GPR12); @@ -395,41 +381,34 @@ writel(gpr8, imx6_pcie->iomuxc_gpr + IOMUXC_GPR8); } -static int imx6_pcie_wait_for_link(struct pcie_port *pp) +static int imx6_pcie_wait_for_link(struct imx6_pcie *imx6_pcie) { - uint64_t start = get_time_ns(); - - while (1) { - if (dw_pcie_link_up(pp)) - return 0; - - if (!is_timeout(start, SECOND)) - continue; - - return -EINVAL; - } + return dw_pcie_wait_for_link(imx6_pcie->pci); } -static int imx6_pcie_wait_for_speed_change(struct pcie_port *pp) +static int imx6_pcie_wait_for_speed_change(struct imx6_pcie *imx6_pcie) { + struct dw_pcie *pci = imx6_pcie->pci; + struct device_d *dev = pci->dev; uint32_t tmp; uint64_t start = get_time_ns(); while (!is_timeout(start, SECOND)) { - tmp = readl(pp->dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL); + tmp = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL); /* Test if the speed change finished. */ if (!(tmp & PORT_LOGIC_SPEED_CHANGE)) return 0; } - dev_err(pp->dev, "Speed change timeout\n"); + dev_err(dev, "Speed change timeout\n"); return -EINVAL; } -static int imx6_pcie_establish_link(struct pcie_port *pp) +static int imx6_pcie_establish_link(struct imx6_pcie *imx6_pcie) { - struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp); + struct dw_pcie *pci = imx6_pcie->pci; + struct device_d *dev = pci->dev; uint32_t tmp; int ret; u32 gpr12; @@ -439,124 +418,98 @@ * started in Gen2 mode, there is a possibility the devices on the * bus will not be detected at all. This happens with PCIe switches. */ - tmp = readl(pp->dbi_base + PCIE_RC_LCR); + tmp = dw_pcie_readl_dbi(pci, PCIE_RC_LCR); tmp &= ~PCIE_RC_LCR_MAX_LINK_SPEEDS_MASK; tmp |= PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN1; - writel(tmp, pp->dbi_base + PCIE_RC_LCR); + dw_pcie_writel_dbi(pci, PCIE_RC_LCR, tmp); /* Start LTSSM. */ gpr12 = readl(imx6_pcie->iomuxc_gpr + IOMUXC_GPR12); gpr12 |= IMX6Q_GPR12_PCIE_CTL_2; writel(gpr12, imx6_pcie->iomuxc_gpr + IOMUXC_GPR12); - ret = imx6_pcie_wait_for_link(pp); - if (ret) { - dev_info(pp->dev, "Link never came up\n"); + ret = imx6_pcie_wait_for_link(imx6_pcie); + if (ret) goto err_reset_phy; - } - /* Allow Gen2 mode after the link is up. */ - tmp = readl(pp->dbi_base + PCIE_RC_LCR); - tmp &= ~PCIE_RC_LCR_MAX_LINK_SPEEDS_MASK; - tmp |= PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN2; - writel(tmp, pp->dbi_base + PCIE_RC_LCR); + + if (imx6_pcie->link_gen == 2) { + /* Allow Gen2 mode after the link is up. */ + tmp = dw_pcie_readl_dbi(pci, PCIE_RC_LCR); + tmp &= ~PCIE_RC_LCR_MAX_LINK_SPEEDS_MASK; + tmp |= PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN2; + dw_pcie_writel_dbi(pci, PCIE_RC_LCR, tmp); + } else { + dev_info(dev, "Link: Gen2 disabled\n"); + } /* * Start Directed Speed Change so the best possible speed both link * partners support can be negotiated. */ - tmp = readl(pp->dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL); + tmp = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL); tmp |= PORT_LOGIC_SPEED_CHANGE; - writel(tmp, pp->dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL); + dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, tmp); - ret = imx6_pcie_wait_for_speed_change(pp); + ret = imx6_pcie_wait_for_speed_change(imx6_pcie); if (ret) { - dev_err(pp->dev, "Failed to bring link up!\n"); + dev_err(dev, "Failed to bring link up!\n"); goto err_reset_phy; } /* Make sure link training is finished as well! */ - ret = imx6_pcie_wait_for_link(pp); + ret = imx6_pcie_wait_for_link(imx6_pcie); if (ret) { - dev_err(pp->dev, "Failed to bring link up!\n"); + dev_err(dev, "Failed to bring link up!\n"); goto err_reset_phy; } - tmp = readl(pp->dbi_base + PCIE_RC_LCSR); - dev_dbg(pp->dev, "Link up, Gen=%i\n", (tmp >> 16) & 0xf); - + tmp = dw_pcie_readl_dbi(pci, PCIE_RC_LCSR); + dev_info(dev, "Link up, Gen%i\n", (tmp >> 16) & 0xf); return 0; err_reset_phy: - dev_dbg(pp->dev, "PHY DEBUG_R0=0x%08x DEBUG_R1=0x%08x\n", - readl(pp->dbi_base + PCIE_PHY_DEBUG_R0), - readl(pp->dbi_base + PCIE_PHY_DEBUG_R1)); - imx6_pcie_reset_phy(pp); + dev_dbg(dev, "PHY DEBUG_R0=0x%08x DEBUG_R1=0x%08x\n", + dw_pcie_readl_dbi(pci, PCIE_PHY_DEBUG_R0), + dw_pcie_readl_dbi(pci, PCIE_PHY_DEBUG_R1)); + imx6_pcie_reset_phy(imx6_pcie); return ret; } -static void imx6_pcie_host_init(struct pcie_port *pp) +static int imx6_pcie_host_init(struct pcie_port *pp) { - imx6_pcie_assert_core_reset(pp); + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct imx6_pcie *imx6_pcie = to_imx6_pcie(pci); - imx6_pcie_init_phy(pp); - - imx6_pcie_deassert_core_reset(pp); - + imx6_pcie_assert_core_reset(imx6_pcie); + imx6_pcie_init_phy(imx6_pcie); + imx6_pcie_deassert_core_reset(imx6_pcie); dw_pcie_setup_rc(pp); - - imx6_pcie_establish_link(pp); -} - -static int imx6_pcie_link_up(struct pcie_port *pp) -{ - u32 rc; - int count = 5; - - /* - * Test if the PHY reports that the link is up and also that the LTSSM - * training finished. There are three possible states of the link when - * this code is called: - * 1) The link is DOWN (unlikely) - * The link didn't come up yet for some reason. This usually means - * we have a real problem somewhere. Reset the PHY and exit. This - * state calls for inspection of the DEBUG registers. - * 2) The link is UP, but still in LTSSM training - * Wait for the training to finish, which should take a very short - * time. If the training does not finish, we have a problem and we - * need to inspect the DEBUG registers. If the training does finish, - * the link is up and operating correctly. - * 3) The link is UP and no longer in LTSSM training - * The link is up and operating correctly. - */ - while (1) { - rc = readl(pp->dbi_base + PCIE_PHY_DEBUG_R1); - if (!(rc & PCIE_PHY_DEBUG_R1_XMLH_LINK_UP)) - break; - if (!(rc & PCIE_PHY_DEBUG_R1_XMLH_LINK_IN_TRAINING)) - return 1; - if (!count--) - break; - dev_dbg(pp->dev, "Link is up, but still in training\n"); - /* - * Wait a little bit, then re-check if the link finished - * the training. - */ - udelay(1000); - } + imx6_pcie_establish_link(imx6_pcie); return 0; } -static struct pcie_host_ops imx6_pcie_host_ops = { - .link_up = imx6_pcie_link_up, +static int imx6_pcie_link_up(struct dw_pcie *pci) +{ + return dw_pcie_readl_dbi(pci, PCIE_PHY_DEBUG_R1) & + PCIE_PHY_DEBUG_R1_XMLH_LINK_UP; +} + +static const struct dw_pcie_ops dw_pcie_ops = { + .link_up = imx6_pcie_link_up, +}; + +static const struct dw_pcie_host_ops imx6_pcie_host_ops = { .host_init = imx6_pcie_host_init, }; -static int __init imx6_add_pcie_port(struct pcie_port *pp, - struct device_d *dev) +static int __init imx6_add_pcie_port(struct imx6_pcie *imx6_pcie, + struct device_d *dev) { + struct dw_pcie *pci = imx6_pcie->pci; + struct pcie_port *pp = &pci->pp; int ret; pp->root_bus_nr = -1; @@ -574,23 +527,25 @@ static int __init imx6_pcie_probe(struct device_d *dev) { struct resource *iores; + struct dw_pcie *pci; struct imx6_pcie *imx6_pcie; - struct pcie_port *pp; struct device_node *np = dev->device_node; int ret; imx6_pcie = xzalloc(sizeof(*imx6_pcie)); - pp = &imx6_pcie->pp; - pp->dev = dev; + pci = xzalloc(sizeof(*pci)); + pci->dev = dev; + pci->ops = &dw_pcie_ops; + imx6_pcie->pci = pci; imx6_pcie->variant = (enum imx6_pcie_variants)of_device_get_match_data(dev); iores = dev_request_mem_resource(dev, 0); if (IS_ERR(iores)) return PTR_ERR(iores); - pp->dbi_base = IOMEM(iores->start); + pci->dbi_base = IOMEM(iores->start); /* Fetch GPIOs */ imx6_pcie->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0); @@ -646,12 +601,18 @@ &imx6_pcie->tx_swing_low)) imx6_pcie->tx_swing_low = 127; - ret = imx6_add_pcie_port(pp, dev); + /* Limit link speed */ + ret = of_property_read_u32(np, "fsl,max-link-speed", + &imx6_pcie->link_gen); + if (ret) + imx6_pcie->link_gen = 1; + + dev->priv = imx6_pcie; + + ret = imx6_add_pcie_port(imx6_pcie, dev); if (ret < 0) return ret; - dev->priv = imx6_pcie; - return 0; } @@ -659,7 +620,34 @@ { struct imx6_pcie *imx6_pcie = dev->priv; - imx6_pcie_assert_core_reset(&imx6_pcie->pp); + if (imx6_pcie->variant == IMX6Q) { + /* + * If the bootloader already enabled the link we need + * some special handling to get the core back into a + * state where it is safe to touch it for + * configuration. As there is no dedicated reset + * signal wired up for MX6QDL, we need to manually + * force LTSSM into "detect" state before completely + * disabling LTSSM, which is a prerequisite for core + * configuration. + */ + struct dw_pcie *pci = imx6_pcie->pci; + u32 gpr12, val; + + val = dw_pcie_readl_dbi(pci, PCIE_PL_PFLR); + val &= ~PCIE_PL_PFLR_LINK_STATE_MASK; + val |= PCIE_PL_PFLR_FORCE_LINK; + + data_abort_mask(); + dw_pcie_writel_dbi(pci, PCIE_PL_PFLR, val); + data_abort_unmask(); + + gpr12 = readl(imx6_pcie->iomuxc_gpr + IOMUXC_GPR12); + gpr12 &= ~IMX6Q_GPR12_PCIE_CTL_2; + writel(gpr12, imx6_pcie->iomuxc_gpr + IOMUXC_GPR12); + } + + imx6_pcie_assert_core_reset(imx6_pcie); } static struct of_device_id imx6_pcie_of_match[] = { diff --git a/drivers/pci/pci-mvebu-phy.c b/drivers/pci/pci-mvebu-phy.c index 55a1d39..f1bfc99 100644 --- a/drivers/pci/pci-mvebu-phy.c +++ b/drivers/pci/pci-mvebu-phy.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 /* * SoC specific PCIe PHY setup for Marvell MVEBU SoCs * * Sebastian Hesselbarth * * based on Marvell BSP code (C) Marvell International Ltd. - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include diff --git a/drivers/pci/pci-mvebu.c b/drivers/pci/pci-mvebu.c index 91e8ca8..1c20f91 100644 --- a/drivers/pci/pci-mvebu.c +++ b/drivers/pci/pci-mvebu.c @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 /* * PCIe driver for Marvell MVEBU SoCs * * Based on Linux drivers/pci/host/pci-mvebu.c - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include diff --git a/drivers/pci/pci-mvebu.h b/drivers/pci/pci-mvebu.h index 8ced9fe..2797bc4 100644 --- a/drivers/pci/pci-mvebu.h +++ b/drivers/pci/pci-mvebu.h @@ -1,11 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * PCIe include for Marvell MVEBU SoCs * * Sebastian Hesselbarth - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #ifndef __MVEBU_PCI_H diff --git a/drivers/pci/pci-tegra.c b/drivers/pci/pci-tegra.c index b532c46..b6ccf8e 100644 --- a/drivers/pci/pci-tegra.c +++ b/drivers/pci/pci-tegra.c @@ -1,21 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2014 Lucas Stach * * based on code * Copyright (c) 2010, CompuLab, Ltd. * Copyright (c) 2008-2009, NVIDIA Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . */ #include diff --git a/drivers/pci/pcie-designware-host.c b/drivers/pci/pcie-designware-host.c new file mode 100644 index 0000000..6cc4b93 --- /dev/null +++ b/drivers/pci/pcie-designware-host.c @@ -0,0 +1,416 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Synopsys DesignWare PCIe host controller driver + * + * Copyright (C) 2013 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Jingoo Han + */ + + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pcie-designware.h" + +#include + +static struct pci_ops dw_pcie_ops; +static unsigned long global_io_offset; + +static int dw_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, + u32 *val) +{ + struct dw_pcie *pci; + + if (pp->ops->rd_own_conf) + return pp->ops->rd_own_conf(pp, where, size, val); + + pci = to_dw_pcie_from_pp(pp); + return dw_pcie_read(pci->dbi_base + where, size, val); +} + +static int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size, + u32 val) +{ + struct dw_pcie *pci; + + if (pp->ops->wr_own_conf) + return pp->ops->wr_own_conf(pp, where, size, val); + + pci = to_dw_pcie_from_pp(pp); + return dw_pcie_write(pci->dbi_base + where, size, val); +} + +static inline struct pcie_port *host_to_pcie(struct pci_controller *host) +{ + return container_of(host, struct pcie_port, pci); +} + +static void dw_pcie_set_local_bus_nr(struct pci_controller *host, int busno) +{ + struct pcie_port *pp = host_to_pcie(host); + + pp->root_bus_nr = busno; +} + +int __init dw_pcie_host_init(struct pcie_port *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct device_d *dev = pci->dev; + struct device_node *np = dev->device_node; + struct of_pci_range range; + struct of_pci_range_parser parser; + struct resource *cfg_res; + u32 na, ns; + const __be32 *addrp; + int index, ret; + + /* Find the address cell size and the number of cells in order to get + * the untranslated address. + */ + of_property_read_u32(np, "#address-cells", &na); + ns = of_n_size_cells(np); + + cfg_res = dev_get_resource_by_name(dev, IORESOURCE_MEM, "config"); + if (cfg_res) { + pp->cfg0_size = resource_size(cfg_res) >> 1; + pp->cfg1_size = resource_size(cfg_res) >> 1; + pp->cfg0_base = cfg_res->start; + pp->cfg1_base = cfg_res->start + pp->cfg0_size; + + /* Find the untranslated configuration space address */ + index = of_property_match_string(np, "reg-names", "config"); + addrp = of_get_address(np, index, NULL, NULL); + pp->cfg0_mod_base = of_read_number(addrp, ns); + pp->cfg1_mod_base = pp->cfg0_mod_base + pp->cfg0_size; + } else { + dev_err(dev, "Missing *config* reg space\n"); + } + + if (of_pci_range_parser_init(&parser, np)) { + dev_err(dev, "Missing ranges property\n"); + return -EINVAL; + } + + /* Get the I/O and memory ranges from DT */ + for_each_of_pci_range(&parser, &range) { + unsigned long restype = range.flags & IORESOURCE_TYPE_BITS; + + if (restype == IORESOURCE_IO) { + of_pci_range_to_resource(&range, np, &pp->io); + pp->io.name = "I/O"; + pp->io.start = range.pci_addr + global_io_offset; + pp->io.end = range.pci_addr + range.size + global_io_offset - 1; + pp->io_size = resource_size(&pp->io); + pp->io_bus_addr = range.pci_addr; + pp->io_base = range.cpu_addr; + + /* Find the untranslated IO space address */ + pp->io_mod_base = of_read_number(parser.range - + parser.np + na, ns); + } + if (restype == IORESOURCE_MEM) { + of_pci_range_to_resource(&range, np, &pp->mem); + pp->mem.name = "MEM"; + pp->mem_size = resource_size(&pp->mem); + pp->mem_bus_addr = range.pci_addr; + + /* Find the untranslated MEM space address */ + pp->mem_mod_base = of_read_number(parser.range - + parser.np + na, ns); + } + if (restype == 0) { + of_pci_range_to_resource(&range, np, &pp->cfg); + pp->cfg0_size = resource_size(&pp->cfg) >> 1; + pp->cfg1_size = resource_size(&pp->cfg) >> 1; + pp->cfg0_base = pp->cfg.start; + pp->cfg1_base = pp->cfg.start + pp->cfg0_size; + + /* Find the untranslated configuration space address */ + pp->cfg0_mod_base = of_read_number(parser.range - + parser.np + na, ns); + pp->cfg1_mod_base = pp->cfg0_mod_base + + pp->cfg0_size; + } + } + + if (!pci->dbi_base) + pci->dbi_base = (void __force *)pp->cfg.start; + + pp->mem_base = pp->mem.start; + + if (!pp->va_cfg0_base) + pp->va_cfg0_base = (void __force *)(u32)pp->cfg0_base; + + if (!pp->va_cfg1_base) + pp->va_cfg1_base = (void __force *)(u32)pp->cfg1_base; + + ret = of_property_read_u32(np, "num-viewport", &pci->num_viewport); + if (ret) + pci->num_viewport = 2; + + if (pp->ops->host_init) { + ret = pp->ops->host_init(pp); + if (ret) + return ret; + } + + pp->pci.parent = dev; + pp->pci.pci_ops = &dw_pcie_ops; + pp->pci.set_busno = dw_pcie_set_local_bus_nr; + pp->pci.mem_resource = &pp->mem; + pp->pci.io_resource = &pp->io; + + register_pci_controller(&pp->pci); + + return 0; +} + +static int dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus, + u32 devfn, int where, int size, u32 *val) +{ + int ret, type; + u32 address, busdev, cfg_size; + u64 cpu_addr; + void __iomem *va_cfg_base; + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + + if (pp->ops->rd_other_conf) + return pp->ops->rd_other_conf(pp, bus, devfn, + where, size, val); + + busdev = PCIE_ATU_BUS(bus->number) | PCIE_ATU_DEV(PCI_SLOT(devfn)) | + PCIE_ATU_FUNC(PCI_FUNC(devfn)); + address = where & ~0x3; + + if (bus->primary == pp->root_bus_nr) { + type = PCIE_ATU_TYPE_CFG0; + cpu_addr = pp->cfg0_mod_base; + cfg_size = pp->cfg0_size; + va_cfg_base = pp->va_cfg0_base; + } else { + type = PCIE_ATU_TYPE_CFG1; + cpu_addr = pp->cfg1_mod_base; + cfg_size = pp->cfg1_size; + va_cfg_base = pp->va_cfg1_base; + } + + dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX1, + type, cpu_addr, + busdev, cfg_size); + ret = dw_pcie_read(va_cfg_base + where, size, val); + if (pci->num_viewport <= 2) + dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX1, + PCIE_ATU_TYPE_IO, pp->io_mod_base, + pp->io_bus_addr, pp->io_size); + + return ret; +} + +static int dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus, + u32 devfn, int where, int size, u32 val) +{ + int ret, type; + u32 busdev, cfg_size; + u64 cpu_addr; + void __iomem *va_cfg_base; + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + + if (pp->ops->wr_other_conf) + return pp->ops->wr_other_conf(pp, bus, devfn, + where, size, val); + + busdev = PCIE_ATU_BUS(bus->number) | PCIE_ATU_DEV(PCI_SLOT(devfn)) | + PCIE_ATU_FUNC(PCI_FUNC(devfn)); + + if (bus->primary == pp->root_bus_nr) { + type = PCIE_ATU_TYPE_CFG0; + cpu_addr = pp->cfg0_mod_base; + cfg_size = pp->cfg0_size; + va_cfg_base = pp->va_cfg0_base; + } else { + type = PCIE_ATU_TYPE_CFG1; + cpu_addr = pp->cfg1_mod_base; + cfg_size = pp->cfg1_size; + va_cfg_base = pp->va_cfg1_base; + } + + dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX1, + type, cpu_addr, + busdev, cfg_size); + ret = dw_pcie_write(va_cfg_base + where, size, val); + if (pci->num_viewport <= 2) + dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX1, + PCIE_ATU_TYPE_IO, pp->io_mod_base, + pp->io_bus_addr, pp->io_size); + + return ret; +} + +static int dw_pcie_valid_device(struct pcie_port *pp, struct pci_bus *bus, + int dev) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + + /* If there is no link, then there is no device */ + if (bus->number != pp->root_bus_nr) { + if (!dw_pcie_link_up(pci)) + return 0; + } + + /* Access only one slot on each root port */ + if (bus->number == pp->root_bus_nr && dev > 0) + return 0; + + /* + * Do not read more than one device on the bus directly attached + * to RC's (Virtual Bridge's) DS side. + */ + if (bus->primary == pp->root_bus_nr && dev > 0) + return 0; + + return 1; +} + +static int dw_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, + int size, u32 *val) +{ + struct pcie_port *pp = host_to_pcie(bus->host); + int ret; + + *val = 0xffffffff; + + if (!dw_pcie_valid_device(pp, bus, PCI_SLOT(devfn))) + return PCIBIOS_DEVICE_NOT_FOUND; + + data_abort_mask(); + + if (bus->number == pp->root_bus_nr) + ret = dw_pcie_rd_own_conf(pp, where, size, val); + else + ret = dw_pcie_rd_other_conf(pp, bus, devfn, + where, size, val); + + if (data_abort_unmask()) + return PCIBIOS_DEVICE_NOT_FOUND; + + return ret; +} + +static int dw_pcie_wr_conf(struct pci_bus *bus, u32 devfn, + int where, int size, u32 val) +{ + struct pcie_port *pp = host_to_pcie(bus->host); + int ret; + + if (!dw_pcie_valid_device(pp, bus, PCI_SLOT(devfn))) + return PCIBIOS_DEVICE_NOT_FOUND; + + data_abort_mask(); + + if (bus->number == pp->root_bus_nr) + ret = dw_pcie_wr_own_conf(pp, where, size, val); + else + ret = dw_pcie_wr_other_conf(pp, bus, devfn, + where, size, val); + + if (data_abort_unmask()) + return PCIBIOS_DEVICE_NOT_FOUND; + + return ret; +} + +static int dw_pcie_res_start(struct pci_bus *bus, resource_size_t res_addr) +{ + return res_addr; +} + +static struct pci_ops dw_pcie_ops = { + .read = dw_pcie_rd_conf, + .write = dw_pcie_wr_conf, + .res_start = dw_pcie_res_start, +}; + +static u8 dw_pcie_iatu_unroll_enabled(struct dw_pcie *pci) +{ + u32 val; + + val = dw_pcie_readl_dbi(pci, PCIE_ATU_VIEWPORT); + if (val == 0xffffffff) + return 1; + + return 0; +} + +void dw_pcie_setup_rc(struct pcie_port *pp) +{ + u32 val; + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + + dw_pcie_setup(pci); + + /* Setup RC BARs */ + dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, 0x00000004); + dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_1, 0x00000000); + + /* Setup bus numbers */ + val = dw_pcie_readl_dbi(pci, PCI_PRIMARY_BUS); + val &= 0xff000000; + val |= 0x00ff0100; + dw_pcie_writel_dbi(pci, PCI_PRIMARY_BUS, val); + + /* Setup command register */ + val = dw_pcie_readl_dbi(pci, PCI_COMMAND); + val &= 0xffff0000; + val |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY | + PCI_COMMAND_MASTER | PCI_COMMAND_SERR; + dw_pcie_writel_dbi(pci, PCI_COMMAND, val); + + /* + * If the platform provides ->rd_other_conf, it means the platform + * uses its own address translation component rather than ATU, so + * we should not program the ATU here. + */ + if (!pp->ops->rd_other_conf) { + /* get iATU unroll support */ + pci->iatu_unroll_enabled = dw_pcie_iatu_unroll_enabled(pci); + dev_dbg(pci->dev, "iATU unroll: %s\n", + pci->iatu_unroll_enabled ? "enabled" : "disabled"); + + dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX0, + PCIE_ATU_TYPE_MEM, pp->mem_mod_base, + pp->mem_bus_addr, pp->mem_size); + if (pci->num_viewport > 2) + dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX2, + PCIE_ATU_TYPE_IO, pp->io_base, + pp->io_bus_addr, pp->io_size); + } + + 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); +} \ No newline at end of file diff --git a/drivers/pci/pcie-designware.c b/drivers/pci/pcie-designware.c index 4962a19..aaea316 100644 --- a/drivers/pci/pcie-designware.c +++ b/drivers/pci/pcie-designware.c @@ -1,14 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0 /* - * Synopsys Designware PCIe host controller driver + * Synopsys DesignWare PCIe host controller driver * * Copyright (C) 2013 Samsung Electronics Co., Ltd. * http://www.samsung.com * * Author: Jingoo Han - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ @@ -30,476 +27,213 @@ #include "pcie-designware.h" -/* Synopsis specific PCIE configuration registers */ -#define PCIE_PORT_LINK_CONTROL 0x710 -#define PORT_LINK_MODE_MASK (0x3f << 16) -#define PORT_LINK_MODE_1_LANES (0x1 << 16) -#define PORT_LINK_MODE_2_LANES (0x3 << 16) -#define PORT_LINK_MODE_4_LANES (0x7 << 16) - -#define PCIE_LINK_WIDTH_SPEED_CONTROL 0x80C -#define PORT_LOGIC_SPEED_CHANGE (0x1 << 17) -#define PORT_LOGIC_LINK_WIDTH_MASK (0x1ff << 8) -#define PORT_LOGIC_LINK_WIDTH_1_LANES (0x1 << 8) -#define PORT_LOGIC_LINK_WIDTH_2_LANES (0x2 << 8) -#define PORT_LOGIC_LINK_WIDTH_4_LANES (0x4 << 8) - -#define PCIE_MSI_ADDR_LO 0x820 -#define PCIE_MSI_ADDR_HI 0x824 -#define PCIE_MSI_INTR0_ENABLE 0x828 -#define PCIE_MSI_INTR0_MASK 0x82C -#define PCIE_MSI_INTR0_STATUS 0x830 - -#define PCIE_ATU_VIEWPORT 0x900 -#define PCIE_ATU_REGION_INBOUND (0x1 << 31) -#define PCIE_ATU_REGION_OUTBOUND (0x0 << 31) -#define PCIE_ATU_REGION_INDEX1 (0x1 << 0) -#define PCIE_ATU_REGION_INDEX0 (0x0 << 0) -#define PCIE_ATU_CR1 0x904 -#define PCIE_ATU_TYPE_MEM (0x0 << 0) -#define PCIE_ATU_TYPE_IO (0x2 << 0) -#define PCIE_ATU_TYPE_CFG0 (0x4 << 0) -#define PCIE_ATU_TYPE_CFG1 (0x5 << 0) -#define PCIE_ATU_CR2 0x908 -#define PCIE_ATU_ENABLE (0x1 << 31) -#define PCIE_ATU_BAR_MODE_ENABLE (0x1 << 30) -#define PCIE_ATU_LOWER_BASE 0x90C -#define PCIE_ATU_UPPER_BASE 0x910 -#define PCIE_ATU_LIMIT 0x914 -#define PCIE_ATU_LOWER_TARGET 0x918 -#define PCIE_ATU_BUS(x) (((x) & 0xff) << 24) -#define PCIE_ATU_DEV(x) (((x) & 0x1f) << 19) -#define PCIE_ATU_FUNC(x) (((x) & 0x7) << 16) -#define PCIE_ATU_UPPER_TARGET 0x91C - -static unsigned long global_io_offset; - -int dw_pcie_cfg_read(void __iomem *addr, int where, int size, u32 *val) +int dw_pcie_read(void __iomem *addr, int size, u32 *val) { - *val = readl(addr); - - if (size == 1) - *val = (*val >> (8 * (where & 3))) & 0xff; - else if (size == 2) - *val = (*val >> (8 * (where & 3))) & 0xffff; - else if (size != 4) + if ((uintptr_t)addr & (size - 1)) { + *val = 0; return PCIBIOS_BAD_REGISTER_NUMBER; + } + + if (size == 4) { + *val = readl(addr); + } else if (size == 2) { + *val = readw(addr); + } else if (size == 1) { + *val = readb(addr); + } else { + *val = 0; + return PCIBIOS_BAD_REGISTER_NUMBER; + } return PCIBIOS_SUCCESSFUL; } -int dw_pcie_cfg_write(void __iomem *addr, int where, int size, u32 val) +int dw_pcie_write(void __iomem *addr, int size, u32 val) { + if ((uintptr_t)addr & (size - 1)) + return PCIBIOS_BAD_REGISTER_NUMBER; + if (size == 4) writel(val, addr); else if (size == 2) - writew(val, addr + (where & 2)); + writew(val, addr); else if (size == 1) - writeb(val, addr + (where & 3)); + writeb(val, addr); else return PCIBIOS_BAD_REGISTER_NUMBER; return PCIBIOS_SUCCESSFUL; } -static inline void dw_pcie_readl_rc(struct pcie_port *pp, u32 reg, u32 *val) +u32 __dw_pcie_readl_dbi(struct dw_pcie *pci, void __iomem *base, u32 reg, + size_t size) { - if (pp->ops->readl_rc) - pp->ops->readl_rc(pp, pp->dbi_base + reg, val); - else - *val = readl(pp->dbi_base + reg); + int ret; + u32 val; + + if (pci->ops->readl_dbi) + return pci->ops->readl_dbi(pci, base, reg, size); + + ret = dw_pcie_read(base + reg, size, &val); + if (ret) + dev_err(pci->dev, "Read DBI address failed\n"); + + return val; } -static inline void dw_pcie_writel_rc(struct pcie_port *pp, u32 val, u32 reg) -{ - if (pp->ops->writel_rc) - pp->ops->writel_rc(pp, val, pp->dbi_base + reg); - else - writel(val, pp->dbi_base + reg); -} - -#include - -static int dw_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, - u32 *val) +void __dw_pcie_writel_dbi(struct dw_pcie *pci, void __iomem *base, u32 reg, + size_t size, u32 val) { int ret; - if (pp->ops->rd_own_conf) - ret = pp->ops->rd_own_conf(pp, where, size, val); - else - ret = dw_pcie_cfg_read(pp->dbi_base + (where & ~0x3), where, - size, val); - - return ret; -} - -static int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size, - u32 val) -{ - int ret; - - if (pp->ops->wr_own_conf) - ret = pp->ops->wr_own_conf(pp, where, size, val); - else - ret = dw_pcie_cfg_write(pp->dbi_base + (where & ~0x3), where, - size, val); - - return ret; -} - -int dw_pcie_link_up(struct pcie_port *pp) -{ - if (pp->ops->link_up) - return pp->ops->link_up(pp); - else - return 0; -} - -static inline struct pcie_port *host_to_pcie(struct pci_controller *host) -{ - return container_of(host, struct pcie_port, pci); -} - -static void dw_pcie_set_local_bus_nr(struct pci_controller *host, int busno) -{ - struct pcie_port *pp = host_to_pcie(host); - - pp->root_bus_nr = busno; -} - -static struct pci_ops dw_pcie_ops; - -int __init dw_pcie_host_init(struct pcie_port *pp) -{ - struct device_node *np = pp->dev->device_node; - struct of_pci_range range; - struct of_pci_range_parser parser; - struct resource *cfg_res; - u32 val, na, ns; - const __be32 *addrp; - int index; - - /* Find the address cell size and the number of cells in order to get - * the untranslated address. - */ - of_property_read_u32(np, "#address-cells", &na); - ns = of_n_size_cells(np); - - cfg_res = dev_get_resource_by_name(pp->dev, IORESOURCE_MEM, "config"); - if (cfg_res) { - pp->cfg0_size = resource_size(cfg_res)/2; - pp->cfg1_size = resource_size(cfg_res)/2; - pp->cfg0_base = cfg_res->start; - pp->cfg1_base = cfg_res->start + pp->cfg0_size; - - /* Find the untranslated configuration space address */ - index = of_property_match_string(np, "reg-names", "config"); - addrp = of_get_address(np, index, NULL, NULL); - pp->cfg0_mod_base = of_read_number(addrp, ns); - pp->cfg1_mod_base = pp->cfg0_mod_base + pp->cfg0_size; - } else { - dev_err(pp->dev, "missing *config* reg space\n"); + if (pci->ops->writel_dbi) { + pci->ops->writel_dbi(pci, base, reg, size, val); + return; } - if (of_pci_range_parser_init(&parser, np)) { - dev_err(pp->dev, "missing ranges property\n"); - return -EINVAL; - } - - /* Get the I/O and memory ranges from DT */ - for_each_of_pci_range(&parser, &range) { - unsigned long restype = range.flags & IORESOURCE_TYPE_BITS; - - if (restype == IORESOURCE_IO) { - of_pci_range_to_resource(&range, np, &pp->io); - pp->io.name = "I/O"; - pp->io.start = range.pci_addr + global_io_offset; - pp->io.end = range.pci_addr + range.size + global_io_offset - 1; - pp->io_size = resource_size(&pp->io); - pp->io_bus_addr = range.pci_addr; - pp->io_base = range.cpu_addr; - - /* Find the untranslated IO space address */ - pp->io_mod_base = of_read_number(parser.range - - parser.np + na, ns); - } - if (restype == IORESOURCE_MEM) { - of_pci_range_to_resource(&range, np, &pp->mem); - pp->mem.name = "MEM"; - pp->mem_size = resource_size(&pp->mem); - pp->mem_bus_addr = range.pci_addr; - - /* Find the untranslated MEM space address */ - pp->mem_mod_base = of_read_number(parser.range - - parser.np + na, ns); - } - if (restype == 0) { - of_pci_range_to_resource(&range, np, &pp->cfg); - pp->cfg0_size = resource_size(&pp->cfg)/2; - pp->cfg1_size = resource_size(&pp->cfg)/2; - pp->cfg0_base = pp->cfg.start; - pp->cfg1_base = pp->cfg.start + pp->cfg0_size; - - /* Find the untranslated configuration space address */ - pp->cfg0_mod_base = of_read_number(parser.range - - parser.np + na, ns); - pp->cfg1_mod_base = pp->cfg0_mod_base + - pp->cfg0_size; - } - } - - if (!pp->dbi_base) - pp->dbi_base = (void __force *)pp->cfg.start; - - pp->mem_base = pp->mem.start; - - if (!pp->va_cfg0_base) - pp->va_cfg0_base = (void __force *)(u32)pp->cfg0_base; - - if (!pp->va_cfg1_base) - pp->va_cfg1_base = (void __force *)(u32)pp->cfg1_base; - - if (of_property_read_u32(np, "num-lanes", &pp->lanes)) { - dev_err(pp->dev, "Failed to parse the number of lanes\n"); - return -EINVAL; - } - - if (pp->ops->host_init) - pp->ops->host_init(pp); - - dw_pcie_wr_own_conf(pp, PCI_BASE_ADDRESS_0, 4, 0); - - /* program correct class for RC */ - dw_pcie_wr_own_conf(pp, PCI_CLASS_DEVICE, 2, PCI_CLASS_BRIDGE_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); - - pp->pci.parent = pp->dev; - pp->pci.pci_ops = &dw_pcie_ops; - pp->pci.set_busno = dw_pcie_set_local_bus_nr; - pp->pci.mem_resource = &pp->mem; - pp->pci.io_resource = &pp->io; - - register_pci_controller(&pp->pci); - - return 0; + ret = dw_pcie_write(base + reg, size, val); + if (ret) + dev_err(pci->dev, "Write DBI address failed\n"); } -static void dw_pcie_prog_viewport_cfg0(struct pcie_port *pp, u32 busdev) +static u32 dw_pcie_readl_ob_unroll(struct dw_pcie *pci, u32 index, u32 reg) { - /* Program viewport 0 : OUTBOUND : CFG0 */ - dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX0, - PCIE_ATU_VIEWPORT); - dw_pcie_writel_rc(pp, pp->cfg0_mod_base, PCIE_ATU_LOWER_BASE); - dw_pcie_writel_rc(pp, (pp->cfg0_mod_base >> 32), PCIE_ATU_UPPER_BASE); - dw_pcie_writel_rc(pp, pp->cfg0_mod_base + pp->cfg0_size - 1, - PCIE_ATU_LIMIT); - dw_pcie_writel_rc(pp, busdev, PCIE_ATU_LOWER_TARGET); - dw_pcie_writel_rc(pp, 0, PCIE_ATU_UPPER_TARGET); - dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_CFG0, PCIE_ATU_CR1); - dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2); + u32 offset = PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index); + + return dw_pcie_readl_dbi(pci, offset + reg); } -static void dw_pcie_prog_viewport_cfg1(struct pcie_port *pp, u32 busdev) +static void dw_pcie_writel_ob_unroll(struct dw_pcie *pci, u32 index, + u32 reg, u32 val) { - /* Program viewport 1 : OUTBOUND : CFG1 */ - dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX1, - PCIE_ATU_VIEWPORT); - dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_CFG1, PCIE_ATU_CR1); - dw_pcie_writel_rc(pp, pp->cfg1_mod_base, PCIE_ATU_LOWER_BASE); - dw_pcie_writel_rc(pp, (pp->cfg1_mod_base >> 32), PCIE_ATU_UPPER_BASE); - dw_pcie_writel_rc(pp, pp->cfg1_mod_base + pp->cfg1_size - 1, - PCIE_ATU_LIMIT); - dw_pcie_writel_rc(pp, busdev, PCIE_ATU_LOWER_TARGET); - dw_pcie_writel_rc(pp, 0, PCIE_ATU_UPPER_TARGET); - dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2); + u32 offset = PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index); + + dw_pcie_writel_dbi(pci, offset + reg, val); } -static void dw_pcie_prog_viewport_mem_outbound(struct pcie_port *pp) +static void dw_pcie_prog_outbound_atu_unroll(struct dw_pcie *pci, int index, + int type, u64 cpu_addr, + u64 pci_addr, u32 size) { - /* Program viewport 0 : OUTBOUND : MEM */ - dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX0, - PCIE_ATU_VIEWPORT); - dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_MEM, PCIE_ATU_CR1); - dw_pcie_writel_rc(pp, pp->mem_mod_base, PCIE_ATU_LOWER_BASE); - dw_pcie_writel_rc(pp, (pp->mem_mod_base >> 32), PCIE_ATU_UPPER_BASE); - dw_pcie_writel_rc(pp, pp->mem_mod_base + pp->mem_size - 1, - PCIE_ATU_LIMIT); - dw_pcie_writel_rc(pp, pp->mem_bus_addr, PCIE_ATU_LOWER_TARGET); - dw_pcie_writel_rc(pp, upper_32_bits(pp->mem_bus_addr), - PCIE_ATU_UPPER_TARGET); - dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2); -} + u32 retries, val; -static void dw_pcie_prog_viewport_io_outbound(struct pcie_port *pp) -{ - /* Program viewport 1 : OUTBOUND : IO */ - dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX1, - PCIE_ATU_VIEWPORT); - dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_IO, PCIE_ATU_CR1); - dw_pcie_writel_rc(pp, pp->io_mod_base, PCIE_ATU_LOWER_BASE); - dw_pcie_writel_rc(pp, (pp->io_mod_base >> 32), PCIE_ATU_UPPER_BASE); - dw_pcie_writel_rc(pp, pp->io_mod_base + pp->io_size - 1, - PCIE_ATU_LIMIT); - dw_pcie_writel_rc(pp, pp->io_bus_addr, PCIE_ATU_LOWER_TARGET); - dw_pcie_writel_rc(pp, upper_32_bits(pp->io_bus_addr), - PCIE_ATU_UPPER_TARGET); - dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2); -} - -static int dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus, - u32 devfn, int where, int size, u32 *val) -{ - int ret = PCIBIOS_SUCCESSFUL; - u32 address, busdev; - - busdev = PCIE_ATU_BUS(bus->number) | PCIE_ATU_DEV(PCI_SLOT(devfn)) | - PCIE_ATU_FUNC(PCI_FUNC(devfn)); - address = where & ~0x3; - - if (bus->primary == pp->root_bus_nr) { - dw_pcie_prog_viewport_cfg0(pp, busdev); - ret = dw_pcie_cfg_read(pp->va_cfg0_base + address, where, size, - val); - dw_pcie_prog_viewport_mem_outbound(pp); - } else { - dw_pcie_prog_viewport_cfg1(pp, busdev); - ret = dw_pcie_cfg_read(pp->va_cfg1_base + address, where, size, - val); - dw_pcie_prog_viewport_io_outbound(pp); - } - - return ret; -} - -static int dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus, - u32 devfn, int where, int size, u32 val) -{ - int ret = PCIBIOS_SUCCESSFUL; - u32 address, busdev; - - busdev = PCIE_ATU_BUS(bus->number) | PCIE_ATU_DEV(PCI_SLOT(devfn)) | - PCIE_ATU_FUNC(PCI_FUNC(devfn)); - address = where & ~0x3; - - if (bus->primary == pp->root_bus_nr) { - dw_pcie_prog_viewport_cfg0(pp, busdev); - ret = dw_pcie_cfg_write(pp->va_cfg0_base + address, where, size, - val); - dw_pcie_prog_viewport_mem_outbound(pp); - } else { - dw_pcie_prog_viewport_cfg1(pp, busdev); - ret = dw_pcie_cfg_write(pp->va_cfg1_base + address, where, size, - val); - dw_pcie_prog_viewport_io_outbound(pp); - } - - return ret; -} - -static int dw_pcie_valid_config(struct pcie_port *pp, - struct pci_bus *bus, int dev) -{ - /* If there is no link, then there is no device */ - if (bus->number != pp->root_bus_nr) { - if (!dw_pcie_link_up(pp)) - return 0; - } - - /* access only one slot on each root port */ - if (bus->number == pp->root_bus_nr && dev > 0) - return 0; + dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_LOWER_BASE, + lower_32_bits(cpu_addr)); + dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_UPPER_BASE, + upper_32_bits(cpu_addr)); + dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_LIMIT, + lower_32_bits(cpu_addr + size - 1)); + dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_LOWER_TARGET, + lower_32_bits(pci_addr)); + dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_UPPER_TARGET, + upper_32_bits(pci_addr)); + dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL1, + type); + dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL2, + PCIE_ATU_ENABLE); /* - * do not read more than one device on the bus directly attached - * to RC's (Virtual Bridge's) DS side. + * Make sure ATU enable takes effect before any subsequent config + * and I/O accesses. */ - if (bus->primary == pp->root_bus_nr && dev > 0) - return 0; + for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) { + val = dw_pcie_readl_ob_unroll(pci, index, + PCIE_ATU_UNR_REGION_CTRL2); + if (val & PCIE_ATU_ENABLE) + return; - return 1; + udelay(LINK_WAIT_IATU_MAX); + } + dev_err(pci->dev, "Outbound iATU is not being enabled\n"); } -static int dw_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, - int size, u32 *val) +void dw_pcie_prog_outbound_atu(struct dw_pcie *pci, int index, + int type, u64 cpu_addr, u64 pci_addr, + u32 size) { - struct pcie_port *pp = host_to_pcie(bus->host); - int ret; + u32 retries, val; - *val = 0xffffffff; + if (pci->iatu_unroll_enabled) { + dw_pcie_prog_outbound_atu_unroll(pci, index, type, cpu_addr, + pci_addr, size); + return; + } - if (dw_pcie_valid_config(pp, bus, PCI_SLOT(devfn)) == 0) - return PCIBIOS_DEVICE_NOT_FOUND; + dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT, + PCIE_ATU_REGION_OUTBOUND | index); + dw_pcie_writel_dbi(pci, PCIE_ATU_LOWER_BASE, + lower_32_bits(cpu_addr)); + dw_pcie_writel_dbi(pci, PCIE_ATU_UPPER_BASE, + upper_32_bits(cpu_addr)); + dw_pcie_writel_dbi(pci, PCIE_ATU_LIMIT, + lower_32_bits(cpu_addr + size - 1)); + dw_pcie_writel_dbi(pci, PCIE_ATU_LOWER_TARGET, + lower_32_bits(pci_addr)); + dw_pcie_writel_dbi(pci, PCIE_ATU_UPPER_TARGET, + upper_32_bits(pci_addr)); + dw_pcie_writel_dbi(pci, PCIE_ATU_CR1, type); + dw_pcie_writel_dbi(pci, PCIE_ATU_CR2, PCIE_ATU_ENABLE); - data_abort_mask(); + /* + * Make sure ATU enable takes effect before any subsequent config + * and I/O accesses. + */ + for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) { + val = dw_pcie_readl_dbi(pci, PCIE_ATU_CR2); + if (val & PCIE_ATU_ENABLE) + return; - if (bus->number != pp->root_bus_nr) - if (pp->ops->rd_other_conf) - ret = pp->ops->rd_other_conf(pp, bus, devfn, - where, size, val); - else - ret = dw_pcie_rd_other_conf(pp, bus, devfn, - where, size, val); - else - ret = dw_pcie_rd_own_conf(pp, where, size, val); - - if (data_abort_unmask()) - return PCIBIOS_DEVICE_NOT_FOUND; - - return ret; + udelay(LINK_WAIT_IATU_MAX); + } + dev_err(pci->dev, "Outbound iATU is not being enabled\n"); } -static int dw_pcie_wr_conf(struct pci_bus *bus, u32 devfn, - int where, int size, u32 val) +int dw_pcie_wait_for_link(struct dw_pcie *pci) { - struct pcie_port *pp = host_to_pcie(bus->host); - int ret; + int retries; - if (dw_pcie_valid_config(pp, bus, PCI_SLOT(devfn)) == 0) - return PCIBIOS_DEVICE_NOT_FOUND; + /* Check if the link is up or not */ + for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) { + if (dw_pcie_link_up(pci)) { + dev_info(pci->dev, "Link up\n"); + return 0; + } + udelay(LINK_WAIT_USLEEP_MAX); + } - data_abort_mask(); + dev_err(pci->dev, "Phy link never came up\n"); - if (bus->number != pp->root_bus_nr) - if (pp->ops->wr_other_conf) - ret = pp->ops->wr_other_conf(pp, bus, devfn, - where, size, val); - else - ret = dw_pcie_wr_other_conf(pp, bus, devfn, - where, size, val); - else - ret = dw_pcie_wr_own_conf(pp, where, size, val); - - if (data_abort_unmask()) - return PCIBIOS_DEVICE_NOT_FOUND; - - return ret; + return -ETIMEDOUT; } -static int dw_pcie_res_start(struct pci_bus *bus, resource_size_t res_addr) -{ - return res_addr; -} - -static struct pci_ops dw_pcie_ops = { - .read = dw_pcie_rd_conf, - .write = dw_pcie_wr_conf, - .res_start = dw_pcie_res_start, -}; - -void dw_pcie_setup_rc(struct pcie_port *pp) +int dw_pcie_link_up(struct dw_pcie *pci) { u32 val; - u32 membase; - u32 memlimit; - /* set the number of lanes */ - dw_pcie_readl_rc(pp, PCIE_PORT_LINK_CONTROL, &val); + if (pci->ops->link_up) + return pci->ops->link_up(pci); + + val = readl(pci->dbi_base + PCIE_PHY_DEBUG_R1); + return ((val & PCIE_PHY_DEBUG_R1_LINK_UP) && + !(val & PCIE_PHY_DEBUG_R1_LINK_IN_TRAINING)); +} + +void dw_pcie_setup(struct dw_pcie *pci) +{ + int ret; + u32 val; + u32 lanes; + struct device_d *dev = pci->dev; + struct device_node *np = dev->device_node; + + ret = of_property_read_u32(np, "num-lanes", &lanes); + if (ret) + lanes = 0; + + /* Set the number of lanes */ + val = dw_pcie_readl_dbi(pci, PCIE_PORT_LINK_CONTROL); val &= ~PORT_LINK_MODE_MASK; - switch (pp->lanes) { + switch (lanes) { case 1: val |= PORT_LINK_MODE_1_LANES; break; @@ -509,13 +243,16 @@ case 4: val |= PORT_LINK_MODE_4_LANES; break; + default: + dev_err(pci->dev, "num-lanes %u: invalid value\n", lanes); + return; } - dw_pcie_writel_rc(pp, val, PCIE_PORT_LINK_CONTROL); + dw_pcie_writel_dbi(pci, PCIE_PORT_LINK_CONTROL, val); - /* set link width speed control register */ - dw_pcie_readl_rc(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, &val); + /* Set link width speed control register */ + val = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL); val &= ~PORT_LOGIC_LINK_WIDTH_MASK; - switch (pp->lanes) { + switch (lanes) { case 1: val |= PORT_LOGIC_LINK_WIDTH_1_LANES; break; @@ -526,30 +263,7 @@ val |= PORT_LOGIC_LINK_WIDTH_4_LANES; break; } - dw_pcie_writel_rc(pp, val, PCIE_LINK_WIDTH_SPEED_CONTROL); - - /* setup RC BARs */ - dw_pcie_writel_rc(pp, 0x00000004, PCI_BASE_ADDRESS_0); - dw_pcie_writel_rc(pp, 0x00000000, PCI_BASE_ADDRESS_1); - - /* setup bus numbers */ - dw_pcie_readl_rc(pp, PCI_PRIMARY_BUS, &val); - val &= 0xff000000; - val |= 0x00010100; - dw_pcie_writel_rc(pp, val, PCI_PRIMARY_BUS); - - /* setup memory base, memory limit */ - membase = ((u32)pp->mem_base & 0xfff00000) >> 16; - memlimit = (pp->mem_size + (u32)pp->mem_base) & 0xfff00000; - val = memlimit | membase; - dw_pcie_writel_rc(pp, val, PCI_MEMORY_BASE); - - /* setup command register */ - dw_pcie_readl_rc(pp, PCI_COMMAND, &val); - val &= 0xffff0000; - val |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY | - PCI_COMMAND_MASTER | PCI_COMMAND_SERR; - dw_pcie_writel_rc(pp, val, PCI_COMMAND); + dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val); } MODULE_AUTHOR("Jingoo Han "); diff --git a/drivers/pci/pcie-designware.h b/drivers/pci/pcie-designware.h index 8d0330a..058a0ac 100644 --- a/drivers/pci/pcie-designware.h +++ b/drivers/pci/pcie-designware.h @@ -1,23 +1,113 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* - * Synopsys Designware PCIe host controller driver + * Synopsys DesignWare PCIe host controller driver * * Copyright (C) 2013 Samsung Electronics Co., Ltd. * http://www.samsung.com * * Author: Jingoo Han - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #ifndef _PCIE_DESIGNWARE_H #define _PCIE_DESIGNWARE_H +/* Parameters for the waiting for link up routine */ +#define LINK_WAIT_MAX_RETRIES 10 +#define LINK_WAIT_USLEEP_MAX 100000 + +/* Parameters for the waiting for iATU enabled routine */ +#define LINK_WAIT_MAX_IATU_RETRIES 5 +#define LINK_WAIT_IATU_MAX 10000 + +/* Synopsis specific PCIE configuration registers */ +#define PCIE_PORT_LINK_CONTROL 0x710 +#define PORT_LINK_MODE_MASK (0x3f << 16) +#define PORT_LINK_MODE_1_LANES (0x1 << 16) +#define PORT_LINK_MODE_2_LANES (0x3 << 16) +#define PORT_LINK_MODE_4_LANES (0x7 << 16) + +#define PCIE_LINK_WIDTH_SPEED_CONTROL 0x80C +#define PORT_LOGIC_SPEED_CHANGE (0x1 << 17) +#define PORT_LOGIC_LINK_WIDTH_MASK (0x1f << 8) +#define PORT_LOGIC_LINK_WIDTH_1_LANES (0x1 << 8) +#define PORT_LOGIC_LINK_WIDTH_2_LANES (0x2 << 8) +#define PORT_LOGIC_LINK_WIDTH_4_LANES (0x4 << 8) + +#define PCIE_MSI_ADDR_LO 0x820 +#define PCIE_MSI_ADDR_HI 0x824 +#define PCIE_MSI_INTR0_ENABLE 0x828 +#define PCIE_MSI_INTR0_MASK 0x82C +#define PCIE_MSI_INTR0_STATUS 0x830 + +#define PCIE_ATU_VIEWPORT 0x900 +#define PCIE_ATU_REGION_INBOUND (0x1 << 31) +#define PCIE_ATU_REGION_OUTBOUND (0x0 << 31) +#define PCIE_ATU_REGION_INDEX2 (0x2 << 0) +#define PCIE_ATU_REGION_INDEX1 (0x1 << 0) +#define PCIE_ATU_REGION_INDEX0 (0x0 << 0) +#define PCIE_ATU_CR1 0x904 +#define PCIE_ATU_TYPE_MEM (0x0 << 0) +#define PCIE_ATU_TYPE_IO (0x2 << 0) +#define PCIE_ATU_TYPE_CFG0 (0x4 << 0) +#define PCIE_ATU_TYPE_CFG1 (0x5 << 0) +#define PCIE_ATU_CR2 0x908 +#define PCIE_ATU_ENABLE (0x1 << 31) +#define PCIE_ATU_BAR_MODE_ENABLE (0x1 << 30) +#define PCIE_ATU_LOWER_BASE 0x90C +#define PCIE_ATU_UPPER_BASE 0x910 +#define PCIE_ATU_LIMIT 0x914 +#define PCIE_ATU_LOWER_TARGET 0x918 +#define PCIE_ATU_BUS(x) (((x) & 0xff) << 24) +#define PCIE_ATU_DEV(x) (((x) & 0x1f) << 19) +#define PCIE_ATU_FUNC(x) (((x) & 0x7) << 16) +#define PCIE_ATU_UPPER_TARGET 0x91C + +/* + * iATU Unroll-specific register definitions + * From 4.80 core version the address translation will be made by unroll + */ +#define PCIE_ATU_UNR_REGION_CTRL1 0x00 +#define PCIE_ATU_UNR_REGION_CTRL2 0x04 +#define PCIE_ATU_UNR_LOWER_BASE 0x08 +#define PCIE_ATU_UNR_UPPER_BASE 0x0C +#define PCIE_ATU_UNR_LIMIT 0x10 +#define PCIE_ATU_UNR_LOWER_TARGET 0x14 +#define PCIE_ATU_UNR_UPPER_TARGET 0x18 + +/* Register address builder */ +#define PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(region) ((0x3 << 20) | (region << 9)) + +/* PCIe Port Logic registers */ +#define PLR_OFFSET 0x700 +#define PCIE_PHY_DEBUG_R1 (PLR_OFFSET + 0x2c) +#define PCIE_PHY_DEBUG_R1_LINK_UP (0x1 << 4) +#define PCIE_PHY_DEBUG_R1_LINK_IN_TRAINING (0x1 << 29) + +#define PCIE_MISC_CONTROL_1_OFF 0x8BC +#define PCIE_DBI_RO_WR_EN (0x1 << 0) + +/* PCIe Port Logic registers */ +#define PLR_OFFSET 0x700 +#define PCIE_PHY_DEBUG_R1 (PLR_OFFSET + 0x2c) +#define PCIE_PHY_DEBUG_R1_LINK_UP (0x1 << 4) +#define PCIE_PHY_DEBUG_R1_LINK_IN_TRAINING (0x1 << 29) + +struct pcie_port; +struct dw_pcie; + +struct dw_pcie_host_ops { + int (*rd_own_conf)(struct pcie_port *pp, int where, int size, u32 *val); + int (*wr_own_conf)(struct pcie_port *pp, int where, int size, u32 val); + int (*rd_other_conf)(struct pcie_port *pp, struct pci_bus *bus, + unsigned int devfn, int where, int size, u32 *val); + int (*wr_other_conf)(struct pcie_port *pp, struct pci_bus *bus, + unsigned int devfn, int where, int size, u32 val); + int (*host_init)(struct pcie_port *pp); + void (*scan_bus)(struct pcie_port *pp); +}; + struct pcie_port { - struct device_d *dev; u8 root_bus_nr; - void __iomem *dbi_base; u64 cfg0_base; u64 cfg0_mod_base; void __iomem *va_cfg0_base; @@ -39,33 +129,75 @@ struct resource mem; struct resource busn; int irq; - u32 lanes; - struct pcie_host_ops *ops; + const struct dw_pcie_host_ops *ops; struct pci_controller pci; }; -struct pcie_host_ops { - void (*readl_rc)(struct pcie_port *pp, - void __iomem *dbi_base, u32 *val); - void (*writel_rc)(struct pcie_port *pp, - u32 val, void __iomem *dbi_base); - int (*rd_own_conf)(struct pcie_port *pp, int where, int size, u32 *val); - int (*wr_own_conf)(struct pcie_port *pp, int where, int size, u32 val); - int (*rd_other_conf)(struct pcie_port *pp, struct pci_bus *bus, - unsigned int devfn, int where, int size, u32 *val); - int (*wr_other_conf)(struct pcie_port *pp, struct pci_bus *bus, - unsigned int devfn, int where, int size, u32 val); - int (*link_up)(struct pcie_port *pp); - void (*host_init)(struct pcie_port *pp); - void (*scan_bus)(struct pcie_port *pp); +struct dw_pcie_ops { + u32 (*readl_dbi)(struct dw_pcie *pcie, void __iomem *base, u32 reg, + size_t size); + void (*writel_dbi)(struct dw_pcie *pcie, void __iomem *base, u32 reg, + size_t size, u32 val); + int (*link_up)(struct dw_pcie *pcie); }; -int dw_pcie_cfg_read(void __iomem *addr, int where, int size, u32 *val); -int dw_pcie_cfg_write(void __iomem *addr, int where, int size, u32 val); -irqreturn_t dw_handle_msi_irq(struct pcie_port *pp); -void dw_pcie_msi_init(struct pcie_port *pp); -int dw_pcie_link_up(struct pcie_port *pp); +struct dw_pcie { + struct device_d *dev; + void __iomem *dbi_base; + u32 num_viewport; + u8 iatu_unroll_enabled; + struct pcie_port pp; + const struct dw_pcie_ops *ops; +}; + +#define to_dw_pcie_from_pp(port) container_of((port), struct dw_pcie, pp) + +int dw_pcie_read(void __iomem *addr, int size, u32 *val); +int dw_pcie_write(void __iomem *addr, int size, u32 val); void dw_pcie_setup_rc(struct pcie_port *pp); int dw_pcie_host_init(struct pcie_port *pp); +u32 __dw_pcie_readl_dbi(struct dw_pcie *pci, void __iomem *addr, u32 reg, + size_t size); +void __dw_pcie_writel_dbi(struct dw_pcie *pci, void __iomem *addr, u32 reg, + size_t size, u32 val); +int dw_pcie_link_up(struct dw_pcie *pci); +int dw_pcie_wait_for_link(struct dw_pcie *pci); +void dw_pcie_prog_outbound_atu(struct dw_pcie *pci, int index, + int type, u64 cpu_addr, u64 pci_addr, + u32 size); +void dw_pcie_setup(struct dw_pcie *pci); + +static inline void dw_pcie_writel_dbi(struct dw_pcie *pci, u32 reg, u32 val) +{ + __dw_pcie_writel_dbi(pci, pci->dbi_base, reg, 0x4, val); +} + +static inline u32 dw_pcie_readl_dbi(struct dw_pcie *pci, u32 reg) +{ + return __dw_pcie_readl_dbi(pci, pci->dbi_base, reg, 0x4); +} + +static inline void dw_pcie_dbi_ro_wr_en(struct dw_pcie *pci) +{ + u32 reg; + u32 val; + + reg = PCIE_MISC_CONTROL_1_OFF; + val = dw_pcie_readl_dbi(pci, reg); + val |= PCIE_DBI_RO_WR_EN; + dw_pcie_writel_dbi(pci, reg, val); +} + +static inline void dw_pcie_dbi_ro_wr_dis(struct dw_pcie *pci) +{ + u32 reg; + u32 val; + + reg = PCIE_MISC_CONTROL_1_OFF; + val = dw_pcie_readl_dbi(pci, reg); + val &= ~PCIE_DBI_RO_WR_EN; + dw_pcie_writel_dbi(pci, reg, val); +} + #endif /* _PCIE_DESIGNWARE_H */