#include "lip/lip_cfg_defs.h"
#include "lip/ll/lip_ll.h"
#include "lip/phy/lip_phy_mdio_frame.h"
#include "lip/phy/lip_phy_defs.h"
#include "lip/link/mac/lip_mac.h"
#include "lip/link/mac/rx/lip_mac_rx.h"
#include "lip/link/mac/tx/lip_mac_tx.h"
#include "lip/net/ipv4/lip_ip_private.h"
#include "fast_crc.h"

#ifdef LIP_CFG_LL_DESCR_MEM
    #define LIP_LL_DESCR_MEM(v)         LIP_CFG_LL_DESCR_MEM(v)
#else
    #define LIP_LL_DESCR_MEM(v)         LIP_DEFAULT_STATIC_BUF_MEM(v)
#endif

#ifdef LIP_CFG_LL_RX_BUF_MEM
    #define LIP_LL_RX_BUF_MEM(v)           LIP_CFG_LL_RX_BUF_MEM(v)
#else
    #define LIP_LL_RX_BUF_MEM(v)           LIP_DEFAULT_STATIC_BUF_MEM(v)
#endif

#ifdef LIP_CFG_LL_RX_DESCR_CNT
    #define LIP_LL_RX_DESCR_CNT         LIP_CFG_LL_RX_DESCR_CNT
#else
    #define LIP_LL_RX_DESCR_CNT         8
#endif
/* размер одного блока */
#ifdef LIP_CFG_LL_RX_BUF_SIZE
    #define LIP_LL_RX_BUF_SIZE          LIP_CFG_LL_RX_BUF_SIZE
#else
    #define LIP_LL_RX_BUF_SIZE          (512)
#endif
/* общий размер всей памяти на прием */
#define LIP_LL_RX_FULL_BUF_SIZE         (LIP_LL_RX_BUF_SIZE * LIP_LL_RX_DESCR_CNT)

/* кол-во дескрипторов на передачу */
#ifdef LIP_CFG_LL_TX_DESCR_CNT
    #define LIP_LL_TX_DESCR_CNT         LIP_CFG_LL_TX_DESCR_CNT
#else
    #define LIP_LL_TX_DESCR_CNT         8
#endif


#define LIP_LL_RX_NEXT_IND(ind) ((ind)==(LIP_LL_RX_DESCR_CNT-1) ? 0 : (ind)+1)
#define LIP_LL_TX_NEXT_IND(ind) ((ind)==(LIP_LL_TX_DESCR_CNT-1) ? 0 : (ind)+1)
#define LIP_LL_DESCR_WRD(wrd)   HTON16(wrd)

#define MDIO_FREQ_MAX       CHIP_KHZ(2500)
#define MDIO_HOLD_MIN_NS    10
#define MDIO_REF_FREQ   CHIP_CLK_IPG_FREQ

#define MDIO_RVAL_SPEED      ((CHIP_CLK_IPG_FREQ + 2*MDIO_FREQ_MAX - 1)/(2*CHIP_KHZ(2500)) - 1)
#define MDIO_RVAL_HOLDTIME   (((MDIO_HOLD_MIN_NS * CHIP_CLK_IPG_FREQ + 1000000000-1)/1000000000) - 1)
#if ((MDIO_RVAL_SPEED < 0) || (MDIO_RVAL_SPEED > 0x3F))
    #error invalid MDIO clk freq
#endif
#if ((MDIO_RVAL_HOLDTIME < 0) || (MDIO_RVAL_HOLDTIME > 0x7))
    #error invalid MDIO holdtime value
#endif

#if LIP_PTP_ENABLE
static bool f_tstmp_en;
static volatile uint32_t f_tstmp_secs;
static void f_tstm_start(void);
static void f_fill_ptp_tstmp(t_lip_ptp_tstmp *tstmp, uint32_t mac_ns) {
    tstmp->hsec = 0;
    tstmp->fract_ns = 0;
    /** @todo нужно по хорошему учесть возможность изменения секунд с момента
     *  фиксации нс аппаратурой и до заполнения полей метки времени (возможно
     *  сделать прерывание на пололвине и иметь флаг) */
    tstmp->sec = f_tstmp_secs;
    tstmp->ns = mac_ns;
}

#endif
static void f_tstm_stop(void);
static bool f_mac_init_finish;


/* номер дескриптора, соответствующий началу принимаемого пакета */
static uint8_t f_rx_cur_descr;

/* номер дескриптора, который будет использован для передачи след. пакета */
static uint8_t f_tx_put_descr;
/* номер дескриптора, следующий за последним, по которому завершена передача */
static uint8_t f_tx_done_descr;
/* кол-во дескрипторов, по которым начата, но не завершена передача */
static uint8_t f_tx_descr_progr_cnt;


#define LSPEC_ALIGNMENT 64

#include "lcspec_align.h"
LIP_LL_DESCR_MEM(static volatile CHIP_ENET_RXBD_T f_rx_descr[LIP_LL_RX_DESCR_CNT]);
#include "lcspec_align.h"
LIP_LL_DESCR_MEM(static volatile CHIP_ENET_TXBD_T f_tx_descr[LIP_LL_TX_DESCR_CNT]);
#include "lcspec_align.h"
static volatile CHIP_ENET_TXBD_T f_tx_dummy;
static t_lip_tx_frame  *f_tx_frames[LIP_LL_TX_DESCR_CNT];
/* Массив данных в который сохраняются входные пакеты */
#include "lcspec_align.h"
LIP_LL_RX_BUF_MEM(static uint8_t f_rx_bufs[LIP_LL_RX_FULL_BUF_SIZE]);




static LINLINE void *f_descr_rx_buf_addr(int idx) {
    return  (void*)&f_rx_bufs[idx*LIP_LL_RX_BUF_SIZE];
}

void f_descr_rx_init(void) {
    memset((void*)&f_rx_descr[0], 0, sizeof(f_rx_descr));

    for (uint8_t idx = 0; idx < LIP_LL_RX_DESCR_CNT; idx++) {
        volatile CHIP_ENET_RXBD_T *rdes = &f_rx_descr[idx];
        uint32_t addr = (intptr_t)f_descr_rx_buf_addr(idx);
        rdes->BUFADDR_H = LIP_LL_DESCR_WRD(addr >> 16);
        rdes->BUFADDR_L = LIP_LL_DESCR_WRD(addr & 0xFFFF);
        rdes->STATUS1 = LIP_LL_DESCR_WRD(CHIP_ENET_RXBD_STATUS1_INT);
        uint16_t status0 = CHIP_ENET_RXBD_STATUS0_EMPTY;
        if (idx == (LIP_LL_RX_DESCR_CNT-1))
            status0 |= CHIP_ENET_RXBD_STATUS0_WRAP;
        rdes->STATUS0 = LIP_LL_DESCR_WRD(status0);
    }

    f_rx_cur_descr = 0;

    CHIP_REGS_ENET1->MRBR = LIP_LL_RX_BUF_SIZE;
    CHIP_REGS_ENET1->RDSR = (intptr_t)&f_rx_descr[0];
}

void f_descr_tx_init(void) {
    memset((void*)&f_tx_descr[0], 0, sizeof(f_tx_descr));
    CHIP_REGS_ENET1->TDSR = (intptr_t)&f_tx_descr[0];

    CHIP_REGS_ENET1->TDSR1 = (intptr_t)&f_tx_dummy;
    CHIP_REGS_ENET1->TDSR2 = (intptr_t)&f_tx_dummy;
    /* сам массив дескрипторов реально инициализируется при начале передачи,
       поэтому здесь доп. инициализация не требуется */
    f_tx_put_descr = f_tx_done_descr = 0;
    f_tx_descr_progr_cnt = 0;

    CHIP_REGS_ENET1->TFWR |= LBITFIELD_SET(CHIP_REGFLD_ENET_TFWR_STRFWD, 1);
}

/* Функция сдвигает текущий указатель на обрабатываемый дескриптор f_rx_cur_descr
 * на cnt дескрпиторов и инициализирует эти cnt дескрипторов для готовности приема
 * новых данных, после чего обновляет f_rx_cur_descr и tail-pointer для DMA,
 * чтобы DMA мог их использовать для нового приема */
static void f_rx_curpos_update(int cnt) {
    uint8_t cur_pos = f_rx_cur_descr;

    for (uint8_t idx = 0; idx < cnt; ++idx) {
        volatile CHIP_ENET_RXBD_T *rdes = &f_rx_descr[cur_pos];
        rdes->STATUS4 = 0; /* clear BDU */
        rdes->STATUS0 |= LIP_LL_DESCR_WRD(CHIP_ENET_RXBD_STATUS0_EMPTY);
        cur_pos = LIP_LL_RX_NEXT_IND(cur_pos);
    }
    f_rx_cur_descr = cur_pos;
    chip_dmb();
    CHIP_REGS_ENET1->RDAR = CHIP_REGFLD_ENET_RDAR_RDAR;
}


/* Функция проходит по завершенным дескрипторам на прием и ищет завершенные
 * полностью кадры. На каждый найденный кадр вызывается lip_mac_rx_proc_frame
 * для обработки данного кадра */
static void f_rx_check_descr(void) {
    int cur_descr_idx = f_rx_cur_descr;
    int start_idx = -1;
    int frame_descr_cnt = 0;
    int proc_cnt = 0;
    int out = 0;

    t_lip_rx_ll_frame rx_frame;

    while (!out && (proc_cnt < LIP_LL_RX_DESCR_CNT)) {
        volatile CHIP_ENET_RXBD_T *rdes = &f_rx_descr[cur_descr_idx];
        const uint16_t status0 = LIP_LL_DESCR_WRD(rdes->STATUS0);
        out = ((status0 & CHIP_ENET_RXBD_STATUS0_EMPTY) != 0);
        if (!out) {
            if (frame_descr_cnt == 0) {
                start_idx = cur_descr_idx;
            }

            frame_descr_cnt++;


            if (status0 & CHIP_ENET_RXBD_STATUS0_LAST) {
                const uint16_t data_len = LIP_LL_DESCR_WRD(rdes->DATALEN);
                const uint16_t status1 = LIP_LL_DESCR_WRD(rdes->STATUS1);
                const uint16_t status2 = LIP_LL_DESCR_WRD(rdes->STATUS2);
                const uint16_t status3 = LIP_LL_DESCR_WRD(rdes->STATUS3);
                uint8_t proto = LBITFIELD_GET(status3, CHIP_ENET_RXBD_STATUS3_PROTO_TYPE);
                /* проверка наличия ошибок в пакете */
                bool has_err = (status0 & (CHIP_ENET_RXBD_STATUS0_ERR_LG | CHIP_ENET_RXBD_STATUS0_ERR_NO | CHIP_ENET_RXBD_STATUS0_ERR_CRC | CHIP_ENET_RXBD_STATUS0_ERR_OVRN))
                               || (status1 & (CHIP_ENET_RXBD_STATUS1_ERR_MAC | CHIP_ENET_RXBD_STATUS1_ERR_PHY | CHIP_ENET_RXBD_STATUS1_ERR_COL));
                if (!has_err) {
                    /* Флаги проверки контрольной суммы IP и Proto устанавливаются даже
                     * если это не IP пакет или с неизвестным протоколом, поэтому их нужно
                     * проверять, только если корректно определен протокол */
                    rx_frame.info.ll_flags = 0;
                    if (proto != 0) {
                        if (status2 & CHIP_ENET_RXBD_STATUS2_ERR_IPHDR_CHK) {
                            has_err = true;
                        } else {
                            rx_frame.info.ll_flags |= LIP_LL_RXFLAGS_IP_CHECKSUM_CHECKED;

                            if (proto == LIP_PROTOCOL_TCP) {
                                if (status2 & CHIP_ENET_RXBD_STATUS2_ERR_PROTO_CHK) {
                                    has_err = true;
                                } else {
                                    rx_frame.info.ll_flags |= LIP_LL_RXFLAGS_TRANSP_CHECKSUM_CHECKED;
                                }
                            } else if (proto == LIP_PROTOCOL_UDP) {
                                if (status2 & CHIP_ENET_RXBD_STATUS2_ERR_PROTO_CHK) {
                                    has_err = true;
                                } else {
                                    rx_frame.info.ll_flags |= LIP_LL_RXFLAGS_TRANSP_CHECKSUM_CHECKED;
                                }
                            } else if (proto == LIP_PROTOCOL_ICMP) {
                                if (status2 & CHIP_ENET_RXBD_STATUS2_ERR_PROTO_CHK) {
                                    has_err = true;
                                } else {
                                    rx_frame.info.ll_flags |= LIP_LL_RXFLAGS_TRANSP_CHECKSUM_CHECKED;
                                }
                            }
                        }
                    }
                }
                if (has_err) {
                    lip_printf(LIP_LOG_MODULE_PORT, LIP_LOG_LVL_DEBUG_HIGH, 0,
                               "lip port: rx buf done with error! status0 = 0x%04X, status1 = 0x%04X, status2 = 0x%04X, proto = 0x%02X\n",
                               status0, status1, status2, proto);
                } else {

                    rx_frame.info.flags = 0;
#if LIP_MULTIPORT_ENABLE
                    rx_frame.info.rx_port = 0;
#endif

                    rx_frame.pl_main.data = f_descr_rx_buf_addr(start_idx);
                    rx_frame.pl_main.size = data_len;
#if LIP_PTP_ENABLE
                    if (f_tstmp_en) {
                        uint32_t ns = (LIP_LL_DESCR_WRD(rdes->TSTMP_H) << 16) | LIP_LL_DESCR_WRD(rdes->TSTMP_L);
                        f_fill_ptp_tstmp(&rx_frame.info.tstmp, ns);
                        rx_frame.info.flags |= LIP_FRAME_RXINFO_FLAG_TSTAMP_VALID;
                    }
#endif
#if LIP_VLAN_ENABLE
                    if (status2 & CHIP_ENET_RXBD_STATUS2_VLAN) {
                        rx_frame.info.vlan_pcp = LBITFIELD_GET(status2, CHIP_ENET_RXBD_STATUS2_VLAN_PCP);
                        rx_frame.info.flags |= LIP_FRAME_RXINFO_FLAG_VLAN_PCP_VALID;
                    }
#endif
                    /* если данные кадра переходят через границу кольцевого буфера (с последнего
                         * на первый дескриптор), то они оказываются не непрерывны.
                         * В конце остается место, чтобы можно было данные из начала
                         * скопировать в конец буфера для непрерывности */
                    if ((start_idx + frame_descr_cnt) > LIP_LL_RX_DESCR_CNT) {
                        rx_frame.pl_tail.size = (t_lip_size)(rx_frame.pl_main.size  - (LIP_LL_RX_DESCR_CNT - start_idx) * LIP_LL_RX_BUF_SIZE);
                        rx_frame.pl_main.size -= rx_frame.pl_tail.size;
                        rx_frame.pl_tail.data = f_descr_rx_buf_addr(0);
                    } else {
                        rx_frame.pl_tail.data = NULL;
                        rx_frame.pl_tail.size = 0;
                    }

                    lip_mac_rx_proc_frame(&rx_frame);
                }

                f_rx_curpos_update(frame_descr_cnt);
                frame_descr_cnt = 0;
            }

            cur_descr_idx = LIP_LL_RX_NEXT_IND(cur_descr_idx);
            proc_cnt++;
        }
    }
}

static void f_tx_check_descr(void) {
    /* проверяем, какие дескрипторы завершились */
    bool out = false;
    while ((f_tx_descr_progr_cnt != 0) && !out) {
        uint8_t tx_descr_num = f_tx_done_descr;
        volatile CHIP_ENET_TXBD_T *tdes = &f_tx_descr[tx_descr_num];
        const uint16_t status0 = LIP_LL_DESCR_WRD(tdes->STATUS0);
        out  = (status0 & CHIP_ENET_TXBD_STATUS0_RDY) != 0;
        if (!out) {
            t_lip_tx_frame  *tx_frame = NULL;
            if (status0 & CHIP_ENET_TXBD_STATUS0_LAST) {
                tx_frame = f_tx_frames[tx_descr_num];
                if (tx_frame) {                    
                    const uint16_t status2 = LIP_LL_DESCR_WRD(tdes->STATUS2);
#if LIP_PTP_ENABLE
                    const uint16_t status1 = LIP_LL_DESCR_WRD(tdes->STATUS1);
                    if ((status1 & CHIP_ENET_TXBD_STATUS1_TSTMP) && !(status2 & CHIP_ENET_TXBD_STATUS2_ERR_TSTMP)) {
                        uint32_t ns = (LIP_LL_DESCR_WRD(tdes->TSTMP_H) << 16) | LIP_LL_DESCR_WRD(tdes->TSTMP_L);
                        f_fill_ptp_tstmp(&tx_frame->status.tstmp, ns);
                        tx_frame->status.flags |= LIP_FRAME_TXSTATUS_FLAG_TSTMP_VALID;
                    }
#endif
                    if (status2 & (CHIP_ENET_TXBD_STATUS2_TX_ERR
                                   | CHIP_ENET_TXBD_STATUS2_ERR_UNFL
                                   | CHIP_ENET_TXBD_STATUS2_ERR_EXC_COL
                                   | CHIP_ENET_TXBD_STATUS2_ERR_FRAME
                                   | CHIP_ENET_TXBD_STATUS2_ERR_LATE_COL
                                   | CHIP_ENET_TXBD_STATUS2_ERR_OVFL
                                   )) {
                        tx_frame->status.flags |= LIP_FRAME_TXSTATUS_FLAG_ERROR;
                        lip_printf(LIP_LOG_MODULE_PORT, LIP_LOG_LVL_DEBUG_HIGH, 0,
                                   "lip port: tx buf done with error! status2 = 0x%04X\n",
                                   status2);
                        /** @todo ovfl требует перезапуск MAC */

                    }
                }
            }

            f_tx_done_descr = LIP_LL_TX_NEXT_IND(tx_descr_num);
            f_tx_descr_progr_cnt--;


            if (tx_frame) {
                tx_frame->status.flags |= LIP_FRAME_TXSTATUS_FLAG_FINISH;
                lip_mac_tx_frame_done_cb(tx_frame);
            }
        }
    }
}

static bool f_tx_descr_rdy(void) {
    return f_tx_descr_progr_cnt < LIP_LL_TX_DESCR_CNT-1;
}

static void f_enet1_isr(const t_chip_isr_ctx *ctx) {
    uint32_t irq_flags = CHIP_REGS_ENET1->EIR
#if !LIP_LL_PORT_RX_INTERRUPT_ENABLE
                        & ~CHIP_REGFLD_ENET_EIR_RXF
#endif
#if !LIP_LL_PORT_RX_INTERRUPT_ENABLE
                        & ~CHIP_REGFLD_ENET_EIR_TXF
#endif
        ;

    if (irq_flags != 0) {
    CHIP_REGS_ENET1->EIR = irq_flags;
#if LIP_PTP_ENABLE
        if (irq_flags & CHIP_REGFLD_ENET_EIR_TS_TIMER) {
            ++f_tstmp_secs;
        }
#endif
#if LIP_LL_PORT_RX_INTERRUPT_ENABLE
        if (irq_flags & CHIP_REGFLD_ENET_EIR_RXF) {
            f_rx_check_descr();
        }
#endif
#if LIP_LL_PORT_TX_INTERRUPT_ENABLE
        if (irq_flags & CHIP_REGFLD_ENET_EIR_TXF) {
            f_tx_check_descr();
        }
#endif
    }
}


static void f_eth_init(void) {
    chip_interrupt_set_isr(CHIP_IRQNUM_ENET1_RX_TX_DONE_0, f_enet1_isr);
    chip_interrupt_set_isr(CHIP_IRQNUM_ENET1_RX_TX_DONE_1, f_enet1_isr);
    chip_interrupt_set_isr(CHIP_IRQNUM_ENET1, f_enet1_isr);
    chip_interrupt_set_isr(CHIP_IRQNUM_ENET1_PTP, f_enet1_isr);

    chip_interrupt_enable(CHIP_IRQNUM_ENET1_RX_TX_DONE_0);
    chip_interrupt_enable(CHIP_IRQNUM_ENET1_RX_TX_DONE_1);
    chip_interrupt_enable(CHIP_IRQNUM_ENET1);
    chip_interrupt_enable(CHIP_IRQNUM_ENET1_PTP);

    CHIP_REGS_ENET1->EIMR =  0
#if LIP_LL_PORT_RX_INTERRUPT_ENABLE
                            | CHIP_REGFLD_ENET_EIR_RXF
#endif
#if LIP_LL_PORT_TX_INTERRUPT_ENABLE
                            | CHIP_REGFLD_ENET_EIR_TXF
#endif
        ;



    CHIP_REGS_ENET1->RSFL = 0;

    f_mac_init_finish = true;
}

static void f_eth_config(const t_lip_phy_mode *mode, uint32_t init_flags) {
    CHIP_REGS_ENET1->RCR =
        LBITFIELD_SET(CHIP_REGFLD_ENET_RCR_LOOP,  init_flags & LIP_INIT_FLAGS_MAC_LOOPBACK ? 1 : 0)
        | LBITFIELD_SET(CHIP_REGFLD_ENET_RCR_DRT, ((mode->flags & LIP_PHY_MODE_FLAG_DUPLEX_FULL)
                                                 || (init_flags & LIP_INIT_FLAGS_MAC_LOOPBACK)) ? 0 : 1) /* disable self rx on hd */
        | LBITFIELD_SET(CHIP_REGFLD_ENET_RCR_MII_MODE, 1) /* must be always 1 */
        | LBITFIELD_SET(CHIP_REGFLD_ENET_RCR_PROM, (init_flags & LIP_INIT_FLAGS_MAC_RX_FILTER_DIS) ? 1 : 0)
        | LBITFIELD_SET(CHIP_REGFLD_ENET_RCR_BC_REJ, 0)
        | LBITFIELD_SET(CHIP_REGFLD_ENET_RCR_FCE, 0) /** @todo configurable flow control (mac flag?) */
        | LBITFIELD_SET(CHIP_REGFLD_ENET_RCR_RGMII_EN, init_flags & LIP_INIT_FLAGS_MAC_LOOPBACK ? 0 : 1) /** @todo configurable phy iface */
        | LBITFIELD_SET(CHIP_REGFLD_ENET_RCR_RMII_MODE, init_flags & LIP_INIT_FLAGS_MAC_LOOPBACK ? 0 : 0) /** @todo configurable phy iface */
        | LBITFIELD_SET(CHIP_REGFLD_ENET_RCR_RMII_10T, mode->speed == LIP_PHY_SPEED_10 ? 1 : 0)
        | LBITFIELD_SET(CHIP_REGFLD_ENET_RCR_PADEN, (init_flags & LIP_INIT_FLAGS_MAC_RX_HWPROC_DIS) ? 0 : 1)  /* remove PAD on receive if not disabled*/
        | LBITFIELD_SET(CHIP_REGFLD_ENET_RCR_PAUFWD, 0)  /* discard PAUSE frame */
        | LBITFIELD_SET(CHIP_REGFLD_ENET_RCR_CRCFWD, (init_flags & LIP_INIT_FLAGS_MAC_RX_HWPROC_DIS) ? 0 : 1)  /* strip CRC from frame if not disabled */
        | LBITFIELD_SET(CHIP_REGFLD_ENET_RCR_CFEN, 0)  /* accept MAC frames other than 0x0001 (pause frame) */
        | LBITFIELD_SET(CHIP_REGFLD_ENET_RCR_MAX_FL, LIP_ETH_MAX_VLAN_FULL_PACKET_SIZE)  /* accept MAC frames other than 0x0001 (pause frame) */
        | LBITFIELD_SET(CHIP_REGFLD_ENET_RCR_NLC, 1) /* enable len check */
        ;
    CHIP_REGS_ENET1->TCR =
        LBITFIELD_SET(CHIP_REGFLD_ENET_TCR_FDEN, ((mode->flags & LIP_PHY_MODE_FLAG_DUPLEX_FULL)
                                                 || (init_flags & LIP_INIT_FLAGS_MAC_LOOPBACK))  ? 1 : 0) /* full duplex transmit */
        | LBITFIELD_SET(CHIP_REGFLD_ENET_TCR_TFC_PAUSE, 0) /** @todo configurable flow control */
        | LBITFIELD_SET(CHIP_REGFLD_ENET_TCR_RFC_PAUSE, 0) /** @todo configurable flow control */
        | LBITFIELD_SET(CHIP_REGFLD_ENET_TCR_ADDSEL, 0) /* support only addr0 */
        | LBITFIELD_SET(CHIP_REGFLD_ENET_TCR_ADDINS, 0) /* замена MAC настраивается только для всего кадра. Т.е. нет возможности
                                                           обработать по дескриптору, не используем данную функцию */
        | LBITFIELD_SET(CHIP_REGFLD_ENET_TCR_CRCFWD, 0) /* добавление CRC определяется по дескриптору */
        ;


    CHIP_REGS_ENET1->ECR = CHIP_REGMSK_ENET_ECR_W1
        | LBITFIELD_SET(CHIP_REGFLD_ENET_ECR_RESET, 0)
        | LBITFIELD_SET(CHIP_REGFLD_ENET_ECR_ETHEREN, 0)
        | LBITFIELD_SET(CHIP_REGFLD_ENET_ECR_MAGICEN, 0)
        | LBITFIELD_SET(CHIP_REGFLD_ENET_ECR_SLEEP, 0)
        | LBITFIELD_SET(CHIP_REGFLD_ENET_ECR_EN1588, 1)
        | LBITFIELD_SET(CHIP_REGFLD_ENET_ECR_SPEED, mode->speed == LIP_PHY_SPEED_1000 ? 1 : 0)
        | LBITFIELD_SET(CHIP_REGFLD_ENET_ECR_DBGEN, 0)
        | LBITFIELD_SET(CHIP_REGFLD_ENET_ECR_DBSWP, 0) /* not support descriptor byte swap */
        | LBITFIELD_SET(CHIP_REGFLD_ENET_ECR_SVLANEN, 0) /* S-VLAN enable */
        | LBITFIELD_SET(CHIP_REGFLD_ENET_ECR_VLANUSE2ND, 0) /* VLAN sec tag enable */
        | LBITFIELD_SET(CHIP_REGFLD_ENET_ECR_SVLANDBL, 0) /* SVLAN sec tag enable */
        ;


    CHIP_REGS_ENET1->QOS = LBITFIELD_SET(CHIP_REGFLD_ENET_QOS_TX_SCHEME, CHIP_REGFLDVAL_ENET_QOS_TX_SCHEME_RR);
}

void f_eth_start(void) {

    f_descr_rx_init();
    f_descr_tx_init();
    chip_dmb();
    CHIP_REGS_ENET1->ECR |= CHIP_REGFLD_ENET_ECR_ETHEREN;
    /* Запрос на обновление информации по дескрипторам всех очередей */
    CHIP_REGS_ENET1->RDAR = CHIP_REGFLD_ENET_RDAR_RDAR;
    CHIP_REGS_ENET1->TDAR = CHIP_REGFLD_ENET_TDAR_TDAR;
    /* даже если используем одну очередь на передачу, все равно нужно проинициализировать
     * указатели всех очередей, иначе просто MAC не сможет проверить все очереди, чтобы режить
     * из какой передавать */
    CHIP_REGS_ENET1->TDAR1 = CHIP_REGFLD_ENET_TDAR_TDAR;
    CHIP_REGS_ENET1->TDAR2 = CHIP_REGFLD_ENET_TDAR_TDAR;

}


t_lip_errs lip_ll_init(t_lip_init_stage stage, int flags) {
    t_lip_errs err = LIP_ERR_SUCCESS;


    if (stage == LIP_INIT_STAGE_PHY_INTF_INIT) {
        chip_per_clk_en(CHIP_PER_CLK_NUM_ENET1);

        CHIP_PIN_CONFIG(LIP_ETH1_PIN_MDC);
        CHIP_PIN_CONFIG(LIP_ETH1_PIN_MDIO);
        CHIP_PIN_CONFIG(LIP_ETH1_PIN_RGMII_RD0);
        CHIP_PIN_CONFIG(LIP_ETH1_PIN_RGMII_RD1);
        CHIP_PIN_CONFIG(LIP_ETH1_PIN_RGMII_RD2);
        CHIP_PIN_CONFIG(LIP_ETH1_PIN_RGMII_RD3);
        CHIP_PIN_CONFIG(LIP_ETH1_PIN_RGMII_RX_CTL);
        CHIP_PIN_CONFIG(LIP_ETH1_PIN_RGMII_RXC);


        CHIP_PIN_CONFIG(LIP_ETH1_PIN_RGMII_TD0);
        CHIP_PIN_CONFIG(LIP_ETH1_PIN_RGMII_TD1);
        CHIP_PIN_CONFIG(LIP_ETH1_PIN_RGMII_TD2);
        CHIP_PIN_CONFIG(LIP_ETH1_PIN_RGMII_TD3);
        CHIP_PIN_CONFIG(LIP_ETH1_PIN_RGMII_TX_CTL);
        CHIP_PIN_CONFIG(LIP_ETH1_PIN_RGMII_TXC);
    } else if (stage == LIP_INIT_STAGE_MAC_INIT) {
        f_eth_init();
        /** @todo config */
#if LIP_PTP_ENABLE
        lip_ll_tstmp_enable();
#endif
    } else if (stage == LIP_INIT_STAGE_MAC_START) {
        /* т.к. данная функция вызывается в том числе и при смене настроек то
         * сперва запрещаем MAC, если был разрешен, изменяем настройки, и запускаем
         * заново */
        lip_ll_stop();
        f_eth_config(lip_mac_phy_mode(), flags);
        f_eth_start();        
    }

    return err;
}

void lip_ll_stop(void) {
    if (CHIP_REGS_ENET1->ECR & CHIP_REGFLD_ENET_ECR_ETHEREN) {
        /* Запускаем останов для передачи, чтобы закончить без обрыва данных.
           Для приема отдельного запроса нет */
        CHIP_REGS_ENET1->EIR = CHIP_REGFLD_ENET_EIR_GRA;

        CHIP_REGS_ENET1->TCR |= CHIP_REGFLD_ENET_TCR_GTS;

        while ((CHIP_REGS_ENET1->EIR & CHIP_REGFLD_ENET_EIR_GRA) == 0) {
        }
        CHIP_REGS_ENET1->ECR &= ~CHIP_REGFLD_ENET_ECR_ETHEREN;
    }
}

void lip_ll_close(void) {
    f_tstm_stop();
    CHIP_REGS_ENET1->MSCR = 0; /* disable MDIO clock */



    CHIP_PIN_CONFIG_DIS(LIP_ETH1_PIN_MDC);
    CHIP_PIN_CONFIG_DIS(LIP_ETH1_PIN_MDIO);
    CHIP_PIN_CONFIG_DIS(LIP_ETH1_PIN_RGMII_RD0);
    CHIP_PIN_CONFIG_DIS(LIP_ETH1_PIN_RGMII_RD1);
    CHIP_PIN_CONFIG_DIS(LIP_ETH1_PIN_RGMII_RD2);
    CHIP_PIN_CONFIG_DIS(LIP_ETH1_PIN_RGMII_RD3);
    CHIP_PIN_CONFIG_DIS(LIP_ETH1_PIN_RGMII_RX_CTL);
    CHIP_PIN_CONFIG_DIS(LIP_ETH1_PIN_RGMII_RXC);


    CHIP_PIN_CONFIG_DIS(LIP_ETH1_PIN_RGMII_TD0);
    CHIP_PIN_CONFIG_DIS(LIP_ETH1_PIN_RGMII_TD1);
    CHIP_PIN_CONFIG_DIS(LIP_ETH1_PIN_RGMII_TD2);
    CHIP_PIN_CONFIG_DIS(LIP_ETH1_PIN_RGMII_TD3);
    CHIP_PIN_CONFIG_DIS(LIP_ETH1_PIN_RGMII_TX_CTL);
    CHIP_PIN_CONFIG_DIS(LIP_ETH1_PIN_RGMII_TXC);

    chip_per_clk_dis(CHIP_PER_CLK_NUM_ENET1);
}

void lip_ll_set_mac_addr(const uint8_t *mac_add) {
    CHIP_REGS_ENET1->PALR = (mac_addr[0] << 24)
                            | (mac_addr[1] << 16)
                            | (mac_addr[2] <<  8)
                            | (mac_addr[3] <<  0);
    CHIP_REGS_ENET1->PAUR = (mac_addr[4] << 24)
                            | (mac_addr[5] << 16);
}


t_lip_errs lip_ll_mdio_write_start(uint8_t phyaddr, uint8_t regaddr, uint16_t value) {
    /* Запуск операции идет либо при смене MSCR с 0 не не 0, либо при
     * записи MMFR, если MSCR не 0. Т.к. мы обнуляем MSCR в 0, чтобы не генерить клок
     * без обращения, то сперва необходима сделать запись в MMFR, а затем уже
     * по записи в MSCR начинается операция. */
    CHIP_REGS_ENET1->MMFR = LBITFIELD_SET(CHIP_REGFLD_ENET_MMFR_ST, LIP_PHY_MDIO_FRAME_SOF)
    | LBITFIELD_SET(CHIP_REGFLD_ENET_MMFR_OP, LIP_PHY_MDIO_FRAME_OP_WR)
        | LBITFIELD_SET(CHIP_REGFLD_ENET_MMFR_PA, phyaddr)
        | LBITFIELD_SET(CHIP_REGFLD_ENET_MMFR_RA, regaddr)
        | LBITFIELD_SET(CHIP_REGFLD_ENET_MMFR_TA, LIP_PHY_MDIO_FRAME_TA_WR)
        | LBITFIELD_SET(CHIP_REGFLD_ENET_MMFR_DATA, value)
        ;
    CHIP_REGS_ENET1->MSCR =  LBITFIELD_SET(CHIP_REGFLD_ENET_MSCR_HOLDTIME, MDIO_RVAL_HOLDTIME)
                            | LBITFIELD_SET(CHIP_REGFLD_ENET_MSCR_DIS_PRE, 0)
                            | LBITFIELD_SET(CHIP_REGFLD_ENET_MSCR_MII_SPEED, MDIO_RVAL_SPEED)
        ;


    return LIP_ERR_SUCCESS;
}




t_lip_errs lip_ll_mdio_read_start(uint8_t phyaddr, uint8_t regaddr) {
    CHIP_REGS_ENET1->EIR = CHIP_REGFLD_ENET_EIR_MII;

    CHIP_REGS_ENET1->MMFR = LBITFIELD_SET(CHIP_REGFLD_ENET_MMFR_ST, LIP_PHY_MDIO_FRAME_SOF)
                            | LBITFIELD_SET(CHIP_REGFLD_ENET_MMFR_OP, LIP_PHY_MDIO_FRAME_OP_RD)
                            | LBITFIELD_SET(CHIP_REGFLD_ENET_MMFR_PA, phyaddr)
                            | LBITFIELD_SET(CHIP_REGFLD_ENET_MMFR_RA, regaddr)
                            | LBITFIELD_SET(CHIP_REGFLD_ENET_MMFR_TA, LIP_PHY_MDIO_FRAME_TA_WR)
                            | LBITFIELD_SET(CHIP_REGFLD_ENET_MMFR_DATA, 0x00)
        ;

    CHIP_REGS_ENET1->MSCR =  LBITFIELD_SET(CHIP_REGFLD_ENET_MSCR_HOLDTIME, MDIO_RVAL_HOLDTIME)
                            | LBITFIELD_SET(CHIP_REGFLD_ENET_MSCR_DIS_PRE, 0)
                            | LBITFIELD_SET(CHIP_REGFLD_ENET_MSCR_MII_SPEED, MDIO_RVAL_SPEED)
        ;


    return LIP_ERR_SUCCESS;
}

bool lip_ll_mdio_rdy(void) {
    bool rdy = CHIP_REGS_ENET1->MSCR == 0;
    if (!rdy && (CHIP_REGS_ENET1->EIR & CHIP_REGFLD_ENET_EIR_MII)) {
        CHIP_REGS_ENET1->EIR = CHIP_REGFLD_ENET_EIR_MII;
        CHIP_REGS_ENET1->MSCR = 0;
        rdy = true;
    }
    return rdy;
}

t_lip_errs lip_ll_mdio_data(uint16_t *data) {
    *data = LBITFIELD_GET(CHIP_REGS_ENET1->MMFR, CHIP_REGFLD_ENET_MMFR_DATA);
    return LIP_ERR_SUCCESS;
}



t_lip_errs lip_ll_tx_frame(t_lip_tx_frame *frame) {

    t_lip_errs res = f_tx_descr_rdy() ?  LIP_ERR_SUCCESS : LIP_ERR_BUF_NOT_RDY;

    if (res == LIP_ERR_SUCCESS) {
        volatile CHIP_ENET_TXBD_T *tdes = &f_tx_descr[f_tx_put_descr];
        uint32_t hdr_size = frame->buf.cur_ptr - frame->buf.start_ptr;
        const uint32_t data_size = frame->exdata_size;



        uint32_t hdr_addr = (intptr_t)frame->buf.start_ptr;
        tdes->BUFADDR_H = LIP_LL_DESCR_WRD(hdr_addr >> 16);
        tdes->BUFADDR_L = LIP_LL_DESCR_WRD(hdr_addr & 0xFFFF);
        tdes->DATALEN   = LIP_LL_DESCR_WRD(hdr_size);

        uint16_t status0 = LBITFIELD_SET(CHIP_ENET_TXBD_STATUS0_RDY, 1)
                           | LBITFIELD_SET(CHIP_ENET_TXBD_STATUS0_LAST, data_size == 0)
                           | LBITFIELD_SET(CHIP_ENET_TXBD_STATUS0_TX_CRC, !(frame->info.flags & LIP_FRAME_TXINFO_FLAG_RAW))
                           | LBITFIELD_SET(CHIP_ENET_TXBD_STATUS0_WRAP, f_tx_put_descr == (LIP_LL_TX_DESCR_CNT - 1))
            ;
        uint16_t status1 = LBITFIELD_SET(CHIP_ENET_TXBD_STATUS1_INT, 1)

#if LIP_PTP_ENABLE
                            | LBITFIELD_SET(CHIP_ENET_TXBD_STATUS1_TSTMP, f_tstmp_en && (frame->info.flags & LIP_FRAME_TXINFO_FLAG_TSTAMP_REQ))
#endif
                            | LBITFIELD_SET(CHIP_ENET_TXBD_STATUS1_PROTO_CHK_INS, !(frame->info.flags & LIP_FRAME_TXINFO_FLAG_RAW)) /* Ptotocol checksum calculation */
                            | LBITFIELD_SET(CHIP_ENET_TXBD_STATUS1_IP_CHK_INS, !(frame->info.flags & LIP_FRAME_TXINFO_FLAG_RAW)) /* IP checksum calculation */
                            | LBITFIELD_SET(CHIP_ENET_TXBD_STATUS1_USE_TLT, 0)
                            | LBITFIELD_SET(CHIP_ENET_TXBD_STATUS1_FRAME_TYPE, 0) /** @todo - номер очереди */

                ;
        tdes->STATUS1 = LIP_LL_DESCR_WRD(status1);
        tdes->STATUS4 = 0;
        tdes->STATUS0 = LIP_LL_DESCR_WRD(status0);

        if (data_size > 0) {
            f_tx_frames[f_tx_put_descr] = NULL;
            f_tx_put_descr = LIP_LL_TX_NEXT_IND(f_tx_put_descr);
            volatile CHIP_ENET_TXBD_T *tdes2 = &f_tx_descr[f_tx_put_descr];
            uint32_t data_addr = (intptr_t)frame->exdata_ptr;
            tdes2->BUFADDR_H = LIP_LL_DESCR_WRD(data_addr >> 16);
            tdes2->BUFADDR_L = LIP_LL_DESCR_WRD(data_addr & 0xFFFF);
            tdes2->DATALEN   = LIP_LL_DESCR_WRD(data_size);

            uint16_t status0_data = LBITFIELD_SET(CHIP_ENET_TXBD_STATUS0_RDY, 1)
                               | LBITFIELD_SET(CHIP_ENET_TXBD_STATUS0_LAST, 1)
                               | LBITFIELD_SET(CHIP_ENET_TXBD_STATUS0_TX_CRC, !(frame->info.flags & LIP_FRAME_TXINFO_FLAG_RAW))
                               | LBITFIELD_SET(CHIP_ENET_TXBD_STATUS0_WRAP, f_tx_put_descr == (LIP_LL_TX_DESCR_CNT - 1));
            tdes2->STATUS1 = LIP_LL_DESCR_WRD(status1);
            tdes2->STATUS4 = 0;
            tdes2->STATUS0 = LIP_LL_DESCR_WRD(status0_data);
            f_tx_descr_progr_cnt++;
        }
        f_tx_frames[f_tx_put_descr] = frame;
        f_tx_put_descr = LIP_LL_TX_NEXT_IND(f_tx_put_descr);
        f_tx_descr_progr_cnt++;
        chip_dmb();

        CHIP_REGS_ENET1->TDAR = CHIP_REGFLD_ENET_TDAR_TDAR;
    }

    return res;
}

#ifdef LIP_USE_IGMP
t_lip_errs lip_ll_join(const uint8_t *mac) {
    uint32_t crc = CRC32_Block8(0, mac, LIP_MAC_ADDR_SIZE) ^ 0xFFFFFFFF;
    uint8_t bit_num = (crc >> 26) & 0x1F;

    if (crc & (0x1UL << 31)) {
        CHIP_REGS_ENET1->GAUR |= 1 << bit_num;
    } else {
        CHIP_REGS_ENET1->GALR |= 1 << bit_num;
    }
    return LIP_ERR_SUCCESS;
}

t_lip_errs lip_ll_leave(const uint8_t *mac) {
    uint32_t crc = CRC32_Block8(0, mac, LIP_MAC_ADDR_SIZE) ^ 0xFFFFFFFF;
    uint8_t bit_num = (crc >> 26) & 0x1F;

    if (crc & (0x1UL << 31)) {
        CHIP_REGS_ENET1->GAUR &= ~(1 << bit_num);
    } else {
        CHIP_REGS_ENET1->GALR &= ~(1 << bit_num);
    }
    return LIP_ERR_SUCCESS;
}
#endif


void lip_ll_irq_enable(void) {
    chip_interrupt_enable(CHIP_IRQNUM_ENET1);
}
void lip_ll_irq_disable(void) {
    chip_interrupt_disable(CHIP_IRQNUM_ENET1);
}


static void f_tstm_stop(void) {
    CHIP_REGS_ENET1->ATCR  = CHIP_REGMSK_ENET_ATCR_W1 | CHIP_REGFLD_ENET_ATCR_RESTART;
}

void lip_ll_pull(void) {
#if !LIP_LL_PORT_RX_INTERRUPT_ENABLE || !LIP_LL_PORT_RX_INTERRUPT_ENABLE
    uint32_t irq_flags = CHIP_REGS_ENET1->EIR &
                         (0
#if !LIP_LL_PORT_RX_INTERRUPT_ENABLE
                         | CHIP_REGFLD_ENET_EIR_RXF
#endif
#if !LIP_LL_PORT_RX_INTERRUPT_ENABLE
                         | CHIP_REGFLD_ENET_EIR_TXF
#endif
                        );

    if (irq_flags != 0) {
        CHIP_REGS_ENET1->EIR = irq_flags;
#if !LIP_LL_PORT_RX_INTERRUPT_ENABLE
        if (irq_flags & CHIP_REGFLD_ENET_EIR_RXF) {
            f_rx_check_descr();
        }
#endif
#if !LIP_LL_PORT_TX_INTERRUPT_ENABLE
        if (irq_flags & CHIP_REGFLD_ENET_EIR_TXF) {
            f_tx_check_descr();
        }
#endif
    }
#endif
}

#if LIP_PTP_ENABLE

static void f_tstm_start(void) {
    uint32_t inc_val = 1000000000/CHIP_CLK_ROOT_ENET_TIMER_FREQ;
    CHIP_REGS_ENET1->ATINC = LBITFIELD_SET(CHIP_REGFLD_ENET_ATINC_INC, inc_val);
    CHIP_REGS_ENET1->ATPER = 1000000000;
    CHIP_REGS_ENET1->ATCR  = CHIP_REGMSK_ENET_ATCR_W1
                            | CHIP_REGFLD_ENET_ATCR_EN
                            | CHIP_REGFLD_ENET_ATCR_PEREN
        ;

}

void lip_ll_tstmp_enable(void) {
    if (!f_tstmp_en) {
        f_tstmp_en = true;
        if (f_mac_init_finish) {
            f_tstm_start();
        }
    }
}

void lip_ll_tstmp_disable(void) {
    if (f_tstmp_en) {
        f_tstmp_en = false;
        if (f_mac_init_finish) {
            f_tstm_stop();
        }
    }
}

bool lip_ll_tstmp_is_enabled(void) {
    return f_tstmp_en;
}

t_lip_errs lip_ll_tstmp_read(t_lip_ptp_tstmp *tstmp) {
    CHIP_REGS_ENET1->ATCR = CHIP_REGFLD_ENET_ATCR_CAPTURE;

    for (int i = 0; i < 6; ++i) {
        (void)CHIP_REGS_ENET1->ATCR;
    }

    f_fill_ptp_tstmp(tstmp, CHIP_REGS_ENET1->ATVR);

    return LIP_ERR_SUCCESS;
}

#endif



