162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * SGMI module initialisation 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2014 Texas Instruments Incorporated 662306a36Sopenharmony_ci * Authors: Sandeep Nair <sandeep_n@ti.com> 762306a36Sopenharmony_ci * Sandeep Paulraj <s-paulraj@ti.com> 862306a36Sopenharmony_ci * Wingman Kwok <w-kwok2@ti.com> 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include "netcp.h" 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#define SGMII_SRESET_RESET BIT(0) 1562306a36Sopenharmony_ci#define SGMII_SRESET_RTRESET BIT(1) 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#define SGMII_REG_STATUS_LOCK BIT(4) 1862306a36Sopenharmony_ci#define SGMII_REG_STATUS_LINK BIT(0) 1962306a36Sopenharmony_ci#define SGMII_REG_STATUS_AUTONEG BIT(2) 2062306a36Sopenharmony_ci#define SGMII_REG_CONTROL_AUTONEG BIT(0) 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define SGMII23_OFFSET(x) ((x - 2) * 0x100) 2362306a36Sopenharmony_ci#define SGMII_OFFSET(x) ((x <= 1) ? (x * 0x100) : (SGMII23_OFFSET(x))) 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci/* SGMII registers */ 2662306a36Sopenharmony_ci#define SGMII_SRESET_REG(x) (SGMII_OFFSET(x) + 0x004) 2762306a36Sopenharmony_ci#define SGMII_CTL_REG(x) (SGMII_OFFSET(x) + 0x010) 2862306a36Sopenharmony_ci#define SGMII_STATUS_REG(x) (SGMII_OFFSET(x) + 0x014) 2962306a36Sopenharmony_ci#define SGMII_MRADV_REG(x) (SGMII_OFFSET(x) + 0x018) 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic void sgmii_write_reg(void __iomem *base, int reg, u32 val) 3262306a36Sopenharmony_ci{ 3362306a36Sopenharmony_ci writel(val, base + reg); 3462306a36Sopenharmony_ci} 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistatic u32 sgmii_read_reg(void __iomem *base, int reg) 3762306a36Sopenharmony_ci{ 3862306a36Sopenharmony_ci return readl(base + reg); 3962306a36Sopenharmony_ci} 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic void sgmii_write_reg_bit(void __iomem *base, int reg, u32 val) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci writel((readl(base + reg) | val), base + reg); 4462306a36Sopenharmony_ci} 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/* port is 0 based */ 4762306a36Sopenharmony_ciint netcp_sgmii_reset(void __iomem *sgmii_ofs, int port) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci /* Soft reset */ 5062306a36Sopenharmony_ci sgmii_write_reg_bit(sgmii_ofs, SGMII_SRESET_REG(port), 5162306a36Sopenharmony_ci SGMII_SRESET_RESET); 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci while ((sgmii_read_reg(sgmii_ofs, SGMII_SRESET_REG(port)) & 5462306a36Sopenharmony_ci SGMII_SRESET_RESET) != 0x0) 5562306a36Sopenharmony_ci ; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci return 0; 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci/* port is 0 based */ 6162306a36Sopenharmony_cibool netcp_sgmii_rtreset(void __iomem *sgmii_ofs, int port, bool set) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci u32 reg; 6462306a36Sopenharmony_ci bool oldval; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci /* Initiate a soft reset */ 6762306a36Sopenharmony_ci reg = sgmii_read_reg(sgmii_ofs, SGMII_SRESET_REG(port)); 6862306a36Sopenharmony_ci oldval = (reg & SGMII_SRESET_RTRESET) != 0x0; 6962306a36Sopenharmony_ci if (set) 7062306a36Sopenharmony_ci reg |= SGMII_SRESET_RTRESET; 7162306a36Sopenharmony_ci else 7262306a36Sopenharmony_ci reg &= ~SGMII_SRESET_RTRESET; 7362306a36Sopenharmony_ci sgmii_write_reg(sgmii_ofs, SGMII_SRESET_REG(port), reg); 7462306a36Sopenharmony_ci wmb(); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci return oldval; 7762306a36Sopenharmony_ci} 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ciint netcp_sgmii_get_port_link(void __iomem *sgmii_ofs, int port) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci u32 status = 0, link = 0; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci status = sgmii_read_reg(sgmii_ofs, SGMII_STATUS_REG(port)); 8462306a36Sopenharmony_ci if ((status & SGMII_REG_STATUS_LINK) != 0) 8562306a36Sopenharmony_ci link = 1; 8662306a36Sopenharmony_ci return link; 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ciint netcp_sgmii_config(void __iomem *sgmii_ofs, int port, u32 interface) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci unsigned int i, status, mask; 9262306a36Sopenharmony_ci u32 mr_adv_ability; 9362306a36Sopenharmony_ci u32 control; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci switch (interface) { 9662306a36Sopenharmony_ci case SGMII_LINK_MAC_MAC_AUTONEG: 9762306a36Sopenharmony_ci mr_adv_ability = 0x9801; 9862306a36Sopenharmony_ci control = 0x21; 9962306a36Sopenharmony_ci break; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci case SGMII_LINK_MAC_PHY: 10262306a36Sopenharmony_ci case SGMII_LINK_MAC_PHY_NO_MDIO: 10362306a36Sopenharmony_ci mr_adv_ability = 1; 10462306a36Sopenharmony_ci control = 1; 10562306a36Sopenharmony_ci break; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci case SGMII_LINK_MAC_MAC_FORCED: 10862306a36Sopenharmony_ci mr_adv_ability = 0x9801; 10962306a36Sopenharmony_ci control = 0x20; 11062306a36Sopenharmony_ci break; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci case SGMII_LINK_MAC_FIBER: 11362306a36Sopenharmony_ci mr_adv_ability = 0x20; 11462306a36Sopenharmony_ci control = 0x1; 11562306a36Sopenharmony_ci break; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci default: 11862306a36Sopenharmony_ci WARN_ONCE(1, "Invalid sgmii interface: %d\n", interface); 11962306a36Sopenharmony_ci return -EINVAL; 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci sgmii_write_reg(sgmii_ofs, SGMII_CTL_REG(port), 0); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci /* Wait for the SerDes pll to lock */ 12562306a36Sopenharmony_ci for (i = 0; i < 1000; i++) { 12662306a36Sopenharmony_ci usleep_range(1000, 2000); 12762306a36Sopenharmony_ci status = sgmii_read_reg(sgmii_ofs, SGMII_STATUS_REG(port)); 12862306a36Sopenharmony_ci if ((status & SGMII_REG_STATUS_LOCK) != 0) 12962306a36Sopenharmony_ci break; 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci if ((status & SGMII_REG_STATUS_LOCK) == 0) 13362306a36Sopenharmony_ci pr_err("serdes PLL not locked\n"); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci sgmii_write_reg(sgmii_ofs, SGMII_MRADV_REG(port), mr_adv_ability); 13662306a36Sopenharmony_ci sgmii_write_reg(sgmii_ofs, SGMII_CTL_REG(port), control); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci mask = SGMII_REG_STATUS_LINK; 13962306a36Sopenharmony_ci if (control & SGMII_REG_CONTROL_AUTONEG) 14062306a36Sopenharmony_ci mask |= SGMII_REG_STATUS_AUTONEG; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci for (i = 0; i < 1000; i++) { 14362306a36Sopenharmony_ci usleep_range(200, 500); 14462306a36Sopenharmony_ci status = sgmii_read_reg(sgmii_ofs, SGMII_STATUS_REG(port)); 14562306a36Sopenharmony_ci if ((status & mask) == mask) 14662306a36Sopenharmony_ci break; 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci return 0; 15062306a36Sopenharmony_ci} 151