On Mon, Feb 26, 2024 at 2:05 AM Sumit Garg <sumit.g...@linaro.org> wrote: > > Add initial support for i.MX8M{M/P} PCIe PHY. On i.MX8M{M/P} SoCs PCIe > PHY initialization moved to this standalone PHY driver. > > Inspired from counterpart Linux kernel v6.8-rc3 driver: > drivers/phy/freescale/phy-fsl-imx8m-pcie.c.
I have a PCIe device that works just fine in Linux. I have applied your series an enabled the same config options as you did along with some others to resolve some build issues. Any ideas how to address: nxp_imx8_pcie_phy pcie-phy@32f00000: PHY: Failed to power on pcie-phy@32f00000: -110. My PHY node looks like this &pcie_phy { fsl,refclk-pad-mode = <IMX8_PCIE_REFCLK_PAD_INPUT>; clocks = <&pcie0_refclk>; clock-names = "ref"; status = "okay"; }; The pcie_refcllk is defined as a 100MHz fixed clock in U-Boot. clk dump shows it to be: 100000000 0 |-- clock-pcie > > Signed-off-by: Sumit Garg <sumit.g...@linaro.org> > --- > drivers/phy/Kconfig | 9 ++ > drivers/phy/Makefile | 1 + > drivers/phy/phy-imx8m-pcie.c | 269 +++++++++++++++++++++++++++++++++++ > 3 files changed, 279 insertions(+) > create mode 100644 drivers/phy/phy-imx8m-pcie.c > > diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig > index 60138beca498..110ec8f5008d 100644 > --- a/drivers/phy/Kconfig > +++ b/drivers/phy/Kconfig > @@ -284,6 +284,15 @@ config PHY_IMX8MQ_USB > help > Support the USB3.0 PHY in NXP i.MX8MQ or i.MX8MP SoC > > +config PHY_IMX8M_PCIE > + bool "NXP i.MX8MM/i.MX8MP PCIe PHY Driver" > + depends on PHY > + depends on IMX8MM || IMX8MP This should also depend on SYSCON and REGMAP to prevent build errors. > + help > + Support the PCIe PHY in NXP i.MX8MM or i.MX8MP SoC > + > + This PHY is found on i.MX8M devices supporting PCIe. > + > config PHY_XILINX_ZYNQMP > tristate "Xilinx ZynqMP PHY driver" > depends on PHY && ARCH_ZYNQMP > diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile > index 2e8723186c05..7a2b764492be 100644 > --- a/drivers/phy/Makefile > +++ b/drivers/phy/Makefile > @@ -38,6 +38,7 @@ obj-$(CONFIG_PHY_DA8XX_USB) += phy-da8xx-usb.o > obj-$(CONFIG_PHY_MTK_TPHY) += phy-mtk-tphy.o > obj-$(CONFIG_PHY_NPCM_USB) += phy-npcm-usb.o > obj-$(CONFIG_PHY_IMX8MQ_USB) += phy-imx8mq-usb.o > +obj-$(CONFIG_PHY_IMX8M_PCIE) += phy-imx8m-pcie.o > obj-$(CONFIG_PHY_XILINX_ZYNQMP) += phy-zynqmp.o > obj-y += cadence/ > obj-y += ti/ > diff --git a/drivers/phy/phy-imx8m-pcie.c b/drivers/phy/phy-imx8m-pcie.c > new file mode 100644 > index 000000000000..e09a7ac97235 > --- /dev/null > +++ b/drivers/phy/phy-imx8m-pcie.c > @@ -0,0 +1,269 @@ > +// SPDX-License-Identifier: GPL-2.0+ > +/* > + * Copyright 2024 Linaro Ltd. > + * > + * Derived from Linux counterpart driver > + */ > + > +#include <asm/io.h> > +#include <clk.h> > +#include <dm.h> > +#include <dm/device_compat.h> > +#include <generic-phy.h> > +#include <linux/bitfield.h> > +#include <linux/clk-provider.h> > +#include <linux/iopoll.h> > +#include <syscon.h> > +#include <regmap.h> > +#include <reset.h> > + > +#include <dt-bindings/phy/phy-imx8-pcie.h> > + > +#define IMX8MM_PCIE_PHY_CMN_REG061 0x184 > +#define ANA_PLL_CLK_OUT_TO_EXT_IO_EN BIT(0) > +#define IMX8MM_PCIE_PHY_CMN_REG062 0x188 > +#define ANA_PLL_CLK_OUT_TO_EXT_IO_SEL BIT(3) > +#define IMX8MM_PCIE_PHY_CMN_REG063 0x18C > +#define AUX_PLL_REFCLK_SEL_SYS_PLL GENMASK(7, 6) > +#define IMX8MM_PCIE_PHY_CMN_REG064 0x190 > +#define ANA_AUX_RX_TX_SEL_TX BIT(7) > +#define ANA_AUX_RX_TERM_GND_EN BIT(3) > +#define ANA_AUX_TX_TERM BIT(2) > +#define IMX8MM_PCIE_PHY_CMN_REG065 0x194 > +#define ANA_AUX_RX_TERM (BIT(7) | BIT(4)) > +#define ANA_AUX_TX_LVL GENMASK(3, 0) > +#define IMX8MM_PCIE_PHY_CMN_REG075 0x1D4 > +#define ANA_PLL_DONE 0x3 > +#define PCIE_PHY_TRSV_REG5 0x414 > +#define PCIE_PHY_TRSV_REG6 0x418 > + > +#define IMX8MM_GPR_PCIE_REF_CLK_SEL GENMASK(25, 24) > +#define IMX8MM_GPR_PCIE_REF_CLK_PLL > FIELD_PREP(IMX8MM_GPR_PCIE_REF_CLK_SEL, 0x3) > +#define IMX8MM_GPR_PCIE_REF_CLK_EXT > FIELD_PREP(IMX8MM_GPR_PCIE_REF_CLK_SEL, 0x2) > +#define IMX8MM_GPR_PCIE_AUX_EN BIT(19) > +#define IMX8MM_GPR_PCIE_CMN_RST BIT(18) > +#define IMX8MM_GPR_PCIE_POWER_OFF BIT(17) > +#define IMX8MM_GPR_PCIE_SSC_EN BIT(16) > +#define IMX8MM_GPR_PCIE_AUX_EN_OVERRIDE BIT(9) > + > +#define IOMUXC_GPR14_OFFSET 0x38 > + > +enum imx8_pcie_phy_type { > + IMX8MM, > + IMX8MP, > +}; > + > +struct imx8_pcie_phy_drvdata { > + const char *gpr; > + enum imx8_pcie_phy_type variant; > +}; > + > +struct imx8_pcie_phy { > + ulong base; > + struct clk hsio_clk; > + struct regmap *iomuxc_gpr; > + struct reset_ctl perst; > + struct reset_ctl reset; > + u32 refclk_pad_mode; > + u32 tx_deemph_gen1; > + u32 tx_deemph_gen2; > + bool clkreq_unused; > + const struct imx8_pcie_phy_drvdata *drvdata; > +}; > + > +static int imx8_pcie_phy_power_on(struct phy *phy) > +{ > + int ret; > + u32 val, pad_mode; > + struct imx8_pcie_phy *imx8_phy = dev_get_priv(phy->dev); > + > + pad_mode = imx8_phy->refclk_pad_mode; > + switch (imx8_phy->drvdata->variant) { > + case IMX8MM: > + reset_assert(&imx8_phy->reset); > + > + /* Tune PHY de-emphasis setting to pass PCIe compliance. */ > + if (imx8_phy->tx_deemph_gen1) > + writel(imx8_phy->tx_deemph_gen1, > + imx8_phy->base + PCIE_PHY_TRSV_REG5); > + if (imx8_phy->tx_deemph_gen2) > + writel(imx8_phy->tx_deemph_gen2, > + imx8_phy->base + PCIE_PHY_TRSV_REG6); > + break; > + case IMX8MP: /* Do nothing. */ > + break; > + } > + > + if (pad_mode == IMX8_PCIE_REFCLK_PAD_INPUT || > + pad_mode == IMX8_PCIE_REFCLK_PAD_UNUSED) { > + /* Configure the pad as input */ > + val = readl(imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG061); > + writel(val & ~ANA_PLL_CLK_OUT_TO_EXT_IO_EN, > + imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG061); > + } else { > + /* Configure the PHY to output the refclock via pad */ > + writel(ANA_PLL_CLK_OUT_TO_EXT_IO_EN, > + imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG061); > + } > + > + if (pad_mode == IMX8_PCIE_REFCLK_PAD_OUTPUT || > + pad_mode == IMX8_PCIE_REFCLK_PAD_UNUSED) { > + /* Source clock from SoC internal PLL */ > + writel(ANA_PLL_CLK_OUT_TO_EXT_IO_SEL, > + imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG062); > + writel(AUX_PLL_REFCLK_SEL_SYS_PLL, > + imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG063); > + val = ANA_AUX_RX_TX_SEL_TX | ANA_AUX_TX_TERM; > + writel(val | ANA_AUX_RX_TERM_GND_EN, > + imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG064); > + writel(ANA_AUX_RX_TERM | ANA_AUX_TX_LVL, > + imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG065); > + } > + > + /* Set AUX_EN_OVERRIDE 1'b0, when the CLKREQ# isn't hooked */ > + regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14_OFFSET, > + IMX8MM_GPR_PCIE_AUX_EN_OVERRIDE, > + imx8_phy->clkreq_unused ? > + 0 : IMX8MM_GPR_PCIE_AUX_EN_OVERRIDE); > + regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14_OFFSET, > + IMX8MM_GPR_PCIE_AUX_EN, > + IMX8MM_GPR_PCIE_AUX_EN); > + regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14_OFFSET, > + IMX8MM_GPR_PCIE_POWER_OFF, 0); > + regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14_OFFSET, > + IMX8MM_GPR_PCIE_SSC_EN, 0); > + > + regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14_OFFSET, > + IMX8MM_GPR_PCIE_REF_CLK_SEL, > + pad_mode == IMX8_PCIE_REFCLK_PAD_INPUT ? > + IMX8MM_GPR_PCIE_REF_CLK_EXT : > + IMX8MM_GPR_PCIE_REF_CLK_PLL); > + udelay(200); > + > + /* Do the PHY common block reset */ > + regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14_OFFSET, > + IMX8MM_GPR_PCIE_CMN_RST, > + IMX8MM_GPR_PCIE_CMN_RST); > + > + switch (imx8_phy->drvdata->variant) { > + case IMX8MP: > + reset_deassert(&imx8_phy->perst); > + fallthrough; > + case IMX8MM: > + reset_deassert(&imx8_phy->reset); > + udelay(500); > + break; > + } > + > + /* Polling to check the phy is ready or not. */ > + ret = readl_poll_timeout(imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG075, > + val, val == ANA_PLL_DONE, 20000); > + return ret; > +} > + > +static int imx8_pcie_phy_init(struct phy *phy) > +{ > + struct imx8_pcie_phy *imx8_phy = dev_get_priv(phy->dev); > + > + return clk_enable(&imx8_phy->hsio_clk); > +} > + > +static int imx8_pcie_phy_exit(struct phy *phy) > +{ > + struct imx8_pcie_phy *imx8_phy = dev_get_priv(phy->dev); > + > + return clk_disable(&imx8_phy->hsio_clk); > +} > + > +static const struct phy_ops imx8_pcie_phy_ops = { > + .init = imx8_pcie_phy_init, > + .exit = imx8_pcie_phy_exit, > + .power_on = imx8_pcie_phy_power_on, > +}; > + > +static const struct imx8_pcie_phy_drvdata imx8mm_drvdata = { > + .gpr = "fsl,imx8mm-iomuxc-gpr", > + .variant = IMX8MM, > +}; > + > +static const struct imx8_pcie_phy_drvdata imx8mp_drvdata = { > + .gpr = "fsl,imx8mp-iomuxc-gpr", > + .variant = IMX8MP, > +}; > + > +static const struct udevice_id imx8_pcie_phy_of_match[] = { > + {.compatible = "fsl,imx8mm-pcie-phy", .data = (ulong)&imx8mm_drvdata, > }, > + {.compatible = "fsl,imx8mp-pcie-phy", .data = (ulong)&imx8mp_drvdata, > }, > + { }, > +}; > + > +static int imx8_pcie_phy_probe(struct udevice *dev) > +{ > + struct imx8_pcie_phy *imx8_phy = dev_get_priv(dev); > + ofnode gpr; > + int ret = 0; > + > + imx8_phy->drvdata = (void *)dev_get_driver_data(dev); > + imx8_phy->base = dev_read_addr(dev); > + if (!imx8_phy->base) > + return -EINVAL; > + > + /* get PHY refclk pad mode */ > + dev_read_u32(dev, "fsl,refclk-pad-mode", &imx8_phy->refclk_pad_mode); > + > + if (dev_read_u32(dev, "fsl,tx-deemph-gen1", > &imx8_phy->tx_deemph_gen1)) > + imx8_phy->tx_deemph_gen1 = 0; > + > + if (dev_read_u32(dev, "fsl,tx-deemph-gen2", > &imx8_phy->tx_deemph_gen2)) > + imx8_phy->tx_deemph_gen2 = 0; > + > + if (dev_read_bool(dev, "fsl,clkreq-unsupported")) > + imx8_phy->clkreq_unused = true; > + else > + imx8_phy->clkreq_unused = false; > + > + /* Grab GPR config register range */ > + gpr = ofnode_by_compatible(ofnode_null(), imx8_phy->drvdata->gpr); > + if (ofnode_equal(gpr, ofnode_null())) { > + dev_err(dev, "unable to find GPR node\n"); > + return -ENODEV; > + } > + > + imx8_phy->iomuxc_gpr = syscon_node_to_regmap(gpr); > + if (IS_ERR(imx8_phy->iomuxc_gpr)) { > + dev_err(dev, "unable to find iomuxc registers\n"); > + return PTR_ERR(imx8_phy->iomuxc_gpr); > + } > + > + ret = clk_get_by_name(dev, "ref", &imx8_phy->hsio_clk); > + if (ret) { > + dev_err(dev, "Failed to get PCIEPHY ref clock\n"); > + return ret; > + } > + > + ret = reset_get_by_name(dev, "pciephy", &imx8_phy->reset); > + if (ret) { > + dev_err(dev, "Failed to get PCIEPHY reset control\n"); > + return ret; > + } > + > + if (imx8_phy->drvdata->variant == IMX8MP) { > + ret = reset_get_by_name(dev, "perst", &imx8_phy->perst); > + if (ret) { > + dev_err(dev, > + "Failed to get PCIEPHY PHY PERST control\n"); > + return ret; > + } > + } > + > + return 0; > +} > + > +U_BOOT_DRIVER(nxp_imx8_pcie_phy) = { > + .name = "nxp_imx8_pcie_phy", > + .id = UCLASS_PHY, > + .of_match = imx8_pcie_phy_of_match, > + .probe = imx8_pcie_phy_probe, > + .ops = &imx8_pcie_phy_ops, > + .priv_auto = sizeof(struct imx8_pcie_phy), > +}; > -- > 2.34.1 >