/* * EMAC driver for NVCom-02TEM-3U evaluation kit * * Copyright (c) 2020 Elvees (support@elvees.com) * Author: Dmitry Evtushenko * */ #include "irq.h" #include "func.h" #include "mc_eth.h" #include "mc_dma.h" #include "../common/fifo.h" #include "../common/debug.h" #include "../common/debug_tm.h" #include #include #include #include ///////////////////////// IRQ: ///////////////////////// u32 cnt_frm_rx_ih = 0; u32 cnt_dma_rx_ih = 0; u32 cnt_frm_tx_ih = 0; u32 cnt_dma_tx_ih = 0; extern bool en_print; static irqreturn_t emac_frm_rx_ih( int irq, void *pv) { mc_emac_t *pe = (mc_emac_t *)pv; bool is_start_dma; is_start_dma = ProcRxFifo( pe, true); if( !is_start_dma && !pe->is_full_ff_rx) _Wr( pe, RG_CONTROL, _Rd( pe, RG_CONTROL) | (F_IRQ_RX_DONE | F_IRQ_RX_OVF)); else if( _Rd( pe, RG_CONTROL) & (F_IRQ_RX_DONE | F_IRQ_RX_OVF)) _Wr( pe, RG_CONTROL, _Rd( pe, RG_CONTROL) & ~(F_IRQ_RX_DONE | F_IRQ_RX_OVF)); return IRQ_HANDLED; } static irqreturn_t emac_dma_rx_ih( int irq, void *pv) { mc_emac_t *pe = (mc_emac_t *)pv; u32 ir, ir_expected; u32 is_ok = false; bool is_start_dma = false; //pr_info( ">> emac_dma_rx_ih:\n"); // проверяем RUN, заодно снимаем признак прерывания: if( _Rd_RxDMA( pe, RG_DMA_CSR) & F_DMA_RUN) { // Так не должно быть! pr_info( "emac_dma_rx_ih: ERROR: RUN is present\n"); return IRQ_HANDLED; } if( pe->pdrx_dma_to_mem) { if( pe->pdrx_dma_to_mem->state == EDDS_DMA_TO_MEM) { u32 len = pe->pdrx_dma_to_mem->len_data; #if !defined(CONFIG_NVCOM01M) && !defined(CONFIG_MC30SF6) len += ((len & 0x7) ? 8 : 0); #endif ir_expected = mips_virt_to_phys( (unsigned)pe->pdrx_dma_to_mem->pdata) + len; ir = _Rd_RxDMA( pe, RG_DMA_IR); if( ir == ir_expected) { pe->pdrx_dma_to_mem->state = EDDS_MEM; if( pe->pdrx_mem == NULL) pe->pdrx_mem = pe->pdrx_dma_to_mem; is_ok = true; EPRINT("RX_DMA >> %u\n", len); } else pr_info( "emac_dma_rx_ih: ERROR: disparity with IR reg: %Xh (wait: %Xh)\n", ir, ir_expected); } else pr_info( "emac_dma_rx_ih: ERROR: incorrect state of data %u\n", pe->pdrx_dma_to_mem->state); //if( F_NUM_FR( _Rd( pe, RG_STATUS_RX))==0 || pe->is_full_ff_rx) wake_up_interruptible( &pe->wq_rxjob); pe->pdrx_dma_to_mem = NULL; if( is_ok) { //@@@: if( _Rd( pe, RG_CONTROL) | F_IRQ_RX_DONE) if( F_NUM_FR( _Rd( pe, RG_STATUS_RX))) is_start_dma = ProcRxFifo( pe, true); else is_start_dma = StartDma_RxFifoToMem( pe); } } else pr_info( "emac_dma_rx_ih: ERROR: pointer to transferred from RX_FIFO data is absent\n"); if( !is_start_dma && !pe->is_full_ff_rx) _Wr( pe, RG_CONTROL, _Rd( pe, RG_CONTROL) | (F_IRQ_RX_DONE | F_IRQ_RX_OVF)); else if( _Rd( pe, RG_CONTROL) & (F_IRQ_RX_DONE | F_IRQ_RX_OVF)) _Wr( pe, RG_CONTROL, _Rd( pe, RG_CONTROL) & ~(F_IRQ_RX_DONE | F_IRQ_RX_OVF)); return IRQ_HANDLED; } // TX: static irqreturn_t emac_frm_tx_ih( int irq, void *pv) { mc_emac_t *pe = (mc_emac_t *)pv; u32 r_stat = _Rd( pe, RG_STATUS_TX); _Wr( pe, RG_STATUS_TX, 0); //PRINT( "emac_frm_tx_ih: STATUS_TX=%Xh\n", r_stat); if( (r_stat & F_ONTX_REQ)) { // Так не должно быть! pr_info( "emac_frm_tx_ih: ERROR: ONTX_REQ is present\n"); return IRQ_HANDLED; } if( pe->stop_tx) return IRQ_HANDLED; if( pe->pdtx_fifo_phy) { if( pe->pdtx_fifo_phy->state == EDDS_FIFO_PHY) { if( pe->pdtx_phy == NULL) pe->pdtx_phy = pe->pdtx_fifo_phy; pe->pdtx_phy->state = EDDS_PHY; pe->pdtx_fifo_phy = NULL; //@@@: wake_up_interruptible( &pe->wq_txjob); if( pe->stat_link_down) return IRQ_HANDLED; if (Fin_TxFrm(pe)) return IRQ_HANDLED; } else pr_info( "emac_frm_tx_ih: ERROR: incorrect state of data\n"); } else pr_info( "emac_frm_tx_ih: ERROR: pointer to transferred to PHY data is absent\n"); // TODO: что-то пошло не так, будим нить и там разбираемся...: //pr_info( "emac_frm_tx_ih: wake_up_interruptible()\n"); //wake_up_interruptible( &pe->wq_txjob); return IRQ_HANDLED; } static irqreturn_t emac_dma_tx_ih( int irq, void *pv) { mc_emac_t *pe = (mc_emac_t *)pv; u32 ir, ir_expected; u32 csr; csr = _Rd_TxDMA( pe, RG_DMA_CSR); //PRINT( "dma_tx_ih: STAT_TX=%Xh, TX_FRM_CTRL=%Xh, CTRL=%Xh, csr=%Xh\n", _Rd( pe, RG_STATUS_TX), _Rd( pe, RG_TX_FRAME_CONTROL), _Rd( pe, RG_CONTROL), csr); //stat_tx=0, 4000h, ctrl=19Fh, csr=0 //- сразу после инициализации // проверяем RUN, заодно снимаем признак прерывания: if( csr & F_DMA_RUN) { // Так не должно быть! pr_info( "emac_dma_tx_ih: ERROR: RUN is present\n"); return IRQ_HANDLED; } if( pe->pdtx_dma_to_fifo) { if( pe->pdtx_dma_to_fifo->state == EDDS_DMA_TO_FIFO) { u32 len = pe->pdtx_dma_to_fifo->len_data; if( pe->stat_link_down) return IRQ_HANDLED; #if !defined(CONFIG_NVCOM01M) && !defined(CONFIG_MC30SF6) len += ((len & 0x7) ? 8 : 0); #endif ir_expected = mips_virt_to_phys( (unsigned)pe->pdtx_dma_to_fifo->pdata) + len; ir = _Rd_TxDMA( pe, RG_DMA_IR); if( ir == ir_expected) { if( pe->pdtx_fifo == NULL) pe->pdtx_fifo = pe->pdtx_dma_to_fifo; pe->pdtx_fifo->state = EDDS_FIFO; pe->pdtx_dma_to_fifo = NULL; EPRINT("TX_DMA << %u\n", len); //pr_info( "emac_dma_tx_ih: Start_TxFrmToPhy()\n"); if( Start_TxFrmToPhy( pe)) { //pr_info( "emac_dma_tx_ih: StartDma_MemToTxFifo()\n"); if( StartDma_MemToTxFifo( pe)) { PROFILER_SET_TM(3) return IRQ_HANDLED; } } } else pr_info( "emac_dma_tx_ih: ERROR: disparity with IR reg: %Xh (wait: %Xh)\n", ir, ir_expected); } else pr_info( "emac_dma_tx_ih: ERROR: incorrect state of data\n"); } else pr_info( "emac_dma_tx_ih: ERROR: pointer to transferred to TX_FIFO data is absent\n"); // TODO: что-то пошло не так, будим нить и там разбираемся...: //pr_info( "emac_dma_tx_ih: wake_up_interruptible()\n"); wake_up_interruptible( &pe->wq_txjob); //pr_info( "emac_dma_tx_ih: END\n"); return IRQ_HANDLED; } #ifdef USE_EXT_IRQ_PHY_LINK #ifdef CONFIG_MC30SF6 #include #else #include #endif static irqreturn_t emac_phy_link_ih( int irq, void *pv) { mc_emac_t *pe = (mc_emac_t *)pv; pr_info( "emac_phy_link_ih: irq=%u ICS=%Xh\n", irq, mc_phy_read( pe, PHY_ICS)); emac_change_link( pe); return IRQ_HANDLED; } #endif //USE_EXT_IRQ_PHY_LINK void emac_free_irqs( mc_emac_t *pe) { if( pe->req_irq_frm_rx) { free_irq( pe->irq_frm_rx, pe); pe->req_irq_frm_rx = 0; } if( pe->req_irq_frm_tx) { free_irq( pe->irq_frm_tx, pe); pe->req_irq_frm_tx = 0; } if( pe->req_irq_dma_rx) { free_irq( pe->irq_dma_rx, pe); pe->req_irq_dma_rx = 0; } if( pe->req_irq_dma_tx) { free_irq( pe->irq_dma_tx, pe); pe->req_irq_dma_tx = 0; } #ifdef USE_EXT_IRQ_PHY_LINK if( pe->req_irq_phy_link) { free_irq( pe->irq_phy_link, pe); pe->req_irq_phy_link = 0; } #endif //USE_EXT_IRQ_PHY_LINK } // macro for drv_emac_init_irqs() function: #define SET_REQUEST_IRQ( qstr, irq_ih, req_irq) \ rc = request_irq( qstr, irq_ih, 0, #qstr, pe); \ if( rc) \ { \ pr_info( "drv_emac_init_irqs: request_irq(%i) ERROR (%i)\n", qstr, rc); \ goto l_err; \ } \ req_irq = qstr; \ disable_irq( qstr); int emac_init_irqs( mc_emac_t *pe) { int rc; pe->req_irq_frm_rx = 0; pe->req_irq_frm_tx = 0; pe->req_irq_dma_rx = 0; pe->req_irq_dma_tx = 0; #ifdef USE_EXT_IRQ_PHY_LINK pe->req_irq_phy_link = 0; #endif //USE_EXT_IRQ_PHY_LINK SET_REQUEST_IRQ( pe->irq_frm_rx, emac_frm_rx_ih, pe->req_irq_frm_rx) SET_REQUEST_IRQ( pe->irq_frm_tx, emac_frm_tx_ih, pe->req_irq_frm_tx) SET_REQUEST_IRQ( pe->irq_dma_rx, emac_dma_rx_ih, pe->req_irq_dma_rx) SET_REQUEST_IRQ( pe->irq_dma_tx, emac_dma_tx_ih, pe->req_irq_dma_tx) #ifdef USE_EXT_IRQ_PHY_LINK SET_REQUEST_IRQ( pe->irq_phy_link, emac_phy_link_ih, pe->req_irq_phy_link) #endif //USE_EXT_IRQ_PHY_LINK return RC_EMAC_SUCCESS; l_err:; emac_free_irqs( pe); return RC_EMAC_ERROR; } void emac_enable_irqs( mc_emac_t *pe, int ena) { if( ena) { enable_irq( pe->irq_frm_rx); enable_irq( pe->irq_frm_tx); enable_irq( pe->irq_dma_rx); enable_irq( pe->irq_dma_tx); #ifdef USE_EXT_IRQ_PHY_LINK enable_irq( pe->irq_phy_link); #endif //USE_EXT_IRQ_PHY_LINK } else { disable_irq( pe->irq_frm_rx); disable_irq( pe->irq_frm_tx); disable_irq( pe->irq_dma_rx); disable_irq( pe->irq_dma_tx); #ifdef USE_EXT_IRQ_PHY_LINK disable_irq( pe->irq_phy_link); #endif //USE_EXT_IRQ_PHY_LINK } }