18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Applied Micro X-Gene SoC Ethernet v2 Driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2017, Applied Micro Circuits Corporation 68c2ecf20Sopenharmony_ci * Author(s): Iyappan Subramanian <isubramanian@apm.com> 78c2ecf20Sopenharmony_ci * Keyur Chudgar <kchudgar@apm.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include "main.h" 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_cistatic int xge_mdio_write(struct mii_bus *bus, int phy_id, int reg, u16 data) 138c2ecf20Sopenharmony_ci{ 148c2ecf20Sopenharmony_ci struct xge_pdata *pdata = bus->priv; 158c2ecf20Sopenharmony_ci u32 done, val = 0; 168c2ecf20Sopenharmony_ci u8 wait = 10; 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci SET_REG_BITS(&val, PHY_ADDR, phy_id); 198c2ecf20Sopenharmony_ci SET_REG_BITS(&val, REG_ADDR, reg); 208c2ecf20Sopenharmony_ci xge_wr_csr(pdata, MII_MGMT_ADDRESS, val); 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci xge_wr_csr(pdata, MII_MGMT_CONTROL, data); 238c2ecf20Sopenharmony_ci do { 248c2ecf20Sopenharmony_ci usleep_range(5, 10); 258c2ecf20Sopenharmony_ci done = xge_rd_csr(pdata, MII_MGMT_INDICATORS); 268c2ecf20Sopenharmony_ci } while ((done & MII_MGMT_BUSY) && wait--); 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci if (done & MII_MGMT_BUSY) { 298c2ecf20Sopenharmony_ci dev_err(&bus->dev, "MII_MGMT write failed\n"); 308c2ecf20Sopenharmony_ci return -ETIMEDOUT; 318c2ecf20Sopenharmony_ci } 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci return 0; 348c2ecf20Sopenharmony_ci} 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic int xge_mdio_read(struct mii_bus *bus, int phy_id, int reg) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci struct xge_pdata *pdata = bus->priv; 398c2ecf20Sopenharmony_ci u32 data, done, val = 0; 408c2ecf20Sopenharmony_ci u8 wait = 10; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci SET_REG_BITS(&val, PHY_ADDR, phy_id); 438c2ecf20Sopenharmony_ci SET_REG_BITS(&val, REG_ADDR, reg); 448c2ecf20Sopenharmony_ci xge_wr_csr(pdata, MII_MGMT_ADDRESS, val); 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci xge_wr_csr(pdata, MII_MGMT_COMMAND, MII_READ_CYCLE); 478c2ecf20Sopenharmony_ci do { 488c2ecf20Sopenharmony_ci usleep_range(5, 10); 498c2ecf20Sopenharmony_ci done = xge_rd_csr(pdata, MII_MGMT_INDICATORS); 508c2ecf20Sopenharmony_ci } while ((done & MII_MGMT_BUSY) && wait--); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci if (done & MII_MGMT_BUSY) { 538c2ecf20Sopenharmony_ci dev_err(&bus->dev, "MII_MGMT read failed\n"); 548c2ecf20Sopenharmony_ci return -ETIMEDOUT; 558c2ecf20Sopenharmony_ci } 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci data = xge_rd_csr(pdata, MII_MGMT_STATUS); 588c2ecf20Sopenharmony_ci xge_wr_csr(pdata, MII_MGMT_COMMAND, 0); 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci return data; 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic void xge_adjust_link(struct net_device *ndev) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci struct xge_pdata *pdata = netdev_priv(ndev); 668c2ecf20Sopenharmony_ci struct phy_device *phydev = ndev->phydev; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci if (phydev->link) { 698c2ecf20Sopenharmony_ci if (pdata->phy_speed != phydev->speed) { 708c2ecf20Sopenharmony_ci pdata->phy_speed = phydev->speed; 718c2ecf20Sopenharmony_ci xge_mac_set_speed(pdata); 728c2ecf20Sopenharmony_ci xge_mac_enable(pdata); 738c2ecf20Sopenharmony_ci phy_print_status(phydev); 748c2ecf20Sopenharmony_ci } 758c2ecf20Sopenharmony_ci } else { 768c2ecf20Sopenharmony_ci if (pdata->phy_speed != SPEED_UNKNOWN) { 778c2ecf20Sopenharmony_ci pdata->phy_speed = SPEED_UNKNOWN; 788c2ecf20Sopenharmony_ci xge_mac_disable(pdata); 798c2ecf20Sopenharmony_ci phy_print_status(phydev); 808c2ecf20Sopenharmony_ci } 818c2ecf20Sopenharmony_ci } 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_civoid xge_mdio_remove(struct net_device *ndev) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci struct xge_pdata *pdata = netdev_priv(ndev); 878c2ecf20Sopenharmony_ci struct mii_bus *mdio_bus = pdata->mdio_bus; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci if (ndev->phydev) 908c2ecf20Sopenharmony_ci phy_disconnect(ndev->phydev); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci if (mdio_bus->state == MDIOBUS_REGISTERED) 938c2ecf20Sopenharmony_ci mdiobus_unregister(mdio_bus); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci mdiobus_free(mdio_bus); 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ciint xge_mdio_config(struct net_device *ndev) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; 1018c2ecf20Sopenharmony_ci struct xge_pdata *pdata = netdev_priv(ndev); 1028c2ecf20Sopenharmony_ci struct device *dev = &pdata->pdev->dev; 1038c2ecf20Sopenharmony_ci struct mii_bus *mdio_bus; 1048c2ecf20Sopenharmony_ci struct phy_device *phydev; 1058c2ecf20Sopenharmony_ci int ret; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci mdio_bus = mdiobus_alloc(); 1088c2ecf20Sopenharmony_ci if (!mdio_bus) 1098c2ecf20Sopenharmony_ci return -ENOMEM; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci mdio_bus->name = "APM X-Gene Ethernet (v2) MDIO Bus"; 1128c2ecf20Sopenharmony_ci mdio_bus->read = xge_mdio_read; 1138c2ecf20Sopenharmony_ci mdio_bus->write = xge_mdio_write; 1148c2ecf20Sopenharmony_ci mdio_bus->priv = pdata; 1158c2ecf20Sopenharmony_ci mdio_bus->parent = dev; 1168c2ecf20Sopenharmony_ci snprintf(mdio_bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(dev)); 1178c2ecf20Sopenharmony_ci pdata->mdio_bus = mdio_bus; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci mdio_bus->phy_mask = 0x1; 1208c2ecf20Sopenharmony_ci ret = mdiobus_register(mdio_bus); 1218c2ecf20Sopenharmony_ci if (ret) 1228c2ecf20Sopenharmony_ci goto err; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci phydev = phy_find_first(mdio_bus); 1258c2ecf20Sopenharmony_ci if (!phydev) { 1268c2ecf20Sopenharmony_ci dev_err(dev, "no PHY found\n"); 1278c2ecf20Sopenharmony_ci ret = -ENODEV; 1288c2ecf20Sopenharmony_ci goto err; 1298c2ecf20Sopenharmony_ci } 1308c2ecf20Sopenharmony_ci phydev = phy_connect(ndev, phydev_name(phydev), 1318c2ecf20Sopenharmony_ci &xge_adjust_link, 1328c2ecf20Sopenharmony_ci pdata->resources.phy_mode); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci if (IS_ERR(phydev)) { 1358c2ecf20Sopenharmony_ci netdev_err(ndev, "Could not attach to PHY\n"); 1368c2ecf20Sopenharmony_ci ret = PTR_ERR(phydev); 1378c2ecf20Sopenharmony_ci goto err; 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci linkmode_set_bit_array(phy_10_100_features_array, 1418c2ecf20Sopenharmony_ci ARRAY_SIZE(phy_10_100_features_array), 1428c2ecf20Sopenharmony_ci mask); 1438c2ecf20Sopenharmony_ci linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, mask); 1448c2ecf20Sopenharmony_ci linkmode_set_bit(ETHTOOL_LINK_MODE_AUI_BIT, mask); 1458c2ecf20Sopenharmony_ci linkmode_set_bit(ETHTOOL_LINK_MODE_MII_BIT, mask); 1468c2ecf20Sopenharmony_ci linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, mask); 1478c2ecf20Sopenharmony_ci linkmode_set_bit(ETHTOOL_LINK_MODE_BNC_BIT, mask); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci linkmode_andnot(phydev->supported, phydev->supported, mask); 1508c2ecf20Sopenharmony_ci linkmode_copy(phydev->advertising, phydev->supported); 1518c2ecf20Sopenharmony_ci pdata->phy_speed = SPEED_UNKNOWN; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci return 0; 1548c2ecf20Sopenharmony_cierr: 1558c2ecf20Sopenharmony_ci xge_mdio_remove(ndev); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci return ret; 1588c2ecf20Sopenharmony_ci} 159