11bd4fe43Sopenharmony_ci/*
21bd4fe43Sopenharmony_ci * Copyright (c) 2021 HiSilicon (Shanghai) Technologies CO., LIMITED.
31bd4fe43Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
41bd4fe43Sopenharmony_ci * you may not use this file except in compliance with the License.
51bd4fe43Sopenharmony_ci * You may obtain a copy of the License at
61bd4fe43Sopenharmony_ci *
71bd4fe43Sopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
81bd4fe43Sopenharmony_ci *
91bd4fe43Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software
101bd4fe43Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
111bd4fe43Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
121bd4fe43Sopenharmony_ci * See the License for the specific language governing permissions and
131bd4fe43Sopenharmony_ci * limitations under the License.
141bd4fe43Sopenharmony_ci */
151bd4fe43Sopenharmony_ci
161bd4fe43Sopenharmony_ci#include "eth_phy.h"
171bd4fe43Sopenharmony_ci#include "linux/kernel.h"
181bd4fe43Sopenharmony_ci#include "hieth.h"
191bd4fe43Sopenharmony_ci#include "mdio.h"
201bd4fe43Sopenharmony_ci#include "stdio.h"
211bd4fe43Sopenharmony_ci
221bd4fe43Sopenharmony_ci#define WAIT_PHY_AUTO_NEG_TIMES 25
231bd4fe43Sopenharmony_ci
241bd4fe43Sopenharmony_cibool HiethGetPhyStat(struct HiethNetdevLocal *ld, EthPhyAccess *phyAccess, uint32_t *state)
251bd4fe43Sopenharmony_ci{
261bd4fe43Sopenharmony_ci    int32_t phyState;
271bd4fe43Sopenharmony_ci    int32_t i;
281bd4fe43Sopenharmony_ci
291bd4fe43Sopenharmony_ci    *state = 0;
301bd4fe43Sopenharmony_ci    phyState = HiethMdioRead(ld, phyAccess->phyAddr, PHY_BMSR);
311bd4fe43Sopenharmony_ci    if (phyState < 0) {
321bd4fe43Sopenharmony_ci        return false;
331bd4fe43Sopenharmony_ci    }
341bd4fe43Sopenharmony_ci    if (!((uint32_t)phyState & BMSR_AN_COMPLETE)) {
351bd4fe43Sopenharmony_ci        HDF_LOGE("waiting for auto-negotiation completed!");
361bd4fe43Sopenharmony_ci        for (i = 0; i < WAIT_PHY_AUTO_NEG_TIMES; i++) {
371bd4fe43Sopenharmony_ci            phyState = HiethMdioRead(ld, phyAccess->phyAddr, PHY_BMSR);
381bd4fe43Sopenharmony_ci            if ((phyState >= 0) && ((uint32_t)phyState & BMSR_AN_COMPLETE)) {
391bd4fe43Sopenharmony_ci                break;
401bd4fe43Sopenharmony_ci            }
411bd4fe43Sopenharmony_ci            msleep(10);
421bd4fe43Sopenharmony_ci        }
431bd4fe43Sopenharmony_ci    }
441bd4fe43Sopenharmony_ci    if ((uint32_t)phyState & BMSR_AN_COMPLETE) {
451bd4fe43Sopenharmony_ci        if ((uint32_t)phyState & BMSR_LINK) {
461bd4fe43Sopenharmony_ci            *state |= ETH_PHY_STAT_LINK;
471bd4fe43Sopenharmony_ci        }
481bd4fe43Sopenharmony_ci        return true;
491bd4fe43Sopenharmony_ci    }
501bd4fe43Sopenharmony_ci    return false;
511bd4fe43Sopenharmony_ci}
521bd4fe43Sopenharmony_ci
531bd4fe43Sopenharmony_ciint32_t MiiphyLink(struct HiethNetdevLocal *ld, EthPhyAccess *phyAccess)
541bd4fe43Sopenharmony_ci{
551bd4fe43Sopenharmony_ci    int32_t reg;
561bd4fe43Sopenharmony_ci
571bd4fe43Sopenharmony_ci    reg = HiethMdioRead(ld, phyAccess->phyAddr, PHY_BMSR);
581bd4fe43Sopenharmony_ci    if (reg < 0) {
591bd4fe43Sopenharmony_ci        HDF_LOGE("PHY_BMSR read failed, assuming no link");
601bd4fe43Sopenharmony_ci        return HDF_SUCCESS;
611bd4fe43Sopenharmony_ci    }
621bd4fe43Sopenharmony_ci
631bd4fe43Sopenharmony_ci    /* Determine if a link is active */
641bd4fe43Sopenharmony_ci    if (((uint32_t)reg & BMSR_LINK) != 0) {
651bd4fe43Sopenharmony_ci        return HDF_FAILURE;
661bd4fe43Sopenharmony_ci    } else {
671bd4fe43Sopenharmony_ci        return HDF_SUCCESS;
681bd4fe43Sopenharmony_ci    }
691bd4fe43Sopenharmony_ci}
701bd4fe43Sopenharmony_ci
711bd4fe43Sopenharmony_ci/*****************************************************************************
721bd4fe43Sopenharmony_ci *
731bd4fe43Sopenharmony_ci * Return 1 if PHY supports 1000BASE-X, 0 if PHY supports 10BASE-T/100BASE-TX/
741bd4fe43Sopenharmony_ci * 1000BASE-T, or on error.
751bd4fe43Sopenharmony_ci */
761bd4fe43Sopenharmony_cistatic int32_t MiiphyIs1000baseX(struct HiethNetdevLocal *ld, EthPhyAccess *phyAccess)
771bd4fe43Sopenharmony_ci{
781bd4fe43Sopenharmony_ci    int32_t reg;
791bd4fe43Sopenharmony_ci
801bd4fe43Sopenharmony_ci    reg = HiethMdioRead(ld, phyAccess->phyAddr, PHY_EXSR);
811bd4fe43Sopenharmony_ci    if (reg < 0) {
821bd4fe43Sopenharmony_ci        HDF_LOGE("PHY_EXSR read failed, assume no 1000BASE-X");
831bd4fe43Sopenharmony_ci        return HDF_SUCCESS;
841bd4fe43Sopenharmony_ci    }
851bd4fe43Sopenharmony_ci    return ((uint32_t)reg & (EXSR_1000XF | EXSR_1000XH)) != 0;
861bd4fe43Sopenharmony_ci}
871bd4fe43Sopenharmony_ci
881bd4fe43Sopenharmony_ci/*****************************************************************************
891bd4fe43Sopenharmony_ci *
901bd4fe43Sopenharmony_ci * Determine the ethernet speed (10/100/1000).  Return 10 on error.
911bd4fe43Sopenharmony_ci */
921bd4fe43Sopenharmony_ciint32_t MiiphySpeed(struct HiethNetdevLocal *ld, EthPhyAccess *phyAccess)
931bd4fe43Sopenharmony_ci{
941bd4fe43Sopenharmony_ci    int32_t bmcr, anlpar;
951bd4fe43Sopenharmony_ci    int32_t btsr, val;
961bd4fe43Sopenharmony_ci
971bd4fe43Sopenharmony_ci    val = HiethMdioRead(ld, phyAccess->phyAddr, PHY_BMSR);
981bd4fe43Sopenharmony_ci    if (val < 0) {
991bd4fe43Sopenharmony_ci        HDF_LOGE("PHY 1000BT status[read PHY_BMSR]\n");
1001bd4fe43Sopenharmony_ci        return PHY_SPEED_10;
1011bd4fe43Sopenharmony_ci    }
1021bd4fe43Sopenharmony_ci
1031bd4fe43Sopenharmony_ci    if ((uint32_t)val & BMSR_ESTATEN) {
1041bd4fe43Sopenharmony_ci        if (MiiphyIs1000baseX(ld, phyAccess)) {
1051bd4fe43Sopenharmony_ci            return PHY_SPEED_1000;
1061bd4fe43Sopenharmony_ci        }
1071bd4fe43Sopenharmony_ci        btsr = HiethMdioRead(ld, phyAccess->phyAddr, PHY_1000BTSR);
1081bd4fe43Sopenharmony_ci        if (btsr < 0) {
1091bd4fe43Sopenharmony_ci            HDF_LOGE("PHY 1000BT status[read PHY_1000BTSR]\n");
1101bd4fe43Sopenharmony_ci            return PHY_SPEED_10;
1111bd4fe43Sopenharmony_ci        }
1121bd4fe43Sopenharmony_ci        if (btsr != 0xFFFF && ((uint32_t)btsr & (PHY_1000BTSR_1000FD | PHY_1000BTSR_1000HD))) {
1131bd4fe43Sopenharmony_ci            return PHY_SPEED_1000;
1141bd4fe43Sopenharmony_ci        }
1151bd4fe43Sopenharmony_ci    }
1161bd4fe43Sopenharmony_ci
1171bd4fe43Sopenharmony_ci    /* Check Basic Management Control Register first. */
1181bd4fe43Sopenharmony_ci    bmcr = HiethMdioRead(ld, phyAccess->phyAddr, PHY_BMCR);
1191bd4fe43Sopenharmony_ci    if (bmcr < 0) {
1201bd4fe43Sopenharmony_ci        HDF_LOGE("PHY speed read failed[read PHY_BMCR]");
1211bd4fe43Sopenharmony_ci        return PHY_SPEED_10;
1221bd4fe43Sopenharmony_ci    }
1231bd4fe43Sopenharmony_ci
1241bd4fe43Sopenharmony_ci    if ((uint32_t)bmcr & BMCR_AN_ENABLE) {
1251bd4fe43Sopenharmony_ci        anlpar = HiethMdioRead(ld, phyAccess->phyAddr, PHY_ANLPAR);
1261bd4fe43Sopenharmony_ci        if (anlpar < 0) {
1271bd4fe43Sopenharmony_ci            HDF_LOGE("PHY AN speed failed[anlpar]");
1281bd4fe43Sopenharmony_ci            return PHY_SPEED_10;
1291bd4fe43Sopenharmony_ci        }
1301bd4fe43Sopenharmony_ci        return ((uint32_t)anlpar & ANLPAR_100) ? PHY_SPEED_100 : PHY_SPEED_10;
1311bd4fe43Sopenharmony_ci    }
1321bd4fe43Sopenharmony_ci    return ((uint32_t)bmcr & BMCR_SPEED100) ? PHY_SPEED_100 : PHY_SPEED_10;
1331bd4fe43Sopenharmony_ci}
1341bd4fe43Sopenharmony_ci
1351bd4fe43Sopenharmony_ci/*
1361bd4fe43Sopenharmony_ci * Determine full/half duplex.  Return half on error.
1371bd4fe43Sopenharmony_ci */
1381bd4fe43Sopenharmony_ciint32_t MiiphyDuplex(struct HiethNetdevLocal *ld, EthPhyAccess *phyAccess)
1391bd4fe43Sopenharmony_ci{
1401bd4fe43Sopenharmony_ci    int32_t bmcr, anlpar, val;
1411bd4fe43Sopenharmony_ci    val = HiethMdioRead(ld, phyAccess->phyAddr, PHY_BMSR);
1421bd4fe43Sopenharmony_ci    if (val < 0) {
1431bd4fe43Sopenharmony_ci        HDF_LOGE("PHY duplex read failed");
1441bd4fe43Sopenharmony_ci        return PHY_DUPLEX_HALF;
1451bd4fe43Sopenharmony_ci    }
1461bd4fe43Sopenharmony_ci
1471bd4fe43Sopenharmony_ci    if ((uint32_t)val & BMSR_ESTATEN) {
1481bd4fe43Sopenharmony_ci        int32_t btsr;
1491bd4fe43Sopenharmony_ci        /* Check for 1000BASE-X */
1501bd4fe43Sopenharmony_ci        if (MiiphyIs1000baseX(ld, phyAccess)) {
1511bd4fe43Sopenharmony_ci            /* 1000BASE-X */
1521bd4fe43Sopenharmony_ci            anlpar = HiethMdioRead(ld, phyAccess->phyAddr, PHY_ANLPAR);
1531bd4fe43Sopenharmony_ci            if (anlpar < 0) {
1541bd4fe43Sopenharmony_ci                HDF_LOGE("1000BASE-X PHY AN duplex failed");
1551bd4fe43Sopenharmony_ci                return PHY_DUPLEX_HALF;
1561bd4fe43Sopenharmony_ci            }
1571bd4fe43Sopenharmony_ci        }
1581bd4fe43Sopenharmony_ci        /* No 1000BASE-X, so assume 1000BASE-T/1000BASE-TX 10BASE-T register set */
1591bd4fe43Sopenharmony_ci        /* Check for 1000BASE-T. */
1601bd4fe43Sopenharmony_ci        btsr = HiethMdioRead(ld, phyAccess->phyAddr, PHY_1000BTSR);
1611bd4fe43Sopenharmony_ci        if (btsr < 0) {
1621bd4fe43Sopenharmony_ci            HDF_LOGE("PHY 1000BT status");
1631bd4fe43Sopenharmony_ci            return PHY_DUPLEX_HALF;
1641bd4fe43Sopenharmony_ci        }
1651bd4fe43Sopenharmony_ci
1661bd4fe43Sopenharmony_ci        if (btsr != 0xFFFF) {
1671bd4fe43Sopenharmony_ci            if ((uint32_t)btsr & PHY_1000BTSR_1000FD) {
1681bd4fe43Sopenharmony_ci                return PHY_DUPLEX_FULL;
1691bd4fe43Sopenharmony_ci            } else if ((uint32_t)btsr & PHY_1000BTSR_1000HD) {
1701bd4fe43Sopenharmony_ci                return PHY_DUPLEX_HALF;
1711bd4fe43Sopenharmony_ci            }
1721bd4fe43Sopenharmony_ci        }
1731bd4fe43Sopenharmony_ci    }
1741bd4fe43Sopenharmony_ci    /* Check Basic Management Control Register first. */
1751bd4fe43Sopenharmony_ci    bmcr = HiethMdioRead(ld, phyAccess->phyAddr, PHY_BMCR);
1761bd4fe43Sopenharmony_ci    if (bmcr < 0) {
1771bd4fe43Sopenharmony_ci        HDF_LOGE("PHY duplex");
1781bd4fe43Sopenharmony_ci        return PHY_DUPLEX_HALF;
1791bd4fe43Sopenharmony_ci    }
1801bd4fe43Sopenharmony_ci    /* Check if auto-negotiation is on. */
1811bd4fe43Sopenharmony_ci    if ((uint32_t)bmcr & BMCR_AN_ENABLE) {
1821bd4fe43Sopenharmony_ci        /* Get auto-negotiation results. */
1831bd4fe43Sopenharmony_ci        anlpar = HiethMdioRead(ld, phyAccess->phyAddr, PHY_ANLPAR);
1841bd4fe43Sopenharmony_ci        if (anlpar < 0) {
1851bd4fe43Sopenharmony_ci            HDF_LOGE("PHY AN duplex");
1861bd4fe43Sopenharmony_ci            return PHY_DUPLEX_HALF;
1871bd4fe43Sopenharmony_ci        }
1881bd4fe43Sopenharmony_ci        return ((uint32_t)anlpar & (ANLPAR_10FD | ANLPAR_TXFD)) ? PHY_DUPLEX_FULL : PHY_DUPLEX_HALF;
1891bd4fe43Sopenharmony_ci    }
1901bd4fe43Sopenharmony_ci    /* Get speed from basic control settings. */
1911bd4fe43Sopenharmony_ci    return ((uint32_t)bmcr & BMCR_FULL_DUPLEX) ? PHY_DUPLEX_FULL : PHY_DUPLEX_HALF;
1921bd4fe43Sopenharmony_ci}
193