/* * SWIC driver for MCT-04EM-3U evaluation kit * * Copyright (c) 2020 Elvees (support@elvees.com) * Author: Dmitry Evtushenko * */ #include "irq.h" #include "reg.h" #include "swic.h" #include "func.h" #include "../common/fifo.h" #include "ctl.h" //#include "test.h" #include #include #include static void set_cur_rx_desc_buf( swic_ctrl_t *pctl) { u32 port = pctl->port.num; u32 ir = MC_SWIC_RX_DESC_IR( port); dma_chain_t *pch = &pctl->dch_rx_desc; fifo_t *pff = &pch->ff; dma_desc_fi_t *pfi; //PRINT("set_cur_rx_desc_buf:\n"); while( 1) { if( (pfi = (dma_desc_fi_t *)fifo_get_first_free( pff)) == NULL) { PRINT( "Rx descriptor list is overflow !!!\n"); break; } if( !pfi->desc.valid) break; pfi->flag |= (MC_SWIC_RX_DESC_RUN(port) & (SWIC_DMA_CSR_END | SWIC_DMA_CSR_DONE)); //PRINT("set_cur_rx_desc_buf: ir=%Xh, init_ir=%Xh\n", ir, pfi->reg_init.ir); if( (ir >= pfi->reg_init.ir) && (ir < pfi->reg_init.ir + pch->sz_data_buf) ) { //pch->pfi_cur = pfi; pfi->stat = DMA_FI_STAT__FILLING; break; } else { if( pfi->desc.type_ep & SPW_EOP) ++pctl->rx_eop; else if( pfi->desc.type_ep & SPW_EEP) ++pctl->rx_eep; pctl->rx_bytes += pfi->desc.size; pfi->reg_init.csr &= ~SWIC_DMA_CSR_RUN; pfi->pos_proc = 0; pfi->stat = DMA_FI_STAT__FILLED; //PRINT("IRQ: rx_desc_buf: +%u B ir=%Xh valid=%u\n", pfi->desc.size, MC_IR_GSPW_RX_DES, pfi->desc.valid); if( pfi != (dma_desc_fi_t *)fifo_hold_first_free( pff)) { // TODO: move print to log PRINT( "set_cur_rx_desc_buf: ERROR: fifo_get_first_free != fifo_hold_first_free\n"); } } } //PRINT("set_cur_rx_desc_buf: END\n"); } irqreturn_t drv_spw_dma_rx_desc_ih( int irq, void *dev_id) { swic_ctrl_t *pctl = dev_id; u32 port = pctl->port.num; //fifo_t *pff = &pctl->dch_rx_desc.ff; //dma_desc_fi_t *pd; //u32 ir; //PRINT( "drv_spw_dma_rx_desc_ih: !!!\n"); if( pctl->mode == SWIC_MODE_CONFIG) { MC_SWIC_RX_DESC_CSR( port); return IRQ_HANDLED; } if( pctl->connected == 0) { MC_SWIC_RX_DESC_CSR( port); return IRQ_HANDLED; } #ifdef USE_DMA_TEST return dma_test_registrate_rx_desc( pctl); #endif //USE_DMA_TEST set_cur_rx_desc_buf( pctl); /* ir = SWIC_DMA_IR(SWIC_RX_DESC_CHAN); if ((pd = (dma_desc_fi_t *)fifo_get_first_free( pff)) == NULL) { MC_CSR_GSPW_RX_DES; wake_up_interruptible( &pctl->wq_rx); PRINT( "Rx descriptor list is overflow !!!\n"); return IRQ_HANDLED; } //if( pdsc->reg_init.ir) // fifo_hold_first_free( &pctl->dch_rx_desc); pd->flag |= (MC_RUN_GSPW_RX_DES & (SWIC_DMA_CSR_END | SWIC_DMA_CSR_DONE)); //PRINT( "drv_spw_dma_rx_desc_ih: list: IR=%Xh, CP=%Xh, CSR=%Xh\n", // pd->reg_init.ir, pd->reg_init.cp, pd->reg_init.csr); if( pd != (dma_desc_fi_t *)fifo_hold_first_free( pff)) { PRINT( "Trying of using held said Rx descriptor list from several threads !!!\n"); return IRQ_HANDLED; } */ // Снимаем признак прерывания //PRINT( " drv_spw_dma_rx_desc_ih: reg: IR=%Xh, CP=%Xh, RUN=%Xh\n", // MC_IR_GSPW_RX_DES, MC_CP_GSPW_RX_DES, MC_RUN_GSPW_RX_DES); MC_SWIC_RX_DESC_CSR( port); //PRINT( "drv_spw_dma_rx_desc_ih: wake_up_interruptible()\n"); PRINT( "dma_RX_desc_ih: wake_up(READ)\n"); print_fifo_held_qnt( pctl, "RX_desc_ih"); wake_up_interruptible( &pctl->wq_rx); //PRINT( "drv_spw_dma_rx_desc_ih: END\n"); return IRQ_HANDLED; } static void set_cur_rx_data_buf( swic_ctrl_t *pctl) { dma_chain_t *pch = &pctl->dch_rx_data; u32 port = pctl->port.num; short wcx, i, max_itr=5; u32 ir; fifo_t *pff = &pch->ff; dma_data_fi_t *pfi; //PRINT("set_cur_rx_data_buf:\n"); ir = MC_SWIC_RX_DATA_IR( port); for( i=0; (i < max_itr); ++i) { wcx = (MC_SWIC_RX_DATA_RUN(port) >> SWIC_DMA_CSR_WCX_OFS) & SWIC_DMA_CSR_WCX_MSK; if( wcx != SWIC_DMA_CSR_WCX_MSK ) { ir = MC_SWIC_RX_DATA_IR(port); break; } } while( 1) { if( (pfi = (dma_data_fi_t *)fifo_get_first_free( pff)) == NULL) { PRINT( "Rx data list is overflow !!!\n"); break; } PRINT("set_cur_rx_data_buf: ir=%Xh, init_ir=%Xh sz=%u\n", ir, pfi->reg_init.ir, pch->sz_data_buf); if( (ir >= pfi->reg_init.ir) && (ir < pfi->reg_init.ir + pch->sz_data_buf) ) { //pch->pfi_cur = pfi; pfi->stat = DMA_FI_STAT__FILLING; break; } else { pfi->reg_init.csr &= ~SWIC_DMA_CSR_RUN; pfi->pos_proc = 0; pfi->stat = DMA_FI_STAT__FILLED; //PRINT("set_cur_rx_data_buf: if()\n"); if( pfi != (dma_data_fi_t *)fifo_hold_first_free( pff)) { // TODO: move print to log PRINT( "set_cur_rx_data_buf: ERROR: fifo_get_first_free != fifo_hold_first_free\n"); } } } //PRINT("set_cur_rx_data_buf: END\n"); } irqreturn_t drv_spw_dma_rx_data_ih( int irq, void *dev_id) { swic_ctrl_t *pctl = dev_id; u32 port = pctl->port.num; //PRINT( "drv_spw_dma_rx_data_ih: !!!\n"); if( pctl->mode == SWIC_MODE_CONFIG) { MC_SWIC_RX_DATA_CSR( port); return IRQ_HANDLED; } if( pctl->connected == 0) { MC_SWIC_RX_DESC_CSR( port); return IRQ_HANDLED; } #ifdef USE_DMA_TEST return dma_test_registrate_rx_data( pctl); #endif //USE_DMA_TEST set_cur_rx_data_buf( pctl); //PRINT( "drv_spw_dma_rx_data_ih: list: IR=%Xh, CP=%Xh, CSR=%Xh\n", // pd->reg_init.ir, pd->reg_init.cp, pd->reg_init.csr); // Снимаем признак прерывания //PRINT( " drv_spw_dma_rx_data_ih: reg: IR=%Xh, CP=%Xh, RUN=%Xh\n", // MC_IR_GSPW_RX_DAT, MC_CP_GSPW_RX_DAT, MC_RUN_GSPW_RX_DAT); MC_SWIC_RX_DATA_CSR( port); PRINT( "dma_RX_data_ih: wake_up(READ)\n"); print_fifo_held_qnt( pctl, "RX_data_ih"); //wake_up_interruptible( &pctl->rxdatawq); //wake_up_interruptible( &pctl->wq_rx); wake_up_interruptible( &pctl->wq_rx); //PRINT( " drv_spw_dma_rx_data_ih: END\n"); return IRQ_HANDLED; } // Обработчик прерывания по завершению выдачи дескриптора(ов) данных: irqreturn_t drv_spw_dma_tx_desc_ih( int irq, void *dev_id) { swic_ctrl_t *pctl = dev_id; u32 port = pctl->port.num; fifo_t *pff_job = &pctl->ff_tx_job; dma_tx_job_t *p_job = NULL; dma_chain_t *pdch = &pctl->dch_tx_desc; dma_desc_fi_t *p_desc; dma_params_t *pdp; u32 ir, run; //PRINT( "dma_tx_data_ih: IRQ_MSK0=%Xh, IRQ_REQ0=%Xh\n", MC_MASKR0, MC_QSTR0); if( pctl->mode == SWIC_MODE_CONFIG) { MC_SWIC_TX_DESC_CSR( port); return IRQ_HANDLED; } #ifdef USE_DMA_TEST return dma_test_registrate_tx_desc( pctl); #endif //USE_DMA_TEST //PRINT( "dma_tx_desc_ih: run=%Xh ir=%Xh!!!\n", MC_SWIC_TX_DESC_RUN( port), MC_SWIC_TX_DESC_IR( port)); if( (p_job=(dma_tx_job_t *)fifo_get_first_held( pff_job)) ) { if( p_job->stat == DMA_JOB_STAT_RUN) { ir = MC_SWIC_TX_DESC_IR( port); run = MC_SWIC_TX_DESC_RUN( port); p_desc = p_job->p_desc_first; for( ; (1); p_desc = p_desc->p_next) { pdp = &p_desc->reg_init; if( p_desc->stat == DMA_FI_STAT__SENT) continue; if ((ir >= pdp->ir) && (ir <= pdp->ir + pdch->sz_data_buf)) { // current buffer: if( ((run & SWIC_DMA_CSR_END) || (run & SWIC_DMA_CSR_DONE)) && ((run >> SWIC_DMA_CSR_WCX_OFS) & SWIC_DMA_CSR_WCX_MSK)==SWIC_DMA_CSR_WCX_MSK ) { p_desc->stat = DMA_FI_STAT__SENT; /*if( p_desc == p_job->p_desc_last) { p_desc->stat = DMA_FI_STAT__SENT; //p_job->stat = DMA_JOB_STAT_FIN; //PRINT( "dma_tx_data_ih: wake_up(job)\n"); //wake_up_interruptible( &pctl->wq_txjob); }*/ } } else if( p_desc->stat != DMA_FI_STAT__SENT) p_desc->stat = DMA_FI_STAT__SENT; if( p_desc->stat == DMA_FI_STAT__SENT) { ++pctl->tx_packets; pctl->tx_bytes += p_desc->desc.size; PRINT("dma_tx_desc_ih: %Xh is sent\n", p_desc->reg_init.ir); } if( p_desc == p_job->p_desc_last) break; } } else PRINT( "dma_tx_desc_ih: running DMA tx job is absent!\n"); } else PRINT( "dma_tx_desc_ih: current DMA tx job is absent!\n"); // Снимаем признак прерывания //PRINT( "dma_tx_desc_ih: RUN_TX_DES=%Xh ir=%Xh first_held=%p\n", // MC_RUN_GSPW_TX_DES, MC_IR_GSPW_TX_DES, fifo_get_first_held( pff_job)); MC_SWIC_TX_DESC_CSR( port); return IRQ_HANDLED; } //- end: drv_spw_dma_tx_desc_ih // Обработчик прерывания по завершению выдачи буфера данных irqreturn_t drv_spw_dma_tx_data_ih( int irq, void *dev_id) { swic_ctrl_t *pctl = dev_id; u32 port = pctl->port.num; fifo_t *pff_job = &pctl->ff_tx_job; dma_tx_job_t *p_job = NULL; dma_chain_t *pdch = &pctl->dch_tx_data; dma_data_fi_t *p_data; dma_params_t *pdp; u32 ir, run; //PRINT( "dma_tx_data_ih: IRQ_MSK0=%Xh, IRQ_REQ0=%Xh\n", MC_MASKR0, MC_QSTR0); if( pctl->mode == SWIC_MODE_CONFIG) { MC_SWIC_TX_DATA_CSR( port); return IRQ_HANDLED; } #ifdef USE_DMA_TEST return dma_test_registrate_tx_data( pctl); #endif //USE_DMA_TEST //PRINT( "dma_tx_data_ih: run=%Xh ir=%Xh!!!\n", MC_SWIC_TX_DATA_RUN( port), MC_SWIC_TX_DATA_IR( port)); if( (p_job=(dma_tx_job_t *)fifo_get_first_held( pff_job)) ) { if (p_job->stat == DMA_JOB_STAT_RUN) { ir = MC_SWIC_TX_DATA_IR(port); run = MC_SWIC_TX_DATA_RUN(port); //PRINT( "dma_tx_data_ih: run=%Xh ir=%Xh \n", run, ir); p_data = p_job->p_data_first; for( ; (1); p_data = p_data->p_next) { pdp = &p_data->reg_init; //PRINT( "dma_tx_data_ih: pdp->ir=%Xh\n", pdp->ir); if( (ir >= pdp->ir) && (ir <= pdp->ir + pdch->sz_data_buf) ) { // current buffer: if( ((run & SWIC_DMA_CSR_END) || (run & SWIC_DMA_CSR_DONE)) && ((run >> SWIC_DMA_CSR_WCX_OFS) & SWIC_DMA_CSR_WCX_MSK)==SWIC_DMA_CSR_WCX_MSK ) { if( p_data == p_job->p_data_last) { p_data->stat = DMA_FI_STAT__SENT; p_job->stat = DMA_JOB_STAT_FIN; PRINT( "dma_tx_data_ih: wake_up(job)\n"); wake_up_interruptible( &pctl->wq_txjob); } } } else p_data->stat = DMA_FI_STAT__SENT; if( p_data->stat == DMA_FI_STAT__SENT) PRINT("dma_tx_data_ih: %Xh is sent\n", p_data->reg_init.ir); if( p_data == p_job->p_data_last) break; } } else PRINT( "dma_tx_data_ih: running DMA tx job is absent!\n"); } else PRINT( "dma_tx_data_ih: current DMA tx job is absent!\n"); // Снимаем признак прерывания //PRINT( "dma_tx_data_ih: RUN_TX_DAT=%Xh ir=%Xh first_held=%p\n", // MC_RUN_GSPW_TX_DAT, MC_IR_GSPW_TX_DAT, fifo_get_first_held( pff_job)); MC_SWIC_TX_DATA_CSR( port); return IRQ_HANDLED; } //- end: drv_spw_dma_tx_data_ih // spacewire link change detector: //-> 1 - link is changed, 0 - nothing int spw_change_state( swic_ctrl_t *pctl, u32 stat) { u32 link_stat = ((stat >> SWIC_STAT_LINK_OFS) & SWIC_STAT_LINK_MSK); if( (link_stat == SWIC_STAT_LINK_RUN) && !(stat & SWIC_STAT_NOCON_ERRS) ) { // have connection: if( pctl->connected && pctl->link_stat==link_stat) return 0; // change connection: ++pctl->cnt_int_con; } else { if( !pctl->connected) return 0; // change connection: //--pctl->cnt_int_con; } pctl->link_stat = link_stat; pctl->connected = (link_stat == SWIC_STAT_LINK_RUN) ? 1 : 0; pr_info( "%s: link %s\n", pctl->port.name, (pctl->connected ? "up" : "down")); drv_swic_enable_dma_irqs( pctl->port.num, pctl->connected); return 1; } // Обработчик прерывания по установке соединения irqreturn_t drv_spw_change_state_ih( int irq, void *dev_id) { swic_ctrl_t *pctl = (swic_ctrl_t *)dev_id; u32 port = pctl->port.num; u32 stat = SWIC_STAT_REG( port); //int need_start_dma = 1; u32 speed; pr_info("change_state_ih(%u): irq=%i, stat=%Xh\n", port, irq, stat); //return IRQ_HANDLED; //pr_info( "change_state_ih: '%s' STATE=%Xh cnt=%u\n", pctl->port.name, stat, pctl->cnt_int_con); if( !(stat & SWIC_STAT_NOCON_ERRS)) { if( pctl->mode == SWIC_MODE_WORK) { // В случае разрыва соединения, имевшего скорость > 10 Mbps, сначала понижаем // скорость до 10 Mbps, а после восстановления соединения поднимаем скорость // до прежнего значения. speed = ((SWIC_TX_SPEED_REG(port) >> SWIC_TX_SPEED_COEF_OFS) & SWIC_TX_SPEED_COEF_MSK) * 5; pr_info("change_state_ih: speed=%u\n", speed); if( stat & SWIC_STAT_DC_ERR) { if( (speed != CONFIG_MULTICORE_SWIC_START_SPEED) && (speed == pctl->speed)) { SWIC_TX_SPEED_REG( port) &= ~(SWIC_TX_SPEED_COEF_MSK << SWIC_TX_SPEED_COEF_OFS); SWIC_TX_SPEED_REG( port) |= (((CONFIG_MULTICORE_SWIC_START_SPEED / 5) & SWIC_TX_SPEED_COEF_MSK) << SWIC_TX_SPEED_COEF_OFS); pctl->has_speed_down = 1; pr_info("change_state_ih: set speed=%u\n", CONFIG_MULTICORE_SWIC_START_SPEED); } SWIC_STAT_REG( port) |= SWIC_STAT_ALL_ERRS; return IRQ_HANDLED; } else if( pctl->has_speed_down && (speed != pctl->speed)) { SWIC_TX_SPEED_REG( port) &= ~(SWIC_TX_SPEED_COEF_MSK << SWIC_TX_SPEED_COEF_OFS); SWIC_TX_SPEED_REG( port) |= (((pctl->speed / 5) & SWIC_TX_SPEED_COEF_MSK) << SWIC_TX_SPEED_COEF_OFS); pctl->has_speed_down = 0; pr_info("change_state_ih: set speed=%u\n", pctl->speed); } } SWIC_STAT_REG( port) |= SWIC_STAT_CONNECTED; //SWIC_STAT_REG(pctl->port.num) |= SWIC_STAT_INT_LINK; } else { SWIC_STAT_REG( port) |= SWIC_STAT_ALL_ERRS; pr_info( "Error detected, waiting reconnection\n"); } if( !spw_change_state( pctl, stat)) return IRQ_HANDLED; //pr_info("change_state_ih: MSKR0=%Xh, QSTR0=%Xh\n", MC_MASKR00, MC_QSTR00); //print_mask_qst_regs( "change_state_ih", pctl->port.num); if( !(stat & SWIC_STAT_NOCON_ERRS)) { /*if( *p_need_start_dma) { wake_up_interruptible( &pctl->wq_st); *p_need_start_dma = 0; }*/ wake_up_interruptible( &pctl->wq_st); // Закрываем прерывание, иначе оно будет возникать по приему каждого пакета //MC_SWIC_MODE_CR( pctl->port.num) &= ~SWIC_MODE_LINK_MSK; } return IRQ_HANDLED; } irqreturn_t drv_spw_error_ih( int irq, void *dev_id) { swic_ctrl_t *pctl = (swic_ctrl_t *)dev_id; u32 stat = SWIC_STAT_REG( pctl->port.num); //pr_info( "error_ih: '%s' STATE=%Xh\n", pctl->port.name, stat); SWIC_STAT_REG( pctl->port.num) |= SWIC_STAT_ALL_ERRS; if( !spw_change_state( pctl, stat)) return IRQ_HANDLED; //print_mask_qst_regs( "error_ih", pctl->port.num); return IRQ_HANDLED; } irqreturn_t drv_spw_time_mark_ih( int irq, void *dev_id) { swic_ctrl_t *pctl = (swic_ctrl_t *)dev_id; u32 stat = SWIC_STAT_REG( pctl->port.num); pr_info( "time_mark_ih: '%s' STATE=%Xh\n", pctl->port.name, stat); MC_SWIC_MODE_CR( pctl->port.num) &= ~SWIC_MODE_TIME_MSK; return IRQ_HANDLED; } void print_mask_qst_regs( const char *ps_msg, int port) { PRINT( "%s(%i): MC_MASKR00 = %08X, MC_QSTR00 = %08X\n", ps_msg, port, MC_MASKR00, MC_QSTR00); //PRINT( "%s(%i): MC_MASKR10 = %08X, MC_QSTR10 = %08X\n", ps_msg, port, MC_MASKR10, MC_QSTR10); }