/* * SWIC driver for MCT-04EM-3U evaluation kit * * Copyright (c) 2020 Elvees (support@elvees.com) * Author: Dmitry Evtushenko * */ #define MULTICORE_DEBUG #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //#include #include #include #include #include #include #include #include #include #include #include //#include #include #include #include #include "swic.h" #include "func.h" #include "reg.h" #include "irq.h" #include "ctl.h" #include "../ua_mem.h" #ifdef USE_ETHOSPW #include "ethospw.h" #endif //USE_ETHOSPW //#define SWIC_MAJOR 251 #define SWIC_MAJOR 249 #ifdef USE_ETHOSPW //#define DEV_NET_NAME "ethospw" #endif //USE_ETHOSPW #define ETHOSPW_DEV_NAME "ethospw" //!TODO: перенести в ethospw или ethoswic ! #define INTF_NAME(is_net) (is_net ? ETHOSPW_DEV_NAME : SWIC_DEV_NAME) #define DEV_NAME SWIC_DEV_NAME // DEBUG: #define DEBUG_DMA_RX_DESC #define DEBUG_DMA_RX_DATA #define DEBUG_DMA_TX_DESC #define DEBUG_DMA_TX_DATA // Макросы для вычисления номеров прерываний //-EDN:#define IRQ_SHIFT 3 //+EDN: #ifdef IRQ_SHIFT #undef IRQ_SHIFT #define IRQ_SHIFT 3 //8 #endif //IRQ_SHIFT //#define spw_err_irq( port_id) (IRQ_SPW0 + ((port_id) << IRQ_SHIFT)) //#define spw_time_irq( port_id) (IRQ_SPW0 + ((port_id) << IRQ_SHIFT)) //#define spw_link_irq( port_id) (IRQ_SPW0 + ((port_id) << IRQ_SHIFT)) #define spw_change_state_irq( port_id) (IRQ_SW_LINK0 + ((port_id) << IRQ_SHIFT)) #define spw_error_irq( port_id) (IRQ_SW_ERR0 + ((port_id) << IRQ_SHIFT)) #define spw_time_mark_irq( port_id) (IRQ_SW_TIME0 + ((port_id) << IRQ_SHIFT)) #define spw_dma_rx_desc_irq( port_id) (IRQ_SW_RX_DES_CH0 + ((port_id) << IRQ_SHIFT)) #define spw_dma_rx_data_irq( port_id) (IRQ_SW_RX_DAT_CH0 + ((port_id) << IRQ_SHIFT)) #define spw_dma_tx_desc_irq( port_id) (IRQ_SW_TX_DES_CH0 + ((port_id) << IRQ_SHIFT)) #define spw_dma_tx_data_irq( port_id) (IRQ_SW_TX_DAT_CH0 + ((port_id) << IRQ_SHIFT)) //int is_init_swic = 0; //gspw_t gspw_chan[ NB_OF_CHANNELS]; struct class *p_swic_class; // Класс устройств SWIC //gspw_switch_t gspw_sw; swic_t swic; int is_init_tx_desc_chain = 0; //////////////////// functions: //////////////////// // timeval to time in mks: static inline u64 get_time_mks( struct timeval *pTv) { //printf( "time: %u %u\n", pTv->tv_sec, pTv->tv_usec); return (1000000LL * pTv->tv_sec) + pTv->tv_usec; } // -> time in mks: static inline u64 get_time_of_day( void) { struct timeval tv; do_gettimeofday( &tv); return get_time_mks( &tv); } //////////////////// swic_desc_t: //////////////////// void swic_desc_print( swic_desc_t *pd) { pr_info("DESC: valid=%i sz=%u type=%i\n", pd->valid, pd->size, pd->type_ep); } //////////////////// dma_desc_fi_t: //////////////////// void dma_desc_fi_print( dma_desc_fi_t *pdd) { swic_desc_print( &pdd->desc); pr_info(" flag=%Xh\n", pdd->flag); } //////////////////// spw_port_t: //////////////////// static int spw_port__init( spw_port_t *p_port, int port_num, const char *port_name) { memset( p_port, 0, sizeof(*p_port)); if( port_name && *port_name) strncpy( p_port->name, port_name, sizeof(p_port->name)); else snprintf( p_port->name, sizeof(p_port->name), "%s%d", SWIC_DEV_NAME, port_num); p_port->num = port_num; pr_info( "spw_port__init: p_port->name='%s'\n", p_port->name); return 0; } //////////////////// dma_chain_t: //////////////////// static void *dma_fifo_create_desc( void *pv) { //dma_chain_t *p_dch = (dma_chain_t *)pv; dma_desc_fi_t *pchi; if ((pchi = kcalloc( 1, sizeof(*pchi), GFP_DMA)) == NULL) { pr_info("dma_fifo_create_data: Could not allocate memory for dma_desc_fi_t\n"); return NULL; } pchi->reg_init.ir = mips_virt_to_phys( (unsigned )&pchi->desc); pchi->reg_init.csr = MC_DMA_CSR_IM | MC_DMA_CSR_WN(0) | MC_DMA_CSR_WCX(0) | MC_DMA_CSR_RUN | MC_DMA_CSR_CHEN; //pchi->reg_init.cp = ; return pchi; } static void *dma_fifo_create_data( void *pv) { //dma_data_fi_t *pfi = (dma_data_fi_t *)pv; dma_chain_t *p_dch = (dma_chain_t *)pv; dma_data_fi_t *pchi; if ((pchi = kcalloc( 1, sizeof(*pchi), GFP_DMA)) == NULL) { pr_info("dma_fifo_create_data: Could not allocate memory for dma_data_fi_t\n"); return NULL; } if ((pchi->p_buf = kcalloc( p_dch->sz_data_buf, 1, GFP_DMA)) == NULL) { pr_info("dma_fifo_create_data: Could not allocate memory for data buffer\n"); kfree( pchi); return NULL; } return pchi; } static void dma_fifo_delete_data( void *pi) { dma_data_fi_t *pfi = (dma_data_fi_t *)pi; if( pfi->p_buf) kfree( pfi->p_buf); kfree( pfi); } static int dma_chain_init( dma_chain_t *p_dch, u32 qnt, u32 szi, u32 sz_data_buf, pf_create_fitem_t pf_create, pf_delete_fitem_t pf_delete) { bool rc; memset( p_dch, 0, sizeof(*p_dch)); p_dch->qnt = qnt; p_dch->szi = szi; p_dch->sz_data_buf = sz_data_buf; rc = fifo_init( &p_dch->ff, qnt, szi, pf_create, p_dch, pf_delete, FIFO_OPT__DOUBLE_LINK/*FIFO_OPT__NONE*/); if (!rc) { pr_info("dma_chain_init: fifo_init: Could not allocate memory\n"); return -ENOMEM; } /*p_dch->pa_dma_reg_init = (dma_params_t *)kcalloc( sz_dch, sizeof(dma_params_t)); if( p_dch->pa_dma_reg_init == NULL) { pr_info("dma_chain_init: kcalloc: Could not allocate memory\n"); fifo_free( &p_dch->ff); return false; }*/ return RC_SPW_SUCCESS; } /*bool dma_chain_create_data_bufs( dma_chain_t *p_dch, u32 sz_buf) { fifo_t *pff = &p_dch->ff; dma_data_fi_t *pfi = (dma_data_fi_t *)pff->p_begin; p_dch->sz_data_buf = sz_buf; for( ; (pfi); pfi = pfi->p_next) { if( pfi->p_buf) kfree( pfi->p_buf); if ((pfi->p_buf = kcalloc(sz_buf, 1)) == NULL) { for( pfi=(dma_data_fi_t *)pff->p_begin; (pfi); pfi = pfi->p_next) { if( pfi->p_buf) kfree( pfi->p_buf); } pr_info("dma_chain_init_data_bufs: kcalloc: Could not allocate memory\n"); return false; } } return true; }*/ //////////////////// SWIC DMA: //////////////////// static inline void dma_chain_print( const char *ps_msg, dma_chain_t *pch) { dma_desc_fi_t *pd, *pd_beg; dma_params_t *pdr; int i = 0; pr_info("%s:\n", ps_msg); pd_beg = (dma_desc_fi_t *)pch->ff.p_begin; for( pd=pd_beg; pd; pd=pd->p_next, ++i) { pdr = &pd->reg_init; pr_info("%i) IR=%Xh, CSR=%Xh, CP=%Xh\n", i, pdr->ir, pdr->csr, pdr->cp); if( pd->p_next == pd_beg) break; } } static inline void set_dflt_dma_desc( dma_desc_fi_t *pd #if defined(DEBUG_DMA_RX_DESC) || defined(DEBUG_DMA_TX_DESC) , int ind #endif ) { dma_params_t *pdr = &pd->reg_init; pdr->ir = mips_virt_to_phys( (unsigned )&pd->desc); pdr->csr = MC_DMA_CSR_IM | MC_DMA_CSR_WN(0) | MC_DMA_CSR_RUN | MC_DMA_CSR_WCX(0) | MC_DMA_CSR_CHEN; pdr->cp = mips_virt_to_phys( (unsigned )&pd->p_next->reg_init); /*#if defined(DEBUG_DMA_RX_DESC) || defined(DEBUG_DMA_TX_DESC) pr_info("%i) IR=%Xh, CSR=%Xh, CP=%Xh\n", ind, pdr->ir, pdr->csr, pdr->cp); #endif*/ } static inline void set_dflt_dma_data( dma_data_fi_t *pd, u32 sz_data_buf #if defined(DEBUG_DMA_RX_DATA) || defined(DEBUG_DMA_TX_DATA) , int ind) #endif { dma_params_t *pdr = &pd->reg_init; pdr->ir = mips_virt_to_phys( (unsigned )pd->p_buf); pdr->csr = MC_DMA_CSR_IM | MC_DMA_CSR_WN(0) | MC_DMA_CSR_RUN | MC_DMA_CSR_WCX( (sz_data_buf >> 3) - 1) | MC_DMA_CSR_CHEN /*| MC_DMA_CSR_END | MC_DMA_CSR_DONE*/; pdr->cp = mips_virt_to_phys((unsigned)&pd->p_next->reg_init); //pr_info("%i) IR=%Xh, CSR=%Xh, CP=%Xh\n", ind, pdr->ir, pdr->csr, pdr->cp); /*#if defined(DEBUG_DMA_RX_DATA) || defined(DEBUG_DMA_TX_DATA) pr_info("%i) IR=%Xh, CSR=%Xh, CP=%Xh\n", ind, pdr->ir, pdr->csr, pdr->cp); #endif //defined(DEBUG_DMA_RX_DATA) || defined(DEBUG_DMA_TX_DATA)*/ } static void dma_rx_desc_chain_set_dflt( dma_chain_t *pch) { dma_desc_fi_t *pd, *pd_beg; int i=0; pd_beg = (dma_desc_fi_t *)pch->ff.p_begin; for( pd=pd_beg; pd; pd=pd->p_next) { set_dflt_dma_desc( pd, i++); if( pd->p_next == pd_beg) { pd->reg_init.csr &= ~MC_DMA_CSR_RUN; break; } } /*#ifdef DEBUG_DMA_RX_DESC { int i=0; dma_params_t *pdr; pr_info("RX_DESC:\n"); for( pd=pd_beg; pd; pd=pd->p_next,++i) { pdr = &pd->reg_init; pr_info("%i) CP=%Xh, IR=%Xh, CSR=%Xh\n", i, pdr->cp, pdr->ir, pdr->csr); if( pd->p_next == pd_beg) break; } } #endif //DEBUG_DMA_RX_DESC*/ } static int dma_rx_desc_chain_init( swic_ctrl_t *pctl, const u32 MAX_RX_DESC_QNT) { int rc; dma_chain_t *pch = &pctl->dch_rx_desc; pr_info("dma_rx_desc_chain_init:\n"); rc = dma_chain_init( pch, MAX_RX_DESC_QNT, sizeof(dma_desc_fi_t), sizeof(swic_desc_t), &dma_fifo_create_desc, NULL); if( rc != RC_SPW_SUCCESS) return rc; dma_rx_desc_chain_set_dflt( pch); #ifdef DEBUG_DMA_RX_DESC dma_chain_print( "RX_DESC", pch); #endif //DEBUG_DMA_RX_DESC return RC_SPW_SUCCESS; } static void dma_rx_data_chain_set_dflt( dma_chain_t *pch) { dma_data_fi_t *pd, *pd_beg; int i=0; pd_beg = (dma_data_fi_t *)pch->ff.p_begin; for( pd=pd_beg; pd; pd=pd->p_next) { set_dflt_dma_data( pd, pch->sz_data_buf, i++); if( pd->p_next == pd_beg) { pd->reg_init.csr &= ~MC_DMA_CSR_RUN; break; } } /*#ifdef DEBUG_DMA_RX_DATA { int i=0; dma_params_t *pdr; pr_info("RX_DATA:\n"); for( pd=pd_beg; pd; pd=pd->p_next,++i) { pdr = &pd->reg_init; pr_info("%i) CP=%Xh, IR=%Xh, CSR=%Xh\n", i, pdr->cp, pdr->ir, pdr->csr); if( pd->p_next == pd_beg) break; } } #endif //DEBUG_DMA_RX_DATA */ } static int dma_rx_data_chain_init( swic_ctrl_t *pctl, const u32 qnt_data_buf, const u32 sz_data_buf) { int rc; dma_chain_t *pch = &pctl->dch_rx_data; pr_info("dma_rx_data_chain_init:\n"); rc = dma_chain_init( pch, qnt_data_buf, sizeof(dma_data_fi_t), sz_data_buf, &dma_fifo_create_data, &dma_fifo_delete_data); if( rc != RC_SPW_SUCCESS) return rc; dma_rx_data_chain_set_dflt( pch); #ifdef DEBUG_DMA_RX_DATA dma_chain_print( "RX_DATA", pch); #endif //DEBUG_DMA_RX_DATA return RC_SPW_SUCCESS; } static void dma_tx_desc_chain_set_dflt( dma_chain_t *pch) { dma_desc_fi_t *pd, *pd_beg; int i=0; pd_beg = (dma_desc_fi_t *)pch->ff.p_begin; for( pd=pd_beg; pd; pd=pd->p_next) { set_dflt_dma_desc( pd, i++); //pr_info(" dsc=%Xh ir=%Xh csr=%Xh cp=%Xh\n", (unsigned )&pd->desc, pdr->ir, pdr->csr, pdr->cp); if( pd->p_next == pd_beg) { //pdr->csr &= ~MC_DMA_CSR_CHEN; break; } } /*#ifdef DEBUG_DMA_TX_DESC { int i=0; dma_params_t *pdr; pr_info("TX_DESC:\n"); for( pd=pd_beg; pd; pd=pd->p_next,++i) { pdr = &pd->reg_init; pr_info("%i) CP=%Xh, IR=%Xh, CSR=%Xh\n", i, pdr->cp, pdr->ir, pdr->csr); if( pd->p_next == pd_beg) break; } } #endif //DEBUG_DMA_TX_DESC*/ } static int dma_tx_desc_chain_init( swic_ctrl_t *pctl, const u32 MAX_TX_DESC_QNT) { int rc; dma_chain_t *pch = &pctl->dch_tx_desc; pr_info("dma_tx_desc_chain_init:\n"); rc = dma_chain_init( pch, MAX_TX_DESC_QNT, sizeof(dma_desc_fi_t), sizeof(swic_desc_t), &dma_fifo_create_desc, NULL); if( rc != RC_SPW_SUCCESS) return rc; dma_tx_desc_chain_set_dflt( pch); #ifdef DEBUG_DMA_TX_DESC dma_chain_print( "TX_DESC", pch); #endif //DEBUG_DMA_TX_DESC return RC_SPW_SUCCESS; } static void dma_tx_data_chain_set_dflt( dma_chain_t *pch) { dma_data_fi_t *pd, *pd_beg; int i=0; pd_beg = (dma_data_fi_t *)pch->ff.p_begin; for (pd=pd_beg; pd; pd=pd->p_next) { set_dflt_dma_data( pd, pch->sz_data_buf, i++); /*pdr->ir = mips_virt_to_phys( (unsigned )pd->p_buf); pdr->csr = MC_DMA_CSR_IM | MC_DMA_CSR_WN(0) | MC_DMA_CSR_RUN | MC_DMA_CSR_WCX(0) | MC_DMA_CSR_CHEN; //| MC_DMA_CSR_END | MC_DMA_CSR_DONE | MC_DMA_CSR_WCX((pch->sz_data_buf >> 3) - 1) pdr->cp = mips_virt_to_phys((unsigned)&pd->p_next->reg_init);*/ if( pd->p_next == pd_beg) break; } /*#ifdef DEBUG_DMA_TX_DATA { int i=0; dma_params_t *pdr; pr_info("TX_DATA:\n"); for( pd=pd_beg; pd; pd=pd->p_next,++i) { pdr = &pd->reg_init; pr_info("%i) CP=%Xh, IR=%Xh, CSR=%Xh\n", i, pdr->cp, pdr->ir, pdr->csr); if( pd->p_next == pd_beg) break; } } #endif //DEBUG_DMA_TX_DATA*/ } static int dma_tx_data_chain_init( swic_ctrl_t *pctl, const u32 qnt_data_buf, const u32 sz_data_buf) { int rc; dma_chain_t *pch = &pctl->dch_tx_data; pr_info("dma_tx_data_chain_init:\n"); rc = dma_chain_init( pch, qnt_data_buf, sizeof(dma_data_fi_t), sz_data_buf, &dma_fifo_create_data, &dma_fifo_delete_data); if( rc != RC_SPW_SUCCESS) return rc; dma_tx_data_chain_set_dflt( pch); #ifdef DEBUG_DMA_TX_DATA dma_chain_print( "TX_DATA", pch); #endif //DEBUG_DMA_TX_DATA return RC_SPW_SUCCESS; } int dma_rx_chain_init( swic_ctrl_t *pctl) { int rc; if( (rc=dma_rx_desc_chain_init( pctl, pctl->qnt_rx_desc)) != RC_SPW_SUCCESS) return rc; init_dma_rx_desk_chain_reg( pctl); if( (rc=dma_rx_data_chain_init( pctl, pctl->qnt_rx_data, pctl->sz_rx_data_buf)) != RC_SPW_SUCCESS) return rc; init_dma_rx_data_chain_reg( pctl); return RC_SPW_SUCCESS; } int dma_tx_chain_init( swic_ctrl_t *pctl) { int rc; if( (rc=dma_tx_desc_chain_init( pctl, pctl->qnt_tx_desc))) return rc; if( (rc=dma_tx_data_chain_init( pctl, pctl->qnt_tx_data, pctl->sz_tx_data_buf))) return rc; rc = fifo_init( &pctl->ff_tx_job, pctl->qnt_tx_desc, sizeof(dma_tx_job_t), NULL, NULL, NULL, FIFO_OPT__DOUBLE_LINK); if (!rc) { pr_info("dma_tx_chain_init: Could not allocate memory for tx job list\n"); return -ENOMEM; } //pcfg->p_cur_tx_job = NULL; return RC_SPW_SUCCESS; } void init_dma_rx_desk_chain_reg( swic_ctrl_t *pctl) { fifo_t *pff = &pctl->dch_rx_desc.ff; dma_desc_fi_t *p_desc; if( (p_desc = (dma_desc_fi_t *)fifo_get_first_free( pff)) ) { MC_SWIC_RX_DESC_CP(pctl->port.num) = mips_virt_to_phys( (unsigned )&p_desc->reg_init) | 1; //pr_info("FF: RX_DESC: CP=%Xh, IR=%Xh, CSR=%Xh\n", p_desc->reg_init.cp, // p_desc->reg_init.ir, p_desc->reg_init.csr); //pr_info("RX_DESC: CP=%Xh, IR=%Xh, CSR=%Xh\n", SWIC_DMA_CP( SWIC_RX_DESC_CHAN), // SWIC_DMA_IR( SWIC_RX_DESC_CHAN), SWIC_DMA_CSR( SWIC_RX_DESC_CHAN)); } } void init_dma_rx_data_chain_reg( swic_ctrl_t *pctl) { fifo_t *pff = &pctl->dch_rx_data.ff; dma_data_fi_t *p_data; if( (p_data = (dma_data_fi_t *)fifo_get_first_free( pff)) ) { MC_SWIC_RX_DATA_CP(pctl->port.num) = mips_virt_to_phys( (unsigned )&p_data->reg_init) | 1; //pr_info("FF: RX_DATA: CP=%Xh, IR=%Xh, CSR=%Xh\n", p_data->reg_init.cp, // p_data->reg_init.ir, p_data->reg_init.csr); //pr_info("RX_DATA: CP=%Xh, IR=%Xh, CSR=%Xh\n", SWIC_DMA_CP( SWIC_RX_DATA_CHAN), // SWIC_DMA_IR( SWIC_RX_DATA_CHAN), SWIC_DMA_CSR( SWIC_RX_DATA_CHAN)); } } static void dma_stop( int port) { MC_SWIC_RX_DESC_RUN( port) = 0; MC_SWIC_RX_DATA_RUN( port) = 0; MC_SWIC_TX_DESC_RUN( port) = 0; MC_SWIC_TX_DATA_RUN( port) = 0; } void reset_all_dma( swic_ctrl_t *pctl) { dma_stop( pctl->port.num); swic_reset_tx_rx( pctl); // set DMAs: dma_rx_desc_chain_set_dflt( &pctl->dch_rx_desc); init_dma_rx_desk_chain_reg( pctl); dma_rx_data_chain_set_dflt( &pctl->dch_rx_data); init_dma_rx_data_chain_reg( pctl); dma_tx_desc_chain_set_dflt( &pctl->dch_tx_desc); dma_tx_data_chain_set_dflt( &pctl->dch_tx_data); } //////////////////// swic_ctrl_t: //////////////////// int thread_dma_tx_job( void *pv); int swic_ctrl_init_job_task( swic_ctrl_t *pctl) { if( (pctl->p_task_tx_dma_job = kthread_run( &thread_dma_tx_job, pctl, "swic_dma_tx_job"))==NULL ) { if( IS_ERR( pctl->p_task_tx_dma_job)) { pr_info( "Could not create 'swic_dma_tx_job' thread\n"); return -ENOMEM; } } return RC_SPW_SUCCESS; } int swic_ctrl__init( swic_ctrl_t *pctl, int port_num) { int rc; pr_info( "swic_ctrl__init:\n"); memset( pctl, 0, sizeof(*pctl)); spw_port__init( &pctl->port, port_num, NULL); pctl->speed = 400; //50; //240; //400; #ifdef USE_ETHOSPW mutex_init( &pctl->mtx_rx); #endif //USE_ETHOSPW pctl->qnt_rx_desc = SPW_RX_DESC_QNT; pctl->qnt_rx_data = SPW_RX_DATA_BUF_QNT; pctl->sz_rx_data_buf = SPW_RX_DATA_BUF_SIZE; pctl->qnt_tx_desc = SPW_TX_DESC_QNT; pctl->qnt_tx_data = SPW_TX_DATA_BUF_QNT; pctl->sz_tx_data_buf = SPW_TX_DATA_BUF_SIZE; pctl->max_sz_tx_pack = 65536; pctl->max_send_desc_qnt = 12; //2; //MAX_TX_DESC_QNT / 3; swic_ctrl_stop( pctl); /* // RX: if( (rc=dma_rx_chain_init( pctl)) != RC_SPW_SUCCESS) return rc; // TX: if( (rc=dma_tx_chain_init( pctl)) != RC_SPW_SUCCESS) return rc; */ init_waitqueue_head( &pctl->wq_st); init_waitqueue_head( &pctl->wq_txdesc); init_waitqueue_head( &pctl->wq_txdata); init_waitqueue_head( &pctl->wq_txjob); init_waitqueue_head( &pctl->wq_rx); #ifdef USE_DMA_TEST dma_test_init( &pctl->test); #endif //USE_DMA_TEST if( (rc=swic_ctrl_init_job_task( pctl)) != RC_SPW_SUCCESS) return rc; //pr_info( "swic_ctrl__init: swic_print_dma_run()\n"); //swic_print_dma_run( 0); //swic_print_dma_run( 1); return RC_SPW_SUCCESS; } void swic_ctrl__exit( swic_ctrl_t *pctl) { #ifdef USE_ETHOSPW mutex_destroy( &pctl->mtx_rx); #endif //USE_ETHOSPW } void dma_rx_chain_free( swic_ctrl_t *pctl) { fifo_free( &pctl->dch_rx_desc.ff); fifo_free( &pctl->dch_rx_data.ff); } void dma_tx_chain_free( swic_ctrl_t *pctl) { int rc; //TODO: soft stop needs !!! if( pctl->p_task_tx_dma_job) { if( (rc = kthread_stop( pctl->p_task_tx_dma_job)) < 0) { pr_info( "dma_tx_chain_free: kthread_stop ERROR(%u)\n", rc); } } fifo_free( &pctl->ff_tx_job); fifo_free( &pctl->dch_tx_desc.ff); fifo_free( &pctl->dch_tx_data.ff); } void swic_ctrl_free( swic_ctrl_t *pctl) { dma_rx_chain_free( pctl); dma_tx_chain_free( pctl); } void swic_ctrl_stop( swic_ctrl_t *pctl) { int port = pctl->port.num; PDEBUG( "swic_ctrl_stop:\n"); // Сброс контроллера: SWIC_MODE_REG( port) = SWIC_MODE_LINK_DSBL | SWIC_MODE_LINK_RST | SWIC_MODE_LVDS_LOOPB | SWIC_MODE_CODEC_LOOPB | SWIC_MODE_LINK_LOOPB; dma_stop( port); } void swic_reset_status( int port) { //SWIC_STAT_REG( port) = 0xFFFFFFFF; //- TEST !!! SWIC_STAT_REG( port) = SWIC_STAT_ALL_ERRS /*| SWIC_STAT_CONNECTED*/ | SWIC_STAT_GOT_FIRST_BIT | SWIC_STAT_GOT_TIME | SWIC_STAT_GOT_INT | SWIC_STAT_GOT_ACK | SWIC_STAT_CC_11 | SWIC_STAT_CC_01; } static void swic_ctrl_start( swic_ctrl_t *pctl) { int port = pctl->port.num; u32 speed; // Сброс контроллера: SWIC_MODE_REG( port) = SWIC_MODE_LINK_DSBL | SWIC_MODE_LINK_RST | SWIC_MODE_LVDS_LOOPB | SWIC_MODE_CODEC_LOOPB | SWIC_MODE_LINK_LOOPB; swic_reset_status( port); // Сброс счетчиков принятых пакетов SWIC_CNT_RX_PACK_REG( port) = 0; SWIC_CNT_RX0_PACK_REG( port) = 0; // выставляем стартовую скорость соединения: speed = (CONFIG_MULTICORE_SWIC_START_SPEED / 5); SWIC_TX_SPEED_REG( port) = ((speed & SWIC_TX_SPEED_COEF_MSK) << SWIC_TX_SPEED_COEF_OFS) | ((speed & SWIC_TX_SPEED_TX_10_MSK) << SWIC_TX_SPEED_TX_10_OFS) | SWIC_TX_SPEED_PLL_TX_EN | SWIC_TX_SPEED_LVDS_EN | SWIC_TX_SPEED_PLL_TX_EN_10 | SWIC_TX_SPEED_LVDS_EN_10 ; // разрешаем соединение: SWIC_MODE_REG( port) = SWIC_MODE_AUTOSTART | SWIC_MODE_LINKSTART /*| SWIC_MODE_DMA_READY*/ /*| SWIC_MODE_AUTO_SPEED*/ | SWIC_MODE_LINK_MSK | SWIC_MODE_ERR_MSK /*| SWIC_MODE_TIME_MSK*/ ; pctl->cnt_int_con = 0; //pr_info( "swic_ctrl_start: SWIC_STAT(%i)=%Xh\n", port, SWIC_STAT_REG( port)); } void swic_print_dma_run( int num) { pr_info( "DMA RUN registers (%i):\n", num); pr_info( " RX_DESC_RUN=%Xh\n", MC_SWIC_RX_DESC_CSR( num)); pr_info( " RX_DATA_RUN=%Xh\n", MC_SWIC_RX_DATA_CSR( num)); pr_info( " TX_DESC_RUN=%Xh\n", MC_SWIC_TX_DESC_CSR( num)); pr_info( " TX_DATA_RUN=%Xh\n", MC_SWIC_TX_DATA_CSR( num)); } void swic_print_main_regs( int num) { pr_info( "Main registers (%i):\n", num); pr_info( "STAT=%Xh\n", SWIC_STAT_REG(num)); pr_info( "RX_CODE=%Xh\n", SWIC_RX_CODE_REG(num)); pr_info( "MODE=%Xh\n", SWIC_MODE_REG(num)); pr_info( "TX_SPEED=%Xh\n", SWIC_TX_SPEED_REG(num)); pr_info( "TX_CODE=%Xh\n", SWIC_TX_CODE_REG(num)); pr_info( "ISR_L=%Xh\n", SWIC_ISR_L_REG(num)); pr_info( "ISR_H=%Xh\n", SWIC_ISR_H_REG(num)); pr_info( "ISR_TOUT_L=%Xh\n", SWIC_ISR_TOUT_L_REG(num)); pr_info( "ISR_TOUT_H=%Xh\n", SWIC_ISR_TOUT_H_REG(num)); } //////////////////// swic_t: //////////////////// void swic_set_settings( swic_t *p_swic); static int swic__init( swic_t *p_swic) { int i; if( p_swic==NULL) { PDEBUGG ("swic__init ERROR: Main GigaSpaceWire switch object is empty\n"); return -EINVAL; } memset( p_swic, 0, sizeof(*p_swic)); // set 120MHz for PLL_CORE: //MC_CRPLL &= ~(MC_CRPLL_CLK_SEL_CORE_MSK << MC_CRPLL_CLK_SEL_CORE_OFS); //MC_CRPLL |= (((120 / 5) & MC_CRPLL_CLK_SEL_CORE_MSK) << MC_CRPLL_CLK_SEL_CORE_OFS); //snprintf( p_swic->name, sizeof(p_swic->name), "%s", DEV_NAME); //!!! if( (rc=gspw_cfg_ctrl__init( &p_swic->cfg_ctrl)) ) //!!! return rc; //for( i=0; (i < MAX_GSPW_PORT_QNT) ;++i) // gspw_ctrl__init( &p_sw->a_gspwc[i], i); for( i=0; (i < MAX_SPW_PORT_QNT) ;++i) swic_ctrl__init(&p_swic->a_ctrl[i], i); swic_set_settings( p_swic); return RC_SPW_SUCCESS; } void swic_set_settings( swic_t *p_swic) { swic_ctrl_t *pctl; swic_set_t *pset; int i; for( i=0; (i < MAX_SPW_PORT_QNT); ++i) { pctl = &swic.a_ctrl[i]; pset = &pctl->set_new; pset->dma_tx.qnt_desc = pctl->qnt_tx_desc; pset->dma_tx.qnt_data = pctl->qnt_tx_data; pset->dma_tx.sz_data_buf = pctl->sz_tx_data_buf; pset->dma_rx.qnt_desc = pctl->qnt_rx_desc; pset->dma_rx.qnt_data = pctl->qnt_rx_data; pset->dma_rx.sz_data_buf = pctl->sz_rx_data_buf; pset->max_tx_pack_size = pctl->max_sz_tx_pack; pset->max_send_desc_qnt = pctl->max_send_desc_qnt; pset->tx_speed = pctl->speed; } } //////////////////// Driver: //////////////////// // Прототипы функций-обработчиков файловых операций static ssize_t drv_swic_read(struct file *file, char *p_buf, size_t count, loff_t *ppos); static ssize_t drv_swic_write(struct file *file, const char *p_buf, size_t count, loff_t *ppos); static int drv_swic_open(struct inode *inode, struct file *file); static int drv_swic_close(struct inode *inode, struct file *file); //long drv_gspw_ioctl (struct file *file, unsigned int cmd, unsigned long arg); // Доступные файловые операции static struct file_operations swic_fops = { .owner = THIS_MODULE, .llseek = no_llseek, .read = drv_swic_read, .write = drv_swic_write, .open = drv_swic_open, .release = drv_swic_close, .compat_ioctl = drv_swic_ioctl, .unlocked_ioctl = drv_swic_ioctl, }; static inline void swic_print_dsk( const char *ps_msg, swic_desc_t *pd) { PRINT( "%s: pd=0x%p sz=%u ep=%u valid=%u\n", ps_msg, pd, pd->size, pd->type_ep, pd->valid); } void print_fifo_held_qnt( swic_ctrl_t *pctl, const char *psMes) { PRINT( "%s: fifo_qnt: job=%u, tx= %u %u, rx= %u %u\n", psMes, fifo_get_held_qnt( &pctl->ff_tx_job), fifo_get_held_qnt( &pctl->dch_tx_desc.ff), fifo_get_held_qnt( &pctl->dch_tx_data.ff), fifo_get_held_qnt( &pctl->dch_rx_desc.ff), fifo_get_held_qnt( &pctl->dch_rx_data.ff)); } /*static inline int has_new_rx_data_in_cur_buf( fifo_t *pff_data) { dma_data_fi_t *p_data = (dma_data_fi_t *)fifo_get_first_free(pff_data); u32 wcx = (MC_SWIC_RX_DATA_RUN() >> SWIC_DMA_CSR_WCX_OFS) & SWIC_DMA_CSR_WCX_MSK; //PRINT( "RUN=%Xh, IR=%Xh\n", MC_RUN_GSPW_RX_DAT, MC_IR_GSPW_RX_DAT); if( p_data==NULL || wcx==SWIC_DMA_CSR_WCX_MSK) return 0; return (p_data->pos_proc != ((wcx + 1)<<3)) ? 1 : 0; }*/ /*void swic_wake_up_all_rx_thread( void) { int i; for( i=0; i < GSPW_SPW_PORT_QNT; ++i) wake_up_interruptible( &swic.a_ctrl[i].wq_rx); }*/ // RX: static ssize_t drv_swic_read( struct file *file, char *p_buf, size_t size, loff_t *ppos) { unsigned int dev_num = iminor( file->f_dentry->d_inode); u32 spw_num = dev_num; swic_ctrl_t *pctl; dma_desc_fi_t *p_desc = NULL, *pdsk; dma_data_fi_t *p_data = NULL; dma_data_fi_t *p_data_free = NULL; dma_chain_t *pch_desc; dma_chain_t *pch_data; fifo_t *pff_desc; fifo_t *pff_data; int rc; u32 sz_rd_avail = size - *ppos; //- returned buffer available size u32 sz_desc_data; u32 sz_data_buf_avail; u32 len_data; u32 cnt_rx = 0; //- returned data counter u32 ir; //short wcx; u32 len_cur_data; int has_new_rx_data; u32 ir_before_wait; //pr_info( "drv_swic_read: pid=%li tid=%li\n", sys_getpid(), sys_gettid()); if( (dev_num != PORT_SWIC0) && (dev_num != PORT_SWIC1)) { PRINT( "drv_swic_read: Reading from unsupportable port=%d\n", dev_num); return -ENODEV; } pctl = &swic.a_ctrl[dev_num]; if( pctl->mode != SWIC_MODE_WORK) { pctl->is_rx_stop |= (1 << spw_num); return 0; } if( pctl->is_reset_io) return 0; PRINT( "\ndrv_swic_read: chan=%d, size=%u\n", pctl->port.num, size); pch_desc = &pctl->dch_rx_desc; pch_data = &pctl->dch_rx_data; pff_desc = &pch_desc->ff; pff_data = &pch_data->ff; while( !pctl->is_reset_io) { if( pctl->mode != SWIC_MODE_WORK) break; //disable_irq( spw_rx_desc_irq()); //disable_irq( spw_rx_data_irq()); /*if(++i==0) { PRINT("RX DESC/DATA: IR=%Xh, RUN=%Xh / IR=%Xh, RUN=%Xh\n", SWIC_DMA_IR( SWIC_RX_DESC_CHAN), SWIC_DMA_RUN( SWIC_RX_DESC_CHAN), SWIC_DMA_IR( SWIC_RX_DATA_CHAN), SWIC_DMA_RUN( SWIC_RX_DATA_CHAN) ); }*/ //wcx = 0; len_cur_data = 0; if( p_desc==NULL) { p_desc = (dma_desc_fi_t *)fifo_get_first_held( pff_desc); } if( p_data==NULL) p_data = (dma_data_fi_t *)fifo_get_first_held( pff_data); #if 1 //PRINT( "drv_swic_read: p_desc=0x%p, p_data=0x%p\n", p_desc, p_data); if( p_desc && p_data==NULL) { // обрабатываем еще незаполненный буфер: // p_data_free = (dma_data_fi_t *)fifo_get_first_free( pff_data); if( p_data_free==NULL) { if( cnt_rx) break; goto l_wait; } p_data = (dma_data_fi_t *)fifo_get_first_held( pff_data); if( p_data==NULL) p_data = p_data_free; else continue; ir = MC_SWIC_RX_DATA_IR(spw_num); if( (ir >= p_data->reg_init.ir) && (ir < p_data->reg_init.ir + pch_data->sz_data_buf) ) { /*if( ir != MC_IR_GSPW_RX_DAT) { //// текущий активный буфер в цепочке сдвинулся: // continue; }*/ len_cur_data = ir - p_data->reg_init.ir; /*wcx = (MC_RUN_GSPW_RX_DAT >> SWIC_DMA_CSR_WCX_OFS) & SWIC_DMA_CSR_WCX_MSK; len_wcx_data = ((wcx + 1) << 3); if( MC_IR_GSPW_RX_DAT == pch_data->pfi_cur->reg_init.ir) p_data = pch_data->pfi_cur; else { // текущий активный буфер в цепочке сдвинулся: wcx = 0; }*/ } } /*else if( p_desc==NULL && p_data==NULL && pch_data->pfi_cur) { ir = MC_IR_GSPW_RX_DAT; if( ir == pch_data->pfi_cur->reg_init.ir) { wcx = (MC_RUN_GSPW_RX_DAT >> SWIC_DMA_CSR_WCX_OFS) & SWIC_DMA_CSR_WCX_MSK; len_wcx_data = ((wcx + 1) << 3); if( MC_IR_GSPW_RX_DAT == pch_data->pfi_cur->reg_init.ir) p_data = pch_data->pfi_cur; else { // текущий активный буфер в цепочке сдвинулся: wcx = 0; } } if( wcx == 0) { if( cnt_rx) break; goto l_wait; } }*/ #endif //0 //enable_irq( spw_rx_desc_irq()); //enable_irq( spw_rx_data_irq()); if( p_desc==NULL || p_data==NULL || (p_desc && !p_desc->desc.valid)) { //if( p_desc && !p_desc->desc.valid) // PRINT("drv_swic_read: valid==0\n"); if( cnt_rx) break; goto l_wait; } //PRINT("Receive spw packet:\n"); //dma_desc_fi_print( p_desc); //PRINT("RX_DATA_RUN=%Xh\n", SWIC_DMA_RUN( SWIC_RX_DATA_CHAN)); sz_rd_avail = size - (*ppos + cnt_rx); sz_desc_data = p_desc->desc.size - p_desc->pos_proc; sz_data_buf_avail = (len_cur_data ? len_cur_data : pch_data->sz_data_buf) - p_data->pos_proc; //PRINT("drv_swic_read: size=%u, szs: %u %u %u\n", // size, sz_rd_avail, sz_desc_data, sz_data_buf_avail); if( sz_rd_avail == 0) break; len_data = min3( sz_rd_avail, sz_desc_data, sz_data_buf_avail); //PRINT("drv_swic_read: len_data=%u\n", len_data); if( len_data==0) { if( cnt_rx) break; goto l_wait; } PRINT( "drv_swic_read: copy_to_user(%u)\n", len_data); PRINT( "drv_swic_read: unused: %Xh %Xh\n", p_desc->desc.unused, p_desc->desc.unused1); copy_to_user( &p_buf[*ppos + cnt_rx], &p_data->p_buf[p_data->pos_proc], len_data); cnt_rx += len_data; p_desc->pos_proc += len_data; p_data->pos_proc += len_data; if( p_data->pos_proc == pch_data->sz_data_buf) { //PRINT( "drv_swic_read: p_data=%p,%p; stat=%u\n", p_data, p_data_free, p_data->stat); if( (p_data != p_data_free) || (p_data->stat == DMA_FI_STAT__FILLED)) { PRINT("drv_swic_read: free_held data ir=%Xh\n", p_data->reg_init.ir); dma_data_fi_reset_proc( p_data); if( p_data != (dma_data_fi_t *)fifo_free_first_held( pff_data)) PRINT( "drv_swic_read: ERROR: Trying of using free said RX data list from several threads!\n"); p_data->p_prev->reg_init.csr |= SWIC_DMA_CSR_RUN; if( !(MC_SWIC_RX_DATA_RUN(spw_num) & SWIC_DMA_CSR_RUN)) MC_SWIC_RX_DATA_RUN(spw_num) |= SWIC_DMA_CSR_RUN; p_data = NULL; } else { // данные буфера переданы, хотя прерывания по завершению вычитывания буфера еще не было: PRINT( "drv_swic_read: Interrupt is absent at full filled data buffer\n"); } } if( p_desc->pos_proc == p_desc->desc.size) { PRINT( "drv_swic_read: free_held desc ir=%Xh\n", p_desc->reg_init.ir); dma_desc_fi_reset_proc( p_desc); if( p_desc != (dma_desc_fi_t *)fifo_free_first_held( pff_desc)) PRINT( "drv_swic_read: ERROR: Trying of using free said RX descriptor list from several threads!\n"); p_desc->p_prev->reg_init.csr |= SWIC_DMA_CSR_RUN; if( !(MC_SWIC_RX_DESC_RUN(spw_num) & SWIC_DMA_CSR_RUN)) MC_SWIC_RX_DESC_RUN(spw_num) |= SWIC_DMA_CSR_RUN; p_desc = NULL; } //print_dma_rx_desc_regs( "drv_swic_read"); //print_dma_rx_data_regs( "drv_swic_read"); //PRINT( "drv_swic_read: QSTR0=%Xh, MASK0=%X\n", MC_QSTR0, MC_MASKR0); continue; l_wait:; //enable_irq( spw_rx_desc_irq()); //enable_irq( spw_rx_data_irq()); //enable_irq( spw_rx_desc_irq()); /*PRINT("Read: IRQ_MSK0=%Xh, IRQ_REQ0=%Xh\n", SYS_IRQ_MSK0_REG, SYS_IRQ_REQ0_REG); PRINT("RX DESC/DATA: IR=%Xh, RUN=%Xh / IR=%Xh, RUN=%Xh\n", SWIC_DMA_IR( SWIC_RX_DESC_CHAN), SWIC_DMA_RUN( SWIC_RX_DESC_CHAN), SWIC_DMA_IR( SWIC_RX_DATA_CHAN), SWIC_DMA_RUN( SWIC_RX_DATA_CHAN) );*/ wake_up_interruptible( &pctl->wq_txdata); wake_up_interruptible( &pctl->wq_txdesc); wake_up_interruptible( &pctl->wq_txjob); PRINT( "\ndrv_swic_read: wait_event_interruptible()\n"); print_fifo_held_qnt( pctl, "READ_wait"); ir_before_wait = MC_SWIC_RX_DATA_IR(spw_num); if( (rc=wait_event_interruptible( pctl->wq_rx, (pdsk=(dma_desc_fi_t *)fifo_get_first_held( pff_desc)) || (fifo_get_first_held( pff_data) && (ir_before_wait != MC_SWIC_RX_DATA_IR(spw_num))) || (pctl->mode != SWIC_MODE_WORK) /*(has_new_rx_data = has_new_rx_data_in_cur_buf(p_data->reg_init.ir, remains))*/ ) )) { PRINT("drv_swic_read: wait_event_interruptible -> (%i) -ERESTARTSYS\n", rc); //!!! disable_irq( spw_rx_desc_irq()); return -ERESTARTSYS; } has_new_rx_data = (ir_before_wait != MC_SWIC_RX_DATA_IR(spw_num)); PRINT( "drv_swic_read: wait_event_interruptible() -> %Xh %Xh %i: %Xh %Xh\n", (u32 )fifo_get_first_held( pff_desc), (u32 )fifo_get_first_held( pff_data), has_new_rx_data, ir_before_wait, MC_SWIC_RX_DATA_IR(spw_num)); /*print_dma_rx_desc_regs( "READ"); print_dma_rx_data_regs( "READ"); { dma_desc_fi_t *pdes; dma_data_fi_t *pdat; pdes = (dma_desc_fi_t *)fifo_get_first_held( pff_desc); pdat = (dma_data_fi_t *)fifo_get_first_held( pff_data); PRINT("drv_swic_read: held: %Xh %Xh\n", (pdes ? pdes->reg_init.ir:0), (pdat ? pdat->reg_init.ir:0)); pdes = (dma_desc_fi_t *)fifo_get_first_free( pff_desc); pdat = (dma_data_fi_t *)fifo_get_first_free( pff_data); PRINT( "drv_swic_read: free: %Xh %Xh\n", (pdes ? pdes->reg_init.ir:0), (pdat ? pdat->reg_init.ir:0)); }*/ //disable_irq( spw_rx_desc_irq()); /*if( (rc=wait_event_timeout( pcfg->wq_rx, fifo_get_first_held( pff), msecs_to_jiffies(7000)))) { PRINT("drv_swic_read: wait_event_interruptible -> (%i) -ERESTARTSYS\n", rc); return -ERESTARTSYS; }*/ //PRINT( "drv_swic_read: wait_event_interruptible -> OK\n"); //disable_irq( spw_rx_desc_irq()); //SWIC_DMA_CSR( pcfg->rx_desc_buf.dma_chan); // Сброс признака прерывания //SWIC_DMA_CSR( SWIC_RX_DESC_CHAN); } //-end: while(1) if( pctl->mode != SWIC_MODE_WORK) pctl->is_rx_stop = 1; //disable_irq( spw_rx_data_irq()); print_fifo_held_qnt( pctl, "READ"); print_dma_rx_desc_regs( "READ", spw_num); print_dma_rx_data_regs( "READ", spw_num); PRINT( "drv_swic_read: -> %u END\n\n", cnt_rx); // check data: /*{ int i=0; u8 cc = 0; u32 cntErrCC = 0; for( i=0; (i < cnt_rx); ++i, ++cc) { if( (u8 )p_buf[i] != cc) { pr_info( "READ CC ERROR: %.2Xh (wait %.2Xh) in pos: %u\n", (u8 )p_buf[i], cc, i); ++cntErrCC; cc = (u8 )p_buf[i]; } } pr_info( "READ cntErrCC=%u\n", cntErrCC); }*/ return cnt_rx; //completed; } //-end: drv_swic_read() // TX: static inline void dma_tx_job_clear( swic_ctrl_t *pctl, dma_tx_job_t *pj) { dma_chain_t *pch_desc = &pctl->dch_tx_desc; dma_chain_t *pch_data = &pctl->dch_tx_data; fifo_t *pff_desc = &pch_desc->ff; fifo_t *pff_data = &pch_data->ff; dma_desc_fi_t *p_desc, *pdsc; dma_data_fi_t *p_data, *pdat; u32 cntFreeDesc=0, cntFreeData=0; p_desc = pj->p_desc_first; while( (pdsc = (dma_desc_fi_t *)fifo_get_first_held( pff_desc)) ) { //PRINT("dma_tx_job_clear: pdsc->ir=%Xh\n", pdsc->reg_init.ir); for( ; (p_desc); p_desc = p_desc->p_next) { if( pdsc != p_desc) { if( p_desc == pj->p_desc_last) { if( cntFreeDesc==0) { PRINT("Logic ERROR: Held desc item %Xh is out of job desc range [%Xh-%Xh]\n", pdsc->reg_init.ir, pj->p_desc_first->reg_init.ir, pj->p_desc_last->reg_init.ir); } p_desc = NULL; break; } continue; } //PRINT("dma_tx_job_clear: p_desc->ir=%Xh\n", p_desc->reg_init.ir); if (pdsc->stat != DMA_FI_STAT__SENT) PRINT("dma_tx_job_clear: Attempt of clearing descriptor item without sent status\n"); PRINT("dma_tx_job_clear: free dsc: ir=%Xh\n", pdsc->reg_init.ir); dma_desc_fi_reset_proc( pdsc); pdsc->reg_init.csr &= ~SWIC_DMA_CSR_CHEN; if( (pdsc != (dma_desc_fi_t *)fifo_free_first_held( pff_desc)) ) PRINT( "dma_tx_job_clear: Trying of using free said Tx descriptor list from several threads !!!\n"); ++cntFreeDesc; if( p_desc == pj->p_desc_last) p_desc = pj->p_desc_first = pj->p_desc_last = pj->p_desc_cur = NULL; else p_desc = p_desc->p_next; break; } //-end: while() if( p_desc==NULL) break; } //-end: for() p_data = pj->p_data_first; while( (pdat = (dma_data_fi_t *)fifo_get_first_held( pff_data)) ) { //PRINT("dma_tx_job_clear: pdat->ir=%Xh\n", pdat->reg_init.ir); for( ; (p_data); p_data = p_data->p_next) { if( pdat != p_data) { if( p_data == pj->p_data_last) { if( cntFreeData==0) { PRINT("Logic ERROR: Held data item %Xh is out of job data range [%Xh-%Xh]\n", pdat->reg_init.ir, pj->p_data_first->reg_init.ir, pj->p_data_last->reg_init.ir); } p_data = NULL; break; } continue; } //PRINT("dma_tx_job_clear: p_data->ir=%Xh\n", p_data->reg_init.ir); if (pdat->stat != DMA_FI_STAT__SENT) PRINT("dma_tx_job_clear: Attempt of clearing data item without sent status\n"); PRINT("dma_tx_job_clear: free dat: ir=%Xh\n", pdat->reg_init.ir); dma_data_fi_reset_proc( pdat); pdat->reg_init.csr &= ~SWIC_DMA_CSR_CHEN; if( (pdat != (dma_data_fi_t *)fifo_free_first_held( pff_data)) ) PRINT( "dma_tx_job_clear: Trying of using free said Tx data list from several threads !!!\n"); ++cntFreeData; if( p_data == pj->p_data_last) p_data = pj->p_data_first = pj->p_data_last = pj->p_data_cur = NULL; else p_data = p_data->p_next; break; } //-end: while() if( p_data==NULL) break; } //-end: for() //pj->p_desc_first = pj->p_desc_last = pj->p_desc_cur = NULL; //pj->p_data_first = pj->p_data_last = pj->p_data_cur = NULL; if( pj->p_desc_first==NULL && pj->p_data_first==NULL) { pj->stat = DMA_JOB_STAT_FREE; } } //- end: dma_tx_job_clear #if 0 static void check_data_before_tx( gspw_cfg_ctrl_t *pcfg, dma_tx_job_t *p_job) { dma_desc_fi_t *p_desc; dma_data_fi_t *p_data; u32 pos = 0, szData = 0; u32 i, len; u32 sz_data_buf = pcfg->dch_tx_data.sz_data_buf; u8 cc = 0; u32 cntErrCC = 0; for( p_desc = p_job->p_desc_first; (1); p_desc = p_desc->p_next) { szData += p_desc->desc.size; if( p_desc == p_job->p_desc_last) break; } //- end: for() for( p_data = p_job->p_data_first; (1); p_data = p_data->p_next) { len = szData - pos; if( len > sz_data_buf) len = sz_data_buf; for( i=0; (i < len); ++i, ++cc) { if( cc != (u8 )p_data->p_buf[i]) { pr_info( "WRITE CC ERROR: %.2Xh (wait %.2Xh) in pos: %u\n", (u8 )p_data->p_buf[i], cc, i); ++cntErrCC; cc = (u8 )p_data->p_buf[i]; } } pos += i; if( p_data == p_job->p_data_last) break; } pr_info( "check_data_before_tx: WRITE cntErrCC=%u\n", cntErrCC); } #endif //0 int thread_dma_tx_job( void *pv) { swic_ctrl_t *pctl = (swic_ctrl_t *)pv; u32 port = pctl->port.num; fifo_t *pff_job = &pctl->ff_tx_job; dma_tx_job_t *p_job = NULL; dma_desc_fi_t *p_desc; dma_data_fi_t *p_data; u32 v; while(1) { if( pctl->mode == SWIC_MODE_FORCE_STOP) goto l_wait_job; if( (p_job=(dma_tx_job_t *)fifo_get_first_held( pff_job))==NULL ) goto l_wait_job; PRINT( "thread_dma_tx_job: select job %Xh stat=%u\n", (u32 )p_job, p_job->stat); if( p_job->stat == DMA_JOB_STAT_FIN) { dma_tx_job_clear( pctl, p_job); PRINT("thread_dma_tx_job: free job %Xh\n", (u32 )p_job); if( p_job != (dma_tx_job_t *)fifo_free_first_held( pff_job)) PRINT( "thread_dma_tx_job: ERROR: Trying of using free said TX job list from several threads!\n"); //PRINT( "thread_dma_tx_job: wake_up_interruptible()\n"); wake_up_interruptible( &pctl->wq_txdesc); wake_up_interruptible( &pctl->wq_txdata); continue; } if (p_job->stat != DMA_JOB_STAT_READY) goto l_wait_job; PRINT( "start_chain_work: TX_RUN: %Xh %Xh\n", MC_SWIC_TX_DESC_RUN(port), MC_SWIC_TX_DATA_RUN(port)); if( (MC_SWIC_TX_DESC_RUN(port) & SWIC_DMA_CSR_RUN) || (MC_SWIC_TX_DATA_RUN(port) & SWIC_DMA_CSR_RUN)) goto l_wait_job; PRINT( "start_chain_work: start new job\n"); for( p_desc = p_job->p_desc_first; (1); p_desc = p_desc->p_next) { if( p_desc == p_job->p_desc_last) { p_desc->reg_init.csr &= ~SWIC_DMA_CSR_CHEN; p_job->stat = DMA_JOB_STAT_RUN; PRINT(" start desc chain\n"); //check_data_before_tx( pcfg, p_job); MC_SWIC_TX_DESC_CP( port) = mips_virt_to_phys((unsigned )&p_job->p_desc_first->reg_init) | 1; pctl->cnt_tx_desc_send = 0; break; } p_desc->reg_init.csr |= SWIC_DMA_CSR_CHEN; } //- end: for() for (p_data = p_job->p_data_first; (1); p_data = p_data->p_next) { p_data->reg_init.csr &= ~(SWIC_DMA_CSR_WCX_MSK << SWIC_DMA_CSR_WCX_OFS); v = p_data->pos_proc; v = (v >> 3) + ((v & 0x7) ? 1:0) - 1; p_data->reg_init.csr |= ((v & SWIC_DMA_CSR_WCX_MSK) << SWIC_DMA_CSR_WCX_OFS); if( p_data == p_job->p_data_last) { p_data->reg_init.csr &= ~SWIC_DMA_CSR_CHEN; PRINT(" start data chain\n"); MC_SWIC_TX_DATA_CP( port) = mips_virt_to_phys((unsigned)&p_job->p_data_first->reg_init) | 1; break; } p_data->reg_init.csr |= SWIC_DMA_CSR_CHEN; } //- end: for() // debug: /*for( p_desc = p_job->p_desc_first; (1); p_desc = p_desc->p_next) { PRINT(" desc csr=%Xh ir=%Xh sz=%u\n", p_desc->reg_init.csr, p_desc->reg_init.ir, p_desc->desc.size); if( p_desc == p_job->p_desc_last) break; } for (p_data = p_job->p_data_first; (1); p_data = p_data->p_next) { PRINT(" data csr=%Xh ir=%Xh\n", p_data->reg_init.csr, p_data->reg_init.ir); if( p_data == p_job->p_data_last) break; }*/ continue; l_wait_job:; if( pctl->mode != SWIC_MODE_WORK) { pctl->is_tx_job_stop = 1; break; } if( (p_job=(dma_tx_job_t *)fifo_get_first_held( pff_job))==NULL ) { PRINT("thread_dma_tx_job: wait_event_interruptible(job!=NULL):\n"); print_fifo_held_qnt( pctl, "job_NULL"); if( wait_event_interruptible( pctl->wq_txjob, fifo_get_first_held( pff_job))) { //disable_irq( spw_tx_data_irq()); PRINT("thread_dma_tx_job: wait_event_interruptible(job) -> -ERESTARTSYS\n"); return -ERESTARTSYS; } } else if (p_job->stat == DMA_JOB_STAT_RUN) { PRINT("thread_dma_tx_job: wait_event_interruptible(stat=%u):\n", p_job->stat); print_fifo_held_qnt( pctl, "job_RUN"); if( wait_event_interruptible( pctl->wq_txjob, (p_job->stat==DMA_JOB_STAT_FIN))) { //disable_irq( spw_tx_data_irq()); PRINT("thread_dma_tx_job: wait_event_interruptible(job) -> -ERESTARTSYS\n"); return -ERESTARTSYS; } } } //- end: while() return 0; } //- end: thread_dma_tx_job ssize_t swic_write( unsigned dev_num, u32 is_net, const char *p_buf, size_t size, loff_t *ppos) { u32 spw_num = dev_num; swic_ctrl_t *pctl; u32 completed = 0; //- returned data counter dma_tx_job_t *p_job = NULL; fifo_t *pff_job; dma_chain_t *pch_desc; dma_chain_t *pch_data; fifo_t *pff_desc; fifo_t *pff_data; dma_desc_fi_t *p_desc = NULL; dma_data_fi_t *p_data = NULL; u32 sz_wr_avail = size - *ppos; //- returned buffer available size u32 sz_desc_data; u32 sz_data_buf_avail; u32 len_data; int is_run = 0; u32 wcx; //pr_info( "drv_spw_write: pid=%li tid=%li\n", sys_getpid(), sys_gettid()); if( (dev_num != PORT_SWIC0) && (dev_num != PORT_SWIC1)) { PRINT( "swic_write: Writing to unsupportable port: '%s%d'\n", INTF_NAME(is_net), dev_num); return -ENODEV; } pctl = &swic.a_ctrl[spw_num]; if( pctl->mode != SWIC_MODE_WORK) { pctl->is_tx_stop |= (1 << spw_num); return 0; } if( pctl->is_reset_io) return 0; if( pctl->port.net_usage != is_net) { pr_info( "swic_write: Could not write to '%s%d': resourse is busy\n", INTF_NAME(is_net), dev_num); return -EBUSY; } PRINT( "swic_write: spw_num=%d, size=%d\n", spw_num, size); pch_desc = &pctl->dch_tx_desc; pch_data = &pctl->dch_tx_data; pff_desc = &pch_desc->ff; pff_data = &pch_data->ff; pff_job = &pctl->ff_tx_job; while( (sz_wr_avail || (sz_wr_avail==0 && p_desc && p_data)) && !pctl->is_reset_io) { if( pctl->mode != SWIC_MODE_WORK) break; if( p_job==NULL && (p_job=(dma_tx_job_t *)fifo_get_first_free( pff_job))==NULL ) break; if( p_data==NULL && (p_data=(dma_data_fi_t *)fifo_get_first_free( pff_data))==NULL ) goto l_wait_data; if( p_desc==NULL) { if( (p_desc = (dma_desc_fi_t *)fifo_get_first_free( pff_desc))==NULL ) goto l_wait_desc; p_desc->desc.size = pctl->max_sz_tx_pack; } sz_desc_data = p_desc->desc.size - p_desc->pos_proc; sz_data_buf_avail = pch_data->sz_data_buf - p_data->pos_proc; PRINT( "swic_write: sz_wr_avail=%u, sz_desc_data=%u, sz_data_buf_avail=%u\n", sz_wr_avail, sz_desc_data, sz_data_buf_avail); PRINT( "swic_write: p_desc->desc.size=%u, pch_data->sz_data_buf=%u\n", p_desc->desc.size, pch_data->sz_data_buf); len_data = min3( sz_wr_avail, sz_desc_data, sz_data_buf_avail); if( len_data) { //PRINT( "drv_gspw_write: copy_from_user(%u) %u %u %u\n", // len_data, sz_wr_avail, sz_desc_data, sz_data_buf_avail); PRINT( "swic_write: copy_from_user(%u)\n", len_data); copy_from_user( &p_data->p_buf[p_data->pos_proc], &p_buf[*ppos + completed], len_data); if( pch_data->sz_data_buf - (p_data->pos_proc + len_data) > 0) memset(&p_data->p_buf[p_data->pos_proc + len_data], 0xFF, pch_data->sz_data_buf - (p_data->pos_proc + len_data)); } else { PRINT( "swic_write: logic ERROR: min3 -> 0 !!!\n"); break; } if( pctl->mode == SWIC_MODE_FORCE_STOP) break; completed += len_data; p_desc->pos_proc += len_data; p_data->pos_proc += len_data; sz_wr_avail -= len_data; // sz_data_buf = 65536 if( (p_data->pos_proc == pch_data->sz_data_buf) || (len_data && sz_wr_avail == 0) || (len_data && (pctl->mode != SWIC_MODE_WORK)) || ( (p_desc->pos_proc == p_desc->desc.size) && (pctl->max_send_desc_qnt && (pctl->cnt_tx_desc_send + 1) >= pctl->max_send_desc_qnt))) { p_data->reg_init.csr &= ~(SWIC_DMA_CSR_WCX_MSK << SWIC_DMA_CSR_WCX_OFS); wcx = ((p_desc->pos_proc >> 3) + ((p_desc->pos_proc & 7) ? 1 : 0) - 1) & SWIC_DMA_CSR_WCX_MSK; p_data->reg_init.csr |= (wcx << SWIC_DMA_CSR_WCX_OFS); PRINT( "swic_write: + data buf %u, csr=%Xh\n", p_data->pos_proc, p_data->reg_init.csr); if( p_data != (dma_data_fi_t *)fifo_hold_first_free( pff_data)) PRINT( "swic_write: ERROR: Trying of using free said TX data list from several threads!\n"); if( p_job->p_data_first==NULL) { p_job->p_data_first = p_data; p_job->stat = DMA_JOB_STAT_PREP; } p_job->p_data_last = p_data; p_data = NULL; } if( (p_desc->pos_proc == p_desc->desc.size) || (len_data && sz_wr_avail == 0) || (len_data && (pctl->mode != SWIC_MODE_WORK)) ) { is_run = 0; p_desc->desc.size = p_desc->pos_proc; p_desc->desc.type_ep = SPW_EOP; p_desc->desc.unused = 0; p_desc->desc.unused1 = 0; p_desc->desc.valid = 1; //PRINT( "drv_gspw_write: desc: pos_proc=%u %u\n", p_desc->pos_proc, p_desc->desc.size); //=32k PRINT( "swic_write: + desc %u, %u\n", p_desc->desc.size, completed); if( p_desc != (dma_desc_fi_t *)fifo_hold_first_free( pff_desc)) PRINT( "swic_write: ERROR: Trying of using free said TX descriptor list from several threads!\n"); if( p_job->p_desc_first==NULL) { p_job->p_desc_first = p_desc; p_job->stat = DMA_JOB_STAT_PREP; } p_job->p_desc_last = p_desc; if( pctl->max_send_desc_qnt) ++pctl->cnt_tx_desc_send; if( (pctl->max_send_desc_qnt && pctl->cnt_tx_desc_send >= pctl->max_send_desc_qnt) || sz_wr_avail==0 || (pctl->mode != SWIC_MODE_WORK) ) { p_job->stat = DMA_JOB_STAT_READY; //p_job->pspw = pspw; PRINT( "swic_write: add new job %Xh\n", (u32 )p_job); if( p_job != (dma_tx_job_t *)fifo_hold_first_free( pff_job)) PRINT( "swic_write: ERROR: Trying of using free said TX job list from several threads!\n"); pctl->cnt_tx_desc_send = 0; //print_dma_all_regs(); #if 0 if( fifo_get_first_free( pff_desc)==0 || fifo_get_first_free( pff_data)==0 ||fifo_get_first_free( pff_job)==0) { wake_up_interruptible( &pcfg->wq_txjob); stop_wr = 1; } #else wake_up_interruptible( &pctl->wq_txjob); #endif p_job = NULL; p_desc = NULL; p_data = NULL; if( pctl->mode != SWIC_MODE_WORK) break; continue; } p_desc = NULL; continue; } //- end: if( (p_desc->pos_proc == p_desc->desc.size) || (sz_wr_avail == 0)) sz_desc_data = p_desc->desc.size - p_desc->pos_proc; sz_data_buf_avail = pch_data->sz_data_buf - p_data->pos_proc; len_data = min3( sz_wr_avail, sz_desc_data, sz_data_buf_avail); //PRINT("drv_gspw_write: min3=%u\n", len_data); if( len_data==0 ) break; continue; l_wait_data:; wake_up_interruptible( &pctl->wq_txjob); p_job = NULL; p_desc = NULL; p_data = NULL; //enable_irq( spw_tx_data_irq()); if( (p_data=(dma_data_fi_t *)fifo_get_first_free( pff_data))==NULL ) { PRINT("gspw_dev_write: wait_event_interruptible(data):\n"); print_fifo_held_qnt( pctl, "write_data_wait"); if( wait_event_interruptible( pctl->wq_txdata, fifo_get_first_free( pff_data))) { //disable_irq( spw_tx_data_irq()); PRINT("gspw_dev_write: wait_event_interruptible(data) -> -ERESTARTSYS\n"); return -ERESTARTSYS; } } //disable_irq( spw_tx_data_irq()); continue; l_wait_desc:; wake_up_interruptible( &pctl->wq_txjob); p_job = NULL; p_desc = NULL; p_data = NULL; //enable_irq( spw_tx_desc_irq()); if( (p_desc=(dma_desc_fi_t *)fifo_get_first_free( pff_desc))==NULL ) { /*PRINT*/pr_info("gspw_dev_write: wait_event_interruptible(desc):\n"); print_fifo_held_qnt( pctl, "write_desc_wait"); //wait_event_timeout() if( wait_event_interruptible( pctl->wq_txdesc, fifo_get_first_free( pff_desc))) { //disable_irq( spw_tx_desc_irq()); PRINT("gspw_dev_write: wait_event_interruptible(desc) -> -ERESTARTSYS\n"); return -ERESTARTSYS; } } //disable_irq( spw_tx_desc_irq()); } //- end: while(1) if( pctl->mode != SWIC_MODE_WORK) pctl->is_tx_stop |= (1 << spw_num); //pr_info( "drv_gspw_write: END mutex_unlock() tid=%li\n", sys_gettid()); PRINT( "gspw_dev_write: END: '%s%d' completed=%d\n", INTF_NAME(is_net), dev_num, completed); return completed; } //- end: drv_gspw_write() static ssize_t drv_swic_write( struct file *file, const char *p_buf, size_t size, loff_t *ppos) { unsigned int minor = iminor( file->f_dentry->d_inode); return swic_write( minor, 0, p_buf, size, ppos); } #define MAX_SWIC_IRQ_QNT 7 #define SWIC_CONNECT_IRQ_FIRST 0 #define SWIC_CONNECT_IRQ_LAST 1 #define SWIC_DMA_IRQ_FIRST 3 #define SWIC_DMA_IRQ_LAST (MAX_SWIC_IRQ_QNT - 1) struct swic_irq_data_s { int irq; irq_handler_t pf_handler; }; static struct swic_irq_data_s a_irq_data[MAX_SWIC_IRQ_QNT]= { { spw_change_state_irq(0), drv_spw_change_state_ih }, { spw_error_irq(0), drv_spw_error_ih }, { spw_time_mark_irq(0), drv_spw_time_mark_ih }, { spw_dma_rx_desc_irq(0), drv_spw_dma_rx_desc_ih }, { spw_dma_rx_data_irq(0), drv_spw_dma_rx_data_ih }, { spw_dma_tx_desc_irq(0), drv_spw_dma_tx_desc_ih }, { spw_dma_tx_data_irq(0), drv_spw_dma_tx_data_ih } }; static int is_irq_connect_ena[MAX_SWIC_IRQ_QNT] = { 0, 0 }; static int is_irq_dma_ena[MAX_SWIC_IRQ_QNT] = { 0, 0 }; void drv_swic_enable_connect_irqs( unsigned dev_num, int ena) { struct swic_irq_data_s *pirqdat; int i, irq; if( ena == is_irq_connect_ena[dev_num]) return; //print_mask_qst_regs( "enable_connect_irqs", dev_num); pr_info( "%s%u: %s_connect_irqs:\n", DEV_NAME, dev_num, ena ? "enable" : "disable"); for( i=SWIC_CONNECT_IRQ_FIRST; (i <= SWIC_CONNECT_IRQ_LAST); ++i) { pirqdat = &a_irq_data[i]; irq = (pirqdat->irq + (dev_num << IRQ_SHIFT)); //pr_info( "gspw_dev_open: disable_irq\n"); ena ? enable_irq( irq) : disable_irq( irq); } is_irq_connect_ena[dev_num] = ena; //print_mask_qst_regs( "enable_connect_irqs", dev_num); } void drv_swic_enable_dma_irqs( unsigned dev_num, int ena) { struct swic_irq_data_s *pirqdat; int i, irq; if( ena == is_irq_dma_ena[dev_num]) return; //print_mask_qst_regs( "enable_dma_irqs", dev_num); pr_info( "%s%u: %s_dma_irqs:\n", DEV_NAME, dev_num, ena ? "enable" : "disable"); for( i=SWIC_DMA_IRQ_FIRST; (i <= SWIC_DMA_IRQ_LAST); ++i) { pirqdat = &a_irq_data[i]; irq = (pirqdat->irq + (dev_num << IRQ_SHIFT)); //pr_info( "gspw_dev_open: disable_irq\n"); ena ? enable_irq( irq) : disable_irq( irq); } is_irq_dma_ena[dev_num] = ena; //print_mask_qst_regs( "enable_dma_irqs", dev_num); } // open SWIC port (in configuration mode): static int drv_swic_open( struct inode *p_inode, struct file *p_file) { unsigned dev_num = iminor(p_inode); struct swic_irq_data_s *pirqdat; swic_ctrl_t *pctl; int i; int irq, a_rc_irq[MAX_SWIC_IRQ_QNT]; //fifo_t *pff; //dma_desc_fi_t *p_desc; //dma_data_fi_t *p_data; pr_info( "gspw_dev_open: open device '%s%d'\n", DEV_NAME, dev_num); if( (dev_num != PORT_SWIC0) && (dev_num != PORT_SWIC1)) { pr_info( "drv_swic_open: Device '%s%d' is absent\n", DEV_NAME, dev_num); return -ENXIO; } pctl = &swic.a_ctrl[dev_num]; // Запрещаем повторное открытие порта if( pctl->port.open) return -EBUSY; #ifdef USE_ETHOSPW pr_info( "gspw_dev_open: mutex_lock()\n"); mutex_lock( &pctl->mtx_rx); #endif //!TODO: stop SWIC interface ! memset( a_rc_irq, -1 ,sizeof(a_rc_irq)); //swic_print_main_regs( dev_num); /* pr_info( "drv_gspw_open: MC_MASKR00 = %08X, MC_QSTR00 = %08X\n", MC_MASKR00, MC_QSTR00); pr_info( "drv_gspw_open: MC_MASKR10 = %08X, MC_QSTR10 = %08X\n", MC_MASKR10, MC_QSTR10); swic_print_dma_run( dev_num); */ //print_mask_qst_regs( "drv_swic_open0", dev_num); for( i=0; (i < MAX_SWIC_IRQ_QNT); ++i) { pirqdat = &a_irq_data[i]; irq = (pirqdat->irq + (dev_num << IRQ_SHIFT)); //pr_info( "gspw_dev_open: request_irq(%i)\n", irq); a_rc_irq[i] = request_irq( irq, pirqdat->pf_handler, 0, pctl->port.name, pctl); if( a_rc_irq[i]) { pr_info( "gspw_dev_open: request_irq(%i) ERROR (%i)\n", irq, a_rc_irq[i]); goto l_err; } //pr_info( "gspw_dev_open: disable_irq\n"); disable_irq( irq); //enable_irq( irq) } //print_mask_qst_regs( "drv_swic_open1", dev_num); /*pr_info( "gspw_dev_open: enable_all_irq\n"); for( i=0; (i < MAX_SWIC_IRQ_QNT); ++i) { pirqdat = &a_irq_data[i]; irq = (pirqdat->irq + (dev_num << IRQ_SHIFT)); //pr_info( "gspw_dev_open: disable_irq\n"); enable_irq( irq); //enable_irq( irq) }*/ // RX: //pr_info( "gspw_dev_open: dma_rx_chain_init()\n"); if( dma_rx_chain_init( pctl) != RC_SPW_SUCCESS) goto l_err; // TX: //pr_info( "gspw_dev_open: dma_tx_chain_init()\n"); if( dma_tx_chain_init( pctl) != RC_SPW_SUCCESS) goto l_err; //pr_info( "gspw_cfg_ctrl_start()\n"); //gspw_cfg_ctrl_start( pcfg); //PDEBUG( "GSPW_CFG_PORT_NUM: MASKR0 = %08X, QSTR0 = %08X\n", MC_MASKR0, MC_QSTR0); //pr_info( "STATE=%Xh\n", GSPW_STATE_REG); pr_info( "drv_swic_open: swic_ctrl_start()\n"); swic_ctrl_start( pctl); //print_mask_qst_regs( "drv_swic_open2", dev_num); pctl->port.open = 1; //swic_print_main_regs( dev_num); //pr_info( "drv_gspw_open: MC_MASKR00 = %08X, MC_QSTR00 = %08X\n", MC_MASKR00, MC_QSTR00); //pr_info( "drv_gspw_open: MC_MASKR10 = %08X, MC_QSTR10 = %08X\n", MC_MASKR10, MC_QSTR10); //swic_print_dma_run( dev_num); #ifdef USE_ETHOSPW pr_info( "gspw_dev_open: mutex_unlock()\n"); mutex_unlock( &pctl->mtx_rx); #endif PDEBUG( "drv_swic_open: Device '%s%d' is done!\n", DEV_NAME, dev_num); return RC_SPW_SUCCESS; l_err:; for( i=0; (i < MAX_SWIC_IRQ_QNT); ++i) { if( a_rc_irq[i] == 0) free_irq(a_irq_data[i].irq + (dev_num << IRQ_SHIFT), pctl); else break; } return -EFAULT; return -EFAULT; } int spw_change_state( swic_ctrl_t *pctl, u32 stat); static int drv_swic_close( struct inode *inode, struct file *p_file) { unsigned dev_num = iminor(inode); // получаем номер устройства (канала SpW) swic_ctrl_t *pctl; struct swic_irq_data_s *pirqdat; int irq; int i; if( (dev_num != PORT_SWIC0) && (dev_num != PORT_SWIC1)) { pr_info( "drv_swic_close: Device '%s%d' is absent\n", DEV_NAME, dev_num); return -ENXIO; } pr_info( "drv_swic_close: '%s%d'\n", DEV_NAME, dev_num); pctl = &swic.a_ctrl[dev_num]; if( !pctl->port.open) return RC_SPW_SUCCESS; if( pctl->port.net_usage) { pr_info( "drv_swic_close: Could not close '%s%d': net interface '%s%d' is still opened\n", DEV_NAME, dev_num, ETHOSPW_DEV_NAME, i); return -EBUSY; } for( i=0; (i < MAX_SWIC_IRQ_QNT); ++i) { pirqdat = &a_irq_data[i]; irq = pirqdat->irq + (dev_num << IRQ_SHIFT); free_irq( irq, pctl); } swic_ctrl_stop( pctl); pctl->port.nonblock &= ~O_NONBLOCK; pctl->port.open = 0; spw_change_state( pctl, 0); return 0; } static const unsigned EXPECTED_HW_VER = 0x00000005; static int drv_swic_module_init(void) { int rc = -EPERM; int err = 0; PDEBUG( "drv_swic_module_init:\n"); pr_info( "\ndrv_swic_module_init:\n"); if( (SWIC_HW_VER_REG(0) != EXPECTED_HW_VER) || (SWIC_HW_VER_REG(1) != EXPECTED_HW_VER)) { printk( SWIC_DEV_NAME ": unsupported HW version\n"); return -ENODEV; } //pr_info( "drv_gspw_sw_module_init: register_chrdev()\n"); if( register_chrdev( SWIC_MAJOR, SWIC_DEV_NAME, &swic_fops) < 0) { printk( SWIC_DEV_NAME ": unable to create char device with major number %d\n", SWIC_MAJOR); return -ENODEV; } //pr_info( "drv_gspw_sw_module_init: class_create()\n"); p_swic_class = class_create( THIS_MODULE, SWIC_DEV_NAME); if( IS_ERR( p_swic_class)) { printk( SWIC_DEV_NAME ": class ERROR!\n"); err = PTR_ERR (p_swic_class); rc = EPERM; goto l_err; } pr_info( "drv_swic_module_init: swic__init()\n"); swic__init( &swic); #ifdef USE_ETHOSPW drv_ethospw_init(); #endif //USE_ETHOSPW pr_info( "drv_swic_module_init: done!\n"); return 0; l_err: PDEBUG( "drv_swic_module_init: unregister_chrdev()\n"); unregister_chrdev( SWIC_MAJOR, SWIC_DEV_NAME); return rc; } static void __exit drv_swic_module_exit( void) { swic_ctrl_t *pctl; int i; #ifdef USE_ETHOSPW drv_ethospw_exit(); #endif //USE_ETHOSPW for( i=0; (i < MAX_SPW_PORT_QNT) ;++i) { //!!! set_configure_mode(); //!!! mdelay( 1500); pctl = &swic.a_ctrl[i]; swic_ctrl_stop( pctl); swic_ctrl__exit( pctl); swic_ctrl_free( pctl); } PDEBUG( "drv_swic_module_exit: unregister_chrdev()\n"); unregister_chrdev( SWIC_MAJOR, SWIC_DEV_NAME); if( p_swic_class == NULL) { pr_info( "drv_swic_module_exit: class_destroy()\n"); class_destroy( p_swic_class); } PDEBUGG ("drv_swic_module_exit: done!\n"); } module_init( drv_swic_module_init); module_exit( drv_swic_module_exit); MODULE_DESCRIPTION("Multicore SWIC driver"); MODULE_AUTHOR("Dmitry Evtushenko"); MODULE_LICENSE("GPL");