#include "lip/phy/lip_phy_cfg_defs.h"
#if LIP_PHY_SUPPORT_KSZ9131
#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 "ksz9131_regs.h"

#define CABLE_TEST_MAX_RESTART_CNTR          128

typedef enum {
    LIP_KSZ9131_POLLSTATS_STATE_IDLE         = 0,
    LIP_KSZ9131_POLLSTATS_STATE_SQI_CAPTURE  = 1,
} t_lip_ksz9131_pollstats_state;


typedef enum {
    LIP_KSZ9131_CABLE_TEST_FSM_IDLE          = 0,
    LIP_KSZ9131_CABLE_TEST_FSM_START_RST     = 1,
    LIP_KSZ9131_CABLE_TEST_FSM_START_TEST    = 2,
    LIP_KSZ9131_CABLE_TEST_FSM_FINISH_RST    = 3,
    LIP_KSZ9131_CABLE_TEST_FSM_DONE          = 4,
} t_lip_ksz9131_cable_test_fsm;


typedef enum {
    LIP_KSZ9131_STATS_FSM_IDLE          = 0,
    LIP_KSZ9131_STATS_FSM_PAIR_CHECK_WR = 1,
    LIP_KSZ9131_STATS_FSM_PAIR_CHECK_RD = 2,
    LIP_KSZ9131_STATS_FSM_ERRS_CNT      = 3
} t_lip_ksz9131_stats_fsm;


/* значения регистров PHY для настройки Quite-Wire Disable */
static const uint16_t f_qw_cfg_dis[KSZ9131_REGCNT_EMITX_COEF + 1] = {
    LBITFIELD_SET(KSZ9131_REGFLD_EMITX_CTL_SCALE, 0),
    KSZ9131_REGVAL_EMITX_COEF( 31,  15), //38
    KSZ9131_REGVAL_EMITX_COEF( 31,  31), //39
    KSZ9131_REGVAL_EMITX_COEF(  0,  16), //40
    KSZ9131_REGVAL_EMITX_COEF(  0,   0), //41
    KSZ9131_REGVAL_EMITX_COEF(  0,   0), //42
    KSZ9131_REGVAL_EMITX_COEF(  0,   0), //43
    KSZ9131_REGVAL_EMITX_COEF(  0,   0), //44
    KSZ9131_REGVAL_EMITX_COEF(  0,   0), //45
    KSZ9131_REGVAL_EMITX_COEF(  0,   0), //46
    KSZ9131_REGVAL_EMITX_COEF(  0,   0), //47
    KSZ9131_REGVAL_EMITX_COEF(  0,   0), //48
    KSZ9131_REGVAL_EMITX_COEF(  0,   0), //49
    KSZ9131_REGVAL_EMITX_COEF(  0,   0), //50
    KSZ9131_REGVAL_EMITX_COEF(  0,   0), //51
    KSZ9131_REGVAL_EMITX_COEF(  0,   0), //52
};

/* значения регистров PHY для настройки Quite-Wire Enable */
static const uint16_t f_qw_cfg_en[KSZ9131_REGCNT_EMITX_COEF + 1] = {
    LBITFIELD_SET(KSZ9131_REGFLD_EMITX_CTL_SCALE, 1),
    KSZ9131_REGVAL_EMITX_COEF( 14,   3), //38
    KSZ9131_REGVAL_EMITX_COEF( 48,  32), //39
    KSZ9131_REGVAL_EMITX_COEF( 46,  54), //40
    KSZ9131_REGVAL_EMITX_COEF( 11,  28), //41
    KSZ9131_REGVAL_EMITX_COEF(126,   1), //42
    KSZ9131_REGVAL_EMITX_COEF(127, 126), //43
    KSZ9131_REGVAL_EMITX_COEF(  0,   0), //44
    KSZ9131_REGVAL_EMITX_COEF(  0,   0), //45
    KSZ9131_REGVAL_EMITX_COEF(  0,   0), //46
    KSZ9131_REGVAL_EMITX_COEF(  0,   0), //47
    KSZ9131_REGVAL_EMITX_COEF(  0,   0), //48
    KSZ9131_REGVAL_EMITX_COEF(  0,   0), //49
    KSZ9131_REGVAL_EMITX_COEF(  0,   0), //50
    KSZ9131_REGVAL_EMITX_COEF(  0,   0), //51
    KSZ9131_REGVAL_EMITX_COEF(  0,   0), //52
};


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
#if LIP_PHY_USE_CABLE_TEST
static t_lip_errs f_cable_test_start(const t_lip_phy_mdio_acc *regs_acc, t_lip_phy_cable_test_ctx *state, uint32_t pair_msk);
static t_lip_errs f_cable_test_check(const t_lip_phy_mdio_acc *regs_acc, t_lip_phy_cable_test_ctx *state, t_lip_phy_cable_test_result *result);
#endif


t_lip_phy_info lip_phy_ksz9131 = {
    .name = "KSZ9131",
    .model_id  = LIP_PHY_MODEL_ID(KSZ9131_ID_VENDOR, KSZ9131_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_MODE_1000_T_FD
        | LIP_PHY_AN_FEAT_PAUSE_SYM | LIP_PHY_AN_FEAT_PAUSE_ASYM
        | LIP_PHY_AN_FEAT_EEE_100_TX | LIP_PHY_AN_FEAT_EEE_1000_T,
    .spec_features = LIP_PHY_SPEC_FEAT_REMOTE_LOOP
        | LIP_PHY_SPEC_FEAT_ED_PD
        | LIP_PHY_SPEC_FEAT_QW
        | LIP_PHY_SPEC_FEAT_LINK_MD,
    .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
#if LIP_PHY_USE_CABLE_TEST
    .cable_check = {
        .start = f_cable_test_start,
        .check = f_cable_test_check
    }
#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;
    if (err == LIP_ERR_SUCCESS) {
        const uint16_t rem_lp = LBITFIELD_SET(KSZ9131_REGFLD_REM_LOOP_EN, cfg->opmode == LIP_PHY_OPMODE_REMOTE_LOOP);
        err = lip_phy_mdio_reg_write_if_modified(regs_acc, KSZ9131_REGADDR_REM_LOOP, rem_lp, KSZ9131_REGFLD_REM_LOOP_EN, NULL);
    }

    if (err == LIP_ERR_SUCCESS) {
        const uint16_t epdp = LBITFIELD_SET(KSZ9131_REGFLD_EDPD_CTL_EN, cfg->flags & LIP_PHY_CFG_FLAG_ED_PD_EN);
        err = lip_phy_mdio_reg_write_if_modified(regs_acc, KSZ9131_REGADDR_EDPD_CTL, epdp, KSZ9131_REGFLD_EDPD_CTL_EN, NULL);
    }

    if (err == LIP_ERR_SUCCESS) {
        const uint16_t qw = LBITFIELD_SET(KSZ9131_REGFLD_QW_EN, cfg->flags & LIP_PHY_CFG_FLAG_QW_EN);
        err = lip_phy_mdio_reg_write_if_modified(regs_acc, KSZ9131_REGADDR_QW, qw, KSZ9131_REGFLD_QW_EN, NULL);
        if (err == LIP_ERR_SUCCESS) {
            err = lip_phy_mdio_mmd_reg_write_array(regs_acc, KSZ9131_REGADDR_EMITX_CTL,
                                                   (cfg->flags & LIP_PHY_CFG_FLAG_QW_EN) ? f_qw_cfg_en : f_qw_cfg_dis,
                                                   sizeof(f_qw_cfg_en)/sizeof(f_qw_cfg_en[0]));
        }
    }

    /* установка регистров в соответствии с пунктом 4.13.1 datasheet
     * @note для выхода из loopback для данного PHY необходим software reset,
     * простого снятия бита looback в basic control register недостаточно */
    if ((err == LIP_ERR_SUCCESS) && (cfg->opmode == LIP_PHY_OPMODE_LOCAL_LOOP)) {
        err = lip_phy_mdio_reg_write(regs_acc, KSZ9131_REGADDR_LOOPBACK_CFG1, 0xEEEE);
        if (err == LIP_ERR_SUCCESS) {
            err = lip_phy_mdio_reg_write(regs_acc, KSZ9131_REGADDR_LOOPBACK_CFG2, 0xEEEE);
        }
        if (err == LIP_ERR_SUCCESS) {
            err = lip_phy_mdio_reg_write(regs_acc, KSZ9131_REGADDR_LOOPBACK_CFG3, 0xEEEE);
        }
        if (err == LIP_ERR_SUCCESS) {
            err = lip_phy_mdio_reg_write(regs_acc, KSZ9131_REGADDR_LOOPBACK_CFG4, 0xEEEE);
        }
    }

    return err;
}
static t_lip_errs f_get_mode(const t_lip_phy_mdio_acc *regs_acc, t_lip_phy_mode *mode) {
    uint16_t ctl;
    t_lip_errs err = lip_phy_mdio_reg_read(regs_acc, KSZ9131_REGADDR_CTL, &ctl);
    if (err == LIP_ERR_SUCCESS) {
        mode->flags = 0;
        if (ctl & KSZ9131_REGFLD_CTL_STAT_DUPLEX) {
            mode->flags |= LIP_PHY_MODE_FLAG_DUPLEX_FULL;
        }
        if (ctl & KSZ9131_REGFLD_CTL_STAT_MS) {
            mode->flags |= LIP_PHY_MODE_FLAG_MASTER;
        }

        if (ctl & KSZ9131_REGFLD_CTL_STAT_SPEED_1000_T)  {
            mode->speed = LIP_PHY_SPEED_1000;
        } else if (ctl & KSZ9131_REGFLD_CTL_STAT_SPEED_100_TX) {
            mode->speed = LIP_PHY_SPEED_100;
        } else if (ctl & KSZ9131_REGFLD_CTL_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, KSZ9131_REGADDR_INT_CTL_STAT, &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;

    if (stats->ctx.physpec.fsm == LIP_KSZ9131_STATS_FSM_IDLE) {        
        err = lip_phy_mdio_reg_read(regs_acc, KSZ9131_REGADDR_DCQ_CTL, &stats->ctx.physpec.save_val);
        if (err == LIP_ERR_SUCCESS) {
            stats->ctx.physpec.fsm = LIP_KSZ9131_STATS_FSM_PAIR_CHECK_WR;
            stats->ctx.physpec.pair_num = 0;
        }
    } else if (stats->ctx.physpec.fsm == LIP_KSZ9131_STATS_FSM_PAIR_CHECK_WR) {
        const uint8_t pair_num = stats->ctx.physpec.pair_num;
        uint16_t dcq_ctl = stats->ctx.physpec.save_val;
        LBITFIELD_UPD(dcq_ctl, KSZ9131_REGFLD_DCQ_CTL_CHNUM, pair_num);
        err = lip_phy_mdio_reg_write(regs_acc, KSZ9131_REGADDR_DCQ_CTL, dcq_ctl | KSZ9131_REGFLD_DCQ_CTL_RD_CAPTURE);
        if (err == LIP_ERR_SUCCESS) {
            stats->ctx.physpec.fsm = LIP_KSZ9131_STATS_FSM_PAIR_CHECK_RD;
        }
    } else if (stats->ctx.physpec.fsm == LIP_KSZ9131_STATS_FSM_PAIR_CHECK_RD) {
        struct {
            uint16_t mse;
            uint16_t mse_wc;
            uint16_t sqi;
            uint16_t dcq_peak;
        } dcq_regs;
        uint8_t pair_num = stats->ctx.physpec.pair_num;

        err = lip_phy_mdio_reg_read_array(regs_acc, KSZ9131_REGADDR_DCQ_MSE, &dcq_regs.mse, sizeof(dcq_regs)/sizeof(dcq_regs.mse));

        if (err == LIP_ERR_SUCCESS){
            if ((dcq_regs.mse & KSZ9131_REGFLD_DCQ_MSE_INVALID) == 0) {
                const uint16_t mse     = LBITFIELD_GET(dcq_regs.mse, KSZ9131_REGFLD_DCQ_MSE_VALUE);
                const uint16_t mse_wc  = LBITFIELD_GET(dcq_regs.mse_wc, KSZ9131_REGFLD_DCQ_MSE_VALUE);
                const uint16_t sqi     = LBITFIELD_GET(dcq_regs.sqi, KSZ9131_REGFLD_DCQ_SQI_VALUE);
                const uint16_t sqi_wc  = LBITFIELD_GET(dcq_regs.sqi, KSZ9131_REGFLD_DCQ_SQI_WORST);
                const uint16_t pmse    = LBITFIELD_GET(dcq_regs.dcq_peak, KSZ9131_REGFLD_DCQ_MSE_PEAK_VALUE);
                const uint16_t pmse_wc = LBITFIELD_GET(dcq_regs.dcq_peak, KSZ9131_REGFLD_DCQ_MSE_PEAK_WORST);

                lip_phy_stats_mse_upd(stats, pair_num, mse << LIP_PHY_STATS_PUT_MSE_STD_SHIFT, mse_wc << LIP_PHY_STATS_PUT_MSE_STD_SHIFT);
                lip_phy_stats_sqi_upd(stats, pair_num, sqi << LIP_PHY_STATS_PUT_SQI_STD_SHIFT, sqi_wc << LIP_PHY_STATS_PUT_SQI_STD_SHIFT);
                lip_phy_stats_pmse_upd(stats, pair_num, pmse << LIP_PHY_STATS_PUT_PMSE_STD_SHIFT, pmse_wc << LIP_PHY_STATS_PUT_PMSE_STD_SHIFT);
            }

            const uint8_t check_pair_cnt = state->mode.speed == LIP_PHY_SPEED_1000 ? 4 :
                                               state->mode.speed == LIP_PHY_SPEED_100 ? 1 : 0;
            if (++pair_num < check_pair_cnt) {
                stats->ctx.physpec.fsm = LIP_KSZ9131_STATS_FSM_PAIR_CHECK_RD;
                stats->ctx.physpec.pair_num = pair_num;
            } else {
                stats->ctx.physpec.fsm = LIP_KSZ9131_STATS_FSM_ERRS_CNT;
            }
        }
    } else if (stats->ctx.physpec.fsm == LIP_KSZ9131_STATS_FSM_ERRS_CNT) {
        uint16_t msstat;
        err = lip_phy_mdio_reg_read(regs_acc, LIP_PHY_REGADDR_MS_STAT, &msstat);
        if (err == LIP_ERR_SUCCESS) {
            uint8_t idle_errs = LBITFIELD_GET(msstat, LIP_PHY_REGFLD_MS_STAT_IDLE_ERR_CNT);
            lip_phy_stats_par_add_cntr(idle_errs, &stats->results.link.idle_errs_cntr);

            stats->ctx.physpec.fsm = LIP_KSZ9131_STATS_FSM_IDLE;
        }
    }
    return err;
}
#endif

#if LIP_PHY_USE_CABLE_TEST
static t_lip_errs f_cable_test_start(const t_lip_phy_mdio_acc *regs_acc, t_lip_phy_cable_test_ctx *state, uint32_t pair_msk) {
    t_lip_errs err = lip_phy_mdio_reg_write(
                regs_acc, LIP_PHY_REGADDR_BASIC_CTL,
                LIP_PHY_REGFLD_BASIC_CTL_RST);
    if (err == LIP_ERR_SUCCESS) {
        state->fsm = LIP_KSZ9131_CABLE_TEST_FSM_START_RST;
        state->pair_msk = pair_msk;
    }
    return err;
}

static t_lip_errs f_cable_test_check(const t_lip_phy_mdio_acc *regs_acc, t_lip_phy_cable_test_ctx *state, t_lip_phy_cable_test_result *result) {
    t_lip_errs err = LIP_ERR_SUCCESS;
    if (state->fsm == LIP_KSZ9131_CABLE_TEST_FSM_START_RST) {
        uint16_t bctl;
        err = lip_phy_mdio_reg_read(regs_acc, LIP_PHY_REGADDR_BASIC_CTL, &bctl);
        if ((err == LIP_ERR_SUCCESS) && ((bctl & LIP_PHY_REGFLD_BASIC_CTL_RST) == 0)) {
            if (err == LIP_ERR_SUCCESS) {
                /* Отключение Auto-MDI/MDI-X */
                err = lip_phy_mdio_reg_write_if_modified(
                            regs_acc, KSZ9131_REGADDR_AUTO_MDIX,
                            KSZ9131_REGFLD_AUTO_MDIX_MAN_EN, KSZ9131_REGFLD_AUTO_MDIX_MAN_EN, NULL);
            }

            if (err == LIP_ERR_SUCCESS) {
                /* Устанавливаем вручную режим SLAVE */
                err = lip_phy_mdio_reg_write_if_modified(
                            regs_acc, LIP_PHY_REGADDR_MS_CTL,
                            LIP_PHY_REGFLD_MS_CTL_MS_MAN_EN | LBITFIELD_SET(LIP_PHY_REGFLD_MS_CTL_MS_MAN_CFG, LIP_PHY_REGFLDVAL_MS_CFG_SLAVE),
                            LIP_PHY_REGFLD_MS_CTL_MS_MAN_EN | LIP_PHY_REGFLD_MS_CTL_MS_MAN_CFG, NULL);
            }

            if (err == LIP_ERR_SUCCESS) {

                err = lip_phy_mdio_reg_read(regs_acc, LIP_PHY_REGADDR_BASIC_CTL, &state->bctl);
            }
            if (err == LIP_ERR_SUCCESS) {
                /* запрет AN, Full-Duplex, 1000 MБит */
                err = lip_phy_mdio_reg_write(
                            regs_acc, LIP_PHY_REGADDR_BASIC_CTL,
                            LIP_PHY_REGFLD_BASIC_CTL_DUPLEX |
                            LIP_PHY_REGFLDMSK_BASIC_CTL_SPPED_1000);
            }

            if (err == LIP_ERR_SUCCESS) {
                uint16_t bcrtl, mdi, msctl;
                err = lip_phy_mdio_reg_read(regs_acc, LIP_PHY_REGADDR_BASIC_CTL, &bcrtl);
                if (err == LIP_ERR_SUCCESS) {
                    err = lip_phy_mdio_reg_read(regs_acc, KSZ9131_REGADDR_AUTO_MDIX, &mdi);
                }
                if (err == LIP_ERR_SUCCESS) {
                    err = lip_phy_mdio_reg_read(regs_acc, LIP_PHY_REGADDR_MS_CTL, &msctl);
                }
            }


            if (err == LIP_ERR_SUCCESS) {
                /* первый запуск теста дает отличный результат.
                 * поэтому выполняем его вхолостую, указывая в состоянии
                 * номер пары LIP_PHY_PAIR_NUM_INVALID */
                state->pair_num = LIP_PHY_PAIR_NUM_INVALID;
                err = lip_phy_mdio_reg_write(regs_acc, KSZ9131_REGADDR_LINKMD,
                                             KSZ9131_REGFLD_LINKMD_VCT_EN |
                                             LBITFIELD_SET(KSZ9131_REGFLD_LINKMD_VCT_PAIR, 0));
            }
            if (err == LIP_ERR_SUCCESS) {
                state->fsm = LIP_KSZ9131_CABLE_TEST_FSM_START_TEST;
                state->res_cntr = 0;
            }
        }
    }

    if (state->fsm == LIP_KSZ9131_CABLE_TEST_FSM_START_TEST) {
        uint16_t vctreg;
        err = lip_phy_mdio_reg_read(regs_acc, KSZ9131_REGADDR_LINKMD, &vctreg);
        if (err == LIP_ERR_SUCCESS) {
            if ((vctreg & KSZ9131_REGFLD_LINKMD_VCT_EN) == 0) {
                bool restart_req = false;
                if (state->pair_num != LIP_PHY_PAIR_NUM_INVALID) {
                    const uint8_t status = LBITFIELD_GET(vctreg, KSZ9131_REGFLD_LINKMD_VCT_STAT);
                    const uint8_t data   = LBITFIELD_GET(vctreg, KSZ9131_REGFLD_LINKMD_VCT_DATA);
                    t_lip_phy_cable_test_pair_result *pair = &result->pair[state->pair_num];

                    pair->valid = true;
                    if (status == KSZ9131_REGFLDVAL_LINKMD_VCT_STAT_NORM) {
                        pair->status = LIP_PHY_CABLE_CHECK_STATUS_NORMAL;
                    } else if (status == KSZ9131_REGFLDVAL_LINKMD_VCT_STAT_OPEN) {
                        pair->status = LIP_PHY_CABLE_CHECK_STATUS_OPEN;
                        pair->distance = 0.8 * (data - 22);
                    } else if (status == KSZ9131_REGFLDVAL_LINKMD_VCT_STAT_SHORT) {
                        pair->status = LIP_PHY_CABLE_CHECK_STATUS_SHORT;
                        pair->distance = 0.8 * (data - 22);
                    } else {
                        /* данные ошибки могут возникать при активности на линии,
                         * когда не удалось обнаружить нужный интервал тишины.
                         * пробуем повтоно запустить тест в этом случае, но не
                         * ограничиваем число попыток в CABLE_TEST_MAX_RESTART_CNTR
                         * раз, чтобы не зациклится бесконечно */
                        if (data & (KSZ9131_REGFLDVAL_LINKMD_VCT_DATA_ERR_NFLP_NOT_SILENT |
                                    KSZ9131_REGFLDVAL_LINKMD_VCT_DATA_ERR_100M_NOT_SILENT  |
                                    KSZ9131_REGFLDVAL_LINKMD_VCT_DATA_ERR_WAIT_TOO_LONG)) {
                            if (state->res_cntr < CABLE_TEST_MAX_RESTART_CNTR) {
                                restart_req = true;
                            } else {
                                pair->status = LIP_PHY_CABLE_CHECK_STATUS_FAILED;
                                pair->fail_code = data;
                            }
                        }
                    }
                }

                if (err == LIP_ERR_SUCCESS) {
                    bool pair_fnd = false;
                    if (restart_req) {
                        ++state->res_cntr;
                        pair_fnd = true;
                    } else {
                        /* проверяем, осталась ли еще диф-пара для проверки */
                        for (uint8_t pair_num = (state->pair_num == LIP_PHY_PAIR_NUM_INVALID) ? 0 : (state->pair_num + 1);
                             (pair_num < LIP_PHY_MAX_PAIR_CNT) && !pair_fnd; ++pair_num) {
                            if (state->pair_msk & (1 << pair_num)) {
                                state->pair_num = pair_num;
                                state->res_cntr = 0;
                                pair_fnd = true;
                            }
                        }
                    }

                    if (pair_fnd) {
                        err = lip_phy_mdio_reg_write(regs_acc, KSZ9131_REGADDR_LINKMD,
                                                     KSZ9131_REGFLD_LINKMD_VCT_EN |
                                                     LBITFIELD_SET(KSZ9131_REGFLD_LINKMD_VCT_PAIR, state->pair_num));
                        if (err == LIP_ERR_SUCCESS) {
                            state->fsm = LIP_KSZ9131_CABLE_TEST_FSM_START_TEST;
                        }
                    } else {
                        /* запрет AN, Full-Duplex, 1000 MБит + RST */
                        err = lip_phy_mdio_reg_write(
                                    regs_acc, LIP_PHY_REGADDR_BASIC_CTL,
                                    state->bctl | LIP_PHY_REGFLD_BASIC_CTL_RST);
                        if (err == LIP_ERR_SUCCESS) {
                            state->fsm = LIP_KSZ9131_CABLE_TEST_FSM_FINISH_RST;
                        }
                    }
                }
            }
        }
    }

    if (state->fsm == LIP_KSZ9131_CABLE_TEST_FSM_FINISH_RST) {
        uint16_t bctl;
        err = lip_phy_mdio_reg_read(regs_acc, LIP_PHY_REGADDR_BASIC_CTL, &bctl);
        if ((err == LIP_ERR_SUCCESS) && ((bctl & LIP_PHY_REGFLD_BASIC_CTL_RST) == 0)) {
            state->fsm = LIP_KSZ9131_CABLE_TEST_FSM_DONE;
            result->finished = true;
        }
    }
    return err;
}
#endif



#endif
