#include "lip/phy/lip_phy_cfg_defs.h"

#if LIP_PHY_SUPPORT_LAN8720
#include "lip/phy/lip_phy_defs.h"
#include "lip/phy/lip_phy_mdio_regs.h"
#include "lip/phy/lip_phy_mdio.h"
#include "lip/phy/lip_phy_stats.h"
#include "lan8720_regs.h"


static t_lip_errs f_spec_cfg(const t_lip_phy_mdio_acc *regs_acc, const t_lip_phy_cfg *cfg);
static t_lip_errs f_get_mode(const t_lip_phy_mdio_acc *regs_acc, t_lip_phy_mode *mode);
static t_lip_errs f_rd_intflags(const t_lip_phy_mdio_acc *regs_acc, uint32_t *intflags);
#if LIP_PHY_USE_STATS
static t_lip_errs f_upd_stats(const t_lip_phy_mdio_acc *regs_acc, const t_lip_phy_state *state, t_lip_phy_stats *stats);
#endif

t_lip_phy_info lip_phy_lan8720 = {
    .name = "LAN8720",
    .model_id  = LIP_PHY_MODEL_ID(LAN8720_ID_VENDOR, LAN8720_ID_MODEL),
    .an_features = LIP_PHY_AN_FEAT_MODE_10_T_FD | LIP_PHY_AN_FEAT_MODE_10_T_HD
        | LIP_PHY_AN_FEAT_MODE_100_TX_FD | LIP_PHY_AN_FEAT_MODE_100_TX_HD
        | LIP_PHY_AN_FEAT_PAUSE_SYM | LIP_PHY_AN_FEAT_PAUSE_ASYM,
    .spec_features = LIP_PHY_SPEC_FEAT_REMOTE_LOOP
        | LIP_PHY_SPEC_FEAT_ED_PD,
    .spec_cfg = f_spec_cfg,
    .get_mode = f_get_mode,
    .rd_intflags = f_rd_intflags,
    #if LIP_PHY_USE_STATS
        .update_stats =  f_upd_stats
    #endif
};


static t_lip_errs f_spec_cfg(const t_lip_phy_mdio_acc *regs_acc, const t_lip_phy_cfg *cfg) {
    t_lip_errs err = LIP_ERR_SUCCESS;
    const uint16_t mode_ctl = LBITFIELD_SET(LAN8720_REGFLD_MODE_CTL_STAT_FAR_LOOP, cfg->opmode == LIP_PHY_OPMODE_REMOTE_LOOP)
                              | LBITFIELD_SET(LAN8720_REGFLD_MODE_CTL_STAT_ED_PD, (cfg->flags & LIP_PHY_CFG_FLAG_ED_PD_EN) != 0);

    err = lip_phy_mdio_reg_write_if_modified(regs_acc,  LAN8720_REGADDR_MODE_CTL_STAT, mode_ctl,
                                             LAN8720_REGFLD_MODE_CTL_STAT_FAR_LOOP | LAN8720_REGFLD_MODE_CTL_STAT_ED_PD, NULL);
    return err;
}

static t_lip_errs f_get_mode(const t_lip_phy_mdio_acc *regs_acc, t_lip_phy_mode *mode) {
    t_lip_errs err = LIP_ERR_SUCCESS;
    uint16_t phy_reg;

    err = lip_phy_mdio_reg_read(regs_acc, LAN8720_REGADDR_PHY_SPECCTL_STAT, &phy_reg);
    if (err == LIP_ERR_SUCCESS) {
        mode->flags = 0;

        if (phy_reg & LAN8720_REGFLD_PHY_SPECCTL_STAT_DUPLEX) {
            mode->flags |= LIP_PHY_MODE_FLAG_DUPLEX_FULL;
        }

        if (phy_reg & LAN8720_REGFLD_PHY_SPECCTL_STAT_SPEED_100_TX) {
            mode->speed = LIP_PHY_SPEED_100;
        } else if (phy_reg & LAN8720_REGFLD_PHY_SPECCTL_STAT_SPEED_10_T) {
            mode->speed = LIP_PHY_SPEED_10;
        } else {
            mode->speed = LIP_PHY_SPEED_INVALID;
        }
    }
    return err;
}

static t_lip_errs f_rd_intflags(const t_lip_phy_mdio_acc *regs_acc, uint32_t *intflags) {
    uint16_t intstat;
    t_lip_errs err = lip_phy_mdio_reg_read(regs_acc, LAN8720_REGADDR_INT_SRC, &intstat);
    if ((err == LIP_ERR_SUCCESS) && (intflags != NULL)) {
        *intflags = intstat;
    }
    return err;
}

#if LIP_PHY_USE_STATS
static t_lip_errs f_upd_stats(const t_lip_phy_mdio_acc *regs_acc, const t_lip_phy_state *state, t_lip_phy_stats *stats) {
    t_lip_errs err = LIP_ERR_SUCCESS;
    uint16_t errs_cnt;
    err = lip_phy_mdio_reg_read(regs_acc, LAN8720_REGADDR_SYMERR_CNT, &errs_cnt);
    if (err == LIP_ERR_SUCCESS) {
        uint16_t upd_cntr = errs_cnt - stats->ctx.physpec.save_val;
        lip_phy_stats_par_add_cntr(upd_cntr, &stats->results.link.rx_errs_cntr);
        stats->ctx.physpec.save_val = errs_cnt;
    }
    return err;
}
#endif

#endif
