151f6b410SQuentin Schulz // SPDX-License-Identifier: (GPL-2.0 OR MIT)
251f6b410SQuentin Schulz /*
351f6b410SQuentin Schulz * SerDes PHY driver for Microsemi Ocelot
451f6b410SQuentin Schulz *
551f6b410SQuentin Schulz * Copyright (c) 2018 Microsemi
651f6b410SQuentin Schulz *
751f6b410SQuentin Schulz */
851f6b410SQuentin Schulz
951f6b410SQuentin Schulz #include <linux/err.h>
1051f6b410SQuentin Schulz #include <linux/mfd/syscon.h>
1151f6b410SQuentin Schulz #include <linux/module.h>
1251f6b410SQuentin Schulz #include <linux/of.h>
1351f6b410SQuentin Schulz #include <linux/of_platform.h>
14c8fe6d7fSGrygorii Strashko #include <linux/phy.h>
1551f6b410SQuentin Schulz #include <linux/phy/phy.h>
1651f6b410SQuentin Schulz #include <linux/platform_device.h>
1751f6b410SQuentin Schulz #include <linux/regmap.h>
1851f6b410SQuentin Schulz #include <soc/mscc/ocelot_hsio.h>
1951f6b410SQuentin Schulz #include <dt-bindings/phy/phy-ocelot-serdes.h>
2051f6b410SQuentin Schulz
2151f6b410SQuentin Schulz struct serdes_ctrl {
2251f6b410SQuentin Schulz struct regmap *regs;
2351f6b410SQuentin Schulz struct device *dev;
2451f6b410SQuentin Schulz struct phy *phys[SERDES_MAX];
2551f6b410SQuentin Schulz };
2651f6b410SQuentin Schulz
2751f6b410SQuentin Schulz struct serdes_macro {
2851f6b410SQuentin Schulz u8 idx;
2951f6b410SQuentin Schulz /* Not used when in QSGMII or PCIe mode */
3051f6b410SQuentin Schulz int port;
3151f6b410SQuentin Schulz struct serdes_ctrl *ctrl;
3251f6b410SQuentin Schulz };
3351f6b410SQuentin Schulz
3461c67bfaSKavya Sree Kotagiri #define MCB_S6G_CFG_TIMEOUT 50
3561c67bfaSKavya Sree Kotagiri
__serdes_write_mcb_s6g(struct regmap * regmap,u8 macro,u32 op)3661c67bfaSKavya Sree Kotagiri static int __serdes_write_mcb_s6g(struct regmap *regmap, u8 macro, u32 op)
3761c67bfaSKavya Sree Kotagiri {
3861c67bfaSKavya Sree Kotagiri unsigned int regval = 0;
3961c67bfaSKavya Sree Kotagiri
4061c67bfaSKavya Sree Kotagiri regmap_write(regmap, HSIO_MCB_S6G_ADDR_CFG, op |
4161c67bfaSKavya Sree Kotagiri HSIO_MCB_S6G_ADDR_CFG_SERDES6G_ADDR(BIT(macro)));
4261c67bfaSKavya Sree Kotagiri
4361c67bfaSKavya Sree Kotagiri return regmap_read_poll_timeout(regmap, HSIO_MCB_S6G_ADDR_CFG, regval,
4461c67bfaSKavya Sree Kotagiri (regval & op) != op, 100,
4561c67bfaSKavya Sree Kotagiri MCB_S6G_CFG_TIMEOUT * 1000);
4661c67bfaSKavya Sree Kotagiri }
4761c67bfaSKavya Sree Kotagiri
serdes_commit_mcb_s6g(struct regmap * regmap,u8 macro)4861c67bfaSKavya Sree Kotagiri static int serdes_commit_mcb_s6g(struct regmap *regmap, u8 macro)
4961c67bfaSKavya Sree Kotagiri {
5061c67bfaSKavya Sree Kotagiri return __serdes_write_mcb_s6g(regmap, macro,
5161c67bfaSKavya Sree Kotagiri HSIO_MCB_S6G_ADDR_CFG_SERDES6G_WR_ONE_SHOT);
5261c67bfaSKavya Sree Kotagiri }
5361c67bfaSKavya Sree Kotagiri
serdes_update_mcb_s6g(struct regmap * regmap,u8 macro)5461c67bfaSKavya Sree Kotagiri static int serdes_update_mcb_s6g(struct regmap *regmap, u8 macro)
5561c67bfaSKavya Sree Kotagiri {
5661c67bfaSKavya Sree Kotagiri return __serdes_write_mcb_s6g(regmap, macro,
5761c67bfaSKavya Sree Kotagiri HSIO_MCB_S6G_ADDR_CFG_SERDES6G_RD_ONE_SHOT);
5861c67bfaSKavya Sree Kotagiri }
5961c67bfaSKavya Sree Kotagiri
serdes_init_s6g(struct regmap * regmap,u8 serdes,int mode)6061c67bfaSKavya Sree Kotagiri static int serdes_init_s6g(struct regmap *regmap, u8 serdes, int mode)
6161c67bfaSKavya Sree Kotagiri {
6261c67bfaSKavya Sree Kotagiri u32 pll_fsm_ctrl_data;
6361c67bfaSKavya Sree Kotagiri u32 ob_ena1v_mode;
6461c67bfaSKavya Sree Kotagiri u32 des_bw_ana;
6561c67bfaSKavya Sree Kotagiri u32 ob_ena_cas;
6661c67bfaSKavya Sree Kotagiri u32 if_mode;
6761c67bfaSKavya Sree Kotagiri u32 ob_lev;
6861c67bfaSKavya Sree Kotagiri u32 qrate;
6961c67bfaSKavya Sree Kotagiri int ret;
7061c67bfaSKavya Sree Kotagiri
7161c67bfaSKavya Sree Kotagiri if (mode == PHY_INTERFACE_MODE_QSGMII) {
7261c67bfaSKavya Sree Kotagiri pll_fsm_ctrl_data = 120;
7361c67bfaSKavya Sree Kotagiri ob_ena1v_mode = 0;
7461c67bfaSKavya Sree Kotagiri ob_ena_cas = 0;
7561c67bfaSKavya Sree Kotagiri des_bw_ana = 5;
7661c67bfaSKavya Sree Kotagiri ob_lev = 24;
7761c67bfaSKavya Sree Kotagiri if_mode = 3;
7861c67bfaSKavya Sree Kotagiri qrate = 0;
7961c67bfaSKavya Sree Kotagiri } else {
8061c67bfaSKavya Sree Kotagiri pll_fsm_ctrl_data = 60;
8161c67bfaSKavya Sree Kotagiri ob_ena1v_mode = 1;
8261c67bfaSKavya Sree Kotagiri ob_ena_cas = 2;
8361c67bfaSKavya Sree Kotagiri des_bw_ana = 3;
8461c67bfaSKavya Sree Kotagiri ob_lev = 48;
8561c67bfaSKavya Sree Kotagiri if_mode = 1;
8661c67bfaSKavya Sree Kotagiri qrate = 1;
8761c67bfaSKavya Sree Kotagiri }
8861c67bfaSKavya Sree Kotagiri
8961c67bfaSKavya Sree Kotagiri ret = serdes_update_mcb_s6g(regmap, serdes);
9061c67bfaSKavya Sree Kotagiri if (ret)
9161c67bfaSKavya Sree Kotagiri return ret;
9261c67bfaSKavya Sree Kotagiri
9361c67bfaSKavya Sree Kotagiri /* Test pattern */
9461c67bfaSKavya Sree Kotagiri
9561c67bfaSKavya Sree Kotagiri regmap_update_bits(regmap, HSIO_S6G_COMMON_CFG,
9661c67bfaSKavya Sree Kotagiri HSIO_S6G_COMMON_CFG_SYS_RST, 0);
9761c67bfaSKavya Sree Kotagiri
9861c67bfaSKavya Sree Kotagiri regmap_update_bits(regmap, HSIO_S6G_PLL_CFG,
9961c67bfaSKavya Sree Kotagiri HSIO_S6G_PLL_CFG_PLL_FSM_ENA, 0);
10061c67bfaSKavya Sree Kotagiri
10161c67bfaSKavya Sree Kotagiri regmap_update_bits(regmap, HSIO_S6G_IB_CFG,
10261c67bfaSKavya Sree Kotagiri HSIO_S6G_IB_CFG_IB_SIG_DET_ENA |
10361c67bfaSKavya Sree Kotagiri HSIO_S6G_IB_CFG_IB_REG_ENA |
10461c67bfaSKavya Sree Kotagiri HSIO_S6G_IB_CFG_IB_SAM_ENA |
10561c67bfaSKavya Sree Kotagiri HSIO_S6G_IB_CFG_IB_EQZ_ENA |
10661c67bfaSKavya Sree Kotagiri HSIO_S6G_IB_CFG_IB_CONCUR |
10761c67bfaSKavya Sree Kotagiri HSIO_S6G_IB_CFG_IB_CAL_ENA,
10861c67bfaSKavya Sree Kotagiri HSIO_S6G_IB_CFG_IB_SIG_DET_ENA |
10961c67bfaSKavya Sree Kotagiri HSIO_S6G_IB_CFG_IB_REG_ENA |
11061c67bfaSKavya Sree Kotagiri HSIO_S6G_IB_CFG_IB_SAM_ENA |
11161c67bfaSKavya Sree Kotagiri HSIO_S6G_IB_CFG_IB_EQZ_ENA |
11261c67bfaSKavya Sree Kotagiri HSIO_S6G_IB_CFG_IB_CONCUR);
11361c67bfaSKavya Sree Kotagiri
11461c67bfaSKavya Sree Kotagiri regmap_update_bits(regmap, HSIO_S6G_IB_CFG1,
11561c67bfaSKavya Sree Kotagiri HSIO_S6G_IB_CFG1_IB_FRC_OFFSET |
11661c67bfaSKavya Sree Kotagiri HSIO_S6G_IB_CFG1_IB_FRC_LP |
11761c67bfaSKavya Sree Kotagiri HSIO_S6G_IB_CFG1_IB_FRC_MID |
11861c67bfaSKavya Sree Kotagiri HSIO_S6G_IB_CFG1_IB_FRC_HP |
11961c67bfaSKavya Sree Kotagiri HSIO_S6G_IB_CFG1_IB_FILT_OFFSET |
12061c67bfaSKavya Sree Kotagiri HSIO_S6G_IB_CFG1_IB_FILT_LP |
12161c67bfaSKavya Sree Kotagiri HSIO_S6G_IB_CFG1_IB_FILT_MID |
12261c67bfaSKavya Sree Kotagiri HSIO_S6G_IB_CFG1_IB_FILT_HP,
12361c67bfaSKavya Sree Kotagiri HSIO_S6G_IB_CFG1_IB_FILT_OFFSET |
12461c67bfaSKavya Sree Kotagiri HSIO_S6G_IB_CFG1_IB_FILT_HP |
12561c67bfaSKavya Sree Kotagiri HSIO_S6G_IB_CFG1_IB_FILT_LP |
12661c67bfaSKavya Sree Kotagiri HSIO_S6G_IB_CFG1_IB_FILT_MID);
12761c67bfaSKavya Sree Kotagiri
12861c67bfaSKavya Sree Kotagiri regmap_update_bits(regmap, HSIO_S6G_IB_CFG2,
12961c67bfaSKavya Sree Kotagiri HSIO_S6G_IB_CFG2_IB_UREG_M,
13061c67bfaSKavya Sree Kotagiri HSIO_S6G_IB_CFG2_IB_UREG(4));
13161c67bfaSKavya Sree Kotagiri
13261c67bfaSKavya Sree Kotagiri regmap_update_bits(regmap, HSIO_S6G_IB_CFG3,
13361c67bfaSKavya Sree Kotagiri HSIO_S6G_IB_CFG3_IB_INI_OFFSET_M |
13461c67bfaSKavya Sree Kotagiri HSIO_S6G_IB_CFG3_IB_INI_LP_M |
13561c67bfaSKavya Sree Kotagiri HSIO_S6G_IB_CFG3_IB_INI_MID_M |
13661c67bfaSKavya Sree Kotagiri HSIO_S6G_IB_CFG3_IB_INI_HP_M,
13761c67bfaSKavya Sree Kotagiri HSIO_S6G_IB_CFG3_IB_INI_OFFSET(31) |
13861c67bfaSKavya Sree Kotagiri HSIO_S6G_IB_CFG3_IB_INI_LP(1) |
13961c67bfaSKavya Sree Kotagiri HSIO_S6G_IB_CFG3_IB_INI_MID(31) |
14061c67bfaSKavya Sree Kotagiri HSIO_S6G_IB_CFG3_IB_INI_HP(0));
14161c67bfaSKavya Sree Kotagiri
14261c67bfaSKavya Sree Kotagiri regmap_update_bits(regmap, HSIO_S6G_MISC_CFG,
14361c67bfaSKavya Sree Kotagiri HSIO_S6G_MISC_CFG_LANE_RST,
14461c67bfaSKavya Sree Kotagiri HSIO_S6G_MISC_CFG_LANE_RST);
14561c67bfaSKavya Sree Kotagiri
14661c67bfaSKavya Sree Kotagiri ret = serdes_commit_mcb_s6g(regmap, serdes);
14761c67bfaSKavya Sree Kotagiri if (ret)
14861c67bfaSKavya Sree Kotagiri return ret;
14961c67bfaSKavya Sree Kotagiri
15061c67bfaSKavya Sree Kotagiri /* OB + DES + IB + SER CFG */
15161c67bfaSKavya Sree Kotagiri regmap_update_bits(regmap, HSIO_S6G_OB_CFG,
15261c67bfaSKavya Sree Kotagiri HSIO_S6G_OB_CFG_OB_IDLE |
15361c67bfaSKavya Sree Kotagiri HSIO_S6G_OB_CFG_OB_ENA1V_MODE |
15461c67bfaSKavya Sree Kotagiri HSIO_S6G_OB_CFG_OB_POST0_M |
15561c67bfaSKavya Sree Kotagiri HSIO_S6G_OB_CFG_OB_PREC_M,
15661c67bfaSKavya Sree Kotagiri (ob_ena1v_mode ? HSIO_S6G_OB_CFG_OB_ENA1V_MODE : 0) |
15761c67bfaSKavya Sree Kotagiri HSIO_S6G_OB_CFG_OB_POST0(0) |
15861c67bfaSKavya Sree Kotagiri HSIO_S6G_OB_CFG_OB_PREC(0));
15961c67bfaSKavya Sree Kotagiri
16061c67bfaSKavya Sree Kotagiri regmap_update_bits(regmap, HSIO_S6G_OB_CFG1,
16161c67bfaSKavya Sree Kotagiri HSIO_S6G_OB_CFG1_OB_ENA_CAS_M |
16261c67bfaSKavya Sree Kotagiri HSIO_S6G_OB_CFG1_OB_LEV_M,
16361c67bfaSKavya Sree Kotagiri HSIO_S6G_OB_CFG1_OB_LEV(ob_lev) |
16461c67bfaSKavya Sree Kotagiri HSIO_S6G_OB_CFG1_OB_ENA_CAS(ob_ena_cas));
16561c67bfaSKavya Sree Kotagiri
16661c67bfaSKavya Sree Kotagiri regmap_update_bits(regmap, HSIO_S6G_DES_CFG,
16761c67bfaSKavya Sree Kotagiri HSIO_S6G_DES_CFG_DES_PHS_CTRL_M |
16861c67bfaSKavya Sree Kotagiri HSIO_S6G_DES_CFG_DES_CPMD_SEL_M |
16961c67bfaSKavya Sree Kotagiri HSIO_S6G_DES_CFG_DES_BW_ANA_M,
17061c67bfaSKavya Sree Kotagiri HSIO_S6G_DES_CFG_DES_PHS_CTRL(2) |
17161c67bfaSKavya Sree Kotagiri HSIO_S6G_DES_CFG_DES_CPMD_SEL(0) |
17261c67bfaSKavya Sree Kotagiri HSIO_S6G_DES_CFG_DES_BW_ANA(des_bw_ana));
17361c67bfaSKavya Sree Kotagiri
17461c67bfaSKavya Sree Kotagiri regmap_update_bits(regmap, HSIO_S6G_IB_CFG,
17561c67bfaSKavya Sree Kotagiri HSIO_S6G_IB_CFG_IB_SIG_DET_CLK_SEL_M |
17661c67bfaSKavya Sree Kotagiri HSIO_S6G_IB_CFG_IB_REG_PAT_SEL_OFFSET_M,
17761c67bfaSKavya Sree Kotagiri HSIO_S6G_IB_CFG_IB_REG_PAT_SEL_OFFSET(0) |
17861c67bfaSKavya Sree Kotagiri HSIO_S6G_IB_CFG_IB_SIG_DET_CLK_SEL(0));
17961c67bfaSKavya Sree Kotagiri
18061c67bfaSKavya Sree Kotagiri regmap_update_bits(regmap, HSIO_S6G_IB_CFG1,
18161c67bfaSKavya Sree Kotagiri HSIO_S6G_IB_CFG1_IB_TSDET_M,
18261c67bfaSKavya Sree Kotagiri HSIO_S6G_IB_CFG1_IB_TSDET(16));
18361c67bfaSKavya Sree Kotagiri
18461c67bfaSKavya Sree Kotagiri regmap_update_bits(regmap, HSIO_S6G_SER_CFG,
18561c67bfaSKavya Sree Kotagiri HSIO_S6G_SER_CFG_SER_ALISEL_M |
18661c67bfaSKavya Sree Kotagiri HSIO_S6G_SER_CFG_SER_ENALI,
18761c67bfaSKavya Sree Kotagiri HSIO_S6G_SER_CFG_SER_ALISEL(0));
18861c67bfaSKavya Sree Kotagiri
18961c67bfaSKavya Sree Kotagiri regmap_update_bits(regmap, HSIO_S6G_PLL_CFG,
19061c67bfaSKavya Sree Kotagiri HSIO_S6G_PLL_CFG_PLL_DIV4 |
19161c67bfaSKavya Sree Kotagiri HSIO_S6G_PLL_CFG_PLL_ENA_ROT |
19261c67bfaSKavya Sree Kotagiri HSIO_S6G_PLL_CFG_PLL_FSM_CTRL_DATA_M |
19361c67bfaSKavya Sree Kotagiri HSIO_S6G_PLL_CFG_PLL_ROT_DIR |
19461c67bfaSKavya Sree Kotagiri HSIO_S6G_PLL_CFG_PLL_ROT_FRQ,
19561c67bfaSKavya Sree Kotagiri HSIO_S6G_PLL_CFG_PLL_FSM_CTRL_DATA
19661c67bfaSKavya Sree Kotagiri (pll_fsm_ctrl_data));
19761c67bfaSKavya Sree Kotagiri
19861c67bfaSKavya Sree Kotagiri regmap_update_bits(regmap, HSIO_S6G_COMMON_CFG,
19961c67bfaSKavya Sree Kotagiri HSIO_S6G_COMMON_CFG_SYS_RST |
20061c67bfaSKavya Sree Kotagiri HSIO_S6G_COMMON_CFG_ENA_LANE |
20161c67bfaSKavya Sree Kotagiri HSIO_S6G_COMMON_CFG_PWD_RX |
20261c67bfaSKavya Sree Kotagiri HSIO_S6G_COMMON_CFG_PWD_TX |
20361c67bfaSKavya Sree Kotagiri HSIO_S6G_COMMON_CFG_HRATE |
20461c67bfaSKavya Sree Kotagiri HSIO_S6G_COMMON_CFG_QRATE |
20561c67bfaSKavya Sree Kotagiri HSIO_S6G_COMMON_CFG_ENA_ELOOP |
20661c67bfaSKavya Sree Kotagiri HSIO_S6G_COMMON_CFG_ENA_FLOOP |
20761c67bfaSKavya Sree Kotagiri HSIO_S6G_COMMON_CFG_IF_MODE_M,
20861c67bfaSKavya Sree Kotagiri HSIO_S6G_COMMON_CFG_SYS_RST |
20961c67bfaSKavya Sree Kotagiri HSIO_S6G_COMMON_CFG_ENA_LANE |
21061c67bfaSKavya Sree Kotagiri (qrate ? HSIO_S6G_COMMON_CFG_QRATE : 0) |
21161c67bfaSKavya Sree Kotagiri HSIO_S6G_COMMON_CFG_IF_MODE(if_mode));
21261c67bfaSKavya Sree Kotagiri
21361c67bfaSKavya Sree Kotagiri regmap_update_bits(regmap, HSIO_S6G_MISC_CFG,
21461c67bfaSKavya Sree Kotagiri HSIO_S6G_MISC_CFG_LANE_RST |
21561c67bfaSKavya Sree Kotagiri HSIO_S6G_MISC_CFG_DES_100FX_CPMD_ENA |
21661c67bfaSKavya Sree Kotagiri HSIO_S6G_MISC_CFG_RX_LPI_MODE_ENA |
21761c67bfaSKavya Sree Kotagiri HSIO_S6G_MISC_CFG_TX_LPI_MODE_ENA,
21861c67bfaSKavya Sree Kotagiri HSIO_S6G_MISC_CFG_LANE_RST |
21961c67bfaSKavya Sree Kotagiri HSIO_S6G_MISC_CFG_RX_LPI_MODE_ENA);
22061c67bfaSKavya Sree Kotagiri
22161c67bfaSKavya Sree Kotagiri
22261c67bfaSKavya Sree Kotagiri ret = serdes_commit_mcb_s6g(regmap, serdes);
22361c67bfaSKavya Sree Kotagiri if (ret)
22461c67bfaSKavya Sree Kotagiri return ret;
22561c67bfaSKavya Sree Kotagiri
22661c67bfaSKavya Sree Kotagiri regmap_update_bits(regmap, HSIO_S6G_PLL_CFG,
22761c67bfaSKavya Sree Kotagiri HSIO_S6G_PLL_CFG_PLL_FSM_ENA,
22861c67bfaSKavya Sree Kotagiri HSIO_S6G_PLL_CFG_PLL_FSM_ENA);
22961c67bfaSKavya Sree Kotagiri
23061c67bfaSKavya Sree Kotagiri ret = serdes_commit_mcb_s6g(regmap, serdes);
23161c67bfaSKavya Sree Kotagiri if (ret)
23261c67bfaSKavya Sree Kotagiri return ret;
23361c67bfaSKavya Sree Kotagiri
23461c67bfaSKavya Sree Kotagiri /* Wait for PLL bringup */
23561c67bfaSKavya Sree Kotagiri msleep(20);
23661c67bfaSKavya Sree Kotagiri
23761c67bfaSKavya Sree Kotagiri regmap_update_bits(regmap, HSIO_S6G_IB_CFG,
23861c67bfaSKavya Sree Kotagiri HSIO_S6G_IB_CFG_IB_CAL_ENA,
23961c67bfaSKavya Sree Kotagiri HSIO_S6G_IB_CFG_IB_CAL_ENA);
24061c67bfaSKavya Sree Kotagiri
24161c67bfaSKavya Sree Kotagiri regmap_update_bits(regmap, HSIO_S6G_MISC_CFG,
24261c67bfaSKavya Sree Kotagiri HSIO_S6G_MISC_CFG_LANE_RST, 0);
24361c67bfaSKavya Sree Kotagiri
24461c67bfaSKavya Sree Kotagiri ret = serdes_commit_mcb_s6g(regmap, serdes);
24561c67bfaSKavya Sree Kotagiri if (ret)
24661c67bfaSKavya Sree Kotagiri return ret;
24761c67bfaSKavya Sree Kotagiri
24861c67bfaSKavya Sree Kotagiri /* Wait for calibration */
24961c67bfaSKavya Sree Kotagiri msleep(60);
25061c67bfaSKavya Sree Kotagiri
25161c67bfaSKavya Sree Kotagiri regmap_update_bits(regmap, HSIO_S6G_IB_CFG,
25261c67bfaSKavya Sree Kotagiri HSIO_S6G_IB_CFG_IB_REG_PAT_SEL_OFFSET_M |
25361c67bfaSKavya Sree Kotagiri HSIO_S6G_IB_CFG_IB_SIG_DET_CLK_SEL_M,
25461c67bfaSKavya Sree Kotagiri HSIO_S6G_IB_CFG_IB_REG_PAT_SEL_OFFSET(0) |
25561c67bfaSKavya Sree Kotagiri HSIO_S6G_IB_CFG_IB_SIG_DET_CLK_SEL(7));
25661c67bfaSKavya Sree Kotagiri
25761c67bfaSKavya Sree Kotagiri regmap_update_bits(regmap, HSIO_S6G_IB_CFG1,
25861c67bfaSKavya Sree Kotagiri HSIO_S6G_IB_CFG1_IB_TSDET_M,
25961c67bfaSKavya Sree Kotagiri HSIO_S6G_IB_CFG1_IB_TSDET(3));
26061c67bfaSKavya Sree Kotagiri
26161c67bfaSKavya Sree Kotagiri /* IB CFG */
26261c67bfaSKavya Sree Kotagiri
26361c67bfaSKavya Sree Kotagiri return 0;
26461c67bfaSKavya Sree Kotagiri }
26561c67bfaSKavya Sree Kotagiri
26651f6b410SQuentin Schulz #define MCB_S1G_CFG_TIMEOUT 50
26751f6b410SQuentin Schulz
__serdes_write_mcb_s1g(struct regmap * regmap,u8 macro,u32 op)26851f6b410SQuentin Schulz static int __serdes_write_mcb_s1g(struct regmap *regmap, u8 macro, u32 op)
26951f6b410SQuentin Schulz {
27051f6b410SQuentin Schulz unsigned int regval;
27151f6b410SQuentin Schulz
27251f6b410SQuentin Schulz regmap_write(regmap, HSIO_MCB_S1G_ADDR_CFG, op |
27351f6b410SQuentin Schulz HSIO_MCB_S1G_ADDR_CFG_SERDES1G_ADDR(BIT(macro)));
27451f6b410SQuentin Schulz
27551f6b410SQuentin Schulz return regmap_read_poll_timeout(regmap, HSIO_MCB_S1G_ADDR_CFG, regval,
27651f6b410SQuentin Schulz (regval & op) != op, 100,
27751f6b410SQuentin Schulz MCB_S1G_CFG_TIMEOUT * 1000);
27851f6b410SQuentin Schulz }
27951f6b410SQuentin Schulz
serdes_commit_mcb_s1g(struct regmap * regmap,u8 macro)28051f6b410SQuentin Schulz static int serdes_commit_mcb_s1g(struct regmap *regmap, u8 macro)
28151f6b410SQuentin Schulz {
28251f6b410SQuentin Schulz return __serdes_write_mcb_s1g(regmap, macro,
28351f6b410SQuentin Schulz HSIO_MCB_S1G_ADDR_CFG_SERDES1G_WR_ONE_SHOT);
28451f6b410SQuentin Schulz }
28551f6b410SQuentin Schulz
serdes_update_mcb_s1g(struct regmap * regmap,u8 macro)28651f6b410SQuentin Schulz static int serdes_update_mcb_s1g(struct regmap *regmap, u8 macro)
28751f6b410SQuentin Schulz {
28851f6b410SQuentin Schulz return __serdes_write_mcb_s1g(regmap, macro,
28951f6b410SQuentin Schulz HSIO_MCB_S1G_ADDR_CFG_SERDES1G_RD_ONE_SHOT);
29051f6b410SQuentin Schulz }
29151f6b410SQuentin Schulz
serdes_init_s1g(struct regmap * regmap,u8 serdes)29251f6b410SQuentin Schulz static int serdes_init_s1g(struct regmap *regmap, u8 serdes)
29351f6b410SQuentin Schulz {
29451f6b410SQuentin Schulz int ret;
29551f6b410SQuentin Schulz
29651f6b410SQuentin Schulz ret = serdes_update_mcb_s1g(regmap, serdes);
29751f6b410SQuentin Schulz if (ret)
29851f6b410SQuentin Schulz return ret;
29951f6b410SQuentin Schulz
30051f6b410SQuentin Schulz regmap_update_bits(regmap, HSIO_S1G_COMMON_CFG,
30151f6b410SQuentin Schulz HSIO_S1G_COMMON_CFG_SYS_RST |
30251f6b410SQuentin Schulz HSIO_S1G_COMMON_CFG_ENA_LANE |
30351f6b410SQuentin Schulz HSIO_S1G_COMMON_CFG_ENA_ELOOP |
30451f6b410SQuentin Schulz HSIO_S1G_COMMON_CFG_ENA_FLOOP,
30551f6b410SQuentin Schulz HSIO_S1G_COMMON_CFG_ENA_LANE);
30651f6b410SQuentin Schulz
30751f6b410SQuentin Schulz regmap_update_bits(regmap, HSIO_S1G_PLL_CFG,
30851f6b410SQuentin Schulz HSIO_S1G_PLL_CFG_PLL_FSM_ENA |
30951f6b410SQuentin Schulz HSIO_S1G_PLL_CFG_PLL_FSM_CTRL_DATA_M,
31051f6b410SQuentin Schulz HSIO_S1G_PLL_CFG_PLL_FSM_CTRL_DATA(200) |
31151f6b410SQuentin Schulz HSIO_S1G_PLL_CFG_PLL_FSM_ENA);
31251f6b410SQuentin Schulz
31351f6b410SQuentin Schulz regmap_update_bits(regmap, HSIO_S1G_MISC_CFG,
31451f6b410SQuentin Schulz HSIO_S1G_MISC_CFG_DES_100FX_CPMD_ENA |
31551f6b410SQuentin Schulz HSIO_S1G_MISC_CFG_LANE_RST,
31651f6b410SQuentin Schulz HSIO_S1G_MISC_CFG_LANE_RST);
31751f6b410SQuentin Schulz
31851f6b410SQuentin Schulz ret = serdes_commit_mcb_s1g(regmap, serdes);
31951f6b410SQuentin Schulz if (ret)
32051f6b410SQuentin Schulz return ret;
32151f6b410SQuentin Schulz
32251f6b410SQuentin Schulz regmap_update_bits(regmap, HSIO_S1G_COMMON_CFG,
32351f6b410SQuentin Schulz HSIO_S1G_COMMON_CFG_SYS_RST,
32451f6b410SQuentin Schulz HSIO_S1G_COMMON_CFG_SYS_RST);
32551f6b410SQuentin Schulz
32651f6b410SQuentin Schulz regmap_update_bits(regmap, HSIO_S1G_MISC_CFG,
32751f6b410SQuentin Schulz HSIO_S1G_MISC_CFG_LANE_RST, 0);
32851f6b410SQuentin Schulz
32951f6b410SQuentin Schulz ret = serdes_commit_mcb_s1g(regmap, serdes);
33051f6b410SQuentin Schulz if (ret)
33151f6b410SQuentin Schulz return ret;
33251f6b410SQuentin Schulz
33351f6b410SQuentin Schulz return 0;
33451f6b410SQuentin Schulz }
33551f6b410SQuentin Schulz
33651f6b410SQuentin Schulz struct serdes_mux {
33751f6b410SQuentin Schulz u8 idx;
33851f6b410SQuentin Schulz u8 port;
33951f6b410SQuentin Schulz enum phy_mode mode;
340c8fe6d7fSGrygorii Strashko int submode;
34151f6b410SQuentin Schulz u32 mask;
34251f6b410SQuentin Schulz u32 mux;
34351f6b410SQuentin Schulz };
34451f6b410SQuentin Schulz
345c8fe6d7fSGrygorii Strashko #define SERDES_MUX(_idx, _port, _mode, _submode, _mask, _mux) { \
34651f6b410SQuentin Schulz .idx = _idx, \
34751f6b410SQuentin Schulz .port = _port, \
34851f6b410SQuentin Schulz .mode = _mode, \
349c8fe6d7fSGrygorii Strashko .submode = _submode, \
35051f6b410SQuentin Schulz .mask = _mask, \
35151f6b410SQuentin Schulz .mux = _mux, \
35251f6b410SQuentin Schulz }
35351f6b410SQuentin Schulz
354c8fe6d7fSGrygorii Strashko #define SERDES_MUX_SGMII(i, p, m, c) \
355c8fe6d7fSGrygorii Strashko SERDES_MUX(i, p, PHY_MODE_ETHERNET, PHY_INTERFACE_MODE_SGMII, m, c)
356c8fe6d7fSGrygorii Strashko #define SERDES_MUX_QSGMII(i, p, m, c) \
357c8fe6d7fSGrygorii Strashko SERDES_MUX(i, p, PHY_MODE_ETHERNET, PHY_INTERFACE_MODE_QSGMII, m, c)
35851f6b410SQuentin Schulz
35951f6b410SQuentin Schulz static const struct serdes_mux ocelot_serdes_muxes[] = {
36051f6b410SQuentin Schulz SERDES_MUX_SGMII(SERDES1G(0), 0, 0, 0),
36151f6b410SQuentin Schulz SERDES_MUX_SGMII(SERDES1G(1), 1, HSIO_HW_CFG_DEV1G_5_MODE, 0),
36251f6b410SQuentin Schulz SERDES_MUX_SGMII(SERDES1G(1), 5, HSIO_HW_CFG_QSGMII_ENA |
36351f6b410SQuentin Schulz HSIO_HW_CFG_DEV1G_5_MODE, HSIO_HW_CFG_DEV1G_5_MODE),
36451f6b410SQuentin Schulz SERDES_MUX_SGMII(SERDES1G(2), 2, HSIO_HW_CFG_DEV1G_4_MODE, 0),
36551f6b410SQuentin Schulz SERDES_MUX_SGMII(SERDES1G(2), 4, HSIO_HW_CFG_QSGMII_ENA |
36651f6b410SQuentin Schulz HSIO_HW_CFG_DEV1G_4_MODE, HSIO_HW_CFG_DEV1G_4_MODE),
36751f6b410SQuentin Schulz SERDES_MUX_SGMII(SERDES1G(3), 3, HSIO_HW_CFG_DEV1G_6_MODE, 0),
36851f6b410SQuentin Schulz SERDES_MUX_SGMII(SERDES1G(3), 6, HSIO_HW_CFG_QSGMII_ENA |
36951f6b410SQuentin Schulz HSIO_HW_CFG_DEV1G_6_MODE, HSIO_HW_CFG_DEV1G_6_MODE),
37051f6b410SQuentin Schulz SERDES_MUX_SGMII(SERDES1G(4), 4, HSIO_HW_CFG_QSGMII_ENA |
37151f6b410SQuentin Schulz HSIO_HW_CFG_DEV1G_4_MODE | HSIO_HW_CFG_DEV1G_9_MODE,
37251f6b410SQuentin Schulz 0),
37351f6b410SQuentin Schulz SERDES_MUX_SGMII(SERDES1G(4), 9, HSIO_HW_CFG_DEV1G_4_MODE |
37451f6b410SQuentin Schulz HSIO_HW_CFG_DEV1G_9_MODE, HSIO_HW_CFG_DEV1G_4_MODE |
37551f6b410SQuentin Schulz HSIO_HW_CFG_DEV1G_9_MODE),
37651f6b410SQuentin Schulz SERDES_MUX_SGMII(SERDES1G(5), 5, HSIO_HW_CFG_QSGMII_ENA |
37751f6b410SQuentin Schulz HSIO_HW_CFG_DEV1G_5_MODE | HSIO_HW_CFG_DEV2G5_10_MODE,
37851f6b410SQuentin Schulz 0),
37951f6b410SQuentin Schulz SERDES_MUX_SGMII(SERDES1G(5), 10, HSIO_HW_CFG_PCIE_ENA |
38051f6b410SQuentin Schulz HSIO_HW_CFG_DEV1G_5_MODE | HSIO_HW_CFG_DEV2G5_10_MODE,
38151f6b410SQuentin Schulz HSIO_HW_CFG_DEV1G_5_MODE | HSIO_HW_CFG_DEV2G5_10_MODE),
38251f6b410SQuentin Schulz SERDES_MUX_QSGMII(SERDES6G(0), 4, HSIO_HW_CFG_QSGMII_ENA,
38351f6b410SQuentin Schulz HSIO_HW_CFG_QSGMII_ENA),
38451f6b410SQuentin Schulz SERDES_MUX_QSGMII(SERDES6G(0), 5, HSIO_HW_CFG_QSGMII_ENA,
38551f6b410SQuentin Schulz HSIO_HW_CFG_QSGMII_ENA),
38651f6b410SQuentin Schulz SERDES_MUX_QSGMII(SERDES6G(0), 6, HSIO_HW_CFG_QSGMII_ENA,
38751f6b410SQuentin Schulz HSIO_HW_CFG_QSGMII_ENA),
38851f6b410SQuentin Schulz SERDES_MUX_SGMII(SERDES6G(0), 7, HSIO_HW_CFG_QSGMII_ENA, 0),
38951f6b410SQuentin Schulz SERDES_MUX_QSGMII(SERDES6G(0), 7, HSIO_HW_CFG_QSGMII_ENA,
39051f6b410SQuentin Schulz HSIO_HW_CFG_QSGMII_ENA),
39151f6b410SQuentin Schulz SERDES_MUX_SGMII(SERDES6G(1), 8, 0, 0),
39251f6b410SQuentin Schulz SERDES_MUX_SGMII(SERDES6G(2), 10, HSIO_HW_CFG_PCIE_ENA |
39351f6b410SQuentin Schulz HSIO_HW_CFG_DEV2G5_10_MODE, 0),
394c8fe6d7fSGrygorii Strashko SERDES_MUX(SERDES6G(2), 10, PHY_MODE_PCIE, 0, HSIO_HW_CFG_PCIE_ENA,
39551f6b410SQuentin Schulz HSIO_HW_CFG_PCIE_ENA),
39651f6b410SQuentin Schulz };
39751f6b410SQuentin Schulz
serdes_set_mode(struct phy * phy,enum phy_mode mode,int submode)39879a5a18aSGrygorii Strashko static int serdes_set_mode(struct phy *phy, enum phy_mode mode, int submode)
39951f6b410SQuentin Schulz {
40051f6b410SQuentin Schulz struct serdes_macro *macro = phy_get_drvdata(phy);
40151f6b410SQuentin Schulz unsigned int i;
40251f6b410SQuentin Schulz int ret;
40351f6b410SQuentin Schulz
404c8fe6d7fSGrygorii Strashko /* As of now only PHY_MODE_ETHERNET is supported */
405c8fe6d7fSGrygorii Strashko if (mode != PHY_MODE_ETHERNET)
406c8fe6d7fSGrygorii Strashko return -EOPNOTSUPP;
407c8fe6d7fSGrygorii Strashko
40851f6b410SQuentin Schulz for (i = 0; i < ARRAY_SIZE(ocelot_serdes_muxes); i++) {
40951f6b410SQuentin Schulz if (macro->idx != ocelot_serdes_muxes[i].idx ||
410c8fe6d7fSGrygorii Strashko mode != ocelot_serdes_muxes[i].mode ||
411c8fe6d7fSGrygorii Strashko submode != ocelot_serdes_muxes[i].submode)
41251f6b410SQuentin Schulz continue;
41351f6b410SQuentin Schulz
414c8fe6d7fSGrygorii Strashko if (submode != PHY_INTERFACE_MODE_QSGMII &&
41551f6b410SQuentin Schulz macro->port != ocelot_serdes_muxes[i].port)
41651f6b410SQuentin Schulz continue;
41751f6b410SQuentin Schulz
41851f6b410SQuentin Schulz ret = regmap_update_bits(macro->ctrl->regs, HSIO_HW_CFG,
41951f6b410SQuentin Schulz ocelot_serdes_muxes[i].mask,
42051f6b410SQuentin Schulz ocelot_serdes_muxes[i].mux);
42151f6b410SQuentin Schulz if (ret)
42251f6b410SQuentin Schulz return ret;
42351f6b410SQuentin Schulz
42451f6b410SQuentin Schulz if (macro->idx <= SERDES1G_MAX)
42551f6b410SQuentin Schulz return serdes_init_s1g(macro->ctrl->regs, macro->idx);
42661c67bfaSKavya Sree Kotagiri else if (macro->idx <= SERDES6G_MAX)
42761c67bfaSKavya Sree Kotagiri return serdes_init_s6g(macro->ctrl->regs,
42861c67bfaSKavya Sree Kotagiri macro->idx - (SERDES1G_MAX + 1),
42961c67bfaSKavya Sree Kotagiri ocelot_serdes_muxes[i].submode);
43051f6b410SQuentin Schulz
43161c67bfaSKavya Sree Kotagiri /* PCIe not supported yet */
43251f6b410SQuentin Schulz return -EOPNOTSUPP;
43351f6b410SQuentin Schulz }
43451f6b410SQuentin Schulz
43551f6b410SQuentin Schulz return -EINVAL;
43651f6b410SQuentin Schulz }
43751f6b410SQuentin Schulz
43851f6b410SQuentin Schulz static const struct phy_ops serdes_ops = {
43951f6b410SQuentin Schulz .set_mode = serdes_set_mode,
44051f6b410SQuentin Schulz .owner = THIS_MODULE,
44151f6b410SQuentin Schulz };
44251f6b410SQuentin Schulz
serdes_simple_xlate(struct device * dev,struct of_phandle_args * args)44351f6b410SQuentin Schulz static struct phy *serdes_simple_xlate(struct device *dev,
44451f6b410SQuentin Schulz struct of_phandle_args *args)
44551f6b410SQuentin Schulz {
44651f6b410SQuentin Schulz struct serdes_ctrl *ctrl = dev_get_drvdata(dev);
44751f6b410SQuentin Schulz unsigned int port, idx, i;
44851f6b410SQuentin Schulz
44951f6b410SQuentin Schulz if (args->args_count != 2)
45051f6b410SQuentin Schulz return ERR_PTR(-EINVAL);
45151f6b410SQuentin Schulz
45251f6b410SQuentin Schulz port = args->args[0];
45351f6b410SQuentin Schulz idx = args->args[1];
45451f6b410SQuentin Schulz
4556acb47d1SGustavo A. R. Silva for (i = 0; i < SERDES_MAX; i++) {
45651f6b410SQuentin Schulz struct serdes_macro *macro = phy_get_drvdata(ctrl->phys[i]);
45751f6b410SQuentin Schulz
45851f6b410SQuentin Schulz if (idx != macro->idx)
45951f6b410SQuentin Schulz continue;
46051f6b410SQuentin Schulz
46151f6b410SQuentin Schulz /* SERDES6G(0) is the only SerDes capable of QSGMII */
46251f6b410SQuentin Schulz if (idx != SERDES6G(0) && macro->port >= 0)
46351f6b410SQuentin Schulz return ERR_PTR(-EBUSY);
46451f6b410SQuentin Schulz
46551f6b410SQuentin Schulz macro->port = port;
46651f6b410SQuentin Schulz return ctrl->phys[i];
46751f6b410SQuentin Schulz }
46851f6b410SQuentin Schulz
46951f6b410SQuentin Schulz return ERR_PTR(-ENODEV);
47051f6b410SQuentin Schulz }
47151f6b410SQuentin Schulz
serdes_phy_create(struct serdes_ctrl * ctrl,u8 idx,struct phy ** phy)47251f6b410SQuentin Schulz static int serdes_phy_create(struct serdes_ctrl *ctrl, u8 idx, struct phy **phy)
47351f6b410SQuentin Schulz {
47451f6b410SQuentin Schulz struct serdes_macro *macro;
47551f6b410SQuentin Schulz
47651f6b410SQuentin Schulz *phy = devm_phy_create(ctrl->dev, NULL, &serdes_ops);
47751f6b410SQuentin Schulz if (IS_ERR(*phy))
47851f6b410SQuentin Schulz return PTR_ERR(*phy);
47951f6b410SQuentin Schulz
48051f6b410SQuentin Schulz macro = devm_kzalloc(ctrl->dev, sizeof(*macro), GFP_KERNEL);
48151f6b410SQuentin Schulz if (!macro)
48251f6b410SQuentin Schulz return -ENOMEM;
48351f6b410SQuentin Schulz
48451f6b410SQuentin Schulz macro->idx = idx;
48551f6b410SQuentin Schulz macro->ctrl = ctrl;
48651f6b410SQuentin Schulz macro->port = -1;
48751f6b410SQuentin Schulz
48851f6b410SQuentin Schulz phy_set_drvdata(*phy, macro);
48951f6b410SQuentin Schulz
49051f6b410SQuentin Schulz return 0;
49151f6b410SQuentin Schulz }
49251f6b410SQuentin Schulz
serdes_probe(struct platform_device * pdev)49351f6b410SQuentin Schulz static int serdes_probe(struct platform_device *pdev)
49451f6b410SQuentin Schulz {
49551f6b410SQuentin Schulz struct phy_provider *provider;
49651f6b410SQuentin Schulz struct serdes_ctrl *ctrl;
497*672faa7bSColin Foster struct resource *res;
49851f6b410SQuentin Schulz unsigned int i;
49951f6b410SQuentin Schulz int ret;
50051f6b410SQuentin Schulz
50151f6b410SQuentin Schulz ctrl = devm_kzalloc(&pdev->dev, sizeof(*ctrl), GFP_KERNEL);
50251f6b410SQuentin Schulz if (!ctrl)
50351f6b410SQuentin Schulz return -ENOMEM;
50451f6b410SQuentin Schulz
50551f6b410SQuentin Schulz ctrl->dev = &pdev->dev;
50651f6b410SQuentin Schulz ctrl->regs = syscon_node_to_regmap(pdev->dev.parent->of_node);
507*672faa7bSColin Foster if (IS_ERR(ctrl->regs)) {
508*672faa7bSColin Foster /* Fall back to using IORESOURCE_REG, if possible */
509*672faa7bSColin Foster res = platform_get_resource(pdev, IORESOURCE_REG, 0);
510*672faa7bSColin Foster if (res)
511*672faa7bSColin Foster ctrl->regs = dev_get_regmap(ctrl->dev->parent,
512*672faa7bSColin Foster res->name);
513*672faa7bSColin Foster }
514*672faa7bSColin Foster
5159047fa5dSWei Yongjun if (IS_ERR(ctrl->regs))
5169047fa5dSWei Yongjun return PTR_ERR(ctrl->regs);
51751f6b410SQuentin Schulz
5186acb47d1SGustavo A. R. Silva for (i = 0; i < SERDES_MAX; i++) {
51951f6b410SQuentin Schulz ret = serdes_phy_create(ctrl, i, &ctrl->phys[i]);
52051f6b410SQuentin Schulz if (ret)
52151f6b410SQuentin Schulz return ret;
52251f6b410SQuentin Schulz }
52351f6b410SQuentin Schulz
52451f6b410SQuentin Schulz dev_set_drvdata(&pdev->dev, ctrl);
52551f6b410SQuentin Schulz
52651f6b410SQuentin Schulz provider = devm_of_phy_provider_register(ctrl->dev,
52751f6b410SQuentin Schulz serdes_simple_xlate);
52851f6b410SQuentin Schulz
52951f6b410SQuentin Schulz return PTR_ERR_OR_ZERO(provider);
53051f6b410SQuentin Schulz }
53151f6b410SQuentin Schulz
53251f6b410SQuentin Schulz static const struct of_device_id serdes_ids[] = {
53351f6b410SQuentin Schulz { .compatible = "mscc,vsc7514-serdes", },
53451f6b410SQuentin Schulz {},
53551f6b410SQuentin Schulz };
53651f6b410SQuentin Schulz MODULE_DEVICE_TABLE(of, serdes_ids);
53751f6b410SQuentin Schulz
53851f6b410SQuentin Schulz static struct platform_driver mscc_ocelot_serdes = {
53951f6b410SQuentin Schulz .probe = serdes_probe,
54051f6b410SQuentin Schulz .driver = {
54151f6b410SQuentin Schulz .name = "mscc,ocelot-serdes",
54251f6b410SQuentin Schulz .of_match_table = of_match_ptr(serdes_ids),
54351f6b410SQuentin Schulz },
54451f6b410SQuentin Schulz };
54551f6b410SQuentin Schulz
54651f6b410SQuentin Schulz module_platform_driver(mscc_ocelot_serdes);
54751f6b410SQuentin Schulz
54851f6b410SQuentin Schulz MODULE_AUTHOR("Quentin Schulz <quentin.schulz@bootlin.com>");
54951f6b410SQuentin Schulz MODULE_DESCRIPTION("SerDes driver for Microsemi Ocelot");
55051f6b410SQuentin Schulz MODULE_LICENSE("Dual MIT/GPL");
551