1*a239b2b1SJijie Shao // SPDX-License-Identifier: GPL-2.0+ 2*a239b2b1SJijie Shao // Copyright (c) 2024 Hisilicon Limited. 3*a239b2b1SJijie Shao 4*a239b2b1SJijie Shao #include <linux/phy.h> 5*a239b2b1SJijie Shao #include "hbg_common.h" 6*a239b2b1SJijie Shao #include "hbg_hw.h" 7*a239b2b1SJijie Shao #include "hbg_mdio.h" 8*a239b2b1SJijie Shao #include "hbg_reg.h" 9*a239b2b1SJijie Shao 10*a239b2b1SJijie Shao #define HBG_MAC_GET_PRIV(mac) ((struct hbg_priv *)(mac)->mdio_bus->priv) 11*a239b2b1SJijie Shao #define HBG_MII_BUS_GET_MAC(bus) (&((struct hbg_priv *)(bus)->priv)->mac) 12*a239b2b1SJijie Shao 13*a239b2b1SJijie Shao #define HBG_MDIO_C22_MODE 0x1 14*a239b2b1SJijie Shao #define HBG_MDIO_C22_REG_WRITE 0x1 15*a239b2b1SJijie Shao #define HBG_MDIO_C22_REG_READ 0x2 16*a239b2b1SJijie Shao 17*a239b2b1SJijie Shao #define HBG_MDIO_OP_TIMEOUT_US (1 * 1000 * 1000) 18*a239b2b1SJijie Shao #define HBG_MDIO_OP_INTERVAL_US (5 * 1000) 19*a239b2b1SJijie Shao 20*a239b2b1SJijie Shao static void hbg_mdio_set_command(struct hbg_mac *mac, u32 cmd) 21*a239b2b1SJijie Shao { 22*a239b2b1SJijie Shao hbg_reg_write(HBG_MAC_GET_PRIV(mac), HBG_REG_MDIO_COMMAND_ADDR, cmd); 23*a239b2b1SJijie Shao } 24*a239b2b1SJijie Shao 25*a239b2b1SJijie Shao static void hbg_mdio_get_command(struct hbg_mac *mac, u32 *cmd) 26*a239b2b1SJijie Shao { 27*a239b2b1SJijie Shao *cmd = hbg_reg_read(HBG_MAC_GET_PRIV(mac), HBG_REG_MDIO_COMMAND_ADDR); 28*a239b2b1SJijie Shao } 29*a239b2b1SJijie Shao 30*a239b2b1SJijie Shao static void hbg_mdio_set_wdata_reg(struct hbg_mac *mac, u16 wdata_value) 31*a239b2b1SJijie Shao { 32*a239b2b1SJijie Shao hbg_reg_write_field(HBG_MAC_GET_PRIV(mac), HBG_REG_MDIO_WDATA_ADDR, 33*a239b2b1SJijie Shao HBG_REG_MDIO_WDATA_M, wdata_value); 34*a239b2b1SJijie Shao } 35*a239b2b1SJijie Shao 36*a239b2b1SJijie Shao static u32 hbg_mdio_get_rdata_reg(struct hbg_mac *mac) 37*a239b2b1SJijie Shao { 38*a239b2b1SJijie Shao return hbg_reg_read_field(HBG_MAC_GET_PRIV(mac), 39*a239b2b1SJijie Shao HBG_REG_MDIO_RDATA_ADDR, 40*a239b2b1SJijie Shao HBG_REG_MDIO_WDATA_M); 41*a239b2b1SJijie Shao } 42*a239b2b1SJijie Shao 43*a239b2b1SJijie Shao static int hbg_mdio_wait_ready(struct hbg_mac *mac) 44*a239b2b1SJijie Shao { 45*a239b2b1SJijie Shao struct hbg_priv *priv = HBG_MAC_GET_PRIV(mac); 46*a239b2b1SJijie Shao u32 cmd = 0; 47*a239b2b1SJijie Shao int ret; 48*a239b2b1SJijie Shao 49*a239b2b1SJijie Shao ret = readl_poll_timeout(priv->io_base + HBG_REG_MDIO_COMMAND_ADDR, cmd, 50*a239b2b1SJijie Shao !FIELD_GET(HBG_REG_MDIO_COMMAND_START_B, cmd), 51*a239b2b1SJijie Shao HBG_MDIO_OP_INTERVAL_US, 52*a239b2b1SJijie Shao HBG_MDIO_OP_TIMEOUT_US); 53*a239b2b1SJijie Shao 54*a239b2b1SJijie Shao return ret ? -ETIMEDOUT : 0; 55*a239b2b1SJijie Shao } 56*a239b2b1SJijie Shao 57*a239b2b1SJijie Shao static int hbg_mdio_cmd_send(struct hbg_mac *mac, u32 prt_addr, u32 dev_addr, 58*a239b2b1SJijie Shao u32 type, u32 op_code) 59*a239b2b1SJijie Shao { 60*a239b2b1SJijie Shao u32 cmd = 0; 61*a239b2b1SJijie Shao 62*a239b2b1SJijie Shao hbg_mdio_get_command(mac, &cmd); 63*a239b2b1SJijie Shao hbg_field_modify(cmd, HBG_REG_MDIO_COMMAND_ST_M, type); 64*a239b2b1SJijie Shao hbg_field_modify(cmd, HBG_REG_MDIO_COMMAND_OP_M, op_code); 65*a239b2b1SJijie Shao hbg_field_modify(cmd, HBG_REG_MDIO_COMMAND_PRTAD_M, prt_addr); 66*a239b2b1SJijie Shao hbg_field_modify(cmd, HBG_REG_MDIO_COMMAND_DEVAD_M, dev_addr); 67*a239b2b1SJijie Shao 68*a239b2b1SJijie Shao /* if auto scan enabled, this value need fix to 0 */ 69*a239b2b1SJijie Shao hbg_field_modify(cmd, HBG_REG_MDIO_COMMAND_START_B, 0x1); 70*a239b2b1SJijie Shao 71*a239b2b1SJijie Shao hbg_mdio_set_command(mac, cmd); 72*a239b2b1SJijie Shao 73*a239b2b1SJijie Shao /* wait operation complete and check the result */ 74*a239b2b1SJijie Shao return hbg_mdio_wait_ready(mac); 75*a239b2b1SJijie Shao } 76*a239b2b1SJijie Shao 77*a239b2b1SJijie Shao static int hbg_mdio_read22(struct mii_bus *bus, int phy_addr, int regnum) 78*a239b2b1SJijie Shao { 79*a239b2b1SJijie Shao struct hbg_mac *mac = HBG_MII_BUS_GET_MAC(bus); 80*a239b2b1SJijie Shao int ret; 81*a239b2b1SJijie Shao 82*a239b2b1SJijie Shao ret = hbg_mdio_cmd_send(mac, phy_addr, regnum, HBG_MDIO_C22_MODE, 83*a239b2b1SJijie Shao HBG_MDIO_C22_REG_READ); 84*a239b2b1SJijie Shao if (ret) 85*a239b2b1SJijie Shao return ret; 86*a239b2b1SJijie Shao 87*a239b2b1SJijie Shao return hbg_mdio_get_rdata_reg(mac); 88*a239b2b1SJijie Shao } 89*a239b2b1SJijie Shao 90*a239b2b1SJijie Shao static int hbg_mdio_write22(struct mii_bus *bus, int phy_addr, int regnum, 91*a239b2b1SJijie Shao u16 val) 92*a239b2b1SJijie Shao { 93*a239b2b1SJijie Shao struct hbg_mac *mac = HBG_MII_BUS_GET_MAC(bus); 94*a239b2b1SJijie Shao 95*a239b2b1SJijie Shao hbg_mdio_set_wdata_reg(mac, val); 96*a239b2b1SJijie Shao return hbg_mdio_cmd_send(mac, phy_addr, regnum, HBG_MDIO_C22_MODE, 97*a239b2b1SJijie Shao HBG_MDIO_C22_REG_WRITE); 98*a239b2b1SJijie Shao } 99*a239b2b1SJijie Shao 100*a239b2b1SJijie Shao static void hbg_mdio_init_hw(struct hbg_priv *priv) 101*a239b2b1SJijie Shao { 102*a239b2b1SJijie Shao u32 freq = priv->dev_specs.mdio_frequency; 103*a239b2b1SJijie Shao struct hbg_mac *mac = &priv->mac; 104*a239b2b1SJijie Shao u32 cmd = 0; 105*a239b2b1SJijie Shao 106*a239b2b1SJijie Shao cmd |= FIELD_PREP(HBG_REG_MDIO_COMMAND_ST_M, HBG_MDIO_C22_MODE); 107*a239b2b1SJijie Shao cmd |= FIELD_PREP(HBG_REG_MDIO_COMMAND_AUTO_SCAN_B, HBG_STATUS_DISABLE); 108*a239b2b1SJijie Shao 109*a239b2b1SJijie Shao /* freq use two bits, which are stored in clk_sel and clk_sel_exp */ 110*a239b2b1SJijie Shao cmd |= FIELD_PREP(HBG_REG_MDIO_COMMAND_CLK_SEL_B, freq & 0x1); 111*a239b2b1SJijie Shao cmd |= FIELD_PREP(HBG_REG_MDIO_COMMAND_CLK_SEL_EXP_B, 112*a239b2b1SJijie Shao (freq >> 1) & 0x1); 113*a239b2b1SJijie Shao 114*a239b2b1SJijie Shao hbg_mdio_set_command(mac, cmd); 115*a239b2b1SJijie Shao } 116*a239b2b1SJijie Shao 117*a239b2b1SJijie Shao static void hbg_phy_adjust_link(struct net_device *netdev) 118*a239b2b1SJijie Shao { 119*a239b2b1SJijie Shao struct hbg_priv *priv = netdev_priv(netdev); 120*a239b2b1SJijie Shao struct phy_device *phydev = netdev->phydev; 121*a239b2b1SJijie Shao u32 speed; 122*a239b2b1SJijie Shao 123*a239b2b1SJijie Shao if (phydev->link != priv->mac.link_status) { 124*a239b2b1SJijie Shao if (phydev->link) { 125*a239b2b1SJijie Shao switch (phydev->speed) { 126*a239b2b1SJijie Shao case SPEED_10: 127*a239b2b1SJijie Shao speed = HBG_PORT_MODE_SGMII_10M; 128*a239b2b1SJijie Shao break; 129*a239b2b1SJijie Shao case SPEED_100: 130*a239b2b1SJijie Shao speed = HBG_PORT_MODE_SGMII_100M; 131*a239b2b1SJijie Shao break; 132*a239b2b1SJijie Shao case SPEED_1000: 133*a239b2b1SJijie Shao speed = HBG_PORT_MODE_SGMII_1000M; 134*a239b2b1SJijie Shao break; 135*a239b2b1SJijie Shao default: 136*a239b2b1SJijie Shao return; 137*a239b2b1SJijie Shao } 138*a239b2b1SJijie Shao 139*a239b2b1SJijie Shao priv->mac.speed = speed; 140*a239b2b1SJijie Shao priv->mac.duplex = phydev->duplex; 141*a239b2b1SJijie Shao priv->mac.autoneg = phydev->autoneg; 142*a239b2b1SJijie Shao hbg_hw_adjust_link(priv, speed, phydev->duplex); 143*a239b2b1SJijie Shao } 144*a239b2b1SJijie Shao 145*a239b2b1SJijie Shao priv->mac.link_status = phydev->link; 146*a239b2b1SJijie Shao phy_print_status(phydev); 147*a239b2b1SJijie Shao } 148*a239b2b1SJijie Shao } 149*a239b2b1SJijie Shao 150*a239b2b1SJijie Shao static void hbg_phy_disconnect(void *data) 151*a239b2b1SJijie Shao { 152*a239b2b1SJijie Shao phy_disconnect((struct phy_device *)data); 153*a239b2b1SJijie Shao } 154*a239b2b1SJijie Shao 155*a239b2b1SJijie Shao static int hbg_phy_connect(struct hbg_priv *priv) 156*a239b2b1SJijie Shao { 157*a239b2b1SJijie Shao struct phy_device *phydev = priv->mac.phydev; 158*a239b2b1SJijie Shao struct device *dev = &priv->pdev->dev; 159*a239b2b1SJijie Shao int ret; 160*a239b2b1SJijie Shao 161*a239b2b1SJijie Shao ret = phy_connect_direct(priv->netdev, phydev, hbg_phy_adjust_link, 162*a239b2b1SJijie Shao PHY_INTERFACE_MODE_SGMII); 163*a239b2b1SJijie Shao if (ret) 164*a239b2b1SJijie Shao return dev_err_probe(dev, ret, "failed to connect phy\n"); 165*a239b2b1SJijie Shao 166*a239b2b1SJijie Shao ret = devm_add_action_or_reset(dev, hbg_phy_disconnect, phydev); 167*a239b2b1SJijie Shao if (ret) 168*a239b2b1SJijie Shao return ret; 169*a239b2b1SJijie Shao 170*a239b2b1SJijie Shao phy_remove_link_mode(phydev, ETHTOOL_LINK_MODE_1000baseT_Half_BIT); 171*a239b2b1SJijie Shao phy_attached_info(phydev); 172*a239b2b1SJijie Shao 173*a239b2b1SJijie Shao return 0; 174*a239b2b1SJijie Shao } 175*a239b2b1SJijie Shao 176*a239b2b1SJijie Shao void hbg_phy_start(struct hbg_priv *priv) 177*a239b2b1SJijie Shao { 178*a239b2b1SJijie Shao phy_start(priv->mac.phydev); 179*a239b2b1SJijie Shao } 180*a239b2b1SJijie Shao 181*a239b2b1SJijie Shao void hbg_phy_stop(struct hbg_priv *priv) 182*a239b2b1SJijie Shao { 183*a239b2b1SJijie Shao phy_stop(priv->mac.phydev); 184*a239b2b1SJijie Shao } 185*a239b2b1SJijie Shao 186*a239b2b1SJijie Shao int hbg_mdio_init(struct hbg_priv *priv) 187*a239b2b1SJijie Shao { 188*a239b2b1SJijie Shao struct device *dev = &priv->pdev->dev; 189*a239b2b1SJijie Shao struct hbg_mac *mac = &priv->mac; 190*a239b2b1SJijie Shao struct phy_device *phydev; 191*a239b2b1SJijie Shao struct mii_bus *mdio_bus; 192*a239b2b1SJijie Shao int ret; 193*a239b2b1SJijie Shao 194*a239b2b1SJijie Shao mac->phy_addr = priv->dev_specs.phy_addr; 195*a239b2b1SJijie Shao mdio_bus = devm_mdiobus_alloc(dev); 196*a239b2b1SJijie Shao if (!mdio_bus) 197*a239b2b1SJijie Shao return dev_err_probe(dev, -ENOMEM, 198*a239b2b1SJijie Shao "failed to alloc MDIO bus\n"); 199*a239b2b1SJijie Shao 200*a239b2b1SJijie Shao mdio_bus->parent = dev; 201*a239b2b1SJijie Shao mdio_bus->priv = priv; 202*a239b2b1SJijie Shao mdio_bus->phy_mask = ~(1 << mac->phy_addr); 203*a239b2b1SJijie Shao mdio_bus->name = "hibmcge mii bus"; 204*a239b2b1SJijie Shao mac->mdio_bus = mdio_bus; 205*a239b2b1SJijie Shao 206*a239b2b1SJijie Shao mdio_bus->read = hbg_mdio_read22; 207*a239b2b1SJijie Shao mdio_bus->write = hbg_mdio_write22; 208*a239b2b1SJijie Shao snprintf(mdio_bus->id, MII_BUS_ID_SIZE, "%s-%s", "mii", dev_name(dev)); 209*a239b2b1SJijie Shao 210*a239b2b1SJijie Shao ret = devm_mdiobus_register(dev, mdio_bus); 211*a239b2b1SJijie Shao if (ret) 212*a239b2b1SJijie Shao return dev_err_probe(dev, ret, "failed to register MDIO bus\n"); 213*a239b2b1SJijie Shao 214*a239b2b1SJijie Shao phydev = mdiobus_get_phy(mdio_bus, mac->phy_addr); 215*a239b2b1SJijie Shao if (!phydev) 216*a239b2b1SJijie Shao return dev_err_probe(dev, -ENODEV, 217*a239b2b1SJijie Shao "failed to get phy device\n"); 218*a239b2b1SJijie Shao 219*a239b2b1SJijie Shao mac->phydev = phydev; 220*a239b2b1SJijie Shao hbg_mdio_init_hw(priv); 221*a239b2b1SJijie Shao return hbg_phy_connect(priv); 222*a239b2b1SJijie Shao } 223