/* comedi/drivers/ni_pcidio.c driver for National Instruments PCI-DIO-96/PCI-6508 National Instruments PCI-DIO-32HS National Instruments PCI-6503 COMEDI - Linux Control and Measurement Device Interface Copyright (C) 1999,2002 David A. Schleef This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* Driver: ni_pcidio.o Description: National Instruments PCI-DIO32HS, PCI-DIO96, PCI-6533, PCI-6503 Author: ds Status: works Devices: [National Instruments] PCI-DIO-32HS (ni_pcidio), PXI-6533, PCI-DIO-96, PCI-DIO-96B, PXI-6508, PCI-6503, PCI-6503B, PCI-6503X, PXI-6503, PCI-6534, PCI-6533 Updated: Sun, 21 Apr 2002 21:03:38 -0700 The DIO-96 appears as four 8255 subdevices. See the 8255 driver notes for details. The DIO32HS board appears as one subdevice, with 32 channels. Each channel is individually I/O configurable. The channel order is 0=A0, 1=A1, 2=A2, ... 8=B0, 16=C0, 24=D0. The driver only supports simple digital I/O; no handshaking is supported. DMA mostly works for the PCI-DIO32HS, but only in timed input mode. This driver could be easily modified to support AT-MIO32HS and AT-MIO96. */ /* This driver is for both the NI PCI-DIO-32HS and the PCI-DIO-96, which have very different architectures. But, since the '96 is so simple, it is included here. Manuals (available from ftp://ftp.natinst.com/support/manuals) 320938c.pdf PCI-DIO-96/PXI-6508/PCI-6503 User Manual 321464b.pdf AT/PCI-DIO-32HS User Manual 341329A.pdf PCI-6533 Register-Level Programmer Manual 341330A.pdf DAQ-DIO Technical Reference Manual */ #define USE_DMA #define DEBUG 1 #define DEBUG_FLAGS #include #include /* for disable_irq */ #include "mite.h" #include "8255.h" #undef DPRINTK #ifdef DEBUG #define DPRINTK(format, args...) printk(format, ## args) #else #define DPRINTK(format, args...) #endif #define PCI_VENDOR_ID_NATINST 0x1093 #define PCI_DIO_SIZE 4096 #define PCI_MITE_SIZE 4096 /* defines for the PCI-DIO-96 */ #define NIDIO_8255_BASE(x) ((x)*4) #define NIDIO_A 0 #define NIDIO_B 4 #define NIDIO_C 8 #define NIDIO_D 12 /* defines for the PCI-DIO-32HS */ #define Window_Address 4 /* W */ #define Interrupt_And_Window_Status 4 /* R */ #define IntStatus1 (1<<0) #define IntStatus2 (1<<1) #define WindowAddressStatus_mask 0x7c #define Master_DMA_And_Interrupt_Control 5 /* W */ #define InterruptLine(x) ((x)&3) #define OpenInt (1<<2) #define Group_Status 5 /* R */ #define DataLeft (1<<0) #define Req (1<<2) #define StopTrig (1<<3) #define Group_1_Flags 6 /* R */ #define Group_2_Flags 7 /* R */ #define TransferReady (1<<0) #define CountExpired (1<<1) #define Waited (1<<5) #define PrimaryTC (1<<6) #define SecondaryTC (1<<7) //#define SerialRose //#define ReqRose //#define Paused #define Group_1_First_Clear 6 /* W */ #define Group_2_First_Clear 7 /* W */ #define ClearWaited (1<<3) #define ClearPrimaryTC (1<<4) #define ClearSecondaryTC (1<<5) #define DMAReset (1<<6) #define FIFOReset (1<<7) #define ClearAll 0xf8 #define Group_1_FIFO 8 /* W */ #define Group_2_FIFO 12 /* W */ #define Transfer_Count 20 #define Chip_ID_D 24 #define Chip_ID_I 25 #define Chip_ID_O 26 #define Chip_Version 27 #define Port_IO(x) (28+(x)) #define Port_Pin_Directions(x) (32+(x)) #define Port_Pin_Mask(x) (36+(x)) #define Port_Pin_Polarities(x) (40+(x)) #define Master_Clock_Routing 45 #define RTSIClocking(x) (((x)&3)<<4) #define Group_1_Second_Clear 46 /* W */ #define Group_2_Second_Clear 47 /* W */ #define ClearExpired (1<<0) #define Port_Pattern(x) (48+(x)) #define Data_Path 64 #define FIFOEnableA (1<<0) #define FIFOEnableB (1<<1) #define FIFOEnableC (1<<2) #define FIFOEnableD (1<<3) #define Funneling(x) (((x)&3)<<4) #define GroupDirection (1<<7) #define Protocol_Register_1 65 #define OpMode Protocol_Register_1 #define RunMode(x) ((x)&7) #define Numbered (1<<3) #define Protocol_Register_2 66 #define ClockReg Protocol_Register_2 #define ClockLine(x) (((x)&3)<<5) #define InvertStopTrig (1<<7) #define Protocol_Register_3 67 #define Sequence Protocol_Register_3 #define Protocol_Register_14 68 /* 16 bit */ #define ClockSpeed Protocol_Register_14 #define Protocol_Register_4 70 #define ReqReg Protocol_Register_4 #define ReqConditioning(x) (((x)&7)<<3) #define Protocol_Register_5 71 #define BlockMode Protocol_Register_5 #define FIFO_Control 72 #define ReadyLevel(x) ((x)&7) #define Protocol_Register_6 73 #define LinePolarities Protocol_Register_6 #define InvertAck (1<<0) #define InvertReq (1<<1) #define InvertClock (1<<2) #define InvertSerial (1<<3) #define OpenAck (1<<4) #define OpenClock (1<<5) #define Protocol_Register_7 74 #define AckSer Protocol_Register_7 #define AckLine(x) (((x)&3)<<2) #define ExchangePins (1<<7) #define Interrupt_Control 75 /* bits same as flags */ #define DMA_Line_Control 76 #define DMAChannel(x) ((x)&0xf) #define Transfer_Size_Control 77 #define TransferWidth(x) ((x)&3) #define TransferLength(x) (((x)&3)<<3) #define RequireRLevel (1<<5) #define Protocol_Register_15 79 #define DAQOptions Protocol_Register_15 #define StartSource(x) ((x)&0x3) #define InvertStart (1<<2) #define StopSource(x) (((x)&0x3)<<3) #define ReqStart (1<<6) #define PreStart (1<<7) #define Pattern_Detection 81 #define DetectionMethod (1<<0) #define InvertMatch (1<<1) #define IE_Pattern_Detection (1<<2) #define Protocol_Register_9 82 #define ReqDelay Protocol_Register_9 #define Protocol_Register_10 83 #define ReqNotDelay Protocol_Register_10 #define Protocol_Register_11 84 #define AckDelay Protocol_Register_11 #define Protocol_Register_12 85 #define AckNotDelay Protocol_Register_12 #define Protocol_Register_13 86 #define Data1Delay Protocol_Register_13 #define Protocol_Register_8 88 /* 32 bit */ #define StartDelay Protocol_Register_8 #define TIMER_BASE 50 /* nanoseconds */ #ifdef USE_DMA #define IntEn (CountExpired|Waited|PrimaryTC|SecondaryTC) #else #define IntEn (TransferReady|CountExpired|Waited|PrimaryTC|SecondaryTC) #endif enum mite_dma_channels { DI_DMA_CHAN = 1, }; static int nidio_attach(comedi_device *dev,comedi_devconfig *it); static int nidio_detach(comedi_device *dev); static comedi_driver driver_pcidio={ driver_name: "ni_pcidio", module: THIS_MODULE, attach: nidio_attach, detach: nidio_detach, }; COMEDI_INITCLEANUP(driver_pcidio); typedef struct{ int dev_id; char *name; int n_8255; int is_diodaq; }nidio_board; static nidio_board nidio_boards[]={ { dev_id: 0x1150, name: "pci-dio-32hs", n_8255: 0, is_diodaq: 1, }, { dev_id: 0x1320, name: "pxi-6533", n_8255: 0, is_diodaq: 1, }, { dev_id: 0x12b0, name: "pci-6534", n_8255: 0, is_diodaq: 1, }, { dev_id: 0x0160, name: "pci-dio-96", n_8255: 4, is_diodaq: 0, }, { dev_id: 0x1630, name: "pci-dio-96b", n_8255: 4, is_diodaq: 0, }, { dev_id: 0x13c0, name: "pxi-6508", n_8255: 4, is_diodaq: 0, }, { dev_id: 0x0400, name: "pci-6503", n_8255: 1, is_diodaq: 0, }, { dev_id: 0x1250, name: "pci-6503b", n_8255: 1, is_diodaq: 0, }, { dev_id: 0x17d0, name: "pci-6503x", n_8255: 1, is_diodaq: 0, }, { dev_id: 0x1800, name: "pxi-6503", n_8255: 1, is_diodaq: 0, }, }; #define n_nidio_boards (sizeof(nidio_boards)/sizeof(nidio_boards[0])) #define this_board ((nidio_board *)dev->board_ptr) static struct pci_device_id ni_pcidio_pci_table[] __devinitdata = { { PCI_VENDOR_ID_NATINST, 0x1150, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, { PCI_VENDOR_ID_NATINST, 0x1320, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, { PCI_VENDOR_ID_NATINST, 0x0160, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, { PCI_VENDOR_ID_NATINST, 0x1630, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, { PCI_VENDOR_ID_NATINST, 0x13c0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, { PCI_VENDOR_ID_NATINST, 0x0400, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, { PCI_VENDOR_ID_NATINST, 0x1250, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, { PCI_VENDOR_ID_NATINST, 0x17d0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, { PCI_VENDOR_ID_NATINST, 0x1800, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, { 0 } }; MODULE_DEVICE_TABLE(pci, ni_pcidio_pci_table); typedef struct{ struct mite_struct *mite; int boardtype; int dio; }nidio96_private; #define devpriv ((nidio96_private *)dev->private) static int ni_pcidio_cmdtest(comedi_device *dev,comedi_subdevice *s, comedi_cmd *cmd); static int ni_pcidio_cmd(comedi_device *dev,comedi_subdevice *s); static int ni_pcidio_inttrig(comedi_device *dev, comedi_subdevice *s, unsigned int trignum); static int nidio_find_device(comedi_device *dev,int bus,int slot); static int ni_pcidio_ns_to_timer(int *nanosec, int round_mode); static void setup_mite_dma(comedi_device *dev,comedi_subdevice *s); #ifdef DEBUG_FLAGS static void ni_pcidio_print_flags(unsigned int flags); static void ni_pcidio_print_status(unsigned int status); #else #define ni_pcidio_print_flags(x) #define ni_pcidio_print_status(x) #endif static int nidio96_8255_cb(int dir,int port,int data,unsigned long iobase) { if(dir){ writeb(data,iobase+port); return 0; }else{ return readb(iobase+port); } } static void nidio_interrupt(int irq, void *d, struct pt_regs *regs) { comedi_device *dev=d; comedi_subdevice *s = dev->subdevices; comedi_async *async = s->async; struct mite_struct *mite = devpriv->mite; struct mite_channel *mite_chan = &mite->channels[ DI_DMA_CHAN ]; //int i, j; long int AuxData = 0; sampl_t data1 = 0; sampl_t data2 = 0; int flags; int status; int work = 0; unsigned int m_status; status = readb(dev->iobase+Interrupt_And_Window_Status); flags = readb(dev->iobase+Group_1_Flags); m_status = readl(mite->mite_io_addr + MITE_CHSR + CHAN_OFFSET( DI_DMA_CHAN )); //interrupcions parasites if(dev->attached == 0){ comedi_error(dev,"premature interrupt"); async->events |= COMEDI_CB_ERROR|COMEDI_CB_EOA; } DPRINTK("ni_pcidio_interrupt: status=0x%02x,flags=0x%02x,m_status=0x%08x\n", status,flags,m_status); ni_pcidio_print_flags(flags); ni_pcidio_print_status(status); #ifdef MITE_DEBUG mite_print_chsr(m_status); #endif //printk("mite_bytes_transferred: %d\n",mite_bytes_transferred(mite,DI_DMA_CHAN)); //mite_dump_regs(mite); //printk("buf[0]=%08x\n",*(unsigned int *)async->prealloc_buf); //printk("buf[4096]=%08x\n",*(unsigned int *)(async->prealloc_buf+4096)); if(m_status & CHSR_INT){ if(m_status & CHSR_LINKC){ unsigned int count; writel(CHOR_CLRLC, mite->mite_io_addr + MITE_CHOR + CHAN_OFFSET(DI_DMA_CHAN)); count = le32_to_cpu(mite_chan->ring[mite_chan->current_link].count); /* XXX need to byteswap */ async->buf_write_count += count; async->buf_write_ptr += count; if(async->buf_write_ptr >= async->prealloc_bufsz){ async->buf_write_ptr -= async->prealloc_bufsz; } mite_chan->current_link++; if(mite_chan->current_link >= mite_chan->n_links){ mite_chan->current_link=0; } } if(m_status & CHSR_DONE){ writel(CHOR_CLRDONE, mite->mite_io_addr + MITE_CHOR + CHAN_OFFSET( DI_DMA_CHAN )); } if(m_status & ~(CHSR_INT | CHSR_LINKC | CHSR_DONE | CHSR_DRDY | CHSR_DRQ1)){ DPRINTK("unknown mite interrupt, disabling IRQ\n"); writel(CHOR_DMARESET, mite->mite_io_addr + MITE_CHOR + CHAN_OFFSET( DI_DMA_CHAN )); disable_irq(dev->irq); } async->events |= COMEDI_CB_BLOCK; } while(status&DataLeft){ work++; if(work>20){ DPRINTK("too much work in interrupt\n"); writeb(0x00,dev->iobase+Master_DMA_And_Interrupt_Control); break; } flags &= IntEn; if(flags & TransferReady){ //DPRINTK("TransferReady\n"); while(flags & TransferReady){ work++; if(work>100){ DPRINTK("too much work in interrupt\n"); writeb(0x00,dev->iobase+Master_DMA_And_Interrupt_Control); goto out; } AuxData = readl(dev->iobase+Group_1_FIFO); data1 = AuxData & 0xffff; data2 = (AuxData & 0xffff0000) >> 16; comedi_buf_put(async,data1); comedi_buf_put(async,data2); //DPRINTK("read:%d, %d\n",data1,data2); flags = readb(dev->iobase+Group_1_Flags); } //DPRINTK("buf_int_count: %d\n",async->buf_int_count); //DPRINTK("1) IntEn=%d,flags=%d,status=%d\n",IntEn,flags,status); //ni_pcidio_print_flags(flags); //ni_pcidio_print_status(status); async->events |= COMEDI_CB_BLOCK; } if(flags & CountExpired){ DPRINTK("CountExpired\n"); writeb(ClearExpired,dev->iobase+Group_1_Second_Clear); async->events |= COMEDI_CB_EOA; writeb(0x00,dev->iobase+OpMode); writeb(0x00,dev->iobase+Master_DMA_And_Interrupt_Control); #ifdef USE_DMA mite_dma_disarm(mite, DI_DMA_CHAN); writel(CHOR_DMARESET, mite->mite_io_addr + MITE_CHOR + CHAN_OFFSET( DI_DMA_CHAN )); #endif break; }else if(flags & Waited){ DPRINTK("Waited\n"); writeb(ClearWaited,dev->iobase+Group_1_First_Clear); writeb(0x00,dev->iobase+Master_DMA_And_Interrupt_Control); async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR; #ifdef USE_DMA mite_dma_disarm(mite, DI_DMA_CHAN); writel(CHOR_DMARESET, mite->mite_io_addr + MITE_CHOR + CHAN_OFFSET( DI_DMA_CHAN )); #endif break; }else if(flags & PrimaryTC){ DPRINTK("PrimaryTC\n"); writeb(ClearPrimaryTC,dev->iobase+Group_1_First_Clear); async->events |= COMEDI_CB_EOA; writeb(0x00,dev->iobase+Master_DMA_And_Interrupt_Control); }else if(flags & SecondaryTC){ DPRINTK("SecondaryTC\n"); writeb(ClearSecondaryTC,dev->iobase+Group_1_First_Clear); async->events |= COMEDI_CB_EOA; writeb(0x00,dev->iobase+Master_DMA_And_Interrupt_Control); } #if 0 else{ printk("ni_pcidio: unknown interrupt\n"); async->events |= COMEDI_CB_ERROR|COMEDI_CB_EOA; writeb(0x00,dev->iobase+Master_DMA_And_Interrupt_Control); } #endif flags = readb(dev->iobase+Group_1_Flags); status = readb(dev->iobase+Interrupt_And_Window_Status); //DPRINTK("loop end: IntEn=0x%02x,flags=0x%02x,status=0x%02x\n", // IntEn,flags,status); //ni_pcidio_print_flags(flags); //ni_pcidio_print_status(status); } out: comedi_event(dev,s,async->events); #if unused if(!tag){ writeb(0x03,dev->iobase+Master_DMA_And_Interrupt_Control); } #endif } #ifdef DEBUG_FLAGS static char *flags_strings[] = { "TransferReady", "CountExpired", "2", "3", "4", "Waited", "PrimaryTC", "SecondaryTC", }; static void ni_pcidio_print_flags(unsigned int flags) { int i; printk("group_1_flags:"); for(i=7;i>=0;i--){ if(flags&(1<=0;i--){ if(flags&(1<iobase+Group_Status); b=readb(dev->iobase+Group_1_Flags); if(n_int < 10){ DPRINTK("status 0x%02x flags 0x%02x time %06d\n",a,b,(int)tv.tv_usec); } while(b&1){ writew(0xff,dev->iobase+Group_1_FIFO); b=readb(dev->iobase+Group_1_Flags); } b=readb(dev->iobase+Group_1_Flags); if(n_int < 10){ DPRINTK("new status 0x%02x\n",b); n_int++; } } #endif static int ni_pcidio_insn_config(comedi_device *dev,comedi_subdevice *s, comedi_insn *insn,lsampl_t *data) { if(insn->n!=1)return -EINVAL; switch(data[0]){ case COMEDI_OUTPUT: s->io_bits |= 1<chanspec); break; case COMEDI_INPUT: s->io_bits &= ~(1<chanspec)); break; default: return -EINVAL; } writel(s->io_bits,dev->iobase+Port_Pin_Directions(0)); return 1; } static int ni_pcidio_insn_bits(comedi_device *dev,comedi_subdevice *s, comedi_insn *insn,lsampl_t *data) { if(insn->n!=2)return -EINVAL; if(data[0]){ s->state &= ~data[0]; s->state |= (data[0]&data[1]); writel(s->state,dev->iobase+Port_IO(0)); } data[1] = readl(dev->iobase+Port_IO(0)); return 2; } static int ni_pcidio_cmdtest(comedi_device *dev,comedi_subdevice *s, comedi_cmd *cmd) { int err=0; int tmp; /* step 1: make sure trigger sources are trivially valid */ tmp=cmd->start_src; cmd->start_src &= TRIG_NOW|TRIG_INT; if(!cmd->start_src || tmp!=cmd->start_src)err++; tmp=cmd->scan_begin_src; cmd->scan_begin_src &= TRIG_TIMER|TRIG_EXT; if(!cmd->scan_begin_src || tmp!=cmd->scan_begin_src)err++; tmp=cmd->convert_src; cmd->convert_src &= TRIG_NOW; if(!cmd->convert_src || tmp!=cmd->convert_src)err++; tmp=cmd->scan_end_src; cmd->scan_end_src &= TRIG_COUNT; if(!cmd->scan_end_src || tmp!=cmd->scan_end_src)err++; tmp=cmd->stop_src; cmd->stop_src &= TRIG_COUNT; if(!cmd->stop_src || tmp!=cmd->stop_src)err++; if(err)return 1; /* step 2: make sure trigger sources are unique and mutually compatible */ /* note that mutual compatiblity is not an issue here */ if(cmd->start_src!=TRIG_NOW && cmd->start_src!=TRIG_INT)err++; if(cmd->scan_begin_src!=TRIG_TIMER && cmd->scan_begin_src!=TRIG_EXT)err++; if(err)return 2; /* step 3: make sure arguments are trivially compatible */ if(cmd->start_arg!=0){ /* same for both TRIG_INT and TRIG_NOW */ cmd->start_arg=0; err++; } #define MAX_SPEED (TIMER_BASE) /* in nanoseconds */ if(cmd->scan_begin_src==TRIG_TIMER){ if(cmd->scan_begin_argscan_begin_arg=MAX_SPEED; err++; } /* no minumum speed */ }else{ /* TRIG_EXT */ /* should be level/edge, hi/lo specification here */ if(cmd->scan_begin_arg!=0){ cmd->scan_begin_arg=0; err++; } } if(cmd->convert_arg!=0){ cmd->convert_arg = 0; err++; } if(cmd->scan_end_arg!=cmd->chanlist_len){ cmd->scan_end_arg=cmd->chanlist_len; err++; } if(cmd->stop_src==TRIG_COUNT){ /* no limit */ }else{ /* TRIG_NONE */ if(cmd->stop_arg!=0){ cmd->stop_arg=0; err++; } } if(err)return 3; /* step 4: fix up any arguments */ if(cmd->scan_begin_src == TRIG_TIMER){ tmp = cmd->scan_begin_arg; ni_pcidio_ns_to_timer(&cmd->scan_begin_arg, cmd->flags&TRIG_ROUND_MASK); if(tmp!=cmd->scan_begin_arg)err++; } if(err)return 4; return 0; } static int ni_pcidio_ns_to_timer(int *nanosec, int round_mode) { int divider, base; base = TIMER_BASE; switch(round_mode){ case TRIG_ROUND_NEAREST: default: divider = (*nanosec + base/2)/base; break; case TRIG_ROUND_DOWN: divider = (*nanosec)/base; break; case TRIG_ROUND_UP: divider = (*nanosec + base - 1)/base; break; } *nanosec = base * divider; return divider; } static int ni_pcidio_cmd(comedi_device *dev,comedi_subdevice *s) { comedi_cmd *cmd = &s->async->cmd; /* XXX configure ports for input*/ writel(0x0000,dev->iobase+Port_Pin_Directions(0)); if(0){ /* enable fifos A B C D */ writeb(0x0f,dev->iobase+Data_Path); /* set transfer width a 32 bits*/ writeb(TransferWidth(0) | TransferLength(0), dev->iobase+Transfer_Size_Control); }else{ writeb(0x03,dev->iobase+Data_Path); writeb(TransferWidth(3) | TransferLength(0), dev->iobase+Transfer_Size_Control); } /* protocol configuration */ if(cmd->scan_begin_src == TRIG_TIMER){ /* page 4-5, "input with internal REQs" */ writeb( 0 ,dev->iobase+OpMode); writeb(0x00,dev->iobase+ClockReg); writeb( 1 ,dev->iobase+Sequence); writeb(0x04,dev->iobase+ReqReg); writeb( 4 ,dev->iobase+BlockMode); writeb( 3 ,dev->iobase+LinePolarities); writeb(0xc0,dev->iobase+AckSer); writel(ni_pcidio_ns_to_timer(&cmd->scan_begin_arg, TRIG_ROUND_NEAREST),dev->iobase+StartDelay); writeb( 1 ,dev->iobase+ReqDelay); writeb( 1 ,dev->iobase+ReqNotDelay); writeb( 1 ,dev->iobase+AckDelay); writeb(0x0b,dev->iobase+AckNotDelay); writeb(0x01,dev->iobase+Data1Delay); /* manual, page 4-5: ClockSpeed comment is incorrectly listed * on DAQOptions */ writew(0 ,dev->iobase+ClockSpeed); writeb(0 ,dev->iobase+DAQOptions); }else{ /* TRIG_EXT */ /* page 4-5, "input with external REQs" */ writeb( 0 ,dev->iobase+OpMode); writeb(0x00,dev->iobase+ClockReg); writeb( 0 ,dev->iobase+Sequence); writeb(0x00,dev->iobase+ReqReg); writeb( 4 ,dev->iobase+BlockMode); writeb( 0 ,dev->iobase+LinePolarities); writeb(0x00,dev->iobase+AckSer); writel( 1 ,dev->iobase+StartDelay); writeb( 1 ,dev->iobase+ReqDelay); writeb( 1 ,dev->iobase+ReqNotDelay); writeb( 1 ,dev->iobase+AckDelay); writeb(0x0C,dev->iobase+AckNotDelay); writeb(0x10,dev->iobase+Data1Delay); writew( 0 ,dev->iobase+ClockSpeed); writeb(0x60,dev->iobase+DAQOptions); } if(cmd->stop_src == TRIG_COUNT){ writel(cmd->stop_arg,dev->iobase+Transfer_Count); }else{ /* XXX */ } #ifdef USE_DMA writeb(0x05,dev->iobase+DMA_Line_Control); writeb(0x30,dev->iobase+Group_1_First_Clear); setup_mite_dma(dev,s); #else writeb(0x00,dev->iobase+DMA_Line_Control); #endif /* clear and enable interrupts */ writeb(0xff,dev->iobase+Group_1_First_Clear); //writeb(ClearExpired,dev->iobase+Group_1_Second_Clear); writeb(IntEn,dev->iobase+Interrupt_Control); writeb(0x03,dev->iobase+Master_DMA_And_Interrupt_Control); if(cmd->start_src == TRIG_NOW){ /* start */ writeb(Numbered | RunMode(7),dev->iobase+OpMode); s->async->inttrig = NULL; }else{ /* TRIG_INT */ s->async->inttrig = ni_pcidio_inttrig; } DPRINTK("ni_pcidio: command started\n"); return 0; } static void setup_mite_dma(comedi_device *dev,comedi_subdevice *s) { struct mite_struct *mite = devpriv->mite; struct mite_channel *mite_chan = &mite->channels[ DI_DMA_CHAN ]; mite_chan->current_link = 0; mite_chan->dir = COMEDI_INPUT; mite_prep_dma(mite, DI_DMA_CHAN, 16, 16); mite_dma_arm(mite, DI_DMA_CHAN); } static int ni_pcidio_inttrig(comedi_device *dev, comedi_subdevice *s, unsigned int trignum) { if(trignum!=0)return -EINVAL; writeb(Numbered | RunMode(7),dev->iobase+OpMode); s->async->inttrig = NULL; return 1; } static int ni_pcidio_cancel(comedi_device *dev, comedi_subdevice *s) { writeb(0x00,dev->iobase+Master_DMA_And_Interrupt_Control); return 0; } static int ni_pcidio_change(comedi_device *dev, comedi_subdevice *s, unsigned long new_size) { int ret; ret = mite_buf_change(devpriv->mite, DI_DMA_CHAN, s->async, new_size); if(ret<0)return ret; memset(s->async->prealloc_buf, 0xaa, s->async->prealloc_bufsz); return 0; } static int nidio_attach(comedi_device *dev,comedi_devconfig *it) { comedi_subdevice *s; int i; int ret; printk("comedi%d: nidio:",dev->minor); if((ret=alloc_private(dev,sizeof(nidio96_private)))<0) return ret; ret=nidio_find_device(dev,it->options[0],it->options[1]); if(ret<0)return ret; ret = mite_setup(devpriv->mite); if(ret < 0) { printk("error setting up mite\n"); return ret; } dev->iobase = mite_iobase(devpriv->mite); dev->board_name=this_board->name; dev->irq=mite_irq(devpriv->mite); printk(" %s",dev->board_name); if(!this_board->is_diodaq){ dev->n_subdevices=this_board->n_8255; }else{ dev->n_subdevices=1; } if((ret=alloc_subdevices(dev))<0) return ret; if(!this_board->is_diodaq){ for(i=0;in_8255;i++){ subdev_8255_init(dev,dev->subdevices+i, nidio96_8255_cb,(unsigned long)(dev->iobase+NIDIO_8255_BASE(i))); } }else{ printk(" rev=%d",readb(dev->iobase+Chip_Version)); s=dev->subdevices+0; dev->read_subdev = s; s->type=COMEDI_SUBD_DIO; s->subdev_flags=SDF_READABLE|SDF_WRITABLE; s->n_chan=32; s->range_table=&range_digital; s->maxdata=1; s->insn_config = ni_pcidio_insn_config; s->insn_bits = ni_pcidio_insn_bits; s->do_cmd = ni_pcidio_cmd; s->do_cmdtest = ni_pcidio_cmdtest; s->cancel = ni_pcidio_cancel; s->len_chanlist=32; /* XXX */ s->buf_change = ni_pcidio_change; writel(0,dev->iobase+Port_IO(0)); writel(0,dev->iobase+Port_Pin_Directions(0)); writel(0,dev->iobase+Port_Pin_Mask(0)); /* disable interrupts on board */ writeb(0x00,dev->iobase+Master_DMA_And_Interrupt_Control); ret=comedi_request_irq(dev->irq,nidio_interrupt,SA_SHIRQ,"ni_pcidio",dev); if(ret<0){ dev->irq=0; printk(" irq not available"); } } printk("\n"); return 0; } static int nidio_detach(comedi_device *dev) { int i; if(this_board && !this_board->is_diodaq){ for(i=0;in_8255;i++){ subdev_8255_cleanup(dev,dev->subdevices+i); } } if(dev->irq) comedi_free_irq(dev->irq,dev); if(devpriv && devpriv->mite) mite_unsetup(devpriv->mite); return 0; } static int nidio_find_device(comedi_device *dev,int bus,int slot) { struct mite_struct *mite; int i; for(mite=mite_devices;mite;mite=mite->next){ if(mite->used)continue; if(bus || slot){ if(bus!=mite->pcidev->bus->number || slot!=PCI_SLOT(mite->pcidev->devfn)) continue; } for(i=0;iboard_ptr=nidio_boards+i; devpriv->mite=mite; return 0; } } } printk("no device found\n"); mite_list_devices(); return -EIO; }