162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0 OR MIT)
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * SerDes PHY driver for Microsemi Ocelot
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2018 Microsemi
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/err.h>
1062306a36Sopenharmony_ci#include <linux/mfd/syscon.h>
1162306a36Sopenharmony_ci#include <linux/module.h>
1262306a36Sopenharmony_ci#include <linux/of.h>
1362306a36Sopenharmony_ci#include <linux/of_platform.h>
1462306a36Sopenharmony_ci#include <linux/phy.h>
1562306a36Sopenharmony_ci#include <linux/phy/phy.h>
1662306a36Sopenharmony_ci#include <linux/platform_device.h>
1762306a36Sopenharmony_ci#include <linux/regmap.h>
1862306a36Sopenharmony_ci#include <soc/mscc/ocelot_hsio.h>
1962306a36Sopenharmony_ci#include <dt-bindings/phy/phy-ocelot-serdes.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistruct serdes_ctrl {
2262306a36Sopenharmony_ci	struct regmap		*regs;
2362306a36Sopenharmony_ci	struct device		*dev;
2462306a36Sopenharmony_ci	struct phy		*phys[SERDES_MAX];
2562306a36Sopenharmony_ci};
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistruct serdes_macro {
2862306a36Sopenharmony_ci	u8			idx;
2962306a36Sopenharmony_ci	/* Not used when in QSGMII or PCIe mode */
3062306a36Sopenharmony_ci	int			port;
3162306a36Sopenharmony_ci	struct serdes_ctrl	*ctrl;
3262306a36Sopenharmony_ci};
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#define MCB_S6G_CFG_TIMEOUT     50
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistatic int __serdes_write_mcb_s6g(struct regmap *regmap, u8 macro, u32 op)
3762306a36Sopenharmony_ci{
3862306a36Sopenharmony_ci	unsigned int regval = 0;
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	regmap_write(regmap, HSIO_MCB_S6G_ADDR_CFG, op |
4162306a36Sopenharmony_ci		     HSIO_MCB_S6G_ADDR_CFG_SERDES6G_ADDR(BIT(macro)));
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	return regmap_read_poll_timeout(regmap, HSIO_MCB_S6G_ADDR_CFG, regval,
4462306a36Sopenharmony_ci					(regval & op) != op, 100,
4562306a36Sopenharmony_ci					MCB_S6G_CFG_TIMEOUT * 1000);
4662306a36Sopenharmony_ci}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic int serdes_commit_mcb_s6g(struct regmap *regmap, u8 macro)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	return __serdes_write_mcb_s6g(regmap, macro,
5162306a36Sopenharmony_ci		HSIO_MCB_S6G_ADDR_CFG_SERDES6G_WR_ONE_SHOT);
5262306a36Sopenharmony_ci}
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistatic int serdes_update_mcb_s6g(struct regmap *regmap, u8 macro)
5562306a36Sopenharmony_ci{
5662306a36Sopenharmony_ci	return __serdes_write_mcb_s6g(regmap, macro,
5762306a36Sopenharmony_ci		HSIO_MCB_S6G_ADDR_CFG_SERDES6G_RD_ONE_SHOT);
5862306a36Sopenharmony_ci}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_cistatic int serdes_init_s6g(struct regmap *regmap, u8 serdes, int mode)
6162306a36Sopenharmony_ci{
6262306a36Sopenharmony_ci	u32 pll_fsm_ctrl_data;
6362306a36Sopenharmony_ci	u32 ob_ena1v_mode;
6462306a36Sopenharmony_ci	u32 des_bw_ana;
6562306a36Sopenharmony_ci	u32 ob_ena_cas;
6662306a36Sopenharmony_ci	u32 if_mode;
6762306a36Sopenharmony_ci	u32 ob_lev;
6862306a36Sopenharmony_ci	u32 qrate;
6962306a36Sopenharmony_ci	int ret;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	if (mode == PHY_INTERFACE_MODE_QSGMII) {
7262306a36Sopenharmony_ci		pll_fsm_ctrl_data = 120;
7362306a36Sopenharmony_ci		ob_ena1v_mode = 0;
7462306a36Sopenharmony_ci		ob_ena_cas = 0;
7562306a36Sopenharmony_ci		des_bw_ana = 5;
7662306a36Sopenharmony_ci		ob_lev = 24;
7762306a36Sopenharmony_ci		if_mode = 3;
7862306a36Sopenharmony_ci		qrate = 0;
7962306a36Sopenharmony_ci	} else {
8062306a36Sopenharmony_ci		pll_fsm_ctrl_data = 60;
8162306a36Sopenharmony_ci		ob_ena1v_mode = 1;
8262306a36Sopenharmony_ci		ob_ena_cas = 2;
8362306a36Sopenharmony_ci		des_bw_ana = 3;
8462306a36Sopenharmony_ci		ob_lev = 48;
8562306a36Sopenharmony_ci		if_mode = 1;
8662306a36Sopenharmony_ci		qrate = 1;
8762306a36Sopenharmony_ci	}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	ret = serdes_update_mcb_s6g(regmap, serdes);
9062306a36Sopenharmony_ci	if (ret)
9162306a36Sopenharmony_ci		return ret;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	/* Test pattern */
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	regmap_update_bits(regmap, HSIO_S6G_COMMON_CFG,
9662306a36Sopenharmony_ci			   HSIO_S6G_COMMON_CFG_SYS_RST, 0);
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	regmap_update_bits(regmap, HSIO_S6G_PLL_CFG,
9962306a36Sopenharmony_ci			   HSIO_S6G_PLL_CFG_PLL_FSM_ENA, 0);
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	regmap_update_bits(regmap, HSIO_S6G_IB_CFG,
10262306a36Sopenharmony_ci			   HSIO_S6G_IB_CFG_IB_SIG_DET_ENA |
10362306a36Sopenharmony_ci			   HSIO_S6G_IB_CFG_IB_REG_ENA |
10462306a36Sopenharmony_ci			   HSIO_S6G_IB_CFG_IB_SAM_ENA |
10562306a36Sopenharmony_ci			   HSIO_S6G_IB_CFG_IB_EQZ_ENA |
10662306a36Sopenharmony_ci			   HSIO_S6G_IB_CFG_IB_CONCUR |
10762306a36Sopenharmony_ci			   HSIO_S6G_IB_CFG_IB_CAL_ENA,
10862306a36Sopenharmony_ci			   HSIO_S6G_IB_CFG_IB_SIG_DET_ENA |
10962306a36Sopenharmony_ci			   HSIO_S6G_IB_CFG_IB_REG_ENA |
11062306a36Sopenharmony_ci			   HSIO_S6G_IB_CFG_IB_SAM_ENA |
11162306a36Sopenharmony_ci			   HSIO_S6G_IB_CFG_IB_EQZ_ENA |
11262306a36Sopenharmony_ci			   HSIO_S6G_IB_CFG_IB_CONCUR);
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	regmap_update_bits(regmap, HSIO_S6G_IB_CFG1,
11562306a36Sopenharmony_ci			   HSIO_S6G_IB_CFG1_IB_FRC_OFFSET |
11662306a36Sopenharmony_ci			   HSIO_S6G_IB_CFG1_IB_FRC_LP |
11762306a36Sopenharmony_ci			   HSIO_S6G_IB_CFG1_IB_FRC_MID |
11862306a36Sopenharmony_ci			   HSIO_S6G_IB_CFG1_IB_FRC_HP |
11962306a36Sopenharmony_ci			   HSIO_S6G_IB_CFG1_IB_FILT_OFFSET |
12062306a36Sopenharmony_ci			   HSIO_S6G_IB_CFG1_IB_FILT_LP |
12162306a36Sopenharmony_ci			   HSIO_S6G_IB_CFG1_IB_FILT_MID |
12262306a36Sopenharmony_ci			   HSIO_S6G_IB_CFG1_IB_FILT_HP,
12362306a36Sopenharmony_ci			   HSIO_S6G_IB_CFG1_IB_FILT_OFFSET |
12462306a36Sopenharmony_ci			   HSIO_S6G_IB_CFG1_IB_FILT_HP |
12562306a36Sopenharmony_ci			   HSIO_S6G_IB_CFG1_IB_FILT_LP |
12662306a36Sopenharmony_ci			   HSIO_S6G_IB_CFG1_IB_FILT_MID);
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	regmap_update_bits(regmap, HSIO_S6G_IB_CFG2,
12962306a36Sopenharmony_ci			   HSIO_S6G_IB_CFG2_IB_UREG_M,
13062306a36Sopenharmony_ci			   HSIO_S6G_IB_CFG2_IB_UREG(4));
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	regmap_update_bits(regmap, HSIO_S6G_IB_CFG3,
13362306a36Sopenharmony_ci			   HSIO_S6G_IB_CFG3_IB_INI_OFFSET_M |
13462306a36Sopenharmony_ci			   HSIO_S6G_IB_CFG3_IB_INI_LP_M |
13562306a36Sopenharmony_ci			   HSIO_S6G_IB_CFG3_IB_INI_MID_M |
13662306a36Sopenharmony_ci			   HSIO_S6G_IB_CFG3_IB_INI_HP_M,
13762306a36Sopenharmony_ci			   HSIO_S6G_IB_CFG3_IB_INI_OFFSET(31) |
13862306a36Sopenharmony_ci			   HSIO_S6G_IB_CFG3_IB_INI_LP(1) |
13962306a36Sopenharmony_ci			   HSIO_S6G_IB_CFG3_IB_INI_MID(31) |
14062306a36Sopenharmony_ci			   HSIO_S6G_IB_CFG3_IB_INI_HP(0));
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	regmap_update_bits(regmap, HSIO_S6G_MISC_CFG,
14362306a36Sopenharmony_ci			   HSIO_S6G_MISC_CFG_LANE_RST,
14462306a36Sopenharmony_ci			   HSIO_S6G_MISC_CFG_LANE_RST);
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	ret = serdes_commit_mcb_s6g(regmap, serdes);
14762306a36Sopenharmony_ci	if (ret)
14862306a36Sopenharmony_ci		return ret;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	/* OB + DES + IB + SER CFG */
15162306a36Sopenharmony_ci	regmap_update_bits(regmap, HSIO_S6G_OB_CFG,
15262306a36Sopenharmony_ci			   HSIO_S6G_OB_CFG_OB_IDLE |
15362306a36Sopenharmony_ci			   HSIO_S6G_OB_CFG_OB_ENA1V_MODE |
15462306a36Sopenharmony_ci			   HSIO_S6G_OB_CFG_OB_POST0_M |
15562306a36Sopenharmony_ci			   HSIO_S6G_OB_CFG_OB_PREC_M,
15662306a36Sopenharmony_ci			   (ob_ena1v_mode ? HSIO_S6G_OB_CFG_OB_ENA1V_MODE : 0) |
15762306a36Sopenharmony_ci			   HSIO_S6G_OB_CFG_OB_POST0(0) |
15862306a36Sopenharmony_ci			   HSIO_S6G_OB_CFG_OB_PREC(0));
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	regmap_update_bits(regmap, HSIO_S6G_OB_CFG1,
16162306a36Sopenharmony_ci			   HSIO_S6G_OB_CFG1_OB_ENA_CAS_M |
16262306a36Sopenharmony_ci			   HSIO_S6G_OB_CFG1_OB_LEV_M,
16362306a36Sopenharmony_ci			   HSIO_S6G_OB_CFG1_OB_LEV(ob_lev) |
16462306a36Sopenharmony_ci			   HSIO_S6G_OB_CFG1_OB_ENA_CAS(ob_ena_cas));
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	regmap_update_bits(regmap, HSIO_S6G_DES_CFG,
16762306a36Sopenharmony_ci			   HSIO_S6G_DES_CFG_DES_PHS_CTRL_M |
16862306a36Sopenharmony_ci			   HSIO_S6G_DES_CFG_DES_CPMD_SEL_M |
16962306a36Sopenharmony_ci			   HSIO_S6G_DES_CFG_DES_BW_ANA_M,
17062306a36Sopenharmony_ci			   HSIO_S6G_DES_CFG_DES_PHS_CTRL(2) |
17162306a36Sopenharmony_ci			   HSIO_S6G_DES_CFG_DES_CPMD_SEL(0) |
17262306a36Sopenharmony_ci			   HSIO_S6G_DES_CFG_DES_BW_ANA(des_bw_ana));
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	regmap_update_bits(regmap, HSIO_S6G_IB_CFG,
17562306a36Sopenharmony_ci			   HSIO_S6G_IB_CFG_IB_SIG_DET_CLK_SEL_M |
17662306a36Sopenharmony_ci			   HSIO_S6G_IB_CFG_IB_REG_PAT_SEL_OFFSET_M,
17762306a36Sopenharmony_ci			   HSIO_S6G_IB_CFG_IB_REG_PAT_SEL_OFFSET(0) |
17862306a36Sopenharmony_ci			   HSIO_S6G_IB_CFG_IB_SIG_DET_CLK_SEL(0));
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	regmap_update_bits(regmap, HSIO_S6G_IB_CFG1,
18162306a36Sopenharmony_ci			   HSIO_S6G_IB_CFG1_IB_TSDET_M,
18262306a36Sopenharmony_ci			   HSIO_S6G_IB_CFG1_IB_TSDET(16));
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	regmap_update_bits(regmap, HSIO_S6G_SER_CFG,
18562306a36Sopenharmony_ci			   HSIO_S6G_SER_CFG_SER_ALISEL_M |
18662306a36Sopenharmony_ci			   HSIO_S6G_SER_CFG_SER_ENALI,
18762306a36Sopenharmony_ci			   HSIO_S6G_SER_CFG_SER_ALISEL(0));
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	regmap_update_bits(regmap, HSIO_S6G_PLL_CFG,
19062306a36Sopenharmony_ci			   HSIO_S6G_PLL_CFG_PLL_DIV4 |
19162306a36Sopenharmony_ci			   HSIO_S6G_PLL_CFG_PLL_ENA_ROT |
19262306a36Sopenharmony_ci			   HSIO_S6G_PLL_CFG_PLL_FSM_CTRL_DATA_M |
19362306a36Sopenharmony_ci			   HSIO_S6G_PLL_CFG_PLL_ROT_DIR |
19462306a36Sopenharmony_ci			   HSIO_S6G_PLL_CFG_PLL_ROT_FRQ,
19562306a36Sopenharmony_ci			   HSIO_S6G_PLL_CFG_PLL_FSM_CTRL_DATA
19662306a36Sopenharmony_ci			   (pll_fsm_ctrl_data));
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	regmap_update_bits(regmap, HSIO_S6G_COMMON_CFG,
19962306a36Sopenharmony_ci			   HSIO_S6G_COMMON_CFG_SYS_RST |
20062306a36Sopenharmony_ci			   HSIO_S6G_COMMON_CFG_ENA_LANE |
20162306a36Sopenharmony_ci			   HSIO_S6G_COMMON_CFG_PWD_RX |
20262306a36Sopenharmony_ci			   HSIO_S6G_COMMON_CFG_PWD_TX |
20362306a36Sopenharmony_ci			   HSIO_S6G_COMMON_CFG_HRATE |
20462306a36Sopenharmony_ci			   HSIO_S6G_COMMON_CFG_QRATE |
20562306a36Sopenharmony_ci			   HSIO_S6G_COMMON_CFG_ENA_ELOOP |
20662306a36Sopenharmony_ci			   HSIO_S6G_COMMON_CFG_ENA_FLOOP |
20762306a36Sopenharmony_ci			   HSIO_S6G_COMMON_CFG_IF_MODE_M,
20862306a36Sopenharmony_ci			   HSIO_S6G_COMMON_CFG_SYS_RST |
20962306a36Sopenharmony_ci			   HSIO_S6G_COMMON_CFG_ENA_LANE |
21062306a36Sopenharmony_ci			   (qrate ? HSIO_S6G_COMMON_CFG_QRATE : 0) |
21162306a36Sopenharmony_ci			   HSIO_S6G_COMMON_CFG_IF_MODE(if_mode));
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	regmap_update_bits(regmap, HSIO_S6G_MISC_CFG,
21462306a36Sopenharmony_ci			   HSIO_S6G_MISC_CFG_LANE_RST |
21562306a36Sopenharmony_ci			   HSIO_S6G_MISC_CFG_DES_100FX_CPMD_ENA |
21662306a36Sopenharmony_ci			   HSIO_S6G_MISC_CFG_RX_LPI_MODE_ENA |
21762306a36Sopenharmony_ci			   HSIO_S6G_MISC_CFG_TX_LPI_MODE_ENA,
21862306a36Sopenharmony_ci			   HSIO_S6G_MISC_CFG_LANE_RST |
21962306a36Sopenharmony_ci			   HSIO_S6G_MISC_CFG_RX_LPI_MODE_ENA);
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	ret = serdes_commit_mcb_s6g(regmap, serdes);
22362306a36Sopenharmony_ci	if (ret)
22462306a36Sopenharmony_ci		return ret;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	regmap_update_bits(regmap, HSIO_S6G_PLL_CFG,
22762306a36Sopenharmony_ci			   HSIO_S6G_PLL_CFG_PLL_FSM_ENA,
22862306a36Sopenharmony_ci			   HSIO_S6G_PLL_CFG_PLL_FSM_ENA);
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	ret = serdes_commit_mcb_s6g(regmap, serdes);
23162306a36Sopenharmony_ci	if (ret)
23262306a36Sopenharmony_ci		return ret;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	/* Wait for PLL bringup */
23562306a36Sopenharmony_ci	msleep(20);
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	regmap_update_bits(regmap, HSIO_S6G_IB_CFG,
23862306a36Sopenharmony_ci			   HSIO_S6G_IB_CFG_IB_CAL_ENA,
23962306a36Sopenharmony_ci			   HSIO_S6G_IB_CFG_IB_CAL_ENA);
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	regmap_update_bits(regmap, HSIO_S6G_MISC_CFG,
24262306a36Sopenharmony_ci			   HSIO_S6G_MISC_CFG_LANE_RST, 0);
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	ret = serdes_commit_mcb_s6g(regmap, serdes);
24562306a36Sopenharmony_ci	if (ret)
24662306a36Sopenharmony_ci		return ret;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	/* Wait for calibration */
24962306a36Sopenharmony_ci	msleep(60);
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	regmap_update_bits(regmap, HSIO_S6G_IB_CFG,
25262306a36Sopenharmony_ci			   HSIO_S6G_IB_CFG_IB_REG_PAT_SEL_OFFSET_M |
25362306a36Sopenharmony_ci			   HSIO_S6G_IB_CFG_IB_SIG_DET_CLK_SEL_M,
25462306a36Sopenharmony_ci			   HSIO_S6G_IB_CFG_IB_REG_PAT_SEL_OFFSET(0) |
25562306a36Sopenharmony_ci			   HSIO_S6G_IB_CFG_IB_SIG_DET_CLK_SEL(7));
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	regmap_update_bits(regmap, HSIO_S6G_IB_CFG1,
25862306a36Sopenharmony_ci			   HSIO_S6G_IB_CFG1_IB_TSDET_M,
25962306a36Sopenharmony_ci			   HSIO_S6G_IB_CFG1_IB_TSDET(3));
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	/* IB CFG */
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	return 0;
26462306a36Sopenharmony_ci}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci#define MCB_S1G_CFG_TIMEOUT     50
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_cistatic int __serdes_write_mcb_s1g(struct regmap *regmap, u8 macro, u32 op)
26962306a36Sopenharmony_ci{
27062306a36Sopenharmony_ci	unsigned int regval;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	regmap_write(regmap, HSIO_MCB_S1G_ADDR_CFG, op |
27362306a36Sopenharmony_ci		     HSIO_MCB_S1G_ADDR_CFG_SERDES1G_ADDR(BIT(macro)));
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	return regmap_read_poll_timeout(regmap, HSIO_MCB_S1G_ADDR_CFG, regval,
27662306a36Sopenharmony_ci					(regval & op) != op, 100,
27762306a36Sopenharmony_ci					MCB_S1G_CFG_TIMEOUT * 1000);
27862306a36Sopenharmony_ci}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_cistatic int serdes_commit_mcb_s1g(struct regmap *regmap, u8 macro)
28162306a36Sopenharmony_ci{
28262306a36Sopenharmony_ci	return __serdes_write_mcb_s1g(regmap, macro,
28362306a36Sopenharmony_ci		HSIO_MCB_S1G_ADDR_CFG_SERDES1G_WR_ONE_SHOT);
28462306a36Sopenharmony_ci}
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_cistatic int serdes_update_mcb_s1g(struct regmap *regmap, u8 macro)
28762306a36Sopenharmony_ci{
28862306a36Sopenharmony_ci	return __serdes_write_mcb_s1g(regmap, macro,
28962306a36Sopenharmony_ci		HSIO_MCB_S1G_ADDR_CFG_SERDES1G_RD_ONE_SHOT);
29062306a36Sopenharmony_ci}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_cistatic int serdes_init_s1g(struct regmap *regmap, u8 serdes)
29362306a36Sopenharmony_ci{
29462306a36Sopenharmony_ci	int ret;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	ret = serdes_update_mcb_s1g(regmap, serdes);
29762306a36Sopenharmony_ci	if (ret)
29862306a36Sopenharmony_ci		return ret;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	regmap_update_bits(regmap, HSIO_S1G_COMMON_CFG,
30162306a36Sopenharmony_ci			   HSIO_S1G_COMMON_CFG_SYS_RST |
30262306a36Sopenharmony_ci			   HSIO_S1G_COMMON_CFG_ENA_LANE |
30362306a36Sopenharmony_ci			   HSIO_S1G_COMMON_CFG_ENA_ELOOP |
30462306a36Sopenharmony_ci			   HSIO_S1G_COMMON_CFG_ENA_FLOOP,
30562306a36Sopenharmony_ci			   HSIO_S1G_COMMON_CFG_ENA_LANE);
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	regmap_update_bits(regmap, HSIO_S1G_PLL_CFG,
30862306a36Sopenharmony_ci			   HSIO_S1G_PLL_CFG_PLL_FSM_ENA |
30962306a36Sopenharmony_ci			   HSIO_S1G_PLL_CFG_PLL_FSM_CTRL_DATA_M,
31062306a36Sopenharmony_ci			   HSIO_S1G_PLL_CFG_PLL_FSM_CTRL_DATA(200) |
31162306a36Sopenharmony_ci			   HSIO_S1G_PLL_CFG_PLL_FSM_ENA);
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	regmap_update_bits(regmap, HSIO_S1G_MISC_CFG,
31462306a36Sopenharmony_ci			   HSIO_S1G_MISC_CFG_DES_100FX_CPMD_ENA |
31562306a36Sopenharmony_ci			   HSIO_S1G_MISC_CFG_LANE_RST,
31662306a36Sopenharmony_ci			   HSIO_S1G_MISC_CFG_LANE_RST);
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	ret = serdes_commit_mcb_s1g(regmap, serdes);
31962306a36Sopenharmony_ci	if (ret)
32062306a36Sopenharmony_ci		return ret;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	regmap_update_bits(regmap, HSIO_S1G_COMMON_CFG,
32362306a36Sopenharmony_ci			   HSIO_S1G_COMMON_CFG_SYS_RST,
32462306a36Sopenharmony_ci			   HSIO_S1G_COMMON_CFG_SYS_RST);
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	regmap_update_bits(regmap, HSIO_S1G_MISC_CFG,
32762306a36Sopenharmony_ci			   HSIO_S1G_MISC_CFG_LANE_RST, 0);
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	ret = serdes_commit_mcb_s1g(regmap, serdes);
33062306a36Sopenharmony_ci	if (ret)
33162306a36Sopenharmony_ci		return ret;
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	return 0;
33462306a36Sopenharmony_ci}
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_cistruct serdes_mux {
33762306a36Sopenharmony_ci	u8			idx;
33862306a36Sopenharmony_ci	u8			port;
33962306a36Sopenharmony_ci	enum phy_mode		mode;
34062306a36Sopenharmony_ci	int			submode;
34162306a36Sopenharmony_ci	u32			mask;
34262306a36Sopenharmony_ci	u32			mux;
34362306a36Sopenharmony_ci};
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci#define SERDES_MUX(_idx, _port, _mode, _submode, _mask, _mux) { \
34662306a36Sopenharmony_ci	.idx = _idx,						\
34762306a36Sopenharmony_ci	.port = _port,						\
34862306a36Sopenharmony_ci	.mode = _mode,						\
34962306a36Sopenharmony_ci	.submode = _submode,					\
35062306a36Sopenharmony_ci	.mask = _mask,						\
35162306a36Sopenharmony_ci	.mux = _mux,						\
35262306a36Sopenharmony_ci}
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci#define SERDES_MUX_SGMII(i, p, m, c) \
35562306a36Sopenharmony_ci	SERDES_MUX(i, p, PHY_MODE_ETHERNET, PHY_INTERFACE_MODE_SGMII, m, c)
35662306a36Sopenharmony_ci#define SERDES_MUX_QSGMII(i, p, m, c) \
35762306a36Sopenharmony_ci	SERDES_MUX(i, p, PHY_MODE_ETHERNET, PHY_INTERFACE_MODE_QSGMII, m, c)
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_cistatic const struct serdes_mux ocelot_serdes_muxes[] = {
36062306a36Sopenharmony_ci	SERDES_MUX_SGMII(SERDES1G(0), 0, 0, 0),
36162306a36Sopenharmony_ci	SERDES_MUX_SGMII(SERDES1G(1), 1, HSIO_HW_CFG_DEV1G_5_MODE, 0),
36262306a36Sopenharmony_ci	SERDES_MUX_SGMII(SERDES1G(1), 5, HSIO_HW_CFG_QSGMII_ENA |
36362306a36Sopenharmony_ci			 HSIO_HW_CFG_DEV1G_5_MODE, HSIO_HW_CFG_DEV1G_5_MODE),
36462306a36Sopenharmony_ci	SERDES_MUX_SGMII(SERDES1G(2), 2, HSIO_HW_CFG_DEV1G_4_MODE, 0),
36562306a36Sopenharmony_ci	SERDES_MUX_SGMII(SERDES1G(2), 4, HSIO_HW_CFG_QSGMII_ENA |
36662306a36Sopenharmony_ci			 HSIO_HW_CFG_DEV1G_4_MODE, HSIO_HW_CFG_DEV1G_4_MODE),
36762306a36Sopenharmony_ci	SERDES_MUX_SGMII(SERDES1G(3), 3, HSIO_HW_CFG_DEV1G_6_MODE, 0),
36862306a36Sopenharmony_ci	SERDES_MUX_SGMII(SERDES1G(3), 6, HSIO_HW_CFG_QSGMII_ENA |
36962306a36Sopenharmony_ci			 HSIO_HW_CFG_DEV1G_6_MODE, HSIO_HW_CFG_DEV1G_6_MODE),
37062306a36Sopenharmony_ci	SERDES_MUX_SGMII(SERDES1G(4), 4, HSIO_HW_CFG_QSGMII_ENA |
37162306a36Sopenharmony_ci			 HSIO_HW_CFG_DEV1G_4_MODE | HSIO_HW_CFG_DEV1G_9_MODE,
37262306a36Sopenharmony_ci			 0),
37362306a36Sopenharmony_ci	SERDES_MUX_SGMII(SERDES1G(4), 9, HSIO_HW_CFG_DEV1G_4_MODE |
37462306a36Sopenharmony_ci			 HSIO_HW_CFG_DEV1G_9_MODE, HSIO_HW_CFG_DEV1G_4_MODE |
37562306a36Sopenharmony_ci			 HSIO_HW_CFG_DEV1G_9_MODE),
37662306a36Sopenharmony_ci	SERDES_MUX_SGMII(SERDES1G(5), 5, HSIO_HW_CFG_QSGMII_ENA |
37762306a36Sopenharmony_ci			 HSIO_HW_CFG_DEV1G_5_MODE | HSIO_HW_CFG_DEV2G5_10_MODE,
37862306a36Sopenharmony_ci			 0),
37962306a36Sopenharmony_ci	SERDES_MUX_SGMII(SERDES1G(5), 10, HSIO_HW_CFG_PCIE_ENA |
38062306a36Sopenharmony_ci			 HSIO_HW_CFG_DEV1G_5_MODE | HSIO_HW_CFG_DEV2G5_10_MODE,
38162306a36Sopenharmony_ci			 HSIO_HW_CFG_DEV1G_5_MODE | HSIO_HW_CFG_DEV2G5_10_MODE),
38262306a36Sopenharmony_ci	SERDES_MUX_QSGMII(SERDES6G(0), 4, HSIO_HW_CFG_QSGMII_ENA,
38362306a36Sopenharmony_ci			  HSIO_HW_CFG_QSGMII_ENA),
38462306a36Sopenharmony_ci	SERDES_MUX_QSGMII(SERDES6G(0), 5, HSIO_HW_CFG_QSGMII_ENA,
38562306a36Sopenharmony_ci			  HSIO_HW_CFG_QSGMII_ENA),
38662306a36Sopenharmony_ci	SERDES_MUX_QSGMII(SERDES6G(0), 6, HSIO_HW_CFG_QSGMII_ENA,
38762306a36Sopenharmony_ci			  HSIO_HW_CFG_QSGMII_ENA),
38862306a36Sopenharmony_ci	SERDES_MUX_SGMII(SERDES6G(0), 7, HSIO_HW_CFG_QSGMII_ENA, 0),
38962306a36Sopenharmony_ci	SERDES_MUX_QSGMII(SERDES6G(0), 7, HSIO_HW_CFG_QSGMII_ENA,
39062306a36Sopenharmony_ci			  HSIO_HW_CFG_QSGMII_ENA),
39162306a36Sopenharmony_ci	SERDES_MUX_SGMII(SERDES6G(1), 8, 0, 0),
39262306a36Sopenharmony_ci	SERDES_MUX_SGMII(SERDES6G(2), 10, HSIO_HW_CFG_PCIE_ENA |
39362306a36Sopenharmony_ci			 HSIO_HW_CFG_DEV2G5_10_MODE, 0),
39462306a36Sopenharmony_ci	SERDES_MUX(SERDES6G(2), 10, PHY_MODE_PCIE, 0, HSIO_HW_CFG_PCIE_ENA,
39562306a36Sopenharmony_ci		   HSIO_HW_CFG_PCIE_ENA),
39662306a36Sopenharmony_ci};
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_cistatic int serdes_set_mode(struct phy *phy, enum phy_mode mode, int submode)
39962306a36Sopenharmony_ci{
40062306a36Sopenharmony_ci	struct serdes_macro *macro = phy_get_drvdata(phy);
40162306a36Sopenharmony_ci	unsigned int i;
40262306a36Sopenharmony_ci	int ret;
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	/* As of now only PHY_MODE_ETHERNET is supported */
40562306a36Sopenharmony_ci	if (mode != PHY_MODE_ETHERNET)
40662306a36Sopenharmony_ci		return -EOPNOTSUPP;
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(ocelot_serdes_muxes); i++) {
40962306a36Sopenharmony_ci		if (macro->idx != ocelot_serdes_muxes[i].idx ||
41062306a36Sopenharmony_ci		    mode != ocelot_serdes_muxes[i].mode ||
41162306a36Sopenharmony_ci		    submode != ocelot_serdes_muxes[i].submode)
41262306a36Sopenharmony_ci			continue;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci		if (submode != PHY_INTERFACE_MODE_QSGMII &&
41562306a36Sopenharmony_ci		    macro->port != ocelot_serdes_muxes[i].port)
41662306a36Sopenharmony_ci			continue;
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci		ret = regmap_update_bits(macro->ctrl->regs, HSIO_HW_CFG,
41962306a36Sopenharmony_ci					 ocelot_serdes_muxes[i].mask,
42062306a36Sopenharmony_ci					 ocelot_serdes_muxes[i].mux);
42162306a36Sopenharmony_ci		if (ret)
42262306a36Sopenharmony_ci			return ret;
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci		if (macro->idx <= SERDES1G_MAX)
42562306a36Sopenharmony_ci			return serdes_init_s1g(macro->ctrl->regs, macro->idx);
42662306a36Sopenharmony_ci		else if (macro->idx <= SERDES6G_MAX)
42762306a36Sopenharmony_ci			return serdes_init_s6g(macro->ctrl->regs,
42862306a36Sopenharmony_ci					       macro->idx - (SERDES1G_MAX + 1),
42962306a36Sopenharmony_ci					       ocelot_serdes_muxes[i].submode);
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci		/* PCIe not supported yet */
43262306a36Sopenharmony_ci		return -EOPNOTSUPP;
43362306a36Sopenharmony_ci	}
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	return -EINVAL;
43662306a36Sopenharmony_ci}
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_cistatic const struct phy_ops serdes_ops = {
43962306a36Sopenharmony_ci	.set_mode	= serdes_set_mode,
44062306a36Sopenharmony_ci	.owner		= THIS_MODULE,
44162306a36Sopenharmony_ci};
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_cistatic struct phy *serdes_simple_xlate(struct device *dev,
44462306a36Sopenharmony_ci				       struct of_phandle_args *args)
44562306a36Sopenharmony_ci{
44662306a36Sopenharmony_ci	struct serdes_ctrl *ctrl = dev_get_drvdata(dev);
44762306a36Sopenharmony_ci	unsigned int port, idx, i;
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	if (args->args_count != 2)
45062306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	port = args->args[0];
45362306a36Sopenharmony_ci	idx = args->args[1];
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	for (i = 0; i < SERDES_MAX; i++) {
45662306a36Sopenharmony_ci		struct serdes_macro *macro = phy_get_drvdata(ctrl->phys[i]);
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci		if (idx != macro->idx)
45962306a36Sopenharmony_ci			continue;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci		/* SERDES6G(0) is the only SerDes capable of QSGMII */
46262306a36Sopenharmony_ci		if (idx != SERDES6G(0) && macro->port >= 0)
46362306a36Sopenharmony_ci			return ERR_PTR(-EBUSY);
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci		macro->port = port;
46662306a36Sopenharmony_ci		return ctrl->phys[i];
46762306a36Sopenharmony_ci	}
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	return ERR_PTR(-ENODEV);
47062306a36Sopenharmony_ci}
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_cistatic int serdes_phy_create(struct serdes_ctrl *ctrl, u8 idx, struct phy **phy)
47362306a36Sopenharmony_ci{
47462306a36Sopenharmony_ci	struct serdes_macro *macro;
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	*phy = devm_phy_create(ctrl->dev, NULL, &serdes_ops);
47762306a36Sopenharmony_ci	if (IS_ERR(*phy))
47862306a36Sopenharmony_ci		return PTR_ERR(*phy);
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	macro = devm_kzalloc(ctrl->dev, sizeof(*macro), GFP_KERNEL);
48162306a36Sopenharmony_ci	if (!macro)
48262306a36Sopenharmony_ci		return -ENOMEM;
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	macro->idx = idx;
48562306a36Sopenharmony_ci	macro->ctrl = ctrl;
48662306a36Sopenharmony_ci	macro->port = -1;
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	phy_set_drvdata(*phy, macro);
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	return 0;
49162306a36Sopenharmony_ci}
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_cistatic int serdes_probe(struct platform_device *pdev)
49462306a36Sopenharmony_ci{
49562306a36Sopenharmony_ci	struct phy_provider *provider;
49662306a36Sopenharmony_ci	struct serdes_ctrl *ctrl;
49762306a36Sopenharmony_ci	struct resource *res;
49862306a36Sopenharmony_ci	unsigned int i;
49962306a36Sopenharmony_ci	int ret;
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	ctrl = devm_kzalloc(&pdev->dev, sizeof(*ctrl), GFP_KERNEL);
50262306a36Sopenharmony_ci	if (!ctrl)
50362306a36Sopenharmony_ci		return -ENOMEM;
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	ctrl->dev = &pdev->dev;
50662306a36Sopenharmony_ci	ctrl->regs = syscon_node_to_regmap(pdev->dev.parent->of_node);
50762306a36Sopenharmony_ci	if (IS_ERR(ctrl->regs)) {
50862306a36Sopenharmony_ci		/* Fall back to using IORESOURCE_REG, if possible */
50962306a36Sopenharmony_ci		res = platform_get_resource(pdev, IORESOURCE_REG, 0);
51062306a36Sopenharmony_ci		if (res)
51162306a36Sopenharmony_ci			ctrl->regs = dev_get_regmap(ctrl->dev->parent,
51262306a36Sopenharmony_ci						    res->name);
51362306a36Sopenharmony_ci	}
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	if (IS_ERR(ctrl->regs))
51662306a36Sopenharmony_ci		return PTR_ERR(ctrl->regs);
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	for (i = 0; i < SERDES_MAX; i++) {
51962306a36Sopenharmony_ci		ret = serdes_phy_create(ctrl, i, &ctrl->phys[i]);
52062306a36Sopenharmony_ci		if (ret)
52162306a36Sopenharmony_ci			return ret;
52262306a36Sopenharmony_ci	}
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	dev_set_drvdata(&pdev->dev, ctrl);
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	provider = devm_of_phy_provider_register(ctrl->dev,
52762306a36Sopenharmony_ci						 serdes_simple_xlate);
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	return PTR_ERR_OR_ZERO(provider);
53062306a36Sopenharmony_ci}
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_cistatic const struct of_device_id serdes_ids[] = {
53362306a36Sopenharmony_ci	{ .compatible = "mscc,vsc7514-serdes", },
53462306a36Sopenharmony_ci	{},
53562306a36Sopenharmony_ci};
53662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, serdes_ids);
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_cistatic struct platform_driver mscc_ocelot_serdes = {
53962306a36Sopenharmony_ci	.probe		= serdes_probe,
54062306a36Sopenharmony_ci	.driver		= {
54162306a36Sopenharmony_ci		.name	= "mscc,ocelot-serdes",
54262306a36Sopenharmony_ci		.of_match_table = of_match_ptr(serdes_ids),
54362306a36Sopenharmony_ci	},
54462306a36Sopenharmony_ci};
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_cimodule_platform_driver(mscc_ocelot_serdes);
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ciMODULE_AUTHOR("Quentin Schulz <quentin.schulz@bootlin.com>");
54962306a36Sopenharmony_ciMODULE_DESCRIPTION("SerDes driver for Microsemi Ocelot");
55062306a36Sopenharmony_ciMODULE_LICENSE("Dual MIT/GPL");
551