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, ®map_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, ®map_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, ®map_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