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