From: H Hartley Sweeten Date: Tue, 4 Nov 2014 17:54:33 +0000 (-0700) Subject: staging: comedi: addi_apci_3120: fix apci3120_ai_insn_read() X-Git-Url: http://drtracing.org/?a=commitdiff_plain;h=89e9057b50369474c1e701faccdeedf08566ec57;p=deliverable%2Flinux.git staging: comedi: addi_apci_3120: fix apci3120_ai_insn_read() Now that the scanning and interrupt support have been removed from this function it can be refactored to work correctly. The comedi core expects (*insn_read) functions to read insn->n values from the hardware and return the number of samples read. This function currently just reads one sample but it returns insn->n. Fix this function to work like the core expects. Use comedi_timeout() to prevent a possible deadlock in the loop that waits for the end-of-conversion. Signed-off-by: H Hartley Sweeten Reviewed-by: Ian Abbott Signed-off-by: Greg Kroah-Hartman --- diff --git a/drivers/staging/comedi/drivers/addi-data/hwdrv_apci3120.c b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci3120.c index 51cdecb211d6..5fb884835643 100644 --- a/drivers/staging/comedi/drivers/addi-data/hwdrv_apci3120.c +++ b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci3120.c @@ -139,6 +139,19 @@ static int apci3120_setup_chan_list(struct comedi_device *dev, return 1; /* we can serve this with scan logic */ } +static int apci3120_ai_eoc(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, + unsigned long context) +{ + unsigned int status; + + status = inw(dev->iobase + APCI3120_RD_STATUS); + if ((status & APCI3120_EOC) == 0) + return 0; + return -EBUSY; +} + static int apci3120_ai_insn_read(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_insn *insn, @@ -146,67 +159,44 @@ static int apci3120_ai_insn_read(struct comedi_device *dev, { struct apci3120_private *devpriv = dev->private; unsigned int divisor; - unsigned int ns; - unsigned short us_TmpValue; - - /* fix conversion time to 10 us */ - ns = 10000; - - /* Clear software registers */ - devpriv->timer_mode = 0; - devpriv->mode = 0; - - if (insn->unused[0] == 222) { /* second insn read */ - } else { - devpriv->tsk_Current = current; /* Save the current process task structure */ - - divisor = apci3120_ns_to_timer(dev, 0, ns, CMDF_ROUND_NEAREST); - - us_TmpValue = (unsigned short) devpriv->b_InterruptMode; - - switch (us_TmpValue) { - - case APCI3120_EOC_MODE: - apci3120_ai_reset_fifo(dev); - - /* Initialize the sequence array */ - if (!apci3120_setup_chan_list(dev, s, 1, - &insn->chanspec)) - return -EINVAL; - - /* Initialize Timer 0 mode 4 */ - apci3120_timer_set_mode(dev, 0, APCI3120_TIMER_MODE4); + int ret; + int i; - outb(devpriv->mode, dev->iobase + APCI3120_MODE_REG); + /* set mode for A/D conversions by software trigger with timer 0 */ + devpriv->mode = APCI3120_MODE_TIMER2_CLK_OSC | + APCI3120_MODE_TIMER2_AS_TIMER; + outb(devpriv->mode, dev->iobase + APCI3120_MODE_REG); - apci3120_timer_enable(dev, 0, true); + /* load chanlist for single channel scan */ + if (!apci3120_setup_chan_list(dev, s, 1, &insn->chanspec)) + return -EINVAL; - /* Set the conversion time */ - apci3120_timer_write(dev, 0, divisor); + /* + * Timer 0 is used in MODE4 (software triggered strobe) to set the + * conversion time for each acquisition. Each conversion is triggered + * when the divisor is written to the timer, The conversion is done + * when the EOC bit in the status register is '0'. + */ + apci3120_timer_set_mode(dev, 0, APCI3120_TIMER_MODE4); + apci3120_timer_enable(dev, 0, true); - us_TmpValue = - (unsigned short) inw(dev->iobase + APCI3120_RD_STATUS); + /* fixed conversion time of 10 us */ + divisor = apci3120_ns_to_timer(dev, 0, 10000, CMDF_ROUND_NEAREST); - do { - /* Waiting for the end of conversion */ - us_TmpValue = inw(dev->iobase + - APCI3120_RD_STATUS); - } while ((us_TmpValue & APCI3120_EOC) == APCI3120_EOC); + apci3120_ai_reset_fifo(dev); - /* Read the result in FIFO and put it in insn data pointer */ - us_TmpValue = inw(dev->iobase + 0); - *data = us_TmpValue; + for (i = 0; i < insn->n; i++) { + /* trigger conversion */ + apci3120_timer_write(dev, 0, divisor); - apci3120_ai_reset_fifo(dev); - break; - default: - dev_err(dev->class_dev, "inputs wrong\n"); + ret = comedi_timeout(dev, s, insn, apci3120_ai_eoc, 0); + if (ret) + return ret; - } + data[i] = inw(dev->iobase + 0); } return insn->n; - } static int apci3120_reset(struct comedi_device *dev)