From bb4d9010df5a10459fec27f667b649c7cffb6533 Mon Sep 17 00:00:00 2001 From: David Schleef Date: Thu, 12 Oct 2000 20:00:46 +0000 Subject: [PATCH] new driver from Michal Dobes, plus fixes to docs --- Documentation/comedi/drivers.txt | 21 +- Documentation/comedi/hardware | 14 +- comedi/Config.in | 1 + comedi/drivers/adl_pci9118.c | 1508 ++++++++++++++++++++++++++++++ comedi/drivers/amcc_s5933.h | 348 +++++++ scripts/Configure.help | 12 +- 6 files changed, 1897 insertions(+), 7 deletions(-) create mode 100644 comedi/drivers/adl_pci9118.c create mode 100644 comedi/drivers/amcc_s5933.h diff --git a/Documentation/comedi/drivers.txt b/Documentation/comedi/drivers.txt index b4da8f09..695d71de 100644 --- a/Documentation/comedi/drivers.txt +++ b/Documentation/comedi/drivers.txt @@ -435,7 +435,7 @@ Interrupts are not supported. -pcl812.o: Advantech PCL-812PG and Advantech PCL-813B +pcl812.o: Advantech PCL-812PG, Advantech PCL-813B and ADLink ACL-8113 Author: Michal Dobes Status: works (I hope. My board fire up under my hands @@ -458,8 +458,8 @@ Options: 1=D/A outputs 0-10V (internal reference -10V) 2=D/A outputs unknow (external reference) -Card: Advantech PCL-813B - This card have integrated 32SE A/D 25kHz. +Card: Advantech PCL-813B, ADLink ACL-8113 + These cards have integrated 32SE A/D 25kHz. Driver support only mode0 A/D becouse construction of this card don't allow anything else. @@ -518,4 +518,19 @@ Author: Anders Blomdell Status: works +adl_pci9118.o: Adlink PCI-9118DG, PCI-9118HG, PCI-9118HR + +Author: Michal Dobes +Status works + +This driver supports AI, AO, DI and DO subdevices. +AI subdevice support cmd, insn and mode0-4 interface, +other subdevices support insn and mode0 interface. +For AI: +- If cmd->scan_begin_src=TRIG_EXT (or trigger mode4) then trigger input + is TGIN (pin 46). +- If cmd->convert_src=TRIG_EXT (or trigger mode3) then trigger input + is EXTTRG (pin 44). + + diff --git a/Documentation/comedi/hardware b/Documentation/comedi/hardware index 8e2f0832..b66195ae 100644 --- a/Documentation/comedi/hardware +++ b/Documentation/comedi/hardware @@ -1,14 +1,24 @@ -ADLink ACL-8112-DG pcl711 -ADLink ACL-8112-HG pcl711 ADLink ACL-6126 pcl726 ADLink ACL-6128 pcl726 +ADLink ACL-7122 pcl724 +ADLink ACL-7124 pcl724 +ADLink ACL-8112-DG pcl711 +ADLink ACL-8112-HG pcl711 +ADLink ACL-8113 pcl812 +ADLink PCI-9118DG adl_pci9118 +ADLink PCI-9118HG adl_pci9118 +ADLink PCI-9118HR adl_pci9118 +ADLink PET-48DIO pcl724 Advantech PCL-711 pcl711 Advantech PCL-711B pcl711 Advantech PCL-718 pcl818 +Advantech PCL-722 pcl724 +Advantech PCL-724 pcl724 Advantech PCL-725 pcl725 Advantech PCL-726 pcl726 Advantech PCL-727 pcl726 Advantech PCL-728 pcl726 +Advantech PCL-731 pcl724 Advantech PCL-812PG pcl812 Advantech PCL-813B pcl812 Advantech PCL-818 pcl818 diff --git a/comedi/Config.in b/comedi/Config.in index 493add4d..45818de5 100644 --- a/comedi/Config.in +++ b/comedi/Config.in @@ -72,6 +72,7 @@ dep_tristate 'DAS-6402 and compatibles' CONFIG_COMEDI_DAS6402 $CONFIG_COMEDI dep_tristate 'Generic 8255 support' CONFIG_COMEDI_8255 $CONFIG_COMEDI dep_tristate 'Quanser Consulting MultiQ-3' CONFIG_COMEDI_MULTIQ3 $CONFIG_COMEDI dep_tristate 'Generic parallel port support' CONFIG_COMEDI_PARPORT $CONFIG_COMEDI +dep_tristate 'ADLink PCI-9118DG/HR/HG' CONFIG_COMEDI_ADLPCI9118 $CONFIG_COMEDI dep_tristate 'PCL-711, PCL-711b, ACL-8112, and compatibles' CONFIG_COMEDI_PCL711 $CONFIG_COMEDI dep_tristate 'PCL-722/724/731, ACL-7122/7124, PET-48DIO' CONFIG_COMEDI_PCL724 $CONFIG_COMEDI if [ "$CONFIG_COMEDI_PCL724" = "m" ];then diff --git a/comedi/drivers/adl_pci9118.c b/comedi/drivers/adl_pci9118.c new file mode 100644 index 00000000..b73cdd9a --- /dev/null +++ b/comedi/drivers/adl_pci9118.c @@ -0,0 +1,1508 @@ +/* + * module/adl_pci9118.c + * + * hardware driver for ADLink cards: + * card: PCI-9118DG, PCI-9118HG, PCI-9118HR + * driver: pci9118dg, pci9118hg, pci9118hr + * + * Author: Michal Dobes + * + * Options: + * [0] - PCI bus number - if bus number and slot number are 0, then driver search for first unused card + * [1] - PCI slot number + * [2] - A/D channels - card have 16 channels, but supports external multiplexor, + * you can select from 1 to 256 channels (0=16 SE/8 DIFF channels) + * +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include <8253.h> + +#define PCL9118_PARANOIDCHECK /* if defined, then is used code which control correct channel number on every 12 bit sample */ + +#define IORANGE_9118 64 /* XXX: I hope */ +#define PCI9118_CHANLEN 255 /* len of chanlist, some source say 256, but reality is 255 :-( */ + +#define PCI9118_CNT0 0x00 /* R/W: 8254 couter 0 */ +#define PCI9118_CNT1 0x04 /* R/W: 8254 couter 0 */ +#define PCI9118_CNT2 0x08 /* R/W: 8254 couter 0 */ +#define PCI9118_CNTCTRL 0x0c /* W: 8254 counter control */ +#define PCI9118_AD_DATA 0x10 /* R: A/D data */ +#define PCI9118_DA1 0x10 /* W: D/A registers */ +#define PCI9118_DA2 0x14 +#define PCI9118_ADSTAT 0x18 /* R: A/D status register */ +#define PCI9118_ADCNTRL 0x18 /* W: A/D control register */ +#define PCI9118_DI 0x1c /* R: digi input register */ +#define PCI9118_DO 0x1c /* W: digi output register */ +#define PCI9118_SOFTTRG 0x20 /* W: soft trigger for A/D */ +#define PCI9118_GAIN 0x24 /* W: A/D gain/channel register */ +#define PCI9118_BURST 0x28 /* W: A/D burst number register */ +#define PCI9118_SCANMOD 0x2c /* W: A/D auto scan mode */ +#define PCI9118_ADFUNC 0x30 /* W: A/D function register */ +#define PCI9118_DELFIFO 0x34 /* W: A/D data FIFO reset */ +#define PCI9118_INTSRC 0x38 /* R: interrupt reason register */ +#define PCI9118_INTCTRL 0x38 /* W: interrupt control register */ + +// bits from A/D control register (PCI9118_ADCNTRL) +#define AdControl_UniP 0x80 /* 1=bipolar, 0=unipolar */ +#define AdControl_Diff 0x40 /* 1=differential, 0= single end inputs */ +#define AdControl_SoftG 0x20 /* 1=8254 counter works, 0=counter stops */ +#define AdControl_ExtG 0x10 /* 1=8254 countrol controlled by TGIN(pin 46), 0=controled by SoftG */ +#define AdControl_ExtM 0x08 /* 1=external hardware trigger (pin 44), 0=internal trigger */ +#define AdControl_TmrTr 0x04 /* 1=8254 is iternal trigger source, 0=software trigger is source (register PCI9118_SOFTTRG) */ +#define AdControl_Int 0x02 /* 1=enable INT, 0=disable */ +#define AdControl_Dma 0x01 /* 1=enable DMA, 0=disable */ + +// bits from A/D function register (PCI9118_ADFUNC) +#define AdFunction_PDTrg 0x80 /* 1=positive, 0=negative digital trigger (only positive is correct) */ +#define AdFunction_PETrg 0x40 /* 1=positive, 0=negative external trigger (only positive is correct) */ +#define AdFunction_BSSH 0x20 /* 1=with sample&hold, 0=without */ +#define AdFunction_BM 0x10 /* 1=burst mode, 0=normal mode */ +#define AdFunction_BS 0x08 /* 1=burst mode start, 0=burst mode stop */ +#define AdFunction_PM 0x04 /* 1=post trigger mode, 0=not post trigger */ +#define AdFunction_AM 0x02 /* 1=about trigger mode, 0=not about trigger */ +#define AdFunction_Start 0x01 /* 1=trigger start, 0=trigger stop */ + +// bits from A/D status register (PCI9118_ADSTAT) +#define AdStatus_nFull 0x100 /* 0=FIFO full (fatal), 1=not full */ +#define AdStatus_nHfull 0x080 /* 0=FIFO half full, 1=FIFO not half full */ +#define AdStatus_nEpty 0x040 /* 0=FIFO empty, 1=FIFO not empty */ +#define AdStatus_Acmp 0x020 /* */ +#define AdStatus_DTH 0x010 /* 1=external digital trigger */ +#define AdStatus_Bover 0x008 /* 1=burst mode overrun (fatal) */ +#define AdStatus_ADOS 0x004 /* 1=A/D over speed (warning) */ +#define AdStatus_ADOR 0x002 /* 1=A/D overrun (fatal) */ +#define AdStatus_ADrdy 0x001 /* 1=A/D already ready, 0=not ready */ + +// bits for interrupt reason and control (PCI9118_INTSRC, PCI9118_INTCTRL) +// 1=interrupt occur, enable source, 0=interrupt not occur, disable source +#define Int_Timer 0x08 /* timer interrupt */ +#define Int_About 0x04 /* about trigger complete */ +#define Int_Hfull 0x02 /* A/D FIFO hlaf full */ +#define Int_DTrg 0x01 /* external digital trigger */ + +comedi_lrange range_pci9118dg_hr={ 8, { + BIP_RANGE(5), + BIP_RANGE(2.5), + BIP_RANGE(1.25), + BIP_RANGE(0.625), + UNI_RANGE(10), + UNI_RANGE(5), + UNI_RANGE(2.5), + UNI_RANGE(1.25) + } +}; + +comedi_lrange range_pci9118hg={ 8, { + BIP_RANGE(5), + BIP_RANGE(0.5), + BIP_RANGE(0.05), + BIP_RANGE(0.005), + UNI_RANGE(10), + UNI_RANGE(1), + UNI_RANGE(0.1), + UNI_RANGE(0.01) + } +}; + +#define PCI9118_BIPOLAR_RANGES 4 /* used for test on mixture of BIP/UNI ranges */ + +static int pci9118_attach(comedi_device *dev,comedi_devconfig *it); +static int pci9118_detach(comedi_device *dev); +static int pci9118_recognize(char *name); + +comedi_driver driver_pci9118={ + driver_name: "adl_pci9118", + module: THIS_MODULE, + attach: pci9118_attach, + detach: pci9118_detach, + recognize: pci9118_recognize, +}; + +typedef struct { + char *name; // driver name + int vendor_id; // PCI vendor a device ID of card + int device_id; + int iorange_amcc; // iorange for own S5933 region + int iorange_9118; // pass thru card region size + int n_aichan; // num of A/D chans + int n_aichand; // num of A/D chans in diff mode + int mux_aichan; // num of A/D chans with external multiplexor + int n_aichanlist; // len of chanlist + int n_aochan; // num of D/A chans + int ai_maxdata; // resolution of A/D + int ao_maxdata; // resolution of D/A + comedi_lrange *rangelist_ai; // rangelist for A/D + comedi_lrange *rangelist_ao; // rangelist for D/A + unsigned int ai_ns_min; // max sample speed of card v ns + unsigned int ai_pacer_min; // minimal pacer value (c1*c2 or c1 in burst) +} boardtype; + +static boardtype boardtypes[] = +{ + {"pci9118dg", PCI_VENDOR_ID_AMCC, 0x80d9, + AMCC_OP_REG_SIZE, IORANGE_9118, + 16, 8, 256, PCI9118_CHANLEN, 2, 0x0fff, 0x0fff, + &range_pci9118dg_hr, &range_bipolar10, + 3030, 12 }, + {"pci9118hg", PCI_VENDOR_ID_AMCC, 0x80d9, + AMCC_OP_REG_SIZE, IORANGE_9118, + 16, 8, 256, PCI9118_CHANLEN, 2, 0x0fff, 0x0fff, + &range_pci9118hg, &range_bipolar10, + 3030, 12 }, + {"pci9118hr", PCI_VENDOR_ID_AMCC, 0x80d9, + AMCC_OP_REG_SIZE, IORANGE_9118, + 16, 8, 256, PCI9118_CHANLEN, 2, 0xffff, 0x0fff, + &range_pci9118dg_hr, &range_bipolar10, + 10000, 40 }, +}; + +#define n_boardtypes (sizeof(boardtypes)/sizeof(boardtype)) + +typedef struct{ + int iobase_a; // base+size for AMCC chip + int iosize_a; + struct amcc_struct *amcc; // ptr too AMCC data + unsigned int master; // master capable + unsigned char allocated; // we have blocked card + unsigned int usemux; // we want to use external multiplexor! +#ifdef PCL9118_PARANOIDCHECK + unsigned short chanlist[PCI9118_CHANLEN]; // list of scaned channel + unsigned char chanlistlen; // number of scanlist +#endif + unsigned char AdControlReg; // A/D control register + unsigned char IntControlReg; // Interrupt control register + unsigned char AdFunctionReg; // A/D function register + char valid; // driver is ok + char neverending_ai; // we do unlimited AI + unsigned int i8254_osc_base; // frequence of onboard oscilator + unsigned int ai_do; // what do AI? 0=nothing, 1 to 4 mode + unsigned int ai1234_act_scan;// how many scans we finished + unsigned int ai1234_buf_ptr; // data buffer ptr in samples + unsigned int ai1234_n_chan;// how many channels is measured + unsigned int *ai1234_chanlist;// actaul chanlist + unsigned int ai1234_timer1; + unsigned int ai1234_timer2; + unsigned int ai1234_flags; + unsigned int ai1234_data_len; + sampl_t *ai1234_data; + unsigned int ai1234_scans; // number of scans to do + unsigned int ai4_divisor1; // AI mode4 divisors + unsigned int ai4_divisor2; + unsigned char ai4_status; // 0=wait for ext trg 1=sampling + unsigned char ai4_cntrl0; // control register for status 0 and 1 + unsigned char ai4_cntrl1; + char dma_doublebuf; // we can use double buffring + unsigned int dma_actbuf; // which buffer is used now + unsigned long dmabuf_virt[2]; // pointers to begin of DMA buffer + unsigned long dmabuf_hw[2]; // hw address of DMA buff + unsigned int dmabuf_size[2]; // size of dma buffer in bytes + unsigned int dmabuf_use_size[2]; // which size e may now used for transfer + unsigned int dmabuf_samples[2]; // size in samples + int dmabuf_pages[2]; // number of pages in buffer + unsigned char cnt0_users; // bit field of 8254 CNT0 users (0-unused, 1-AO, 2-DI, 3-DO) + unsigned char exttrg_users; // bit field of external trigger users (0-AI, 1-AO, 2-DI, 3-DO) + unsigned int cnt0_divisor; // actual CNT0 divisor +}pci9118_private; + +#define devpriv ((pci9118_private *)dev->private) +#define this_board (boardtypes+dev->board) + +/* +============================================================================== +*/ + +int check_and_setup_channel_list(comedi_device * dev, comedi_subdevice * s, int n_chan, unsigned int *chanlist,char rot); +void start_pacer(comedi_device * dev, int mode, unsigned int divisor1, unsigned int divisor2); +static int pci9118_reset(comedi_device *dev); +int pci9118_exttrg_add(comedi_device * dev, unsigned char source); +int pci9118_exttrg_del(comedi_device * dev, unsigned char source); +int pci9118_ai_cancel(comedi_device * dev, comedi_subdevice * s); + +/* +============================================================================== +*/ +static void interrupt_pci9118_ai_mode4_switch(void *d) +{ + comedi_device *dev = d; + + if (devpriv->ai4_status) { + pci9118_exttrg_add(dev,0); // activate EXT trigger + devpriv->ai4_status=0; // next int start pacer! + outl(inl(devpriv->iobase_a+AMCC_OP_REG_INTCSR)&(~AINT_WRITE_COMPL), devpriv->iobase_a+AMCC_OP_REG_INTCSR); // stop dma irq + outl(devpriv->ai4_cntrl0, dev->iobase+PCI9118_ADCNTRL); + start_pacer(dev, 0, 0, 0); // stop pacer + } else { + pci9118_exttrg_del(dev,0); // deactivate EXT trigger + outl(inl(devpriv->iobase_a+AMCC_OP_REG_INTCSR)|(AINT_WRITE_COMPL), devpriv->iobase_a+AMCC_OP_REG_INTCSR); // enable dma irq + //outw(0, dev->iobase+PCI9118_SOFTTRG); /* start 1. conversion */ + devpriv->ai4_status++; // next int wi will have data! + outl(devpriv->ai4_cntrl1, dev->iobase+PCI9118_ADCNTRL); + start_pacer(dev, 4, devpriv->ai4_divisor1, devpriv->ai4_divisor2); // start pacer + } +} + +/* +============================================================================== +*/ +static void move_block_from_dma_12bit(comedi_device *dev,comedi_subdevice *s,sampl_t *dma,sampl_t *data,int n) +{ + int i,j; + + j=s->cur_chan; + for(i=0;ichanlist[j]) { // data dropout! + rt_printk("comedi: A/D DMA - data dropout: received channel %d, expected %d!\n",(*dma & 0x0f00)>>8,devpriv->chanlist[j]); + pci9118_ai_cancel(dev,s); + comedi_error_done(dev,s); + return; + } +#endif + *data=((*dma & 0xff)<<4)|((*dma & 0xf000)>>12); // get one sample + data++; dma++; + j++; + if(j>=devpriv->ai1234_n_chan){ + j=0; + devpriv->ai1234_act_scan++; + if (devpriv->ai1234_flags & TRIG_WAKE_EOS) + comedi_eos(dev,s); + } + } + s->cur_chan=j; +} + +/* +============================================================================== +*/ +static void move_block_from_dma_16bit(comedi_device *dev,comedi_subdevice *s,sampl_t *dma,sampl_t *data,int n) +{ + int i,j; + + j=s->cur_chan; + for(i=0;i>8); // get one sample + data++; dma++; + j++; + if(j>=devpriv->ai1234_n_chan){ + j=0; + devpriv->ai1234_act_scan++; + if (devpriv->ai1234_flags & TRIG_WAKE_EOS) + comedi_eos(dev,s); + } + } + s->cur_chan=j; +} + +/* +============================================================================== +*/ +static void interrupt_pci9118_ai_dma(int irq, void *d, struct pt_regs *regs) +{ + comedi_device *dev = d; + comedi_subdevice *s = dev->subdevices + 0; + sampl_t *ptr; + unsigned int next_dma_buf, samplesinbuf, m; + + if (devpriv->ai_do==4) + interrupt_pci9118_ai_mode4_switch(d); + + samplesinbuf=devpriv->dmabuf_use_size[devpriv->dma_actbuf]-inl(devpriv->iobase_a+AMCC_OP_REG_MWTC); // how many bytes is in DMA buffer? + + if (samplesinbufdmabuf_use_size[devpriv->dma_actbuf]) { + comedi_error(dev,"Interrupted DMA transfer!"); + } + + if (samplesinbuf & 1) { + comedi_error(dev,"Odd count of bytes in DMA ring!"); + pci9118_ai_cancel(dev,s); + comedi_error_done(dev,s); + return; + } + + m=inw(dev->iobase+PCI9118_ADSTAT); + if (m & 0x10e) { + if (m & 0x100) + comedi_error(dev,"A/D FIFO Full status (Fatal Error!)"); + if (m & 0x008) + comedi_error(dev,"A/D Burst Mode Overrun Status (Fatal Error!)"); + if (m & 0x004) + comedi_error(dev,"A/D Over Speed Status (Warning!)"); + if (m & 0x002) + comedi_error(dev,"A/D Overrun Status (Fatal Error!)"); + if (m & 0x10a) { + pci9118_ai_cancel(dev,s); + comedi_error_done(dev,s); + return; + } + } + + samplesinbuf=samplesinbuf>>1; // number of received samples + + if (devpriv->dma_doublebuf) { // switch DMA buffers if is used double buffering + next_dma_buf=1-devpriv->dma_actbuf; + outl(devpriv->dmabuf_hw[next_dma_buf], devpriv->iobase_a+AMCC_OP_REG_MWAR); + outl(devpriv->dmabuf_use_size[next_dma_buf], devpriv->iobase_a+AMCC_OP_REG_MWTC); + } + + ptr=(sampl_t *)devpriv->dmabuf_virt[devpriv->dma_actbuf]; + + if(s->buf_int_ptr+samplesinbuf*sizeof(sampl_t)>=devpriv->ai1234_data_len){ + m=(devpriv->ai1234_data_len-s->buf_int_ptr)/sizeof(sampl_t); + if (this_board->ai_maxdata==0xfff) { move_block_from_dma_12bit(dev,s,(void *)ptr,((void *)(s->cur_trig.data))+s->buf_int_ptr,m); } + else { move_block_from_dma_16bit(dev,s,(void *)ptr,((void *)(s->cur_trig.data))+s->buf_int_ptr,m); } + s->buf_int_count+=m*sizeof(sampl_t); + ptr+=m*sizeof(sampl_t); + samplesinbuf-=m; + s->buf_int_ptr=0; + + comedi_eobuf(dev,s); + } + if (this_board->ai_maxdata==0xfff) { move_block_from_dma_12bit(dev,s,(void *)ptr,((void *)(s->cur_trig.data))+s->buf_int_ptr,samplesinbuf); } + else { move_block_from_dma_16bit(dev,s,(void *)ptr,((void *)(s->cur_trig.data))+s->buf_int_ptr,samplesinbuf); } + s->buf_int_count+=samplesinbuf*sizeof(sampl_t); + s->buf_int_ptr+=samplesinbuf*sizeof(sampl_t); + + + if (!devpriv->neverending_ai) + if ( devpriv->ai1234_act_scan>=devpriv->ai1234_scans ) { /* all data sampled */ + pci9118_ai_cancel(dev,s); + comedi_done(dev,s); + return; + } + + if (!(devpriv->ai1234_flags & TRIG_WAKE_EOS)) + comedi_bufcheck(dev,s); + + if (devpriv->dma_doublebuf) { // switch dma buffers + devpriv->dma_actbuf=1-devpriv->dma_actbuf; + } else { // restart DMA if is not used double buffering + outl(devpriv->dmabuf_hw[0], devpriv->iobase_a+AMCC_OP_REG_MWAR); + outl(devpriv->dmabuf_use_size[0], devpriv->iobase_a+AMCC_OP_REG_MWTC); + } +} + +/* +============================================================================== +*/ +static void interrupt_pci9118(int irq, void *d, struct pt_regs *regs) +{ + comedi_device *dev = d; + unsigned int int_daq,int_amcc,int_adstat; + + int_daq=inl(dev->iobase+PCI9118_INTSRC) & 0xf; // get IRQ reasons + int_adstat=inw(dev->iobase+PCI9118_ADSTAT)&0x1ff; // get STATUS register + int_amcc=inl(devpriv->iobase_a+AMCC_OP_REG_INTCSR); // get AMCC INT register + + outl(int_amcc|0x00ff0000, devpriv->iobase_a+AMCC_OP_REG_INTCSR); // shutdown IRQ reasons in AMCC + +#if 0 + rt_printk("INT daq=0x%01x amcc=0x%08x MWAR=0x%08x MWTC=0x%08x ADSTAT=0x%02x\n", int_daq, int_amcc, + inl(devpriv->iobase_a+AMCC_OP_REG_MWAR), inl(devpriv->iobase_a+AMCC_OP_REG_MWTC), + int_adstat); +#endif + + if ((!int_daq)&&(!(int_amcc&ANY_S593X_INT))) { + comedi_error(dev,"IRQ from unknow source"); + return; + } + if (int_amcc&MASTER_ABORT_INT) + comedi_error(dev,"AMCC IRQ - MASTER DMA ABORT!"); + if (int_amcc&TARGET_ABORT_INT) + comedi_error(dev,"AMCC IRQ - TARGET DMA ABORT!"); + + + if (devpriv->ai_do) { + if (int_amcc&WRITE_TC_INT) + if (devpriv->master) { + interrupt_pci9118_ai_dma(irq,d,regs); // do some data transfer + } + if ((int_adstat&AdStatus_DTH)&&(int_daq&Int_DTrg)&&(int_amcc&IN_MB_INT)&&(devpriv->ai_do==4)) { + interrupt_pci9118_ai_mode4_switch(d); // AI mode 4, scan begin EXT interrupt + } + } + +} + +/* +============================================================================== +*/ +static int pci9118_ai_docmd_and_mode(int mode, comedi_device * dev, comedi_subdevice * s) +{ + unsigned int divisor1, divisor2; + unsigned int dmalen0,dmalen1; + char use_bssh=0; + + start_pacer(dev, -1, 0, 0); // stop pacer + + devpriv->AdControlReg=AdControl_Int; // bipolar, S.E., use 8254, stop 8354, internal trigger, soft trigger, disable DMA + + if (!check_and_setup_channel_list(dev, s, devpriv->ai1234_n_chan, devpriv->ai1234_chanlist, 8)) return -EINVAL; + + outl(devpriv->AdControlReg,dev->iobase+PCI9118_ADCNTRL); + devpriv->AdFunctionReg=AdFunction_PDTrg|AdFunction_PETrg; // positive triggers, no S&H, no burst, burst stop, no post trigger, no about trigger, trigger stop + outl(devpriv->AdFunctionReg,dev->iobase+PCI9118_ADFUNC); + udelay(10); + outl(0,dev->iobase+PCI9118_DELFIFO); // flush FIFO + inl(dev->iobase+PCI9118_ADSTAT); // flush A/D status register + + devpriv->ai1234_act_scan=0; + s->cur_chan=0; + devpriv->ai1234_buf_ptr=0; + devpriv->neverending_ai=0; + + devpriv->dma_actbuf=0; + + if ((devpriv->ai1234_scans==0)||(devpriv->ai1234_scans==-1)) devpriv->neverending_ai=1; //well, user want neverending + + switch (mode) { + case 1: + if (devpriv->ai1234_timer1ai_ns_min) devpriv->ai1234_timer1=this_board->ai_ns_min; + i8253_cascade_ns_to_timer(devpriv->i8254_osc_base,&divisor1,&divisor2,&devpriv->ai1234_timer1,TRIG_ROUND_NEAREST); + break; + case 2: + if (devpriv->ai1234_timer2==0) use_bssh=1; // use S&H + if (devpriv->ai1234_timer2ai_ns_min) devpriv->ai1234_timer2=this_board->ai_ns_min; + divisor1=(devpriv->ai1234_timer2+devpriv->i8254_osc_base/2)/devpriv->i8254_osc_base; // minor timer + if (divisor1ai_pacer_min) divisor1=this_board->ai_pacer_min; + divisor2=(devpriv->ai1234_timer1+devpriv->i8254_osc_base/2)/devpriv->i8254_osc_base; + divisor2=divisor2/divisor1; // major timer is c1*c2 + if (divisor2ai1234_n_chan) divisor2=devpriv->ai1234_n_chan; + if (use_bssh) + if (divisor2<(devpriv->ai1234_n_chan+2)) + divisor2=devpriv->ai1234_n_chan+2; + devpriv->ai1234_timer2=divisor1*devpriv->i8254_osc_base; + devpriv->ai1234_timer1=divisor1*divisor2*devpriv->i8254_osc_base; + break; + case 4: + if (devpriv->ai1234_timer2ai_ns_min) devpriv->ai1234_timer2=this_board->ai_ns_min; + i8253_cascade_ns_to_timer(devpriv->i8254_osc_base,&divisor1,&divisor2,&devpriv->ai1234_timer2,TRIG_ROUND_NEAREST); + devpriv->ai4_divisor1=divisor1; + devpriv->ai4_divisor2=divisor2; + devpriv->ai4_status=0; + break; + } + + if (devpriv->master) { // bus master DMA + + dmalen0=devpriv->dmabuf_size[0]; + dmalen1=devpriv->dmabuf_size[1]; + + if (!devpriv->neverending_ai) { + if (dmalen0>(devpriv->ai1234_scans*devpriv->ai1234_n_chan*2)) { // must we fill full first buffer? + dmalen0=devpriv->ai1234_scans*devpriv->ai1234_n_chan*2; + } else + if (dmalen1>(devpriv->ai1234_scans*devpriv->ai1234_n_chan*2-dmalen0)) // and must we fill full second buffer when first is once filled? + dmalen1=devpriv->ai1234_scans*devpriv->ai1234_n_chan*2-dmalen0; + } + + if ((devpriv->ai1234_flags & TRIG_WAKE_EOS)||(mode==4)) { // don't we want wake up every scan? + if (dmalen0>(devpriv->ai1234_n_chan*2)) + dmalen0=devpriv->ai1234_n_chan*2; + if (dmalen1>(devpriv->ai1234_n_chan*2)) + dmalen1=devpriv->ai1234_n_chan*2; + } else { // isn't output buff smaller that our DMA buff? + if (dmalen0>(devpriv->ai1234_data_len)) + dmalen0=devpriv->ai1234_data_len; + if (dmalen1>(devpriv->ai1234_data_len)) + dmalen1=devpriv->ai1234_data_len; + } + + devpriv->dmabuf_use_size[0]=dmalen0; + devpriv->dmabuf_use_size[1]=dmalen1; + outl(devpriv->dmabuf_hw[0], devpriv->iobase_a+AMCC_OP_REG_MWAR); + outl(devpriv->dmabuf_use_size[0], devpriv->iobase_a+AMCC_OP_REG_MWTC); + + outl(0x02000000|AINT_WRITE_COMPL, devpriv->iobase_a+AMCC_OP_REG_INTCSR); + outl(inl(devpriv->iobase_a+AMCC_OP_REG_MCSR)|RESET_A2P_FLAGS|A2P_HI_PRIORITY|EN_A2P_TRANSFERS, devpriv->iobase_a+AMCC_OP_REG_MCSR); + + switch (mode) { + case 1: + devpriv->AdControlReg|=((AdControl_SoftG|AdControl_TmrTr|AdControl_Dma) & 0xff); + break; + case 2: + devpriv->AdControlReg|=((AdControl_SoftG|AdControl_TmrTr|AdControl_Dma) & 0xff); + devpriv->AdFunctionReg|=AdFunction_BM; + if (use_bssh) { outl(devpriv->ai1234_n_chan+1, dev->iobase+PCI9118_BURST); devpriv->AdFunctionReg|=AdFunction_BSSH; } + else { outl(devpriv->ai1234_n_chan, dev->iobase+PCI9118_BURST); } + break; + case 3: + devpriv->AdControlReg|=((AdControl_ExtM|AdControl_Dma) & 0xff); + break; + case 4: + devpriv->ai4_cntrl1=devpriv->AdControlReg|((AdControl_SoftG|AdControl_TmrTr|AdControl_Dma) & 0xff); + devpriv->AdControlReg|=((AdControl_Int) & 0xff); + devpriv->ai4_cntrl0=devpriv->AdControlReg; + pci9118_exttrg_add(dev,0); // activate EXT trigger + break; + }; + + devpriv->ai_do=mode; + + outl(devpriv->AdFunctionReg,dev->iobase+PCI9118_ADFUNC); + outl(devpriv->AdControlReg, dev->iobase+PCI9118_ADCNTRL); + + if ((mode==1)||(mode==2)) + start_pacer(dev, mode, divisor1, divisor2); + + if (mode==2) { + devpriv->AdFunctionReg=AdFunction_PDTrg|AdFunction_PETrg|AdFunction_BM|AdFunction_BS; + if (use_bssh) devpriv->AdFunctionReg|=AdFunction_BSSH; + outl(devpriv->AdFunctionReg,dev->iobase+PCI9118_ADFUNC); + } + + } else { + return -EINVAL; + } + + return 0; +} + + +#ifdef CONFIG_COMEDI_MODES + +/* +============================================================================== +*/ +static int pci9118_ai_mode1234(int mode, comedi_device * dev, comedi_subdevice * s, comedi_trig * it) +{ + int ret; + + devpriv->ai1234_n_chan=it->n_chan; + devpriv->ai1234_chanlist=it->chanlist; + devpriv->ai1234_scans=it->n; + devpriv->ai1234_flags=it->flags; + devpriv->ai1234_data_len=it->data_len; + devpriv->ai1234_data=it->data; + devpriv->ai1234_timer1=it->trigvar; + devpriv->ai1234_timer2=it->trigvar1; + + ret=pci9118_ai_docmd_and_mode(mode, dev, s); + + it->trigvar=devpriv->ai1234_timer1; + it->trigvar1=devpriv->ai1234_timer2; + + return ret; +} + +/* +============================================================================== +*/ +static int pci9118_ai_mode1(comedi_device * dev, comedi_subdevice * s, comedi_trig * it) +{ + return pci9118_ai_mode1234(1, dev, s, it); +} + +/* +============================================================================== +*/ +static int pci9118_ai_mode2(comedi_device * dev, comedi_subdevice * s, comedi_trig * it) +{ + return pci9118_ai_mode1234(2, dev, s, it); +} + +/* +============================================================================== +*/ +static int pci9118_ai_mode3(comedi_device * dev, comedi_subdevice * s, comedi_trig * it) +{ + return pci9118_ai_mode1234(3, dev, s, it); +} + +/* +============================================================================== +*/ +static int pci9118_ai_mode4(comedi_device * dev, comedi_subdevice * s, comedi_trig * it) +{ + return pci9118_ai_mode1234(4, dev, s, it); +} + +#endif + +#ifdef CONFIG_COMEDI_MODE0 + +/* +============================================================================== +*/ +static int pci9118_ai_mode0(comedi_device * dev, comedi_subdevice * s, comedi_trig * it) +{ + int timeout,i; +#ifdef PCL9118_PARANOIDCHECK + unsigned int data,m=0; +#endif + + devpriv->AdControlReg=AdControl_Int & 0xff; + devpriv->AdFunctionReg=AdFunction_PDTrg|AdFunction_PETrg; + outl(devpriv->AdFunctionReg,dev->iobase+PCI9118_ADFUNC);// positive triggers, no S&H, no burst, burst stop, no post trigger, no about trigger, trigger stop + + if (!check_and_setup_channel_list(dev,s,it->n_chan, it->chanlist, 0)) return -EINVAL; + + outl(0,dev->iobase+PCI9118_DELFIFO); // flush FIFO + + if (it->n==0) it->n=1; + + for (i=0; i<(it->n_chan*it->n); i++) { + outw(0, dev->iobase+PCI9118_SOFTTRG); /* start conversion */ + udelay(3); + timeout=100; + while (timeout--) { + if (inl(dev->iobase+PCI9118_ADSTAT) & AdStatus_ADrdy) goto conv_finish; + udelay(1); + } + + comedi_error(dev,"A/D mode0 timeout"); + it->data[i]=0; + outl(0,dev->iobase+PCI9118_DELFIFO); // flush FIFO + return -ETIME; + +conv_finish: + if (this_board->ai_maxdata==0xfff) { +#ifdef PCL9118_PARANOIDCHECK + data=inw(dev->iobase+PCI9118_AD_DATA); + if ((data & 0xf)!=devpriv->chanlist[m++]) { + comedi_error(dev,"A/D mode0 data droput!"); + return -ETIME; + } + it->data[i] = (data >> 4) & 0xfff; + if (m>=it->n_chan) m=0; +#else + it->data[i] = (inw(dev->iobase+PCI9118_AD_DATA)>>4) & 0xfff; +#endif + } else { + it->data[i] = inl(dev->iobase+PCI9118_AD_DATA) & 0xffff; + } + } + + outl(0,dev->iobase+PCI9118_DELFIFO); // flush FIFO + + return i; +} + +/* +============================================================================== +*/ +static int pci9118_ao_mode0(comedi_device * dev, comedi_subdevice * s, comedi_trig * it) +{ + int chan,i; + sampl_t data; + + for (i=0;in_chan;i++){ + data=it->data[i]; + chan=CR_CHAN(it->chanlist[i]); + if (chan) { + outl(data, dev->iobase + PCI9118_DA2); + } else { + outl(data, dev->iobase + PCI9118_DA1); + } + } + + return it->n_chan; +} +/* +============================================================================== +*/ +static int pci9118_di_mode0(comedi_device * dev, comedi_subdevice * s, comedi_trig * it) +{ + unsigned int data; + int chan; + int i; + + data = inl(dev->iobase + PCI9118_DI); + + for(i=0;in_chan;i++) { + chan=CR_CHAN(it->chanlist[i]); + it->data[i]=(data>>chan)&1; + } + + return it->n_chan; +} + +/* +============================================================================== +*/ +static int pci9118_do_mode0(comedi_device * dev, comedi_subdevice * s, comedi_trig * it) +{ + unsigned int mask, data; + int chan; + int i; + + data=s->state; + for(i=0;in_chan;i++) { + chan=CR_CHAN(it->chanlist[i]); + mask=(1<data[i]) + data |= mask; + } + outl((data & 0xf), dev->iobase + PCI9118_DO); + s->state = data; + + return it->n_chan; +} + +#endif + +/* +============================================================================== +*/ +int pci9118_insn_read_ai(comedi_device *dev,comedi_subdevice *s, comedi_insn *insn,lsampl_t *data) +{ + + int n,timeout; + + devpriv->AdControlReg=AdControl_Int & 0xff; + devpriv->AdFunctionReg=AdFunction_PDTrg|AdFunction_PETrg; + outl(devpriv->AdFunctionReg,dev->iobase+PCI9118_ADFUNC);// positive triggers, no S&H, no burst, burst stop, no post trigger, no about trigger, trigger stop + + if (!check_and_setup_channel_list(dev,s,1,&insn->chanspec, 0)) return -EINVAL; + + outl(0,dev->iobase+PCI9118_DELFIFO); // flush FIFO + + for (n=0; nn; n++) { + outw(0, dev->iobase+PCI9118_SOFTTRG); /* start conversion */ + udelay(3); + timeout=100; + while (timeout--) { + if (inl(dev->iobase+PCI9118_ADSTAT) & AdStatus_ADrdy) goto conv_finish; + udelay(1); + } + + comedi_error(dev,"A/D insn timeout"); + insn->data[n]=0; + outl(0,dev->iobase+PCI9118_DELFIFO); // flush FIFO + return -ETIME; + +conv_finish: + if (this_board->ai_maxdata==0xfff) { + data[n] = (inw(dev->iobase+PCI9118_AD_DATA)>>4) & 0xfff; + } else { + data[n] = inl(dev->iobase+PCI9118_AD_DATA) & 0xffff; + } + } + + outl(0,dev->iobase+PCI9118_DELFIFO); // flush FIFO + return n; + +} + +/* +============================================================================== +*/ +int pci9118_insn_write_ao(comedi_device *dev,comedi_subdevice *s, comedi_insn *insn,lsampl_t *data) +{ + int n,chanreg; + + if (CR_CHAN(insn->chanspec)) { chanreg=PCI9118_DA2;} + else { chanreg=PCI9118_DA1; } + + for (n=0; nn; n++) { + outl(data[n], dev->iobase + chanreg); + } + + return n; +} + +/* +============================================================================== +*/ +int pci9118_insn_read_di(comedi_device *dev,comedi_subdevice *s, comedi_insn *insn,lsampl_t *data) +{ + int n; + + for (n=0; nn; n++) { + data[n] = inl(dev->iobase + PCI9118_DI) & 0xf; + } + + return n; +} + +/* +============================================================================== +*/ +int pci9118_insn_bits_di(comedi_device *dev,comedi_subdevice *s, comedi_insn *insn,lsampl_t *data) +{ + data[1] = inl(dev->iobase + PCI9118_DI) & 0xf; + + return 2; +} + +/* +============================================================================== +*/ +int pci9118_insn_write_do(comedi_device *dev,comedi_subdevice *s, comedi_insn *insn,lsampl_t *data) +{ + int n; + + for (n=0; nn; n++) { + s->state=data[n]& 0xf; + outl(s->state, dev->iobase + PCI9118_DO); + } + + return n; +} + +/* +============================================================================== +*/ +int pci9118_insn_bits_do(comedi_device *dev,comedi_subdevice *s, comedi_insn *insn,lsampl_t *data) +{ + if(data[0]){ + s->state &= ~data[0]; + s->state |= (data[0]&data[1]); + outl(s->state & 0xf, dev->iobase + PCI9118_DO); + } + data[1] = inl(dev->iobase + PCI9118_DI) & 0xf; + + return 2; +} + +/* +============================================================================== +*/ +static int pci9118_ai_cmdtest(comedi_device *dev,comedi_subdevice *s,comedi_cmd *cmd) +{ + int err=0; + int tmp,divisor1,divisor2; + + /* step 1: make sure trigger sources are trivially valid */ + + tmp=cmd->start_src; + cmd->start_src &= TRIG_NOW; + if(!cmd->start_src && tmp!=cmd->start_src)err++; + + tmp=cmd->scan_begin_src; + cmd->scan_begin_src &= TRIG_TIMER|TRIG_EXT|TRIG_FOLLOW; + if(!cmd->scan_begin_src && tmp!=cmd->scan_begin_src)err++; + + tmp=cmd->convert_src; + cmd->convert_src &= TRIG_TIMER|TRIG_EXT; + 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|TRIG_NONE; + if(!cmd->stop_src && tmp!=cmd->stop_src)err++; + + if(err)return 1; + + /* step 2: make sure trigger sources are unique and mutually compatible */ + + if(cmd->scan_begin_src!=TRIG_TIMER && + cmd->scan_begin_src!=TRIG_EXT && + cmd->scan_begin_src!=TRIG_FOLLOW)err++; + if(cmd->convert_src!=TRIG_TIMER && + cmd->convert_src!=TRIG_EXT)err++; + if(cmd->scan_begin_src==!TRIG_FOLLOW && + cmd->convert_src==TRIG_EXT)err++; + + if(err)return 2; + + /* step 3: make sure arguments are trivially compatible */ + + if(cmd->start_arg!=0){ + cmd->start_arg=0; + err++; + } + if(cmd->scan_begin_src==TRIG_TIMER){ + if(cmd->scan_begin_argai_ns_min){ + cmd->scan_begin_arg=this_board->ai_ns_min; + err++; + } + if(cmd->scan_begin_arg>devpriv->i8254_osc_base*0xffffff){ + cmd->scan_begin_arg=devpriv->i8254_osc_base*0xffffff; + err++; + } + } + if(cmd->convert_src==TRIG_TIMER){ + if (cmd->scan_begin_src==TRIG_TIMER) { + if((cmd->convert_arg)&&(cmd->convert_argai_ns_min)){ + cmd->convert_arg=this_board->ai_ns_min; + err++; + } + } else { + if(cmd->convert_argai_ns_min){ + cmd->convert_arg=this_board->ai_ns_min; + err++; + } + } + if(cmd->convert_arg>devpriv->i8254_osc_base*0xffffff){ + cmd->convert_arg=devpriv->i8254_osc_base*0xffffff; + err++; + } + } + + if(!cmd->chanlist_len){ + cmd->chanlist_len=1; + err++; + } + if(cmd->chanlist_len>this_board->n_aichanlist){ + cmd->chanlist_len=this_board->n_aichanlist; + err++; + } + if(cmd->scan_end_arg!=cmd->chanlist_len){ + cmd->scan_end_arg=cmd->chanlist_len; + err++; + } + if(cmd->stop_src==TRIG_COUNT){ + if(!cmd->stop_arg){ + cmd->stop_arg=1; + err++; + } + } 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; + i8253_cascade_ns_to_timer(devpriv->i8254_osc_base,&divisor1,&divisor2,&cmd->scan_begin_arg,cmd->flags&TRIG_ROUND_MASK); + if(tmp!=cmd->scan_begin_arg)err++; + } + if(cmd->convert_src==TRIG_TIMER){ + tmp=cmd->convert_arg; + i8253_cascade_ns_to_timer(devpriv->i8254_osc_base,&divisor1,&divisor2,&cmd->convert_arg,cmd->flags&TRIG_ROUND_MASK); + if(tmp!=cmd->convert_arg)err++; + if(cmd->scan_begin_src==TRIG_TIMER) { + if (cmd->convert_arg==0) { + if (cmd->scan_begin_argai_ns_min*(cmd->scan_end_arg+2)) { + cmd->scan_begin_arg=this_board->ai_ns_min*(cmd->scan_end_arg+2); + err++; + } + } else { + if (cmd->scan_begin_argconvert_arg*cmd->scan_end_arg){ + cmd->scan_begin_arg=cmd->convert_arg*cmd->scan_end_arg; + err++; + } + } + } + } + + if(err)return 4; + + return 0; +} + +/* +============================================================================== +*/ +static int pci9118_ai_cmd(comedi_device *dev,comedi_subdevice *s) +{ + comedi_cmd *cmd=&s->cmd; + + devpriv->ai1234_flags=cmd->flags; + devpriv->ai1234_n_chan=cmd->chanlist_len; + devpriv->ai1234_chanlist=cmd->chanlist; + devpriv->ai1234_data=cmd->data; + devpriv->ai1234_data_len=cmd->data_len; + if (cmd->stop_arg==TRIG_COUNT) { devpriv->ai1234_scans=cmd->stop_arg; } + else { devpriv->ai1234_scans=0; } + devpriv->ai1234_timer1=0; + devpriv->ai1234_timer2=0; + + if(cmd->scan_begin_src==TRIG_FOLLOW){ // mode 1 or 3 + if (cmd->convert_src==TRIG_TIMER) { // mode 1 + devpriv->ai1234_timer1=cmd->convert_arg; + return pci9118_ai_docmd_and_mode(1,dev,s); + } + if (cmd->convert_src==TRIG_EXT) { // mode 3 + return pci9118_ai_docmd_and_mode(3,dev,s); + } + } + + if((cmd->scan_begin_src==TRIG_TIMER)&&(cmd->convert_src==TRIG_TIMER)){ // mode 2 + devpriv->ai1234_timer1=cmd->scan_begin_arg; + devpriv->ai1234_timer2=cmd->convert_arg; + return pci9118_ai_docmd_and_mode(2,dev,s); + } + + if((cmd->scan_begin_src==TRIG_EXT)&&(cmd->convert_src==TRIG_TIMER)){ // mode 4 + devpriv->ai1234_timer2=cmd->convert_arg; + return pci9118_ai_docmd_and_mode(4,dev,s); + } + + return -1; +} + +/* +============================================================================== +*/ +int check_and_setup_channel_list(comedi_device * dev, comedi_subdevice * s, int n_chan, unsigned int *chanlist,char rot) +{ + unsigned int i, differencial=0, bipolar=0; + unsigned int scanquad, gain; + + /* correct channel and range number check itself comedi/range.c */ + if (n_chan<1) { + comedi_error(dev,"range/channel list is empty!"); + return 0; + } + + if (CR_AREF(chanlist[0])==AREF_DIFF) + differencial=1; // all input must be diff + if (CR_RANGE(chanlist[0]) 1) + for (i=1 ; iusemux)&(differencial)&(CR_CHAN(chanlist[i])>=this_board->n_aichand)) { + comedi_error(dev,"If AREF_DIFF is used then is available only 8 channels!"); + return 0; + } + } + + // All is ok, so we can setup channel/range list + + if (!bipolar) { + devpriv->AdControlReg|=AdControl_UniP; // enable bipolar + } else { + devpriv->AdControlReg&=((~AdControl_UniP)&0xff); // set unibipolar + } + + if (differencial) { + devpriv->AdControlReg|=AdControl_UniP; // enable diff inputs + } else { + devpriv->AdControlReg&=((~AdControl_Diff)&0xff); // set single ended inputs + } + + outl(devpriv->AdControlReg, dev->iobase+PCI9118_ADCNTRL); // setup mode + + outl(2,dev->iobase+PCI9118_SCANMOD); // gods know why this sequence (disasembled from DOS driver)! + outl(0,dev->iobase+PCI9118_SCANMOD); + outl(1,dev->iobase+PCI9118_SCANMOD); + +#ifdef PCL9118_PARANOIDCHECK + devpriv->chanlistlen=n_chan; +#endif + + for (i=0; ichanlist[i]=(scanquad & 0xf)<iobase+PCI9118_GAIN); + } + + outl(0,dev->iobase+PCI9118_SCANMOD); // close scan queue + udelay(100); // important delay, or first sample will be cripled + + return 1; // we can serve this with scan logic +} + + +/* +============================================================================== +*/ +void start_pacer(comedi_device * dev, int mode, unsigned int divisor1, unsigned int divisor2) +{ + outl(0x74, dev->iobase + PCI9118_CNTCTRL); + outl(0x30, dev->iobase + PCI9118_CNTCTRL); + udelay(1); + + if ((mode==1)||(mode==2)||(mode==4)) { + outl(divisor2 & 0xff, dev->iobase + PCI9118_CNT2); + outl((divisor2 >> 8) & 0xff, dev->iobase + PCI9118_CNT2); + outl(divisor1 & 0xff, dev->iobase + PCI9118_CNT1); + outl((divisor1 >> 8) & 0xff, dev->iobase + PCI9118_CNT1); + } +} + +/* +============================================================================== +*/ +int pci9118_exttrg_add(comedi_device * dev, unsigned char source) +{ + if (source>3) return -1; // incorrect source + devpriv->exttrg_users|=(1<IntControlReg|=Int_DTrg; + outl(devpriv->IntControlReg,dev->iobase+PCI9118_INTCTRL); + outl(inl(devpriv->iobase_a+AMCC_OP_REG_INTCSR)|0x1f00, devpriv->iobase_a+AMCC_OP_REG_INTCSR); // allow INT in AMCC + return 0; +} + +/* +============================================================================== +*/ +int pci9118_exttrg_del(comedi_device * dev, unsigned char source) +{ + if (source>3) return -1; // incorrect source + devpriv->exttrg_users&=~(1<exttrg_users) { // shutdown ext trg intterrupts + devpriv->IntControlReg&=~Int_DTrg; + if (!devpriv->IntControlReg) // all IRQ disabled + outl(inl(devpriv->iobase_a+AMCC_OP_REG_INTCSR)&(~0x00001f00), devpriv->iobase_a+AMCC_OP_REG_INTCSR); // disable int in AMCC + outl(devpriv->IntControlReg,dev->iobase+PCI9118_INTCTRL); + } + return 0; +} + +/* +============================================================================== +*/ +int pci9118_ai_cancel(comedi_device * dev, comedi_subdevice * s) +{ + outl(inl(devpriv->iobase_a+AMCC_OP_REG_INTCSR)&(~AINT_WRITE_COMPL), devpriv->iobase_a+AMCC_OP_REG_INTCSR); // stop amcc irqs + outl(inl(devpriv->iobase_a+AMCC_OP_REG_MCSR)&(~EN_A2P_TRANSFERS), devpriv->iobase_a+AMCC_OP_REG_MCSR); // stop DMA + pci9118_exttrg_del(dev,0); + start_pacer(dev,0,0,0); // stop 8254 counters + devpriv->AdFunctionReg=AdFunction_PDTrg|AdFunction_PETrg; + outl(devpriv->AdFunctionReg,dev->iobase+PCI9118_ADFUNC);// positive triggers, no S&H, no burst, burst stop, no post trigger, no about trigger, trigger stop + devpriv->AdControlReg=AdControl_Int; + outl(devpriv->AdControlReg,dev->iobase+PCI9118_ADCNTRL);// bipolar, S.E., use 8254, stop 8354, internal trigger, soft trigger, disable INT and DMA + outl(0,dev->iobase+PCI9118_BURST); + outl(1,dev->iobase+PCI9118_SCANMOD); + outl(2,dev->iobase+PCI9118_SCANMOD);// reset scan queue + outl(0,dev->iobase+PCI9118_DELFIFO); // flush FIFO + + devpriv->ai_do=0; + devpriv->ai1234_act_scan=0; + s->cur_chan=0; + devpriv->ai1234_buf_ptr=0; + devpriv->neverending_ai=0; + devpriv->ai4_status=0; + devpriv->dma_actbuf=0; + + return 0; +} + +/* +============================================================================== +*/ +static int pci9118_reset(comedi_device *dev) +{ + devpriv->IntControlReg=0; + devpriv->exttrg_users=0; + inl(dev->iobase+PCI9118_INTCTRL); + outl(devpriv->IntControlReg,dev->iobase+PCI9118_INTCTRL);// disable interrupts source + outl(0xb4, dev->iobase + PCI9118_CNTCTRL); + start_pacer(dev,0,0,0); // stop 8254 counters + devpriv->AdControlReg=0; + outl(devpriv->AdControlReg,dev->iobase+PCI9118_ADCNTRL);// bipolar, S.E., use 8254, stop 8354, internal trigger, soft trigger, disable INT and DMA + outl(0,dev->iobase+PCI9118_BURST); + outl(1,dev->iobase+PCI9118_SCANMOD); + outl(2,dev->iobase+PCI9118_SCANMOD);// reset scan queue + devpriv->AdFunctionReg=AdFunction_PDTrg|AdFunction_PETrg; + outl(devpriv->AdFunctionReg,dev->iobase+PCI9118_ADFUNC);// positive triggers, no S&H, no burst, burst stop, no post trigger, no about trigger, trigger stop + + outl(2047,dev->iobase+PCI9118_DA1);// reset A/D outs to 0V + outl(2047,dev->iobase+PCI9118_DA2); + outl(0,dev->iobase+PCI9118_DO); // reset digi outs to L + udelay(10); + inl(dev->iobase+PCI9118_AD_DATA); + outl(0,dev->iobase+PCI9118_DELFIFO); // flush FIFO + inl(dev->iobase+PCI9118_ADSTAT); // flush A/D status register + inl(dev->iobase+PCI9118_INTSRC); // flush INT requests + devpriv->AdControlReg=AdControl_Int; + outl(devpriv->AdControlReg,dev->iobase+PCI9118_ADCNTRL);// bipolar, S.E., use 8254, stop 8354, internal trigger, soft trigger, disable INT and DMA + + devpriv->cnt0_users=0; + devpriv->exttrg_users=0; + + return 0; +} + +/* +============================================================================== +*/ +static int pci9118_attach(comedi_device *dev,comedi_devconfig *it) +{ + comedi_subdevice *s; + int ret,pages,i; + unsigned int board,iobase_a,iobase_9,irq; + struct amcc_struct *card=NULL; + unsigned char pci_bus,pci_slot,pci_func; + unsigned int master; + + board=dev->board; + + rt_printk("comedi%d: adl_pci9118: board=%s",dev->minor,boardtypes[board].name); + + if ((it->options[0]<1)&(it->options[1]<1)) { // use autodetection + if ((card=find_free_card_by_device(boardtypes[board].device_id))==NULL) { + rt_printk(" - Unused card not found in system!\n"); + return -EIO; + } + } else { + switch (find_free_card_by_position(boardtypes[board].device_id,it->options[0],it->options[1],&card)) { + case 1: + rt_printk(" - Card not found on requested position!\n"); + return -EIO; + case 2: + rt_printk(" - Card on requested position is used!\n"); + return -EIO; + } + } + + + if (alloc_amcc_card(card)!=0) { + rt_printk(" - Can't allocate card!\n"); + return -EIO; + } + + if ((card_data(card,&pci_bus,&pci_slot,&pci_func, + &iobase_a,&iobase_9,&irq,&master))<0) { + rt_printk(" - Can't get AMCC data!\n"); + return -EIO; + } + + rt_printk(", b:s:f=%d:%d:%d, io=0x%4x, 0x%4x",pci_bus,pci_slot,pci_func,iobase_9,iobase_a); + + if (check_region(iobase_9, boardtypes[board].iorange_9118) < 0) { + rt_printk("I/O port conflict\n"); + return -EIO; + } + + dev->iobase=iobase_9; + dev->iosize=boardtypes[board].iorange_9118; + request_region(dev->iobase, dev->iosize, "ADLink PCI-9118"); + + dev->board_name = boardtypes[board].name; + + if((ret=alloc_private(dev,sizeof(pci9118_private)))<0) + return -ENOMEM; + + devpriv->amcc=card; + devpriv->master=master; + devpriv->iobase_a=iobase_a; + devpriv->iosize_a=boardtypes[board].iorange_amcc; + request_region(devpriv->iobase_a, devpriv->iosize_a, "ADLink PCI-9118"); + + if (irq>0) { + if (request_irq(irq, interrupt_pci9118, SA_INTERRUPT, "ADLink PCI-9118", dev)) { + rt_printk(", unable to allocate IRQ %d, DISABLING IT", irq); + irq=0; /* Can't use IRQ */ + } else { + rt_printk(", irq=%d", irq); + } + } else { + rt_printk(", IRQ disabled"); + } + + dev->irq = irq; + + if (master) { // alloc DMA buffers + devpriv->dma_doublebuf=0; + for (i=0; i<2; i++) { + for (pages=4; pages>=0; pages--) + if ((devpriv->dmabuf_virt[i]=__get_free_pages(GFP_KERNEL,4))) + break; + if (devpriv->dmabuf_virt[i]) { + devpriv->dmabuf_pages[i]=pages; + devpriv->dmabuf_size[i]=PAGE_SIZE*pages; + devpriv->dmabuf_samples[i]=devpriv->dmabuf_size[i]>>1; + devpriv->dmabuf_hw[i]=virt_to_bus((void *)devpriv->dmabuf_virt[i]); + } + } + if (!devpriv->dmabuf_virt[0]) { + rt_printk(", Can't allocate DMA buffer, DMA disabled!"); + master=0; + } + + if (devpriv->dmabuf_virt[1]) + devpriv->dma_doublebuf=0; + + } + + if ((devpriv->master=master)) { + rt_printk(", bus master"); + } else { + rt_printk(", no bus master"); + } + + devpriv->usemux=0; + if (it->options[2]>0) { + devpriv->usemux=it->options[2]; + if (devpriv->usemux>256) devpriv->usemux=256; // max 256 channels! + if (it->options[3]>0) + if (devpriv->usemux>128) { + devpriv->usemux=128; // max 128 channels with softare S&H! + } + rt_printk(", ext. mux %d channels",devpriv->usemux); + } + + printk(".\n"); + + dev->n_subdevices = 4; + if((ret=alloc_subdevices(dev))<0) + return ret; + + s = dev->subdevices + 0; + s->type = COMEDI_SUBD_AI; + s->subdev_flags = SDF_READABLE|SDF_RT|SDF_COMMON|SDF_GROUND|SDF_DIFF; + if (devpriv->usemux) { s->n_chan = devpriv->usemux; } + else { s->n_chan = this_board->n_aichan; } + s->maxdata = this_board->ai_maxdata; + s->len_chanlist = this_board->n_aichanlist; + s->range_table = this_board->rangelist_ai; + s->cancel=pci9118_ai_cancel; +#ifdef CONFIG_COMEDI_MODE0 + s->trig[0] = pci9118_ai_mode0; +#endif +#ifdef CONFIG_COMEDI_MODES + if (irq&&master) { + s->trig[1] = pci9118_ai_mode1; + s->trig[2] = pci9118_ai_mode2; + s->trig[3] = pci9118_ai_mode3; + s->trig[4] = pci9118_ai_mode4; + } +#endif + s->insn_read=pci9118_insn_read_ai; + s->do_cmdtest=pci9118_ai_cmdtest; + s->do_cmd=pci9118_ai_cmd; + + s = dev->subdevices + 1; + s->type = COMEDI_SUBD_AO; + s->subdev_flags = SDF_WRITEABLE|SDF_GROUND|SDF_COMMON|SDF_RT; + s->n_chan = this_board->n_aochan; + s->maxdata = this_board->ao_maxdata; + s->len_chanlist = this_board->n_aochan; + s->range_table = this_board->rangelist_ao; +#ifdef CONFIG_COMEDI_MODE0 + s->trig[0] = pci9118_ao_mode0; +#endif + s->insn_write=pci9118_insn_write_ao; + + s = dev->subdevices + 2; + s->type = COMEDI_SUBD_DI; + s->subdev_flags = SDF_READABLE|SDF_RT|SDF_GROUND|SDF_COMMON; + s->n_chan = 4; + s->maxdata = 1; + s->len_chanlist = 4; + s->range_table = &range_digital; +#ifdef CONFIG_COMEDI_MODE0 + s->trig[0] = pci9118_di_mode0; +#endif + s->io_bits=0; /* all bits input */ + s->insn_read=pci9118_insn_read_di; + s->insn_bits=pci9118_insn_bits_di; + + s = dev->subdevices + 3; + s->type = COMEDI_SUBD_DO; + s->subdev_flags = SDF_WRITEABLE|SDF_RT|SDF_GROUND|SDF_COMMON; + s->n_chan = 4; + s->maxdata = 1; + s->len_chanlist = 4; + s->range_table = &range_digital; +#ifdef CONFIG_COMEDI_MODE0 + s->trig[0] = pci9118_do_mode0; +#endif + s->io_bits=0xf; /* all bits output */ + s->insn_write=pci9118_insn_write_do; + s->insn_bits=pci9118_insn_bits_do; + + pci9118_reset(dev); + + devpriv->valid=1; + devpriv->i8254_osc_base=250; // 250ns=4MHz + + return 0; +} + + +/* +============================================================================== +*/ +static int pci9118_detach(comedi_device *dev) +{ + if (dev->private) { + if (devpriv->valid) pci9118_reset(dev); + release_region(devpriv->iobase_a,devpriv->iosize_a); + if (devpriv->allocated) + free_amcc_card(devpriv->amcc); + if (devpriv->dmabuf_virt[0]) + free_pages(devpriv->dmabuf_virt[0],devpriv->dmabuf_pages[0]); + if (devpriv->dmabuf_virt[1]) + free_pages(devpriv->dmabuf_virt[1],devpriv->dmabuf_pages[1]); + } + + if(dev->irq){ + free_irq(dev->irq,dev); + } + + release_region(dev->iobase,dev->iosize); + + return 0; +} + +/* +============================================================================== +*/ +static int pci9118_recognize(char *name) +{ + int i; + + for (i = 0; i < n_boardtypes; i++) { + if (!strcmp(boardtypes[i].name, name)) { + return i; + } + } + + return -1; +} + + + +#ifdef MODULE +/* +============================================================================== +*/ + int init_module(void) +{ + amcc_init(); + comedi_driver_register(&driver_pci9118); + + return 0; +} + +/* +============================================================================== +*/ +void cleanup_module(void) +{ + comedi_driver_unregister(&driver_pci9118); + amcc_cleanup(); + +} +/* +============================================================================== +*/ + +#endif + +/* a unknow future: + * [3] - sample&hold signal - card can generate hold signal for external S&H board + * 0=use SSHO (pin 45) signal with onboard hardware S&H logic + * 1=use ADCHN7 (pin 23) signal and use software for timing + * (in this case external multiplexor can serve only 128 A/D channels) +*/ \ No newline at end of file diff --git a/comedi/drivers/amcc_s5933.h b/comedi/drivers/amcc_s5933.h new file mode 100644 index 00000000..d28faeb8 --- /dev/null +++ b/comedi/drivers/amcc_s5933.h @@ -0,0 +1,348 @@ +/* + module/amcc_s5933.h + + Stuff for AMCC S5933 PCI Controller + + Author: Michal Dobes + + Inspirated from general-purpose AMCC S5933 PCI Matchmaker driver + made by Andrea Cisternino + and as result of espionage from MITE code made by David A. Schleef. + Thanks to AMCC for their on-line documentation and bus master DMA + example. +*/ + +#ifndef _AMCC_S5933_H_ +#define _AMCC_S5933_H_ + +#include + +#ifdef PCI_SUPPORT_VER1 + Sorry, no support for 2.1.55 and older! :-(((( +#endif + + +/****************************************************************************/ +/* AMCC Operation Register Offsets - PCI */ +/****************************************************************************/ + +#define AMCC_OP_REG_OMB1 0x00 +#define AMCC_OP_REG_OMB2 0x04 +#define AMCC_OP_REG_OMB3 0x08 +#define AMCC_OP_REG_OMB4 0x0c +#define AMCC_OP_REG_IMB1 0x10 +#define AMCC_OP_REG_IMB2 0x14 +#define AMCC_OP_REG_IMB3 0x18 +#define AMCC_OP_REG_IMB4 0x1c +#define AMCC_OP_REG_FIFO 0x20 +#define AMCC_OP_REG_MWAR 0x24 +#define AMCC_OP_REG_MWTC 0x28 +#define AMCC_OP_REG_MRAR 0x2c +#define AMCC_OP_REG_MRTC 0x30 +#define AMCC_OP_REG_MBEF 0x34 +#define AMCC_OP_REG_INTCSR 0x38 +#define AMCC_OP_REG_INTCSR_SRC (AMCC_OP_REG_INTCSR + 2) /* INT source */ +#define AMCC_OP_REG_INTCSR_FEC (AMCC_OP_REG_INTCSR + 3) /* FIFO ctrl */ +#define AMCC_OP_REG_MCSR 0x3c +#define AMCC_OP_REG_MCSR_NVDATA (AMCC_OP_REG_MCSR + 2) /* Data in byte 2 */ +#define AMCC_OP_REG_MCSR_NVCMD (AMCC_OP_REG_MCSR + 3) /* Command in byte 3 */ + +#define AMCC_FIFO_DEPTH_DWORD 8 +#define AMCC_FIFO_DEPTH_BYTES (8 * sizeof (u32)) + +/****************************************************************************/ +/* AMCC Operation Registers Size - PCI */ +/****************************************************************************/ + +#define AMCC_OP_REG_SIZE 64 /* in bytes */ + +/****************************************************************************/ +/* AMCC Operation Register Offsets - Add-on */ +/****************************************************************************/ + +#define AMCC_OP_REG_AIMB1 0x00 +#define AMCC_OP_REG_AIMB2 0x04 +#define AMCC_OP_REG_AIMB3 0x08 +#define AMCC_OP_REG_AIMB4 0x0c +#define AMCC_OP_REG_AOMB1 0x10 +#define AMCC_OP_REG_AOMB2 0x14 +#define AMCC_OP_REG_AOMB3 0x18 +#define AMCC_OP_REG_AOMB4 0x1c +#define AMCC_OP_REG_AFIFO 0x20 +#define AMCC_OP_REG_AMWAR 0x24 +#define AMCC_OP_REG_APTA 0x28 +#define AMCC_OP_REG_APTD 0x2c +#define AMCC_OP_REG_AMRAR 0x30 +#define AMCC_OP_REG_AMBEF 0x34 +#define AMCC_OP_REG_AINT 0x38 +#define AMCC_OP_REG_AGCSTS 0x3c +#define AMCC_OP_REG_AMWTC 0x58 +#define AMCC_OP_REG_AMRTC 0x5c + +/****************************************************************************/ +/* AMCC - Add-on General Control/Status Register */ +/****************************************************************************/ + +#define AGCSTS_CONTROL_MASK 0xfffff000 +#define AGCSTS_NV_ACC_MASK 0xe0000000 +#define AGCSTS_RESET_MASK 0x0e000000 +#define AGCSTS_NV_DA_MASK 0x00ff0000 +#define AGCSTS_BIST_MASK 0x0000f000 +#define AGCSTS_STATUS_MASK 0x000000ff +#define AGCSTS_TCZERO_MASK 0x000000c0 +#define AGCSTS_FIFO_ST_MASK 0x0000003f + +#define AGCSTS_RESET_MBFLAGS 0x08000000 +#define AGCSTS_RESET_P2A_FIFO 0x04000000 +#define AGCSTS_RESET_A2P_FIFO 0x02000000 +#define AGCSTS_RESET_FIFOS (AGCSTS_RESET_A2P_FIFO | AGCSTS_RESET_P2A_FIFO) + +#define AGCSTS_A2P_TCOUNT 0x00000080 +#define AGCSTS_P2A_TCOUNT 0x00000040 + +#define AGCSTS_FS_P2A_EMPTY 0x00000020 +#define AGCSTS_FS_P2A_HALF 0x00000010 +#define AGCSTS_FS_P2A_FULL 0x00000008 + +#define AGCSTS_FS_A2P_EMPTY 0x00000004 +#define AGCSTS_FS_A2P_HALF 0x00000002 +#define AGCSTS_FS_A2P_FULL 0x00000001 + +/****************************************************************************/ +/* AMCC - Add-on Interrupt Control/Status Register */ +/****************************************************************************/ + +#define AINT_INT_MASK 0x00ff0000 +#define AINT_SEL_MASK 0x0000ffff +#define AINT_IS_ENSEL_MASK 0x00001f1f + +#define AINT_INT_ASSERTED 0x00800000 +#define AINT_BM_ERROR 0x00200000 +#define AINT_BIST_INT 0x00100000 + +#define AINT_RT_COMPLETE 0x00080000 +#define AINT_WT_COMPLETE 0x00040000 + +#define AINT_OUT_MB_INT 0x00020000 +#define AINT_IN_MB_INT 0x00010000 + +#define AINT_READ_COMPL 0x00008000 +#define AINT_WRITE_COMPL 0x00004000 + +#define AINT_OMB_ENABLE 0x00001000 +#define AINT_OMB_SELECT 0x00000c00 +#define AINT_OMB_BYTE 0x00000300 + +#define AINT_IMB_ENABLE 0x00000010 +#define AINT_IMB_SELECT 0x0000000c +#define AINT_IMB_BYTE 0x00000003 + +/* Enable Bus Mastering */ +#define EN_A2P_TRANSFERS 0x00000400 +/* FIFO Flag Reset */ +#define RESET_A2P_FLAGS 0x04000000L +/* FIFO Relative Priority */ +#define A2P_HI_PRIORITY 0x00000100L +/* Identify Interrupt Sources */ +#define ANY_S593X_INT 0x00800000L +#define READ_TC_INT 0x00080000L +#define WRITE_TC_INT 0x00040000L +#define IN_MB_INT 0x00020000L +#define MASTER_ABORT_INT 0x00100000L +#define TARGET_ABORT_INT 0x00200000L +#define BUS_MASTER_INT 0x00200000L + +/****************************************************************************/ + +struct amcc_struct{ + struct amcc_struct *next; + int used; + struct pci_dev *pcidev; + unsigned short device; + unsigned int master; + unsigned char pci_bus; + unsigned char pci_slot; + unsigned char pci_func; + unsigned int amcc_io_addr; + unsigned int daq_io_addr; + unsigned int irq; +}; + +struct amcc_struct *amcc_devices; // ptr to root list of all amcc devices + +/****************************************************************************/ + +void amcc_init(void); +void amcc_cleanup(void); +struct amcc_struct *find_free_card_by_device(unsigned short device_id); +int alloc_amcc_card(struct amcc_struct *amcc); +int free_amcc_card(struct amcc_struct *amcc); +void display_amcc_cards(void); +int card_data(struct amcc_struct *amcc, + unsigned char *pci_bus, unsigned char *pci_slot, unsigned char *pci_func, + unsigned int *amcc_io, unsigned int *daq_io, unsigned int *irq, + unsigned int *master); + +/****************************************************************************/ + +/* build list of amcc cards in this system */ +void amcc_init(void) +{ + struct pci_dev *pcidev; + struct amcc_struct *amcc,*last; + + amcc_devices=NULL; + last=NULL; + +#if LINUX_VERSION_CODE < 0x020300 + for(pcidev=pci_devices;pcidev;pcidev=pcidev->next){ +#else + pci_for_each_dev(pcidev){ +#endif + if(pcidev->vendor==PCI_VENDOR_ID_AMCC){ + amcc=kmalloc(sizeof(*amcc),GFP_KERNEL); + memset(amcc,0,sizeof(*amcc)); + + amcc->pcidev=pcidev; + if (last) { last->next=amcc; } + else { amcc_devices=amcc; } + last=amcc; + +#if LINUX_VERSION_CODE < 0x020300 + amcc->device=pcidev->device; + amcc->master=pcidev->master; + amcc->pci_bus=pcidev->bus->number; + amcc->pci_slot=PCI_SLOT(pcidev->devfn); + amcc->pci_func=PCI_FUNC(pcidev->devfn); + amcc->amcc_io_addr=pcidev->base_address[0] & ~3UL; + amcc->daq_io_addr=pcidev->base_address[2] & ~3UL; + amcc->irq=pcidev->irq; +#else + amcc->device=???; + amcc->master=???; + amcc->pci_bus=???; + amcc->pci_slot=???; + amcc->pci_func=???; + amcc->amcc_io_addr=pcidev->resource[0].start & ~3UL; + amcc->daq_io_addr=pcidev->resource[2].start & ~3UL; + amcc->irq=???; +#endif + + } + } + +#if 0 + display_amcc_cards(); +#endif +} + +/****************************************************************************/ +/* free up list of amcc cards in this system */ +void amcc_cleanup(void) +{ + struct amcc_struct *amcc,*next; + + for(amcc=amcc_devices;amcc;amcc=next){ + next=amcc->next; + kfree(amcc); + } + + amcc_devices=NULL; +} + +/****************************************************************************/ +/* find first unused card with this device_id */ +struct amcc_struct *find_free_card_by_device(unsigned short device_id) +{ + struct amcc_struct *amcc,*next; + + for (amcc=amcc_devices;amcc;amcc=next) { + next=amcc->next; + if ((!amcc->used)&&(amcc->device==device_id)) return amcc; + + } + + return NULL; +} + +/****************************************************************************/ +/* find card on requested position */ +int find_free_card_by_position(unsigned short device_id, unsigned short pci_bus, unsigned short pci_slot, struct amcc_struct **card) +{ + struct amcc_struct *amcc,*next; + + *card=NULL; + for (amcc=amcc_devices;amcc;amcc=next) { + next=amcc->next; + if ((amcc->device==device_id)&(amcc->pci_bus==pci_bus)&(amcc->pci_slot==pci_slot)) { + if (!(amcc->used)) { + *card=amcc; + return 0; // ok, card is found + } else { + return 2; // card exist but is used + } + } + } + + return 1; // no card found +} + +/****************************************************************************/ +/* mark card as used */ +int alloc_amcc_card(struct amcc_struct *amcc) +{ + if (!amcc) return -1; + + if (amcc->used) return 1; + amcc->used=1; + return 0; +} + +/****************************************************************************/ +/* mark card as free */ +int free_amcc_card(struct amcc_struct *amcc) +{ + if (!amcc) return -1; + + if (!amcc->used) return 1; + amcc->used=0; + return 0; +} + +/****************************************************************************/ +/* display list of found cards */ +void display_amcc_cards(void) +{ + struct amcc_struct *amcc,*next; + + printk("List of cards with AMCC S5933 PCI chip\n"); + printk("bus:slot:func device master io_amcc io_daq irq used\n"); + + for (amcc=amcc_devices;amcc;amcc=next) { + next=amcc->next; + printk("%2d %2d %2d 0x%4x %2d 0x%4x 0x%4x %2d %2d\n", + amcc->pci_bus,amcc->pci_slot,amcc->pci_func,amcc->device,amcc->master, + amcc->amcc_io_addr,amcc->daq_io_addr,amcc->irq,amcc->used); + + } +} + +/****************************************************************************/ +/* return all card information for driver */ +int card_data(struct amcc_struct *amcc, + unsigned char *pci_bus, unsigned char *pci_slot, unsigned char *pci_func, + unsigned int *amcc_io, unsigned int *daq_io, unsigned int *irq, + unsigned int *master) +{ + if (!amcc) return -1; + *pci_bus=amcc->pci_bus; + *pci_slot=amcc->pci_slot; + *pci_func=amcc->pci_func; + *amcc_io=amcc->amcc_io_addr; + *daq_io=amcc->daq_io_addr; + *irq=amcc->irq; + *master=amcc->master; + return 0; +} + +#endif diff --git a/scripts/Configure.help b/scripts/Configure.help index a12fa125..1ca51030 100644 --- a/scripts/Configure.help +++ b/scripts/Configure.help @@ -152,6 +152,13 @@ CONFIG_COMEDI_PARPORT cause your printer to stop working. This driver is not compatible with the generic Linux parallel port interface. +ADLink PCI-9118DG/HR/HG driver +CONFIG_COMEDI_ADLPCI9118 + Includes support for these ADLink cards: + ADLink PCI-9118DG + ADLink PCI-9118HR + ADLink PCI-9118HG + PCL 711 driver CONFIG_COMEDI_PCL711 Includes support for PCL 711, 711b and ACL 8112. @@ -170,9 +177,10 @@ PCL 726 driver CONFIG_COMEDI_PCL726 Includes support for PCL 726. -Advantech PCL-812PG, PCL-813B +Advantech PCL-812PG, PCL-813B, ADLink ACL-8113 CONFIG_COMEDI_PCL812 - Includes support for PCL-812PG and PCL813B. + Includes support for Advantech PCL-812PG, Advantech PCL-813B + and ADLink ACL-8113. Advantech PCL-818, PCL-718 CONFIG_COMEDI_PCL818 -- 2.26.2