162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright 2021 NXP 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/bitfield.h> 762306a36Sopenharmony_ci#include <linux/clk.h> 862306a36Sopenharmony_ci#include <linux/delay.h> 962306a36Sopenharmony_ci#include <linux/io.h> 1062306a36Sopenharmony_ci#include <linux/iopoll.h> 1162306a36Sopenharmony_ci#include <linux/mfd/syscon.h> 1262306a36Sopenharmony_ci#include <linux/mfd/syscon/imx7-iomuxc-gpr.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/of.h> 1562306a36Sopenharmony_ci#include <linux/phy/phy.h> 1662306a36Sopenharmony_ci#include <linux/platform_device.h> 1762306a36Sopenharmony_ci#include <linux/regmap.h> 1862306a36Sopenharmony_ci#include <linux/reset.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <dt-bindings/phy/phy-imx8-pcie.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define IMX8MM_PCIE_PHY_CMN_REG061 0x184 2362306a36Sopenharmony_ci#define ANA_PLL_CLK_OUT_TO_EXT_IO_EN BIT(0) 2462306a36Sopenharmony_ci#define IMX8MM_PCIE_PHY_CMN_REG062 0x188 2562306a36Sopenharmony_ci#define ANA_PLL_CLK_OUT_TO_EXT_IO_SEL BIT(3) 2662306a36Sopenharmony_ci#define IMX8MM_PCIE_PHY_CMN_REG063 0x18C 2762306a36Sopenharmony_ci#define AUX_PLL_REFCLK_SEL_SYS_PLL GENMASK(7, 6) 2862306a36Sopenharmony_ci#define IMX8MM_PCIE_PHY_CMN_REG064 0x190 2962306a36Sopenharmony_ci#define ANA_AUX_RX_TX_SEL_TX BIT(7) 3062306a36Sopenharmony_ci#define ANA_AUX_RX_TERM_GND_EN BIT(3) 3162306a36Sopenharmony_ci#define ANA_AUX_TX_TERM BIT(2) 3262306a36Sopenharmony_ci#define IMX8MM_PCIE_PHY_CMN_REG065 0x194 3362306a36Sopenharmony_ci#define ANA_AUX_RX_TERM (BIT(7) | BIT(4)) 3462306a36Sopenharmony_ci#define ANA_AUX_TX_LVL GENMASK(3, 0) 3562306a36Sopenharmony_ci#define IMX8MM_PCIE_PHY_CMN_REG075 0x1D4 3662306a36Sopenharmony_ci#define ANA_PLL_DONE 0x3 3762306a36Sopenharmony_ci#define PCIE_PHY_TRSV_REG5 0x414 3862306a36Sopenharmony_ci#define PCIE_PHY_TRSV_REG6 0x418 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#define IMX8MM_GPR_PCIE_REF_CLK_SEL GENMASK(25, 24) 4162306a36Sopenharmony_ci#define IMX8MM_GPR_PCIE_REF_CLK_PLL FIELD_PREP(IMX8MM_GPR_PCIE_REF_CLK_SEL, 0x3) 4262306a36Sopenharmony_ci#define IMX8MM_GPR_PCIE_REF_CLK_EXT FIELD_PREP(IMX8MM_GPR_PCIE_REF_CLK_SEL, 0x2) 4362306a36Sopenharmony_ci#define IMX8MM_GPR_PCIE_AUX_EN BIT(19) 4462306a36Sopenharmony_ci#define IMX8MM_GPR_PCIE_CMN_RST BIT(18) 4562306a36Sopenharmony_ci#define IMX8MM_GPR_PCIE_POWER_OFF BIT(17) 4662306a36Sopenharmony_ci#define IMX8MM_GPR_PCIE_SSC_EN BIT(16) 4762306a36Sopenharmony_ci#define IMX8MM_GPR_PCIE_AUX_EN_OVERRIDE BIT(9) 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cienum imx8_pcie_phy_type { 5062306a36Sopenharmony_ci IMX8MM, 5162306a36Sopenharmony_ci IMX8MP, 5262306a36Sopenharmony_ci}; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistruct imx8_pcie_phy_drvdata { 5562306a36Sopenharmony_ci const char *gpr; 5662306a36Sopenharmony_ci enum imx8_pcie_phy_type variant; 5762306a36Sopenharmony_ci}; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistruct imx8_pcie_phy { 6062306a36Sopenharmony_ci void __iomem *base; 6162306a36Sopenharmony_ci struct clk *clk; 6262306a36Sopenharmony_ci struct phy *phy; 6362306a36Sopenharmony_ci struct regmap *iomuxc_gpr; 6462306a36Sopenharmony_ci struct reset_control *perst; 6562306a36Sopenharmony_ci struct reset_control *reset; 6662306a36Sopenharmony_ci u32 refclk_pad_mode; 6762306a36Sopenharmony_ci u32 tx_deemph_gen1; 6862306a36Sopenharmony_ci u32 tx_deemph_gen2; 6962306a36Sopenharmony_ci bool clkreq_unused; 7062306a36Sopenharmony_ci const struct imx8_pcie_phy_drvdata *drvdata; 7162306a36Sopenharmony_ci}; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic int imx8_pcie_phy_power_on(struct phy *phy) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci int ret; 7662306a36Sopenharmony_ci u32 val, pad_mode; 7762306a36Sopenharmony_ci struct imx8_pcie_phy *imx8_phy = phy_get_drvdata(phy); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci pad_mode = imx8_phy->refclk_pad_mode; 8062306a36Sopenharmony_ci switch (imx8_phy->drvdata->variant) { 8162306a36Sopenharmony_ci case IMX8MM: 8262306a36Sopenharmony_ci reset_control_assert(imx8_phy->reset); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci /* Tune PHY de-emphasis setting to pass PCIe compliance. */ 8562306a36Sopenharmony_ci if (imx8_phy->tx_deemph_gen1) 8662306a36Sopenharmony_ci writel(imx8_phy->tx_deemph_gen1, 8762306a36Sopenharmony_ci imx8_phy->base + PCIE_PHY_TRSV_REG5); 8862306a36Sopenharmony_ci if (imx8_phy->tx_deemph_gen2) 8962306a36Sopenharmony_ci writel(imx8_phy->tx_deemph_gen2, 9062306a36Sopenharmony_ci imx8_phy->base + PCIE_PHY_TRSV_REG6); 9162306a36Sopenharmony_ci break; 9262306a36Sopenharmony_ci case IMX8MP: /* Do nothing. */ 9362306a36Sopenharmony_ci break; 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci if (pad_mode == IMX8_PCIE_REFCLK_PAD_INPUT || 9762306a36Sopenharmony_ci pad_mode == IMX8_PCIE_REFCLK_PAD_UNUSED) { 9862306a36Sopenharmony_ci /* Configure the pad as input */ 9962306a36Sopenharmony_ci val = readl(imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG061); 10062306a36Sopenharmony_ci writel(val & ~ANA_PLL_CLK_OUT_TO_EXT_IO_EN, 10162306a36Sopenharmony_ci imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG061); 10262306a36Sopenharmony_ci } else { 10362306a36Sopenharmony_ci /* Configure the PHY to output the refclock via pad */ 10462306a36Sopenharmony_ci writel(ANA_PLL_CLK_OUT_TO_EXT_IO_EN, 10562306a36Sopenharmony_ci imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG061); 10662306a36Sopenharmony_ci } 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci if (pad_mode == IMX8_PCIE_REFCLK_PAD_OUTPUT || 10962306a36Sopenharmony_ci pad_mode == IMX8_PCIE_REFCLK_PAD_UNUSED) { 11062306a36Sopenharmony_ci /* Source clock from SoC internal PLL */ 11162306a36Sopenharmony_ci writel(ANA_PLL_CLK_OUT_TO_EXT_IO_SEL, 11262306a36Sopenharmony_ci imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG062); 11362306a36Sopenharmony_ci writel(AUX_PLL_REFCLK_SEL_SYS_PLL, 11462306a36Sopenharmony_ci imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG063); 11562306a36Sopenharmony_ci val = ANA_AUX_RX_TX_SEL_TX | ANA_AUX_TX_TERM; 11662306a36Sopenharmony_ci writel(val | ANA_AUX_RX_TERM_GND_EN, 11762306a36Sopenharmony_ci imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG064); 11862306a36Sopenharmony_ci writel(ANA_AUX_RX_TERM | ANA_AUX_TX_LVL, 11962306a36Sopenharmony_ci imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG065); 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci /* Set AUX_EN_OVERRIDE 1'b0, when the CLKREQ# isn't hooked */ 12362306a36Sopenharmony_ci regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14, 12462306a36Sopenharmony_ci IMX8MM_GPR_PCIE_AUX_EN_OVERRIDE, 12562306a36Sopenharmony_ci imx8_phy->clkreq_unused ? 12662306a36Sopenharmony_ci 0 : IMX8MM_GPR_PCIE_AUX_EN_OVERRIDE); 12762306a36Sopenharmony_ci regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14, 12862306a36Sopenharmony_ci IMX8MM_GPR_PCIE_AUX_EN, 12962306a36Sopenharmony_ci IMX8MM_GPR_PCIE_AUX_EN); 13062306a36Sopenharmony_ci regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14, 13162306a36Sopenharmony_ci IMX8MM_GPR_PCIE_POWER_OFF, 0); 13262306a36Sopenharmony_ci regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14, 13362306a36Sopenharmony_ci IMX8MM_GPR_PCIE_SSC_EN, 0); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14, 13662306a36Sopenharmony_ci IMX8MM_GPR_PCIE_REF_CLK_SEL, 13762306a36Sopenharmony_ci pad_mode == IMX8_PCIE_REFCLK_PAD_INPUT ? 13862306a36Sopenharmony_ci IMX8MM_GPR_PCIE_REF_CLK_EXT : 13962306a36Sopenharmony_ci IMX8MM_GPR_PCIE_REF_CLK_PLL); 14062306a36Sopenharmony_ci usleep_range(100, 200); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci /* Do the PHY common block reset */ 14362306a36Sopenharmony_ci regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14, 14462306a36Sopenharmony_ci IMX8MM_GPR_PCIE_CMN_RST, 14562306a36Sopenharmony_ci IMX8MM_GPR_PCIE_CMN_RST); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci switch (imx8_phy->drvdata->variant) { 14862306a36Sopenharmony_ci case IMX8MP: 14962306a36Sopenharmony_ci reset_control_deassert(imx8_phy->perst); 15062306a36Sopenharmony_ci fallthrough; 15162306a36Sopenharmony_ci case IMX8MM: 15262306a36Sopenharmony_ci reset_control_deassert(imx8_phy->reset); 15362306a36Sopenharmony_ci usleep_range(200, 500); 15462306a36Sopenharmony_ci break; 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci /* Polling to check the phy is ready or not. */ 15862306a36Sopenharmony_ci ret = readl_poll_timeout(imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG075, 15962306a36Sopenharmony_ci val, val == ANA_PLL_DONE, 10, 20000); 16062306a36Sopenharmony_ci return ret; 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_cistatic int imx8_pcie_phy_init(struct phy *phy) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci struct imx8_pcie_phy *imx8_phy = phy_get_drvdata(phy); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci return clk_prepare_enable(imx8_phy->clk); 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_cistatic int imx8_pcie_phy_exit(struct phy *phy) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci struct imx8_pcie_phy *imx8_phy = phy_get_drvdata(phy); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci clk_disable_unprepare(imx8_phy->clk); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci return 0; 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistatic const struct phy_ops imx8_pcie_phy_ops = { 18062306a36Sopenharmony_ci .init = imx8_pcie_phy_init, 18162306a36Sopenharmony_ci .exit = imx8_pcie_phy_exit, 18262306a36Sopenharmony_ci .power_on = imx8_pcie_phy_power_on, 18362306a36Sopenharmony_ci .owner = THIS_MODULE, 18462306a36Sopenharmony_ci}; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_cistatic const struct imx8_pcie_phy_drvdata imx8mm_drvdata = { 18762306a36Sopenharmony_ci .gpr = "fsl,imx8mm-iomuxc-gpr", 18862306a36Sopenharmony_ci .variant = IMX8MM, 18962306a36Sopenharmony_ci}; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cistatic const struct imx8_pcie_phy_drvdata imx8mp_drvdata = { 19262306a36Sopenharmony_ci .gpr = "fsl,imx8mp-iomuxc-gpr", 19362306a36Sopenharmony_ci .variant = IMX8MP, 19462306a36Sopenharmony_ci}; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_cistatic const struct of_device_id imx8_pcie_phy_of_match[] = { 19762306a36Sopenharmony_ci {.compatible = "fsl,imx8mm-pcie-phy", .data = &imx8mm_drvdata, }, 19862306a36Sopenharmony_ci {.compatible = "fsl,imx8mp-pcie-phy", .data = &imx8mp_drvdata, }, 19962306a36Sopenharmony_ci { }, 20062306a36Sopenharmony_ci}; 20162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, imx8_pcie_phy_of_match); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistatic int imx8_pcie_phy_probe(struct platform_device *pdev) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci struct phy_provider *phy_provider; 20662306a36Sopenharmony_ci struct device *dev = &pdev->dev; 20762306a36Sopenharmony_ci struct device_node *np = dev->of_node; 20862306a36Sopenharmony_ci struct imx8_pcie_phy *imx8_phy; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci imx8_phy = devm_kzalloc(dev, sizeof(*imx8_phy), GFP_KERNEL); 21162306a36Sopenharmony_ci if (!imx8_phy) 21262306a36Sopenharmony_ci return -ENOMEM; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci imx8_phy->drvdata = of_device_get_match_data(dev); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci /* get PHY refclk pad mode */ 21762306a36Sopenharmony_ci of_property_read_u32(np, "fsl,refclk-pad-mode", 21862306a36Sopenharmony_ci &imx8_phy->refclk_pad_mode); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci if (of_property_read_u32(np, "fsl,tx-deemph-gen1", 22162306a36Sopenharmony_ci &imx8_phy->tx_deemph_gen1)) 22262306a36Sopenharmony_ci imx8_phy->tx_deemph_gen1 = 0; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci if (of_property_read_u32(np, "fsl,tx-deemph-gen2", 22562306a36Sopenharmony_ci &imx8_phy->tx_deemph_gen2)) 22662306a36Sopenharmony_ci imx8_phy->tx_deemph_gen2 = 0; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci if (of_property_read_bool(np, "fsl,clkreq-unsupported")) 22962306a36Sopenharmony_ci imx8_phy->clkreq_unused = true; 23062306a36Sopenharmony_ci else 23162306a36Sopenharmony_ci imx8_phy->clkreq_unused = false; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci imx8_phy->clk = devm_clk_get(dev, "ref"); 23462306a36Sopenharmony_ci if (IS_ERR(imx8_phy->clk)) { 23562306a36Sopenharmony_ci dev_err(dev, "failed to get imx pcie phy clock\n"); 23662306a36Sopenharmony_ci return PTR_ERR(imx8_phy->clk); 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci /* Grab GPR config register range */ 24062306a36Sopenharmony_ci imx8_phy->iomuxc_gpr = 24162306a36Sopenharmony_ci syscon_regmap_lookup_by_compatible(imx8_phy->drvdata->gpr); 24262306a36Sopenharmony_ci if (IS_ERR(imx8_phy->iomuxc_gpr)) { 24362306a36Sopenharmony_ci dev_err(dev, "unable to find iomuxc registers\n"); 24462306a36Sopenharmony_ci return PTR_ERR(imx8_phy->iomuxc_gpr); 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci imx8_phy->reset = devm_reset_control_get_exclusive(dev, "pciephy"); 24862306a36Sopenharmony_ci if (IS_ERR(imx8_phy->reset)) { 24962306a36Sopenharmony_ci dev_err(dev, "Failed to get PCIEPHY reset control\n"); 25062306a36Sopenharmony_ci return PTR_ERR(imx8_phy->reset); 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci if (imx8_phy->drvdata->variant == IMX8MP) { 25462306a36Sopenharmony_ci imx8_phy->perst = 25562306a36Sopenharmony_ci devm_reset_control_get_exclusive(dev, "perst"); 25662306a36Sopenharmony_ci if (IS_ERR(imx8_phy->perst)) 25762306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(imx8_phy->perst), 25862306a36Sopenharmony_ci "Failed to get PCIE PHY PERST control\n"); 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci imx8_phy->base = devm_platform_ioremap_resource(pdev, 0); 26262306a36Sopenharmony_ci if (IS_ERR(imx8_phy->base)) 26362306a36Sopenharmony_ci return PTR_ERR(imx8_phy->base); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci imx8_phy->phy = devm_phy_create(dev, NULL, &imx8_pcie_phy_ops); 26662306a36Sopenharmony_ci if (IS_ERR(imx8_phy->phy)) 26762306a36Sopenharmony_ci return PTR_ERR(imx8_phy->phy); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci phy_set_drvdata(imx8_phy->phy, imx8_phy); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci return PTR_ERR_OR_ZERO(phy_provider); 27462306a36Sopenharmony_ci} 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_cistatic struct platform_driver imx8_pcie_phy_driver = { 27762306a36Sopenharmony_ci .probe = imx8_pcie_phy_probe, 27862306a36Sopenharmony_ci .driver = { 27962306a36Sopenharmony_ci .name = "imx8-pcie-phy", 28062306a36Sopenharmony_ci .of_match_table = imx8_pcie_phy_of_match, 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci}; 28362306a36Sopenharmony_cimodule_platform_driver(imx8_pcie_phy_driver); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ciMODULE_DESCRIPTION("FSL IMX8 PCIE PHY driver"); 28662306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 287