/////////////////////////////////////////////////////////////////////////////////// // File : nic_driver.c // Date : 23/05/2013 // Author : alain greiner // Copyright (c) UPMC-LIP6 /////////////////////////////////////////////////////////////////////////////////// #include #include #include #include #include #include #if !defined(GIET_USE_IOMMU) # error: You must define GIET_USE_IOMMU in the giet_config.h file #endif #if !defined(SEG_NIC_BASE) # error: You must define SEG_NIC_BASE in the hard_config.h file #endif #if !defined(NB_NIC_CHANNELS) # error: You must define NB_NIC_CHANNELS in the hard_config.h file #endif #if !defined(X_IO) # error: You must define X_IO in the hard_config.h file #endif #if !defined(Y_IO) # error: You must define Y_IO in the hard_config.h file #endif #if ( NB_NIC_CHANNELS > 8 ) # error: NB_NIC_CHANNELS cannot be larger than 8 #endif #if !defined(NB_CMA_CHANNELS) # error: You must define NB_CMA_CHANNELS in the hard_config.h file #endif #if ( NB_CMA_CHANNELS > 8 ) # error: NB_CMA_CHANNELS cannot be larger than 8 #endif #if !defined( USE_IOB ) # error: You must define USE_IOB in the hard_config.h file #endif #if !defined( GIET_CHBUF_NBUFS ) # error: You must define GIET_CHBUF_NBUFS in the giet_config.h file #endif #define in_unckdata __attribute__((section (".unckdata"))) /////////////////////////////////////////////////////////////////////////////// // This low_level function returns the value contained in a channel register. /////////////////////////////////////////////////////////////////////////////// unsigned int _nic_get_channel_register( unsigned int channel, unsigned int index ) { unsigned int* vaddr = (unsigned int*)SEG_NIC_BASE + NIC_CHANNEL_SPAN * channel + index; return _io_extended_read( vaddr ); } /////////////////////////////////////////////////////////////////////////////// // This low-level function set a new value in a channel register. /////////////////////////////////////////////////////////////////////////////// void _nic_set_channel_register( unsigned int channel, unsigned int index, unsigned int value ) { unsigned int* vaddr = (unsigned int*)SEG_NIC_BASE + NIC_CHANNEL_SPAN * channel + index; _io_extended_write( vaddr, value ); } /////////////////////////////////////////////////////////////////////////////// // This low_level function returns the value contained in a global register. /////////////////////////////////////////////////////////////////////////////// unsigned int _nic_get_global_register( unsigned int index ) { unsigned int* vaddr = (unsigned int*)SEG_NIC_BASE + NIC_CHANNEL_SPAN * NB_NIC_CHANNELS + index; return _io_extended_read( vaddr ); } /////////////////////////////////////////////////////////////////////////////// // This low-level function set a new value in a global register. /////////////////////////////////////////////////////////////////////////////// void _nic_set_global_register( unsigned int index, unsigned int value ) { unsigned int* vaddr = (unsigned int*)SEG_NIC_BASE + NIC_CHANNEL_SPAN * NB_NIC_CHANNELS + index; _io_extended_write( vaddr, value ); } //////////////////////////////////////////// int _nic_global_init( unsigned int channels, unsigned int vis, unsigned int bc_enable, unsigned int bypass_enable ) { _nic_set_global_register( NIC_G_VIS , vis ); _nic_set_global_register( NIC_G_NB_CHAN , channels ); _nic_set_global_register( NIC_G_BC_ENABLE , bc_enable ); _nic_set_global_register( NIC_G_BYPASS_ENABLE, bypass_enable ); _nic_set_global_register( NIC_G_ON , 1 ); return 0; } //////////////////////////////////////////// int _nic_channel_init( unsigned int index, unsigned int mac4, unsigned int mac2 ) { unsigned int base = SEG_NIC_BASE; unsigned int extend = (X_IO << Y_WIDTH) + Y_IO; _nic_set_channel_register( index, NIC_RX_DESC_LO_0 + 4096, base ); _nic_set_channel_register( index, NIC_RX_DESC_LO_1 + 4096, base + 0x1000 ); _nic_set_channel_register( index, NIC_TX_DESC_LO_0 + 4096, base + 0x2000 ); _nic_set_channel_register( index, NIC_TX_DESC_LO_1 + 4096, base + 0x3000 ); _nic_set_channel_register( index, NIC_RX_DESC_HI_0 , extend ); _nic_set_channel_register( index, NIC_RX_DESC_HI_1 , extend ); _nic_set_channel_register( index, NIC_TX_DESC_HI_0 , extend ); _nic_set_channel_register( index, NIC_TX_DESC_HI_1 , extend ); _nic_set_channel_register( index, NIC_MAC_4 , mac4 ); _nic_set_channel_register( index, NIC_MAC_2 , mac2 ); _nic_set_channel_register( index, NIC_RX_RUN , 1 ); _nic_set_channel_register( index, NIC_TX_RUN , 1 ); return 0; } ///////////////////////////////////////////////////////////////////////////////////// // Synchronous access functions ///////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////// int _nic_sync_send( unsigned int channel, unsigned long long user_paddr ) { unsigned long long nic_paddr; // nic buffer physical address unsigned int done = 0; unsigned int lsb; unsigned int msb; if ( channel >= NB_NIC_CHANNELS ) { _puts("[GIET ERROR] in _timer_sync_send()\n"); return -1; } // poll the NIC buffers while ( done == 0 ) { // test availability of NIC TX buffer 0 lsb = _nic_get_channel_register( channel, NIC_TX_DESC_LO_0 ); msb = _nic_get_channel_register( channel, NIC_TX_DESC_HI_0 ); if ( (msb & 0x80000000) == 0 ) { msb = msb & 0x0000FFFF; done = 1; continue; } // test availability of NIC TX buffer 1 lsb = _nic_get_channel_register( channel, NIC_TX_DESC_LO_1 ); msb = _nic_get_channel_register( channel, NIC_TX_DESC_HI_1 ); if ( (msb & 0x80000000) == 0 ) { msb = msb & 0x0000FFFF; done = 1; continue; } // random delay (average value: 380 cycle) _random_wait( 8 ); } // make the transfer nic_paddr = (unsigned long long)lsb + (((unsigned long long)msb) << 32); _physical_memcpy( nic_paddr , user_paddr, 4096 ); return 0; } /////////////////////////////////////////////////// int _nic_sync_receive( unsigned int channel, unsigned long long user_paddr ) { unsigned long long nic_paddr; // nic buffer physical address unsigned int done = 0; unsigned int lsb; unsigned int msb; if ( channel >= NB_NIC_CHANNELS ) { _puts("[GIET ERROR] in _timer_sync_receive()\n"); return -1; } // polling the NIC buffers while ( done == 0 ) { // test availability of NIC RX buffer 0 lsb = _nic_get_channel_register( channel, NIC_RX_DESC_LO_0 ); msb = _nic_get_channel_register( channel, NIC_RX_DESC_HI_0 ); if ( (msb & 0x80000000) == 1 ) { msb = msb & 0x0000FFFF; done = 1; continue; } // test availability of NIC RX buffer 1 lsb = _nic_get_channel_register( channel, NIC_RX_DESC_LO_1 ); msb = _nic_get_channel_register( channel, NIC_RX_DESC_HI_1 ); if ( (msb & 0x80000000) == 1 ) { msb = msb & 0x0000FFFF; done = 1; continue; } // random delay (average value: 380 cycle) _random_wait( 8 ); } // make the transfer nic_paddr = (unsigned long long)lsb + (((unsigned long long)msb) << 32); _physical_memcpy( user_paddr, nic_paddr , 4096 ); return 0; } ///////////////////////////////////////////////////////////////////////////////////// // CMA access functions ///////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////// int _nic_cma_receive( unsigned int nic_channel, unsigned int cma_channel, nic_chbuf_t* kernel_chbuf ) { unsigned int nic_chbuf_lsb; // 32 LSB bits of the NIC chbuf physical address unsigned int nic_chbuf_msb; // 16 MSB bits of the NIC chbuf physical address unsigned int mem_chbuf_lsb; // 32 LSB bits of the kernel chbuf physical address unsigned int mem_chbuf_msb; // 16 MSB bits of the kernel chbuf physical address unsigned int ppn; unsigned int flags; // checking parameters if ( nic_channel >= NB_NIC_CHANNELS ) { _puts("[GIET ERROR] in _nic_cma_start_receive() : nic_channel index too large\n"); return -1; } if ( cma_channel >= NB_CMA_CHANNELS ) { _puts("[GIET ERROR] in _nic_cma_start_receive() : cma_channel index too large\n"); return -1; } // get the NIC_RX chbuf descriptor physical address nic_chbuf_lsb = SEG_NIC_BASE + (nic_channel * NIC_CHANNEL_SPAN) + 0x1000; nic_chbuf_msb = (X_IO << Y_WIDTH) + Y_IO; // compute the kernel chbuf physical address unsigned int ptab = _get_context_slot(CTX_PTAB_ID); unsigned int vaddr = (unsigned int)kernel_chbuf; unsigned int ko = _v2p_translate( (page_table_t*)ptab, vaddr, &ppn, &flags ); if ( ko ) { _puts("\n[GIET ERROR] in _nic_cma_start_receive() : kernel buffer unmapped\n"); return -1; } mem_chbuf_lsb = (ppn << 12) | (vaddr & 0x00000FFF); mem_chbuf_msb = ppn >> 20; // initializes CMA registers defining the source chbuf (NIC_RX) _cma_set_register( cma_channel, CHBUF_SRC_DESC , nic_chbuf_lsb ); _cma_set_register( cma_channel, CHBUF_DST_EXT , nic_chbuf_msb ); _cma_set_register( cma_channel, CHBUF_SRC_NBUFS, 2 ); // initializes CMA registers defining the destination chbuf (kernel memory) _cma_set_register( cma_channel, CHBUF_DST_DESC , mem_chbuf_lsb ); _cma_set_register( cma_channel, CHBUF_DST_EXT , mem_chbuf_msb ); _cma_set_register( cma_channel, CHBUF_DST_NBUFS, GIET_CHBUF_NBUFS ); // set buffer size, polling period, and start _cma_set_register( cma_channel, CHBUF_BUF_SIZE , 4096 ); _cma_set_register( cma_channel, CHBUF_PERIOD , 300 ); _cma_set_register( cma_channel, CHBUF_RUN , 1 ); return 0; } ////////////////////////////////////////////////////////// int _nic_cma_send( unsigned int nic_channel, unsigned int cma_channel, nic_chbuf_t* kernel_chbuf ) { unsigned int nic_chbuf_lsb; // 32 LSB bits of the NIC chbuf physical address unsigned int nic_chbuf_msb; // 16 MSB bits of the NIC chbuf physical address unsigned int mem_chbuf_lsb; // 32 LSB bits of the kernel chbuf physical address unsigned int mem_chbuf_msb; // 16 MSB bits of the kernel chbuf physical address unsigned int ppn; unsigned int flags; // checking parameters if ( nic_channel >= NB_NIC_CHANNELS ) { _puts("[GIET ERROR] in _nic_cma_start_send() : nic_channel index too large\n"); return -1; } if ( cma_channel >= NB_CMA_CHANNELS ) { _puts("[GIET ERROR] in _nic_cma_start_send() : cma_channel index too large\n"); return -1; } // get the NIC_TX chbuf descriptor physical address nic_chbuf_lsb = SEG_NIC_BASE + (nic_channel * NIC_CHANNEL_SPAN) + 0x1010; nic_chbuf_msb = (X_IO << Y_WIDTH) + Y_IO; // compute the kernel chbuf physical address unsigned int ptab = _get_context_slot(CTX_PTAB_ID); unsigned int vaddr = (unsigned int)kernel_chbuf; unsigned int ko = _v2p_translate( (page_table_t*)ptab, vaddr, &ppn, &flags ); if ( ko ) { _puts("\n[GIET ERROR] in _nic_cma_start_send() : kernel buffer unmapped\n"); return -1; } mem_chbuf_lsb = (ppn << 12) | (vaddr & 0x00000FFF); mem_chbuf_msb = ppn >> 20; // initializes CMA registers defining the source chbuf (kernel memory) _cma_set_register( cma_channel, CHBUF_SRC_DESC , mem_chbuf_lsb ); _cma_set_register( cma_channel, CHBUF_DST_EXT , mem_chbuf_msb ); _cma_set_register( cma_channel, CHBUF_SRC_NBUFS, GIET_CHBUF_NBUFS ); // initializes CMA registers defining the destination chbuf (NIC_TX) _cma_set_register( cma_channel, CHBUF_DST_DESC , nic_chbuf_lsb ); _cma_set_register( cma_channel, CHBUF_DST_EXT , nic_chbuf_msb ); _cma_set_register( cma_channel, CHBUF_DST_NBUFS, 2 ); // set buffer size, polling period, and start _cma_set_register( cma_channel, CHBUF_BUF_SIZE , 4096 ); _cma_set_register( cma_channel, CHBUF_PERIOD , 300 ); _cma_set_register( cma_channel, CHBUF_RUN , 1 ); return 0; } //////////////////////////////////////////////////////////////////////////////////////////// // Interrupt Service Routines //////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////// void _nic_rx_isr( unsigned int irq_type, unsigned int irq_id, unsigned int channel ) { _puts("[NIC WARNING] RX buffers are full for NIC channel "); _putd( channel ); _puts("\n"); } //////////////////////////////////////// void _nic_tx_isr( unsigned int irq_type, unsigned int irq_id, unsigned int channel ) { _puts("[NIC WARNING] TX buffers are full for NIC channel "); _putd( channel ); _puts("\n"); } // Local Variables: // tab-width: 4 // c-basic-offset: 4 // c-file-offsets:((innamespace . 0)(inline-open . 0)) // indent-tabs-mode: nil // End: // vim: filetype=c:expandtab:shiftwidth=4:tabstop=4:softtabstop=4