#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"

/* !!! DMA для EMAC в T113s работает только с DDR памятью и не работает с
 * внутренней SRAM, поэтому необходимо чтобы декскриторы DMA, буфер на прием
 * и буфер на передачу все располагались именно в DDR.
 * Кроме того дескрипторы должны располагаться в некашируемой области памяти для
 * избежания кофликтов между синхронизацией кэша и emac dma
 * (определяется через LIP_CFG_LL_DESCR_MEM)
 *
 * Буфер для данных на прием и передачу как правило располагаются в кэшируемой области
 * DDR памяти. Это определяется через LIP_CFG_LL_RX_BUF_MEM на прием и через LIP_MAC_TX_BUF_MEM
 * на передачу (или через LIP_CFG_DEFAULT_STATIC_BUF_MEM для всех статических буферов
 * стека) */

#if !LIP_MAC_NO_PHY
#ifdef LIP_CFG_LL_EMAC_PIN_MDC
    #define LIP_LL_EMAC_PIN_MDC          LIP_CFG_LL_EMAC_PIN_MDC
#else
    #define LIP_LL_EMAC_PIN_MDC          CHIP_PIN_PG14_MDC
#endif
#ifdef LIP_CFG_LL_EMAC_PIN_MDIO
    #define LIP_LL_EMAC_PIN_MDIO         LIP_CFG_LL_EMAC_PIN_MDIO
#else
    #define LIP_LL_EMAC_PIN_MDIO         CHIP_PIN_PG15_MDIO
#endif
#endif
#ifdef LIP_CFG_LL_EMAC_PIN_RGMII_RXD0
    #define LIP_LL_EMAC_PIN_RGMII_RXD0   LIP_CFG_LL_EMAC_PIN_RGMII_RXD0
#else
    #define LIP_LL_EMAC_PIN_RGMII_RXD0   CHIP_PIN_PG1_RGMII_RXD0
#endif
#ifdef LIP_CFG_LL_EMAC_PIN_RGMII_RXD1
    #define LIP_LL_EMAC_PIN_RGMII_RXD1   LIP_CFG_LL_EMAC_PIN_RGMII_RXD1
#else
    #define LIP_LL_EMAC_PIN_RGMII_RXD1   CHIP_PIN_PG2_RGMII_RXD1
#endif
#ifdef LIP_CFG_LL_EMAC_PIN_RGMII_RXD2
    #define LIP_LL_EMAC_PIN_RGMII_RXD2   LIP_CFG_LL_EMAC_PIN_RGMII_RXD2
#else
    #define LIP_LL_EMAC_PIN_RGMII_RXD2   CHIP_PIN_PG8_RGMII_RXD2
#endif
#ifdef LIP_CFG_LL_EMAC_PIN_RGMII_RXD3
    #define LIP_LL_EMAC_PIN_RGMII_RXD3   LIP_CFG_LL_EMAC_PIN_RGMII_RXD3
#else
    #define LIP_LL_EMAC_PIN_RGMII_RXD3   CHIP_PIN_PG9_RGMII_RXD3
#endif
#ifdef LIP_CFG_LL_EMAC_PIN_RGMII_RXCTRL
    #define LIP_LL_EMAC_PIN_RGMII_RXCTRL LIP_CFG_LL_EMAC_PIN_RGMII_RXCTRL
#else
    #define LIP_LL_EMAC_PIN_RGMII_RXCTRL CHIP_PIN_PG0_RGMII_RXCTRL
#endif
#ifdef LIP_CFG_LL_EMAC_PIN_RGMII_RXCK
    #define LIP_LL_EMAC_PIN_RGMII_RXCK  LIP_CFG_LL_EMAC_PIN_RGMII_RXCK
#else
    #define LIP_LL_EMAC_PIN_RGMII_RXCK  CHIP_PIN_PG10_RGMII_RXCK
#endif
#ifdef LIP_CFG_LL_EMAC_PIN_RGMII_TXD0
    #define LIP_LL_EMAC_PIN_RGMII_TXD0   LIP_CFG_LL_EMAC_PIN_RGMII_TXD0
#else
    #define LIP_LL_EMAC_PIN_RGMII_TXD0   CHIP_PIN_PG4_RGMII_TXD0
#endif
#ifdef LIP_CFG_LL_EMAC_PIN_RGMII_TXD1
    #define LIP_LL_EMAC_PIN_RGMII_TXD1   LIP_CFG_LL_EMAC_PIN_RGMII_TXD1
#else
    #define LIP_LL_EMAC_PIN_RGMII_TXD1   CHIP_PIN_PG5_RGMII_TXD1
#endif
#ifdef LIP_CFG_LL_EMAC_PIN_RGMII_TXD2
    #define LIP_LL_EMAC_PIN_RGMII_TXD2   LIP_CFG_LL_EMAC_PIN_RGMII_TXD2
#else
    #define LIP_LL_EMAC_PIN_RGMII_TXD2   CHIP_PIN_PG6_RGMII_TXD2
#endif
#ifdef LIP_CFG_LL_EMAC_PIN_RGMII_TXD3
    #define LIP_LL_EMAC_PIN_RGMII_TXD3   LIP_CFG_LL_EMAC_PIN_RGMII_TXD3
#else
    #define LIP_LL_EMAC_PIN_RGMII_TXD3   CHIP_PIN_PG7_RGMII_TXD3
#endif
#ifdef LIP_CFG_LL_EMAC_PIN_RGMII_TXCTRL
    #define LIP_LL_EMAC_PIN_RGMII_TXCTRL LIP_CFG_LL_EMAC_PIN_RGMII_TXCTRL
#else
    #define LIP_LL_EMAC_PIN_RGMII_TXCTRL CHIP_PIN_PG12_RGMII_TXCTRL
#endif
#ifdef LIP_CFG_LL_EMAC_PIN_RGMII_TXCK
    #define LIP_LL_EMAC_PIN_RGMII_TXCK  LIP_CFG_LL_EMAC_PIN_RGMII_TXCK
#else
    #define LIP_LL_EMAC_PIN_RGMII_TXCK  CHIP_PIN_PG3_RGMII_TXCK
#endif
#ifdef LIP_CFG_LL_EMAC_PIN_RGMII_CLKIN
    #define LIP_LL_EMAC_PIN_RGMII_CLKIN LIP_CFG_LL_EMAC_PIN_RGMII_CLKIN
#endif

#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         LIP_MAC_TX_FRAME_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_RVAL_CMD_MDC_DIV_RATIO      CHIP_REGFLDVAL_EMAC_MII_CMD_MDC_DIV_RATIO_M_128

#define LSPEC_ALIGNMENT 32
#include "lcspec_align.h"
LIP_LL_DESCR_MEM(static volatile CHIP_EMAC_BD_T f_rx_descr[LIP_LL_RX_DESCR_CNT]);
#include "lcspec_align.h"
LIP_LL_DESCR_MEM(static volatile CHIP_EMAC_BD_T f_tx_descr[LIP_LL_TX_DESCR_CNT]);
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]);

typedef uint32_t t_descr_pos;

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

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

static void f_dma_reset(void) {
    CHIP_REGS_EMAC->RX_CTL1 = 0;
    CHIP_REGS_EMAC->TX_CTL1 = 0;
    CHIP_REGS_EMAC->RX_FRM_FLT = 0;
    CHIP_REGS_EMAC->RX_DMA_DESC_LIST = 0;
    CHIP_REGS_EMAC->TX_DMA_DESC_LIST = 0;
    CHIP_REGS_EMAC->INT_EN = 0;
}

static void f_dma_tx_descrs_init(void) {

    for (unsigned i = 0; i < LIP_LL_TX_DESCR_CNT; ++i) {
        f_tx_descr[i].NEXT_DESC_ADDR = (i != (LIP_LL_TX_DESCR_CNT - 1)) ? (uint32_t)&f_tx_descr[i+1] : (uint32_t)&f_tx_descr[0];
        f_tx_descr[i].BUF_ADDR = 0;
        f_tx_descr[i].SIZE = 0;
        f_tx_descr[i].STATUS = 0;
    }
    f_tx_put_descr = f_tx_done_descr = 0;
    f_tx_descr_progr_cnt = 0;
    CHIP_REGS_EMAC->TX_DMA_DESC_LIST = (uint32_t)&f_tx_descr[0];
}

static void f_dma_rx_descrs_init(void) {

    for (unsigned i = 0; i < LIP_LL_RX_DESCR_CNT; ++i) {
        f_rx_descr[i].NEXT_DESC_ADDR = (i != (LIP_LL_RX_DESCR_CNT - 1)) ? (uint32_t)&f_rx_descr[i+1] : (uint32_t)&f_rx_descr[0];
        f_rx_descr[i].BUF_ADDR = (uint32_t)f_descr_rx_buf_addr(i);
        f_rx_descr[i].SIZE = LBITFIELD_SET(CHIP_ENET_RXBD_SIZE_BUF_SIZE, LIP_LL_RX_BUF_SIZE);
        f_rx_descr[i].STATUS = CHIP_ENET_RXBD_STATUS_DESC_CTL;
    }
    f_rx_cur_descr = 0;
    CHIP_REGS_EMAC->RX_DMA_DESC_LIST = (uint32_t)&f_rx_descr[0];
}

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

    for (t_descr_pos idx = 0; idx < cnt; ++idx) {
        volatile CHIP_EMAC_BD_T *rdes = &f_rx_descr[cur_pos];
        rdes->STATUS = CHIP_ENET_RXBD_STATUS_DESC_CTL;
        cur_pos = LIP_LL_RX_NEXT_IND(cur_pos);
    }
    f_rx_cur_descr = cur_pos;
    /** @todo cache */
    chip_dmb();
    CHIP_REGS_EMAC->RX_CTL1 |= CHIP_REGFLD_EMAC_RX_CTL1_RX_DMA_START;
}


static void f_eth_config(const t_lip_phy_mode *mode, uint32_t init_flags) {
    uint8_t speed_code = mode->speed == LIP_PHY_SPEED_1000 ? CHIP_REGFLDVAL_EMAC_BASIC_CTL0_SPEED_1000 :
                         mode->speed == LIP_PHY_SPEED_100 ? CHIP_REGFLDVAL_EMAC_BASIC_CTL0_SPEED_100 :
                                                            CHIP_REGFLDVAL_EMAC_BASIC_CTL0_SPEED_10;

    CHIP_REGS_EMAC->BASIC_CTL0 =
            LBITFIELD_SET(CHIP_REGFLD_EMAC_BASIC_CTL0_SPEED, speed_code) |
            LBITFIELD_SET(CHIP_REGFLD_EMAC_BASIC_CTL0_LOOPBACK, (init_flags & LIP_INIT_FLAGS_MAC_LOOPBACK) != 0) |
            LBITFIELD_SET(CHIP_REGFLD_EMAC_BASIC_CTL0_DUPLEX, (mode->flags & LIP_PHY_MODE_FLAG_DUPLEX_FULL) != 0);

}

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_ID_EMAC);

#ifdef LIP_LL_EMAC_PIN_MDC
        CHIP_PIN_CONFIG(LIP_LL_EMAC_PIN_MDC);
#endif
#ifdef LIP_LL_EMAC_PIN_MDIO
        CHIP_PIN_CONFIG(LIP_LL_EMAC_PIN_MDIO);
#endif
        CHIP_PIN_CONFIG(LIP_LL_EMAC_PIN_RGMII_RXD0);
        CHIP_PIN_CONFIG(LIP_LL_EMAC_PIN_RGMII_RXD1);
        CHIP_PIN_CONFIG(LIP_LL_EMAC_PIN_RGMII_RXD2);
        CHIP_PIN_CONFIG(LIP_LL_EMAC_PIN_RGMII_RXD3);
        CHIP_PIN_CONFIG(LIP_LL_EMAC_PIN_RGMII_RXCTRL);
        CHIP_PIN_CONFIG(LIP_LL_EMAC_PIN_RGMII_RXCK);
        CHIP_PIN_CONFIG(LIP_LL_EMAC_PIN_RGMII_TXD0);
        CHIP_PIN_CONFIG(LIP_LL_EMAC_PIN_RGMII_TXD1);
        CHIP_PIN_CONFIG(LIP_LL_EMAC_PIN_RGMII_TXD2);
        CHIP_PIN_CONFIG(LIP_LL_EMAC_PIN_RGMII_TXD3);
        CHIP_PIN_CONFIG(LIP_LL_EMAC_PIN_RGMII_TXCTRL);
        CHIP_PIN_CONFIG(LIP_LL_EMAC_PIN_RGMII_TXCK);
#ifdef LIP_LL_EMAC_PIN_RGMII_CLKIN
        CHIP_PIN_CONFIG(LIP_LL_EMAC_PIN_RGMII_CLKIN);
#endif


        //0x00001006
        CHIP_REGS_SYSCFG->EMAC_EPHY_CLK0 =
          LBITFIELD_SET(CHIP_REGFLD_SYSCFG_EMAC_EPHY_CLK0_PHY_RMII_EN, 0) |
          LBITFIELD_SET(CHIP_REGFLD_SYSCFG_EMAC_EPHY_CLK0_EPIT, CHIP_REGFLDVAL_SYSCFG_EMAC_EPHY_CLK0_EPIT_RGMII) |
          LBITFIELD_SET(CHIP_REGFLD_SYSCFG_EMAC_EPHY_CLK0_ETCS, CHIP_REGFLDVAL_SYSCFG_EMAC_EPHY_CLK0_ETCS_RGMII_INT) |
          LBITFIELD_SET(CHIP_REGFLD_SYSCFG_EMAC_EPHY_CLK0_ETXIE, 0) |
          LBITFIELD_SET(CHIP_REGFLD_SYSCFG_EMAC_EPHY_CLK0_ERXIE, 0) |
          LBITFIELD_SET(CHIP_REGFLD_SYSCFG_EMAC_EPHY_CLK0_ERXDC, 0) |
          LBITFIELD_SET(CHIP_REGFLD_SYSCFG_EMAC_EPHY_CLK0_ETXDC, 4) |
          LBITFIELD_SET(CHIP_REGFLD_SYSCFG_EMAC_EPHY_CLK0_PHY_SEL, CHIP_REGFLDVAL_SYSCFG_EMAC_EPHY_CLK0_PHY_SEL_EXTERNAL)
        ;
    } else if (stage == LIP_INIT_STAGE_MAC_INIT) {
        CHIP_REGS_EMAC->BASIC_CTL1 |= CHIP_REGFLD_EMAC_BASIC_CTL1_SOFT_RST;
        /** @todo сделать таймаут !! (возможно через верхний уровень через две стадии) */
        while (CHIP_REGS_EMAC->BASIC_CTL1 & CHIP_REGFLD_EMAC_BASIC_CTL1_SOFT_RST) {
        }
        CHIP_REGS_EMAC->BASIC_CTL1 = LBITFIELD_SET(CHIP_REGFLD_EMAC_BASIC_CTL1_BURST_LEN, 8) |
                                     LBITFIELD_SET(CHIP_REGFLD_EMAC_BASIC_CTL1_RX_TX_PRI, 0);

        f_dma_reset();

        CHIP_REGS_EMAC->RX_CTL0 =
                LBITFIELD_SET(CHIP_REGFLD_EMAC_RX_CTL0_RX_FRM_LEN_CTL, 0) |
                LBITFIELD_SET(CHIP_REGFLD_EMAC_RX_CTL0_JUMBO_FRM_EN, 0) |
                LBITFIELD_SET(CHIP_REGFLD_EMAC_RX_CTL0_STRIP_FCS, 1) |
                LBITFIELD_SET(CHIP_REGFLD_EMAC_RX_CTL0_CHECK_CRC, 1)  |
                LBITFIELD_SET(CHIP_REGFLD_EMAC_RX_CTL0_RX_PAUSE_FRM_MD, 0) |
                LBITFIELD_SET(CHIP_REGFLD_EMAC_RX_CTL0_RX_FLOW_CTL_EN, 0)
                ;
        CHIP_REGS_EMAC->RX_CTL1 =
                LBITFIELD_SET(CHIP_REGFLD_EMAC_RX_CTL1_RX_FIFO_FLOW_CTL, 0) |
                LBITFIELD_SET(CHIP_REGFLD_EMAC_RX_CTL1_RX_FLOW_CTL_TH_DEACT, 0) |
                LBITFIELD_SET(CHIP_REGFLD_EMAC_RX_CTL1_RX_FLOW_CTL_TH_ACT, 0) |
                LBITFIELD_SET(CHIP_REGFLD_EMAC_RX_CTL1_RX_TH, 0) |
                LBITFIELD_SET(CHIP_REGFLD_EMAC_RX_CTL1_RX_ERR_FRM, 0) | /* forwards frames with error */
                LBITFIELD_SET(CHIP_REGFLD_EMAC_RX_CTL1_RX_RUNT_FRM, 0) | /* Forward undersized frames */
                LBITFIELD_SET(CHIP_REGFLD_EMAC_RX_CTL1_RX_MD, 1) |  /* Receive Full Frame */
                LBITFIELD_SET(CHIP_REGFLD_EMAC_RX_CTL1_FLUSH_RX_FRM, 0) /* Flush Receive Frames */
                ;
        CHIP_REGS_EMAC->TX_CTL1 =
                LBITFIELD_SET(CHIP_REGFLD_EMAC_TX_CTL1_TX_TH, 0) |
                LBITFIELD_SET(CHIP_REGFLD_EMAC_TX_CTL1_TX_MD, 1); /* Transmit Full Frame */

        CHIP_REGS_EMAC->RX_FRM_FLT =
                LBITFIELD_SET(CHIP_REGFLD_EMAC_RX_FRM_FLT_DIS_ADDR_FILTER, 0) | /* Disable Address Filter  */
                LBITFIELD_SET(CHIP_REGFLD_EMAC_RX_FRM_FLT_DIS_BROADCAST, 0) |   /* Disable Receive Broadcast Frame */
                LBITFIELD_SET(CHIP_REGFLD_EMAC_RX_FRM_FLT_RX_ALL_MULTICAST, 1) |/* Receive All Multicast Frames Filter */
                LBITFIELD_SET(CHIP_REGFLD_EMAC_RX_FRM_FLT_CTL_FRM_FILTER, CHIP_REGFLDVAL_EMAC_RX_FRM_FLT_CTL_FRM_FILTER_RX_ALL) | /* Receive all control frames */
                LBITFIELD_SET(CHIP_REGFLD_EMAC_RX_FRM_FLT_HASH_MULTICAST, 0) |  /*  Filter Multicast Frames According to the hash table */
                LBITFIELD_SET(CHIP_REGFLD_EMAC_RX_FRM_FLT_HASH_UNICAST, 0) |    /*  Filter Unicast Frames According to the hash table */
                LBITFIELD_SET(CHIP_REGFLD_EMAC_RX_FRM_FLT_SA_FILTER_EN, 0) |    /*  Receive SA Filter Enable*/
                LBITFIELD_SET(CHIP_REGFLD_EMAC_RX_FRM_FLT_SA_INV_FILTER, 0) |   /*  Receive SA Invert Filter */
                LBITFIELD_SET(CHIP_REGFLD_EMAC_RX_FRM_FLT_DA_INV_FILTER, 0) |   /*  Filter both unicast and multicast frames by comparing DA field  in inverse filtering mode */
                LBITFIELD_SET(CHIP_REGFLD_EMAC_RX_FRM_FLT_FLT_MD, 0) |          /*  Receive the frame when it passes the address register filter or the hash filter */
                LBITFIELD_SET(CHIP_REGFLD_EMAC_RX_FRM_FLT_RX_ALL, (flags & LIP_INIT_FLAGS_MAC_RX_FILTER_DIS) != 0); /*  Receive All Frame */

        CHIP_REGS_EMAC->TX_CTL0 = LBITFIELD_SET(CHIP_REGFLD_EMAC_TX_CTL0_TX_FRM_LEN_CTL, 0);


    } else if (stage == LIP_INIT_STAGE_MAC_START) {
        f_dma_tx_descrs_init();
        f_dma_rx_descrs_init();
        f_eth_config(lip_mac_phy_mode(), flags);
        CHIP_REGS_EMAC->INT_STA = CHIP_REGS_EMAC->INT_STA; /* сброс прерываний */
        CHIP_REGS_EMAC->TX_CTL1 |= CHIP_REGFLD_EMAC_TX_CTL1_TX_DMA_START | CHIP_REGFLD_EMAC_TX_CTL1_TX_DMA_EN;
        CHIP_REGS_EMAC->RX_CTL1 |= CHIP_REGFLD_EMAC_RX_CTL1_RX_DMA_START | CHIP_REGFLD_EMAC_RX_CTL1_RX_DMA_EN;


        CHIP_REGS_EMAC->RX_CTL0 |= CHIP_REGFLD_EMAC_RX_CTL0_RX_EN;
        CHIP_REGS_EMAC->TX_CTL0 |= CHIP_REGFLD_EMAC_TX_CTL0_TX_EN;
        f_mac_started = true;
    }
    return err;
}

void lip_ll_set_mac_addr(const uint8_t *mac_addr) {
    CHIP_REGS_EMAC->ADDR[0].HIGH = (mac_addr[5] << 8) | mac_addr[4];
    CHIP_REGS_EMAC->ADDR[0].LOW  = (mac_addr[3] << 24) | (mac_addr[2] << 16) | (mac_addr[1] << 8) | mac_addr[0];
}


/* Функция проходит по завершенным дескрипторам на прием и ищет завершенные
 * полностью кадры. На каждый найденный кадр вызывается 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_EMAC_BD_T *rdes = &f_rx_descr[cur_descr_idx];
        const uint32_t status = rdes->STATUS;
        out = ((status & CHIP_ENET_RXBD_STATUS_DESC_CTL) != 0);
        if (!out) {
            if (frame_descr_cnt == 0) {
                start_idx = cur_descr_idx;
            }

            frame_descr_cnt++;


            if (status & CHIP_ENET_RXBD_STATUS_LAST_DESC) {
                const uint16_t data_len = LBITFIELD_GET(status, CHIP_ENET_RXBD_STATUS_FRM_LEN);
                bool has_err = (status & (CHIP_ENET_RXBD_STATUS_DAF_FAIL |
                                           CHIP_ENET_RXBD_STATUS_NO_ENOUGH_BUF_ERR |
                                           CHIP_ENET_RXBD_STATUS_SAF_FAIL |
                                           CHIP_ENET_RXBD_STATUS_OVERFLOW_ERR |
                                           CHIP_ENET_RXBD_STATUS_COL_ERR |
                                           CHIP_ENET_RXBD_STATUS_LENGTH_ERR |
                                           CHIP_ENET_RXBD_STATUS_PHY_ERR |
                                           CHIP_ENET_RXBD_STATUS_CRC_ERR
                                           )
                                );
                if (has_err) {
                    lip_printf(LIP_LOG_MODULE_PORT, LIP_LOG_LVL_DEBUG_HIGH, 0,
                               "lip port: rx buf done with error! status = 0x%08X\n", status);
                } else {

                    rx_frame.info.flags = 0;
                    /* Судя по всему всегда выполняется попытка проверки суммы заголовка
                     * IP и пакета TCP/UDP, в связи с чем флаги HEADER_ERR и PAYLOAD_ERR
                     * выставляются на не IP пакеты. Если флагов нет, то считаем, что пакет
                     * сумма проверена корректно */
                    if (!(status & CHIP_ENET_RXBD_STATUS_HEADER_ERR)) {
                        rx_frame.info.ll_flags |= LIP_LL_RXFLAGS_IP_CHECKSUM_CHECKED;
                        if (!(status & CHIP_ENET_RXBD_STATUS_PAYLOAD_ERR)) {
                            rx_frame.info.ll_flags |= LIP_LL_RXFLAGS_TRANSP_CHECKSUM_CHECKED;
                        }
                    }

#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 ((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);
                        chip_dcache_invalidate((intptr_t)rx_frame.pl_tail.data, rx_frame.pl_tail.size);
                    } else {
                        rx_frame.pl_tail.data = NULL;
                        rx_frame.pl_tail.size = 0;
                    }
                    chip_dcache_invalidate((intptr_t)rx_frame.pl_main.data, rx_frame.pl_main.size);
                    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++;
        }
    }
}



void lip_ll_stop(void) {
    CHIP_REGS_EMAC->TX_CTL1 &= ~CHIP_REGFLD_EMAC_TX_CTL1_TX_DMA_EN;
    CHIP_REGS_EMAC->RX_CTL1 &= ~CHIP_REGFLD_EMAC_RX_CTL1_RX_DMA_EN;

    while ((CHIP_REGS_EMAC->TX_DMA_STA != CHIP_REGFLDVAL_EMAC_TX_DMA_STA_STOP)
            && (CHIP_REGS_EMAC->RX_DMA_STA != CHIP_REGFLDVAL_EMAC_RX_DMA_STA_STOP)) {
        continue;
    }

    CHIP_REGS_EMAC->RX_CTL0 &= ~CHIP_REGFLD_EMAC_RX_CTL0_RX_EN;
    CHIP_REGS_EMAC->TX_CTL0 &= ~CHIP_REGFLD_EMAC_TX_CTL0_TX_EN;

}

void lip_ll_close(void) {
    chip_per_clk_dis(CHIP_PER_ID_EMAC);

#ifdef LIP_LL_EMAC_PIN_MDC
        CHIP_PIN_CONFIG_DIS(LIP_LL_EMAC_PIN_MDC, CHIP_PIN_CFG_STD);
#endif
#ifdef LIP_LL_EMAC_PIN_MDIO
        CHIP_PIN_CONFIG_DIS(LIP_LL_EMAC_PIN_MDIO, CHIP_PIN_CFG_STD);
#endif
        CHIP_PIN_CONFIG_DIS(LIP_LL_EMAC_PIN_RGMII_RXD0, CHIP_PIN_CFG_STD);
        CHIP_PIN_CONFIG_DIS(LIP_LL_EMAC_PIN_RGMII_RXD1, CHIP_PIN_CFG_STD);
        CHIP_PIN_CONFIG_DIS(LIP_LL_EMAC_PIN_RGMII_RXD2, CHIP_PIN_CFG_STD);
        CHIP_PIN_CONFIG_DIS(LIP_LL_EMAC_PIN_RGMII_RXD3, CHIP_PIN_CFG_STD);
        CHIP_PIN_CONFIG_DIS(LIP_LL_EMAC_PIN_RGMII_RXCTRL, CHIP_PIN_CFG_STD);
        CHIP_PIN_CONFIG_DIS(LIP_LL_EMAC_PIN_RGMII_RXCK, CHIP_PIN_CFG_STD);
        CHIP_PIN_CONFIG_DIS(LIP_LL_EMAC_PIN_RGMII_TXD0, CHIP_PIN_CFG_STD);
        CHIP_PIN_CONFIG_DIS(LIP_LL_EMAC_PIN_RGMII_TXD1, CHIP_PIN_CFG_STD);
        CHIP_PIN_CONFIG_DIS(LIP_LL_EMAC_PIN_RGMII_TXD2, CHIP_PIN_CFG_STD);
        CHIP_PIN_CONFIG_DIS(LIP_LL_EMAC_PIN_RGMII_TXD3, CHIP_PIN_CFG_STD);
        CHIP_PIN_CONFIG_DIS(LIP_LL_EMAC_PIN_RGMII_TXCTRL, CHIP_PIN_CFG_STD);
        CHIP_PIN_CONFIG_DIS(LIP_LL_EMAC_PIN_RGMII_TXCK, CHIP_PIN_CFG_STD);
#ifdef LIP_LL_EMAC_PIN_RGMII_CLKIN
        CHIP_PIN_CONFIG_DIS(LIP_LL_EMAC_PIN_RGMII_CLKIN, CHIP_PIN_CFG_STD);
#endif
}

#if !LIP_MAC_NO_PHY
t_lip_errs lip_ll_mdio_write_start(uint8_t phyaddr, uint8_t regaddr, uint16_t value) {
    CHIP_REGS_EMAC->MII_DATA = value;
    CHIP_REGS_EMAC->MII_CMD =
        LBITFIELD_SET(CHIP_REGFLD_EMAC_MII_CMD_MDC_DIV_RATIO_M, LIP_RVAL_CMD_MDC_DIV_RATIO)
      | LBITFIELD_SET(CHIP_REGFLD_EMAC_MII_CMD_PHY_ADDR, phyaddr)
      | LBITFIELD_SET(CHIP_REGFLD_EMAC_MII_CMD_PHY_REG_ADDR, regaddr)
      | LBITFIELD_SET(CHIP_REGFLD_EMAC_MII_CMD_MII_WR, 1)
      | LBITFIELD_SET(CHIP_REGFLD_EMAC_MII_CMD_MII_BUSY, 1)
    ;
    return LIP_ERR_SUCCESS;
}

t_lip_errs lip_ll_mdio_read_start(uint8_t phyaddr, uint8_t regaddr) {
    CHIP_REGS_EMAC->MII_CMD =
        LBITFIELD_SET(CHIP_REGFLD_EMAC_MII_CMD_MDC_DIV_RATIO_M, LIP_RVAL_CMD_MDC_DIV_RATIO)
        | LBITFIELD_SET(CHIP_REGFLD_EMAC_MII_CMD_PHY_ADDR, phyaddr)
        | LBITFIELD_SET(CHIP_REGFLD_EMAC_MII_CMD_PHY_REG_ADDR, regaddr)
        | LBITFIELD_SET(CHIP_REGFLD_EMAC_MII_CMD_MII_WR, 0)
        | LBITFIELD_SET(CHIP_REGFLD_EMAC_MII_CMD_MII_BUSY, 1)
  ;

    return LIP_ERR_SUCCESS;
}

bool lip_ll_mdio_rdy(void) {
    return (CHIP_REGS_EMAC->MII_CMD & CHIP_REGFLD_EMAC_MII_CMD_MII_BUSY) == 0;
}

t_lip_errs lip_ll_mdio_data(uint16_t *data) {
    *data = CHIP_REGS_EMAC->MII_DATA ;
    return LIP_ERR_SUCCESS;
}
#endif


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

static void f_tx_check_descr(void) {
    /* проверяем, какие дескрипторы завершились */
    bool out = false;
    while ((f_tx_descr_progr_cnt != 0) && !out) {
        t_descr_pos tx_descr_num = f_tx_done_descr;
        volatile CHIP_EMAC_BD_T *tdes = &f_tx_descr[tx_descr_num];
        const uint32_t status = tdes->STATUS;
        out  = (status & CHIP_ENET_TXBD_STATUS_DESC_CTL) != 0;
        if (!out) {
            t_lip_tx_frame  *tx_frame = NULL;
            const uint32_t size = tdes->SIZE;
            if (size & CHIP_ENET_TXBD_SIZE_LAST_DESC) {
                tx_frame = f_tx_frames[tx_descr_num];
                if (tx_frame) {
                    /* CHIP_ENET_TXBD_STATUS_PAYLOAD_ERR, CHIP_ENET_TXBD_STATUS_HDR_ERR */
                    if (status & (CHIP_ENET_TXBD_STATUS_LEN_ERR
                                   | CHIP_ENET_TXBD_STATUS_CRS_ERR
                                   | CHIP_ENET_TXBD_STATUS_COL_ERR0
                                   | CHIP_ENET_TXBD_STATUS_COL_ERR1
                                   | CHIP_ENET_TXBD_STATUS_COL_CNT
                                   | CHIP_ENET_TXBD_STATUS_DEFER_ERR
                                   | CHIP_ENET_TXBD_STATUS_UNDERFLOW_ERR
                                   )) {
                        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",
                                   status);
                        /** @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);
            }
        }
    }
}

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_EMAC_BD_T *ptdes = &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;


        uint32_t tdes_size   = LBITFIELD_SET(CHIP_ENET_TXBD_SIZE_BUF_SIZE, hdr_size)
                       |  CHIP_ENET_TXBD_SIZE_FIRST_DESC;
        if (data_size == 0) {
            tdes_size |= CHIP_ENET_TXBD_SIZE_LAST_DESC
                    | LBITFIELD_SET(CHIP_ENET_TXBD_SIZE_INT_CTL, 1)
                    | LBITFIELD_SET(CHIP_ENET_TXBD_SIZE_CHECKSUM_CTL, 0);
        } else {
            f_tx_frames[f_tx_put_descr] = NULL;
            f_tx_put_descr = LIP_LL_TX_NEXT_IND(f_tx_put_descr);
            chip_dcache_flush((intptr_t)frame->exdata_ptr, data_size);
            volatile CHIP_EMAC_BD_T *tdes2 = &f_tx_descr[f_tx_put_descr];            
            tdes2->BUF_ADDR = (uint32_t)frame->exdata_ptr;
            tdes2->SIZE   = LBITFIELD_SET(CHIP_ENET_TXBD_SIZE_BUF_SIZE, data_size)
                    | CHIP_ENET_TXBD_SIZE_LAST_DESC
                    | LBITFIELD_SET(CHIP_ENET_TXBD_SIZE_INT_CTL, 1)
                    | LBITFIELD_SET(CHIP_ENET_TXBD_SIZE_CHECKSUM_CTL, 0);

            tdes2->STATUS = CHIP_ENET_TXBD_STATUS_DESC_CTL;


            f_tx_descr_progr_cnt++;
        }

        chip_dcache_flush(hdr_addr, hdr_size);
        ptdes->BUF_ADDR = hdr_addr;
        ptdes->SIZE = tdes_size;
        ptdes->STATUS = CHIP_ENET_TXBD_STATUS_DESC_CTL;


        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_EMAC->TX_CTL1 |= CHIP_REGFLD_EMAC_TX_CTL1_TX_DMA_START;
    }

    return res;
}

void lip_ll_pull(void) {
    if (f_mac_started) {
        uint32_t int_st = CHIP_REGS_EMAC->INT_STA;
        if (int_st != 0) {
            CHIP_REGS_EMAC->INT_STA = int_st;
        }
        if (int_st & CHIP_REGFLD_EMAC_INT_STA_RX_P) {
            f_rx_check_descr();
        }
        if (int_st & CHIP_REGFLD_EMAC_INT_STA_TX_P) {
            f_tx_check_descr();
        }
    }
}

t_lip_errs lip_ll_join(const uint8_t *mac) {

    return LIP_ERR_SUCCESS;
}

t_lip_errs lip_ll_leave(const uint8_t *mac) {

    return LIP_ERR_SUCCESS;
}
