/*
 *  Copyright (C) 2024 Texas Instruments Incorporated
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *
 *    Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 *    Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the
 *    distribution.
 *
 *    Neither the name of Texas Instruments Incorporated nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/* ========================================================================== */
/*                             Include Files                                  */
/* ========================================================================== */

#include <stddef.h>
#include <board/ethphy/icss_emac/ethphy_dp83822.h>
#include <drivers/mdio.h>

/* ========================================================================== */
/*                           Macros & Typedefs                                */
/* ========================================================================== */

/* -------------------------------------------------------------------------- */
/*  Register Addresses                                                         */
/* -------------------------------------------------------------------------- */

/** \brief Basic Mode Control Register (IEEE standard) */
#define ETHPHY_DP83822_BMCR_REG                                 (0x0000U)

/** \brief Basic Mode Status Register (IEEE standard) */
#define ETHPHY_DP83822_BMSR_REG                                 (0x0001U)

/** \brief PHY Identifier Register 1 (IEEE standard) */
#define ETHPHY_DP83822_PHYIDR1_REG                              (0x0002U)

/** \brief Auto-Negotiation Expansion Register (IEEE standard) */
#define ETHPHY_DP83822_ANER_REG                                 (0x0006U)

/** \brief Control Register 1 (DP83822-specific) */
#define ETHPHY_DP83822_CR1_REG                                  (0x0009U)

/** \brief Control Register 2 (DP83822-specific) */
#define ETHPHY_DP83822_CR2_REG                                  (0x000AU)

/** \brief Control Register 3 (DP83822-specific) */
#define ETHPHY_DP83822_CR3_REG                                  (0x000BU)

/** \brief PHY Status Register (DP83822-specific) */
#define ETHPHY_DP83822_PHYSTS_REG                               (0x0010U)

/** \brief RMII and Status Control Register (DP83822-specific) */
#define ETHPHY_DP83822_RCSR_REG                                 (0x0017U)

/** \brief LED Control Register (DP83822-specific) */
#define ETHPHY_DP83822_LEDCR_REG                                (0x0018U)

/** \brief PHY Control Register (DP83822-specific) */
#define ETHPHY_DP83822_PHYCR_REG                                (0x0019U)

/** \brief Reset Control Register (DP83822-specific) */
#define ETHPHY_DP83822_RCR_REG                                  (0x001FU)

/* -------------------------------------------------------------------------- */
/*  BMCR Register Bit Masks                                                    */
/* -------------------------------------------------------------------------- */

/** \brief BMCR[11]: IEEE Power Down enable */
#define ETHPHY_DP83822_BMCR_IEEE_POWER_DOWN_ENABLE_MASK         (0x0800U)

/** \brief BMCR[12]: Auto-Negotiate Enable */
#define ETHPHY_DP83822_BMCR_AUTO_NEGOTIATE_ENABLE_MASK          (0x1000U)

/** \brief BMCR[13]: Speed Select LSB (1 = 100 Mbps) */
#define ETHPHY_DP83822_BMCR_SPEED_SEL_MASK                      (0x2000U)

/** \brief BMCR[8]: Duplex Mode (1 = Full Duplex) */
#define ETHPHY_DP83822_BMCR_DUPLEX_SEL_MASK                     (0x0100U)

/* -------------------------------------------------------------------------- */
/*  BMSR Register Bit Masks                                                    */
/* -------------------------------------------------------------------------- */

/** \brief BMSR[5]: Auto-Negotiation Complete */
#define ETHPHY_DP83822_BMSR_AUTONEG_COMP_MASK                   (0x0020U)

/* -------------------------------------------------------------------------- */
/*  ANER Register Bit Masks                                                    */
/* -------------------------------------------------------------------------- */

/** \brief ANER[0]: Link Partner Auto-Negotiation Able */
#define ETHPHY_DP83822_ANER_LP_AUTONEG_ABLE_MASK                (0x0001U)

/* -------------------------------------------------------------------------- */
/*  CR1 Register Bit Masks                                                     */
/* -------------------------------------------------------------------------- */

/** \brief CR1[1]: Fast RX_DV Detection Enable */
#define ETHPHY_DP83822_CR1_FAST_RXDV_ENABLE_MASK                (0x0002U)

/** \brief CR1[5]: Robust Auto-MDIX Enable */
#define ETHPHY_DP83822_CR1_ROBUST_AUTOMDIX_MASK                 (0x0020U)

/* -------------------------------------------------------------------------- */
/*  CR2 Register Bit Masks                                                     */
/* -------------------------------------------------------------------------- */

/** \brief CR2[2]: Enhanced IPG Detection Enable (RX Error Idle) */
#define ETHPHY_DP83822_CR2_ENHANCED_IPG_DET_ENABLE_MASK         (0x0004U)

/** \brief CR2[5]: Extended Full-Duplex Ability Enable */
#define ETHPHY_DP83822_CR2_EXT_FD_ENABLE_MASK                   (0x0020U)

/** \brief CR2[1]: ODD Nibble Detection DISABLE (Table 8-24, reg 0x000A, bit 1).
 *  DATASHEET CORRECTION: This bit is "Odd-Nibble Detection Disable".
 *  1 = DISABLE detection, 0 = ENABLE detection (default).
 *  enableOddNibbleDetection() must CLEAR this bit to enable the feature. */
#define ETHPHY_DP83822_CR2_ODD_NIBBLE_DETECTION_DISABLE_MASK    (0x0002U)

/** \brief CR3 Fast Link Down mode mask covering all 5 options (Table 8-25, reg 0x000B).
 *  Datasheet: "The Fast Link Down function is an OR of all 5 options
 *  (bit[10] and bits[3:0]), the designer can enable any combination."
 *  bit[3]=RX Error Count, bit[2]=MLT3 Error Count, bit[1]=Low SNR,
 *  bit[0]=Signal/Energy Loss, bit[10]=Descrambler Fast Link Drop. */
#define ETHPHY_DP83822_CR3_FAST_LINK_DOWN_MODES_MASK            (0x040FU)

/* -------------------------------------------------------------------------- */
/*  CR3 Register Bit Masks                                                     */
/* -------------------------------------------------------------------------- */

/** \brief CR3[3:0] and CR3[10]: Fast Link Down detection mode bits */
#define ETHPHY_DP83822_CR3_FAST_LINK_DOWN_MODES_MASK            (0x000FU)

/* -------------------------------------------------------------------------- */
/*  PHYSTS Register Bit Masks                                                  */
/* -------------------------------------------------------------------------- */

/** \brief PHYSTS[1]: Speed (0 = 100 Mbps, 1 = 10 Mbps) */
#define ETHPHY_DP83822_PHYSTS_SPEED_MASK                        (0x0002U)

/** \brief PHYSTS[2]: Duplex (1 = Full Duplex) */
#define ETHPHY_DP83822_PHYSTS_DUPLEX_MASK                       (0x0004U)

/* -------------------------------------------------------------------------- */
/*  LEDCR Register Bit Masks and Shifts                                        */
/* -------------------------------------------------------------------------- */

/** \brief LEDCR[10:9]: LED blink rate field mask (before shift) */
#define ETHPHY_DP83822_LEDCR_LEDS_BLINK_RATE_MASK               (0x0600U)

/** \brief Shift for LEDCR blink rate field */
#define ETHPHY_DP83822_LEDCR_LEDS_BLINK_RATE_SHIFT              (0x0009U)

/* -------------------------------------------------------------------------- */
/*  PHYCR Register Bit Masks                                                   */
/* -------------------------------------------------------------------------- */

/** \brief PHYCR[15]: Auto-MDIX Enable */
#define ETHPHY_DP83822_PHYCR_AUTOMDIX_ENABLE_MASK               (0x8000U)

/* -------------------------------------------------------------------------- */
/*  RCR Register Bit Masks                                                     */
/* -------------------------------------------------------------------------- */

/** \brief RCR[15]: Software Hard Reset (resets all registers and circuitry) */
#define ETHPHY_DP83822_RCR_SW_HARD_RESET_MASK                   (0x8000U)

/** \brief RCR[14]: Software Restart (resets circuitry only, registers intact) */
#define ETHPHY_DP83822_RCR_SW_RESTART_MASK                      (0x4000U)

/* -------------------------------------------------------------------------- */
/*  PHY Identifier                                                             */
/* -------------------------------------------------------------------------- */

/**
 * \brief Expected value of PHYIDR1 register for DP83822.
 *
 * PHYIDR1 = OUI[3:18] = 0x2000 (OUI = 0x080028).
 * Used by ETHPHY_CMD_VERIFY_IDENTIFIER_REGISTER to confirm PHY identity.
 */
/**
 * \brief Expected PHYIDR1 value for DP83822 identity verification.
 *
 * From datasheet SNLS505G, Table 8-16 (reg 0x0002):
 *   bits[15:0] = OUI[21:6] = 0x2000
 * PHYIDR2 (reg 0x0003) = 0xA240 (OUI[5:0]=0x28, Model=0x24, Rev=0x0)
 * Combined 32-bit ID = 0x2000A240 (mask 0xFFFFFFF0 for all revisions).
 * PHYIDR1 alone (0x2000) is shared with DP83826E; use both registers for
 * definitive identification.
 */
#define ETHPHY_DP83822_PHYIDR1_REG_VALUE                        (0x2000U)

/* ========================================================================== */
/*                          Function Declarations                             */
/* ========================================================================== */

int32_t ETHPHY_DP83822_open(ETHPHY_Config *config, const ETHPHY_Params *params);

int32_t ETHPHY_DP83822_command(ETHPHY_Config *config,
                               uint32_t      command,
                               void         *data,
                               uint32_t      dataSize);

static int32_t ETHPHY_DP83822_softRestart(ETHPHY_Attrs *attrs);

static int32_t ETHPHY_DP83822_enableAutoMdix(ETHPHY_Attrs *attrs);

static int32_t ETHPHY_DP83822_verifyIdentifierRegister(ETHPHY_Attrs *attrs);

static int32_t ETHPHY_DP83822_enableFastLinkDownDetection(ETHPHY_Attrs *attrs,
                                                          void         *data,
                                                          uint32_t      dataSize);

static int32_t ETHPHY_DP83822_configureLedBlinkRate(ETHPHY_Attrs *attrs,
                                                    void         *data,
                                                    uint32_t      dataSize);

static int32_t ETHPHY_DP83822_enableExtendedFdAbility(ETHPHY_Attrs *attrs);

static int32_t ETHPHY_DP83822_enableOddNibbleDetection(ETHPHY_Attrs *attrs);

static int32_t ETHPHY_DP83822_enableEnhancedIpgDetection(ETHPHY_Attrs *attrs);

static int32_t ETHPHY_DP83822_enableFastRxDvDetection(ETHPHY_Attrs *attrs);

static int32_t ETHPHY_DP83822_getSpeedDuplex(ETHPHY_Attrs *attrs,
                                             void         *data,
                                             uint32_t      dataSize);

static int32_t ETHPHY_DP83822_setSpeedDuplex(ETHPHY_Attrs *attrs,
                                             void         *data,
                                             uint32_t      dataSize);

static int32_t ETHPHY_DP83822_getAutonegCompleteStatus(ETHPHY_Attrs *attrs,
                                                       void         *data,
                                                       uint32_t      dataSize);

static int32_t ETHPHY_DP83822_getLinkPartnerAutonegAbility(ETHPHY_Attrs *attrs,
                                                           void         *data,
                                                           uint32_t      dataSize);

static int32_t ETHPHY_DP83822_enableIeeePowerDownMode(ETHPHY_Attrs *attrs);

static int32_t ETHPHY_DP83822_disableIeeePowerDownMode(ETHPHY_Attrs *attrs);

/* ========================================================================== */
/*                            Global Variables                                */
/* ========================================================================== */

/** \brief ETHPHY function table for the DP83822 PHY driver */
ETHPHY_Fxns gEthPhyFxns_DP83822 =
{
    .openFxn    = ETHPHY_DP83822_open,
    .closeFxn   = NULL,
    .commandFxn = ETHPHY_DP83822_command,
};

/* ========================================================================== */
/*                          Function Definitions                              */
/* ========================================================================== */

/**
 * \brief Open the DP83822 PHY driver instance.
 *
 * Validates the config and params pointers, then initialises the MDIO clock
 * for the MDIO base address specified in the PHY attributes.
 *
 * \param config    Pointer to the ETHPHY_Config for this instance.
 * \param params    Pointer to open parameters (must not be NULL).
 * \return          SystemP_SUCCESS on success, SystemP_FAILURE otherwise.
 */
int32_t ETHPHY_DP83822_open(ETHPHY_Config *config, const ETHPHY_Params *params)
{
    int32_t      status = SystemP_SUCCESS;
    ETHPHY_Attrs *attrs;

    if ((NULL == config) || (NULL == params) || (NULL == config->attrs))
    {
        status = SystemP_FAILURE;
    }
    else
    {
        attrs  = config->attrs;
        status = MDIO_initClock(attrs->mdioBaseAddress);
    }

    return status;
}

/**
 * \brief Dispatch a command to the DP83822 PHY driver.
 *
 * Supported commands:
 *  - ETHPHY_CMD_SOFT_RESTART
 *  - ETHPHY_CMD_ENABLE_AUTO_MDIX
 *  - ETHPHY_CMD_VERIFY_IDENTIFIER_REGISTER
 *  - ETHPHY_CMD_ENABLE_FAST_LINK_DOWN_DETECTION
 *  - ETHPHY_CMD_CONFIGURE_LED_BLINK_RATE
 *  - ETHPHY_CMD_ENABLE_EXTENDED_FD_ABILITY
 *  - ETHPHY_CMD_ENABLE_ODD_NIBBLE_DETECTION
 *  - ETHPHY_CMD_ENABLE_ENHANCED_IPG_DETECTION
 *  - ETHPHY_CMD_GET_LINK_STATUS
 *  - ETHPHY_CMD_GET_SPEED_AND_DUPLEX_CONFIG
 *  - ETHPHY_CMD_SET_SPEED_AND_DUPLEX_CONFIG
 *  - ETHPHY_CMD_GET_AUTONEG_COMPLETE_STATUS
 *  - ETHPHY_CMD_GET_LINK_PARTNER_AUTONEG_ABILITY
 *  - ETHPHY_CMD_ENABLE_IEEE_POWER_DOWN
 *  - ETHPHY_CMD_DISABLE_IEEE_POWER_DOWN
 *
 * Commands not applicable to the DP83822 (MII enable, 1000M advertisement
 * disable, RGMII-specific commands, LED source config) return SystemP_FAILURE.
 *
 * \param config    Pointer to the ETHPHY_Config for this instance.
 * \param command   Command identifier from board/ethphy.h.
 * \param data      Pointer to command-specific data structure (may be NULL).
 * \param dataSize  Size in bytes of the data structure.
 * \return          SystemP_SUCCESS on success, SystemP_FAILURE otherwise.
 */
int32_t ETHPHY_DP83822_command(ETHPHY_Config *config,
                               uint32_t      command,
                               void         *data,
                               uint32_t      dataSize)
{
    int32_t      status = SystemP_SUCCESS;
    ETHPHY_Attrs *attrs;

    if (NULL == config)
    {
        status = SystemP_FAILURE;
    }

    if (status == SystemP_SUCCESS)
    {
        attrs = config->attrs;

        switch (command)
        {
            case ETHPHY_CMD_ENABLE_MII:
                /* DP83822 supports MII but this driver targets RMII/ICSS-EMAC.
                 * MII mode selection is handled via the RCSR register at board
                 * init time; runtime switching is not supported here. */
                status = SystemP_FAILURE;
                break;

            case ETHPHY_CMD_SOFT_RESTART:
                status = ETHPHY_DP83822_softRestart(attrs);
                break;

            case ETHPHY_CMD_ENABLE_AUTO_MDIX:
                status = ETHPHY_DP83822_enableAutoMdix(attrs);
                break;

            case ETHPHY_CMD_VERIFY_IDENTIFIER_REGISTER:
                status = ETHPHY_DP83822_verifyIdentifierRegister(attrs);
                break;

            case ETHPHY_CMD_DISABLE_1000M_ADVERTISEMENT:
                /* DP83822 is a 10/100 Mbps PHY; 1000M is not supported */
                status = SystemP_FAILURE;
                break;

            case ETHPHY_CMD_ENABLE_FAST_LINK_DOWN_DETECTION:
                status = ETHPHY_DP83822_enableFastLinkDownDetection(attrs, data, dataSize);
                break;

            case ETHPHY_CMD_CONFIGURE_LED_SOURCE:
                /* DP83822 has a single LED_LINK pin; per-LED source
                 * configuration via extended registers is not available.
                 * Use ETHPHY_CMD_CONFIGURE_LED_BLINK_RATE instead. */
                status = SystemP_FAILURE;
                break;

            case ETHPHY_CMD_CONFIGURE_LED_BLINK_RATE:
                status = ETHPHY_DP83822_configureLedBlinkRate(attrs, data, dataSize);
                break;

            case ETHPHY_CMD_ENABLE_EXTENDED_FD_ABILITY:
                status = ETHPHY_DP83822_enableExtendedFdAbility(attrs);
                break;

            case ETHPHY_CMD_ENABLE_ODD_NIBBLE_DETECTION:
                status = ETHPHY_DP83822_enableOddNibbleDetection(attrs);
                break;

            case ETHPHY_CMD_ENABLE_ENHANCED_IPG_DETECTION:
                status = ETHPHY_DP83822_enableEnhancedIpgDetection(attrs);
                break;

            /* ETHPHY_CMD_ENABLE_FAST_RXDV_DETECTION is a DP83822-specific
             * extension not yet defined in the standard ETHPHY command enum.
             * If added to board/ethphy.h, uncomment the case below.
             * For now, fall through to default (SystemP_FAILURE). */
            /* case ETHPHY_CMD_ENABLE_FAST_RXDV_DETECTION:
                status = ETHPHY_DP83822_enableFastRxDvDetection(attrs);
                break; */

            case ETHPHY_CMD_GET_LINK_STATUS:
                status = MDIO_phyLinkStatus(attrs->mdioBaseAddress, attrs->phyAddress);
                break;

            case ETHPHY_CMD_GET_SPEED_AND_DUPLEX_CONFIG:
                status = ETHPHY_DP83822_getSpeedDuplex(attrs, data, dataSize);
                break;

            case ETHPHY_CMD_SET_SPEED_AND_DUPLEX_CONFIG:
                status = ETHPHY_DP83822_setSpeedDuplex(attrs, data, dataSize);
                break;

            case ETHPHY_CMD_ENABLE_LOW_LATENCY_10M_100M_RGMII:
                /* DP83822 does not support RGMII */
                status = SystemP_FAILURE;
                break;

            case ETHPHY_CMD_SET_RX_HALF_FULL_THRESHOLD_RGMII:
                /* DP83822 does not support RGMII */
                status = SystemP_FAILURE;
                break;

            case ETHPHY_CMD_SET_TX_HALF_FULL_THRESHOLD_RGMII:
                /* DP83822 does not support RGMII */
                status = SystemP_FAILURE;
                break;

            case ETHPHY_CMD_GET_AUTONEG_COMPLETE_STATUS:
                status = ETHPHY_DP83822_getAutonegCompleteStatus(attrs, data, dataSize);
                break;

            case ETHPHY_CMD_GET_LINK_PARTNER_AUTONEG_ABILITY:
                status = ETHPHY_DP83822_getLinkPartnerAutonegAbility(attrs, data, dataSize);
                break;

            case ETHPHY_CMD_ENABLE_IEEE_POWER_DOWN:
                status = ETHPHY_DP83822_enableIeeePowerDownMode(attrs);
                break;

            case ETHPHY_CMD_DISABLE_IEEE_POWER_DOWN:
                status = ETHPHY_DP83822_disableIeeePowerDownMode(attrs);
                break;

            default:
                status = SystemP_FAILURE;
                break;
        }
    }

    return status;
}

/**
 * \brief Issue a software restart to the DP83822.
 *
 * Sets RCR[14] (SW_RESTART). This resets all PHY circuitry but leaves
 * register contents intact. The bit self-clears when the restart completes.
 *
 * \param attrs     Pointer to PHY attributes (MDIO base address, PHY address).
 * \return          SystemP_SUCCESS on success, SystemP_FAILURE otherwise.
 */
static int32_t ETHPHY_DP83822_softRestart(ETHPHY_Attrs *attrs)
{
    int32_t  status    = SystemP_FAILURE;
    uint16_t phyRegVal = 0U;

    status = MDIO_phyRegRead(attrs->mdioBaseAddress, NULL, attrs->phyAddress,
                             ETHPHY_DP83822_RCR_REG, &phyRegVal);

    if (status == SystemP_SUCCESS)
    {
        phyRegVal |= ETHPHY_DP83822_RCR_SW_RESTART_MASK;
        status = MDIO_phyRegWrite(attrs->mdioBaseAddress, NULL, attrs->phyAddress,
                                  ETHPHY_DP83822_RCR_REG, phyRegVal);
    }

    return status;
}

/**
 * \brief Enable Auto-MDIX and Robust Auto-MDIX on the DP83822.
 *
 * Sets PHYCR[15] (AUTOMDIX_ENABLE) to enable automatic MDI/MDI-X crossover,
 * and CR1[5] (ROBUST_AUTOMDIX) to enable the robust auto-MDIX algorithm.
 *
 * \param attrs     Pointer to PHY attributes.
 * \return          SystemP_SUCCESS on success, SystemP_FAILURE otherwise.
 */
static int32_t ETHPHY_DP83822_enableAutoMdix(ETHPHY_Attrs *attrs)
{
    int32_t  status    = SystemP_FAILURE;
    uint16_t phyRegVal = 0U;

    /* Enable Auto-MDIX via PHYCR register */
    status = MDIO_phyRegRead(attrs->mdioBaseAddress, NULL, attrs->phyAddress,
                             ETHPHY_DP83822_PHYCR_REG, &phyRegVal);

    if (status == SystemP_SUCCESS)
    {
        phyRegVal |= ETHPHY_DP83822_PHYCR_AUTOMDIX_ENABLE_MASK;
        status = MDIO_phyRegWrite(attrs->mdioBaseAddress, NULL, attrs->phyAddress,
                                  ETHPHY_DP83822_PHYCR_REG, phyRegVal);
    }

    /* Enable Robust Auto-MDIX via CR1 register */
    if (status == SystemP_SUCCESS)
    {
        phyRegVal = 0U;
        status = MDIO_phyRegRead(attrs->mdioBaseAddress, NULL, attrs->phyAddress,
                                 ETHPHY_DP83822_CR1_REG, &phyRegVal);
    }

    if (status == SystemP_SUCCESS)
    {
        phyRegVal |= ETHPHY_DP83822_CR1_ROBUST_AUTOMDIX_MASK;
        status = MDIO_phyRegWrite(attrs->mdioBaseAddress, NULL, attrs->phyAddress,
                                  ETHPHY_DP83822_CR1_REG, phyRegVal);
    }

    return status;
}

/**
 * \brief Verify the DP83822 PHY identifier register.
 *
 * Reads PHYIDR1 (reg 0x02) and compares it against the expected value
 * (0x2000) to confirm that the correct PHY is present at the configured
 * MDIO address.
 *
 * \param attrs     Pointer to PHY attributes.
 * \return          SystemP_SUCCESS if PHYIDR1 matches, SystemP_FAILURE otherwise.
 */
static int32_t ETHPHY_DP83822_verifyIdentifierRegister(ETHPHY_Attrs *attrs)
{
    int32_t  status    = SystemP_FAILURE;
    uint16_t phyRegVal = 0U;

    status = MDIO_phyRegRead(attrs->mdioBaseAddress, NULL, attrs->phyAddress,
                             ETHPHY_DP83822_PHYIDR1_REG, &phyRegVal);

    if (status == SystemP_SUCCESS)
    {
        if (phyRegVal == ETHPHY_DP83822_PHYIDR1_REG_VALUE)
        {
            status = SystemP_SUCCESS;
        }
        else
        {
            status = SystemP_FAILURE;
        }
    }

    return status;
}

/**
 * \brief Enable fast link-down detection on the DP83822.
 *
 * Writes the requested mode bits into CR3[3:0]. The mode value is taken
 * from the \ref ETHPHY_DP83822_FastLinkDownDetectionConfig data structure.
 * Multiple modes may be combined with bitwise OR.
 *
 * \param attrs     Pointer to PHY attributes.
 * \param data      Pointer to \ref ETHPHY_DP83822_FastLinkDownDetectionConfig.
 * \param dataSize  Must equal sizeof(ETHPHY_DP83822_FastLinkDownDetectionConfig).
 * \return          SystemP_SUCCESS on success, SystemP_FAILURE otherwise.
 */
static int32_t ETHPHY_DP83822_enableFastLinkDownDetection(ETHPHY_Attrs *attrs,
                                                          void         *data,
                                                          uint32_t      dataSize)
{
    int32_t  status    = SystemP_SUCCESS;
    uint16_t phyRegVal = 0U;
    uint32_t mode;

    if ((data == NULL) || (dataSize != sizeof(ETHPHY_DP83822_FastLinkDownDetectionConfig)))
    {
        status = SystemP_FAILURE;
    }

    if (status == SystemP_SUCCESS)
    {
        status = MDIO_phyRegRead(attrs->mdioBaseAddress, NULL, attrs->phyAddress,
                                 ETHPHY_DP83822_CR3_REG, &phyRegVal);
    }

    if (status == SystemP_SUCCESS)
    {
        mode = ((ETHPHY_DP83822_FastLinkDownDetectionConfig *)data)->mode;

        phyRegVal &= ~(ETHPHY_DP83822_CR3_FAST_LINK_DOWN_MODES_MASK);
        phyRegVal |= (uint16_t)(mode & ETHPHY_DP83822_CR3_FAST_LINK_DOWN_MODES_MASK);

        status = MDIO_phyRegWrite(attrs->mdioBaseAddress, NULL, attrs->phyAddress,
                                  ETHPHY_DP83822_CR3_REG, phyRegVal);
    }

    return status;
}

/**
 * \brief Configure the LED blink rate on the DP83822.
 *
 * Sets LEDCR[10:9] to the requested blink rate. The rate value is taken
 * from the \ref ETHPHY_DP83822_LedBlinkRateConfig data structure.
 *
 * \param attrs     Pointer to PHY attributes.
 * \param data      Pointer to \ref ETHPHY_DP83822_LedBlinkRateConfig.
 * \param dataSize  Must equal sizeof(ETHPHY_DP83822_LedBlinkRateConfig).
 * \return          SystemP_SUCCESS on success, SystemP_FAILURE otherwise.
 */
static int32_t ETHPHY_DP83822_configureLedBlinkRate(ETHPHY_Attrs *attrs,
                                                    void         *data,
                                                    uint32_t      dataSize)
{
    int32_t  status    = SystemP_SUCCESS;
    uint16_t phyRegVal = 0U;
    uint32_t blinkRate;

    if ((data == NULL) || (dataSize != sizeof(ETHPHY_DP83822_LedBlinkRateConfig)))
    {
        status = SystemP_FAILURE;
    }

    if (status == SystemP_SUCCESS)
    {
        status = MDIO_phyRegRead(attrs->mdioBaseAddress, NULL, attrs->phyAddress,
                                 ETHPHY_DP83822_LEDCR_REG, &phyRegVal);
    }

    if (status == SystemP_SUCCESS)
    {
        blinkRate = ((ETHPHY_DP83822_LedBlinkRateConfig *)data)->rate;

        phyRegVal &= ~(ETHPHY_DP83822_LEDCR_LEDS_BLINK_RATE_MASK);
        phyRegVal |= (uint16_t)(blinkRate << ETHPHY_DP83822_LEDCR_LEDS_BLINK_RATE_SHIFT);

        status = MDIO_phyRegWrite(attrs->mdioBaseAddress, NULL, attrs->phyAddress,
                                  ETHPHY_DP83822_LEDCR_REG, phyRegVal);
    }

    return status;
}

/**
 * \brief Enable Extended Full-Duplex ability on the DP83822.
 *
 * Sets CR2[5] (EXT_FD_ENABLE) to allow full-duplex operation with
 * half-duplex link partners.
 *
 * \param attrs     Pointer to PHY attributes.
 * \return          SystemP_SUCCESS on success, SystemP_FAILURE otherwise.
 */
static int32_t ETHPHY_DP83822_enableExtendedFdAbility(ETHPHY_Attrs *attrs)
{
    int32_t  status    = SystemP_FAILURE;
    uint16_t phyRegVal = 0U;

    status = MDIO_phyRegRead(attrs->mdioBaseAddress, NULL, attrs->phyAddress,
                             ETHPHY_DP83822_CR2_REG, &phyRegVal);

    if (status == SystemP_SUCCESS)
    {
        phyRegVal |= ETHPHY_DP83822_CR2_EXT_FD_ENABLE_MASK;
        status = MDIO_phyRegWrite(attrs->mdioBaseAddress, NULL, attrs->phyAddress,
                                  ETHPHY_DP83822_CR2_REG, phyRegVal);
    }

    return status;
}

/**
 * \brief Enable ODD Nibble Detection on the DP83822.
 *
 * DATASHEET CORRECTION (Table 8-24, reg 0x000A, bit 1):
 * CR2[1] is "Odd-Nibble Detection DISABLE" — 1=disable, 0=enable.
 * This function CLEARS the bit to ENABLE odd-nibble detection.
 *
 * \param attrs     Pointer to PHY attributes.
 * \return          SystemP_SUCCESS on success, SystemP_FAILURE otherwise.
 */
static int32_t ETHPHY_DP83822_enableOddNibbleDetection(ETHPHY_Attrs *attrs)
{
    int32_t  status    = SystemP_FAILURE;
    uint16_t phyRegVal = 0U;

    status = MDIO_phyRegRead(attrs->mdioBaseAddress, NULL, attrs->phyAddress,
                             ETHPHY_DP83822_CR2_REG, &phyRegVal);

    if (status == SystemP_SUCCESS)
    {
        /* Clear the DISABLE bit to ENABLE odd-nibble detection */
        phyRegVal &= ~(ETHPHY_DP83822_CR2_ODD_NIBBLE_DETECTION_DISABLE_MASK);
        status = MDIO_phyRegWrite(attrs->mdioBaseAddress, NULL, attrs->phyAddress,
                                  ETHPHY_DP83822_CR2_REG, phyRegVal);
    }

    return status;
}

/**
 * \brief Enable Enhanced IPG Detection (RX Error Idle) on the DP83822.
 *
 * Sets CR2[2] (ENHANCED_IPG_DET_ENABLE) to enable enhanced inter-packet
 * gap detection, which improves RX error idle signalling.
 *
 * \param attrs     Pointer to PHY attributes.
 * \return          SystemP_SUCCESS on success, SystemP_FAILURE otherwise.
 */
static int32_t ETHPHY_DP83822_enableEnhancedIpgDetection(ETHPHY_Attrs *attrs)
{
    int32_t  status    = SystemP_FAILURE;
    uint16_t phyRegVal = 0U;

    status = MDIO_phyRegRead(attrs->mdioBaseAddress, NULL, attrs->phyAddress,
                             ETHPHY_DP83822_CR2_REG, &phyRegVal);

    if (status == SystemP_SUCCESS)
    {
        phyRegVal |= ETHPHY_DP83822_CR2_ENHANCED_IPG_DET_ENABLE_MASK;
        status = MDIO_phyRegWrite(attrs->mdioBaseAddress, NULL, attrs->phyAddress,
                                  ETHPHY_DP83822_CR2_REG, phyRegVal);
    }

    return status;
}

/**
 * \brief Enable Fast RX_DV Detection on the DP83822.
 *
 * Sets CR1[1] (FAST_RXDV_ENABLE) to enable fast receive data valid
 * detection, reducing latency on link establishment.
 *
 * \param attrs     Pointer to PHY attributes.
 * \return          SystemP_SUCCESS on success, SystemP_FAILURE otherwise.
 */
static int32_t ETHPHY_DP83822_enableFastRxDvDetection(ETHPHY_Attrs *attrs)
{
    int32_t  status    = SystemP_FAILURE;
    uint16_t phyRegVal = 0U;

    status = MDIO_phyRegRead(attrs->mdioBaseAddress, NULL, attrs->phyAddress,
                             ETHPHY_DP83822_CR1_REG, &phyRegVal);

    if (status == SystemP_SUCCESS)
    {
        phyRegVal |= ETHPHY_DP83822_CR1_FAST_RXDV_ENABLE_MASK;
        status = MDIO_phyRegWrite(attrs->mdioBaseAddress, NULL, attrs->phyAddress,
                                  ETHPHY_DP83822_CR1_REG, phyRegVal);
    }

    return status;
}

/**
 * \brief Read the current link speed and duplex mode from the DP83822.
 *
 * Reads PHYSTS[2:1]:
 *   - PHYSTS[1] = 0: 100 Mbps; PHYSTS[1] = 1: 10 Mbps
 *   - PHYSTS[2] = 1: Full Duplex; PHYSTS[2] = 0: Half Duplex
 *
 * \param attrs     Pointer to PHY attributes.
 * \param data      Pointer to \ref ETHPHY_SpeedDuplexConfig.
 * \param dataSize  Must equal sizeof(ETHPHY_SpeedDuplexConfig).
 * \return          SystemP_SUCCESS on success, SystemP_FAILURE otherwise.
 */
static int32_t ETHPHY_DP83822_getSpeedDuplex(ETHPHY_Attrs *attrs,
                                             void         *data,
                                             uint32_t      dataSize)
{
    uint16_t              phyRegVal    = 0U;
    int32_t               status       = SystemP_SUCCESS;
    ETHPHY_SpeedDuplexConfig *pSpeedDuplex = NULL;

    if ((data == NULL) || (dataSize != sizeof(ETHPHY_SpeedDuplexConfig)))
    {
        status = SystemP_FAILURE;
    }

    if (status == SystemP_SUCCESS)
    {
        status = MDIO_phyRegRead(attrs->mdioBaseAddress, NULL, attrs->phyAddress,
                                 ETHPHY_DP83822_PHYSTS_REG, &phyRegVal);
        pSpeedDuplex = (ETHPHY_SpeedDuplexConfig *)data;
    }

    if (status == SystemP_SUCCESS)
    {
        /* PHYSTS[1] = 1 means 10 Mbps; PHYSTS[1] = 0 means 100 Mbps */
        if (phyRegVal & ETHPHY_DP83822_PHYSTS_SPEED_MASK)
        {
            /* 10 Mbps */
            if (phyRegVal & ETHPHY_DP83822_PHYSTS_DUPLEX_MASK)
            {
                pSpeedDuplex->config = ETHPHY_SPEED_DUPLEX_CONFIG_10FD;
            }
            else
            {
                pSpeedDuplex->config = ETHPHY_SPEED_DUPLEX_CONFIG_10HD;
            }
        }
        else
        {
            /* 100 Mbps */
            if (phyRegVal & ETHPHY_DP83822_PHYSTS_DUPLEX_MASK)
            {
                pSpeedDuplex->config = ETHPHY_SPEED_DUPLEX_CONFIG_100FD;
            }
            else
            {
                pSpeedDuplex->config = ETHPHY_SPEED_DUPLEX_CONFIG_100HD;
            }
        }
    }

    return status;
}

/**
 * \brief Set the link speed and duplex mode on the DP83822.
 *
 * Configures BMCR for the requested speed/duplex combination.
 * Supported configurations:
 *   - ETHPHY_SPEED_DUPLEX_CONFIG_AUTONEG  (enables auto-negotiation)
 *   - ETHPHY_SPEED_DUPLEX_CONFIG_10FD
 *   - ETHPHY_SPEED_DUPLEX_CONFIG_10HD
 *   - ETHPHY_SPEED_DUPLEX_CONFIG_100FD
 *   - ETHPHY_SPEED_DUPLEX_CONFIG_100HD
 *
 * Note: 1000 Mbps configurations are not supported by the DP83822.
 *
 * \param attrs     Pointer to PHY attributes.
 * \param data      Pointer to \ref ETHPHY_SpeedDuplexConfig.
 * \param dataSize  Must equal sizeof(ETHPHY_SpeedDuplexConfig).
 * \return          SystemP_SUCCESS on success, SystemP_FAILURE otherwise.
 */
static int32_t ETHPHY_DP83822_setSpeedDuplex(ETHPHY_Attrs *attrs,
                                             void         *data,
                                             uint32_t      dataSize)
{
    uint16_t phyRegVal = 0U;
    int32_t  status    = SystemP_SUCCESS;
    uint32_t config;

    if ((data == NULL) || (dataSize != sizeof(ETHPHY_SpeedDuplexConfig)))
    {
        status = SystemP_FAILURE;
    }

    if (status == SystemP_SUCCESS)
    {
        status = MDIO_phyRegRead(attrs->mdioBaseAddress, NULL, attrs->phyAddress,
                                 ETHPHY_DP83822_BMCR_REG, &phyRegVal);
    }

    if (status == SystemP_SUCCESS)
    {
        config = ((ETHPHY_SpeedDuplexConfig *)data)->config;

        switch (config)
        {
            case ETHPHY_SPEED_DUPLEX_CONFIG_AUTONEG:
                phyRegVal |= ETHPHY_DP83822_BMCR_AUTO_NEGOTIATE_ENABLE_MASK;
                break;

            case ETHPHY_SPEED_DUPLEX_CONFIG_10FD:
                phyRegVal &= ~(ETHPHY_DP83822_BMCR_AUTO_NEGOTIATE_ENABLE_MASK);
                phyRegVal &= ~(ETHPHY_DP83822_BMCR_SPEED_SEL_MASK);
                phyRegVal |= ETHPHY_DP83822_BMCR_DUPLEX_SEL_MASK;
                break;

            case ETHPHY_SPEED_DUPLEX_CONFIG_100FD:
                phyRegVal &= ~(ETHPHY_DP83822_BMCR_AUTO_NEGOTIATE_ENABLE_MASK);
                phyRegVal |= ETHPHY_DP83822_BMCR_SPEED_SEL_MASK;
                phyRegVal |= ETHPHY_DP83822_BMCR_DUPLEX_SEL_MASK;
                break;

            case ETHPHY_SPEED_DUPLEX_CONFIG_10HD:
                phyRegVal &= ~(ETHPHY_DP83822_BMCR_AUTO_NEGOTIATE_ENABLE_MASK);
                phyRegVal &= ~(ETHPHY_DP83822_BMCR_SPEED_SEL_MASK);
                phyRegVal &= ~(ETHPHY_DP83822_BMCR_DUPLEX_SEL_MASK);
                break;

            case ETHPHY_SPEED_DUPLEX_CONFIG_100HD:
                phyRegVal &= ~(ETHPHY_DP83822_BMCR_AUTO_NEGOTIATE_ENABLE_MASK);
                phyRegVal |= ETHPHY_DP83822_BMCR_SPEED_SEL_MASK;
                phyRegVal &= ~(ETHPHY_DP83822_BMCR_DUPLEX_SEL_MASK);
                break;

            default:
                /* 1000 Mbps and other unsupported configurations */
                status = SystemP_FAILURE;
                break;
        }
    }

    if (status == SystemP_SUCCESS)
    {
        status = MDIO_phyRegWrite(attrs->mdioBaseAddress, NULL, attrs->phyAddress,
                                  ETHPHY_DP83822_BMCR_REG, phyRegVal);
    }

    return status;
}

/**
 * \brief Get the auto-negotiation complete status from the DP83822.
 *
 * Reads BMSR[5] (Auto-Negotiation Complete). Returns 1 in *data if
 * auto-negotiation has completed, 0 otherwise.
 *
 * \param attrs     Pointer to PHY attributes.
 * \param data      Pointer to uint8_t to receive the status (1 = complete).
 * \param dataSize  Must equal sizeof(uint8_t).
 * \return          SystemP_SUCCESS on success, SystemP_FAILURE otherwise.
 */
static int32_t ETHPHY_DP83822_getAutonegCompleteStatus(ETHPHY_Attrs *attrs,
                                                       void         *data,
                                                       uint32_t      dataSize)
{
    int32_t  status    = SystemP_SUCCESS;
    uint16_t phyRegVal = 0U;
    uint8_t  *value    = (uint8_t *)data;

    if ((data == NULL) || (dataSize != sizeof(uint8_t)))
    {
        status = SystemP_FAILURE;
    }

    if (status == SystemP_SUCCESS)
    {
        status = MDIO_phyRegRead(attrs->mdioBaseAddress, NULL, attrs->phyAddress,
                                 ETHPHY_DP83822_BMSR_REG, &phyRegVal);
    }

    if (status == SystemP_SUCCESS)
    {
        *value = (phyRegVal & ETHPHY_DP83822_BMSR_AUTONEG_COMP_MASK) ? 1U : 0U;
    }

    return status;
}

/**
 * \brief Get the link partner auto-negotiation ability from the DP83822.
 *
 * Reads ANER[0] (Link Partner Auto-Negotiation Able). Returns 1 in *data
 * if the link partner supports auto-negotiation, 0 otherwise.
 *
 * \param attrs     Pointer to PHY attributes.
 * \param data      Pointer to uint8_t to receive the status (1 = able).
 * \param dataSize  Must equal sizeof(uint8_t).
 * \return          SystemP_SUCCESS on success, SystemP_FAILURE otherwise.
 */
static int32_t ETHPHY_DP83822_getLinkPartnerAutonegAbility(ETHPHY_Attrs *attrs,
                                                           void         *data,
                                                           uint32_t      dataSize)
{
    int32_t  status    = SystemP_SUCCESS;
    uint16_t phyRegVal = 0U;
    uint8_t  *value    = (uint8_t *)data;

    if ((data == NULL) || (dataSize != sizeof(uint8_t)))
    {
        status = SystemP_FAILURE;
    }

    if (status == SystemP_SUCCESS)
    {
        status = MDIO_phyRegRead(attrs->mdioBaseAddress, NULL, attrs->phyAddress,
                                 ETHPHY_DP83822_ANER_REG, &phyRegVal);
    }

    if (status == SystemP_SUCCESS)
    {
        *value = (phyRegVal & ETHPHY_DP83822_ANER_LP_AUTONEG_ABLE_MASK) ? 1U : 0U;
    }

    return status;
}

/**
 * \brief Enable IEEE Power Down mode on the DP83822.
 *
 * Sets BMCR[11] (IEEE_POWER_DOWN_ENABLE) to put the PHY into low-power
 * standby mode. The PHY will not respond to network traffic while in this
 * mode. Use ETHPHY_CMD_DISABLE_IEEE_POWER_DOWN to resume normal operation.
 *
 * \param attrs     Pointer to PHY attributes.
 * \return          SystemP_SUCCESS on success, SystemP_FAILURE otherwise.
 */
static int32_t ETHPHY_DP83822_enableIeeePowerDownMode(ETHPHY_Attrs *attrs)
{
    int32_t  status    = SystemP_FAILURE;
    uint16_t phyRegVal = 0U;

    status = MDIO_phyRegRead(attrs->mdioBaseAddress, NULL, attrs->phyAddress,
                             ETHPHY_DP83822_BMCR_REG, &phyRegVal);

    if (status == SystemP_SUCCESS)
    {
        phyRegVal |= ETHPHY_DP83822_BMCR_IEEE_POWER_DOWN_ENABLE_MASK;
        status = MDIO_phyRegWrite(attrs->mdioBaseAddress, NULL, attrs->phyAddress,
                                  ETHPHY_DP83822_BMCR_REG, phyRegVal);
    }

    return status;
}

/**
 * \brief Disable IEEE Power Down mode on the DP83822.
 *
 * Clears BMCR[11] (IEEE_POWER_DOWN_ENABLE) to return the PHY to normal
 * operating mode.
 *
 * \param attrs     Pointer to PHY attributes.
 * \return          SystemP_SUCCESS on success, SystemP_FAILURE otherwise.
 */
static int32_t ETHPHY_DP83822_disableIeeePowerDownMode(ETHPHY_Attrs *attrs)
{
    int32_t  status    = SystemP_FAILURE;
    uint16_t phyRegVal = 0U;

    status = MDIO_phyRegRead(attrs->mdioBaseAddress, NULL, attrs->phyAddress,
                             ETHPHY_DP83822_BMCR_REG, &phyRegVal);

    if (status == SystemP_SUCCESS)
    {
        phyRegVal &= ~(ETHPHY_DP83822_BMCR_IEEE_POWER_DOWN_ENABLE_MASK);
        status = MDIO_phyRegWrite(attrs->mdioBaseAddress, NULL, attrs->phyAddress,
                                  ETHPHY_DP83822_BMCR_REG, phyRegVal);
    }

    return status;
}
