1*6f8d3f33SWingman Kwok /* 2*6f8d3f33SWingman Kwok * SGMI module initialisation 3*6f8d3f33SWingman Kwok * 4*6f8d3f33SWingman Kwok * Copyright (C) 2014 Texas Instruments Incorporated 5*6f8d3f33SWingman Kwok * Authors: Sandeep Nair <sandeep_n@ti.com> 6*6f8d3f33SWingman Kwok * Sandeep Paulraj <s-paulraj@ti.com> 7*6f8d3f33SWingman Kwok * Wingman Kwok <w-kwok2@ti.com> 8*6f8d3f33SWingman Kwok * 9*6f8d3f33SWingman Kwok * This program is free software; you can redistribute it and/or 10*6f8d3f33SWingman Kwok * modify it under the terms of the GNU General Public License as 11*6f8d3f33SWingman Kwok * published by the Free Software Foundation version 2. 12*6f8d3f33SWingman Kwok * 13*6f8d3f33SWingman Kwok * This program is distributed "as is" WITHOUT ANY WARRANTY of any 14*6f8d3f33SWingman Kwok * kind, whether express or implied; without even the implied warranty 15*6f8d3f33SWingman Kwok * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16*6f8d3f33SWingman Kwok * GNU General Public License for more details. 17*6f8d3f33SWingman Kwok */ 18*6f8d3f33SWingman Kwok 19*6f8d3f33SWingman Kwok #include "netcp.h" 20*6f8d3f33SWingman Kwok 21*6f8d3f33SWingman Kwok #define SGMII_REG_STATUS_LOCK BIT(4) 22*6f8d3f33SWingman Kwok #define SGMII_REG_STATUS_LINK BIT(0) 23*6f8d3f33SWingman Kwok #define SGMII_REG_STATUS_AUTONEG BIT(2) 24*6f8d3f33SWingman Kwok #define SGMII_REG_CONTROL_AUTONEG BIT(0) 25*6f8d3f33SWingman Kwok 26*6f8d3f33SWingman Kwok #define SGMII23_OFFSET(x) ((x - 2) * 0x100) 27*6f8d3f33SWingman Kwok #define SGMII_OFFSET(x) ((x <= 1) ? (x * 0x100) : (SGMII23_OFFSET(x))) 28*6f8d3f33SWingman Kwok 29*6f8d3f33SWingman Kwok /* SGMII registers */ 30*6f8d3f33SWingman Kwok #define SGMII_SRESET_REG(x) (SGMII_OFFSET(x) + 0x004) 31*6f8d3f33SWingman Kwok #define SGMII_CTL_REG(x) (SGMII_OFFSET(x) + 0x010) 32*6f8d3f33SWingman Kwok #define SGMII_STATUS_REG(x) (SGMII_OFFSET(x) + 0x014) 33*6f8d3f33SWingman Kwok #define SGMII_MRADV_REG(x) (SGMII_OFFSET(x) + 0x018) 34*6f8d3f33SWingman Kwok 35*6f8d3f33SWingman Kwok static void sgmii_write_reg(void __iomem *base, int reg, u32 val) 36*6f8d3f33SWingman Kwok { 37*6f8d3f33SWingman Kwok writel(val, base + reg); 38*6f8d3f33SWingman Kwok } 39*6f8d3f33SWingman Kwok 40*6f8d3f33SWingman Kwok static u32 sgmii_read_reg(void __iomem *base, int reg) 41*6f8d3f33SWingman Kwok { 42*6f8d3f33SWingman Kwok return readl(base + reg); 43*6f8d3f33SWingman Kwok } 44*6f8d3f33SWingman Kwok 45*6f8d3f33SWingman Kwok static void sgmii_write_reg_bit(void __iomem *base, int reg, u32 val) 46*6f8d3f33SWingman Kwok { 47*6f8d3f33SWingman Kwok writel((readl(base + reg) | val), base + reg); 48*6f8d3f33SWingman Kwok } 49*6f8d3f33SWingman Kwok 50*6f8d3f33SWingman Kwok /* port is 0 based */ 51*6f8d3f33SWingman Kwok int netcp_sgmii_reset(void __iomem *sgmii_ofs, int port) 52*6f8d3f33SWingman Kwok { 53*6f8d3f33SWingman Kwok /* Soft reset */ 54*6f8d3f33SWingman Kwok sgmii_write_reg_bit(sgmii_ofs, SGMII_SRESET_REG(port), 0x1); 55*6f8d3f33SWingman Kwok while (sgmii_read_reg(sgmii_ofs, SGMII_SRESET_REG(port)) != 0x0) 56*6f8d3f33SWingman Kwok ; 57*6f8d3f33SWingman Kwok return 0; 58*6f8d3f33SWingman Kwok } 59*6f8d3f33SWingman Kwok 60*6f8d3f33SWingman Kwok int netcp_sgmii_get_port_link(void __iomem *sgmii_ofs, int port) 61*6f8d3f33SWingman Kwok { 62*6f8d3f33SWingman Kwok u32 status = 0, link = 0; 63*6f8d3f33SWingman Kwok 64*6f8d3f33SWingman Kwok status = sgmii_read_reg(sgmii_ofs, SGMII_STATUS_REG(port)); 65*6f8d3f33SWingman Kwok if ((status & SGMII_REG_STATUS_LINK) != 0) 66*6f8d3f33SWingman Kwok link = 1; 67*6f8d3f33SWingman Kwok return link; 68*6f8d3f33SWingman Kwok } 69*6f8d3f33SWingman Kwok 70*6f8d3f33SWingman Kwok int netcp_sgmii_config(void __iomem *sgmii_ofs, int port, u32 interface) 71*6f8d3f33SWingman Kwok { 72*6f8d3f33SWingman Kwok unsigned int i, status, mask; 73*6f8d3f33SWingman Kwok u32 mr_adv_ability; 74*6f8d3f33SWingman Kwok u32 control; 75*6f8d3f33SWingman Kwok 76*6f8d3f33SWingman Kwok switch (interface) { 77*6f8d3f33SWingman Kwok case SGMII_LINK_MAC_MAC_AUTONEG: 78*6f8d3f33SWingman Kwok mr_adv_ability = 0x9801; 79*6f8d3f33SWingman Kwok control = 0x21; 80*6f8d3f33SWingman Kwok break; 81*6f8d3f33SWingman Kwok 82*6f8d3f33SWingman Kwok case SGMII_LINK_MAC_PHY: 83*6f8d3f33SWingman Kwok case SGMII_LINK_MAC_PHY_NO_MDIO: 84*6f8d3f33SWingman Kwok mr_adv_ability = 1; 85*6f8d3f33SWingman Kwok control = 1; 86*6f8d3f33SWingman Kwok break; 87*6f8d3f33SWingman Kwok 88*6f8d3f33SWingman Kwok case SGMII_LINK_MAC_MAC_FORCED: 89*6f8d3f33SWingman Kwok mr_adv_ability = 0x9801; 90*6f8d3f33SWingman Kwok control = 0x20; 91*6f8d3f33SWingman Kwok break; 92*6f8d3f33SWingman Kwok 93*6f8d3f33SWingman Kwok case SGMII_LINK_MAC_FIBER: 94*6f8d3f33SWingman Kwok mr_adv_ability = 0x20; 95*6f8d3f33SWingman Kwok control = 0x1; 96*6f8d3f33SWingman Kwok break; 97*6f8d3f33SWingman Kwok 98*6f8d3f33SWingman Kwok default: 99*6f8d3f33SWingman Kwok WARN_ONCE(1, "Invalid sgmii interface: %d\n", interface); 100*6f8d3f33SWingman Kwok return -EINVAL; 101*6f8d3f33SWingman Kwok } 102*6f8d3f33SWingman Kwok 103*6f8d3f33SWingman Kwok sgmii_write_reg(sgmii_ofs, SGMII_CTL_REG(port), 0); 104*6f8d3f33SWingman Kwok 105*6f8d3f33SWingman Kwok /* Wait for the SerDes pll to lock */ 106*6f8d3f33SWingman Kwok for (i = 0; i < 1000; i++) { 107*6f8d3f33SWingman Kwok usleep_range(1000, 2000); 108*6f8d3f33SWingman Kwok status = sgmii_read_reg(sgmii_ofs, SGMII_STATUS_REG(port)); 109*6f8d3f33SWingman Kwok if ((status & SGMII_REG_STATUS_LOCK) != 0) 110*6f8d3f33SWingman Kwok break; 111*6f8d3f33SWingman Kwok } 112*6f8d3f33SWingman Kwok 113*6f8d3f33SWingman Kwok if ((status & SGMII_REG_STATUS_LOCK) == 0) 114*6f8d3f33SWingman Kwok pr_err("serdes PLL not locked\n"); 115*6f8d3f33SWingman Kwok 116*6f8d3f33SWingman Kwok sgmii_write_reg(sgmii_ofs, SGMII_MRADV_REG(port), mr_adv_ability); 117*6f8d3f33SWingman Kwok sgmii_write_reg(sgmii_ofs, SGMII_CTL_REG(port), control); 118*6f8d3f33SWingman Kwok 119*6f8d3f33SWingman Kwok mask = SGMII_REG_STATUS_LINK; 120*6f8d3f33SWingman Kwok if (control & SGMII_REG_CONTROL_AUTONEG) 121*6f8d3f33SWingman Kwok mask |= SGMII_REG_STATUS_AUTONEG; 122*6f8d3f33SWingman Kwok 123*6f8d3f33SWingman Kwok for (i = 0; i < 1000; i++) { 124*6f8d3f33SWingman Kwok usleep_range(200, 500); 125*6f8d3f33SWingman Kwok status = sgmii_read_reg(sgmii_ofs, SGMII_STATUS_REG(port)); 126*6f8d3f33SWingman Kwok if ((status & mask) == mask) 127*6f8d3f33SWingman Kwok break; 128*6f8d3f33SWingman Kwok } 129*6f8d3f33SWingman Kwok 130*6f8d3f33SWingman Kwok return 0; 131*6f8d3f33SWingman Kwok } 132