#include "lip/phy/lip_phy_cfg_defs.h"

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


static const uint16_t f_qw_cfg_dis[KSZ8567_PHY_REGCNT_QUIET_WIRE_CFG] = {
    0x0000, 0x1F0F, 0x1F1F, 0x0010, 0x0000, 0x0000, 0x0000, 0x0000,
    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
};

static const uint16_t f_qw_cfg_en [KSZ8567_PHY_REGCNT_QUIET_WIRE_CFG] = {
    0x0001, 0x0E03, 0x3020, 0x2E36, 0x0B1C, 0x7E01, 0x7F7E, 0x0000,
    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
};



t_lip_phy_info lip_phy_ksz8567 = {
    .name = "KSZ8567",
    .model_id  = LIP_PHY_MODEL_ID(KSZ8567_PHY_ID_VENDOR, KSZ8567_PHY_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
        | LIP_PHY_AN_FEAT_EEE_100_TX,
    .spec_features = LIP_PHY_SPEC_FEAT_REMOTE_LOOP
        | LIP_PHY_SPEC_FEAT_QW,
    .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;

    /* Before configuring the PHY MMD registers, it is necessary to set the PHY
     * to 100 Mbps speed with auto-negotiation disabled */
    /* Т.к. spec_cfg идет перед основной конфигурацией, то явно восстанавливать
     * предыдущий режим не обязательно */
    lip_phy_mdio_reg_write(regs_acc, LIP_PHY_REGADDR_BASIC_CTL, LIP_PHY_REGFLDMSK_BASIC_CTL_SPPED_100 | LIP_PHY_REGFLD_BASIC_CTL_DUPLEX);


    /* Errata Module 1 */
    lip_phy_mdio_reg_write(regs_acc, LIP_PHY_REGADDR_MMD(0x01, 0x6F), 0xDD0B);
    lip_phy_mdio_reg_write(regs_acc, LIP_PHY_REGADDR_MMD(0x01, 0x8F), 0x6032);
    lip_phy_mdio_reg_write(regs_acc, LIP_PHY_REGADDR_MMD(0x01, 0x9D), 0x248C);
    lip_phy_mdio_reg_write(regs_acc, LIP_PHY_REGADDR_MMD(0x01, 0x75), 0x0060);
    lip_phy_mdio_reg_write(regs_acc, LIP_PHY_REGADDR_MMD(0x01, 0xD3), 0x7777);
    lip_phy_mdio_reg_write(regs_acc, LIP_PHY_REGADDR_MMD(0x1C, 0x06), 0x3008);
    lip_phy_mdio_reg_write(regs_acc, LIP_PHY_REGADDR_MMD(0x1C, 0x08), 0x2000);
    /* Errata Module 2 */
    lip_phy_mdio_reg_write(regs_acc, LIP_PHY_REGADDR_MMD(0x1C, 0x09), 0xE214);
    lip_phy_mdio_reg_write(regs_acc, LIP_PHY_REGADDR_MMD(0x1C, 0x04), 0x00D0);
    /* Errata Module 7 */
    lip_phy_mdio_reg_write(regs_acc, LIP_PHY_REGADDR_MMD(0x1C, 0x13), 0x6EFF);
    lip_phy_mdio_reg_write(regs_acc, LIP_PHY_REGADDR_MMD(0x1C, 0x14), 0xE6FF);
    lip_phy_mdio_reg_write(regs_acc, LIP_PHY_REGADDR_MMD(0x1C, 0x15), 0x6EFF);
    lip_phy_mdio_reg_write(regs_acc, LIP_PHY_REGADDR_MMD(0x1C, 0x16), 0xE6FF);
    lip_phy_mdio_reg_write(regs_acc, LIP_PHY_REGADDR_MMD(0x1C, 0x17), 0x00FF);
    lip_phy_mdio_reg_write(regs_acc, LIP_PHY_REGADDR_MMD(0x1C, 0x18), 0x43FF);
    lip_phy_mdio_reg_write(regs_acc, LIP_PHY_REGADDR_MMD(0x1C, 0x19), 0xC3FF);
    lip_phy_mdio_reg_write(regs_acc, LIP_PHY_REGADDR_MMD(0x1C, 0x1A), 0x6FFF);
    lip_phy_mdio_reg_write(regs_acc, LIP_PHY_REGADDR_MMD(0x1C, 0x1B), 0x07FF);
    lip_phy_mdio_reg_write(regs_acc, LIP_PHY_REGADDR_MMD(0x1C, 0x1C), 0x0FFF);
    lip_phy_mdio_reg_write(regs_acc, LIP_PHY_REGADDR_MMD(0x1C, 0x1D), 0xE7FF);
    lip_phy_mdio_reg_write(regs_acc, LIP_PHY_REGADDR_MMD(0x1C, 0x1E), 0xEFFF);
    lip_phy_mdio_reg_write(regs_acc, LIP_PHY_REGADDR_MMD(0x1C, 0x20), 0xEEEE);
    /* Errata Module 4. EEE требуется либо запретить, либо установить регистры.
     * Требуется также установить регистр свича 0x03C2 в 0x0080 */
    if ((cfg->an_dis_features & LIP_PHY_AN_FEAT_EEE_100_TX) == 0) {
        lip_phy_mdio_reg_write(regs_acc, LIP_PHY_REGADDR_MMD(0x01, 0xCE), 0x0900);
        lip_phy_mdio_reg_write(regs_acc, LIP_PHY_REGADDR_MMD(0x01, 0xCC), 0x0FE0 );
        lip_phy_mdio_reg_write(regs_acc, LIP_PHY_REGADDR_MMD(0x01, 0xCA), 0x0141);
        lip_phy_mdio_reg_write(regs_acc, LIP_PHY_REGADDR_MMD(0x01, 0xCB), 0x0FD6);
        lip_phy_mdio_reg_write(regs_acc, LIP_PHY_REGADDR_MMD(0x01, 0xC8), 0x0010);
        lip_phy_mdio_reg_write(regs_acc, LIP_PHY_REGADDR_MMD(0x01, 0xD9), 0x0160);
        lip_phy_mdio_reg_write(regs_acc, LIP_PHY_REGADDR_MMD(0x01, 0xC9), 0x0180);
        lip_phy_mdio_reg_write(regs_acc, LIP_PHY_REGADDR_MMD(0x01, 0xC7), 0x81E0);
    }


    lip_phy_mdio_reg_write_array(regs_acc, KSZ8567_PHY_REGADDR_QUIET_WIRE_CFG(0),
                                     (cfg->flags & LIP_PHY_CFG_FLAG_QW_EN) ? f_qw_cfg_en : f_qw_cfg_dis,
                                     KSZ8567_PHY_REGCNT_QUIET_WIRE_CFG);



    if (err == LIP_ERR_SUCCESS) {
        if (cfg->opmode == LIP_PHY_OPMODE_REMOTE_LOOP) {
            /* Последовательность записи из раздела 4.1.9 REMOTE PHY LOOPBACK */
            err = lip_phy_mdio_std_reg_write(regs_acc, LIP_PHY_REGADDR_AN_ADV, 0x0181);
            if (err == LIP_ERR_SUCCESS) {

                /* После включения питания первая запись/чтение в данный регистр может не выполняться.
                 * В связи с этим делаем несколько попыток до тех пор, пока не будет установлен
                 * бит KSZ8567_PHY_REGFLD_REM_LOOP_EN */
                uint16_t rd_val = 0;
                int rd_cnt = 0;
                do {
                    err = lip_phy_mdio_std_reg_write(regs_acc, KSZ8567_PHY_REGADDR_REM_LOOP, 0x01F0);
                    if (err == LIP_ERR_SUCCESS) {
                        err = lip_phy_mdio_reg_read(regs_acc, KSZ8567_PHY_REGADDR_REM_LOOP, &rd_val);
                    }
                    ++rd_cnt;
                } while ((err == LIP_ERR_SUCCESS) && (rd_cnt < 3) && !(rd_val & KSZ8567_PHY_REGFLD_REM_LOOP_EN));

                if ((err == LIP_ERR_SUCCESS) && !(rd_val & KSZ8567_PHY_REGFLD_REM_LOOP_EN)) {
                    err = LIP_ERR_PHY_MDIO_REG_WR_ERR;
                }
            }
            if (err == LIP_ERR_SUCCESS) {
                err = lip_phy_mdio_std_reg_write(regs_acc, LIP_PHY_REGADDR_BASIC_CTL, 0x3300);
            }
        } else {
            err = lip_phy_mdio_std_reg_write(regs_acc, KSZ8567_PHY_REGADDR_REM_LOOP, 0x00F0);
        }
    }

    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, KSZ8567_PHY_REGADDR_CTL, &ctl);
    if (err == LIP_ERR_SUCCESS) {
        mode->flags = 0;
        if (ctl & KSZ8567_PHY_REGFLD_CTL_STAT_DUPLEX) {
            mode->flags |= LIP_PHY_MODE_FLAG_DUPLEX_FULL;
        }
        if (ctl & KSZ8567_PHY_REGFLD_CTL_STAT_SPEED_100_TX) {
            mode->speed = LIP_PHY_SPEED_100;
        } else if (ctl & KSZ8567_PHY_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, KSZ8567_PHY_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 (state->flags & LIP_PHY_STATE_FLAG_LINK_UP) {
        uint16_t sqireg;

        err = lip_phy_mdio_reg_read(regs_acc, KSZ8567_PHY_REGADDR_SQI, &sqireg);
        if (err == LIP_ERR_SUCCESS) {
            uint8_t sqival = LBITFIELD_GET(sqireg, KSZ8567_PHY_REGFLD_SQI_VAL);
            /* по аналогии с драйвером linux, меняем на положительный счет и считаем не действительными значения выше 15.
               тут используем 14 уровней для более простого мэппинга на стандартные уровни 0-7, т.е.
               каждый уровень KSZ8567 соответствует половине стандартного */
            uint16_t sqi = (sqival > 14 ? 0 : 14 - sqival) << (LIP_PHY_STATS_PUT_SQI_STD_SHIFT - 1);
            lip_phy_stats_sqi_upd(stats, 0, sqi, sqi);
        }


        if (err == LIP_ERR_SUCCESS) {
            uint16_t err_cnt;
            err = lip_phy_mdio_mmd_reg_read(regs_acc, KSZ8567_PHY_REGADDR_RXERR_CNT, &err_cnt);
            if (err == LIP_ERR_SUCCESS) {
                lip_phy_stats_par_add_cntr(err_cnt, &stats->results.link.rx_errs_cntr);
            }
        }
    }
    return err;
}
#endif

#endif
