1*68cf027fSGrygorii Strashko // SPDX-License-Identifier: GPL-2.0 26f8d3f33SWingman Kwok /* 36f8d3f33SWingman Kwok * SGMI module initialisation 46f8d3f33SWingman Kwok * 56f8d3f33SWingman Kwok * Copyright (C) 2014 Texas Instruments Incorporated 66f8d3f33SWingman Kwok * Authors: Sandeep Nair <sandeep_n@ti.com> 76f8d3f33SWingman Kwok * Sandeep Paulraj <s-paulraj@ti.com> 86f8d3f33SWingman Kwok * Wingman Kwok <w-kwok2@ti.com> 96f8d3f33SWingman Kwok * 106f8d3f33SWingman Kwok */ 116f8d3f33SWingman Kwok 126f8d3f33SWingman Kwok #include "netcp.h" 136f8d3f33SWingman Kwok 147025e88aSWingMan Kwok #define SGMII_SRESET_RESET BIT(0) 157025e88aSWingMan Kwok #define SGMII_SRESET_RTRESET BIT(1) 167025e88aSWingMan Kwok 176f8d3f33SWingman Kwok #define SGMII_REG_STATUS_LOCK BIT(4) 186f8d3f33SWingman Kwok #define SGMII_REG_STATUS_LINK BIT(0) 196f8d3f33SWingman Kwok #define SGMII_REG_STATUS_AUTONEG BIT(2) 206f8d3f33SWingman Kwok #define SGMII_REG_CONTROL_AUTONEG BIT(0) 216f8d3f33SWingman Kwok 226f8d3f33SWingman Kwok #define SGMII23_OFFSET(x) ((x - 2) * 0x100) 236f8d3f33SWingman Kwok #define SGMII_OFFSET(x) ((x <= 1) ? (x * 0x100) : (SGMII23_OFFSET(x))) 246f8d3f33SWingman Kwok 256f8d3f33SWingman Kwok /* SGMII registers */ 266f8d3f33SWingman Kwok #define SGMII_SRESET_REG(x) (SGMII_OFFSET(x) + 0x004) 276f8d3f33SWingman Kwok #define SGMII_CTL_REG(x) (SGMII_OFFSET(x) + 0x010) 286f8d3f33SWingman Kwok #define SGMII_STATUS_REG(x) (SGMII_OFFSET(x) + 0x014) 296f8d3f33SWingman Kwok #define SGMII_MRADV_REG(x) (SGMII_OFFSET(x) + 0x018) 306f8d3f33SWingman Kwok 316f8d3f33SWingman Kwok static void sgmii_write_reg(void __iomem *base, int reg, u32 val) 326f8d3f33SWingman Kwok { 336f8d3f33SWingman Kwok writel(val, base + reg); 346f8d3f33SWingman Kwok } 356f8d3f33SWingman Kwok 366f8d3f33SWingman Kwok static u32 sgmii_read_reg(void __iomem *base, int reg) 376f8d3f33SWingman Kwok { 386f8d3f33SWingman Kwok return readl(base + reg); 396f8d3f33SWingman Kwok } 406f8d3f33SWingman Kwok 416f8d3f33SWingman Kwok static void sgmii_write_reg_bit(void __iomem *base, int reg, u32 val) 426f8d3f33SWingman Kwok { 436f8d3f33SWingman Kwok writel((readl(base + reg) | val), base + reg); 446f8d3f33SWingman Kwok } 456f8d3f33SWingman Kwok 466f8d3f33SWingman Kwok /* port is 0 based */ 476f8d3f33SWingman Kwok int netcp_sgmii_reset(void __iomem *sgmii_ofs, int port) 486f8d3f33SWingman Kwok { 496f8d3f33SWingman Kwok /* Soft reset */ 507025e88aSWingMan Kwok sgmii_write_reg_bit(sgmii_ofs, SGMII_SRESET_REG(port), 517025e88aSWingMan Kwok SGMII_SRESET_RESET); 527025e88aSWingMan Kwok 537025e88aSWingMan Kwok while ((sgmii_read_reg(sgmii_ofs, SGMII_SRESET_REG(port)) & 547025e88aSWingMan Kwok SGMII_SRESET_RESET) != 0x0) 556f8d3f33SWingman Kwok ; 567025e88aSWingMan Kwok 576f8d3f33SWingman Kwok return 0; 586f8d3f33SWingman Kwok } 596f8d3f33SWingman Kwok 607025e88aSWingMan Kwok /* port is 0 based */ 617025e88aSWingMan Kwok bool netcp_sgmii_rtreset(void __iomem *sgmii_ofs, int port, bool set) 627025e88aSWingMan Kwok { 637025e88aSWingMan Kwok u32 reg; 647025e88aSWingMan Kwok bool oldval; 657025e88aSWingMan Kwok 667025e88aSWingMan Kwok /* Initiate a soft reset */ 677025e88aSWingMan Kwok reg = sgmii_read_reg(sgmii_ofs, SGMII_SRESET_REG(port)); 687025e88aSWingMan Kwok oldval = (reg & SGMII_SRESET_RTRESET) != 0x0; 697025e88aSWingMan Kwok if (set) 707025e88aSWingMan Kwok reg |= SGMII_SRESET_RTRESET; 717025e88aSWingMan Kwok else 727025e88aSWingMan Kwok reg &= ~SGMII_SRESET_RTRESET; 737025e88aSWingMan Kwok sgmii_write_reg(sgmii_ofs, SGMII_SRESET_REG(port), reg); 747025e88aSWingMan Kwok wmb(); 757025e88aSWingMan Kwok 767025e88aSWingMan Kwok return oldval; 777025e88aSWingMan Kwok } 787025e88aSWingMan Kwok 796f8d3f33SWingman Kwok int netcp_sgmii_get_port_link(void __iomem *sgmii_ofs, int port) 806f8d3f33SWingman Kwok { 816f8d3f33SWingman Kwok u32 status = 0, link = 0; 826f8d3f33SWingman Kwok 836f8d3f33SWingman Kwok status = sgmii_read_reg(sgmii_ofs, SGMII_STATUS_REG(port)); 846f8d3f33SWingman Kwok if ((status & SGMII_REG_STATUS_LINK) != 0) 856f8d3f33SWingman Kwok link = 1; 866f8d3f33SWingman Kwok return link; 876f8d3f33SWingman Kwok } 886f8d3f33SWingman Kwok 896f8d3f33SWingman Kwok int netcp_sgmii_config(void __iomem *sgmii_ofs, int port, u32 interface) 906f8d3f33SWingman Kwok { 916f8d3f33SWingman Kwok unsigned int i, status, mask; 926f8d3f33SWingman Kwok u32 mr_adv_ability; 936f8d3f33SWingman Kwok u32 control; 946f8d3f33SWingman Kwok 956f8d3f33SWingman Kwok switch (interface) { 966f8d3f33SWingman Kwok case SGMII_LINK_MAC_MAC_AUTONEG: 976f8d3f33SWingman Kwok mr_adv_ability = 0x9801; 986f8d3f33SWingman Kwok control = 0x21; 996f8d3f33SWingman Kwok break; 1006f8d3f33SWingman Kwok 1016f8d3f33SWingman Kwok case SGMII_LINK_MAC_PHY: 1026f8d3f33SWingman Kwok case SGMII_LINK_MAC_PHY_NO_MDIO: 1036f8d3f33SWingman Kwok mr_adv_ability = 1; 1046f8d3f33SWingman Kwok control = 1; 1056f8d3f33SWingman Kwok break; 1066f8d3f33SWingman Kwok 1076f8d3f33SWingman Kwok case SGMII_LINK_MAC_MAC_FORCED: 1086f8d3f33SWingman Kwok mr_adv_ability = 0x9801; 1096f8d3f33SWingman Kwok control = 0x20; 1106f8d3f33SWingman Kwok break; 1116f8d3f33SWingman Kwok 1126f8d3f33SWingman Kwok case SGMII_LINK_MAC_FIBER: 1136f8d3f33SWingman Kwok mr_adv_ability = 0x20; 1146f8d3f33SWingman Kwok control = 0x1; 1156f8d3f33SWingman Kwok break; 1166f8d3f33SWingman Kwok 1176f8d3f33SWingman Kwok default: 1186f8d3f33SWingman Kwok WARN_ONCE(1, "Invalid sgmii interface: %d\n", interface); 1196f8d3f33SWingman Kwok return -EINVAL; 1206f8d3f33SWingman Kwok } 1216f8d3f33SWingman Kwok 1226f8d3f33SWingman Kwok sgmii_write_reg(sgmii_ofs, SGMII_CTL_REG(port), 0); 1236f8d3f33SWingman Kwok 1246f8d3f33SWingman Kwok /* Wait for the SerDes pll to lock */ 1256f8d3f33SWingman Kwok for (i = 0; i < 1000; i++) { 1266f8d3f33SWingman Kwok usleep_range(1000, 2000); 1276f8d3f33SWingman Kwok status = sgmii_read_reg(sgmii_ofs, SGMII_STATUS_REG(port)); 1286f8d3f33SWingman Kwok if ((status & SGMII_REG_STATUS_LOCK) != 0) 1296f8d3f33SWingman Kwok break; 1306f8d3f33SWingman Kwok } 1316f8d3f33SWingman Kwok 1326f8d3f33SWingman Kwok if ((status & SGMII_REG_STATUS_LOCK) == 0) 1336f8d3f33SWingman Kwok pr_err("serdes PLL not locked\n"); 1346f8d3f33SWingman Kwok 1356f8d3f33SWingman Kwok sgmii_write_reg(sgmii_ofs, SGMII_MRADV_REG(port), mr_adv_ability); 1366f8d3f33SWingman Kwok sgmii_write_reg(sgmii_ofs, SGMII_CTL_REG(port), control); 1376f8d3f33SWingman Kwok 1386f8d3f33SWingman Kwok mask = SGMII_REG_STATUS_LINK; 1396f8d3f33SWingman Kwok if (control & SGMII_REG_CONTROL_AUTONEG) 1406f8d3f33SWingman Kwok mask |= SGMII_REG_STATUS_AUTONEG; 1416f8d3f33SWingman Kwok 1426f8d3f33SWingman Kwok for (i = 0; i < 1000; i++) { 1436f8d3f33SWingman Kwok usleep_range(200, 500); 1446f8d3f33SWingman Kwok status = sgmii_read_reg(sgmii_ofs, SGMII_STATUS_REG(port)); 1456f8d3f33SWingman Kwok if ((status & mask) == mask) 1466f8d3f33SWingman Kwok break; 1476f8d3f33SWingman Kwok } 1486f8d3f33SWingman Kwok 1496f8d3f33SWingman Kwok return 0; 1506f8d3f33SWingman Kwok } 151