/**************************************************************************//**
  @file lip_ll_lpc43xx.c

  Файл содержит реализацию порта TCP/IP стека lip под сигнальный процессор bf609.
  Порт использует определения из chip.h (https://bitbucket.org/lcard/lpc_chip)
  для настройки пинов и определения установленной частоты.

  Файл использует функции для работы с дескрипторами из порта для lpc43xx

  @author Borisov Alexey <borisov@lcard.ru>
  ******************************************************************************/

#include "lip_ll.h"
#include "lip_log.h"
#include "ltimer.h"
#include "ports/lpc43xx/lip_ll_lpc43xx_descr.h"
#include "phy/lip_phy.h"

#include "chip.h"

/* таймаут на выполнение сброса MAC-контроллера */
#define MAC_RESET_TOUT  10

/* Макро для склеивания строк на уровне препроцессора */
#ifndef CONCAT
#define CONCAT_(a, b)       a ## b
#define CONCAT(a,b)         CONCAT_(a, b)
#endif

#ifndef LIP_PORT_MAC_NUM
    #define LIP_PORT_MAC_NUM  0
#endif


#define MAC_REG(name) (*CONCAT(CONCAT(CONCAT(pREG_EMAC,LIP_PORT_MAC_NUM), _), name))

#if CHIP_CLKF_SCLK0 > 100
    #define SMI_CR_VALUE     1
#elif CHIP_CLKF_SCLK0 > 60
    #define SMI_CR_VALUE     0
#elif CHIP_CLKF_SCLK0 > 35
    #define SMI_CR_VALUE     3
#else
    #define SMI_CR_VALUE     2
#endif
#define SMI_ADDR_REGVAL(dev_addr, reg_addr) BF_SET(BITM_EMAC_SMI_ADDR_PA, dev_addr) | \
    BF_SET(BITM_EMAC_SMI_ADDR_SMIR, reg_addr) | \
    BF_SET(BITM_EMAC_SMI_ADDR_CR, SMI_CR_VALUE) | \
    BITM_EMAC_SMI_ADDR_SMIB


t_lip_errs lip_ll_init(t_lip_init_stage stage, int flags) {
    t_lip_errs err = LIP_ERR_SUCCESS;
    switch (stage) {
        case LIP_INIT_STAGE_PHY_INTF_INIT:

#if LIP_PORT_MAC_NUM == 0
            CHIP_PIN_CONFIG(CHIP_PIN_PC02_ETH0_TXD0);
            CHIP_PIN_CONFIG(CHIP_PIN_PC03_ETH0_TXD1);
            CHIP_PIN_CONFIG(CHIP_PIN_PC00_ETH0_RXD0);
            CHIP_PIN_CONFIG(CHIP_PIN_PC01_ETH0_RXD1);
            CHIP_PIN_CONFIG(CHIP_PIN_PB14_ETH0_REFCLK);
            CHIP_PIN_CONFIG(CHIP_PIN_PB13_ETH0_TXEN);
            CHIP_PIN_CONFIG(CHIP_PIN_PC05_ETH0_CRS);
            CHIP_PIN_CONFIG(CHIP_PIN_PC06_ETH0_MDC);
            CHIP_PIN_CONFIG(CHIP_PIN_PC07_ETH0_MDIO);
#elif LIP_PORT_MAC_NUM == 1
            CHIP_PIN_CONFIG(CHIP_PIN_PG03_ETH1_TXD0);
            CHIP_PIN_CONFIG(CHIP_PIN_PG02_ETH1_TXD1);
            CHIP_PIN_CONFIG(CHIP_PIN_PG00_ETH1_RXD0);
            CHIP_PIN_CONFIG(CHIP_PIN_PE15_ETH1_RXD1);
            CHIP_PIN_CONFIG(CHIP_PIN_PG06_ETH1_REFCLK);
            CHIP_PIN_CONFIG(CHIP_PIN_PG05_ETH1_TXEN);
            CHIP_PIN_CONFIG(CHIP_PIN_PE13_ETH1_CRS);
            CHIP_PIN_CONFIG(CHIP_PIN_PE10_ETH1_MDC);
            CHIP_PIN_CONFIG(CHIP_PIN_PE11_ETH1_MDIO);
#else
            #error invlad MAC number
#endif

            break;
        case LIP_INIT_STAGE_MAC_INIT: {
                t_ltimer tmr;
                /* сброс DMA */


                MAC_REG(DMA_BUSMODE) = BITM_EMAC_DMA_BUSMODE_SWR;
                ltimer_set(&tmr, LTIMER_MS_TO_CLOCK_TICKS(MAC_RESET_TOUT));
                while ((MAC_REG(DMA_BUSMODE) & BITM_EMAC_DMA_BUSMODE_SWR) && !ltimer_expired(&tmr))
                {}
                /* если не удалось выполнить сброс, то это означает как правило,
                 * что нет действительного клока от PHY. дальнейшая работа
                 * невозможна */
                if (MAC_REG(DMA_BUSMODE) & BITM_EMAC_DMA_BUSMODE_SWR) {
                    err = LIP_ERR_INIT_MAC_RESET;
                    lip_printf(LIP_LOG_MODULE_PORT, LIP_LOG_LVL_ERROR_FATAL, err,
                               "lip port: mac reset error. possibly phy clock not present!\n");
                } else {
                    /* ожидание завершения всех транзакций */
                    while ((MAC_REG(DMA_BMSTAT) & (BITM_EMAC_DMA_BMSTAT_BUSRD | BITM_EMAC_DMA_BMSTAT_BUSWR))
                            && !ltimer_expired(&tmr)) {}

                    if (MAC_REG(DMA_BMSTAT) & (BITM_EMAC_DMA_BMSTAT_BUSRD | BITM_EMAC_DMA_BMSTAT_BUSWR)) {
                        err = LIP_ERR_INIT_MAC_RESET;
                        lip_printf(LIP_LOG_MODULE_PORT, LIP_LOG_LVL_ERROR_FATAL, err,
                                   "lip port: cant't wait for all dma transfer completion!\n");
                    }
                }

                if (err == LIP_ERR_SUCCESS) {
                    /* Enhanced descriptors, burst length = 32 */
                    MAC_REG(DMA_BUSMODE) = BF_SET(BITM_EMAC_DMA_BUSMODE_PBL, 32) |
                                           BF_SET(BITM_EMAC_DMA_BUSMODE_RPBL, 16) |
                                           BITM_EMAC_DMA_BUSMODE_USP |
                                           BITM_EMAC_DMA_BUSMODE_ATDS;

                    /* checksum offload, full duplex,
                        100Mbps, disable receive own in half duplex, inter-frame gap
                        of 64-bits */
                    MAC_REG(MACCFG) = BF_SET(BITM_EMAC_MACCFG_IFG, 3) |
                                      BITM_EMAC_MACCFG_FES |
                                      BITM_EMAC_MACCFG_DM |
                                      BITM_EMAC_MACCFG_DO |
                                      BITM_EMAC_MACCFG_IPC | (1 << 15);

                    if (flags & LIP_INIT_FLAGS_MAC_RX_FILTER_DIS) {
                        /* при запрете фильтра разрешаем все пакеты */
                        MAC_REG(MACFRMFILT) = BITM_EMAC_MACFRMFILT_RA |
                                              BITM_EMAC_MACFRMFILT_PR;
                    } else {
#ifdef LIP_USE_IGMP
                        /** @todo сейчас разрешаем все multicast. мб сделать в будущем
                                  по hesh-таблицам */
                        MAC_REG(MACFRMFILT) = BITM_EMAC_MACFRMFILT_PM;
#else
                        MAC_REG(MACFRMFILT) = 0;
#endif
                    }

                    /* Flush transmit FIFO */
                    MAC_REG(DMA_OPMODE) = BITM_EMAC_DMA_OPMODE_FTF;

                    /* Setup DMA to flush receive FIFOs at 32 bytes, service TX FIFOs at
                       64 bytes */

                    /** @warning Включение OSF режима приводит к тому, что при
                        постоянном потоке на передачу, данные перестают передаваться
                        вообще (хотя DMA дескрипторы закрывает).
                        Так как большой разницы в скорости не наблюдается, то OSF
                        режим оставляем выключенным */
                    MAC_REG(DMA_OPMODE) |= BF_SET(BITM_EMAC_DMA_OPMODE_RTC, 1) |
                                            BF_SET(BITM_EMAC_DMA_OPMODE_TTC, 0);


                    /* disable MAC interrupts */
                    MAC_REG(DMA_IEN) = 0;

                }
            }



            break;
        case LIP_INIT_STAGE_MAC_START:
            /* настраиваем режим в соответствии с полученынм режимом phy */
            if (lip_phy_mode()!=LIP_PHY_DUPLEX_MODE_FULL)
                MAC_REG(MACCFG) &= ~BITM_EMAC_MACCFG_DM;

            if (lip_phy_speed()!=LIP_PHY_SPEED_100)
                MAC_REG(MACCFG) &= ~BITM_EMAC_MACCFG_FES;

            /* инициализация дескрипторов */
            MAC_REG(DMA_RXDSC_ADDR) = lip_ll_lpc43descr_rx_init();
            MAC_REG(DMA_TXDSC_ADDR) = lip_ll_lpc43descr_tx_init();

            /* разрешение приема, передачи и DMA в порядке из user manual */
            MAC_REG(MACCFG) |= BITM_EMAC_MACCFG_TE;
            MAC_REG(DMA_OPMODE) |= BITM_EMAC_DMA_OPMODE_ST | BITM_EMAC_DMA_OPMODE_SR;
            MAC_REG(MACCFG) |= BITM_EMAC_MACCFG_RE;
            break;
    }


    return err;
}


void lip_ll_close(void) {
    /* Flush transmit FIFO */
    MAC_REG(DMA_OPMODE) = BITM_EMAC_DMA_OPMODE_FTF;
    /* Disable Rx/Tx */
    MAC_REG(MACCFG) = 0;
}

void lip_ll_set_mac_addr(const uint8_t *mac_addr) {
    MAC_REG(ADDR0_LO) = ((uint32_t) mac_addr[3] << 24) |
                         ((uint32_t) mac_addr[2] << 16) |
                         ((uint32_t) mac_addr[1] << 8) |
                         ((uint32_t) mac_addr[0]);
    MAC_REG(ADDR0_HI) = ((uint32_t) mac_addr[5] << 8) |
                         ((uint32_t) mac_addr[4]);
}


t_lip_errs lip_ll_mdio_write_start(unsigned dev_addr, unsigned reg_addr, unsigned value) {
    MAC_REG(SMI_DATA) = BF_SET(BITM_EMAC_SMI_DATA_SMID, value);
    MAC_REG(SMI_ADDR) = SMI_ADDR_REGVAL(dev_addr, reg_addr) | BITM_EMAC_SMI_ADDR_SMIW;
    return LIP_ERR_SUCCESS;
}


t_lip_errs lip_ll_mdio_read_start(unsigned dev_addr, unsigned reg_addr) {
    MAC_REG(SMI_ADDR) = SMI_ADDR_REGVAL(dev_addr, reg_addr);
    return LIP_ERR_SUCCESS;
}

int lip_ll_mdio_rdy(void) {
    return  (MAC_REG(SMI_ADDR) & BITM_EMAC_SMI_ADDR_SMIB) == 0;
}

unsigned lip_ll_mdio_data(void) {    
    return BF_GET(MAC_REG(SMI_DATA), BITM_EMAC_SMI_DATA_SMID);
}

uint8_t* lip_ll_rx_get_buf(size_t* length) {
    return lip_ll_lpc43descr_rx_get_buf(length);
}

t_lip_errs lip_ll_rx_drop(void) {
    return lip_ll_lpc43descr_rx_drop();
}

uint8_t *lip_ll_tx_get_buf(int *max_length) {
    return lip_ll_lpc43descr_tx_get_buf(max_length);
}

t_lip_errs lip_ll_tx_flush_split(size_t size, const uint8_t* data, int data_size) {
    return lip_ll_lpc43descr_tx_flush_split(size, data, data_size);
}

void lip_ll_lpc43descr_tx_poll_req(void) {
    MAC_REG(DMA_TXPOLL) = 1;
}

void lip_ll_lpc43descr_clear_stat(void) {
    /** @todo Проверить, нужно ли что-то при underrun (в LPC - нужен сброс статуса) */
    //MAC_REG(DMA_BMSTAT) = MAC_REG(DMA_BMSTAT);
}

void lip_ll_lpc43descr_dmb(void) {
    csync();
}


/* ----------------- Функции для работы с групповыми передачами --------------*/
#ifdef LIP_USE_IGMP
    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;
    }
#endif
