/* * GigaSpaceWire switch driver for MC-30SF6EM-6U evaluation kit * * Copyright (c) 2019 Elvees (support@elvees.com) * Author: Dmitry Evtushenko * */ #include "irq.h" #include "reg.h" #include "gspwsw.h" #include "func.h" #include "fifo.h" #include "ctl.h" #include "test.h" #include #include #include #ifdef CONFIG_MC30SF6 #include #endif //CONFIG_MC30SF6 static void set_cur_rx_desc_buf( void) { u32 ir = MC_IR_GSPW_RX_DES; dma_chain_t *pch = &gspw_sw.cfg_ctrl.dch_rx_desc; fifo_t *pff = &pch->ff; dma_desc_fi_t *pfi; gspw_cfg_ctrl_t *pcfg; //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_RUN_GSPW_RX_DES & (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 { pcfg = &gspw_sw.cfg_ctrl; if( pfi->desc.type_ep & SPW_EOP) ++pcfg->rx_eop; else if( pfi->desc.type_ep & SPW_EEP) ++pcfg->rx_eep; pcfg->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) { gspw_cfg_ctrl_t *pcfg = (gspw_cfg_ctrl_t *)dev_id; //fifo_t *pff = &pcfg->dch_rx_desc.ff; //dma_desc_fi_t *pd; //u32 ir; //PRINT( "drv_spw_dma_rx_desc_ih: !!!\n"); if( gspw_sw.mode == GSPW_MODE_CONFIG) { MC_CSR_GSPW_RX_DES; return IRQ_HANDLED; } if( pcfg != &gspw_sw.cfg_ctrl) return IRQ_HANDLED; #ifdef USE_DMA_TEST return dma_test_registrate_rx_desc( pcfg); #endif //USE_DMA_TEST set_cur_rx_desc_buf(); /* 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( &pcfg->wq_rx); PRINT( "Rx descriptor list is overflow !!!\n"); return IRQ_HANDLED; } //if( pdsc->reg_init.ir) // fifo_hold_first_free( &pcfg->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_CSR_GSPW_RX_DES; //PRINT( "drv_spw_dma_rx_desc_ih: wake_up_interruptible()\n"); PRINT( "dma_RX_desc_ih: wake_up(READ)\n"); print_fifo_held_qnt( pcfg, "RX_desc_ih"); //wake_up_interruptible( &pcfg->wq_rx); gspw_wake_up_all_rx_thread(); //PRINT( "drv_spw_dma_rx_desc_ih: END\n"); return IRQ_HANDLED; } static void set_cur_rx_data_buf( void) { short wcx, i, max_itr=5; u32 ir; dma_chain_t *pch = &gspw_sw.cfg_ctrl.dch_rx_data; fifo_t *pff = &pch->ff; dma_data_fi_t *pfi; //PRINT("set_cur_rx_data_buf:\n"); ir = MC_IR_GSPW_RX_DAT; for( i=0; (i < max_itr); ++i) { wcx = (MC_RUN_GSPW_RX_DAT >> SWIC_DMA_CSR_WCX_OFS) & SWIC_DMA_CSR_WCX_MSK; if( wcx != SWIC_DMA_CSR_WCX_MSK ) { ir = MC_IR_GSPW_RX_DAT; 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) { gspw_cfg_ctrl_t *pcfg = (gspw_cfg_ctrl_t *)dev_id; //dma_data_fi_t *pd; //PRINT( "drv_spw_dma_rx_data_ih: !!!\n"); if( gspw_sw.mode == GSPW_MODE_CONFIG) { MC_CSR_GSPW_RX_DAT; return IRQ_HANDLED; } if( pcfg != &gspw_sw.cfg_ctrl) return IRQ_HANDLED; #ifdef USE_DMA_TEST return dma_test_registrate_rx_data( pcfg); #endif //USE_DMA_TEST set_cur_rx_data_buf(); //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_CSR_GSPW_RX_DAT; PRINT( "dma_RX_data_ih: wake_up(READ)\n"); print_fifo_held_qnt( pcfg, "RX_data_ih"); //wake_up_interruptible( &pcfg->rxdatawq); //wake_up_interruptible( &pcfg->wq_rx); gspw_wake_up_all_rx_thread(); //PRINT( " drv_spw_dma_rx_data_ih: END\n"); return IRQ_HANDLED; } #if 0 // Обработчик прерывания по завершению выдачи буфера данных static irqreturn_t drv_spw_dma_tx_data_ih( int irq, void *dev_id) { gspw_cfg_ctrl_t *pcc = dev_id; dma_chain_t *pdch = &pcc->dch_tx_data; fifo_t *pff = &pdch->ff; 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); //PRINT( "drv_spw_dma_tx_data_ih: IRQ_MSK0=%Xh, IRQ_REQ0=%Xh !!!\n", // SYS_IRQ_MSK0_REG, SYS_IRQ_REQ0_REG); if( pcc != &gspw_sw.cfg_ctrl) return IRQ_HANDLED; //ir = MC_IR_GSPW_TX_DAT; while( (p_data = (dma_data_fi_t *)fifo_get_first_held( pff)) ) { ir = MC_IR_GSPW_TX_DAT; run = MC_RUN_GSPW_TX_DAT; pdp = &p_data->reg_init; if( (ir >= pdp->ir) && (ir < pdp->ir + pdch->sz_data_buf) ) { 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 ) ) { break; //- current buffer } } PRINT("dma_tx_data_ih: free: csr=%Xh ir=%Xh\n", pdp->csr, pdp->ir); dma_data_fi_reset_proc( p_data); if( (p_data != (dma_data_fi_t *)fifo_free_first_held( pff)) ) { PRINT( "drv_spw_dma_tx_data_ih: Trying of using held said Tx data list from several threads !!!\n"); } } // Снимаем признак прерывания PRINT( "dma_tx_data_ih: RUN_TX_DAT=%Xh ir=%Xh first_held=%p\n", MC_RUN_GSPW_TX_DAT, ir, fifo_get_first_held( pff)); MC_CSR_GSPW_TX_DAT; //if(MC_RUN_GSPW_TX_DAT & (SWIC_DMA_CSR_DONE | SWIC_DMA_CSR_END)) // MC_CSR_GSPW_TX_DAT; //PRINT( "drv_spw_dma_tx_data_ih: clear irq: IRQ_MSK0=%Xh, IRQ_REQ0=%Xh !!!\n", // SYS_IRQ_MSK0_REG, SYS_IRQ_REQ0_REG); //PRINT( "drv_spw_dma_tx_data_ih: wake_up_interruptible()\n"); wake_up_interruptible( &pcc->wq_txdata); return IRQ_HANDLED; } static irqreturn_t drv_spw_dma_tx_desc_ih( int irq, void *dev_id) { gspw_cfg_ctrl_t *pcc = dev_id; dma_chain_t *pdch = &pcc->dch_tx_desc; fifo_t *pff = &pdch->ff; dma_desc_fi_t *p_desc; dma_params_t *pdp; u32 ir; PRINT( "dma_tx_desc_ih: IRQ_MSK0=%Xh, IRQ_REQ0=%Xh\n", MC_MASKR0, MC_QSTR0); if( pcc != &gspw_sw.cfg_ctrl) return IRQ_HANDLED; //ir = MC_IR_GSPW_TX_DES; while( (p_desc = (dma_desc_fi_t *)fifo_get_first_held( pff)) ) { ir = MC_IR_GSPW_TX_DES; pdp = &p_desc->reg_init; if( (ir >= pdp->ir) && (ir < pdp->ir + pdch->sz_data_buf) ) break; //- current buffer PRINT("dma_tx_desc_ih: free: csr=%Xh ir=%Xh\n", pdp->csr, pdp->ir); dma_desc_fi_reset_proc( p_desc); p_desc->reg_init.csr &= ~SWIC_DMA_CSR_CHEN; if( (p_desc != (dma_desc_fi_t *)fifo_free_first_held( pff)) ) { PRINT( "drv_spw_dma_tx_desc_ih: Trying of using held said Tx descriptor list from several threads !!!\n"); } } // Снимаем признак прерывания: PRINT( "dma_tx_desc_ih: RUN_TX_DES=%Xh ir=%Xh first_held=%p\n", MC_RUN_GSPW_TX_DES, ir, fifo_get_first_held( pff)); MC_CSR_GSPW_TX_DES; //if(MC_RUN_GSPW_TX_DES & (SWIC_DMA_CSR_DONE | SWIC_DMA_CSR_END)) // MC_CSR_GSPW_TX_DES; //PRINT( "drv_spw_dma_tx_desc_ih: wake_up_interruptible()\n"); wake_up_interruptible( &pcc->wq_txdesc); //PRINT( "drv_spw_dma_tx_desc_ih: END\n"); return IRQ_HANDLED; } #endif //0 // Обработчик прерывания по завершению выдачи дескриптора(ов) данных: irqreturn_t drv_spw_dma_tx_desc_ih( int irq, void *dev_id) { gspw_cfg_ctrl_t *pcfg = dev_id; fifo_t *pff_job = &pcfg->ff_tx_job; dma_tx_job_t *p_job = NULL; dma_chain_t *pdch = &pcfg->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( gspw_sw.mode == GSPW_MODE_CONFIG) { MC_CSR_GSPW_TX_DES; return IRQ_HANDLED; } if( pcfg != &gspw_sw.cfg_ctrl) return IRQ_HANDLED; #ifdef USE_DMA_TEST return dma_test_registrate_tx_desc( pcfg); #endif //USE_DMA_TEST if( (p_job=(dma_tx_job_t *)fifo_get_first_held( pff_job)) ) { if( p_job->stat == DMA_JOB_STAT_RUN) { ir = MC_IR_GSPW_TX_DES; run = MC_RUN_GSPW_TX_DES; 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( &pcfg->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) { ++pcfg->tx_packets; pcfg->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_CSR_GSPW_TX_DES; return IRQ_HANDLED; } //- end: drv_spw_dma_tx_desc_ih // Обработчик прерывания по завершению выдачи буфера данных irqreturn_t drv_spw_dma_tx_data_ih( int irq, void *dev_id) { gspw_cfg_ctrl_t *pcfg = dev_id; fifo_t *pff_job = &pcfg->ff_tx_job; dma_tx_job_t *p_job = NULL; dma_chain_t *pdch = &pcfg->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( gspw_sw.mode == GSPW_MODE_CONFIG) { MC_CSR_GSPW_TX_DAT; return IRQ_HANDLED; } if( pcfg != &gspw_sw.cfg_ctrl) return IRQ_HANDLED; #ifdef USE_DMA_TEST return dma_test_registrate_tx_data( pcfg); #endif //USE_DMA_TEST if( (p_job=(dma_tx_job_t *)fifo_get_first_held( pff_job)) ) { if (p_job->stat == DMA_JOB_STAT_RUN) { ir = MC_IR_GSPW_TX_DAT; run = MC_RUN_GSPW_TX_DAT; //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( &pcfg->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_CSR_GSPW_TX_DAT; return IRQ_HANDLED; } //- end: drv_spw_dma_tx_data_ih static int spw_change_state( gspw_ctrl_t *pgsc, u32 stat, int *p_need_start_dma) { u32 stat_spw = GSPW_SPW_STAT_REG( pgsc->port.num); enum EBdsStat bds_stat = ((stat_spw >> GSPW_SPW_STAT_BDS_OFS) & GSPW_SPW_STAT_BDS_MSK); gspw_cfg_ctrl_t *pcfg = &gspw_sw.cfg_ctrl; u32 bit = (1 << pgsc->port.num); if( (stat & (bit << (GSPW_STATE_SPW_CON_MSK_OFS + pgsc->port.num))) && (bds_stat == BDS_STAT_RUN) && !(stat_spw & GSPW_SPW_STAT_NOCON_ERRS) ) { // have connection: if( pgsc->connected && pgsc->bds==bds_stat) return 0; // change connection: pgsc->cnt_int_con = 0; ++pgsc->cnt_int_con; } else { if( !pgsc->connected) return 0; // change connection: pgsc->cnt_int_con = 0; } pr_info("spw_change_state: '%s', STAT_SPW = %Xh, MSKR0=%Xh, QSTR0=%Xh\n", pgsc->port.name, stat_spw, MC_MASKR0, MC_QSTR0); //PDEBUG ("Connected, channel: %d, STATUS = %08X\n", u->port, GSPW_SPW_STAT_REG( u->port)); pr_info( " STATE=%Xh, GSPW_ID_NET_REG=%Xh\n", stat, GSPW_ID_NET_REG); //if( GSPW_SPW_STAT_REG( psw->cfg_ctrl->port) & GSPW_SPW_STAT_NOCON_ERRS) if( !(stat_spw & GSPW_SPW_STAT_NOCON_ERRS)) { //??? MC_SWIC_STATUS( psw->cfg_ctrl->port) |= MC_SWIC_CONNECTED; swic_set_tx_speed( pgsc->port.num, pgsc->speed); if( *p_need_start_dma) { /* - EDN: необходимо перенести этот код в открытие конфигурационного порта!!! : */ //wake_up_interruptible( &u->wq_st); wake_up_interruptible( &pcfg->wq_st); *p_need_start_dma = 0; } // Закрываем прерывание, иначе оно будет возникать по приему каждого пакета //MC_SWIC_MODE_CR( pgsc->port) &= ~MC_SWIC_LINK_MASK; // +EDN: //GSPW_MODE_REG( pgsc->port.num) &= ~GSPW_MODE_LINKSTART; //GSPW_MODE_REG( pgsc->port.num) |= GSPW_MODE_LINK_DISA; } else { //GSPW_SPW_STAT_REG( psw->cfg_ctrl->port) |= GSPW_SPW_STAT_ALL_ERRS; GSPW_SPW_STAT_REG( pgsc->port.num) |= GSPW_SPW_STAT_ALL_ERRS; PDEBUG( "Error detected, waiting reconnection\n"); // +EDN: //GSPW_MODE_REG( pgsc->port.num) &= ~GSPW_MODE_LINK_DISA; //GSPW_MODE_REG( pgsc->port.num) |= GSPW_MODE_LINKSTART; } pgsc->bds = bds_stat; pgsc->connected = (bds_stat == BDS_STAT_RUN) ? 1 : 0; { pgsc->state = 0; if( stat & (bit << GSPW_STATE_SPW_CON_MSK_OFS)) pgsc->state |= (bit << GSPW_STATE_SPW_CON_MSK_OFS); if( stat & (bit << GSPW_STATE_SPW_ERR_MSK_OFS)) pgsc->state |= (bit << GSPW_STATE_SPW_ERR_MSK_OFS); if( stat & (bit << GSPW_STATE_PORT_CON_MSK_OFS)) pgsc->state |= (bit << GSPW_STATE_PORT_CON_MSK_OFS); if( stat & (bit << GSPW_STATE_PORT_ERR_MSK_OFS)) pgsc->state |= (bit << GSPW_STATE_PORT_ERR_MSK_OFS); if( pgsc->state) GSPW_STATE_REG |= pgsc->state; } pr_info( " STATE=%Xh -> %Xh, connection is %s\n", stat, GSPW_STATE_REG, (pgsc->connected ? "on" : "off")); return 1; } // // Обработчик прерывания по установке соединения irqreturn_t drv_spw_change_state_ih( int irq, void *dev_id) { //gspw_t *u = dev_id; //gspw_switch_t *psw = dev_id; gspw_ctrl_t *pgsc = (gspw_ctrl_t *)dev_id; u32 stat = GSPW_STATE_REG; int need_start_dma = 1; int i, first; //pr_info("***EDN: drv_spw_change_state_ih: irq=%i, dev_id=0x%p\n", irq, dev_id); //return IRQ_HANDLED; pr_info( " port=%u cnt=%u STATE=%Xh\n", pgsc->port.num, pgsc->cnt_int_con, stat); for( i=first=pgsc->port.num; (1); ) { spw_change_state( pgsc, stat, &need_start_dma); // далее проверяем по другим портам были ли по ним изменения состояния: if( ++i == MAX_SPW_PORT_QNT) i = 0; if( i==first) break; pgsc = &gspw_sw.a_gspwc[i]; } return IRQ_HANDLED; }