18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0 OR MIT) 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * SerDes PHY driver for Microsemi Ocelot 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2018 Microsemi 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/err.h> 108c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/of.h> 138c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 148c2ecf20Sopenharmony_ci#include <linux/phy.h> 158c2ecf20Sopenharmony_ci#include <linux/phy/phy.h> 168c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 178c2ecf20Sopenharmony_ci#include <linux/regmap.h> 188c2ecf20Sopenharmony_ci#include <soc/mscc/ocelot_hsio.h> 198c2ecf20Sopenharmony_ci#include <dt-bindings/phy/phy-ocelot-serdes.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistruct serdes_ctrl { 228c2ecf20Sopenharmony_ci struct regmap *regs; 238c2ecf20Sopenharmony_ci struct device *dev; 248c2ecf20Sopenharmony_ci struct phy *phys[SERDES_MAX]; 258c2ecf20Sopenharmony_ci}; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistruct serdes_macro { 288c2ecf20Sopenharmony_ci u8 idx; 298c2ecf20Sopenharmony_ci /* Not used when in QSGMII or PCIe mode */ 308c2ecf20Sopenharmony_ci int port; 318c2ecf20Sopenharmony_ci struct serdes_ctrl *ctrl; 328c2ecf20Sopenharmony_ci}; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define MCB_S6G_CFG_TIMEOUT 50 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic int __serdes_write_mcb_s6g(struct regmap *regmap, u8 macro, u32 op) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci unsigned int regval = 0; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci regmap_write(regmap, HSIO_MCB_S6G_ADDR_CFG, op | 418c2ecf20Sopenharmony_ci HSIO_MCB_S6G_ADDR_CFG_SERDES6G_ADDR(BIT(macro))); 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci return regmap_read_poll_timeout(regmap, HSIO_MCB_S6G_ADDR_CFG, regval, 448c2ecf20Sopenharmony_ci (regval & op) != op, 100, 458c2ecf20Sopenharmony_ci MCB_S6G_CFG_TIMEOUT * 1000); 468c2ecf20Sopenharmony_ci} 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic int serdes_commit_mcb_s6g(struct regmap *regmap, u8 macro) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci return __serdes_write_mcb_s6g(regmap, macro, 518c2ecf20Sopenharmony_ci HSIO_MCB_S6G_ADDR_CFG_SERDES6G_WR_ONE_SHOT); 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic int serdes_update_mcb_s6g(struct regmap *regmap, u8 macro) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci return __serdes_write_mcb_s6g(regmap, macro, 578c2ecf20Sopenharmony_ci HSIO_MCB_S6G_ADDR_CFG_SERDES6G_RD_ONE_SHOT); 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic int serdes_init_s6g(struct regmap *regmap, u8 serdes, int mode) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci u32 pll_fsm_ctrl_data; 638c2ecf20Sopenharmony_ci u32 ob_ena1v_mode; 648c2ecf20Sopenharmony_ci u32 des_bw_ana; 658c2ecf20Sopenharmony_ci u32 ob_ena_cas; 668c2ecf20Sopenharmony_ci u32 if_mode; 678c2ecf20Sopenharmony_ci u32 ob_lev; 688c2ecf20Sopenharmony_ci u32 qrate; 698c2ecf20Sopenharmony_ci int ret; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci if (mode == PHY_INTERFACE_MODE_QSGMII) { 728c2ecf20Sopenharmony_ci pll_fsm_ctrl_data = 120; 738c2ecf20Sopenharmony_ci ob_ena1v_mode = 0; 748c2ecf20Sopenharmony_ci ob_ena_cas = 0; 758c2ecf20Sopenharmony_ci des_bw_ana = 5; 768c2ecf20Sopenharmony_ci ob_lev = 24; 778c2ecf20Sopenharmony_ci if_mode = 3; 788c2ecf20Sopenharmony_ci qrate = 0; 798c2ecf20Sopenharmony_ci } else { 808c2ecf20Sopenharmony_ci pll_fsm_ctrl_data = 60; 818c2ecf20Sopenharmony_ci ob_ena1v_mode = 1; 828c2ecf20Sopenharmony_ci ob_ena_cas = 2; 838c2ecf20Sopenharmony_ci des_bw_ana = 3; 848c2ecf20Sopenharmony_ci ob_lev = 48; 858c2ecf20Sopenharmony_ci if_mode = 1; 868c2ecf20Sopenharmony_ci qrate = 1; 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci ret = serdes_update_mcb_s6g(regmap, serdes); 908c2ecf20Sopenharmony_ci if (ret) 918c2ecf20Sopenharmony_ci return ret; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci /* Test pattern */ 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci regmap_update_bits(regmap, HSIO_S6G_COMMON_CFG, 968c2ecf20Sopenharmony_ci HSIO_S6G_COMMON_CFG_SYS_RST, 0); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci regmap_update_bits(regmap, HSIO_S6G_PLL_CFG, 998c2ecf20Sopenharmony_ci HSIO_S6G_PLL_CFG_PLL_FSM_ENA, 0); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci regmap_update_bits(regmap, HSIO_S6G_IB_CFG, 1028c2ecf20Sopenharmony_ci HSIO_S6G_IB_CFG_IB_SIG_DET_ENA | 1038c2ecf20Sopenharmony_ci HSIO_S6G_IB_CFG_IB_REG_ENA | 1048c2ecf20Sopenharmony_ci HSIO_S6G_IB_CFG_IB_SAM_ENA | 1058c2ecf20Sopenharmony_ci HSIO_S6G_IB_CFG_IB_EQZ_ENA | 1068c2ecf20Sopenharmony_ci HSIO_S6G_IB_CFG_IB_CONCUR | 1078c2ecf20Sopenharmony_ci HSIO_S6G_IB_CFG_IB_CAL_ENA, 1088c2ecf20Sopenharmony_ci HSIO_S6G_IB_CFG_IB_SIG_DET_ENA | 1098c2ecf20Sopenharmony_ci HSIO_S6G_IB_CFG_IB_REG_ENA | 1108c2ecf20Sopenharmony_ci HSIO_S6G_IB_CFG_IB_SAM_ENA | 1118c2ecf20Sopenharmony_ci HSIO_S6G_IB_CFG_IB_EQZ_ENA | 1128c2ecf20Sopenharmony_ci HSIO_S6G_IB_CFG_IB_CONCUR); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci regmap_update_bits(regmap, HSIO_S6G_IB_CFG1, 1158c2ecf20Sopenharmony_ci HSIO_S6G_IB_CFG1_IB_FRC_OFFSET | 1168c2ecf20Sopenharmony_ci HSIO_S6G_IB_CFG1_IB_FRC_LP | 1178c2ecf20Sopenharmony_ci HSIO_S6G_IB_CFG1_IB_FRC_MID | 1188c2ecf20Sopenharmony_ci HSIO_S6G_IB_CFG1_IB_FRC_HP | 1198c2ecf20Sopenharmony_ci HSIO_S6G_IB_CFG1_IB_FILT_OFFSET | 1208c2ecf20Sopenharmony_ci HSIO_S6G_IB_CFG1_IB_FILT_LP | 1218c2ecf20Sopenharmony_ci HSIO_S6G_IB_CFG1_IB_FILT_MID | 1228c2ecf20Sopenharmony_ci HSIO_S6G_IB_CFG1_IB_FILT_HP, 1238c2ecf20Sopenharmony_ci HSIO_S6G_IB_CFG1_IB_FILT_OFFSET | 1248c2ecf20Sopenharmony_ci HSIO_S6G_IB_CFG1_IB_FILT_HP | 1258c2ecf20Sopenharmony_ci HSIO_S6G_IB_CFG1_IB_FILT_LP | 1268c2ecf20Sopenharmony_ci HSIO_S6G_IB_CFG1_IB_FILT_MID); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci regmap_update_bits(regmap, HSIO_S6G_IB_CFG2, 1298c2ecf20Sopenharmony_ci HSIO_S6G_IB_CFG2_IB_UREG_M, 1308c2ecf20Sopenharmony_ci HSIO_S6G_IB_CFG2_IB_UREG(4)); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci regmap_update_bits(regmap, HSIO_S6G_IB_CFG3, 1338c2ecf20Sopenharmony_ci HSIO_S6G_IB_CFG3_IB_INI_OFFSET_M | 1348c2ecf20Sopenharmony_ci HSIO_S6G_IB_CFG3_IB_INI_LP_M | 1358c2ecf20Sopenharmony_ci HSIO_S6G_IB_CFG3_IB_INI_MID_M | 1368c2ecf20Sopenharmony_ci HSIO_S6G_IB_CFG3_IB_INI_HP_M, 1378c2ecf20Sopenharmony_ci HSIO_S6G_IB_CFG3_IB_INI_OFFSET(31) | 1388c2ecf20Sopenharmony_ci HSIO_S6G_IB_CFG3_IB_INI_LP(1) | 1398c2ecf20Sopenharmony_ci HSIO_S6G_IB_CFG3_IB_INI_MID(31) | 1408c2ecf20Sopenharmony_ci HSIO_S6G_IB_CFG3_IB_INI_HP(0)); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci regmap_update_bits(regmap, HSIO_S6G_MISC_CFG, 1438c2ecf20Sopenharmony_ci HSIO_S6G_MISC_CFG_LANE_RST, 1448c2ecf20Sopenharmony_ci HSIO_S6G_MISC_CFG_LANE_RST); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci ret = serdes_commit_mcb_s6g(regmap, serdes); 1478c2ecf20Sopenharmony_ci if (ret) 1488c2ecf20Sopenharmony_ci return ret; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci /* OB + DES + IB + SER CFG */ 1518c2ecf20Sopenharmony_ci regmap_update_bits(regmap, HSIO_S6G_OB_CFG, 1528c2ecf20Sopenharmony_ci HSIO_S6G_OB_CFG_OB_IDLE | 1538c2ecf20Sopenharmony_ci HSIO_S6G_OB_CFG_OB_ENA1V_MODE | 1548c2ecf20Sopenharmony_ci HSIO_S6G_OB_CFG_OB_POST0_M | 1558c2ecf20Sopenharmony_ci HSIO_S6G_OB_CFG_OB_PREC_M, 1568c2ecf20Sopenharmony_ci (ob_ena1v_mode ? HSIO_S6G_OB_CFG_OB_ENA1V_MODE : 0) | 1578c2ecf20Sopenharmony_ci HSIO_S6G_OB_CFG_OB_POST0(0) | 1588c2ecf20Sopenharmony_ci HSIO_S6G_OB_CFG_OB_PREC(0)); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci regmap_update_bits(regmap, HSIO_S6G_OB_CFG1, 1618c2ecf20Sopenharmony_ci HSIO_S6G_OB_CFG1_OB_ENA_CAS_M | 1628c2ecf20Sopenharmony_ci HSIO_S6G_OB_CFG1_OB_LEV_M, 1638c2ecf20Sopenharmony_ci HSIO_S6G_OB_CFG1_OB_LEV(ob_lev) | 1648c2ecf20Sopenharmony_ci HSIO_S6G_OB_CFG1_OB_ENA_CAS(ob_ena_cas)); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci regmap_update_bits(regmap, HSIO_S6G_DES_CFG, 1678c2ecf20Sopenharmony_ci HSIO_S6G_DES_CFG_DES_PHS_CTRL_M | 1688c2ecf20Sopenharmony_ci HSIO_S6G_DES_CFG_DES_CPMD_SEL_M | 1698c2ecf20Sopenharmony_ci HSIO_S6G_DES_CFG_DES_BW_ANA_M, 1708c2ecf20Sopenharmony_ci HSIO_S6G_DES_CFG_DES_PHS_CTRL(2) | 1718c2ecf20Sopenharmony_ci HSIO_S6G_DES_CFG_DES_CPMD_SEL(0) | 1728c2ecf20Sopenharmony_ci HSIO_S6G_DES_CFG_DES_BW_ANA(des_bw_ana)); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci regmap_update_bits(regmap, HSIO_S6G_IB_CFG, 1758c2ecf20Sopenharmony_ci HSIO_S6G_IB_CFG_IB_SIG_DET_CLK_SEL_M | 1768c2ecf20Sopenharmony_ci HSIO_S6G_IB_CFG_IB_REG_PAT_SEL_OFFSET_M, 1778c2ecf20Sopenharmony_ci HSIO_S6G_IB_CFG_IB_REG_PAT_SEL_OFFSET(0) | 1788c2ecf20Sopenharmony_ci HSIO_S6G_IB_CFG_IB_SIG_DET_CLK_SEL(0)); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci regmap_update_bits(regmap, HSIO_S6G_IB_CFG1, 1818c2ecf20Sopenharmony_ci HSIO_S6G_IB_CFG1_IB_TSDET_M, 1828c2ecf20Sopenharmony_ci HSIO_S6G_IB_CFG1_IB_TSDET(16)); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci regmap_update_bits(regmap, HSIO_S6G_SER_CFG, 1858c2ecf20Sopenharmony_ci HSIO_S6G_SER_CFG_SER_ALISEL_M | 1868c2ecf20Sopenharmony_ci HSIO_S6G_SER_CFG_SER_ENALI, 1878c2ecf20Sopenharmony_ci HSIO_S6G_SER_CFG_SER_ALISEL(0)); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci regmap_update_bits(regmap, HSIO_S6G_PLL_CFG, 1908c2ecf20Sopenharmony_ci HSIO_S6G_PLL_CFG_PLL_DIV4 | 1918c2ecf20Sopenharmony_ci HSIO_S6G_PLL_CFG_PLL_ENA_ROT | 1928c2ecf20Sopenharmony_ci HSIO_S6G_PLL_CFG_PLL_FSM_CTRL_DATA_M | 1938c2ecf20Sopenharmony_ci HSIO_S6G_PLL_CFG_PLL_ROT_DIR | 1948c2ecf20Sopenharmony_ci HSIO_S6G_PLL_CFG_PLL_ROT_FRQ, 1958c2ecf20Sopenharmony_ci HSIO_S6G_PLL_CFG_PLL_FSM_CTRL_DATA 1968c2ecf20Sopenharmony_ci (pll_fsm_ctrl_data)); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci regmap_update_bits(regmap, HSIO_S6G_COMMON_CFG, 1998c2ecf20Sopenharmony_ci HSIO_S6G_COMMON_CFG_SYS_RST | 2008c2ecf20Sopenharmony_ci HSIO_S6G_COMMON_CFG_ENA_LANE | 2018c2ecf20Sopenharmony_ci HSIO_S6G_COMMON_CFG_PWD_RX | 2028c2ecf20Sopenharmony_ci HSIO_S6G_COMMON_CFG_PWD_TX | 2038c2ecf20Sopenharmony_ci HSIO_S6G_COMMON_CFG_HRATE | 2048c2ecf20Sopenharmony_ci HSIO_S6G_COMMON_CFG_QRATE | 2058c2ecf20Sopenharmony_ci HSIO_S6G_COMMON_CFG_ENA_ELOOP | 2068c2ecf20Sopenharmony_ci HSIO_S6G_COMMON_CFG_ENA_FLOOP | 2078c2ecf20Sopenharmony_ci HSIO_S6G_COMMON_CFG_IF_MODE_M, 2088c2ecf20Sopenharmony_ci HSIO_S6G_COMMON_CFG_SYS_RST | 2098c2ecf20Sopenharmony_ci HSIO_S6G_COMMON_CFG_ENA_LANE | 2108c2ecf20Sopenharmony_ci (qrate ? HSIO_S6G_COMMON_CFG_QRATE : 0) | 2118c2ecf20Sopenharmony_ci HSIO_S6G_COMMON_CFG_IF_MODE(if_mode)); 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci regmap_update_bits(regmap, HSIO_S6G_MISC_CFG, 2148c2ecf20Sopenharmony_ci HSIO_S6G_MISC_CFG_LANE_RST | 2158c2ecf20Sopenharmony_ci HSIO_S6G_MISC_CFG_DES_100FX_CPMD_ENA | 2168c2ecf20Sopenharmony_ci HSIO_S6G_MISC_CFG_RX_LPI_MODE_ENA | 2178c2ecf20Sopenharmony_ci HSIO_S6G_MISC_CFG_TX_LPI_MODE_ENA, 2188c2ecf20Sopenharmony_ci HSIO_S6G_MISC_CFG_LANE_RST | 2198c2ecf20Sopenharmony_ci HSIO_S6G_MISC_CFG_RX_LPI_MODE_ENA); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci ret = serdes_commit_mcb_s6g(regmap, serdes); 2238c2ecf20Sopenharmony_ci if (ret) 2248c2ecf20Sopenharmony_ci return ret; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci regmap_update_bits(regmap, HSIO_S6G_PLL_CFG, 2278c2ecf20Sopenharmony_ci HSIO_S6G_PLL_CFG_PLL_FSM_ENA, 2288c2ecf20Sopenharmony_ci HSIO_S6G_PLL_CFG_PLL_FSM_ENA); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci ret = serdes_commit_mcb_s6g(regmap, serdes); 2318c2ecf20Sopenharmony_ci if (ret) 2328c2ecf20Sopenharmony_ci return ret; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci /* Wait for PLL bringup */ 2358c2ecf20Sopenharmony_ci msleep(20); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci regmap_update_bits(regmap, HSIO_S6G_IB_CFG, 2388c2ecf20Sopenharmony_ci HSIO_S6G_IB_CFG_IB_CAL_ENA, 2398c2ecf20Sopenharmony_ci HSIO_S6G_IB_CFG_IB_CAL_ENA); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci regmap_update_bits(regmap, HSIO_S6G_MISC_CFG, 2428c2ecf20Sopenharmony_ci HSIO_S6G_MISC_CFG_LANE_RST, 0); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci ret = serdes_commit_mcb_s6g(regmap, serdes); 2458c2ecf20Sopenharmony_ci if (ret) 2468c2ecf20Sopenharmony_ci return ret; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci /* Wait for calibration */ 2498c2ecf20Sopenharmony_ci msleep(60); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci regmap_update_bits(regmap, HSIO_S6G_IB_CFG, 2528c2ecf20Sopenharmony_ci HSIO_S6G_IB_CFG_IB_REG_PAT_SEL_OFFSET_M | 2538c2ecf20Sopenharmony_ci HSIO_S6G_IB_CFG_IB_SIG_DET_CLK_SEL_M, 2548c2ecf20Sopenharmony_ci HSIO_S6G_IB_CFG_IB_REG_PAT_SEL_OFFSET(0) | 2558c2ecf20Sopenharmony_ci HSIO_S6G_IB_CFG_IB_SIG_DET_CLK_SEL(7)); 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci regmap_update_bits(regmap, HSIO_S6G_IB_CFG1, 2588c2ecf20Sopenharmony_ci HSIO_S6G_IB_CFG1_IB_TSDET_M, 2598c2ecf20Sopenharmony_ci HSIO_S6G_IB_CFG1_IB_TSDET(3)); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci /* IB CFG */ 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci return 0; 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci#define MCB_S1G_CFG_TIMEOUT 50 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic int __serdes_write_mcb_s1g(struct regmap *regmap, u8 macro, u32 op) 2698c2ecf20Sopenharmony_ci{ 2708c2ecf20Sopenharmony_ci unsigned int regval; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci regmap_write(regmap, HSIO_MCB_S1G_ADDR_CFG, op | 2738c2ecf20Sopenharmony_ci HSIO_MCB_S1G_ADDR_CFG_SERDES1G_ADDR(BIT(macro))); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci return regmap_read_poll_timeout(regmap, HSIO_MCB_S1G_ADDR_CFG, regval, 2768c2ecf20Sopenharmony_ci (regval & op) != op, 100, 2778c2ecf20Sopenharmony_ci MCB_S1G_CFG_TIMEOUT * 1000); 2788c2ecf20Sopenharmony_ci} 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_cistatic int serdes_commit_mcb_s1g(struct regmap *regmap, u8 macro) 2818c2ecf20Sopenharmony_ci{ 2828c2ecf20Sopenharmony_ci return __serdes_write_mcb_s1g(regmap, macro, 2838c2ecf20Sopenharmony_ci HSIO_MCB_S1G_ADDR_CFG_SERDES1G_WR_ONE_SHOT); 2848c2ecf20Sopenharmony_ci} 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_cistatic int serdes_update_mcb_s1g(struct regmap *regmap, u8 macro) 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci return __serdes_write_mcb_s1g(regmap, macro, 2898c2ecf20Sopenharmony_ci HSIO_MCB_S1G_ADDR_CFG_SERDES1G_RD_ONE_SHOT); 2908c2ecf20Sopenharmony_ci} 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_cistatic int serdes_init_s1g(struct regmap *regmap, u8 serdes) 2938c2ecf20Sopenharmony_ci{ 2948c2ecf20Sopenharmony_ci int ret; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci ret = serdes_update_mcb_s1g(regmap, serdes); 2978c2ecf20Sopenharmony_ci if (ret) 2988c2ecf20Sopenharmony_ci return ret; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci regmap_update_bits(regmap, HSIO_S1G_COMMON_CFG, 3018c2ecf20Sopenharmony_ci HSIO_S1G_COMMON_CFG_SYS_RST | 3028c2ecf20Sopenharmony_ci HSIO_S1G_COMMON_CFG_ENA_LANE | 3038c2ecf20Sopenharmony_ci HSIO_S1G_COMMON_CFG_ENA_ELOOP | 3048c2ecf20Sopenharmony_ci HSIO_S1G_COMMON_CFG_ENA_FLOOP, 3058c2ecf20Sopenharmony_ci HSIO_S1G_COMMON_CFG_ENA_LANE); 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci regmap_update_bits(regmap, HSIO_S1G_PLL_CFG, 3088c2ecf20Sopenharmony_ci HSIO_S1G_PLL_CFG_PLL_FSM_ENA | 3098c2ecf20Sopenharmony_ci HSIO_S1G_PLL_CFG_PLL_FSM_CTRL_DATA_M, 3108c2ecf20Sopenharmony_ci HSIO_S1G_PLL_CFG_PLL_FSM_CTRL_DATA(200) | 3118c2ecf20Sopenharmony_ci HSIO_S1G_PLL_CFG_PLL_FSM_ENA); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci regmap_update_bits(regmap, HSIO_S1G_MISC_CFG, 3148c2ecf20Sopenharmony_ci HSIO_S1G_MISC_CFG_DES_100FX_CPMD_ENA | 3158c2ecf20Sopenharmony_ci HSIO_S1G_MISC_CFG_LANE_RST, 3168c2ecf20Sopenharmony_ci HSIO_S1G_MISC_CFG_LANE_RST); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci ret = serdes_commit_mcb_s1g(regmap, serdes); 3198c2ecf20Sopenharmony_ci if (ret) 3208c2ecf20Sopenharmony_ci return ret; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci regmap_update_bits(regmap, HSIO_S1G_COMMON_CFG, 3238c2ecf20Sopenharmony_ci HSIO_S1G_COMMON_CFG_SYS_RST, 3248c2ecf20Sopenharmony_ci HSIO_S1G_COMMON_CFG_SYS_RST); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci regmap_update_bits(regmap, HSIO_S1G_MISC_CFG, 3278c2ecf20Sopenharmony_ci HSIO_S1G_MISC_CFG_LANE_RST, 0); 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci ret = serdes_commit_mcb_s1g(regmap, serdes); 3308c2ecf20Sopenharmony_ci if (ret) 3318c2ecf20Sopenharmony_ci return ret; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci return 0; 3348c2ecf20Sopenharmony_ci} 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_cistruct serdes_mux { 3378c2ecf20Sopenharmony_ci u8 idx; 3388c2ecf20Sopenharmony_ci u8 port; 3398c2ecf20Sopenharmony_ci enum phy_mode mode; 3408c2ecf20Sopenharmony_ci int submode; 3418c2ecf20Sopenharmony_ci u32 mask; 3428c2ecf20Sopenharmony_ci u32 mux; 3438c2ecf20Sopenharmony_ci}; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci#define SERDES_MUX(_idx, _port, _mode, _submode, _mask, _mux) { \ 3468c2ecf20Sopenharmony_ci .idx = _idx, \ 3478c2ecf20Sopenharmony_ci .port = _port, \ 3488c2ecf20Sopenharmony_ci .mode = _mode, \ 3498c2ecf20Sopenharmony_ci .submode = _submode, \ 3508c2ecf20Sopenharmony_ci .mask = _mask, \ 3518c2ecf20Sopenharmony_ci .mux = _mux, \ 3528c2ecf20Sopenharmony_ci} 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci#define SERDES_MUX_SGMII(i, p, m, c) \ 3558c2ecf20Sopenharmony_ci SERDES_MUX(i, p, PHY_MODE_ETHERNET, PHY_INTERFACE_MODE_SGMII, m, c) 3568c2ecf20Sopenharmony_ci#define SERDES_MUX_QSGMII(i, p, m, c) \ 3578c2ecf20Sopenharmony_ci SERDES_MUX(i, p, PHY_MODE_ETHERNET, PHY_INTERFACE_MODE_QSGMII, m, c) 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_cistatic const struct serdes_mux ocelot_serdes_muxes[] = { 3608c2ecf20Sopenharmony_ci SERDES_MUX_SGMII(SERDES1G(0), 0, 0, 0), 3618c2ecf20Sopenharmony_ci SERDES_MUX_SGMII(SERDES1G(1), 1, HSIO_HW_CFG_DEV1G_5_MODE, 0), 3628c2ecf20Sopenharmony_ci SERDES_MUX_SGMII(SERDES1G(1), 5, HSIO_HW_CFG_QSGMII_ENA | 3638c2ecf20Sopenharmony_ci HSIO_HW_CFG_DEV1G_5_MODE, HSIO_HW_CFG_DEV1G_5_MODE), 3648c2ecf20Sopenharmony_ci SERDES_MUX_SGMII(SERDES1G(2), 2, HSIO_HW_CFG_DEV1G_4_MODE, 0), 3658c2ecf20Sopenharmony_ci SERDES_MUX_SGMII(SERDES1G(2), 4, HSIO_HW_CFG_QSGMII_ENA | 3668c2ecf20Sopenharmony_ci HSIO_HW_CFG_DEV1G_4_MODE, HSIO_HW_CFG_DEV1G_4_MODE), 3678c2ecf20Sopenharmony_ci SERDES_MUX_SGMII(SERDES1G(3), 3, HSIO_HW_CFG_DEV1G_6_MODE, 0), 3688c2ecf20Sopenharmony_ci SERDES_MUX_SGMII(SERDES1G(3), 6, HSIO_HW_CFG_QSGMII_ENA | 3698c2ecf20Sopenharmony_ci HSIO_HW_CFG_DEV1G_6_MODE, HSIO_HW_CFG_DEV1G_6_MODE), 3708c2ecf20Sopenharmony_ci SERDES_MUX_SGMII(SERDES1G(4), 4, HSIO_HW_CFG_QSGMII_ENA | 3718c2ecf20Sopenharmony_ci HSIO_HW_CFG_DEV1G_4_MODE | HSIO_HW_CFG_DEV1G_9_MODE, 3728c2ecf20Sopenharmony_ci 0), 3738c2ecf20Sopenharmony_ci SERDES_MUX_SGMII(SERDES1G(4), 9, HSIO_HW_CFG_DEV1G_4_MODE | 3748c2ecf20Sopenharmony_ci HSIO_HW_CFG_DEV1G_9_MODE, HSIO_HW_CFG_DEV1G_4_MODE | 3758c2ecf20Sopenharmony_ci HSIO_HW_CFG_DEV1G_9_MODE), 3768c2ecf20Sopenharmony_ci SERDES_MUX_SGMII(SERDES1G(5), 5, HSIO_HW_CFG_QSGMII_ENA | 3778c2ecf20Sopenharmony_ci HSIO_HW_CFG_DEV1G_5_MODE | HSIO_HW_CFG_DEV2G5_10_MODE, 3788c2ecf20Sopenharmony_ci 0), 3798c2ecf20Sopenharmony_ci SERDES_MUX_SGMII(SERDES1G(5), 10, HSIO_HW_CFG_PCIE_ENA | 3808c2ecf20Sopenharmony_ci HSIO_HW_CFG_DEV1G_5_MODE | HSIO_HW_CFG_DEV2G5_10_MODE, 3818c2ecf20Sopenharmony_ci HSIO_HW_CFG_DEV1G_5_MODE | HSIO_HW_CFG_DEV2G5_10_MODE), 3828c2ecf20Sopenharmony_ci SERDES_MUX_QSGMII(SERDES6G(0), 4, HSIO_HW_CFG_QSGMII_ENA, 3838c2ecf20Sopenharmony_ci HSIO_HW_CFG_QSGMII_ENA), 3848c2ecf20Sopenharmony_ci SERDES_MUX_QSGMII(SERDES6G(0), 5, HSIO_HW_CFG_QSGMII_ENA, 3858c2ecf20Sopenharmony_ci HSIO_HW_CFG_QSGMII_ENA), 3868c2ecf20Sopenharmony_ci SERDES_MUX_QSGMII(SERDES6G(0), 6, HSIO_HW_CFG_QSGMII_ENA, 3878c2ecf20Sopenharmony_ci HSIO_HW_CFG_QSGMII_ENA), 3888c2ecf20Sopenharmony_ci SERDES_MUX_SGMII(SERDES6G(0), 7, HSIO_HW_CFG_QSGMII_ENA, 0), 3898c2ecf20Sopenharmony_ci SERDES_MUX_QSGMII(SERDES6G(0), 7, HSIO_HW_CFG_QSGMII_ENA, 3908c2ecf20Sopenharmony_ci HSIO_HW_CFG_QSGMII_ENA), 3918c2ecf20Sopenharmony_ci SERDES_MUX_SGMII(SERDES6G(1), 8, 0, 0), 3928c2ecf20Sopenharmony_ci SERDES_MUX_SGMII(SERDES6G(2), 10, HSIO_HW_CFG_PCIE_ENA | 3938c2ecf20Sopenharmony_ci HSIO_HW_CFG_DEV2G5_10_MODE, 0), 3948c2ecf20Sopenharmony_ci SERDES_MUX(SERDES6G(2), 10, PHY_MODE_PCIE, 0, HSIO_HW_CFG_PCIE_ENA, 3958c2ecf20Sopenharmony_ci HSIO_HW_CFG_PCIE_ENA), 3968c2ecf20Sopenharmony_ci}; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_cistatic int serdes_set_mode(struct phy *phy, enum phy_mode mode, int submode) 3998c2ecf20Sopenharmony_ci{ 4008c2ecf20Sopenharmony_ci struct serdes_macro *macro = phy_get_drvdata(phy); 4018c2ecf20Sopenharmony_ci unsigned int i; 4028c2ecf20Sopenharmony_ci int ret; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci /* As of now only PHY_MODE_ETHERNET is supported */ 4058c2ecf20Sopenharmony_ci if (mode != PHY_MODE_ETHERNET) 4068c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ocelot_serdes_muxes); i++) { 4098c2ecf20Sopenharmony_ci if (macro->idx != ocelot_serdes_muxes[i].idx || 4108c2ecf20Sopenharmony_ci mode != ocelot_serdes_muxes[i].mode || 4118c2ecf20Sopenharmony_ci submode != ocelot_serdes_muxes[i].submode) 4128c2ecf20Sopenharmony_ci continue; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci if (submode != PHY_INTERFACE_MODE_QSGMII && 4158c2ecf20Sopenharmony_ci macro->port != ocelot_serdes_muxes[i].port) 4168c2ecf20Sopenharmony_ci continue; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci ret = regmap_update_bits(macro->ctrl->regs, HSIO_HW_CFG, 4198c2ecf20Sopenharmony_ci ocelot_serdes_muxes[i].mask, 4208c2ecf20Sopenharmony_ci ocelot_serdes_muxes[i].mux); 4218c2ecf20Sopenharmony_ci if (ret) 4228c2ecf20Sopenharmony_ci return ret; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci if (macro->idx <= SERDES1G_MAX) 4258c2ecf20Sopenharmony_ci return serdes_init_s1g(macro->ctrl->regs, macro->idx); 4268c2ecf20Sopenharmony_ci else if (macro->idx <= SERDES6G_MAX) 4278c2ecf20Sopenharmony_ci return serdes_init_s6g(macro->ctrl->regs, 4288c2ecf20Sopenharmony_ci macro->idx - (SERDES1G_MAX + 1), 4298c2ecf20Sopenharmony_ci ocelot_serdes_muxes[i].submode); 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci /* PCIe not supported yet */ 4328c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 4338c2ecf20Sopenharmony_ci } 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci return -EINVAL; 4368c2ecf20Sopenharmony_ci} 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_cistatic const struct phy_ops serdes_ops = { 4398c2ecf20Sopenharmony_ci .set_mode = serdes_set_mode, 4408c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 4418c2ecf20Sopenharmony_ci}; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_cistatic struct phy *serdes_simple_xlate(struct device *dev, 4448c2ecf20Sopenharmony_ci struct of_phandle_args *args) 4458c2ecf20Sopenharmony_ci{ 4468c2ecf20Sopenharmony_ci struct serdes_ctrl *ctrl = dev_get_drvdata(dev); 4478c2ecf20Sopenharmony_ci unsigned int port, idx, i; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci if (args->args_count != 2) 4508c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci port = args->args[0]; 4538c2ecf20Sopenharmony_ci idx = args->args[1]; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci for (i = 0; i < SERDES_MAX; i++) { 4568c2ecf20Sopenharmony_ci struct serdes_macro *macro = phy_get_drvdata(ctrl->phys[i]); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci if (idx != macro->idx) 4598c2ecf20Sopenharmony_ci continue; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci /* SERDES6G(0) is the only SerDes capable of QSGMII */ 4628c2ecf20Sopenharmony_ci if (idx != SERDES6G(0) && macro->port >= 0) 4638c2ecf20Sopenharmony_ci return ERR_PTR(-EBUSY); 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci macro->port = port; 4668c2ecf20Sopenharmony_ci return ctrl->phys[i]; 4678c2ecf20Sopenharmony_ci } 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 4708c2ecf20Sopenharmony_ci} 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_cistatic int serdes_phy_create(struct serdes_ctrl *ctrl, u8 idx, struct phy **phy) 4738c2ecf20Sopenharmony_ci{ 4748c2ecf20Sopenharmony_ci struct serdes_macro *macro; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci *phy = devm_phy_create(ctrl->dev, NULL, &serdes_ops); 4778c2ecf20Sopenharmony_ci if (IS_ERR(*phy)) 4788c2ecf20Sopenharmony_ci return PTR_ERR(*phy); 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci macro = devm_kzalloc(ctrl->dev, sizeof(*macro), GFP_KERNEL); 4818c2ecf20Sopenharmony_ci if (!macro) 4828c2ecf20Sopenharmony_ci return -ENOMEM; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci macro->idx = idx; 4858c2ecf20Sopenharmony_ci macro->ctrl = ctrl; 4868c2ecf20Sopenharmony_ci macro->port = -1; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci phy_set_drvdata(*phy, macro); 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci return 0; 4918c2ecf20Sopenharmony_ci} 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_cistatic int serdes_probe(struct platform_device *pdev) 4948c2ecf20Sopenharmony_ci{ 4958c2ecf20Sopenharmony_ci struct phy_provider *provider; 4968c2ecf20Sopenharmony_ci struct serdes_ctrl *ctrl; 4978c2ecf20Sopenharmony_ci unsigned int i; 4988c2ecf20Sopenharmony_ci int ret; 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci ctrl = devm_kzalloc(&pdev->dev, sizeof(*ctrl), GFP_KERNEL); 5018c2ecf20Sopenharmony_ci if (!ctrl) 5028c2ecf20Sopenharmony_ci return -ENOMEM; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci ctrl->dev = &pdev->dev; 5058c2ecf20Sopenharmony_ci ctrl->regs = syscon_node_to_regmap(pdev->dev.parent->of_node); 5068c2ecf20Sopenharmony_ci if (IS_ERR(ctrl->regs)) 5078c2ecf20Sopenharmony_ci return PTR_ERR(ctrl->regs); 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci for (i = 0; i < SERDES_MAX; i++) { 5108c2ecf20Sopenharmony_ci ret = serdes_phy_create(ctrl, i, &ctrl->phys[i]); 5118c2ecf20Sopenharmony_ci if (ret) 5128c2ecf20Sopenharmony_ci return ret; 5138c2ecf20Sopenharmony_ci } 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci dev_set_drvdata(&pdev->dev, ctrl); 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci provider = devm_of_phy_provider_register(ctrl->dev, 5188c2ecf20Sopenharmony_ci serdes_simple_xlate); 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci return PTR_ERR_OR_ZERO(provider); 5218c2ecf20Sopenharmony_ci} 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_cistatic const struct of_device_id serdes_ids[] = { 5248c2ecf20Sopenharmony_ci { .compatible = "mscc,vsc7514-serdes", }, 5258c2ecf20Sopenharmony_ci {}, 5268c2ecf20Sopenharmony_ci}; 5278c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, serdes_ids); 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_cistatic struct platform_driver mscc_ocelot_serdes = { 5308c2ecf20Sopenharmony_ci .probe = serdes_probe, 5318c2ecf20Sopenharmony_ci .driver = { 5328c2ecf20Sopenharmony_ci .name = "mscc,ocelot-serdes", 5338c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(serdes_ids), 5348c2ecf20Sopenharmony_ci }, 5358c2ecf20Sopenharmony_ci}; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_cimodule_platform_driver(mscc_ocelot_serdes); 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ciMODULE_AUTHOR("Quentin Schulz <quentin.schulz@bootlin.com>"); 5408c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("SerDes driver for Microsemi Ocelot"); 5418c2ecf20Sopenharmony_ciMODULE_LICENSE("Dual MIT/GPL"); 542