📄 ni_mio_common.c
字号:
#ifdef DEBUG_INTERRUPT rt_printk("ni_mio_common: interrupt: a_status=%04x m0_status=%08x\n", status, m_status); ni_mio_print_status_a(status);#endif#ifdef PCIDMA /* Currently, mite.c requires us to handle LINKC and DONE */ if(m_status & CHSR_LINKC){ ni_sync_ai_dma(devpriv->mite, dev); } if(m_status & CHSR_DONE){ writel(CHOR_CLRDONE, devpriv->mite->mite_io_addr + MITE_CHOR + CHAN_OFFSET(AI_DMA_CHAN)); } if(m_status & ~(CHSR_INT | CHSR_LINKC | CHSR_DONE | CHSR_MRDY | CHSR_DRDY | CHSR_DRQ1 | CHSR_DRQ0 | CHSR_ERROR | CHSR_SABORT | CHSR_XFERR | CHSR_LxERR_mask)){ printk("unknown mite interrupt, ack! (m_status=%08x)\n", m_status); //mite_print_chsr(m_status); mite_dma_disarm(devpriv->mite, AI_DMA_CHAN ); writel(CHOR_DMARESET, devpriv->mite->mite_io_addr + MITE_CHOR + CHAN_OFFSET(AI_DMA_CHAN)); //disable_irq(dev->irq); }#endif /* test for all uncommon interrupt events at the same time */ if(status&(AI_Overrun_St|AI_Overflow_St|AI_SC_TC_Error_St|AI_SC_TC_St|AI_START1_St)){ if(status==0xffff){ rt_printk("ni_mio_common: a_status=0xffff. Card removed?\n"); /* we probably aren't even running a command now, * so it's a good idea to be careful. */ if(s->subdev_flags&SDF_RUNNING){ s->async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA; //comedi_event(dev,s,s->async->events); } return; } if(status&(AI_Overrun_St|AI_Overflow_St|AI_SC_TC_Error_St)){ rt_printk("ni_mio_common: ai error a_status=%04x\n", status); ni_mio_print_status_a(status); ni_ai_reset(dev,dev->subdevices); win_out(AI_Error_Interrupt_Ack, Interrupt_A_Ack_Register); shutdown_ai_command( dev ); s->async->events |= COMEDI_CB_ERROR; comedi_event(dev,s,s->async->events); return; } if(status&AI_SC_TC_St){#ifdef DEBUG_INTERRUPT rt_printk("ni_mio_common: SC_TC interrupt\n");#endif if(!devpriv->ai_continuous){ shutdown_ai_command( dev ); } ack|=AI_SC_TC_Interrupt_Ack; } if(status&AI_START1_St){ ack|=AI_START1_Interrupt_Ack; } }#ifndef PCIDMA if(status&AI_FIFO_Half_Full_St){ ni_handle_fifo_half_full(dev); }#endif // !PCIDMA if( (status & AI_STOP_St) ){ if(devpriv->aimode==AIMODE_SCAN){#ifdef PCIDMA int bytes_in_transit; do{ bytes_in_transit = mite_bytes_in_transit( devpriv->mite, AI_DMA_CHAN ); ni_sync_ai_dma(devpriv->mite, dev); }while( ( s->async->events & COMEDI_CB_EOS ) == 0 && bytes_in_transit );#else ni_handle_fifo_dregs(dev); s->async->events |= COMEDI_CB_EOS;#endif } /* handle special case of single scan using AI_End_On_End_Of_Scan */ if( s->async->cmd.stop_src == TRIG_COUNT && s->async->cmd.stop_arg == 1 ){ shutdown_ai_command( dev ); } /* we need to ack the START, also */ ack|=AI_STOP_Interrupt_Ack|AI_START_Interrupt_Ack; } if(devpriv->aimode==AIMODE_SAMPLE){ ni_handle_fifo_dregs(dev); //s->async->events |= COMEDI_CB_SAMPLE; } if(ack) win_out(ack,Interrupt_A_Ack_Register); comedi_event(dev,s,s->async->events);#ifdef DEBUG_INTERRUPT status=win_in(AI_Status_1_Register); if(status&Interrupt_A_St){ printk("handle_a_interrupt: BUG, didn't clear interrupt. disabling.\n"); win_out(0,Interrupt_Control_Register); }#endif}static void handle_b_interrupt(comedi_device *dev,unsigned short b_status, unsigned int m_status){ comedi_subdevice *s=dev->subdevices+1; //unsigned short ack=0;#ifdef DEBUG_INTERRUPT rt_printk("ni_mio_common: interrupt: b_status=%04x m1_status=%08x\n", b_status,m_status); ni_mio_print_status_b(b_status);#endif#ifdef PCIDMA /* Currently, mite.c requires us to handle LINKC and DONE */ if(m_status & CHSR_LINKC){ mite_handle_b_linkc(devpriv->mite, dev); } if(m_status & CHSR_DONE){ writel(CHOR_CLRDONE, devpriv->mite->mite_io_addr + MITE_CHOR + CHAN_OFFSET(AO_DMA_CHAN)); } if(m_status & ~(CHSR_INT | CHSR_LINKC | CHSR_DONE | CHSR_MRDY | CHSR_DRDY | CHSR_DRQ1 | CHSR_DRQ0 | CHSR_ERROR | CHSR_SABORT | CHSR_XFERR | CHSR_LxERR_mask)){ printk("unknown mite interrupt, ack! (m_status=%08x)\n", m_status); //mite_print_chsr(m_status); mite_dma_disarm(devpriv->mite, AO_DMA_CHAN ); writel(CHOR_DMARESET, devpriv->mite->mite_io_addr + MITE_CHOR + CHAN_OFFSET(AO_DMA_CHAN)); }#endif if(b_status==0xffff)return; if(b_status&AO_Overrun_St){ rt_printk("ni_mio_common: AO FIFO underrun status=0x%04x status2=0x%04x\n",b_status,win_in(AO_Status_2_Register)); ni_ao_reset(dev,s); s->async->events |= COMEDI_CB_OVERFLOW; } if(b_status&AO_BC_TC_St){ MDPRINTK("ni_mio_common: AO BC_TC status=0x%04x status2=0x%04x\n",b_status,win_in(AO_Status_2_Register)); ni_ao_reset(dev,s); s->async->events |= COMEDI_CB_EOA; }#ifndef PCIDMA if(b_status&AO_FIFO_Request_St){ int ret; ret = ni_ao_fifo_half_empty(dev,s); if(!ret){ rt_printk("ni_mio_common: AO buffer underrun\n"); ni_set_bits(dev, Interrupt_B_Enable_Register, AO_FIFO_Interrupt_Enable|AO_Error_Interrupt_Enable, 0); s->async->events |= COMEDI_CB_OVERFLOW; } }#endif b_status=win_in(AO_Status_1_Register); if(b_status&Interrupt_B_St){ if(b_status&AO_FIFO_Request_St){ rt_printk("ni_mio_common: AO buffer underrun\n"); } rt_printk("Ack! didn't clear AO interrupt. b_status=0x%04x\n",b_status); ni_set_bits(dev,Interrupt_B_Enable_Register,~0,0); ni_ao_reset(dev,s); s->async->events |= COMEDI_CB_OVERFLOW; } comedi_event(dev,s,s->async->events);}#ifdef DEBUG_STATUS_Astatic char *status_a_strings[]={ "passthru0","fifo","G0_gate","G0_TC", "stop","start","sc_tc","start1", "start2","sc_tc_error","overflow","overrun", "fifo_empty","fifo_half_full","fifo_full","interrupt_a"};static void ni_mio_print_status_a(int status){ int i; rt_printk("A status:"); for(i=15;i>=0;i--){ if(status&(1<<i)){ rt_printk(" %s",status_a_strings[i]); } } rt_printk("\n");}#endif#ifdef DEBUG_STATUS_Bstatic char *status_b_strings[]={ "passthru1","fifo","G1_gate","G1_TC", "UI2_TC","UPDATE","UC_TC","BC_TC", "start1","overrun","start","bc_tc_error", "fifo_empty","fifo_half_full","fifo_full","interrupt_b"};static void ni_mio_print_status_b(int status){ int i; rt_printk("B status:"); for(i=15;i>=0;i--){ if(status&(1<<i)){ rt_printk(" %s",status_b_strings[i]); } } rt_printk("\n");}#endif#ifndef PCIDMAstatic void ni_ao_fifo_load(comedi_device *dev,comedi_subdevice *s, int n){ comedi_async *async = s->async; comedi_cmd *cmd = &async->cmd; int chan; int i; sampl_t d; u32 packed_data; int range; int offset; int err = 1; offset = 1 << (boardtype.aobits - 1); chan = async->cur_chan; for(i=0;i<n;i++){ err &= comedi_buf_get(async, &d); if(err == 0) break; range = CR_RANGE(cmd->chanlist[chan]); if(!boardtype.ao_unipolar || !(range & 1)){ d -= offset; } if( boardtype.ao_671x ){ packed_data = d & 0xffff; err &= comedi_buf_get(async, &d); if(err == 0) break; d -= offset; chan++; i++; packed_data |= ( d << 16 ) & 0xffff0000; ni_writel( packed_data, DAC_FIFO_Data_611x ); }else{ ni_writew(d, DAC_FIFO_Data); } chan++; chan %= cmd->chanlist_len; } async->cur_chan = chan; if(err==0){ async->events |= COMEDI_CB_OVERFLOW; }}/* * There's a small problem if the FIFO gets really low and we * don't have the data to fill it. Basically, if after we fill * the FIFO with all the data available, the FIFO is _still_ * less than half full, we never clear the interrupt. If the * IRQ is in edge mode, we never get another interrupt, because * this one wasn't cleared. If in level mode, we get flooded * with interrupts that we can't fulfill, because nothing ever * gets put into the buffer. * * This kind of situation is recoverable, but it is easier to * just pretend we had a FIFO underrun, since there is a good * chance it will happen anyway. This is _not_ the case for * RT code, as RT code might purposely be running close to the * metal. Needs to be fixed eventually. */static int ni_ao_fifo_half_empty(comedi_device *dev,comedi_subdevice *s){ int n; n = comedi_buf_read_n_available(s->async); if(n==0){ s->async->events |= COMEDI_CB_OVERFLOW; return 0; } n /= sizeof(sampl_t); if(n>boardtype.ao_fifo_depth/2) n=boardtype.ao_fifo_depth/2; ni_ao_fifo_load(dev,s,n); s->async->events |= COMEDI_CB_BLOCK; return 1;}static int ni_ao_prep_fifo(comedi_device *dev,comedi_subdevice *s){ int n; /* reset fifo */ win_out(0,DAC_FIFO_Clear); /* load some data */ n = comedi_buf_read_n_available(s->async); if(n==0)return 0; n /= sizeof(sampl_t); if(n>boardtype.ao_fifo_depth) n=boardtype.ao_fifo_depth; ni_ao_fifo_load(dev,s,n); return n;}static void ni_ai_fifo_read(comedi_device *dev,comedi_subdevice *s, int n){ comedi_async *async = s->async; int i; u32 dl; sampl_t data; unsigned int mask; int err = 1; mask=(1<<boardtype.adbits)-1; if(boardtype.reg_611x){ for( i = 0; i < n / 2; i++ ){ dl=ni_readl(ADC_FIFO_Data_611x); /* This may get the hi/lo data in the wrong order */ data = (dl>>16) & 0xffff; data += devpriv->ai_offset[ async->cur_chan++ ]; err &= comedi_buf_put(s->async, data); async->cur_chan %= async->cmd.chanlist_len; data = dl & 0xffff; data += devpriv->ai_offset[ async->cur_chan++ ]; err &= comedi_buf_put(s->async, data); async->cur_chan %= async->cmd.chanlist_len; } /* Check if there's a single sample stuck in the FIFO */ if( n % 2){ dl=ni_readl(ADC_FIFO_Data_611x); data = dl & 0xffff; data += devpriv->ai_offset[ async->cur_chan++ ]; err &= comedi_buf_put(s->async, data); async->cur_chan %= async->cmd.chanlist_len; } }else{ for(i=0;i<n;i++){ data=ni_readw(ADC_FIFO_Data_Register); data += devpriv->ai_offset[ async->cur_chan++ ]; async->cur_chan %= async->cmd.chanlist_len; err &= comedi_buf_put(async, data); } } if(err==0){ async->events |= COMEDI_CB_OVERFLOW; }}static void ni_handle_fifo_half_full(comedi_device *dev){ int n; comedi_subdevice *s=dev->subdevices+0; /* if we got a fifo_half_full interrupt, we can transfer fifo/2 samples without checking the empty flag. It doesn't matter if we transfer the rest of the samples, the performance trade-off is minimal (checking empty flag for a few samples vs. having 1% more interrupts.) At really high speeds, it's better to ignore them. */ n=boardtype.ai_fifo_depth/2; ni_ai_fifo_read(dev,s,n);}#endif#ifdef PCIDMAstatic int ni_ai_drain_dma(comedi_device *dev ){ struct mite_struct *mite = devpriv->mite; int i; static const int timeout = 10000; for( i = 0; i < timeout; i++ ) { if( ( win_in( AI_Status_1_Register ) & AI_FIFO_Empty_St ) && mite_bytes_in_transit( mite, AI_DMA_CHAN ) == 0 ) break; comedi_udelay( 1 ); } if( i == timeout ) { rt_printk( "ni_mio_common: wait for dma drain timed out\n" ); return -1; } ni_sync_ai_dma( mite, dev ); return 0;}#endif/* Empties the AI fifo*/static void ni_handle_fifo_dregs(comedi_device *dev){ comedi_subdevice *s=dev->subdevices+0; comedi_async *async = s->async; sampl_t data; u32 dl; int err = 1; if(boardtype.reg_611x){ while((win_in(AI_Status_1_Register)&AI_FIFO_Empty_St) == 0){ dl=ni_readl(ADC_FIFO_Data_611x); /* This may get the hi/lo data in the wrong order */ data = (dl>>16) + devpriv->ai_offset[async->cur_chan++]; async->cur_chan %= async->cmd.chanlist_len; err &= comedi_buf_put(s->async, data); data = (dl&0xffff) + devpriv->ai_offset[async->cur_chan++]; async->cur_chan %= async->cmd.chanlist_len; err &= comedi_buf_put(s->async, data); } }else{ while((win_in(AI_Status_1_Register)&AI_FIFO_Empty_St) == 0){ data=ni_readw(ADC_FIFO_Data_Register); data+=devpriv->ai_offset[async->cur_chan++]; async->cur_chan %= async->cmd.chanlist_len; err &= comedi_buf_put(s->async, data); } } if(err==0){ async->events |= COMEDI_CB_OVERFLOW; }}static void get_last_sample_611x( comedi_device *dev ){ comedi_subdevice *s=dev->subdevices+0; comedi_async *async = s->async; sampl_t data; u32 dl; int err = 1; if( boardtype.reg_611x == 0 ) return; /* Check if there's a single sample stuck in the FIFO */ if(ni_readb(XXX_Status)&0x80){ dl=ni_readl(ADC_FIFO_Data_611x); data = (dl&0xffff) + devpriv->ai_offset[async->cur_chan++]; async->cur_chan %= async->cmd.chanlist_len; err &= comedi_buf_put(s->async, data); } if(err==0){ async->events |= COMEDI_CB_OVERFLOW; }}static void ni_ai_munge(comedi_device *dev, comedi_subdevice *s, void *data, unsigned int num_bytes, unsigned int chan_index ){ comedi_async *async = s->async; unsigned int i; unsigned int length = num_bytes / sizeof( sampl_t ); sampl_t *array = data; for(i = 0; i < length; i++) {#ifdef PCIDMA array[i] = __le16_to_cpu(array[i]);#endif array[i] += devpriv->ai_offset[ chan_index ]; chan_index++; chan_index %= async->cmd.chanlist_len; }}#ifdef PCIDMA
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -