Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Richard Zhu | 3045 | 99.90% | 1 | 50.00% |
Nathan Chancellor | 3 | 0.10% | 1 | 50.00% |
Total | 3048 | 2 |
// SPDX-License-Identifier: GPL-2.0+ /* * Copyright 2024 NXP */ #include <linux/bitfield.h> #include <linux/clk.h> #include <linux/delay.h> #include <linux/io.h> #include <linux/iopoll.h> #include <linux/module.h> #include <linux/of.h> #include <linux/pci_regs.h> #include <linux/phy/phy.h> #include <linux/phy/pcie.h> #include <linux/platform_device.h> #include <linux/regmap.h> #include <dt-bindings/phy/phy.h> #include <dt-bindings/phy/phy-imx8-pcie.h> #define MAX_NUM_LANE 3 #define LANE_NUM_CLKS 5 /* Parameters for the waiting for PCIe PHY PLL to lock */ #define PHY_INIT_WAIT_USLEEP_MAX 10 #define PHY_INIT_WAIT_TIMEOUT (1000 * PHY_INIT_WAIT_USLEEP_MAX) /* i.MX8Q HSIO registers */ #define HSIO_CTRL0 0x0 #define HSIO_APB_RSTN_0 BIT(0) #define HSIO_APB_RSTN_1 BIT(1) #define HSIO_PIPE_RSTN_0_MASK GENMASK(25, 24) #define HSIO_PIPE_RSTN_1_MASK GENMASK(27, 26) #define HSIO_MODE_MASK GENMASK(20, 17) #define HSIO_MODE_PCIE 0x0 #define HSIO_MODE_SATA 0x4 #define HSIO_DEVICE_TYPE_MASK GENMASK(27, 24) #define HSIO_EPCS_TXDEEMP BIT(5) #define HSIO_EPCS_TXDEEMP_SEL BIT(6) #define HSIO_EPCS_PHYRESET_N BIT(7) #define HSIO_RESET_N BIT(12) #define HSIO_IOB_RXENA BIT(0) #define HSIO_IOB_TXENA BIT(1) #define HSIO_IOB_A_0_TXOE BIT(2) #define HSIO_IOB_A_0_M1M0_2 BIT(4) #define HSIO_IOB_A_0_M1M0_MASK GENMASK(4, 3) #define HSIO_PHYX1_EPCS_SEL BIT(12) #define HSIO_PCIE_AB_SELECT BIT(13) #define HSIO_PHY_STS0 0x4 #define HSIO_LANE0_TX_PLL_LOCK BIT(4) #define HSIO_LANE1_TX_PLL_LOCK BIT(12) #define HSIO_CTRL2 0x8 #define HSIO_LTSSM_ENABLE BIT(4) #define HSIO_BUTTON_RST_N BIT(21) #define HSIO_PERST_N BIT(22) #define HSIO_POWER_UP_RST_N BIT(23) #define HSIO_PCIE_STS0 0xc #define HSIO_PM_REQ_CORE_RST BIT(19) #define HSIO_REG48_PMA_STATUS 0x30 #define HSIO_REG48_PMA_RDY BIT(7) struct imx_hsio_drvdata { int lane_num; }; struct imx_hsio_lane { u32 ctrl_index; u32 ctrl_off; u32 idx; u32 phy_off; u32 phy_type; const char * const *clk_names; struct clk_bulk_data clks[LANE_NUM_CLKS]; struct imx_hsio_priv *priv; struct phy *phy; enum phy_mode phy_mode; }; struct imx_hsio_priv { void __iomem *base; struct device *dev; struct mutex lock; const char *hsio_cfg; const char *refclk_pad; u32 open_cnt; struct regmap *phy; struct regmap *ctrl; struct regmap *misc; const struct imx_hsio_drvdata *drvdata; struct imx_hsio_lane lane[MAX_NUM_LANE]; }; static const char * const lan0_pcie_clks[] = {"apb_pclk0", "pclk0", "ctl0_crr", "phy0_crr", "misc_crr"}; static const char * const lan1_pciea_clks[] = {"apb_pclk1", "pclk1", "ctl0_crr", "phy0_crr", "misc_crr"}; static const char * const lan1_pcieb_clks[] = {"apb_pclk1", "pclk1", "ctl1_crr", "phy0_crr", "misc_crr"}; static const char * const lan2_pcieb_clks[] = {"apb_pclk2", "pclk2", "ctl1_crr", "phy1_crr", "misc_crr"}; static const char * const lan2_sata_clks[] = {"pclk2", "epcs_tx", "epcs_rx", "phy1_crr", "misc_crr"}; static const struct regmap_config regmap_config = { .reg_bits = 32, .val_bits = 32, .reg_stride = 4, }; static int imx_hsio_init(struct phy *phy) { int ret, i; struct imx_hsio_lane *lane = phy_get_drvdata(phy); struct imx_hsio_priv *priv = lane->priv; struct device *dev = priv->dev; /* Assign clocks refer to different modes */ switch (lane->phy_type) { case PHY_TYPE_PCIE: lane->phy_mode = PHY_MODE_PCIE; if (lane->ctrl_index == 0) { /* PCIEA */ lane->ctrl_off = 0; lane->phy_off = 0; for (i = 0; i < LANE_NUM_CLKS; i++) { if (lane->idx == 0) lane->clks[i].id = lan0_pcie_clks[i]; else lane->clks[i].id = lan1_pciea_clks[i]; } } else { /* PCIEB */ if (lane->idx == 0) { /* i.MX8QXP */ lane->ctrl_off = 0; lane->phy_off = 0; } else { /* * On i.MX8QM, only second or third lane can be * bound to PCIEB. */ lane->ctrl_off = SZ_64K; if (lane->idx == 1) lane->phy_off = 0; else /* the third lane is bound to PCIEB */ lane->phy_off = SZ_64K; } for (i = 0; i < LANE_NUM_CLKS; i++) { if (lane->idx == 1) lane->clks[i].id = lan1_pcieb_clks[i]; else if (lane->idx == 2) lane->clks[i].id = lan2_pcieb_clks[i]; else /* i.MX8QXP only has PCIEB, idx is 0 */ lane->clks[i].id = lan0_pcie_clks[i]; } } break; case PHY_TYPE_SATA: /* On i.MX8QM, only the third lane can be bound to SATA */ lane->phy_mode = PHY_MODE_SATA; lane->ctrl_off = SZ_128K; lane->phy_off = SZ_64K; for (i = 0; i < LANE_NUM_CLKS; i++) lane->clks[i].id = lan2_sata_clks[i]; break; default: return -EINVAL; } /* Fetch clocks and enable them */ ret = devm_clk_bulk_get(dev, LANE_NUM_CLKS, lane->clks); if (ret) return ret; ret = clk_bulk_prepare_enable(LANE_NUM_CLKS, lane->clks); if (ret) return ret; /* allow the clocks to stabilize */ usleep_range(200, 500); return 0; } static int imx_hsio_exit(struct phy *phy) { struct imx_hsio_lane *lane = phy_get_drvdata(phy); clk_bulk_disable_unprepare(LANE_NUM_CLKS, lane->clks); return 0; } static void imx_hsio_pcie_phy_resets(struct phy *phy) { struct imx_hsio_lane *lane = phy_get_drvdata(phy); struct imx_hsio_priv *priv = lane->priv; regmap_clear_bits(priv->ctrl, lane->ctrl_off + HSIO_CTRL2, HSIO_BUTTON_RST_N); regmap_clear_bits(priv->ctrl, lane->ctrl_off + HSIO_CTRL2, HSIO_PERST_N); regmap_clear_bits(priv->ctrl, lane->ctrl_off + HSIO_CTRL2, HSIO_POWER_UP_RST_N); regmap_set_bits(priv->ctrl, lane->ctrl_off + HSIO_CTRL2, HSIO_BUTTON_RST_N); regmap_set_bits(priv->ctrl, lane->ctrl_off + HSIO_CTRL2, HSIO_PERST_N); regmap_set_bits(priv->ctrl, lane->ctrl_off + HSIO_CTRL2, HSIO_POWER_UP_RST_N); if (lane->idx == 1) { regmap_set_bits(priv->phy, lane->phy_off + HSIO_CTRL0, HSIO_APB_RSTN_1); regmap_set_bits(priv->phy, lane->phy_off + HSIO_CTRL0, HSIO_PIPE_RSTN_1_MASK); } else { regmap_set_bits(priv->phy, lane->phy_off + HSIO_CTRL0, HSIO_APB_RSTN_0); regmap_set_bits(priv->phy, lane->phy_off + HSIO_CTRL0, HSIO_PIPE_RSTN_0_MASK); } } static void imx_hsio_sata_phy_resets(struct phy *phy) { struct imx_hsio_lane *lane = phy_get_drvdata(phy); struct imx_hsio_priv *priv = lane->priv; /* clear PHY RST, then set it */ regmap_clear_bits(priv->ctrl, lane->ctrl_off + HSIO_CTRL0, HSIO_EPCS_PHYRESET_N); regmap_set_bits(priv->ctrl, lane->ctrl_off + HSIO_CTRL0, HSIO_EPCS_PHYRESET_N); /* CTRL RST: SET -> delay 1 us -> CLEAR -> SET */ regmap_set_bits(priv->ctrl, lane->ctrl_off + HSIO_CTRL0, HSIO_RESET_N); udelay(1); regmap_clear_bits(priv->ctrl, lane->ctrl_off + HSIO_CTRL0, HSIO_RESET_N); regmap_set_bits(priv->ctrl, lane->ctrl_off + HSIO_CTRL0, HSIO_RESET_N); } static void imx_hsio_configure_clk_pad(struct phy *phy) { bool pll = false; struct imx_hsio_lane *lane = phy_get_drvdata(phy); struct imx_hsio_priv *priv = lane->priv; if (strncmp(priv->refclk_pad, "output", 6) == 0) { pll = true; regmap_update_bits(priv->misc, HSIO_CTRL0, HSIO_IOB_A_0_TXOE | HSIO_IOB_A_0_M1M0_MASK, HSIO_IOB_A_0_TXOE | HSIO_IOB_A_0_M1M0_2); } else { regmap_update_bits(priv->misc, HSIO_CTRL0, HSIO_IOB_A_0_TXOE | HSIO_IOB_A_0_M1M0_MASK, 0); } regmap_update_bits(priv->misc, HSIO_CTRL0, HSIO_IOB_RXENA, pll ? 0 : HSIO_IOB_RXENA); regmap_update_bits(priv->misc, HSIO_CTRL0, HSIO_IOB_TXENA, pll ? HSIO_IOB_TXENA : 0); } static void imx_hsio_pre_set(struct phy *phy) { struct imx_hsio_lane *lane = phy_get_drvdata(phy); struct imx_hsio_priv *priv = lane->priv; if (strncmp(priv->hsio_cfg, "pciea-x2-pcieb", 14) == 0) { regmap_set_bits(priv->misc, HSIO_CTRL0, HSIO_PCIE_AB_SELECT); } else if (strncmp(priv->hsio_cfg, "pciea-x2-sata", 13) == 0) { regmap_set_bits(priv->misc, HSIO_CTRL0, HSIO_PHYX1_EPCS_SEL); } else if (strncmp(priv->hsio_cfg, "pciea-pcieb-sata", 16) == 0) { regmap_set_bits(priv->misc, HSIO_CTRL0, HSIO_PCIE_AB_SELECT); regmap_set_bits(priv->misc, HSIO_CTRL0, HSIO_PHYX1_EPCS_SEL); } imx_hsio_configure_clk_pad(phy); } static int imx_hsio_pcie_power_on(struct phy *phy) { int ret; u32 val, addr, cond; struct imx_hsio_lane *lane = phy_get_drvdata(phy); struct imx_hsio_priv *priv = lane->priv; imx_hsio_pcie_phy_resets(phy); /* Toggle apb_pclk to make sure PM_REQ_CORE_RST is cleared. */ clk_disable_unprepare(lane->clks[0].clk); mdelay(1); ret = clk_prepare_enable(lane->clks[0].clk); if (ret) { dev_err(priv->dev, "unable to enable phy apb_pclk\n"); return ret; } addr = lane->ctrl_off + HSIO_PCIE_STS0; cond = HSIO_PM_REQ_CORE_RST; ret = regmap_read_poll_timeout(priv->ctrl, addr, val, (val & cond) == 0, PHY_INIT_WAIT_USLEEP_MAX, PHY_INIT_WAIT_TIMEOUT); if (ret) dev_err(priv->dev, "HSIO_PM_REQ_CORE_RST is set\n"); return ret; } static int imx_hsio_sata_power_on(struct phy *phy) { int ret; u32 val, cond; struct imx_hsio_lane *lane = phy_get_drvdata(phy); struct imx_hsio_priv *priv = lane->priv; regmap_set_bits(priv->phy, lane->phy_off + HSIO_CTRL0, HSIO_APB_RSTN_0); regmap_set_bits(priv->ctrl, lane->ctrl_off + HSIO_CTRL0, HSIO_EPCS_TXDEEMP); regmap_set_bits(priv->ctrl, lane->ctrl_off + HSIO_CTRL0, HSIO_EPCS_TXDEEMP_SEL); imx_hsio_sata_phy_resets(phy); cond = HSIO_REG48_PMA_RDY; ret = read_poll_timeout(readb, val, ((val & cond) == cond), PHY_INIT_WAIT_USLEEP_MAX, PHY_INIT_WAIT_TIMEOUT, false, priv->base + HSIO_REG48_PMA_STATUS); if (ret) dev_err(priv->dev, "PHY calibration is timeout\n"); else dev_dbg(priv->dev, "PHY calibration is done\n"); return ret; } static int imx_hsio_power_on(struct phy *phy) { int ret; u32 val, cond; struct imx_hsio_lane *lane = phy_get_drvdata(phy); struct imx_hsio_priv *priv = lane->priv; scoped_guard(mutex, &priv->lock) { if (!priv->open_cnt) imx_hsio_pre_set(phy); priv->open_cnt++; } if (lane->phy_mode == PHY_MODE_PCIE) ret = imx_hsio_pcie_power_on(phy); else /* SATA */ ret = imx_hsio_sata_power_on(phy); if (ret) return ret; /* Polling to check the PHY is ready or not. */ if (lane->idx == 1) cond = HSIO_LANE1_TX_PLL_LOCK; else /* * Except the phy_off, the bit-offset of lane2 is same to lane0. * Merge the lane0 and lane2 bit-operations together. */ cond = HSIO_LANE0_TX_PLL_LOCK; ret = regmap_read_poll_timeout(priv->phy, lane->phy_off + HSIO_PHY_STS0, val, ((val & cond) == cond), PHY_INIT_WAIT_USLEEP_MAX, PHY_INIT_WAIT_TIMEOUT); if (ret) { dev_err(priv->dev, "IMX8Q PHY%d PLL lock timeout\n", lane->idx); return ret; } dev_dbg(priv->dev, "IMX8Q PHY%d PLL is locked\n", lane->idx); return ret; } static int imx_hsio_power_off(struct phy *phy) { struct imx_hsio_lane *lane = phy_get_drvdata(phy); struct imx_hsio_priv *priv = lane->priv; scoped_guard(mutex, &priv->lock) { priv->open_cnt--; if (priv->open_cnt == 0) { regmap_clear_bits(priv->misc, HSIO_CTRL0, HSIO_PCIE_AB_SELECT); regmap_clear_bits(priv->misc, HSIO_CTRL0, HSIO_PHYX1_EPCS_SEL); if (lane->phy_mode == PHY_MODE_PCIE) { regmap_clear_bits(priv->ctrl, lane->ctrl_off + HSIO_CTRL2, HSIO_BUTTON_RST_N); regmap_clear_bits(priv->ctrl, lane->ctrl_off + HSIO_CTRL2, HSIO_PERST_N); regmap_clear_bits(priv->ctrl, lane->ctrl_off + HSIO_CTRL2, HSIO_POWER_UP_RST_N); } else { regmap_clear_bits(priv->ctrl, lane->ctrl_off + HSIO_CTRL0, HSIO_EPCS_TXDEEMP); regmap_clear_bits(priv->ctrl, lane->ctrl_off + HSIO_CTRL0, HSIO_EPCS_TXDEEMP_SEL); regmap_clear_bits(priv->ctrl, lane->ctrl_off + HSIO_CTRL0, HSIO_RESET_N); } if (lane->idx == 1) { regmap_clear_bits(priv->phy, lane->phy_off + HSIO_CTRL0, HSIO_APB_RSTN_1); regmap_clear_bits(priv->phy, lane->phy_off + HSIO_CTRL0, HSIO_PIPE_RSTN_1_MASK); } else { /* * Except the phy_off, the bit-offset of lane2 is same * to lane0. Merge the lane0 and lane2 bit-operations * together. */ regmap_clear_bits(priv->phy, lane->phy_off + HSIO_CTRL0, HSIO_APB_RSTN_0); regmap_clear_bits(priv->phy, lane->phy_off + HSIO_CTRL0, HSIO_PIPE_RSTN_0_MASK); } } } return 0; } static int imx_hsio_set_mode(struct phy *phy, enum phy_mode mode, int submode) { u32 val; struct imx_hsio_lane *lane = phy_get_drvdata(phy); struct imx_hsio_priv *priv = lane->priv; if (lane->phy_mode != mode) return -EINVAL; val = (mode == PHY_MODE_PCIE) ? HSIO_MODE_PCIE : HSIO_MODE_SATA; val = FIELD_PREP(HSIO_MODE_MASK, val); regmap_update_bits(priv->phy, lane->phy_off + HSIO_CTRL0, HSIO_MODE_MASK, val); switch (submode) { case PHY_MODE_PCIE_RC: val = FIELD_PREP(HSIO_DEVICE_TYPE_MASK, PCI_EXP_TYPE_ROOT_PORT); break; case PHY_MODE_PCIE_EP: val = FIELD_PREP(HSIO_DEVICE_TYPE_MASK, PCI_EXP_TYPE_ENDPOINT); break; default: /* Support only PCIe EP and RC now. */ return 0; } if (submode) regmap_update_bits(priv->ctrl, lane->ctrl_off + HSIO_CTRL0, HSIO_DEVICE_TYPE_MASK, val); return 0; } static int imx_hsio_set_speed(struct phy *phy, int speed) { struct imx_hsio_lane *lane = phy_get_drvdata(phy); struct imx_hsio_priv *priv = lane->priv; regmap_update_bits(priv->ctrl, lane->ctrl_off + HSIO_CTRL2, HSIO_LTSSM_ENABLE, speed ? HSIO_LTSSM_ENABLE : 0); return 0; } static const struct phy_ops imx_hsio_ops = { .init = imx_hsio_init, .exit = imx_hsio_exit, .power_on = imx_hsio_power_on, .power_off = imx_hsio_power_off, .set_mode = imx_hsio_set_mode, .set_speed = imx_hsio_set_speed, .owner = THIS_MODULE, }; static const struct imx_hsio_drvdata imx8qxp_hsio_drvdata = { .lane_num = 0x1, }; static const struct imx_hsio_drvdata imx8qm_hsio_drvdata = { .lane_num = 0x3, }; static const struct of_device_id imx_hsio_of_match[] = { {.compatible = "fsl,imx8qm-hsio", .data = &imx8qm_hsio_drvdata}, {.compatible = "fsl,imx8qxp-hsio", .data = &imx8qxp_hsio_drvdata}, { }, }; MODULE_DEVICE_TABLE(of, imx_hsio_of_match); static struct phy *imx_hsio_xlate(struct device *dev, const struct of_phandle_args *args) { struct imx_hsio_priv *priv = dev_get_drvdata(dev); int idx = args->args[0]; int phy_type = args->args[1]; int ctrl_index = args->args[2]; if (idx < 0 || idx >= priv->drvdata->lane_num) return ERR_PTR(-EINVAL); priv->lane[idx].idx = idx; priv->lane[idx].phy_type = phy_type; priv->lane[idx].ctrl_index = ctrl_index; return priv->lane[idx].phy; } static int imx_hsio_probe(struct platform_device *pdev) { int i; void __iomem *off; struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; struct imx_hsio_priv *priv; struct phy_provider *provider; priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; priv->dev = &pdev->dev; priv->drvdata = of_device_get_match_data(dev); /* Get HSIO configuration mode */ if (of_property_read_string(np, "fsl,hsio-cfg", &priv->hsio_cfg)) priv->hsio_cfg = "pciea-pcieb-sata"; /* Get PHY refclk pad mode */ if (of_property_read_string(np, "fsl,refclk-pad-mode", &priv->refclk_pad)) priv->refclk_pad = NULL; priv->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(priv->base)) return PTR_ERR(priv->base); off = devm_platform_ioremap_resource_byname(pdev, "phy"); priv->phy = devm_regmap_init_mmio(dev, off, ®map_config); if (IS_ERR(priv->phy)) return dev_err_probe(dev, PTR_ERR(priv->phy), "unable to find phy csr registers\n"); off = devm_platform_ioremap_resource_byname(pdev, "ctrl"); priv->ctrl = devm_regmap_init_mmio(dev, off, ®map_config); if (IS_ERR(priv->ctrl)) return dev_err_probe(dev, PTR_ERR(priv->ctrl), "unable to find ctrl csr registers\n"); off = devm_platform_ioremap_resource_byname(pdev, "misc"); priv->misc = devm_regmap_init_mmio(dev, off, ®map_config); if (IS_ERR(priv->misc)) return dev_err_probe(dev, PTR_ERR(priv->misc), "unable to find misc csr registers\n"); for (i = 0; i < priv->drvdata->lane_num; i++) { struct imx_hsio_lane *lane = &priv->lane[i]; struct phy *phy; phy = devm_phy_create(&pdev->dev, NULL, &imx_hsio_ops); if (IS_ERR(phy)) return PTR_ERR(phy); lane->priv = priv; lane->phy = phy; lane->idx = i; phy_set_drvdata(phy, lane); } dev_set_drvdata(dev, priv); dev_set_drvdata(&pdev->dev, priv); provider = devm_of_phy_provider_register(&pdev->dev, imx_hsio_xlate); return PTR_ERR_OR_ZERO(provider); } static struct platform_driver imx_hsio_driver = { .probe = imx_hsio_probe, .driver = { .name = "imx8qm-hsio-phy", .of_match_table = imx_hsio_of_match, } }; module_platform_driver(imx_hsio_driver); MODULE_DESCRIPTION("FSL IMX8QM HSIO SERDES PHY driver"); MODULE_LICENSE("GPL");
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1