On Mon, Feb 26, 2024 at 2:05 AM Sumit Garg <sumit.g...@linaro.org> wrote: > > pcie_imx doesn't seem to share any useful code for iMX8 SoC and it is > tied to quite old port of pcie_designware driver from Linux which > suffices only iMX6 specific needs. > > But currently we have the common DWC specific bits which alligns pretty > well with DW PCIe controller on iMX8MP SoC. So lets reuse those common > bits instead as a new driver for iMX8 SoCs. It should be fairly easy to > add support for other iMX8 variants to this driver. > > iMX8MP SoC also comes up with standalone PCIe PHY support, so hence we > can reuse the generic PHY infrastructure to power on PCIe PHY. > > Signed-off-by: Sumit Garg <sumit.g...@linaro.org> > --- > drivers/pci/Kconfig | 8 + > drivers/pci/Makefile | 1 + > drivers/pci/pcie_dw_imx.c | 314 ++++++++++++++++++++++++++++++++++++++ > 3 files changed, 323 insertions(+) > create mode 100644 drivers/pci/pcie_dw_imx.c > > diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig > index 463ec47eb92d..7edbf35004a9 100644 > --- a/drivers/pci/Kconfig > +++ b/drivers/pci/Kconfig > @@ -413,4 +413,12 @@ config PCIE_STARFIVE_JH7110 > Say Y here if you want to enable PLDA XpressRich PCIe controller > support on StarFive JH7110 SoC. > > +config PCIE_DW_IMX > + bool "i.MX DW PCIe controller support" > + depends on ARCH_IMX8M
I think this also needs to depend on REGMAP to prevent build errors. adam > + select PCIE_DW_COMMON > + help > + Say Y here if you want to enable DW PCIe controller support on > + iMX SoCs. > + > endif > diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile > index 72ef8b4bc772..2927c519129b 100644 > --- a/drivers/pci/Makefile > +++ b/drivers/pci/Makefile > @@ -53,3 +53,4 @@ obj-$(CONFIG_PCIE_UNIPHIER) += pcie_uniphier.o > obj-$(CONFIG_PCIE_XILINX_NWL) += pcie-xilinx-nwl.o > obj-$(CONFIG_PCIE_PLDA_COMMON) += pcie_plda_common.o > obj-$(CONFIG_PCIE_STARFIVE_JH7110) += pcie_starfive_jh7110.o > +obj-$(CONFIG_PCIE_DW_IMX) += pcie_dw_imx.o > diff --git a/drivers/pci/pcie_dw_imx.c b/drivers/pci/pcie_dw_imx.c > new file mode 100644 > index 000000000000..7856823c9188 > --- /dev/null > +++ b/drivers/pci/pcie_dw_imx.c > @@ -0,0 +1,314 @@ > +// SPDX-License-Identifier: GPL-2.0+ > +/* > + * Copyright (C) 2024 Linaro Ltd. > + * > + * Author: Sumit Garg <sumit.g...@linaro.org> > + */ > + > +#include <asm/io.h> > +#include <asm-generic/gpio.h> > +#include <clk.h> > +#include <dm.h> > +#include <dm/device_compat.h> > +#include <generic-phy.h> > +#include <linux/bitops.h> > +#include <linux/delay.h> > +#include <linux/err.h> > +#include <linux/iopoll.h> > +#include <log.h> > +#include <pci.h> > +#include <regmap.h> > +#include <reset.h> > +#include <syscon.h> > +#include <time.h> > + > +#include "pcie_dw_common.h" > + > +#define PCIE_LINK_CAPABILITY 0x7c > +#define TARGET_LINK_SPEED_MASK 0xf > +#define LINK_SPEED_GEN_1 0x1 > +#define LINK_SPEED_GEN_2 0x2 > +#define LINK_SPEED_GEN_3 0x3 > + > +#define PCIE_MISC_CONTROL_1_OFF 0x8bc > +#define PCIE_DBI_RO_WR_EN BIT(0) > + > +#define PCIE_PORT_DEBUG0 0x728 > +#define PCIE_PORT_DEBUG1 0x72c > +#define PCIE_PORT_DEBUG1_LINK_UP BIT(4) > +#define PCIE_PORT_DEBUG1_LINK_IN_TRAINING BIT(29) > + > +#define PCIE_LINK_UP_TIMEOUT_MS 100 > + > +#define IOMUXC_GPR14_OFFSET 0x38 > +#define IMX8M_GPR_PCIE_CLK_REQ_OVERRIDE_EN BIT(10) > +#define IMX8M_GPR_PCIE_CLK_REQ_OVERRIDE BIT(11) > + > +struct pcie_dw_imx { > + /* Must be first member of the struct */ > + struct pcie_dw dw; > + struct regmap *iomuxc_gpr; > + struct clk_bulk clks; > + struct gpio_desc reset_gpio; > + struct reset_ctl apps_reset; > + struct phy phy; > +}; > + > +static void pcie_dw_configure(struct pcie_dw_imx *priv, u32 cap_speed) > +{ > + dw_pcie_dbi_write_enable(&priv->dw, true); > + > + clrsetbits_le32(priv->dw.dbi_base + PCIE_LINK_CAPABILITY, > + TARGET_LINK_SPEED_MASK, cap_speed); > + > + dw_pcie_dbi_write_enable(&priv->dw, false); > +} > + > +static void imx_pcie_ltssm_enable(struct pcie_dw_imx *priv) > +{ > + reset_deassert(&priv->apps_reset); > +} > + > +static void imx_pcie_ltssm_disable(struct pcie_dw_imx *priv) > +{ > + reset_assert(&priv->apps_reset); > +} > + > +static bool is_link_up(u32 val) > +{ > + return ((val & PCIE_PORT_DEBUG1_LINK_UP) && > + (!(val & PCIE_PORT_DEBUG1_LINK_IN_TRAINING))); > +} > + > +static int wait_link_up(struct pcie_dw_imx *priv) > +{ > + u32 val; > + > + return readl_poll_sleep_timeout(priv->dw.dbi_base + PCIE_PORT_DEBUG1, > + val, is_link_up(val), 10000, 100000); > +} > + > +static int pcie_link_up(struct pcie_dw_imx *priv, u32 cap_speed) > +{ > + int ret; > + > + /* DW pre link configurations */ > + pcie_dw_configure(priv, cap_speed); > + > + /* Initiate link training */ > + imx_pcie_ltssm_enable(priv); > + > + /* Check that link was established */ > + ret = wait_link_up(priv); > + if (ret) > + imx_pcie_ltssm_disable(priv); > + > + return ret; > +} > + > +static int imx_pcie_assert_core_reset(struct pcie_dw_imx *priv) > +{ > + if (dm_gpio_is_valid(&priv->reset_gpio)) { > + dm_gpio_set_value(&priv->reset_gpio, 1); > + mdelay(20); > + } > + > + return reset_assert(&priv->apps_reset); > +} > + > +static int imx_pcie_clk_enable(struct pcie_dw_imx *priv) > +{ > + int ret; > + > + ret = clk_enable_bulk(&priv->clks); > + if (ret) > + return ret; > + > + /* > + * Set the over ride low and enabled make sure that > + * REF_CLK is turned on. > + */ > + regmap_update_bits(priv->iomuxc_gpr, IOMUXC_GPR14_OFFSET, > + IMX8M_GPR_PCIE_CLK_REQ_OVERRIDE, 0); > + regmap_update_bits(priv->iomuxc_gpr, IOMUXC_GPR14_OFFSET, > + IMX8M_GPR_PCIE_CLK_REQ_OVERRIDE_EN, > + IMX8M_GPR_PCIE_CLK_REQ_OVERRIDE_EN); > + > + /* allow the clocks to stabilize */ > + udelay(500); > + > + return 0; > +} > + > +static void imx_pcie_deassert_core_reset(struct pcie_dw_imx *priv) > +{ > + if (!dm_gpio_is_valid(&priv->reset_gpio)) > + return; > + > + mdelay(100); > + dm_gpio_set_value(&priv->reset_gpio, 0); > + /* Wait for 100ms after PERST# deassertion (PCIe r5.0, 6.6.1) */ > + mdelay(100); > +} > + > +static int pcie_dw_imx_probe(struct udevice *dev) > +{ > + struct pcie_dw_imx *priv = dev_get_priv(dev); > + struct udevice *ctlr = pci_get_controller(dev); > + struct pci_controller *hose = dev_get_uclass_priv(ctlr); > + int ret; > + > + ret = imx_pcie_assert_core_reset(priv); > + if (ret) { > + dev_err(dev, "failed to assert core reset\n"); > + return ret; > + } > + > + ret = imx_pcie_clk_enable(priv); > + if (ret) { > + dev_err(dev, "failed to enable clocks\n"); > + goto err_clk; > + } > + > + ret = generic_phy_init(&priv->phy); > + if (ret) { > + dev_err(dev, "failed to initialize PHY\n"); > + goto err_phy_init; > + } > + > + ret = generic_phy_power_on(&priv->phy); > + if (ret) { > + dev_err(dev, "failed to power on PHY\n"); > + goto err_phy_power; > + } > + > + imx_pcie_deassert_core_reset(priv); > + > + priv->dw.first_busno = dev_seq(dev); > + priv->dw.dev = dev; > + pcie_dw_setup_host(&priv->dw); > + > + if (pcie_link_up(priv, LINK_SPEED_GEN_1)) { > + printf("PCIE-%d: Link down\n", dev_seq(dev)); > + ret = -ENODEV; > + goto err_link; > + } > + > + printf("PCIE-%d: Link up (Gen%d-x%d, Bus%d)\n", dev_seq(dev), > + pcie_dw_get_link_speed(&priv->dw), > + pcie_dw_get_link_width(&priv->dw), > + hose->first_busno); > + > + pcie_dw_prog_outbound_atu_unroll(&priv->dw, PCIE_ATU_REGION_INDEX0, > + PCIE_ATU_TYPE_MEM, > + priv->dw.mem.phys_start, > + priv->dw.mem.bus_start, > priv->dw.mem.size); > + > + return 0; > + > +err_link: > + generic_shutdown_phy(&priv->phy); > +err_phy_power: > + generic_phy_exit(&priv->phy); > +err_phy_init: > + clk_disable_bulk(&priv->clks); > +err_clk: > + imx_pcie_deassert_core_reset(priv); > + > + return ret; > +} > + > +static int pcie_dw_imx_remove(struct udevice *dev) > +{ > + struct pcie_dw_imx *priv = dev_get_priv(dev); > + > + generic_shutdown_phy(&priv->phy); > + dm_gpio_free(dev, &priv->reset_gpio); > + reset_release_all(&priv->apps_reset, 1); > + clk_release_bulk(&priv->clks); > + > + return 0; > +} > + > +static int pcie_dw_imx_of_to_plat(struct udevice *dev) > +{ > + struct pcie_dw_imx *priv = dev_get_priv(dev); > + ofnode gpr; > + int ret; > + > + /* Get the controller base address */ > + priv->dw.dbi_base = (void *)dev_read_addr_name(dev, "dbi"); > + if ((fdt_addr_t)priv->dw.dbi_base == FDT_ADDR_T_NONE) { > + dev_err(dev, "failed to get dbi_base address\n"); > + return -EINVAL; > + } > + > + /* Get the config space base address and size */ > + priv->dw.cfg_base = (void *)dev_read_addr_size_name(dev, "config", > + > &priv->dw.cfg_size); > + if ((fdt_addr_t)priv->dw.cfg_base == FDT_ADDR_T_NONE) { > + dev_err(dev, "failed to get cfg_base address\n"); > + return -EINVAL; > + } > + > + ret = clk_get_bulk(dev, &priv->clks); > + if (ret) { > + dev_err(dev, "failed to get PCIe clks\n"); > + return ret; > + } > + > + ret = reset_get_by_name(dev, "apps", &priv->apps_reset); > + if (ret) { > + dev_err(dev, > + "Failed to get PCIe apps reset control\n"); > + return ret; > + } > + > + ret = gpio_request_by_name(dev, "reset-gpio", 0, &priv->reset_gpio, > + (GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE)); > + if (ret) { > + dev_err(dev, "unable to get reset-gpio\n"); > + return ret; > + } > + > + ret = generic_phy_get_by_name(dev, "pcie-phy", &priv->phy); > + if (ret) { > + dev_err(dev, "failed to get pcie phy\n"); > + return ret; > + } > + > + gpr = ofnode_by_compatible(ofnode_null(), "fsl,imx8mp-iomuxc-gpr"); > + if (ofnode_equal(gpr, ofnode_null())) { > + dev_err(dev, "unable to find GPR node\n"); > + return -ENODEV; > + } > + > + priv->iomuxc_gpr = syscon_node_to_regmap(gpr); > + if (IS_ERR(priv->iomuxc_gpr)) { > + dev_err(dev, "unable to find iomuxc registers\n"); > + return PTR_ERR(priv->iomuxc_gpr); > + } > + > + return 0; > +} > + > +static const struct dm_pci_ops pcie_dw_imx_ops = { > + .read_config = pcie_dw_read_config, > + .write_config = pcie_dw_write_config, > +}; > + > +static const struct udevice_id pcie_dw_imx_ids[] = { > + { .compatible = "fsl,imx8mp-pcie" }, > + { } > +}; > + > +U_BOOT_DRIVER(pcie_dw_imx) = { > + .name = "pcie_dw_imx", > + .id = UCLASS_PCI, > + .of_match = pcie_dw_imx_ids, > + .ops = &pcie_dw_imx_ops, > + .of_to_plat = pcie_dw_imx_of_to_plat, > + .probe = pcie_dw_imx_probe, > + .remove = pcie_dw_imx_remove, > + .priv_auto = sizeof(struct pcie_dw_imx), > +}; > -- > 2.34.1 >