xref: /linux/drivers/phy/freescale/phy-fsl-imx8qm-hsio.c (revision a1ff5a7d78a036d6c2178ee5acd6ba4946243800)
182c56b6dSRichard Zhu // SPDX-License-Identifier: GPL-2.0+
282c56b6dSRichard Zhu /*
382c56b6dSRichard Zhu  * Copyright 2024 NXP
482c56b6dSRichard Zhu  */
582c56b6dSRichard Zhu 
6*aefa036bSNathan Chancellor #include <linux/bitfield.h>
782c56b6dSRichard Zhu #include <linux/clk.h>
882c56b6dSRichard Zhu #include <linux/delay.h>
982c56b6dSRichard Zhu #include <linux/io.h>
1082c56b6dSRichard Zhu #include <linux/iopoll.h>
1182c56b6dSRichard Zhu #include <linux/module.h>
1282c56b6dSRichard Zhu #include <linux/of.h>
1382c56b6dSRichard Zhu #include <linux/pci_regs.h>
1482c56b6dSRichard Zhu #include <linux/phy/phy.h>
1582c56b6dSRichard Zhu #include <linux/phy/pcie.h>
1682c56b6dSRichard Zhu #include <linux/platform_device.h>
1782c56b6dSRichard Zhu #include <linux/regmap.h>
1882c56b6dSRichard Zhu 
1982c56b6dSRichard Zhu #include <dt-bindings/phy/phy.h>
2082c56b6dSRichard Zhu #include <dt-bindings/phy/phy-imx8-pcie.h>
2182c56b6dSRichard Zhu 
2282c56b6dSRichard Zhu #define MAX_NUM_LANE	3
2382c56b6dSRichard Zhu #define LANE_NUM_CLKS	5
2482c56b6dSRichard Zhu 
2582c56b6dSRichard Zhu /* Parameters for the waiting for PCIe PHY PLL to lock */
2682c56b6dSRichard Zhu #define PHY_INIT_WAIT_USLEEP_MAX	10
2782c56b6dSRichard Zhu #define PHY_INIT_WAIT_TIMEOUT		(1000 * PHY_INIT_WAIT_USLEEP_MAX)
2882c56b6dSRichard Zhu 
2982c56b6dSRichard Zhu /* i.MX8Q HSIO registers */
3082c56b6dSRichard Zhu #define HSIO_CTRL0			0x0
3182c56b6dSRichard Zhu #define HSIO_APB_RSTN_0			BIT(0)
3282c56b6dSRichard Zhu #define HSIO_APB_RSTN_1			BIT(1)
3382c56b6dSRichard Zhu #define HSIO_PIPE_RSTN_0_MASK		GENMASK(25, 24)
3482c56b6dSRichard Zhu #define HSIO_PIPE_RSTN_1_MASK		GENMASK(27, 26)
3582c56b6dSRichard Zhu #define HSIO_MODE_MASK			GENMASK(20, 17)
3682c56b6dSRichard Zhu #define HSIO_MODE_PCIE			0x0
3782c56b6dSRichard Zhu #define HSIO_MODE_SATA			0x4
3882c56b6dSRichard Zhu #define HSIO_DEVICE_TYPE_MASK		GENMASK(27, 24)
3982c56b6dSRichard Zhu #define HSIO_EPCS_TXDEEMP		BIT(5)
4082c56b6dSRichard Zhu #define HSIO_EPCS_TXDEEMP_SEL		BIT(6)
4182c56b6dSRichard Zhu #define HSIO_EPCS_PHYRESET_N		BIT(7)
4282c56b6dSRichard Zhu #define HSIO_RESET_N			BIT(12)
4382c56b6dSRichard Zhu 
4482c56b6dSRichard Zhu #define HSIO_IOB_RXENA			BIT(0)
4582c56b6dSRichard Zhu #define HSIO_IOB_TXENA			BIT(1)
4682c56b6dSRichard Zhu #define HSIO_IOB_A_0_TXOE		BIT(2)
4782c56b6dSRichard Zhu #define HSIO_IOB_A_0_M1M0_2		BIT(4)
4882c56b6dSRichard Zhu #define HSIO_IOB_A_0_M1M0_MASK		GENMASK(4, 3)
4982c56b6dSRichard Zhu #define HSIO_PHYX1_EPCS_SEL		BIT(12)
5082c56b6dSRichard Zhu #define HSIO_PCIE_AB_SELECT		BIT(13)
5182c56b6dSRichard Zhu 
5282c56b6dSRichard Zhu #define HSIO_PHY_STS0			0x4
5382c56b6dSRichard Zhu #define HSIO_LANE0_TX_PLL_LOCK		BIT(4)
5482c56b6dSRichard Zhu #define HSIO_LANE1_TX_PLL_LOCK		BIT(12)
5582c56b6dSRichard Zhu 
5682c56b6dSRichard Zhu #define HSIO_CTRL2			0x8
5782c56b6dSRichard Zhu #define HSIO_LTSSM_ENABLE		BIT(4)
5882c56b6dSRichard Zhu #define HSIO_BUTTON_RST_N		BIT(21)
5982c56b6dSRichard Zhu #define HSIO_PERST_N			BIT(22)
6082c56b6dSRichard Zhu #define HSIO_POWER_UP_RST_N		BIT(23)
6182c56b6dSRichard Zhu 
6282c56b6dSRichard Zhu #define HSIO_PCIE_STS0			0xc
6382c56b6dSRichard Zhu #define HSIO_PM_REQ_CORE_RST		BIT(19)
6482c56b6dSRichard Zhu 
6582c56b6dSRichard Zhu #define HSIO_REG48_PMA_STATUS		0x30
6682c56b6dSRichard Zhu #define HSIO_REG48_PMA_RDY		BIT(7)
6782c56b6dSRichard Zhu 
6882c56b6dSRichard Zhu struct imx_hsio_drvdata {
6982c56b6dSRichard Zhu 	int lane_num;
7082c56b6dSRichard Zhu };
7182c56b6dSRichard Zhu 
7282c56b6dSRichard Zhu struct imx_hsio_lane {
7382c56b6dSRichard Zhu 	u32 ctrl_index;
7482c56b6dSRichard Zhu 	u32 ctrl_off;
7582c56b6dSRichard Zhu 	u32 idx;
7682c56b6dSRichard Zhu 	u32 phy_off;
7782c56b6dSRichard Zhu 	u32 phy_type;
7882c56b6dSRichard Zhu 	const char * const *clk_names;
7982c56b6dSRichard Zhu 	struct clk_bulk_data clks[LANE_NUM_CLKS];
8082c56b6dSRichard Zhu 	struct imx_hsio_priv *priv;
8182c56b6dSRichard Zhu 	struct phy *phy;
8282c56b6dSRichard Zhu 	enum phy_mode phy_mode;
8382c56b6dSRichard Zhu };
8482c56b6dSRichard Zhu 
8582c56b6dSRichard Zhu struct imx_hsio_priv {
8682c56b6dSRichard Zhu 	void __iomem *base;
8782c56b6dSRichard Zhu 	struct device *dev;
8882c56b6dSRichard Zhu 	struct mutex lock;
8982c56b6dSRichard Zhu 	const char *hsio_cfg;
9082c56b6dSRichard Zhu 	const char *refclk_pad;
9182c56b6dSRichard Zhu 	u32 open_cnt;
9282c56b6dSRichard Zhu 	struct regmap *phy;
9382c56b6dSRichard Zhu 	struct regmap *ctrl;
9482c56b6dSRichard Zhu 	struct regmap *misc;
9582c56b6dSRichard Zhu 	const struct imx_hsio_drvdata *drvdata;
9682c56b6dSRichard Zhu 	struct imx_hsio_lane lane[MAX_NUM_LANE];
9782c56b6dSRichard Zhu };
9882c56b6dSRichard Zhu 
9982c56b6dSRichard Zhu static const char * const lan0_pcie_clks[] = {"apb_pclk0", "pclk0", "ctl0_crr",
10082c56b6dSRichard Zhu 					      "phy0_crr", "misc_crr"};
10182c56b6dSRichard Zhu static const char * const lan1_pciea_clks[] = {"apb_pclk1", "pclk1", "ctl0_crr",
10282c56b6dSRichard Zhu 					       "phy0_crr", "misc_crr"};
10382c56b6dSRichard Zhu static const char * const lan1_pcieb_clks[] = {"apb_pclk1", "pclk1", "ctl1_crr",
10482c56b6dSRichard Zhu 					       "phy0_crr", "misc_crr"};
10582c56b6dSRichard Zhu static const char * const lan2_pcieb_clks[] = {"apb_pclk2", "pclk2", "ctl1_crr",
10682c56b6dSRichard Zhu 					       "phy1_crr", "misc_crr"};
10782c56b6dSRichard Zhu static const char * const lan2_sata_clks[] = {"pclk2", "epcs_tx", "epcs_rx",
10882c56b6dSRichard Zhu 					      "phy1_crr", "misc_crr"};
10982c56b6dSRichard Zhu 
11082c56b6dSRichard Zhu static const struct regmap_config regmap_config = {
11182c56b6dSRichard Zhu 	.reg_bits = 32,
11282c56b6dSRichard Zhu 	.val_bits = 32,
11382c56b6dSRichard Zhu 	.reg_stride = 4,
11482c56b6dSRichard Zhu };
11582c56b6dSRichard Zhu 
imx_hsio_init(struct phy * phy)11682c56b6dSRichard Zhu static int imx_hsio_init(struct phy *phy)
11782c56b6dSRichard Zhu {
11882c56b6dSRichard Zhu 	int ret, i;
11982c56b6dSRichard Zhu 	struct imx_hsio_lane *lane = phy_get_drvdata(phy);
12082c56b6dSRichard Zhu 	struct imx_hsio_priv *priv = lane->priv;
12182c56b6dSRichard Zhu 	struct device *dev = priv->dev;
12282c56b6dSRichard Zhu 
12382c56b6dSRichard Zhu 	/* Assign clocks refer to different modes */
12482c56b6dSRichard Zhu 	switch (lane->phy_type) {
12582c56b6dSRichard Zhu 	case PHY_TYPE_PCIE:
12682c56b6dSRichard Zhu 		lane->phy_mode = PHY_MODE_PCIE;
12782c56b6dSRichard Zhu 		if (lane->ctrl_index == 0) { /* PCIEA */
12882c56b6dSRichard Zhu 			lane->ctrl_off = 0;
12982c56b6dSRichard Zhu 			lane->phy_off = 0;
13082c56b6dSRichard Zhu 
13182c56b6dSRichard Zhu 			for (i = 0; i < LANE_NUM_CLKS; i++) {
13282c56b6dSRichard Zhu 				if (lane->idx == 0)
13382c56b6dSRichard Zhu 					lane->clks[i].id = lan0_pcie_clks[i];
13482c56b6dSRichard Zhu 				else
13582c56b6dSRichard Zhu 					lane->clks[i].id = lan1_pciea_clks[i];
13682c56b6dSRichard Zhu 			}
13782c56b6dSRichard Zhu 		} else { /* PCIEB */
13882c56b6dSRichard Zhu 			if (lane->idx == 0) { /* i.MX8QXP */
13982c56b6dSRichard Zhu 				lane->ctrl_off = 0;
14082c56b6dSRichard Zhu 				lane->phy_off = 0;
14182c56b6dSRichard Zhu 			} else {
14282c56b6dSRichard Zhu 				/*
14382c56b6dSRichard Zhu 				 * On i.MX8QM, only second or third lane can be
14482c56b6dSRichard Zhu 				 * bound to PCIEB.
14582c56b6dSRichard Zhu 				 */
14682c56b6dSRichard Zhu 				lane->ctrl_off = SZ_64K;
14782c56b6dSRichard Zhu 				if (lane->idx == 1)
14882c56b6dSRichard Zhu 					lane->phy_off = 0;
14982c56b6dSRichard Zhu 				else /* the third lane is bound to PCIEB */
15082c56b6dSRichard Zhu 					lane->phy_off = SZ_64K;
15182c56b6dSRichard Zhu 			}
15282c56b6dSRichard Zhu 
15382c56b6dSRichard Zhu 			for (i = 0; i < LANE_NUM_CLKS; i++) {
15482c56b6dSRichard Zhu 				if (lane->idx == 1)
15582c56b6dSRichard Zhu 					lane->clks[i].id = lan1_pcieb_clks[i];
15682c56b6dSRichard Zhu 				else if (lane->idx == 2)
15782c56b6dSRichard Zhu 					lane->clks[i].id = lan2_pcieb_clks[i];
15882c56b6dSRichard Zhu 				else /* i.MX8QXP only has PCIEB, idx is 0 */
15982c56b6dSRichard Zhu 					lane->clks[i].id = lan0_pcie_clks[i];
16082c56b6dSRichard Zhu 			}
16182c56b6dSRichard Zhu 		}
16282c56b6dSRichard Zhu 		break;
16382c56b6dSRichard Zhu 	case PHY_TYPE_SATA:
16482c56b6dSRichard Zhu 		/* On i.MX8QM, only the third lane can be bound to SATA */
16582c56b6dSRichard Zhu 		lane->phy_mode = PHY_MODE_SATA;
16682c56b6dSRichard Zhu 		lane->ctrl_off = SZ_128K;
16782c56b6dSRichard Zhu 		lane->phy_off = SZ_64K;
16882c56b6dSRichard Zhu 
16982c56b6dSRichard Zhu 		for (i = 0; i < LANE_NUM_CLKS; i++)
17082c56b6dSRichard Zhu 			lane->clks[i].id = lan2_sata_clks[i];
17182c56b6dSRichard Zhu 		break;
17282c56b6dSRichard Zhu 	default:
17382c56b6dSRichard Zhu 		return -EINVAL;
17482c56b6dSRichard Zhu 	}
17582c56b6dSRichard Zhu 
17682c56b6dSRichard Zhu 	/* Fetch clocks and enable them */
17782c56b6dSRichard Zhu 	ret = devm_clk_bulk_get(dev, LANE_NUM_CLKS, lane->clks);
17882c56b6dSRichard Zhu 	if (ret)
17982c56b6dSRichard Zhu 		return ret;
18082c56b6dSRichard Zhu 	ret = clk_bulk_prepare_enable(LANE_NUM_CLKS, lane->clks);
18182c56b6dSRichard Zhu 	if (ret)
18282c56b6dSRichard Zhu 		return ret;
18382c56b6dSRichard Zhu 
18482c56b6dSRichard Zhu 	/* allow the clocks to stabilize */
18582c56b6dSRichard Zhu 	usleep_range(200, 500);
18682c56b6dSRichard Zhu 	return 0;
18782c56b6dSRichard Zhu }
18882c56b6dSRichard Zhu 
imx_hsio_exit(struct phy * phy)18982c56b6dSRichard Zhu static int imx_hsio_exit(struct phy *phy)
19082c56b6dSRichard Zhu {
19182c56b6dSRichard Zhu 	struct imx_hsio_lane *lane = phy_get_drvdata(phy);
19282c56b6dSRichard Zhu 
19382c56b6dSRichard Zhu 	clk_bulk_disable_unprepare(LANE_NUM_CLKS, lane->clks);
19482c56b6dSRichard Zhu 
19582c56b6dSRichard Zhu 	return 0;
19682c56b6dSRichard Zhu }
19782c56b6dSRichard Zhu 
imx_hsio_pcie_phy_resets(struct phy * phy)19882c56b6dSRichard Zhu static void imx_hsio_pcie_phy_resets(struct phy *phy)
19982c56b6dSRichard Zhu {
20082c56b6dSRichard Zhu 	struct imx_hsio_lane *lane = phy_get_drvdata(phy);
20182c56b6dSRichard Zhu 	struct imx_hsio_priv *priv = lane->priv;
20282c56b6dSRichard Zhu 
20382c56b6dSRichard Zhu 	regmap_clear_bits(priv->ctrl, lane->ctrl_off + HSIO_CTRL2,
20482c56b6dSRichard Zhu 			  HSIO_BUTTON_RST_N);
20582c56b6dSRichard Zhu 	regmap_clear_bits(priv->ctrl, lane->ctrl_off + HSIO_CTRL2,
20682c56b6dSRichard Zhu 			  HSIO_PERST_N);
20782c56b6dSRichard Zhu 	regmap_clear_bits(priv->ctrl, lane->ctrl_off + HSIO_CTRL2,
20882c56b6dSRichard Zhu 			  HSIO_POWER_UP_RST_N);
20982c56b6dSRichard Zhu 	regmap_set_bits(priv->ctrl, lane->ctrl_off + HSIO_CTRL2,
21082c56b6dSRichard Zhu 			HSIO_BUTTON_RST_N);
21182c56b6dSRichard Zhu 	regmap_set_bits(priv->ctrl, lane->ctrl_off + HSIO_CTRL2,
21282c56b6dSRichard Zhu 			HSIO_PERST_N);
21382c56b6dSRichard Zhu 	regmap_set_bits(priv->ctrl, lane->ctrl_off + HSIO_CTRL2,
21482c56b6dSRichard Zhu 			HSIO_POWER_UP_RST_N);
21582c56b6dSRichard Zhu 
21682c56b6dSRichard Zhu 	if (lane->idx == 1) {
21782c56b6dSRichard Zhu 		regmap_set_bits(priv->phy, lane->phy_off + HSIO_CTRL0,
21882c56b6dSRichard Zhu 				HSIO_APB_RSTN_1);
21982c56b6dSRichard Zhu 		regmap_set_bits(priv->phy, lane->phy_off + HSIO_CTRL0,
22082c56b6dSRichard Zhu 				HSIO_PIPE_RSTN_1_MASK);
22182c56b6dSRichard Zhu 	} else {
22282c56b6dSRichard Zhu 		regmap_set_bits(priv->phy, lane->phy_off + HSIO_CTRL0,
22382c56b6dSRichard Zhu 				HSIO_APB_RSTN_0);
22482c56b6dSRichard Zhu 		regmap_set_bits(priv->phy, lane->phy_off + HSIO_CTRL0,
22582c56b6dSRichard Zhu 				HSIO_PIPE_RSTN_0_MASK);
22682c56b6dSRichard Zhu 	}
22782c56b6dSRichard Zhu }
22882c56b6dSRichard Zhu 
imx_hsio_sata_phy_resets(struct phy * phy)22982c56b6dSRichard Zhu static void imx_hsio_sata_phy_resets(struct phy *phy)
23082c56b6dSRichard Zhu {
23182c56b6dSRichard Zhu 	struct imx_hsio_lane *lane = phy_get_drvdata(phy);
23282c56b6dSRichard Zhu 	struct imx_hsio_priv *priv = lane->priv;
23382c56b6dSRichard Zhu 
23482c56b6dSRichard Zhu 	/* clear PHY RST, then set it */
23582c56b6dSRichard Zhu 	regmap_clear_bits(priv->ctrl, lane->ctrl_off + HSIO_CTRL0,
23682c56b6dSRichard Zhu 			  HSIO_EPCS_PHYRESET_N);
23782c56b6dSRichard Zhu 	regmap_set_bits(priv->ctrl, lane->ctrl_off + HSIO_CTRL0,
23882c56b6dSRichard Zhu 			HSIO_EPCS_PHYRESET_N);
23982c56b6dSRichard Zhu 
24082c56b6dSRichard Zhu 	/* CTRL RST: SET -> delay 1 us -> CLEAR -> SET */
24182c56b6dSRichard Zhu 	regmap_set_bits(priv->ctrl, lane->ctrl_off + HSIO_CTRL0, HSIO_RESET_N);
24282c56b6dSRichard Zhu 	udelay(1);
24382c56b6dSRichard Zhu 	regmap_clear_bits(priv->ctrl, lane->ctrl_off + HSIO_CTRL0,
24482c56b6dSRichard Zhu 			  HSIO_RESET_N);
24582c56b6dSRichard Zhu 	regmap_set_bits(priv->ctrl, lane->ctrl_off + HSIO_CTRL0, HSIO_RESET_N);
24682c56b6dSRichard Zhu }
24782c56b6dSRichard Zhu 
imx_hsio_configure_clk_pad(struct phy * phy)24882c56b6dSRichard Zhu static void imx_hsio_configure_clk_pad(struct phy *phy)
24982c56b6dSRichard Zhu {
25082c56b6dSRichard Zhu 	bool pll = false;
25182c56b6dSRichard Zhu 	struct imx_hsio_lane *lane = phy_get_drvdata(phy);
25282c56b6dSRichard Zhu 	struct imx_hsio_priv *priv = lane->priv;
25382c56b6dSRichard Zhu 
25482c56b6dSRichard Zhu 	if (strncmp(priv->refclk_pad, "output", 6) == 0) {
25582c56b6dSRichard Zhu 		pll = true;
25682c56b6dSRichard Zhu 		regmap_update_bits(priv->misc, HSIO_CTRL0,
25782c56b6dSRichard Zhu 				   HSIO_IOB_A_0_TXOE | HSIO_IOB_A_0_M1M0_MASK,
25882c56b6dSRichard Zhu 				   HSIO_IOB_A_0_TXOE | HSIO_IOB_A_0_M1M0_2);
25982c56b6dSRichard Zhu 	} else {
26082c56b6dSRichard Zhu 		regmap_update_bits(priv->misc, HSIO_CTRL0,
26182c56b6dSRichard Zhu 				   HSIO_IOB_A_0_TXOE | HSIO_IOB_A_0_M1M0_MASK,
26282c56b6dSRichard Zhu 				   0);
26382c56b6dSRichard Zhu 	}
26482c56b6dSRichard Zhu 
26582c56b6dSRichard Zhu 	regmap_update_bits(priv->misc, HSIO_CTRL0, HSIO_IOB_RXENA,
26682c56b6dSRichard Zhu 			   pll ? 0 : HSIO_IOB_RXENA);
26782c56b6dSRichard Zhu 	regmap_update_bits(priv->misc, HSIO_CTRL0, HSIO_IOB_TXENA,
26882c56b6dSRichard Zhu 			   pll ? HSIO_IOB_TXENA : 0);
26982c56b6dSRichard Zhu }
27082c56b6dSRichard Zhu 
imx_hsio_pre_set(struct phy * phy)27182c56b6dSRichard Zhu static void imx_hsio_pre_set(struct phy *phy)
27282c56b6dSRichard Zhu {
27382c56b6dSRichard Zhu 	struct imx_hsio_lane *lane = phy_get_drvdata(phy);
27482c56b6dSRichard Zhu 	struct imx_hsio_priv *priv = lane->priv;
27582c56b6dSRichard Zhu 
27682c56b6dSRichard Zhu 	if (strncmp(priv->hsio_cfg, "pciea-x2-pcieb", 14) == 0) {
27782c56b6dSRichard Zhu 		regmap_set_bits(priv->misc, HSIO_CTRL0, HSIO_PCIE_AB_SELECT);
27882c56b6dSRichard Zhu 	} else if (strncmp(priv->hsio_cfg, "pciea-x2-sata", 13) == 0) {
27982c56b6dSRichard Zhu 		regmap_set_bits(priv->misc, HSIO_CTRL0, HSIO_PHYX1_EPCS_SEL);
28082c56b6dSRichard Zhu 	} else if (strncmp(priv->hsio_cfg, "pciea-pcieb-sata", 16) == 0) {
28182c56b6dSRichard Zhu 		regmap_set_bits(priv->misc, HSIO_CTRL0, HSIO_PCIE_AB_SELECT);
28282c56b6dSRichard Zhu 		regmap_set_bits(priv->misc, HSIO_CTRL0, HSIO_PHYX1_EPCS_SEL);
28382c56b6dSRichard Zhu 	}
28482c56b6dSRichard Zhu 
28582c56b6dSRichard Zhu 	imx_hsio_configure_clk_pad(phy);
28682c56b6dSRichard Zhu }
28782c56b6dSRichard Zhu 
imx_hsio_pcie_power_on(struct phy * phy)28882c56b6dSRichard Zhu static int imx_hsio_pcie_power_on(struct phy *phy)
28982c56b6dSRichard Zhu {
29082c56b6dSRichard Zhu 	int ret;
29182c56b6dSRichard Zhu 	u32 val, addr, cond;
29282c56b6dSRichard Zhu 	struct imx_hsio_lane *lane = phy_get_drvdata(phy);
29382c56b6dSRichard Zhu 	struct imx_hsio_priv *priv = lane->priv;
29482c56b6dSRichard Zhu 
29582c56b6dSRichard Zhu 	imx_hsio_pcie_phy_resets(phy);
29682c56b6dSRichard Zhu 
29782c56b6dSRichard Zhu 	/* Toggle apb_pclk to make sure PM_REQ_CORE_RST is cleared. */
29882c56b6dSRichard Zhu 	clk_disable_unprepare(lane->clks[0].clk);
29982c56b6dSRichard Zhu 	mdelay(1);
30082c56b6dSRichard Zhu 	ret = clk_prepare_enable(lane->clks[0].clk);
30182c56b6dSRichard Zhu 	if (ret) {
30282c56b6dSRichard Zhu 		dev_err(priv->dev, "unable to enable phy apb_pclk\n");
30382c56b6dSRichard Zhu 		return ret;
30482c56b6dSRichard Zhu 	}
30582c56b6dSRichard Zhu 
30682c56b6dSRichard Zhu 	addr = lane->ctrl_off + HSIO_PCIE_STS0;
30782c56b6dSRichard Zhu 	cond = HSIO_PM_REQ_CORE_RST;
30882c56b6dSRichard Zhu 	ret = regmap_read_poll_timeout(priv->ctrl, addr, val,
30982c56b6dSRichard Zhu 				       (val & cond) == 0,
31082c56b6dSRichard Zhu 				       PHY_INIT_WAIT_USLEEP_MAX,
31182c56b6dSRichard Zhu 				       PHY_INIT_WAIT_TIMEOUT);
31282c56b6dSRichard Zhu 	if (ret)
31382c56b6dSRichard Zhu 		dev_err(priv->dev, "HSIO_PM_REQ_CORE_RST is set\n");
31482c56b6dSRichard Zhu 	return ret;
31582c56b6dSRichard Zhu }
31682c56b6dSRichard Zhu 
imx_hsio_sata_power_on(struct phy * phy)31782c56b6dSRichard Zhu static int imx_hsio_sata_power_on(struct phy *phy)
31882c56b6dSRichard Zhu {
31982c56b6dSRichard Zhu 	int ret;
32082c56b6dSRichard Zhu 	u32 val, cond;
32182c56b6dSRichard Zhu 	struct imx_hsio_lane *lane = phy_get_drvdata(phy);
32282c56b6dSRichard Zhu 	struct imx_hsio_priv *priv = lane->priv;
32382c56b6dSRichard Zhu 
32482c56b6dSRichard Zhu 	regmap_set_bits(priv->phy, lane->phy_off + HSIO_CTRL0, HSIO_APB_RSTN_0);
32582c56b6dSRichard Zhu 	regmap_set_bits(priv->ctrl, lane->ctrl_off + HSIO_CTRL0,
32682c56b6dSRichard Zhu 			HSIO_EPCS_TXDEEMP);
32782c56b6dSRichard Zhu 	regmap_set_bits(priv->ctrl, lane->ctrl_off + HSIO_CTRL0,
32882c56b6dSRichard Zhu 			HSIO_EPCS_TXDEEMP_SEL);
32982c56b6dSRichard Zhu 
33082c56b6dSRichard Zhu 	imx_hsio_sata_phy_resets(phy);
33182c56b6dSRichard Zhu 
33282c56b6dSRichard Zhu 	cond = HSIO_REG48_PMA_RDY;
33382c56b6dSRichard Zhu 	ret = read_poll_timeout(readb, val, ((val & cond) == cond),
33482c56b6dSRichard Zhu 				PHY_INIT_WAIT_USLEEP_MAX,
33582c56b6dSRichard Zhu 				PHY_INIT_WAIT_TIMEOUT, false,
33682c56b6dSRichard Zhu 				priv->base + HSIO_REG48_PMA_STATUS);
33782c56b6dSRichard Zhu 	if (ret)
33882c56b6dSRichard Zhu 		dev_err(priv->dev, "PHY calibration is timeout\n");
33982c56b6dSRichard Zhu 	else
34082c56b6dSRichard Zhu 		dev_dbg(priv->dev, "PHY calibration is done\n");
34182c56b6dSRichard Zhu 
34282c56b6dSRichard Zhu 	return ret;
34382c56b6dSRichard Zhu }
34482c56b6dSRichard Zhu 
imx_hsio_power_on(struct phy * phy)34582c56b6dSRichard Zhu static int imx_hsio_power_on(struct phy *phy)
34682c56b6dSRichard Zhu {
34782c56b6dSRichard Zhu 	int ret;
34882c56b6dSRichard Zhu 	u32 val, cond;
34982c56b6dSRichard Zhu 	struct imx_hsio_lane *lane = phy_get_drvdata(phy);
35082c56b6dSRichard Zhu 	struct imx_hsio_priv *priv = lane->priv;
35182c56b6dSRichard Zhu 
35282c56b6dSRichard Zhu 	scoped_guard(mutex, &priv->lock) {
35382c56b6dSRichard Zhu 		if (!priv->open_cnt)
35482c56b6dSRichard Zhu 			imx_hsio_pre_set(phy);
35582c56b6dSRichard Zhu 		priv->open_cnt++;
35682c56b6dSRichard Zhu 	}
35782c56b6dSRichard Zhu 
35882c56b6dSRichard Zhu 	if (lane->phy_mode == PHY_MODE_PCIE)
35982c56b6dSRichard Zhu 		ret = imx_hsio_pcie_power_on(phy);
36082c56b6dSRichard Zhu 	else /* SATA */
36182c56b6dSRichard Zhu 		ret = imx_hsio_sata_power_on(phy);
36282c56b6dSRichard Zhu 	if (ret)
36382c56b6dSRichard Zhu 		return ret;
36482c56b6dSRichard Zhu 
36582c56b6dSRichard Zhu 	/* Polling to check the PHY is ready or not. */
36682c56b6dSRichard Zhu 	if (lane->idx == 1)
36782c56b6dSRichard Zhu 		cond = HSIO_LANE1_TX_PLL_LOCK;
36882c56b6dSRichard Zhu 	else
36982c56b6dSRichard Zhu 		/*
37082c56b6dSRichard Zhu 		 * Except the phy_off, the bit-offset of lane2 is same to lane0.
37182c56b6dSRichard Zhu 		 * Merge the lane0 and lane2 bit-operations together.
37282c56b6dSRichard Zhu 		 */
37382c56b6dSRichard Zhu 		cond = HSIO_LANE0_TX_PLL_LOCK;
37482c56b6dSRichard Zhu 
37582c56b6dSRichard Zhu 	ret = regmap_read_poll_timeout(priv->phy, lane->phy_off + HSIO_PHY_STS0,
37682c56b6dSRichard Zhu 				       val, ((val & cond) == cond),
37782c56b6dSRichard Zhu 				       PHY_INIT_WAIT_USLEEP_MAX,
37882c56b6dSRichard Zhu 				       PHY_INIT_WAIT_TIMEOUT);
37982c56b6dSRichard Zhu 	if (ret) {
38082c56b6dSRichard Zhu 		dev_err(priv->dev, "IMX8Q PHY%d PLL lock timeout\n", lane->idx);
38182c56b6dSRichard Zhu 		return ret;
38282c56b6dSRichard Zhu 	}
38382c56b6dSRichard Zhu 	dev_dbg(priv->dev, "IMX8Q PHY%d PLL is locked\n", lane->idx);
38482c56b6dSRichard Zhu 
38582c56b6dSRichard Zhu 	return ret;
38682c56b6dSRichard Zhu }
38782c56b6dSRichard Zhu 
imx_hsio_power_off(struct phy * phy)38882c56b6dSRichard Zhu static int imx_hsio_power_off(struct phy *phy)
38982c56b6dSRichard Zhu {
39082c56b6dSRichard Zhu 	struct imx_hsio_lane *lane = phy_get_drvdata(phy);
39182c56b6dSRichard Zhu 	struct imx_hsio_priv *priv = lane->priv;
39282c56b6dSRichard Zhu 
39382c56b6dSRichard Zhu 	scoped_guard(mutex, &priv->lock) {
39482c56b6dSRichard Zhu 		priv->open_cnt--;
39582c56b6dSRichard Zhu 		if (priv->open_cnt == 0) {
39682c56b6dSRichard Zhu 			regmap_clear_bits(priv->misc, HSIO_CTRL0,
39782c56b6dSRichard Zhu 					  HSIO_PCIE_AB_SELECT);
39882c56b6dSRichard Zhu 			regmap_clear_bits(priv->misc, HSIO_CTRL0,
39982c56b6dSRichard Zhu 					  HSIO_PHYX1_EPCS_SEL);
40082c56b6dSRichard Zhu 
40182c56b6dSRichard Zhu 			if (lane->phy_mode == PHY_MODE_PCIE) {
40282c56b6dSRichard Zhu 				regmap_clear_bits(priv->ctrl,
40382c56b6dSRichard Zhu 						  lane->ctrl_off + HSIO_CTRL2,
40482c56b6dSRichard Zhu 						  HSIO_BUTTON_RST_N);
40582c56b6dSRichard Zhu 				regmap_clear_bits(priv->ctrl,
40682c56b6dSRichard Zhu 						  lane->ctrl_off + HSIO_CTRL2,
40782c56b6dSRichard Zhu 						  HSIO_PERST_N);
40882c56b6dSRichard Zhu 				regmap_clear_bits(priv->ctrl,
40982c56b6dSRichard Zhu 						  lane->ctrl_off + HSIO_CTRL2,
41082c56b6dSRichard Zhu 						  HSIO_POWER_UP_RST_N);
41182c56b6dSRichard Zhu 			} else {
41282c56b6dSRichard Zhu 				regmap_clear_bits(priv->ctrl,
41382c56b6dSRichard Zhu 						  lane->ctrl_off + HSIO_CTRL0,
41482c56b6dSRichard Zhu 						  HSIO_EPCS_TXDEEMP);
41582c56b6dSRichard Zhu 				regmap_clear_bits(priv->ctrl,
41682c56b6dSRichard Zhu 						  lane->ctrl_off + HSIO_CTRL0,
41782c56b6dSRichard Zhu 						  HSIO_EPCS_TXDEEMP_SEL);
41882c56b6dSRichard Zhu 				regmap_clear_bits(priv->ctrl,
41982c56b6dSRichard Zhu 						  lane->ctrl_off + HSIO_CTRL0,
42082c56b6dSRichard Zhu 						  HSIO_RESET_N);
42182c56b6dSRichard Zhu 			}
42282c56b6dSRichard Zhu 
42382c56b6dSRichard Zhu 			if (lane->idx == 1) {
42482c56b6dSRichard Zhu 				regmap_clear_bits(priv->phy,
42582c56b6dSRichard Zhu 						  lane->phy_off + HSIO_CTRL0,
42682c56b6dSRichard Zhu 						  HSIO_APB_RSTN_1);
42782c56b6dSRichard Zhu 				regmap_clear_bits(priv->phy,
42882c56b6dSRichard Zhu 						  lane->phy_off + HSIO_CTRL0,
42982c56b6dSRichard Zhu 						  HSIO_PIPE_RSTN_1_MASK);
43082c56b6dSRichard Zhu 			} else {
43182c56b6dSRichard Zhu 				/*
43282c56b6dSRichard Zhu 				 * Except the phy_off, the bit-offset of lane2 is same
43382c56b6dSRichard Zhu 				 * to lane0. Merge the lane0 and lane2 bit-operations
43482c56b6dSRichard Zhu 				 * together.
43582c56b6dSRichard Zhu 				 */
43682c56b6dSRichard Zhu 				regmap_clear_bits(priv->phy,
43782c56b6dSRichard Zhu 						  lane->phy_off + HSIO_CTRL0,
43882c56b6dSRichard Zhu 						  HSIO_APB_RSTN_0);
43982c56b6dSRichard Zhu 				regmap_clear_bits(priv->phy,
44082c56b6dSRichard Zhu 						  lane->phy_off + HSIO_CTRL0,
44182c56b6dSRichard Zhu 						  HSIO_PIPE_RSTN_0_MASK);
44282c56b6dSRichard Zhu 			}
44382c56b6dSRichard Zhu 		}
44482c56b6dSRichard Zhu 	}
44582c56b6dSRichard Zhu 
44682c56b6dSRichard Zhu 	return 0;
44782c56b6dSRichard Zhu }
44882c56b6dSRichard Zhu 
imx_hsio_set_mode(struct phy * phy,enum phy_mode mode,int submode)44982c56b6dSRichard Zhu static int imx_hsio_set_mode(struct phy *phy, enum phy_mode mode,
45082c56b6dSRichard Zhu 			     int submode)
45182c56b6dSRichard Zhu {
45282c56b6dSRichard Zhu 	u32 val;
45382c56b6dSRichard Zhu 	struct imx_hsio_lane *lane = phy_get_drvdata(phy);
45482c56b6dSRichard Zhu 	struct imx_hsio_priv *priv = lane->priv;
45582c56b6dSRichard Zhu 
45682c56b6dSRichard Zhu 	if (lane->phy_mode != mode)
45782c56b6dSRichard Zhu 		return -EINVAL;
45882c56b6dSRichard Zhu 
45982c56b6dSRichard Zhu 	val = (mode == PHY_MODE_PCIE) ? HSIO_MODE_PCIE : HSIO_MODE_SATA;
46082c56b6dSRichard Zhu 	val = FIELD_PREP(HSIO_MODE_MASK, val);
46182c56b6dSRichard Zhu 	regmap_update_bits(priv->phy, lane->phy_off + HSIO_CTRL0,
46282c56b6dSRichard Zhu 			   HSIO_MODE_MASK, val);
46382c56b6dSRichard Zhu 
46482c56b6dSRichard Zhu 	switch (submode) {
46582c56b6dSRichard Zhu 	case PHY_MODE_PCIE_RC:
46682c56b6dSRichard Zhu 		val = FIELD_PREP(HSIO_DEVICE_TYPE_MASK, PCI_EXP_TYPE_ROOT_PORT);
46782c56b6dSRichard Zhu 		break;
46882c56b6dSRichard Zhu 	case PHY_MODE_PCIE_EP:
46982c56b6dSRichard Zhu 		val = FIELD_PREP(HSIO_DEVICE_TYPE_MASK, PCI_EXP_TYPE_ENDPOINT);
47082c56b6dSRichard Zhu 		break;
47182c56b6dSRichard Zhu 	default: /* Support only PCIe EP and RC now. */
47282c56b6dSRichard Zhu 		return 0;
47382c56b6dSRichard Zhu 	}
47482c56b6dSRichard Zhu 	if (submode)
47582c56b6dSRichard Zhu 		regmap_update_bits(priv->ctrl, lane->ctrl_off + HSIO_CTRL0,
47682c56b6dSRichard Zhu 				   HSIO_DEVICE_TYPE_MASK, val);
47782c56b6dSRichard Zhu 
47882c56b6dSRichard Zhu 	return 0;
47982c56b6dSRichard Zhu }
48082c56b6dSRichard Zhu 
imx_hsio_set_speed(struct phy * phy,int speed)48182c56b6dSRichard Zhu static int imx_hsio_set_speed(struct phy *phy, int speed)
48282c56b6dSRichard Zhu {
48382c56b6dSRichard Zhu 	struct imx_hsio_lane *lane = phy_get_drvdata(phy);
48482c56b6dSRichard Zhu 	struct imx_hsio_priv *priv = lane->priv;
48582c56b6dSRichard Zhu 
48682c56b6dSRichard Zhu 	regmap_update_bits(priv->ctrl, lane->ctrl_off + HSIO_CTRL2,
48782c56b6dSRichard Zhu 			   HSIO_LTSSM_ENABLE,
48882c56b6dSRichard Zhu 			   speed ? HSIO_LTSSM_ENABLE : 0);
48982c56b6dSRichard Zhu 	return 0;
49082c56b6dSRichard Zhu }
49182c56b6dSRichard Zhu 
49282c56b6dSRichard Zhu static const struct phy_ops imx_hsio_ops = {
49382c56b6dSRichard Zhu 	.init = imx_hsio_init,
49482c56b6dSRichard Zhu 	.exit = imx_hsio_exit,
49582c56b6dSRichard Zhu 	.power_on = imx_hsio_power_on,
49682c56b6dSRichard Zhu 	.power_off = imx_hsio_power_off,
49782c56b6dSRichard Zhu 	.set_mode = imx_hsio_set_mode,
49882c56b6dSRichard Zhu 	.set_speed = imx_hsio_set_speed,
49982c56b6dSRichard Zhu 	.owner = THIS_MODULE,
50082c56b6dSRichard Zhu };
50182c56b6dSRichard Zhu 
50282c56b6dSRichard Zhu static const struct imx_hsio_drvdata imx8qxp_hsio_drvdata = {
50382c56b6dSRichard Zhu 	.lane_num = 0x1,
50482c56b6dSRichard Zhu };
50582c56b6dSRichard Zhu 
50682c56b6dSRichard Zhu static const struct imx_hsio_drvdata imx8qm_hsio_drvdata = {
50782c56b6dSRichard Zhu 	.lane_num = 0x3,
50882c56b6dSRichard Zhu };
50982c56b6dSRichard Zhu 
51082c56b6dSRichard Zhu static const struct of_device_id imx_hsio_of_match[] = {
51182c56b6dSRichard Zhu 	{.compatible = "fsl,imx8qm-hsio", .data = &imx8qm_hsio_drvdata},
51282c56b6dSRichard Zhu 	{.compatible = "fsl,imx8qxp-hsio", .data = &imx8qxp_hsio_drvdata},
51382c56b6dSRichard Zhu 	{ },
51482c56b6dSRichard Zhu };
51582c56b6dSRichard Zhu MODULE_DEVICE_TABLE(of, imx_hsio_of_match);
51682c56b6dSRichard Zhu 
imx_hsio_xlate(struct device * dev,const struct of_phandle_args * args)51782c56b6dSRichard Zhu static struct phy *imx_hsio_xlate(struct device *dev,
51882c56b6dSRichard Zhu 				  const struct of_phandle_args *args)
51982c56b6dSRichard Zhu {
52082c56b6dSRichard Zhu 	struct imx_hsio_priv *priv = dev_get_drvdata(dev);
52182c56b6dSRichard Zhu 	int idx = args->args[0];
52282c56b6dSRichard Zhu 	int phy_type = args->args[1];
52382c56b6dSRichard Zhu 	int ctrl_index = args->args[2];
52482c56b6dSRichard Zhu 
52582c56b6dSRichard Zhu 	if (idx < 0 || idx >= priv->drvdata->lane_num)
52682c56b6dSRichard Zhu 		return ERR_PTR(-EINVAL);
52782c56b6dSRichard Zhu 	priv->lane[idx].idx = idx;
52882c56b6dSRichard Zhu 	priv->lane[idx].phy_type = phy_type;
52982c56b6dSRichard Zhu 	priv->lane[idx].ctrl_index = ctrl_index;
53082c56b6dSRichard Zhu 
53182c56b6dSRichard Zhu 	return priv->lane[idx].phy;
53282c56b6dSRichard Zhu }
53382c56b6dSRichard Zhu 
imx_hsio_probe(struct platform_device * pdev)53482c56b6dSRichard Zhu static int imx_hsio_probe(struct platform_device *pdev)
53582c56b6dSRichard Zhu {
53682c56b6dSRichard Zhu 	int i;
53782c56b6dSRichard Zhu 	void __iomem *off;
53882c56b6dSRichard Zhu 	struct device *dev = &pdev->dev;
53982c56b6dSRichard Zhu 	struct device_node *np = dev->of_node;
54082c56b6dSRichard Zhu 	struct imx_hsio_priv *priv;
54182c56b6dSRichard Zhu 	struct phy_provider *provider;
54282c56b6dSRichard Zhu 
54382c56b6dSRichard Zhu 	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
54482c56b6dSRichard Zhu 	if (!priv)
54582c56b6dSRichard Zhu 		return -ENOMEM;
54682c56b6dSRichard Zhu 	priv->dev = &pdev->dev;
54782c56b6dSRichard Zhu 	priv->drvdata = of_device_get_match_data(dev);
54882c56b6dSRichard Zhu 
54982c56b6dSRichard Zhu 	/* Get HSIO configuration mode */
55082c56b6dSRichard Zhu 	if (of_property_read_string(np, "fsl,hsio-cfg", &priv->hsio_cfg))
55182c56b6dSRichard Zhu 		priv->hsio_cfg = "pciea-pcieb-sata";
55282c56b6dSRichard Zhu 	/* Get PHY refclk pad mode */
55382c56b6dSRichard Zhu 	if (of_property_read_string(np, "fsl,refclk-pad-mode",
55482c56b6dSRichard Zhu 				    &priv->refclk_pad))
55582c56b6dSRichard Zhu 		priv->refclk_pad = NULL;
55682c56b6dSRichard Zhu 
55782c56b6dSRichard Zhu 	priv->base = devm_platform_ioremap_resource(pdev, 0);
55882c56b6dSRichard Zhu 	if (IS_ERR(priv->base))
55982c56b6dSRichard Zhu 		return PTR_ERR(priv->base);
56082c56b6dSRichard Zhu 
56182c56b6dSRichard Zhu 	off = devm_platform_ioremap_resource_byname(pdev, "phy");
56282c56b6dSRichard Zhu 	priv->phy = devm_regmap_init_mmio(dev, off, &regmap_config);
56382c56b6dSRichard Zhu 	if (IS_ERR(priv->phy))
56482c56b6dSRichard Zhu 		return dev_err_probe(dev, PTR_ERR(priv->phy),
56582c56b6dSRichard Zhu 				     "unable to find phy csr registers\n");
56682c56b6dSRichard Zhu 
56782c56b6dSRichard Zhu 	off = devm_platform_ioremap_resource_byname(pdev, "ctrl");
56882c56b6dSRichard Zhu 	priv->ctrl = devm_regmap_init_mmio(dev, off, &regmap_config);
56982c56b6dSRichard Zhu 	if (IS_ERR(priv->ctrl))
57082c56b6dSRichard Zhu 		return dev_err_probe(dev, PTR_ERR(priv->ctrl),
57182c56b6dSRichard Zhu 				     "unable to find ctrl csr registers\n");
57282c56b6dSRichard Zhu 
57382c56b6dSRichard Zhu 	off = devm_platform_ioremap_resource_byname(pdev, "misc");
57482c56b6dSRichard Zhu 	priv->misc = devm_regmap_init_mmio(dev, off, &regmap_config);
57582c56b6dSRichard Zhu 	if (IS_ERR(priv->misc))
57682c56b6dSRichard Zhu 		return dev_err_probe(dev, PTR_ERR(priv->misc),
57782c56b6dSRichard Zhu 				     "unable to find misc csr registers\n");
57882c56b6dSRichard Zhu 
57982c56b6dSRichard Zhu 	for (i = 0; i < priv->drvdata->lane_num; i++) {
58082c56b6dSRichard Zhu 		struct imx_hsio_lane *lane = &priv->lane[i];
58182c56b6dSRichard Zhu 		struct phy *phy;
58282c56b6dSRichard Zhu 
58382c56b6dSRichard Zhu 		phy = devm_phy_create(&pdev->dev, NULL, &imx_hsio_ops);
58482c56b6dSRichard Zhu 		if (IS_ERR(phy))
58582c56b6dSRichard Zhu 			return PTR_ERR(phy);
58682c56b6dSRichard Zhu 
58782c56b6dSRichard Zhu 		lane->priv = priv;
58882c56b6dSRichard Zhu 		lane->phy = phy;
58982c56b6dSRichard Zhu 		lane->idx = i;
59082c56b6dSRichard Zhu 		phy_set_drvdata(phy, lane);
59182c56b6dSRichard Zhu 	}
59282c56b6dSRichard Zhu 
59382c56b6dSRichard Zhu 	dev_set_drvdata(dev, priv);
59482c56b6dSRichard Zhu 	dev_set_drvdata(&pdev->dev, priv);
59582c56b6dSRichard Zhu 
59682c56b6dSRichard Zhu 	provider = devm_of_phy_provider_register(&pdev->dev, imx_hsio_xlate);
59782c56b6dSRichard Zhu 
59882c56b6dSRichard Zhu 	return PTR_ERR_OR_ZERO(provider);
59982c56b6dSRichard Zhu }
60082c56b6dSRichard Zhu 
60182c56b6dSRichard Zhu static struct platform_driver imx_hsio_driver = {
60282c56b6dSRichard Zhu 	.probe	= imx_hsio_probe,
60382c56b6dSRichard Zhu 	.driver = {
60482c56b6dSRichard Zhu 		.name	= "imx8qm-hsio-phy",
60582c56b6dSRichard Zhu 		.of_match_table	= imx_hsio_of_match,
60682c56b6dSRichard Zhu 	}
60782c56b6dSRichard Zhu };
60882c56b6dSRichard Zhu module_platform_driver(imx_hsio_driver);
60982c56b6dSRichard Zhu 
61082c56b6dSRichard Zhu MODULE_DESCRIPTION("FSL IMX8QM HSIO SERDES PHY driver");
61182c56b6dSRichard Zhu MODULE_LICENSE("GPL");
612