linux/drivers/net/ethernet/apm/xgene-v2/mdio.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * Applied Micro X-Gene SoC Ethernet v2 Driver
   4 *
   5 * Copyright (c) 2017, Applied Micro Circuits Corporation
   6 * Author(s): Iyappan Subramanian <isubramanian@apm.com>
   7 *            Keyur Chudgar <kchudgar@apm.com>
   8 */
   9
  10#include "main.h"
  11
  12static int xge_mdio_write(struct mii_bus *bus, int phy_id, int reg, u16 data)
  13{
  14        struct xge_pdata *pdata = bus->priv;
  15        u32 done, val = 0;
  16        u8 wait = 10;
  17
  18        SET_REG_BITS(&val, PHY_ADDR, phy_id);
  19        SET_REG_BITS(&val, REG_ADDR, reg);
  20        xge_wr_csr(pdata, MII_MGMT_ADDRESS, val);
  21
  22        xge_wr_csr(pdata, MII_MGMT_CONTROL, data);
  23        do {
  24                usleep_range(5, 10);
  25                done = xge_rd_csr(pdata, MII_MGMT_INDICATORS);
  26        } while ((done & MII_MGMT_BUSY) && wait--);
  27
  28        if (done & MII_MGMT_BUSY) {
  29                dev_err(&bus->dev, "MII_MGMT write failed\n");
  30                return -ETIMEDOUT;
  31        }
  32
  33        return 0;
  34}
  35
  36static int xge_mdio_read(struct mii_bus *bus, int phy_id, int reg)
  37{
  38        struct xge_pdata *pdata = bus->priv;
  39        u32 data, done, val = 0;
  40        u8 wait = 10;
  41
  42        SET_REG_BITS(&val, PHY_ADDR, phy_id);
  43        SET_REG_BITS(&val, REG_ADDR, reg);
  44        xge_wr_csr(pdata, MII_MGMT_ADDRESS, val);
  45
  46        xge_wr_csr(pdata, MII_MGMT_COMMAND, MII_READ_CYCLE);
  47        do {
  48                usleep_range(5, 10);
  49                done = xge_rd_csr(pdata, MII_MGMT_INDICATORS);
  50        } while ((done & MII_MGMT_BUSY) && wait--);
  51
  52        if (done & MII_MGMT_BUSY) {
  53                dev_err(&bus->dev, "MII_MGMT read failed\n");
  54                return -ETIMEDOUT;
  55        }
  56
  57        data = xge_rd_csr(pdata, MII_MGMT_STATUS);
  58        xge_wr_csr(pdata, MII_MGMT_COMMAND, 0);
  59
  60        return data;
  61}
  62
  63static void xge_adjust_link(struct net_device *ndev)
  64{
  65        struct xge_pdata *pdata = netdev_priv(ndev);
  66        struct phy_device *phydev = ndev->phydev;
  67
  68        if (phydev->link) {
  69                if (pdata->phy_speed != phydev->speed) {
  70                        pdata->phy_speed = phydev->speed;
  71                        xge_mac_set_speed(pdata);
  72                        xge_mac_enable(pdata);
  73                        phy_print_status(phydev);
  74                }
  75        } else {
  76                if (pdata->phy_speed != SPEED_UNKNOWN) {
  77                        pdata->phy_speed = SPEED_UNKNOWN;
  78                        xge_mac_disable(pdata);
  79                        phy_print_status(phydev);
  80                }
  81        }
  82}
  83
  84void xge_mdio_remove(struct net_device *ndev)
  85{
  86        struct xge_pdata *pdata = netdev_priv(ndev);
  87        struct mii_bus *mdio_bus = pdata->mdio_bus;
  88
  89        if (ndev->phydev)
  90                phy_disconnect(ndev->phydev);
  91
  92        if (mdio_bus->state == MDIOBUS_REGISTERED)
  93                mdiobus_unregister(mdio_bus);
  94
  95        mdiobus_free(mdio_bus);
  96}
  97
  98int xge_mdio_config(struct net_device *ndev)
  99{
 100        __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
 101        struct xge_pdata *pdata = netdev_priv(ndev);
 102        struct device *dev = &pdata->pdev->dev;
 103        struct mii_bus *mdio_bus;
 104        struct phy_device *phydev;
 105        int ret;
 106
 107        mdio_bus = mdiobus_alloc();
 108        if (!mdio_bus)
 109                return -ENOMEM;
 110
 111        mdio_bus->name = "APM X-Gene Ethernet (v2) MDIO Bus";
 112        mdio_bus->read = xge_mdio_read;
 113        mdio_bus->write = xge_mdio_write;
 114        mdio_bus->priv = pdata;
 115        mdio_bus->parent = dev;
 116        snprintf(mdio_bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(dev));
 117        pdata->mdio_bus = mdio_bus;
 118
 119        mdio_bus->phy_mask = 0x1;
 120        ret = mdiobus_register(mdio_bus);
 121        if (ret)
 122                goto err;
 123
 124        phydev = phy_find_first(mdio_bus);
 125        if (!phydev) {
 126                dev_err(dev, "no PHY found\n");
 127                ret = -ENODEV;
 128                goto err;
 129        }
 130        phydev = phy_connect(ndev, phydev_name(phydev),
 131                             &xge_adjust_link,
 132                             pdata->resources.phy_mode);
 133
 134        if (IS_ERR(phydev)) {
 135                netdev_err(ndev, "Could not attach to PHY\n");
 136                ret = PTR_ERR(phydev);
 137                goto err;
 138        }
 139
 140        linkmode_set_bit_array(phy_10_100_features_array,
 141                               ARRAY_SIZE(phy_10_100_features_array),
 142                               mask);
 143        linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, mask);
 144        linkmode_set_bit(ETHTOOL_LINK_MODE_AUI_BIT, mask);
 145        linkmode_set_bit(ETHTOOL_LINK_MODE_MII_BIT, mask);
 146        linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, mask);
 147        linkmode_set_bit(ETHTOOL_LINK_MODE_BNC_BIT, mask);
 148
 149        linkmode_andnot(phydev->supported, phydev->supported, mask);
 150        linkmode_copy(phydev->advertising, phydev->supported);
 151        pdata->phy_speed = SPEED_UNKNOWN;
 152
 153        return 0;
 154err:
 155        xge_mdio_remove(ndev);
 156
 157        return ret;
 158}
 159