--- /dev/null
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ Copyright (c) 2015 Broadcom Corporation
+ All Rights Reserved
+
+
+*/
+
+/*
+ * Created on: Dec 2015
+ * Author: yuval.raviv@broadcom.com
+ */
+
+#include "bus_drv.h"
+#include "phy_drv.h"
+#include "phy_drv_mii.h"
+
+int mii_phyid_get(phy_dev_t *phy_dev, uint32_t *phyid)
+{
+ int ret;
+ uint16_t phyid1, phyid2;
+
+ if ((ret = phy_bus_read(phy_dev, MII_PHYSID1, &phyid1)))
+ goto Exit;
+
+ if ((ret = phy_bus_read(phy_dev, MII_PHYSID2, &phyid2)))
+ goto Exit;
+
+ *phyid = phyid1 << 16 | phyid2;
+
+Exit:
+ return ret;
+}
+
+int mii_autoneg_restart(phy_dev_t *phy_dev)
+{
+ uint16_t val;
+ int ret;
+
+ if ((ret = phy_bus_read(phy_dev, MII_BMCR, &val)))
+ goto Exit;
+
+ if ((ret = phy_bus_write(phy_dev, MII_BMCR, val | BMCR_ANRESTART)))
+ goto Exit;
+
+Exit:
+ return ret;
+}
+
+int mii_power_get(phy_dev_t *phy_dev, int *enable)
+{
+ uint16_t val;
+ int ret;
+
+ if ((ret = phy_bus_read(phy_dev, MII_BMCR, &val)))
+ goto Exit;
+
+ *enable = (val & BMCR_POWERDOWN) ? 0 : 1;
+
+Exit:
+ return ret;
+}
+
+int mii_power_set(phy_dev_t *phy_dev, int enable)
+{
+ uint16_t val;
+ int ret;
+
+ if ((ret = phy_bus_read(phy_dev, MII_BMCR, &val)))
+ goto Exit;
+
+ if (!enable || (val & BMCR_POWERDOWN))
+ val |= BMCR_ANRESTART;
+
+ if (enable)
+ val &= ~BMCR_POWERDOWN;
+ else
+ val |= BMCR_POWERDOWN;
+
+ if ((ret = phy_bus_write(phy_dev, MII_BMCR, val)))
+ goto Exit;
+
+Exit:
+ return ret;
+}
+
+int mii_caps_get(phy_dev_t *phy_dev, int caps_type, uint32_t *pcaps)
+{
+ int ret;
+ uint32_t caps = 0;
+
+ if (caps_type == CAPS_TYPE_ADVERTISE)
+ {
+ int speed = 0;
+ uint16_t bmcr, ctrl1000, adv;
+
+ if ((ret = phy_bus_read(phy_dev, MII_BMCR, &bmcr)))
+ goto Exit;
+
+ if ((ret = phy_bus_read(phy_dev, MII_CTRL1000, &ctrl1000)))
+ goto Exit;
+
+ if ((ret = phy_bus_read(phy_dev, MII_ADVERTISE, &adv)))
+ goto Exit;
+
+ if (bmcr & BMCR_ANENABLE)
+ {
+ caps |= PHY_CAP_AUTONEG;
+
+ if (adv & ADVERTISE_PAUSE_CAP) caps |= PHY_CAP_PAUSE;
+ if (adv & ADVERTISE_PAUSE_ASYM) caps |= PHY_CAP_PAUSE_ASYM;
+ if (adv & ADVERTISE_10HALF) caps |= PHY_CAP_10_HALF;
+ if (adv & ADVERTISE_10FULL) caps |= PHY_CAP_10_FULL;
+ if (adv & ADVERTISE_100HALF) caps |= PHY_CAP_100_HALF;
+ if (adv & ADVERTISE_100FULL) caps |= PHY_CAP_100_FULL;
+ if (ctrl1000 & ADVERTISE_1000HALF) caps |= PHY_CAP_1000_HALF;
+ if (ctrl1000 & ADVERTISE_1000FULL) caps |= PHY_CAP_1000_FULL;
+ if (ctrl1000 & ADVERTISE_REPEATER) caps |= PHY_CAP_REPEATER;
+ }
+ else
+ {
+ speed = (bmcr & BMCR_SPEED100) >> 13 | (bmcr & BMCR_SPEED1000) >> 5;
+
+ if (speed == 0) caps |= bmcr & BMCR_FULLDPLX ? PHY_CAP_10_FULL : PHY_CAP_10_HALF;
+ if (speed == 1) caps |= bmcr & BMCR_FULLDPLX ? PHY_CAP_100_FULL : PHY_CAP_100_HALF;
+ if (speed == 2) caps |= bmcr & BMCR_FULLDPLX ? PHY_CAP_1000_FULL : PHY_CAP_1000_HALF;
+ }
+
+ }
+ else if (caps_type == CAPS_TYPE_SUPPORTED)
+ {
+ uint16_t estatus = 0;
+ uint16_t bmsr = 0;
+
+ if ((ret = phy_bus_read(phy_dev, MII_BMSR, &bmsr)))
+ goto Exit;
+
+ if ((phy_dev->mii_type != PHY_MII_TYPE_MII)
+ && (phy_dev->mii_type != PHY_MII_TYPE_TMII))
+ {
+ if ((ret = phy_bus_read(phy_dev, MII_ESTATUS, &estatus)))
+ goto Exit;
+ }
+
+ if (bmsr & BMSR_ANEGCAPABLE) caps |= PHY_CAP_AUTONEG;
+ if (bmsr & BMSR_10HALF) caps |= PHY_CAP_10_HALF;
+ if (bmsr & BMSR_10FULL) caps |= PHY_CAP_10_FULL;
+ if (bmsr & BMSR_100HALF) caps |= PHY_CAP_100_HALF;
+ if (bmsr & BMSR_100FULL) caps |= PHY_CAP_100_FULL;
+ if (estatus & ESTATUS_1000_THALF) caps |= PHY_CAP_1000_HALF;
+ if (estatus & ESTATUS_1000_TFULL) caps |= PHY_CAP_1000_FULL;
+ }
+ else if (caps_type == CAPS_TYPE_LP_ADVERTISED)
+ {
+ uint16_t stat1000, lpa;
+
+ if ((ret = phy_bus_read(phy_dev, MII_STAT1000, &stat1000)))
+ goto Exit;
+
+ if ((ret = phy_bus_read(phy_dev, MII_LPA, &lpa)))
+ goto Exit;
+
+ if (lpa & LPA_LPACK)
+ {
+ caps |= PHY_CAP_AUTONEG;
+ if (lpa & LPA_PAUSE_CAP) caps |= PHY_CAP_PAUSE;
+ if (lpa & LPA_PAUSE_ASYM) caps |= PHY_CAP_PAUSE_ASYM;
+ if (lpa & LPA_10HALF) caps |= PHY_CAP_10_HALF;
+ if (lpa & LPA_10FULL) caps |= PHY_CAP_10_FULL;
+ if (lpa & LPA_100HALF) caps |= PHY_CAP_100_HALF;
+ if (lpa & LPA_100FULL) caps |= PHY_CAP_100_FULL;
+ if (stat1000 & LPA_1000HALF) caps |= PHY_CAP_1000_HALF;
+ if (stat1000 & LPA_1000FULL) caps |= PHY_CAP_1000_FULL;
+ }
+ }
+
+ *pcaps = caps;
+
+Exit:
+ return ret;
+}
+
+int mii_caps_set(phy_dev_t *phy_dev, uint32_t caps)
+{
+ int ret;
+ uint16_t bmcr, ctrl1000, adv;
+
+ if ((ret = phy_bus_read(phy_dev, MII_BMCR, &bmcr)))
+ goto Exit;
+
+ if ((ret = phy_bus_read(phy_dev, MII_CTRL1000, &ctrl1000)))
+ goto Exit;
+
+ if ((ret = phy_bus_read(phy_dev, MII_ADVERTISE, &adv)))
+ goto Exit;
+
+ bmcr &= ~(BMCR_ANENABLE | BMCR_FULLDPLX | BMCR_SPEED100 | BMCR_SPEED1000);
+ ctrl1000 &= ~(ADVERTISE_1000HALF | ADVERTISE_1000FULL | ADVERTISE_REPEATER);
+ adv &= ~(ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM | ADVERTISE_10HALF | ADVERTISE_10FULL | ADVERTISE_100HALF | ADVERTISE_100FULL);
+
+ if (caps & (PHY_CAP_1000_HALF | PHY_CAP_1000_FULL))
+ bmcr |= BMCR_SPEED1000;
+ else if (caps & (PHY_CAP_100_HALF | PHY_CAP_100_FULL))
+ bmcr |= BMCR_SPEED100;
+
+ if (caps & (PHY_CAP_10_FULL | PHY_CAP_100_FULL | PHY_CAP_1000_FULL))
+ bmcr |= BMCR_FULLDPLX;
+
+ if (caps & (PHY_CAP_1000_HALF | PHY_CAP_1000_FULL))
+ caps |= PHY_CAP_AUTONEG;
+
+ if (caps & PHY_CAP_AUTONEG)
+ {
+ bmcr |= BMCR_ANENABLE | BMCR_ANRESTART;
+
+ if (caps & PHY_CAP_PAUSE)
+ adv |= ADVERTISE_PAUSE_CAP;
+
+ if (caps & PHY_CAP_PAUSE_ASYM)
+ adv |= ADVERTISE_PAUSE_ASYM;
+
+ if (caps & PHY_CAP_10_HALF)
+ adv |= ADVERTISE_10HALF;
+
+ if (caps & PHY_CAP_10_FULL)
+ adv |= ADVERTISE_10FULL;
+
+ if (caps & PHY_CAP_100_HALF)
+ adv |= ADVERTISE_100HALF;
+
+ if (caps & PHY_CAP_100_FULL)
+ adv |= ADVERTISE_100FULL;
+
+ if (caps & PHY_CAP_1000_HALF)
+ ctrl1000 |= ADVERTISE_1000HALF;
+
+ if (caps & PHY_CAP_1000_FULL)
+ ctrl1000 |= ADVERTISE_1000FULL;
+
+ if (caps & PHY_CAP_REPEATER)
+ ctrl1000 |= ADVERTISE_REPEATER;
+ }
+
+ if ((ret = phy_bus_write(phy_dev, MII_ADVERTISE, adv)))
+ goto Exit;
+
+ if ((ret = phy_bus_write(phy_dev, MII_CTRL1000, ctrl1000)))
+ goto Exit;
+
+ if ((ret = phy_bus_write(phy_dev, MII_BMCR, bmcr)))
+ goto Exit;
+
+Exit:
+ return ret;
+}
+
+int mii_speed_set(phy_dev_t *phy_dev, phy_speed_t speed, phy_duplex_t duplex)
+{
+ int ret;
+ uint32_t caps;
+
+ if (speed == PHY_SPEED_UNKNOWN)
+ {
+ speed = PHY_SPEED_1000;
+ duplex = PHY_DUPLEX_FULL;
+ }
+
+ if ((ret = mii_caps_get(phy_dev, CAPS_TYPE_ADVERTISE, &caps)))
+ goto Exit;
+
+ caps &= ~(PHY_CAP_10_HALF | PHY_CAP_10_FULL |
+ PHY_CAP_100_HALF | PHY_CAP_100_FULL |
+ PHY_CAP_1000_HALF | PHY_CAP_1000_FULL);
+ caps |= PHY_CAP_AUTONEG;
+
+ switch (speed)
+ {
+ case PHY_SPEED_1000:
+ caps |= PHY_CAP_1000_HALF | ((duplex == PHY_DUPLEX_FULL) ? PHY_CAP_1000_FULL : 0);
+ case PHY_SPEED_100:
+ caps |= PHY_CAP_100_HALF | ((duplex == PHY_DUPLEX_FULL) ? PHY_CAP_100_FULL : 0);
+ case PHY_SPEED_10:
+ caps |= PHY_CAP_10_HALF | ((duplex == PHY_DUPLEX_FULL) ? PHY_CAP_10_FULL : 0);
+ break;
+ default:
+ break;
+ }
+
+ if ((ret = mii_caps_set(phy_dev, caps)))
+ goto Exit;
+
+Exit:
+ return ret;
+}
+
+int mii_init(phy_dev_t *phy_dev)
+{
+ int ret;
+ phy_speed_t speed = PHY_SPEED_100;
+ phy_duplex_t duplex = PHY_DUPLEX_FULL;
+
+ /* Reset phy */
+ if ((ret = phy_bus_write(phy_dev, MII_BMCR, BMCR_RESET)))
+ goto Exit;
+
+ if ((ret = mii_caps_set(phy_dev, PHY_CAP_AUTONEG | PHY_CAP_PAUSE | PHY_CAP_REPEATER)))
+ goto Exit;
+
+ /* Set supported speed */
+ if ((phy_dev->mii_type != PHY_MII_TYPE_MII) && (phy_dev->mii_type != PHY_MII_TYPE_TMII))
+ speed = PHY_SPEED_1000;
+
+ ret = mii_speed_set(phy_dev, speed, duplex);
+
+Exit:
+ return ret;
+}
+
+int mii_isolate_phy(phy_dev_t *phy, int isolate)
+{
+ uint16_t val;
+ int ret;
+ int enabled;
+
+ ret = phy_dev_power_get(phy, &enabled);
+ if (!enabled)
+ return -1;
+
+ ret += phy_bus_read(phy, MII_BMCR, &val);
+ if (isolate) {
+ val |= BMCR_ISOLATE;
+ } else {
+ val &= ~BMCR_ISOLATE;
+ }
+ ret += phy_bus_write(phy, MII_BMCR, val);
+ return ret;
+}
+