162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Intel Combo-PHY driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2019-2020 Intel Corporation.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/bitfield.h>
962306a36Sopenharmony_ci#include <linux/clk.h>
1062306a36Sopenharmony_ci#include <linux/iopoll.h>
1162306a36Sopenharmony_ci#include <linux/mfd/syscon.h>
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <linux/mutex.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.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#define PCIE_PHY_GEN_CTRL	0x00
2362306a36Sopenharmony_ci#define PCIE_PHY_CLK_PAD	BIT(17)
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#define PAD_DIS_CFG		0x174
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#define PCS_XF_ATE_OVRD_IN_2	0x3008
2862306a36Sopenharmony_ci#define ADAPT_REQ_MSK		GENMASK(5, 4)
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#define PCS_XF_RX_ADAPT_ACK	0x3010
3162306a36Sopenharmony_ci#define RX_ADAPT_ACK_BIT	BIT(0)
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#define CR_ADDR(addr, lane)	(((addr) + (lane) * 0x100) << 2)
3462306a36Sopenharmony_ci#define REG_COMBO_MODE(x)	((x) * 0x200)
3562306a36Sopenharmony_ci#define REG_CLK_DISABLE(x)	((x) * 0x200 + 0x124)
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#define COMBO_PHY_ID(x)		((x)->parent->id)
3862306a36Sopenharmony_ci#define PHY_ID(x)		((x)->id)
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci#define CLK_100MHZ		100000000
4162306a36Sopenharmony_ci#define CLK_156_25MHZ		156250000
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic const unsigned long intel_iphy_clk_rates[] = {
4462306a36Sopenharmony_ci	CLK_100MHZ, CLK_156_25MHZ, CLK_100MHZ,
4562306a36Sopenharmony_ci};
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cienum {
4862306a36Sopenharmony_ci	PHY_0,
4962306a36Sopenharmony_ci	PHY_1,
5062306a36Sopenharmony_ci	PHY_MAX_NUM
5162306a36Sopenharmony_ci};
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci/*
5462306a36Sopenharmony_ci * Clock Register bit fields to enable clocks
5562306a36Sopenharmony_ci * for ComboPhy according to the mode.
5662306a36Sopenharmony_ci */
5762306a36Sopenharmony_cienum intel_phy_mode {
5862306a36Sopenharmony_ci	PHY_PCIE_MODE = 0,
5962306a36Sopenharmony_ci	PHY_XPCS_MODE,
6062306a36Sopenharmony_ci	PHY_SATA_MODE,
6162306a36Sopenharmony_ci};
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci/* ComboPhy mode Register values */
6462306a36Sopenharmony_cienum intel_combo_mode {
6562306a36Sopenharmony_ci	PCIE0_PCIE1_MODE = 0,
6662306a36Sopenharmony_ci	PCIE_DL_MODE,
6762306a36Sopenharmony_ci	RXAUI_MODE,
6862306a36Sopenharmony_ci	XPCS0_XPCS1_MODE,
6962306a36Sopenharmony_ci	SATA0_SATA1_MODE,
7062306a36Sopenharmony_ci};
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cienum aggregated_mode {
7362306a36Sopenharmony_ci	PHY_SL_MODE,
7462306a36Sopenharmony_ci	PHY_DL_MODE,
7562306a36Sopenharmony_ci};
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_cistruct intel_combo_phy;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistruct intel_cbphy_iphy {
8062306a36Sopenharmony_ci	struct phy		*phy;
8162306a36Sopenharmony_ci	struct intel_combo_phy	*parent;
8262306a36Sopenharmony_ci	struct reset_control	*app_rst;
8362306a36Sopenharmony_ci	u32			id;
8462306a36Sopenharmony_ci};
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cistruct intel_combo_phy {
8762306a36Sopenharmony_ci	struct device		*dev;
8862306a36Sopenharmony_ci	struct clk		*core_clk;
8962306a36Sopenharmony_ci	unsigned long		clk_rate;
9062306a36Sopenharmony_ci	void __iomem		*app_base;
9162306a36Sopenharmony_ci	void __iomem		*cr_base;
9262306a36Sopenharmony_ci	struct regmap		*syscfg;
9362306a36Sopenharmony_ci	struct regmap		*hsiocfg;
9462306a36Sopenharmony_ci	u32			id;
9562306a36Sopenharmony_ci	u32			bid;
9662306a36Sopenharmony_ci	struct reset_control	*phy_rst;
9762306a36Sopenharmony_ci	struct reset_control	*core_rst;
9862306a36Sopenharmony_ci	struct intel_cbphy_iphy	iphy[PHY_MAX_NUM];
9962306a36Sopenharmony_ci	enum intel_phy_mode	phy_mode;
10062306a36Sopenharmony_ci	enum aggregated_mode	aggr_mode;
10162306a36Sopenharmony_ci	u32			init_cnt;
10262306a36Sopenharmony_ci	struct mutex		lock;
10362306a36Sopenharmony_ci};
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cistatic int intel_cbphy_iphy_enable(struct intel_cbphy_iphy *iphy, bool set)
10662306a36Sopenharmony_ci{
10762306a36Sopenharmony_ci	struct intel_combo_phy *cbphy = iphy->parent;
10862306a36Sopenharmony_ci	u32 mask = BIT(cbphy->phy_mode * 2 + iphy->id);
10962306a36Sopenharmony_ci	u32 val;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	/* Register: 0 is enable, 1 is disable */
11262306a36Sopenharmony_ci	val = set ? 0 : mask;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	return regmap_update_bits(cbphy->hsiocfg, REG_CLK_DISABLE(cbphy->bid),
11562306a36Sopenharmony_ci				  mask, val);
11662306a36Sopenharmony_ci}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_cistatic int intel_cbphy_pcie_refclk_cfg(struct intel_cbphy_iphy *iphy, bool set)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci	struct intel_combo_phy *cbphy = iphy->parent;
12162306a36Sopenharmony_ci	u32 mask = BIT(cbphy->id * 2 + iphy->id);
12262306a36Sopenharmony_ci	u32 val;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	/* Register: 0 is enable, 1 is disable */
12562306a36Sopenharmony_ci	val = set ? 0 : mask;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	return regmap_update_bits(cbphy->syscfg, PAD_DIS_CFG, mask, val);
12862306a36Sopenharmony_ci}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_cistatic inline void combo_phy_w32_off_mask(void __iomem *base, unsigned int reg,
13162306a36Sopenharmony_ci					  u32 mask, u32 val)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	u32 reg_val;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	reg_val = readl(base + reg);
13662306a36Sopenharmony_ci	reg_val &= ~mask;
13762306a36Sopenharmony_ci	reg_val |= val;
13862306a36Sopenharmony_ci	writel(reg_val, base + reg);
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_cistatic int intel_cbphy_iphy_cfg(struct intel_cbphy_iphy *iphy,
14262306a36Sopenharmony_ci				int (*phy_cfg)(struct intel_cbphy_iphy *))
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	struct intel_combo_phy *cbphy = iphy->parent;
14562306a36Sopenharmony_ci	int ret;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	ret = phy_cfg(iphy);
14862306a36Sopenharmony_ci	if (ret)
14962306a36Sopenharmony_ci		return ret;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	if (cbphy->aggr_mode != PHY_DL_MODE)
15262306a36Sopenharmony_ci		return 0;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	return phy_cfg(&cbphy->iphy[PHY_1]);
15562306a36Sopenharmony_ci}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_cistatic int intel_cbphy_pcie_en_pad_refclk(struct intel_cbphy_iphy *iphy)
15862306a36Sopenharmony_ci{
15962306a36Sopenharmony_ci	struct intel_combo_phy *cbphy = iphy->parent;
16062306a36Sopenharmony_ci	int ret;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	ret = intel_cbphy_pcie_refclk_cfg(iphy, true);
16362306a36Sopenharmony_ci	if (ret) {
16462306a36Sopenharmony_ci		dev_err(cbphy->dev, "Failed to enable PCIe pad refclk\n");
16562306a36Sopenharmony_ci		return ret;
16662306a36Sopenharmony_ci	}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	if (cbphy->init_cnt)
16962306a36Sopenharmony_ci		return 0;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	combo_phy_w32_off_mask(cbphy->app_base, PCIE_PHY_GEN_CTRL,
17262306a36Sopenharmony_ci			       PCIE_PHY_CLK_PAD, FIELD_PREP(PCIE_PHY_CLK_PAD, 0));
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	/* Delay for stable clock PLL */
17562306a36Sopenharmony_ci	usleep_range(50, 100);
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	return 0;
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_cistatic int intel_cbphy_pcie_dis_pad_refclk(struct intel_cbphy_iphy *iphy)
18162306a36Sopenharmony_ci{
18262306a36Sopenharmony_ci	struct intel_combo_phy *cbphy = iphy->parent;
18362306a36Sopenharmony_ci	int ret;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	ret = intel_cbphy_pcie_refclk_cfg(iphy, false);
18662306a36Sopenharmony_ci	if (ret) {
18762306a36Sopenharmony_ci		dev_err(cbphy->dev, "Failed to disable PCIe pad refclk\n");
18862306a36Sopenharmony_ci		return ret;
18962306a36Sopenharmony_ci	}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	if (cbphy->init_cnt)
19262306a36Sopenharmony_ci		return 0;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	combo_phy_w32_off_mask(cbphy->app_base, PCIE_PHY_GEN_CTRL,
19562306a36Sopenharmony_ci			       PCIE_PHY_CLK_PAD, FIELD_PREP(PCIE_PHY_CLK_PAD, 1));
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	return 0;
19862306a36Sopenharmony_ci}
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_cistatic int intel_cbphy_set_mode(struct intel_combo_phy *cbphy)
20162306a36Sopenharmony_ci{
20262306a36Sopenharmony_ci	enum intel_combo_mode cb_mode;
20362306a36Sopenharmony_ci	enum aggregated_mode aggr = cbphy->aggr_mode;
20462306a36Sopenharmony_ci	struct device *dev = cbphy->dev;
20562306a36Sopenharmony_ci	enum intel_phy_mode mode;
20662306a36Sopenharmony_ci	int ret;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	mode = cbphy->phy_mode;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	switch (mode) {
21162306a36Sopenharmony_ci	case PHY_PCIE_MODE:
21262306a36Sopenharmony_ci		cb_mode = (aggr == PHY_DL_MODE) ? PCIE_DL_MODE : PCIE0_PCIE1_MODE;
21362306a36Sopenharmony_ci		break;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	case PHY_XPCS_MODE:
21662306a36Sopenharmony_ci		cb_mode = (aggr == PHY_DL_MODE) ? RXAUI_MODE : XPCS0_XPCS1_MODE;
21762306a36Sopenharmony_ci		break;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	case PHY_SATA_MODE:
22062306a36Sopenharmony_ci		if (aggr == PHY_DL_MODE) {
22162306a36Sopenharmony_ci			dev_err(dev, "Mode:%u not support dual lane!\n", mode);
22262306a36Sopenharmony_ci			return -EINVAL;
22362306a36Sopenharmony_ci		}
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci		cb_mode = SATA0_SATA1_MODE;
22662306a36Sopenharmony_ci		break;
22762306a36Sopenharmony_ci	default:
22862306a36Sopenharmony_ci		return -EINVAL;
22962306a36Sopenharmony_ci	}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	ret = regmap_write(cbphy->hsiocfg, REG_COMBO_MODE(cbphy->bid), cb_mode);
23262306a36Sopenharmony_ci	if (ret)
23362306a36Sopenharmony_ci		dev_err(dev, "Failed to set ComboPhy mode: %d\n", ret);
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	return ret;
23662306a36Sopenharmony_ci}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_cistatic void intel_cbphy_rst_assert(struct intel_combo_phy *cbphy)
23962306a36Sopenharmony_ci{
24062306a36Sopenharmony_ci	reset_control_assert(cbphy->core_rst);
24162306a36Sopenharmony_ci	reset_control_assert(cbphy->phy_rst);
24262306a36Sopenharmony_ci}
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_cistatic void intel_cbphy_rst_deassert(struct intel_combo_phy *cbphy)
24562306a36Sopenharmony_ci{
24662306a36Sopenharmony_ci	reset_control_deassert(cbphy->core_rst);
24762306a36Sopenharmony_ci	reset_control_deassert(cbphy->phy_rst);
24862306a36Sopenharmony_ci	/* Delay to ensure reset process is done */
24962306a36Sopenharmony_ci	usleep_range(10, 20);
25062306a36Sopenharmony_ci}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_cistatic int intel_cbphy_iphy_power_on(struct intel_cbphy_iphy *iphy)
25362306a36Sopenharmony_ci{
25462306a36Sopenharmony_ci	struct intel_combo_phy *cbphy = iphy->parent;
25562306a36Sopenharmony_ci	int ret;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	if (!cbphy->init_cnt) {
25862306a36Sopenharmony_ci		ret = clk_prepare_enable(cbphy->core_clk);
25962306a36Sopenharmony_ci		if (ret) {
26062306a36Sopenharmony_ci			dev_err(cbphy->dev, "Clock enable failed!\n");
26162306a36Sopenharmony_ci			return ret;
26262306a36Sopenharmony_ci		}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci		ret = clk_set_rate(cbphy->core_clk, cbphy->clk_rate);
26562306a36Sopenharmony_ci		if (ret) {
26662306a36Sopenharmony_ci			dev_err(cbphy->dev, "Clock freq set to %lu failed!\n",
26762306a36Sopenharmony_ci				cbphy->clk_rate);
26862306a36Sopenharmony_ci			goto clk_err;
26962306a36Sopenharmony_ci		}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci		intel_cbphy_rst_assert(cbphy);
27262306a36Sopenharmony_ci		intel_cbphy_rst_deassert(cbphy);
27362306a36Sopenharmony_ci		ret = intel_cbphy_set_mode(cbphy);
27462306a36Sopenharmony_ci		if (ret)
27562306a36Sopenharmony_ci			goto clk_err;
27662306a36Sopenharmony_ci	}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	ret = intel_cbphy_iphy_enable(iphy, true);
27962306a36Sopenharmony_ci	if (ret) {
28062306a36Sopenharmony_ci		dev_err(cbphy->dev, "Failed enabling PHY core\n");
28162306a36Sopenharmony_ci		goto clk_err;
28262306a36Sopenharmony_ci	}
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	ret = reset_control_deassert(iphy->app_rst);
28562306a36Sopenharmony_ci	if (ret) {
28662306a36Sopenharmony_ci		dev_err(cbphy->dev, "PHY(%u:%u) reset deassert failed!\n",
28762306a36Sopenharmony_ci			COMBO_PHY_ID(iphy), PHY_ID(iphy));
28862306a36Sopenharmony_ci		goto clk_err;
28962306a36Sopenharmony_ci	}
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	/* Delay to ensure reset process is done */
29262306a36Sopenharmony_ci	udelay(1);
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	return 0;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ciclk_err:
29762306a36Sopenharmony_ci	clk_disable_unprepare(cbphy->core_clk);
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	return ret;
30062306a36Sopenharmony_ci}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_cistatic int intel_cbphy_iphy_power_off(struct intel_cbphy_iphy *iphy)
30362306a36Sopenharmony_ci{
30462306a36Sopenharmony_ci	struct intel_combo_phy *cbphy = iphy->parent;
30562306a36Sopenharmony_ci	int ret;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	ret = reset_control_assert(iphy->app_rst);
30862306a36Sopenharmony_ci	if (ret) {
30962306a36Sopenharmony_ci		dev_err(cbphy->dev, "PHY(%u:%u) reset assert failed!\n",
31062306a36Sopenharmony_ci			COMBO_PHY_ID(iphy), PHY_ID(iphy));
31162306a36Sopenharmony_ci		return ret;
31262306a36Sopenharmony_ci	}
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	ret = intel_cbphy_iphy_enable(iphy, false);
31562306a36Sopenharmony_ci	if (ret) {
31662306a36Sopenharmony_ci		dev_err(cbphy->dev, "Failed disabling PHY core\n");
31762306a36Sopenharmony_ci		return ret;
31862306a36Sopenharmony_ci	}
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	if (cbphy->init_cnt)
32162306a36Sopenharmony_ci		return 0;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	clk_disable_unprepare(cbphy->core_clk);
32462306a36Sopenharmony_ci	intel_cbphy_rst_assert(cbphy);
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	return 0;
32762306a36Sopenharmony_ci}
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_cistatic int intel_cbphy_init(struct phy *phy)
33062306a36Sopenharmony_ci{
33162306a36Sopenharmony_ci	struct intel_cbphy_iphy *iphy = phy_get_drvdata(phy);
33262306a36Sopenharmony_ci	struct intel_combo_phy *cbphy = iphy->parent;
33362306a36Sopenharmony_ci	int ret;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	mutex_lock(&cbphy->lock);
33662306a36Sopenharmony_ci	ret = intel_cbphy_iphy_cfg(iphy, intel_cbphy_iphy_power_on);
33762306a36Sopenharmony_ci	if (ret)
33862306a36Sopenharmony_ci		goto err;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	if (cbphy->phy_mode == PHY_PCIE_MODE) {
34162306a36Sopenharmony_ci		ret = intel_cbphy_iphy_cfg(iphy, intel_cbphy_pcie_en_pad_refclk);
34262306a36Sopenharmony_ci		if (ret)
34362306a36Sopenharmony_ci			goto err;
34462306a36Sopenharmony_ci	}
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	cbphy->init_cnt++;
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_cierr:
34962306a36Sopenharmony_ci	mutex_unlock(&cbphy->lock);
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	return ret;
35262306a36Sopenharmony_ci}
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_cistatic int intel_cbphy_exit(struct phy *phy)
35562306a36Sopenharmony_ci{
35662306a36Sopenharmony_ci	struct intel_cbphy_iphy *iphy = phy_get_drvdata(phy);
35762306a36Sopenharmony_ci	struct intel_combo_phy *cbphy = iphy->parent;
35862306a36Sopenharmony_ci	int ret;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	mutex_lock(&cbphy->lock);
36162306a36Sopenharmony_ci	cbphy->init_cnt--;
36262306a36Sopenharmony_ci	if (cbphy->phy_mode == PHY_PCIE_MODE) {
36362306a36Sopenharmony_ci		ret = intel_cbphy_iphy_cfg(iphy, intel_cbphy_pcie_dis_pad_refclk);
36462306a36Sopenharmony_ci		if (ret)
36562306a36Sopenharmony_ci			goto err;
36662306a36Sopenharmony_ci	}
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	ret = intel_cbphy_iphy_cfg(iphy, intel_cbphy_iphy_power_off);
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_cierr:
37162306a36Sopenharmony_ci	mutex_unlock(&cbphy->lock);
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	return ret;
37462306a36Sopenharmony_ci}
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_cistatic int intel_cbphy_calibrate(struct phy *phy)
37762306a36Sopenharmony_ci{
37862306a36Sopenharmony_ci	struct intel_cbphy_iphy *iphy = phy_get_drvdata(phy);
37962306a36Sopenharmony_ci	struct intel_combo_phy *cbphy = iphy->parent;
38062306a36Sopenharmony_ci	void __iomem *cr_base = cbphy->cr_base;
38162306a36Sopenharmony_ci	int val, ret, id;
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	if (cbphy->phy_mode != PHY_XPCS_MODE)
38462306a36Sopenharmony_ci		return 0;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	id = PHY_ID(iphy);
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	/* trigger auto RX adaptation */
38962306a36Sopenharmony_ci	combo_phy_w32_off_mask(cr_base, CR_ADDR(PCS_XF_ATE_OVRD_IN_2, id),
39062306a36Sopenharmony_ci			       ADAPT_REQ_MSK, FIELD_PREP(ADAPT_REQ_MSK, 3));
39162306a36Sopenharmony_ci	/* Wait RX adaptation to finish */
39262306a36Sopenharmony_ci	ret = readl_poll_timeout(cr_base + CR_ADDR(PCS_XF_RX_ADAPT_ACK, id),
39362306a36Sopenharmony_ci				 val, val & RX_ADAPT_ACK_BIT, 10, 5000);
39462306a36Sopenharmony_ci	if (ret)
39562306a36Sopenharmony_ci		dev_err(cbphy->dev, "RX Adaptation failed!\n");
39662306a36Sopenharmony_ci	else
39762306a36Sopenharmony_ci		dev_dbg(cbphy->dev, "RX Adaptation success!\n");
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	/* Stop RX adaptation */
40062306a36Sopenharmony_ci	combo_phy_w32_off_mask(cr_base, CR_ADDR(PCS_XF_ATE_OVRD_IN_2, id),
40162306a36Sopenharmony_ci			       ADAPT_REQ_MSK, FIELD_PREP(ADAPT_REQ_MSK, 0));
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	return ret;
40462306a36Sopenharmony_ci}
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_cistatic int intel_cbphy_fwnode_parse(struct intel_combo_phy *cbphy)
40762306a36Sopenharmony_ci{
40862306a36Sopenharmony_ci	struct device *dev = cbphy->dev;
40962306a36Sopenharmony_ci	struct platform_device *pdev = to_platform_device(dev);
41062306a36Sopenharmony_ci	struct fwnode_handle *fwnode = dev_fwnode(dev);
41162306a36Sopenharmony_ci	struct fwnode_reference_args ref;
41262306a36Sopenharmony_ci	int ret;
41362306a36Sopenharmony_ci	u32 val;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	cbphy->core_clk = devm_clk_get(dev, NULL);
41662306a36Sopenharmony_ci	if (IS_ERR(cbphy->core_clk))
41762306a36Sopenharmony_ci		return dev_err_probe(dev, PTR_ERR(cbphy->core_clk),
41862306a36Sopenharmony_ci				     "Get clk failed!\n");
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	cbphy->core_rst = devm_reset_control_get_optional(dev, "core");
42162306a36Sopenharmony_ci	if (IS_ERR(cbphy->core_rst))
42262306a36Sopenharmony_ci		return dev_err_probe(dev, PTR_ERR(cbphy->core_rst),
42362306a36Sopenharmony_ci				     "Get core reset control err!\n");
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	cbphy->phy_rst = devm_reset_control_get_optional(dev, "phy");
42662306a36Sopenharmony_ci	if (IS_ERR(cbphy->phy_rst))
42762306a36Sopenharmony_ci		return dev_err_probe(dev, PTR_ERR(cbphy->phy_rst),
42862306a36Sopenharmony_ci				     "Get PHY reset control err!\n");
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	cbphy->iphy[0].app_rst = devm_reset_control_get_optional(dev, "iphy0");
43162306a36Sopenharmony_ci	if (IS_ERR(cbphy->iphy[0].app_rst))
43262306a36Sopenharmony_ci		return dev_err_probe(dev, PTR_ERR(cbphy->iphy[0].app_rst),
43362306a36Sopenharmony_ci				     "Get phy0 reset control err!\n");
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	cbphy->iphy[1].app_rst = devm_reset_control_get_optional(dev, "iphy1");
43662306a36Sopenharmony_ci	if (IS_ERR(cbphy->iphy[1].app_rst))
43762306a36Sopenharmony_ci		return dev_err_probe(dev, PTR_ERR(cbphy->iphy[1].app_rst),
43862306a36Sopenharmony_ci				     "Get phy1 reset control err!\n");
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	cbphy->app_base = devm_platform_ioremap_resource_byname(pdev, "app");
44162306a36Sopenharmony_ci	if (IS_ERR(cbphy->app_base))
44262306a36Sopenharmony_ci		return PTR_ERR(cbphy->app_base);
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	cbphy->cr_base = devm_platform_ioremap_resource_byname(pdev, "core");
44562306a36Sopenharmony_ci	if (IS_ERR(cbphy->cr_base))
44662306a36Sopenharmony_ci		return PTR_ERR(cbphy->cr_base);
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	/*
44962306a36Sopenharmony_ci	 * syscfg and hsiocfg variables stores the handle of the registers set
45062306a36Sopenharmony_ci	 * in which ComboPhy subsystem specific registers are subset. Using
45162306a36Sopenharmony_ci	 * Register map framework to access the registers set.
45262306a36Sopenharmony_ci	 */
45362306a36Sopenharmony_ci	ret = fwnode_property_get_reference_args(fwnode, "intel,syscfg", NULL,
45462306a36Sopenharmony_ci						 1, 0, &ref);
45562306a36Sopenharmony_ci	if (ret < 0)
45662306a36Sopenharmony_ci		return ret;
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	cbphy->id = ref.args[0];
45962306a36Sopenharmony_ci	cbphy->syscfg = device_node_to_regmap(to_of_node(ref.fwnode));
46062306a36Sopenharmony_ci	fwnode_handle_put(ref.fwnode);
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	ret = fwnode_property_get_reference_args(fwnode, "intel,hsio", NULL, 1,
46362306a36Sopenharmony_ci						 0, &ref);
46462306a36Sopenharmony_ci	if (ret < 0)
46562306a36Sopenharmony_ci		return ret;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	cbphy->bid = ref.args[0];
46862306a36Sopenharmony_ci	cbphy->hsiocfg = device_node_to_regmap(to_of_node(ref.fwnode));
46962306a36Sopenharmony_ci	fwnode_handle_put(ref.fwnode);
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	ret = fwnode_property_read_u32_array(fwnode, "intel,phy-mode", &val, 1);
47262306a36Sopenharmony_ci	if (ret)
47362306a36Sopenharmony_ci		return ret;
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	switch (val) {
47662306a36Sopenharmony_ci	case PHY_TYPE_PCIE:
47762306a36Sopenharmony_ci		cbphy->phy_mode = PHY_PCIE_MODE;
47862306a36Sopenharmony_ci		break;
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	case PHY_TYPE_SATA:
48162306a36Sopenharmony_ci		cbphy->phy_mode = PHY_SATA_MODE;
48262306a36Sopenharmony_ci		break;
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	case PHY_TYPE_XPCS:
48562306a36Sopenharmony_ci		cbphy->phy_mode = PHY_XPCS_MODE;
48662306a36Sopenharmony_ci		break;
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	default:
48962306a36Sopenharmony_ci		dev_err(dev, "Invalid PHY mode: %u\n", val);
49062306a36Sopenharmony_ci		return -EINVAL;
49162306a36Sopenharmony_ci	}
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	cbphy->clk_rate = intel_iphy_clk_rates[cbphy->phy_mode];
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	if (fwnode_property_present(fwnode, "intel,aggregation"))
49662306a36Sopenharmony_ci		cbphy->aggr_mode = PHY_DL_MODE;
49762306a36Sopenharmony_ci	else
49862306a36Sopenharmony_ci		cbphy->aggr_mode = PHY_SL_MODE;
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	return 0;
50162306a36Sopenharmony_ci}
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_cistatic const struct phy_ops intel_cbphy_ops = {
50462306a36Sopenharmony_ci	.init		= intel_cbphy_init,
50562306a36Sopenharmony_ci	.exit		= intel_cbphy_exit,
50662306a36Sopenharmony_ci	.calibrate	= intel_cbphy_calibrate,
50762306a36Sopenharmony_ci	.owner		= THIS_MODULE,
50862306a36Sopenharmony_ci};
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_cistatic struct phy *intel_cbphy_xlate(struct device *dev,
51162306a36Sopenharmony_ci				     struct of_phandle_args *args)
51262306a36Sopenharmony_ci{
51362306a36Sopenharmony_ci	struct intel_combo_phy *cbphy = dev_get_drvdata(dev);
51462306a36Sopenharmony_ci	u32 iphy_id;
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	if (args->args_count < 1) {
51762306a36Sopenharmony_ci		dev_err(dev, "Invalid number of arguments\n");
51862306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
51962306a36Sopenharmony_ci	}
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	iphy_id = args->args[0];
52262306a36Sopenharmony_ci	if (iphy_id >= PHY_MAX_NUM) {
52362306a36Sopenharmony_ci		dev_err(dev, "Invalid phy instance %d\n", iphy_id);
52462306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
52562306a36Sopenharmony_ci	}
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	if (cbphy->aggr_mode == PHY_DL_MODE && iphy_id == PHY_1) {
52862306a36Sopenharmony_ci		dev_err(dev, "Invalid. ComboPhy is in Dual lane mode %d\n", iphy_id);
52962306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
53062306a36Sopenharmony_ci	}
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	return cbphy->iphy[iphy_id].phy;
53362306a36Sopenharmony_ci}
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_cistatic int intel_cbphy_create(struct intel_combo_phy *cbphy)
53662306a36Sopenharmony_ci{
53762306a36Sopenharmony_ci	struct phy_provider *phy_provider;
53862306a36Sopenharmony_ci	struct device *dev = cbphy->dev;
53962306a36Sopenharmony_ci	struct intel_cbphy_iphy *iphy;
54062306a36Sopenharmony_ci	int i;
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	for (i = 0; i < PHY_MAX_NUM; i++) {
54362306a36Sopenharmony_ci		iphy = &cbphy->iphy[i];
54462306a36Sopenharmony_ci		iphy->parent = cbphy;
54562306a36Sopenharmony_ci		iphy->id = i;
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci		/* In dual lane mode skip phy creation for the second phy */
54862306a36Sopenharmony_ci		if (cbphy->aggr_mode == PHY_DL_MODE && iphy->id == PHY_1)
54962306a36Sopenharmony_ci			continue;
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci		iphy->phy = devm_phy_create(dev, NULL, &intel_cbphy_ops);
55262306a36Sopenharmony_ci		if (IS_ERR(iphy->phy)) {
55362306a36Sopenharmony_ci			dev_err(dev, "PHY[%u:%u]: create PHY instance failed!\n",
55462306a36Sopenharmony_ci				COMBO_PHY_ID(iphy), PHY_ID(iphy));
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci			return PTR_ERR(iphy->phy);
55762306a36Sopenharmony_ci		}
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci		phy_set_drvdata(iphy->phy, iphy);
56062306a36Sopenharmony_ci	}
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	dev_set_drvdata(dev, cbphy);
56362306a36Sopenharmony_ci	phy_provider = devm_of_phy_provider_register(dev, intel_cbphy_xlate);
56462306a36Sopenharmony_ci	if (IS_ERR(phy_provider))
56562306a36Sopenharmony_ci		dev_err(dev, "Register PHY provider failed!\n");
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	return PTR_ERR_OR_ZERO(phy_provider);
56862306a36Sopenharmony_ci}
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_cistatic int intel_cbphy_probe(struct platform_device *pdev)
57162306a36Sopenharmony_ci{
57262306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
57362306a36Sopenharmony_ci	struct intel_combo_phy *cbphy;
57462306a36Sopenharmony_ci	int ret;
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	cbphy = devm_kzalloc(dev, sizeof(*cbphy), GFP_KERNEL);
57762306a36Sopenharmony_ci	if (!cbphy)
57862306a36Sopenharmony_ci		return -ENOMEM;
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	cbphy->dev = dev;
58162306a36Sopenharmony_ci	cbphy->init_cnt = 0;
58262306a36Sopenharmony_ci	mutex_init(&cbphy->lock);
58362306a36Sopenharmony_ci	ret = intel_cbphy_fwnode_parse(cbphy);
58462306a36Sopenharmony_ci	if (ret)
58562306a36Sopenharmony_ci		return ret;
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	platform_set_drvdata(pdev, cbphy);
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	return intel_cbphy_create(cbphy);
59062306a36Sopenharmony_ci}
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_cistatic void intel_cbphy_remove(struct platform_device *pdev)
59362306a36Sopenharmony_ci{
59462306a36Sopenharmony_ci	struct intel_combo_phy *cbphy = platform_get_drvdata(pdev);
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	intel_cbphy_rst_assert(cbphy);
59762306a36Sopenharmony_ci	clk_disable_unprepare(cbphy->core_clk);
59862306a36Sopenharmony_ci}
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_cistatic const struct of_device_id of_intel_cbphy_match[] = {
60162306a36Sopenharmony_ci	{ .compatible = "intel,combo-phy" },
60262306a36Sopenharmony_ci	{ .compatible = "intel,combophy-lgm" },
60362306a36Sopenharmony_ci	{}
60462306a36Sopenharmony_ci};
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_cistatic struct platform_driver intel_cbphy_driver = {
60762306a36Sopenharmony_ci	.probe = intel_cbphy_probe,
60862306a36Sopenharmony_ci	.remove_new = intel_cbphy_remove,
60962306a36Sopenharmony_ci	.driver = {
61062306a36Sopenharmony_ci		.name = "intel-combo-phy",
61162306a36Sopenharmony_ci		.of_match_table = of_intel_cbphy_match,
61262306a36Sopenharmony_ci	}
61362306a36Sopenharmony_ci};
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_cimodule_platform_driver(intel_cbphy_driver);
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ciMODULE_DESCRIPTION("Intel Combo-phy driver");
618