// SPDX-License-Identifier: GPL-2.0 /* Copyright (c) 2019 - 2022 Beijing WangXun Technology Co., Ltd. */ #include #include #include #include #include "../libwx/wx_type.h" #include "../libwx/wx_hw.h" #include "ngbe_type.h" #include "ngbe_mdio.h" static int ngbe_phy_read_reg_internal(struct mii_bus *bus, int phy_addr, int regnum) { struct wx *wx = bus->priv; if (phy_addr != 0) return 0xffff; return (u16)rd32(wx, NGBE_PHY_CONFIG(regnum)); } static int ngbe_phy_write_reg_internal(struct mii_bus *bus, int phy_addr, int regnum, u16 value) { struct wx *wx = bus->priv; if (phy_addr == 0) wr32(wx, NGBE_PHY_CONFIG(regnum), value); return 0; } static int ngbe_phy_read_reg_c22(struct mii_bus *bus, int phy_addr, int regnum) { struct wx *wx = bus->priv; u16 phy_data; if (wx->mac_type == em_mac_type_mdi) phy_data = ngbe_phy_read_reg_internal(bus, phy_addr, regnum); else phy_data = wx_phy_read_reg_mdi_c22(bus, phy_addr, regnum); return phy_data; } static int ngbe_phy_write_reg_c22(struct mii_bus *bus, int phy_addr, int regnum, u16 value) { struct wx *wx = bus->priv; int ret; if (wx->mac_type == em_mac_type_mdi) ret = ngbe_phy_write_reg_internal(bus, phy_addr, regnum, value); else ret = wx_phy_write_reg_mdi_c22(bus, phy_addr, regnum, value); return ret; } static void ngbe_mac_config(struct phylink_config *config, unsigned int mode, const struct phylink_link_state *state) { } static void ngbe_mac_link_down(struct phylink_config *config, unsigned int mode, phy_interface_t interface) { } static void ngbe_mac_link_up(struct phylink_config *config, struct phy_device *phy, unsigned int mode, phy_interface_t interface, int speed, int duplex, bool tx_pause, bool rx_pause) { struct wx *wx = phylink_to_wx(config); u32 lan_speed, reg; wx_fc_enable(wx, tx_pause, rx_pause); switch (speed) { case SPEED_10: lan_speed = 0; break; case SPEED_100: lan_speed = 1; break; case SPEED_1000: default: lan_speed = 2; break; } wr32m(wx, NGBE_CFG_LAN_SPEED, 0x3, lan_speed); reg = rd32(wx, WX_MAC_TX_CFG); reg &= ~WX_MAC_TX_CFG_SPEED_MASK; reg |= WX_MAC_TX_CFG_SPEED_1G | WX_MAC_TX_CFG_TE; wr32(wx, WX_MAC_TX_CFG, reg); /* Re configure MAC Rx */ reg = rd32(wx, WX_MAC_RX_CFG); wr32(wx, WX_MAC_RX_CFG, reg); wr32(wx, WX_MAC_PKT_FLT, WX_MAC_PKT_FLT_PR); reg = rd32(wx, WX_MAC_WDG_TIMEOUT); wr32(wx, WX_MAC_WDG_TIMEOUT, reg); } static const struct phylink_mac_ops ngbe_mac_ops = { .mac_config = ngbe_mac_config, .mac_link_down = ngbe_mac_link_down, .mac_link_up = ngbe_mac_link_up, }; static int ngbe_phylink_init(struct wx *wx) { struct phylink_config *config; phy_interface_t phy_mode; struct phylink *phylink; config = &wx->phylink_config; config->dev = &wx->netdev->dev; config->type = PHYLINK_NETDEV; config->mac_capabilities = MAC_1000FD | MAC_100FD | MAC_10FD | MAC_SYM_PAUSE | MAC_ASYM_PAUSE; config->mac_managed_pm = true; phy_mode = PHY_INTERFACE_MODE_RGMII_ID; __set_bit(PHY_INTERFACE_MODE_RGMII_ID, config->supported_interfaces); phylink = phylink_create(config, NULL, phy_mode, &ngbe_mac_ops); if (IS_ERR(phylink)) return PTR_ERR(phylink); wx->phylink = phylink; return 0; } int ngbe_mdio_init(struct wx *wx) { struct pci_dev *pdev = wx->pdev; struct mii_bus *mii_bus; int ret; mii_bus = devm_mdiobus_alloc(&pdev->dev); if (!mii_bus) return -ENOMEM; mii_bus->name = "ngbe_mii_bus"; mii_bus->read = ngbe_phy_read_reg_c22; mii_bus->write = ngbe_phy_write_reg_c22; mii_bus->phy_mask = GENMASK(31, 4); mii_bus->parent = &pdev->dev; mii_bus->priv = wx; if (wx->mac_type == em_mac_type_rgmii) { mii_bus->read_c45 = wx_phy_read_reg_mdi_c45; mii_bus->write_c45 = wx_phy_write_reg_mdi_c45; } snprintf(mii_bus->id, MII_BUS_ID_SIZE, "ngbe-%x", pci_dev_id(pdev)); ret = devm_mdiobus_register(&pdev->dev, mii_bus); if (ret) return ret; wx->phydev = phy_find_first(mii_bus); if (!wx->phydev) return -ENODEV; phy_attached_info(wx->phydev); wx->link = 0; wx->speed = 0; wx->duplex = 0; ret = ngbe_phylink_init(wx); if (ret) { wx_err(wx, "failed to init phylink: %d\n", ret); return ret; } return 0; }