#include "kszsw_reg_acc.h"
#include "ksz8567_phy_regs.h"

#if LIP_SW_SUPPORT_KSZ8567

t_lip_errs kszsw_global_reg_read8(const t_kszsw_reg_acc *reg_acc, uint16_t regaddr, uint8_t *regval) {
    return reg_acc->iface->rd8(reg_acc->ctx, KSZSW_GLOBAL_REG_ADDR(regaddr), regval);
}

t_lip_errs kszsw_global_reg_read16(const t_kszsw_reg_acc *reg_acc, uint16_t regaddr, uint16_t *regval) {
    return reg_acc->iface->rd16(reg_acc->ctx, KSZSW_GLOBAL_REG_ADDR(regaddr), regval);
}

t_lip_errs kszsw_global_reg_read32(const t_kszsw_reg_acc *reg_acc, uint16_t regaddr, uint32_t *regval) {
    return reg_acc->iface->rd32(reg_acc->ctx, KSZSW_GLOBAL_REG_ADDR(regaddr), regval);
}

t_lip_errs kszsw_global_reg_write8(const t_kszsw_reg_acc *reg_acc, uint16_t regaddr, uint8_t val) {
    return reg_acc->iface->wr8(reg_acc->ctx, KSZSW_GLOBAL_REG_ADDR(regaddr), val);
}

t_lip_errs kszsw_global_reg_write16(const t_kszsw_reg_acc *reg_acc, uint16_t regaddr, uint16_t val) {
    return reg_acc->iface->wr16(reg_acc->ctx, KSZSW_GLOBAL_REG_ADDR(regaddr), val);
}

t_lip_errs kszsw_global_reg_write32(const t_kszsw_reg_acc *reg_acc, uint16_t regaddr, uint32_t val) {
    return reg_acc->iface->wr32(reg_acc->ctx, KSZSW_GLOBAL_REG_ADDR(regaddr), val);
}


t_lip_errs kszsw_port_reg_read8(const t_kszsw_reg_acc *reg_acc, uint8_t port, uint16_t regaddr, uint8_t *regval) {
    return reg_acc->iface->rd8(reg_acc->ctx, KSZSW_PORT_REG_ADDR(port, regaddr), regval);
}
t_lip_errs kszsw_port_reg_read16(const t_kszsw_reg_acc *reg_acc, uint8_t port, uint16_t regaddr, uint16_t *regval) {
    return reg_acc->iface->rd16(reg_acc->ctx, KSZSW_PORT_REG_ADDR(port, regaddr), regval);
}
t_lip_errs kszsw_port_reg_read32(const t_kszsw_reg_acc *reg_acc, uint8_t port, uint16_t regaddr, uint32_t *regval) {
    return reg_acc->iface->rd32(reg_acc->ctx, KSZSW_PORT_REG_ADDR(port, regaddr), regval);
}

t_lip_errs kszsw_port_reg_write8(const t_kszsw_reg_acc *reg_acc, uint8_t port, uint16_t regaddr, uint8_t val) {
    return reg_acc->iface->wr8(reg_acc->ctx, KSZSW_PORT_REG_ADDR(port, regaddr), val);
}
t_lip_errs kszsw_port_reg_write16(const t_kszsw_reg_acc *reg_acc, uint8_t port, uint16_t regaddr, uint16_t val) {
    return reg_acc->iface->wr16(reg_acc->ctx, KSZSW_PORT_REG_ADDR(port, regaddr), val);
}
t_lip_errs kszsw_port_reg_write32(const t_kszsw_reg_acc *reg_acc, uint8_t port, uint16_t regaddr, uint32_t val) {
    return reg_acc->iface->wr32(reg_acc->ctx, KSZSW_PORT_REG_ADDR(port, regaddr), val);
}


t_lip_errs kszsw_get_id(const t_kszsw_reg_acc *reg_acc, uint16_t *id) {
    return kszsw_global_reg_read16(reg_acc, KSZSW_REGADDR_CHIP_ID1, id);
}


t_lip_errs kszsw_phy_reg_read(const t_kszsw_reg_acc *reg_acc, uint8_t port, uint8_t regaddr, uint16_t *regval) {
    return kszsw_port_reg_read16(reg_acc, port, KSZSW_REGADDR_PORT_PHY(regaddr), regval);
}

t_lip_errs kszsw_phy_reg_write(const t_kszsw_reg_acc *reg_acc, uint8_t port, uint8_t regaddr, uint16_t val) {
    t_lip_errs err = LIP_ERR_SUCCESS;
    /* Errata module 6. Часть регистров должны записываться парами как write32, при
     * этом второй регистр пары должен быть вначале прочитан для сохранения его
     * значений (могут быть не 0 даже в резервных регистрах)
     * Обновленная errata расширяет диапазон регистров с 0x10-0x17 на 0x10-0x1F,
     * но по практике 32-битная запись не работает для регистра INT_CTL_STAT! */
    if ((regaddr >= 0x10) && (regaddr < 0x20) && (regaddr != KSZ8567_PHY_REGADDR_INT_CTL_STAT)) {
        uint16_t h, l;
        if (regaddr & 1) {
            h = val;
            err = kszsw_phy_reg_read(reg_acc, port, regaddr - 1, &l);
            regaddr -= 1;
        } else {
            l = val;
            err = kszsw_phy_reg_read(reg_acc, port, regaddr + 1, &h);
        }
        if (err == LIP_ERR_SUCCESS) {
            err = kszsw_port_reg_write32(reg_acc, port, KSZSW_REGADDR_PORT_PHY(regaddr), ((uint32_t)h << 16) | l);
        }
    } else {
        err = kszsw_port_reg_write16(reg_acc, port, KSZSW_REGADDR_PORT_PHY(regaddr), val);
    }
    return err;
}


t_lip_errs kszsw_port_mib_read(const t_kszsw_reg_acc *reg_acc, uint8_t port, uint8_t mib_idx, t_kszsw_mib_value *result) {
    uint32_t ctlstat =  LBITFIELD_SET(KSZSW_REGFLD_PORT_MIB_CTL_STAT_RDEN_CNTVALID, 1)
            | LBITFIELD_SET(KSZSW_REGFLD_PORT_MIB_CTL_STAT_IDX, mib_idx);    
    t_lip_errs err = kszsw_port_reg_write32(reg_acc, port, KSZSW_REGADDR_PORT_MIB_CTL_STAT, ctlstat);
    while ((err == LIP_ERR_SUCCESS) && ((ctlstat & KSZSW_REGFLD_PORT_MIB_CTL_STAT_RDEN_CNTVALID) != 0)) {
        err = kszsw_port_reg_read32(reg_acc, port, KSZSW_REGADDR_PORT_MIB_CTL_STAT, &ctlstat);
    }

    if ((err == LIP_ERR_SUCCESS) && (result != NULL)) {
        err = kszsw_port_reg_read32(reg_acc, port, KSZSW_REGADDR_PORT_MIB_DATA, &result->lval);
        if (err == LIP_ERR_SUCCESS) {
            result->hval = LBITFIELD_GET(ctlstat, KSZSW_REGFLD_PORT_MIB_CTL_STAT_CNTR_VAL_H);
            result->flags = KSZSW_MIB_VALUE_FLAG_VALID | ((ctlstat & KSZSW_REGFLD_PORT_MIB_CTL_STAT_CNTR_OV) ? KSZSW_MIB_VALUE_FLAG_OV : 0);
        }
    }

    return err;
}

t_lip_errs kszsw_tbl_staddr_rd_wrds(const t_kszsw_reg_acc *reg_acc, uint8_t idx, uint32_t *tbl_entry) {
    t_lip_errs err;
    uint32_t ctlstat =   LBITFIELD_SET(KSZSW_REGFLD_STADDR_RMCAST_TBL_CTL_STADDR_IDX, idx)
                       | LBITFIELD_SET(KSZSW_REGFLD_STADDR_RMCAST_TBL_CTL_START, 1)
                       | LBITFIELD_SET(KSZSW_REGFLD_STADDR_RMCAST_TBL_CTL_TBL_SEL, KSZSW_REGFLDVAL_STADDR_RMCAST_TBL_CTL_TBL_SEL_SADDR)
                       | LBITFIELD_SET(KSZSW_REGFLD_STADDR_RMCAST_TBL_CTL_ACTION, KSZSW_REGFLDVAL_STADDR_RMCAST_TBL_CTL_ACTION_READ);
    err =  kszsw_global_reg_write32(reg_acc, KSZSW_REGADDR_STADDR_RMCAST_TBL_CTL, ctlstat);
    while ((err == LIP_ERR_SUCCESS) && ((ctlstat & KSZSW_REGFLD_STADDR_RMCAST_TBL_CTL_START) != 0)) {
        err = kszsw_global_reg_read32(reg_acc, KSZSW_REGADDR_STADDR_RMCAST_TBL_CTL, &ctlstat);
    }

    if (err == LIP_ERR_SUCCESS) {
        err = kszsw_global_reg_read32(reg_acc, KSZSW_REGADDR_STADDR_TBL_ENTRY1, &tbl_entry[0]);
    }
    if (err == LIP_ERR_SUCCESS) {
        err = kszsw_global_reg_read32(reg_acc, KSZSW_REGADDR_STADDR_TBL_ENTRY2, &tbl_entry[1]);
    }
    if (err == LIP_ERR_SUCCESS) {
        err = kszsw_global_reg_read32(reg_acc, KSZSW_REGADDR_STADDR_TBL_ENTRY3, &tbl_entry[2]);
    }
    if (err == LIP_ERR_SUCCESS) {
        err = kszsw_global_reg_read32(reg_acc, KSZSW_REGADDR_STADDR_TBL_ENTRY4, &tbl_entry[3]);
    }
    return err;
}

t_lip_errs kszsw_tbl_staddr_wr_wrds(const t_kszsw_reg_acc *reg_acc, uint8_t idx, const uint32_t *tbl_entry) {
    t_lip_errs err = kszsw_global_reg_write32(reg_acc, KSZSW_REGADDR_STADDR_TBL_ENTRY1, tbl_entry[0]);
    if (err == LIP_ERR_SUCCESS) {
        err =  kszsw_global_reg_write32(reg_acc, KSZSW_REGADDR_STADDR_TBL_ENTRY2, tbl_entry[1]);
    }
    if (err == LIP_ERR_SUCCESS) {
        err =  kszsw_global_reg_write32(reg_acc, KSZSW_REGADDR_STADDR_TBL_ENTRY3, tbl_entry[2]);
    }
    if (err == LIP_ERR_SUCCESS) {
        err =  kszsw_global_reg_write32(reg_acc, KSZSW_REGADDR_STADDR_TBL_ENTRY4, tbl_entry[3]);
    }
    if (err == LIP_ERR_SUCCESS) {
        uint32_t ctlstat =   LBITFIELD_SET(KSZSW_REGFLD_STADDR_RMCAST_TBL_CTL_STADDR_IDX, idx)
                           | LBITFIELD_SET(KSZSW_REGFLD_STADDR_RMCAST_TBL_CTL_START, 1)
                           | LBITFIELD_SET(KSZSW_REGFLD_STADDR_RMCAST_TBL_CTL_TBL_SEL, KSZSW_REGFLDVAL_STADDR_RMCAST_TBL_CTL_TBL_SEL_SADDR)
                           | LBITFIELD_SET(KSZSW_REGFLD_STADDR_RMCAST_TBL_CTL_ACTION, KSZSW_REGFLDVAL_STADDR_RMCAST_TBL_CTL_ACTION_WRITE);
        err =  kszsw_global_reg_write32(reg_acc, KSZSW_REGADDR_STADDR_RMCAST_TBL_CTL, ctlstat);
        while ((err == LIP_ERR_SUCCESS) && ((ctlstat & KSZSW_REGFLD_STADDR_RMCAST_TBL_CTL_START) != 0)) {
            err = kszsw_global_reg_read32(reg_acc, KSZSW_REGADDR_STADDR_RMCAST_TBL_CTL, &ctlstat);
        }
    }
    return err;
}

t_lip_errs kszsw_tbl_staddr_wr(const t_kszsw_reg_acc *reg_acc, uint8_t idx, const t_kszsw_staddr_entry *tbl_entry) {
    uint32_t wrds[4];
    wrds[0] = LBITFIELD_SET(KSZSW_REGFLD_STADDR_TBL_ENTRY1_VALID, !!(tbl_entry->flags & KSZSW_STADDR_ENTRY_FLAG_VALID))
               | LBITFIELD_SET(KSZSW_REGFLD_STADDR_TBL_ENTRY1_SRC_FLT, !!(tbl_entry->flags & KSZSW_STADDR_ENTRY_FLAG_SRC_FLT))
               | LBITFIELD_SET(KSZSW_REGFLD_STADDR_TBL_ENTRY1_DST_FLT, !!(tbl_entry->flags & KSZSW_STADDR_ENTRY_FLAG_DST_FLT))
               | LBITFIELD_SET(KSZSW_REGFLD_STADDR_TBL_ENTRY1_PRIO, tbl_entry->prio)
               | LBITFIELD_SET(KSZSW_REGFLD_STADDR_TBL_ENTRY1_MSTP, tbl_entry->mstp)
        ;

    wrds[1] = LBITFIELD_SET(KSZSW_REGFLD_STADDR_TBL_ENTRY2_OVRD, !!(tbl_entry->flags & KSZSW_STADDR_ENTRY_FLAG_PORTST_OV))
               | LBITFIELD_SET(KSZSW_REGFLD_STADDR_TBL_ENTRY2_USE_FID, !!(tbl_entry->flags & KSZSW_STADDR_ENTRY_FLAG_USE_FID))
               | LBITFIELD_SET(KSZSW_REGFLD_STADDR_TBL_ENTRY2_PORT_FWD, tbl_entry->port_fwd)
        ;
    wrds[2] = LBITFIELD_SET(KSZSW_REGFLD_STADDR_TBL_ENTRY3_FID, !!(tbl_entry->flags & KSZSW_STADDR_ENTRY_FLAG_PORTST_OV))
              | LBITFIELD_SET(KSZSW_REGFLD_STADDR_TBL_ENTRY3_MAC_H, (((uint16_t)tbl_entry->mac[0] << 8) | tbl_entry->mac[1]))
        ;
    wrds[3] = (tbl_entry->mac[2] << 24)
                | (tbl_entry->mac[3] << 16)
                | (tbl_entry->mac[4] << 8)
               | (tbl_entry->mac[5] << 0);
    return kszsw_tbl_staddr_wr_wrds(reg_acc, idx, wrds);
}


#endif











