From 37fecffb245d8a6eb366766a6a1b504f5d549a77 Mon Sep 17 00:00:00 2001 From: Michal Dobes Date: Thu, 12 Feb 2009 15:44:54 -0800 Subject: Staging: comedi: add adl_pci9118 driver From: Michal Dobes For ADLink cards: PCI-9118DG, PCI-9118HG, PCI-9118HR From: Michal Dobes Cc: David Schleef Cc: Frank Mori Hess Cc: Ian Abbott Signed-off-by: Greg Kroah-Hartman --- drivers/staging/comedi/drivers/adl_pci9118.c | 2100 +++++++++++++++++++++++++++ 1 file changed, 2100 insertions(+) create mode 100644 drivers/staging/comedi/drivers/adl_pci9118.c --- /dev/null +++ b/drivers/staging/comedi/drivers/adl_pci9118.c @@ -0,0 +1,2100 @@ +/* + * comedi/drivers/adl_pci9118.c + * + * hardware driver for ADLink cards: + * card: PCI-9118DG, PCI-9118HG, PCI-9118HR + * driver: pci9118dg, pci9118hg, pci9118hr + * + * Author: Michal Dobes + * +*/ +/* +Driver: adl_pci9118 +Description: Adlink PCI-9118DG, PCI-9118HG, PCI-9118HR +Author: Michal Dobes +Devices: [ADLink] PCI-9118DG (pci9118dg), PCI-9118HG (pci9118hg), + PCI-9118HR (pci9118hr) +Status: works + +This driver supports AI, AO, DI and DO subdevices. +AI subdevice supports cmd and insn interface, +other subdevices support only insn interface. +For AI: +- If cmd->scan_begin_src=TRIG_EXT then trigger input is TGIN (pin 46). +- If cmd->convert_src=TRIG_EXT then trigger input is EXTTRG (pin 44). +- If cmd->start_src/stop_src=TRIG_EXT then trigger input is TGIN (pin 46). +- It is not neccessary to have cmd.scan_end_arg=cmd.chanlist_len but + cmd.scan_end_arg modulo cmd.chanlist_len must by 0. +- If return value of cmdtest is 5 then you've bad channel list + (it isn't possible mixture S.E. and DIFF inputs or bipolar and unipolar + ranges). + +There are some hardware limitations: +a) You cann't use mixture of unipolar/bipoar ranges or differencial/single + ended inputs. +b) DMA transfers must have the length aligned to two samples (32 bit), + so there is some problems if cmd->chanlist_len is odd. This driver tries + bypass this with adding one sample to the end of the every scan and discard + it on output but this cann't be used if cmd->scan_begin_src=TRIG_FOLLOW + and is used flag TRIG_WAKE_EOS, then driver switch to interrupt driven mode + with interrupt after every sample. +c) If isn't used DMA then you can use only mode where + cmd->scan_begin_src=TRIG_FOLLOW. + +Configuration options: + [0] - PCI bus of device (optional) + [1] - PCI slot of device (optional) + If bus/slot is not specified, then first available PCI + card will be used. + [2] - 0= standard 8 DIFF/16 SE channels configuration + n= external multiplexer connected, 1<=n<=256 + [3] - 0=autoselect DMA or EOC interrupts operation + 1=disable DMA mode + 3=disable DMA and INT, only insn interface will work + [4] - sample&hold signal - card can generate signal for external S&H board + 0=use SSHO (pin 45) signal is generated in onboard hardware S&H logic + 0!=use ADCHN7 (pin 23) signal is generated from driver, number + say how long delay is requested in ns and sign polarity of the hold + (in this case external multiplexor can serve only 128 channels) + [5] - 0=stop measure on all hardware errors + 2|=ignore ADOR - A/D Overrun status + 8|=ignore Bover - A/D Burst Mode Overrun status + 256|=ignore nFull - A/D FIFO Full status + +*/ +#include "../comedidev.h" +#include "../pci_ids.h" + +#include + +#include "amcc_s5933.h" +#include "8253.h" +#include "comedi_pci.h" +#include "comedi_fc.h" + +/* paranoid checks are broken */ +#undef PCI9118_PARANOIDCHECK /* if defined, then is used code which control correct channel number on every 12 bit sample */ + +#undef PCI9118_EXTDEBUG /* if defined then driver prints a lot of messages */ + +#undef DPRINTK +#ifdef PCI9118_EXTDEBUG +#define DPRINTK(fmt, args...) rt_printk(fmt, ## args) +#else +#define DPRINTK(fmt, args...) +#endif + +#define IORANGE_9118 64 /* I hope */ +#define PCI9118_CHANLEN 255 /* len of chanlist, some source say 256, but reality looks like 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 */ + +#define START_AI_EXT 0x01 /* start measure on external trigger */ +#define STOP_AI_EXT 0x02 /* stop measure on external trigger */ +#define START_AI_INT 0x04 /* start measure on internal trigger */ +#define STOP_AI_INT 0x08 /* stop measure on internal trigger */ + +#define EXTTRG_AI 0 /* ext trg is used by AI */ + +static const 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) + } +}; + +static const 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); + +typedef struct { + const char *name; // board 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 + const comedi_lrange *rangelist_ai; // rangelist for A/D + const 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) + int half_fifo_size; // size of FIFO/2 + +} boardtype; + +static DEFINE_PCI_DEVICE_TABLE(pci9118_pci_table) = { + {PCI_VENDOR_ID_AMCC, 0x80d9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {0} +}; + +MODULE_DEVICE_TABLE(pci, pci9118_pci_table); + +static const 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, + 3000, 12, 512}, + {"pci9118hg", PCI_VENDOR_ID_AMCC, 0x80d9, + AMCC_OP_REG_SIZE, IORANGE_9118, + 16, 8, 256, PCI9118_CHANLEN, 2, 0x0fff, 0x0fff, + &range_pci9118hg, &range_bipolar10, + 3000, 12, 512}, + {"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, 512}, +}; + +#define n_boardtypes (sizeof(boardtypes)/sizeof(boardtype)) + +static comedi_driver driver_pci9118 = { + driver_name:"adl_pci9118", + module:THIS_MODULE, + attach:pci9118_attach, + detach:pci9118_detach, + num_names:n_boardtypes, + board_name:&boardtypes[0].name, + offset:sizeof(boardtype), +}; + +COMEDI_PCI_INITCLEANUP(driver_pci9118, pci9118_pci_table); + +typedef struct { + unsigned long iobase_a; // base+size for AMCC chip + unsigned int master; // master capable + struct pci_dev *pcidev; // ptr to actual pcidev + unsigned int usemux; // we want to use external multiplexor! +#ifdef PCI9118_PARANOIDCHECK + unsigned short chanlist[PCI9118_CHANLEN + 1]; // 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 ai_neverending; // 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 ai_act_scan; // how many scans we finished + unsigned int ai_buf_ptr; // data buffer ptr in samples + unsigned int ai_n_chan; // how many channels is measured + unsigned int ai_n_scanlen; // len of actual scanlist + unsigned int ai_n_realscanlen; // what we must transfer for one outgoing scan include front/back adds + unsigned int ai_act_dmapos; // position in actual real stream + unsigned int ai_add_front; // how many channels we must add before scan to satisfy S&H? + unsigned int ai_add_back; // how many channels we must add before scan to satisfy DMA? + unsigned int *ai_chanlist; // actaul chanlist + unsigned int ai_timer1; + unsigned int ai_timer2; + unsigned int ai_flags; + char ai12_startstop; // measure can start/stop on external trigger + unsigned int ai_divisor1, ai_divisor2; // divisors for start of measure on external start + unsigned int ai_data_len; + sampl_t *ai_data; + sampl_t ao_data[2]; // data output buffer + unsigned int ai_scans; // number of scans to do + char dma_doublebuf; // we can use double buffring + unsigned int dma_actbuf; // which buffer is used now + sampl_t *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 we may now used for transfer + unsigned int dmabuf_used_size[2]; // which size was trully used + unsigned int dmabuf_panic_size[2]; + 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 + void (*int_ai_func) (comedi_device *, comedi_subdevice *, unsigned short, unsigned int, unsigned short); // ptr to actual interrupt AI function + unsigned char ai16bits; // =1 16 bit card + unsigned char usedma; // =1 use DMA transfer and not INT + unsigned char useeoshandle; // =1 change WAKE_EOS DMA transfer to fit on every second + unsigned char usessh; // =1 turn on S&H support + int softsshdelay; // >0 use software S&H, numer is requested delay in ns + unsigned char softsshsample; // polarity of S&H signal in sample state + unsigned char softsshhold; // polarity of S&H signal in hold state + unsigned int ai_maskerr; // which warning was printed + unsigned int ai_maskharderr; // on which error bits stops + unsigned int ai_inttrig_start; // TRIG_INT for start +} pci9118_private; + +#define devpriv ((pci9118_private *)dev->private) +#define this_board ((boardtype *)dev->board_ptr) + +/* +============================================================================== +*/ + +static int check_channel_list(comedi_device * dev, comedi_subdevice * s, + int n_chan, unsigned int *chanlist, int frontadd, int backadd); +static int setup_channel_list(comedi_device * dev, comedi_subdevice * s, + int n_chan, unsigned int *chanlist, int rot, int frontadd, int backadd, + int usedma, char eoshandle); +static void start_pacer(comedi_device * dev, int mode, unsigned int divisor1, + unsigned int divisor2); +static int pci9118_reset(comedi_device * dev); +static int pci9118_exttrg_add(comedi_device * dev, unsigned char source); +static int pci9118_exttrg_del(comedi_device * dev, unsigned char source); +static int pci9118_ai_cancel(comedi_device * dev, comedi_subdevice * s); +static void pci9118_calc_divisors(char mode, comedi_device * dev, + comedi_subdevice * s, unsigned int *tim1, unsigned int *tim2, + unsigned int flags, int chans, unsigned int *div1, unsigned int *div2, + char usessh, unsigned int chnsshfront); + +/* +============================================================================== +*/ +static 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 (!setup_channel_list(dev, s, 1, &insn->chanspec, 0, 0, 0, 0, 0)) + return -EINVAL; + + outl(0, dev->iobase + PCI9118_DELFIFO); // flush FIFO + + for (n = 0; n < insn->n; n++) { + outw(0, dev->iobase + PCI9118_SOFTTRG); /* start conversion */ + comedi_udelay(2); + timeout = 100; + while (timeout--) { + if (inl(dev->iobase + PCI9118_ADSTAT) & AdStatus_ADrdy) + goto conv_finish; + comedi_udelay(1); + } + + comedi_error(dev, "A/D insn timeout"); + data[n] = 0; + outl(0, dev->iobase + PCI9118_DELFIFO); // flush FIFO + return -ETIME; + + conv_finish: + if (devpriv->ai16bits) { + data[n] = + (inl(dev->iobase + + PCI9118_AD_DATA) & 0xffff) ^ 0x8000; + } else { + data[n] = + (inw(dev->iobase + + PCI9118_AD_DATA) >> 4) & 0xfff; + } + } + + outl(0, dev->iobase + PCI9118_DELFIFO); // flush FIFO + return n; + +} + +/* +============================================================================== +*/ +static int pci9118_insn_write_ao(comedi_device * dev, comedi_subdevice * s, + comedi_insn * insn, lsampl_t * data) +{ + int n, chanreg, ch; + + ch = CR_CHAN(insn->chanspec); + if (ch) { + chanreg = PCI9118_DA2; + } else { + chanreg = PCI9118_DA1; + } + + for (n = 0; n < insn->n; n++) { + outl(data[n], dev->iobase + chanreg); + devpriv->ao_data[ch] = data[n]; + } + + return n; +} + +/* +============================================================================== +*/ +static int pci9118_insn_read_ao(comedi_device * dev, comedi_subdevice * s, + comedi_insn * insn, lsampl_t * data) +{ + int n, chan; + + chan = CR_CHAN(insn->chanspec); + for (n = 0; n < insn->n; n++) + data[n] = devpriv->ao_data[chan]; + + return n; +} + +/* +============================================================================== +*/ +static 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; +} + +/* +============================================================================== +*/ +static 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 & 0x0f, dev->iobase + PCI9118_DO); + } + data[1] = s->state; + + return 2; +} + +/* +============================================================================== +*/ +static void interrupt_pci9118_ai_mode4_switch(comedi_device * dev) +{ + devpriv->AdFunctionReg = + AdFunction_PDTrg | AdFunction_PETrg | AdFunction_AM; + outl(devpriv->AdFunctionReg, dev->iobase + PCI9118_ADFUNC); + outl(0x30, dev->iobase + PCI9118_CNTCTRL); + outl((devpriv->dmabuf_hw[1 - devpriv->dma_actbuf] >> 1) & 0xff, + dev->iobase + PCI9118_CNT0); + outl((devpriv->dmabuf_hw[1 - devpriv->dma_actbuf] >> 9) & 0xff, + dev->iobase + PCI9118_CNT0); + devpriv->AdFunctionReg |= AdFunction_Start; + outl(devpriv->AdFunctionReg, dev->iobase + PCI9118_ADFUNC); +} + +static unsigned int defragment_dma_buffer(comedi_device * dev, + comedi_subdevice * s, sampl_t * dma_buffer, unsigned int num_samples) +{ + unsigned int i = 0, j = 0; + unsigned int start_pos = devpriv->ai_add_front, + stop_pos = devpriv->ai_add_front + devpriv->ai_n_chan; + unsigned int raw_scanlen = devpriv->ai_add_front + devpriv->ai_n_chan + + devpriv->ai_add_back; + + for (i = 0; i < num_samples; i++) { + if (devpriv->ai_act_dmapos >= start_pos && + devpriv->ai_act_dmapos < stop_pos) { + dma_buffer[j++] = dma_buffer[i]; + } + devpriv->ai_act_dmapos++; + devpriv->ai_act_dmapos %= raw_scanlen; + } + + return j; +} + +/* +============================================================================== +*/ +static unsigned int move_block_from_dma(comedi_device * dev, + comedi_subdevice * s, sampl_t * dma_buffer, unsigned int num_samples) +{ + unsigned int num_bytes; + + num_samples = defragment_dma_buffer(dev, s, dma_buffer, num_samples); + devpriv->ai_act_scan += + (s->async->cur_chan + num_samples) / devpriv->ai_n_scanlen; + s->async->cur_chan += num_samples; + s->async->cur_chan %= devpriv->ai_n_scanlen; + num_bytes = + cfc_write_array_to_buffer(s, dma_buffer, + num_samples * sizeof(sampl_t)); + if (num_bytes < num_samples * sizeof(sampl_t)) + return -1; + return 0; +} + +/* +============================================================================== +*/ +static char pci9118_decode_error_status(comedi_device * dev, + comedi_subdevice * s, unsigned char m) +{ + if (m & 0x100) { + comedi_error(dev, "A/D FIFO Full status (Fatal Error!)"); + devpriv->ai_maskerr &= ~0x100L; + } + if (m & 0x008) { + comedi_error(dev, + "A/D Burst Mode Overrun Status (Fatal Error!)"); + devpriv->ai_maskerr &= ~0x008L; + } + if (m & 0x004) { + comedi_error(dev, "A/D Over Speed Status (Warning!)"); + devpriv->ai_maskerr &= ~0x004L; + } + if (m & 0x002) { + comedi_error(dev, "A/D Overrun Status (Fatal Error!)"); + devpriv->ai_maskerr &= ~0x002L; + } + if (m & devpriv->ai_maskharderr) { + s->async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA; + pci9118_ai_cancel(dev, s); + comedi_event(dev, s); + return 1; + } + + return 0; +} + +static void pci9118_ai_munge(comedi_device * dev, comedi_subdevice * s, + void *data, unsigned int num_bytes, unsigned int start_chan_index) +{ + unsigned int i, num_samples = num_bytes / sizeof(sampl_t); + sampl_t *array = data; + + for (i = 0; i < num_samples; i++) { + if (devpriv->usedma) + array[i] = be16_to_cpu(array[i]); + if (devpriv->ai16bits) { + array[i] ^= 0x8000; + } else { + array[i] = (array[i] >> 4) & 0x0fff; + } + } +} + +/* +============================================================================== +*/ +static void interrupt_pci9118_ai_onesample(comedi_device * dev, + comedi_subdevice * s, unsigned short int_adstat, unsigned int int_amcc, + unsigned short int_daq) +{ + register sampl_t sampl; + + s->async->events = 0; + + if (int_adstat & devpriv->ai_maskerr) + if (pci9118_decode_error_status(dev, s, int_adstat)) + return; + + sampl = inw(dev->iobase + PCI9118_AD_DATA); + +#ifdef PCI9118_PARANOIDCHECK + if (devpriv->ai16bits == 0) { + if ((sampl & 0x000f) != devpriv->chanlist[s->async->cur_chan]) { // data dropout! + rt_printk + ("comedi: A/D SAMPL - data dropout: received channel %d, expected %d!\n", + sampl & 0x000f, + devpriv->chanlist[s->async->cur_chan]); + s->async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA; + pci9118_ai_cancel(dev, s); + comedi_event(dev, s); + return; + } + } +#endif + cfc_write_to_buffer(s, sampl); + s->async->cur_chan++; + if (s->async->cur_chan >= devpriv->ai_n_scanlen) { /* one scan done */ + s->async->cur_chan %= devpriv->ai_n_scanlen; + devpriv->ai_act_scan++; + if (!(devpriv->ai_neverending)) + if (devpriv->ai_act_scan >= devpriv->ai_scans) { /* all data sampled */ + pci9118_ai_cancel(dev, s); + s->async->events |= COMEDI_CB_EOA; + } + } + + if (s->async->events) + comedi_event(dev, s); +} + +/* +============================================================================== +*/ +static void interrupt_pci9118_ai_dma(comedi_device * dev, comedi_subdevice * s, + unsigned short int_adstat, unsigned int int_amcc, + unsigned short int_daq) +{ + unsigned int next_dma_buf, samplesinbuf, sampls, m; + + if (int_amcc & MASTER_ABORT_INT) { + comedi_error(dev, "AMCC IRQ - MASTER DMA ABORT!"); + s->async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA; + pci9118_ai_cancel(dev, s); + comedi_event(dev, s); + return; + } + + if (int_amcc & TARGET_ABORT_INT) { + comedi_error(dev, "AMCC IRQ - TARGET DMA ABORT!"); + s->async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA; + pci9118_ai_cancel(dev, s); + comedi_event(dev, s); + return; + } + + if (int_adstat & devpriv->ai_maskerr) +// if (int_adstat & 0x106) + if (pci9118_decode_error_status(dev, s, int_adstat)) + return; + + samplesinbuf = devpriv->dmabuf_use_size[devpriv->dma_actbuf] >> 1; // number of received real samples +// DPRINTK("dma_actbuf=%d\n",devpriv->dma_actbuf); + + 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); + devpriv->dmabuf_used_size[next_dma_buf] = + devpriv->dmabuf_use_size[next_dma_buf]; + if (devpriv->ai_do == 4) + interrupt_pci9118_ai_mode4_switch(dev); + } + + if (samplesinbuf) { + m = devpriv->ai_data_len >> 1; // how many samples is to end of buffer +// DPRINTK("samps=%d m=%d %d %d\n",samplesinbuf,m,s->async->buf_int_count,s->async->buf_int_ptr); + sampls = m; + move_block_from_dma(dev, s, + devpriv->dmabuf_virt[devpriv->dma_actbuf], + samplesinbuf); + m = m - sampls; // m= how many samples was transfered + } +// DPRINTK("YYY\n"); + + if (!devpriv->ai_neverending) + if (devpriv->ai_act_scan >= devpriv->ai_scans) { /* all data sampled */ + pci9118_ai_cancel(dev, s); + s->async->events |= COMEDI_CB_EOA; + } + + 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); + if (devpriv->ai_do == 4) + interrupt_pci9118_ai_mode4_switch(dev); + } + + comedi_event(dev, s); +} + +/* +============================================================================== +*/ +static irqreturn_t interrupt_pci9118(int irq, void *d PT_REGS_ARG) +{ + comedi_device *dev = d; + unsigned int int_daq = 0, int_amcc, int_adstat; + + if (!dev->attached) + return IRQ_NONE; // not fully initialized + + int_daq = inl(dev->iobase + PCI9118_INTSRC) & 0xf; // get IRQ reasons from card + int_amcc = inl(devpriv->iobase_a + AMCC_OP_REG_INTCSR); // get INT register from AMCC chip + +// DPRINTK("INT daq=0x%01x amcc=0x%08x MWAR=0x%08x MWTC=0x%08x ADSTAT=0x%02x ai_do=%d\n", int_daq, int_amcc, inl(devpriv->iobase_a+AMCC_OP_REG_MWAR), inl(devpriv->iobase_a+AMCC_OP_REG_MWTC), inw(dev->iobase+PCI9118_ADSTAT)&0x1ff,devpriv->ai_do); + + if ((!int_daq) && (!(int_amcc & ANY_S593X_INT))) + return IRQ_NONE; // interrupt from other source + + outl(int_amcc | 0x00ff0000, devpriv->iobase_a + AMCC_OP_REG_INTCSR); // shutdown IRQ reasons in AMCC + + int_adstat = inw(dev->iobase + PCI9118_ADSTAT) & 0x1ff; // get STATUS register + + if (devpriv->ai_do) { + if (devpriv->ai12_startstop) + if ((int_adstat & AdStatus_DTH) && (int_daq & Int_DTrg)) { // start stop of measure + if (devpriv->ai12_startstop & START_AI_EXT) { + devpriv->ai12_startstop &= + ~START_AI_EXT; + if (!(devpriv->ai12_startstop & + STOP_AI_EXT)) + pci9118_exttrg_del(dev, EXTTRG_AI); // deactivate EXT trigger + start_pacer(dev, devpriv->ai_do, devpriv->ai_divisor1, devpriv->ai_divisor2); // start pacer + outl(devpriv->AdControlReg, + dev->iobase + PCI9118_ADCNTRL); + } else { + if (devpriv-> + ai12_startstop & STOP_AI_EXT) { + devpriv->ai12_startstop &= + ~STOP_AI_EXT; + pci9118_exttrg_del(dev, EXTTRG_AI); // deactivate EXT trigger + devpriv->ai_neverending = 0; //well, on next interrupt from DMA/EOC measure will stop + } + } + } + + (devpriv->int_ai_func) (dev, dev->subdevices + 0, int_adstat, + int_amcc, int_daq); + + } + return IRQ_HANDLED; +} + +/* +============================================================================== +*/ +static int pci9118_ai_inttrig(comedi_device * dev, comedi_subdevice * s, + unsigned int trignum) +{ + if (trignum != devpriv->ai_inttrig_start) + return -EINVAL; + + devpriv->ai12_startstop &= ~START_AI_INT; + s->async->inttrig = NULL; + + outl(devpriv->IntControlReg, dev->iobase + PCI9118_INTCTRL); + outl(devpriv->AdFunctionReg, dev->iobase + PCI9118_ADFUNC); + if (devpriv->ai_do != 3) { + start_pacer(dev, devpriv->ai_do, devpriv->ai_divisor1, + devpriv->ai_divisor2); + devpriv->AdControlReg |= AdControl_SoftG; + } + outl(devpriv->AdControlReg, dev->iobase + PCI9118_ADCNTRL); + + return 1; +} + +/* +============================================================================== +*/ +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 | TRIG_EXT | TRIG_INT; + if (!cmd->start_src || tmp != cmd->start_src) + err++; + + tmp = cmd->scan_begin_src; + if (devpriv->master) { + cmd->scan_begin_src &= TRIG_TIMER | TRIG_EXT | TRIG_FOLLOW; + } else { + cmd->scan_begin_src &= TRIG_FOLLOW; + } + if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src) + err++; + + tmp = cmd->convert_src; + if (devpriv->master) { + cmd->convert_src &= TRIG_TIMER | TRIG_EXT | TRIG_NOW; + } else { + 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 | TRIG_EXT; + 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->start_src != TRIG_NOW && + cmd->start_src != TRIG_INT && cmd->start_src != TRIG_EXT) { + cmd->start_src = TRIG_NOW; + err++; + } + + if (cmd->scan_begin_src != TRIG_TIMER && + cmd->scan_begin_src != TRIG_EXT && + cmd->scan_begin_src != TRIG_INT && + cmd->scan_begin_src != TRIG_FOLLOW) { + cmd->scan_begin_src = TRIG_FOLLOW; + err++; + } + + if (cmd->convert_src != TRIG_TIMER && + cmd->convert_src != TRIG_EXT && cmd->convert_src != TRIG_NOW) { + cmd->convert_src = TRIG_TIMER; + err++; + } + + if (cmd->scan_end_src != TRIG_COUNT) { + cmd->scan_end_src = TRIG_COUNT; + err++; + } + + if (cmd->stop_src != TRIG_NONE && + cmd->stop_src != TRIG_COUNT && + cmd->stop_src != TRIG_INT && cmd->stop_src != TRIG_EXT) { + cmd->stop_src = TRIG_COUNT; + err++; + } + + if (cmd->start_src == TRIG_EXT && cmd->scan_begin_src == TRIG_EXT) { + cmd->start_src = TRIG_NOW; + err++; + } + + if (cmd->start_src == TRIG_INT && cmd->scan_begin_src == TRIG_INT) { + cmd->start_src = TRIG_NOW; + err++; + } + + if ((cmd->scan_begin_src & (TRIG_TIMER | TRIG_EXT)) && + (!(cmd->convert_src & (TRIG_TIMER | TRIG_NOW)))) { + cmd->convert_src = TRIG_TIMER; + err++; + } + + if ((cmd->scan_begin_src == TRIG_FOLLOW) && + (!(cmd->convert_src & (TRIG_TIMER | TRIG_EXT)))) { + cmd->convert_src = TRIG_TIMER; + err++; + } + + if (cmd->stop_src == TRIG_EXT && cmd->scan_begin_src == TRIG_EXT) { + cmd->stop_src = TRIG_COUNT; + err++; + } + + if (err) + return 2; + + /* step 3: make sure arguments are trivially compatible */ + + if (cmd->start_src & (TRIG_NOW | TRIG_EXT)) + if (cmd->start_arg != 0) { + cmd->start_arg = 0; + err++; + } + + if (cmd->scan_begin_src & (TRIG_FOLLOW | TRIG_EXT)) + if (cmd->scan_begin_arg != 0) { + cmd->scan_begin_arg = 0; + err++; + } + + if ((cmd->scan_begin_src == TRIG_TIMER) && + (cmd->convert_src == TRIG_TIMER) && (cmd->scan_end_arg == 1)) { + cmd->scan_begin_src = TRIG_FOLLOW; + cmd->convert_arg = cmd->scan_begin_arg; + cmd->scan_begin_arg = 0; + } + + if (cmd->scan_begin_src == TRIG_TIMER) + if (cmd->scan_begin_arg < this_board->ai_ns_min) { + cmd->scan_begin_arg = this_board->ai_ns_min; + err++; + } + + if (cmd->scan_begin_src == TRIG_EXT) + if (cmd->scan_begin_arg) { + cmd->scan_begin_arg = 0; + err++; + if (cmd->scan_end_arg > 65535) { + cmd->scan_end_arg = 65535; + err++; + } + } + + if (cmd->convert_src & (TRIG_TIMER | TRIG_NOW)) + if (cmd->convert_arg < this_board->ai_ns_min) { + cmd->convert_arg = this_board->ai_ns_min; + err++; + } + + if (cmd->convert_src == TRIG_EXT) + if (cmd->convert_arg) { + cmd->convert_arg = 0; + 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 (!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->scan_end_arg % cmd->chanlist_len)) { + cmd->scan_end_arg = + cmd->chanlist_len * (cmd->scan_end_arg / + cmd->chanlist_len); + err++; + } + + if (err) + return 3; + + /* step 4: fix up any arguments */ + + if (cmd->scan_begin_src == TRIG_TIMER) { + tmp = cmd->scan_begin_arg; +// rt_printk("S1 timer1=%u timer2=%u\n",cmd->scan_begin_arg,cmd->convert_arg); + i8253_cascade_ns_to_timer(devpriv->i8254_osc_base, &divisor1, + &divisor2, &cmd->scan_begin_arg, + cmd->flags & TRIG_ROUND_MASK); +// rt_printk("S2 timer1=%u timer2=%u\n",cmd->scan_begin_arg,cmd->convert_arg); + if (cmd->scan_begin_arg < this_board->ai_ns_min) + cmd->scan_begin_arg = this_board->ai_ns_min; + if (tmp != cmd->scan_begin_arg) + err++; + } + + if (cmd->convert_src & (TRIG_TIMER | TRIG_NOW)) { + tmp = cmd->convert_arg; + i8253_cascade_ns_to_timer(devpriv->i8254_osc_base, &divisor1, + &divisor2, &cmd->convert_arg, + cmd->flags & TRIG_ROUND_MASK); +// rt_printk("s1 timer1=%u timer2=%u\n",cmd->scan_begin_arg,cmd->convert_arg); + if (cmd->convert_arg < this_board->ai_ns_min) + cmd->convert_arg = this_board->ai_ns_min; + if (tmp != cmd->convert_arg) + err++; + if (cmd->scan_begin_src == TRIG_TIMER + && cmd->convert_src == TRIG_NOW) { + if (cmd->convert_arg == 0) { + if (cmd->scan_begin_arg < + this_board->ai_ns_min * + (cmd->scan_end_arg + 2)) { + cmd->scan_begin_arg = + this_board->ai_ns_min * + (cmd->scan_end_arg + 2); +// rt_printk("s2 timer1=%u timer2=%u\n",cmd->scan_begin_arg,cmd->convert_arg); + err++; + } + } else { + if (cmd->scan_begin_arg < + cmd->convert_arg * cmd->chanlist_len) { + cmd->scan_begin_arg = + cmd->convert_arg * + cmd->chanlist_len; +// rt_printk("s3 timer1=%u timer2=%u\n",cmd->scan_begin_arg,cmd->convert_arg); + err++; + } + } + } + } + + if (err) + return 4; + + if (cmd->chanlist) + if (!check_channel_list(dev, s, cmd->chanlist_len, + cmd->chanlist, 0, 0)) + return 5; // incorrect channels list + + return 0; +} + +/* +============================================================================== +*/ +static int Compute_and_setup_dma(comedi_device * dev) +{ + unsigned int dmalen0, dmalen1, i; + + DPRINTK("adl_pci9118 EDBG: BGN: Compute_and_setup_dma()\n"); + dmalen0 = devpriv->dmabuf_size[0]; + dmalen1 = devpriv->dmabuf_size[1]; + DPRINTK("1 dmalen0=%d dmalen1=%d ai_data_len=%d\n", dmalen0, dmalen1, + devpriv->ai_data_len); + // isn't output buff smaller that our DMA buff? + if (dmalen0 > (devpriv->ai_data_len)) { + dmalen0 = devpriv->ai_data_len & ~3L; // allign to 32bit down + } + if (dmalen1 > (devpriv->ai_data_len)) { + dmalen1 = devpriv->ai_data_len & ~3L; // allign to 32bit down + } + DPRINTK("2 dmalen0=%d dmalen1=%d \n", dmalen0, dmalen1); + + // we want wake up every scan? + if (devpriv->ai_flags & TRIG_WAKE_EOS) { + if (dmalen0 < (devpriv->ai_n_realscanlen << 1)) { + // uff, too short DMA buffer, disable EOS support! + devpriv->ai_flags &= (~TRIG_WAKE_EOS); + rt_printk + ("comedi%d: WAR: DMA0 buf too short, cann't support TRIG_WAKE_EOS (%d<%d)\n", + dev->minor, dmalen0, + devpriv->ai_n_realscanlen << 1); + } else { + // short first DMA buffer to one scan + dmalen0 = devpriv->ai_n_realscanlen << 1; + DPRINTK("21 dmalen0=%d ai_n_realscanlen=%d useeoshandle=%d\n", dmalen0, devpriv->ai_n_realscanlen, devpriv->useeoshandle); + if (devpriv->useeoshandle) + dmalen0 += 2; + if (dmalen0 < 4) { + rt_printk + ("comedi%d: ERR: DMA0 buf len bug? (%d<4)\n", + dev->minor, dmalen0); + dmalen0 = 4; + } + } + } + if (devpriv->ai_flags & TRIG_WAKE_EOS) { + if (dmalen1 < (devpriv->ai_n_realscanlen << 1)) { + // uff, too short DMA buffer, disable EOS support! + devpriv->ai_flags &= (~TRIG_WAKE_EOS); + rt_printk + ("comedi%d: WAR: DMA1 buf too short, cann't support TRIG_WAKE_EOS (%d<%d)\n", + dev->minor, dmalen1, + devpriv->ai_n_realscanlen << 1); + } else { + // short second DMA buffer to one scan + dmalen1 = devpriv->ai_n_realscanlen << 1; + DPRINTK("22 dmalen1=%d ai_n_realscanlen=%d useeoshandle=%d\n", dmalen1, devpriv->ai_n_realscanlen, devpriv->useeoshandle); + if (devpriv->useeoshandle) + dmalen1 -= 2; + if (dmalen1 < 4) { + rt_printk + ("comedi%d: ERR: DMA1 buf len bug? (%d<4)\n", + dev->minor, dmalen1); + dmalen1 = 4; + } + } + } + + DPRINTK("3 dmalen0=%d dmalen1=%d \n", dmalen0, dmalen1); + // transfer without TRIG_WAKE_EOS + if (!(devpriv->ai_flags & TRIG_WAKE_EOS)) { + // if it's possible then allign DMA buffers to length of scan + i = dmalen0; + dmalen0 = + (dmalen0 / (devpriv->ai_n_realscanlen << 1)) * + (devpriv->ai_n_realscanlen << 1); + dmalen0 &= ~3L; + if (!dmalen0) + dmalen0 = i; // uff. very long scan? + i = dmalen1; + dmalen1 = + (dmalen1 / (devpriv->ai_n_realscanlen << 1)) * + (devpriv->ai_n_realscanlen << 1); + dmalen1 &= ~3L; + if (!dmalen1) + dmalen1 = i; // uff. very long scan? + // if measure isn't neverending then test, if it whole fits into one or two DMA buffers + if (!devpriv->ai_neverending) { + // fits whole measure into one DMA buffer? + if (dmalen0 > + ((devpriv->ai_n_realscanlen << 1) * + devpriv->ai_scans)) { + DPRINTK("3.0 ai_n_realscanlen=%d ai_scans=%d \n", devpriv->ai_n_realscanlen, devpriv->ai_scans); + dmalen0 = + (devpriv->ai_n_realscanlen << 1) * + devpriv->ai_scans; + DPRINTK("3.1 dmalen0=%d dmalen1=%d \n", dmalen0, + dmalen1); + dmalen0 &= ~3L; + } else { // fits whole measure into two DMA buffer? + if (dmalen1 > + ((devpriv->ai_n_realscanlen << 1) * + devpriv->ai_scans - dmalen0)) + dmalen1 = + (devpriv-> + ai_n_realscanlen << 1) * + devpriv->ai_scans - dmalen0; + DPRINTK("3.2 dmalen0=%d dmalen1=%d \n", dmalen0, + dmalen1); + dmalen1 &= ~3L; + } + } + } + + DPRINTK("4 dmalen0=%d dmalen1=%d \n", dmalen0, dmalen1); + + // these DMA buffer size we'll be used + devpriv->dma_actbuf = 0; + devpriv->dmabuf_use_size[0] = dmalen0; + devpriv->dmabuf_use_size[1] = dmalen1; + + DPRINTK("5 dmalen0=%d dmalen1=%d \n", dmalen0, dmalen1); +#if 0 + if (devpriv->ai_n_scanlen < this_board->half_fifo_size) { + devpriv->dmabuf_panic_size[0] = + (this_board->half_fifo_size / devpriv->ai_n_scanlen + + 1) * devpriv->ai_n_scanlen * sizeof(sampl_t); + devpriv->dmabuf_panic_size[1] = + (this_board->half_fifo_size / devpriv->ai_n_scanlen + + 1) * devpriv->ai_n_scanlen * sizeof(sampl_t); + } else { + devpriv->dmabuf_panic_size[0] = + (devpriv->ai_n_scanlen << 1) % devpriv->dmabuf_size[0]; + devpriv->dmabuf_panic_size[1] = + (devpriv->ai_n_scanlen << 1) % devpriv->dmabuf_size[1]; + } +#endif + + outl(inl(devpriv->iobase_a + AMCC_OP_REG_MCSR) & (~EN_A2P_TRANSFERS), devpriv->iobase_a + AMCC_OP_REG_MCSR); // stop DMA + 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); + // init DMA transfer + outl(0x00000000 | AINT_WRITE_COMPL, + devpriv->iobase_a + AMCC_OP_REG_INTCSR); +// 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); + outl(inl(devpriv->iobase_a + AMCC_OP_REG_INTCSR) | EN_A2P_TRANSFERS, devpriv->iobase_a + AMCC_OP_REG_INTCSR); // allow bus mastering + + DPRINTK("adl_pci9118 EDBG: END: Compute_and_setup_dma()\n"); + return 0; +} + +/* +============================================================================== +*/ +static int pci9118_ai_docmd_sampl(comedi_device * dev, comedi_subdevice * s) +{ + DPRINTK("adl_pci9118 EDBG: BGN: pci9118_ai_docmd_sampl(%d,) [%d]\n", + dev->minor, devpriv->ai_do); + switch (devpriv->ai_do) { + case 1: + devpriv->AdControlReg |= AdControl_TmrTr; + break; + case 2: + comedi_error(dev, "pci9118_ai_docmd_sampl() mode 2 bug!\n"); + return -EIO; + case 3: + devpriv->AdControlReg |= AdControl_ExtM; + break; + case 4: + comedi_error(dev, "pci9118_ai_docmd_sampl() mode 4 bug!\n"); + return -EIO; + default: + comedi_error(dev, + "pci9118_ai_docmd_sampl() mode number bug!\n"); + return -EIO; + }; + + devpriv->int_ai_func = interrupt_pci9118_ai_onesample; //transfer function + + if (devpriv->ai12_startstop) + pci9118_exttrg_add(dev, EXTTRG_AI); // activate EXT trigger + + if ((devpriv->ai_do == 1) || (devpriv->ai_do == 2)) + devpriv->IntControlReg |= Int_Timer; + + devpriv->AdControlReg |= AdControl_Int; + + outl(inl(devpriv->iobase_a + AMCC_OP_REG_INTCSR) | 0x1f00, devpriv->iobase_a + AMCC_OP_REG_INTCSR); // allow INT in AMCC + + if (!(devpriv->ai12_startstop & (START_AI_EXT | START_AI_INT))) { + outl(devpriv->IntControlReg, dev->iobase + PCI9118_INTCTRL); + outl(devpriv->AdFunctionReg, dev->iobase + PCI9118_ADFUNC); + if (devpriv->ai_do != 3) { + start_pacer(dev, devpriv->ai_do, devpriv->ai_divisor1, + devpriv->ai_divisor2); + devpriv->AdControlReg |= AdControl_SoftG; + } + outl(devpriv->IntControlReg, dev->iobase + PCI9118_INTCTRL); + } + + DPRINTK("adl_pci9118 EDBG: END: pci9118_ai_docmd_sampl()\n"); + return 0; +} + +/* +============================================================================== +*/ +static int pci9118_ai_docmd_dma(comedi_device * dev, comedi_subdevice * s) +{ + DPRINTK("adl_pci9118 EDBG: BGN: pci9118_ai_docmd_dma(%d,) [%d,%d]\n", + dev->minor, devpriv->ai_do, devpriv->usedma); + Compute_and_setup_dma(dev); + + switch (devpriv->ai_do) { + case 1: + devpriv->AdControlReg |= + ((AdControl_TmrTr | AdControl_Dma) & 0xff); + break; + case 2: + devpriv->AdControlReg |= + ((AdControl_TmrTr | AdControl_Dma) & 0xff); + devpriv->AdFunctionReg = + AdFunction_PDTrg | AdFunction_PETrg | AdFunction_BM | + AdFunction_BS; + if (devpriv->usessh && (!devpriv->softsshdelay)) + devpriv->AdFunctionReg |= AdFunction_BSSH; + outl(devpriv->ai_n_realscanlen, dev->iobase + PCI9118_BURST); + break; + case 3: + devpriv->AdControlReg |= + ((AdControl_ExtM | AdControl_Dma) & 0xff); + devpriv->AdFunctionReg = AdFunction_PDTrg | AdFunction_PETrg; + break; + case 4: + devpriv->AdControlReg |= + ((AdControl_TmrTr | AdControl_Dma) & 0xff); + devpriv->AdFunctionReg = + AdFunction_PDTrg | AdFunction_PETrg | AdFunction_AM; + outl(devpriv->AdFunctionReg, dev->iobase + PCI9118_ADFUNC); + outl(0x30, dev->iobase + PCI9118_CNTCTRL); + outl((devpriv->dmabuf_hw[0] >> 1) & 0xff, + dev->iobase + PCI9118_CNT0); + outl((devpriv->dmabuf_hw[0] >> 9) & 0xff, + dev->iobase + PCI9118_CNT0); + devpriv->AdFunctionReg |= AdFunction_Start; + break; + default: + comedi_error(dev, "pci9118_ai_docmd_dma() mode number bug!\n"); + return -EIO; + }; + + if (devpriv->ai12_startstop) { + pci9118_exttrg_add(dev, EXTTRG_AI); // activate EXT trigger + } + + devpriv->int_ai_func = interrupt_pci9118_ai_dma; //transfer function + + outl(0x02000000 | AINT_WRITE_COMPL, + devpriv->iobase_a + AMCC_OP_REG_INTCSR); + + if (!(devpriv->ai12_startstop & (START_AI_EXT | START_AI_INT))) { + outl(devpriv->AdFunctionReg, dev->iobase + PCI9118_ADFUNC); + outl(devpriv->IntControlReg, dev->iobase + PCI9118_INTCTRL); + if (devpriv->ai_do != 3) { + start_pacer(dev, devpriv->ai_do, devpriv->ai_divisor1, + devpriv->ai_divisor2); + devpriv->AdControlReg |= AdControl_SoftG; + } + outl(devpriv->AdControlReg, dev->iobase + PCI9118_ADCNTRL); + } + + DPRINTK("adl_pci9118 EDBG: BGN: pci9118_ai_docmd_dma()\n"); + return 0; +} + +/* +============================================================================== +*/ +static int pci9118_ai_cmd(comedi_device * dev, comedi_subdevice * s) +{ + comedi_cmd *cmd = &s->async->cmd; + unsigned int addchans = 0; + int ret = 0; + + DPRINTK("adl_pci9118 EDBG: BGN: pci9118_ai_cmd(%d,)\n", dev->minor); + devpriv->ai12_startstop = 0; + devpriv->ai_flags = cmd->flags; + devpriv->ai_n_chan = cmd->chanlist_len; + devpriv->ai_n_scanlen = cmd->scan_end_arg; + devpriv->ai_chanlist = cmd->chanlist; + devpriv->ai_data = s->async->prealloc_buf; + devpriv->ai_data_len = s->async->prealloc_bufsz; + devpriv->ai_timer1 = 0; + devpriv->ai_timer2 = 0; + devpriv->ai_add_front = 0; + devpriv->ai_add_back = 0; + devpriv->ai_maskerr = 0x10e; + + // prepare for start/stop conditions + if (cmd->start_src == TRIG_EXT) + devpriv->ai12_startstop |= START_AI_EXT; + if (cmd->stop_src == TRIG_EXT) { + devpriv->ai_neverending = 1; + devpriv->ai12_startstop |= STOP_AI_EXT; + } + if (cmd->start_src == TRIG_INT) { + devpriv->ai12_startstop |= START_AI_INT; + devpriv->ai_inttrig_start = cmd->start_arg; + s->async->inttrig = pci9118_ai_inttrig; + } +#if 0 + if (cmd->stop_src == TRIG_INT) { + devpriv->ai_neverending = 1; + devpriv->ai12_startstop |= STOP_AI_INT; + } +#endif + if (cmd->stop_src == TRIG_NONE) + devpriv->ai_neverending = 1; + if (cmd->stop_src == TRIG_COUNT) { + devpriv->ai_scans = cmd->stop_arg; + devpriv->ai_neverending = 0; + } else { + devpriv->ai_scans = 0; + } + + // use sample&hold signal? + if (cmd->convert_src == TRIG_NOW) { + devpriv->usessh = 1; + } // yes + else { + devpriv->usessh = 0; + } // no + + DPRINTK("1 neverending=%d scans=%u usessh=%d ai_startstop=0x%2x\n", + devpriv->ai_neverending, devpriv->ai_scans, devpriv->usessh, + devpriv->ai12_startstop); + + // use additional sample at end of every scan to satisty DMA 32 bit transfer? + devpriv->ai_add_front = 0; + devpriv->ai_add_back = 0; + devpriv->useeoshandle = 0; + if (devpriv->master) { + devpriv->usedma = 1; + if ((cmd->flags & TRIG_WAKE_EOS) && + (devpriv->ai_n_scanlen == 1)) { + if (cmd->convert_src == TRIG_NOW) { + devpriv->ai_add_back = 1; + } + if (cmd->convert_src == TRIG_TIMER) { + devpriv->usedma = 0; // use INT transfer if scanlist have only one channel + } + } + if ((cmd->flags & TRIG_WAKE_EOS) && + (devpriv->ai_n_scanlen & 1) && + (devpriv->ai_n_scanlen > 1)) { + if (cmd->scan_begin_src == TRIG_FOLLOW) { + //vpriv->useeoshandle=1; // change DMA transfer block to fit EOS on every second call + devpriv->usedma = 0; // XXX maybe can be corrected to use 16 bit DMA + } else { // well, we must insert one sample to end of EOS to meet 32 bit transfer + devpriv->ai_add_back = 1; + } + } + } else { // interrupt transfer don't need any correction + devpriv->usedma = 0; + } + + // we need software S&H signal? It add two samples before every scan as minimum + if (devpriv->usessh && devpriv->softsshdelay) { + devpriv->ai_add_front = 2; + if ((devpriv->usedma == 1) && (devpriv->ai_add_back == 1)) { // move it to front + devpriv->ai_add_front++; + devpriv->ai_add_back = 0; + } + if (cmd->convert_arg < this_board->ai_ns_min) + cmd->convert_arg = this_board->ai_ns_min; + addchans = devpriv->softsshdelay / cmd->convert_arg; + if (devpriv->softsshdelay % cmd->convert_arg) + addchans++; + if (addchans > (devpriv->ai_add_front - 1)) { // uff, still short :-( + devpriv->ai_add_front = addchans + 1; + if (devpriv->usedma == 1) + if ((devpriv->ai_add_front + + devpriv->ai_n_chan + + devpriv->ai_add_back) & 1) + devpriv->ai_add_front++; // round up to 32 bit + } + } // well, we now know what must be all added + + devpriv->ai_n_realscanlen = // what we must take from card in real to have ai_n_scanlen on output? + (devpriv->ai_add_front + devpriv->ai_n_chan + + devpriv->ai_add_back) * (devpriv->ai_n_scanlen / + devpriv->ai_n_chan); + + DPRINTK("2 usedma=%d realscan=%d af=%u n_chan=%d ab=%d n_scanlen=%d\n", + devpriv->usedma, + devpriv->ai_n_realscanlen, devpriv->ai_add_front, + devpriv->ai_n_chan, devpriv->ai_add_back, + devpriv->ai_n_scanlen); + + // check and setup channel list + if (!check_channel_list(dev, s, devpriv->ai_n_chan, + devpriv->ai_chanlist, devpriv->ai_add_front, + devpriv->ai_add_back)) + return -EINVAL; + if (!setup_channel_list(dev, s, devpriv->ai_n_chan, + devpriv->ai_chanlist, 0, devpriv->ai_add_front, + devpriv->ai_add_back, devpriv->usedma, + devpriv->useeoshandle)) + return -EINVAL; + + // compute timers settings + // simplest way, fr=4Mhz/(tim1*tim2), channel manipulation without timers effect + if (((cmd->scan_begin_src == TRIG_FOLLOW) || (cmd->scan_begin_src == TRIG_EXT) || (cmd->scan_begin_src == TRIG_INT)) && (cmd->convert_src == TRIG_TIMER)) { // both timer is used for one time + if (cmd->scan_begin_src == TRIG_EXT) { + devpriv->ai_do = 4; + } else { + devpriv->ai_do = 1; + } + pci9118_calc_divisors(devpriv->ai_do, dev, s, + &cmd->scan_begin_arg, &cmd->convert_arg, + devpriv->ai_flags, devpriv->ai_n_realscanlen, + &devpriv->ai_divisor1, &devpriv->ai_divisor2, + devpriv->usessh, devpriv->ai_add_front); + devpriv->ai_timer2 = cmd->convert_arg; + } + + if ((cmd->scan_begin_src == TRIG_TIMER) && ((cmd->convert_src == TRIG_TIMER) || (cmd->convert_src == TRIG_NOW))) { // double timed action + if (!devpriv->usedma) { + comedi_error(dev, + "cmd->scan_begin_src=TRIG_TIMER works only with bus mastering!"); + return -EIO; + } + + devpriv->ai_do = 2; + pci9118_calc_divisors(devpriv->ai_do, dev, s, + &cmd->scan_begin_arg, &cmd->convert_arg, + devpriv->ai_flags, devpriv->ai_n_realscanlen, + &devpriv->ai_divisor1, &devpriv->ai_divisor2, + devpriv->usessh, devpriv->ai_add_front); + devpriv->ai_timer1 = cmd->scan_begin_arg; + devpriv->ai_timer2 = cmd->convert_arg; + } + + if ((cmd->scan_begin_src == TRIG_FOLLOW) + && (cmd->convert_src == TRIG_EXT)) { + devpriv->ai_do = 3; + } + + start_pacer(dev, -1, 0, 0); // stop pacer + + devpriv->AdControlReg = 0; // bipolar, S.E., use 8254, stop 8354, internal trigger, soft trigger, disable DMA + 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); + comedi_udelay(1); + outl(0, dev->iobase + PCI9118_DELFIFO); // flush FIFO + inl(dev->iobase + PCI9118_ADSTAT); // flush A/D and INT status register + inl(dev->iobase + PCI9118_INTSRC); + + devpriv->ai_act_scan = 0; + devpriv->ai_act_dmapos = 0; + s->async->cur_chan = 0; + devpriv->ai_buf_ptr = 0; + + if (devpriv->usedma) { + ret = pci9118_ai_docmd_dma(dev, s); + } else { + ret = pci9118_ai_docmd_sampl(dev, s); + } + + DPRINTK("adl_pci9118 EDBG: END: pci9118_ai_cmd()\n"); + return ret; +} + +/* +============================================================================== +*/ +static int check_channel_list(comedi_device * dev, comedi_subdevice * s, + int n_chan, unsigned int *chanlist, int frontadd, int backadd) +{ + unsigned int i, differencial = 0, bipolar = 0; + + /* 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 ((frontadd + n_chan + backadd) > s->len_chanlist) { + rt_printk + ("comedi%d: range/channel list is too long for actual configuration (%d>%d)!", + dev->minor, n_chan, + s->len_chanlist - frontadd - backadd); + return 0; + } + + if (CR_AREF(chanlist[0]) == AREF_DIFF) + differencial = 1; // all input must be diff + if (CR_RANGE(chanlist[0]) < PCI9118_BIPOLAR_RANGES) + bipolar = 1; // all input must be bipolar + if (n_chan > 1) + for (i = 1; i < n_chan; i++) { // check S.E/diff + if ((CR_AREF(chanlist[i]) == AREF_DIFF) != + (differencial)) { + comedi_error(dev, + "Differencial and single ended inputs cann't be mixtured!"); + return 0; + } + if ((CR_RANGE(chanlist[i]) < PCI9118_BIPOLAR_RANGES) != + (bipolar)) { + comedi_error(dev, + "Bipolar and unipolar ranges cann't be mixtured!"); + return 0; + } + if ((!devpriv->usemux) & (differencial) & + (CR_CHAN(chanlist[i]) >= + this_board->n_aichand)) { + comedi_error(dev, + "If AREF_DIFF is used then is available only first 8 channels!"); + return 0; + } + } + + return 1; +} + +/* +============================================================================== +*/ +static int setup_channel_list(comedi_device * dev, comedi_subdevice * s, + int n_chan, unsigned int *chanlist, int rot, int frontadd, int backadd, + int usedma, char useeos) +{ + unsigned int i, differencial = 0, bipolar = 0; + unsigned int scanquad, gain, ssh = 0x00; + + DPRINTK("adl_pci9118 EDBG: BGN: setup_channel_list(%d,.,%d,.,%d,%d,%d,%d)\n", dev->minor, n_chan, rot, frontadd, backadd, usedma); + + if (usedma == 1) { + rot = 8; + usedma = 0; + } + + if (CR_AREF(chanlist[0]) == AREF_DIFF) + differencial = 1; // all input must be diff + if (CR_RANGE(chanlist[0]) < PCI9118_BIPOLAR_RANGES) + bipolar = 1; // all input must be bipolar + + // All is ok, so we can setup channel/range list + + if (!bipolar) { + devpriv->AdControlReg |= AdControl_UniP; // set unibipolar + } else { + devpriv->AdControlReg &= ((~AdControl_UniP) & 0xff); // enable bipolar + } + + if (differencial) { + devpriv->AdControlReg |= AdControl_Diff; // 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! + outl(0, dev->iobase + PCI9118_SCANMOD); + outl(1, dev->iobase + PCI9118_SCANMOD); + +#ifdef PCI9118_PARANOIDCHECK + devpriv->chanlistlen = n_chan; + for (i = 0; i < (PCI9118_CHANLEN + 1); i++) + devpriv->chanlist[i] = 0x55aa; +#endif + + if (frontadd) { // insert channels for S&H + ssh = devpriv->softsshsample; + DPRINTK("FA: %04x: ", ssh); + for (i = 0; i < frontadd; i++) { // store range list to card + scanquad = CR_CHAN(chanlist[0]); // get channel number; + gain = CR_RANGE(chanlist[0]); // get gain number + scanquad |= ((gain & 0x03) << 8); + outl(scanquad | ssh, dev->iobase + PCI9118_GAIN); + DPRINTK("%02x ", scanquad | ssh); + ssh = devpriv->softsshhold; + } + DPRINTK("\n "); + } + + DPRINTK("SL: ", ssh); + for (i = 0; i < n_chan; i++) { // store range list to card + scanquad = CR_CHAN(chanlist[i]); // get channel number; +#ifdef PCI9118_PARANOIDCHECK + devpriv->chanlist[i ^ usedma] = (scanquad & 0xf) << rot; +#endif + gain = CR_RANGE(chanlist[i]); // get gain number + scanquad |= ((gain & 0x03) << 8); + outl(scanquad | ssh, dev->iobase + PCI9118_GAIN); + DPRINTK("%02x ", scanquad | ssh); + } + DPRINTK("\n "); + + if (backadd) { // insert channels for fit onto 32bit DMA + DPRINTK("BA: %04x: ", ssh); + for (i = 0; i < backadd; i++) { // store range list to card + scanquad = CR_CHAN(chanlist[0]); // get channel number; + gain = CR_RANGE(chanlist[0]); // get gain number + scanquad |= ((gain & 0x03) << 8); + outl(scanquad | ssh, dev->iobase + PCI9118_GAIN); + DPRINTK("%02x ", scanquad | ssh); + } + DPRINTK("\n "); + } +#ifdef PCI9118_PARANOIDCHECK + devpriv->chanlist[n_chan ^ usedma] = devpriv->chanlist[0 ^ usedma]; // for 32bit oerations + if (useeos) { + for (i = 1; i < n_chan; i++) { // store range list to card + devpriv->chanlist[(n_chan + i) ^ usedma] = + (CR_CHAN(chanlist[i]) & 0xf) << rot; + } + devpriv->chanlist[(2 * n_chan) ^ usedma] = devpriv->chanlist[0 ^ usedma]; // for 32bit oerations + useeos = 2; + } else { + useeos = 1; + } +#ifdef PCI9118_EXTDEBUG + DPRINTK("CHL: "); + for (i = 0; i <= (useeos * n_chan); i++) { + DPRINTK("%04x ", devpriv->chanlist[i]); + } + DPRINTK("\n "); +#endif +#endif + outl(0, dev->iobase + PCI9118_SCANMOD); // close scan queue +// comedi_udelay(100); // important delay, or first sample will be cripled + + DPRINTK("adl_pci9118 EDBG: END: setup_channel_list()\n"); + return 1; // we can serve this with scan logic +} + +/* +============================================================================== + calculate 8254 divisors if they are used for dual timing +*/ +static void pci9118_calc_divisors(char mode, comedi_device * dev, + comedi_subdevice * s, unsigned int *tim1, unsigned int *tim2, + unsigned int flags, int chans, unsigned int *div1, unsigned int *div2, + char usessh, unsigned int chnsshfront) +{ + DPRINTK("adl_pci9118 EDBG: BGN: pci9118_calc_divisors(%d,%d,.,%u,%u,%u,%d,.,.,,%u,%u)\n", mode, dev->minor, *tim1, *tim2, flags, chans, usessh, chnsshfront); + switch (mode) { + case 1: + case 4: + if (*tim2 < this_board->ai_ns_min) + *tim2 = this_board->ai_ns_min; + i8253_cascade_ns_to_timer(devpriv->i8254_osc_base, div1, div2, + tim2, flags & TRIG_ROUND_NEAREST); + DPRINTK("OSC base=%u div1=%u div2=%u timer1=%u\n", + devpriv->i8254_osc_base, *div1, *div2, *tim1); + break; + case 2: + if (*tim2 < this_board->ai_ns_min) + *tim2 = this_board->ai_ns_min; + DPRINTK("1 div1=%u div2=%u timer1=%u timer2=%u\n", *div1, *div2, + *tim1, *tim2); + *div1 = *tim2 / devpriv->i8254_osc_base; // convert timer (burst) + DPRINTK("2 div1=%u div2=%u timer1=%u timer2=%u\n", *div1, *div2, + *tim1, *tim2); + if (*div1 < this_board->ai_pacer_min) + *div1 = this_board->ai_pacer_min; + DPRINTK("3 div1=%u div2=%u timer1=%u timer2=%u\n", *div1, *div2, + *tim1, *tim2); + *div2 = *tim1 / devpriv->i8254_osc_base; // scan timer + DPRINTK("4 div1=%u div2=%u timer1=%u timer2=%u\n", *div1, *div2, + *tim1, *tim2); + *div2 = *div2 / *div1; // major timer is c1*c2 + DPRINTK("5 div1=%u div2=%u timer1=%u timer2=%u\n", *div1, *div2, + *tim1, *tim2); + if (*div2 < chans) + *div2 = chans; + DPRINTK("6 div1=%u div2=%u timer1=%u timer2=%u\n", *div1, *div2, + *tim1, *tim2); + + *tim2 = *div1 * devpriv->i8254_osc_base; // real convert timer + + if (usessh & (chnsshfront == 0)) // use BSSH signal + if (*div2 < (chans + 2)) + *div2 = chans + 2; + + DPRINTK("7 div1=%u div2=%u timer1=%u timer2=%u\n", *div1, *div2, + *tim1, *tim2); + *tim1 = *div1 * *div2 * devpriv->i8254_osc_base; + DPRINTK("OSC base=%u div1=%u div2=%u timer1=%u timer2=%u\n", + devpriv->i8254_osc_base, *div1, *div2, *tim1, *tim2); + break; + } + DPRINTK("adl_pci9118 EDBG: END: pci9118_calc_divisors(%u,%u)\n", + *div1, *div2); +} + +/* +============================================================================== +*/ +static void start_pacer(comedi_device * dev, int mode, unsigned int divisor1, + unsigned int divisor2) +{ + outl(0x74, dev->iobase + PCI9118_CNTCTRL); + outl(0xb4, dev->iobase + PCI9118_CNTCTRL); +// outl(0x30, dev->iobase + PCI9118_CNTCTRL); + comedi_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); + } +} + +/* +============================================================================== +*/ +static int pci9118_exttrg_add(comedi_device * dev, unsigned char source) +{ + if (source > 3) + return -1; // incorrect source + devpriv->exttrg_users |= (1 << source); + devpriv->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; +} + +/* +============================================================================== +*/ +static int pci9118_exttrg_del(comedi_device * dev, unsigned char source) +{ + if (source > 3) + return -1; // incorrect source + devpriv->exttrg_users &= ~(1 << source); + if (!devpriv->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; +} + +/* +============================================================================== +*/ +static int pci9118_ai_cancel(comedi_device * dev, comedi_subdevice * s) +{ + if (devpriv->usedma) + 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, EXTTRG_AI); + 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 = 0x00; + 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->usedma = 0; + + devpriv->ai_act_scan = 0; + devpriv->ai_act_dmapos = 0; + s->async->cur_chan = 0; + s->async->inttrig = NULL; + devpriv->ai_buf_ptr = 0; + devpriv->ai_neverending = 0; + devpriv->dma_actbuf = 0; + + if (!devpriv->IntControlReg) + outl(inl(devpriv->iobase_a + AMCC_OP_REG_INTCSR) | 0x1f00, devpriv->iobase_a + AMCC_OP_REG_INTCSR); // allow INT in AMCC + + 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(0x30, dev->iobase + PCI9118_CNTCTRL); +// 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 + + devpriv->ao_data[0] = 2047; + devpriv->ao_data[1] = 2047; + outl(devpriv->ao_data[0], dev->iobase + PCI9118_DA1); // reset A/D outs to 0V + outl(devpriv->ao_data[1], dev->iobase + PCI9118_DA2); + outl(0, dev->iobase + PCI9118_DO); // reset digi outs to L + comedi_udelay(10); + inl(dev->iobase + PCI9118_AD_DATA); + outl(0, dev->iobase + PCI9118_DELFIFO); // flush FIFO + outl(0, dev->iobase + PCI9118_INTSRC); // remove INT requests + inl(dev->iobase + PCI9118_ADSTAT); // flush A/D status register + inl(dev->iobase + PCI9118_INTSRC); // flush INT requests + 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 + + 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 short master; + unsigned int irq; + unsigned long iobase_a, iobase_9; + struct pci_dev *pcidev; + int opt_bus, opt_slot; + const char *errstr; + unsigned char pci_bus, pci_slot, pci_func; + u16 u16w; + + rt_printk("comedi%d: adl_pci9118: board=%s", dev->minor, + this_board->name); + + opt_bus = it->options[0]; + opt_slot = it->options[1]; + if (it->options[3] & 1) { + master = 0; // user don't want use bus master + } else { + master = 1; + } + + if ((ret = alloc_private(dev, sizeof(pci9118_private))) < 0) { + rt_printk(" - Allocation failed!\n"); + return -ENOMEM; + } + + /* Look for matching PCI device */ + errstr = "not found!"; + pcidev = NULL; + while (NULL != (pcidev = pci_get_device(PCI_VENDOR_ID_AMCC, + this_board->device_id, pcidev))) { + /* Found matching vendor/device. */ + if (opt_bus || opt_slot) { + /* Check bus/slot. */ + if (opt_bus != pcidev->bus->number + || opt_slot != PCI_SLOT(pcidev->devfn)) + continue; /* no match */ + } + /* + * Look for device that isn't in use. + * Enable PCI device and request regions. + */ + if (comedi_pci_enable(pcidev, "adl_pci9118")) { + errstr = "failed to enable PCI device and request regions!"; + continue; + } + break; + } + + if (!pcidev) { + if (opt_bus || opt_slot) { + rt_printk(" - Card at b:s %d:%d %s\n", + opt_bus, opt_slot, errstr); + } else { + rt_printk(" - Card %s\n", errstr); + } + return -EIO; + } + + if (master) { + pci_set_master(pcidev); + } + + pci_bus = pcidev->bus->number; + pci_slot = PCI_SLOT(pcidev->devfn); + pci_func = PCI_FUNC(pcidev->devfn); + irq = pcidev->irq; + iobase_a = pci_resource_start(pcidev, 0); + iobase_9 = pci_resource_start(pcidev, 2); + + rt_printk(", b:s:f=%d:%d:%d, io=0x%4lx, 0x%4lx", pci_bus, pci_slot, + pci_func, iobase_9, iobase_a); + + dev->iobase = iobase_9; + dev->board_name = this_board->name; + + devpriv->pcidev = pcidev; + devpriv->iobase_a = iobase_a; + + pci9118_reset(dev); + + if (it->options[3] & 2) + irq = 0; // user don't want use IRQ + if (irq > 0) { + if (comedi_request_irq(irq, interrupt_pci9118, IRQF_SHARED, + "ADLink PCI-9118", dev)) { + rt_printk(", unable to allocate IRQ %d, DISABLING IT", + irq); + irq = 0; /* Can't use IRQ */ + } else { + rt_printk(", irq=%u", 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] = (sampl_t *) + __get_free_pages(GFP_KERNEL, + pages))) + 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 = 1; + + } + + 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[4] > 0) + if (devpriv->usemux > 128) { + devpriv->usemux = 128; // max 128 channels with softare S&H! + } + rt_printk(", ext. mux %d channels", devpriv->usemux); + } + + devpriv->softsshdelay = it->options[4]; + if (devpriv->softsshdelay < 0) { // select sample&hold signal polarity + devpriv->softsshdelay = -devpriv->softsshdelay; + devpriv->softsshsample = 0x80; + devpriv->softsshhold = 0x00; + } else { + devpriv->softsshsample = 0x00; + devpriv->softsshhold = 0x80; + } + + rt_printk(".\n"); + + pci_read_config_word(devpriv->pcidev, PCI_COMMAND, &u16w); + pci_write_config_word(devpriv->pcidev, PCI_COMMAND, u16w | 64); // Enable parity check for parity error + + if ((ret = alloc_subdevices(dev, 4)) < 0) + return ret; + + s = dev->subdevices + 0; + dev->read_subdev = s; + s->type = COMEDI_SUBD_AI; + s->subdev_flags = SDF_READABLE | 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; + s->insn_read = pci9118_insn_read_ai; + if (dev->irq) { + s->subdev_flags |= SDF_CMD_READ; + s->do_cmdtest = pci9118_ai_cmdtest; + s->do_cmd = pci9118_ai_cmd; + s->munge = pci9118_ai_munge; + } + + s = dev->subdevices + 1; + s->type = COMEDI_SUBD_AO; + s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON; + 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; + s->insn_write = pci9118_insn_write_ao; + s->insn_read = pci9118_insn_read_ao; + + s = dev->subdevices + 2; + s->type = COMEDI_SUBD_DI; + s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_COMMON; + s->n_chan = 4; + s->maxdata = 1; + s->len_chanlist = 4; + s->range_table = &range_digital; + s->io_bits = 0; /* all bits input */ + s->insn_bits = pci9118_insn_bits_di; + + s = dev->subdevices + 3; + s->type = COMEDI_SUBD_DO; + s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON; + s->n_chan = 4; + s->maxdata = 1; + s->len_chanlist = 4; + s->range_table = &range_digital; + s->io_bits = 0xf; /* all bits output */ + s->insn_bits = pci9118_insn_bits_do; + + devpriv->valid = 1; + devpriv->i8254_osc_base = 250; // 250ns=4MHz + devpriv->ai_maskharderr = 0x10a; // default measure crash condition + if (it->options[5]) // disable some requested + devpriv->ai_maskharderr &= ~it->options[5]; + + switch (this_board->ai_maxdata) { + case 0xffff: + devpriv->ai16bits = 1; + break; + default: + devpriv->ai16bits = 0; + break; + } + return 0; +} + +/* +============================================================================== +*/ +static int pci9118_detach(comedi_device * dev) +{ + if (dev->private) { + if (devpriv->valid) + pci9118_reset(dev); + if (dev->irq) + comedi_free_irq(dev->irq, dev); + if (devpriv->pcidev) { + if (dev->iobase) { + comedi_pci_disable(devpriv->pcidev); + } + pci_dev_put(devpriv->pcidev); + } + if (devpriv->dmabuf_virt[0]) + free_pages((unsigned long)devpriv->dmabuf_virt[0], + devpriv->dmabuf_pages[0]); + if (devpriv->dmabuf_virt[1]) + free_pages((unsigned long)devpriv->dmabuf_virt[1], + devpriv->dmabuf_pages[1]); + } + + return 0; +} + +/* +============================================================================== +*/