diff options
Diffstat (limited to 'drivers/staging/comedi/drivers/adl_pci9118.c')
-rw-r--r-- | drivers/staging/comedi/drivers/adl_pci9118.c | 289 |
1 files changed, 132 insertions, 157 deletions
diff --git a/drivers/staging/comedi/drivers/adl_pci9118.c b/drivers/staging/comedi/drivers/adl_pci9118.c index fb3043dcfff1..0dff1dbb53fb 100644 --- a/drivers/staging/comedi/drivers/adl_pci9118.c +++ b/drivers/staging/comedi/drivers/adl_pci9118.c @@ -83,12 +83,6 @@ #include "amcc_s5933.h" #include "comedi_8254.h" -#define IORANGE_9118 64 /* I hope */ -#define PCI9118_CHANLEN 255 /* - * len of chanlist, some source say 256, - * but reality looks like 255 :-( - */ - /* * PCI BAR2 Register map (dev->iobase) */ @@ -96,24 +90,24 @@ #define PCI9118_AI_FIFO_REG 0x10 #define PCI9118_AO_REG(x) (0x10 + ((x) * 4)) #define PCI9118_AI_STATUS_REG 0x18 -#define PCI9118_AI_STATUS_NFULL (1 << 8) /* 0=FIFO full (fatal) */ -#define PCI9118_AI_STATUS_NHFULL (1 << 7) /* 0=FIFO half full */ -#define PCI9118_AI_STATUS_NEPTY (1 << 6) /* 0=FIFO empty */ -#define PCI9118_AI_STATUS_ACMP (1 << 5) /* 1=about trigger complete */ -#define PCI9118_AI_STATUS_DTH (1 << 4) /* 1=ext. digital trigger */ -#define PCI9118_AI_STATUS_BOVER (1 << 3) /* 1=burst overrun (fatal) */ -#define PCI9118_AI_STATUS_ADOS (1 << 2) /* 1=A/D over speed (warn) */ -#define PCI9118_AI_STATUS_ADOR (1 << 1) /* 1=A/D overrun (fatal) */ -#define PCI9118_AI_STATUS_ADRDY (1 << 0) /* 1=A/D ready */ +#define PCI9118_AI_STATUS_NFULL BIT(8) /* 0=FIFO full (fatal) */ +#define PCI9118_AI_STATUS_NHFULL BIT(7) /* 0=FIFO half full */ +#define PCI9118_AI_STATUS_NEPTY BIT(6) /* 0=FIFO empty */ +#define PCI9118_AI_STATUS_ACMP BIT(5) /* 1=about trigger complete */ +#define PCI9118_AI_STATUS_DTH BIT(4) /* 1=ext. digital trigger */ +#define PCI9118_AI_STATUS_BOVER BIT(3) /* 1=burst overrun (fatal) */ +#define PCI9118_AI_STATUS_ADOS BIT(2) /* 1=A/D over speed (warn) */ +#define PCI9118_AI_STATUS_ADOR BIT(1) /* 1=A/D overrun (fatal) */ +#define PCI9118_AI_STATUS_ADRDY BIT(0) /* 1=A/D ready */ #define PCI9118_AI_CTRL_REG 0x18 -#define PCI9118_AI_CTRL_UNIP (1 << 7) /* 1=unipolar */ -#define PCI9118_AI_CTRL_DIFF (1 << 6) /* 1=differential inputs */ -#define PCI9118_AI_CTRL_SOFTG (1 << 5) /* 1=8254 software gate */ -#define PCI9118_AI_CTRL_EXTG (1 << 4) /* 1=8254 TGIN(pin 46) gate */ -#define PCI9118_AI_CTRL_EXTM (1 << 3) /* 1=ext. trigger (pin 44) */ -#define PCI9118_AI_CTRL_TMRTR (1 << 2) /* 1=8254 is trigger source */ -#define PCI9118_AI_CTRL_INT (1 << 1) /* 1=enable interrupt */ -#define PCI9118_AI_CTRL_DMA (1 << 0) /* 1=enable DMA */ +#define PCI9118_AI_CTRL_UNIP BIT(7) /* 1=unipolar */ +#define PCI9118_AI_CTRL_DIFF BIT(6) /* 1=differential inputs */ +#define PCI9118_AI_CTRL_SOFTG BIT(5) /* 1=8254 software gate */ +#define PCI9118_AI_CTRL_EXTG BIT(4) /* 1=8254 TGIN(pin 46) gate */ +#define PCI9118_AI_CTRL_EXTM BIT(3) /* 1=ext. trigger (pin 44) */ +#define PCI9118_AI_CTRL_TMRTR BIT(2) /* 1=8254 is trigger source */ +#define PCI9118_AI_CTRL_INT BIT(1) /* 1=enable interrupt */ +#define PCI9118_AI_CTRL_DMA BIT(0) /* 1=enable DMA */ #define PCI9118_DIO_REG 0x1c #define PCI9118_SOFTTRG_REG 0x20 #define PCI9118_AI_CHANLIST_REG 0x24 @@ -122,27 +116,25 @@ #define PCI9118_AI_BURST_NUM_REG 0x28 #define PCI9118_AI_AUTOSCAN_MODE_REG 0x2c #define PCI9118_AI_CFG_REG 0x30 -#define PCI9118_AI_CFG_PDTRG (1 << 7) /* 1=positive trigger */ -#define PCI9118_AI_CFG_PETRG (1 << 6) /* 1=positive ext. trigger */ -#define PCI9118_AI_CFG_BSSH (1 << 5) /* 1=with sample & hold */ -#define PCI9118_AI_CFG_BM (1 << 4) /* 1=burst mode */ -#define PCI9118_AI_CFG_BS (1 << 3) /* 1=burst mode start */ -#define PCI9118_AI_CFG_PM (1 << 2) /* 1=post trigger */ -#define PCI9118_AI_CFG_AM (1 << 1) /* 1=about trigger */ -#define PCI9118_AI_CFG_START (1 << 0) /* 1=trigger start */ +#define PCI9118_AI_CFG_PDTRG BIT(7) /* 1=positive trigger */ +#define PCI9118_AI_CFG_PETRG BIT(6) /* 1=positive ext. trigger */ +#define PCI9118_AI_CFG_BSSH BIT(5) /* 1=with sample & hold */ +#define PCI9118_AI_CFG_BM BIT(4) /* 1=burst mode */ +#define PCI9118_AI_CFG_BS BIT(3) /* 1=burst mode start */ +#define PCI9118_AI_CFG_PM BIT(2) /* 1=post trigger */ +#define PCI9118_AI_CFG_AM BIT(1) /* 1=about trigger */ +#define PCI9118_AI_CFG_START BIT(0) /* 1=trigger start */ #define PCI9118_FIFO_RESET_REG 0x34 #define PCI9118_INT_CTRL_REG 0x38 -#define PCI9118_INT_CTRL_TIMER (1 << 3) /* timer interrupt */ -#define PCI9118_INT_CTRL_ABOUT (1 << 2) /* about trigger complete */ -#define PCI9118_INT_CTRL_HFULL (1 << 1) /* A/D FIFO half full */ -#define PCI9118_INT_CTRL_DTRG (1 << 0) /* ext. digital trigger */ +#define PCI9118_INT_CTRL_TIMER BIT(3) /* timer interrupt */ +#define PCI9118_INT_CTRL_ABOUT BIT(2) /* about trigger complete */ +#define PCI9118_INT_CTRL_HFULL BIT(1) /* A/D FIFO half full */ +#define PCI9118_INT_CTRL_DTRG BIT(0) /* ext. digital trigger */ #define START_AI_EXT 0x01 /* start measure on external trigger */ #define STOP_AI_EXT 0x02 /* stop measure on external trigger */ #define STOP_AI_INT 0x08 /* stop measure on internal trigger */ -#define PCI9118_HALF_FIFO_SZ (1024 / 2) - static const struct comedi_lrange pci9118_ai_range = { 8, { BIP_RANGE(5), @@ -169,11 +161,6 @@ static const struct comedi_lrange pci9118hg_ai_range = { } }; -#define PCI9118_BIPOLAR_RANGES 4 /* - * used for test on mixture - * of BIP/UNI ranges - */ - enum pci9118_boardid { BOARD_PCI9118DG, BOARD_PCI9118HG, @@ -296,51 +283,44 @@ static void pci9118_ai_reset_fifo(struct comedi_device *dev) outl(0, dev->iobase + PCI9118_FIFO_RESET_REG); } -static int check_channel_list(struct comedi_device *dev, - struct comedi_subdevice *s, int n_chan, - unsigned int *chanlist, int frontadd, int backadd) +static int pci9118_ai_check_chanlist(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_cmd *cmd) { struct pci9118_private *devpriv = dev->private; - unsigned int i, differencial = 0, bipolar = 0; + unsigned int range0 = CR_RANGE(cmd->chanlist[0]); + unsigned int aref0 = CR_AREF(cmd->chanlist[0]); + int i; - /* correct channel and range number check itself comedi/range.c */ - if (n_chan < 1) { - dev_err(dev->class_dev, "range/channel list is empty!\n"); + /* single channel scans are always ok */ + if (cmd->chanlist_len == 1) return 0; - } - if ((frontadd + n_chan + backadd) > s->len_chanlist) { - dev_err(dev->class_dev, - "range/channel list is too long for actual configuration!\n"); - 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)) { - dev_err(dev->class_dev, - "Differential and single ended inputs can't be mixed!\n"); - return 0; - } - if ((CR_RANGE(chanlist[i]) < PCI9118_BIPOLAR_RANGES) != - (bipolar)) { - dev_err(dev->class_dev, - "Bipolar and unipolar ranges can't be mixed!\n"); - return 0; - } - if (!devpriv->usemux && differencial && - (CR_CHAN(chanlist[i]) >= (s->n_chan / 2))) { - dev_err(dev->class_dev, - "AREF_DIFF is only available for the first 8 channels!\n"); - return 0; - } + for (i = 1; i < cmd->chanlist_len; i++) { + unsigned int chan = CR_CHAN(cmd->chanlist[i]); + unsigned int range = CR_RANGE(cmd->chanlist[i]); + unsigned int aref = CR_AREF(cmd->chanlist[i]); + + if (aref != aref0) { + dev_err(dev->class_dev, + "Differential and single ended inputs can't be mixed!\n"); + return -EINVAL; + } + if (comedi_range_is_bipolar(s, range) != + comedi_range_is_bipolar(s, range0)) { + dev_err(dev->class_dev, + "Bipolar and unipolar ranges can't be mixed!\n"); + return -EINVAL; } + if (!devpriv->usemux && aref == AREF_DIFF && + (chan >= (s->n_chan / 2))) { + dev_err(dev->class_dev, + "AREF_DIFF is only available for the first 8 channels!\n"); + return -EINVAL; + } + } - return 1; + return 0; } static void pci9118_set_chanlist(struct comedi_device *dev, @@ -406,8 +386,8 @@ static void pci9118_set_chanlist(struct comedi_device *dev, /* udelay(100); important delay, or first sample will be crippled */ } -static void interrupt_pci9118_ai_mode4_switch(struct comedi_device *dev, - unsigned int next_buf) +static void pci9118_ai_mode4_switch(struct comedi_device *dev, + unsigned int next_buf) { struct pci9118_private *devpriv = dev->private; struct pci9118_dmabuf *dmabuf = &devpriv->dmabuf[next_buf]; @@ -421,9 +401,9 @@ static void interrupt_pci9118_ai_mode4_switch(struct comedi_device *dev, outl(devpriv->ai_cfg, dev->iobase + PCI9118_AI_CFG_REG); } -static unsigned int valid_samples_in_act_dma_buf(struct comedi_device *dev, - struct comedi_subdevice *s, - unsigned int n_raw_samples) +static unsigned int pci9118_ai_samples_ready(struct comedi_device *dev, + struct comedi_subdevice *s, + unsigned int n_raw_samples) { struct pci9118_private *devpriv = dev->private; struct comedi_cmd *cmd = &s->async->cmd; @@ -477,7 +457,7 @@ static unsigned int valid_samples_in_act_dma_buf(struct comedi_device *dev, return n_samples; } -static void move_block_from_dma(struct comedi_device *dev, +static void pci9118_ai_dma_xfer(struct comedi_device *dev, struct comedi_subdevice *s, unsigned short *dma_buffer, unsigned int n_raw_samples) @@ -634,8 +614,8 @@ static void pci9118_ai_munge(struct comedi_device *dev, } } -static void interrupt_pci9118_ai_onesample(struct comedi_device *dev, - struct comedi_subdevice *s) +static void pci9118_ai_get_onesample(struct comedi_device *dev, + struct comedi_subdevice *s) { struct pci9118_private *devpriv = dev->private; struct comedi_cmd *cmd = &s->async->cmd; @@ -651,8 +631,8 @@ static void interrupt_pci9118_ai_onesample(struct comedi_device *dev, } } -static void interrupt_pci9118_ai_dma(struct comedi_device *dev, - struct comedi_subdevice *s) +static void pci9118_ai_get_dma(struct comedi_device *dev, + struct comedi_subdevice *s) { struct pci9118_private *devpriv = dev->private; struct comedi_cmd *cmd = &s->async->cmd; @@ -662,21 +642,19 @@ static void interrupt_pci9118_ai_dma(struct comedi_device *dev, bool more_dma; /* determine whether more DMA buffers to do after this one */ - n_valid = valid_samples_in_act_dma_buf(dev, s, n_all); + n_valid = pci9118_ai_samples_ready(dev, s, n_all); more_dma = n_valid < comedi_nsamples_left(s, n_valid + 1); /* switch DMA buffers and restart DMA if double buffering */ if (more_dma && devpriv->dma_doublebuf) { devpriv->dma_actbuf = 1 - devpriv->dma_actbuf; pci9118_amcc_setup_dma(dev, devpriv->dma_actbuf); - if (devpriv->ai_do == 4) { - interrupt_pci9118_ai_mode4_switch(dev, - devpriv->dma_actbuf); - } + if (devpriv->ai_do == 4) + pci9118_ai_mode4_switch(dev, devpriv->dma_actbuf); } if (n_all) - move_block_from_dma(dev, s, dmabuf->virt, n_all); + pci9118_ai_dma_xfer(dev, s, dmabuf->virt, n_all); if (!devpriv->ai_neverending) { if (s->async->scans_done >= cmd->stop_arg) @@ -690,7 +668,7 @@ static void interrupt_pci9118_ai_dma(struct comedi_device *dev, if (more_dma && !devpriv->dma_doublebuf) { pci9118_amcc_setup_dma(dev, 0); if (devpriv->ai_do == 4) - interrupt_pci9118_ai_mode4_switch(dev, 0); + pci9118_ai_mode4_switch(dev, 0); } } @@ -779,9 +757,9 @@ static irqreturn_t pci9118_interrupt(int irq, void *d) } if (devpriv->usedma) - interrupt_pci9118_ai_dma(dev, s); + pci9118_ai_get_dma(dev, s); else - interrupt_pci9118_ai_onesample(dev, s); + pci9118_ai_get_onesample(dev, s); interrupt_exit: comedi_handle_events(dev, s); @@ -816,17 +794,18 @@ static int pci9118_ai_inttrig(struct comedi_device *dev, return 1; } -static int Compute_and_setup_dma(struct comedi_device *dev, - struct comedi_subdevice *s) +static int pci9118_ai_setup_dma(struct comedi_device *dev, + struct comedi_subdevice *s) { struct pci9118_private *devpriv = dev->private; struct comedi_cmd *cmd = &s->async->cmd; struct pci9118_dmabuf *dmabuf0 = &devpriv->dmabuf[0]; struct pci9118_dmabuf *dmabuf1 = &devpriv->dmabuf[1]; - unsigned int dmalen0, dmalen1, i; + unsigned int dmalen0 = dmabuf0->size; + unsigned int dmalen1 = dmabuf1->size; + unsigned int scan_bytes = devpriv->ai_n_realscanlen * + comedi_bytes_per_sample(s); - dmalen0 = dmabuf0->size; - dmalen1 = dmabuf1->size; /* isn't output buff smaller that our DMA buff? */ if (dmalen0 > s->async->prealloc_bufsz) { /* align to 32bit down */ @@ -839,15 +818,15 @@ static int Compute_and_setup_dma(struct comedi_device *dev, /* we want wake up every scan? */ if (devpriv->ai_flags & CMDF_WAKE_EOS) { - if (dmalen0 < (devpriv->ai_n_realscanlen << 1)) { + if (dmalen0 < scan_bytes) { /* uff, too short DMA buffer, disable EOS support! */ devpriv->ai_flags &= (~CMDF_WAKE_EOS); dev_info(dev->class_dev, "WAR: DMA0 buf too short, can't support CMDF_WAKE_EOS (%d<%d)\n", - dmalen0, devpriv->ai_n_realscanlen << 1); + dmalen0, scan_bytes); } else { /* short first DMA buffer to one scan */ - dmalen0 = devpriv->ai_n_realscanlen << 1; + dmalen0 = scan_bytes; if (dmalen0 < 4) { dev_info(dev->class_dev, "ERR: DMA0 buf len bug? (%d<4)\n", @@ -857,15 +836,15 @@ static int Compute_and_setup_dma(struct comedi_device *dev, } } if (devpriv->ai_flags & CMDF_WAKE_EOS) { - if (dmalen1 < (devpriv->ai_n_realscanlen << 1)) { + if (dmalen1 < scan_bytes) { /* uff, too short DMA buffer, disable EOS support! */ devpriv->ai_flags &= (~CMDF_WAKE_EOS); dev_info(dev->class_dev, "WAR: DMA1 buf too short, can't support CMDF_WAKE_EOS (%d<%d)\n", - dmalen1, devpriv->ai_n_realscanlen << 1); + dmalen1, scan_bytes); } else { /* short second DMA buffer to one scan */ - dmalen1 = devpriv->ai_n_realscanlen << 1; + dmalen1 = scan_bytes; if (dmalen1 < 4) { dev_info(dev->class_dev, "ERR: DMA1 buf len bug? (%d<4)\n", @@ -877,45 +856,39 @@ static int Compute_and_setup_dma(struct comedi_device *dev, /* transfer without CMDF_WAKE_EOS */ if (!(devpriv->ai_flags & CMDF_WAKE_EOS)) { + unsigned int tmp; + /* if it's possible then align DMA buffers to length of scan */ - i = dmalen0; - dmalen0 = - (dmalen0 / (devpriv->ai_n_realscanlen << 1)) * - (devpriv->ai_n_realscanlen << 1); + tmp = dmalen0; + dmalen0 = (dmalen0 / scan_bytes) * scan_bytes; dmalen0 &= ~3L; if (!dmalen0) - dmalen0 = i; /* uff. very long scan? */ - i = dmalen1; - dmalen1 = - (dmalen1 / (devpriv->ai_n_realscanlen << 1)) * - (devpriv->ai_n_realscanlen << 1); + dmalen0 = tmp; /* uff. very long scan? */ + tmp = dmalen1; + dmalen1 = (dmalen1 / scan_bytes) * scan_bytes; dmalen1 &= ~3L; if (!dmalen1) - dmalen1 = i; /* uff. very long scan? */ + dmalen1 = tmp; /* uff. very long scan? */ /* * if measure isn't neverending then test, if it fits whole * into one or two DMA buffers */ if (!devpriv->ai_neverending) { + unsigned long long scanlen; + + scanlen = (unsigned long long)scan_bytes * + cmd->stop_arg; + /* fits whole measure into one DMA buffer? */ - if (dmalen0 > - ((devpriv->ai_n_realscanlen << 1) * - cmd->stop_arg)) { - dmalen0 = - (devpriv->ai_n_realscanlen << 1) * - cmd->stop_arg; + if (dmalen0 > scanlen) { + dmalen0 = scanlen; dmalen0 &= ~3L; - } else { /* - * fits whole measure into - * two DMA buffer? - */ - if (dmalen1 > - ((devpriv->ai_n_realscanlen << 1) * - cmd->stop_arg - dmalen0)) - dmalen1 = - (devpriv->ai_n_realscanlen << 1) * - cmd->stop_arg - dmalen0; - dmalen1 &= ~3L; + } else { + /* fits whole measure into two DMA buffer? */ + if (dmalen1 > (scanlen - dmalen0)) { + dmalen1 = scanlen - dmalen0; + dmalen1 &= ~3L; + } } } } @@ -945,6 +918,7 @@ static int pci9118_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s) struct comedi_8254 *pacer = dev->pacer; struct comedi_cmd *cmd = &s->async->cmd; unsigned int addchans = 0; + unsigned int scanlen; devpriv->ai12_startstop = 0; devpriv->ai_flags = cmd->flags; @@ -1030,19 +1004,20 @@ static int pci9118_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s) } } /* well, we now know what must be all added */ - devpriv->ai_n_realscanlen = /* - * what we must take from card in real - * to have cmd->scan_end_arg on output? - */ - (devpriv->ai_add_front + cmd->chanlist_len + - devpriv->ai_add_back) * (cmd->scan_end_arg / - cmd->chanlist_len); - - /* check and setup channel list */ - if (!check_channel_list(dev, s, cmd->chanlist_len, - cmd->chanlist, devpriv->ai_add_front, - devpriv->ai_add_back)) + scanlen = devpriv->ai_add_front + cmd->chanlist_len + + devpriv->ai_add_back; + /* + * what we must take from card in real to have cmd->scan_end_arg + * on output? + */ + devpriv->ai_n_realscanlen = scanlen * + (cmd->scan_end_arg / cmd->chanlist_len); + + if (scanlen > s->len_chanlist) { + dev_err(dev->class_dev, + "range/channel list is too long for actual configuration!\n"); return -EINVAL; + } /* * Configure analog input and load the chanlist. @@ -1141,7 +1116,7 @@ static int pci9118_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s) devpriv->ai_act_dmapos = 0; if (devpriv->usedma) { - Compute_and_setup_dma(dev, s); + pci9118_ai_setup_dma(dev, s); outl(0x02000000 | AINT_WRITE_COMPL, devpriv->iobase_a + AMCC_OP_REG_INTCSR); @@ -1205,9 +1180,6 @@ static int pci9118_ai_cmdtest(struct comedi_device *dev, if (cmd->start_src == TRIG_EXT && cmd->scan_begin_src == TRIG_EXT) err |= -EINVAL; - if (cmd->start_src == TRIG_INT && cmd->scan_begin_src == TRIG_INT) - err |= -EINVAL; - if ((cmd->scan_begin_src & (TRIG_TIMER | TRIG_EXT)) && (!(cmd->convert_src & (TRIG_TIMER | TRIG_NOW)))) err |= -EINVAL; @@ -1315,10 +1287,13 @@ static int pci9118_ai_cmdtest(struct comedi_device *dev, if (err) return 4; + /* Step 5: check channel list if it exists */ + if (cmd->chanlist) - if (!check_channel_list(dev, s, cmd->chanlist_len, - cmd->chanlist, 0, 0)) - return 5; /* incorrect channels list */ + err |= pci9118_ai_check_chanlist(dev, s, cmd); + + if (err) + return 5; return 0; } @@ -1608,7 +1583,7 @@ static int pci9118_common_attach(struct comedi_device *dev, if (dev->irq) { dev->read_subdev = s; s->subdev_flags |= SDF_CMD_READ; - s->len_chanlist = PCI9118_CHANLEN; + s->len_chanlist = 255; s->do_cmdtest = pci9118_ai_cmdtest; s->do_cmd = pci9118_ai_cmd; s->cancel = pci9118_ai_cancel; |